From 894d81c57767444425c15e340fec292b3341aacb Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 9 Jul 2024 04:07:54 +0200 Subject: [PATCH 0001/1052] [CI] Allow running specific target test(s) only (#7051) --- script/test_build_components | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/script/test_build_components b/script/test_build_components index 9bbb694dcc..e885294b99 100755 --- a/script/test_build_components +++ b/script/test_build_components @@ -7,12 +7,13 @@ set -e # - `c` - Component folder name to test. Default `*`. esphome_command="compile" target_component="*" -while getopts e:c: flag +while getopts e:c:t: flag do case $flag in e) esphome_command=${OPTARG};; c) target_component=${OPTARG};; - \?) echo "Usage: $0 [-e ] [-c ]" 1>&2; exit 1;; + t) requested_target_platform=${OPTARG};; + \?) echo "Usage: $0 [-e ] [-c ] [-t ]" 1>&2; exit 1;; esac done @@ -23,6 +24,10 @@ if ! [ -d "./tests/test_build_components/build" ]; then fi start_esphome() { + if [ -n "$requested_target_platform" ] && [ "$requested_target_platform" != "$target_platform" ]; then + echo "Skiping $target_platform" + return + fi # create dynamic yaml file in `build` folder. # `./tests/test_build_components/build/[target_component].[test_name].[target_platform_with_version].yaml` component_test_file="./tests/test_build_components/build/$target_component.$test_name.$target_platform_with_version.yaml" From ee398441b664722d9af67699035af5fd38b6ab28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:21:11 +0200 Subject: [PATCH 0002/1052] Bump actions/setup-python from 5.1.0 to 5.1.1 in /.github/actions/restore-python (#7071) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index 4ad9e2eaed..d3fe2a89dc 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -17,7 +17,7 @@ runs: steps: - name: Set up Python ${{ inputs.python-version }} id: python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.1.1 with: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment From 2da939c81c1ec506eb96010a101e6263060f9a1c Mon Sep 17 00:00:00 2001 From: MichD Date: Wed, 10 Jul 2024 23:37:50 +0100 Subject: [PATCH 0003/1052] Fix RC Switch protocol not transmitting correctly via IR (#5411) --- esphome/components/remote_base/rc_switch_protocol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/remote_base/rc_switch_protocol.cpp b/esphome/components/remote_base/rc_switch_protocol.cpp index 1f38fdca67..cb8a077975 100644 --- a/esphome/components/remote_base/rc_switch_protocol.cpp +++ b/esphome/components/remote_base/rc_switch_protocol.cpp @@ -54,7 +54,7 @@ void RCSwitchBase::sync(RemoteTransmitData *dst) const { } } void RCSwitchBase::transmit(RemoteTransmitData *dst, uint64_t code, uint8_t len) const { - dst->set_carrier_frequency(0); + dst->set_carrier_frequency(38000); this->sync(dst); for (int16_t i = len - 1; i >= 0; i--) { if (code & ((uint64_t) 1 << i)) { From 2873c6bbaffc22383f870dc5203f4246279c709e Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 10 Jul 2024 21:21:04 -0400 Subject: [PATCH 0004/1052] [micro_wake_word] Version 2 (#7032) --- .../components/micro_wake_word/__init__.py | 262 +++++++-- .../audio_preprocessor_int8_model_data.h | 493 ----------------- .../micro_wake_word/micro_wake_word.cpp | 522 ++++++++---------- .../micro_wake_word/micro_wake_word.h | 190 +++---- .../micro_wake_word/preprocessor_settings.h | 20 + .../micro_wake_word/streaming_model.cpp | 189 +++++++ .../micro_wake_word/streaming_model.h | 84 +++ esphome/core/defines.h | 1 + platformio.ini | 3 +- tests/components/micro_wake_word/common.yaml | 6 +- .../micro_wake_word/test.esp32-idf.yaml | 1 + 11 files changed, 822 insertions(+), 949 deletions(-) delete mode 100644 esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h create mode 100644 esphome/components/micro_wake_word/preprocessor_settings.h create mode 100644 esphome/components/micro_wake_word/streaming_model.cpp create mode 100644 esphome/components/micro_wake_word/streaming_model.h create mode 100644 tests/components/micro_wake_word/test.esp32-idf.yaml diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index 35ee3cfedc..3d3459ccab 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -9,7 +9,7 @@ import requests import esphome.config_validation as cv import esphome.codegen as cg -from esphome.core import CORE, HexInt, EsphomeError +from esphome.core import CORE, HexInt from esphome.components import esp32, microphone from esphome import automation, git, external_files @@ -41,9 +41,15 @@ CODEOWNERS = ["@kahrendt", "@jesserockz"] DEPENDENCIES = ["microphone"] DOMAIN = "micro_wake_word" + +CONF_FEATURE_STEP_SIZE = "feature_step_size" +CONF_MODELS = "models" +CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected" CONF_PROBABILITY_CUTOFF = "probability_cutoff" CONF_SLIDING_WINDOW_AVERAGE_SIZE = "sliding_window_average_size" -CONF_ON_WAKE_WORD_DETECTED = "on_wake_word_detected" +CONF_SLIDING_WINDOW_SIZE = "sliding_window_size" +CONF_TENSOR_ARENA_SIZE = "tensor_arena_size" +CONF_VAD = "vad" TYPE_HTTP = "http" @@ -98,12 +104,14 @@ GIT_SCHEMA = cv.All( _process_git_source, ) -KEY_WAKE_WORD = "wake_word" + KEY_AUTHOR = "author" -KEY_WEBSITE = "website" -KEY_VERSION = "version" KEY_MICRO = "micro" KEY_MINIMUM_ESPHOME_VERSION = "minimum_esphome_version" +KEY_TRAINED_LANGUAGES = "trained_languages" +KEY_VERSION = "version" +KEY_WAKE_WORD = "wake_word" +KEY_WEBSITE = "website" MANIFEST_SCHEMA_V1 = cv.Schema( { @@ -125,6 +133,29 @@ MANIFEST_SCHEMA_V1 = cv.Schema( } ) +MANIFEST_SCHEMA_V2 = cv.Schema( + { + cv.Required(CONF_TYPE): "micro", + cv.Required(CONF_MODEL): cv.string, + cv.Required(KEY_AUTHOR): cv.string, + cv.Required(KEY_VERSION): cv.All(cv.int_, 2), + cv.Required(KEY_WAKE_WORD): cv.string, + cv.Required(KEY_TRAINED_LANGUAGES): cv.ensure_list(cv.string), + cv.Optional(KEY_WEBSITE): cv.url, + cv.Required(KEY_MICRO): cv.Schema( + { + cv.Required(CONF_FEATURE_STEP_SIZE): cv.int_range(min=0, max=30), + cv.Required(CONF_TENSOR_ARENA_SIZE): cv.int_, + cv.Required(CONF_PROBABILITY_CUTOFF): cv.float_, + cv.Required(CONF_SLIDING_WINDOW_SIZE): cv.positive_int, + cv.Required(KEY_MINIMUM_ESPHOME_VERSION): cv.All( + cv.version_number, cv.validate_esphome_version + ), + } + ), + } +) + def _compute_local_file_path(config: dict) -> Path: url = config[CONF_URL] @@ -135,6 +166,24 @@ def _compute_local_file_path(config: dict) -> Path: return base_dir / key +def _convert_manifest_v1_to_v2(v1_manifest): + v2_manifest = v1_manifest.copy() + + v2_manifest[KEY_VERSION] = 2 + v2_manifest[KEY_MICRO][CONF_SLIDING_WINDOW_SIZE] = v1_manifest[KEY_MICRO][ + CONF_SLIDING_WINDOW_AVERAGE_SIZE + ] + del v2_manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE] + v2_manifest[KEY_MICRO][ + CONF_TENSOR_ARENA_SIZE + ] = 45672 # Original Inception-based V1 manifest models require a minimum of 45672 bytes + v2_manifest[KEY_MICRO][ + CONF_FEATURE_STEP_SIZE + ] = 20 # Original Inception-based V1 manifest models use a 20 ms feature step size + + return v2_manifest + + def _download_file(url: str, path: Path) -> bytes: if not external_files.has_remote_file_changed(url, path): _LOGGER.debug("Remote file has not changed, skipping download") @@ -155,6 +204,24 @@ def _download_file(url: str, path: Path) -> bytes: return req.content +def _validate_manifest_version(manifest_data): + if manifest_version := manifest_data.get(KEY_VERSION): + if manifest_version == 1: + try: + MANIFEST_SCHEMA_V1(manifest_data) + except cv.Invalid as e: + raise cv.Invalid(f"Invalid manifest file: {e}") from e + elif manifest_version == 2: + try: + MANIFEST_SCHEMA_V2(manifest_data) + except cv.Invalid as e: + raise cv.Invalid(f"Invalid manifest file: {e}") from e + else: + raise cv.Invalid("Invalid manifest version") + else: + raise cv.Invalid("Invalid manifest file, missing 'version' key.") + + def _process_http_source(config): url = config[CONF_URL] path = _compute_local_file_path(config) @@ -167,11 +234,6 @@ def _process_http_source(config): if not isinstance(manifest_data, dict): raise cv.Invalid("Manifest file must contain a JSON object") - try: - MANIFEST_SCHEMA_V1(manifest_data) - except cv.Invalid as e: - raise cv.Invalid(f"Invalid manifest file: {e}") from e - model = manifest_data[CONF_MODEL] model_url = urljoin(url, model) @@ -206,7 +268,7 @@ def _validate_source_model_name(value): return MODEL_SOURCE_SCHEMA( { CONF_TYPE: TYPE_HTTP, - CONF_URL: f"https://github.com/esphome/micro-wake-word-models/raw/main/models/{value}.json", + CONF_URL: f"https://github.com/esphome/micro-wake-word-models/raw/main/models/v2/{value}.json", } ) @@ -260,18 +322,55 @@ MODEL_SOURCE_SCHEMA = cv.Any( msg="Not a valid model name, local path, http(s) url, or github shorthand", ) +MODEL_SCHEMA = cv.Schema( + { + cv.Optional(CONF_MODEL): MODEL_SOURCE_SCHEMA, + cv.Optional(CONF_PROBABILITY_CUTOFF): cv.percentage, + cv.Optional(CONF_SLIDING_WINDOW_SIZE): cv.positive_int, + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + } +) + +# Provide a default VAD model that could be overridden +VAD_MODEL_SCHEMA = MODEL_SCHEMA.extend( + cv.Schema( + { + cv.Optional( + CONF_MODEL, + default="vad", + ): MODEL_SOURCE_SCHEMA, + } + ) +) + + +def _maybe_empty_vad_schema(value): + # Idea borrowed from uart/__init__.py's ``maybe_empty_debug`` function. Accessed 2 July 2024. + # Loads a default VAD model without any parameters overridden. + if value is None: + value = {} + return VAD_MODEL_SCHEMA(value) + + CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(MicroWakeWord), cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), - cv.Optional(CONF_PROBABILITY_CUTOFF): cv.percentage, - cv.Optional(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.positive_int, + cv.Required(CONF_MODELS): cv.ensure_list(MODEL_SCHEMA), cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( single=True ), - cv.Required(CONF_MODEL): MODEL_SOURCE_SCHEMA, - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + cv.Optional(CONF_VAD): _maybe_empty_vad_schema, + cv.Optional(CONF_MODEL): cv.invalid( + f"The {CONF_MODEL} parameter has moved to be a list element under the {CONF_MODELS} parameter." + ), + cv.Optional(CONF_PROBABILITY_CUTOFF): cv.invalid( + f"The {CONF_PROBABILITY_CUTOFF} parameter has moved to be a list element under the {CONF_MODELS} parameter." + ), + cv.Optional(CONF_SLIDING_WINDOW_AVERAGE_SIZE): cv.invalid( + f"The {CONF_SLIDING_WINDOW_AVERAGE_SIZE} parameter has been renamed to {CONF_SLIDING_WINDOW_SIZE} and moved to be a list element under the {CONF_MODELS} parameter." + ), } ).extend(cv.COMPONENT_SCHEMA), cv.only_with_esp_idf, @@ -282,45 +381,20 @@ def _load_model_data(manifest_path: Path): with open(manifest_path, encoding="utf-8") as f: manifest = json.load(f) - try: - MANIFEST_SCHEMA_V1(manifest) - except cv.Invalid as e: - raise EsphomeError(f"Invalid manifest file: {e}") from e + _validate_manifest_version(manifest) model_path = manifest_path.parent / manifest[CONF_MODEL] with open(model_path, "rb") as f: model = f.read() + if manifest.get(KEY_VERSION) == 1: + manifest = _convert_manifest_v1_to_v2(manifest) + return manifest, model -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(var, config) - - mic = await cg.get_variable(config[CONF_MICROPHONE]) - cg.add(var.set_microphone(mic)) - - if on_wake_word_detection_config := config.get(CONF_ON_WAKE_WORD_DETECTED): - await automation.build_automation( - var.get_wake_word_detected_trigger(), - [(cg.std_string, "wake_word")], - on_wake_word_detection_config, - ) - - esp32.add_idf_component( - name="esp-tflite-micro", - repo="https://github.com/espressif/esp-tflite-micro", - ref="v1.3.1", - ) - - cg.add_build_flag("-DTF_LITE_STATIC_MEMORY") - cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON") - cg.add_build_flag("-DESP_NN") - - model_config = config.get(CONF_MODEL) - data = [] +def _model_config_to_manifest_data(model_config): if model_config[CONF_TYPE] == TYPE_GIT: # compute path to model file key = f"{model_config[CONF_URL]}@{model_config.get(CONF_REF)}" @@ -338,23 +412,95 @@ async def to_code(config): else: raise ValueError("Unsupported config type: {model_config[CONF_TYPE]}") - manifest, data = _load_model_data(file) + return _load_model_data(file) - rhs = [HexInt(x) for x in data] - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.add(var.set_model_start(prog_arr)) - probability_cutoff = config.get( - CONF_PROBABILITY_CUTOFF, manifest[KEY_MICRO][CONF_PROBABILITY_CUTOFF] +def _feature_step_size_validate(config): + features_step_size = None + + for model_parameters in config[CONF_MODELS]: + model_config = model_parameters.get(CONF_MODEL) + manifest, _ = _model_config_to_manifest_data(model_config) + + model_step_size = manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE] + + if features_step_size is None: + features_step_size = model_step_size + elif features_step_size != model_step_size: + raise cv.Invalid("Cannot load models with different features step sizes.") + + +FINAL_VALIDATE_SCHEMA = _feature_step_size_validate + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + mic = await cg.get_variable(config[CONF_MICROPHONE]) + cg.add(var.set_microphone(mic)) + + esp32.add_idf_component( + name="esp-tflite-micro", + repo="https://github.com/espressif/esp-tflite-micro", + ref="v1.3.1", ) - cg.add(var.set_probability_cutoff(probability_cutoff)) - sliding_window_average_size = config.get( - CONF_SLIDING_WINDOW_AVERAGE_SIZE, - manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE], - ) - cg.add(var.set_sliding_window_average_size(sliding_window_average_size)) - cg.add(var.set_wake_word(manifest[KEY_WAKE_WORD])) + cg.add_build_flag("-DTF_LITE_STATIC_MEMORY") + cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON") + cg.add_build_flag("-DESP_NN") + + if on_wake_word_detection_config := config.get(CONF_ON_WAKE_WORD_DETECTED): + await automation.build_automation( + var.get_wake_word_detected_trigger(), + [(cg.std_string, "wake_word")], + on_wake_word_detection_config, + ) + + if vad_model := config.get(CONF_VAD): + cg.add_define("USE_MICRO_WAKE_WORD_VAD") + + # Use the general model loading code for the VAD codegen + config[CONF_MODELS].append(vad_model) + + for model_parameters in config[CONF_MODELS]: + model_config = model_parameters.get(CONF_MODEL) + data = [] + manifest, data = _model_config_to_manifest_data(model_config) + + rhs = [HexInt(x) for x in data] + prog_arr = cg.progmem_array(model_parameters[CONF_RAW_DATA_ID], rhs) + + probability_cutoff = model_parameters.get( + CONF_PROBABILITY_CUTOFF, manifest[KEY_MICRO][CONF_PROBABILITY_CUTOFF] + ) + sliding_window_size = model_parameters.get( + CONF_SLIDING_WINDOW_SIZE, + manifest[KEY_MICRO][CONF_SLIDING_WINDOW_SIZE], + ) + + if manifest[KEY_WAKE_WORD] == "vad": + cg.add( + var.add_vad_model( + prog_arr, + probability_cutoff, + sliding_window_size, + manifest[KEY_MICRO][CONF_TENSOR_ARENA_SIZE], + ) + ) + else: + cg.add( + var.add_wake_word_model( + prog_arr, + probability_cutoff, + sliding_window_size, + manifest[KEY_WAKE_WORD], + manifest[KEY_MICRO][CONF_TENSOR_ARENA_SIZE], + ) + ) + + cg.add(var.set_features_step_size(manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE])) + cg.add_library("kahrendt/ESPMicroSpeechFeatures", "1.0.0") MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)}) diff --git a/esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h b/esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h deleted file mode 100644 index 918e76045f..0000000000 --- a/esphome/components/micro_wake_word/audio_preprocessor_int8_model_data.h +++ /dev/null @@ -1,493 +0,0 @@ -#pragma once - -#ifdef USE_ESP_IDF - -// Converted audio_preprocessor_int8.tflite -// From https://github.com/tensorflow/tflite-micro/tree/main/tensorflow/lite/micro/examples/micro_speech/models accessed -// January 2024 -// -// Copyright 2023 The TensorFlow Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -namespace esphome { -namespace micro_wake_word { - -const unsigned char G_AUDIO_PREPROCESSOR_INT8_TFLITE[] = { - 0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x14, 0x00, 0x20, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, - 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x88, 0x00, - 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, 0x90, 0x0e, 0x00, 0x00, 0xcc, 0x1f, 0x00, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xe2, 0xeb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, - 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x94, 0xff, - 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x5f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc2, 0xf5, 0xff, 0xff, - 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xdc, 0xff, 0xff, 0xff, 0x2d, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x43, 0x4f, 0x4e, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, - 0x4e, 0x5f, 0x4d, 0x45, 0x54, 0x41, 0x44, 0x41, 0x54, 0x41, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x6d, 0x69, 0x6e, - 0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x2e, 0x00, - 0x00, 0x00, 0x9c, 0x0d, 0x00, 0x00, 0x94, 0x0d, 0x00, 0x00, 0xc4, 0x09, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00, 0x48, - 0x09, 0x00, 0x00, 0x34, 0x09, 0x00, 0x00, 0x20, 0x09, 0x00, 0x00, 0x0c, 0x09, 0x00, 0x00, 0xf8, 0x08, 0x00, 0x00, - 0xec, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x24, 0x07, 0x00, 0x00, 0xc0, 0x06, 0x00, 0x00, 0x38, 0x04, 0x00, - 0x00, 0xb0, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x60, 0x01, - 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x2c, - 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, - 0x04, 0x01, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, - 0x00, 0xdc, 0x00, 0x00, 0x00, 0xd4, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0xbc, 0x00, - 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x94, - 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff, - 0x04, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x08, 0x00, - 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x31, 0x32, 0x2e, 0x30, 0x00, - 0x00, 0x56, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x2e, 0x38, 0x2e, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0xe1, 0xff, 0xff, 0xd8, 0xe1, 0xff, 0xff, 0xdc, - 0xe1, 0xff, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe4, 0xe1, 0xff, 0xff, 0xe8, 0xe1, 0xff, 0xff, 0xec, 0xe1, 0xff, 0xff, - 0xf0, 0xe1, 0xff, 0xff, 0xf4, 0xe1, 0xff, 0xff, 0xf8, 0xe1, 0xff, 0xff, 0xfc, 0xe1, 0xff, 0xff, 0x00, 0xe2, 0xff, - 0xff, 0x04, 0xe2, 0xff, 0xff, 0x08, 0xe2, 0xff, 0xff, 0x0c, 0xe2, 0xff, 0xff, 0x10, 0xe2, 0xff, 0xff, 0x14, 0xe2, - 0xff, 0xff, 0x18, 0xe2, 0xff, 0xff, 0x1c, 0xe2, 0xff, 0xff, 0x20, 0xe2, 0xff, 0xff, 0x24, 0xe2, 0xff, 0xff, 0x28, - 0xe2, 0xff, 0xff, 0x2c, 0xe2, 0xff, 0xff, 0x30, 0xe2, 0xff, 0xff, 0xd2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x9a, 0x02, 0x00, 0x00, 0xe2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0xf2, 0xf7, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0xff, - 0xff, 0xff, 0x02, 0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, - 0xf8, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x22, 0xf8, 0xff, 0xff, - 0x04, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x61, 0x05, 0x00, 0x00, 0x00, 0x00, 0x23, 0x0b, 0x41, - 0x01, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x0e, 0x80, 0x05, - 0x00, 0x00, 0x00, 0x00, 0xd1, 0x0c, 0x63, 0x04, 0x00, 0x00, 0x00, 0x00, 0x34, 0x0c, 0x3f, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x81, 0x0c, 0xf7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x9f, 0x0d, 0x77, 0x06, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x0f, - 0xa9, 0x08, 0x01, 0x02, 0x7f, 0x0b, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0e, 0xd1, 0x08, 0xdb, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x0d, 0x4a, 0x07, 0xad, 0x01, 0x2c, 0x0c, 0xc6, 0x06, 0x79, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x0c, 0x29, 0x07, 0x23, 0x02, 0x34, 0x0d, 0x5b, 0x08, 0x96, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe5, 0x0e, 0x48, - 0x0a, 0xbd, 0x05, 0x45, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x0c, 0x88, 0x08, 0x43, 0x04, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x0b, 0xd3, 0x07, 0xcb, 0x03, 0xd2, 0x0f, 0xe7, - 0x0b, 0x09, 0x08, 0x39, 0x04, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x0c, 0x14, 0x09, - 0x75, 0x05, 0xe2, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x0e, 0xdd, 0x0a, 0x6b, 0x07, 0x03, - 0x04, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x0d, 0x09, 0x0a, 0xc9, 0x06, 0x93, 0x03, 0x65, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0d, 0x25, 0x0a, 0x12, 0x07, 0x07, 0x04, 0x05, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x0a, 0x0e, 0x17, 0x0b, 0x2c, 0x08, 0x49, 0x05, 0x6d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x98, 0x0f, 0xcb, 0x0c, 0x04, 0x0a, 0x44, 0x07, 0x8b, 0x04, 0xd8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x0f, 0x87, - 0x0c, 0xe7, 0x09, 0x4e, 0x07, 0xba, 0x04, 0x2d, 0x02, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x0f, 0x23, 0x0d, 0xa7, 0x0a, - 0x30, 0x08, 0xbe, 0x05, 0x52, 0x03, 0xeb, 0x00, 0x89, 0x0e, 0x2c, 0x0c, 0xd4, 0x09, 0x81, 0x07, 0x33, 0x05, 0xe9, - 0x02, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x0e, 0x29, 0x0c, 0xf1, 0x09, 0xbe, 0x07, 0x90, 0x05, 0x65, 0x03, - 0x3f, 0x01, 0x1d, 0x0f, 0xff, 0x0c, 0xe5, 0x0a, 0xcf, 0x08, 0xbc, 0x06, 0xae, 0x04, 0xa3, 0x02, 0x9c, 0x00, 0x99, - 0x0e, 0x99, 0x0c, 0x9d, 0x0a, 0xa4, 0x08, 0xaf, 0x06, 0xbd, 0x04, 0xcf, 0x02, 0xe4, 0x00, 0xfc, 0x0e, 0x17, 0x0d, - 0x36, 0x0b, 0x57, 0x09, 0x7c, 0x07, 0xa4, 0x05, 0xcf, 0x03, 0xfd, 0x01, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x62, 0x0e, 0x98, 0x0c, 0xd2, 0x0a, 0x0e, 0x09, 0x4d, 0x07, 0x8f, 0x05, 0xd4, 0x03, 0x1b, 0x02, - 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x0e, 0x00, 0x0d, 0x52, 0x0b, 0xa6, 0x09, 0xfd, 0x07, 0x56, 0x06, 0xb1, - 0x04, 0x0f, 0x03, 0x6f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x0f, 0x37, 0x0e, 0x9e, 0x0c, - 0x08, 0x0b, 0x73, 0x09, 0xe1, 0x07, 0x52, 0x06, 0xc4, 0x04, 0x38, 0x03, 0xaf, 0x01, 0x28, 0x00, 0xa3, 0x0e, 0x1f, - 0x0d, 0x9e, 0x0b, 0x1f, 0x0a, 0xa2, 0x08, 0x27, 0x07, 0xae, 0x05, 0x37, 0x04, 0xc2, 0x02, 0x4e, 0x01, 0x00, 0x00, - 0x00, 0x00, 0xdd, 0x0f, 0x6d, 0x0e, 0xff, 0x0c, 0x93, 0x0b, 0x29, 0x0a, 0xc1, 0x08, 0x5a, 0x07, 0xf5, 0x05, 0x92, - 0x04, 0x30, 0x03, 0xd1, 0x01, 0x73, 0x00, 0x16, 0x0f, 0xbc, 0x0d, 0x62, 0x0c, 0x0b, 0x0b, 0xb5, 0x09, 0x61, 0x08, - 0x0e, 0x07, 0xbd, 0x05, 0x6d, 0x04, 0x1f, 0x03, 0xd3, 0x01, 0x88, 0x00, 0x3e, 0x0f, 0xf6, 0x0d, 0xaf, 0x0c, 0x6a, - 0x0b, 0x27, 0x0a, 0xe4, 0x08, 0xa3, 0x07, 0x64, 0x06, 0x26, 0x05, 0xe9, 0x03, 0xae, 0x02, 0x74, 0x01, 0x3b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x0f, 0xce, 0x0d, 0x99, 0x0c, 0x66, 0x0b, 0x34, 0x0a, 0x03, - 0x09, 0xd3, 0x07, 0xa5, 0x06, 0x78, 0x05, 0x4c, 0x04, 0x22, 0x03, 0xf8, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa9, 0x0f, 0x83, 0x0e, 0x5f, 0x0d, 0x3b, 0x0c, 0x19, 0x0b, 0xf8, 0x09, 0xd8, 0x08, 0xb9, 0x07, 0x9b, 0x06, 0x7e, - 0x05, 0x63, 0x04, 0x48, 0x03, 0x2f, 0x02, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xfa, 0xff, 0xff, 0x04, 0x00, - 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x0a, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x04, 0xbe, 0x0e, 0x00, - 0x00, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x01, 0x7f, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x2e, 0x03, 0x9c, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xcb, 0x03, 0xc0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x7e, - 0x03, 0x08, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x88, 0x09, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x56, 0x07, - 0xfe, 0x0d, 0x80, 0x04, 0xdd, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x16, 0x01, 0x2e, 0x07, 0x24, 0x0d, 0x00, 0x00, 0x00, - 0x00, 0xfc, 0x02, 0xb5, 0x08, 0x52, 0x0e, 0xd3, 0x03, 0x39, 0x09, 0x86, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xba, 0x03, - 0xd6, 0x08, 0xdc, 0x0d, 0xcb, 0x02, 0xa4, 0x07, 0x69, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0xb7, 0x05, 0x42, - 0x0a, 0xba, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x03, 0x77, 0x07, 0xbc, 0x0b, 0xf1, 0x0f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x04, 0x2c, 0x08, 0x34, 0x0c, 0x2d, 0x00, 0x18, 0x04, 0xf6, - 0x07, 0xc6, 0x0b, 0x89, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0xeb, 0x06, 0x8a, 0x0a, - 0x1d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x01, 0x22, 0x05, 0x94, 0x08, 0xfc, 0x0b, 0x59, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0xac, 0x02, 0xf6, 0x05, 0x36, 0x09, 0x6c, 0x0c, 0x9a, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x02, 0xda, 0x05, 0xed, 0x08, 0xf8, 0x0b, 0xfa, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xf5, - 0x01, 0xe8, 0x04, 0xd3, 0x07, 0xb6, 0x0a, 0x92, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, - 0x34, 0x03, 0xfb, 0x05, 0xbb, 0x08, 0x74, 0x0b, 0x27, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x78, 0x03, 0x18, - 0x06, 0xb1, 0x08, 0x45, 0x0b, 0xd2, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0xdc, 0x02, 0x58, 0x05, 0xcf, 0x07, - 0x41, 0x0a, 0xad, 0x0c, 0x14, 0x0f, 0x76, 0x01, 0xd3, 0x03, 0x2b, 0x06, 0x7e, 0x08, 0xcc, 0x0a, 0x16, 0x0d, 0x5a, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x01, 0xd6, 0x03, 0x0e, 0x06, 0x41, 0x08, 0x6f, 0x0a, 0x9a, 0x0c, 0xc0, 0x0e, - 0xe2, 0x00, 0x00, 0x03, 0x1a, 0x05, 0x30, 0x07, 0x43, 0x09, 0x51, 0x0b, 0x5c, 0x0d, 0x63, 0x0f, 0x66, 0x01, 0x66, - 0x03, 0x62, 0x05, 0x5b, 0x07, 0x50, 0x09, 0x42, 0x0b, 0x30, 0x0d, 0x1b, 0x0f, 0x03, 0x01, 0xe8, 0x02, 0xc9, 0x04, - 0xa8, 0x06, 0x83, 0x08, 0x5b, 0x0a, 0x30, 0x0c, 0x02, 0x0e, 0xd1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9d, 0x01, 0x67, 0x03, 0x2d, 0x05, 0xf1, 0x06, 0xb2, 0x08, 0x70, 0x0a, 0x2b, 0x0c, 0xe4, 0x0d, 0x9a, 0x0f, - 0x00, 0x00, 0x00, 0x00, 0x4e, 0x01, 0xff, 0x02, 0xad, 0x04, 0x59, 0x06, 0x02, 0x08, 0xa9, 0x09, 0x4e, 0x0b, 0xf0, - 0x0c, 0x90, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0xc8, 0x01, 0x61, 0x03, 0xf7, 0x04, - 0x8c, 0x06, 0x1e, 0x08, 0xad, 0x09, 0x3b, 0x0b, 0xc7, 0x0c, 0x50, 0x0e, 0xd7, 0x0f, 0x5c, 0x01, 0xe0, 0x02, 0x61, - 0x04, 0xe0, 0x05, 0x5d, 0x07, 0xd8, 0x08, 0x51, 0x0a, 0xc8, 0x0b, 0x3d, 0x0d, 0xb1, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x92, 0x01, 0x00, 0x03, 0x6c, 0x04, 0xd6, 0x05, 0x3e, 0x07, 0xa5, 0x08, 0x0a, 0x0a, 0x6d, 0x0b, 0xcf, - 0x0c, 0x2e, 0x0e, 0x8c, 0x0f, 0xe9, 0x00, 0x43, 0x02, 0x9d, 0x03, 0xf4, 0x04, 0x4a, 0x06, 0x9e, 0x07, 0xf1, 0x08, - 0x42, 0x0a, 0x92, 0x0b, 0xe0, 0x0c, 0x2c, 0x0e, 0x77, 0x0f, 0xc1, 0x00, 0x09, 0x02, 0x50, 0x03, 0x95, 0x04, 0xd8, - 0x05, 0x1b, 0x07, 0x5c, 0x08, 0x9b, 0x09, 0xd9, 0x0a, 0x16, 0x0c, 0x51, 0x0d, 0x8b, 0x0e, 0xc4, 0x0f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x31, 0x02, 0x66, 0x03, 0x99, 0x04, 0xcb, 0x05, 0xfc, 0x06, 0x2c, - 0x08, 0x5a, 0x09, 0x87, 0x0a, 0xb3, 0x0b, 0xdd, 0x0c, 0x07, 0x0e, 0x2f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, - 0x7c, 0x01, 0xa0, 0x02, 0xc4, 0x03, 0xe6, 0x04, 0x07, 0x06, 0x27, 0x07, 0x46, 0x08, 0x64, 0x09, 0x81, 0x0a, 0x9c, - 0x0b, 0xb7, 0x0c, 0xd0, 0x0d, 0xe8, 0x0e, 0x00, 0x10, 0x00, 0x00, 0x2a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, - 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1e, 0x00, 0x20, 0x00, 0x24, 0x00, 0x26, 0x00, 0x2a, 0x00, - 0x2e, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x5a, - 0x00, 0x62, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00, 0x92, 0x00, 0x9a, 0x00, 0xa6, 0x00, - 0xb0, 0x00, 0xbc, 0x00, 0xc8, 0x00, 0xd4, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x8a, 0xfd, 0xff, 0xff, 0x04, 0x00, 0x00, - 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, - 0x1c, 0x00, 0x20, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x30, 0x00, 0x34, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x44, - 0x00, 0x4c, 0x00, 0x50, 0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x88, 0x00, - 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xc4, 0x00, 0xd0, 0x00, 0xdc, 0x00, 0xe8, - 0x00, 0xf4, 0x00, 0x00, 0x01, 0x0c, 0x01, 0x1c, 0x01, 0x2c, 0x01, 0x00, 0x00, 0xea, 0xfd, 0xff, 0xff, 0x04, 0x00, - 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, - 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x08, 0x00, - 0x08, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, - 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, - 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4a, 0xfe, 0xff, 0xff, 0x04, - 0x00, 0x00, 0x00, 0xfa, 0x00, 0x00, 0x00, 0x7c, 0x7f, 0x79, 0x7f, 0x76, 0x7f, 0xfa, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x70, 0x7f, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x64, 0x7f, 0xe9, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x4b, 0x7f, 0xd0, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x7f, 0xa0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x7e, 0x42, 0xff, 0x00, 0x00, - 0x00, 0x00, 0xfd, 0x7d, 0x86, 0xfe, 0x04, 0x00, 0x00, 0x00, 0x87, 0x7c, 0x1d, 0xfd, 0x12, 0x00, 0x00, 0x00, 0xb6, - 0x79, 0x7f, 0xfa, 0x3e, 0x00, 0x00, 0x00, 0x73, 0x74, 0xf9, 0xf5, 0xca, 0x00, 0x00, 0x00, 0x36, 0x6b, 0x33, 0xef, - 0x32, 0x02, 0x00, 0x00, 0x9b, 0x5c, 0x87, 0xe7, 0xce, 0x04, 0x00, 0x00, 0xf0, 0x48, 0xde, 0xe2, 0xa0, 0x07, 0x00, - 0x00, 0x6e, 0x33, 0x8a, 0xe4, 0xa4, 0x08, 0x00, 0x00, 0x9c, 0x20, 0x22, 0xeb, 0x4c, 0x07, 0x00, 0x00, 0x0a, 0x13, - 0x7d, 0xf2, 0x02, 0x05, 0x00, 0x00, 0x89, 0x0a, 0x17, 0xf8, 0x06, 0x03, 0x00, 0x00, 0xa6, 0x05, 0xa0, 0xfb, 0xb4, - 0x01, 0x00, 0x00, 0xfa, 0x02, 0xac, 0xfd, 0xe8, 0x00, 0x00, 0x00, 0x8e, 0x01, 0xc7, 0xfe, 0x7a, 0x00, 0x00, 0x00, - 0xcf, 0x00, 0x5c, 0xff, 0x40, 0x00, 0x00, 0x00, 0x6b, 0x00, 0xab, 0xff, 0x22, 0x00, 0x00, 0x00, 0x38, 0x00, 0xd3, - 0xff, 0x12, 0x00, 0x00, 0x00, 0x1d, 0x00, 0xea, 0xff, 0x08, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xf3, 0xff, 0x06, 0x00, - 0x00, 0x00, 0x08, 0x00, 0xf8, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0xfd, 0xff, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xfd, 0xff, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x82, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4d, 0x01, 0x00, 0x00, - 0x92, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0xff, 0xff, 0xff, 0x04, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, - 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x02, 0x00, 0x04, 0x00, 0x05, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x13, 0x00, 0x17, 0x00, - 0x1b, 0x00, 0x20, 0x00, 0x25, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x3c, 0x00, 0x42, 0x00, 0x49, 0x00, 0x51, - 0x00, 0x58, 0x00, 0x60, 0x00, 0x68, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8d, 0x00, 0x97, 0x00, 0xa1, 0x00, - 0xac, 0x00, 0xb7, 0x00, 0xc2, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xe5, 0x00, 0xf2, 0x00, 0xff, 0x00, 0x0c, 0x01, 0x19, - 0x01, 0x27, 0x01, 0x35, 0x01, 0x43, 0x01, 0x52, 0x01, 0x61, 0x01, 0x70, 0x01, 0x7f, 0x01, 0x8f, 0x01, 0x9f, 0x01, - 0xaf, 0x01, 0xc0, 0x01, 0xd1, 0x01, 0xe2, 0x01, 0xf3, 0x01, 0x05, 0x02, 0x17, 0x02, 0x29, 0x02, 0x3c, 0x02, 0x4e, - 0x02, 0x61, 0x02, 0x75, 0x02, 0x88, 0x02, 0x9c, 0x02, 0xb0, 0x02, 0xc4, 0x02, 0xd8, 0x02, 0xed, 0x02, 0x02, 0x03, - 0x17, 0x03, 0x2c, 0x03, 0x41, 0x03, 0x57, 0x03, 0x6d, 0x03, 0x83, 0x03, 0x99, 0x03, 0xb0, 0x03, 0xc7, 0x03, 0xdd, - 0x03, 0xf4, 0x03, 0x0c, 0x04, 0x23, 0x04, 0x3b, 0x04, 0x52, 0x04, 0x6a, 0x04, 0x82, 0x04, 0x9a, 0x04, 0xb3, 0x04, - 0xcb, 0x04, 0xe4, 0x04, 0xfd, 0x04, 0x16, 0x05, 0x2f, 0x05, 0x48, 0x05, 0x61, 0x05, 0x7a, 0x05, 0x94, 0x05, 0xad, - 0x05, 0xc7, 0x05, 0xe1, 0x05, 0xfb, 0x05, 0x15, 0x06, 0x2f, 0x06, 0x49, 0x06, 0x63, 0x06, 0x7e, 0x06, 0x98, 0x06, - 0xb2, 0x06, 0xcd, 0x06, 0xe7, 0x06, 0x02, 0x07, 0x1d, 0x07, 0x37, 0x07, 0x52, 0x07, 0x6d, 0x07, 0x87, 0x07, 0xa2, - 0x07, 0xbd, 0x07, 0xd8, 0x07, 0xf3, 0x07, 0x0d, 0x08, 0x28, 0x08, 0x43, 0x08, 0x5e, 0x08, 0x79, 0x08, 0x93, 0x08, - 0xae, 0x08, 0xc9, 0x08, 0xe3, 0x08, 0xfe, 0x08, 0x19, 0x09, 0x33, 0x09, 0x4e, 0x09, 0x68, 0x09, 0x82, 0x09, 0x9d, - 0x09, 0xb7, 0x09, 0xd1, 0x09, 0xeb, 0x09, 0x05, 0x0a, 0x1f, 0x0a, 0x39, 0x0a, 0x53, 0x0a, 0x6c, 0x0a, 0x86, 0x0a, - 0x9f, 0x0a, 0xb8, 0x0a, 0xd1, 0x0a, 0xea, 0x0a, 0x03, 0x0b, 0x1c, 0x0b, 0x35, 0x0b, 0x4d, 0x0b, 0x66, 0x0b, 0x7e, - 0x0b, 0x96, 0x0b, 0xae, 0x0b, 0xc5, 0x0b, 0xdd, 0x0b, 0xf4, 0x0b, 0x0c, 0x0c, 0x23, 0x0c, 0x39, 0x0c, 0x50, 0x0c, - 0x67, 0x0c, 0x7d, 0x0c, 0x93, 0x0c, 0xa9, 0x0c, 0xbf, 0x0c, 0xd4, 0x0c, 0xe9, 0x0c, 0xfe, 0x0c, 0x13, 0x0d, 0x28, - 0x0d, 0x3c, 0x0d, 0x50, 0x0d, 0x64, 0x0d, 0x78, 0x0d, 0x8b, 0x0d, 0x9f, 0x0d, 0xb2, 0x0d, 0xc4, 0x0d, 0xd7, 0x0d, - 0xe9, 0x0d, 0xfb, 0x0d, 0x0d, 0x0e, 0x1e, 0x0e, 0x2f, 0x0e, 0x40, 0x0e, 0x51, 0x0e, 0x61, 0x0e, 0x71, 0x0e, 0x81, - 0x0e, 0x90, 0x0e, 0x9f, 0x0e, 0xae, 0x0e, 0xbd, 0x0e, 0xcb, 0x0e, 0xd9, 0x0e, 0xe7, 0x0e, 0xf4, 0x0e, 0x01, 0x0f, - 0x0e, 0x0f, 0x1b, 0x0f, 0x27, 0x0f, 0x33, 0x0f, 0x3e, 0x0f, 0x49, 0x0f, 0x54, 0x0f, 0x5f, 0x0f, 0x69, 0x0f, 0x73, - 0x0f, 0x7d, 0x0f, 0x86, 0x0f, 0x8f, 0x0f, 0x98, 0x0f, 0xa0, 0x0f, 0xa8, 0x0f, 0xaf, 0x0f, 0xb7, 0x0f, 0xbe, 0x0f, - 0xc4, 0x0f, 0xcb, 0x0f, 0xd0, 0x0f, 0xd6, 0x0f, 0xdb, 0x0f, 0xe0, 0x0f, 0xe5, 0x0f, 0xe9, 0x0f, 0xed, 0x0f, 0xf0, - 0x0f, 0xf3, 0x0f, 0xf6, 0x0f, 0xf9, 0x0f, 0xfb, 0x0f, 0xfc, 0x0f, 0xfe, 0x0f, 0xff, 0x0f, 0x00, 0x10, 0x00, 0x10, - 0x00, 0x10, 0x00, 0x10, 0xff, 0x0f, 0xfe, 0x0f, 0xfc, 0x0f, 0xfb, 0x0f, 0xf9, 0x0f, 0xf6, 0x0f, 0xf3, 0x0f, 0xf0, - 0x0f, 0xed, 0x0f, 0xe9, 0x0f, 0xe5, 0x0f, 0xe0, 0x0f, 0xdb, 0x0f, 0xd6, 0x0f, 0xd0, 0x0f, 0xcb, 0x0f, 0xc4, 0x0f, - 0xbe, 0x0f, 0xb7, 0x0f, 0xaf, 0x0f, 0xa8, 0x0f, 0xa0, 0x0f, 0x98, 0x0f, 0x8f, 0x0f, 0x86, 0x0f, 0x7d, 0x0f, 0x73, - 0x0f, 0x69, 0x0f, 0x5f, 0x0f, 0x54, 0x0f, 0x49, 0x0f, 0x3e, 0x0f, 0x33, 0x0f, 0x27, 0x0f, 0x1b, 0x0f, 0x0e, 0x0f, - 0x01, 0x0f, 0xf4, 0x0e, 0xe7, 0x0e, 0xd9, 0x0e, 0xcb, 0x0e, 0xbd, 0x0e, 0xae, 0x0e, 0x9f, 0x0e, 0x90, 0x0e, 0x81, - 0x0e, 0x71, 0x0e, 0x61, 0x0e, 0x51, 0x0e, 0x40, 0x0e, 0x2f, 0x0e, 0x1e, 0x0e, 0x0d, 0x0e, 0xfb, 0x0d, 0xe9, 0x0d, - 0xd7, 0x0d, 0xc4, 0x0d, 0xb2, 0x0d, 0x9f, 0x0d, 0x8b, 0x0d, 0x78, 0x0d, 0x64, 0x0d, 0x50, 0x0d, 0x3c, 0x0d, 0x28, - 0x0d, 0x13, 0x0d, 0xfe, 0x0c, 0xe9, 0x0c, 0xd4, 0x0c, 0xbf, 0x0c, 0xa9, 0x0c, 0x93, 0x0c, 0x7d, 0x0c, 0x67, 0x0c, - 0x50, 0x0c, 0x39, 0x0c, 0x23, 0x0c, 0x0c, 0x0c, 0xf4, 0x0b, 0xdd, 0x0b, 0xc5, 0x0b, 0xae, 0x0b, 0x96, 0x0b, 0x7e, - 0x0b, 0x66, 0x0b, 0x4d, 0x0b, 0x35, 0x0b, 0x1c, 0x0b, 0x03, 0x0b, 0xea, 0x0a, 0xd1, 0x0a, 0xb8, 0x0a, 0x9f, 0x0a, - 0x86, 0x0a, 0x6c, 0x0a, 0x53, 0x0a, 0x39, 0x0a, 0x1f, 0x0a, 0x05, 0x0a, 0xeb, 0x09, 0xd1, 0x09, 0xb7, 0x09, 0x9d, - 0x09, 0x82, 0x09, 0x68, 0x09, 0x4e, 0x09, 0x33, 0x09, 0x19, 0x09, 0xfe, 0x08, 0xe3, 0x08, 0xc9, 0x08, 0xae, 0x08, - 0x93, 0x08, 0x79, 0x08, 0x5e, 0x08, 0x43, 0x08, 0x28, 0x08, 0x0d, 0x08, 0xf3, 0x07, 0xd8, 0x07, 0xbd, 0x07, 0xa2, - 0x07, 0x87, 0x07, 0x6d, 0x07, 0x52, 0x07, 0x37, 0x07, 0x1d, 0x07, 0x02, 0x07, 0xe7, 0x06, 0xcd, 0x06, 0xb2, 0x06, - 0x98, 0x06, 0x7e, 0x06, 0x63, 0x06, 0x49, 0x06, 0x2f, 0x06, 0x15, 0x06, 0xfb, 0x05, 0xe1, 0x05, 0xc7, 0x05, 0xad, - 0x05, 0x94, 0x05, 0x7a, 0x05, 0x61, 0x05, 0x48, 0x05, 0x2f, 0x05, 0x16, 0x05, 0xfd, 0x04, 0xe4, 0x04, 0xcb, 0x04, - 0xb3, 0x04, 0x9a, 0x04, 0x82, 0x04, 0x6a, 0x04, 0x52, 0x04, 0x3b, 0x04, 0x23, 0x04, 0x0c, 0x04, 0xf4, 0x03, 0xdd, - 0x03, 0xc7, 0x03, 0xb0, 0x03, 0x99, 0x03, 0x83, 0x03, 0x6d, 0x03, 0x57, 0x03, 0x41, 0x03, 0x2c, 0x03, 0x17, 0x03, - 0x02, 0x03, 0xed, 0x02, 0xd8, 0x02, 0xc4, 0x02, 0xb0, 0x02, 0x9c, 0x02, 0x88, 0x02, 0x75, 0x02, 0x61, 0x02, 0x4e, - 0x02, 0x3c, 0x02, 0x29, 0x02, 0x17, 0x02, 0x05, 0x02, 0xf3, 0x01, 0xe2, 0x01, 0xd1, 0x01, 0xc0, 0x01, 0xaf, 0x01, - 0x9f, 0x01, 0x8f, 0x01, 0x7f, 0x01, 0x70, 0x01, 0x61, 0x01, 0x52, 0x01, 0x43, 0x01, 0x35, 0x01, 0x27, 0x01, 0x19, - 0x01, 0x0c, 0x01, 0xff, 0x00, 0xf2, 0x00, 0xe5, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xac, 0x00, - 0xa1, 0x00, 0x97, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x68, 0x00, 0x60, 0x00, 0x58, 0x00, 0x51, - 0x00, 0x49, 0x00, 0x42, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x30, 0x00, 0x2a, 0x00, 0x25, 0x00, 0x20, 0x00, 0x1b, 0x00, - 0x17, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xee, 0xff, 0xff, 0x38, 0xee, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x4d, 0x4c, - 0x49, 0x52, 0x20, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x04, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0xf4, 0x05, 0x00, 0x00, 0xf8, 0x05, 0x00, - 0x00, 0xfc, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, - 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x68, 0x05, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, 0xc8, 0x04, 0x00, 0x00, 0x70, - 0x04, 0x00, 0x00, 0x4c, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0xc8, 0x03, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x00, - 0x4c, 0x03, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x6c, 0x01, 0x00, - 0x00, 0x48, 0x01, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x78, 0x00, - 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xf6, 0xfa, 0xff, 0xff, 0x0c, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x16, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, - 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3a, 0xfb, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0xa6, 0xfc, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, 0x00, - 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x68, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x27, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd6, 0xfc, 0xff, 0xff, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x98, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, - 0x00, 0x12, 0x00, 0x00, 0x00, 0x06, 0xfd, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x10, 0x00, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xc8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x25, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x36, 0xfd, 0xff, 0xff, - 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, - 0x00, 0xf8, 0xef, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, - 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x84, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x30, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x63, 0x61, 0x6c, - 0x65, 0x00, 0x02, 0x24, 0x0f, 0x02, 0x01, 0x02, 0x03, 0x40, 0x04, 0x04, 0x04, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xdc, 0xfc, 0xff, 0xff, 0x10, 0x00, 0x00, - 0x00, 0x24, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x73, 0x6e, - 0x72, 0x5f, 0x73, 0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x0b, 0x01, 0x01, 0x01, 0x06, 0x04, 0x02, 0x24, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x20, 0xfd, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xec, 0x00, 0x00, - 0x00, 0x0a, 0x00, 0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f, - 0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, - 0x00, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, - 0x67, 0x00, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x69, 0x6e, 0x67, 0x00, 0x6d, 0x69, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x6d, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x6f, 0x6e, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x5f, 0x73, 0x6d, - 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x00, 0x73, - 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x00, 0x73, 0x70, 0x65, 0x63, 0x74, - 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, - 0x73, 0x00, 0x09, 0xa5, 0x88, 0x75, 0x6d, 0x59, 0x4d, 0x3a, 0x31, 0x23, 0x09, 0x00, 0x01, 0x00, 0x09, 0x00, 0x29, - 0x3c, 0xd7, 0x03, 0x00, 0x00, 0x33, 0x03, 0x28, 0x00, 0x67, 0x3e, 0x99, 0x01, 0x0a, 0x00, 0x0e, 0x00, 0x05, 0x05, - 0x69, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x1b, 0x25, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x20, 0xfe, 0xff, 0xff, 0x10, 0x00, - 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x54, 0xfe, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, - 0x00, 0x2c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6d, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x00, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x28, 0x04, 0x02, 0x24, 0x01, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0xfe, 0xff, - 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0xca, 0xff, 0xff, 0xff, 0x14, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0a, 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8c, 0xf2, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x0b, 0x00, - 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0xf2, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, - 0x00, 0xfe, 0xfe, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x64, 0xff, 0xff, 0xff, 0x10, - 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x00, 0x02, 0x17, 0x0e, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0xf1, 0x00, 0x05, 0x00, 0x05, 0x05, - 0x06, 0x25, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, - 0x00, 0x00, 0x00, 0xb8, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x54, 0x00, 0x66, 0x66, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, - 0x68, 0x00, 0x02, 0x0e, 0x0d, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x02, 0x05, 0x05, 0x06, 0x25, - 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x10, - 0x00, 0x14, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x24, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, - 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x73, - 0x68, 0x69, 0x66, 0x74, 0x00, 0x01, 0x07, 0x01, 0x01, 0x01, 0x0c, 0x04, 0x02, 0x24, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0xc4, 0x0a, - 0x00, 0x00, 0x74, 0x0a, 0x00, 0x00, 0x3c, 0x0a, 0x00, 0x00, 0x04, 0x0a, 0x00, 0x00, 0xd0, 0x09, 0x00, 0x00, 0x88, - 0x09, 0x00, 0x00, 0x40, 0x09, 0x00, 0x00, 0xfc, 0x08, 0x00, 0x00, 0xb8, 0x08, 0x00, 0x00, 0x6c, 0x08, 0x00, 0x00, - 0x20, 0x08, 0x00, 0x00, 0xd4, 0x07, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x3c, 0x07, 0x00, 0x00, 0xf8, 0x06, 0x00, - 0x00, 0xb8, 0x06, 0x00, 0x00, 0x84, 0x06, 0x00, 0x00, 0x50, 0x06, 0x00, 0x00, 0x1c, 0x06, 0x00, 0x00, 0xd8, 0x05, - 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x14, 0x05, 0x00, 0x00, 0xd8, 0x04, 0x00, 0x00, 0x98, - 0x04, 0x00, 0x00, 0x60, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, - 0x6c, 0x03, 0x00, 0x00, 0x1c, 0x03, 0x00, 0x00, 0xc4, 0x02, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x2c, 0x02, 0x00, - 0x00, 0xe4, 0x01, 0x00, 0x00, 0xac, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x08, 0x01, - 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfe, - 0xf5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x44, 0xf5, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x50, 0x61, 0x72, - 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x64, 0x43, 0x61, 0x6c, 0x6c, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3e, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x84, 0xf5, 0xff, 0xff, - 0x0d, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x7a, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0xc0, - 0xf5, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, - 0x00, 0xbe, 0xf6, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x04, 0xf6, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x61, - 0x64, 0x64, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf2, 0xf6, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x18, 0x00, 0x00, 0x00, 0x38, 0xf6, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, - 0x74, 0x65, 0x44, 0x69, 0x76, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x2a, 0xf7, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x10, 0x00, 0x00, 0x00, 0x70, 0xf6, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x61, 0x64, 0x64, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x28, 0x00, 0x00, 0x00, 0x5a, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, - 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00, 0xa0, 0xf6, 0xff, 0xff, 0x03, - 0x00, 0x00, 0x00, 0x6d, 0x75, 0x6c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x8a, 0xf7, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x14, 0x00, 0x00, 0x00, 0xd0, 0xf6, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x32, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xbe, 0xf7, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, - 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00, - 0x04, 0xf7, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x6c, 0x6f, 0x67, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x02, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x00, 0x48, 0xf7, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, - 0x00, 0x3a, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x38, 0x00, 0x00, 0x00, 0x80, 0xf7, 0xff, 0xff, 0x28, 0x00, 0x00, 0x00, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73, - 0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x31, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x92, 0xf8, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x34, - 0x00, 0x00, 0x00, 0xd8, 0xf7, 0xff, 0xff, 0x27, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x72, 0x61, 0x6c, - 0x5f, 0x73, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x00, 0xe6, 0xf8, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x2c, 0x00, 0x00, 0x00, 0x2c, 0xf8, 0xff, 0xff, 0x1e, 0x00, 0x00, 0x00, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x5f, - 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x00, 0x00, 0x32, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x78, 0xf8, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x72, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x14, 0x00, 0x00, 0x00, 0xb8, - 0xf8, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x00, 0x00, 0xa6, 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, - 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xec, 0xf8, 0xff, 0xff, 0x06, 0x00, - 0x00, 0x00, 0x63, 0x6f, 0x6e, 0x63, 0x61, 0x74, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0xda, - 0xf9, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x20, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, - 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xec, 0x00, - 0x00, 0x00, 0x16, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x1a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xf9, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, - 0x43, 0x61, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x4a, 0xfa, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x1c, 0x00, 0x00, 0x00, 0x90, 0xf9, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x6c, 0x5f, 0x65, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, - 0x86, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x00, 0x00, 0x00, 0xcc, 0xf9, 0xff, 0xff, 0x0b, 0x00, 0x00, 0x00, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x66, 0x66, 0x74, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0xbe, - 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x04, 0xfa, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfa, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x00, 0x00, 0x00, 0x44, 0xfa, 0xff, 0xff, - 0x15, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x66, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x6f, - 0x5f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x42, 0xfb, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x88, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68, - 0x61, 0x70, 0x65, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x76, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, - 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1c, 0x00, - 0x00, 0x00, 0xbc, 0xfa, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x77, 0x69, - 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, - 0xb6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xfc, 0xfa, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43, 0x6f, - 0x6e, 0x73, 0x74, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xfb, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, - 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, - 0x2c, 0xfb, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x16, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x5c, 0xfb, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x43, - 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, - 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, - 0x00, 0x8c, 0xfb, 0xff, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x52, 0x65, 0x73, 0x68, 0x61, 0x70, 0x65, 0x2f, 0x73, 0x68, - 0x61, 0x70, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x82, 0xfc, 0xff, 0xff, 0x00, - 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x24, 0x00, 0x00, 0x00, 0xc8, 0xfb, 0xff, 0xff, 0x17, 0x00, 0x00, 0x00, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x62, 0x79, - 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x2f, 0x79, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xc2, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x08, 0xfc, 0xff, 0xff, 0x18, 0x00, 0x00, 0x00, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, - 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x0a, 0xfd, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x50, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, - 0x74, 0x5f, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00, 0x52, 0xfd, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, - 0x00, 0x00, 0x00, 0x98, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x32, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x9a, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0xe0, - 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x5f, 0x62, 0x61, 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x33, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x29, 0x00, 0x00, 0x00, 0xe2, 0xfd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x28, 0x00, 0x00, 0x00, 0x28, 0xfd, 0xff, 0xff, 0x1a, - 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x61, - 0x6e, 0x6b, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x5f, 0x34, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, - 0x00, 0x2a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x70, 0xfd, 0xff, 0xff, 0x11, 0x00, 0x00, 0x00, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x5f, 0x70, 0x63, 0x61, 0x6e, 0x2f, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x6a, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, - 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0xb0, 0xfd, - 0xff, 0xff, 0x13, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, - 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xaa, 0xfe, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x24, 0x00, 0x00, 0x00, 0xf0, 0xfd, 0xff, 0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, - 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xee, 0xfe, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x34, 0xfe, 0xff, - 0xff, 0x15, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x69, 0x64, 0x65, 0x64, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x2f, - 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x32, - 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x78, 0xfe, 0xff, 0xff, 0x06, 0x00, 0x00, 0x00, 0x43, 0x61, 0x73, - 0x74, 0x5f, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, - 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xa8, - 0xfe, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x96, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0xdc, 0xfe, 0xff, 0xff, 0x07, 0x00, - 0x00, 0x00, 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x5f, 0x31, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xca, - 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x14, 0x00, 0x00, 0x00, 0x10, 0xff, 0xff, 0xff, 0x05, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x6e, - 0x73, 0x74, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x1c, 0x00, - 0x18, 0x00, 0x17, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x16, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x2c, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x00, 0x00, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x5f, - 0x66, 0x72, 0x61, 0x6d, 0x65, 0x3a, 0x30, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, - 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1c, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, - 0xa4, 0x01, 0x00, 0x00, 0x7c, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, - 0x00, 0x10, 0x01, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x50, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x50, 0xfe, 0xff, 0xff, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x5c, 0xfe, 0xff, 0xff, - 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x68, 0xfe, 0xff, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2a, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7c, 0xfe, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x12, 0x70, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x13, - 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, - 0x4c, 0x6f, 0x67, 0x00, 0x98, 0xfe, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x0a, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x50, 0x43, 0x41, 0x4e, 0x00, 0x00, 0xb8, 0xfe, - 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x23, 0x00, 0x00, 0x00, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x70, 0x65, 0x63, - 0x74, 0x72, 0x61, 0x6c, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0xf0, 0xfe, 0xff, - 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x1a, 0x00, 0x00, 0x00, 0x53, 0x69, - 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x53, 0x71, 0x75, 0x61, 0x72, - 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x20, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x42, 0x61, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x6c, 0xff, 0xff, 0xff, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x0c, 0x00, 0x10, 0x00, 0x0f, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x35, 0x7c, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x45, 0x6e, 0x65, 0x72, 0x67, 0x79, 0x00, 0x00, - 0x00, 0x00, 0xa0, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0a, - 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x52, 0x66, 0x66, 0x74, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, - 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x12, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, - 0x6e, 0x61, 0x6c, 0x46, 0x66, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x00, 0x00, 0x0c, 0x00, - 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x16, 0x0c, 0x00, 0x10, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x0c, 0x00, 0x00, 0x00, 0x53, 0x69, 0x67, - 0x6e, 0x61, 0x6c, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x00}; - -} // namespace micro_wake_word -} // namespace esphome - -#endif // USE_ESP_IDF diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index 5a89708127..b58c7ec434 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -1,12 +1,5 @@ #include "micro_wake_word.h" - -/** - * This is a workaround until we can figure out a way to get - * the tflite-micro idf component code available in CI - * - * */ -// -#ifndef CLANG_TIDY +#include "streaming_model.h" #ifdef USE_ESP_IDF @@ -14,13 +7,13 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "audio_preprocessor_int8_model_data.h" +#include +#include #include #include #include -#include #include namespace esphome { @@ -29,9 +22,9 @@ namespace micro_wake_word { static const char *const TAG = "micro_wake_word"; static const size_t SAMPLE_RATE_HZ = 16000; // 16 kHz -static const size_t BUFFER_LENGTH = 500; // 0.5 seconds +static const size_t BUFFER_LENGTH = 64; // 0.064 seconds static const size_t BUFFER_SIZE = SAMPLE_RATE_HZ / 1000 * BUFFER_LENGTH; -static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms +static const size_t INPUT_BUFFER_SIZE = 16 * SAMPLE_RATE_HZ / 1000; // 16ms * 16kHz / 1000ms float MicroWakeWord::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } @@ -56,58 +49,56 @@ static const LogString *micro_wake_word_state_to_string(State state) { void MicroWakeWord::dump_config() { ESP_LOGCONFIG(TAG, "microWakeWord:"); - ESP_LOGCONFIG(TAG, " Wake Word: %s", this->get_wake_word().c_str()); - ESP_LOGCONFIG(TAG, " Probability cutoff: %.3f", this->probability_cutoff_); - ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_average_size_); + ESP_LOGCONFIG(TAG, " models:"); + for (auto &model : this->wake_word_models_) { + model.log_model_config(); + } +#ifdef USE_MICRO_WAKE_WORD_VAD + this->vad_model_->log_model_config(); +#endif } void MicroWakeWord::setup() { ESP_LOGCONFIG(TAG, "Setting up microWakeWord..."); - if (!this->initialize_models()) { - ESP_LOGE(TAG, "Failed to initialize models"); - this->mark_failed(); - return; - } - - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - this->input_buffer_ = allocator.allocate(INPUT_BUFFER_SIZE * sizeof(int16_t)); - if (this->input_buffer_ == nullptr) { - ESP_LOGW(TAG, "Could not allocate input buffer"); - this->mark_failed(); - return; - } - - this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t)); - if (this->ring_buffer_ == nullptr) { - ESP_LOGW(TAG, "Could not allocate ring buffer"); + if (!this->register_streaming_ops_(this->streaming_op_resolver_)) { this->mark_failed(); return; } ESP_LOGCONFIG(TAG, "Micro Wake Word initialized"); + + this->frontend_config_.window.size_ms = FEATURE_DURATION_MS; + this->frontend_config_.window.step_size_ms = this->features_step_size_; + this->frontend_config_.filterbank.num_channels = PREPROCESSOR_FEATURE_SIZE; + this->frontend_config_.filterbank.lower_band_limit = 125.0; + this->frontend_config_.filterbank.upper_band_limit = 7500.0; + this->frontend_config_.noise_reduction.smoothing_bits = 10; + this->frontend_config_.noise_reduction.even_smoothing = 0.025; + this->frontend_config_.noise_reduction.odd_smoothing = 0.06; + this->frontend_config_.noise_reduction.min_signal_remaining = 0.05; + this->frontend_config_.pcan_gain_control.enable_pcan = 1; + this->frontend_config_.pcan_gain_control.strength = 0.95; + this->frontend_config_.pcan_gain_control.offset = 80.0; + this->frontend_config_.pcan_gain_control.gain_bits = 21; + this->frontend_config_.log_scale.enable_log = 1; + this->frontend_config_.log_scale.scale_shift = 6; } -int MicroWakeWord::read_microphone_() { - size_t bytes_read = this->microphone_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); - if (bytes_read == 0) { - return 0; - } - - size_t bytes_free = this->ring_buffer_->free(); - - if (bytes_free < bytes_read) { - ESP_LOGW(TAG, - "Not enough free bytes in ring buffer to store incoming audio data (free bytes=%d, incoming bytes=%d). " - "Resetting the ring buffer. Wake word detection accuracy will be reduced.", - bytes_free, bytes_read); - - this->ring_buffer_->reset(); - } - - return this->ring_buffer_->write((void *) this->input_buffer_, bytes_read); +void MicroWakeWord::add_wake_word_model(const uint8_t *model_start, float probability_cutoff, + size_t sliding_window_average_size, const std::string &wake_word, + size_t tensor_arena_size) { + this->wake_word_models_.emplace_back(model_start, probability_cutoff, sliding_window_average_size, wake_word, + tensor_arena_size); } +#ifdef USE_MICRO_WAKE_WORD_VAD +void MicroWakeWord::add_vad_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, + size_t tensor_arena_size) { + this->vad_model_ = make_unique(model_start, probability_cutoff, sliding_window_size, tensor_arena_size); +} +#endif + void MicroWakeWord::loop() { switch (this->state_) { case State::IDLE: @@ -124,9 +115,12 @@ void MicroWakeWord::loop() { } break; case State::DETECTING_WAKE_WORD: - this->read_microphone_(); - if (this->detect_wake_word_()) { - ESP_LOGD(TAG, "Wake Word Detected"); + while (!this->has_enough_samples_()) { + this->read_microphone_(); + } + this->update_model_probabilities_(); + if (this->detect_wake_words_()) { + ESP_LOGD(TAG, "Wake Word '%s' Detected", (this->detected_wake_word_).c_str()); this->detected_ = true; this->set_state_(State::STOP_MICROPHONE); } @@ -136,13 +130,16 @@ void MicroWakeWord::loop() { this->microphone_->stop(); this->set_state_(State::STOPPING_MICROPHONE); this->high_freq_.stop(); + this->unload_models_(); + this->deallocate_buffers_(); break; case State::STOPPING_MICROPHONE: if (this->microphone_->is_stopped()) { this->set_state_(State::IDLE); if (this->detected_) { + this->wake_word_detected_trigger_->trigger(this->detected_wake_word_); this->detected_ = false; - this->wake_word_detected_trigger_->trigger(this->wake_word_); + this->detected_wake_word_ = ""; } } break; @@ -150,14 +147,34 @@ void MicroWakeWord::loop() { } void MicroWakeWord::start() { + if (!this->is_ready()) { + ESP_LOGW(TAG, "Wake word detection can't start as the component hasn't been setup yet"); + return; + } + if (this->is_failed()) { ESP_LOGW(TAG, "Wake word component is marked as failed. Please check setup logs"); return; } + + if (!this->load_models_() || !this->allocate_buffers_()) { + ESP_LOGE(TAG, "Failed to load the wake word model(s) or allocate buffers"); + this->status_set_error(); + } else { + this->status_clear_error(); + } + + if (this->status_has_error()) { + ESP_LOGW(TAG, "Wake word component has an error. Please check logs"); + return; + } + if (this->state_ != State::IDLE) { ESP_LOGW(TAG, "Wake word is already running"); return; } + + this->reset_states_(); this->set_state_(State::START_MICROPHONE); } @@ -179,289 +196,218 @@ void MicroWakeWord::set_state_(State state) { this->state_ = state; } -bool MicroWakeWord::initialize_models() { - ExternalRAMAllocator arena_allocator(ExternalRAMAllocator::ALLOW_FAILURE); - ExternalRAMAllocator features_allocator(ExternalRAMAllocator::ALLOW_FAILURE); +size_t MicroWakeWord::read_microphone_() { + size_t bytes_read = this->microphone_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); + if (bytes_read == 0) { + return 0; + } + + size_t bytes_free = this->ring_buffer_->free(); + + if (bytes_free < bytes_read) { + ESP_LOGW(TAG, + "Not enough free bytes in ring buffer to store incoming audio data (free bytes=%d, incoming bytes=%d). " + "Resetting the ring buffer. Wake word detection accuracy will be reduced.", + bytes_free, bytes_read); + + this->ring_buffer_->reset(); + } + + return this->ring_buffer_->write((void *) this->input_buffer_, bytes_read); +} + +bool MicroWakeWord::allocate_buffers_() { ExternalRAMAllocator audio_samples_allocator(ExternalRAMAllocator::ALLOW_FAILURE); - this->streaming_tensor_arena_ = arena_allocator.allocate(STREAMING_MODEL_ARENA_SIZE); - if (this->streaming_tensor_arena_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate the streaming model's tensor arena."); - return false; + if (this->input_buffer_ == nullptr) { + this->input_buffer_ = audio_samples_allocator.allocate(INPUT_BUFFER_SIZE * sizeof(int16_t)); + if (this->input_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate input buffer"); + return false; + } } - this->streaming_var_arena_ = arena_allocator.allocate(STREAMING_MODEL_VARIABLE_ARENA_SIZE); - if (this->streaming_var_arena_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate the streaming model variable's tensor arena."); - return false; - } - - this->preprocessor_tensor_arena_ = arena_allocator.allocate(PREPROCESSOR_ARENA_SIZE); - if (this->preprocessor_tensor_arena_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate the audio preprocessor model's tensor arena."); - return false; - } - - this->new_features_data_ = features_allocator.allocate(PREPROCESSOR_FEATURE_SIZE); - if (this->new_features_data_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate the audio features buffer."); - return false; - } - - this->preprocessor_audio_buffer_ = audio_samples_allocator.allocate(SAMPLE_DURATION_COUNT); if (this->preprocessor_audio_buffer_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate the audio preprocessor's buffer."); - return false; + this->preprocessor_audio_buffer_ = audio_samples_allocator.allocate(this->new_samples_to_get_()); + if (this->preprocessor_audio_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the audio preprocessor's buffer."); + return false; + } } - this->preprocessor_model_ = tflite::GetModel(G_AUDIO_PREPROCESSOR_INT8_TFLITE); - if (this->preprocessor_model_->version() != TFLITE_SCHEMA_VERSION) { - ESP_LOGE(TAG, "Wake word's audio preprocessor model's schema is not supported"); - return false; - } - - this->streaming_model_ = tflite::GetModel(this->model_start_); - if (this->streaming_model_->version() != TFLITE_SCHEMA_VERSION) { - ESP_LOGE(TAG, "Wake word's streaming model's schema is not supported"); - return false; - } - - static tflite::MicroMutableOpResolver<18> preprocessor_op_resolver; - static tflite::MicroMutableOpResolver<17> streaming_op_resolver; - - if (!this->register_preprocessor_ops_(preprocessor_op_resolver)) - return false; - if (!this->register_streaming_ops_(streaming_op_resolver)) - return false; - - tflite::MicroAllocator *ma = - tflite::MicroAllocator::Create(this->streaming_var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE); - this->mrv_ = tflite::MicroResourceVariables::Create(ma, 15); - - static tflite::MicroInterpreter static_preprocessor_interpreter( - this->preprocessor_model_, preprocessor_op_resolver, this->preprocessor_tensor_arena_, PREPROCESSOR_ARENA_SIZE); - - static tflite::MicroInterpreter static_streaming_interpreter(this->streaming_model_, streaming_op_resolver, - this->streaming_tensor_arena_, - STREAMING_MODEL_ARENA_SIZE, this->mrv_); - - this->preprocessor_interperter_ = &static_preprocessor_interpreter; - this->streaming_interpreter_ = &static_streaming_interpreter; - - // Allocate tensors for each models. - if (this->preprocessor_interperter_->AllocateTensors() != kTfLiteOk) { - ESP_LOGE(TAG, "Failed to allocate tensors for the audio preprocessor"); - return false; - } - if (this->streaming_interpreter_->AllocateTensors() != kTfLiteOk) { - ESP_LOGE(TAG, "Failed to allocate tensors for the streaming model"); - return false; - } - - // Verify input tensor matches expected values - TfLiteTensor *input = this->streaming_interpreter_->input(0); - if ((input->dims->size != 3) || (input->dims->data[0] != 1) || (input->dims->data[0] != 1) || - (input->dims->data[1] != 1) || (input->dims->data[2] != PREPROCESSOR_FEATURE_SIZE)) { - ESP_LOGE(TAG, "Wake word detection model tensor input dimensions is not 1x1x%u", input->dims->data[2]); - return false; - } - - if (input->type != kTfLiteInt8) { - ESP_LOGE(TAG, "Wake word detection model tensor input is not int8."); - return false; - } - - // Verify output tensor matches expected values - TfLiteTensor *output = this->streaming_interpreter_->output(0); - if ((output->dims->size != 2) || (output->dims->data[0] != 1) || (output->dims->data[1] != 1)) { - ESP_LOGE(TAG, "Wake word detection model tensor output dimensions is not 1x1."); - } - - if (output->type != kTfLiteUInt8) { - ESP_LOGE(TAG, "Wake word detection model tensor input is not uint8."); - return false; - } - - this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0); - - return true; -} - -bool MicroWakeWord::update_features_() { - // Retrieve strided audio samples - int16_t *audio_samples = nullptr; - if (!this->stride_audio_samples_(&audio_samples)) { - return false; - } - - // Compute the features for the newest audio samples - if (!this->generate_single_feature_(audio_samples, SAMPLE_DURATION_COUNT, this->new_features_data_)) { - return false; + if (this->ring_buffer_ == nullptr) { + this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t)); + if (this->ring_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate ring buffer"); + return false; + } } return true; } -float MicroWakeWord::perform_streaming_inference_() { - TfLiteTensor *input = this->streaming_interpreter_->input(0); - - size_t bytes_to_copy = input->bytes; - - memcpy((void *) (tflite::GetTensorData(input)), (const void *) (this->new_features_data_), bytes_to_copy); - - uint32_t prior_invoke = millis(); - - TfLiteStatus invoke_status = this->streaming_interpreter_->Invoke(); - if (invoke_status != kTfLiteOk) { - ESP_LOGW(TAG, "Streaming Interpreter Invoke failed"); - return false; - } - - ESP_LOGV(TAG, "Streaming Inference Latency=%" PRIu32 " ms", (millis() - prior_invoke)); - - TfLiteTensor *output = this->streaming_interpreter_->output(0); - - return static_cast(output->data.uint8[0]) / 255.0; +void MicroWakeWord::deallocate_buffers_() { + ExternalRAMAllocator audio_samples_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + audio_samples_allocator.deallocate(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t)); + this->input_buffer_ = nullptr; + audio_samples_allocator.deallocate(this->preprocessor_audio_buffer_, this->new_samples_to_get_()); + this->preprocessor_audio_buffer_ = nullptr; } -bool MicroWakeWord::detect_wake_word_() { - // Preprocess the newest audio samples into features - if (!this->update_features_()) { +bool MicroWakeWord::load_models_() { + // Setup preprocesor feature generator + if (!FrontendPopulateState(&this->frontend_config_, &this->frontend_state_, AUDIO_SAMPLE_FREQUENCY)) { + ESP_LOGD(TAG, "Failed to populate frontend state"); + FrontendFreeStateContents(&this->frontend_state_); return false; } - // Perform inference - float streaming_prob = this->perform_streaming_inference_(); + // Setup streaming models + for (auto &model : this->wake_word_models_) { + if (!model.load_model(this->streaming_op_resolver_)) { + ESP_LOGE(TAG, "Failed to initialize a wake word model."); + return false; + } + } +#ifdef USE_MICRO_WAKE_WORD_VAD + if (!this->vad_model_->load_model(this->streaming_op_resolver_)) { + ESP_LOGE(TAG, "Failed to initialize VAD model."); + return false; + } +#endif - // Add the most recent probability to the sliding window - this->recent_streaming_probabilities_[this->last_n_index_] = streaming_prob; - ++this->last_n_index_; - if (this->last_n_index_ == this->sliding_window_average_size_) - this->last_n_index_ = 0; + return true; +} - float sum = 0.0; - for (auto &prob : this->recent_streaming_probabilities_) { - sum += prob; +void MicroWakeWord::unload_models_() { + FrontendFreeStateContents(&this->frontend_state_); + + for (auto &model : this->wake_word_models_) { + model.unload_model(); + } +#ifdef USE_MICRO_WAKE_WORD_VAD + this->vad_model_->unload_model(); +#endif +} + +void MicroWakeWord::update_model_probabilities_() { + int8_t audio_features[PREPROCESSOR_FEATURE_SIZE]; + + if (!this->generate_features_for_window_(audio_features)) { + return; } - float sliding_window_average = sum / static_cast(this->sliding_window_average_size_); - - // Ensure we have enough samples since the last positive detection + // Increase the counter since the last positive detection this->ignore_windows_ = std::min(this->ignore_windows_ + 1, 0); + + for (auto &model : this->wake_word_models_) { + // Perform inference + model.perform_streaming_inference(audio_features); + } +#ifdef USE_MICRO_WAKE_WORD_VAD + this->vad_model_->perform_streaming_inference(audio_features); +#endif +} + +bool MicroWakeWord::detect_wake_words_() { + // Verify we have processed samples since the last positive detection if (this->ignore_windows_ < 0) { return false; } - // Detect the wake word if the sliding window average is above the cutoff - if (sliding_window_average > this->probability_cutoff_) { - this->ignore_windows_ = -MIN_SLICES_BEFORE_DETECTION; - for (auto &prob : this->recent_streaming_probabilities_) { - prob = 0; - } +#ifdef USE_MICRO_WAKE_WORD_VAD + bool vad_state = this->vad_model_->determine_detected(); +#endif - ESP_LOGD(TAG, "Wake word sliding average probability is %.3f and most recent probability is %.3f", - sliding_window_average, streaming_prob); - return true; + for (auto &model : this->wake_word_models_) { + if (model.determine_detected()) { +#ifdef USE_MICRO_WAKE_WORD_VAD + if (vad_state) { +#endif + this->detected_wake_word_ = model.get_wake_word(); + return true; +#ifdef USE_MICRO_WAKE_WORD_VAD + } else { + ESP_LOGD(TAG, "Wake word model predicts %s, but VAD model doesn't.", model.get_wake_word().c_str()); + } +#endif + } } return false; } -void MicroWakeWord::set_sliding_window_average_size(size_t size) { - this->sliding_window_average_size_ = size; - this->recent_streaming_probabilities_.resize(this->sliding_window_average_size_, 0.0); +bool MicroWakeWord::has_enough_samples_() { + return this->ring_buffer_->available() >= + (this->features_step_size_ * (AUDIO_SAMPLE_FREQUENCY / 1000)) * sizeof(int16_t); } -bool MicroWakeWord::slice_available_() { - size_t available = this->ring_buffer_->available(); - - return available > (NEW_SAMPLES_TO_GET * sizeof(int16_t)); -} - -bool MicroWakeWord::stride_audio_samples_(int16_t **audio_samples) { - if (!this->slice_available_()) { +bool MicroWakeWord::generate_features_for_window_(int8_t features[PREPROCESSOR_FEATURE_SIZE]) { + // Ensure we have enough new audio samples in the ring buffer for a full window + if (!this->has_enough_samples_()) { return false; } - // Copy the last 320 bytes (160 samples over 10 ms) from the audio buffer to the start of the audio buffer - memcpy((void *) (this->preprocessor_audio_buffer_), (void *) (this->preprocessor_audio_buffer_ + NEW_SAMPLES_TO_GET), - HISTORY_SAMPLES_TO_KEEP * sizeof(int16_t)); - - // Copy 640 bytes (320 samples over 20 ms) from the ring buffer into the audio buffer offset 320 bytes (160 samples - // over 10 ms) - size_t bytes_read = this->ring_buffer_->read((void *) (this->preprocessor_audio_buffer_ + HISTORY_SAMPLES_TO_KEEP), - NEW_SAMPLES_TO_GET * sizeof(int16_t), pdMS_TO_TICKS(200)); + size_t bytes_read = this->ring_buffer_->read((void *) (this->preprocessor_audio_buffer_), + this->new_samples_to_get_() * sizeof(int16_t), pdMS_TO_TICKS(200)); if (bytes_read == 0) { ESP_LOGE(TAG, "Could not read data from Ring Buffer"); - } else if (bytes_read < NEW_SAMPLES_TO_GET * sizeof(int16_t)) { + } else if (bytes_read < this->new_samples_to_get_() * sizeof(int16_t)) { ESP_LOGD(TAG, "Partial Read of Data by Model"); ESP_LOGD(TAG, "Could only read %d bytes when required %d bytes ", bytes_read, - (int) (NEW_SAMPLES_TO_GET * sizeof(int16_t))); + (int) (this->new_samples_to_get_() * sizeof(int16_t))); return false; } - *audio_samples = this->preprocessor_audio_buffer_; - return true; -} + size_t num_samples_read; + struct FrontendOutput frontend_output = FrontendProcessSamples( + &this->frontend_state_, this->preprocessor_audio_buffer_, this->new_samples_to_get_(), &num_samples_read); -bool MicroWakeWord::generate_single_feature_(const int16_t *audio_data, const int audio_data_size, - int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]) { - TfLiteTensor *input = this->preprocessor_interperter_->input(0); - TfLiteTensor *output = this->preprocessor_interperter_->output(0); - std::copy_n(audio_data, audio_data_size, tflite::GetTensorData(input)); - - if (this->preprocessor_interperter_->Invoke() != kTfLiteOk) { - ESP_LOGE(TAG, "Failed to preprocess audio for local wake word."); - return false; + for (size_t i = 0; i < frontend_output.size; ++i) { + // These scaling values are set to match the TFLite audio frontend int8 output. + // The feature pipeline outputs 16-bit signed integers in roughly a 0 to 670 + // range. In training, these are then arbitrarily divided by 25.6 to get + // float values in the rough range of 0.0 to 26.0. This scaling is performed + // for historical reasons, to match up with the output of other feature + // generators. + // The process is then further complicated when we quantize the model. This + // means we have to scale the 0.0 to 26.0 real values to the -128 to 127 + // signed integer numbers. + // All this means that to get matching values from our integer feature + // output into the tensor input, we have to perform: + // input = (((feature / 25.6) / 26.0) * 256) - 128 + // To simplify this and perform it in 32-bit integer math, we rearrange to: + // input = (feature * 256) / (25.6 * 26.0) - 128 + constexpr int32_t value_scale = 256; + constexpr int32_t value_div = 666; // 666 = 25.6 * 26.0 after rounding + int32_t value = ((frontend_output.values[i] * value_scale) + (value_div / 2)) / value_div; + value -= 128; + if (value < -128) { + value = -128; + } + if (value > 127) { + value = 127; + } + features[i] = value; } - std::memcpy(feature_output, tflite::GetTensorData(output), PREPROCESSOR_FEATURE_SIZE * sizeof(int8_t)); return true; } -bool MicroWakeWord::register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver) { - if (op_resolver.AddReshape() != kTfLiteOk) - return false; - if (op_resolver.AddCast() != kTfLiteOk) - return false; - if (op_resolver.AddStridedSlice() != kTfLiteOk) - return false; - if (op_resolver.AddConcatenation() != kTfLiteOk) - return false; - if (op_resolver.AddMul() != kTfLiteOk) - return false; - if (op_resolver.AddAdd() != kTfLiteOk) - return false; - if (op_resolver.AddDiv() != kTfLiteOk) - return false; - if (op_resolver.AddMinimum() != kTfLiteOk) - return false; - if (op_resolver.AddMaximum() != kTfLiteOk) - return false; - if (op_resolver.AddWindow() != kTfLiteOk) - return false; - if (op_resolver.AddFftAutoScale() != kTfLiteOk) - return false; - if (op_resolver.AddRfft() != kTfLiteOk) - return false; - if (op_resolver.AddEnergy() != kTfLiteOk) - return false; - if (op_resolver.AddFilterBank() != kTfLiteOk) - return false; - if (op_resolver.AddFilterBankSquareRoot() != kTfLiteOk) - return false; - if (op_resolver.AddFilterBankSpectralSubtraction() != kTfLiteOk) - return false; - if (op_resolver.AddPCAN() != kTfLiteOk) - return false; - if (op_resolver.AddFilterBankLog() != kTfLiteOk) - return false; - - return true; +void MicroWakeWord::reset_states_() { + ESP_LOGD(TAG, "Resetting buffers and probabilities"); + this->ring_buffer_->reset(); + this->ignore_windows_ = -MIN_SLICES_BEFORE_DETECTION; + for (auto &model : this->wake_word_models_) { + model.reset_probabilities(); + } +#ifdef USE_MICRO_WAKE_WORD_VAD + this->vad_model_->reset_probabilities(); +#endif } -bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<17> &op_resolver) { +bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<20> &op_resolver) { if (op_resolver.AddCallOnce() != kTfLiteOk) return false; if (op_resolver.AddVarHandle() != kTfLiteOk) @@ -496,6 +442,12 @@ bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<17> & return false; if (op_resolver.AddMaxPool2D() != kTfLiteOk) return false; + if (op_resolver.AddPad() != kTfLiteOk) + return false; + if (op_resolver.AddPack() != kTfLiteOk) + return false; + if (op_resolver.AddSplitV() != kTfLiteOk) + return false; return true; } @@ -504,5 +456,3 @@ bool MicroWakeWord::register_streaming_ops_(tflite::MicroMutableOpResolver<17> & } // namespace esphome #endif // USE_ESP_IDF - -#endif // CLANG_TIDY diff --git a/esphome/components/micro_wake_word/micro_wake_word.h b/esphome/components/micro_wake_word/micro_wake_word.h index 1d7c18d686..0c805b75fc 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.h +++ b/esphome/components/micro_wake_word/micro_wake_word.h @@ -1,21 +1,18 @@ #pragma once -/** - * This is a workaround until we can figure out a way to get - * the tflite-micro idf component code available in CI - * - * */ -// -#ifndef CLANG_TIDY - #ifdef USE_ESP_IDF +#include "preprocessor_settings.h" +#include "streaming_model.h" + #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/ring_buffer.h" #include "esphome/components/microphone/microphone.h" +#include + #include #include #include @@ -23,35 +20,6 @@ namespace esphome { namespace micro_wake_word { -// The following are dictated by the preprocessor model -// -// The number of features the audio preprocessor generates per slice -static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40; -// How frequently the preprocessor generates a new set of features -static const uint8_t FEATURE_STRIDE_MS = 20; -// Duration of each slice used as input into the preprocessor -static const uint8_t FEATURE_DURATION_MS = 30; -// Audio sample frequency in hertz -static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000; -// The number of old audio samples that are saved to be part of the next feature window -static const uint16_t HISTORY_SAMPLES_TO_KEEP = - ((FEATURE_DURATION_MS - FEATURE_STRIDE_MS) * (AUDIO_SAMPLE_FREQUENCY / 1000)); -// The number of new audio samples to receive to be included with the next feature window -static const uint16_t NEW_SAMPLES_TO_GET = (FEATURE_STRIDE_MS * (AUDIO_SAMPLE_FREQUENCY / 1000)); -// The total number of audio samples included in the feature window -static const uint16_t SAMPLE_DURATION_COUNT = FEATURE_DURATION_MS * AUDIO_SAMPLE_FREQUENCY / 1000; -// Number of bytes in memory needed for the preprocessor arena -static const uint32_t PREPROCESSOR_ARENA_SIZE = 9528; - -// The following configure the streaming wake word model -// -// The number of audio slices to process before accepting a positive detection -static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74; - -// Number of bytes in memory needed for the streaming wake word model -static const uint32_t STREAMING_MODEL_ARENA_SIZE = 64000; -static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024; - enum State { IDLE, START_MICROPHONE, @@ -61,6 +29,9 @@ enum State { STOPPING_MICROPHONE, }; +// The number of audio slices to process before accepting a positive detection +static const uint8_t MIN_SLICES_BEFORE_DETECTION = 74; + class MicroWakeWord : public Component { public: void setup() override; @@ -73,28 +44,21 @@ class MicroWakeWord : public Component { bool is_running() const { return this->state_ != State::IDLE; } - bool initialize_models(); - - std::string get_wake_word() { return this->wake_word_; } - - // Increasing either of these will reduce the rate of false acceptances while increasing the false rejection rate - void set_probability_cutoff(float probability_cutoff) { this->probability_cutoff_ = probability_cutoff; } - void set_sliding_window_average_size(size_t size); + void set_features_step_size(uint8_t step_size) { this->features_step_size_ = step_size; } void set_microphone(microphone::Microphone *microphone) { this->microphone_ = microphone; } Trigger *get_wake_word_detected_trigger() const { return this->wake_word_detected_trigger_; } - void set_model_start(const uint8_t *model_start) { this->model_start_ = model_start; } - void set_wake_word(const std::string &wake_word) { this->wake_word_ = wake_word; } + void add_wake_word_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size, + const std::string &wake_word, size_t tensor_arena_size); + +#ifdef USE_MICRO_WAKE_WORD_VAD + void add_vad_model(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, + size_t tensor_arena_size); +#endif protected: - void set_state_(State state); - int read_microphone_(); - - const uint8_t *model_start_; - std::string wake_word_; - microphone::Microphone *microphone_{nullptr}; Trigger *wake_word_detected_trigger_ = new Trigger(); State state_{State::IDLE}; @@ -102,85 +66,93 @@ class MicroWakeWord : public Component { std::unique_ptr ring_buffer_; - int16_t *input_buffer_; + std::vector wake_word_models_; - const tflite::Model *preprocessor_model_{nullptr}; - const tflite::Model *streaming_model_{nullptr}; - tflite::MicroInterpreter *streaming_interpreter_{nullptr}; - tflite::MicroInterpreter *preprocessor_interperter_{nullptr}; +#ifdef USE_MICRO_WAKE_WORD_VAD + std::unique_ptr vad_model_; +#endif - std::vector recent_streaming_probabilities_; - size_t last_n_index_{0}; + tflite::MicroMutableOpResolver<20> streaming_op_resolver_; - float probability_cutoff_{0.5}; - size_t sliding_window_average_size_{10}; + // Audio frontend handles generating spectrogram features + struct FrontendConfig frontend_config_; + struct FrontendState frontend_state_; - // When the wake word detection first starts or after the word has been detected once, we ignore this many audio - // feature slices before accepting a positive detection again + // When the wake word detection first starts, we ignore this many audio + // feature slices before accepting a positive detection int16_t ignore_windows_{-MIN_SLICES_BEFORE_DETECTION}; - uint8_t *streaming_var_arena_{nullptr}; - uint8_t *streaming_tensor_arena_{nullptr}; - uint8_t *preprocessor_tensor_arena_{nullptr}; - int8_t *new_features_data_{nullptr}; + uint8_t features_step_size_; - tflite::MicroResourceVariables *mrv_{nullptr}; - - // Stores audio fed into feature generator preprocessor - int16_t *preprocessor_audio_buffer_; + // Stores audio read from the microphone before being added to the ring buffer. + int16_t *input_buffer_{nullptr}; + // Stores audio to be fed into the audio frontend for generating features. + int16_t *preprocessor_audio_buffer_{nullptr}; bool detected_{false}; + std::string detected_wake_word_{""}; - /** Detects if wake word has been said + void set_state_(State state); + + /// @brief Tests if there are enough samples in the ring buffer to generate new features. + /// @return True if enough samples, false otherwise. + bool has_enough_samples_(); + + /** Reads audio from microphone into the ring buffer + * + * Audio data (16000 kHz with int16 samples) is read into the input_buffer_. + * Verifies the ring buffer has enough space for all audio data. If not, it logs + * a warning and resets the ring buffer entirely. + * @return Number of bytes written to the ring buffer + */ + size_t read_microphone_(); + + /// @brief Allocates memory for input_buffer_, preprocessor_audio_buffer_, and ring_buffer_ + /// @return True if successful, false otherwise + bool allocate_buffers_(); + + /// @brief Frees memory allocated for input_buffer_ and preprocessor_audio_buffer_ + void deallocate_buffers_(); + + /// @brief Loads streaming models and prepares the feature generation frontend + /// @return True if successful, false otherwise + bool load_models_(); + + /// @brief Deletes each model's TFLite interpreters and frees tensor arena memory. Frees memory used by the feature + /// generation frontend. + void unload_models_(); + + /** Performs inference with each configured model * * If enough audio samples are available, it will generate one slice of new features. - * If the streaming model predicts the wake word, then the nonstreaming model confirms it. - * @param ring_Buffer Ring buffer containing raw audio samples - * @return True if the wake word is detected, false otherwise + * It then loops through and performs inference with each of the loaded models. */ - bool detect_wake_word_(); + void update_model_probabilities_(); - /// @brief Returns true if there are enough audio samples in the buffer to generate another slice of features - bool slice_available_(); - - /** Shifts previous feature slices over by one and generates a new slice of features + /** Checks every model's recent probabilities to determine if the wake word has been predicted * - * @param ring_buffer ring buffer containing raw audio samples - * @return True if a new slice of features was generated, false otherwise + * Verifies the models have processed enough new samples for accurate predictions. + * Sets detected_wake_word_ to the wake word, if one is detected. + * @return True if a wake word is predicted, false otherwise */ - bool update_features_(); + bool detect_wake_words_(); - /** Generates features from audio samples + /** Generates features for a window of audio samples * - * Adapted from TFLite micro speech example - * @param audio_data Pointer to array with the audio samples - * @param audio_data_size The number of samples to use as input to the preprocessor model - * @param feature_output Array that will store the features + * Reads samples from the ring buffer and feeds them into the preprocessor frontend. + * Adapted from TFLite microspeech frontend. + * @param features int8_t array to store the audio features * @return True if successful, false otherwise. */ - bool generate_single_feature_(const int16_t *audio_data, int audio_data_size, - int8_t feature_output[PREPROCESSOR_FEATURE_SIZE]); + bool generate_features_for_window_(int8_t features[PREPROCESSOR_FEATURE_SIZE]); - /** Performs inference over the most recent feature slice with the streaming model - * - * @return Probability of the wake word between 0.0 and 1.0 - */ - float perform_streaming_inference_(); - - /** Strides the audio samples by keeping the last 10 ms of the previous slice - * - * Adapted from the TFLite micro speech example - * @param ring_buffer Ring buffer containing raw audio samples - * @param audio_samples Pointer to an array that will store the strided audio samples - * @return True if successful, false otherwise - */ - bool stride_audio_samples_(int16_t **audio_samples); - - /// @brief Returns true if successfully registered the preprocessor's TensorFlow operations - bool register_preprocessor_ops_(tflite::MicroMutableOpResolver<18> &op_resolver); + /// @brief Resets the ring buffer, ignore_windows_, and sliding window probabilities + void reset_states_(); /// @brief Returns true if successfully registered the streaming model's TensorFlow operations - bool register_streaming_ops_(tflite::MicroMutableOpResolver<17> &op_resolver); + bool register_streaming_ops_(tflite::MicroMutableOpResolver<20> &op_resolver); + + inline uint16_t new_samples_to_get_() { return (this->features_step_size_ * (AUDIO_SAMPLE_FREQUENCY / 1000)); } }; template class StartAction : public Action, public Parented { @@ -202,5 +174,3 @@ template class IsRunningCondition : public Condition, pub } // namespace esphome #endif // USE_ESP_IDF - -#endif // CLANG_TIDY diff --git a/esphome/components/micro_wake_word/preprocessor_settings.h b/esphome/components/micro_wake_word/preprocessor_settings.h new file mode 100644 index 0000000000..03f4fb5230 --- /dev/null +++ b/esphome/components/micro_wake_word/preprocessor_settings.h @@ -0,0 +1,20 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include + +namespace esphome { +namespace micro_wake_word { + +// The number of features the audio preprocessor generates per slice +static const uint8_t PREPROCESSOR_FEATURE_SIZE = 40; +// Duration of each slice used as input into the preprocessor +static const uint8_t FEATURE_DURATION_MS = 30; +// Audio sample frequency in hertz +static const uint16_t AUDIO_SAMPLE_FREQUENCY = 16000; + +} // namespace micro_wake_word +} // namespace esphome + +#endif diff --git a/esphome/components/micro_wake_word/streaming_model.cpp b/esphome/components/micro_wake_word/streaming_model.cpp new file mode 100644 index 0000000000..013fa2ce6e --- /dev/null +++ b/esphome/components/micro_wake_word/streaming_model.cpp @@ -0,0 +1,189 @@ +#ifdef USE_ESP_IDF + +#include "streaming_model.h" + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +static const char *const TAG = "micro_wake_word"; + +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: %.3f", this->probability_cutoff_); + ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_); +} + +void VADModel::log_model_config() { + ESP_LOGCONFIG(TAG, " - VAD Model"); + ESP_LOGCONFIG(TAG, " Probability cutoff: %.3f", this->probability_cutoff_); + ESP_LOGCONFIG(TAG, " Sliding window size: %d", this->sliding_window_size_); +} + +bool StreamingModel::load_model(tflite::MicroMutableOpResolver<20> &op_resolver) { + ExternalRAMAllocator arena_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + + if (this->tensor_arena_ == nullptr) { + this->tensor_arena_ = arena_allocator.allocate(this->tensor_arena_size_); + if (this->tensor_arena_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the streaming model's tensor arena."); + return false; + } + } + + if (this->var_arena_ == nullptr) { + this->var_arena_ = arena_allocator.allocate(STREAMING_MODEL_VARIABLE_ARENA_SIZE); + if (this->var_arena_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate the streaming model's variable tensor arena."); + return false; + } + this->ma_ = tflite::MicroAllocator::Create(this->var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE); + this->mrv_ = tflite::MicroResourceVariables::Create(this->ma_, 20); + } + + const tflite::Model *model = tflite::GetModel(this->model_start_); + if (model->version() != TFLITE_SCHEMA_VERSION) { + ESP_LOGE(TAG, "Streaming model's schema is not supported"); + return false; + } + + if (this->interpreter_ == nullptr) { + this->interpreter_ = make_unique( + tflite::GetModel(this->model_start_), op_resolver, this->tensor_arena_, this->tensor_arena_size_, this->mrv_); + if (this->interpreter_->AllocateTensors() != kTfLiteOk) { + ESP_LOGE(TAG, "Failed to allocate tensors for the streaming model"); + return false; + } + + // Verify input tensor matches expected values + // Dimension 3 will represent the first layer stride, so skip it may vary + TfLiteTensor *input = this->interpreter_->input(0); + if ((input->dims->size != 3) || (input->dims->data[0] != 1) || + (input->dims->data[2] != PREPROCESSOR_FEATURE_SIZE)) { + ESP_LOGE(TAG, "Streaming model tensor input dimensions has improper dimensions."); + return false; + } + + if (input->type != kTfLiteInt8) { + ESP_LOGE(TAG, "Streaming model tensor input is not int8."); + return false; + } + + // Verify output tensor matches expected values + TfLiteTensor *output = this->interpreter_->output(0); + if ((output->dims->size != 2) || (output->dims->data[0] != 1) || (output->dims->data[1] != 1)) { + ESP_LOGE(TAG, "Streaming model tensor output dimension is not 1x1."); + } + + if (output->type != kTfLiteUInt8) { + ESP_LOGE(TAG, "Streaming model tensor output is not uint8."); + return false; + } + } + + return true; +} + +void StreamingModel::unload_model() { + this->interpreter_.reset(); + + ExternalRAMAllocator arena_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + + arena_allocator.deallocate(this->tensor_arena_, this->tensor_arena_size_); + this->tensor_arena_ = nullptr; + arena_allocator.deallocate(this->var_arena_, STREAMING_MODEL_VARIABLE_ARENA_SIZE); + this->var_arena_ = nullptr; +} + +bool StreamingModel::perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE]) { + if (this->interpreter_ != nullptr) { + TfLiteTensor *input = this->interpreter_->input(0); + + std::memmove( + (int8_t *) (tflite::GetTensorData(input)) + PREPROCESSOR_FEATURE_SIZE * this->current_stride_step_, + features, PREPROCESSOR_FEATURE_SIZE); + ++this->current_stride_step_; + + uint8_t stride = this->interpreter_->input(0)->dims->data[1]; + + if (this->current_stride_step_ >= stride) { + this->current_stride_step_ = 0; + + TfLiteStatus invoke_status = this->interpreter_->Invoke(); + if (invoke_status != kTfLiteOk) { + ESP_LOGW(TAG, "Streaming interpreter invoke failed"); + return false; + } + + TfLiteTensor *output = this->interpreter_->output(0); + + ++this->last_n_index_; + if (this->last_n_index_ == this->sliding_window_size_) + this->last_n_index_ = 0; + this->recent_streaming_probabilities_[this->last_n_index_] = output->data.uint8[0]; // probability; + } + return true; + } + ESP_LOGE(TAG, "Streaming interpreter is not initialized."); + return false; +} + +void StreamingModel::reset_probabilities() { + for (auto &prob : this->recent_streaming_probabilities_) { + prob = 0; + } +} + +WakeWordModel::WakeWordModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size, + const std::string &wake_word, size_t tensor_arena_size) { + this->model_start_ = model_start; + this->probability_cutoff_ = probability_cutoff; + this->sliding_window_size_ = sliding_window_average_size; + this->recent_streaming_probabilities_.resize(sliding_window_average_size, 0); + this->wake_word_ = wake_word; + this->tensor_arena_size_ = tensor_arena_size; +}; + +bool WakeWordModel::determine_detected() { + int32_t sum = 0; + for (auto &prob : this->recent_streaming_probabilities_) { + sum += prob; + } + + float sliding_window_average = static_cast(sum) / static_cast(255 * this->sliding_window_size_); + + // Detect the wake word if the sliding window average is above the cutoff + if (sliding_window_average > this->probability_cutoff_) { + ESP_LOGD(TAG, "The '%s' model sliding average probability is %.3f and most recent probability is %.3f", + this->wake_word_.c_str(), sliding_window_average, + this->recent_streaming_probabilities_[this->last_n_index_] / (255.0)); + return true; + } + return false; +} + +VADModel::VADModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, + size_t tensor_arena_size) { + this->model_start_ = model_start; + this->probability_cutoff_ = probability_cutoff; + this->sliding_window_size_ = sliding_window_size; + this->recent_streaming_probabilities_.resize(sliding_window_size, 0); + this->tensor_arena_size_ = tensor_arena_size; +}; + +bool VADModel::determine_detected() { + uint8_t max = 0; + for (auto &prob : this->recent_streaming_probabilities_) { + max = std::max(prob, max); + } + + return max > this->probability_cutoff_; +} + +} // namespace micro_wake_word +} // namespace esphome + +#endif diff --git a/esphome/components/micro_wake_word/streaming_model.h b/esphome/components/micro_wake_word/streaming_model.h new file mode 100644 index 0000000000..0d85579f35 --- /dev/null +++ b/esphome/components/micro_wake_word/streaming_model.h @@ -0,0 +1,84 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include "preprocessor_settings.h" + +#include +#include +#include + +namespace esphome { +namespace micro_wake_word { + +static const uint32_t STREAMING_MODEL_VARIABLE_ARENA_SIZE = 1024; + +class StreamingModel { + public: + virtual void log_model_config() = 0; + virtual bool determine_detected() = 0; + + bool perform_streaming_inference(const int8_t features[PREPROCESSOR_FEATURE_SIZE]); + + /// @brief Sets all recent_streaming_probabilities to 0 + void reset_probabilities(); + + /// @brief Allocates tensor and variable arenas and sets up the model interpreter + /// @param op_resolver MicroMutableOpResolver object that must exist until the model is unloaded + /// @return True if successful, false otherwise + bool load_model(tflite::MicroMutableOpResolver<20> &op_resolver); + + /// @brief Destroys the TFLite interpreter and frees the tensor and variable arenas' memory + void unload_model(); + + protected: + uint8_t current_stride_step_{0}; + + float probability_cutoff_; + size_t sliding_window_size_; + size_t last_n_index_{0}; + size_t tensor_arena_size_; + std::vector recent_streaming_probabilities_; + + const uint8_t *model_start_; + uint8_t *tensor_arena_{nullptr}; + uint8_t *var_arena_{nullptr}; + std::unique_ptr interpreter_; + tflite::MicroResourceVariables *mrv_{nullptr}; + tflite::MicroAllocator *ma_{nullptr}; +}; + +class WakeWordModel final : public StreamingModel { + public: + WakeWordModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_average_size, + const std::string &wake_word, size_t tensor_arena_size); + + void log_model_config() override; + + /// @brief Checks for the wake word by comparing the mean probability in the sliding window with the probability + /// cutoff + /// @return True if wake word is detected, false otherwise + bool determine_detected() override; + + const std::string &get_wake_word() const { return this->wake_word_; } + + protected: + std::string wake_word_; +}; + +class VADModel final : public StreamingModel { + public: + VADModel(const uint8_t *model_start, float probability_cutoff, size_t sliding_window_size, size_t tensor_arena_size); + + void log_model_config() override; + + /// @brief Checks for voice activity by comparing the max probability in the sliding window with the probability + /// cutoff + /// @return True if voice activity is detected, false otherwise + bool determine_detected() override; +}; + +} // namespace micro_wake_word +} // namespace esphome + +#endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 1e6f3517db..4831ed2c9e 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -86,6 +86,7 @@ #define USE_ESP32_BLE_SERVER #define USE_ESP32_CAMERA #define USE_IMPROV +#define USE_MICRO_WAKE_WORD_VAD #define USE_MICROPHONE #define USE_PSRAM #define USE_SOCKET_IMPL_BSD_SOCKETS diff --git a/platformio.ini b/platformio.ini index a72bf598c5..f07889526f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -142,7 +142,8 @@ platform_packages = framework = espidf lib_deps = ${common:idf.lib_deps} - droscy/esp_wireguard@0.4.2 ; wireguard + droscy/esp_wireguard@0.4.2 ; wireguard + kahrendt/ESPMicroSpeechFeatures@1.0.0 ; micro_wake_word build_flags = ${common:idf.build_flags} -Wno-nonnull-compare diff --git a/tests/components/micro_wake_word/common.yaml b/tests/components/micro_wake_word/common.yaml index c0f3593cc6..8bd7345307 100644 --- a/tests/components/micro_wake_word/common.yaml +++ b/tests/components/micro_wake_word/common.yaml @@ -10,6 +10,10 @@ microphone: pdm: true micro_wake_word: - model: hey_jarvis on_wake_word_detected: - logger.log: "Wake word detected" + models: + - model: hey_jarvis + probability_cutoff: 0.7 + - model: okay_nabu + sliding_window_size: 5 diff --git a/tests/components/micro_wake_word/test.esp32-idf.yaml b/tests/components/micro_wake_word/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/micro_wake_word/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From aa8c963c50467b194fa5d7ebb25f81a6692d3d19 Mon Sep 17 00:00:00 2001 From: Pavlo Dudnytskyi Date: Thu, 11 Jul 2024 03:30:55 +0200 Subject: [PATCH 0005/1052] UART component support added for host platform (#6912) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Pavlo Dudnytskyi --- esphome/components/uart/__init__.py | 66 +++- .../components/uart/uart_component_host.cpp | 295 ++++++++++++++++++ esphome/components/uart/uart_component_host.h | 38 +++ tests/components/uart/test.host.yaml | 13 + 4 files changed, 410 insertions(+), 2 deletions(-) create mode 100644 esphome/components/uart/uart_component_host.cpp create mode 100644 esphome/components/uart/uart_component_host.h create mode 100644 tests/components/uart/test.host.yaml diff --git a/esphome/components/uart/__init__.py b/esphome/components/uart/__init__.py index b036288078..0738a127e1 100644 --- a/esphome/components/uart/__init__.py +++ b/esphome/components/uart/__init__.py @@ -1,5 +1,5 @@ from typing import Optional - +import re import esphome.codegen as cg import esphome.config_validation as cv import esphome.final_validate as fv @@ -11,6 +11,7 @@ from esphome.const import ( CONF_NUMBER, CONF_RX_PIN, CONF_TX_PIN, + CONF_PORT, CONF_UART_ID, CONF_DATA, CONF_RX_BUFFER_SIZE, @@ -27,6 +28,7 @@ from esphome.const import ( CONF_DUMMY_RECEIVER, CONF_DUMMY_RECEIVER_ID, CONF_LAMBDA, + PLATFORM_HOST, ) from esphome.core import CORE @@ -45,6 +47,7 @@ RP2040UartComponent = uart_ns.class_("RP2040UartComponent", UARTComponent, cg.Co LibreTinyUARTComponent = uart_ns.class_( "LibreTinyUARTComponent", UARTComponent, cg.Component ) +HostUartComponent = uart_ns.class_("HostUartComponent", UARTComponent, cg.Component) NATIVE_UART_CLASSES = ( str(IDFUARTComponent), @@ -54,6 +57,39 @@ NATIVE_UART_CLASSES = ( str(LibreTinyUARTComponent), ) +HOST_BAUD_RATES = [ + 50, + 75, + 110, + 134, + 150, + 200, + 300, + 600, + 1200, + 1800, + 2400, + 4800, + 9600, + 19200, + 38400, + 57600, + 115200, + 230400, + 460800, + 500000, + 576000, + 921600, + 1000000, + 1152000, + 1500000, + 2000000, + 2500000, + 3000000, + 3500000, + 4000000, +] + UARTDevice = uart_ns.class_("UARTDevice") UARTWriteAction = uart_ns.class_("UARTWriteAction", automation.Action) UARTDebugger = uart_ns.class_("UARTDebugger", cg.Component, automation.Action) @@ -95,6 +131,20 @@ def validate_invert_esp32(config): return config +def validate_host_config(config): + if CORE.is_host: + if CONF_TX_PIN in config or CONF_RX_PIN in config: + raise cv.Invalid( + "TX and RX pins are not supported for UART on host platform." + ) + if config[CONF_BAUD_RATE] not in HOST_BAUD_RATES: + raise cv.Invalid( + f"Host platform doesn't support baud rate {config[CONF_BAUD_RATE]}", + path=[CONF_BAUD_RATE], + ) + return config + + def _uart_declare_type(value): if CORE.is_esp8266: return cv.declare_id(ESP8266UartComponent)(value) @@ -107,6 +157,8 @@ def _uart_declare_type(value): return cv.declare_id(RP2040UartComponent)(value) if CORE.is_libretiny: return cv.declare_id(LibreTinyUARTComponent)(value) + if CORE.is_host: + return cv.declare_id(HostUartComponent)(value) raise NotImplementedError @@ -149,6 +201,12 @@ def maybe_empty_debug(value): return DEBUG_SCHEMA(value) +def validate_port(value): + if not re.match(r"^/(?:[^/]+/)[^/]+$", value): + raise cv.Invalid("Port must be a valid device path") + return value + + DEBUG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UARTDebugger), @@ -181,6 +239,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_BAUD_RATE): cv.int_range(min=1), cv.Optional(CONF_TX_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_RX_PIN): validate_rx_pin, + cv.Optional(CONF_PORT): cv.All(validate_port, cv.only_on(PLATFORM_HOST)), cv.Optional(CONF_RX_BUFFER_SIZE, default=256): cv.validate_bytes, cv.Optional(CONF_STOP_BITS, default=1): cv.one_of(1, 2, int=True), cv.Optional(CONF_DATA_BITS, default=8): cv.int_range(min=5, max=8), @@ -193,8 +252,9 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_DEBUG): maybe_empty_debug, } ).extend(cv.COMPONENT_SCHEMA), - cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN), + cv.has_at_least_one_key(CONF_TX_PIN, CONF_RX_PIN, CONF_PORT), validate_invert_esp32, + validate_host_config, ) @@ -236,6 +296,8 @@ async def to_code(config): if CONF_RX_PIN in config: rx_pin = await cg.gpio_pin_expression(config[CONF_RX_PIN]) cg.add(var.set_rx_pin(rx_pin)) + if CONF_PORT in config: + cg.add(var.set_name(config[CONF_PORT])) cg.add(var.set_rx_buffer_size(config[CONF_RX_BUFFER_SIZE])) cg.add(var.set_stop_bits(config[CONF_STOP_BITS])) cg.add(var.set_data_bits(config[CONF_DATA_BITS])) diff --git a/esphome/components/uart/uart_component_host.cpp b/esphome/components/uart/uart_component_host.cpp new file mode 100644 index 0000000000..d8d2fd75b8 --- /dev/null +++ b/esphome/components/uart/uart_component_host.cpp @@ -0,0 +1,295 @@ +#ifdef USE_HOST +#include "uart_component_host.h" +#include "esphome/core/application.h" +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifndef __linux__ +#error This HostUartComponent implementation is only for Linux +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_LOGGER +#include "esphome/components/logger/logger.h" +#endif + +namespace { + +speed_t get_baud(int baud) { + switch (baud) { + case 50: + return B50; + case 75: + return B75; + case 110: + return B110; + case 134: + return B134; + case 150: + return B150; + case 200: + return B200; + case 300: + return B300; + case 600: + return B600; + case 1200: + return B1200; + case 1800: + return B1800; + case 2400: + return B2400; + case 4800: + return B4800; + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return B0; + } +} + +} // namespace + +namespace esphome { +namespace uart { + +static const char *const TAG = "uart.host"; + +HostUartComponent::~HostUartComponent() { + if (this->file_descriptor_ != -1) { + close(this->file_descriptor_); + this->file_descriptor_ = -1; + } +} + +void HostUartComponent::setup() { + 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_); + this->mark_failed(); + return; + } + this->file_descriptor_ = ::open(this->port_name_.c_str(), O_RDWR | O_NOCTTY | O_NDELAY); + if (this->file_descriptor_ == -1) { + this->update_error_(strerror(errno)); + this->mark_failed(); + return; + } + fcntl(this->file_descriptor_, F_SETFL, 0); + struct termios options; + tcgetattr(this->file_descriptor_, &options); + options.c_cflag &= ~CRTSCTS; + options.c_cflag |= CREAD | CLOCAL; + options.c_lflag &= ~ICANON; + options.c_lflag &= ~ECHO; + options.c_lflag &= ~ECHOE; + options.c_lflag &= ~ECHONL; + options.c_lflag &= ~ISIG; + options.c_iflag &= ~(IXON | IXOFF | IXANY); + options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); + options.c_oflag &= ~OPOST; + options.c_oflag &= ~ONLCR; + // Set data bits + options.c_cflag &= ~CSIZE; // Mask the character size bits + switch (this->data_bits_) { + case 5: + options.c_cflag |= CS5; + break; + case 6: + options.c_cflag |= CS6; + break; + case 7: + options.c_cflag |= CS7; + break; + case 8: + default: + options.c_cflag |= CS8; + break; + } + // Set parity + switch (this->parity_) { + case UART_CONFIG_PARITY_NONE: + options.c_cflag &= ~PARENB; + break; + case UART_CONFIG_PARITY_EVEN: + options.c_cflag |= PARENB; + options.c_cflag &= ~PARODD; + break; + case UART_CONFIG_PARITY_ODD: + options.c_cflag |= PARENB; + options.c_cflag |= PARODD; + break; + }; + // Set stop bits + if (this->stop_bits_ == 2) { + options.c_cflag |= CSTOPB; + } else { + options.c_cflag &= ~CSTOPB; + } + cfsetispeed(&options, baud); + cfsetospeed(&options, baud); + tcsetattr(this->file_descriptor_, TCSANOW, &options); +} + +void HostUartComponent::dump_config() { + ESP_LOGCONFIG(TAG, "UART:"); + ESP_LOGCONFIG(TAG, " Port: %s", this->port_name_.c_str()); + if (this->file_descriptor_ == -1) { + ESP_LOGCONFIG(TAG, " Port status: Not opened"); + if (!this->first_error_.empty()) { + ESP_LOGCONFIG(TAG, " Error: %s", this->first_error_.c_str()); + } + 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", + this->parity_ == UART_CONFIG_PARITY_NONE ? "None" + : this->parity_ == UART_CONFIG_PARITY_EVEN ? "Even" + : "Odd"); + ESP_LOGCONFIG(TAG, " Stop Bits: %d", this->stop_bits_); + this->check_logger_conflict(); +} + +void HostUartComponent::write_array(const uint8_t *data, size_t len) { + if (this->file_descriptor_ == -1) { + return; + } + size_t written = ::write(this->file_descriptor_, data, len); + if (written != len) { + this->update_error_(strerror(errno)); + return; + } +#ifdef USE_UART_DEBUGGER + for (size_t i = 0; i < len; i++) { + this->debug_callback_.call(UART_DIRECTION_TX, data[i]); + } +#endif + return; +} + +bool HostUartComponent::peek_byte(uint8_t *data) { + if (this->file_descriptor_ == -1) { + return false; + } + if (!this->has_peek_) { + if (!this->check_read_timeout_()) { + return false; + } + if (::read(this->file_descriptor_, &this->peek_byte_, 1) != 1) { + this->update_error_(strerror(errno)); + return false; + } + this->has_peek_ = true; + } + *data = this->peek_byte_; + return true; +} + +bool HostUartComponent::read_array(uint8_t *data, size_t len) { + if ((this->file_descriptor_ == -1) || (len == 0)) { + return false; + } + if (!this->check_read_timeout_(len)) + return false; + uint8_t *data_ptr = data; + size_t length_to_read = len; + if (this->has_peek_) { + length_to_read--; + *data_ptr = this->peek_byte_; + data_ptr++; + this->has_peek_ = false; + } + if (length_to_read > 0) { + int sz = ::read(this->file_descriptor_, data_ptr, length_to_read); + if (sz == -1) { + this->update_error_(strerror(errno)); + return false; + } + } +#ifdef USE_UART_DEBUGGER + for (size_t i = 0; i < len; i++) { + this->debug_callback_.call(UART_DIRECTION_RX, data[i]); + } +#endif + return true; +} + +int HostUartComponent::available() { + if (this->file_descriptor_ == -1) { + return 0; + } + int available; + int res = ioctl(this->file_descriptor_, FIONREAD, &available); + if (res == -1) { + this->update_error_(strerror(errno)); + return 0; + } + if (this->has_peek_) + available++; + return available; +}; + +void HostUartComponent::flush() { + if (this->file_descriptor_ == -1) { + return; + } + tcflush(this->file_descriptor_, TCIOFLUSH); + ESP_LOGV(TAG, " Flushing..."); +} + +void HostUartComponent::update_error_(const std::string &error) { + if (this->first_error_.empty()) { + this->first_error_ = error; + } + ESP_LOGE(TAG, "Port error: %s", error.c_str()); +} + +} // namespace uart +} // namespace esphome + +#endif // USE_HOST diff --git a/esphome/components/uart/uart_component_host.h b/esphome/components/uart/uart_component_host.h new file mode 100644 index 0000000000..c1f1dd0d2c --- /dev/null +++ b/esphome/components/uart/uart_component_host.h @@ -0,0 +1,38 @@ +#pragma once + +#ifdef USE_HOST + +#include "esphome/core/component.h" +#include "esphome/core/log.h" +#include "uart_component.h" + +namespace esphome { +namespace uart { + +class HostUartComponent : public UARTComponent, public Component { + public: + virtual ~HostUartComponent(); + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS; } + 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; + void flush() override; + void set_name(std::string port_name) { port_name_ = port_name; }; + + protected: + void update_error_(const std::string &error); + void check_logger_conflict() override {} + std::string port_name_; + std::string first_error_{""}; + int file_descriptor_ = -1; + bool has_peek_{false}; + uint8_t peek_byte_; +}; + +} // namespace uart +} // namespace esphome + +#endif // USE_HOST diff --git a/tests/components/uart/test.host.yaml b/tests/components/uart/test.host.yaml new file mode 100644 index 0000000000..63f0ade084 --- /dev/null +++ b/tests/components/uart/test.host.yaml @@ -0,0 +1,13 @@ +esphome: + on_boot: + then: + - uart.write: 'Hello World' + - uart.write: [0x00, 0x20, 0x42] + +uart: + - id: uart_uart + port: "/dev/ttyS0" + baud_rate: 9600 + data_bits: 8 + parity: EVEN + stop_bits: 2 From 2f669c99f8954ccc8c960fb527040ae2febe13cf Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Thu, 11 Jul 2024 03:32:17 +0200 Subject: [PATCH 0006/1052] Configure ap ip for RP2040 (#7065) --- .../components/wifi/wifi_component_pico_w.cpp | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index 2bb1af5489..4afcf2d78b 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -141,13 +141,29 @@ bool WiFiComponent::wifi_scan_start_(bool passive) { #ifdef USE_WIFI_AP bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { - // TODO: - return false; + esphome::network::IPAddress ip_address, gateway, subnet, dns; + if (manual_ip.has_value()) { + ip_address = manual_ip->static_ip; + gateway = manual_ip->gateway; + subnet = manual_ip->subnet; + dns = manual_ip->static_ip; + } else { + ip_address = network::IPAddress(192, 168, 4, 1); + gateway = network::IPAddress(192, 168, 4, 1); + subnet = network::IPAddress(255, 255, 255, 0); + dns = network::IPAddress(192, 168, 4, 1); + } + WiFi.config(ip_address, dns, gateway, subnet); + return true; } bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { if (!this->wifi_mode_({}, true)) return false; + if (!this->wifi_ap_ip_config_(ap.get_manual_ip())) { + ESP_LOGV(TAG, "wifi_ap_ip_config_ failed!"); + return false; + } WiFi.beginAP(ap.get_ssid().c_str(), ap.get_password().c_str(), ap.get_channel().value_or(1)); From d1b0e6b5fe11d0e77e20432c11f8f68b421ac841 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Jul 2024 15:41:48 +1200 Subject: [PATCH 0007/1052] Bump version to 2024.8.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 543b1d00cc..faf6ce19fa 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.0-dev" +__version__ = "2024.8.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 6417f1f907d0dbbef3f2011905a3cb7c9abf9763 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 11 Jul 2024 15:41:48 +1200 Subject: [PATCH 0008/1052] Bump version to 2024.7.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 543b1d00cc..d672cc92af 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.0-dev" +__version__ = "2024.7.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From fb6c2aef59d5b3ca1463705eba01d0eb3de19535 Mon Sep 17 00:00:00 2001 From: Christian Ferbar <5595808+ferbar@users.noreply.github.com> Date: Thu, 11 Jul 2024 05:58:54 +0200 Subject: [PATCH 0009/1052] helpers.cpp: Fix GLIBCXX_RELEASE check < 8 (#7062) --- esphome/core/helpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 7f040f855f..e75b06ccd3 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -78,7 +78,7 @@ static const uint16_t CRC16_1021_BE_LUT_H[] = {0x0000, 0x1231, 0x2462, 0x3653, 0 // STL backports -#if _GLIBCXX_RELEASE < 7 +#if _GLIBCXX_RELEASE < 8 std::string to_string(int value) { return str_snprintf("%d", 32, value); } // NOLINT std::string to_string(long value) { return str_snprintf("%ld", 32, value); } // NOLINT std::string to_string(long long value) { return str_snprintf("%lld", 32, value); } // NOLINT From fa4fbf9d7384e1962e2982ab2ad0db449d33e4c6 Mon Sep 17 00:00:00 2001 From: Z3LIFF Date: Thu, 11 Jul 2024 00:01:14 -0400 Subject: [PATCH 0010/1052] Fix pmsa003i cold boot marked as failed on ESP32 et al (#7064) --- esphome/components/pmsa003i/pmsa003i.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/esphome/components/pmsa003i/pmsa003i.cpp b/esphome/components/pmsa003i/pmsa003i.cpp index ca3d28367a..a9665c6a5a 100644 --- a/esphome/components/pmsa003i/pmsa003i.cpp +++ b/esphome/components/pmsa003i/pmsa003i.cpp @@ -13,6 +13,15 @@ void PMSA003IComponent::setup() { PM25AQIData data; bool successful_read = this->read_data_(&data); + if (!successful_read) { + for (int i = 0; i < 3; i++) { + successful_read = this->read_data_(&data); + if (successful_read) { + break; + } + } + } + if (!successful_read) { this->mark_failed(); return; From dea1e9a1e05ea1b01c3a70d6789d02569498becf Mon Sep 17 00:00:00 2001 From: guillempages Date: Thu, 11 Jul 2024 06:08:51 +0200 Subject: [PATCH 0011/1052] [http_request] Fix follow_redirects on arduino (#7054) --- esphome/components/http_request/http_request_arduino.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index 248a85a439..95b1cdc38e 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -32,6 +32,13 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s watchdog::WatchdogManager wdm(this->get_watchdog_timeout()); + if (this->follow_redirects_) { + container->client_.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); + container->client_.setRedirectLimit(this->redirect_limit_); + } else { + container->client_.setFollowRedirects(HTTPC_DISABLE_FOLLOW_REDIRECTS); + } + #if defined(USE_ESP8266) std::unique_ptr stream_ptr; #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS @@ -59,8 +66,6 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s "in your YAML, or use HTTPS"); } #endif // USE_ARDUINO_VERSION_CODE - - container->client_.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); bool status = container->client_.begin(*stream_ptr, url.c_str()); #elif defined(USE_RP2040) From ee4d5178d6fa54dcc8069b86bfdd63571d8a4f9e Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Thu, 11 Jul 2024 06:09:51 +0200 Subject: [PATCH 0012/1052] [ethernet] Fix compile warning for IPv6 (#7048) --- esphome/components/ethernet/ethernet_component.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 6b34157b9d..962a864a29 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -394,7 +394,7 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b const esp_netif_ip_info_t *ip_info = &event->ip_info; ESP_LOGV(TAG, "[Ethernet event] ETH Got IP " IPSTR, IP2STR(&ip_info->ip)); global_eth_component->got_ipv4_address_ = true; -#if USE_NETWORK_IPV6 +#if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT; #else global_eth_component->connected_ = true; @@ -407,8 +407,12 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_ ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data; ESP_LOGV(TAG, "[Ethernet event] ETH Got IPv6: " IPV6STR, IPV62STR(event->ip6_info.ip)); global_eth_component->ipv6_count_ += 1; +#if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) global_eth_component->connected_ = global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); +#else + global_eth_component->connected_ = global_eth_component->got_ipv4_address_; +#endif } #endif /* USE_NETWORK_IPV6 */ From 1b57d8511be329fac18c039d22ac06679e616b8f Mon Sep 17 00:00:00 2001 From: esphomebot Date: Thu, 11 Jul 2024 16:10:18 +1200 Subject: [PATCH 0013/1052] Update webserver local assets to 20240704-081526 (#7041) --- .../components/web_server/server_index_v2.h | 83 +- .../components/web_server/server_index_v3.h | 735 +++++++++--------- 2 files changed, 410 insertions(+), 408 deletions(-) diff --git a/esphome/components/web_server/server_index_v2.h b/esphome/components/web_server/server_index_v2.h index c942cda592..c9932624ba 100644 --- a/esphome/components/web_server/server_index_v2.h +++ b/esphome/components/web_server/server_index_v2.h @@ -68,7 +68,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xe3, 0x9b, 0x82, 0xde, 0xc5, 0x0e, 0x21, 0xa9, 0x64, 0x54, 0x44, 0x2c, 0x97, 0x29, 0x21, 0x29, 0xc2, 0x3f, 0x90, 0x45, 0x68, 0xd6, 0x13, 0xec, 0x34, 0x30, 0x9c, 0xcb, 0x40, 0x71, 0x17, 0x3c, 0xe0, 0xc9, 0x9c, 0xa6, 0x82, 0xa6, 0xc1, 0x5f, 0x71, 0x4a, 0x87, 0x31, 0x40, 0xb1, 0xd3, 0xc4, 0xe3, 0x30, 0x3b, 0x1f, 0x87, 0xc9, 0x88, 0x46, 0xc1, - 0x8d, 0xc8, 0xf1, 0xdf, 0x89, 0x3b, 0x64, 0x49, 0x18, 0xb3, 0x5f, 0x69, 0xe4, 0x6a, 0x69, 0x70, 0xeb, 0xd0, 0x5b, + 0x8d, 0xc8, 0xf1, 0xdf, 0x89, 0x3b, 0x64, 0x49, 0x18, 0xb3, 0x5f, 0x69, 0xe4, 0x6a, 0x69, 0x70, 0xe6, 0xd0, 0x5b, 0x41, 0x93, 0x28, 0x73, 0x9e, 0xbf, 0x7b, 0xf9, 0x42, 0xef, 0x63, 0x45, 0x40, 0xa0, 0x45, 0x36, 0x9b, 0xd2, 0xd4, 0x43, 0x58, 0x0b, 0x88, 0x67, 0x4c, 0x32, 0xc7, 0x97, 0xe1, 0x54, 0x95, 0xb0, 0xec, 0xfd, 0x34, 0x0a, 0x05, 0x7d, 0x43, 0x93, 0x88, 0x25, 0x23, 0xb2, 0xd3, 0x54, 0xe5, 0xe3, 0x50, 0x57, 0x44, 0x45, 0xd1, 0xe5, 0xee, 0xb3, 0x58, @@ -150,19 +150,19 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xa6, 0x57, 0xfd, 0xcf, 0xf9, 0x64, 0x0a, 0xda, 0xd8, 0x0a, 0x49, 0x8f, 0xa8, 0x9e, 0xb0, 0xac, 0xcf, 0x37, 0x94, 0x55, 0xfa, 0xc8, 0xf3, 0x58, 0xa1, 0xa6, 0xc2, 0x5e, 0xde, 0x69, 0xe4, 0xb3, 0xa2, 0xa8, 0x60, 0x1c, 0x9b, 0x9c, 0x2a, 0xe7, 0xab, 0x2e, 0x19, 0x53, 0xf1, 0xda, 0x63, 0x8a, 0x0f, 0x33, 0xe0, 0x75, 0x16, 0xfb, 0x31, 0xe4, 0x6e, - 0xef, 0x7f, 0x5e, 0x22, 0x67, 0x91, 0xaf, 0xa0, 0x6f, 0x91, 0xe7, 0xb7, 0xca, 0xc8, 0xc6, 0xb7, 0xdb, 0xad, 0xe1, - 0xb2, 0x4e, 0x1b, 0x8b, 0xbd, 0x3e, 0xbe, 0x5d, 0x57, 0x1d, 0xc9, 0x62, 0xc2, 0x23, 0x1a, 0xb8, 0x7c, 0x4a, 0x13, - 0x37, 0x07, 0xaf, 0xaa, 0xde, 0xfb, 0x81, 0xf0, 0x16, 0x6f, 0xab, 0xee, 0xd5, 0xe0, 0x36, 0x07, 0xef, 0xd7, 0xd7, + 0xef, 0x7f, 0x5e, 0x22, 0x67, 0x91, 0xaf, 0xa0, 0x6f, 0x91, 0xe7, 0x67, 0xca, 0xc8, 0xc6, 0x67, 0xdb, 0xad, 0xe1, + 0xb2, 0x4e, 0x1b, 0x8b, 0xbd, 0x3e, 0x3e, 0x5b, 0x57, 0x1d, 0xc9, 0x62, 0xc2, 0x23, 0x1a, 0xb8, 0x7c, 0x4a, 0x13, + 0x37, 0x07, 0xaf, 0xaa, 0xde, 0xfb, 0x81, 0xf0, 0x16, 0x6f, 0xab, 0xee, 0xd5, 0xe0, 0x2c, 0x07, 0xef, 0xd7, 0xd7, 0xeb, 0x8e, 0xd7, 0xef, 0x69, 0x9a, 0x49, 0x45, 0xb4, 0xd0, 0x69, 0xbf, 0x2e, 0xc5, 0xd2, 0xd7, 0xc1, 0xd6, 0xf6, 0xa5, 0x09, 0xe2, 0x36, 0xfd, 0x63, 0xff, 0xc0, 0x45, 0xd2, 0x2d, 0xfc, 0x93, 0x3e, 0xf0, 0x1f, 0x8c, 0x5b, 0xf8, 0x19, 0xf9, 0x50, 0xf5, 0x0a, 0x47, 0x82, 0x3c, 0xeb, 0x3e, 0x33, 0x16, 0x33, 0x8f, 0xd9, 0xe0, 0xce, 0x73, 0x63, 0x26, 0xea, 0x10, 0x7a, 0x73, 0xf1, 0x42, 0x55, 0x80, 0x4b, 0x51, 0xba, 0xb3, 0x73, 0x63, 0xeb, 0x61, 0x21, 0x88, 0xbb, 0x1b, 0x33, 0xb1, 0xeb, 0xe2, 0x09, 0xb9, 0x82, 0x1f, 0xbb, 0x0b, 0xef, 0x65, 0x28, 0xc6, 0x7e, 0x1a, 0x26, - 0x11, 0x9f, 0x78, 0xa8, 0xe6, 0xba, 0xc8, 0xcf, 0xa4, 0xbd, 0xf1, 0x18, 0xe5, 0xbb, 0x57, 0xf8, 0x4c, 0x10, 0xb7, - 0xeb, 0xd6, 0x26, 0xf8, 0x95, 0x20, 0x57, 0xa7, 0xbb, 0x8b, 0x33, 0x91, 0x77, 0xae, 0xf0, 0x59, 0xe1, 0xb1, 0xc7, - 0x6f, 0x88, 0x87, 0x48, 0xe7, 0x4c, 0x43, 0x73, 0xce, 0x27, 0xca, 0x73, 0xef, 0x22, 0xfc, 0x1e, 0xe2, 0x2a, 0x69, - 0xc9, 0x6d, 0x74, 0x68, 0x65, 0x87, 0xb8, 0x5c, 0xba, 0x08, 0xdc, 0xbd, 0x3d, 0xab, 0xac, 0x50, 0x15, 0xf0, 0xad, - 0x20, 0x15, 0x83, 0x1c, 0xbf, 0x95, 0x11, 0x9a, 0x5b, 0xe1, 0xa5, 0xc8, 0x0c, 0xe3, 0x19, 0x3f, 0xb4, 0x3e, 0x9a, + 0x11, 0x9f, 0x78, 0xa8, 0xe6, 0xba, 0xc8, 0xcf, 0xa4, 0xbd, 0xf1, 0x18, 0xe5, 0xbb, 0x57, 0xf8, 0x56, 0x10, 0xb7, + 0xeb, 0xd6, 0x26, 0xf8, 0x95, 0x20, 0x57, 0xa7, 0xbb, 0x8b, 0x5b, 0x91, 0x77, 0xae, 0xf0, 0x6d, 0xe1, 0xb1, 0xc7, + 0x6f, 0x88, 0x87, 0x48, 0xe7, 0x56, 0x43, 0x73, 0xce, 0x27, 0xca, 0x73, 0xef, 0x22, 0xfc, 0x1e, 0xe2, 0x2a, 0x69, + 0xc9, 0x6d, 0x74, 0x68, 0x65, 0x87, 0xb8, 0x5c, 0xba, 0x08, 0xdc, 0xbd, 0x3d, 0xab, 0xac, 0x50, 0x15, 0xf0, 0x99, + 0x20, 0x15, 0x83, 0x1c, 0xbf, 0x95, 0x11, 0x9a, 0x33, 0xe1, 0xa5, 0xc8, 0x0c, 0xe3, 0x19, 0x3f, 0xb4, 0x3e, 0x9a, 0x69, 0x4f, 0x79, 0x18, 0x7c, 0x26, 0x68, 0x1a, 0x0a, 0x9e, 0xf6, 0x91, 0xad, 0x7e, 0xe0, 0xbf, 0x91, 0xab, 0x9e, 0xf3, 0x9f, 0xbe, 0xf8, 0x79, 0xf8, 0x73, 0xda, 0xbf, 0xc2, 0xaf, 0xc9, 0xfe, 0xa9, 0xd7, 0x0d, 0xbc, 0x9d, 0x7a, 0x7d, 0xf9, 0xf3, 0x7e, 0xef, 0x1f, 0x61, 0xfd, 0xd7, 0xb3, 0xfa, 0x4f, 0x7d, 0xb4, 0xf4, 0x7e, 0xde, 0xef, 0xf6, @@ -177,7 +177,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xf2, 0x03, 0x64, 0x81, 0xc0, 0xf3, 0x30, 0x9e, 0xd1, 0x2c, 0xa0, 0x39, 0xc2, 0x03, 0x72, 0x21, 0xbc, 0x26, 0xc2, 0xcf, 0x05, 0xfc, 0x68, 0x21, 0x7c, 0xa1, 0x03, 0x98, 0x70, 0x90, 0x15, 0x51, 0x25, 0x5c, 0x69, 0x2c, 0x2e, 0xc2, 0xd3, 0x0d, 0x95, 0x62, 0x0c, 0xde, 0x05, 0x84, 0x87, 0x95, 0x70, 0x27, 0xbe, 0x21, 0x86, 0x24, 0xde, 0xa5, 0x94, - 0xfe, 0x10, 0xc6, 0x1f, 0x69, 0xea, 0x9d, 0xe1, 0x66, 0xeb, 0x31, 0x96, 0x2e, 0xe8, 0x9d, 0x26, 0x6a, 0x17, 0xb1, + 0xfe, 0x10, 0xc6, 0x1f, 0x69, 0xea, 0xdd, 0xe2, 0x66, 0xeb, 0x31, 0x96, 0x2e, 0xe8, 0x9d, 0x26, 0x6a, 0x17, 0xb1, 0xaa, 0x73, 0xa1, 0x62, 0x04, 0x20, 0x64, 0xab, 0xbe, 0x18, 0xd8, 0xf1, 0x9d, 0x74, 0xcd, 0x61, 0x95, 0x86, 0x37, 0x2e, 0xaa, 0xc6, 0x45, 0x59, 0x32, 0x0f, 0x63, 0x16, 0x39, 0x82, 0x4e, 0xa6, 0x71, 0x28, 0xa8, 0xa3, 0xd7, 0xeb, 0x84, 0x30, 0x90, 0x5b, 0xa8, 0x0c, 0x91, 0x65, 0x70, 0x46, 0x26, 0xe0, 0x04, 0x67, 0xc5, 0x83, 0xe8, 0x94, 0x56, @@ -220,10 +220,10 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x73, 0xd4, 0x69, 0xa0, 0x45, 0xa5, 0xad, 0xd4, 0x99, 0xaa, 0x71, 0xf4, 0x82, 0x4f, 0xcf, 0x49, 0xa3, 0x3d, 0x3f, 0x1d, 0xb5, 0xe7, 0xb5, 0x1a, 0xca, 0x0c, 0x69, 0xcd, 0x7a, 0xf3, 0x3e, 0x7e, 0x03, 0x4e, 0x3d, 0x9b, 0x96, 0x70, 0x65, 0x79, 0x2d, 0xbd, 0xbc, 0x5a, 0x2d, 0xc9, 0x51, 0xdb, 0xea, 0x3a, 0x52, 0x5d, 0xf3, 0x5c, 0xe1, 0x64, 0x95, - 0xd4, 0x4e, 0x90, 0x2c, 0x81, 0x64, 0x28, 0x42, 0xc8, 0x99, 0x40, 0x1b, 0x47, 0x85, 0x31, 0xa1, 0xbb, 0x3c, 0xb3, + 0xd4, 0x4e, 0x90, 0x2c, 0x81, 0x64, 0x28, 0x42, 0xc8, 0xad, 0x40, 0x1b, 0x47, 0x85, 0x31, 0xa1, 0xbb, 0x3c, 0xb3, 0xc0, 0x3e, 0x95, 0x94, 0xf0, 0x00, 0x0b, 0xd0, 0xb5, 0xf0, 0x04, 0x4f, 0xf0, 0xac, 0xd6, 0x94, 0x64, 0x5e, 0x6f, 0xb6, 0xab, 0x63, 0x3d, 0x2a, 0xc7, 0xc2, 0xb3, 0x1a, 0x99, 0x14, 0x58, 0xca, 0x93, 0x5a, 0x2d, 0xaf, 0x06, 0x3b, - 0xcd, 0xc9, 0xad, 0x04, 0x20, 0xce, 0x56, 0x93, 0x32, 0x8c, 0x84, 0x2d, 0x65, 0x2a, 0xf3, 0x59, 0x92, 0xd0, 0x14, + 0xcd, 0xc9, 0xad, 0x04, 0x20, 0x6e, 0x57, 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, 0x2c, 0xf9, 0x06, 0x21, 0xe4, 0xc2, 0xa4, 0xb3, 0x08, 0xc9, 0x83, 0x62, 0xc2, 0x2c, 0x99, 0x94, 0x11, 0xea, 0x5f, 0xee, 0x9e, 0xf3, 0x7b, 0x6d, 0xb2, 0x1e, 0xeb, 0x07, 0xb2, 0x59, 0xac, 0x39, 0x57, 0x48, 0xde, 0x7b, @@ -238,14 +238,14 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x13, 0xf3, 0xec, 0xa5, 0x5f, 0xd6, 0xca, 0xc6, 0x97, 0xbb, 0x67, 0xef, 0x37, 0x35, 0x83, 0xf2, 0x7c, 0x56, 0xda, 0xf8, 0x12, 0xbe, 0x05, 0x8d, 0x83, 0x85, 0x16, 0x0e, 0x01, 0xcb, 0xb1, 0x14, 0x48, 0x41, 0x96, 0x17, 0xae, 0x91, 0xa7, 0x38, 0x21, 0x32, 0x0c, 0x54, 0xdd, 0x35, 0xad, 0xe6, 0x31, 0x9e, 0x5c, 0x0c, 0xf8, 0x94, 0x6e, 0x89, 0x0d, - 0x9d, 0x21, 0x9f, 0x4d, 0x20, 0x75, 0x46, 0x82, 0xce, 0xf0, 0x4e, 0x03, 0xb5, 0xab, 0xe2, 0x2b, 0x91, 0x44, 0xca, + 0xdd, 0x22, 0x9f, 0x4d, 0x20, 0x75, 0x46, 0x82, 0xce, 0xf0, 0x4e, 0x03, 0xb5, 0xab, 0xe2, 0x2b, 0x91, 0x44, 0xca, 0x2b, 0xb2, 0x05, 0x8f, 0x49, 0x03, 0xc7, 0xa4, 0x81, 0x43, 0x92, 0xf5, 0x1a, 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, 0xf5, 0x2e, 0xc7, 0x7b, 0x7b, 0x9e, 0xea, 0xf4, 0x8b, 0xf0, 0xb8, 0xa9, 0x2f, 0x23, 0x77, 0xdf, 0x2b, 0x5e, 0x11, 0x21, 0x09, 0x7f, 0xad, 0x16, 0xf7, 0x73, 0x08, 0x43, 0x7b, 0x61, 0x15, 0x83, 0x06, 0x78, 0xa9, 0xeb, 0x55, 0x97, 0x5f, 0xab, 0x15, 0x51, - 0xda, 0x2a, 0xb6, 0xce, 0x70, 0x92, 0xcf, 0xbd, 0x22, 0xf5, 0xa7, 0xb1, 0x96, 0x2f, 0x65, 0x40, 0x40, 0xcc, 0xa6, + 0xda, 0x2a, 0xb6, 0x6e, 0x71, 0x92, 0xcf, 0xbd, 0x22, 0xf5, 0xa7, 0xb1, 0x96, 0x2f, 0x65, 0x40, 0x40, 0xcc, 0xa6, 0x59, 0x66, 0x16, 0x63, 0x1d, 0x09, 0x06, 0xed, 0xbe, 0xd1, 0x59, 0x0b, 0x58, 0x66, 0x57, 0xe9, 0x46, 0x86, 0x9d, 0xb5, 0x50, 0x60, 0x1a, 0x41, 0x54, 0x0a, 0x1a, 0xd5, 0x72, 0x4d, 0xde, 0x6f, 0xd7, 0x73, 0x2e, 0x71, 0x86, 0xb4, 0x93, 0x4b, 0x42, 0x21, 0x91, 0xd5, 0x2a, 0x90, 0xf2, 0x9c, 0x4c, 0xb7, 0x93, 0xfc, 0x99, 0x45, 0xf2, 0x4f, 0x08, @@ -268,7 +268,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x36, 0x44, 0x4d, 0xa5, 0xd4, 0x91, 0x2d, 0x50, 0xd1, 0xc1, 0x9f, 0x7b, 0x4c, 0x2b, 0x6e, 0x26, 0x6e, 0x06, 0x0c, 0xf8, 0x89, 0xf0, 0x54, 0x30, 0x0a, 0x64, 0x06, 0xf7, 0x67, 0x5e, 0x65, 0xea, 0x36, 0x97, 0xdd, 0xb0, 0x46, 0xdc, 0xd8, 0x46, 0x13, 0x97, 0x71, 0xbd, 0xf3, 0x92, 0x97, 0x0e, 0x55, 0x06, 0xb5, 0x30, 0x5c, 0xb0, 0x4c, 0x24, 0xb1, - 0x96, 0x3f, 0x54, 0x49, 0xd1, 0x45, 0x23, 0x4c, 0x25, 0x18, 0xef, 0xe4, 0x1e, 0xd0, 0x1c, 0xfe, 0x2e, 0x6e, 0x85, + 0x96, 0x3f, 0x54, 0x49, 0xd1, 0x45, 0x23, 0x4c, 0x25, 0x18, 0xef, 0xe4, 0x1e, 0xd0, 0x1c, 0xfe, 0x2e, 0xce, 0x84, 0xb5, 0xa3, 0xc6, 0x89, 0x2d, 0xe7, 0xb4, 0xa4, 0xfe, 0x5b, 0x48, 0x75, 0x59, 0x3d, 0xf3, 0xcf, 0xa5, 0x2c, 0x64, 0x38, 0xab, 0x30, 0xf6, 0x44, 0x32, 0x76, 0x04, 0x7a, 0x9a, 0x49, 0xfc, 0xee, 0xea, 0x8c, 0x17, 0xa6, 0xa5, 0x9c, 0x26, 0xb1, 0x37, 0x45, 0xb4, 0xdc, 0xfa, 0xbd, 0xb2, 0x1b, 0x01, 0x23, 0x90, 0x05, 0x84, 0x35, 0x67, 0x4f, 0x10, @@ -312,7 +312,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xf1, 0xb5, 0x7b, 0x78, 0x63, 0x02, 0x1e, 0xb4, 0x87, 0x4d, 0x61, 0x19, 0xdb, 0x99, 0xba, 0x07, 0x64, 0x8f, 0x4f, 0xb8, 0xd1, 0xdd, 0xaa, 0x56, 0xc6, 0x1b, 0xb0, 0xff, 0x11, 0x1e, 0x9b, 0xcb, 0x71, 0x54, 0x73, 0x60, 0x1a, 0x2c, 0xf2, 0xc2, 0x29, 0xc0, 0x95, 0xf2, 0x96, 0x22, 0xcc, 0x73, 0x19, 0xe0, 0xfe, 0x16, 0x7f, 0xa7, 0x59, 0xe2, 0xb0, - 0xe0, 0x38, 0xb7, 0x0f, 0xe5, 0x88, 0x0a, 0xfc, 0x22, 0x7e, 0x0f, 0x74, 0x2c, 0x29, 0x34, 0x37, 0x54, 0xf4, 0x94, + 0xe0, 0x38, 0x67, 0x0f, 0xe5, 0x88, 0x0a, 0xfc, 0x22, 0x7e, 0x0f, 0x74, 0x2c, 0x29, 0x34, 0x37, 0x54, 0xf4, 0x94, 0xeb, 0x85, 0x6c, 0x4d, 0x4b, 0xc5, 0xb4, 0x48, 0xa9, 0x91, 0xd3, 0x6c, 0xc8, 0xe3, 0x34, 0x56, 0xb6, 0x28, 0x4e, 0x55, 0x65, 0x5e, 0xb4, 0x05, 0x8b, 0x65, 0x68, 0x71, 0xb9, 0xf4, 0xaa, 0xa8, 0x26, 0xcc, 0x8a, 0x64, 0x20, 0xcc, 0xac, 0x8c, 0x8a, 0x8a, 0x66, 0xad, 0xfa, 0x78, 0x68, 0x35, 0xa1, 0xc8, 0xe8, 0xe6, 0x15, 0x38, 0x6c, 0x17, 0x82, @@ -612,31 +612,32 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xde, 0x0b, 0x3e, 0xda, 0x3c, 0x56, 0xcc, 0x47, 0x5d, 0x79, 0x05, 0x42, 0xdd, 0xb5, 0x35, 0xca, 0x2f, 0x8f, 0xdd, 0xce, 0xa9, 0x56, 0x06, 0x1c, 0x19, 0x0e, 0x77, 0x8f, 0x1a, 0xe6, 0x56, 0x45, 0xcc, 0x47, 0x70, 0x20, 0x55, 0x17, 0x6b, 0x92, 0x8a, 0xc7, 0x7d, 0xdc, 0xec, 0x9c, 0x86, 0x8e, 0xe4, 0x2d, 0x92, 0x79, 0x64, 0xc1, 0x3e, 0x74, 0x1e, - 0xf3, 0x09, 0xf5, 0x19, 0xdf, 0xbf, 0xa1, 0xd7, 0xf5, 0x70, 0xca, 0x4a, 0xf7, 0x36, 0x28, 0x1d, 0xc5, 0x94, 0xbc, - 0x9c, 0x09, 0x7e, 0x86, 0x2b, 0x8b, 0x94, 0x2c, 0x3c, 0xd7, 0xbe, 0x73, 0xb0, 0x55, 0x80, 0x84, 0x5c, 0x47, 0x71, - 0x78, 0xe3, 0x63, 0xd7, 0xb2, 0x37, 0x77, 0x3b, 0xff, 0xfa, 0x3f, 0xfe, 0x97, 0x76, 0x9b, 0x9f, 0xee, 0x8f, 0x9b, - 0x66, 0xac, 0x15, 0x44, 0xe7, 0xa7, 0x70, 0x11, 0xb1, 0x8c, 0xf3, 0xd2, 0xdb, 0xfa, 0x28, 0x65, 0x51, 0x7d, 0x1c, - 0xc6, 0x43, 0xb7, 0xb3, 0x1d, 0x41, 0xf6, 0x0d, 0x24, 0x0d, 0x75, 0xb5, 0x08, 0x48, 0xf0, 0x37, 0xdd, 0xa1, 0x31, - 0x57, 0x31, 0xe4, 0x69, 0xb5, 0x6f, 0xd4, 0x94, 0x07, 0xaa, 0x72, 0xab, 0x26, 0xd5, 0x5f, 0xaf, 0xd2, 0x4c, 0x2d, - 0xad, 0x5c, 0xa6, 0xc9, 0x5d, 0xa7, 0x88, 0x53, 0xfd, 0xdf, 0xff, 0xf9, 0x5f, 0xfe, 0x9b, 0x79, 0x84, 0xf0, 0xd3, - 0xbf, 0xfe, 0xf7, 0xff, 0xfc, 0x7f, 0xfe, 0xf7, 0x7f, 0x85, 0x0b, 0x18, 0x3a, 0x44, 0x25, 0xf9, 0x84, 0x53, 0xc6, - 0xa7, 0x14, 0xc3, 0x70, 0x20, 0x47, 0x71, 0xc2, 0x32, 0xc1, 0x06, 0xd5, 0xeb, 0x35, 0x17, 0x72, 0x42, 0x79, 0xd8, - 0x34, 0x74, 0xf2, 0xd0, 0xe6, 0x25, 0x8d, 0x54, 0x50, 0x2e, 0x69, 0x31, 0x3f, 0xdd, 0x07, 0x7c, 0x3f, 0xec, 0x46, - 0xa2, 0x5f, 0x6c, 0xc7, 0xc2, 0x38, 0x65, 0xa1, 0x24, 0x2f, 0xcb, 0x1d, 0x08, 0x97, 0x2c, 0xe0, 0x31, 0x68, 0x59, - 0xc5, 0x72, 0xf7, 0x2a, 0x7d, 0xda, 0x1f, 0x66, 0x99, 0x60, 0x43, 0x40, 0xb9, 0x72, 0xfd, 0xca, 0xc8, 0x74, 0x1d, - 0xd4, 0xbf, 0xf8, 0x2e, 0x97, 0xa3, 0x28, 0xdb, 0xfa, 0xf0, 0xe4, 0x4f, 0xf9, 0x5f, 0x26, 0xa0, 0x64, 0x39, 0xde, - 0x24, 0xbc, 0xd5, 0x16, 0xf7, 0x71, 0xa3, 0x31, 0xbd, 0x45, 0x8b, 0x72, 0x06, 0xbc, 0x6d, 0x32, 0xe9, 0x2e, 0xb6, - 0x07, 0x94, 0x21, 0xed, 0xc2, 0x33, 0xdd, 0x70, 0xc0, 0xbd, 0xed, 0x34, 0xf2, 0xfc, 0xcf, 0x0b, 0xe9, 0x1c, 0x65, - 0xbf, 0x42, 0xe8, 0x59, 0xfb, 0x91, 0xaf, 0xb9, 0xbd, 0xb8, 0x85, 0xd5, 0xab, 0xa5, 0x7a, 0x8d, 0x9b, 0xeb, 0x17, - 0xed, 0xec, 0xd0, 0xb9, 0x1d, 0xf4, 0x3e, 0x84, 0x30, 0xf6, 0xb8, 0x89, 0xc7, 0xad, 0x45, 0x31, 0xbc, 0x10, 0x7c, - 0x62, 0xc7, 0xca, 0x69, 0x48, 0x07, 0x74, 0x68, 0xfc, 0xef, 0xba, 0x5e, 0xc5, 0xc1, 0xf3, 0xf1, 0xc1, 0x86, 0xb9, - 0x34, 0x48, 0x32, 0x46, 0xee, 0x34, 0xf2, 0x2f, 0xe1, 0x04, 0x2e, 0x86, 0x31, 0x0f, 0x45, 0x20, 0x09, 0xb6, 0x6d, - 0x47, 0xdc, 0x43, 0x60, 0x33, 0x7c, 0x61, 0xc1, 0xd3, 0x56, 0x4d, 0xc1, 0x13, 0x5e, 0xbd, 0x0e, 0x99, 0xfb, 0xb2, - 0xbb, 0x3d, 0x94, 0x72, 0xa4, 0x7d, 0xaf, 0x03, 0xd9, 0xaf, 0x2a, 0x1e, 0x28, 0x2d, 0x63, 0x5a, 0x68, 0x73, 0xbd, - 0x12, 0xd5, 0xaa, 0xf6, 0x27, 0xe1, 0xb9, 0x12, 0x4c, 0x77, 0xb5, 0x95, 0x2c, 0x84, 0x56, 0xaf, 0xc8, 0xf7, 0x85, - 0x15, 0x14, 0x4e, 0xa7, 0xb2, 0x21, 0x6a, 0x9f, 0xee, 0x2b, 0xe5, 0x15, 0xb8, 0x87, 0xcc, 0xd2, 0x50, 0x49, 0x11, - 0xba, 0x91, 0x3e, 0x0a, 0xea, 0x97, 0x4e, 0x97, 0x80, 0xcf, 0x9a, 0x75, 0xfe, 0x1f, 0xd6, 0xb2, 0x30, 0xa4, 0x67, - 0x88, 0x00, 0x00}; + 0xf3, 0x09, 0xf5, 0x19, 0xdf, 0xbf, 0xa1, 0xd7, 0xf5, 0x70, 0xca, 0x4a, 0xf7, 0x36, 0x28, 0x1d, 0xc5, 0x94, 0xdc, + 0x78, 0xc4, 0xf5, 0x9d, 0xa3, 0x56, 0xe9, 0x6e, 0x3b, 0x04, 0x9b, 0xc7, 0xb8, 0xe6, 0xa4, 0x4f, 0xce, 0x02, 0x8b, + 0x77, 0x4e, 0xf7, 0xc3, 0x15, 0x8c, 0x48, 0x7e, 0x9f, 0x6b, 0x47, 0x3b, 0x18, 0x36, 0x40, 0x6f, 0xae, 0xa3, 0xc4, + 0x81, 0x71, 0xc8, 0x6b, 0x41, 0x9d, 0xbb, 0x9d, 0x7f, 0xfd, 0x1f, 0xff, 0x4b, 0xfb, 0xd8, 0x4f, 0xf7, 0xc7, 0x4d, + 0x33, 0xd6, 0xca, 0xae, 0xe4, 0xa7, 0x70, 0x6b, 0xb1, 0x0c, 0x0a, 0xd3, 0xdb, 0xfa, 0x28, 0x65, 0x51, 0x7d, 0x1c, + 0xc6, 0x43, 0xb7, 0xb3, 0x1d, 0x9b, 0xf6, 0x75, 0x25, 0x0d, 0x75, 0xb5, 0x08, 0xe8, 0xf5, 0x37, 0x5d, 0xb8, 0x31, + 0xf7, 0x36, 0xe4, 0xd1, 0xb6, 0xaf, 0xdf, 0x94, 0xa7, 0xaf, 0x72, 0x05, 0x27, 0xd5, 0x9f, 0xba, 0xd2, 0x1c, 0x30, + 0xad, 0xdc, 0xbc, 0xc9, 0x5d, 0xa7, 0x08, 0x6a, 0xfd, 0xdf, 0xff, 0xf9, 0x5f, 0xfe, 0x9b, 0x79, 0x84, 0x58, 0xd5, + 0xbf, 0xfe, 0xf7, 0xff, 0xfc, 0x7f, 0xfe, 0xf7, 0x7f, 0x85, 0xdb, 0x1a, 0x3a, 0x9e, 0x25, 0x99, 0x8a, 0x53, 0x06, + 0xb3, 0x14, 0x77, 0x71, 0x20, 0xa1, 0x71, 0xc2, 0x32, 0xc1, 0x06, 0xd5, 0xbb, 0x38, 0x17, 0x72, 0x42, 0x79, 0x32, + 0x35, 0x74, 0xf2, 0x84, 0xe7, 0x25, 0x41, 0x55, 0x50, 0x2e, 0x09, 0x37, 0x3f, 0xdd, 0x07, 0x7c, 0x3f, 0xec, 0xfa, + 0xa2, 0x5f, 0x6c, 0xc7, 0xc2, 0x90, 0x09, 0x94, 0xe4, 0x65, 0xb9, 0x03, 0xb1, 0x95, 0x05, 0x3c, 0x06, 0x2d, 0xab, + 0x58, 0xee, 0x5e, 0xa5, 0x4f, 0xfb, 0xc3, 0x2c, 0x13, 0x6c, 0x08, 0x28, 0x57, 0x7e, 0x62, 0x19, 0xc6, 0xae, 0x83, + 0xae, 0x18, 0xdf, 0xe5, 0x72, 0x14, 0x45, 0xa0, 0x87, 0x27, 0x7f, 0xca, 0xff, 0x32, 0x01, 0x8d, 0xcc, 0xf1, 0x26, + 0xe1, 0xad, 0x36, 0xcf, 0x8f, 0x1b, 0x8d, 0xe9, 0x2d, 0x5a, 0x94, 0x33, 0xe0, 0x6d, 0x93, 0x49, 0x3a, 0xb6, 0x07, + 0x94, 0xf1, 0xef, 0xc2, 0x8d, 0xdd, 0x70, 0xc0, 0x17, 0xee, 0x34, 0xf2, 0xfc, 0xcf, 0x0b, 0xe9, 0x49, 0x65, 0xbf, + 0x42, 0x9c, 0x5a, 0x3b, 0x9d, 0xaf, 0xb9, 0xbd, 0xb8, 0x85, 0xd5, 0xab, 0xa5, 0x7a, 0x8d, 0x9b, 0xeb, 0xb7, 0xf2, + 0xec, 0x38, 0xbb, 0x1d, 0x21, 0x3f, 0x84, 0x98, 0xf7, 0xb8, 0x89, 0xc7, 0xad, 0x45, 0x31, 0xbc, 0x10, 0x7c, 0x62, + 0x07, 0xd6, 0x69, 0x48, 0x07, 0x74, 0x68, 0x9c, 0xf5, 0xba, 0x5e, 0x05, 0xcd, 0xf3, 0xf1, 0xc1, 0x86, 0xb9, 0x34, + 0x48, 0x32, 0xa0, 0xee, 0x34, 0xf2, 0x2f, 0xe1, 0x04, 0x2e, 0x86, 0x31, 0x0f, 0x45, 0x20, 0x09, 0xb6, 0x6d, 0x87, + 0xe7, 0x43, 0xe0, 0x49, 0x7c, 0x61, 0xc1, 0xd3, 0x56, 0x4d, 0xc1, 0x6d, 0x5e, 0xbd, 0x3b, 0x99, 0xfb, 0xb2, 0xbb, + 0x3d, 0x94, 0xf2, 0xba, 0x7d, 0xaf, 0xa3, 0xde, 0xaf, 0x2a, 0xee, 0x2a, 0x2d, 0x90, 0x5a, 0x68, 0x73, 0xbd, 0x92, + 0xeb, 0xaa, 0xf6, 0x27, 0xe1, 0xb9, 0x12, 0x4c, 0x77, 0xb5, 0x95, 0x2c, 0x84, 0x56, 0xaf, 0xc8, 0xf7, 0x85, 0xc9, + 0x14, 0x4e, 0xa7, 0xb2, 0x21, 0x6a, 0x9f, 0xee, 0x2b, 0x4d, 0x17, 0xb8, 0x87, 0x4c, 0xe9, 0x50, 0x19, 0x14, 0xba, + 0x91, 0x3e, 0x0a, 0xea, 0x97, 0xce, 0xad, 0x80, 0x6f, 0xa0, 0x75, 0xfe, 0x1f, 0xa2, 0x48, 0xf6, 0xdd, 0x94, 0x88, + 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 bde1ce1fb5..0c16ea9f37 100644 --- a/esphome/components/web_server/server_index_v3.h +++ b/esphome/components/web_server/server_index_v3.h @@ -3632,373 +3632,374 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xe7, 0xec, 0xd8, 0x98, 0x31, 0x94, 0x4f, 0x43, 0x40, 0x9e, 0xd0, 0xf7, 0x01, 0xcd, 0x25, 0x67, 0x23, 0xad, 0x2b, 0xfb, 0x10, 0x17, 0x97, 0xdc, 0x84, 0x6a, 0x31, 0x6f, 0x2b, 0x3d, 0x2a, 0xc4, 0x1b, 0x16, 0x80, 0x65, 0xe9, 0x69, 0x93, 0x82, 0x6c, 0x94, 0x54, 0x45, 0xfe, 0x13, 0xbf, 0x03, 0xae, 0xad, 0xac, 0xe4, 0x0a, 0x78, 0xf5, 0xff, 0xd3, - 0xdc, 0x93, 0x2e, 0xb7, 0x6d, 0x24, 0xfd, 0x3f, 0x4f, 0x01, 0xc3, 0x5e, 0x87, 0xb0, 0x01, 0x08, 0x00, 0x45, 0x89, - 0x26, 0x45, 0x69, 0x13, 0x1f, 0xb5, 0x4e, 0x29, 0x71, 0xca, 0x56, 0x5c, 0xbb, 0x51, 0x54, 0x22, 0x48, 0x0e, 0x49, - 0xac, 0x41, 0x80, 0x05, 0x80, 0x3a, 0x42, 0x63, 0x9f, 0x65, 0x9f, 0x65, 0x9f, 0xec, 0xab, 0xee, 0x9e, 0x19, 0x0c, - 0x0e, 0x1e, 0x8a, 0x9d, 0xdd, 0xaf, 0x12, 0xdb, 0xc4, 0xdc, 0xd3, 0x33, 0xd3, 0xd3, 0xd3, 0xa7, 0x3f, 0xe3, 0xbc, - 0x17, 0xb3, 0xc5, 0xf6, 0xeb, 0xee, 0xf3, 0x67, 0x66, 0xe3, 0x96, 0x04, 0x82, 0xcf, 0xce, 0xe2, 0xd9, 0x2c, 0x64, - 0x2d, 0x5d, 0x04, 0x0f, 0xd1, 0x4d, 0xd9, 0xcd, 0xd9, 0x23, 0x47, 0x78, 0xec, 0x34, 0xf2, 0x4d, 0x47, 0x4b, 0xcc, - 0x98, 0x49, 0x97, 0x76, 0x44, 0xb9, 0x22, 0x6f, 0xf6, 0x06, 0xc5, 0x1b, 0x7c, 0x5d, 0x8a, 0xa3, 0x6b, 0x4d, 0xe2, - 0xd5, 0x28, 0x64, 0x16, 0x6e, 0x77, 0xe8, 0x72, 0x3d, 0x5a, 0x8d, 0x46, 0x10, 0xa5, 0xe5, 0x91, 0x63, 0x82, 0xdf, - 0x99, 0x38, 0xc5, 0xf7, 0x60, 0x6e, 0xf4, 0x61, 0x52, 0x76, 0x56, 0x1d, 0x3e, 0xe8, 0x8a, 0x00, 0xab, 0x87, 0x3a, - 0xc8, 0xe0, 0xed, 0xd7, 0x70, 0x6a, 0x07, 0xfa, 0x07, 0xd8, 0x7d, 0xa9, 0xde, 0x6f, 0x3a, 0xfa, 0x83, 0x4b, 0xfd, - 0x03, 0xc2, 0x18, 0xa3, 0x17, 0xbf, 0xa4, 0xdd, 0xab, 0x9b, 0x3a, 0x09, 0xbd, 0x57, 0x18, 0xc7, 0x00, 0x98, 0xbe, - 0xaf, 0x02, 0x7f, 0x16, 0xc5, 0x69, 0x16, 0x8c, 0xf5, 0xab, 0xfe, 0xdb, 0xa0, 0x75, 0xb9, 0xc8, 0x5a, 0xc6, 0x95, - 0x39, 0xce, 0xd4, 0x10, 0x28, 0x02, 0x61, 0x62, 0x04, 0x94, 0x4d, 0x85, 0xd4, 0x13, 0xb4, 0xb5, 0xa0, 0x40, 0xcd, - 0x58, 0x68, 0x9c, 0x0d, 0xa0, 0x5c, 0x25, 0x9e, 0x0a, 0x06, 0x86, 0xd2, 0xb1, 0xa6, 0xd1, 0xa7, 0x97, 0xca, 0xcb, - 0xd5, 0x1a, 0xaf, 0xf2, 0xac, 0xb8, 0x2d, 0xd1, 0x07, 0xb0, 0x30, 0x9c, 0xa1, 0xef, 0x47, 0xaa, 0xd2, 0x67, 0xe9, - 0xde, 0x1d, 0x7e, 0x57, 0xa6, 0x0b, 0xe0, 0xfe, 0x06, 0x8d, 0x8b, 0x28, 0xce, 0x34, 0x70, 0x6c, 0x03, 0x3d, 0x0e, - 0xab, 0x4a, 0x62, 0xbc, 0xd5, 0x96, 0x91, 0x73, 0x64, 0xf0, 0x3d, 0x5e, 0x7e, 0x2d, 0xee, 0xde, 0xac, 0xe4, 0xc1, - 0x82, 0x1e, 0x0b, 0x11, 0x2c, 0x60, 0x16, 0x9f, 0xc7, 0xb7, 0x55, 0x39, 0xc8, 0xcb, 0xe1, 0xee, 0xbb, 0xb7, 0x25, - 0xc8, 0x64, 0x11, 0xd5, 0xaf, 0xc5, 0x03, 0x93, 0x0a, 0x42, 0xa7, 0x72, 0xa6, 0x50, 0xf1, 0x43, 0xd0, 0x30, 0x19, - 0xe8, 0x89, 0xe1, 0x5d, 0x00, 0x28, 0x89, 0x5f, 0xd3, 0xc3, 0xfc, 0x5a, 0x84, 0x4e, 0x16, 0x81, 0x8b, 0x95, 0xcb, - 0x19, 0xb0, 0x6b, 0xb4, 0x5c, 0x65, 0xe8, 0x6a, 0x17, 0x06, 0xc0, 0x72, 0x5d, 0x43, 0xd7, 0x9d, 0x80, 0xa5, 0x0b, - 0x32, 0x31, 0xd7, 0xb5, 0x60, 0x52, 0x4f, 0xe3, 0x44, 0x2f, 0x20, 0x2f, 0xc4, 0xef, 0xc8, 0xa8, 0x82, 0xcf, 0x84, - 0x4f, 0x63, 0x6c, 0x16, 0x7e, 0xea, 0x5b, 0x63, 0x14, 0xe8, 0x34, 0x60, 0x86, 0x31, 0xb5, 0xd3, 0x6f, 0x85, 0x8d, - 0x93, 0x05, 0xf7, 0x9b, 0xa5, 0x69, 0x0e, 0x9f, 0xac, 0xa3, 0xfc, 0xec, 0xc9, 0x3a, 0xcd, 0x07, 0x4f, 0xd6, 0xbe, - 0xd4, 0x15, 0xd0, 0x2f, 0x74, 0x52, 0x14, 0x18, 0x22, 0x18, 0x86, 0xf9, 0x75, 0x61, 0xb9, 0x53, 0xcc, 0x17, 0x76, - 0x19, 0xa5, 0x6b, 0x28, 0xba, 0x1f, 0x70, 0x01, 0xfd, 0x32, 0x09, 0x16, 0x7e, 0x72, 0x4f, 0xf2, 0x7c, 0x53, 0x15, - 0xfa, 0x1b, 0xba, 0x46, 0x88, 0x9e, 0x00, 0x40, 0x38, 0x5f, 0xd7, 0xfe, 0x2a, 0xd3, 0x18, 0x9f, 0xad, 0x14, 0x6a, - 0x42, 0x5f, 0xd7, 0xfa, 0x73, 0x66, 0x4f, 0x58, 0xe6, 0x07, 0x21, 0x55, 0xe9, 0x8b, 0x68, 0xf5, 0xb5, 0xe9, 0xa5, - 0xe5, 0xe9, 0x45, 0xe5, 0xfd, 0x83, 0x93, 0xa1, 0x2b, 0x80, 0xc6, 0x8d, 0x33, 0xc3, 0x28, 0x56, 0xcd, 0x2b, 0x4a, - 0x79, 0xff, 0xd5, 0xe5, 0x60, 0xb0, 0x1c, 0x11, 0x2c, 0x07, 0x8b, 0xc6, 0xf1, 0x84, 0xfd, 0xf2, 0xfe, 0xad, 0x0c, - 0x9b, 0x05, 0x1c, 0xa0, 0x21, 0xdf, 0x98, 0x29, 0xd2, 0x0f, 0x09, 0xd2, 0x0e, 0x14, 0xe0, 0x4a, 0x93, 0x5b, 0x28, - 0xc9, 0x75, 0xed, 0x8c, 0xc6, 0xce, 0x26, 0x34, 0xea, 0x41, 0x8c, 0xb5, 0x92, 0xfc, 0xe4, 0x80, 0x4a, 0xd3, 0x6d, - 0x47, 0x85, 0x00, 0x0c, 0x09, 0xcc, 0xb0, 0x80, 0x02, 0x44, 0xf8, 0x1c, 0xb8, 0xc5, 0x83, 0xc2, 0x5e, 0x20, 0x9f, - 0xdd, 0x3d, 0x2b, 0x93, 0x2a, 0x58, 0x4b, 0x3f, 0x3d, 0xc1, 0x98, 0x5d, 0x70, 0x5f, 0x83, 0x97, 0x8f, 0x93, 0x03, - 0xfa, 0xd4, 0x2a, 0x27, 0xa2, 0x68, 0x44, 0x3c, 0xed, 0x7a, 0xbc, 0x81, 0x07, 0x1d, 0x15, 0x08, 0x11, 0x0f, 0xa9, - 0x7e, 0xae, 0x6b, 0x0b, 0x4e, 0x1a, 0x71, 0x77, 0x42, 0xe0, 0x6b, 0xc0, 0x81, 0xb3, 0xab, 0x6b, 0x0b, 0xff, 0x0e, - 0x67, 0x2e, 0x72, 0xfc, 0xbb, 0x96, 0xcb, 0xb3, 0x8a, 0xb3, 0x96, 0x96, 0xcf, 0xda, 0x98, 0x2f, 0x2e, 0x18, 0x12, - 0xc8, 0x97, 0xf5, 0x1c, 0x05, 0xb4, 0x0d, 0x8b, 0x3b, 0x17, 0x8b, 0x3b, 0xd9, 0xb0, 0xb8, 0x93, 0x2d, 0x8b, 0x1b, - 0xf2, 0x85, 0xd4, 0x24, 0xe8, 0x12, 0x34, 0x0e, 0x93, 0xc0, 0xe3, 0x84, 0x46, 0x8f, 0x9f, 0x33, 0x84, 0x93, 0x95, - 0x86, 0xa0, 0x1c, 0xb5, 0x01, 0x56, 0x4d, 0x70, 0x51, 0x00, 0x51, 0x9f, 0xb8, 0x3c, 0x75, 0x62, 0xde, 0x10, 0x83, - 0xb3, 0x15, 0x56, 0xe7, 0x0b, 0xbb, 0x94, 0xe2, 0x8b, 0xb7, 0xe6, 0x1b, 0x66, 0x3a, 0xdf, 0x32, 0xd3, 0x71, 0xe9, - 0xe8, 0xf2, 0x69, 0xd3, 0x21, 0x54, 0x27, 0x05, 0x7b, 0x10, 0x14, 0x46, 0x71, 0xcb, 0x94, 0xf7, 0xe1, 0x66, 0x1c, - 0xab, 0xec, 0xa8, 0xa5, 0x9f, 0xa6, 0xb7, 0x71, 0x02, 0x12, 0x17, 0x68, 0xe6, 0x61, 0x5b, 0x6a, 0x11, 0x44, 0xdc, - 0x99, 0xcb, 0xc6, 0xcd, 0x54, 0xe4, 0xab, 0x5b, 0xca, 0xeb, 0x74, 0xa8, 0xc4, 0xd2, 0xcf, 0x32, 0x96, 0x20, 0xd0, - 0x7d, 0xf0, 0xfa, 0xfd, 0xff, 0x64, 0x9b, 0x35, 0xe0, 0x90, 0x50, 0xc1, 0xea, 0x88, 0xa1, 0x97, 0x40, 0x5b, 0x25, - 0xe2, 0x22, 0x56, 0x1c, 0xc3, 0x25, 0x12, 0xf0, 0x3f, 0xe1, 0x71, 0x6d, 0x25, 0x8a, 0xe9, 0x92, 0x7b, 0x64, 0xd8, - 0x4b, 0x7f, 0xf2, 0x01, 0x04, 0x7b, 0x2d, 0xcf, 0x04, 0x25, 0x5d, 0xd5, 0x0d, 0x5c, 0x42, 0xc4, 0xde, 0xb8, 0x40, - 0x92, 0x88, 0x25, 0xb9, 0x0a, 0x14, 0x58, 0x4f, 0xfa, 0xd6, 0xf4, 0x6a, 0xed, 0xe5, 0x07, 0xb3, 0xc0, 0xa8, 0x61, - 0x4d, 0x40, 0x6d, 0xe1, 0xe0, 0x54, 0xbe, 0xb9, 0x42, 0xd3, 0x3d, 0x32, 0x80, 0xf3, 0x7b, 0x09, 0xf1, 0x4c, 0x1d, - 0xf1, 0xa0, 0x1d, 0x26, 0x70, 0x6b, 0x5d, 0x3a, 0x57, 0xf9, 0xd3, 0x19, 0xfe, 0x72, 0xaf, 0xf2, 0xa7, 0x23, 0xfc, - 0xe5, 0x5d, 0x61, 0xe4, 0xba, 0x86, 0x87, 0xbc, 0x32, 0x67, 0xfd, 0xb4, 0xb4, 0x9f, 0x48, 0xff, 0xec, 0x01, 0xdb, - 0x86, 0x2f, 0xf0, 0xe3, 0x27, 0xeb, 0x14, 0x2c, 0x2e, 0xd5, 0x39, 0x44, 0x76, 0x62, 0xe4, 0x8d, 0xe9, 0xb3, 0x0d, - 0xe9, 0x23, 0xe3, 0xbf, 0x7c, 0xf1, 0xe3, 0x2e, 0x89, 0x8b, 0x3b, 0xa5, 0xcc, 0x86, 0xb8, 0x1e, 0x05, 0x91, 0x9f, - 0xdc, 0x5f, 0xd3, 0xf3, 0xa2, 0x25, 0x68, 0x77, 0xc9, 0x5e, 0x21, 0xf2, 0xb2, 0x2c, 0xee, 0xca, 0x14, 0x06, 0xef, - 0x3d, 0xbf, 0xe8, 0x07, 0x7f, 0x4f, 0x14, 0xb2, 0xad, 0xf4, 0x00, 0xe5, 0x0b, 0x52, 0xea, 0xe8, 0xfa, 0xc9, 0xba, - 0xc5, 0xea, 0xcd, 0x54, 0x66, 0x5b, 0xa1, 0x0b, 0x61, 0x79, 0xf0, 0x31, 0xbb, 0x98, 0x04, 0x3d, 0x94, 0x67, 0x8d, - 0xe2, 0x3b, 0xeb, 0xc9, 0x3a, 0x3b, 0xd3, 0x17, 0x7e, 0xf2, 0x89, 0x4d, 0xac, 0x71, 0x90, 0x8c, 0x43, 0xa6, 0xf7, - 0xf4, 0x51, 0xe8, 0x47, 0x9f, 0xf8, 0xa7, 0x15, 0xaf, 0x32, 0x94, 0x50, 0xef, 0x7c, 0xfb, 0x0a, 0x98, 0x10, 0xcb, - 0x0e, 0x89, 0xd5, 0x06, 0x28, 0x68, 0x2f, 0x25, 0xc3, 0xab, 0x20, 0x14, 0x8b, 0x52, 0x26, 0x28, 0x58, 0x82, 0xd0, - 0x1c, 0x2c, 0x56, 0x4d, 0x1d, 0xd7, 0x4b, 0x37, 0xd5, 0xa9, 0x12, 0xb3, 0x52, 0x86, 0x5c, 0xbc, 0xc6, 0x16, 0xfe, - 0x78, 0x77, 0x14, 0x0c, 0x7b, 0xff, 0xee, 0x64, 0x2b, 0x5f, 0x36, 0x43, 0x48, 0xb5, 0xc8, 0x52, 0xe2, 0x01, 0x9d, - 0x73, 0x02, 0x73, 0x73, 0xd7, 0x6a, 0x65, 0x3f, 0x4d, 0x57, 0x0b, 0x36, 0x21, 0xc9, 0xe0, 0x59, 0x31, 0xa8, 0xf2, - 0xcb, 0x42, 0x1d, 0xd8, 0x6f, 0x2b, 0xef, 0xf8, 0xf0, 0x25, 0x68, 0x2c, 0x00, 0x41, 0x19, 0x4f, 0xa7, 0x7a, 0xf1, - 0xc6, 0xdf, 0x51, 0xcd, 0x3d, 0xfc, 0x6d, 0xf5, 0xe6, 0xb5, 0xf3, 0x46, 0x56, 0x8e, 0x80, 0x30, 0x16, 0xe2, 0x57, - 0x4e, 0x17, 0x2b, 0xe3, 0x15, 0x33, 0x9a, 0xfa, 0xd1, 0xe6, 0xe9, 0x5c, 0x96, 0xb6, 0xf8, 0x92, 0xb1, 0x09, 0x10, - 0xdc, 0x66, 0x2d, 0xf5, 0x3a, 0x64, 0x37, 0x4c, 0x8a, 0x76, 0xeb, 0x9d, 0x35, 0xd4, 0x40, 0xdf, 0x73, 0x5c, 0x64, - 0xcc, 0xa9, 0x3a, 0x65, 0x4a, 0x43, 0x9c, 0x03, 0x9f, 0xb9, 0x7a, 0xc4, 0x2a, 0x47, 0x6a, 0x68, 0xea, 0xca, 0x00, - 0x36, 0x8e, 0xec, 0x6c, 0x43, 0x7a, 0x0f, 0x03, 0x4f, 0x37, 0x8f, 0xcd, 0x74, 0x8d, 0x1e, 0xf8, 0xea, 0xe6, 0x70, - 0x0a, 0xe1, 0xe4, 0xb5, 0x0a, 0x76, 0xc8, 0x26, 0x88, 0x35, 0x31, 0xc9, 0x74, 0xe2, 0xbe, 0x08, 0x6d, 0x47, 0x54, - 0xfb, 0x15, 0x7c, 0xa8, 0xc6, 0xb5, 0xd1, 0xca, 0x33, 0x1f, 0x61, 0x40, 0xd7, 0x88, 0xa5, 0xe9, 0x46, 0x80, 0xc9, - 0x45, 0x37, 0xf5, 0xa2, 0x74, 0x19, 0x1e, 0x45, 0xba, 0xe9, 0x98, 0x40, 0x12, 0xe0, 0x04, 0xab, 0x7d, 0xe1, 0xf5, - 0x72, 0xbd, 0xe0, 0xfa, 0x2a, 0xc9, 0x6c, 0xa4, 0x73, 0x5d, 0x82, 0x4d, 0xf9, 0xb7, 0x3a, 0x1f, 0x54, 0xe9, 0x9a, - 0x6e, 0x1c, 0x5a, 0xab, 0x84, 0x7a, 0x6b, 0xec, 0x22, 0x6c, 0x40, 0x8c, 0xa9, 0x82, 0x5f, 0xd9, 0x74, 0xca, 0xc6, - 0x59, 0x6a, 0x08, 0xe6, 0x91, 0xf4, 0x1e, 0x0b, 0x56, 0x43, 0x8f, 0x06, 0xfa, 0x4f, 0x60, 0x43, 0x2f, 0x9c, 0x2c, - 0xf1, 0x01, 0x89, 0x37, 0x53, 0x33, 0x98, 0xa8, 0xc5, 0x32, 0x88, 0x78, 0x2f, 0x10, 0x1c, 0xbc, 0x21, 0x1d, 0x87, - 0xc6, 0xef, 0x9f, 0x62, 0x5f, 0xc4, 0x52, 0xab, 0x65, 0x3b, 0x2a, 0xda, 0x76, 0x7c, 0xd7, 0xee, 0x9b, 0x8e, 0xeb, - 0xe4, 0xba, 0x09, 0xb6, 0x5b, 0x9f, 0xf6, 0x3d, 0xf4, 0x58, 0xab, 0x0d, 0xb5, 0x56, 0xd1, 0x43, 0xea, 0x79, 0xee, - 0x0b, 0x57, 0x37, 0x49, 0x65, 0x4e, 0xc1, 0x6d, 0xe3, 0xf8, 0x86, 0x25, 0x5f, 0x3c, 0x95, 0x72, 0xe3, 0xfb, 0x8d, - 0xe7, 0xc8, 0x75, 0x00, 0x09, 0x67, 0xf1, 0xf2, 0x01, 0x53, 0x68, 0xeb, 0xa6, 0x3e, 0x0e, 0xe3, 0x94, 0xa9, 0x73, - 0x20, 0x26, 0xc8, 0x17, 0x4e, 0xe2, 0xe7, 0xf7, 0xaf, 0x3f, 0x7c, 0xd0, 0x4d, 0x8c, 0x04, 0x9a, 0xaa, 0xad, 0xf3, - 0x0d, 0xb5, 0x03, 0xfb, 0x37, 0xee, 0x3b, 0xba, 0x61, 0xe8, 0x51, 0x5b, 0xde, 0x73, 0x94, 0x56, 0xdb, 0x72, 0xfc, - 0xe6, 0xe1, 0x3d, 0xd3, 0x4b, 0x74, 0xaf, 0x79, 0x35, 0xe0, 0x86, 0xed, 0xd7, 0x5b, 0x29, 0x65, 0x11, 0x44, 0xd7, - 0x0d, 0xa9, 0xfe, 0x5d, 0x43, 0x2a, 0x3c, 0xe5, 0x6a, 0xb8, 0x6a, 0x15, 0x2f, 0x14, 0xd2, 0x00, 0x02, 0x39, 0xef, - 0x02, 0x97, 0xf2, 0x9e, 0xfa, 0x82, 0x41, 0x73, 0x4f, 0xee, 0xd5, 0x51, 0x37, 0x24, 0xf3, 0x47, 0x90, 0x84, 0xed, - 0x38, 0x04, 0x85, 0x3f, 0xa6, 0x4a, 0xe5, 0xca, 0x64, 0xa3, 0x54, 0xd7, 0x55, 0x1a, 0x21, 0xf2, 0xf6, 0x3a, 0x63, - 0x8b, 0x25, 0x4b, 0xfc, 0x6c, 0x95, 0xb0, 0xeb, 0x30, 0xbe, 0x7d, 0x54, 0xa8, 0xd3, 0xef, 0x28, 0x3c, 0x0f, 0x66, - 0x73, 0x59, 0xfa, 0xac, 0xc5, 0x06, 0x72, 0x01, 0xb7, 0x76, 0x90, 0xff, 0xe7, 0xdf, 0xb6, 0xfd, 0x9f, 0x7f, 0xef, - 0x2c, 0x0a, 0xcd, 0xe7, 0x43, 0x33, 0x1b, 0xec, 0xb1, 0x2f, 0x9a, 0x7b, 0x2a, 0xc3, 0xbc, 0xb9, 0x4c, 0x6d, 0x11, - 0x20, 0xbf, 0xb6, 0x04, 0xb5, 0xc4, 0xf2, 0xbe, 0x79, 0xd0, 0xc0, 0x60, 0x5e, 0x3b, 0x47, 0x06, 0x85, 0xbe, 0x68, - 0x68, 0x43, 0xa3, 0xb7, 0xd7, 0x8a, 0xfc, 0x71, 0x08, 0xef, 0x9a, 0xc3, 0x17, 0x0e, 0x9f, 0xf3, 0x25, 0x5f, 0x0e, - 0x87, 0x32, 0xb6, 0x9c, 0x5a, 0x15, 0x54, 0xfc, 0xcf, 0x6a, 0x29, 0xfc, 0xf2, 0xec, 0x39, 0x06, 0xd9, 0xde, 0x0f, - 0x5e, 0x0e, 0x51, 0x19, 0xed, 0x64, 0x94, 0x14, 0xc4, 0xca, 0x46, 0xd4, 0x46, 0xca, 0xe4, 0xb5, 0x46, 0x6b, 0x78, - 0x0d, 0x52, 0x31, 0xe0, 0x58, 0x3e, 0x34, 0xcc, 0x97, 0x43, 0xce, 0x58, 0xe2, 0xfa, 0xaf, 0xbd, 0xea, 0xd6, 0xe6, - 0x6c, 0xd9, 0x12, 0xd0, 0x4d, 0x8d, 0xe4, 0x3f, 0x58, 0x98, 0x15, 0x7c, 0x3c, 0x64, 0xf0, 0x03, 0x47, 0x61, 0x98, - 0x63, 0xbc, 0x93, 0x77, 0x9b, 0x74, 0xc4, 0x7e, 0xde, 0xad, 0x23, 0x76, 0xb1, 0x97, 0x8e, 0xd8, 0xcf, 0x5f, 0x5d, - 0x47, 0xec, 0x9d, 0xaa, 0x23, 0x06, 0x8b, 0xf8, 0x9a, 0xed, 0xa5, 0xb8, 0x25, 0xb4, 0x36, 0xe2, 0xdb, 0x74, 0xe0, - 0x72, 0x92, 0x36, 0x1d, 0xcf, 0x19, 0xf0, 0x08, 0xf8, 0xaa, 0x84, 0xf1, 0x0c, 0x94, 0xb8, 0xfe, 0x7c, 0x75, 0xab, - 0x30, 0x9e, 0xa9, 0xca, 0x56, 0x11, 0xf7, 0xf8, 0x5a, 0x78, 0x71, 0x22, 0x05, 0x27, 0xc7, 0x14, 0x3e, 0x9f, 0xac, - 0x43, 0x43, 0x89, 0x6a, 0x2d, 0xb5, 0xd7, 0x3c, 0xa1, 0x02, 0xd5, 0x43, 0xed, 0x29, 0x59, 0xd1, 0x7b, 0x2e, 0x7c, - 0x5b, 0xa8, 0x2d, 0x48, 0x2d, 0x61, 0xf2, 0x13, 0xb1, 0xd6, 0x7f, 0xbb, 0x73, 0xbf, 0xbf, 0x74, 0xfb, 0x6d, 0x17, - 0x8c, 0xb3, 0xe1, 0x85, 0x89, 0x09, 0x4e, 0xbf, 0xdd, 0x86, 0x84, 0x5b, 0x25, 0xc1, 0x83, 0x84, 0x40, 0x49, 0xe8, - 0x40, 0xc2, 0x58, 0x49, 0x38, 0x82, 0x84, 0x89, 0x92, 0x70, 0x0c, 0x09, 0x37, 0x7a, 0x7e, 0x19, 0xc9, 0xe1, 0x1e, - 0x1b, 0x57, 0x26, 0x3d, 0x2a, 0x44, 0xda, 0xb1, 0xe9, 0x82, 0xd6, 0x94, 0x3f, 0xeb, 0xc5, 0x26, 0x71, 0x17, 0x7b, - 0x89, 0x79, 0x3b, 0x67, 0xe4, 0x28, 0xfa, 0x15, 0xde, 0x39, 0x76, 0x16, 0x83, 0xde, 0xb4, 0x70, 0xc0, 0x20, 0xe0, - 0xa0, 0xe9, 0x06, 0x30, 0x8c, 0xfa, 0x72, 0xe5, 0x84, 0x13, 0x0b, 0x65, 0x2d, 0x8b, 0x3c, 0xea, 0xce, 0x92, 0x5b, - 0xa0, 0xd0, 0x38, 0x69, 0xa9, 0x5c, 0xc9, 0xaf, 0xa1, 0x77, 0xf0, 0x8a, 0x8d, 0x56, 0x33, 0xed, 0x3c, 0x9e, 0xed, - 0x54, 0x21, 0x50, 0xb3, 0x60, 0x94, 0x3a, 0x89, 0x5f, 0x2c, 0xb1, 0x2d, 0x79, 0x5f, 0xf4, 0x99, 0x97, 0xcb, 0x67, - 0x30, 0x36, 0x2d, 0x23, 0x05, 0x16, 0xe8, 0x07, 0x60, 0xa4, 0xc8, 0xf0, 0xcf, 0x01, 0xce, 0xca, 0xf7, 0x85, 0xaf, - 0x8c, 0xe7, 0xf4, 0x47, 0x96, 0xa6, 0xfe, 0x4c, 0x94, 0xaf, 0x8f, 0x13, 0x94, 0x76, 0xe4, 0xfb, 0x0b, 0x01, 0x08, - 0x9c, 0xbc, 0xa0, 0xa6, 0x9b, 0x91, 0xc4, 0xb7, 0x1a, 0x68, 0xff, 0xc0, 0x86, 0x2a, 0xf4, 0x14, 0x02, 0x1b, 0x96, - 0xb0, 0xac, 0x51, 0x00, 0x87, 0xff, 0x86, 0x85, 0xd5, 0xc4, 0xcc, 0x9f, 0x55, 0x93, 0x68, 0x1f, 0xe4, 0xea, 0xd8, - 0xa4, 0x40, 0xbf, 0x94, 0xf8, 0x25, 0x12, 0xea, 0x30, 0x9e, 0xfd, 0xa9, 0xe2, 0xe9, 0x2d, 0x6a, 0x05, 0x1f, 0x22, - 0x33, 0xc8, 0x86, 0x36, 0xc2, 0x58, 0xb3, 0x01, 0x84, 0xbd, 0x28, 0x9b, 0x5b, 0x68, 0x5a, 0xd6, 0xf2, 0x22, 0xc3, - 0xb4, 0x71, 0x6d, 0xd7, 0x55, 0x83, 0xda, 0x5e, 0x32, 0x1b, 0xf9, 0x2d, 0xd7, 0x3b, 0x36, 0xc5, 0x1f, 0xdb, 0xe9, - 0x18, 0x39, 0xb6, 0xa0, 0x4d, 0x82, 0x9b, 0xf5, 0x34, 0x8e, 0x32, 0x6b, 0xea, 0x2f, 0x82, 0xf0, 0xbe, 0xb7, 0x88, - 0xa3, 0x38, 0x5d, 0xfa, 0x63, 0xd6, 0x2f, 0x1e, 0xd4, 0x7d, 0x74, 0xd5, 0xc0, 0xad, 0x05, 0x5d, 0xdb, 0x4b, 0xd8, - 0x82, 0x6a, 0x4b, 0x4f, 0x0c, 0xd3, 0x90, 0xdd, 0xe5, 0xbc, 0xfb, 0x52, 0x61, 0x2a, 0x8a, 0x5b, 0x8e, 0x6a, 0x00, - 0x45, 0xca, 0xdd, 0x3c, 0x80, 0x73, 0xa3, 0xfe, 0xd2, 0x9f, 0xa0, 0x67, 0x42, 0xdb, 0xeb, 0x24, 0x6c, 0xa1, 0xd9, - 0x9d, 0x8d, 0x8d, 0x27, 0xf1, 0xed, 0x29, 0x8c, 0x16, 0x2b, 0x5b, 0x29, 0x0b, 0xa7, 0x98, 0x63, 0xa1, 0x65, 0x89, - 0x68, 0xc7, 0xc2, 0x87, 0x38, 0xb4, 0xc6, 0x16, 0x7d, 0xc8, 0xee, 0x79, 0x9a, 0xd3, 0x5f, 0x04, 0x91, 0x45, 0xd3, - 0x39, 0x76, 0x96, 0x4a, 0x5b, 0x2a, 0xfc, 0x8c, 0x35, 0x16, 0x77, 0x35, 0xa7, 0x0f, 0x8f, 0xb5, 0x69, 0x18, 0xdf, - 0xf6, 0xe6, 0xc1, 0x64, 0xc2, 0xa2, 0x3e, 0x8e, 0x59, 0x26, 0xb2, 0x30, 0x0c, 0x96, 0x69, 0x90, 0xf6, 0x17, 0xfe, - 0x1d, 0x6f, 0xf5, 0x70, 0x53, 0xab, 0x6d, 0xde, 0x6a, 0x7b, 0xef, 0x56, 0x95, 0x66, 0xc0, 0x8a, 0x85, 0xda, 0xe1, - 0x43, 0xeb, 0x68, 0x4e, 0x65, 0x9e, 0x7b, 0xb7, 0xba, 0x4c, 0xd8, 0x7a, 0xe1, 0x27, 0xb3, 0x20, 0xea, 0x39, 0xb9, - 0x7d, 0xb3, 0xa6, 0x8d, 0xf1, 0xb8, 0xdb, 0xed, 0xe6, 0xf6, 0x44, 0x7c, 0x39, 0x93, 0x49, 0x6e, 0x8f, 0xc5, 0xd7, - 0x74, 0xea, 0x38, 0xd3, 0x69, 0x6e, 0x07, 0x22, 0xa1, 0xed, 0x8d, 0x27, 0x6d, 0x2f, 0xb7, 0x6f, 0x95, 0x12, 0xb9, - 0xcd, 0xf8, 0x57, 0xc2, 0x26, 0x7d, 0xdc, 0x48, 0xa4, 0x56, 0xda, 0x3b, 0x76, 0x9c, 0x1c, 0x31, 0xc0, 0x65, 0x09, - 0x37, 0x21, 0xaf, 0xe7, 0x6a, 0xbd, 0x77, 0x49, 0xad, 0xe8, 0x6e, 0x3c, 0x6e, 0x2c, 0x37, 0xf1, 0x93, 0x4f, 0x57, - 0x9a, 0x32, 0x0b, 0xdf, 0xa7, 0x62, 0x6b, 0x01, 0x06, 0xeb, 0xae, 0x07, 0x2e, 0xbb, 0xfa, 0xa3, 0x38, 0x81, 0x33, - 0x9b, 0xf8, 0x93, 0x60, 0x95, 0xf6, 0x5c, 0x6f, 0x79, 0x27, 0x92, 0xf8, 0x5e, 0x2f, 0x12, 0xf0, 0xec, 0xf5, 0xd2, - 0x38, 0x0c, 0x26, 0x22, 0x69, 0xd3, 0x59, 0x72, 0x3d, 0xa3, 0x8f, 0x06, 0xeb, 0x01, 0xba, 0x5d, 0xf0, 0xc3, 0x50, - 0xb3, 0xdb, 0xa9, 0xc6, 0xfc, 0x14, 0xf9, 0xcb, 0x9a, 0x93, 0x12, 0x5c, 0xd0, 0x38, 0xdd, 0x3d, 0x5c, 0xde, 0xc9, - 0x3d, 0xef, 0x1e, 0x2d, 0xef, 0xf2, 0xbf, 0x2e, 0xd8, 0x24, 0xf0, 0xb5, 0x56, 0xb1, 0x9b, 0x5c, 0x07, 0x78, 0xd0, - 0xc6, 0x7a, 0xc3, 0x36, 0x15, 0xc7, 0x02, 0x5c, 0x1b, 0x3e, 0x0a, 0x16, 0xcb, 0x38, 0xc9, 0xfc, 0x28, 0xcb, 0xf3, - 0xe1, 0x55, 0x9e, 0xf7, 0x2f, 0x82, 0xd6, 0xe5, 0x3f, 0x5a, 0x74, 0x4f, 0x93, 0xcc, 0x26, 0x37, 0xae, 0xcc, 0xd7, - 0x4c, 0xd5, 0x19, 0x81, 0x6b, 0x0c, 0xf5, 0x45, 0xd4, 0xc2, 0x74, 0x4b, 0xd6, 0x0b, 0x13, 0x90, 0x65, 0x71, 0xd2, - 0x41, 0x29, 0x17, 0xc1, 0x1b, 0x08, 0x0a, 0xbc, 0x66, 0x83, 0x0b, 0x45, 0xff, 0x04, 0x88, 0x15, 0x2c, 0x4c, 0x76, - 0x05, 0x4f, 0x36, 0xd1, 0x8c, 0xdf, 0xed, 0xa6, 0x19, 0x7f, 0xcd, 0xf6, 0xa1, 0x19, 0xbf, 0xfb, 0xea, 0x34, 0xe3, - 0x93, 0xba, 0x5d, 0xc1, 0xdb, 0x78, 0xa0, 0x4b, 0x09, 0x03, 0x5c, 0x4d, 0x09, 0x79, 0xec, 0x79, 0xfb, 0x87, 0xcd, - 0x00, 0x44, 0x6b, 0x14, 0x83, 0x8e, 0x6e, 0x6e, 0xe0, 0xc7, 0xbe, 0x8b, 0x06, 0x7f, 0x4f, 0xd4, 0xef, 0xe9, 0x74, - 0xf0, 0x2a, 0x56, 0x12, 0xe4, 0x17, 0x57, 0xbe, 0x28, 0x79, 0x57, 0xa0, 0x1c, 0xa1, 0x85, 0x89, 0xf1, 0x27, 0xc0, - 0x38, 0x9b, 0xb4, 0x8e, 0x27, 0x52, 0xfb, 0xac, 0x5f, 0x1e, 0x42, 0x4b, 0xaa, 0x7c, 0x0a, 0x13, 0x9c, 0x1a, 0x2b, - 0x71, 0xc6, 0x32, 0x6e, 0x33, 0xfb, 0xfd, 0xfd, 0xdb, 0x49, 0xeb, 0x6d, 0x6c, 0xe4, 0x41, 0xfa, 0xae, 0x6a, 0x00, - 0xc3, 0x65, 0x3f, 0x03, 0x75, 0x3a, 0x39, 0xd7, 0x20, 0x53, 0x03, 0x4c, 0x43, 0x36, 0x55, 0x3f, 0x2b, 0xcd, 0xb4, - 0xa7, 0x56, 0xe4, 0x81, 0xae, 0x6a, 0x97, 0x31, 0xb7, 0x3e, 0x58, 0x73, 0x0a, 0x10, 0x63, 0x77, 0xa1, 0xdd, 0xf0, - 0x84, 0xaa, 0x07, 0x93, 0x3c, 0x37, 0xfa, 0x02, 0x10, 0xca, 0x45, 0xcb, 0x76, 0x11, 0x71, 0xe9, 0xad, 0xd4, 0x69, - 0xe0, 0x12, 0x42, 0x12, 0xff, 0xbd, 0x05, 0x81, 0x3a, 0x17, 0x16, 0x72, 0x98, 0xe9, 0x1a, 0x81, 0x8f, 0x14, 0x2d, - 0x94, 0x09, 0x81, 0x04, 0x58, 0xc2, 0x5f, 0x64, 0x89, 0x84, 0xba, 0x0e, 0x27, 0x01, 0x07, 0x35, 0x02, 0xc0, 0xca, - 0x5f, 0xf0, 0xb5, 0x09, 0xed, 0xf0, 0x32, 0xf8, 0x91, 0xeb, 0x92, 0xf6, 0xc3, 0xed, 0x77, 0x7a, 0x72, 0x00, 0x15, - 0x4e, 0x2b, 0x8a, 0x03, 0x3b, 0x34, 0x14, 0x81, 0x94, 0x48, 0x6f, 0x4d, 0x3b, 0xbd, 0xd5, 0x9e, 0xad, 0x85, 0x87, - 0x8c, 0xcc, 0x5f, 0x5a, 0xf0, 0xc4, 0x47, 0xdc, 0xcb, 0x31, 0x9e, 0xe2, 0x8c, 0xa3, 0xbf, 0x4a, 0x01, 0x37, 0xe2, - 0x43, 0x15, 0xf1, 0x4f, 0x7f, 0xbc, 0x4a, 0xd2, 0x38, 0xe9, 0x2d, 0xe3, 0x20, 0xca, 0x58, 0x92, 0x23, 0xa8, 0x2e, - 0x11, 0x3e, 0x02, 0x3c, 0x57, 0xeb, 0x78, 0xe9, 0x8f, 0x83, 0xec, 0xbe, 0xe7, 0x70, 0x92, 0xc2, 0xe9, 0x73, 0xea, - 0xc0, 0x69, 0x2c, 0xdf, 0xe3, 0xd0, 0x7c, 0x8e, 0x84, 0x5f, 0x52, 0x27, 0x67, 0xd4, 0x6d, 0xde, 0x57, 0x72, 0xc9, - 0x47, 0x08, 0x90, 0x1f, 0x7e, 0x62, 0xcd, 0x00, 0xcb, 0xc3, 0x52, 0x3b, 0x13, 0x36, 0x33, 0x11, 0x6b, 0x03, 0x5f, - 0x5e, 0xfc, 0xb1, 0x3b, 0x86, 0xe6, 0x34, 0x27, 0x03, 0xc5, 0x63, 0xec, 0x33, 0xb2, 0x9e, 0x0f, 0x11, 0xb5, 0xcc, - 0x7d, 0x4a, 0x8e, 0xd8, 0x34, 0x4e, 0x18, 0xf9, 0x93, 0x75, 0xbb, 0xcb, 0xbb, 0xfd, 0x9b, 0xdf, 0x3e, 0xfd, 0xe6, - 0x76, 0xa2, 0x38, 0x6b, 0x89, 0xc6, 0x8c, 0x1d, 0xad, 0xd5, 0xef, 0x33, 0x20, 0x0d, 0x09, 0xf2, 0x63, 0x72, 0xdd, - 0xd5, 0xd3, 0xf5, 0x7e, 0xa3, 0xdb, 0xae, 0x65, 0xcc, 0xef, 0xbc, 0x84, 0x85, 0x7e, 0x16, 0xdc, 0x08, 0x9a, 0xb1, - 0x7d, 0xb4, 0xbc, 0x13, 0x6b, 0x8c, 0x17, 0xde, 0x03, 0x16, 0xa9, 0x32, 0x14, 0xb1, 0x48, 0xd5, 0x64, 0x5c, 0xa4, - 0x7e, 0x6d, 0x36, 0xc2, 0x93, 0x45, 0xe5, 0xa6, 0xef, 0x2c, 0xef, 0xd4, 0x2b, 0xba, 0xa8, 0x26, 0x6f, 0xea, 0xaa, - 0x0b, 0xb2, 0x45, 0x30, 0x99, 0x84, 0x2c, 0x2f, 0x2d, 0x74, 0x79, 0x2d, 0x15, 0xe0, 0x48, 0x38, 0xf8, 0xa3, 0x34, - 0x0e, 0x57, 0x19, 0x6b, 0x06, 0x17, 0x01, 0xc7, 0x73, 0x0a, 0xe0, 0xe0, 0xef, 0xf2, 0x58, 0x3b, 0x40, 0x6e, 0xc3, - 0x36, 0x71, 0xfa, 0xe0, 0x71, 0xd8, 0x6a, 0x97, 0x87, 0x0e, 0x59, 0x72, 0xd0, 0x66, 0xc3, 0x44, 0x4c, 0xb8, 0x96, - 0x08, 0x7b, 0x6b, 0xb6, 0xcb, 0xd3, 0xa4, 0xd7, 0x55, 0x99, 0x94, 0x97, 0x27, 0xf3, 0xe7, 0x9c, 0xb1, 0x17, 0xcd, - 0x67, 0xec, 0x85, 0x38, 0x63, 0xdb, 0x77, 0xe6, 0xe3, 0xa9, 0x0b, 0xff, 0xf5, 0x8b, 0x09, 0xf5, 0x1c, 0xad, 0xbd, - 0xbc, 0xd3, 0xdc, 0xe5, 0x9d, 0x66, 0x79, 0xcb, 0x3b, 0x0d, 0x9b, 0x46, 0x7d, 0x10, 0xd3, 0xf6, 0x0c, 0xd3, 0xd1, - 0x20, 0x11, 0xfe, 0x38, 0xa5, 0x2c, 0xf7, 0x10, 0xf2, 0xa0, 0x56, 0xa7, 0x9e, 0xe7, 0x6d, 0x3f, 0xea, 0x74, 0x96, - 0x04, 0xd2, 0x36, 0xec, 0xcc, 0x1f, 0x8d, 0xd8, 0xa4, 0x37, 0x8d, 0xc7, 0xab, 0xf4, 0x5f, 0x7c, 0xfc, 0x1c, 0x88, - 0x5b, 0x11, 0x41, 0xa5, 0x1d, 0x51, 0x15, 0x04, 0x25, 0x37, 0x4c, 0xb4, 0xb0, 0x96, 0xeb, 0xd4, 0x23, 0xf7, 0xc8, - 0x9e, 0x7d, 0xd8, 0xb0, 0xc9, 0x9b, 0x01, 0xfd, 0xa7, 0xad, 0xd2, 0x66, 0x14, 0xf3, 0x05, 0x60, 0xd9, 0x0a, 0x8e, - 0x87, 0x43, 0x83, 0xaf, 0xa6, 0xd3, 0x6d, 0x1e, 0xee, 0xa5, 0xe8, 0xe9, 0x4a, 0x5c, 0x2a, 0xfc, 0xde, 0xe2, 0x86, - 0x29, 0xdb, 0x5b, 0xdd, 0xb4, 0x47, 0x6a, 0xad, 0x6e, 0xb9, 0x10, 0x8a, 0xb2, 0x7b, 0x62, 0xf9, 0xc7, 0x2f, 0x0e, - 0xe1, 0x3f, 0xa2, 0xea, 0x7f, 0xcd, 0x9a, 0x08, 0xf5, 0xb7, 0x65, 0x4d, 0x70, 0x22, 0x95, 0x90, 0x10, 0xdf, 0xbf, - 0xfc, 0x74, 0xfa, 0xb0, 0x0a, 0x7b, 0x97, 0x26, 0x55, 0xaa, 0x6a, 0xe9, 0xef, 0xe3, 0x18, 0x42, 0x77, 0xd6, 0x8b, - 0x0b, 0xf0, 0x90, 0xb2, 0x7b, 0x36, 0x80, 0x4a, 0xe2, 0x1d, 0x41, 0x52, 0x7c, 0x1d, 0xeb, 0xd0, 0x53, 0xe2, 0xf5, - 0xa6, 0xa7, 0xc4, 0xab, 0xdd, 0x4f, 0x89, 0x1f, 0xf6, 0x7a, 0x4a, 0xbc, 0xfa, 0xea, 0x4f, 0x89, 0xd7, 0xf5, 0xa7, - 0xc4, 0x45, 0x2c, 0xf4, 0x67, 0xcd, 0xb7, 0x2b, 0xfe, 0xf3, 0x23, 0x09, 0xe5, 0xce, 0xe3, 0x41, 0xc7, 0x21, 0x97, - 0xc7, 0x17, 0x7f, 0xf8, 0x61, 0x81, 0x1b, 0xf1, 0x3d, 0xaa, 0x93, 0x15, 0x4f, 0x0b, 0x8e, 0xd9, 0xb1, 0x1f, 0x25, - 0x39, 0x8c, 0xa3, 0xd9, 0xcf, 0x20, 0x94, 0x05, 0x76, 0x60, 0xa2, 0x64, 0x04, 0xe9, 0xcf, 0xf1, 0x72, 0xb5, 0x7c, - 0x0b, 0x6d, 0x7d, 0x0c, 0xd2, 0x60, 0x14, 0x32, 0x69, 0x89, 0x4c, 0xea, 0x6f, 0x9c, 0x27, 0x0e, 0x1a, 0xa7, 0xe2, - 0xa7, 0x7f, 0x27, 0x7e, 0xa2, 0x4e, 0x2a, 0xff, 0x4d, 0x7a, 0x75, 0x7a, 0xf3, 0x43, 0x44, 0x08, 0x01, 0x95, 0x41, - 0x3f, 0xfc, 0x31, 0x72, 0x11, 0x1b, 0x0d, 0xb3, 0x14, 0xfa, 0x0e, 0x1b, 0xdb, 0x61, 0xb5, 0x47, 0xcd, 0xca, 0x30, - 0xa5, 0x0b, 0xae, 0x3a, 0x1b, 0x7e, 0x11, 0xaf, 0x52, 0x36, 0x89, 0x6f, 0x23, 0xdd, 0x8c, 0xa4, 0x91, 0x01, 0x48, - 0x38, 0x65, 0x1d, 0x0c, 0x1e, 0xf9, 0x01, 0x09, 0xe5, 0x38, 0x69, 0xe9, 0x10, 0xbb, 0x74, 0xb5, 0xb4, 0x48, 0xd4, - 0x6c, 0xe1, 0x14, 0x75, 0x19, 0xe5, 0xe8, 0x51, 0xab, 0x15, 0x0f, 0x1e, 0x56, 0x53, 0xa8, 0x6a, 0xc4, 0x36, 0xe7, - 0x0a, 0xa7, 0xad, 0x48, 0x30, 0x17, 0x85, 0x1f, 0x8c, 0x86, 0x85, 0xe3, 0x39, 0x64, 0xba, 0x5a, 0xe4, 0x82, 0x17, - 0x91, 0x7c, 0xc5, 0xd7, 0x83, 0x7b, 0x85, 0xa0, 0xcf, 0x97, 0x0a, 0x18, 0xdf, 0xdd, 0xb0, 0x24, 0xf4, 0xef, 0x5b, - 0x46, 0x1e, 0x47, 0x3f, 0x02, 0x00, 0x5e, 0xc5, 0xb7, 0x91, 0x5a, 0x00, 0x83, 0xb5, 0x34, 0xec, 0xa5, 0x46, 0xff, - 0x25, 0x60, 0xb8, 0xa2, 0x8c, 0x00, 0xc2, 0xe4, 0xce, 0xd8, 0xdf, 0x4d, 0xfa, 0xf7, 0x1f, 0x46, 0x6e, 0x9e, 0xc7, - 0xb2, 0xa3, 0x5f, 0x96, 0x7b, 0x74, 0xf3, 0xf4, 0xe9, 0xa3, 0xcd, 0xd3, 0x2e, 0x87, 0x67, 0x6f, 0xa8, 0x6d, 0x6c, - 0x3c, 0x05, 0x30, 0x8a, 0x8b, 0x78, 0x35, 0x9e, 0xa3, 0xa2, 0xeb, 0xd7, 0x9b, 0x6f, 0x06, 0x6d, 0x62, 0x94, 0x52, - 0x39, 0xf5, 0x4a, 0x52, 0x01, 0x05, 0xec, 0xff, 0x35, 0x38, 0xe0, 0xfc, 0x1f, 0x82, 0xa1, 0xbe, 0x6b, 0xf8, 0x2b, - 0x3e, 0x78, 0xd8, 0xe6, 0xed, 0x43, 0x30, 0x4d, 0xee, 0xda, 0x42, 0x08, 0xd7, 0x9a, 0x91, 0x4c, 0x5e, 0x05, 0x9a, - 0xea, 0x46, 0x6e, 0x93, 0x87, 0x3c, 0xd1, 0x0b, 0xb3, 0xe9, 0x99, 0xce, 0x0d, 0x0d, 0x4c, 0xc6, 0xb1, 0x55, 0x05, - 0xc9, 0x70, 0x95, 0x07, 0x86, 0xe8, 0xab, 0x9a, 0xb7, 0x08, 0x22, 0x13, 0xbd, 0xc0, 0xd7, 0x73, 0xfc, 0x3b, 0xf0, - 0x83, 0x0c, 0xc8, 0xad, 0x9a, 0x05, 0x89, 0xa6, 0x6a, 0x37, 0x07, 0xa1, 0x9e, 0xf4, 0x46, 0x48, 0x08, 0x29, 0xde, - 0xf0, 0x1b, 0x4d, 0xd3, 0x34, 0xf9, 0x8c, 0xd0, 0xe4, 0x3b, 0x02, 0xd3, 0xf1, 0x39, 0x00, 0xd2, 0x92, 0x7c, 0x79, - 0x47, 0x29, 0xf0, 0x32, 0x40, 0x99, 0xac, 0x48, 0xe0, 0xae, 0xfe, 0x3a, 0x8e, 0x48, 0x10, 0x0f, 0x7a, 0x70, 0xd3, - 0xe6, 0x27, 0xe0, 0x11, 0xb8, 0xa7, 0xe1, 0x83, 0x1d, 0x73, 0x39, 0x27, 0x58, 0x73, 0xe8, 0x73, 0xd8, 0x67, 0xcd, - 0x3e, 0xe1, 0x22, 0x05, 0x0b, 0x82, 0xd4, 0xa1, 0xe2, 0xe2, 0xd9, 0x64, 0x0d, 0xb8, 0x11, 0xdf, 0x45, 0x77, 0xd9, - 0x82, 0x45, 0x2b, 0x1d, 0x63, 0x42, 0xa1, 0x8f, 0x3e, 0x28, 0xf3, 0x8a, 0x88, 0x2d, 0xc0, 0x36, 0xcd, 0x35, 0xe7, - 0x74, 0x17, 0xa6, 0x1c, 0xa5, 0xfa, 0xe6, 0x98, 0x0b, 0x36, 0x53, 0x8e, 0xdb, 0xaa, 0x37, 0x04, 0x5f, 0xd2, 0xb8, - 0x6a, 0xc8, 0x45, 0x9a, 0xd0, 0xd0, 0x06, 0x79, 0xc7, 0xe0, 0xec, 0x22, 0x01, 0xf6, 0x96, 0x5f, 0x5d, 0x34, 0x29, - 0x91, 0xf1, 0x2b, 0x8c, 0xa2, 0xc4, 0xa8, 0x37, 0xc3, 0xc7, 0x09, 0x8e, 0x89, 0x36, 0xb6, 0x33, 0xae, 0xb5, 0xb3, - 0x61, 0xd2, 0x9f, 0xd8, 0x3d, 0x5d, 0x24, 0x04, 0xaa, 0x4f, 0xec, 0x1e, 0x74, 0xff, 0x5e, 0x03, 0x37, 0x45, 0xdf, - 0x82, 0xae, 0x4d, 0x70, 0xf5, 0x3f, 0x06, 0x67, 0x55, 0x5b, 0x0e, 0x90, 0x93, 0x6f, 0xc1, 0xe2, 0x08, 0x62, 0x88, - 0xea, 0x2c, 0x0e, 0x31, 0x57, 0xf1, 0x6f, 0x35, 0xc2, 0xd8, 0x6a, 0x38, 0x1a, 0xc6, 0x33, 0xd7, 0x71, 0x0e, 0x6a, - 0xe5, 0x81, 0x91, 0xdd, 0x54, 0xda, 0x30, 0xb3, 0x81, 0xeb, 0x58, 0xc1, 0x33, 0xdb, 0xeb, 0xd7, 0xee, 0x68, 0xc5, - 0x97, 0xe4, 0x10, 0xd9, 0x5f, 0xa7, 0x4f, 0xd6, 0xad, 0xda, 0x81, 0x34, 0xaa, 0x2a, 0xf3, 0x38, 0xb6, 0x9c, 0xf3, - 0xbf, 0x86, 0xf5, 0xab, 0x9f, 0x3c, 0x59, 0x52, 0x5c, 0x93, 0x21, 0x78, 0x43, 0x6e, 0xc1, 0x31, 0xfa, 0x8b, 0xf6, - 0x5c, 0x6b, 0xd1, 0xf1, 0x31, 0x8c, 0xa1, 0x0c, 0x97, 0x2d, 0x6c, 0xca, 0xd4, 0x06, 0x2a, 0x3d, 0xa6, 0x55, 0x0c, - 0xc7, 0xfd, 0xae, 0xb2, 0x42, 0xa2, 0xb7, 0x95, 0x5a, 0xc0, 0xf6, 0x37, 0x5c, 0x9f, 0xf6, 0x08, 0xfc, 0x12, 0x40, - 0x09, 0xf0, 0x9d, 0xbe, 0xb3, 0xc1, 0xd5, 0xb2, 0xdc, 0x5c, 0xf9, 0x92, 0xdc, 0xbf, 0x31, 0xbc, 0x74, 0x50, 0x86, - 0x26, 0xdb, 0x6b, 0xbe, 0xee, 0x1e, 0xd8, 0x24, 0x8b, 0x26, 0xe5, 0x06, 0x2b, 0xf7, 0xd7, 0xfe, 0xcd, 0x95, 0x30, - 0x0a, 0x04, 0x15, 0x88, 0x1b, 0x30, 0x4a, 0x1e, 0x47, 0xb8, 0xf9, 0xe9, 0xb8, 0x05, 0x7b, 0x51, 0x31, 0x58, 0x81, - 0x3c, 0x82, 0xc9, 0x6a, 0x0a, 0x53, 0x1c, 0x3c, 0x57, 0xa3, 0x59, 0x70, 0x4b, 0x10, 0xa2, 0x1b, 0x77, 0x62, 0x26, - 0x74, 0x0a, 0x8b, 0x3a, 0x01, 0xf7, 0x45, 0xb9, 0x2f, 0xd7, 0x3a, 0xd8, 0xcd, 0xb5, 0xce, 0x76, 0x71, 0xad, 0xc9, - 0x9c, 0xea, 0x36, 0xf1, 0x97, 0x8a, 0x45, 0x9e, 0x20, 0xce, 0x55, 0xc3, 0xbc, 0x12, 0xab, 0x1b, 0xad, 0xaf, 0x44, - 0xad, 0x5a, 0x6b, 0xa4, 0x25, 0x88, 0xec, 0x6f, 0xe5, 0x81, 0x22, 0x04, 0xea, 0x2a, 0x6f, 0xfc, 0xa2, 0xe0, 0x8d, - 0xd3, 0xab, 0xa6, 0x30, 0xa4, 0x11, 0xd4, 0xbf, 0x62, 0xa4, 0x26, 0x5f, 0x07, 0x85, 0xb1, 0x5a, 0x31, 0x52, 0xc5, - 0xfc, 0xaa, 0x78, 0x68, 0x28, 0x46, 0x7d, 0xe2, 0x95, 0x51, 0xb6, 0xed, 0x2b, 0x17, 0x2d, 0xac, 0xaf, 0x8a, 0x74, - 0xe0, 0xba, 0xe3, 0x90, 0x65, 0xb2, 0xba, 0x6d, 0xca, 0xe6, 0x37, 0x6a, 0xb6, 0xb2, 0x49, 0xa4, 0x9d, 0x0c, 0x01, - 0x58, 0xb0, 0xe9, 0x2b, 0x72, 0x6d, 0xa9, 0x03, 0x81, 0x83, 0x6c, 0x30, 0xeb, 0xdb, 0xcd, 0x9d, 0xa7, 0x78, 0x09, - 0x85, 0x14, 0x5e, 0xe5, 0x41, 0x20, 0x7c, 0xaf, 0xd6, 0x0d, 0xb7, 0x3c, 0x5e, 0xf2, 0xfc, 0x7e, 0x07, 0xf6, 0xa2, - 0xe6, 0xa8, 0x82, 0x7c, 0x3c, 0x99, 0x16, 0xa9, 0xe7, 0x62, 0xd1, 0x7a, 0xa3, 0xc4, 0xc4, 0x59, 0x73, 0xcb, 0x98, - 0x32, 0x8f, 0x9e, 0x97, 0xe8, 0x89, 0x7e, 0xf9, 0xd6, 0x49, 0x56, 0x11, 0xfa, 0xb6, 0xb7, 0xb2, 0xc4, 0x1f, 0x7f, - 0x52, 0x86, 0x2c, 0xf8, 0x9c, 0xc0, 0x03, 0x2e, 0x4b, 0x0a, 0xfa, 0x3e, 0xba, 0x82, 0x64, 0x3d, 0xdb, 0x4b, 0x15, - 0xee, 0x4b, 0xef, 0xb1, 0xd3, 0xf6, 0x5f, 0x4c, 0x0f, 0x2b, 0x4c, 0x51, 0xaf, 0x53, 0x66, 0x99, 0x6f, 0x18, 0x47, - 0x36, 0x5f, 0x2d, 0x46, 0x6b, 0x95, 0xb7, 0xaa, 0xb0, 0x5c, 0xeb, 0x6c, 0x56, 0xb5, 0xdb, 0xe9, 0x74, 0x5a, 0x66, - 0x34, 0x3a, 0xda, 0x21, 0x32, 0x0b, 0x1f, 0x3b, 0x8e, 0x53, 0x1d, 0xfb, 0x76, 0xb0, 0x5b, 0xc8, 0xb7, 0xed, 0x36, - 0x8e, 0x18, 0x61, 0xbb, 0x0b, 0x7e, 0x75, 0x70, 0xe4, 0x76, 0x71, 0xb2, 0x4b, 0x6a, 0x11, 0x7d, 0x52, 0x86, 0x08, - 0x32, 0xb6, 0x48, 0x7b, 0x63, 0x86, 0x32, 0x18, 0x5b, 0x39, 0xd0, 0xa8, 0x38, 0x60, 0xcd, 0x40, 0x55, 0xc4, 0x15, - 0xbb, 0xc2, 0xd1, 0x90, 0x1f, 0x5e, 0x63, 0xde, 0x8b, 0x4e, 0xf0, 0xa0, 0xac, 0xeb, 0x3c, 0x6d, 0x9c, 0x56, 0xc7, - 0xf9, 0x4b, 0xa9, 0x9c, 0x06, 0x17, 0xe0, 0x5a, 0x08, 0xb4, 0x89, 0x3f, 0x8b, 0x7f, 0x4b, 0xfe, 0xff, 0x8b, 0xe5, - 0x5d, 0x59, 0x7f, 0xa4, 0x0b, 0x1c, 0xed, 0xe2, 0xb4, 0xd0, 0xa8, 0x9b, 0xf6, 0x80, 0xd4, 0x32, 0x98, 0xaa, 0x02, - 0x74, 0x10, 0xd2, 0x97, 0x02, 0x80, 0x34, 0xb0, 0xdf, 0x91, 0x62, 0x86, 0x25, 0x2e, 0x58, 0x88, 0x45, 0xf8, 0x3a, - 0x98, 0x83, 0xf9, 0xbc, 0x8b, 0xf2, 0x83, 0xd2, 0x9e, 0x00, 0x69, 0x7c, 0x6d, 0x6e, 0x7b, 0xb1, 0xfb, 0xab, 0x72, - 0x2d, 0xd1, 0x30, 0x80, 0xcc, 0x85, 0x43, 0x88, 0x8a, 0x04, 0x5a, 0x65, 0x73, 0xd3, 0x28, 0x65, 0xae, 0x2a, 0x67, - 0x13, 0x03, 0xc3, 0xe6, 0x9a, 0x8b, 0x50, 0xdb, 0x42, 0x5a, 0x00, 0x93, 0xe5, 0xdb, 0x0f, 0xbf, 0x2d, 0x58, 0x62, - 0x75, 0x3f, 0xba, 0xb8, 0xe4, 0xb8, 0x7f, 0x2d, 0xbc, 0x3b, 0x53, 0x3a, 0xff, 0xc8, 0x5f, 0xfc, 0xa1, 0x91, 0xa1, - 0x77, 0x51, 0xe2, 0xd0, 0x71, 0x6d, 0x71, 0xcf, 0xd8, 0xab, 0xf4, 0x22, 0x88, 0xf6, 0x2f, 0xeb, 0xdf, 0xed, 0x5d, - 0x16, 0x2e, 0x8c, 0xbd, 0x0b, 0xc3, 0x8d, 0x43, 0x9a, 0x0b, 0xd9, 0xe0, 0x07, 0x85, 0xa1, 0xa8, 0x5a, 0x1d, 0xeb, - 0x58, 0x8b, 0xa8, 0xfc, 0x8b, 0xd5, 0x60, 0x78, 0x72, 0x76, 0xb7, 0x08, 0xb5, 0x1b, 0x96, 0x40, 0x68, 0x9f, 0x81, - 0xee, 0xda, 0x8e, 0xae, 0xa1, 0x0d, 0x6d, 0x10, 0xcd, 0x06, 0xfa, 0x2f, 0x17, 0x6f, 0xac, 0xae, 0x7e, 0x06, 0x22, - 0xda, 0x9b, 0x19, 0x5e, 0x7b, 0xe7, 0xfe, 0x3d, 0x4b, 0xae, 0x3d, 0x5d, 0xc3, 0x08, 0x3e, 0x74, 0xe1, 0x61, 0x9a, - 0xe6, 0xe9, 0x7b, 0x04, 0x8a, 0xd0, 0x44, 0xac, 0x37, 0x1d, 0x50, 0x8e, 0xeb, 0x75, 0x35, 0xd7, 0x3b, 0xb4, 0x8f, - 0xba, 0xfa, 0xe9, 0x37, 0x9a, 0x76, 0x32, 0x61, 0xd3, 0xf4, 0x14, 0x9f, 0x68, 0x27, 0x78, 0x47, 0xd0, 0x6f, 0x4d, - 0xb3, 0xc7, 0x61, 0x6a, 0xb9, 0xda, 0x9a, 0x7f, 0x6a, 0xda, 0x34, 0x08, 0xc3, 0x9e, 0xf6, 0x78, 0xea, 0x4d, 0x0f, - 0xa7, 0x2f, 0xfa, 0x3c, 0x39, 0xff, 0xa6, 0x54, 0xdc, 0xa4, 0x7f, 0x3d, 0xa5, 0x5a, 0x9a, 0x25, 0xf1, 0x27, 0xc6, - 0xd5, 0x4e, 0x34, 0xf9, 0x78, 0xac, 0x56, 0xf5, 0xea, 0x3d, 0xb9, 0xdd, 0xd1, 0x78, 0xea, 0x15, 0xc5, 0x71, 0x8c, - 0x07, 0x72, 0x90, 0x27, 0x07, 0x62, 0xe8, 0x27, 0x2a, 0x98, 0x5c, 0xab, 0x09, 0x50, 0xae, 0xce, 0xe7, 0x38, 0x13, - 0xf3, 0x3b, 0x01, 0x3f, 0x8c, 0xd2, 0x5c, 0x17, 0x46, 0xa0, 0x6b, 0x93, 0x81, 0xfe, 0xa3, 0xeb, 0x75, 0x4d, 0xd7, - 0x3d, 0xb2, 0x8f, 0xba, 0x63, 0xc7, 0x3c, 0xb4, 0x0f, 0xad, 0xb6, 0x7d, 0x64, 0x76, 0xad, 0xae, 0xd9, 0xfd, 0x5b, - 0x77, 0x6c, 0x1d, 0xda, 0x87, 0xa6, 0x63, 0x75, 0x21, 0xd1, 0xea, 0x5a, 0xdd, 0x1b, 0xeb, 0xb0, 0x3b, 0x76, 0x30, - 0xd5, 0xb3, 0x3b, 0x1d, 0xcb, 0x75, 0xec, 0x4e, 0xc7, 0xec, 0xd8, 0x47, 0x47, 0x96, 0xdb, 0xb6, 0x8f, 0x8e, 0xce, - 0x3b, 0x5d, 0xbb, 0x0d, 0x79, 0xed, 0xf6, 0xb8, 0x6d, 0xbb, 0xae, 0x05, 0x7f, 0x99, 0x5d, 0xdb, 0xa3, 0x1f, 0xae, - 0x6b, 0xb7, 0x5d, 0xd3, 0x09, 0x3b, 0x9e, 0x7d, 0xf4, 0xc2, 0xc4, 0xbf, 0xb1, 0x98, 0x89, 0x7f, 0x41, 0x33, 0xe6, - 0x0b, 0xdb, 0x3b, 0xa2, 0x5f, 0xd8, 0xe0, 0xcd, 0x61, 0xf7, 0x57, 0xfd, 0x60, 0xe3, 0x1c, 0x5c, 0x9a, 0x43, 0xb7, - 0x63, 0xb7, 0xdb, 0xe6, 0xa1, 0x6b, 0x77, 0xdb, 0x73, 0xeb, 0xd0, 0xb3, 0x8f, 0x8e, 0xc7, 0x96, 0x6b, 0x1f, 0x1f, - 0x9b, 0x8e, 0xd5, 0xb6, 0x3d, 0xd3, 0xb5, 0x0f, 0xdb, 0xf8, 0xa3, 0x6d, 0x7b, 0x37, 0xc7, 0x2f, 0xec, 0xa3, 0xce, - 0xfc, 0xc8, 0x3e, 0xfc, 0x78, 0xd8, 0xb5, 0xbd, 0xf6, 0xbc, 0x7d, 0x64, 0x7b, 0xc7, 0x37, 0x47, 0xf6, 0xe1, 0xdc, - 0xf2, 0x8e, 0xb6, 0xd6, 0x74, 0x3d, 0x1b, 0x60, 0x84, 0xd9, 0x90, 0x61, 0xf2, 0x0c, 0xf8, 0x33, 0xc7, 0xba, 0xff, - 0xc5, 0x66, 0xd2, 0x7a, 0xd5, 0x17, 0x76, 0xf7, 0x78, 0x4c, 0xc5, 0x21, 0xc1, 0x12, 0x25, 0xa0, 0xca, 0x8d, 0x45, - 0xdd, 0x62, 0x73, 0x96, 0x68, 0x48, 0xfc, 0xe1, 0x9d, 0xdd, 0x58, 0xd0, 0x31, 0xf5, 0xfb, 0x3f, 0x6d, 0x47, 0x2e, - 0x39, 0x44, 0xae, 0xfc, 0x86, 0xff, 0x43, 0x41, 0x5f, 0x86, 0xe6, 0xf9, 0x26, 0x41, 0xc5, 0xfb, 0xdd, 0x82, 0x8a, - 0x37, 0xab, 0x7d, 0x04, 0x15, 0xef, 0xbf, 0xba, 0xa0, 0xe2, 0xbc, 0xaa, 0x27, 0xff, 0xbe, 0xea, 0x9b, 0xfe, 0xd7, - 0x75, 0xf5, 0x19, 0x12, 0xf8, 0xad, 0xcb, 0x8b, 0xd5, 0x15, 0x78, 0x57, 0x7a, 0x1f, 0x0f, 0xde, 0xac, 0x4a, 0x4a, - 0x60, 0x31, 0xe0, 0xd8, 0xf7, 0x31, 0xe1, 0xd8, 0xdf, 0x57, 0x03, 0xd0, 0x3c, 0xe1, 0x74, 0x49, 0x30, 0xb1, 0xe6, - 0x7e, 0x38, 0x95, 0x34, 0x0d, 0xa4, 0xf4, 0x31, 0x19, 0xac, 0x12, 0xe0, 0xba, 0x06, 0x71, 0xd8, 0x6a, 0x11, 0xa5, - 0xbd, 0x23, 0x07, 0x2e, 0x52, 0x6f, 0x9a, 0xe4, 0x95, 0xca, 0xb6, 0xf0, 0x47, 0x75, 0xcd, 0xad, 0x26, 0x36, 0xe6, - 0xa3, 0x52, 0x60, 0x73, 0xeb, 0x6e, 0xbd, 0x5d, 0x0d, 0xb4, 0x6d, 0x84, 0xd2, 0x24, 0x90, 0x73, 0x4d, 0xf9, 0x65, - 0xd5, 0xbc, 0x8a, 0x32, 0xe6, 0xe6, 0x91, 0xc2, 0x48, 0xaa, 0xf5, 0xdd, 0xb2, 0x6a, 0xdf, 0xae, 0x69, 0x36, 0x74, - 0x5f, 0xaa, 0xbe, 0x45, 0xaf, 0x50, 0x36, 0x5c, 0x05, 0x55, 0x25, 0xb2, 0x5a, 0x23, 0x40, 0x0a, 0xea, 0xbe, 0x50, - 0x3e, 0x2c, 0x48, 0x4b, 0x47, 0x43, 0x7a, 0xc7, 0x51, 0xf2, 0x4a, 0x6d, 0xaa, 0x0a, 0x8b, 0xcf, 0xd6, 0x48, 0x71, - 0x07, 0xbf, 0x03, 0xe9, 0xc8, 0x29, 0x9e, 0x51, 0xac, 0xc2, 0x79, 0xad, 0xb4, 0x4b, 0x8f, 0x99, 0x7c, 0xee, 0xae, - 0xeb, 0xc4, 0xe3, 0x46, 0x55, 0x65, 0x97, 0x2d, 0x04, 0x15, 0x84, 0xdd, 0x93, 0x62, 0x70, 0x4e, 0xca, 0xdb, 0xa8, - 0xfb, 0xbc, 0xad, 0x31, 0x51, 0xee, 0x31, 0x6c, 0x62, 0x93, 0x7f, 0xa8, 0x7e, 0x01, 0xd6, 0x53, 0x88, 0x82, 0xdd, - 0x43, 0x32, 0x4d, 0xa1, 0x51, 0x3d, 0xd4, 0x62, 0xee, 0x6f, 0x51, 0xb0, 0x51, 0x1b, 0xe6, 0x8d, 0xa0, 0x36, 0xf4, - 0x36, 0x9d, 0x1c, 0x69, 0x3c, 0xb2, 0x2e, 0x89, 0xa8, 0xdd, 0xce, 0xb1, 0xe9, 0x1e, 0x99, 0xf6, 0x71, 0xc7, 0xc8, - 0xc5, 0x81, 0x53, 0x9b, 0x2c, 0x01, 0x04, 0x94, 0xa2, 0xe5, 0x30, 0x83, 0x28, 0xc8, 0x02, 0x3f, 0xcc, 0x81, 0x3e, - 0x2e, 0xbf, 0x2a, 0xfe, 0xb9, 0x4a, 0x33, 0x98, 0xa3, 0x20, 0x7a, 0x51, 0x21, 0xdc, 0x1a, 0xb1, 0xec, 0x96, 0xb1, - 0x68, 0x83, 0xb0, 0xbc, 0xaa, 0x5f, 0xfe, 0xe7, 0x69, 0xdb, 0xe6, 0xa4, 0xc9, 0x32, 0xca, 0x22, 0xbe, 0x3f, 0x84, - 0x32, 0x74, 0x3e, 0x34, 0x7f, 0xda, 0x84, 0x70, 0xff, 0xb9, 0x1b, 0xe1, 0x66, 0x6c, 0x1f, 0x84, 0xfb, 0xcf, 0xaf, - 0x8e, 0x70, 0x7f, 0x52, 0x11, 0x6e, 0xc9, 0x16, 0xa8, 0xe0, 0x3a, 0x7f, 0xc0, 0xef, 0x16, 0x38, 0x75, 0x7e, 0xae, - 0x1f, 0x10, 0x01, 0xaf, 0x2b, 0xc1, 0x76, 0x3f, 0x96, 0xa2, 0x07, 0x21, 0x53, 0x04, 0x9d, 0xd0, 0x52, 0xa4, 0x12, - 0x08, 0x44, 0x2b, 0x43, 0xaa, 0x43, 0x9b, 0x6f, 0xa3, 0x2c, 0xb4, 0xdf, 0xf3, 0x87, 0x1f, 0x08, 0x79, 0xde, 0xc4, - 0xc9, 0xc2, 0x47, 0x07, 0x7c, 0x3a, 0x46, 0x1d, 0x84, 0x0f, 0x07, 0xec, 0xcf, 0xc6, 0x71, 0x34, 0x91, 0x92, 0x0a, - 0x36, 0xb8, 0x24, 0x8a, 0x5b, 0xbf, 0x67, 0x7e, 0xa2, 0x9b, 0x94, 0x0d, 0x8b, 0xfb, 0xac, 0xed, 0x3c, 0xf3, 0x0e, - 0x9f, 0x1d, 0x39, 0xf0, 0xbf, 0xcb, 0xda, 0xb9, 0xc9, 0x0b, 0x2e, 0xe2, 0x08, 0x02, 0x9f, 0x88, 0x92, 0x9b, 0x8a, - 0xdd, 0x32, 0xf6, 0xa9, 0x28, 0x75, 0xdc, 0x5c, 0x68, 0xe2, 0xdf, 0x17, 0x65, 0x1a, 0x4b, 0xcc, 0xe3, 0x95, 0x32, - 0xac, 0x86, 0xd1, 0x04, 0xd1, 0x0a, 0x78, 0x6f, 0x4a, 0x09, 0x35, 0x9b, 0x4f, 0xb7, 0x98, 0x17, 0x6b, 0xe7, 0x57, - 0x45, 0x74, 0x25, 0x11, 0xe5, 0x65, 0x27, 0x04, 0xb9, 0xd8, 0xc2, 0x41, 0xdf, 0xec, 0x18, 0x5f, 0x48, 0x83, 0xd8, - 0x86, 0x62, 0x81, 0x7c, 0x5a, 0xa0, 0x2c, 0x59, 0x45, 0xe3, 0x16, 0xfe, 0xf4, 0x47, 0x69, 0x2b, 0x38, 0x00, 0xef, - 0xac, 0xd8, 0xb1, 0x81, 0xab, 0xe6, 0x9f, 0x3a, 0x45, 0x28, 0x8a, 0x54, 0xac, 0x8a, 0xff, 0x2c, 0x33, 0x13, 0x0a, - 0x60, 0x8b, 0x4b, 0x6b, 0x0d, 0xfc, 0x67, 0xb2, 0xe2, 0xb3, 0xcc, 0x84, 0x20, 0xb2, 0xb0, 0xdc, 0x4f, 0x9f, 0x52, - 0x29, 0x08, 0xeb, 0x48, 0xd3, 0x3a, 0x1b, 0x17, 0xee, 0xdd, 0x34, 0x7f, 0x16, 0x93, 0x87, 0xb7, 0xde, 0xd8, 0x8c, - 0x9f, 0x3f, 0x3f, 0x1d, 0xb8, 0x06, 0x0f, 0x4a, 0x5a, 0x8a, 0xa0, 0x75, 0xbe, 0x9f, 0xf2, 0x81, 0xd1, 0x68, 0x16, - 0xb7, 0x84, 0x37, 0x93, 0x23, 0x54, 0x94, 0x39, 0xf6, 0x82, 0x88, 0x16, 0x24, 0x64, 0xf4, 0x85, 0x12, 0x80, 0x28, - 0x23, 0x5f, 0x5d, 0x6d, 0xdb, 0xb1, 0x1d, 0x5d, 0x56, 0x9c, 0x06, 0xb3, 0xc1, 0x3a, 0xce, 0x7c, 0x88, 0x0d, 0x14, - 0xc6, 0x33, 0xb0, 0xad, 0xc9, 0x82, 0x2c, 0x84, 0x40, 0x33, 0x60, 0x64, 0xb3, 0xa0, 0x77, 0x79, 0xce, 0x35, 0x9e, - 0xfd, 0xe4, 0x13, 0x06, 0x1b, 0x14, 0x66, 0x75, 0xe8, 0x71, 0xe8, 0x47, 0xb8, 0x0c, 0x5b, 0x7a, 0x0b, 0x42, 0x5d, - 0xb2, 0x24, 0xb5, 0x54, 0x0b, 0x82, 0x9e, 0x06, 0x75, 0x20, 0x0c, 0x3d, 0x36, 0x30, 0x4d, 0xfc, 0x05, 0xf8, 0x64, - 0x5f, 0xe7, 0x26, 0xc7, 0xb4, 0x3a, 0x47, 0xb5, 0x9a, 0xfb, 0xe2, 0xc8, 0xd4, 0x3c, 0xd7, 0xd4, 0x1c, 0x40, 0xb7, - 0x7a, 0x6e, 0xae, 0xf3, 0xab, 0xfe, 0x2e, 0x21, 0x28, 0xe1, 0x97, 0xc7, 0x34, 0x0f, 0x12, 0x7f, 0x72, 0xf6, 0x72, - 0x46, 0x0e, 0x24, 0x5b, 0x8a, 0xb7, 0xf4, 0x80, 0x04, 0x21, 0x17, 0xec, 0x2e, 0x33, 0x30, 0x10, 0x0b, 0x2f, 0x12, - 0x18, 0x6b, 0x34, 0xfe, 0x0b, 0x22, 0x2d, 0xf8, 0xfc, 0xb9, 0x15, 0x80, 0x81, 0xc3, 0x40, 0x81, 0x0f, 0x7c, 0x1b, - 0x25, 0x80, 0x05, 0x85, 0xe8, 0x0e, 0x81, 0x05, 0xd6, 0x47, 0xf0, 0x6f, 0x91, 0x2c, 0x7e, 0x70, 0xd1, 0xa9, 0x1d, - 0xfa, 0xd1, 0x0c, 0x50, 0x9a, 0x1f, 0xcd, 0x6a, 0x2a, 0x1a, 0x64, 0xbf, 0x58, 0x49, 0x2d, 0x9a, 0x2a, 0xd4, 0x27, - 0xd2, 0xef, 0xef, 0x2f, 0x28, 0xd0, 0x14, 0x04, 0x35, 0xf7, 0x27, 0x68, 0x6c, 0x57, 0x48, 0x77, 0x9e, 0x0f, 0xbe, - 0x3d, 0x59, 0xb0, 0xcc, 0x27, 0xd6, 0x30, 0x3c, 0x7e, 0x81, 0x1c, 0xd0, 0xc6, 0x22, 0x48, 0x2c, 0x05, 0x93, 0x9f, - 0xb0, 0x9b, 0x60, 0xcc, 0xdf, 0xa5, 0xa6, 0xc6, 0xef, 0x29, 0x0b, 0xb5, 0xc0, 0x06, 0xae, 0x49, 0x4a, 0xc8, 0x63, - 0x1f, 0xdd, 0x4c, 0x0e, 0xa2, 0x58, 0x3f, 0xfd, 0x56, 0xda, 0x6b, 0x6d, 0x5a, 0x04, 0x88, 0xf6, 0x78, 0x99, 0xb0, - 0xf0, 0x5f, 0x83, 0x6f, 0xe1, 0xe2, 0xfe, 0xf6, 0x4a, 0x37, 0xfa, 0x99, 0x3d, 0x4f, 0xd8, 0x74, 0xf0, 0x6d, 0x43, - 0xd4, 0x43, 0x7c, 0xde, 0xd3, 0x58, 0xf4, 0xb6, 0x57, 0x38, 0x07, 0x6a, 0xef, 0xf5, 0xa8, 0x3f, 0xe5, 0xaf, 0x75, - 0x78, 0x01, 0xae, 0x4b, 0x6f, 0x6c, 0xb7, 0x8f, 0xef, 0xe7, 0x51, 0xe8, 0x8f, 0x3f, 0xf5, 0x29, 0xa7, 0xf4, 0x61, - 0xc1, 0x6d, 0x3d, 0xf6, 0x97, 0x3d, 0xbc, 0x5e, 0xd5, 0x44, 0x30, 0xd7, 0xa4, 0x54, 0x49, 0xd9, 0x35, 0xee, 0x65, - 0xdc, 0xca, 0x6b, 0xec, 0x19, 0xbb, 0xba, 0x9d, 0x07, 0x19, 0x13, 0x5d, 0xe1, 0x47, 0x9e, 0x8b, 0x87, 0x3a, 0x3d, - 0x51, 0xf1, 0x61, 0x6d, 0xb7, 0x35, 0xb7, 0xfb, 0xb7, 0xce, 0x8d, 0xeb, 0xcc, 0x3d, 0xd7, 0xee, 0x7e, 0x74, 0xbb, - 0xf3, 0xb6, 0x7d, 0x1c, 0x5a, 0x6d, 0xfb, 0x18, 0xfe, 0x7c, 0x3c, 0xb6, 0xbb, 0x73, 0xcb, 0xb3, 0x0f, 0x3f, 0xba, - 0x5e, 0x68, 0x75, 0xed, 0x63, 0xf8, 0x73, 0x4e, 0xb5, 0xe0, 0x01, 0x44, 0xef, 0x9d, 0x6f, 0x4b, 0x58, 0x40, 0xf9, - 0x2d, 0xe5, 0x34, 0x66, 0xe9, 0x7a, 0x6b, 0x90, 0xf5, 0x00, 0xca, 0xd0, 0x4d, 0xe1, 0x04, 0x32, 0xea, 0xb7, 0x20, - 0x0c, 0x3b, 0x06, 0x10, 0x10, 0x2a, 0x2f, 0xc2, 0x2e, 0x55, 0xb8, 0xd2, 0x6f, 0x3c, 0x46, 0xbc, 0x4e, 0xb3, 0xc3, - 0x75, 0x11, 0x99, 0x8a, 0x84, 0x43, 0xbf, 0x2c, 0xd1, 0x89, 0x91, 0x70, 0x11, 0xaf, 0x60, 0xa5, 0x22, 0x3a, 0x62, - 0xbe, 0x7b, 0xe0, 0x68, 0x99, 0xcb, 0x64, 0x74, 0x9e, 0xaf, 0xda, 0x36, 0x17, 0x18, 0xc9, 0xd6, 0xff, 0x68, 0x3b, - 0x18, 0x94, 0x96, 0xda, 0x11, 0xde, 0x5c, 0x27, 0x41, 0x22, 0x87, 0xa7, 0xa0, 0x68, 0xb7, 0xd9, 0x53, 0xbd, 0x01, - 0x61, 0x4c, 0xde, 0x02, 0x95, 0x7c, 0xe3, 0x87, 0x8a, 0x72, 0x8b, 0x52, 0xf3, 0x91, 0xc4, 0xfc, 0x4f, 0x9f, 0x16, - 0x83, 0xb3, 0x2a, 0xe3, 0x3e, 0x71, 0x3b, 0x70, 0xed, 0x76, 0x58, 0x7b, 0xab, 0x9e, 0xd5, 0x6e, 0x77, 0xc0, 0x85, - 0xbb, 0x50, 0xa1, 0x4b, 0x21, 0xa4, 0xb8, 0x1b, 0x95, 0xbd, 0x6a, 0x32, 0x5c, 0x70, 0xa4, 0x5c, 0x79, 0xea, 0xe8, - 0x46, 0x3f, 0x12, 0x22, 0xc9, 0x68, 0x8b, 0x0b, 0x64, 0xfe, 0x16, 0xd3, 0x01, 0x34, 0x5b, 0xe6, 0xb1, 0xc3, 0x68, - 0xf4, 0x7f, 0x3d, 0x09, 0x34, 0xe0, 0x02, 0x19, 0x6a, 0xe5, 0xb4, 0x96, 0x0c, 0x7a, 0xe4, 0xbd, 0x4a, 0x17, 0x2a, - 0x4b, 0xcf, 0x74, 0x48, 0x82, 0xf8, 0x56, 0x18, 0xd2, 0x4e, 0x2a, 0x90, 0xc9, 0xdb, 0xa2, 0x48, 0x30, 0x03, 0xf0, - 0x01, 0xde, 0x12, 0xc6, 0x64, 0xc6, 0xd3, 0xa7, 0x1b, 0x2f, 0x21, 0x12, 0xd8, 0xab, 0x91, 0x3d, 0x75, 0x15, 0xbf, - 0xe9, 0x2a, 0x8a, 0x91, 0xed, 0x22, 0xd6, 0x10, 0x7a, 0x6f, 0xb4, 0xf7, 0xf0, 0xe7, 0x88, 0xf9, 0x99, 0xcd, 0x25, - 0x4d, 0x2d, 0xe5, 0x72, 0x37, 0x5d, 0xd6, 0x06, 0x8d, 0x37, 0xee, 0xeb, 0x8c, 0xfb, 0x12, 0x7c, 0xb2, 0xfe, 0xb8, - 0xe2, 0x96, 0xde, 0xd0, 0xc6, 0x67, 0xa7, 0x70, 0x4f, 0xf3, 0x2e, 0xf3, 0xc9, 0x87, 0x89, 0x7a, 0xe5, 0xc6, 0x99, - 0x2f, 0xe2, 0xc8, 0x00, 0x5d, 0xde, 0x6f, 0x14, 0xc9, 0x2a, 0xd6, 0xe0, 0xa7, 0xef, 0x2e, 0xbe, 0xd3, 0xf8, 0xfe, - 0x27, 0x09, 0x22, 0x3e, 0x64, 0x28, 0xea, 0xc1, 0x80, 0xa2, 0x1e, 0x68, 0x3c, 0x8c, 0x08, 0xc4, 0x0e, 0xc8, 0x0f, - 0x08, 0x82, 0xc8, 0x80, 0x26, 0xb9, 0xea, 0x62, 0x15, 0x66, 0xc1, 0xd2, 0x4f, 0xb2, 0x03, 0xa8, 0x6a, 0x01, 0x92, - 0xd3, 0x37, 0xd9, 0x88, 0x93, 0x68, 0x56, 0xb8, 0xd8, 0xcb, 0x22, 0x21, 0x9b, 0x9d, 0x06, 0xa1, 0x14, 0xcd, 0x8a, - 0x0e, 0xfc, 0xf1, 0x98, 0x2d, 0xb3, 0x81, 0xee, 0x2f, 0x21, 0xfa, 0x05, 0xfa, 0xb3, 0x3e, 0x88, 0xc7, 0x19, 0xcb, - 0xac, 0x34, 0x4b, 0x98, 0xbf, 0xd0, 0xa5, 0x2b, 0xd7, 0x7a, 0x7b, 0xe9, 0x6a, 0xb4, 0x08, 0x32, 0xe9, 0x0b, 0x91, - 0x26, 0x08, 0x42, 0x52, 0x18, 0xe2, 0xe9, 0x30, 0xe7, 0x20, 0x3c, 0x8f, 0x67, 0x95, 0x1d, 0x55, 0x50, 0x2e, 0x67, - 0xe8, 0x69, 0x97, 0x47, 0x3c, 0x98, 0xa0, 0xcd, 0xd3, 0x35, 0xb7, 0x6b, 0x97, 0x2e, 0x1b, 0xf5, 0xd3, 0x13, 0xfe, - 0xbc, 0xd5, 0xd0, 0x15, 0x83, 0xde, 0x71, 0xc0, 0x97, 0xf0, 0x26, 0x8b, 0xf7, 0x03, 0x5e, 0x18, 0xae, 0x26, 0x6a, - 0x19, 0xfd, 0xbc, 0xd3, 0x58, 0x2e, 0x80, 0x10, 0x2a, 0x09, 0xd1, 0xe7, 0xee, 0xa9, 0x34, 0xb1, 0xc2, 0x51, 0x21, - 0xad, 0xf4, 0xf9, 0xf3, 0xcb, 0xe1, 0x7f, 0xfe, 0x0d, 0xce, 0xe8, 0xe7, 0xae, 0xb0, 0x33, 0xbf, 0x54, 0x4b, 0x71, - 0xea, 0xd3, 0x1c, 0xa2, 0x02, 0x05, 0x9b, 0x08, 0xc7, 0x2b, 0x62, 0x6b, 0xe5, 0xc3, 0x2b, 0xe1, 0x4c, 0x0b, 0x02, - 0x4e, 0x18, 0xc2, 0x1a, 0x7e, 0x08, 0xcb, 0x3b, 0x14, 0x4e, 0x18, 0xb4, 0xdf, 0xee, 0xbe, 0x3f, 0x06, 0x67, 0xcb, - 0xb5, 0x38, 0x10, 0xca, 0x00, 0x71, 0x0f, 0x9d, 0x9e, 0xf8, 0x1a, 0x12, 0x2d, 0x48, 0x7e, 0xa4, 0xbd, 0x03, 0x98, - 0xe6, 0x3c, 0x5e, 0x30, 0x3b, 0x88, 0x0f, 0x6e, 0xd9, 0xc8, 0xf2, 0x97, 0x01, 0xc9, 0xea, 0x91, 0xef, 0xa6, 0x11, - 0xe5, 0x27, 0x45, 0xe0, 0x44, 0x5f, 0xe7, 0x05, 0x28, 0xe3, 0x02, 0x50, 0xf0, 0xd3, 0x3f, 0x2d, 0xfb, 0x67, 0xb4, - 0x45, 0x84, 0x80, 0x32, 0x96, 0x3f, 0x23, 0x37, 0x8b, 0xc2, 0xa3, 0x62, 0xf1, 0x61, 0xc5, 0xd3, 0xa9, 0xea, 0x53, - 0xd1, 0x2e, 0xf7, 0x2f, 0xa1, 0x52, 0xec, 0xd9, 0x78, 0x49, 0x3d, 0xd5, 0xbb, 0x90, 0x3f, 0x21, 0x3a, 0x32, 0x77, - 0xbf, 0x09, 0xe7, 0xb9, 0xe6, 0x9b, 0x51, 0x82, 0xe4, 0x31, 0x15, 0xe2, 0x88, 0xa2, 0xea, 0x09, 0x7c, 0x03, 0x69, - 0xf2, 0x68, 0x30, 0x20, 0x3c, 0x56, 0x45, 0x67, 0x00, 0xa5, 0x86, 0x68, 0x09, 0x30, 0xd9, 0x0c, 0x2a, 0x5a, 0x64, - 0x23, 0x87, 0x95, 0xaa, 0xd3, 0xa9, 0x8f, 0xf1, 0xc0, 0x17, 0xfb, 0xab, 0xb4, 0x03, 0x61, 0x67, 0xf1, 0x85, 0x05, - 0x04, 0x2e, 0xda, 0xa9, 0xe0, 0x71, 0xed, 0xaf, 0x84, 0xb2, 0xad, 0xd0, 0xbf, 0x8f, 0x15, 0xdd, 0x05, 0xee, 0xc6, - 0xe0, 0x1c, 0x53, 0x2f, 0x84, 0xf9, 0x60, 0xed, 0x24, 0x49, 0x8f, 0xf3, 0xf5, 0xd3, 0xa4, 0xba, 0x88, 0xdf, 0x75, - 0x98, 0xd4, 0xb2, 0xe5, 0xc9, 0x20, 0x76, 0xcc, 0x8b, 0x83, 0x56, 0xca, 0xc4, 0x73, 0x9f, 0x9f, 0x1c, 0xc0, 0xfc, - 0xc0, 0xf5, 0x42, 0x89, 0x32, 0x0a, 0x0c, 0xf0, 0xef, 0xe0, 0xa7, 0xa4, 0x7f, 0xf1, 0x76, 0x22, 0x88, 0x3a, 0x7c, - 0x39, 0x4a, 0xe7, 0xaf, 0xa5, 0x22, 0x75, 0x62, 0xc5, 0x69, 0xa6, 0xf2, 0x76, 0x47, 0x68, 0xf8, 0x7d, 0x85, 0xe1, - 0x19, 0xf2, 0x7e, 0xc6, 0x84, 0x65, 0xf3, 0x79, 0xb6, 0xc1, 0xf8, 0x79, 0x53, 0x11, 0x22, 0x58, 0xb7, 0x14, 0x28, - 0xf6, 0xf1, 0xb6, 0x52, 0x05, 0x69, 0x24, 0x8b, 0x2d, 0xfd, 0x96, 0xfe, 0x18, 0x77, 0x7c, 0xad, 0x34, 0xa6, 0x42, - 0xb9, 0xf3, 0x6c, 0x00, 0x45, 0x05, 0xb3, 0xdd, 0x5f, 0x2e, 0xa9, 0xb0, 0xd1, 0x3f, 0x39, 0xa0, 0x77, 0xe7, 0x29, - 0xed, 0xb0, 0xd3, 0x13, 0xd0, 0xdf, 0xa4, 0x45, 0xf7, 0x97, 0x4b, 0xbe, 0xa4, 0xf4, 0x8b, 0x72, 0x0e, 0xe6, 0xd9, - 0x22, 0x3c, 0xfd, 0x3f, 0x1d, 0xdb, 0x6f, 0x83, 0x01, 0x5c, 0x03, 0x00}; + 0xdc, 0xb3, 0x37, 0xb7, 0x6d, 0x23, 0xff, 0x7f, 0x3f, 0x05, 0xc3, 0xe4, 0x52, 0x31, 0x21, 0x69, 0x92, 0xb2, 0x6c, + 0x45, 0xb2, 0xec, 0x6b, 0xf3, 0x98, 0x4b, 0xc7, 0x6d, 0x3a, 0x89, 0x9b, 0xb9, 0xab, 0xeb, 0xb1, 0x28, 0x09, 0x92, + 0x78, 0xa1, 0x48, 0x0d, 0x49, 0xf9, 0x51, 0x85, 0xf7, 0x59, 0xee, 0xb3, 0xdc, 0x27, 0xfb, 0xcd, 0xee, 0x02, 0x20, + 0xf8, 0xd0, 0xc3, 0x4d, 0x7a, 0xf7, 0x9b, 0x36, 0x89, 0x08, 0x02, 0x4b, 0x60, 0x01, 0x2c, 0x16, 0xfb, 0xf4, 0x67, + 0x5c, 0xf6, 0x62, 0xb6, 0xd8, 0x7e, 0x9f, 0xfb, 0xfc, 0x99, 0xd9, 0xb8, 0x24, 0x81, 0xe1, 0xb3, 0xb3, 0x78, 0x36, + 0x0b, 0x59, 0x4b, 0x17, 0xc9, 0x43, 0x74, 0x53, 0x7e, 0xe6, 0xec, 0x91, 0x23, 0x22, 0x76, 0x1a, 0xf9, 0xa6, 0xad, + 0x25, 0x46, 0xcc, 0x64, 0x48, 0x3b, 0xe2, 0x5c, 0x51, 0x36, 0x7b, 0x83, 0xea, 0x0d, 0x3e, 0x2f, 0xc5, 0xd6, 0xb5, + 0x26, 0xf1, 0x6a, 0x14, 0x32, 0x0b, 0x97, 0x3b, 0x7c, 0x72, 0x3d, 0x5a, 0x8d, 0x46, 0x90, 0xa5, 0xe5, 0x91, 0x63, + 0x42, 0xdc, 0x99, 0x38, 0xc5, 0xfb, 0x60, 0x6e, 0xf4, 0x61, 0x50, 0x76, 0x56, 0xed, 0x3e, 0xd8, 0x8a, 0x80, 0xa8, + 0x87, 0x3e, 0x90, 0xc1, 0xdd, 0xaf, 0x61, 0xd7, 0x0e, 0xf4, 0x0f, 0xb0, 0xfa, 0x52, 0xbd, 0xdf, 0xb4, 0xf5, 0x07, + 0x97, 0xfa, 0x07, 0xc4, 0x31, 0x66, 0x2f, 0x7e, 0x49, 0xab, 0x57, 0x37, 0x75, 0x52, 0x7a, 0xaf, 0x30, 0x8f, 0x01, + 0x08, 0x7d, 0x5f, 0x05, 0xfe, 0x2c, 0x8a, 0xd3, 0x2c, 0x18, 0xeb, 0x57, 0xfd, 0xb7, 0x41, 0xeb, 0x72, 0x91, 0xb5, + 0x8c, 0x2b, 0x73, 0x9c, 0xa9, 0x29, 0x50, 0x04, 0xc1, 0xc4, 0x0c, 0x28, 0x9b, 0x2a, 0xa9, 0x3b, 0x68, 0x6b, 0x45, + 0x41, 0x9a, 0xb1, 0xd2, 0x38, 0x1b, 0x40, 0xbd, 0x4a, 0x3e, 0x15, 0x4c, 0x0c, 0xa5, 0x63, 0x4b, 0xa3, 0x4f, 0x37, + 0x95, 0x97, 0xab, 0x35, 0x1e, 0xe5, 0x59, 0x71, 0x5a, 0x62, 0x0c, 0x60, 0xe1, 0x38, 0x43, 0xcf, 0x8f, 0x54, 0xa3, + 0xcf, 0xd2, 0xb9, 0x3b, 0xfc, 0xae, 0xcc, 0x17, 0xc0, 0xf9, 0x0d, 0x16, 0x17, 0x51, 0x9c, 0x69, 0x10, 0xd8, 0x06, + 0xbe, 0x38, 0xac, 0x1a, 0x89, 0x71, 0xa8, 0x2d, 0x23, 0xe7, 0xc4, 0xe0, 0x7b, 0x3c, 0xfc, 0x5a, 0x3c, 0xbc, 0x59, + 0x29, 0x82, 0x05, 0x5d, 0x16, 0x22, 0x98, 0xc0, 0x2c, 0x3e, 0x8f, 0x6f, 0xab, 0x7a, 0x90, 0x97, 0xc3, 0xdd, 0x67, + 0x6f, 0x4b, 0xb0, 0xc9, 0x22, 0xab, 0x5f, 0x8b, 0x27, 0x26, 0x15, 0x8c, 0x4e, 0x65, 0x4f, 0xa1, 0xe1, 0x87, 0xe0, + 0x61, 0x32, 0xb0, 0x13, 0xc3, 0xb3, 0x00, 0x48, 0x12, 0x3f, 0xa6, 0x87, 0xf9, 0xb5, 0x48, 0x9d, 0x2c, 0x12, 0x17, + 0x2b, 0x87, 0x33, 0x50, 0xd7, 0x68, 0xb9, 0xca, 0x30, 0xd4, 0x2e, 0x74, 0x80, 0xe5, 0xba, 0x86, 0xa1, 0x3b, 0x81, + 0x4a, 0x17, 0x6c, 0x62, 0xae, 0x6b, 0xc1, 0xa4, 0x5e, 0xc6, 0x99, 0x5e, 0x20, 0x5e, 0x48, 0xdf, 0x51, 0x50, 0x05, + 0x8f, 0x09, 0x1f, 0xc6, 0xd8, 0x2c, 0xe2, 0xd4, 0xb7, 0xc6, 0xa8, 0xd0, 0x69, 0xa0, 0x0c, 0x63, 0x82, 0xd3, 0x6f, + 0x85, 0x8d, 0x83, 0x85, 0xf0, 0x9b, 0xa5, 0x61, 0x0e, 0x9f, 0xac, 0xa3, 0xfc, 0xec, 0xc9, 0x3a, 0xcd, 0x07, 0x4f, + 0xd6, 0xbe, 0xb4, 0x15, 0xd0, 0x2f, 0x74, 0x32, 0x14, 0x18, 0x22, 0x1a, 0x86, 0xf9, 0x75, 0xe1, 0xb9, 0x53, 0x8c, + 0x17, 0x56, 0x19, 0x95, 0x6b, 0xa8, 0xba, 0x1f, 0x70, 0x05, 0xfd, 0x32, 0x09, 0x16, 0x7e, 0x72, 0x4f, 0xfa, 0x7c, + 0x53, 0x55, 0xfa, 0x1b, 0xba, 0x46, 0x84, 0x9e, 0x10, 0x40, 0x34, 0x5f, 0xd7, 0xfe, 0x2a, 0xcb, 0x18, 0x1f, 0xad, + 0x54, 0x6a, 0xc2, 0xb7, 0xae, 0xf5, 0xe7, 0xcc, 0x9e, 0xb0, 0xcc, 0x0f, 0x42, 0x6a, 0xd2, 0x17, 0xd9, 0xea, 0x6b, + 0xc3, 0x4b, 0xcb, 0xc3, 0x8b, 0xca, 0xeb, 0x07, 0x07, 0x43, 0x47, 0x00, 0xf5, 0x1b, 0x47, 0x86, 0x59, 0xac, 0x9a, + 0x67, 0x94, 0xde, 0xfd, 0x57, 0xa7, 0x83, 0xc1, 0x74, 0x44, 0x30, 0x1d, 0x2c, 0x1a, 0xc7, 0x13, 0xf6, 0xcb, 0xfb, + 0xb7, 0x32, 0x6d, 0x16, 0x48, 0x80, 0x86, 0x7c, 0x61, 0xa6, 0xc8, 0x3f, 0x24, 0xc8, 0x3b, 0x50, 0x82, 0x2b, 0x4d, + 0x2e, 0xa1, 0x24, 0xd7, 0xb5, 0x33, 0xea, 0x3b, 0x9b, 0x50, 0xaf, 0x07, 0x31, 0xb6, 0x4a, 0xf2, 0x93, 0x03, 0xaa, + 0x4d, 0xa7, 0x1d, 0x55, 0x02, 0x34, 0x24, 0x30, 0xc2, 0x02, 0x0b, 0x90, 0xe1, 0x73, 0xe0, 0x16, 0x17, 0x0a, 0x7b, + 0x81, 0x72, 0x76, 0xf7, 0xac, 0xcc, 0xaa, 0x60, 0x2b, 0xfd, 0xf4, 0x04, 0x73, 0x76, 0xc1, 0x79, 0x0d, 0x51, 0x3e, + 0x4e, 0x0e, 0xe8, 0x51, 0xab, 0xec, 0x88, 0x02, 0x88, 0xb8, 0xda, 0xf5, 0x38, 0x80, 0x07, 0x6d, 0x15, 0x48, 0x11, + 0x0f, 0xa5, 0x7e, 0xae, 0x6b, 0x0b, 0xce, 0x1a, 0xf1, 0x70, 0x42, 0x10, 0x6b, 0xc0, 0x81, 0xbd, 0xab, 0x6b, 0x0b, + 0xff, 0x0e, 0x47, 0x2e, 0xde, 0xf8, 0x77, 0x2d, 0x97, 0xbf, 0x2a, 0xf6, 0x5a, 0x5a, 0xde, 0x6b, 0x63, 0x3e, 0xb9, + 0xe0, 0x48, 0x20, 0x6f, 0xd6, 0x73, 0x54, 0xd0, 0x36, 0x4c, 0xee, 0x5c, 0x4c, 0xee, 0x64, 0xc3, 0xe4, 0x4e, 0xb6, + 0x4c, 0x6e, 0xc8, 0x27, 0x52, 0x93, 0xa8, 0x4b, 0xd0, 0x39, 0x4c, 0x22, 0x8f, 0x33, 0x1a, 0x3d, 0xbe, 0xcf, 0x10, + 0x4f, 0x56, 0x1a, 0x82, 0x71, 0xd4, 0x06, 0x5c, 0x35, 0xe1, 0x45, 0x41, 0x44, 0x7d, 0xe0, 0x72, 0xd7, 0x89, 0x71, + 0x43, 0x0e, 0xce, 0x56, 0x58, 0x1d, 0x2f, 0xac, 0x52, 0xca, 0x2f, 0xde, 0x9a, 0x6f, 0x18, 0xe9, 0x7c, 0xcb, 0x48, + 0xc7, 0xa5, 0xad, 0xcb, 0x87, 0x4d, 0x9b, 0x50, 0x1d, 0x14, 0xac, 0x41, 0x30, 0x18, 0xc5, 0x25, 0x53, 0x5e, 0x87, + 0x9b, 0x69, 0xac, 0xb2, 0xa2, 0x96, 0x7e, 0x9a, 0xde, 0xc6, 0x09, 0x68, 0x5c, 0x00, 0xcc, 0xc3, 0x96, 0xd4, 0x22, + 0x88, 0x78, 0x30, 0x97, 0x8d, 0x8b, 0xa9, 0x78, 0xaf, 0x2e, 0x29, 0xaf, 0xd3, 0xa1, 0x1a, 0x4b, 0x3f, 0xcb, 0x58, + 0x82, 0x48, 0xf7, 0x21, 0xea, 0xf7, 0xff, 0x93, 0x65, 0xd6, 0x40, 0x43, 0x42, 0x85, 0xaa, 0x23, 0x85, 0x5e, 0x02, + 0x6f, 0x95, 0x88, 0x83, 0x58, 0x09, 0x0c, 0x97, 0x48, 0xc4, 0xff, 0x84, 0xdb, 0xb5, 0x95, 0x28, 0xae, 0x4b, 0xee, + 0x91, 0x61, 0x2f, 0xfd, 0xc9, 0x07, 0x50, 0xec, 0xb5, 0x3c, 0x13, 0x8c, 0x74, 0xd5, 0x30, 0x70, 0x09, 0x31, 0x7b, + 0xe3, 0x82, 0x48, 0x22, 0x95, 0xe4, 0x26, 0x50, 0xe0, 0x3d, 0xe9, 0x5b, 0xd3, 0xab, 0xb5, 0x97, 0x1f, 0xcc, 0x02, + 0xa3, 0x46, 0x35, 0x81, 0xb4, 0x85, 0x83, 0x53, 0x79, 0xe7, 0x0a, 0x4d, 0xf7, 0xc8, 0x00, 0xc9, 0xef, 0x25, 0xe4, + 0x33, 0x75, 0xc4, 0x85, 0x76, 0x98, 0xc0, 0xa9, 0x75, 0xe9, 0x5c, 0xe5, 0x4f, 0x67, 0xf8, 0xcb, 0xbd, 0xca, 0x9f, + 0x8e, 0xf0, 0x97, 0x77, 0x85, 0x99, 0xeb, 0x1a, 0x2e, 0xf2, 0xca, 0x98, 0xf5, 0xd3, 0xd2, 0x7a, 0x22, 0xfb, 0xb3, + 0x07, 0x2c, 0x1b, 0x3e, 0xc1, 0x8f, 0x9f, 0xac, 0x53, 0xf0, 0xb8, 0x54, 0xc7, 0x10, 0xd9, 0x89, 0x91, 0x37, 0x96, + 0xcf, 0x36, 0x94, 0x8f, 0x8c, 0xff, 0xf2, 0xc1, 0x8f, 0xab, 0x24, 0x2e, 0xce, 0x94, 0xb2, 0x18, 0xe2, 0x7a, 0x14, + 0x44, 0x7e, 0x72, 0x7f, 0x4d, 0xd7, 0x8b, 0x96, 0xe0, 0xdd, 0xa5, 0x78, 0x85, 0xd8, 0xcb, 0xb2, 0xba, 0x2b, 0x53, + 0x04, 0xbc, 0xf7, 0xfc, 0xa0, 0x1f, 0xfc, 0x3d, 0x51, 0xd8, 0xb6, 0xd2, 0x05, 0x94, 0x4f, 0x48, 0xe9, 0x43, 0xd7, + 0x4f, 0xd6, 0x2d, 0x56, 0x07, 0x53, 0x19, 0x6d, 0x85, 0x2f, 0x84, 0xe9, 0xc1, 0xcb, 0xec, 0x62, 0x12, 0xf4, 0x50, + 0x9f, 0x35, 0x8a, 0xef, 0xac, 0x27, 0xeb, 0xec, 0x4c, 0x5f, 0xf8, 0xc9, 0x27, 0x36, 0xb1, 0xc6, 0x41, 0x32, 0x0e, + 0x99, 0xde, 0xd3, 0x47, 0xa1, 0x1f, 0x7d, 0xe2, 0x8f, 0x56, 0xbc, 0xca, 0x50, 0x43, 0xbd, 0xf3, 0xee, 0x2b, 0x70, + 0x42, 0x22, 0x3b, 0x64, 0x56, 0x1b, 0xb0, 0xa0, 0xbd, 0x94, 0x02, 0xaf, 0x82, 0x51, 0x2c, 0x6a, 0x99, 0x60, 0x60, + 0x09, 0x4a, 0x73, 0xf0, 0x58, 0x35, 0x75, 0x9c, 0x2f, 0xdd, 0x54, 0x87, 0x4a, 0xc2, 0x4a, 0x99, 0x72, 0xf1, 0x1a, + 0x21, 0xfc, 0xf1, 0xcf, 0x51, 0x32, 0xec, 0xfd, 0x3f, 0x27, 0xa1, 0x7c, 0xd9, 0x08, 0xa1, 0xd4, 0x22, 0x4f, 0x89, + 0x07, 0x7c, 0x9c, 0x33, 0x98, 0x9b, 0x3f, 0xad, 0x36, 0xf6, 0xd3, 0x74, 0xb5, 0x60, 0x13, 0xd2, 0x0c, 0x9e, 0x15, + 0x9d, 0x2a, 0xdf, 0x2c, 0xd4, 0x8e, 0xfd, 0xb6, 0xf2, 0x8e, 0x0f, 0x5f, 0x82, 0xc5, 0x02, 0x30, 0x94, 0xf1, 0x74, + 0xaa, 0x17, 0x77, 0xfc, 0x1d, 0xcd, 0xdc, 0xc3, 0xdf, 0x56, 0x6f, 0x5e, 0x3b, 0x6f, 0x64, 0xe3, 0x08, 0x18, 0x63, + 0xa1, 0x7e, 0xe5, 0x7c, 0xb1, 0xd2, 0x5f, 0x31, 0xa2, 0xa9, 0x1f, 0x6d, 0x1e, 0xce, 0x65, 0x69, 0x89, 0x2f, 0x19, + 0x9b, 0x00, 0xc3, 0x6d, 0xd6, 0x4a, 0xaf, 0x43, 0x76, 0xc3, 0xa4, 0x6a, 0xb7, 0xfe, 0xb1, 0x86, 0x16, 0x18, 0x7b, + 0x8e, 0xab, 0x8c, 0x39, 0x57, 0xa7, 0x0c, 0x69, 0x88, 0x63, 0xe0, 0x23, 0x57, 0xb7, 0x58, 0x65, 0x4b, 0x0d, 0x4d, + 0x5d, 0xe9, 0xc0, 0xc6, 0x9e, 0x9d, 0x6d, 0x28, 0xef, 0x61, 0xe2, 0xe9, 0xe6, 0xbe, 0x99, 0xae, 0xd1, 0x83, 0x58, + 0xdd, 0x1c, 0x4f, 0x21, 0xec, 0xbc, 0x56, 0x21, 0x0e, 0xd9, 0x84, 0xb1, 0x26, 0x21, 0x99, 0x4e, 0xd2, 0x17, 0x61, + 0xed, 0x88, 0x66, 0xbf, 0x42, 0x0e, 0xd5, 0x38, 0x37, 0x5a, 0x79, 0xe4, 0x23, 0x4c, 0xe8, 0x1a, 0xb1, 0x34, 0xdd, + 0x88, 0x30, 0x39, 0xe9, 0xa6, 0x5e, 0xd4, 0x2e, 0xe3, 0xa3, 0x28, 0x37, 0x1d, 0x13, 0x58, 0x02, 0x1c, 0x60, 0xf5, + 0x5b, 0x78, 0xbc, 0x5c, 0x2f, 0xb8, 0xbd, 0x4a, 0x32, 0x1b, 0xe9, 0xdc, 0x96, 0x60, 0xd3, 0xfb, 0x5b, 0x9d, 0x77, + 0xaa, 0x74, 0x4c, 0x37, 0x76, 0xad, 0x55, 0x22, 0xbd, 0x35, 0x71, 0x11, 0x02, 0x10, 0x7d, 0xaa, 0xd0, 0x57, 0x36, + 0x9d, 0xb2, 0x71, 0x96, 0x1a, 0x42, 0x78, 0x24, 0xa3, 0xc7, 0x82, 0xd7, 0xd0, 0xa3, 0x81, 0xfe, 0x13, 0xf8, 0xd0, + 0x8b, 0x20, 0x4b, 0xbc, 0x43, 0xe2, 0xce, 0xd4, 0x8c, 0x26, 0x82, 0x58, 0x46, 0x11, 0xff, 0x0a, 0x24, 0x07, 0x6f, + 0x28, 0xc7, 0xae, 0xf1, 0xf3, 0xa7, 0x58, 0x17, 0xb1, 0xb4, 0x6a, 0xd9, 0x4e, 0x8a, 0xb6, 0x6d, 0xdf, 0xb5, 0xfb, + 0xa6, 0xe3, 0x3a, 0xb9, 0x6e, 0x82, 0xef, 0xd6, 0xa7, 0x7d, 0x37, 0x3d, 0xb6, 0x6a, 0x43, 0xab, 0x55, 0xf4, 0x90, + 0x76, 0x9e, 0xfb, 0xc2, 0xd5, 0x4d, 0x32, 0x99, 0x53, 0x68, 0xdb, 0x38, 0xbe, 0x61, 0xc9, 0x17, 0x0f, 0xa5, 0x0c, + 0x7c, 0xbf, 0xfe, 0x1c, 0xb9, 0x0e, 0x10, 0xe1, 0x2c, 0x5e, 0x3e, 0x60, 0x08, 0x6d, 0xdd, 0xd4, 0xc7, 0x61, 0x9c, + 0x32, 0x75, 0x0c, 0x24, 0x04, 0xf9, 0xc2, 0x41, 0xfc, 0xfc, 0xfe, 0xf5, 0x87, 0x0f, 0xba, 0x89, 0x99, 0x40, 0x53, + 0x15, 0x3a, 0x5f, 0x50, 0x3b, 0xa8, 0x7f, 0xe3, 0xba, 0xa3, 0x13, 0x86, 0x2e, 0xb5, 0xe5, 0x35, 0x47, 0x65, 0xb5, + 0x25, 0xc7, 0x4f, 0x1e, 0xfe, 0x65, 0xba, 0x89, 0xee, 0x35, 0xae, 0x06, 0xda, 0xb0, 0xfd, 0x78, 0x2b, 0x95, 0x2c, + 0x82, 0xe8, 0xba, 0xa1, 0xd4, 0xbf, 0x6b, 0x28, 0x85, 0xab, 0x5c, 0x8d, 0x56, 0xad, 0xe2, 0x85, 0xc2, 0x1a, 0x40, + 0x22, 0xe7, 0x5d, 0xe8, 0x52, 0xee, 0x53, 0x5f, 0xd0, 0x69, 0x1e, 0xc9, 0xbd, 0xda, 0xeb, 0x86, 0x62, 0x7e, 0x09, + 0x92, 0xb8, 0x1d, 0x87, 0x60, 0xf0, 0xc7, 0x54, 0xad, 0x5c, 0x99, 0x6d, 0x94, 0xe6, 0xba, 0x0a, 0x10, 0x62, 0x6f, + 0xaf, 0x33, 0xb6, 0x58, 0xb2, 0xc4, 0xcf, 0x56, 0x09, 0xbb, 0x0e, 0xe3, 0xdb, 0x47, 0x85, 0x39, 0xfd, 0x8e, 0xca, + 0xf3, 0x60, 0x36, 0x97, 0xb5, 0xcf, 0x5a, 0x6c, 0x20, 0x27, 0x70, 0xeb, 0x07, 0xf2, 0xff, 0xfc, 0xdb, 0xb6, 0xff, + 0xf3, 0xef, 0x9d, 0x55, 0x01, 0x7c, 0x3e, 0x34, 0xb3, 0xc1, 0x1e, 0xeb, 0xa2, 0xf9, 0x4b, 0x65, 0x9c, 0x37, 0xd7, + 0xa9, 0x4d, 0x02, 0xbc, 0xaf, 0x4d, 0x41, 0xad, 0xb0, 0xbc, 0x6e, 0x1e, 0xd4, 0x31, 0x18, 0xd7, 0xce, 0x9e, 0x41, + 0xa5, 0x2f, 0xea, 0xda, 0xd0, 0xe8, 0xed, 0x35, 0x23, 0x7f, 0x1c, 0xc3, 0xbb, 0xc6, 0xf0, 0x85, 0xdd, 0xe7, 0x72, + 0xc9, 0x97, 0xc3, 0xa1, 0xcc, 0x2d, 0xa7, 0x36, 0x05, 0x13, 0xff, 0xb3, 0x5a, 0x09, 0x3f, 0x3c, 0x7b, 0x8e, 0x41, + 0xbe, 0xf7, 0x83, 0x97, 0x43, 0x34, 0x46, 0x3b, 0x19, 0x25, 0x05, 0xb3, 0xb2, 0x91, 0xb4, 0x91, 0x31, 0x79, 0x0d, + 0x68, 0x8d, 0xae, 0x41, 0x29, 0x26, 0x1c, 0xcb, 0x87, 0x86, 0xf9, 0x72, 0xc8, 0x05, 0x4b, 0xdc, 0xfe, 0xb5, 0x57, + 0x5d, 0xda, 0x5c, 0x2c, 0x5b, 0x42, 0xba, 0xa9, 0x91, 0xfe, 0x07, 0x2b, 0xb3, 0x42, 0x8e, 0x87, 0x02, 0x7e, 0x90, + 0x28, 0x0c, 0x73, 0xcc, 0x77, 0xf2, 0x6e, 0x93, 0x8d, 0xd8, 0xcf, 0xbb, 0x6d, 0xc4, 0x2e, 0xf6, 0xb2, 0x11, 0xfb, + 0xf9, 0xab, 0xdb, 0x88, 0xbd, 0x53, 0x6d, 0xc4, 0x60, 0x12, 0x5f, 0xb3, 0xbd, 0x0c, 0xb7, 0x84, 0xd5, 0x46, 0x7c, + 0x9b, 0x0e, 0x5c, 0xce, 0xd2, 0xa6, 0xe3, 0x39, 0x03, 0x19, 0x01, 0x9f, 0x95, 0x30, 0x9e, 0x81, 0x11, 0xd7, 0x9f, + 0x6f, 0x6e, 0x15, 0xc6, 0x33, 0xd5, 0xd8, 0x2a, 0xe2, 0x11, 0x5f, 0x8b, 0x28, 0x4e, 0x64, 0xe0, 0xe4, 0x98, 0x22, + 0xe6, 0x93, 0x75, 0x68, 0x28, 0x59, 0xad, 0xa5, 0xf5, 0x9a, 0x27, 0x4c, 0xa0, 0x7a, 0x68, 0x3d, 0x25, 0x1b, 0x7a, + 0xcf, 0x45, 0x6c, 0x0b, 0x15, 0x82, 0xb4, 0x12, 0xa6, 0x38, 0x11, 0x6b, 0xfd, 0xb7, 0x3b, 0xf7, 0xfb, 0x4b, 0xb7, + 0xdf, 0x76, 0xc1, 0x39, 0x1b, 0x6e, 0x98, 0x58, 0xe0, 0xf4, 0xdb, 0x6d, 0x28, 0xb8, 0x55, 0x0a, 0x3c, 0x28, 0x08, + 0x94, 0x82, 0x0e, 0x14, 0x8c, 0x95, 0x82, 0x23, 0x28, 0x98, 0x28, 0x05, 0xc7, 0x50, 0x70, 0xa3, 0xe7, 0x97, 0x91, + 0xec, 0xee, 0xb1, 0x71, 0x65, 0xd2, 0xa5, 0x42, 0x94, 0x1d, 0x9b, 0x2e, 0x58, 0x4d, 0xf9, 0xb3, 0x5e, 0x6c, 0x92, + 0x74, 0xb1, 0x97, 0x98, 0xb7, 0x73, 0x46, 0x81, 0xa2, 0x5f, 0xe1, 0x99, 0x63, 0x67, 0x31, 0xd8, 0x4d, 0x8b, 0x00, + 0x0c, 0x02, 0x0f, 0x9a, 0x6e, 0x80, 0xc0, 0xa8, 0x2f, 0x67, 0x4e, 0x04, 0xb1, 0x50, 0xe6, 0xb2, 0x78, 0x47, 0x9f, + 0xb3, 0xe4, 0x12, 0x28, 0x2c, 0x4e, 0x5a, 0xaa, 0x54, 0xf2, 0x6b, 0xd8, 0x1d, 0xbc, 0x62, 0xa3, 0xd5, 0x4c, 0x3b, + 0x8f, 0x67, 0x3b, 0x4d, 0x08, 0xd4, 0x57, 0xd0, 0x4b, 0x9d, 0xd4, 0x2f, 0x96, 0x58, 0x96, 0xfc, 0x5b, 0xf4, 0x98, + 0x97, 0xeb, 0x67, 0xd0, 0x37, 0x2d, 0x23, 0x03, 0x16, 0xf8, 0x0e, 0xe0, 0x48, 0xd1, 0xe1, 0x9f, 0x03, 0x9e, 0x95, + 0xe7, 0x0b, 0x5f, 0xe9, 0xcf, 0xe9, 0x8f, 0x2c, 0x4d, 0xfd, 0x99, 0xa8, 0x5f, 0xef, 0x27, 0x18, 0xed, 0xc8, 0xfb, + 0x17, 0x22, 0x10, 0x24, 0x79, 0x41, 0xcd, 0x36, 0x23, 0x89, 0x6f, 0x35, 0xb0, 0xfe, 0x81, 0x05, 0x55, 0xd8, 0x29, + 0x04, 0x36, 0x4c, 0x61, 0xd9, 0xa2, 0x00, 0x36, 0xff, 0x0d, 0x0b, 0xab, 0x85, 0x99, 0x3f, 0xab, 0x16, 0xd1, 0x3a, + 0xc8, 0xd5, 0xbe, 0x49, 0x85, 0x7e, 0xa9, 0xf0, 0x4b, 0x34, 0xd4, 0x61, 0x3c, 0xfb, 0x53, 0xd5, 0xd3, 0x5b, 0xcc, + 0x0a, 0x3e, 0x44, 0x66, 0x90, 0x0d, 0x6d, 0xc4, 0xb1, 0x66, 0x03, 0x0a, 0x7b, 0x51, 0x36, 0xb7, 0xd0, 0xb5, 0xac, + 0xe5, 0x45, 0x86, 0x69, 0xe3, 0xdc, 0xae, 0xab, 0x0e, 0xb5, 0xbd, 0x64, 0x36, 0xf2, 0x5b, 0xae, 0x77, 0x6c, 0x8a, + 0x3f, 0xb6, 0xd3, 0x31, 0x72, 0x84, 0xa0, 0x4d, 0x82, 0x9b, 0xf5, 0x34, 0x8e, 0x32, 0x6b, 0xea, 0x2f, 0x82, 0xf0, + 0xbe, 0xb7, 0x88, 0xa3, 0x38, 0x5d, 0xfa, 0x63, 0xd6, 0x2f, 0x2e, 0xd4, 0x7d, 0x0c, 0xd5, 0xc0, 0xbd, 0x05, 0x5d, + 0xdb, 0x4b, 0xd8, 0x82, 0x5a, 0xcb, 0x48, 0x0c, 0xd3, 0x90, 0xdd, 0xe5, 0xfc, 0xf3, 0xa5, 0xca, 0x54, 0x15, 0x97, + 0x1c, 0xb5, 0x00, 0x8e, 0x94, 0x87, 0x79, 0x80, 0xe0, 0x46, 0xfd, 0xa5, 0x3f, 0xc1, 0xc8, 0x84, 0xb6, 0xd7, 0x49, + 0xd8, 0x42, 0xb3, 0x3b, 0x1b, 0x81, 0x27, 0xf1, 0xed, 0x29, 0xf4, 0x16, 0x1b, 0x5b, 0x29, 0x0b, 0xa7, 0xf8, 0xc6, + 0x42, 0xcf, 0x12, 0x01, 0xc7, 0xc2, 0x8b, 0x38, 0x40, 0x63, 0x8b, 0x3e, 0xbc, 0xee, 0x79, 0x9a, 0xd3, 0x5f, 0x04, + 0x91, 0x45, 0xc3, 0x39, 0x76, 0x96, 0x0a, 0x2c, 0x15, 0x7f, 0xc6, 0x1a, 0xab, 0xbb, 0x9a, 0xd3, 0x87, 0xcb, 0xda, + 0x34, 0x8c, 0x6f, 0x7b, 0xf3, 0x60, 0x32, 0x61, 0x51, 0x1f, 0xfb, 0x2c, 0x0b, 0x59, 0x18, 0x06, 0xcb, 0x34, 0x48, + 0xfb, 0x0b, 0xff, 0x8e, 0x43, 0x3d, 0xdc, 0x04, 0xb5, 0xcd, 0xa1, 0xb6, 0xf7, 0x86, 0xaa, 0x80, 0x01, 0x2f, 0x16, + 0x82, 0xc3, 0xbb, 0xd6, 0xd1, 0x9c, 0xca, 0x38, 0xf7, 0x86, 0xba, 0x4c, 0xd8, 0x7a, 0xe1, 0x27, 0xb3, 0x20, 0xea, + 0x39, 0xb9, 0x7d, 0xb3, 0xa6, 0x85, 0xf1, 0xb8, 0xdb, 0xed, 0xe6, 0xf6, 0x44, 0x3c, 0x39, 0x93, 0x49, 0x6e, 0x8f, + 0xc5, 0xd3, 0x74, 0xea, 0x38, 0xd3, 0x69, 0x6e, 0x07, 0xa2, 0xa0, 0xed, 0x8d, 0x27, 0x6d, 0x2f, 0xb7, 0x6f, 0x95, + 0x1a, 0xb9, 0xcd, 0xf8, 0x53, 0xc2, 0x26, 0x7d, 0x5c, 0x48, 0x64, 0x56, 0xda, 0x3b, 0x76, 0x9c, 0x1c, 0x29, 0xc0, + 0x65, 0x89, 0x36, 0xa1, 0xac, 0xe7, 0x6a, 0xbd, 0x77, 0x4d, 0xad, 0xf8, 0xdc, 0x78, 0xdc, 0x58, 0x6f, 0xe2, 0x27, + 0x9f, 0xae, 0x34, 0x65, 0x14, 0xbe, 0x4f, 0xd5, 0xd6, 0x02, 0x0d, 0xd6, 0x5d, 0x0f, 0x42, 0x76, 0xf5, 0x47, 0x71, + 0x02, 0x7b, 0x36, 0xf1, 0x27, 0xc1, 0x2a, 0xed, 0xb9, 0xde, 0xf2, 0x4e, 0x14, 0xf1, 0xb5, 0x5e, 0x14, 0xe0, 0xde, + 0xeb, 0xa5, 0x71, 0x18, 0x4c, 0x44, 0xd1, 0xa6, 0xbd, 0xe4, 0x7a, 0x46, 0x1f, 0x1d, 0xd6, 0x03, 0x0c, 0xbb, 0xe0, + 0x87, 0xa1, 0x66, 0xb7, 0x53, 0x8d, 0xf9, 0x29, 0xca, 0x97, 0x35, 0x27, 0x25, 0xbc, 0xa0, 0x73, 0xba, 0x7b, 0xb8, + 0xbc, 0x93, 0x6b, 0xde, 0x3d, 0x5a, 0xde, 0xe5, 0x7f, 0x5d, 0xb0, 0x49, 0xe0, 0x6b, 0xad, 0x62, 0x35, 0xb9, 0x0e, + 0xc8, 0xa0, 0x8d, 0xf5, 0x86, 0x65, 0x2a, 0xb6, 0x05, 0x84, 0x36, 0x7c, 0x14, 0x2c, 0x96, 0x71, 0x92, 0xf9, 0x51, + 0x96, 0xe7, 0xc3, 0xab, 0x3c, 0xef, 0x5f, 0x04, 0xad, 0xcb, 0x7f, 0xb4, 0xe8, 0x9c, 0x26, 0x9d, 0x4d, 0x6e, 0x5c, + 0x99, 0xaf, 0x99, 0x6a, 0x33, 0x02, 0xc7, 0x18, 0xda, 0x8b, 0xa8, 0x95, 0xe9, 0x94, 0xac, 0x57, 0x26, 0x24, 0xcb, + 0xea, 0x64, 0x83, 0x52, 0xae, 0x82, 0x27, 0x10, 0x54, 0x78, 0xcd, 0x06, 0x17, 0x8a, 0xfd, 0x09, 0x30, 0x2b, 0x58, + 0x99, 0xfc, 0x0a, 0x9e, 0x6c, 0xe2, 0x19, 0xbf, 0xdb, 0xcd, 0x33, 0xfe, 0x9a, 0xed, 0xc3, 0x33, 0x7e, 0xf7, 0xd5, + 0x79, 0xc6, 0x27, 0x75, 0xbf, 0x82, 0xb7, 0xf1, 0x40, 0x97, 0x1a, 0x06, 0x38, 0x9a, 0x12, 0x8a, 0xd8, 0xf3, 0xf6, + 0x0f, 0xbb, 0x01, 0x08, 0x68, 0x94, 0x83, 0x8e, 0x4e, 0x6e, 0x90, 0xc7, 0xbe, 0x8b, 0x06, 0x7f, 0x4f, 0xd4, 0xe7, + 0xe9, 0x74, 0xf0, 0x2a, 0x56, 0x0a, 0xe4, 0x13, 0x37, 0xbe, 0x28, 0x45, 0x57, 0xa0, 0x37, 0xc2, 0x0a, 0x13, 0xf3, + 0x4f, 0x80, 0x73, 0x36, 0x59, 0x1d, 0x4f, 0xa4, 0xf5, 0x59, 0xbf, 0xdc, 0x85, 0x96, 0x34, 0xf9, 0x14, 0x2e, 0x38, + 0x35, 0x51, 0xe2, 0x8c, 0x65, 0xdc, 0x67, 0xf6, 0xfb, 0xfb, 0xb7, 0x93, 0xd6, 0xdb, 0xd8, 0xc8, 0x83, 0xf4, 0x5d, + 0xd5, 0x01, 0x86, 0xeb, 0x7e, 0x06, 0xea, 0x70, 0x72, 0x6e, 0x41, 0xa6, 0x26, 0x98, 0x86, 0xd7, 0xd4, 0xfc, 0xac, + 0x34, 0xd2, 0x9e, 0xda, 0x90, 0x27, 0xba, 0xaa, 0x1d, 0xc6, 0xdc, 0xfb, 0x60, 0xcd, 0x39, 0x40, 0xcc, 0xdd, 0x85, + 0x7e, 0xc3, 0x13, 0x6a, 0x1e, 0x4c, 0xf2, 0xdc, 0xe8, 0x0b, 0x44, 0x28, 0x07, 0x2d, 0xdb, 0xc5, 0xc4, 0xa5, 0xb7, + 0xd2, 0xa6, 0x81, 0x6b, 0x08, 0x49, 0xfd, 0xf7, 0x16, 0x14, 0xea, 0x5c, 0x59, 0xc8, 0x71, 0xa6, 0x6b, 0x84, 0x3e, + 0x32, 0xb4, 0x50, 0x06, 0x04, 0x1a, 0x60, 0x89, 0x7f, 0xf1, 0x4a, 0x14, 0xd4, 0x6d, 0x38, 0x09, 0x39, 0x68, 0x11, + 0x00, 0x5e, 0xfe, 0x42, 0xae, 0x4d, 0x64, 0x87, 0xd7, 0xc1, 0x87, 0x5c, 0x97, 0xbc, 0x1f, 0x2e, 0xbf, 0xd3, 0x93, + 0x03, 0x68, 0x70, 0x5a, 0x31, 0x1c, 0xd8, 0x61, 0xa1, 0x08, 0xac, 0x44, 0x7a, 0x6b, 0xda, 0xe9, 0xad, 0xf6, 0x6c, + 0x2d, 0x22, 0x64, 0x64, 0xfe, 0xd2, 0x82, 0x2b, 0x3e, 0xd2, 0x5e, 0x4e, 0xf1, 0x94, 0x60, 0x1c, 0xfd, 0x55, 0x0a, + 0xb4, 0x11, 0x2f, 0xaa, 0x48, 0x7f, 0xfa, 0xe3, 0x55, 0x92, 0xc6, 0x49, 0x6f, 0x19, 0x07, 0x51, 0xc6, 0x92, 0x1c, + 0x51, 0x75, 0x89, 0xf8, 0x11, 0xe8, 0xb9, 0x5a, 0xc7, 0x4b, 0x7f, 0x1c, 0x64, 0xf7, 0x3d, 0x87, 0xb3, 0x14, 0x4e, + 0x9f, 0x73, 0x07, 0x4e, 0x63, 0xfd, 0x1e, 0xc7, 0xe6, 0x73, 0x64, 0xfc, 0x92, 0x3a, 0x3b, 0xa3, 0x2e, 0xf3, 0xbe, + 0xf2, 0x96, 0x62, 0x84, 0x00, 0xfb, 0xe1, 0x27, 0xd6, 0x0c, 0xa8, 0x3c, 0x4c, 0xb5, 0x33, 0x61, 0x33, 0x13, 0xa9, + 0x36, 0xc8, 0xe5, 0xc5, 0x1f, 0xbb, 0x63, 0x68, 0x4e, 0x73, 0x31, 0x70, 0x3c, 0xc6, 0x3e, 0x3d, 0xeb, 0xf9, 0x90, + 0x51, 0xcb, 0xdc, 0xa7, 0xe6, 0x88, 0x4d, 0xe3, 0x84, 0x51, 0x3c, 0x59, 0xb7, 0xbb, 0xbc, 0xdb, 0x1f, 0xfc, 0xf6, + 0xe1, 0x37, 0xc3, 0x89, 0xe2, 0xac, 0x25, 0x80, 0x19, 0x3b, 0xa0, 0xd5, 0xcf, 0x33, 0x60, 0x0d, 0x09, 0xf3, 0x63, + 0x0a, 0xdd, 0xd5, 0xd3, 0xf5, 0x7e, 0x63, 0xd8, 0xae, 0x65, 0xcc, 0xcf, 0xbc, 0x84, 0x85, 0x7e, 0x16, 0xdc, 0x08, + 0x9e, 0xb1, 0x7d, 0xb4, 0xbc, 0x13, 0x73, 0x8c, 0x07, 0xde, 0x03, 0x26, 0xa9, 0xd2, 0x15, 0x31, 0x49, 0xd5, 0x62, + 0x9c, 0xa4, 0x7e, 0x6d, 0x34, 0x22, 0x92, 0x45, 0xe5, 0xa4, 0xef, 0x2c, 0xef, 0xd4, 0x23, 0xba, 0x68, 0x26, 0x4f, + 0xea, 0x6a, 0x08, 0xb2, 0x45, 0x30, 0x99, 0x84, 0x2c, 0x2f, 0x4d, 0x74, 0x79, 0x2e, 0x15, 0xe4, 0x48, 0x3c, 0xf8, + 0xa3, 0x34, 0x0e, 0x57, 0x19, 0x6b, 0x46, 0x17, 0x21, 0xc7, 0x73, 0x0a, 0xe4, 0xe0, 0xef, 0x72, 0x5f, 0x3b, 0xc0, + 0x6e, 0xc3, 0x32, 0x71, 0xfa, 0x10, 0x71, 0xd8, 0x6a, 0x97, 0xbb, 0x0e, 0xaf, 0x64, 0xa7, 0xcd, 0x86, 0x81, 0x98, + 0x70, 0x2c, 0x11, 0xf5, 0xd6, 0x6c, 0x97, 0x97, 0xc9, 0xa8, 0xab, 0xb2, 0x28, 0x2f, 0x0f, 0xe6, 0xcf, 0xd9, 0x63, + 0x2f, 0x9a, 0xf7, 0xd8, 0x0b, 0xb1, 0xc7, 0xb6, 0xaf, 0xcc, 0xc7, 0x53, 0x17, 0xfe, 0xeb, 0x17, 0x03, 0xea, 0x39, + 0x5a, 0x7b, 0x79, 0xa7, 0xb9, 0xcb, 0x3b, 0xcd, 0xf2, 0x96, 0x77, 0x1a, 0x82, 0x46, 0x7b, 0x10, 0xd3, 0xf6, 0x0c, + 0xd3, 0xd1, 0xa0, 0x10, 0xfe, 0x38, 0xa5, 0x57, 0xee, 0x21, 0xbc, 0x83, 0x56, 0x9d, 0xfa, 0x3b, 0x6f, 0xfb, 0x56, + 0xa7, 0xbd, 0x24, 0x88, 0xb6, 0x61, 0x67, 0xfe, 0x68, 0xc4, 0x26, 0xbd, 0x69, 0x3c, 0x5e, 0xa5, 0xff, 0xe2, 0xfd, + 0xe7, 0x48, 0xdc, 0x4a, 0x08, 0x2a, 0x70, 0x44, 0x53, 0x50, 0x94, 0xdc, 0x30, 0x01, 0x61, 0x2d, 0xe7, 0xa9, 0x47, + 0xe1, 0x91, 0x3d, 0xfb, 0xb0, 0x61, 0x91, 0x37, 0x23, 0xfa, 0x4f, 0x9b, 0xa5, 0xcd, 0x24, 0xe6, 0x0b, 0xd0, 0xb2, + 0x15, 0x1d, 0x0f, 0xc7, 0x06, 0x9f, 0x4d, 0xa7, 0xdb, 0xdc, 0xdd, 0x4b, 0xf1, 0xa5, 0x2b, 0x71, 0xa8, 0xf0, 0x73, + 0x8b, 0x3b, 0xa6, 0x6c, 0x87, 0xba, 0x69, 0x8d, 0xd4, 0xa0, 0x6e, 0x39, 0x10, 0x8a, 0xba, 0x7b, 0x52, 0xf9, 0xc7, + 0x2f, 0x0e, 0xe1, 0x3f, 0xe2, 0xea, 0x7f, 0xcd, 0x9a, 0x18, 0xf5, 0xb7, 0x65, 0x4b, 0x70, 0x62, 0x95, 0x90, 0x11, + 0xdf, 0xbf, 0xfe, 0x74, 0xfa, 0xb0, 0x06, 0x7b, 0xd7, 0x26, 0x53, 0xaa, 0x6a, 0xed, 0xef, 0xe3, 0x18, 0x52, 0x77, + 0xd6, 0xab, 0x0b, 0xf4, 0x90, 0xb1, 0x7b, 0x36, 0x80, 0x46, 0xe2, 0x1e, 0x41, 0x5a, 0x7c, 0x1d, 0xdb, 0xd0, 0x55, + 0xe2, 0xf5, 0xa6, 0xab, 0xc4, 0xab, 0xdd, 0x57, 0x89, 0x1f, 0xf6, 0xba, 0x4a, 0xbc, 0xfa, 0xea, 0x57, 0x89, 0xd7, + 0xf5, 0xab, 0xc4, 0x45, 0x2c, 0xec, 0x67, 0xcd, 0xb7, 0x2b, 0xfe, 0xf3, 0x23, 0x29, 0xe5, 0xce, 0xe3, 0x41, 0xc7, + 0xa1, 0x90, 0xc7, 0x17, 0x7f, 0xf8, 0x62, 0x81, 0x0b, 0xf1, 0x3d, 0x9a, 0x93, 0x15, 0x57, 0x0b, 0x4e, 0xd9, 0xf1, + 0x3b, 0x4a, 0x71, 0x18, 0x47, 0xb3, 0x9f, 0x41, 0x29, 0x0b, 0xe2, 0xc0, 0x44, 0x79, 0x11, 0xa4, 0x3f, 0xc7, 0xcb, + 0xd5, 0xf2, 0x2d, 0xc0, 0xfa, 0x18, 0xa4, 0xc1, 0x28, 0x64, 0xd2, 0x13, 0x99, 0xcc, 0xdf, 0xb8, 0x4c, 0x1c, 0x2c, + 0x4e, 0xc5, 0x4f, 0xff, 0x4e, 0xfc, 0x44, 0x9b, 0x54, 0xfe, 0x9b, 0xec, 0xea, 0xf4, 0xe6, 0x8b, 0x88, 0x50, 0x02, + 0x2a, 0x9d, 0x7e, 0xf8, 0x65, 0xe4, 0x22, 0x36, 0x1a, 0x46, 0x29, 0xec, 0x1d, 0x36, 0xc2, 0x61, 0xb5, 0x4b, 0xcd, + 0xca, 0x30, 0x65, 0x08, 0xae, 0xba, 0x18, 0x7e, 0x11, 0xaf, 0x52, 0x36, 0x89, 0x6f, 0x23, 0xdd, 0x8c, 0xa4, 0x93, + 0x01, 0x68, 0x38, 0x65, 0x1b, 0x4c, 0x1e, 0xf9, 0x01, 0x19, 0xe5, 0x38, 0x69, 0xe9, 0x90, 0xbb, 0x74, 0xb5, 0xb4, + 0x48, 0xd5, 0x6c, 0xe1, 0x10, 0x75, 0x99, 0xe5, 0xe8, 0x51, 0xab, 0x15, 0x0f, 0x1e, 0xd6, 0x52, 0x98, 0x6a, 0xc4, + 0x36, 0x97, 0x0a, 0xa7, 0xad, 0x48, 0x08, 0x17, 0x45, 0x1c, 0x8c, 0x86, 0x89, 0xe3, 0x6f, 0xc8, 0x75, 0xb5, 0x78, + 0x0b, 0x51, 0x44, 0xf2, 0x15, 0x9f, 0x0f, 0x1e, 0x15, 0x82, 0x1e, 0x5f, 0x2a, 0x68, 0x7c, 0x77, 0xc3, 0x92, 0xd0, + 0xbf, 0x6f, 0x19, 0x79, 0x1c, 0xfd, 0x08, 0x08, 0x78, 0x15, 0xdf, 0x46, 0x6a, 0x05, 0x4c, 0xd6, 0xd2, 0xb0, 0x96, + 0x1a, 0xe3, 0x97, 0x80, 0xe3, 0x8a, 0xd2, 0x03, 0x48, 0x93, 0x3b, 0x63, 0x7f, 0x37, 0xe9, 0xdf, 0x7f, 0x18, 0xb9, + 0x79, 0x1e, 0xcb, 0x0f, 0xfd, 0xb2, 0xdc, 0xe3, 0x33, 0x4f, 0x9f, 0x3e, 0xda, 0x3c, 0xec, 0x72, 0x7a, 0xf6, 0x86, + 0xd6, 0xc6, 0xc6, 0x5d, 0x00, 0xbd, 0xb8, 0x88, 0x57, 0xe3, 0x39, 0x1a, 0xba, 0x7e, 0xbd, 0xf1, 0x66, 0x00, 0x13, + 0xb3, 0x94, 0xca, 0xa1, 0x57, 0x8a, 0x0a, 0x2c, 0xe0, 0xf7, 0x5f, 0x43, 0x00, 0xce, 0xff, 0x21, 0x1a, 0xea, 0xab, + 0x86, 0xdf, 0xe2, 0x83, 0x87, 0x2d, 0xde, 0x3e, 0x24, 0xd3, 0xe4, 0xa1, 0x2d, 0x84, 0x72, 0xad, 0x99, 0xc8, 0xe4, + 0x55, 0xa4, 0xa9, 0x61, 0xe4, 0x36, 0x45, 0xc8, 0x13, 0x5f, 0x61, 0x36, 0x5d, 0xd3, 0xb9, 0xa3, 0x81, 0xc9, 0x38, + 0xb5, 0xaa, 0x10, 0x19, 0x6e, 0xf2, 0xc0, 0x90, 0x7c, 0x55, 0xdf, 0x2d, 0x82, 0xc8, 0xc4, 0x28, 0xf0, 0xf5, 0x37, + 0xfe, 0x1d, 0xc4, 0x41, 0x06, 0xe2, 0x56, 0x7d, 0x05, 0x85, 0xa6, 0xea, 0x37, 0x07, 0xa9, 0x9e, 0xf4, 0x46, 0x4c, + 0x08, 0x2d, 0xde, 0xf0, 0x1b, 0x4d, 0xd3, 0x34, 0x79, 0x8d, 0xd0, 0xe4, 0x3d, 0x02, 0xcb, 0xf1, 0x3a, 0x00, 0xda, + 0x92, 0x7c, 0x79, 0x47, 0x25, 0x70, 0x33, 0x40, 0x9d, 0xac, 0x28, 0xe0, 0xa1, 0xfe, 0x3a, 0x8e, 0x28, 0x10, 0x17, + 0x7a, 0x08, 0xd3, 0xe6, 0x27, 0x10, 0x11, 0xb8, 0xa7, 0xe1, 0x85, 0x1d, 0xdf, 0x72, 0x49, 0xb0, 0xe6, 0xd0, 0xe3, + 0xb0, 0xcf, 0x9a, 0x63, 0xc2, 0x45, 0x0a, 0x15, 0x04, 0xad, 0x43, 0x25, 0xc4, 0xb3, 0xc9, 0x1a, 0x68, 0x23, 0xde, + 0x8b, 0xee, 0xb2, 0x05, 0x8b, 0x56, 0x3a, 0xe6, 0x84, 0xc2, 0x18, 0x7d, 0x50, 0xe7, 0x15, 0x31, 0x5b, 0x40, 0x6d, + 0x9a, 0x5b, 0xce, 0xe9, 0x2c, 0x4c, 0x39, 0x49, 0xf5, 0xcd, 0x31, 0x57, 0x6c, 0xa6, 0x9c, 0xb6, 0x55, 0x4f, 0x08, + 0x3e, 0xa5, 0x71, 0xd5, 0x91, 0x8b, 0x2c, 0xa1, 0x01, 0x06, 0x45, 0xc7, 0xe0, 0xe2, 0x22, 0x81, 0xf6, 0x96, 0x5f, + 0x9d, 0x34, 0xa9, 0x91, 0xf1, 0x2b, 0x82, 0xa2, 0xc4, 0xa8, 0x83, 0xe1, 0xfd, 0x84, 0xc0, 0x44, 0x1b, 0xe1, 0x8c, + 0x6b, 0x70, 0x36, 0x0c, 0xfa, 0x13, 0xbb, 0xa7, 0x83, 0x84, 0x50, 0xf5, 0x89, 0xdd, 0x83, 0xed, 0xdf, 0x6b, 0x90, + 0xa6, 0xe8, 0x5b, 0xc8, 0xb5, 0x09, 0xa1, 0xfe, 0xc7, 0x10, 0xac, 0x6a, 0xcb, 0x06, 0x72, 0xf2, 0x2d, 0x54, 0x1c, + 0x51, 0x0c, 0x59, 0x9d, 0xc5, 0x26, 0xe6, 0x26, 0xfe, 0xad, 0x46, 0x1c, 0x5b, 0x0d, 0x5b, 0xc3, 0x78, 0xe6, 0x3a, + 0xce, 0x41, 0xad, 0x3e, 0x08, 0xb2, 0x9b, 0x6a, 0x1b, 0x66, 0x36, 0x70, 0x1d, 0x2b, 0x78, 0x66, 0x7b, 0xfd, 0xda, + 0x19, 0xad, 0xc4, 0x92, 0x1c, 0xa2, 0xf8, 0xeb, 0xf4, 0xc9, 0xba, 0x55, 0xdb, 0x90, 0x46, 0xd5, 0x64, 0x1e, 0xfb, + 0x96, 0x73, 0xf9, 0xd7, 0xb0, 0x7e, 0xf4, 0x53, 0x24, 0x4b, 0xca, 0x6b, 0x32, 0x84, 0x68, 0xc8, 0x2d, 0xd8, 0x46, + 0x7f, 0xd1, 0x9e, 0x6b, 0x2d, 0xda, 0x3e, 0x86, 0x31, 0x94, 0xe9, 0xb2, 0x85, 0x4f, 0x99, 0x0a, 0xa0, 0xf2, 0xc5, + 0xb4, 0x4a, 0xe1, 0x78, 0xdc, 0x55, 0x56, 0x68, 0xf4, 0xb6, 0x72, 0x0b, 0x08, 0x7f, 0xc3, 0xf1, 0x69, 0x8f, 0x20, + 0x2e, 0x01, 0xd4, 0x80, 0xd8, 0xe9, 0x3b, 0x01, 0xae, 0x96, 0x65, 0x70, 0xe5, 0x43, 0x72, 0x7f, 0x60, 0x78, 0xe8, + 0xa0, 0x0e, 0x4d, 0xc2, 0x6b, 0x3e, 0xee, 0x1e, 0x08, 0x92, 0x45, 0x93, 0x32, 0xc0, 0xca, 0xf9, 0xb5, 0x3f, 0xb8, + 0x12, 0x45, 0x81, 0xa4, 0x02, 0x71, 0x03, 0x45, 0xc9, 0xe3, 0x08, 0x17, 0x3f, 0x6d, 0xb7, 0x60, 0x2f, 0x2e, 0x06, + 0x1b, 0x50, 0x44, 0x30, 0xd9, 0x4c, 0x11, 0x8a, 0x43, 0xe4, 0x6a, 0x74, 0x0b, 0x6e, 0x09, 0x46, 0x74, 0xe3, 0x4a, + 0xcc, 0x84, 0x4d, 0x61, 0xd1, 0x26, 0xe0, 0xb1, 0x28, 0xf7, 0x95, 0x5a, 0x07, 0xbb, 0xa5, 0xd6, 0xd9, 0x2e, 0xa9, + 0x35, 0xb9, 0x53, 0xdd, 0x26, 0xfe, 0x52, 0xf1, 0xc8, 0x13, 0xcc, 0xb9, 0xea, 0x98, 0x57, 0x12, 0x75, 0xa3, 0xf7, + 0x95, 0x68, 0x55, 0x83, 0x46, 0x56, 0x82, 0x28, 0xfe, 0x56, 0x2e, 0x28, 0x42, 0xa1, 0xae, 0xca, 0xc6, 0x2f, 0x0a, + 0xd9, 0x38, 0xdd, 0x6a, 0x0a, 0x47, 0x1a, 0xc1, 0xfd, 0x2b, 0x4e, 0x6a, 0xf2, 0x76, 0x50, 0x38, 0xab, 0x15, 0x3d, + 0x55, 0xdc, 0xaf, 0x8a, 0x8b, 0x86, 0xe2, 0xd4, 0x27, 0x6e, 0x19, 0x65, 0xdf, 0xbe, 0x72, 0xd5, 0xc2, 0xfb, 0xaa, + 0x28, 0x07, 0xa9, 0x3b, 0x76, 0x59, 0x16, 0xab, 0xcb, 0xa6, 0xec, 0x7e, 0xa3, 0xbe, 0x56, 0x16, 0x89, 0xf4, 0x93, + 0x21, 0x04, 0x0b, 0x31, 0x7d, 0x45, 0xaf, 0x2d, 0x6d, 0x20, 0xb0, 0x93, 0x0d, 0x6e, 0x7d, 0xbb, 0xa5, 0xf3, 0x94, + 0x2f, 0xa1, 0xd0, 0xc2, 0xab, 0x32, 0x08, 0xc4, 0xef, 0xd5, 0xba, 0xe1, 0x94, 0xc7, 0x43, 0x9e, 0x9f, 0xef, 0x20, + 0x5e, 0xd4, 0x1c, 0x55, 0x91, 0x8f, 0x3b, 0xd3, 0x22, 0xf3, 0x5c, 0xac, 0x5a, 0x07, 0x4a, 0x42, 0x9c, 0x35, 0xf7, + 0x8c, 0x29, 0xcb, 0xe8, 0x79, 0x8d, 0x9e, 0xf8, 0x2e, 0x5f, 0x3a, 0xc9, 0x2a, 0xc2, 0xd8, 0xf6, 0x56, 0x96, 0xf8, + 0xe3, 0x4f, 0x4a, 0x97, 0x85, 0x9c, 0x13, 0x64, 0xc0, 0x65, 0x4d, 0x41, 0xdf, 0xc7, 0x50, 0x90, 0xac, 0x67, 0x7b, + 0xa9, 0x22, 0x7d, 0xe9, 0x3d, 0x76, 0xda, 0xfe, 0x8b, 0xe9, 0x61, 0x45, 0x28, 0xea, 0x75, 0xca, 0x22, 0xf3, 0x0d, + 0xfd, 0xc8, 0xe6, 0xab, 0xc5, 0x68, 0xad, 0xca, 0x56, 0x15, 0x91, 0x6b, 0x5d, 0xcc, 0xaa, 0x7e, 0x76, 0x3a, 0x9d, + 0x96, 0x05, 0x8d, 0x8e, 0x76, 0x88, 0xc2, 0xc2, 0xc7, 0x8e, 0xe3, 0x54, 0xfb, 0xbe, 0x1d, 0xed, 0x16, 0xca, 0x6d, + 0xbb, 0x8d, 0x3d, 0x46, 0xdc, 0xee, 0xc2, 0x5f, 0x1d, 0x1d, 0xb9, 0x5d, 0xec, 0xec, 0x92, 0x59, 0x44, 0x9f, 0x8c, + 0x21, 0x82, 0x8c, 0x2d, 0xd2, 0xde, 0x98, 0xa1, 0x0e, 0xc6, 0x56, 0x36, 0x34, 0x1a, 0x0e, 0x58, 0x33, 0x30, 0x15, + 0x71, 0xc5, 0xaa, 0x70, 0x34, 0x94, 0x87, 0xd7, 0x84, 0xf7, 0xe2, 0x23, 0xb8, 0x51, 0xd6, 0x75, 0x99, 0x36, 0x0e, + 0xab, 0xe3, 0xfc, 0xa5, 0x54, 0x4f, 0x83, 0x03, 0x70, 0x2d, 0x14, 0xda, 0x24, 0x9f, 0xc5, 0xbf, 0xa5, 0xfc, 0xff, + 0xc5, 0xf2, 0xae, 0x6c, 0x3f, 0xd2, 0x05, 0x89, 0x76, 0xb1, 0x5b, 0xa8, 0xd7, 0x4d, 0x6b, 0x40, 0x5a, 0x19, 0x4c, + 0x55, 0x05, 0x3a, 0x28, 0xe9, 0x4b, 0x09, 0x40, 0x1a, 0xc4, 0xef, 0xc8, 0x31, 0xc3, 0x14, 0x17, 0x22, 0xc4, 0x22, + 0x7d, 0x1d, 0x8c, 0xc1, 0x7c, 0xde, 0x45, 0xfd, 0x41, 0x69, 0x4d, 0x80, 0x36, 0xbe, 0x36, 0xb6, 0xbd, 0xc4, 0xfd, + 0x55, 0xbd, 0x96, 0x00, 0x0c, 0x28, 0x73, 0x61, 0x13, 0xa2, 0x21, 0x81, 0x56, 0x59, 0xdc, 0xd4, 0x4b, 0xf9, 0x56, + 0xd5, 0xb3, 0x89, 0x8e, 0x21, 0xb8, 0xe6, 0x2a, 0x04, 0x5b, 0x68, 0x0b, 0x60, 0xb0, 0x7c, 0xf9, 0xe1, 0xb3, 0x05, + 0x53, 0xac, 0xae, 0x47, 0x17, 0xa7, 0x1c, 0xd7, 0xaf, 0x85, 0x67, 0x67, 0x4a, 0xfb, 0x1f, 0xe5, 0x8b, 0x3f, 0x34, + 0x0a, 0xf4, 0x2e, 0x4a, 0x12, 0x3a, 0x6e, 0x2d, 0xee, 0x19, 0x7b, 0xd5, 0x5e, 0x04, 0xd1, 0xfe, 0x75, 0xfd, 0xbb, + 0xbd, 0xeb, 0xc2, 0x81, 0xb1, 0x77, 0x65, 0x38, 0x71, 0xc8, 0x72, 0x21, 0x1b, 0xfc, 0xa0, 0x08, 0x14, 0x55, 0xaf, + 0x63, 0x1d, 0x5b, 0x11, 0x97, 0x7f, 0xb1, 0x1a, 0x0c, 0x4f, 0xce, 0xee, 0x16, 0xa1, 0x76, 0xc3, 0x12, 0x48, 0xed, + 0x33, 0xd0, 0x5d, 0xdb, 0xd1, 0x35, 0xf4, 0xa1, 0x0d, 0xa2, 0xd9, 0x40, 0xff, 0xe5, 0xe2, 0x8d, 0xd5, 0xd5, 0xcf, + 0x40, 0x45, 0x7b, 0x33, 0xc3, 0x63, 0xef, 0xdc, 0xbf, 0x67, 0xc9, 0xb5, 0xa7, 0x6b, 0x98, 0xc1, 0x87, 0x0e, 0x3c, + 0x2c, 0xd3, 0x3c, 0x7d, 0x8f, 0x44, 0x11, 0x9a, 0xc8, 0xf5, 0xa6, 0x03, 0xc9, 0x71, 0xbd, 0xae, 0xe6, 0x7a, 0x87, + 0xf6, 0x51, 0x57, 0x3f, 0xfd, 0x46, 0xd3, 0x4e, 0x26, 0x6c, 0x9a, 0x9e, 0xe2, 0x15, 0xed, 0x04, 0xcf, 0x08, 0xfa, + 0xad, 0x69, 0xf6, 0x38, 0x4c, 0x2d, 0x57, 0x5b, 0xf3, 0x47, 0x4d, 0x9b, 0x06, 0x61, 0xd8, 0xd3, 0x1e, 0x4f, 0xbd, + 0xe9, 0xe1, 0xf4, 0x45, 0x9f, 0x17, 0xe7, 0xdf, 0x94, 0xaa, 0x9b, 0xf4, 0xaf, 0xa7, 0x34, 0x4b, 0xb3, 0x24, 0xfe, + 0xc4, 0xb8, 0xd9, 0x89, 0x26, 0x2f, 0x8f, 0xd5, 0xa6, 0x5e, 0xfd, 0x4b, 0x6e, 0x77, 0x34, 0x9e, 0x7a, 0x45, 0x75, + 0xec, 0xe3, 0x81, 0xec, 0xe4, 0xc9, 0x81, 0xe8, 0xfa, 0x89, 0x8a, 0x26, 0xd7, 0x6a, 0x42, 0x94, 0xab, 0xf3, 0x31, + 0xce, 0xc4, 0xf8, 0x4e, 0x20, 0x0e, 0xa3, 0x74, 0xd7, 0x85, 0x1e, 0xe8, 0xda, 0x64, 0xa0, 0xff, 0xe8, 0x7a, 0x5d, + 0xd3, 0x75, 0x8f, 0xec, 0xa3, 0xee, 0xd8, 0x31, 0x0f, 0xed, 0x43, 0xab, 0x6d, 0x1f, 0x99, 0x5d, 0xab, 0x6b, 0x76, + 0xff, 0xd6, 0x1d, 0x5b, 0x87, 0xf6, 0xa1, 0xe9, 0x58, 0x5d, 0x28, 0xb4, 0xba, 0x56, 0xf7, 0xc6, 0x3a, 0xec, 0x8e, + 0x1d, 0x2c, 0xf5, 0xec, 0x4e, 0xc7, 0x72, 0x1d, 0xbb, 0xd3, 0x31, 0x3b, 0xf6, 0xd1, 0x91, 0xe5, 0xb6, 0xed, 0xa3, + 0xa3, 0xf3, 0x4e, 0xd7, 0x6e, 0xc3, 0xbb, 0x76, 0x7b, 0xdc, 0xb6, 0x5d, 0xd7, 0x82, 0xbf, 0xcc, 0xae, 0xed, 0xd1, + 0x0f, 0xd7, 0xb5, 0xdb, 0xae, 0xe9, 0x84, 0x1d, 0xcf, 0x3e, 0x7a, 0x61, 0xe2, 0xdf, 0x58, 0xcd, 0xc4, 0xbf, 0x00, + 0x8c, 0xf9, 0xc2, 0xf6, 0x8e, 0xe8, 0x17, 0x02, 0xbc, 0x39, 0xec, 0xfe, 0xaa, 0x1f, 0x6c, 0x1c, 0x83, 0x4b, 0x63, + 0xe8, 0x76, 0xec, 0x76, 0xdb, 0x3c, 0x74, 0xed, 0x6e, 0x7b, 0x6e, 0x1d, 0x7a, 0xf6, 0xd1, 0xf1, 0xd8, 0x72, 0xed, + 0xe3, 0x63, 0xd3, 0xb1, 0xda, 0xb6, 0x67, 0xba, 0xf6, 0x61, 0x1b, 0x7f, 0xb4, 0x6d, 0xef, 0xe6, 0xf8, 0x85, 0x7d, + 0xd4, 0x99, 0x1f, 0xd9, 0x87, 0x1f, 0x0f, 0xbb, 0xb6, 0xd7, 0x9e, 0xb7, 0x8f, 0x6c, 0xef, 0xf8, 0xe6, 0xc8, 0x3e, + 0x9c, 0x5b, 0xde, 0xd1, 0xd6, 0x96, 0xae, 0x67, 0x03, 0x8e, 0xf0, 0x35, 0xbc, 0x30, 0xf9, 0x0b, 0xf8, 0x33, 0xc7, + 0xb6, 0xff, 0x45, 0x30, 0x69, 0xbd, 0xe9, 0x0b, 0xbb, 0x7b, 0x3c, 0xa6, 0xea, 0x50, 0x60, 0x89, 0x1a, 0xd0, 0xe4, + 0xc6, 0xa2, 0xcf, 0x22, 0x38, 0x4b, 0x00, 0x12, 0x7f, 0xf8, 0xc7, 0x6e, 0x2c, 0xf8, 0x30, 0x7d, 0xf7, 0x7f, 0x0a, + 0x47, 0x4e, 0x39, 0x64, 0xae, 0xfc, 0x86, 0xff, 0x43, 0x49, 0x5f, 0x86, 0xe6, 0xf9, 0x26, 0x45, 0xc5, 0xfb, 0xdd, + 0x8a, 0x8a, 0x37, 0xab, 0x7d, 0x14, 0x15, 0xef, 0xbf, 0xba, 0xa2, 0xe2, 0xbc, 0x6a, 0x27, 0xff, 0xbe, 0x1a, 0x9b, + 0xfe, 0xd7, 0x75, 0xf5, 0x1a, 0x12, 0xf8, 0xad, 0xcb, 0x8b, 0xd5, 0x15, 0x44, 0x57, 0x7a, 0x1f, 0x0f, 0xde, 0xac, + 0x4a, 0x46, 0x60, 0x31, 0xd0, 0xd8, 0xf7, 0x31, 0xd1, 0xd8, 0xdf, 0x57, 0x03, 0xb0, 0x3c, 0xe1, 0x7c, 0x49, 0x30, + 0xb1, 0xe6, 0x7e, 0x38, 0x95, 0x3c, 0x0d, 0x94, 0xf4, 0xb1, 0x18, 0xbc, 0x12, 0xe0, 0xb8, 0x06, 0x75, 0xd8, 0x6a, + 0x11, 0xa5, 0xbd, 0x23, 0x07, 0x0e, 0x52, 0x6f, 0x9a, 0xe4, 0x95, 0xc6, 0xb6, 0x88, 0x47, 0x75, 0xcd, 0xbd, 0x26, + 0x36, 0xbe, 0x47, 0xa3, 0xc0, 0x66, 0xe8, 0x6e, 0x1d, 0xae, 0x06, 0xd6, 0x36, 0xc2, 0x68, 0x12, 0xd8, 0xb9, 0xa6, + 0xf7, 0x65, 0xd3, 0xbc, 0x8a, 0x31, 0xe6, 0xe6, 0x9e, 0x42, 0x4f, 0xaa, 0xed, 0xdd, 0xb2, 0x69, 0xdf, 0xae, 0x61, + 0x36, 0x7c, 0xbe, 0xd4, 0x7c, 0x8b, 0x5d, 0xa1, 0x04, 0x5c, 0x45, 0x55, 0x25, 0xb3, 0x5a, 0x23, 0x42, 0x0a, 0xee, + 0xbe, 0x30, 0x3e, 0x2c, 0x58, 0x4b, 0x47, 0x43, 0x7e, 0xc7, 0x51, 0xde, 0x95, 0x60, 0xaa, 0x06, 0x8b, 0xcf, 0xd6, + 0xc8, 0x71, 0x07, 0xbf, 0x03, 0xeb, 0xc8, 0x39, 0x9e, 0x51, 0xac, 0xe2, 0x79, 0xad, 0xc0, 0xa5, 0xcb, 0x4c, 0x3e, + 0x77, 0xd7, 0x75, 0xe6, 0x71, 0xa3, 0xa9, 0xb2, 0xcb, 0x16, 0x82, 0x0b, 0xc2, 0xcf, 0x93, 0x61, 0x70, 0x4e, 0xc6, + 0xdb, 0x68, 0xfb, 0xbc, 0x0d, 0x98, 0xa8, 0xf7, 0x18, 0x16, 0xb1, 0xc9, 0x1f, 0xd4, 0xb8, 0x00, 0xeb, 0x29, 0x64, + 0xc1, 0xee, 0x21, 0x9b, 0xa6, 0xf0, 0xa8, 0x1e, 0x5a, 0x31, 0xf7, 0xb7, 0x18, 0xd8, 0xa8, 0x80, 0x39, 0x10, 0xb4, + 0x86, 0xde, 0x66, 0x93, 0x23, 0x9d, 0x47, 0xd6, 0x25, 0x15, 0xb5, 0xdb, 0x39, 0x36, 0xdd, 0x23, 0xd3, 0x3e, 0xee, + 0x18, 0xb9, 0xd8, 0x70, 0x2a, 0xc8, 0x12, 0x42, 0xc0, 0x28, 0x5a, 0x76, 0x33, 0x88, 0x82, 0x2c, 0xf0, 0xc3, 0x1c, + 0xf8, 0xe3, 0xf2, 0xad, 0xe2, 0x9f, 0xab, 0x34, 0x83, 0x31, 0x0a, 0xa6, 0x17, 0x0d, 0xc2, 0xad, 0x11, 0xcb, 0x6e, + 0x19, 0x8b, 0x36, 0x28, 0xcb, 0xab, 0xf6, 0xe5, 0x7f, 0x9e, 0xb5, 0x6d, 0x4e, 0x96, 0x2c, 0xa3, 0x2c, 0xe2, 0xeb, + 0x43, 0x18, 0x43, 0xe7, 0x43, 0xf3, 0xa7, 0x4d, 0x04, 0xf7, 0x9f, 0xbb, 0x09, 0x6e, 0xc6, 0xf6, 0x21, 0xb8, 0xff, + 0xfc, 0xea, 0x04, 0xf7, 0x27, 0x95, 0xe0, 0x96, 0x7c, 0x81, 0x0a, 0xa9, 0xf3, 0x07, 0x7c, 0x6e, 0x41, 0x50, 0xe7, + 0xe7, 0xfa, 0x01, 0x31, 0xf0, 0xba, 0x92, 0x6c, 0xf7, 0x63, 0x29, 0x7b, 0x10, 0x0a, 0x45, 0x30, 0x08, 0x2d, 0x65, + 0x2a, 0x81, 0x44, 0xb4, 0x32, 0xa5, 0x3a, 0xc0, 0x7c, 0x1b, 0x65, 0xa1, 0xfd, 0x9e, 0x5f, 0xfc, 0x40, 0xc9, 0xf3, + 0x26, 0x4e, 0x16, 0x3e, 0x06, 0xe0, 0xd3, 0x31, 0xeb, 0x20, 0x3c, 0x38, 0xe0, 0x7f, 0x36, 0x8e, 0xa3, 0x89, 0xd4, + 0x54, 0xb0, 0xc1, 0x25, 0x71, 0xdc, 0xfa, 0x3d, 0xf3, 0x13, 0xdd, 0xa4, 0xd7, 0x30, 0xb9, 0xcf, 0xda, 0xce, 0x33, + 0xef, 0xf0, 0xd9, 0x91, 0x03, 0xff, 0xbb, 0xac, 0x9d, 0x9b, 0xbc, 0xe2, 0x22, 0x8e, 0x20, 0xf1, 0x89, 0xa8, 0xb9, + 0xa9, 0xda, 0x2d, 0x63, 0x9f, 0x8a, 0x5a, 0xc7, 0xcd, 0x95, 0x26, 0xfe, 0x7d, 0x51, 0xa7, 0xb1, 0xc6, 0x3c, 0x5e, + 0x29, 0xdd, 0x6a, 0xe8, 0x4d, 0x10, 0xad, 0x40, 0xf6, 0xa6, 0xd4, 0x50, 0x5f, 0xf3, 0xe1, 0x16, 0xe3, 0x62, 0xed, + 0xfc, 0xaa, 0xc8, 0xae, 0x24, 0xb2, 0xbc, 0xec, 0xc4, 0x20, 0x57, 0x5b, 0x38, 0x18, 0x9b, 0x1d, 0xf3, 0x0b, 0x69, + 0x90, 0xdb, 0x50, 0x4c, 0x90, 0x4f, 0x13, 0x94, 0x25, 0xab, 0x68, 0xdc, 0xc2, 0x9f, 0xfe, 0x28, 0x6d, 0x05, 0x07, + 0x10, 0x9d, 0x15, 0x3f, 0x6c, 0xe0, 0xac, 0xf9, 0xa7, 0x4e, 0x91, 0x8a, 0x22, 0x15, 0xb3, 0xe2, 0x3f, 0xcb, 0xcc, + 0x84, 0x12, 0xd8, 0xe2, 0xd4, 0x5a, 0x03, 0xff, 0x99, 0x6c, 0xf8, 0x2c, 0x33, 0x21, 0x89, 0x2c, 0x4c, 0xf7, 0xd3, + 0xa7, 0x54, 0x0b, 0xd2, 0x3a, 0xd2, 0xb0, 0xce, 0xc6, 0x45, 0x78, 0x37, 0xcd, 0x9f, 0xc5, 0x14, 0xe1, 0xad, 0x37, + 0x36, 0xe3, 0xe7, 0xcf, 0x4f, 0x07, 0xae, 0xc1, 0x93, 0x92, 0x96, 0x32, 0x68, 0x9d, 0xef, 0x67, 0x7c, 0x60, 0x34, + 0xba, 0xc5, 0x2d, 0xe1, 0xce, 0xe4, 0x08, 0x13, 0x65, 0x4e, 0xbd, 0x20, 0xa3, 0x05, 0x29, 0x19, 0x7d, 0x61, 0x04, + 0x20, 0xea, 0xc8, 0x5b, 0x57, 0xdb, 0x76, 0x6c, 0x47, 0x97, 0x0d, 0xa7, 0xc1, 0x6c, 0xb0, 0x8e, 0x33, 0x1f, 0x72, + 0x03, 0x85, 0xf1, 0x0c, 0x7c, 0x6b, 0xb2, 0x20, 0x0b, 0x21, 0xd1, 0x0c, 0x38, 0xd9, 0x2c, 0xe8, 0x5e, 0x9e, 0x73, + 0x8b, 0x67, 0x3f, 0xf9, 0x84, 0xc9, 0x06, 0x85, 0x5b, 0x1d, 0x46, 0x1c, 0xfa, 0x11, 0x0e, 0xc3, 0x96, 0xde, 0x82, + 0x54, 0x97, 0x2c, 0x49, 0x2d, 0xd5, 0x83, 0xa0, 0xa7, 0x41, 0x1b, 0x48, 0x43, 0x8f, 0x00, 0xa6, 0x89, 0xbf, 0x80, + 0x98, 0xec, 0xeb, 0xdc, 0xe4, 0x94, 0x56, 0xe7, 0xa4, 0x56, 0x73, 0x5f, 0x1c, 0x99, 0x9a, 0xe7, 0x9a, 0x9a, 0x03, + 0xe4, 0x56, 0xcf, 0xcd, 0x75, 0x7e, 0xd5, 0xdf, 0xa5, 0x04, 0x25, 0xfa, 0xf2, 0x98, 0xc6, 0x41, 0xea, 0x4f, 0x2e, + 0x5e, 0xce, 0x28, 0x80, 0x64, 0x4b, 0x89, 0x96, 0x1e, 0x90, 0x22, 0xe4, 0x82, 0xdd, 0x65, 0x06, 0x26, 0x62, 0xe1, + 0x55, 0x02, 0x63, 0x8d, 0xce, 0x7f, 0x41, 0xa4, 0x05, 0x9f, 0x3f, 0xb7, 0x02, 0x70, 0x70, 0x18, 0x28, 0xf8, 0x81, + 0x67, 0xa3, 0x84, 0xb0, 0xa0, 0x50, 0xdd, 0x21, 0xb2, 0xc0, 0xfb, 0x08, 0xfe, 0x2d, 0x8a, 0xc5, 0x0f, 0xae, 0x3a, + 0xb5, 0x43, 0x3f, 0x9a, 0x01, 0x49, 0xf3, 0xa3, 0x59, 0xcd, 0x44, 0x83, 0xfc, 0x17, 0x2b, 0xa5, 0x05, 0xa8, 0xc2, + 0x7c, 0x22, 0xfd, 0xfe, 0xfe, 0x82, 0x12, 0x4d, 0x41, 0x52, 0x73, 0x7f, 0x82, 0xce, 0x76, 0x85, 0x76, 0xe7, 0xf9, + 0xe0, 0xdb, 0x93, 0x05, 0xcb, 0x7c, 0x12, 0x0d, 0xc3, 0xe5, 0x17, 0xd8, 0x01, 0x6d, 0x2c, 0x92, 0xc4, 0x52, 0x32, + 0xf9, 0x09, 0xbb, 0x09, 0xc6, 0xfc, 0x5e, 0x6a, 0x6a, 0xfc, 0x9c, 0xb2, 0xd0, 0x0a, 0x6c, 0xe0, 0x9a, 0x64, 0x84, + 0x3c, 0xf6, 0x31, 0xcc, 0xe4, 0x20, 0x8a, 0xf5, 0xd3, 0x6f, 0xa5, 0xbf, 0xd6, 0xa6, 0x49, 0x80, 0x6c, 0x8f, 0x97, + 0x09, 0x0b, 0xff, 0x35, 0xf8, 0x16, 0x0e, 0xee, 0x6f, 0xaf, 0x74, 0xa3, 0x9f, 0xd9, 0xf3, 0x84, 0x4d, 0x07, 0xdf, + 0x36, 0x64, 0x3d, 0xc4, 0xeb, 0x3d, 0xf5, 0x45, 0x6f, 0x7b, 0x45, 0x70, 0xa0, 0xf6, 0x5e, 0x97, 0xfa, 0x53, 0x7e, + 0x5b, 0x87, 0x1b, 0xe0, 0xba, 0x74, 0xc7, 0x76, 0xfb, 0x78, 0x7f, 0x1e, 0x85, 0xfe, 0xf8, 0x53, 0x9f, 0xde, 0x94, + 0x1e, 0x2c, 0x38, 0xad, 0xc7, 0xfe, 0xb2, 0x87, 0xc7, 0xab, 0x5a, 0x08, 0xee, 0x9a, 0x54, 0x2a, 0x39, 0xbb, 0xc6, + 0xb5, 0x8c, 0x4b, 0x79, 0x8d, 0x5f, 0xc6, 0x4f, 0xdd, 0xce, 0x83, 0x8c, 0x89, 0x4f, 0xe1, 0x43, 0x9e, 0x8b, 0x8b, + 0x3a, 0x5d, 0x51, 0xf1, 0x62, 0x6d, 0xb7, 0x35, 0xb7, 0xfb, 0xb7, 0xce, 0x8d, 0xeb, 0xcc, 0x3d, 0xd7, 0xee, 0x7e, + 0x74, 0xbb, 0xf3, 0xb6, 0x7d, 0x1c, 0x5a, 0x6d, 0xfb, 0x18, 0xfe, 0x7c, 0x3c, 0xb6, 0xbb, 0x73, 0xcb, 0xb3, 0x0f, + 0x3f, 0xba, 0x5e, 0x68, 0x75, 0xed, 0x63, 0xf8, 0x73, 0x4e, 0xad, 0xe0, 0x02, 0x44, 0xf7, 0x9d, 0x6f, 0x4b, 0x54, + 0x40, 0xf9, 0x2d, 0xf5, 0x34, 0x66, 0xe9, 0x78, 0x6b, 0xd0, 0xf5, 0x00, 0xc9, 0xd0, 0x4d, 0x11, 0x04, 0x32, 0xea, + 0xb7, 0x20, 0x0d, 0x3b, 0x26, 0x10, 0x10, 0x26, 0x2f, 0xc2, 0x2f, 0x55, 0x84, 0xd2, 0x6f, 0xdc, 0x46, 0xbc, 0x4d, + 0x73, 0xc0, 0x75, 0x91, 0x99, 0x8a, 0x94, 0x43, 0xbf, 0x2c, 0x31, 0x88, 0x91, 0x08, 0x11, 0xaf, 0x50, 0xa5, 0x22, + 0x3b, 0x62, 0xbe, 0xbb, 0xe3, 0xe8, 0x99, 0xcb, 0x64, 0x76, 0x9e, 0xaf, 0x0a, 0x9b, 0x2b, 0x8c, 0x24, 0xf4, 0x3f, + 0x0a, 0x07, 0x93, 0xd2, 0x12, 0x1c, 0x11, 0xcd, 0x75, 0x12, 0x24, 0xb2, 0x7b, 0x0a, 0x89, 0x76, 0x9b, 0x23, 0xd5, + 0x1b, 0x90, 0xc6, 0xe4, 0x2d, 0x70, 0xc9, 0x37, 0x7e, 0xa8, 0x18, 0xb7, 0x28, 0x2d, 0x1f, 0x49, 0xca, 0xff, 0xf4, + 0x69, 0xd1, 0x39, 0xab, 0xd2, 0xef, 0x13, 0xb7, 0x03, 0xc7, 0x6e, 0x87, 0xb5, 0xb7, 0xda, 0x59, 0xed, 0x0e, 0x07, + 0x5c, 0x84, 0x0b, 0x15, 0xb6, 0x14, 0x42, 0x8b, 0xbb, 0xd1, 0xd8, 0xab, 0xa6, 0xc3, 0x85, 0x40, 0xca, 0x95, 0xab, + 0x8e, 0x6e, 0xf4, 0x23, 0xa1, 0x92, 0x8c, 0xb6, 0x84, 0x40, 0xe6, 0x77, 0x31, 0x1d, 0x50, 0xb3, 0x65, 0x1c, 0x3b, + 0x9c, 0x46, 0xff, 0xd7, 0x83, 0x40, 0x07, 0x2e, 0xd0, 0xa1, 0x56, 0x76, 0x6b, 0xc9, 0xa1, 0x47, 0x9e, 0xab, 0x74, + 0xa0, 0xb2, 0xf4, 0x4c, 0x87, 0x22, 0xc8, 0x6f, 0x85, 0x29, 0xed, 0xa4, 0x01, 0x99, 0x3c, 0x2d, 0x8a, 0x02, 0x33, + 0x80, 0x18, 0xe0, 0x2d, 0xe1, 0x4c, 0x66, 0x3c, 0x7d, 0xba, 0xf1, 0x10, 0x22, 0x85, 0xbd, 0x9a, 0xd9, 0x53, 0x57, + 0xe9, 0x9b, 0xae, 0x92, 0x18, 0x09, 0x17, 0xa9, 0x86, 0xb0, 0x7b, 0xa3, 0xb5, 0x87, 0x3f, 0x47, 0xcc, 0xcf, 0x6c, + 0xae, 0x69, 0x6a, 0x29, 0x87, 0xbb, 0xe9, 0xb2, 0x36, 0x58, 0xbc, 0xf1, 0x58, 0x67, 0x3c, 0x96, 0xe0, 0x93, 0xf5, + 0xc7, 0x15, 0xf7, 0xf4, 0x06, 0x18, 0x9f, 0x9d, 0x22, 0x3c, 0xcd, 0xbb, 0xcc, 0xa7, 0x18, 0x26, 0xea, 0x91, 0x1b, + 0x67, 0xbe, 0xc8, 0x23, 0x03, 0x7c, 0x79, 0xbf, 0x51, 0x25, 0xab, 0x78, 0x83, 0x9f, 0xbe, 0xbb, 0xf8, 0x4e, 0xe3, + 0xeb, 0x9f, 0x34, 0x88, 0x78, 0x91, 0xa1, 0xac, 0x07, 0x03, 0xca, 0x7a, 0xa0, 0xf1, 0x34, 0x22, 0x90, 0x3b, 0x20, + 0x3f, 0x20, 0x0c, 0xa2, 0x00, 0x9a, 0xf4, 0xaa, 0x8b, 0x55, 0x98, 0x05, 0x4b, 0x3f, 0xc9, 0x0e, 0xa0, 0xa9, 0x05, + 0x44, 0x4e, 0xdf, 0xe4, 0x23, 0x4e, 0xaa, 0x59, 0x11, 0x62, 0x2f, 0x8b, 0x84, 0x6e, 0x76, 0x1a, 0x84, 0x52, 0x35, + 0x2b, 0x3e, 0xe0, 0x8f, 0xc7, 0x6c, 0x99, 0x0d, 0x74, 0x7f, 0x09, 0xd9, 0x2f, 0x30, 0x9e, 0xf5, 0x41, 0x3c, 0xce, + 0x58, 0x66, 0xa5, 0x59, 0xc2, 0xfc, 0x85, 0x2e, 0x43, 0xb9, 0xd6, 0xe1, 0xa5, 0xab, 0xd1, 0x22, 0xc8, 0x64, 0x2c, + 0x44, 0x1a, 0x20, 0x28, 0x49, 0xa1, 0x8b, 0xa7, 0xc3, 0x9c, 0xa3, 0xf0, 0x3c, 0x9e, 0x55, 0x56, 0x54, 0xc1, 0xb9, + 0x9c, 0x61, 0xa4, 0x5d, 0x9e, 0xf1, 0x60, 0x82, 0x3e, 0x4f, 0xd7, 0xdc, 0xaf, 0x5d, 0x86, 0x6c, 0xd4, 0x4f, 0x4f, + 0xf8, 0xf5, 0x56, 0xc3, 0x50, 0x0c, 0x7a, 0xc7, 0x81, 0x58, 0xc2, 0x9b, 0x3c, 0xde, 0x0f, 0x78, 0x65, 0x38, 0x9a, + 0x08, 0x32, 0xc6, 0x79, 0xa7, 0xbe, 0x5c, 0x00, 0x23, 0x54, 0x52, 0xa2, 0xcf, 0xdd, 0x53, 0xe9, 0x62, 0x85, 0xbd, + 0x42, 0x5e, 0xe9, 0xf3, 0xe7, 0x97, 0xc3, 0xff, 0xfc, 0x1b, 0x82, 0xd1, 0xcf, 0x5d, 0xe1, 0x67, 0x7e, 0xa9, 0xd6, + 0xe2, 0xdc, 0xa7, 0x39, 0x44, 0x03, 0x0a, 0x36, 0x11, 0x81, 0x57, 0xc4, 0xd2, 0xca, 0x87, 0x57, 0x22, 0x98, 0x16, + 0x24, 0x9c, 0x30, 0x84, 0x37, 0xfc, 0x10, 0xa6, 0x77, 0x28, 0x82, 0x30, 0x68, 0xbf, 0xdd, 0x7d, 0x7f, 0x0c, 0xc1, + 0x96, 0x6b, 0x79, 0x20, 0x94, 0x0e, 0xe2, 0x1a, 0x3a, 0x3d, 0xf1, 0x35, 0x64, 0x5a, 0x90, 0xfd, 0x48, 0x7b, 0x07, + 0x30, 0xcc, 0x79, 0xbc, 0x60, 0x76, 0x10, 0x1f, 0xdc, 0xb2, 0x91, 0xe5, 0x2f, 0x03, 0xd2, 0xd5, 0xa3, 0xdc, 0x4d, + 0x23, 0xce, 0x4f, 0xaa, 0xc0, 0x89, 0xbf, 0xce, 0x0b, 0x54, 0xc6, 0xe5, 0xe8, 0x69, 0x1d, 0xaf, 0x50, 0xdc, 0x81, + 0x4f, 0xb3, 0x82, 0xc7, 0xf8, 0xf4, 0xe4, 0xc0, 0x3f, 0x2d, 0x87, 0x6f, 0xb4, 0x45, 0x02, 0x81, 0xf2, 0x21, 0x70, + 0x46, 0x51, 0x18, 0x45, 0xc0, 0xc5, 0xe2, 0xc1, 0x8a, 0xa7, 0x53, 0x35, 0xe4, 0xa2, 0x5d, 0xee, 0x9e, 0x44, 0x5a, + 0xb1, 0xa4, 0xe3, 0x25, 0x7d, 0xa9, 0xfe, 0x09, 0xf9, 0x13, 0x92, 0x27, 0xf3, 0xe8, 0x9c, 0xb0, 0xdd, 0x6b, 0xa1, + 0x1b, 0x25, 0xc6, 0x1e, 0x53, 0x25, 0x4e, 0x47, 0xaa, 0x81, 0xc2, 0x37, 0x70, 0x2e, 0x8f, 0x06, 0x03, 0x22, 0x73, + 0x55, 0x6a, 0x07, 0x48, 0x6c, 0x48, 0xa6, 0x00, 0x83, 0xcd, 0xa0, 0xa1, 0x45, 0x2e, 0x74, 0xd8, 0xa8, 0x3a, 0x9c, + 0x7a, 0x1f, 0x0f, 0x7c, 0xb1, 0xfc, 0x4a, 0x0b, 0x14, 0x16, 0x1e, 0x9f, 0x77, 0xa0, 0xef, 0x02, 0x4e, 0x85, 0xcc, + 0x6b, 0x7f, 0x25, 0x8a, 0x6e, 0x85, 0xfe, 0x7d, 0xac, 0x98, 0x36, 0xf0, 0x28, 0x07, 0xe7, 0x58, 0x7a, 0x21, 0xbc, + 0x0b, 0x6b, 0x1b, 0x4d, 0x06, 0xa4, 0xaf, 0x6f, 0x36, 0x35, 0x82, 0xfc, 0xae, 0xbd, 0xa6, 0xd6, 0x2d, 0x0f, 0x06, + 0x89, 0x67, 0x5e, 0xec, 0xc3, 0xd2, 0x4b, 0x24, 0x0b, 0xf9, 0xc9, 0x01, 0x8c, 0x0f, 0x22, 0x33, 0x94, 0x18, 0xa7, + 0xc0, 0x80, 0xf0, 0x0f, 0x7e, 0x4a, 0xe6, 0x19, 0x6f, 0x27, 0x82, 0xe7, 0xc3, 0x8b, 0xa5, 0x8c, 0x0d, 0x5b, 0xaa, + 0x52, 0xe7, 0x65, 0x9c, 0x66, 0x26, 0x70, 0x77, 0x02, 0x87, 0xdf, 0x57, 0x98, 0xbd, 0x21, 0xef, 0x67, 0x4c, 0x38, + 0x3e, 0x9f, 0x67, 0x1b, 0x7c, 0xa3, 0x37, 0x55, 0x21, 0x7e, 0x76, 0x4b, 0x85, 0x62, 0x1d, 0x6f, 0xab, 0x55, 0x70, + 0x4e, 0xb2, 0xda, 0xd2, 0x6f, 0xe9, 0x8f, 0x71, 0xc5, 0xd7, 0x6a, 0x63, 0x29, 0xd4, 0x3b, 0xcf, 0x06, 0x50, 0x55, + 0xc8, 0xe2, 0xfd, 0xe5, 0x92, 0x2a, 0x1b, 0xfd, 0x93, 0x03, 0xba, 0x96, 0x9e, 0xd2, 0x0a, 0x3b, 0x3d, 0x01, 0xf3, + 0x4e, 0x9a, 0x74, 0x7f, 0xb9, 0xe4, 0x53, 0x4a, 0xbf, 0xe8, 0xcd, 0xc1, 0x3c, 0x5b, 0x84, 0xa7, 0xff, 0x07, 0xc0, + 0xb1, 0x34, 0x8b, 0x20, 0x5c, 0x03, 0x00}; } // namespace web_server } // namespace esphome From 08b8ab837a2b6da234691cbd750625eea9fe38c0 Mon Sep 17 00:00:00 2001 From: Colm Date: Thu, 11 Jul 2024 05:10:58 +0100 Subject: [PATCH 0014/1052] Add braces to if statement to avoid compiler warning. (#7036) --- esphome/components/aht10/aht10.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index 332218b9e9..441c1ac9df 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -93,8 +93,9 @@ void AHT10Component::restart_read_() { void AHT10Component::read_data_() { uint8_t data[6]; - if (this->read_count_ > 1) + if (this->read_count_ > 1) { 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->restart_read_(); @@ -119,8 +120,9 @@ void AHT10Component::read_data_() { return; } } - if (this->read_count_ > 1) + 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; From 6e624ff7974311bc22058466f0688d554a340b1f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 10 Jul 2024 23:21:24 -0500 Subject: [PATCH 0015/1052] [wifi] Fix EAP for IDF 5.1+, add test (#7061) --- esphome/components/wifi/wifi_component.h | 4 ++ .../wifi/wifi_component_esp_idf.cpp | 48 ++++++++++++++++--- tests/components/wifi/common-eap.yaml | 7 +++ tests/components/wifi/test-eap.esp32-ard.yaml | 8 +--- tests/components/wifi/test-eap.esp32-idf.yaml | 1 + 5 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 tests/components/wifi/common-eap.yaml create mode 100644 tests/components/wifi/test-eap.esp32-idf.yaml diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 0b077819ae..d79cde0b18 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -20,8 +20,12 @@ #endif #if defined(USE_ESP_IDF) && defined(USE_WIFI_WPA2_EAP) +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) +#include +#else #include #endif +#endif #ifdef USE_ESP8266 #include diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 96fa837767..a8d67ed44d 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -15,8 +15,12 @@ #include #include #ifdef USE_WIFI_WPA2_EAP +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) +#include +#else #include #endif +#endif #ifdef USE_WIFI_AP #include "dhcpserver/dhcpserver.h" @@ -364,48 +368,78 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { if (ap.get_eap().has_value()) { // note: all certificates and keys have to be null terminated. Lengths are appended by +1 to include \0. EAPAuth eap = ap.get_eap().value(); +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) + err = esp_eap_client_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length()); +#else err = esp_wifi_sta_wpa2_ent_set_identity((uint8_t *) eap.identity.c_str(), eap.identity.length()); +#endif if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_identity failed! %d", err); + ESP_LOGV(TAG, "set_identity failed %d", err); } int ca_cert_len = strlen(eap.ca_cert); int client_cert_len = strlen(eap.client_cert); int client_key_len = strlen(eap.client_key); if (ca_cert_len) { +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) + err = esp_eap_client_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1); +#else err = esp_wifi_sta_wpa2_ent_set_ca_cert((uint8_t *) eap.ca_cert, ca_cert_len + 1); +#endif if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ca_cert failed! %d", err); + ESP_LOGV(TAG, "set_ca_cert failed %d", err); } } // workout what type of EAP this is // validation is not required as the config tool has already validated it if (client_cert_len && client_key_len) { // if we have certs, this must be EAP-TLS +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) + err = esp_eap_client_set_certificate_and_key((uint8_t *) eap.client_cert, client_cert_len + 1, + (uint8_t *) eap.client_key, client_key_len + 1, + (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str())); +#else err = esp_wifi_sta_wpa2_ent_set_cert_key((uint8_t *) eap.client_cert, client_cert_len + 1, (uint8_t *) eap.client_key, client_key_len + 1, (uint8_t *) eap.password.c_str(), strlen(eap.password.c_str())); +#endif if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_cert_key failed! %d", err); + ESP_LOGV(TAG, "set_cert_key failed %d", err); } } else { // in the absence of certs, assume this is username/password based +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) + err = esp_eap_client_set_username((uint8_t *) eap.username.c_str(), eap.username.length()); +#else err = esp_wifi_sta_wpa2_ent_set_username((uint8_t *) eap.username.c_str(), eap.username.length()); +#endif if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_username failed! %d", err); + ESP_LOGV(TAG, "set_username failed %d", err); } +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) + err = esp_eap_client_set_password((uint8_t *) eap.password.c_str(), eap.password.length()); +#else err = esp_wifi_sta_wpa2_ent_set_password((uint8_t *) eap.password.c_str(), eap.password.length()); +#endif if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_password failed! %d", err); + ESP_LOGV(TAG, "set_password failed %d", err); } // set TTLS Phase 2, defaults to MSCHAPV2 +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) + err = esp_eap_client_set_ttls_phase2_method(eap.ttls_phase_2); +#else err = esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(eap.ttls_phase_2); +#endif if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_set_ttls_phase2_method failed! %d", err); + ESP_LOGV(TAG, "set_ttls_phase2_method failed %d", err); } } +#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1) + err = esp_wifi_sta_enterprise_enable(); +#else err = esp_wifi_sta_wpa2_ent_enable(); +#endif if (err != ESP_OK) { - ESP_LOGV(TAG, "esp_wifi_sta_wpa2_ent_enable failed! %d", err); + ESP_LOGV(TAG, "enterprise_enable failed %d", err); } } #endif // USE_WIFI_WPA2_EAP diff --git a/tests/components/wifi/common-eap.yaml b/tests/components/wifi/common-eap.yaml new file mode 100644 index 0000000000..779cd6b49a --- /dev/null +++ b/tests/components/wifi/common-eap.yaml @@ -0,0 +1,7 @@ +wifi: + networks: + - ssid: MySSID + eap: + username: username + password: password + identity: identity diff --git a/tests/components/wifi/test-eap.esp32-ard.yaml b/tests/components/wifi/test-eap.esp32-ard.yaml index 779cd6b49a..9177e5de10 100644 --- a/tests/components/wifi/test-eap.esp32-ard.yaml +++ b/tests/components/wifi/test-eap.esp32-ard.yaml @@ -1,7 +1 @@ -wifi: - networks: - - ssid: MySSID - eap: - username: username - password: password - identity: identity +<<: !include common-eap.yaml diff --git a/tests/components/wifi/test-eap.esp32-idf.yaml b/tests/components/wifi/test-eap.esp32-idf.yaml new file mode 100644 index 0000000000..9177e5de10 --- /dev/null +++ b/tests/components/wifi/test-eap.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common-eap.yaml From 5ac875545fe14fa1d818336efb392f48f61b3fc1 Mon Sep 17 00:00:00 2001 From: ttaborda <80131527+ttaborda@users.noreply.github.com> Date: Thu, 11 Jul 2024 05:26:37 +0100 Subject: [PATCH 0016/1052] Update mitsubishi.cpp (#6909) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mitsubishi/mitsubishi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/mitsubishi/mitsubishi.cpp b/esphome/components/mitsubishi/mitsubishi.cpp index fd57adc586..a02aabf14d 100644 --- a/esphome/components/mitsubishi/mitsubishi.cpp +++ b/esphome/components/mitsubishi/mitsubishi.cpp @@ -52,6 +52,7 @@ const uint8_t MITSUBISHI_BYTE16 = 0X00; climate::ClimateTraits MitsubishiClimate::traits() { auto traits = climate::ClimateTraits(); + traits.set_supports_current_temperature(this->sensor_ != nullptr); traits.set_supports_action(false); traits.set_visual_min_temperature(MITSUBISHI_TEMP_MIN); traits.set_visual_max_temperature(MITSUBISHI_TEMP_MAX); From 66b36afe90431b89b8e7c26c782be2431f2e7890 Mon Sep 17 00:00:00 2001 From: Sergey Dudanov Date: Thu, 11 Jul 2024 08:23:29 +0300 Subject: [PATCH 0017/1052] [climate] fix dump output of unsupported features (#7005) --- esphome/components/climate/climate.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 1822707152..bc8d932089 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -574,21 +574,25 @@ void Climate::dump_traits_(const char *tag) { 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, " Current: %.1f", traits.get_visual_current_temperature_step()); - ESP_LOGCONFIG(tag, " - Min humidity: %.0f", traits.get_visual_min_humidity()); - ESP_LOGCONFIG(tag, " - Max humidity: %.0f", traits.get_visual_max_humidity()); if (traits.get_supports_current_temperature()) { - ESP_LOGCONFIG(tag, " [x] Supports current temperature"); + ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); } - if (traits.get_supports_current_humidity()) { - ESP_LOGCONFIG(tag, " [x] Supports current humidity"); + 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()); } if (traits.get_supports_two_point_target_temperature()) { ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); } + if (traits.get_supports_current_temperature()) { + ESP_LOGCONFIG(tag, " [x] Supports current temperature"); + } if (traits.get_supports_target_humidity()) { ESP_LOGCONFIG(tag, " [x] Supports target humidity"); } + if (traits.get_supports_current_humidity()) { + ESP_LOGCONFIG(tag, " [x] Supports current humidity"); + } if (traits.get_supports_action()) { ESP_LOGCONFIG(tag, " [x] Supports action"); } From d071b05249fc4d4310fda64ed8227896bb3f51d6 Mon Sep 17 00:00:00 2001 From: Sergey Dudanov Date: Thu, 11 Jul 2024 08:24:36 +0300 Subject: [PATCH 0018/1052] [climate-traits] improved performance (#7006) --- esphome/components/climate/climate_traits.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index fd5b025a03..58d7b586d7 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -73,7 +73,7 @@ class ClimateTraits { ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); } bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); } - std::set get_supported_modes() const { return supported_modes_; } + const std::set &get_supported_modes() const { return supported_modes_; } void set_supports_action(bool supports_action) { supports_action_ = supports_action; } bool get_supports_action() const { return supports_action_; } @@ -101,7 +101,7 @@ class ClimateTraits { void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); } bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); } bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); } - std::set get_supported_fan_modes() const { return supported_fan_modes_; } + const std::set &get_supported_fan_modes() const { return supported_fan_modes_; } void set_supported_custom_fan_modes(std::set supported_custom_fan_modes) { supported_custom_fan_modes_ = std::move(supported_custom_fan_modes); @@ -140,7 +140,7 @@ class ClimateTraits { } bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); } bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); } - std::set get_supported_swing_modes() const { return supported_swing_modes_; } + const std::set &get_supported_swing_modes() const { return supported_swing_modes_; } float get_visual_min_temperature() const { return visual_min_temperature_; } void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; } From d209a2b45acff1e975d7ab68af5b75959624284e Mon Sep 17 00:00:00 2001 From: leejoow Date: Thu, 11 Jul 2024 22:20:58 +0200 Subject: [PATCH 0019/1052] Add default icon to restart button (#7076) Co-authored-by: Leo Schelvis --- esphome/components/restart/button/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/restart/button/__init__.py b/esphome/components/restart/button/__init__.py index 1b2c991261..6aff8cb351 100644 --- a/esphome/components/restart/button/__init__.py +++ b/esphome/components/restart/button/__init__.py @@ -5,6 +5,7 @@ from esphome.const import ( CONF_ID, DEVICE_CLASS_RESTART, ENTITY_CATEGORY_CONFIG, + ICON_RESTART, ) restart_ns = cg.esphome_ns.namespace("restart") @@ -12,6 +13,7 @@ RestartButton = restart_ns.class_("RestartButton", button.Button, cg.Component) CONFIG_SCHEMA = button.button_schema( RestartButton, + icon=ICON_RESTART, device_class=DEVICE_CLASS_RESTART, entity_category=ENTITY_CATEGORY_CONFIG, ).extend(cv.COMPONENT_SCHEMA) From 2e8a2fdbd4677b9075ae9bb572b4d66b8fc67794 Mon Sep 17 00:00:00 2001 From: Tomi Junnila Date: Thu, 11 Jul 2024 23:32:38 +0300 Subject: [PATCH 0020/1052] Add support for the Gree YAC1FB9 in climate_ir (#7056) --- esphome/components/gree/climate.py | 1 + esphome/components/gree/gree.cpp | 14 +++++++++++--- esphome/components/gree/gree.h | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/esphome/components/gree/climate.py b/esphome/components/gree/climate.py index 02ce7b12d4..c88a428391 100644 --- a/esphome/components/gree/climate.py +++ b/esphome/components/gree/climate.py @@ -16,6 +16,7 @@ MODELS = { "yan": Model.GREE_YAN, "yaa": Model.GREE_YAA, "yac": Model.GREE_YAC, + "yac1fb9": Model.GREE_YAC1FB9, } CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( diff --git a/esphome/components/gree/gree.cpp b/esphome/components/gree/gree.cpp index 1bbb443fce..cce2a8ffee 100644 --- a/esphome/components/gree/gree.cpp +++ b/esphome/components/gree/gree.cpp @@ -24,7 +24,7 @@ void GreeClimate::transmit_state() { remote_state[4] |= (this->horizontal_swing_() << 4); } - if (this->model_ == GREE_YAA || this->model_ == GREE_YAC) { + if (this->model_ == GREE_YAA || this->model_ == GREE_YAC || this->model_ == GREE_YAC1FB9) { remote_state[2] = 0x20; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN remote_state[3] = 0x50; // bits 4..7 always 0101 remote_state[6] = 0x20; // YAA1FB, FAA1FB1, YB1F2 bits 4..7 always 0010 @@ -53,7 +53,11 @@ void GreeClimate::transmit_state() { data->set_carrier_frequency(GREE_IR_FREQUENCY); data->mark(GREE_HEADER_MARK); - data->space(GREE_HEADER_SPACE); + if (this->model_ == GREE_YAC1FB9) { + data->space(GREE_YAC1FB9_HEADER_SPACE); + } else { + data->space(GREE_HEADER_SPACE); + } for (int i = 0; i < 4; i++) { for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask @@ -71,7 +75,11 @@ void GreeClimate::transmit_state() { data->space(GREE_ZERO_SPACE); data->mark(GREE_BIT_MARK); - data->space(GREE_MESSAGE_SPACE); + if (this->model_ == GREE_YAC1FB9) { + data->space(GREE_YAC1FB9_MESSAGE_SPACE); + } else { + data->space(GREE_MESSAGE_SPACE); + } for (int i = 4; i < 8; i++) { for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask diff --git a/esphome/components/gree/gree.h b/esphome/components/gree/gree.h index e7131a2b89..524a95aebd 100644 --- a/esphome/components/gree/gree.h +++ b/esphome/components/gree/gree.h @@ -41,6 +41,10 @@ const uint32_t GREE_YAC_HEADER_MARK = 6000; const uint32_t GREE_YAC_HEADER_SPACE = 3000; const uint32_t GREE_YAC_BIT_MARK = 650; +// Timing specific to YAC1FB9 +const uint32_t GREE_YAC1FB9_HEADER_SPACE = 4500; +const uint32_t GREE_YAC1FB9_MESSAGE_SPACE = 19980; + // State Frame size const uint8_t GREE_STATE_FRAME_SIZE = 8; @@ -67,7 +71,7 @@ const uint8_t GREE_HDIR_MRIGHT = 0x05; const uint8_t GREE_HDIR_RIGHT = 0x06; // Model codes -enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC }; +enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC, GREE_YAC1FB9 }; class GreeClimate : public climate_ir::ClimateIR { public: From 99cba0ae7fa0e9897c7f0dfb764a329ba6d431f4 Mon Sep 17 00:00:00 2001 From: Eugen Date: Thu, 11 Jul 2024 23:26:04 +0200 Subject: [PATCH 0021/1052] add ESP32-C6 support to esp32_can (#7063) --- esphome/components/esp32_can/canbus.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/esp32_can/canbus.py b/esphome/components/esp32_can/canbus.py index 74f331f30b..f4ba032009 100644 --- a/esphome/components/esp32_can/canbus.py +++ b/esphome/components/esp32_can/canbus.py @@ -11,6 +11,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, VARIANT_ESP32C3, + VARIANT_ESP32C6, VARIANT_ESP32H2, ) @@ -47,6 +48,7 @@ CAN_SPEEDS_ESP32_S2 = { CAN_SPEEDS_ESP32_S3 = {**CAN_SPEEDS_ESP32_S2} CAN_SPEEDS_ESP32_C3 = {**CAN_SPEEDS_ESP32_S2} +CAN_SPEEDS_ESP32_C6 = {**CAN_SPEEDS_ESP32_S2} CAN_SPEEDS_ESP32_H2 = {**CAN_SPEEDS_ESP32_S2} CAN_SPEEDS = { @@ -54,6 +56,7 @@ CAN_SPEEDS = { VARIANT_ESP32S2: CAN_SPEEDS_ESP32_S2, VARIANT_ESP32S3: CAN_SPEEDS_ESP32_S3, VARIANT_ESP32C3: CAN_SPEEDS_ESP32_C3, + VARIANT_ESP32C6: CAN_SPEEDS_ESP32_C6, VARIANT_ESP32H2: CAN_SPEEDS_ESP32_H2, } From 7f83bcfdd91aba4fed05d0c1918c2899dac1682e Mon Sep 17 00:00:00 2001 From: soeffi Date: Thu, 11 Jul 2024 23:30:45 +0200 Subject: [PATCH 0022/1052] jsn_sr04t component: AJ_SR04M compatibility mode in checksum calculation (#7044) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/jsn_sr04t/jsn_sr04t.cpp | 19 ++++++++++++++++++- esphome/components/jsn_sr04t/jsn_sr04t.h | 8 +++++++- esphome/components/jsn_sr04t/sensor.py | 13 +++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/esphome/components/jsn_sr04t/jsn_sr04t.cpp b/esphome/components/jsn_sr04t/jsn_sr04t.cpp index b96bf8f762..077d4e58ea 100644 --- a/esphome/components/jsn_sr04t/jsn_sr04t.cpp +++ b/esphome/components/jsn_sr04t/jsn_sr04t.cpp @@ -31,7 +31,16 @@ void Jsnsr04tComponent::loop() { } void Jsnsr04tComponent::check_buffer_() { - uint8_t checksum = this->buffer_[0] + this->buffer_[1] + this->buffer_[2]; + uint8_t checksum = 0; + switch (this->model_) { + case JSN_SR04T: + checksum = this->buffer_[0] + this->buffer_[1] + this->buffer_[2]; + break; + case AJ_SR04M: + checksum = this->buffer_[1] + this->buffer_[2]; + break; + } + if (this->buffer_[3] == checksum) { uint16_t distance = encode_uint16(this->buffer_[1], this->buffer_[2]); if (distance > 250) { @@ -49,6 +58,14 @@ void Jsnsr04tComponent::check_buffer_() { void Jsnsr04tComponent::dump_config() { LOG_SENSOR("", "JST_SR04T Sensor", this); + switch (this->model_) { + case JSN_SR04T: + ESP_LOGCONFIG(TAG, " sensor model: jsn_sr04t"); + break; + case AJ_SR04M: + ESP_LOGCONFIG(TAG, " sensor model: aj_sr04m"); + break; + } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/jsn_sr04t/jsn_sr04t.h b/esphome/components/jsn_sr04t/jsn_sr04t.h index bd43252be8..2a22ff92ec 100644 --- a/esphome/components/jsn_sr04t/jsn_sr04t.h +++ b/esphome/components/jsn_sr04t/jsn_sr04t.h @@ -9,9 +9,14 @@ namespace esphome { namespace jsn_sr04t { +enum Model { + JSN_SR04T, + AJ_SR04M, +}; + class Jsnsr04tComponent : public sensor::Sensor, public PollingComponent, public uart::UARTDevice { public: - // Nothing really public. + void set_model(Model model) { this->model_ = model; } // ========== INTERNAL METHODS ========== void update() override; @@ -20,6 +25,7 @@ class Jsnsr04tComponent : public sensor::Sensor, public PollingComponent, public protected: void check_buffer_(); + Model model_; std::vector buffer_; }; diff --git a/esphome/components/jsn_sr04t/sensor.py b/esphome/components/jsn_sr04t/sensor.py index 4b062e81e9..682cf06570 100644 --- a/esphome/components/jsn_sr04t/sensor.py +++ b/esphome/components/jsn_sr04t/sensor.py @@ -5,6 +5,7 @@ from esphome.const import ( STATE_CLASS_MEASUREMENT, UNIT_METER, ICON_ARROW_EXPAND_VERTICAL, + CONF_MODEL, ) CODEOWNERS = ["@Mafus1"] @@ -14,6 +15,11 @@ jsn_sr04t_ns = cg.esphome_ns.namespace("jsn_sr04t") Jsnsr04tComponent = jsn_sr04t_ns.class_( "Jsnsr04tComponent", sensor.Sensor, cg.PollingComponent, uart.UARTDevice ) +Model = jsn_sr04t_ns.enum("Model") +MODEL = { + "jsn_sr04t": Model.JSN_SR04T, + "aj_sr04m": Model.AJ_SR04M, +} CONFIG_SCHEMA = ( sensor.sensor_schema( @@ -25,6 +31,11 @@ CONFIG_SCHEMA = ( ) .extend(cv.polling_component_schema("60s")) .extend(uart.UART_DEVICE_SCHEMA) + .extend( + { + cv.Optional(CONF_MODEL, default="jsn_sr04t"): cv.enum(MODEL, upper=False), + } + ) ) FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( @@ -42,3 +53,5 @@ async def to_code(config): var = await sensor.new_sensor(config) await cg.register_component(var, config) await uart.register_uart_device(var, config) + + cg.add(var.set_model(config[CONF_MODEL])) From 4a80a09db3c3a871f2825a2b7b7f1621ffab0f8b Mon Sep 17 00:00:00 2001 From: kevdliu <1766838+kevdliu@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:32:31 -0400 Subject: [PATCH 0023/1052] Fix voice assistant crash when no speaker configured (#7075) --- esphome/components/voice_assistant/voice_assistant.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 8a8a9e92aa..e4f388db68 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -684,7 +684,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { this->defer([this, text]() { this->tts_start_trigger_->trigger(text); #ifdef USE_SPEAKER - this->speaker_->start(); + if (this->speaker_ != nullptr) { + this->speaker_->start(); + } #endif }); break; From 8a3f0e3b93878a24a6cc9cdbe9babb846243c522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Fri, 12 Jul 2024 23:19:33 +0200 Subject: [PATCH 0024/1052] Bump HeatpumpIR, add protocols, remove IRremoteESP8266 (#6996) --- esphome/components/heatpumpir/climate.py | 11 ++++++----- esphome/components/heatpumpir/heatpumpir.cpp | 5 +++++ esphome/components/heatpumpir/heatpumpir.h | 5 +++++ platformio.ini | 6 +++--- .../heatpumpir/test.bk72xx-ard.yaml | 19 +++++++++++++++++++ 5 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 tests/components/heatpumpir/test.bk72xx-ard.yaml diff --git a/esphome/components/heatpumpir/climate.py b/esphome/components/heatpumpir/climate.py index b86d405b7e..80900d7db9 100644 --- a/esphome/components/heatpumpir/climate.py +++ b/esphome/components/heatpumpir/climate.py @@ -8,7 +8,6 @@ from esphome.const import ( CONF_PROTOCOL, CONF_VISUAL, ) -from esphome.core import CORE CODEOWNERS = ["@rob-deutsch"] @@ -67,6 +66,11 @@ PROTOCOLS = { "carrier_qlima_2": Protocol.PROTOCOL_QLIMA_2, "samsung_aqv12msan": Protocol.PROTOCOL_SAMSUNG_AQV12MSAN, "zhjg01": Protocol.PROTOCOL_ZHJG01, + "airway": Protocol.PROTOCOL_AIRWAY, + "bgh_aud": Protocol.PROTOCOL_BGH_AUD, + "panasonic_altdke": Protocol.PROTOCOL_PANASONIC_ALTDKE, + "vaillantvai8": Protocol.PROTOCOL_VAILLANTVAI8, + "r51m": Protocol.PROTOCOL_R51M, } CONF_HORIZONTAL_DEFAULT = "horizontal_default" @@ -122,7 +126,4 @@ def to_code(config): cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE])) cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) - cg.add_library("tonia/HeatpumpIR", "1.0.26") - - if CORE.is_esp8266 or CORE.is_esp32: - cg.add_library("crankyoldgit/IRremoteESP8266", "2.8.6") + cg.add_library("tonia/HeatpumpIR", "1.0.27") diff --git a/esphome/components/heatpumpir/heatpumpir.cpp b/esphome/components/heatpumpir/heatpumpir.cpp index 22a5779c8d..144dcc9bfa 100644 --- a/esphome/components/heatpumpir/heatpumpir.cpp +++ b/esphome/components/heatpumpir/heatpumpir.cpp @@ -61,6 +61,11 @@ const std::map> PROTOCOL_CONSTRUCTOR_MAP {PROTOCOL_QLIMA_2, []() { return new Qlima2HeatpumpIR(); }}, // NOLINT {PROTOCOL_SAMSUNG_AQV12MSAN, []() { return new SamsungAQV12MSANHeatpumpIR(); }}, // NOLINT {PROTOCOL_ZHJG01, []() { return new ZHJG01HeatpumpIR(); }}, // NOLINT + {PROTOCOL_AIRWAY, []() { return new AIRWAYHeatpumpIR(); }}, // NOLINT + {PROTOCOL_BGH_AUD, []() { return new BGHHeatpumpIR(); }}, // NOLINT + {PROTOCOL_PANASONIC_ALTDKE, []() { return new PanasonicAltDKEHeatpumpIR(); }}, // NOLINT + {PROTOCOL_VAILLANTVAI8, []() { return new VaillantHeatpumpIR(); }}, // NOLINT + {PROTOCOL_R51M, []() { return new R51MHeatpumpIR(); }}, // NOLINT }; void HeatpumpIRClimate::setup() { diff --git a/esphome/components/heatpumpir/heatpumpir.h b/esphome/components/heatpumpir/heatpumpir.h index 0e6ea2218f..f6e7ff3cd6 100644 --- a/esphome/components/heatpumpir/heatpumpir.h +++ b/esphome/components/heatpumpir/heatpumpir.h @@ -61,6 +61,11 @@ enum Protocol { PROTOCOL_QLIMA_2, PROTOCOL_SAMSUNG_AQV12MSAN, PROTOCOL_ZHJG01, + PROTOCOL_AIRWAY, + PROTOCOL_BGH_AUD, + PROTOCOL_PANASONIC_ALTDKE, + PROTOCOL_VAILLANTVAI8, + PROTOCOL_R51M, }; // Simple enum to represent horizontal directios diff --git a/platformio.ini b/platformio.ini index f07889526f..fc7f35b6c3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -65,7 +65,7 @@ lib_deps = glmnet/Dsmr@0.7 ; dsmr rweather/Crypto@0.4.0 ; dsmr dudanov/MideaUART@1.1.9 ; midea - tonia/HeatpumpIR@1.0.26 ; heatpumpir + tonia/HeatpumpIR@1.0.27 ; heatpumpir build_flags = ${common.build_flags} -DUSE_ARDUINO @@ -93,8 +93,8 @@ lib_deps = ESP8266HTTPClient ; http_request (Arduino built-in) ESP8266mDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) - crankyoldgit/IRremoteESP8266@2.8.6 ; heatpumpir droscy/esp_wireguard@0.4.2 ; wireguard + build_flags = ${common:arduino.build_flags} -Wno-nonnull-compare @@ -123,8 +123,8 @@ lib_deps = ESPmDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) esphome/ESP32-audioI2S@2.0.7 ; i2s_audio - crankyoldgit/IRremoteESP8266@2.8.6 ; heatpumpir droscy/esp_wireguard@0.4.2 ; wireguard + build_flags = ${common:arduino.build_flags} -DUSE_ESP32 diff --git a/tests/components/heatpumpir/test.bk72xx-ard.yaml b/tests/components/heatpumpir/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..90259f1244 --- /dev/null +++ b/tests/components/heatpumpir/test.bk72xx-ard.yaml @@ -0,0 +1,19 @@ +remote_transmitter: + pin: 6 + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: daikin + horizontal_default: mleft + vertical_default: mup + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 + - platform: heatpumpir + protocol: panasonic_altdke + horizontal_default: mright + vertical_default: mdown + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 From feae794787ca3ad0cc1b20be9496a73117e6844f Mon Sep 17 00:00:00 2001 From: Anton Viktorov Date: Fri, 12 Jul 2024 21:42:41 +0000 Subject: [PATCH 0025/1052] LTR390 separate ALS and UV gain and resolution (#7026) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 2 +- esphome/components/ltr390/ltr390.cpp | 73 +++++++++++-------- esphome/components/ltr390/ltr390.h | 14 ++-- esphome/components/ltr390/sensor.py | 40 ++++++++-- tests/components/ltr390/test.esp32-ard.yaml | 18 +++++ tests/components/ltr390/test.esp8266-ard.yaml | 4 +- 6 files changed, 110 insertions(+), 41 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 5c14d30371..210c567f78 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -214,7 +214,7 @@ esphome/components/lightwaverf/* @max246 esphome/components/lilygo_t5_47/touchscreen/* @jesserockz esphome/components/lock/* @esphome/core esphome/components/logger/* @esphome/core -esphome/components/ltr390/* @sjtrny +esphome/components/ltr390/* @latonita @sjtrny esphome/components/ltr_als_ps/* @latonita esphome/components/matrix_keypad/* @ssieb esphome/components/max31865/* @DAVe3283 diff --git a/esphome/components/ltr390/ltr390.cpp b/esphome/components/ltr390/ltr390.cpp index 4eb1ff2c46..198d15ebd8 100644 --- a/esphome/components/ltr390/ltr390.cpp +++ b/esphome/components/ltr390/ltr390.cpp @@ -19,6 +19,7 @@ static const uint8_t LTR390_MAIN_STATUS = 0x07; static const float GAINVALUES[5] = {1.0, 3.0, 6.0, 9.0, 18.0}; static const float RESOLUTIONVALUE[6] = {4.0, 2.0, 1.0, 0.5, 0.25, 0.125}; +static const uint8_t RESOLUTION_BITS[6] = {20, 19, 18, 17, 16, 13}; // Request fastest measurement rate - will be slowed by device if conversion rate is slower. static const float RESOLUTION_SETTING[6] = {0x00, 0x10, 0x20, 0x30, 0x40, 0x50}; @@ -74,7 +75,7 @@ void LTR390Component::read_als_() { uint32_t als = *val; if (this->light_sensor_ != nullptr) { - float lux = ((0.6 * als) / (GAINVALUES[this->gain_] * RESOLUTIONVALUE[this->res_])) * this->wfac_; + float lux = ((0.6 * als) / (GAINVALUES[this->gain_als_] * RESOLUTIONVALUE[this->res_als_])) * this->wfac_; this->light_sensor_->publish_state(lux); } @@ -90,7 +91,7 @@ void LTR390Component::read_uvs_() { uint32_t uv = *val; if (this->uvi_sensor_ != nullptr) { - this->uvi_sensor_->publish_state((uv / this->sensitivity_) * this->wfac_); + this->uvi_sensor_->publish_state((uv / this->sensitivity_uv_) * this->wfac_); } if (this->uv_sensor_ != nullptr) { @@ -107,24 +108,38 @@ void LTR390Component::read_mode_(int mode_index) { ctrl[LTR390_CTRL_EN] = true; this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); - // After the sensor integration time do the following - this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100 + LTR390_WAKEUP_TIME + LTR390_SETTLE_TIME, - [this, mode_index]() { - // Read from the sensor - std::get<1>(this->mode_funcs_[mode_index])(); + uint32_t int_time{0}; + // Set gain, resolution and measurement rate + switch (mode) { + case LTR390_MODE_ALS: + this->reg(LTR390_GAIN) = this->gain_als_; + this->reg(LTR390_MEAS_RATE) = RESOLUTION_SETTING[this->res_als_]; + int_time = ((uint32_t) RESOLUTIONVALUE[this->res_als_]) * 100; + break; + case LTR390_MODE_UVS: + this->reg(LTR390_GAIN) = this->gain_uv_; + this->reg(LTR390_MEAS_RATE) = RESOLUTION_SETTING[this->res_uv_]; + int_time = ((uint32_t) RESOLUTIONVALUE[this->res_uv_]) * 100; + break; + } - // If there are more modes to read then begin the next - // otherwise stop - if (mode_index + 1 < (int) this->mode_funcs_.size()) { - this->read_mode_(mode_index + 1); - } else { - // put sensor in standby - std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); - ctrl[LTR390_CTRL_EN] = false; - this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); - this->reading_ = false; - } - }); + // After the sensor integration time do the following + this->set_timeout(int_time + LTR390_WAKEUP_TIME + LTR390_SETTLE_TIME, [this, mode_index]() { + // Read from the sensor + std::get<1>(this->mode_funcs_[mode_index])(); + + // If there are more modes to read then begin the next + // otherwise stop + if (mode_index + 1 < (int) this->mode_funcs_.size()) { + this->read_mode_(mode_index + 1); + } else { + // put sensor in standby + std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_EN] = false; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); + this->reading_ = false; + } + }); } void LTR390Component::setup() { @@ -151,16 +166,10 @@ void LTR390Component::setup() { return; } - // Set gain - this->reg(LTR390_GAIN) = gain_; - - // Set resolution and measurement rate - this->reg(LTR390_MEAS_RATE) = RESOLUTION_SETTING[this->res_]; - // Set sensitivity by linearly scaling against known value in the datasheet - float gain_scale = GAINVALUES[this->gain_] / GAIN_MAX; - float intg_scale = (RESOLUTIONVALUE[this->res_] * 100) / INTG_MAX; - this->sensitivity_ = SENSITIVITY_MAX * gain_scale * intg_scale; + float gain_scale_uv = GAINVALUES[this->gain_uv_] / GAIN_MAX; + float intg_scale_uv = (RESOLUTIONVALUE[this->res_uv_] * 100) / INTG_MAX; + this->sensitivity_uv_ = SENSITIVITY_MAX * gain_scale_uv * intg_scale_uv; // Set sensor read state this->reading_ = false; @@ -176,7 +185,13 @@ void LTR390Component::setup() { } } -void LTR390Component::dump_config() { LOG_I2C_DEVICE(this); } +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_]); +} void LTR390Component::update() { if (!this->reading_ && !mode_funcs_.empty()) { diff --git a/esphome/components/ltr390/ltr390.h b/esphome/components/ltr390/ltr390.h index bc98518fe9..24afd3c411 100644 --- a/esphome/components/ltr390/ltr390.h +++ b/esphome/components/ltr390/ltr390.h @@ -49,8 +49,10 @@ class LTR390Component : public PollingComponent, public i2c::I2CDevice { void dump_config() override; void update() override; - void set_gain_value(LTR390GAIN gain) { this->gain_ = gain; } - void set_res_value(LTR390RESOLUTION res) { this->res_ = res; } + void set_als_gain_value(LTR390GAIN gain) { this->gain_als_ = gain; } + void set_uv_gain_value(LTR390GAIN gain) { this->gain_uv_ = gain; } + void set_als_res_value(LTR390RESOLUTION res) { this->res_als_ = res; } + void set_uv_res_value(LTR390RESOLUTION res) { this->res_uv_ = res; } void set_wfac_value(float wfac) { this->wfac_ = wfac; } void set_light_sensor(sensor::Sensor *light_sensor) { this->light_sensor_ = light_sensor; } @@ -71,9 +73,11 @@ class LTR390Component : public PollingComponent, public i2c::I2CDevice { // a list of modes and corresponding read functions std::vector>> mode_funcs_; - LTR390GAIN gain_; - LTR390RESOLUTION res_; - float sensitivity_; + LTR390GAIN gain_als_; + LTR390GAIN gain_uv_; + LTR390RESOLUTION res_als_; + LTR390RESOLUTION res_uv_; + float sensitivity_uv_; float wfac_; sensor::Sensor *light_sensor_{nullptr}; diff --git a/esphome/components/ltr390/sensor.py b/esphome/components/ltr390/sensor.py index 8b2676599c..62c3edf8cb 100644 --- a/esphome/components/ltr390/sensor.py +++ b/esphome/components/ltr390/sensor.py @@ -13,7 +13,7 @@ from esphome.const import ( UNIT_LUX, ) -CODEOWNERS = ["@sjtrny"] +CODEOWNERS = ["@sjtrny", "@latonita"] DEPENDENCIES = ["i2c"] ltr390_ns = cg.esphome_ns.namespace("ltr390") @@ -76,8 +76,24 @@ CONFIG_SCHEMA = cv.All( accuracy_decimals=1, device_class=DEVICE_CLASS_EMPTY, ), - cv.Optional(CONF_GAIN, default="X18"): cv.enum(GAIN_OPTIONS), - cv.Optional(CONF_RESOLUTION, default=20): cv.enum(RES_OPTIONS), + cv.Optional(CONF_GAIN, default="X18"): cv.Any( + cv.enum(GAIN_OPTIONS), + cv.Schema( + { + cv.Required(CONF_AMBIENT_LIGHT): cv.enum(GAIN_OPTIONS), + cv.Required(CONF_UV): cv.enum(GAIN_OPTIONS), + } + ), + ), + cv.Optional(CONF_RESOLUTION, default=20): cv.Any( + cv.enum(RES_OPTIONS), + cv.Schema( + { + cv.Required(CONF_AMBIENT_LIGHT): cv.enum(RES_OPTIONS), + cv.Required(CONF_UV): cv.enum(RES_OPTIONS), + } + ), + ), cv.Optional(CONF_WINDOW_CORRECTION_FACTOR, default=1.0): cv.float_range( min=1.0 ), @@ -101,11 +117,25 @@ async def to_code(config): await cg.register_component(var, config) await i2c.register_i2c_device(var, config) - cg.add(var.set_gain_value(config[CONF_GAIN])) - cg.add(var.set_res_value(config[CONF_RESOLUTION])) cg.add(var.set_wfac_value(config[CONF_WINDOW_CORRECTION_FACTOR])) for key, funcName in TYPES.items(): if key in config: sens = await sensor.new_sensor(config[key]) cg.add(getattr(var, funcName)(sens)) + + gain_value = config[CONF_GAIN] + if isinstance(gain_value, dict): + cg.add(var.set_als_gain_value(gain_value[CONF_AMBIENT_LIGHT])) + cg.add(var.set_uv_gain_value(gain_value[CONF_UV])) + else: + cg.add(var.set_als_gain_value(gain_value)) + cg.add(var.set_uv_gain_value(gain_value)) + + res_value = config[CONF_RESOLUTION] + if isinstance(res_value, dict): + cg.add(var.set_als_res_value(res_value[CONF_AMBIENT_LIGHT])) + cg.add(var.set_uv_res_value(res_value[CONF_UV])) + else: + cg.add(var.set_als_res_value(res_value)) + cg.add(var.set_uv_res_value(res_value)) diff --git a/tests/components/ltr390/test.esp32-ard.yaml b/tests/components/ltr390/test.esp32-ard.yaml index 9786c7dac3..bdfe349b77 100644 --- a/tests/components/ltr390/test.esp32-ard.yaml +++ b/tests/components/ltr390/test.esp32-ard.yaml @@ -18,3 +18,21 @@ sensor: window_correction_factor: 1.0 address: 0x53 update_interval: 60s + - platform: ltr390 + uv: + name: LTR390 UV + uv_index: + name: LTR390 UVI + light: + name: LTR390 Light + ambient_light: + name: LTR390 ALS + gain: + ambient_light: X9 + uv: X3 + resolution: + ambient_light: 18 + uv: 13 + window_correction_factor: 1.0 + address: 0x53 + update_interval: 60s diff --git a/tests/components/ltr390/test.esp8266-ard.yaml b/tests/components/ltr390/test.esp8266-ard.yaml index fee0f37ce1..149f46f9c8 100644 --- a/tests/components/ltr390/test.esp8266-ard.yaml +++ b/tests/components/ltr390/test.esp8266-ard.yaml @@ -13,7 +13,9 @@ sensor: name: LTR390 Light ambient_light: name: LTR390 ALS - gain: X3 + gain: + ambient_light: X9 + uv: X3 resolution: 18 window_correction_factor: 1.0 address: 0x53 From d1bfad98906433899d529875f9b0613d5fc6c9c0 Mon Sep 17 00:00:00 2001 From: Christian Ferbar <5595808+ferbar@users.noreply.github.com> Date: Thu, 11 Jul 2024 05:58:54 +0200 Subject: [PATCH 0026/1052] helpers.cpp: Fix GLIBCXX_RELEASE check < 8 (#7062) --- esphome/core/helpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 7f040f855f..e75b06ccd3 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -78,7 +78,7 @@ static const uint16_t CRC16_1021_BE_LUT_H[] = {0x0000, 0x1231, 0x2462, 0x3653, 0 // STL backports -#if _GLIBCXX_RELEASE < 7 +#if _GLIBCXX_RELEASE < 8 std::string to_string(int value) { return str_snprintf("%d", 32, value); } // NOLINT std::string to_string(long value) { return str_snprintf("%ld", 32, value); } // NOLINT std::string to_string(long long value) { return str_snprintf("%lld", 32, value); } // NOLINT From 114476d8b15f173aac926ec653c69fccc2dccdc7 Mon Sep 17 00:00:00 2001 From: Z3LIFF Date: Thu, 11 Jul 2024 00:01:14 -0400 Subject: [PATCH 0027/1052] Fix pmsa003i cold boot marked as failed on ESP32 et al (#7064) --- esphome/components/pmsa003i/pmsa003i.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/esphome/components/pmsa003i/pmsa003i.cpp b/esphome/components/pmsa003i/pmsa003i.cpp index ca3d28367a..a9665c6a5a 100644 --- a/esphome/components/pmsa003i/pmsa003i.cpp +++ b/esphome/components/pmsa003i/pmsa003i.cpp @@ -13,6 +13,15 @@ void PMSA003IComponent::setup() { PM25AQIData data; bool successful_read = this->read_data_(&data); + if (!successful_read) { + for (int i = 0; i < 3; i++) { + successful_read = this->read_data_(&data); + if (successful_read) { + break; + } + } + } + if (!successful_read) { this->mark_failed(); return; From 8d28c53fd3e72120fcc8bd13759e67ee4892e91f Mon Sep 17 00:00:00 2001 From: guillempages Date: Thu, 11 Jul 2024 06:08:51 +0200 Subject: [PATCH 0028/1052] [http_request] Fix follow_redirects on arduino (#7054) --- esphome/components/http_request/http_request_arduino.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index 248a85a439..95b1cdc38e 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -32,6 +32,13 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s watchdog::WatchdogManager wdm(this->get_watchdog_timeout()); + if (this->follow_redirects_) { + container->client_.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); + container->client_.setRedirectLimit(this->redirect_limit_); + } else { + container->client_.setFollowRedirects(HTTPC_DISABLE_FOLLOW_REDIRECTS); + } + #if defined(USE_ESP8266) std::unique_ptr stream_ptr; #ifdef USE_HTTP_REQUEST_ESP8266_HTTPS @@ -59,8 +66,6 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s "in your YAML, or use HTTPS"); } #endif // USE_ARDUINO_VERSION_CODE - - container->client_.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); bool status = container->client_.begin(*stream_ptr, url.c_str()); #elif defined(USE_RP2040) From 8a89dac5d541f631e491832965f00a4549eb0210 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Thu, 11 Jul 2024 06:09:51 +0200 Subject: [PATCH 0029/1052] [ethernet] Fix compile warning for IPv6 (#7048) --- esphome/components/ethernet/ethernet_component.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 6b34157b9d..962a864a29 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -394,7 +394,7 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b const esp_netif_ip_info_t *ip_info = &event->ip_info; ESP_LOGV(TAG, "[Ethernet event] ETH Got IP " IPSTR, IP2STR(&ip_info->ip)); global_eth_component->got_ipv4_address_ = true; -#if USE_NETWORK_IPV6 +#if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT; #else global_eth_component->connected_ = true; @@ -407,8 +407,12 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_ ip_event_got_ip6_t *event = (ip_event_got_ip6_t *) event_data; ESP_LOGV(TAG, "[Ethernet event] ETH Got IPv6: " IPV6STR, IPV62STR(event->ip6_info.ip)); global_eth_component->ipv6_count_ += 1; +#if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0) global_eth_component->connected_ = global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT); +#else + global_eth_component->connected_ = global_eth_component->got_ipv4_address_; +#endif } #endif /* USE_NETWORK_IPV6 */ From bdd0a36aa3ada035ead9eff3a1bc29d2993e4cb5 Mon Sep 17 00:00:00 2001 From: esphomebot Date: Thu, 11 Jul 2024 16:10:18 +1200 Subject: [PATCH 0030/1052] Update webserver local assets to 20240704-081526 (#7041) --- .../components/web_server/server_index_v2.h | 83 +- .../components/web_server/server_index_v3.h | 735 +++++++++--------- 2 files changed, 410 insertions(+), 408 deletions(-) diff --git a/esphome/components/web_server/server_index_v2.h b/esphome/components/web_server/server_index_v2.h index c942cda592..c9932624ba 100644 --- a/esphome/components/web_server/server_index_v2.h +++ b/esphome/components/web_server/server_index_v2.h @@ -68,7 +68,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xe3, 0x9b, 0x82, 0xde, 0xc5, 0x0e, 0x21, 0xa9, 0x64, 0x54, 0x44, 0x2c, 0x97, 0x29, 0x21, 0x29, 0xc2, 0x3f, 0x90, 0x45, 0x68, 0xd6, 0x13, 0xec, 0x34, 0x30, 0x9c, 0xcb, 0x40, 0x71, 0x17, 0x3c, 0xe0, 0xc9, 0x9c, 0xa6, 0x82, 0xa6, 0xc1, 0x5f, 0x71, 0x4a, 0x87, 0x31, 0x40, 0xb1, 0xd3, 0xc4, 0xe3, 0x30, 0x3b, 0x1f, 0x87, 0xc9, 0x88, 0x46, 0xc1, - 0x8d, 0xc8, 0xf1, 0xdf, 0x89, 0x3b, 0x64, 0x49, 0x18, 0xb3, 0x5f, 0x69, 0xe4, 0x6a, 0x69, 0x70, 0xeb, 0xd0, 0x5b, + 0x8d, 0xc8, 0xf1, 0xdf, 0x89, 0x3b, 0x64, 0x49, 0x18, 0xb3, 0x5f, 0x69, 0xe4, 0x6a, 0x69, 0x70, 0xe6, 0xd0, 0x5b, 0x41, 0x93, 0x28, 0x73, 0x9e, 0xbf, 0x7b, 0xf9, 0x42, 0xef, 0x63, 0x45, 0x40, 0xa0, 0x45, 0x36, 0x9b, 0xd2, 0xd4, 0x43, 0x58, 0x0b, 0x88, 0x67, 0x4c, 0x32, 0xc7, 0x97, 0xe1, 0x54, 0x95, 0xb0, 0xec, 0xfd, 0x34, 0x0a, 0x05, 0x7d, 0x43, 0x93, 0x88, 0x25, 0x23, 0xb2, 0xd3, 0x54, 0xe5, 0xe3, 0x50, 0x57, 0x44, 0x45, 0xd1, 0xe5, 0xee, 0xb3, 0x58, @@ -150,19 +150,19 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xa6, 0x57, 0xfd, 0xcf, 0xf9, 0x64, 0x0a, 0xda, 0xd8, 0x0a, 0x49, 0x8f, 0xa8, 0x9e, 0xb0, 0xac, 0xcf, 0x37, 0x94, 0x55, 0xfa, 0xc8, 0xf3, 0x58, 0xa1, 0xa6, 0xc2, 0x5e, 0xde, 0x69, 0xe4, 0xb3, 0xa2, 0xa8, 0x60, 0x1c, 0x9b, 0x9c, 0x2a, 0xe7, 0xab, 0x2e, 0x19, 0x53, 0xf1, 0xda, 0x63, 0x8a, 0x0f, 0x33, 0xe0, 0x75, 0x16, 0xfb, 0x31, 0xe4, 0x6e, - 0xef, 0x7f, 0x5e, 0x22, 0x67, 0x91, 0xaf, 0xa0, 0x6f, 0x91, 0xe7, 0xb7, 0xca, 0xc8, 0xc6, 0xb7, 0xdb, 0xad, 0xe1, - 0xb2, 0x4e, 0x1b, 0x8b, 0xbd, 0x3e, 0xbe, 0x5d, 0x57, 0x1d, 0xc9, 0x62, 0xc2, 0x23, 0x1a, 0xb8, 0x7c, 0x4a, 0x13, - 0x37, 0x07, 0xaf, 0xaa, 0xde, 0xfb, 0x81, 0xf0, 0x16, 0x6f, 0xab, 0xee, 0xd5, 0xe0, 0x36, 0x07, 0xef, 0xd7, 0xd7, + 0xef, 0x7f, 0x5e, 0x22, 0x67, 0x91, 0xaf, 0xa0, 0x6f, 0x91, 0xe7, 0x67, 0xca, 0xc8, 0xc6, 0x67, 0xdb, 0xad, 0xe1, + 0xb2, 0x4e, 0x1b, 0x8b, 0xbd, 0x3e, 0x3e, 0x5b, 0x57, 0x1d, 0xc9, 0x62, 0xc2, 0x23, 0x1a, 0xb8, 0x7c, 0x4a, 0x13, + 0x37, 0x07, 0xaf, 0xaa, 0xde, 0xfb, 0x81, 0xf0, 0x16, 0x6f, 0xab, 0xee, 0xd5, 0xe0, 0x2c, 0x07, 0xef, 0xd7, 0xd7, 0xeb, 0x8e, 0xd7, 0xef, 0x69, 0x9a, 0x49, 0x45, 0xb4, 0xd0, 0x69, 0xbf, 0x2e, 0xc5, 0xd2, 0xd7, 0xc1, 0xd6, 0xf6, 0xa5, 0x09, 0xe2, 0x36, 0xfd, 0x63, 0xff, 0xc0, 0x45, 0xd2, 0x2d, 0xfc, 0x93, 0x3e, 0xf0, 0x1f, 0x8c, 0x5b, 0xf8, 0x19, 0xf9, 0x50, 0xf5, 0x0a, 0x47, 0x82, 0x3c, 0xeb, 0x3e, 0x33, 0x16, 0x33, 0x8f, 0xd9, 0xe0, 0xce, 0x73, 0x63, 0x26, 0xea, 0x10, 0x7a, 0x73, 0xf1, 0x42, 0x55, 0x80, 0x4b, 0x51, 0xba, 0xb3, 0x73, 0x63, 0xeb, 0x61, 0x21, 0x88, 0xbb, 0x1b, 0x33, 0xb1, 0xeb, 0xe2, 0x09, 0xb9, 0x82, 0x1f, 0xbb, 0x0b, 0xef, 0x65, 0x28, 0xc6, 0x7e, 0x1a, 0x26, - 0x11, 0x9f, 0x78, 0xa8, 0xe6, 0xba, 0xc8, 0xcf, 0xa4, 0xbd, 0xf1, 0x18, 0xe5, 0xbb, 0x57, 0xf8, 0x4c, 0x10, 0xb7, - 0xeb, 0xd6, 0x26, 0xf8, 0x95, 0x20, 0x57, 0xa7, 0xbb, 0x8b, 0x33, 0x91, 0x77, 0xae, 0xf0, 0x59, 0xe1, 0xb1, 0xc7, - 0x6f, 0x88, 0x87, 0x48, 0xe7, 0x4c, 0x43, 0x73, 0xce, 0x27, 0xca, 0x73, 0xef, 0x22, 0xfc, 0x1e, 0xe2, 0x2a, 0x69, - 0xc9, 0x6d, 0x74, 0x68, 0x65, 0x87, 0xb8, 0x5c, 0xba, 0x08, 0xdc, 0xbd, 0x3d, 0xab, 0xac, 0x50, 0x15, 0xf0, 0xad, - 0x20, 0x15, 0x83, 0x1c, 0xbf, 0x95, 0x11, 0x9a, 0x5b, 0xe1, 0xa5, 0xc8, 0x0c, 0xe3, 0x19, 0x3f, 0xb4, 0x3e, 0x9a, + 0x11, 0x9f, 0x78, 0xa8, 0xe6, 0xba, 0xc8, 0xcf, 0xa4, 0xbd, 0xf1, 0x18, 0xe5, 0xbb, 0x57, 0xf8, 0x56, 0x10, 0xb7, + 0xeb, 0xd6, 0x26, 0xf8, 0x95, 0x20, 0x57, 0xa7, 0xbb, 0x8b, 0x5b, 0x91, 0x77, 0xae, 0xf0, 0x6d, 0xe1, 0xb1, 0xc7, + 0x6f, 0x88, 0x87, 0x48, 0xe7, 0x56, 0x43, 0x73, 0xce, 0x27, 0xca, 0x73, 0xef, 0x22, 0xfc, 0x1e, 0xe2, 0x2a, 0x69, + 0xc9, 0x6d, 0x74, 0x68, 0x65, 0x87, 0xb8, 0x5c, 0xba, 0x08, 0xdc, 0xbd, 0x3d, 0xab, 0xac, 0x50, 0x15, 0xf0, 0x99, + 0x20, 0x15, 0x83, 0x1c, 0xbf, 0x95, 0x11, 0x9a, 0x33, 0xe1, 0xa5, 0xc8, 0x0c, 0xe3, 0x19, 0x3f, 0xb4, 0x3e, 0x9a, 0x69, 0x4f, 0x79, 0x18, 0x7c, 0x26, 0x68, 0x1a, 0x0a, 0x9e, 0xf6, 0x91, 0xad, 0x7e, 0xe0, 0xbf, 0x91, 0xab, 0x9e, 0xf3, 0x9f, 0xbe, 0xf8, 0x79, 0xf8, 0x73, 0xda, 0xbf, 0xc2, 0xaf, 0xc9, 0xfe, 0xa9, 0xd7, 0x0d, 0xbc, 0x9d, 0x7a, 0x7d, 0xf9, 0xf3, 0x7e, 0xef, 0x1f, 0x61, 0xfd, 0xd7, 0xb3, 0xfa, 0x4f, 0x7d, 0xb4, 0xf4, 0x7e, 0xde, 0xef, 0xf6, @@ -177,7 +177,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xf2, 0x03, 0x64, 0x81, 0xc0, 0xf3, 0x30, 0x9e, 0xd1, 0x2c, 0xa0, 0x39, 0xc2, 0x03, 0x72, 0x21, 0xbc, 0x26, 0xc2, 0xcf, 0x05, 0xfc, 0x68, 0x21, 0x7c, 0xa1, 0x03, 0x98, 0x70, 0x90, 0x15, 0x51, 0x25, 0x5c, 0x69, 0x2c, 0x2e, 0xc2, 0xd3, 0x0d, 0x95, 0x62, 0x0c, 0xde, 0x05, 0x84, 0x87, 0x95, 0x70, 0x27, 0xbe, 0x21, 0x86, 0x24, 0xde, 0xa5, 0x94, - 0xfe, 0x10, 0xc6, 0x1f, 0x69, 0xea, 0x9d, 0xe1, 0x66, 0xeb, 0x31, 0x96, 0x2e, 0xe8, 0x9d, 0x26, 0x6a, 0x17, 0xb1, + 0xfe, 0x10, 0xc6, 0x1f, 0x69, 0xea, 0xdd, 0xe2, 0x66, 0xeb, 0x31, 0x96, 0x2e, 0xe8, 0x9d, 0x26, 0x6a, 0x17, 0xb1, 0xaa, 0x73, 0xa1, 0x62, 0x04, 0x20, 0x64, 0xab, 0xbe, 0x18, 0xd8, 0xf1, 0x9d, 0x74, 0xcd, 0x61, 0x95, 0x86, 0x37, 0x2e, 0xaa, 0xc6, 0x45, 0x59, 0x32, 0x0f, 0x63, 0x16, 0x39, 0x82, 0x4e, 0xa6, 0x71, 0x28, 0xa8, 0xa3, 0xd7, 0xeb, 0x84, 0x30, 0x90, 0x5b, 0xa8, 0x0c, 0x91, 0x65, 0x70, 0x46, 0x26, 0xe0, 0x04, 0x67, 0xc5, 0x83, 0xe8, 0x94, 0x56, @@ -220,10 +220,10 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x73, 0xd4, 0x69, 0xa0, 0x45, 0xa5, 0xad, 0xd4, 0x99, 0xaa, 0x71, 0xf4, 0x82, 0x4f, 0xcf, 0x49, 0xa3, 0x3d, 0x3f, 0x1d, 0xb5, 0xe7, 0xb5, 0x1a, 0xca, 0x0c, 0x69, 0xcd, 0x7a, 0xf3, 0x3e, 0x7e, 0x03, 0x4e, 0x3d, 0x9b, 0x96, 0x70, 0x65, 0x79, 0x2d, 0xbd, 0xbc, 0x5a, 0x2d, 0xc9, 0x51, 0xdb, 0xea, 0x3a, 0x52, 0x5d, 0xf3, 0x5c, 0xe1, 0x64, 0x95, - 0xd4, 0x4e, 0x90, 0x2c, 0x81, 0x64, 0x28, 0x42, 0xc8, 0x99, 0x40, 0x1b, 0x47, 0x85, 0x31, 0xa1, 0xbb, 0x3c, 0xb3, + 0xd4, 0x4e, 0x90, 0x2c, 0x81, 0x64, 0x28, 0x42, 0xc8, 0xad, 0x40, 0x1b, 0x47, 0x85, 0x31, 0xa1, 0xbb, 0x3c, 0xb3, 0xc0, 0x3e, 0x95, 0x94, 0xf0, 0x00, 0x0b, 0xd0, 0xb5, 0xf0, 0x04, 0x4f, 0xf0, 0xac, 0xd6, 0x94, 0x64, 0x5e, 0x6f, 0xb6, 0xab, 0x63, 0x3d, 0x2a, 0xc7, 0xc2, 0xb3, 0x1a, 0x99, 0x14, 0x58, 0xca, 0x93, 0x5a, 0x2d, 0xaf, 0x06, 0x3b, - 0xcd, 0xc9, 0xad, 0x04, 0x20, 0xce, 0x56, 0x93, 0x32, 0x8c, 0x84, 0x2d, 0x65, 0x2a, 0xf3, 0x59, 0x92, 0xd0, 0x14, + 0xcd, 0xc9, 0xad, 0x04, 0x20, 0x6e, 0x57, 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, 0x2c, 0xf9, 0x06, 0x21, 0xe4, 0xc2, 0xa4, 0xb3, 0x08, 0xc9, 0x83, 0x62, 0xc2, 0x2c, 0x99, 0x94, 0x11, 0xea, 0x5f, 0xee, 0x9e, 0xf3, 0x7b, 0x6d, 0xb2, 0x1e, 0xeb, 0x07, 0xb2, 0x59, 0xac, 0x39, 0x57, 0x48, 0xde, 0x7b, @@ -238,14 +238,14 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x13, 0xf3, 0xec, 0xa5, 0x5f, 0xd6, 0xca, 0xc6, 0x97, 0xbb, 0x67, 0xef, 0x37, 0x35, 0x83, 0xf2, 0x7c, 0x56, 0xda, 0xf8, 0x12, 0xbe, 0x05, 0x8d, 0x83, 0x85, 0x16, 0x0e, 0x01, 0xcb, 0xb1, 0x14, 0x48, 0x41, 0x96, 0x17, 0xae, 0x91, 0xa7, 0x38, 0x21, 0x32, 0x0c, 0x54, 0xdd, 0x35, 0xad, 0xe6, 0x31, 0x9e, 0x5c, 0x0c, 0xf8, 0x94, 0x6e, 0x89, 0x0d, - 0x9d, 0x21, 0x9f, 0x4d, 0x20, 0x75, 0x46, 0x82, 0xce, 0xf0, 0x4e, 0x03, 0xb5, 0xab, 0xe2, 0x2b, 0x91, 0x44, 0xca, + 0xdd, 0x22, 0x9f, 0x4d, 0x20, 0x75, 0x46, 0x82, 0xce, 0xf0, 0x4e, 0x03, 0xb5, 0xab, 0xe2, 0x2b, 0x91, 0x44, 0xca, 0x2b, 0xb2, 0x05, 0x8f, 0x49, 0x03, 0xc7, 0xa4, 0x81, 0x43, 0x92, 0xf5, 0x1a, 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, 0xf5, 0x2e, 0xc7, 0x7b, 0x7b, 0x9e, 0xea, 0xf4, 0x8b, 0xf0, 0xb8, 0xa9, 0x2f, 0x23, 0x77, 0xdf, 0x2b, 0x5e, 0x11, 0x21, 0x09, 0x7f, 0xad, 0x16, 0xf7, 0x73, 0x08, 0x43, 0x7b, 0x61, 0x15, 0x83, 0x06, 0x78, 0xa9, 0xeb, 0x55, 0x97, 0x5f, 0xab, 0x15, 0x51, - 0xda, 0x2a, 0xb6, 0xce, 0x70, 0x92, 0xcf, 0xbd, 0x22, 0xf5, 0xa7, 0xb1, 0x96, 0x2f, 0x65, 0x40, 0x40, 0xcc, 0xa6, + 0xda, 0x2a, 0xb6, 0x6e, 0x71, 0x92, 0xcf, 0xbd, 0x22, 0xf5, 0xa7, 0xb1, 0x96, 0x2f, 0x65, 0x40, 0x40, 0xcc, 0xa6, 0x59, 0x66, 0x16, 0x63, 0x1d, 0x09, 0x06, 0xed, 0xbe, 0xd1, 0x59, 0x0b, 0x58, 0x66, 0x57, 0xe9, 0x46, 0x86, 0x9d, 0xb5, 0x50, 0x60, 0x1a, 0x41, 0x54, 0x0a, 0x1a, 0xd5, 0x72, 0x4d, 0xde, 0x6f, 0xd7, 0x73, 0x2e, 0x71, 0x86, 0xb4, 0x93, 0x4b, 0x42, 0x21, 0x91, 0xd5, 0x2a, 0x90, 0xf2, 0x9c, 0x4c, 0xb7, 0x93, 0xfc, 0x99, 0x45, 0xf2, 0x4f, 0x08, @@ -268,7 +268,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x36, 0x44, 0x4d, 0xa5, 0xd4, 0x91, 0x2d, 0x50, 0xd1, 0xc1, 0x9f, 0x7b, 0x4c, 0x2b, 0x6e, 0x26, 0x6e, 0x06, 0x0c, 0xf8, 0x89, 0xf0, 0x54, 0x30, 0x0a, 0x64, 0x06, 0xf7, 0x67, 0x5e, 0x65, 0xea, 0x36, 0x97, 0xdd, 0xb0, 0x46, 0xdc, 0xd8, 0x46, 0x13, 0x97, 0x71, 0xbd, 0xf3, 0x92, 0x97, 0x0e, 0x55, 0x06, 0xb5, 0x30, 0x5c, 0xb0, 0x4c, 0x24, 0xb1, - 0x96, 0x3f, 0x54, 0x49, 0xd1, 0x45, 0x23, 0x4c, 0x25, 0x18, 0xef, 0xe4, 0x1e, 0xd0, 0x1c, 0xfe, 0x2e, 0x6e, 0x85, + 0x96, 0x3f, 0x54, 0x49, 0xd1, 0x45, 0x23, 0x4c, 0x25, 0x18, 0xef, 0xe4, 0x1e, 0xd0, 0x1c, 0xfe, 0x2e, 0xce, 0x84, 0xb5, 0xa3, 0xc6, 0x89, 0x2d, 0xe7, 0xb4, 0xa4, 0xfe, 0x5b, 0x48, 0x75, 0x59, 0x3d, 0xf3, 0xcf, 0xa5, 0x2c, 0x64, 0x38, 0xab, 0x30, 0xf6, 0x44, 0x32, 0x76, 0x04, 0x7a, 0x9a, 0x49, 0xfc, 0xee, 0xea, 0x8c, 0x17, 0xa6, 0xa5, 0x9c, 0x26, 0xb1, 0x37, 0x45, 0xb4, 0xdc, 0xfa, 0xbd, 0xb2, 0x1b, 0x01, 0x23, 0x90, 0x05, 0x84, 0x35, 0x67, 0x4f, 0x10, @@ -312,7 +312,7 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xf1, 0xb5, 0x7b, 0x78, 0x63, 0x02, 0x1e, 0xb4, 0x87, 0x4d, 0x61, 0x19, 0xdb, 0x99, 0xba, 0x07, 0x64, 0x8f, 0x4f, 0xb8, 0xd1, 0xdd, 0xaa, 0x56, 0xc6, 0x1b, 0xb0, 0xff, 0x11, 0x1e, 0x9b, 0xcb, 0x71, 0x54, 0x73, 0x60, 0x1a, 0x2c, 0xf2, 0xc2, 0x29, 0xc0, 0x95, 0xf2, 0x96, 0x22, 0xcc, 0x73, 0x19, 0xe0, 0xfe, 0x16, 0x7f, 0xa7, 0x59, 0xe2, 0xb0, - 0xe0, 0x38, 0xb7, 0x0f, 0xe5, 0x88, 0x0a, 0xfc, 0x22, 0x7e, 0x0f, 0x74, 0x2c, 0x29, 0x34, 0x37, 0x54, 0xf4, 0x94, + 0xe0, 0x38, 0x67, 0x0f, 0xe5, 0x88, 0x0a, 0xfc, 0x22, 0x7e, 0x0f, 0x74, 0x2c, 0x29, 0x34, 0x37, 0x54, 0xf4, 0x94, 0xeb, 0x85, 0x6c, 0x4d, 0x4b, 0xc5, 0xb4, 0x48, 0xa9, 0x91, 0xd3, 0x6c, 0xc8, 0xe3, 0x34, 0x56, 0xb6, 0x28, 0x4e, 0x55, 0x65, 0x5e, 0xb4, 0x05, 0x8b, 0x65, 0x68, 0x71, 0xb9, 0xf4, 0xaa, 0xa8, 0x26, 0xcc, 0x8a, 0x64, 0x20, 0xcc, 0xac, 0x8c, 0x8a, 0x8a, 0x66, 0xad, 0xfa, 0x78, 0x68, 0x35, 0xa1, 0xc8, 0xe8, 0xe6, 0x15, 0x38, 0x6c, 0x17, 0x82, @@ -612,31 +612,32 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xde, 0x0b, 0x3e, 0xda, 0x3c, 0x56, 0xcc, 0x47, 0x5d, 0x79, 0x05, 0x42, 0xdd, 0xb5, 0x35, 0xca, 0x2f, 0x8f, 0xdd, 0xce, 0xa9, 0x56, 0x06, 0x1c, 0x19, 0x0e, 0x77, 0x8f, 0x1a, 0xe6, 0x56, 0x45, 0xcc, 0x47, 0x70, 0x20, 0x55, 0x17, 0x6b, 0x92, 0x8a, 0xc7, 0x7d, 0xdc, 0xec, 0x9c, 0x86, 0x8e, 0xe4, 0x2d, 0x92, 0x79, 0x64, 0xc1, 0x3e, 0x74, 0x1e, - 0xf3, 0x09, 0xf5, 0x19, 0xdf, 0xbf, 0xa1, 0xd7, 0xf5, 0x70, 0xca, 0x4a, 0xf7, 0x36, 0x28, 0x1d, 0xc5, 0x94, 0xbc, - 0x9c, 0x09, 0x7e, 0x86, 0x2b, 0x8b, 0x94, 0x2c, 0x3c, 0xd7, 0xbe, 0x73, 0xb0, 0x55, 0x80, 0x84, 0x5c, 0x47, 0x71, - 0x78, 0xe3, 0x63, 0xd7, 0xb2, 0x37, 0x77, 0x3b, 0xff, 0xfa, 0x3f, 0xfe, 0x97, 0x76, 0x9b, 0x9f, 0xee, 0x8f, 0x9b, - 0x66, 0xac, 0x15, 0x44, 0xe7, 0xa7, 0x70, 0x11, 0xb1, 0x8c, 0xf3, 0xd2, 0xdb, 0xfa, 0x28, 0x65, 0x51, 0x7d, 0x1c, - 0xc6, 0x43, 0xb7, 0xb3, 0x1d, 0x41, 0xf6, 0x0d, 0x24, 0x0d, 0x75, 0xb5, 0x08, 0x48, 0xf0, 0x37, 0xdd, 0xa1, 0x31, - 0x57, 0x31, 0xe4, 0x69, 0xb5, 0x6f, 0xd4, 0x94, 0x07, 0xaa, 0x72, 0xab, 0x26, 0xd5, 0x5f, 0xaf, 0xd2, 0x4c, 0x2d, - 0xad, 0x5c, 0xa6, 0xc9, 0x5d, 0xa7, 0x88, 0x53, 0xfd, 0xdf, 0xff, 0xf9, 0x5f, 0xfe, 0x9b, 0x79, 0x84, 0xf0, 0xd3, - 0xbf, 0xfe, 0xf7, 0xff, 0xfc, 0x7f, 0xfe, 0xf7, 0x7f, 0x85, 0x0b, 0x18, 0x3a, 0x44, 0x25, 0xf9, 0x84, 0x53, 0xc6, - 0xa7, 0x14, 0xc3, 0x70, 0x20, 0x47, 0x71, 0xc2, 0x32, 0xc1, 0x06, 0xd5, 0xeb, 0x35, 0x17, 0x72, 0x42, 0x79, 0xd8, - 0x34, 0x74, 0xf2, 0xd0, 0xe6, 0x25, 0x8d, 0x54, 0x50, 0x2e, 0x69, 0x31, 0x3f, 0xdd, 0x07, 0x7c, 0x3f, 0xec, 0x46, - 0xa2, 0x5f, 0x6c, 0xc7, 0xc2, 0x38, 0x65, 0xa1, 0x24, 0x2f, 0xcb, 0x1d, 0x08, 0x97, 0x2c, 0xe0, 0x31, 0x68, 0x59, - 0xc5, 0x72, 0xf7, 0x2a, 0x7d, 0xda, 0x1f, 0x66, 0x99, 0x60, 0x43, 0x40, 0xb9, 0x72, 0xfd, 0xca, 0xc8, 0x74, 0x1d, - 0xd4, 0xbf, 0xf8, 0x2e, 0x97, 0xa3, 0x28, 0xdb, 0xfa, 0xf0, 0xe4, 0x4f, 0xf9, 0x5f, 0x26, 0xa0, 0x64, 0x39, 0xde, - 0x24, 0xbc, 0xd5, 0x16, 0xf7, 0x71, 0xa3, 0x31, 0xbd, 0x45, 0x8b, 0x72, 0x06, 0xbc, 0x6d, 0x32, 0xe9, 0x2e, 0xb6, - 0x07, 0x94, 0x21, 0xed, 0xc2, 0x33, 0xdd, 0x70, 0xc0, 0xbd, 0xed, 0x34, 0xf2, 0xfc, 0xcf, 0x0b, 0xe9, 0x1c, 0x65, - 0xbf, 0x42, 0xe8, 0x59, 0xfb, 0x91, 0xaf, 0xb9, 0xbd, 0xb8, 0x85, 0xd5, 0xab, 0xa5, 0x7a, 0x8d, 0x9b, 0xeb, 0x17, - 0xed, 0xec, 0xd0, 0xb9, 0x1d, 0xf4, 0x3e, 0x84, 0x30, 0xf6, 0xb8, 0x89, 0xc7, 0xad, 0x45, 0x31, 0xbc, 0x10, 0x7c, - 0x62, 0xc7, 0xca, 0x69, 0x48, 0x07, 0x74, 0x68, 0xfc, 0xef, 0xba, 0x5e, 0xc5, 0xc1, 0xf3, 0xf1, 0xc1, 0x86, 0xb9, - 0x34, 0x48, 0x32, 0x46, 0xee, 0x34, 0xf2, 0x2f, 0xe1, 0x04, 0x2e, 0x86, 0x31, 0x0f, 0x45, 0x20, 0x09, 0xb6, 0x6d, - 0x47, 0xdc, 0x43, 0x60, 0x33, 0x7c, 0x61, 0xc1, 0xd3, 0x56, 0x4d, 0xc1, 0x13, 0x5e, 0xbd, 0x0e, 0x99, 0xfb, 0xb2, - 0xbb, 0x3d, 0x94, 0x72, 0xa4, 0x7d, 0xaf, 0x03, 0xd9, 0xaf, 0x2a, 0x1e, 0x28, 0x2d, 0x63, 0x5a, 0x68, 0x73, 0xbd, - 0x12, 0xd5, 0xaa, 0xf6, 0x27, 0xe1, 0xb9, 0x12, 0x4c, 0x77, 0xb5, 0x95, 0x2c, 0x84, 0x56, 0xaf, 0xc8, 0xf7, 0x85, - 0x15, 0x14, 0x4e, 0xa7, 0xb2, 0x21, 0x6a, 0x9f, 0xee, 0x2b, 0xe5, 0x15, 0xb8, 0x87, 0xcc, 0xd2, 0x50, 0x49, 0x11, - 0xba, 0x91, 0x3e, 0x0a, 0xea, 0x97, 0x4e, 0x97, 0x80, 0xcf, 0x9a, 0x75, 0xfe, 0x1f, 0xd6, 0xb2, 0x30, 0xa4, 0x67, - 0x88, 0x00, 0x00}; + 0xf3, 0x09, 0xf5, 0x19, 0xdf, 0xbf, 0xa1, 0xd7, 0xf5, 0x70, 0xca, 0x4a, 0xf7, 0x36, 0x28, 0x1d, 0xc5, 0x94, 0xdc, + 0x78, 0xc4, 0xf5, 0x9d, 0xa3, 0x56, 0xe9, 0x6e, 0x3b, 0x04, 0x9b, 0xc7, 0xb8, 0xe6, 0xa4, 0x4f, 0xce, 0x02, 0x8b, + 0x77, 0x4e, 0xf7, 0xc3, 0x15, 0x8c, 0x48, 0x7e, 0x9f, 0x6b, 0x47, 0x3b, 0x18, 0x36, 0x40, 0x6f, 0xae, 0xa3, 0xc4, + 0x81, 0x71, 0xc8, 0x6b, 0x41, 0x9d, 0xbb, 0x9d, 0x7f, 0xfd, 0x1f, 0xff, 0x4b, 0xfb, 0xd8, 0x4f, 0xf7, 0xc7, 0x4d, + 0x33, 0xd6, 0xca, 0xae, 0xe4, 0xa7, 0x70, 0x6b, 0xb1, 0x0c, 0x0a, 0xd3, 0xdb, 0xfa, 0x28, 0x65, 0x51, 0x7d, 0x1c, + 0xc6, 0x43, 0xb7, 0xb3, 0x1d, 0x9b, 0xf6, 0x75, 0x25, 0x0d, 0x75, 0xb5, 0x08, 0xe8, 0xf5, 0x37, 0x5d, 0xb8, 0x31, + 0xf7, 0x36, 0xe4, 0xd1, 0xb6, 0xaf, 0xdf, 0x94, 0xa7, 0xaf, 0x72, 0x05, 0x27, 0xd5, 0x9f, 0xba, 0xd2, 0x1c, 0x30, + 0xad, 0xdc, 0xbc, 0xc9, 0x5d, 0xa7, 0x08, 0x6a, 0xfd, 0xdf, 0xff, 0xf9, 0x5f, 0xfe, 0x9b, 0x79, 0x84, 0x58, 0xd5, + 0xbf, 0xfe, 0xf7, 0xff, 0xfc, 0x7f, 0xfe, 0xf7, 0x7f, 0x85, 0xdb, 0x1a, 0x3a, 0x9e, 0x25, 0x99, 0x8a, 0x53, 0x06, + 0xb3, 0x14, 0x77, 0x71, 0x20, 0xa1, 0x71, 0xc2, 0x32, 0xc1, 0x06, 0xd5, 0xbb, 0x38, 0x17, 0x72, 0x42, 0x79, 0x32, + 0x35, 0x74, 0xf2, 0x84, 0xe7, 0x25, 0x41, 0x55, 0x50, 0x2e, 0x09, 0x37, 0x3f, 0xdd, 0x07, 0x7c, 0x3f, 0xec, 0xfa, + 0xa2, 0x5f, 0x6c, 0xc7, 0xc2, 0x90, 0x09, 0x94, 0xe4, 0x65, 0xb9, 0x03, 0xb1, 0x95, 0x05, 0x3c, 0x06, 0x2d, 0xab, + 0x58, 0xee, 0x5e, 0xa5, 0x4f, 0xfb, 0xc3, 0x2c, 0x13, 0x6c, 0x08, 0x28, 0x57, 0x7e, 0x62, 0x19, 0xc6, 0xae, 0x83, + 0xae, 0x18, 0xdf, 0xe5, 0x72, 0x14, 0x45, 0xa0, 0x87, 0x27, 0x7f, 0xca, 0xff, 0x32, 0x01, 0x8d, 0xcc, 0xf1, 0x26, + 0xe1, 0xad, 0x36, 0xcf, 0x8f, 0x1b, 0x8d, 0xe9, 0x2d, 0x5a, 0x94, 0x33, 0xe0, 0x6d, 0x93, 0x49, 0x3a, 0xb6, 0x07, + 0x94, 0xf1, 0xef, 0xc2, 0x8d, 0xdd, 0x70, 0xc0, 0x17, 0xee, 0x34, 0xf2, 0xfc, 0xcf, 0x0b, 0xe9, 0x49, 0x65, 0xbf, + 0x42, 0x9c, 0x5a, 0x3b, 0x9d, 0xaf, 0xb9, 0xbd, 0xb8, 0x85, 0xd5, 0xab, 0xa5, 0x7a, 0x8d, 0x9b, 0xeb, 0xb7, 0xf2, + 0xec, 0x38, 0xbb, 0x1d, 0x21, 0x3f, 0x84, 0x98, 0xf7, 0xb8, 0x89, 0xc7, 0xad, 0x45, 0x31, 0xbc, 0x10, 0x7c, 0x62, + 0x07, 0xd6, 0x69, 0x48, 0x07, 0x74, 0x68, 0x9c, 0xf5, 0xba, 0x5e, 0x05, 0xcd, 0xf3, 0xf1, 0xc1, 0x86, 0xb9, 0x34, + 0x48, 0x32, 0xa0, 0xee, 0x34, 0xf2, 0x2f, 0xe1, 0x04, 0x2e, 0x86, 0x31, 0x0f, 0x45, 0x20, 0x09, 0xb6, 0x6d, 0x87, + 0xe7, 0x43, 0xe0, 0x49, 0x7c, 0x61, 0xc1, 0xd3, 0x56, 0x4d, 0xc1, 0x6d, 0x5e, 0xbd, 0x3b, 0x99, 0xfb, 0xb2, 0xbb, + 0x3d, 0x94, 0xf2, 0xba, 0x7d, 0xaf, 0xa3, 0xde, 0xaf, 0x2a, 0xee, 0x2a, 0x2d, 0x90, 0x5a, 0x68, 0x73, 0xbd, 0x92, + 0xeb, 0xaa, 0xf6, 0x27, 0xe1, 0xb9, 0x12, 0x4c, 0x77, 0xb5, 0x95, 0x2c, 0x84, 0x56, 0xaf, 0xc8, 0xf7, 0x85, 0xc9, + 0x14, 0x4e, 0xa7, 0xb2, 0x21, 0x6a, 0x9f, 0xee, 0x2b, 0x4d, 0x17, 0xb8, 0x87, 0x4c, 0xe9, 0x50, 0x19, 0x14, 0xba, + 0x91, 0x3e, 0x0a, 0xea, 0x97, 0xce, 0xad, 0x80, 0x6f, 0xa0, 0x75, 0xfe, 0x1f, 0xa2, 0x48, 0xf6, 0xdd, 0x94, 0x88, + 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 bde1ce1fb5..0c16ea9f37 100644 --- a/esphome/components/web_server/server_index_v3.h +++ b/esphome/components/web_server/server_index_v3.h @@ -3632,373 +3632,374 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xe7, 0xec, 0xd8, 0x98, 0x31, 0x94, 0x4f, 0x43, 0x40, 0x9e, 0xd0, 0xf7, 0x01, 0xcd, 0x25, 0x67, 0x23, 0xad, 0x2b, 0xfb, 0x10, 0x17, 0x97, 0xdc, 0x84, 0x6a, 0x31, 0x6f, 0x2b, 0x3d, 0x2a, 0xc4, 0x1b, 0x16, 0x80, 0x65, 0xe9, 0x69, 0x93, 0x82, 0x6c, 0x94, 0x54, 0x45, 0xfe, 0x13, 0xbf, 0x03, 0xae, 0xad, 0xac, 0xe4, 0x0a, 0x78, 0xf5, 0xff, 0xd3, - 0xdc, 0x93, 0x2e, 0xb7, 0x6d, 0x24, 0xfd, 0x3f, 0x4f, 0x01, 0xc3, 0x5e, 0x87, 0xb0, 0x01, 0x08, 0x00, 0x45, 0x89, - 0x26, 0x45, 0x69, 0x13, 0x1f, 0xb5, 0x4e, 0x29, 0x71, 0xca, 0x56, 0x5c, 0xbb, 0x51, 0x54, 0x22, 0x48, 0x0e, 0x49, - 0xac, 0x41, 0x80, 0x05, 0x80, 0x3a, 0x42, 0x63, 0x9f, 0x65, 0x9f, 0x65, 0x9f, 0xec, 0xab, 0xee, 0x9e, 0x19, 0x0c, - 0x0e, 0x1e, 0x8a, 0x9d, 0xdd, 0xaf, 0x12, 0xdb, 0xc4, 0xdc, 0xd3, 0x33, 0xd3, 0xd3, 0xd3, 0xa7, 0x3f, 0xe3, 0xbc, - 0x17, 0xb3, 0xc5, 0xf6, 0xeb, 0xee, 0xf3, 0x67, 0x66, 0xe3, 0x96, 0x04, 0x82, 0xcf, 0xce, 0xe2, 0xd9, 0x2c, 0x64, - 0x2d, 0x5d, 0x04, 0x0f, 0xd1, 0x4d, 0xd9, 0xcd, 0xd9, 0x23, 0x47, 0x78, 0xec, 0x34, 0xf2, 0x4d, 0x47, 0x4b, 0xcc, - 0x98, 0x49, 0x97, 0x76, 0x44, 0xb9, 0x22, 0x6f, 0xf6, 0x06, 0xc5, 0x1b, 0x7c, 0x5d, 0x8a, 0xa3, 0x6b, 0x4d, 0xe2, - 0xd5, 0x28, 0x64, 0x16, 0x6e, 0x77, 0xe8, 0x72, 0x3d, 0x5a, 0x8d, 0x46, 0x10, 0xa5, 0xe5, 0x91, 0x63, 0x82, 0xdf, - 0x99, 0x38, 0xc5, 0xf7, 0x60, 0x6e, 0xf4, 0x61, 0x52, 0x76, 0x56, 0x1d, 0x3e, 0xe8, 0x8a, 0x00, 0xab, 0x87, 0x3a, - 0xc8, 0xe0, 0xed, 0xd7, 0x70, 0x6a, 0x07, 0xfa, 0x07, 0xd8, 0x7d, 0xa9, 0xde, 0x6f, 0x3a, 0xfa, 0x83, 0x4b, 0xfd, - 0x03, 0xc2, 0x18, 0xa3, 0x17, 0xbf, 0xa4, 0xdd, 0xab, 0x9b, 0x3a, 0x09, 0xbd, 0x57, 0x18, 0xc7, 0x00, 0x98, 0xbe, - 0xaf, 0x02, 0x7f, 0x16, 0xc5, 0x69, 0x16, 0x8c, 0xf5, 0xab, 0xfe, 0xdb, 0xa0, 0x75, 0xb9, 0xc8, 0x5a, 0xc6, 0x95, - 0x39, 0xce, 0xd4, 0x10, 0x28, 0x02, 0x61, 0x62, 0x04, 0x94, 0x4d, 0x85, 0xd4, 0x13, 0xb4, 0xb5, 0xa0, 0x40, 0xcd, - 0x58, 0x68, 0x9c, 0x0d, 0xa0, 0x5c, 0x25, 0x9e, 0x0a, 0x06, 0x86, 0xd2, 0xb1, 0xa6, 0xd1, 0xa7, 0x97, 0xca, 0xcb, - 0xd5, 0x1a, 0xaf, 0xf2, 0xac, 0xb8, 0x2d, 0xd1, 0x07, 0xb0, 0x30, 0x9c, 0xa1, 0xef, 0x47, 0xaa, 0xd2, 0x67, 0xe9, - 0xde, 0x1d, 0x7e, 0x57, 0xa6, 0x0b, 0xe0, 0xfe, 0x06, 0x8d, 0x8b, 0x28, 0xce, 0x34, 0x70, 0x6c, 0x03, 0x3d, 0x0e, - 0xab, 0x4a, 0x62, 0xbc, 0xd5, 0x96, 0x91, 0x73, 0x64, 0xf0, 0x3d, 0x5e, 0x7e, 0x2d, 0xee, 0xde, 0xac, 0xe4, 0xc1, - 0x82, 0x1e, 0x0b, 0x11, 0x2c, 0x60, 0x16, 0x9f, 0xc7, 0xb7, 0x55, 0x39, 0xc8, 0xcb, 0xe1, 0xee, 0xbb, 0xb7, 0x25, - 0xc8, 0x64, 0x11, 0xd5, 0xaf, 0xc5, 0x03, 0x93, 0x0a, 0x42, 0xa7, 0x72, 0xa6, 0x50, 0xf1, 0x43, 0xd0, 0x30, 0x19, - 0xe8, 0x89, 0xe1, 0x5d, 0x00, 0x28, 0x89, 0x5f, 0xd3, 0xc3, 0xfc, 0x5a, 0x84, 0x4e, 0x16, 0x81, 0x8b, 0x95, 0xcb, - 0x19, 0xb0, 0x6b, 0xb4, 0x5c, 0x65, 0xe8, 0x6a, 0x17, 0x06, 0xc0, 0x72, 0x5d, 0x43, 0xd7, 0x9d, 0x80, 0xa5, 0x0b, - 0x32, 0x31, 0xd7, 0xb5, 0x60, 0x52, 0x4f, 0xe3, 0x44, 0x2f, 0x20, 0x2f, 0xc4, 0xef, 0xc8, 0xa8, 0x82, 0xcf, 0x84, - 0x4f, 0x63, 0x6c, 0x16, 0x7e, 0xea, 0x5b, 0x63, 0x14, 0xe8, 0x34, 0x60, 0x86, 0x31, 0xb5, 0xd3, 0x6f, 0x85, 0x8d, - 0x93, 0x05, 0xf7, 0x9b, 0xa5, 0x69, 0x0e, 0x9f, 0xac, 0xa3, 0xfc, 0xec, 0xc9, 0x3a, 0xcd, 0x07, 0x4f, 0xd6, 0xbe, - 0xd4, 0x15, 0xd0, 0x2f, 0x74, 0x52, 0x14, 0x18, 0x22, 0x18, 0x86, 0xf9, 0x75, 0x61, 0xb9, 0x53, 0xcc, 0x17, 0x76, - 0x19, 0xa5, 0x6b, 0x28, 0xba, 0x1f, 0x70, 0x01, 0xfd, 0x32, 0x09, 0x16, 0x7e, 0x72, 0x4f, 0xf2, 0x7c, 0x53, 0x15, - 0xfa, 0x1b, 0xba, 0x46, 0x88, 0x9e, 0x00, 0x40, 0x38, 0x5f, 0xd7, 0xfe, 0x2a, 0xd3, 0x18, 0x9f, 0xad, 0x14, 0x6a, - 0x42, 0x5f, 0xd7, 0xfa, 0x73, 0x66, 0x4f, 0x58, 0xe6, 0x07, 0x21, 0x55, 0xe9, 0x8b, 0x68, 0xf5, 0xb5, 0xe9, 0xa5, - 0xe5, 0xe9, 0x45, 0xe5, 0xfd, 0x83, 0x93, 0xa1, 0x2b, 0x80, 0xc6, 0x8d, 0x33, 0xc3, 0x28, 0x56, 0xcd, 0x2b, 0x4a, - 0x79, 0xff, 0xd5, 0xe5, 0x60, 0xb0, 0x1c, 0x11, 0x2c, 0x07, 0x8b, 0xc6, 0xf1, 0x84, 0xfd, 0xf2, 0xfe, 0xad, 0x0c, - 0x9b, 0x05, 0x1c, 0xa0, 0x21, 0xdf, 0x98, 0x29, 0xd2, 0x0f, 0x09, 0xd2, 0x0e, 0x14, 0xe0, 0x4a, 0x93, 0x5b, 0x28, - 0xc9, 0x75, 0xed, 0x8c, 0xc6, 0xce, 0x26, 0x34, 0xea, 0x41, 0x8c, 0xb5, 0x92, 0xfc, 0xe4, 0x80, 0x4a, 0xd3, 0x6d, - 0x47, 0x85, 0x00, 0x0c, 0x09, 0xcc, 0xb0, 0x80, 0x02, 0x44, 0xf8, 0x1c, 0xb8, 0xc5, 0x83, 0xc2, 0x5e, 0x20, 0x9f, - 0xdd, 0x3d, 0x2b, 0x93, 0x2a, 0x58, 0x4b, 0x3f, 0x3d, 0xc1, 0x98, 0x5d, 0x70, 0x5f, 0x83, 0x97, 0x8f, 0x93, 0x03, - 0xfa, 0xd4, 0x2a, 0x27, 0xa2, 0x68, 0x44, 0x3c, 0xed, 0x7a, 0xbc, 0x81, 0x07, 0x1d, 0x15, 0x08, 0x11, 0x0f, 0xa9, - 0x7e, 0xae, 0x6b, 0x0b, 0x4e, 0x1a, 0x71, 0x77, 0x42, 0xe0, 0x6b, 0xc0, 0x81, 0xb3, 0xab, 0x6b, 0x0b, 0xff, 0x0e, - 0x67, 0x2e, 0x72, 0xfc, 0xbb, 0x96, 0xcb, 0xb3, 0x8a, 0xb3, 0x96, 0x96, 0xcf, 0xda, 0x98, 0x2f, 0x2e, 0x18, 0x12, - 0xc8, 0x97, 0xf5, 0x1c, 0x05, 0xb4, 0x0d, 0x8b, 0x3b, 0x17, 0x8b, 0x3b, 0xd9, 0xb0, 0xb8, 0x93, 0x2d, 0x8b, 0x1b, - 0xf2, 0x85, 0xd4, 0x24, 0xe8, 0x12, 0x34, 0x0e, 0x93, 0xc0, 0xe3, 0x84, 0x46, 0x8f, 0x9f, 0x33, 0x84, 0x93, 0x95, - 0x86, 0xa0, 0x1c, 0xb5, 0x01, 0x56, 0x4d, 0x70, 0x51, 0x00, 0x51, 0x9f, 0xb8, 0x3c, 0x75, 0x62, 0xde, 0x10, 0x83, - 0xb3, 0x15, 0x56, 0xe7, 0x0b, 0xbb, 0x94, 0xe2, 0x8b, 0xb7, 0xe6, 0x1b, 0x66, 0x3a, 0xdf, 0x32, 0xd3, 0x71, 0xe9, - 0xe8, 0xf2, 0x69, 0xd3, 0x21, 0x54, 0x27, 0x05, 0x7b, 0x10, 0x14, 0x46, 0x71, 0xcb, 0x94, 0xf7, 0xe1, 0x66, 0x1c, - 0xab, 0xec, 0xa8, 0xa5, 0x9f, 0xa6, 0xb7, 0x71, 0x02, 0x12, 0x17, 0x68, 0xe6, 0x61, 0x5b, 0x6a, 0x11, 0x44, 0xdc, - 0x99, 0xcb, 0xc6, 0xcd, 0x54, 0xe4, 0xab, 0x5b, 0xca, 0xeb, 0x74, 0xa8, 0xc4, 0xd2, 0xcf, 0x32, 0x96, 0x20, 0xd0, - 0x7d, 0xf0, 0xfa, 0xfd, 0xff, 0x64, 0x9b, 0x35, 0xe0, 0x90, 0x50, 0xc1, 0xea, 0x88, 0xa1, 0x97, 0x40, 0x5b, 0x25, - 0xe2, 0x22, 0x56, 0x1c, 0xc3, 0x25, 0x12, 0xf0, 0x3f, 0xe1, 0x71, 0x6d, 0x25, 0x8a, 0xe9, 0x92, 0x7b, 0x64, 0xd8, - 0x4b, 0x7f, 0xf2, 0x01, 0x04, 0x7b, 0x2d, 0xcf, 0x04, 0x25, 0x5d, 0xd5, 0x0d, 0x5c, 0x42, 0xc4, 0xde, 0xb8, 0x40, - 0x92, 0x88, 0x25, 0xb9, 0x0a, 0x14, 0x58, 0x4f, 0xfa, 0xd6, 0xf4, 0x6a, 0xed, 0xe5, 0x07, 0xb3, 0xc0, 0xa8, 0x61, - 0x4d, 0x40, 0x6d, 0xe1, 0xe0, 0x54, 0xbe, 0xb9, 0x42, 0xd3, 0x3d, 0x32, 0x80, 0xf3, 0x7b, 0x09, 0xf1, 0x4c, 0x1d, - 0xf1, 0xa0, 0x1d, 0x26, 0x70, 0x6b, 0x5d, 0x3a, 0x57, 0xf9, 0xd3, 0x19, 0xfe, 0x72, 0xaf, 0xf2, 0xa7, 0x23, 0xfc, - 0xe5, 0x5d, 0x61, 0xe4, 0xba, 0x86, 0x87, 0xbc, 0x32, 0x67, 0xfd, 0xb4, 0xb4, 0x9f, 0x48, 0xff, 0xec, 0x01, 0xdb, - 0x86, 0x2f, 0xf0, 0xe3, 0x27, 0xeb, 0x14, 0x2c, 0x2e, 0xd5, 0x39, 0x44, 0x76, 0x62, 0xe4, 0x8d, 0xe9, 0xb3, 0x0d, - 0xe9, 0x23, 0xe3, 0xbf, 0x7c, 0xf1, 0xe3, 0x2e, 0x89, 0x8b, 0x3b, 0xa5, 0xcc, 0x86, 0xb8, 0x1e, 0x05, 0x91, 0x9f, - 0xdc, 0x5f, 0xd3, 0xf3, 0xa2, 0x25, 0x68, 0x77, 0xc9, 0x5e, 0x21, 0xf2, 0xb2, 0x2c, 0xee, 0xca, 0x14, 0x06, 0xef, - 0x3d, 0xbf, 0xe8, 0x07, 0x7f, 0x4f, 0x14, 0xb2, 0xad, 0xf4, 0x00, 0xe5, 0x0b, 0x52, 0xea, 0xe8, 0xfa, 0xc9, 0xba, - 0xc5, 0xea, 0xcd, 0x54, 0x66, 0x5b, 0xa1, 0x0b, 0x61, 0x79, 0xf0, 0x31, 0xbb, 0x98, 0x04, 0x3d, 0x94, 0x67, 0x8d, - 0xe2, 0x3b, 0xeb, 0xc9, 0x3a, 0x3b, 0xd3, 0x17, 0x7e, 0xf2, 0x89, 0x4d, 0xac, 0x71, 0x90, 0x8c, 0x43, 0xa6, 0xf7, - 0xf4, 0x51, 0xe8, 0x47, 0x9f, 0xf8, 0xa7, 0x15, 0xaf, 0x32, 0x94, 0x50, 0xef, 0x7c, 0xfb, 0x0a, 0x98, 0x10, 0xcb, - 0x0e, 0x89, 0xd5, 0x06, 0x28, 0x68, 0x2f, 0x25, 0xc3, 0xab, 0x20, 0x14, 0x8b, 0x52, 0x26, 0x28, 0x58, 0x82, 0xd0, - 0x1c, 0x2c, 0x56, 0x4d, 0x1d, 0xd7, 0x4b, 0x37, 0xd5, 0xa9, 0x12, 0xb3, 0x52, 0x86, 0x5c, 0xbc, 0xc6, 0x16, 0xfe, - 0x78, 0x77, 0x14, 0x0c, 0x7b, 0xff, 0xee, 0x64, 0x2b, 0x5f, 0x36, 0x43, 0x48, 0xb5, 0xc8, 0x52, 0xe2, 0x01, 0x9d, - 0x73, 0x02, 0x73, 0x73, 0xd7, 0x6a, 0x65, 0x3f, 0x4d, 0x57, 0x0b, 0x36, 0x21, 0xc9, 0xe0, 0x59, 0x31, 0xa8, 0xf2, - 0xcb, 0x42, 0x1d, 0xd8, 0x6f, 0x2b, 0xef, 0xf8, 0xf0, 0x25, 0x68, 0x2c, 0x00, 0x41, 0x19, 0x4f, 0xa7, 0x7a, 0xf1, - 0xc6, 0xdf, 0x51, 0xcd, 0x3d, 0xfc, 0x6d, 0xf5, 0xe6, 0xb5, 0xf3, 0x46, 0x56, 0x8e, 0x80, 0x30, 0x16, 0xe2, 0x57, - 0x4e, 0x17, 0x2b, 0xe3, 0x15, 0x33, 0x9a, 0xfa, 0xd1, 0xe6, 0xe9, 0x5c, 0x96, 0xb6, 0xf8, 0x92, 0xb1, 0x09, 0x10, - 0xdc, 0x66, 0x2d, 0xf5, 0x3a, 0x64, 0x37, 0x4c, 0x8a, 0x76, 0xeb, 0x9d, 0x35, 0xd4, 0x40, 0xdf, 0x73, 0x5c, 0x64, - 0xcc, 0xa9, 0x3a, 0x65, 0x4a, 0x43, 0x9c, 0x03, 0x9f, 0xb9, 0x7a, 0xc4, 0x2a, 0x47, 0x6a, 0x68, 0xea, 0xca, 0x00, - 0x36, 0x8e, 0xec, 0x6c, 0x43, 0x7a, 0x0f, 0x03, 0x4f, 0x37, 0x8f, 0xcd, 0x74, 0x8d, 0x1e, 0xf8, 0xea, 0xe6, 0x70, - 0x0a, 0xe1, 0xe4, 0xb5, 0x0a, 0x76, 0xc8, 0x26, 0x88, 0x35, 0x31, 0xc9, 0x74, 0xe2, 0xbe, 0x08, 0x6d, 0x47, 0x54, - 0xfb, 0x15, 0x7c, 0xa8, 0xc6, 0xb5, 0xd1, 0xca, 0x33, 0x1f, 0x61, 0x40, 0xd7, 0x88, 0xa5, 0xe9, 0x46, 0x80, 0xc9, - 0x45, 0x37, 0xf5, 0xa2, 0x74, 0x19, 0x1e, 0x45, 0xba, 0xe9, 0x98, 0x40, 0x12, 0xe0, 0x04, 0xab, 0x7d, 0xe1, 0xf5, - 0x72, 0xbd, 0xe0, 0xfa, 0x2a, 0xc9, 0x6c, 0xa4, 0x73, 0x5d, 0x82, 0x4d, 0xf9, 0xb7, 0x3a, 0x1f, 0x54, 0xe9, 0x9a, - 0x6e, 0x1c, 0x5a, 0xab, 0x84, 0x7a, 0x6b, 0xec, 0x22, 0x6c, 0x40, 0x8c, 0xa9, 0x82, 0x5f, 0xd9, 0x74, 0xca, 0xc6, - 0x59, 0x6a, 0x08, 0xe6, 0x91, 0xf4, 0x1e, 0x0b, 0x56, 0x43, 0x8f, 0x06, 0xfa, 0x4f, 0x60, 0x43, 0x2f, 0x9c, 0x2c, - 0xf1, 0x01, 0x89, 0x37, 0x53, 0x33, 0x98, 0xa8, 0xc5, 0x32, 0x88, 0x78, 0x2f, 0x10, 0x1c, 0xbc, 0x21, 0x1d, 0x87, - 0xc6, 0xef, 0x9f, 0x62, 0x5f, 0xc4, 0x52, 0xab, 0x65, 0x3b, 0x2a, 0xda, 0x76, 0x7c, 0xd7, 0xee, 0x9b, 0x8e, 0xeb, - 0xe4, 0xba, 0x09, 0xb6, 0x5b, 0x9f, 0xf6, 0x3d, 0xf4, 0x58, 0xab, 0x0d, 0xb5, 0x56, 0xd1, 0x43, 0xea, 0x79, 0xee, - 0x0b, 0x57, 0x37, 0x49, 0x65, 0x4e, 0xc1, 0x6d, 0xe3, 0xf8, 0x86, 0x25, 0x5f, 0x3c, 0x95, 0x72, 0xe3, 0xfb, 0x8d, - 0xe7, 0xc8, 0x75, 0x00, 0x09, 0x67, 0xf1, 0xf2, 0x01, 0x53, 0x68, 0xeb, 0xa6, 0x3e, 0x0e, 0xe3, 0x94, 0xa9, 0x73, - 0x20, 0x26, 0xc8, 0x17, 0x4e, 0xe2, 0xe7, 0xf7, 0xaf, 0x3f, 0x7c, 0xd0, 0x4d, 0x8c, 0x04, 0x9a, 0xaa, 0xad, 0xf3, - 0x0d, 0xb5, 0x03, 0xfb, 0x37, 0xee, 0x3b, 0xba, 0x61, 0xe8, 0x51, 0x5b, 0xde, 0x73, 0x94, 0x56, 0xdb, 0x72, 0xfc, - 0xe6, 0xe1, 0x3d, 0xd3, 0x4b, 0x74, 0xaf, 0x79, 0x35, 0xe0, 0x86, 0xed, 0xd7, 0x5b, 0x29, 0x65, 0x11, 0x44, 0xd7, - 0x0d, 0xa9, 0xfe, 0x5d, 0x43, 0x2a, 0x3c, 0xe5, 0x6a, 0xb8, 0x6a, 0x15, 0x2f, 0x14, 0xd2, 0x00, 0x02, 0x39, 0xef, - 0x02, 0x97, 0xf2, 0x9e, 0xfa, 0x82, 0x41, 0x73, 0x4f, 0xee, 0xd5, 0x51, 0x37, 0x24, 0xf3, 0x47, 0x90, 0x84, 0xed, - 0x38, 0x04, 0x85, 0x3f, 0xa6, 0x4a, 0xe5, 0xca, 0x64, 0xa3, 0x54, 0xd7, 0x55, 0x1a, 0x21, 0xf2, 0xf6, 0x3a, 0x63, - 0x8b, 0x25, 0x4b, 0xfc, 0x6c, 0x95, 0xb0, 0xeb, 0x30, 0xbe, 0x7d, 0x54, 0xa8, 0xd3, 0xef, 0x28, 0x3c, 0x0f, 0x66, - 0x73, 0x59, 0xfa, 0xac, 0xc5, 0x06, 0x72, 0x01, 0xb7, 0x76, 0x90, 0xff, 0xe7, 0xdf, 0xb6, 0xfd, 0x9f, 0x7f, 0xef, - 0x2c, 0x0a, 0xcd, 0xe7, 0x43, 0x33, 0x1b, 0xec, 0xb1, 0x2f, 0x9a, 0x7b, 0x2a, 0xc3, 0xbc, 0xb9, 0x4c, 0x6d, 0x11, - 0x20, 0xbf, 0xb6, 0x04, 0xb5, 0xc4, 0xf2, 0xbe, 0x79, 0xd0, 0xc0, 0x60, 0x5e, 0x3b, 0x47, 0x06, 0x85, 0xbe, 0x68, - 0x68, 0x43, 0xa3, 0xb7, 0xd7, 0x8a, 0xfc, 0x71, 0x08, 0xef, 0x9a, 0xc3, 0x17, 0x0e, 0x9f, 0xf3, 0x25, 0x5f, 0x0e, - 0x87, 0x32, 0xb6, 0x9c, 0x5a, 0x15, 0x54, 0xfc, 0xcf, 0x6a, 0x29, 0xfc, 0xf2, 0xec, 0x39, 0x06, 0xd9, 0xde, 0x0f, - 0x5e, 0x0e, 0x51, 0x19, 0xed, 0x64, 0x94, 0x14, 0xc4, 0xca, 0x46, 0xd4, 0x46, 0xca, 0xe4, 0xb5, 0x46, 0x6b, 0x78, - 0x0d, 0x52, 0x31, 0xe0, 0x58, 0x3e, 0x34, 0xcc, 0x97, 0x43, 0xce, 0x58, 0xe2, 0xfa, 0xaf, 0xbd, 0xea, 0xd6, 0xe6, - 0x6c, 0xd9, 0x12, 0xd0, 0x4d, 0x8d, 0xe4, 0x3f, 0x58, 0x98, 0x15, 0x7c, 0x3c, 0x64, 0xf0, 0x03, 0x47, 0x61, 0x98, - 0x63, 0xbc, 0x93, 0x77, 0x9b, 0x74, 0xc4, 0x7e, 0xde, 0xad, 0x23, 0x76, 0xb1, 0x97, 0x8e, 0xd8, 0xcf, 0x5f, 0x5d, - 0x47, 0xec, 0x9d, 0xaa, 0x23, 0x06, 0x8b, 0xf8, 0x9a, 0xed, 0xa5, 0xb8, 0x25, 0xb4, 0x36, 0xe2, 0xdb, 0x74, 0xe0, - 0x72, 0x92, 0x36, 0x1d, 0xcf, 0x19, 0xf0, 0x08, 0xf8, 0xaa, 0x84, 0xf1, 0x0c, 0x94, 0xb8, 0xfe, 0x7c, 0x75, 0xab, - 0x30, 0x9e, 0xa9, 0xca, 0x56, 0x11, 0xf7, 0xf8, 0x5a, 0x78, 0x71, 0x22, 0x05, 0x27, 0xc7, 0x14, 0x3e, 0x9f, 0xac, - 0x43, 0x43, 0x89, 0x6a, 0x2d, 0xb5, 0xd7, 0x3c, 0xa1, 0x02, 0xd5, 0x43, 0xed, 0x29, 0x59, 0xd1, 0x7b, 0x2e, 0x7c, - 0x5b, 0xa8, 0x2d, 0x48, 0x2d, 0x61, 0xf2, 0x13, 0xb1, 0xd6, 0x7f, 0xbb, 0x73, 0xbf, 0xbf, 0x74, 0xfb, 0x6d, 0x17, - 0x8c, 0xb3, 0xe1, 0x85, 0x89, 0x09, 0x4e, 0xbf, 0xdd, 0x86, 0x84, 0x5b, 0x25, 0xc1, 0x83, 0x84, 0x40, 0x49, 0xe8, - 0x40, 0xc2, 0x58, 0x49, 0x38, 0x82, 0x84, 0x89, 0x92, 0x70, 0x0c, 0x09, 0x37, 0x7a, 0x7e, 0x19, 0xc9, 0xe1, 0x1e, - 0x1b, 0x57, 0x26, 0x3d, 0x2a, 0x44, 0xda, 0xb1, 0xe9, 0x82, 0xd6, 0x94, 0x3f, 0xeb, 0xc5, 0x26, 0x71, 0x17, 0x7b, - 0x89, 0x79, 0x3b, 0x67, 0xe4, 0x28, 0xfa, 0x15, 0xde, 0x39, 0x76, 0x16, 0x83, 0xde, 0xb4, 0x70, 0xc0, 0x20, 0xe0, - 0xa0, 0xe9, 0x06, 0x30, 0x8c, 0xfa, 0x72, 0xe5, 0x84, 0x13, 0x0b, 0x65, 0x2d, 0x8b, 0x3c, 0xea, 0xce, 0x92, 0x5b, - 0xa0, 0xd0, 0x38, 0x69, 0xa9, 0x5c, 0xc9, 0xaf, 0xa1, 0x77, 0xf0, 0x8a, 0x8d, 0x56, 0x33, 0xed, 0x3c, 0x9e, 0xed, - 0x54, 0x21, 0x50, 0xb3, 0x60, 0x94, 0x3a, 0x89, 0x5f, 0x2c, 0xb1, 0x2d, 0x79, 0x5f, 0xf4, 0x99, 0x97, 0xcb, 0x67, - 0x30, 0x36, 0x2d, 0x23, 0x05, 0x16, 0xe8, 0x07, 0x60, 0xa4, 0xc8, 0xf0, 0xcf, 0x01, 0xce, 0xca, 0xf7, 0x85, 0xaf, - 0x8c, 0xe7, 0xf4, 0x47, 0x96, 0xa6, 0xfe, 0x4c, 0x94, 0xaf, 0x8f, 0x13, 0x94, 0x76, 0xe4, 0xfb, 0x0b, 0x01, 0x08, - 0x9c, 0xbc, 0xa0, 0xa6, 0x9b, 0x91, 0xc4, 0xb7, 0x1a, 0x68, 0xff, 0xc0, 0x86, 0x2a, 0xf4, 0x14, 0x02, 0x1b, 0x96, - 0xb0, 0xac, 0x51, 0x00, 0x87, 0xff, 0x86, 0x85, 0xd5, 0xc4, 0xcc, 0x9f, 0x55, 0x93, 0x68, 0x1f, 0xe4, 0xea, 0xd8, - 0xa4, 0x40, 0xbf, 0x94, 0xf8, 0x25, 0x12, 0xea, 0x30, 0x9e, 0xfd, 0xa9, 0xe2, 0xe9, 0x2d, 0x6a, 0x05, 0x1f, 0x22, - 0x33, 0xc8, 0x86, 0x36, 0xc2, 0x58, 0xb3, 0x01, 0x84, 0xbd, 0x28, 0x9b, 0x5b, 0x68, 0x5a, 0xd6, 0xf2, 0x22, 0xc3, - 0xb4, 0x71, 0x6d, 0xd7, 0x55, 0x83, 0xda, 0x5e, 0x32, 0x1b, 0xf9, 0x2d, 0xd7, 0x3b, 0x36, 0xc5, 0x1f, 0xdb, 0xe9, - 0x18, 0x39, 0xb6, 0xa0, 0x4d, 0x82, 0x9b, 0xf5, 0x34, 0x8e, 0x32, 0x6b, 0xea, 0x2f, 0x82, 0xf0, 0xbe, 0xb7, 0x88, - 0xa3, 0x38, 0x5d, 0xfa, 0x63, 0xd6, 0x2f, 0x1e, 0xd4, 0x7d, 0x74, 0xd5, 0xc0, 0xad, 0x05, 0x5d, 0xdb, 0x4b, 0xd8, - 0x82, 0x6a, 0x4b, 0x4f, 0x0c, 0xd3, 0x90, 0xdd, 0xe5, 0xbc, 0xfb, 0x52, 0x61, 0x2a, 0x8a, 0x5b, 0x8e, 0x6a, 0x00, - 0x45, 0xca, 0xdd, 0x3c, 0x80, 0x73, 0xa3, 0xfe, 0xd2, 0x9f, 0xa0, 0x67, 0x42, 0xdb, 0xeb, 0x24, 0x6c, 0xa1, 0xd9, - 0x9d, 0x8d, 0x8d, 0x27, 0xf1, 0xed, 0x29, 0x8c, 0x16, 0x2b, 0x5b, 0x29, 0x0b, 0xa7, 0x98, 0x63, 0xa1, 0x65, 0x89, - 0x68, 0xc7, 0xc2, 0x87, 0x38, 0xb4, 0xc6, 0x16, 0x7d, 0xc8, 0xee, 0x79, 0x9a, 0xd3, 0x5f, 0x04, 0x91, 0x45, 0xd3, - 0x39, 0x76, 0x96, 0x4a, 0x5b, 0x2a, 0xfc, 0x8c, 0x35, 0x16, 0x77, 0x35, 0xa7, 0x0f, 0x8f, 0xb5, 0x69, 0x18, 0xdf, - 0xf6, 0xe6, 0xc1, 0x64, 0xc2, 0xa2, 0x3e, 0x8e, 0x59, 0x26, 0xb2, 0x30, 0x0c, 0x96, 0x69, 0x90, 0xf6, 0x17, 0xfe, - 0x1d, 0x6f, 0xf5, 0x70, 0x53, 0xab, 0x6d, 0xde, 0x6a, 0x7b, 0xef, 0x56, 0x95, 0x66, 0xc0, 0x8a, 0x85, 0xda, 0xe1, - 0x43, 0xeb, 0x68, 0x4e, 0x65, 0x9e, 0x7b, 0xb7, 0xba, 0x4c, 0xd8, 0x7a, 0xe1, 0x27, 0xb3, 0x20, 0xea, 0x39, 0xb9, - 0x7d, 0xb3, 0xa6, 0x8d, 0xf1, 0xb8, 0xdb, 0xed, 0xe6, 0xf6, 0x44, 0x7c, 0x39, 0x93, 0x49, 0x6e, 0x8f, 0xc5, 0xd7, - 0x74, 0xea, 0x38, 0xd3, 0x69, 0x6e, 0x07, 0x22, 0xa1, 0xed, 0x8d, 0x27, 0x6d, 0x2f, 0xb7, 0x6f, 0x95, 0x12, 0xb9, - 0xcd, 0xf8, 0x57, 0xc2, 0x26, 0x7d, 0xdc, 0x48, 0xa4, 0x56, 0xda, 0x3b, 0x76, 0x9c, 0x1c, 0x31, 0xc0, 0x65, 0x09, - 0x37, 0x21, 0xaf, 0xe7, 0x6a, 0xbd, 0x77, 0x49, 0xad, 0xe8, 0x6e, 0x3c, 0x6e, 0x2c, 0x37, 0xf1, 0x93, 0x4f, 0x57, - 0x9a, 0x32, 0x0b, 0xdf, 0xa7, 0x62, 0x6b, 0x01, 0x06, 0xeb, 0xae, 0x07, 0x2e, 0xbb, 0xfa, 0xa3, 0x38, 0x81, 0x33, - 0x9b, 0xf8, 0x93, 0x60, 0x95, 0xf6, 0x5c, 0x6f, 0x79, 0x27, 0x92, 0xf8, 0x5e, 0x2f, 0x12, 0xf0, 0xec, 0xf5, 0xd2, - 0x38, 0x0c, 0x26, 0x22, 0x69, 0xd3, 0x59, 0x72, 0x3d, 0xa3, 0x8f, 0x06, 0xeb, 0x01, 0xba, 0x5d, 0xf0, 0xc3, 0x50, - 0xb3, 0xdb, 0xa9, 0xc6, 0xfc, 0x14, 0xf9, 0xcb, 0x9a, 0x93, 0x12, 0x5c, 0xd0, 0x38, 0xdd, 0x3d, 0x5c, 0xde, 0xc9, - 0x3d, 0xef, 0x1e, 0x2d, 0xef, 0xf2, 0xbf, 0x2e, 0xd8, 0x24, 0xf0, 0xb5, 0x56, 0xb1, 0x9b, 0x5c, 0x07, 0x78, 0xd0, - 0xc6, 0x7a, 0xc3, 0x36, 0x15, 0xc7, 0x02, 0x5c, 0x1b, 0x3e, 0x0a, 0x16, 0xcb, 0x38, 0xc9, 0xfc, 0x28, 0xcb, 0xf3, - 0xe1, 0x55, 0x9e, 0xf7, 0x2f, 0x82, 0xd6, 0xe5, 0x3f, 0x5a, 0x74, 0x4f, 0x93, 0xcc, 0x26, 0x37, 0xae, 0xcc, 0xd7, - 0x4c, 0xd5, 0x19, 0x81, 0x6b, 0x0c, 0xf5, 0x45, 0xd4, 0xc2, 0x74, 0x4b, 0xd6, 0x0b, 0x13, 0x90, 0x65, 0x71, 0xd2, - 0x41, 0x29, 0x17, 0xc1, 0x1b, 0x08, 0x0a, 0xbc, 0x66, 0x83, 0x0b, 0x45, 0xff, 0x04, 0x88, 0x15, 0x2c, 0x4c, 0x76, - 0x05, 0x4f, 0x36, 0xd1, 0x8c, 0xdf, 0xed, 0xa6, 0x19, 0x7f, 0xcd, 0xf6, 0xa1, 0x19, 0xbf, 0xfb, 0xea, 0x34, 0xe3, - 0x93, 0xba, 0x5d, 0xc1, 0xdb, 0x78, 0xa0, 0x4b, 0x09, 0x03, 0x5c, 0x4d, 0x09, 0x79, 0xec, 0x79, 0xfb, 0x87, 0xcd, - 0x00, 0x44, 0x6b, 0x14, 0x83, 0x8e, 0x6e, 0x6e, 0xe0, 0xc7, 0xbe, 0x8b, 0x06, 0x7f, 0x4f, 0xd4, 0xef, 0xe9, 0x74, - 0xf0, 0x2a, 0x56, 0x12, 0xe4, 0x17, 0x57, 0xbe, 0x28, 0x79, 0x57, 0xa0, 0x1c, 0xa1, 0x85, 0x89, 0xf1, 0x27, 0xc0, - 0x38, 0x9b, 0xb4, 0x8e, 0x27, 0x52, 0xfb, 0xac, 0x5f, 0x1e, 0x42, 0x4b, 0xaa, 0x7c, 0x0a, 0x13, 0x9c, 0x1a, 0x2b, - 0x71, 0xc6, 0x32, 0x6e, 0x33, 0xfb, 0xfd, 0xfd, 0xdb, 0x49, 0xeb, 0x6d, 0x6c, 0xe4, 0x41, 0xfa, 0xae, 0x6a, 0x00, - 0xc3, 0x65, 0x3f, 0x03, 0x75, 0x3a, 0x39, 0xd7, 0x20, 0x53, 0x03, 0x4c, 0x43, 0x36, 0x55, 0x3f, 0x2b, 0xcd, 0xb4, - 0xa7, 0x56, 0xe4, 0x81, 0xae, 0x6a, 0x97, 0x31, 0xb7, 0x3e, 0x58, 0x73, 0x0a, 0x10, 0x63, 0x77, 0xa1, 0xdd, 0xf0, - 0x84, 0xaa, 0x07, 0x93, 0x3c, 0x37, 0xfa, 0x02, 0x10, 0xca, 0x45, 0xcb, 0x76, 0x11, 0x71, 0xe9, 0xad, 0xd4, 0x69, - 0xe0, 0x12, 0x42, 0x12, 0xff, 0xbd, 0x05, 0x81, 0x3a, 0x17, 0x16, 0x72, 0x98, 0xe9, 0x1a, 0x81, 0x8f, 0x14, 0x2d, - 0x94, 0x09, 0x81, 0x04, 0x58, 0xc2, 0x5f, 0x64, 0x89, 0x84, 0xba, 0x0e, 0x27, 0x01, 0x07, 0x35, 0x02, 0xc0, 0xca, - 0x5f, 0xf0, 0xb5, 0x09, 0xed, 0xf0, 0x32, 0xf8, 0x91, 0xeb, 0x92, 0xf6, 0xc3, 0xed, 0x77, 0x7a, 0x72, 0x00, 0x15, - 0x4e, 0x2b, 0x8a, 0x03, 0x3b, 0x34, 0x14, 0x81, 0x94, 0x48, 0x6f, 0x4d, 0x3b, 0xbd, 0xd5, 0x9e, 0xad, 0x85, 0x87, - 0x8c, 0xcc, 0x5f, 0x5a, 0xf0, 0xc4, 0x47, 0xdc, 0xcb, 0x31, 0x9e, 0xe2, 0x8c, 0xa3, 0xbf, 0x4a, 0x01, 0x37, 0xe2, - 0x43, 0x15, 0xf1, 0x4f, 0x7f, 0xbc, 0x4a, 0xd2, 0x38, 0xe9, 0x2d, 0xe3, 0x20, 0xca, 0x58, 0x92, 0x23, 0xa8, 0x2e, - 0x11, 0x3e, 0x02, 0x3c, 0x57, 0xeb, 0x78, 0xe9, 0x8f, 0x83, 0xec, 0xbe, 0xe7, 0x70, 0x92, 0xc2, 0xe9, 0x73, 0xea, - 0xc0, 0x69, 0x2c, 0xdf, 0xe3, 0xd0, 0x7c, 0x8e, 0x84, 0x5f, 0x52, 0x27, 0x67, 0xd4, 0x6d, 0xde, 0x57, 0x72, 0xc9, - 0x47, 0x08, 0x90, 0x1f, 0x7e, 0x62, 0xcd, 0x00, 0xcb, 0xc3, 0x52, 0x3b, 0x13, 0x36, 0x33, 0x11, 0x6b, 0x03, 0x5f, - 0x5e, 0xfc, 0xb1, 0x3b, 0x86, 0xe6, 0x34, 0x27, 0x03, 0xc5, 0x63, 0xec, 0x33, 0xb2, 0x9e, 0x0f, 0x11, 0xb5, 0xcc, - 0x7d, 0x4a, 0x8e, 0xd8, 0x34, 0x4e, 0x18, 0xf9, 0x93, 0x75, 0xbb, 0xcb, 0xbb, 0xfd, 0x9b, 0xdf, 0x3e, 0xfd, 0xe6, - 0x76, 0xa2, 0x38, 0x6b, 0x89, 0xc6, 0x8c, 0x1d, 0xad, 0xd5, 0xef, 0x33, 0x20, 0x0d, 0x09, 0xf2, 0x63, 0x72, 0xdd, - 0xd5, 0xd3, 0xf5, 0x7e, 0xa3, 0xdb, 0xae, 0x65, 0xcc, 0xef, 0xbc, 0x84, 0x85, 0x7e, 0x16, 0xdc, 0x08, 0x9a, 0xb1, - 0x7d, 0xb4, 0xbc, 0x13, 0x6b, 0x8c, 0x17, 0xde, 0x03, 0x16, 0xa9, 0x32, 0x14, 0xb1, 0x48, 0xd5, 0x64, 0x5c, 0xa4, - 0x7e, 0x6d, 0x36, 0xc2, 0x93, 0x45, 0xe5, 0xa6, 0xef, 0x2c, 0xef, 0xd4, 0x2b, 0xba, 0xa8, 0x26, 0x6f, 0xea, 0xaa, - 0x0b, 0xb2, 0x45, 0x30, 0x99, 0x84, 0x2c, 0x2f, 0x2d, 0x74, 0x79, 0x2d, 0x15, 0xe0, 0x48, 0x38, 0xf8, 0xa3, 0x34, - 0x0e, 0x57, 0x19, 0x6b, 0x06, 0x17, 0x01, 0xc7, 0x73, 0x0a, 0xe0, 0xe0, 0xef, 0xf2, 0x58, 0x3b, 0x40, 0x6e, 0xc3, - 0x36, 0x71, 0xfa, 0xe0, 0x71, 0xd8, 0x6a, 0x97, 0x87, 0x0e, 0x59, 0x72, 0xd0, 0x66, 0xc3, 0x44, 0x4c, 0xb8, 0x96, - 0x08, 0x7b, 0x6b, 0xb6, 0xcb, 0xd3, 0xa4, 0xd7, 0x55, 0x99, 0x94, 0x97, 0x27, 0xf3, 0xe7, 0x9c, 0xb1, 0x17, 0xcd, - 0x67, 0xec, 0x85, 0x38, 0x63, 0xdb, 0x77, 0xe6, 0xe3, 0xa9, 0x0b, 0xff, 0xf5, 0x8b, 0x09, 0xf5, 0x1c, 0xad, 0xbd, - 0xbc, 0xd3, 0xdc, 0xe5, 0x9d, 0x66, 0x79, 0xcb, 0x3b, 0x0d, 0x9b, 0x46, 0x7d, 0x10, 0xd3, 0xf6, 0x0c, 0xd3, 0xd1, - 0x20, 0x11, 0xfe, 0x38, 0xa5, 0x2c, 0xf7, 0x10, 0xf2, 0xa0, 0x56, 0xa7, 0x9e, 0xe7, 0x6d, 0x3f, 0xea, 0x74, 0x96, - 0x04, 0xd2, 0x36, 0xec, 0xcc, 0x1f, 0x8d, 0xd8, 0xa4, 0x37, 0x8d, 0xc7, 0xab, 0xf4, 0x5f, 0x7c, 0xfc, 0x1c, 0x88, - 0x5b, 0x11, 0x41, 0xa5, 0x1d, 0x51, 0x15, 0x04, 0x25, 0x37, 0x4c, 0xb4, 0xb0, 0x96, 0xeb, 0xd4, 0x23, 0xf7, 0xc8, - 0x9e, 0x7d, 0xd8, 0xb0, 0xc9, 0x9b, 0x01, 0xfd, 0xa7, 0xad, 0xd2, 0x66, 0x14, 0xf3, 0x05, 0x60, 0xd9, 0x0a, 0x8e, - 0x87, 0x43, 0x83, 0xaf, 0xa6, 0xd3, 0x6d, 0x1e, 0xee, 0xa5, 0xe8, 0xe9, 0x4a, 0x5c, 0x2a, 0xfc, 0xde, 0xe2, 0x86, - 0x29, 0xdb, 0x5b, 0xdd, 0xb4, 0x47, 0x6a, 0xad, 0x6e, 0xb9, 0x10, 0x8a, 0xb2, 0x7b, 0x62, 0xf9, 0xc7, 0x2f, 0x0e, - 0xe1, 0x3f, 0xa2, 0xea, 0x7f, 0xcd, 0x9a, 0x08, 0xf5, 0xb7, 0x65, 0x4d, 0x70, 0x22, 0x95, 0x90, 0x10, 0xdf, 0xbf, - 0xfc, 0x74, 0xfa, 0xb0, 0x0a, 0x7b, 0x97, 0x26, 0x55, 0xaa, 0x6a, 0xe9, 0xef, 0xe3, 0x18, 0x42, 0x77, 0xd6, 0x8b, - 0x0b, 0xf0, 0x90, 0xb2, 0x7b, 0x36, 0x80, 0x4a, 0xe2, 0x1d, 0x41, 0x52, 0x7c, 0x1d, 0xeb, 0xd0, 0x53, 0xe2, 0xf5, - 0xa6, 0xa7, 0xc4, 0xab, 0xdd, 0x4f, 0x89, 0x1f, 0xf6, 0x7a, 0x4a, 0xbc, 0xfa, 0xea, 0x4f, 0x89, 0xd7, 0xf5, 0xa7, - 0xc4, 0x45, 0x2c, 0xf4, 0x67, 0xcd, 0xb7, 0x2b, 0xfe, 0xf3, 0x23, 0x09, 0xe5, 0xce, 0xe3, 0x41, 0xc7, 0x21, 0x97, - 0xc7, 0x17, 0x7f, 0xf8, 0x61, 0x81, 0x1b, 0xf1, 0x3d, 0xaa, 0x93, 0x15, 0x4f, 0x0b, 0x8e, 0xd9, 0xb1, 0x1f, 0x25, - 0x39, 0x8c, 0xa3, 0xd9, 0xcf, 0x20, 0x94, 0x05, 0x76, 0x60, 0xa2, 0x64, 0x04, 0xe9, 0xcf, 0xf1, 0x72, 0xb5, 0x7c, - 0x0b, 0x6d, 0x7d, 0x0c, 0xd2, 0x60, 0x14, 0x32, 0x69, 0x89, 0x4c, 0xea, 0x6f, 0x9c, 0x27, 0x0e, 0x1a, 0xa7, 0xe2, - 0xa7, 0x7f, 0x27, 0x7e, 0xa2, 0x4e, 0x2a, 0xff, 0x4d, 0x7a, 0x75, 0x7a, 0xf3, 0x43, 0x44, 0x08, 0x01, 0x95, 0x41, - 0x3f, 0xfc, 0x31, 0x72, 0x11, 0x1b, 0x0d, 0xb3, 0x14, 0xfa, 0x0e, 0x1b, 0xdb, 0x61, 0xb5, 0x47, 0xcd, 0xca, 0x30, - 0xa5, 0x0b, 0xae, 0x3a, 0x1b, 0x7e, 0x11, 0xaf, 0x52, 0x36, 0x89, 0x6f, 0x23, 0xdd, 0x8c, 0xa4, 0x91, 0x01, 0x48, - 0x38, 0x65, 0x1d, 0x0c, 0x1e, 0xf9, 0x01, 0x09, 0xe5, 0x38, 0x69, 0xe9, 0x10, 0xbb, 0x74, 0xb5, 0xb4, 0x48, 0xd4, - 0x6c, 0xe1, 0x14, 0x75, 0x19, 0xe5, 0xe8, 0x51, 0xab, 0x15, 0x0f, 0x1e, 0x56, 0x53, 0xa8, 0x6a, 0xc4, 0x36, 0xe7, - 0x0a, 0xa7, 0xad, 0x48, 0x30, 0x17, 0x85, 0x1f, 0x8c, 0x86, 0x85, 0xe3, 0x39, 0x64, 0xba, 0x5a, 0xe4, 0x82, 0x17, - 0x91, 0x7c, 0xc5, 0xd7, 0x83, 0x7b, 0x85, 0xa0, 0xcf, 0x97, 0x0a, 0x18, 0xdf, 0xdd, 0xb0, 0x24, 0xf4, 0xef, 0x5b, - 0x46, 0x1e, 0x47, 0x3f, 0x02, 0x00, 0x5e, 0xc5, 0xb7, 0x91, 0x5a, 0x00, 0x83, 0xb5, 0x34, 0xec, 0xa5, 0x46, 0xff, - 0x25, 0x60, 0xb8, 0xa2, 0x8c, 0x00, 0xc2, 0xe4, 0xce, 0xd8, 0xdf, 0x4d, 0xfa, 0xf7, 0x1f, 0x46, 0x6e, 0x9e, 0xc7, - 0xb2, 0xa3, 0x5f, 0x96, 0x7b, 0x74, 0xf3, 0xf4, 0xe9, 0xa3, 0xcd, 0xd3, 0x2e, 0x87, 0x67, 0x6f, 0xa8, 0x6d, 0x6c, - 0x3c, 0x05, 0x30, 0x8a, 0x8b, 0x78, 0x35, 0x9e, 0xa3, 0xa2, 0xeb, 0xd7, 0x9b, 0x6f, 0x06, 0x6d, 0x62, 0x94, 0x52, - 0x39, 0xf5, 0x4a, 0x52, 0x01, 0x05, 0xec, 0xff, 0x35, 0x38, 0xe0, 0xfc, 0x1f, 0x82, 0xa1, 0xbe, 0x6b, 0xf8, 0x2b, - 0x3e, 0x78, 0xd8, 0xe6, 0xed, 0x43, 0x30, 0x4d, 0xee, 0xda, 0x42, 0x08, 0xd7, 0x9a, 0x91, 0x4c, 0x5e, 0x05, 0x9a, - 0xea, 0x46, 0x6e, 0x93, 0x87, 0x3c, 0xd1, 0x0b, 0xb3, 0xe9, 0x99, 0xce, 0x0d, 0x0d, 0x4c, 0xc6, 0xb1, 0x55, 0x05, - 0xc9, 0x70, 0x95, 0x07, 0x86, 0xe8, 0xab, 0x9a, 0xb7, 0x08, 0x22, 0x13, 0xbd, 0xc0, 0xd7, 0x73, 0xfc, 0x3b, 0xf0, - 0x83, 0x0c, 0xc8, 0xad, 0x9a, 0x05, 0x89, 0xa6, 0x6a, 0x37, 0x07, 0xa1, 0x9e, 0xf4, 0x46, 0x48, 0x08, 0x29, 0xde, - 0xf0, 0x1b, 0x4d, 0xd3, 0x34, 0xf9, 0x8c, 0xd0, 0xe4, 0x3b, 0x02, 0xd3, 0xf1, 0x39, 0x00, 0xd2, 0x92, 0x7c, 0x79, - 0x47, 0x29, 0xf0, 0x32, 0x40, 0x99, 0xac, 0x48, 0xe0, 0xae, 0xfe, 0x3a, 0x8e, 0x48, 0x10, 0x0f, 0x7a, 0x70, 0xd3, - 0xe6, 0x27, 0xe0, 0x11, 0xb8, 0xa7, 0xe1, 0x83, 0x1d, 0x73, 0x39, 0x27, 0x58, 0x73, 0xe8, 0x73, 0xd8, 0x67, 0xcd, - 0x3e, 0xe1, 0x22, 0x05, 0x0b, 0x82, 0xd4, 0xa1, 0xe2, 0xe2, 0xd9, 0x64, 0x0d, 0xb8, 0x11, 0xdf, 0x45, 0x77, 0xd9, - 0x82, 0x45, 0x2b, 0x1d, 0x63, 0x42, 0xa1, 0x8f, 0x3e, 0x28, 0xf3, 0x8a, 0x88, 0x2d, 0xc0, 0x36, 0xcd, 0x35, 0xe7, - 0x74, 0x17, 0xa6, 0x1c, 0xa5, 0xfa, 0xe6, 0x98, 0x0b, 0x36, 0x53, 0x8e, 0xdb, 0xaa, 0x37, 0x04, 0x5f, 0xd2, 0xb8, - 0x6a, 0xc8, 0x45, 0x9a, 0xd0, 0xd0, 0x06, 0x79, 0xc7, 0xe0, 0xec, 0x22, 0x01, 0xf6, 0x96, 0x5f, 0x5d, 0x34, 0x29, - 0x91, 0xf1, 0x2b, 0x8c, 0xa2, 0xc4, 0xa8, 0x37, 0xc3, 0xc7, 0x09, 0x8e, 0x89, 0x36, 0xb6, 0x33, 0xae, 0xb5, 0xb3, - 0x61, 0xd2, 0x9f, 0xd8, 0x3d, 0x5d, 0x24, 0x04, 0xaa, 0x4f, 0xec, 0x1e, 0x74, 0xff, 0x5e, 0x03, 0x37, 0x45, 0xdf, - 0x82, 0xae, 0x4d, 0x70, 0xf5, 0x3f, 0x06, 0x67, 0x55, 0x5b, 0x0e, 0x90, 0x93, 0x6f, 0xc1, 0xe2, 0x08, 0x62, 0x88, - 0xea, 0x2c, 0x0e, 0x31, 0x57, 0xf1, 0x6f, 0x35, 0xc2, 0xd8, 0x6a, 0x38, 0x1a, 0xc6, 0x33, 0xd7, 0x71, 0x0e, 0x6a, - 0xe5, 0x81, 0x91, 0xdd, 0x54, 0xda, 0x30, 0xb3, 0x81, 0xeb, 0x58, 0xc1, 0x33, 0xdb, 0xeb, 0xd7, 0xee, 0x68, 0xc5, - 0x97, 0xe4, 0x10, 0xd9, 0x5f, 0xa7, 0x4f, 0xd6, 0xad, 0xda, 0x81, 0x34, 0xaa, 0x2a, 0xf3, 0x38, 0xb6, 0x9c, 0xf3, - 0xbf, 0x86, 0xf5, 0xab, 0x9f, 0x3c, 0x59, 0x52, 0x5c, 0x93, 0x21, 0x78, 0x43, 0x6e, 0xc1, 0x31, 0xfa, 0x8b, 0xf6, - 0x5c, 0x6b, 0xd1, 0xf1, 0x31, 0x8c, 0xa1, 0x0c, 0x97, 0x2d, 0x6c, 0xca, 0xd4, 0x06, 0x2a, 0x3d, 0xa6, 0x55, 0x0c, - 0xc7, 0xfd, 0xae, 0xb2, 0x42, 0xa2, 0xb7, 0x95, 0x5a, 0xc0, 0xf6, 0x37, 0x5c, 0x9f, 0xf6, 0x08, 0xfc, 0x12, 0x40, - 0x09, 0xf0, 0x9d, 0xbe, 0xb3, 0xc1, 0xd5, 0xb2, 0xdc, 0x5c, 0xf9, 0x92, 0xdc, 0xbf, 0x31, 0xbc, 0x74, 0x50, 0x86, - 0x26, 0xdb, 0x6b, 0xbe, 0xee, 0x1e, 0xd8, 0x24, 0x8b, 0x26, 0xe5, 0x06, 0x2b, 0xf7, 0xd7, 0xfe, 0xcd, 0x95, 0x30, - 0x0a, 0x04, 0x15, 0x88, 0x1b, 0x30, 0x4a, 0x1e, 0x47, 0xb8, 0xf9, 0xe9, 0xb8, 0x05, 0x7b, 0x51, 0x31, 0x58, 0x81, - 0x3c, 0x82, 0xc9, 0x6a, 0x0a, 0x53, 0x1c, 0x3c, 0x57, 0xa3, 0x59, 0x70, 0x4b, 0x10, 0xa2, 0x1b, 0x77, 0x62, 0x26, - 0x74, 0x0a, 0x8b, 0x3a, 0x01, 0xf7, 0x45, 0xb9, 0x2f, 0xd7, 0x3a, 0xd8, 0xcd, 0xb5, 0xce, 0x76, 0x71, 0xad, 0xc9, - 0x9c, 0xea, 0x36, 0xf1, 0x97, 0x8a, 0x45, 0x9e, 0x20, 0xce, 0x55, 0xc3, 0xbc, 0x12, 0xab, 0x1b, 0xad, 0xaf, 0x44, - 0xad, 0x5a, 0x6b, 0xa4, 0x25, 0x88, 0xec, 0x6f, 0xe5, 0x81, 0x22, 0x04, 0xea, 0x2a, 0x6f, 0xfc, 0xa2, 0xe0, 0x8d, - 0xd3, 0xab, 0xa6, 0x30, 0xa4, 0x11, 0xd4, 0xbf, 0x62, 0xa4, 0x26, 0x5f, 0x07, 0x85, 0xb1, 0x5a, 0x31, 0x52, 0xc5, - 0xfc, 0xaa, 0x78, 0x68, 0x28, 0x46, 0x7d, 0xe2, 0x95, 0x51, 0xb6, 0xed, 0x2b, 0x17, 0x2d, 0xac, 0xaf, 0x8a, 0x74, - 0xe0, 0xba, 0xe3, 0x90, 0x65, 0xb2, 0xba, 0x6d, 0xca, 0xe6, 0x37, 0x6a, 0xb6, 0xb2, 0x49, 0xa4, 0x9d, 0x0c, 0x01, - 0x58, 0xb0, 0xe9, 0x2b, 0x72, 0x6d, 0xa9, 0x03, 0x81, 0x83, 0x6c, 0x30, 0xeb, 0xdb, 0xcd, 0x9d, 0xa7, 0x78, 0x09, - 0x85, 0x14, 0x5e, 0xe5, 0x41, 0x20, 0x7c, 0xaf, 0xd6, 0x0d, 0xb7, 0x3c, 0x5e, 0xf2, 0xfc, 0x7e, 0x07, 0xf6, 0xa2, - 0xe6, 0xa8, 0x82, 0x7c, 0x3c, 0x99, 0x16, 0xa9, 0xe7, 0x62, 0xd1, 0x7a, 0xa3, 0xc4, 0xc4, 0x59, 0x73, 0xcb, 0x98, - 0x32, 0x8f, 0x9e, 0x97, 0xe8, 0x89, 0x7e, 0xf9, 0xd6, 0x49, 0x56, 0x11, 0xfa, 0xb6, 0xb7, 0xb2, 0xc4, 0x1f, 0x7f, - 0x52, 0x86, 0x2c, 0xf8, 0x9c, 0xc0, 0x03, 0x2e, 0x4b, 0x0a, 0xfa, 0x3e, 0xba, 0x82, 0x64, 0x3d, 0xdb, 0x4b, 0x15, - 0xee, 0x4b, 0xef, 0xb1, 0xd3, 0xf6, 0x5f, 0x4c, 0x0f, 0x2b, 0x4c, 0x51, 0xaf, 0x53, 0x66, 0x99, 0x6f, 0x18, 0x47, - 0x36, 0x5f, 0x2d, 0x46, 0x6b, 0x95, 0xb7, 0xaa, 0xb0, 0x5c, 0xeb, 0x6c, 0x56, 0xb5, 0xdb, 0xe9, 0x74, 0x5a, 0x66, - 0x34, 0x3a, 0xda, 0x21, 0x32, 0x0b, 0x1f, 0x3b, 0x8e, 0x53, 0x1d, 0xfb, 0x76, 0xb0, 0x5b, 0xc8, 0xb7, 0xed, 0x36, - 0x8e, 0x18, 0x61, 0xbb, 0x0b, 0x7e, 0x75, 0x70, 0xe4, 0x76, 0x71, 0xb2, 0x4b, 0x6a, 0x11, 0x7d, 0x52, 0x86, 0x08, - 0x32, 0xb6, 0x48, 0x7b, 0x63, 0x86, 0x32, 0x18, 0x5b, 0x39, 0xd0, 0xa8, 0x38, 0x60, 0xcd, 0x40, 0x55, 0xc4, 0x15, - 0xbb, 0xc2, 0xd1, 0x90, 0x1f, 0x5e, 0x63, 0xde, 0x8b, 0x4e, 0xf0, 0xa0, 0xac, 0xeb, 0x3c, 0x6d, 0x9c, 0x56, 0xc7, - 0xf9, 0x4b, 0xa9, 0x9c, 0x06, 0x17, 0xe0, 0x5a, 0x08, 0xb4, 0x89, 0x3f, 0x8b, 0x7f, 0x4b, 0xfe, 0xff, 0x8b, 0xe5, - 0x5d, 0x59, 0x7f, 0xa4, 0x0b, 0x1c, 0xed, 0xe2, 0xb4, 0xd0, 0xa8, 0x9b, 0xf6, 0x80, 0xd4, 0x32, 0x98, 0xaa, 0x02, - 0x74, 0x10, 0xd2, 0x97, 0x02, 0x80, 0x34, 0xb0, 0xdf, 0x91, 0x62, 0x86, 0x25, 0x2e, 0x58, 0x88, 0x45, 0xf8, 0x3a, - 0x98, 0x83, 0xf9, 0xbc, 0x8b, 0xf2, 0x83, 0xd2, 0x9e, 0x00, 0x69, 0x7c, 0x6d, 0x6e, 0x7b, 0xb1, 0xfb, 0xab, 0x72, - 0x2d, 0xd1, 0x30, 0x80, 0xcc, 0x85, 0x43, 0x88, 0x8a, 0x04, 0x5a, 0x65, 0x73, 0xd3, 0x28, 0x65, 0xae, 0x2a, 0x67, - 0x13, 0x03, 0xc3, 0xe6, 0x9a, 0x8b, 0x50, 0xdb, 0x42, 0x5a, 0x00, 0x93, 0xe5, 0xdb, 0x0f, 0xbf, 0x2d, 0x58, 0x62, - 0x75, 0x3f, 0xba, 0xb8, 0xe4, 0xb8, 0x7f, 0x2d, 0xbc, 0x3b, 0x53, 0x3a, 0xff, 0xc8, 0x5f, 0xfc, 0xa1, 0x91, 0xa1, - 0x77, 0x51, 0xe2, 0xd0, 0x71, 0x6d, 0x71, 0xcf, 0xd8, 0xab, 0xf4, 0x22, 0x88, 0xf6, 0x2f, 0xeb, 0xdf, 0xed, 0x5d, - 0x16, 0x2e, 0x8c, 0xbd, 0x0b, 0xc3, 0x8d, 0x43, 0x9a, 0x0b, 0xd9, 0xe0, 0x07, 0x85, 0xa1, 0xa8, 0x5a, 0x1d, 0xeb, - 0x58, 0x8b, 0xa8, 0xfc, 0x8b, 0xd5, 0x60, 0x78, 0x72, 0x76, 0xb7, 0x08, 0xb5, 0x1b, 0x96, 0x40, 0x68, 0x9f, 0x81, - 0xee, 0xda, 0x8e, 0xae, 0xa1, 0x0d, 0x6d, 0x10, 0xcd, 0x06, 0xfa, 0x2f, 0x17, 0x6f, 0xac, 0xae, 0x7e, 0x06, 0x22, - 0xda, 0x9b, 0x19, 0x5e, 0x7b, 0xe7, 0xfe, 0x3d, 0x4b, 0xae, 0x3d, 0x5d, 0xc3, 0x08, 0x3e, 0x74, 0xe1, 0x61, 0x9a, - 0xe6, 0xe9, 0x7b, 0x04, 0x8a, 0xd0, 0x44, 0xac, 0x37, 0x1d, 0x50, 0x8e, 0xeb, 0x75, 0x35, 0xd7, 0x3b, 0xb4, 0x8f, - 0xba, 0xfa, 0xe9, 0x37, 0x9a, 0x76, 0x32, 0x61, 0xd3, 0xf4, 0x14, 0x9f, 0x68, 0x27, 0x78, 0x47, 0xd0, 0x6f, 0x4d, - 0xb3, 0xc7, 0x61, 0x6a, 0xb9, 0xda, 0x9a, 0x7f, 0x6a, 0xda, 0x34, 0x08, 0xc3, 0x9e, 0xf6, 0x78, 0xea, 0x4d, 0x0f, - 0xa7, 0x2f, 0xfa, 0x3c, 0x39, 0xff, 0xa6, 0x54, 0xdc, 0xa4, 0x7f, 0x3d, 0xa5, 0x5a, 0x9a, 0x25, 0xf1, 0x27, 0xc6, - 0xd5, 0x4e, 0x34, 0xf9, 0x78, 0xac, 0x56, 0xf5, 0xea, 0x3d, 0xb9, 0xdd, 0xd1, 0x78, 0xea, 0x15, 0xc5, 0x71, 0x8c, - 0x07, 0x72, 0x90, 0x27, 0x07, 0x62, 0xe8, 0x27, 0x2a, 0x98, 0x5c, 0xab, 0x09, 0x50, 0xae, 0xce, 0xe7, 0x38, 0x13, - 0xf3, 0x3b, 0x01, 0x3f, 0x8c, 0xd2, 0x5c, 0x17, 0x46, 0xa0, 0x6b, 0x93, 0x81, 0xfe, 0xa3, 0xeb, 0x75, 0x4d, 0xd7, - 0x3d, 0xb2, 0x8f, 0xba, 0x63, 0xc7, 0x3c, 0xb4, 0x0f, 0xad, 0xb6, 0x7d, 0x64, 0x76, 0xad, 0xae, 0xd9, 0xfd, 0x5b, - 0x77, 0x6c, 0x1d, 0xda, 0x87, 0xa6, 0x63, 0x75, 0x21, 0xd1, 0xea, 0x5a, 0xdd, 0x1b, 0xeb, 0xb0, 0x3b, 0x76, 0x30, - 0xd5, 0xb3, 0x3b, 0x1d, 0xcb, 0x75, 0xec, 0x4e, 0xc7, 0xec, 0xd8, 0x47, 0x47, 0x96, 0xdb, 0xb6, 0x8f, 0x8e, 0xce, - 0x3b, 0x5d, 0xbb, 0x0d, 0x79, 0xed, 0xf6, 0xb8, 0x6d, 0xbb, 0xae, 0x05, 0x7f, 0x99, 0x5d, 0xdb, 0xa3, 0x1f, 0xae, - 0x6b, 0xb7, 0x5d, 0xd3, 0x09, 0x3b, 0x9e, 0x7d, 0xf4, 0xc2, 0xc4, 0xbf, 0xb1, 0x98, 0x89, 0x7f, 0x41, 0x33, 0xe6, - 0x0b, 0xdb, 0x3b, 0xa2, 0x5f, 0xd8, 0xe0, 0xcd, 0x61, 0xf7, 0x57, 0xfd, 0x60, 0xe3, 0x1c, 0x5c, 0x9a, 0x43, 0xb7, - 0x63, 0xb7, 0xdb, 0xe6, 0xa1, 0x6b, 0x77, 0xdb, 0x73, 0xeb, 0xd0, 0xb3, 0x8f, 0x8e, 0xc7, 0x96, 0x6b, 0x1f, 0x1f, - 0x9b, 0x8e, 0xd5, 0xb6, 0x3d, 0xd3, 0xb5, 0x0f, 0xdb, 0xf8, 0xa3, 0x6d, 0x7b, 0x37, 0xc7, 0x2f, 0xec, 0xa3, 0xce, - 0xfc, 0xc8, 0x3e, 0xfc, 0x78, 0xd8, 0xb5, 0xbd, 0xf6, 0xbc, 0x7d, 0x64, 0x7b, 0xc7, 0x37, 0x47, 0xf6, 0xe1, 0xdc, - 0xf2, 0x8e, 0xb6, 0xd6, 0x74, 0x3d, 0x1b, 0x60, 0x84, 0xd9, 0x90, 0x61, 0xf2, 0x0c, 0xf8, 0x33, 0xc7, 0xba, 0xff, - 0xc5, 0x66, 0xd2, 0x7a, 0xd5, 0x17, 0x76, 0xf7, 0x78, 0x4c, 0xc5, 0x21, 0xc1, 0x12, 0x25, 0xa0, 0xca, 0x8d, 0x45, - 0xdd, 0x62, 0x73, 0x96, 0x68, 0x48, 0xfc, 0xe1, 0x9d, 0xdd, 0x58, 0xd0, 0x31, 0xf5, 0xfb, 0x3f, 0x6d, 0x47, 0x2e, - 0x39, 0x44, 0xae, 0xfc, 0x86, 0xff, 0x43, 0x41, 0x5f, 0x86, 0xe6, 0xf9, 0x26, 0x41, 0xc5, 0xfb, 0xdd, 0x82, 0x8a, - 0x37, 0xab, 0x7d, 0x04, 0x15, 0xef, 0xbf, 0xba, 0xa0, 0xe2, 0xbc, 0xaa, 0x27, 0xff, 0xbe, 0xea, 0x9b, 0xfe, 0xd7, - 0x75, 0xf5, 0x19, 0x12, 0xf8, 0xad, 0xcb, 0x8b, 0xd5, 0x15, 0x78, 0x57, 0x7a, 0x1f, 0x0f, 0xde, 0xac, 0x4a, 0x4a, - 0x60, 0x31, 0xe0, 0xd8, 0xf7, 0x31, 0xe1, 0xd8, 0xdf, 0x57, 0x03, 0xd0, 0x3c, 0xe1, 0x74, 0x49, 0x30, 0xb1, 0xe6, - 0x7e, 0x38, 0x95, 0x34, 0x0d, 0xa4, 0xf4, 0x31, 0x19, 0xac, 0x12, 0xe0, 0xba, 0x06, 0x71, 0xd8, 0x6a, 0x11, 0xa5, - 0xbd, 0x23, 0x07, 0x2e, 0x52, 0x6f, 0x9a, 0xe4, 0x95, 0xca, 0xb6, 0xf0, 0x47, 0x75, 0xcd, 0xad, 0x26, 0x36, 0xe6, - 0xa3, 0x52, 0x60, 0x73, 0xeb, 0x6e, 0xbd, 0x5d, 0x0d, 0xb4, 0x6d, 0x84, 0xd2, 0x24, 0x90, 0x73, 0x4d, 0xf9, 0x65, - 0xd5, 0xbc, 0x8a, 0x32, 0xe6, 0xe6, 0x91, 0xc2, 0x48, 0xaa, 0xf5, 0xdd, 0xb2, 0x6a, 0xdf, 0xae, 0x69, 0x36, 0x74, - 0x5f, 0xaa, 0xbe, 0x45, 0xaf, 0x50, 0x36, 0x5c, 0x05, 0x55, 0x25, 0xb2, 0x5a, 0x23, 0x40, 0x0a, 0xea, 0xbe, 0x50, - 0x3e, 0x2c, 0x48, 0x4b, 0x47, 0x43, 0x7a, 0xc7, 0x51, 0xf2, 0x4a, 0x6d, 0xaa, 0x0a, 0x8b, 0xcf, 0xd6, 0x48, 0x71, - 0x07, 0xbf, 0x03, 0xe9, 0xc8, 0x29, 0x9e, 0x51, 0xac, 0xc2, 0x79, 0xad, 0xb4, 0x4b, 0x8f, 0x99, 0x7c, 0xee, 0xae, - 0xeb, 0xc4, 0xe3, 0x46, 0x55, 0x65, 0x97, 0x2d, 0x04, 0x15, 0x84, 0xdd, 0x93, 0x62, 0x70, 0x4e, 0xca, 0xdb, 0xa8, - 0xfb, 0xbc, 0xad, 0x31, 0x51, 0xee, 0x31, 0x6c, 0x62, 0x93, 0x7f, 0xa8, 0x7e, 0x01, 0xd6, 0x53, 0x88, 0x82, 0xdd, - 0x43, 0x32, 0x4d, 0xa1, 0x51, 0x3d, 0xd4, 0x62, 0xee, 0x6f, 0x51, 0xb0, 0x51, 0x1b, 0xe6, 0x8d, 0xa0, 0x36, 0xf4, - 0x36, 0x9d, 0x1c, 0x69, 0x3c, 0xb2, 0x2e, 0x89, 0xa8, 0xdd, 0xce, 0xb1, 0xe9, 0x1e, 0x99, 0xf6, 0x71, 0xc7, 0xc8, - 0xc5, 0x81, 0x53, 0x9b, 0x2c, 0x01, 0x04, 0x94, 0xa2, 0xe5, 0x30, 0x83, 0x28, 0xc8, 0x02, 0x3f, 0xcc, 0x81, 0x3e, - 0x2e, 0xbf, 0x2a, 0xfe, 0xb9, 0x4a, 0x33, 0x98, 0xa3, 0x20, 0x7a, 0x51, 0x21, 0xdc, 0x1a, 0xb1, 0xec, 0x96, 0xb1, - 0x68, 0x83, 0xb0, 0xbc, 0xaa, 0x5f, 0xfe, 0xe7, 0x69, 0xdb, 0xe6, 0xa4, 0xc9, 0x32, 0xca, 0x22, 0xbe, 0x3f, 0x84, - 0x32, 0x74, 0x3e, 0x34, 0x7f, 0xda, 0x84, 0x70, 0xff, 0xb9, 0x1b, 0xe1, 0x66, 0x6c, 0x1f, 0x84, 0xfb, 0xcf, 0xaf, - 0x8e, 0x70, 0x7f, 0x52, 0x11, 0x6e, 0xc9, 0x16, 0xa8, 0xe0, 0x3a, 0x7f, 0xc0, 0xef, 0x16, 0x38, 0x75, 0x7e, 0xae, - 0x1f, 0x10, 0x01, 0xaf, 0x2b, 0xc1, 0x76, 0x3f, 0x96, 0xa2, 0x07, 0x21, 0x53, 0x04, 0x9d, 0xd0, 0x52, 0xa4, 0x12, - 0x08, 0x44, 0x2b, 0x43, 0xaa, 0x43, 0x9b, 0x6f, 0xa3, 0x2c, 0xb4, 0xdf, 0xf3, 0x87, 0x1f, 0x08, 0x79, 0xde, 0xc4, - 0xc9, 0xc2, 0x47, 0x07, 0x7c, 0x3a, 0x46, 0x1d, 0x84, 0x0f, 0x07, 0xec, 0xcf, 0xc6, 0x71, 0x34, 0x91, 0x92, 0x0a, - 0x36, 0xb8, 0x24, 0x8a, 0x5b, 0xbf, 0x67, 0x7e, 0xa2, 0x9b, 0x94, 0x0d, 0x8b, 0xfb, 0xac, 0xed, 0x3c, 0xf3, 0x0e, - 0x9f, 0x1d, 0x39, 0xf0, 0xbf, 0xcb, 0xda, 0xb9, 0xc9, 0x0b, 0x2e, 0xe2, 0x08, 0x02, 0x9f, 0x88, 0x92, 0x9b, 0x8a, - 0xdd, 0x32, 0xf6, 0xa9, 0x28, 0x75, 0xdc, 0x5c, 0x68, 0xe2, 0xdf, 0x17, 0x65, 0x1a, 0x4b, 0xcc, 0xe3, 0x95, 0x32, - 0xac, 0x86, 0xd1, 0x04, 0xd1, 0x0a, 0x78, 0x6f, 0x4a, 0x09, 0x35, 0x9b, 0x4f, 0xb7, 0x98, 0x17, 0x6b, 0xe7, 0x57, - 0x45, 0x74, 0x25, 0x11, 0xe5, 0x65, 0x27, 0x04, 0xb9, 0xd8, 0xc2, 0x41, 0xdf, 0xec, 0x18, 0x5f, 0x48, 0x83, 0xd8, - 0x86, 0x62, 0x81, 0x7c, 0x5a, 0xa0, 0x2c, 0x59, 0x45, 0xe3, 0x16, 0xfe, 0xf4, 0x47, 0x69, 0x2b, 0x38, 0x00, 0xef, - 0xac, 0xd8, 0xb1, 0x81, 0xab, 0xe6, 0x9f, 0x3a, 0x45, 0x28, 0x8a, 0x54, 0xac, 0x8a, 0xff, 0x2c, 0x33, 0x13, 0x0a, - 0x60, 0x8b, 0x4b, 0x6b, 0x0d, 0xfc, 0x67, 0xb2, 0xe2, 0xb3, 0xcc, 0x84, 0x20, 0xb2, 0xb0, 0xdc, 0x4f, 0x9f, 0x52, - 0x29, 0x08, 0xeb, 0x48, 0xd3, 0x3a, 0x1b, 0x17, 0xee, 0xdd, 0x34, 0x7f, 0x16, 0x93, 0x87, 0xb7, 0xde, 0xd8, 0x8c, - 0x9f, 0x3f, 0x3f, 0x1d, 0xb8, 0x06, 0x0f, 0x4a, 0x5a, 0x8a, 0xa0, 0x75, 0xbe, 0x9f, 0xf2, 0x81, 0xd1, 0x68, 0x16, - 0xb7, 0x84, 0x37, 0x93, 0x23, 0x54, 0x94, 0x39, 0xf6, 0x82, 0x88, 0x16, 0x24, 0x64, 0xf4, 0x85, 0x12, 0x80, 0x28, - 0x23, 0x5f, 0x5d, 0x6d, 0xdb, 0xb1, 0x1d, 0x5d, 0x56, 0x9c, 0x06, 0xb3, 0xc1, 0x3a, 0xce, 0x7c, 0x88, 0x0d, 0x14, - 0xc6, 0x33, 0xb0, 0xad, 0xc9, 0x82, 0x2c, 0x84, 0x40, 0x33, 0x60, 0x64, 0xb3, 0xa0, 0x77, 0x79, 0xce, 0x35, 0x9e, - 0xfd, 0xe4, 0x13, 0x06, 0x1b, 0x14, 0x66, 0x75, 0xe8, 0x71, 0xe8, 0x47, 0xb8, 0x0c, 0x5b, 0x7a, 0x0b, 0x42, 0x5d, - 0xb2, 0x24, 0xb5, 0x54, 0x0b, 0x82, 0x9e, 0x06, 0x75, 0x20, 0x0c, 0x3d, 0x36, 0x30, 0x4d, 0xfc, 0x05, 0xf8, 0x64, - 0x5f, 0xe7, 0x26, 0xc7, 0xb4, 0x3a, 0x47, 0xb5, 0x9a, 0xfb, 0xe2, 0xc8, 0xd4, 0x3c, 0xd7, 0xd4, 0x1c, 0x40, 0xb7, - 0x7a, 0x6e, 0xae, 0xf3, 0xab, 0xfe, 0x2e, 0x21, 0x28, 0xe1, 0x97, 0xc7, 0x34, 0x0f, 0x12, 0x7f, 0x72, 0xf6, 0x72, - 0x46, 0x0e, 0x24, 0x5b, 0x8a, 0xb7, 0xf4, 0x80, 0x04, 0x21, 0x17, 0xec, 0x2e, 0x33, 0x30, 0x10, 0x0b, 0x2f, 0x12, - 0x18, 0x6b, 0x34, 0xfe, 0x0b, 0x22, 0x2d, 0xf8, 0xfc, 0xb9, 0x15, 0x80, 0x81, 0xc3, 0x40, 0x81, 0x0f, 0x7c, 0x1b, - 0x25, 0x80, 0x05, 0x85, 0xe8, 0x0e, 0x81, 0x05, 0xd6, 0x47, 0xf0, 0x6f, 0x91, 0x2c, 0x7e, 0x70, 0xd1, 0xa9, 0x1d, - 0xfa, 0xd1, 0x0c, 0x50, 0x9a, 0x1f, 0xcd, 0x6a, 0x2a, 0x1a, 0x64, 0xbf, 0x58, 0x49, 0x2d, 0x9a, 0x2a, 0xd4, 0x27, - 0xd2, 0xef, 0xef, 0x2f, 0x28, 0xd0, 0x14, 0x04, 0x35, 0xf7, 0x27, 0x68, 0x6c, 0x57, 0x48, 0x77, 0x9e, 0x0f, 0xbe, - 0x3d, 0x59, 0xb0, 0xcc, 0x27, 0xd6, 0x30, 0x3c, 0x7e, 0x81, 0x1c, 0xd0, 0xc6, 0x22, 0x48, 0x2c, 0x05, 0x93, 0x9f, - 0xb0, 0x9b, 0x60, 0xcc, 0xdf, 0xa5, 0xa6, 0xc6, 0xef, 0x29, 0x0b, 0xb5, 0xc0, 0x06, 0xae, 0x49, 0x4a, 0xc8, 0x63, - 0x1f, 0xdd, 0x4c, 0x0e, 0xa2, 0x58, 0x3f, 0xfd, 0x56, 0xda, 0x6b, 0x6d, 0x5a, 0x04, 0x88, 0xf6, 0x78, 0x99, 0xb0, - 0xf0, 0x5f, 0x83, 0x6f, 0xe1, 0xe2, 0xfe, 0xf6, 0x4a, 0x37, 0xfa, 0x99, 0x3d, 0x4f, 0xd8, 0x74, 0xf0, 0x6d, 0x43, - 0xd4, 0x43, 0x7c, 0xde, 0xd3, 0x58, 0xf4, 0xb6, 0x57, 0x38, 0x07, 0x6a, 0xef, 0xf5, 0xa8, 0x3f, 0xe5, 0xaf, 0x75, - 0x78, 0x01, 0xae, 0x4b, 0x6f, 0x6c, 0xb7, 0x8f, 0xef, 0xe7, 0x51, 0xe8, 0x8f, 0x3f, 0xf5, 0x29, 0xa7, 0xf4, 0x61, - 0xc1, 0x6d, 0x3d, 0xf6, 0x97, 0x3d, 0xbc, 0x5e, 0xd5, 0x44, 0x30, 0xd7, 0xa4, 0x54, 0x49, 0xd9, 0x35, 0xee, 0x65, - 0xdc, 0xca, 0x6b, 0xec, 0x19, 0xbb, 0xba, 0x9d, 0x07, 0x19, 0x13, 0x5d, 0xe1, 0x47, 0x9e, 0x8b, 0x87, 0x3a, 0x3d, - 0x51, 0xf1, 0x61, 0x6d, 0xb7, 0x35, 0xb7, 0xfb, 0xb7, 0xce, 0x8d, 0xeb, 0xcc, 0x3d, 0xd7, 0xee, 0x7e, 0x74, 0xbb, - 0xf3, 0xb6, 0x7d, 0x1c, 0x5a, 0x6d, 0xfb, 0x18, 0xfe, 0x7c, 0x3c, 0xb6, 0xbb, 0x73, 0xcb, 0xb3, 0x0f, 0x3f, 0xba, - 0x5e, 0x68, 0x75, 0xed, 0x63, 0xf8, 0x73, 0x4e, 0xb5, 0xe0, 0x01, 0x44, 0xef, 0x9d, 0x6f, 0x4b, 0x58, 0x40, 0xf9, - 0x2d, 0xe5, 0x34, 0x66, 0xe9, 0x7a, 0x6b, 0x90, 0xf5, 0x00, 0xca, 0xd0, 0x4d, 0xe1, 0x04, 0x32, 0xea, 0xb7, 0x20, - 0x0c, 0x3b, 0x06, 0x10, 0x10, 0x2a, 0x2f, 0xc2, 0x2e, 0x55, 0xb8, 0xd2, 0x6f, 0x3c, 0x46, 0xbc, 0x4e, 0xb3, 0xc3, - 0x75, 0x11, 0x99, 0x8a, 0x84, 0x43, 0xbf, 0x2c, 0xd1, 0x89, 0x91, 0x70, 0x11, 0xaf, 0x60, 0xa5, 0x22, 0x3a, 0x62, - 0xbe, 0x7b, 0xe0, 0x68, 0x99, 0xcb, 0x64, 0x74, 0x9e, 0xaf, 0xda, 0x36, 0x17, 0x18, 0xc9, 0xd6, 0xff, 0x68, 0x3b, - 0x18, 0x94, 0x96, 0xda, 0x11, 0xde, 0x5c, 0x27, 0x41, 0x22, 0x87, 0xa7, 0xa0, 0x68, 0xb7, 0xd9, 0x53, 0xbd, 0x01, - 0x61, 0x4c, 0xde, 0x02, 0x95, 0x7c, 0xe3, 0x87, 0x8a, 0x72, 0x8b, 0x52, 0xf3, 0x91, 0xc4, 0xfc, 0x4f, 0x9f, 0x16, - 0x83, 0xb3, 0x2a, 0xe3, 0x3e, 0x71, 0x3b, 0x70, 0xed, 0x76, 0x58, 0x7b, 0xab, 0x9e, 0xd5, 0x6e, 0x77, 0xc0, 0x85, - 0xbb, 0x50, 0xa1, 0x4b, 0x21, 0xa4, 0xb8, 0x1b, 0x95, 0xbd, 0x6a, 0x32, 0x5c, 0x70, 0xa4, 0x5c, 0x79, 0xea, 0xe8, - 0x46, 0x3f, 0x12, 0x22, 0xc9, 0x68, 0x8b, 0x0b, 0x64, 0xfe, 0x16, 0xd3, 0x01, 0x34, 0x5b, 0xe6, 0xb1, 0xc3, 0x68, - 0xf4, 0x7f, 0x3d, 0x09, 0x34, 0xe0, 0x02, 0x19, 0x6a, 0xe5, 0xb4, 0x96, 0x0c, 0x7a, 0xe4, 0xbd, 0x4a, 0x17, 0x2a, - 0x4b, 0xcf, 0x74, 0x48, 0x82, 0xf8, 0x56, 0x18, 0xd2, 0x4e, 0x2a, 0x90, 0xc9, 0xdb, 0xa2, 0x48, 0x30, 0x03, 0xf0, - 0x01, 0xde, 0x12, 0xc6, 0x64, 0xc6, 0xd3, 0xa7, 0x1b, 0x2f, 0x21, 0x12, 0xd8, 0xab, 0x91, 0x3d, 0x75, 0x15, 0xbf, - 0xe9, 0x2a, 0x8a, 0x91, 0xed, 0x22, 0xd6, 0x10, 0x7a, 0x6f, 0xb4, 0xf7, 0xf0, 0xe7, 0x88, 0xf9, 0x99, 0xcd, 0x25, - 0x4d, 0x2d, 0xe5, 0x72, 0x37, 0x5d, 0xd6, 0x06, 0x8d, 0x37, 0xee, 0xeb, 0x8c, 0xfb, 0x12, 0x7c, 0xb2, 0xfe, 0xb8, - 0xe2, 0x96, 0xde, 0xd0, 0xc6, 0x67, 0xa7, 0x70, 0x4f, 0xf3, 0x2e, 0xf3, 0xc9, 0x87, 0x89, 0x7a, 0xe5, 0xc6, 0x99, - 0x2f, 0xe2, 0xc8, 0x00, 0x5d, 0xde, 0x6f, 0x14, 0xc9, 0x2a, 0xd6, 0xe0, 0xa7, 0xef, 0x2e, 0xbe, 0xd3, 0xf8, 0xfe, - 0x27, 0x09, 0x22, 0x3e, 0x64, 0x28, 0xea, 0xc1, 0x80, 0xa2, 0x1e, 0x68, 0x3c, 0x8c, 0x08, 0xc4, 0x0e, 0xc8, 0x0f, - 0x08, 0x82, 0xc8, 0x80, 0x26, 0xb9, 0xea, 0x62, 0x15, 0x66, 0xc1, 0xd2, 0x4f, 0xb2, 0x03, 0xa8, 0x6a, 0x01, 0x92, - 0xd3, 0x37, 0xd9, 0x88, 0x93, 0x68, 0x56, 0xb8, 0xd8, 0xcb, 0x22, 0x21, 0x9b, 0x9d, 0x06, 0xa1, 0x14, 0xcd, 0x8a, - 0x0e, 0xfc, 0xf1, 0x98, 0x2d, 0xb3, 0x81, 0xee, 0x2f, 0x21, 0xfa, 0x05, 0xfa, 0xb3, 0x3e, 0x88, 0xc7, 0x19, 0xcb, - 0xac, 0x34, 0x4b, 0x98, 0xbf, 0xd0, 0xa5, 0x2b, 0xd7, 0x7a, 0x7b, 0xe9, 0x6a, 0xb4, 0x08, 0x32, 0xe9, 0x0b, 0x91, - 0x26, 0x08, 0x42, 0x52, 0x18, 0xe2, 0xe9, 0x30, 0xe7, 0x20, 0x3c, 0x8f, 0x67, 0x95, 0x1d, 0x55, 0x50, 0x2e, 0x67, - 0xe8, 0x69, 0x97, 0x47, 0x3c, 0x98, 0xa0, 0xcd, 0xd3, 0x35, 0xb7, 0x6b, 0x97, 0x2e, 0x1b, 0xf5, 0xd3, 0x13, 0xfe, - 0xbc, 0xd5, 0xd0, 0x15, 0x83, 0xde, 0x71, 0xc0, 0x97, 0xf0, 0x26, 0x8b, 0xf7, 0x03, 0x5e, 0x18, 0xae, 0x26, 0x6a, - 0x19, 0xfd, 0xbc, 0xd3, 0x58, 0x2e, 0x80, 0x10, 0x2a, 0x09, 0xd1, 0xe7, 0xee, 0xa9, 0x34, 0xb1, 0xc2, 0x51, 0x21, - 0xad, 0xf4, 0xf9, 0xf3, 0xcb, 0xe1, 0x7f, 0xfe, 0x0d, 0xce, 0xe8, 0xe7, 0xae, 0xb0, 0x33, 0xbf, 0x54, 0x4b, 0x71, - 0xea, 0xd3, 0x1c, 0xa2, 0x02, 0x05, 0x9b, 0x08, 0xc7, 0x2b, 0x62, 0x6b, 0xe5, 0xc3, 0x2b, 0xe1, 0x4c, 0x0b, 0x02, - 0x4e, 0x18, 0xc2, 0x1a, 0x7e, 0x08, 0xcb, 0x3b, 0x14, 0x4e, 0x18, 0xb4, 0xdf, 0xee, 0xbe, 0x3f, 0x06, 0x67, 0xcb, - 0xb5, 0x38, 0x10, 0xca, 0x00, 0x71, 0x0f, 0x9d, 0x9e, 0xf8, 0x1a, 0x12, 0x2d, 0x48, 0x7e, 0xa4, 0xbd, 0x03, 0x98, - 0xe6, 0x3c, 0x5e, 0x30, 0x3b, 0x88, 0x0f, 0x6e, 0xd9, 0xc8, 0xf2, 0x97, 0x01, 0xc9, 0xea, 0x91, 0xef, 0xa6, 0x11, - 0xe5, 0x27, 0x45, 0xe0, 0x44, 0x5f, 0xe7, 0x05, 0x28, 0xe3, 0x02, 0x50, 0xf0, 0xd3, 0x3f, 0x2d, 0xfb, 0x67, 0xb4, - 0x45, 0x84, 0x80, 0x32, 0x96, 0x3f, 0x23, 0x37, 0x8b, 0xc2, 0xa3, 0x62, 0xf1, 0x61, 0xc5, 0xd3, 0xa9, 0xea, 0x53, - 0xd1, 0x2e, 0xf7, 0x2f, 0xa1, 0x52, 0xec, 0xd9, 0x78, 0x49, 0x3d, 0xd5, 0xbb, 0x90, 0x3f, 0x21, 0x3a, 0x32, 0x77, - 0xbf, 0x09, 0xe7, 0xb9, 0xe6, 0x9b, 0x51, 0x82, 0xe4, 0x31, 0x15, 0xe2, 0x88, 0xa2, 0xea, 0x09, 0x7c, 0x03, 0x69, - 0xf2, 0x68, 0x30, 0x20, 0x3c, 0x56, 0x45, 0x67, 0x00, 0xa5, 0x86, 0x68, 0x09, 0x30, 0xd9, 0x0c, 0x2a, 0x5a, 0x64, - 0x23, 0x87, 0x95, 0xaa, 0xd3, 0xa9, 0x8f, 0xf1, 0xc0, 0x17, 0xfb, 0xab, 0xb4, 0x03, 0x61, 0x67, 0xf1, 0x85, 0x05, - 0x04, 0x2e, 0xda, 0xa9, 0xe0, 0x71, 0xed, 0xaf, 0x84, 0xb2, 0xad, 0xd0, 0xbf, 0x8f, 0x15, 0xdd, 0x05, 0xee, 0xc6, - 0xe0, 0x1c, 0x53, 0x2f, 0x84, 0xf9, 0x60, 0xed, 0x24, 0x49, 0x8f, 0xf3, 0xf5, 0xd3, 0xa4, 0xba, 0x88, 0xdf, 0x75, - 0x98, 0xd4, 0xb2, 0xe5, 0xc9, 0x20, 0x76, 0xcc, 0x8b, 0x83, 0x56, 0xca, 0xc4, 0x73, 0x9f, 0x9f, 0x1c, 0xc0, 0xfc, - 0xc0, 0xf5, 0x42, 0x89, 0x32, 0x0a, 0x0c, 0xf0, 0xef, 0xe0, 0xa7, 0xa4, 0x7f, 0xf1, 0x76, 0x22, 0x88, 0x3a, 0x7c, - 0x39, 0x4a, 0xe7, 0xaf, 0xa5, 0x22, 0x75, 0x62, 0xc5, 0x69, 0xa6, 0xf2, 0x76, 0x47, 0x68, 0xf8, 0x7d, 0x85, 0xe1, - 0x19, 0xf2, 0x7e, 0xc6, 0x84, 0x65, 0xf3, 0x79, 0xb6, 0xc1, 0xf8, 0x79, 0x53, 0x11, 0x22, 0x58, 0xb7, 0x14, 0x28, - 0xf6, 0xf1, 0xb6, 0x52, 0x05, 0x69, 0x24, 0x8b, 0x2d, 0xfd, 0x96, 0xfe, 0x18, 0x77, 0x7c, 0xad, 0x34, 0xa6, 0x42, - 0xb9, 0xf3, 0x6c, 0x00, 0x45, 0x05, 0xb3, 0xdd, 0x5f, 0x2e, 0xa9, 0xb0, 0xd1, 0x3f, 0x39, 0xa0, 0x77, 0xe7, 0x29, - 0xed, 0xb0, 0xd3, 0x13, 0xd0, 0xdf, 0xa4, 0x45, 0xf7, 0x97, 0x4b, 0xbe, 0xa4, 0xf4, 0x8b, 0x72, 0x0e, 0xe6, 0xd9, - 0x22, 0x3c, 0xfd, 0x3f, 0x1d, 0xdb, 0x6f, 0x83, 0x01, 0x5c, 0x03, 0x00}; + 0xdc, 0xb3, 0x37, 0xb7, 0x6d, 0x23, 0xff, 0x7f, 0x3f, 0x05, 0xc3, 0xe4, 0x52, 0x31, 0x21, 0x69, 0x92, 0xb2, 0x6c, + 0x45, 0xb2, 0xec, 0x6b, 0xf3, 0x98, 0x4b, 0xc7, 0x6d, 0x3a, 0x89, 0x9b, 0xb9, 0xab, 0xeb, 0xb1, 0x28, 0x09, 0x92, + 0x78, 0xa1, 0x48, 0x0d, 0x49, 0xf9, 0x51, 0x85, 0xf7, 0x59, 0xee, 0xb3, 0xdc, 0x27, 0xfb, 0xcd, 0xee, 0x02, 0x20, + 0xf8, 0xd0, 0xc3, 0x4d, 0x7a, 0xf7, 0x9b, 0x36, 0x89, 0x08, 0x02, 0x4b, 0x60, 0x01, 0x2c, 0x16, 0xfb, 0xf4, 0x67, + 0x5c, 0xf6, 0x62, 0xb6, 0xd8, 0x7e, 0x9f, 0xfb, 0xfc, 0x99, 0xd9, 0xb8, 0x24, 0x81, 0xe1, 0xb3, 0xb3, 0x78, 0x36, + 0x0b, 0x59, 0x4b, 0x17, 0xc9, 0x43, 0x74, 0x53, 0x7e, 0xe6, 0xec, 0x91, 0x23, 0x22, 0x76, 0x1a, 0xf9, 0xa6, 0xad, + 0x25, 0x46, 0xcc, 0x64, 0x48, 0x3b, 0xe2, 0x5c, 0x51, 0x36, 0x7b, 0x83, 0xea, 0x0d, 0x3e, 0x2f, 0xc5, 0xd6, 0xb5, + 0x26, 0xf1, 0x6a, 0x14, 0x32, 0x0b, 0x97, 0x3b, 0x7c, 0x72, 0x3d, 0x5a, 0x8d, 0x46, 0x90, 0xa5, 0xe5, 0x91, 0x63, + 0x42, 0xdc, 0x99, 0x38, 0xc5, 0xfb, 0x60, 0x6e, 0xf4, 0x61, 0x50, 0x76, 0x56, 0xed, 0x3e, 0xd8, 0x8a, 0x80, 0xa8, + 0x87, 0x3e, 0x90, 0xc1, 0xdd, 0xaf, 0x61, 0xd7, 0x0e, 0xf4, 0x0f, 0xb0, 0xfa, 0x52, 0xbd, 0xdf, 0xb4, 0xf5, 0x07, + 0x97, 0xfa, 0x07, 0xc4, 0x31, 0x66, 0x2f, 0x7e, 0x49, 0xab, 0x57, 0x37, 0x75, 0x52, 0x7a, 0xaf, 0x30, 0x8f, 0x01, + 0x08, 0x7d, 0x5f, 0x05, 0xfe, 0x2c, 0x8a, 0xd3, 0x2c, 0x18, 0xeb, 0x57, 0xfd, 0xb7, 0x41, 0xeb, 0x72, 0x91, 0xb5, + 0x8c, 0x2b, 0x73, 0x9c, 0xa9, 0x29, 0x50, 0x04, 0xc1, 0xc4, 0x0c, 0x28, 0x9b, 0x2a, 0xa9, 0x3b, 0x68, 0x6b, 0x45, + 0x41, 0x9a, 0xb1, 0xd2, 0x38, 0x1b, 0x40, 0xbd, 0x4a, 0x3e, 0x15, 0x4c, 0x0c, 0xa5, 0x63, 0x4b, 0xa3, 0x4f, 0x37, + 0x95, 0x97, 0xab, 0x35, 0x1e, 0xe5, 0x59, 0x71, 0x5a, 0x62, 0x0c, 0x60, 0xe1, 0x38, 0x43, 0xcf, 0x8f, 0x54, 0xa3, + 0xcf, 0xd2, 0xb9, 0x3b, 0xfc, 0xae, 0xcc, 0x17, 0xc0, 0xf9, 0x0d, 0x16, 0x17, 0x51, 0x9c, 0x69, 0x10, 0xd8, 0x06, + 0xbe, 0x38, 0xac, 0x1a, 0x89, 0x71, 0xa8, 0x2d, 0x23, 0xe7, 0xc4, 0xe0, 0x7b, 0x3c, 0xfc, 0x5a, 0x3c, 0xbc, 0x59, + 0x29, 0x82, 0x05, 0x5d, 0x16, 0x22, 0x98, 0xc0, 0x2c, 0x3e, 0x8f, 0x6f, 0xab, 0x7a, 0x90, 0x97, 0xc3, 0xdd, 0x67, + 0x6f, 0x4b, 0xb0, 0xc9, 0x22, 0xab, 0x5f, 0x8b, 0x27, 0x26, 0x15, 0x8c, 0x4e, 0x65, 0x4f, 0xa1, 0xe1, 0x87, 0xe0, + 0x61, 0x32, 0xb0, 0x13, 0xc3, 0xb3, 0x00, 0x48, 0x12, 0x3f, 0xa6, 0x87, 0xf9, 0xb5, 0x48, 0x9d, 0x2c, 0x12, 0x17, + 0x2b, 0x87, 0x33, 0x50, 0xd7, 0x68, 0xb9, 0xca, 0x30, 0xd4, 0x2e, 0x74, 0x80, 0xe5, 0xba, 0x86, 0xa1, 0x3b, 0x81, + 0x4a, 0x17, 0x6c, 0x62, 0xae, 0x6b, 0xc1, 0xa4, 0x5e, 0xc6, 0x99, 0x5e, 0x20, 0x5e, 0x48, 0xdf, 0x51, 0x50, 0x05, + 0x8f, 0x09, 0x1f, 0xc6, 0xd8, 0x2c, 0xe2, 0xd4, 0xb7, 0xc6, 0xa8, 0xd0, 0x69, 0xa0, 0x0c, 0x63, 0x82, 0xd3, 0x6f, + 0x85, 0x8d, 0x83, 0x85, 0xf0, 0x9b, 0xa5, 0x61, 0x0e, 0x9f, 0xac, 0xa3, 0xfc, 0xec, 0xc9, 0x3a, 0xcd, 0x07, 0x4f, + 0xd6, 0xbe, 0xb4, 0x15, 0xd0, 0x2f, 0x74, 0x32, 0x14, 0x18, 0x22, 0x1a, 0x86, 0xf9, 0x75, 0xe1, 0xb9, 0x53, 0x8c, + 0x17, 0x56, 0x19, 0x95, 0x6b, 0xa8, 0xba, 0x1f, 0x70, 0x05, 0xfd, 0x32, 0x09, 0x16, 0x7e, 0x72, 0x4f, 0xfa, 0x7c, + 0x53, 0x55, 0xfa, 0x1b, 0xba, 0x46, 0x84, 0x9e, 0x10, 0x40, 0x34, 0x5f, 0xd7, 0xfe, 0x2a, 0xcb, 0x18, 0x1f, 0xad, + 0x54, 0x6a, 0xc2, 0xb7, 0xae, 0xf5, 0xe7, 0xcc, 0x9e, 0xb0, 0xcc, 0x0f, 0x42, 0x6a, 0xd2, 0x17, 0xd9, 0xea, 0x6b, + 0xc3, 0x4b, 0xcb, 0xc3, 0x8b, 0xca, 0xeb, 0x07, 0x07, 0x43, 0x47, 0x00, 0xf5, 0x1b, 0x47, 0x86, 0x59, 0xac, 0x9a, + 0x67, 0x94, 0xde, 0xfd, 0x57, 0xa7, 0x83, 0xc1, 0x74, 0x44, 0x30, 0x1d, 0x2c, 0x1a, 0xc7, 0x13, 0xf6, 0xcb, 0xfb, + 0xb7, 0x32, 0x6d, 0x16, 0x48, 0x80, 0x86, 0x7c, 0x61, 0xa6, 0xc8, 0x3f, 0x24, 0xc8, 0x3b, 0x50, 0x82, 0x2b, 0x4d, + 0x2e, 0xa1, 0x24, 0xd7, 0xb5, 0x33, 0xea, 0x3b, 0x9b, 0x50, 0xaf, 0x07, 0x31, 0xb6, 0x4a, 0xf2, 0x93, 0x03, 0xaa, + 0x4d, 0xa7, 0x1d, 0x55, 0x02, 0x34, 0x24, 0x30, 0xc2, 0x02, 0x0b, 0x90, 0xe1, 0x73, 0xe0, 0x16, 0x17, 0x0a, 0x7b, + 0x81, 0x72, 0x76, 0xf7, 0xac, 0xcc, 0xaa, 0x60, 0x2b, 0xfd, 0xf4, 0x04, 0x73, 0x76, 0xc1, 0x79, 0x0d, 0x51, 0x3e, + 0x4e, 0x0e, 0xe8, 0x51, 0xab, 0xec, 0x88, 0x02, 0x88, 0xb8, 0xda, 0xf5, 0x38, 0x80, 0x07, 0x6d, 0x15, 0x48, 0x11, + 0x0f, 0xa5, 0x7e, 0xae, 0x6b, 0x0b, 0xce, 0x1a, 0xf1, 0x70, 0x42, 0x10, 0x6b, 0xc0, 0x81, 0xbd, 0xab, 0x6b, 0x0b, + 0xff, 0x0e, 0x47, 0x2e, 0xde, 0xf8, 0x77, 0x2d, 0x97, 0xbf, 0x2a, 0xf6, 0x5a, 0x5a, 0xde, 0x6b, 0x63, 0x3e, 0xb9, + 0xe0, 0x48, 0x20, 0x6f, 0xd6, 0x73, 0x54, 0xd0, 0x36, 0x4c, 0xee, 0x5c, 0x4c, 0xee, 0x64, 0xc3, 0xe4, 0x4e, 0xb6, + 0x4c, 0x6e, 0xc8, 0x27, 0x52, 0x93, 0xa8, 0x4b, 0xd0, 0x39, 0x4c, 0x22, 0x8f, 0x33, 0x1a, 0x3d, 0xbe, 0xcf, 0x10, + 0x4f, 0x56, 0x1a, 0x82, 0x71, 0xd4, 0x06, 0x5c, 0x35, 0xe1, 0x45, 0x41, 0x44, 0x7d, 0xe0, 0x72, 0xd7, 0x89, 0x71, + 0x43, 0x0e, 0xce, 0x56, 0x58, 0x1d, 0x2f, 0xac, 0x52, 0xca, 0x2f, 0xde, 0x9a, 0x6f, 0x18, 0xe9, 0x7c, 0xcb, 0x48, + 0xc7, 0xa5, 0xad, 0xcb, 0x87, 0x4d, 0x9b, 0x50, 0x1d, 0x14, 0xac, 0x41, 0x30, 0x18, 0xc5, 0x25, 0x53, 0x5e, 0x87, + 0x9b, 0x69, 0xac, 0xb2, 0xa2, 0x96, 0x7e, 0x9a, 0xde, 0xc6, 0x09, 0x68, 0x5c, 0x00, 0xcc, 0xc3, 0x96, 0xd4, 0x22, + 0x88, 0x78, 0x30, 0x97, 0x8d, 0x8b, 0xa9, 0x78, 0xaf, 0x2e, 0x29, 0xaf, 0xd3, 0xa1, 0x1a, 0x4b, 0x3f, 0xcb, 0x58, + 0x82, 0x48, 0xf7, 0x21, 0xea, 0xf7, 0xff, 0x93, 0x65, 0xd6, 0x40, 0x43, 0x42, 0x85, 0xaa, 0x23, 0x85, 0x5e, 0x02, + 0x6f, 0x95, 0x88, 0x83, 0x58, 0x09, 0x0c, 0x97, 0x48, 0xc4, 0xff, 0x84, 0xdb, 0xb5, 0x95, 0x28, 0xae, 0x4b, 0xee, + 0x91, 0x61, 0x2f, 0xfd, 0xc9, 0x07, 0x50, 0xec, 0xb5, 0x3c, 0x13, 0x8c, 0x74, 0xd5, 0x30, 0x70, 0x09, 0x31, 0x7b, + 0xe3, 0x82, 0x48, 0x22, 0x95, 0xe4, 0x26, 0x50, 0xe0, 0x3d, 0xe9, 0x5b, 0xd3, 0xab, 0xb5, 0x97, 0x1f, 0xcc, 0x02, + 0xa3, 0x46, 0x35, 0x81, 0xb4, 0x85, 0x83, 0x53, 0x79, 0xe7, 0x0a, 0x4d, 0xf7, 0xc8, 0x00, 0xc9, 0xef, 0x25, 0xe4, + 0x33, 0x75, 0xc4, 0x85, 0x76, 0x98, 0xc0, 0xa9, 0x75, 0xe9, 0x5c, 0xe5, 0x4f, 0x67, 0xf8, 0xcb, 0xbd, 0xca, 0x9f, + 0x8e, 0xf0, 0x97, 0x77, 0x85, 0x99, 0xeb, 0x1a, 0x2e, 0xf2, 0xca, 0x98, 0xf5, 0xd3, 0xd2, 0x7a, 0x22, 0xfb, 0xb3, + 0x07, 0x2c, 0x1b, 0x3e, 0xc1, 0x8f, 0x9f, 0xac, 0x53, 0xf0, 0xb8, 0x54, 0xc7, 0x10, 0xd9, 0x89, 0x91, 0x37, 0x96, + 0xcf, 0x36, 0x94, 0x8f, 0x8c, 0xff, 0xf2, 0xc1, 0x8f, 0xab, 0x24, 0x2e, 0xce, 0x94, 0xb2, 0x18, 0xe2, 0x7a, 0x14, + 0x44, 0x7e, 0x72, 0x7f, 0x4d, 0xd7, 0x8b, 0x96, 0xe0, 0xdd, 0xa5, 0x78, 0x85, 0xd8, 0xcb, 0xb2, 0xba, 0x2b, 0x53, + 0x04, 0xbc, 0xf7, 0xfc, 0xa0, 0x1f, 0xfc, 0x3d, 0x51, 0xd8, 0xb6, 0xd2, 0x05, 0x94, 0x4f, 0x48, 0xe9, 0x43, 0xd7, + 0x4f, 0xd6, 0x2d, 0x56, 0x07, 0x53, 0x19, 0x6d, 0x85, 0x2f, 0x84, 0xe9, 0xc1, 0xcb, 0xec, 0x62, 0x12, 0xf4, 0x50, + 0x9f, 0x35, 0x8a, 0xef, 0xac, 0x27, 0xeb, 0xec, 0x4c, 0x5f, 0xf8, 0xc9, 0x27, 0x36, 0xb1, 0xc6, 0x41, 0x32, 0x0e, + 0x99, 0xde, 0xd3, 0x47, 0xa1, 0x1f, 0x7d, 0xe2, 0x8f, 0x56, 0xbc, 0xca, 0x50, 0x43, 0xbd, 0xf3, 0xee, 0x2b, 0x70, + 0x42, 0x22, 0x3b, 0x64, 0x56, 0x1b, 0xb0, 0xa0, 0xbd, 0x94, 0x02, 0xaf, 0x82, 0x51, 0x2c, 0x6a, 0x99, 0x60, 0x60, + 0x09, 0x4a, 0x73, 0xf0, 0x58, 0x35, 0x75, 0x9c, 0x2f, 0xdd, 0x54, 0x87, 0x4a, 0xc2, 0x4a, 0x99, 0x72, 0xf1, 0x1a, + 0x21, 0xfc, 0xf1, 0xcf, 0x51, 0x32, 0xec, 0xfd, 0x3f, 0x27, 0xa1, 0x7c, 0xd9, 0x08, 0xa1, 0xd4, 0x22, 0x4f, 0x89, + 0x07, 0x7c, 0x9c, 0x33, 0x98, 0x9b, 0x3f, 0xad, 0x36, 0xf6, 0xd3, 0x74, 0xb5, 0x60, 0x13, 0xd2, 0x0c, 0x9e, 0x15, + 0x9d, 0x2a, 0xdf, 0x2c, 0xd4, 0x8e, 0xfd, 0xb6, 0xf2, 0x8e, 0x0f, 0x5f, 0x82, 0xc5, 0x02, 0x30, 0x94, 0xf1, 0x74, + 0xaa, 0x17, 0x77, 0xfc, 0x1d, 0xcd, 0xdc, 0xc3, 0xdf, 0x56, 0x6f, 0x5e, 0x3b, 0x6f, 0x64, 0xe3, 0x08, 0x18, 0x63, + 0xa1, 0x7e, 0xe5, 0x7c, 0xb1, 0xd2, 0x5f, 0x31, 0xa2, 0xa9, 0x1f, 0x6d, 0x1e, 0xce, 0x65, 0x69, 0x89, 0x2f, 0x19, + 0x9b, 0x00, 0xc3, 0x6d, 0xd6, 0x4a, 0xaf, 0x43, 0x76, 0xc3, 0xa4, 0x6a, 0xb7, 0xfe, 0xb1, 0x86, 0x16, 0x18, 0x7b, + 0x8e, 0xab, 0x8c, 0x39, 0x57, 0xa7, 0x0c, 0x69, 0x88, 0x63, 0xe0, 0x23, 0x57, 0xb7, 0x58, 0x65, 0x4b, 0x0d, 0x4d, + 0x5d, 0xe9, 0xc0, 0xc6, 0x9e, 0x9d, 0x6d, 0x28, 0xef, 0x61, 0xe2, 0xe9, 0xe6, 0xbe, 0x99, 0xae, 0xd1, 0x83, 0x58, + 0xdd, 0x1c, 0x4f, 0x21, 0xec, 0xbc, 0x56, 0x21, 0x0e, 0xd9, 0x84, 0xb1, 0x26, 0x21, 0x99, 0x4e, 0xd2, 0x17, 0x61, + 0xed, 0x88, 0x66, 0xbf, 0x42, 0x0e, 0xd5, 0x38, 0x37, 0x5a, 0x79, 0xe4, 0x23, 0x4c, 0xe8, 0x1a, 0xb1, 0x34, 0xdd, + 0x88, 0x30, 0x39, 0xe9, 0xa6, 0x5e, 0xd4, 0x2e, 0xe3, 0xa3, 0x28, 0x37, 0x1d, 0x13, 0x58, 0x02, 0x1c, 0x60, 0xf5, + 0x5b, 0x78, 0xbc, 0x5c, 0x2f, 0xb8, 0xbd, 0x4a, 0x32, 0x1b, 0xe9, 0xdc, 0x96, 0x60, 0xd3, 0xfb, 0x5b, 0x9d, 0x77, + 0xaa, 0x74, 0x4c, 0x37, 0x76, 0xad, 0x55, 0x22, 0xbd, 0x35, 0x71, 0x11, 0x02, 0x10, 0x7d, 0xaa, 0xd0, 0x57, 0x36, + 0x9d, 0xb2, 0x71, 0x96, 0x1a, 0x42, 0x78, 0x24, 0xa3, 0xc7, 0x82, 0xd7, 0xd0, 0xa3, 0x81, 0xfe, 0x13, 0xf8, 0xd0, + 0x8b, 0x20, 0x4b, 0xbc, 0x43, 0xe2, 0xce, 0xd4, 0x8c, 0x26, 0x82, 0x58, 0x46, 0x11, 0xff, 0x0a, 0x24, 0x07, 0x6f, + 0x28, 0xc7, 0xae, 0xf1, 0xf3, 0xa7, 0x58, 0x17, 0xb1, 0xb4, 0x6a, 0xd9, 0x4e, 0x8a, 0xb6, 0x6d, 0xdf, 0xb5, 0xfb, + 0xa6, 0xe3, 0x3a, 0xb9, 0x6e, 0x82, 0xef, 0xd6, 0xa7, 0x7d, 0x37, 0x3d, 0xb6, 0x6a, 0x43, 0xab, 0x55, 0xf4, 0x90, + 0x76, 0x9e, 0xfb, 0xc2, 0xd5, 0x4d, 0x32, 0x99, 0x53, 0x68, 0xdb, 0x38, 0xbe, 0x61, 0xc9, 0x17, 0x0f, 0xa5, 0x0c, + 0x7c, 0xbf, 0xfe, 0x1c, 0xb9, 0x0e, 0x10, 0xe1, 0x2c, 0x5e, 0x3e, 0x60, 0x08, 0x6d, 0xdd, 0xd4, 0xc7, 0x61, 0x9c, + 0x32, 0x75, 0x0c, 0x24, 0x04, 0xf9, 0xc2, 0x41, 0xfc, 0xfc, 0xfe, 0xf5, 0x87, 0x0f, 0xba, 0x89, 0x99, 0x40, 0x53, + 0x15, 0x3a, 0x5f, 0x50, 0x3b, 0xa8, 0x7f, 0xe3, 0xba, 0xa3, 0x13, 0x86, 0x2e, 0xb5, 0xe5, 0x35, 0x47, 0x65, 0xb5, + 0x25, 0xc7, 0x4f, 0x1e, 0xfe, 0x65, 0xba, 0x89, 0xee, 0x35, 0xae, 0x06, 0xda, 0xb0, 0xfd, 0x78, 0x2b, 0x95, 0x2c, + 0x82, 0xe8, 0xba, 0xa1, 0xd4, 0xbf, 0x6b, 0x28, 0x85, 0xab, 0x5c, 0x8d, 0x56, 0xad, 0xe2, 0x85, 0xc2, 0x1a, 0x40, + 0x22, 0xe7, 0x5d, 0xe8, 0x52, 0xee, 0x53, 0x5f, 0xd0, 0x69, 0x1e, 0xc9, 0xbd, 0xda, 0xeb, 0x86, 0x62, 0x7e, 0x09, + 0x92, 0xb8, 0x1d, 0x87, 0x60, 0xf0, 0xc7, 0x54, 0xad, 0x5c, 0x99, 0x6d, 0x94, 0xe6, 0xba, 0x0a, 0x10, 0x62, 0x6f, + 0xaf, 0x33, 0xb6, 0x58, 0xb2, 0xc4, 0xcf, 0x56, 0x09, 0xbb, 0x0e, 0xe3, 0xdb, 0x47, 0x85, 0x39, 0xfd, 0x8e, 0xca, + 0xf3, 0x60, 0x36, 0x97, 0xb5, 0xcf, 0x5a, 0x6c, 0x20, 0x27, 0x70, 0xeb, 0x07, 0xf2, 0xff, 0xfc, 0xdb, 0xb6, 0xff, + 0xf3, 0xef, 0x9d, 0x55, 0x01, 0x7c, 0x3e, 0x34, 0xb3, 0xc1, 0x1e, 0xeb, 0xa2, 0xf9, 0x4b, 0x65, 0x9c, 0x37, 0xd7, + 0xa9, 0x4d, 0x02, 0xbc, 0xaf, 0x4d, 0x41, 0xad, 0xb0, 0xbc, 0x6e, 0x1e, 0xd4, 0x31, 0x18, 0xd7, 0xce, 0x9e, 0x41, + 0xa5, 0x2f, 0xea, 0xda, 0xd0, 0xe8, 0xed, 0x35, 0x23, 0x7f, 0x1c, 0xc3, 0xbb, 0xc6, 0xf0, 0x85, 0xdd, 0xe7, 0x72, + 0xc9, 0x97, 0xc3, 0xa1, 0xcc, 0x2d, 0xa7, 0x36, 0x05, 0x13, 0xff, 0xb3, 0x5a, 0x09, 0x3f, 0x3c, 0x7b, 0x8e, 0x41, + 0xbe, 0xf7, 0x83, 0x97, 0x43, 0x34, 0x46, 0x3b, 0x19, 0x25, 0x05, 0xb3, 0xb2, 0x91, 0xb4, 0x91, 0x31, 0x79, 0x0d, + 0x68, 0x8d, 0xae, 0x41, 0x29, 0x26, 0x1c, 0xcb, 0x87, 0x86, 0xf9, 0x72, 0xc8, 0x05, 0x4b, 0xdc, 0xfe, 0xb5, 0x57, + 0x5d, 0xda, 0x5c, 0x2c, 0x5b, 0x42, 0xba, 0xa9, 0x91, 0xfe, 0x07, 0x2b, 0xb3, 0x42, 0x8e, 0x87, 0x02, 0x7e, 0x90, + 0x28, 0x0c, 0x73, 0xcc, 0x77, 0xf2, 0x6e, 0x93, 0x8d, 0xd8, 0xcf, 0xbb, 0x6d, 0xc4, 0x2e, 0xf6, 0xb2, 0x11, 0xfb, + 0xf9, 0xab, 0xdb, 0x88, 0xbd, 0x53, 0x6d, 0xc4, 0x60, 0x12, 0x5f, 0xb3, 0xbd, 0x0c, 0xb7, 0x84, 0xd5, 0x46, 0x7c, + 0x9b, 0x0e, 0x5c, 0xce, 0xd2, 0xa6, 0xe3, 0x39, 0x03, 0x19, 0x01, 0x9f, 0x95, 0x30, 0x9e, 0x81, 0x11, 0xd7, 0x9f, + 0x6f, 0x6e, 0x15, 0xc6, 0x33, 0xd5, 0xd8, 0x2a, 0xe2, 0x11, 0x5f, 0x8b, 0x28, 0x4e, 0x64, 0xe0, 0xe4, 0x98, 0x22, + 0xe6, 0x93, 0x75, 0x68, 0x28, 0x59, 0xad, 0xa5, 0xf5, 0x9a, 0x27, 0x4c, 0xa0, 0x7a, 0x68, 0x3d, 0x25, 0x1b, 0x7a, + 0xcf, 0x45, 0x6c, 0x0b, 0x15, 0x82, 0xb4, 0x12, 0xa6, 0x38, 0x11, 0x6b, 0xfd, 0xb7, 0x3b, 0xf7, 0xfb, 0x4b, 0xb7, + 0xdf, 0x76, 0xc1, 0x39, 0x1b, 0x6e, 0x98, 0x58, 0xe0, 0xf4, 0xdb, 0x6d, 0x28, 0xb8, 0x55, 0x0a, 0x3c, 0x28, 0x08, + 0x94, 0x82, 0x0e, 0x14, 0x8c, 0x95, 0x82, 0x23, 0x28, 0x98, 0x28, 0x05, 0xc7, 0x50, 0x70, 0xa3, 0xe7, 0x97, 0x91, + 0xec, 0xee, 0xb1, 0x71, 0x65, 0xd2, 0xa5, 0x42, 0x94, 0x1d, 0x9b, 0x2e, 0x58, 0x4d, 0xf9, 0xb3, 0x5e, 0x6c, 0x92, + 0x74, 0xb1, 0x97, 0x98, 0xb7, 0x73, 0x46, 0x81, 0xa2, 0x5f, 0xe1, 0x99, 0x63, 0x67, 0x31, 0xd8, 0x4d, 0x8b, 0x00, + 0x0c, 0x02, 0x0f, 0x9a, 0x6e, 0x80, 0xc0, 0xa8, 0x2f, 0x67, 0x4e, 0x04, 0xb1, 0x50, 0xe6, 0xb2, 0x78, 0x47, 0x9f, + 0xb3, 0xe4, 0x12, 0x28, 0x2c, 0x4e, 0x5a, 0xaa, 0x54, 0xf2, 0x6b, 0xd8, 0x1d, 0xbc, 0x62, 0xa3, 0xd5, 0x4c, 0x3b, + 0x8f, 0x67, 0x3b, 0x4d, 0x08, 0xd4, 0x57, 0xd0, 0x4b, 0x9d, 0xd4, 0x2f, 0x96, 0x58, 0x96, 0xfc, 0x5b, 0xf4, 0x98, + 0x97, 0xeb, 0x67, 0xd0, 0x37, 0x2d, 0x23, 0x03, 0x16, 0xf8, 0x0e, 0xe0, 0x48, 0xd1, 0xe1, 0x9f, 0x03, 0x9e, 0x95, + 0xe7, 0x0b, 0x5f, 0xe9, 0xcf, 0xe9, 0x8f, 0x2c, 0x4d, 0xfd, 0x99, 0xa8, 0x5f, 0xef, 0x27, 0x18, 0xed, 0xc8, 0xfb, + 0x17, 0x22, 0x10, 0x24, 0x79, 0x41, 0xcd, 0x36, 0x23, 0x89, 0x6f, 0x35, 0xb0, 0xfe, 0x81, 0x05, 0x55, 0xd8, 0x29, + 0x04, 0x36, 0x4c, 0x61, 0xd9, 0xa2, 0x00, 0x36, 0xff, 0x0d, 0x0b, 0xab, 0x85, 0x99, 0x3f, 0xab, 0x16, 0xd1, 0x3a, + 0xc8, 0xd5, 0xbe, 0x49, 0x85, 0x7e, 0xa9, 0xf0, 0x4b, 0x34, 0xd4, 0x61, 0x3c, 0xfb, 0x53, 0xd5, 0xd3, 0x5b, 0xcc, + 0x0a, 0x3e, 0x44, 0x66, 0x90, 0x0d, 0x6d, 0xc4, 0xb1, 0x66, 0x03, 0x0a, 0x7b, 0x51, 0x36, 0xb7, 0xd0, 0xb5, 0xac, + 0xe5, 0x45, 0x86, 0x69, 0xe3, 0xdc, 0xae, 0xab, 0x0e, 0xb5, 0xbd, 0x64, 0x36, 0xf2, 0x5b, 0xae, 0x77, 0x6c, 0x8a, + 0x3f, 0xb6, 0xd3, 0x31, 0x72, 0x84, 0xa0, 0x4d, 0x82, 0x9b, 0xf5, 0x34, 0x8e, 0x32, 0x6b, 0xea, 0x2f, 0x82, 0xf0, + 0xbe, 0xb7, 0x88, 0xa3, 0x38, 0x5d, 0xfa, 0x63, 0xd6, 0x2f, 0x2e, 0xd4, 0x7d, 0x0c, 0xd5, 0xc0, 0xbd, 0x05, 0x5d, + 0xdb, 0x4b, 0xd8, 0x82, 0x5a, 0xcb, 0x48, 0x0c, 0xd3, 0x90, 0xdd, 0xe5, 0xfc, 0xf3, 0xa5, 0xca, 0x54, 0x15, 0x97, + 0x1c, 0xb5, 0x00, 0x8e, 0x94, 0x87, 0x79, 0x80, 0xe0, 0x46, 0xfd, 0xa5, 0x3f, 0xc1, 0xc8, 0x84, 0xb6, 0xd7, 0x49, + 0xd8, 0x42, 0xb3, 0x3b, 0x1b, 0x81, 0x27, 0xf1, 0xed, 0x29, 0xf4, 0x16, 0x1b, 0x5b, 0x29, 0x0b, 0xa7, 0xf8, 0xc6, + 0x42, 0xcf, 0x12, 0x01, 0xc7, 0xc2, 0x8b, 0x38, 0x40, 0x63, 0x8b, 0x3e, 0xbc, 0xee, 0x79, 0x9a, 0xd3, 0x5f, 0x04, + 0x91, 0x45, 0xc3, 0x39, 0x76, 0x96, 0x0a, 0x2c, 0x15, 0x7f, 0xc6, 0x1a, 0xab, 0xbb, 0x9a, 0xd3, 0x87, 0xcb, 0xda, + 0x34, 0x8c, 0x6f, 0x7b, 0xf3, 0x60, 0x32, 0x61, 0x51, 0x1f, 0xfb, 0x2c, 0x0b, 0x59, 0x18, 0x06, 0xcb, 0x34, 0x48, + 0xfb, 0x0b, 0xff, 0x8e, 0x43, 0x3d, 0xdc, 0x04, 0xb5, 0xcd, 0xa1, 0xb6, 0xf7, 0x86, 0xaa, 0x80, 0x01, 0x2f, 0x16, + 0x82, 0xc3, 0xbb, 0xd6, 0xd1, 0x9c, 0xca, 0x38, 0xf7, 0x86, 0xba, 0x4c, 0xd8, 0x7a, 0xe1, 0x27, 0xb3, 0x20, 0xea, + 0x39, 0xb9, 0x7d, 0xb3, 0xa6, 0x85, 0xf1, 0xb8, 0xdb, 0xed, 0xe6, 0xf6, 0x44, 0x3c, 0x39, 0x93, 0x49, 0x6e, 0x8f, + 0xc5, 0xd3, 0x74, 0xea, 0x38, 0xd3, 0x69, 0x6e, 0x07, 0xa2, 0xa0, 0xed, 0x8d, 0x27, 0x6d, 0x2f, 0xb7, 0x6f, 0x95, + 0x1a, 0xb9, 0xcd, 0xf8, 0x53, 0xc2, 0x26, 0x7d, 0x5c, 0x48, 0x64, 0x56, 0xda, 0x3b, 0x76, 0x9c, 0x1c, 0x29, 0xc0, + 0x65, 0x89, 0x36, 0xa1, 0xac, 0xe7, 0x6a, 0xbd, 0x77, 0x4d, 0xad, 0xf8, 0xdc, 0x78, 0xdc, 0x58, 0x6f, 0xe2, 0x27, + 0x9f, 0xae, 0x34, 0x65, 0x14, 0xbe, 0x4f, 0xd5, 0xd6, 0x02, 0x0d, 0xd6, 0x5d, 0x0f, 0x42, 0x76, 0xf5, 0x47, 0x71, + 0x02, 0x7b, 0x36, 0xf1, 0x27, 0xc1, 0x2a, 0xed, 0xb9, 0xde, 0xf2, 0x4e, 0x14, 0xf1, 0xb5, 0x5e, 0x14, 0xe0, 0xde, + 0xeb, 0xa5, 0x71, 0x18, 0x4c, 0x44, 0xd1, 0xa6, 0xbd, 0xe4, 0x7a, 0x46, 0x1f, 0x1d, 0xd6, 0x03, 0x0c, 0xbb, 0xe0, + 0x87, 0xa1, 0x66, 0xb7, 0x53, 0x8d, 0xf9, 0x29, 0xca, 0x97, 0x35, 0x27, 0x25, 0xbc, 0xa0, 0x73, 0xba, 0x7b, 0xb8, + 0xbc, 0x93, 0x6b, 0xde, 0x3d, 0x5a, 0xde, 0xe5, 0x7f, 0x5d, 0xb0, 0x49, 0xe0, 0x6b, 0xad, 0x62, 0x35, 0xb9, 0x0e, + 0xc8, 0xa0, 0x8d, 0xf5, 0x86, 0x65, 0x2a, 0xb6, 0x05, 0x84, 0x36, 0x7c, 0x14, 0x2c, 0x96, 0x71, 0x92, 0xf9, 0x51, + 0x96, 0xe7, 0xc3, 0xab, 0x3c, 0xef, 0x5f, 0x04, 0xad, 0xcb, 0x7f, 0xb4, 0xe8, 0x9c, 0x26, 0x9d, 0x4d, 0x6e, 0x5c, + 0x99, 0xaf, 0x99, 0x6a, 0x33, 0x02, 0xc7, 0x18, 0xda, 0x8b, 0xa8, 0x95, 0xe9, 0x94, 0xac, 0x57, 0x26, 0x24, 0xcb, + 0xea, 0x64, 0x83, 0x52, 0xae, 0x82, 0x27, 0x10, 0x54, 0x78, 0xcd, 0x06, 0x17, 0x8a, 0xfd, 0x09, 0x30, 0x2b, 0x58, + 0x99, 0xfc, 0x0a, 0x9e, 0x6c, 0xe2, 0x19, 0xbf, 0xdb, 0xcd, 0x33, 0xfe, 0x9a, 0xed, 0xc3, 0x33, 0x7e, 0xf7, 0xd5, + 0x79, 0xc6, 0x27, 0x75, 0xbf, 0x82, 0xb7, 0xf1, 0x40, 0x97, 0x1a, 0x06, 0x38, 0x9a, 0x12, 0x8a, 0xd8, 0xf3, 0xf6, + 0x0f, 0xbb, 0x01, 0x08, 0x68, 0x94, 0x83, 0x8e, 0x4e, 0x6e, 0x90, 0xc7, 0xbe, 0x8b, 0x06, 0x7f, 0x4f, 0xd4, 0xe7, + 0xe9, 0x74, 0xf0, 0x2a, 0x56, 0x0a, 0xe4, 0x13, 0x37, 0xbe, 0x28, 0x45, 0x57, 0xa0, 0x37, 0xc2, 0x0a, 0x13, 0xf3, + 0x4f, 0x80, 0x73, 0x36, 0x59, 0x1d, 0x4f, 0xa4, 0xf5, 0x59, 0xbf, 0xdc, 0x85, 0x96, 0x34, 0xf9, 0x14, 0x2e, 0x38, + 0x35, 0x51, 0xe2, 0x8c, 0x65, 0xdc, 0x67, 0xf6, 0xfb, 0xfb, 0xb7, 0x93, 0xd6, 0xdb, 0xd8, 0xc8, 0x83, 0xf4, 0x5d, + 0xd5, 0x01, 0x86, 0xeb, 0x7e, 0x06, 0xea, 0x70, 0x72, 0x6e, 0x41, 0xa6, 0x26, 0x98, 0x86, 0xd7, 0xd4, 0xfc, 0xac, + 0x34, 0xd2, 0x9e, 0xda, 0x90, 0x27, 0xba, 0xaa, 0x1d, 0xc6, 0xdc, 0xfb, 0x60, 0xcd, 0x39, 0x40, 0xcc, 0xdd, 0x85, + 0x7e, 0xc3, 0x13, 0x6a, 0x1e, 0x4c, 0xf2, 0xdc, 0xe8, 0x0b, 0x44, 0x28, 0x07, 0x2d, 0xdb, 0xc5, 0xc4, 0xa5, 0xb7, + 0xd2, 0xa6, 0x81, 0x6b, 0x08, 0x49, 0xfd, 0xf7, 0x16, 0x14, 0xea, 0x5c, 0x59, 0xc8, 0x71, 0xa6, 0x6b, 0x84, 0x3e, + 0x32, 0xb4, 0x50, 0x06, 0x04, 0x1a, 0x60, 0x89, 0x7f, 0xf1, 0x4a, 0x14, 0xd4, 0x6d, 0x38, 0x09, 0x39, 0x68, 0x11, + 0x00, 0x5e, 0xfe, 0x42, 0xae, 0x4d, 0x64, 0x87, 0xd7, 0xc1, 0x87, 0x5c, 0x97, 0xbc, 0x1f, 0x2e, 0xbf, 0xd3, 0x93, + 0x03, 0x68, 0x70, 0x5a, 0x31, 0x1c, 0xd8, 0x61, 0xa1, 0x08, 0xac, 0x44, 0x7a, 0x6b, 0xda, 0xe9, 0xad, 0xf6, 0x6c, + 0x2d, 0x22, 0x64, 0x64, 0xfe, 0xd2, 0x82, 0x2b, 0x3e, 0xd2, 0x5e, 0x4e, 0xf1, 0x94, 0x60, 0x1c, 0xfd, 0x55, 0x0a, + 0xb4, 0x11, 0x2f, 0xaa, 0x48, 0x7f, 0xfa, 0xe3, 0x55, 0x92, 0xc6, 0x49, 0x6f, 0x19, 0x07, 0x51, 0xc6, 0x92, 0x1c, + 0x51, 0x75, 0x89, 0xf8, 0x11, 0xe8, 0xb9, 0x5a, 0xc7, 0x4b, 0x7f, 0x1c, 0x64, 0xf7, 0x3d, 0x87, 0xb3, 0x14, 0x4e, + 0x9f, 0x73, 0x07, 0x4e, 0x63, 0xfd, 0x1e, 0xc7, 0xe6, 0x73, 0x64, 0xfc, 0x92, 0x3a, 0x3b, 0xa3, 0x2e, 0xf3, 0xbe, + 0xf2, 0x96, 0x62, 0x84, 0x00, 0xfb, 0xe1, 0x27, 0xd6, 0x0c, 0xa8, 0x3c, 0x4c, 0xb5, 0x33, 0x61, 0x33, 0x13, 0xa9, + 0x36, 0xc8, 0xe5, 0xc5, 0x1f, 0xbb, 0x63, 0x68, 0x4e, 0x73, 0x31, 0x70, 0x3c, 0xc6, 0x3e, 0x3d, 0xeb, 0xf9, 0x90, + 0x51, 0xcb, 0xdc, 0xa7, 0xe6, 0x88, 0x4d, 0xe3, 0x84, 0x51, 0x3c, 0x59, 0xb7, 0xbb, 0xbc, 0xdb, 0x1f, 0xfc, 0xf6, + 0xe1, 0x37, 0xc3, 0x89, 0xe2, 0xac, 0x25, 0x80, 0x19, 0x3b, 0xa0, 0xd5, 0xcf, 0x33, 0x60, 0x0d, 0x09, 0xf3, 0x63, + 0x0a, 0xdd, 0xd5, 0xd3, 0xf5, 0x7e, 0x63, 0xd8, 0xae, 0x65, 0xcc, 0xcf, 0xbc, 0x84, 0x85, 0x7e, 0x16, 0xdc, 0x08, + 0x9e, 0xb1, 0x7d, 0xb4, 0xbc, 0x13, 0x73, 0x8c, 0x07, 0xde, 0x03, 0x26, 0xa9, 0xd2, 0x15, 0x31, 0x49, 0xd5, 0x62, + 0x9c, 0xa4, 0x7e, 0x6d, 0x34, 0x22, 0x92, 0x45, 0xe5, 0xa4, 0xef, 0x2c, 0xef, 0xd4, 0x23, 0xba, 0x68, 0x26, 0x4f, + 0xea, 0x6a, 0x08, 0xb2, 0x45, 0x30, 0x99, 0x84, 0x2c, 0x2f, 0x4d, 0x74, 0x79, 0x2e, 0x15, 0xe4, 0x48, 0x3c, 0xf8, + 0xa3, 0x34, 0x0e, 0x57, 0x19, 0x6b, 0x46, 0x17, 0x21, 0xc7, 0x73, 0x0a, 0xe4, 0xe0, 0xef, 0x72, 0x5f, 0x3b, 0xc0, + 0x6e, 0xc3, 0x32, 0x71, 0xfa, 0x10, 0x71, 0xd8, 0x6a, 0x97, 0xbb, 0x0e, 0xaf, 0x64, 0xa7, 0xcd, 0x86, 0x81, 0x98, + 0x70, 0x2c, 0x11, 0xf5, 0xd6, 0x6c, 0x97, 0x97, 0xc9, 0xa8, 0xab, 0xb2, 0x28, 0x2f, 0x0f, 0xe6, 0xcf, 0xd9, 0x63, + 0x2f, 0x9a, 0xf7, 0xd8, 0x0b, 0xb1, 0xc7, 0xb6, 0xaf, 0xcc, 0xc7, 0x53, 0x17, 0xfe, 0xeb, 0x17, 0x03, 0xea, 0x39, + 0x5a, 0x7b, 0x79, 0xa7, 0xb9, 0xcb, 0x3b, 0xcd, 0xf2, 0x96, 0x77, 0x1a, 0x82, 0x46, 0x7b, 0x10, 0xd3, 0xf6, 0x0c, + 0xd3, 0xd1, 0xa0, 0x10, 0xfe, 0x38, 0xa5, 0x57, 0xee, 0x21, 0xbc, 0x83, 0x56, 0x9d, 0xfa, 0x3b, 0x6f, 0xfb, 0x56, + 0xa7, 0xbd, 0x24, 0x88, 0xb6, 0x61, 0x67, 0xfe, 0x68, 0xc4, 0x26, 0xbd, 0x69, 0x3c, 0x5e, 0xa5, 0xff, 0xe2, 0xfd, + 0xe7, 0x48, 0xdc, 0x4a, 0x08, 0x2a, 0x70, 0x44, 0x53, 0x50, 0x94, 0xdc, 0x30, 0x01, 0x61, 0x2d, 0xe7, 0xa9, 0x47, + 0xe1, 0x91, 0x3d, 0xfb, 0xb0, 0x61, 0x91, 0x37, 0x23, 0xfa, 0x4f, 0x9b, 0xa5, 0xcd, 0x24, 0xe6, 0x0b, 0xd0, 0xb2, + 0x15, 0x1d, 0x0f, 0xc7, 0x06, 0x9f, 0x4d, 0xa7, 0xdb, 0xdc, 0xdd, 0x4b, 0xf1, 0xa5, 0x2b, 0x71, 0xa8, 0xf0, 0x73, + 0x8b, 0x3b, 0xa6, 0x6c, 0x87, 0xba, 0x69, 0x8d, 0xd4, 0xa0, 0x6e, 0x39, 0x10, 0x8a, 0xba, 0x7b, 0x52, 0xf9, 0xc7, + 0x2f, 0x0e, 0xe1, 0x3f, 0xe2, 0xea, 0x7f, 0xcd, 0x9a, 0x18, 0xf5, 0xb7, 0x65, 0x4b, 0x70, 0x62, 0x95, 0x90, 0x11, + 0xdf, 0xbf, 0xfe, 0x74, 0xfa, 0xb0, 0x06, 0x7b, 0xd7, 0x26, 0x53, 0xaa, 0x6a, 0xed, 0xef, 0xe3, 0x18, 0x52, 0x77, + 0xd6, 0xab, 0x0b, 0xf4, 0x90, 0xb1, 0x7b, 0x36, 0x80, 0x46, 0xe2, 0x1e, 0x41, 0x5a, 0x7c, 0x1d, 0xdb, 0xd0, 0x55, + 0xe2, 0xf5, 0xa6, 0xab, 0xc4, 0xab, 0xdd, 0x57, 0x89, 0x1f, 0xf6, 0xba, 0x4a, 0xbc, 0xfa, 0xea, 0x57, 0x89, 0xd7, + 0xf5, 0xab, 0xc4, 0x45, 0x2c, 0xec, 0x67, 0xcd, 0xb7, 0x2b, 0xfe, 0xf3, 0x23, 0x29, 0xe5, 0xce, 0xe3, 0x41, 0xc7, + 0xa1, 0x90, 0xc7, 0x17, 0x7f, 0xf8, 0x62, 0x81, 0x0b, 0xf1, 0x3d, 0x9a, 0x93, 0x15, 0x57, 0x0b, 0x4e, 0xd9, 0xf1, + 0x3b, 0x4a, 0x71, 0x18, 0x47, 0xb3, 0x9f, 0x41, 0x29, 0x0b, 0xe2, 0xc0, 0x44, 0x79, 0x11, 0xa4, 0x3f, 0xc7, 0xcb, + 0xd5, 0xf2, 0x2d, 0xc0, 0xfa, 0x18, 0xa4, 0xc1, 0x28, 0x64, 0xd2, 0x13, 0x99, 0xcc, 0xdf, 0xb8, 0x4c, 0x1c, 0x2c, + 0x4e, 0xc5, 0x4f, 0xff, 0x4e, 0xfc, 0x44, 0x9b, 0x54, 0xfe, 0x9b, 0xec, 0xea, 0xf4, 0xe6, 0x8b, 0x88, 0x50, 0x02, + 0x2a, 0x9d, 0x7e, 0xf8, 0x65, 0xe4, 0x22, 0x36, 0x1a, 0x46, 0x29, 0xec, 0x1d, 0x36, 0xc2, 0x61, 0xb5, 0x4b, 0xcd, + 0xca, 0x30, 0x65, 0x08, 0xae, 0xba, 0x18, 0x7e, 0x11, 0xaf, 0x52, 0x36, 0x89, 0x6f, 0x23, 0xdd, 0x8c, 0xa4, 0x93, + 0x01, 0x68, 0x38, 0x65, 0x1b, 0x4c, 0x1e, 0xf9, 0x01, 0x19, 0xe5, 0x38, 0x69, 0xe9, 0x90, 0xbb, 0x74, 0xb5, 0xb4, + 0x48, 0xd5, 0x6c, 0xe1, 0x10, 0x75, 0x99, 0xe5, 0xe8, 0x51, 0xab, 0x15, 0x0f, 0x1e, 0xd6, 0x52, 0x98, 0x6a, 0xc4, + 0x36, 0x97, 0x0a, 0xa7, 0xad, 0x48, 0x08, 0x17, 0x45, 0x1c, 0x8c, 0x86, 0x89, 0xe3, 0x6f, 0xc8, 0x75, 0xb5, 0x78, + 0x0b, 0x51, 0x44, 0xf2, 0x15, 0x9f, 0x0f, 0x1e, 0x15, 0x82, 0x1e, 0x5f, 0x2a, 0x68, 0x7c, 0x77, 0xc3, 0x92, 0xd0, + 0xbf, 0x6f, 0x19, 0x79, 0x1c, 0xfd, 0x08, 0x08, 0x78, 0x15, 0xdf, 0x46, 0x6a, 0x05, 0x4c, 0xd6, 0xd2, 0xb0, 0x96, + 0x1a, 0xe3, 0x97, 0x80, 0xe3, 0x8a, 0xd2, 0x03, 0x48, 0x93, 0x3b, 0x63, 0x7f, 0x37, 0xe9, 0xdf, 0x7f, 0x18, 0xb9, + 0x79, 0x1e, 0xcb, 0x0f, 0xfd, 0xb2, 0xdc, 0xe3, 0x33, 0x4f, 0x9f, 0x3e, 0xda, 0x3c, 0xec, 0x72, 0x7a, 0xf6, 0x86, + 0xd6, 0xc6, 0xc6, 0x5d, 0x00, 0xbd, 0xb8, 0x88, 0x57, 0xe3, 0x39, 0x1a, 0xba, 0x7e, 0xbd, 0xf1, 0x66, 0x00, 0x13, + 0xb3, 0x94, 0xca, 0xa1, 0x57, 0x8a, 0x0a, 0x2c, 0xe0, 0xf7, 0x5f, 0x43, 0x00, 0xce, 0xff, 0x21, 0x1a, 0xea, 0xab, + 0x86, 0xdf, 0xe2, 0x83, 0x87, 0x2d, 0xde, 0x3e, 0x24, 0xd3, 0xe4, 0xa1, 0x2d, 0x84, 0x72, 0xad, 0x99, 0xc8, 0xe4, + 0x55, 0xa4, 0xa9, 0x61, 0xe4, 0x36, 0x45, 0xc8, 0x13, 0x5f, 0x61, 0x36, 0x5d, 0xd3, 0xb9, 0xa3, 0x81, 0xc9, 0x38, + 0xb5, 0xaa, 0x10, 0x19, 0x6e, 0xf2, 0xc0, 0x90, 0x7c, 0x55, 0xdf, 0x2d, 0x82, 0xc8, 0xc4, 0x28, 0xf0, 0xf5, 0x37, + 0xfe, 0x1d, 0xc4, 0x41, 0x06, 0xe2, 0x56, 0x7d, 0x05, 0x85, 0xa6, 0xea, 0x37, 0x07, 0xa9, 0x9e, 0xf4, 0x46, 0x4c, + 0x08, 0x2d, 0xde, 0xf0, 0x1b, 0x4d, 0xd3, 0x34, 0x79, 0x8d, 0xd0, 0xe4, 0x3d, 0x02, 0xcb, 0xf1, 0x3a, 0x00, 0xda, + 0x92, 0x7c, 0x79, 0x47, 0x25, 0x70, 0x33, 0x40, 0x9d, 0xac, 0x28, 0xe0, 0xa1, 0xfe, 0x3a, 0x8e, 0x28, 0x10, 0x17, + 0x7a, 0x08, 0xd3, 0xe6, 0x27, 0x10, 0x11, 0xb8, 0xa7, 0xe1, 0x85, 0x1d, 0xdf, 0x72, 0x49, 0xb0, 0xe6, 0xd0, 0xe3, + 0xb0, 0xcf, 0x9a, 0x63, 0xc2, 0x45, 0x0a, 0x15, 0x04, 0xad, 0x43, 0x25, 0xc4, 0xb3, 0xc9, 0x1a, 0x68, 0x23, 0xde, + 0x8b, 0xee, 0xb2, 0x05, 0x8b, 0x56, 0x3a, 0xe6, 0x84, 0xc2, 0x18, 0x7d, 0x50, 0xe7, 0x15, 0x31, 0x5b, 0x40, 0x6d, + 0x9a, 0x5b, 0xce, 0xe9, 0x2c, 0x4c, 0x39, 0x49, 0xf5, 0xcd, 0x31, 0x57, 0x6c, 0xa6, 0x9c, 0xb6, 0x55, 0x4f, 0x08, + 0x3e, 0xa5, 0x71, 0xd5, 0x91, 0x8b, 0x2c, 0xa1, 0x01, 0x06, 0x45, 0xc7, 0xe0, 0xe2, 0x22, 0x81, 0xf6, 0x96, 0x5f, + 0x9d, 0x34, 0xa9, 0x91, 0xf1, 0x2b, 0x82, 0xa2, 0xc4, 0xa8, 0x83, 0xe1, 0xfd, 0x84, 0xc0, 0x44, 0x1b, 0xe1, 0x8c, + 0x6b, 0x70, 0x36, 0x0c, 0xfa, 0x13, 0xbb, 0xa7, 0x83, 0x84, 0x50, 0xf5, 0x89, 0xdd, 0x83, 0xed, 0xdf, 0x6b, 0x90, + 0xa6, 0xe8, 0x5b, 0xc8, 0xb5, 0x09, 0xa1, 0xfe, 0xc7, 0x10, 0xac, 0x6a, 0xcb, 0x06, 0x72, 0xf2, 0x2d, 0x54, 0x1c, + 0x51, 0x0c, 0x59, 0x9d, 0xc5, 0x26, 0xe6, 0x26, 0xfe, 0xad, 0x46, 0x1c, 0x5b, 0x0d, 0x5b, 0xc3, 0x78, 0xe6, 0x3a, + 0xce, 0x41, 0xad, 0x3e, 0x08, 0xb2, 0x9b, 0x6a, 0x1b, 0x66, 0x36, 0x70, 0x1d, 0x2b, 0x78, 0x66, 0x7b, 0xfd, 0xda, + 0x19, 0xad, 0xc4, 0x92, 0x1c, 0xa2, 0xf8, 0xeb, 0xf4, 0xc9, 0xba, 0x55, 0xdb, 0x90, 0x46, 0xd5, 0x64, 0x1e, 0xfb, + 0x96, 0x73, 0xf9, 0xd7, 0xb0, 0x7e, 0xf4, 0x53, 0x24, 0x4b, 0xca, 0x6b, 0x32, 0x84, 0x68, 0xc8, 0x2d, 0xd8, 0x46, + 0x7f, 0xd1, 0x9e, 0x6b, 0x2d, 0xda, 0x3e, 0x86, 0x31, 0x94, 0xe9, 0xb2, 0x85, 0x4f, 0x99, 0x0a, 0xa0, 0xf2, 0xc5, + 0xb4, 0x4a, 0xe1, 0x78, 0xdc, 0x55, 0x56, 0x68, 0xf4, 0xb6, 0x72, 0x0b, 0x08, 0x7f, 0xc3, 0xf1, 0x69, 0x8f, 0x20, + 0x2e, 0x01, 0xd4, 0x80, 0xd8, 0xe9, 0x3b, 0x01, 0xae, 0x96, 0x65, 0x70, 0xe5, 0x43, 0x72, 0x7f, 0x60, 0x78, 0xe8, + 0xa0, 0x0e, 0x4d, 0xc2, 0x6b, 0x3e, 0xee, 0x1e, 0x08, 0x92, 0x45, 0x93, 0x32, 0xc0, 0xca, 0xf9, 0xb5, 0x3f, 0xb8, + 0x12, 0x45, 0x81, 0xa4, 0x02, 0x71, 0x03, 0x45, 0xc9, 0xe3, 0x08, 0x17, 0x3f, 0x6d, 0xb7, 0x60, 0x2f, 0x2e, 0x06, + 0x1b, 0x50, 0x44, 0x30, 0xd9, 0x4c, 0x11, 0x8a, 0x43, 0xe4, 0x6a, 0x74, 0x0b, 0x6e, 0x09, 0x46, 0x74, 0xe3, 0x4a, + 0xcc, 0x84, 0x4d, 0x61, 0xd1, 0x26, 0xe0, 0xb1, 0x28, 0xf7, 0x95, 0x5a, 0x07, 0xbb, 0xa5, 0xd6, 0xd9, 0x2e, 0xa9, + 0x35, 0xb9, 0x53, 0xdd, 0x26, 0xfe, 0x52, 0xf1, 0xc8, 0x13, 0xcc, 0xb9, 0xea, 0x98, 0x57, 0x12, 0x75, 0xa3, 0xf7, + 0x95, 0x68, 0x55, 0x83, 0x46, 0x56, 0x82, 0x28, 0xfe, 0x56, 0x2e, 0x28, 0x42, 0xa1, 0xae, 0xca, 0xc6, 0x2f, 0x0a, + 0xd9, 0x38, 0xdd, 0x6a, 0x0a, 0x47, 0x1a, 0xc1, 0xfd, 0x2b, 0x4e, 0x6a, 0xf2, 0x76, 0x50, 0x38, 0xab, 0x15, 0x3d, + 0x55, 0xdc, 0xaf, 0x8a, 0x8b, 0x86, 0xe2, 0xd4, 0x27, 0x6e, 0x19, 0x65, 0xdf, 0xbe, 0x72, 0xd5, 0xc2, 0xfb, 0xaa, + 0x28, 0x07, 0xa9, 0x3b, 0x76, 0x59, 0x16, 0xab, 0xcb, 0xa6, 0xec, 0x7e, 0xa3, 0xbe, 0x56, 0x16, 0x89, 0xf4, 0x93, + 0x21, 0x04, 0x0b, 0x31, 0x7d, 0x45, 0xaf, 0x2d, 0x6d, 0x20, 0xb0, 0x93, 0x0d, 0x6e, 0x7d, 0xbb, 0xa5, 0xf3, 0x94, + 0x2f, 0xa1, 0xd0, 0xc2, 0xab, 0x32, 0x08, 0xc4, 0xef, 0xd5, 0xba, 0xe1, 0x94, 0xc7, 0x43, 0x9e, 0x9f, 0xef, 0x20, + 0x5e, 0xd4, 0x1c, 0x55, 0x91, 0x8f, 0x3b, 0xd3, 0x22, 0xf3, 0x5c, 0xac, 0x5a, 0x07, 0x4a, 0x42, 0x9c, 0x35, 0xf7, + 0x8c, 0x29, 0xcb, 0xe8, 0x79, 0x8d, 0x9e, 0xf8, 0x2e, 0x5f, 0x3a, 0xc9, 0x2a, 0xc2, 0xd8, 0xf6, 0x56, 0x96, 0xf8, + 0xe3, 0x4f, 0x4a, 0x97, 0x85, 0x9c, 0x13, 0x64, 0xc0, 0x65, 0x4d, 0x41, 0xdf, 0xc7, 0x50, 0x90, 0xac, 0x67, 0x7b, + 0xa9, 0x22, 0x7d, 0xe9, 0x3d, 0x76, 0xda, 0xfe, 0x8b, 0xe9, 0x61, 0x45, 0x28, 0xea, 0x75, 0xca, 0x22, 0xf3, 0x0d, + 0xfd, 0xc8, 0xe6, 0xab, 0xc5, 0x68, 0xad, 0xca, 0x56, 0x15, 0x91, 0x6b, 0x5d, 0xcc, 0xaa, 0x7e, 0x76, 0x3a, 0x9d, + 0x96, 0x05, 0x8d, 0x8e, 0x76, 0x88, 0xc2, 0xc2, 0xc7, 0x8e, 0xe3, 0x54, 0xfb, 0xbe, 0x1d, 0xed, 0x16, 0xca, 0x6d, + 0xbb, 0x8d, 0x3d, 0x46, 0xdc, 0xee, 0xc2, 0x5f, 0x1d, 0x1d, 0xb9, 0x5d, 0xec, 0xec, 0x92, 0x59, 0x44, 0x9f, 0x8c, + 0x21, 0x82, 0x8c, 0x2d, 0xd2, 0xde, 0x98, 0xa1, 0x0e, 0xc6, 0x56, 0x36, 0x34, 0x1a, 0x0e, 0x58, 0x33, 0x30, 0x15, + 0x71, 0xc5, 0xaa, 0x70, 0x34, 0x94, 0x87, 0xd7, 0x84, 0xf7, 0xe2, 0x23, 0xb8, 0x51, 0xd6, 0x75, 0x99, 0x36, 0x0e, + 0xab, 0xe3, 0xfc, 0xa5, 0x54, 0x4f, 0x83, 0x03, 0x70, 0x2d, 0x14, 0xda, 0x24, 0x9f, 0xc5, 0xbf, 0xa5, 0xfc, 0xff, + 0xc5, 0xf2, 0xae, 0x6c, 0x3f, 0xd2, 0x05, 0x89, 0x76, 0xb1, 0x5b, 0xa8, 0xd7, 0x4d, 0x6b, 0x40, 0x5a, 0x19, 0x4c, + 0x55, 0x05, 0x3a, 0x28, 0xe9, 0x4b, 0x09, 0x40, 0x1a, 0xc4, 0xef, 0xc8, 0x31, 0xc3, 0x14, 0x17, 0x22, 0xc4, 0x22, + 0x7d, 0x1d, 0x8c, 0xc1, 0x7c, 0xde, 0x45, 0xfd, 0x41, 0x69, 0x4d, 0x80, 0x36, 0xbe, 0x36, 0xb6, 0xbd, 0xc4, 0xfd, + 0x55, 0xbd, 0x96, 0x00, 0x0c, 0x28, 0x73, 0x61, 0x13, 0xa2, 0x21, 0x81, 0x56, 0x59, 0xdc, 0xd4, 0x4b, 0xf9, 0x56, + 0xd5, 0xb3, 0x89, 0x8e, 0x21, 0xb8, 0xe6, 0x2a, 0x04, 0x5b, 0x68, 0x0b, 0x60, 0xb0, 0x7c, 0xf9, 0xe1, 0xb3, 0x05, + 0x53, 0xac, 0xae, 0x47, 0x17, 0xa7, 0x1c, 0xd7, 0xaf, 0x85, 0x67, 0x67, 0x4a, 0xfb, 0x1f, 0xe5, 0x8b, 0x3f, 0x34, + 0x0a, 0xf4, 0x2e, 0x4a, 0x12, 0x3a, 0x6e, 0x2d, 0xee, 0x19, 0x7b, 0xd5, 0x5e, 0x04, 0xd1, 0xfe, 0x75, 0xfd, 0xbb, + 0xbd, 0xeb, 0xc2, 0x81, 0xb1, 0x77, 0x65, 0x38, 0x71, 0xc8, 0x72, 0x21, 0x1b, 0xfc, 0xa0, 0x08, 0x14, 0x55, 0xaf, + 0x63, 0x1d, 0x5b, 0x11, 0x97, 0x7f, 0xb1, 0x1a, 0x0c, 0x4f, 0xce, 0xee, 0x16, 0xa1, 0x76, 0xc3, 0x12, 0x48, 0xed, + 0x33, 0xd0, 0x5d, 0xdb, 0xd1, 0x35, 0xf4, 0xa1, 0x0d, 0xa2, 0xd9, 0x40, 0xff, 0xe5, 0xe2, 0x8d, 0xd5, 0xd5, 0xcf, + 0x40, 0x45, 0x7b, 0x33, 0xc3, 0x63, 0xef, 0xdc, 0xbf, 0x67, 0xc9, 0xb5, 0xa7, 0x6b, 0x98, 0xc1, 0x87, 0x0e, 0x3c, + 0x2c, 0xd3, 0x3c, 0x7d, 0x8f, 0x44, 0x11, 0x9a, 0xc8, 0xf5, 0xa6, 0x03, 0xc9, 0x71, 0xbd, 0xae, 0xe6, 0x7a, 0x87, + 0xf6, 0x51, 0x57, 0x3f, 0xfd, 0x46, 0xd3, 0x4e, 0x26, 0x6c, 0x9a, 0x9e, 0xe2, 0x15, 0xed, 0x04, 0xcf, 0x08, 0xfa, + 0xad, 0x69, 0xf6, 0x38, 0x4c, 0x2d, 0x57, 0x5b, 0xf3, 0x47, 0x4d, 0x9b, 0x06, 0x61, 0xd8, 0xd3, 0x1e, 0x4f, 0xbd, + 0xe9, 0xe1, 0xf4, 0x45, 0x9f, 0x17, 0xe7, 0xdf, 0x94, 0xaa, 0x9b, 0xf4, 0xaf, 0xa7, 0x34, 0x4b, 0xb3, 0x24, 0xfe, + 0xc4, 0xb8, 0xd9, 0x89, 0x26, 0x2f, 0x8f, 0xd5, 0xa6, 0x5e, 0xfd, 0x4b, 0x6e, 0x77, 0x34, 0x9e, 0x7a, 0x45, 0x75, + 0xec, 0xe3, 0x81, 0xec, 0xe4, 0xc9, 0x81, 0xe8, 0xfa, 0x89, 0x8a, 0x26, 0xd7, 0x6a, 0x42, 0x94, 0xab, 0xf3, 0x31, + 0xce, 0xc4, 0xf8, 0x4e, 0x20, 0x0e, 0xa3, 0x74, 0xd7, 0x85, 0x1e, 0xe8, 0xda, 0x64, 0xa0, 0xff, 0xe8, 0x7a, 0x5d, + 0xd3, 0x75, 0x8f, 0xec, 0xa3, 0xee, 0xd8, 0x31, 0x0f, 0xed, 0x43, 0xab, 0x6d, 0x1f, 0x99, 0x5d, 0xab, 0x6b, 0x76, + 0xff, 0xd6, 0x1d, 0x5b, 0x87, 0xf6, 0xa1, 0xe9, 0x58, 0x5d, 0x28, 0xb4, 0xba, 0x56, 0xf7, 0xc6, 0x3a, 0xec, 0x8e, + 0x1d, 0x2c, 0xf5, 0xec, 0x4e, 0xc7, 0x72, 0x1d, 0xbb, 0xd3, 0x31, 0x3b, 0xf6, 0xd1, 0x91, 0xe5, 0xb6, 0xed, 0xa3, + 0xa3, 0xf3, 0x4e, 0xd7, 0x6e, 0xc3, 0xbb, 0x76, 0x7b, 0xdc, 0xb6, 0x5d, 0xd7, 0x82, 0xbf, 0xcc, 0xae, 0xed, 0xd1, + 0x0f, 0xd7, 0xb5, 0xdb, 0xae, 0xe9, 0x84, 0x1d, 0xcf, 0x3e, 0x7a, 0x61, 0xe2, 0xdf, 0x58, 0xcd, 0xc4, 0xbf, 0x00, + 0x8c, 0xf9, 0xc2, 0xf6, 0x8e, 0xe8, 0x17, 0x02, 0xbc, 0x39, 0xec, 0xfe, 0xaa, 0x1f, 0x6c, 0x1c, 0x83, 0x4b, 0x63, + 0xe8, 0x76, 0xec, 0x76, 0xdb, 0x3c, 0x74, 0xed, 0x6e, 0x7b, 0x6e, 0x1d, 0x7a, 0xf6, 0xd1, 0xf1, 0xd8, 0x72, 0xed, + 0xe3, 0x63, 0xd3, 0xb1, 0xda, 0xb6, 0x67, 0xba, 0xf6, 0x61, 0x1b, 0x7f, 0xb4, 0x6d, 0xef, 0xe6, 0xf8, 0x85, 0x7d, + 0xd4, 0x99, 0x1f, 0xd9, 0x87, 0x1f, 0x0f, 0xbb, 0xb6, 0xd7, 0x9e, 0xb7, 0x8f, 0x6c, 0xef, 0xf8, 0xe6, 0xc8, 0x3e, + 0x9c, 0x5b, 0xde, 0xd1, 0xd6, 0x96, 0xae, 0x67, 0x03, 0x8e, 0xf0, 0x35, 0xbc, 0x30, 0xf9, 0x0b, 0xf8, 0x33, 0xc7, + 0xb6, 0xff, 0x45, 0x30, 0x69, 0xbd, 0xe9, 0x0b, 0xbb, 0x7b, 0x3c, 0xa6, 0xea, 0x50, 0x60, 0x89, 0x1a, 0xd0, 0xe4, + 0xc6, 0xa2, 0xcf, 0x22, 0x38, 0x4b, 0x00, 0x12, 0x7f, 0xf8, 0xc7, 0x6e, 0x2c, 0xf8, 0x30, 0x7d, 0xf7, 0x7f, 0x0a, + 0x47, 0x4e, 0x39, 0x64, 0xae, 0xfc, 0x86, 0xff, 0x43, 0x49, 0x5f, 0x86, 0xe6, 0xf9, 0x26, 0x45, 0xc5, 0xfb, 0xdd, + 0x8a, 0x8a, 0x37, 0xab, 0x7d, 0x14, 0x15, 0xef, 0xbf, 0xba, 0xa2, 0xe2, 0xbc, 0x6a, 0x27, 0xff, 0xbe, 0x1a, 0x9b, + 0xfe, 0xd7, 0x75, 0xf5, 0x1a, 0x12, 0xf8, 0xad, 0xcb, 0x8b, 0xd5, 0x15, 0x44, 0x57, 0x7a, 0x1f, 0x0f, 0xde, 0xac, + 0x4a, 0x46, 0x60, 0x31, 0xd0, 0xd8, 0xf7, 0x31, 0xd1, 0xd8, 0xdf, 0x57, 0x03, 0xb0, 0x3c, 0xe1, 0x7c, 0x49, 0x30, + 0xb1, 0xe6, 0x7e, 0x38, 0x95, 0x3c, 0x0d, 0x94, 0xf4, 0xb1, 0x18, 0xbc, 0x12, 0xe0, 0xb8, 0x06, 0x75, 0xd8, 0x6a, + 0x11, 0xa5, 0xbd, 0x23, 0x07, 0x0e, 0x52, 0x6f, 0x9a, 0xe4, 0x95, 0xc6, 0xb6, 0x88, 0x47, 0x75, 0xcd, 0xbd, 0x26, + 0x36, 0xbe, 0x47, 0xa3, 0xc0, 0x66, 0xe8, 0x6e, 0x1d, 0xae, 0x06, 0xd6, 0x36, 0xc2, 0x68, 0x12, 0xd8, 0xb9, 0xa6, + 0xf7, 0x65, 0xd3, 0xbc, 0x8a, 0x31, 0xe6, 0xe6, 0x9e, 0x42, 0x4f, 0xaa, 0xed, 0xdd, 0xb2, 0x69, 0xdf, 0xae, 0x61, + 0x36, 0x7c, 0xbe, 0xd4, 0x7c, 0x8b, 0x5d, 0xa1, 0x04, 0x5c, 0x45, 0x55, 0x25, 0xb3, 0x5a, 0x23, 0x42, 0x0a, 0xee, + 0xbe, 0x30, 0x3e, 0x2c, 0x58, 0x4b, 0x47, 0x43, 0x7e, 0xc7, 0x51, 0xde, 0x95, 0x60, 0xaa, 0x06, 0x8b, 0xcf, 0xd6, + 0xc8, 0x71, 0x07, 0xbf, 0x03, 0xeb, 0xc8, 0x39, 0x9e, 0x51, 0xac, 0xe2, 0x79, 0xad, 0xc0, 0xa5, 0xcb, 0x4c, 0x3e, + 0x77, 0xd7, 0x75, 0xe6, 0x71, 0xa3, 0xa9, 0xb2, 0xcb, 0x16, 0x82, 0x0b, 0xc2, 0xcf, 0x93, 0x61, 0x70, 0x4e, 0xc6, + 0xdb, 0x68, 0xfb, 0xbc, 0x0d, 0x98, 0xa8, 0xf7, 0x18, 0x16, 0xb1, 0xc9, 0x1f, 0xd4, 0xb8, 0x00, 0xeb, 0x29, 0x64, + 0xc1, 0xee, 0x21, 0x9b, 0xa6, 0xf0, 0xa8, 0x1e, 0x5a, 0x31, 0xf7, 0xb7, 0x18, 0xd8, 0xa8, 0x80, 0x39, 0x10, 0xb4, + 0x86, 0xde, 0x66, 0x93, 0x23, 0x9d, 0x47, 0xd6, 0x25, 0x15, 0xb5, 0xdb, 0x39, 0x36, 0xdd, 0x23, 0xd3, 0x3e, 0xee, + 0x18, 0xb9, 0xd8, 0x70, 0x2a, 0xc8, 0x12, 0x42, 0xc0, 0x28, 0x5a, 0x76, 0x33, 0x88, 0x82, 0x2c, 0xf0, 0xc3, 0x1c, + 0xf8, 0xe3, 0xf2, 0xad, 0xe2, 0x9f, 0xab, 0x34, 0x83, 0x31, 0x0a, 0xa6, 0x17, 0x0d, 0xc2, 0xad, 0x11, 0xcb, 0x6e, + 0x19, 0x8b, 0x36, 0x28, 0xcb, 0xab, 0xf6, 0xe5, 0x7f, 0x9e, 0xb5, 0x6d, 0x4e, 0x96, 0x2c, 0xa3, 0x2c, 0xe2, 0xeb, + 0x43, 0x18, 0x43, 0xe7, 0x43, 0xf3, 0xa7, 0x4d, 0x04, 0xf7, 0x9f, 0xbb, 0x09, 0x6e, 0xc6, 0xf6, 0x21, 0xb8, 0xff, + 0xfc, 0xea, 0x04, 0xf7, 0x27, 0x95, 0xe0, 0x96, 0x7c, 0x81, 0x0a, 0xa9, 0xf3, 0x07, 0x7c, 0x6e, 0x41, 0x50, 0xe7, + 0xe7, 0xfa, 0x01, 0x31, 0xf0, 0xba, 0x92, 0x6c, 0xf7, 0x63, 0x29, 0x7b, 0x10, 0x0a, 0x45, 0x30, 0x08, 0x2d, 0x65, + 0x2a, 0x81, 0x44, 0xb4, 0x32, 0xa5, 0x3a, 0xc0, 0x7c, 0x1b, 0x65, 0xa1, 0xfd, 0x9e, 0x5f, 0xfc, 0x40, 0xc9, 0xf3, + 0x26, 0x4e, 0x16, 0x3e, 0x06, 0xe0, 0xd3, 0x31, 0xeb, 0x20, 0x3c, 0x38, 0xe0, 0x7f, 0x36, 0x8e, 0xa3, 0x89, 0xd4, + 0x54, 0xb0, 0xc1, 0x25, 0x71, 0xdc, 0xfa, 0x3d, 0xf3, 0x13, 0xdd, 0xa4, 0xd7, 0x30, 0xb9, 0xcf, 0xda, 0xce, 0x33, + 0xef, 0xf0, 0xd9, 0x91, 0x03, 0xff, 0xbb, 0xac, 0x9d, 0x9b, 0xbc, 0xe2, 0x22, 0x8e, 0x20, 0xf1, 0x89, 0xa8, 0xb9, + 0xa9, 0xda, 0x2d, 0x63, 0x9f, 0x8a, 0x5a, 0xc7, 0xcd, 0x95, 0x26, 0xfe, 0x7d, 0x51, 0xa7, 0xb1, 0xc6, 0x3c, 0x5e, + 0x29, 0xdd, 0x6a, 0xe8, 0x4d, 0x10, 0xad, 0x40, 0xf6, 0xa6, 0xd4, 0x50, 0x5f, 0xf3, 0xe1, 0x16, 0xe3, 0x62, 0xed, + 0xfc, 0xaa, 0xc8, 0xae, 0x24, 0xb2, 0xbc, 0xec, 0xc4, 0x20, 0x57, 0x5b, 0x38, 0x18, 0x9b, 0x1d, 0xf3, 0x0b, 0x69, + 0x90, 0xdb, 0x50, 0x4c, 0x90, 0x4f, 0x13, 0x94, 0x25, 0xab, 0x68, 0xdc, 0xc2, 0x9f, 0xfe, 0x28, 0x6d, 0x05, 0x07, + 0x10, 0x9d, 0x15, 0x3f, 0x6c, 0xe0, 0xac, 0xf9, 0xa7, 0x4e, 0x91, 0x8a, 0x22, 0x15, 0xb3, 0xe2, 0x3f, 0xcb, 0xcc, + 0x84, 0x12, 0xd8, 0xe2, 0xd4, 0x5a, 0x03, 0xff, 0x99, 0x6c, 0xf8, 0x2c, 0x33, 0x21, 0x89, 0x2c, 0x4c, 0xf7, 0xd3, + 0xa7, 0x54, 0x0b, 0xd2, 0x3a, 0xd2, 0xb0, 0xce, 0xc6, 0x45, 0x78, 0x37, 0xcd, 0x9f, 0xc5, 0x14, 0xe1, 0xad, 0x37, + 0x36, 0xe3, 0xe7, 0xcf, 0x4f, 0x07, 0xae, 0xc1, 0x93, 0x92, 0x96, 0x32, 0x68, 0x9d, 0xef, 0x67, 0x7c, 0x60, 0x34, + 0xba, 0xc5, 0x2d, 0xe1, 0xce, 0xe4, 0x08, 0x13, 0x65, 0x4e, 0xbd, 0x20, 0xa3, 0x05, 0x29, 0x19, 0x7d, 0x61, 0x04, + 0x20, 0xea, 0xc8, 0x5b, 0x57, 0xdb, 0x76, 0x6c, 0x47, 0x97, 0x0d, 0xa7, 0xc1, 0x6c, 0xb0, 0x8e, 0x33, 0x1f, 0x72, + 0x03, 0x85, 0xf1, 0x0c, 0x7c, 0x6b, 0xb2, 0x20, 0x0b, 0x21, 0xd1, 0x0c, 0x38, 0xd9, 0x2c, 0xe8, 0x5e, 0x9e, 0x73, + 0x8b, 0x67, 0x3f, 0xf9, 0x84, 0xc9, 0x06, 0x85, 0x5b, 0x1d, 0x46, 0x1c, 0xfa, 0x11, 0x0e, 0xc3, 0x96, 0xde, 0x82, + 0x54, 0x97, 0x2c, 0x49, 0x2d, 0xd5, 0x83, 0xa0, 0xa7, 0x41, 0x1b, 0x48, 0x43, 0x8f, 0x00, 0xa6, 0x89, 0xbf, 0x80, + 0x98, 0xec, 0xeb, 0xdc, 0xe4, 0x94, 0x56, 0xe7, 0xa4, 0x56, 0x73, 0x5f, 0x1c, 0x99, 0x9a, 0xe7, 0x9a, 0x9a, 0x03, + 0xe4, 0x56, 0xcf, 0xcd, 0x75, 0x7e, 0xd5, 0xdf, 0xa5, 0x04, 0x25, 0xfa, 0xf2, 0x98, 0xc6, 0x41, 0xea, 0x4f, 0x2e, + 0x5e, 0xce, 0x28, 0x80, 0x64, 0x4b, 0x89, 0x96, 0x1e, 0x90, 0x22, 0xe4, 0x82, 0xdd, 0x65, 0x06, 0x26, 0x62, 0xe1, + 0x55, 0x02, 0x63, 0x8d, 0xce, 0x7f, 0x41, 0xa4, 0x05, 0x9f, 0x3f, 0xb7, 0x02, 0x70, 0x70, 0x18, 0x28, 0xf8, 0x81, + 0x67, 0xa3, 0x84, 0xb0, 0xa0, 0x50, 0xdd, 0x21, 0xb2, 0xc0, 0xfb, 0x08, 0xfe, 0x2d, 0x8a, 0xc5, 0x0f, 0xae, 0x3a, + 0xb5, 0x43, 0x3f, 0x9a, 0x01, 0x49, 0xf3, 0xa3, 0x59, 0xcd, 0x44, 0x83, 0xfc, 0x17, 0x2b, 0xa5, 0x05, 0xa8, 0xc2, + 0x7c, 0x22, 0xfd, 0xfe, 0xfe, 0x82, 0x12, 0x4d, 0x41, 0x52, 0x73, 0x7f, 0x82, 0xce, 0x76, 0x85, 0x76, 0xe7, 0xf9, + 0xe0, 0xdb, 0x93, 0x05, 0xcb, 0x7c, 0x12, 0x0d, 0xc3, 0xe5, 0x17, 0xd8, 0x01, 0x6d, 0x2c, 0x92, 0xc4, 0x52, 0x32, + 0xf9, 0x09, 0xbb, 0x09, 0xc6, 0xfc, 0x5e, 0x6a, 0x6a, 0xfc, 0x9c, 0xb2, 0xd0, 0x0a, 0x6c, 0xe0, 0x9a, 0x64, 0x84, + 0x3c, 0xf6, 0x31, 0xcc, 0xe4, 0x20, 0x8a, 0xf5, 0xd3, 0x6f, 0xa5, 0xbf, 0xd6, 0xa6, 0x49, 0x80, 0x6c, 0x8f, 0x97, + 0x09, 0x0b, 0xff, 0x35, 0xf8, 0x16, 0x0e, 0xee, 0x6f, 0xaf, 0x74, 0xa3, 0x9f, 0xd9, 0xf3, 0x84, 0x4d, 0x07, 0xdf, + 0x36, 0x64, 0x3d, 0xc4, 0xeb, 0x3d, 0xf5, 0x45, 0x6f, 0x7b, 0x45, 0x70, 0xa0, 0xf6, 0x5e, 0x97, 0xfa, 0x53, 0x7e, + 0x5b, 0x87, 0x1b, 0xe0, 0xba, 0x74, 0xc7, 0x76, 0xfb, 0x78, 0x7f, 0x1e, 0x85, 0xfe, 0xf8, 0x53, 0x9f, 0xde, 0x94, + 0x1e, 0x2c, 0x38, 0xad, 0xc7, 0xfe, 0xb2, 0x87, 0xc7, 0xab, 0x5a, 0x08, 0xee, 0x9a, 0x54, 0x2a, 0x39, 0xbb, 0xc6, + 0xb5, 0x8c, 0x4b, 0x79, 0x8d, 0x5f, 0xc6, 0x4f, 0xdd, 0xce, 0x83, 0x8c, 0x89, 0x4f, 0xe1, 0x43, 0x9e, 0x8b, 0x8b, + 0x3a, 0x5d, 0x51, 0xf1, 0x62, 0x6d, 0xb7, 0x35, 0xb7, 0xfb, 0xb7, 0xce, 0x8d, 0xeb, 0xcc, 0x3d, 0xd7, 0xee, 0x7e, + 0x74, 0xbb, 0xf3, 0xb6, 0x7d, 0x1c, 0x5a, 0x6d, 0xfb, 0x18, 0xfe, 0x7c, 0x3c, 0xb6, 0xbb, 0x73, 0xcb, 0xb3, 0x0f, + 0x3f, 0xba, 0x5e, 0x68, 0x75, 0xed, 0x63, 0xf8, 0x73, 0x4e, 0xad, 0xe0, 0x02, 0x44, 0xf7, 0x9d, 0x6f, 0x4b, 0x54, + 0x40, 0xf9, 0x2d, 0xf5, 0x34, 0x66, 0xe9, 0x78, 0x6b, 0xd0, 0xf5, 0x00, 0xc9, 0xd0, 0x4d, 0x11, 0x04, 0x32, 0xea, + 0xb7, 0x20, 0x0d, 0x3b, 0x26, 0x10, 0x10, 0x26, 0x2f, 0xc2, 0x2f, 0x55, 0x84, 0xd2, 0x6f, 0xdc, 0x46, 0xbc, 0x4d, + 0x73, 0xc0, 0x75, 0x91, 0x99, 0x8a, 0x94, 0x43, 0xbf, 0x2c, 0x31, 0x88, 0x91, 0x08, 0x11, 0xaf, 0x50, 0xa5, 0x22, + 0x3b, 0x62, 0xbe, 0xbb, 0xe3, 0xe8, 0x99, 0xcb, 0x64, 0x76, 0x9e, 0xaf, 0x0a, 0x9b, 0x2b, 0x8c, 0x24, 0xf4, 0x3f, + 0x0a, 0x07, 0x93, 0xd2, 0x12, 0x1c, 0x11, 0xcd, 0x75, 0x12, 0x24, 0xb2, 0x7b, 0x0a, 0x89, 0x76, 0x9b, 0x23, 0xd5, + 0x1b, 0x90, 0xc6, 0xe4, 0x2d, 0x70, 0xc9, 0x37, 0x7e, 0xa8, 0x18, 0xb7, 0x28, 0x2d, 0x1f, 0x49, 0xca, 0xff, 0xf4, + 0x69, 0xd1, 0x39, 0xab, 0xd2, 0xef, 0x13, 0xb7, 0x03, 0xc7, 0x6e, 0x87, 0xb5, 0xb7, 0xda, 0x59, 0xed, 0x0e, 0x07, + 0x5c, 0x84, 0x0b, 0x15, 0xb6, 0x14, 0x42, 0x8b, 0xbb, 0xd1, 0xd8, 0xab, 0xa6, 0xc3, 0x85, 0x40, 0xca, 0x95, 0xab, + 0x8e, 0x6e, 0xf4, 0x23, 0xa1, 0x92, 0x8c, 0xb6, 0x84, 0x40, 0xe6, 0x77, 0x31, 0x1d, 0x50, 0xb3, 0x65, 0x1c, 0x3b, + 0x9c, 0x46, 0xff, 0xd7, 0x83, 0x40, 0x07, 0x2e, 0xd0, 0xa1, 0x56, 0x76, 0x6b, 0xc9, 0xa1, 0x47, 0x9e, 0xab, 0x74, + 0xa0, 0xb2, 0xf4, 0x4c, 0x87, 0x22, 0xc8, 0x6f, 0x85, 0x29, 0xed, 0xa4, 0x01, 0x99, 0x3c, 0x2d, 0x8a, 0x02, 0x33, + 0x80, 0x18, 0xe0, 0x2d, 0xe1, 0x4c, 0x66, 0x3c, 0x7d, 0xba, 0xf1, 0x10, 0x22, 0x85, 0xbd, 0x9a, 0xd9, 0x53, 0x57, + 0xe9, 0x9b, 0xae, 0x92, 0x18, 0x09, 0x17, 0xa9, 0x86, 0xb0, 0x7b, 0xa3, 0xb5, 0x87, 0x3f, 0x47, 0xcc, 0xcf, 0x6c, + 0xae, 0x69, 0x6a, 0x29, 0x87, 0xbb, 0xe9, 0xb2, 0x36, 0x58, 0xbc, 0xf1, 0x58, 0x67, 0x3c, 0x96, 0xe0, 0x93, 0xf5, + 0xc7, 0x15, 0xf7, 0xf4, 0x06, 0x18, 0x9f, 0x9d, 0x22, 0x3c, 0xcd, 0xbb, 0xcc, 0xa7, 0x18, 0x26, 0xea, 0x91, 0x1b, + 0x67, 0xbe, 0xc8, 0x23, 0x03, 0x7c, 0x79, 0xbf, 0x51, 0x25, 0xab, 0x78, 0x83, 0x9f, 0xbe, 0xbb, 0xf8, 0x4e, 0xe3, + 0xeb, 0x9f, 0x34, 0x88, 0x78, 0x91, 0xa1, 0xac, 0x07, 0x03, 0xca, 0x7a, 0xa0, 0xf1, 0x34, 0x22, 0x90, 0x3b, 0x20, + 0x3f, 0x20, 0x0c, 0xa2, 0x00, 0x9a, 0xf4, 0xaa, 0x8b, 0x55, 0x98, 0x05, 0x4b, 0x3f, 0xc9, 0x0e, 0xa0, 0xa9, 0x05, + 0x44, 0x4e, 0xdf, 0xe4, 0x23, 0x4e, 0xaa, 0x59, 0x11, 0x62, 0x2f, 0x8b, 0x84, 0x6e, 0x76, 0x1a, 0x84, 0x52, 0x35, + 0x2b, 0x3e, 0xe0, 0x8f, 0xc7, 0x6c, 0x99, 0x0d, 0x74, 0x7f, 0x09, 0xd9, 0x2f, 0x30, 0x9e, 0xf5, 0x41, 0x3c, 0xce, + 0x58, 0x66, 0xa5, 0x59, 0xc2, 0xfc, 0x85, 0x2e, 0x43, 0xb9, 0xd6, 0xe1, 0xa5, 0xab, 0xd1, 0x22, 0xc8, 0x64, 0x2c, + 0x44, 0x1a, 0x20, 0x28, 0x49, 0xa1, 0x8b, 0xa7, 0xc3, 0x9c, 0xa3, 0xf0, 0x3c, 0x9e, 0x55, 0x56, 0x54, 0xc1, 0xb9, + 0x9c, 0x61, 0xa4, 0x5d, 0x9e, 0xf1, 0x60, 0x82, 0x3e, 0x4f, 0xd7, 0xdc, 0xaf, 0x5d, 0x86, 0x6c, 0xd4, 0x4f, 0x4f, + 0xf8, 0xf5, 0x56, 0xc3, 0x50, 0x0c, 0x7a, 0xc7, 0x81, 0x58, 0xc2, 0x9b, 0x3c, 0xde, 0x0f, 0x78, 0x65, 0x38, 0x9a, + 0x08, 0x32, 0xc6, 0x79, 0xa7, 0xbe, 0x5c, 0x00, 0x23, 0x54, 0x52, 0xa2, 0xcf, 0xdd, 0x53, 0xe9, 0x62, 0x85, 0xbd, + 0x42, 0x5e, 0xe9, 0xf3, 0xe7, 0x97, 0xc3, 0xff, 0xfc, 0x1b, 0x82, 0xd1, 0xcf, 0x5d, 0xe1, 0x67, 0x7e, 0xa9, 0xd6, + 0xe2, 0xdc, 0xa7, 0x39, 0x44, 0x03, 0x0a, 0x36, 0x11, 0x81, 0x57, 0xc4, 0xd2, 0xca, 0x87, 0x57, 0x22, 0x98, 0x16, + 0x24, 0x9c, 0x30, 0x84, 0x37, 0xfc, 0x10, 0xa6, 0x77, 0x28, 0x82, 0x30, 0x68, 0xbf, 0xdd, 0x7d, 0x7f, 0x0c, 0xc1, + 0x96, 0x6b, 0x79, 0x20, 0x94, 0x0e, 0xe2, 0x1a, 0x3a, 0x3d, 0xf1, 0x35, 0x64, 0x5a, 0x90, 0xfd, 0x48, 0x7b, 0x07, + 0x30, 0xcc, 0x79, 0xbc, 0x60, 0x76, 0x10, 0x1f, 0xdc, 0xb2, 0x91, 0xe5, 0x2f, 0x03, 0xd2, 0xd5, 0xa3, 0xdc, 0x4d, + 0x23, 0xce, 0x4f, 0xaa, 0xc0, 0x89, 0xbf, 0xce, 0x0b, 0x54, 0xc6, 0xe5, 0xe8, 0x69, 0x1d, 0xaf, 0x50, 0xdc, 0x81, + 0x4f, 0xb3, 0x82, 0xc7, 0xf8, 0xf4, 0xe4, 0xc0, 0x3f, 0x2d, 0x87, 0x6f, 0xb4, 0x45, 0x02, 0x81, 0xf2, 0x21, 0x70, + 0x46, 0x51, 0x18, 0x45, 0xc0, 0xc5, 0xe2, 0xc1, 0x8a, 0xa7, 0x53, 0x35, 0xe4, 0xa2, 0x5d, 0xee, 0x9e, 0x44, 0x5a, + 0xb1, 0xa4, 0xe3, 0x25, 0x7d, 0xa9, 0xfe, 0x09, 0xf9, 0x13, 0x92, 0x27, 0xf3, 0xe8, 0x9c, 0xb0, 0xdd, 0x6b, 0xa1, + 0x1b, 0x25, 0xc6, 0x1e, 0x53, 0x25, 0x4e, 0x47, 0xaa, 0x81, 0xc2, 0x37, 0x70, 0x2e, 0x8f, 0x06, 0x03, 0x22, 0x73, + 0x55, 0x6a, 0x07, 0x48, 0x6c, 0x48, 0xa6, 0x00, 0x83, 0xcd, 0xa0, 0xa1, 0x45, 0x2e, 0x74, 0xd8, 0xa8, 0x3a, 0x9c, + 0x7a, 0x1f, 0x0f, 0x7c, 0xb1, 0xfc, 0x4a, 0x0b, 0x14, 0x16, 0x1e, 0x9f, 0x77, 0xa0, 0xef, 0x02, 0x4e, 0x85, 0xcc, + 0x6b, 0x7f, 0x25, 0x8a, 0x6e, 0x85, 0xfe, 0x7d, 0xac, 0x98, 0x36, 0xf0, 0x28, 0x07, 0xe7, 0x58, 0x7a, 0x21, 0xbc, + 0x0b, 0x6b, 0x1b, 0x4d, 0x06, 0xa4, 0xaf, 0x6f, 0x36, 0x35, 0x82, 0xfc, 0xae, 0xbd, 0xa6, 0xd6, 0x2d, 0x0f, 0x06, + 0x89, 0x67, 0x5e, 0xec, 0xc3, 0xd2, 0x4b, 0x24, 0x0b, 0xf9, 0xc9, 0x01, 0x8c, 0x0f, 0x22, 0x33, 0x94, 0x18, 0xa7, + 0xc0, 0x80, 0xf0, 0x0f, 0x7e, 0x4a, 0xe6, 0x19, 0x6f, 0x27, 0x82, 0xe7, 0xc3, 0x8b, 0xa5, 0x8c, 0x0d, 0x5b, 0xaa, + 0x52, 0xe7, 0x65, 0x9c, 0x66, 0x26, 0x70, 0x77, 0x02, 0x87, 0xdf, 0x57, 0x98, 0xbd, 0x21, 0xef, 0x67, 0x4c, 0x38, + 0x3e, 0x9f, 0x67, 0x1b, 0x7c, 0xa3, 0x37, 0x55, 0x21, 0x7e, 0x76, 0x4b, 0x85, 0x62, 0x1d, 0x6f, 0xab, 0x55, 0x70, + 0x4e, 0xb2, 0xda, 0xd2, 0x6f, 0xe9, 0x8f, 0x71, 0xc5, 0xd7, 0x6a, 0x63, 0x29, 0xd4, 0x3b, 0xcf, 0x06, 0x50, 0x55, + 0xc8, 0xe2, 0xfd, 0xe5, 0x92, 0x2a, 0x1b, 0xfd, 0x93, 0x03, 0xba, 0x96, 0x9e, 0xd2, 0x0a, 0x3b, 0x3d, 0x01, 0xf3, + 0x4e, 0x9a, 0x74, 0x7f, 0xb9, 0xe4, 0x53, 0x4a, 0xbf, 0xe8, 0xcd, 0xc1, 0x3c, 0x5b, 0x84, 0xa7, 0xff, 0x07, 0xc0, + 0xb1, 0x34, 0x8b, 0x20, 0x5c, 0x03, 0x00}; } // namespace web_server } // namespace esphome From d7f6d4436e1b4e894306ded3489f6ea337dfffbc Mon Sep 17 00:00:00 2001 From: Colm Date: Thu, 11 Jul 2024 05:10:58 +0100 Subject: [PATCH 0031/1052] Add braces to if statement to avoid compiler warning. (#7036) --- esphome/components/aht10/aht10.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index 332218b9e9..441c1ac9df 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -93,8 +93,9 @@ void AHT10Component::restart_read_() { void AHT10Component::read_data_() { uint8_t data[6]; - if (this->read_count_ > 1) + if (this->read_count_ > 1) { 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->restart_read_(); @@ -119,8 +120,9 @@ void AHT10Component::read_data_() { return; } } - if (this->read_count_ > 1) + 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; From 2d826768b05f33951d1bdd917f62f9db3792c7ce Mon Sep 17 00:00:00 2001 From: ttaborda <80131527+ttaborda@users.noreply.github.com> Date: Thu, 11 Jul 2024 05:26:37 +0100 Subject: [PATCH 0032/1052] Update mitsubishi.cpp (#6909) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mitsubishi/mitsubishi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/mitsubishi/mitsubishi.cpp b/esphome/components/mitsubishi/mitsubishi.cpp index fd57adc586..a02aabf14d 100644 --- a/esphome/components/mitsubishi/mitsubishi.cpp +++ b/esphome/components/mitsubishi/mitsubishi.cpp @@ -52,6 +52,7 @@ const uint8_t MITSUBISHI_BYTE16 = 0X00; climate::ClimateTraits MitsubishiClimate::traits() { auto traits = climate::ClimateTraits(); + traits.set_supports_current_temperature(this->sensor_ != nullptr); traits.set_supports_action(false); traits.set_visual_min_temperature(MITSUBISHI_TEMP_MIN); traits.set_visual_max_temperature(MITSUBISHI_TEMP_MAX); From 531f33a158370f59fa246c064d01e28b80610bbc Mon Sep 17 00:00:00 2001 From: Sergey Dudanov Date: Thu, 11 Jul 2024 08:23:29 +0300 Subject: [PATCH 0033/1052] [climate] fix dump output of unsupported features (#7005) --- esphome/components/climate/climate.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 1822707152..bc8d932089 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -574,21 +574,25 @@ void Climate::dump_traits_(const char *tag) { 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, " Current: %.1f", traits.get_visual_current_temperature_step()); - ESP_LOGCONFIG(tag, " - Min humidity: %.0f", traits.get_visual_min_humidity()); - ESP_LOGCONFIG(tag, " - Max humidity: %.0f", traits.get_visual_max_humidity()); if (traits.get_supports_current_temperature()) { - ESP_LOGCONFIG(tag, " [x] Supports current temperature"); + ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step()); } - if (traits.get_supports_current_humidity()) { - ESP_LOGCONFIG(tag, " [x] Supports current humidity"); + 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()); } if (traits.get_supports_two_point_target_temperature()) { ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature"); } + if (traits.get_supports_current_temperature()) { + ESP_LOGCONFIG(tag, " [x] Supports current temperature"); + } if (traits.get_supports_target_humidity()) { ESP_LOGCONFIG(tag, " [x] Supports target humidity"); } + if (traits.get_supports_current_humidity()) { + ESP_LOGCONFIG(tag, " [x] Supports current humidity"); + } if (traits.get_supports_action()) { ESP_LOGCONFIG(tag, " [x] Supports action"); } From 91bb38553d9eeec1f5a0347e48f9ffc5a70dadbf Mon Sep 17 00:00:00 2001 From: Sergey Dudanov Date: Thu, 11 Jul 2024 08:24:36 +0300 Subject: [PATCH 0034/1052] [climate-traits] improved performance (#7006) --- esphome/components/climate/climate_traits.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index fd5b025a03..58d7b586d7 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -73,7 +73,7 @@ class ClimateTraits { ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20") void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); } bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); } - std::set get_supported_modes() const { return supported_modes_; } + const std::set &get_supported_modes() const { return supported_modes_; } void set_supports_action(bool supports_action) { supports_action_ = supports_action; } bool get_supports_action() const { return supports_action_; } @@ -101,7 +101,7 @@ class ClimateTraits { void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); } bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); } bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); } - std::set get_supported_fan_modes() const { return supported_fan_modes_; } + const std::set &get_supported_fan_modes() const { return supported_fan_modes_; } void set_supported_custom_fan_modes(std::set supported_custom_fan_modes) { supported_custom_fan_modes_ = std::move(supported_custom_fan_modes); @@ -140,7 +140,7 @@ class ClimateTraits { } bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); } bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); } - std::set get_supported_swing_modes() const { return supported_swing_modes_; } + const std::set &get_supported_swing_modes() const { return supported_swing_modes_; } float get_visual_min_temperature() const { return visual_min_temperature_; } void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; } From a34cec217e50e9bbf26ce8494f9bec5abf9951ab Mon Sep 17 00:00:00 2001 From: leejoow Date: Thu, 11 Jul 2024 22:20:58 +0200 Subject: [PATCH 0035/1052] Add default icon to restart button (#7076) Co-authored-by: Leo Schelvis --- esphome/components/restart/button/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/restart/button/__init__.py b/esphome/components/restart/button/__init__.py index 1b2c991261..6aff8cb351 100644 --- a/esphome/components/restart/button/__init__.py +++ b/esphome/components/restart/button/__init__.py @@ -5,6 +5,7 @@ from esphome.const import ( CONF_ID, DEVICE_CLASS_RESTART, ENTITY_CATEGORY_CONFIG, + ICON_RESTART, ) restart_ns = cg.esphome_ns.namespace("restart") @@ -12,6 +13,7 @@ RestartButton = restart_ns.class_("RestartButton", button.Button, cg.Component) CONFIG_SCHEMA = button.button_schema( RestartButton, + icon=ICON_RESTART, device_class=DEVICE_CLASS_RESTART, entity_category=ENTITY_CATEGORY_CONFIG, ).extend(cv.COMPONENT_SCHEMA) From 54b77a1174b0912e028c5d8c1ba56068e30bc84e Mon Sep 17 00:00:00 2001 From: Tomi Junnila Date: Thu, 11 Jul 2024 23:32:38 +0300 Subject: [PATCH 0036/1052] Add support for the Gree YAC1FB9 in climate_ir (#7056) --- esphome/components/gree/climate.py | 1 + esphome/components/gree/gree.cpp | 14 +++++++++++--- esphome/components/gree/gree.h | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/esphome/components/gree/climate.py b/esphome/components/gree/climate.py index 02ce7b12d4..c88a428391 100644 --- a/esphome/components/gree/climate.py +++ b/esphome/components/gree/climate.py @@ -16,6 +16,7 @@ MODELS = { "yan": Model.GREE_YAN, "yaa": Model.GREE_YAA, "yac": Model.GREE_YAC, + "yac1fb9": Model.GREE_YAC1FB9, } CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( diff --git a/esphome/components/gree/gree.cpp b/esphome/components/gree/gree.cpp index 1bbb443fce..cce2a8ffee 100644 --- a/esphome/components/gree/gree.cpp +++ b/esphome/components/gree/gree.cpp @@ -24,7 +24,7 @@ void GreeClimate::transmit_state() { remote_state[4] |= (this->horizontal_swing_() << 4); } - if (this->model_ == GREE_YAA || this->model_ == GREE_YAC) { + if (this->model_ == GREE_YAA || this->model_ == GREE_YAC || this->model_ == GREE_YAC1FB9) { remote_state[2] = 0x20; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN remote_state[3] = 0x50; // bits 4..7 always 0101 remote_state[6] = 0x20; // YAA1FB, FAA1FB1, YB1F2 bits 4..7 always 0010 @@ -53,7 +53,11 @@ void GreeClimate::transmit_state() { data->set_carrier_frequency(GREE_IR_FREQUENCY); data->mark(GREE_HEADER_MARK); - data->space(GREE_HEADER_SPACE); + if (this->model_ == GREE_YAC1FB9) { + data->space(GREE_YAC1FB9_HEADER_SPACE); + } else { + data->space(GREE_HEADER_SPACE); + } for (int i = 0; i < 4; i++) { for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask @@ -71,7 +75,11 @@ void GreeClimate::transmit_state() { data->space(GREE_ZERO_SPACE); data->mark(GREE_BIT_MARK); - data->space(GREE_MESSAGE_SPACE); + if (this->model_ == GREE_YAC1FB9) { + data->space(GREE_YAC1FB9_MESSAGE_SPACE); + } else { + data->space(GREE_MESSAGE_SPACE); + } for (int i = 4; i < 8; i++) { for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask diff --git a/esphome/components/gree/gree.h b/esphome/components/gree/gree.h index e7131a2b89..524a95aebd 100644 --- a/esphome/components/gree/gree.h +++ b/esphome/components/gree/gree.h @@ -41,6 +41,10 @@ const uint32_t GREE_YAC_HEADER_MARK = 6000; const uint32_t GREE_YAC_HEADER_SPACE = 3000; const uint32_t GREE_YAC_BIT_MARK = 650; +// Timing specific to YAC1FB9 +const uint32_t GREE_YAC1FB9_HEADER_SPACE = 4500; +const uint32_t GREE_YAC1FB9_MESSAGE_SPACE = 19980; + // State Frame size const uint8_t GREE_STATE_FRAME_SIZE = 8; @@ -67,7 +71,7 @@ const uint8_t GREE_HDIR_MRIGHT = 0x05; const uint8_t GREE_HDIR_RIGHT = 0x06; // Model codes -enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC }; +enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC, GREE_YAC1FB9 }; class GreeClimate : public climate_ir::ClimateIR { public: From fbab0aceb024175c86b330048c6f45814248ae97 Mon Sep 17 00:00:00 2001 From: Eugen Date: Thu, 11 Jul 2024 23:26:04 +0200 Subject: [PATCH 0037/1052] add ESP32-C6 support to esp32_can (#7063) --- esphome/components/esp32_can/canbus.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/esp32_can/canbus.py b/esphome/components/esp32_can/canbus.py index 74f331f30b..f4ba032009 100644 --- a/esphome/components/esp32_can/canbus.py +++ b/esphome/components/esp32_can/canbus.py @@ -11,6 +11,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, VARIANT_ESP32C3, + VARIANT_ESP32C6, VARIANT_ESP32H2, ) @@ -47,6 +48,7 @@ CAN_SPEEDS_ESP32_S2 = { CAN_SPEEDS_ESP32_S3 = {**CAN_SPEEDS_ESP32_S2} CAN_SPEEDS_ESP32_C3 = {**CAN_SPEEDS_ESP32_S2} +CAN_SPEEDS_ESP32_C6 = {**CAN_SPEEDS_ESP32_S2} CAN_SPEEDS_ESP32_H2 = {**CAN_SPEEDS_ESP32_S2} CAN_SPEEDS = { @@ -54,6 +56,7 @@ CAN_SPEEDS = { VARIANT_ESP32S2: CAN_SPEEDS_ESP32_S2, VARIANT_ESP32S3: CAN_SPEEDS_ESP32_S3, VARIANT_ESP32C3: CAN_SPEEDS_ESP32_C3, + VARIANT_ESP32C6: CAN_SPEEDS_ESP32_C6, VARIANT_ESP32H2: CAN_SPEEDS_ESP32_H2, } From c6c1d3a3ad2e6fefecd6de60584eea1e263734e2 Mon Sep 17 00:00:00 2001 From: kevdliu <1766838+kevdliu@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:32:31 -0400 Subject: [PATCH 0038/1052] Fix voice assistant crash when no speaker configured (#7075) --- esphome/components/voice_assistant/voice_assistant.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 8a8a9e92aa..e4f388db68 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -684,7 +684,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { this->defer([this, text]() { this->tts_start_trigger_->trigger(text); #ifdef USE_SPEAKER - this->speaker_->start(); + if (this->speaker_ != nullptr) { + this->speaker_->start(); + } #endif }); break; From 0c2f9b9dbbfaa14af7b608e6c6223fd6527bda52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20=C3=81rkosi=20R=C3=B3bert?= Date: Fri, 12 Jul 2024 23:19:33 +0200 Subject: [PATCH 0039/1052] Bump HeatpumpIR, add protocols, remove IRremoteESP8266 (#6996) --- esphome/components/heatpumpir/climate.py | 11 ++++++----- esphome/components/heatpumpir/heatpumpir.cpp | 5 +++++ esphome/components/heatpumpir/heatpumpir.h | 5 +++++ platformio.ini | 6 +++--- .../heatpumpir/test.bk72xx-ard.yaml | 19 +++++++++++++++++++ 5 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 tests/components/heatpumpir/test.bk72xx-ard.yaml diff --git a/esphome/components/heatpumpir/climate.py b/esphome/components/heatpumpir/climate.py index b86d405b7e..80900d7db9 100644 --- a/esphome/components/heatpumpir/climate.py +++ b/esphome/components/heatpumpir/climate.py @@ -8,7 +8,6 @@ from esphome.const import ( CONF_PROTOCOL, CONF_VISUAL, ) -from esphome.core import CORE CODEOWNERS = ["@rob-deutsch"] @@ -67,6 +66,11 @@ PROTOCOLS = { "carrier_qlima_2": Protocol.PROTOCOL_QLIMA_2, "samsung_aqv12msan": Protocol.PROTOCOL_SAMSUNG_AQV12MSAN, "zhjg01": Protocol.PROTOCOL_ZHJG01, + "airway": Protocol.PROTOCOL_AIRWAY, + "bgh_aud": Protocol.PROTOCOL_BGH_AUD, + "panasonic_altdke": Protocol.PROTOCOL_PANASONIC_ALTDKE, + "vaillantvai8": Protocol.PROTOCOL_VAILLANTVAI8, + "r51m": Protocol.PROTOCOL_R51M, } CONF_HORIZONTAL_DEFAULT = "horizontal_default" @@ -122,7 +126,4 @@ def to_code(config): cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE])) cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) - cg.add_library("tonia/HeatpumpIR", "1.0.26") - - if CORE.is_esp8266 or CORE.is_esp32: - cg.add_library("crankyoldgit/IRremoteESP8266", "2.8.6") + cg.add_library("tonia/HeatpumpIR", "1.0.27") diff --git a/esphome/components/heatpumpir/heatpumpir.cpp b/esphome/components/heatpumpir/heatpumpir.cpp index 22a5779c8d..144dcc9bfa 100644 --- a/esphome/components/heatpumpir/heatpumpir.cpp +++ b/esphome/components/heatpumpir/heatpumpir.cpp @@ -61,6 +61,11 @@ const std::map> PROTOCOL_CONSTRUCTOR_MAP {PROTOCOL_QLIMA_2, []() { return new Qlima2HeatpumpIR(); }}, // NOLINT {PROTOCOL_SAMSUNG_AQV12MSAN, []() { return new SamsungAQV12MSANHeatpumpIR(); }}, // NOLINT {PROTOCOL_ZHJG01, []() { return new ZHJG01HeatpumpIR(); }}, // NOLINT + {PROTOCOL_AIRWAY, []() { return new AIRWAYHeatpumpIR(); }}, // NOLINT + {PROTOCOL_BGH_AUD, []() { return new BGHHeatpumpIR(); }}, // NOLINT + {PROTOCOL_PANASONIC_ALTDKE, []() { return new PanasonicAltDKEHeatpumpIR(); }}, // NOLINT + {PROTOCOL_VAILLANTVAI8, []() { return new VaillantHeatpumpIR(); }}, // NOLINT + {PROTOCOL_R51M, []() { return new R51MHeatpumpIR(); }}, // NOLINT }; void HeatpumpIRClimate::setup() { diff --git a/esphome/components/heatpumpir/heatpumpir.h b/esphome/components/heatpumpir/heatpumpir.h index 0e6ea2218f..f6e7ff3cd6 100644 --- a/esphome/components/heatpumpir/heatpumpir.h +++ b/esphome/components/heatpumpir/heatpumpir.h @@ -61,6 +61,11 @@ enum Protocol { PROTOCOL_QLIMA_2, PROTOCOL_SAMSUNG_AQV12MSAN, PROTOCOL_ZHJG01, + PROTOCOL_AIRWAY, + PROTOCOL_BGH_AUD, + PROTOCOL_PANASONIC_ALTDKE, + PROTOCOL_VAILLANTVAI8, + PROTOCOL_R51M, }; // Simple enum to represent horizontal directios diff --git a/platformio.ini b/platformio.ini index f07889526f..fc7f35b6c3 100644 --- a/platformio.ini +++ b/platformio.ini @@ -65,7 +65,7 @@ lib_deps = glmnet/Dsmr@0.7 ; dsmr rweather/Crypto@0.4.0 ; dsmr dudanov/MideaUART@1.1.9 ; midea - tonia/HeatpumpIR@1.0.26 ; heatpumpir + tonia/HeatpumpIR@1.0.27 ; heatpumpir build_flags = ${common.build_flags} -DUSE_ARDUINO @@ -93,8 +93,8 @@ lib_deps = ESP8266HTTPClient ; http_request (Arduino built-in) ESP8266mDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) - crankyoldgit/IRremoteESP8266@2.8.6 ; heatpumpir droscy/esp_wireguard@0.4.2 ; wireguard + build_flags = ${common:arduino.build_flags} -Wno-nonnull-compare @@ -123,8 +123,8 @@ lib_deps = ESPmDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) esphome/ESP32-audioI2S@2.0.7 ; i2s_audio - crankyoldgit/IRremoteESP8266@2.8.6 ; heatpumpir droscy/esp_wireguard@0.4.2 ; wireguard + build_flags = ${common:arduino.build_flags} -DUSE_ESP32 diff --git a/tests/components/heatpumpir/test.bk72xx-ard.yaml b/tests/components/heatpumpir/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..90259f1244 --- /dev/null +++ b/tests/components/heatpumpir/test.bk72xx-ard.yaml @@ -0,0 +1,19 @@ +remote_transmitter: + pin: 6 + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: daikin + horizontal_default: mleft + vertical_default: mup + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 + - platform: heatpumpir + protocol: panasonic_altdke + horizontal_default: mright + vertical_default: mdown + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 From 316a0e1c967ad28f4ec43144cafea512f519d0a4 Mon Sep 17 00:00:00 2001 From: Anton Viktorov Date: Fri, 12 Jul 2024 21:42:41 +0000 Subject: [PATCH 0040/1052] LTR390 separate ALS and UV gain and resolution (#7026) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 2 +- esphome/components/ltr390/ltr390.cpp | 73 +++++++++++-------- esphome/components/ltr390/ltr390.h | 14 ++-- esphome/components/ltr390/sensor.py | 40 ++++++++-- tests/components/ltr390/test.esp32-ard.yaml | 18 +++++ tests/components/ltr390/test.esp8266-ard.yaml | 4 +- 6 files changed, 110 insertions(+), 41 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 5c14d30371..210c567f78 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -214,7 +214,7 @@ esphome/components/lightwaverf/* @max246 esphome/components/lilygo_t5_47/touchscreen/* @jesserockz esphome/components/lock/* @esphome/core esphome/components/logger/* @esphome/core -esphome/components/ltr390/* @sjtrny +esphome/components/ltr390/* @latonita @sjtrny esphome/components/ltr_als_ps/* @latonita esphome/components/matrix_keypad/* @ssieb esphome/components/max31865/* @DAVe3283 diff --git a/esphome/components/ltr390/ltr390.cpp b/esphome/components/ltr390/ltr390.cpp index 4eb1ff2c46..198d15ebd8 100644 --- a/esphome/components/ltr390/ltr390.cpp +++ b/esphome/components/ltr390/ltr390.cpp @@ -19,6 +19,7 @@ static const uint8_t LTR390_MAIN_STATUS = 0x07; static const float GAINVALUES[5] = {1.0, 3.0, 6.0, 9.0, 18.0}; static const float RESOLUTIONVALUE[6] = {4.0, 2.0, 1.0, 0.5, 0.25, 0.125}; +static const uint8_t RESOLUTION_BITS[6] = {20, 19, 18, 17, 16, 13}; // Request fastest measurement rate - will be slowed by device if conversion rate is slower. static const float RESOLUTION_SETTING[6] = {0x00, 0x10, 0x20, 0x30, 0x40, 0x50}; @@ -74,7 +75,7 @@ void LTR390Component::read_als_() { uint32_t als = *val; if (this->light_sensor_ != nullptr) { - float lux = ((0.6 * als) / (GAINVALUES[this->gain_] * RESOLUTIONVALUE[this->res_])) * this->wfac_; + float lux = ((0.6 * als) / (GAINVALUES[this->gain_als_] * RESOLUTIONVALUE[this->res_als_])) * this->wfac_; this->light_sensor_->publish_state(lux); } @@ -90,7 +91,7 @@ void LTR390Component::read_uvs_() { uint32_t uv = *val; if (this->uvi_sensor_ != nullptr) { - this->uvi_sensor_->publish_state((uv / this->sensitivity_) * this->wfac_); + this->uvi_sensor_->publish_state((uv / this->sensitivity_uv_) * this->wfac_); } if (this->uv_sensor_ != nullptr) { @@ -107,24 +108,38 @@ void LTR390Component::read_mode_(int mode_index) { ctrl[LTR390_CTRL_EN] = true; this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); - // After the sensor integration time do the following - this->set_timeout(((uint32_t) RESOLUTIONVALUE[this->res_]) * 100 + LTR390_WAKEUP_TIME + LTR390_SETTLE_TIME, - [this, mode_index]() { - // Read from the sensor - std::get<1>(this->mode_funcs_[mode_index])(); + uint32_t int_time{0}; + // Set gain, resolution and measurement rate + switch (mode) { + case LTR390_MODE_ALS: + this->reg(LTR390_GAIN) = this->gain_als_; + this->reg(LTR390_MEAS_RATE) = RESOLUTION_SETTING[this->res_als_]; + int_time = ((uint32_t) RESOLUTIONVALUE[this->res_als_]) * 100; + break; + case LTR390_MODE_UVS: + this->reg(LTR390_GAIN) = this->gain_uv_; + this->reg(LTR390_MEAS_RATE) = RESOLUTION_SETTING[this->res_uv_]; + int_time = ((uint32_t) RESOLUTIONVALUE[this->res_uv_]) * 100; + break; + } - // If there are more modes to read then begin the next - // otherwise stop - if (mode_index + 1 < (int) this->mode_funcs_.size()) { - this->read_mode_(mode_index + 1); - } else { - // put sensor in standby - std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); - ctrl[LTR390_CTRL_EN] = false; - this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); - this->reading_ = false; - } - }); + // After the sensor integration time do the following + this->set_timeout(int_time + LTR390_WAKEUP_TIME + LTR390_SETTLE_TIME, [this, mode_index]() { + // Read from the sensor + std::get<1>(this->mode_funcs_[mode_index])(); + + // If there are more modes to read then begin the next + // otherwise stop + if (mode_index + 1 < (int) this->mode_funcs_.size()) { + this->read_mode_(mode_index + 1); + } else { + // put sensor in standby + std::bitset<8> ctrl = this->reg(LTR390_MAIN_CTRL).get(); + ctrl[LTR390_CTRL_EN] = false; + this->reg(LTR390_MAIN_CTRL) = ctrl.to_ulong(); + this->reading_ = false; + } + }); } void LTR390Component::setup() { @@ -151,16 +166,10 @@ void LTR390Component::setup() { return; } - // Set gain - this->reg(LTR390_GAIN) = gain_; - - // Set resolution and measurement rate - this->reg(LTR390_MEAS_RATE) = RESOLUTION_SETTING[this->res_]; - // Set sensitivity by linearly scaling against known value in the datasheet - float gain_scale = GAINVALUES[this->gain_] / GAIN_MAX; - float intg_scale = (RESOLUTIONVALUE[this->res_] * 100) / INTG_MAX; - this->sensitivity_ = SENSITIVITY_MAX * gain_scale * intg_scale; + float gain_scale_uv = GAINVALUES[this->gain_uv_] / GAIN_MAX; + float intg_scale_uv = (RESOLUTIONVALUE[this->res_uv_] * 100) / INTG_MAX; + this->sensitivity_uv_ = SENSITIVITY_MAX * gain_scale_uv * intg_scale_uv; // Set sensor read state this->reading_ = false; @@ -176,7 +185,13 @@ void LTR390Component::setup() { } } -void LTR390Component::dump_config() { LOG_I2C_DEVICE(this); } +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_]); +} void LTR390Component::update() { if (!this->reading_ && !mode_funcs_.empty()) { diff --git a/esphome/components/ltr390/ltr390.h b/esphome/components/ltr390/ltr390.h index bc98518fe9..24afd3c411 100644 --- a/esphome/components/ltr390/ltr390.h +++ b/esphome/components/ltr390/ltr390.h @@ -49,8 +49,10 @@ class LTR390Component : public PollingComponent, public i2c::I2CDevice { void dump_config() override; void update() override; - void set_gain_value(LTR390GAIN gain) { this->gain_ = gain; } - void set_res_value(LTR390RESOLUTION res) { this->res_ = res; } + void set_als_gain_value(LTR390GAIN gain) { this->gain_als_ = gain; } + void set_uv_gain_value(LTR390GAIN gain) { this->gain_uv_ = gain; } + void set_als_res_value(LTR390RESOLUTION res) { this->res_als_ = res; } + void set_uv_res_value(LTR390RESOLUTION res) { this->res_uv_ = res; } void set_wfac_value(float wfac) { this->wfac_ = wfac; } void set_light_sensor(sensor::Sensor *light_sensor) { this->light_sensor_ = light_sensor; } @@ -71,9 +73,11 @@ class LTR390Component : public PollingComponent, public i2c::I2CDevice { // a list of modes and corresponding read functions std::vector>> mode_funcs_; - LTR390GAIN gain_; - LTR390RESOLUTION res_; - float sensitivity_; + LTR390GAIN gain_als_; + LTR390GAIN gain_uv_; + LTR390RESOLUTION res_als_; + LTR390RESOLUTION res_uv_; + float sensitivity_uv_; float wfac_; sensor::Sensor *light_sensor_{nullptr}; diff --git a/esphome/components/ltr390/sensor.py b/esphome/components/ltr390/sensor.py index 8b2676599c..62c3edf8cb 100644 --- a/esphome/components/ltr390/sensor.py +++ b/esphome/components/ltr390/sensor.py @@ -13,7 +13,7 @@ from esphome.const import ( UNIT_LUX, ) -CODEOWNERS = ["@sjtrny"] +CODEOWNERS = ["@sjtrny", "@latonita"] DEPENDENCIES = ["i2c"] ltr390_ns = cg.esphome_ns.namespace("ltr390") @@ -76,8 +76,24 @@ CONFIG_SCHEMA = cv.All( accuracy_decimals=1, device_class=DEVICE_CLASS_EMPTY, ), - cv.Optional(CONF_GAIN, default="X18"): cv.enum(GAIN_OPTIONS), - cv.Optional(CONF_RESOLUTION, default=20): cv.enum(RES_OPTIONS), + cv.Optional(CONF_GAIN, default="X18"): cv.Any( + cv.enum(GAIN_OPTIONS), + cv.Schema( + { + cv.Required(CONF_AMBIENT_LIGHT): cv.enum(GAIN_OPTIONS), + cv.Required(CONF_UV): cv.enum(GAIN_OPTIONS), + } + ), + ), + cv.Optional(CONF_RESOLUTION, default=20): cv.Any( + cv.enum(RES_OPTIONS), + cv.Schema( + { + cv.Required(CONF_AMBIENT_LIGHT): cv.enum(RES_OPTIONS), + cv.Required(CONF_UV): cv.enum(RES_OPTIONS), + } + ), + ), cv.Optional(CONF_WINDOW_CORRECTION_FACTOR, default=1.0): cv.float_range( min=1.0 ), @@ -101,11 +117,25 @@ async def to_code(config): await cg.register_component(var, config) await i2c.register_i2c_device(var, config) - cg.add(var.set_gain_value(config[CONF_GAIN])) - cg.add(var.set_res_value(config[CONF_RESOLUTION])) cg.add(var.set_wfac_value(config[CONF_WINDOW_CORRECTION_FACTOR])) for key, funcName in TYPES.items(): if key in config: sens = await sensor.new_sensor(config[key]) cg.add(getattr(var, funcName)(sens)) + + gain_value = config[CONF_GAIN] + if isinstance(gain_value, dict): + cg.add(var.set_als_gain_value(gain_value[CONF_AMBIENT_LIGHT])) + cg.add(var.set_uv_gain_value(gain_value[CONF_UV])) + else: + cg.add(var.set_als_gain_value(gain_value)) + cg.add(var.set_uv_gain_value(gain_value)) + + res_value = config[CONF_RESOLUTION] + if isinstance(res_value, dict): + cg.add(var.set_als_res_value(res_value[CONF_AMBIENT_LIGHT])) + cg.add(var.set_uv_res_value(res_value[CONF_UV])) + else: + cg.add(var.set_als_res_value(res_value)) + cg.add(var.set_uv_res_value(res_value)) diff --git a/tests/components/ltr390/test.esp32-ard.yaml b/tests/components/ltr390/test.esp32-ard.yaml index 9786c7dac3..bdfe349b77 100644 --- a/tests/components/ltr390/test.esp32-ard.yaml +++ b/tests/components/ltr390/test.esp32-ard.yaml @@ -18,3 +18,21 @@ sensor: window_correction_factor: 1.0 address: 0x53 update_interval: 60s + - platform: ltr390 + uv: + name: LTR390 UV + uv_index: + name: LTR390 UVI + light: + name: LTR390 Light + ambient_light: + name: LTR390 ALS + gain: + ambient_light: X9 + uv: X3 + resolution: + ambient_light: 18 + uv: 13 + window_correction_factor: 1.0 + address: 0x53 + update_interval: 60s diff --git a/tests/components/ltr390/test.esp8266-ard.yaml b/tests/components/ltr390/test.esp8266-ard.yaml index fee0f37ce1..149f46f9c8 100644 --- a/tests/components/ltr390/test.esp8266-ard.yaml +++ b/tests/components/ltr390/test.esp8266-ard.yaml @@ -13,7 +13,9 @@ sensor: name: LTR390 Light ambient_light: name: LTR390 ALS - gain: X3 + gain: + ambient_light: X9 + uv: X3 resolution: 18 window_correction_factor: 1.0 address: 0x53 From bb92ab01d736d8bffa0a2d90713d12ff2f03d774 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 13 Jul 2024 09:46:08 +1200 Subject: [PATCH 0041/1052] Bump version to 2024.7.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index d672cc92af..4776de4d67 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.0b1" +__version__ = "2024.7.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 44d609b205c1549ae4c2ab7be31d81b0f67e8755 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 14 Jul 2024 22:05:02 +0200 Subject: [PATCH 0042/1052] [CI] compile entire web_server during tests (#7084) --- tests/components/web_server/common.yaml | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/components/web_server/common.yaml b/tests/components/web_server/common.yaml index 94388726c3..338d9e97d2 100644 --- a/tests/components/web_server/common.yaml +++ b/tests/components/web_server/common.yaml @@ -5,3 +5,38 @@ wifi: web_server: port: 8080 version: 2 + +binary_sensor: +cover: +fan: +light: +sensor: +switch: +button: +text_sensor: +climate: +number: +text: +select: +lock: +valve: +alarm_control_panel: +api: +time: + - platform: homeassistant + id: homeassistant_time +datetime: + - platform: template + id: my_datetime_date + type: date + optimistic: yes + - platform: template + id: my_datetime_time + type: time + optimistic: yes + - platform: template + id: my_datetime + type: datetime + optimistic: yes +event: +update: From 896af84acc967aca74bc3cf832fb4e7da47ad032 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 14 Jul 2024 15:06:10 -0500 Subject: [PATCH 0043/1052] [improv_serial] Fix linker error created in #6998 (#7082) --- esphome/components/improv_serial/improv_serial_component.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 02ffa9f31c..12809e38cb 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -57,7 +57,7 @@ optional ImprovSerialComponent::read_byte_() { } } break; -#ifdef USE_LOGGER_USB_CDC +#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) case logger::UART_SELECTION_USB_CDC: #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) if (esp_usb_console_available_for_read()) { @@ -99,7 +99,7 @@ void ImprovSerialComponent::write_data_(std::vector &data) { #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 uart_write_bytes(this->uart_num_, data.data(), data.size()); break; -#ifdef USE_LOGGER_USB_CDC +#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) case logger::UART_SELECTION_USB_CDC: { const char *msg = (char *) data.data(); esp_usb_console_write_buf(msg, data.size()); @@ -109,6 +109,7 @@ void ImprovSerialComponent::write_data_(std::vector &data) { #ifdef USE_LOGGER_USB_SERIAL_JTAG case logger::UART_SELECTION_USB_SERIAL_JTAG: usb_serial_jtag_write_bytes((char *) data.data(), data.size(), 20 / portTICK_PERIOD_MS); + delay(10); usb_serial_jtag_ll_txfifo_flush(); // fixes for issue in IDF 4.4.7 break; #endif // USE_LOGGER_USB_SERIAL_JTAG From 07b78fea760e78e5be5c0dbbf506f2fca490cff0 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 14 Jul 2024 15:32:10 -0500 Subject: [PATCH 0044/1052] [CI] Add more ``improv_serial`` tests (#7081) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../improv_serial/{common.yaml => common-uart0.yaml} | 3 +++ tests/components/improv_serial/common-usb_cdc.yaml | 8 ++++++++ .../components/improv_serial/common-usb_serial_jtag.yaml | 8 ++++++++ tests/components/improv_serial/test-uart0.esp32-ard.yaml | 1 + .../components/improv_serial/test-uart0.esp32-c3-ard.yaml | 1 + .../components/improv_serial/test-uart0.esp32-c3-idf.yaml | 1 + tests/components/improv_serial/test-uart0.esp32-idf.yaml | 1 + .../components/improv_serial/test-uart0.esp32-s2-ard.yaml | 1 + .../components/improv_serial/test-uart0.esp32-s2-idf.yaml | 1 + .../components/improv_serial/test-uart0.esp32-s3-ard.yaml | 1 + .../components/improv_serial/test-uart0.esp32-s3-idf.yaml | 1 + .../components/improv_serial/test-uart0.esp8266-ard.yaml | 1 + tests/components/improv_serial/test-uart0.rp2040-ard.yaml | 1 + .../improv_serial/test-usb_cdc.esp32-c3-ard.yaml | 1 + .../improv_serial/test-usb_cdc.esp32-s2-ard.yaml | 1 + .../improv_serial/test-usb_cdc.esp32-s2-idf.yaml | 1 + .../improv_serial/test-usb_cdc.esp32-s3-ard.yaml | 1 + .../components/improv_serial/test-usb_cdc.rp2040-ard.yaml | 1 + .../improv_serial/test-usb_serial_jtag.esp32-c3-idf.yaml | 1 + .../improv_serial/test-usb_serial_jtag.esp32-s3-idf.yaml | 1 + tests/components/improv_serial/test.esp32-ard.yaml | 1 - tests/components/improv_serial/test.esp32-c3-ard.yaml | 1 - tests/components/improv_serial/test.esp32-c3-idf.yaml | 1 - tests/components/improv_serial/test.esp32-idf.yaml | 1 - tests/components/improv_serial/test.esp8266-ard.yaml | 1 - tests/components/improv_serial/test.rp2040-ard.yaml | 1 - 26 files changed, 36 insertions(+), 6 deletions(-) rename tests/components/improv_serial/{common.yaml => common-uart0.yaml} (64%) create mode 100644 tests/components/improv_serial/common-usb_cdc.yaml create mode 100644 tests/components/improv_serial/common-usb_serial_jtag.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-ard.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-c3-idf.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-idf.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-s2-idf.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp32-s3-idf.yaml create mode 100644 tests/components/improv_serial/test-uart0.esp8266-ard.yaml create mode 100644 tests/components/improv_serial/test-uart0.rp2040-ard.yaml create mode 100644 tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml create mode 100644 tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml create mode 100644 tests/components/improv_serial/test-usb_cdc.esp32-s2-idf.yaml create mode 100644 tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml create mode 100644 tests/components/improv_serial/test-usb_cdc.rp2040-ard.yaml create mode 100644 tests/components/improv_serial/test-usb_serial_jtag.esp32-c3-idf.yaml create mode 100644 tests/components/improv_serial/test-usb_serial_jtag.esp32-s3-idf.yaml delete mode 100644 tests/components/improv_serial/test.esp32-ard.yaml delete mode 100644 tests/components/improv_serial/test.esp32-c3-ard.yaml delete mode 100644 tests/components/improv_serial/test.esp32-c3-idf.yaml delete mode 100644 tests/components/improv_serial/test.esp32-idf.yaml delete mode 100644 tests/components/improv_serial/test.esp8266-ard.yaml delete mode 100644 tests/components/improv_serial/test.rp2040-ard.yaml diff --git a/tests/components/improv_serial/common.yaml b/tests/components/improv_serial/common-uart0.yaml similarity index 64% rename from tests/components/improv_serial/common.yaml rename to tests/components/improv_serial/common-uart0.yaml index b36fe5a4a7..7b7730fd46 100644 --- a/tests/components/improv_serial/common.yaml +++ b/tests/components/improv_serial/common-uart0.yaml @@ -2,4 +2,7 @@ wifi: ssid: MySSID password: password1 +logger: + hardware_uart: UART0 + improv_serial: diff --git a/tests/components/improv_serial/common-usb_cdc.yaml b/tests/components/improv_serial/common-usb_cdc.yaml new file mode 100644 index 0000000000..fc72b6aa7e --- /dev/null +++ b/tests/components/improv_serial/common-usb_cdc.yaml @@ -0,0 +1,8 @@ +wifi: + ssid: MySSID + password: password1 + +logger: + hardware_uart: USB_CDC + +improv_serial: diff --git a/tests/components/improv_serial/common-usb_serial_jtag.yaml b/tests/components/improv_serial/common-usb_serial_jtag.yaml new file mode 100644 index 0000000000..0abcc07168 --- /dev/null +++ b/tests/components/improv_serial/common-usb_serial_jtag.yaml @@ -0,0 +1,8 @@ +wifi: + ssid: MySSID + password: password1 + +logger: + hardware_uart: USB_SERIAL_JTAG + +improv_serial: diff --git a/tests/components/improv_serial/test-uart0.esp32-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-ard.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-c3-idf.yaml b/tests/components/improv_serial/test-uart0.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-idf.yaml b/tests/components/improv_serial/test-uart0.esp32-idf.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-s2-ard.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-s2-idf.yaml b/tests/components/improv_serial/test-uart0.esp32-s2-idf.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-s2-idf.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml b/tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-s3-ard.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp32-s3-idf.yaml b/tests/components/improv_serial/test-uart0.esp32-s3-idf.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp32-s3-idf.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.esp8266-ard.yaml b/tests/components/improv_serial/test-uart0.esp8266-ard.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-uart0.rp2040-ard.yaml b/tests/components/improv_serial/test-uart0.rp2040-ard.yaml new file mode 100644 index 0000000000..ef8c799241 --- /dev/null +++ b/tests/components/improv_serial/test-uart0.rp2040-ard.yaml @@ -0,0 +1 @@ +<<: !include common-uart0.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml b/tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml new file mode 100644 index 0000000000..cfdaec9771 --- /dev/null +++ b/tests/components/improv_serial/test-usb_cdc.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common-usb_cdc.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml b/tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml new file mode 100644 index 0000000000..cfdaec9771 --- /dev/null +++ b/tests/components/improv_serial/test-usb_cdc.esp32-s2-ard.yaml @@ -0,0 +1 @@ +<<: !include common-usb_cdc.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.esp32-s2-idf.yaml b/tests/components/improv_serial/test-usb_cdc.esp32-s2-idf.yaml new file mode 100644 index 0000000000..cfdaec9771 --- /dev/null +++ b/tests/components/improv_serial/test-usb_cdc.esp32-s2-idf.yaml @@ -0,0 +1 @@ +<<: !include common-usb_cdc.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml b/tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml new file mode 100644 index 0000000000..cfdaec9771 --- /dev/null +++ b/tests/components/improv_serial/test-usb_cdc.esp32-s3-ard.yaml @@ -0,0 +1 @@ +<<: !include common-usb_cdc.yaml diff --git a/tests/components/improv_serial/test-usb_cdc.rp2040-ard.yaml b/tests/components/improv_serial/test-usb_cdc.rp2040-ard.yaml new file mode 100644 index 0000000000..cfdaec9771 --- /dev/null +++ b/tests/components/improv_serial/test-usb_cdc.rp2040-ard.yaml @@ -0,0 +1 @@ +<<: !include common-usb_cdc.yaml diff --git a/tests/components/improv_serial/test-usb_serial_jtag.esp32-c3-idf.yaml b/tests/components/improv_serial/test-usb_serial_jtag.esp32-c3-idf.yaml new file mode 100644 index 0000000000..46a927ff10 --- /dev/null +++ b/tests/components/improv_serial/test-usb_serial_jtag.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common-usb_serial_jtag.yaml diff --git a/tests/components/improv_serial/test-usb_serial_jtag.esp32-s3-idf.yaml b/tests/components/improv_serial/test-usb_serial_jtag.esp32-s3-idf.yaml new file mode 100644 index 0000000000..46a927ff10 --- /dev/null +++ b/tests/components/improv_serial/test-usb_serial_jtag.esp32-s3-idf.yaml @@ -0,0 +1 @@ +<<: !include common-usb_serial_jtag.yaml diff --git a/tests/components/improv_serial/test.esp32-ard.yaml b/tests/components/improv_serial/test.esp32-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/improv_serial/test.esp32-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/improv_serial/test.esp32-c3-ard.yaml b/tests/components/improv_serial/test.esp32-c3-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/improv_serial/test.esp32-c3-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/improv_serial/test.esp32-c3-idf.yaml b/tests/components/improv_serial/test.esp32-c3-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/improv_serial/test.esp32-c3-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/improv_serial/test.esp32-idf.yaml b/tests/components/improv_serial/test.esp32-idf.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/improv_serial/test.esp32-idf.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/improv_serial/test.esp8266-ard.yaml b/tests/components/improv_serial/test.esp8266-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/improv_serial/test.esp8266-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml diff --git a/tests/components/improv_serial/test.rp2040-ard.yaml b/tests/components/improv_serial/test.rp2040-ard.yaml deleted file mode 100644 index dade44d145..0000000000 --- a/tests/components/improv_serial/test.rp2040-ard.yaml +++ /dev/null @@ -1 +0,0 @@ -<<: !include common.yaml From f1d19416be8753ff189562f5719b3e96a783414f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:28:41 +1200 Subject: [PATCH 0045/1052] [i2s_audio] Allow config for primary/secondary i2s mode (#7092) --- esphome/components/i2s_audio/__init__.py | 10 ++++++++++ esphome/components/i2s_audio/microphone/__init__.py | 7 +++++++ .../i2s_audio/microphone/i2s_audio_microphone.cpp | 5 ++--- .../i2s_audio/microphone/i2s_audio_microphone.h | 3 +++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index d72e13630f..05e44696d8 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -25,6 +25,10 @@ CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin" CONF_I2S_AUDIO = "i2s_audio" CONF_I2S_AUDIO_ID = "i2s_audio_id" +CONF_I2S_MODE = "i2s_mode" +CONF_PRIMARY = "primary" +CONF_SECONDARY = "secondary" + i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio") I2SAudioComponent = i2s_audio_ns.class_("I2SAudioComponent", cg.Component) I2SAudioIn = i2s_audio_ns.class_("I2SAudioIn", cg.Parented.template(I2SAudioComponent)) @@ -32,6 +36,12 @@ I2SAudioOut = i2s_audio_ns.class_( "I2SAudioOut", cg.Parented.template(I2SAudioComponent) ) +i2s_mode_t = cg.global_ns.enum("i2s_mode_t") +I2S_MODE_OPTIONS = { + CONF_PRIMARY: i2s_mode_t.I2S_MODE_MASTER, # NOLINT + CONF_SECONDARY: i2s_mode_t.I2S_MODE_SLAVE, # NOLINT +} + # https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h I2S_PORTS = { VARIANT_ESP32: 2, diff --git a/esphome/components/i2s_audio/microphone/__init__.py b/esphome/components/i2s_audio/microphone/__init__.py index d9c31e8e7b..844f176bea 100644 --- a/esphome/components/i2s_audio/microphone/__init__.py +++ b/esphome/components/i2s_audio/microphone/__init__.py @@ -7,6 +7,9 @@ from esphome.components import microphone, esp32 from esphome.components.adc import ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, validate_adc_pin from .. import ( + CONF_I2S_MODE, + CONF_PRIMARY, + I2S_MODE_OPTIONS, i2s_audio_ns, I2SAudioComponent, I2SAudioIn, @@ -68,6 +71,9 @@ BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend( _validate_bits, cv.enum(BITS_PER_SAMPLE) ), cv.Optional(CONF_USE_APLL, default=False): cv.boolean, + cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.enum( + I2S_MODE_OPTIONS, lower=True + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -107,6 +113,7 @@ async def to_code(config): cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN])) cg.add(var.set_pdm(config[CONF_PDM])) + cg.add(var.set_i2s_mode(config[CONF_I2S_MODE])) cg.add(var.set_channel(config[CONF_CHANNEL])) cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index a672348d85..009fecdf90 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -46,7 +46,7 @@ void I2SAudioMicrophone::start_() { return; // Waiting for another i2s to return lock } i2s_driver_config_t config = { - .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX), + .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_RX), .sample_rate = this->sample_rate_, .bits_per_sample = this->bits_per_sample_, .channel_format = this->channel_, @@ -174,8 +174,7 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) { size_t samples_read = bytes_read / sizeof(int32_t); samples.resize(samples_read); for (size_t i = 0; i < samples_read; i++) { - int32_t temp = reinterpret_cast(buf)[i] >> 14; - samples[i] = clamp(temp, INT16_MIN, INT16_MAX); + samples[i] = reinterpret_cast(buf)[i] >> 16; } memcpy(buf, samples.data(), samples_read * sizeof(int16_t)); return samples_read * sizeof(int16_t); diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index 68b9a94fbd..07ca0528aa 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -30,6 +30,8 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub } #endif + void set_i2s_mode(i2s_mode_t mode) { this->i2s_mode_ = mode; } + void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; } void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } @@ -46,6 +48,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub bool adc_{false}; #endif bool pdm_{false}; + i2s_mode_t i2s_mode_{}; i2s_channel_fmt_t channel_; uint32_t sample_rate_; i2s_bits_per_sample_t bits_per_sample_; From c910fdf7e52354e8d4945bdeac44cd2000448d4c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:29:45 +1200 Subject: [PATCH 0046/1052] [micro_wake_word] Allow simpler model config (#7094) --- esphome/components/micro_wake_word/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index 3d3459ccab..c2faca25f4 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -357,7 +357,9 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(MicroWakeWord), cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), - cv.Required(CONF_MODELS): cv.ensure_list(MODEL_SCHEMA), + cv.Required(CONF_MODELS): cv.ensure_list( + cv.maybe_simple_value(MODEL_SCHEMA, key=CONF_MODEL) + ), cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( single=True ), From eaf2bb70d9008d040427ec446cf9c4f7e515d524 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 14 Jul 2024 15:06:10 -0500 Subject: [PATCH 0047/1052] [improv_serial] Fix linker error created in #6998 (#7082) --- esphome/components/improv_serial/improv_serial_component.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 02ffa9f31c..12809e38cb 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -57,7 +57,7 @@ optional ImprovSerialComponent::read_byte_() { } } break; -#ifdef USE_LOGGER_USB_CDC +#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) case logger::UART_SELECTION_USB_CDC: #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) if (esp_usb_console_available_for_read()) { @@ -99,7 +99,7 @@ void ImprovSerialComponent::write_data_(std::vector &data) { #endif // !USE_ESP32_VARIANT_ESP32C3 && !USE_ESP32_VARIANT_ESP32S2 && !USE_ESP32_VARIANT_ESP32S3 uart_write_bytes(this->uart_num_, data.data(), data.size()); break; -#ifdef USE_LOGGER_USB_CDC +#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) case logger::UART_SELECTION_USB_CDC: { const char *msg = (char *) data.data(); esp_usb_console_write_buf(msg, data.size()); @@ -109,6 +109,7 @@ void ImprovSerialComponent::write_data_(std::vector &data) { #ifdef USE_LOGGER_USB_SERIAL_JTAG case logger::UART_SELECTION_USB_SERIAL_JTAG: usb_serial_jtag_write_bytes((char *) data.data(), data.size(), 20 / portTICK_PERIOD_MS); + delay(10); usb_serial_jtag_ll_txfifo_flush(); // fixes for issue in IDF 4.4.7 break; #endif // USE_LOGGER_USB_SERIAL_JTAG From 41baf7066040900571bee2caeb5388578ce70268 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:28:41 +1200 Subject: [PATCH 0048/1052] [i2s_audio] Allow config for primary/secondary i2s mode (#7092) --- esphome/components/i2s_audio/__init__.py | 10 ++++++++++ esphome/components/i2s_audio/microphone/__init__.py | 7 +++++++ .../i2s_audio/microphone/i2s_audio_microphone.cpp | 5 ++--- .../i2s_audio/microphone/i2s_audio_microphone.h | 3 +++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index d72e13630f..05e44696d8 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -25,6 +25,10 @@ CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin" CONF_I2S_AUDIO = "i2s_audio" CONF_I2S_AUDIO_ID = "i2s_audio_id" +CONF_I2S_MODE = "i2s_mode" +CONF_PRIMARY = "primary" +CONF_SECONDARY = "secondary" + i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio") I2SAudioComponent = i2s_audio_ns.class_("I2SAudioComponent", cg.Component) I2SAudioIn = i2s_audio_ns.class_("I2SAudioIn", cg.Parented.template(I2SAudioComponent)) @@ -32,6 +36,12 @@ I2SAudioOut = i2s_audio_ns.class_( "I2SAudioOut", cg.Parented.template(I2SAudioComponent) ) +i2s_mode_t = cg.global_ns.enum("i2s_mode_t") +I2S_MODE_OPTIONS = { + CONF_PRIMARY: i2s_mode_t.I2S_MODE_MASTER, # NOLINT + CONF_SECONDARY: i2s_mode_t.I2S_MODE_SLAVE, # NOLINT +} + # https://github.com/espressif/esp-idf/blob/master/components/soc/{variant}/include/soc/soc_caps.h I2S_PORTS = { VARIANT_ESP32: 2, diff --git a/esphome/components/i2s_audio/microphone/__init__.py b/esphome/components/i2s_audio/microphone/__init__.py index d9c31e8e7b..844f176bea 100644 --- a/esphome/components/i2s_audio/microphone/__init__.py +++ b/esphome/components/i2s_audio/microphone/__init__.py @@ -7,6 +7,9 @@ from esphome.components import microphone, esp32 from esphome.components.adc import ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, validate_adc_pin from .. import ( + CONF_I2S_MODE, + CONF_PRIMARY, + I2S_MODE_OPTIONS, i2s_audio_ns, I2SAudioComponent, I2SAudioIn, @@ -68,6 +71,9 @@ BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend( _validate_bits, cv.enum(BITS_PER_SAMPLE) ), cv.Optional(CONF_USE_APLL, default=False): cv.boolean, + cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.enum( + I2S_MODE_OPTIONS, lower=True + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -107,6 +113,7 @@ async def to_code(config): cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN])) cg.add(var.set_pdm(config[CONF_PDM])) + cg.add(var.set_i2s_mode(config[CONF_I2S_MODE])) cg.add(var.set_channel(config[CONF_CHANNEL])) cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index a672348d85..009fecdf90 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -46,7 +46,7 @@ void I2SAudioMicrophone::start_() { return; // Waiting for another i2s to return lock } i2s_driver_config_t config = { - .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_RX), + .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_RX), .sample_rate = this->sample_rate_, .bits_per_sample = this->bits_per_sample_, .channel_format = this->channel_, @@ -174,8 +174,7 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) { size_t samples_read = bytes_read / sizeof(int32_t); samples.resize(samples_read); for (size_t i = 0; i < samples_read; i++) { - int32_t temp = reinterpret_cast(buf)[i] >> 14; - samples[i] = clamp(temp, INT16_MIN, INT16_MAX); + samples[i] = reinterpret_cast(buf)[i] >> 16; } memcpy(buf, samples.data(), samples_read * sizeof(int16_t)); return samples_read * sizeof(int16_t); diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index 68b9a94fbd..07ca0528aa 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -30,6 +30,8 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub } #endif + void set_i2s_mode(i2s_mode_t mode) { this->i2s_mode_ = mode; } + void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; } void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } @@ -46,6 +48,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub bool adc_{false}; #endif bool pdm_{false}; + i2s_mode_t i2s_mode_{}; i2s_channel_fmt_t channel_; uint32_t sample_rate_; i2s_bits_per_sample_t bits_per_sample_; From 0bbefb5b2a08f0f6b163f4d86c05b025e704b648 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:29:45 +1200 Subject: [PATCH 0049/1052] [micro_wake_word] Allow simpler model config (#7094) --- esphome/components/micro_wake_word/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index 3d3459ccab..c2faca25f4 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -357,7 +357,9 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(MicroWakeWord), cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone), - cv.Required(CONF_MODELS): cv.ensure_list(MODEL_SCHEMA), + cv.Required(CONF_MODELS): cv.ensure_list( + cv.maybe_simple_value(MODEL_SCHEMA, key=CONF_MODEL) + ), cv.Optional(CONF_ON_WAKE_WORD_DETECTED): automation.validate_automation( single=True ), From 4af8230b4f1b8c420caf53e7270d9f8e31d988c4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:51:13 +1200 Subject: [PATCH 0050/1052] Bump version to 2024.7.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4776de4d67..dacd839eab 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.0b2" +__version__ = "2024.7.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 0b3fe73b74b44f0be4f282a4cbe74252ba51453c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:13:02 +1200 Subject: [PATCH 0051/1052] Bump docker/build-push-action from 6.3.0 to 6.4.0 in /.github/actions/build-image (#7089) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index d5baf339aa..ac9a6ae53a 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.3.0 + uses: docker/build-push-action@v6.4.0 with: context: . file: ./docker/Dockerfile @@ -69,7 +69,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.3.0 + uses: docker/build-push-action@v6.4.0 with: context: . file: ./docker/Dockerfile From 8980996b1aa84316e71b8ba5dea26c38c7b3f364 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 16 Jul 2024 07:14:33 +0200 Subject: [PATCH 0052/1052] [CI] add web_server v1 test (#7090) --- tests/components/web_server/common.yaml | 4 ---- tests/components/web_server/common_v1.yaml | 5 +++++ tests/components/web_server/common_v2.yaml | 5 +++++ tests/components/web_server/test.esp32-ard.yaml | 2 +- tests/components/web_server/test.esp32-c3-ard.yaml | 2 +- tests/components/web_server/test.esp32-c3-idf.yaml | 2 +- tests/components/web_server/test.esp32-idf.yaml | 2 +- tests/components/web_server/test.esp8266-ard.yaml | 2 +- tests/components/web_server/test_v1.esp32-ard.yaml | 1 + 9 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 tests/components/web_server/common_v1.yaml create mode 100644 tests/components/web_server/common_v2.yaml create mode 100644 tests/components/web_server/test_v1.esp32-ard.yaml diff --git a/tests/components/web_server/common.yaml b/tests/components/web_server/common.yaml index 338d9e97d2..eb768eeb91 100644 --- a/tests/components/web_server/common.yaml +++ b/tests/components/web_server/common.yaml @@ -2,10 +2,6 @@ wifi: ssid: MySSID password: password1 -web_server: - port: 8080 - version: 2 - binary_sensor: cover: fan: diff --git a/tests/components/web_server/common_v1.yaml b/tests/components/web_server/common_v1.yaml new file mode 100644 index 0000000000..bf5aab4ce6 --- /dev/null +++ b/tests/components/web_server/common_v1.yaml @@ -0,0 +1,5 @@ +<<: !include common.yaml + +web_server: + port: 8080 + version: 1 diff --git a/tests/components/web_server/common_v2.yaml b/tests/components/web_server/common_v2.yaml new file mode 100644 index 0000000000..564c43e553 --- /dev/null +++ b/tests/components/web_server/common_v2.yaml @@ -0,0 +1,5 @@ +<<: !include common.yaml + +web_server: + port: 8080 + version: 2 diff --git a/tests/components/web_server/test.esp32-ard.yaml b/tests/components/web_server/test.esp32-ard.yaml index dade44d145..7e6658e20e 100644 --- a/tests/components/web_server/test.esp32-ard.yaml +++ b/tests/components/web_server/test.esp32-ard.yaml @@ -1 +1 @@ -<<: !include common.yaml +<<: !include common_v2.yaml diff --git a/tests/components/web_server/test.esp32-c3-ard.yaml b/tests/components/web_server/test.esp32-c3-ard.yaml index dade44d145..7e6658e20e 100644 --- a/tests/components/web_server/test.esp32-c3-ard.yaml +++ b/tests/components/web_server/test.esp32-c3-ard.yaml @@ -1 +1 @@ -<<: !include common.yaml +<<: !include common_v2.yaml diff --git a/tests/components/web_server/test.esp32-c3-idf.yaml b/tests/components/web_server/test.esp32-c3-idf.yaml index dade44d145..7e6658e20e 100644 --- a/tests/components/web_server/test.esp32-c3-idf.yaml +++ b/tests/components/web_server/test.esp32-c3-idf.yaml @@ -1 +1 @@ -<<: !include common.yaml +<<: !include common_v2.yaml diff --git a/tests/components/web_server/test.esp32-idf.yaml b/tests/components/web_server/test.esp32-idf.yaml index dade44d145..7e6658e20e 100644 --- a/tests/components/web_server/test.esp32-idf.yaml +++ b/tests/components/web_server/test.esp32-idf.yaml @@ -1 +1 @@ -<<: !include common.yaml +<<: !include common_v2.yaml diff --git a/tests/components/web_server/test.esp8266-ard.yaml b/tests/components/web_server/test.esp8266-ard.yaml index dade44d145..7e6658e20e 100644 --- a/tests/components/web_server/test.esp8266-ard.yaml +++ b/tests/components/web_server/test.esp8266-ard.yaml @@ -1 +1 @@ -<<: !include common.yaml +<<: !include common_v2.yaml diff --git a/tests/components/web_server/test_v1.esp32-ard.yaml b/tests/components/web_server/test_v1.esp32-ard.yaml new file mode 100644 index 0000000000..389a930284 --- /dev/null +++ b/tests/components/web_server/test_v1.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common_v1.yaml From 659fdefccb851fa9e76d560b7574e5589b890d24 Mon Sep 17 00:00:00 2001 From: NewoPL <27411874+NewoPL@users.noreply.github.com> Date: Tue, 16 Jul 2024 08:28:23 +0200 Subject: [PATCH 0053/1052] [wifi] Hostname may not be set as expected on Arduino platform (#7050) * bug #6014: workaround for not setting hostname on arduino plarform * moving handle initailisation to ESPHOME_EVENT_ID_WIFI_STA_START callback --- esphome/components/wifi/wifi_component_esp32_arduino.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index fc954a2333..71548b7a3e 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -82,8 +82,8 @@ bool WiFiComponent::wifi_mode_(optional sta, optional ap) { // WiFiClass::mode above calls esp_netif_create_default_wifi_sta() and // esp_netif_create_default_wifi_ap(), which creates the interfaces. - if (set_sta) - s_sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + // s_sta_netif handle is set during ESPHOME_EVENT_ID_WIFI_STA_START event + #ifdef USE_WIFI_AP if (set_ap) s_ap_netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"); @@ -495,6 +495,7 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ case ESPHOME_EVENT_ID_WIFI_STA_START: { ESP_LOGV(TAG, "Event: WiFi STA start"); // apply hostname + s_sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); esp_err_t err = esp_netif_set_hostname(s_sta_netif, App.get_name().c_str()); if (err != ERR_OK) { ESP_LOGW(TAG, "esp_netif_set_hostname failed: %s", esp_err_to_name(err)); From 193db50668d7f343e5ae22bcf6e3a5b1e2309adf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:18:43 +1200 Subject: [PATCH 0054/1052] [ota] Print Arduino update errors (#7096) --- .../ota/ota_backend_arduino_esp32.cpp | 29 ++++++++++++----- .../ota/ota_backend_arduino_esp8266.cpp | 31 ++++++++++++++----- .../ota/ota_backend_arduino_libretiny.cpp | 29 ++++++++++++----- .../ota/ota_backend_arduino_rp2040.cpp | 29 ++++++++++++----- 4 files changed, 89 insertions(+), 29 deletions(-) diff --git a/esphome/components/ota/ota_backend_arduino_esp32.cpp b/esphome/components/ota/ota_backend_arduino_esp32.cpp index 62c6a72388..15dfc98a6c 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp32.cpp @@ -1,14 +1,17 @@ #ifdef USE_ESP32_FRAMEWORK_ARDUINO #include "esphome/core/defines.h" +#include "esphome/core/log.h" -#include "ota_backend_arduino_esp32.h" #include "ota_backend.h" +#include "ota_backend_arduino_esp32.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_esp32"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { @@ -20,6 +23,9 @@ OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { uint8_t error = Update.getError(); if (error == UPDATE_ERROR_SIZE) return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -27,16 +33,25 @@ void ArduinoESP32OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5 OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoESP32OTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoESP32OTABackend::abort() { Update.abort(); } diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota/ota_backend_arduino_esp8266.cpp index b317075bd0..42edbf5d2b 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp8266.cpp @@ -1,16 +1,19 @@ #ifdef USE_ARDUINO #ifdef USE_ESP8266 -#include "ota_backend.h" #include "ota_backend_arduino_esp8266.h" +#include "ota_backend.h" -#include "esphome/core/defines.h" #include "esphome/components/esp8266/preferences.h" +#include "esphome/core/defines.h" +#include "esphome/core/log.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_esp8266"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { @@ -29,6 +32,9 @@ OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; if (error == UPDATE_ERROR_SPACE) return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -36,16 +42,25 @@ void ArduinoESP8266OTABackend::set_update_md5(const char *md5) { Update.setMD5(m OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoESP8266OTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoESP8266OTABackend::abort() { diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.cpp b/esphome/components/ota/ota_backend_arduino_libretiny.cpp index df4e774ebc..6b2cf80684 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota/ota_backend_arduino_libretiny.cpp @@ -1,14 +1,17 @@ #ifdef USE_LIBRETINY -#include "ota_backend.h" #include "ota_backend_arduino_libretiny.h" +#include "ota_backend.h" #include "esphome/core/defines.h" +#include "esphome/core/log.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_libretiny"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { @@ -20,6 +23,9 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { uint8_t error = Update.getError(); if (error == UPDATE_ERROR_SIZE) return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -27,16 +33,25 @@ void ArduinoLibreTinyOTABackend::set_update_md5(const char *md5) { Update.setMD5 OTAResponseTypes ArduinoLibreTinyOTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoLibreTinyOTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoLibreTinyOTABackend::abort() { Update.abort(); } diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.cpp b/esphome/components/ota/ota_backend_arduino_rp2040.cpp index 4448b0c95e..ffeab2e93f 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota/ota_backend_arduino_rp2040.cpp @@ -1,16 +1,19 @@ #ifdef USE_ARDUINO #ifdef USE_RP2040 -#include "ota_backend.h" #include "ota_backend_arduino_rp2040.h" +#include "ota_backend.h" #include "esphome/components/rp2040/preferences.h" #include "esphome/core/defines.h" +#include "esphome/core/log.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_rp2040"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { @@ -29,6 +32,9 @@ OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; if (error == UPDATE_ERROR_SPACE) return OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -36,16 +42,25 @@ void ArduinoRP2040OTABackend::set_update_md5(const char *md5) { Update.setMD5(md OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoRP2040OTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoRP2040OTABackend::abort() { From 10205e06cb1de3b65e81dfcdc5a20fe890155162 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:06:27 +1000 Subject: [PATCH 0055/1052] Add host uart support for MacOS (#7095) --- esphome/components/uart/uart_component_host.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/uart/uart_component_host.cpp b/esphome/components/uart/uart_component_host.cpp index d8d2fd75b8..40d3e91ab2 100644 --- a/esphome/components/uart/uart_component_host.cpp +++ b/esphome/components/uart/uart_component_host.cpp @@ -5,8 +5,8 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#ifndef __linux__ -#error This HostUartComponent implementation is only for Linux +#if !(defined(__linux__) || defined(__APPLE__)) +#error This HostUartComponent implementation is not supported on this host OS #endif #include @@ -24,6 +24,9 @@ namespace { speed_t get_baud(int baud) { +#ifdef __APPLE__ + return baud; +#else switch (baud) { case 50: return B50; @@ -88,6 +91,7 @@ speed_t get_baud(int baud) { default: return B0; } +#endif } } // namespace From f153a7b0fd20bdf9a6b0772553c99f05021b04b7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:18:43 +1200 Subject: [PATCH 0056/1052] [ota] Print Arduino update errors (#7096) --- .../ota/ota_backend_arduino_esp32.cpp | 29 ++++++++++++----- .../ota/ota_backend_arduino_esp8266.cpp | 31 ++++++++++++++----- .../ota/ota_backend_arduino_libretiny.cpp | 29 ++++++++++++----- .../ota/ota_backend_arduino_rp2040.cpp | 29 ++++++++++++----- 4 files changed, 89 insertions(+), 29 deletions(-) diff --git a/esphome/components/ota/ota_backend_arduino_esp32.cpp b/esphome/components/ota/ota_backend_arduino_esp32.cpp index 62c6a72388..15dfc98a6c 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp32.cpp @@ -1,14 +1,17 @@ #ifdef USE_ESP32_FRAMEWORK_ARDUINO #include "esphome/core/defines.h" +#include "esphome/core/log.h" -#include "ota_backend_arduino_esp32.h" #include "ota_backend.h" +#include "ota_backend_arduino_esp32.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_esp32"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { @@ -20,6 +23,9 @@ OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { uint8_t error = Update.getError(); if (error == UPDATE_ERROR_SIZE) return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -27,16 +33,25 @@ void ArduinoESP32OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5 OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoESP32OTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoESP32OTABackend::abort() { Update.abort(); } diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota/ota_backend_arduino_esp8266.cpp index b317075bd0..42edbf5d2b 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp8266.cpp @@ -1,16 +1,19 @@ #ifdef USE_ARDUINO #ifdef USE_ESP8266 -#include "ota_backend.h" #include "ota_backend_arduino_esp8266.h" +#include "ota_backend.h" -#include "esphome/core/defines.h" #include "esphome/components/esp8266/preferences.h" +#include "esphome/core/defines.h" +#include "esphome/core/log.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_esp8266"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { @@ -29,6 +32,9 @@ OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; if (error == UPDATE_ERROR_SPACE) return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -36,16 +42,25 @@ void ArduinoESP8266OTABackend::set_update_md5(const char *md5) { Update.setMD5(m OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoESP8266OTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoESP8266OTABackend::abort() { diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.cpp b/esphome/components/ota/ota_backend_arduino_libretiny.cpp index df4e774ebc..6b2cf80684 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota/ota_backend_arduino_libretiny.cpp @@ -1,14 +1,17 @@ #ifdef USE_LIBRETINY -#include "ota_backend.h" #include "ota_backend_arduino_libretiny.h" +#include "ota_backend.h" #include "esphome/core/defines.h" +#include "esphome/core/log.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_libretiny"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { @@ -20,6 +23,9 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { uint8_t error = Update.getError(); if (error == UPDATE_ERROR_SIZE) return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -27,16 +33,25 @@ void ArduinoLibreTinyOTABackend::set_update_md5(const char *md5) { Update.setMD5 OTAResponseTypes ArduinoLibreTinyOTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoLibreTinyOTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoLibreTinyOTABackend::abort() { Update.abort(); } diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.cpp b/esphome/components/ota/ota_backend_arduino_rp2040.cpp index 4448b0c95e..ffeab2e93f 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota/ota_backend_arduino_rp2040.cpp @@ -1,16 +1,19 @@ #ifdef USE_ARDUINO #ifdef USE_RP2040 -#include "ota_backend.h" #include "ota_backend_arduino_rp2040.h" +#include "ota_backend.h" #include "esphome/components/rp2040/preferences.h" #include "esphome/core/defines.h" +#include "esphome/core/log.h" #include namespace esphome { namespace ota { +static const char *const TAG = "ota.arduino_rp2040"; + std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { @@ -29,6 +32,9 @@ OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; if (error == UPDATE_ERROR_SPACE) return OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE; + + ESP_LOGE(TAG, "Begin error: %d", error); + return OTA_RESPONSE_ERROR_UNKNOWN; } @@ -36,16 +42,25 @@ void ArduinoRP2040OTABackend::set_update_md5(const char *md5) { Update.setMD5(md OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); - if (written != len) { - return OTA_RESPONSE_ERROR_WRITING_FLASH; + if (written == len) { + return OTA_RESPONSE_OK; } - return OTA_RESPONSE_OK; + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "Write error: %d", error); + + return OTA_RESPONSE_ERROR_WRITING_FLASH; } OTAResponseTypes ArduinoRP2040OTABackend::end() { - if (!Update.end()) - return OTA_RESPONSE_ERROR_UPDATE_END; - return OTA_RESPONSE_OK; + if (Update.end()) { + return OTA_RESPONSE_OK; + } + + uint8_t error = Update.getError(); + ESP_LOGE(TAG, "End error: %d", error); + + return OTA_RESPONSE_ERROR_UPDATE_END; } void ArduinoRP2040OTABackend::abort() { From c512d5ebb66346cea81144b207408f19936f006d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:15:19 +1200 Subject: [PATCH 0057/1052] Bump version to 2024.7.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index dacd839eab..b59a2e9517 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.0b3" +__version__ = "2024.7.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From e15d0ee150e879e2a6dce7b53ad7d35a2d500624 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:54:44 +1200 Subject: [PATCH 0058/1052] Bump version to 2024.7.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index b59a2e9517..d581f68470 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.0b4" +__version__ = "2024.7.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From dd20c5eab0ca2441e7911ae85638a5a8237534cc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:33:30 +1200 Subject: [PATCH 0059/1052] Bump docker/build-push-action from 6.4.0 to 6.4.1 in /.github/actions/build-image (#7102) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index ac9a6ae53a..e2febdad1b 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.4.0 + uses: docker/build-push-action@v6.4.1 with: context: . file: ./docker/Dockerfile @@ -69,7 +69,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.4.0 + uses: docker/build-push-action@v6.4.1 with: context: . file: ./docker/Dockerfile From b32078a5fe8b05f6f3bf4234b24ec8b3a6871d02 Mon Sep 17 00:00:00 2001 From: Alex Cortelyou <1689668+acortelyou@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:04:11 -0700 Subject: [PATCH 0060/1052] Prevent rename from deleting new config (#7104) --- esphome/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 5ff1a28ec7..b13f96daf7 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -695,7 +695,8 @@ def command_rename(args, config): os.remove(new_path) return 1 - os.remove(CORE.config_path) + if CORE.config_path != new_path: + os.remove(CORE.config_path) print(color(Fore.BOLD_GREEN, "SUCCESS")) print() From 0fb89d186964bc31b28b9873d3245981d19ce8df Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 19 Jul 2024 11:26:21 +1200 Subject: [PATCH 0061/1052] [code-quality] Add some ruff configuration (#7103) --- pyproject.toml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index fe558f695f..cfc279845f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,3 +105,33 @@ disable = [ [tool.pylint.FORMAT] expected-line-ending-format = "LF" + +[tool.ruff] +required-version = ">=0.5.0" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle + "F", # pyflakes/autoflake + "I", # isort + "PL", # pylint + "UP", # pyupgrade +] + +ignore = [ + "E501", # line too long + "PLR0911", # Too many return statements ({returns} > {max_returns}) + "PLR0912", # Too many branches ({branches} > {max_branches}) + "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) + "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 +] + +[tool.ruff.lint.isort] +force-sort-within-sections = true +known-first-party = [ + "esphome", +] +combine-as-imports = true +split-on-trailing-comma = false From c5b77f45902c7a3497278ecbadc6c4b799927d11 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Fri, 19 Jul 2024 06:35:41 +0200 Subject: [PATCH 0062/1052] [web_server] move v1 code to separate file (#7091) --- esphome/components/web_server/web_server.cpp | 209 ----------------- .../components/web_server/web_server_v1.cpp | 217 ++++++++++++++++++ 2 files changed, 217 insertions(+), 209 deletions(-) create mode 100644 esphome/components/web_server/web_server_v1.cpp diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 9a1641e86f..6fb04f558a 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -46,29 +46,6 @@ static const char *const HEADER_CORS_REQ_PNA = "Access-Control-Request-Private-N static const char *const HEADER_CORS_ALLOW_PNA = "Access-Control-Allow-Private-Network"; #endif -#if USE_WEBSERVER_VERSION == 1 -void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action, - const std::function &action_func = nullptr) { - stream->print("print(klass.c_str()); - if (obj->is_internal()) - stream->print(" internal"); - stream->print("\" id=\""); - stream->print(klass.c_str()); - stream->print("-"); - stream->print(obj->get_object_id().c_str()); - stream->print("\">"); - stream->print(obj->get_name().c_str()); - stream->print(""); - stream->print(action.c_str()); - if (action_func) { - action_func(*stream, obj); - } - stream->print(""); - stream->print(""); -} -#endif - UrlMatch match_url(const std::string &url, bool only_domain = false) { UrlMatch match; match.valid = false; @@ -102,11 +79,6 @@ WebServer::WebServer(web_server_base::WebServerBase *base) #endif } -#if USE_WEBSERVER_VERSION == 1 -void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; } -void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; } -#endif - #ifdef USE_WEBSERVER_CSS_INCLUDE void WebServer::set_css_include(const char *css_include) { this->css_include_ = css_include; } #endif @@ -181,187 +153,6 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { response->addHeader("Content-Encoding", "gzip"); request->send(response); } -#elif USE_WEBSERVER_VERSION == 1 -void WebServer::handle_index_request(AsyncWebServerRequest *request) { - AsyncResponseStream *stream = request->beginResponseStream("text/html"); - const std::string &title = App.get_name(); - stream->print(F("")); - stream->print(title.c_str()); - stream->print(F("")); -#ifdef USE_WEBSERVER_CSS_INCLUDE - stream->print(F("")); -#endif - if (strlen(this->css_url_) > 0) { - stream->print(F(R"(print(this->css_url_); - stream->print(F("\">")); - } - stream->print(F("")); - stream->print(F("

")); - stream->print(title.c_str()); - stream->print(F("

")); - stream->print(F("

States

")); - -#ifdef USE_SENSOR - for (auto *obj : App.get_sensors()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "sensor", ""); - } -#endif - -#ifdef USE_SWITCH - for (auto *obj : App.get_switches()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "switch", ""); - } -#endif - -#ifdef USE_BUTTON - for (auto *obj : App.get_buttons()) - write_row(stream, obj, "button", ""); -#endif - -#ifdef USE_BINARY_SENSOR - for (auto *obj : App.get_binary_sensors()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "binary_sensor", ""); - } -#endif - -#ifdef USE_FAN - for (auto *obj : App.get_fans()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "fan", ""); - } -#endif - -#ifdef USE_LIGHT - for (auto *obj : App.get_lights()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "light", ""); - } -#endif - -#ifdef USE_TEXT_SENSOR - for (auto *obj : App.get_text_sensors()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "text_sensor", ""); - } -#endif - -#ifdef USE_COVER - for (auto *obj : App.get_covers()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "cover", ""); - } -#endif - -#ifdef USE_NUMBER - for (auto *obj : App.get_numbers()) { - if (this->include_internal_ || !obj->is_internal()) { - write_row(stream, obj, "number", "", [](AsyncResponseStream &stream, EntityBase *obj) { - number::Number *number = (number::Number *) obj; - stream.print(R"(traits.get_min_value()); - stream.print(R"(" max=")"); - stream.print(number->traits.get_max_value()); - stream.print(R"(" step=")"); - stream.print(number->traits.get_step()); - stream.print(R"(" value=")"); - stream.print(number->state); - stream.print(R"("/>)"); - }); - } - } -#endif - -#ifdef USE_TEXT - for (auto *obj : App.get_texts()) { - if (this->include_internal_ || !obj->is_internal()) { - write_row(stream, obj, "text", "", [](AsyncResponseStream &stream, EntityBase *obj) { - text::Text *text = (text::Text *) obj; - auto mode = (int) text->traits.get_mode(); - stream.print(R"(traits.get_min_length()); - stream.print(R"(" maxlength=")"); - stream.print(text->traits.get_max_length()); - stream.print(R"(" pattern=")"); - stream.print(text->traits.get_pattern().c_str()); - stream.print(R"(" value=")"); - stream.print(text->state.c_str()); - stream.print(R"("/>)"); - }); - } - } -#endif - -#ifdef USE_SELECT - for (auto *obj : App.get_selects()) { - if (this->include_internal_ || !obj->is_internal()) { - write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, EntityBase *obj) { - select::Select *select = (select::Select *) obj; - stream.print(""); - }); - } - } -#endif - -#ifdef USE_LOCK - for (auto *obj : App.get_locks()) { - if (this->include_internal_ || !obj->is_internal()) { - write_row(stream, obj, "lock", "", [](AsyncResponseStream &stream, EntityBase *obj) { - lock::Lock *lock = (lock::Lock *) obj; - stream.print(""); - if (lock->traits.get_supports_open()) { - stream.print(""); - } - }); - } - } -#endif - -#ifdef USE_CLIMATE - for (auto *obj : App.get_climates()) { - if (this->include_internal_ || !obj->is_internal()) - write_row(stream, obj, "climate", ""); - } -#endif - - stream->print(F("
NameStateActions

See ESPHome Web API for " - "REST API documentation.

")); - if (this->allow_ota_) { - stream->print( - F("

OTA Update

")); - } - stream->print(F("

Debug Log

"));
-#ifdef USE_WEBSERVER_JS_INCLUDE
-  if (this->js_include_ != nullptr) {
-    stream->print(F(""));
-  }
-#endif
-  if (strlen(this->js_url_) > 0) {
-    stream->print(F(""));
-  }
-  stream->print(F("
")); - request->send(stream); -} #elif USE_WEBSERVER_VERSION >= 2 void WebServer::handle_index_request(AsyncWebServerRequest *request) { AsyncWebServerResponse *response = diff --git a/esphome/components/web_server/web_server_v1.cpp b/esphome/components/web_server/web_server_v1.cpp new file mode 100644 index 0000000000..c9b38a2dc4 --- /dev/null +++ b/esphome/components/web_server/web_server_v1.cpp @@ -0,0 +1,217 @@ +#include "web_server.h" +#include "esphome/core/application.h" + +#if USE_WEBSERVER_VERSION == 1 + +namespace esphome { +namespace web_server { + +void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action, + const std::function &action_func = nullptr) { + stream->print("print(klass.c_str()); + if (obj->is_internal()) + stream->print(" internal"); + stream->print("\" id=\""); + stream->print(klass.c_str()); + stream->print("-"); + stream->print(obj->get_object_id().c_str()); + stream->print("\">"); + stream->print(obj->get_name().c_str()); + stream->print(""); + stream->print(action.c_str()); + if (action_func) { + action_func(*stream, obj); + } + stream->print(""); + stream->print(""); +} + +void WebServer::set_css_url(const char *css_url) { this->css_url_ = css_url; } + +void WebServer::set_js_url(const char *js_url) { this->js_url_ = js_url; } + +void WebServer::handle_index_request(AsyncWebServerRequest *request) { + AsyncResponseStream *stream = request->beginResponseStream("text/html"); + const std::string &title = App.get_name(); + stream->print(F("")); + stream->print(title.c_str()); + stream->print(F("")); +#ifdef USE_WEBSERVER_CSS_INCLUDE + stream->print(F("")); +#endif + if (strlen(this->css_url_) > 0) { + stream->print(F(R"(print(this->css_url_); + stream->print(F("\">")); + } + stream->print(F("")); + stream->print(F("

")); + stream->print(title.c_str()); + stream->print(F("

")); + stream->print(F("

States

")); + +#ifdef USE_SENSOR + for (auto *obj : App.get_sensors()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "sensor", ""); + } +#endif + +#ifdef USE_SWITCH + for (auto *obj : App.get_switches()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "switch", ""); + } +#endif + +#ifdef USE_BUTTON + for (auto *obj : App.get_buttons()) + write_row(stream, obj, "button", ""); +#endif + +#ifdef USE_BINARY_SENSOR + for (auto *obj : App.get_binary_sensors()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "binary_sensor", ""); + } +#endif + +#ifdef USE_FAN + for (auto *obj : App.get_fans()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "fan", ""); + } +#endif + +#ifdef USE_LIGHT + for (auto *obj : App.get_lights()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "light", ""); + } +#endif + +#ifdef USE_TEXT_SENSOR + for (auto *obj : App.get_text_sensors()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "text_sensor", ""); + } +#endif + +#ifdef USE_COVER + for (auto *obj : App.get_covers()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "cover", ""); + } +#endif + +#ifdef USE_NUMBER + for (auto *obj : App.get_numbers()) { + if (this->include_internal_ || !obj->is_internal()) { + write_row(stream, obj, "number", "", [](AsyncResponseStream &stream, EntityBase *obj) { + number::Number *number = (number::Number *) obj; + stream.print(R"(traits.get_min_value()); + stream.print(R"(" max=")"); + stream.print(number->traits.get_max_value()); + stream.print(R"(" step=")"); + stream.print(number->traits.get_step()); + stream.print(R"(" value=")"); + stream.print(number->state); + stream.print(R"("/>)"); + }); + } + } +#endif + +#ifdef USE_TEXT + for (auto *obj : App.get_texts()) { + if (this->include_internal_ || !obj->is_internal()) { + write_row(stream, obj, "text", "", [](AsyncResponseStream &stream, EntityBase *obj) { + text::Text *text = (text::Text *) obj; + auto mode = (int) text->traits.get_mode(); + stream.print(R"(traits.get_min_length()); + stream.print(R"(" maxlength=")"); + stream.print(text->traits.get_max_length()); + stream.print(R"(" pattern=")"); + stream.print(text->traits.get_pattern().c_str()); + stream.print(R"(" value=")"); + stream.print(text->state.c_str()); + stream.print(R"("/>)"); + }); + } + } +#endif + +#ifdef USE_SELECT + for (auto *obj : App.get_selects()) { + if (this->include_internal_ || !obj->is_internal()) { + write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, EntityBase *obj) { + select::Select *select = (select::Select *) obj; + stream.print(""); + }); + } + } +#endif + +#ifdef USE_LOCK + for (auto *obj : App.get_locks()) { + if (this->include_internal_ || !obj->is_internal()) { + write_row(stream, obj, "lock", "", [](AsyncResponseStream &stream, EntityBase *obj) { + lock::Lock *lock = (lock::Lock *) obj; + stream.print(""); + if (lock->traits.get_supports_open()) { + stream.print(""); + } + }); + } + } +#endif + +#ifdef USE_CLIMATE + for (auto *obj : App.get_climates()) { + if (this->include_internal_ || !obj->is_internal()) + write_row(stream, obj, "climate", ""); + } +#endif + + stream->print(F("
NameStateActions

See ESPHome Web API for " + "REST API documentation.

")); + if (this->allow_ota_) { + stream->print( + F("

OTA Update

")); + } + stream->print(F("

Debug Log

"));
+#ifdef USE_WEBSERVER_JS_INCLUDE
+  if (this->js_include_ != nullptr) {
+    stream->print(F(""));
+  }
+#endif
+  if (strlen(this->js_url_) > 0) {
+    stream->print(F(""));
+  }
+  stream->print(F("
")); + request->send(stream); +} + +} // namespace web_server +} // namespace esphome +#endif From 32b927de7e319a4a1357a047959d365ccb85e919 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Fri, 19 Jul 2024 15:15:11 -0400 Subject: [PATCH 0063/1052] revert bit shift to match previous behavior (#7109) --- .../components/i2s_audio/microphone/i2s_audio_microphone.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 009fecdf90..cb49a744fc 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -174,7 +174,8 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) { size_t samples_read = bytes_read / sizeof(int32_t); samples.resize(samples_read); for (size_t i = 0; i < samples_read; i++) { - samples[i] = reinterpret_cast(buf)[i] >> 16; + int32_t temp = reinterpret_cast(buf)[i] >> 14; + samples[i] = clamp(temp, INT16_MIN, INT16_MAX); } memcpy(buf, samples.data(), samples_read * sizeof(int16_t)); return samples_read * sizeof(int16_t); From 43b818f2b1ca949ccc1ec216db9b5682e051541c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Jul 2024 07:54:16 +1200 Subject: [PATCH 0064/1052] [validation] Add ``host`` to ``require_framework_version`` (#7107) --- esphome/config_validation.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 7259e3c062..3ef92ad460 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -829,7 +829,6 @@ def time_of_day(value): def date_time(date: bool, time: bool): - pattern_str = r"^" # Start of string if date: pattern_str += r"\d{4}-\d{1,2}-\d{1,2}" @@ -2031,6 +2030,7 @@ def require_framework_version( esp32_arduino=None, esp8266_arduino=None, rp2040_arduino=None, + host=None, max_version=False, extra_message=None, ): @@ -2065,6 +2065,13 @@ def require_framework_version( msg += f". {extra_message}" raise Invalid(msg) required = rp2040_arduino + elif CORE.is_host and framework == "host": + if host is None: + msg = "This feature is incompatible with host platform" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) + required = host else: raise Invalid( f""" From cfb20abb9f0e7fc0b25b4cc792a560b4d91eb748 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Jul 2024 08:09:06 +1200 Subject: [PATCH 0065/1052] [code-quality] Tidy up some duplicate CONFIG_SCHEMA assignments (#7106) --- esphome/components/ade7953/sensor.py | 2 +- esphome/components/bmp3xx/sensor.py | 2 +- esphome/components/ens160/sensor.py | 2 +- esphome/components/kalman_combinator/sensor.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/ade7953/sensor.py b/esphome/components/ade7953/sensor.py index 0caa2ef454..fc79888129 100644 --- a/esphome/components/ade7953/sensor.py +++ b/esphome/components/ade7953/sensor.py @@ -1,5 +1,5 @@ import esphome.config_validation as cv -CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid( +CONFIG_SCHEMA = cv.invalid( "The ade7953 sensor component has been renamed to ade7953_i2c." ) diff --git a/esphome/components/bmp3xx/sensor.py b/esphome/components/bmp3xx/sensor.py index 89753768c3..3c7927f1a8 100644 --- a/esphome/components/bmp3xx/sensor.py +++ b/esphome/components/bmp3xx/sensor.py @@ -2,6 +2,6 @@ import esphome.config_validation as cv CODEOWNERS = ["@latonita"] -CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid( +CONFIG_SCHEMA = cv.invalid( "The bmp3xx sensor component has been renamed to bmp3xx_i2c." ) diff --git a/esphome/components/ens160/sensor.py b/esphome/components/ens160/sensor.py index f666b530b3..441671fb7b 100644 --- a/esphome/components/ens160/sensor.py +++ b/esphome/components/ens160/sensor.py @@ -2,6 +2,6 @@ import esphome.config_validation as cv CODEOWNERS = ["@latonita"] -CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid( +CONFIG_SCHEMA = cv.invalid( "The ens160 sensor component has been renamed to ens160_i2c." ) diff --git a/esphome/components/kalman_combinator/sensor.py b/esphome/components/kalman_combinator/sensor.py index eca1ba7b85..c19a17462d 100644 --- a/esphome/components/kalman_combinator/sensor.py +++ b/esphome/components/kalman_combinator/sensor.py @@ -1,6 +1,6 @@ import esphome.config_validation as cv -CONFIG_SCHEMA = CONFIG_SCHEMA = cv.invalid( +CONFIG_SCHEMA = cv.invalid( "The kalman_combinator sensor has moved.\nPlease use the combination platform instead with type: kalman.\n" "See https://esphome.io/components/sensor/combination.html" ) From fbc830176f612a341b5ace5418ab4f76cd4a578c Mon Sep 17 00:00:00 2001 From: Lucio Tarantino Date: Sun, 21 Jul 2024 23:16:51 +0200 Subject: [PATCH 0066/1052] [heatpumpir] Fix BK72XX Compile error with IRremoteESP8266 (#6955) --- esphome/components/heatpumpir/climate.py | 3 +++ tests/components/heatpumpir/test.bk72xx-ard.yaml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/esphome/components/heatpumpir/climate.py b/esphome/components/heatpumpir/climate.py index 80900d7db9..9d31668deb 100644 --- a/esphome/components/heatpumpir/climate.py +++ b/esphome/components/heatpumpir/climate.py @@ -8,6 +8,7 @@ from esphome.const import ( CONF_PROTOCOL, CONF_VISUAL, ) +from esphome.core import CORE CODEOWNERS = ["@rob-deutsch"] @@ -127,3 +128,5 @@ def to_code(config): cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) cg.add_library("tonia/HeatpumpIR", "1.0.27") + if CORE.is_libretiny: + CORE.add_platformio_option("lib_ignore", "IRremoteESP8266") diff --git a/tests/components/heatpumpir/test.bk72xx-ard.yaml b/tests/components/heatpumpir/test.bk72xx-ard.yaml index 90259f1244..b616f9157c 100644 --- a/tests/components/heatpumpir/test.bk72xx-ard.yaml +++ b/tests/components/heatpumpir/test.bk72xx-ard.yaml @@ -3,6 +3,13 @@ remote_transmitter: carrier_duty_percent: 50% climate: + - platform: heatpumpir + protocol: mitsubishi_heavy_zm + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 - platform: heatpumpir protocol: daikin horizontal_default: mleft From 368662969ed4c4df267499c720497d195acbed06 Mon Sep 17 00:00:00 2001 From: Markus <974709+Links2004@users.noreply.github.com> Date: Sun, 21 Jul 2024 23:36:46 +0200 Subject: [PATCH 0067/1052] Move MQTT ip discovery to deticated config option. (#6673) --- esphome/components/mqtt/__init__.py | 8 ++++++++ esphome/components/mqtt/mqtt_client.cpp | 18 +++++++++++++++--- esphome/components/mqtt/mqtt_client.h | 6 +++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 96a02cb60e..f4bd34bfd3 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -61,6 +61,7 @@ def AUTO_LOAD(): return ["json"] +CONF_DISCOVER_IP = "discover_ip" CONF_IDF_SEND_ASYNC = "idf_send_async" CONF_SKIP_CERT_CN_CHECK = "skip_cert_cn_check" @@ -225,6 +226,7 @@ CONFIG_SCHEMA = cv.All( cv.boolean, cv.one_of("CLEAN", upper=True) ), cv.Optional(CONF_DISCOVERY_RETAIN, default=True): cv.boolean, + cv.Optional(CONF_DISCOVER_IP, default=True): cv.boolean, cv.Optional( CONF_DISCOVERY_PREFIX, default="homeassistant" ): cv.publish_topic, @@ -328,8 +330,12 @@ async def to_code(config): discovery_prefix = config[CONF_DISCOVERY_PREFIX] discovery_unique_id_generator = config[CONF_DISCOVERY_UNIQUE_ID_GENERATOR] discovery_object_id_generator = config[CONF_DISCOVERY_OBJECT_ID_GENERATOR] + discover_ip = config[CONF_DISCOVER_IP] if not discovery: + discovery_prefix = "" + + if not discovery and not discover_ip: cg.add(var.disable_discovery()) elif discovery == "CLEAN": cg.add( @@ -338,6 +344,7 @@ async def to_code(config): discovery_unique_id_generator, discovery_object_id_generator, discovery_retain, + discover_ip, True, ) ) @@ -348,6 +355,7 @@ async def to_code(config): discovery_unique_id_generator, discovery_object_id_generator, discovery_retain, + discover_ip, ) ) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index d70b9cbd30..876367aaea 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -66,7 +66,7 @@ void MQTTClientComponent::setup() { } #endif - if (this->is_discovery_enabled()) { + if (this->is_discovery_ip_enabled()) { this->subscribe( "esphome/discover", [this](const std::string &topic, const std::string &payload) { this->send_device_info_(); }, 2); @@ -82,7 +82,7 @@ void MQTTClientComponent::setup() { } void MQTTClientComponent::send_device_info_() { - if (!this->is_connected() or !this->is_discovery_enabled()) { + if (!this->is_connected() or !this->is_discovery_ip_enabled()) { return; } std::string topic = "esphome/discover/"; @@ -99,6 +99,9 @@ void MQTTClientComponent::send_device_info_() { } } root["name"] = App.get_name(); + if (!App.get_friendly_name().empty()) { + root["friendly_name"] = App.get_friendly_name(); + } #ifdef USE_API root["port"] = api::global_api_server->get_port(); #endif @@ -130,6 +133,10 @@ void MQTTClientComponent::send_device_info_() { #ifdef USE_DASHBOARD_IMPORT root["package_import_url"] = dashboard_import::get_package_import_url(); #endif + +#ifdef USE_API_NOISE + root["api_encryption"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256"; +#endif }, 2, this->discovery_info_.retain); } @@ -140,6 +147,9 @@ void MQTTClientComponent::dump_config() { 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()); + 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)); @@ -581,6 +591,7 @@ void MQTTClientComponent::disable_shutdown_message() { this->recalculate_availability_(); } bool MQTTClientComponent::is_discovery_enabled() const { return !this->discovery_info_.prefix.empty(); } +bool MQTTClientComponent::is_discovery_ip_enabled() const { return this->discovery_info_.discover_ip; } const Availability &MQTTClientComponent::get_availability() { return this->availability_; } void MQTTClientComponent::recalculate_availability_() { if (this->birth_message_.topic.empty() || this->birth_message_.topic != this->last_will_.topic) { @@ -606,8 +617,9 @@ void MQTTClientComponent::set_shutdown_message(MQTTMessage &&message) { this->sh void MQTTClientComponent::set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator, MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain, - bool clean) { + bool discover_ip, bool clean) { this->discovery_info_.prefix = std::move(prefix); + this->discovery_info_.discover_ip = discover_ip; this->discovery_info_.unique_id_generator = unique_id_generator; this->discovery_info_.object_id_generator = object_id_generator; this->discovery_info_.retain = retain; diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 454316aa87..b0d3bbe66d 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -79,6 +79,7 @@ enum MQTTDiscoveryObjectIdGenerator { struct MQTTDiscoveryInfo { std::string prefix; ///< The Home Assistant discovery prefix. Empty means disabled. bool retain; ///< Whether to retain discovery messages. + bool discover_ip; ///< Enable the Home Assistant device discovery. bool clean; MQTTDiscoveryUniqueIdGenerator unique_id_generator; MQTTDiscoveryObjectIdGenerator object_id_generator; @@ -122,12 +123,14 @@ class MQTTClientComponent : public Component { * @param retain Whether to retain discovery messages. */ void set_discovery_info(std::string &&prefix, MQTTDiscoveryUniqueIdGenerator unique_id_generator, - MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain, bool clean = false); + MQTTDiscoveryObjectIdGenerator object_id_generator, bool retain, bool discover_ip, + bool clean = false); /// Get Home Assistant discovery info. const MQTTDiscoveryInfo &get_discovery_info() const; /// Globally disable Home Assistant discovery. void disable_discovery(); bool is_discovery_enabled() const; + bool is_discovery_ip_enabled() const; #if ASYNC_TCP_SSL_ENABLED /** Add a SSL fingerprint to use for TCP SSL connections to the MQTT broker. @@ -290,6 +293,7 @@ class MQTTClientComponent : public Component { MQTTDiscoveryInfo discovery_info_{ .prefix = "homeassistant", .retain = true, + .discover_ip = true, .clean = false, .unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR, .object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR, From 40e79299d55cd61e2f81b9f694e37ac3598c3d56 Mon Sep 17 00:00:00 2001 From: rnauber <7414650+rnauber@users.noreply.github.com> Date: Sun, 21 Jul 2024 23:57:59 +0200 Subject: [PATCH 0068/1052] Feature/m5angle8: Add support for m5angle8 input device (#6799) Co-authored-by: Richard Nauber Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/m5stack_8angle/__init__.py | 33 +++++++++ .../m5stack_8angle/binary_sensor/__init__.py | 30 ++++++++ .../m5stack_8angle_binary_sensor.cpp | 17 +++++ .../m5stack_8angle_binary_sensor.h | 19 +++++ .../m5stack_8angle/light/__init__.py | 31 ++++++++ .../light/m5stack_8angle_light.cpp | 45 +++++++++++ .../light/m5stack_8angle_light.h | 37 ++++++++++ .../m5stack_8angle/m5stack_8angle.cpp | 74 +++++++++++++++++++ .../m5stack_8angle/m5stack_8angle.h | 34 +++++++++ .../m5stack_8angle/sensor/__init__.py | 66 +++++++++++++++++ .../sensor/m5stack_8angle_sensor.cpp | 24 ++++++ .../sensor/m5stack_8angle_sensor.h | 27 +++++++ tests/components/m5stack_8angle/common.yaml | 30 ++++++++ .../m5stack_8angle/test.esp32-ard.yaml | 1 + .../m5stack_8angle/test.esp32-c3-ard.yaml | 1 + .../m5stack_8angle/test.esp32-c3-idf.yaml | 1 + .../m5stack_8angle/test.esp32-idf.yaml | 1 + .../m5stack_8angle/test.esp8266-ard.yaml | 1 + .../m5stack_8angle/test.rp2040-ard.yaml | 1 + 20 files changed, 474 insertions(+) create mode 100644 esphome/components/m5stack_8angle/__init__.py create mode 100644 esphome/components/m5stack_8angle/binary_sensor/__init__.py create mode 100644 esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp create mode 100644 esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.h create mode 100644 esphome/components/m5stack_8angle/light/__init__.py create mode 100644 esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp create mode 100644 esphome/components/m5stack_8angle/light/m5stack_8angle_light.h create mode 100644 esphome/components/m5stack_8angle/m5stack_8angle.cpp create mode 100644 esphome/components/m5stack_8angle/m5stack_8angle.h create mode 100644 esphome/components/m5stack_8angle/sensor/__init__.py create mode 100644 esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp create mode 100644 esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.h create mode 100644 tests/components/m5stack_8angle/common.yaml create mode 100644 tests/components/m5stack_8angle/test.esp32-ard.yaml create mode 100644 tests/components/m5stack_8angle/test.esp32-c3-ard.yaml create mode 100644 tests/components/m5stack_8angle/test.esp32-c3-idf.yaml create mode 100644 tests/components/m5stack_8angle/test.esp32-idf.yaml create mode 100644 tests/components/m5stack_8angle/test.esp8266-ard.yaml create mode 100644 tests/components/m5stack_8angle/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 210c567f78..ee76a072fc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -216,6 +216,7 @@ esphome/components/lock/* @esphome/core esphome/components/logger/* @esphome/core esphome/components/ltr390/* @latonita @sjtrny esphome/components/ltr_als_ps/* @latonita +esphome/components/m5stack_8angle/* @rnauber esphome/components/matrix_keypad/* @ssieb esphome/components/max31865/* @DAVe3283 esphome/components/max44009/* @berfenger diff --git a/esphome/components/m5stack_8angle/__init__.py b/esphome/components/m5stack_8angle/__init__.py new file mode 100644 index 0000000000..1aaa86a6fd --- /dev/null +++ b/esphome/components/m5stack_8angle/__init__.py @@ -0,0 +1,33 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c +from esphome.const import CONF_ID + + +DEPENDENCIES = ["i2c"] +CODEOWNERS = ["@rnauber"] +MULTI_CONF = True + +CONF_M5STACK_8ANGLE_ID = "m5stack_8angle_id" + +m5stack_8angle_ns = cg.esphome_ns.namespace("m5stack_8angle") +M5Stack8AngleComponent = m5stack_8angle_ns.class_( + "M5Stack8AngleComponent", + i2c.I2CDevice, + cg.Component, +) + +AnalogBits = m5stack_8angle_ns.enum("AnalogBits") + + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(M5Stack8AngleComponent), + } +).extend(i2c.i2c_device_schema(0x43)) + + +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/m5stack_8angle/binary_sensor/__init__.py b/esphome/components/m5stack_8angle/binary_sensor/__init__.py new file mode 100644 index 0000000000..a8b2690083 --- /dev/null +++ b/esphome/components/m5stack_8angle/binary_sensor/__init__.py @@ -0,0 +1,30 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor + +from .. import M5Stack8AngleComponent, m5stack_8angle_ns, CONF_M5STACK_8ANGLE_ID + + +M5Stack8AngleSwitchBinarySensor = m5stack_8angle_ns.class_( + "M5Stack8AngleSwitchBinarySensor", + binary_sensor.BinarySensor, + cg.PollingComponent, +) + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(CONF_M5STACK_8ANGLE_ID): cv.use_id(M5Stack8AngleComponent), + } + ) + .extend(binary_sensor.binary_sensor_schema(M5Stack8AngleSwitchBinarySensor)) + .extend(cv.polling_component_schema("10s")) +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_M5STACK_8ANGLE_ID]) + sens = await binary_sensor.new_binary_sensor(config) + cg.add(sens.set_parent(hub)) + await cg.register_component(sens, config) diff --git a/esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp b/esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp new file mode 100644 index 0000000000..2f68d9f254 --- /dev/null +++ b/esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp @@ -0,0 +1,17 @@ +#include "m5stack_8angle_binary_sensor.h" + +namespace esphome { +namespace m5stack_8angle { + +void M5Stack8AngleSwitchBinarySensor::update() { + int8_t out = this->parent_->read_switch(); + if (out == -1) { + this->status_set_warning("Could not read binary sensor state from M5Stack 8Angle."); + return; + } + this->publish_state(out != 0); + this->status_clear_warning(); +} + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.h b/esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.h new file mode 100644 index 0000000000..b8bb601525 --- /dev/null +++ b/esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.h @@ -0,0 +1,19 @@ +#pragma once + +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/core/component.h" + +#include "../m5stack_8angle.h" + +namespace esphome { +namespace m5stack_8angle { + +class M5Stack8AngleSwitchBinarySensor : public binary_sensor::BinarySensor, + public PollingComponent, + public Parented { + public: + void update() override; +}; + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/esphome/components/m5stack_8angle/light/__init__.py b/esphome/components/m5stack_8angle/light/__init__.py new file mode 100644 index 0000000000..07384ecd61 --- /dev/null +++ b/esphome/components/m5stack_8angle/light/__init__.py @@ -0,0 +1,31 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import light + +from esphome.const import CONF_OUTPUT_ID + +from .. import M5Stack8AngleComponent, m5stack_8angle_ns, CONF_M5STACK_8ANGLE_ID + + +M5Stack8AngleLightsComponent = m5stack_8angle_ns.class_( + "M5Stack8AngleLightOutput", + light.AddressableLight, +) + + +CONFIG_SCHEMA = cv.All( + light.ADDRESSABLE_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_M5STACK_8ANGLE_ID): cv.use_id(M5Stack8AngleComponent), + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(M5Stack8AngleLightsComponent), + } + ) +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_M5STACK_8ANGLE_ID]) + lights = cg.new_Pvariable(config[CONF_OUTPUT_ID]) + await light.register_light(lights, config) + await cg.register_component(lights, config) + cg.add(lights.set_parent(hub)) diff --git a/esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp b/esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp new file mode 100644 index 0000000000..95fd8cb98f --- /dev/null +++ b/esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp @@ -0,0 +1,45 @@ +#include "m5stack_8angle_light.h" + +#include "esphome/core/log.h" + +namespace esphome { +namespace m5stack_8angle { + +static const char *const TAG = "m5stack_8angle.light"; + +void M5Stack8AngleLightOutput::setup() { + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->buf_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); + if (this->buf_ == nullptr) { + ESP_LOGE(TAG, "Failed to allocate buffer of size %u", M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); + this->mark_failed(); + return; + }; + memset(this->buf_, 0xFF, M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); + + this->effect_data_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS); + if (this->effect_data_ == nullptr) { + ESP_LOGE(TAG, "Failed to allocate effect data of size %u", M5STACK_8ANGLE_NUM_LEDS); + this->mark_failed(); + return; + }; + memset(this->effect_data_, 0x00, M5STACK_8ANGLE_NUM_LEDS); +} + +void M5Stack8AngleLightOutput::write_state(light::LightState *state) { + for (int i = 0; i < M5STACK_8ANGLE_NUM_LEDS; + i++) { // write one LED at a time, otherwise the message will be truncated + this->parent_->write_register(M5STACK_8ANGLE_REGISTER_RGB_24B + i * M5STACK_8ANGLE_BYTES_PER_LED, + this->buf_ + i * M5STACK_8ANGLE_BYTES_PER_LED, M5STACK_8ANGLE_BYTES_PER_LED); + } +} + +light::ESPColorView M5Stack8AngleLightOutput::get_view_internal(int32_t index) const { + size_t pos = index * M5STACK_8ANGLE_BYTES_PER_LED; + // red, green, blue, white, effect_data, color_correction + return {this->buf_ + pos, this->buf_ + pos + 1, this->buf_ + pos + 2, + nullptr, this->effect_data_ + index, &this->correction_}; +} + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/esphome/components/m5stack_8angle/light/m5stack_8angle_light.h b/esphome/components/m5stack_8angle/light/m5stack_8angle_light.h new file mode 100644 index 0000000000..204f2c04c7 --- /dev/null +++ b/esphome/components/m5stack_8angle/light/m5stack_8angle_light.h @@ -0,0 +1,37 @@ +#pragma once + +#include "esphome/components/light/addressable_light.h" +#include "esphome/components/light/light_output.h" + +#include "../m5stack_8angle.h" + +namespace esphome { +namespace m5stack_8angle { + +static const uint8_t M5STACK_8ANGLE_NUM_LEDS = 9; +static const uint8_t M5STACK_8ANGLE_BYTES_PER_LED = 4; + +class M5Stack8AngleLightOutput : public light::AddressableLight, public Parented { + public: + void setup() override; + + void write_state(light::LightState *state) override; + + int32_t size() const override { return M5STACK_8ANGLE_NUM_LEDS; } + light::LightTraits get_traits() override { + auto traits = light::LightTraits(); + traits.set_supported_color_modes({light::ColorMode::RGB}); + return traits; + }; + + void clear_effect_data() override { memset(this->effect_data_, 0x00, M5STACK_8ANGLE_NUM_LEDS); }; + + protected: + light::ESPColorView get_view_internal(int32_t index) const override; + + uint8_t *buf_{nullptr}; + uint8_t *effect_data_{nullptr}; +}; + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/esphome/components/m5stack_8angle/m5stack_8angle.cpp b/esphome/components/m5stack_8angle/m5stack_8angle.cpp new file mode 100644 index 0000000000..6a584eddbc --- /dev/null +++ b/esphome/components/m5stack_8angle/m5stack_8angle.cpp @@ -0,0 +1,74 @@ +#include "m5stack_8angle.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace m5stack_8angle { + +static const char *const TAG = "m5stack_8angle"; + +void M5Stack8AngleComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up M5STACK_8ANGLE..."); + i2c::ErrorCode err; + + err = this->read(nullptr, 0); + if (err != i2c::NO_ERROR) { + 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); + this->mark_failed(); + return; + }; +} + +void M5Stack8AngleComponent::dump_config() { + ESP_LOGCONFIG(TAG, "M5STACK_8ANGLE:"); + LOG_I2C_DEVICE(this); + ESP_LOGCONFIG(TAG, " Firmware version: %d ", this->fw_version_); +} + +float M5Stack8AngleComponent::read_knob_pos(uint8_t channel, AnalogBits bits) { + int32_t raw_pos = this->read_knob_pos_raw(channel, bits); + if (raw_pos == -1) { + return NAN; + } + return (float) raw_pos / ((1 << bits) - 1); +} + +int32_t M5Stack8AngleComponent::read_knob_pos_raw(uint8_t channel, AnalogBits bits) { + uint16_t knob_pos = 0; + i2c::ErrorCode err; + if (bits == BITS_8) { + err = this->read_register(M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_8B + channel, (uint8_t *) &knob_pos, 1); + } else if (bits == BITS_12) { + err = this->read_register(M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_12B + (channel * 2), (uint8_t *) &knob_pos, 2); + } else { + ESP_LOGE(TAG, "Invalid number of bits: %d", bits); + return -1; + } + if (err == i2c::NO_ERROR) { + return knob_pos; + } else { + return -1; + } +} + +int8_t M5Stack8AngleComponent::read_switch() { + uint8_t out; + i2c::ErrorCode err = this->read_register(M5STACK_8ANGLE_REGISTER_DIGITAL_INPUT, (uint8_t *) &out, 1); + if (err == i2c::NO_ERROR) { + return out ? 1 : 0; + } else { + return -1; + } +} + +float M5Stack8AngleComponent::get_setup_priority() const { return setup_priority::DATA; } + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/esphome/components/m5stack_8angle/m5stack_8angle.h b/esphome/components/m5stack_8angle/m5stack_8angle.h new file mode 100644 index 0000000000..831b1422fd --- /dev/null +++ b/esphome/components/m5stack_8angle/m5stack_8angle.h @@ -0,0 +1,34 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace m5stack_8angle { + +static const uint8_t M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_12B = 0x00; +static const uint8_t M5STACK_8ANGLE_REGISTER_ANALOG_INPUT_8B = 0x10; +static const uint8_t M5STACK_8ANGLE_REGISTER_DIGITAL_INPUT = 0x20; +static const uint8_t M5STACK_8ANGLE_REGISTER_RGB_24B = 0x30; +static const uint8_t M5STACK_8ANGLE_REGISTER_FW_VERSION = 0xFE; + +enum AnalogBits : uint8_t { + BITS_8 = 8, + BITS_12 = 12, +}; + +class M5Stack8AngleComponent : public i2c::I2CDevice, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + float read_knob_pos(uint8_t channel, AnalogBits bits = AnalogBits::BITS_8); + int32_t read_knob_pos_raw(uint8_t channel, AnalogBits bits = AnalogBits::BITS_8); + int8_t read_switch(); + + protected: + uint8_t fw_version_; +}; + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/esphome/components/m5stack_8angle/sensor/__init__.py b/esphome/components/m5stack_8angle/sensor/__init__.py new file mode 100644 index 0000000000..70744a59e6 --- /dev/null +++ b/esphome/components/m5stack_8angle/sensor/__init__.py @@ -0,0 +1,66 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor + +from esphome.const import ( + CONF_BIT_DEPTH, + CONF_CHANNEL, + CONF_RAW, + ICON_ROTATE_RIGHT, + STATE_CLASS_MEASUREMENT, +) + +from .. import ( + AnalogBits, + M5Stack8AngleComponent, + m5stack_8angle_ns, + CONF_M5STACK_8ANGLE_ID, +) + + +M5Stack8AngleKnobSensor = m5stack_8angle_ns.class_( + "M5Stack8AngleKnobSensor", + sensor.Sensor, + cg.PollingComponent, +) + + +BIT_DEPTHS = { + 8: AnalogBits.BITS_8, + 12: AnalogBits.BITS_12, +} + +_validate_bits = cv.float_with_unit("bits", "bit") + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(M5Stack8AngleKnobSensor), + cv.GenerateID(CONF_M5STACK_8ANGLE_ID): cv.use_id(M5Stack8AngleComponent), + cv.Required(CONF_CHANNEL): cv.int_range(min=1, max=8), + cv.Optional(CONF_BIT_DEPTH, default="8bit"): cv.All( + _validate_bits, cv.enum(BIT_DEPTHS) + ), + cv.Optional(CONF_RAW, default=False): cv.boolean, + } + ) + .extend( + sensor.sensor_schema( + M5Stack8AngleKnobSensor, + accuracy_decimals=2, + icon=ICON_ROTATE_RIGHT, + state_class=STATE_CLASS_MEASUREMENT, + ) + ) + .extend(cv.polling_component_schema("10s")) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + await cg.register_parented(var, config[CONF_M5STACK_8ANGLE_ID]) + cg.add(var.set_channel(config[CONF_CHANNEL] - 1)) + cg.add(var.set_bit_depth(BIT_DEPTHS[config[CONF_BIT_DEPTH]])) + cg.add(var.set_raw(config[CONF_RAW])) diff --git a/esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp b/esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp new file mode 100644 index 0000000000..5e034f1dd3 --- /dev/null +++ b/esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp @@ -0,0 +1,24 @@ +#include "m5stack_8angle_sensor.h" + +namespace esphome { +namespace m5stack_8angle { + +void M5Stack8AngleKnobSensor::update() { + if (this->parent_ != nullptr) { + int32_t raw_pos = this->parent_->read_knob_pos_raw(this->channel_, this->bits_); + if (raw_pos == -1) { + this->status_set_warning("Could not read knob position from M5Stack 8Angle."); + return; + } + if (this->raw_) { + this->publish_state(raw_pos); + } else { + float knob_pos = (float) raw_pos / ((1 << this->bits_) - 1); + this->publish_state(knob_pos); + } + this->status_clear_warning(); + }; +} + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.h b/esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.h new file mode 100644 index 0000000000..4848f8f80f --- /dev/null +++ b/esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" + +#include "../m5stack_8angle.h" + +namespace esphome { +namespace m5stack_8angle { + +class M5Stack8AngleKnobSensor : public sensor::Sensor, + public PollingComponent, + public Parented { + public: + void update() override; + void set_channel(uint8_t channel) { this->channel_ = channel; }; + void set_bit_depth(AnalogBits bits) { this->bits_ = bits; }; + void set_raw(bool raw) { this->raw_ = raw; }; + + protected: + uint8_t channel_; + AnalogBits bits_; + bool raw_; +}; + +} // namespace m5stack_8angle +} // namespace esphome diff --git a/tests/components/m5stack_8angle/common.yaml b/tests/components/m5stack_8angle/common.yaml new file mode 100644 index 0000000000..d7f988ed3a --- /dev/null +++ b/tests/components/m5stack_8angle/common.yaml @@ -0,0 +1,30 @@ +i2c: + sda: 0 + scl: 1 + id: bus_external + +m5stack_8angle: + i2c_id: bus_external + id: m5stack_8angle_base + +light: + - platform: m5stack_8angle + m5stack_8angle_id: m5stack_8angle_base + id: m8_angle_leds + name: Lights + effects: + - addressable_scan: + +sensor: + - platform: m5stack_8angle + m5stack_8angle_id: m5stack_8angle_base + channel: 1 + name: Knob 1 + - platform: m5stack_8angle + m5stack_8angle_id: m5stack_8angle_base + channel: 2 + name: Knob 2 +binary_sensor: + - platform: m5stack_8angle + m5stack_8angle_id: m5stack_8angle_base + name: Switch diff --git a/tests/components/m5stack_8angle/test.esp32-ard.yaml b/tests/components/m5stack_8angle/test.esp32-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/m5stack_8angle/test.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp32-c3-ard.yaml b/tests/components/m5stack_8angle/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/m5stack_8angle/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml b/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/m5stack_8angle/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp32-idf.yaml b/tests/components/m5stack_8angle/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/m5stack_8angle/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.esp8266-ard.yaml b/tests/components/m5stack_8angle/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/m5stack_8angle/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/m5stack_8angle/test.rp2040-ard.yaml b/tests/components/m5stack_8angle/test.rp2040-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/m5stack_8angle/test.rp2040-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 1f4829598a62424170fdd3fcc58dcc4bd2224162 Mon Sep 17 00:00:00 2001 From: Olivier ARCHER Date: Mon, 22 Jul 2024 01:29:09 +0200 Subject: [PATCH 0069/1052] [http_request] allow basic auth for idf (#7086) --- esphome/components/http_request/http_request_idf.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index d6fac7a133..68e0639b99 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -52,6 +52,7 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin config.timeout_ms = this->timeout_; config.disable_auto_redirect = !this->follow_redirects_; config.max_redirection_count = this->redirect_limit_; + config.auth_type = HTTP_AUTH_TYPE_BASIC; #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE if (secure) { config.crt_bundle_attach = esp_crt_bundle_attach; From f322ec8f3d1f70543578f0ec179d0049681c48c8 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 22 Jul 2024 01:33:26 +0200 Subject: [PATCH 0070/1052] use cache to build tests for compoenents (#7059) --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45fe336d77..a8e93248bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -468,6 +468,8 @@ jobs: - name: Compile config run: | . venv/bin/activate + mkdir build_cache + export PLATFORMIO_BUILD_CACHE_DIR=$PWD/build_cache for component in ${{ matrix.components }}; do ./script/test_build_components -e compile -c $component done From a464e46d4d5630f6fe18d5bcffe7026739fd8254 Mon Sep 17 00:00:00 2001 From: irgendwienet Date: Mon, 22 Jul 2024 01:42:09 +0200 Subject: [PATCH 0071/1052] Fixes sml parser to process extended length lists with a number of items that is dividable by 16 (#6148) --- esphome/components/sml/sml_parser.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/sml/sml_parser.cpp b/esphome/components/sml/sml_parser.cpp index 3b23522b21..c782c0fc5e 100644 --- a/esphome/components/sml/sml_parser.cpp +++ b/esphome/components/sml/sml_parser.cpp @@ -27,7 +27,7 @@ bool SmlFile::setup_node(SmlNode *node) { uint8_t parse_length = length; if (has_extended_length) { length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f); - parse_length = length - 1; + parse_length = length; this->pos_ += 1; } @@ -37,7 +37,9 @@ bool SmlFile::setup_node(SmlNode *node) { node->type = type & 0x07; node->nodes.clear(); node->value_bytes.clear(); - if (this->buffer_[this->pos_] == 0x00) { // end of message + + // if the list is a has_extended_length list with e.g. 16 elements this is a 0x00 byte but not the end of message + if (!has_extended_length && this->buffer_[this->pos_] == 0x00) { // end of message this->pos_ += 1; } else if (is_list) { // list this->pos_ += 1; From d187340fc4d5781de91c2db3958e7a2fb295d795 Mon Sep 17 00:00:00 2001 From: Alex Cortelyou <1689668+acortelyou@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:04:11 -0700 Subject: [PATCH 0072/1052] Prevent rename from deleting new config (#7104) --- esphome/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 5ff1a28ec7..b13f96daf7 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -695,7 +695,8 @@ def command_rename(args, config): os.remove(new_path) return 1 - os.remove(CORE.config_path) + if CORE.config_path != new_path: + os.remove(CORE.config_path) print(color(Fore.BOLD_GREEN, "SUCCESS")) print() From 74aee1d453f39b84d5783e9b051fe788e5fadb12 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Fri, 19 Jul 2024 15:15:11 -0400 Subject: [PATCH 0073/1052] revert bit shift to match previous behavior (#7109) --- .../components/i2s_audio/microphone/i2s_audio_microphone.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 009fecdf90..cb49a744fc 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -174,7 +174,8 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) { size_t samples_read = bytes_read / sizeof(int32_t); samples.resize(samples_read); for (size_t i = 0; i < samples_read; i++) { - samples[i] = reinterpret_cast(buf)[i] >> 16; + int32_t temp = reinterpret_cast(buf)[i] >> 14; + samples[i] = clamp(temp, INT16_MIN, INT16_MAX); } memcpy(buf, samples.data(), samples_read * sizeof(int16_t)); return samples_read * sizeof(int16_t); From 626ed815fb289b7532eb64d210bbff142f6c7666 Mon Sep 17 00:00:00 2001 From: Lucio Tarantino Date: Sun, 21 Jul 2024 23:16:51 +0200 Subject: [PATCH 0074/1052] [heatpumpir] Fix BK72XX Compile error with IRremoteESP8266 (#6955) --- esphome/components/heatpumpir/climate.py | 3 +++ tests/components/heatpumpir/test.bk72xx-ard.yaml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/esphome/components/heatpumpir/climate.py b/esphome/components/heatpumpir/climate.py index 80900d7db9..9d31668deb 100644 --- a/esphome/components/heatpumpir/climate.py +++ b/esphome/components/heatpumpir/climate.py @@ -8,6 +8,7 @@ from esphome.const import ( CONF_PROTOCOL, CONF_VISUAL, ) +from esphome.core import CORE CODEOWNERS = ["@rob-deutsch"] @@ -127,3 +128,5 @@ def to_code(config): cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE])) cg.add_library("tonia/HeatpumpIR", "1.0.27") + if CORE.is_libretiny: + CORE.add_platformio_option("lib_ignore", "IRremoteESP8266") diff --git a/tests/components/heatpumpir/test.bk72xx-ard.yaml b/tests/components/heatpumpir/test.bk72xx-ard.yaml index 90259f1244..b616f9157c 100644 --- a/tests/components/heatpumpir/test.bk72xx-ard.yaml +++ b/tests/components/heatpumpir/test.bk72xx-ard.yaml @@ -3,6 +3,13 @@ remote_transmitter: carrier_duty_percent: 50% climate: + - platform: heatpumpir + protocol: mitsubishi_heavy_zm + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 - platform: heatpumpir protocol: daikin horizontal_default: mleft From 5bec0a6534028a834df91aad8e6d9c95cc825c5f Mon Sep 17 00:00:00 2001 From: Olivier ARCHER Date: Mon, 22 Jul 2024 01:29:09 +0200 Subject: [PATCH 0075/1052] [http_request] allow basic auth for idf (#7086) --- esphome/components/http_request/http_request_idf.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index d6fac7a133..68e0639b99 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -52,6 +52,7 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin config.timeout_ms = this->timeout_; config.disable_auto_redirect = !this->follow_redirects_; config.max_redirection_count = this->redirect_limit_; + config.auth_type = HTTP_AUTH_TYPE_BASIC; #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE if (secure) { config.crt_bundle_attach = esp_crt_bundle_attach; From 4690e227b858a7d2a34cd53aa55645fc03b93878 Mon Sep 17 00:00:00 2001 From: irgendwienet Date: Mon, 22 Jul 2024 01:42:09 +0200 Subject: [PATCH 0076/1052] Fixes sml parser to process extended length lists with a number of items that is dividable by 16 (#6148) --- esphome/components/sml/sml_parser.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/sml/sml_parser.cpp b/esphome/components/sml/sml_parser.cpp index 3b23522b21..c782c0fc5e 100644 --- a/esphome/components/sml/sml_parser.cpp +++ b/esphome/components/sml/sml_parser.cpp @@ -27,7 +27,7 @@ bool SmlFile::setup_node(SmlNode *node) { uint8_t parse_length = length; if (has_extended_length) { length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f); - parse_length = length - 1; + parse_length = length; this->pos_ += 1; } @@ -37,7 +37,9 @@ bool SmlFile::setup_node(SmlNode *node) { node->type = type & 0x07; node->nodes.clear(); node->value_bytes.clear(); - if (this->buffer_[this->pos_] == 0x00) { // end of message + + // if the list is a has_extended_length list with e.g. 16 elements this is a 0x00 byte but not the end of message + if (!has_extended_length && this->buffer_[this->pos_] == 0x00) { // end of message this->pos_ += 1; } else if (is_list) { // list this->pos_ += 1; From 41813b0a1ffeada67d4c5abbdaf9e4ac6095be75 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 22 Jul 2024 12:35:06 +1200 Subject: [PATCH 0077/1052] Bump version to 2024.7.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index d581f68470..ff5f7b699d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.0" +__version__ = "2024.7.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 0a7d8836339ffc59c37d3363414a827ff4cf42ec Mon Sep 17 00:00:00 2001 From: leejoow Date: Mon, 22 Jul 2024 03:33:11 +0200 Subject: [PATCH 0078/1052] [modbus_controller] Add on_command_sent trigger (#7078) Co-authored-by: Leo Schelvis --- .../components/modbus_controller/__init__.py | 28 +++++++++++++++++-- .../components/modbus_controller/automation.h | 19 +++++++++++++ esphome/components/modbus_controller/const.py | 1 + .../modbus_controller/modbus_controller.cpp | 8 ++++++ .../modbus_controller/modbus_controller.h | 3 ++ 5 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 esphome/components/modbus_controller/automation.h diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index b8ab48fcc6..1d0f406783 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -1,8 +1,16 @@ import binascii import esphome.codegen as cg import esphome.config_validation as cv +from esphome import automation from esphome.components import modbus -from esphome.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_LAMBDA, CONF_OFFSET +from esphome.const import ( + CONF_ADDRESS, + CONF_ID, + CONF_NAME, + CONF_LAMBDA, + CONF_OFFSET, + CONF_TRIGGER_ID, +) from esphome.cpp_helpers import logging from .const import ( CONF_BITMASK, @@ -12,6 +20,7 @@ from .const import ( CONF_CUSTOM_COMMAND, CONF_FORCE_NEW_RANGE, CONF_MODBUS_CONTROLLER_ID, + CONF_ON_COMMAND_SENT, CONF_REGISTER_COUNT, CONF_REGISTER_TYPE, CONF_RESPONSE_SIZE, @@ -97,6 +106,10 @@ TYPE_REGISTER_MAP = { "FP32_R": 2, } +ModbusCommandSentTrigger = modbus_controller_ns.class_( + "ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_) +) + _LOGGER = logging.getLogger(__name__) ModbusServerRegisterSchema = cv.Schema( @@ -120,13 +133,19 @@ CONFIG_SCHEMA = cv.All( cv.Optional( CONF_SERVER_REGISTERS, ): cv.ensure_list(ModbusServerRegisterSchema), + cv.Optional(CONF_ON_COMMAND_SENT): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ModbusCommandSentTrigger + ), + } + ), } ) .extend(cv.polling_component_schema("60s")) .extend(modbus.modbus_device_schema(0x01)) ) - ModbusItemBaseSchema = cv.Schema( { cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), @@ -254,6 +273,11 @@ async def to_code(config): ) ) await register_modbus_device(var, config) + for conf in config.get(CONF_ON_COMMAND_SENT, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(int, "function_code"), (int, "address")], conf + ) async def register_modbus_device(var, config): diff --git a/esphome/components/modbus_controller/automation.h b/esphome/components/modbus_controller/automation.h new file mode 100644 index 0000000000..ad8de4b05d --- /dev/null +++ b/esphome/components/modbus_controller/automation.h @@ -0,0 +1,19 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/modbus_controller/modbus_controller.h" + +namespace esphome { +namespace modbus_controller { + +class ModbusCommandSentTrigger : public Trigger { + public: + ModbusCommandSentTrigger(ModbusController *a_modbuscontroller) { + a_modbuscontroller->add_on_command_sent_callback( + [this](int function_code, int address) { this->trigger(function_code, address); }); + } +}; + +} // namespace modbus_controller +} // namespace esphome diff --git a/esphome/components/modbus_controller/const.py b/esphome/components/modbus_controller/const.py index 1a23640e17..1f5c39895c 100644 --- a/esphome/components/modbus_controller/const.py +++ b/esphome/components/modbus_controller/const.py @@ -6,6 +6,7 @@ CONF_CUSTOM_COMMAND = "custom_command" CONF_FORCE_NEW_RANGE = "force_new_range" CONF_MODBUS_CONTROLLER_ID = "modbus_controller_id" CONF_MODBUS_FUNCTIONCODE = "modbus_functioncode" +CONF_ON_COMMAND_SENT = "on_command_sent" CONF_RAW_ENCODE = "raw_encode" CONF_REGISTER_COUNT = "register_count" CONF_REGISTER_TYPE = "register_type" diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 29d3137603..378e5c06c0 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -43,7 +43,11 @@ bool ModbusController::send_next_command_() { ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_, command->register_address, command->register_count); command->send(); + this->last_command_timestamp_ = millis(); + + this->command_sent_callback_.call((int) command->function_code, command->register_address); + // remove from queue if no handler is defined if (!command->on_data_func) { command_queue_.pop_front(); @@ -659,5 +663,9 @@ int64_t payload_to_number(const std::vector &data, SensorValueType sens return value; } +void ModbusController::add_on_command_sent_callback(std::function &&callback) { + this->command_sent_callback_.add(std::move(callback)); +} + } // namespace modbus_controller } // namespace esphome diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h index 9b7d59c93f..3bc11da879 100644 --- a/esphome/components/modbus_controller/modbus_controller.h +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -456,6 +456,8 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { size_t get_command_queue_length() { return command_queue_.size(); } /// get if the module is offline, didn't respond the last command bool get_module_offline() { return module_offline_; } + /// Set callback for commands + void add_on_command_sent_callback(std::function &&callback); protected: /// parse sensormap_ and create range of sequential addresses @@ -488,6 +490,7 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { bool module_offline_; /// how many updates to skip if module is offline uint16_t offline_skip_updates_; + CallbackManager command_sent_callback_{}; }; /** Convert vector response payload to float. From 8fc42694f69743561ee5d4474d768aeb2be9d35b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:42:25 +1000 Subject: [PATCH 0079/1052] [ili9xxx] Rework delay handling (#7115) --- esphome/components/ili9xxx/ili9xxx_defines.h | 4 ++- .../components/ili9xxx/ili9xxx_display.cpp | 31 ++++++------------- esphome/components/ili9xxx/ili9xxx_display.h | 12 +++---- esphome/components/ili9xxx/ili9xxx_init.h | 8 ++--- 4 files changed, 21 insertions(+), 34 deletions(-) diff --git a/esphome/components/ili9xxx/ili9xxx_defines.h b/esphome/components/ili9xxx/ili9xxx_defines.h index 744013db3d..f4c5aad957 100644 --- a/esphome/components/ili9xxx/ili9xxx_defines.h +++ b/esphome/components/ili9xxx/ili9xxx_defines.h @@ -92,7 +92,9 @@ static const uint8_t ILI9XXX_GMCTRN1 = 0xE1; static const uint8_t ILI9XXX_CSCON = 0xF0; static const uint8_t ILI9XXX_ADJCTL3 = 0xF7; -static const uint8_t ILI9XXX_DELAY = 0xFF; // followed by one byte of delay time in ms +static const uint8_t ILI9XXX_DELAY_FLAG = 0xFF; +// special marker for delay - command byte reprents ms, length byte is an impossible value +#define ILI9XXX_DELAY(ms) ((uint8_t) ((ms) | 0x80)), ILI9XXX_DELAY_FLAG } // namespace ili9xxx } // namespace esphome diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index de03df5d41..4f035edbb0 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -34,8 +34,8 @@ void ILI9XXXDisplay::setup() { ESP_LOGD(TAG, "Setting up ILI9xxx"); this->setup_pins_(); - this->init_lcd(this->init_sequence_); - this->init_lcd(this->extra_init_sequence_.data()); + this->init_lcd_(this->init_sequence_); + this->init_lcd_(this->extra_init_sequence_.data()); switch (this->pixel_mode_) { case PIXEL_MODE_16: if (this->is_18bitdisplay_) { @@ -405,42 +405,29 @@ void ILI9XXXDisplay::reset_() { } } -void ILI9XXXDisplay::init_lcd(const uint8_t *addr) { +void ILI9XXXDisplay::init_lcd_(const uint8_t *addr) { if (addr == nullptr) return; uint8_t cmd, x, num_args; while ((cmd = *addr++) != 0) { x = *addr++; - if (cmd == ILI9XXX_DELAY) { - ESP_LOGD(TAG, "Delay %dms", x); - delay(x); + if (x == ILI9XXX_DELAY_FLAG) { + cmd &= 0x7F; + ESP_LOGV(TAG, "Delay %dms", cmd); + delay(cmd); } else { num_args = x & 0x7F; - ESP_LOGD(TAG, "Command %02X, length %d, bits %02X", cmd, num_args, *addr); + ESP_LOGV(TAG, "Command %02X, length %d, bits %02X", cmd, num_args, *addr); this->send_command(cmd, addr, num_args); addr += num_args; if (x & 0x80) { - ESP_LOGD(TAG, "Delay 150ms"); + ESP_LOGV(TAG, "Delay 150ms"); delay(150); // NOLINT } } } } -void ILI9XXXGC9A01A::init_lcd(const uint8_t *addr) { - if (addr == nullptr) - return; - uint8_t cmd, x, num_args; - while ((cmd = *addr++) != 0) { - x = *addr++; - num_args = x & 0x7F; - this->send_command(cmd, addr, num_args); - addr += num_args; - if (x & 0x80) - delay(150); // NOLINT - } -} - // Tell the display controller where we want to draw pixels. void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { x1 += this->offset_x_; diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index b60047a8c3..6121488d15 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -33,7 +33,9 @@ class ILI9XXXDisplay : public display::DisplayBuffer, uint8_t cmd, num_args, bits; const uint8_t *addr = init_sequence; while ((cmd = *addr++) != 0) { - num_args = *addr++ & 0x7F; + num_args = *addr++; + if (num_args == ILI9XXX_DELAY_FLAG) + continue; bits = *addr; switch (cmd) { case ILI9XXX_MADCTL: { @@ -50,13 +52,10 @@ class ILI9XXXDisplay : public display::DisplayBuffer, break; } - case ILI9XXX_DELAY: - continue; // no args to skip - default: break; } - addr += num_args; + addr += (num_args & 0x7F); } } @@ -109,7 +108,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, virtual void set_madctl(); void display_(); - virtual void init_lcd(const uint8_t *addr); + void init_lcd_(const uint8_t *addr); void set_addr_window_(uint16_t x, uint16_t y, uint16_t x2, uint16_t y2); void reset_(); @@ -269,7 +268,6 @@ class ILI9XXXS3BoxLite : public ILI9XXXDisplay { class ILI9XXXGC9A01A : public ILI9XXXDisplay { public: ILI9XXXGC9A01A() : ILI9XXXDisplay(INITCMD_GC9A01A, 240, 240, true) {} - void init_lcd(const uint8_t *addr) override; }; //----------- ILI9XXX_24_TFT display -------------- diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index 260bde4c80..5a67812bc1 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -372,9 +372,9 @@ static const uint8_t PROGMEM INITCMD_GC9A01A[] = { static const uint8_t PROGMEM INITCMD_ST7735[] = { ILI9XXX_SWRESET, 0, // Soft reset, then delay 10ms - ILI9XXX_DELAY, 10, + ILI9XXX_DELAY(10), ILI9XXX_SLPOUT , 0, // Exit Sleep, delay - ILI9XXX_DELAY, 10, + ILI9XXX_DELAY(10), ILI9XXX_PIXFMT , 1, 0x05, ILI9XXX_FRMCTR1, 3, // 4: Frame rate control, 3 args + delay: 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) @@ -415,9 +415,9 @@ static const uint8_t PROGMEM INITCMD_ST7735[] = { 0x00, 0x00, 0x02, 0x10, ILI9XXX_MADCTL , 1, 0x00, // Memory Access Control, BGR ILI9XXX_NORON , 0, - ILI9XXX_DELAY, 10, + ILI9XXX_DELAY(10), ILI9XXX_DISPON , 0, // Display on - ILI9XXX_DELAY, 10, + ILI9XXX_DELAY(10), 00, // endo of list }; From 5d5f3276e9ec73f8463554137ee67f02aa4d91e5 Mon Sep 17 00:00:00 2001 From: Jan-Philipp Benecke Date: Mon, 22 Jul 2024 06:20:09 +0200 Subject: [PATCH 0080/1052] Inherit `esp32_ble_beacon` from `esp32_ble` (#6908) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32_ble/__init__.py | 19 ++- esphome/components/esp32_ble/ble.cpp | 10 +- esphome/components/esp32_ble/ble.h | 9 + .../components/esp32_ble/ble_advertising.cpp | 51 +++++- .../components/esp32_ble/ble_advertising.h | 22 ++- .../components/esp32_ble_beacon/__init__.py | 18 +- .../esp32_ble_beacon/esp32_ble_beacon.cpp | 160 +++++------------- .../esp32_ble_beacon/esp32_ble_beacon.h | 36 ++-- .../components/esp32_ble_server/__init__.py | 1 - esphome/components/esp32_improv/__init__.py | 1 - 10 files changed, 176 insertions(+), 151 deletions(-) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index d88161e3e0..472669a381 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -7,10 +7,10 @@ from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz", "@Rapsssito"] -CONFLICTS_WITH = ["esp32_ble_beacon"] CONF_BLE_ID = "ble_id" CONF_IO_CAPABILITY = "io_capability" +CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time" NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2] @@ -34,6 +34,19 @@ IO_CAPABILITY = { "display_yes_no": IoCapability.IO_CAP_IO, } +esp_power_level_t = cg.global_ns.enum("esp_power_level_t") + +TX_POWER_LEVELS = { + -12: esp_power_level_t.ESP_PWR_LVL_N12, + -9: esp_power_level_t.ESP_PWR_LVL_N9, + -6: esp_power_level_t.ESP_PWR_LVL_N6, + -3: esp_power_level_t.ESP_PWR_LVL_N3, + 0: esp_power_level_t.ESP_PWR_LVL_N0, + 3: esp_power_level_t.ESP_PWR_LVL_P3, + 6: esp_power_level_t.ESP_PWR_LVL_P6, + 9: esp_power_level_t.ESP_PWR_LVL_P9, +} + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(ESP32BLE), @@ -41,6 +54,9 @@ CONFIG_SCHEMA = cv.Schema( IO_CAPABILITY, lower=True ), cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean, + cv.Optional( + CONF_ADVERTISING_CYCLE_TIME, default="10s" + ): cv.positive_time_period_milliseconds, } ).extend(cv.COMPONENT_SCHEMA) @@ -58,6 +74,7 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) + cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME])) await cg.register_component(var, config) if CORE.using_esp_idf: diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index ceb6516a02..5d08b6e973 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -78,6 +78,11 @@ void ESP32BLE::advertising_set_manufacturer_data(const std::vector &dat this->advertising_start(); } +void ESP32BLE::advertising_register_raw_advertisement_callback(std::function &&callback) { + this->advertising_init_(); + this->advertising_->register_raw_advertisement_callback(std::move(callback)); +} + void ESP32BLE::advertising_add_service_uuid(ESPBTUUID uuid) { this->advertising_init_(); this->advertising_->add_service_uuid(uuid); @@ -102,7 +107,7 @@ bool ESP32BLE::ble_pre_setup_() { void ESP32BLE::advertising_init_() { if (this->advertising_ != nullptr) return; - this->advertising_ = new BLEAdvertising(); // NOLINT(cppcoreguidelines-owning-memory) + this->advertising_ = new BLEAdvertising(this->advertising_cycle_time_); // NOLINT(cppcoreguidelines-owning-memory) this->advertising_->set_scan_response(true); this->advertising_->set_min_preferred_interval(0x06); @@ -312,6 +317,9 @@ void ESP32BLE::loop() { delete ble_event; // NOLINT(cppcoreguidelines-owning-memory) ble_event = this->ble_events_.pop(); } + if (this->advertising_ != nullptr) { + this->advertising_->loop(); + } } void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 023960d6e4..7c55583852 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -3,6 +3,8 @@ #include "ble_advertising.h" #include "ble_uuid.h" +#include + #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" @@ -76,6 +78,11 @@ class ESP32BLE : public Component { public: void set_io_capability(IoCapability io_capability) { this->io_cap_ = (esp_ble_io_cap_t) io_capability; } + void set_advertising_cycle_time(uint32_t advertising_cycle_time) { + this->advertising_cycle_time_ = advertising_cycle_time; + } + uint32_t get_advertising_cycle_time() const { return this->advertising_cycle_time_; } + void enable(); void disable(); bool is_active(); @@ -89,6 +96,7 @@ class ESP32BLE : public Component { void advertising_set_manufacturer_data(const std::vector &data); void advertising_add_service_uuid(ESPBTUUID uuid); void advertising_remove_service_uuid(ESPBTUUID uuid); + void advertising_register_raw_advertisement_callback(std::function &&callback); void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); } void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); } @@ -121,6 +129,7 @@ class ESP32BLE : public Component { Queue ble_events_; BLEAdvertising *advertising_; esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; + uint32_t advertising_cycle_time_; bool enable_on_boot_; }; diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index 59d2398829..1d340c76d9 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -10,9 +10,9 @@ namespace esphome { namespace esp32_ble { -static const char *const TAG = "esp32_ble"; +static const char *const TAG = "esp32_ble.advertising"; -BLEAdvertising::BLEAdvertising() { +BLEAdvertising::BLEAdvertising(uint32_t advertising_cycle_time) : advertising_cycle_time_(advertising_cycle_time) { this->advertising_data_.set_scan_rsp = false; this->advertising_data_.include_name = true; this->advertising_data_.include_txpower = true; @@ -64,7 +64,7 @@ void BLEAdvertising::set_manufacturer_data(const std::vector &data) { } } -void BLEAdvertising::start() { +esp_err_t BLEAdvertising::services_advertisement_() { int num_services = this->advertising_uuids_.size(); if (num_services == 0) { this->advertising_data_.service_uuid_len = 0; @@ -87,8 +87,8 @@ void BLEAdvertising::start() { this->advertising_data_.include_txpower = !this->scan_response_; err = esp_ble_gap_config_adv_data(&this->advertising_data_); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Advertising): %d", err); - return; + ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Advertising): %s", esp_err_to_name(err)); + return err; } if (this->scan_response_) { @@ -101,8 +101,8 @@ void BLEAdvertising::start() { this->scan_response_data_.flag = 0; err = esp_ble_gap_config_adv_data(&this->scan_response_data_); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %d", err); - return; + ESP_LOGE(TAG, "esp_ble_gap_config_adv_data failed (Scan response): %s", esp_err_to_name(err)); + return err; } } @@ -113,8 +113,18 @@ void BLEAdvertising::start() { err = esp_ble_gap_start_advertising(&this->advertising_params_); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %d", err); - return; + ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %s", esp_err_to_name(err)); + return err; + } + + return ESP_OK; +} + +void BLEAdvertising::start() { + if (this->current_adv_index_ == -1) { + this->services_advertisement_(); + } else { + this->raw_advertisements_callbacks_[this->current_adv_index_](true); } } @@ -124,6 +134,29 @@ void BLEAdvertising::stop() { ESP_LOGE(TAG, "esp_ble_gap_stop_advertising failed: %d", err); return; } + if (this->current_adv_index_ != -1) { + this->raw_advertisements_callbacks_[this->current_adv_index_](false); + } +} + +void BLEAdvertising::loop() { + if (this->raw_advertisements_callbacks_.empty()) { + return; + } + const uint32_t now = millis(); + if (now - this->last_advertisement_time_ > this->advertising_cycle_time_) { + this->stop(); + this->current_adv_index_ += 1; + if (this->current_adv_index_ >= this->raw_advertisements_callbacks_.size()) { + this->current_adv_index_ = -1; + } + this->start(); + this->last_advertisement_time_ = now; + } +} + +void BLEAdvertising::register_raw_advertisement_callback(std::function &&callback) { + this->raw_advertisements_callbacks_.push_back(std::move(callback)); } } // namespace esp32_ble diff --git a/esphome/components/esp32_ble/ble_advertising.h b/esphome/components/esp32_ble/ble_advertising.h index 16a7dd1d8e..946e414c1d 100644 --- a/esphome/components/esp32_ble/ble_advertising.h +++ b/esphome/components/esp32_ble/ble_advertising.h @@ -1,20 +1,31 @@ #pragma once +#include +#include #include #ifdef USE_ESP32 +#include #include #include namespace esphome { namespace esp32_ble { +using raw_adv_data_t = struct { + uint8_t *data; + size_t length; + esp_power_level_t power_level; +}; + class ESPBTUUID; class BLEAdvertising { public: - BLEAdvertising(); + BLEAdvertising(uint32_t advertising_cycle_time); + + void loop(); void add_service_uuid(ESPBTUUID uuid); void remove_service_uuid(ESPBTUUID uuid); @@ -22,16 +33,25 @@ class BLEAdvertising { void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; } void set_manufacturer_data(const std::vector &data); void set_service_data(const std::vector &data); + void register_raw_advertisement_callback(std::function &&callback); void start(); void stop(); protected: + esp_err_t services_advertisement_(); + bool scan_response_; esp_ble_adv_data_t advertising_data_; esp_ble_adv_data_t scan_response_data_; esp_ble_adv_params_t advertising_params_; std::vector advertising_uuids_; + + std::vector> raw_advertisements_callbacks_; + + const uint32_t advertising_cycle_time_; + uint32_t last_advertisement_time_{0}; + int8_t current_adv_index_{-1}; // -1 means standard scan response }; } // namespace esp32_ble diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index 9aac48cbb2..d063209478 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -1,16 +1,21 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.components.esp32_ble import CONF_BLE_ID from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, CONF_TX_POWER from esphome.core import CORE, TimePeriod from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components import esp32_ble +AUTO_LOAD = ["esp32_ble"] DEPENDENCIES = ["esp32"] -CONFLICTS_WITH = ["esp32_ble_tracker"] esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon") -ESP32BLEBeacon = esp32_ble_beacon_ns.class_("ESP32BLEBeacon", cg.Component) - +ESP32BLEBeacon = esp32_ble_beacon_ns.class_( + "ESP32BLEBeacon", + cg.Component, + esp32_ble.GAPEventHandler, + cg.Parented.template(esp32_ble.ESP32BLE), +) CONF_MAJOR = "major" CONF_MINOR = "minor" CONF_MIN_INTERVAL = "min_interval" @@ -28,6 +33,7 @@ CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(ESP32BLEBeacon), + cv.GenerateID(CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE), cv.Required(CONF_TYPE): cv.one_of("IBEACON", upper=True), cv.Required(CONF_UUID): cv.uuid, cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t, @@ -48,7 +54,7 @@ CONFIG_SCHEMA = cv.All( min=-128, max=0 ), cv.Optional(CONF_TX_POWER, default="3dBm"): cv.All( - cv.decibel, cv.one_of(-12, -9, -6, -3, 0, 3, 6, 9, int=True) + cv.decibel, cv.enum(esp32_ble.TX_POWER_LEVELS, int=True) ), } ).extend(cv.COMPONENT_SCHEMA), @@ -62,6 +68,10 @@ async def to_code(config): uuid = config[CONF_UUID].hex uuid_arr = [cg.RawExpression(f"0x{uuid[i:i + 2]}") for i in range(0, len(uuid), 2)] var = cg.new_Pvariable(config[CONF_ID], uuid_arr) + + parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) + cg.add(parent.register_gap_event_handler(var)) + await cg.register_component(var, config) cg.add(var.set_major(config[CONF_MAJOR])) cg.add(var.set_minor(config[CONF_MINOR])) diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index 589fcc1e82..423fe61592 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -3,14 +3,16 @@ #ifdef USE_ESP32 -#include -#include -#include #include -#include +#include #include +#include +#include +#include #include + #include "esphome/core/hal.h" +#include "esphome/core/helpers.h" #ifdef USE_ARDUINO #include @@ -21,20 +23,6 @@ namespace esp32_ble_beacon { static const char *const TAG = "esp32_ble_beacon"; -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -static esp_ble_adv_params_t ble_adv_params = { - .adv_int_min = 0x20, - .adv_int_max = 0x40, - .adv_type = ADV_TYPE_NONCONN_IND, - .own_addr_type = BLE_ADDR_TYPE_PUBLIC, - .peer_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - .peer_addr_type = BLE_ADDR_TYPE_PUBLIC, - .channel_map = ADV_CHNL_ALL, - .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, -}; - -#define ENDIAN_CHANGE_U16(x) ((((x) &0xFF00) >> 8) + (((x) &0xFF) << 8)) - static const esp_ble_ibeacon_head_t IBEACON_COMMON_HEAD = { .flags = {0x02, 0x01, 0x06}, .length = 0x1A, .type = 0xFF, .company_id = {0x4C, 0x00}, .beacon_type = {0x02, 0x15}}; @@ -53,117 +41,62 @@ void ESP32BLEBeacon::dump_config() { " UUID: %s, Major: %u, Minor: %u, Min Interval: %ums, Max Interval: %ums, Measured Power: %d" ", TX Power: %ddBm", uuid, this->major_, this->minor_, this->min_interval_, this->max_interval_, this->measured_power_, - this->tx_power_); + (this->tx_power_ * 3) - 12); } +float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; } + void ESP32BLEBeacon::setup() { - ESP_LOGCONFIG(TAG, "Setting up ESP32 BLE beacon..."); - global_esp32_ble_beacon = this; + this->ble_adv_params_ = { + .adv_int_min = static_cast(this->min_interval_ / 0.625f), + .adv_int_max = static_cast(this->max_interval_ / 0.625f), + .adv_type = ADV_TYPE_NONCONN_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .peer_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .peer_addr_type = BLE_ADDR_TYPE_PUBLIC, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + }; - xTaskCreatePinnedToCore(ESP32BLEBeacon::ble_core_task, - "ble_task", // name - 10000, // stack size (in words) - nullptr, // input params - 1, // priority - nullptr, // Handle, not needed - 0 // core - ); + global_ble->advertising_register_raw_advertisement_callback([this](bool advertise) { + this->advertising_ = advertise; + if (advertise) { + this->on_advertise_(); + } + }); } -float ESP32BLEBeacon::get_setup_priority() const { return setup_priority::BLUETOOTH; } -void ESP32BLEBeacon::ble_core_task(void *params) { - ble_setup(); - - while (true) { - delay(1000); // NOLINT - } -} - -void ESP32BLEBeacon::ble_setup() { - ble_adv_params.adv_int_min = static_cast(global_esp32_ble_beacon->min_interval_ / 0.625f); - ble_adv_params.adv_int_max = static_cast(global_esp32_ble_beacon->max_interval_ / 0.625f); - - // Initialize non-volatile storage for the bluetooth controller - esp_err_t err = nvs_flash_init(); - if (err != ESP_OK) { - ESP_LOGE(TAG, "nvs_flash_init failed: %d", err); - return; - } - -#ifdef USE_ARDUINO - if (!btStart()) { - ESP_LOGE(TAG, "btStart failed: %d", esp_bt_controller_get_status()); - return; - } -#else - if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { - // start bt controller - if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) { - esp_bt_controller_config_t cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - err = esp_bt_controller_init(&cfg); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_bt_controller_init failed: %s", esp_err_to_name(err)); - return; - } - while (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_IDLE) - ; - } - if (esp_bt_controller_get_status() == ESP_BT_CONTROLLER_STATUS_INITED) { - err = esp_bt_controller_enable(ESP_BT_MODE_BLE); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_bt_controller_enable failed: %s", esp_err_to_name(err)); - return; - } - } - if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) { - ESP_LOGE(TAG, "esp bt controller enable failed"); - return; - } - } -#endif - - esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); - - err = esp_bluedroid_init(); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", err); - return; - } - err = esp_bluedroid_enable(); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", err); - return; - } - err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, - static_cast((global_esp32_ble_beacon->tx_power_ + 12) / 3)); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err)); - return; - } - err = esp_ble_gap_register_callback(ESP32BLEBeacon::gap_event_handler); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", err); - return; - } - +void ESP32BLEBeacon::on_advertise_() { esp_ble_ibeacon_t ibeacon_adv_data; memcpy(&ibeacon_adv_data.ibeacon_head, &IBEACON_COMMON_HEAD, sizeof(esp_ble_ibeacon_head_t)); - memcpy(&ibeacon_adv_data.ibeacon_vendor.proximity_uuid, global_esp32_ble_beacon->uuid_.data(), + memcpy(&ibeacon_adv_data.ibeacon_vendor.proximity_uuid, this->uuid_.data(), sizeof(ibeacon_adv_data.ibeacon_vendor.proximity_uuid)); - ibeacon_adv_data.ibeacon_vendor.minor = ENDIAN_CHANGE_U16(global_esp32_ble_beacon->minor_); - ibeacon_adv_data.ibeacon_vendor.major = ENDIAN_CHANGE_U16(global_esp32_ble_beacon->major_); - ibeacon_adv_data.ibeacon_vendor.measured_power = static_cast(global_esp32_ble_beacon->measured_power_); + ibeacon_adv_data.ibeacon_vendor.minor = byteswap(this->minor_); + ibeacon_adv_data.ibeacon_vendor.major = byteswap(this->major_); + ibeacon_adv_data.ibeacon_vendor.measured_power = static_cast(this->measured_power_); - esp_ble_gap_config_adv_data_raw((uint8_t *) &ibeacon_adv_data, sizeof(ibeacon_adv_data)); + ESP_LOGD(TAG, "Setting BLE TX power"); + esp_err_t err = esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, this->tx_power_); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_ble_tx_power_set failed: %s", esp_err_to_name(err)); + } + err = esp_ble_gap_config_adv_data_raw((uint8_t *) &ibeacon_adv_data, sizeof(ibeacon_adv_data)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ble_gap_config_adv_data_raw failed: %s", esp_err_to_name(err)); + return; + } } void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { + if (!this->advertising_) + return; + esp_err_t err; switch (event) { case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { - err = esp_ble_gap_start_advertising(&ble_adv_params); + err = esp_ble_gap_start_advertising(&this->ble_adv_params_); if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %d", err); + ESP_LOGE(TAG, "esp_ble_gap_start_advertising failed: %s", esp_err_to_name(err)); } break; } @@ -181,6 +114,7 @@ void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap } else { ESP_LOGD(TAG, "BLE stopped advertising successfully"); } + // this->advertising_ = false; break; } default: @@ -188,8 +122,6 @@ void ESP32BLEBeacon::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap } } -ESP32BLEBeacon *global_esp32_ble_beacon = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - } // namespace esp32_ble_beacon } // namespace esphome diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h index 5208b67ea3..e37edf6cde 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.h @@ -1,39 +1,39 @@ #pragma once +#include "esphome/components/esp32_ble/ble.h" #include "esphome/core/component.h" #ifdef USE_ESP32 -#include #include +#include namespace esphome { namespace esp32_ble_beacon { -// NOLINTNEXTLINE(modernize-use-using) -typedef struct { +using esp_ble_ibeacon_head_t = struct { uint8_t flags[3]; uint8_t length; uint8_t type; uint8_t company_id[2]; uint8_t beacon_type[2]; -} __attribute__((packed)) esp_ble_ibeacon_head_t; +} __attribute__((packed)); -// NOLINTNEXTLINE(modernize-use-using) -typedef struct { +using esp_ble_ibeacon_vendor_t = struct { uint8_t proximity_uuid[16]; uint16_t major; uint16_t minor; uint8_t measured_power; -} __attribute__((packed)) esp_ble_ibeacon_vendor_t; +} __attribute__((packed)); -// NOLINTNEXTLINE(modernize-use-using) -typedef struct { +using esp_ble_ibeacon_t = struct { esp_ble_ibeacon_head_t ibeacon_head; esp_ble_ibeacon_vendor_t ibeacon_vendor; -} __attribute__((packed)) esp_ble_ibeacon_t; +} __attribute__((packed)); -class ESP32BLEBeacon : public Component { +using namespace esp32_ble; + +class ESP32BLEBeacon : public Component, public GAPEventHandler, public Parented { public: explicit ESP32BLEBeacon(const std::array &uuid) : uuid_(uuid) {} @@ -46,12 +46,11 @@ class ESP32BLEBeacon : public Component { void set_min_interval(uint16_t val) { this->min_interval_ = val; } void set_max_interval(uint16_t val) { this->max_interval_ = val; } void set_measured_power(int8_t val) { this->measured_power_ = val; } - void set_tx_power(int8_t val) { this->tx_power_ = val; } + void set_tx_power(esp_power_level_t val) { this->tx_power_ = val; } + void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; protected: - static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); - static void ble_core_task(void *params); - static void ble_setup(); + void on_advertise_(); std::array uuid_; uint16_t major_{}; @@ -59,12 +58,11 @@ class ESP32BLEBeacon : public Component { uint16_t min_interval_{}; uint16_t max_interval_{}; int8_t measured_power_{}; - int8_t tx_power_{}; + esp_power_level_t tx_power_{}; + esp_ble_adv_params_t ble_adv_params_; + bool advertising_{false}; }; -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -extern ESP32BLEBeacon *global_esp32_ble_beacon; - } // namespace esp32_ble_beacon } // namespace esphome diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index f53c9450f4..ce9fdc2cf3 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -7,7 +7,6 @@ from esphome.components.esp32 import add_idf_sdkconfig_option AUTO_LOAD = ["esp32_ble"] CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"] -CONFLICTS_WITH = ["esp32_ble_beacon"] DEPENDENCIES = ["esp32"] CONF_MANUFACTURER = "manufacturer" diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 49d95d89e5..62d9cd376c 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -6,7 +6,6 @@ from esphome.const import CONF_ID AUTO_LOAD = ["esp32_ble_server"] CODEOWNERS = ["@jesserockz"] -CONFLICTS_WITH = ["esp32_ble_beacon"] DEPENDENCIES = ["wifi", "esp32"] CONF_AUTHORIZED_DURATION = "authorized_duration" From f1aa254e4810999d7bb70619ba9c09bb9be5cf98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aodren=20Auffr=C3=A9dou-Heinicke?= <54121510+aodrenah@users.noreply.github.com> Date: Sun, 21 Jul 2024 22:29:54 -0700 Subject: [PATCH 0081/1052] APDS9306 Ambient Light Sensor (#6709) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Keith Burzinski Co-authored-by: Mat931 <49403702+Mat931@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/apds9306/__init__.py | 4 + esphome/components/apds9306/apds9306.cpp | 151 ++++++++++++++++++ esphome/components/apds9306/apds9306.h | 66 ++++++++ esphome/components/apds9306/sensor.py | 95 +++++++++++ tests/components/apds9306/common.yaml | 12 ++ tests/components/apds9306/test.esp32-ard.yaml | 5 + .../apds9306/test.esp32-c3-ard.yaml | 5 + .../apds9306/test.esp32-c3-idf.yaml | 5 + tests/components/apds9306/test.esp32-idf.yaml | 5 + .../components/apds9306/test.esp8266-ard.yaml | 5 + .../components/apds9306/test.rp2040-ard.yaml | 5 + 12 files changed, 359 insertions(+) create mode 100644 esphome/components/apds9306/__init__.py create mode 100644 esphome/components/apds9306/apds9306.cpp create mode 100644 esphome/components/apds9306/apds9306.h create mode 100644 esphome/components/apds9306/sensor.py create mode 100644 tests/components/apds9306/common.yaml create mode 100644 tests/components/apds9306/test.esp32-ard.yaml create mode 100644 tests/components/apds9306/test.esp32-c3-ard.yaml create mode 100644 tests/components/apds9306/test.esp32-c3-idf.yaml create mode 100644 tests/components/apds9306/test.esp32-idf.yaml create mode 100644 tests/components/apds9306/test.esp8266-ard.yaml create mode 100644 tests/components/apds9306/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index ee76a072fc..c5e144bdfa 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -37,6 +37,7 @@ esphome/components/am43/sensor/* @buxtronix esphome/components/analog_threshold/* @ianchi esphome/components/animation/* @syndlex esphome/components/anova/* @buxtronix +esphome/components/apds9306/* @aodrenah esphome/components/api/* @OttoWinter esphome/components/as5600/* @ammmze esphome/components/as5600/sensor/* @ammmze diff --git a/esphome/components/apds9306/__init__.py b/esphome/components/apds9306/__init__.py new file mode 100644 index 0000000000..3dc8fcf5ff --- /dev/null +++ b/esphome/components/apds9306/__init__.py @@ -0,0 +1,4 @@ +# Based on this datasheet: +# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf + +CODEOWNERS = ["@aodrenah"] diff --git a/esphome/components/apds9306/apds9306.cpp b/esphome/components/apds9306/apds9306.cpp new file mode 100644 index 0000000000..7b79b0964c --- /dev/null +++ b/esphome/components/apds9306/apds9306.cpp @@ -0,0 +1,151 @@ +// Based on this datasheet: +// https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf + +#include "apds9306.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace apds9306 { + +static const char *const TAG = "apds9306"; + +enum { // APDS9306 registers + APDS9306_MAIN_CTRL = 0x00, + APDS9306_ALS_MEAS_RATE = 0x04, + APDS9306_ALS_GAIN = 0x05, + APDS9306_PART_ID = 0x06, + APDS9306_MAIN_STATUS = 0x07, + APDS9306_CLEAR_DATA_0 = 0x0A, // LSB + APDS9306_CLEAR_DATA_1 = 0x0B, + APDS9306_CLEAR_DATA_2 = 0x0C, // MSB + APDS9306_ALS_DATA_0 = 0x0D, // LSB + APDS9306_ALS_DATA_1 = 0x0E, + APDS9306_ALS_DATA_2 = 0x0F, // MSB + APDS9306_INT_CFG = 0x19, + APDS9306_INT_PERSISTENCE = 0x1A, + APDS9306_ALS_THRES_UP_0 = 0x21, // LSB + APDS9306_ALS_THRES_UP_1 = 0x22, + APDS9306_ALS_THRES_UP_2 = 0x23, // MSB + APDS9306_ALS_THRES_LOW_0 = 0x24, // LSB + APDS9306_ALS_THRES_LOW_1 = 0x25, + APDS9306_ALS_THRES_LOW_2 = 0x26, // MSB + APDS9306_ALS_THRES_VAR = 0x27 +}; + +#define APDS9306_ERROR_CHECK(func, error) \ + if (!(func)) { \ + ESP_LOGE(TAG, error); \ + this->mark_failed(); \ + return; \ + } +#define APDS9306_WARNING_CHECK(func, warning) \ + if (!(func)) { \ + ESP_LOGW(TAG, warning); \ + this->status_set_warning(); \ + return; \ + } +#define APDS9306_WRITE_BYTE(reg, value) \ + ESP_LOGV(TAG, "Writing 0x%02x to 0x%02x", value, reg); \ + if (!this->write_byte(reg, value)) { \ + ESP_LOGE(TAG, "Failed writing 0x%02x to 0x%02x", value, reg); \ + this->mark_failed(); \ + return; \ + } + +void APDS9306::setup() { + ESP_LOGCONFIG(TAG, "Setting up APDS9306..."); + + uint8_t id; + if (!this->read_byte(APDS9306_PART_ID, &id)) { // Part ID register + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } + + if (id != 0xB1 && id != 0xB3) { // 0xB1 for APDS9306 0xB3 for APDS9306-065 + this->error_code_ = WRONG_ID; + this->mark_failed(); + return; + } + + // ALS resolution and measurement, see datasheet or init.py for options + uint8_t als_meas_rate = ((this->bit_width_ & 0x07) << 4) | (this->measurement_rate_ & 0x07); + APDS9306_WRITE_BYTE(APDS9306_ALS_MEAS_RATE, als_meas_rate); + + // ALS gain, see datasheet or init.py for options + uint8_t als_gain = (this->gain_ & 0x07); + APDS9306_WRITE_BYTE(APDS9306_ALS_GAIN, als_gain); + + // Set to standby mode + APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00); + + // Check for data, clear main status + uint8_t status; + APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed."); + + // Set to active mode + APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02); + + ESP_LOGCONFIG(TAG, "APDS9306 setup complete"); +} + +void APDS9306::dump_config() { + LOG_SENSOR("", "APDS9306", this); + LOG_I2C_DEVICE(this); + + if (this->is_failed()) { + switch (this->error_code_) { + case COMMUNICATION_FAILED: + ESP_LOGE(TAG, "Communication with APDS9306 failed!"); + break; + case WRONG_ID: + ESP_LOGE(TAG, "APDS9306 has invalid id!"); + break; + default: + ESP_LOGE(TAG, "Setting up APDS9306 registers failed!"); + break; + } + } + + 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_]); + + LOG_UPDATE_INTERVAL(this); +} + +void APDS9306::update() { + // Check for new data + uint8_t status; + APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed."); + + this->status_clear_warning(); + + if (!(status &= 0b00001000)) { // No new data + return; + } + + // Set to standby mode + APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x00); + + // Clear MAIN STATUS + APDS9306_WARNING_CHECK(this->read_byte(APDS9306_MAIN_STATUS, &status), "Reading MAIN STATUS failed."); + + uint8_t als_data[3]; + APDS9306_WARNING_CHECK(this->read_bytes(APDS9306_ALS_DATA_0, als_data, 3), "Reading ALS data has failed."); + + // Set to active mode + APDS9306_WRITE_BYTE(APDS9306_MAIN_CTRL, 0x02); + + uint32_t light_level = 0x00 | encode_uint24(als_data[2], als_data[1], als_data[0]); + + float lux = ((float) light_level / AMBIENT_LIGHT_GAIN_VALUES[this->gain_]) * + (100.0f / MEASUREMENT_RATE_VALUES[this->measurement_rate_]); + + ESP_LOGD(TAG, "Got illuminance=%.1flx from", lux); + this->publish_state(lux); +} + +} // namespace apds9306 +} // namespace esphome diff --git a/esphome/components/apds9306/apds9306.h b/esphome/components/apds9306/apds9306.h new file mode 100644 index 0000000000..44362908c8 --- /dev/null +++ b/esphome/components/apds9306/apds9306.h @@ -0,0 +1,66 @@ +// Based on this datasheet: +// https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf + +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace apds9306 { + +enum MeasurementBitWidth : uint8_t { + MEASUREMENT_BIT_WIDTH_20 = 0, + MEASUREMENT_BIT_WIDTH_19 = 1, + MEASUREMENT_BIT_WIDTH_18 = 2, + MEASUREMENT_BIT_WIDTH_17 = 3, + MEASUREMENT_BIT_WIDTH_16 = 4, + MEASUREMENT_BIT_WIDTH_13 = 5, +}; +static const uint8_t MEASUREMENT_BIT_WIDTH_VALUES[] = {20, 19, 18, 17, 16, 13}; + +enum MeasurementRate : uint8_t { + MEASUREMENT_RATE_25 = 0, + MEASUREMENT_RATE_50 = 1, + MEASUREMENT_RATE_100 = 2, + MEASUREMENT_RATE_200 = 3, + MEASUREMENT_RATE_500 = 4, + MEASUREMENT_RATE_1000 = 5, + MEASUREMENT_RATE_2000 = 6, +}; +static const uint16_t MEASUREMENT_RATE_VALUES[] = {25, 50, 100, 200, 500, 1000, 2000}; + +enum AmbientLightGain : uint8_t { + AMBIENT_LIGHT_GAIN_1 = 0, + AMBIENT_LIGHT_GAIN_3 = 1, + AMBIENT_LIGHT_GAIN_6 = 2, + AMBIENT_LIGHT_GAIN_9 = 3, + AMBIENT_LIGHT_GAIN_18 = 4, +}; +static const uint8_t AMBIENT_LIGHT_GAIN_VALUES[] = {1, 3, 6, 9, 18}; + +class APDS9306 : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + float get_setup_priority() const override { return setup_priority::BUS; } + void dump_config() override; + void update() override; + void set_bit_width(MeasurementBitWidth bit_width) { this->bit_width_ = bit_width; } + void set_measurement_rate(MeasurementRate measurement_rate) { this->measurement_rate_ = measurement_rate; } + void set_ambient_light_gain(AmbientLightGain gain) { this->gain_ = gain; } + + protected: + enum ErrorCode { + NONE = 0, + COMMUNICATION_FAILED, + WRONG_ID, + } error_code_{NONE}; + + MeasurementBitWidth bit_width_; + MeasurementRate measurement_rate_; + AmbientLightGain gain_; +}; + +} // namespace apds9306 +} // namespace esphome diff --git a/esphome/components/apds9306/sensor.py b/esphome/components/apds9306/sensor.py new file mode 100644 index 0000000000..25b301444f --- /dev/null +++ b/esphome/components/apds9306/sensor.py @@ -0,0 +1,95 @@ +# Based on this datasheet: +# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_GAIN, + DEVICE_CLASS_ILLUMINANCE, + ICON_LIGHTBULB, + STATE_CLASS_MEASUREMENT, + UNIT_LUX, +) + +DEPENDENCIES = ["i2c"] + +CONF_APDS9306_ID = "apds9306_id" +CONF_BIT_WIDTH = "bit_width" +CONF_MEASUREMENT_RATE = "measurement_rate" + +apds9306_ns = cg.esphome_ns.namespace("apds9306") +APDS9306 = apds9306_ns.class_( + "APDS9306", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) + +MeasurementBitWidth = apds9306_ns.enum("MeasurementBitWidth") +MeasurementRate = apds9306_ns.enum("MeasurementRate") +AmbientLightGain = apds9306_ns.enum("AmbientLightGain") + +MEASUREMENT_BIT_WIDTHS = { + 20: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_20, + 19: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_19, + 18: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_18, + 17: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_17, + 16: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_16, + 13: MeasurementBitWidth.MEASUREMENT_BIT_WIDTH_13, +} + +MEASUREMENT_RATES = { + 25: MeasurementRate.MEASUREMENT_RATE_25, + 50: MeasurementRate.MEASUREMENT_RATE_50, + 100: MeasurementRate.MEASUREMENT_RATE_100, + 200: MeasurementRate.MEASUREMENT_RATE_200, + 500: MeasurementRate.MEASUREMENT_RATE_500, + 1000: MeasurementRate.MEASUREMENT_RATE_1000, + 2000: MeasurementRate.MEASUREMENT_RATE_2000, +} + +AMBIENT_LIGHT_GAINS = { + 1: AmbientLightGain.AMBIENT_LIGHT_GAIN_1, + 3: AmbientLightGain.AMBIENT_LIGHT_GAIN_3, + 6: AmbientLightGain.AMBIENT_LIGHT_GAIN_6, + 9: AmbientLightGain.AMBIENT_LIGHT_GAIN_9, + 18: AmbientLightGain.AMBIENT_LIGHT_GAIN_18, +} + + +def _validate_measurement_rate(value): + value = cv.positive_time_period_milliseconds(value) + return cv.enum(MEASUREMENT_RATES, int=True)(value.total_milliseconds) + + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + APDS9306, + unit_of_measurement=UNIT_LUX, + accuracy_decimals=1, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + icon=ICON_LIGHTBULB, + ) + .extend( + { + cv.Optional(CONF_GAIN, default="1"): cv.enum(AMBIENT_LIGHT_GAINS, int=True), + cv.Optional(CONF_BIT_WIDTH, default="18"): cv.enum( + MEASUREMENT_BIT_WIDTHS, int=True + ), + cv.Optional( + CONF_MEASUREMENT_RATE, default="100ms" + ): _validate_measurement_rate, + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x52)) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + cg.add(var.set_bit_width(config[CONF_BIT_WIDTH])) + cg.add(var.set_measurement_rate(config[CONF_MEASUREMENT_RATE])) + cg.add(var.set_ambient_light_gain(config[CONF_GAIN])) diff --git a/tests/components/apds9306/common.yaml b/tests/components/apds9306/common.yaml new file mode 100644 index 0000000000..b3828e62ff --- /dev/null +++ b/tests/components/apds9306/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_apds9306 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: apds9306 + name: "APDS9306 Light Level" + gain: 3 + bit_width: 16 + measurement_rate: 2000ms + update_interval: 60s diff --git a/tests/components/apds9306/test.esp32-ard.yaml b/tests/components/apds9306/test.esp32-ard.yaml new file mode 100644 index 0000000000..3b761d3fc1 --- /dev/null +++ b/tests/components/apds9306/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +<<: !include common.yaml diff --git a/tests/components/apds9306/test.esp32-c3-ard.yaml b/tests/components/apds9306/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/apds9306/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/apds9306/test.esp32-c3-idf.yaml b/tests/components/apds9306/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/apds9306/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/apds9306/test.esp32-idf.yaml b/tests/components/apds9306/test.esp32-idf.yaml new file mode 100644 index 0000000000..3b761d3fc1 --- /dev/null +++ b/tests/components/apds9306/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +<<: !include common.yaml diff --git a/tests/components/apds9306/test.esp8266-ard.yaml b/tests/components/apds9306/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/apds9306/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/apds9306/test.rp2040-ard.yaml b/tests/components/apds9306/test.rp2040-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/apds9306/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml From dc24eefe08fb44491ef7623e7db551c319c11521 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:01:45 +1200 Subject: [PATCH 0082/1052] Bump docker/build-push-action from 6.4.1 to 6.5.0 in /.github/actions/build-image (#7119) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index e2febdad1b..bd9ceb8072 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.4.1 + uses: docker/build-push-action@v6.5.0 with: context: . file: ./docker/Dockerfile @@ -69,7 +69,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.4.1 + uses: docker/build-push-action@v6.5.0 with: context: . file: ./docker/Dockerfile From ae476bb400f14d51c0e569a3807264a7abcd01e1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:51:32 +1200 Subject: [PATCH 0083/1052] [http_request] Change default timeout to 4.5s (#7123) --- esphome/components/http_request/__init__.py | 2 +- esphome/components/http_request/http_request.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index ade7024bed..ef387553fe 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -99,7 +99,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_FOLLOW_REDIRECTS, True): cv.boolean, cv.Optional(CONF_REDIRECT_LIMIT, 3): cv.int_, cv.Optional( - CONF_TIMEOUT, default="5s" + CONF_TIMEOUT, default="4.5s" ): cv.positive_time_period_milliseconds, cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All( cv.only_on_esp8266, cv.boolean diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 82b7392648..c01baf8644 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -80,7 +80,7 @@ class HttpRequestComponent : public Component { const char *useragent_{nullptr}; bool follow_redirects_; uint16_t redirect_limit_; - uint16_t timeout_{5000}; + uint16_t timeout_{4500}; uint32_t watchdog_timeout_{0}; }; From 2b2a83273fdad8c105acf5a7aedd1d63042891ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:20:44 +1200 Subject: [PATCH 0084/1052] Bump docker/setup-qemu-action from 3.1.0 to 3.2.0 (#7120) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 147efe089e..999a2f03d2 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -48,7 +48,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.4.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v3.1.0 + uses: docker/setup-qemu-action@v3.2.0 - name: Set TAG run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f66c504bda..2fdac00589 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,7 @@ jobs: uses: docker/setup-buildx-action@v3.4.0 - name: Set up QEMU if: matrix.platform != 'linux/amd64' - uses: docker/setup-qemu-action@v3.1.0 + uses: docker/setup-qemu-action@v3.2.0 - name: Log in to docker hub uses: docker/login-action@v3.2.0 From f0d4b5f74051b596752f3df0e3332720ef79f089 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:20:54 +1200 Subject: [PATCH 0085/1052] Bump docker/login-action from 3.2.0 to 3.3.0 (#7121) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2fdac00589..c1488d8b89 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -96,12 +96,12 @@ jobs: uses: docker/setup-qemu-action@v3.2.0 - name: Log in to docker hub - uses: docker/login-action@v3.2.0 + uses: docker/login-action@v3.3.0 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the GitHub container registry - uses: docker/login-action@v3.2.0 + uses: docker/login-action@v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -188,13 +188,13 @@ jobs: - name: Log in to docker hub if: matrix.registry == 'dockerhub' - uses: docker/login-action@v3.2.0 + uses: docker/login-action@v3.3.0 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the GitHub container registry if: matrix.registry == 'ghcr' - uses: docker/login-action@v3.2.0 + uses: docker/login-action@v3.3.0 with: registry: ghcr.io username: ${{ github.actor }} From e88e32bf232080d9f7ab0cbb47e22d59557b9860 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:21:03 +1200 Subject: [PATCH 0086/1052] Bump docker/setup-buildx-action from 3.4.0 to 3.5.0 (#7122) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 999a2f03d2..2b4539105b 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -46,7 +46,7 @@ jobs: with: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.4.0 + uses: docker/setup-buildx-action@v3.5.0 - name: Set up QEMU uses: docker/setup-qemu-action@v3.2.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c1488d8b89..efec556059 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,7 +90,7 @@ jobs: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.4.0 + uses: docker/setup-buildx-action@v3.5.0 - name: Set up QEMU if: matrix.platform != 'linux/amd64' uses: docker/setup-qemu-action@v3.2.0 @@ -184,7 +184,7 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.4.0 + uses: docker/setup-buildx-action@v3.5.0 - name: Log in to docker hub if: matrix.registry == 'dockerhub' From 19a787c2352bca62c3e642ed421a4e2a0185cfaa Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 23 Jul 2024 04:53:31 +0200 Subject: [PATCH 0087/1052] [fan] fix initial FanCall to properly set speed (#7113) Speed settings were ignored for the first FanCall, if no speed has been restored before. This commit changes the behaviour to: set speed to 100%, iff current speed AND new speed are not set. --- esphome/components/fan/fan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/fan/fan.cpp b/esphome/components/fan/fan.cpp index 95e3ae0758..1d560d2fc6 100644 --- a/esphome/components/fan/fan.cpp +++ b/esphome/components/fan/fan.cpp @@ -45,8 +45,8 @@ void FanCall::validate_() { this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count()); if (this->binary_state_.has_value() && *this->binary_state_) { - // when turning on, if current speed is zero, set speed to 100% - if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0) { + // when turning on, if neither current nor new speed available, set speed to 100% + if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0 && !this->speed_.has_value()) { this->speed_ = traits.supported_speed_count(); } } From 2cc14055cfa4080dfb3c0612cefa96ee0a76df9b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:12:23 +1000 Subject: [PATCH 0088/1052] Added ruff to pre-commit hooks (#7124) --- .pre-commit-config.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 74acfa1c1d..aeb434167a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,6 +2,15 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.5.4 + hooks: + # Run the linter. + - id: ruff + args: [--fix] + # Run the formatter. + - id: ruff-format - repo: https://github.com/psf/black-pre-commit-mirror rev: 24.4.2 hooks: From 39de179e213f79f8016a1ae764c87fe77333d5dc Mon Sep 17 00:00:00 2001 From: dentra Date: Wed, 24 Jul 2024 03:12:59 +0300 Subject: [PATCH 0089/1052] [http_request] Fix ESP-IDF follow redirect (#7101) --- esphome/components/http_request/__init__.py | 14 ++-- .../http_request/http_request_idf.cpp | 66 +++++++++++++++---- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index ef387553fe..161486fbb2 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -1,17 +1,17 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg +from esphome.components import esp32 +import esphome.config_validation as cv from esphome.const import ( - __version__, + CONF_ESP8266_DISABLE_SSL_SUPPORT, CONF_ID, - CONF_TIMEOUT, CONF_METHOD, + CONF_TIMEOUT, CONF_TRIGGER_ID, CONF_URL, - CONF_ESP8266_DISABLE_SSL_SUPPORT, + __version__, ) -from esphome.core import Lambda, CORE -from esphome.components import esp32 +from esphome.core import CORE, Lambda DEPENDENCIES = ["network"] AUTO_LOAD = ["json"] diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 68e0639b99..1f03b5f3bf 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -77,7 +77,7 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin esp_http_client_set_header(client, header.name, header.value); } - int body_len = body.length(); + const int body_len = body.length(); esp_err_t err = esp_http_client_open(client, body_len); if (err != ESP_OK) { @@ -109,18 +109,62 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } - container->content_length = esp_http_client_fetch_headers(client); - const auto status_code = esp_http_client_get_status_code(client); - container->status_code = status_code; + auto is_ok = [](int code) { return code >= HttpStatus_Ok && code < HttpStatus_MultipleChoices; }; - if (status_code < 200 || status_code >= 300) { - ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), status_code); - this->status_momentary_error("failed", 1000); - esp_http_client_cleanup(client); - return nullptr; + container->content_length = esp_http_client_fetch_headers(client); + container->status_code = esp_http_client_get_status_code(client); + if (is_ok(container->status_code)) { + container->duration_ms = millis() - start; + return container; } - container->duration_ms = millis() - start; - return container; + + if (this->follow_redirects_) { + auto is_redirect = [](int code) { + return code == HttpStatus_MovedPermanently || code == HttpStatus_Found || code == HttpStatus_SeeOther || + code == HttpStatus_TemporaryRedirect || code == HttpStatus_PermanentRedirect; + }; + auto num_redirects = this->redirect_limit_; + while (is_redirect(container->status_code) && num_redirects > 0) { + err = esp_http_client_set_redirection(client); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_http_client_set_redirection failed: %s", esp_err_to_name(err)); + this->status_momentary_error("failed", 1000); + esp_http_client_cleanup(client); + return nullptr; + } +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + char url[256]{}; + if (esp_http_client_get_url(client, url, sizeof(url) - 1) == ESP_OK) { + ESP_LOGV(TAG, "redirecting to url: %s", url); + } +#endif + err = esp_http_client_open(client, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_http_client_open failed: %s", esp_err_to_name(err)); + this->status_momentary_error("failed", 1000); + esp_http_client_cleanup(client); + return nullptr; + } + + container->content_length = esp_http_client_fetch_headers(client); + container->status_code = esp_http_client_get_status_code(client); + if (is_ok(container->status_code)) { + container->duration_ms = millis() - start; + return container; + } + + num_redirects--; + } + + if (num_redirects == 0) { + ESP_LOGW(TAG, "Reach redirect limit count=%d", this->redirect_limit_); + } + } + + ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code); + this->status_momentary_error("failed", 1000); + esp_http_client_cleanup(client); + return nullptr; } int HttpContainerIDF::read(uint8_t *buf, size_t max_len) { From da10de9ea8c0129394cb98ccd3b79fcdcb78dfb0 Mon Sep 17 00:00:00 2001 From: esphomebot Date: Wed, 24 Jul 2024 13:57:02 +1200 Subject: [PATCH 0090/1052] Update webserver local assets to 20240724-013115 (#7126) --- .../components/web_server/server_index_v3.h | 7218 +++++++++-------- 1 file changed, 3615 insertions(+), 3603 deletions(-) diff --git a/esphome/components/web_server/server_index_v3.h b/esphome/components/web_server/server_index_v3.h index 0c16ea9f37..0f3f743a73 100644 --- a/esphome/components/web_server/server_index_v3.h +++ b/esphome/components/web_server/server_index_v3.h @@ -397,3609 +397,3621 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0xa5, 0x01, 0xe6, 0xde, 0x48, 0xc3, 0x50, 0xf1, 0xe6, 0x90, 0xe9, 0x1b, 0xc6, 0xc4, 0xb7, 0x07, 0x71, 0x99, 0x4a, 0x91, 0xdf, 0xd9, 0xcf, 0x65, 0xd8, 0x25, 0x5d, 0x68, 0xb9, 0x4e, 0x86, 0x5c, 0xd0, 0xe2, 0xee, 0x5a, 0x31, 0xa1, 0x64, 0x71, 0x2d, 0xc7, 0xe3, 0xe5, 0xb7, 0x48, 0xcb, 0x7d, 0xb4, 0x4e, 0x14, 0x17, 0x93, 0x9c, 0x59, 0xa2, 0x64, - 0x10, 0xc1, 0x11, 0x73, 0xdb, 0xae, 0x69, 0xb2, 0x36, 0xe8, 0x70, 0xe7, 0x99, 0x76, 0x07, 0x69, 0xda, 0xbc, 0x61, - 0xc3, 0xcf, 0x5c, 0x5b, 0x3c, 0x6b, 0xaa, 0x1b, 0xf0, 0x78, 0x31, 0xcb, 0x30, 0x67, 0xc5, 0xd2, 0xd3, 0xf0, 0x56, - 0x8d, 0xea, 0x5c, 0x09, 0x73, 0x7a, 0x68, 0x3a, 0x6c, 0x42, 0xfc, 0x2f, 0x56, 0x94, 0x7b, 0x8c, 0x0b, 0x83, 0x31, - 0x06, 0x40, 0x9b, 0xbb, 0x24, 0x3c, 0x03, 0x4e, 0x5a, 0x2d, 0x7f, 0x3e, 0x34, 0x6d, 0x9d, 0xb4, 0x9d, 0x9c, 0xb2, - 0xd9, 0xae, 0xfd, 0x59, 0x27, 0x46, 0xed, 0xce, 0xfc, 0x76, 0xcf, 0xfc, 0xd3, 0xda, 0x6b, 0x6d, 0x13, 0x9f, 0x6d, - 0x38, 0x1d, 0x23, 0xbf, 0xb2, 0x5a, 0xce, 0xd3, 0x36, 0x9b, 0x75, 0x17, 0x0a, 0x0e, 0x1a, 0x43, 0x1b, 0xcd, 0x01, - 0xb6, 0x36, 0x33, 0x81, 0x75, 0xa4, 0x5c, 0x00, 0x5d, 0xb7, 0x67, 0x1b, 0xf4, 0xa1, 0x24, 0x18, 0x62, 0xef, 0x6c, - 0xb4, 0x3e, 0xac, 0xd6, 0x5e, 0x35, 0x30, 0xf8, 0x67, 0xfd, 0x57, 0xc5, 0x19, 0xbe, 0x60, 0x01, 0x67, 0xce, 0x1b, - 0xc9, 0xe9, 0xaa, 0xe5, 0xb8, 0xf1, 0x91, 0xae, 0x84, 0x04, 0xe3, 0xcb, 0x30, 0xa3, 0xb7, 0xd6, 0xa9, 0x61, 0xc6, - 0x05, 0x98, 0x4c, 0x21, 0xac, 0x03, 0xe3, 0xf2, 0x69, 0xd8, 0xd0, 0x48, 0xc7, 0xd0, 0xf0, 0x61, 0x27, 0x39, 0x3d, - 0x45, 0xb8, 0x85, 0x3b, 0xa7, 0xa7, 0x81, 0x34, 0x30, 0xd6, 0xbb, 0x8a, 0xee, 0x2a, 0xa9, 0x76, 0x94, 0x3c, 0x32, - 0x8d, 0x1e, 0xb5, 0x5b, 0x2d, 0x6c, 0x1c, 0xb7, 0xcb, 0xc2, 0x5c, 0xed, 0x68, 0xb6, 0xdd, 0x6a, 0x41, 0xb3, 0xf0, - 0xc7, 0xcd, 0xeb, 0x17, 0xb2, 0x6c, 0xa5, 0x2d, 0xdc, 0x4e, 0xdb, 0xb8, 0x93, 0x76, 0xf0, 0x71, 0x7a, 0x8c, 0x4f, - 0xd2, 0x13, 0x7c, 0x9a, 0x9e, 0xe2, 0xb3, 0xf4, 0x0c, 0xdf, 0x4f, 0xef, 0xe3, 0xf3, 0xf4, 0x1c, 0x3f, 0x48, 0x1f, - 0xe0, 0x87, 0x69, 0xbb, 0x85, 0x1f, 0xa5, 0xed, 0x36, 0x7e, 0x9c, 0xb6, 0x3b, 0xf8, 0x49, 0xda, 0x3e, 0xc6, 0x4f, - 0xd3, 0xf6, 0x09, 0x7e, 0x96, 0xb6, 0x4f, 0x31, 0x85, 0xdc, 0x21, 0xe4, 0x66, 0x90, 0x3b, 0x82, 0x5c, 0x06, 0xb9, - 0xe3, 0xb4, 0x7d, 0xba, 0xc6, 0xca, 0x06, 0x7b, 0x88, 0x5a, 0xed, 0xce, 0xf1, 0xc9, 0xe9, 0xd9, 0xfd, 0xf3, 0x07, - 0x0f, 0x1f, 0x3d, 0x7e, 0xf2, 0xf4, 0x59, 0x34, 0xc0, 0x43, 0xe3, 0x73, 0xa1, 0x44, 0x9f, 0x1f, 0xb4, 0x4f, 0x07, - 0xf8, 0xda, 0x7f, 0xc6, 0xfc, 0xa0, 0x73, 0xd2, 0x42, 0x97, 0x97, 0x27, 0x83, 0x46, 0x99, 0xfb, 0xde, 0xb8, 0x7a, - 0x54, 0x59, 0x84, 0x90, 0x18, 0x72, 0x10, 0xbe, 0x33, 0xf5, 0xde, 0xb3, 0x98, 0x27, 0x05, 0x3a, 0x38, 0x30, 0x3f, - 0x26, 0xfe, 0xc7, 0xd0, 0xff, 0xa0, 0xc1, 0x22, 0xdd, 0xd2, 0xd8, 0xf9, 0xfa, 0xea, 0xd2, 0xd2, 0xbe, 0x34, 0x62, - 0xd9, 0xe3, 0xce, 0x9c, 0xfc, 0xbf, 0x22, 0x6b, 0x2e, 0x42, 0x4e, 0xac, 0x4a, 0xe6, 0xb4, 0xc7, 0xc8, 0xb2, 0x48, - 0x3b, 0xa7, 0xa7, 0x07, 0xbf, 0xf4, 0x79, 0xbf, 0x3d, 0x18, 0x1c, 0xb6, 0xef, 0xe3, 0x49, 0x99, 0xd0, 0xb1, 0x09, - 0xc3, 0x32, 0xe1, 0xd8, 0x26, 0xd0, 0xd4, 0xd6, 0x86, 0xa4, 0x13, 0x93, 0x04, 0x25, 0xd6, 0xa9, 0x69, 0xfb, 0xbe, - 0x6d, 0xfb, 0x01, 0xd8, 0x31, 0x99, 0xe6, 0x5d, 0xd3, 0x17, 0x17, 0x27, 0x2b, 0xd7, 0x28, 0x9e, 0xa4, 0xae, 0x35, - 0x9f, 0x78, 0x32, 0x18, 0xe0, 0xa1, 0x49, 0x3c, 0xad, 0x12, 0xcf, 0x06, 0x03, 0xd7, 0xd5, 0x03, 0xd3, 0xd5, 0xfd, - 0x2a, 0xeb, 0x7c, 0x30, 0x30, 0x5d, 0x22, 0xe7, 0xb5, 0xae, 0xf4, 0xde, 0x97, 0x52, 0x73, 0xc0, 0x2f, 0x3a, 0xa7, - 0xa7, 0x3d, 0xc0, 0x30, 0x63, 0x8d, 0xea, 0x61, 0x74, 0x13, 0xc0, 0xe8, 0x0e, 0x7e, 0xf7, 0x86, 0x34, 0xbd, 0xa6, - 0x25, 0x90, 0x7a, 0xd1, 0x7f, 0x45, 0x0d, 0x6d, 0x60, 0x6e, 0xfe, 0x4c, 0xec, 0x9f, 0x21, 0x6a, 0x7c, 0xa1, 0x00, - 0x6e, 0xd0, 0x85, 0x78, 0x65, 0xa6, 0xe9, 0xf1, 0x33, 0x05, 0xe7, 0x92, 0xa9, 0xca, 0x69, 0x6f, 0x35, 0xbd, 0x19, - 0xae, 0xa6, 0xea, 0x0b, 0xfa, 0x33, 0xfe, 0x53, 0x1d, 0xc6, 0xfd, 0x66, 0x23, 0x61, 0x7f, 0x8e, 0xc0, 0x8b, 0xa5, - 0x97, 0x8e, 0xd8, 0x04, 0xf5, 0xfa, 0x7f, 0x2a, 0x3c, 0x68, 0x04, 0x19, 0x3f, 0x6c, 0xa7, 0x80, 0x8f, 0xcb, 0x66, - 0x62, 0xfc, 0x03, 0xea, 0xa1, 0xde, 0x9f, 0xea, 0xf0, 0x4f, 0x74, 0xef, 0xa8, 0x9a, 0xcb, 0xef, 0xd2, 0x6d, 0xe1, - 0x2a, 0xf0, 0xcd, 0x61, 0xb9, 0x85, 0x19, 0x6e, 0x37, 0x19, 0x84, 0x09, 0x03, 0x27, 0x68, 0x12, 0xcb, 0x06, 0x3f, - 0x3a, 0x6e, 0xa1, 0x1f, 0xda, 0x1d, 0x10, 0xeb, 0x9b, 0xe2, 0x70, 0x7b, 0xd3, 0x17, 0xcd, 0x63, 0xfc, 0xa0, 0x59, - 0xe0, 0x36, 0xc2, 0xcd, 0xb6, 0xd7, 0xb7, 0xf6, 0x55, 0xdc, 0x42, 0x58, 0xc5, 0xe7, 0xf0, 0xcf, 0x09, 0x1a, 0x54, - 0x1b, 0xf2, 0x8a, 0x6e, 0xf6, 0x0e, 0x1e, 0x9b, 0x24, 0x56, 0x0d, 0x7e, 0x74, 0xd6, 0x42, 0x3f, 0x9c, 0x99, 0x8e, - 0xd8, 0xa1, 0xde, 0xd1, 0x95, 0xc4, 0x27, 0x4d, 0x09, 0x1d, 0xb5, 0xca, 0x7e, 0x44, 0x7c, 0x8a, 0xb0, 0x88, 0x8f, - 0xe1, 0x9f, 0x76, 0xd8, 0xcf, 0xaf, 0x5b, 0xfd, 0x98, 0x79, 0xb7, 0x71, 0x72, 0x6a, 0x1d, 0x40, 0x95, 0xbd, 0x8d, - 0x6d, 0xb0, 0xcb, 0xb6, 0xb9, 0x46, 0x6a, 0x1f, 0xc1, 0x07, 0xc2, 0xfa, 0x90, 0x28, 0xcc, 0x0e, 0xc1, 0x73, 0x14, - 0x0c, 0x26, 0xd4, 0xc5, 0x71, 0x57, 0x35, 0x1a, 0x48, 0xf4, 0xd5, 0xe0, 0x90, 0xb4, 0x9b, 0xba, 0xc9, 0x30, 0xfc, - 0x6e, 0x90, 0x32, 0x1c, 0x99, 0xa8, 0x7a, 0x7d, 0xec, 0x7a, 0xb5, 0x77, 0xce, 0x1e, 0x3b, 0x08, 0x21, 0xaa, 0x17, - 0xeb, 0x26, 0x43, 0x47, 0xa2, 0x11, 0xeb, 0x0b, 0xd6, 0x3b, 0x4b, 0x5b, 0xc8, 0x60, 0xa7, 0xea, 0xc5, 0xac, 0xc9, - 0x21, 0xbd, 0x93, 0xc6, 0xbc, 0xa9, 0xe1, 0xd7, 0x49, 0x30, 0x0b, 0x01, 0x78, 0x57, 0xf9, 0xc1, 0x14, 0x47, 0x9d, - 0xd3, 0x53, 0x2c, 0x08, 0x4f, 0x26, 0xe6, 0x97, 0x22, 0x3c, 0x19, 0x9a, 0x5f, 0x92, 0x94, 0xf0, 0xb2, 0xbd, 0xe3, - 0x82, 0x04, 0xab, 0x6a, 0x52, 0x28, 0x2c, 0x68, 0x81, 0x8e, 0x3a, 0xfe, 0x42, 0x1a, 0x4f, 0xfd, 0x1c, 0x40, 0x00, - 0x2f, 0x8c, 0x2d, 0xa2, 0x6c, 0x16, 0x38, 0x27, 0xf4, 0x32, 0x39, 0xed, 0x4d, 0x8f, 0xe2, 0x4e, 0x53, 0x36, 0x0b, - 0x94, 0x4e, 0x8f, 0x4c, 0x4d, 0x9c, 0x91, 0xc7, 0xd4, 0xb6, 0x86, 0xa7, 0x70, 0x8b, 0x98, 0x91, 0xec, 0xf0, 0xac, - 0xd5, 0x48, 0x4e, 0x11, 0xee, 0x67, 0xab, 0x16, 0xce, 0x57, 0xab, 0x16, 0xa6, 0xc1, 0x32, 0x3c, 0x16, 0x1e, 0x20, - 0xa5, 0x8e, 0x68, 0x33, 0x2a, 0x4c, 0x8f, 0xc7, 0x1a, 0x6e, 0xc4, 0x35, 0xf8, 0x99, 0x68, 0xf0, 0x80, 0x49, 0xb9, - 0xbb, 0x8a, 0x42, 0x26, 0x2e, 0xde, 0x38, 0xd4, 0x1a, 0xbd, 0x16, 0x7e, 0x5d, 0xbd, 0x4d, 0xa3, 0x88, 0x7f, 0x97, - 0xd8, 0xa6, 0x05, 0xc5, 0xe8, 0x76, 0xb1, 0x5f, 0xe9, 0x56, 0xb1, 0x37, 0x3b, 0x8a, 0x5d, 0x6d, 0x17, 0xfb, 0x28, - 0x03, 0x1d, 0x17, 0xff, 0xe1, 0xf8, 0xac, 0xd5, 0x38, 0x06, 0x64, 0x3d, 0x3e, 0x6b, 0x55, 0x85, 0xee, 0xd1, 0x6a, - 0xad, 0x34, 0xf9, 0x4c, 0xad, 0x95, 0x3f, 0xf7, 0xee, 0xc6, 0x66, 0xe1, 0xac, 0xb3, 0x73, 0xe9, 0xd9, 0xdc, 0x3f, - 0x05, 0x2b, 0x0a, 0x61, 0xa8, 0x9d, 0xee, 0x9f, 0x0d, 0x7a, 0x53, 0x16, 0x37, 0x20, 0x15, 0xa5, 0x63, 0xed, 0x7e, - 0xa1, 0xf2, 0x32, 0xf5, 0xa3, 0x84, 0xa4, 0xce, 0x00, 0x61, 0x49, 0x1a, 0xba, 0x7f, 0x3c, 0x30, 0xe7, 0x5d, 0x01, - 0xbf, 0x4f, 0xcc, 0xef, 0x52, 0x95, 0xe1, 0x5c, 0x01, 0xa6, 0x37, 0xc3, 0xa8, 0x27, 0xc8, 0x6b, 0x1a, 0x1b, 0xeb, - 0x6e, 0x94, 0x96, 0x19, 0xea, 0x0b, 0x64, 0xbc, 0x29, 0x33, 0x04, 0x79, 0x2d, 0xdc, 0x6f, 0xbc, 0x2c, 0x52, 0xb0, - 0xf4, 0xc0, 0x93, 0x14, 0xac, 0x3c, 0xf0, 0x30, 0x15, 0xe0, 0x89, 0x40, 0x53, 0x16, 0xd8, 0x8f, 0x3f, 0x74, 0xba, - 0x23, 0x73, 0xdf, 0x49, 0x0c, 0x96, 0x76, 0x19, 0x9c, 0x14, 0x1f, 0x65, 0x0c, 0x7f, 0x1b, 0x1a, 0x61, 0x06, 0x6d, - 0x32, 0x84, 0x79, 0x52, 0x10, 0x48, 0xc3, 0x3c, 0x99, 0x10, 0x06, 0x4d, 0xf2, 0x64, 0x48, 0x58, 0xbf, 0x13, 0xa0, - 0xc9, 0x53, 0x03, 0x3b, 0x00, 0x0e, 0xaf, 0x5f, 0x21, 0x6b, 0xdb, 0x38, 0xdc, 0x4d, 0x43, 0x13, 0x82, 0x70, 0x15, - 0xc3, 0x2c, 0x60, 0x73, 0x9a, 0x9f, 0x9d, 0x2a, 0xf0, 0x22, 0x4f, 0xa8, 0xa1, 0xde, 0x7f, 0x01, 0x59, 0x8d, 0xef, - 0x2d, 0xd9, 0x1a, 0xef, 0xdd, 0x5b, 0x8a, 0xf5, 0x0f, 0xf0, 0x47, 0xb9, 0x3f, 0xda, 0x9c, 0x7e, 0x6b, 0xf4, 0x57, - 0x0a, 0xc5, 0x76, 0x94, 0x42, 0x7f, 0x79, 0x47, 0x34, 0x45, 0x96, 0xb7, 0x69, 0x34, 0xa2, 0xc5, 0xe7, 0x08, 0x7f, - 0x4a, 0xa3, 0x1c, 0x18, 0xc1, 0x08, 0x7f, 0x4c, 0xa3, 0x82, 0x45, 0xf8, 0x8f, 0x34, 0x1a, 0xe6, 0x8b, 0x08, 0x7f, - 0x48, 0xa3, 0x49, 0x11, 0xe1, 0xf7, 0xa0, 0x26, 0x1c, 0xf1, 0xc5, 0x2c, 0xc2, 0xbf, 0xa7, 0x91, 0x32, 0x76, 0xf8, - 0xf8, 0x61, 0x1a, 0x31, 0x16, 0xe1, 0x77, 0x69, 0x24, 0xf3, 0x08, 0x5f, 0xa5, 0x91, 0x2c, 0x22, 0xfc, 0x28, 0x8d, - 0x0a, 0x1a, 0xe1, 0xc7, 0x69, 0x04, 0x85, 0x26, 0x11, 0x7e, 0x92, 0x46, 0xd0, 0xb2, 0x8a, 0xf0, 0xdb, 0x34, 0xe2, - 0x22, 0xc2, 0xbf, 0xa5, 0x91, 0x5e, 0x14, 0xff, 0x2c, 0x24, 0x57, 0x11, 0x7e, 0x9a, 0x46, 0x53, 0x1e, 0xe1, 0x37, - 0x69, 0x54, 0xc8, 0x08, 0xbf, 0x4e, 0x23, 0x9a, 0x47, 0xf8, 0x55, 0x1a, 0xe5, 0x2c, 0xc2, 0xbf, 0xa6, 0xd1, 0x88, - 0x45, 0xf8, 0x65, 0x1a, 0xdd, 0xb1, 0x3c, 0x97, 0x11, 0x7e, 0x96, 0x46, 0x4c, 0x44, 0xf8, 0x97, 0x34, 0xca, 0xa6, - 0x11, 0xfe, 0x29, 0x8d, 0x68, 0xf1, 0x59, 0x45, 0xf8, 0x79, 0x1a, 0x31, 0x1a, 0xe1, 0x17, 0xb6, 0xa3, 0x49, 0x84, - 0x7f, 0x4e, 0xa3, 0x9b, 0x69, 0xb4, 0xc6, 0x4a, 0x91, 0xe5, 0x6b, 0x9e, 0xb1, 0x3f, 0x58, 0x1a, 0x8d, 0x5b, 0xe3, - 0xf3, 0xf1, 0x38, 0xc2, 0x54, 0x68, 0xfe, 0xcf, 0x82, 0xdd, 0x3c, 0xd5, 0x90, 0x48, 0xd9, 0x70, 0x74, 0x3f, 0xc2, - 0xf4, 0x9f, 0x05, 0x4d, 0xa3, 0xf1, 0xd8, 0x14, 0xf8, 0x67, 0x41, 0x67, 0xb4, 0x78, 0xcb, 0xd2, 0xe8, 0xfe, 0x78, - 0x3c, 0x1e, 0x9d, 0x44, 0x98, 0x7e, 0x5d, 0x7c, 0x34, 0x2d, 0x98, 0x02, 0x43, 0xc6, 0x27, 0x50, 0xf7, 0x74, 0x7c, - 0x3a, 0xca, 0x22, 0x3c, 0xe4, 0xea, 0x9f, 0x05, 0x7c, 0x8f, 0xd9, 0x49, 0x76, 0x12, 0xe1, 0x61, 0x4e, 0xb3, 0xcf, - 0x69, 0xd4, 0x32, 0xbf, 0xc4, 0x2f, 0x6c, 0xf4, 0x7a, 0x26, 0x8d, 0x12, 0x7d, 0xcc, 0x86, 0xd9, 0x28, 0xc2, 0x66, - 0x30, 0x63, 0xf8, 0xfb, 0x85, 0xbf, 0x63, 0x3a, 0x8d, 0xce, 0x69, 0x67, 0xc8, 0x3a, 0x11, 0x1e, 0xbe, 0xb9, 0x11, - 0x69, 0x44, 0x4f, 0x3b, 0xb4, 0x43, 0x23, 0x3c, 0x5c, 0x14, 0xf9, 0xdd, 0x8d, 0x94, 0x23, 0x00, 0xc2, 0xf0, 0xfc, - 0xfc, 0x7e, 0x84, 0x33, 0xfa, 0xab, 0x86, 0xda, 0xa7, 0xe3, 0x07, 0x8c, 0xb6, 0x22, 0xfc, 0x0b, 0x2d, 0xf4, 0xc7, - 0x85, 0x72, 0x03, 0x6d, 0x41, 0x8a, 0xcc, 0xde, 0x81, 0x82, 0x39, 0x1a, 0x75, 0xce, 0x1e, 0xb4, 0x59, 0x84, 0xb3, - 0xab, 0xd7, 0xd0, 0xdb, 0xfd, 0xf1, 0x69, 0x0b, 0x3e, 0x04, 0x48, 0x6a, 0xac, 0x80, 0x46, 0xce, 0x4e, 0x1e, 0x9c, - 0xb2, 0x91, 0x49, 0x54, 0x3c, 0xff, 0x6c, 0x66, 0x7f, 0x0e, 0xf3, 0xc9, 0x0a, 0x3e, 0x53, 0x52, 0xa4, 0xd1, 0x28, - 0x6b, 0x9f, 0x1c, 0x43, 0xc2, 0x1d, 0x15, 0x1e, 0x38, 0xb7, 0x50, 0xf5, 0x7c, 0x18, 0xe1, 0x5b, 0x9b, 0x7a, 0x3e, - 0x34, 0x1f, 0x93, 0x77, 0xbf, 0x8a, 0x37, 0xa3, 0x34, 0x1a, 0x9e, 0x9f, 0x9f, 0xb5, 0x20, 0xe1, 0x03, 0xbd, 0x4b, - 0x23, 0xfa, 0x00, 0xfe, 0x83, 0xec, 0x8f, 0xcf, 0xa0, 0x43, 0x18, 0xe1, 0xed, 0xe4, 0x63, 0x98, 0xf3, 0x79, 0x4a, - 0x3f, 0xf3, 0x34, 0x1a, 0x8e, 0x86, 0xf7, 0xcf, 0xa0, 0xde, 0x8c, 0x4e, 0x9e, 0x69, 0x0a, 0xed, 0xb6, 0x5a, 0xa6, - 0xe5, 0x77, 0xfc, 0x0b, 0x33, 0xd5, 0x4f, 0x4f, 0xcf, 0x86, 0x1d, 0x18, 0xc1, 0x15, 0xa8, 0x18, 0x60, 0x3c, 0xe7, - 0x99, 0x69, 0xf0, 0x2a, 0x7b, 0x3a, 0x4a, 0xa3, 0x07, 0x0f, 0x8e, 0x3b, 0x59, 0x16, 0xe1, 0xdb, 0x8f, 0x23, 0x5b, - 0xdb, 0xe4, 0x29, 0x80, 0x7d, 0x1a, 0xb1, 0x07, 0x0f, 0xce, 0xee, 0x53, 0xf8, 0x7e, 0x6e, 0xda, 0x3a, 0x1f, 0x0f, - 0xb3, 0x73, 0x68, 0xeb, 0x77, 0x98, 0xce, 0xc9, 0xf9, 0xf1, 0xc8, 0xf4, 0xf5, 0xbb, 0x19, 0x75, 0x67, 0x7c, 0x32, - 0x3e, 0x31, 0x99, 0x66, 0xa8, 0xe5, 0xe7, 0x6f, 0x2c, 0x8d, 0x32, 0x36, 0x6a, 0x47, 0xf8, 0xd6, 0x2d, 0xdc, 0x83, - 0x93, 0x56, 0x6b, 0x74, 0x1c, 0xe1, 0xd1, 0xc3, 0xf9, 0xfc, 0xad, 0x81, 0x60, 0xfb, 0xe4, 0x81, 0xfd, 0x56, 0x9f, - 0xef, 0xa0, 0xe9, 0xa1, 0x01, 0xda, 0x88, 0xcf, 0x4c, 0xcb, 0x67, 0x0f, 0xe0, 0x3f, 0xf3, 0x6d, 0x9a, 0x2e, 0xbf, - 0xe5, 0x68, 0x62, 0x17, 0xa5, 0xcd, 0x1e, 0xb4, 0xa0, 0xc6, 0x98, 0x7f, 0x1c, 0x16, 0x1c, 0xd0, 0x68, 0xd8, 0x81, - 0xff, 0x8b, 0xf0, 0x38, 0xbf, 0x7a, 0xed, 0x70, 0x76, 0x3c, 0xa6, 0xe3, 0x56, 0x84, 0xc7, 0xf2, 0xa3, 0xd2, 0x1f, - 0x1e, 0x8a, 0x34, 0xea, 0x74, 0xce, 0x87, 0xa6, 0xcc, 0xe2, 0x17, 0xc5, 0x0d, 0x1e, 0xb7, 0x4c, 0x2b, 0x13, 0xfa, - 0x56, 0x0d, 0xaf, 0x24, 0xac, 0x24, 0xfc, 0x17, 0xe1, 0x09, 0xe8, 0xa5, 0x5c, 0x2b, 0xe7, 0x76, 0x3b, 0x4c, 0xde, - 0x19, 0xd4, 0x1c, 0xdd, 0x07, 0x78, 0xf9, 0x65, 0x1c, 0x51, 0x7a, 0xda, 0x69, 0x45, 0xd8, 0x8c, 0xfa, 0xbc, 0x05, - 0xff, 0x45, 0xd8, 0x42, 0xce, 0xc0, 0x75, 0xf2, 0xf1, 0xd9, 0xcb, 0x9b, 0x34, 0xa2, 0xa3, 0xf1, 0x18, 0x96, 0xc4, - 0x4c, 0xc6, 0x17, 0x9b, 0x4a, 0xc1, 0xee, 0x7e, 0xbd, 0x71, 0xdb, 0xc5, 0x24, 0x68, 0x07, 0x9d, 0xb3, 0x07, 0xc3, - 0x93, 0x08, 0xbf, 0x1d, 0x71, 0x2a, 0x60, 0x95, 0xb2, 0xd1, 0x69, 0x76, 0x9a, 0x99, 0x84, 0x89, 0x4c, 0xa3, 0x13, - 0x58, 0xf2, 0x4e, 0x84, 0xf9, 0x97, 0xab, 0x3b, 0x8b, 0x6e, 0x50, 0xdb, 0x21, 0xc8, 0xb8, 0xc5, 0xce, 0xce, 0xb3, - 0x08, 0xe7, 0xf4, 0xcb, 0xb3, 0x5f, 0x8b, 0x34, 0x62, 0x67, 0xec, 0x6c, 0x4c, 0xfd, 0xf7, 0x1f, 0x6a, 0x6a, 0x6a, - 0xb4, 0xc6, 0xa7, 0x90, 0x74, 0x23, 0xcc, 0x58, 0xef, 0x67, 0x63, 0x83, 0x21, 0xaf, 0x66, 0x52, 0x64, 0x4f, 0xc7, - 0x63, 0x69, 0xb1, 0x98, 0xc2, 0x26, 0xfc, 0x04, 0xd0, 0xa6, 0xa3, 0xd1, 0x39, 0x3b, 0x8b, 0xf0, 0x27, 0xbb, 0x4b, - 0xdc, 0x04, 0x3e, 0x59, 0xcc, 0x66, 0x6e, 0xb7, 0x7f, 0xb2, 0x40, 0x81, 0xf9, 0x8e, 0xe9, 0x98, 0x8e, 0x3a, 0x11, - 0xfe, 0x64, 0xe0, 0x32, 0x3a, 0x86, 0xff, 0xa0, 0x00, 0x74, 0xf6, 0xa0, 0xc5, 0xd8, 0x83, 0x96, 0xf9, 0x0a, 0xf3, - 0xdc, 0xcc, 0x87, 0x67, 0x59, 0x3b, 0xc2, 0x9f, 0x1c, 0x3a, 0x8e, 0xc7, 0xb4, 0x05, 0xe8, 0xf8, 0xc9, 0xa1, 0x63, - 0xa7, 0x35, 0xec, 0x50, 0xf3, 0x6d, 0xb1, 0xe6, 0xfc, 0x7e, 0xc6, 0x60, 0x72, 0x9f, 0x2c, 0x42, 0xde, 0xbf, 0x7f, - 0x7e, 0xfe, 0xe0, 0x01, 0x7c, 0x9a, 0xb6, 0xcb, 0x4f, 0xa5, 0x1f, 0xe6, 0x06, 0xc9, 0x5a, 0xd9, 0x09, 0xd0, 0xc9, - 0x4f, 0x66, 0x8c, 0xe3, 0xf1, 0x98, 0xb5, 0x22, 0x9c, 0xf3, 0x19, 0xb3, 0x98, 0x60, 0x7f, 0x9b, 0x8e, 0x8e, 0x3b, - 0xd9, 0xe8, 0xb8, 0x13, 0xe1, 0xfc, 0xed, 0x33, 0x33, 0x9b, 0x16, 0xcc, 0xde, 0x6f, 0x39, 0x8f, 0x35, 0x33, 0xfa, - 0x06, 0x06, 0x09, 0x2b, 0x0d, 0x95, 0xdf, 0x07, 0xf4, 0xf0, 0xec, 0x2c, 0x1b, 0xc1, 0x40, 0xdf, 0x43, 0xb7, 0x00, - 0xc6, 0xf7, 0x76, 0xf3, 0x0d, 0xe9, 0xe9, 0x29, 0x4c, 0xf7, 0xfd, 0x7c, 0x51, 0xcc, 0x5f, 0xa5, 0xd1, 0x83, 0xe3, - 0xfb, 0xad, 0xd1, 0x30, 0xc2, 0xef, 0xdd, 0x04, 0x8f, 0xb3, 0xe1, 0xf1, 0xfd, 0x76, 0x84, 0xdf, 0x9b, 0xfd, 0x76, - 0x7f, 0x78, 0x76, 0x0e, 0xe7, 0xc6, 0x7b, 0x35, 0x2f, 0xde, 0x4e, 0x4c, 0x81, 0x31, 0x7d, 0x00, 0xcd, 0xfe, 0x66, - 0x76, 0xe3, 0xa8, 0x0d, 0x1b, 0xf9, 0xbd, 0xd9, 0x64, 0x06, 0x4f, 0xee, 0xb7, 0x4f, 0xcf, 0x4f, 0x23, 0x3c, 0xe3, - 0x23, 0x01, 0x04, 0xde, 0x6c, 0x94, 0x07, 0xed, 0x07, 0xf7, 0x5b, 0x11, 0x9e, 0xbd, 0xd5, 0xd9, 0x47, 0x3a, 0x33, - 0xd4, 0x78, 0x0c, 0x30, 0x9b, 0x71, 0xa5, 0xef, 0xde, 0x28, 0x47, 0x8f, 0x59, 0x3b, 0xc2, 0x33, 0x99, 0x65, 0x54, - 0xbd, 0xb5, 0x09, 0xc3, 0xd3, 0x08, 0x0b, 0xfa, 0x85, 0xfe, 0x2d, 0xfd, 0x66, 0x1a, 0x31, 0x3a, 0x32, 0x69, 0x06, - 0x87, 0x23, 0xfc, 0x6e, 0x04, 0xd7, 0x60, 0x69, 0x34, 0x1e, 0x8d, 0x4f, 0x01, 0x3c, 0x40, 0x80, 0x2c, 0x76, 0x03, - 0x34, 0xe0, 0x6b, 0xf4, 0x68, 0x98, 0x46, 0x67, 0xc3, 0x73, 0xd6, 0x39, 0x8e, 0x70, 0x49, 0x8d, 0xe8, 0x29, 0xe4, - 0x9b, 0xcf, 0x8f, 0x66, 0x4b, 0x9d, 0xd8, 0x04, 0x03, 0xa0, 0x11, 0xbd, 0xdf, 0x1a, 0x9d, 0x45, 0x78, 0xfe, 0x9a, - 0xf9, 0x3d, 0xc6, 0x18, 0x3b, 0x07, 0x58, 0x42, 0x92, 0x41, 0xa0, 0xf3, 0xf1, 0xf0, 0xc1, 0xb9, 0xf9, 0x06, 0x30, - 0xd0, 0x31, 0x63, 0x00, 0xa4, 0xf9, 0x6b, 0x56, 0x02, 0x62, 0x34, 0xbc, 0xdf, 0x02, 0xfa, 0x32, 0xa7, 0x73, 0x7a, - 0x47, 0x6f, 0x9e, 0xce, 0xcd, 0x9c, 0xc6, 0xa3, 0xd3, 0x08, 0xcf, 0x9f, 0xff, 0x32, 0x5f, 0x8c, 0xc7, 0x66, 0x42, - 0x74, 0xf8, 0x20, 0xc2, 0x73, 0x56, 0x2c, 0x60, 0x8d, 0xce, 0x4f, 0x8f, 0xc7, 0x11, 0x76, 0x68, 0x98, 0xb5, 0xb2, - 0x21, 0xdc, 0xf3, 0x2d, 0x66, 0x69, 0x34, 0x1a, 0xd1, 0xd6, 0x08, 0x6e, 0xfd, 0xe4, 0xcd, 0xaf, 0x85, 0x45, 0x23, - 0x66, 0xf0, 0xc1, 0xad, 0x21, 0xcc, 0x17, 0xe0, 0xf1, 0x71, 0xc8, 0xb2, 0x8c, 0xba, 0xc4, 0xb3, 0xb3, 0xe3, 0x63, - 0xc0, 0x3d, 0x3b, 0x43, 0x8b, 0x20, 0x6f, 0xd4, 0xdd, 0xb0, 0x90, 0x70, 0x74, 0x01, 0x51, 0x05, 0xb2, 0xfa, 0xe6, - 0xee, 0xb5, 0xa1, 0xab, 0xed, 0xb3, 0x07, 0xb0, 0x00, 0x8a, 0x8e, 0x46, 0xaf, 0xec, 0xe1, 0x76, 0x3e, 0x3c, 0x39, - 0x6d, 0x1f, 0x47, 0xd8, 0x6f, 0x04, 0x7a, 0xde, 0xba, 0xdf, 0x81, 0x12, 0x62, 0x74, 0x67, 0x4b, 0x8c, 0x4f, 0xe8, - 0xc9, 0x59, 0x2b, 0xc2, 0x7e, 0x6b, 0xb0, 0xf3, 0xe1, 0xe9, 0x7d, 0xf8, 0x54, 0x53, 0x96, 0xe7, 0x06, 0xbf, 0x4f, - 0x01, 0x2e, 0x8a, 0x3f, 0x13, 0x34, 0x8d, 0x68, 0xeb, 0xb4, 0xd3, 0x19, 0xc1, 0x67, 0xfe, 0x85, 0x15, 0x69, 0x94, - 0xb5, 0xe0, 0xbf, 0x08, 0x07, 0x3b, 0x89, 0x0d, 0x23, 0x6c, 0xf0, 0xee, 0x8c, 0x9e, 0x9a, 0xbd, 0xef, 0x76, 0x55, - 0xeb, 0xbc, 0x05, 0x1b, 0xd6, 0x6d, 0x2a, 0xf7, 0xa5, 0x84, 0xbc, 0x71, 0x24, 0x96, 0x46, 0x38, 0x40, 0xd0, 0xf1, - 0xfd, 0x71, 0x84, 0xfd, 0x8e, 0x3b, 0x39, 0x3b, 0xef, 0x00, 0x29, 0xd3, 0x40, 0x28, 0x46, 0x9d, 0xe1, 0x09, 0x90, - 0x26, 0xcd, 0x5e, 0x5b, 0x3c, 0x89, 0xb0, 0x7e, 0xaa, 0xf4, 0xab, 0x34, 0x1a, 0x9d, 0x0f, 0xc7, 0xa3, 0xf3, 0x08, - 0x6b, 0x39, 0xa3, 0x5a, 0x1a, 0x0a, 0x78, 0x7c, 0x72, 0x3f, 0xc2, 0x06, 0xcd, 0x5b, 0xac, 0x35, 0x6a, 0x45, 0xd8, - 0x1d, 0x25, 0x8c, 0x9d, 0x77, 0x60, 0x5a, 0x3f, 0x3f, 0xd7, 0x80, 0xcb, 0x23, 0x36, 0x3c, 0x8e, 0x70, 0x49, 0xef, - 0x0d, 0x21, 0x82, 0x2f, 0x35, 0x93, 0x9f, 0x1d, 0xeb, 0x01, 0xa4, 0xce, 0x6f, 0x78, 0x58, 0x86, 0x97, 0x37, 0x16, - 0x8d, 0xa8, 0xd9, 0xe2, 0xc1, 0x3d, 0xe8, 0x13, 0x1a, 0x7b, 0xb6, 0x9d, 0x93, 0xe5, 0x1a, 0x97, 0xe1, 0x45, 0x3f, - 0xb3, 0x3b, 0x15, 0x2b, 0x65, 0x38, 0xd9, 0x20, 0x05, 0x5c, 0x00, 0x9c, 0x41, 0xbd, 0xf3, 0x99, 0x04, 0x41, 0x52, - 0x90, 0x56, 0x57, 0x5c, 0x78, 0x3f, 0xce, 0xae, 0x80, 0xa0, 0x03, 0x90, 0x5e, 0x10, 0x4a, 0x34, 0xc4, 0x66, 0xb1, - 0xc2, 0xa4, 0x37, 0x6f, 0x37, 0x32, 0xa5, 0xb4, 0x06, 0xf3, 0x94, 0x50, 0x1f, 0x95, 0x1d, 0x2e, 0x69, 0x21, 0x6e, - 0x11, 0xea, 0x4a, 0x62, 0x62, 0x2c, 0xbf, 0x10, 0x3a, 0x56, 0xaa, 0x5f, 0x0c, 0x70, 0xfb, 0x0c, 0x61, 0x88, 0x5e, - 0x40, 0xfa, 0xf2, 0xf2, 0xb2, 0x7d, 0x76, 0x60, 0x84, 0xbe, 0xcb, 0xcb, 0x73, 0xfb, 0x03, 0xfe, 0x1d, 0x54, 0x11, - 0xa3, 0x61, 0x7c, 0x8f, 0x58, 0xa0, 0xd1, 0x33, 0xfc, 0xf5, 0x23, 0xb6, 0x5a, 0xc5, 0x8f, 0x18, 0x81, 0x19, 0xe3, - 0x47, 0x2c, 0x31, 0xb7, 0x06, 0xd6, 0x37, 0x85, 0xf4, 0x41, 0x73, 0xd6, 0xc2, 0x10, 0xc7, 0xdc, 0x73, 0xde, 0x8f, - 0x58, 0x9f, 0xd7, 0xfd, 0x9a, 0xab, 0xe0, 0xc1, 0x07, 0x07, 0xcb, 0x22, 0xd5, 0x56, 0x4c, 0xd0, 0x56, 0x4c, 0xd0, - 0x56, 0x4c, 0xd0, 0x55, 0xf8, 0xf6, 0x93, 0x1e, 0x48, 0x29, 0x46, 0xd9, 0xe2, 0x78, 0xea, 0x77, 0xa0, 0xf6, 0x00, - 0xed, 0x64, 0xaf, 0x52, 0x76, 0x94, 0xba, 0x8a, 0x9d, 0x0a, 0x8c, 0x9d, 0x89, 0x4e, 0xdb, 0x71, 0xf4, 0xef, 0xa8, - 0x3b, 0x5e, 0xd6, 0xc4, 0xb2, 0x77, 0x3b, 0xc5, 0x32, 0x58, 0x49, 0x23, 0x9a, 0xed, 0xdb, 0x48, 0x18, 0xba, 0x7f, - 0xdf, 0x08, 0x66, 0x55, 0x78, 0xb6, 0x06, 0x24, 0x75, 0x41, 0x0a, 0x39, 0x37, 0x52, 0x5a, 0x81, 0xd2, 0x91, 0x8e, - 0x0b, 0xd0, 0x50, 0x7a, 0x05, 0x65, 0x19, 0x45, 0xb4, 0x61, 0x00, 0xa2, 0xac, 0x8c, 0x66, 0x65, 0xb5, 0x53, 0x10, - 0x5d, 0x40, 0x13, 0x66, 0x24, 0x16, 0x68, 0x40, 0x98, 0x06, 0x84, 0xab, 0x0c, 0xe2, 0x8c, 0xcb, 0x3e, 0x31, 0xd9, - 0xca, 0x64, 0xab, 0x32, 0x5b, 0xfa, 0x6c, 0x2b, 0x24, 0x4a, 0x93, 0x2d, 0xcb, 0x6c, 0x90, 0xd9, 0xf0, 0x24, 0x55, - 0x78, 0x98, 0x4a, 0x2b, 0xaa, 0x55, 0xb2, 0xd5, 0x5b, 0x1a, 0x6a, 0x73, 0x0f, 0x0e, 0xe2, 0x52, 0x4e, 0x32, 0x6a, - 0xe2, 0x7b, 0x4b, 0x9e, 0x14, 0x46, 0x06, 0xe2, 0xc9, 0xc4, 0xfd, 0x1d, 0xae, 0x37, 0x65, 0xa5, 0x62, 0x32, 0xfc, - 0x46, 0x49, 0xf4, 0x97, 0x57, 0xa2, 0x3e, 0xe2, 0x26, 0xfe, 0xcc, 0x05, 0x49, 0x5a, 0xad, 0xe3, 0xf6, 0x71, 0xeb, - 0xbc, 0xc7, 0x0f, 0xdb, 0x9d, 0xe4, 0x41, 0x27, 0x35, 0x8a, 0x88, 0xb9, 0xbc, 0x01, 0x05, 0xcc, 0x51, 0x27, 0x39, - 0x41, 0x87, 0xed, 0xa4, 0x75, 0x7a, 0xda, 0x84, 0x7f, 0xf0, 0x7b, 0x5d, 0x56, 0x3b, 0x69, 0x9d, 0x9c, 0xf6, 0xf8, - 0xd1, 0x46, 0xa5, 0x98, 0x37, 0xa0, 0x20, 0x3a, 0x32, 0x95, 0x30, 0xd4, 0xaf, 0x96, 0xf7, 0xd9, 0x96, 0x9e, 0xe7, - 0xbd, 0x8e, 0x95, 0x55, 0xc5, 0x01, 0x54, 0xfd, 0xd7, 0xc4, 0x00, 0xd1, 0x7f, 0x0d, 0xcb, 0x18, 0xb1, 0xcb, 0x02, - 0x44, 0xed, 0x47, 0x3c, 0x16, 0x0d, 0x76, 0x18, 0xdb, 0x7c, 0x0d, 0x75, 0x9b, 0x10, 0xb7, 0x0d, 0x4f, 0x5c, 0xae, - 0x0a, 0x73, 0x27, 0x08, 0x35, 0x15, 0xe4, 0x0e, 0x5d, 0xae, 0x0c, 0x73, 0x87, 0x08, 0x35, 0x25, 0xe4, 0xd2, 0x94, - 0x27, 0x14, 0x72, 0x74, 0x42, 0x9b, 0x06, 0x92, 0xd5, 0xa2, 0x3c, 0x67, 0x7e, 0xd8, 0x7c, 0x0c, 0xcb, 0x63, 0x08, - 0x8a, 0x13, 0xa4, 0x05, 0xbc, 0xed, 0x51, 0x6a, 0x73, 0x5a, 0xb8, 0x54, 0xe3, 0x40, 0x46, 0x03, 0xfe, 0x39, 0x64, - 0xe6, 0xc1, 0x87, 0x56, 0xef, 0xf8, 0xac, 0x95, 0xb6, 0xc1, 0x49, 0x19, 0x64, 0x6d, 0x61, 0x65, 0x6d, 0xe1, 0x65, - 0x6d, 0xe1, 0x65, 0x6d, 0x10, 0xe0, 0x83, 0xbe, 0xff, 0x91, 0x35, 0xc3, 0x0f, 0x5e, 0x5a, 0x91, 0x58, 0x33, 0x81, - 0x58, 0xaf, 0x56, 0xcb, 0x35, 0xd8, 0xf8, 0x94, 0x35, 0xa4, 0xaa, 0xd4, 0x9f, 0xcb, 0x22, 0x6d, 0xe1, 0x49, 0x0a, - 0x5a, 0xee, 0x16, 0xa6, 0x66, 0x73, 0x7b, 0xaa, 0xb0, 0x19, 0x3f, 0xa6, 0xe7, 0xd5, 0xc9, 0x97, 0xe4, 0xd8, 0x68, - 0x8f, 0x97, 0x45, 0xca, 0x2d, 0xcd, 0xe0, 0x96, 0x66, 0x70, 0x4b, 0x33, 0xa0, 0x11, 0x5c, 0x16, 0x36, 0x65, 0x13, - 0x4a, 0xe0, 0x4a, 0xa0, 0x7f, 0x3c, 0x80, 0xf0, 0x79, 0xb1, 0x26, 0x66, 0xd4, 0x1b, 0x9d, 0xb7, 0x21, 0x5c, 0x98, - 0x2d, 0xa9, 0x13, 0x6a, 0xbc, 0xa6, 0xcb, 0x31, 0x7f, 0xad, 0xa1, 0x7d, 0x02, 0x6f, 0xb9, 0x3c, 0xd4, 0x71, 0x0b, - 0x8c, 0x26, 0xa2, 0x22, 0xea, 0x19, 0xb2, 0x90, 0x1a, 0x9d, 0x8d, 0x33, 0x86, 0xfe, 0xbc, 0xe1, 0x83, 0x6a, 0x29, - 0x41, 0xf8, 0x82, 0xc1, 0x67, 0x56, 0x39, 0xc5, 0x97, 0xb6, 0x9e, 0xce, 0x50, 0xcb, 0x1e, 0x09, 0x5d, 0x30, 0xd8, - 0xf6, 0xd1, 0x96, 0x7a, 0x82, 0x48, 0x65, 0xdc, 0x05, 0x49, 0x15, 0x2f, 0x18, 0xdc, 0x67, 0xc9, 0x2d, 0x35, 0xce, - 0x24, 0x2f, 0xec, 0x9f, 0xaf, 0x34, 0xf0, 0xb6, 0x2b, 0x26, 0x43, 0xef, 0xa4, 0x7a, 0x6d, 0xa2, 0xea, 0x90, 0xfd, - 0x7d, 0x6b, 0x4b, 0x6d, 0xbe, 0x36, 0x8d, 0xa9, 0x4d, 0xa2, 0xc9, 0x86, 0x1d, 0xea, 0xd7, 0xe8, 0x1f, 0xef, 0x2b, - 0x56, 0x4c, 0x86, 0x28, 0xa0, 0xd9, 0x06, 0xac, 0xaa, 0x02, 0x96, 0x72, 0xf5, 0x4a, 0x17, 0x42, 0xe8, 0xdd, 0x8c, - 0x79, 0x5d, 0x4c, 0x86, 0x3b, 0x1f, 0xfd, 0xb0, 0x3d, 0xf6, 0xde, 0xd2, 0xa0, 0x07, 0xaf, 0xda, 0x9e, 0xb2, 0xdb, - 0xef, 0xd5, 0xb9, 0xd9, 0x59, 0x47, 0xe5, 0xdf, 0xab, 0xf3, 0x74, 0x57, 0x9d, 0x19, 0xbf, 0x8d, 0xfd, 0xde, 0xd1, - 0x81, 0x1a, 0xdb, 0x18, 0xe8, 0x4c, 0x86, 0x10, 0xa5, 0x1d, 0xfe, 0xda, 0x58, 0x2a, 0x5d, 0x4f, 0xc2, 0x61, 0x15, - 0x64, 0x2f, 0x39, 0x4d, 0x19, 0xa6, 0xa4, 0x73, 0x58, 0x98, 0x68, 0x2a, 0x22, 0xa1, 0x4d, 0x95, 0x50, 0x9c, 0x93, - 0x38, 0xa6, 0x87, 0x19, 0xc4, 0x84, 0x69, 0xf7, 0x68, 0x1a, 0xd3, 0x46, 0x86, 0x8e, 0xe2, 0x76, 0x83, 0x1e, 0x66, - 0x08, 0x35, 0xda, 0xa0, 0x33, 0x95, 0xa4, 0xdd, 0xcc, 0x21, 0x4a, 0xa4, 0x21, 0xc5, 0xf9, 0xa1, 0x48, 0x8a, 0x86, - 0x3c, 0x54, 0x49, 0xd1, 0x48, 0x4e, 0xb1, 0x48, 0x26, 0x65, 0xf2, 0xc4, 0x24, 0x4f, 0x6c, 0xf2, 0xb0, 0x4c, 0x1e, - 0x9a, 0xe4, 0xa1, 0x4d, 0xa6, 0xa4, 0x38, 0x14, 0x09, 0x6d, 0xc4, 0xed, 0x66, 0x81, 0x0e, 0x61, 0x04, 0x7e, 0xf4, - 0x44, 0x84, 0xc1, 0xb9, 0xd7, 0xc6, 0xba, 0x65, 0x2e, 0x73, 0x17, 0x2e, 0xb3, 0x02, 0x52, 0xe9, 0x72, 0x04, 0x75, - 0x9e, 0x05, 0x60, 0xc2, 0xda, 0xfe, 0xf1, 0xc1, 0xe0, 0xd6, 0x59, 0x2e, 0x45, 0xe0, 0x52, 0x05, 0x56, 0xe0, 0x9f, - 0x9d, 0x23, 0x09, 0x40, 0x75, 0x4d, 0xf3, 0xf9, 0x94, 0x6e, 0xf9, 0xad, 0x16, 0x93, 0xa1, 0xdb, 0x59, 0x65, 0x33, - 0x8c, 0x16, 0x36, 0xc8, 0x72, 0xdd, 0xc3, 0x10, 0x40, 0xed, 0xbd, 0x1a, 0x13, 0x6a, 0x94, 0xe4, 0xb6, 0xc6, 0xa4, - 0x60, 0x77, 0x2a, 0xa3, 0x39, 0x8b, 0xab, 0x03, 0xb8, 0x1a, 0x26, 0x23, 0x2f, 0xc0, 0x16, 0xbd, 0x38, 0x4c, 0x8e, - 0x1b, 0x3a, 0x99, 0x1c, 0x26, 0xa7, 0x0f, 0x1a, 0x3a, 0x19, 0x1e, 0x26, 0xed, 0x76, 0x85, 0xb3, 0x49, 0x41, 0x74, - 0x32, 0x21, 0x1a, 0x34, 0x86, 0xb6, 0x51, 0x39, 0xa7, 0x60, 0x5c, 0xf5, 0x6f, 0x0c, 0xa3, 0xe1, 0x86, 0x21, 0xd8, - 0xc4, 0xc6, 0x9b, 0xdc, 0x1a, 0x43, 0xd8, 0x4d, 0xe7, 0xf4, 0xb4, 0xa9, 0x93, 0x02, 0x6b, 0xbb, 0x92, 0x4d, 0x9d, - 0x4c, 0xb0, 0xb6, 0xcb, 0xd7, 0xd4, 0xc9, 0xd0, 0x36, 0x65, 0x74, 0x80, 0x4c, 0x04, 0xc0, 0x7a, 0xce, 0x02, 0xc8, - 0x77, 0xbc, 0x7b, 0xc8, 0x1a, 0xb4, 0x86, 0xdf, 0x2b, 0xd7, 0xf4, 0x05, 0x15, 0xd5, 0x60, 0x64, 0xc3, 0xbe, 0x55, - 0xb4, 0x5d, 0x35, 0xc9, 0xfe, 0x75, 0xd9, 0xb2, 0xd9, 0x42, 0xea, 0x7a, 0xc1, 0x87, 0x35, 0x0c, 0x71, 0xa5, 0xdc, - 0xc1, 0xfd, 0x8a, 0x92, 0x18, 0xa2, 0xca, 0x99, 0x53, 0x88, 0x13, 0xaf, 0x47, 0x86, 0x24, 0xde, 0x68, 0xac, 0x51, - 0x1c, 0x9c, 0xb7, 0x4f, 0x43, 0xaa, 0xba, 0x15, 0x6a, 0x8e, 0x90, 0x68, 0x21, 0xac, 0x31, 0xe2, 0x28, 0x0a, 0x58, - 0x10, 0xa7, 0xdd, 0xad, 0x1d, 0x10, 0x07, 0x07, 0x9b, 0xe7, 0x85, 0x0f, 0xfa, 0xbf, 0x15, 0xe8, 0xbf, 0xb2, 0x64, - 0xf3, 0x4f, 0x11, 0x59, 0x1b, 0x57, 0x1e, 0x20, 0x8a, 0x0f, 0xfa, 0x74, 0xdf, 0x50, 0xf8, 0x7e, 0x15, 0xf1, 0xce, - 0xe5, 0x34, 0xcf, 0x4c, 0x86, 0xe9, 0x6b, 0x10, 0x8c, 0xed, 0x4d, 0x38, 0xa1, 0xd2, 0x4a, 0xef, 0x5f, 0x76, 0x1c, - 0x74, 0xe2, 0x9e, 0x4a, 0x09, 0x1b, 0xfd, 0x3b, 0xb4, 0x89, 0xad, 0x60, 0xe3, 0xbc, 0xa1, 0x57, 0xab, 0xda, 0xc3, - 0x38, 0xf6, 0xf9, 0x15, 0x74, 0x70, 0xc0, 0xd5, 0x33, 0x30, 0xe3, 0x65, 0x71, 0x23, 0x3c, 0x7c, 0xff, 0xa9, 0x9d, - 0xd6, 0x7f, 0x9b, 0x73, 0x35, 0x0d, 0x0e, 0xba, 0x87, 0xb5, 0xfc, 0x9d, 0x2b, 0xd1, 0xd3, 0x29, 0x77, 0x6b, 0xfd, - 0x77, 0x65, 0x24, 0xbd, 0xf5, 0x44, 0xd3, 0xc1, 0x01, 0xaf, 0x02, 0x25, 0x45, 0x3f, 0x44, 0xa8, 0x67, 0x64, 0x90, - 0x67, 0xb9, 0xa4, 0x70, 0x23, 0x0a, 0x57, 0x0c, 0x69, 0x83, 0x1f, 0x69, 0xfc, 0x87, 0xfc, 0xff, 0xd4, 0xc8, 0xa1, - 0x4e, 0x1b, 0x3c, 0x10, 0xc0, 0x42, 0x56, 0xa8, 0x0a, 0x51, 0x68, 0x20, 0x1d, 0xda, 0x3c, 0xa3, 0xf2, 0x30, 0xa7, - 0xf3, 0x79, 0x7e, 0x67, 0x5e, 0xa9, 0x0a, 0x38, 0xaa, 0xea, 0xa2, 0xc9, 0xc5, 0x87, 0xc3, 0x05, 0xf0, 0xf4, 0x80, - 0x7b, 0xc8, 0xf8, 0x77, 0x96, 0x97, 0xdb, 0x02, 0x81, 0x64, 0xa6, 0x88, 0x6c, 0xb6, 0xbb, 0xea, 0x12, 0xe4, 0xb2, - 0x66, 0x13, 0x69, 0x17, 0x36, 0x1b, 0x73, 0x90, 0xc9, 0x94, 0xf5, 0xe1, 0xdc, 0xb3, 0x05, 0x41, 0x72, 0x93, 0x46, - 0x64, 0xdb, 0x5d, 0x8a, 0x8f, 0x63, 0x40, 0x23, 0x64, 0x05, 0xbe, 0x50, 0x58, 0xe4, 0xc0, 0x75, 0x16, 0xbe, 0xe3, - 0x6f, 0xb4, 0x54, 0xf4, 0xd5, 0x60, 0x80, 0x0b, 0xf3, 0x30, 0x43, 0x39, 0x9f, 0x42, 0x05, 0x0f, 0xfd, 0x04, 0x22, - 0x0a, 0x5f, 0xad, 0xf6, 0xe1, 0x1d, 0x1d, 0xd7, 0x26, 0x38, 0x7d, 0xba, 0x9f, 0xd5, 0x9b, 0x19, 0x30, 0x0e, 0x46, - 0x5a, 0xe6, 0xa2, 0xd0, 0xc9, 0x9b, 0xec, 0x42, 0x74, 0x1b, 0x0d, 0x66, 0x42, 0x1c, 0x11, 0x88, 0x67, 0x06, 0x1e, - 0x79, 0xf0, 0xc7, 0x46, 0x2d, 0x52, 0xcc, 0xc6, 0x7e, 0x83, 0xa0, 0xd4, 0xb5, 0x84, 0xd5, 0x4a, 0xd9, 0xd8, 0x22, - 0x26, 0xc7, 0x46, 0x19, 0x29, 0xfb, 0x29, 0x83, 0x98, 0x56, 0x66, 0x1c, 0xdc, 0x6d, 0xf5, 0xb7, 0xd5, 0x7e, 0xde, - 0xe3, 0xf6, 0x1a, 0x8f, 0x1b, 0x8f, 0x7d, 0x03, 0xa8, 0xe5, 0xc6, 0x06, 0xb7, 0x16, 0xe6, 0xb1, 0x35, 0x87, 0x65, - 0x9b, 0x10, 0x14, 0xa5, 0x87, 0xba, 0xbd, 0xb9, 0xf5, 0x11, 0xfb, 0x94, 0x99, 0x93, 0x42, 0xba, 0x0f, 0x72, 0xf4, - 0x80, 0x40, 0xe7, 0xf6, 0x67, 0x45, 0x17, 0x2a, 0x99, 0xb8, 0x1c, 0xe3, 0x2f, 0xc1, 0x6d, 0x5e, 0x3f, 0xba, 0xbe, - 0x36, 0x9b, 0xfc, 0xfa, 0x3a, 0xc2, 0xa1, 0x59, 0x77, 0x14, 0xf0, 0x82, 0xd1, 0xa0, 0x0c, 0xea, 0x64, 0x36, 0x7e, - 0xb3, 0x5d, 0x35, 0xf6, 0x9e, 0x56, 0x78, 0x07, 0xcb, 0x63, 0x1a, 0xdf, 0x72, 0x83, 0xec, 0x73, 0x80, 0x37, 0xeb, - 0xf3, 0x41, 0xf7, 0x4d, 0xac, 0xd0, 0xc1, 0xc1, 0x9b, 0x58, 0xa2, 0xde, 0x15, 0x33, 0x77, 0x6e, 0xe0, 0x07, 0xdd, - 0xe7, 0x66, 0xf8, 0x32, 0x40, 0x80, 0x2b, 0xb6, 0x29, 0xd9, 0xbc, 0x35, 0x51, 0x27, 0x52, 0x88, 0x6a, 0x0d, 0xb1, - 0x75, 0x1d, 0x48, 0xa0, 0xd7, 0x37, 0x21, 0xb4, 0xbb, 0x8c, 0x30, 0x60, 0xe1, 0x4b, 0x2f, 0x35, 0x96, 0xcc, 0x58, - 0x31, 0x61, 0xc5, 0x6a, 0xf5, 0x9e, 0x5a, 0xcf, 0xb3, 0x8d, 0x20, 0x89, 0xaa, 0xdb, 0x68, 0x50, 0x33, 0x7e, 0x10, - 0x1f, 0xe8, 0x00, 0xef, 0xbf, 0x89, 0x0b, 0x84, 0xc0, 0xc2, 0x88, 0x8b, 0x85, 0xf7, 0xb2, 0xca, 0x6a, 0xeb, 0x52, - 0xa0, 0xb2, 0x91, 0x9c, 0xb4, 0xf0, 0x94, 0x64, 0xe5, 0x1a, 0x5d, 0x4c, 0xbb, 0x8d, 0x46, 0x8e, 0x64, 0x9c, 0xf5, - 0xf3, 0x01, 0xe6, 0xb8, 0x80, 0xcb, 0xd4, 0xed, 0x75, 0x98, 0xb3, 0x1a, 0xe5, 0x72, 0xf3, 0x5d, 0xda, 0xb1, 0xa6, - 0x8f, 0xe8, 0x3a, 0x00, 0xc6, 0x23, 0x1a, 0x10, 0x89, 0x5d, 0x40, 0x16, 0x16, 0xc8, 0xca, 0x03, 0x59, 0x18, 0x20, - 0x2b, 0xd4, 0x9b, 0x43, 0xb8, 0x20, 0x85, 0xd2, 0x2d, 0x8a, 0x5e, 0x0f, 0x6c, 0xe9, 0x9c, 0x26, 0x30, 0x37, 0xb1, - 0x15, 0xdc, 0x72, 0x80, 0x03, 0x85, 0xf3, 0xc3, 0x53, 0x64, 0x19, 0x45, 0x26, 0xc6, 0x2b, 0xbe, 0x35, 0x7f, 0x92, - 0x5b, 0x7c, 0x67, 0x7f, 0xdc, 0x05, 0xca, 0xa4, 0xe7, 0x35, 0x6d, 0x03, 0x77, 0x11, 0xd1, 0xa2, 0x24, 0x02, 0xb4, - 0x76, 0xe1, 0xfd, 0x44, 0xfd, 0xc5, 0x33, 0x65, 0x03, 0x31, 0x88, 0x06, 0x51, 0x58, 0x04, 0xa4, 0xf3, 0xcf, 0x3f, - 0x23, 0xd4, 0x13, 0x10, 0x47, 0xc7, 0x9d, 0x6c, 0xcd, 0x36, 0x6a, 0x44, 0x49, 0x94, 0xc6, 0x3e, 0x4c, 0x03, 0xec, - 0x8c, 0x28, 0x0a, 0x5e, 0x3b, 0x29, 0x87, 0xf1, 0xa1, 0x36, 0x0c, 0x33, 0xa8, 0x2a, 0xf0, 0xc4, 0xe5, 0x72, 0x33, - 0xcc, 0x8f, 0x81, 0xaa, 0x30, 0x31, 0x56, 0x90, 0x7d, 0x02, 0x8c, 0x11, 0x76, 0x70, 0xc0, 0xfa, 0x62, 0x10, 0xbc, - 0xe9, 0x55, 0x5d, 0x87, 0xeb, 0x70, 0xe1, 0x62, 0x0a, 0x71, 0xd6, 0x57, 0x2b, 0xfb, 0x97, 0x7c, 0x30, 0xd2, 0x0c, - 0x3c, 0xce, 0x16, 0x9c, 0xb1, 0x62, 0xb7, 0x2c, 0x96, 0x68, 0xf9, 0x3b, 0x98, 0xed, 0xb9, 0xa8, 0x79, 0xdc, 0x4d, - 0xb5, 0xed, 0xa1, 0x3e, 0x37, 0x1a, 0x85, 0x20, 0x66, 0x6d, 0x75, 0xa4, 0xe1, 0xb9, 0x0e, 0xf3, 0x6a, 0xb1, 0x67, - 0x33, 0x55, 0x86, 0x10, 0x85, 0x23, 0x25, 0x01, 0xbb, 0x6d, 0x43, 0x27, 0xe1, 0x47, 0x9d, 0x4a, 0x3a, 0x16, 0x12, - 0xa0, 0xc0, 0x91, 0xb9, 0x9c, 0x37, 0x21, 0xe2, 0x19, 0xda, 0x41, 0xe4, 0x02, 0x13, 0x9a, 0xba, 0x6c, 0xe9, 0x62, - 0x39, 0x45, 0x33, 0xb9, 0x50, 0x6c, 0x31, 0x87, 0xf3, 0xbd, 0x4c, 0xcb, 0x72, 0x9e, 0x7d, 0xae, 0xa7, 0x80, 0xfd, - 0xe5, 0xad, 0x9e, 0x31, 0xb1, 0x88, 0xdc, 0x3c, 0xbf, 0x5a, 0x71, 0xff, 0xcd, 0x0b, 0xfc, 0x88, 0x74, 0x0e, 0xbf, - 0xe2, 0x8f, 0x94, 0x3c, 0x6a, 0x7c, 0xc5, 0x13, 0x4e, 0x2c, 0x6f, 0x90, 0xbc, 0x79, 0x7d, 0xf5, 0xe2, 0xdd, 0x8b, - 0xf7, 0x4f, 0xaf, 0x5f, 0xbc, 0x7a, 0xf6, 0xe2, 0xd5, 0x8b, 0x77, 0x1f, 0xf1, 0x3f, 0x94, 0x7c, 0x3d, 0x6a, 0x9f, - 0xb7, 0xf0, 0x07, 0xf2, 0xf5, 0xa8, 0x83, 0x6f, 0x35, 0xf9, 0x7a, 0x74, 0x82, 0x73, 0x45, 0xbe, 0x1e, 0x76, 0x8e, - 0x8e, 0xf1, 0x42, 0xdb, 0x26, 0x73, 0x39, 0x69, 0xb7, 0xf0, 0x3f, 0xee, 0x0b, 0xc4, 0xfb, 0x6a, 0x16, 0x13, 0xb6, - 0x61, 0xfc, 0x60, 0xca, 0xd0, 0xa1, 0x32, 0x86, 0x28, 0x17, 0x01, 0x3a, 0x4d, 0x55, 0x88, 0x4e, 0x36, 0x88, 0x31, - 0xd8, 0x30, 0x02, 0x5a, 0x71, 0xe2, 0xda, 0xe1, 0x47, 0x6d, 0x76, 0x0c, 0xf4, 0x89, 0x97, 0xc2, 0x71, 0xa9, 0xc2, - 0x69, 0x3b, 0x2d, 0xc6, 0x38, 0x97, 0xb2, 0x88, 0x17, 0xc0, 0x08, 0x18, 0xad, 0x05, 0x3f, 0x2a, 0xa3, 0x25, 0x89, - 0x0b, 0xd2, 0xee, 0xb5, 0x53, 0x71, 0x41, 0x3a, 0xbd, 0x0e, 0xfc, 0x39, 0xed, 0x9d, 0xa6, 0xed, 0x16, 0x3a, 0x0c, - 0xc6, 0xf1, 0x47, 0x0d, 0xad, 0xfb, 0x03, 0xec, 0xba, 0x50, 0xff, 0x14, 0xda, 0xab, 0xf4, 0x84, 0x53, 0xc7, 0xb6, - 0xbb, 0xe2, 0x82, 0x19, 0x3d, 0x2c, 0xff, 0x01, 0x50, 0xdb, 0x38, 0x74, 0x94, 0x1b, 0xc7, 0xfd, 0xe2, 0x47, 0x02, - 0xd5, 0x42, 0xb2, 0xc4, 0x6c, 0xd5, 0x42, 0xc0, 0x34, 0x9a, 0x6c, 0x30, 0x07, 0x4a, 0x94, 0x2c, 0xb4, 0x0f, 0x2b, - 0xaf, 0x9a, 0x12, 0x25, 0x73, 0x39, 0x8f, 0x6b, 0xaa, 0x86, 0x5f, 0x03, 0x33, 0xc7, 0x7d, 0xae, 0x5e, 0xd1, 0x57, - 0x71, 0x8d, 0xe7, 0x09, 0x59, 0xbb, 0x70, 0x5b, 0xfc, 0xe2, 0xac, 0x28, 0x6a, 0xe0, 0x2a, 0x01, 0xeb, 0x47, 0xd5, - 0xd4, 0x17, 0xf0, 0x7e, 0x1e, 0x6b, 0xe8, 0x4b, 0x12, 0x50, 0xcf, 0x9f, 0x4a, 0x33, 0xae, 0x52, 0x19, 0xed, 0x15, - 0xd1, 0xc6, 0x2c, 0xc8, 0x2b, 0xa2, 0x2f, 0x94, 0x01, 0x82, 0x24, 0xbc, 0x2f, 0x06, 0x70, 0xe0, 0xdb, 0x01, 0x4a, - 0x43, 0xe7, 0x40, 0xad, 0x54, 0x99, 0x09, 0x99, 0x4f, 0x13, 0x1c, 0x00, 0x34, 0x4f, 0x95, 0x0a, 0xca, 0x7c, 0x62, - 0x89, 0x82, 0xa1, 0xff, 0x16, 0x6e, 0x80, 0xc3, 0xd8, 0xa0, 0x62, 0x90, 0x7d, 0x4f, 0xd4, 0xf3, 0xdb, 0xe7, 0xad, - 0xa3, 0xaf, 0x41, 0xfe, 0x48, 0x79, 0x7b, 0x8f, 0xbf, 0x03, 0x4a, 0x6e, 0xc3, 0x59, 0xb5, 0xb1, 0x8f, 0x44, 0xd6, - 0x0d, 0x01, 0x72, 0xa8, 0xd1, 0x91, 0x79, 0x4a, 0xb0, 0x8b, 0xf4, 0x21, 0x69, 0xb7, 0x20, 0x7c, 0xd8, 0x0e, 0xca, - 0xf7, 0xd3, 0x06, 0x4c, 0x75, 0x72, 0xdb, 0x04, 0x5a, 0x0d, 0xaf, 0x0b, 0xdd, 0x35, 0x79, 0x72, 0x87, 0x55, 0x80, - 0x33, 0xec, 0x90, 0x35, 0xc4, 0xa1, 0x40, 0x2e, 0xec, 0xaa, 0xdd, 0x00, 0x9a, 0x8a, 0x8e, 0x7d, 0xe5, 0xce, 0x1b, - 0x47, 0x5d, 0x34, 0x93, 0xd3, 0xc3, 0xaf, 0x07, 0x07, 0xb1, 0x6c, 0x90, 0x47, 0x08, 0x2f, 0x29, 0x58, 0x31, 0x83, - 0xd7, 0x17, 0xb7, 0x4c, 0x7c, 0xaa, 0x02, 0xea, 0xb8, 0x50, 0xb5, 0x63, 0xad, 0xea, 0xac, 0xdc, 0x0d, 0x7e, 0x4c, - 0x1d, 0xd4, 0x08, 0xd2, 0xec, 0xe8, 0x3a, 0x35, 0x28, 0xd7, 0x5c, 0xb4, 0x60, 0x5b, 0x36, 0x3e, 0x52, 0xf4, 0xc3, - 0xa3, 0xe6, 0xd7, 0x60, 0xc2, 0x35, 0xd3, 0xa4, 0x47, 0x8d, 0x47, 0xe8, 0x87, 0x47, 0x81, 0x93, 0x1d, 0xaf, 0xd8, - 0x13, 0xcf, 0x8d, 0xfc, 0x64, 0xb9, 0xd2, 0x9f, 0x40, 0xb2, 0x2f, 0xc8, 0x4f, 0x80, 0xe5, 0x94, 0xfc, 0x14, 0xcb, - 0x26, 0x04, 0x1f, 0x24, 0x3f, 0xc5, 0x05, 0xfc, 0xc8, 0xc9, 0x4f, 0x31, 0x60, 0x3b, 0x9e, 0x9a, 0x1f, 0x45, 0x09, - 0x0c, 0x70, 0xec, 0x92, 0xd6, 0xbf, 0xab, 0x58, 0xad, 0xc4, 0xc1, 0x81, 0xb4, 0xbf, 0xe8, 0x65, 0x76, 0x70, 0x90, - 0x5f, 0x4c, 0xab, 0xbe, 0x99, 0xde, 0x45, 0x5f, 0x0c, 0x42, 0xe1, 0xc0, 0x34, 0x8d, 0x87, 0x33, 0xfe, 0x14, 0x52, - 0x56, 0xd3, 0x40, 0xf3, 0xb8, 0x73, 0xff, 0xec, 0x1c, 0xc3, 0xbf, 0xf7, 0x83, 0x82, 0x3f, 0x97, 0x7c, 0x17, 0x69, - 0xb3, 0xe6, 0x59, 0x85, 0x6c, 0x97, 0x01, 0x3e, 0x63, 0x86, 0x9a, 0xe2, 0xe0, 0x80, 0x5f, 0x04, 0xb8, 0x8c, 0x19, - 0x6a, 0x04, 0x16, 0x7b, 0x0f, 0x4b, 0x7b, 0x32, 0xc3, 0x35, 0xc1, 0xb3, 0xb2, 0xbc, 0x5f, 0x0c, 0x2e, 0xb4, 0xa3, - 0x26, 0x61, 0xf0, 0x69, 0x45, 0x5a, 0x6e, 0x93, 0x75, 0x45, 0x53, 0x5d, 0xb6, 0xbb, 0x48, 0x12, 0xd5, 0x10, 0x97, - 0x97, 0x6d, 0x0c, 0x2a, 0xf9, 0x9e, 0x22, 0x32, 0x15, 0xc4, 0x3b, 0xc8, 0x2d, 0x73, 0x99, 0x2a, 0x3c, 0xe5, 0xa9, - 0xf0, 0x72, 0xf6, 0x6b, 0x6f, 0x3d, 0x6d, 0x5c, 0x16, 0x4d, 0xcf, 0x0c, 0x8b, 0x9e, 0x2a, 0x5d, 0xed, 0x60, 0x93, - 0xaa, 0x01, 0xbc, 0xda, 0x57, 0x62, 0x1e, 0xb3, 0xfe, 0x65, 0x0c, 0xa2, 0x22, 0xab, 0x46, 0x1b, 0x32, 0xe1, 0x73, - 0x9d, 0x2a, 0x18, 0xa8, 0x29, 0x7c, 0x01, 0x64, 0x2a, 0xab, 0x0c, 0xb3, 0x7d, 0xc3, 0x50, 0x40, 0x40, 0x81, 0x4b, - 0xc2, 0x02, 0x09, 0x1e, 0x6e, 0x3f, 0x02, 0xc2, 0x51, 0x27, 0x17, 0x76, 0x72, 0x17, 0x0a, 0xba, 0x13, 0x83, 0x0b, - 0xdd, 0x45, 0xa2, 0xd1, 0x70, 0xdc, 0xf6, 0xa5, 0x30, 0x83, 0x68, 0xb6, 0x07, 0x97, 0xac, 0x8b, 0x54, 0xb3, 0x59, - 0x1a, 0x40, 0x5e, 0xb6, 0x56, 0x2b, 0x75, 0xe1, 0x1b, 0xe9, 0xf9, 0x73, 0xdc, 0xf0, 0x5d, 0x5e, 0xf0, 0xfc, 0x4d, - 0x92, 0x7e, 0x04, 0x54, 0x15, 0xf8, 0x6c, 0x39, 0x8f, 0x70, 0x64, 0x1e, 0x74, 0x83, 0xbf, 0xe6, 0x21, 0xae, 0x08, - 0x47, 0xee, 0x8d, 0xb7, 0x68, 0x50, 0x0d, 0x96, 0x67, 0x65, 0x78, 0x72, 0x9e, 0x5c, 0x03, 0xe3, 0xa0, 0xff, 0x56, - 0x68, 0x59, 0xfd, 0x4e, 0x72, 0x17, 0xa8, 0x43, 0xf9, 0x67, 0xc7, 0xdc, 0xa8, 0xd6, 0xbb, 0x5d, 0x23, 0x39, 0x8e, - 0x7c, 0x55, 0x08, 0xdf, 0xff, 0x9d, 0x77, 0x0f, 0xdb, 0xee, 0xb9, 0xed, 0x65, 0xd9, 0x03, 0x70, 0xde, 0xeb, 0x35, - 0xc2, 0xbf, 0xc9, 0x9d, 0x6f, 0xef, 0x46, 0xd7, 0x52, 0x3c, 0xa1, 0x9a, 0x46, 0x8d, 0x37, 0xc6, 0xf0, 0xcd, 0xca, - 0x59, 0xdd, 0x6f, 0x8d, 0x83, 0xfd, 0x5b, 0xdd, 0x43, 0xe8, 0x84, 0xda, 0x33, 0x41, 0x56, 0xf6, 0x35, 0x01, 0x33, - 0x64, 0x60, 0xfa, 0xb6, 0x03, 0x1e, 0x7e, 0x8c, 0x14, 0x1c, 0x7a, 0x2d, 0x9f, 0x44, 0x21, 0x26, 0x69, 0xcd, 0x8d, - 0x18, 0x52, 0x6c, 0x1f, 0xc6, 0xe5, 0x74, 0x8d, 0x42, 0xae, 0x7b, 0xac, 0xea, 0xc4, 0xb4, 0xea, 0xc6, 0x48, 0x1d, - 0x6c, 0x93, 0x05, 0x67, 0x55, 0xef, 0x46, 0x42, 0xa9, 0x5e, 0x54, 0x33, 0xaf, 0x62, 0x36, 0xdb, 0xe6, 0x99, 0x61, - 0xfb, 0xee, 0x9a, 0x02, 0x43, 0xde, 0xfd, 0x32, 0x5c, 0xd4, 0x25, 0x1c, 0xbb, 0x71, 0x00, 0x59, 0x49, 0x2e, 0x97, - 0xee, 0x4d, 0x34, 0xde, 0x97, 0x83, 0x75, 0xf9, 0x42, 0x5a, 0x80, 0x07, 0xd5, 0x48, 0x45, 0x16, 0x72, 0x06, 0xfe, - 0x79, 0xc1, 0x9a, 0x7e, 0x88, 0x7f, 0x85, 0x03, 0xbe, 0x42, 0xd2, 0xd4, 0xaa, 0x9f, 0xe0, 0xe5, 0x22, 0x50, 0x78, - 0xdb, 0xba, 0x9f, 0x64, 0xe8, 0x22, 0x5a, 0xd7, 0xa9, 0x58, 0x2f, 0xcc, 0xba, 0x62, 0xa5, 0x2c, 0x1c, 0x1c, 0x77, - 0x31, 0x5a, 0xa7, 0xce, 0x63, 0xd3, 0x3d, 0x77, 0xf4, 0x50, 0xf0, 0x99, 0x71, 0xa4, 0x7b, 0x56, 0x40, 0xfc, 0xaa, - 0x50, 0x9f, 0xf6, 0xb3, 0x0c, 0xdf, 0xf3, 0xed, 0xc3, 0x3d, 0x61, 0xc9, 0x73, 0x96, 0x6f, 0x50, 0xc3, 0x02, 0x29, - 0xa0, 0x50, 0x0a, 0x8b, 0xd5, 0x2a, 0x16, 0x26, 0xaa, 0x81, 0x0b, 0x6a, 0xeb, 0x5e, 0xaf, 0x30, 0xfa, 0x3b, 0xa8, - 0x8b, 0xbd, 0x7a, 0xc4, 0x98, 0xb0, 0xa2, 0xf0, 0xd2, 0x49, 0x65, 0x41, 0x5f, 0xbb, 0xfa, 0x10, 0xd5, 0x94, 0x7b, - 0xb1, 0xd1, 0xf7, 0xbe, 0xe3, 0x33, 0x26, 0x17, 0xf0, 0x6c, 0x10, 0x66, 0x44, 0x31, 0xed, 0xbf, 0x81, 0x82, 0xc0, - 0xdb, 0x33, 0x3c, 0xc4, 0x47, 0xe0, 0xab, 0x3c, 0xad, 0x93, 0x99, 0x7f, 0x8c, 0x22, 0x32, 0xc1, 0x22, 0xa3, 0x5e, - 0x04, 0x5e, 0x43, 0x20, 0x42, 0x11, 0x12, 0x31, 0x31, 0x8a, 0x7a, 0x91, 0x71, 0x52, 0x8a, 0xc0, 0x6a, 0x0c, 0x94, - 0xdc, 0x11, 0x9e, 0xab, 0x8a, 0x88, 0x85, 0x35, 0x75, 0x50, 0x89, 0xa5, 0xc6, 0x4c, 0xfb, 0xa8, 0x53, 0x81, 0xb0, - 0xc8, 0x36, 0x05, 0x65, 0xbd, 0xa1, 0x2e, 0xc0, 0x92, 0x18, 0xd3, 0x5b, 0x9e, 0x5c, 0x03, 0x37, 0xc7, 0x46, 0xae, - 0xe8, 0x92, 0x5f, 0x81, 0x7a, 0x3a, 0x2d, 0xf0, 0xb5, 0x61, 0xd8, 0x46, 0x29, 0x5d, 0x13, 0x8e, 0x33, 0x52, 0x24, - 0xf4, 0x16, 0xa2, 0x3a, 0xcc, 0xb8, 0x48, 0x73, 0x3c, 0xa3, 0xb7, 0xe9, 0x14, 0xcf, 0xb8, 0x78, 0x62, 0x97, 0x3d, - 0x1d, 0x41, 0x92, 0xff, 0x58, 0xac, 0x89, 0x79, 0x94, 0xea, 0x77, 0xc5, 0x8a, 0x47, 0xc0, 0xab, 0xa8, 0x18, 0x75, - 0x47, 0xc6, 0xa6, 0x9c, 0xe9, 0xca, 0x78, 0xfd, 0xb5, 0x8e, 0x29, 0xce, 0x70, 0x8e, 0x92, 0x5c, 0x62, 0xd6, 0x13, - 0xe9, 0x6b, 0x88, 0xe8, 0x9c, 0x61, 0xfb, 0xa0, 0x15, 0xbf, 0x65, 0xf9, 0x33, 0x59, 0xbc, 0x37, 0x5b, 0x3e, 0x47, - 0x50, 0x08, 0x5c, 0x54, 0x44, 0x13, 0x6e, 0xf7, 0x16, 0x3d, 0x59, 0x35, 0x45, 0x6f, 0x6d, 0x53, 0x6e, 0x88, 0x53, - 0x08, 0x85, 0x9b, 0x4c, 0x79, 0xa3, 0x8d, 0x59, 0xaf, 0xf5, 0x9d, 0x46, 0xa7, 0xa8, 0x2c, 0x89, 0x30, 0xac, 0x55, - 0x53, 0xa5, 0x92, 0x88, 0xa6, 0x72, 0x12, 0xde, 0xd2, 0x00, 0x3b, 0x55, 0x38, 0x93, 0x0b, 0xa1, 0x53, 0x19, 0xe0, - 0x0d, 0xad, 0x36, 0xd7, 0xf2, 0xd6, 0x42, 0x4c, 0xe3, 0x3b, 0xfb, 0x83, 0xe1, 0x6b, 0xa3, 0xe2, 0x7f, 0x0b, 0x86, - 0x3d, 0x2a, 0x15, 0x00, 0x3f, 0x30, 0x9c, 0x05, 0xc8, 0x59, 0x7e, 0xf2, 0x16, 0xc0, 0x67, 0x59, 0xc8, 0x3b, 0x48, - 0x65, 0x26, 0xf5, 0x0e, 0x52, 0x19, 0xa4, 0x1a, 0x5f, 0xee, 0x7d, 0x51, 0x29, 0x8b, 0xc2, 0x06, 0x89, 0xc2, 0xa5, - 0x3a, 0x58, 0x12, 0x91, 0x40, 0xbb, 0x46, 0x94, 0x9b, 0x71, 0x01, 0x41, 0xfd, 0xa0, 0x71, 0xfb, 0x4d, 0x6f, 0xe1, - 0xfb, 0xce, 0xe6, 0x33, 0x9f, 0x7f, 0x67, 0xf3, 0x4d, 0x47, 0x1e, 0xe3, 0xeb, 0xb7, 0x9d, 0xc6, 0x32, 0x5e, 0x3a, - 0xac, 0xfd, 0x50, 0x3e, 0xa1, 0xd2, 0x32, 0x4f, 0x55, 0x93, 0x36, 0x9e, 0x04, 0x48, 0xd9, 0xac, 0x78, 0xb8, 0x0e, - 0x6e, 0xb7, 0x0e, 0x63, 0xde, 0x24, 0x6d, 0x84, 0x0e, 0x9d, 0x70, 0x25, 0x62, 0x23, 0x39, 0x1d, 0x3e, 0x3a, 0x82, - 0xbb, 0x97, 0x99, 0xda, 0xf0, 0x95, 0xb2, 0xd5, 0x9a, 0xed, 0xd6, 0x21, 0xdf, 0x59, 0xa5, 0xd1, 0xc6, 0x33, 0x46, - 0x96, 0xe0, 0x5c, 0x46, 0x0b, 0xab, 0x6a, 0x00, 0x27, 0xce, 0x17, 0xe2, 0xb7, 0x05, 0x1d, 0x99, 0xef, 0x43, 0x9b, - 0xf2, 0x7a, 0xa1, 0x7d, 0x52, 0x93, 0xc3, 0x20, 0x3a, 0xc8, 0x95, 0x0c, 0x72, 0x62, 0x7e, 0x44, 0x92, 0x53, 0x74, - 0xd1, 0xee, 0x25, 0xa7, 0x87, 0xfc, 0x90, 0xa7, 0xc0, 0xc3, 0xc6, 0x4d, 0x5f, 0xa1, 0xd9, 0xf6, 0x75, 0x1e, 0x2f, - 0x86, 0x3c, 0x73, 0xcd, 0x57, 0x1d, 0x94, 0xa9, 0x76, 0x8e, 0x90, 0x05, 0x28, 0xe6, 0x7b, 0x09, 0xb2, 0xeb, 0xdd, - 0x1c, 0xf2, 0x14, 0xfa, 0x81, 0x5a, 0x1d, 0x5b, 0xab, 0x1c, 0xdc, 0x6f, 0x0b, 0x40, 0x30, 0xdf, 0x51, 0x6d, 0x2e, - 0x36, 0xbd, 0x19, 0x57, 0x9d, 0x1d, 0xf2, 0x6a, 0x84, 0x61, 0x99, 0xed, 0xfe, 0xfc, 0xd4, 0xaa, 0x2e, 0x0f, 0x03, - 0x88, 0xfc, 0xb6, 0xe0, 0x22, 0xec, 0x34, 0xec, 0xd6, 0xe5, 0x84, 0x9d, 0xd6, 0x67, 0x19, 0x14, 0xd9, 0xee, 0x75, - 0x6b, 0xa6, 0xf5, 0xd9, 0x5e, 0x81, 0x8f, 0x20, 0x4c, 0xca, 0xac, 0x74, 0x06, 0x57, 0xe8, 0x87, 0x1f, 0x90, 0x6b, - 0xfd, 0xf5, 0x42, 0xfb, 0xfc, 0x12, 0x11, 0x20, 0xbb, 0xea, 0xba, 0xac, 0x0e, 0x7d, 0x94, 0x4d, 0x7c, 0x3d, 0xe4, - 0xc1, 0xca, 0x3d, 0xbd, 0x9d, 0xcb, 0xd4, 0xe3, 0x6b, 0xaf, 0x95, 0x6e, 0x21, 0x27, 0x10, 0x0f, 0xd7, 0x5d, 0x58, - 0x16, 0xe4, 0xec, 0xe6, 0x16, 0x4a, 0x86, 0x13, 0xf7, 0xa5, 0x3f, 0x30, 0x7b, 0xdd, 0xc0, 0x2f, 0x92, 0x53, 0x98, - 0xfa, 0x66, 0x0f, 0x87, 0x1d, 0xe8, 0xc3, 0xc0, 0x61, 0xb3, 0x41, 0x9f, 0x59, 0x41, 0xe4, 0x31, 0x2f, 0x2c, 0x9e, - 0x5d, 0x92, 0x76, 0x8f, 0xa7, 0x6e, 0x33, 0x19, 0xd1, 0xa8, 0xdd, 0xe4, 0xc1, 0xcc, 0x00, 0xbf, 0x5c, 0xd9, 0xb0, - 0x88, 0x5f, 0xa7, 0x00, 0x4a, 0xbe, 0x58, 0xb5, 0x3e, 0x15, 0xbc, 0xea, 0x0d, 0xa7, 0x9b, 0xe9, 0x7e, 0xdd, 0xe0, - 0x76, 0xd7, 0xc3, 0x13, 0x9e, 0x40, 0xb1, 0x68, 0xed, 0x27, 0x3e, 0x01, 0x0e, 0x28, 0x69, 0xdd, 0x3f, 0x05, 0x17, - 0xca, 0x12, 0x96, 0xdb, 0xe5, 0x66, 0x5b, 0xe5, 0x2c, 0x1c, 0x6d, 0xc9, 0x80, 0x3b, 0xd8, 0x84, 0x28, 0x74, 0x70, - 0xd8, 0xc1, 0x49, 0xbb, 0xdd, 0x39, 0xc5, 0xc9, 0xc9, 0x29, 0x0c, 0xb4, 0x91, 0x9c, 0x1e, 0xce, 0x94, 0x05, 0x60, - 0x90, 0xb3, 0x76, 0xed, 0x3e, 0x82, 0x70, 0x49, 0xa1, 0x78, 0xcd, 0x0f, 0xe3, 0xb8, 0x9d, 0xdc, 0x6f, 0xb5, 0x4f, - 0xcf, 0x1b, 0x00, 0xa0, 0xa6, 0xfb, 0x70, 0x35, 0x5e, 0x2f, 0x74, 0xbd, 0x4a, 0x89, 0xf0, 0xf5, 0x6a, 0x0d, 0x5f, - 0xad, 0xd1, 0x5e, 0x57, 0x53, 0xf0, 0x55, 0x9d, 0x70, 0x6e, 0x8b, 0x78, 0xa5, 0x4d, 0xb8, 0x2d, 0x62, 0x3b, 0x90, - 0x18, 0xa4, 0xf3, 0xe4, 0xb4, 0x73, 0x8a, 0xec, 0x58, 0xb4, 0xc3, 0x8f, 0x72, 0x9f, 0x6c, 0x15, 0x69, 0x68, 0x40, - 0x92, 0x72, 0x76, 0x72, 0x01, 0x12, 0x35, 0x27, 0x97, 0xed, 0xe6, 0x8c, 0x25, 0x7e, 0x02, 0x26, 0x15, 0x96, 0xb3, - 0x5c, 0x05, 0x97, 0x14, 0x00, 0xe2, 0x02, 0x8c, 0x8b, 0xee, 0x9f, 0xf6, 0xee, 0x27, 0xa7, 0x67, 0x1d, 0x4b, 0xf4, - 0xf8, 0x45, 0xa7, 0x96, 0x66, 0xa6, 0x9e, 0x9c, 0x9a, 0x34, 0xe8, 0x3a, 0xb9, 0x7f, 0x0a, 0x65, 0x5c, 0x4a, 0x58, - 0x0a, 0xc2, 0x3c, 0x54, 0xc5, 0x20, 0xb6, 0x43, 0x5a, 0xcb, 0x3d, 0xab, 0x65, 0x9f, 0x9f, 0x1c, 0xdf, 0x3f, 0x0d, - 0xa1, 0x56, 0xce, 0xc2, 0x2c, 0xb4, 0x9b, 0x88, 0x9f, 0x1d, 0x2c, 0x2d, 0x3a, 0x4c, 0x4e, 0xd3, 0xad, 0x09, 0xda, - 0x4d, 0x73, 0x68, 0x70, 0x20, 0x50, 0x38, 0x3e, 0x15, 0x4e, 0x5f, 0x12, 0xdc, 0x8f, 0x55, 0x86, 0x26, 0xa1, 0xc2, - 0xd9, 0xdf, 0x53, 0x06, 0x2f, 0x39, 0x86, 0x57, 0x95, 0x8f, 0xa9, 0xf8, 0x42, 0xd5, 0x1b, 0x0a, 0xb1, 0x2b, 0xc4, - 0x20, 0x72, 0x91, 0xb5, 0xeb, 0xb9, 0x3f, 0x81, 0x8b, 0x30, 0x13, 0x70, 0xa1, 0xe9, 0x95, 0xa0, 0x15, 0x2f, 0x30, - 0x0c, 0x1d, 0x6a, 0xcd, 0xb0, 0x7a, 0x3c, 0x75, 0x26, 0x05, 0xa1, 0x6e, 0xeb, 0x39, 0xff, 0x5e, 0xb9, 0xa4, 0xbc, - 0xca, 0x4e, 0x4e, 0x51, 0xe2, 0x2e, 0xcb, 0x93, 0x36, 0x4a, 0x02, 0x13, 0x12, 0x77, 0x24, 0x67, 0x19, 0xe9, 0x47, - 0xb7, 0x11, 0x8e, 0xee, 0x22, 0x1c, 0x59, 0x1f, 0xe6, 0x0f, 0xe0, 0x3c, 0x1f, 0xe1, 0xc8, 0xba, 0x32, 0x47, 0x38, - 0xd2, 0x4c, 0x40, 0x48, 0xab, 0x68, 0x80, 0x73, 0x28, 0x6d, 0x3c, 0xab, 0xcb, 0xd2, 0x8f, 0xfd, 0x57, 0xe9, 0x7a, - 0x6d, 0x53, 0x02, 0x29, 0x73, 0x6a, 0x76, 0xa8, 0x7d, 0x92, 0x39, 0xa2, 0x9e, 0x59, 0x8f, 0x30, 0x08, 0x20, 0xf4, - 0xce, 0x3f, 0xe9, 0x56, 0x45, 0xc3, 0x60, 0xc7, 0xb0, 0xd2, 0xe0, 0x67, 0x1e, 0x85, 0x67, 0x58, 0x84, 0xc7, 0xc2, - 0x17, 0x06, 0xb1, 0xc2, 0xff, 0xce, 0xa5, 0x9c, 0xfb, 0xdf, 0x5a, 0x96, 0xbf, 0xe0, 0x21, 0x10, 0x67, 0xd1, 0x02, - 0x96, 0x5b, 0x36, 0xf8, 0xce, 0x90, 0xd5, 0x47, 0x70, 0x3d, 0x76, 0x01, 0xd2, 0x40, 0x22, 0xbc, 0x36, 0x02, 0x95, - 0x97, 0x0f, 0xaf, 0x6d, 0xb0, 0x1e, 0xf3, 0x09, 0xd1, 0xba, 0x20, 0x20, 0xaf, 0x84, 0x0b, 0x8d, 0x49, 0xc1, 0x94, - 0x8a, 0x6c, 0x14, 0xbb, 0x48, 0x0a, 0xff, 0x2c, 0xa1, 0x4f, 0x19, 0x8b, 0xc8, 0x74, 0x58, 0x9f, 0xad, 0x15, 0x87, - 0x73, 0x59, 0xa8, 0xd4, 0xbe, 0x51, 0xe2, 0xc1, 0x58, 0x3d, 0x55, 0x9f, 0xe6, 0xd9, 0x1a, 0xdb, 0x3b, 0xec, 0xb2, - 0x90, 0xbb, 0xd2, 0x0e, 0x4b, 0x65, 0xd9, 0xfa, 0x5b, 0x13, 0x52, 0xb5, 0x19, 0x05, 0x13, 0xad, 0x06, 0x54, 0x85, - 0xb2, 0x80, 0xc2, 0x36, 0x1c, 0x4a, 0xba, 0x2c, 0x4b, 0xa6, 0xcb, 0x72, 0x19, 0x4e, 0x5a, 0xad, 0xf5, 0x1a, 0x17, - 0xcc, 0x84, 0x65, 0xd9, 0x59, 0x02, 0xf2, 0xd5, 0x54, 0xde, 0x04, 0xb9, 0x2a, 0x2d, 0x67, 0x69, 0x96, 0x28, 0x0a, - 0x8c, 0x60, 0xa3, 0x35, 0xfe, 0xc2, 0x15, 0x07, 0x78, 0xba, 0xd9, 0x0d, 0xa5, 0xcc, 0x19, 0x85, 0xe8, 0x5d, 0x41, - 0x93, 0x6b, 0x3c, 0xe5, 0x23, 0xb6, 0xbb, 0x4d, 0x30, 0x63, 0xfe, 0xf7, 0x5a, 0xf4, 0x08, 0x64, 0xd9, 0x3d, 0x83, - 0x3a, 0xb0, 0x88, 0x2b, 0xe8, 0x20, 0x94, 0xc1, 0x47, 0x21, 0x6e, 0xe6, 0xf4, 0x4e, 0x2e, 0x34, 0xc0, 0x65, 0xa1, - 0xe5, 0x1b, 0x17, 0xeb, 0x60, 0xbf, 0x85, 0x7d, 0xd8, 0x83, 0x25, 0x84, 0x0c, 0x68, 0x61, 0x1b, 0x23, 0xa2, 0x85, - 0x87, 0x52, 0x6b, 0x39, 0x4b, 0x5b, 0xd8, 0x04, 0x6c, 0x68, 0xad, 0xcb, 0xa8, 0x5a, 0xd7, 0xe5, 0x63, 0x8e, 0xd5, - 0x26, 0x58, 0x38, 0xe9, 0x50, 0x13, 0x1d, 0xdc, 0x1e, 0x32, 0xc2, 0x1b, 0x3f, 0x5f, 0xbd, 0x7e, 0xe5, 0x62, 0x26, - 0xf3, 0x31, 0xb8, 0x6c, 0x3a, 0xd5, 0xd8, 0xb5, 0x79, 0x05, 0x29, 0xae, 0x14, 0xa5, 0x56, 0x38, 0x85, 0x96, 0x5f, - 0x08, 0x9d, 0x27, 0xf6, 0xf2, 0xe2, 0x99, 0x2c, 0x66, 0xd4, 0xde, 0x18, 0xe1, 0x6b, 0xe5, 0x9e, 0x3d, 0x37, 0x2f, - 0xab, 0x54, 0x93, 0x7c, 0xb7, 0x79, 0x15, 0xb1, 0xc8, 0x8c, 0xfc, 0x0a, 0xda, 0x00, 0x53, 0xb9, 0x7c, 0xb5, 0xb6, - 0x20, 0x2e, 0xf2, 0x7c, 0x40, 0x5e, 0xde, 0x5a, 0xea, 0x12, 0x45, 0x0d, 0x6e, 0xf0, 0x93, 0x15, 0x3c, 0x0b, 0xae, - 0x0b, 0x0d, 0x7b, 0xe4, 0xc4, 0x8b, 0xa8, 0x15, 0xd5, 0x5f, 0x7d, 0x35, 0xaa, 0x04, 0x1f, 0xb5, 0x34, 0xc9, 0x25, - 0x88, 0x1e, 0xe5, 0x03, 0x73, 0x1c, 0x44, 0x13, 0x7f, 0xf7, 0x7c, 0xd9, 0xf6, 0x74, 0x36, 0xaf, 0xd4, 0x89, 0xe5, - 0x95, 0x09, 0x78, 0x38, 0xda, 0x27, 0x5c, 0x10, 0x0e, 0x12, 0x59, 0xa9, 0x3d, 0xf4, 0xb9, 0xa8, 0x1b, 0xe7, 0x17, - 0x6d, 0xd6, 0x3c, 0x59, 0xad, 0xf2, 0xcb, 0x36, 0x6b, 0x9f, 0xda, 0x07, 0xdf, 0x22, 0x95, 0x01, 0xcd, 0xe5, 0x63, - 0x9e, 0x45, 0xa0, 0x9d, 0x1d, 0x67, 0x26, 0x9c, 0x82, 0x0f, 0x51, 0x4c, 0x16, 0xba, 0xea, 0x4b, 0x82, 0x71, 0x29, - 0xb1, 0x7a, 0xfc, 0x02, 0xf5, 0xda, 0xe9, 0xb6, 0xab, 0x74, 0xb3, 0x7d, 0x18, 0x5c, 0xb8, 0x14, 0x08, 0x77, 0x20, - 0xe4, 0x01, 0xe8, 0x77, 0x97, 0x02, 0x4c, 0x83, 0x00, 0x95, 0x15, 0x88, 0xb4, 0x7c, 0xb6, 0x98, 0x3d, 0x2b, 0xa8, - 0x59, 0x86, 0x27, 0x7c, 0xc2, 0xb5, 0x4a, 0x29, 0x48, 0xb7, 0xbb, 0xd2, 0xd7, 0xbb, 0x25, 0xa8, 0xac, 0x16, 0xf9, - 0x35, 0xd1, 0x3c, 0xfb, 0xac, 0xdc, 0xc2, 0x21, 0x6c, 0x56, 0x56, 0xe0, 0x0c, 0xad, 0x71, 0x2e, 0x27, 0xb4, 0xe0, - 0x7a, 0x3a, 0xfb, 0xb7, 0x56, 0x87, 0xf5, 0xf5, 0xc0, 0x5c, 0x58, 0x01, 0x48, 0xa8, 0x18, 0xad, 0x56, 0xfc, 0xe8, - 0xfb, 0xf7, 0x49, 0xde, 0x27, 0xbc, 0x8d, 0x3b, 0xf8, 0x18, 0x9f, 0xe2, 0x76, 0x0b, 0xb7, 0x4f, 0xe1, 0xea, 0x3e, - 0xcb, 0x17, 0x23, 0xa6, 0x62, 0x78, 0xf9, 0x4b, 0x5f, 0x26, 0xe7, 0x87, 0x65, 0xbc, 0x7b, 0x5d, 0x24, 0x0e, 0x5d, - 0x82, 0xb0, 0xeb, 0x2e, 0x5e, 0x5d, 0x14, 0x85, 0xc1, 0xd2, 0xc6, 0xa1, 0xea, 0xa4, 0xd4, 0x2f, 0x5c, 0x1e, 0xf7, - 0xc0, 0x9e, 0xdb, 0xae, 0x6c, 0x13, 0xcc, 0xbe, 0xed, 0xcf, 0xb4, 0xfa, 0xd9, 0xd4, 0x25, 0x62, 0x78, 0xe8, 0x55, - 0xe8, 0x81, 0x2e, 0x49, 0xfb, 0xe0, 0x00, 0xac, 0x8e, 0x82, 0xd9, 0x70, 0x1b, 0xfd, 0x80, 0x37, 0x6b, 0x69, 0x10, - 0xac, 0x00, 0x8c, 0x3b, 0xdf, 0x70, 0xb2, 0xb4, 0xb0, 0xd5, 0x40, 0x85, 0x75, 0x11, 0x46, 0x74, 0x0b, 0x49, 0x85, - 0x11, 0xa2, 0xe1, 0x08, 0x73, 0x91, 0x4e, 0xf6, 0x5b, 0x58, 0x8e, 0xc7, 0x8a, 0x69, 0x38, 0x3a, 0x0a, 0xf6, 0x85, - 0x15, 0xca, 0x9c, 0x22, 0x43, 0x36, 0xe1, 0xe2, 0xa1, 0xfe, 0xc4, 0x0a, 0x69, 0x3e, 0x8d, 0x06, 0x23, 0x8d, 0xcc, - 0x2a, 0x46, 0x38, 0xcb, 0xf9, 0x1c, 0xaa, 0x4e, 0x0a, 0x70, 0xfa, 0x81, 0xbf, 0x7c, 0x94, 0x86, 0x6d, 0x02, 0xf9, - 0xfa, 0x60, 0x83, 0xd9, 0xe0, 0x51, 0x41, 0x6f, 0x5e, 0x8b, 0xc7, 0xb0, 0xa3, 0x1e, 0x16, 0x8c, 0x42, 0x36, 0x24, - 0xbd, 0x83, 0xa6, 0xe0, 0x03, 0xda, 0x7c, 0x69, 0x00, 0x97, 0x9e, 0x9b, 0x0f, 0x5b, 0xd1, 0x47, 0x0d, 0x4c, 0xca, - 0xb6, 0x4c, 0xa6, 0x39, 0xa5, 0xab, 0x4c, 0x1b, 0x97, 0xa9, 0x9c, 0xc2, 0x1a, 0xbb, 0xa8, 0x27, 0xe1, 0x60, 0x46, - 0x54, 0x4d, 0xd3, 0xfe, 0xc0, 0xfc, 0x7d, 0x6d, 0x4b, 0xb6, 0xb0, 0x0b, 0xb5, 0xb3, 0xc6, 0xe6, 0xc9, 0xce, 0xa0, - 0x7c, 0x1b, 0xc3, 0x3d, 0x2c, 0xbc, 0x1b, 0x59, 0x23, 0x9f, 0x27, 0x9e, 0x6c, 0x9e, 0xac, 0xd7, 0x66, 0x20, 0x2a, - 0x05, 0x3d, 0xd0, 0x5b, 0xbf, 0x6d, 0x5a, 0xb0, 0x3d, 0xca, 0xaf, 0xd3, 0x16, 0x9e, 0x71, 0x78, 0x06, 0xd3, 0xb7, - 0x77, 0xa5, 0x0b, 0xf9, 0xd9, 0x81, 0xa4, 0x15, 0xa4, 0xd8, 0xe9, 0x04, 0x9d, 0x1d, 0xe3, 0x60, 0xe4, 0x40, 0xcf, - 0xaf, 0x3e, 0x5b, 0x58, 0xfb, 0xdf, 0x6f, 0xca, 0x82, 0x39, 0x1d, 0xb2, 0xbc, 0x9c, 0x50, 0xe6, 0xcf, 0xcf, 0x37, - 0x3c, 0xa9, 0x50, 0xc1, 0xbd, 0x1f, 0x05, 0x7b, 0xda, 0x86, 0x98, 0x9c, 0xd1, 0xbf, 0xed, 0x0f, 0x1b, 0x11, 0xa8, - 0xd4, 0xb2, 0x65, 0x85, 0x54, 0xea, 0xa1, 0x4d, 0xb3, 0x47, 0x0f, 0x1c, 0x91, 0x2f, 0xa1, 0x0b, 0xe0, 0xf5, 0x47, - 0x85, 0x9c, 0x1b, 0x44, 0x70, 0xbf, 0xdd, 0xb8, 0x8d, 0xaf, 0x00, 0x78, 0x3b, 0xec, 0x55, 0xff, 0xb4, 0x80, 0xfd, - 0x8d, 0xca, 0x92, 0x7e, 0xbc, 0x1d, 0x7b, 0xfc, 0x17, 0x12, 0xe2, 0x95, 0x5b, 0x3c, 0x4c, 0x1c, 0x3a, 0x95, 0xac, - 0x59, 0xf9, 0x73, 0xab, 0x24, 0x60, 0x58, 0xbd, 0x60, 0xc8, 0xc6, 0x6d, 0x15, 0xb7, 0x99, 0xff, 0x41, 0x05, 0x83, - 0x05, 0xdf, 0x1a, 0x49, 0xc5, 0xb2, 0xf8, 0xed, 0x53, 0xe7, 0xbf, 0xea, 0x1c, 0xd7, 0xbe, 0xae, 0xbd, 0x51, 0x39, - 0x34, 0xf1, 0x81, 0x23, 0x74, 0x70, 0xb0, 0x91, 0x41, 0xc7, 0x00, 0x78, 0xe4, 0xd8, 0x2f, 0xbf, 0x7c, 0x9e, 0x1d, - 0x33, 0x9a, 0xc7, 0x22, 0x0a, 0x99, 0x3b, 0xcf, 0xcd, 0xd9, 0x89, 0x3c, 0xa1, 0x6a, 0xea, 0x0b, 0x03, 0x1c, 0x1f, - 0x6d, 0xa5, 0x02, 0xbe, 0x47, 0xeb, 0x1d, 0x13, 0xd8, 0xe0, 0xb7, 0xec, 0xa4, 0x76, 0x15, 0xf4, 0x0b, 0xb4, 0xdc, - 0xc5, 0x54, 0x6e, 0x2c, 0x70, 0xb4, 0x39, 0x91, 0x9d, 0x43, 0xdf, 0xa8, 0x53, 0xb2, 0x1e, 0x4f, 0x76, 0x1b, 0x7d, - 0x49, 0xb1, 0x2b, 0xb9, 0xa2, 0x6d, 0x43, 0x56, 0xbd, 0x53, 0xab, 0x2b, 0x53, 0xa7, 0xea, 0x9a, 0xb7, 0xb2, 0xb4, - 0x29, 0xed, 0x92, 0xec, 0xdd, 0x16, 0x0b, 0xaf, 0xc2, 0x1b, 0x8d, 0xf2, 0x22, 0x14, 0xec, 0xb1, 0xc4, 0xa0, 0xcb, - 0x09, 0x5c, 0x2f, 0xac, 0x56, 0x31, 0xfc, 0xd9, 0x35, 0x86, 0x5d, 0xa6, 0x4b, 0x1f, 0xf8, 0x06, 0xbf, 0x12, 0x84, - 0xca, 0x75, 0x76, 0x90, 0x60, 0xdd, 0xe5, 0x06, 0x0d, 0xc7, 0x89, 0xff, 0x82, 0x87, 0x9a, 0xb5, 0x77, 0x39, 0x98, - 0x64, 0xdf, 0x78, 0xdc, 0xad, 0x64, 0x2d, 0x6b, 0x71, 0xd6, 0x37, 0x24, 0x18, 0x62, 0x37, 0xa5, 0x73, 0xdc, 0x4a, - 0xda, 0x28, 0x72, 0xc5, 0x2a, 0xf4, 0xff, 0x56, 0x91, 0xcc, 0x66, 0xfe, 0xd7, 0xd9, 0xd9, 0x99, 0x4b, 0x71, 0x36, - 0x7f, 0xca, 0x78, 0xc0, 0x99, 0x04, 0xf6, 0x85, 0x67, 0xcc, 0xe8, 0x90, 0xdf, 0xc2, 0x50, 0x88, 0x20, 0x97, 0xc2, - 0xb1, 0x4b, 0xf0, 0xce, 0x20, 0x50, 0x1e, 0x60, 0xff, 0x9e, 0x6c, 0x94, 0xf3, 0x0f, 0x15, 0xf9, 0x40, 0xbe, 0x65, - 0x83, 0xec, 0x8b, 0xf9, 0xec, 0x5b, 0x33, 0x19, 0x88, 0xcd, 0x1f, 0x61, 0xfb, 0xdb, 0xb0, 0xb4, 0xce, 0x52, 0x06, - 0x47, 0x5a, 0x2e, 0xb2, 0xa9, 0xd5, 0xfc, 0xbb, 0x0f, 0x53, 0xd6, 0x3d, 0x72, 0x03, 0x41, 0xb9, 0xc8, 0xd2, 0xc5, - 0xa3, 0x8c, 0x7e, 0x2c, 0x43, 0x4f, 0xee, 0xbd, 0x62, 0x0b, 0xf6, 0x23, 0xde, 0xab, 0x52, 0xe0, 0xe3, 0x61, 0xc1, - 0x69, 0xfe, 0x23, 0xde, 0xab, 0x42, 0x50, 0x82, 0x2b, 0xa4, 0x89, 0xe2, 0x88, 0xcd, 0x83, 0xce, 0x69, 0x24, 0x80, - 0x82, 0xe6, 0x91, 0x39, 0xc8, 0x9e, 0xbb, 0xa8, 0x85, 0x49, 0x07, 0xbb, 0xb8, 0x5f, 0x36, 0x16, 0xa9, 0x0d, 0xe1, - 0x0d, 0x91, 0xdc, 0xca, 0xd9, 0x98, 0xaf, 0x47, 0x1b, 0x0b, 0x62, 0x94, 0xc9, 0xe4, 0xf2, 0x39, 0x8f, 0xb7, 0x16, - 0x0b, 0x85, 0xd5, 0x82, 0x05, 0xaa, 0x55, 0xa9, 0xd2, 0xc3, 0xe2, 0xdb, 0x05, 0xb3, 0xa0, 0x88, 0xd9, 0x7a, 0x0f, - 0x6f, 0xb9, 0x22, 0x20, 0x25, 0xbb, 0x24, 0x78, 0x93, 0xdb, 0x60, 0xaa, 0x7f, 0x82, 0x1d, 0x08, 0x3d, 0x53, 0x3a, - 0xc2, 0x26, 0x4f, 0x41, 0x24, 0xb1, 0xfd, 0x16, 0x76, 0xac, 0xd1, 0x0b, 0xe1, 0x85, 0x14, 0x38, 0x57, 0x4d, 0x13, - 0x33, 0xca, 0x4d, 0x74, 0xb1, 0x87, 0x6a, 0xce, 0x32, 0x6d, 0x11, 0x60, 0xdf, 0xa1, 0xa1, 0x14, 0xcf, 0x0d, 0x28, - 0xcc, 0x63, 0xd2, 0x2e, 0xe5, 0x31, 0x2c, 0x5e, 0x90, 0x02, 0x44, 0x8d, 0x8b, 0x49, 0x59, 0x67, 0x9e, 0x2f, 0x26, - 0x5c, 0x54, 0xc8, 0x50, 0x30, 0x35, 0x97, 0x02, 0xde, 0x72, 0x28, 0x8b, 0x18, 0x3a, 0x54, 0xc3, 0x77, 0x4b, 0xc2, - 0xca, 0x3a, 0xe6, 0x98, 0xe2, 0xa2, 0xaa, 0x01, 0xcc, 0xc5, 0xc3, 0xf0, 0xd1, 0x77, 0xf5, 0x5a, 0xbc, 0x93, 0xf3, - 0x2a, 0xdf, 0xd3, 0x38, 0x1f, 0x32, 0xdd, 0xd9, 0x0d, 0xa3, 0xb5, 0x79, 0x6e, 0x29, 0xd8, 0xbe, 0x1f, 0x78, 0xf5, - 0x04, 0xd9, 0xda, 0x3c, 0xd8, 0x54, 0x66, 0x0d, 0x59, 0xf9, 0x3a, 0x41, 0xd5, 0x5e, 0xbd, 0xaa, 0x14, 0xb6, 0x22, - 0x40, 0xa5, 0xe0, 0xa3, 0xad, 0xfc, 0x27, 0xda, 0xe6, 0xdb, 0x73, 0xa8, 0x0c, 0x0f, 0xe4, 0xc9, 0x50, 0xd5, 0x03, - 0x2e, 0xca, 0x0f, 0x01, 0x2c, 0x7e, 0x64, 0x22, 0xd7, 0xee, 0xba, 0x40, 0xe6, 0x4c, 0xc5, 0x12, 0x2f, 0xfb, 0x74, - 0x90, 0x5a, 0x79, 0x28, 0x95, 0x60, 0xdb, 0x73, 0x53, 0x70, 0xed, 0x43, 0xe4, 0xe2, 0x3e, 0x1b, 0xa4, 0xcb, 0x7a, - 0x18, 0x5d, 0x1b, 0xc8, 0xd7, 0x9b, 0x73, 0x9a, 0xb8, 0xb3, 0x74, 0x80, 0x73, 0x02, 0xb6, 0xc7, 0x9e, 0x3d, 0x7d, - 0x13, 0x67, 0xa8, 0x57, 0xe7, 0xf0, 0x97, 0x6b, 0x9c, 0xe3, 0x0c, 0xa5, 0x0f, 0x63, 0xb8, 0xc0, 0x5a, 0x63, 0x00, - 0x5f, 0x66, 0x49, 0x15, 0x78, 0xa4, 0x66, 0x46, 0x62, 0x75, 0x17, 0x81, 0x68, 0xa9, 0xc3, 0xdb, 0x71, 0xe6, 0x03, - 0x51, 0x1b, 0xee, 0xf5, 0x99, 0x11, 0x0e, 0x27, 0x59, 0x5c, 0x3b, 0x67, 0x38, 0xb9, 0xdc, 0xe7, 0xb5, 0x13, 0x13, - 0xac, 0xbd, 0xc3, 0x53, 0x05, 0xf4, 0x68, 0x70, 0xaa, 0x58, 0x1a, 0x02, 0x31, 0x13, 0xc0, 0x9b, 0x39, 0x3c, 0xda, - 0x02, 0x9c, 0x8f, 0xd6, 0x38, 0xf8, 0x4a, 0x6b, 0x5d, 0x6d, 0x2a, 0x51, 0xd6, 0x6b, 0xdc, 0x9f, 0x66, 0x78, 0x94, - 0xe1, 0x79, 0x36, 0x08, 0x8e, 0x9b, 0x59, 0x16, 0x9a, 0x74, 0xad, 0x56, 0x4f, 0x9d, 0x19, 0x21, 0xb2, 0x3f, 0x2d, - 0xfd, 0x41, 0x3d, 0x40, 0xf8, 0x14, 0xb2, 0x80, 0x96, 0xf4, 0xdc, 0xdf, 0x86, 0x7d, 0xa7, 0x1a, 0x35, 0x62, 0x9e, - 0x58, 0x32, 0xd2, 0xf3, 0x3f, 0xca, 0x2c, 0xdb, 0x5a, 0x23, 0x9a, 0xdf, 0xee, 0x45, 0x0d, 0xdf, 0x5e, 0xa0, 0x65, - 0x2b, 0xcd, 0x76, 0x00, 0x51, 0xac, 0x71, 0x92, 0x0e, 0xd6, 0x48, 0xae, 0x56, 0xb1, 0x4d, 0x21, 0x3c, 0x99, 0x31, - 0xaa, 0x16, 0x85, 0x79, 0xba, 0x2d, 0x56, 0x28, 0x31, 0xfc, 0x2e, 0x76, 0x36, 0xa2, 0xf0, 0x52, 0x9a, 0x04, 0xc3, - 0x8d, 0x58, 0x10, 0x59, 0x13, 0xb9, 0x87, 0x59, 0x65, 0x19, 0x24, 0x88, 0x30, 0x22, 0xbf, 0xbd, 0x2e, 0x15, 0xf6, - 0x71, 0x38, 0xfb, 0xc7, 0xf8, 0x02, 0xc2, 0xcd, 0xdb, 0x84, 0x16, 0x43, 0x3a, 0x01, 0x36, 0x16, 0xe2, 0x10, 0x6e, - 0x25, 0xac, 0x56, 0xfd, 0x41, 0x57, 0x18, 0xf2, 0xec, 0x9e, 0xae, 0x2b, 0x1b, 0xda, 0xdd, 0x00, 0x5c, 0x75, 0x5b, - 0x6a, 0xae, 0x8d, 0xee, 0x87, 0x9a, 0xd7, 0xb5, 0xb8, 0x4b, 0x72, 0x0f, 0x64, 0x54, 0x6f, 0x60, 0xd7, 0x2c, 0xc0, - 0x4d, 0xe8, 0x2a, 0x3c, 0xc2, 0x0b, 0x6b, 0xc3, 0x69, 0x9e, 0x52, 0xa2, 0xe6, 0x05, 0x25, 0x78, 0xb8, 0x99, 0xb0, - 0x7e, 0x36, 0xc0, 0x23, 0x1f, 0x68, 0x7b, 0xff, 0x6d, 0x3c, 0x42, 0xa8, 0x20, 0x06, 0xa6, 0xd6, 0x65, 0x7b, 0x54, - 0xd9, 0xed, 0x9b, 0x4c, 0xc3, 0x30, 0x18, 0x23, 0xe6, 0x51, 0x68, 0xc4, 0x9c, 0x37, 0x1a, 0x68, 0x41, 0x46, 0x60, - 0xc4, 0xbc, 0x08, 0x5a, 0x5b, 0xd8, 0x67, 0x36, 0x83, 0xf6, 0x16, 0x08, 0x75, 0x39, 0xd0, 0x34, 0x0d, 0x0f, 0x6a, - 0x54, 0x0f, 0x9a, 0xfb, 0xe7, 0x9d, 0x8e, 0x3a, 0xa0, 0x48, 0x18, 0x5f, 0xfa, 0x49, 0x58, 0xd7, 0x70, 0x3b, 0xee, - 0xb1, 0x19, 0xb7, 0xb3, 0x6d, 0x50, 0x7d, 0xd9, 0xcf, 0x06, 0x83, 0xae, 0xf4, 0x56, 0x12, 0x2d, 0x3c, 0xae, 0x9e, - 0xe0, 0xa8, 0x16, 0xef, 0x8b, 0xde, 0xbc, 0xf2, 0xe6, 0xfe, 0x65, 0xcf, 0xcd, 0xf3, 0x18, 0x38, 0xa0, 0x7d, 0xb8, - 0x1f, 0xaa, 0xe2, 0x83, 0x1d, 0x75, 0x20, 0x0a, 0x5a, 0xda, 0xaa, 0x09, 0xa4, 0xd6, 0xcc, 0x2e, 0xd6, 0x4d, 0x85, - 0x0e, 0x05, 0x84, 0x21, 0x53, 0x55, 0x77, 0x77, 0x2a, 0x50, 0x0d, 0x71, 0x38, 0xf5, 0x1f, 0x5b, 0x23, 0xd6, 0x38, - 0xea, 0x8c, 0x22, 0x63, 0x24, 0x69, 0x97, 0x0f, 0x5e, 0xdd, 0x01, 0x2b, 0x01, 0x1f, 0xfd, 0xd8, 0x24, 0x19, 0x43, - 0x82, 0xb7, 0x2c, 0xd3, 0x86, 0x0f, 0xe1, 0x0e, 0x41, 0x79, 0x62, 0x63, 0x6d, 0xba, 0x4a, 0x16, 0x72, 0x55, 0x97, - 0xd7, 0x01, 0x7a, 0xde, 0x95, 0xbf, 0xb1, 0xe1, 0xc8, 0x82, 0x81, 0x65, 0x5b, 0xfb, 0x04, 0x3c, 0xf2, 0x71, 0x85, - 0x20, 0x7e, 0x29, 0x74, 0x62, 0x22, 0x45, 0x5f, 0xc1, 0x06, 0xc5, 0x73, 0x70, 0x10, 0x74, 0x12, 0x1c, 0x06, 0xef, - 0x32, 0xab, 0x49, 0x36, 0xb8, 0x35, 0x23, 0xf1, 0x7c, 0xb5, 0x6a, 0xa1, 0xc3, 0x7f, 0xcc, 0x63, 0xc8, 0xe3, 0x52, - 0xe1, 0x3e, 0xae, 0x14, 0xee, 0x60, 0x09, 0x48, 0xc6, 0x81, 0xae, 0x1d, 0xcb, 0x50, 0x8d, 0x0e, 0x71, 0xba, 0x5f, - 0x40, 0xd4, 0x66, 0x77, 0x2c, 0x81, 0x9e, 0x7d, 0xab, 0x80, 0xd5, 0xb5, 0x97, 0x25, 0x90, 0x11, 0xdc, 0xfd, 0x26, - 0x30, 0x2a, 0x44, 0xe3, 0xf3, 0x67, 0xde, 0x53, 0xe0, 0x89, 0xf3, 0xe7, 0x9a, 0x19, 0xd6, 0xbd, 0xa0, 0x37, 0xa6, - 0xf9, 0x78, 0x8c, 0x9b, 0x63, 0x0b, 0xce, 0xa3, 0x0e, 0xfc, 0xb4, 0x10, 0x3d, 0xea, 0x60, 0x97, 0x8a, 0xc7, 0x25, - 0x90, 0x43, 0xf4, 0x74, 0x06, 0x52, 0xc0, 0x4a, 0xc7, 0x56, 0x8b, 0x34, 0x41, 0xab, 0xd5, 0xe4, 0x82, 0xb4, 0x10, - 0x5a, 0xaa, 0x1b, 0xae, 0xb3, 0x29, 0xf8, 0x48, 0x83, 0x62, 0xe0, 0x0d, 0xd5, 0xd3, 0x18, 0xe1, 0x31, 0x5a, 0x8e, - 0xd8, 0x98, 0x2e, 0x72, 0x9d, 0xaa, 0x1e, 0x4f, 0x6c, 0x28, 0x5b, 0x66, 0x23, 0xc1, 0x1d, 0x75, 0xf0, 0xc4, 0xf0, - 0x97, 0x8f, 0x8c, 0x39, 0x48, 0x91, 0x99, 0xe4, 0x89, 0x49, 0xc0, 0x3c, 0xc9, 0x72, 0xa9, 0x98, 0x6d, 0xa6, 0x6b, - 0x6d, 0xcb, 0x21, 0x18, 0x76, 0xa4, 0x0b, 0x6e, 0xac, 0x28, 0xa3, 0x74, 0x4a, 0x54, 0x4f, 0x1d, 0x75, 0xd2, 0x09, - 0xe6, 0x09, 0x70, 0x7a, 0xef, 0x64, 0xcc, 0x1a, 0xe5, 0xad, 0xe8, 0x0c, 0x1d, 0x4e, 0xb1, 0xa8, 0x2e, 0x51, 0x67, - 0xe8, 0x70, 0x82, 0xf0, 0xac, 0x41, 0x72, 0x05, 0x1e, 0xc3, 0x5c, 0xfc, 0x1f, 0x29, 0xff, 0xcd, 0x61, 0x43, 0xd0, - 0xe5, 0xb7, 0xb0, 0x53, 0xd8, 0x28, 0x4a, 0x73, 0x02, 0x5e, 0x8b, 0xed, 0x33, 0x9c, 0x91, 0x49, 0x33, 0xf7, 0x01, - 0xf7, 0x4c, 0x2b, 0x8d, 0x5b, 0x8d, 0x0e, 0x33, 0x3c, 0xda, 0x4c, 0x8a, 0xcd, 0x5c, 0x9b, 0x79, 0x9a, 0xc1, 0xf9, - 0x5e, 0x8d, 0xc2, 0x95, 0x5f, 0x6c, 0x26, 0x85, 0xe5, 0x1d, 0x70, 0x9b, 0x23, 0x2c, 0x9a, 0x14, 0xe7, 0x78, 0xd6, - 0xfc, 0x8a, 0x67, 0xcd, 0x0f, 0x65, 0x46, 0x63, 0x81, 0x05, 0x04, 0xef, 0x83, 0x44, 0x3c, 0xab, 0x92, 0x47, 0x58, - 0x34, 0x4c, 0x79, 0x3c, 0x6b, 0x54, 0xa5, 0x9b, 0x0b, 0x2c, 0x1a, 0xa6, 0x74, 0xe3, 0x03, 0x9e, 0x35, 0xbe, 0xfe, - 0x8b, 0x49, 0x47, 0x29, 0xa0, 0xcb, 0x1c, 0x2d, 0x33, 0x3b, 0xc4, 0xab, 0xdf, 0xde, 0xbe, 0x6b, 0x5f, 0x77, 0x0e, - 0x27, 0xd8, 0xaf, 0x5f, 0x66, 0x70, 0x2c, 0xd3, 0x31, 0x6b, 0x02, 0x44, 0x33, 0xdc, 0x39, 0x9c, 0xe2, 0xce, 0x61, - 0xe6, 0x9a, 0x5a, 0xcf, 0x1a, 0xe4, 0x56, 0x87, 0x50, 0xd4, 0x51, 0x1a, 0xc2, 0xc7, 0x4f, 0x36, 0x9d, 0xa0, 0x1a, - 0x28, 0xd1, 0xe1, 0xa4, 0x06, 0x2a, 0xf8, 0x5e, 0xd4, 0xbe, 0xab, 0x7a, 0x15, 0x06, 0x59, 0x28, 0xa1, 0x70, 0xcd, - 0x0d, 0x78, 0x6a, 0x29, 0x06, 0x32, 0x61, 0x8a, 0x05, 0xca, 0x77, 0x40, 0x61, 0x94, 0x27, 0x66, 0xe8, 0xc1, 0x74, - 0x4c, 0xe2, 0xff, 0xcf, 0x93, 0x29, 0x87, 0x5e, 0x6e, 0x99, 0xad, 0xe9, 0xb9, 0xc9, 0x84, 0xc3, 0x07, 0x1e, 0xeb, - 0xff, 0xda, 0x81, 0x62, 0x03, 0x52, 0xfc, 0x7f, 0xe9, 0xe8, 0x42, 0x30, 0x42, 0x56, 0x94, 0x16, 0x0e, 0xf1, 0xbf, - 0x3f, 0xac, 0xa0, 0xfb, 0x62, 0xab, 0xfb, 0xc2, 0x74, 0x1f, 0x36, 0x6d, 0x54, 0x39, 0x69, 0x55, 0xc9, 0x92, 0xff, - 0x3a, 0xdd, 0xda, 0x02, 0x8d, 0xa8, 0xd1, 0xb3, 0x49, 0xd8, 0xe0, 0x7e, 0x3b, 0xdd, 0x81, 0xcc, 0x6b, 0x6e, 0xdf, - 0xe6, 0x84, 0xc3, 0x37, 0xb8, 0x53, 0xbd, 0x6c, 0x81, 0xf7, 0xa6, 0x32, 0xfa, 0xca, 0x38, 0xb4, 0x1c, 0x2c, 0x36, - 0x4d, 0xb9, 0x8d, 0xb1, 0x74, 0x72, 0x8a, 0x8d, 0x2b, 0x22, 0x54, 0xba, 0xbd, 0x04, 0xa5, 0xf8, 0x58, 0x37, 0x99, - 0xf9, 0xba, 0xd0, 0x89, 0xb9, 0x84, 0x6a, 0x98, 0xcf, 0xbb, 0x4b, 0x9d, 0x68, 0x39, 0xb7, 0x79, 0x77, 0x17, 0xd0, - 0x27, 0x68, 0x58, 0x1b, 0x81, 0xdd, 0x3e, 0x2b, 0x9c, 0x7e, 0xa7, 0x3a, 0x04, 0xc3, 0x03, 0xc8, 0x91, 0x16, 0xdb, - 0x07, 0x36, 0xad, 0x61, 0xd7, 0x45, 0xb3, 0x4c, 0xb4, 0xad, 0x36, 0x4d, 0xae, 0xdd, 0xc3, 0x7c, 0x1e, 0xf2, 0x14, - 0xbc, 0xb0, 0xfa, 0xf1, 0x1d, 0xec, 0xc6, 0x6d, 0x8d, 0x91, 0xa8, 0x2b, 0x99, 0x4a, 0xe8, 0x27, 0xb7, 0x98, 0x25, - 0x77, 0xc6, 0x8b, 0x51, 0x19, 0x7f, 0x1f, 0x13, 0xa9, 0x3e, 0xaa, 0x24, 0x39, 0xb0, 0xec, 0x6f, 0xb0, 0xe4, 0x16, - 0xcc, 0x13, 0xcb, 0x6a, 0x12, 0xeb, 0xe4, 0x2e, 0x58, 0x44, 0x69, 0x1a, 0x59, 0x1b, 0x06, 0xd4, 0x34, 0x63, 0xd5, - 0x83, 0xfb, 0x10, 0xe8, 0xa1, 0x57, 0x96, 0xd2, 0xae, 0xb3, 0xb4, 0xd6, 0xbd, 0x36, 0xdd, 0x6f, 0x0e, 0x28, 0xe0, - 0x0b, 0x03, 0xae, 0xe9, 0x5f, 0x4d, 0x22, 0x19, 0xb2, 0xaf, 0x9c, 0x15, 0x8f, 0x17, 0x85, 0xc1, 0x34, 0xd1, 0xd3, - 0x49, 0x36, 0x6f, 0x83, 0xa9, 0x5e, 0x36, 0xef, 0xdc, 0x62, 0xf7, 0x7d, 0x67, 0xbf, 0xef, 0xb0, 0xe8, 0x31, 0x93, - 0x91, 0x32, 0x53, 0xcc, 0x7f, 0xdf, 0xd9, 0xef, 0x3b, 0xbc, 0x3d, 0x98, 0x1b, 0x7f, 0xa1, 0x58, 0xb2, 0x33, 0x5c, - 0x82, 0x09, 0x79, 0xc0, 0xdd, 0xd4, 0xb2, 0x4c, 0x10, 0xd8, 0x5a, 0x02, 0xc4, 0xf9, 0x7c, 0x1a, 0x57, 0xbc, 0x1a, - 0x02, 0xee, 0xd3, 0xbb, 0xb6, 0x57, 0xa9, 0xc0, 0x63, 0x82, 0x46, 0xc4, 0xc4, 0xb6, 0x31, 0xef, 0x6a, 0x01, 0x97, - 0x47, 0x74, 0xa9, 0x27, 0x49, 0x80, 0x57, 0x35, 0x2a, 0x6f, 0x53, 0xa4, 0xfc, 0x22, 0x41, 0x8e, 0x2f, 0xf6, 0x88, - 0x2a, 0x06, 0xb0, 0x2a, 0x4b, 0xfa, 0x04, 0x52, 0xcf, 0x0f, 0x26, 0xfa, 0x79, 0x13, 0x79, 0xec, 0x0b, 0xb3, 0x9f, - 0x99, 0x9e, 0x16, 0x72, 0x31, 0x99, 0x82, 0x0f, 0x2d, 0xb0, 0x0c, 0x85, 0xa9, 0x57, 0xd9, 0xfa, 0xd7, 0x24, 0x37, - 0x01, 0x14, 0x4e, 0x37, 0x65, 0x42, 0x33, 0xbd, 0xa0, 0xb9, 0xb1, 0x24, 0xe5, 0x62, 0xf2, 0x48, 0xde, 0xbe, 0x04, - 0xec, 0xa6, 0x44, 0x37, 0x76, 0xe4, 0xbd, 0x85, 0x1d, 0x80, 0x33, 0xc2, 0x76, 0x55, 0x7c, 0xa8, 0x40, 0xe7, 0x8f, - 0x73, 0xc2, 0x76, 0x55, 0x7d, 0xc2, 0x6c, 0xf6, 0x94, 0x6c, 0x0c, 0xb7, 0x17, 0x67, 0x8d, 0x1c, 0x1d, 0x75, 0xd2, - 0xbc, 0xeb, 0x89, 0x81, 0x05, 0x68, 0x00, 0xdc, 0xad, 0xed, 0x59, 0xde, 0xdd, 0x10, 0xd0, 0xbb, 0x64, 0xd2, 0x5e, - 0x97, 0x9b, 0x94, 0xd5, 0xaa, 0x53, 0x51, 0xc1, 0x02, 0x4f, 0x83, 0xbd, 0x40, 0xed, 0xd7, 0x0e, 0x8a, 0x73, 0x95, - 0x6d, 0x9a, 0x9e, 0x97, 0x7d, 0x77, 0x77, 0x2c, 0x32, 0xb6, 0x69, 0x6f, 0x77, 0x10, 0x09, 0xcb, 0x09, 0xeb, 0x80, - 0x13, 0xae, 0x6a, 0x07, 0x04, 0xe8, 0x3a, 0x10, 0xb9, 0xb1, 0x24, 0xcb, 0x75, 0x65, 0x74, 0x1f, 0xf8, 0xdd, 0x52, - 0x22, 0xdd, 0x68, 0x4b, 0x82, 0xe9, 0x13, 0x8c, 0x9a, 0xce, 0x3c, 0x8a, 0x5c, 0x7b, 0xef, 0x77, 0x53, 0xb4, 0xf5, - 0xaf, 0x0f, 0x63, 0xb3, 0x3d, 0x4c, 0x0c, 0x65, 0x10, 0x03, 0xbd, 0x8f, 0x78, 0xb7, 0xd1, 0xc8, 0x10, 0x28, 0x64, - 0xb2, 0x01, 0x96, 0x89, 0xd7, 0xa2, 0x1f, 0x1c, 0x18, 0x78, 0x54, 0x09, 0x08, 0x53, 0x10, 0x42, 0xc2, 0xae, 0x0d, - 0xc2, 0x86, 0xcb, 0x55, 0xcb, 0x85, 0x8d, 0x54, 0x1b, 0x3a, 0xf8, 0x7f, 0x85, 0xcb, 0x56, 0xcf, 0x2c, 0x17, 0xc5, - 0xe0, 0x66, 0x6e, 0xc0, 0x22, 0x41, 0x7a, 0xb4, 0xd9, 0x1e, 0x8a, 0xbb, 0x73, 0xb1, 0xd9, 0x10, 0x90, 0x98, 0xc3, - 0x04, 0x45, 0xc3, 0xb9, 0x31, 0xc6, 0x2a, 0xa9, 0xb4, 0xac, 0x35, 0x89, 0x39, 0xf0, 0xa5, 0x0b, 0xd7, 0x7d, 0x79, - 0x9b, 0x32, 0x7c, 0x97, 0x0a, 0x7c, 0x03, 0x9e, 0x34, 0xa9, 0xc4, 0xee, 0xf1, 0x82, 0x62, 0x4d, 0x74, 0xd7, 0xb3, - 0xb7, 0x05, 0xac, 0xb3, 0xd9, 0x23, 0x22, 0xf8, 0x5d, 0xfd, 0x6a, 0x83, 0xef, 0x16, 0xfe, 0x0a, 0xd6, 0xcf, 0xc1, - 0x49, 0x8a, 0x45, 0x43, 0x36, 0x0b, 0x77, 0x64, 0x40, 0xb9, 0x8a, 0x5f, 0x0e, 0x53, 0xb7, 0x8a, 0xe1, 0xda, 0xc7, - 0x57, 0xfc, 0x61, 0xa3, 0xdd, 0x86, 0x2a, 0x8b, 0xdb, 0xbd, 0x29, 0x1a, 0xb2, 0x6a, 0x7a, 0x47, 0xe6, 0x46, 0x4a, - 0xfd, 0xeb, 0x03, 0x6e, 0x6d, 0xb5, 0xef, 0xa7, 0xf9, 0xd6, 0xa3, 0x73, 0xd5, 0xb4, 0x4f, 0xad, 0x15, 0xc1, 0xc1, - 0xcf, 0x16, 0x6e, 0x6e, 0x0d, 0x38, 0x80, 0x9f, 0xbf, 0xa3, 0x79, 0x9c, 0x41, 0x74, 0x7a, 0xab, 0x19, 0x5f, 0xc5, - 0x7f, 0x8e, 0x1a, 0x71, 0x2f, 0xfd, 0x33, 0xf9, 0x73, 0xd4, 0x40, 0x3d, 0x14, 0xcf, 0x6f, 0x57, 0x6c, 0xb6, 0x82, - 0x60, 0x6b, 0xf7, 0x8e, 0xf0, 0xeb, 0xb0, 0x24, 0xd7, 0x34, 0xe7, 0xd9, 0xca, 0x3d, 0x45, 0xb7, 0x72, 0xef, 0xf4, - 0xac, 0xcc, 0xeb, 0x4a, 0xab, 0x58, 0x0e, 0x73, 0x08, 0x2c, 0x1c, 0xef, 0x35, 0x7b, 0xfd, 0x56, 0xf3, 0xc1, 0xc0, - 0xfe, 0x6b, 0x22, 0xdc, 0xa3, 0x5a, 0xc4, 0xb6, 0x37, 0x1b, 0x5b, 0x3f, 0x06, 0xc3, 0x0e, 0x08, 0x05, 0x0e, 0x72, - 0xe9, 0xe3, 0x0c, 0x59, 0xdf, 0x93, 0xd5, 0x8a, 0xb9, 0x68, 0xd6, 0x4e, 0x83, 0x5f, 0xc6, 0x66, 0x3a, 0x6c, 0x27, - 0x9d, 0xae, 0x17, 0x63, 0x49, 0x03, 0x22, 0x4d, 0x63, 0x06, 0x81, 0xa4, 0x96, 0x86, 0xc3, 0x9a, 0xdf, 0x46, 0x69, - 0x75, 0x7f, 0x04, 0x29, 0x3f, 0x44, 0x29, 0x3f, 0x22, 0x10, 0x40, 0xdb, 0x32, 0x47, 0x65, 0x43, 0xde, 0x77, 0xe9, - 0x9e, 0x71, 0x66, 0x68, 0xf0, 0xd5, 0xaa, 0x55, 0x0d, 0x53, 0x14, 0xf5, 0x61, 0x2e, 0xd7, 0x58, 0x90, 0x37, 0xa0, - 0x6b, 0x56, 0x44, 0xf4, 0x42, 0x57, 0x79, 0x78, 0x89, 0x17, 0x4b, 0x02, 0x4e, 0xfa, 0x3d, 0xd1, 0x2b, 0xc8, 0xe5, - 0xc3, 0x18, 0x7c, 0xcc, 0x30, 0xef, 0xeb, 0x7e, 0x31, 0x18, 0xa0, 0xd4, 0x39, 0x9d, 0xa5, 0x26, 0xe2, 0x4a, 0xe0, - 0x97, 0x5c, 0x80, 0x5f, 0xb2, 0x42, 0xac, 0x5f, 0x0c, 0xc8, 0xbd, 0x2c, 0x96, 0xe0, 0x94, 0xbf, 0xc3, 0xe7, 0xf1, - 0x61, 0x68, 0x60, 0x6a, 0x86, 0x65, 0x2e, 0xb2, 0xc1, 0x62, 0xce, 0x5a, 0x02, 0xc1, 0xcd, 0x80, 0xbb, 0xd4, 0x86, - 0x44, 0x63, 0x0d, 0x14, 0xdd, 0x46, 0xa1, 0x99, 0xd1, 0xd3, 0xad, 0x36, 0xfa, 0x91, 0xc3, 0x0b, 0x73, 0x0d, 0x63, - 0x11, 0xc8, 0x5c, 0xae, 0x7a, 0xec, 0x2f, 0x3f, 0x6c, 0x56, 0x18, 0xbc, 0xc2, 0x98, 0xec, 0x94, 0x56, 0x89, 0x66, - 0x7c, 0x95, 0x27, 0x8e, 0x21, 0xc8, 0xc4, 0x52, 0xe9, 0x86, 0x63, 0xe2, 0x4a, 0xfa, 0x4c, 0x0c, 0xd9, 0x6e, 0x78, - 0x66, 0x2e, 0x74, 0xb3, 0xfd, 0xc3, 0xb9, 0x9d, 0x73, 0xc2, 0x8d, 0x56, 0xd2, 0x68, 0xa3, 0x9e, 0x19, 0xaa, 0xea, - 0x82, 0xf9, 0x3d, 0x74, 0x5a, 0x5a, 0xec, 0x5c, 0xbd, 0xbb, 0xe1, 0xbb, 0x70, 0x65, 0xfc, 0x2d, 0x56, 0x85, 0x56, - 0x64, 0xb8, 0xdd, 0x42, 0xde, 0x9c, 0xe9, 0xa1, 0x57, 0xe4, 0x42, 0x75, 0xf8, 0x8b, 0xba, 0xc2, 0x3c, 0x15, 0x19, - 0x35, 0x84, 0x47, 0xbf, 0xd7, 0x19, 0x28, 0xff, 0x60, 0x62, 0x32, 0x67, 0xc9, 0x0d, 0x2d, 0x44, 0xfc, 0xe3, 0x0b, - 0x61, 0x62, 0x55, 0xed, 0xc1, 0x40, 0xf6, 0x4c, 0xc5, 0x3d, 0xb8, 0x35, 0xe1, 0x63, 0xce, 0x46, 0xe9, 0x5e, 0xf4, - 0x63, 0x43, 0x34, 0x7e, 0x8c, 0x7e, 0x04, 0x77, 0x67, 0xf7, 0x2e, 0x61, 0x19, 0x17, 0xc2, 0xdf, 0x63, 0x3d, 0x2c, - 0x55, 0xca, 0x58, 0x7b, 0xdd, 0x72, 0x78, 0x21, 0xf5, 0x26, 0x8b, 0x1f, 0x3a, 0x62, 0x6d, 0x53, 0xb0, 0x0e, 0x29, - 0x29, 0x3c, 0xbb, 0x62, 0x6e, 0xb5, 0x98, 0xbb, 0xd4, 0x12, 0xfe, 0xfa, 0xea, 0x61, 0xa9, 0x82, 0x86, 0x83, 0xd0, - 0x95, 0xb6, 0x90, 0x00, 0x03, 0x97, 0xd2, 0xa7, 0xd3, 0x9d, 0x49, 0x64, 0x96, 0xc5, 0xf0, 0xee, 0x41, 0x05, 0xf3, - 0xdf, 0xd9, 0x46, 0x58, 0x15, 0xb8, 0x5c, 0xa9, 0xa2, 0x5e, 0x4a, 0x02, 0x01, 0xe8, 0x4b, 0xef, 0x41, 0x79, 0x51, - 0x74, 0x1b, 0x0d, 0x09, 0x5a, 0x58, 0x6a, 0xae, 0x55, 0x31, 0xdd, 0x0f, 0xdf, 0xd3, 0x0b, 0x3e, 0xbc, 0x43, 0xda, - 0xc6, 0xa3, 0x96, 0x94, 0x50, 0xbb, 0x83, 0xf6, 0xc1, 0x2a, 0x3b, 0x28, 0xff, 0x36, 0xa6, 0xc8, 0xe6, 0xf7, 0xd9, - 0x0f, 0xd4, 0x75, 0x38, 0x70, 0x05, 0xab, 0x5e, 0xca, 0x28, 0x18, 0xc2, 0x3e, 0x60, 0x1f, 0x8b, 0x24, 0xa3, 0xd9, - 0x94, 0x81, 0xba, 0xdf, 0x16, 0xad, 0xe6, 0xf6, 0xa4, 0xee, 0x37, 0x64, 0x9c, 0x7d, 0x84, 0x71, 0xf6, 0x51, 0xe0, - 0xc5, 0x22, 0xc9, 0x1f, 0x32, 0xd6, 0x38, 0x56, 0x4d, 0x81, 0x8e, 0x3a, 0xc0, 0x9d, 0x81, 0x03, 0x0f, 0xd8, 0xa2, - 0x1c, 0x1c, 0x50, 0x67, 0x71, 0x4f, 0x1b, 0x99, 0xf7, 0xf6, 0x84, 0xda, 0x45, 0x2c, 0x70, 0xb3, 0x66, 0xa6, 0x05, - 0xad, 0x15, 0xc6, 0x79, 0x3c, 0xe0, 0x6d, 0x9e, 0xd5, 0xe2, 0x27, 0x6c, 0x58, 0x53, 0xd5, 0x6f, 0xa0, 0x39, 0xaa, - 0x05, 0xb9, 0x79, 0x62, 0xbc, 0x55, 0x49, 0x3f, 0x8a, 0x06, 0x96, 0x53, 0x21, 0x86, 0x64, 0xf4, 0x5b, 0x83, 0xe0, - 0x56, 0x7b, 0xb5, 0xe2, 0x1e, 0xf1, 0x45, 0xcd, 0x5b, 0xcd, 0xdc, 0x02, 0xd0, 0x22, 0x8e, 0xca, 0x7b, 0x93, 0x08, - 0xbc, 0x6f, 0xcb, 0x08, 0x69, 0xcb, 0xbe, 0x7d, 0x34, 0xb1, 0x54, 0x6c, 0xbe, 0xa3, 0x93, 0x41, 0x1a, 0xd9, 0x11, - 0x45, 0xf8, 0xba, 0x84, 0x24, 0x5c, 0x25, 0x5d, 0xab, 0x4c, 0xce, 0x99, 0x4a, 0x39, 0xbe, 0x2e, 0xa4, 0xd4, 0x57, - 0xf6, 0x4b, 0xe2, 0xea, 0x4e, 0x46, 0xe0, 0xeb, 0x09, 0xd3, 0xef, 0x68, 0x31, 0x61, 0xe0, 0x57, 0xe4, 0x6f, 0xc7, - 0x52, 0x4a, 0x2e, 0x9f, 0x88, 0xb8, 0x4f, 0x31, 0xbc, 0xf8, 0x39, 0xc0, 0xda, 0x84, 0x40, 0x29, 0x71, 0x11, 0x2e, - 0x88, 0xde, 0x14, 0xf2, 0xf6, 0x2e, 0x2e, 0xb0, 0x73, 0x00, 0x2c, 0x9d, 0x26, 0x01, 0xfe, 0xe5, 0x63, 0x3e, 0x56, - 0x63, 0x4e, 0x8d, 0xae, 0xdf, 0xfd, 0x4e, 0xae, 0x81, 0xde, 0x96, 0x8e, 0x82, 0xfd, 0xd6, 0x00, 0x72, 0xe1, 0x2e, - 0x0c, 0x2e, 0xbe, 0xc2, 0xda, 0xb2, 0x30, 0xde, 0x58, 0x00, 0xbd, 0xbf, 0x33, 0xb0, 0x60, 0xc3, 0x1c, 0x53, 0x78, - 0x2e, 0x75, 0xc2, 0x74, 0x10, 0x15, 0xe4, 0x49, 0xf9, 0x20, 0x66, 0xad, 0xf6, 0x5b, 0x36, 0x86, 0x3b, 0x8c, 0xe4, - 0xdb, 0x85, 0x13, 0x07, 0x1e, 0x90, 0x69, 0x32, 0xdb, 0xec, 0x1b, 0x1f, 0x79, 0xe4, 0xf5, 0x38, 0xde, 0xd5, 0x52, - 0x98, 0x6f, 0x56, 0x74, 0x8d, 0x21, 0x14, 0x45, 0xd8, 0xef, 0x17, 0x15, 0x53, 0x54, 0x19, 0xb4, 0x41, 0xc3, 0xf2, - 0x46, 0xfc, 0x02, 0x67, 0x0c, 0xad, 0x17, 0xb2, 0x77, 0x74, 0xd6, 0xe1, 0xcc, 0x61, 0xc6, 0x94, 0xc0, 0xa8, 0xb4, - 0x2c, 0xe8, 0x04, 0x1c, 0x9d, 0xab, 0x0f, 0xa2, 0xe2, 0xea, 0x58, 0x01, 0x78, 0x92, 0x29, 0xfc, 0x93, 0x6f, 0x82, - 0x75, 0xbf, 0x55, 0x33, 0x4c, 0xfd, 0x45, 0x6f, 0xbb, 0x96, 0x2f, 0x43, 0x1c, 0x69, 0x63, 0x08, 0xad, 0x73, 0x7b, - 0x07, 0x28, 0xe2, 0x82, 0x5e, 0xa4, 0x1a, 0x5f, 0xab, 0xc5, 0xd0, 0xac, 0xaf, 0x71, 0x1d, 0xd3, 0x06, 0x51, 0xac, - 0xbb, 0x26, 0xbe, 0xae, 0xde, 0x1f, 0x55, 0xa9, 0x82, 0x33, 0x48, 0x20, 0xac, 0xca, 0xcb, 0x86, 0x54, 0x92, 0x4b, - 0xd3, 0xa9, 0x34, 0x9d, 0x56, 0x08, 0xe5, 0xd2, 0x93, 0xf2, 0xfe, 0x15, 0x42, 0x18, 0x98, 0x32, 0x3b, 0xb0, 0x4a, - 0x6d, 0x61, 0x15, 0xbc, 0x7a, 0xb1, 0x81, 0x55, 0x12, 0x8e, 0xe7, 0x12, 0x8d, 0x8a, 0x0a, 0x87, 0x0c, 0xe9, 0x0b, - 0xb1, 0x08, 0x12, 0x00, 0x8b, 0xde, 0x65, 0x2e, 0xef, 0x7b, 0x38, 0x14, 0xf6, 0x24, 0x93, 0x70, 0xba, 0x09, 0xcd, - 0xe1, 0x61, 0x5a, 0xd5, 0xf3, 0x08, 0x01, 0x4b, 0xcf, 0x31, 0x3c, 0x48, 0xfc, 0xfd, 0x87, 0x50, 0x9d, 0x05, 0x79, - 0xfa, 0x2f, 0x51, 0x12, 0x1a, 0xfb, 0xcf, 0xf1, 0xd0, 0x21, 0x61, 0x38, 0xf0, 0xcd, 0x11, 0x56, 0x38, 0xb8, 0x55, - 0xc4, 0x67, 0x70, 0x87, 0x8f, 0x75, 0xe8, 0x01, 0x60, 0x09, 0xc5, 0x21, 0xc8, 0x37, 0x50, 0xcc, 0xe0, 0x80, 0x26, - 0xcb, 0xf0, 0x02, 0x17, 0xac, 0x16, 0xca, 0xfb, 0xdb, 0x96, 0x97, 0xd2, 0x6a, 0x97, 0xbc, 0xc6, 0x1c, 0xa8, 0xfc, - 0x0c, 0x2f, 0x7c, 0x85, 0x79, 0x29, 0xd9, 0x7d, 0xe1, 0x6b, 0x07, 0xf4, 0x14, 0x02, 0x46, 0xba, 0xdf, 0x6b, 0xc2, - 0x3d, 0x45, 0x2f, 0x73, 0x71, 0xd8, 0x76, 0xd0, 0xbd, 0xc0, 0x5c, 0x5d, 0x55, 0x59, 0x73, 0x30, 0x85, 0x06, 0x07, - 0x55, 0x38, 0x23, 0x30, 0x57, 0x2f, 0xca, 0x82, 0x73, 0x10, 0xef, 0x7b, 0xc2, 0xe4, 0x94, 0xd1, 0x00, 0x5e, 0x64, - 0xe5, 0xa3, 0x53, 0x3d, 0x0e, 0x2e, 0xe3, 0x86, 0x4d, 0x7c, 0x21, 0x7c, 0x2a, 0xb0, 0x92, 0xd6, 0x38, 0x34, 0xa2, - 0x23, 0x3a, 0x07, 0xb3, 0x0d, 0xa0, 0xe0, 0xee, 0x7c, 0xd8, 0x58, 0xa8, 0xe0, 0x31, 0xd8, 0xda, 0xdb, 0xcd, 0x84, - 0x38, 0x93, 0xa6, 0xe0, 0x6e, 0xdb, 0x20, 0x83, 0x37, 0xbf, 0xfd, 0xb7, 0xc2, 0x22, 0xc1, 0x80, 0x4a, 0x4d, 0x12, - 0x84, 0x27, 0x28, 0x8d, 0x74, 0x2b, 0x37, 0x13, 0x48, 0x27, 0xa2, 0x66, 0xd4, 0xbd, 0x71, 0xbe, 0x3a, 0x6a, 0x20, - 0x2a, 0x6a, 0xa0, 0x02, 0x6a, 0x20, 0xeb, 0xdb, 0xbf, 0x80, 0x85, 0xb0, 0x11, 0xaa, 0x44, 0x10, 0x10, 0x61, 0xae, - 0x0d, 0x1f, 0x50, 0x24, 0x21, 0xe4, 0x0d, 0xa0, 0x62, 0x4a, 0x5e, 0x82, 0xd1, 0x38, 0xbc, 0xde, 0x03, 0xee, 0x97, - 0x96, 0x61, 0xf0, 0x9c, 0x82, 0xc9, 0x7f, 0xeb, 0xf3, 0xa1, 0x7a, 0xb9, 0x3a, 0x08, 0xe1, 0x17, 0x10, 0x2b, 0xc2, - 0xf1, 0x17, 0xbf, 0x00, 0xd9, 0x54, 0x58, 0x1e, 0x1c, 0x48, 0x10, 0xf8, 0x21, 0x8a, 0x70, 0xc0, 0x33, 0xbc, 0xcc, - 0x36, 0x88, 0x9e, 0x9f, 0x95, 0xaa, 0x66, 0x25, 0x83, 0x59, 0x15, 0x9e, 0xc6, 0xd1, 0x35, 0x61, 0x20, 0xb8, 0x50, - 0xbb, 0x6f, 0x10, 0x02, 0x65, 0xcb, 0x8d, 0xa1, 0x4b, 0x4f, 0xc1, 0x7c, 0x34, 0x8e, 0xde, 0x32, 0x78, 0xd2, 0xd6, - 0x98, 0xfc, 0x33, 0x6d, 0x1e, 0xb8, 0x4f, 0xf7, 0xa2, 0x46, 0xe0, 0xa4, 0x4e, 0x51, 0xf2, 0xb7, 0xe4, 0x22, 0x8e, - 0x9a, 0x97, 0x11, 0x6a, 0xc0, 0xbf, 0x0d, 0x8e, 0xba, 0x34, 0xa1, 0xa3, 0x91, 0x0f, 0x7e, 0x93, 0x11, 0xb3, 0xc9, - 0x56, 0x2b, 0x51, 0x11, 0xf4, 0xc4, 0x6e, 0x30, 0x60, 0x25, 0x5e, 0x00, 0xfb, 0x60, 0x39, 0x58, 0xf2, 0x4e, 0xc4, - 0xca, 0x9f, 0x52, 0x18, 0xac, 0x9e, 0x33, 0x84, 0x70, 0x16, 0xc4, 0x6c, 0xfc, 0xcf, 0x67, 0x1a, 0xae, 0x9f, 0x9f, - 0xaf, 0x63, 0x44, 0xa4, 0x0f, 0x22, 0x57, 0x63, 0x47, 0x44, 0x10, 0xb6, 0x4c, 0xf7, 0x5d, 0x99, 0x1f, 0xbc, 0x75, - 0xf5, 0xc0, 0x86, 0x8b, 0x03, 0x03, 0x6a, 0x14, 0x18, 0xad, 0xe0, 0x9c, 0x94, 0x03, 0x07, 0x25, 0x84, 0x66, 0x45, - 0x3c, 0x25, 0x97, 0x10, 0x09, 0x2f, 0x43, 0x5d, 0x30, 0x2c, 0x08, 0x24, 0xa8, 0x29, 0x48, 0x50, 0x99, 0xaf, 0x3d, - 0x82, 0x59, 0xe7, 0x66, 0xb6, 0x53, 0xd4, 0x75, 0x41, 0x7e, 0x7e, 0xd1, 0xf1, 0x08, 0x58, 0xda, 0x83, 0x83, 0x02, - 0x22, 0x88, 0x01, 0x05, 0x2f, 0x25, 0xc0, 0x40, 0x03, 0x5e, 0x6c, 0x68, 0xc0, 0xe7, 0xda, 0x78, 0x1d, 0x18, 0x5b, - 0x9f, 0x32, 0xc8, 0xc5, 0xb3, 0x6a, 0x4f, 0x13, 0x42, 0xf6, 0x5b, 0x3d, 0x9d, 0x6e, 0x47, 0x48, 0xec, 0x7d, 0xd4, - 0x26, 0xd0, 0x98, 0x23, 0xdd, 0xd5, 0xc6, 0xfc, 0x5a, 0xd3, 0x23, 0x56, 0x93, 0x90, 0x2e, 0x48, 0x97, 0xe7, 0xd3, - 0x9e, 0xc1, 0x15, 0xab, 0x34, 0x72, 0x70, 0x01, 0xfa, 0x6c, 0x40, 0x80, 0x02, 0x95, 0xa6, 0x12, 0x45, 0x11, 0x17, - 0x49, 0xc9, 0x86, 0x61, 0x06, 0x61, 0x0a, 0xab, 0x95, 0xa0, 0x1b, 0x6b, 0x00, 0xbc, 0x33, 0xb3, 0x7f, 0x4a, 0x1f, - 0x6c, 0xba, 0xf6, 0xe6, 0x11, 0x40, 0x40, 0xf6, 0xdb, 0x25, 0xbb, 0x2e, 0x36, 0x2a, 0xb3, 0xb0, 0x96, 0xb1, 0x95, - 0xdb, 0xf6, 0x18, 0x7b, 0x27, 0xb6, 0xf9, 0x04, 0x08, 0x51, 0x5b, 0x32, 0x8d, 0x10, 0x21, 0xb1, 0x88, 0x75, 0x6d, - 0xc8, 0x46, 0x1b, 0xda, 0x37, 0x4f, 0xc2, 0x43, 0xec, 0x03, 0x50, 0xbc, 0x39, 0x2e, 0xc1, 0x21, 0xbc, 0xf0, 0x08, - 0x7f, 0x0b, 0x2c, 0x52, 0x81, 0x19, 0x96, 0xab, 0x15, 0xd4, 0xf3, 0x78, 0x9f, 0x6d, 0x06, 0x27, 0x95, 0x1b, 0x63, - 0x97, 0x76, 0xe2, 0x71, 0xd9, 0x84, 0xc4, 0x19, 0xf4, 0xeb, 0x2b, 0xa2, 0xde, 0x7e, 0x3b, 0x7d, 0xe2, 0xdf, 0x2b, - 0x73, 0x3b, 0x10, 0x1b, 0xd6, 0x1b, 0xac, 0x3e, 0x80, 0x96, 0xbf, 0xca, 0xfc, 0x43, 0x65, 0xc1, 0x4d, 0x82, 0xda, - 0x5c, 0xc4, 0x2e, 0xeb, 0x22, 0x46, 0x6a, 0x8b, 0xbb, 0x43, 0x88, 0x7f, 0xb5, 0x15, 0xc5, 0x80, 0x27, 0x15, 0xff, - 0x1c, 0xa3, 0x2e, 0x84, 0xa2, 0xb6, 0x1e, 0x36, 0x40, 0x69, 0x97, 0xeb, 0x4a, 0x8c, 0x0c, 0x09, 0xe4, 0x5b, 0x17, - 0x5e, 0xd0, 0x9c, 0x44, 0x0a, 0xe4, 0xe4, 0x20, 0x2a, 0x69, 0xb6, 0x21, 0xcc, 0x75, 0xb7, 0x70, 0xcc, 0x5c, 0x6d, - 0xd0, 0x22, 0x7e, 0x01, 0xec, 0x0c, 0x37, 0x92, 0xa5, 0x03, 0x9f, 0xaa, 0x81, 0xcf, 0xaf, 0xb9, 0xa1, 0x28, 0x0a, - 0xf5, 0xde, 0xd9, 0x47, 0xe6, 0xe0, 0x77, 0x1a, 0x88, 0x8f, 0xd4, 0xe9, 0x48, 0x36, 0x42, 0xad, 0x39, 0x3b, 0x5e, - 0xb6, 0x19, 0x61, 0x50, 0xd8, 0xe8, 0x7d, 0x15, 0xb2, 0x8a, 0x9d, 0x9d, 0x8a, 0x60, 0x4e, 0x5f, 0x54, 0xe5, 0x9c, - 0xca, 0x2d, 0xa3, 0x5a, 0x6a, 0x1a, 0x20, 0xc2, 0x95, 0x4f, 0x24, 0xef, 0x33, 0x13, 0xfe, 0xc1, 0x60, 0x5c, 0x3d, - 0x52, 0xf8, 0xfb, 0x5d, 0xb1, 0x43, 0xb6, 0xa3, 0xc3, 0x6d, 0x04, 0xcd, 0x0b, 0x15, 0x3c, 0xe0, 0xa8, 0x64, 0x09, - 0x91, 0x22, 0x97, 0xfb, 0xaa, 0x66, 0xca, 0x76, 0x1d, 0x21, 0x84, 0xb4, 0xc7, 0x59, 0x37, 0xb4, 0x7a, 0xe8, 0x91, - 0x2a, 0xca, 0xe1, 0x16, 0xcd, 0x75, 0x01, 0x2a, 0x8c, 0x40, 0xba, 0xfc, 0xcc, 0xee, 0x52, 0x09, 0xd1, 0xcb, 0xd7, - 0x2e, 0x84, 0xb1, 0xb3, 0xb2, 0xc4, 0x85, 0x19, 0xb5, 0x0d, 0xa3, 0xeb, 0x36, 0x86, 0xb3, 0x81, 0x31, 0xd3, 0xa0, - 0xa4, 0x05, 0xa1, 0xae, 0xbb, 0xf4, 0x22, 0x33, 0x81, 0x1e, 0x73, 0x42, 0x1b, 0x0c, 0x4f, 0x89, 0x06, 0xcb, 0xa6, - 0x02, 0x2c, 0xf8, 0x96, 0x45, 0x6a, 0x6d, 0x36, 0x59, 0xfc, 0x51, 0xc7, 0xe6, 0x69, 0xbf, 0xbc, 0x62, 0x9e, 0x0b, - 0x47, 0xdd, 0x9e, 0x67, 0x3e, 0x1e, 0xdd, 0xd3, 0x37, 0x57, 0x2f, 0x5e, 0xbe, 0x7e, 0xb5, 0x5a, 0xb5, 0x59, 0xb3, - 0x7d, 0x82, 0x7f, 0xd2, 0x65, 0x3c, 0xd8, 0x32, 0x0a, 0xd0, 0xc1, 0xc1, 0x3e, 0x37, 0x2e, 0x3c, 0x9f, 0xf9, 0x1c, - 0xe2, 0x06, 0xe9, 0x01, 0xce, 0x8a, 0x32, 0x26, 0xc8, 0x6d, 0xd4, 0x8b, 0xee, 0x22, 0x50, 0x42, 0x55, 0xe4, 0xef, - 0xc3, 0xe6, 0xec, 0xf7, 0x20, 0x30, 0x11, 0xd4, 0x87, 0x08, 0x20, 0x10, 0xaf, 0x14, 0x17, 0x84, 0xf9, 0x04, 0x88, - 0xe2, 0xbd, 0x00, 0xce, 0xd4, 0x44, 0xad, 0x5a, 0xa8, 0xb8, 0x00, 0x92, 0x68, 0xc3, 0x51, 0xd2, 0x23, 0x13, 0xc0, - 0x1b, 0x82, 0x52, 0xda, 0x5f, 0xdd, 0xdc, 0xb9, 0x4b, 0xe5, 0xa8, 0xd7, 0x4a, 0x73, 0x3c, 0x75, 0x9f, 0x53, 0xf8, - 0x9c, 0x76, 0xfd, 0xe9, 0x20, 0x0e, 0x73, 0xbc, 0x20, 0xe2, 0xd0, 0x3f, 0x8b, 0xb8, 0x9c, 0x17, 0xec, 0x0b, 0x97, - 0x0b, 0x95, 0x2e, 0x6f, 0x53, 0x99, 0xdc, 0x36, 0x47, 0x87, 0x71, 0x91, 0xdc, 0x36, 0x55, 0x72, 0x8b, 0xf0, 0x5d, - 0x2a, 0x93, 0x3b, 0x9b, 0x72, 0xd7, 0x54, 0x70, 0xf3, 0x85, 0x05, 0x1c, 0x8a, 0xb6, 0x68, 0x63, 0xb1, 0x59, 0xd4, - 0xa6, 0xb8, 0xa2, 0x01, 0x06, 0xff, 0xbe, 0x63, 0xe3, 0x87, 0xe1, 0x4b, 0x70, 0x69, 0xd2, 0x44, 0x7e, 0x02, 0xe9, - 0xa7, 0x55, 0x19, 0xb8, 0x4f, 0x49, 0xab, 0x3b, 0xbd, 0x10, 0xcd, 0x76, 0xb7, 0xd1, 0x98, 0xc2, 0xde, 0xcd, 0x48, - 0xee, 0x8b, 0x4d, 0x1b, 0x26, 0xbe, 0xce, 0x7e, 0xb6, 0x5a, 0xed, 0xe7, 0xc8, 0x6c, 0xb8, 0x09, 0x8b, 0x75, 0x7f, - 0x3a, 0xc0, 0x2d, 0xfc, 0x3c, 0x43, 0x68, 0xc9, 0xfa, 0xd3, 0x01, 0x61, 0xfd, 0x69, 0xa3, 0x3d, 0xb0, 0x86, 0x76, - 0x66, 0x2b, 0xae, 0x21, 0x84, 0xe6, 0x74, 0x70, 0x64, 0x4a, 0x4a, 0x97, 0x6f, 0xbf, 0x68, 0x15, 0xd0, 0x4f, 0xd5, - 0x82, 0x97, 0x49, 0xdc, 0x81, 0xbe, 0xe8, 0x85, 0x7d, 0xba, 0xb5, 0x20, 0xc7, 0x47, 0x95, 0xab, 0x3d, 0x45, 0xd8, - 0xf4, 0xa4, 0x0e, 0x8b, 0x43, 0xd3, 0x8c, 0xeb, 0x52, 0xba, 0xef, 0x50, 0x33, 0xf2, 0xd1, 0xc1, 0x02, 0x10, 0xa4, - 0x82, 0x47, 0x56, 0xb8, 0x70, 0x4a, 0x21, 0x5c, 0x1c, 0x54, 0xb6, 0x60, 0x92, 0x93, 0x56, 0x37, 0x37, 0x96, 0xfe, - 0xb9, 0x8b, 0x68, 0x4a, 0x31, 0x25, 0x99, 0x2f, 0x99, 0x1b, 0xb0, 0xd0, 0x4d, 0xca, 0x33, 0x05, 0xbd, 0xd2, 0x00, - 0x8f, 0x08, 0xc4, 0x43, 0xea, 0x16, 0xc6, 0xc0, 0x2b, 0x9e, 0x36, 0x8b, 0x3e, 0x1b, 0xa0, 0xa3, 0x63, 0x4c, 0xfb, - 0x7f, 0x65, 0xf3, 0x36, 0x3c, 0x16, 0xf8, 0xd7, 0x80, 0x4c, 0x9b, 0xb2, 0x4c, 0x10, 0x90, 0x30, 0x6a, 0xca, 0x43, - 0xd8, 0x4b, 0x08, 0x67, 0xb6, 0x62, 0xd6, 0x67, 0x83, 0xe6, 0xb4, 0xac, 0xd8, 0xf1, 0x15, 0x1b, 0xb2, 0x4c, 0xb0, - 0x15, 0x1b, 0xae, 0x62, 0xf8, 0x3a, 0x83, 0x01, 0x41, 0x08, 0x00, 0x06, 0x00, 0xd0, 0x28, 0x88, 0xe6, 0x8b, 0x15, - 0xf1, 0x9b, 0xdd, 0xde, 0xe3, 0xb7, 0xc0, 0x02, 0xad, 0xb6, 0xff, 0x77, 0xa1, 0x0c, 0xd8, 0x53, 0x16, 0x26, 0x66, - 0x6e, 0x61, 0x55, 0x74, 0x00, 0x95, 0x12, 0x61, 0x0a, 0x03, 0x99, 0xfd, 0xcc, 0x40, 0x2d, 0xd0, 0x1a, 0xe4, 0x7d, - 0x3d, 0x68, 0x66, 0x70, 0xc4, 0xc0, 0x3b, 0x34, 0x64, 0x6a, 0x8c, 0x09, 0xe3, 0x1c, 0xa6, 0x98, 0x19, 0xf0, 0x4c, - 0xd3, 0xd6, 0x5a, 0x1a, 0x59, 0xae, 0x97, 0xf7, 0xfe, 0xd1, 0xb1, 0xea, 0x17, 0xcd, 0xf6, 0x00, 0xed, 0x13, 0x62, - 0x3f, 0x06, 0xb0, 0xc9, 0x5c, 0x6a, 0xc3, 0x7c, 0x1f, 0x75, 0x52, 0xfb, 0x09, 0x7f, 0x06, 0x6b, 0xb3, 0x03, 0x40, - 0x47, 0x86, 0xcd, 0xfa, 0xcb, 0x9a, 0xca, 0xeb, 0xe3, 0xce, 0x28, 0x95, 0xbb, 0xde, 0x9d, 0x0e, 0x34, 0xc5, 0xa1, - 0xb7, 0x1e, 0x2e, 0x1f, 0xea, 0x21, 0x60, 0xc6, 0x60, 0x6e, 0x99, 0xd1, 0xf7, 0x42, 0x24, 0x17, 0x44, 0x62, 0x69, - 0xb0, 0x86, 0xc1, 0xde, 0x3a, 0x38, 0x30, 0xd5, 0x58, 0x03, 0x9e, 0x27, 0x45, 0x20, 0x18, 0xf8, 0x08, 0xca, 0x80, - 0x26, 0xca, 0xdc, 0x86, 0x93, 0x8f, 0xcc, 0xfd, 0xc2, 0xe5, 0xed, 0x63, 0xe1, 0xb4, 0xad, 0xe6, 0x7a, 0xbc, 0x2c, - 0x70, 0x57, 0xde, 0x4b, 0x5a, 0x05, 0x37, 0xb2, 0x37, 0x79, 0xca, 0xdc, 0xad, 0xfb, 0x52, 0x9d, 0xdd, 0xcd, 0x74, - 0xca, 0x66, 0x3a, 0xdb, 0xcd, 0x84, 0x9a, 0x99, 0x6f, 0x59, 0x45, 0x9a, 0x93, 0x35, 0x51, 0x73, 0x2a, 0x7e, 0xa2, - 0x73, 0xd0, 0x8e, 0x72, 0x7b, 0xaf, 0x0a, 0x27, 0x57, 0x4e, 0x2e, 0xf7, 0x73, 0x43, 0x5c, 0x91, 0xb9, 0x50, 0x87, - 0x00, 0x2f, 0x2f, 0xca, 0xc7, 0x07, 0xb8, 0x14, 0xbf, 0xca, 0x91, 0x8b, 0x72, 0x2a, 0xa4, 0x96, 0x82, 0x45, 0xc8, - 0xa0, 0xaa, 0x8b, 0x81, 0xbd, 0xb4, 0x7b, 0x4f, 0xf4, 0x78, 0xbf, 0x8a, 0x98, 0x37, 0x30, 0xcf, 0x7d, 0x7c, 0x4f, - 0x53, 0xec, 0xd4, 0xc4, 0x19, 0xf9, 0x90, 0xc5, 0x39, 0xc8, 0x66, 0xfd, 0xea, 0xb5, 0xdf, 0x46, 0x1b, 0x17, 0xcd, - 0x58, 0xf4, 0xcc, 0x13, 0x27, 0x3f, 0x14, 0xc6, 0x38, 0xc0, 0x3a, 0xfa, 0x23, 0x4c, 0x2d, 0xd8, 0xb3, 0xc4, 0x53, - 0xe8, 0xe4, 0xd6, 0xa6, 0xdd, 0x85, 0x69, 0x77, 0x26, 0xad, 0x03, 0xe5, 0x80, 0x34, 0xbb, 0x32, 0x9d, 0x3b, 0xff, - 0x7d, 0x07, 0x2f, 0xdd, 0xae, 0x21, 0x12, 0xf7, 0xfc, 0x91, 0x31, 0x86, 0x78, 0x03, 0x36, 0xa2, 0xea, 0xe0, 0xe0, - 0x0f, 0xe7, 0x7d, 0x5b, 0xc9, 0x7d, 0xdf, 0x0a, 0x07, 0xb6, 0xc1, 0x54, 0xba, 0xbc, 0x91, 0xcc, 0x16, 0x60, 0xd7, - 0xb9, 0xff, 0x8d, 0x78, 0xf8, 0x22, 0x64, 0x5a, 0xac, 0xab, 0xf8, 0x2b, 0x39, 0x2a, 0x3d, 0x44, 0x35, 0x44, 0x20, - 0xad, 0xac, 0x4b, 0x43, 0xd3, 0xd1, 0xab, 0x29, 0x1d, 0xc9, 0x9b, 0xb7, 0x52, 0xea, 0x81, 0x7d, 0x91, 0x5b, 0x27, - 0xf0, 0x68, 0x61, 0x8d, 0xa1, 0xb9, 0x2b, 0xbd, 0x93, 0x6c, 0x40, 0xd4, 0xfa, 0xb8, 0x43, 0x49, 0x24, 0x16, 0xd5, - 0x5d, 0x08, 0x87, 0xbb, 0x10, 0xcc, 0xcb, 0xa0, 0x6d, 0x10, 0xbb, 0xdd, 0x05, 0x6d, 0x03, 0xa7, 0x6e, 0x1b, 0xb8, - 0x3d, 0x18, 0x2c, 0xec, 0x7d, 0x78, 0x39, 0x96, 0x63, 0xe1, 0xaf, 0xc9, 0xec, 0x03, 0x40, 0xa0, 0xf6, 0x61, 0xc5, - 0x13, 0x07, 0x82, 0xc4, 0x19, 0x8e, 0xbe, 0xe7, 0xec, 0xc6, 0x5a, 0x0e, 0xcf, 0xe6, 0x0b, 0xcd, 0x46, 0xe6, 0x8e, - 0x1a, 0x54, 0x7c, 0x75, 0x3f, 0xaf, 0x9f, 0xb2, 0x9a, 0x6e, 0xfc, 0x1e, 0x84, 0x91, 0x70, 0xca, 0x0e, 0xa3, 0x90, - 0xb0, 0xc1, 0xac, 0xca, 0x78, 0x6d, 0xbf, 0x41, 0xbc, 0x07, 0x6d, 0xc2, 0x09, 0x16, 0xb5, 0x0b, 0xaa, 0x08, 0xdb, - 0x78, 0x63, 0x41, 0x94, 0x87, 0x37, 0x5b, 0x46, 0xd3, 0xcb, 0x35, 0x04, 0x3a, 0xee, 0x45, 0xcd, 0xa8, 0xc1, 0x52, - 0x17, 0x94, 0xd9, 0x47, 0x18, 0x57, 0x17, 0x27, 0x26, 0x4e, 0x7b, 0xa9, 0x57, 0xff, 0x2d, 0x03, 0x03, 0x7c, 0x01, - 0x5e, 0x62, 0x61, 0x74, 0xd7, 0xbe, 0x6e, 0x40, 0x7d, 0xd9, 0x60, 0x03, 0xb4, 0x5a, 0xb5, 0xca, 0x67, 0xa0, 0xdc, - 0x35, 0x97, 0xb0, 0xd7, 0x5c, 0xc2, 0x5d, 0x73, 0x09, 0x7f, 0xcd, 0x25, 0xcc, 0x35, 0x97, 0xf0, 0xd7, 0x5c, 0x1e, - 0x84, 0x9f, 0x82, 0x38, 0x8e, 0x31, 0x87, 0xb8, 0x8a, 0xda, 0x46, 0xc6, 0x83, 0x0b, 0xcf, 0x7d, 0x96, 0xa8, 0x72, - 0xf9, 0xc3, 0x18, 0x72, 0x5b, 0xb6, 0x12, 0xc6, 0x6d, 0x8a, 0x29, 0x88, 0x9c, 0x7e, 0x70, 0x50, 0xba, 0x3b, 0x83, - 0x8f, 0x7a, 0xca, 0xf1, 0xd2, 0x3a, 0xd1, 0xfe, 0x01, 0x3a, 0x79, 0xf3, 0xeb, 0x63, 0x2a, 0xd7, 0x44, 0x38, 0x93, - 0xfb, 0xfd, 0xb6, 0xa7, 0x14, 0x9f, 0x32, 0x13, 0x9e, 0x9c, 0x27, 0xda, 0x88, 0x20, 0x08, 0x51, 0xa2, 0x70, 0x46, - 0xa4, 0xdd, 0xef, 0xde, 0x15, 0xde, 0xa8, 0xa2, 0xbc, 0x59, 0xc9, 0xe3, 0x1c, 0x9c, 0xd8, 0x8d, 0x15, 0x06, 0xea, - 0x82, 0x0b, 0x41, 0x66, 0x12, 0xfe, 0x68, 0xe6, 0x96, 0x9c, 0x65, 0x65, 0xd2, 0xc7, 0x66, 0x6e, 0x08, 0x58, 0x41, - 0xf6, 0x3d, 0xcc, 0x96, 0xb7, 0x29, 0xc5, 0x77, 0x69, 0x86, 0x87, 0xf2, 0x36, 0x2d, 0x42, 0x5b, 0x10, 0x7f, 0xf1, - 0x37, 0x8e, 0x23, 0x41, 0xc1, 0xdf, 0x27, 0xe2, 0x62, 0x8f, 0x6f, 0x78, 0x01, 0x2e, 0x33, 0x63, 0x51, 0x9d, 0x32, - 0xfc, 0x0d, 0x4b, 0x78, 0x08, 0x4e, 0xa6, 0xb1, 0x22, 0xf7, 0x38, 0xb0, 0x13, 0x92, 0x80, 0xc3, 0xd5, 0xed, 0x15, - 0xff, 0x0a, 0x17, 0x5f, 0xa5, 0xb3, 0x65, 0x73, 0x28, 0x6f, 0x23, 0x5c, 0x90, 0x37, 0xf0, 0xfa, 0xd6, 0xff, 0xcb, - 0xde, 0xdb, 0x36, 0xb7, 0x6d, 0x64, 0xeb, 0xa2, 0x7f, 0x45, 0x62, 0xd9, 0x0c, 0x60, 0x36, 0x29, 0xca, 0xe7, 0xcc, - 0x54, 0x5d, 0x50, 0x6d, 0x96, 0x63, 0xc7, 0x13, 0x67, 0x22, 0xdb, 0x63, 0x79, 0x32, 0xc9, 0xb0, 0x78, 0x19, 0x08, - 0x68, 0x0a, 0x70, 0x40, 0x80, 0x01, 0x40, 0x89, 0x34, 0x89, 0xff, 0x7e, 0x6a, 0xad, 0xd5, 0xaf, 0x20, 0x28, 0x7b, - 0xf6, 0x3e, 0xfb, 0xd3, 0xbd, 0x5f, 0x6c, 0xb1, 0xd1, 0x68, 0xf4, 0x7b, 0xaf, 0x5e, 0x2f, 0xcf, 0xd3, 0x93, 0xb1, - 0xba, 0x3d, 0x70, 0xd6, 0xa5, 0x14, 0x1d, 0x6f, 0x8a, 0xc3, 0xdb, 0xf3, 0xd9, 0x7e, 0x1b, 0x44, 0x6c, 0x17, 0x64, - 0x58, 0xeb, 0xa4, 0xe1, 0x3f, 0xd1, 0xd6, 0xc1, 0x62, 0x84, 0xfd, 0x5f, 0xd6, 0x03, 0x2f, 0x21, 0x35, 0x14, 0xb8, - 0x18, 0x6c, 0x38, 0x5a, 0xdb, 0x65, 0x1a, 0xb8, 0xa9, 0x41, 0xaf, 0xef, 0x29, 0x44, 0x79, 0xc9, 0x68, 0x6e, 0x04, - 0xeb, 0xc6, 0x90, 0x8b, 0xc3, 0x71, 0xb3, 0x1c, 0xf2, 0x92, 0xa6, 0xd3, 0x20, 0x94, 0xee, 0x2c, 0x6b, 0x48, 0xa2, - 0xec, 0x83, 0x50, 0xbb, 0xb6, 0xec, 0xb7, 0x81, 0xed, 0xcb, 0x1f, 0x0d, 0x63, 0xff, 0x62, 0xf9, 0x4c, 0x48, 0x17, - 0xf1, 0x1c, 0x04, 0x51, 0xfb, 0x79, 0x36, 0xdc, 0xf8, 0x17, 0xeb, 0x67, 0x42, 0xf9, 0x8d, 0xe7, 0xb6, 0x1c, 0x52, - 0x67, 0x2d, 0x7c, 0x61, 0x3c, 0x3c, 0xb8, 0x32, 0xb4, 0x1d, 0x0e, 0x42, 0xff, 0x6d, 0xd6, 0x08, 0x6e, 0x6c, 0x68, - 0x9f, 0x2f, 0x7c, 0xd8, 0xda, 0x68, 0xac, 0x29, 0xa6, 0x5b, 0xe8, 0xdf, 0x64, 0xb6, 0xb4, 0xa7, 0x51, 0xc9, 0x8b, - 0x53, 0xd3, 0x88, 0x85, 0x30, 0x60, 0xe8, 0x27, 0xf3, 0x01, 0x54, 0x73, 0xc7, 0x23, 0x90, 0xc9, 0x07, 0x7a, 0xb0, - 0x26, 0xb5, 0xea, 0xaf, 0x61, 0x26, 0xff, 0x8f, 0x54, 0x58, 0x8c, 0xee, 0xb6, 0x61, 0xa6, 0xfe, 0x88, 0xe4, 0x1f, - 0x2c, 0xe7, 0xbb, 0xd4, 0x0b, 0xb5, 0x1f, 0x0b, 0x2b, 0x30, 0x28, 0x51, 0x35, 0xa0, 0x07, 0x22, 0xa8, 0xca, 0x20, - 0xcd, 0xb0, 0x3a, 0x07, 0xfd, 0xee, 0x69, 0xd5, 0x91, 0x1c, 0xd2, 0x5a, 0x0d, 0xa9, 0x60, 0xaa, 0xd4, 0x20, 0x3f, - 0x1c, 0xee, 0x52, 0xa6, 0xcb, 0x80, 0x4b, 0xfa, 0x5d, 0xaa, 0x94, 0xc2, 0x7f, 0x22, 0x00, 0x9d, 0x83, 0x7b, 0x7c, - 0x39, 0x06, 0xd2, 0x0c, 0x0b, 0xbf, 0x35, 0x3b, 0xbe, 0x26, 0xe1, 0x36, 0x09, 0x2e, 0x06, 0x38, 0x47, 0x57, 0x61, - 0x79, 0x97, 0x42, 0x04, 0x55, 0x09, 0xf5, 0xad, 0x4c, 0x83, 0xd2, 0x56, 0x83, 0xb0, 0x26, 0xa1, 0xce, 0x24, 0x1b, - 0x95, 0xb6, 0x1b, 0x85, 0xd9, 0x22, 0xae, 0x67, 0x84, 0x35, 0x67, 0x33, 0xd5, 0xc0, 0xa4, 0xe1, 0xb8, 0x69, 0xb4, - 0x16, 0x15, 0x6a, 0x0a, 0xf3, 0x1a, 0x57, 0x95, 0xaa, 0xee, 0xe6, 0xd4, 0x52, 0x5a, 0xb6, 0x57, 0xdd, 0x24, 0x1b, - 0x72, 0x19, 0xca, 0x30, 0xd8, 0xc8, 0x11, 0x4c, 0x20, 0x49, 0xce, 0xfc, 0x8d, 0xfc, 0x43, 0x6d, 0xba, 0x16, 0x30, - 0xc7, 0x98, 0x65, 0xc3, 0x82, 0x5e, 0x81, 0x7b, 0xa0, 0x95, 0x9e, 0x4f, 0xb3, 0x8b, 0x3c, 0x48, 0x86, 0x85, 0x5e, - 0x36, 0x19, 0xff, 0x53, 0x18, 0x69, 0x32, 0x63, 0x25, 0x8b, 0x6c, 0x57, 0xa7, 0xc4, 0x79, 0x9c, 0xc0, 0xf6, 0x68, - 0x7a, 0xcb, 0xf7, 0x19, 0x44, 0x05, 0x81, 0x82, 0x19, 0xf3, 0x65, 0x17, 0xcf, 0x7d, 0x9f, 0x59, 0xa6, 0xee, 0xc3, - 0xc1, 0x98, 0xb1, 0xfd, 0x7e, 0x3f, 0xef, 0xf7, 0xd5, 0x7c, 0xeb, 0xf7, 0x93, 0x17, 0xe6, 0x6f, 0x0f, 0x18, 0x14, - 0xe4, 0x44, 0x34, 0x15, 0x22, 0xf8, 0x87, 0xe4, 0x19, 0x92, 0xd1, 0x1d, 0xf7, 0xb9, 0xe5, 0x6c, 0x59, 0x1d, 0x81, - 0x60, 0x1e, 0x0e, 0x97, 0x0a, 0xec, 0x5a, 0xa2, 0x48, 0xc8, 0xf2, 0x9f, 0x81, 0xf1, 0xcc, 0x7d, 0x80, 0x25, 0x03, - 0x10, 0xb6, 0xca, 0xd3, 0xf5, 0x9e, 0xaf, 0x82, 0x77, 0x3a, 0xde, 0x35, 0x56, 0x64, 0x20, 0x6e, 0x81, 0x8d, 0x58, - 0x6b, 0x0f, 0xc8, 0x99, 0x02, 0x1c, 0x2f, 0x0e, 0x87, 0x73, 0xf9, 0x4b, 0x37, 0x5b, 0x27, 0x50, 0x29, 0x70, 0x7b, - 0x74, 0x72, 0xf0, 0xdf, 0x81, 0x66, 0x50, 0x0e, 0xf3, 0x7a, 0xfb, 0x3b, 0x73, 0xf2, 0xd3, 0x53, 0xfc, 0x13, 0x1e, - 0xa2, 0xd3, 0x6f, 0xf7, 0xe6, 0x0f, 0x8a, 0xca, 0xc3, 0x41, 0x2d, 0xfe, 0x73, 0xce, 0x2b, 0xf8, 0x85, 0x6f, 0x02, - 0xb3, 0xc9, 0xd4, 0x3b, 0xf9, 0x26, 0xcf, 0x99, 0x7a, 0x8d, 0x57, 0x4c, 0xbe, 0xc3, 0xe1, 0x5c, 0x8c, 0xea, 0xed, - 0xc8, 0x89, 0x76, 0xca, 0x31, 0x0e, 0x06, 0xff, 0x45, 0xb4, 0x4d, 0x08, 0x30, 0xa4, 0x6e, 0x49, 0x33, 0x1b, 0x57, - 0x96, 0x78, 0x96, 0xce, 0x2f, 0x27, 0x75, 0xb9, 0xd3, 0x8a, 0xa7, 0x3d, 0xb0, 0xb8, 0xad, 0xc1, 0x0b, 0xe0, 0xde, - 0x62, 0xeb, 0x4a, 0xc1, 0xe1, 0x02, 0xe2, 0x14, 0x27, 0x20, 0x82, 0xf6, 0xfb, 0x12, 0xef, 0x15, 0xf4, 0x49, 0x3f, - 0x40, 0x30, 0xe4, 0xcf, 0x12, 0x70, 0xd7, 0xeb, 0xd5, 0x18, 0xdf, 0x4b, 0x21, 0xb8, 0x3e, 0xd3, 0x00, 0xb4, 0xe0, - 0x77, 0xf9, 0x58, 0x4e, 0xbf, 0x89, 0xc0, 0xb3, 0x65, 0x6f, 0xa2, 0xdc, 0x6d, 0x78, 0xda, 0x3f, 0x5a, 0x08, 0xc0, - 0x52, 0x3c, 0x53, 0x82, 0x05, 0x39, 0xc5, 0x5c, 0xfc, 0xbf, 0xe0, 0x23, 0xe6, 0x7b, 0xd2, 0x45, 0x6c, 0xbd, 0x7d, - 0x72, 0x61, 0x20, 0x81, 0xa6, 0x03, 0xf0, 0xe3, 0x55, 0x40, 0x57, 0xc6, 0xcf, 0xcf, 0xb2, 0x1e, 0xeb, 0xe3, 0x3f, - 0x05, 0xf7, 0xe9, 0x67, 0x0a, 0x1f, 0x1d, 0x8e, 0xab, 0x74, 0xb4, 0xa3, 0x14, 0x44, 0x47, 0xb7, 0xcf, 0xa7, 0x3c, - 0xfb, 0xa6, 0x02, 0x72, 0xcb, 0x51, 0x7b, 0x2a, 0x00, 0x8b, 0x2d, 0x1d, 0x81, 0x4f, 0xb3, 0x7c, 0x42, 0xbe, 0xd7, - 0x53, 0x71, 0x75, 0xa9, 0xd3, 0xc5, 0x8b, 0xf1, 0x14, 0xfe, 0x07, 0x62, 0x0f, 0xcb, 0x14, 0xd9, 0xb1, 0xeb, 0xe2, - 0x07, 0xf1, 0xb6, 0xb6, 0xa3, 0x3f, 0x76, 0x10, 0xe9, 0xb8, 0x27, 0x17, 0xea, 0x4b, 0x48, 0x25, 0x17, 0xea, 0x06, - 0x62, 0x17, 0x6a, 0xbc, 0xe3, 0x22, 0xd6, 0xfa, 0x75, 0x8d, 0x82, 0x95, 0x80, 0x33, 0xed, 0x1a, 0x0c, 0x36, 0xb0, - 0x6e, 0x59, 0x06, 0x7f, 0xc3, 0x35, 0x4d, 0xe0, 0x86, 0x45, 0xd6, 0x7b, 0x83, 0xad, 0x74, 0x0d, 0x8e, 0x96, 0x89, - 0x73, 0x29, 0xc9, 0xca, 0x16, 0x19, 0x57, 0x8f, 0x42, 0xaa, 0xa6, 0xfb, 0x5b, 0x51, 0x3f, 0x08, 0x91, 0x07, 0xab, - 0x94, 0x45, 0xc5, 0x0a, 0x64, 0xf6, 0xe0, 0x1f, 0x21, 0x23, 0x47, 0x39, 0x70, 0x14, 0xfa, 0x5b, 0x13, 0xe8, 0x3c, - 0x3f, 0x85, 0x3a, 0x8f, 0x04, 0x5b, 0xa9, 0x87, 0xc2, 0xca, 0x0b, 0x88, 0x0e, 0xb6, 0x30, 0x56, 0x79, 0x12, 0x2a, - 0x36, 0x65, 0x22, 0x8f, 0x83, 0x5a, 0x02, 0xc6, 0x0a, 0x82, 0x39, 0xcb, 0xa5, 0x0b, 0x52, 0xd5, 0xe8, 0x61, 0x91, - 0xb9, 0x9f, 0x0a, 0xca, 0xff, 0x54, 0xe5, 0x84, 0xeb, 0xcb, 0x10, 0xe0, 0x68, 0x9f, 0x82, 0x28, 0x31, 0xd6, 0x2f, - 0x5a, 0xbc, 0x93, 0x99, 0xb3, 0xa9, 0xed, 0x25, 0xc8, 0xd8, 0x0e, 0xbf, 0x42, 0x68, 0xb5, 0x50, 0x64, 0xd1, 0x70, - 0xc1, 0x74, 0x7b, 0x4a, 0xab, 0xee, 0x61, 0xc3, 0xb3, 0xd2, 0x43, 0xa5, 0xbe, 0x8d, 0x09, 0x2c, 0xab, 0x94, 0xe1, - 0xdb, 0x09, 0x55, 0x27, 0x06, 0x15, 0xeb, 0x86, 0x2d, 0xe1, 0x10, 0x8b, 0x49, 0x63, 0x9d, 0x0d, 0x78, 0xc4, 0x12, - 0xf8, 0x67, 0xc3, 0xc7, 0x6c, 0xc9, 0xa3, 0xc9, 0xe6, 0x6a, 0xd9, 0xef, 0x97, 0x5e, 0xe8, 0xd5, 0xb3, 0xec, 0x69, - 0x34, 0x9f, 0xe5, 0x73, 0x1f, 0x15, 0x17, 0x93, 0xc1, 0x60, 0xe3, 0x67, 0xc3, 0x21, 0x4b, 0x86, 0xc3, 0x49, 0xf6, - 0x14, 0x5e, 0x7b, 0xca, 0x23, 0xb5, 0xa4, 0x92, 0xab, 0x0c, 0xf6, 0xf7, 0x01, 0x8f, 0x7c, 0xd6, 0xf9, 0x69, 0xd9, - 0x74, 0xe9, 0x7e, 0x66, 0xc7, 0x5d, 0xe8, 0x0e, 0xb0, 0xf1, 0xb6, 0x41, 0x47, 0xfe, 0xf5, 0x0e, 0x29, 0x75, 0x93, - 0x01, 0xd8, 0x8d, 0x06, 0x38, 0x64, 0xaa, 0x97, 0x22, 0xab, 0x97, 0x32, 0xd5, 0x4b, 0xb2, 0x72, 0x09, 0x16, 0x12, - 0x53, 0xe5, 0x36, 0xb2, 0x72, 0xcb, 0x86, 0xeb, 0xe1, 0x60, 0x6b, 0xc5, 0x65, 0x73, 0x07, 0xf7, 0x85, 0x15, 0x05, - 0xfe, 0xdf, 0xb2, 0x05, 0xbb, 0x97, 0xc7, 0xc0, 0x35, 0x3a, 0x26, 0xc1, 0x05, 0xe2, 0x9e, 0xdd, 0x82, 0x1d, 0x16, - 0xfe, 0x82, 0xeb, 0xe4, 0x98, 0xed, 0xf0, 0x51, 0xe8, 0x15, 0xec, 0xd6, 0x27, 0xa0, 0x5d, 0xb0, 0x35, 0x40, 0x36, - 0xb6, 0xc5, 0x47, 0x77, 0x87, 0xc3, 0xb5, 0xe7, 0xb3, 0x07, 0xfc, 0x71, 0x7e, 0x77, 0x38, 0xec, 0x3c, 0xa3, 0xde, - 0xbb, 0xe1, 0x09, 0x7b, 0xcf, 0x93, 0xc9, 0xcd, 0x15, 0x8f, 0x27, 0x83, 0xc1, 0x8d, 0xbf, 0xe0, 0xf5, 0xec, 0x06, - 0xb4, 0x03, 0xe7, 0x0b, 0xa9, 0x6b, 0xf6, 0x6e, 0x79, 0xe6, 0x2d, 0x70, 0x6c, 0x6e, 0xe1, 0xe8, 0xed, 0xf7, 0xbd, - 0x3b, 0x1e, 0x79, 0xb7, 0xa4, 0x62, 0x5a, 0x71, 0xc5, 0xf1, 0xb6, 0xc5, 0xfd, 0x74, 0xc5, 0x43, 0x78, 0x84, 0x55, - 0x99, 0xde, 0x04, 0xef, 0x7d, 0xb6, 0xd2, 0x2c, 0x70, 0x0f, 0x98, 0x63, 0x4d, 0x76, 0x42, 0x33, 0xf1, 0x57, 0xd8, - 0x3f, 0x37, 0xaa, 0x7f, 0x68, 0xfe, 0x97, 0xba, 0x9f, 0xc0, 0xed, 0x8b, 0x2c, 0x48, 0xec, 0x3d, 0xbf, 0x61, 0xf7, - 0xdc, 0xb0, 0xcd, 0x9e, 0x99, 0xb2, 0x4f, 0x94, 0x1a, 0x3f, 0x52, 0xea, 0xda, 0x32, 0xac, 0x64, 0xee, 0xbe, 0x8c, - 0xc0, 0xe1, 0x80, 0xfc, 0x74, 0x87, 0x38, 0x08, 0xad, 0x9b, 0xac, 0xe6, 0x8a, 0x72, 0x2e, 0xb4, 0x65, 0xe6, 0xe5, - 0xc0, 0x62, 0x96, 0x52, 0x68, 0x2c, 0x00, 0x10, 0x4c, 0x0a, 0xad, 0xbd, 0x97, 0x01, 0xe4, 0x04, 0x0d, 0x7f, 0x6c, - 0xae, 0x8a, 0xb2, 0x96, 0x2d, 0x09, 0x51, 0xb6, 0xeb, 0xe1, 0x25, 0x42, 0xa6, 0xf5, 0xfb, 0xe7, 0x44, 0xb2, 0x36, - 0xa9, 0xae, 0x6a, 0xb4, 0x04, 0x54, 0x64, 0x09, 0x98, 0xf8, 0x95, 0xe6, 0x13, 0x80, 0x27, 0x1d, 0x0f, 0xaa, 0xa7, - 0xbc, 0x66, 0x82, 0xc8, 0x36, 0x2a, 0x7f, 0x52, 0xbc, 0x40, 0x32, 0x82, 0xe2, 0x69, 0xad, 0x32, 0x16, 0x86, 0x79, - 0xa0, 0x80, 0xbc, 0x7b, 0x77, 0xea, 0x5b, 0xfb, 0x63, 0xc7, 0x9e, 0xad, 0x55, 0xa8, 0x85, 0x9a, 0xc2, 0x25, 0x87, - 0xe8, 0x0a, 0x32, 0x50, 0xc8, 0x78, 0xf2, 0x7a, 0x70, 0x39, 0x89, 0xae, 0xb8, 0x40, 0x67, 0x7c, 0x7d, 0xd3, 0x4d, - 0x67, 0xd1, 0xd3, 0x6a, 0x3e, 0x21, 0x25, 0xd9, 0xe1, 0x90, 0x8d, 0xaa, 0xba, 0x58, 0x4f, 0x43, 0xf9, 0xd3, 0x43, - 0xf0, 0xf5, 0x82, 0x7a, 0x4d, 0x56, 0xa9, 0x7e, 0x4a, 0x95, 0xf2, 0xa2, 0xe1, 0xa5, 0xff, 0xb4, 0x92, 0xfb, 0x1e, - 0x90, 0xd6, 0xf2, 0x92, 0xcb, 0xf7, 0x23, 0xc4, 0x18, 0xf1, 0x03, 0xaf, 0xe4, 0x11, 0x0b, 0xd5, 0x14, 0xae, 0x79, - 0x84, 0x20, 0x6f, 0x99, 0x0e, 0xfe, 0xd6, 0x13, 0xa7, 0xfb, 0x13, 0xa5, 0x5d, 0x7c, 0x61, 0x51, 0xf7, 0x64, 0x6d, - 0xdd, 0x80, 0x1c, 0x6c, 0x98, 0x2e, 0x0a, 0xb2, 0x4d, 0x69, 0x04, 0x6d, 0xb4, 0x1c, 0xd8, 0x70, 0x2a, 0xb5, 0xe1, - 0xcc, 0x35, 0x04, 0xf7, 0xf9, 0x79, 0x3a, 0x5a, 0xc0, 0x87, 0x54, 0xb7, 0x97, 0xf8, 0xf9, 0xb0, 0xe1, 0x11, 0x90, - 0xd9, 0x11, 0x9f, 0xd9, 0x44, 0xd2, 0x49, 0x9d, 0x2b, 0x60, 0xb7, 0xb3, 0x6b, 0x90, 0x23, 0x66, 0xee, 0x2b, 0x54, - 0xdf, 0xa2, 0x01, 0x57, 0xc6, 0xda, 0xd7, 0x24, 0x63, 0xe1, 0x55, 0x39, 0x0d, 0x07, 0x00, 0x43, 0x97, 0xd1, 0xd7, - 0x96, 0x9b, 0x2c, 0xfb, 0xb9, 0x80, 0x20, 0x88, 0x92, 0x78, 0x7c, 0xc0, 0xfb, 0xb2, 0x1a, 0x6a, 0x94, 0x7c, 0x2c, - 0x1b, 0xa9, 0xf4, 0x4a, 0xf4, 0x77, 0x63, 0x2e, 0x31, 0xe0, 0x75, 0xd5, 0x16, 0x14, 0xce, 0xf3, 0xc3, 0xe1, 0x3c, - 0x1f, 0x19, 0xcf, 0x32, 0x50, 0xad, 0x4c, 0xeb, 0x20, 0x36, 0xf3, 0xc5, 0xc2, 0x5f, 0xec, 0x9c, 0x44, 0x44, 0x41, - 0x60, 0x47, 0xc2, 0x83, 0x48, 0xfd, 0xaa, 0xf2, 0x74, 0xa7, 0xfa, 0x6c, 0xbf, 0xb0, 0x89, 0xf4, 0x82, 0x92, 0xc9, - 0x27, 0xc1, 0x5e, 0xf5, 0x77, 0x10, 0x36, 0x84, 0x37, 0xaf, 0x7a, 0x9d, 0x65, 0x6a, 0x56, 0x82, 0x84, 0x19, 0x73, - 0x04, 0x8f, 0xc3, 0x4e, 0x63, 0x1b, 0x1e, 0x5b, 0x70, 0x74, 0xde, 0x9a, 0xdd, 0xb1, 0x15, 0xbb, 0x55, 0x75, 0x5a, - 0xf0, 0x70, 0x3a, 0xbc, 0x0c, 0x70, 0xf5, 0xad, 0xcf, 0x39, 0xbf, 0xa3, 0x13, 0x6c, 0x3d, 0xe0, 0xd1, 0x44, 0xcc, - 0xd6, 0x4f, 0x23, 0xb5, 0x78, 0xd6, 0x43, 0xbe, 0xa0, 0xf5, 0x27, 0x66, 0x77, 0x26, 0xf9, 0x6e, 0xc0, 0x17, 0x93, - 0xf5, 0xd3, 0x08, 0x5e, 0x7d, 0x0a, 0x56, 0x8c, 0xcc, 0x99, 0x65, 0xeb, 0xa7, 0x11, 0x8e, 0xd9, 0xdd, 0xd3, 0x88, - 0x46, 0x6d, 0x25, 0xf7, 0xa5, 0xdb, 0x06, 0x84, 0x95, 0x5b, 0x16, 0xc3, 0x6b, 0x20, 0x9e, 0x69, 0x23, 0xe9, 0x5a, - 0x1a, 0x7a, 0x63, 0x1e, 0x4e, 0xe3, 0x60, 0x4d, 0xad, 0x90, 0x67, 0x86, 0x98, 0xc5, 0x4f, 0xa3, 0x39, 0x5b, 0x61, - 0x45, 0x36, 0x3c, 0x1e, 0x5c, 0x4e, 0x36, 0x57, 0x7c, 0x0d, 0xe4, 0x67, 0x93, 0x8d, 0xd9, 0xa2, 0x6e, 0xb9, 0x98, - 0x6d, 0x9e, 0x46, 0xf3, 0xc9, 0x0a, 0x7a, 0xd6, 0x1e, 0x30, 0xef, 0x0d, 0x88, 0x50, 0x12, 0x52, 0x53, 0x6e, 0x7a, - 0x3d, 0xb6, 0x1e, 0x07, 0x77, 0x6c, 0x7d, 0x19, 0xdc, 0xb2, 0xf5, 0x18, 0x88, 0x38, 0xa8, 0xdf, 0xbd, 0x0d, 0x2c, - 0xbe, 0x88, 0xad, 0x2f, 0x4d, 0xda, 0xe6, 0x69, 0xc4, 0xdc, 0xc1, 0x69, 0xe0, 0x82, 0xb5, 0xc9, 0xbc, 0x15, 0x83, - 0x4b, 0xc8, 0xd2, 0x8b, 0xd9, 0x66, 0x78, 0xc9, 0xd6, 0x23, 0x9c, 0xea, 0x89, 0xcf, 0xee, 0xf8, 0x2d, 0x4b, 0xf8, - 0xaa, 0x89, 0xaf, 0x36, 0xa0, 0x11, 0x3d, 0xca, 0xa0, 0xaf, 0xa0, 0x66, 0xe6, 0xbc, 0xb2, 0x30, 0x2a, 0xf7, 0x2d, - 0x38, 0xa0, 0x20, 0x6d, 0x03, 0x04, 0x49, 0x3c, 0xbb, 0x57, 0xe1, 0xfa, 0x46, 0x0a, 0x03, 0x6e, 0x02, 0x33, 0x60, - 0x60, 0xfa, 0x19, 0xfc, 0xb0, 0xd2, 0x25, 0x42, 0x9c, 0xfd, 0x94, 0x92, 0x64, 0x9e, 0x9f, 0x8a, 0x34, 0x77, 0x0b, - 0xd7, 0x29, 0xcc, 0x8a, 0x02, 0xd5, 0x4f, 0x49, 0x69, 0x60, 0xa1, 0x12, 0x99, 0x4a, 0xc1, 0x2f, 0x9b, 0xf3, 0x28, - 0x3b, 0x46, 0xe7, 0x3a, 0xbf, 0x9c, 0x38, 0xa7, 0x93, 0xbe, 0xff, 0xc0, 0x31, 0x6c, 0x21, 0x03, 0x17, 0xfe, 0xd4, - 0x13, 0xc6, 0xa9, 0x15, 0x88, 0xa9, 0xe4, 0xd9, 0x53, 0xf8, 0x4c, 0x68, 0x75, 0x74, 0xe1, 0xfb, 0x41, 0xa1, 0x4d, - 0xd2, 0x2d, 0x48, 0x52, 0xf0, 0x14, 0x3d, 0xe7, 0xbc, 0x0d, 0x54, 0x8a, 0x11, 0x2d, 0x88, 0xb4, 0xb5, 0xcc, 0x1c, - 0xa4, 0x2d, 0xcd, 0x77, 0x4d, 0xfc, 0x1c, 0x16, 0x70, 0x11, 0x2d, 0x6c, 0x0d, 0x8f, 0xaa, 0x58, 0xb9, 0x37, 0x79, - 0x8e, 0x70, 0x46, 0x97, 0x32, 0x01, 0x70, 0xbd, 0x5f, 0x87, 0xb5, 0xc2, 0x2b, 0x6a, 0x16, 0x79, 0x51, 0xd3, 0x27, - 0x5b, 0xe0, 0x3e, 0x16, 0x25, 0x0a, 0x9c, 0xb5, 0x60, 0xc0, 0x56, 0x58, 0xb2, 0x93, 0xc2, 0xa6, 0x68, 0x09, 0xbd, - 0x3d, 0x7e, 0x3a, 0xa8, 0x99, 0x0c, 0xa0, 0x09, 0xa0, 0xf1, 0xf8, 0x17, 0x80, 0x9a, 0xde, 0xd4, 0x62, 0x5d, 0x05, - 0xa5, 0x52, 0x6e, 0xc2, 0xcf, 0xc0, 0x30, 0xc3, 0x0f, 0x85, 0xdc, 0x26, 0x4a, 0xe4, 0xfc, 0x58, 0x94, 0x62, 0x59, - 0x8a, 0x2a, 0x69, 0x37, 0x14, 0x3c, 0x22, 0xdc, 0x06, 0x8d, 0x99, 0xdb, 0x13, 0x5d, 0xb4, 0x22, 0x94, 0x63, 0xb3, - 0x8e, 0x91, 0x46, 0x99, 0x9d, 0xec, 0x3a, 0x59, 0x68, 0xbf, 0xaf, 0x72, 0xc8, 0x3a, 0x60, 0x8d, 0xe4, 0xeb, 0x35, - 0x87, 0x6e, 0x1b, 0xe5, 0xc5, 0x83, 0xe7, 0x2b, 0x38, 0xcd, 0xf1, 0xc4, 0xee, 0x7a, 0xdd, 0x29, 0x12, 0xf1, 0x0a, - 0x27, 0x55, 0x3e, 0x92, 0x85, 0xe3, 0xce, 0x9d, 0xd6, 0x62, 0x55, 0xb9, 0xac, 0xa7, 0x16, 0x47, 0x04, 0x3e, 0x95, - 0x47, 0x7b, 0xa1, 0x6d, 0x51, 0x2c, 0x84, 0xd1, 0xa3, 0x13, 0x7e, 0x52, 0x02, 0xeb, 0xeb, 0x70, 0x58, 0xfa, 0x11, - 0x47, 0xbf, 0xd3, 0x68, 0xb4, 0x20, 0xa4, 0xe1, 0xa9, 0x17, 0x8d, 0x16, 0x75, 0x51, 0x87, 0xd9, 0x8b, 0x5c, 0x0f, - 0x14, 0x86, 0x11, 0xa8, 0x1f, 0x5c, 0x65, 0xf0, 0x59, 0x84, 0xa8, 0x79, 0x60, 0x9a, 0x0d, 0xe1, 0xa8, 0x0b, 0x3c, - 0xb4, 0x82, 0x16, 0x33, 0xf3, 0x51, 0x88, 0xe1, 0x43, 0xba, 0x38, 0x7f, 0x42, 0x56, 0x3e, 0xc0, 0xee, 0xd0, 0x5d, - 0x28, 0xe7, 0x4c, 0xc5, 0x00, 0x3f, 0x0a, 0xc8, 0x47, 0x09, 0xb8, 0x19, 0x20, 0x7b, 0x64, 0x09, 0x20, 0x56, 0x8c, - 0x8e, 0x26, 0x9f, 0xfb, 0x5e, 0xa4, 0xe0, 0x9d, 0x7d, 0x96, 0xab, 0x09, 0x43, 0xe1, 0x13, 0x03, 0xdd, 0xfc, 0xc6, - 0x6f, 0xcf, 0x5b, 0x30, 0xb2, 0x4b, 0x52, 0xbc, 0xd6, 0x0c, 0xf7, 0x1b, 0x70, 0x3b, 0x02, 0xca, 0x9a, 0xea, 0x98, - 0x64, 0x9b, 0x86, 0x48, 0x06, 0xcc, 0x88, 0x11, 0x41, 0x65, 0xb9, 0xf0, 0xbf, 0x7b, 0x59, 0x14, 0x38, 0x80, 0xab, - 0x99, 0x0c, 0x5e, 0xbb, 0x30, 0x2a, 0x00, 0xce, 0x69, 0xe8, 0x94, 0xf6, 0xaa, 0xea, 0x90, 0xac, 0x9a, 0x1f, 0xcc, - 0xe6, 0x4d, 0xc3, 0xc4, 0x88, 0x20, 0xba, 0x08, 0x27, 0x98, 0x5e, 0x91, 0xbe, 0x56, 0x72, 0x3a, 0x5a, 0x75, 0xb4, - 0x96, 0x98, 0x98, 0x2b, 0x8a, 0xbf, 0x06, 0x3c, 0x6e, 0xf0, 0xea, 0x24, 0x4d, 0x27, 0xaa, 0x47, 0x8f, 0x5f, 0xa7, - 0xe9, 0xa4, 0xc4, 0x5d, 0xe1, 0x37, 0xe0, 0xa2, 0xd9, 0xe6, 0x43, 0x3f, 0x7e, 0x41, 0x11, 0x17, 0x35, 0xb8, 0xf2, - 0x4e, 0xf5, 0x95, 0xea, 0x23, 0xa8, 0x85, 0x27, 0x46, 0xd6, 0xc2, 0x93, 0x4b, 0xd6, 0x5a, 0x10, 0xcc, 0x6c, 0x0e, - 0x5c, 0xc8, 0xaf, 0x94, 0x22, 0xde, 0x44, 0x42, 0x2d, 0x06, 0xad, 0xc7, 0xcc, 0x59, 0x35, 0x5a, 0xa8, 0xcc, 0x08, - 0xed, 0xdb, 0x5a, 0x74, 0x7e, 0x23, 0x3f, 0xe5, 0xa9, 0x7d, 0xd9, 0x1e, 0xe7, 0xe3, 0x3d, 0xba, 0xab, 0xce, 0x32, - 0x93, 0x32, 0x3e, 0x99, 0x25, 0x28, 0xdc, 0x25, 0xd8, 0x80, 0x24, 0xfb, 0xb5, 0x0e, 0x90, 0x51, 0x7b, 0xed, 0x77, - 0x9d, 0xe5, 0xab, 0x9b, 0xad, 0xa1, 0xa8, 0xd4, 0x4a, 0x52, 0x1c, 0x64, 0xb8, 0x6e, 0x2b, 0x1f, 0x2e, 0x2e, 0xa0, - 0x67, 0x8c, 0x44, 0xe6, 0xf9, 0x13, 0xf9, 0x12, 0x9c, 0x33, 0xce, 0x0a, 0x81, 0x09, 0x63, 0xf5, 0xae, 0xb5, 0x54, - 0x1a, 0x52, 0x8c, 0x1d, 0x8d, 0xb2, 0xac, 0xb2, 0x74, 0x99, 0xad, 0x25, 0x6c, 0x59, 0x4e, 0x6e, 0x61, 0xcb, 0x4c, - 0x56, 0xf3, 0x7d, 0xc5, 0x1d, 0x94, 0x6f, 0xb6, 0xce, 0xf8, 0x5e, 0x22, 0x7b, 0xb7, 0x81, 0x12, 0x5e, 0x8c, 0xfe, - 0x82, 0xf4, 0xdb, 0x0c, 0xe3, 0x94, 0xdb, 0x4a, 0x5a, 0x80, 0xd3, 0x3f, 0x1c, 0xde, 0x57, 0x18, 0x34, 0x38, 0xc2, - 0x38, 0xb2, 0x7e, 0xff, 0xb6, 0xf2, 0x6a, 0x4c, 0xd4, 0xf1, 0x59, 0xfd, 0x7e, 0x45, 0x0f, 0xa7, 0xd5, 0x68, 0x95, - 0x6e, 0x91, 0x9d, 0xd0, 0xc6, 0xca, 0x0f, 0x6a, 0x05, 0xcc, 0xde, 0xfa, 0x7c, 0x3a, 0x00, 0x1d, 0x0b, 0x90, 0x68, - 0x36, 0x13, 0x89, 0x39, 0xe9, 0x9e, 0x84, 0xc7, 0x07, 0x16, 0x38, 0xc0, 0x54, 0xfc, 0x9f, 0xc2, 0x9b, 0x81, 0x0d, - 0x1a, 0x25, 0xfa, 0x1a, 0x5d, 0xd5, 0xe6, 0x46, 0xc7, 0x4b, 0x4f, 0x21, 0x91, 0x15, 0xac, 0x9a, 0xfb, 0x72, 0x03, - 0xa7, 0x3d, 0xd4, 0x1c, 0x2a, 0x4b, 0xf0, 0xb7, 0x5f, 0xe6, 0x87, 0xc3, 0x3a, 0x83, 0xc2, 0x76, 0x6b, 0xa1, 0xbd, - 0x31, 0x4b, 0x35, 0x54, 0x84, 0x83, 0xce, 0x57, 0x62, 0x56, 0x8f, 0xe8, 0xef, 0xf9, 0xe1, 0xb0, 0x22, 0x30, 0xe0, - 0xb0, 0x94, 0x99, 0x68, 0xa1, 0x58, 0x5a, 0x67, 0x33, 0xaa, 0x03, 0x0f, 0x4c, 0xcc, 0x59, 0xb8, 0x03, 0xd0, 0x26, - 0xb5, 0x0a, 0xf4, 0x2a, 0xa2, 0x9f, 0xb8, 0x5f, 0xdb, 0xaf, 0xd7, 0x23, 0xb3, 0x74, 0xe4, 0xc6, 0x58, 0x00, 0x70, - 0xe0, 0x79, 0x4d, 0xf2, 0x9c, 0x7c, 0x0d, 0xed, 0x9e, 0x5c, 0xc8, 0x9f, 0xa0, 0x6c, 0xe1, 0xb9, 0x6a, 0x5a, 0x59, - 0xac, 0xb8, 0xaa, 0x5e, 0x5d, 0xf0, 0xca, 0x64, 0x5a, 0xa5, 0x95, 0xa8, 0x94, 0x60, 0x40, 0x5d, 0xe2, 0xb5, 0xa6, - 0x19, 0xa5, 0x36, 0xea, 0x4c, 0xd4, 0x80, 0x0d, 0xf6, 0x53, 0xb5, 0xd1, 0xc9, 0xb9, 0x7c, 0x7e, 0x69, 0x1c, 0x3e, - 0xed, 0xea, 0xcd, 0x4c, 0xe5, 0xc0, 0x5f, 0x2b, 0x1f, 0x5a, 0x3d, 0x06, 0x3a, 0x20, 0xa7, 0x3f, 0x86, 0xc5, 0xc4, - 0xee, 0xd0, 0xbc, 0xdd, 0x5d, 0x56, 0x17, 0xe9, 0x9d, 0xa6, 0x64, 0x56, 0x6f, 0xf9, 0xcc, 0xea, 0xd1, 0x01, 0x2f, - 0x1e, 0xeb, 0xbd, 0xc2, 0x4c, 0x22, 0xb8, 0x18, 0xaa, 0x49, 0x64, 0x77, 0xa0, 0x35, 0x8f, 0x2a, 0x26, 0xc0, 0x0f, - 0x4a, 0xad, 0xe9, 0xbd, 0xdd, 0x15, 0xea, 0x94, 0xc2, 0xe3, 0xd6, 0x92, 0x1f, 0x98, 0x3b, 0xed, 0x5a, 0xe7, 0xe3, - 0xf9, 0xa5, 0xef, 0x37, 0xf2, 0x84, 0x36, 0x3b, 0x93, 0xd3, 0x3f, 0x79, 0xab, 0x7f, 0x98, 0xea, 0x5b, 0xe8, 0x4e, - 0xd0, 0x67, 0xe8, 0xaa, 0xea, 0xae, 0xc4, 0x16, 0x86, 0x7a, 0x62, 0x91, 0x17, 0xf2, 0xa4, 0x35, 0x76, 0x1c, 0xec, - 0x0d, 0x70, 0xe2, 0x97, 0x87, 0x83, 0xb8, 0xca, 0x7d, 0x76, 0xde, 0x35, 0xb2, 0x72, 0x00, 0x2b, 0x88, 0x82, 0x71, - 0x6b, 0x3e, 0xb6, 0x41, 0xba, 0xc4, 0xd5, 0xf8, 0xf8, 0x0d, 0xc5, 0x32, 0xd9, 0x44, 0x5c, 0x5c, 0xe4, 0x4f, 0x9f, - 0x03, 0x69, 0x59, 0xbf, 0x1f, 0xbd, 0xb8, 0x9c, 0x3e, 0x1f, 0x46, 0x01, 0x38, 0x76, 0xd9, 0xcb, 0xcb, 0x98, 0xaf, - 0x2e, 0x99, 0x65, 0x0a, 0x8b, 0x7c, 0x33, 0xa0, 0xba, 0x64, 0xb5, 0x74, 0xbd, 0x02, 0x2c, 0x5d, 0x7e, 0xf3, 0x10, - 0xa6, 0x06, 0x34, 0xb2, 0xe6, 0xee, 0x34, 0xd7, 0x02, 0xa5, 0x9e, 0xf7, 0x33, 0x43, 0xbe, 0x2e, 0x83, 0xae, 0x20, - 0xdd, 0xf3, 0x88, 0xf4, 0x72, 0x2f, 0x9d, 0xee, 0xf7, 0xa5, 0x00, 0x4b, 0x7d, 0x29, 0x3e, 0x83, 0xc2, 0xa2, 0xf1, - 0x8d, 0x00, 0x6d, 0x0d, 0xd5, 0xb4, 0x57, 0x8a, 0xaa, 0x17, 0xf4, 0x4a, 0xf1, 0xb9, 0xa7, 0x87, 0xca, 0x7c, 0x59, - 0x3a, 0xfa, 0x9f, 0x50, 0x73, 0xc1, 0x09, 0x31, 0x13, 0x73, 0x00, 0x95, 0xa0, 0x8d, 0x6f, 0x75, 0xb4, 0xf1, 0xa9, - 0x5e, 0xc5, 0x4d, 0x9f, 0xd7, 0xd6, 0x32, 0x27, 0x84, 0x4d, 0xf7, 0x12, 0xa0, 0x22, 0xaf, 0x84, 0x47, 0xb0, 0xfc, - 0xf2, 0x87, 0x3c, 0x5d, 0x21, 0x5a, 0xc7, 0x3d, 0xcb, 0x5c, 0x1a, 0xfb, 0x37, 0x06, 0xd3, 0xd7, 0xb7, 0xdb, 0x22, - 0x3f, 0x35, 0x31, 0x61, 0x3d, 0x56, 0xf4, 0xcd, 0xbb, 0x70, 0x25, 0x50, 0xe0, 0x50, 0x22, 0xb1, 0x4d, 0x15, 0x8a, - 0x78, 0x90, 0xf4, 0xe9, 0xa2, 0xf5, 0x69, 0x80, 0xa9, 0xb5, 0x1c, 0x98, 0x43, 0xb8, 0x8a, 0x0b, 0x1f, 0x3d, 0x7d, - 0x8b, 0x59, 0x38, 0x9f, 0x78, 0x1f, 0xbd, 0x62, 0x64, 0x3e, 0xee, 0xa3, 0x52, 0x49, 0xff, 0x3c, 0x1c, 0x66, 0xd5, - 0xdc, 0x77, 0xe8, 0x23, 0x3d, 0x54, 0xb9, 0xa0, 0xec, 0x8d, 0x31, 0x89, 0x40, 0x69, 0x8c, 0xf7, 0x71, 0x70, 0x9c, - 0xf7, 0x69, 0x00, 0xa9, 0x7d, 0xe2, 0x3d, 0x29, 0x39, 0x3c, 0xe7, 0x98, 0x13, 0x4a, 0x2b, 0xc2, 0x2a, 0xbe, 0xc8, - 0x50, 0xae, 0x3b, 0xa5, 0x60, 0x92, 0x43, 0x82, 0xe1, 0xaf, 0x9a, 0x37, 0xb1, 0x02, 0x61, 0xd7, 0xcc, 0xab, 0xd1, - 0x93, 0x2a, 0x09, 0x4b, 0x01, 0x47, 0x65, 0xe6, 0x19, 0xf6, 0x86, 0x27, 0x86, 0x91, 0x83, 0xe5, 0xfe, 0xa8, 0x4e, - 0x44, 0xee, 0xd1, 0x05, 0x46, 0x65, 0xe1, 0x79, 0x43, 0x57, 0x1a, 0x54, 0x92, 0x1d, 0x7f, 0xc5, 0x35, 0xa0, 0xb6, - 0xc6, 0x88, 0xa1, 0x80, 0x51, 0xf0, 0xda, 0xfe, 0x10, 0xb2, 0x28, 0x5b, 0xbf, 0xc1, 0x31, 0x9f, 0x95, 0xdc, 0xf5, - 0x0e, 0x67, 0xa1, 0x25, 0xe4, 0xc9, 0x1d, 0x83, 0x34, 0x8d, 0xa5, 0x11, 0x70, 0x22, 0x92, 0x6d, 0x2c, 0x85, 0x23, - 0x80, 0x80, 0x40, 0x37, 0x65, 0x86, 0x31, 0x1d, 0x8c, 0x3c, 0x4f, 0x7a, 0xc6, 0x7b, 0x15, 0x9e, 0x42, 0x9a, 0x6c, - 0x5f, 0xcf, 0xdf, 0x1b, 0x41, 0x56, 0x6e, 0x39, 0xc7, 0xc3, 0xe2, 0x1b, 0x67, 0x5f, 0xe5, 0xe4, 0x29, 0x66, 0x19, - 0xe9, 0x9d, 0x62, 0x5e, 0xc0, 0x9f, 0xca, 0x52, 0x9f, 0xa3, 0xf4, 0x96, 0xf9, 0x64, 0x15, 0x49, 0x97, 0xde, 0xa6, - 0xdf, 0x8f, 0x47, 0xea, 0x50, 0xf3, 0xf7, 0xf1, 0x48, 0x9e, 0x61, 0x1b, 0x96, 0xb0, 0xd0, 0x2a, 0x18, 0x03, 0x48, - 0x62, 0x23, 0xa2, 0xc1, 0x68, 0x6f, 0x0e, 0x87, 0xf3, 0x8d, 0x39, 0x4b, 0xf6, 0xe0, 0xfa, 0xca, 0x13, 0xf3, 0x0e, - 0x7c, 0x99, 0xc7, 0x04, 0x11, 0x9b, 0x79, 0x1b, 0x56, 0x83, 0x07, 0x3b, 0xb8, 0x3e, 0x62, 0x8b, 0x62, 0xad, 0x63, - 0xa9, 0xac, 0x83, 0xd3, 0x3a, 0x36, 0xcd, 0x48, 0x29, 0xb2, 0xcf, 0xb1, 0xbf, 0x77, 0x83, 0xab, 0x6b, 0x63, 0x50, - 0x6b, 0xdc, 0x61, 0xee, 0x9c, 0x0a, 0xa8, 0xc7, 0x74, 0x05, 0xd5, 0xb3, 0x9c, 0x7c, 0xf9, 0xad, 0x9d, 0x03, 0x82, - 0x46, 0x20, 0x70, 0xd1, 0x40, 0xab, 0x76, 0x29, 0xe7, 0x5d, 0x40, 0x88, 0x6f, 0x52, 0xd0, 0xa7, 0x33, 0xd8, 0xc4, - 0xe6, 0x13, 0x88, 0x45, 0xd3, 0x7d, 0xae, 0x35, 0xf3, 0xc5, 0x88, 0x76, 0x66, 0xdd, 0x2d, 0x72, 0xab, 0x85, 0x48, - 0x46, 0xcf, 0x36, 0x13, 0x2e, 0x3a, 0x94, 0x33, 0x12, 0x30, 0x41, 0x6b, 0x2b, 0x25, 0x9f, 0xeb, 0x5e, 0x27, 0x68, - 0x0f, 0x24, 0xad, 0xfb, 0x37, 0x8b, 0xce, 0x28, 0x39, 0xb9, 0xde, 0xe4, 0x0c, 0x52, 0xb0, 0x60, 0x7b, 0x99, 0x13, - 0x6e, 0x80, 0x4f, 0x6c, 0x96, 0x9c, 0xa6, 0x41, 0x1e, 0x0b, 0xe3, 0x91, 0xd7, 0xe6, 0x97, 0x05, 0x74, 0x28, 0x59, - 0x34, 0x42, 0x3c, 0xc0, 0xce, 0x21, 0xb9, 0x2a, 0x50, 0x37, 0x0d, 0x74, 0xe5, 0xca, 0x99, 0x62, 0x0a, 0x5c, 0x08, - 0x05, 0x51, 0x3b, 0x3a, 0x89, 0xca, 0x79, 0x9f, 0x54, 0x97, 0xf9, 0xb4, 0x90, 0xa6, 0x81, 0x7c, 0x5a, 0x39, 0xe6, - 0x81, 0x9d, 0x6d, 0x5c, 0x13, 0x18, 0xe8, 0xd4, 0xbe, 0x16, 0xe5, 0x1c, 0xab, 0x88, 0xde, 0xe7, 0x1f, 0x2a, 0x7b, - 0xfa, 0x20, 0xc2, 0x46, 0x05, 0x1a, 0x4b, 0x89, 0xb1, 0x91, 0xe3, 0xdf, 0x12, 0x65, 0x43, 0x86, 0x80, 0x10, 0xd2, - 0x46, 0x4e, 0x3f, 0xac, 0x2f, 0x6f, 0x33, 0xed, 0xff, 0x49, 0xe2, 0xb7, 0xc1, 0x5e, 0x4e, 0xfd, 0xa9, 0x47, 0x3c, - 0x5e, 0x6b, 0xf4, 0x98, 0x92, 0x6e, 0x83, 0x3c, 0x55, 0x9e, 0x82, 0x64, 0xc2, 0x58, 0x42, 0xb0, 0x28, 0x17, 0x3c, - 0xe7, 0x15, 0x97, 0x70, 0x1f, 0xb5, 0xac, 0x88, 0x50, 0x95, 0xc8, 0xe9, 0xf3, 0x15, 0xf0, 0x4c, 0x40, 0xa0, 0x63, - 0x8c, 0x34, 0xaa, 0xe0, 0x4b, 0x60, 0xac, 0x03, 0x65, 0xa7, 0x19, 0x09, 0x2e, 0xbb, 0x37, 0x48, 0x94, 0xfa, 0x9a, - 0x94, 0xa4, 0xd7, 0xa2, 0xc6, 0x2b, 0xb1, 0x8a, 0x48, 0x20, 0x43, 0x0d, 0x11, 0xab, 0xea, 0xa9, 0x7b, 0x55, 0x4c, - 0x06, 0x83, 0xca, 0x97, 0xd3, 0x13, 0x6f, 0x68, 0xa8, 0xbc, 0xeb, 0x8a, 0x76, 0x7a, 0xa2, 0x95, 0xf2, 0x16, 0xd2, - 0x12, 0x34, 0x0d, 0x23, 0xcd, 0xa1, 0xd4, 0x95, 0x74, 0x37, 0x06, 0xf1, 0x25, 0x13, 0x3d, 0xdb, 0xa9, 0x1d, 0xa5, - 0x2d, 0x69, 0x0f, 0x21, 0x3d, 0x77, 0xc9, 0xc7, 0x2c, 0xe4, 0xea, 0x4e, 0x39, 0x29, 0xaf, 0x42, 0x74, 0x72, 0xdf, - 0x63, 0x48, 0x04, 0xfa, 0x9c, 0x63, 0x58, 0x17, 0x0d, 0x75, 0x0e, 0x2b, 0xc4, 0x6c, 0xa1, 0x84, 0xf9, 0x92, 0xf1, - 0x54, 0x32, 0x68, 0x00, 0x64, 0xc0, 0x67, 0x2f, 0x03, 0xcb, 0x5f, 0x41, 0xfc, 0x68, 0xe3, 0xc3, 0xe1, 0xcf, 0x9a, - 0x42, 0x6c, 0xff, 0x84, 0xcd, 0x10, 0x1e, 0xd5, 0x03, 0x9e, 0xf9, 0x26, 0x4e, 0xd0, 0x0a, 0x48, 0xca, 0xec, 0x68, - 0x22, 0x7b, 0xd5, 0x43, 0x38, 0x95, 0x15, 0xa8, 0xa3, 0xac, 0xb3, 0x12, 0x7e, 0x84, 0xa9, 0x6e, 0x25, 0xd6, 0x02, - 0x6d, 0xae, 0x56, 0xac, 0x05, 0x70, 0xe0, 0xe7, 0x10, 0x3c, 0x91, 0xcf, 0xc1, 0xc5, 0xa0, 0x00, 0x9f, 0x03, 0xe0, - 0x45, 0xee, 0xc2, 0x83, 0x79, 0x64, 0x59, 0x8d, 0x30, 0x1c, 0x55, 0xc4, 0xfa, 0x35, 0xdb, 0x91, 0x0f, 0xdc, 0x8e, - 0xf1, 0xb9, 0xf6, 0x58, 0xb2, 0x1c, 0x8c, 0x32, 0xf7, 0x6a, 0x89, 0x9e, 0x37, 0x69, 0xdc, 0x8c, 0x9e, 0xec, 0x6b, - 0xf9, 0xbf, 0xa0, 0x97, 0x41, 0x7f, 0x0b, 0xb7, 0xbc, 0xe6, 0x77, 0x0b, 0x22, 0xcd, 0xf4, 0x0a, 0x22, 0x65, 0xd4, - 0x88, 0x8c, 0x21, 0x6c, 0x52, 0xdd, 0xdc, 0x26, 0xd5, 0x85, 0x80, 0xa7, 0x23, 0x52, 0x5d, 0x0b, 0x69, 0x23, 0x9f, - 0xd6, 0x81, 0x8c, 0x45, 0x7a, 0xf7, 0xc3, 0xdf, 0x5e, 0x7e, 0x7a, 0xfb, 0xcb, 0x0f, 0x8b, 0xb7, 0xef, 0xde, 0xbc, - 0x7d, 0xf7, 0xf6, 0xd3, 0x6f, 0x04, 0xe1, 0x31, 0x15, 0x2a, 0xc3, 0x87, 0xf7, 0x37, 0x6f, 0x9d, 0x0c, 0xb6, 0x37, - 0x43, 0xd6, 0xbe, 0x91, 0x83, 0x21, 0x10, 0xd9, 0x20, 0x64, 0x90, 0x9d, 0xda, 0xf6, 0x67, 0x62, 0x8e, 0xb1, 0x77, - 0x02, 0x93, 0x2d, 0xe0, 0x1c, 0xcb, 0xbc, 0x64, 0x44, 0xae, 0x0a, 0xad, 0x1f, 0xd0, 0x82, 0x6b, 0x70, 0x91, 0x49, - 0xf3, 0xbb, 0x5f, 0x08, 0x62, 0x9f, 0x56, 0x52, 0xee, 0xab, 0x6d, 0xcd, 0xf3, 0xed, 0xfd, 0x5e, 0xc2, 0xf9, 0xcf, - 0xa5, 0x11, 0xb5, 0x00, 0x07, 0xe0, 0x73, 0xf8, 0xe3, 0x4a, 0x5b, 0xd2, 0x64, 0x16, 0xed, 0x67, 0x0c, 0x41, 0x97, - 0x06, 0x1f, 0xc4, 0x1e, 0x79, 0xa9, 0x4f, 0x16, 0x12, 0xb8, 0x23, 0x86, 0x4f, 0x2b, 0x82, 0x5e, 0x31, 0xa2, 0xb8, - 0xe4, 0x0a, 0x95, 0x52, 0xf2, 0x6f, 0x94, 0x5d, 0x54, 0xc8, 0x59, 0xc1, 0xee, 0x15, 0x39, 0x32, 0x7e, 0x10, 0x4c, - 0x7c, 0x39, 0xb8, 0xff, 0x12, 0xef, 0x70, 0xa6, 0x38, 0x92, 0x13, 0xfe, 0x90, 0x61, 0x60, 0x7f, 0x0e, 0x3e, 0xaf, - 0x0e, 0xf3, 0xf2, 0x46, 0x9f, 0x72, 0x4b, 0x3e, 0x9e, 0x2c, 0xaf, 0xc0, 0x60, 0xbf, 0x54, 0xcd, 0x5d, 0xf3, 0x7a, - 0xb6, 0x9c, 0xb3, 0xfd, 0x2c, 0x9a, 0x07, 0x77, 0x6c, 0x96, 0xcd, 0x83, 0x55, 0xc3, 0xd7, 0xec, 0x96, 0xaf, 0xad, - 0xaa, 0xad, 0xed, 0xaa, 0x4d, 0x36, 0xfc, 0x16, 0x24, 0x84, 0x9b, 0xcc, 0x03, 0xde, 0xe3, 0x3b, 0x9f, 0x6d, 0x40, - 0xa2, 0x5d, 0xb1, 0x0d, 0x5c, 0xc4, 0xd6, 0xfc, 0x87, 0xca, 0xdb, 0xb0, 0x92, 0x9d, 0x8f, 0x59, 0x8e, 0xf3, 0xcf, - 0x87, 0x07, 0xb4, 0x17, 0xea, 0x67, 0x97, 0xea, 0xd9, 0x44, 0xd9, 0xcd, 0x36, 0xa3, 0xc5, 0x7d, 0x5a, 0x6d, 0xc2, - 0x0c, 0x3d, 0xcb, 0xe1, 0xa3, 0xad, 0x14, 0xfc, 0xf4, 0x02, 0xbf, 0x64, 0x47, 0x6d, 0xa5, 0x6d, 0xbb, 0x2a, 0xb1, - 0x15, 0xb4, 0x28, 0xb2, 0x5a, 0xe1, 0x81, 0x39, 0x7f, 0x01, 0x0b, 0x18, 0x7b, 0x8e, 0x73, 0x5e, 0xfb, 0x23, 0x64, - 0xbc, 0x77, 0x00, 0xd0, 0x32, 0xc7, 0x01, 0x1e, 0xb1, 0x62, 0x14, 0x0d, 0xde, 0xf9, 0xa5, 0xb2, 0x5a, 0x69, 0x4e, - 0x42, 0xdb, 0x88, 0x55, 0xcb, 0x91, 0xaa, 0x19, 0x91, 0x3e, 0x48, 0xcf, 0xfb, 0x1e, 0x51, 0x0d, 0xf6, 0x64, 0x5e, - 0x07, 0xf6, 0xe9, 0x55, 0x6b, 0x55, 0x77, 0x7e, 0x4f, 0x95, 0x2e, 0x39, 0xb2, 0xe5, 0xa7, 0xcb, 0xf0, 0x41, 0xfd, - 0x29, 0xb9, 0x3e, 0x14, 0x38, 0xc2, 0x63, 0x15, 0x70, 0xbe, 0x5e, 0x89, 0x76, 0x27, 0xc2, 0xae, 0x5c, 0x02, 0x42, - 0x7c, 0x49, 0xd3, 0x1c, 0x8f, 0x23, 0x9a, 0x88, 0xb0, 0x89, 0xd1, 0x5f, 0xd8, 0x7d, 0x28, 0xb1, 0x9c, 0xe7, 0x1a, - 0x94, 0x5c, 0x32, 0x78, 0x4f, 0xda, 0x6b, 0xd0, 0x2c, 0xaf, 0x4a, 0x4d, 0x26, 0x72, 0x50, 0x3e, 0x1c, 0x0a, 0xd8, - 0x4b, 0x8d, 0x9f, 0x26, 0xfc, 0x84, 0xe5, 0xad, 0xbd, 0x35, 0xa5, 0xa8, 0xa4, 0x01, 0x2a, 0xf0, 0x31, 0x83, 0xff, - 0xdd, 0x19, 0x62, 0xc1, 0x14, 0x1d, 0x3f, 0x9c, 0x89, 0xb9, 0xf5, 0xdc, 0x2a, 0xeb, 0x28, 0x5b, 0xa3, 0x9c, 0x80, - 0x7f, 0x4b, 0x75, 0x9c, 0x24, 0xc2, 0xa9, 0xf7, 0x88, 0x8b, 0xba, 0x97, 0x43, 0xd4, 0x0d, 0x7b, 0x5b, 0xe9, 0x60, - 0xcb, 0x69, 0x1a, 0x1c, 0x89, 0x5f, 0xa9, 0xcf, 0xde, 0x67, 0x16, 0x8f, 0x3a, 0xb2, 0x11, 0x25, 0x69, 0x1c, 0x8b, - 0x1c, 0xb6, 0xf7, 0x85, 0xdc, 0xff, 0xfb, 0x7d, 0x08, 0x27, 0xad, 0x82, 0xa4, 0xf4, 0x04, 0x22, 0xc2, 0xd1, 0xe1, - 0x47, 0x84, 0x27, 0x52, 0x55, 0xf8, 0xa4, 0x3e, 0x71, 0x63, 0x76, 0x2f, 0xcc, 0x51, 0xbd, 0x05, 0x18, 0xc6, 0x7a, - 0x6b, 0x11, 0x92, 0x68, 0xa5, 0x19, 0x6d, 0x3d, 0x20, 0x46, 0xbc, 0x5f, 0x5b, 0x64, 0x30, 0xd6, 0x96, 0x44, 0x02, - 0xf8, 0x1d, 0x09, 0x19, 0xda, 0x36, 0x02, 0x33, 0x86, 0xb7, 0xb3, 0xe2, 0xd2, 0x75, 0xd8, 0xe6, 0x1c, 0xbe, 0x90, - 0x85, 0x66, 0x1d, 0x51, 0x9a, 0x20, 0xe4, 0x1f, 0x70, 0xb2, 0x50, 0x18, 0xcd, 0xeb, 0xa3, 0x74, 0x92, 0x58, 0xdf, - 0x77, 0x95, 0x0a, 0x36, 0x9b, 0x1b, 0xd4, 0x97, 0x1d, 0x25, 0xbf, 0x02, 0x27, 0x1d, 0x27, 0x59, 0xe4, 0x20, 0x6a, - 0x51, 0x39, 0x37, 0x49, 0x58, 0xda, 0xd5, 0xa9, 0x36, 0xeb, 0x75, 0x51, 0xd6, 0xd5, 0x6b, 0x11, 0x29, 0x7a, 0x1f, - 0xf5, 0xe8, 0x89, 0x84, 0x54, 0x68, 0x55, 0x6a, 0x97, 0x47, 0xe0, 0xb6, 0xa9, 0x15, 0xdb, 0x72, 0x09, 0x4b, 0xd4, - 0xf8, 0x4f, 0xd0, 0x47, 0xb9, 0x78, 0x90, 0x01, 0x1a, 0x1d, 0x4f, 0xcd, 0x5b, 0x8f, 0xbc, 0x72, 0x94, 0x5f, 0x5a, - 0x6d, 0xd2, 0x2f, 0x80, 0xcc, 0x68, 0xff, 0x68, 0x29, 0x81, 0xcc, 0xc0, 0x4c, 0x5a, 0x1a, 0x12, 0x39, 0x8a, 0x59, - 0x9a, 0xff, 0x81, 0x2b, 0xb6, 0x42, 0xa4, 0x61, 0x35, 0xf7, 0xf8, 0xcb, 0xca, 0xab, 0xe5, 0x5a, 0x66, 0x9a, 0x9b, - 0x25, 0x8e, 0x15, 0x8b, 0x8b, 0x7a, 0x5d, 0x89, 0x2c, 0x10, 0xe2, 0x08, 0xd3, 0x58, 0x4f, 0xbd, 0x51, 0x5a, 0x7d, - 0x40, 0x42, 0x99, 0x1f, 0xb0, 0xb7, 0x63, 0xaf, 0x07, 0x59, 0x88, 0x63, 0xcb, 0xc1, 0x66, 0xeb, 0x7d, 0x2a, 0x53, - 0x11, 0x9f, 0xd5, 0xc5, 0xd9, 0xa6, 0x12, 0x67, 0x75, 0x22, 0xce, 0xbe, 0x83, 0x9c, 0xdf, 0x9d, 0x51, 0xd1, 0x67, - 0x0f, 0x69, 0x9d, 0x14, 0x9b, 0x9a, 0x9e, 0xbc, 0xc1, 0x32, 0xbe, 0x3b, 0x23, 0xae, 0x9a, 0x33, 0x1a, 0xc9, 0x78, - 0x74, 0xf6, 0x21, 0x03, 0x92, 0xd7, 0xb3, 0x74, 0x05, 0x83, 0x77, 0x16, 0xe6, 0xf1, 0x59, 0x29, 0xee, 0xc0, 0xe2, - 0x54, 0x76, 0xbe, 0x07, 0x19, 0x56, 0xe1, 0x1f, 0xe2, 0x0c, 0xa0, 0x5d, 0xcf, 0xd2, 0xfa, 0x2c, 0xad, 0xce, 0xf2, - 0xa2, 0x3e, 0x53, 0x52, 0x38, 0x84, 0xf1, 0xc3, 0x7b, 0xfa, 0xca, 0x2e, 0x6f, 0xb3, 0xb8, 0xcb, 0x22, 0x7f, 0x8a, - 0x5e, 0x45, 0xc4, 0xa4, 0x51, 0x09, 0xaf, 0xdd, 0xdf, 0x36, 0xf7, 0x0f, 0xaf, 0x1b, 0xbb, 0x9f, 0xdd, 0x31, 0xa2, - 0x0b, 0xea, 0xf1, 0x4a, 0x52, 0x2a, 0x28, 0x20, 0x70, 0xa2, 0x59, 0xe3, 0xc1, 0x1d, 0x07, 0xbc, 0x1a, 0xd8, 0x92, - 0xad, 0x7d, 0xfe, 0x22, 0x96, 0x61, 0xda, 0x9b, 0x00, 0xff, 0x2a, 0x7b, 0xd3, 0x75, 0xb0, 0xc4, 0xfb, 0x16, 0xb2, - 0x0d, 0xbd, 0x7d, 0xcd, 0x5f, 0x7a, 0xb9, 0xfa, 0x9b, 0xfd, 0x13, 0x80, 0x30, 0x20, 0x66, 0xd5, 0x47, 0x13, 0xf7, - 0xce, 0xca, 0xb2, 0x73, 0xb2, 0xec, 0x7a, 0xe8, 0xd7, 0x24, 0x46, 0xa5, 0x95, 0xa5, 0x74, 0xb2, 0x94, 0x90, 0x05, - 0x7c, 0x62, 0x34, 0xb5, 0x11, 0x40, 0xd8, 0x8e, 0x52, 0xf9, 0x42, 0xe5, 0x45, 0x14, 0xce, 0x09, 0x9e, 0x27, 0x62, - 0x74, 0x6f, 0x25, 0x03, 0x86, 0x43, 0x08, 0xe6, 0xa0, 0x2d, 0xf6, 0x86, 0x6e, 0x22, 0xfe, 0x7a, 0x53, 0x94, 0x6f, - 0x63, 0xf2, 0x29, 0xd8, 0x9d, 0x7c, 0x5c, 0xc2, 0xe3, 0xf2, 0xe4, 0xe3, 0x10, 0x3d, 0x12, 0x4e, 0x3e, 0x06, 0xdf, - 0x23, 0x39, 0xaf, 0xbb, 0x1e, 0x27, 0xc8, 0x2d, 0xa4, 0xfb, 0xdb, 0x31, 0x09, 0xd0, 0xbc, 0x86, 0xe5, 0xa8, 0xa9, - 0xb8, 0x66, 0x66, 0x8c, 0xe7, 0x8d, 0xde, 0x1f, 0x3b, 0xde, 0x32, 0x85, 0x62, 0x16, 0xf3, 0x1a, 0x7e, 0xcf, 0xaa, - 0x40, 0xdd, 0xf5, 0x36, 0xc9, 0x2d, 0xb3, 0x7a, 0x8e, 0x76, 0xdf, 0xf7, 0x75, 0x22, 0xa8, 0xfd, 0x1d, 0xf6, 0x3c, - 0xb3, 0xde, 0x55, 0x31, 0x70, 0xa9, 0x92, 0x1d, 0x32, 0x55, 0x4d, 0x0f, 0x54, 0x4a, 0x83, 0xa7, 0x97, 0xd6, 0xe5, - 0x4b, 0xa5, 0x8d, 0x3c, 0xd3, 0xfc, 0x06, 0xf0, 0x62, 0xea, 0xb2, 0xd8, 0x7d, 0x75, 0x5f, 0xc1, 0x6d, 0xbc, 0xdf, - 0x5f, 0x56, 0x9e, 0xf9, 0x89, 0x0b, 0xc0, 0xde, 0x54, 0x68, 0x9d, 0x40, 0xa9, 0x61, 0x1d, 0xbe, 0x4a, 0x44, 0xf4, - 0x47, 0xbb, 0x5c, 0x67, 0xae, 0x03, 0x46, 0x14, 0xf1, 0xdb, 0x78, 0xf4, 0x07, 0x28, 0xae, 0x8d, 0x3d, 0x20, 0xac, - 0x43, 0x42, 0x9f, 0x11, 0x80, 0xd4, 0x63, 0x8e, 0x12, 0xd0, 0xac, 0x68, 0xee, 0x98, 0xfc, 0x5c, 0x5f, 0x29, 0xfd, - 0xfd, 0xb2, 0xf2, 0xc8, 0x9c, 0xd2, 0x36, 0xd3, 0x58, 0xad, 0xa9, 0x04, 0xc2, 0x2b, 0x2a, 0x59, 0x85, 0xcf, 0xe6, - 0x8d, 0xe8, 0xf7, 0xe5, 0x11, 0x9e, 0x56, 0x3f, 0x6c, 0x31, 0xbe, 0x15, 0x10, 0x8d, 0x04, 0x40, 0x3f, 0x01, 0xcc, - 0x8b, 0x6c, 0x66, 0xf7, 0x71, 0x40, 0x95, 0x12, 0x4d, 0xe3, 0x6c, 0x9e, 0xdf, 0xd2, 0x9b, 0xb2, 0x83, 0x4e, 0x9d, - 0x2a, 0x70, 0xc1, 0x55, 0xc9, 0x78, 0x65, 0x3d, 0x91, 0xcf, 0x6f, 0x6e, 0x37, 0x69, 0x16, 0xbf, 0x2f, 0xff, 0x89, - 0x63, 0xab, 0xeb, 0xf0, 0xc8, 0xd4, 0xe9, 0xda, 0x79, 0xa4, 0xb5, 0x17, 0x02, 0x22, 0xda, 0x35, 0xd4, 0x7a, 0x61, - 0xa1, 0x47, 0x7a, 0x22, 0x9c, 0x93, 0x44, 0x4d, 0x3b, 0xd0, 0xd2, 0x08, 0x7d, 0x7d, 0xcd, 0xe9, 0x2f, 0x0c, 0xd6, - 0x3e, 0x1f, 0x33, 0x20, 0x2b, 0xd1, 0x8f, 0xd5, 0x43, 0x63, 0x33, 0x87, 0x9e, 0xb5, 0x2a, 0xcf, 0xbc, 0xea, 0x70, - 0x40, 0x7c, 0x18, 0xfd, 0x25, 0xbf, 0xdf, 0x7f, 0x4d, 0xf3, 0x8f, 0x09, 0x35, 0x7e, 0xb6, 0x19, 0xa0, 0x6b, 0xdf, - 0x95, 0x07, 0xa2, 0x9e, 0x6b, 0x95, 0x20, 0xc4, 0x1b, 0xc4, 0x44, 0x33, 0x62, 0x0e, 0x4e, 0x3b, 0xd4, 0xfc, 0x93, - 0xd4, 0x80, 0x10, 0x25, 0x5e, 0xc7, 0x94, 0x05, 0x39, 0x6d, 0xe2, 0x48, 0x3f, 0x0a, 0x27, 0xf2, 0xa3, 0xa8, 0x8a, - 0xec, 0x1e, 0x2e, 0x18, 0x4c, 0xbd, 0xa7, 0xfd, 0x12, 0xfd, 0x96, 0x70, 0xe4, 0x1c, 0xad, 0x0a, 0x41, 0xe4, 0x84, - 0xb0, 0xd6, 0x10, 0x26, 0x88, 0x0d, 0xe2, 0x65, 0xdf, 0x25, 0x19, 0x8e, 0x14, 0x5c, 0xd6, 0xb1, 0x63, 0xcc, 0xd5, - 0x51, 0xf5, 0x1a, 0xc0, 0x78, 0xe5, 0x08, 0x9a, 0x8d, 0x22, 0xbb, 0x84, 0xa8, 0x22, 0xc7, 0x13, 0x50, 0x3b, 0x28, - 0x8d, 0xcd, 0xf4, 0x7c, 0x1c, 0xe4, 0xa3, 0x45, 0x85, 0x3a, 0x27, 0x96, 0xf1, 0x1a, 0x80, 0xb5, 0x73, 0xd5, 0xcf, - 0xb3, 0x1a, 0x3c, 0x69, 0x88, 0xcf, 0xc7, 0x68, 0x7b, 0x65, 0x73, 0x50, 0x6d, 0xa7, 0xb3, 0xf2, 0x8a, 0xe9, 0x72, - 0x60, 0xdc, 0x37, 0xbc, 0xa2, 0x38, 0xc3, 0x8f, 0x1e, 0x6c, 0x71, 0xfe, 0x74, 0x43, 0xed, 0xc7, 0xdc, 0xa8, 0x87, - 0x81, 0xd6, 0x82, 0x37, 0x05, 0xb1, 0xfe, 0x7e, 0xe8, 0xc8, 0xf6, 0x5e, 0x8b, 0x8c, 0x26, 0x9f, 0xfd, 0xfc, 0x43, - 0x99, 0xae, 0x52, 0xb8, 0x2f, 0x39, 0x59, 0x34, 0xf3, 0x10, 0xd8, 0x1b, 0x62, 0xb8, 0x3e, 0x2a, 0x3c, 0xa2, 0xac, - 0xdf, 0x87, 0xdf, 0x57, 0x19, 0x98, 0x62, 0xe0, 0xba, 0x42, 0x30, 0x1e, 0x02, 0x41, 0x3c, 0x4c, 0xa3, 0x93, 0x41, - 0x0d, 0xda, 0xf0, 0x0d, 0x40, 0x66, 0x80, 0x47, 0xe6, 0xd2, 0x23, 0xe0, 0x2e, 0x70, 0xed, 0xc9, 0x78, 0xec, 0x4f, - 0x4c, 0x43, 0xa3, 0xa6, 0x34, 0xd3, 0x73, 0xe3, 0x37, 0x1d, 0xd5, 0x72, 0xed, 0xfc, 0xc7, 0x97, 0xfc, 0x06, 0xbd, - 0xa0, 0xe5, 0xe5, 0x3e, 0x52, 0x97, 0xfb, 0x8c, 0xe2, 0x32, 0x91, 0x1c, 0x16, 0xc4, 0xb2, 0x84, 0x03, 0x8f, 0x51, - 0xc9, 0x62, 0x4b, 0x8f, 0x55, 0xd1, 0xf2, 0x45, 0xb9, 0x41, 0x3a, 0x74, 0x42, 0xb0, 0x44, 0x05, 0xc1, 0x12, 0x18, - 0x17, 0xb1, 0xe6, 0x9b, 0x41, 0xce, 0xe2, 0xd9, 0x66, 0xce, 0x91, 0xb0, 0x2e, 0x39, 0x1c, 0x0a, 0x09, 0x36, 0x93, - 0xcd, 0xd6, 0x73, 0xb6, 0xf6, 0x19, 0x28, 0x01, 0x4a, 0x99, 0x26, 0x28, 0x4d, 0x2b, 0xb6, 0xe2, 0xa6, 0x35, 0x58, - 0xad, 0xa6, 0x6c, 0x55, 0x53, 0x76, 0x4e, 0x53, 0x8e, 0x2a, 0x28, 0x39, 0xa1, 0x14, 0x65, 0x18, 0xc0, 0x88, 0x4d, - 0xa2, 0xab, 0x0c, 0x7d, 0xbc, 0x13, 0x1e, 0x41, 0x15, 0x11, 0xf9, 0x84, 0x21, 0x04, 0x26, 0xa2, 0xb8, 0x50, 0x85, - 0x62, 0x80, 0x8c, 0x48, 0x20, 0x98, 0xa8, 0xd4, 0x29, 0x30, 0x1f, 0x4d, 0x15, 0xc3, 0xa6, 0x3d, 0x51, 0xbe, 0xa5, - 0x8e, 0x7b, 0x94, 0x6d, 0xfe, 0x2e, 0x76, 0x41, 0x88, 0xdc, 0x8d, 0x3b, 0xf5, 0x33, 0xe2, 0xbd, 0xdd, 0x11, 0xc6, - 0x4f, 0x76, 0xdc, 0x22, 0x5c, 0x11, 0x6c, 0xa9, 0xe6, 0x10, 0x8b, 0x79, 0x35, 0x49, 0x50, 0xcb, 0x92, 0xf8, 0x1b, - 0x9e, 0x0c, 0x72, 0xb6, 0x04, 0x0f, 0xda, 0x39, 0xcb, 0x00, 0x7f, 0xc5, 0x6a, 0xd1, 0x6f, 0xb5, 0xb7, 0x04, 0xf9, - 0x69, 0x63, 0x37, 0x0a, 0x13, 0x23, 0x48, 0xd4, 0xed, 0xca, 0x40, 0x7e, 0xf8, 0x80, 0xd3, 0xf1, 0xd8, 0x53, 0xc6, - 0xdc, 0xca, 0xf4, 0x32, 0x9d, 0x2b, 0xf9, 0x46, 0xee, 0xa5, 0x8f, 0xbd, 0x04, 0x3b, 0x07, 0xbc, 0x81, 0xb4, 0x81, - 0x37, 0xb0, 0x5d, 0x78, 0x6d, 0x90, 0x30, 0x23, 0xc0, 0x16, 0xc7, 0xc7, 0x48, 0x09, 0x0c, 0xe1, 0x38, 0x4b, 0x01, - 0x98, 0x46, 0x5f, 0x66, 0x2b, 0xfb, 0x32, 0xab, 0x35, 0x5b, 0x2a, 0xa7, 0x7b, 0xe7, 0xd6, 0xed, 0x7c, 0x22, 0x01, - 0xc0, 0xa4, 0xce, 0x81, 0x38, 0x33, 0xc1, 0x2e, 0x4d, 0x22, 0xcb, 0xc7, 0x30, 0xbf, 0x13, 0x6f, 0xca, 0x62, 0xa5, - 0xba, 0xa2, 0xed, 0x33, 0x93, 0xcf, 0x48, 0x27, 0xa1, 0x02, 0x0a, 0x0a, 0xb9, 0xd6, 0xa7, 0xef, 0xc2, 0x77, 0x41, - 0xa1, 0x81, 0xd9, 0x2a, 0xdc, 0xd3, 0x64, 0x8d, 0xd4, 0x1b, 0x55, 0xbf, 0x4f, 0xae, 0x81, 0x54, 0x67, 0x0e, 0x2d, - 0x7b, 0x52, 0x61, 0x80, 0xd8, 0x51, 0x9f, 0x91, 0x50, 0x07, 0x52, 0x0f, 0x18, 0x42, 0xb4, 0x4d, 0x1f, 0x7f, 0x32, - 0x24, 0xba, 0x00, 0x5b, 0x88, 0x36, 0xf0, 0xe3, 0x4f, 0xb0, 0xcf, 0x82, 0xf0, 0x98, 0xe6, 0xd7, 0x90, 0x74, 0x6c, - 0xe0, 0xb4, 0xfa, 0x14, 0x7c, 0x90, 0xe4, 0x60, 0xa2, 0x0e, 0x5e, 0xee, 0x2f, 0xfd, 0x3e, 0x6c, 0xd9, 0xb9, 0x94, - 0xea, 0x58, 0xa9, 0xb7, 0x6d, 0xed, 0x07, 0xd1, 0x16, 0x1c, 0x59, 0xc4, 0xdf, 0x67, 0x88, 0x08, 0x66, 0x06, 0x11, - 0x76, 0x2d, 0xd4, 0xdd, 0x9e, 0x52, 0xcb, 0xa2, 0xde, 0xf6, 0x94, 0x52, 0xb7, 0x61, 0xf8, 0x6e, 0x82, 0x99, 0xe2, - 0x86, 0xff, 0x91, 0x79, 0xa1, 0xde, 0x78, 0x2c, 0x0a, 0x74, 0xcf, 0xdf, 0x2f, 0x79, 0x35, 0xdb, 0x28, 0x13, 0xe6, - 0x1d, 0x5f, 0xce, 0x42, 0xd9, 0xd5, 0xd2, 0xb8, 0xf3, 0xd9, 0x5b, 0xaa, 0xf9, 0xe0, 0x1f, 0x0e, 0x09, 0xc4, 0x1b, - 0xc5, 0x57, 0x77, 0x8d, 0xdc, 0xba, 0x26, 0x9b, 0xab, 0x12, 0x50, 0xbf, 0xcf, 0xd7, 0xb8, 0xdf, 0x62, 0xfd, 0xbb, - 0xa7, 0x41, 0xc6, 0x6a, 0x86, 0x2b, 0xa6, 0xf0, 0x29, 0x00, 0x0c, 0x0e, 0xa7, 0x82, 0xb4, 0xc0, 0x1b, 0x5e, 0x0e, - 0x2f, 0x27, 0x1b, 0x32, 0xe9, 0x6e, 0x7c, 0xe4, 0xce, 0x02, 0x55, 0xef, 0x37, 0x14, 0x27, 0x0d, 0x12, 0x8d, 0xbd, - 0x06, 0x5f, 0x66, 0x19, 0xe5, 0xa2, 0x89, 0xfb, 0x98, 0x7c, 0xa5, 0x07, 0x30, 0x57, 0xa1, 0x04, 0x88, 0x7e, 0x63, - 0x59, 0x6c, 0x44, 0xdb, 0x62, 0x03, 0x4b, 0xa9, 0x9a, 0xeb, 0xd5, 0xf4, 0xd9, 0x2b, 0xd1, 0xbc, 0x8f, 0x66, 0x9c, - 0xd2, 0x68, 0xc0, 0x71, 0x1a, 0x85, 0xdb, 0xf7, 0xf7, 0xa2, 0x5c, 0x66, 0x60, 0xc9, 0x56, 0xe1, 0x14, 0x97, 0x8d, - 0x3a, 0x23, 0x5e, 0xe6, 0xb1, 0x02, 0xe8, 0x78, 0x4c, 0x00, 0x54, 0x17, 0x04, 0x54, 0x44, 0x4b, 0xe9, 0xad, 0xd0, - 0x62, 0xa1, 0xde, 0x70, 0x94, 0xc2, 0x1f, 0xe9, 0xcf, 0x83, 0x7c, 0x0a, 0x40, 0xec, 0xfa, 0x38, 0x7a, 0x53, 0x94, - 0xf4, 0xa9, 0x62, 0x96, 0xcb, 0xc1, 0x04, 0x76, 0x75, 0x22, 0x43, 0xad, 0x20, 0x6f, 0xd5, 0x95, 0xb7, 0x32, 0x79, - 0x1b, 0xe3, 0x94, 0xfc, 0xc8, 0x4d, 0xc7, 0x1a, 0x31, 0xf0, 0xca, 0xd3, 0x3a, 0x4d, 0x90, 0x26, 0x17, 0xc0, 0x30, - 0xc4, 0xb7, 0x99, 0xf7, 0xd2, 0x73, 0xa4, 0x2a, 0x48, 0x66, 0xbb, 0xcc, 0x53, 0x17, 0x51, 0x7d, 0xe5, 0xd4, 0xd2, - 0x99, 0xd3, 0x8f, 0x00, 0xde, 0x63, 0x6a, 0xd2, 0x90, 0x8f, 0x70, 0x5b, 0x8a, 0xaf, 0xb7, 0xea, 0x1a, 0x2f, 0x8d, - 0xce, 0xdd, 0xcb, 0x97, 0xee, 0x34, 0xe8, 0xa7, 0x20, 0x28, 0xe7, 0xcb, 0x52, 0xc0, 0x9e, 0x32, 0x9b, 0xeb, 0xd5, - 0xaa, 0x15, 0x5a, 0x87, 0xc3, 0x58, 0x3b, 0x0a, 0x69, 0x75, 0x16, 0xb0, 0xd5, 0x48, 0xa7, 0x04, 0x08, 0xc1, 0x71, - 0x1a, 0x76, 0x82, 0x71, 0x97, 0x4e, 0x23, 0xb2, 0x5e, 0x29, 0x49, 0x17, 0x66, 0x90, 0xfc, 0x93, 0xbc, 0x9e, 0x01, - 0x2d, 0x01, 0x1c, 0x8a, 0x58, 0xc2, 0xc3, 0x49, 0x72, 0x05, 0xd0, 0xe9, 0x70, 0x50, 0x69, 0x68, 0xce, 0x6a, 0x96, - 0xcc, 0x27, 0xb1, 0x54, 0x55, 0x1e, 0x0e, 0x9e, 0x72, 0x33, 0xe8, 0xf7, 0xb3, 0x69, 0xa9, 0x5c, 0x00, 0x82, 0x58, - 0x17, 0x06, 0x88, 0x47, 0x5a, 0x78, 0xb2, 0xe8, 0x53, 0x12, 0xbf, 0x9c, 0x25, 0x73, 0x93, 0x0d, 0xef, 0xc0, 0x08, - 0x36, 0xe3, 0xba, 0xa4, 0x4c, 0x7b, 0x54, 0x7e, 0xcf, 0xe8, 0xa9, 0xed, 0x6b, 0xad, 0xb6, 0x88, 0x75, 0x1d, 0x5c, - 0x95, 0xa8, 0xa7, 0xf8, 0xa0, 0x24, 0xc1, 0xfb, 0xb5, 0x73, 0x33, 0x52, 0xbe, 0x16, 0xb9, 0x1f, 0xb4, 0x33, 0xb5, - 0x72, 0xe0, 0x08, 0xe4, 0x58, 0x45, 0x25, 0xaf, 0x77, 0x1d, 0x82, 0x47, 0x77, 0xa5, 0x02, 0xe5, 0xe0, 0x17, 0x20, - 0x46, 0xd7, 0x57, 0x9d, 0x35, 0xd4, 0x4c, 0xa3, 0xca, 0x23, 0xe8, 0xd4, 0x01, 0x3c, 0x29, 0x78, 0xa9, 0xd5, 0x8f, - 0x87, 0x83, 0x67, 0x7e, 0xf0, 0x57, 0x99, 0xbe, 0x85, 0x98, 0x28, 0xa7, 0x1a, 0x21, 0x71, 0xa5, 0x24, 0x11, 0x1f, - 0x2f, 0x5a, 0x56, 0x8c, 0xca, 0xf0, 0x81, 0x57, 0xaa, 0x7c, 0x75, 0xaa, 0xf2, 0x62, 0xa4, 0x6d, 0x09, 0xbc, 0x26, - 0xff, 0x10, 0xb9, 0xe6, 0xad, 0xaf, 0xbb, 0xca, 0xd0, 0x6b, 0x59, 0x81, 0x8e, 0x60, 0x2b, 0x4b, 0xc9, 0x01, 0x9f, - 0x54, 0x77, 0xd5, 0xaa, 0xf5, 0x39, 0x65, 0x1b, 0xe1, 0x26, 0xbf, 0x8e, 0x1d, 0x1c, 0x29, 0xbf, 0xc1, 0x73, 0x01, - 0xec, 0x35, 0x60, 0x6f, 0xce, 0x59, 0xd1, 0x3c, 0x3a, 0xa4, 0x6d, 0x81, 0x46, 0x66, 0x6e, 0xe7, 0xea, 0xbe, 0x2d, - 0x8f, 0xd2, 0x18, 0x22, 0xd3, 0x1e, 0x99, 0x0e, 0x36, 0xa3, 0xfc, 0xb7, 0x94, 0xdf, 0x2a, 0x1c, 0x03, 0xdf, 0x4e, - 0xbd, 0x03, 0xa8, 0x7a, 0xda, 0x20, 0x63, 0xcd, 0x30, 0xb4, 0xb2, 0xcb, 0xa5, 0xd0, 0x12, 0xb4, 0xd4, 0x4d, 0x10, - 0x9c, 0x1f, 0x11, 0xe5, 0x08, 0x40, 0x17, 0x29, 0x60, 0x82, 0x9f, 0xd2, 0x76, 0xf7, 0xfb, 0xeb, 0xd4, 0x23, 0xf7, - 0xae, 0x50, 0xa3, 0x84, 0x12, 0x8c, 0xfd, 0x44, 0x63, 0x06, 0x1d, 0x5d, 0x91, 0x13, 0x9e, 0xb5, 0x3a, 0xac, 0xeb, - 0xa6, 0x0c, 0xca, 0xe2, 0x98, 0x57, 0xd3, 0xd9, 0xef, 0x4f, 0xf6, 0x75, 0x83, 0x2c, 0xe4, 0xbf, 0xb3, 0x1e, 0x92, - 0x41, 0xf7, 0x20, 0x14, 0xa2, 0x37, 0x0f, 0x66, 0xf8, 0x1f, 0xdb, 0xf0, 0xec, 0x1b, 0x6e, 0xd4, 0x09, 0x60, 0x8e, - 0xb8, 0x5e, 0x7a, 0x8a, 0xb6, 0x1e, 0x6e, 0x81, 0x6c, 0x8d, 0x97, 0xb7, 0xf6, 0x1a, 0xc8, 0x29, 0x8e, 0xff, 0x8e, - 0x67, 0x6a, 0x65, 0x83, 0x9f, 0x9e, 0xb2, 0x1d, 0x78, 0x78, 0x11, 0x02, 0x8a, 0x61, 0xd9, 0xf8, 0x3b, 0xcb, 0x71, - 0x46, 0xff, 0xcd, 0x23, 0x86, 0xc1, 0x22, 0xf2, 0xe3, 0xcb, 0x52, 0x88, 0x2f, 0xc2, 0x7b, 0x5b, 0x79, 0x77, 0xe4, - 0x94, 0x79, 0xa7, 0x87, 0xd1, 0x75, 0x49, 0xfa, 0x26, 0xf9, 0xd8, 0x1a, 0xb6, 0xdf, 0xb5, 0xfb, 0xcd, 0x10, 0x41, - 0x08, 0xe5, 0xf8, 0x39, 0xa3, 0x13, 0x1a, 0x1f, 0x56, 0xb3, 0xd3, 0xeb, 0xf7, 0xce, 0xf1, 0x82, 0xad, 0xd1, 0x00, - 0x8f, 0x87, 0x2e, 0xe6, 0x89, 0x1a, 0x3a, 0x5d, 0xd7, 0xce, 0xc1, 0x03, 0x83, 0x2c, 0x4f, 0xbe, 0x61, 0x58, 0x62, - 0x7f, 0x12, 0xf1, 0xa4, 0xad, 0xda, 0xd8, 0x1c, 0xa9, 0x36, 0x6a, 0x06, 0x7e, 0xf0, 0x0a, 0x0a, 0x8c, 0x2e, 0x48, - 0x2b, 0x30, 0x0e, 0x47, 0x00, 0xb2, 0x62, 0x1c, 0x8f, 0x0c, 0x26, 0x30, 0xa4, 0x1b, 0x8a, 0x02, 0xf0, 0xf0, 0x38, - 0x1e, 0x84, 0x0c, 0x20, 0x5d, 0xf0, 0xd0, 0xb0, 0x4d, 0x42, 0xca, 0xcf, 0xf3, 0xbc, 0x56, 0x43, 0xe8, 0x3b, 0x0b, - 0xd5, 0xb1, 0x1f, 0x69, 0xaf, 0x58, 0xd7, 0xaa, 0x74, 0x64, 0xab, 0x03, 0xf4, 0x0d, 0x19, 0xf8, 0xd6, 0xb1, 0x05, - 0x40, 0xb4, 0xc4, 0x6f, 0xa9, 0x57, 0xfb, 0x32, 0x66, 0x85, 0x7a, 0x7d, 0x61, 0xda, 0xf5, 0x5a, 0x5a, 0x14, 0x50, - 0x71, 0xdb, 0xaa, 0xed, 0x91, 0x9c, 0xff, 0xf8, 0xae, 0xa3, 0x1d, 0x9f, 0x9d, 0x1a, 0x5b, 0x42, 0x99, 0x5b, 0x3c, - 0x91, 0xd5, 0xd1, 0x96, 0xea, 0x54, 0x1f, 0x70, 0xa9, 0x49, 0x75, 0x66, 0x60, 0x78, 0x8d, 0x00, 0xe5, 0x16, 0x22, - 0x69, 0x1c, 0xf6, 0xce, 0x27, 0x83, 0x82, 0xb9, 0x45, 0x02, 0x12, 0xd8, 0xc6, 0xd6, 0x2e, 0x9a, 0xeb, 0xd7, 0x6f, - 0xa9, 0x57, 0xb5, 0xa9, 0xea, 0xc1, 0x1b, 0x2f, 0x70, 0xf6, 0x4e, 0x6b, 0x01, 0x01, 0x14, 0xb6, 0x96, 0xe5, 0xe0, - 0xdc, 0xed, 0xaa, 0x96, 0x8a, 0x32, 0xea, 0xf7, 0xcf, 0x7f, 0x4b, 0x51, 0x11, 0x7b, 0xaa, 0x38, 0x65, 0xfd, 0x76, - 0xcb, 0x5c, 0x54, 0x96, 0xbc, 0x41, 0x15, 0xad, 0xd5, 0x51, 0x53, 0xb9, 0x6e, 0xae, 0x5a, 0x32, 0x41, 0x8c, 0xee, - 0xd3, 0xb5, 0xce, 0x9d, 0x7a, 0xef, 0x55, 0x1c, 0x31, 0x10, 0xdc, 0x74, 0x8f, 0x0f, 0x0e, 0x42, 0xa3, 0xa2, 0x5c, - 0x70, 0xa3, 0xb4, 0xaa, 0xa4, 0x14, 0xf2, 0x56, 0x45, 0x73, 0xa6, 0x8f, 0x00, 0x88, 0x00, 0xab, 0x44, 0xfd, 0x1f, - 0xbe, 0x34, 0xc6, 0x83, 0x07, 0xbe, 0x26, 0xd7, 0xb1, 0xf5, 0xfe, 0x69, 0x8d, 0xb4, 0xda, 0x38, 0x26, 0xb5, 0xea, - 0x65, 0xab, 0x78, 0xd9, 0xbd, 0x4e, 0xc5, 0xe0, 0xf9, 0xff, 0xdc, 0x07, 0xa8, 0x11, 0x2d, 0x65, 0x70, 0xeb, 0x6a, - 0x80, 0xc6, 0x87, 0x63, 0xe1, 0x1b, 0x3f, 0x64, 0x9c, 0x0f, 0x66, 0xe8, 0xa8, 0x36, 0x07, 0x07, 0x04, 0x47, 0x75, - 0x8f, 0xc6, 0x84, 0x59, 0x38, 0xf7, 0x20, 0x50, 0x7d, 0xe2, 0x3e, 0xe3, 0xda, 0x0b, 0xda, 0x04, 0x3e, 0x59, 0xd7, - 0x35, 0x45, 0x80, 0x8b, 0xd8, 0x98, 0x88, 0x21, 0x2e, 0x9b, 0x44, 0xea, 0x9b, 0x31, 0x28, 0x00, 0x8a, 0x17, 0x15, - 0xc9, 0xa5, 0x8b, 0x34, 0xaf, 0x44, 0x59, 0xeb, 0x66, 0x54, 0xac, 0x18, 0x02, 0xc0, 0x43, 0x50, 0x5c, 0x55, 0x66, - 0x42, 0x23, 0x36, 0x90, 0xca, 0x52, 0xb0, 0x6a, 0x58, 0xf8, 0x4d, 0xfb, 0x4d, 0x72, 0xd2, 0x3b, 0x1f, 0xb7, 0xce, - 0x1d, 0xfb, 0xde, 0x51, 0x48, 0x69, 0x0f, 0xc5, 0x04, 0x41, 0xf0, 0xd3, 0x3a, 0x9c, 0x3f, 0xe3, 0x2f, 0x08, 0x4c, - 0x45, 0x36, 0x63, 0xc0, 0x41, 0x88, 0xc8, 0x8c, 0xdf, 0x73, 0xf8, 0x82, 0x97, 0x93, 0x70, 0x38, 0xf4, 0x41, 0x1f, - 0xca, 0xb3, 0x59, 0x38, 0x14, 0x73, 0xe9, 0xbd, 0x0e, 0xd6, 0xba, 0x90, 0xd7, 0x93, 0x10, 0xd1, 0x42, 0x43, 0x1f, - 0x9c, 0xd7, 0x5d, 0x73, 0x84, 0x25, 0x00, 0x4d, 0x1c, 0x7d, 0x59, 0xbf, 0x1f, 0x79, 0xda, 0xd0, 0x22, 0xc5, 0x45, - 0xa3, 0xcc, 0x66, 0xb9, 0xec, 0x84, 0x8d, 0x6b, 0xb7, 0x40, 0x28, 0x1e, 0xa6, 0x2d, 0x54, 0xad, 0xa7, 0x7a, 0x3d, - 0x37, 0xed, 0xbe, 0x7b, 0x54, 0xad, 0x72, 0xa4, 0xb3, 0x36, 0x5d, 0xa9, 0xd5, 0x2d, 0xa3, 0x6a, 0x9d, 0xa5, 0x11, - 0x55, 0x6e, 0x92, 0xbb, 0x46, 0x2d, 0xf8, 0x64, 0x43, 0x97, 0x29, 0x3b, 0x5b, 0x83, 0x13, 0x47, 0x9e, 0x4b, 0x6e, - 0xf9, 0xee, 0xbc, 0xa2, 0xbb, 0x53, 0xed, 0x5b, 0x80, 0x7b, 0x33, 0x6c, 0xc8, 0x9c, 0xd7, 0xd8, 0x69, 0x10, 0x26, - 0x81, 0x1f, 0xb1, 0x8f, 0x19, 0xb2, 0xc1, 0x80, 0x8e, 0x42, 0xfa, 0x5f, 0x5b, 0xe6, 0x48, 0xc0, 0xe4, 0xaf, 0xe7, - 0x7e, 0xb3, 0x28, 0x72, 0x58, 0x8c, 0x1f, 0x36, 0x18, 0x69, 0xac, 0xd6, 0x60, 0x58, 0xde, 0x21, 0xf2, 0xa7, 0x76, - 0xc7, 0x34, 0xd5, 0xf1, 0x66, 0xbd, 0xd6, 0xfc, 0xea, 0xe9, 0x53, 0x5d, 0x9f, 0xff, 0xf6, 0xfd, 0x65, 0x58, 0x33, - 0xfb, 0x43, 0x10, 0x4a, 0xbb, 0x77, 0x8b, 0x73, 0x47, 0xa2, 0x77, 0xac, 0x34, 0xb3, 0x4b, 0xbb, 0x64, 0x97, 0xa6, - 0xb4, 0x1b, 0x72, 0xbd, 0xfa, 0x4a, 0x79, 0x63, 0xe7, 0x15, 0xd3, 0xfd, 0x7b, 0xa1, 0x77, 0x94, 0x53, 0x35, 0x81, - 0x88, 0x26, 0xed, 0x48, 0xdc, 0xee, 0x95, 0xe1, 0xf3, 0x49, 0xde, 0x2e, 0xe1, 0xa8, 0x6b, 0x58, 0x6e, 0xbe, 0xfd, - 0xcf, 0xbc, 0xea, 0xac, 0x70, 0xfb, 0xa5, 0x31, 0x6b, 0x7f, 0x0a, 0xe2, 0xaa, 0xfe, 0xf0, 0x9e, 0xd4, 0x4c, 0xc9, - 0xff, 0x55, 0x8f, 0x81, 0xab, 0x9f, 0x4c, 0x3b, 0xba, 0xa7, 0x10, 0x36, 0x98, 0xfd, 0xfc, 0xf8, 0xa1, 0x05, 0xab, - 0xea, 0x02, 0x45, 0x72, 0x00, 0x9d, 0xbb, 0x64, 0x84, 0xf7, 0x3b, 0xc6, 0xb9, 0x7f, 0xf5, 0xbd, 0x9a, 0x1c, 0x21, - 0xa2, 0x5d, 0x84, 0x03, 0x80, 0xb8, 0xd3, 0x54, 0xd6, 0xa1, 0x06, 0xe8, 0x03, 0x02, 0xeb, 0xd0, 0xb7, 0x19, 0xc0, - 0x41, 0x1f, 0x6d, 0x9e, 0x45, 0x20, 0xaf, 0x7b, 0xf7, 0xec, 0x9a, 0xed, 0x7c, 0xfe, 0x62, 0x95, 0x7a, 0xf7, 0xe8, - 0x10, 0x7c, 0x3e, 0xf6, 0xa7, 0x97, 0x81, 0xc1, 0x85, 0x66, 0xd7, 0xcf, 0x04, 0xdb, 0xb1, 0xdd, 0x33, 0x44, 0x2a, - 0xea, 0xce, 0x3f, 0xbc, 0x34, 0xd1, 0xf3, 0xce, 0x0b, 0x77, 0x7c, 0x09, 0xe0, 0x81, 0x2c, 0x06, 0x14, 0x9f, 0xa5, - 0xf7, 0x2f, 0x96, 0x80, 0x9a, 0xfc, 0x96, 0xaf, 0xbd, 0x2f, 0x94, 0xba, 0x80, 0x3f, 0x07, 0x94, 0x3e, 0xc9, 0xb9, - 0x77, 0x37, 0xbc, 0xf5, 0x2f, 0x9e, 0x83, 0xf3, 0xc4, 0x6a, 0xb8, 0x80, 0xbf, 0x0a, 0x3e, 0xf4, 0xee, 0x06, 0x98, - 0x58, 0xf2, 0xa1, 0xb7, 0x1a, 0x40, 0xaa, 0xc2, 0x85, 0xc4, 0xd8, 0x87, 0x5f, 0x83, 0x9c, 0xe1, 0x1f, 0xbf, 0x69, - 0x0c, 0xd6, 0x5f, 0x83, 0x42, 0xa3, 0xb1, 0x96, 0x2a, 0x64, 0x29, 0x16, 0x67, 0x02, 0x6c, 0xc2, 0x71, 0xb7, 0x2f, - 0x56, 0xb5, 0x59, 0x0b, 0xfa, 0xf3, 0x11, 0xdf, 0xa3, 0xb1, 0xba, 0x2a, 0xe7, 0xa2, 0xfc, 0x88, 0xf4, 0xa9, 0x8e, - 0x8f, 0x51, 0xb1, 0xa9, 0xbb, 0xd3, 0xa9, 0x56, 0x1d, 0x69, 0xbf, 0x29, 0xd7, 0x60, 0xc7, 0xeb, 0xe4, 0xc8, 0x52, - 0x78, 0xd6, 0x61, 0xe7, 0xa5, 0x53, 0xa2, 0xc3, 0x30, 0xde, 0x6d, 0xd5, 0x33, 0x86, 0xf2, 0xdc, 0x60, 0x4c, 0x17, - 0x3c, 0xe2, 0x2f, 0x06, 0xb9, 0x0c, 0x8d, 0xf9, 0x80, 0x6c, 0x18, 0xca, 0x87, 0x16, 0x19, 0x12, 0x22, 0xde, 0x43, - 0x25, 0x60, 0xdb, 0x82, 0x32, 0x29, 0xe0, 0x2c, 0x1a, 0xfc, 0x56, 0x7b, 0x39, 0xf0, 0x1e, 0x44, 0x7e, 0x23, 0x5d, - 0xca, 0x25, 0x36, 0x3a, 0x71, 0x2c, 0x0b, 0xed, 0x3c, 0xae, 0xbf, 0x8e, 0x41, 0xfd, 0x5e, 0xe9, 0x37, 0x28, 0x67, - 0x7f, 0x94, 0xac, 0xd3, 0xc6, 0x13, 0xe3, 0x1f, 0xae, 0xf2, 0x4f, 0xd1, 0x52, 0x0f, 0xff, 0x9f, 0x31, 0x85, 0xd2, - 0xbf, 0x4a, 0xcb, 0x68, 0xb3, 0x5a, 0x8a, 0x52, 0xe4, 0x91, 0x38, 0xf9, 0x5a, 0x64, 0xe7, 0xf2, 0x9d, 0x4f, 0xa1, - 0x5f, 0x00, 0x5a, 0xf6, 0x09, 0x32, 0xfa, 0x7b, 0x26, 0xf8, 0xf0, 0x7b, 0xed, 0x5c, 0x9b, 0xf3, 0xf1, 0x24, 0xbf, - 0xb2, 0xf6, 0x6e, 0xc7, 0x8b, 0xc4, 0x28, 0xc6, 0x72, 0x5f, 0x75, 0xb3, 0x72, 0xa2, 0x92, 0x03, 0x23, 0x5d, 0x93, - 0xbd, 0x5c, 0xc9, 0xba, 0x9d, 0x6e, 0x25, 0x10, 0x51, 0x05, 0xde, 0x63, 0x5c, 0xc5, 0x3e, 0x82, 0xe9, 0xba, 0xe3, - 0x32, 0xda, 0xf1, 0x9e, 0xf1, 0xea, 0x44, 0x59, 0xc1, 0xed, 0x46, 0xb4, 0x27, 0x74, 0xf4, 0xd3, 0xa4, 0xb6, 0x2c, - 0x1c, 0x80, 0xdc, 0x25, 0x8c, 0x65, 0x43, 0xb0, 0x62, 0x50, 0xfa, 0x7a, 0x4d, 0xc9, 0xb2, 0x00, 0x8b, 0xce, 0x2e, - 0x23, 0x10, 0xc3, 0xba, 0x69, 0x4e, 0xe8, 0x78, 0xe9, 0xe2, 0xbc, 0xd7, 0x2a, 0x52, 0xf0, 0x8c, 0x16, 0x1d, 0x73, - 0xd3, 0x91, 0x6e, 0x8c, 0xf6, 0xf6, 0x7b, 0x83, 0x90, 0xe2, 0xf9, 0x03, 0x5b, 0xad, 0x8b, 0x8b, 0xc4, 0x2b, 0x64, - 0xa2, 0x05, 0xb1, 0x14, 0x81, 0x19, 0x2f, 0x34, 0x8d, 0x30, 0x41, 0x99, 0x12, 0x2c, 0x5a, 0xa3, 0x43, 0xfb, 0xc3, - 0x12, 0x76, 0x8f, 0x31, 0x02, 0x04, 0xaa, 0x4c, 0x9f, 0xc3, 0xd6, 0x84, 0xd9, 0xd4, 0xc5, 0x06, 0x68, 0xab, 0x18, - 0x1a, 0x84, 0xb5, 0x21, 0xe6, 0x63, 0x9a, 0xdf, 0xfd, 0x0b, 0x8b, 0xb1, 0x3d, 0x81, 0xd8, 0xde, 0xed, 0x9a, 0x84, - 0xe9, 0x5e, 0x8b, 0x1b, 0xeb, 0xe5, 0xf6, 0x94, 0x63, 0x6a, 0xc7, 0xda, 0xa8, 0x1d, 0x6b, 0xa9, 0x77, 0xac, 0xb5, - 0xde, 0xb1, 0xee, 0x1a, 0xfe, 0x21, 0xf3, 0x62, 0x96, 0x80, 0x7e, 0x77, 0xc5, 0x55, 0x83, 0xa0, 0x19, 0x1b, 0x76, - 0x0b, 0xbf, 0x25, 0xd6, 0x6e, 0xe9, 0x5f, 0x2c, 0xd9, 0xc2, 0xf4, 0x81, 0x6e, 0x1d, 0x60, 0x19, 0x51, 0x93, 0xef, - 0x91, 0x77, 0xd3, 0x59, 0x51, 0xb8, 0x3d, 0xb1, 0x85, 0xcf, 0xae, 0xcd, 0x9b, 0xf7, 0xcf, 0x22, 0xc8, 0xbd, 0xe3, - 0xde, 0xfd, 0xf0, 0xda, 0xbf, 0xd0, 0x2d, 0x90, 0x93, 0x59, 0xce, 0x40, 0xea, 0x88, 0x4f, 0x10, 0xad, 0xec, 0x29, - 0xdf, 0x09, 0xb9, 0xb3, 0xad, 0x9f, 0xdd, 0xbb, 0xdb, 0xda, 0xdd, 0xb3, 0x7b, 0x56, 0x8d, 0x28, 0x56, 0x9c, 0xa6, - 0x48, 0x98, 0x45, 0x1b, 0xe0, 0xa9, 0x97, 0xef, 0x77, 0xec, 0x98, 0xc3, 0xdd, 0xb3, 0x8e, 0x8e, 0x97, 0x73, 0xc0, - 0xee, 0xfe, 0xa3, 0x4d, 0xd8, 0x58, 0xe9, 0x5a, 0x85, 0x0e, 0x77, 0xcf, 0x32, 0x8d, 0xe7, 0x70, 0x24, 0x9f, 0x8e, - 0x35, 0x36, 0x08, 0xea, 0xfa, 0x9c, 0x41, 0xed, 0xd8, 0x7d, 0x4d, 0xd8, 0x65, 0xc7, 0xbc, 0xd6, 0x35, 0x6f, 0xaf, - 0x3c, 0x15, 0x1b, 0x02, 0x3a, 0x7c, 0xad, 0x6e, 0x90, 0x7f, 0x09, 0x9c, 0x22, 0x00, 0xe4, 0x70, 0xbc, 0xe4, 0xb1, - 0xef, 0xd3, 0x2c, 0xad, 0x77, 0xa8, 0xb5, 0xa8, 0x2c, 0xcb, 0xb0, 0xf6, 0x7e, 0xd0, 0x8a, 0x61, 0xa9, 0xe9, 0x9f, - 0x8e, 0x03, 0xb7, 0xb3, 0xdd, 0xca, 0xd8, 0x65, 0x3c, 0x2b, 0x2e, 0xbe, 0x3f, 0x2d, 0x94, 0x6b, 0x37, 0x6f, 0xe3, - 0x37, 0xad, 0x96, 0x2c, 0xad, 0xf5, 0x90, 0x97, 0x96, 0x45, 0x04, 0x02, 0x18, 0x8e, 0x94, 0x5d, 0x2c, 0xe1, 0x1e, - 0x61, 0x75, 0x0f, 0x42, 0xc9, 0xbc, 0x70, 0xf1, 0x9c, 0xc5, 0x90, 0x08, 0xb0, 0xdd, 0xa1, 0x62, 0x5b, 0xb8, 0x78, - 0xce, 0x36, 0xbc, 0xe8, 0xf7, 0x33, 0xd5, 0x29, 0x64, 0xdd, 0x59, 0xf2, 0x8d, 0x6a, 0x8e, 0x35, 0xd4, 0x6c, 0x6d, - 0x92, 0xad, 0x71, 0x6e, 0x2b, 0x3e, 0xee, 0xda, 0x8a, 0x8f, 0x95, 0xb5, 0x2e, 0xdd, 0xeb, 0x3d, 0xaa, 0x0b, 0x60, - 0xeb, 0xbf, 0x3d, 0x5e, 0xb9, 0x9e, 0xcf, 0x08, 0xe0, 0x6b, 0xc1, 0xc7, 0x93, 0x05, 0x7a, 0x95, 0x2c, 0xfc, 0xdb, - 0x81, 0x1a, 0x7f, 0xa7, 0x73, 0x17, 0x00, 0x5d, 0x49, 0x79, 0x05, 0xe4, 0x1d, 0xe4, 0x98, 0x5b, 0x76, 0xe5, 0xfd, - 0xc9, 0x77, 0xd8, 0x35, 0xaf, 0x67, 0x8b, 0x39, 0xdb, 0x81, 0x53, 0x41, 0x32, 0xb0, 0x97, 0x15, 0xdb, 0x05, 0xb1, - 0x9d, 0xf0, 0x1b, 0x01, 0x53, 0xbe, 0x84, 0x20, 0xae, 0xe0, 0x16, 0xe2, 0xf0, 0xe4, 0x9f, 0x83, 0xfb, 0xd6, 0x66, - 0x7d, 0xcf, 0xac, 0xce, 0x09, 0xd6, 0xcc, 0xea, 0xc1, 0x60, 0xd9, 0x4c, 0x56, 0xfd, 0xbe, 0xb7, 0xd3, 0x8e, 0x4f, - 0x77, 0x52, 0x27, 0x76, 0x5a, 0xab, 0xb5, 0x60, 0xd7, 0x52, 0xeb, 0x62, 0x0c, 0x3d, 0x40, 0xfc, 0x74, 0x3b, 0xe0, - 0xf7, 0x1d, 0x6b, 0xcb, 0xbb, 0x66, 0x0b, 0xb6, 0x83, 0x4b, 0x50, 0xd3, 0x5e, 0xf6, 0x27, 0x95, 0x0b, 0xda, 0xb1, - 0x4b, 0xe2, 0xe1, 0x8c, 0x59, 0xa5, 0xcc, 0xac, 0x93, 0xea, 0x4a, 0x74, 0xc6, 0x74, 0xd6, 0x7a, 0x3e, 0x57, 0xf3, - 0x49, 0xa1, 0x41, 0xfd, 0xce, 0x89, 0x8f, 0xa8, 0xe8, 0x3c, 0x81, 0xad, 0x65, 0x05, 0xb1, 0xda, 0xe7, 0x60, 0xad, - 0xd5, 0x2e, 0xfd, 0x5e, 0x3e, 0xe0, 0x36, 0xe5, 0xb0, 0x0e, 0x0c, 0x6a, 0x4e, 0xac, 0xa8, 0xc7, 0x6c, 0xc7, 0xb8, - 0xf9, 0xe9, 0xe5, 0x0f, 0x4e, 0x58, 0xb2, 0x62, 0xb5, 0x3f, 0xfd, 0xfe, 0x99, 0xa7, 0xbf, 0x53, 0xfb, 0x17, 0xc2, - 0x0f, 0xc6, 0xff, 0xa9, 0xdd, 0xd7, 0x5a, 0x8c, 0xca, 0x56, 0x39, 0x42, 0xe3, 0x6e, 0x25, 0x4d, 0x96, 0x9f, 0x84, - 0x27, 0xac, 0x05, 0xcf, 0x72, 0xbd, 0x44, 0xb3, 0x02, 0x56, 0x58, 0xcb, 0x24, 0x5c, 0x61, 0xac, 0x96, 0xb6, 0xfa, - 0x16, 0x4d, 0x73, 0x7c, 0x38, 0xd7, 0x06, 0x65, 0xca, 0xd9, 0x19, 0xb1, 0x1a, 0x2e, 0xc3, 0xd2, 0x84, 0x22, 0x64, - 0xf7, 0x76, 0x70, 0x63, 0xa7, 0x2c, 0xa5, 0x0c, 0xe7, 0x18, 0x4c, 0x78, 0x24, 0x46, 0x55, 0xbe, 0xbf, 0x2f, 0x29, - 0x72, 0xda, 0x96, 0x83, 0x2a, 0x84, 0x7d, 0x24, 0x51, 0x02, 0xb7, 0x22, 0x2d, 0x14, 0x29, 0x8b, 0xbf, 0x1d, 0xa0, - 0x0b, 0xbc, 0x80, 0xba, 0x1a, 0x75, 0xfb, 0xc3, 0x11, 0x0f, 0x1f, 0x99, 0xfa, 0xc0, 0x88, 0x25, 0x81, 0xda, 0x5e, - 0x66, 0xe9, 0x1d, 0xa8, 0xf0, 0x7b, 0xb8, 0x9a, 0x88, 0xfd, 0xdc, 0x92, 0xa2, 0x22, 0x1b, 0xe9, 0x0d, 0xad, 0xc1, - 0x23, 0xb4, 0xa6, 0x7c, 0xef, 0xa4, 0xda, 0xa4, 0xf3, 0x8e, 0x90, 0x63, 0xf5, 0xad, 0x25, 0x8c, 0x76, 0x45, 0x2f, - 0xee, 0x1d, 0xbd, 0xe7, 0xe9, 0xaa, 0xe7, 0xfe, 0xc4, 0x15, 0xf3, 0xe4, 0x36, 0x02, 0x75, 0x2b, 0xa8, 0x6e, 0xef, - 0x55, 0x82, 0x05, 0x4b, 0xda, 0x7d, 0xfc, 0x76, 0xd6, 0x0e, 0x44, 0x65, 0xac, 0xd2, 0xd7, 0x24, 0x61, 0x4f, 0x0c, - 0x3a, 0x85, 0xaa, 0xdc, 0xee, 0x8e, 0xb6, 0xc0, 0x75, 0xcc, 0x52, 0xf4, 0xd2, 0x16, 0xb9, 0x5b, 0xfe, 0xdd, 0x73, - 0x45, 0xce, 0x7e, 0x09, 0x08, 0x4e, 0xcd, 0x57, 0xc4, 0x97, 0x23, 0x3c, 0xaa, 0x6e, 0x81, 0xe3, 0xf4, 0x1d, 0xc0, - 0x3f, 0x1c, 0x2e, 0x41, 0x13, 0x10, 0x0b, 0xd6, 0x4b, 0xe3, 0x1e, 0xeb, 0xc5, 0xc5, 0xe6, 0x2e, 0xc9, 0x37, 0xe0, - 0xcc, 0x40, 0xa9, 0x96, 0x7e, 0xe0, 0x58, 0x2d, 0xa0, 0xc2, 0xc1, 0xec, 0xa4, 0x5e, 0x58, 0x46, 0x3d, 0xa6, 0xcf, - 0xcf, 0x60, 0xef, 0x08, 0x09, 0x80, 0xfb, 0x65, 0x1f, 0x90, 0x80, 0x87, 0xce, 0xec, 0x80, 0x70, 0xc2, 0x2c, 0xaa, - 0x02, 0x89, 0xe4, 0x48, 0x3f, 0x7b, 0xcc, 0x44, 0xf2, 0x07, 0xb3, 0x9e, 0x73, 0x4a, 0xf4, 0x58, 0x4f, 0x1d, 0x21, - 0x3d, 0xd6, 0xb3, 0x8e, 0x88, 0x1e, 0xeb, 0x59, 0xc7, 0x47, 0x8f, 0xf5, 0xcc, 0xb1, 0xd3, 0x83, 0xc0, 0x04, 0x88, - 0x3c, 0x60, 0x3d, 0x9a, 0x4c, 0x3d, 0xc5, 0x3d, 0x40, 0x34, 0x08, 0xac, 0x27, 0x85, 0xf3, 0x1e, 0x20, 0x8f, 0x91, - 0x58, 0x1d, 0xf4, 0xfe, 0x32, 0x7e, 0xda, 0x33, 0x32, 0xf2, 0xb8, 0x75, 0x58, 0xfd, 0xaf, 0xbf, 0x42, 0x00, 0x1c, - 0x9e, 0x4d, 0xbd, 0xcb, 0x31, 0x64, 0x95, 0x65, 0x04, 0x92, 0x9f, 0x18, 0x7c, 0xf9, 0x02, 0xa0, 0xea, 0x33, 0x5d, - 0xab, 0xc9, 0x51, 0x7b, 0xcc, 0xa1, 0x2b, 0x06, 0x80, 0x6d, 0x58, 0xa2, 0xaa, 0x16, 0x36, 0x61, 0x71, 0xfb, 0x19, - 0x46, 0x73, 0xd9, 0xf4, 0x82, 0x06, 0xea, 0x11, 0x82, 0x5f, 0x5a, 0x0f, 0xad, 0xb5, 0x4c, 0x39, 0x74, 0x6d, 0x14, - 0x55, 0x36, 0xd4, 0x25, 0xac, 0xd6, 0x22, 0xaa, 0x89, 0x22, 0xe5, 0x92, 0x51, 0x14, 0x4b, 0x15, 0xec, 0x33, 0x71, - 0x07, 0x51, 0xf3, 0xb4, 0xd5, 0x56, 0xc1, 0xfe, 0x0e, 0x10, 0xd6, 0xc2, 0x5a, 0x48, 0x67, 0x50, 0x7b, 0xa7, 0x1f, - 0x29, 0x7f, 0x79, 0x21, 0xb7, 0x73, 0x0b, 0x45, 0xb8, 0x3d, 0x07, 0xe5, 0x4d, 0x5d, 0x95, 0x8a, 0x68, 0xb4, 0x04, - 0x4a, 0x99, 0x13, 0x44, 0x16, 0x20, 0x80, 0xe3, 0x06, 0x02, 0x9f, 0xd7, 0xf8, 0x04, 0x1a, 0x85, 0x40, 0x7e, 0x60, - 0x15, 0xae, 0x3d, 0xa4, 0xa5, 0xd6, 0x88, 0x28, 0x11, 0x3f, 0xba, 0x7a, 0x8e, 0xed, 0xab, 0xa7, 0xb1, 0xb6, 0x94, - 0x26, 0x88, 0x9f, 0x58, 0x6c, 0x21, 0x26, 0x88, 0xea, 0x10, 0x1d, 0xc1, 0x72, 0x42, 0x88, 0xc2, 0x1f, 0x42, 0x3f, - 0x35, 0xf0, 0x97, 0x6c, 0x59, 0xe4, 0x35, 0xc1, 0x62, 0x56, 0x0c, 0xd0, 0xaa, 0x08, 0x3c, 0xd3, 0xd9, 0x52, 0x99, - 0xd3, 0x3c, 0x3a, 0xb2, 0x83, 0xf3, 0xae, 0x83, 0xbd, 0xf4, 0x65, 0xec, 0x64, 0xd9, 0x34, 0x6a, 0x63, 0x43, 0x24, - 0xbc, 0x22, 0x7f, 0x95, 0xa5, 0xc6, 0x39, 0x32, 0x97, 0xeb, 0xbb, 0x2e, 0xee, 0xee, 0x68, 0x9b, 0xb0, 0x0a, 0x11, - 0xea, 0xb6, 0xa1, 0x72, 0x29, 0xcc, 0xc6, 0xa6, 0x69, 0x80, 0x2f, 0x14, 0x95, 0x4a, 0x55, 0x6a, 0x2b, 0x95, 0x9c, - 0xf0, 0xae, 0xaf, 0x6a, 0x91, 0xba, 0x22, 0xd8, 0xc6, 0x0c, 0xf5, 0x50, 0x6e, 0xd4, 0xd8, 0xd7, 0x1d, 0xab, 0xf4, - 0x0e, 0x13, 0xe4, 0x8c, 0xbc, 0xc8, 0xc1, 0x45, 0x49, 0x41, 0xe6, 0x6a, 0x08, 0xf3, 0x47, 0x0d, 0x9f, 0x16, 0x96, - 0x7b, 0x28, 0x01, 0xb3, 0xa3, 0x86, 0x97, 0x11, 0x02, 0x11, 0x97, 0xca, 0xbe, 0x62, 0xe2, 0xf7, 0x14, 0xcc, 0x92, - 0x09, 0xdd, 0x8b, 0x58, 0x18, 0xa1, 0x8d, 0x4f, 0x92, 0x64, 0xea, 0x69, 0x0a, 0x6e, 0xe4, 0x32, 0xcc, 0xd1, 0x08, - 0x2d, 0xf9, 0xc8, 0x81, 0xf4, 0xb5, 0x9c, 0x4a, 0xf0, 0x11, 0x75, 0x0a, 0x38, 0x9e, 0x9f, 0x17, 0xd6, 0x4f, 0x96, - 0x4b, 0xcc, 0x65, 0x6d, 0xfe, 0xcb, 0x8e, 0x8e, 0xc1, 0x2e, 0x4f, 0x13, 0xc7, 0xd5, 0x7f, 0x54, 0x25, 0xc5, 0xc3, - 0xcf, 0x69, 0x0e, 0x28, 0x82, 0x99, 0x3d, 0xc5, 0xf8, 0xd8, 0x67, 0x99, 0x02, 0xfe, 0x76, 0xbd, 0xb5, 0x64, 0x62, - 0x97, 0xb4, 0x9b, 0x2b, 0xe3, 0x97, 0xda, 0xb0, 0xe3, 0xe0, 0xdc, 0x00, 0x14, 0x67, 0x8d, 0x0e, 0xcb, 0x6b, 0xdd, - 0xb6, 0x2a, 0x54, 0xa0, 0xd6, 0xff, 0xd9, 0x2d, 0x4c, 0x79, 0x9b, 0x97, 0xca, 0xdb, 0x3c, 0x34, 0x01, 0x02, 0x91, - 0x19, 0xf2, 0xac, 0xe9, 0x98, 0x24, 0xee, 0x1d, 0x29, 0x69, 0xdf, 0x91, 0xe2, 0x47, 0xef, 0x48, 0xc8, 0xb7, 0x84, - 0x8e, 0xec, 0x4b, 0x4e, 0x4e, 0xa0, 0xcc, 0x60, 0x2f, 0xaf, 0x99, 0xec, 0x1f, 0xd0, 0x5e, 0x38, 0x97, 0xe5, 0x15, - 0xbf, 0x16, 0xde, 0xda, 0x9f, 0xae, 0x4f, 0xbb, 0xaa, 0xde, 0x7e, 0x65, 0x66, 0x1e, 0x0e, 0xc5, 0xe1, 0x50, 0x99, - 0xa0, 0xdd, 0x05, 0x17, 0x83, 0x9c, 0xdd, 0xbb, 0xf1, 0xf1, 0xd7, 0x1c, 0x45, 0x6c, 0xa5, 0x3c, 0x92, 0x2e, 0x54, - 0x62, 0x78, 0x69, 0xe0, 0x61, 0x76, 0x7c, 0x3c, 0xd9, 0x5d, 0xdd, 0x4f, 0x06, 0x83, 0x9d, 0xea, 0xdb, 0x2d, 0xaf, - 0x67, 0xbb, 0x39, 0x7b, 0xe0, 0xb7, 0xd3, 0x6d, 0xb0, 0x6f, 0x60, 0xdb, 0xdd, 0x5d, 0x89, 0xc3, 0x61, 0xf7, 0x82, - 0x2f, 0xfc, 0xfd, 0x03, 0x02, 0x3a, 0xf3, 0xf3, 0x71, 0x1b, 0xe3, 0xe7, 0xa6, 0xed, 0xaa, 0xb5, 0x03, 0x78, 0xfa, - 0x1f, 0xbc, 0x9b, 0xd9, 0x72, 0xee, 0xb3, 0x27, 0xfc, 0x01, 0xfc, 0xf3, 0x71, 0x93, 0x44, 0xea, 0x13, 0xed, 0x32, - 0x79, 0x03, 0x0e, 0xe4, 0x3b, 0x9f, 0xbd, 0xe5, 0x0f, 0xb3, 0xe5, 0x9c, 0x17, 0x87, 0xc3, 0xfb, 0x69, 0x88, 0x64, - 0x4d, 0x61, 0x45, 0x2c, 0x29, 0x9e, 0x1f, 0x84, 0xc7, 0xef, 0x45, 0x64, 0x88, 0xb4, 0xdc, 0xbb, 0x43, 0x76, 0xc3, - 0x22, 0x3f, 0x80, 0x0f, 0xb2, 0x9d, 0x3f, 0x91, 0x35, 0xa5, 0xfb, 0xc5, 0x13, 0xff, 0x70, 0xa0, 0xbf, 0xde, 0xfa, - 0x87, 0xc3, 0x7b, 0xf6, 0x80, 0xe0, 0xe8, 0x7c, 0x07, 0xfd, 0xa3, 0x6f, 0x1d, 0x50, 0x95, 0xe1, 0xf5, 0x6c, 0x33, - 0xf7, 0x5f, 0xac, 0xd8, 0x1d, 0x70, 0xa1, 0x28, 0x2f, 0xb4, 0x1b, 0xf6, 0x80, 0x5e, 0x67, 0xe4, 0x44, 0x34, 0xdb, - 0xcd, 0x7d, 0x16, 0xe3, 0x73, 0x75, 0x5f, 0x4c, 0xbe, 0x7a, 0x5f, 0xdc, 0xb1, 0x6d, 0xf7, 0x7d, 0x51, 0xbe, 0xe9, - 0xae, 0x9f, 0x2d, 0xdb, 0xb1, 0x07, 0x98, 0x61, 0xd7, 0xfc, 0xa6, 0x39, 0x76, 0x8c, 0xfd, 0xea, 0x8d, 0x11, 0x40, - 0x99, 0x2d, 0x58, 0x2c, 0x38, 0x28, 0xd5, 0xaa, 0x6d, 0x49, 0xe4, 0x95, 0x0e, 0x54, 0x9b, 0x11, 0xdc, 0x57, 0x0b, - 0x39, 0xf3, 0xcc, 0x40, 0xdf, 0x56, 0x88, 0x16, 0x0e, 0x1b, 0xf0, 0x57, 0xda, 0x3a, 0xc6, 0x30, 0xcd, 0x6a, 0xa6, - 0x6d, 0x51, 0x97, 0xdf, 0xf6, 0x9e, 0xc9, 0x6f, 0x64, 0x60, 0x0b, 0x91, 0x14, 0x8e, 0xe3, 0x8b, 0xe7, 0x27, 0xfc, - 0x57, 0x2d, 0x8f, 0x5a, 0xed, 0x17, 0x4a, 0x7d, 0xfa, 0x8a, 0x8e, 0x68, 0xe2, 0x5e, 0xb4, 0x65, 0x58, 0xa3, 0xac, - 0xa9, 0xa5, 0xc3, 0x30, 0xae, 0x61, 0x5f, 0x1e, 0x38, 0xf4, 0x1d, 0x10, 0x68, 0xab, 0x54, 0x0a, 0xb4, 0x70, 0x0c, - 0xa3, 0x30, 0x0b, 0x29, 0x8f, 0x0b, 0xb3, 0x94, 0xf7, 0x58, 0xa0, 0xc5, 0xad, 0xba, 0xc7, 0xd4, 0x76, 0x0b, 0x22, - 0xac, 0xde, 0x32, 0xce, 0x2f, 0x1b, 0x55, 0xb8, 0x2d, 0x40, 0x51, 0x04, 0x65, 0xb0, 0x27, 0xb9, 0x6d, 0xa1, 0xa4, - 0xd9, 0x28, 0xac, 0xc5, 0x5d, 0x51, 0xee, 0x7a, 0x0d, 0x5b, 0xe0, 0x05, 0x55, 0x3f, 0x21, 0x6c, 0xcb, 0x9e, 0x75, - 0x28, 0x17, 0xe9, 0x7f, 0x64, 0xe9, 0xf9, 0x76, 0x6b, 0xce, 0xff, 0xf4, 0x15, 0x7d, 0x54, 0xfe, 0xe7, 0x97, 0xf4, - 0x93, 0xc1, 0x32, 0x72, 0x4a, 0x7d, 0x1f, 0x8d, 0x6e, 0xd3, 0x9c, 0x30, 0xb6, 0x7c, 0xfd, 0xf4, 0x1b, 0x64, 0x0a, - 0x92, 0x43, 0x29, 0x55, 0x39, 0xd9, 0x43, 0x5f, 0x78, 0xdd, 0x87, 0x99, 0x60, 0x00, 0xc2, 0x6b, 0xb4, 0xa9, 0x26, - 0x4c, 0xe2, 0xd1, 0x15, 0xfc, 0xdf, 0x08, 0x62, 0xd0, 0x3e, 0x51, 0xd4, 0xb1, 0x6d, 0xa4, 0xeb, 0xb6, 0x73, 0x90, - 0xdc, 0xa9, 0x2b, 0x7f, 0x54, 0x4e, 0xfe, 0x13, 0x0d, 0x91, 0x57, 0x5c, 0x21, 0x56, 0x16, 0x5c, 0x62, 0x31, 0x54, - 0xa4, 0x00, 0xd7, 0x10, 0x44, 0xca, 0xa2, 0xa4, 0x70, 0xcb, 0x41, 0x55, 0x04, 0x60, 0x5c, 0xad, 0x8e, 0x3a, 0x11, - 0x3e, 0x6e, 0xad, 0x45, 0x08, 0x56, 0x34, 0x6a, 0x65, 0xad, 0xc0, 0x17, 0xa4, 0x2f, 0x1d, 0x0a, 0x62, 0x7a, 0x14, - 0x52, 0x55, 0x3a, 0x14, 0x48, 0x73, 0xa8, 0xf8, 0xc6, 0x60, 0xa3, 0xa8, 0x48, 0xcf, 0x5f, 0x9a, 0x94, 0x5c, 0x1a, - 0x33, 0x3e, 0x88, 0x32, 0x12, 0x79, 0x1d, 0xde, 0x89, 0x69, 0x81, 0x7c, 0xa3, 0xc7, 0x0f, 0x82, 0x4b, 0x78, 0x37, - 0xe4, 0x5e, 0x01, 0xb6, 0x04, 0xec, 0x00, 0xf7, 0xca, 0x8c, 0x72, 0x9d, 0xd6, 0xf5, 0x5b, 0xeb, 0xa1, 0x18, 0x86, - 0xcf, 0x2c, 0x81, 0xed, 0x68, 0x1d, 0x1d, 0xe9, 0xe1, 0xc3, 0xff, 0xba, 0xaa, 0x39, 0xea, 0x54, 0x2e, 0x67, 0xc7, - 0x13, 0x96, 0x22, 0x66, 0xd0, 0xfd, 0x75, 0xfb, 0x4a, 0x00, 0xdd, 0x2e, 0x8b, 0x79, 0x36, 0xda, 0xc9, 0xbf, 0xa5, - 0x1b, 0x2b, 0x4a, 0x9b, 0x78, 0x97, 0xf5, 0xc6, 0xfe, 0x70, 0xf4, 0x97, 0x67, 0x5f, 0x26, 0x84, 0xaa, 0xb3, 0x61, - 0x6b, 0x1d, 0xe7, 0xf2, 0xbf, 0xfe, 0x3a, 0x26, 0x2b, 0x08, 0x0a, 0xc2, 0xb2, 0x53, 0x4c, 0x54, 0x30, 0x8a, 0x14, - 0x6b, 0x3e, 0x9e, 0xac, 0x51, 0x27, 0xbc, 0xf6, 0x97, 0x5a, 0x27, 0x4c, 0x8c, 0xac, 0x54, 0xfe, 0x9a, 0x55, 0xec, - 0x4e, 0x65, 0x16, 0x90, 0x79, 0x90, 0x4f, 0xd6, 0x46, 0x83, 0xb9, 0xe2, 0xf5, 0x6c, 0x3d, 0x97, 0xca, 0x67, 0x30, - 0xe5, 0x2c, 0x07, 0x27, 0x4b, 0x61, 0xf7, 0x24, 0x50, 0xb4, 0x66, 0xe8, 0xda, 0x9f, 0x62, 0xab, 0x5e, 0xa7, 0x55, - 0x0d, 0xf0, 0x80, 0x10, 0x03, 0x43, 0xed, 0xd5, 0xc2, 0x43, 0x6b, 0x01, 0xac, 0xfd, 0x51, 0xe9, 0x07, 0xe3, 0xc9, - 0x92, 0x2f, 0x90, 0x7f, 0x39, 0x72, 0xd4, 0xee, 0xfd, 0xbe, 0x77, 0x0f, 0x52, 0x70, 0xe4, 0x5a, 0x28, 0x90, 0x08, - 0x68, 0xc1, 0x37, 0xbe, 0xf2, 0xc1, 0xb8, 0x46, 0x6d, 0x35, 0x28, 0xa8, 0x1d, 0xdd, 0xf2, 0xd8, 0xd1, 0x3b, 0xdf, - 0x9f, 0xd0, 0x57, 0x2f, 0xb4, 0x70, 0xfc, 0x95, 0x33, 0x72, 0xcd, 0x56, 0x1d, 0x72, 0x44, 0x33, 0xe9, 0x10, 0x22, - 0x56, 0x6c, 0xcd, 0xae, 0x49, 0xe5, 0xdc, 0x39, 0x64, 0xa7, 0x8f, 0x50, 0xa5, 0xd7, 0x7a, 0x7c, 0x3b, 0x51, 0xba, - 0xdb, 0xe3, 0xdd, 0xe4, 0x5b, 0x36, 0x11, 0x31, 0x18, 0xd0, 0x06, 0xe1, 0x8c, 0xac, 0x43, 0xa4, 0xd2, 0x01, 0x42, - 0xe0, 0x98, 0x80, 0xa6, 0xff, 0xf8, 0x9a, 0x44, 0x01, 0x47, 0xda, 0x08, 0x59, 0xcb, 0x0e, 0x87, 0x1c, 0x34, 0xca, - 0xcd, 0x1f, 0x5e, 0xa1, 0x4e, 0x73, 0x60, 0x9e, 0x2e, 0x61, 0xcf, 0xc1, 0x23, 0xbd, 0x38, 0x3e, 0xd2, 0xff, 0x3b, - 0x9a, 0xa8, 0xf1, 0x7f, 0xae, 0x89, 0x52, 0x5a, 0x24, 0x47, 0xb5, 0xf4, 0x4d, 0xea, 0x28, 0xb8, 0xc8, 0x3b, 0x6a, - 0x21, 0x7b, 0x96, 0x8d, 0x1b, 0xd5, 0xbc, 0xff, 0x5f, 0x2b, 0xf3, 0xff, 0x35, 0xad, 0x0c, 0x53, 0xb2, 0x63, 0xa9, - 0x66, 0x1e, 0x68, 0x15, 0xc3, 0xec, 0x67, 0x92, 0x10, 0x19, 0x2e, 0x0d, 0xf8, 0x51, 0x05, 0xfb, 0x38, 0xad, 0xd6, - 0x59, 0xb8, 0x43, 0x25, 0xea, 0xad, 0xb8, 0x4b, 0xf3, 0x97, 0xf5, 0xbf, 0x45, 0x59, 0xc0, 0xd4, 0xbe, 0x2b, 0xd3, - 0x38, 0x20, 0x0b, 0x7f, 0x16, 0x96, 0x38, 0xb9, 0xb1, 0x8d, 0x3f, 0xcb, 0xf1, 0xb4, 0x5f, 0x75, 0x66, 0x1e, 0x48, - 0xa0, 0x06, 0xe2, 0x8f, 0x9c, 0xcb, 0xca, 0xe2, 0x01, 0xa1, 0x9b, 0x7f, 0x28, 0xcb, 0xa2, 0xf4, 0x7a, 0x9f, 0x92, - 0xb4, 0x3a, 0x5b, 0x89, 0x3a, 0x29, 0x62, 0x05, 0x65, 0x93, 0x02, 0x8c, 0x3e, 0xac, 0x3c, 0x11, 0x07, 0x67, 0x08, - 0xd4, 0x70, 0x56, 0x27, 0x21, 0x00, 0x0d, 0x2b, 0x84, 0xfd, 0x33, 0x68, 0xe1, 0x59, 0x18, 0x87, 0x6b, 0x80, 0xc9, - 0x49, 0xab, 0xb3, 0x75, 0x59, 0xdc, 0xa7, 0xb1, 0x88, 0x47, 0x3d, 0x45, 0xc9, 0xf2, 0x26, 0x77, 0xe5, 0x5c, 0x7f, - 0xff, 0x07, 0x05, 0xb0, 0x1b, 0x30, 0xdb, 0x16, 0xd8, 0x01, 0x40, 0x82, 0x02, 0xd9, 0x42, 0x9d, 0x46, 0x67, 0x6a, - 0xa9, 0xc0, 0x7b, 0xae, 0x07, 0xf8, 0x9b, 0x1c, 0xb0, 0x8c, 0xeb, 0x42, 0x06, 0x8c, 0x20, 0x80, 0x11, 0x38, 0x28, - 0x01, 0x43, 0x67, 0x88, 0xdb, 0xaa, 0x9c, 0xb5, 0xd0, 0x5c, 0xe9, 0xb6, 0xe4, 0xa6, 0x51, 0xce, 0x56, 0x22, 0x80, - 0xbe, 0xba, 0x29, 0x71, 0xba, 0x5c, 0xb6, 0x92, 0xb0, 0x6f, 0xdf, 0xb7, 0x53, 0x45, 0x1e, 0x1f, 0xa5, 0x21, 0xaf, - 0xc0, 0x93, 0x8c, 0x23, 0x49, 0x94, 0x08, 0xde, 0xe4, 0x8d, 0x19, 0x87, 0x97, 0x6d, 0xca, 0xa9, 0xbd, 0x59, 0x2f, - 0x00, 0xe7, 0x09, 0xda, 0x32, 0xc0, 0x58, 0xc0, 0xe0, 0x5c, 0x88, 0x25, 0x4f, 0x11, 0xfc, 0xd2, 0x89, 0x14, 0xc6, - 0x5d, 0x0e, 0xc3, 0x3c, 0x28, 0x7a, 0x97, 0xd4, 0x1f, 0xfd, 0x3e, 0x6a, 0x93, 0xc1, 0x10, 0x54, 0x02, 0xa8, 0xac, - 0x1b, 0x24, 0x06, 0x56, 0xa5, 0x85, 0xc4, 0x25, 0xc4, 0xcb, 0x7c, 0x35, 0xad, 0xa3, 0xe0, 0x7d, 0x3d, 0x21, 0x84, - 0x13, 0x8c, 0x0f, 0x71, 0x03, 0x04, 0x0c, 0x56, 0x71, 0x81, 0x41, 0xf2, 0x5c, 0xa2, 0xfb, 0xe3, 0xf9, 0x8e, 0x01, - 0xae, 0x9c, 0xf7, 0x54, 0xbb, 0x7a, 0x60, 0x2f, 0x57, 0xe9, 0x92, 0x11, 0xc2, 0x8a, 0xff, 0x8b, 0xc8, 0xfb, 0x76, - 0x98, 0x80, 0xda, 0x46, 0xfe, 0x18, 0x24, 0xe6, 0x32, 0x51, 0x04, 0xf1, 0x28, 0x2b, 0x58, 0x92, 0x06, 0x9b, 0x51, - 0x92, 0x82, 0x46, 0x13, 0x63, 0xc8, 0x54, 0x68, 0x87, 0xa4, 0xd1, 0x6c, 0x4c, 0xf6, 0x31, 0xe4, 0x35, 0x5c, 0x2c, - 0x16, 0x78, 0xdf, 0xcf, 0x42, 0x75, 0xb0, 0x2d, 0xcd, 0x21, 0xe0, 0x24, 0xc1, 0x9e, 0xba, 0x22, 0x25, 0x61, 0x36, - 0xfa, 0x14, 0x72, 0x6e, 0x40, 0xc7, 0x49, 0x63, 0xa8, 0x3e, 0x30, 0x09, 0xaf, 0x22, 0x74, 0x52, 0x56, 0x08, 0x0b, - 0xb8, 0x6f, 0x64, 0x34, 0x5a, 0x49, 0x83, 0xc0, 0xdb, 0x0c, 0x5b, 0x81, 0x4d, 0x68, 0xf8, 0xcb, 0xcc, 0xc3, 0xb4, - 0x9a, 0x95, 0x60, 0xce, 0x37, 0x50, 0x89, 0xf1, 0x64, 0x79, 0xc5, 0x37, 0x2e, 0x56, 0x62, 0x32, 0x5b, 0xce, 0x27, - 0x6b, 0x49, 0x35, 0x97, 0x7b, 0x6b, 0x96, 0xb1, 0x25, 0xec, 0x1f, 0x06, 0x86, 0xd2, 0x81, 0x1d, 0x4d, 0x35, 0x6d, - 0x12, 0x60, 0x32, 0x9d, 0x73, 0x3e, 0xbc, 0x44, 0x34, 0x59, 0x9d, 0xba, 0x93, 0xa9, 0x6a, 0x07, 0xd7, 0xe4, 0x4c, - 0x4e, 0x8f, 0xd4, 0x53, 0xad, 0x7b, 0xc9, 0x47, 0xdb, 0x61, 0x35, 0xda, 0xfa, 0x01, 0xb8, 0x75, 0x0a, 0x3b, 0x7d, - 0x37, 0xac, 0x46, 0x3b, 0x5f, 0xc3, 0xee, 0x92, 0x42, 0xa0, 0xfa, 0xb3, 0xac, 0xc9, 0x5c, 0xbc, 0x2e, 0x1e, 0xbc, - 0x82, 0x3d, 0xf7, 0x07, 0xfa, 0x57, 0xc9, 0x9e, 0xfb, 0x36, 0x93, 0xeb, 0x9f, 0x69, 0xd7, 0x68, 0xcc, 0x74, 0xbc, - 0x76, 0x05, 0x56, 0x68, 0x80, 0xfc, 0x82, 0x1d, 0xed, 0x6d, 0x0e, 0x02, 0x01, 0xba, 0x97, 0xe0, 0x28, 0x0a, 0x88, - 0x9a, 0x56, 0x95, 0x47, 0xa7, 0x7b, 0x7f, 0x8f, 0x6f, 0x94, 0x80, 0x4d, 0x9e, 0x5a, 0xf7, 0x96, 0xb1, 0x7f, 0x38, - 0x40, 0x08, 0xbd, 0x9c, 0x7e, 0xa3, 0x2d, 0xab, 0x47, 0x3b, 0x96, 0xfb, 0x86, 0x51, 0x4f, 0xc1, 0x18, 0x86, 0x2e, - 0xac, 0x62, 0x24, 0xcf, 0x80, 0xac, 0xf1, 0x1b, 0x44, 0x17, 0xb0, 0xe8, 0xf5, 0x5e, 0x1f, 0xd1, 0x20, 0x02, 0x2a, - 0xbd, 0xe6, 0x2f, 0x45, 0x3e, 0x57, 0x85, 0xe8, 0xbd, 0xb7, 0x76, 0xde, 0xcc, 0x48, 0x96, 0x49, 0x23, 0xd5, 0x6e, - 0x65, 0xb1, 0xae, 0xbc, 0xd9, 0x09, 0xe9, 0x62, 0x8e, 0xa1, 0x32, 0x78, 0x1c, 0x80, 0xd2, 0xf3, 0x6f, 0xa1, 0x57, - 0x32, 0x64, 0x9a, 0x25, 0x9a, 0xd9, 0x5d, 0xe3, 0x4f, 0x56, 0xa9, 0x17, 0x23, 0x62, 0x36, 0xb0, 0x85, 0xb8, 0x2d, - 0x2a, 0xdd, 0x16, 0x85, 0xb2, 0x45, 0x91, 0x3e, 0xd4, 0xce, 0x74, 0x67, 0x16, 0x3e, 0xab, 0x4c, 0xfb, 0xde, 0x66, - 0x66, 0x6c, 0x80, 0xb6, 0x8b, 0xf0, 0x0d, 0x74, 0xa0, 0x42, 0xc8, 0x7f, 0x40, 0x44, 0x24, 0x02, 0x76, 0x39, 0x75, - 0x27, 0x36, 0x1d, 0x92, 0x79, 0x88, 0x59, 0xa1, 0x46, 0x79, 0xc9, 0x93, 0xa3, 0x01, 0xa9, 0x08, 0x75, 0xbb, 0xdf, - 0x3f, 0x5f, 0xba, 0xa0, 0xf6, 0x6b, 0x8a, 0x1d, 0xa3, 0x9b, 0x02, 0xce, 0x05, 0x8f, 0xf2, 0x9e, 0x7b, 0xe7, 0x80, - 0xe6, 0xd8, 0x9e, 0x22, 0x6b, 0xc0, 0xe9, 0x6d, 0x17, 0x02, 0x6c, 0x9f, 0x35, 0x5b, 0xfb, 0x93, 0xd5, 0x55, 0x34, - 0xf5, 0x4a, 0x3e, 0xd3, 0x5d, 0x94, 0xb8, 0x5d, 0x14, 0xcb, 0x2e, 0xda, 0x34, 0x10, 0xec, 0xb8, 0xf2, 0x03, 0xe0, - 0x0d, 0x8d, 0xfa, 0xfd, 0xb2, 0xd5, 0xb3, 0x27, 0x5f, 0x3b, 0xee, 0xd9, 0xcc, 0x67, 0xa5, 0xe9, 0xd9, 0x5f, 0x53, - 0xb7, 0x67, 0xe5, 0x64, 0x2f, 0x3a, 0x27, 0xfb, 0x74, 0x36, 0x0f, 0x04, 0x97, 0x3b, 0xf7, 0x79, 0x3e, 0xd5, 0xd3, - 0xae, 0xf2, 0x83, 0xd6, 0x10, 0x99, 0x2f, 0x7c, 0xaa, 0xba, 0xd7, 0x15, 0x2c, 0x60, 0x09, 0xee, 0xd6, 0x4b, 0xf3, - 0x5f, 0xb1, 0xfb, 0x7b, 0x41, 0x2f, 0xcd, 0x7f, 0xa3, 0x3f, 0x29, 0x80, 0x03, 0xd0, 0x98, 0xda, 0x2d, 0xf0, 0x10, - 0x43, 0x05, 0x85, 0xbb, 0x59, 0x39, 0xf7, 0x6a, 0x80, 0xc3, 0x24, 0x7d, 0x43, 0xab, 0x57, 0x5a, 0xec, 0x7a, 0x99, - 0xec, 0x15, 0xe0, 0xa1, 0x0a, 0x79, 0x78, 0x38, 0x44, 0x1d, 0xc3, 0x0e, 0xea, 0x08, 0x18, 0xf6, 0x10, 0x1a, 0x5b, - 0xe0, 0xf9, 0xf8, 0x29, 0xe3, 0x7b, 0x01, 0x6a, 0x23, 0x84, 0xc7, 0xab, 0x45, 0x19, 0x62, 0xcb, 0xde, 0x22, 0x95, - 0xd4, 0xcf, 0x02, 0x51, 0x46, 0xab, 0x80, 0xb6, 0xda, 0x63, 0x96, 0xc6, 0x1b, 0x08, 0x15, 0x4b, 0x7d, 0x0c, 0xa1, - 0x81, 0xc3, 0xef, 0x70, 0x00, 0x09, 0xbe, 0xe4, 0x9a, 0x6c, 0xee, 0x6d, 0x7e, 0x4f, 0xfb, 0xfc, 0xe1, 0x70, 0x7e, - 0x89, 0xa0, 0x74, 0x29, 0x7c, 0xa4, 0x12, 0x51, 0x3d, 0xc5, 0x4d, 0x09, 0xd9, 0x2c, 0x59, 0xe9, 0x07, 0xbf, 0xaa, - 0x5f, 0x00, 0x20, 0x0b, 0x81, 0x36, 0x91, 0xd9, 0x9f, 0xce, 0x54, 0x74, 0x01, 0x70, 0x88, 0x3f, 0x7e, 0x82, 0xe8, - 0x1b, 0x5a, 0xa6, 0xe5, 0xe3, 0x84, 0x87, 0xa0, 0xb5, 0x25, 0x9d, 0x44, 0xac, 0x14, 0xd8, 0x10, 0x09, 0xdf, 0xef, - 0x9f, 0xc7, 0x92, 0x0e, 0x34, 0x6a, 0x75, 0x6f, 0xdc, 0xea, 0x5e, 0xf9, 0xba, 0xee, 0xe4, 0xc6, 0x07, 0x45, 0xfb, - 0x6c, 0xde, 0xa8, 0x7c, 0xdf, 0xd6, 0x39, 0xbb, 0xd3, 0xbd, 0x23, 0xe7, 0xc4, 0xb7, 0xf7, 0x10, 0x8a, 0x1e, 0x9a, - 0x22, 0xcb, 0x92, 0x30, 0xa0, 0xb5, 0x76, 0xed, 0x59, 0x46, 0x07, 0xaf, 0x7d, 0x43, 0x88, 0xc8, 0x53, 0x7c, 0x12, - 0x72, 0x8b, 0xe3, 0x83, 0x02, 0xfd, 0x33, 0xe3, 0xcf, 0x9c, 0xf8, 0x61, 0xab, 0x5f, 0x00, 0xe7, 0xa6, 0x7b, 0xef, - 0x4e, 0xcc, 0x7a, 0x0c, 0xa5, 0x6c, 0xfc, 0xdf, 0xef, 0x13, 0x59, 0xa0, 0xd3, 0x11, 0x0d, 0x03, 0xc1, 0x5d, 0x54, - 0xff, 0xf7, 0x8a, 0xd7, 0x3d, 0x6b, 0x75, 0xbe, 0xfc, 0xd4, 0xe9, 0x49, 0xaf, 0x5e, 0xc6, 0x3d, 0xa0, 0x42, 0x07, - 0x08, 0xe7, 0x75, 0xbf, 0x61, 0xbb, 0x6f, 0x7e, 0x79, 0x77, 0xf4, 0x32, 0xb0, 0x49, 0x91, 0xd8, 0x56, 0xf2, 0x59, - 0x0f, 0x14, 0x7e, 0x3d, 0xd6, 0xab, 0x8b, 0x75, 0x8f, 0xf5, 0x50, 0x0b, 0x88, 0x1e, 0x16, 0xa0, 0xfe, 0xeb, 0xd9, - 0xa7, 0xa1, 0x70, 0x90, 0x8d, 0x53, 0x05, 0x8a, 0x2c, 0xf8, 0x0b, 0x31, 0x5a, 0x17, 0x04, 0x88, 0x6c, 0x09, 0x69, - 0xd5, 0xc9, 0xec, 0x71, 0xa9, 0x25, 0x19, 0x7c, 0x13, 0x90, 0xd9, 0x81, 0x95, 0x13, 0x94, 0x8e, 0x5b, 0x03, 0xae, - 0x6c, 0xf1, 0x68, 0xb7, 0x3f, 0x0d, 0xb2, 0xb3, 0xe6, 0xa4, 0xd1, 0x3e, 0xec, 0xd3, 0x3c, 0x40, 0x20, 0x92, 0xa9, - 0x08, 0x72, 0xcd, 0xbd, 0x25, 0x7d, 0x74, 0x38, 0xe7, 0x85, 0xfc, 0x73, 0x2a, 0x75, 0x88, 0x43, 0x89, 0x35, 0x10, - 0xa8, 0x3c, 0x43, 0x95, 0xc3, 0x06, 0x39, 0xfe, 0xd9, 0x91, 0xcc, 0x24, 0x26, 0x8b, 0xdc, 0xad, 0x99, 0x0a, 0x3f, - 0x10, 0x7c, 0xcc, 0x72, 0x0e, 0x5c, 0x60, 0xb3, 0xb9, 0xaf, 0xa6, 0xb8, 0xb8, 0x02, 0x7f, 0x4c, 0xe1, 0x57, 0x3c, - 0x85, 0x9d, 0x76, 0xbf, 0x2e, 0xaa, 0x14, 0x75, 0x1b, 0x85, 0x45, 0x25, 0x0b, 0xa6, 0x35, 0xa4, 0x89, 0x0e, 0xa3, - 0x3f, 0xc8, 0x19, 0x28, 0x08, 0xf9, 0x65, 0xd3, 0x00, 0x23, 0x95, 0x5c, 0x1e, 0x54, 0x49, 0xe0, 0x05, 0xd8, 0x06, - 0x15, 0x5b, 0x17, 0x10, 0x64, 0x9b, 0x14, 0x65, 0xfa, 0xa5, 0xc8, 0xeb, 0x30, 0x0b, 0xaa, 0x51, 0x5a, 0xfd, 0xa8, - 0x7f, 0x02, 0xf3, 0x36, 0x15, 0xa3, 0x5a, 0xc5, 0xe4, 0x37, 0xfa, 0xfd, 0x62, 0xd0, 0xfa, 0x90, 0xc1, 0x47, 0xaf, - 0x4d, 0x83, 0x3f, 0x3a, 0x0d, 0x76, 0x98, 0x68, 0x04, 0x40, 0x32, 0xa7, 0x96, 0x3c, 0x14, 0xfd, 0x11, 0xe4, 0x58, - 0xa3, 0xca, 0x29, 0x18, 0xac, 0xff, 0x78, 0xb4, 0x03, 0x53, 0x2f, 0x8e, 0xb6, 0x64, 0x07, 0xad, 0x7c, 0x03, 0xdc, - 0xaf, 0x91, 0x2d, 0x66, 0x39, 0x40, 0xb3, 0xd7, 0x88, 0x8c, 0x4f, 0x5e, 0x00, 0x63, 0xb6, 0xce, 0xc2, 0x48, 0xc4, - 0xc1, 0x58, 0x35, 0x66, 0xcc, 0xc0, 0xc0, 0x05, 0xba, 0x96, 0x49, 0x49, 0x1a, 0xd2, 0xc1, 0x80, 0x95, 0xb2, 0x85, - 0x03, 0x5e, 0x34, 0xc7, 0xed, 0x78, 0xd3, 0xa2, 0xf1, 0xc0, 0x76, 0xb1, 0xfd, 0xfd, 0xf7, 0xc5, 0xf6, 0x3a, 0xdc, - 0x92, 0x5e, 0x21, 0x67, 0x09, 0xfd, 0xfc, 0x51, 0xf6, 0x59, 0xc3, 0xc9, 0xa9, 0xd0, 0x0c, 0x2d, 0x45, 0x42, 0x29, - 0xde, 0xe9, 0x49, 0x81, 0xb1, 0x8c, 0x85, 0xbf, 0x07, 0xce, 0xe9, 0x42, 0x11, 0xb9, 0x03, 0xc7, 0xf1, 0x0d, 0x54, - 0x30, 0x6a, 0x38, 0x78, 0x19, 0xc3, 0xb6, 0x28, 0x66, 0x21, 0xe1, 0x14, 0xc2, 0xc5, 0x2a, 0xeb, 0xf7, 0xe5, 0x2f, - 0xea, 0xa2, 0x8b, 0x4c, 0xd6, 0x7d, 0x12, 0x8e, 0xcc, 0x58, 0x4e, 0xbd, 0x90, 0x3c, 0xef, 0x79, 0x32, 0x4d, 0x9e, - 0xe5, 0x41, 0x04, 0x90, 0xcf, 0xe1, 0x7d, 0x98, 0x66, 0x60, 0x95, 0x26, 0xe5, 0x47, 0x28, 0x7d, 0xf1, 0x79, 0xe5, - 0x07, 0x3a, 0x7b, 0x6e, 0x92, 0xe1, 0xcd, 0xaa, 0xf5, 0x26, 0xb5, 0xae, 0x8b, 0x07, 0xfc, 0x8b, 0x33, 0xd8, 0x38, - 0xd7, 0x99, 0xe0, 0xc0, 0x8b, 0xa4, 0xd6, 0x6b, 0xc6, 0x5f, 0x64, 0xb8, 0x2e, 0x55, 0x1b, 0x7d, 0x14, 0xa2, 0x73, - 0xc8, 0x54, 0x80, 0x42, 0x91, 0xf6, 0x0f, 0x4a, 0xad, 0x4c, 0x2a, 0x6d, 0x24, 0x80, 0xee, 0x61, 0xd2, 0x60, 0x8b, - 0xa1, 0x8c, 0xa5, 0x49, 0x94, 0x3b, 0x0d, 0xe2, 0xca, 0xfe, 0x5c, 0x49, 0x1c, 0x5a, 0x16, 0xc9, 0xbf, 0x77, 0x3d, - 0x7d, 0x85, 0xd4, 0x9d, 0x2c, 0x90, 0x19, 0xe3, 0x65, 0x1e, 0x7f, 0x02, 0xc2, 0x6c, 0xd0, 0x46, 0x45, 0x21, 0x84, - 0x6c, 0x10, 0x83, 0xc6, 0xcb, 0x3c, 0xfe, 0x5e, 0xd1, 0x78, 0xc8, 0x47, 0x91, 0xaf, 0xfe, 0x2a, 0xf5, 0x5f, 0xa1, - 0xcf, 0x4c, 0xf0, 0x08, 0xd5, 0x44, 0xff, 0xee, 0xf9, 0xec, 0x1e, 0xd4, 0x86, 0x51, 0x98, 0x99, 0xf2, 0x2b, 0xdf, - 0x14, 0x67, 0xaf, 0xbf, 0xa2, 0xab, 0x6c, 0xeb, 0x7e, 0xf4, 0xf1, 0x88, 0xc0, 0xda, 0x18, 0x5d, 0x71, 0x63, 0x00, - 0x39, 0x4c, 0xde, 0xaf, 0x28, 0x2d, 0x87, 0x34, 0x08, 0x1d, 0x34, 0x04, 0xbd, 0x92, 0xe8, 0x03, 0x89, 0x45, 0x8c, - 0xe1, 0x85, 0x78, 0x46, 0x6a, 0x32, 0xd1, 0x10, 0xaf, 0x88, 0xfd, 0x10, 0x2d, 0x39, 0x35, 0xd1, 0x8d, 0x30, 0xc5, - 0x40, 0x62, 0x67, 0x90, 0x9c, 0x24, 0xb5, 0xf2, 0x8b, 0x67, 0x92, 0xb0, 0xc4, 0xce, 0x43, 0x0c, 0x26, 0xb5, 0x74, - 0xa7, 0x37, 0x55, 0x7a, 0x77, 0xa4, 0xe5, 0xa0, 0x7d, 0x00, 0x76, 0x29, 0xe9, 0xfd, 0x93, 0x42, 0x11, 0x1f, 0xc2, - 0x38, 0x86, 0xf0, 0x2d, 0xa2, 0xba, 0x02, 0xe7, 0x5a, 0x81, 0xc6, 0x6a, 0xe0, 0xa1, 0x99, 0x55, 0xf3, 0x21, 0xa7, - 0x9f, 0x4a, 0xcb, 0x1f, 0x23, 0x1a, 0x1b, 0xad, 0x9b, 0xc3, 0x61, 0x4f, 0xab, 0x5e, 0x3a, 0x07, 0x5d, 0x36, 0x93, - 0x98, 0xb8, 0x81, 0x74, 0xfd, 0xe8, 0x37, 0x13, 0xf6, 0x22, 0x2a, 0xe4, 0x52, 0x08, 0x0a, 0x5a, 0x1d, 0x08, 0x1c, - 0x0a, 0x6f, 0x51, 0xe6, 0x8b, 0x98, 0x36, 0x10, 0x06, 0x9f, 0x1f, 0xc8, 0xcf, 0x37, 0x05, 0xa9, 0xd8, 0xb1, 0xae, - 0xfd, 0xfe, 0xa6, 0xf4, 0x00, 0x4f, 0xce, 0x24, 0x79, 0xda, 0x0c, 0x61, 0x45, 0x00, 0x8d, 0x59, 0x4d, 0x16, 0x27, - 0x5c, 0x99, 0xc3, 0x8f, 0x95, 0x57, 0xb2, 0x94, 0xa9, 0xf3, 0x54, 0x2f, 0x80, 0xa8, 0xe3, 0x0d, 0x5a, 0x91, 0xfa, - 0x15, 0x3a, 0x7b, 0xcd, 0x4a, 0xc8, 0x78, 0x78, 0xce, 0x79, 0x3a, 0x7a, 0x60, 0x09, 0x8f, 0xf0, 0xaf, 0x64, 0xa2, - 0x0f, 0xbf, 0x07, 0x0e, 0x37, 0xe3, 0x84, 0x47, 0x6e, 0xb3, 0xf7, 0x55, 0xb8, 0x82, 0x9b, 0x69, 0x01, 0x48, 0x6e, - 0x41, 0xd2, 0x04, 0x94, 0x90, 0xc8, 0x84, 0xcc, 0x9a, 0x92, 0x9f, 0x5b, 0xda, 0x06, 0x6b, 0x98, 0x74, 0x1e, 0xf0, - 0xa2, 0xd5, 0x47, 0xab, 0x89, 0x76, 0x99, 0xe5, 0xf3, 0x21, 0xce, 0x50, 0xcd, 0x71, 0x77, 0x06, 0x3f, 0x07, 0xbc, - 0x62, 0x55, 0x93, 0x8e, 0x76, 0x03, 0x2e, 0x3c, 0xb9, 0xce, 0xd3, 0xd1, 0x16, 0x7f, 0xc9, 0xfd, 0x01, 0xa0, 0x83, - 0xa9, 0x4b, 0xe0, 0x4f, 0xd5, 0x56, 0x53, 0xa9, 0x5f, 0x5a, 0xfb, 0x75, 0xdd, 0x59, 0xad, 0xdc, 0xb3, 0x2e, 0x43, - 0x7b, 0x64, 0xc8, 0x19, 0x33, 0xe0, 0xcf, 0x19, 0x4b, 0xfe, 0x9c, 0xb1, 0xe2, 0xcf, 0x19, 0x37, 0x46, 0x06, 0x50, - 0x82, 0x7b, 0xc9, 0x5f, 0xec, 0x11, 0x33, 0xc4, 0x6a, 0x50, 0x09, 0xac, 0x2c, 0xe5, 0xdc, 0x47, 0x4e, 0x31, 0xe5, - 0x94, 0xe1, 0xa5, 0xd3, 0x99, 0x3b, 0x90, 0xf3, 0x60, 0xe6, 0x0e, 0x93, 0xb3, 0x3e, 0xc5, 0xb1, 0x34, 0x26, 0x45, - 0x05, 0xe9, 0x9c, 0x0e, 0x37, 0xaf, 0x8e, 0xf3, 0x84, 0x65, 0x7c, 0xdc, 0x3e, 0x53, 0x20, 0xc4, 0x16, 0xcf, 0x90, - 0x48, 0xa9, 0x9a, 0xe5, 0x36, 0x7f, 0x38, 0xd4, 0xa3, 0x07, 0xbd, 0xd3, 0xc3, 0xaf, 0x84, 0xfd, 0x92, 0x79, 0xf6, - 0x09, 0x02, 0x98, 0x24, 0xf2, 0x4c, 0xc2, 0xd1, 0x8f, 0xe5, 0xe8, 0x6f, 0x1a, 0xfe, 0x2e, 0x43, 0x75, 0x77, 0x08, - 0x4c, 0x6c, 0xd9, 0x81, 0x43, 0x70, 0xba, 0xaa, 0x44, 0x02, 0x0e, 0x36, 0x1b, 0x16, 0xe9, 0x3d, 0x1e, 0xe2, 0x7c, - 0x50, 0xf8, 0x08, 0x0d, 0x33, 0x7a, 0xbf, 0xbf, 0x11, 0x5e, 0x25, 0x5b, 0x79, 0x38, 0x24, 0xd6, 0x5d, 0xd8, 0xd1, - 0xc7, 0xd1, 0x1e, 0x25, 0xd4, 0x7e, 0x54, 0xeb, 0x4d, 0xa5, 0x1e, 0xe4, 0x66, 0x17, 0x12, 0x83, 0x8a, 0xa5, 0xfa, - 0xf4, 0x4a, 0xf5, 0xa1, 0x66, 0x9d, 0xdf, 0xd5, 0x71, 0x9f, 0x8a, 0xd1, 0x5a, 0x4e, 0x08, 0x70, 0x1d, 0x24, 0x1a, - 0x1d, 0x00, 0xe3, 0x6c, 0xb3, 0xe5, 0xa5, 0xb6, 0x4e, 0x94, 0x8e, 0xe3, 0x5c, 0x1f, 0xc7, 0x87, 0x83, 0x14, 0x33, - 0x2e, 0x8f, 0xc4, 0x8c, 0xcb, 0x06, 0xe0, 0xcd, 0x3a, 0x0f, 0xea, 0xc3, 0xe1, 0x92, 0x2e, 0x45, 0xa6, 0xb3, 0x8d, - 0xf2, 0xb3, 0x1e, 0x3d, 0x3c, 0x4b, 0xd0, 0xdc, 0x5b, 0x61, 0xef, 0x45, 0xb2, 0x3d, 0x93, 0x75, 0xea, 0x65, 0xe4, - 0xd3, 0x0b, 0xf7, 0xec, 0x92, 0xab, 0x1f, 0x56, 0x5f, 0x4f, 0x7f, 0x15, 0x5e, 0xc4, 0x2a, 0xda, 0xad, 0x4b, 0x26, - 0xec, 0x2d, 0xa5, 0x92, 0x56, 0x79, 0xf9, 0x74, 0xe3, 0x07, 0x98, 0x99, 0xf6, 0xf4, 0x41, 0x36, 0xa2, 0xfa, 0xb3, - 0x12, 0xb5, 0x32, 0x4c, 0x16, 0xce, 0x4b, 0xa6, 0x9e, 0x0c, 0x78, 0xcc, 0x4a, 0x1e, 0xc9, 0x4e, 0x6f, 0x0c, 0x82, - 0x00, 0xd6, 0x39, 0x69, 0xd5, 0x19, 0x47, 0xa3, 0x55, 0xe5, 0xe2, 0x74, 0x95, 0x0b, 0x0c, 0xb7, 0x5b, 0xb3, 0x8d, - 0xaa, 0xb3, 0xdc, 0xd4, 0x2a, 0xe5, 0x3b, 0x80, 0x8f, 0x65, 0x95, 0x0b, 0x3a, 0xa6, 0x4c, 0x9d, 0x37, 0x10, 0x8c, - 0xad, 0x6a, 0x5c, 0x38, 0x35, 0x2e, 0x78, 0x44, 0xed, 0x6e, 0x9a, 0x7a, 0xb4, 0x05, 0x96, 0xd2, 0xd1, 0x8e, 0x97, - 0xa8, 0x52, 0xf8, 0xbb, 0xe0, 0xfb, 0x30, 0x8e, 0xbf, 0x2f, 0xb6, 0xea, 0x40, 0xbc, 0x2d, 0xb6, 0x48, 0xfb, 0x22, - 0xff, 0x42, 0x1c, 0xf0, 0x5a, 0xd7, 0x94, 0xd7, 0xd6, 0x9c, 0x06, 0xb6, 0x86, 0x91, 0x92, 0xc2, 0xb9, 0xf9, 0xf3, - 0x70, 0xa0, 0x95, 0x5d, 0xab, 0xbb, 0x42, 0xad, 0xc7, 0x1c, 0x36, 0xec, 0x45, 0x16, 0xee, 0x44, 0x09, 0x8e, 0x5c, - 0xf2, 0xaf, 0xc3, 0x41, 0xab, 0x2c, 0xd5, 0x91, 0x3e, 0xdb, 0x7f, 0x09, 0xc6, 0x0c, 0x5d, 0x9a, 0x80, 0x65, 0x63, - 0x24, 0xff, 0x6a, 0x9a, 0x79, 0xc3, 0x64, 0xcd, 0x14, 0x8e, 0x43, 0xc3, 0x08, 0x69, 0x40, 0xb7, 0x41, 0x6d, 0x78, - 0x32, 0xdf, 0x54, 0xe5, 0x57, 0x77, 0xa4, 0xda, 0x0f, 0x86, 0x97, 0x13, 0x71, 0x4e, 0x97, 0x24, 0xf5, 0x54, 0x42, - 0x49, 0x08, 0x76, 0xe9, 0x03, 0x39, 0xb1, 0x02, 0xb2, 0x96, 0xb1, 0xfc, 0x56, 0x0f, 0x08, 0xfd, 0xa7, 0xdd, 0x7a, - 0xa1, 0xff, 0x34, 0xcd, 0x16, 0xea, 0xfa, 0xc3, 0xe4, 0xbe, 0xa3, 0xd7, 0x1f, 0x1c, 0xde, 0xa9, 0xab, 0x8a, 0xab, - 0x78, 0x58, 0x1b, 0x26, 0xb9, 0x51, 0x16, 0xee, 0x8a, 0x4d, 0xad, 0x96, 0xa7, 0xe3, 0x30, 0x02, 0x33, 0x82, 0x02, - 0x64, 0x5d, 0xb7, 0x11, 0x31, 0xac, 0xe4, 0x32, 0x21, 0x9f, 0x10, 0x90, 0x45, 0xa9, 0x71, 0x3e, 0x6e, 0x81, 0x4a, - 0x04, 0x83, 0xd3, 0xd0, 0x5a, 0x75, 0x93, 0x1f, 0x55, 0x36, 0x76, 0x07, 0xe4, 0x90, 0x64, 0xb2, 0xb8, 0x1b, 0xdd, - 0x8a, 0x65, 0x51, 0x8a, 0x9f, 0xb1, 0x1e, 0xae, 0xd9, 0xc2, 0x7d, 0x06, 0x84, 0xf6, 0x13, 0xa5, 0xbd, 0x89, 0x34, - 0x41, 0xf7, 0x1d, 0x5b, 0x01, 0xc8, 0x00, 0x8a, 0xba, 0xda, 0xad, 0xcf, 0xf9, 0x39, 0x92, 0x66, 0x38, 0x8c, 0x6e, - 0x9f, 0xde, 0x05, 0x77, 0x83, 0x4b, 0xd4, 0x4a, 0x5f, 0xb2, 0xb8, 0x85, 0x41, 0xb5, 0x37, 0x4b, 0x38, 0xa8, 0x99, - 0xb5, 0x36, 0x02, 0xc1, 0x64, 0x0f, 0x05, 0x15, 0x73, 0x05, 0xfb, 0xa0, 0x60, 0x2d, 0x79, 0x1d, 0x1c, 0x6e, 0xed, - 0xcb, 0x4a, 0x71, 0xf1, 0xfc, 0x22, 0x69, 0x5d, 0x58, 0xca, 0x8b, 0xe7, 0x0d, 0x18, 0x5c, 0x8e, 0xb0, 0xa9, 0x2a, - 0x7f, 0xb2, 0x01, 0xd0, 0xad, 0x90, 0x22, 0x5e, 0x94, 0xc2, 0xb6, 0x95, 0xcf, 0x9c, 0xb0, 0xc1, 0x86, 0x3d, 0xc0, - 0xbd, 0x32, 0x28, 0x19, 0x5c, 0x88, 0x71, 0xbb, 0xd9, 0x05, 0xb8, 0x82, 0xa1, 0x30, 0xb6, 0xe6, 0x6f, 0x32, 0x2f, - 0x52, 0x02, 0x6e, 0x86, 0x28, 0x5f, 0x1b, 0x38, 0x99, 0xf4, 0xe4, 0x5a, 0xb2, 0x18, 0xb0, 0xa0, 0xc1, 0x77, 0xd4, - 0xfa, 0x3b, 0x93, 0x7f, 0xe3, 0xe9, 0xa1, 0x1f, 0x7c, 0xce, 0xbc, 0xa5, 0xcf, 0xde, 0x54, 0x32, 0x5a, 0x93, 0x44, - 0x79, 0xf5, 0x70, 0x09, 0x72, 0xc3, 0x72, 0xf4, 0xc0, 0x96, 0x20, 0x4e, 0x2c, 0x47, 0x09, 0x65, 0x74, 0x85, 0x7b, - 0x95, 0xd9, 0x32, 0x11, 0x48, 0x71, 0x60, 0x29, 0xe5, 0xde, 0x62, 0x1d, 0x2c, 0x71, 0x7f, 0x22, 0xb9, 0x80, 0x92, - 0x07, 0x50, 0xae, 0x14, 0x10, 0xf0, 0xe9, 0x00, 0xca, 0x97, 0xf2, 0x22, 0xfc, 0x89, 0x13, 0x35, 0x58, 0x8e, 0x1e, - 0x1a, 0xf6, 0xa3, 0x17, 0x5a, 0xf6, 0x87, 0x3b, 0xad, 0x69, 0x58, 0xf1, 0x3b, 0x98, 0x16, 0x13, 0xb7, 0x2f, 0x57, - 0x76, 0x55, 0x7c, 0xb6, 0x52, 0x67, 0x37, 0x35, 0x24, 0x61, 0x5f, 0x91, 0x55, 0x80, 0x83, 0x55, 0x11, 0xf7, 0x2c, - 0xcb, 0x7d, 0x18, 0xfd, 0xb9, 0x49, 0x4b, 0x61, 0xa1, 0x4a, 0xfa, 0xfb, 0xa6, 0x14, 0x48, 0x65, 0xa2, 0x13, 0x2d, - 0x04, 0x57, 0x60, 0x10, 0xb8, 0x17, 0x79, 0x0d, 0x80, 0x31, 0xe0, 0x52, 0xa0, 0x2c, 0xdb, 0x12, 0x42, 0xaa, 0xfb, - 0x19, 0xa8, 0xed, 0xc4, 0x7d, 0x1a, 0x91, 0xb5, 0x10, 0x7d, 0x15, 0x8c, 0x99, 0xf3, 0x52, 0xba, 0xc5, 0xa6, 0xab, - 0xcd, 0xea, 0x06, 0x9d, 0x4b, 0x5b, 0x6e, 0x7e, 0xc2, 0x16, 0x6b, 0x05, 0xca, 0x26, 0x24, 0x6d, 0xe7, 0x3c, 0x47, - 0xd9, 0x84, 0x96, 0xf6, 0x9e, 0x7a, 0x54, 0xa8, 0x4e, 0xb6, 0x5e, 0xaa, 0xa6, 0x16, 0x61, 0xb5, 0xb8, 0xa8, 0xfc, - 0x00, 0x74, 0x53, 0x69, 0xf5, 0xb2, 0xae, 0xd1, 0x14, 0x6a, 0xb5, 0x70, 0xdc, 0x68, 0x67, 0xd3, 0x65, 0x7a, 0x87, - 0x38, 0xab, 0xd2, 0x0e, 0xfd, 0x7d, 0xa6, 0x5d, 0x2f, 0x3b, 0xfa, 0xcd, 0xb8, 0xba, 0xc0, 0x85, 0xd8, 0x80, 0xcf, - 0xb9, 0xbf, 0xbc, 0xde, 0xf3, 0xb8, 0xe7, 0x1f, 0x0e, 0xc8, 0x9e, 0xd4, 0xfe, 0x50, 0x7d, 0xec, 0x0a, 0x86, 0x2c, - 0x8c, 0x52, 0x7f, 0x91, 0xf2, 0xde, 0x13, 0x1c, 0xf7, 0xcf, 0x55, 0x8f, 0xfd, 0x98, 0xf1, 0x7d, 0x5d, 0x6c, 0xa2, - 0x84, 0xa2, 0x1a, 0x7a, 0xab, 0x62, 0x53, 0x89, 0xb8, 0x78, 0xc8, 0x7b, 0x0c, 0x93, 0x61, 0x2c, 0x64, 0x2a, 0xfc, - 0x29, 0x53, 0xc1, 0x23, 0x84, 0x12, 0x37, 0xeb, 0x1e, 0x69, 0x37, 0x21, 0x4e, 0xa9, 0x16, 0xa5, 0x4c, 0xc6, 0xbf, - 0xf5, 0x13, 0x28, 0xcf, 0x29, 0x5a, 0xa6, 0x1f, 0x15, 0x2e, 0xd3, 0x37, 0xeb, 0xe3, 0xd2, 0x33, 0x11, 0xea, 0xcc, - 0xc5, 0xa6, 0xd6, 0xe9, 0x18, 0x3b, 0xa5, 0x53, 0x1b, 0xf6, 0xa5, 0x52, 0x5c, 0x56, 0x14, 0xfe, 0x8d, 0x44, 0x56, - 0x3d, 0x23, 0x8e, 0xff, 0x2b, 0x6b, 0x9f, 0x61, 0x15, 0xf8, 0x65, 0x20, 0xef, 0x17, 0x00, 0x1f, 0xd7, 0x75, 0x99, - 0xde, 0x6e, 0x80, 0x36, 0x84, 0x86, 0xbf, 0xe7, 0x23, 0x03, 0xa6, 0xfb, 0x08, 0x67, 0x48, 0x0f, 0x75, 0xce, 0xe9, - 0xac, 0x4c, 0xe7, 0x5c, 0x85, 0xb5, 0x04, 0x7b, 0x39, 0x69, 0x72, 0xb9, 0x2e, 0x41, 0xcd, 0x04, 0x6e, 0x1f, 0xda, - 0x23, 0x42, 0xa8, 0x4d, 0x59, 0x4d, 0x2f, 0xa1, 0xe6, 0x9d, 0x9c, 0x76, 0x34, 0x29, 0xc1, 0x55, 0x43, 0x67, 0xe5, - 0xfa, 0xaf, 0xc3, 0xa1, 0x77, 0x9b, 0x15, 0xd1, 0x1f, 0x3d, 0xf4, 0x77, 0xdc, 0xde, 0xa4, 0x5f, 0x20, 0x5a, 0xc6, - 0xfa, 0x1b, 0x32, 0xa0, 0xe3, 0xc9, 0xf0, 0xb6, 0xd8, 0xf6, 0xd8, 0x17, 0xd4, 0x60, 0xe9, 0xeb, 0xc7, 0x1f, 0x20, - 0xa1, 0xea, 0xda, 0x17, 0x16, 0x4f, 0x98, 0xa7, 0x44, 0xdb, 0xc2, 0x87, 0xb0, 0xd0, 0x2f, 0x10, 0x19, 0x09, 0xe1, - 0xa6, 0xb2, 0x7b, 0x94, 0xb4, 0x0b, 0x7d, 0xe9, 0x6b, 0xd9, 0x57, 0xbe, 0x73, 0x01, 0xb0, 0xb2, 0xcf, 0x6d, 0xb8, - 0x27, 0xfd, 0x29, 0xd5, 0x87, 0xed, 0x6f, 0xc9, 0x02, 0x0a, 0x2d, 0xac, 0xa7, 0x72, 0x76, 0xae, 0x4b, 0x9e, 0x66, - 0xd3, 0xfd, 0x1a, 0xf6, 0xa8, 0x7b, 0xf4, 0x9a, 0x0a, 0xce, 0x2f, 0xcd, 0xe8, 0xfd, 0xd3, 0x50, 0xa8, 0x8e, 0x3a, - 0x77, 0x90, 0x75, 0x69, 0x5d, 0x72, 0x7e, 0xb3, 0x72, 0x47, 0x61, 0x7e, 0x1f, 0x82, 0x67, 0x58, 0xf7, 0xee, 0xe2, - 0xbc, 0xf7, 0x67, 0x6b, 0x8e, 0xfc, 0x98, 0xcd, 0x52, 0xc4, 0x22, 0x99, 0x83, 0xd5, 0x0f, 0xfd, 0x3c, 0xf6, 0xdb, - 0x20, 0x87, 0xe3, 0xa6, 0x01, 0x1d, 0x36, 0x64, 0xd6, 0xbe, 0x44, 0xe0, 0x54, 0x23, 0x48, 0x53, 0x13, 0xd4, 0x2c, - 0x0f, 0x91, 0xd8, 0x2e, 0x65, 0xdb, 0x20, 0xd7, 0x5d, 0x30, 0xcd, 0x91, 0xf6, 0x0c, 0xde, 0x37, 0x69, 0x92, 0x0a, - 0xcd, 0x22, 0x6d, 0x95, 0x8c, 0x7f, 0x47, 0xda, 0x4c, 0xc9, 0x1e, 0x5b, 0x03, 0xef, 0x25, 0x28, 0x27, 0xc3, 0x14, - 0xc3, 0x77, 0x7c, 0xbd, 0xf3, 0x98, 0x7b, 0xce, 0x31, 0xdb, 0xa4, 0xec, 0x08, 0x26, 0xc9, 0xc6, 0x37, 0x14, 0x6f, - 0xf8, 0xfe, 0xb6, 0x12, 0x25, 0x80, 0x5e, 0x16, 0xfc, 0x85, 0xb4, 0xb9, 0x42, 0xb7, 0xbb, 0x77, 0x94, 0xc2, 0x2f, - 0x79, 0x79, 0x38, 0x6c, 0x53, 0x2f, 0x84, 0xce, 0x17, 0xf1, 0x3b, 0x30, 0x87, 0x31, 0xc4, 0x66, 0x04, 0x08, 0x73, - 0x7c, 0x40, 0x1d, 0xac, 0x1f, 0x01, 0x68, 0x9c, 0x40, 0x01, 0x46, 0x5f, 0x6d, 0x0b, 0xfa, 0x96, 0x17, 0x17, 0x11, - 0xa2, 0x46, 0x01, 0x26, 0x4a, 0x9a, 0xc5, 0x30, 0x1c, 0xe8, 0xfc, 0xbe, 0xb9, 0xad, 0x4b, 0x81, 0x43, 0xef, 0x58, - 0x86, 0xff, 0xfe, 0x3f, 0xd6, 0x96, 0x56, 0x95, 0xed, 0xd6, 0x38, 0xcd, 0xfc, 0x6f, 0xb7, 0x85, 0xbe, 0xff, 0x4a, - 0x28, 0x9e, 0x77, 0xbc, 0x6e, 0xbf, 0x83, 0xe8, 0x7d, 0xdd, 0xca, 0xbb, 0x52, 0xbb, 0x61, 0xa6, 0xfc, 0x21, 0xcd, - 0xe3, 0xe2, 0x61, 0x14, 0xb7, 0x8e, 0xbc, 0x49, 0x7a, 0xce, 0xf9, 0xbb, 0xaa, 0xdf, 0xf7, 0xde, 0x01, 0x19, 0xef, - 0x2b, 0x61, 0x1c, 0x31, 0x89, 0x83, 0x6f, 0x2f, 0x46, 0xd1, 0xa6, 0x84, 0x0d, 0xb9, 0x7d, 0x5a, 0x82, 0x66, 0xa6, - 0xdf, 0x47, 0x89, 0xd2, 0x9a, 0xef, 0x7f, 0x93, 0xf3, 0xfd, 0x95, 0x90, 0x37, 0x2b, 0xf9, 0xe1, 0xa3, 0x15, 0x06, - 0xbe, 0xc7, 0xe9, 0x17, 0xd1, 0x63, 0x77, 0xa5, 0x0f, 0xdf, 0x95, 0x96, 0x3e, 0xab, 0xa8, 0x7f, 0xa0, 0xa2, 0xe6, - 0x95, 0x18, 0x11, 0xf1, 0x20, 0x68, 0x67, 0xdb, 0xa5, 0x76, 0x2d, 0x41, 0xbb, 0x60, 0x53, 0xd8, 0xbf, 0x1f, 0x1d, - 0xf2, 0x7e, 0xff, 0x63, 0xee, 0xb5, 0x78, 0xdd, 0x75, 0x68, 0xca, 0x4f, 0x85, 0x87, 0x10, 0xc0, 0x5a, 0x06, 0xca, - 0x38, 0xc2, 0xa4, 0x8b, 0xbc, 0x46, 0xd9, 0x74, 0x22, 0xf0, 0x31, 0xcb, 0xae, 0x9c, 0x64, 0x1a, 0x60, 0x46, 0x35, - 0x85, 0x99, 0x00, 0x23, 0xf5, 0x11, 0xeb, 0xa6, 0xa7, 0x55, 0x68, 0xf9, 0x1a, 0x82, 0x75, 0x91, 0x65, 0x1c, 0xc5, - 0x4c, 0x00, 0xb0, 0xf9, 0x08, 0xf2, 0x15, 0x5d, 0x1d, 0x92, 0x56, 0xaa, 0xbc, 0x5f, 0x67, 0x44, 0x46, 0x93, 0x10, - 0xcd, 0x6f, 0xe1, 0x81, 0x7d, 0xdb, 0xcc, 0xa8, 0x52, 0xcf, 0xa8, 0xca, 0x67, 0x38, 0x2c, 0x85, 0x63, 0xc4, 0xff, - 0x7b, 0xaa, 0x7a, 0x44, 0xa0, 0x57, 0x65, 0x5a, 0x45, 0x45, 0x9e, 0x8b, 0x08, 0x11, 0xaa, 0xa5, 0x73, 0x38, 0xf4, - 0x63, 0xbf, 0x8f, 0x03, 0x61, 0x5e, 0xfc, 0xe9, 0xb1, 0xae, 0xfc, 0xa9, 0xc0, 0xb5, 0x92, 0x02, 0xa7, 0xa2, 0x46, - 0x88, 0x10, 0xde, 0x9f, 0xc0, 0xb3, 0x9a, 0xfa, 0x7e, 0x63, 0x99, 0xe8, 0xfe, 0x99, 0x01, 0xe5, 0x0f, 0xc8, 0xd7, - 0x95, 0x14, 0x67, 0xea, 0xe4, 0x31, 0x71, 0xc6, 0x01, 0x88, 0xf9, 0xba, 0x44, 0xa3, 0xb1, 0xff, 0x01, 0x09, 0x86, - 0xea, 0x07, 0x3b, 0xdd, 0xd4, 0xfb, 0x57, 0x26, 0x71, 0x14, 0x7d, 0xda, 0x26, 0x8f, 0x25, 0x4b, 0xa3, 0x85, 0xa3, - 0xf7, 0x88, 0x61, 0x1c, 0x4e, 0xe7, 0x63, 0x92, 0x6d, 0x4c, 0x56, 0x01, 0xa4, 0x93, 0x99, 0x3a, 0xa6, 0xd4, 0xd1, - 0x38, 0xd7, 0x0b, 0xaa, 0xd0, 0x63, 0x5d, 0xf2, 0x1c, 0xac, 0x27, 0x3f, 0x78, 0xa5, 0x3f, 0x15, 0x72, 0x0e, 0x1b, - 0x89, 0xa0, 0xf0, 0x03, 0x5c, 0x0d, 0x56, 0x0a, 0x18, 0x4c, 0x7d, 0x0b, 0x5f, 0x13, 0xcf, 0x51, 0xf0, 0x28, 0xec, - 0x62, 0x6c, 0xad, 0x7c, 0xe7, 0x93, 0x82, 0x72, 0xcf, 0x8a, 0x39, 0xaf, 0x80, 0x73, 0x19, 0x14, 0xc2, 0x74, 0x3c, - 0xcb, 0xff, 0x99, 0xe4, 0xf5, 0xc4, 0x86, 0x00, 0x19, 0xfc, 0x29, 0x71, 0x5a, 0xba, 0x43, 0x77, 0x1e, 0x7a, 0x16, - 0x71, 0xd8, 0xe8, 0xc9, 0xba, 0x2c, 0xb6, 0x29, 0xea, 0x25, 0xcc, 0x0f, 0xe4, 0xe7, 0x2d, 0xf9, 0x3e, 0x44, 0xf1, - 0x36, 0xf8, 0x35, 0x63, 0xb1, 0xc0, 0xbf, 0xfe, 0x9e, 0x31, 0x9a, 0x68, 0xc1, 0xbf, 0xb3, 0x06, 0x89, 0x8a, 0x7f, - 0xca, 0x26, 0x00, 0xeb, 0xc8, 0xd5, 0x87, 0x4f, 0x89, 0xf1, 0xd6, 0x6c, 0x78, 0xe4, 0x9b, 0x15, 0xe8, 0xd4, 0xe7, - 0xee, 0xca, 0xf6, 0x54, 0x35, 0xfe, 0x9e, 0xea, 0x6a, 0xa4, 0xaa, 0x1a, 0x7f, 0x4f, 0xa9, 0x1a, 0xbf, 0x65, 0x14, - 0xbf, 0x53, 0xf9, 0x0c, 0x99, 0x93, 0x4d, 0x4c, 0xd2, 0xe9, 0x7b, 0xc3, 0x89, 0x5d, 0xf6, 0xab, 0xb7, 0x89, 0xcc, - 0x44, 0x0a, 0xb9, 0x37, 0x00, 0x6d, 0xbf, 0xcb, 0x0d, 0xa7, 0xc4, 0xf9, 0xb9, 0x87, 0x2b, 0x36, 0xad, 0x5e, 0xd1, - 0x82, 0x05, 0x36, 0x2f, 0xb3, 0x3c, 0x45, 0x02, 0xdb, 0xa6, 0xcc, 0xfa, 0x73, 0xee, 0x01, 0x04, 0x33, 0xa9, 0x09, - 0x00, 0x69, 0x21, 0x2a, 0x85, 0xc8, 0x5f, 0xe1, 0xac, 0x3e, 0xe7, 0xbd, 0x4d, 0x1e, 0x13, 0x69, 0x75, 0xaf, 0xdf, - 0x4f, 0xcf, 0xd2, 0x9c, 0x82, 0x1a, 0x8e, 0xb3, 0x4e, 0xbf, 0xcf, 0x82, 0x3a, 0x91, 0xab, 0xf4, 0x1f, 0x6e, 0x90, - 0x97, 0xf1, 0x7d, 0xdd, 0xf6, 0xfc, 0x89, 0xfa, 0x7b, 0x67, 0xfd, 0x6d, 0x81, 0xe0, 0x4e, 0x8e, 0xfd, 0x64, 0x55, - 0xca, 0x13, 0xe3, 0xd2, 0xde, 0xf3, 0x9b, 0xba, 0x28, 0xb2, 0x3a, 0x5d, 0x7f, 0x90, 0x7a, 0x1a, 0xdd, 0x17, 0x7b, - 0x30, 0x06, 0xef, 0x00, 0xf0, 0x4c, 0x87, 0x06, 0x48, 0xdf, 0x33, 0xf2, 0x70, 0x9f, 0x5b, 0xf2, 0x93, 0xca, 0xda, - 0x24, 0x61, 0x45, 0xb1, 0x19, 0xc6, 0x08, 0x25, 0xe3, 0x34, 0xb6, 0x7e, 0xbf, 0xaf, 0xfe, 0xde, 0x61, 0x14, 0x15, - 0x15, 0x77, 0x8c, 0x46, 0x65, 0x55, 0x8f, 0xb6, 0x83, 0xc3, 0xe1, 0x3c, 0xb7, 0x71, 0xb4, 0xf5, 0x0a, 0xd8, 0x5b, - 0xa1, 0x52, 0xf6, 0x4a, 0x84, 0xe5, 0x87, 0x2b, 0xbf, 0xdf, 0x87, 0x7f, 0x65, 0xa4, 0x85, 0xe7, 0x4f, 0xf1, 0xd7, - 0xa2, 0x2e, 0x30, 0x3c, 0x83, 0xd6, 0x68, 0x05, 0xc1, 0x04, 0xff, 0xe8, 0x40, 0xbd, 0xb4, 0xd2, 0x3e, 0x82, 0x6e, - 0x05, 0x7a, 0x50, 0x0f, 0x7d, 0x9a, 0xb4, 0x2f, 0x24, 0xea, 0xf6, 0x56, 0xa7, 0xd1, 0x1f, 0x15, 0x5c, 0x4e, 0x61, - 0x72, 0xb8, 0xa1, 0x4f, 0xab, 0x70, 0xfb, 0x09, 0x9e, 0xfe, 0x0c, 0x94, 0x5b, 0x87, 0x43, 0x0e, 0x62, 0x0b, 0xb8, - 0x79, 0xac, 0xc2, 0xcf, 0x45, 0x29, 0x23, 0xea, 0xe3, 0x69, 0x01, 0xda, 0xbb, 0x00, 0x1d, 0xb0, 0x34, 0x88, 0x57, - 0x48, 0x9e, 0xb3, 0x11, 0xc0, 0xb2, 0x03, 0xcb, 0x59, 0xc6, 0x29, 0xcc, 0xb3, 0x7c, 0xa1, 0x56, 0xda, 0x59, 0x99, - 0x78, 0x35, 0xcb, 0xc0, 0x59, 0xe0, 0xa2, 0xf2, 0x59, 0xa6, 0x55, 0x4f, 0x55, 0x82, 0x3e, 0xaf, 0xe4, 0x04, 0x57, - 0x82, 0x93, 0x0d, 0xc8, 0x2f, 0x40, 0x92, 0xa6, 0x94, 0x35, 0xe5, 0x8b, 0x4b, 0xba, 0x21, 0xa3, 0xe7, 0xbc, 0xe7, - 0x45, 0xc3, 0xd0, 0xbf, 0xf0, 0x4a, 0x08, 0xdf, 0xc4, 0x6d, 0x1b, 0xa5, 0xb0, 0xbf, 0x09, 0x2c, 0x3e, 0x61, 0x3f, - 0x78, 0x4b, 0x7f, 0x3a, 0x0e, 0xc2, 0x21, 0x72, 0x43, 0xc5, 0x1c, 0xd8, 0xd3, 0x80, 0xc5, 0x26, 0xbe, 0xda, 0x4c, - 0xe2, 0xc1, 0xc0, 0xd7, 0x19, 0x8b, 0x59, 0x0c, 0x34, 0xc8, 0xf1, 0xe0, 0x72, 0xae, 0x4f, 0x08, 0xfd, 0x30, 0xa2, - 0x72, 0x54, 0xa0, 0x73, 0x10, 0x0d, 0x96, 0x80, 0xa7, 0xde, 0xca, 0x06, 0x49, 0xc6, 0x24, 0x93, 0xb8, 0xd6, 0x24, - 0xd5, 0xe1, 0x84, 0xd6, 0x81, 0x8e, 0xab, 0x0b, 0xe8, 0x7c, 0x5c, 0xf7, 0x3e, 0x5e, 0x0d, 0x17, 0x54, 0xfa, 0x85, - 0x18, 0x78, 0xf5, 0x74, 0x1c, 0x5c, 0xd2, 0xad, 0x70, 0xb1, 0x0a, 0xb7, 0x3f, 0xcb, 0x07, 0x8e, 0x3b, 0x2a, 0x69, - 0x08, 0x0c, 0xde, 0x1e, 0xba, 0x9b, 0x19, 0x1a, 0xea, 0xa4, 0x7d, 0x18, 0x87, 0x72, 0x88, 0x55, 0x2b, 0x2e, 0xa4, - 0x37, 0x82, 0x6f, 0x17, 0x8a, 0xb1, 0x6c, 0xec, 0xd2, 0x50, 0x14, 0xfe, 0x0a, 0x60, 0x87, 0xda, 0x5f, 0xa9, 0xe4, - 0x63, 0x64, 0x54, 0xd3, 0x40, 0xc7, 0x00, 0x2c, 0x59, 0x9a, 0x48, 0xaa, 0x48, 0x23, 0xf1, 0x47, 0x66, 0xac, 0xa3, - 0xa6, 0xeb, 0x0b, 0xa6, 0xaa, 0x45, 0xd2, 0xed, 0x4c, 0x62, 0x39, 0x91, 0xa4, 0xb6, 0xfb, 0x88, 0x18, 0x0c, 0x7c, - 0xb0, 0x11, 0xd3, 0x4c, 0x84, 0x23, 0x1e, 0x95, 0xc8, 0xa2, 0xcb, 0x6f, 0xa3, 0x4c, 0xda, 0xbe, 0xac, 0xc8, 0x16, - 0x04, 0xd3, 0x93, 0xe8, 0x83, 0x24, 0xe5, 0x54, 0x24, 0xd2, 0x8c, 0x10, 0xe0, 0xc7, 0x93, 0xf2, 0x4a, 0x7f, 0x0e, - 0x9a, 0x56, 0x82, 0x97, 0x0c, 0x92, 0x47, 0xe2, 0x67, 0x52, 0x30, 0x8b, 0xb1, 0x6a, 0x30, 0xc0, 0x72, 0xaa, 0x67, - 0x8e, 0x49, 0xfa, 0x6f, 0x9d, 0x4e, 0xd8, 0x2f, 0xbd, 0xdc, 0xd6, 0xf2, 0xa6, 0xb9, 0xf7, 0xd2, 0xab, 0x58, 0xaa, - 0x61, 0x19, 0xf4, 0x5f, 0x13, 0xed, 0x82, 0xad, 0x2d, 0x63, 0xc2, 0xaa, 0x1f, 0x40, 0xda, 0x23, 0x5d, 0x5e, 0x35, - 0xcc, 0x99, 0xe0, 0xd1, 0x85, 0x35, 0x0f, 0xa2, 0x0b, 0xe1, 0x23, 0x97, 0xdd, 0x24, 0xb9, 0x1a, 0x4f, 0xfc, 0x70, - 0x30, 0x50, 0x00, 0xb4, 0xb4, 0x4e, 0x8a, 0x41, 0xf8, 0x4c, 0xc8, 0x81, 0x34, 0x3a, 0xaa, 0x02, 0x2c, 0x96, 0xd9, - 0x55, 0x39, 0xc9, 0x06, 0x03, 0x1f, 0xc4, 0xc6, 0xc4, 0x6e, 0x68, 0x36, 0xf7, 0xd9, 0x89, 0x82, 0xac, 0x36, 0x87, - 0xad, 0x99, 0x6e, 0x81, 0x01, 0xc0, 0x20, 0x22, 0x58, 0xee, 0x73, 0x23, 0x1f, 0x51, 0xa7, 0xa7, 0x30, 0x02, 0x82, - 0x5f, 0x4e, 0x04, 0x22, 0x17, 0x09, 0xd4, 0x03, 0xcc, 0x04, 0x98, 0x51, 0xc5, 0xf0, 0x12, 0xd8, 0xc5, 0x73, 0xf3, - 0x8a, 0x41, 0xff, 0xa2, 0x49, 0x96, 0x68, 0x2a, 0x71, 0x34, 0x46, 0x4e, 0xa5, 0x31, 0x32, 0x20, 0x76, 0x71, 0xfc, - 0x7b, 0x4a, 0x8f, 0x82, 0x94, 0x7d, 0xae, 0x0c, 0x71, 0x38, 0x8a, 0xaf, 0x60, 0xd5, 0x38, 0x1c, 0x6a, 0xf3, 0x7a, - 0x3a, 0xab, 0xe7, 0x03, 0x11, 0xc0, 0x7f, 0x43, 0xc1, 0x7e, 0xd1, 0x54, 0xe4, 0x06, 0xa9, 0xf3, 0x70, 0x48, 0x41, - 0x3e, 0xd5, 0x4d, 0xfe, 0xbe, 0x72, 0xf7, 0xd3, 0xd9, 0xdc, 0x9a, 0xa3, 0x17, 0x35, 0xae, 0x5b, 0xab, 0x1b, 0x0a, - 0x89, 0xd6, 0x34, 0x29, 0xae, 0xaa, 0x49, 0x31, 0xe0, 0xb9, 0x2f, 0x54, 0x17, 0x5b, 0x23, 0x58, 0xf8, 0x73, 0x0b, - 0x84, 0xc9, 0xb8, 0x17, 0x1f, 0x2d, 0xe4, 0x94, 0x76, 0x6d, 0xb5, 0xdb, 0x56, 0x36, 0xa4, 0x68, 0x3e, 0xbc, 0x84, - 0x5d, 0x3a, 0x45, 0xb4, 0xed, 0x92, 0xe0, 0x0b, 0xd0, 0xb2, 0xba, 0x10, 0x79, 0x4c, 0xbf, 0x42, 0x7e, 0x29, 0x86, - 0x7f, 0x95, 0xee, 0xcd, 0xa9, 0x0d, 0x72, 0x00, 0xdb, 0xbd, 0x87, 0xdb, 0x31, 0x7a, 0x20, 0x83, 0x37, 0x42, 0xce, - 0x39, 0xbf, 0x9c, 0x5a, 0x33, 0x26, 0x1a, 0x16, 0xac, 0x1c, 0x46, 0x7e, 0x80, 0x8c, 0x97, 0x53, 0x60, 0x65, 0x3f, - 0x2a, 0xe2, 0xd2, 0x1f, 0x46, 0xfe, 0xc5, 0xf3, 0x20, 0xe3, 0x5e, 0x34, 0xec, 0xf8, 0x02, 0xec, 0xd5, 0x17, 0xcf, - 0x59, 0x34, 0xe0, 0xd5, 0x55, 0x3d, 0xcd, 0x82, 0x61, 0xc6, 0xa2, 0xab, 0x62, 0x08, 0x3e, 0xb4, 0x2f, 0xca, 0x41, - 0xe8, 0xfb, 0x66, 0xe7, 0xd0, 0xdd, 0x90, 0xc8, 0x23, 0xec, 0x47, 0x70, 0xdb, 0xd5, 0x12, 0x33, 0x98, 0x6c, 0xee, - 0x22, 0x66, 0xb0, 0xe5, 0x2f, 0x9e, 0x1b, 0x2e, 0xa1, 0xea, 0x85, 0xd4, 0x6c, 0x14, 0x68, 0x4e, 0xae, 0xd0, 0x9c, - 0xac, 0x84, 0x5a, 0xf2, 0x49, 0x85, 0x13, 0x76, 0x3e, 0xc9, 0x95, 0xdd, 0x68, 0x8c, 0x81, 0x8b, 0xf6, 0xdc, 0x16, - 0x46, 0x66, 0x3a, 0x4b, 0xd1, 0x80, 0x85, 0x67, 0xe2, 0x94, 0xc6, 0x80, 0xf6, 0xe5, 0xc0, 0xd2, 0x86, 0xfc, 0x28, - 0x67, 0x06, 0xda, 0x86, 0x94, 0x46, 0xcd, 0xc0, 0x9f, 0xa9, 0x09, 0xf3, 0x2b, 0x58, 0x89, 0x20, 0xaa, 0x0b, 0x30, - 0x49, 0x72, 0x32, 0x1a, 0x29, 0x2b, 0x91, 0x9c, 0x03, 0xde, 0x47, 0xf0, 0x64, 0x11, 0xdb, 0xda, 0x9f, 0xd2, 0xff, - 0xea, 0xf0, 0xb9, 0xf4, 0x9f, 0x09, 0x60, 0x21, 0x97, 0x06, 0x91, 0x81, 0xc2, 0x21, 0x35, 0x95, 0x88, 0x13, 0xc7, - 0x33, 0xf0, 0x0d, 0x5c, 0xa0, 0x29, 0xa0, 0x3f, 0xa8, 0x19, 0x45, 0x64, 0xe1, 0xaf, 0x9e, 0xdd, 0xd4, 0x8d, 0x9e, - 0x67, 0xce, 0x6b, 0xd0, 0xcc, 0x40, 0x48, 0x8f, 0x53, 0xf5, 0x36, 0x24, 0x3a, 0x2f, 0x2f, 0xf5, 0xcb, 0x84, 0x48, - 0x56, 0x44, 0x9e, 0xbe, 0xcf, 0xc1, 0x3c, 0xa2, 0x08, 0x1d, 0x5c, 0x99, 0x87, 0xc3, 0xb9, 0xa0, 0xf0, 0x1d, 0xe5, - 0xf9, 0x80, 0xd3, 0x2c, 0x4a, 0x40, 0x1b, 0xc8, 0x72, 0x53, 0xe6, 0x3a, 0x69, 0x99, 0xba, 0xf7, 0x60, 0x25, 0xa8, - 0xd0, 0xcd, 0x29, 0x28, 0x94, 0x91, 0xa0, 0x94, 0x56, 0x83, 0x50, 0xaa, 0xc3, 0x22, 0x88, 0x1c, 0xb2, 0x10, 0x70, - 0x33, 0x15, 0x8d, 0x96, 0x34, 0x3c, 0xc2, 0xb9, 0x81, 0x42, 0x00, 0x12, 0x7b, 0xaa, 0x28, 0xe3, 0x72, 0x08, 0xf8, - 0x28, 0xe1, 0x10, 0x67, 0x4d, 0xda, 0xf2, 0x1c, 0xc4, 0xb1, 0x5c, 0xf2, 0x75, 0x85, 0x60, 0x10, 0xa1, 0xcf, 0x90, - 0x3f, 0x59, 0xce, 0xbf, 0x5b, 0x87, 0x69, 0x47, 0xf8, 0xb0, 0xab, 0x2d, 0xb8, 0x98, 0xdd, 0xce, 0x27, 0x10, 0xdf, - 0x72, 0x3b, 0x3f, 0xc6, 0x10, 0x59, 0xf8, 0x83, 0xbb, 0xa1, 0xe4, 0x8a, 0x42, 0x97, 0xf5, 0x88, 0x14, 0xd9, 0xd3, - 0x35, 0x47, 0x10, 0x1c, 0x68, 0xd5, 0x20, 0x43, 0x23, 0xf1, 0xc5, 0x73, 0xc8, 0x1a, 0xac, 0xf9, 0xe7, 0x8a, 0x9c, - 0xd5, 0xfd, 0xc9, 0x06, 0xaa, 0x49, 0x26, 0x6b, 0x45, 0xe5, 0xfc, 0xf5, 0xaa, 0x2c, 0x4f, 0x56, 0x65, 0xb8, 0x1a, - 0x74, 0x55, 0x65, 0xc9, 0x91, 0xda, 0x00, 0xad, 0xe9, 0x0a, 0x31, 0x14, 0xb2, 0x06, 0x4b, 0xab, 0x2a, 0x6b, 0xea, - 0x13, 0x08, 0xf4, 0x01, 0x96, 0x51, 0xb3, 0x9f, 0x0e, 0xff, 0x15, 0xfc, 0x4b, 0x85, 0x2c, 0xd5, 0x69, 0x9d, 0x89, - 0x5f, 0x83, 0x25, 0xc3, 0x3f, 0x7e, 0x0b, 0xd6, 0x80, 0x25, 0x40, 0x96, 0xbb, 0x8d, 0x8d, 0xd6, 0x2b, 0xaf, 0x10, - 0x5f, 0x6a, 0x7d, 0xd1, 0x6f, 0xdd, 0x26, 0x6a, 0x05, 0x18, 0xa1, 0xd0, 0x22, 0xc0, 0x56, 0x0f, 0xdc, 0x53, 0xf0, - 0x03, 0x31, 0x9c, 0x6b, 0xd2, 0x9a, 0x3a, 0xe1, 0x75, 0x36, 0x8e, 0x44, 0x54, 0x6f, 0xe1, 0xe2, 0x5e, 0x6f, 0x2d, - 0xfe, 0x46, 0x05, 0x02, 0x20, 0x8b, 0x29, 0xd6, 0xce, 0x1b, 0xd2, 0x2b, 0xc3, 0x4e, 0x42, 0xef, 0x0d, 0x3b, 0x81, - 0xbc, 0x38, 0xec, 0x14, 0xba, 0x44, 0xdb, 0x29, 0x52, 0x13, 0x6d, 0x27, 0x2d, 0x56, 0x61, 0x09, 0xc1, 0xaf, 0xda, - 0x5b, 0x47, 0xd9, 0xbe, 0xc8, 0x12, 0xa6, 0x2d, 0x60, 0x94, 0x5b, 0xf5, 0x99, 0x53, 0xc4, 0x4a, 0xd9, 0x3b, 0x9d, - 0x54, 0xb9, 0x8b, 0x7c, 0x6a, 0x35, 0x45, 0x26, 0x7f, 0x7f, 0xdc, 0x22, 0xf9, 0xe4, 0xe7, 0x76, 0xc3, 0x64, 0xfa, - 0xc7, 0xa3, 0x2f, 0xa0, 0x2b, 0xb2, 0xd3, 0x27, 0x10, 0x90, 0xa9, 0xa0, 0x5a, 0xdd, 0x2a, 0xa6, 0x79, 0xbb, 0xca, - 0x6e, 0x2f, 0x94, 0x18, 0x4e, 0x67, 0x27, 0xe1, 0xd1, 0x66, 0xc8, 0xc0, 0x21, 0x08, 0x14, 0x42, 0x45, 0x31, 0x3c, - 0x02, 0xb5, 0x46, 0xf2, 0x01, 0x7e, 0xb4, 0x3b, 0x15, 0x44, 0x6a, 0x37, 0x15, 0x37, 0x4e, 0x6e, 0xba, 0x5e, 0x0a, - 0xd4, 0x3a, 0x25, 0x2b, 0x80, 0x12, 0xa2, 0xfe, 0x24, 0xb6, 0xf5, 0x2b, 0xb8, 0x62, 0xf3, 0x7d, 0xa3, 0xe8, 0xc9, - 0xf5, 0x29, 0xea, 0x56, 0x5c, 0x9d, 0xa6, 0xad, 0xe6, 0xd8, 0x71, 0x86, 0x1c, 0x3c, 0x2b, 0x08, 0xb6, 0xa3, 0x12, - 0xe5, 0x75, 0xbb, 0xe9, 0x98, 0xd8, 0xea, 0x9f, 0x45, 0xb5, 0xb9, 0x83, 0x8a, 0x88, 0xf8, 0x28, 0xbb, 0x79, 0xd2, - 0x7e, 0x07, 0x7b, 0xac, 0xd5, 0x20, 0xb2, 0xcf, 0xe0, 0x2a, 0xd7, 0x69, 0x91, 0xdb, 0x32, 0x38, 0xff, 0xf0, 0x6a, - 0x57, 0x61, 0x93, 0x63, 0x5d, 0x5d, 0xcd, 0x54, 0x27, 0x15, 0x1b, 0x18, 0x6b, 0x5a, 0x4b, 0x35, 0x8f, 0x21, 0xe9, - 0xae, 0x2c, 0xce, 0xaa, 0xa4, 0x9b, 0x9e, 0x1b, 0x67, 0x0a, 0x31, 0x70, 0xb6, 0x1a, 0x2d, 0x67, 0x18, 0xa2, 0xeb, - 0xc3, 0x2c, 0xf1, 0x5b, 0x3d, 0xe5, 0x3e, 0x0f, 0xb7, 0x7e, 0x57, 0x2f, 0x38, 0x99, 0xec, 0x27, 0xc7, 0xb9, 0xdb, - 0x45, 0xda, 0x4f, 0x7c, 0x1b, 0xe6, 0x5f, 0xdf, 0x20, 0xee, 0x44, 0xfd, 0xcf, 0x0a, 0x80, 0x06, 0x37, 0x79, 0x2c, - 0x51, 0xea, 0xf7, 0xaa, 0xfa, 0x41, 0xcd, 0x54, 0x4d, 0x03, 0xc1, 0x9c, 0x4a, 0x01, 0x7f, 0xb8, 0x5d, 0xb8, 0xe2, - 0x11, 0x37, 0x2c, 0x8c, 0x7f, 0x7a, 0x35, 0x3b, 0x15, 0x54, 0x06, 0x6e, 0xc6, 0x7f, 0x7a, 0x82, 0x9d, 0xc2, 0x5a, - 0x01, 0x59, 0xe1, 0x4f, 0x2f, 0x7f, 0xe4, 0xfd, 0x8a, 0xff, 0xe9, 0x55, 0x8f, 0xbc, 0x8f, 0x38, 0x2f, 0x7f, 0x22, - 0xa9, 0x13, 0xa2, 0xba, 0xfc, 0x49, 0x98, 0x62, 0xab, 0x34, 0x7f, 0x4d, 0x0a, 0x9f, 0xe0, 0x33, 0xf0, 0x1d, 0xae, - 0xc2, 0xad, 0xf9, 0x0d, 0x1e, 0x3b, 0x16, 0xdb, 0x2e, 0xf5, 0x05, 0x94, 0x23, 0xb0, 0x88, 0xdc, 0x7e, 0xbb, 0xb2, - 0x5f, 0x2d, 0x8c, 0x32, 0xc6, 0xee, 0x4b, 0x56, 0xa2, 0x74, 0xd6, 0xef, 0x17, 0x52, 0x30, 0xb2, 0x0b, 0x6b, 0xb4, - 0x47, 0xa9, 0x7a, 0xf5, 0x3a, 0xac, 0xa3, 0x24, 0xcd, 0xef, 0x64, 0xf4, 0x91, 0x0c, 0x3b, 0xd2, 0x57, 0x52, 0xa2, - 0xbd, 0x56, 0x61, 0x39, 0x9a, 0xfd, 0xba, 0xe4, 0x40, 0x79, 0xdd, 0x0a, 0xca, 0x57, 0x4d, 0x00, 0xbd, 0x52, 0xed, - 0x33, 0xd0, 0x0a, 0x0a, 0x4b, 0xe5, 0xc1, 0x4a, 0x9c, 0x8b, 0x3e, 0x2b, 0x0e, 0x07, 0x75, 0x31, 0x24, 0x14, 0xa8, - 0x12, 0x27, 0xa1, 0x11, 0xcf, 0xe1, 0x42, 0x28, 0x5e, 0xe4, 0x18, 0x5b, 0x91, 0x03, 0x07, 0x32, 0xfc, 0x80, 0xc0, - 0x7b, 0xd9, 0xbf, 0x82, 0xc1, 0x30, 0xc1, 0x8d, 0x8c, 0x3a, 0x39, 0x67, 0x7f, 0x62, 0x60, 0x06, 0xf5, 0xa4, 0x76, - 0x9f, 0xdd, 0xab, 0xc0, 0x5e, 0x38, 0x03, 0xda, 0xbb, 0x31, 0xfa, 0x59, 0x15, 0x6b, 0x27, 0xfd, 0x53, 0xb1, 0x86, - 0x64, 0x3a, 0x2c, 0x8e, 0xb6, 0x69, 0x78, 0x24, 0x4f, 0x8e, 0xe3, 0x4d, 0xff, 0x70, 0x18, 0xe3, 0xc7, 0x51, 0x7e, - 0x6d, 0x01, 0xaf, 0xe2, 0x16, 0xd2, 0x58, 0xa4, 0xe8, 0x1d, 0x88, 0x39, 0x14, 0xbd, 0x64, 0xbf, 0x65, 0xbc, 0x9c, - 0x08, 0x4a, 0x49, 0x62, 0xc3, 0x3b, 0xd2, 0xd3, 0xb4, 0x1e, 0x6d, 0x65, 0xc0, 0x7e, 0x3d, 0xda, 0xd1, 0x5f, 0xa0, - 0x78, 0xb4, 0xf0, 0x97, 0xf4, 0x77, 0x71, 0x37, 0xf7, 0x9c, 0x6f, 0x1a, 0xdf, 0x11, 0x17, 0x28, 0xd6, 0xec, 0xfe, - 0x9a, 0x96, 0xce, 0x3a, 0x10, 0x1c, 0xf0, 0x16, 0xbb, 0x68, 0xdf, 0x6f, 0x5c, 0xa7, 0xa7, 0xfd, 0xb7, 0x6e, 0x8d, - 0xf2, 0xbd, 0x7f, 0x4a, 0x94, 0x83, 0xfd, 0x6b, 0x17, 0xcd, 0xdf, 0x7e, 0xca, 0x90, 0x54, 0x68, 0x6e, 0xb0, 0x9d, - 0x6c, 0x11, 0xd6, 0xc6, 0x38, 0xa8, 0xd8, 0x5d, 0x19, 0x46, 0xc0, 0xa0, 0x8e, 0xfd, 0x8f, 0x3e, 0x9b, 0x36, 0x64, - 0x1f, 0x00, 0x2a, 0x57, 0x21, 0x60, 0x0f, 0xc0, 0x89, 0x46, 0xb8, 0x01, 0x6e, 0x35, 0x5a, 0xd2, 0x41, 0xdd, 0x16, - 0x0c, 0x44, 0x4b, 0xd8, 0xc8, 0xdb, 0xae, 0x4e, 0x5f, 0x11, 0x3e, 0xd4, 0x4e, 0x4a, 0x87, 0xf2, 0x57, 0xcf, 0xd9, - 0xff, 0xec, 0xb0, 0xa6, 0xa6, 0xdc, 0x00, 0x66, 0xce, 0x4a, 0xe4, 0x15, 0x42, 0xa7, 0xc8, 0xef, 0x55, 0x5d, 0x89, - 0xe1, 0xb2, 0x16, 0x65, 0x67, 0x76, 0xeb, 0x44, 0xef, 0x9c, 0x82, 0x5a, 0x2a, 0x1b, 0xe4, 0x24, 0xd5, 0xe6, 0x23, - 0x6b, 0x05, 0x25, 0xea, 0x1a, 0x05, 0x8e, 0x4f, 0xb9, 0x76, 0xff, 0xef, 0x9c, 0x09, 0x6a, 0xb6, 0x51, 0xdd, 0x5f, - 0xeb, 0xa7, 0xaa, 0x26, 0xb1, 0x00, 0x97, 0x93, 0x34, 0xef, 0x78, 0x84, 0xd5, 0x3f, 0x4e, 0x96, 0x22, 0xd0, 0xeb, - 0x88, 0x76, 0x25, 0x20, 0x41, 0x3b, 0x39, 0x0b, 0x15, 0x81, 0x02, 0x7d, 0xfd, 0xfb, 0x4d, 0x9a, 0xc5, 0x72, 0x35, - 0xdb, 0xc3, 0x44, 0x59, 0xac, 0x87, 0x08, 0x72, 0x66, 0xea, 0x60, 0xbf, 0xa7, 0x19, 0xcd, 0xc2, 0x2b, 0x53, 0x82, - 0x4b, 0x71, 0x15, 0x15, 0x39, 0xf8, 0x1c, 0xe2, 0x0b, 0x9f, 0x0a, 0xb9, 0x41, 0x44, 0xd3, 0xef, 0x25, 0xaa, 0x1d, - 0x29, 0x90, 0x43, 0xc9, 0x4f, 0x88, 0xbf, 0x64, 0x6d, 0x8c, 0xfb, 0xa5, 0x53, 0xed, 0x57, 0x0a, 0xc1, 0xfd, 0x67, - 0x5b, 0x6c, 0x54, 0x79, 0xa2, 0x47, 0x9f, 0x62, 0xfd, 0x4f, 0x16, 0x50, 0xaa, 0xfb, 0x36, 0x38, 0x15, 0x8f, 0xc2, - 0x4d, 0x5d, 0xdc, 0x20, 0xb4, 0x40, 0x39, 0xaa, 0x8a, 0x4d, 0x19, 0x11, 0x27, 0xec, 0xa6, 0x2e, 0x7a, 0x9a, 0x03, - 0x9d, 0x3a, 0x2c, 0x4d, 0xe4, 0x89, 0xd0, 0x6e, 0x41, 0xf7, 0x34, 0xc7, 0x4a, 0xbc, 0x94, 0xa5, 0x83, 0xac, 0x13, - 0x69, 0x42, 0xe5, 0xae, 0xae, 0x3a, 0x2a, 0x95, 0xba, 0xe1, 0x4d, 0xaa, 0x19, 0x7f, 0x97, 0xe6, 0x4f, 0x2c, 0xfb, - 0x4d, 0xeb, 0xb7, 0x5a, 0xed, 0x8d, 0xd5, 0xa3, 0x92, 0x35, 0xc7, 0xd9, 0x84, 0xa4, 0xf4, 0x09, 0xdb, 0xcd, 0xa4, - 0x6b, 0x1d, 0x78, 0x12, 0x5c, 0x0e, 0x3d, 0x01, 0x15, 0x83, 0x26, 0xde, 0xee, 0x02, 0xf5, 0x08, 0x3c, 0x03, 0xe5, - 0x13, 0xb5, 0x0e, 0xf8, 0x79, 0xad, 0xe5, 0x29, 0x23, 0x0c, 0xab, 0x9d, 0x45, 0xcb, 0xc1, 0x79, 0xa7, 0x08, 0x5c, - 0xbb, 0x12, 0x78, 0x3e, 0x54, 0xef, 0x85, 0x80, 0xe1, 0xfe, 0xa9, 0x50, 0xd9, 0xec, 0x66, 0x38, 0x8f, 0x1a, 0xa7, - 0x07, 0xda, 0xdb, 0xae, 0xf5, 0x50, 0xef, 0xba, 0x9d, 0xdb, 0x4a, 0xf7, 0x7e, 0xed, 0x64, 0xd2, 0x05, 0xb4, 0x36, - 0x9f, 0x7d, 0x67, 0x57, 0x5a, 0x37, 0x3d, 0x67, 0x0f, 0xb6, 0x6e, 0x89, 0xce, 0x05, 0xd1, 0xe4, 0xf7, 0x03, 0xcf, - 0xda, 0x76, 0xf4, 0xdb, 0xb4, 0x63, 0x9b, 0x7b, 0xa8, 0x7b, 0x05, 0xb5, 0xde, 0xd0, 0xbc, 0x7f, 0xe6, 0xda, 0x76, - 0x7c, 0xf5, 0xeb, 0xba, 0xc3, 0x75, 0xde, 0x04, 0xc7, 0x4d, 0xd7, 0xb6, 0xda, 0xd9, 0xcf, 0xdd, 0xbd, 0xb5, 0x88, - 0xc2, 0x2c, 0xfb, 0xb1, 0x28, 0xfe, 0xa8, 0xf4, 0x1d, 0x81, 0x8e, 0xee, 0xbc, 0xa8, 0xd3, 0xe5, 0xee, 0x03, 0x61, - 0x3c, 0x79, 0xf5, 0x11, 0xd1, 0xad, 0xef, 0x33, 0xf7, 0x2b, 0xc0, 0x8d, 0xe0, 0x0e, 0xa2, 0xbd, 0x5b, 0xea, 0x93, - 0x5a, 0x7d, 0xad, 0xd7, 0xce, 0xd3, 0xf3, 0x9b, 0xce, 0xed, 0x77, 0xdf, 0x1c, 0x6d, 0xbd, 0xc7, 0x85, 0xb5, 0xb2, - 0xf4, 0x54, 0x15, 0xec, 0xcd, 0xf2, 0x54, 0x15, 0x4c, 0x1e, 0x78, 0xcd, 0x7e, 0x41, 0x83, 0x2b, 0x1d, 0x6d, 0xbc, - 0x27, 0x6a, 0xe0, 0x16, 0x85, 0xa5, 0xc3, 0x2f, 0xb9, 0x99, 0xbc, 0xc2, 0xfd, 0xa5, 0x22, 0x17, 0xfb, 0xce, 0x19, - 0xdd, 0x99, 0x59, 0xf7, 0xaa, 0xc2, 0xd5, 0x82, 0x5c, 0x1d, 0xd8, 0x5a, 0x76, 0x71, 0xb8, 0x61, 0x11, 0x05, 0x08, - 0xc4, 0xf4, 0x4a, 0xad, 0xfd, 0x11, 0x0d, 0x42, 0x3e, 0x18, 0xf8, 0x05, 0x06, 0xab, 0x02, 0x85, 0x0f, 0x14, 0xc9, - 0x5f, 0x7b, 0x02, 0x76, 0xf1, 0x0c, 0xd0, 0xad, 0xd8, 0xac, 0x18, 0x21, 0x42, 0x26, 0xcb, 0x59, 0x4d, 0x67, 0x90, - 0x4f, 0x7d, 0xf1, 0x8d, 0xad, 0x3a, 0x9d, 0xb7, 0x35, 0x55, 0x4e, 0x1d, 0x0a, 0xdd, 0xdd, 0xd4, 0x9d, 0x5b, 0x17, - 0x79, 0xea, 0x10, 0x72, 0xa5, 0x62, 0x25, 0xa6, 0xa1, 0xe6, 0x49, 0x9a, 0x51, 0x7f, 0xb1, 0xf7, 0x7b, 0x8d, 0xc2, - 0x29, 0x7f, 0x3a, 0x06, 0x55, 0xb8, 0xaa, 0x21, 0x8e, 0xa5, 0x2a, 0x1e, 0xd9, 0x20, 0xd0, 0xbc, 0xba, 0x55, 0x49, - 0x13, 0x32, 0xb9, 0x11, 0x3e, 0x35, 0x29, 0xe5, 0x69, 0xda, 0xa4, 0x95, 0x22, 0x75, 0xf0, 0x41, 0x9d, 0x6a, 0x3c, - 0x37, 0xab, 0x17, 0x00, 0x66, 0x9c, 0x5f, 0xf1, 0x4b, 0xc5, 0x65, 0xd4, 0x56, 0x66, 0xd2, 0xfe, 0xe4, 0x68, 0x6c, - 0xd4, 0xe5, 0xb4, 0x51, 0x46, 0x58, 0x29, 0xcd, 0x49, 0xb1, 0x1c, 0xcf, 0x3f, 0x60, 0xb0, 0xe6, 0x09, 0xec, 0x60, - 0xa2, 0x52, 0xde, 0x47, 0x40, 0x7c, 0x9d, 0xa4, 0x77, 0x09, 0xa4, 0x48, 0xff, 0xd2, 0x25, 0x77, 0x19, 0x1b, 0x88, - 0x31, 0x2b, 0x66, 0x46, 0xff, 0x83, 0xbb, 0xa4, 0x3f, 0x09, 0x01, 0x70, 0x13, 0x4d, 0xa1, 0x53, 0xe7, 0xc9, 0x45, - 0x1e, 0x2c, 0x2f, 0x3c, 0xb4, 0x62, 0xc4, 0x83, 0xbf, 0xbe, 0x08, 0x11, 0xc4, 0x1c, 0x53, 0x3c, 0xfd, 0xc2, 0xe8, - 0x2f, 0xc1, 0x25, 0x46, 0x10, 0xba, 0x7b, 0xe7, 0x30, 0x84, 0x9b, 0x3d, 0xc8, 0xa0, 0xfe, 0x50, 0x87, 0x44, 0x0d, - 0x7f, 0xac, 0x3c, 0xe8, 0xff, 0x3a, 0x13, 0x96, 0xda, 0x4f, 0x4f, 0x07, 0x50, 0xc1, 0xfb, 0x8a, 0xb7, 0x11, 0xf1, - 0x7d, 0xe2, 0x67, 0xf1, 0x60, 0xf3, 0x6c, 0x03, 0xd6, 0xba, 0x27, 0xb9, 0xb1, 0xae, 0x12, 0x36, 0x10, 0xf0, 0x35, - 0x8a, 0xda, 0xf3, 0xda, 0xed, 0x1e, 0xfc, 0xd5, 0xbf, 0x08, 0x19, 0x30, 0x71, 0xfa, 0x3e, 0x73, 0xb2, 0x46, 0x17, - 0x99, 0x4c, 0x1f, 0x3a, 0xe9, 0x1b, 0x9d, 0xee, 0x3b, 0xe1, 0x1f, 0x15, 0xb3, 0xf8, 0x70, 0x4b, 0x5f, 0x69, 0x52, - 0xdc, 0x01, 0x2b, 0x9b, 0x47, 0x05, 0xa1, 0xce, 0x45, 0xf4, 0x95, 0x29, 0xdf, 0x12, 0x6a, 0xf6, 0x8d, 0x25, 0xa5, - 0x74, 0xaf, 0xa1, 0x37, 0x69, 0xad, 0xdf, 0x46, 0x09, 0xc6, 0x44, 0xc7, 0x93, 0x97, 0xf1, 0x58, 0x79, 0x1f, 0x8f, - 0x1b, 0xa9, 0x90, 0x07, 0x20, 0x02, 0x15, 0xe3, 0x4f, 0x57, 0x9e, 0x9c, 0xf4, 0xc2, 0x78, 0x15, 0x4a, 0x41, 0x61, - 0x40, 0x57, 0x20, 0x05, 0x3c, 0x6a, 0x4f, 0x74, 0x16, 0x76, 0x09, 0xf7, 0xe8, 0x26, 0x60, 0xac, 0xcf, 0x3f, 0x02, - 0x9a, 0xbb, 0x70, 0x87, 0x17, 0x03, 0xd4, 0xa6, 0x5e, 0xdd, 0x7d, 0x5c, 0xab, 0x73, 0x38, 0x04, 0x07, 0xab, 0x41, - 0x04, 0xa7, 0xf3, 0xa9, 0xa3, 0x59, 0x16, 0xa0, 0x72, 0xb2, 0xdc, 0xc8, 0x9b, 0x47, 0x8b, 0x5e, 0xdd, 0xf7, 0x96, - 0x69, 0x59, 0xd5, 0x41, 0xc6, 0xb2, 0xb0, 0x02, 0x5c, 0x1d, 0x5a, 0x3f, 0x08, 0x97, 0x85, 0xf3, 0x07, 0x42, 0x10, - 0xbb, 0x57, 0xdb, 0x92, 0xe7, 0x6a, 0x0e, 0x3f, 0x7b, 0xce, 0xd6, 0x5c, 0xa2, 0x4e, 0x3a, 0x13, 0x01, 0x88, 0x3d, - 0x35, 0xab, 0xe8, 0x1a, 0x48, 0xea, 0x34, 0xab, 0xe8, 0x9a, 0x9a, 0x6d, 0x8c, 0x03, 0xf9, 0x68, 0x95, 0x02, 0xf6, - 0xdd, 0x74, 0x1c, 0xac, 0x9e, 0xc5, 0xf2, 0x3a, 0x74, 0xf7, 0x6c, 0xa3, 0x7c, 0x06, 0x75, 0xab, 0x8d, 0x31, 0xb1, - 0xdd, 0x7c, 0x39, 0xd7, 0x6f, 0x07, 0x4b, 0xdf, 0x0e, 0x9a, 0x73, 0xca, 0xbe, 0xd3, 0x65, 0xaf, 0xec, 0xb2, 0xa9, - 0xe7, 0x8e, 0x8a, 0x56, 0x63, 0x40, 0x6f, 0x60, 0xc1, 0xfa, 0x5c, 0xa4, 0xd9, 0xaa, 0x54, 0x25, 0xe0, 0x85, 0xb1, - 0x62, 0x77, 0x7e, 0x23, 0x33, 0x24, 0x61, 0x1e, 0x67, 0xe2, 0x9a, 0xee, 0xb5, 0x30, 0x39, 0x8e, 0x45, 0x32, 0x25, - 0x74, 0x4a, 0x77, 0xb6, 0xa1, 0x73, 0x15, 0x46, 0x11, 0xad, 0x95, 0x54, 0x1a, 0x09, 0x4c, 0xcd, 0x00, 0x25, 0x73, - 0x05, 0x4e, 0xe9, 0x72, 0xff, 0x3b, 0x12, 0xe3, 0xcc, 0x17, 0x25, 0x33, 0xa0, 0x5b, 0x7e, 0x5d, 0xac, 0x5b, 0x29, - 0x32, 0xc2, 0xbc, 0x39, 0x6e, 0xaf, 0xeb, 0x43, 0x20, 0x57, 0xcb, 0x1e, 0x45, 0xe3, 0xa0, 0xd0, 0xe1, 0x52, 0x25, - 0xc0, 0xbe, 0x48, 0xfc, 0x8c, 0xb0, 0xa5, 0x3d, 0x90, 0xdb, 0xa3, 0x33, 0x61, 0xce, 0x39, 0x29, 0xcb, 0xce, 0xa5, - 0x19, 0x5c, 0x4e, 0x5c, 0x09, 0x2e, 0xd2, 0xdb, 0xf6, 0x34, 0x69, 0x69, 0xfb, 0xd8, 0x70, 0x8e, 0x86, 0xb6, 0x41, - 0x77, 0xec, 0x0f, 0xcd, 0xc5, 0x22, 0xb6, 0x2e, 0x16, 0xc3, 0xce, 0xec, 0x47, 0x8b, 0x05, 0xc8, 0x01, 0xe0, 0xa8, - 0xdb, 0xf0, 0x31, 0x5b, 0x02, 0xa7, 0xd5, 0x34, 0x9b, 0x7a, 0x1b, 0x5e, 0x3d, 0x53, 0x3d, 0xbd, 0xe4, 0xf9, 0x33, - 0x61, 0xc6, 0x62, 0xc3, 0xf3, 0x67, 0xd6, 0x91, 0x53, 0x3d, 0x13, 0x4a, 0xb4, 0x2e, 0xa0, 0x19, 0x78, 0x4d, 0x01, - 0x23, 0x96, 0x4c, 0xa6, 0x54, 0x91, 0xc7, 0xbd, 0xe9, 0x46, 0x0d, 0x5e, 0x50, 0x38, 0x04, 0x52, 0x3a, 0xfd, 0xe2, - 0x39, 0xd3, 0xef, 0x5d, 0x3c, 0xef, 0x90, 0xb5, 0x0d, 0xd3, 0xe5, 0x66, 0x98, 0x0c, 0x4a, 0xff, 0x99, 0x99, 0x18, - 0x17, 0xd6, 0x24, 0x01, 0xc4, 0xbf, 0xb1, 0xdf, 0x21, 0x85, 0x9b, 0xf7, 0x97, 0xc3, 0xf8, 0x91, 0xf7, 0x63, 0x64, - 0x4f, 0xd2, 0x0c, 0xb1, 0x66, 0x52, 0x21, 0x77, 0x5f, 0xad, 0x7f, 0x4c, 0xec, 0x26, 0x7b, 0x60, 0x01, 0x88, 0xad, - 0x69, 0xab, 0x5b, 0xde, 0xef, 0x7b, 0xa6, 0x08, 0xf0, 0x83, 0xf2, 0x8f, 0xee, 0x0c, 0xc9, 0xa0, 0xec, 0xba, 0x21, - 0xc4, 0x83, 0xb2, 0x69, 0xda, 0xeb, 0x6d, 0xef, 0xcc, 0x63, 0x75, 0x9d, 0x76, 0x16, 0x57, 0x8b, 0x0c, 0xd2, 0xea, - 0x43, 0x76, 0x9c, 0xd9, 0x67, 0x47, 0x4b, 0xa5, 0xfb, 0x7d, 0x88, 0x88, 0x3b, 0xca, 0xda, 0x7e, 0xbb, 0x05, 0xd7, - 0x70, 0x34, 0x08, 0x5d, 0xd9, 0xdb, 0x65, 0xb4, 0x71, 0x21, 0x8e, 0x7b, 0xa6, 0xf3, 0x05, 0x5f, 0x1e, 0xa5, 0x9d, - 0x07, 0xa7, 0x7a, 0xa2, 0xcf, 0x4d, 0x77, 0x95, 0xc9, 0xb5, 0x0e, 0xab, 0x31, 0xa8, 0xcd, 0xc2, 0x16, 0xee, 0xc2, - 0x36, 0x3a, 0x68, 0xed, 0xcb, 0x82, 0x7f, 0xca, 0x00, 0x7c, 0xe9, 0xd9, 0xb2, 0xed, 0x35, 0x69, 0xf5, 0x46, 0x46, - 0x21, 0xb6, 0xb4, 0xbd, 0xfa, 0x74, 0x94, 0x8f, 0x9b, 0x13, 0x8a, 0x0b, 0x39, 0xca, 0x8f, 0x5e, 0x43, 0xd4, 0xb5, - 0xae, 0xe3, 0x62, 0xd1, 0xe1, 0xc6, 0x55, 0xb7, 0xdd, 0xb8, 0x7e, 0x40, 0xbc, 0x35, 0xda, 0xa4, 0x50, 0x2b, 0x63, - 0x47, 0xf0, 0xb2, 0x7c, 0x38, 0x64, 0x62, 0x38, 0x94, 0x90, 0xa9, 0x8f, 0xdd, 0x1b, 0x9a, 0xf6, 0xf9, 0x69, 0xeb, - 0x47, 0x2c, 0x35, 0x8e, 0x62, 0xc3, 0x3b, 0x7d, 0xe7, 0xb1, 0x35, 0xae, 0xe4, 0xcb, 0x60, 0xb6, 0x2b, 0xa8, 0xb6, - 0xc6, 0x1b, 0xf6, 0x72, 0xfe, 0x7d, 0x25, 0x95, 0xfc, 0xed, 0xcf, 0x70, 0x0d, 0x6f, 0x6d, 0xe9, 0xa0, 0xa9, 0x66, - 0x39, 0xcb, 0xf5, 0xbd, 0xe0, 0xf8, 0xe3, 0xee, 0x15, 0xc1, 0xe0, 0xf7, 0x74, 0x14, 0xe4, 0x62, 0xa9, 0xd6, 0x80, - 0x82, 0x74, 0x64, 0xc7, 0x54, 0x16, 0x18, 0x06, 0xf0, 0x86, 0x0c, 0x90, 0xc7, 0x14, 0xee, 0x86, 0x0a, 0x2f, 0xfc, - 0xa5, 0x22, 0xbb, 0x04, 0xb6, 0x35, 0xe3, 0x63, 0x86, 0x3b, 0x08, 0xf9, 0x47, 0xb0, 0x3b, 0xb6, 0x62, 0xb7, 0x6c, - 0xc1, 0x90, 0x6c, 0x1c, 0x87, 0x31, 0xe6, 0xe3, 0x49, 0x7c, 0x25, 0x26, 0xf1, 0x80, 0x47, 0xe8, 0x18, 0xb1, 0xe6, - 0xf5, 0x2c, 0x96, 0x03, 0xc8, 0xee, 0xb8, 0xd2, 0x01, 0x21, 0x34, 0x36, 0xb4, 0xe4, 0x4d, 0x61, 0x70, 0xb1, 0x63, - 0x9f, 0x91, 0x48, 0xc6, 0x21, 0x58, 0xb4, 0xaa, 0x81, 0x85, 0x89, 0xdd, 0xf2, 0x62, 0xb6, 0x9a, 0xe3, 0x3f, 0x87, - 0x03, 0x02, 0x60, 0x07, 0xfb, 0x86, 0xdd, 0x45, 0x88, 0xf4, 0xb6, 0xe0, 0x77, 0x96, 0xa7, 0x0b, 0xbb, 0xe7, 0xd7, - 0x7c, 0xcc, 0xce, 0x7f, 0xf0, 0x20, 0x72, 0xf6, 0xfc, 0x23, 0xa0, 0x21, 0xde, 0xf3, 0xdb, 0xd4, 0xab, 0xd8, 0x2d, - 0x51, 0x10, 0xde, 0x82, 0x33, 0xd0, 0x3d, 0x44, 0xc0, 0x5e, 0xf3, 0x05, 0xc6, 0x8a, 0x9d, 0xa5, 0x4b, 0x0f, 0x33, - 0x42, 0xed, 0xe9, 0x7c, 0x59, 0xab, 0x49, 0xb8, 0xb9, 0x5a, 0x4e, 0x06, 0x83, 0x8d, 0xbf, 0xe3, 0x6b, 0xe0, 0x83, - 0x39, 0xff, 0xc1, 0xdb, 0x51, 0xb9, 0xf0, 0x9f, 0xd7, 0x59, 0xf2, 0xce, 0x67, 0xd7, 0x03, 0xbe, 0x00, 0xbc, 0x25, - 0x74, 0xe0, 0xba, 0xf7, 0x99, 0xc4, 0x6b, 0xbb, 0xd6, 0xd7, 0x08, 0x24, 0xf2, 0x05, 0x60, 0xc4, 0xc4, 0xfc, 0xbe, - 0x86, 0x08, 0x8c, 0x04, 0x7c, 0x5b, 0xb5, 0x47, 0xfc, 0x96, 0x1b, 0xc0, 0xaf, 0xcc, 0x67, 0x0f, 0x3c, 0xd4, 0x3f, - 0x13, 0x9f, 0xdd, 0xf0, 0xf7, 0xfc, 0x85, 0x27, 0x25, 0xe9, 0x72, 0xf6, 0x7e, 0x0e, 0xd7, 0x43, 0x29, 0x4f, 0x87, - 0xf4, 0xb3, 0x31, 0x18, 0x40, 0x28, 0x64, 0xde, 0x78, 0xc0, 0x9a, 0x14, 0xe2, 0x5f, 0xc0, 0xb7, 0xa3, 0x84, 0xcd, - 0x1b, 0x6f, 0xeb, 0x6b, 0x79, 0xf3, 0xc6, 0x7b, 0xf0, 0x29, 0x0a, 0xb0, 0x0a, 0x4a, 0x59, 0x60, 0x15, 0x84, 0x8d, - 0x36, 0xc2, 0x18, 0xb8, 0x7a, 0xd7, 0x18, 0xea, 0x7a, 0x8e, 0xd8, 0xb6, 0xd2, 0x77, 0xe1, 0x3b, 0xc8, 0x80, 0x0f, - 0xde, 0x14, 0x25, 0xd1, 0xe7, 0xd4, 0x14, 0x49, 0xeb, 0x9e, 0xfb, 0xad, 0x75, 0x47, 0x6b, 0x4a, 0x7d, 0xe4, 0x6a, - 0x7c, 0x38, 0xd4, 0x2f, 0x84, 0x16, 0x09, 0xa6, 0xa0, 0x71, 0x0d, 0xda, 0x02, 0x04, 0x7d, 0x1e, 0x20, 0x6b, 0x49, - 0xb1, 0xe0, 0xdb, 0x5f, 0x21, 0x06, 0xaf, 0x4c, 0xef, 0x5c, 0xae, 0x32, 0x12, 0xb6, 0x17, 0x7e, 0x39, 0xac, 0xfd, - 0x89, 0x53, 0x0b, 0x4b, 0xab, 0x39, 0xa8, 0x9f, 0xd9, 0x72, 0x9c, 0xaa, 0xda, 0xbf, 0x25, 0x49, 0xb5, 0xab, 0xb4, - 0x9c, 0xde, 0xdb, 0x37, 0x5d, 0x26, 0xd8, 0xd8, 0x0f, 0xa8, 0x3a, 0xb2, 0x1a, 0x76, 0x5f, 0xa8, 0x2f, 0x7a, 0x4a, - 0x26, 0x34, 0x1f, 0x55, 0x34, 0xcf, 0xee, 0x37, 0x3b, 0xea, 0x3f, 0xbd, 0x1c, 0x8a, 0x00, 0xc9, 0x2a, 0x2d, 0x96, - 0x22, 0x67, 0x63, 0x3f, 0x1e, 0x26, 0x99, 0x0a, 0x2f, 0x48, 0x47, 0x77, 0xbf, 0x71, 0x7f, 0xcb, 0x0d, 0x64, 0x85, - 0x56, 0x6d, 0x30, 0x56, 0x8a, 0x96, 0xc1, 0xfa, 0x6a, 0xdc, 0xef, 0x8b, 0xab, 0xf1, 0x54, 0x04, 0x35, 0x10, 0x17, - 0x89, 0x17, 0xe3, 0x69, 0x4d, 0x2c, 0xa9, 0x5d, 0x81, 0x31, 0x7a, 0x5c, 0x15, 0xb5, 0x4f, 0xfd, 0x02, 0x42, 0x91, - 0x6a, 0xcd, 0x1c, 0x6b, 0xdc, 0x18, 0x11, 0x77, 0x58, 0xb9, 0x76, 0x6a, 0xaf, 0x03, 0xb0, 0xbc, 0x1a, 0x17, 0x84, - 0x4d, 0x72, 0xec, 0x5c, 0xc0, 0x6a, 0x34, 0xa4, 0xda, 0x0d, 0xb7, 0x5e, 0x76, 0x7e, 0xf3, 0x38, 0xb1, 0xb5, 0x11, - 0x6e, 0x29, 0xa0, 0x8c, 0xf2, 0x1b, 0xcb, 0x09, 0xbb, 0x53, 0xbd, 0x23, 0x55, 0x3b, 0xe2, 0xc4, 0x05, 0x2c, 0x37, - 0x3c, 0xb5, 0xfa, 0x26, 0x06, 0x27, 0x42, 0xd5, 0x4a, 0xc7, 0x6b, 0x3f, 0xe2, 0x7e, 0x75, 0x5f, 0xf7, 0x4a, 0xf0, - 0x93, 0x90, 0xd7, 0x6f, 0x79, 0x07, 0x80, 0x15, 0x1f, 0xf2, 0x62, 0x5a, 0x38, 0x5a, 0x97, 0x41, 0x19, 0x20, 0x42, - 0x33, 0x00, 0x3a, 0xb9, 0x3a, 0x88, 0xd2, 0xc0, 0x15, 0x77, 0x88, 0xf0, 0xd3, 0xe8, 0x59, 0xfe, 0x22, 0x7c, 0x56, - 0x4d, 0xc3, 0x8b, 0x3c, 0x88, 0x2e, 0xaa, 0x20, 0x7a, 0x56, 0x5d, 0x85, 0xcf, 0xf2, 0x69, 0x74, 0x91, 0x07, 0xe1, - 0x45, 0xd5, 0xd8, 0x77, 0xed, 0xee, 0x9e, 0x90, 0xb7, 0x5d, 0xfd, 0x91, 0x73, 0x65, 0x4f, 0x99, 0x9e, 0x9f, 0xd7, - 0x7a, 0xa5, 0x76, 0x9b, 0xeb, 0x35, 0x6a, 0xa6, 0x3e, 0xca, 0xfe, 0x66, 0x1b, 0x0b, 0x8f, 0xe6, 0x10, 0xfa, 0x8c, - 0xb4, 0x98, 0x7b, 0x9c, 0xeb, 0xcd, 0x9e, 0x14, 0x06, 0x46, 0x4c, 0x2a, 0x19, 0x39, 0xbd, 0xc0, 0x45, 0xa8, 0x42, - 0x0c, 0x6b, 0xe9, 0x6a, 0x9f, 0x75, 0xe9, 0x0d, 0xd4, 0x35, 0xc5, 0xbe, 0x86, 0x0c, 0xbc, 0x68, 0x7a, 0x19, 0x8c, - 0x01, 0x39, 0x02, 0xef, 0xf8, 0x6c, 0x09, 0x07, 0xe6, 0x1a, 0xa0, 0x6f, 0x1e, 0xf5, 0x75, 0xb9, 0xe3, 0x6b, 0xd5, - 0x37, 0xd3, 0xf5, 0x48, 0x29, 0x3f, 0x56, 0xfc, 0xee, 0xe2, 0x39, 0xbb, 0xe5, 0x1a, 0x15, 0xe5, 0x17, 0xbd, 0x58, - 0xef, 0x81, 0xab, 0xee, 0x17, 0xb8, 0xcd, 0xe2, 0xb1, 0x2b, 0x0f, 0x58, 0xb6, 0x65, 0x0f, 0xec, 0x86, 0xbd, 0x67, - 0x4f, 0xd8, 0x5b, 0xf6, 0x8e, 0xfd, 0x84, 0xaa, 0x0d, 0x25, 0xe4, 0xf9, 0x0b, 0x7e, 0x2b, 0x4d, 0x8f, 0x12, 0x95, - 0xec, 0xc1, 0x36, 0xd3, 0x0c, 0x37, 0xec, 0x3d, 0x5f, 0x0c, 0x57, 0xec, 0x2d, 0x64, 0x43, 0x99, 0x78, 0xb0, 0x62, - 0x3f, 0x71, 0x05, 0x62, 0xa6, 0xcf, 0xc2, 0xd2, 0x12, 0x15, 0x4d, 0x99, 0x28, 0x43, 0xbf, 0xe5, 0xf8, 0x22, 0xfb, - 0x09, 0x8b, 0x90, 0x9f, 0x19, 0xae, 0xd8, 0x03, 0x5f, 0x0c, 0x56, 0xec, 0xbd, 0x36, 0x10, 0x0d, 0x36, 0x6e, 0x69, - 0x84, 0x64, 0xa5, 0xcb, 0x92, 0xd2, 0xf4, 0xd6, 0xbe, 0x06, 0x6e, 0xd8, 0x0d, 0xd6, 0xee, 0x09, 0x16, 0x8d, 0x02, - 0xff, 0x60, 0xc5, 0xde, 0x71, 0x09, 0xa0, 0xe6, 0x96, 0x27, 0xbd, 0x42, 0x75, 0x81, 0x74, 0x3f, 0x78, 0xc2, 0xe9, - 0x45, 0xf6, 0x0e, 0xcb, 0xa0, 0xaf, 0x0c, 0x57, 0x6c, 0x8b, 0xb5, 0xbb, 0x31, 0x96, 0x2d, 0xab, 0x7a, 0x12, 0x11, - 0x18, 0x05, 0x95, 0xd2, 0xf2, 0x6f, 0xc4, 0xb2, 0xa9, 0x9b, 0x06, 0xb5, 0xa1, 0x3f, 0x1f, 0x8c, 0xfe, 0xe2, 0xeb, - 0x77, 0x3f, 0x78, 0xa5, 0xbe, 0xf6, 0xfe, 0xe2, 0xb8, 0x56, 0x96, 0xe8, 0x5a, 0xf9, 0x2b, 0x2f, 0x67, 0xbf, 0xcc, - 0x27, 0xba, 0x96, 0xb4, 0xc3, 0x90, 0xaf, 0xe9, 0xec, 0x97, 0x0e, 0x67, 0xcb, 0x5f, 0x7d, 0xbf, 0x31, 0x5d, 0xac, - 0x3e, 0xab, 0x7b, 0xf7, 0x61, 0xb0, 0x69, 0x9c, 0x7a, 0xef, 0x4e, 0xd7, 0x1b, 0x9b, 0x59, 0x6b, 0xcf, 0xcc, 0xff, - 0xe1, 0x4a, 0x6f, 0x71, 0xe8, 0x6e, 0xf8, 0x76, 0xb8, 0xb1, 0x47, 0x41, 0x7e, 0x5f, 0x2a, 0x8d, 0xb3, 0x9a, 0xbf, - 0xf4, 0x3a, 0xa5, 0x58, 0x40, 0x34, 0xfa, 0x64, 0x24, 0xa1, 0x4b, 0x66, 0xe2, 0x19, 0xe2, 0x8b, 0x0c, 0x90, 0xb9, - 0x40, 0x34, 0xbb, 0xe7, 0xe3, 0xc9, 0xfd, 0x55, 0x3c, 0xb9, 0x1f, 0xf0, 0x4f, 0xa6, 0x05, 0xed, 0xc5, 0x76, 0xef, - 0xb3, 0x5f, 0x79, 0x61, 0x2f, 0xc7, 0x5f, 0x7c, 0xf6, 0x45, 0xb8, 0x2b, 0xf4, 0x17, 0x9f, 0xbd, 0x13, 0xfc, 0xd7, - 0x91, 0x26, 0xca, 0x60, 0xef, 0x6a, 0xfe, 0xeb, 0x08, 0x19, 0x3f, 0xd8, 0x67, 0xc1, 0xbf, 0x80, 0xef, 0x77, 0x95, - 0xa0, 0x55, 0xfc, 0x73, 0xad, 0x7e, 0xbe, 0x97, 0x71, 0x39, 0xf0, 0x26, 0xb4, 0x82, 0xde, 0xbc, 0xad, 0xe5, 0x4f, - 0xe2, 0xe1, 0x48, 0xd5, 0x53, 0xc3, 0x3f, 0x8b, 0xc5, 0x2c, 0xea, 0xa3, 0x74, 0x2a, 0x6f, 0x72, 0xcd, 0x33, 0x69, - 0x5d, 0xbe, 0x87, 0x50, 0xe0, 0x6b, 0x1b, 0xa2, 0x60, 0xc7, 0x71, 0x23, 0xb8, 0x66, 0xef, 0x84, 0xcf, 0xb2, 0xe9, - 0x96, 0xdf, 0xf0, 0x27, 0xfc, 0x1d, 0xdf, 0x05, 0x0f, 0xfc, 0x3d, 0x7f, 0xcb, 0x7f, 0xe2, 0x3b, 0xb6, 0x94, 0x68, - 0xa7, 0xf5, 0xf6, 0x32, 0xd8, 0xb2, 0x7a, 0x77, 0x19, 0x3c, 0xb0, 0x7a, 0xfb, 0x3c, 0xb8, 0x61, 0xf5, 0xee, 0x79, - 0xf0, 0x9e, 0x6d, 0x2f, 0x83, 0x27, 0x6c, 0x77, 0x19, 0xbc, 0x65, 0xdb, 0xe7, 0xc1, 0x3b, 0xb6, 0x7b, 0x1e, 0xfc, - 0x24, 0x31, 0x1e, 0xde, 0x09, 0xc9, 0x71, 0xf2, 0xae, 0x66, 0x86, 0x4f, 0x37, 0xf8, 0x2c, 0xac, 0x5f, 0x54, 0xc7, - 0xe0, 0x73, 0xcd, 0x74, 0x8b, 0x03, 0x21, 0x98, 0x6e, 0x6f, 0x70, 0x4b, 0x4f, 0x4c, 0xab, 0x82, 0x54, 0xb0, 0xae, - 0x76, 0x06, 0x8b, 0xba, 0x69, 0x9d, 0xc9, 0x8e, 0x5f, 0x62, 0xdc, 0xe1, 0x97, 0xb8, 0x60, 0xcb, 0xa6, 0xd3, 0x49, - 0xe7, 0xf4, 0x49, 0xa0, 0x37, 0x7f, 0xbd, 0xeb, 0x57, 0xd2, 0x77, 0xa6, 0x68, 0x78, 0xae, 0xb4, 0xc6, 0xad, 0x9d, - 0x3e, 0xb4, 0x76, 0x7a, 0x26, 0x55, 0x68, 0x11, 0x8b, 0xca, 0xa2, 0xaa, 0x90, 0x49, 0x3c, 0xc8, 0xb4, 0x3e, 0x2d, - 0x61, 0xa4, 0xc8, 0x04, 0x34, 0xfa, 0x82, 0x8e, 0x81, 0x9c, 0x2c, 0x0a, 0x6c, 0xc9, 0x37, 0x83, 0x84, 0xad, 0x79, - 0x3c, 0x1d, 0x26, 0xc1, 0x92, 0xdd, 0xf1, 0x61, 0xb7, 0x40, 0xb0, 0x52, 0x01, 0x4c, 0xfa, 0xe2, 0xd4, 0xde, 0xd7, - 0x79, 0x6f, 0x95, 0xc6, 0x71, 0x26, 0x50, 0xd9, 0x56, 0xe9, 0x0d, 0x7e, 0xeb, 0xec, 0xe7, 0x6b, 0xb5, 0xbf, 0x83, - 0xa4, 0xf0, 0x2b, 0x30, 0xec, 0x10, 0xe1, 0x1d, 0x54, 0x18, 0x79, 0x96, 0xcc, 0xa2, 0xaf, 0xec, 0x2d, 0x7d, 0x6b, - 0xb6, 0xe9, 0xff, 0xb4, 0x08, 0xda, 0xc7, 0x65, 0xe7, 0x7f, 0x32, 0xaf, 0xfe, 0xd6, 0xf1, 0xea, 0xc6, 0x9f, 0x3c, - 0xf0, 0x4f, 0x18, 0x96, 0x80, 0x89, 0x6c, 0xc7, 0x3f, 0x8d, 0xb6, 0x8d, 0x53, 0x9e, 0xdc, 0xc7, 0xff, 0xaf, 0x14, - 0x68, 0xef, 0xe4, 0x95, 0xbd, 0x23, 0x6e, 0x79, 0xc7, 0x3e, 0xbe, 0xb4, 0x36, 0x44, 0x03, 0x4d, 0xf2, 0x89, 0xbb, - 0xd1, 0xd0, 0xb0, 0x21, 0xfe, 0xc2, 0xab, 0xd9, 0xa7, 0xf9, 0x64, 0xcb, 0x8f, 0xb7, 0xc3, 0x4f, 0x1d, 0xdb, 0xe1, - 0x2f, 0xfe, 0x60, 0xd9, 0x7c, 0xad, 0x57, 0x3b, 0xb7, 0x71, 0xa7, 0xd2, 0x3b, 0x7e, 0xbc, 0x89, 0x0f, 0xff, 0xe3, - 0x4a, 0xef, 0xbe, 0xb9, 0xd2, 0x76, 0x95, 0xbb, 0x3b, 0xdf, 0x74, 0x7c, 0x23, 0x6b, 0x8d, 0x71, 0x66, 0x46, 0xb3, - 0xf8, 0x13, 0xcd, 0xd2, 0x20, 0xb2, 0x14, 0x8a, 0x3f, 0x99, 0x69, 0xa7, 0xee, 0x54, 0x59, 0xdd, 0x2d, 0xdf, 0xe2, - 0x1e, 0x7f, 0xcb, 0xc7, 0x6c, 0x61, 0x3c, 0x35, 0x6f, 0xaf, 0x16, 0x93, 0xc1, 0xe0, 0xd6, 0xdf, 0xdf, 0xf3, 0x70, - 0x76, 0x3b, 0x67, 0xd7, 0xfc, 0x9e, 0x16, 0xd3, 0x44, 0x35, 0xbd, 0x78, 0x4c, 0xf0, 0xba, 0xf5, 0xfd, 0x89, 0xc5, - 0xff, 0x6a, 0x5f, 0x34, 0x6f, 0xfd, 0x81, 0xb4, 0x46, 0xcb, 0x5d, 0xfd, 0xfd, 0xe3, 0x8a, 0x89, 0x5b, 0x10, 0x2f, - 0xde, 0xdb, 0x9a, 0x86, 0xb7, 0xfc, 0xa3, 0x77, 0xed, 0x4f, 0xaf, 0x75, 0xcc, 0xcd, 0x44, 0x1d, 0x49, 0x6f, 0x2f, - 0x9e, 0xb3, 0x5f, 0xf9, 0x27, 0x79, 0x9c, 0x7c, 0x11, 0x72, 0xd2, 0xde, 0x20, 0x77, 0x13, 0x9d, 0x12, 0xef, 0xdc, - 0x44, 0xc2, 0x82, 0x40, 0x18, 0x8e, 0x9a, 0x3f, 0x4c, 0xca, 0xa9, 0xb7, 0x03, 0x6e, 0x57, 0x6e, 0xeb, 0x9f, 0x6f, - 0x39, 0xe7, 0x8b, 0xe1, 0xe5, 0xf4, 0x5d, 0xb7, 0x4b, 0x8f, 0x8a, 0x66, 0x53, 0x81, 0x6e, 0xb7, 0x18, 0x7b, 0x75, - 0x32, 0xb3, 0xcc, 0x25, 0x5f, 0x7a, 0x57, 0x9b, 0x99, 0xc7, 0xf4, 0x7e, 0x33, 0xcd, 0x90, 0xc8, 0x17, 0x08, 0x99, - 0x0e, 0x87, 0xbb, 0x73, 0x2c, 0x8f, 0x0f, 0xdf, 0x3e, 0x7b, 0x32, 0x78, 0x82, 0x91, 0x5b, 0x56, 0x34, 0xc8, 0x3b, - 0x3e, 0xcc, 0xea, 0xd6, 0x6d, 0xe3, 0xe2, 0xf9, 0xf0, 0x17, 0xc8, 0x1b, 0x74, 0x3d, 0x34, 0x45, 0xb4, 0xca, 0xef, - 0x28, 0xfa, 0x44, 0xc9, 0x41, 0xc7, 0x13, 0xa8, 0x1d, 0x52, 0xe0, 0xbe, 0x7b, 0xc6, 0x41, 0xbf, 0x81, 0xa5, 0xf6, - 0xfb, 0xe7, 0x9f, 0x88, 0x47, 0x1a, 0xc6, 0xfb, 0xfb, 0x30, 0xfa, 0x23, 0x2e, 0x8b, 0x35, 0x9c, 0xae, 0x03, 0xf8, - 0xdc, 0x33, 0x7d, 0xfb, 0xba, 0xf3, 0x7d, 0x3f, 0xf0, 0xb6, 0xfc, 0x86, 0xbd, 0xe3, 0xde, 0xe5, 0xf0, 0xad, 0xff, - 0xec, 0x09, 0x88, 0x4e, 0x30, 0x2e, 0x9f, 0x31, 0x12, 0xb6, 0xa3, 0x18, 0xb5, 0x0a, 0x3f, 0xd7, 0x10, 0xa2, 0xf5, - 0x09, 0x19, 0xbb, 0x20, 0xfd, 0x83, 0x02, 0xf4, 0x13, 0x02, 0xab, 0x49, 0x6a, 0x14, 0x98, 0xc4, 0xb7, 0x35, 0x24, - 0x90, 0x82, 0x05, 0x42, 0x6f, 0xa0, 0xf8, 0x54, 0xf0, 0x77, 0xc3, 0xcf, 0x24, 0xf9, 0x2d, 0x6a, 0x3e, 0x86, 0xbf, - 0x61, 0x68, 0x26, 0xd5, 0x43, 0x5a, 0x47, 0x89, 0xf7, 0x93, 0xbf, 0x8f, 0xc2, 0x4a, 0xa8, 0x63, 0x21, 0x48, 0xc5, - 0x90, 0x0b, 0x71, 0xf1, 0x7c, 0x72, 0x5b, 0x8a, 0xf0, 0x8f, 0x09, 0x3e, 0x93, 0x0b, 0x4d, 0x3e, 0xa3, 0x27, 0x8d, - 0x7c, 0xff, 0x41, 0xbe, 0x2f, 0x3b, 0x35, 0x58, 0xd4, 0x43, 0x7e, 0x5b, 0xbb, 0xef, 0xcb, 0x29, 0x41, 0x8f, 0xec, - 0x07, 0x34, 0x05, 0x03, 0x35, 0x01, 0x29, 0x43, 0x70, 0x0b, 0x57, 0x7d, 0x4f, 0x15, 0xe4, 0xcb, 0xef, 0x7d, 0x16, - 0x32, 0x5c, 0x65, 0x41, 0x48, 0x72, 0xa9, 0x90, 0xc2, 0xc6, 0x6d, 0x3d, 0xf8, 0xac, 0x31, 0x49, 0x24, 0xe4, 0x94, - 0x80, 0x24, 0x69, 0x6f, 0x20, 0x49, 0xc4, 0xf4, 0x1f, 0xae, 0x93, 0xa6, 0x59, 0x49, 0xe9, 0x86, 0x38, 0x55, 0xaf, - 0x91, 0xe6, 0x2c, 0x78, 0xcf, 0x60, 0xe9, 0x48, 0xb1, 0xe2, 0x9d, 0x31, 0x18, 0xeb, 0x60, 0xa1, 0x3b, 0x59, 0xdc, - 0xaf, 0x92, 0x30, 0x8d, 0x44, 0x95, 0x2f, 0x42, 0xfe, 0xfc, 0x97, 0x12, 0x7f, 0xf4, 0x96, 0x06, 0x22, 0x10, 0xfc, - 0x00, 0xad, 0x07, 0xac, 0xf1, 0xe0, 0x27, 0x56, 0x97, 0x61, 0x5e, 0x65, 0x54, 0xde, 0x6c, 0xc7, 0xb6, 0x73, 0xa6, - 0xaa, 0x16, 0x7c, 0x16, 0x86, 0x16, 0xed, 0x6c, 0xd5, 0x9c, 0xdc, 0xe6, 0x0d, 0xbe, 0x33, 0x49, 0x22, 0xb5, 0x94, - 0x44, 0xda, 0xea, 0xfa, 0x74, 0xe9, 0x75, 0x8b, 0x0a, 0x1a, 0x23, 0x40, 0x2f, 0x49, 0x77, 0x95, 0x4f, 0x28, 0x5e, - 0x59, 0x0d, 0xab, 0xe1, 0xa5, 0x43, 0x11, 0xc6, 0xda, 0x9b, 0x2b, 0x79, 0x76, 0x07, 0xd6, 0x23, 0xb4, 0x76, 0x55, - 0xea, 0x10, 0xb6, 0x9f, 0xe8, 0x3d, 0xa7, 0x52, 0x7f, 0x03, 0xaa, 0xc0, 0xa9, 0xa3, 0xa1, 0x3e, 0x6a, 0xa7, 0x90, - 0xed, 0xdc, 0x5b, 0x12, 0x54, 0xae, 0xe4, 0xa6, 0x4a, 0x8b, 0x52, 0xca, 0x94, 0xaf, 0x65, 0xb6, 0xb2, 0xfb, 0x64, - 0x00, 0xf1, 0x6c, 0x50, 0x20, 0xb9, 0xa8, 0xad, 0xe6, 0x20, 0x7d, 0x34, 0x4b, 0x1c, 0x6b, 0x07, 0x85, 0x97, 0x55, - 0x60, 0xe6, 0x32, 0x97, 0xcb, 0x41, 0xc1, 0x72, 0xbd, 0xd5, 0x4c, 0x33, 0xd5, 0x17, 0xb9, 0xbd, 0xcd, 0x78, 0x99, - 0xfe, 0x9b, 0x25, 0x03, 0x1e, 0x5d, 0x3c, 0xf7, 0x03, 0x48, 0x93, 0xbc, 0x0e, 0x90, 0x04, 0x9b, 0x83, 0x5d, 0xec, - 0x30, 0x6c, 0x15, 0x2b, 0x7b, 0xf2, 0x74, 0xb9, 0x43, 0x53, 0x2e, 0x61, 0x24, 0x27, 0xe6, 0x52, 0xea, 0xfb, 0x92, - 0xea, 0x86, 0x82, 0x93, 0x4d, 0x13, 0x50, 0x0a, 0x68, 0xb7, 0xe0, 0xbf, 0xf0, 0xa9, 0xa1, 0xd3, 0x02, 0x2c, 0xb5, - 0xdd, 0x80, 0xff, 0x42, 0xbf, 0xd8, 0x3e, 0xa2, 0x7e, 0x60, 0x1e, 0xec, 0xcd, 0xda, 0xca, 0x18, 0x10, 0x91, 0xb8, - 0x82, 0x3c, 0x12, 0xfc, 0xa0, 0xd8, 0xd3, 0x65, 0xe2, 0xc0, 0x99, 0xe2, 0x62, 0x29, 0xb5, 0x99, 0x79, 0xed, 0xb7, - 0xd4, 0xc4, 0x9b, 0x28, 0x89, 0x0a, 0xdb, 0x21, 0x8d, 0x5e, 0x52, 0xc6, 0x54, 0xc1, 0x86, 0xe8, 0xbe, 0x6e, 0x82, - 0x29, 0xf0, 0xa6, 0xaa, 0x02, 0x22, 0xd4, 0x5e, 0x64, 0x79, 0x7e, 0xd3, 0x05, 0x56, 0x17, 0x7c, 0x6c, 0x4c, 0xb3, - 0x0b, 0x56, 0x72, 0x35, 0x93, 0x3e, 0xf3, 0x76, 0xa0, 0x85, 0xbc, 0xcb, 0xcb, 0xa2, 0x15, 0xba, 0x1e, 0x44, 0x0b, - 0x7f, 0xaf, 0x39, 0x1e, 0x3d, 0xdb, 0x56, 0x53, 0x9b, 0x7d, 0xad, 0xc5, 0x02, 0x19, 0x88, 0x86, 0xbe, 0x90, 0x33, - 0x0a, 0x77, 0x95, 0xe6, 0x6a, 0xb5, 0xaf, 0xca, 0x20, 0x81, 0x89, 0x20, 0x6b, 0x59, 0x78, 0x8f, 0xee, 0xd5, 0x23, - 0xcd, 0x2b, 0x09, 0x9e, 0xb9, 0xf8, 0x0b, 0x00, 0xa1, 0x3c, 0x49, 0xc8, 0x01, 0x39, 0x80, 0xbf, 0xa5, 0x28, 0x95, - 0x06, 0xf8, 0x67, 0x75, 0x39, 0xb6, 0xf5, 0xfd, 0x9d, 0x56, 0x31, 0xb8, 0xfe, 0x7c, 0xdd, 0xf5, 0xac, 0x1d, 0xe2, - 0x5c, 0xd9, 0xea, 0xb5, 0x65, 0x9a, 0xc7, 0x48, 0x5d, 0x03, 0x70, 0x27, 0xd2, 0x23, 0x10, 0xc9, 0x4c, 0x34, 0xc8, - 0xd9, 0x0b, 0x3e, 0x9e, 0x8a, 0xc7, 0xa4, 0xbd, 0xca, 0xf7, 0xcd, 0x85, 0x3e, 0x18, 0x63, 0xdf, 0x82, 0x06, 0xf1, - 0xd1, 0x6a, 0x6b, 0x05, 0x62, 0xbd, 0x55, 0xea, 0x43, 0x37, 0x46, 0x41, 0x07, 0x8f, 0xb8, 0x91, 0x0b, 0x8e, 0xed, - 0xae, 0xad, 0xa7, 0xf4, 0x15, 0x80, 0xb9, 0x0e, 0x54, 0x32, 0x0c, 0x52, 0xe7, 0x89, 0xc2, 0x24, 0x3f, 0x4f, 0x48, - 0x42, 0x44, 0x75, 0xb6, 0x1c, 0xa5, 0xdc, 0xb4, 0x80, 0xcb, 0x8c, 0x0c, 0x30, 0x9b, 0x34, 0xeb, 0x27, 0x97, 0x2f, - 0x41, 0x2a, 0x0d, 0x11, 0xdc, 0xb0, 0xbd, 0x64, 0x74, 0xeb, 0xa8, 0x1b, 0x54, 0x49, 0xe6, 0xfa, 0xcd, 0xed, 0x2c, - 0x52, 0xe6, 0xcd, 0x47, 0x18, 0x6b, 0xf2, 0x21, 0xac, 0x13, 0xfc, 0x36, 0x40, 0x25, 0x7d, 0x2a, 0xbc, 0x68, 0x04, - 0x10, 0xea, 0x3b, 0x55, 0xc6, 0xa7, 0xc2, 0xcb, 0x46, 0x5b, 0x96, 0x51, 0x0a, 0xd5, 0x05, 0xb3, 0x5b, 0xd3, 0x85, - 0xe8, 0x56, 0xd5, 0x40, 0x1b, 0xb8, 0x76, 0x1d, 0x28, 0xa0, 0xa1, 0xda, 0x95, 0x1b, 0x16, 0x80, 0xd5, 0x4c, 0x04, - 0x86, 0xcb, 0xbf, 0xcf, 0x5f, 0xa9, 0x18, 0x9e, 0x7e, 0x3f, 0xf4, 0xf6, 0xdb, 0x20, 0x1a, 0x6d, 0x2f, 0xd9, 0x2e, - 0x88, 0x46, 0xbb, 0xcb, 0x86, 0xd1, 0xef, 0xe7, 0xf4, 0xfb, 0x79, 0x03, 0x3a, 0x12, 0x61, 0xc2, 0xec, 0xf5, 0x1b, - 0xb5, 0x7c, 0xa5, 0xd6, 0xef, 0xd4, 0xf2, 0xa5, 0x1a, 0xde, 0xda, 0x93, 0x44, 0x10, 0x59, 0xaa, 0x9a, 0x07, 0x49, - 0x91, 0x6a, 0xe9, 0x72, 0x8c, 0x16, 0x23, 0x6a, 0x29, 0x6b, 0x8e, 0x75, 0x22, 0xed, 0x1c, 0x94, 0x0c, 0x70, 0xb4, - 0xb8, 0xaa, 0x31, 0xdd, 0xac, 0x68, 0x09, 0xc4, 0x08, 0x2b, 0xdb, 0x72, 0x71, 0x93, 0xfa, 0xe8, 0x9c, 0x7c, 0xdb, - 0x2a, 0xe5, 0xdb, 0x56, 0xf0, 0xfc, 0x2b, 0x0a, 0xe5, 0x92, 0x6b, 0xd7, 0xb2, 0x69, 0xa1, 0x14, 0xca, 0xb8, 0x06, - 0x5b, 0xfb, 0x26, 0x30, 0x64, 0x3e, 0x52, 0xd4, 0xd8, 0x5e, 0x34, 0xca, 0x21, 0xc8, 0xd6, 0xc1, 0xa8, 0x53, 0x16, - 0x2c, 0xbe, 0xdd, 0x21, 0x03, 0x19, 0xe8, 0xa8, 0x6a, 0xe3, 0xd5, 0xce, 0x4a, 0x7f, 0x58, 0x5e, 0x3c, 0x67, 0x89, - 0x95, 0x4e, 0x7e, 0x53, 0xa1, 0x3f, 0x08, 0xd1, 0x37, 0x65, 0xc3, 0xc1, 0x8b, 0x2e, 0xb6, 0x32, 0x20, 0xde, 0x30, - 0xbd, 0xb7, 0xb1, 0x92, 0xe5, 0xae, 0x29, 0x5f, 0xcc, 0x78, 0xc2, 0x71, 0xf4, 0xe5, 0x6a, 0x11, 0xd6, 0x6a, 0x91, - 0x9d, 0x00, 0x0f, 0xad, 0xd5, 0x52, 0xc8, 0xd5, 0x22, 0x9c, 0x99, 0x2e, 0xd4, 0x4c, 0xcf, 0x40, 0xf3, 0x28, 0xd4, - 0x2c, 0x4f, 0x00, 0x0b, 0x5e, 0x98, 0x19, 0x2e, 0xcc, 0x0c, 0xc7, 0x21, 0x35, 0x4e, 0x0f, 0x7a, 0xaf, 0x73, 0xcf, - 0x2d, 0x77, 0xa3, 0xd3, 0x30, 0x6f, 0x47, 0x1b, 0xcc, 0xf1, 0x41, 0x38, 0x81, 0xf8, 0xc0, 0x12, 0x01, 0x7a, 0x34, - 0xac, 0x8e, 0x1a, 0x2a, 0x47, 0xf1, 0x65, 0x01, 0x48, 0x96, 0x04, 0x20, 0xb9, 0x57, 0xe3, 0x5c, 0x5a, 0x7e, 0x5d, - 0x25, 0x21, 0x47, 0x64, 0xbc, 0x94, 0x76, 0xf7, 0x84, 0x97, 0x23, 0x23, 0x34, 0x4f, 0x16, 0xa9, 0x97, 0xb3, 0x8c, - 0x8d, 0x11, 0xb8, 0x28, 0xf4, 0x9b, 0xaa, 0xdf, 0x4f, 0x4b, 0x2f, 0xa7, 0x76, 0x7e, 0x02, 0x7f, 0xcb, 0x53, 0x67, - 0x91, 0x23, 0xe4, 0xd5, 0xc8, 0x24, 0x2c, 0x2f, 0x95, 0x7a, 0xfa, 0x12, 0x66, 0x50, 0x77, 0x6f, 0x14, 0x80, 0x6b, - 0x91, 0x4b, 0xa7, 0xda, 0x12, 0xae, 0x4c, 0xb9, 0xc1, 0x3e, 0x0f, 0x79, 0x4e, 0x42, 0xa8, 0x44, 0x1e, 0x29, 0xac, - 0xfb, 0xf6, 0xc5, 0xf3, 0x89, 0xeb, 0xc3, 0x62, 0xa3, 0x11, 0x1c, 0x0e, 0x00, 0x73, 0x30, 0xf5, 0xa2, 0x01, 0x2f, - 0xd5, 0x9c, 0xf9, 0xe8, 0xe5, 0x84, 0x8d, 0x01, 0x6a, 0x8a, 0x81, 0x53, 0xd6, 0x33, 0xf9, 0xc8, 0xf8, 0x96, 0xf9, - 0x7e, 0x80, 0xef, 0xd6, 0x85, 0x84, 0x7c, 0x50, 0xa8, 0x04, 0x99, 0x42, 0x25, 0x48, 0x0c, 0x2a, 0x41, 0x6c, 0x50, - 0x09, 0x36, 0x0d, 0x5f, 0x4b, 0xe5, 0x6d, 0x04, 0x1c, 0x11, 0x3e, 0xf4, 0x2c, 0x6c, 0xac, 0x50, 0x3c, 0x1b, 0xb3, - 0x31, 0x2b, 0xd4, 0xce, 0x93, 0xcb, 0xa9, 0xd8, 0x59, 0x8c, 0x75, 0x13, 0x59, 0x26, 0x5e, 0x48, 0xd0, 0x71, 0xce, - 0x85, 0x44, 0x5d, 0xfd, 0xdc, 0x7b, 0x49, 0xc6, 0x92, 0x79, 0x43, 0xa3, 0x06, 0xf3, 0xb2, 0xeb, 0x00, 0xa6, 0x25, - 0xdf, 0x16, 0x34, 0x98, 0x4e, 0x95, 0x47, 0xa4, 0x49, 0x50, 0x3b, 0x97, 0x49, 0x91, 0x13, 0xc2, 0x24, 0xe8, 0x95, - 0xe0, 0x37, 0x12, 0xda, 0xff, 0xab, 0x9e, 0xef, 0x80, 0xc1, 0x44, 0xab, 0xe4, 0x0b, 0x58, 0x2d, 0x73, 0xfe, 0x52, - 0x7a, 0x62, 0x23, 0xfe, 0x62, 0x99, 0xc6, 0xa3, 0x2f, 0x6c, 0x88, 0x78, 0x56, 0x2f, 0xd1, 0xb4, 0x04, 0x75, 0x80, - 0x47, 0xf4, 0xd7, 0xe8, 0x8b, 0xe1, 0x4d, 0xe9, 0x6a, 0xa4, 0xae, 0xd9, 0x39, 0xe7, 0x5f, 0x6a, 0x43, 0x84, 0x8c, - 0x69, 0x53, 0x20, 0x19, 0x10, 0x48, 0x32, 0x10, 0x00, 0x98, 0x9a, 0xce, 0xec, 0x15, 0x40, 0x34, 0x10, 0xc0, 0xe3, - 0xbc, 0xe3, 0xf1, 0x23, 0xfd, 0x55, 0x1c, 0xf7, 0x4e, 0xd3, 0xb0, 0xfd, 0x17, 0xa0, 0x29, 0x86, 0x72, 0x3c, 0xdf, - 0x29, 0x48, 0xf6, 0x28, 0x65, 0xe9, 0xaa, 0x89, 0xec, 0x50, 0xac, 0x4f, 0x73, 0xca, 0x42, 0xda, 0x96, 0x63, 0xb4, - 0xc5, 0xfa, 0x31, 0xf2, 0xde, 0xdc, 0xa8, 0xc8, 0x07, 0x3d, 0xb8, 0xbd, 0xbd, 0x7d, 0xdd, 0x63, 0x36, 0xc9, 0x8a, - 0x45, 0xae, 0x22, 0x4e, 0x9c, 0xd6, 0x21, 0x07, 0x0c, 0xc8, 0x49, 0x08, 0x4c, 0x63, 0x5c, 0x2a, 0xd0, 0x41, 0xc9, - 0x72, 0x5e, 0x03, 0xb5, 0x2c, 0x22, 0x6b, 0x80, 0xa8, 0xa6, 0xf9, 0x57, 0x0d, 0xf9, 0x49, 0xd5, 0x9c, 0x52, 0xa8, - 0x7d, 0xc5, 0xc3, 0xea, 0xf4, 0x89, 0x55, 0x9b, 0x18, 0xeb, 0x5f, 0x6b, 0x4f, 0xd0, 0x56, 0xd2, 0x40, 0x7c, 0xe7, - 0xeb, 0xf4, 0x8e, 0x42, 0x77, 0x9c, 0x99, 0x78, 0xaa, 0x02, 0x63, 0xdf, 0xda, 0x11, 0x14, 0x0e, 0x4d, 0xd7, 0x01, - 0x87, 0x69, 0x74, 0xc2, 0xe2, 0x9f, 0xd2, 0x71, 0xf2, 0xa2, 0x56, 0x88, 0x24, 0xff, 0x10, 0x2e, 0x0c, 0x89, 0x05, - 0x79, 0x49, 0xa8, 0x23, 0x32, 0x62, 0x35, 0x2a, 0xd6, 0x42, 0x45, 0xc5, 0x29, 0x1e, 0x6f, 0x15, 0x14, 0x97, 0xa2, - 0x54, 0x29, 0x15, 0xb9, 0x51, 0x29, 0x20, 0x96, 0x0d, 0xbc, 0x5b, 0xc0, 0x01, 0x10, 0x74, 0x96, 0xbb, 0xb5, 0xed, - 0x6e, 0x23, 0xf3, 0x99, 0x69, 0x9e, 0x56, 0x1f, 0xd4, 0xdf, 0xef, 0x97, 0x18, 0x5b, 0xe3, 0xe9, 0xef, 0xdb, 0xb4, - 0xe0, 0xe6, 0x6f, 0x18, 0xa2, 0x3b, 0x40, 0xc4, 0x2c, 0xed, 0xa1, 0x90, 0x05, 0x13, 0x96, 0xa1, 0x2a, 0x4f, 0x39, - 0xea, 0xe5, 0x93, 0x5b, 0x80, 0x50, 0x43, 0xbf, 0x36, 0x3a, 0xd5, 0x55, 0x09, 0xc2, 0xf7, 0x5d, 0xa1, 0x1e, 0x9b, - 0x03, 0x9e, 0x0c, 0x80, 0xbf, 0x22, 0xaf, 0xf5, 0xd8, 0xfe, 0x41, 0x6f, 0xd4, 0x1b, 0x20, 0x88, 0xce, 0x79, 0xe1, - 0x1f, 0x71, 0xae, 0x53, 0x7f, 0xc6, 0x85, 0x20, 0xbe, 0xf5, 0x24, 0xbc, 0x17, 0x67, 0x69, 0x1c, 0x9c, 0xf5, 0x06, - 0xe6, 0x22, 0x50, 0x9c, 0xa5, 0xf9, 0x19, 0x88, 0xe5, 0x88, 0x89, 0x58, 0xb3, 0x3b, 0x80, 0x09, 0x2c, 0x75, 0x1c, - 0xb2, 0xea, 0xd8, 0x7e, 0xff, 0xcd, 0xc8, 0x90, 0xa5, 0x23, 0x0c, 0x8c, 0xfe, 0x5d, 0x81, 0x00, 0x05, 0xcb, 0xcc, - 0xf6, 0x60, 0xd2, 0xd5, 0x9e, 0xd5, 0xf3, 0x66, 0x93, 0x77, 0xf5, 0x8e, 0xd5, 0xb4, 0x9c, 0x9a, 0x56, 0x59, 0x4d, - 0x9b, 0xe4, 0x50, 0x33, 0xd1, 0xef, 0x6b, 0x50, 0xd4, 0x7c, 0x0e, 0x60, 0x6c, 0x98, 0xfc, 0x66, 0x56, 0xcd, 0xfb, - 0x7d, 0x4f, 0x3e, 0x82, 0x5f, 0xc8, 0x56, 0xe6, 0xd6, 0x58, 0x3e, 0x7d, 0x4d, 0x24, 0x66, 0x06, 0xe6, 0xe8, 0xee, - 0x08, 0xdf, 0xeb, 0x46, 0x78, 0x1d, 0x73, 0x85, 0xcd, 0xc4, 0xf4, 0x0d, 0x0c, 0x9e, 0x27, 0x7c, 0x70, 0x91, 0xa3, - 0xbf, 0x91, 0xc3, 0x4c, 0x61, 0x41, 0xce, 0xfd, 0xc9, 0x1b, 0xc4, 0x4b, 0x46, 0x78, 0x07, 0x9d, 0x4e, 0x78, 0x90, - 0xfd, 0xfe, 0x0a, 0x3a, 0xb3, 0x95, 0x4a, 0xd9, 0xaa, 0xa8, 0x4c, 0xd7, 0x75, 0x51, 0x56, 0xd0, 0xb1, 0xf4, 0xf3, - 0x56, 0xc8, 0xcc, 0xfa, 0x99, 0x05, 0xf7, 0xb4, 0x92, 0x00, 0x53, 0xb6, 0x6d, 0xa2, 0x36, 0xf0, 0xb2, 0x2e, 0x3e, - 0x17, 0x78, 0x74, 0xd6, 0x5e, 0x6f, 0x84, 0xda, 0xe7, 0x7c, 0xb4, 0x2e, 0xd6, 0x1e, 0xf8, 0xc1, 0xcc, 0xd2, 0xb9, - 0x22, 0xce, 0xc8, 0xfd, 0xd1, 0xe7, 0x22, 0xcd, 0x29, 0x0f, 0x70, 0x1f, 0x8a, 0xb9, 0xfd, 0x16, 0x48, 0x3f, 0xf4, - 0x16, 0xc8, 0x3e, 0x3a, 0xe7, 0xe4, 0x0d, 0x20, 0xd2, 0x21, 0x0c, 0x6e, 0x45, 0x82, 0x8e, 0x55, 0xc3, 0x5b, 0x0b, - 0xec, 0xb4, 0x97, 0xc6, 0xbd, 0x34, 0x3f, 0x4b, 0xfb, 0x7d, 0x83, 0x9a, 0x99, 0x22, 0x1c, 0x3c, 0xce, 0xc8, 0x45, - 0xd2, 0x82, 0x2d, 0xa5, 0xfd, 0x57, 0x03, 0x47, 0x10, 0xf2, 0xf7, 0x3f, 0x84, 0xf7, 0x04, 0x20, 0x36, 0x69, 0x03, - 0xae, 0x7a, 0x4c, 0x47, 0x63, 0x4b, 0xa2, 0x56, 0x9d, 0x0d, 0x90, 0x38, 0x55, 0x5a, 0x4f, 0xb9, 0x59, 0x53, 0x18, - 0xa4, 0xca, 0x42, 0xfd, 0xc6, 0x7a, 0x32, 0x59, 0xe5, 0x22, 0x23, 0x8e, 0xca, 0xf4, 0xa5, 0x66, 0x04, 0xd3, 0xa5, - 0x9f, 0x2f, 0x60, 0xc9, 0xc6, 0x1f, 0x71, 0xf2, 0x96, 0x80, 0x63, 0x3b, 0x6b, 0x57, 0xd5, 0x2e, 0xc7, 0xad, 0xdd, - 0x1c, 0xe0, 0x7b, 0xbd, 0xd1, 0x68, 0xa4, 0x9d, 0xe3, 0x04, 0x0c, 0x55, 0x4f, 0x2d, 0x85, 0x1e, 0xab, 0x15, 0xa0, - 0x6e, 0x47, 0x2e, 0xb3, 0x64, 0x30, 0x5f, 0x18, 0xc7, 0xaf, 0xcc, 0x47, 0x1f, 0x2f, 0x95, 0xb5, 0xeb, 0x88, 0xaf, - 0xff, 0x20, 0xab, 0xf5, 0x2d, 0xef, 0xaa, 0x26, 0xe0, 0x8b, 0x2a, 0xa0, 0xf4, 0x1b, 0xde, 0x93, 0xbd, 0x8b, 0xaf, - 0xdd, 0x60, 0x97, 0x7c, 0xcb, 0x5b, 0xd4, 0x79, 0xbe, 0x72, 0x70, 0xa3, 0x4a, 0xb7, 0xf7, 0x92, 0x05, 0xae, 0xbd, - 0xa3, 0xa6, 0xb1, 0x9e, 0xf9, 0xd1, 0xc3, 0x22, 0x64, 0x3b, 0x1f, 0x7b, 0x5f, 0x35, 0x4f, 0xcf, 0x1a, 0x7a, 0x93, - 0x1a, 0xfa, 0xd8, 0x8b, 0xb2, 0x7d, 0x6a, 0x1a, 0xd1, 0x6b, 0xd8, 0xd0, 0xc7, 0xde, 0x92, 0x93, 0x43, 0x22, 0xc0, - 0xa9, 0x31, 0x7f, 0x7c, 0x38, 0x9d, 0xe1, 0xef, 0x18, 0x50, 0x09, 0xc4, 0x7c, 0x7a, 0x4c, 0x3b, 0x0a, 0x30, 0xa3, - 0x4a, 0x6f, 0x9f, 0x1e, 0xd8, 0x8e, 0x97, 0xf5, 0xd0, 0xd2, 0xbb, 0x27, 0x47, 0xb7, 0xe3, 0x55, 0x35, 0xbe, 0x94, - 0x43, 0x9e, 0xe7, 0xb3, 0xd1, 0x68, 0x24, 0x0c, 0x24, 0x77, 0xa5, 0x37, 0xb0, 0x02, 0x69, 0x5b, 0x54, 0x1f, 0xca, - 0xa5, 0xb7, 0x53, 0x87, 0x76, 0xe5, 0x4f, 0xf2, 0xc3, 0xa1, 0x18, 0x99, 0x63, 0x1c, 0xc0, 0x4d, 0x0a, 0x25, 0x47, - 0xc9, 0x5a, 0x82, 0xe8, 0x94, 0xc6, 0x53, 0x59, 0xaf, 0xad, 0x88, 0xbc, 0x1a, 0x71, 0x1e, 0x82, 0x1f, 0x3d, 0x50, - 0x8b, 0xbf, 0xd0, 0x82, 0xd8, 0x63, 0x9f, 0x2a, 0xa5, 0x17, 0xbc, 0x2a, 0x20, 0x44, 0xec, 0xef, 0x06, 0xda, 0x41, - 0x09, 0x0e, 0x25, 0xdc, 0x07, 0x84, 0x85, 0x7e, 0xed, 0xe5, 0x33, 0x19, 0xa3, 0xdc, 0x1b, 0x54, 0x73, 0x06, 0x30, - 0x95, 0x3e, 0x03, 0xbf, 0x4b, 0x80, 0x3a, 0xc5, 0xa7, 0xe8, 0x54, 0x6f, 0x1e, 0x36, 0x5d, 0x9f, 0x96, 0x28, 0x8a, - 0xe8, 0xce, 0xcf, 0xc7, 0x80, 0xd8, 0xd9, 0xb5, 0x19, 0x69, 0xd7, 0x7e, 0x83, 0x06, 0x2b, 0x25, 0x89, 0x76, 0x4e, - 0x09, 0xbb, 0x9d, 0x8f, 0x6c, 0xe9, 0x47, 0x29, 0x10, 0x73, 0xc7, 0x89, 0x44, 0xf6, 0x60, 0x23, 0x27, 0x70, 0x8b, - 0xf6, 0x8e, 0x0e, 0x40, 0xe5, 0x46, 0x41, 0x7e, 0x35, 0x47, 0x72, 0xc7, 0x77, 0xbd, 0xef, 0x06, 0xf5, 0xe0, 0xbb, - 0xde, 0x59, 0x4a, 0x72, 0x47, 0x78, 0xa6, 0xa6, 0x84, 0x88, 0xcf, 0xbe, 0x1b, 0xe4, 0x03, 0x3c, 0x4b, 0xb4, 0x48, - 0x8b, 0x84, 0x6a, 0x75, 0x8d, 0x9b, 0xf0, 0x22, 0x91, 0xdc, 0x43, 0xbb, 0xce, 0x23, 0x62, 0x01, 0xc8, 0x58, 0x7c, - 0x36, 0x6f, 0x28, 0xd4, 0xdd, 0xc4, 0x6c, 0xd1, 0x5d, 0x16, 0xfb, 0xfd, 0x6d, 0x9e, 0xd6, 0x3d, 0x1d, 0x1f, 0x83, - 0x2f, 0x48, 0x35, 0x01, 0x1e, 0xed, 0xaf, 0xcd, 0xf1, 0xea, 0xd5, 0xe6, 0x48, 0x59, 0xa8, 0x12, 0xf5, 0x5b, 0xac, - 0x66, 0x3d, 0x84, 0xe1, 0xce, 0x32, 0x63, 0x6d, 0x2f, 0x78, 0x25, 0x67, 0x55, 0x6c, 0x97, 0xe3, 0x2b, 0x96, 0xda, - 0x4a, 0xa2, 0x72, 0xb4, 0x1e, 0x6b, 0x53, 0x8c, 0xfc, 0x4a, 0x21, 0x51, 0x16, 0x1d, 0x5b, 0x0b, 0x05, 0xc4, 0x0b, - 0xd0, 0x97, 0xec, 0x4c, 0x03, 0xac, 0x37, 0x7a, 0x15, 0x11, 0x5a, 0x3e, 0x52, 0xe1, 0x4d, 0x6e, 0xaa, 0xcc, 0xca, - 0x66, 0xd1, 0xee, 0xa7, 0x8a, 0x57, 0x08, 0x56, 0x6f, 0xd4, 0x1e, 0x05, 0xa8, 0x3d, 0xb4, 0x50, 0x06, 0x90, 0xd2, - 0x34, 0x03, 0x40, 0x06, 0x00, 0x99, 0x2a, 0xe2, 0x33, 0x01, 0x2a, 0x6d, 0x75, 0xa3, 0xc0, 0x89, 0xf4, 0x1a, 0x68, - 0x16, 0x58, 0xe9, 0x23, 0x05, 0x19, 0x2c, 0xb6, 0x08, 0xc0, 0xca, 0x91, 0x33, 0x4c, 0x63, 0xc8, 0x36, 0x9a, 0xb8, - 0x24, 0xcd, 0xef, 0xc3, 0x2c, 0x95, 0x78, 0x12, 0x3f, 0xc8, 0x1a, 0x23, 0x00, 0x90, 0xbe, 0x4f, 0x2f, 0x8a, 0x2c, - 0x26, 0x1c, 0x38, 0xeb, 0xa9, 0x83, 0xa2, 0x26, 0xe7, 0x5a, 0xd3, 0xea, 0x59, 0x6d, 0xf2, 0x90, 0x05, 0x3a, 0x7b, - 0x30, 0x26, 0xb5, 0x7c, 0xcf, 0x23, 0xfb, 0x2b, 0xc7, 0x33, 0xc2, 0x77, 0xdd, 0xc1, 0xa9, 0xff, 0x6e, 0x6a, 0x60, - 0x62, 0x4a, 0x00, 0x36, 0x06, 0x47, 0x13, 0xe2, 0x77, 0x3a, 0x26, 0x53, 0x9b, 0x14, 0x81, 0xc0, 0x43, 0xf0, 0x0a, - 0x9e, 0x1b, 0x2e, 0xb7, 0xdc, 0xd8, 0x59, 0xe4, 0x69, 0x02, 0x70, 0xe2, 0x05, 0xdf, 0x02, 0x1c, 0xa7, 0x5e, 0x15, - 0xb2, 0x67, 0xcf, 0xc5, 0x74, 0x36, 0x0f, 0x1e, 0x12, 0xda, 0xbf, 0x98, 0xf0, 0x9b, 0xee, 0x2a, 0xb9, 0x32, 0xb5, - 0xee, 0x4d, 0x74, 0x95, 0xcb, 0x9d, 0x3e, 0xad, 0x38, 0x86, 0x39, 0x83, 0x55, 0x40, 0xce, 0xd9, 0x90, 0xbf, 0x38, - 0x07, 0xc0, 0x96, 0x95, 0xf0, 0x22, 0xfe, 0x22, 0x94, 0xd5, 0x02, 0xb8, 0x47, 0xce, 0x23, 0xf3, 0xcb, 0x57, 0xdb, - 0xa1, 0x9c, 0x53, 0x14, 0xc6, 0x72, 0x6a, 0x5a, 0x52, 0x9c, 0x0e, 0x3d, 0x05, 0x93, 0xa9, 0x2d, 0x7f, 0x6f, 0x13, - 0x97, 0xd9, 0x9b, 0x49, 0x38, 0x5f, 0x47, 0xb6, 0xad, 0x55, 0xf7, 0xd0, 0x0d, 0xc1, 0xa0, 0x8f, 0x11, 0xb4, 0x6c, - 0xae, 0xef, 0xd6, 0x83, 0x81, 0xc2, 0xf6, 0xad, 0xe9, 0xa6, 0x45, 0xa7, 0x38, 0xe0, 0xcc, 0x5a, 0xd7, 0xa8, 0x54, - 0x15, 0x87, 0x5e, 0xf2, 0x6e, 0x59, 0x95, 0x5d, 0x96, 0x5e, 0x08, 0x52, 0xa3, 0xae, 0x22, 0x44, 0x4a, 0xc5, 0x0e, - 0xef, 0xc9, 0xaf, 0x81, 0x89, 0x67, 0x56, 0x8e, 0xd2, 0x78, 0x0e, 0x30, 0x41, 0x0a, 0x7d, 0x53, 0x7e, 0x05, 0xb8, - 0xa1, 0x8b, 0x28, 0xcc, 0xde, 0xc6, 0x55, 0x50, 0x5b, 0x4d, 0xbf, 0x77, 0x70, 0x62, 0xcf, 0xeb, 0x7e, 0x3f, 0x25, - 0x1a, 0x3f, 0x0c, 0xbd, 0xc0, 0xbf, 0xc7, 0xd3, 0x7d, 0x13, 0xa4, 0xe6, 0x95, 0x07, 0x78, 0x45, 0x97, 0x5b, 0x9b, - 0x72, 0x45, 0xe3, 0x62, 0x5e, 0x23, 0x22, 0x7c, 0xea, 0x28, 0xb6, 0xdb, 0xfc, 0x38, 0xb5, 0x31, 0x18, 0x84, 0x70, - 0xdf, 0xca, 0xf8, 0x7d, 0xe2, 0xe5, 0xb3, 0x68, 0x0e, 0x8a, 0xd2, 0x4c, 0x93, 0x84, 0x14, 0xd2, 0x4b, 0x80, 0x3e, - 0x1a, 0x84, 0x5a, 0x5d, 0xf9, 0x47, 0xe2, 0xa5, 0x6a, 0x5a, 0x9b, 0xa7, 0x58, 0xa3, 0x40, 0xcc, 0xa2, 0x79, 0xc3, - 0x32, 0x3a, 0x24, 0xd5, 0xe5, 0xd2, 0x34, 0xe3, 0x0f, 0xab, 0x19, 0xaa, 0x15, 0x47, 0x4d, 0x50, 0xa3, 0x74, 0x03, - 0x17, 0xc0, 0xbf, 0xd3, 0x1d, 0x47, 0x35, 0x8a, 0x14, 0x0d, 0xf8, 0x04, 0x81, 0x61, 0xcd, 0xe6, 0x09, 0x6b, 0x4d, - 0x5d, 0x33, 0xfa, 0x7d, 0x19, 0x27, 0x64, 0x92, 0x90, 0x9c, 0x0f, 0x97, 0xeb, 0x47, 0x52, 0x5d, 0x00, 0xa9, 0x72, - 0xc5, 0x66, 0xbd, 0xde, 0x1c, 0x30, 0x7a, 0x61, 0xfd, 0xc2, 0xc6, 0x15, 0x9c, 0x5f, 0x12, 0xe6, 0xae, 0xfa, 0x11, - 0x66, 0x19, 0x54, 0x01, 0x69, 0x7e, 0x2c, 0x78, 0xf3, 0xdc, 0x05, 0xa2, 0x7e, 0x33, 0x52, 0x17, 0x94, 0x59, 0x3a, - 0xb7, 0x88, 0x40, 0xc0, 0x6b, 0x58, 0x3d, 0x81, 0x64, 0x5f, 0x3e, 0xf6, 0x69, 0x46, 0x81, 0xea, 0x08, 0x40, 0xd9, - 0xac, 0x1f, 0xc2, 0xfe, 0x01, 0xe1, 0x84, 0xfa, 0x9b, 0x37, 0x72, 0xd6, 0x90, 0x3c, 0x90, 0x6a, 0xc2, 0x63, 0x38, - 0x35, 0x16, 0xf8, 0xd2, 0xa2, 0x37, 0x15, 0xbc, 0x26, 0x38, 0xee, 0x05, 0x5a, 0xfb, 0x16, 0x70, 0x84, 0x08, 0x2e, - 0x43, 0x13, 0xa7, 0xbd, 0x5d, 0x2f, 0x40, 0x42, 0x73, 0x0b, 0xe7, 0xfa, 0xda, 0x05, 0x2d, 0x4e, 0x91, 0x93, 0x45, - 0x17, 0x18, 0xe8, 0x82, 0xcc, 0x1b, 0xff, 0xaa, 0x60, 0xe5, 0x02, 0x64, 0x2f, 0x15, 0x2b, 0x89, 0xd8, 0x76, 0xea, - 0x8f, 0x52, 0xd9, 0x6f, 0xcf, 0xac, 0x09, 0xfc, 0x2a, 0xb1, 0x5f, 0x22, 0x93, 0x6f, 0x7a, 0x6c, 0xf2, 0x95, 0xb1, - 0xd0, 0xa9, 0x65, 0x70, 0x4e, 0x8f, 0x0c, 0xce, 0xbd, 0x9d, 0x55, 0x9b, 0x10, 0x86, 0x82, 0x24, 0xd0, 0x74, 0xe9, - 0x61, 0xdd, 0xf4, 0xe7, 0x27, 0x2d, 0x7e, 0xad, 0xda, 0xb7, 0xee, 0xc7, 0x21, 0x76, 0xf1, 0xab, 0xc4, 0x33, 0xec, - 0xa3, 0x3e, 0x70, 0x80, 0xc9, 0x88, 0x89, 0xcb, 0x7e, 0x1f, 0x0a, 0x9b, 0x8d, 0xe7, 0xa3, 0xba, 0xf8, 0xb9, 0x78, - 0x00, 0x28, 0x87, 0x0a, 0xec, 0x72, 0x28, 0x43, 0x19, 0xb1, 0xa9, 0x2d, 0xf7, 0xfc, 0xfe, 0x2a, 0xcc, 0x41, 0xde, - 0xd1, 0x98, 0x38, 0x67, 0x20, 0x86, 0xc1, 0xd7, 0xbf, 0x7b, 0xb2, 0x4f, 0x9b, 0xef, 0xce, 0xe0, 0xbb, 0xa3, 0xb3, - 0x0f, 0xc8, 0x71, 0x73, 0xb6, 0x2e, 0x8b, 0xfb, 0x34, 0x16, 0x67, 0xdf, 0x41, 0xea, 0x77, 0x67, 0x45, 0x79, 0xf6, - 0x9d, 0xaa, 0xcc, 0x77, 0x67, 0xb4, 0xe0, 0x46, 0xbf, 0x5b, 0x13, 0xef, 0x9f, 0x95, 0xa6, 0x3d, 0x5b, 0x42, 0x38, - 0x96, 0x56, 0x3f, 0x82, 0x12, 0x51, 0x91, 0xa2, 0xca, 0x50, 0x56, 0x6b, 0xc7, 0x79, 0x9f, 0x68, 0x78, 0x6c, 0x9a, - 0x90, 0xb8, 0x5a, 0xc2, 0x3a, 0xd4, 0xb3, 0xd3, 0x26, 0xd9, 0x71, 0x1e, 0xa8, 0x03, 0x22, 0xe7, 0x2f, 0xf2, 0xd1, - 0x96, 0xbe, 0x06, 0xdf, 0x3a, 0x1c, 0xf2, 0xd1, 0xce, 0xfc, 0xf4, 0xc9, 0x5a, 0x29, 0x83, 0x8d, 0x14, 0xa3, 0x10, - 0x12, 0xc5, 0x6d, 0x7b, 0x0c, 0x80, 0xff, 0xfd, 0xc3, 0x81, 0x7e, 0xef, 0xe4, 0x6f, 0xb5, 0x5b, 0x5a, 0xf5, 0xfc, - 0xd0, 0x22, 0xcc, 0x78, 0x5d, 0x1b, 0x76, 0xb6, 0xbd, 0x04, 0x94, 0xde, 0x37, 0x0d, 0x6a, 0x8a, 0xe8, 0x27, 0xac, - 0x26, 0x56, 0x71, 0x58, 0x90, 0x12, 0x87, 0x18, 0x8e, 0xd1, 0x0e, 0x3d, 0x4e, 0x17, 0x35, 0x4f, 0xee, 0x3b, 0x64, - 0xdc, 0xfa, 0x3e, 0x20, 0xb9, 0x14, 0xce, 0x3f, 0x78, 0xa1, 0xc1, 0x44, 0x2f, 0xf2, 0xaa, 0xc8, 0xc4, 0x48, 0xd0, - 0x28, 0xbf, 0x25, 0x71, 0xe6, 0x0c, 0x6b, 0x71, 0xa6, 0x10, 0xc2, 0x42, 0x42, 0xe5, 0x2e, 0x4a, 0x4a, 0x0f, 0xce, - 0x9e, 0xec, 0xcb, 0xe6, 0x77, 0xc2, 0x84, 0x18, 0x2d, 0x80, 0x06, 0x67, 0xd7, 0x2e, 0xef, 0x21, 0x2c, 0x73, 0xef, - 0xf7, 0xb7, 0x77, 0x79, 0x01, 0x71, 0x99, 0x67, 0x52, 0xb1, 0x5a, 0x9e, 0x01, 0x4d, 0x9e, 0x88, 0xcf, 0xc2, 0x4a, - 0x4e, 0x83, 0xaa, 0xa3, 0x58, 0xbd, 0x8d, 0xe7, 0x1e, 0xf0, 0x7a, 0xbf, 0x4f, 0x80, 0xc0, 0xdd, 0x67, 0x6f, 0x94, - 0x5b, 0x2a, 0xe9, 0x91, 0xe7, 0x18, 0x22, 0x99, 0x00, 0xaf, 0x33, 0x04, 0x47, 0x0a, 0xab, 0xe7, 0x26, 0xc8, 0x3f, - 0xbe, 0x3e, 0xa1, 0xf8, 0xa2, 0x79, 0x14, 0x35, 0x2c, 0x64, 0x09, 0x1c, 0x0f, 0xc9, 0x2c, 0x9b, 0x23, 0x35, 0x79, - 0xda, 0x9e, 0x22, 0x1d, 0x9d, 0x58, 0xe2, 0xb7, 0x35, 0xa9, 0x5e, 0xa4, 0xc2, 0x2e, 0x69, 0x67, 0x2b, 0x73, 0x2f, - 0x84, 0xa1, 0x4a, 0xb8, 0xf7, 0xba, 0x9e, 0x85, 0x72, 0x53, 0xb4, 0x2a, 0x66, 0x0f, 0x53, 0x62, 0x86, 0x29, 0xd6, - 0x5f, 0xd8, 0xf0, 0x9b, 0xc4, 0x8b, 0xc1, 0x70, 0xbd, 0xe4, 0xe5, 0x6c, 0x63, 0x16, 0xc2, 0xe1, 0xb0, 0x99, 0x14, - 0xb3, 0x25, 0xc4, 0xb6, 0x2e, 0xe7, 0x87, 0x43, 0x57, 0xcb, 0xd6, 0xc2, 0x83, 0x87, 0xaa, 0x85, 0x9b, 0x86, 0xe5, - 0xf0, 0x33, 0x99, 0xc5, 0xd8, 0xbe, 0xc6, 0x67, 0xf6, 0xe7, 0x8b, 0xee, 0x59, 0x82, 0x8c, 0x1b, 0x6b, 0xe0, 0x1a, - 0x9b, 0xb5, 0x3b, 0x5c, 0x8d, 0x80, 0xe4, 0x71, 0x37, 0xfa, 0xbb, 0xb2, 0x93, 0x9c, 0x04, 0x09, 0xa3, 0x15, 0xc2, - 0xef, 0xbe, 0xf1, 0x27, 0x5a, 0xec, 0x41, 0xbb, 0x8d, 0x2d, 0x21, 0xaa, 0x69, 0xcf, 0xe5, 0x4a, 0xb1, 0x34, 0x6f, - 0xa5, 0x0d, 0x99, 0x0f, 0xeb, 0x73, 0xdf, 0xc8, 0x81, 0x82, 0x31, 0xe2, 0xa9, 0x75, 0x10, 0xcd, 0xe6, 0xc0, 0x7d, - 0x81, 0xe6, 0x11, 0x9e, 0x5a, 0x90, 0xa0, 0xcc, 0xda, 0xb0, 0x9f, 0x24, 0x27, 0xcb, 0xe3, 0xf0, 0x2d, 0xfc, 0xcb, - 0x67, 0xd8, 0x24, 0xa6, 0x28, 0x1e, 0x7f, 0xab, 0x14, 0xff, 0x1d, 0x5b, 0x10, 0xc1, 0xda, 0x8d, 0xa8, 0x0d, 0x7f, - 0xc3, 0xbf, 0x85, 0x7d, 0x84, 0xfd, 0x86, 0x26, 0x08, 0x03, 0x58, 0x7f, 0x26, 0x10, 0x17, 0x16, 0x82, 0x04, 0x7f, - 0xab, 0x24, 0xff, 0x9c, 0xf0, 0xd9, 0xa2, 0x04, 0xb2, 0x3a, 0x8c, 0xe2, 0x13, 0x8a, 0x89, 0x42, 0x18, 0x6e, 0x09, - 0xbd, 0xa3, 0xff, 0x46, 0x94, 0x64, 0x93, 0xdc, 0x8a, 0xf5, 0x40, 0x26, 0x49, 0x30, 0xc1, 0xca, 0x0b, 0xe5, 0x4b, - 0xf7, 0x42, 0xa9, 0xb5, 0x16, 0xb4, 0x7e, 0xf9, 0x93, 0xc4, 0x33, 0xa0, 0x7b, 0x20, 0x63, 0xd0, 0x6d, 0x44, 0x35, - 0xc9, 0x31, 0x7d, 0x94, 0xce, 0x33, 0x50, 0x01, 0x9d, 0xad, 0xb3, 0xb0, 0x5e, 0x16, 0xe5, 0xaa, 0x15, 0x1e, 0x2a, - 0x4b, 0x1f, 0xa9, 0xc7, 0x98, 0x17, 0xe6, 0xc9, 0x89, 0x7c, 0xf0, 0x08, 0xd0, 0xf0, 0x28, 0x4f, 0xab, 0x8e, 0xd2, - 0xfa, 0x81, 0x65, 0xc0, 0x08, 0x9c, 0x28, 0x03, 0x1e, 0x61, 0x19, 0x98, 0xa7, 0x5d, 0x86, 0x1a, 0xc4, 0x1a, 0x55, - 0x57, 0x6a, 0x83, 0x39, 0x51, 0x94, 0x7c, 0x8a, 0xa5, 0x15, 0xc6, 0xd0, 0xd4, 0x95, 0x47, 0xd6, 0x4b, 0x4e, 0xd8, - 0x93, 0xdd, 0x40, 0xba, 0x85, 0x8d, 0x02, 0x17, 0x74, 0x2d, 0x4b, 0x94, 0x8b, 0x6e, 0x19, 0x51, 0x26, 0x42, 0xea, - 0x67, 0x0f, 0x67, 0x5a, 0xed, 0x37, 0x76, 0xd2, 0xbe, 0x3d, 0x52, 0xf4, 0x82, 0x81, 0xf8, 0xb4, 0x47, 0x4a, 0x3d, - 0x6b, 0xe4, 0x32, 0xb0, 0xa5, 0x4b, 0x55, 0xcf, 0x7f, 0x83, 0xf2, 0x1d, 0xcc, 0x8c, 0xb3, 0xd9, 0xef, 0x7a, 0x73, - 0x7b, 0xb2, 0xaf, 0x9b, 0xdf, 0x59, 0xaf, 0x07, 0x5b, 0x83, 0x4c, 0x7c, 0xa9, 0xa8, 0xa7, 0xac, 0x42, 0xac, 0xc8, - 0xec, 0x7f, 0x0b, 0xef, 0x77, 0x78, 0x6b, 0x84, 0x66, 0x65, 0x3c, 0xcc, 0x47, 0x4f, 0xf6, 0xa2, 0xf9, 0xbd, 0xb3, - 0x6c, 0x2b, 0x57, 0x25, 0xb3, 0xfd, 0x7e, 0x94, 0x34, 0x67, 0x8f, 0xd7, 0x48, 0xea, 0x00, 0x1f, 0xaf, 0xcf, 0xf0, - 0x91, 0x4a, 0x28, 0xb5, 0xa0, 0xaa, 0x41, 0xeb, 0x63, 0xbf, 0xb7, 0x9e, 0xd3, 0xc7, 0x8f, 0xe5, 0x74, 0x4b, 0x8a, - 0x30, 0x7e, 0x60, 0x30, 0x65, 0x27, 0x4e, 0x5d, 0xf2, 0x66, 0x48, 0xef, 0xba, 0x55, 0x52, 0x97, 0x3d, 0x4a, 0x04, - 0xa1, 0x0e, 0xd6, 0x2f, 0xf6, 0x43, 0x98, 0xd9, 0xa2, 0x3f, 0x6c, 0x56, 0x73, 0x42, 0x41, 0x04, 0x88, 0x56, 0x79, - 0x1f, 0x38, 0x26, 0x09, 0xb3, 0xe6, 0x86, 0x74, 0xeb, 0xcd, 0x95, 0xf6, 0x4a, 0x0a, 0xe8, 0xe7, 0x20, 0x73, 0xfb, - 0xe8, 0x96, 0xab, 0x96, 0x79, 0x2e, 0x6d, 0x39, 0x60, 0xd1, 0x42, 0x74, 0x66, 0xe7, 0xd2, 0xe1, 0xe0, 0x3f, 0xa8, - 0x2b, 0x51, 0x45, 0x04, 0x1d, 0x45, 0x0b, 0x46, 0xab, 0x55, 0xbb, 0x9c, 0x6c, 0x2a, 0x64, 0x4b, 0x22, 0x9c, 0x28, - 0xd9, 0x2b, 0xa1, 0x3e, 0xca, 0xd5, 0x9e, 0x69, 0x88, 0x3f, 0x13, 0xb0, 0x69, 0x83, 0xbf, 0x05, 0xee, 0x65, 0x70, - 0x66, 0xda, 0xa7, 0x61, 0x04, 0x44, 0xe6, 0x10, 0xec, 0xe7, 0x77, 0x3d, 0xa8, 0xe0, 0x41, 0x47, 0xfa, 0xeb, 0x7a, - 0x56, 0xe0, 0x99, 0x7b, 0xe2, 0xf9, 0x9b, 0x13, 0xe9, 0x45, 0x0e, 0x0f, 0x34, 0xf7, 0x61, 0xc6, 0x5f, 0x96, 0x65, - 0xb8, 0x1b, 0x2d, 0xcb, 0x62, 0xe5, 0x45, 0x7a, 0x1f, 0xcf, 0xa4, 0x18, 0x48, 0x74, 0x98, 0x19, 0x5d, 0xc5, 0x3a, - 0xce, 0x61, 0xdc, 0xdb, 0x93, 0xb0, 0x42, 0xfb, 0x67, 0x89, 0xbd, 0x2e, 0x00, 0xc0, 0x21, 0x6b, 0xd0, 0x0a, 0xef, - 0x74, 0x7b, 0xbb, 0xc7, 0x25, 0x25, 0x8a, 0x1b, 0x35, 0x3f, 0xab, 0xa1, 0x65, 0x82, 0x5a, 0x66, 0xdd, 0xc9, 0x64, - 0x8a, 0x24, 0xf0, 0x6d, 0xd8, 0x1b, 0x56, 0xe4, 0xf3, 0x46, 0x6e, 0x0f, 0xef, 0xc2, 0x95, 0x88, 0xb5, 0x05, 0x9d, - 0x74, 0x64, 0x1c, 0xee, 0x85, 0xe6, 0x46, 0xba, 0x7f, 0x52, 0x25, 0x61, 0x29, 0x62, 0xb8, 0x05, 0xb2, 0xbd, 0xda, - 0x56, 0x82, 0x12, 0x48, 0x60, 0x3f, 0x94, 0x62, 0x99, 0x6e, 0x05, 0x80, 0x39, 0xf0, 0x3f, 0x25, 0x0c, 0xa1, 0xbb, - 0xf3, 0x10, 0xaf, 0x1a, 0x79, 0xdf, 0x20, 0x04, 0xfb, 0x6b, 0x90, 0xd3, 0x80, 0x41, 0xa4, 0x18, 0xc9, 0x82, 0x81, - 0x04, 0x20, 0xe7, 0x6b, 0x30, 0xc9, 0x4d, 0x73, 0xcf, 0x0f, 0x72, 0xdd, 0xc1, 0xb4, 0x0f, 0xba, 0x17, 0xd7, 0x9a, - 0xe5, 0xe0, 0x15, 0x13, 0xf1, 0xbf, 0xd7, 0x5e, 0xc9, 0x72, 0x96, 0xf9, 0x8d, 0xb9, 0xe8, 0x64, 0x70, 0xd5, 0x10, - 0x7e, 0x31, 0xcb, 0xe6, 0x3c, 0x9a, 0x65, 0x3a, 0xd4, 0xbf, 0x68, 0x8e, 0x4a, 0x01, 0x0c, 0x75, 0xbc, 0x00, 0x6b, - 0xbc, 0x2b, 0xdd, 0xb4, 0xe2, 0x91, 0xc6, 0x18, 0x05, 0x15, 0x3a, 0x08, 0xfd, 0xbd, 0x06, 0x78, 0x0d, 0x26, 0xb9, - 0x11, 0x2a, 0x1f, 0x5c, 0xd0, 0x0d, 0xdd, 0x72, 0xe5, 0x12, 0xd4, 0xa4, 0x6a, 0xf9, 0xe5, 0x08, 0xf5, 0xae, 0x96, - 0x5c, 0xaa, 0xcd, 0xa7, 0x46, 0x59, 0x23, 0xc8, 0xe4, 0x28, 0xfd, 0x3e, 0xe5, 0xc2, 0xad, 0x8c, 0xc9, 0xfa, 0x70, - 0xf0, 0x0a, 0x6e, 0x6a, 0xfc, 0x3a, 0x27, 0x16, 0x51, 0x7b, 0x48, 0x84, 0xad, 0xdd, 0x0a, 0xdd, 0x7b, 0xdc, 0x28, - 0xcd, 0xa3, 0x6c, 0x13, 0x8b, 0xca, 0xeb, 0x25, 0x60, 0x2d, 0xee, 0x01, 0x19, 0x2a, 0x2d, 0xfd, 0x8a, 0x15, 0x00, - 0x19, 0x20, 0x85, 0x8d, 0x1f, 0x90, 0xf6, 0xea, 0x83, 0x97, 0xfa, 0xfd, 0xbe, 0x31, 0xe5, 0xbf, 0x7f, 0xc8, 0x81, - 0x99, 0x50, 0x94, 0xf5, 0x0e, 0x26, 0x10, 0x5c, 0x3b, 0x49, 0x7b, 0x56, 0xf3, 0x17, 0xeb, 0xda, 0x03, 0x52, 0x2b, - 0xdf, 0x62, 0xae, 0x7a, 0x6d, 0x5f, 0x6c, 0xf6, 0x69, 0x75, 0x63, 0x34, 0x0e, 0x82, 0xa5, 0xd5, 0x5b, 0xad, 0x72, - 0xc8, 0x1b, 0x5e, 0x81, 0x48, 0x65, 0x5d, 0x5d, 0x2b, 0xe7, 0xea, 0x5a, 0x70, 0x24, 0x90, 0x2d, 0x79, 0x0e, 0xff, - 0x85, 0xdc, 0x2b, 0x0f, 0x87, 0xc2, 0xef, 0xf7, 0xd3, 0x19, 0x69, 0x65, 0x81, 0x32, 0x6d, 0x5d, 0x7b, 0xa1, 0x7f, - 0x38, 0xfc, 0x00, 0x5e, 0x23, 0xfe, 0xe1, 0x50, 0xf6, 0xfb, 0x1f, 0xcd, 0x4d, 0xe6, 0x7c, 0xac, 0x94, 0xb2, 0x97, - 0xa8, 0x74, 0x7f, 0x9b, 0xf0, 0xde, 0xff, 0x1e, 0xfd, 0xef, 0xd1, 0x65, 0x4f, 0x05, 0x80, 0x25, 0x7c, 0x86, 0x37, - 0x74, 0xa6, 0x2e, 0xe7, 0x4c, 0xba, 0xbb, 0x2b, 0x3f, 0xf4, 0x9e, 0xc6, 0x87, 0xef, 0xcd, 0x4d, 0x1b, 0x7f, 0xad, - 0x8e, 0x34, 0x09, 0x1d, 0x17, 0xfd, 0xc3, 0xe1, 0x53, 0xa2, 0xf5, 0x69, 0xa9, 0xd2, 0xa7, 0x29, 0xf0, 0x24, 0xc3, - 0x86, 0xeb, 0x16, 0xa6, 0xa3, 0xf9, 0x71, 0xf3, 0x55, 0xf2, 0xe2, 0x2c, 0x85, 0x6b, 0x6f, 0x3e, 0x4b, 0xe7, 0x53, - 0xb0, 0xae, 0x0c, 0xf3, 0x59, 0x3d, 0x0f, 0x20, 0x75, 0x08, 0x69, 0xd6, 0x34, 0xfc, 0x5b, 0xe5, 0x0a, 0xde, 0xda, - 0xe3, 0xdd, 0x60, 0x44, 0xa9, 0x23, 0x7d, 0xd2, 0x86, 0xd0, 0x25, 0x95, 0xfc, 0x47, 0x91, 0xc7, 0x18, 0xb3, 0xf1, - 0x9a, 0xc8, 0x3e, 0x8b, 0xfc, 0x55, 0x01, 0x80, 0x45, 0x80, 0x80, 0x9c, 0xce, 0x1d, 0x49, 0xfc, 0xe7, 0xe4, 0xdb, - 0x3f, 0xa6, 0x4b, 0xfb, 0x50, 0x16, 0x77, 0xa5, 0xa8, 0xaa, 0xa3, 0xd2, 0x76, 0xb6, 0x5c, 0x0f, 0xf4, 0xa1, 0xfd, - 0xbe, 0xa4, 0x0f, 0x4d, 0x31, 0x14, 0x05, 0x6e, 0x8d, 0xbd, 0x69, 0xca, 0x15, 0x4d, 0xf5, 0xc8, 0x58, 0x3f, 0xbf, - 0xdf, 0xbd, 0x8d, 0xbd, 0xd4, 0x0f, 0x52, 0x10, 0x84, 0x35, 0x7e, 0x52, 0x8a, 0x24, 0x70, 0x3e, 0xc3, 0x54, 0xe2, - 0xd3, 0xa5, 0x54, 0xf9, 0xc3, 0x48, 0xf3, 0x61, 0x0a, 0x7a, 0xd9, 0x7f, 0x54, 0x30, 0xff, 0x75, 0x7b, 0xb0, 0x3e, - 0xad, 0xcb, 0x34, 0xaa, 0x88, 0x2a, 0x2f, 0x4c, 0xb5, 0x09, 0x44, 0xf0, 0x17, 0xc2, 0x22, 0xf9, 0xf5, 0xc9, 0x91, - 0xa0, 0x31, 0x93, 0xe5, 0xe3, 0x91, 0xfb, 0x85, 0x7d, 0xe5, 0x3a, 0x9e, 0xff, 0xb9, 0x99, 0xff, 0x03, 0x74, 0x86, - 0x2c, 0x5e, 0x70, 0xcb, 0x60, 0x81, 0xb3, 0x5f, 0xba, 0x7a, 0xc0, 0xdf, 0xcc, 0x13, 0x2f, 0x80, 0x83, 0xf9, 0x05, - 0xba, 0x2a, 0xa6, 0xb3, 0x62, 0x00, 0x04, 0xb6, 0x7e, 0x63, 0xcd, 0x89, 0x37, 0x16, 0xcf, 0x95, 0x5c, 0x10, 0xfa, - 0xba, 0x0a, 0xb3, 0x71, 0x55, 0x6c, 0x2a, 0x51, 0x6c, 0xea, 0x1e, 0xa9, 0x65, 0xf3, 0x69, 0x6d, 0x2b, 0x64, 0x7f, - 0x12, 0x2d, 0xda, 0x2e, 0x43, 0x35, 0x19, 0x65, 0xe9, 0x7a, 0x0a, 0xa4, 0x7a, 0x01, 0x9c, 0x45, 0xe6, 0x95, 0x2f, - 0xce, 0x1e, 0xb0, 0x45, 0xe3, 0x29, 0x30, 0xa2, 0xd2, 0x1f, 0x79, 0x63, 0x74, 0x7a, 0xa2, 0xdf, 0xcf, 0xa7, 0x14, - 0xf2, 0xf5, 0x13, 0x60, 0x72, 0xd5, 0x72, 0x01, 0xfa, 0x32, 0xd4, 0x41, 0x25, 0x4a, 0xad, 0x18, 0x46, 0x2c, 0xfc, - 0x24, 0x90, 0xbd, 0x99, 0x82, 0x9a, 0x55, 0x94, 0x84, 0x4a, 0x54, 0x4a, 0xb6, 0x26, 0xa8, 0xa5, 0xf7, 0x45, 0x51, - 0xef, 0x2b, 0x70, 0x94, 0x8c, 0xb4, 0x59, 0x4e, 0x99, 0x71, 0x51, 0xe6, 0xa2, 0x1f, 0xec, 0xdf, 0x95, 0xe7, 0x37, - 0x32, 0x9f, 0xe5, 0xbe, 0xa3, 0x73, 0xda, 0x8e, 0x0b, 0x94, 0xb9, 0xe5, 0xb4, 0xd5, 0x92, 0xc7, 0xe4, 0x3d, 0x0b, - 0xb6, 0xfd, 0x97, 0x09, 0xf2, 0x2a, 0xc2, 0x7c, 0x42, 0x95, 0xcd, 0x3f, 0x20, 0xcc, 0x16, 0x07, 0xf6, 0xd8, 0x85, - 0x89, 0x48, 0x6f, 0xc1, 0x92, 0x18, 0x66, 0xa5, 0x08, 0xe3, 0x1d, 0x78, 0xff, 0x6c, 0x2a, 0x31, 0x3a, 0x43, 0x27, - 0xf7, 0xb3, 0x87, 0xb4, 0x4e, 0xce, 0xde, 0xbe, 0x3e, 0xfb, 0xae, 0x37, 0x28, 0x46, 0x69, 0x3c, 0xe8, 0x7d, 0x77, - 0xb6, 0xda, 0x00, 0x44, 0xa6, 0x38, 0x8b, 0xc9, 0x94, 0x26, 0xe2, 0x33, 0x32, 0x0c, 0x9e, 0xd5, 0x89, 0x38, 0xa3, - 0x89, 0xe9, 0xbe, 0x46, 0x69, 0xf2, 0xed, 0x28, 0xcc, 0xe1, 0xe5, 0x52, 0x6c, 0x2a, 0x11, 0x83, 0x9d, 0x52, 0xcd, - 0xb3, 0xbc, 0x7d, 0x16, 0xe7, 0xa3, 0x0e, 0x59, 0xa5, 0x03, 0x74, 0x7b, 0x22, 0xed, 0xaa, 0x74, 0x05, 0x84, 0x1e, - 0x00, 0x27, 0x5d, 0xf9, 0xf3, 0x70, 0x10, 0x09, 0x84, 0x5a, 0x30, 0x27, 0xd3, 0x88, 0x6e, 0x48, 0xaf, 0xb0, 0xcf, - 0xc0, 0x2c, 0xa4, 0x34, 0x0f, 0x6e, 0xae, 0x16, 0x2d, 0x77, 0xc5, 0xca, 0x51, 0x58, 0xad, 0x45, 0x54, 0x23, 0xd5, - 0x31, 0x38, 0xef, 0x40, 0x04, 0x80, 0x62, 0x04, 0xcf, 0x78, 0xd4, 0xef, 0x47, 0x2a, 0x28, 0x27, 0xa1, 0x5f, 0x14, - 0xfa, 0xa5, 0x31, 0x28, 0x63, 0xfe, 0x2e, 0xd4, 0xc4, 0x00, 0xf5, 0x96, 0x87, 0x8a, 0x23, 0x00, 0x97, 0x73, 0xc4, - 0x8c, 0xf3, 0x1e, 0x77, 0xd1, 0x38, 0x15, 0xef, 0x84, 0xba, 0x0e, 0x96, 0x0a, 0x75, 0xde, 0xd4, 0x47, 0x7a, 0x4e, - 0x9a, 0x04, 0x0d, 0xe2, 0x06, 0x1e, 0xaf, 0x86, 0x80, 0x6a, 0x25, 0xa4, 0xde, 0x42, 0xa7, 0x54, 0x75, 0x08, 0xac, - 0x01, 0x2e, 0x51, 0xd8, 0x56, 0x98, 0x1c, 0xd1, 0xa6, 0x2c, 0x45, 0x7e, 0xc4, 0x06, 0xed, 0x92, 0x91, 0xa9, 0x83, - 0xcb, 0xe5, 0x72, 0x22, 0xea, 0x5f, 0xf3, 0x2d, 0x80, 0xf3, 0x42, 0x7e, 0x6b, 0x37, 0x5b, 0x26, 0xd9, 0xae, 0x2b, - 0xc3, 0x59, 0x52, 0x8a, 0x6a, 0x5d, 0xe4, 0x55, 0x7a, 0x2f, 0x7e, 0xd6, 0x0f, 0x5d, 0x02, 0x29, 0xf4, 0x23, 0xbd, - 0x6e, 0x37, 0x47, 0xaa, 0x71, 0x74, 0x39, 0xb6, 0xa7, 0xd2, 0x4e, 0xf6, 0xaa, 0xc5, 0x9b, 0x2d, 0x73, 0x25, 0x69, - 0x1c, 0x8b, 0xfc, 0x6d, 0x1e, 0xa7, 0x91, 0x95, 0x1c, 0xfe, 0x1f, 0xde, 0xbe, 0x85, 0xbb, 0x6d, 0x1b, 0x5b, 0xf7, - 0xaf, 0x58, 0xbc, 0xa9, 0x4a, 0x44, 0x90, 0x2c, 0x39, 0x49, 0x67, 0x4a, 0x19, 0xd6, 0x71, 0xf3, 0x68, 0xd3, 0x36, - 0x71, 0x1a, 0xa7, 0x9d, 0xce, 0xe8, 0xea, 0xb8, 0x34, 0x09, 0x5b, 0x6c, 0x68, 0x40, 0x25, 0x29, 0x3f, 0x22, 0xf1, - 0xbf, 0xdf, 0xb5, 0x37, 0x9e, 0xa4, 0x68, 0x27, 0x33, 0xf7, 0xdc, 0xbb, 0xb2, 0x56, 0x2c, 0x82, 0x20, 0xde, 0xd8, - 0xd8, 0xd8, 0x8f, 0x6f, 0xeb, 0x00, 0xd5, 0x2e, 0xf2, 0x95, 0x8b, 0x8d, 0xfc, 0x22, 0x2b, 0x31, 0x60, 0x70, 0xa3, - 0x51, 0xad, 0x50, 0x53, 0x26, 0xf0, 0x85, 0x7c, 0x8f, 0x11, 0xb7, 0x59, 0x99, 0x00, 0xc3, 0x8f, 0x89, 0xfa, 0x92, - 0x9e, 0x42, 0x94, 0x07, 0x15, 0x8f, 0xfb, 0x05, 0x47, 0xc4, 0x6b, 0xab, 0x32, 0x07, 0x26, 0x5b, 0xab, 0x20, 0x11, - 0xec, 0x2e, 0x9b, 0xeb, 0x45, 0xb4, 0x50, 0x77, 0xa1, 0x5e, 0xbc, 0xdd, 0xf6, 0x12, 0x45, 0x07, 0x9c, 0xfc, 0x34, - 0x78, 0x15, 0x67, 0x39, 0x4f, 0xf7, 0x2a, 0xb9, 0xa7, 0x36, 0xd4, 0x9e, 0x72, 0xe6, 0x80, 0x9d, 0xf7, 0x75, 0xb5, - 0xa7, 0xd7, 0xf4, 0x9e, 0x6e, 0xe7, 0x1e, 0x5c, 0x30, 0x70, 0xe7, 0x5e, 0x66, 0xd7, 0x5c, 0xec, 0x81, 0x32, 0xd0, - 0x1a, 0x0f, 0xd4, 0xa2, 0x1a, 0xa9, 0x89, 0xd1, 0x81, 0xab, 0x13, 0x7d, 0x30, 0x07, 0xf4, 0x7b, 0x88, 0x15, 0xde, - 0x7a, 0xbb, 0xd2, 0x07, 0x6d, 0x40, 0x7f, 0x5e, 0x9a, 0x3e, 0xe8, 0x68, 0xf1, 0x2a, 0x24, 0x70, 0x63, 0x48, 0x35, - 0x52, 0xab, 0x91, 0x55, 0xa0, 0x78, 0xc3, 0x5b, 0xbc, 0x3b, 0xd7, 0x92, 0x8d, 0xf7, 0x12, 0x81, 0xbd, 0x32, 0x51, - 0xc5, 0x99, 0x38, 0xf6, 0x52, 0x79, 0xad, 0x9d, 0x64, 0x84, 0xf1, 0x2d, 0x2b, 0xa9, 0xbf, 0x43, 0xcc, 0x2d, 0xd2, - 0x1c, 0x06, 0x2f, 0xc3, 0x8a, 0xcc, 0x78, 0xbf, 0x2f, 0x67, 0x32, 0x2a, 0x67, 0x62, 0xbf, 0x8c, 0x14, 0x42, 0xdb, - 0x7d, 0x22, 0xa0, 0x07, 0x25, 0x40, 0xbe, 0x00, 0xa8, 0x7a, 0x48, 0xf8, 0xf3, 0x90, 0xd4, 0xa7, 0x53, 0xe8, 0x53, - 0x68, 0xeb, 0x15, 0xaf, 0xa0, 0xaa, 0x6e, 0x8c, 0x6c, 0xa3, 0x82, 0x16, 0x8f, 0xe5, 0x59, 0x6d, 0x18, 0x9b, 0x53, - 0xeb, 0x5d, 0x6f, 0x36, 0x98, 0xb2, 0xb9, 0x50, 0xab, 0x30, 0x24, 0xd1, 0x4d, 0xe9, 0x85, 0x0f, 0xb1, 0x58, 0x59, - 0xad, 0xcd, 0x6f, 0x62, 0x7f, 0x64, 0x22, 0xc5, 0xfd, 0x6c, 0x89, 0x73, 0x17, 0x8f, 0xe7, 0x55, 0x5f, 0x6b, 0x69, - 0x91, 0x69, 0xf3, 0x9d, 0xbe, 0x0c, 0x69, 0x2a, 0x6a, 0x48, 0xa3, 0xce, 0x0c, 0xba, 0x6f, 0x97, 0x57, 0x54, 0x23, - 0x4c, 0x80, 0x57, 0x3a, 0x83, 0x6e, 0x34, 0x1e, 0x88, 0xa2, 0x1a, 0x15, 0x6b, 0x21, 0x10, 0x6d, 0x18, 0x72, 0xcc, - 0x2c, 0x21, 0xc9, 0x3e, 0xf1, 0xef, 0x54, 0x70, 0x85, 0x22, 0xbe, 0x31, 0x70, 0xde, 0x95, 0xf5, 0xec, 0xae, 0x23, - 0x3f, 0x27, 0x16, 0x56, 0xfb, 0x0f, 0xcd, 0xa3, 0xd6, 0x38, 0x0b, 0x68, 0x6b, 0x5a, 0xdd, 0x70, 0xb8, 0x47, 0x75, - 0x2c, 0x4a, 0x03, 0x48, 0xec, 0x91, 0xe5, 0xa2, 0x75, 0xcc, 0xa0, 0x01, 0xfd, 0x6d, 0x76, 0xb5, 0xbe, 0x42, 0xd4, - 0xb6, 0x12, 0x59, 0x27, 0xa9, 0xfc, 0x4b, 0xda, 0xa3, 0xae, 0xed, 0xa9, 0xfc, 0x6f, 0xdb, 0x54, 0x39, 0xb4, 0x40, - 0xf2, 0xd8, 0xcd, 0x59, 0xa0, 0x3a, 0x12, 0x44, 0x81, 0xda, 0x7a, 0xc1, 0xd4, 0x3b, 0x65, 0x8a, 0x0e, 0xe4, 0xe7, - 0xc2, 0x9c, 0x61, 0x5f, 0x70, 0xc4, 0x98, 0xa5, 0x12, 0x83, 0xa9, 0x8f, 0x31, 0xaa, 0x69, 0xad, 0x00, 0x5d, 0x3f, - 0xdd, 0xc0, 0x9f, 0xa8, 0xa8, 0xd1, 0x50, 0x6b, 0x24, 0x85, 0xa2, 0x89, 0x0a, 0x3a, 0x96, 0x16, 0x3a, 0x98, 0x42, - 0x27, 0x91, 0xb0, 0x04, 0x34, 0x4c, 0x88, 0x4e, 0x2a, 0xf0, 0xd6, 0x00, 0xce, 0x7c, 0x5c, 0x94, 0xeb, 0x42, 0x1b, - 0xcc, 0xfd, 0x10, 0x5f, 0xf3, 0xd7, 0x2f, 0x9c, 0x51, 0x7d, 0xcb, 0x5a, 0xdf, 0xd3, 0x82, 0xfc, 0x10, 0x72, 0x8a, - 0x0e, 0x4c, 0xec, 0x68, 0x83, 0xc6, 0x18, 0x65, 0xad, 0x43, 0x5d, 0x9c, 0xe8, 0xf8, 0x2b, 0xda, 0x04, 0xef, 0x01, - 0x4f, 0x11, 0x6d, 0x78, 0x28, 0x8c, 0x55, 0x35, 0x3e, 0x95, 0xac, 0xa5, 0x07, 0x2b, 0x78, 0xba, 0x4e, 0x78, 0x08, - 0x7a, 0x24, 0xc2, 0x8e, 0xc2, 0x62, 0x1e, 0x2f, 0xe0, 0x38, 0x29, 0x08, 0xa8, 0x1d, 0xf4, 0x15, 0x7c, 0xbe, 0x40, - 0xf7, 0x57, 0x89, 0x1e, 0x60, 0x68, 0x41, 0xdc, 0x0c, 0x7d, 0x3a, 0xba, 0x8a, 0x57, 0x0d, 0x15, 0x09, 0x9f, 0x17, - 0x60, 0x3b, 0xa4, 0xd4, 0x53, 0xa0, 0x85, 0x4a, 0x94, 0x7e, 0x18, 0xf8, 0x0e, 0x0d, 0x7c, 0xad, 0x75, 0x80, 0x86, - 0x7e, 0xc6, 0x34, 0xb5, 0xce, 0x50, 0xf9, 0xcc, 0xbb, 0x67, 0x46, 0xcb, 0x99, 0x05, 0x63, 0xd0, 0xb7, 0xd1, 0x14, - 0xc5, 0x39, 0xf9, 0x2c, 0x28, 0xe2, 0x34, 0x8b, 0x73, 0xf0, 0xdb, 0x8c, 0x0b, 0xcc, 0x98, 0xc4, 0x15, 0xbf, 0x94, - 0x05, 0x68, 0xbb, 0x73, 0x95, 0x5a, 0xd7, 0x20, 0x20, 0xfb, 0x01, 0xac, 0x5e, 0x1a, 0x3a, 0x2a, 0xe7, 0xdd, 0xa5, - 0x4d, 0x21, 0x62, 0x11, 0x82, 0x4d, 0x33, 0x5d, 0xb2, 0xe3, 0x50, 0x69, 0x73, 0x20, 0xbe, 0x11, 0x1a, 0xf7, 0x4f, - 0xc3, 0xd8, 0x6a, 0x8a, 0xad, 0xdd, 0xdb, 0x76, 0xfb, 0x7b, 0xe9, 0xa5, 0xd3, 0x9c, 0xf4, 0x18, 0xfb, 0xbd, 0x0c, - 0x8b, 0x91, 0xed, 0x08, 0x81, 0x25, 0xe7, 0x7d, 0xea, 0xbf, 0xa2, 0xe5, 0x3c, 0x01, 0xd3, 0x11, 0x1d, 0x21, 0x17, - 0x28, 0x3b, 0x46, 0x71, 0x07, 0x06, 0x57, 0xf4, 0xfb, 0x60, 0x95, 0x61, 0x2e, 0x24, 0x4b, 0x92, 0x32, 0x78, 0x9e, - 0x7a, 0x18, 0xf0, 0x6b, 0xa6, 0xcc, 0x5d, 0x94, 0xf5, 0xe9, 0x92, 0x4c, 0x53, 0x64, 0x20, 0xd6, 0xe1, 0x26, 0x4b, - 0xa3, 0x44, 0x89, 0xc8, 0x96, 0xe8, 0x1f, 0x69, 0x28, 0x96, 0x0e, 0xd7, 0x8b, 0x54, 0x89, 0x50, 0x31, 0x4f, 0xf1, - 0xa4, 0x4e, 0xeb, 0x74, 0x84, 0xf1, 0x26, 0x41, 0x29, 0x57, 0xc3, 0x40, 0x95, 0x54, 0x2f, 0x85, 0x4d, 0xb1, 0xdd, - 0xea, 0x8b, 0x95, 0x98, 0xc7, 0x0b, 0x7c, 0x29, 0x70, 0x14, 0x7f, 0xe2, 0x5e, 0xac, 0x29, 0xb5, 0x3d, 0xa8, 0x1d, - 0x51, 0x42, 0x7f, 0xe2, 0x70, 0x91, 0xf8, 0x4e, 0xea, 0xb8, 0x7f, 0x68, 0x11, 0x72, 0xa6, 0x0e, 0x52, 0xc3, 0x0d, - 0xed, 0x08, 0xff, 0x0d, 0xd7, 0x67, 0x9c, 0xd1, 0x9b, 0x6a, 0x46, 0x8d, 0xdf, 0xeb, 0xe1, 0x19, 0xa3, 0x3e, 0x1b, - 0x38, 0xac, 0x10, 0x85, 0x36, 0xec, 0xa8, 0x54, 0xa2, 0x85, 0xa1, 0x54, 0x7f, 0x09, 0x15, 0x47, 0xdc, 0x99, 0x51, - 0x96, 0x8c, 0x4f, 0xcb, 0x43, 0x31, 0x1d, 0x0c, 0x4a, 0x52, 0x19, 0x0b, 0x3d, 0xb8, 0x1e, 0x78, 0xfe, 0x3d, 0x70, - 0x0b, 0xf1, 0xe0, 0x90, 0xc5, 0x90, 0x1b, 0x70, 0xfc, 0x16, 0x27, 0x57, 0x8d, 0x4a, 0x15, 0xbc, 0x9a, 0xa8, 0x16, - 0xfc, 0x54, 0x86, 0x01, 0xfa, 0x24, 0x05, 0x60, 0x32, 0x98, 0xf2, 0x5b, 0x90, 0x28, 0x9d, 0xa9, 0x1b, 0xd2, 0xaf, - 0xa2, 0xe0, 0x17, 0xbc, 0xe0, 0x22, 0x71, 0x05, 0x58, 0xde, 0xc1, 0xf6, 0x3a, 0xaa, 0xa8, 0x02, 0xe2, 0x35, 0x3d, - 0x8e, 0xb8, 0xf1, 0xfe, 0x33, 0x3d, 0xb6, 0x40, 0xad, 0xd6, 0xb1, 0xc1, 0x67, 0x8e, 0xc1, 0x05, 0x5d, 0x4b, 0x6c, - 0x0d, 0xd5, 0xb0, 0x22, 0x30, 0x70, 0x01, 0x07, 0x61, 0x89, 0xe2, 0xd8, 0x4a, 0x5e, 0x91, 0x86, 0x94, 0xf6, 0x81, - 0xe1, 0x68, 0x93, 0x1c, 0xdf, 0x66, 0xd9, 0x4d, 0xe0, 0x7c, 0xd1, 0x39, 0x69, 0x26, 0x96, 0x0d, 0xde, 0xe7, 0xcd, - 0xf9, 0x75, 0xff, 0x90, 0x50, 0x15, 0xec, 0x86, 0xb7, 0x83, 0xdd, 0x38, 0xe1, 0xd7, 0x5c, 0x2c, 0x74, 0x7c, 0x16, - 0x73, 0xc9, 0xf2, 0x5b, 0xeb, 0xdd, 0x92, 0xa4, 0x56, 0x40, 0xfb, 0x2c, 0x0b, 0x6a, 0x22, 0x00, 0xdd, 0x0f, 0x7f, - 0x81, 0xd0, 0x19, 0xfe, 0xf6, 0x18, 0x5c, 0x91, 0xc2, 0x7b, 0x87, 0x40, 0x58, 0xd3, 0xcd, 0x9d, 0xda, 0x80, 0x2f, - 0xc6, 0xfd, 0x19, 0x53, 0x4f, 0xbf, 0xcd, 0xe4, 0xae, 0xae, 0xdb, 0x23, 0xcb, 0xf0, 0x11, 0xae, 0x14, 0x00, 0xcb, - 0x84, 0xbf, 0x18, 0x5b, 0x52, 0x7d, 0x02, 0x70, 0x6a, 0x3a, 0xa2, 0x4f, 0x10, 0x18, 0x38, 0x25, 0x5a, 0x8c, 0xae, - 0x95, 0x23, 0x9a, 0x41, 0x5a, 0xd3, 0xad, 0x30, 0xde, 0x7a, 0xd0, 0x42, 0xcf, 0x34, 0x9c, 0xf8, 0x0f, 0x9a, 0x79, - 0x55, 0x40, 0x00, 0xad, 0x8c, 0xe0, 0xad, 0xf5, 0xd1, 0x1c, 0x21, 0x3e, 0x61, 0x49, 0x34, 0x61, 0xf1, 0x4c, 0xf1, - 0x63, 0x42, 0x37, 0x4d, 0x6d, 0xd3, 0x07, 0xa4, 0xbf, 0xb8, 0x66, 0xfd, 0x94, 0x65, 0xed, 0xdb, 0x43, 0xc5, 0x8b, - 0x69, 0x33, 0xf8, 0x61, 0xa2, 0x8a, 0xf1, 0xbf, 0xa8, 0x7c, 0xa9, 0x15, 0xc0, 0x30, 0x77, 0xd5, 0xd3, 0xef, 0x37, - 0xb3, 0xe5, 0x40, 0xa8, 0xfc, 0xce, 0x20, 0xe9, 0xd3, 0xf1, 0xfc, 0xc0, 0x26, 0x51, 0x5b, 0xe8, 0xf9, 0xe3, 0x52, - 0x37, 0xa1, 0xf2, 0xda, 0xd4, 0x88, 0x56, 0xc8, 0x50, 0xd9, 0x3a, 0x60, 0x7d, 0xff, 0x10, 0xee, 0x2e, 0x6a, 0x1a, - 0x6a, 0xdd, 0x73, 0xd7, 0xa2, 0xe0, 0xc4, 0x1f, 0x60, 0x2c, 0x2e, 0x24, 0xb5, 0x0e, 0xc2, 0xa4, 0x1f, 0x2d, 0x4e, - 0x72, 0xa3, 0xae, 0x4e, 0xce, 0x14, 0xf3, 0x04, 0x2e, 0xaa, 0x65, 0xdb, 0x5f, 0x51, 0xa9, 0x4b, 0xb9, 0xbd, 0xa2, - 0x34, 0x3d, 0xa4, 0xed, 0x55, 0x9c, 0xb7, 0x05, 0x17, 0xfc, 0x0b, 0x05, 0x17, 0xd6, 0xc1, 0xba, 0xe3, 0x4e, 0xd9, - 0x13, 0x9e, 0x28, 0xd3, 0xda, 0xe0, 0xae, 0x1b, 0x8c, 0x89, 0xb1, 0xdf, 0x5d, 0xf2, 0xe4, 0x23, 0xb2, 0xe0, 0xdf, - 0x65, 0x02, 0x3c, 0x93, 0xdd, 0x2b, 0x95, 0xff, 0x07, 0xff, 0x6a, 0x6b, 0xdf, 0x59, 0xf3, 0x4f, 0xcf, 0x7a, 0xb8, - 0x73, 0x98, 0xfc, 0x00, 0x9d, 0x01, 0xdd, 0x5c, 0xc9, 0x94, 0x03, 0x32, 0x80, 0xb5, 0x48, 0x46, 0x03, 0x3e, 0xb4, - 0xb2, 0x6c, 0xfb, 0x4e, 0xab, 0x0b, 0xc2, 0xbd, 0x04, 0x6e, 0x7a, 0x7f, 0x6d, 0x66, 0xe6, 0x74, 0xad, 0x44, 0xd3, - 0xa5, 0xb1, 0xb5, 0x2c, 0x55, 0xc0, 0xee, 0xf7, 0x9e, 0x64, 0xd3, 0xfc, 0x70, 0x39, 0xcd, 0x2d, 0x75, 0xdb, 0xb8, - 0x65, 0x03, 0x40, 0x88, 0x5d, 0x6b, 0x2b, 0x07, 0x90, 0xdc, 0x1e, 0x84, 0xf0, 0xb5, 0x22, 0xf4, 0x54, 0x89, 0xd0, - 0xa7, 0x69, 0xb3, 0x0f, 0x76, 0x55, 0xad, 0x1b, 0x71, 0x8e, 0x06, 0xa9, 0x66, 0xe4, 0x4f, 0xae, 0x79, 0x71, 0x91, - 0xcb, 0x1b, 0xc0, 0x40, 0x26, 0xb5, 0x51, 0x58, 0x5e, 0x81, 0x3b, 0x3f, 0x3a, 0x8e, 0x33, 0x31, 0xca, 0x31, 0x58, - 0x2b, 0xc2, 0x23, 0xeb, 0xc4, 0x19, 0x80, 0x20, 0xfb, 0x93, 0xa6, 0xe3, 0xb9, 0x16, 0x18, 0xd3, 0x17, 0xb8, 0xab, - 0x9c, 0x1d, 0x6d, 0x72, 0xbb, 0xe8, 0x9b, 0x33, 0xac, 0x3b, 0x52, 0x5a, 0x1b, 0x8b, 0xae, 0x3b, 0x58, 0x6b, 0x06, - 0x6d, 0x11, 0x4a, 0x3e, 0xe4, 0x4e, 0xda, 0x4f, 0x01, 0x0d, 0xce, 0xb2, 0xf4, 0xd6, 0x5a, 0xe5, 0x6f, 0xb4, 0x10, - 0x27, 0x8a, 0xa9, 0x13, 0xdf, 0x44, 0x89, 0x3e, 0x3f, 0x13, 0xe3, 0x06, 0x02, 0xa9, 0x3f, 0x60, 0x50, 0x8d, 0x22, - 0x4c, 0xe0, 0x3a, 0x10, 0xc5, 0xf6, 0x44, 0x6d, 0x2c, 0x47, 0xd0, 0x09, 0x21, 0xde, 0x41, 0x19, 0xc6, 0xea, 0xe2, - 0x40, 0x1b, 0x2c, 0x7d, 0xdd, 0x5a, 0xe7, 0x86, 0x50, 0x18, 0x27, 0x30, 0xc5, 0x20, 0xa9, 0xb3, 0xce, 0x32, 0x41, - 0x95, 0x1d, 0x93, 0xce, 0xfb, 0x00, 0xdd, 0x5d, 0x8b, 0xa6, 0xf8, 0xba, 0x73, 0x07, 0xdd, 0xc7, 0xf5, 0x6b, 0x2d, - 0x72, 0x83, 0x3f, 0x6f, 0x89, 0xb0, 0x08, 0x9c, 0xb5, 0x26, 0x5f, 0x35, 0xc2, 0x81, 0x29, 0xc9, 0x34, 0xec, 0x25, - 0xca, 0xa6, 0x7b, 0xbb, 0xed, 0xf5, 0xee, 0x15, 0x71, 0xf5, 0x18, 0xab, 0xbc, 0x9b, 0xb9, 0xbd, 0x53, 0xad, 0xc5, - 0xee, 0x4d, 0xdb, 0x4f, 0xb1, 0xa3, 0xd6, 0xda, 0xed, 0x86, 0x13, 0x6a, 0xc8, 0xb7, 0xa2, 0x4a, 0xab, 0xd3, 0x8d, - 0x41, 0x3b, 0xc4, 0xb3, 0x16, 0x19, 0xdc, 0x28, 0x5f, 0x38, 0xa1, 0x93, 0x8a, 0xb3, 0xea, 0xd4, 0x05, 0x9b, 0x2b, - 0x5e, 0x2d, 0x65, 0x1a, 0x09, 0x8a, 0x36, 0xe7, 0x51, 0x49, 0x13, 0xb9, 0x16, 0x55, 0x24, 0x6b, 0xd4, 0x8b, 0x5a, - 0x8d, 0x01, 0x02, 0x32, 0x9d, 0x35, 0x3d, 0xa8, 0x82, 0xd9, 0x50, 0x46, 0x72, 0xfa, 0x1e, 0x2c, 0xed, 0x91, 0x63, - 0xad, 0xef, 0xab, 0xb3, 0xc5, 0xb7, 0x7a, 0x42, 0x30, 0x85, 0xd9, 0x03, 0x61, 0xe0, 0x9a, 0xc6, 0x90, 0xd3, 0x2e, - 0x71, 0x59, 0xd3, 0x2d, 0xe1, 0x1e, 0x6e, 0x57, 0xb2, 0x23, 0x37, 0x4f, 0x9a, 0x9b, 0x2b, 0xd8, 0x51, 0x31, 0x1f, - 0x83, 0xf6, 0x4b, 0xaa, 0x6b, 0x97, 0xe6, 0xd6, 0xe3, 0x41, 0x40, 0x83, 0x41, 0x61, 0xf8, 0xd7, 0x89, 0xf1, 0xf0, - 0xa4, 0x01, 0x41, 0x52, 0x2e, 0xc2, 0xb1, 0x6f, 0x44, 0x3f, 0x99, 0xca, 0x43, 0x8e, 0x16, 0xef, 0xd0, 0xea, 0x04, - 0xa2, 0x78, 0x89, 0x50, 0x12, 0xa3, 0x2a, 0x34, 0x22, 0x28, 0x4f, 0xcb, 0x5f, 0xaa, 0xea, 0x10, 0x50, 0x48, 0xfb, - 0x8a, 0x42, 0xd9, 0x26, 0x31, 0x34, 0xc3, 0x2f, 0xe7, 0x93, 0x85, 0x9e, 0x81, 0x81, 0x9c, 0x1f, 0x2c, 0xf4, 0x2c, - 0x0c, 0xe4, 0xfc, 0xc9, 0xa2, 0x76, 0xeb, 0x40, 0x13, 0x10, 0xcf, 0x85, 0xa3, 0x93, 0xd2, 0xaa, 0x6c, 0x01, 0xdd, - 0x3c, 0x44, 0xd0, 0x7f, 0xb2, 0x87, 0xa0, 0x93, 0x0b, 0xed, 0xc8, 0x0d, 0x68, 0x3b, 0x0e, 0x81, 0xbd, 0x62, 0x52, - 0x61, 0x02, 0x10, 0x1d, 0xb2, 0x31, 0x18, 0x62, 0xab, 0x0f, 0x0e, 0xd9, 0x78, 0xea, 0x93, 0x20, 0x60, 0x74, 0x7f, - 0x30, 0x90, 0xe0, 0xb7, 0x78, 0x95, 0x3e, 0xda, 0x08, 0x74, 0xd3, 0x77, 0x77, 0x43, 0xef, 0xe2, 0x0a, 0x4e, 0xd5, - 0xee, 0x9e, 0x84, 0x6e, 0x32, 0xed, 0x00, 0xbd, 0x86, 0xb8, 0x21, 0xbf, 0x32, 0x1a, 0x8d, 0x6c, 0x4a, 0x48, 0x88, - 0xe1, 0x1c, 0x9a, 0x39, 0x2d, 0x97, 0xaf, 0x6e, 0x3d, 0x1b, 0x90, 0x61, 0xa6, 0xb7, 0x4c, 0xd6, 0x0f, 0x50, 0x56, - 0x3d, 0x86, 0x76, 0xe8, 0x3d, 0x72, 0xfc, 0xf0, 0xe0, 0x9b, 0x8c, 0x9f, 0x39, 0x5c, 0x7b, 0x38, 0x17, 0xbe, 0xcb, - 0x9a, 0x91, 0x39, 0x74, 0x9e, 0x7d, 0x1c, 0xef, 0x61, 0x9c, 0x7c, 0x9e, 0x85, 0xf2, 0xc6, 0x6b, 0xfa, 0x1f, 0x95, - 0xde, 0xec, 0x70, 0xc8, 0xe9, 0x0a, 0x56, 0xdc, 0xac, 0x0a, 0x0d, 0x3f, 0x8b, 0xbc, 0x71, 0xc4, 0x6b, 0x12, 0x55, - 0xdd, 0xe7, 0xbd, 0x0d, 0x53, 0xda, 0x31, 0x0e, 0x00, 0x4e, 0xd4, 0xaa, 0x61, 0x57, 0x1a, 0xd7, 0xea, 0x20, 0x86, - 0xa1, 0x84, 0xad, 0x12, 0x47, 0x42, 0xf9, 0x1b, 0x80, 0xb0, 0x18, 0x8a, 0xe3, 0xad, 0x61, 0x7d, 0x80, 0xfd, 0xd0, - 0x05, 0x9a, 0xe6, 0x94, 0x6a, 0x06, 0x00, 0x49, 0xc0, 0x1f, 0x3d, 0xdd, 0x34, 0x54, 0xb6, 0x79, 0x1e, 0x5a, 0x56, - 0x57, 0xf0, 0x40, 0x4f, 0x5d, 0xc9, 0xc0, 0xb8, 0xaa, 0x63, 0x6f, 0x73, 0x7f, 0x7b, 0xb4, 0x8a, 0x7c, 0x67, 0x93, - 0xda, 0x66, 0x55, 0x68, 0xec, 0xe3, 0x09, 0x3d, 0x9d, 0x00, 0xad, 0xd7, 0x96, 0x8a, 0xf6, 0xfb, 0x28, 0x46, 0x8d, - 0x0b, 0x05, 0x56, 0x61, 0x22, 0xc1, 0x21, 0xc2, 0x08, 0xa1, 0xdf, 0x97, 0xe1, 0xc6, 0x17, 0x64, 0x10, 0x0d, 0xd7, - 0xa2, 0xe3, 0x0f, 0x39, 0x5e, 0xb4, 0x2d, 0x55, 0x35, 0x27, 0x4d, 0x5b, 0x02, 0x6f, 0xc2, 0x01, 0xb6, 0xf3, 0x4f, - 0x1b, 0x22, 0x57, 0xe1, 0xa2, 0x84, 0xef, 0x88, 0x6b, 0x41, 0x74, 0x53, 0x9b, 0x7a, 0x1b, 0x76, 0x88, 0x8e, 0xa6, - 0x78, 0x74, 0xc8, 0x3d, 0x77, 0xcf, 0x6d, 0x11, 0xdf, 0x7c, 0x86, 0xdc, 0x35, 0x9d, 0xbd, 0x14, 0x61, 0x50, 0xb7, - 0x6c, 0xa0, 0x58, 0x87, 0x4e, 0x50, 0x80, 0x51, 0x5b, 0x3e, 0x01, 0x1d, 0x1b, 0x0c, 0x2a, 0x82, 0x4f, 0x0a, 0xdb, - 0xa6, 0x41, 0xfe, 0x88, 0x77, 0x43, 0x87, 0xd7, 0x96, 0x3c, 0x10, 0xaf, 0xb0, 0xcf, 0x94, 0x70, 0xff, 0x82, 0x82, - 0xee, 0x28, 0x2f, 0x57, 0x85, 0xab, 0xd2, 0x00, 0x54, 0xd9, 0xf1, 0x5c, 0x6b, 0x4a, 0x5a, 0xc0, 0x4a, 0x49, 0xdd, - 0xf9, 0x4d, 0x44, 0xdc, 0x92, 0xa9, 0x98, 0xad, 0xba, 0x51, 0xe5, 0xa1, 0x44, 0x91, 0x8e, 0x3d, 0xdb, 0x39, 0x58, - 0x03, 0xe0, 0x29, 0x6c, 0x2f, 0xce, 0xb0, 0xa0, 0x8c, 0xcb, 0x96, 0xb9, 0x04, 0x8a, 0xfa, 0x61, 0x9c, 0x97, 0x1d, - 0x5f, 0xee, 0x8e, 0xb6, 0xf7, 0xd0, 0x1b, 0xb1, 0x31, 0x5e, 0x5f, 0x46, 0x4d, 0xbf, 0x78, 0x86, 0x2b, 0x4b, 0x41, - 0x1e, 0x68, 0xaa, 0x47, 0x18, 0x1d, 0x02, 0xd3, 0x94, 0x1f, 0xb1, 0xf1, 0x74, 0x38, 0x34, 0x64, 0xd0, 0x6b, 0x26, - 0xc6, 0xff, 0xfa, 0x02, 0x5a, 0x67, 0x26, 0xae, 0xf1, 0x69, 0xfb, 0x0a, 0x5a, 0xdd, 0xa2, 0x4c, 0xee, 0x0c, 0x0c, - 0x1f, 0x68, 0xc9, 0x14, 0x4c, 0x15, 0xde, 0x10, 0xa9, 0x64, 0x9f, 0x96, 0xd6, 0x61, 0xdf, 0x2e, 0x14, 0x5a, 0x68, - 0xe2, 0x57, 0x19, 0xe2, 0xa7, 0xae, 0x33, 0xff, 0x36, 0xed, 0x53, 0x83, 0x58, 0x58, 0x12, 0xa3, 0x10, 0xbf, 0x38, - 0x55, 0xb6, 0x13, 0x42, 0x05, 0xc4, 0x43, 0xd7, 0xba, 0x71, 0x24, 0x55, 0xec, 0x49, 0xa1, 0xf1, 0xd4, 0x70, 0xdf, - 0x0b, 0x1d, 0xb3, 0x0e, 0xb3, 0xb8, 0xcd, 0x1a, 0x49, 0x8d, 0x71, 0x2a, 0x4c, 0x70, 0x4a, 0xb9, 0x8a, 0x04, 0x46, - 0xc7, 0xb3, 0x85, 0x41, 0x54, 0x49, 0x4c, 0x32, 0xb6, 0x16, 0xc2, 0xc4, 0xae, 0x73, 0x85, 0x69, 0xea, 0x22, 0xf5, - 0x9b, 0x81, 0xc9, 0x82, 0x86, 0xfc, 0x1e, 0x8d, 0xd6, 0x54, 0x4d, 0x01, 0x86, 0x71, 0x94, 0x6a, 0xfc, 0x5b, 0x84, - 0xda, 0x0c, 0x03, 0x00, 0xdb, 0xbc, 0x93, 0x99, 0xa8, 0x5e, 0x0b, 0x84, 0x40, 0x73, 0xf6, 0x53, 0x45, 0xb5, 0x33, - 0x0b, 0x46, 0xd1, 0x6e, 0xaf, 0x7c, 0x3e, 0x70, 0x42, 0x79, 0xac, 0x2e, 0x50, 0xaf, 0x64, 0xf1, 0x46, 0xa6, 0xbc, - 0x15, 0x17, 0x73, 0x4f, 0xb2, 0x0f, 0xf9, 0x08, 0xce, 0x2b, 0x74, 0x2a, 0x37, 0xdb, 0x44, 0x99, 0x25, 0x49, 0xc6, - 0x02, 0x63, 0xf3, 0x12, 0xcc, 0xa4, 0x66, 0xc6, 0xf0, 0x6b, 0x08, 0x2e, 0xb6, 0x73, 0x12, 0x6e, 0xee, 0xe7, 0x81, - 0x21, 0x34, 0xb9, 0x68, 0x89, 0x86, 0xad, 0x1d, 0xaf, 0x27, 0xd7, 0x84, 0xfb, 0xb0, 0x11, 0x6b, 0x32, 0xc6, 0xb8, - 0x36, 0x37, 0xb2, 0x7e, 0xb4, 0xc0, 0x83, 0x31, 0x65, 0xfd, 0x09, 0x64, 0x5a, 0x49, 0x59, 0xe7, 0x0b, 0x23, 0x66, - 0x52, 0x89, 0xde, 0xed, 0x1b, 0x9f, 0xd5, 0x5d, 0x44, 0xfd, 0xd6, 0x7e, 0x4f, 0xea, 0xe1, 0xce, 0x7f, 0x50, 0x58, - 0x83, 0xca, 0x88, 0xcb, 0x88, 0xf2, 0xcc, 0x81, 0x6e, 0x9a, 0x14, 0x71, 0x7a, 0xb6, 0x8a, 0x8b, 0x92, 0xa7, 0x50, - 0xa9, 0xa6, 0x6e, 0x51, 0x6f, 0x02, 0xf6, 0x86, 0x48, 0x92, 0xac, 0xa5, 0xb1, 0x15, 0xbb, 0x34, 0x48, 0xcf, 0xbd, - 0x61, 0x96, 0x5e, 0x55, 0x68, 0x48, 0x4b, 0xbd, 0xb3, 0x50, 0xc9, 0xfc, 0x15, 0xff, 0x19, 0xd4, 0x0a, 0x74, 0xb4, - 0x49, 0x31, 0x9e, 0x03, 0x23, 0xbe, 0x1b, 0xc1, 0xea, 0x01, 0xe2, 0xa2, 0x09, 0x4a, 0xbd, 0x23, 0x76, 0xfc, 0xdc, - 0xe4, 0xe1, 0x5d, 0xc8, 0x39, 0x83, 0x4f, 0x1f, 0x66, 0x89, 0x5a, 0xeb, 0x48, 0x8c, 0xd4, 0x0c, 0xa0, 0xe9, 0xa0, - 0xcc, 0x79, 0x2c, 0x82, 0x59, 0xcf, 0x24, 0x46, 0x3d, 0xae, 0x7f, 0x81, 0x86, 0xda, 0x6f, 0x56, 0x96, 0x67, 0xd5, - 0xdd, 0x97, 0x70, 0x60, 0x53, 0x5b, 0x41, 0x8f, 0xd7, 0x95, 0xbc, 0xbc, 0x54, 0xdd, 0xf6, 0x0b, 0x31, 0x72, 0xba, - 0xc6, 0xb5, 0x74, 0x5e, 0x2d, 0x58, 0xaf, 0x3b, 0xdd, 0x2c, 0xee, 0x66, 0x19, 0x0d, 0x84, 0xb5, 0x9d, 0x4f, 0x34, - 0x7f, 0xd6, 0x6c, 0xbb, 0x8f, 0xb7, 0x20, 0x66, 0x01, 0x00, 0xa4, 0x07, 0x51, 0xb0, 0xcc, 0x52, 0x1e, 0x50, 0x79, - 0x1f, 0x47, 0x59, 0x28, 0xbd, 0x9c, 0x65, 0xfc, 0xb4, 0x69, 0xac, 0x75, 0x56, 0x28, 0x43, 0x6b, 0xa3, 0x3b, 0x5d, - 0x65, 0x88, 0xed, 0x27, 0x71, 0xb6, 0x00, 0xf7, 0xc7, 0x0c, 0x85, 0x86, 0xce, 0x32, 0xd2, 0x44, 0xc3, 0x77, 0xdd, - 0x33, 0xc8, 0x28, 0x4e, 0xd6, 0x79, 0x25, 0xdd, 0xe8, 0xb3, 0x36, 0x12, 0xe6, 0x1e, 0xa2, 0x5f, 0xc5, 0xe0, 0x51, - 0xee, 0xf3, 0xda, 0xe8, 0x64, 0x5a, 0x46, 0xda, 0x9d, 0x9f, 0xd4, 0xcb, 0x2c, 0xd5, 0x3a, 0x6c, 0x9f, 0x61, 0x6f, - 0x8d, 0x49, 0x6f, 0x42, 0x6a, 0x18, 0x89, 0xcf, 0x67, 0xd4, 0x08, 0x01, 0x6d, 0x39, 0xfe, 0x0e, 0x9f, 0x61, 0x68, - 0x0a, 0x2c, 0x55, 0xdc, 0xc2, 0x6e, 0xf8, 0x9a, 0x4f, 0x56, 0x2d, 0x00, 0x11, 0xac, 0x7c, 0xbd, 0x8b, 0x57, 0x42, - 0x7d, 0xa6, 0xcd, 0x00, 0x90, 0x05, 0xa5, 0xdc, 0xf1, 0x53, 0x2a, 0x1d, 0x2c, 0x51, 0xb4, 0xbd, 0x9c, 0xbe, 0xd1, - 0xb1, 0xf1, 0x43, 0x7a, 0x2e, 0x60, 0xbb, 0x90, 0xdf, 0xba, 0x57, 0x2f, 0x51, 0x91, 0xda, 0x36, 0xeb, 0x01, 0xbe, - 0xdc, 0xa0, 0x49, 0x18, 0x41, 0x99, 0x32, 0x05, 0x30, 0xb8, 0xa9, 0x46, 0xc1, 0xa4, 0xd5, 0x48, 0xd8, 0x52, 0x4f, - 0xb2, 0xdc, 0xf4, 0xc1, 0xa9, 0xee, 0x11, 0xf4, 0x68, 0x87, 0x93, 0x96, 0xfd, 0x5a, 0xc1, 0xd1, 0xc9, 0xd5, 0x10, - 0x35, 0xf3, 0x5e, 0xdb, 0x91, 0x21, 0xe5, 0x32, 0x0c, 0x04, 0x53, 0x8e, 0x79, 0x7a, 0x6c, 0x3d, 0x23, 0xa2, 0x07, - 0xce, 0x3e, 0xd3, 0xad, 0xba, 0x92, 0x80, 0xe8, 0xf8, 0xcd, 0xd3, 0xd7, 0x57, 0xf1, 0xa5, 0x41, 0x51, 0x6a, 0x58, - 0xc4, 0x28, 0xd3, 0xbe, 0x4a, 0xc2, 0xe0, 0xfd, 0xfa, 0xfe, 0x67, 0x95, 0xa5, 0xf6, 0x7b, 0xb0, 0xb1, 0xa2, 0xaa, - 0x5f, 0x4b, 0x5e, 0x34, 0x05, 0x58, 0xf7, 0x59, 0xa2, 0x40, 0xee, 0xf7, 0x36, 0xcd, 0x7c, 0x13, 0x35, 0x6e, 0x36, - 0xac, 0x37, 0xae, 0xdb, 0xa5, 0xb6, 0x64, 0x47, 0x56, 0x22, 0x67, 0x16, 0x83, 0x19, 0x3f, 0x2a, 0x0c, 0x4a, 0xc3, - 0x06, 0x55, 0xa9, 0xf8, 0xbd, 0x11, 0xc1, 0xa9, 0x63, 0x55, 0x61, 0x4c, 0x03, 0x66, 0x5b, 0x51, 0x6b, 0x50, 0x07, - 0xa5, 0xb4, 0x35, 0x51, 0xd8, 0x7e, 0x67, 0x05, 0x35, 0xbf, 0xff, 0x69, 0x0c, 0xf9, 0x9a, 0x52, 0x50, 0x49, 0xc0, - 0xce, 0xa0, 0xd1, 0x53, 0x25, 0x0c, 0xa4, 0x20, 0x78, 0x02, 0x94, 0x2f, 0xa2, 0xc6, 0x6a, 0xb7, 0xaf, 0x4e, 0x8d, - 0xd1, 0x16, 0x10, 0x5a, 0x48, 0x8f, 0x2e, 0xfb, 0xb8, 0x8d, 0x75, 0x20, 0xf1, 0xe0, 0x04, 0xdb, 0xb9, 0xba, 0x46, - 0x23, 0xa1, 0xf9, 0x43, 0xa3, 0x01, 0xaf, 0x69, 0x05, 0x0a, 0xf5, 0x1c, 0x47, 0x43, 0x67, 0x87, 0x14, 0x44, 0x6c, - 0xd0, 0xc2, 0xbe, 0x7b, 0x3e, 0x34, 0xfb, 0x7a, 0x9e, 0x2c, 0x48, 0x4d, 0xa5, 0xfb, 0xdc, 0x2d, 0x21, 0x6b, 0xd5, - 0xa1, 0xac, 0x3c, 0xc0, 0xf1, 0x42, 0xc9, 0xfc, 0x1d, 0x26, 0x35, 0x4a, 0x63, 0x42, 0x63, 0xc4, 0x02, 0x96, 0x04, - 0xed, 0xf5, 0x40, 0xfd, 0x32, 0x08, 0x15, 0xce, 0xf4, 0x44, 0xe2, 0x53, 0xca, 0xd5, 0xa7, 0x05, 0xa9, 0xa7, 0x05, - 0x73, 0xa0, 0x97, 0xbe, 0x95, 0x5f, 0xd9, 0xf8, 0x68, 0x77, 0xef, 0x9a, 0x0b, 0xeb, 0x18, 0x82, 0x61, 0x0b, 0xbf, - 0x39, 0x35, 0x05, 0x60, 0xc3, 0x63, 0x5d, 0x96, 0x6f, 0xd4, 0x44, 0x66, 0x71, 0x48, 0x22, 0x90, 0x6c, 0x37, 0x37, - 0xb7, 0x11, 0x6c, 0x7b, 0x0b, 0xb5, 0xa1, 0xfe, 0xf2, 0xb6, 0xfb, 0x3d, 0xc3, 0xcb, 0x3d, 0xb9, 0x77, 0xd3, 0x86, - 0xf2, 0x87, 0xfb, 0x57, 0xc9, 0xff, 0x55, 0x25, 0xf7, 0x5b, 0x65, 0xd6, 0x6d, 0xf1, 0x7e, 0xd7, 0x71, 0xcb, 0x31, - 0x1a, 0x04, 0xd6, 0x14, 0x18, 0x48, 0x4f, 0x1a, 0xd3, 0x44, 0x87, 0x54, 0x66, 0xcc, 0xe0, 0xd1, 0x05, 0x68, 0x0e, - 0xd3, 0x79, 0x1e, 0x03, 0x70, 0x80, 0x7f, 0xe4, 0x11, 0xea, 0x9f, 0xce, 0xf3, 0xe0, 0x2c, 0x18, 0x94, 0x83, 0x40, - 0x7f, 0xe2, 0x9a, 0x13, 0x2c, 0x40, 0xe7, 0x16, 0x33, 0x08, 0x36, 0x69, 0xcd, 0x1c, 0xe2, 0xc3, 0x64, 0x3a, 0x18, - 0xc4, 0x64, 0x03, 0x20, 0x7d, 0xf1, 0xc2, 0x3a, 0x07, 0x15, 0x7a, 0x41, 0xb6, 0xea, 0x2e, 0x9a, 0x15, 0x7b, 0xd5, - 0x4e, 0xf3, 0x7e, 0x3f, 0x9f, 0x97, 0x83, 0xa0, 0x51, 0x61, 0x61, 0xbc, 0xff, 0x68, 0xf3, 0x4b, 0xa3, 0x93, 0x26, - 0x18, 0xa6, 0xf6, 0x18, 0xd5, 0x2b, 0x9e, 0x66, 0xb4, 0x71, 0x3b, 0x56, 0xca, 0x17, 0x10, 0xc5, 0x03, 0x43, 0xd6, - 0xca, 0xbb, 0x73, 0xf0, 0xba, 0xdc, 0x78, 0x73, 0x44, 0x01, 0x76, 0x53, 0x18, 0x27, 0x35, 0x17, 0x5d, 0xd4, 0xc4, - 0x33, 0xd8, 0xe9, 0xea, 0xad, 0x44, 0xab, 0xf1, 0x5e, 0xbc, 0x6b, 0x36, 0xfe, 0x56, 0xee, 0xe9, 0x32, 0xf7, 0x2e, - 0x00, 0x71, 0x76, 0x2f, 0xae, 0xf6, 0xb0, 0xd4, 0xbd, 0x60, 0x60, 0x91, 0x43, 0xda, 0xd5, 0xea, 0xa1, 0x88, 0xd4, - 0x79, 0x0c, 0x06, 0x4c, 0xa6, 0x21, 0x35, 0x99, 0xf6, 0x0a, 0x05, 0x69, 0x63, 0xad, 0x05, 0xb4, 0xe1, 0xb0, 0xd8, - 0xb1, 0x1b, 0x76, 0xa7, 0x5b, 0x87, 0x42, 0x09, 0xa3, 0x57, 0xd7, 0xcd, 0x43, 0xad, 0xe1, 0x89, 0xa0, 0x07, 0xd5, - 0x68, 0x3f, 0x3d, 0x94, 0x27, 0xed, 0xb1, 0x00, 0x17, 0x3d, 0x7c, 0xf9, 0x52, 0xe0, 0x45, 0x7b, 0x07, 0x79, 0xce, - 0x7c, 0xaa, 0x7c, 0x10, 0x1b, 0x6e, 0x19, 0x3e, 0xb4, 0x8f, 0x6f, 0x05, 0x32, 0xa9, 0x3b, 0x9a, 0xda, 0xda, 0x1d, - 0x8d, 0x63, 0x02, 0xfd, 0xa6, 0x1c, 0xa5, 0x4c, 0x4c, 0x2d, 0x4b, 0x76, 0xd4, 0xcb, 0x95, 0x37, 0x54, 0xca, 0x8e, - 0x96, 0x6d, 0xce, 0x2f, 0x6d, 0x24, 0xf4, 0xfb, 0xda, 0x1d, 0x08, 0xdf, 0xa8, 0xf5, 0x86, 0xbc, 0x6c, 0x88, 0x58, - 0x0e, 0x31, 0x03, 0xc7, 0x0b, 0xa9, 0x5c, 0xbb, 0x8b, 0xa6, 0xaa, 0x6e, 0x67, 0x2b, 0x17, 0xb4, 0xc4, 0x5b, 0x29, - 0xb0, 0x8a, 0xd4, 0xe9, 0xf5, 0x54, 0xe2, 0x7d, 0x1f, 0xc5, 0xf6, 0x23, 0x60, 0x1b, 0x1b, 0x47, 0x63, 0xe3, 0x16, - 0xb1, 0xc1, 0x57, 0x51, 0x45, 0x0b, 0x0e, 0x10, 0xdc, 0x6d, 0x49, 0x2d, 0xcd, 0x1c, 0xe2, 0xbe, 0xe2, 0x01, 0xda, - 0x77, 0x71, 0xc4, 0xa9, 0x00, 0xdb, 0xba, 0xd6, 0x39, 0xab, 0xe5, 0x80, 0xcd, 0x44, 0xcf, 0x3f, 0xad, 0x1a, 0x89, - 0x18, 0x56, 0xd9, 0x48, 0x59, 0xa1, 0x3d, 0x28, 0x5d, 0xc2, 0xc5, 0x17, 0xe0, 0x65, 0xfb, 0x7e, 0x65, 0xf7, 0xd9, - 0x12, 0xfb, 0x87, 0x79, 0xd5, 0x04, 0x8f, 0xbc, 0xc6, 0xdb, 0x7b, 0x98, 0xf8, 0x52, 0x29, 0x84, 0x57, 0x29, 0x0d, - 0x25, 0x00, 0x83, 0x24, 0xa8, 0xe1, 0x4a, 0xdb, 0x66, 0x90, 0xca, 0x18, 0x76, 0xb7, 0x7a, 0xab, 0xff, 0xd3, 0x2a, - 0x5c, 0x54, 0xb2, 0x18, 0x93, 0x40, 0xe7, 0x54, 0xcb, 0x4d, 0x4c, 0xc1, 0xb3, 0x5d, 0x72, 0x04, 0x0a, 0x3b, 0x01, - 0xdc, 0x50, 0xc2, 0x7e, 0xc5, 0xdb, 0x50, 0xce, 0x5e, 0x59, 0xc9, 0x93, 0xdb, 0x97, 0x54, 0xd0, 0x84, 0x4c, 0x85, - 0xdd, 0xbf, 0xad, 0x0d, 0xfb, 0x22, 0x94, 0x23, 0x29, 0x70, 0x71, 0xd0, 0x39, 0x80, 0xfd, 0x41, 0x2e, 0x63, 0xf3, - 0x99, 0xf4, 0xfb, 0xea, 0xfd, 0xf3, 0x3c, 0x4b, 0x3e, 0xee, 0xbc, 0x37, 0x3c, 0xcd, 0x92, 0x01, 0x95, 0x88, 0xa9, - 0x75, 0x55, 0x0c, 0x97, 0xda, 0xc5, 0xb8, 0x41, 0x32, 0xe2, 0x7b, 0xa9, 0x43, 0x8c, 0x18, 0x5f, 0x64, 0x87, 0xa4, - 0xe4, 0x74, 0x59, 0x77, 0xf6, 0x5c, 0x8b, 0x66, 0xd0, 0x18, 0x6e, 0xc7, 0x7b, 0x49, 0xaf, 0x00, 0x15, 0x15, 0xba, - 0x67, 0x81, 0x6b, 0x78, 0x73, 0x49, 0x34, 0xb6, 0xf4, 0xb4, 0x25, 0x1a, 0xb8, 0x57, 0x26, 0x24, 0xd5, 0xc6, 0x01, - 0x16, 0xb1, 0xae, 0x3f, 0x86, 0x12, 0x80, 0x5a, 0x0d, 0xd2, 0x2b, 0x7d, 0x45, 0xa8, 0x4a, 0x42, 0x30, 0x3a, 0x91, - 0xf0, 0x32, 0xa0, 0x71, 0x66, 0x12, 0x2d, 0x6c, 0x70, 0x40, 0x5f, 0x54, 0x26, 0xd1, 0xd8, 0x90, 0x07, 0xb4, 0xb2, - 0x69, 0x00, 0x83, 0x0f, 0x92, 0x24, 0xfa, 0x7a, 0x69, 0x92, 0x40, 0x50, 0x82, 0xf2, 0x0d, 0xfa, 0x4b, 0xe9, 0xf9, - 0x58, 0xfe, 0xcb, 0x3b, 0x94, 0x7e, 0x08, 0x25, 0xc8, 0x14, 0x75, 0xc5, 0x34, 0x63, 0x47, 0x59, 0xb7, 0x31, 0x89, - 0xe7, 0x69, 0x77, 0x5b, 0x28, 0x97, 0x2e, 0xf0, 0x2b, 0xcb, 0x10, 0xc7, 0xfa, 0x79, 0xbc, 0x62, 0xc7, 0x21, 0xd7, - 0x78, 0xe9, 0xcf, 0xe3, 0x15, 0xce, 0x10, 0xad, 0x5a, 0x09, 0x44, 0xf9, 0xaf, 0xda, 0xc0, 0x21, 0xee, 0x13, 0x0c, - 0x72, 0x51, 0x79, 0x0f, 0x04, 0xf2, 0xb6, 0x82, 0x88, 0x34, 0xb3, 0xeb, 0x30, 0x22, 0xd5, 0x4e, 0x92, 0xf9, 0xf2, - 0x47, 0x99, 0x09, 0xef, 0x1b, 0x78, 0x6c, 0x36, 0xcb, 0xa6, 0x98, 0x2f, 0x54, 0x30, 0x07, 0xf7, 0x89, 0x8a, 0x4b, - 0x51, 0xf9, 0x4f, 0xd8, 0x05, 0x2f, 0xc6, 0x83, 0xd7, 0x6b, 0x04, 0xd8, 0xaf, 0xfc, 0x27, 0x6f, 0xcc, 0xde, 0x5a, - 0x37, 0xbe, 0xcc, 0x44, 0x7c, 0xe0, 0xa3, 0x5b, 0xca, 0x47, 0x77, 0x5e, 0xa6, 0x3f, 0x1b, 0x50, 0x22, 0xa3, 0xb2, - 0xe2, 0xab, 0x15, 0x4f, 0x67, 0xb7, 0x49, 0x94, 0x8d, 0x2a, 0x2e, 0x60, 0x7a, 0xc1, 0xf1, 0x2e, 0x59, 0x9f, 0x67, - 0xc9, 0x6b, 0x88, 0x3d, 0xb0, 0x92, 0x0a, 0x8b, 0x1f, 0x96, 0x99, 0x5a, 0xcc, 0x42, 0x56, 0x52, 0xf0, 0x60, 0x76, - 0x9d, 0x44, 0x6f, 0x97, 0x1e, 0x92, 0x9a, 0x99, 0xb2, 0x4d, 0xed, 0x08, 0xb5, 0xf1, 0x75, 0xa4, 0x1b, 0x6d, 0x01, - 0x00, 0xf7, 0x6c, 0x91, 0x46, 0x92, 0x89, 0xe1, 0xa4, 0x66, 0xdc, 0xa4, 0x17, 0x98, 0x1a, 0xd7, 0xac, 0xa2, 0x89, - 0xb3, 0x90, 0x01, 0xbd, 0x3f, 0xe0, 0xe5, 0xe0, 0x73, 0x06, 0xf7, 0x1f, 0xb4, 0x06, 0x2e, 0x0f, 0x8b, 0x7e, 0x5f, - 0x1e, 0x16, 0xdb, 0x6d, 0x79, 0x14, 0xf7, 0xfb, 0xf2, 0x28, 0x36, 0xfc, 0x83, 0x52, 0x6c, 0x1b, 0x73, 0x83, 0x84, - 0xe6, 0x12, 0xa2, 0x16, 0x8d, 0xe0, 0x0f, 0xcd, 0x72, 0x2e, 0xa2, 0xfc, 0x30, 0xe9, 0xf7, 0x7b, 0xcb, 0x99, 0x18, - 0xe4, 0xc3, 0x24, 0xca, 0x87, 0x89, 0xe7, 0x84, 0xf8, 0x9b, 0xe7, 0x84, 0xa8, 0x68, 0xe0, 0x0a, 0xce, 0x0c, 0x40, - 0x14, 0xf0, 0xe9, 0x1f, 0xd5, 0xb5, 0x14, 0xba, 0x96, 0x58, 0xd5, 0x92, 0xe8, 0x0a, 0x6a, 0x76, 0x5d, 0x84, 0x25, - 0x96, 0x42, 0x97, 0xec, 0xcf, 0x25, 0xf0, 0x44, 0x39, 0xaf, 0x36, 0xc0, 0xc0, 0x46, 0x78, 0xe7, 0x30, 0xe1, 0x24, - 0xd6, 0x35, 0xa0, 0x9d, 0x6e, 0x6a, 0x7a, 0x41, 0x57, 0xf4, 0x12, 0xf9, 0xd9, 0x0b, 0x30, 0x58, 0x3a, 0x64, 0xf9, - 0x74, 0x30, 0xb8, 0x20, 0x2b, 0x56, 0xce, 0xc3, 0x78, 0x10, 0xae, 0x67, 0xf9, 0xf0, 0x22, 0xba, 0x20, 0xe4, 0xab, - 0x62, 0x41, 0x7b, 0xab, 0x51, 0xf9, 0x31, 0x83, 0xe0, 0x7e, 0xe9, 0x2c, 0xcc, 0x4c, 0x9c, 0x8f, 0xd5, 0xe8, 0x96, - 0xae, 0x20, 0x7e, 0x0d, 0xdc, 0x48, 0x48, 0x04, 0x1d, 0xb9, 0xa4, 0x2b, 0xba, 0xa6, 0xd2, 0xcc, 0x30, 0x86, 0xe8, - 0xb6, 0xc7, 0x49, 0x02, 0x8e, 0xc9, 0xae, 0xf8, 0x68, 0xac, 0x0a, 0xef, 0xfa, 0x8e, 0xd0, 0x5e, 0x2f, 0x71, 0x83, - 0xf4, 0x5d, 0x7b, 0x90, 0x80, 0x11, 0x19, 0xa9, 0x81, 0x32, 0x23, 0x23, 0xa9, 0x99, 0x54, 0x1c, 0x92, 0xd8, 0x1f, - 0x12, 0x35, 0x0e, 0x89, 0x3f, 0x0e, 0xb9, 0x1e, 0x07, 0xe4, 0xee, 0x97, 0x6c, 0x4c, 0x53, 0x36, 0xa6, 0x6b, 0x35, - 0x2a, 0xf4, 0x8a, 0x9e, 0x6b, 0xea, 0x78, 0xc6, 0xde, 0xc0, 0x81, 0x3d, 0x08, 0xf3, 0x59, 0x3c, 0x7c, 0x13, 0xbd, - 0x21, 0xe4, 0x2b, 0x49, 0xaf, 0xd5, 0xa5, 0x0c, 0xc2, 0x20, 0x5e, 0x81, 0x73, 0xa9, 0x0b, 0x75, 0x72, 0x65, 0x76, - 0x1c, 0x3e, 0x5d, 0x36, 0x9e, 0xce, 0x21, 0xa2, 0x0f, 0x5a, 0xa9, 0xf4, 0xfb, 0xe1, 0x05, 0x2b, 0xe7, 0x67, 0xe1, - 0x98, 0x00, 0x0e, 0x8f, 0x1e, 0xce, 0x8b, 0xd1, 0x2d, 0xbd, 0x18, 0xdd, 0x11, 0xb0, 0xf0, 0x1a, 0x4f, 0xd7, 0x87, - 0x2c, 0x9e, 0x0e, 0x06, 0x6b, 0xa4, 0xea, 0x2a, 0xf7, 0x9a, 0x2c, 0xe8, 0x05, 0x4e, 0x04, 0x01, 0x86, 0x3e, 0x13, - 0x6b, 0x43, 0xc3, 0xdf, 0x30, 0xf8, 0xf8, 0x8e, 0x5d, 0x8c, 0xee, 0xe8, 0x2d, 0x7b, 0xb3, 0x1d, 0x4f, 0x81, 0x99, - 0x5a, 0xcd, 0xc2, 0xbb, 0xc3, 0xcb, 0xd9, 0x25, 0xbb, 0x8b, 0xee, 0x8e, 0xa0, 0xa1, 0x57, 0xec, 0x0e, 0x01, 0x97, - 0xd2, 0xc7, 0xcb, 0xc1, 0x1b, 0xb2, 0x3f, 0x18, 0xa4, 0x24, 0x0a, 0xaf, 0x43, 0xaf, 0x95, 0x6f, 0xe8, 0x1d, 0xa1, - 0x2b, 0x76, 0x8b, 0xa3, 0x71, 0xc9, 0xf0, 0x83, 0x73, 0x76, 0x57, 0x5f, 0x87, 0xde, 0x6e, 0x4e, 0x44, 0x27, 0x88, - 0x11, 0xfa, 0x1a, 0x38, 0x9a, 0xe5, 0xc2, 0x4c, 0xc0, 0x93, 0xb9, 0xc8, 0x68, 0x51, 0x68, 0x06, 0xe2, 0xac, 0x04, - 0xc4, 0x92, 0xa8, 0xfb, 0xcd, 0x46, 0x67, 0xb0, 0x9c, 0xfb, 0xfd, 0x5e, 0x65, 0xe8, 0x01, 0x22, 0x67, 0x76, 0xd2, - 0x83, 0x9e, 0x4f, 0x0f, 0xf0, 0x13, 0xbd, 0x6a, 0x10, 0x27, 0xf3, 0xbb, 0x65, 0xf4, 0x9b, 0x47, 0x1f, 0x7e, 0xe8, - 0xa6, 0x3c, 0x22, 0xff, 0xf7, 0x29, 0x4f, 0x99, 0x47, 0x6f, 0x2a, 0x0f, 0x04, 0xcf, 0x5b, 0x93, 0x4a, 0x23, 0x51, - 0x8d, 0xce, 0x56, 0x31, 0x68, 0x23, 0x51, 0xdb, 0xa0, 0x9f, 0xd0, 0xc2, 0x0a, 0x22, 0xe4, 0x1c, 0xbc, 0x00, 0x83, - 0x54, 0x08, 0x95, 0xa3, 0x16, 0x25, 0x1a, 0x82, 0xe4, 0xb2, 0xe4, 0x2a, 0x7c, 0x0e, 0xa1, 0xea, 0xf4, 0x71, 0x26, - 0xc2, 0x86, 0x1e, 0x87, 0x3e, 0x00, 0xfc, 0xaf, 0x3b, 0xe4, 0xa2, 0xe4, 0x97, 0x78, 0x36, 0xb7, 0x09, 0x46, 0xc1, - 0x12, 0xd1, 0x0c, 0x6d, 0x83, 0xd8, 0x8f, 0x25, 0xc1, 0x7a, 0x24, 0x8d, 0x47, 0xa5, 0x39, 0x22, 0xfc, 0x28, 0x3e, - 0x8a, 0x9e, 0xc6, 0x86, 0x44, 0x72, 0x24, 0x91, 0x7c, 0x00, 0x84, 0x93, 0xa0, 0xbf, 0xb8, 0x6b, 0xb2, 0x6b, 0x21, - 0x31, 0xe8, 0x4f, 0x4b, 0xa6, 0x65, 0xf7, 0xaa, 0xc7, 0xbe, 0x22, 0xc8, 0x1d, 0xd3, 0x7f, 0x79, 0x7d, 0xf8, 0xe7, - 0x12, 0x67, 0xd0, 0x7a, 0xbe, 0xa8, 0xce, 0xcc, 0xbc, 0xc1, 0x8d, 0xbc, 0x2e, 0x6b, 0xd7, 0xe5, 0x0b, 0xbe, 0xc7, - 0x6f, 0x2b, 0x2e, 0xd2, 0x72, 0xef, 0x97, 0xaa, 0x8d, 0xe7, 0x54, 0xae, 0x57, 0x2e, 0xce, 0x8a, 0x32, 0x4e, 0xf5, - 0xa4, 0x2e, 0xc6, 0x1a, 0xb6, 0xe1, 0xf7, 0x88, 0xba, 0x92, 0x96, 0xa3, 0xa7, 0x94, 0xab, 0x66, 0xca, 0xc5, 0x3a, - 0xcf, 0x7f, 0xde, 0x49, 0xc5, 0x29, 0x6e, 0xa6, 0x20, 0x55, 0x6a, 0xb9, 0x80, 0xea, 0x39, 0x6a, 0xb9, 0x5b, 0x9a, - 0x1d, 0xe0, 0xdc, 0x36, 0xd5, 0xc7, 0xca, 0xec, 0xc2, 0x4b, 0x6e, 0xdc, 0x9f, 0x4c, 0x19, 0x16, 0x8c, 0x42, 0x9b, - 0x55, 0x57, 0xda, 0xbe, 0xd0, 0x3a, 0x0d, 0xc3, 0x95, 0x1f, 0x2f, 0x20, 0x5d, 0xc0, 0x38, 0x5e, 0x94, 0x4c, 0x8c, - 0xdb, 0xa3, 0xb7, 0x82, 0xf8, 0x92, 0xad, 0x40, 0xc0, 0x5c, 0xc3, 0xdb, 0x75, 0x1d, 0x6d, 0xf7, 0xc4, 0x29, 0xa3, - 0x72, 0x15, 0x8b, 0xef, 0xe3, 0x95, 0x81, 0x4c, 0x56, 0xc7, 0x63, 0x63, 0x4c, 0xa7, 0x3f, 0x25, 0xa1, 0x5f, 0x08, - 0x05, 0x9f, 0xf5, 0xd2, 0xca, 0x93, 0xdb, 0xc3, 0x32, 0xae, 0xd1, 0x2b, 0x71, 0xa5, 0xfb, 0x66, 0xa4, 0x90, 0x7a, - 0xe4, 0xab, 0xa6, 0x80, 0xde, 0x8c, 0x7d, 0x33, 0x15, 0xe6, 0xed, 0x9e, 0x31, 0x57, 0x08, 0x56, 0xaa, 0xec, 0xf6, - 0x9d, 0x1a, 0x53, 0x31, 0x83, 0x29, 0xb6, 0x9d, 0xc5, 0xa4, 0x5b, 0xf9, 0xa7, 0x9d, 0xfb, 0x65, 0xde, 0xe1, 0xae, - 0xa8, 0xdf, 0x02, 0x17, 0x9a, 0x15, 0x65, 0xd5, 0x96, 0x0d, 0xdb, 0xc6, 0x1b, 0x59, 0x28, 0x36, 0xc0, 0xb2, 0xe7, - 0xbe, 0x85, 0x07, 0x88, 0x9b, 0x70, 0xcf, 0x2e, 0x6a, 0xb8, 0x31, 0x7c, 0x59, 0x49, 0xbe, 0x2b, 0x8d, 0xb9, 0xf4, - 0xa9, 0xd2, 0xc4, 0x70, 0xb2, 0x18, 0x71, 0x91, 0x2e, 0xea, 0xcc, 0xae, 0x85, 0xcf, 0x78, 0x19, 0xce, 0xf9, 0xc2, - 0xe8, 0xa6, 0x74, 0xe9, 0x05, 0x8b, 0x75, 0xa7, 0x37, 0x2b, 0x8d, 0x95, 0x12, 0x71, 0x6b, 0x96, 0x09, 0x94, 0xa5, - 0xac, 0x95, 0xf0, 0xa6, 0x68, 0xd9, 0x4a, 0x1a, 0x79, 0xcf, 0x1c, 0xdc, 0xc7, 0x7e, 0x40, 0x4c, 0x64, 0x13, 0x98, - 0x14, 0x0d, 0x1d, 0xd0, 0xae, 0xba, 0xf0, 0xcd, 0xa8, 0x07, 0x83, 0xdc, 0x92, 0x44, 0xac, 0x20, 0xc5, 0x0a, 0xd6, - 0x35, 0x2b, 0xe6, 0xf9, 0x82, 0x5e, 0x30, 0x39, 0x4f, 0x17, 0x74, 0xc5, 0xe4, 0x7c, 0x8d, 0x37, 0xa1, 0x0b, 0x38, - 0x21, 0xc9, 0x26, 0x56, 0x0a, 0xd8, 0x0b, 0xbc, 0xbc, 0xe1, 0x99, 0xaa, 0x69, 0xd9, 0xa5, 0xe2, 0x00, 0xe3, 0xf3, - 0x32, 0x0c, 0xcb, 0xe1, 0x05, 0x58, 0x4b, 0xec, 0x87, 0xab, 0x39, 0x5f, 0xa8, 0xdf, 0x10, 0x70, 0x3e, 0x09, 0x15, - 0xbb, 0x60, 0xf7, 0x02, 0x99, 0x5e, 0xcd, 0xf9, 0x42, 0x8d, 0x84, 0x2e, 0xf8, 0xca, 0x1a, 0x9b, 0xc4, 0x9e, 0xa0, - 0x65, 0x16, 0xcf, 0xc7, 0x8b, 0x28, 0xae, 0x61, 0x19, 0x9e, 0xaa, 0x99, 0x69, 0xc9, 0x7f, 0x12, 0xb5, 0xa1, 0x89, - 0xbe, 0xc1, 0x2a, 0xf2, 0x87, 0xc7, 0x47, 0x97, 0x40, 0xc6, 0xce, 0xae, 0x64, 0xe6, 0x43, 0xdf, 0x47, 0x06, 0xf7, - 0xdc, 0x94, 0x33, 0xae, 0x82, 0x44, 0x19, 0xb8, 0x7b, 0x35, 0x4b, 0xc6, 0x5a, 0x84, 0xef, 0x1e, 0x15, 0x45, 0x9f, - 0x49, 0xd3, 0x80, 0xee, 0x23, 0xc1, 0x1c, 0xe8, 0xbd, 0x42, 0x87, 0xcb, 0x6a, 0x9b, 0x09, 0xf8, 0x8b, 0x04, 0xf9, - 0xad, 0xd0, 0xab, 0x1a, 0x83, 0x2a, 0xda, 0x45, 0x2c, 0xfd, 0xfb, 0x88, 0x1f, 0x65, 0xf3, 0x2f, 0x73, 0x8f, 0x57, - 0x12, 0x06, 0x3f, 0xa4, 0x66, 0x93, 0xcc, 0xdb, 0x2b, 0xf6, 0x3d, 0x74, 0xd4, 0xa3, 0xd6, 0x78, 0x5f, 0xbd, 0xe0, - 0x14, 0x62, 0x94, 0x50, 0x74, 0x12, 0x0c, 0xe0, 0x76, 0x09, 0x29, 0xee, 0x06, 0xbb, 0x69, 0x5e, 0xf3, 0xa2, 0xe0, - 0x7c, 0x5d, 0x55, 0x81, 0x1f, 0xd0, 0x70, 0xbe, 0xd8, 0x0d, 0x61, 0x38, 0xa6, 0xad, 0x6b, 0x18, 0x84, 0x19, 0xc3, - 0x48, 0x08, 0x5e, 0xff, 0xa2, 0x27, 0x34, 0x89, 0x57, 0xdf, 0xf1, 0x4f, 0x19, 0x2f, 0x14, 0x91, 0x06, 0x11, 0x52, - 0x37, 0xf1, 0x8d, 0x4c, 0x93, 0x02, 0x0a, 0x01, 0x46, 0x01, 0x95, 0xd8, 0xd0, 0x54, 0xfc, 0xad, 0x16, 0x1f, 0xfc, - 0xd4, 0x74, 0x3c, 0x1a, 0xd7, 0xad, 0xce, 0xa8, 0xa0, 0x33, 0xd0, 0xa3, 0x56, 0xd4, 0xd3, 0xa0, 0x95, 0x60, 0x1a, - 0x69, 0xde, 0xba, 0x87, 0xc0, 0x2b, 0xd3, 0xe2, 0x9d, 0x07, 0x74, 0x73, 0xe6, 0x83, 0x27, 0x8f, 0xe9, 0x99, 0x43, - 0x4f, 0xae, 0xd8, 0x51, 0xd5, 0x43, 0xed, 0xbd, 0x19, 0xa1, 0xa0, 0xdf, 0xc7, 0x14, 0xe8, 0x46, 0x50, 0x7b, 0x57, - 0xf7, 0x1f, 0xcb, 0x5d, 0x0e, 0xdf, 0x71, 0x96, 0x1b, 0xc0, 0x52, 0x91, 0xb5, 0x02, 0x8f, 0x02, 0xd4, 0xa5, 0x32, - 0x84, 0x2d, 0xe6, 0x70, 0xa8, 0xec, 0x56, 0xad, 0x86, 0x92, 0x1c, 0x96, 0x23, 0x70, 0x08, 0x5d, 0x97, 0x83, 0x72, - 0xb4, 0xcc, 0xaa, 0xf7, 0xf8, 0x5b, 0xb3, 0x0e, 0x49, 0x76, 0x1f, 0xeb, 0xc0, 0x2d, 0xeb, 0x30, 0xfd, 0x68, 0x90, - 0x02, 0xd0, 0x64, 0x23, 0x70, 0x09, 0xc0, 0x7b, 0xfb, 0x8f, 0x08, 0xb5, 0x32, 0xbd, 0x97, 0xb1, 0x50, 0xdf, 0x37, - 0x92, 0xa0, 0x84, 0x66, 0x42, 0xe5, 0x58, 0x0a, 0xde, 0x79, 0xa4, 0x73, 0x52, 0x67, 0xe2, 0x3d, 0x88, 0xd3, 0xc2, - 0x07, 0xf6, 0x16, 0x04, 0xe7, 0x2c, 0xe8, 0x1d, 0xde, 0x66, 0xb5, 0xd4, 0x46, 0x0f, 0x14, 0xc0, 0xef, 0x06, 0x77, - 0x08, 0xf2, 0xd5, 0x18, 0xae, 0x95, 0xbc, 0x09, 0xf9, 0xb0, 0xa0, 0x07, 0x64, 0x60, 0x9f, 0xc5, 0x30, 0xa6, 0x07, - 0xe4, 0xd0, 0x3e, 0x4b, 0x37, 0x80, 0x03, 0xa9, 0x47, 0x95, 0x1e, 0x40, 0x83, 0x7e, 0xb7, 0x2d, 0xb2, 0x24, 0xeb, - 0xc7, 0xd2, 0x28, 0x62, 0xa0, 0x4a, 0x10, 0x51, 0x8b, 0x7f, 0x3e, 0x98, 0xeb, 0x0e, 0x73, 0x81, 0x30, 0x07, 0x03, - 0x0e, 0xe2, 0x36, 0x08, 0xcd, 0x01, 0xb3, 0xb9, 0x8d, 0x04, 0xbd, 0xb3, 0x86, 0x99, 0x1d, 0xfd, 0xe1, 0x56, 0x82, - 0x6f, 0xb2, 0xd6, 0xa8, 0xf3, 0xe2, 0x10, 0x08, 0x82, 0x37, 0x85, 0xaa, 0xf6, 0xaa, 0x07, 0x36, 0xde, 0xaa, 0x1f, - 0xdb, 0xed, 0x78, 0x2a, 0xdc, 0xb5, 0x5f, 0x50, 0x38, 0xf9, 0x94, 0xfc, 0xeb, 0xbd, 0xc9, 0xe0, 0xc0, 0xc8, 0xf0, - 0xa5, 0xb7, 0x7f, 0xe1, 0x6b, 0x2d, 0xdd, 0x13, 0x83, 0x92, 0x3c, 0x3e, 0x50, 0xf4, 0xef, 0x5e, 0x59, 0xf9, 0xd4, - 0x4e, 0xff, 0x76, 0x6b, 0xd6, 0xe7, 0xe1, 0x68, 0xb2, 0xdd, 0xf6, 0xb4, 0x81, 0x2b, 0xd5, 0x2a, 0x04, 0xec, 0x42, - 0x49, 0xf6, 0x0f, 0x20, 0x2a, 0x42, 0x33, 0xee, 0x66, 0xd9, 0x90, 0xc8, 0xf8, 0x71, 0x3a, 0xcb, 0x86, 0x60, 0x87, - 0x7b, 0x51, 0x89, 0xcb, 0x51, 0x6b, 0x83, 0xd3, 0xb3, 0x24, 0x84, 0x50, 0x0e, 0x58, 0xd9, 0xad, 0xfa, 0x73, 0xa7, - 0xcc, 0x84, 0xd4, 0x64, 0x75, 0x3b, 0xa5, 0x7b, 0x98, 0xe6, 0x7b, 0x66, 0x04, 0x07, 0xdc, 0xdb, 0x5f, 0xf5, 0xc7, - 0x30, 0xc9, 0x34, 0x39, 0x45, 0xf2, 0x8b, 0xf4, 0x14, 0x92, 0x76, 0xe8, 0xa9, 0x22, 0x80, 0x13, 0x6a, 0x3f, 0x86, - 0xdf, 0x30, 0xee, 0xdf, 0x35, 0x5f, 0xbb, 0xa9, 0x88, 0x9e, 0x52, 0x2c, 0x53, 0x93, 0xd3, 0x24, 0x2b, 0x12, 0x88, - 0xda, 0xa8, 0x9a, 0x11, 0x3d, 0x71, 0x31, 0x1f, 0x15, 0xe1, 0xf3, 0x6a, 0xfd, 0x9f, 0x21, 0x7c, 0x46, 0xe1, 0x06, - 0x70, 0x79, 0xc5, 0xe5, 0x79, 0xf8, 0xec, 0x29, 0xdd, 0x9b, 0x7c, 0x73, 0x40, 0xf7, 0x0e, 0x9e, 0x3c, 0x23, 0x00, - 0x8b, 0x76, 0x79, 0x1e, 0x1e, 0x3c, 0x7b, 0x46, 0xf7, 0xbe, 0xfd, 0x96, 0xee, 0x4d, 0x9e, 0x1c, 0x34, 0xd2, 0x26, - 0xcf, 0xbe, 0xa5, 0x7b, 0xdf, 0x3c, 0x6d, 0xa4, 0x1d, 0x8c, 0x9f, 0xd1, 0xbd, 0xbf, 0x7f, 0x63, 0xd2, 0xfe, 0x06, - 0xd9, 0xbe, 0x3d, 0xc0, 0xff, 0x4c, 0xda, 0xe4, 0xd9, 0x13, 0xba, 0x37, 0x19, 0x43, 0x25, 0xcf, 0x5c, 0x25, 0xe3, - 0x09, 0x7c, 0xfc, 0x04, 0xfe, 0xfb, 0x1b, 0x09, 0x16, 0xb4, 0x92, 0x2c, 0x17, 0xa8, 0x3f, 0x43, 0x11, 0x27, 0xaa, - 0x26, 0x12, 0x1e, 0x62, 0x66, 0xf5, 0x4d, 0x1c, 0x06, 0xc4, 0xa5, 0x43, 0x41, 0x74, 0x6f, 0x3c, 0x7a, 0x46, 0x02, - 0x1f, 0x9e, 0xee, 0xc6, 0x07, 0x19, 0xcb, 0xc5, 0x3c, 0xfb, 0x2a, 0x37, 0xb1, 0x15, 0x3c, 0x00, 0xab, 0x8f, 0x7e, - 0xae, 0x4a, 0xce, 0xb3, 0xaf, 0x2a, 0xb9, 0x9b, 0xeb, 0xf7, 0x16, 0xa0, 0xbc, 0xbf, 0x6a, 0xd9, 0x4d, 0xa1, 0x42, - 0xa7, 0xb5, 0x46, 0x9f, 0x7d, 0xc4, 0xf4, 0xc1, 0xc0, 0xbb, 0x61, 0xff, 0xb4, 0x53, 0x4e, 0xeb, 0x1b, 0x8d, 0x42, - 0x8d, 0xca, 0x43, 0xc2, 0x8e, 0xa0, 0xe8, 0xc1, 0x00, 0x78, 0x02, 0x0f, 0xf7, 0xed, 0xdf, 0x2c, 0xe3, 0x63, 0x47, - 0x19, 0xbf, 0xa0, 0x0c, 0x01, 0x8d, 0x7a, 0x98, 0xdd, 0xf4, 0xb0, 0xd1, 0xad, 0x5e, 0xb2, 0x54, 0x27, 0x53, 0xd3, - 0x33, 0xd8, 0xd7, 0xba, 0x96, 0x7b, 0x46, 0x14, 0x2d, 0x2f, 0xf6, 0x52, 0x3e, 0xab, 0xd8, 0x4f, 0x4b, 0x54, 0x6f, - 0x45, 0x8d, 0x37, 0x32, 0x9b, 0x55, 0xec, 0x7b, 0xf3, 0x06, 0xb8, 0x19, 0xf6, 0xbb, 0x7a, 0xf2, 0x03, 0x67, 0x70, - 0x69, 0xdb, 0xa3, 0x4c, 0x8c, 0x00, 0x2b, 0x20, 0x03, 0x07, 0x1e, 0x00, 0x1d, 0xf4, 0x47, 0x7b, 0xbb, 0x55, 0x29, - 0xcd, 0x3e, 0x5b, 0x18, 0x40, 0xc3, 0xbc, 0x4d, 0x5c, 0xd9, 0xff, 0x6a, 0xc8, 0x4b, 0x50, 0xb8, 0xd5, 0x2c, 0x6f, - 0xa7, 0x30, 0x84, 0x10, 0xfc, 0x71, 0xc9, 0x00, 0x70, 0x20, 0xc0, 0x60, 0xac, 0x65, 0x40, 0xcd, 0x96, 0x8f, 0x36, - 0x5c, 0xa9, 0x27, 0x81, 0x33, 0xb8, 0x90, 0x45, 0xc2, 0x4f, 0xb4, 0xd8, 0x1f, 0xad, 0x1f, 0x7d, 0xdf, 0x1e, 0x0f, - 0xd6, 0xbe, 0xc7, 0x47, 0xfa, 0xb3, 0xc6, 0x75, 0x60, 0xd3, 0xf2, 0x8d, 0x17, 0xb5, 0x95, 0x78, 0x94, 0xc0, 0x1b, - 0x98, 0x88, 0x14, 0x06, 0xa9, 0x16, 0x38, 0x06, 0xe5, 0x8d, 0x85, 0x58, 0xaa, 0xae, 0x6e, 0xb0, 0x05, 0x91, 0x21, - 0x78, 0xb8, 0xfd, 0x6b, 0xa9, 0x02, 0x47, 0xf5, 0xfb, 0x5c, 0xfa, 0x6e, 0x4f, 0xc6, 0x8e, 0x1c, 0xa7, 0x7e, 0x2a, - 0x1c, 0xfc, 0x37, 0xa9, 0x6b, 0x63, 0xb9, 0x92, 0x32, 0xcb, 0xb2, 0xb0, 0xa3, 0x50, 0xcb, 0x3d, 0x2a, 0x0f, 0x92, - 0x2f, 0xe4, 0x10, 0xc9, 0x02, 0xa3, 0x50, 0x90, 0xe1, 0x84, 0x8a, 0xd1, 0x5a, 0x94, 0xcb, 0xec, 0xa2, 0x0a, 0x37, - 0x4a, 0xa1, 0xcc, 0x29, 0xfa, 0x76, 0x83, 0x03, 0x09, 0x89, 0xb2, 0xf2, 0x6d, 0xfc, 0x36, 0x44, 0xb0, 0x3a, 0xae, - 0x6d, 0xa1, 0xb8, 0xb7, 0x3f, 0x79, 0xda, 0xc5, 0x1f, 0x19, 0x17, 0x50, 0x17, 0x8b, 0x69, 0x38, 0xb1, 0xb1, 0x6f, - 0xdc, 0x17, 0x56, 0xd3, 0x03, 0x50, 0xdf, 0xa5, 0x12, 0x23, 0xa8, 0xaf, 0x8c, 0x7d, 0x6c, 0x8f, 0x31, 0x39, 0x83, - 0x58, 0xc3, 0x2a, 0x67, 0xa6, 0xfa, 0x46, 0xd8, 0x11, 0x00, 0x37, 0x42, 0x6b, 0x14, 0x04, 0x1e, 0xaf, 0x42, 0x3c, - 0x2f, 0x55, 0xf8, 0xd6, 0x8c, 0xd0, 0x31, 0x78, 0x53, 0xd9, 0x46, 0x66, 0xd2, 0x17, 0x0c, 0x9a, 0x63, 0x5b, 0x47, - 0x61, 0xb5, 0x95, 0x65, 0x47, 0x00, 0x37, 0x90, 0x1d, 0x9a, 0x8b, 0xe7, 0xac, 0x9a, 0x67, 0x8b, 0xc8, 0x04, 0x05, - 0x5c, 0x0a, 0xcb, 0xa0, 0x7d, 0xba, 0x47, 0xb6, 0xe3, 0x10, 0xba, 0xe1, 0x3e, 0x82, 0xf1, 0xb4, 0x9b, 0x82, 0x15, - 0x44, 0x23, 0xc4, 0xc3, 0x8c, 0x59, 0x7c, 0xaf, 0x34, 0xe5, 0xa9, 0x6a, 0x09, 0x04, 0x8e, 0x42, 0xa8, 0x8b, 0x5d, - 0xa3, 0x04, 0x97, 0xa9, 0x11, 0xcc, 0x60, 0xc7, 0x8e, 0xd4, 0x76, 0xc9, 0x39, 0x1d, 0xaa, 0x29, 0x2d, 0xf5, 0x94, - 0x6a, 0x5f, 0x43, 0x31, 0x2f, 0xd1, 0x43, 0x0f, 0x5c, 0x0f, 0xb4, 0x43, 0x5e, 0x49, 0x27, 0x26, 0x82, 0x4e, 0xab, - 0x4d, 0xd8, 0xb9, 0x91, 0x6e, 0x59, 0x8d, 0xbc, 0x63, 0x68, 0x76, 0xc4, 0x4b, 0x3f, 0x50, 0x17, 0x40, 0x84, 0xdc, - 0xdb, 0x22, 0x73, 0x44, 0xb3, 0xac, 0x7c, 0x05, 0x65, 0x71, 0xc4, 0xd6, 0x15, 0x70, 0x2d, 0x05, 0x93, 0x4b, 0x1e, - 0xf1, 0x14, 0x11, 0x01, 0x8f, 0x95, 0x76, 0x7d, 0xa7, 0x25, 0x84, 0x66, 0x29, 0x10, 0x37, 0x17, 0xc5, 0xb9, 0xb6, - 0x81, 0x2c, 0x80, 0xbe, 0xfd, 0x9c, 0x5d, 0x79, 0xe1, 0x60, 0x37, 0x57, 0x99, 0x78, 0xc1, 0x2f, 0x32, 0xc1, 0x53, - 0x04, 0xbb, 0xba, 0x35, 0x0f, 0xdc, 0xb1, 0x6d, 0x60, 0xf9, 0xf6, 0x1d, 0x2c, 0x98, 0x32, 0xd4, 0x4a, 0x89, 0x4c, - 0x44, 0x02, 0x32, 0xfb, 0xcc, 0xdd, 0x9b, 0x4c, 0xbc, 0x89, 0x6f, 0xc1, 0x9b, 0xa2, 0xc1, 0x4f, 0x8f, 0xce, 0xf1, - 0x4b, 0x44, 0x12, 0x85, 0x18, 0xb6, 0x18, 0x11, 0x0b, 0x91, 0x63, 0xc7, 0x84, 0x72, 0x25, 0x68, 0x6d, 0x0d, 0x81, - 0x17, 0x7f, 0x5a, 0x75, 0xef, 0x2a, 0x13, 0xc6, 0x3e, 0xe3, 0x2a, 0xbe, 0x65, 0xa5, 0x02, 0xb3, 0xc0, 0x38, 0xf7, - 0x6d, 0x29, 0xc9, 0x55, 0x26, 0x8c, 0x80, 0xe4, 0x2a, 0xbe, 0xa5, 0x4d, 0x19, 0x87, 0xb6, 0xa2, 0xf3, 0xe2, 0xfc, - 0xee, 0x0e, 0xbf, 0xc4, 0x50, 0x2b, 0xe3, 0x7e, 0x1f, 0x24, 0x66, 0xd2, 0x36, 0x65, 0x26, 0x23, 0xa9, 0xd1, 0x42, - 0x2a, 0xca, 0x07, 0x13, 0xb2, 0xbb, 0x52, 0x2d, 0x23, 0x6a, 0xbf, 0x0a, 0xc5, 0x6c, 0x1c, 0x4d, 0x08, 0x9d, 0x74, - 0xac, 0x77, 0xd3, 0x5a, 0xc8, 0x34, 0x7a, 0x16, 0x79, 0x3e, 0x9d, 0x05, 0xab, 0xa6, 0xc5, 0x21, 0xe3, 0xd3, 0x62, - 0x30, 0x20, 0xda, 0xa5, 0x70, 0x83, 0xf5, 0x80, 0x29, 0x8d, 0x8b, 0xb7, 0x66, 0x5a, 0xfd, 0x4a, 0xaa, 0x90, 0xf4, - 0x9e, 0x01, 0x49, 0x26, 0x5d, 0xb0, 0x5b, 0x90, 0x28, 0x7a, 0xfe, 0x77, 0x6a, 0x0b, 0xee, 0x7a, 0x30, 0x36, 0xa3, - 0xfb, 0x7a, 0xc6, 0x7f, 0xa8, 0x6d, 0x41, 0xd4, 0xa7, 0x92, 0xf5, 0x3a, 0x12, 0x55, 0xc8, 0x45, 0xf8, 0xd9, 0xd1, - 0x10, 0x43, 0x54, 0x7b, 0x2c, 0x10, 0xeb, 0xab, 0x73, 0x5e, 0xe0, 0xf4, 0x33, 0x77, 0xb9, 0x82, 0x6d, 0x41, 0x2b, - 0x43, 0xa3, 0xde, 0xc6, 0x6f, 0x23, 0x7b, 0x59, 0xd0, 0x45, 0xbe, 0x40, 0x21, 0x6b, 0x1e, 0x86, 0xd5, 0xb0, 0x3d, - 0x88, 0x64, 0xbf, 0x3d, 0x09, 0x8d, 0xc6, 0xc0, 0x02, 0xd9, 0xa1, 0x11, 0xb8, 0x08, 0xad, 0xfc, 0xed, 0x10, 0x5c, - 0xb8, 0x2c, 0x22, 0xcb, 0x50, 0xc7, 0x6f, 0x6a, 0x37, 0x41, 0xf5, 0x0a, 0x9d, 0xa6, 0xb0, 0x2a, 0x65, 0x92, 0x0f, - 0xbf, 0x5e, 0xc9, 0x02, 0x33, 0x79, 0x5d, 0xf6, 0xe8, 0x6b, 0xbb, 0xbd, 0x03, 0x53, 0xb0, 0xee, 0x93, 0xf7, 0xf5, - 0xe3, 0xce, 0x9e, 0x80, 0x51, 0xac, 0xca, 0xd1, 0x14, 0x52, 0x6a, 0x1f, 0x94, 0xfa, 0x63, 0xb8, 0x14, 0x9a, 0x63, - 0xb7, 0x80, 0x49, 0xc0, 0x3e, 0x43, 0xaa, 0xc7, 0xb4, 0x63, 0x9f, 0xa3, 0x0d, 0x2c, 0x09, 0x38, 0xfc, 0xa3, 0x4c, - 0xd6, 0xfe, 0xd5, 0x5d, 0xa4, 0xcd, 0x90, 0x2d, 0xf3, 0x05, 0xf0, 0xf9, 0xb0, 0x6b, 0xa3, 0x12, 0x65, 0x13, 0x91, - 0xa4, 0xb0, 0xe5, 0x31, 0x48, 0x7b, 0x14, 0xd3, 0x55, 0xc1, 0x93, 0x0c, 0xa5, 0x14, 0x89, 0xf6, 0x09, 0xce, 0xe1, - 0x0d, 0xee, 0x47, 0x15, 0x10, 0x5e, 0x85, 0x9c, 0x8e, 0x52, 0xaa, 0x2d, 0x60, 0x14, 0xf5, 0x00, 0x51, 0x5e, 0x06, - 0x72, 0xbc, 0xed, 0x76, 0x42, 0x57, 0x6c, 0x39, 0x9c, 0x50, 0x24, 0x25, 0x97, 0x58, 0xee, 0x15, 0xe8, 0x3c, 0xce, - 0x59, 0xef, 0x25, 0x60, 0x11, 0x9c, 0xc1, 0xdf, 0x98, 0xd0, 0x6b, 0xf8, 0x9b, 0x13, 0xfa, 0x86, 0x85, 0x57, 0xc3, - 0x4b, 0xb2, 0x1f, 0xa6, 0x83, 0x89, 0x12, 0x8c, 0xdd, 0xb1, 0x65, 0x19, 0xaa, 0xc4, 0xd5, 0xfe, 0x05, 0x79, 0x7c, - 0x41, 0x6f, 0xe9, 0x0d, 0x3d, 0xa5, 0x27, 0x40, 0xf8, 0xef, 0x0e, 0x27, 0x7c, 0x38, 0x79, 0xda, 0xef, 0xf7, 0xce, - 0xfb, 0xfd, 0xde, 0x99, 0x31, 0xa0, 0xd0, 0xbb, 0xe8, 0xb2, 0xa6, 0xfa, 0xd7, 0x55, 0xbd, 0x98, 0x9e, 0xa8, 0x8d, - 0x9b, 0xf0, 0x2c, 0x0f, 0xaf, 0xf6, 0xef, 0xc8, 0x10, 0x1f, 0x2f, 0x72, 0x29, 0x8b, 0xf0, 0x72, 0xff, 0x8e, 0xd0, - 0x93, 0x23, 0xd0, 0x9b, 0x62, 0x7d, 0x27, 0x8f, 0xef, 0x74, 0x6d, 0x84, 0xbe, 0x0c, 0x13, 0xd8, 0x26, 0xb7, 0xcc, - 0xde, 0xb5, 0x27, 0x63, 0x88, 0x65, 0x72, 0xe7, 0x95, 0x77, 0xf7, 0xf8, 0x96, 0xec, 0xdf, 0x82, 0xa7, 0xa8, 0x25, - 0x7f, 0xb3, 0xf0, 0x86, 0xb5, 0x6a, 0x78, 0x7c, 0x47, 0x4f, 0x5b, 0x8d, 0x78, 0x7c, 0x47, 0xa2, 0xf0, 0x86, 0x5d, - 0xd2, 0x53, 0x76, 0x45, 0xe8, 0x79, 0xbf, 0x7f, 0xd6, 0xef, 0xcb, 0x7e, 0xff, 0xa7, 0x38, 0x0c, 0xe3, 0x61, 0x41, - 0xf6, 0x25, 0xbd, 0xdb, 0x9f, 0xf0, 0x27, 0x64, 0x16, 0xea, 0xe6, 0xab, 0x05, 0x67, 0x55, 0xde, 0x2a, 0xd7, 0x1d, - 0x05, 0x6b, 0x85, 0x3b, 0xa6, 0x9e, 0x4e, 0xe8, 0x0d, 0x2b, 0xe8, 0x29, 0x8b, 0x49, 0x74, 0x0d, 0xad, 0x38, 0x9f, - 0x15, 0xd1, 0x0d, 0x3d, 0x65, 0x67, 0xb3, 0x38, 0x3a, 0xa5, 0x27, 0x2c, 0x1f, 0x4e, 0x20, 0xef, 0xe9, 0xf0, 0x86, - 0xec, 0x9f, 0x90, 0x28, 0x3c, 0xd1, 0xbf, 0xef, 0xe8, 0x25, 0x0f, 0x4f, 0xa8, 0x57, 0xcd, 0x09, 0x31, 0xd5, 0x37, - 0x6a, 0x3f, 0x21, 0x91, 0x3f, 0x98, 0x27, 0xd6, 0x9e, 0xe6, 0x91, 0xa3, 0x8d, 0x69, 0x19, 0x82, 0xbe, 0xb9, 0x0c, - 0x6f, 0x08, 0x99, 0x36, 0xc7, 0x0e, 0x06, 0x74, 0xf6, 0x28, 0x4a, 0x08, 0xbd, 0xf1, 0x4b, 0xbd, 0xc1, 0x31, 0x34, - 0x23, 0xa4, 0xd2, 0x4e, 0x31, 0x0d, 0xd7, 0xc1, 0x6b, 0x0d, 0xd6, 0x71, 0xde, 0xef, 0x87, 0xeb, 0x7e, 0x1f, 0x22, - 0xdd, 0x17, 0x33, 0x13, 0xdb, 0xcd, 0x91, 0x4d, 0x7a, 0x03, 0xda, 0xff, 0xd7, 0x83, 0x01, 0x74, 0xc6, 0x2b, 0x29, - 0xbc, 0x19, 0xbc, 0x7e, 0x7c, 0x47, 0x54, 0x1d, 0x05, 0x15, 0x32, 0x2c, 0xe8, 0x1b, 0x9a, 0x01, 0xe0, 0xd7, 0xeb, - 0xc1, 0x80, 0x44, 0xe6, 0x33, 0x32, 0x7d, 0x7d, 0x78, 0x32, 0x1d, 0x0c, 0x5e, 0x9b, 0x6d, 0xf2, 0x96, 0xdd, 0x53, - 0x0a, 0xac, 0xbf, 0xb3, 0x7e, 0xff, 0xed, 0x51, 0x4c, 0xce, 0x0b, 0x1e, 0x7f, 0x9c, 0x36, 0xdb, 0xf2, 0xd6, 0x45, - 0x55, 0x3b, 0xeb, 0xf7, 0xd7, 0xfd, 0xfe, 0x29, 0x60, 0x17, 0xcd, 0x9c, 0xaf, 0x27, 0x48, 0x5b, 0xe6, 0x8e, 0x22, - 0x69, 0x92, 0x43, 0x63, 0x68, 0x5b, 0xac, 0xda, 0x36, 0xeb, 0xc8, 0xc0, 0xe2, 0xa8, 0x59, 0x51, 0x5c, 0x93, 0x28, - 0xec, 0x9d, 0x6d, 0xb7, 0xa7, 0x8c, 0xb1, 0x98, 0x80, 0xf4, 0xc3, 0x7f, 0x7d, 0x5a, 0x37, 0x62, 0x88, 0x09, 0x89, - 0xcc, 0xe6, 0x66, 0x69, 0x0f, 0x81, 0x88, 0xc3, 0xa6, 0x7f, 0x6f, 0xee, 0xe5, 0xa2, 0x76, 0x7c, 0xeb, 0x2f, 0x00, - 0x42, 0x24, 0x59, 0xc8, 0x67, 0x38, 0x06, 0x65, 0x06, 0x40, 0xe6, 0x91, 0x9a, 0x79, 0x09, 0x20, 0xc0, 0x64, 0xbb, - 0x1d, 0x8d, 0xc7, 0x13, 0x5a, 0xb0, 0xd1, 0xdf, 0x9e, 0x3d, 0xae, 0x1e, 0x87, 0x41, 0x30, 0xc8, 0x48, 0x4b, 0x4f, - 0x61, 0x17, 0x6b, 0xb5, 0x0f, 0x46, 0xf0, 0x9a, 0x7d, 0xbc, 0xce, 0xbe, 0x98, 0x7d, 0x44, 0xc2, 0xda, 0x60, 0x1c, - 0xb9, 0x48, 0x5b, 0x7a, 0xbb, 0x7b, 0x18, 0x4c, 0x2e, 0xd2, 0xcf, 0xb0, 0x9d, 0x3e, 0xff, 0xe6, 0xc1, 0x78, 0xc2, - 0xc1, 0xe8, 0x2e, 0x0a, 0xfa, 0x4c, 0xdb, 0x6e, 0x2b, 0xff, 0x12, 0xf8, 0x16, 0x53, 0x41, 0xc7, 0x66, 0x59, 0xb8, - 0x41, 0x45, 0xd4, 0xd1, 0x32, 0xa8, 0x6a, 0x65, 0x3b, 0x07, 0xd4, 0x12, 0xab, 0x32, 0x71, 0x0b, 0x0c, 0x43, 0x86, - 0xba, 0xdc, 0xe3, 0xea, 0x5f, 0xbc, 0x90, 0x06, 0x3e, 0xc3, 0x89, 0x08, 0x3d, 0x6e, 0x8d, 0xfb, 0xdc, 0x9a, 0xf8, - 0x0c, 0xb7, 0x56, 0x22, 0x89, 0x35, 0xb0, 0xa4, 0xe6, 0x72, 0x94, 0xb0, 0xa3, 0x92, 0xf1, 0x59, 0x19, 0x25, 0x34, - 0x86, 0x07, 0xc9, 0xc4, 0x4c, 0x46, 0x09, 0xda, 0x27, 0xba, 0x08, 0x83, 0x7f, 0x01, 0x66, 0x3f, 0xcd, 0xe1, 0xaf, - 0x24, 0xd3, 0xe4, 0x10, 0x02, 0x42, 0x1c, 0x8e, 0x67, 0x71, 0x38, 0x26, 0x51, 0x72, 0x04, 0x4f, 0xf0, 0x5f, 0x11, - 0x8e, 0x49, 0xad, 0xef, 0x30, 0x52, 0x5d, 0x6e, 0x13, 0x06, 0x70, 0x65, 0xe3, 0xd9, 0x24, 0xb2, 0xd2, 0x5d, 0xf9, - 0x78, 0x34, 0x7e, 0x46, 0xa6, 0x71, 0x28, 0x07, 0x09, 0xa1, 0xe0, 0xdd, 0x1b, 0x96, 0xc3, 0x44, 0xc3, 0xb3, 0x01, - 0x9b, 0x57, 0x3a, 0x36, 0x4f, 0xc2, 0x09, 0x08, 0xc3, 0x84, 0x1c, 0xeb, 0x3d, 0x48, 0x29, 0xfa, 0x3c, 0xc7, 0x7e, - 0xea, 0x23, 0x08, 0xb3, 0xa3, 0x96, 0x8a, 0xaf, 0x00, 0xe8, 0x12, 0x07, 0x87, 0xda, 0x33, 0x5f, 0xcc, 0xc2, 0xd2, - 0xa3, 0x52, 0xa6, 0xba, 0x7d, 0xd1, 0xa0, 0xfc, 0xa6, 0x41, 0xfb, 0x82, 0x0c, 0x26, 0xb4, 0x3c, 0x9a, 0xf0, 0x27, - 0x10, 0xc0, 0xa3, 0x11, 0xf1, 0x4b, 0xe1, 0xc4, 0x40, 0x78, 0x15, 0x64, 0xa0, 0xd2, 0x5a, 0x35, 0x66, 0x64, 0x2b, - 0xde, 0x83, 0x30, 0x29, 0x7b, 0x37, 0x72, 0x9d, 0xa7, 0x10, 0x15, 0x6c, 0x9d, 0x57, 0x7b, 0x97, 0x60, 0xc9, 0x1e, - 0x57, 0x10, 0x27, 0x6c, 0xbd, 0x02, 0xec, 0xdc, 0x47, 0x9b, 0xb2, 0xde, 0x53, 0xdf, 0xed, 0x61, 0xcb, 0xe1, 0x55, - 0x25, 0xf7, 0x26, 0xe3, 0xf1, 0x78, 0xf4, 0x07, 0x1c, 0x1d, 0x40, 0x68, 0x49, 0x64, 0xf8, 0x64, 0x80, 0xc6, 0x5d, - 0x57, 0xdc, 0x1b, 0x17, 0x8a, 0xb2, 0xd2, 0xc9, 0x84, 0x80, 0xf8, 0xd9, 0xf4, 0x0d, 0xf6, 0x15, 0xd7, 0xf1, 0x4f, - 0x76, 0x3f, 0x31, 0x2b, 0x5a, 0xad, 0xd4, 0xd1, 0xbb, 0x93, 0xd3, 0xd7, 0x1f, 0x5e, 0xff, 0xf6, 0xf2, 0xec, 0xf5, - 0xdb, 0x57, 0xaf, 0xdf, 0xbe, 0xfe, 0xf0, 0xcf, 0x07, 0x18, 0x6c, 0xdf, 0x56, 0xc4, 0x8e, 0xbd, 0x77, 0x8f, 0xf1, - 0x6a, 0xf1, 0x85, 0xb3, 0x07, 0xee, 0x16, 0x0b, 0xb0, 0x09, 0x86, 0x5b, 0x10, 0x54, 0x33, 0x1a, 0x95, 0xbe, 0x27, - 0x20, 0xa3, 0x51, 0x21, 0x1b, 0x0f, 0x2b, 0xb6, 0x42, 0x2e, 0xde, 0x31, 0x1c, 0x7c, 0x64, 0x7f, 0x2b, 0xce, 0x84, - 0xdb, 0xd1, 0xd6, 0xac, 0x08, 0xf8, 0x7c, 0xad, 0x45, 0xe5, 0x71, 0x21, 0x6a, 0x6f, 0xdb, 0xe7, 0x90, 0x50, 0x8f, - 0xc8, 0x75, 0xf0, 0xbe, 0x0d, 0xb2, 0xc7, 0x47, 0xde, 0x93, 0xf2, 0x0c, 0xf5, 0x39, 0x1a, 0x3e, 0x6a, 0x3c, 0xa3, - 0x13, 0x73, 0x6d, 0x74, 0xa8, 0x67, 0x05, 0xec, 0x6f, 0x25, 0xc6, 0x86, 0x68, 0x0f, 0x29, 0x62, 0x7d, 0x38, 0xdd, - 0xef, 0xee, 0xcd, 0xe8, 0x7b, 0x38, 0x7e, 0x94, 0x6a, 0x02, 0x69, 0x51, 0xa0, 0x74, 0x65, 0xc8, 0x6d, 0xcf, 0xc2, - 0xc2, 0xfc, 0x0c, 0x1b, 0x04, 0xd0, 0x5e, 0x76, 0x2c, 0x09, 0x34, 0x8b, 0xd7, 0xba, 0xfe, 0x79, 0xf9, 0x32, 0xd1, - 0xce, 0x17, 0xdf, 0x42, 0x88, 0x61, 0xff, 0x8a, 0xd0, 0x98, 0x70, 0x37, 0xc9, 0xee, 0xd2, 0x62, 0xee, 0x55, 0x57, - 0x31, 0x1e, 0x77, 0xf7, 0x5c, 0x29, 0x9a, 0xb7, 0x2e, 0xb0, 0x07, 0x6a, 0x5e, 0xc7, 0x4b, 0x16, 0x02, 0x36, 0xe3, - 0xbe, 0x5d, 0x24, 0xce, 0xef, 0x9d, 0x4e, 0xc8, 0xfe, 0xc1, 0x94, 0x0f, 0x59, 0x49, 0xc5, 0x80, 0x95, 0xf5, 0x0e, - 0x35, 0xe7, 0x6d, 0x42, 0x2e, 0x76, 0x69, 0xb8, 0x18, 0xf2, 0x87, 0x2e, 0x49, 0x1f, 0x78, 0xc3, 0xa1, 0xda, 0x36, - 0x17, 0x43, 0x9a, 0x72, 0xba, 0x4b, 0x65, 0x40, 0x88, 0x74, 0x15, 0x57, 0xa4, 0xd6, 0x47, 0x55, 0xea, 0x24, 0x1d, - 0xd7, 0xd9, 0xe6, 0x33, 0x97, 0x6c, 0x75, 0xbb, 0xf6, 0xaf, 0xd5, 0xed, 0x0b, 0x33, 0x90, 0xbf, 0x3f, 0x11, 0xd5, - 0xc4, 0x40, 0x74, 0x01, 0x15, 0xfc, 0x13, 0xbc, 0x3c, 0x79, 0xa4, 0x15, 0xa0, 0xf7, 0x9d, 0x1d, 0x5d, 0x7b, 0xbc, - 0x31, 0x8b, 0xad, 0x25, 0xce, 0x59, 0xe5, 0x3b, 0xcb, 0xab, 0xb2, 0x15, 0xba, 0x8e, 0x60, 0xbf, 0x84, 0x1d, 0x7d, - 0xf7, 0xb6, 0x01, 0x10, 0xa5, 0xb0, 0x72, 0x67, 0xbf, 0xf0, 0xce, 0x7e, 0x61, 0xcf, 0x7e, 0xbb, 0x09, 0x94, 0x0f, - 0x2b, 0xb4, 0xec, 0x95, 0x14, 0x95, 0x69, 0xf2, 0xb8, 0xa9, 0xcb, 0x42, 0x5a, 0xcc, 0xf7, 0x2d, 0xed, 0x7a, 0x3a, - 0xa6, 0x12, 0xd5, 0x23, 0x3f, 0x60, 0xab, 0xf6, 0x4b, 0xf2, 0xf0, 0x3d, 0xf3, 0x7f, 0xf6, 0x06, 0x79, 0xdf, 0xdd, - 0xee, 0xff, 0xe6, 0x42, 0x07, 0xb7, 0xb5, 0x54, 0x78, 0xea, 0xea, 0xb8, 0xc0, 0xbb, 0x5a, 0xfa, 0xf0, 0x5d, 0xed, - 0x5d, 0xa6, 0x97, 0x5d, 0x05, 0xa8, 0x41, 0x62, 0x7d, 0xc5, 0x8b, 0x2c, 0xa9, 0xad, 0x42, 0xe3, 0x84, 0x43, 0x68, - 0x0f, 0xef, 0xe0, 0x02, 0x39, 0x2c, 0x21, 0xf4, 0x63, 0x65, 0x04, 0x80, 0x3e, 0x8b, 0x7d, 0xc2, 0xc3, 0x8c, 0x0c, - 0x7c, 0x89, 0x5f, 0x29, 0x7d, 0x71, 0xf1, 0xfe, 0x4e, 0x66, 0x82, 0x5e, 0x25, 0x36, 0xbb, 0x94, 0xed, 0x98, 0x1f, - 0xfe, 0x17, 0x18, 0x0d, 0xc2, 0x6b, 0x4b, 0xb6, 0x2f, 0x3a, 0x66, 0xb9, 0x82, 0xa3, 0xb6, 0x74, 0x65, 0x96, 0xad, - 0xeb, 0x67, 0x35, 0xcc, 0xf4, 0x99, 0x72, 0x02, 0xb2, 0x2f, 0xe4, 0xee, 0xa7, 0xba, 0x62, 0x41, 0x8e, 0x26, 0xe3, - 0x29, 0x11, 0x83, 0x41, 0x2b, 0xf9, 0x10, 0x93, 0x87, 0xc3, 0x1d, 0xe6, 0x52, 0xe8, 0x7e, 0x78, 0x7d, 0x80, 0xfa, - 0x1a, 0x5b, 0x92, 0x6c, 0x2a, 0xf6, 0x17, 0x98, 0xc5, 0x02, 0x71, 0x74, 0xf0, 0x8b, 0xf3, 0x05, 0x80, 0x2c, 0xc3, - 0x32, 0xd3, 0xc2, 0xa2, 0x32, 0x55, 0x3e, 0xb2, 0x05, 0x93, 0x87, 0xe3, 0x99, 0xdf, 0x73, 0xc7, 0xe0, 0x10, 0x12, - 0x4d, 0xac, 0xf1, 0x8b, 0x9f, 0x05, 0xe3, 0x38, 0x94, 0x47, 0xb2, 0xf1, 0x5d, 0x49, 0xa2, 0xb1, 0x31, 0x55, 0xd6, - 0x57, 0x89, 0x6a, 0x98, 0x90, 0xc7, 0x05, 0xd9, 0x2f, 0xe8, 0xd2, 0x1f, 0x4b, 0x4c, 0xdf, 0x8f, 0xf7, 0x27, 0x63, - 0xf2, 0x38, 0x7e, 0x3c, 0x31, 0x70, 0xc3, 0x7e, 0x8e, 0x7c, 0xb8, 0x24, 0xfb, 0xcd, 0x2a, 0xc1, 0x14, 0xd5, 0xf4, - 0xcc, 0xaf, 0x24, 0x19, 0x2c, 0x07, 0xe9, 0xe3, 0x56, 0x5e, 0xac, 0x55, 0x8f, 0xf7, 0xfa, 0x90, 0x4f, 0x89, 0x68, - 0xdc, 0x18, 0xd6, 0xf4, 0x2a, 0xfe, 0x53, 0x16, 0x51, 0x29, 0x01, 0x91, 0x10, 0xd4, 0xdb, 0xd9, 0x45, 0x96, 0xc4, - 0x22, 0x8d, 0xd2, 0x9a, 0xd0, 0xf4, 0x88, 0x4d, 0xc6, 0xb3, 0x94, 0xa5, 0x87, 0x93, 0x67, 0xb3, 0xc9, 0xb3, 0xe8, - 0x60, 0x1c, 0xa5, 0x83, 0x01, 0x24, 0x1f, 0x8c, 0xc1, 0xc5, 0x0e, 0x7e, 0xb3, 0x03, 0x18, 0xba, 0x23, 0x64, 0x09, - 0x0b, 0x68, 0xda, 0x97, 0x35, 0x49, 0x0f, 0xe7, 0x85, 0xea, 0x49, 0x7c, 0x4b, 0xd7, 0x9e, 0x83, 0x8b, 0xdf, 0xc2, - 0x0b, 0xd7, 0xc2, 0x8b, 0xdd, 0x16, 0x0a, 0x4d, 0xb6, 0x0b, 0xf9, 0xff, 0xe3, 0x86, 0x71, 0xdf, 0x5d, 0xc2, 0x2c, - 0xae, 0xeb, 0x6c, 0xb4, 0x2a, 0x64, 0x25, 0xe1, 0x36, 0xa1, 0x44, 0x61, 0xa3, 0x78, 0xb5, 0xca, 0xb5, 0x8b, 0xd8, - 0xbc, 0xa2, 0x00, 0xee, 0x02, 0x71, 0x8a, 0x81, 0x85, 0x36, 0x06, 0x72, 0x9f, 0x78, 0x21, 0x99, 0x55, 0xfb, 0x98, - 0x7b, 0xe4, 0x9f, 0x21, 0x18, 0xa3, 0x8a, 0xa3, 0xf1, 0x4c, 0x61, 0x5d, 0x7c, 0x4e, 0xde, 0xfb, 0x6f, 0x1c, 0x45, - 0xf6, 0x68, 0x06, 0x3d, 0x41, 0xe4, 0x3c, 0xe2, 0xec, 0xc9, 0xe4, 0x65, 0xe0, 0x7e, 0x06, 0x2b, 0xfd, 0x75, 0xb7, - 0x19, 0x6b, 0xdb, 0xa3, 0x7b, 0x61, 0x84, 0xa2, 0x9f, 0xf0, 0x9d, 0xa9, 0x17, 0x70, 0x09, 0xd5, 0xc0, 0xae, 0x2f, - 0x2f, 0x79, 0x09, 0x20, 0x42, 0x99, 0xe8, 0xf7, 0x7b, 0x7f, 0x1a, 0x68, 0xd2, 0x92, 0x17, 0x6f, 0x32, 0x61, 0x9d, - 0x71, 0xa0, 0xa9, 0x40, 0xfd, 0x3f, 0x56, 0xf6, 0x99, 0x8e, 0xc9, 0xcc, 0x7f, 0x1c, 0x4e, 0x48, 0xd4, 0x7c, 0x4d, - 0x3e, 0x73, 0x9a, 0x7e, 0xe6, 0x8a, 0xf6, 0x1f, 0xc8, 0xcc, 0x0d, 0x87, 0x0c, 0xf5, 0x97, 0x8e, 0x79, 0x32, 0x7a, - 0x9d, 0x98, 0x1d, 0x09, 0x56, 0xcd, 0x20, 0x0a, 0x7b, 0x01, 0x0f, 0xea, 0x5a, 0x16, 0x4f, 0x61, 0xf6, 0x41, 0x8d, - 0x28, 0x0e, 0xd9, 0x78, 0x16, 0xca, 0x70, 0x02, 0xf6, 0xbd, 0x93, 0x31, 0xdc, 0x07, 0x64, 0xf8, 0xb1, 0x0a, 0xb1, - 0x73, 0x90, 0xf6, 0xb1, 0x42, 0xc5, 0x04, 0x40, 0x04, 0x42, 0xde, 0x7e, 0x5f, 0xaa, 0x24, 0x7c, 0x5d, 0x62, 0x4a, - 0xa1, 0x3e, 0xf8, 0x4f, 0xa4, 0xea, 0x8e, 0xe9, 0x57, 0xeb, 0xc7, 0x9f, 0x09, 0xc5, 0xa7, 0xbb, 0x94, 0xf8, 0x16, - 0x82, 0x3b, 0x4b, 0xd0, 0x41, 0x54, 0x68, 0xc6, 0xf6, 0x30, 0xbf, 0x2b, 0xee, 0xe7, 0x77, 0xc5, 0xff, 0x3b, 0x7e, - 0x57, 0x3c, 0xc4, 0x18, 0x56, 0x16, 0x1a, 0x7e, 0x16, 0x8c, 0x83, 0xe8, 0x3f, 0xe7, 0x13, 0xef, 0xe5, 0xa9, 0xaf, - 0x32, 0x31, 0xbd, 0x87, 0x69, 0xf6, 0x09, 0x0a, 0xc2, 0x2a, 0xee, 0xd2, 0x93, 0x75, 0x65, 0x6f, 0xad, 0x64, 0x88, - 0x79, 0x1e, 0x60, 0x8d, 0xc2, 0xca, 0x03, 0xba, 0x47, 0xd5, 0x06, 0x71, 0x22, 0x78, 0x18, 0x33, 0x2b, 0x7d, 0xdf, - 0x6e, 0x8d, 0x0a, 0xf3, 0x41, 0x2e, 0x0a, 0xb2, 0x9b, 0x8f, 0x67, 0xe3, 0x28, 0xc4, 0x06, 0xfc, 0xc7, 0x8c, 0x55, - 0x43, 0x36, 0xdf, 0xc9, 0x48, 0xed, 0x98, 0x3c, 0x4d, 0x76, 0x49, 0xef, 0x80, 0x77, 0xc8, 0xcf, 0xeb, 0x8f, 0x61, - 0x21, 0x0d, 0xbf, 0x25, 0x2f, 0xe3, 0x22, 0xab, 0x96, 0x57, 0x59, 0x82, 0x4c, 0x17, 0xbc, 0xf8, 0x62, 0xa6, 0xcb, - 0xfb, 0x58, 0x1f, 0x30, 0x9e, 0x52, 0xbc, 0x6e, 0x88, 0xd2, 0xd7, 0x2d, 0xcf, 0x0a, 0x75, 0x79, 0x52, 0x31, 0xdb, - 0xb3, 0x12, 0x9c, 0x4e, 0xc1, 0x04, 0x5f, 0xff, 0x74, 0xbd, 0x8f, 0x01, 0x17, 0x14, 0x6a, 0x4e, 0x0b, 0xb9, 0x32, - 0x58, 0x4e, 0x16, 0xba, 0x13, 0x30, 0x43, 0xa5, 0xc0, 0x0b, 0x14, 0xfc, 0x45, 0x03, 0x23, 0xfa, 0xca, 0xfd, 0x26, - 0x03, 0x83, 0x74, 0x69, 0x4e, 0x84, 0xb1, 0xe3, 0x76, 0x8a, 0xb4, 0x15, 0xe5, 0x8c, 0xb3, 0xf7, 0xea, 0x4a, 0x01, - 0x06, 0x78, 0x9b, 0x9b, 0xe8, 0x3c, 0x41, 0xaf, 0x05, 0xa5, 0xf3, 0x06, 0xee, 0x66, 0x19, 0x19, 0xe1, 0xe2, 0xe3, - 0xca, 0x63, 0xc1, 0x3d, 0xfb, 0x85, 0x58, 0x1a, 0xcd, 0x34, 0x18, 0xb3, 0x79, 0xc1, 0x02, 0x85, 0x0a, 0x14, 0x58, - 0xce, 0xb4, 0xa5, 0x69, 0x35, 0xe4, 0xfb, 0x07, 0x68, 0x6d, 0x5a, 0x0d, 0xf8, 0xfe, 0x41, 0x1d, 0x65, 0x87, 0x90, - 0xe5, 0xc8, 0xcf, 0xa0, 0x5e, 0xd7, 0x91, 0x49, 0x31, 0xd9, 0xfd, 0xfa, 0x52, 0x7f, 0x54, 0x37, 0xe0, 0xfa, 0x01, - 0x08, 0x60, 0x03, 0x70, 0x08, 0x54, 0x83, 0xa5, 0x11, 0xc1, 0xa2, 0x4c, 0xa1, 0x7d, 0x0d, 0xbd, 0x37, 0x1a, 0xfe, - 0x0b, 0xdc, 0x45, 0xe4, 0xca, 0xff, 0x04, 0x81, 0xbf, 0xa2, 0x4c, 0x2b, 0x53, 0xfc, 0x4f, 0xb4, 0x7a, 0x85, 0x72, - 0xd6, 0xb4, 0xe6, 0x83, 0x68, 0x4d, 0x84, 0x6a, 0xc6, 0x10, 0xfc, 0x5b, 0x59, 0xa6, 0x2d, 0x55, 0x95, 0xfa, 0xd0, - 0x78, 0xad, 0x15, 0xce, 0xf2, 0x71, 0xe4, 0xbd, 0xc6, 0xd0, 0xb1, 0x89, 0xb3, 0x94, 0x53, 0xa9, 0xb3, 0x4f, 0xfb, - 0x32, 0x72, 0x80, 0xd3, 0x09, 0x1b, 0x4f, 0x93, 0x43, 0x39, 0x4d, 0x1c, 0x64, 0x7e, 0xce, 0x30, 0xb2, 0xaa, 0x01, - 0x61, 0x51, 0x36, 0x94, 0xb6, 0x00, 0x93, 0x9c, 0x10, 0x32, 0xc5, 0x50, 0x14, 0xf9, 0x48, 0xf7, 0xc3, 0x7a, 0xb3, - 0xba, 0x2f, 0xde, 0x69, 0x80, 0xd3, 0x30, 0x81, 0x40, 0xe0, 0x45, 0x7c, 0x93, 0x89, 0x4b, 0xf0, 0x18, 0x1e, 0xc0, - 0x97, 0xe0, 0x26, 0x97, 0xb2, 0xdf, 0xab, 0x30, 0xc7, 0xb5, 0x05, 0x0c, 0x1a, 0xac, 0x1e, 0x44, 0x87, 0x4b, 0x69, - 0xb3, 0xab, 0x00, 0xb1, 0x31, 0x85, 0x58, 0x16, 0x6c, 0x6d, 0xd9, 0xb3, 0xef, 0x55, 0xd3, 0xd0, 0x3a, 0xe1, 0x58, - 0x5c, 0xe6, 0x10, 0x45, 0x65, 0x10, 0x83, 0x3b, 0x92, 0xc7, 0xe7, 0x3d, 0x12, 0xe1, 0x05, 0x01, 0xb7, 0xb2, 0x58, - 0x86, 0x2b, 0xba, 0x1c, 0xdd, 0xd2, 0xf5, 0xe8, 0x86, 0x8e, 0xe9, 0xe4, 0xef, 0x63, 0xb0, 0xc8, 0xd6, 0xa9, 0x77, - 0x74, 0x3d, 0x5a, 0xd2, 0x6f, 0xc7, 0xf4, 0xe0, 0x6f, 0x60, 0xc2, 0x87, 0x87, 0x09, 0xbd, 0x00, 0xc7, 0x2e, 0x52, - 0xa3, 0xa7, 0xa6, 0x6f, 0x70, 0x58, 0x8d, 0xf2, 0x21, 0x1f, 0xe5, 0x94, 0x8f, 0x8a, 0x61, 0x35, 0x02, 0x4f, 0xc7, - 0x6a, 0xc8, 0x47, 0x15, 0xe5, 0xa3, 0xf3, 0x61, 0x35, 0x3a, 0x27, 0xcd, 0xa6, 0xbf, 0xae, 0xf8, 0x55, 0xc9, 0x52, - 0xd8, 0x16, 0xb0, 0x7c, 0x3d, 0xaf, 0xa8, 0xd4, 0x5f, 0xd5, 0xe6, 0x64, 0xb6, 0x9c, 0xbd, 0xbd, 0xee, 0x72, 0x62, - 0xf1, 0xb8, 0x6d, 0x3a, 0x5c, 0x7d, 0x39, 0x51, 0x27, 0xbd, 0x42, 0x7e, 0x18, 0x4f, 0x85, 0x3a, 0x87, 0xc0, 0x4c, - 0x62, 0x16, 0xc6, 0x0c, 0x9b, 0xa9, 0xd3, 0x40, 0x81, 0x93, 0x8d, 0x3c, 0x17, 0xc5, 0x6c, 0x94, 0x53, 0x78, 0x1f, - 0x13, 0x12, 0x09, 0x38, 0xab, 0x8e, 0xaa, 0x51, 0x01, 0x31, 0x47, 0x58, 0x88, 0x8f, 0xd0, 0x2f, 0xf5, 0x91, 0x87, - 0x04, 0x9e, 0x61, 0x5f, 0x8b, 0x41, 0x0c, 0x47, 0xbc, 0xad, 0xac, 0x9a, 0x85, 0x09, 0x54, 0x56, 0x0d, 0x4b, 0x53, - 0x59, 0x41, 0xb3, 0x51, 0xe5, 0x57, 0x56, 0xe1, 0x18, 0x25, 0x84, 0x44, 0xa5, 0xae, 0x0c, 0xd4, 0x27, 0x09, 0x0b, - 0x4b, 0x5d, 0xd9, 0xb9, 0xfa, 0xe8, 0xdc, 0xaf, 0xec, 0x1c, 0x5c, 0x48, 0x07, 0x89, 0x7f, 0x95, 0xca, 0xd3, 0xf6, - 0x75, 0xb0, 0xb1, 0xaa, 0xe8, 0x86, 0xdf, 0x56, 0x45, 0x1c, 0x95, 0xd4, 0xc5, 0x80, 0xc6, 0x85, 0x11, 0x49, 0xaa, - 0xd7, 0x28, 0xf8, 0x43, 0x82, 0xa8, 0x34, 0x06, 0xaf, 0xce, 0xa4, 0x6b, 0xa5, 0x56, 0x54, 0x0c, 0xca, 0x41, 0x01, - 0xf7, 0xa7, 0xbc, 0xb5, 0x90, 0xbe, 0x87, 0x88, 0xca, 0x50, 0xde, 0xe0, 0x1f, 0x18, 0x3c, 0x99, 0xad, 0xd2, 0x30, - 0x19, 0xdd, 0xd1, 0x78, 0xb4, 0x44, 0x38, 0x18, 0xb6, 0x4e, 0x15, 0xde, 0xfa, 0x05, 0xa4, 0xdf, 0xd2, 0x78, 0x74, - 0x43, 0x53, 0x6b, 0x73, 0x6a, 0xa0, 0xae, 0x7a, 0x63, 0x7a, 0x1b, 0xc1, 0xeb, 0xbb, 0x68, 0x49, 0x61, 0x2b, 0x1d, - 0xe7, 0xd9, 0xa5, 0x88, 0x52, 0x8a, 0x08, 0x84, 0x6b, 0x44, 0x0e, 0x5c, 0x6a, 0xb4, 0xc1, 0xf5, 0x00, 0xca, 0xd0, - 0x70, 0x81, 0xcb, 0x41, 0x3c, 0x5a, 0x7a, 0x64, 0x6a, 0xa9, 0x2f, 0xb2, 0x08, 0x1f, 0xed, 0x6c, 0xb4, 0x14, 0xcf, - 0x88, 0x85, 0x71, 0x05, 0x43, 0xa8, 0x0b, 0x2b, 0x4d, 0x41, 0xd2, 0x05, 0x8e, 0xec, 0x85, 0x45, 0x15, 0x6e, 0xc0, - 0xb4, 0xe8, 0x0e, 0xcc, 0xa3, 0x40, 0xe1, 0xe0, 0x12, 0xa4, 0x9f, 0x50, 0xb6, 0x73, 0x94, 0x26, 0x87, 0x37, 0x41, - 0xe9, 0xce, 0x04, 0x21, 0xed, 0xea, 0x26, 0x5b, 0xd2, 0x37, 0xd8, 0xde, 0xa1, 0x53, 0x51, 0x41, 0xf5, 0xb9, 0x05, - 0x93, 0x25, 0x1b, 0x84, 0x2d, 0x61, 0x7a, 0xa6, 0xd7, 0x80, 0x3d, 0xbd, 0x7f, 0xb0, 0x33, 0xdf, 0xc5, 0xec, 0xd3, - 0x7e, 0x19, 0x8d, 0x95, 0x05, 0x6f, 0x6e, 0x89, 0xdd, 0x92, 0x8d, 0xa7, 0xcb, 0xc3, 0x72, 0xba, 0x44, 0x62, 0x67, - 0xe8, 0x16, 0xe3, 0xf3, 0xe5, 0x82, 0x26, 0x78, 0xb6, 0xb1, 0x6a, 0xbe, 0x34, 0x68, 0x29, 0x29, 0xc3, 0xf5, 0xb6, - 0x44, 0xff, 0x7f, 0x75, 0xf1, 0x4b, 0x01, 0x5e, 0x82, 0xb1, 0x00, 0x10, 0xee, 0xc1, 0xb4, 0x20, 0xb5, 0x51, 0x36, - 0x96, 0x69, 0x98, 0xe2, 0x22, 0x30, 0x29, 0xfd, 0x7e, 0x98, 0xb3, 0x94, 0x78, 0xd0, 0xa1, 0xee, 0xd4, 0x4e, 0x7d, - 0x21, 0x08, 0xf0, 0x48, 0xea, 0x1c, 0x9b, 0xfc, 0x7d, 0x3c, 0x0b, 0xd4, 0x40, 0x04, 0x51, 0x76, 0x88, 0x8f, 0x18, - 0xb8, 0x28, 0xd2, 0x71, 0x3b, 0x5d, 0x11, 0x17, 0xbb, 0xc7, 0x2c, 0xc4, 0x49, 0xc2, 0x5c, 0xb3, 0x6c, 0xc8, 0xaa, - 0x08, 0x13, 0x74, 0x61, 0x60, 0x96, 0x37, 0x64, 0xd5, 0xfe, 0x01, 0x44, 0x6a, 0xb5, 0x65, 0xac, 0xba, 0xca, 0xf8, - 0x16, 0x80, 0xac, 0x19, 0x63, 0x07, 0x7f, 0x1b, 0xcf, 0xd4, 0x37, 0x51, 0xc8, 0x8f, 0x0e, 0xfe, 0x06, 0xc9, 0x87, - 0xdf, 0x22, 0x33, 0x07, 0xc9, 0x8d, 0x82, 0x2e, 0x9b, 0xb3, 0xae, 0xa1, 0x34, 0x71, 0xed, 0x95, 0x7a, 0xed, 0x49, - 0xb3, 0xf6, 0x0a, 0x74, 0xa7, 0x36, 0xbc, 0x87, 0xb2, 0x9d, 0x05, 0x13, 0x74, 0x34, 0xbb, 0x03, 0x1d, 0xbc, 0x53, - 0x04, 0xbd, 0x4c, 0x42, 0xe3, 0x11, 0xaa, 0x8c, 0x7a, 0x61, 0x47, 0x76, 0xb3, 0x2e, 0x99, 0x67, 0xc0, 0x1c, 0xdb, - 0x73, 0x48, 0x0c, 0x73, 0x75, 0x50, 0xa7, 0xac, 0x1c, 0xe6, 0x78, 0x00, 0xaf, 0x99, 0x1c, 0x8a, 0x41, 0xae, 0x51, - 0xbe, 0x2f, 0x58, 0x31, 0x2c, 0x07, 0xb9, 0xe6, 0x66, 0xa6, 0xcd, 0xd8, 0xb4, 0x89, 0x0e, 0xcf, 0xbc, 0x62, 0x47, - 0xab, 0x1e, 0xf0, 0xb1, 0xe0, 0xc9, 0xec, 0x7b, 0x3e, 0xbe, 0x01, 0x4e, 0x66, 0x73, 0x1b, 0x2d, 0xe9, 0x5d, 0x94, - 0xd2, 0x9b, 0x68, 0x4d, 0x97, 0xd1, 0x85, 0x31, 0x31, 0x4e, 0x6a, 0x38, 0x07, 0xa0, 0x55, 0x00, 0x89, 0xa7, 0x7e, - 0xbd, 0xe7, 0x49, 0x15, 0x2e, 0x69, 0x0a, 0x6e, 0xc3, 0xbe, 0x7d, 0xe6, 0x95, 0x2f, 0x91, 0xda, 0x20, 0xc6, 0x9a, - 0x35, 0x54, 0xdc, 0x78, 0xeb, 0x3e, 0x12, 0x35, 0xec, 0x5c, 0x17, 0x9b, 0xa8, 0x1a, 0x4e, 0xa6, 0x25, 0x20, 0xb6, - 0x96, 0xc3, 0xa1, 0x3b, 0x42, 0x76, 0x8f, 0x1f, 0x1d, 0xe8, 0xb9, 0x27, 0x2d, 0xb6, 0x6d, 0xcb, 0x1f, 0x18, 0xc2, - 0x94, 0x7e, 0xfe, 0xc8, 0x07, 0xc4, 0x8a, 0x4b, 0x38, 0x1b, 0x81, 0x3a, 0x5a, 0xa1, 0xd3, 0xef, 0x55, 0x58, 0xe8, - 0x03, 0x7c, 0x73, 0x1b, 0x25, 0xf4, 0x2e, 0xca, 0x3d, 0xb2, 0xb6, 0xac, 0x99, 0x9c, 0x9e, 0x65, 0x21, 0x6f, 0x1f, - 0xe8, 0xe5, 0x02, 0x40, 0xb4, 0x06, 0xb1, 0x2f, 0x75, 0x3d, 0x00, 0xa7, 0x21, 0x34, 0x09, 0x8d, 0xe0, 0xaa, 0x82, - 0x30, 0x02, 0xae, 0x24, 0xfc, 0x0d, 0x26, 0x2a, 0xf0, 0x05, 0xb8, 0xc8, 0xa4, 0x69, 0xce, 0x83, 0xda, 0x1f, 0xc9, - 0xd3, 0xa2, 0xed, 0xed, 0x0a, 0xa3, 0x09, 0xc6, 0x9e, 0x68, 0x9f, 0x47, 0xca, 0x51, 0x5c, 0x24, 0x61, 0x36, 0xba, - 0x55, 0xe7, 0x39, 0xcd, 0x46, 0x77, 0xfa, 0x57, 0x45, 0xc7, 0xf4, 0x3b, 0x1d, 0xd0, 0x46, 0x49, 0xdf, 0x3a, 0xce, - 0x06, 0xb4, 0x5e, 0x2c, 0x8d, 0xff, 0xb5, 0x1c, 0xdd, 0x52, 0x39, 0xba, 0xf3, 0x2d, 0xa9, 0x26, 0xd3, 0xe2, 0x50, - 0xa0, 0x21, 0x55, 0xe7, 0xf7, 0x05, 0xf0, 0x73, 0xa5, 0xf1, 0x9d, 0x36, 0xdf, 0x7b, 0xed, 0x3f, 0xef, 0xe4, 0x09, - 0x14, 0x4b, 0x54, 0xb0, 0x6a, 0x04, 0x76, 0xec, 0xeb, 0x3c, 0x2e, 0xcc, 0x28, 0xc5, 0xd4, 0x9a, 0xf4, 0x63, 0xe0, - 0x8a, 0x69, 0xaf, 0x00, 0x57, 0xcb, 0xed, 0x56, 0xc5, 0xd0, 0x84, 0x3d, 0x3b, 0x86, 0xa8, 0xe7, 0xc6, 0x31, 0x4a, - 0x36, 0xdc, 0x03, 0x62, 0x2d, 0xf3, 0x56, 0x2e, 0x01, 0x09, 0xbc, 0xf5, 0x30, 0x29, 0x00, 0x63, 0xb0, 0x5c, 0x12, - 0x9d, 0xc7, 0x43, 0x9f, 0x50, 0x2f, 0x34, 0xea, 0x84, 0x6c, 0x6c, 0x09, 0x1c, 0x7f, 0x58, 0x1f, 0x02, 0xc1, 0xab, - 0x3c, 0xd7, 0x5f, 0x69, 0x5d, 0x7f, 0xa9, 0xf4, 0xdc, 0xb1, 0x5c, 0xd7, 0xcf, 0xda, 0xd4, 0xe8, 0x15, 0x58, 0xf8, - 0x6e, 0x94, 0x79, 0x24, 0xb7, 0x08, 0xa9, 0x0a, 0xac, 0xd4, 0x2d, 0x24, 0x98, 0x7f, 0x25, 0x67, 0xab, 0x32, 0x5f, - 0x3d, 0xf2, 0xa0, 0x9c, 0x4d, 0x4f, 0x7f, 0x43, 0x82, 0x76, 0xd7, 0x91, 0xe6, 0xf1, 0x16, 0x1d, 0x3e, 0xbb, 0xd6, - 0x12, 0x73, 0x27, 0x51, 0xf1, 0x7c, 0x0a, 0xd8, 0xea, 0x45, 0x76, 0xa5, 0x7c, 0xac, 0x76, 0x71, 0xfc, 0xcc, 0xf9, - 0x13, 0x57, 0xe1, 0x5a, 0x34, 0x94, 0x20, 0xe0, 0xcd, 0x61, 0xec, 0x0a, 0x55, 0x40, 0x43, 0x73, 0x03, 0xc7, 0xb9, - 0x1a, 0x56, 0x9a, 0x80, 0x69, 0x29, 0x8f, 0x0e, 0x70, 0x68, 0xf2, 0xa8, 0xdd, 0x34, 0xac, 0x0c, 0x5d, 0x6b, 0xf4, - 0xb9, 0xad, 0x74, 0xc6, 0x9b, 0x0d, 0xdf, 0x3f, 0x18, 0x54, 0xf8, 0x93, 0x34, 0x47, 0xa3, 0x9d, 0x1b, 0xee, 0x34, - 0x02, 0x33, 0x57, 0x72, 0x45, 0x76, 0x47, 0xc9, 0xcb, 0xef, 0xe9, 0x85, 0x05, 0xf4, 0xe7, 0x3f, 0x17, 0x13, 0x4e, - 0x5a, 0x62, 0x42, 0xb4, 0x74, 0xd0, 0xa2, 0x83, 0x1d, 0xe5, 0x95, 0x7d, 0x89, 0x97, 0xce, 0xf1, 0xbf, 0xaf, 0xc7, - 0xda, 0x55, 0x20, 0xb4, 0x3a, 0xb9, 0xdf, 0x9e, 0x2c, 0x10, 0x35, 0xa0, 0x9a, 0x5d, 0x95, 0xa3, 0x4c, 0x3b, 0x2b, - 0xb2, 0x69, 0xc8, 0x5c, 0x77, 0xb3, 0x34, 0x6c, 0x26, 0x3b, 0x16, 0x96, 0x19, 0x06, 0x6b, 0xa7, 0x8a, 0x3e, 0x07, - 0x2d, 0x3f, 0x82, 0x17, 0x4d, 0xe5, 0x99, 0xcf, 0x66, 0x19, 0xf1, 0x02, 0x9d, 0x73, 0x2a, 0x16, 0x4d, 0xe9, 0x58, - 0xb9, 0xdd, 0x96, 0x68, 0x2c, 0x51, 0x46, 0x41, 0x50, 0xdb, 0x20, 0xec, 0xba, 0x74, 0x4f, 0xfa, 0xb4, 0x8b, 0x4f, - 0x2b, 0xd0, 0xf7, 0xf8, 0x3e, 0x03, 0x89, 0xa9, 0x27, 0x79, 0xa8, 0x1a, 0xcd, 0xd1, 0xc9, 0xb3, 0x38, 0xd5, 0xf8, - 0xfc, 0x4a, 0x76, 0xd6, 0xbc, 0x5b, 0x8d, 0x29, 0xfe, 0x23, 0x75, 0xfb, 0xce, 0x65, 0x68, 0xa2, 0xbf, 0x96, 0x07, - 0x2d, 0x85, 0x05, 0xc7, 0x6d, 0xe3, 0xaf, 0xdf, 0x66, 0x0e, 0x31, 0x2c, 0x5d, 0x0e, 0x6f, 0x42, 0x87, 0xee, 0xae, - 0xb2, 0x33, 0xd7, 0x07, 0xd4, 0xa9, 0x8b, 0x75, 0x1b, 0x50, 0xb2, 0xe4, 0xdd, 0x3a, 0x3d, 0xb1, 0xd2, 0x77, 0xfb, - 0xe1, 0xce, 0x3c, 0x6a, 0x76, 0x77, 0xbb, 0x9d, 0x90, 0xb6, 0x7d, 0x30, 0xde, 0x97, 0xb0, 0x10, 0xe7, 0x1d, 0xb6, - 0xf7, 0x7d, 0x58, 0x3d, 0xe6, 0x83, 0x5f, 0x70, 0x9c, 0x61, 0xf4, 0x33, 0x65, 0xe8, 0xf3, 0xaa, 0x90, 0x57, 0xaa, - 0x53, 0xbe, 0xd0, 0xad, 0x65, 0xea, 0xfd, 0x36, 0x7e, 0xdb, 0x0a, 0x10, 0xe3, 0x75, 0xc5, 0x4a, 0xf1, 0x86, 0x56, - 0x18, 0xd7, 0xc0, 0x6d, 0x72, 0xa8, 0xa5, 0x5a, 0x20, 0xea, 0xf2, 0x93, 0xc7, 0x3c, 0x32, 0xea, 0x4c, 0xf8, 0xee, - 0x31, 0xf7, 0xa5, 0x6b, 0xbb, 0x4d, 0xfc, 0x5c, 0xd3, 0xf6, 0x77, 0x07, 0xba, 0xa3, 0x75, 0x0f, 0x37, 0xcf, 0xe6, - 0xe7, 0x91, 0xf9, 0x62, 0x80, 0xcd, 0xda, 0x65, 0x5c, 0x76, 0x0c, 0xf7, 0xbd, 0xe9, 0xc1, 0x58, 0x40, 0x20, 0x31, - 0x43, 0x2f, 0x03, 0x17, 0xb8, 0xc0, 0x5d, 0x61, 0xc0, 0x10, 0xd7, 0xb4, 0xe4, 0x4c, 0x5b, 0xd9, 0xfa, 0xc8, 0xdb, - 0xa8, 0x10, 0xac, 0xeb, 0x8e, 0x9b, 0x24, 0x87, 0xe0, 0x84, 0x2d, 0xf7, 0xbe, 0xf6, 0xda, 0x19, 0xfe, 0x63, 0x20, - 0x9c, 0x5b, 0xa2, 0x67, 0xd4, 0xf6, 0x58, 0xab, 0x7b, 0x0d, 0xaf, 0x72, 0x17, 0x79, 0xd6, 0x6f, 0xe6, 0xa5, 0x61, - 0x5f, 0xf0, 0x5a, 0x0a, 0x0e, 0x8d, 0xed, 0x56, 0xb8, 0xc5, 0xe2, 0x1d, 0xad, 0x56, 0xd6, 0xda, 0x6a, 0xaf, 0x95, - 0x8a, 0xde, 0xbf, 0xe6, 0x38, 0x71, 0x96, 0xc2, 0xf6, 0xc3, 0x87, 0x0b, 0x76, 0x4d, 0x00, 0x83, 0x16, 0x93, 0x05, - 0x4a, 0x50, 0xc9, 0x5a, 0xd5, 0x6e, 0xa7, 0xc4, 0x2f, 0xf7, 0x8b, 0x2e, 0xb3, 0x9d, 0xc7, 0xaf, 0x9b, 0xb4, 0xcf, - 0x7c, 0x8e, 0x7e, 0x98, 0xdf, 0x59, 0x27, 0x25, 0x67, 0x18, 0xd7, 0xf2, 0xff, 0xab, 0xe8, 0x65, 0x91, 0xa5, 0xd1, - 0xc6, 0xf0, 0x60, 0x36, 0xd4, 0xa6, 0x0f, 0x8d, 0x51, 0xb9, 0x65, 0xa3, 0x88, 0x68, 0x75, 0x0b, 0x82, 0x19, 0xc5, - 0x7d, 0x89, 0x36, 0xaf, 0x54, 0x59, 0x78, 0x87, 0xcf, 0x6c, 0xf4, 0x86, 0xed, 0x09, 0xa1, 0x7c, 0xf7, 0xb4, 0x30, - 0xab, 0x96, 0x8a, 0x06, 0xdb, 0x25, 0xbc, 0x8b, 0x51, 0xa5, 0x9f, 0x30, 0xd9, 0xb2, 0x60, 0xaa, 0xff, 0xdf, 0x17, - 0x59, 0xda, 0xa6, 0xe8, 0xc0, 0x74, 0x36, 0x7d, 0x3a, 0xe9, 0x06, 0xd7, 0x19, 0xb0, 0x88, 0x60, 0x4b, 0x85, 0xe3, - 0x51, 0x6a, 0x37, 0x48, 0x98, 0x08, 0x6e, 0xa2, 0x5e, 0x76, 0xb4, 0x4c, 0xc9, 0xaa, 0x80, 0xe7, 0x57, 0xae, 0x32, - 0x1d, 0x47, 0x43, 0xbf, 0x7f, 0x95, 0x9a, 0xd0, 0xaf, 0xd4, 0x4b, 0x55, 0x9c, 0x87, 0x51, 0x75, 0xa8, 0x30, 0x46, - 0x4b, 0x9a, 0xc2, 0x31, 0x98, 0x5d, 0x84, 0x29, 0x5e, 0xce, 0x36, 0x09, 0xfb, 0x82, 0x81, 0x5c, 0x6a, 0x83, 0x7a, - 0x4d, 0x89, 0xd6, 0xac, 0xbd, 0x99, 0x53, 0x42, 0x2f, 0x58, 0xe9, 0xdf, 0x85, 0xd6, 0x20, 0x50, 0x94, 0xcd, 0x94, - 0xe9, 0xb9, 0x6e, 0xe7, 0x05, 0x4d, 0x68, 0x41, 0x57, 0xa4, 0x06, 0x7d, 0xaf, 0x93, 0xb3, 0xa3, 0x93, 0x9d, 0x99, - 0xf5, 0x98, 0x15, 0xc3, 0xc9, 0x34, 0x86, 0x6b, 0x5a, 0xec, 0xae, 0x69, 0xcb, 0xe6, 0x8d, 0xab, 0xb1, 0x71, 0x1a, - 0xb4, 0x0b, 0xa4, 0x6d, 0x9a, 0xdb, 0x4f, 0x3d, 0x6e, 0x7f, 0x5d, 0xb3, 0xe5, 0xb4, 0xb7, 0xde, 0x6e, 0x7b, 0x29, - 0xd8, 0x88, 0x7a, 0x7c, 0xfc, 0x5a, 0x49, 0xd7, 0x2d, 0x97, 0x9f, 0xc2, 0xb3, 0xc7, 0xd7, 0x2f, 0x7d, 0x70, 0x39, - 0x5a, 0xb5, 0xb9, 0xfb, 0xe5, 0x2e, 0xb2, 0xdc, 0x17, 0x0d, 0x2d, 0xd7, 0x33, 0xd4, 0x24, 0xcf, 0x46, 0x7b, 0x87, - 0x5a, 0xb0, 0x9c, 0x75, 0x13, 0x9e, 0x18, 0xec, 0xd8, 0xab, 0xc6, 0xe6, 0xa8, 0xcc, 0x25, 0xab, 0x41, 0x02, 0x7d, - 0x92, 0x67, 0x9a, 0xfe, 0x41, 0x86, 0xf9, 0xe8, 0x96, 0xe6, 0x80, 0x2b, 0x56, 0xd9, 0x4b, 0x06, 0xa9, 0xab, 0xf6, - 0x12, 0x57, 0xbe, 0xc2, 0x21, 0xd9, 0xe0, 0x93, 0x61, 0xaa, 0x3e, 0xbb, 0xe4, 0xc1, 0xff, 0xdb, 0xaa, 0x55, 0x7a, - 0x6e, 0x92, 0x1b, 0x8e, 0x7f, 0x9d, 0xb4, 0x7d, 0x4c, 0x0c, 0x12, 0xf0, 0xd4, 0x2e, 0x86, 0x6a, 0x54, 0x15, 0xb1, - 0x28, 0x73, 0x13, 0x73, 0xec, 0xde, 0xae, 0xa1, 0x83, 0x32, 0xf8, 0x75, 0xc3, 0x27, 0xe6, 0x0e, 0x6c, 0x05, 0x3a, - 0x3a, 0xd1, 0x5c, 0x86, 0x99, 0xb9, 0x0c, 0xd3, 0xae, 0xad, 0x02, 0xc3, 0xab, 0xb6, 0x4a, 0xa2, 0x5c, 0x8d, 0x7a, - 0xdc, 0xcc, 0x52, 0xb3, 0x17, 0x79, 0xf7, 0x9a, 0xf4, 0x24, 0xfe, 0x74, 0xe9, 0xc9, 0xeb, 0x61, 0x40, 0xe4, 0x97, - 0x2c, 0x0d, 0xd7, 0x28, 0x08, 0x4e, 0xad, 0x76, 0x20, 0xcd, 0x47, 0x80, 0xcc, 0x8f, 0xd3, 0xf0, 0x9d, 0x16, 0xe7, - 0x90, 0x8d, 0xd2, 0x38, 0xb1, 0xa5, 0x51, 0x0f, 0xc1, 0x9d, 0xf7, 0x8a, 0xc7, 0x10, 0xf8, 0xf0, 0x03, 0x6e, 0x06, - 0x15, 0xdd, 0x96, 0x98, 0x28, 0x6d, 0x1e, 0x75, 0xcb, 0x47, 0x0d, 0xa1, 0x92, 0x95, 0xe1, 0xc5, 0xd0, 0xde, 0x1d, - 0x81, 0x51, 0xe5, 0x04, 0x32, 0xc3, 0x62, 0xff, 0x60, 0x98, 0x2a, 0x41, 0xd1, 0x50, 0x0e, 0x97, 0x28, 0x07, 0xc4, - 0x24, 0x10, 0x18, 0x15, 0x83, 0x54, 0x57, 0xa6, 0x5e, 0x0c, 0x52, 0x7d, 0xab, 0x22, 0xf5, 0x59, 0x16, 0x56, 0x54, - 0xb7, 0x88, 0x8e, 0xe9, 0x50, 0xd2, 0xa5, 0xd9, 0xa9, 0xb9, 0x96, 0x5e, 0xa8, 0xe5, 0xf8, 0x5c, 0xa7, 0xc1, 0x28, - 0x9e, 0xba, 0x14, 0xfd, 0x56, 0xed, 0x67, 0xff, 0x2d, 0xa6, 0xd4, 0x88, 0x4d, 0xed, 0x2d, 0x62, 0x58, 0xb5, 0x1f, - 0xb2, 0x2a, 0x07, 0xed, 0x2e, 0x28, 0x1b, 0x2b, 0xe3, 0x3c, 0xdf, 0x08, 0x66, 0x0e, 0xda, 0xc6, 0xaa, 0xe9, 0x43, - 0x6f, 0xc4, 0xa8, 0xbd, 0x31, 0xd5, 0xb8, 0x27, 0xf0, 0xd3, 0x06, 0x4d, 0xf7, 0x22, 0xcf, 0x51, 0x8f, 0xbc, 0xfb, - 0x9f, 0x39, 0xb2, 0x33, 0xf9, 0x2c, 0x96, 0x49, 0xdd, 0x3e, 0x26, 0xc1, 0x42, 0xd5, 0x31, 0xba, 0x70, 0x23, 0x53, - 0xda, 0xcf, 0x9d, 0xe9, 0x47, 0x3c, 0x93, 0x87, 0xed, 0xd0, 0xa8, 0x2f, 0x0d, 0x6b, 0x49, 0x11, 0xf5, 0x05, 0xbd, - 0x35, 0xd5, 0xd1, 0x01, 0xf5, 0x3a, 0x02, 0xab, 0x2b, 0xda, 0xa0, 0x06, 0x60, 0x32, 0xae, 0x6d, 0x6d, 0x3e, 0x07, - 0x53, 0x5b, 0x55, 0xc1, 0x33, 0xba, 0x2b, 0x94, 0xee, 0x4d, 0xea, 0xba, 0x35, 0xc4, 0x16, 0x30, 0x20, 0x70, 0xa3, - 0xa7, 0xa6, 0x3f, 0x68, 0xa2, 0x02, 0xd0, 0xa0, 0x71, 0x3b, 0xd3, 0x39, 0x12, 0xfd, 0x4e, 0x6d, 0xda, 0x66, 0xaa, - 0x57, 0x95, 0x0f, 0xa0, 0xe2, 0xcf, 0xd2, 0xd9, 0x85, 0x19, 0xb1, 0x00, 0xc6, 0x3d, 0x70, 0xa6, 0x7a, 0xc7, 0x19, - 0x58, 0x4f, 0xe4, 0x79, 0x56, 0xf2, 0x44, 0x0a, 0x98, 0x11, 0x79, 0x75, 0x25, 0x05, 0x0c, 0x83, 0x1a, 0x00, 0xb4, - 0x68, 0x2e, 0xa3, 0x09, 0x7f, 0x52, 0xd3, 0xfb, 0xf2, 0xf0, 0x27, 0x3a, 0xd7, 0x37, 0xe3, 0x1a, 0x0c, 0x95, 0xd7, - 0x15, 0xdf, 0xc9, 0xf4, 0x0d, 0x7f, 0xea, 0x65, 0x5a, 0xca, 0x75, 0xb1, 0x93, 0xe5, 0xc9, 0x37, 0xfc, 0x99, 0xce, - 0x73, 0xf0, 0xb4, 0xa6, 0x69, 0x7c, 0xb7, 0x93, 0xe5, 0xef, 0xdf, 0x3c, 0xb5, 0x79, 0x9e, 0x8c, 0x6b, 0x7a, 0xc3, - 0xf9, 0x47, 0x97, 0x69, 0xa2, 0xab, 0x1a, 0x3f, 0xfd, 0xbb, 0xcd, 0xf5, 0xb4, 0xa6, 0x57, 0x52, 0x54, 0xcb, 0x9d, - 0xa2, 0x0e, 0xbe, 0x39, 0xf8, 0x3b, 0xff, 0xc6, 0x74, 0xef, 0xa0, 0xa6, 0x7f, 0xad, 0xe3, 0xa2, 0xe2, 0xc5, 0x4e, - 0x71, 0x7f, 0xfb, 0xfb, 0xdf, 0x9f, 0xda, 0x8c, 0x4f, 0x6b, 0x7a, 0xc7, 0xe3, 0x8e, 0xb6, 0x4f, 0x9e, 0x3d, 0xe5, - 0x7f, 0xab, 0x6b, 0xfa, 0x2b, 0xf3, 0x83, 0xa3, 0x1e, 0x67, 0x9e, 0x1e, 0x3e, 0x91, 0x4d, 0xd4, 0x80, 0xa1, 0x87, - 0x06, 0x90, 0x4b, 0xab, 0xa6, 0xb9, 0xc7, 0x2b, 0x17, 0xdc, 0xbe, 0xcf, 0xe2, 0x34, 0x5e, 0xc1, 0x41, 0xb0, 0x41, - 0xe3, 0xac, 0x02, 0x38, 0x55, 0xe0, 0x3d, 0xa3, 0x92, 0x66, 0xa5, 0xfc, 0x07, 0xe7, 0x1f, 0x61, 0xd0, 0x10, 0xd2, - 0x46, 0x45, 0x06, 0x3a, 0x59, 0xe9, 0xc8, 0x46, 0xe8, 0xbf, 0xd9, 0x8c, 0x83, 0xe3, 0xc3, 0xe8, 0xf5, 0xfb, 0x61, - 0xc1, 0x44, 0x58, 0x10, 0x42, 0xff, 0x0c, 0x0b, 0x70, 0x28, 0x29, 0x98, 0x97, 0xcf, 0xf8, 0x9e, 0x6b, 0xa3, 0xb0, - 0x10, 0x44, 0x77, 0x91, 0x7d, 0x40, 0xd5, 0xa3, 0xef, 0xd0, 0x0d, 0xf1, 0xb2, 0xc2, 0x82, 0xa1, 0x55, 0x0d, 0xcc, - 0x10, 0x14, 0xff, 0x86, 0x87, 0x12, 0x7c, 0xe2, 0x01, 0x3e, 0x7a, 0x4c, 0x66, 0x5c, 0x5d, 0x6b, 0x4f, 0x2e, 0xc2, - 0x82, 0x06, 0xba, 0xed, 0x10, 0x74, 0x20, 0xf2, 0x5f, 0x80, 0xa7, 0xc0, 0xc0, 0x87, 0x85, 0x5d, 0xca, 0x5d, 0x7f, - 0xf5, 0x5f, 0x0d, 0xeb, 0xe8, 0xc2, 0x8f, 0xfe, 0x6a, 0x5d, 0xd8, 0x33, 0x32, 0x95, 0x87, 0xe5, 0x70, 0x32, 0x1d, - 0x0c, 0xa4, 0x8b, 0xe3, 0x76, 0x9c, 0xcd, 0x7f, 0x9d, 0xcb, 0xc5, 0x02, 0x75, 0xdf, 0x38, 0xaf, 0x33, 0xfd, 0x37, - 0xd2, 0xce, 0x07, 0x6f, 0x8e, 0x7f, 0x3f, 0x3b, 0x3d, 0x7e, 0x05, 0xce, 0x07, 0x1f, 0x5e, 0x7e, 0xff, 0xf2, 0xbd, - 0x0a, 0xee, 0xae, 0xe6, 0xbc, 0xdf, 0x77, 0x52, 0x9f, 0x90, 0x0f, 0x2b, 0xb2, 0x1f, 0xc6, 0x8f, 0x0b, 0x65, 0xf4, - 0x40, 0x0e, 0x99, 0x85, 0x42, 0x86, 0x2a, 0x6a, 0xfb, 0xbb, 0x1c, 0x4e, 0x3c, 0x30, 0x8b, 0xbb, 0x86, 0x08, 0xd7, - 0x6f, 0xb9, 0x0d, 0xb2, 0x26, 0x8f, 0xbc, 0x7e, 0x70, 0x32, 0x95, 0x8e, 0x2d, 0x2c, 0x18, 0x94, 0x0d, 0x6d, 0x3a, - 0xce, 0xe6, 0xc5, 0xc2, 0xb6, 0xcb, 0x2d, 0x90, 0x51, 0x9a, 0x5d, 0x5c, 0x84, 0x0a, 0xba, 0xfa, 0x08, 0x34, 0x00, - 0xa6, 0x51, 0x85, 0x6b, 0x11, 0x9f, 0xf9, 0xe5, 0x47, 0x63, 0xaf, 0x79, 0xb7, 0xa8, 0x7b, 0x32, 0xcd, 0xaa, 0x1a, - 0x03, 0x3a, 0x98, 0x50, 0xee, 0x06, 0xdd, 0x04, 0x93, 0x51, 0x6d, 0xf9, 0x75, 0x5e, 0x2d, 0x4c, 0x73, 0xdc, 0x30, - 0x54, 0x5e, 0xc9, 0x6b, 0xd9, 0x40, 0x64, 0x20, 0x19, 0x86, 0x3d, 0x1a, 0xa3, 0x48, 0x7d, 0x6f, 0xd7, 0x3b, 0x7e, - 0x93, 0x4b, 0x88, 0xa6, 0x98, 0x81, 0x74, 0xfe, 0x58, 0x28, 0xe7, 0x72, 0xc9, 0xf8, 0x5c, 0x2c, 0x8e, 0xc0, 0xed, - 0x7c, 0x2e, 0x16, 0x11, 0x06, 0xe5, 0xcb, 0x20, 0x56, 0x09, 0xd8, 0xbd, 0x38, 0x08, 0xdf, 0x4e, 0x68, 0x03, 0xbb, - 0x81, 0x24, 0x1b, 0x94, 0x76, 0xa5, 0x21, 0xca, 0x9d, 0xf2, 0x68, 0x83, 0xc8, 0x43, 0xac, 0x9a, 0x57, 0x6d, 0x4f, - 0x36, 0x73, 0x31, 0xc1, 0x55, 0x16, 0x33, 0x39, 0x8d, 0x0f, 0x59, 0x31, 0x8d, 0xa1, 0x94, 0x38, 0x4d, 0xc3, 0x98, - 0x4e, 0xa8, 0x20, 0x24, 0x61, 0x7c, 0x1e, 0x2f, 0x68, 0x82, 0x52, 0x82, 0x10, 0x42, 0x7e, 0x8c, 0xd0, 0x36, 0x07, - 0x96, 0xbc, 0xdd, 0x7e, 0x9e, 0x7e, 0x6e, 0xc7, 0x70, 0x19, 0x15, 0xa1, 0x1b, 0x74, 0xd6, 0xf0, 0x6f, 0x44, 0x05, - 0x8d, 0xb1, 0x62, 0x08, 0x02, 0x5e, 0x60, 0x54, 0xc2, 0x82, 0xc4, 0xac, 0x82, 0x28, 0x02, 0xe5, 0x3c, 0x5e, 0xb0, - 0x82, 0x36, 0x6d, 0x4e, 0x63, 0x6d, 0x12, 0xd4, 0x73, 0x58, 0x6a, 0x7b, 0x52, 0xa9, 0x10, 0x7b, 0x7c, 0x26, 0xa2, - 0x6b, 0x6d, 0x68, 0x00, 0x28, 0x50, 0x4a, 0x2e, 0x7e, 0xf3, 0xe5, 0x1e, 0x6e, 0x0a, 0xfa, 0x9f, 0x6d, 0x4c, 0xb4, - 0xb3, 0x5c, 0x1d, 0x7a, 0xf3, 0x05, 0x8d, 0xf3, 0x1c, 0x42, 0xb1, 0x19, 0x04, 0x72, 0x91, 0x55, 0x10, 0xd1, 0xe2, - 0x2e, 0x30, 0x21, 0xe1, 0xa0, 0x4d, 0xbf, 0x42, 0x6a, 0x43, 0x4c, 0xae, 0x3c, 0x31, 0xb0, 0xdb, 0x2a, 0x41, 0xc0, - 0x91, 0x9e, 0x67, 0x9f, 0x9a, 0x18, 0x6b, 0x9a, 0x9a, 0x99, 0x78, 0x1b, 0x0a, 0xd1, 0xa0, 0x05, 0xd1, 0x0c, 0xde, - 0x3f, 0x57, 0x1c, 0xaf, 0x3a, 0xf0, 0x03, 0xde, 0xb9, 0x38, 0xf3, 0x6a, 0xe6, 0x11, 0x39, 0xf5, 0x51, 0x8e, 0xe8, - 0x97, 0x3c, 0xac, 0x46, 0x3a, 0x19, 0x63, 0x25, 0x71, 0xd0, 0xdb, 0x60, 0xc1, 0x9c, 0xd0, 0x15, 0x0f, 0x2d, 0x1f, - 0xff, 0x0a, 0x99, 0x8c, 0x92, 0x1a, 0x2b, 0xba, 0xd2, 0x62, 0xc4, 0x79, 0x0d, 0xb3, 0x34, 0x59, 0xd1, 0xc5, 0x42, - 0x93, 0x66, 0xa1, 0x4c, 0x03, 0x7c, 0x02, 0x2d, 0x46, 0xee, 0xa1, 0xa6, 0x0d, 0x84, 0x86, 0xdd, 0x21, 0xe0, 0x23, - 0xf7, 0xd0, 0xe1, 0xff, 0xe7, 0xd9, 0x05, 0x22, 0xed, 0xcd, 0x4d, 0x64, 0x3c, 0x52, 0x37, 0x70, 0x50, 0x8c, 0x8f, - 0x7d, 0x33, 0xf1, 0x0b, 0x67, 0xf4, 0x21, 0xa9, 0x7c, 0x87, 0x0f, 0x96, 0x3f, 0xde, 0xd4, 0xcc, 0xca, 0x08, 0xd6, - 0xc3, 0x76, 0x8b, 0x0b, 0xa2, 0xed, 0x02, 0x48, 0x3d, 0xe3, 0xd5, 0xc2, 0x37, 0x5e, 0x8d, 0xef, 0x31, 0x5e, 0x75, - 0x67, 0x6a, 0x98, 0x93, 0x0d, 0xea, 0xb3, 0x94, 0x3c, 0x3f, 0x47, 0x99, 0x60, 0xd3, 0xe5, 0xac, 0xa4, 0x2a, 0x95, - 0xd0, 0x5e, 0xec, 0x67, 0x8c, 0x6f, 0x09, 0xc6, 0x59, 0x71, 0x18, 0x09, 0x54, 0xa5, 0x92, 0x3a, 0xec, 0x15, 0xa0, - 0x1e, 0x83, 0xf7, 0x06, 0x43, 0xd4, 0xc8, 0xd8, 0x4d, 0x1b, 0x08, 0x0d, 0x8d, 0xf5, 0x68, 0xcf, 0x5a, 0x8f, 0x6e, - 0xb7, 0x95, 0xf1, 0xb7, 0x93, 0xeb, 0x22, 0x41, 0x54, 0x61, 0x35, 0x9a, 0x00, 0x6f, 0x9a, 0xd8, 0xdb, 0x92, 0x53, - 0x5a, 0x60, 0xf8, 0xec, 0x3f, 0xc3, 0xd2, 0xa9, 0x24, 0x4a, 0x32, 0x2b, 0xa3, 0x81, 0x3b, 0x07, 0x5f, 0xc4, 0x15, - 0xac, 0x01, 0x88, 0xe4, 0x88, 0x1e, 0xae, 0x7f, 0x86, 0xd2, 0x65, 0x96, 0x64, 0x26, 0x21, 0x33, 0x17, 0x69, 0x3b, - 0xeb, 0x60, 0xe2, 0x4c, 0x6a, 0xbd, 0xb1, 0x90, 0x43, 0x83, 0xfc, 0x00, 0xca, 0x10, 0x87, 0x4f, 0x3e, 0x98, 0x50, - 0xa9, 0x42, 0xa9, 0x36, 0xba, 0xd9, 0x0d, 0xbc, 0xf2, 0x21, 0xbb, 0xe2, 0x65, 0x15, 0x5f, 0xad, 0x8c, 0x25, 0x31, - 0x67, 0xf7, 0xb9, 0xed, 0x51, 0x61, 0x5e, 0xbd, 0x7d, 0xf9, 0xfd, 0x71, 0xe3, 0xd5, 0x2e, 0xe2, 0x68, 0x08, 0xb6, - 0x15, 0x63, 0x8c, 0xde, 0xe2, 0xd3, 0x60, 0xa2, 0x5c, 0x23, 0xd0, 0xbb, 0x14, 0xf4, 0xdb, 0x5f, 0xea, 0x09, 0x78, - 0xc5, 0xf5, 0xf2, 0x4b, 0x3e, 0x02, 0x96, 0xa8, 0xd0, 0xb3, 0xc2, 0xdc, 0xac, 0xcc, 0xee, 0xed, 0x56, 0x64, 0xa6, - 0x5d, 0x69, 0x64, 0x20, 0x5e, 0x6d, 0x87, 0xb1, 0x70, 0xe9, 0x9a, 0x6e, 0x07, 0xbb, 0x5a, 0x7a, 0x96, 0xc8, 0xdb, - 0x6d, 0x09, 0x1d, 0xb2, 0x03, 0xee, 0xbd, 0x8c, 0x6f, 0xe1, 0x65, 0xe9, 0x75, 0xb3, 0x19, 0x3c, 0x01, 0xcc, 0x84, - 0x0b, 0x67, 0x59, 0x1c, 0x33, 0x9e, 0x84, 0x2a, 0x36, 0x57, 0x43, 0xe4, 0xad, 0x08, 0xad, 0xd9, 0x5f, 0xa1, 0x18, - 0x81, 0xdd, 0xc9, 0xe9, 0xc7, 0x6c, 0x35, 0x5b, 0x02, 0x6a, 0xfe, 0x55, 0x26, 0x80, 0xe6, 0xda, 0xb5, 0x60, 0x9b, - 0x42, 0x9b, 0xeb, 0xfa, 0x79, 0xbc, 0x8a, 0x13, 0x50, 0xdd, 0x80, 0xb7, 0xc8, 0x9d, 0x16, 0x5d, 0x19, 0x74, 0x51, - 0xfa, 0x40, 0x39, 0x96, 0x14, 0x3a, 0xfa, 0xde, 0x13, 0xea, 0xdc, 0x33, 0x80, 0x4b, 0x1a, 0x35, 0x4f, 0xb5, 0x94, - 0xb1, 0x00, 0x58, 0xe8, 0x60, 0xa6, 0xc8, 0x56, 0x74, 0x6b, 0x30, 0x29, 0xe0, 0xad, 0x01, 0xfe, 0x10, 0x59, 0xa5, - 0xee, 0x8a, 0x65, 0x58, 0x7a, 0xf6, 0xd7, 0xfd, 0x7e, 0xec, 0xd9, 0x5f, 0x5f, 0x68, 0x5a, 0x17, 0xb7, 0x1b, 0x40, - 0x6a, 0x0c, 0x20, 0x72, 0xac, 0x07, 0xc2, 0x44, 0x14, 0x6b, 0xfa, 0xfe, 0x1d, 0x9b, 0x2c, 0x0a, 0x84, 0x7e, 0xa7, - 0x5e, 0x4f, 0x4a, 0x02, 0x3a, 0xb5, 0x8a, 0x1d, 0x0d, 0xb4, 0xd9, 0x07, 0x04, 0x44, 0xf5, 0x33, 0xb2, 0xf9, 0x42, - 0x39, 0x17, 0xab, 0xf0, 0xe1, 0x63, 0x0a, 0x01, 0x85, 0x3b, 0x6a, 0x74, 0xde, 0x86, 0x48, 0xa0, 0xac, 0x50, 0xc4, - 0x9a, 0x17, 0x6b, 0x49, 0xc8, 0x7c, 0xbc, 0x40, 0xc1, 0x95, 0x03, 0x76, 0xe5, 0x6c, 0x32, 0x2c, 0x23, 0xce, 0xc2, - 0xfb, 0xbf, 0x99, 0x2c, 0x08, 0x6a, 0xae, 0xfc, 0x40, 0x8e, 0x3b, 0x99, 0x1a, 0x7b, 0xaa, 0x51, 0x83, 0x60, 0x32, - 0x82, 0xc0, 0x70, 0xc3, 0x2f, 0xf8, 0xf8, 0x60, 0x41, 0x40, 0x45, 0x66, 0xcd, 0x42, 0xcc, 0x8b, 0xc3, 0x27, 0x80, - 0x1a, 0x33, 0x3a, 0x78, 0x06, 0xa0, 0xb0, 0x10, 0x10, 0x7d, 0x0c, 0x32, 0x5a, 0x01, 0xbf, 0x85, 0xfa, 0xdd, 0x3a, - 0xf1, 0x7d, 0xe8, 0x57, 0x41, 0x2f, 0x62, 0x60, 0x38, 0xa2, 0xc9, 0x7e, 0xc8, 0x07, 0x93, 0x01, 0x68, 0x4b, 0xbc, - 0xdd, 0xd7, 0xd2, 0x8a, 0x9b, 0xd3, 0xa5, 0xd3, 0xfd, 0x93, 0x36, 0x41, 0x12, 0xa9, 0x64, 0xa5, 0x22, 0x06, 0x10, - 0xca, 0x52, 0x6d, 0x93, 0x25, 0x58, 0x56, 0x98, 0x25, 0xcd, 0x0d, 0x4a, 0xe2, 0xee, 0x66, 0xe0, 0x18, 0x35, 0xeb, - 0x38, 0x2c, 0x5b, 0x6e, 0xd4, 0x00, 0x9f, 0x93, 0xb0, 0xc2, 0xde, 0x70, 0x66, 0xd2, 0x3b, 0xd3, 0xe1, 0xea, 0x98, - 0xb3, 0x37, 0x1c, 0xc1, 0x38, 0x12, 0xbc, 0xf1, 0xd0, 0x25, 0xd3, 0x50, 0x91, 0x29, 0xe3, 0x60, 0xda, 0x03, 0xdc, - 0x7b, 0x0e, 0xc6, 0x61, 0x6c, 0x50, 0x59, 0x52, 0x9f, 0x7a, 0x77, 0x21, 0x10, 0xa4, 0xb5, 0x5e, 0xe6, 0x33, 0x3c, - 0x3d, 0x23, 0x94, 0xfd, 0x21, 0x87, 0x2f, 0xc0, 0x8e, 0x82, 0x1c, 0x4d, 0xf8, 0xb3, 0xc7, 0xbb, 0x81, 0xaa, 0xf8, - 0x20, 0xd8, 0x8b, 0x45, 0xba, 0x17, 0x0c, 0x04, 0xfc, 0x2a, 0xf8, 0x5e, 0x25, 0xe5, 0xde, 0x45, 0x5c, 0xec, 0xc5, - 0xab, 0xb8, 0xa8, 0xf6, 0x6e, 0xb2, 0x6a, 0xb9, 0x67, 0x3a, 0x04, 0xd0, 0xbc, 0xc1, 0x20, 0x1e, 0x04, 0x7b, 0xc1, - 0xa0, 0x30, 0x53, 0xbb, 0x62, 0x65, 0xe3, 0x38, 0x33, 0x21, 0xca, 0x82, 0x66, 0x80, 0xb0, 0xc6, 0x69, 0x00, 0x7c, - 0xea, 0x9a, 0xa5, 0xf4, 0x02, 0xc3, 0x0d, 0x88, 0xe9, 0x1a, 0xfa, 0x00, 0x3c, 0xf2, 0x9a, 0xc6, 0xb0, 0x04, 0x2e, - 0x06, 0x03, 0xb2, 0x86, 0xc8, 0x05, 0x6b, 0x6a, 0x83, 0x38, 0x84, 0x6b, 0x65, 0xa7, 0xbd, 0x0b, 0xcc, 0xb4, 0xdd, - 0x02, 0xa2, 0xf2, 0x84, 0xf4, 0xfb, 0xf6, 0x1b, 0xea, 0x5f, 0xb0, 0x97, 0x60, 0x7f, 0x55, 0x54, 0x61, 0x22, 0x95, - 0xe6, 0xfb, 0x92, 0x1d, 0x0d, 0x54, 0xc4, 0xe1, 0x1d, 0x47, 0x8a, 0x36, 0x2a, 0x97, 0x65, 0x4f, 0x96, 0x0d, 0x5f, - 0x89, 0x2b, 0xee, 0xfc, 0xb8, 0x2a, 0x29, 0xf3, 0x2a, 0x5b, 0x29, 0xf6, 0x6f, 0xc6, 0x35, 0xf7, 0x07, 0xd6, 0x9f, - 0xcd, 0x57, 0x70, 0x6d, 0xf5, 0xde, 0x35, 0xb9, 0x46, 0xe4, 0x2c, 0xa1, 0x5c, 0x52, 0xdb, 0x3c, 0xbc, 0xa5, 0xef, - 0xf3, 0xab, 0x6f, 0x33, 0x9d, 0xc6, 0x67, 0x15, 0x16, 0x2e, 0x44, 0x2b, 0x82, 0x43, 0x43, 0x2e, 0x9a, 0x47, 0x80, - 0xb9, 0xf6, 0xd9, 0x0a, 0x0a, 0x52, 0x9f, 0x55, 0xe8, 0xdd, 0x0a, 0x09, 0xaf, 0x34, 0xbb, 0xf4, 0x30, 0x90, 0x32, - 0x6e, 0x0f, 0x2d, 0x61, 0xd2, 0xf2, 0x22, 0xbc, 0xf7, 0x9a, 0x9b, 0xdc, 0x8b, 0x10, 0xa3, 0x17, 0x79, 0x76, 0x02, - 0xc6, 0xba, 0x4b, 0x76, 0x36, 0x3c, 0xf1, 0x1b, 0x9e, 0xb3, 0x16, 0x8d, 0xa6, 0x4b, 0x96, 0xf4, 0xfb, 0x31, 0x98, - 0x78, 0xa7, 0x2c, 0x87, 0x5f, 0xf9, 0x82, 0xae, 0x19, 0x60, 0x8a, 0xd1, 0x0b, 0x48, 0x48, 0x11, 0x89, 0x64, 0xad, - 0x4e, 0x92, 0xcf, 0x74, 0x17, 0x80, 0xd1, 0x2f, 0x66, 0x69, 0xb4, 0xbc, 0xd7, 0xcc, 0x02, 0xc9, 0x33, 0xf4, 0x5d, - 0x07, 0xdb, 0x1b, 0xfb, 0x20, 0xe5, 0xfc, 0x50, 0x4c, 0x07, 0x03, 0x4e, 0x34, 0xdc, 0x78, 0xa9, 0xc4, 0xb5, 0xba, - 0xc5, 0x1d, 0xc3, 0x58, 0xea, 0xdb, 0x22, 0x06, 0x07, 0xec, 0xa2, 0x95, 0xdd, 0x3e, 0xc0, 0xbe, 0x72, 0xbc, 0x4b, - 0x95, 0xdd, 0xe9, 0x31, 0xd3, 0x5c, 0xb6, 0x9a, 0x74, 0x52, 0x71, 0x3f, 0x91, 0x6f, 0x72, 0x07, 0x5d, 0x2e, 0xc7, - 0x9a, 0xb7, 0x1c, 0x80, 0x8a, 0x7e, 0xa4, 0xa8, 0xee, 0x17, 0x38, 0xc2, 0x3c, 0x58, 0xb7, 0xf9, 0x64, 0xdf, 0x14, - 0x38, 0x44, 0x9e, 0xb4, 0xd1, 0x14, 0xd0, 0xbd, 0x8b, 0xc7, 0x5d, 0xfd, 0xb6, 0x74, 0x17, 0x28, 0xd1, 0x4e, 0xc5, - 0x0d, 0x3f, 0x26, 0xea, 0x74, 0xa6, 0x0d, 0xa1, 0x7f, 0x65, 0xc4, 0xfd, 0xa5, 0x71, 0x15, 0x6f, 0x7a, 0x97, 0xcf, - 0x38, 0xd4, 0xd9, 0x0d, 0xa1, 0x00, 0x5c, 0xb5, 0xa7, 0x53, 0x37, 0x86, 0xf4, 0x4a, 0x89, 0x6e, 0x83, 0x83, 0xdd, - 0xeb, 0x33, 0x8e, 0xa2, 0x1f, 0xa3, 0x46, 0xbe, 0x89, 0xc4, 0x63, 0x39, 0x88, 0x1f, 0x17, 0x74, 0x19, 0x89, 0xc7, - 0xc5, 0x20, 0x7e, 0x2c, 0xeb, 0x7a, 0xf7, 0x5c, 0xb9, 0xbf, 0x8f, 0xc8, 0xb3, 0xee, 0xec, 0xa5, 0x12, 0x36, 0x06, - 0x9e, 0x5d, 0x0b, 0x08, 0xa7, 0xe0, 0x89, 0x6c, 0x2d, 0x7d, 0xe8, 0xdc, 0xee, 0x63, 0xcb, 0x24, 0x41, 0xd0, 0xf3, - 0x36, 0x9b, 0x44, 0xb1, 0xb3, 0xcd, 0xa3, 0x0f, 0xa7, 0x40, 0x42, 0xb7, 0xdb, 0x66, 0x5d, 0xad, 0x01, 0xc5, 0x34, - 0x1c, 0xf3, 0xfd, 0x62, 0x74, 0xe3, 0xbb, 0xeb, 0xef, 0x17, 0xa3, 0x25, 0x19, 0x4e, 0xcc, 0xe4, 0xc7, 0x47, 0xe3, - 0x59, 0x1c, 0x4d, 0xea, 0x8e, 0xd3, 0x42, 0xe3, 0x9f, 0x7a, 0xb7, 0x50, 0x04, 0x4e, 0xc5, 0x08, 0x8e, 0x9c, 0x0a, - 0xe5, 0xa4, 0xd4, 0xc0, 0xf0, 0xdf, 0xab, 0x76, 0xb4, 0x69, 0x6f, 0xe2, 0x2a, 0x59, 0x66, 0xe2, 0x52, 0x87, 0x0f, - 0xd7, 0xd1, 0xc5, 0x6d, 0x40, 0x3b, 0xef, 0x32, 0xed, 0xf8, 0x75, 0xd2, 0xa0, 0x27, 0xae, 0x66, 0x06, 0xdc, 0xba, - 0x1f, 0xa1, 0x19, 0x02, 0xa3, 0xe5, 0xf9, 0x3b, 0xc4, 0xdc, 0xfe, 0x4d, 0xd9, 0xfc, 0x2a, 0xda, 0xe7, 0xc8, 0x48, - 0xd9, 0x26, 0x23, 0x15, 0x18, 0x61, 0x4a, 0x91, 0xc4, 0x55, 0x08, 0x81, 0xec, 0xbf, 0xa4, 0xb8, 0x16, 0x4b, 0xef, - 0x35, 0x08, 0x13, 0x6c, 0x17, 0xb4, 0x5f, 0xdd, 0xce, 0x6d, 0xa5, 0xc5, 0x1e, 0xa9, 0xef, 0x73, 0x67, 0xbb, 0xa2, - 0xc9, 0xdf, 0x97, 0x0d, 0x68, 0x03, 0x88, 0xf2, 0xbe, 0x3e, 0x2a, 0x81, 0x93, 0x11, 0x37, 0x94, 0x18, 0xbd, 0xa0, - 0xab, 0x13, 0xb9, 0x67, 0xa7, 0xe6, 0x4d, 0xc5, 0x4c, 0xc5, 0x95, 0x6f, 0xf6, 0xcc, 0x7f, 0x30, 0x14, 0x54, 0x80, - 0x81, 0xb7, 0x39, 0xe3, 0xd1, 0x81, 0xee, 0xc6, 0xe8, 0xb4, 0x60, 0xb3, 0xa0, 0x2e, 0xeb, 0xa6, 0x8d, 0x07, 0x8d, - 0x38, 0x28, 0x8a, 0x55, 0xa1, 0x46, 0xc2, 0x13, 0x81, 0x80, 0x29, 0xbb, 0xe2, 0x91, 0x11, 0xd4, 0xf4, 0x26, 0x14, - 0x36, 0x14, 0xfc, 0x55, 0xa2, 0x9a, 0xde, 0x84, 0x36, 0x99, 0x38, 0xcd, 0x20, 0x82, 0x19, 0xb1, 0xdd, 0x6f, 0x01, - 0x6d, 0x6e, 0xcd, 0x68, 0x53, 0xd7, 0x56, 0x5b, 0x85, 0x5c, 0x52, 0xa4, 0x2c, 0xff, 0x9d, 0x9a, 0x0a, 0x4a, 0x6a, - 0xb9, 0xe8, 0x4d, 0x9a, 0x2e, 0x7a, 0x3c, 0x33, 0x92, 0x40, 0xe5, 0x96, 0x3b, 0x46, 0x7f, 0x08, 0x0b, 0x3c, 0x62, - 0xe2, 0xc4, 0x82, 0xb9, 0xd5, 0x11, 0xcb, 0xe6, 0x62, 0x31, 0x5a, 0x49, 0x08, 0x1b, 0x7c, 0xc8, 0xb2, 0x79, 0xa9, - 0x1f, 0x42, 0x5f, 0x58, 0x7a, 0x02, 0x76, 0xb1, 0xc1, 0x4a, 0x96, 0x01, 0xf8, 0x5e, 0xd0, 0xcd, 0x4a, 0x96, 0x91, - 0x54, 0xdd, 0x8f, 0x6b, 0x2c, 0x41, 0xa5, 0x15, 0x2a, 0x2d, 0xa9, 0xb1, 0x20, 0xf0, 0x55, 0xd5, 0xe5, 0x43, 0xb2, - 0xab, 0x40, 0x3d, 0x75, 0xd4, 0x80, 0x53, 0xa0, 0xaa, 0xc0, 0x82, 0x24, 0xa8, 0x0c, 0x5d, 0x15, 0x98, 0x56, 0x60, - 0x9a, 0xa9, 0xc2, 0x45, 0x99, 0x1d, 0x4a, 0xb3, 0x5e, 0xf2, 0x59, 0x3c, 0x08, 0x93, 0x61, 0x4c, 0x1e, 0x23, 0xd4, - 0xfe, 0x7e, 0x1e, 0xc5, 0x5a, 0x2e, 0xb9, 0x72, 0x7e, 0xf1, 0x37, 0x9f, 0xb1, 0xd7, 0x3d, 0xc3, 0x60, 0x01, 0xce, - 0xd2, 0xf6, 0x2a, 0x13, 0xef, 0x64, 0x2b, 0x38, 0x0e, 0x66, 0x51, 0x0e, 0xab, 0x9e, 0x1c, 0xd1, 0x5c, 0xe4, 0xda, - 0xbb, 0x08, 0x91, 0x83, 0xcc, 0x1e, 0x03, 0xec, 0x46, 0xf8, 0x3a, 0xb4, 0x36, 0xb7, 0xba, 0x42, 0xfc, 0x8d, 0x12, - 0x89, 0x9f, 0xa5, 0xfc, 0xb8, 0x5e, 0xa9, 0x5c, 0x95, 0xc1, 0x63, 0xd5, 0xcd, 0xe0, 0x99, 0xf6, 0x3d, 0xd6, 0xfe, - 0xad, 0xed, 0xe6, 0x78, 0xef, 0xc1, 0x83, 0xd6, 0xff, 0xd6, 0x93, 0x10, 0xda, 0x2b, 0x27, 0xa9, 0x3b, 0x6a, 0xf4, - 0xcc, 0x64, 0x8d, 0xa8, 0x84, 0xa9, 0xdd, 0xa9, 0x1c, 0x03, 0x35, 0x1d, 0xc0, 0xb5, 0x44, 0x4d, 0xd0, 0x93, 0x82, - 0x8d, 0xe1, 0x88, 0xb3, 0x38, 0x68, 0x87, 0x31, 0x8a, 0x97, 0x73, 0x25, 0x5e, 0xce, 0x8f, 0x18, 0x07, 0x68, 0x2d, - 0x40, 0xaa, 0xd7, 0xb0, 0x9f, 0xb9, 0x82, 0x05, 0x36, 0x77, 0xbe, 0x03, 0x0b, 0x64, 0x88, 0x93, 0xcd, 0x71, 0xb2, - 0xc7, 0xb5, 0x9e, 0x7b, 0x81, 0x8f, 0x93, 0x7a, 0xe1, 0xd5, 0x55, 0xb6, 0xeb, 0x5a, 0xb2, 0x72, 0x5e, 0x0c, 0x26, - 0x10, 0x94, 0xa5, 0x9c, 0x17, 0xc3, 0xc9, 0x82, 0xe6, 0xf0, 0x63, 0xd1, 0x40, 0x87, 0x58, 0x0e, 0x12, 0xb8, 0x74, - 0xf6, 0x18, 0xf0, 0x86, 0x52, 0x8b, 0xbb, 0xb1, 0x8e, 0x1c, 0xeb, 0x28, 0xf6, 0xc3, 0x18, 0x70, 0x65, 0x9d, 0xc0, - 0xfb, 0xfe, 0xeb, 0x63, 0x13, 0x90, 0x55, 0xbb, 0xc2, 0xab, 0x51, 0xee, 0xba, 0xd2, 0xe8, 0x4b, 0x4a, 0x4f, 0x78, - 0xc1, 0x53, 0xc9, 0x76, 0xdb, 0x33, 0x70, 0xb6, 0xc4, 0x43, 0xe2, 0x1d, 0x23, 0x7a, 0x31, 0x6d, 0x64, 0xe6, 0x04, - 0xce, 0x6c, 0x77, 0xd9, 0xc6, 0xfc, 0xd8, 0x01, 0x0e, 0x16, 0x41, 0x48, 0xdc, 0x10, 0x86, 0x89, 0x1d, 0x95, 0x43, - 0x2d, 0x84, 0xeb, 0x5a, 0x78, 0x1d, 0xa7, 0x65, 0x0c, 0x2e, 0xd2, 0xda, 0x36, 0xf1, 0x1e, 0xba, 0xee, 0xf9, 0x31, - 0xb7, 0x3a, 0x46, 0x5b, 0x48, 0xbf, 0x1d, 0x9d, 0xde, 0x73, 0x18, 0x80, 0xa6, 0x07, 0xb3, 0xaa, 0x7d, 0x26, 0x71, - 0x73, 0xda, 0x09, 0x42, 0x22, 0x10, 0x45, 0xe9, 0x8c, 0x30, 0xfd, 0x3b, 0xcd, 0x65, 0x15, 0xad, 0x1e, 0xe4, 0x99, - 0x43, 0x9e, 0x85, 0xde, 0xf6, 0xa0, 0x55, 0x73, 0x37, 0x18, 0x27, 0x6e, 0xb7, 0x77, 0xfe, 0xdf, 0xb2, 0xae, 0xad, - 0xd6, 0x88, 0xc7, 0xed, 0xea, 0x07, 0x8d, 0xbd, 0xda, 0x53, 0x31, 0x60, 0x56, 0xd2, 0x3b, 0xa3, 0x4a, 0x5e, 0x64, - 0xbc, 0xc4, 0x93, 0x6a, 0xd5, 0xf0, 0xf1, 0xbe, 0xc9, 0x46, 0xe6, 0x81, 0x4c, 0x01, 0xf1, 0xfc, 0x26, 0x35, 0xea, - 0xe3, 0x14, 0x25, 0xe0, 0xef, 0x74, 0x7c, 0x23, 0xfa, 0xd1, 0xbe, 0xb8, 0xe4, 0xd5, 0xc9, 0x8d, 0x30, 0x2f, 0x5e, - 0x58, 0x9d, 0x3f, 0x7d, 0x53, 0xf8, 0xd0, 0xe1, 0xa8, 0xbd, 0x83, 0x22, 0x4b, 0x26, 0x8e, 0x26, 0x46, 0xd6, 0x26, - 0x66, 0x1f, 0x15, 0x5c, 0x4c, 0x54, 0xa1, 0x67, 0x9d, 0x3d, 0x61, 0x0a, 0xd0, 0x37, 0x8e, 0x51, 0xc9, 0x18, 0x16, - 0x0c, 0xd4, 0x69, 0x4a, 0x88, 0x1e, 0x8a, 0x19, 0xc6, 0x2b, 0x06, 0x50, 0x98, 0x42, 0x81, 0x28, 0x3a, 0xfb, 0x70, - 0xa0, 0x09, 0xfd, 0xfe, 0x4d, 0xaa, 0x33, 0xd0, 0xb2, 0x9e, 0x4a, 0x10, 0xd5, 0x41, 0xb4, 0x55, 0x5e, 0x84, 0x3f, - 0x2e, 0x69, 0x99, 0xd1, 0xa5, 0xa0, 0xa9, 0xa0, 0x49, 0x46, 0x2f, 0xb8, 0x12, 0x15, 0x5f, 0x08, 0xa6, 0x68, 0xbb, - 0x21, 0xec, 0xff, 0x6a, 0xd0, 0xf5, 0x56, 0xac, 0x35, 0xb4, 0x3b, 0x41, 0x46, 0x68, 0xbe, 0xd0, 0x41, 0xc8, 0x50, - 0x39, 0x09, 0x5d, 0xab, 0x34, 0x5e, 0x81, 0x4b, 0xa6, 0xd9, 0x68, 0x19, 0x97, 0x61, 0x60, 0xbf, 0x0a, 0x2c, 0x26, - 0x07, 0x26, 0x9d, 0xae, 0xcf, 0x9f, 0xcb, 0xab, 0x95, 0x14, 0x5c, 0x54, 0x0a, 0xa2, 0xdf, 0xe0, 0xbe, 0x9b, 0xb8, - 0xea, 0xac, 0x59, 0x2b, 0x7d, 0xe8, 0x5b, 0x9f, 0xb5, 0x71, 0x5f, 0x18, 0x1c, 0x83, 0x9d, 0x8f, 0x88, 0x81, 0x34, - 0xa8, 0x74, 0x8b, 0x43, 0x13, 0xa0, 0x4b, 0x87, 0x14, 0xb2, 0x64, 0x2a, 0x53, 0x25, 0xa8, 0xf8, 0xc6, 0xef, 0xa5, - 0xac, 0x46, 0x7f, 0xad, 0x79, 0x71, 0x77, 0xca, 0x73, 0x8e, 0x63, 0x14, 0x24, 0xb1, 0xb8, 0x8e, 0xcb, 0x80, 0xf8, - 0x96, 0x57, 0xc1, 0x41, 0x6a, 0xc2, 0xc6, 0xec, 0x54, 0x8d, 0x5a, 0x2f, 0x89, 0xbe, 0x32, 0xca, 0x37, 0x06, 0x43, - 0x13, 0x51, 0x05, 0x7d, 0xaf, 0xd5, 0x3d, 0xad, 0x6e, 0x58, 0x40, 0xfc, 0xb9, 0xd2, 0x0b, 0xb5, 0x5e, 0x37, 0x63, - 0x6e, 0x98, 0x08, 0x41, 0xa3, 0x27, 0xf5, 0xa2, 0xf6, 0xdc, 0xd2, 0x54, 0x64, 0xdc, 0x68, 0x93, 0xf3, 0x4b, 0x90, - 0xf1, 0x39, 0x73, 0xa1, 0x49, 0x5d, 0x53, 0x05, 0x55, 0x18, 0x6d, 0x6e, 0x1b, 0xe9, 0xf4, 0x0e, 0xdc, 0xd9, 0x8c, - 0xd9, 0x91, 0x76, 0x69, 0xac, 0x69, 0xc1, 0xcb, 0x95, 0x14, 0x25, 0x84, 0x71, 0xee, 0x8d, 0xe9, 0x55, 0x9c, 0x89, - 0x2a, 0xce, 0xc4, 0x71, 0xb9, 0xe2, 0x49, 0xf5, 0x1e, 0x6e, 0x71, 0xca, 0xea, 0xa6, 0x2e, 0xe1, 0x4a, 0x97, 0xec, - 0x61, 0x30, 0x35, 0x15, 0xf7, 0xd8, 0x19, 0x5c, 0x54, 0x7f, 0x44, 0x4b, 0x89, 0xb1, 0x50, 0x75, 0xf1, 0xf1, 0x79, - 0x29, 0xf3, 0x75, 0x05, 0xda, 0xdd, 0x8b, 0x2a, 0x3a, 0x78, 0xba, 0xba, 0x9d, 0xaa, 0x1b, 0x4c, 0xf4, 0xf4, 0x60, - 0x75, 0xdb, 0xcb, 0xae, 0x56, 0xb2, 0xa8, 0x62, 0x51, 0x4d, 0x15, 0x22, 0x59, 0x12, 0xe7, 0x49, 0x38, 0x19, 0x8f, - 0xbf, 0xda, 0x1b, 0xee, 0x41, 0x06, 0x32, 0xfd, 0x34, 0x54, 0x2e, 0x47, 0xc3, 0xc9, 0x78, 0x3c, 0x95, 0xea, 0x6e, - 0x17, 0x8d, 0x26, 0x35, 0xd6, 0x33, 0x4c, 0xf4, 0xcc, 0x8c, 0xf8, 0xed, 0x2a, 0x16, 0x29, 0xc4, 0xaf, 0xd3, 0xc5, - 0x1f, 0x3c, 0x1d, 0x37, 0xca, 0xb7, 0x9f, 0x3e, 0xab, 0xff, 0xa8, 0x4d, 0x58, 0x6b, 0xd3, 0xee, 0xe7, 0x7f, 0x1c, - 0xaa, 0xf9, 0x3e, 0x3a, 0xdc, 0xd7, 0x3f, 0xfe, 0xa8, 0xeb, 0xe9, 0x9b, 0x22, 0x9c, 0xff, 0x33, 0x54, 0xf3, 0x79, - 0x5c, 0x14, 0xf1, 0x5d, 0x0d, 0x91, 0x3c, 0x85, 0xf3, 0x26, 0xa1, 0xde, 0x36, 0xa0, 0x07, 0x64, 0x7a, 0x21, 0x18, - 0x7c, 0xf3, 0xbe, 0x0a, 0x03, 0x5e, 0xae, 0x86, 0x5c, 0x54, 0x59, 0x75, 0x37, 0xc4, 0x3c, 0x01, 0x7e, 0x6a, 0x78, - 0xb3, 0xe7, 0x85, 0x21, 0x36, 0x17, 0x05, 0xe7, 0x9f, 0x78, 0xa8, 0x8c, 0xa3, 0xc7, 0x68, 0x1c, 0x3d, 0xa6, 0x6a, - 0x30, 0x26, 0xdf, 0x50, 0xdd, 0x99, 0xc9, 0x37, 0x60, 0x82, 0x94, 0xb5, 0xbf, 0x51, 0xc6, 0x89, 0xd1, 0x98, 0x5e, - 0xbf, 0xca, 0xb3, 0x15, 0x30, 0xc1, 0x4b, 0xfd, 0xa3, 0x26, 0xf4, 0x3d, 0x6f, 0x67, 0x1f, 0x8d, 0x46, 0xcf, 0x0b, - 0x3a, 0x1a, 0x8d, 0x3e, 0x66, 0x35, 0xa1, 0x2b, 0xd1, 0xf1, 0xfe, 0x3d, 0xa7, 0xe7, 0x32, 0xbd, 0x8b, 0x82, 0x80, - 0x2e, 0xb3, 0x34, 0xe5, 0x42, 0x95, 0x75, 0x9a, 0xb6, 0xf3, 0xaa, 0x16, 0x22, 0xf0, 0x8f, 0x6e, 0x23, 0x42, 0x10, - 0x11, 0x7a, 0xb2, 0xd3, 0xb3, 0xd1, 0x68, 0x74, 0x9a, 0x9a, 0x6a, 0x1d, 0x43, 0xfe, 0x06, 0xcd, 0x07, 0x9c, 0x5d, - 0x3e, 0x58, 0xdf, 0x98, 0x68, 0x27, 0xfb, 0xff, 0x3d, 0x9c, 0xcd, 0xc7, 0xc3, 0x6f, 0x47, 0x8b, 0xc7, 0xfb, 0x34, - 0x08, 0x7c, 0xd0, 0xea, 0x50, 0x5b, 0x73, 0x4c, 0xcb, 0xc3, 0xf1, 0x94, 0x94, 0x03, 0xf6, 0xd4, 0xfa, 0xd2, 0x7c, - 0xf5, 0x14, 0x90, 0x48, 0x51, 0x84, 0x1a, 0x38, 0xe9, 0x1f, 0x5e, 0x45, 0x5e, 0x0b, 0xc0, 0x47, 0xb3, 0x91, 0x0c, - 0x8c, 0x16, 0x70, 0x1c, 0x41, 0x79, 0xb5, 0x35, 0x8d, 0xe8, 0x31, 0x96, 0x99, 0xa8, 0xa0, 0xe3, 0x69, 0x79, 0x93, - 0x55, 0xc9, 0x12, 0x03, 0x1b, 0xc5, 0x25, 0x0f, 0xbe, 0x0a, 0xa2, 0x92, 0x1d, 0x3c, 0x9b, 0x2a, 0x78, 0x5f, 0x4c, - 0x4a, 0xf9, 0x25, 0x24, 0x7e, 0x3b, 0x46, 0x08, 0x54, 0xa2, 0x3d, 0x16, 0xb1, 0xc6, 0x57, 0xb9, 0x8c, 0xc1, 0x83, - 0xb3, 0xd4, 0x3c, 0x8b, 0x3d, 0x09, 0xac, 0xfd, 0x45, 0xab, 0x39, 0x12, 0x9a, 0x13, 0x4a, 0x26, 0xf7, 0x4b, 0x2a, - 0xbf, 0x9a, 0xa0, 0x57, 0x10, 0xb8, 0x55, 0x47, 0x70, 0xdc, 0x59, 0xcb, 0x06, 0xbd, 0x7c, 0x52, 0xb6, 0x3f, 0xff, - 0xdf, 0x25, 0x5d, 0x0c, 0xf6, 0xdd, 0xd0, 0x9c, 0x68, 0xf7, 0xd5, 0x0a, 0x19, 0xa5, 0x2a, 0x7c, 0x9e, 0x12, 0x6b, - 0x7c, 0xca, 0xd9, 0xd1, 0xc6, 0x74, 0x67, 0x54, 0x15, 0xd9, 0x55, 0x48, 0x74, 0xaf, 0x1c, 0x28, 0x66, 0x10, 0x65, - 0x23, 0x5c, 0x3f, 0x60, 0x2d, 0xe2, 0x75, 0xf2, 0x9a, 0x17, 0x55, 0x96, 0xa8, 0xf7, 0xd7, 0x8d, 0xf7, 0x40, 0x0d, - 0x54, 0x83, 0xde, 0x15, 0x0c, 0xe6, 0xf9, 0xa4, 0x00, 0xd0, 0xce, 0x92, 0x17, 0xd7, 0xdc, 0xa7, 0x1b, 0x41, 0x50, - 0xbb, 0x66, 0x5e, 0x36, 0x82, 0x4d, 0xc0, 0x57, 0xef, 0x0a, 0xc0, 0xdc, 0x08, 0x41, 0x6a, 0x0a, 0xa1, 0x70, 0xe0, - 0x02, 0x5f, 0x55, 0x45, 0x76, 0xbe, 0xae, 0x38, 0x06, 0xfb, 0xf0, 0xe2, 0x26, 0x2a, 0x27, 0x3c, 0x1e, 0x06, 0xf8, - 0x23, 0xa0, 0x2a, 0xe0, 0x86, 0xf1, 0xb0, 0x83, 0x17, 0xea, 0x97, 0x7b, 0xa3, 0xf6, 0x08, 0x7b, 0x93, 0x86, 0x10, - 0x5c, 0x07, 0x1f, 0x02, 0x58, 0x52, 0x84, 0x9e, 0xe0, 0xa9, 0x1a, 0x06, 0x17, 0x79, 0xb6, 0xd2, 0x49, 0xd5, 0xa8, - 0xa3, 0xf9, 0x50, 0x6a, 0x47, 0x72, 0x40, 0xbd, 0xf4, 0x18, 0xd3, 0x0b, 0x95, 0xae, 0x8a, 0x72, 0x46, 0x28, 0xef, - 0xf4, 0xc4, 0xb8, 0x30, 0x7d, 0x1c, 0x22, 0xbf, 0xbc, 0x2b, 0x54, 0xe8, 0x17, 0xbe, 0x00, 0xf0, 0x2b, 0xb8, 0xdd, - 0xef, 0xc6, 0x77, 0x51, 0xd9, 0xcf, 0x38, 0xdb, 0xff, 0xef, 0x79, 0x3c, 0xfc, 0x34, 0x1e, 0x7e, 0xbb, 0x18, 0x84, - 0x43, 0xfb, 0x93, 0x3c, 0x7e, 0xb4, 0x4f, 0x5f, 0x71, 0xcb, 0x95, 0xc0, 0xc2, 0x6f, 0x04, 0xb7, 0x51, 0x2b, 0x21, - 0x88, 0x02, 0xbc, 0x51, 0xb8, 0xd5, 0x38, 0x01, 0x80, 0xbf, 0xe0, 0xbf, 0x02, 0x34, 0x12, 0x72, 0x17, 0x0d, 0xd0, - 0x0f, 0xa8, 0xdf, 0x47, 0x4f, 0x1a, 0x06, 0x72, 0x20, 0x9e, 0x50, 0x31, 0x50, 0x88, 0x2e, 0x63, 0xa2, 0x60, 0x7f, - 0x6d, 0xf6, 0xed, 0xb6, 0xd7, 0x96, 0xfc, 0xe0, 0x97, 0x7e, 0xa6, 0x89, 0x99, 0x77, 0xb8, 0xa1, 0xac, 0xe4, 0x2a, - 0x44, 0x6c, 0x3c, 0xfd, 0x2b, 0x67, 0x10, 0x6b, 0xf2, 0x3a, 0x03, 0x1f, 0x06, 0xfb, 0xc5, 0x78, 0x06, 0x6c, 0x03, - 0xdc, 0x71, 0x0a, 0x7e, 0x91, 0x81, 0x5b, 0xb3, 0x88, 0xf1, 0x82, 0x6d, 0x97, 0x44, 0xbf, 0xdf, 0xcb, 0xb3, 0x30, - 0xd7, 0x38, 0xcb, 0x79, 0x6d, 0xc4, 0xee, 0xa8, 0x13, 0x06, 0x71, 0xbb, 0x1e, 0x82, 0xa1, 0x1a, 0x82, 0xa2, 0xa3, - 0x2d, 0xae, 0x5e, 0x5b, 0x4f, 0x61, 0x7a, 0xab, 0xea, 0x2b, 0x46, 0x7f, 0xca, 0x4c, 0x60, 0x21, 0xed, 0x9a, 0x63, - 0x5d, 0x73, 0x8c, 0xb4, 0xa7, 0xdf, 0x17, 0x0d, 0xf2, 0xd3, 0x59, 0x78, 0x10, 0xa8, 0x52, 0xe5, 0x4e, 0x59, 0x94, - 0xdb, 0xd2, 0xbc, 0x31, 0xac, 0x69, 0x9e, 0xd9, 0xb8, 0x2e, 0xb3, 0x5e, 0x2f, 0x0c, 0xd1, 0xa1, 0x11, 0x4b, 0xc5, - 0xda, 0x20, 0xbc, 0x8f, 0x49, 0x18, 0x5d, 0x81, 0xac, 0x2e, 0x3c, 0xe3, 0x04, 0xf9, 0x32, 0x30, 0x59, 0x53, 0xd5, - 0x7a, 0x39, 0xe1, 0xb1, 0x91, 0x2f, 0x1b, 0x41, 0x83, 0xbc, 0xa4, 0xa8, 0x37, 0x71, 0x3b, 0xf6, 0x51, 0x0b, 0xa9, - 0x71, 0x53, 0x4f, 0x7b, 0x9a, 0x54, 0xf4, 0x58, 0xaf, 0x52, 0xbf, 0xc0, 0xb2, 0xc0, 0x92, 0x0f, 0x42, 0x7b, 0x9a, - 0x56, 0x60, 0x86, 0x6b, 0x9b, 0xc1, 0xd0, 0x0f, 0x87, 0xb6, 0x08, 0x9d, 0x51, 0xdb, 0x12, 0xc2, 0xb6, 0x0d, 0xc2, - 0xca, 0x7b, 0x22, 0x5f, 0x3d, 0xf5, 0x18, 0xe1, 0x90, 0x9b, 0xcd, 0x2c, 0x1a, 0x18, 0xe6, 0x57, 0xb2, 0xd9, 0x3c, - 0xdd, 0x5c, 0x2f, 0x2a, 0xa6, 0x80, 0xed, 0xb6, 0x12, 0x04, 0xff, 0x7e, 0xcc, 0x66, 0xf8, 0x37, 0xeb, 0xf7, 0x7b, - 0x21, 0xfe, 0xe2, 0x18, 0xbc, 0x67, 0x2e, 0x16, 0xec, 0x23, 0xc8, 0x54, 0x48, 0x84, 0xa9, 0xca, 0xf8, 0x8d, 0x55, - 0x60, 0x01, 0x67, 0x3e, 0x50, 0xb9, 0x30, 0x93, 0xbd, 0xbc, 0xb8, 0x86, 0x1c, 0xb7, 0xc6, 0x29, 0x1b, 0x65, 0x89, - 0x72, 0x5d, 0xc8, 0x46, 0x71, 0x9e, 0xc5, 0x25, 0x2f, 0xb7, 0x5b, 0x7d, 0x38, 0x26, 0x05, 0x07, 0xf6, 0x54, 0x51, - 0xa9, 0x92, 0x75, 0xa4, 0xba, 0xf1, 0x97, 0x61, 0x81, 0xfb, 0x94, 0xcf, 0x0b, 0x43, 0x23, 0xf6, 0xe0, 0xf2, 0xce, - 0xd4, 0xad, 0xb4, 0x17, 0x16, 0xd0, 0xbc, 0x92, 0x90, 0x0d, 0xa6, 0x7a, 0x16, 0xad, 0x31, 0x13, 0xf3, 0x62, 0x01, - 0x61, 0x64, 0x8a, 0x05, 0xd8, 0x4c, 0x71, 0x01, 0x5e, 0x24, 0x31, 0xc0, 0xc4, 0xc5, 0x64, 0x0a, 0xf1, 0xcc, 0x55, - 0x39, 0xf1, 0xc2, 0xdc, 0x2f, 0x13, 0x87, 0x94, 0x01, 0xaf, 0x6a, 0x83, 0x26, 0x66, 0x1b, 0x8e, 0x3a, 0x41, 0x4e, - 0x4c, 0x7e, 0x3f, 0x55, 0x10, 0xe2, 0x4e, 0x1c, 0x09, 0x97, 0x15, 0xdb, 0x85, 0x97, 0x1d, 0x88, 0x31, 0x6a, 0x70, - 0xca, 0xcf, 0x0c, 0x8e, 0xc6, 0xe0, 0xdc, 0x78, 0x27, 0x48, 0x11, 0xc6, 0x64, 0x23, 0xd9, 0x95, 0x0c, 0xc5, 0x3c, - 0x5e, 0x80, 0xb2, 0x2e, 0x5e, 0x80, 0x65, 0x8d, 0x31, 0xc0, 0x04, 0x79, 0x15, 0xf7, 0x42, 0x3f, 0x51, 0x5c, 0x21, - 0xd2, 0xb3, 0x72, 0x7d, 0x54, 0xb4, 0x43, 0x5f, 0xe0, 0xf5, 0x5e, 0x99, 0xe3, 0x66, 0x3d, 0x16, 0x48, 0x6c, 0x08, - 0x18, 0x1b, 0xe9, 0x34, 0xd5, 0x5a, 0xf7, 0xc6, 0xcc, 0x03, 0x9f, 0x66, 0x23, 0x21, 0xab, 0xb3, 0x0b, 0x10, 0xa1, - 0xf8, 0x68, 0xf0, 0xc8, 0x2f, 0xe2, 0xce, 0x32, 0x6f, 0x6d, 0x8b, 0x4a, 0x76, 0xb4, 0x01, 0x90, 0x3e, 0x1d, 0x2d, - 0x4a, 0xc9, 0x29, 0x4a, 0x52, 0xbb, 0x4d, 0x01, 0x2b, 0xc9, 0x5f, 0xc0, 0x10, 0x6c, 0x6c, 0x4f, 0x38, 0x9d, 0x22, - 0xc4, 0x27, 0x9a, 0x22, 0xb2, 0x62, 0x58, 0x52, 0x1c, 0xdb, 0x12, 0x51, 0x3f, 0x6d, 0x59, 0x76, 0x30, 0x4c, 0x54, - 0xdc, 0x17, 0xa9, 0x47, 0x89, 0x82, 0x80, 0xea, 0x21, 0x07, 0x89, 0xad, 0x6d, 0x20, 0x3c, 0x20, 0x8f, 0xe8, 0x8d, - 0xf5, 0xf7, 0x59, 0xe7, 0xd9, 0x85, 0xe6, 0xa8, 0x5c, 0xef, 0x0a, 0x33, 0x46, 0x78, 0x92, 0x19, 0x98, 0x7c, 0xef, - 0x3c, 0x33, 0x6a, 0x8a, 0x9e, 0x87, 0x4f, 0x76, 0x8c, 0x11, 0xe9, 0xee, 0x19, 0x74, 0x13, 0xbc, 0xaa, 0xc3, 0x46, - 0xbb, 0x52, 0x10, 0x12, 0xa6, 0x16, 0x4d, 0xcc, 0x7a, 0xd6, 0x80, 0x7a, 0xbb, 0xed, 0xe9, 0xb9, 0xba, 0x7f, 0xee, - 0xb6, 0xdb, 0x1e, 0x76, 0xeb, 0x45, 0xda, 0x6d, 0x05, 0x5e, 0xa9, 0x0f, 0xda, 0xe3, 0xcf, 0xdd, 0xf8, 0x73, 0x83, - 0xe4, 0x51, 0x3a, 0x9a, 0x69, 0xeb, 0x83, 0x70, 0xb8, 0xe9, 0x5d, 0xa3, 0x49, 0xdf, 0x67, 0xa1, 0xa4, 0x2b, 0xd1, - 0xa8, 0xae, 0x76, 0x26, 0x95, 0x0f, 0xae, 0xff, 0x87, 0x57, 0x01, 0x1e, 0x71, 0x6a, 0x67, 0xdf, 0xdb, 0xa0, 0xa2, - 0xd1, 0x16, 0x8e, 0x14, 0xa1, 0x07, 0x24, 0xe1, 0xbe, 0x96, 0xb5, 0xb8, 0xcd, 0xd3, 0xec, 0x61, 0xfa, 0xf4, 0x3a, - 0xf5, 0xad, 0xee, 0xdd, 0x32, 0xcb, 0xcc, 0x81, 0x57, 0x51, 0x1c, 0xd0, 0xa8, 0x8b, 0xf6, 0x5d, 0x65, 0x65, 0x09, - 0x5e, 0x1e, 0x70, 0x7d, 0x3e, 0xe5, 0x3e, 0xdc, 0xdc, 0x65, 0xd5, 0xdc, 0xa4, 0xa7, 0xd9, 0x3c, 0x5b, 0x6c, 0xb7, - 0x21, 0xfe, 0xed, 0x6a, 0x91, 0xa3, 0xc9, 0x73, 0xd0, 0xe1, 0x61, 0xe4, 0x1e, 0xa6, 0x1b, 0xe7, 0x6d, 0xfe, 0x4f, - 0xa2, 0xe1, 0x24, 0x70, 0x0c, 0xf4, 0x62, 0xf6, 0x08, 0x64, 0x30, 0xc6, 0xa9, 0x5f, 0xcc, 0xf4, 0x9a, 0x81, 0xe8, - 0x5b, 0x22, 0x02, 0x1c, 0x5d, 0x6c, 0x24, 0x1a, 0x59, 0x70, 0x52, 0x13, 0xb0, 0xd8, 0xb4, 0xe5, 0x7d, 0xb0, 0xb4, - 0xad, 0x2a, 0xee, 0xbc, 0x25, 0xcd, 0x71, 0x1d, 0x38, 0xdb, 0x7e, 0x33, 0xc4, 0xa6, 0xec, 0x6a, 0x81, 0xdc, 0x2f, - 0xaf, 0x69, 0x6f, 0x5c, 0x27, 0x30, 0x6b, 0x9b, 0xda, 0x32, 0x7e, 0xb6, 0xf4, 0x9f, 0xf5, 0xe0, 0x2a, 0xe3, 0xa7, - 0xb9, 0xb1, 0x4a, 0xb0, 0xfb, 0xc6, 0xf3, 0x1d, 0x80, 0x70, 0x6c, 0x3e, 0x3d, 0x3e, 0xcd, 0x3c, 0x7a, 0x0c, 0x44, - 0xc7, 0x7c, 0x54, 0xba, 0x8f, 0xec, 0xee, 0xf5, 0x03, 0xe0, 0xcd, 0xab, 0x76, 0x41, 0xf3, 0x72, 0x01, 0x81, 0x44, - 0xbd, 0xf2, 0x0a, 0xcb, 0x67, 0xc6, 0xec, 0x12, 0xc8, 0x50, 0x41, 0xc0, 0x26, 0xa9, 0xeb, 0x5c, 0x88, 0x55, 0x87, - 0x95, 0xf9, 0x48, 0xc2, 0x8e, 0x42, 0x34, 0xe7, 0x0c, 0x66, 0xc1, 0x7f, 0x05, 0x83, 0x72, 0x10, 0x44, 0x41, 0x14, - 0x04, 0x64, 0x50, 0xc0, 0x2f, 0xc4, 0x19, 0x23, 0x18, 0xa3, 0x04, 0x3a, 0xfc, 0x8e, 0x33, 0x9f, 0x11, 0x79, 0xd9, - 0x08, 0x63, 0xe9, 0x06, 0xe0, 0x5c, 0xca, 0x9c, 0xc7, 0xe8, 0x63, 0xf1, 0x8e, 0xb3, 0x8c, 0xd0, 0x77, 0xde, 0xa9, - 0xfc, 0x88, 0x37, 0x82, 0xdb, 0xed, 0x0e, 0xdb, 0x2b, 0x1e, 0x66, 0xb4, 0x37, 0xa6, 0xef, 0x38, 0x89, 0xb2, 0x86, - 0xf3, 0x30, 0x87, 0x9e, 0x55, 0x96, 0xb5, 0xa2, 0x86, 0xdc, 0xa0, 0x58, 0x17, 0x59, 0x26, 0x27, 0xc3, 0x55, 0x73, - 0x2a, 0x70, 0xdd, 0xd9, 0xf5, 0x02, 0x92, 0x32, 0xa1, 0x59, 0x3a, 0x1b, 0xbe, 0xda, 0xb6, 0xec, 0x45, 0xeb, 0x14, - 0xf2, 0x1a, 0xa2, 0xa2, 0x1f, 0x3a, 0x02, 0x6a, 0x68, 0xc5, 0x65, 0x05, 0x2e, 0xbb, 0xa6, 0x3d, 0xdc, 0xb4, 0xc7, - 0x34, 0xe3, 0x03, 0xc4, 0x88, 0xe3, 0xd8, 0x32, 0xb0, 0x9b, 0x70, 0x78, 0x36, 0xce, 0xf7, 0x65, 0x97, 0xde, 0xba, - 0x5a, 0x3c, 0xc2, 0xda, 0xf3, 0x56, 0x48, 0x08, 0x90, 0x96, 0xa6, 0xd2, 0xed, 0x36, 0x08, 0x60, 0x80, 0xfb, 0xfd, - 0x1e, 0x70, 0xad, 0x86, 0x9d, 0x34, 0xb7, 0x66, 0x4b, 0xec, 0x15, 0x85, 0xc7, 0x40, 0x94, 0x9a, 0xff, 0x0c, 0x02, - 0x8a, 0xe7, 0x6e, 0x08, 0xf6, 0x95, 0xec, 0x68, 0x53, 0xf4, 0xfb, 0x2f, 0x0a, 0x7c, 0x40, 0x39, 0x28, 0x88, 0x75, - 0x75, 0xdc, 0x0a, 0xc3, 0x3e, 0xa9, 0x0f, 0x71, 0x2c, 0xf2, 0x2c, 0x74, 0x84, 0xa5, 0x32, 0x84, 0x85, 0x2b, 0x46, - 0x3a, 0x88, 0x83, 0x9a, 0x74, 0x0e, 0x56, 0xe5, 0x82, 0x0d, 0xf7, 0x7a, 0x9f, 0x00, 0x16, 0x3c, 0xf3, 0x86, 0xe5, - 0xbd, 0x07, 0x00, 0xd6, 0xeb, 0xe1, 0x42, 0x71, 0x2f, 0x5f, 0x35, 0xd0, 0x27, 0xf1, 0xa5, 0x65, 0xd7, 0x67, 0x5a, - 0x56, 0x32, 0x1a, 0x8d, 0xaa, 0x5a, 0x49, 0x3e, 0x1c, 0x79, 0x69, 0xa1, 0x56, 0xca, 0x38, 0xe5, 0x29, 0x58, 0x7a, - 0x1b, 0x4a, 0x37, 0x5f, 0xd0, 0x15, 0x17, 0xa9, 0xfa, 0xe9, 0xa1, 0x4d, 0x36, 0x88, 0x6b, 0xd6, 0xd4, 0x59, 0xd8, - 0xe1, 0x87, 0x80, 0x8f, 0xf6, 0x61, 0xe6, 0xd2, 0x35, 0x2c, 0x2d, 0x88, 0x23, 0xe3, 0x82, 0x87, 0x2e, 0x0f, 0x60, - 0xfd, 0x99, 0x43, 0x12, 0x3f, 0x85, 0x9f, 0x33, 0x93, 0xd6, 0xf1, 0x19, 0xce, 0x66, 0x54, 0xaa, 0x1b, 0x41, 0xfb, - 0x35, 0x24, 0x12, 0x83, 0x6c, 0xdc, 0x60, 0x28, 0x5a, 0x77, 0x1b, 0xb8, 0xf2, 0x5b, 0x7a, 0xe7, 0xd3, 0x20, 0xc0, - 0xb6, 0xc6, 0x62, 0x00, 0x30, 0x14, 0x7f, 0xa0, 0xaa, 0xc6, 0x5c, 0x51, 0x4c, 0xc3, 0x54, 0xa2, 0xbd, 0xe3, 0xb8, - 0x8e, 0x1a, 0x57, 0x59, 0xc1, 0x4a, 0x6b, 0xcb, 0xeb, 0xde, 0xd2, 0xc2, 0x96, 0x80, 0x6a, 0x30, 0xdc, 0x09, 0xe0, - 0x33, 0x22, 0xd5, 0x81, 0x20, 0xbb, 0x0f, 0x0e, 0x9a, 0xb3, 0x04, 0xcf, 0xc3, 0x10, 0xfe, 0xc0, 0xc2, 0x81, 0x65, - 0xa9, 0xfa, 0xb9, 0x9c, 0xc6, 0x70, 0xee, 0xe6, 0x6a, 0x87, 0xcf, 0x96, 0xa0, 0xc8, 0x53, 0x73, 0x6a, 0x2e, 0x5f, - 0x79, 0x63, 0xbf, 0xc7, 0x04, 0xf3, 0x98, 0xd9, 0x86, 0xdf, 0x7a, 0xba, 0xad, 0x2f, 0xac, 0x1b, 0x38, 0x69, 0x2f, - 0x9c, 0xf6, 0x62, 0xbb, 0x34, 0x10, 0x77, 0x75, 0x43, 0x88, 0xf0, 0x5a, 0x13, 0x8b, 0xac, 0x21, 0xd3, 0xb1, 0xd8, - 0x18, 0xaa, 0x4d, 0xc5, 0x73, 0xad, 0x10, 0x2f, 0xa7, 0xea, 0xc2, 0xd4, 0x4a, 0x65, 0xc2, 0x20, 0xcc, 0x94, 0xb0, - 0xa8, 0x32, 0xf0, 0xd9, 0xaf, 0x90, 0xe2, 0xda, 0x7a, 0xde, 0xe2, 0xf2, 0xcd, 0x4c, 0x9b, 0xed, 0xa7, 0xaf, 0xf2, - 0xf8, 0x72, 0xbb, 0x0d, 0xbb, 0x5f, 0x80, 0xf9, 0x65, 0xa9, 0x34, 0x6a, 0xe0, 0xf4, 0x10, 0xa2, 0x9f, 0xf3, 0x3d, - 0x39, 0x27, 0x8e, 0x93, 0x6b, 0x37, 0x6f, 0xb6, 0x93, 0x62, 0x04, 0x16, 0x70, 0xe2, 0x22, 0x1d, 0x68, 0xa9, 0xe0, - 0xb4, 0x65, 0xbc, 0xb7, 0xe9, 0x1d, 0xa5, 0xc2, 0xab, 0x85, 0x26, 0x21, 0x95, 0xbb, 0x97, 0xd8, 0x51, 0x03, 0xce, - 0x49, 0xdd, 0x41, 0xc0, 0x49, 0x4d, 0x37, 0xd6, 0x2a, 0x4e, 0x4d, 0x82, 0xf7, 0x4a, 0x0f, 0x5d, 0xa2, 0x9d, 0xb8, - 0xdd, 0xb6, 0x2a, 0x5b, 0xa8, 0x8f, 0x7b, 0x39, 0x4b, 0xd4, 0xf1, 0x80, 0x42, 0x17, 0x75, 0x34, 0xe4, 0x0b, 0x52, - 0xe8, 0x95, 0xa3, 0x55, 0xab, 0xbb, 0x92, 0x81, 0x52, 0xad, 0x82, 0xbc, 0x26, 0xd6, 0x5d, 0x2b, 0x6b, 0x2c, 0xae, - 0x9c, 0x90, 0xc2, 0x26, 0x7c, 0x69, 0x29, 0x16, 0x56, 0xb0, 0x37, 0xa6, 0xbe, 0x70, 0x89, 0xd0, 0x76, 0xb7, 0x21, - 0x26, 0x19, 0xac, 0x9b, 0xed, 0xf6, 0x75, 0x11, 0xce, 0xb3, 0x05, 0x95, 0xa3, 0x2c, 0x45, 0x08, 0x31, 0xe3, 0xa1, - 0x6b, 0xbb, 0x60, 0x26, 0x86, 0xba, 0xf6, 0x78, 0x49, 0xa6, 0x58, 0x9b, 0x24, 0x47, 0xf1, 0xb9, 0x2c, 0xd4, 0x5a, - 0x23, 0x04, 0x0f, 0xf7, 0x3f, 0x53, 0x88, 0xe1, 0x66, 0xd6, 0xdd, 0x6f, 0x3b, 0x37, 0xc4, 0x3f, 0x21, 0x90, 0x40, - 0xc9, 0x5e, 0x17, 0xa3, 0xf3, 0x4c, 0xa4, 0xb8, 0x53, 0x55, 0x54, 0x5c, 0xb5, 0x0e, 0x9a, 0x2d, 0xb7, 0xf7, 0x62, - 0x4b, 0x14, 0x20, 0xae, 0xb1, 0xd0, 0x8c, 0x67, 0xe5, 0x2c, 0x45, 0x32, 0x8a, 0x0d, 0x89, 0x4a, 0x2f, 0x2a, 0xba, - 0xcf, 0xd3, 0x98, 0x1e, 0xba, 0x35, 0x08, 0xae, 0x9a, 0x3b, 0x1b, 0x69, 0xbe, 0x20, 0x44, 0x4d, 0x80, 0x84, 0x8d, - 0x6a, 0x4e, 0xad, 0x4b, 0xf1, 0x30, 0xab, 0x7c, 0xa6, 0x0f, 0xe2, 0x4b, 0x01, 0x3c, 0xac, 0xb7, 0xbd, 0xaf, 0x84, - 0xc7, 0xda, 0xe0, 0xdb, 0xed, 0xf6, 0x52, 0xcc, 0x83, 0xc0, 0x63, 0x34, 0xbf, 0x53, 0x12, 0xf3, 0xde, 0x98, 0xc2, - 0x8a, 0xf7, 0x5d, 0xda, 0xba, 0x49, 0xad, 0xb5, 0x40, 0xdd, 0xe1, 0xfa, 0x80, 0xe7, 0x29, 0x71, 0xb4, 0xa3, 0x72, - 0x2a, 0xad, 0xae, 0x1c, 0xbb, 0x22, 0x30, 0x30, 0xf4, 0x0f, 0x29, 0xdb, 0x80, 0x39, 0x1e, 0x58, 0xdb, 0xa0, 0x9f, - 0x92, 0xd2, 0xc2, 0x8c, 0xd1, 0x98, 0x45, 0xae, 0xab, 0xe8, 0x80, 0xeb, 0xe8, 0xed, 0x3c, 0xfa, 0xdb, 0xb3, 0x31, - 0x2d, 0x62, 0x91, 0xca, 0x2b, 0x50, 0x41, 0x80, 0x32, 0x04, 0x0d, 0xff, 0x35, 0x35, 0x00, 0x0d, 0x82, 0x1b, 0x80, - 0x7f, 0x74, 0x3a, 0x0d, 0xda, 0x9a, 0x7c, 0x4c, 0x52, 0x55, 0xe4, 0xac, 0x0d, 0x65, 0xa6, 0x92, 0x43, 0xf2, 0xb8, - 0x04, 0x3c, 0x47, 0x6c, 0x96, 0xb2, 0xb9, 0x50, 0x9b, 0x4d, 0xbd, 0x56, 0xec, 0xc8, 0x6d, 0xa3, 0x68, 0xb3, 0x16, - 0xb5, 0x9d, 0xc4, 0x7c, 0x31, 0xbd, 0xb5, 0xc2, 0xc0, 0xa9, 0x69, 0xcd, 0xcd, 0x0e, 0x74, 0x9a, 0xad, 0xcf, 0xe4, - 0x26, 0x40, 0x1c, 0x60, 0xb8, 0x6e, 0xe7, 0x37, 0x0b, 0x42, 0x6f, 0xd9, 0xad, 0x15, 0xab, 0xde, 0x58, 0xb9, 0x88, - 0x49, 0xbb, 0x19, 0x4c, 0xe0, 0x32, 0xce, 0x0a, 0xfb, 0x42, 0xab, 0x1b, 0x8a, 0x8e, 0xb6, 0x49, 0xfb, 0x79, 0x47, - 0xbb, 0xe1, 0x82, 0x6f, 0xc5, 0x3a, 0xce, 0x0d, 0x69, 0xaa, 0xd0, 0xa3, 0x03, 0xbd, 0x1d, 0x02, 0x9a, 0xb3, 0x31, - 0x5d, 0xd2, 0x14, 0x2f, 0xd0, 0x74, 0x0d, 0x66, 0x29, 0x17, 0xd0, 0xd7, 0x6e, 0x9f, 0xe4, 0x0b, 0xd5, 0x13, 0xe1, - 0x2d, 0x51, 0xf0, 0xe5, 0x48, 0xc1, 0x2b, 0x2b, 0xe7, 0xb1, 0x99, 0x43, 0xc0, 0x63, 0x51, 0x25, 0x7a, 0x27, 0xc5, - 0x25, 0x28, 0x53, 0xe1, 0x08, 0x34, 0x55, 0x23, 0x96, 0x70, 0x80, 0xdb, 0x8b, 0xa7, 0x01, 0xa1, 0x20, 0xd5, 0x5d, - 0xdb, 0x15, 0x79, 0xcb, 0x8e, 0x36, 0xb7, 0x60, 0x16, 0x5b, 0xad, 0xcb, 0xd6, 0x57, 0x36, 0xd9, 0x7d, 0x5c, 0x13, - 0x6c, 0xbb, 0xb7, 0x41, 0xc2, 0x5b, 0x7a, 0x43, 0x36, 0x37, 0xfd, 0x7e, 0x08, 0xfd, 0x21, 0x54, 0x77, 0xe8, 0xb6, - 0xb3, 0x43, 0xb7, 0x3e, 0xf3, 0x6b, 0xf5, 0x7c, 0xca, 0x1b, 0xe2, 0x03, 0x9a, 0x68, 0xd1, 0x55, 0x7c, 0x07, 0x9b, - 0x3a, 0xaa, 0xa8, 0xaa, 0x3c, 0x4a, 0x28, 0xa8, 0x80, 0x33, 0x5e, 0x9e, 0x72, 0x8c, 0x6d, 0xaa, 0x9f, 0xde, 0x69, - 0x5e, 0x6d, 0x6d, 0xd6, 0x66, 0xb9, 0x3e, 0x07, 0x8b, 0x80, 0x73, 0x1e, 0x5d, 0x69, 0x5a, 0x72, 0xe9, 0x31, 0xf5, - 0x67, 0x38, 0x2a, 0xc1, 0x45, 0x9c, 0xe5, 0x3c, 0x0d, 0xe8, 0x45, 0xb3, 0xff, 0xa1, 0xb6, 0x95, 0x5a, 0x36, 0xce, - 0xdc, 0xeb, 0x90, 0x6c, 0xfe, 0xc7, 0x06, 0xea, 0x4d, 0x88, 0x11, 0x51, 0xcd, 0x82, 0x3e, 0x61, 0x10, 0x1b, 0x33, - 0x28, 0xd7, 0x49, 0xc2, 0xcb, 0x32, 0x30, 0x4a, 0xad, 0x35, 0x5b, 0x9b, 0xf3, 0xec, 0x11, 0x3b, 0x7a, 0xd4, 0x63, - 0xec, 0x96, 0xd0, 0x44, 0xeb, 0x84, 0x4c, 0x8d, 0x91, 0xa7, 0x05, 0xd2, 0x1d, 0x8a, 0xb2, 0x8b, 0xf0, 0x04, 0x85, - 0x2c, 0xed, 0x7d, 0x6e, 0x4e, 0x64, 0xf5, 0x8d, 0x36, 0xba, 0x88, 0x54, 0x22, 0xc8, 0xc6, 0x6f, 0x10, 0xb0, 0x17, - 0x9a, 0x1d, 0x90, 0xcd, 0x92, 0x9d, 0xd2, 0x33, 0x6b, 0x02, 0x03, 0xaf, 0x4f, 0x54, 0xa2, 0x19, 0x65, 0x45, 0x74, - 0x95, 0x91, 0xcb, 0x5d, 0x48, 0xa2, 0xb3, 0x90, 0xf8, 0xb9, 0x61, 0x69, 0x5d, 0x87, 0x28, 0x66, 0x36, 0x1b, 0x5e, - 0x75, 0xf7, 0x51, 0x63, 0x5b, 0x19, 0x9f, 0xea, 0x5b, 0x9b, 0x46, 0xa6, 0xd0, 0xd7, 0xe1, 0xa4, 0xdf, 0x87, 0xbf, - 0x9a, 0x7e, 0xe0, 0x2d, 0x05, 0x7f, 0xb1, 0x47, 0xa4, 0x4e, 0x58, 0x00, 0x70, 0x84, 0x39, 0xaf, 0x9a, 0x13, 0xf8, - 0x88, 0x1d, 0x6d, 0x1e, 0x85, 0xa7, 0x8d, 0x99, 0xbb, 0x0b, 0xf1, 0x52, 0x95, 0xf4, 0xbc, 0x79, 0x32, 0x03, 0xb1, - 0x0a, 0xcd, 0x7e, 0xbd, 0x65, 0x56, 0x9f, 0x00, 0x44, 0xea, 0xd6, 0x3a, 0x94, 0xe2, 0xc7, 0xa6, 0xcb, 0x64, 0x93, - 0xb2, 0x36, 0x13, 0xa5, 0x54, 0x24, 0xcd, 0x45, 0x00, 0xfd, 0x86, 0xe1, 0xa8, 0x01, 0xde, 0x5b, 0x8f, 0xbd, 0x19, - 0x1a, 0x6f, 0x4c, 0x0d, 0x3d, 0xdb, 0xe8, 0xe5, 0xed, 0x28, 0x84, 0x19, 0x8b, 0xe8, 0xd6, 0x1d, 0x8b, 0xe1, 0x29, - 0x3d, 0x81, 0x0a, 0xdf, 0x84, 0x18, 0x4d, 0x97, 0xd4, 0xf5, 0x74, 0xad, 0xb6, 0xd2, 0x0d, 0xa1, 0x39, 0x46, 0xf1, - 0xf1, 0xda, 0x76, 0x47, 0x8d, 0xd0, 0x9e, 0x50, 0x1e, 0xde, 0xd2, 0x8a, 0xde, 0x58, 0x16, 0xc1, 0xc9, 0x8f, 0xbd, - 0xfc, 0x84, 0x9e, 0x7b, 0x02, 0x93, 0xa2, 0xad, 0x01, 0xfc, 0x01, 0xf5, 0xc3, 0x59, 0x3d, 0xb5, 0x52, 0x0e, 0x4f, - 0xe1, 0x4b, 0x36, 0x20, 0x57, 0xd0, 0x8b, 0x35, 0x66, 0x47, 0x31, 0xe8, 0xa0, 0x76, 0x76, 0x87, 0x37, 0x29, 0x65, - 0x88, 0xd6, 0x77, 0x0e, 0xe2, 0xe9, 0x1f, 0xa0, 0xe9, 0x83, 0xb4, 0x30, 0xa5, 0x6b, 0x14, 0xf0, 0x80, 0xbe, 0xa9, - 0xdf, 0xcf, 0xf1, 0xb9, 0xf6, 0x2c, 0xb1, 0xb0, 0xc7, 0x4b, 0x42, 0x97, 0x5e, 0xdc, 0x28, 0x90, 0x36, 0x3b, 0x56, - 0x01, 0x58, 0x91, 0x04, 0x1a, 0x91, 0x80, 0xa5, 0x8e, 0x27, 0x2e, 0xdb, 0xa0, 0x01, 0x49, 0x54, 0x52, 0xc8, 0x12, - 0x49, 0xe0, 0x87, 0x11, 0x84, 0x28, 0x8a, 0x41, 0xdc, 0xab, 0x97, 0x57, 0x5c, 0x53, 0x03, 0x4e, 0x14, 0xc1, 0x04, - 0xeb, 0x74, 0x0a, 0xc4, 0x56, 0xac, 0x57, 0xe0, 0x79, 0xe9, 0x38, 0x71, 0x64, 0x09, 0xd0, 0x20, 0xcd, 0x97, 0x4e, - 0xbb, 0xe5, 0xed, 0x89, 0x96, 0x2a, 0x36, 0xf7, 0x5e, 0x2c, 0x2c, 0xf7, 0x58, 0xf9, 0xdb, 0x81, 0xf6, 0xc2, 0x6a, - 0x47, 0x44, 0x0d, 0x56, 0x76, 0x6d, 0xbb, 0x36, 0x94, 0x86, 0xea, 0x5e, 0x39, 0x26, 0xa0, 0xa2, 0xab, 0xb8, 0x5a, - 0x46, 0xd9, 0x08, 0xfe, 0x6c, 0xb7, 0xc1, 0x7e, 0x00, 0x16, 0x90, 0xbf, 0xbe, 0xff, 0x39, 0xc2, 0xf0, 0x4c, 0xbf, - 0xbe, 0xff, 0x79, 0xbb, 0x7d, 0x36, 0x1e, 0x1b, 0xae, 0xc0, 0xa9, 0x75, 0x80, 0x3f, 0x30, 0x6c, 0x83, 0x5d, 0xb2, - 0xdb, 0xed, 0x33, 0xe0, 0x20, 0x14, 0xdb, 0x60, 0x76, 0xb1, 0x72, 0xe4, 0x52, 0xac, 0x86, 0xde, 0x91, 0x80, 0x55, - 0xb7, 0xc3, 0x52, 0xec, 0x52, 0x1f, 0x15, 0x82, 0x51, 0x2f, 0xfa, 0x97, 0x9d, 0x02, 0x4b, 0x0a, 0xa6, 0xab, 0xc1, - 0xb2, 0xaa, 0x56, 0x65, 0xb4, 0xbf, 0x1f, 0xaf, 0xb2, 0x51, 0x99, 0xc1, 0x36, 0x2f, 0xaf, 0x2f, 0x01, 0x50, 0x21, - 0xa0, 0x8d, 0x77, 0x6b, 0x91, 0x99, 0x17, 0x0b, 0xba, 0xcc, 0x70, 0x4d, 0x82, 0xd9, 0x41, 0xce, 0xad, 0x6e, 0x72, - 0x4a, 0xec, 0x03, 0xd8, 0x1c, 0x6e, 0xb7, 0x0d, 0x7e, 0xe1, 0x68, 0xf4, 0x6c, 0xb6, 0xcc, 0xb4, 0x41, 0x27, 0x37, - 0xfb, 0x9f, 0x44, 0x5e, 0x1a, 0x2a, 0x3e, 0xc9, 0xf4, 0x65, 0x06, 0x7c, 0x1e, 0x7b, 0x2b, 0x42, 0x9f, 0xe5, 0x6a, - 0xb4, 0x06, 0xd8, 0xd8, 0xec, 0xe2, 0x6e, 0x94, 0x72, 0x88, 0x48, 0x11, 0x58, 0x75, 0xcd, 0x32, 0x23, 0xbe, 0x4d, - 0xc5, 0x5d, 0x4b, 0x15, 0xf6, 0x56, 0x78, 0xce, 0x2a, 0xdc, 0x38, 0xca, 0xf4, 0x26, 0x51, 0xf8, 0x12, 0x85, 0xa8, - 0x1c, 0x8d, 0xe9, 0x9c, 0x40, 0x2a, 0xf3, 0x98, 0x50, 0xcc, 0xe1, 0xde, 0xfd, 0x9a, 0x3a, 0x73, 0x19, 0x5f, 0xb8, - 0xf7, 0xd2, 0x97, 0x99, 0xdc, 0x4a, 0x00, 0x45, 0x52, 0xb5, 0xff, 0xf2, 0x19, 0xa9, 0xf1, 0x3f, 0x53, 0xad, 0x01, - 0xe8, 0xfd, 0x02, 0x35, 0x39, 0x82, 0x80, 0xad, 0x98, 0xfa, 0x68, 0xfa, 0x56, 0x32, 0xff, 0x01, 0x75, 0x3b, 0x82, - 0x6d, 0x54, 0xfc, 0x9c, 0xa8, 0xa2, 0x05, 0x4f, 0xd7, 0x22, 0x8d, 0x45, 0x72, 0x17, 0xf1, 0x7a, 0x8a, 0x25, 0x31, - 0x1b, 0x21, 0xeb, 0x97, 0x66, 0x17, 0x7e, 0x2e, 0x1a, 0x26, 0xe0, 0xb4, 0xf4, 0xb7, 0x95, 0xb7, 0x99, 0x2c, 0xe3, - 0x8c, 0x4c, 0xb9, 0x42, 0xec, 0xb6, 0xfa, 0x1e, 0x73, 0x82, 0x3f, 0x3d, 0x78, 0x4a, 0xe8, 0xad, 0x9c, 0x96, 0x08, - 0x4a, 0x27, 0x52, 0xeb, 0xaa, 0x89, 0xfd, 0x9a, 0x42, 0x14, 0x07, 0xc1, 0x20, 0x74, 0xa7, 0x69, 0x9f, 0xe2, 0xfb, - 0x6c, 0xd9, 0x6f, 0x4d, 0xd9, 0x92, 0x6c, 0x04, 0x74, 0x4c, 0x3a, 0x6f, 0x4f, 0x6f, 0xcf, 0xce, 0xbc, 0xdf, 0xa0, - 0x09, 0x07, 0xd5, 0x0d, 0xb4, 0xab, 0x20, 0xd3, 0x18, 0xc5, 0x66, 0x31, 0xd6, 0x6e, 0x4d, 0x44, 0x10, 0x74, 0xba, - 0x9c, 0x85, 0xed, 0x76, 0x42, 0x7c, 0x09, 0x24, 0x50, 0xe0, 0xca, 0x45, 0x39, 0x09, 0x89, 0xba, 0x90, 0xe9, 0xc9, - 0xba, 0x96, 0x2c, 0xd0, 0x6b, 0xec, 0x20, 0xa0, 0xc7, 0xdc, 0x3e, 0x05, 0xf4, 0x7d, 0xc1, 0x8e, 0xf9, 0x20, 0x18, - 0x62, 0x7c, 0xd5, 0x80, 0xde, 0x48, 0xf5, 0x08, 0x1e, 0xc2, 0xc0, 0x72, 0xd1, 0x57, 0x05, 0x43, 0x58, 0xa1, 0xbf, - 0x52, 0x36, 0xf9, 0xe6, 0xef, 0x6e, 0x7e, 0xcf, 0xb5, 0x98, 0x1d, 0x84, 0xe2, 0xf6, 0x7a, 0x02, 0xc4, 0xaf, 0xe2, - 0x57, 0x60, 0x5d, 0xad, 0x25, 0xde, 0x6e, 0x7a, 0xfe, 0x14, 0xbe, 0x1c, 0xdd, 0x7e, 0x52, 0x9a, 0x4f, 0x20, 0x48, - 0x8d, 0x93, 0x94, 0xbb, 0xef, 0x3e, 0x4a, 0x57, 0x11, 0x8c, 0x16, 0x20, 0xd6, 0xdd, 0x5b, 0xc9, 0x59, 0x53, 0xf8, - 0x8f, 0x75, 0xbe, 0xc7, 0xd8, 0x21, 0xf2, 0x14, 0xa7, 0xbf, 0x01, 0x86, 0x7d, 0xe7, 0xdf, 0xca, 0xac, 0x21, 0xd1, - 0xb9, 0xfa, 0x08, 0xe8, 0xff, 0x58, 0x8f, 0xdf, 0x31, 0x4a, 0xfa, 0x92, 0x38, 0x47, 0xb8, 0x22, 0x5e, 0xa2, 0xa9, - 0x5e, 0x6f, 0x5c, 0xd3, 0x4f, 0x85, 0x79, 0xa1, 0x15, 0x1c, 0xf6, 0xad, 0x51, 0x78, 0xe0, 0x99, 0xf7, 0x9b, 0x68, - 0x08, 0xba, 0x7f, 0xc7, 0xbd, 0xf1, 0x9b, 0x60, 0x19, 0xde, 0x94, 0xb3, 0xcc, 0xdc, 0xe1, 0x6e, 0x32, 0x91, 0xca, - 0x1b, 0xc6, 0x82, 0xb5, 0x50, 0xe6, 0xab, 0x69, 0x30, 0xdb, 0xd4, 0x91, 0x4a, 0x76, 0xdf, 0xbf, 0x6d, 0x9c, 0xb0, - 0xd9, 0x20, 0x38, 0xad, 0x64, 0x11, 0x5f, 0xf2, 0x60, 0xaa, 0x55, 0x14, 0x59, 0xd6, 0xef, 0x67, 0x80, 0x0c, 0xe3, - 0xb4, 0x77, 0xf0, 0x64, 0xa9, 0x99, 0x09, 0x71, 0x6d, 0x75, 0x16, 0xf0, 0xd6, 0x8c, 0xe6, 0x71, 0x05, 0xbb, 0xcc, - 0x57, 0x52, 0xfc, 0xd9, 0x92, 0x64, 0x63, 0xfd, 0x0d, 0x19, 0xb6, 0x95, 0xcf, 0x9c, 0x03, 0xc6, 0xcc, 0x8d, 0x54, - 0x41, 0xee, 0x7a, 0xc0, 0x08, 0x21, 0x11, 0x10, 0xce, 0x62, 0xe2, 0x4e, 0x98, 0xf0, 0x8f, 0x2e, 0x30, 0x4e, 0x8c, - 0x81, 0x71, 0x3e, 0xca, 0x90, 0xd3, 0x63, 0x3e, 0x48, 0x1a, 0xb3, 0xf5, 0xa7, 0x2a, 0x91, 0x5e, 0x4b, 0x42, 0xcf, - 0xe0, 0xf7, 0xb8, 0xc5, 0x03, 0x35, 0x82, 0x53, 0xba, 0x9b, 0xd3, 0xfe, 0xab, 0x82, 0x0c, 0xff, 0x02, 0xef, 0xae, - 0xd8, 0x5e, 0x96, 0x13, 0x58, 0xdc, 0xb1, 0x57, 0x3c, 0xcd, 0x55, 0x8b, 0x13, 0xe2, 0x11, 0x8b, 0xdc, 0x27, 0x16, - 0x30, 0xa2, 0x86, 0xd1, 0xf8, 0xf1, 0xf4, 0xe4, 0xad, 0xc6, 0x6c, 0xca, 0xfd, 0x0f, 0x60, 0x44, 0xb5, 0xb4, 0xdd, - 0x0e, 0xf8, 0x72, 0x84, 0x06, 0xdb, 0xa9, 0x1b, 0xec, 0x7e, 0xdf, 0xa4, 0x1d, 0x95, 0x5e, 0x36, 0x27, 0x06, 0xdd, - 0x51, 0xda, 0x2c, 0x95, 0x41, 0x6d, 0x57, 0xe1, 0x68, 0x3e, 0x6b, 0xc4, 0xaa, 0xde, 0x87, 0xe1, 0x92, 0xc6, 0x56, - 0x56, 0x6e, 0x77, 0x13, 0x8e, 0x6c, 0x02, 0x5c, 0x9f, 0x82, 0xb2, 0x6a, 0xce, 0x41, 0x0b, 0x3a, 0x13, 0x38, 0xa2, - 0xed, 0x36, 0x84, 0x08, 0x1c, 0xc5, 0x70, 0x32, 0x0b, 0x8b, 0xe1, 0x50, 0x0d, 0x7c, 0x41, 0x48, 0xf4, 0xa9, 0x98, - 0x67, 0x0b, 0x85, 0xd8, 0xe3, 0xef, 0xa4, 0xdf, 0x0a, 0xc5, 0x29, 0xf7, 0x7e, 0x13, 0x64, 0xf3, 0x7b, 0x8a, 0x31, - 0x07, 0x9d, 0x66, 0x33, 0x03, 0x09, 0xeb, 0x71, 0x45, 0xd4, 0x3a, 0xb2, 0xb3, 0x01, 0xaa, 0x58, 0x34, 0x85, 0x05, - 0x75, 0x8b, 0x27, 0xd6, 0x33, 0x7a, 0x0f, 0x2a, 0x41, 0x54, 0x0b, 0x76, 0x63, 0xb8, 0xd6, 0x3e, 0x89, 0x50, 0x52, - 0x4e, 0x9a, 0xcc, 0x8c, 0x15, 0x0d, 0x16, 0x20, 0x24, 0x8d, 0xcb, 0xea, 0x8d, 0x4c, 0xb3, 0x8b, 0x0c, 0x10, 0x13, - 0x9c, 0xff, 0x9c, 0x6c, 0xbc, 0x79, 0xae, 0xe6, 0xa5, 0x2b, 0x71, 0x66, 0x61, 0x3e, 0xba, 0xde, 0xd2, 0x82, 0x44, - 0x05, 0xd0, 0x28, 0x5f, 0xcb, 0xf3, 0xd3, 0x8e, 0x55, 0xc8, 0xee, 0x87, 0x53, 0x65, 0x3b, 0xc4, 0x8f, 0x58, 0x45, - 0xbc, 0xd3, 0xba, 0x52, 0x22, 0x8d, 0x8e, 0xb6, 0x01, 0x31, 0x6c, 0xd9, 0xb7, 0xa8, 0xe1, 0x83, 0x30, 0x83, 0x4e, - 0xf2, 0x83, 0x9e, 0xd1, 0xb1, 0x35, 0x90, 0xf4, 0xb5, 0x08, 0xbe, 0x46, 0x47, 0x3a, 0x51, 0xa6, 0x91, 0x98, 0x42, - 0xa2, 0x5f, 0x2f, 0xb4, 0xc6, 0x32, 0xca, 0xbe, 0x22, 0xff, 0x7b, 0xdd, 0xbd, 0xdf, 0xc4, 0x76, 0x0b, 0x93, 0xec, - 0x79, 0x5c, 0xc1, 0xa6, 0x46, 0xad, 0x10, 0xce, 0xce, 0x71, 0x85, 0xda, 0xb1, 0x5e, 0x58, 0x02, 0x79, 0x00, 0x5b, - 0x91, 0x06, 0x65, 0x90, 0xec, 0x53, 0x31, 0x17, 0x0b, 0x27, 0xca, 0x91, 0x0a, 0xef, 0x4b, 0x8e, 0x52, 0x0e, 0x57, - 0xb1, 0xb0, 0x60, 0xc8, 0xaf, 0x8e, 0x2e, 0x0a, 0x79, 0x05, 0x92, 0x12, 0xc3, 0x50, 0x59, 0x5e, 0x17, 0x57, 0x6d, - 0x49, 0x68, 0xef, 0x0c, 0x40, 0x58, 0x0a, 0x10, 0xbc, 0x34, 0x6a, 0x88, 0xd9, 0x46, 0xed, 0xae, 0xe8, 0x5e, 0x72, - 0x40, 0x9d, 0xee, 0xda, 0xad, 0x37, 0x65, 0x9b, 0x6d, 0xc5, 0x85, 0x7f, 0x42, 0xe9, 0xc7, 0x7c, 0x50, 0xf8, 0x54, - 0x02, 0x37, 0xbe, 0xda, 0x64, 0xd9, 0xc5, 0x1d, 0x2e, 0xfd, 0xaa, 0x31, 0x7e, 0xfd, 0x7e, 0x4f, 0x2d, 0x84, 0x46, - 0x2a, 0x30, 0xdf, 0x3e, 0x33, 0x55, 0x19, 0x4d, 0xa9, 0xbd, 0x04, 0x57, 0xce, 0x7e, 0x04, 0x15, 0x71, 0x5d, 0x91, - 0xc9, 0xd4, 0x00, 0xed, 0x79, 0x59, 0xe1, 0x56, 0x16, 0xe0, 0xb1, 0x13, 0x90, 0xed, 0x96, 0x87, 0x81, 0x3e, 0x74, - 0x02, 0x7f, 0x4b, 0x9e, 0x22, 0xb3, 0x66, 0x1f, 0x7f, 0xd1, 0x82, 0x7f, 0x6c, 0xc1, 0xcf, 0x28, 0xee, 0xb4, 0x32, - 0xff, 0x56, 0x5a, 0xb7, 0xb8, 0x7f, 0x27, 0xd3, 0x84, 0xa2, 0x32, 0xa1, 0xf6, 0x2b, 0xfd, 0x97, 0x09, 0x96, 0xa4, - 0xb2, 0x7f, 0x90, 0xf0, 0xc1, 0xac, 0xf1, 0xc4, 0x1a, 0x4f, 0x86, 0xd3, 0xad, 0x34, 0x0c, 0x01, 0x0a, 0xfd, 0xbc, - 0xcc, 0x15, 0xd5, 0xcf, 0xbf, 0xac, 0xf9, 0x9a, 0x37, 0x5b, 0x6c, 0x93, 0x1e, 0x68, 0xb0, 0x97, 0x47, 0x53, 0x0a, - 0x27, 0x51, 0xe7, 0x46, 0xa2, 0x2e, 0x6a, 0x96, 0xa1, 0x3a, 0xc1, 0xab, 0x79, 0xaa, 0x87, 0xbd, 0x99, 0x88, 0xd6, - 0x4a, 0xca, 0x12, 0x03, 0xd6, 0x3a, 0xf2, 0x90, 0xdc, 0xad, 0x75, 0xdc, 0x69, 0xa8, 0x4b, 0x53, 0x28, 0x01, 0x56, - 0xb8, 0x00, 0x47, 0xd0, 0xcf, 0x45, 0xc8, 0xe1, 0x9a, 0xaa, 0xf4, 0x0b, 0x9a, 0x92, 0x27, 0x9e, 0xa2, 0x56, 0x2b, - 0xd2, 0xed, 0x47, 0x39, 0x76, 0xc3, 0x37, 0x4e, 0xc8, 0x89, 0x11, 0xfa, 0xbb, 0x63, 0x29, 0x67, 0x68, 0xf1, 0xa0, - 0x4e, 0xb0, 0x5e, 0xde, 0x52, 0xa0, 0x98, 0xa3, 0xcb, 0xaa, 0x6b, 0x5e, 0xa3, 0xed, 0xcb, 0xb2, 0xdf, 0xcf, 0x6d, - 0x3d, 0x29, 0x3b, 0xda, 0x2c, 0xcd, 0x3e, 0x44, 0xc5, 0x14, 0xee, 0xfa, 0x44, 0xf3, 0x57, 0xa1, 0xbe, 0x6a, 0xcb, - 0x9c, 0x8f, 0x38, 0xe2, 0x62, 0xe4, 0xa4, 0xfe, 0x45, 0x4d, 0xbd, 0x12, 0xf7, 0xab, 0x4a, 0xbe, 0x13, 0xc6, 0x8a, - 0xd1, 0x01, 0xf5, 0xa7, 0x4a, 0xe5, 0xfd, 0xb2, 0x00, 0xb8, 0x27, 0xc1, 0x3e, 0x81, 0x7d, 0x85, 0x46, 0xf8, 0x6d, - 0x09, 0xf8, 0x37, 0x8a, 0x1b, 0xb0, 0x0a, 0x0c, 0x30, 0x9a, 0x6c, 0xcf, 0x69, 0x02, 0x07, 0x9c, 0xd0, 0x2a, 0x0a, - 0x2a, 0xcc, 0xd0, 0x50, 0x5b, 0x18, 0x3d, 0x45, 0x19, 0xb7, 0xca, 0xec, 0xdd, 0x18, 0x3b, 0x2d, 0xf0, 0x1a, 0xfe, - 0x7c, 0x5e, 0xe8, 0x61, 0xa3, 0x0e, 0xd2, 0xa3, 0x93, 0x98, 0xfe, 0xb8, 0x85, 0x93, 0x9b, 0x85, 0xb3, 0xac, 0x59, - 0x02, 0xdd, 0x81, 0x0b, 0x62, 0xdc, 0xef, 0xe7, 0x70, 0x64, 0x9a, 0x91, 0x2f, 0x58, 0x4e, 0x63, 0xb6, 0xa4, 0xda, - 0xd3, 0xee, 0xb2, 0x0a, 0x73, 0xba, 0xb4, 0x32, 0xde, 0x94, 0x81, 0xca, 0x68, 0xbb, 0x0d, 0xe1, 0x4f, 0xb7, 0xb5, - 0x4b, 0x3a, 0x5f, 0x42, 0x06, 0xf8, 0x03, 0x12, 0x51, 0xc4, 0xbe, 0xfe, 0xb7, 0x1a, 0xa7, 0xf4, 0x44, 0x69, 0xcd, - 0x12, 0xba, 0x66, 0xba, 0x7e, 0x7a, 0xc1, 0xd6, 0x8d, 0xa5, 0xb0, 0xdd, 0x86, 0xcd, 0x04, 0xa6, 0x39, 0x57, 0x32, - 0xbd, 0x40, 0x9d, 0x14, 0x50, 0xb1, 0xf0, 0x02, 0x97, 0x5f, 0x4a, 0x28, 0x34, 0x77, 0xbe, 0x5c, 0x18, 0x25, 0x26, - 0xb4, 0x4a, 0x7e, 0xf9, 0x50, 0x99, 0xaf, 0x8d, 0x47, 0xdc, 0xbf, 0xd2, 0x30, 0x31, 0x45, 0xa2, 0x42, 0x74, 0xf6, - 0x1b, 0xc8, 0x72, 0x04, 0xe0, 0x58, 0x9e, 0xca, 0x9a, 0xfe, 0x98, 0x42, 0x1c, 0x74, 0x68, 0xd0, 0xbb, 0x42, 0x5e, - 0x65, 0x25, 0x0f, 0xf1, 0x9e, 0xe0, 0x69, 0x46, 0xef, 0x37, 0xf8, 0xd0, 0xd6, 0x1e, 0x3d, 0x41, 0x36, 0x9e, 0x72, - 0xbf, 0xfe, 0x4e, 0x84, 0x73, 0x88, 0x56, 0xb9, 0xa0, 0x5a, 0x5d, 0xed, 0x00, 0xa8, 0x3c, 0xdb, 0xab, 0x47, 0x70, - 0xba, 0xe9, 0xeb, 0x5b, 0x15, 0x3a, 0x73, 0x00, 0x69, 0x0f, 0xc9, 0xba, 0xe6, 0x7a, 0x07, 0xb8, 0x23, 0xb1, 0x5a, - 0x03, 0x8d, 0x75, 0x5b, 0xb3, 0xd3, 0x1e, 0xc5, 0x63, 0x22, 0x33, 0x63, 0x91, 0x62, 0xcc, 0xdd, 0x3a, 0x2d, 0x8a, - 0x36, 0x68, 0x86, 0xb0, 0x7b, 0xd7, 0xe1, 0xeb, 0x56, 0x84, 0xf5, 0xfb, 0x6d, 0x5f, 0x60, 0x34, 0x8c, 0xb9, 0x76, - 0xcf, 0x33, 0x74, 0xc3, 0x06, 0xdb, 0xc8, 0x79, 0x88, 0x7c, 0x98, 0xa9, 0x03, 0x51, 0xd6, 0xd6, 0x80, 0xed, 0x11, - 0xd7, 0x9b, 0x56, 0xf1, 0xf3, 0x2a, 0xe6, 0x6c, 0xcf, 0x1a, 0xa7, 0xb4, 0xbe, 0xc6, 0x35, 0xc7, 0x55, 0x21, 0xa2, - 0xb6, 0x9e, 0xf1, 0x30, 0xec, 0x7c, 0x81, 0x3b, 0xb3, 0xc2, 0xe0, 0x45, 0x58, 0x2a, 0xd9, 0xa9, 0x5c, 0x7f, 0x0e, - 0x5b, 0x1c, 0xa4, 0xf2, 0xa5, 0xd7, 0xdf, 0x7f, 0xf9, 0xe2, 0x0b, 0x74, 0x53, 0x73, 0x7e, 0x04, 0x41, 0x26, 0xd0, - 0x21, 0x4b, 0xa9, 0x1e, 0xbf, 0x2b, 0x80, 0xda, 0xc3, 0x3c, 0x7c, 0x57, 0x30, 0x11, 0x5f, 0x67, 0x97, 0x71, 0x25, - 0x8b, 0xd1, 0x35, 0x17, 0xa9, 0x2c, 0xac, 0xd4, 0x38, 0x38, 0x5e, 0xad, 0x72, 0x1e, 0x80, 0xa9, 0xbc, 0x65, 0x94, - 0x6d, 0x65, 0x99, 0x1e, 0x5c, 0x2d, 0x4f, 0xaf, 0xb4, 0xe8, 0xbc, 0xbc, 0xbe, 0x0c, 0x22, 0xfc, 0x75, 0x6e, 0x7e, - 0x5c, 0xc5, 0xe5, 0xc7, 0x20, 0xb2, 0x36, 0x75, 0xe6, 0x07, 0x4a, 0xe5, 0xc1, 0x7f, 0x0a, 0x64, 0xba, 0xdf, 0x15, - 0x60, 0x99, 0x6d, 0x2b, 0x3e, 0x8c, 0xb1, 0xd6, 0xe1, 0x84, 0xcc, 0x54, 0x89, 0xde, 0xbb, 0x64, 0x5d, 0x80, 0xb5, - 0x9f, 0xc2, 0x32, 0x56, 0xb9, 0x66, 0x58, 0x99, 0xaa, 0xc8, 0xcc, 0xca, 0x9a, 0xed, 0x87, 0xd6, 0x89, 0x66, 0x8e, - 0xde, 0x02, 0xfa, 0x81, 0xec, 0x5f, 0xd2, 0x72, 0xcd, 0x3c, 0x1f, 0x9b, 0xc6, 0xeb, 0x47, 0xfb, 0x97, 0x6e, 0xc1, - 0xde, 0xda, 0x3b, 0x39, 0x0a, 0x13, 0xc1, 0xb3, 0xd6, 0x8c, 0x2f, 0xf2, 0xac, 0x80, 0x95, 0x33, 0x19, 0x8f, 0xa9, - 0xb7, 0xb4, 0x5a, 0x37, 0x47, 0x87, 0xe4, 0x9a, 0x3d, 0xae, 0x1e, 0x73, 0xb2, 0xcf, 0x5b, 0xa6, 0xb6, 0x6d, 0xeb, - 0x38, 0x4f, 0x93, 0xaf, 0x4c, 0xf7, 0xc5, 0xda, 0x46, 0x44, 0x57, 0xce, 0x7d, 0xce, 0x2b, 0xb8, 0xf5, 0x4d, 0x69, - 0xe8, 0xb5, 0x04, 0x20, 0x3a, 0x6d, 0xc0, 0x5f, 0xb0, 0x72, 0x3d, 0xaa, 0x78, 0x59, 0x81, 0x84, 0x05, 0x45, 0x78, - 0x53, 0xec, 0x4d, 0xe1, 0x6e, 0x9c, 0x9e, 0xc3, 0x0e, 0x5c, 0x4c, 0xd1, 0x1d, 0x27, 0x26, 0xb3, 0xd2, 0x68, 0x45, - 0x23, 0xfd, 0xcb, 0xf5, 0x25, 0xd6, 0x7d, 0xd1, 0xca, 0x3c, 0x9b, 0x53, 0x61, 0xb1, 0xbb, 0xca, 0xa5, 0x13, 0xf5, - 0x5b, 0x26, 0x5c, 0xb9, 0x12, 0x04, 0x64, 0x5a, 0xb0, 0x5e, 0x61, 0x76, 0x91, 0x5c, 0x03, 0x21, 0x03, 0xc3, 0xd7, - 0x60, 0x2d, 0x4a, 0x6e, 0xac, 0x60, 0xbd, 0x7b, 0xbe, 0x4e, 0x10, 0x52, 0xf0, 0xc0, 0x4d, 0xd0, 0x0f, 0xad, 0x9b, - 0xb7, 0xa3, 0x44, 0x19, 0xc4, 0xe3, 0xd6, 0x4e, 0x39, 0x48, 0x20, 0x00, 0xf7, 0x54, 0x85, 0xe0, 0x50, 0x20, 0xeb, - 0xe0, 0x6a, 0xc6, 0x11, 0x5c, 0x5d, 0x39, 0x73, 0x71, 0x03, 0xb0, 0xae, 0xfc, 0xb9, 0x6c, 0x70, 0x61, 0x3d, 0xa2, - 0xca, 0x9c, 0x71, 0x8a, 0x41, 0x8c, 0x2c, 0x41, 0x5f, 0x59, 0x4a, 0x7b, 0x09, 0x9a, 0xc6, 0x2b, 0xb6, 0x52, 0x3e, - 0x00, 0xf4, 0x9c, 0xad, 0x94, 0xb1, 0x3f, 0x7e, 0x7d, 0xc6, 0x56, 0x5a, 0x1a, 0x3c, 0xbd, 0x9a, 0x9d, 0xcf, 0xce, - 0x06, 0xec, 0x20, 0x0a, 0xb5, 0x01, 0x43, 0xe0, 0x90, 0xf8, 0x83, 0x41, 0xa8, 0xf1, 0x4e, 0x06, 0x2a, 0x20, 0x16, - 0xf1, 0x78, 0x6c, 0xc4, 0xcd, 0x0a, 0xc7, 0x43, 0x0c, 0x7e, 0xd5, 0x7c, 0x41, 0x02, 0x42, 0x4d, 0x69, 0xe8, 0xf2, - 0x18, 0x0e, 0x27, 0x7b, 0x13, 0x48, 0xc5, 0xcc, 0x4c, 0x15, 0xc6, 0xc6, 0x24, 0x82, 0x78, 0xa7, 0x9d, 0xf5, 0x42, - 0xb9, 0xdd, 0x35, 0x1a, 0xc8, 0x95, 0xc1, 0x17, 0x55, 0x3c, 0xd9, 0x1b, 0x76, 0x55, 0x8c, 0xa3, 0x70, 0x6d, 0x94, - 0x6f, 0x67, 0x87, 0x00, 0x5e, 0x7b, 0x36, 0xf4, 0xe5, 0x12, 0x67, 0xfb, 0x4f, 0xc9, 0xe3, 0xa7, 0x84, 0x9e, 0xb1, - 0xb3, 0xaf, 0x9e, 0xd2, 0x33, 0x45, 0x4e, 0xf6, 0x26, 0xd1, 0x35, 0xb3, 0x98, 0x2f, 0x07, 0xaa, 0x09, 0xf4, 0x72, - 0xb4, 0x16, 0x6a, 0x81, 0x69, 0x87, 0xa6, 0xf0, 0xdb, 0xf1, 0x5e, 0x30, 0xb8, 0x6e, 0x37, 0xfd, 0xba, 0xdd, 0x56, - 0xcf, 0xab, 0x6b, 0xef, 0x20, 0xda, 0x2d, 0x66, 0xf2, 0xf7, 0xf1, 0x9e, 0x9b, 0x03, 0xac, 0xef, 0xe1, 0x31, 0x31, - 0x4d, 0xda, 0x19, 0x15, 0xbf, 0xa6, 0x27, 0xd8, 0x87, 0x66, 0x91, 0x1d, 0x7d, 0x18, 0xfe, 0x5b, 0x9d, 0xa8, 0xcf, - 0xbe, 0x3a, 0x00, 0x72, 0x04, 0x32, 0x50, 0x2c, 0x11, 0xcc, 0x70, 0xa0, 0x29, 0xa0, 0x20, 0xd3, 0xe3, 0x4e, 0xf5, - 0xf0, 0xab, 0x51, 0x53, 0x33, 0x72, 0x0d, 0x53, 0x83, 0x6d, 0xc1, 0x0f, 0x54, 0x37, 0xf4, 0x37, 0x1a, 0xdd, 0x48, - 0x3b, 0x99, 0x99, 0x97, 0xd4, 0xc6, 0x75, 0xbb, 0x86, 0x00, 0xc6, 0x0e, 0x5e, 0x50, 0xb2, 0xaf, 0x0f, 0x2f, 0xf7, - 0x70, 0x15, 0x01, 0x4a, 0x16, 0x0b, 0xbe, 0x1e, 0x5c, 0xea, 0xcd, 0xbd, 0x17, 0x90, 0xc1, 0xd7, 0xc1, 0xd1, 0xd7, - 0x03, 0x39, 0x08, 0x0e, 0xf7, 0x2f, 0x8f, 0x02, 0x67, 0xdc, 0x0f, 0x21, 0x1e, 0x55, 0x45, 0x31, 0x13, 0xa6, 0x8a, - 0xc4, 0xd6, 0x9e, 0xdb, 0x7a, 0x95, 0xf1, 0x19, 0x4d, 0xa7, 0x16, 0xf9, 0x3b, 0x4c, 0x59, 0x6c, 0x7e, 0x07, 0x13, - 0x7e, 0x15, 0x44, 0x2e, 0x08, 0xea, 0x2c, 0x8f, 0x62, 0xba, 0x64, 0xb7, 0x22, 0x4c, 0x69, 0xb2, 0x9f, 0x13, 0x12, - 0x85, 0x4b, 0x05, 0x9e, 0xa7, 0x5e, 0x27, 0x10, 0xc7, 0xd5, 0x7d, 0x7e, 0x2b, 0xc2, 0x25, 0xcd, 0xf7, 0x13, 0xd2, - 0x2a, 0xc2, 0x45, 0x64, 0xd9, 0xd4, 0xf4, 0x82, 0x85, 0x2b, 0x7a, 0x09, 0xcc, 0x94, 0x5c, 0x87, 0x97, 0xc0, 0xe5, - 0xad, 0xe7, 0xab, 0x05, 0xbb, 0x6c, 0x48, 0xdf, 0x0c, 0x5f, 0x7c, 0x61, 0x7d, 0xf2, 0x80, 0x87, 0x74, 0x7e, 0x78, - 0x29, 0xd8, 0x00, 0x5c, 0x67, 0xfc, 0xe6, 0x3b, 0x79, 0xab, 0xe7, 0xa5, 0x3d, 0xc5, 0x38, 0x33, 0xed, 0xc4, 0xa4, - 0x9d, 0x90, 0xfb, 0xf7, 0xed, 0x4d, 0x6c, 0x4e, 0xf6, 0x32, 0x5a, 0x2b, 0x97, 0x55, 0xcb, 0x90, 0x14, 0x6b, 0x86, - 0xfc, 0x3d, 0x4a, 0x4e, 0xad, 0xc0, 0x93, 0x5d, 0xf0, 0x2a, 0x59, 0xfa, 0x07, 0x95, 0xb5, 0x1a, 0xb0, 0xc7, 0x88, - 0x65, 0xa1, 0x70, 0xec, 0xdf, 0x64, 0xac, 0x58, 0xfb, 0x02, 0x8d, 0x18, 0xb9, 0xb7, 0x37, 0x19, 0xf3, 0x62, 0xae, - 0x26, 0x6b, 0x2f, 0x54, 0x9d, 0x97, 0x9e, 0xb7, 0x78, 0x2f, 0xa7, 0xd4, 0x30, 0x12, 0xd1, 0xbd, 0xb1, 0x32, 0xa3, - 0x54, 0x89, 0x5a, 0x83, 0x46, 0x04, 0x1b, 0xbb, 0xe0, 0x97, 0xe0, 0x84, 0xca, 0x3d, 0x75, 0xb6, 0x6f, 0xa7, 0x54, - 0x7a, 0xc0, 0xb2, 0xd4, 0xa8, 0xca, 0xdd, 0x32, 0x93, 0xac, 0x1a, 0x04, 0xa3, 0x3f, 0x4b, 0x29, 0x66, 0x78, 0x67, - 0x64, 0xc1, 0x14, 0xac, 0x04, 0x55, 0x2d, 0xc3, 0x72, 0xc8, 0x51, 0x8b, 0x67, 0x7c, 0x52, 0xa5, 0xfe, 0xd1, 0x11, - 0x24, 0x77, 0xb9, 0x6e, 0x05, 0xc9, 0x7d, 0x3a, 0x7e, 0xaa, 0x07, 0x3a, 0x5d, 0x6b, 0xc7, 0x43, 0x9f, 0xdf, 0x46, - 0x7c, 0x6d, 0xdd, 0x7b, 0xaa, 0xb5, 0x0a, 0x65, 0xa0, 0xc5, 0x8a, 0xca, 0x95, 0x5a, 0xd2, 0xfb, 0x5d, 0x04, 0xc0, - 0x22, 0x36, 0x66, 0xe3, 0x5d, 0xdb, 0xac, 0x10, 0x34, 0xba, 0xec, 0x68, 0x13, 0x0f, 0x58, 0xa2, 0x5b, 0x3b, 0x98, - 0xd0, 0xf8, 0x88, 0x95, 0xfd, 0x7e, 0x7e, 0x04, 0xf4, 0x54, 0x1b, 0x31, 0x15, 0x70, 0xe4, 0x7f, 0x69, 0x45, 0xa6, - 0x28, 0xb0, 0x59, 0x53, 0x77, 0x6b, 0x2c, 0x23, 0xd1, 0x97, 0x29, 0x5d, 0x9e, 0xf0, 0x0c, 0x98, 0xd6, 0xeb, 0x96, - 0xe3, 0xca, 0xae, 0xe2, 0xc8, 0x53, 0x61, 0x59, 0x71, 0x5e, 0x85, 0xe3, 0xad, 0xc7, 0x37, 0xd8, 0x37, 0x6c, 0xda, - 0x85, 0x3f, 0x84, 0xb0, 0x10, 0xde, 0x64, 0x70, 0x1b, 0xd1, 0x76, 0x12, 0xa8, 0xbc, 0x31, 0xd7, 0x09, 0x65, 0x73, - 0xbb, 0x5e, 0x7b, 0x06, 0xe9, 0xc4, 0x1c, 0x28, 0xd5, 0x08, 0x5a, 0xa3, 0x59, 0x50, 0x35, 0xe2, 0x91, 0xe3, 0xe1, - 0x9d, 0x41, 0xac, 0x96, 0x2f, 0x69, 0x2a, 0x45, 0x03, 0x30, 0x2e, 0x80, 0xcb, 0xd3, 0xaf, 0xef, 0x7f, 0x3e, 0xe5, - 0x71, 0x91, 0x2c, 0xdf, 0xc5, 0x45, 0x7c, 0x55, 0x86, 0x1b, 0x35, 0x46, 0x71, 0x4d, 0xa6, 0x62, 0xc0, 0xa4, 0x59, - 0x49, 0xcd, 0x5d, 0xa9, 0x09, 0x31, 0xd6, 0x99, 0xac, 0xcb, 0x4a, 0x5e, 0x35, 0x2a, 0x5d, 0x17, 0x19, 0x7e, 0xdc, - 0xf2, 0x39, 0xdd, 0x07, 0x20, 0x4f, 0xe3, 0x42, 0x1a, 0x49, 0x5d, 0x88, 0x31, 0x17, 0xf1, 0xba, 0x3e, 0x1e, 0x37, - 0xba, 0x5e, 0xb2, 0x67, 0xe3, 0x27, 0xd3, 0x37, 0x59, 0x98, 0x0d, 0x04, 0x19, 0x55, 0x4b, 0x2e, 0x5a, 0xa6, 0x9c, - 0xca, 0x24, 0x00, 0x7d, 0x3c, 0x7b, 0x8c, 0x1d, 0x8c, 0xc7, 0x64, 0xd3, 0x16, 0x0f, 0xf0, 0x70, 0xb9, 0x0e, 0x0b, - 0x32, 0xd3, 0x75, 0x44, 0x81, 0xe0, 0xb7, 0x55, 0x00, 0x48, 0x8e, 0xb6, 0x2a, 0xc3, 0xa5, 0xb1, 0x67, 0xe3, 0x09, - 0x95, 0xd8, 0xed, 0x90, 0xd4, 0x5e, 0x85, 0x6e, 0xe6, 0xa5, 0xef, 0x51, 0x24, 0x8d, 0xcb, 0xd2, 0x4e, 0xa5, 0x52, - 0xed, 0x99, 0x99, 0xeb, 0x1a, 0xc4, 0x60, 0x08, 0x75, 0xdd, 0xa5, 0x57, 0xf7, 0x6e, 0x73, 0xad, 0xd9, 0x0e, 0x78, - 0xaf, 0x41, 0x33, 0x94, 0xbc, 0xc5, 0xbc, 0x75, 0x45, 0xd4, 0x74, 0xb5, 0x06, 0xb3, 0x62, 0x94, 0x2d, 0x45, 0xe9, - 0x9a, 0x82, 0x52, 0x30, 0xba, 0x58, 0x7b, 0x0b, 0xf7, 0x8d, 0x6c, 0x5c, 0x58, 0x32, 0xbd, 0x5a, 0x94, 0x94, 0x50, - 0xdd, 0x54, 0x8c, 0x94, 0x30, 0x52, 0x1a, 0x9e, 0xca, 0xf7, 0x02, 0x8f, 0xf3, 0x3c, 0x88, 0x5a, 0x5e, 0x60, 0xc7, - 0x15, 0x39, 0x06, 0x47, 0x2f, 0x93, 0xd3, 0x50, 0xe0, 0x1f, 0x33, 0x05, 0x62, 0x3a, 0x54, 0xf7, 0x1b, 0xdc, 0xfc, - 0xff, 0x28, 0x58, 0xe0, 0xf1, 0xad, 0x97, 0xb8, 0x8d, 0xfe, 0x51, 0xf8, 0xb4, 0xf4, 0xb9, 0xf4, 0x5d, 0x5d, 0x3c, - 0x69, 0x6f, 0x36, 0x4a, 0x96, 0x59, 0x9e, 0xbe, 0x95, 0x29, 0x07, 0x91, 0x19, 0x5a, 0x83, 0xb2, 0x23, 0xd1, 0xb8, - 0xe1, 0x81, 0x11, 0x63, 0xe3, 0xc6, 0xf7, 0x63, 0x06, 0xb2, 0x61, 0xb0, 0xfa, 0x66, 0xa9, 0x4c, 0xd6, 0x57, 0x80, - 0x29, 0xa2, 0xe4, 0x27, 0x2f, 0x73, 0x0e, 0x4f, 0xa1, 0xbe, 0x7e, 0x81, 0xdb, 0x5c, 0xe9, 0xfb, 0x9c, 0xff, 0x98, - 0xd1, 0x1f, 0x11, 0xe8, 0x24, 0x5e, 0x81, 0xdc, 0xe3, 0x39, 0xd4, 0x8d, 0x30, 0xb5, 0x1c, 0x83, 0x03, 0x21, 0x1a, - 0x88, 0xa8, 0x58, 0xa0, 0xa0, 0x2e, 0x0c, 0xb0, 0x86, 0xba, 0x60, 0x0e, 0xcf, 0x73, 0x99, 0x7c, 0x9c, 0x1a, 0x9f, - 0xf9, 0x61, 0x8c, 0x31, 0x93, 0x83, 0x41, 0x58, 0xcd, 0x82, 0xe1, 0x78, 0x34, 0x39, 0x78, 0x06, 0xe7, 0x76, 0x30, - 0x0e, 0xc8, 0x20, 0xa8, 0xcb, 0x55, 0x2c, 0x68, 0x79, 0x7d, 0x69, 0xcb, 0xc0, 0x8f, 0xeb, 0x60, 0xf0, 0x8f, 0xc2, - 0x53, 0xbc, 0x83, 0xe6, 0xe4, 0x4c, 0x86, 0x60, 0x63, 0xbf, 0x26, 0x20, 0x29, 0xeb, 0x69, 0x7e, 0x52, 0x1f, 0x6e, - 0x4c, 0x69, 0xff, 0xcc, 0xe1, 0x05, 0x87, 0x1d, 0x12, 0x28, 0x90, 0xc6, 0xd3, 0x6c, 0xf4, 0x5a, 0x29, 0x72, 0xdf, - 0x15, 0x1c, 0xee, 0xcc, 0x3d, 0x67, 0x7a, 0xe4, 0x14, 0x12, 0xcd, 0x2c, 0xe0, 0x46, 0xfe, 0x5a, 0x5c, 0xc7, 0x79, - 0x96, 0xee, 0x35, 0xdf, 0xec, 0x95, 0x77, 0xa2, 0x8a, 0x6f, 0x47, 0x81, 0xb1, 0x26, 0xe4, 0xbe, 0xea, 0x09, 0xd0, - 0x13, 0x60, 0x0b, 0x80, 0x01, 0xf1, 0x8e, 0x99, 0xc9, 0x8c, 0x47, 0xe0, 0x11, 0xd8, 0xf4, 0x81, 0x2c, 0xee, 0x9c, - 0x4b, 0x92, 0xbf, 0x99, 0x4a, 0x7b, 0xd5, 0x2b, 0x77, 0x0a, 0xb2, 0x5e, 0x6d, 0xe5, 0xae, 0x5b, 0x9f, 0x7d, 0xd3, - 0xe1, 0x15, 0x78, 0x2e, 0xc1, 0x2d, 0xb2, 0xdf, 0x6f, 0x0a, 0x2a, 0x85, 0x51, 0x11, 0xef, 0x24, 0xd7, 0xe8, 0xdf, - 0xee, 0x8d, 0x8d, 0x22, 0xb9, 0xe5, 0xc3, 0x03, 0xa8, 0x33, 0x79, 0x57, 0xdc, 0xce, 0x21, 0x6a, 0xeb, 0x6e, 0x3c, - 0xb0, 0xda, 0xa0, 0x5d, 0xd6, 0x1c, 0xc1, 0x85, 0x17, 0x7b, 0x19, 0x8c, 0x05, 0xce, 0xca, 0x48, 0xa9, 0x71, 0x0d, - 0xa9, 0x05, 0x9f, 0xe4, 0xe9, 0x3d, 0x64, 0xa9, 0x27, 0x41, 0x91, 0xe3, 0x59, 0x0c, 0x99, 0xc6, 0xdb, 0xc0, 0xe3, - 0x77, 0x32, 0x04, 0x69, 0xda, 0x76, 0xdb, 0x1c, 0x81, 0xb2, 0x7b, 0x60, 0x4a, 0x52, 0xd7, 0xc6, 0xd4, 0x40, 0x43, - 0xed, 0xa1, 0x46, 0x2a, 0xe2, 0xec, 0xe8, 0x0d, 0xe8, 0x10, 0xc1, 0xf7, 0x3b, 0xcd, 0xca, 0x8e, 0x17, 0x13, 0x82, - 0x27, 0xef, 0xcb, 0xdb, 0xac, 0xac, 0xca, 0xe8, 0x7d, 0x8a, 0x86, 0x50, 0x89, 0x14, 0xd1, 0x2b, 0x88, 0xa7, 0x57, - 0xe2, 0xef, 0x32, 0xfa, 0x39, 0xa5, 0x71, 0x9a, 0x62, 0xfa, 0x8b, 0x02, 0x7e, 0x3e, 0x07, 0x54, 0x47, 0xdc, 0x09, - 0xd1, 0xb9, 0x04, 0x7b, 0x35, 0x88, 0x66, 0x55, 0x71, 0xc0, 0xd0, 0x8c, 0x6e, 0x05, 0x45, 0x8c, 0x36, 0xcc, 0xfe, - 0x43, 0x81, 0x42, 0x21, 0x55, 0xcc, 0x77, 0xc2, 0x3e, 0x44, 0x3f, 0x62, 0x91, 0xc7, 0xef, 0x5e, 0x9b, 0x21, 0x8d, - 0xee, 0x24, 0xd5, 0x5b, 0x1b, 0x8f, 0x2d, 0x0c, 0x5c, 0x16, 0x5d, 0xae, 0xe9, 0x59, 0xbc, 0xca, 0xa2, 0x0d, 0xe0, - 0x4f, 0xbc, 0x7b, 0xfd, 0x5c, 0x59, 0x98, 0xbc, 0xc8, 0x40, 0x71, 0x70, 0xfc, 0xee, 0xf5, 0x1b, 0x99, 0xae, 0x73, - 0x1e, 0x9d, 0x49, 0x24, 0xad, 0xc7, 0xef, 0x5e, 0xff, 0x82, 0xe6, 0x5e, 0x3f, 0x17, 0xf0, 0xfe, 0x15, 0xf0, 0x96, - 0x51, 0xbc, 0x86, 0x3e, 0xa9, 0xdf, 0xc9, 0x1a, 0x3b, 0xe5, 0xd5, 0x5a, 0x46, 0xbf, 0xa6, 0xb5, 0x27, 0xad, 0xfa, - 0x67, 0xe1, 0x53, 0x3b, 0x4f, 0xc0, 0x73, 0x9b, 0x67, 0xe2, 0x63, 0x64, 0x45, 0x3b, 0x41, 0xf4, 0xf5, 0xde, 0xed, - 0x55, 0x2e, 0xca, 0x08, 0x5f, 0x30, 0xb4, 0x0b, 0x8a, 0xf6, 0xf7, 0x6f, 0x6e, 0x6e, 0x46, 0x37, 0x4f, 0x46, 0xb2, - 0xb8, 0xdc, 0x9f, 0x7c, 0xfb, 0xed, 0xb7, 0xfb, 0xf8, 0x36, 0xf8, 0xba, 0xed, 0xf6, 0x5e, 0x11, 0x3e, 0x60, 0x01, - 0x22, 0x54, 0x7f, 0x0d, 0x57, 0x14, 0xd0, 0xc2, 0x0d, 0xbe, 0x0e, 0xbe, 0xd6, 0x87, 0xce, 0xd7, 0x87, 0xe5, 0xf5, - 0xa5, 0x2a, 0xbf, 0xab, 0xe4, 0x83, 0xf1, 0x78, 0xbc, 0x0f, 0x12, 0xa8, 0xaf, 0x07, 0x7c, 0x10, 0x1c, 0x05, 0x83, - 0x0c, 0x2e, 0x34, 0xe5, 0xf5, 0xe5, 0x51, 0xe0, 0x99, 0xe6, 0x36, 0x58, 0x44, 0x07, 0xe2, 0x12, 0xec, 0x5f, 0xd2, - 0xe0, 0xeb, 0x80, 0xb8, 0x94, 0xaf, 0x20, 0xe5, 0xab, 0x83, 0x67, 0x7e, 0xda, 0xff, 0x52, 0x69, 0x4f, 0xfc, 0xb4, - 0x43, 0x4c, 0x7b, 0xf2, 0xdc, 0x4f, 0x3b, 0x52, 0x69, 0x2f, 0xfd, 0xb4, 0xff, 0x5d, 0x0e, 0x20, 0x75, 0xcf, 0xb7, - 0xfe, 0x3b, 0xf7, 0x5a, 0x83, 0xa7, 0x50, 0x94, 0x5d, 0xc5, 0x97, 0x1c, 0x1a, 0x3d, 0xb8, 0xbd, 0xca, 0x69, 0x30, - 0xc0, 0xf6, 0x7a, 0x26, 0x21, 0xde, 0x07, 0x5f, 0xaf, 0x8b, 0x3c, 0x0c, 0xbe, 0x1e, 0x60, 0x21, 0x83, 0xaf, 0x03, - 0xf2, 0xb5, 0x31, 0x90, 0x11, 0x6c, 0x13, 0xb8, 0x50, 0xa4, 0x43, 0x1b, 0x20, 0xcc, 0x97, 0xc6, 0xd5, 0xf4, 0xaf, - 0xa2, 0x3b, 0x1b, 0xde, 0x12, 0x95, 0x9b, 0x6e, 0x50, 0xd3, 0x13, 0xf0, 0x4e, 0x80, 0x46, 0x45, 0xc1, 0x75, 0x5c, - 0x84, 0xc3, 0x61, 0x79, 0x7d, 0x49, 0xc0, 0x2e, 0x73, 0xc5, 0xe3, 0x2a, 0x0a, 0x84, 0x1c, 0xaa, 0x9f, 0x81, 0x8a, - 0x7c, 0x15, 0x20, 0x20, 0x12, 0xfc, 0x17, 0xd4, 0xf4, 0x9d, 0x64, 0x9b, 0x60, 0x78, 0xc3, 0xcf, 0x3f, 0x66, 0xd5, - 0x50, 0x89, 0x16, 0xaf, 0x05, 0x85, 0x1f, 0xf0, 0xd7, 0x55, 0x1d, 0xfd, 0x05, 0x6e, 0xdc, 0x4d, 0x0d, 0xfb, 0x3b, - 0xe9, 0x58, 0xd4, 0x77, 0x72, 0x9e, 0x2d, 0xa6, 0xad, 0x03, 0xfd, 0x44, 0x92, 0x6a, 0x9e, 0x0d, 0x82, 0x61, 0x30, - 0xe0, 0x0b, 0x76, 0x22, 0xe7, 0xdc, 0x33, 0x9f, 0x7a, 0x24, 0xfd, 0x69, 0x9e, 0x65, 0x03, 0xf0, 0x4d, 0x41, 0x7e, - 0x64, 0xff, 0xbf, 0xe7, 0x43, 0x14, 0x1e, 0x0e, 0x1e, 0xed, 0x93, 0x59, 0xb0, 0xba, 0x45, 0x8f, 0xce, 0x28, 0xc8, - 0xc4, 0x92, 0x17, 0x59, 0xe5, 0x2d, 0x95, 0xbb, 0x75, 0xdb, 0xcb, 0xe3, 0xde, 0xb3, 0x79, 0x15, 0x8b, 0x40, 0x9d, - 0x73, 0xa0, 0x78, 0x43, 0xd9, 0x53, 0xd9, 0x94, 0x90, 0x6a, 0x43, 0xde, 0xb0, 0x1c, 0xb0, 0xe0, 0xb0, 0x37, 0x1c, - 0xee, 0x05, 0x03, 0xa7, 0xce, 0x1d, 0x04, 0x7b, 0xc3, 0xe1, 0x51, 0xe0, 0xee, 0x43, 0xd9, 0xc8, 0xdd, 0x19, 0x69, - 0xc1, 0xfe, 0x59, 0x84, 0x25, 0x05, 0xf1, 0x98, 0xd4, 0xe2, 0x2f, 0x0d, 0x2e, 0x33, 0x00, 0xe8, 0x23, 0x25, 0x01, - 0x33, 0xb0, 0x32, 0x03, 0x08, 0xcd, 0x4d, 0x63, 0x76, 0x06, 0xcc, 0x23, 0x70, 0xcc, 0x23, 0x64, 0x1c, 0x00, 0xb1, - 0x24, 0xc0, 0xb9, 0x0b, 0xa2, 0x58, 0x17, 0xf2, 0x08, 0x40, 0xef, 0xf1, 0x27, 0x31, 0xa5, 0x60, 0x92, 0x8e, 0x55, - 0x08, 0x82, 0x38, 0x3e, 0xbb, 0x16, 0xad, 0xc9, 0x59, 0xa2, 0x83, 0x19, 0x49, 0x80, 0x0d, 0x31, 0xb0, 0x73, 0x70, - 0x3f, 0x07, 0xa5, 0x87, 0xd5, 0x3b, 0x21, 0x17, 0x7c, 0xc7, 0x3d, 0xd9, 0x2c, 0x5c, 0x3d, 0xe1, 0x20, 0xb8, 0xe3, - 0x9a, 0x05, 0x18, 0x55, 0xc5, 0xba, 0xac, 0x78, 0xfa, 0xe1, 0x6e, 0x05, 0xb1, 0xef, 0x70, 0x40, 0xdf, 0xc9, 0x3c, - 0x4b, 0xee, 0x42, 0x67, 0xcf, 0xb5, 0x51, 0xe9, 0x3f, 0x7c, 0x78, 0xf3, 0x73, 0x04, 0x22, 0xc7, 0xda, 0x50, 0xfa, - 0x3b, 0x8e, 0x67, 0x93, 0x1f, 0xe1, 0xc9, 0xdf, 0xd8, 0x77, 0xdc, 0x9e, 0x1e, 0xfd, 0x3e, 0xd4, 0x4d, 0xef, 0xf8, - 0xec, 0x8e, 0x8f, 0x5c, 0x71, 0xa8, 0xae, 0x70, 0x5f, 0xdf, 0xac, 0x7d, 0x23, 0xa4, 0x87, 0xe7, 0x99, 0xf2, 0xc6, - 0xfc, 0x68, 0x07, 0xc3, 0x20, 0x98, 0x6a, 0xa1, 0x24, 0x44, 0xdd, 0x60, 0x4a, 0xc0, 0x10, 0xed, 0xe9, 0x65, 0x35, - 0x45, 0xce, 0x4d, 0x8d, 0x2c, 0xbc, 0x1f, 0x30, 0x2d, 0x74, 0x68, 0xe4, 0x50, 0x7e, 0x70, 0x38, 0x61, 0xcc, 0xc2, - 0x6f, 0x95, 0x30, 0xfd, 0x6a, 0x51, 0x39, 0x07, 0xd1, 0x3d, 0x30, 0xc6, 0x15, 0xbc, 0x80, 0xae, 0xb0, 0xeb, 0xb5, - 0x8a, 0x8a, 0x81, 0xe0, 0x71, 0xc8, 0x01, 0x7a, 0xd8, 0x05, 0x2d, 0x2b, 0x4b, 0x75, 0xab, 0x72, 0x96, 0x2a, 0xea, - 0x32, 0x94, 0x95, 0xb1, 0xc2, 0x7c, 0x2f, 0xd9, 0x0f, 0x05, 0x7a, 0x96, 0x4f, 0x45, 0x17, 0xbc, 0x10, 0x4a, 0xb0, - 0x5c, 0xd7, 0x3b, 0x11, 0x88, 0x3a, 0x3f, 0xf4, 0xae, 0xfa, 0x1a, 0xc7, 0x8e, 0xa7, 0x6f, 0x64, 0xca, 0xb5, 0x09, - 0x85, 0xe6, 0xf3, 0xa5, 0xaf, 0x98, 0x28, 0xd8, 0x0d, 0xf4, 0xab, 0x6d, 0xa3, 0xcf, 0xee, 0xd6, 0x7a, 0x33, 0x28, - 0xd1, 0x31, 0xaf, 0x51, 0x70, 0xad, 0x14, 0x0a, 0x46, 0x7b, 0x1b, 0x7f, 0x86, 0x23, 0xb7, 0xba, 0x3d, 0xf4, 0x7e, - 0xab, 0xe2, 0xcb, 0xb7, 0xe8, 0xdb, 0x69, 0x7f, 0x8e, 0x2a, 0xf9, 0xeb, 0x6a, 0x05, 0x3e, 0x54, 0x10, 0x59, 0xc4, - 0xe2, 0xd2, 0x42, 0x3d, 0xa7, 0xef, 0x8e, 0xdf, 0x82, 0x1f, 0x25, 0xfe, 0xfe, 0xed, 0xfb, 0xa0, 0x26, 0xd3, 0x78, - 0x56, 0x98, 0x0f, 0x6d, 0x0e, 0x08, 0x4d, 0xe2, 0xd2, 0xec, 0xfb, 0x59, 0xdc, 0x64, 0xdf, 0x35, 0x5b, 0x4f, 0x8b, - 0x26, 0x92, 0x94, 0xe1, 0xf6, 0xc1, 0x80, 0x40, 0x1f, 0x20, 0x8a, 0xb3, 0x2f, 0x68, 0x0c, 0x69, 0x3e, 0xb3, 0xef, - 0x47, 0xc4, 0x7b, 0xb9, 0x13, 0x42, 0x8c, 0x2b, 0x2c, 0x1a, 0x3d, 0xe4, 0x33, 0x1e, 0x29, 0xc3, 0xa2, 0xf7, 0x98, - 0x40, 0x9c, 0xe1, 0xb4, 0x7a, 0x8f, 0x98, 0xc7, 0x78, 0x37, 0xd0, 0xb2, 0x87, 0x28, 0xa3, 0x2e, 0x7b, 0xc3, 0xe2, - 0xfb, 0xe3, 0x3a, 0xcc, 0xac, 0xe5, 0xe5, 0x10, 0xfe, 0x06, 0xda, 0x00, 0x9c, 0x72, 0x64, 0xf9, 0x2a, 0xb3, 0xd1, - 0xd5, 0x12, 0xd3, 0x9b, 0x08, 0x62, 0xf1, 0xe8, 0x74, 0x58, 0xbb, 0x3a, 0x55, 0xef, 0x6a, 0xe7, 0x33, 0xd1, 0xab, - 0x40, 0x2b, 0xd7, 0xb6, 0xc7, 0x43, 0xb8, 0x4b, 0x2d, 0xad, 0xb0, 0x11, 0xe5, 0x5c, 0x3c, 0xdd, 0x39, 0x36, 0x27, - 0xa0, 0xc1, 0x95, 0x4c, 0x01, 0x38, 0x4b, 0xab, 0xd1, 0xa8, 0x11, 0xf6, 0x59, 0x39, 0x9f, 0xc3, 0xd6, 0x42, 0x3c, - 0x2d, 0x00, 0xc3, 0x6d, 0x62, 0x50, 0xf2, 0x6e, 0x0c, 0xca, 0xe9, 0x47, 0x05, 0x6f, 0x1d, 0x9c, 0x95, 0xcb, 0x38, - 0x95, 0x37, 0x80, 0xc5, 0x18, 0xf8, 0xa9, 0x58, 0xaa, 0x97, 0x90, 0x2c, 0x79, 0xf2, 0x11, 0xad, 0x36, 0xd2, 0x00, - 0xb8, 0xca, 0xa9, 0xb1, 0xdc, 0x53, 0x20, 0xa1, 0xae, 0x14, 0x95, 0x10, 0x57, 0x55, 0x9c, 0x2c, 0x4f, 0x31, 0x35, - 0xdc, 0x40, 0x2f, 0xa2, 0x40, 0xae, 0xb8, 0x00, 0x92, 0x9e, 0xb3, 0x7f, 0x65, 0x1a, 0x6b, 0xfc, 0xb9, 0x44, 0x01, - 0x93, 0x46, 0x0d, 0xc6, 0x4a, 0xd9, 0x4b, 0x69, 0xa2, 0xbd, 0x05, 0x41, 0xed, 0x5e, 0xfe, 0x05, 0x75, 0x3f, 0x87, - 0x56, 0x84, 0x0d, 0x30, 0x44, 0x79, 0x8e, 0x3b, 0x34, 0xb5, 0x4b, 0xce, 0x03, 0x46, 0x74, 0xde, 0x67, 0xb5, 0xdd, - 0xea, 0xcf, 0x97, 0x80, 0x6d, 0x9a, 0x1a, 0x9f, 0xc2, 0x30, 0x21, 0x26, 0x36, 0xb0, 0x55, 0x56, 0xda, 0x0d, 0x65, - 0xda, 0x49, 0x97, 0xcc, 0x6b, 0xe1, 0x34, 0xef, 0x31, 0xb6, 0x1c, 0xa9, 0xdc, 0xfd, 0x7e, 0x68, 0x7e, 0xb2, 0x9c, - 0x3e, 0xd7, 0x21, 0x9b, 0xbd, 0xf1, 0xa0, 0x39, 0xd1, 0xea, 0xaa, 0x8e, 0x7e, 0x40, 0x07, 0x60, 0xa6, 0x2d, 0x40, - 0xa6, 0x0b, 0x36, 0xed, 0x2b, 0x51, 0x71, 0x49, 0xc2, 0x52, 0x49, 0x60, 0x67, 0x37, 0x25, 0x3b, 0x9b, 0x80, 0x78, - 0x86, 0xbb, 0x9e, 0x16, 0x3b, 0x21, 0x4d, 0x78, 0x8b, 0xbd, 0x04, 0x44, 0x1d, 0xaa, 0xba, 0x84, 0x6c, 0x8c, 0xa1, - 0x8b, 0x7f, 0x51, 0x0a, 0x13, 0xd6, 0x32, 0xa9, 0x4a, 0x4c, 0x10, 0xa4, 0x72, 0xb7, 0x45, 0x60, 0x89, 0x82, 0x1d, - 0xc0, 0xde, 0xbb, 0x51, 0x37, 0xa3, 0xa6, 0xaa, 0x53, 0x2f, 0xc1, 0xc7, 0x69, 0xd6, 0x55, 0x90, 0x59, 0xd8, 0x55, - 0xb1, 0xe6, 0x81, 0x8e, 0x4d, 0xa5, 0x8c, 0x89, 0xbb, 0xb4, 0xc8, 0x10, 0x0f, 0x18, 0x63, 0xe9, 0x42, 0x20, 0xdf, - 0x6c, 0x77, 0xdc, 0xf4, 0x04, 0xa1, 0x9f, 0xb0, 0xa1, 0x04, 0x6e, 0x3a, 0xdb, 0x53, 0xd3, 0xcc, 0x07, 0x44, 0x1c, - 0x06, 0x14, 0x48, 0x36, 0x0e, 0x69, 0x8e, 0xf4, 0x05, 0x49, 0x13, 0x06, 0x86, 0x56, 0x3c, 0x27, 0xc8, 0x8a, 0x42, - 0xcf, 0xd6, 0x55, 0x1b, 0xe7, 0xca, 0x30, 0x47, 0x4b, 0x4e, 0x85, 0xcf, 0x09, 0x32, 0xb1, 0x7b, 0xda, 0x66, 0x26, - 0xc3, 0x51, 0xb2, 0xc0, 0xfc, 0x0a, 0xa2, 0xc4, 0x9d, 0x69, 0x56, 0xe5, 0x60, 0x5c, 0xc0, 0x02, 0xad, 0x7c, 0x0f, - 0xea, 0xc6, 0x1a, 0xda, 0x68, 0x18, 0x62, 0xb7, 0x3f, 0xc1, 0x7e, 0xad, 0x9d, 0xd6, 0x65, 0x8a, 0xe5, 0x65, 0x0a, - 0xd1, 0x5e, 0xc8, 0xfc, 0x46, 0x91, 0xe8, 0x4e, 0x11, 0x86, 0x84, 0x75, 0x94, 0x3d, 0x69, 0x53, 0x03, 0xe8, 0xa9, - 0x17, 0xf0, 0xbc, 0x73, 0x2d, 0xc3, 0x2e, 0xd2, 0xfd, 0x55, 0xc1, 0xa7, 0x74, 0x83, 0x20, 0x45, 0x6f, 0x52, 0x30, - 0xe7, 0xf5, 0x28, 0xa9, 0x37, 0xa7, 0x2d, 0x33, 0xaa, 0x8e, 0x8a, 0x90, 0x72, 0x82, 0xff, 0xe4, 0xa5, 0xd4, 0xc4, - 0x26, 0x4c, 0xf0, 0xc0, 0x87, 0x79, 0x86, 0x0d, 0xbc, 0xdd, 0xbe, 0x4b, 0xc3, 0xa4, 0xcd, 0x36, 0xa4, 0x20, 0xad, - 0x30, 0x71, 0x31, 0xa0, 0xb2, 0xd7, 0xb8, 0x5f, 0xb0, 0x9d, 0x34, 0x05, 0x0f, 0xc2, 0x46, 0x03, 0x13, 0xb7, 0xba, - 0xf8, 0x3a, 0x4c, 0x68, 0xb8, 0xa4, 0xda, 0xd9, 0x49, 0x4b, 0x9a, 0xdb, 0xeb, 0xf2, 0xc2, 0xf6, 0x41, 0xc7, 0x0e, - 0xeb, 0x1a, 0x1e, 0x68, 0x5e, 0xb3, 0x8b, 0x2b, 0xa6, 0x69, 0xa2, 0xb1, 0x1e, 0x52, 0x96, 0x1c, 0xeb, 0x7a, 0xba, - 0xc2, 0xd5, 0x32, 0xd3, 0xc0, 0xee, 0x12, 0x2f, 0xf4, 0x80, 0x87, 0x1d, 0xae, 0x48, 0x74, 0x81, 0xcd, 0x66, 0xab, - 0x9a, 0x4c, 0xf3, 0xfb, 0xb2, 0xe5, 0x26, 0x20, 0x9c, 0xa5, 0xbe, 0xb9, 0x4f, 0x8e, 0x35, 0x6d, 0xf3, 0x93, 0x00, - 0xc7, 0xdb, 0x2b, 0x20, 0xe9, 0x58, 0x82, 0x2e, 0xbe, 0xa5, 0x3f, 0x88, 0xd4, 0x4c, 0x05, 0xbd, 0x77, 0xbe, 0x48, - 0xdd, 0xfc, 0x02, 0x6c, 0xa3, 0x36, 0xc6, 0x34, 0x2b, 0x5b, 0x87, 0x89, 0xb2, 0xb0, 0x46, 0x16, 0x72, 0x09, 0x3e, - 0x98, 0xbb, 0x4d, 0x9d, 0x1e, 0x77, 0x10, 0x61, 0xbf, 0x8b, 0x1e, 0x8f, 0x30, 0x56, 0xac, 0x41, 0x62, 0x58, 0x85, - 0x35, 0x6d, 0x2e, 0x87, 0x28, 0xa7, 0x66, 0xc9, 0x44, 0x4b, 0xea, 0x53, 0x8a, 0x28, 0x05, 0x73, 0xe3, 0x69, 0xd9, - 0x30, 0x25, 0x44, 0xc8, 0x0a, 0xe9, 0x80, 0x6a, 0x2d, 0xb4, 0x54, 0x13, 0xf4, 0x3a, 0xf4, 0xb2, 0xd0, 0x98, 0x82, - 0xe8, 0x23, 0x32, 0xdc, 0x88, 0x23, 0xa3, 0xbb, 0x63, 0x14, 0x13, 0x08, 0x55, 0xed, 0xe5, 0x85, 0xd5, 0xa7, 0x65, - 0x5b, 0x1d, 0xc4, 0x15, 0x22, 0xdf, 0x77, 0x13, 0xd4, 0x18, 0x05, 0x6d, 0x4e, 0x37, 0xfa, 0x6b, 0x11, 0xfa, 0x76, - 0xe1, 0xd8, 0x8d, 0x82, 0x48, 0x88, 0xc0, 0xea, 0x35, 0x15, 0x03, 0xb2, 0xce, 0x63, 0x17, 0xa1, 0x49, 0x77, 0x0b, - 0x51, 0xde, 0xa8, 0xac, 0x3f, 0xae, 0x43, 0xb2, 0xdd, 0x62, 0x59, 0xe0, 0xcb, 0x7e, 0xba, 0xbe, 0x07, 0xf2, 0xfb, - 0xcd, 0xfa, 0xb3, 0x90, 0xdf, 0xaf, 0xb3, 0x2f, 0x81, 0xfc, 0x7e, 0xb3, 0xfe, 0x9f, 0x86, 0xfc, 0x3e, 0x5d, 0x7b, - 0x90, 0xdf, 0x6a, 0x30, 0x7e, 0x2f, 0x58, 0x70, 0xf2, 0x36, 0xa0, 0x2f, 0x24, 0x0b, 0x4e, 0x5e, 0xbd, 0xf2, 0x8d, - 0x40, 0x84, 0x46, 0xae, 0x37, 0xb2, 0x60, 0xc4, 0x6d, 0x81, 0x57, 0xa8, 0x75, 0xf2, 0x81, 0x8a, 0x32, 0x00, 0x5e, - 0x2f, 0xff, 0x91, 0x55, 0xcb, 0x30, 0xd8, 0x0f, 0xc8, 0xcc, 0x41, 0x82, 0x0e, 0x27, 0x70, 0x7b, 0x43, 0x23, 0xcb, - 0xea, 0x8b, 0xe0, 0xc3, 0x47, 0xa3, 0x51, 0x5c, 0x5c, 0xe2, 0xa5, 0xce, 0x6c, 0x24, 0x04, 0x3c, 0xce, 0x78, 0x69, - 0x43, 0x44, 0x2c, 0xe3, 0xf2, 0x4c, 0xc7, 0x66, 0x29, 0xed, 0x56, 0x84, 0x88, 0xf3, 0x67, 0x80, 0x53, 0x6f, 0xf7, - 0x66, 0x8c, 0xfd, 0x50, 0x1c, 0xb1, 0x0e, 0x20, 0xfb, 0x7c, 0xad, 0xdf, 0x9d, 0xc7, 0x25, 0x7f, 0x17, 0x57, 0x4b, - 0x06, 0xbd, 0x84, 0xbb, 0x88, 0xe0, 0x49, 0xe5, 0xb1, 0x4d, 0x0a, 0xa8, 0x3c, 0xd3, 0x40, 0xe5, 0x1d, 0xef, 0x69, - 0x68, 0x87, 0x45, 0xfb, 0x00, 0x1b, 0xe9, 0x72, 0x06, 0x46, 0x8b, 0x2f, 0xaf, 0xb9, 0xa8, 0x7e, 0x06, 0x3c, 0x75, - 0xc1, 0x0b, 0xb8, 0x25, 0x20, 0x17, 0xdb, 0x70, 0x42, 0xa0, 0xc2, 0xf7, 0xec, 0x50, 0x51, 0x63, 0x8c, 0x68, 0xa2, - 0xd1, 0x6f, 0xbc, 0x09, 0xa1, 0x77, 0x27, 0xe8, 0x8a, 0x30, 0x12, 0xde, 0x5f, 0x6b, 0x7e, 0x96, 0x81, 0xf9, 0xbc, - 0x00, 0x28, 0x0d, 0x84, 0x43, 0x65, 0x4a, 0x6e, 0x81, 0x09, 0x1b, 0x63, 0xae, 0x94, 0xa5, 0x1e, 0x52, 0x29, 0x55, - 0x70, 0xba, 0x82, 0xa6, 0x12, 0x70, 0xb8, 0x23, 0x09, 0x60, 0xa6, 0xb6, 0x30, 0x88, 0x6e, 0x9b, 0xd2, 0x2c, 0x8d, - 0x9c, 0x22, 0xcd, 0xe1, 0x93, 0x52, 0x05, 0x3a, 0x7d, 0x96, 0xc4, 0x15, 0xbf, 0x94, 0x05, 0x84, 0xc2, 0x6d, 0xa5, - 0x50, 0xa4, 0xdf, 0x67, 0x62, 0x7d, 0xc5, 0x8b, 0x2c, 0x39, 0x5b, 0x66, 0x65, 0x05, 0xf9, 0xe6, 0xfa, 0xf4, 0x5b, - 0xd4, 0xd3, 0x02, 0xe7, 0x4d, 0x4d, 0x0a, 0x33, 0xf3, 0x78, 0xac, 0x76, 0x3a, 0xa8, 0x57, 0xbd, 0xd7, 0x06, 0xfb, - 0xbd, 0x39, 0xd1, 0xe3, 0xd6, 0x7a, 0xb0, 0x9a, 0xd4, 0x66, 0xaa, 0x82, 0x38, 0x02, 0xea, 0xc0, 0x8e, 0x70, 0x16, - 0x53, 0xba, 0xb6, 0x08, 0x2a, 0x60, 0xed, 0x80, 0x39, 0x32, 0x71, 0x79, 0x76, 0xa3, 0xe4, 0x27, 0x3d, 0x45, 0x61, - 0xd2, 0x28, 0x56, 0xc8, 0x3e, 0x4b, 0x16, 0xae, 0x59, 0x72, 0x4f, 0xae, 0x75, 0x94, 0x34, 0x30, 0xba, 0xe2, 0xf6, - 0x40, 0x1c, 0x26, 0xed, 0x94, 0xed, 0x76, 0x27, 0x13, 0xb0, 0x06, 0xad, 0x24, 0x88, 0xd6, 0xb1, 0x9c, 0x0d, 0x27, - 0x11, 0x40, 0xa8, 0x68, 0xb2, 0xf6, 0xd7, 0x9a, 0x1b, 0x90, 0xf9, 0x50, 0x7b, 0xfa, 0x39, 0x91, 0xbc, 0x1e, 0x59, - 0xcf, 0x38, 0x4e, 0x4f, 0xfb, 0x1c, 0x0c, 0xb3, 0xfc, 0x21, 0x81, 0x48, 0x30, 0x9d, 0xd3, 0xb3, 0x98, 0x6a, 0x33, - 0x61, 0xc3, 0xa3, 0xd0, 0x2f, 0xfb, 0x4e, 0x03, 0xe0, 0x26, 0x3c, 0x1c, 0x3e, 0x1b, 0x93, 0x5a, 0xdb, 0xac, 0xe3, - 0x02, 0xb2, 0xbf, 0xd5, 0x22, 0x73, 0xcf, 0x76, 0xa1, 0xd1, 0xa6, 0xb7, 0x41, 0xbb, 0x46, 0x2a, 0xee, 0xe9, 0x7e, - 0x4d, 0x6a, 0xb7, 0x60, 0xac, 0x04, 0xe9, 0x0f, 0x75, 0x6a, 0x9d, 0x3d, 0xda, 0x64, 0xba, 0xca, 0xfa, 0x8f, 0xcc, - 0xc6, 0x9b, 0x6b, 0x50, 0x80, 0x5a, 0x2f, 0x25, 0x1f, 0xee, 0xad, 0x23, 0x9b, 0x9e, 0x18, 0x96, 0x75, 0x92, 0x91, - 0x91, 0x7a, 0xe4, 0x2a, 0xfc, 0x56, 0x77, 0x16, 0x7e, 0xcb, 0x93, 0xb0, 0xab, 0x61, 0x8a, 0xc3, 0x37, 0x5d, 0x40, - 0x04, 0x4c, 0x90, 0xeb, 0x87, 0x7f, 0x3c, 0xda, 0x34, 0xc9, 0x52, 0xbd, 0xef, 0x7d, 0x86, 0xbf, 0xb3, 0x14, 0xfe, - 0x56, 0xf5, 0x1f, 0x74, 0x73, 0xc5, 0xab, 0xa5, 0x4c, 0xa3, 0xe0, 0xdd, 0xc9, 0xe9, 0x87, 0x40, 0x23, 0xab, 0xe3, - 0xfd, 0xc2, 0x68, 0x94, 0x0d, 0x86, 0x13, 0x68, 0x58, 0x72, 0x79, 0x89, 0xe0, 0x82, 0x1a, 0x9d, 0xfe, 0x74, 0x29, - 0x6f, 0x8e, 0xf3, 0xdc, 0x67, 0x82, 0x0d, 0xe1, 0xd4, 0x7c, 0x61, 0x83, 0xea, 0x84, 0x20, 0xcb, 0x1b, 0x65, 0xe5, - 0x99, 0xd6, 0xbe, 0xa4, 0x67, 0xe7, 0x77, 0x67, 0x5a, 0xc2, 0x63, 0xd1, 0x1d, 0x9f, 0xff, 0x71, 0x98, 0x66, 0xd7, - 0x7b, 0x48, 0xdd, 0x59, 0x00, 0xa6, 0xf1, 0x39, 0x3f, 0x5f, 0x57, 0x95, 0x14, 0xc3, 0x42, 0xde, 0x04, 0x47, 0x87, - 0xea, 0xc1, 0x64, 0x88, 0xd5, 0x63, 0xb0, 0xf7, 0x5f, 0x49, 0x9e, 0x25, 0x1f, 0x59, 0xf0, 0x68, 0x93, 0xb1, 0xa3, - 0x16, 0x0d, 0x1f, 0xd7, 0xc1, 0x11, 0xb4, 0x75, 0xef, 0x38, 0xcf, 0x0f, 0xf7, 0xd5, 0x17, 0x47, 0x87, 0xfb, 0x69, - 0x76, 0x7d, 0xe4, 0x01, 0xed, 0x3b, 0xbb, 0x59, 0x84, 0x34, 0x73, 0xf7, 0x62, 0x70, 0x90, 0x4d, 0x78, 0x68, 0x39, - 0x09, 0x90, 0xc6, 0x98, 0x30, 0x25, 0x28, 0xc1, 0x09, 0x63, 0x38, 0x37, 0xb7, 0xdb, 0xd0, 0x1a, 0xf5, 0x24, 0x1e, - 0xe2, 0x4d, 0x01, 0x9c, 0x06, 0x66, 0xa1, 0x09, 0xa1, 0x49, 0x4d, 0x42, 0x83, 0xcb, 0x13, 0x13, 0x5a, 0xd4, 0x14, - 0x8e, 0x92, 0x37, 0xf1, 0xca, 0x08, 0xb1, 0xb4, 0x50, 0xc0, 0xb4, 0x7e, 0xd6, 0x18, 0xc7, 0xa8, 0x3d, 0xaa, 0x06, - 0x29, 0xab, 0x57, 0xde, 0x37, 0xb0, 0x20, 0xb7, 0x0c, 0x2b, 0x1a, 0xb4, 0x28, 0x04, 0xc8, 0x1d, 0x7d, 0x71, 0x19, - 0xa7, 0xe1, 0xbc, 0xa4, 0x72, 0x41, 0xd8, 0x51, 0xb8, 0x41, 0xb6, 0xb9, 0x54, 0x54, 0x38, 0x92, 0xb5, 0x83, 0xad, - 0x54, 0xb3, 0x73, 0xf4, 0x68, 0x23, 0x10, 0x29, 0xb1, 0x64, 0x47, 0xcd, 0xf9, 0xaa, 0xe2, 0xf3, 0xe1, 0x92, 0x83, - 0x7f, 0x4d, 0xb0, 0xf7, 0x5f, 0xe9, 0x79, 0x6e, 0x27, 0x45, 0xad, 0xc8, 0x65, 0x2c, 0xd2, 0x9c, 0x7f, 0x88, 0xcf, - 0x7f, 0xc0, 0x3c, 0x2f, 0xce, 0xf3, 0xe7, 0x90, 0xa1, 0x0e, 0x8e, 0x1e, 0x6d, 0x92, 0x6a, 0xf4, 0xf2, 0xed, 0x87, - 0xd7, 0x1f, 0xfe, 0x79, 0xf6, 0xfc, 0xf8, 0xc3, 0xcb, 0xef, 0x4f, 0xde, 0xbf, 0x7e, 0x79, 0x3a, 0xb7, 0x0e, 0xad, - 0x0a, 0x27, 0x8d, 0x2c, 0xb6, 0x5b, 0x97, 0xef, 0xd7, 0xb7, 0x2f, 0x5e, 0xbe, 0x7a, 0xfd, 0xf6, 0xe5, 0x8b, 0x5a, - 0xcd, 0x65, 0xbb, 0x21, 0xb0, 0x43, 0xe3, 0x4c, 0xf0, 0x02, 0x8a, 0xd7, 0xd1, 0x1a, 0xb1, 0xd9, 0x1a, 0xde, 0xaf, - 0xd9, 0x74, 0x1d, 0x09, 0x01, 0x16, 0xd9, 0x9e, 0xde, 0x2c, 0xd0, 0x70, 0x69, 0x36, 0x8e, 0xbf, 0xc4, 0xfc, 0xde, - 0xbc, 0xc4, 0xef, 0xde, 0xcb, 0x1b, 0xd3, 0x15, 0x3d, 0x42, 0x0a, 0xb9, 0x6b, 0xf6, 0xfc, 0x8f, 0x43, 0x5f, 0x5a, - 0x86, 0x22, 0x05, 0x55, 0x2e, 0xfc, 0xaa, 0x83, 0x3d, 0x6d, 0xb9, 0x17, 0x40, 0xe0, 0x89, 0xe0, 0xe8, 0x70, 0xdf, - 0xcf, 0x7d, 0xf4, 0x47, 0xf4, 0xb3, 0xd7, 0x39, 0x2c, 0x15, 0xc6, 0xa1, 0x99, 0xb6, 0x73, 0xba, 0x41, 0x68, 0x24, - 0x77, 0xfe, 0xa9, 0x15, 0x64, 0xc8, 0x95, 0x24, 0x91, 0x9d, 0x44, 0x65, 0x91, 0x62, 0x4a, 0xfb, 0x43, 0xff, 0x75, - 0x7d, 0xc6, 0x1b, 0x3e, 0x17, 0xa5, 0x2c, 0x02, 0xe8, 0x47, 0x3b, 0x5e, 0xc4, 0x9e, 0x17, 0x97, 0x05, 0x7b, 0xd4, - 0x49, 0xde, 0x61, 0x44, 0xf6, 0xdb, 0x9f, 0x7a, 0x1d, 0xfb, 0x83, 0xb8, 0x1f, 0x7b, 0xba, 0x33, 0x2d, 0xf2, 0x62, - 0x1b, 0x78, 0x7f, 0x5c, 0x8f, 0xf9, 0x49, 0x46, 0xff, 0x21, 0xe9, 0x65, 0x4c, 0xaf, 0x62, 0x7a, 0x2a, 0x16, 0x75, - 0xe7, 0xec, 0xd8, 0x98, 0x31, 0x94, 0x4f, 0x43, 0x40, 0x9e, 0xd0, 0xf7, 0x01, 0xcd, 0x25, 0x67, 0x23, 0xad, 0x2b, - 0xfb, 0x10, 0x17, 0x97, 0xdc, 0x84, 0x6a, 0x31, 0x6f, 0x2b, 0x3d, 0x2a, 0xc4, 0x1b, 0x16, 0x80, 0x65, 0xe9, 0x69, - 0x93, 0x82, 0x6c, 0x94, 0x54, 0x45, 0xfe, 0x13, 0xbf, 0x03, 0xae, 0xad, 0xac, 0xe4, 0x0a, 0x78, 0xf5, 0xff, 0xd3, - 0xdc, 0xb3, 0x37, 0xb7, 0x6d, 0x23, 0xff, 0x7f, 0x3f, 0x05, 0xc3, 0xe4, 0x52, 0x31, 0x21, 0x69, 0x92, 0xb2, 0x6c, - 0x45, 0xb2, 0xec, 0x6b, 0xf3, 0x98, 0x4b, 0xc7, 0x6d, 0x3a, 0x89, 0x9b, 0xb9, 0xab, 0xeb, 0xb1, 0x28, 0x09, 0x92, - 0x78, 0xa1, 0x48, 0x0d, 0x49, 0xf9, 0x51, 0x85, 0xf7, 0x59, 0xee, 0xb3, 0xdc, 0x27, 0xfb, 0xcd, 0xee, 0x02, 0x20, - 0xf8, 0xd0, 0xc3, 0x4d, 0x7a, 0xf7, 0x9b, 0x36, 0x89, 0x08, 0x02, 0x4b, 0x60, 0x01, 0x2c, 0x16, 0xfb, 0xf4, 0x67, - 0x5c, 0xf6, 0x62, 0xb6, 0xd8, 0x7e, 0x9f, 0xfb, 0xfc, 0x99, 0xd9, 0xb8, 0x24, 0x81, 0xe1, 0xb3, 0xb3, 0x78, 0x36, - 0x0b, 0x59, 0x4b, 0x17, 0xc9, 0x43, 0x74, 0x53, 0x7e, 0xe6, 0xec, 0x91, 0x23, 0x22, 0x76, 0x1a, 0xf9, 0xa6, 0xad, - 0x25, 0x46, 0xcc, 0x64, 0x48, 0x3b, 0xe2, 0x5c, 0x51, 0x36, 0x7b, 0x83, 0xea, 0x0d, 0x3e, 0x2f, 0xc5, 0xd6, 0xb5, - 0x26, 0xf1, 0x6a, 0x14, 0x32, 0x0b, 0x97, 0x3b, 0x7c, 0x72, 0x3d, 0x5a, 0x8d, 0x46, 0x90, 0xa5, 0xe5, 0x91, 0x63, - 0x42, 0xdc, 0x99, 0x38, 0xc5, 0xfb, 0x60, 0x6e, 0xf4, 0x61, 0x50, 0x76, 0x56, 0xed, 0x3e, 0xd8, 0x8a, 0x80, 0xa8, - 0x87, 0x3e, 0x90, 0xc1, 0xdd, 0xaf, 0x61, 0xd7, 0x0e, 0xf4, 0x0f, 0xb0, 0xfa, 0x52, 0xbd, 0xdf, 0xb4, 0xf5, 0x07, - 0x97, 0xfa, 0x07, 0xc4, 0x31, 0x66, 0x2f, 0x7e, 0x49, 0xab, 0x57, 0x37, 0x75, 0x52, 0x7a, 0xaf, 0x30, 0x8f, 0x01, - 0x08, 0x7d, 0x5f, 0x05, 0xfe, 0x2c, 0x8a, 0xd3, 0x2c, 0x18, 0xeb, 0x57, 0xfd, 0xb7, 0x41, 0xeb, 0x72, 0x91, 0xb5, - 0x8c, 0x2b, 0x73, 0x9c, 0xa9, 0x29, 0x50, 0x04, 0xc1, 0xc4, 0x0c, 0x28, 0x9b, 0x2a, 0xa9, 0x3b, 0x68, 0x6b, 0x45, - 0x41, 0x9a, 0xb1, 0xd2, 0x38, 0x1b, 0x40, 0xbd, 0x4a, 0x3e, 0x15, 0x4c, 0x0c, 0xa5, 0x63, 0x4b, 0xa3, 0x4f, 0x37, - 0x95, 0x97, 0xab, 0x35, 0x1e, 0xe5, 0x59, 0x71, 0x5a, 0x62, 0x0c, 0x60, 0xe1, 0x38, 0x43, 0xcf, 0x8f, 0x54, 0xa3, - 0xcf, 0xd2, 0xb9, 0x3b, 0xfc, 0xae, 0xcc, 0x17, 0xc0, 0xf9, 0x0d, 0x16, 0x17, 0x51, 0x9c, 0x69, 0x10, 0xd8, 0x06, - 0xbe, 0x38, 0xac, 0x1a, 0x89, 0x71, 0xa8, 0x2d, 0x23, 0xe7, 0xc4, 0xe0, 0x7b, 0x3c, 0xfc, 0x5a, 0x3c, 0xbc, 0x59, - 0x29, 0x82, 0x05, 0x5d, 0x16, 0x22, 0x98, 0xc0, 0x2c, 0x3e, 0x8f, 0x6f, 0xab, 0x7a, 0x90, 0x97, 0xc3, 0xdd, 0x67, - 0x6f, 0x4b, 0xb0, 0xc9, 0x22, 0xab, 0x5f, 0x8b, 0x27, 0x26, 0x15, 0x8c, 0x4e, 0x65, 0x4f, 0xa1, 0xe1, 0x87, 0xe0, - 0x61, 0x32, 0xb0, 0x13, 0xc3, 0xb3, 0x00, 0x48, 0x12, 0x3f, 0xa6, 0x87, 0xf9, 0xb5, 0x48, 0x9d, 0x2c, 0x12, 0x17, - 0x2b, 0x87, 0x33, 0x50, 0xd7, 0x68, 0xb9, 0xca, 0x30, 0xd4, 0x2e, 0x74, 0x80, 0xe5, 0xba, 0x86, 0xa1, 0x3b, 0x81, - 0x4a, 0x17, 0x6c, 0x62, 0xae, 0x6b, 0xc1, 0xa4, 0x5e, 0xc6, 0x99, 0x5e, 0x20, 0x5e, 0x48, 0xdf, 0x51, 0x50, 0x05, - 0x8f, 0x09, 0x1f, 0xc6, 0xd8, 0x2c, 0xe2, 0xd4, 0xb7, 0xc6, 0xa8, 0xd0, 0x69, 0xa0, 0x0c, 0x63, 0x82, 0xd3, 0x6f, - 0x85, 0x8d, 0x83, 0x85, 0xf0, 0x9b, 0xa5, 0x61, 0x0e, 0x9f, 0xac, 0xa3, 0xfc, 0xec, 0xc9, 0x3a, 0xcd, 0x07, 0x4f, - 0xd6, 0xbe, 0xb4, 0x15, 0xd0, 0x2f, 0x74, 0x32, 0x14, 0x18, 0x22, 0x1a, 0x86, 0xf9, 0x75, 0xe1, 0xb9, 0x53, 0x8c, - 0x17, 0x56, 0x19, 0x95, 0x6b, 0xa8, 0xba, 0x1f, 0x70, 0x05, 0xfd, 0x32, 0x09, 0x16, 0x7e, 0x72, 0x4f, 0xfa, 0x7c, - 0x53, 0x55, 0xfa, 0x1b, 0xba, 0x46, 0x84, 0x9e, 0x10, 0x40, 0x34, 0x5f, 0xd7, 0xfe, 0x2a, 0xcb, 0x18, 0x1f, 0xad, - 0x54, 0x6a, 0xc2, 0xb7, 0xae, 0xf5, 0xe7, 0xcc, 0x9e, 0xb0, 0xcc, 0x0f, 0x42, 0x6a, 0xd2, 0x17, 0xd9, 0xea, 0x6b, - 0xc3, 0x4b, 0xcb, 0xc3, 0x8b, 0xca, 0xeb, 0x07, 0x07, 0x43, 0x47, 0x00, 0xf5, 0x1b, 0x47, 0x86, 0x59, 0xac, 0x9a, - 0x67, 0x94, 0xde, 0xfd, 0x57, 0xa7, 0x83, 0xc1, 0x74, 0x44, 0x30, 0x1d, 0x2c, 0x1a, 0xc7, 0x13, 0xf6, 0xcb, 0xfb, - 0xb7, 0x32, 0x6d, 0x16, 0x48, 0x80, 0x86, 0x7c, 0x61, 0xa6, 0xc8, 0x3f, 0x24, 0xc8, 0x3b, 0x50, 0x82, 0x2b, 0x4d, - 0x2e, 0xa1, 0x24, 0xd7, 0xb5, 0x33, 0xea, 0x3b, 0x9b, 0x50, 0xaf, 0x07, 0x31, 0xb6, 0x4a, 0xf2, 0x93, 0x03, 0xaa, - 0x4d, 0xa7, 0x1d, 0x55, 0x02, 0x34, 0x24, 0x30, 0xc2, 0x02, 0x0b, 0x90, 0xe1, 0x73, 0xe0, 0x16, 0x17, 0x0a, 0x7b, - 0x81, 0x72, 0x76, 0xf7, 0xac, 0xcc, 0xaa, 0x60, 0x2b, 0xfd, 0xf4, 0x04, 0x73, 0x76, 0xc1, 0x79, 0x0d, 0x51, 0x3e, - 0x4e, 0x0e, 0xe8, 0x51, 0xab, 0xec, 0x88, 0x02, 0x88, 0xb8, 0xda, 0xf5, 0x38, 0x80, 0x07, 0x6d, 0x15, 0x48, 0x11, - 0x0f, 0xa5, 0x7e, 0xae, 0x6b, 0x0b, 0xce, 0x1a, 0xf1, 0x70, 0x42, 0x10, 0x6b, 0xc0, 0x81, 0xbd, 0xab, 0x6b, 0x0b, - 0xff, 0x0e, 0x47, 0x2e, 0xde, 0xf8, 0x77, 0x2d, 0x97, 0xbf, 0x2a, 0xf6, 0x5a, 0x5a, 0xde, 0x6b, 0x63, 0x3e, 0xb9, - 0xe0, 0x48, 0x20, 0x6f, 0xd6, 0x73, 0x54, 0xd0, 0x36, 0x4c, 0xee, 0x5c, 0x4c, 0xee, 0x64, 0xc3, 0xe4, 0x4e, 0xb6, - 0x4c, 0x6e, 0xc8, 0x27, 0x52, 0x93, 0xa8, 0x4b, 0xd0, 0x39, 0x4c, 0x22, 0x8f, 0x33, 0x1a, 0x3d, 0xbe, 0xcf, 0x10, - 0x4f, 0x56, 0x1a, 0x82, 0x71, 0xd4, 0x06, 0x5c, 0x35, 0xe1, 0x45, 0x41, 0x44, 0x7d, 0xe0, 0x72, 0xd7, 0x89, 0x71, - 0x43, 0x0e, 0xce, 0x56, 0x58, 0x1d, 0x2f, 0xac, 0x52, 0xca, 0x2f, 0xde, 0x9a, 0x6f, 0x18, 0xe9, 0x7c, 0xcb, 0x48, - 0xc7, 0xa5, 0xad, 0xcb, 0x87, 0x4d, 0x9b, 0x50, 0x1d, 0x14, 0xac, 0x41, 0x30, 0x18, 0xc5, 0x25, 0x53, 0x5e, 0x87, - 0x9b, 0x69, 0xac, 0xb2, 0xa2, 0x96, 0x7e, 0x9a, 0xde, 0xc6, 0x09, 0x68, 0x5c, 0x00, 0xcc, 0xc3, 0x96, 0xd4, 0x22, - 0x88, 0x78, 0x30, 0x97, 0x8d, 0x8b, 0xa9, 0x78, 0xaf, 0x2e, 0x29, 0xaf, 0xd3, 0xa1, 0x1a, 0x4b, 0x3f, 0xcb, 0x58, - 0x82, 0x48, 0xf7, 0x21, 0xea, 0xf7, 0xff, 0x93, 0x65, 0xd6, 0x40, 0x43, 0x42, 0x85, 0xaa, 0x23, 0x85, 0x5e, 0x02, - 0x6f, 0x95, 0x88, 0x83, 0x58, 0x09, 0x0c, 0x97, 0x48, 0xc4, 0xff, 0x84, 0xdb, 0xb5, 0x95, 0x28, 0xae, 0x4b, 0xee, - 0x91, 0x61, 0x2f, 0xfd, 0xc9, 0x07, 0x50, 0xec, 0xb5, 0x3c, 0x13, 0x8c, 0x74, 0xd5, 0x30, 0x70, 0x09, 0x31, 0x7b, - 0xe3, 0x82, 0x48, 0x22, 0x95, 0xe4, 0x26, 0x50, 0xe0, 0x3d, 0xe9, 0x5b, 0xd3, 0xab, 0xb5, 0x97, 0x1f, 0xcc, 0x02, - 0xa3, 0x46, 0x35, 0x81, 0xb4, 0x85, 0x83, 0x53, 0x79, 0xe7, 0x0a, 0x4d, 0xf7, 0xc8, 0x00, 0xc9, 0xef, 0x25, 0xe4, - 0x33, 0x75, 0xc4, 0x85, 0x76, 0x98, 0xc0, 0xa9, 0x75, 0xe9, 0x5c, 0xe5, 0x4f, 0x67, 0xf8, 0xcb, 0xbd, 0xca, 0x9f, - 0x8e, 0xf0, 0x97, 0x77, 0x85, 0x99, 0xeb, 0x1a, 0x2e, 0xf2, 0xca, 0x98, 0xf5, 0xd3, 0xd2, 0x7a, 0x22, 0xfb, 0xb3, - 0x07, 0x2c, 0x1b, 0x3e, 0xc1, 0x8f, 0x9f, 0xac, 0x53, 0xf0, 0xb8, 0x54, 0xc7, 0x10, 0xd9, 0x89, 0x91, 0x37, 0x96, - 0xcf, 0x36, 0x94, 0x8f, 0x8c, 0xff, 0xf2, 0xc1, 0x8f, 0xab, 0x24, 0x2e, 0xce, 0x94, 0xb2, 0x18, 0xe2, 0x7a, 0x14, - 0x44, 0x7e, 0x72, 0x7f, 0x4d, 0xd7, 0x8b, 0x96, 0xe0, 0xdd, 0xa5, 0x78, 0x85, 0xd8, 0xcb, 0xb2, 0xba, 0x2b, 0x53, - 0x04, 0xbc, 0xf7, 0xfc, 0xa0, 0x1f, 0xfc, 0x3d, 0x51, 0xd8, 0xb6, 0xd2, 0x05, 0x94, 0x4f, 0x48, 0xe9, 0x43, 0xd7, - 0x4f, 0xd6, 0x2d, 0x56, 0x07, 0x53, 0x19, 0x6d, 0x85, 0x2f, 0x84, 0xe9, 0xc1, 0xcb, 0xec, 0x62, 0x12, 0xf4, 0x50, - 0x9f, 0x35, 0x8a, 0xef, 0xac, 0x27, 0xeb, 0xec, 0x4c, 0x5f, 0xf8, 0xc9, 0x27, 0x36, 0xb1, 0xc6, 0x41, 0x32, 0x0e, - 0x99, 0xde, 0xd3, 0x47, 0xa1, 0x1f, 0x7d, 0xe2, 0x8f, 0x56, 0xbc, 0xca, 0x50, 0x43, 0xbd, 0xf3, 0xee, 0x2b, 0x70, - 0x42, 0x22, 0x3b, 0x64, 0x56, 0x1b, 0xb0, 0xa0, 0xbd, 0x94, 0x02, 0xaf, 0x82, 0x51, 0x2c, 0x6a, 0x99, 0x60, 0x60, - 0x09, 0x4a, 0x73, 0xf0, 0x58, 0x35, 0x75, 0x9c, 0x2f, 0xdd, 0x54, 0x87, 0x4a, 0xc2, 0x4a, 0x99, 0x72, 0xf1, 0x1a, - 0x21, 0xfc, 0xf1, 0xcf, 0x51, 0x32, 0xec, 0xfd, 0x3f, 0x27, 0xa1, 0x7c, 0xd9, 0x08, 0xa1, 0xd4, 0x22, 0x4f, 0x89, - 0x07, 0x7c, 0x9c, 0x33, 0x98, 0x9b, 0x3f, 0xad, 0x36, 0xf6, 0xd3, 0x74, 0xb5, 0x60, 0x13, 0xd2, 0x0c, 0x9e, 0x15, - 0x9d, 0x2a, 0xdf, 0x2c, 0xd4, 0x8e, 0xfd, 0xb6, 0xf2, 0x8e, 0x0f, 0x5f, 0x82, 0xc5, 0x02, 0x30, 0x94, 0xf1, 0x74, - 0xaa, 0x17, 0x77, 0xfc, 0x1d, 0xcd, 0xdc, 0xc3, 0xdf, 0x56, 0x6f, 0x5e, 0x3b, 0x6f, 0x64, 0xe3, 0x08, 0x18, 0x63, - 0xa1, 0x7e, 0xe5, 0x7c, 0xb1, 0xd2, 0x5f, 0x31, 0xa2, 0xa9, 0x1f, 0x6d, 0x1e, 0xce, 0x65, 0x69, 0x89, 0x2f, 0x19, - 0x9b, 0x00, 0xc3, 0x6d, 0xd6, 0x4a, 0xaf, 0x43, 0x76, 0xc3, 0xa4, 0x6a, 0xb7, 0xfe, 0xb1, 0x86, 0x16, 0x18, 0x7b, - 0x8e, 0xab, 0x8c, 0x39, 0x57, 0xa7, 0x0c, 0x69, 0x88, 0x63, 0xe0, 0x23, 0x57, 0xb7, 0x58, 0x65, 0x4b, 0x0d, 0x4d, - 0x5d, 0xe9, 0xc0, 0xc6, 0x9e, 0x9d, 0x6d, 0x28, 0xef, 0x61, 0xe2, 0xe9, 0xe6, 0xbe, 0x99, 0xae, 0xd1, 0x83, 0x58, - 0xdd, 0x1c, 0x4f, 0x21, 0xec, 0xbc, 0x56, 0x21, 0x0e, 0xd9, 0x84, 0xb1, 0x26, 0x21, 0x99, 0x4e, 0xd2, 0x17, 0x61, - 0xed, 0x88, 0x66, 0xbf, 0x42, 0x0e, 0xd5, 0x38, 0x37, 0x5a, 0x79, 0xe4, 0x23, 0x4c, 0xe8, 0x1a, 0xb1, 0x34, 0xdd, - 0x88, 0x30, 0x39, 0xe9, 0xa6, 0x5e, 0xd4, 0x2e, 0xe3, 0xa3, 0x28, 0x37, 0x1d, 0x13, 0x58, 0x02, 0x1c, 0x60, 0xf5, - 0x5b, 0x78, 0xbc, 0x5c, 0x2f, 0xb8, 0xbd, 0x4a, 0x32, 0x1b, 0xe9, 0xdc, 0x96, 0x60, 0xd3, 0xfb, 0x5b, 0x9d, 0x77, - 0xaa, 0x74, 0x4c, 0x37, 0x76, 0xad, 0x55, 0x22, 0xbd, 0x35, 0x71, 0x11, 0x02, 0x10, 0x7d, 0xaa, 0xd0, 0x57, 0x36, - 0x9d, 0xb2, 0x71, 0x96, 0x1a, 0x42, 0x78, 0x24, 0xa3, 0xc7, 0x82, 0xd7, 0xd0, 0xa3, 0x81, 0xfe, 0x13, 0xf8, 0xd0, - 0x8b, 0x20, 0x4b, 0xbc, 0x43, 0xe2, 0xce, 0xd4, 0x8c, 0x26, 0x82, 0x58, 0x46, 0x11, 0xff, 0x0a, 0x24, 0x07, 0x6f, - 0x28, 0xc7, 0xae, 0xf1, 0xf3, 0xa7, 0x58, 0x17, 0xb1, 0xb4, 0x6a, 0xd9, 0x4e, 0x8a, 0xb6, 0x6d, 0xdf, 0xb5, 0xfb, - 0xa6, 0xe3, 0x3a, 0xb9, 0x6e, 0x82, 0xef, 0xd6, 0xa7, 0x7d, 0x37, 0x3d, 0xb6, 0x6a, 0x43, 0xab, 0x55, 0xf4, 0x90, - 0x76, 0x9e, 0xfb, 0xc2, 0xd5, 0x4d, 0x32, 0x99, 0x53, 0x68, 0xdb, 0x38, 0xbe, 0x61, 0xc9, 0x17, 0x0f, 0xa5, 0x0c, - 0x7c, 0xbf, 0xfe, 0x1c, 0xb9, 0x0e, 0x10, 0xe1, 0x2c, 0x5e, 0x3e, 0x60, 0x08, 0x6d, 0xdd, 0xd4, 0xc7, 0x61, 0x9c, - 0x32, 0x75, 0x0c, 0x24, 0x04, 0xf9, 0xc2, 0x41, 0xfc, 0xfc, 0xfe, 0xf5, 0x87, 0x0f, 0xba, 0x89, 0x99, 0x40, 0x53, - 0x15, 0x3a, 0x5f, 0x50, 0x3b, 0xa8, 0x7f, 0xe3, 0xba, 0xa3, 0x13, 0x86, 0x2e, 0xb5, 0xe5, 0x35, 0x47, 0x65, 0xb5, - 0x25, 0xc7, 0x4f, 0x1e, 0xfe, 0x65, 0xba, 0x89, 0xee, 0x35, 0xae, 0x06, 0xda, 0xb0, 0xfd, 0x78, 0x2b, 0x95, 0x2c, - 0x82, 0xe8, 0xba, 0xa1, 0xd4, 0xbf, 0x6b, 0x28, 0x85, 0xab, 0x5c, 0x8d, 0x56, 0xad, 0xe2, 0x85, 0xc2, 0x1a, 0x40, - 0x22, 0xe7, 0x5d, 0xe8, 0x52, 0xee, 0x53, 0x5f, 0xd0, 0x69, 0x1e, 0xc9, 0xbd, 0xda, 0xeb, 0x86, 0x62, 0x7e, 0x09, - 0x92, 0xb8, 0x1d, 0x87, 0x60, 0xf0, 0xc7, 0x54, 0xad, 0x5c, 0x99, 0x6d, 0x94, 0xe6, 0xba, 0x0a, 0x10, 0x62, 0x6f, - 0xaf, 0x33, 0xb6, 0x58, 0xb2, 0xc4, 0xcf, 0x56, 0x09, 0xbb, 0x0e, 0xe3, 0xdb, 0x47, 0x85, 0x39, 0xfd, 0x8e, 0xca, - 0xf3, 0x60, 0x36, 0x97, 0xb5, 0xcf, 0x5a, 0x6c, 0x20, 0x27, 0x70, 0xeb, 0x07, 0xf2, 0xff, 0xfc, 0xdb, 0xb6, 0xff, - 0xf3, 0xef, 0x9d, 0x55, 0x01, 0x7c, 0x3e, 0x34, 0xb3, 0xc1, 0x1e, 0xeb, 0xa2, 0xf9, 0x4b, 0x65, 0x9c, 0x37, 0xd7, - 0xa9, 0x4d, 0x02, 0xbc, 0xaf, 0x4d, 0x41, 0xad, 0xb0, 0xbc, 0x6e, 0x1e, 0xd4, 0x31, 0x18, 0xd7, 0xce, 0x9e, 0x41, - 0xa5, 0x2f, 0xea, 0xda, 0xd0, 0xe8, 0xed, 0x35, 0x23, 0x7f, 0x1c, 0xc3, 0xbb, 0xc6, 0xf0, 0x85, 0xdd, 0xe7, 0x72, - 0xc9, 0x97, 0xc3, 0xa1, 0xcc, 0x2d, 0xa7, 0x36, 0x05, 0x13, 0xff, 0xb3, 0x5a, 0x09, 0x3f, 0x3c, 0x7b, 0x8e, 0x41, - 0xbe, 0xf7, 0x83, 0x97, 0x43, 0x34, 0x46, 0x3b, 0x19, 0x25, 0x05, 0xb3, 0xb2, 0x91, 0xb4, 0x91, 0x31, 0x79, 0x0d, - 0x68, 0x8d, 0xae, 0x41, 0x29, 0x26, 0x1c, 0xcb, 0x87, 0x86, 0xf9, 0x72, 0xc8, 0x05, 0x4b, 0xdc, 0xfe, 0xb5, 0x57, - 0x5d, 0xda, 0x5c, 0x2c, 0x5b, 0x42, 0xba, 0xa9, 0x91, 0xfe, 0x07, 0x2b, 0xb3, 0x42, 0x8e, 0x87, 0x02, 0x7e, 0x90, - 0x28, 0x0c, 0x73, 0xcc, 0x77, 0xf2, 0x6e, 0x93, 0x8d, 0xd8, 0xcf, 0xbb, 0x6d, 0xc4, 0x2e, 0xf6, 0xb2, 0x11, 0xfb, - 0xf9, 0xab, 0xdb, 0x88, 0xbd, 0x53, 0x6d, 0xc4, 0x60, 0x12, 0x5f, 0xb3, 0xbd, 0x0c, 0xb7, 0x84, 0xd5, 0x46, 0x7c, - 0x9b, 0x0e, 0x5c, 0xce, 0xd2, 0xa6, 0xe3, 0x39, 0x03, 0x19, 0x01, 0x9f, 0x95, 0x30, 0x9e, 0x81, 0x11, 0xd7, 0x9f, - 0x6f, 0x6e, 0x15, 0xc6, 0x33, 0xd5, 0xd8, 0x2a, 0xe2, 0x11, 0x5f, 0x8b, 0x28, 0x4e, 0x64, 0xe0, 0xe4, 0x98, 0x22, - 0xe6, 0x93, 0x75, 0x68, 0x28, 0x59, 0xad, 0xa5, 0xf5, 0x9a, 0x27, 0x4c, 0xa0, 0x7a, 0x68, 0x3d, 0x25, 0x1b, 0x7a, - 0xcf, 0x45, 0x6c, 0x0b, 0x15, 0x82, 0xb4, 0x12, 0xa6, 0x38, 0x11, 0x6b, 0xfd, 0xb7, 0x3b, 0xf7, 0xfb, 0x4b, 0xb7, - 0xdf, 0x76, 0xc1, 0x39, 0x1b, 0x6e, 0x98, 0x58, 0xe0, 0xf4, 0xdb, 0x6d, 0x28, 0xb8, 0x55, 0x0a, 0x3c, 0x28, 0x08, - 0x94, 0x82, 0x0e, 0x14, 0x8c, 0x95, 0x82, 0x23, 0x28, 0x98, 0x28, 0x05, 0xc7, 0x50, 0x70, 0xa3, 0xe7, 0x97, 0x91, - 0xec, 0xee, 0xb1, 0x71, 0x65, 0xd2, 0xa5, 0x42, 0x94, 0x1d, 0x9b, 0x2e, 0x58, 0x4d, 0xf9, 0xb3, 0x5e, 0x6c, 0x92, - 0x74, 0xb1, 0x97, 0x98, 0xb7, 0x73, 0x46, 0x81, 0xa2, 0x5f, 0xe1, 0x99, 0x63, 0x67, 0x31, 0xd8, 0x4d, 0x8b, 0x00, - 0x0c, 0x02, 0x0f, 0x9a, 0x6e, 0x80, 0xc0, 0xa8, 0x2f, 0x67, 0x4e, 0x04, 0xb1, 0x50, 0xe6, 0xb2, 0x78, 0x47, 0x9f, - 0xb3, 0xe4, 0x12, 0x28, 0x2c, 0x4e, 0x5a, 0xaa, 0x54, 0xf2, 0x6b, 0xd8, 0x1d, 0xbc, 0x62, 0xa3, 0xd5, 0x4c, 0x3b, - 0x8f, 0x67, 0x3b, 0x4d, 0x08, 0xd4, 0x57, 0xd0, 0x4b, 0x9d, 0xd4, 0x2f, 0x96, 0x58, 0x96, 0xfc, 0x5b, 0xf4, 0x98, - 0x97, 0xeb, 0x67, 0xd0, 0x37, 0x2d, 0x23, 0x03, 0x16, 0xf8, 0x0e, 0xe0, 0x48, 0xd1, 0xe1, 0x9f, 0x03, 0x9e, 0x95, - 0xe7, 0x0b, 0x5f, 0xe9, 0xcf, 0xe9, 0x8f, 0x2c, 0x4d, 0xfd, 0x99, 0xa8, 0x5f, 0xef, 0x27, 0x18, 0xed, 0xc8, 0xfb, - 0x17, 0x22, 0x10, 0x24, 0x79, 0x41, 0xcd, 0x36, 0x23, 0x89, 0x6f, 0x35, 0xb0, 0xfe, 0x81, 0x05, 0x55, 0xd8, 0x29, - 0x04, 0x36, 0x4c, 0x61, 0xd9, 0xa2, 0x00, 0x36, 0xff, 0x0d, 0x0b, 0xab, 0x85, 0x99, 0x3f, 0xab, 0x16, 0xd1, 0x3a, - 0xc8, 0xd5, 0xbe, 0x49, 0x85, 0x7e, 0xa9, 0xf0, 0x4b, 0x34, 0xd4, 0x61, 0x3c, 0xfb, 0x53, 0xd5, 0xd3, 0x5b, 0xcc, - 0x0a, 0x3e, 0x44, 0x66, 0x90, 0x0d, 0x6d, 0xc4, 0xb1, 0x66, 0x03, 0x0a, 0x7b, 0x51, 0x36, 0xb7, 0xd0, 0xb5, 0xac, - 0xe5, 0x45, 0x86, 0x69, 0xe3, 0xdc, 0xae, 0xab, 0x0e, 0xb5, 0xbd, 0x64, 0x36, 0xf2, 0x5b, 0xae, 0x77, 0x6c, 0x8a, - 0x3f, 0xb6, 0xd3, 0x31, 0x72, 0x84, 0xa0, 0x4d, 0x82, 0x9b, 0xf5, 0x34, 0x8e, 0x32, 0x6b, 0xea, 0x2f, 0x82, 0xf0, - 0xbe, 0xb7, 0x88, 0xa3, 0x38, 0x5d, 0xfa, 0x63, 0xd6, 0x2f, 0x2e, 0xd4, 0x7d, 0x0c, 0xd5, 0xc0, 0xbd, 0x05, 0x5d, - 0xdb, 0x4b, 0xd8, 0x82, 0x5a, 0xcb, 0x48, 0x0c, 0xd3, 0x90, 0xdd, 0xe5, 0xfc, 0xf3, 0xa5, 0xca, 0x54, 0x15, 0x97, - 0x1c, 0xb5, 0x00, 0x8e, 0x94, 0x87, 0x79, 0x80, 0xe0, 0x46, 0xfd, 0xa5, 0x3f, 0xc1, 0xc8, 0x84, 0xb6, 0xd7, 0x49, - 0xd8, 0x42, 0xb3, 0x3b, 0x1b, 0x81, 0x27, 0xf1, 0xed, 0x29, 0xf4, 0x16, 0x1b, 0x5b, 0x29, 0x0b, 0xa7, 0xf8, 0xc6, - 0x42, 0xcf, 0x12, 0x01, 0xc7, 0xc2, 0x8b, 0x38, 0x40, 0x63, 0x8b, 0x3e, 0xbc, 0xee, 0x79, 0x9a, 0xd3, 0x5f, 0x04, - 0x91, 0x45, 0xc3, 0x39, 0x76, 0x96, 0x0a, 0x2c, 0x15, 0x7f, 0xc6, 0x1a, 0xab, 0xbb, 0x9a, 0xd3, 0x87, 0xcb, 0xda, - 0x34, 0x8c, 0x6f, 0x7b, 0xf3, 0x60, 0x32, 0x61, 0x51, 0x1f, 0xfb, 0x2c, 0x0b, 0x59, 0x18, 0x06, 0xcb, 0x34, 0x48, - 0xfb, 0x0b, 0xff, 0x8e, 0x43, 0x3d, 0xdc, 0x04, 0xb5, 0xcd, 0xa1, 0xb6, 0xf7, 0x86, 0xaa, 0x80, 0x01, 0x2f, 0x16, - 0x82, 0xc3, 0xbb, 0xd6, 0xd1, 0x9c, 0xca, 0x38, 0xf7, 0x86, 0xba, 0x4c, 0xd8, 0x7a, 0xe1, 0x27, 0xb3, 0x20, 0xea, - 0x39, 0xb9, 0x7d, 0xb3, 0xa6, 0x85, 0xf1, 0xb8, 0xdb, 0xed, 0xe6, 0xf6, 0x44, 0x3c, 0x39, 0x93, 0x49, 0x6e, 0x8f, - 0xc5, 0xd3, 0x74, 0xea, 0x38, 0xd3, 0x69, 0x6e, 0x07, 0xa2, 0xa0, 0xed, 0x8d, 0x27, 0x6d, 0x2f, 0xb7, 0x6f, 0x95, - 0x1a, 0xb9, 0xcd, 0xf8, 0x53, 0xc2, 0x26, 0x7d, 0x5c, 0x48, 0x64, 0x56, 0xda, 0x3b, 0x76, 0x9c, 0x1c, 0x29, 0xc0, - 0x65, 0x89, 0x36, 0xa1, 0xac, 0xe7, 0x6a, 0xbd, 0x77, 0x4d, 0xad, 0xf8, 0xdc, 0x78, 0xdc, 0x58, 0x6f, 0xe2, 0x27, - 0x9f, 0xae, 0x34, 0x65, 0x14, 0xbe, 0x4f, 0xd5, 0xd6, 0x02, 0x0d, 0xd6, 0x5d, 0x0f, 0x42, 0x76, 0xf5, 0x47, 0x71, - 0x02, 0x7b, 0x36, 0xf1, 0x27, 0xc1, 0x2a, 0xed, 0xb9, 0xde, 0xf2, 0x4e, 0x14, 0xf1, 0xb5, 0x5e, 0x14, 0xe0, 0xde, - 0xeb, 0xa5, 0x71, 0x18, 0x4c, 0x44, 0xd1, 0xa6, 0xbd, 0xe4, 0x7a, 0x46, 0x1f, 0x1d, 0xd6, 0x03, 0x0c, 0xbb, 0xe0, - 0x87, 0xa1, 0x66, 0xb7, 0x53, 0x8d, 0xf9, 0x29, 0xca, 0x97, 0x35, 0x27, 0x25, 0xbc, 0xa0, 0x73, 0xba, 0x7b, 0xb8, - 0xbc, 0x93, 0x6b, 0xde, 0x3d, 0x5a, 0xde, 0xe5, 0x7f, 0x5d, 0xb0, 0x49, 0xe0, 0x6b, 0xad, 0x62, 0x35, 0xb9, 0x0e, - 0xc8, 0xa0, 0x8d, 0xf5, 0x86, 0x65, 0x2a, 0xb6, 0x05, 0x84, 0x36, 0x7c, 0x14, 0x2c, 0x96, 0x71, 0x92, 0xf9, 0x51, - 0x96, 0xe7, 0xc3, 0xab, 0x3c, 0xef, 0x5f, 0x04, 0xad, 0xcb, 0x7f, 0xb4, 0xe8, 0x9c, 0x26, 0x9d, 0x4d, 0x6e, 0x5c, - 0x99, 0xaf, 0x99, 0x6a, 0x33, 0x02, 0xc7, 0x18, 0xda, 0x8b, 0xa8, 0x95, 0xe9, 0x94, 0xac, 0x57, 0x26, 0x24, 0xcb, - 0xea, 0x64, 0x83, 0x52, 0xae, 0x82, 0x27, 0x10, 0x54, 0x78, 0xcd, 0x06, 0x17, 0x8a, 0xfd, 0x09, 0x30, 0x2b, 0x58, - 0x99, 0xfc, 0x0a, 0x9e, 0x6c, 0xe2, 0x19, 0xbf, 0xdb, 0xcd, 0x33, 0xfe, 0x9a, 0xed, 0xc3, 0x33, 0x7e, 0xf7, 0xd5, - 0x79, 0xc6, 0x27, 0x75, 0xbf, 0x82, 0xb7, 0xf1, 0x40, 0x97, 0x1a, 0x06, 0x38, 0x9a, 0x12, 0x8a, 0xd8, 0xf3, 0xf6, - 0x0f, 0xbb, 0x01, 0x08, 0x68, 0x94, 0x83, 0x8e, 0x4e, 0x6e, 0x90, 0xc7, 0xbe, 0x8b, 0x06, 0x7f, 0x4f, 0xd4, 0xe7, - 0xe9, 0x74, 0xf0, 0x2a, 0x56, 0x0a, 0xe4, 0x13, 0x37, 0xbe, 0x28, 0x45, 0x57, 0xa0, 0x37, 0xc2, 0x0a, 0x13, 0xf3, - 0x4f, 0x80, 0x73, 0x36, 0x59, 0x1d, 0x4f, 0xa4, 0xf5, 0x59, 0xbf, 0xdc, 0x85, 0x96, 0x34, 0xf9, 0x14, 0x2e, 0x38, - 0x35, 0x51, 0xe2, 0x8c, 0x65, 0xdc, 0x67, 0xf6, 0xfb, 0xfb, 0xb7, 0x93, 0xd6, 0xdb, 0xd8, 0xc8, 0x83, 0xf4, 0x5d, - 0xd5, 0x01, 0x86, 0xeb, 0x7e, 0x06, 0xea, 0x70, 0x72, 0x6e, 0x41, 0xa6, 0x26, 0x98, 0x86, 0xd7, 0xd4, 0xfc, 0xac, - 0x34, 0xd2, 0x9e, 0xda, 0x90, 0x27, 0xba, 0xaa, 0x1d, 0xc6, 0xdc, 0xfb, 0x60, 0xcd, 0x39, 0x40, 0xcc, 0xdd, 0x85, - 0x7e, 0xc3, 0x13, 0x6a, 0x1e, 0x4c, 0xf2, 0xdc, 0xe8, 0x0b, 0x44, 0x28, 0x07, 0x2d, 0xdb, 0xc5, 0xc4, 0xa5, 0xb7, - 0xd2, 0xa6, 0x81, 0x6b, 0x08, 0x49, 0xfd, 0xf7, 0x16, 0x14, 0xea, 0x5c, 0x59, 0xc8, 0x71, 0xa6, 0x6b, 0x84, 0x3e, - 0x32, 0xb4, 0x50, 0x06, 0x04, 0x1a, 0x60, 0x89, 0x7f, 0xf1, 0x4a, 0x14, 0xd4, 0x6d, 0x38, 0x09, 0x39, 0x68, 0x11, - 0x00, 0x5e, 0xfe, 0x42, 0xae, 0x4d, 0x64, 0x87, 0xd7, 0xc1, 0x87, 0x5c, 0x97, 0xbc, 0x1f, 0x2e, 0xbf, 0xd3, 0x93, - 0x03, 0x68, 0x70, 0x5a, 0x31, 0x1c, 0xd8, 0x61, 0xa1, 0x08, 0xac, 0x44, 0x7a, 0x6b, 0xda, 0xe9, 0xad, 0xf6, 0x6c, - 0x2d, 0x22, 0x64, 0x64, 0xfe, 0xd2, 0x82, 0x2b, 0x3e, 0xd2, 0x5e, 0x4e, 0xf1, 0x94, 0x60, 0x1c, 0xfd, 0x55, 0x0a, - 0xb4, 0x11, 0x2f, 0xaa, 0x48, 0x7f, 0xfa, 0xe3, 0x55, 0x92, 0xc6, 0x49, 0x6f, 0x19, 0x07, 0x51, 0xc6, 0x92, 0x1c, - 0x51, 0x75, 0x89, 0xf8, 0x11, 0xe8, 0xb9, 0x5a, 0xc7, 0x4b, 0x7f, 0x1c, 0x64, 0xf7, 0x3d, 0x87, 0xb3, 0x14, 0x4e, - 0x9f, 0x73, 0x07, 0x4e, 0x63, 0xfd, 0x1e, 0xc7, 0xe6, 0x73, 0x64, 0xfc, 0x92, 0x3a, 0x3b, 0xa3, 0x2e, 0xf3, 0xbe, - 0xf2, 0x96, 0x62, 0x84, 0x00, 0xfb, 0xe1, 0x27, 0xd6, 0x0c, 0xa8, 0x3c, 0x4c, 0xb5, 0x33, 0x61, 0x33, 0x13, 0xa9, - 0x36, 0xc8, 0xe5, 0xc5, 0x1f, 0xbb, 0x63, 0x68, 0x4e, 0x73, 0x31, 0x70, 0x3c, 0xc6, 0x3e, 0x3d, 0xeb, 0xf9, 0x90, - 0x51, 0xcb, 0xdc, 0xa7, 0xe6, 0x88, 0x4d, 0xe3, 0x84, 0x51, 0x3c, 0x59, 0xb7, 0xbb, 0xbc, 0xdb, 0x1f, 0xfc, 0xf6, - 0xe1, 0x37, 0xc3, 0x89, 0xe2, 0xac, 0x25, 0x80, 0x19, 0x3b, 0xa0, 0xd5, 0xcf, 0x33, 0x60, 0x0d, 0x09, 0xf3, 0x63, - 0x0a, 0xdd, 0xd5, 0xd3, 0xf5, 0x7e, 0x63, 0xd8, 0xae, 0x65, 0xcc, 0xcf, 0xbc, 0x84, 0x85, 0x7e, 0x16, 0xdc, 0x08, - 0x9e, 0xb1, 0x7d, 0xb4, 0xbc, 0x13, 0x73, 0x8c, 0x07, 0xde, 0x03, 0x26, 0xa9, 0xd2, 0x15, 0x31, 0x49, 0xd5, 0x62, - 0x9c, 0xa4, 0x7e, 0x6d, 0x34, 0x22, 0x92, 0x45, 0xe5, 0xa4, 0xef, 0x2c, 0xef, 0xd4, 0x23, 0xba, 0x68, 0x26, 0x4f, - 0xea, 0x6a, 0x08, 0xb2, 0x45, 0x30, 0x99, 0x84, 0x2c, 0x2f, 0x4d, 0x74, 0x79, 0x2e, 0x15, 0xe4, 0x48, 0x3c, 0xf8, - 0xa3, 0x34, 0x0e, 0x57, 0x19, 0x6b, 0x46, 0x17, 0x21, 0xc7, 0x73, 0x0a, 0xe4, 0xe0, 0xef, 0x72, 0x5f, 0x3b, 0xc0, - 0x6e, 0xc3, 0x32, 0x71, 0xfa, 0x10, 0x71, 0xd8, 0x6a, 0x97, 0xbb, 0x0e, 0xaf, 0x64, 0xa7, 0xcd, 0x86, 0x81, 0x98, - 0x70, 0x2c, 0x11, 0xf5, 0xd6, 0x6c, 0x97, 0x97, 0xc9, 0xa8, 0xab, 0xb2, 0x28, 0x2f, 0x0f, 0xe6, 0xcf, 0xd9, 0x63, - 0x2f, 0x9a, 0xf7, 0xd8, 0x0b, 0xb1, 0xc7, 0xb6, 0xaf, 0xcc, 0xc7, 0x53, 0x17, 0xfe, 0xeb, 0x17, 0x03, 0xea, 0x39, - 0x5a, 0x7b, 0x79, 0xa7, 0xb9, 0xcb, 0x3b, 0xcd, 0xf2, 0x96, 0x77, 0x1a, 0x82, 0x46, 0x7b, 0x10, 0xd3, 0xf6, 0x0c, - 0xd3, 0xd1, 0xa0, 0x10, 0xfe, 0x38, 0xa5, 0x57, 0xee, 0x21, 0xbc, 0x83, 0x56, 0x9d, 0xfa, 0x3b, 0x6f, 0xfb, 0x56, - 0xa7, 0xbd, 0x24, 0x88, 0xb6, 0x61, 0x67, 0xfe, 0x68, 0xc4, 0x26, 0xbd, 0x69, 0x3c, 0x5e, 0xa5, 0xff, 0xe2, 0xfd, - 0xe7, 0x48, 0xdc, 0x4a, 0x08, 0x2a, 0x70, 0x44, 0x53, 0x50, 0x94, 0xdc, 0x30, 0x01, 0x61, 0x2d, 0xe7, 0xa9, 0x47, - 0xe1, 0x91, 0x3d, 0xfb, 0xb0, 0x61, 0x91, 0x37, 0x23, 0xfa, 0x4f, 0x9b, 0xa5, 0xcd, 0x24, 0xe6, 0x0b, 0xd0, 0xb2, - 0x15, 0x1d, 0x0f, 0xc7, 0x06, 0x9f, 0x4d, 0xa7, 0xdb, 0xdc, 0xdd, 0x4b, 0xf1, 0xa5, 0x2b, 0x71, 0xa8, 0xf0, 0x73, - 0x8b, 0x3b, 0xa6, 0x6c, 0x87, 0xba, 0x69, 0x8d, 0xd4, 0xa0, 0x6e, 0x39, 0x10, 0x8a, 0xba, 0x7b, 0x52, 0xf9, 0xc7, - 0x2f, 0x0e, 0xe1, 0x3f, 0xe2, 0xea, 0x7f, 0xcd, 0x9a, 0x18, 0xf5, 0xb7, 0x65, 0x4b, 0x70, 0x62, 0x95, 0x90, 0x11, - 0xdf, 0xbf, 0xfe, 0x74, 0xfa, 0xb0, 0x06, 0x7b, 0xd7, 0x26, 0x53, 0xaa, 0x6a, 0xed, 0xef, 0xe3, 0x18, 0x52, 0x77, - 0xd6, 0xab, 0x0b, 0xf4, 0x90, 0xb1, 0x7b, 0x36, 0x80, 0x46, 0xe2, 0x1e, 0x41, 0x5a, 0x7c, 0x1d, 0xdb, 0xd0, 0x55, - 0xe2, 0xf5, 0xa6, 0xab, 0xc4, 0xab, 0xdd, 0x57, 0x89, 0x1f, 0xf6, 0xba, 0x4a, 0xbc, 0xfa, 0xea, 0x57, 0x89, 0xd7, - 0xf5, 0xab, 0xc4, 0x45, 0x2c, 0xec, 0x67, 0xcd, 0xb7, 0x2b, 0xfe, 0xf3, 0x23, 0x29, 0xe5, 0xce, 0xe3, 0x41, 0xc7, - 0xa1, 0x90, 0xc7, 0x17, 0x7f, 0xf8, 0x62, 0x81, 0x0b, 0xf1, 0x3d, 0x9a, 0x93, 0x15, 0x57, 0x0b, 0x4e, 0xd9, 0xf1, - 0x3b, 0x4a, 0x71, 0x18, 0x47, 0xb3, 0x9f, 0x41, 0x29, 0x0b, 0xe2, 0xc0, 0x44, 0x79, 0x11, 0xa4, 0x3f, 0xc7, 0xcb, - 0xd5, 0xf2, 0x2d, 0xc0, 0xfa, 0x18, 0xa4, 0xc1, 0x28, 0x64, 0xd2, 0x13, 0x99, 0xcc, 0xdf, 0xb8, 0x4c, 0x1c, 0x2c, - 0x4e, 0xc5, 0x4f, 0xff, 0x4e, 0xfc, 0x44, 0x9b, 0x54, 0xfe, 0x9b, 0xec, 0xea, 0xf4, 0xe6, 0x8b, 0x88, 0x50, 0x02, - 0x2a, 0x9d, 0x7e, 0xf8, 0x65, 0xe4, 0x22, 0x36, 0x1a, 0x46, 0x29, 0xec, 0x1d, 0x36, 0xc2, 0x61, 0xb5, 0x4b, 0xcd, - 0xca, 0x30, 0x65, 0x08, 0xae, 0xba, 0x18, 0x7e, 0x11, 0xaf, 0x52, 0x36, 0x89, 0x6f, 0x23, 0xdd, 0x8c, 0xa4, 0x93, - 0x01, 0x68, 0x38, 0x65, 0x1b, 0x4c, 0x1e, 0xf9, 0x01, 0x19, 0xe5, 0x38, 0x69, 0xe9, 0x90, 0xbb, 0x74, 0xb5, 0xb4, - 0x48, 0xd5, 0x6c, 0xe1, 0x10, 0x75, 0x99, 0xe5, 0xe8, 0x51, 0xab, 0x15, 0x0f, 0x1e, 0xd6, 0x52, 0x98, 0x6a, 0xc4, - 0x36, 0x97, 0x0a, 0xa7, 0xad, 0x48, 0x08, 0x17, 0x45, 0x1c, 0x8c, 0x86, 0x89, 0xe3, 0x6f, 0xc8, 0x75, 0xb5, 0x78, - 0x0b, 0x51, 0x44, 0xf2, 0x15, 0x9f, 0x0f, 0x1e, 0x15, 0x82, 0x1e, 0x5f, 0x2a, 0x68, 0x7c, 0x77, 0xc3, 0x92, 0xd0, - 0xbf, 0x6f, 0x19, 0x79, 0x1c, 0xfd, 0x08, 0x08, 0x78, 0x15, 0xdf, 0x46, 0x6a, 0x05, 0x4c, 0xd6, 0xd2, 0xb0, 0x96, - 0x1a, 0xe3, 0x97, 0x80, 0xe3, 0x8a, 0xd2, 0x03, 0x48, 0x93, 0x3b, 0x63, 0x7f, 0x37, 0xe9, 0xdf, 0x7f, 0x18, 0xb9, - 0x79, 0x1e, 0xcb, 0x0f, 0xfd, 0xb2, 0xdc, 0xe3, 0x33, 0x4f, 0x9f, 0x3e, 0xda, 0x3c, 0xec, 0x72, 0x7a, 0xf6, 0x86, - 0xd6, 0xc6, 0xc6, 0x5d, 0x00, 0xbd, 0xb8, 0x88, 0x57, 0xe3, 0x39, 0x1a, 0xba, 0x7e, 0xbd, 0xf1, 0x66, 0x00, 0x13, - 0xb3, 0x94, 0xca, 0xa1, 0x57, 0x8a, 0x0a, 0x2c, 0xe0, 0xf7, 0x5f, 0x43, 0x00, 0xce, 0xff, 0x21, 0x1a, 0xea, 0xab, - 0x86, 0xdf, 0xe2, 0x83, 0x87, 0x2d, 0xde, 0x3e, 0x24, 0xd3, 0xe4, 0xa1, 0x2d, 0x84, 0x72, 0xad, 0x99, 0xc8, 0xe4, - 0x55, 0xa4, 0xa9, 0x61, 0xe4, 0x36, 0x45, 0xc8, 0x13, 0x5f, 0x61, 0x36, 0x5d, 0xd3, 0xb9, 0xa3, 0x81, 0xc9, 0x38, - 0xb5, 0xaa, 0x10, 0x19, 0x6e, 0xf2, 0xc0, 0x90, 0x7c, 0x55, 0xdf, 0x2d, 0x82, 0xc8, 0xc4, 0x28, 0xf0, 0xf5, 0x37, - 0xfe, 0x1d, 0xc4, 0x41, 0x06, 0xe2, 0x56, 0x7d, 0x05, 0x85, 0xa6, 0xea, 0x37, 0x07, 0xa9, 0x9e, 0xf4, 0x46, 0x4c, - 0x08, 0x2d, 0xde, 0xf0, 0x1b, 0x4d, 0xd3, 0x34, 0x79, 0x8d, 0xd0, 0xe4, 0x3d, 0x02, 0xcb, 0xf1, 0x3a, 0x00, 0xda, - 0x92, 0x7c, 0x79, 0x47, 0x25, 0x70, 0x33, 0x40, 0x9d, 0xac, 0x28, 0xe0, 0xa1, 0xfe, 0x3a, 0x8e, 0x28, 0x10, 0x17, - 0x7a, 0x08, 0xd3, 0xe6, 0x27, 0x10, 0x11, 0xb8, 0xa7, 0xe1, 0x85, 0x1d, 0xdf, 0x72, 0x49, 0xb0, 0xe6, 0xd0, 0xe3, - 0xb0, 0xcf, 0x9a, 0x63, 0xc2, 0x45, 0x0a, 0x15, 0x04, 0xad, 0x43, 0x25, 0xc4, 0xb3, 0xc9, 0x1a, 0x68, 0x23, 0xde, - 0x8b, 0xee, 0xb2, 0x05, 0x8b, 0x56, 0x3a, 0xe6, 0x84, 0xc2, 0x18, 0x7d, 0x50, 0xe7, 0x15, 0x31, 0x5b, 0x40, 0x6d, - 0x9a, 0x5b, 0xce, 0xe9, 0x2c, 0x4c, 0x39, 0x49, 0xf5, 0xcd, 0x31, 0x57, 0x6c, 0xa6, 0x9c, 0xb6, 0x55, 0x4f, 0x08, - 0x3e, 0xa5, 0x71, 0xd5, 0x91, 0x8b, 0x2c, 0xa1, 0x01, 0x06, 0x45, 0xc7, 0xe0, 0xe2, 0x22, 0x81, 0xf6, 0x96, 0x5f, - 0x9d, 0x34, 0xa9, 0x91, 0xf1, 0x2b, 0x82, 0xa2, 0xc4, 0xa8, 0x83, 0xe1, 0xfd, 0x84, 0xc0, 0x44, 0x1b, 0xe1, 0x8c, - 0x6b, 0x70, 0x36, 0x0c, 0xfa, 0x13, 0xbb, 0xa7, 0x83, 0x84, 0x50, 0xf5, 0x89, 0xdd, 0x83, 0xed, 0xdf, 0x6b, 0x90, - 0xa6, 0xe8, 0x5b, 0xc8, 0xb5, 0x09, 0xa1, 0xfe, 0xc7, 0x10, 0xac, 0x6a, 0xcb, 0x06, 0x72, 0xf2, 0x2d, 0x54, 0x1c, - 0x51, 0x0c, 0x59, 0x9d, 0xc5, 0x26, 0xe6, 0x26, 0xfe, 0xad, 0x46, 0x1c, 0x5b, 0x0d, 0x5b, 0xc3, 0x78, 0xe6, 0x3a, - 0xce, 0x41, 0xad, 0x3e, 0x08, 0xb2, 0x9b, 0x6a, 0x1b, 0x66, 0x36, 0x70, 0x1d, 0x2b, 0x78, 0x66, 0x7b, 0xfd, 0xda, - 0x19, 0xad, 0xc4, 0x92, 0x1c, 0xa2, 0xf8, 0xeb, 0xf4, 0xc9, 0xba, 0x55, 0xdb, 0x90, 0x46, 0xd5, 0x64, 0x1e, 0xfb, - 0x96, 0x73, 0xf9, 0xd7, 0xb0, 0x7e, 0xf4, 0x53, 0x24, 0x4b, 0xca, 0x6b, 0x32, 0x84, 0x68, 0xc8, 0x2d, 0xd8, 0x46, - 0x7f, 0xd1, 0x9e, 0x6b, 0x2d, 0xda, 0x3e, 0x86, 0x31, 0x94, 0xe9, 0xb2, 0x85, 0x4f, 0x99, 0x0a, 0xa0, 0xf2, 0xc5, - 0xb4, 0x4a, 0xe1, 0x78, 0xdc, 0x55, 0x56, 0x68, 0xf4, 0xb6, 0x72, 0x0b, 0x08, 0x7f, 0xc3, 0xf1, 0x69, 0x8f, 0x20, - 0x2e, 0x01, 0xd4, 0x80, 0xd8, 0xe9, 0x3b, 0x01, 0xae, 0x96, 0x65, 0x70, 0xe5, 0x43, 0x72, 0x7f, 0x60, 0x78, 0xe8, - 0xa0, 0x0e, 0x4d, 0xc2, 0x6b, 0x3e, 0xee, 0x1e, 0x08, 0x92, 0x45, 0x93, 0x32, 0xc0, 0xca, 0xf9, 0xb5, 0x3f, 0xb8, - 0x12, 0x45, 0x81, 0xa4, 0x02, 0x71, 0x03, 0x45, 0xc9, 0xe3, 0x08, 0x17, 0x3f, 0x6d, 0xb7, 0x60, 0x2f, 0x2e, 0x06, - 0x1b, 0x50, 0x44, 0x30, 0xd9, 0x4c, 0x11, 0x8a, 0x43, 0xe4, 0x6a, 0x74, 0x0b, 0x6e, 0x09, 0x46, 0x74, 0xe3, 0x4a, - 0xcc, 0x84, 0x4d, 0x61, 0xd1, 0x26, 0xe0, 0xb1, 0x28, 0xf7, 0x95, 0x5a, 0x07, 0xbb, 0xa5, 0xd6, 0xd9, 0x2e, 0xa9, - 0x35, 0xb9, 0x53, 0xdd, 0x26, 0xfe, 0x52, 0xf1, 0xc8, 0x13, 0xcc, 0xb9, 0xea, 0x98, 0x57, 0x12, 0x75, 0xa3, 0xf7, - 0x95, 0x68, 0x55, 0x83, 0x46, 0x56, 0x82, 0x28, 0xfe, 0x56, 0x2e, 0x28, 0x42, 0xa1, 0xae, 0xca, 0xc6, 0x2f, 0x0a, - 0xd9, 0x38, 0xdd, 0x6a, 0x0a, 0x47, 0x1a, 0xc1, 0xfd, 0x2b, 0x4e, 0x6a, 0xf2, 0x76, 0x50, 0x38, 0xab, 0x15, 0x3d, - 0x55, 0xdc, 0xaf, 0x8a, 0x8b, 0x86, 0xe2, 0xd4, 0x27, 0x6e, 0x19, 0x65, 0xdf, 0xbe, 0x72, 0xd5, 0xc2, 0xfb, 0xaa, - 0x28, 0x07, 0xa9, 0x3b, 0x76, 0x59, 0x16, 0xab, 0xcb, 0xa6, 0xec, 0x7e, 0xa3, 0xbe, 0x56, 0x16, 0x89, 0xf4, 0x93, - 0x21, 0x04, 0x0b, 0x31, 0x7d, 0x45, 0xaf, 0x2d, 0x6d, 0x20, 0xb0, 0x93, 0x0d, 0x6e, 0x7d, 0xbb, 0xa5, 0xf3, 0x94, - 0x2f, 0xa1, 0xd0, 0xc2, 0xab, 0x32, 0x08, 0xc4, 0xef, 0xd5, 0xba, 0xe1, 0x94, 0xc7, 0x43, 0x9e, 0x9f, 0xef, 0x20, - 0x5e, 0xd4, 0x1c, 0x55, 0x91, 0x8f, 0x3b, 0xd3, 0x22, 0xf3, 0x5c, 0xac, 0x5a, 0x07, 0x4a, 0x42, 0x9c, 0x35, 0xf7, - 0x8c, 0x29, 0xcb, 0xe8, 0x79, 0x8d, 0x9e, 0xf8, 0x2e, 0x5f, 0x3a, 0xc9, 0x2a, 0xc2, 0xd8, 0xf6, 0x56, 0x96, 0xf8, - 0xe3, 0x4f, 0x4a, 0x97, 0x85, 0x9c, 0x13, 0x64, 0xc0, 0x65, 0x4d, 0x41, 0xdf, 0xc7, 0x50, 0x90, 0xac, 0x67, 0x7b, - 0xa9, 0x22, 0x7d, 0xe9, 0x3d, 0x76, 0xda, 0xfe, 0x8b, 0xe9, 0x61, 0x45, 0x28, 0xea, 0x75, 0xca, 0x22, 0xf3, 0x0d, - 0xfd, 0xc8, 0xe6, 0xab, 0xc5, 0x68, 0xad, 0xca, 0x56, 0x15, 0x91, 0x6b, 0x5d, 0xcc, 0xaa, 0x7e, 0x76, 0x3a, 0x9d, - 0x96, 0x05, 0x8d, 0x8e, 0x76, 0x88, 0xc2, 0xc2, 0xc7, 0x8e, 0xe3, 0x54, 0xfb, 0xbe, 0x1d, 0xed, 0x16, 0xca, 0x6d, - 0xbb, 0x8d, 0x3d, 0x46, 0xdc, 0xee, 0xc2, 0x5f, 0x1d, 0x1d, 0xb9, 0x5d, 0xec, 0xec, 0x92, 0x59, 0x44, 0x9f, 0x8c, - 0x21, 0x82, 0x8c, 0x2d, 0xd2, 0xde, 0x98, 0xa1, 0x0e, 0xc6, 0x56, 0x36, 0x34, 0x1a, 0x0e, 0x58, 0x33, 0x30, 0x15, - 0x71, 0xc5, 0xaa, 0x70, 0x34, 0x94, 0x87, 0xd7, 0x84, 0xf7, 0xe2, 0x23, 0xb8, 0x51, 0xd6, 0x75, 0x99, 0x36, 0x0e, - 0xab, 0xe3, 0xfc, 0xa5, 0x54, 0x4f, 0x83, 0x03, 0x70, 0x2d, 0x14, 0xda, 0x24, 0x9f, 0xc5, 0xbf, 0xa5, 0xfc, 0xff, - 0xc5, 0xf2, 0xae, 0x6c, 0x3f, 0xd2, 0x05, 0x89, 0x76, 0xb1, 0x5b, 0xa8, 0xd7, 0x4d, 0x6b, 0x40, 0x5a, 0x19, 0x4c, - 0x55, 0x05, 0x3a, 0x28, 0xe9, 0x4b, 0x09, 0x40, 0x1a, 0xc4, 0xef, 0xc8, 0x31, 0xc3, 0x14, 0x17, 0x22, 0xc4, 0x22, - 0x7d, 0x1d, 0x8c, 0xc1, 0x7c, 0xde, 0x45, 0xfd, 0x41, 0x69, 0x4d, 0x80, 0x36, 0xbe, 0x36, 0xb6, 0xbd, 0xc4, 0xfd, - 0x55, 0xbd, 0x96, 0x00, 0x0c, 0x28, 0x73, 0x61, 0x13, 0xa2, 0x21, 0x81, 0x56, 0x59, 0xdc, 0xd4, 0x4b, 0xf9, 0x56, - 0xd5, 0xb3, 0x89, 0x8e, 0x21, 0xb8, 0xe6, 0x2a, 0x04, 0x5b, 0x68, 0x0b, 0x60, 0xb0, 0x7c, 0xf9, 0xe1, 0xb3, 0x05, - 0x53, 0xac, 0xae, 0x47, 0x17, 0xa7, 0x1c, 0xd7, 0xaf, 0x85, 0x67, 0x67, 0x4a, 0xfb, 0x1f, 0xe5, 0x8b, 0x3f, 0x34, - 0x0a, 0xf4, 0x2e, 0x4a, 0x12, 0x3a, 0x6e, 0x2d, 0xee, 0x19, 0x7b, 0xd5, 0x5e, 0x04, 0xd1, 0xfe, 0x75, 0xfd, 0xbb, - 0xbd, 0xeb, 0xc2, 0x81, 0xb1, 0x77, 0x65, 0x38, 0x71, 0xc8, 0x72, 0x21, 0x1b, 0xfc, 0xa0, 0x08, 0x14, 0x55, 0xaf, - 0x63, 0x1d, 0x5b, 0x11, 0x97, 0x7f, 0xb1, 0x1a, 0x0c, 0x4f, 0xce, 0xee, 0x16, 0xa1, 0x76, 0xc3, 0x12, 0x48, 0xed, - 0x33, 0xd0, 0x5d, 0xdb, 0xd1, 0x35, 0xf4, 0xa1, 0x0d, 0xa2, 0xd9, 0x40, 0xff, 0xe5, 0xe2, 0x8d, 0xd5, 0xd5, 0xcf, - 0x40, 0x45, 0x7b, 0x33, 0xc3, 0x63, 0xef, 0xdc, 0xbf, 0x67, 0xc9, 0xb5, 0xa7, 0x6b, 0x98, 0xc1, 0x87, 0x0e, 0x3c, - 0x2c, 0xd3, 0x3c, 0x7d, 0x8f, 0x44, 0x11, 0x9a, 0xc8, 0xf5, 0xa6, 0x03, 0xc9, 0x71, 0xbd, 0xae, 0xe6, 0x7a, 0x87, - 0xf6, 0x51, 0x57, 0x3f, 0xfd, 0x46, 0xd3, 0x4e, 0x26, 0x6c, 0x9a, 0x9e, 0xe2, 0x15, 0xed, 0x04, 0xcf, 0x08, 0xfa, - 0xad, 0x69, 0xf6, 0x38, 0x4c, 0x2d, 0x57, 0x5b, 0xf3, 0x47, 0x4d, 0x9b, 0x06, 0x61, 0xd8, 0xd3, 0x1e, 0x4f, 0xbd, - 0xe9, 0xe1, 0xf4, 0x45, 0x9f, 0x17, 0xe7, 0xdf, 0x94, 0xaa, 0x9b, 0xf4, 0xaf, 0xa7, 0x34, 0x4b, 0xb3, 0x24, 0xfe, - 0xc4, 0xb8, 0xd9, 0x89, 0x26, 0x2f, 0x8f, 0xd5, 0xa6, 0x5e, 0xfd, 0x4b, 0x6e, 0x77, 0x34, 0x9e, 0x7a, 0x45, 0x75, - 0xec, 0xe3, 0x81, 0xec, 0xe4, 0xc9, 0x81, 0xe8, 0xfa, 0x89, 0x8a, 0x26, 0xd7, 0x6a, 0x42, 0x94, 0xab, 0xf3, 0x31, - 0xce, 0xc4, 0xf8, 0x4e, 0x20, 0x0e, 0xa3, 0x74, 0xd7, 0x85, 0x1e, 0xe8, 0xda, 0x64, 0xa0, 0xff, 0xe8, 0x7a, 0x5d, - 0xd3, 0x75, 0x8f, 0xec, 0xa3, 0xee, 0xd8, 0x31, 0x0f, 0xed, 0x43, 0xab, 0x6d, 0x1f, 0x99, 0x5d, 0xab, 0x6b, 0x76, - 0xff, 0xd6, 0x1d, 0x5b, 0x87, 0xf6, 0xa1, 0xe9, 0x58, 0x5d, 0x28, 0xb4, 0xba, 0x56, 0xf7, 0xc6, 0x3a, 0xec, 0x8e, - 0x1d, 0x2c, 0xf5, 0xec, 0x4e, 0xc7, 0x72, 0x1d, 0xbb, 0xd3, 0x31, 0x3b, 0xf6, 0xd1, 0x91, 0xe5, 0xb6, 0xed, 0xa3, - 0xa3, 0xf3, 0x4e, 0xd7, 0x6e, 0xc3, 0xbb, 0x76, 0x7b, 0xdc, 0xb6, 0x5d, 0xd7, 0x82, 0xbf, 0xcc, 0xae, 0xed, 0xd1, - 0x0f, 0xd7, 0xb5, 0xdb, 0xae, 0xe9, 0x84, 0x1d, 0xcf, 0x3e, 0x7a, 0x61, 0xe2, 0xdf, 0x58, 0xcd, 0xc4, 0xbf, 0x00, - 0x8c, 0xf9, 0xc2, 0xf6, 0x8e, 0xe8, 0x17, 0x02, 0xbc, 0x39, 0xec, 0xfe, 0xaa, 0x1f, 0x6c, 0x1c, 0x83, 0x4b, 0x63, - 0xe8, 0x76, 0xec, 0x76, 0xdb, 0x3c, 0x74, 0xed, 0x6e, 0x7b, 0x6e, 0x1d, 0x7a, 0xf6, 0xd1, 0xf1, 0xd8, 0x72, 0xed, - 0xe3, 0x63, 0xd3, 0xb1, 0xda, 0xb6, 0x67, 0xba, 0xf6, 0x61, 0x1b, 0x7f, 0xb4, 0x6d, 0xef, 0xe6, 0xf8, 0x85, 0x7d, - 0xd4, 0x99, 0x1f, 0xd9, 0x87, 0x1f, 0x0f, 0xbb, 0xb6, 0xd7, 0x9e, 0xb7, 0x8f, 0x6c, 0xef, 0xf8, 0xe6, 0xc8, 0x3e, - 0x9c, 0x5b, 0xde, 0xd1, 0xd6, 0x96, 0xae, 0x67, 0x03, 0x8e, 0xf0, 0x35, 0xbc, 0x30, 0xf9, 0x0b, 0xf8, 0x33, 0xc7, - 0xb6, 0xff, 0x45, 0x30, 0x69, 0xbd, 0xe9, 0x0b, 0xbb, 0x7b, 0x3c, 0xa6, 0xea, 0x50, 0x60, 0x89, 0x1a, 0xd0, 0xe4, - 0xc6, 0xa2, 0xcf, 0x22, 0x38, 0x4b, 0x00, 0x12, 0x7f, 0xf8, 0xc7, 0x6e, 0x2c, 0xf8, 0x30, 0x7d, 0xf7, 0x7f, 0x0a, - 0x47, 0x4e, 0x39, 0x64, 0xae, 0xfc, 0x86, 0xff, 0x43, 0x49, 0x5f, 0x86, 0xe6, 0xf9, 0x26, 0x45, 0xc5, 0xfb, 0xdd, - 0x8a, 0x8a, 0x37, 0xab, 0x7d, 0x14, 0x15, 0xef, 0xbf, 0xba, 0xa2, 0xe2, 0xbc, 0x6a, 0x27, 0xff, 0xbe, 0x1a, 0x9b, - 0xfe, 0xd7, 0x75, 0xf5, 0x1a, 0x12, 0xf8, 0xad, 0xcb, 0x8b, 0xd5, 0x15, 0x44, 0x57, 0x7a, 0x1f, 0x0f, 0xde, 0xac, - 0x4a, 0x46, 0x60, 0x31, 0xd0, 0xd8, 0xf7, 0x31, 0xd1, 0xd8, 0xdf, 0x57, 0x03, 0xb0, 0x3c, 0xe1, 0x7c, 0x49, 0x30, - 0xb1, 0xe6, 0x7e, 0x38, 0x95, 0x3c, 0x0d, 0x94, 0xf4, 0xb1, 0x18, 0xbc, 0x12, 0xe0, 0xb8, 0x06, 0x75, 0xd8, 0x6a, - 0x11, 0xa5, 0xbd, 0x23, 0x07, 0x0e, 0x52, 0x6f, 0x9a, 0xe4, 0x95, 0xc6, 0xb6, 0x88, 0x47, 0x75, 0xcd, 0xbd, 0x26, - 0x36, 0xbe, 0x47, 0xa3, 0xc0, 0x66, 0xe8, 0x6e, 0x1d, 0xae, 0x06, 0xd6, 0x36, 0xc2, 0x68, 0x12, 0xd8, 0xb9, 0xa6, - 0xf7, 0x65, 0xd3, 0xbc, 0x8a, 0x31, 0xe6, 0xe6, 0x9e, 0x42, 0x4f, 0xaa, 0xed, 0xdd, 0xb2, 0x69, 0xdf, 0xae, 0x61, - 0x36, 0x7c, 0xbe, 0xd4, 0x7c, 0x8b, 0x5d, 0xa1, 0x04, 0x5c, 0x45, 0x55, 0x25, 0xb3, 0x5a, 0x23, 0x42, 0x0a, 0xee, - 0xbe, 0x30, 0x3e, 0x2c, 0x58, 0x4b, 0x47, 0x43, 0x7e, 0xc7, 0x51, 0xde, 0x95, 0x60, 0xaa, 0x06, 0x8b, 0xcf, 0xd6, - 0xc8, 0x71, 0x07, 0xbf, 0x03, 0xeb, 0xc8, 0x39, 0x9e, 0x51, 0xac, 0xe2, 0x79, 0xad, 0xc0, 0xa5, 0xcb, 0x4c, 0x3e, - 0x77, 0xd7, 0x75, 0xe6, 0x71, 0xa3, 0xa9, 0xb2, 0xcb, 0x16, 0x82, 0x0b, 0xc2, 0xcf, 0x93, 0x61, 0x70, 0x4e, 0xc6, - 0xdb, 0x68, 0xfb, 0xbc, 0x0d, 0x98, 0xa8, 0xf7, 0x18, 0x16, 0xb1, 0xc9, 0x1f, 0xd4, 0xb8, 0x00, 0xeb, 0x29, 0x64, - 0xc1, 0xee, 0x21, 0x9b, 0xa6, 0xf0, 0xa8, 0x1e, 0x5a, 0x31, 0xf7, 0xb7, 0x18, 0xd8, 0xa8, 0x80, 0x39, 0x10, 0xb4, - 0x86, 0xde, 0x66, 0x93, 0x23, 0x9d, 0x47, 0xd6, 0x25, 0x15, 0xb5, 0xdb, 0x39, 0x36, 0xdd, 0x23, 0xd3, 0x3e, 0xee, - 0x18, 0xb9, 0xd8, 0x70, 0x2a, 0xc8, 0x12, 0x42, 0xc0, 0x28, 0x5a, 0x76, 0x33, 0x88, 0x82, 0x2c, 0xf0, 0xc3, 0x1c, - 0xf8, 0xe3, 0xf2, 0xad, 0xe2, 0x9f, 0xab, 0x34, 0x83, 0x31, 0x0a, 0xa6, 0x17, 0x0d, 0xc2, 0xad, 0x11, 0xcb, 0x6e, - 0x19, 0x8b, 0x36, 0x28, 0xcb, 0xab, 0xf6, 0xe5, 0x7f, 0x9e, 0xb5, 0x6d, 0x4e, 0x96, 0x2c, 0xa3, 0x2c, 0xe2, 0xeb, - 0x43, 0x18, 0x43, 0xe7, 0x43, 0xf3, 0xa7, 0x4d, 0x04, 0xf7, 0x9f, 0xbb, 0x09, 0x6e, 0xc6, 0xf6, 0x21, 0xb8, 0xff, - 0xfc, 0xea, 0x04, 0xf7, 0x27, 0x95, 0xe0, 0x96, 0x7c, 0x81, 0x0a, 0xa9, 0xf3, 0x07, 0x7c, 0x6e, 0x41, 0x50, 0xe7, - 0xe7, 0xfa, 0x01, 0x31, 0xf0, 0xba, 0x92, 0x6c, 0xf7, 0x63, 0x29, 0x7b, 0x10, 0x0a, 0x45, 0x30, 0x08, 0x2d, 0x65, - 0x2a, 0x81, 0x44, 0xb4, 0x32, 0xa5, 0x3a, 0xc0, 0x7c, 0x1b, 0x65, 0xa1, 0xfd, 0x9e, 0x5f, 0xfc, 0x40, 0xc9, 0xf3, - 0x26, 0x4e, 0x16, 0x3e, 0x06, 0xe0, 0xd3, 0x31, 0xeb, 0x20, 0x3c, 0x38, 0xe0, 0x7f, 0x36, 0x8e, 0xa3, 0x89, 0xd4, - 0x54, 0xb0, 0xc1, 0x25, 0x71, 0xdc, 0xfa, 0x3d, 0xf3, 0x13, 0xdd, 0xa4, 0xd7, 0x30, 0xb9, 0xcf, 0xda, 0xce, 0x33, - 0xef, 0xf0, 0xd9, 0x91, 0x03, 0xff, 0xbb, 0xac, 0x9d, 0x9b, 0xbc, 0xe2, 0x22, 0x8e, 0x20, 0xf1, 0x89, 0xa8, 0xb9, - 0xa9, 0xda, 0x2d, 0x63, 0x9f, 0x8a, 0x5a, 0xc7, 0xcd, 0x95, 0x26, 0xfe, 0x7d, 0x51, 0xa7, 0xb1, 0xc6, 0x3c, 0x5e, - 0x29, 0xdd, 0x6a, 0xe8, 0x4d, 0x10, 0xad, 0x40, 0xf6, 0xa6, 0xd4, 0x50, 0x5f, 0xf3, 0xe1, 0x16, 0xe3, 0x62, 0xed, - 0xfc, 0xaa, 0xc8, 0xae, 0x24, 0xb2, 0xbc, 0xec, 0xc4, 0x20, 0x57, 0x5b, 0x38, 0x18, 0x9b, 0x1d, 0xf3, 0x0b, 0x69, - 0x90, 0xdb, 0x50, 0x4c, 0x90, 0x4f, 0x13, 0x94, 0x25, 0xab, 0x68, 0xdc, 0xc2, 0x9f, 0xfe, 0x28, 0x6d, 0x05, 0x07, - 0x10, 0x9d, 0x15, 0x3f, 0x6c, 0xe0, 0xac, 0xf9, 0xa7, 0x4e, 0x91, 0x8a, 0x22, 0x15, 0xb3, 0xe2, 0x3f, 0xcb, 0xcc, - 0x84, 0x12, 0xd8, 0xe2, 0xd4, 0x5a, 0x03, 0xff, 0x99, 0x6c, 0xf8, 0x2c, 0x33, 0x21, 0x89, 0x2c, 0x4c, 0xf7, 0xd3, - 0xa7, 0x54, 0x0b, 0xd2, 0x3a, 0xd2, 0xb0, 0xce, 0xc6, 0x45, 0x78, 0x37, 0xcd, 0x9f, 0xc5, 0x14, 0xe1, 0xad, 0x37, - 0x36, 0xe3, 0xe7, 0xcf, 0x4f, 0x07, 0xae, 0xc1, 0x93, 0x92, 0x96, 0x32, 0x68, 0x9d, 0xef, 0x67, 0x7c, 0x60, 0x34, - 0xba, 0xc5, 0x2d, 0xe1, 0xce, 0xe4, 0x08, 0x13, 0x65, 0x4e, 0xbd, 0x20, 0xa3, 0x05, 0x29, 0x19, 0x7d, 0x61, 0x04, - 0x20, 0xea, 0xc8, 0x5b, 0x57, 0xdb, 0x76, 0x6c, 0x47, 0x97, 0x0d, 0xa7, 0xc1, 0x6c, 0xb0, 0x8e, 0x33, 0x1f, 0x72, - 0x03, 0x85, 0xf1, 0x0c, 0x7c, 0x6b, 0xb2, 0x20, 0x0b, 0x21, 0xd1, 0x0c, 0x38, 0xd9, 0x2c, 0xe8, 0x5e, 0x9e, 0x73, - 0x8b, 0x67, 0x3f, 0xf9, 0x84, 0xc9, 0x06, 0x85, 0x5b, 0x1d, 0x46, 0x1c, 0xfa, 0x11, 0x0e, 0xc3, 0x96, 0xde, 0x82, - 0x54, 0x97, 0x2c, 0x49, 0x2d, 0xd5, 0x83, 0xa0, 0xa7, 0x41, 0x1b, 0x48, 0x43, 0x8f, 0x00, 0xa6, 0x89, 0xbf, 0x80, - 0x98, 0xec, 0xeb, 0xdc, 0xe4, 0x94, 0x56, 0xe7, 0xa4, 0x56, 0x73, 0x5f, 0x1c, 0x99, 0x9a, 0xe7, 0x9a, 0x9a, 0x03, - 0xe4, 0x56, 0xcf, 0xcd, 0x75, 0x7e, 0xd5, 0xdf, 0xa5, 0x04, 0x25, 0xfa, 0xf2, 0x98, 0xc6, 0x41, 0xea, 0x4f, 0x2e, - 0x5e, 0xce, 0x28, 0x80, 0x64, 0x4b, 0x89, 0x96, 0x1e, 0x90, 0x22, 0xe4, 0x82, 0xdd, 0x65, 0x06, 0x26, 0x62, 0xe1, - 0x55, 0x02, 0x63, 0x8d, 0xce, 0x7f, 0x41, 0xa4, 0x05, 0x9f, 0x3f, 0xb7, 0x02, 0x70, 0x70, 0x18, 0x28, 0xf8, 0x81, - 0x67, 0xa3, 0x84, 0xb0, 0xa0, 0x50, 0xdd, 0x21, 0xb2, 0xc0, 0xfb, 0x08, 0xfe, 0x2d, 0x8a, 0xc5, 0x0f, 0xae, 0x3a, - 0xb5, 0x43, 0x3f, 0x9a, 0x01, 0x49, 0xf3, 0xa3, 0x59, 0xcd, 0x44, 0x83, 0xfc, 0x17, 0x2b, 0xa5, 0x05, 0xa8, 0xc2, - 0x7c, 0x22, 0xfd, 0xfe, 0xfe, 0x82, 0x12, 0x4d, 0x41, 0x52, 0x73, 0x7f, 0x82, 0xce, 0x76, 0x85, 0x76, 0xe7, 0xf9, - 0xe0, 0xdb, 0x93, 0x05, 0xcb, 0x7c, 0x12, 0x0d, 0xc3, 0xe5, 0x17, 0xd8, 0x01, 0x6d, 0x2c, 0x92, 0xc4, 0x52, 0x32, - 0xf9, 0x09, 0xbb, 0x09, 0xc6, 0xfc, 0x5e, 0x6a, 0x6a, 0xfc, 0x9c, 0xb2, 0xd0, 0x0a, 0x6c, 0xe0, 0x9a, 0x64, 0x84, - 0x3c, 0xf6, 0x31, 0xcc, 0xe4, 0x20, 0x8a, 0xf5, 0xd3, 0x6f, 0xa5, 0xbf, 0xd6, 0xa6, 0x49, 0x80, 0x6c, 0x8f, 0x97, - 0x09, 0x0b, 0xff, 0x35, 0xf8, 0x16, 0x0e, 0xee, 0x6f, 0xaf, 0x74, 0xa3, 0x9f, 0xd9, 0xf3, 0x84, 0x4d, 0x07, 0xdf, - 0x36, 0x64, 0x3d, 0xc4, 0xeb, 0x3d, 0xf5, 0x45, 0x6f, 0x7b, 0x45, 0x70, 0xa0, 0xf6, 0x5e, 0x97, 0xfa, 0x53, 0x7e, - 0x5b, 0x87, 0x1b, 0xe0, 0xba, 0x74, 0xc7, 0x76, 0xfb, 0x78, 0x7f, 0x1e, 0x85, 0xfe, 0xf8, 0x53, 0x9f, 0xde, 0x94, - 0x1e, 0x2c, 0x38, 0xad, 0xc7, 0xfe, 0xb2, 0x87, 0xc7, 0xab, 0x5a, 0x08, 0xee, 0x9a, 0x54, 0x2a, 0x39, 0xbb, 0xc6, - 0xb5, 0x8c, 0x4b, 0x79, 0x8d, 0x5f, 0xc6, 0x4f, 0xdd, 0xce, 0x83, 0x8c, 0x89, 0x4f, 0xe1, 0x43, 0x9e, 0x8b, 0x8b, - 0x3a, 0x5d, 0x51, 0xf1, 0x62, 0x6d, 0xb7, 0x35, 0xb7, 0xfb, 0xb7, 0xce, 0x8d, 0xeb, 0xcc, 0x3d, 0xd7, 0xee, 0x7e, - 0x74, 0xbb, 0xf3, 0xb6, 0x7d, 0x1c, 0x5a, 0x6d, 0xfb, 0x18, 0xfe, 0x7c, 0x3c, 0xb6, 0xbb, 0x73, 0xcb, 0xb3, 0x0f, - 0x3f, 0xba, 0x5e, 0x68, 0x75, 0xed, 0x63, 0xf8, 0x73, 0x4e, 0xad, 0xe0, 0x02, 0x44, 0xf7, 0x9d, 0x6f, 0x4b, 0x54, - 0x40, 0xf9, 0x2d, 0xf5, 0x34, 0x66, 0xe9, 0x78, 0x6b, 0xd0, 0xf5, 0x00, 0xc9, 0xd0, 0x4d, 0x11, 0x04, 0x32, 0xea, - 0xb7, 0x20, 0x0d, 0x3b, 0x26, 0x10, 0x10, 0x26, 0x2f, 0xc2, 0x2f, 0x55, 0x84, 0xd2, 0x6f, 0xdc, 0x46, 0xbc, 0x4d, - 0x73, 0xc0, 0x75, 0x91, 0x99, 0x8a, 0x94, 0x43, 0xbf, 0x2c, 0x31, 0x88, 0x91, 0x08, 0x11, 0xaf, 0x50, 0xa5, 0x22, - 0x3b, 0x62, 0xbe, 0xbb, 0xe3, 0xe8, 0x99, 0xcb, 0x64, 0x76, 0x9e, 0xaf, 0x0a, 0x9b, 0x2b, 0x8c, 0x24, 0xf4, 0x3f, - 0x0a, 0x07, 0x93, 0xd2, 0x12, 0x1c, 0x11, 0xcd, 0x75, 0x12, 0x24, 0xb2, 0x7b, 0x0a, 0x89, 0x76, 0x9b, 0x23, 0xd5, - 0x1b, 0x90, 0xc6, 0xe4, 0x2d, 0x70, 0xc9, 0x37, 0x7e, 0xa8, 0x18, 0xb7, 0x28, 0x2d, 0x1f, 0x49, 0xca, 0xff, 0xf4, - 0x69, 0xd1, 0x39, 0xab, 0xd2, 0xef, 0x13, 0xb7, 0x03, 0xc7, 0x6e, 0x87, 0xb5, 0xb7, 0xda, 0x59, 0xed, 0x0e, 0x07, - 0x5c, 0x84, 0x0b, 0x15, 0xb6, 0x14, 0x42, 0x8b, 0xbb, 0xd1, 0xd8, 0xab, 0xa6, 0xc3, 0x85, 0x40, 0xca, 0x95, 0xab, - 0x8e, 0x6e, 0xf4, 0x23, 0xa1, 0x92, 0x8c, 0xb6, 0x84, 0x40, 0xe6, 0x77, 0x31, 0x1d, 0x50, 0xb3, 0x65, 0x1c, 0x3b, - 0x9c, 0x46, 0xff, 0xd7, 0x83, 0x40, 0x07, 0x2e, 0xd0, 0xa1, 0x56, 0x76, 0x6b, 0xc9, 0xa1, 0x47, 0x9e, 0xab, 0x74, - 0xa0, 0xb2, 0xf4, 0x4c, 0x87, 0x22, 0xc8, 0x6f, 0x85, 0x29, 0xed, 0xa4, 0x01, 0x99, 0x3c, 0x2d, 0x8a, 0x02, 0x33, - 0x80, 0x18, 0xe0, 0x2d, 0xe1, 0x4c, 0x66, 0x3c, 0x7d, 0xba, 0xf1, 0x10, 0x22, 0x85, 0xbd, 0x9a, 0xd9, 0x53, 0x57, - 0xe9, 0x9b, 0xae, 0x92, 0x18, 0x09, 0x17, 0xa9, 0x86, 0xb0, 0x7b, 0xa3, 0xb5, 0x87, 0x3f, 0x47, 0xcc, 0xcf, 0x6c, - 0xae, 0x69, 0x6a, 0x29, 0x87, 0xbb, 0xe9, 0xb2, 0x36, 0x58, 0xbc, 0xf1, 0x58, 0x67, 0x3c, 0x96, 0xe0, 0x93, 0xf5, - 0xc7, 0x15, 0xf7, 0xf4, 0x06, 0x18, 0x9f, 0x9d, 0x22, 0x3c, 0xcd, 0xbb, 0xcc, 0xa7, 0x18, 0x26, 0xea, 0x91, 0x1b, - 0x67, 0xbe, 0xc8, 0x23, 0x03, 0x7c, 0x79, 0xbf, 0x51, 0x25, 0xab, 0x78, 0x83, 0x9f, 0xbe, 0xbb, 0xf8, 0x4e, 0xe3, - 0xeb, 0x9f, 0x34, 0x88, 0x78, 0x91, 0xa1, 0xac, 0x07, 0x03, 0xca, 0x7a, 0xa0, 0xf1, 0x34, 0x22, 0x90, 0x3b, 0x20, - 0x3f, 0x20, 0x0c, 0xa2, 0x00, 0x9a, 0xf4, 0xaa, 0x8b, 0x55, 0x98, 0x05, 0x4b, 0x3f, 0xc9, 0x0e, 0xa0, 0xa9, 0x05, - 0x44, 0x4e, 0xdf, 0xe4, 0x23, 0x4e, 0xaa, 0x59, 0x11, 0x62, 0x2f, 0x8b, 0x84, 0x6e, 0x76, 0x1a, 0x84, 0x52, 0x35, - 0x2b, 0x3e, 0xe0, 0x8f, 0xc7, 0x6c, 0x99, 0x0d, 0x74, 0x7f, 0x09, 0xd9, 0x2f, 0x30, 0x9e, 0xf5, 0x41, 0x3c, 0xce, - 0x58, 0x66, 0xa5, 0x59, 0xc2, 0xfc, 0x85, 0x2e, 0x43, 0xb9, 0xd6, 0xe1, 0xa5, 0xab, 0xd1, 0x22, 0xc8, 0x64, 0x2c, - 0x44, 0x1a, 0x20, 0x28, 0x49, 0xa1, 0x8b, 0xa7, 0xc3, 0x9c, 0xa3, 0xf0, 0x3c, 0x9e, 0x55, 0x56, 0x54, 0xc1, 0xb9, - 0x9c, 0x61, 0xa4, 0x5d, 0x9e, 0xf1, 0x60, 0x82, 0x3e, 0x4f, 0xd7, 0xdc, 0xaf, 0x5d, 0x86, 0x6c, 0xd4, 0x4f, 0x4f, - 0xf8, 0xf5, 0x56, 0xc3, 0x50, 0x0c, 0x7a, 0xc7, 0x81, 0x58, 0xc2, 0x9b, 0x3c, 0xde, 0x0f, 0x78, 0x65, 0x38, 0x9a, - 0x08, 0x32, 0xc6, 0x79, 0xa7, 0xbe, 0x5c, 0x00, 0x23, 0x54, 0x52, 0xa2, 0xcf, 0xdd, 0x53, 0xe9, 0x62, 0x85, 0xbd, - 0x42, 0x5e, 0xe9, 0xf3, 0xe7, 0x97, 0xc3, 0xff, 0xfc, 0x1b, 0x82, 0xd1, 0xcf, 0x5d, 0xe1, 0x67, 0x7e, 0xa9, 0xd6, - 0xe2, 0xdc, 0xa7, 0x39, 0x44, 0x03, 0x0a, 0x36, 0x11, 0x81, 0x57, 0xc4, 0xd2, 0xca, 0x87, 0x57, 0x22, 0x98, 0x16, - 0x24, 0x9c, 0x30, 0x84, 0x37, 0xfc, 0x10, 0xa6, 0x77, 0x28, 0x82, 0x30, 0x68, 0xbf, 0xdd, 0x7d, 0x7f, 0x0c, 0xc1, - 0x96, 0x6b, 0x79, 0x20, 0x94, 0x0e, 0xe2, 0x1a, 0x3a, 0x3d, 0xf1, 0x35, 0x64, 0x5a, 0x90, 0xfd, 0x48, 0x7b, 0x07, - 0x30, 0xcc, 0x79, 0xbc, 0x60, 0x76, 0x10, 0x1f, 0xdc, 0xb2, 0x91, 0xe5, 0x2f, 0x03, 0xd2, 0xd5, 0xa3, 0xdc, 0x4d, - 0x23, 0xce, 0x4f, 0xaa, 0xc0, 0x89, 0xbf, 0xce, 0x0b, 0x54, 0xc6, 0xe5, 0xe8, 0x69, 0x1d, 0xaf, 0x50, 0xdc, 0x81, - 0x4f, 0xb3, 0x82, 0xc7, 0xf8, 0xf4, 0xe4, 0xc0, 0x3f, 0x2d, 0x87, 0x6f, 0xb4, 0x45, 0x02, 0x81, 0xf2, 0x21, 0x70, - 0x46, 0x51, 0x18, 0x45, 0xc0, 0xc5, 0xe2, 0xc1, 0x8a, 0xa7, 0x53, 0x35, 0xe4, 0xa2, 0x5d, 0xee, 0x9e, 0x44, 0x5a, - 0xb1, 0xa4, 0xe3, 0x25, 0x7d, 0xa9, 0xfe, 0x09, 0xf9, 0x13, 0x92, 0x27, 0xf3, 0xe8, 0x9c, 0xb0, 0xdd, 0x6b, 0xa1, - 0x1b, 0x25, 0xc6, 0x1e, 0x53, 0x25, 0x4e, 0x47, 0xaa, 0x81, 0xc2, 0x37, 0x70, 0x2e, 0x8f, 0x06, 0x03, 0x22, 0x73, - 0x55, 0x6a, 0x07, 0x48, 0x6c, 0x48, 0xa6, 0x00, 0x83, 0xcd, 0xa0, 0xa1, 0x45, 0x2e, 0x74, 0xd8, 0xa8, 0x3a, 0x9c, - 0x7a, 0x1f, 0x0f, 0x7c, 0xb1, 0xfc, 0x4a, 0x0b, 0x14, 0x16, 0x1e, 0x9f, 0x77, 0xa0, 0xef, 0x02, 0x4e, 0x85, 0xcc, - 0x6b, 0x7f, 0x25, 0x8a, 0x6e, 0x85, 0xfe, 0x7d, 0xac, 0x98, 0x36, 0xf0, 0x28, 0x07, 0xe7, 0x58, 0x7a, 0x21, 0xbc, - 0x0b, 0x6b, 0x1b, 0x4d, 0x06, 0xa4, 0xaf, 0x6f, 0x36, 0x35, 0x82, 0xfc, 0xae, 0xbd, 0xa6, 0xd6, 0x2d, 0x0f, 0x06, - 0x89, 0x67, 0x5e, 0xec, 0xc3, 0xd2, 0x4b, 0x24, 0x0b, 0xf9, 0xc9, 0x01, 0x8c, 0x0f, 0x22, 0x33, 0x94, 0x18, 0xa7, - 0xc0, 0x80, 0xf0, 0x0f, 0x7e, 0x4a, 0xe6, 0x19, 0x6f, 0x27, 0x82, 0xe7, 0xc3, 0x8b, 0xa5, 0x8c, 0x0d, 0x5b, 0xaa, - 0x52, 0xe7, 0x65, 0x9c, 0x66, 0x26, 0x70, 0x77, 0x02, 0x87, 0xdf, 0x57, 0x98, 0xbd, 0x21, 0xef, 0x67, 0x4c, 0x38, - 0x3e, 0x9f, 0x67, 0x1b, 0x7c, 0xa3, 0x37, 0x55, 0x21, 0x7e, 0x76, 0x4b, 0x85, 0x62, 0x1d, 0x6f, 0xab, 0x55, 0x70, - 0x4e, 0xb2, 0xda, 0xd2, 0x6f, 0xe9, 0x8f, 0x71, 0xc5, 0xd7, 0x6a, 0x63, 0x29, 0xd4, 0x3b, 0xcf, 0x06, 0x50, 0x55, - 0xc8, 0xe2, 0xfd, 0xe5, 0x92, 0x2a, 0x1b, 0xfd, 0x93, 0x03, 0xba, 0x96, 0x9e, 0xd2, 0x0a, 0x3b, 0x3d, 0x01, 0xf3, - 0x4e, 0x9a, 0x74, 0x7f, 0xb9, 0xe4, 0x53, 0x4a, 0xbf, 0xe8, 0xcd, 0xc1, 0x3c, 0x5b, 0x84, 0xa7, 0xff, 0x07, 0xc0, - 0xb1, 0x34, 0x8b, 0x20, 0x5c, 0x03, 0x00}; + 0x10, 0xc1, 0x11, 0x73, 0xdb, 0xae, 0x69, 0xb2, 0x36, 0xe8, 0x75, 0x92, 0xe5, 0x7c, 0x46, 0x35, 0x33, 0x70, 0x0e, + 0x48, 0x8d, 0x9b, 0x7c, 0xda, 0x6e, 0xcd, 0x6f, 0xf7, 0x5a, 0x7b, 0xf6, 0x4f, 0x55, 0x1a, 0xb6, 0x51, 0x50, 0xd8, + 0x37, 0xc9, 0x85, 0xc1, 0x0f, 0x03, 0x0e, 0xb3, 0x8b, 0xcc, 0xea, 0x99, 0xb5, 0x0b, 0x60, 0x07, 0xb3, 0xab, 0x35, + 0x75, 0xe9, 0xe8, 0x92, 0x6d, 0xf1, 0xb4, 0xf5, 0x43, 0x3d, 0x37, 0xa7, 0x43, 0x96, 0x2f, 0xed, 0x46, 0xf5, 0xc0, + 0x75, 0x5b, 0x35, 0x5c, 0xe6, 0x80, 0x64, 0x18, 0x10, 0x0d, 0xd2, 0xb4, 0x79, 0xc3, 0x86, 0x9f, 0xb9, 0xb6, 0x5b, + 0xa6, 0xa9, 0x6e, 0xc0, 0x79, 0xc7, 0x8c, 0x69, 0xce, 0x8a, 0xa5, 0x3f, 0x8e, 0x5a, 0x35, 0x02, 0x7a, 0x25, 0xcc, + 0x41, 0xa8, 0xe9, 0xb0, 0x09, 0xa1, 0xcc, 0x58, 0xb1, 0xdc, 0x35, 0xb9, 0xcd, 0x0d, 0x1f, 0x1e, 0x67, 0x27, 0xad, + 0x96, 0x3f, 0xea, 0x9a, 0xb6, 0x4e, 0xda, 0x4e, 0x4e, 0xd9, 0x6c, 0x17, 0xa9, 0xa9, 0xd3, 0xd5, 0x76, 0x67, 0x7e, + 0xbb, 0x67, 0xfe, 0x69, 0xed, 0xb5, 0xb6, 0xe9, 0xe8, 0xf6, 0x92, 0x1f, 0x23, 0x8f, 0xa4, 0x5a, 0xce, 0xd3, 0x36, + 0x9b, 0x75, 0x17, 0x0a, 0xce, 0x4c, 0x03, 0x4e, 0x73, 0x16, 0xaf, 0xcd, 0x4c, 0x00, 0x6a, 0x94, 0x0b, 0x38, 0xa2, + 0xec, 0x31, 0x0d, 0x7d, 0x28, 0x09, 0x36, 0xe5, 0x3b, 0x1b, 0xad, 0x0f, 0xab, 0xb5, 0x57, 0x0d, 0x0c, 0xfe, 0x59, + 0xff, 0x55, 0x31, 0xb9, 0x2f, 0x58, 0x20, 0x64, 0xf0, 0x46, 0x72, 0xba, 0x6a, 0x39, 0xc1, 0x62, 0xa4, 0x2b, 0x79, + 0xc7, 0xb8, 0x65, 0xcc, 0xe8, 0xad, 0xf5, 0xcf, 0x98, 0x71, 0x01, 0xd6, 0x5f, 0x08, 0xeb, 0xc0, 0x4e, 0x7e, 0x1a, + 0x36, 0x34, 0xd2, 0x31, 0x34, 0x7c, 0xd8, 0x49, 0x4e, 0x4f, 0x11, 0x6e, 0xe1, 0xce, 0xe9, 0x69, 0x20, 0xd8, 0x8c, + 0xf5, 0xae, 0xa2, 0xbb, 0x4a, 0xaa, 0x1d, 0x25, 0x8f, 0x4c, 0xa3, 0x47, 0xed, 0x56, 0x0b, 0x1b, 0x1f, 0xf4, 0xb2, + 0x30, 0x57, 0x3b, 0x9a, 0x6d, 0xb7, 0x5a, 0xd0, 0x2c, 0xfc, 0x71, 0xf3, 0xfa, 0x85, 0x2c, 0x5b, 0x69, 0x0b, 0xb7, + 0xd3, 0x36, 0xee, 0xa4, 0x1d, 0x7c, 0x9c, 0x1e, 0xe3, 0x93, 0xf4, 0x04, 0x9f, 0xa6, 0xa7, 0xf8, 0x2c, 0x3d, 0xc3, + 0xf7, 0xd3, 0xfb, 0xf8, 0x3c, 0x3d, 0xc7, 0x0f, 0xd2, 0x07, 0xf8, 0x61, 0xda, 0x6e, 0xe1, 0x47, 0x69, 0xbb, 0x8d, + 0x1f, 0xa7, 0xed, 0x0e, 0x7e, 0x92, 0xb6, 0x8f, 0xf1, 0xd3, 0xb4, 0x7d, 0x82, 0x9f, 0xa5, 0xed, 0x53, 0x4c, 0x21, + 0x77, 0x08, 0xb9, 0x19, 0xe4, 0x8e, 0x20, 0x97, 0x41, 0xee, 0x38, 0x6d, 0x9f, 0xae, 0xb1, 0xb2, 0x71, 0x2b, 0xa2, + 0x56, 0xbb, 0x73, 0x7c, 0x72, 0x7a, 0x76, 0xff, 0xfc, 0xc1, 0xc3, 0x47, 0x8f, 0x9f, 0x3c, 0x7d, 0x16, 0x0d, 0xf0, + 0xd0, 0xb8, 0x8f, 0x28, 0xd1, 0xe7, 0x07, 0xed, 0xd3, 0x01, 0xbe, 0xf6, 0x9f, 0x31, 0x3f, 0xe8, 0x9c, 0xb4, 0xd0, + 0xe5, 0xe5, 0xc9, 0xa0, 0x51, 0xe6, 0xbe, 0x37, 0x5e, 0x2b, 0x55, 0x16, 0x21, 0x24, 0x86, 0x1c, 0x84, 0xef, 0x4c, + 0xbd, 0xf7, 0x2c, 0xe6, 0x49, 0x81, 0x0e, 0x0e, 0xcc, 0x8f, 0x89, 0xff, 0x31, 0xf4, 0x3f, 0x68, 0xb0, 0x48, 0xb7, + 0x34, 0x76, 0x6e, 0xcb, 0xba, 0x74, 0x1a, 0x28, 0xed, 0x71, 0xf6, 0xb8, 0xb3, 0x8c, 0xff, 0xaf, 0xc8, 0x5a, 0xbe, + 0x90, 0x13, 0xab, 0x5d, 0x3a, 0xed, 0x31, 0xb2, 0x2c, 0xd2, 0xce, 0xe9, 0xe9, 0xc1, 0x2f, 0x7d, 0xde, 0x6f, 0x0f, + 0x06, 0x87, 0xed, 0xfb, 0x78, 0x52, 0x26, 0x74, 0x6c, 0xc2, 0xb0, 0x4c, 0x38, 0xb6, 0x09, 0x34, 0xb5, 0xb5, 0x21, + 0xe9, 0xc4, 0x24, 0x41, 0x89, 0x75, 0x6a, 0xda, 0xbe, 0x6f, 0xdb, 0x7e, 0x00, 0x26, 0x59, 0xa6, 0x79, 0xd7, 0xf4, + 0xc5, 0xc5, 0xc9, 0xca, 0x35, 0x8a, 0x27, 0xa9, 0x6b, 0xcd, 0x27, 0x9e, 0x0c, 0x06, 0x78, 0x68, 0x12, 0x4f, 0xab, + 0xc4, 0xb3, 0xc1, 0xc0, 0x75, 0xf5, 0xc0, 0x74, 0x75, 0xbf, 0xca, 0x3a, 0x1f, 0x0c, 0x4c, 0x97, 0xc8, 0x39, 0xe0, + 0x2b, 0xbd, 0xf7, 0xa5, 0x54, 0x82, 0xf0, 0x8b, 0xce, 0xe9, 0x69, 0x0f, 0x30, 0xcc, 0x18, 0xd6, 0x7a, 0x18, 0xdd, + 0x04, 0x30, 0xba, 0x83, 0xdf, 0xbd, 0x21, 0x4d, 0xaf, 0x69, 0x09, 0xa4, 0x5e, 0xf4, 0x5f, 0x51, 0x43, 0x1b, 0x98, + 0x9b, 0x3f, 0x13, 0xfb, 0x67, 0x88, 0x1a, 0x5f, 0x28, 0x80, 0x1b, 0xd4, 0x3a, 0x5e, 0x2f, 0x6b, 0x7a, 0xfc, 0x4c, + 0xc1, 0x4f, 0x66, 0xaa, 0x72, 0xda, 0x5b, 0x4d, 0x6f, 0x86, 0xab, 0xa9, 0xfa, 0x82, 0xfe, 0x8c, 0xff, 0x54, 0x87, + 0x71, 0xbf, 0xd9, 0x48, 0xd8, 0x9f, 0x23, 0x70, 0xc8, 0xe9, 0xa5, 0x23, 0x36, 0x41, 0xbd, 0xfe, 0x9f, 0x0a, 0x0f, + 0x1a, 0x41, 0xc6, 0x0f, 0xdb, 0x29, 0xe0, 0xae, 0xb3, 0x99, 0x18, 0xff, 0x80, 0x7a, 0xa8, 0xf7, 0xa7, 0x3a, 0xfc, + 0x13, 0xdd, 0x3b, 0xaa, 0xe6, 0xf2, 0xbb, 0x74, 0x5b, 0xb8, 0x8a, 0xe1, 0x73, 0x58, 0x6e, 0x61, 0x86, 0xdb, 0x4d, + 0x06, 0x11, 0xcf, 0xc0, 0x9f, 0x9b, 0xc4, 0xb2, 0xc1, 0x8f, 0x8e, 0x5b, 0xe8, 0x87, 0x76, 0x07, 0x34, 0x14, 0x4d, + 0x71, 0xb8, 0xbd, 0xe9, 0x8b, 0xe6, 0x31, 0x7e, 0xd0, 0x2c, 0x70, 0x1b, 0xe1, 0x66, 0xdb, 0xab, 0x8e, 0xfb, 0x2a, + 0x6e, 0x21, 0xac, 0xe2, 0x73, 0xf8, 0xe7, 0x04, 0x0d, 0xaa, 0x0d, 0x79, 0x45, 0x37, 0x7b, 0x07, 0xe7, 0x53, 0x12, + 0xab, 0x06, 0x3f, 0x3a, 0x6b, 0xa1, 0x1f, 0xce, 0x4c, 0x47, 0xec, 0x50, 0xef, 0xe8, 0x4a, 0xe2, 0x93, 0xa6, 0x84, + 0x8e, 0x5a, 0x65, 0x3f, 0x22, 0x3e, 0x45, 0x58, 0xc4, 0xc7, 0xf0, 0x4f, 0x3b, 0xec, 0xe7, 0xd7, 0xad, 0x7e, 0xcc, + 0xbc, 0xdb, 0x38, 0x39, 0xb5, 0xbe, 0xac, 0xca, 0x5e, 0x2c, 0x37, 0xd8, 0x65, 0xdb, 0xdc, 0x88, 0xb5, 0x8f, 0xe0, + 0x03, 0x61, 0x7d, 0x48, 0x14, 0x66, 0x87, 0xe0, 0x04, 0x0b, 0xb6, 0x1f, 0xea, 0xe2, 0xb8, 0xab, 0x1a, 0x0d, 0x24, + 0xfa, 0x6a, 0x70, 0x48, 0xda, 0x4d, 0xdd, 0x64, 0x18, 0x7e, 0x37, 0x48, 0x19, 0x59, 0x4d, 0x54, 0xbd, 0x3e, 0x76, + 0xbd, 0xda, 0xeb, 0x73, 0x8f, 0x1d, 0x84, 0x10, 0xd5, 0x8b, 0x75, 0x93, 0xa1, 0x23, 0xd1, 0x88, 0xf5, 0x05, 0xeb, + 0x9d, 0xa5, 0x2d, 0x64, 0xb0, 0x53, 0xf5, 0x62, 0xd6, 0xe4, 0x90, 0xde, 0x49, 0x63, 0xde, 0xd4, 0xf0, 0xeb, 0x24, + 0x98, 0x85, 0x00, 0xbc, 0xab, 0x5c, 0x7a, 0x8a, 0xa3, 0xce, 0xe9, 0x29, 0x16, 0x84, 0x27, 0x13, 0xf3, 0x4b, 0x11, + 0x9e, 0x0c, 0xcd, 0x2f, 0x49, 0x4a, 0x78, 0xd9, 0xde, 0x71, 0x41, 0x82, 0x55, 0x35, 0x29, 0x14, 0x16, 0xb4, 0x40, + 0x47, 0x1d, 0x7f, 0xb7, 0x8e, 0xa7, 0x7e, 0x0e, 0xa0, 0x4b, 0x28, 0x8c, 0x59, 0xa5, 0x6c, 0x16, 0x38, 0x27, 0xf4, + 0x32, 0x39, 0xed, 0x4d, 0x8f, 0xe2, 0x4e, 0x53, 0x36, 0x0b, 0x94, 0x4e, 0x8f, 0x4c, 0x4d, 0x9c, 0x91, 0xc7, 0xd4, + 0xb6, 0x86, 0xa7, 0x70, 0x21, 0x9a, 0x91, 0xec, 0xf0, 0xac, 0xd5, 0x48, 0x4e, 0x11, 0xee, 0x67, 0xab, 0x16, 0xce, + 0x57, 0xab, 0x16, 0xa6, 0xc1, 0x32, 0x3c, 0x16, 0x1e, 0x20, 0xa5, 0xba, 0x6b, 0x33, 0xc0, 0x4d, 0x8f, 0xc7, 0x1a, + 0x2e, 0xf7, 0x35, 0xb8, 0xcc, 0x68, 0x70, 0xe6, 0x49, 0xb9, 0xbb, 0x55, 0x43, 0x26, 0xc4, 0xdf, 0x38, 0x54, 0x80, + 0xbd, 0x16, 0x7e, 0x5d, 0xbd, 0x79, 0xa6, 0x88, 0x7f, 0x97, 0xd8, 0xa6, 0x05, 0xc5, 0xe8, 0x76, 0xb1, 0x5f, 0xe9, + 0x56, 0xb1, 0x37, 0x3b, 0x8a, 0x5d, 0x6d, 0x17, 0xfb, 0x28, 0x03, 0x75, 0x1d, 0xff, 0xe1, 0xf8, 0xac, 0xd5, 0x38, + 0x06, 0x64, 0x3d, 0x3e, 0x6b, 0x55, 0x85, 0xee, 0xd1, 0x6a, 0xad, 0x34, 0xf9, 0x4c, 0xad, 0xc3, 0x02, 0xf7, 0x9e, + 0xd3, 0x66, 0xe1, 0xac, 0xdf, 0x76, 0xe9, 0xa4, 0xdd, 0x3f, 0x05, 0x83, 0x10, 0x61, 0xa8, 0x9d, 0xee, 0x9f, 0x0d, + 0x7a, 0x53, 0x16, 0x37, 0x20, 0x15, 0xa5, 0x63, 0xed, 0x7e, 0xa1, 0xf2, 0x5e, 0xf8, 0xa3, 0x84, 0xa4, 0xce, 0x00, + 0x61, 0x49, 0x1a, 0xba, 0x7f, 0x3c, 0x30, 0xe7, 0x5d, 0x01, 0xbf, 0x4f, 0xcc, 0xef, 0x52, 0x2b, 0xe3, 0xbc, 0x1a, + 0xa6, 0x37, 0xc3, 0xa8, 0x27, 0xc8, 0x6b, 0x1a, 0x1b, 0x43, 0x75, 0x94, 0x96, 0x19, 0xea, 0x0b, 0x64, 0xbc, 0x29, + 0x33, 0x04, 0x79, 0x2d, 0xdc, 0x6f, 0xbc, 0x2c, 0x52, 0x30, 0x5a, 0xc1, 0x93, 0x14, 0x0c, 0x56, 0xf0, 0x30, 0x15, + 0xe0, 0x54, 0x41, 0x53, 0x16, 0x98, 0xc2, 0x3f, 0x74, 0x6a, 0x30, 0x73, 0x75, 0x4b, 0x0c, 0x96, 0x76, 0x19, 0x9c, + 0x14, 0x1f, 0x65, 0x0c, 0x7f, 0x1b, 0x1a, 0x61, 0x06, 0x6d, 0x32, 0x84, 0x79, 0x52, 0x10, 0x48, 0xc3, 0x3c, 0x99, + 0x10, 0x06, 0x4d, 0xf2, 0x64, 0x48, 0x58, 0xbf, 0x13, 0xa0, 0xc9, 0x53, 0x03, 0x3b, 0x00, 0x0e, 0xaf, 0xdf, 0x86, + 0x6b, 0xdb, 0x38, 0x5c, 0xb3, 0x43, 0x13, 0x82, 0x70, 0x15, 0xc3, 0x2c, 0x60, 0x73, 0x9a, 0x9f, 0x9d, 0x2a, 0x86, + 0x24, 0x4f, 0xa8, 0xa1, 0xde, 0x7f, 0x01, 0x59, 0x8d, 0xef, 0x2d, 0xd9, 0x1a, 0xef, 0xdd, 0x5b, 0x8a, 0xf5, 0x0f, + 0xf0, 0x47, 0xb9, 0x3f, 0xda, 0x9c, 0x7e, 0x6b, 0xf4, 0x57, 0x0a, 0xc5, 0x76, 0x94, 0x42, 0x7f, 0x79, 0x9f, 0x3a, + 0x45, 0x96, 0xb7, 0x69, 0x34, 0xa2, 0xc5, 0xe7, 0x08, 0x7f, 0x4a, 0xa3, 0x1c, 0x18, 0xc1, 0x08, 0x7f, 0x4c, 0xa3, + 0x82, 0x45, 0xf8, 0x8f, 0x34, 0x1a, 0xe6, 0x8b, 0x08, 0x7f, 0x48, 0xa3, 0x49, 0x11, 0xe1, 0xf7, 0xa0, 0xf1, 0x1c, + 0xf1, 0xc5, 0x2c, 0xc2, 0xbf, 0xa7, 0x91, 0x32, 0x2e, 0x05, 0xf8, 0x61, 0x1a, 0x31, 0x16, 0xe1, 0x77, 0x69, 0x24, + 0xf3, 0x08, 0x5f, 0xa5, 0x91, 0x2c, 0x22, 0xfc, 0x28, 0x8d, 0x0a, 0x1a, 0xe1, 0xc7, 0x69, 0x04, 0x85, 0x26, 0x11, + 0x7e, 0x92, 0x46, 0xd0, 0xb2, 0x8a, 0xf0, 0xdb, 0x34, 0xe2, 0x22, 0xc2, 0xbf, 0xa5, 0x91, 0x5e, 0x14, 0xff, 0x2c, + 0x24, 0x57, 0x11, 0x7e, 0x9a, 0x46, 0x53, 0x1e, 0xe1, 0x37, 0x69, 0x54, 0xc8, 0x08, 0xbf, 0x4e, 0x23, 0x9a, 0x47, + 0xf8, 0x55, 0x1a, 0xe5, 0x2c, 0xc2, 0xbf, 0xa6, 0xd1, 0x88, 0x45, 0xf8, 0x65, 0x1a, 0xdd, 0xb1, 0x3c, 0x97, 0x11, + 0x7e, 0x96, 0x46, 0x4c, 0x44, 0xf8, 0x97, 0x34, 0xca, 0xa6, 0x11, 0xfe, 0x29, 0x8d, 0x68, 0xf1, 0x59, 0x45, 0xf8, + 0x79, 0x1a, 0x31, 0x1a, 0xe1, 0x17, 0xb6, 0xa3, 0x49, 0x84, 0x7f, 0x4e, 0xa3, 0x9b, 0x69, 0xb4, 0xc6, 0x4a, 0x91, + 0xe5, 0x6b, 0x9e, 0xb1, 0x3f, 0x58, 0x1a, 0x8d, 0x5b, 0xe3, 0xf3, 0xf1, 0x38, 0xc2, 0x54, 0x68, 0xfe, 0xcf, 0x82, + 0xdd, 0x3c, 0xd5, 0x90, 0x48, 0xd9, 0x70, 0x74, 0x3f, 0xc2, 0xf4, 0x9f, 0x05, 0x4d, 0xa3, 0xf1, 0xd8, 0x14, 0xf8, + 0x67, 0x41, 0x67, 0xb4, 0x78, 0xcb, 0xd2, 0xe8, 0xfe, 0x78, 0x3c, 0x1e, 0x9d, 0x44, 0x98, 0x7e, 0x5d, 0x7c, 0x34, + 0x2d, 0x98, 0x02, 0x43, 0xc6, 0x27, 0x50, 0xf7, 0x74, 0x7c, 0x3a, 0xca, 0x22, 0x3c, 0xe4, 0xea, 0x9f, 0x05, 0x7c, + 0x8f, 0xd9, 0x49, 0x76, 0x12, 0xe1, 0x61, 0x4e, 0xb3, 0xcf, 0x69, 0xd4, 0x32, 0xbf, 0xc4, 0x2f, 0x6c, 0xf4, 0x7a, + 0x26, 0xcd, 0x7d, 0xc0, 0x98, 0x0d, 0xb3, 0x51, 0x84, 0xcd, 0x60, 0xc6, 0xf0, 0xf7, 0x0b, 0x7f, 0xc7, 0x74, 0x1a, + 0x9d, 0xd3, 0xce, 0x90, 0x75, 0x22, 0x3c, 0x7c, 0x73, 0x23, 0xd2, 0x88, 0x9e, 0x76, 0x68, 0x87, 0x46, 0x78, 0xb8, + 0x28, 0xf2, 0xbb, 0x1b, 0x29, 0x47, 0x00, 0x84, 0xe1, 0xf9, 0xf9, 0xfd, 0x08, 0x67, 0xf4, 0x57, 0x0d, 0xb5, 0x4f, + 0xc7, 0x0f, 0x18, 0x6d, 0x45, 0xf8, 0x17, 0x5a, 0xe8, 0x8f, 0x0b, 0xe5, 0x06, 0xda, 0x82, 0x14, 0x99, 0xbd, 0x03, + 0x5d, 0x79, 0x34, 0xea, 0x9c, 0x3d, 0x68, 0xb3, 0x08, 0x67, 0x57, 0xaf, 0xa1, 0xb7, 0xfb, 0xe3, 0xd3, 0x16, 0x7c, + 0x08, 0x10, 0x3a, 0x59, 0x01, 0x8d, 0x9c, 0x9d, 0x3c, 0x38, 0x65, 0x23, 0x93, 0xa8, 0x78, 0xfe, 0xd9, 0xcc, 0xfe, + 0x1c, 0xe6, 0x93, 0x15, 0x7c, 0xa6, 0xa4, 0x48, 0xa3, 0x51, 0xd6, 0x3e, 0x39, 0x86, 0x84, 0x3b, 0x2a, 0x3c, 0x70, + 0x6e, 0xa1, 0xea, 0xf9, 0x30, 0xc2, 0xb7, 0x36, 0xf5, 0x7c, 0x68, 0x3e, 0x26, 0xef, 0x7e, 0x15, 0x6f, 0x46, 0x69, + 0x34, 0x3c, 0x3f, 0x3f, 0x6b, 0x41, 0xc2, 0x07, 0x7a, 0x97, 0x46, 0xf4, 0x01, 0xfc, 0x07, 0xd9, 0x1f, 0x9f, 0x41, + 0x87, 0x30, 0xc2, 0xdb, 0xc9, 0xc7, 0x30, 0xe7, 0xf3, 0x94, 0x7e, 0xe6, 0x69, 0x34, 0x1c, 0x0d, 0xef, 0x9f, 0x41, + 0xbd, 0x19, 0x9d, 0x3c, 0xd3, 0x14, 0xda, 0x6d, 0xb5, 0x4c, 0xcb, 0xef, 0xf8, 0x17, 0x66, 0xaa, 0x9f, 0x9e, 0x9e, + 0x0d, 0x3b, 0x30, 0x82, 0x2b, 0xd0, 0x96, 0xc0, 0x78, 0xce, 0x33, 0xd3, 0xe0, 0x55, 0xf6, 0x74, 0x94, 0x46, 0x0f, + 0x1e, 0x1c, 0x77, 0xb2, 0x2c, 0xc2, 0xb7, 0x1f, 0x47, 0xb6, 0xb6, 0xc9, 0x53, 0x00, 0xfb, 0x34, 0x62, 0x0f, 0x1e, + 0x9c, 0xdd, 0xa7, 0xf0, 0xfd, 0xdc, 0xb4, 0x75, 0x3e, 0x1e, 0x66, 0xe7, 0xd0, 0xd6, 0xef, 0x30, 0x9d, 0x93, 0xf3, + 0xe3, 0x91, 0xe9, 0xeb, 0x77, 0x33, 0xea, 0xce, 0xf8, 0x64, 0x7c, 0x62, 0x32, 0xcd, 0x50, 0xcb, 0xcf, 0xdf, 0x58, + 0x1a, 0x65, 0x6c, 0xd4, 0x8e, 0xf0, 0xad, 0x5b, 0xb8, 0x07, 0x27, 0xad, 0xd6, 0xe8, 0x38, 0xc2, 0xa3, 0x87, 0xf3, + 0xf9, 0x5b, 0x03, 0xc1, 0xf6, 0xc9, 0x03, 0xfb, 0xad, 0x3e, 0xdf, 0x41, 0xd3, 0x43, 0x03, 0xb4, 0x11, 0x9f, 0x99, + 0x96, 0xcf, 0x1e, 0xc0, 0x7f, 0xe6, 0xdb, 0x34, 0x5d, 0x7e, 0xcb, 0xd1, 0xc4, 0x2e, 0x4a, 0x9b, 0x3d, 0x68, 0x41, + 0x8d, 0x31, 0xff, 0x38, 0x2c, 0x38, 0xa0, 0xd1, 0xb0, 0x03, 0xff, 0x17, 0xe1, 0x71, 0x7e, 0xf5, 0xda, 0xe1, 0xec, + 0x78, 0x4c, 0xc7, 0xad, 0x08, 0x8f, 0xe5, 0x47, 0xa5, 0x3f, 0x3c, 0x14, 0x69, 0xd4, 0xe9, 0x9c, 0x0f, 0x4d, 0x99, + 0xc5, 0x2f, 0x8a, 0x1b, 0x3c, 0x6e, 0x99, 0x56, 0x26, 0xf4, 0xad, 0x1a, 0x5e, 0x49, 0x58, 0x49, 0xf8, 0x2f, 0xc2, + 0x13, 0x50, 0xb1, 0xb9, 0x56, 0xce, 0xed, 0x76, 0x98, 0xbc, 0x33, 0xa8, 0x39, 0xba, 0x0f, 0xf0, 0xf2, 0xcb, 0x38, + 0xa2, 0xf4, 0xb4, 0xd3, 0x8a, 0xb0, 0x19, 0xf5, 0x79, 0x0b, 0xfe, 0x8b, 0xb0, 0x85, 0x9c, 0x81, 0xeb, 0xe4, 0xe3, + 0xb3, 0x97, 0x37, 0x69, 0x44, 0x47, 0xe3, 0x31, 0x2c, 0x89, 0x99, 0x8c, 0x2f, 0x36, 0x95, 0x82, 0xdd, 0xfd, 0x7a, + 0xe3, 0xb6, 0x8b, 0x49, 0xd0, 0x0e, 0x3a, 0x67, 0x0f, 0x86, 0x27, 0x11, 0x7e, 0x3b, 0xe2, 0x54, 0xc0, 0x2a, 0x65, + 0xa3, 0xd3, 0xec, 0x34, 0x33, 0x09, 0x13, 0x99, 0x46, 0x27, 0xb0, 0xe4, 0x9d, 0x08, 0xf3, 0x2f, 0x57, 0x77, 0x16, + 0xdd, 0xa0, 0xb6, 0x43, 0x90, 0x71, 0x8b, 0x9d, 0x9d, 0x67, 0x11, 0xce, 0xe9, 0x97, 0x67, 0xbf, 0x16, 0x69, 0xc4, + 0xce, 0xd8, 0xd9, 0x98, 0xfa, 0xef, 0x3f, 0xd4, 0xd4, 0xd4, 0x68, 0x8d, 0x4f, 0x21, 0xe9, 0x46, 0x98, 0xb1, 0xde, + 0xcf, 0xc6, 0x06, 0x43, 0x5e, 0xcd, 0xa4, 0xc8, 0x9e, 0x8e, 0xc7, 0xd2, 0x62, 0x31, 0x85, 0x4d, 0xf8, 0x09, 0xa0, + 0x4d, 0x47, 0xa3, 0x73, 0x76, 0x16, 0xe1, 0x4f, 0x76, 0x97, 0xb8, 0x09, 0x7c, 0xb2, 0x98, 0xcd, 0xdc, 0x6e, 0xff, + 0x64, 0x81, 0x02, 0xf3, 0x1d, 0xd3, 0x31, 0x1d, 0x75, 0x22, 0xfc, 0xc9, 0xc0, 0x65, 0x74, 0x0c, 0xff, 0x41, 0x01, + 0xe8, 0xec, 0x41, 0x8b, 0xb1, 0x07, 0x2d, 0xf3, 0x15, 0xe6, 0xb9, 0x99, 0x0f, 0xcf, 0xb2, 0x76, 0x84, 0x3f, 0x39, + 0x74, 0x1c, 0x8f, 0x69, 0x0b, 0xd0, 0xf1, 0x93, 0x43, 0xc7, 0x4e, 0x6b, 0xd8, 0xa1, 0xe6, 0xdb, 0x62, 0xcd, 0xf9, + 0xfd, 0x8c, 0xc1, 0xe4, 0x3e, 0x59, 0x84, 0xbc, 0x7f, 0xff, 0xfc, 0xfc, 0xc1, 0x03, 0xf8, 0x34, 0x6d, 0x97, 0x9f, + 0x4a, 0x3f, 0xcc, 0x0d, 0x92, 0xb5, 0xb2, 0x13, 0xa0, 0x93, 0x9f, 0xcc, 0x18, 0xc7, 0xe3, 0x31, 0x6b, 0x45, 0x38, + 0xe7, 0x33, 0x66, 0x31, 0xc1, 0xfe, 0x36, 0x1d, 0x1d, 0x77, 0xb2, 0xd1, 0x71, 0x27, 0xc2, 0xf9, 0xdb, 0x67, 0x66, + 0x36, 0x2d, 0x98, 0xbd, 0xdf, 0x72, 0x1e, 0x6b, 0x66, 0xf4, 0x0d, 0x0c, 0x12, 0x56, 0x1a, 0x2a, 0xbf, 0x0f, 0xe8, + 0xe1, 0xd9, 0x59, 0x36, 0x82, 0x81, 0xbe, 0x87, 0x6e, 0x01, 0x8c, 0xef, 0xed, 0xe6, 0x1b, 0xd2, 0xd3, 0x53, 0x98, + 0xee, 0xfb, 0xf9, 0xa2, 0x98, 0xbf, 0x4a, 0xa3, 0x07, 0xc7, 0xf7, 0x5b, 0xa3, 0x61, 0x84, 0xdf, 0xbb, 0x09, 0x1e, + 0x67, 0xc3, 0xe3, 0xfb, 0xed, 0x08, 0xbf, 0x37, 0xfb, 0xed, 0xfe, 0xf0, 0xec, 0x1c, 0xce, 0x8d, 0xf7, 0x6a, 0x5e, + 0xbc, 0x9d, 0x98, 0x02, 0x63, 0xfa, 0x00, 0x9a, 0xfd, 0xcd, 0xec, 0xc6, 0x51, 0x1b, 0x36, 0xf2, 0x7b, 0xb3, 0xc9, + 0x0c, 0x9e, 0xdc, 0x6f, 0x9f, 0x9e, 0x9f, 0x46, 0x78, 0xc6, 0x47, 0x02, 0x08, 0xbc, 0xd9, 0x28, 0x0f, 0xda, 0x0f, + 0xee, 0xb7, 0x22, 0x3c, 0x7b, 0xab, 0xb3, 0x8f, 0x74, 0x66, 0xa8, 0xf1, 0x18, 0x60, 0x36, 0xe3, 0x4a, 0xdf, 0xbd, + 0x51, 0x8e, 0x1e, 0xb3, 0x76, 0x84, 0x67, 0x32, 0xcb, 0xa8, 0x7a, 0x6b, 0x13, 0x86, 0xa7, 0x11, 0x16, 0xf4, 0x0b, + 0xfd, 0x5b, 0xfa, 0xcd, 0x34, 0x62, 0x74, 0x64, 0xd2, 0x0c, 0x0e, 0x47, 0xf8, 0xdd, 0x08, 0x6e, 0xf4, 0xd2, 0x68, + 0x3c, 0x1a, 0x9f, 0x02, 0x78, 0x80, 0x00, 0x59, 0xec, 0x06, 0x68, 0xc0, 0xd7, 0xe8, 0xd1, 0x30, 0x8d, 0xce, 0x86, + 0xe7, 0xac, 0x73, 0x1c, 0xe1, 0x92, 0x1a, 0xd1, 0x53, 0xc8, 0x37, 0x9f, 0x1f, 0xcd, 0x96, 0x3a, 0xb1, 0x09, 0x06, + 0x40, 0x23, 0x7a, 0xbf, 0x35, 0x3a, 0x8b, 0xf0, 0xfc, 0x35, 0xf3, 0x7b, 0x8c, 0x31, 0x76, 0x0e, 0xb0, 0x84, 0x24, + 0x83, 0x40, 0xe7, 0xe3, 0xe1, 0x83, 0x73, 0xf3, 0x0d, 0x60, 0xa0, 0x63, 0xc6, 0x00, 0x48, 0xf3, 0xd7, 0xac, 0x04, + 0xc4, 0x68, 0x78, 0xbf, 0x05, 0xf4, 0x65, 0x4e, 0xe7, 0xf4, 0x8e, 0xde, 0x3c, 0x9d, 0x9b, 0x39, 0x8d, 0x47, 0xa7, + 0x11, 0x9e, 0x3f, 0xff, 0x65, 0xbe, 0x18, 0x8f, 0xcd, 0x84, 0xe8, 0xf0, 0x41, 0x84, 0xe7, 0xac, 0x58, 0xc0, 0x1a, + 0x9d, 0x9f, 0x1e, 0x8f, 0x23, 0xec, 0xd0, 0x30, 0x6b, 0x65, 0x43, 0xb8, 0xb2, 0x5c, 0xcc, 0xd2, 0x68, 0x34, 0xa2, + 0xad, 0x11, 0x5c, 0x60, 0xca, 0x9b, 0x5f, 0x0b, 0x8b, 0x46, 0xcc, 0xe0, 0x83, 0x5b, 0x43, 0x98, 0x2f, 0xc0, 0xe3, + 0xe3, 0x90, 0x65, 0x19, 0x75, 0x89, 0x67, 0x67, 0xc7, 0xc7, 0x80, 0x7b, 0x76, 0x86, 0x16, 0x41, 0xde, 0xa8, 0xbb, + 0x61, 0x21, 0xe1, 0xe8, 0x02, 0xa2, 0x0a, 0x64, 0xf5, 0xcd, 0xdd, 0x6b, 0x43, 0x57, 0xdb, 0x67, 0x0f, 0x60, 0x01, + 0x14, 0x1d, 0x8d, 0x5e, 0xd9, 0xc3, 0xed, 0x7c, 0x78, 0x72, 0xda, 0x3e, 0x8e, 0xb0, 0xdf, 0x08, 0xf4, 0xbc, 0x75, + 0xbf, 0x03, 0x25, 0xc4, 0xe8, 0xce, 0x96, 0x18, 0x9f, 0xd0, 0x93, 0xb3, 0x56, 0x84, 0xfd, 0xd6, 0x60, 0xe7, 0xc3, + 0xd3, 0xfb, 0xf0, 0xa9, 0xa6, 0x2c, 0xcf, 0x0d, 0x7e, 0x9f, 0x02, 0x5c, 0x14, 0x7f, 0x26, 0x68, 0x1a, 0xd1, 0xd6, + 0x69, 0xa7, 0x33, 0x82, 0xcf, 0xfc, 0x0b, 0x2b, 0xd2, 0x28, 0x6b, 0xc1, 0x7f, 0x11, 0x0e, 0x76, 0x12, 0x1b, 0x46, + 0xd8, 0xe0, 0xdd, 0x19, 0x3d, 0x35, 0x7b, 0xdf, 0xed, 0xaa, 0xd6, 0x79, 0x0b, 0x36, 0xac, 0xdb, 0x54, 0xee, 0x4b, + 0x09, 0x79, 0xe3, 0x48, 0x2c, 0x8d, 0x70, 0x80, 0xa0, 0xe3, 0xfb, 0xe3, 0x08, 0xfb, 0x1d, 0x77, 0x72, 0x76, 0xde, + 0x01, 0x52, 0xa6, 0x81, 0x50, 0x8c, 0x3a, 0xc3, 0x13, 0x20, 0x4d, 0x9a, 0xbd, 0xb6, 0x78, 0x12, 0x61, 0xfd, 0x54, + 0xe9, 0x57, 0x69, 0x34, 0x3a, 0x1f, 0x8e, 0x47, 0xe7, 0x11, 0xd6, 0x72, 0x46, 0xb5, 0x34, 0x14, 0xf0, 0xf8, 0xe4, + 0x7e, 0x84, 0x0d, 0x9a, 0xb7, 0x58, 0x6b, 0xd4, 0x8a, 0xb0, 0x3b, 0x4a, 0x18, 0x3b, 0xef, 0xc0, 0xb4, 0x7e, 0x7e, + 0xae, 0x01, 0x97, 0x47, 0x6c, 0x78, 0x1c, 0xe1, 0x92, 0xde, 0x1b, 0x42, 0x04, 0x5f, 0x6a, 0x26, 0x3f, 0x3b, 0xd6, + 0x03, 0x48, 0x9d, 0xdf, 0xf0, 0xb0, 0x0c, 0x2f, 0x6f, 0x2c, 0x1a, 0x51, 0xb3, 0xc5, 0x83, 0x2b, 0xdd, 0x27, 0x34, + 0xf6, 0x6c, 0x3b, 0x27, 0xcb, 0x35, 0x2e, 0x23, 0xa5, 0x7e, 0x66, 0x77, 0x2a, 0x56, 0xca, 0x70, 0xb2, 0x41, 0x0a, + 0x78, 0x33, 0x38, 0xdf, 0x00, 0xe7, 0xfe, 0x09, 0x82, 0xa4, 0x20, 0xad, 0xae, 0xb8, 0xf0, 0x2e, 0xa9, 0x5d, 0x01, + 0xf1, 0x13, 0x20, 0xbd, 0x20, 0x94, 0x68, 0x08, 0x33, 0x63, 0x85, 0x49, 0x6f, 0xa9, 0x6f, 0x64, 0x4a, 0x69, 0x6d, + 0xff, 0x29, 0xa1, 0x3e, 0xc0, 0x3c, 0xdc, 0x37, 0x43, 0x08, 0x26, 0xd4, 0x95, 0xc4, 0x84, 0x8b, 0x7e, 0x21, 0x74, + 0xac, 0x54, 0xbf, 0x18, 0xe0, 0xf6, 0x19, 0xc2, 0x10, 0x88, 0x81, 0xf4, 0xe5, 0xe5, 0x65, 0xfb, 0xec, 0xc0, 0x08, + 0x7d, 0x97, 0x97, 0xe7, 0xf6, 0x07, 0xfc, 0x3b, 0xa8, 0x82, 0x5f, 0xc3, 0xf8, 0x1e, 0xb1, 0x40, 0xa3, 0x67, 0xf8, + 0xeb, 0x47, 0x6c, 0xb5, 0x8a, 0x1f, 0x31, 0x02, 0x33, 0xc6, 0x8f, 0x58, 0x62, 0x2e, 0x40, 0xac, 0x9b, 0x0d, 0xe9, + 0x83, 0xe6, 0xac, 0x85, 0x21, 0x24, 0xbb, 0xe7, 0xbc, 0x1f, 0xb1, 0x3e, 0xaf, 0xbb, 0x68, 0x57, 0x71, 0x90, 0x0f, + 0x0e, 0x96, 0x45, 0xaa, 0xad, 0x98, 0xa0, 0xad, 0x98, 0xa0, 0xad, 0x98, 0xa0, 0xab, 0x48, 0xf4, 0x27, 0x3d, 0x90, + 0x52, 0x8c, 0xb2, 0xc5, 0xf1, 0xd4, 0xef, 0x40, 0xed, 0x01, 0xda, 0xc9, 0x5e, 0xa5, 0xec, 0x28, 0x75, 0x15, 0x3b, + 0x15, 0x18, 0x3b, 0x13, 0x9d, 0xb6, 0xe3, 0xe8, 0xdf, 0x51, 0x77, 0xbc, 0xac, 0x89, 0x65, 0xef, 0x76, 0x8a, 0x65, + 0xb0, 0x92, 0x46, 0x34, 0xdb, 0xb7, 0x41, 0x3d, 0x74, 0xff, 0xbe, 0x11, 0xcc, 0xaa, 0x48, 0x73, 0x0d, 0x48, 0xea, + 0x82, 0x14, 0x72, 0x6e, 0xa4, 0xb4, 0x02, 0xa5, 0x23, 0x1d, 0x17, 0xa0, 0xa1, 0xf4, 0x0a, 0xca, 0x32, 0x20, 0x6a, + 0xc3, 0x00, 0x44, 0x59, 0x19, 0xcd, 0xca, 0x6a, 0xa7, 0x20, 0xba, 0x80, 0x26, 0xcc, 0x48, 0x2c, 0xd0, 0x80, 0x30, + 0x0d, 0x08, 0x57, 0x19, 0xc4, 0x19, 0x97, 0x7d, 0x62, 0xb2, 0x95, 0xc9, 0x56, 0x65, 0xb6, 0xf4, 0xd9, 0x56, 0x48, + 0x94, 0x26, 0x5b, 0x96, 0xd9, 0x20, 0xb3, 0xe1, 0x49, 0xaa, 0xf0, 0x30, 0x95, 0x56, 0x54, 0xab, 0x64, 0xab, 0xb7, + 0x34, 0xd4, 0xe6, 0x1e, 0x1c, 0xc4, 0xa5, 0x9c, 0x64, 0xd4, 0xc4, 0xf7, 0x96, 0x3c, 0x29, 0x8c, 0x0c, 0xc4, 0x93, + 0x89, 0xfb, 0x3b, 0x5c, 0x6f, 0xca, 0x4a, 0xc5, 0x64, 0xf8, 0x8d, 0x92, 0xe8, 0x2f, 0xaf, 0x44, 0x7d, 0xc4, 0x4d, + 0x28, 0x9d, 0x0b, 0x92, 0xb4, 0x5a, 0xc7, 0xed, 0xe3, 0xd6, 0x79, 0x8f, 0x1f, 0xb6, 0x3b, 0xc9, 0x83, 0x4e, 0x6a, + 0x14, 0x11, 0x73, 0x79, 0x03, 0x0a, 0x98, 0xa3, 0x4e, 0x72, 0x82, 0x0e, 0xdb, 0x49, 0xeb, 0xf4, 0xb4, 0x09, 0xff, + 0xe0, 0xf7, 0xba, 0xac, 0x76, 0xd2, 0x3a, 0x39, 0xed, 0xf1, 0xa3, 0x8d, 0x4a, 0x31, 0x6f, 0x40, 0x41, 0x74, 0x64, + 0x2a, 0x61, 0xa8, 0x5f, 0x2d, 0xef, 0xb3, 0x2d, 0x3d, 0xcf, 0x7b, 0x1d, 0x2b, 0xab, 0x8a, 0x03, 0xa8, 0xfa, 0xaf, + 0x89, 0x01, 0xa2, 0xff, 0x1a, 0x96, 0xe1, 0x6e, 0x97, 0x05, 0x88, 0xda, 0x8f, 0x78, 0x2c, 0x1a, 0xec, 0x30, 0xb6, + 0xf9, 0x1a, 0xea, 0x36, 0x21, 0x04, 0x1d, 0x9e, 0xb8, 0x5c, 0x15, 0xe6, 0x4e, 0x10, 0x6a, 0x2a, 0xc8, 0x1d, 0xba, + 0x5c, 0x19, 0xe6, 0x0e, 0x11, 0x6a, 0x4a, 0xc8, 0xa5, 0x29, 0x4f, 0x28, 0xe4, 0xe8, 0x84, 0x36, 0x0d, 0x24, 0xab, + 0x45, 0x79, 0xce, 0xfc, 0xb0, 0xf9, 0x18, 0x96, 0xc7, 0x10, 0x14, 0x27, 0x48, 0x0b, 0x78, 0xa6, 0xa4, 0xd4, 0xe6, + 0xb4, 0x70, 0xa9, 0xc6, 0x81, 0x8c, 0x06, 0xfc, 0x73, 0xc8, 0xcc, 0xdb, 0x15, 0xad, 0xde, 0xf1, 0x59, 0x2b, 0x6d, + 0x83, 0xbf, 0x35, 0xc8, 0xda, 0xc2, 0xca, 0xda, 0xc2, 0xcb, 0xda, 0xc2, 0xcb, 0xda, 0x20, 0xc0, 0x07, 0x7d, 0xff, + 0x23, 0x6b, 0x36, 0x2c, 0xbc, 0x34, 0x88, 0xb1, 0x16, 0x0f, 0xb1, 0x5e, 0xad, 0x96, 0x6b, 0x30, 0x57, 0x2a, 0x6b, + 0x48, 0x55, 0xa9, 0x3f, 0x97, 0x45, 0xda, 0xc2, 0x93, 0x14, 0xb4, 0xdc, 0x2d, 0x4c, 0xcd, 0xe6, 0xf6, 0x54, 0x61, + 0x33, 0x14, 0x4e, 0xcf, 0xab, 0x93, 0x2f, 0xc9, 0xb1, 0xd1, 0x1e, 0x2f, 0x8b, 0x94, 0x5b, 0x9a, 0xc1, 0x2d, 0xcd, + 0xe0, 0x96, 0x66, 0x40, 0x23, 0xb8, 0x2c, 0x6c, 0xca, 0x26, 0x94, 0xc0, 0x95, 0x40, 0xff, 0x78, 0x00, 0x91, 0x00, + 0x63, 0x4d, 0xcc, 0xa8, 0x37, 0x3a, 0x6f, 0x43, 0xe4, 0x33, 0x5b, 0x52, 0x27, 0xd4, 0x38, 0x80, 0x97, 0x63, 0xfe, + 0x5a, 0x43, 0xfb, 0x04, 0x9e, 0xa5, 0x79, 0xa8, 0xe3, 0x16, 0xd8, 0x7f, 0x44, 0x45, 0xd4, 0x33, 0x64, 0x21, 0x35, + 0x3a, 0x1b, 0x67, 0xd7, 0xfd, 0x79, 0xc3, 0x9d, 0xd6, 0x52, 0x82, 0xf0, 0x31, 0x86, 0xcf, 0xac, 0xf2, 0xef, 0x2f, + 0xcd, 0x56, 0x9d, 0xcd, 0x99, 0x3d, 0x12, 0xba, 0x60, 0x7b, 0xee, 0x03, 0x47, 0xf5, 0x04, 0x91, 0xca, 0x78, 0x3e, + 0x92, 0x2a, 0xf4, 0x31, 0x78, 0x02, 0x93, 0x5b, 0x6a, 0xfc, 0x62, 0x5e, 0xd8, 0x3f, 0x5f, 0x69, 0xe0, 0x38, 0x58, + 0x4c, 0x86, 0xde, 0xdf, 0xf6, 0xda, 0x04, 0x08, 0x22, 0xfb, 0xfb, 0xd6, 0x2c, 0xdc, 0x7c, 0x6d, 0xda, 0x85, 0x9b, + 0x44, 0x93, 0x0d, 0x3b, 0xd4, 0xaf, 0xd1, 0x3f, 0xde, 0xed, 0xad, 0x98, 0x0c, 0x51, 0x40, 0xb3, 0x0d, 0x58, 0x55, + 0x05, 0x2c, 0xe5, 0xea, 0x95, 0xde, 0x90, 0xd0, 0xbb, 0x19, 0xf3, 0xba, 0x98, 0x0c, 0x77, 0xbe, 0x5f, 0x62, 0x7b, + 0xec, 0xbd, 0xa5, 0x41, 0x0f, 0x5e, 0xb5, 0x3d, 0x65, 0xb7, 0xdf, 0xab, 0x73, 0xb3, 0xb3, 0x8e, 0xca, 0xbf, 0x57, + 0xe7, 0xe9, 0xae, 0x3a, 0x33, 0x7e, 0x1b, 0xfb, 0xbd, 0xa3, 0x03, 0x35, 0xb6, 0xb1, 0x35, 0x9a, 0x0c, 0x21, 0xe0, + 0x3c, 0xfc, 0xb5, 0x61, 0x61, 0xba, 0x9e, 0x84, 0xc3, 0x2a, 0xc8, 0x5e, 0x72, 0x9a, 0x32, 0x4c, 0x49, 0xe7, 0xb0, + 0x30, 0x81, 0x61, 0x44, 0x42, 0x9b, 0x2a, 0xa1, 0x38, 0x27, 0x71, 0x4c, 0x0f, 0x33, 0x08, 0x6f, 0xd3, 0xee, 0xd1, + 0x34, 0xa6, 0x8d, 0x0c, 0x1d, 0xc5, 0xed, 0x06, 0x3d, 0xcc, 0x10, 0x6a, 0xb4, 0x41, 0x67, 0x2a, 0x49, 0xbb, 0x99, + 0x43, 0xc0, 0x4b, 0x43, 0x8a, 0xf3, 0x43, 0x91, 0x14, 0x0d, 0x79, 0xa8, 0x92, 0xa2, 0x91, 0x9c, 0x62, 0x91, 0x4c, + 0xca, 0xe4, 0x89, 0x49, 0x9e, 0xd8, 0xe4, 0x61, 0x99, 0x3c, 0x34, 0xc9, 0x43, 0x9b, 0x4c, 0x49, 0x71, 0x28, 0x12, + 0xda, 0x88, 0xdb, 0xcd, 0x02, 0x1d, 0xc2, 0x08, 0xfc, 0xe8, 0x89, 0x08, 0xe3, 0x8c, 0xaf, 0x8d, 0xa1, 0xce, 0x5c, + 0xe6, 0x2e, 0xf2, 0x67, 0x05, 0xa4, 0xd2, 0x7b, 0x0a, 0xea, 0x3c, 0x0b, 0xc0, 0x84, 0xb5, 0xfd, 0xe3, 0xe3, 0xda, + 0xad, 0xb3, 0x5c, 0x8a, 0xc0, 0x3b, 0x0c, 0x0c, 0xda, 0x3f, 0x3b, 0x9f, 0x18, 0x80, 0xea, 0x9a, 0xe6, 0xf3, 0x29, + 0xdd, 0x72, 0xc1, 0x2d, 0x26, 0x43, 0xb7, 0xb3, 0xca, 0x66, 0x18, 0x2d, 0x6c, 0xbc, 0xe8, 0xba, 0xb3, 0x24, 0x80, + 0xda, 0x3b, 0x68, 0x26, 0xd4, 0x28, 0xc9, 0x6d, 0x8d, 0x49, 0xc1, 0xee, 0x54, 0x46, 0x73, 0x16, 0x57, 0x07, 0x70, + 0x35, 0x4c, 0x46, 0x5e, 0x80, 0x59, 0x7d, 0x71, 0x98, 0x1c, 0x37, 0x74, 0x32, 0x39, 0x4c, 0x4e, 0x1f, 0x34, 0x74, + 0x32, 0x3c, 0x4c, 0xda, 0xed, 0x0a, 0x67, 0x93, 0x82, 0xe8, 0x64, 0x42, 0x34, 0x68, 0x0c, 0x6d, 0xa3, 0x72, 0x4e, + 0xc1, 0x4e, 0xec, 0xdf, 0x18, 0x46, 0xc3, 0x0d, 0x43, 0xb0, 0x89, 0x0d, 0x9d, 0xb9, 0x35, 0x86, 0xb0, 0x9b, 0xce, + 0xe9, 0x69, 0x53, 0x27, 0x05, 0xd6, 0x76, 0x25, 0x9b, 0x3a, 0x99, 0x60, 0x6d, 0x97, 0xaf, 0xa9, 0x93, 0xa1, 0x6d, + 0xca, 0xe8, 0x00, 0x99, 0x08, 0x80, 0xf5, 0x9c, 0x05, 0x90, 0xef, 0x78, 0x4f, 0x97, 0x35, 0x68, 0x0d, 0xbf, 0x57, + 0xae, 0xe9, 0x0b, 0x2a, 0xaa, 0xc1, 0x5e, 0x88, 0x7d, 0xab, 0x68, 0xbb, 0x6a, 0x92, 0xfd, 0xeb, 0xb2, 0x65, 0xb3, + 0x85, 0xd4, 0xf5, 0x82, 0x0f, 0x6b, 0x18, 0xe2, 0x4a, 0xb9, 0x83, 0xfb, 0x15, 0x25, 0x31, 0x04, 0xc8, 0x33, 0xa7, + 0x10, 0x27, 0x5e, 0x8f, 0x0c, 0x49, 0xbc, 0xd1, 0x58, 0xa3, 0x38, 0x38, 0x6f, 0x9f, 0x86, 0x54, 0x75, 0x2b, 0x6a, + 0x1e, 0x21, 0xd1, 0x42, 0x58, 0xbb, 0xca, 0x51, 0x14, 0xb0, 0x20, 0x4e, 0xbb, 0x5b, 0x3b, 0x20, 0x0e, 0x0e, 0x36, + 0xcf, 0x0b, 0xff, 0x7e, 0xc1, 0xd6, 0x9b, 0x05, 0x95, 0x51, 0x9e, 0x7f, 0x55, 0xc9, 0x9a, 0xeb, 0xf2, 0x00, 0x51, + 0x7c, 0xfc, 0xaa, 0xfb, 0x86, 0xc2, 0xf7, 0xab, 0xe0, 0x7d, 0x2e, 0xa7, 0x79, 0x66, 0x32, 0x4c, 0x5f, 0x83, 0x60, + 0x6c, 0x6f, 0xc2, 0x09, 0x95, 0x06, 0x87, 0xff, 0xb2, 0xe3, 0xa0, 0x13, 0xf7, 0xea, 0x4b, 0xd8, 0xe8, 0xdf, 0xa1, + 0x79, 0x6f, 0x05, 0x1b, 0xe7, 0xd8, 0xbd, 0x5a, 0xd5, 0xde, 0xf8, 0xb1, 0x2f, 0xc9, 0xa0, 0x83, 0x03, 0xae, 0x9e, + 0x81, 0x45, 0x32, 0x8b, 0x1b, 0xe1, 0xe1, 0xfb, 0x4f, 0xed, 0xb4, 0xfe, 0xdb, 0x9c, 0xab, 0x69, 0x70, 0xd0, 0x3d, + 0xac, 0xe5, 0xef, 0x5c, 0x89, 0x9e, 0x4e, 0xb9, 0x5b, 0xeb, 0xbf, 0x2b, 0x7b, 0xef, 0xad, 0xd7, 0xa6, 0x0e, 0x0e, + 0x78, 0x15, 0xf3, 0x29, 0xfa, 0x21, 0x42, 0x3d, 0x23, 0x83, 0x3c, 0xcb, 0x25, 0x85, 0x1b, 0x51, 0xb8, 0x62, 0x48, + 0x1b, 0xfc, 0x48, 0xe3, 0x3f, 0xe4, 0xff, 0xa7, 0x46, 0x0e, 0x75, 0xda, 0xe0, 0x81, 0x00, 0x16, 0xb2, 0x42, 0x55, + 0xb4, 0x45, 0x03, 0xe9, 0xd0, 0x7c, 0x1b, 0x95, 0x87, 0x39, 0x9d, 0xcf, 0xf3, 0x3b, 0xf3, 0xe0, 0x56, 0xc0, 0x51, + 0x55, 0x17, 0x4d, 0x2e, 0xd4, 0x1d, 0x2e, 0x80, 0xa7, 0x07, 0xdc, 0x43, 0xc6, 0x55, 0xb5, 0xbc, 0xdc, 0x16, 0x08, + 0x24, 0x33, 0x45, 0x64, 0xb3, 0xdd, 0x55, 0x97, 0x20, 0x97, 0x35, 0x9b, 0x48, 0xbb, 0x08, 0xe0, 0x98, 0x83, 0x4c, + 0xa6, 0xac, 0x3b, 0xea, 0x9e, 0x2d, 0x08, 0x92, 0x9b, 0x34, 0x22, 0xdb, 0xee, 0x52, 0x7c, 0x1c, 0x03, 0x1a, 0x21, + 0x2b, 0xf0, 0x85, 0xc2, 0x22, 0x07, 0xae, 0xb3, 0xf0, 0x1d, 0x7f, 0xa3, 0xa5, 0xa2, 0xaf, 0x06, 0x03, 0x5c, 0x98, + 0x37, 0x26, 0xca, 0xf9, 0x14, 0x2a, 0x78, 0xb3, 0x28, 0x10, 0x51, 0xf8, 0x6a, 0xb5, 0x0f, 0x4f, 0x02, 0xb9, 0x36, + 0xc1, 0x7f, 0xd5, 0xfd, 0xac, 0x9e, 0xff, 0x80, 0x71, 0x30, 0xd2, 0x32, 0x17, 0x85, 0x4e, 0xde, 0x64, 0x17, 0xa2, + 0xdb, 0x68, 0x30, 0x13, 0xad, 0x89, 0x40, 0x68, 0x36, 0x70, 0x2e, 0x84, 0x3f, 0x36, 0x00, 0x93, 0x62, 0x36, 0x8c, + 0x1d, 0xc4, 0xd7, 0xae, 0x25, 0xac, 0x56, 0xca, 0x86, 0x49, 0x31, 0x39, 0x36, 0x60, 0x4a, 0xd9, 0x4f, 0x19, 0x8f, + 0xb5, 0x32, 0xe3, 0xe0, 0x6e, 0xab, 0xbf, 0xad, 0xf6, 0xf3, 0x1e, 0xb7, 0xd7, 0x78, 0xdc, 0x04, 0x1f, 0x30, 0x80, + 0x5a, 0x6e, 0x6c, 0x70, 0x6b, 0x2c, 0x1f, 0x5b, 0xcb, 0x5e, 0xb6, 0x09, 0x41, 0x51, 0x3a, 0xdb, 0xdb, 0x9b, 0x5b, + 0x1f, 0x7c, 0x50, 0x99, 0x39, 0x29, 0xa4, 0xfb, 0x20, 0x47, 0x0f, 0x08, 0x74, 0x6e, 0x7f, 0x56, 0x74, 0xa1, 0x92, + 0x89, 0xcb, 0x31, 0xfe, 0x12, 0xdc, 0xe6, 0xf5, 0xa3, 0xeb, 0x6b, 0xb3, 0xc9, 0xaf, 0xaf, 0x23, 0x1c, 0x5a, 0xa8, + 0x47, 0x01, 0x2f, 0x18, 0x0d, 0xca, 0xf8, 0x54, 0x66, 0xe3, 0x37, 0xdb, 0x55, 0x63, 0xef, 0x69, 0x85, 0x77, 0xb0, + 0x3c, 0xa6, 0xf1, 0x2d, 0x8f, 0xce, 0x3e, 0x07, 0x78, 0xb3, 0x3e, 0x1f, 0x74, 0xdf, 0xc4, 0x0a, 0x1d, 0x1c, 0xbc, + 0x89, 0x25, 0xea, 0x5d, 0x31, 0x73, 0xe7, 0x06, 0x2e, 0xdd, 0x7d, 0x6e, 0x86, 0x2f, 0x03, 0x04, 0xb8, 0x62, 0x9b, + 0x92, 0xcd, 0x5b, 0x13, 0x40, 0x23, 0x85, 0x00, 0xdd, 0x10, 0x26, 0xd8, 0x81, 0x04, 0x7a, 0x7d, 0x13, 0x42, 0xbb, + 0xcb, 0x08, 0x03, 0x16, 0xbe, 0x74, 0xb8, 0x63, 0xc9, 0x8c, 0x15, 0x13, 0x56, 0xac, 0x56, 0xef, 0xa9, 0x75, 0xa2, + 0xdb, 0x88, 0xf7, 0xa8, 0xba, 0x8d, 0x06, 0x35, 0xe3, 0x07, 0xf1, 0x81, 0x0e, 0xf0, 0xfe, 0x9b, 0xb8, 0x40, 0x08, + 0x2c, 0x8c, 0xb8, 0x58, 0x78, 0x87, 0xb1, 0xac, 0xb6, 0x2e, 0x05, 0x2a, 0x1b, 0xc9, 0x49, 0x0b, 0x4f, 0x49, 0x56, + 0xae, 0xd1, 0xc5, 0xb4, 0xdb, 0x68, 0xe4, 0x48, 0xc6, 0x59, 0x3f, 0x1f, 0x60, 0x8e, 0x0b, 0xb8, 0x4c, 0xdd, 0x5e, + 0x87, 0x39, 0xab, 0x51, 0x2e, 0x37, 0xdf, 0xa5, 0x1d, 0x6b, 0xfa, 0x88, 0xae, 0x03, 0x60, 0x3c, 0xa2, 0x01, 0x91, + 0xd8, 0x05, 0x64, 0x61, 0x81, 0xac, 0x3c, 0x90, 0x85, 0x01, 0xb2, 0x42, 0xbd, 0x39, 0x44, 0x3e, 0x52, 0x28, 0xdd, + 0xa2, 0xe8, 0xf5, 0x18, 0x9d, 0xce, 0xff, 0x03, 0x73, 0x13, 0x26, 0xc2, 0x2d, 0x07, 0xf8, 0x82, 0x38, 0x97, 0x42, + 0x45, 0x96, 0x51, 0x64, 0xc2, 0xd5, 0xe2, 0x5b, 0xf3, 0x27, 0xb9, 0xc5, 0x77, 0xf6, 0xc7, 0x5d, 0xa0, 0x4c, 0x7a, + 0x5e, 0xd3, 0x36, 0x70, 0x17, 0xdc, 0x2d, 0x4a, 0x22, 0x40, 0x6b, 0x17, 0xa9, 0x50, 0xd4, 0x1f, 0x6f, 0x53, 0x36, + 0xa6, 0x84, 0x68, 0x10, 0x85, 0x45, 0x40, 0x3a, 0xff, 0xfc, 0x33, 0x42, 0x3d, 0x01, 0x21, 0x81, 0xdc, 0xc9, 0xd6, + 0x6c, 0xa3, 0x46, 0x94, 0x44, 0x69, 0xec, 0x23, 0x4e, 0xc0, 0xce, 0x88, 0xa2, 0xe0, 0xe1, 0x96, 0x72, 0x18, 0x1f, + 0x6a, 0xc3, 0x30, 0x83, 0xaa, 0x62, 0x68, 0x5c, 0x2e, 0x37, 0x23, 0x16, 0x19, 0xa8, 0x0a, 0x13, 0x2e, 0x06, 0xd9, + 0xd7, 0xcc, 0x18, 0x61, 0x07, 0x07, 0xac, 0x2f, 0x06, 0xc1, 0xf3, 0x64, 0xd5, 0x75, 0xb8, 0x0e, 0x17, 0x2e, 0xa6, + 0x10, 0x32, 0x7e, 0xb5, 0xb2, 0x7f, 0xc9, 0x07, 0x23, 0xcd, 0xc0, 0x3b, 0x73, 0xc1, 0x19, 0x2b, 0x76, 0xcb, 0x62, + 0x89, 0x96, 0xbf, 0x83, 0xd9, 0x9e, 0x0b, 0x00, 0xc8, 0xdd, 0x54, 0xdb, 0x1e, 0xea, 0x73, 0xa3, 0x51, 0x08, 0xc2, + 0xef, 0x56, 0x47, 0x1a, 0x9e, 0xeb, 0x30, 0xaf, 0x16, 0x46, 0x37, 0x53, 0x65, 0x34, 0x54, 0x38, 0x52, 0x12, 0x30, + 0x41, 0x37, 0x74, 0x12, 0x7e, 0xd4, 0xa9, 0xa4, 0x63, 0x21, 0x01, 0x0a, 0x1c, 0x99, 0xcb, 0x79, 0x13, 0xed, 0x9e, + 0xa1, 0x1d, 0x44, 0x2e, 0x30, 0xa1, 0xa9, 0xcb, 0x96, 0x2e, 0x2c, 0x55, 0x34, 0x93, 0x0b, 0xc5, 0x16, 0x73, 0x38, + 0xdf, 0xcb, 0xb4, 0x2c, 0xe7, 0xd9, 0xe7, 0x7a, 0x0a, 0x58, 0x3b, 0xde, 0xea, 0x19, 0x13, 0x8b, 0xc8, 0xcd, 0xf3, + 0xab, 0x15, 0xf7, 0xdf, 0xbc, 0xc0, 0x8f, 0x48, 0xe7, 0xf0, 0x2b, 0xfe, 0x48, 0xc9, 0xa3, 0xc6, 0x57, 0x3c, 0xe1, + 0xc4, 0xf2, 0x06, 0xc9, 0x9b, 0xd7, 0x57, 0x2f, 0xde, 0xbd, 0x78, 0xff, 0xf4, 0xfa, 0xc5, 0xab, 0x67, 0x2f, 0x5e, + 0xbd, 0x78, 0xf7, 0x11, 0xff, 0x43, 0xc9, 0xd7, 0xa3, 0xf6, 0x79, 0x0b, 0x7f, 0x20, 0x5f, 0x8f, 0x3a, 0xf8, 0x56, + 0x93, 0xaf, 0x47, 0x27, 0x38, 0x57, 0xe4, 0xeb, 0x61, 0xe7, 0xe8, 0x18, 0x2f, 0xb4, 0x6d, 0x32, 0x97, 0x93, 0x76, + 0x0b, 0xff, 0xe3, 0xbe, 0x40, 0xbc, 0xaf, 0x66, 0x31, 0x61, 0x1b, 0xc6, 0x0f, 0xa6, 0x0c, 0x1d, 0x2a, 0x63, 0x88, + 0x72, 0x11, 0xa0, 0xd3, 0x54, 0x85, 0xe8, 0x64, 0xe3, 0x31, 0x83, 0x0d, 0x23, 0xa0, 0x15, 0x27, 0xae, 0x1d, 0x7e, + 0xd4, 0x66, 0xc7, 0x40, 0x9f, 0x78, 0x29, 0x1c, 0x97, 0x2a, 0x9c, 0xb6, 0xd3, 0x62, 0x8c, 0x73, 0x29, 0x8b, 0x78, + 0x01, 0x8c, 0x80, 0xd1, 0x5a, 0xf0, 0xa3, 0x32, 0xf0, 0x93, 0xb8, 0x20, 0xed, 0x5e, 0x3b, 0x15, 0x17, 0xa4, 0xd3, + 0xeb, 0xc0, 0x9f, 0xd3, 0xde, 0x69, 0xda, 0x6e, 0xa1, 0xc3, 0x60, 0x1c, 0x7f, 0xd4, 0xd0, 0xba, 0x3f, 0xc0, 0xae, + 0x0b, 0xf5, 0x4f, 0xa1, 0xbd, 0x4a, 0x4f, 0x38, 0x75, 0x6c, 0xbb, 0x2b, 0x2e, 0x98, 0xd1, 0xc3, 0xf2, 0x1f, 0x00, + 0xb5, 0x8d, 0x6f, 0x4a, 0xb9, 0x71, 0xdc, 0x2f, 0x7e, 0x24, 0x50, 0x2d, 0xba, 0x4c, 0xcc, 0x56, 0x2d, 0x04, 0x4c, + 0xa3, 0xc9, 0x06, 0x73, 0xa0, 0x44, 0xc9, 0x42, 0xfb, 0x08, 0xf9, 0xaa, 0x29, 0x51, 0x32, 0x97, 0xf3, 0xb8, 0xa6, + 0x6a, 0xf8, 0x35, 0x30, 0x73, 0xdc, 0xe7, 0xea, 0x15, 0x7d, 0x15, 0xd7, 0x78, 0x9e, 0x90, 0xb5, 0x0b, 0xb7, 0xc5, + 0x2f, 0xce, 0x8a, 0xa2, 0x06, 0xae, 0x12, 0xb0, 0x7e, 0x54, 0x4d, 0x7d, 0x01, 0x4f, 0x01, 0xb2, 0x86, 0xbe, 0x24, + 0x01, 0xf5, 0xfc, 0xa9, 0x34, 0xe3, 0x2a, 0x95, 0xd1, 0x5e, 0x11, 0x6d, 0xcc, 0x82, 0xbc, 0x22, 0xfa, 0x42, 0x19, + 0x20, 0x48, 0xc2, 0xfb, 0x62, 0x00, 0x07, 0xbe, 0x1d, 0xa0, 0x34, 0x74, 0x0e, 0xd4, 0x4a, 0x95, 0x99, 0x90, 0xf9, + 0x34, 0x71, 0x0e, 0x40, 0xf3, 0x54, 0xa9, 0xa0, 0xcc, 0x27, 0x96, 0x28, 0x18, 0xfa, 0x6f, 0xe1, 0x06, 0x38, 0x8c, + 0x0d, 0x2a, 0x06, 0xd9, 0xf7, 0x44, 0x3d, 0xbf, 0x7d, 0xde, 0x3a, 0xfa, 0x1a, 0xe4, 0x8f, 0x94, 0xb7, 0xf7, 0xf8, + 0x3b, 0xa0, 0xe4, 0x36, 0x32, 0x57, 0x1b, 0xfb, 0xa0, 0x6a, 0xdd, 0x10, 0x20, 0x87, 0x1a, 0x1d, 0x99, 0x57, 0x11, + 0xbb, 0x48, 0x1f, 0x92, 0x76, 0x0b, 0x22, 0xa1, 0xed, 0xa0, 0x7c, 0x3f, 0x6d, 0xc0, 0x54, 0x27, 0xb7, 0x4d, 0xa0, + 0xd5, 0xf0, 0x50, 0xd2, 0x5d, 0x93, 0x27, 0x77, 0x58, 0x05, 0x38, 0xc3, 0x0e, 0x59, 0x43, 0x1c, 0x0a, 0xe4, 0x22, + 0xc8, 0xda, 0x0d, 0xa0, 0xa9, 0xe8, 0xd8, 0x07, 0xfb, 0xbc, 0x71, 0xd4, 0x45, 0x33, 0x39, 0x3d, 0xfc, 0x7a, 0x70, + 0x10, 0xcb, 0x06, 0x79, 0x84, 0xf0, 0x92, 0x82, 0x41, 0x36, 0x38, 0xb0, 0x71, 0xcb, 0xc4, 0xa7, 0x2a, 0xa0, 0x8e, + 0x0b, 0x55, 0x3b, 0xd6, 0xaa, 0xce, 0xca, 0xdd, 0xe0, 0xc7, 0xd4, 0x41, 0x8d, 0x20, 0xcd, 0x8e, 0xae, 0x53, 0x83, + 0x72, 0xcd, 0xdb, 0x0c, 0xb6, 0x65, 0xe3, 0x23, 0x45, 0x3f, 0x3c, 0x6a, 0x7e, 0x0d, 0x26, 0x5c, 0x33, 0x4d, 0x7a, + 0xd4, 0x78, 0x84, 0x7e, 0x78, 0x14, 0xf8, 0x0b, 0xf2, 0x8a, 0x3d, 0xf1, 0xdc, 0xc8, 0x4f, 0x96, 0x2b, 0xfd, 0x09, + 0x24, 0xfb, 0x82, 0xfc, 0x04, 0x58, 0x4e, 0xc9, 0x4f, 0xb1, 0x6c, 0x42, 0x1c, 0x45, 0xf2, 0x53, 0x5c, 0xc0, 0x8f, + 0x9c, 0xfc, 0x14, 0x03, 0xb6, 0xe3, 0xa9, 0xf9, 0x51, 0x94, 0xc0, 0x00, 0x1f, 0x35, 0x69, 0x5d, 0xd5, 0x8a, 0xd5, + 0x4a, 0x1c, 0x1c, 0x48, 0xfb, 0x8b, 0x5e, 0x66, 0x07, 0x07, 0xf9, 0xc5, 0xb4, 0xea, 0x9b, 0xe9, 0x5d, 0xf4, 0xc5, + 0x20, 0x14, 0x0e, 0x4c, 0xd3, 0x78, 0x38, 0xe3, 0x4f, 0x21, 0x65, 0x35, 0x0d, 0x34, 0x8f, 0x3b, 0xf7, 0xcf, 0xce, + 0x31, 0xfc, 0x7b, 0x3f, 0x28, 0xf8, 0x73, 0xc9, 0x77, 0x91, 0x36, 0x6b, 0x9e, 0x55, 0xc8, 0x76, 0x19, 0xe0, 0x33, + 0x66, 0xa8, 0x29, 0x0e, 0x0e, 0xf8, 0x45, 0x80, 0xcb, 0x98, 0xa1, 0x46, 0x60, 0xb1, 0xf7, 0xb0, 0xb4, 0x27, 0x33, + 0x5c, 0x13, 0xbc, 0x90, 0xcb, 0xfb, 0xc5, 0xe0, 0x42, 0x3b, 0x6a, 0x12, 0xc6, 0xd1, 0x56, 0xa4, 0xe5, 0x36, 0x59, + 0x57, 0x34, 0xd5, 0x65, 0xbb, 0x8b, 0x24, 0x51, 0x0d, 0x71, 0x79, 0xd9, 0xc6, 0xa0, 0x92, 0xef, 0x29, 0x22, 0x53, + 0x41, 0xbc, 0xaf, 0xdf, 0x32, 0x97, 0xa9, 0xc2, 0x53, 0x9e, 0x0a, 0x2f, 0x67, 0xbf, 0xf6, 0xd6, 0xd3, 0xc6, 0xfb, + 0xd2, 0xf4, 0xcc, 0xb0, 0xe8, 0xa9, 0xd2, 0x6b, 0x10, 0x36, 0xa9, 0x1a, 0xc0, 0x03, 0x84, 0x25, 0xe6, 0x31, 0xeb, + 0x2a, 0xc7, 0x20, 0xc0, 0xb3, 0x6a, 0xb4, 0x21, 0x13, 0x3e, 0xd7, 0xa9, 0x82, 0x81, 0x9a, 0xc2, 0x17, 0x40, 0xa6, + 0xb2, 0xca, 0x30, 0xdb, 0x37, 0x0c, 0x05, 0x04, 0x14, 0xb8, 0x24, 0x2c, 0x90, 0xe0, 0xe1, 0xf6, 0x23, 0x20, 0x1c, + 0x75, 0x72, 0x61, 0x27, 0x77, 0xa1, 0xa0, 0x3b, 0x31, 0xb8, 0xd0, 0x5d, 0x24, 0x1a, 0x0d, 0xc7, 0x6d, 0x5f, 0x0a, + 0x33, 0x88, 0x66, 0x7b, 0x70, 0xc9, 0xba, 0x48, 0x35, 0x9b, 0xa5, 0x01, 0xe4, 0x65, 0x6b, 0xb5, 0x52, 0x17, 0xbe, + 0x91, 0x9e, 0x3f, 0xc7, 0x0d, 0xdf, 0xe5, 0x05, 0xcf, 0xdf, 0x24, 0xe9, 0x47, 0x40, 0x55, 0x81, 0xcf, 0x96, 0xf3, + 0x08, 0x47, 0xe6, 0x6d, 0x3a, 0xf8, 0x6b, 0xde, 0x14, 0x8b, 0x70, 0xe4, 0x9e, 0xab, 0x8b, 0x06, 0xd5, 0x60, 0x79, + 0x56, 0x46, 0x5a, 0xe7, 0xc9, 0x35, 0x30, 0x0e, 0xfa, 0x6f, 0x85, 0x96, 0xd5, 0xef, 0x24, 0x77, 0x31, 0x47, 0x94, + 0x7f, 0x41, 0xcd, 0x8d, 0x6a, 0xbd, 0xdb, 0xcb, 0x93, 0xe3, 0xc8, 0x57, 0x85, 0x97, 0x08, 0xbe, 0xf3, 0x84, 0x63, + 0xdb, 0xbd, 0x1c, 0xbe, 0x2c, 0x7b, 0x00, 0xce, 0x7b, 0xbd, 0x46, 0xf8, 0x37, 0xb9, 0xf3, 0x19, 0xe1, 0xe8, 0x5a, + 0x8a, 0x27, 0x54, 0xd3, 0xa8, 0xf1, 0xc6, 0x18, 0xbe, 0x59, 0x39, 0xab, 0xfb, 0xad, 0x71, 0xb0, 0x7f, 0xab, 0x7b, + 0x88, 0x02, 0x51, 0x7b, 0xf1, 0xc8, 0xca, 0xbe, 0x26, 0xf6, 0x87, 0x0c, 0x4c, 0xdf, 0x76, 0xc0, 0xc3, 0x8f, 0x91, + 0x82, 0x6f, 0xb2, 0xe5, 0x93, 0x28, 0x84, 0x57, 0xad, 0x79, 0x44, 0x43, 0x8a, 0xed, 0xc3, 0x78, 0xcf, 0xae, 0x51, + 0xc8, 0x75, 0x8f, 0x55, 0x9d, 0x98, 0x56, 0xdd, 0x18, 0xa9, 0x83, 0x6d, 0xb2, 0xe0, 0xac, 0xea, 0xdd, 0x48, 0x28, + 0xd5, 0xe3, 0x70, 0xe6, 0x81, 0xcf, 0x66, 0xdb, 0xbc, 0x98, 0x6c, 0x9f, 0x90, 0x53, 0x60, 0xc8, 0xbb, 0x5f, 0x46, + 0xbe, 0xba, 0x84, 0x63, 0x37, 0x0e, 0x20, 0x2b, 0xc9, 0xe5, 0xd2, 0x3d, 0xef, 0xc6, 0xfb, 0x72, 0xb0, 0x2e, 0x1f, + 0x7b, 0x0b, 0xf0, 0xa0, 0x1a, 0xa9, 0xc8, 0x42, 0xce, 0xc0, 0xbf, 0x94, 0x58, 0xd3, 0x0f, 0xf1, 0xaf, 0x70, 0xc0, + 0x57, 0x48, 0x9a, 0x5a, 0xf5, 0x13, 0x3c, 0xc2, 0x04, 0x0a, 0x6f, 0x5b, 0xf7, 0x93, 0x0c, 0xbd, 0x5d, 0xeb, 0x3a, + 0x15, 0xeb, 0x50, 0x5a, 0x57, 0xac, 0x94, 0x85, 0x83, 0xe3, 0x2e, 0x46, 0xeb, 0xd4, 0x39, 0x9f, 0xba, 0x97, 0x9b, + 0x1e, 0x0a, 0x70, 0x7c, 0xe1, 0x52, 0x3c, 0x2b, 0x20, 0x14, 0x57, 0xa8, 0x4f, 0xfb, 0x59, 0x86, 0x4f, 0x13, 0xf7, + 0xe1, 0x9e, 0xb0, 0xe4, 0x39, 0xcb, 0xe7, 0xb4, 0x61, 0x81, 0x14, 0x50, 0x28, 0x85, 0xc5, 0x6a, 0x15, 0x0b, 0x13, + 0xa0, 0xc1, 0xc5, 0xe7, 0x75, 0x0f, 0x71, 0x18, 0xfd, 0x1d, 0xd4, 0xc5, 0x5e, 0x3d, 0x62, 0x4c, 0x58, 0x51, 0x78, + 0xe9, 0xa4, 0xb2, 0xa0, 0xaf, 0x5d, 0x7d, 0x88, 0x6a, 0xca, 0xbd, 0xd8, 0xe8, 0x7b, 0xdf, 0xf1, 0x19, 0x93, 0x0b, + 0x78, 0x01, 0x09, 0x33, 0xa2, 0x98, 0xf6, 0xdf, 0x40, 0x41, 0xe0, 0x19, 0x1d, 0x1e, 0xe2, 0x23, 0xf0, 0x55, 0x9e, + 0xd6, 0xc9, 0xcc, 0xbf, 0xab, 0x11, 0x99, 0xb8, 0x97, 0x51, 0x2f, 0x02, 0x17, 0x21, 0x10, 0xa1, 0x08, 0x89, 0x98, + 0x18, 0x45, 0xbd, 0xc8, 0xf8, 0x5b, 0x45, 0x60, 0x35, 0x06, 0x4a, 0xee, 0x08, 0xcf, 0x55, 0x45, 0xc4, 0xc2, 0x9a, + 0x3a, 0xa8, 0xc4, 0x52, 0x63, 0xa6, 0x7d, 0xd4, 0xa9, 0x40, 0x58, 0x64, 0x9b, 0x82, 0xb2, 0xde, 0x50, 0x17, 0x60, + 0x49, 0x8c, 0xe9, 0x2d, 0x4f, 0xae, 0x81, 0x9b, 0x63, 0x23, 0x57, 0x74, 0xc9, 0xaf, 0x40, 0x3d, 0x9d, 0x16, 0xf8, + 0xda, 0x30, 0x6c, 0xa3, 0x94, 0xae, 0x09, 0xc7, 0x19, 0x29, 0x12, 0x7a, 0x0b, 0x01, 0x2a, 0x66, 0x5c, 0xa4, 0x39, + 0x9e, 0xd1, 0xdb, 0x74, 0x8a, 0x67, 0x5c, 0x3c, 0xb1, 0xcb, 0x9e, 0x8e, 0x20, 0xc9, 0x7f, 0x2c, 0xd6, 0xc4, 0xbc, + 0xaf, 0xf5, 0xbb, 0x62, 0xc5, 0x23, 0xe0, 0x55, 0x54, 0x8c, 0xba, 0x23, 0x63, 0x53, 0xce, 0x74, 0x65, 0xbc, 0xfe, + 0x5a, 0xc7, 0x14, 0x67, 0x38, 0x47, 0x49, 0x2e, 0x31, 0xeb, 0x89, 0xf4, 0x35, 0x04, 0xa7, 0xce, 0xb0, 0x7d, 0x9b, + 0x8b, 0xdf, 0xb2, 0xfc, 0x99, 0x2c, 0xde, 0x9b, 0x2d, 0x9f, 0x23, 0x28, 0x04, 0x2e, 0x2a, 0xa2, 0x09, 0xb7, 0x7b, + 0x8b, 0x9e, 0xac, 0x9a, 0xa2, 0xb7, 0xb6, 0x29, 0x37, 0xc4, 0x29, 0x44, 0xf5, 0x4d, 0xa6, 0xbc, 0xd1, 0xc6, 0xac, + 0xd7, 0xfa, 0x4e, 0xa3, 0x53, 0x54, 0x96, 0x44, 0x18, 0xd6, 0xaa, 0xa9, 0x52, 0x49, 0x44, 0x53, 0x39, 0x09, 0x6f, + 0x69, 0x80, 0x9d, 0x2a, 0x9c, 0xc9, 0x85, 0xd0, 0xa9, 0x0c, 0xf0, 0x86, 0x56, 0x9b, 0x6b, 0x79, 0x6b, 0x21, 0xa6, + 0xf1, 0x9d, 0xfd, 0xc1, 0xf0, 0xb5, 0x51, 0xf1, 0xbf, 0x05, 0xc3, 0x1e, 0x95, 0x0a, 0x80, 0x1f, 0x18, 0xce, 0x02, + 0xe4, 0x2c, 0x3f, 0x79, 0x0b, 0xe0, 0xb3, 0x2c, 0xe4, 0x1d, 0xa4, 0x32, 0x93, 0x7a, 0x07, 0xa9, 0x0c, 0x52, 0x8d, + 0x5b, 0xfa, 0xbe, 0xa8, 0x94, 0x45, 0x61, 0x83, 0x44, 0xe1, 0x52, 0x1d, 0x2c, 0x89, 0x48, 0xa0, 0x5d, 0x23, 0xca, + 0xcd, 0xb8, 0x80, 0xf8, 0x84, 0xd0, 0xb8, 0xfd, 0xa6, 0xb7, 0xf0, 0x7d, 0x67, 0xf3, 0x99, 0xcf, 0xbf, 0xb3, 0xf9, + 0xa6, 0x23, 0x8f, 0xf1, 0xf5, 0xdb, 0x4e, 0x63, 0x19, 0x2f, 0x1d, 0xd6, 0x7e, 0x28, 0x5f, 0x83, 0x69, 0x99, 0x57, + 0xb7, 0x49, 0x1b, 0x4f, 0x02, 0xa4, 0x6c, 0x56, 0x3c, 0x5c, 0x07, 0xb7, 0x5b, 0x87, 0x31, 0x6f, 0x92, 0x36, 0x42, + 0x87, 0x4e, 0xb8, 0x12, 0xb1, 0x91, 0x9c, 0x0e, 0x1f, 0x1d, 0xc1, 0xdd, 0xcb, 0x4c, 0x6d, 0xf8, 0x4a, 0xd9, 0x6a, + 0xcd, 0x76, 0xeb, 0x90, 0xef, 0xac, 0xd2, 0x68, 0xe3, 0x19, 0x23, 0x4b, 0x70, 0x2e, 0xa3, 0x85, 0x55, 0x35, 0x80, + 0x3f, 0xea, 0x0b, 0xf1, 0xdb, 0x82, 0x8e, 0xcc, 0xf7, 0xa1, 0x4d, 0x79, 0xbd, 0xd0, 0x3e, 0xa9, 0xc9, 0x61, 0x10, + 0x1d, 0xe4, 0x4a, 0x06, 0x39, 0x31, 0x3f, 0x22, 0xc9, 0x29, 0xba, 0x68, 0xf7, 0x92, 0xd3, 0x43, 0x7e, 0xc8, 0x53, + 0xe0, 0x61, 0xe3, 0xa6, 0xaf, 0xd0, 0x6c, 0xfb, 0x3a, 0x8f, 0x17, 0x43, 0x9e, 0xb9, 0xe6, 0xab, 0x0e, 0xca, 0x54, + 0x3b, 0x47, 0xc8, 0x02, 0x14, 0xf3, 0xbd, 0x04, 0xd9, 0xf5, 0x6e, 0x0e, 0x79, 0x0a, 0xfd, 0x40, 0xad, 0x8e, 0xad, + 0x55, 0x0e, 0xee, 0xb7, 0x05, 0x20, 0x98, 0xef, 0xa8, 0x36, 0x17, 0x9b, 0xde, 0x8c, 0xab, 0xce, 0x0e, 0x79, 0x35, + 0xc2, 0xb0, 0xcc, 0x76, 0x7f, 0x7e, 0x6a, 0x55, 0x97, 0x87, 0x01, 0x44, 0x7e, 0x5b, 0x70, 0x11, 0x76, 0x1a, 0x76, + 0xeb, 0x72, 0xc2, 0x4e, 0xeb, 0xb3, 0x0c, 0x8a, 0x6c, 0xf7, 0xba, 0x35, 0xd3, 0xfa, 0x6c, 0xaf, 0xc0, 0x47, 0x10, + 0x26, 0x65, 0x56, 0x3a, 0x83, 0x2b, 0xf4, 0xc3, 0x0f, 0xc8, 0xb5, 0xfe, 0x7a, 0xa1, 0x7d, 0x7e, 0x89, 0x08, 0x90, + 0x5d, 0x75, 0x5d, 0x56, 0x87, 0x3e, 0xca, 0x26, 0xbe, 0x1e, 0xf2, 0x60, 0xe5, 0x9e, 0xde, 0xce, 0x65, 0xea, 0xf1, + 0xb5, 0xd7, 0x4a, 0xb7, 0x90, 0x13, 0x88, 0x87, 0xeb, 0x2e, 0x2c, 0x0b, 0x72, 0x76, 0x73, 0x0b, 0x25, 0xc3, 0x89, + 0xfb, 0xd2, 0x1f, 0x98, 0xbd, 0x6e, 0xe0, 0x17, 0xc9, 0x29, 0x4c, 0x7d, 0xb3, 0x87, 0xc3, 0x0e, 0xf4, 0x61, 0xe0, + 0xb0, 0xd9, 0xa0, 0xcf, 0xac, 0x20, 0xf2, 0x98, 0x17, 0x16, 0xcf, 0x2e, 0x49, 0xbb, 0xc7, 0x53, 0xb7, 0x99, 0x8c, + 0x68, 0xd4, 0x6e, 0xf2, 0x60, 0x66, 0x80, 0x5f, 0xae, 0x6c, 0x58, 0xc4, 0xaf, 0x53, 0x00, 0x25, 0x5f, 0xac, 0x5a, + 0x9f, 0x0a, 0x5e, 0xf5, 0x86, 0xd3, 0xcd, 0x74, 0xbf, 0x6e, 0x70, 0xbb, 0xeb, 0xe1, 0x09, 0xaf, 0xb9, 0x58, 0xb4, + 0xf6, 0x13, 0x9f, 0x00, 0x07, 0x94, 0xb4, 0xee, 0x9f, 0x82, 0x0b, 0x65, 0x09, 0xcb, 0xed, 0x72, 0xb3, 0xad, 0x72, + 0x16, 0x8e, 0xb6, 0x64, 0xc0, 0x1d, 0x6c, 0x42, 0x14, 0x3a, 0x38, 0xec, 0xe0, 0xa4, 0xdd, 0xee, 0x9c, 0xe2, 0xe4, + 0xe4, 0x14, 0x06, 0xda, 0x48, 0x4e, 0x0f, 0x67, 0xca, 0x02, 0x30, 0xc8, 0x59, 0xbb, 0x76, 0x1f, 0x41, 0xe4, 0xa7, + 0x50, 0xbc, 0xe6, 0x87, 0x71, 0xdc, 0x4e, 0xee, 0xb7, 0xda, 0xa7, 0xe7, 0x0d, 0x00, 0x50, 0xd3, 0x7d, 0xb8, 0x1a, + 0xaf, 0x17, 0xba, 0x5e, 0xa5, 0x44, 0xf8, 0x7a, 0xb5, 0x86, 0xaf, 0xd6, 0x68, 0xaf, 0xab, 0x29, 0xf8, 0xaa, 0x4e, + 0x38, 0xb7, 0x45, 0xbc, 0xd2, 0x26, 0xdc, 0x16, 0xb1, 0x1d, 0x48, 0x0c, 0xd2, 0x79, 0x72, 0xda, 0x39, 0x45, 0x76, + 0x2c, 0xda, 0xe1, 0x47, 0xb9, 0x4f, 0xb6, 0x8a, 0x34, 0x34, 0x20, 0x49, 0x39, 0x3b, 0xb9, 0x00, 0x89, 0x9a, 0x93, + 0xcb, 0x76, 0x73, 0xc6, 0x12, 0x3f, 0x01, 0x93, 0x0a, 0xcb, 0x59, 0xae, 0x82, 0x4b, 0x0a, 0x00, 0x71, 0x01, 0xc6, + 0x45, 0xf7, 0x4f, 0x7b, 0xf7, 0x93, 0xd3, 0xb3, 0x8e, 0x25, 0x7a, 0xfc, 0xa2, 0x53, 0x4b, 0x33, 0x53, 0x4f, 0x4e, + 0x4d, 0x1a, 0x74, 0x9d, 0xdc, 0x3f, 0x85, 0x32, 0x2e, 0x25, 0x2c, 0x05, 0x11, 0x2b, 0xaa, 0x62, 0x10, 0xa6, 0x22, + 0xad, 0xe5, 0x9e, 0xd5, 0xb2, 0xcf, 0x4f, 0x8e, 0xef, 0x9f, 0x86, 0x50, 0x2b, 0x67, 0x61, 0x16, 0xda, 0x4d, 0xc4, + 0xcf, 0x0e, 0x96, 0x16, 0x1d, 0x26, 0xa7, 0xe9, 0xd6, 0x04, 0xed, 0xa6, 0x39, 0x34, 0x38, 0x10, 0x28, 0x1c, 0x9f, + 0x0a, 0xa7, 0x2f, 0x09, 0xee, 0xc7, 0x2a, 0x43, 0x93, 0x50, 0xe1, 0xec, 0xef, 0x29, 0x83, 0x47, 0x29, 0xc3, 0xab, + 0xca, 0xc7, 0x54, 0x7c, 0xa1, 0xea, 0x0d, 0x85, 0x30, 0x1c, 0x62, 0x10, 0xb9, 0x20, 0xe1, 0xf5, 0xdc, 0x9f, 0xc0, + 0x45, 0x98, 0x09, 0xb8, 0xd0, 0xf4, 0x4a, 0xd0, 0x8a, 0x17, 0x18, 0x86, 0x0e, 0xb5, 0x66, 0x58, 0x3d, 0x9e, 0x3a, + 0x93, 0x82, 0x50, 0xb7, 0xf5, 0x9c, 0x7f, 0xaf, 0x5c, 0x52, 0x5e, 0x65, 0x27, 0xa7, 0x28, 0x71, 0x97, 0xe5, 0x49, + 0x1b, 0x25, 0x81, 0x09, 0x89, 0x3b, 0x92, 0xb3, 0x8c, 0xf4, 0xa3, 0xdb, 0x08, 0x47, 0x77, 0x11, 0x8e, 0xac, 0x0f, + 0xf3, 0x07, 0x70, 0x10, 0x8f, 0x70, 0x64, 0x5d, 0x99, 0x23, 0x1c, 0x69, 0x26, 0x20, 0x3a, 0x57, 0x34, 0xc0, 0x39, + 0x94, 0x36, 0x9e, 0xd5, 0x65, 0xe9, 0xc7, 0xfe, 0xab, 0x74, 0xbd, 0xb6, 0x29, 0x81, 0x94, 0x39, 0x35, 0x3b, 0xd4, + 0xbe, 0x2e, 0x1d, 0x51, 0xcf, 0xac, 0x47, 0x18, 0x04, 0x10, 0x7a, 0xe7, 0x5f, 0xa7, 0xab, 0x02, 0x7b, 0xb0, 0x63, + 0x58, 0x69, 0xf0, 0x33, 0x8f, 0xc2, 0x33, 0x2c, 0xc2, 0x63, 0xe1, 0x0b, 0x83, 0x58, 0xe1, 0x7f, 0xe7, 0x52, 0xce, + 0xfd, 0x6f, 0x2d, 0xcb, 0x5f, 0xf0, 0xa6, 0x89, 0xb3, 0x68, 0x01, 0xcb, 0x2d, 0x1b, 0x47, 0x68, 0xc8, 0xea, 0x23, + 0xb8, 0x1e, 0xbb, 0x58, 0x6f, 0x20, 0x11, 0x5e, 0x1b, 0x81, 0xca, 0xcb, 0x87, 0xd7, 0x36, 0xee, 0x90, 0xf9, 0x84, + 0xc0, 0x63, 0x10, 0x5b, 0x58, 0xc2, 0x85, 0xc6, 0xa4, 0x60, 0x4a, 0x45, 0x36, 0x20, 0x5f, 0x24, 0x85, 0x7f, 0x61, + 0xd1, 0xa7, 0x8c, 0x45, 0x64, 0x3a, 0xac, 0xcf, 0xd6, 0x8a, 0xc3, 0xb9, 0x2c, 0x54, 0x6a, 0x9f, 0x5b, 0xf1, 0x60, + 0x9c, 0x97, 0x6f, 0x19, 0xa6, 0x79, 0xb6, 0xc6, 0xf6, 0x0e, 0xbb, 0x2c, 0xe4, 0xae, 0xb4, 0xc3, 0x52, 0x59, 0xb6, + 0xfe, 0xd6, 0x84, 0x54, 0x6d, 0x46, 0xc1, 0x44, 0xab, 0x01, 0x55, 0x51, 0x39, 0xa0, 0xb0, 0x8d, 0xec, 0x92, 0x2e, + 0xcb, 0x92, 0xe9, 0xb2, 0x5c, 0x86, 0x93, 0x56, 0x6b, 0xbd, 0xc6, 0x05, 0x33, 0x11, 0x66, 0x76, 0x96, 0x80, 0x7c, + 0x35, 0x95, 0x37, 0x41, 0xae, 0x4a, 0xcb, 0x59, 0x9a, 0x25, 0x8a, 0x02, 0x23, 0xd8, 0x68, 0x8d, 0xbf, 0x70, 0xc5, + 0x01, 0x9e, 0x6e, 0x76, 0x43, 0x29, 0x73, 0x46, 0x21, 0x10, 0x59, 0xd0, 0xe4, 0x1a, 0x4f, 0xf9, 0x88, 0xed, 0x6e, + 0x13, 0xcc, 0x98, 0xff, 0xbd, 0x16, 0x3d, 0x02, 0x59, 0x76, 0xcf, 0xa0, 0x0e, 0x2c, 0xe2, 0x0a, 0x3a, 0x08, 0x65, + 0xf0, 0x51, 0x88, 0x9b, 0x39, 0xbd, 0x93, 0x0b, 0x0d, 0x70, 0x59, 0x68, 0xf9, 0xc6, 0xc5, 0x3a, 0xd8, 0x6f, 0x61, + 0x1f, 0xf6, 0x60, 0x09, 0x21, 0x03, 0x5a, 0xd8, 0x86, 0xbb, 0x68, 0xe1, 0xa1, 0xd4, 0x5a, 0xce, 0xd2, 0x16, 0x36, + 0xb1, 0x27, 0x5a, 0xeb, 0x32, 0x40, 0xd8, 0x75, 0xf9, 0x2e, 0x65, 0xb5, 0x09, 0x16, 0x4e, 0x3a, 0xd4, 0x44, 0x07, + 0xb7, 0x87, 0x8c, 0xf0, 0xc6, 0xcf, 0x57, 0xaf, 0x5f, 0xb9, 0xf0, 0xcf, 0x7c, 0x0c, 0x2e, 0x9b, 0x4e, 0x35, 0x76, + 0x6d, 0x1e, 0x74, 0x8a, 0x2b, 0x45, 0xa9, 0x15, 0x4e, 0xa1, 0xe5, 0x17, 0x42, 0xe7, 0x89, 0xbd, 0xbc, 0x78, 0x26, + 0x8b, 0x19, 0xb5, 0x37, 0x46, 0xf8, 0x5a, 0xb9, 0x17, 0xdc, 0xcd, 0x23, 0x31, 0xd5, 0x24, 0xdf, 0x6d, 0x5e, 0x45, + 0x2c, 0x32, 0x23, 0xbf, 0x82, 0x36, 0xc0, 0x54, 0x2e, 0x1f, 0xe0, 0x2d, 0x88, 0x0b, 0xa2, 0x1f, 0x90, 0x97, 0xb7, + 0x96, 0xba, 0x44, 0x51, 0x83, 0x1b, 0xfc, 0x64, 0x05, 0xcf, 0x82, 0xeb, 0x42, 0xc3, 0x1e, 0x39, 0xf1, 0x22, 0x6a, + 0x45, 0xf5, 0x07, 0x6c, 0x8d, 0x2a, 0xc1, 0x07, 0x60, 0x4d, 0x72, 0x09, 0xa2, 0x47, 0xf9, 0x56, 0x1e, 0x07, 0xd1, + 0xc4, 0xdf, 0x3d, 0x5f, 0xb6, 0x3d, 0x9d, 0xcd, 0x2b, 0x75, 0x62, 0x79, 0x65, 0x02, 0x1e, 0x8e, 0xf6, 0x35, 0x1a, + 0x84, 0x83, 0x44, 0x56, 0x6a, 0x0f, 0x7d, 0x2e, 0xea, 0xc6, 0xf9, 0x45, 0x9b, 0x35, 0x4f, 0x56, 0xab, 0xfc, 0xb2, + 0xcd, 0xda, 0xa7, 0xf6, 0xed, 0xba, 0x48, 0x65, 0x40, 0x73, 0xf9, 0x98, 0x67, 0x11, 0x68, 0x67, 0xc7, 0x99, 0x09, + 0xa7, 0xe0, 0xa3, 0x2d, 0x93, 0x85, 0xae, 0xfa, 0x92, 0x60, 0x5c, 0x4a, 0xac, 0x1e, 0xbf, 0x40, 0xbd, 0x76, 0xba, + 0xed, 0x2a, 0xdd, 0x6c, 0x1f, 0x06, 0x17, 0x2e, 0x05, 0xc2, 0x1d, 0x08, 0x79, 0x00, 0xfa, 0xdd, 0xa5, 0x00, 0xd3, + 0x20, 0x40, 0x65, 0x05, 0x22, 0x2d, 0x9f, 0x2d, 0x66, 0xcf, 0x0a, 0x6a, 0x96, 0xe1, 0x09, 0x9f, 0x70, 0xad, 0x52, + 0x0a, 0xd2, 0xed, 0xae, 0xf4, 0xf5, 0x6e, 0x09, 0x2a, 0xab, 0x05, 0xb1, 0x4d, 0x34, 0xcf, 0x3e, 0x2b, 0xb7, 0x70, + 0x08, 0x9b, 0x95, 0x15, 0x38, 0x43, 0x6b, 0x9c, 0xcb, 0x09, 0x2d, 0xb8, 0x9e, 0xce, 0xfe, 0xad, 0xd5, 0x61, 0x7d, + 0x3d, 0x30, 0x17, 0x56, 0x00, 0x12, 0x2a, 0x46, 0xab, 0x15, 0x3f, 0xfa, 0xfe, 0x7d, 0x92, 0xf7, 0x09, 0x6f, 0xe3, + 0x0e, 0x3e, 0xc6, 0xa7, 0xb8, 0xdd, 0xc2, 0xed, 0x53, 0xb8, 0xba, 0xcf, 0xf2, 0xc5, 0x88, 0xa9, 0x18, 0x1e, 0x31, + 0xd3, 0x97, 0xc9, 0xf9, 0x61, 0x19, 0xba, 0x5f, 0x17, 0x89, 0x43, 0x97, 0x20, 0x82, 0xbc, 0x0b, 0xbd, 0x17, 0x45, + 0x61, 0xdc, 0xb7, 0x71, 0xa8, 0x3a, 0x29, 0xf5, 0x0b, 0x97, 0xc7, 0x3d, 0xb0, 0xe7, 0xb6, 0x2b, 0xdb, 0x04, 0xb3, + 0x6f, 0xfb, 0x33, 0xad, 0x7e, 0x36, 0x75, 0x89, 0x18, 0x1e, 0x7a, 0x15, 0x7a, 0xa0, 0x4b, 0xd2, 0x3e, 0x38, 0x00, + 0xab, 0xa3, 0x60, 0x36, 0xdc, 0x46, 0x3f, 0xe0, 0xcd, 0x5a, 0x1a, 0x04, 0x2b, 0x00, 0xe3, 0xce, 0x37, 0x9c, 0x2c, + 0x2d, 0x6c, 0x35, 0x50, 0x61, 0x5d, 0x84, 0xc1, 0xe9, 0x42, 0x52, 0x61, 0x84, 0x68, 0x38, 0xc2, 0x5c, 0xa4, 0x93, + 0xfd, 0x16, 0x96, 0xe3, 0xb1, 0x62, 0x1a, 0x8e, 0x8e, 0x82, 0x7d, 0x61, 0x85, 0x32, 0xa7, 0xc8, 0x90, 0x4d, 0xb8, + 0x78, 0xa8, 0x3f, 0xb1, 0x42, 0x9a, 0x4f, 0xa3, 0xc1, 0x48, 0x23, 0xb3, 0x8a, 0x11, 0xce, 0x72, 0x3e, 0x87, 0xaa, + 0x93, 0x02, 0x9c, 0x7e, 0xe0, 0x2f, 0x1f, 0xa5, 0x61, 0x9b, 0x40, 0xbe, 0x3e, 0xd8, 0x80, 0x2d, 0x78, 0x54, 0xd0, + 0x9b, 0xd7, 0xe2, 0x31, 0xec, 0xa8, 0x87, 0x05, 0xa3, 0x90, 0x0d, 0x49, 0xef, 0xa0, 0x29, 0xf8, 0x80, 0x36, 0x5f, + 0x1a, 0xc0, 0xa5, 0xe7, 0xe6, 0xc3, 0x56, 0xf4, 0x01, 0x10, 0x93, 0xb2, 0x2d, 0x93, 0x69, 0x4e, 0xe9, 0x2a, 0xd3, + 0x86, 0x98, 0x2a, 0xa7, 0xb0, 0xc6, 0x2e, 0xea, 0x49, 0x38, 0x98, 0x11, 0x55, 0xd3, 0xb4, 0x3f, 0x30, 0x7f, 0x5f, + 0xdb, 0x92, 0x2d, 0xec, 0xc2, 0xc9, 0xac, 0xb1, 0x79, 0x7d, 0x34, 0x28, 0xdf, 0xc6, 0x70, 0x0f, 0x0b, 0x4f, 0x60, + 0xd6, 0xc8, 0xe7, 0x89, 0x27, 0x9b, 0x27, 0xeb, 0xb5, 0x19, 0x88, 0x4a, 0x41, 0x0f, 0xf4, 0xd6, 0x6f, 0x9b, 0x16, + 0x6c, 0x8f, 0xf2, 0xeb, 0xb4, 0x85, 0x67, 0x1c, 0x5e, 0xf4, 0xf4, 0xed, 0x5d, 0xe9, 0x42, 0x7e, 0x76, 0x20, 0x69, + 0x05, 0x29, 0x76, 0x3a, 0x41, 0x67, 0xc7, 0x38, 0x18, 0x39, 0xd0, 0xf3, 0xab, 0xcf, 0x16, 0xd6, 0xfe, 0xf7, 0x9b, + 0xb2, 0xa0, 0x09, 0x96, 0x53, 0x4e, 0x28, 0xf3, 0xe7, 0xe7, 0x1b, 0x9e, 0x54, 0xa8, 0xe0, 0x9e, 0xc2, 0x82, 0x3d, + 0x6d, 0xa3, 0x65, 0xce, 0xe8, 0xdf, 0xf6, 0x87, 0x0d, 0xd0, 0x53, 0x6a, 0xd9, 0xb2, 0x42, 0x2a, 0xf5, 0xd0, 0xa6, + 0xd9, 0xa3, 0x07, 0x8e, 0xc8, 0x97, 0xd0, 0x05, 0xf0, 0xfa, 0xa3, 0x42, 0xce, 0x0d, 0x22, 0xb8, 0xdf, 0x6e, 0xdc, + 0xc6, 0x57, 0x00, 0xbc, 0x1d, 0xf6, 0xaa, 0x7f, 0x5a, 0xc0, 0xfe, 0x46, 0x65, 0x49, 0x3f, 0xde, 0x8e, 0x3d, 0xfe, + 0x0b, 0x09, 0xa1, 0xd7, 0x2d, 0x1e, 0x26, 0x0e, 0x9d, 0x4a, 0xd6, 0xac, 0xfc, 0xb9, 0x55, 0x12, 0x30, 0xac, 0x5e, + 0x30, 0x64, 0xe3, 0xb6, 0x8a, 0xdb, 0xcc, 0xff, 0xa0, 0x82, 0xc1, 0x82, 0x6f, 0x8d, 0xa4, 0x62, 0x59, 0xfc, 0xf6, + 0xa9, 0xf3, 0x5f, 0x75, 0x8e, 0x6b, 0x5f, 0xd7, 0x9e, 0xdb, 0x1c, 0x9a, 0x50, 0xc7, 0x11, 0x3a, 0x38, 0xd8, 0xc8, + 0xa0, 0x63, 0x00, 0x3c, 0x72, 0xec, 0x97, 0x5f, 0x3e, 0xcf, 0x8e, 0x19, 0xcd, 0x63, 0x11, 0x85, 0xcc, 0x9d, 0xe7, + 0xe6, 0xec, 0x44, 0x9e, 0x50, 0x35, 0xf5, 0x85, 0x01, 0x8e, 0x8f, 0xb6, 0x52, 0x01, 0xdf, 0xa3, 0xf5, 0x8e, 0x09, + 0x6c, 0xf0, 0x5b, 0x76, 0x52, 0xbb, 0x0a, 0xfa, 0x05, 0x5a, 0xee, 0x62, 0x2a, 0x37, 0x16, 0x38, 0xda, 0x9c, 0xc8, + 0xce, 0xa1, 0x6f, 0xd4, 0x29, 0x59, 0x8f, 0x27, 0xbb, 0x8d, 0xbe, 0xa4, 0xd8, 0x95, 0x5c, 0xd1, 0xb6, 0x21, 0xab, + 0x9e, 0xdc, 0xd5, 0x95, 0xa9, 0x53, 0x75, 0xcd, 0x5b, 0x59, 0xda, 0x94, 0x76, 0x49, 0xf6, 0x6e, 0x8b, 0x85, 0x57, + 0xe1, 0x8d, 0x46, 0x79, 0x11, 0x0a, 0xf6, 0x58, 0x62, 0xd0, 0xe5, 0x04, 0xae, 0x17, 0x56, 0xab, 0x18, 0xfe, 0xec, + 0x1a, 0xc3, 0x2e, 0xd3, 0xa5, 0x0f, 0x7c, 0x83, 0x5f, 0x09, 0xa2, 0xfe, 0x3a, 0x3b, 0x48, 0xb0, 0xee, 0x72, 0x83, + 0x86, 0xe3, 0xc4, 0x7f, 0xc1, 0x9b, 0xd3, 0xda, 0xbb, 0x1c, 0x4c, 0xb2, 0x6f, 0xbc, 0x53, 0x57, 0xb2, 0x96, 0xb5, + 0x90, 0xf1, 0x1b, 0x12, 0x0c, 0xb1, 0x9b, 0xd2, 0x39, 0x6e, 0x25, 0x6d, 0x14, 0xb9, 0x62, 0x15, 0xfa, 0x7f, 0xab, + 0x48, 0x66, 0x33, 0xff, 0xeb, 0xec, 0xec, 0xcc, 0xa5, 0x38, 0x9b, 0x3f, 0x65, 0x3c, 0xe0, 0x4c, 0x02, 0xfb, 0xc2, + 0x33, 0x66, 0x74, 0xc8, 0x6f, 0x61, 0x28, 0x44, 0x90, 0x4b, 0xe1, 0xd8, 0x25, 0x78, 0x32, 0x11, 0x28, 0x0f, 0xb0, + 0x7f, 0x4f, 0x36, 0xca, 0xf9, 0x37, 0x97, 0x7c, 0x4c, 0xe2, 0xb2, 0x41, 0xf6, 0xc5, 0x7c, 0xf6, 0xad, 0x99, 0x0c, + 0x3c, 0x33, 0x10, 0x61, 0xfb, 0xdb, 0xb0, 0xb4, 0xce, 0x52, 0x06, 0x47, 0x5a, 0x2e, 0xb2, 0xa9, 0xd5, 0xfc, 0xbb, + 0x0f, 0x53, 0xd6, 0xbd, 0xd7, 0x03, 0x41, 0xb9, 0xc8, 0xd2, 0x85, 0xd6, 0x8c, 0x7e, 0x2c, 0xa3, 0x68, 0xee, 0xbd, + 0x62, 0x0b, 0xf6, 0x23, 0xde, 0xab, 0x52, 0xe0, 0xe3, 0x61, 0xc1, 0x69, 0xfe, 0x23, 0xde, 0xab, 0xa2, 0x69, 0x82, + 0x2b, 0xa4, 0x09, 0x48, 0x89, 0xcd, 0xdb, 0xd4, 0x69, 0x24, 0x80, 0x82, 0xe6, 0x91, 0x39, 0xc8, 0x9e, 0xbb, 0x00, + 0x8c, 0x49, 0x07, 0xbb, 0xb8, 0x5f, 0x36, 0xac, 0xaa, 0x8d, 0x46, 0x0e, 0x41, 0xe9, 0xca, 0xd9, 0x98, 0xaf, 0x47, + 0x1b, 0x0b, 0x62, 0x94, 0xc9, 0xe4, 0xf2, 0x39, 0x8f, 0xb7, 0x16, 0x0b, 0x85, 0xd5, 0x82, 0x05, 0xaa, 0x55, 0xa9, + 0xd2, 0xc3, 0xe2, 0xdb, 0x05, 0xb3, 0xa0, 0x88, 0xd9, 0x7a, 0x0f, 0x6f, 0xb9, 0x22, 0x20, 0x25, 0xbb, 0x24, 0x78, + 0x5e, 0xdc, 0x60, 0xaa, 0x7f, 0x4d, 0x1e, 0x08, 0x3d, 0x53, 0x3a, 0xc2, 0x26, 0x4f, 0x41, 0x24, 0xb1, 0xfd, 0x16, + 0x76, 0xac, 0xd1, 0x0b, 0xe1, 0x85, 0x14, 0x38, 0x57, 0x4d, 0x13, 0x33, 0xca, 0x4d, 0x74, 0xb1, 0x87, 0x6a, 0xce, + 0x32, 0x6d, 0x11, 0x60, 0xdf, 0xa1, 0xa1, 0x14, 0xcf, 0x0d, 0x28, 0xcc, 0xbb, 0xd8, 0x2e, 0xe5, 0x31, 0x2c, 0x5e, + 0x90, 0x02, 0x44, 0x8d, 0x8b, 0x49, 0x59, 0x67, 0x9e, 0x2f, 0x26, 0x5c, 0x54, 0xc8, 0x50, 0x30, 0x35, 0x97, 0x02, + 0x9e, 0xa5, 0x28, 0x8b, 0x18, 0x3a, 0x54, 0xc3, 0x77, 0x4b, 0xc2, 0xca, 0x3a, 0xe6, 0x98, 0xe2, 0xa2, 0xaa, 0x01, + 0xcc, 0xc5, 0xc3, 0xf0, 0xfd, 0x7a, 0xf5, 0x5a, 0xbc, 0x93, 0xf3, 0x2a, 0xdf, 0xd3, 0x38, 0x1f, 0xfd, 0xdd, 0xd9, + 0x0d, 0xa3, 0xb5, 0x79, 0x39, 0x2a, 0xd8, 0xbe, 0x1f, 0x78, 0xf5, 0x9a, 0xda, 0xda, 0xbc, 0x3d, 0x55, 0x66, 0x0d, + 0x59, 0xf9, 0xd0, 0x42, 0xd5, 0x5e, 0xbd, 0xaa, 0x14, 0xb6, 0x22, 0x40, 0xa5, 0xe0, 0xa3, 0xad, 0xfc, 0x27, 0xda, + 0xe6, 0xdb, 0x73, 0xa8, 0x0c, 0x0f, 0xe4, 0xc9, 0x50, 0xd5, 0x03, 0x2e, 0xca, 0x0f, 0x01, 0x2c, 0x7e, 0x64, 0x82, + 0xf0, 0xee, 0xba, 0x40, 0xe6, 0x4c, 0xc5, 0x12, 0x2f, 0xfb, 0x74, 0x90, 0x5a, 0x79, 0x28, 0x95, 0x60, 0xdb, 0x73, + 0x53, 0x70, 0xed, 0xa3, 0xfd, 0xe2, 0x3e, 0x1b, 0xa4, 0xcb, 0x7a, 0x44, 0x60, 0x1b, 0x93, 0xd8, 0x9b, 0x73, 0x9a, + 0x10, 0xba, 0x74, 0x80, 0x73, 0x02, 0xb6, 0xc7, 0x9e, 0x3d, 0x7d, 0x13, 0x67, 0xa8, 0x57, 0xe7, 0xf0, 0x97, 0x6b, + 0x9c, 0xe3, 0x0c, 0xa5, 0x0f, 0x63, 0xb8, 0xc0, 0x5a, 0x63, 0x00, 0x5f, 0x66, 0x49, 0x15, 0x78, 0xa4, 0x66, 0x46, + 0x62, 0x75, 0x17, 0x81, 0x68, 0xa9, 0xc3, 0xdb, 0x71, 0xe6, 0x63, 0x6a, 0x1b, 0xee, 0xf5, 0x99, 0x11, 0x0e, 0x27, + 0x59, 0x5c, 0x3b, 0x67, 0x38, 0xb9, 0xdc, 0xe7, 0xb5, 0x13, 0x13, 0xac, 0xbd, 0xc3, 0x53, 0x05, 0xf4, 0x68, 0x70, + 0xaa, 0x58, 0x1a, 0x02, 0x31, 0x13, 0xc0, 0x9b, 0x39, 0x3c, 0xda, 0x02, 0x9c, 0x8f, 0xd6, 0x38, 0xf8, 0x4a, 0x6b, + 0x5d, 0x6d, 0x2a, 0x51, 0xd6, 0x6b, 0xdc, 0x9f, 0x66, 0x78, 0x94, 0xe1, 0x79, 0x36, 0x08, 0x8e, 0x9b, 0x59, 0x16, + 0x9a, 0x74, 0xad, 0x56, 0x4f, 0x9d, 0x19, 0x21, 0xb2, 0x3f, 0x2d, 0xfd, 0x41, 0x3d, 0x40, 0xf8, 0x14, 0xb2, 0x80, + 0x96, 0xf4, 0xdc, 0xdf, 0x86, 0x7d, 0x72, 0x1b, 0x35, 0x62, 0x9e, 0x58, 0x32, 0xd2, 0xf3, 0x3f, 0xca, 0x2c, 0xdb, + 0x5a, 0x23, 0x9a, 0xdf, 0xee, 0x45, 0x0d, 0xdf, 0x5e, 0xa0, 0x65, 0x2b, 0xcd, 0x76, 0x00, 0x51, 0xac, 0x71, 0x92, + 0x0e, 0xd6, 0x48, 0xae, 0x56, 0xb1, 0x4d, 0x21, 0x3c, 0x99, 0x31, 0xaa, 0x16, 0x85, 0x79, 0x85, 0x2e, 0x56, 0x28, + 0x31, 0xfc, 0x2e, 0x76, 0x36, 0xa2, 0xf0, 0xe8, 0x9b, 0x04, 0xc3, 0x8d, 0x58, 0x10, 0x59, 0x13, 0xb9, 0x87, 0x59, + 0x65, 0x19, 0x24, 0x88, 0x30, 0x22, 0xbf, 0xbd, 0x2e, 0x15, 0xf6, 0x9d, 0x3b, 0xfb, 0xc7, 0xf8, 0x02, 0xc2, 0xcd, + 0xdb, 0x84, 0x16, 0x43, 0x3a, 0x01, 0x36, 0x16, 0xe2, 0x10, 0x6e, 0x25, 0xac, 0x56, 0xfd, 0x41, 0x57, 0x18, 0xf2, + 0xec, 0x5e, 0xe1, 0x2b, 0x1b, 0xda, 0xdd, 0x00, 0x5c, 0x75, 0x5b, 0x6a, 0xae, 0x8d, 0xee, 0x87, 0x9a, 0x87, 0xc2, + 0xb8, 0x4b, 0x72, 0x6f, 0x7d, 0x54, 0xcf, 0x79, 0xd7, 0x2c, 0xc0, 0x4d, 0xe8, 0x2a, 0x3c, 0xc2, 0x0b, 0x6b, 0xc3, + 0x69, 0x5e, 0x85, 0xa2, 0xe6, 0x31, 0x28, 0x78, 0x83, 0x9a, 0xb0, 0x7e, 0x36, 0xc0, 0x23, 0x1f, 0x33, 0x7c, 0xff, + 0x6d, 0x3c, 0x42, 0xa8, 0x20, 0x06, 0xa6, 0xd6, 0x65, 0x7b, 0x54, 0xd9, 0xed, 0x9b, 0x4c, 0xc3, 0x30, 0x18, 0x23, + 0xe6, 0x51, 0x68, 0xc4, 0x9c, 0x37, 0x1a, 0x68, 0x41, 0x46, 0x60, 0xc4, 0xbc, 0x08, 0x5a, 0x5b, 0xd8, 0x17, 0x43, + 0x83, 0xf6, 0x16, 0x08, 0x75, 0x39, 0xd0, 0x34, 0x0d, 0x6f, 0x83, 0x54, 0x6f, 0xb3, 0xfb, 0x97, 0xaa, 0x8e, 0x3a, + 0xa0, 0x48, 0x18, 0x5f, 0xfa, 0x49, 0x58, 0xd7, 0x70, 0x3b, 0xee, 0xb1, 0x19, 0xb7, 0xb3, 0x6d, 0x50, 0x7d, 0xd9, + 0xcf, 0x06, 0x83, 0xae, 0xf4, 0x56, 0x12, 0x2d, 0x3c, 0xae, 0x5e, 0x13, 0xa9, 0x16, 0xef, 0x8b, 0xde, 0xbc, 0xf2, + 0xe6, 0xfe, 0x91, 0xd2, 0xcd, 0xf3, 0x18, 0x38, 0xa0, 0x7d, 0xb8, 0x1f, 0xaa, 0xe2, 0x83, 0x1d, 0x75, 0x20, 0x0a, + 0x5a, 0xda, 0xaa, 0x09, 0xa4, 0xd6, 0xcc, 0x2e, 0xd6, 0x4d, 0x85, 0x0e, 0x05, 0x84, 0x21, 0x53, 0x55, 0x77, 0x77, + 0x2a, 0x50, 0x0d, 0x71, 0x38, 0xf5, 0x1f, 0x5b, 0x23, 0xd6, 0x38, 0xea, 0x8c, 0x22, 0x63, 0x24, 0x69, 0x97, 0x0f, + 0x1e, 0x10, 0x02, 0x2b, 0x01, 0x1f, 0xc8, 0xd9, 0x24, 0x19, 0x43, 0x82, 0xb7, 0x2c, 0xd3, 0x86, 0x0f, 0xe1, 0x0e, + 0x41, 0x79, 0x62, 0x63, 0x6d, 0xba, 0x4a, 0x16, 0x72, 0x55, 0x97, 0xd7, 0x01, 0x7a, 0xde, 0x95, 0xbf, 0xb1, 0xe1, + 0xc8, 0x82, 0x81, 0x65, 0x5b, 0xfb, 0x04, 0x3c, 0xf2, 0x71, 0x85, 0x20, 0x7e, 0x29, 0x74, 0x62, 0x82, 0x5e, 0x5f, + 0xc1, 0x06, 0xc5, 0x73, 0x70, 0x10, 0x74, 0x12, 0x1c, 0x06, 0xef, 0x32, 0xab, 0x49, 0x36, 0xb8, 0x35, 0x23, 0xf1, + 0x7c, 0xb5, 0x6a, 0xa1, 0xc3, 0x7f, 0xcc, 0xbb, 0xce, 0xe3, 0x52, 0xe1, 0x3e, 0xae, 0x14, 0xee, 0x60, 0x09, 0x48, + 0xc6, 0x81, 0xae, 0x1d, 0xcb, 0x50, 0x8d, 0x0e, 0x21, 0xc7, 0x5f, 0x40, 0x00, 0x6a, 0x77, 0x2c, 0x81, 0x9e, 0x7d, + 0xab, 0x80, 0xd5, 0xb5, 0x97, 0x25, 0x90, 0x11, 0xdc, 0xfd, 0x26, 0x30, 0x2a, 0x44, 0xe3, 0xf3, 0x67, 0x9e, 0x86, + 0xe0, 0x89, 0xf3, 0xe7, 0x9a, 0x19, 0xd6, 0xbd, 0xa0, 0x37, 0xa6, 0xf9, 0x78, 0x8c, 0x9b, 0x63, 0x0b, 0xce, 0xa3, + 0x0e, 0xfc, 0xb4, 0x10, 0x3d, 0xea, 0x60, 0x97, 0x8a, 0xc7, 0x25, 0x90, 0x43, 0xf4, 0x74, 0x06, 0x52, 0xc0, 0x4a, + 0xc7, 0x56, 0x8b, 0x34, 0x41, 0xab, 0xd5, 0xe4, 0x82, 0xb4, 0x10, 0x5a, 0xaa, 0x1b, 0xae, 0xb3, 0x29, 0xf8, 0x48, + 0x83, 0x62, 0xe0, 0x0d, 0xd5, 0xd3, 0x18, 0xe1, 0x31, 0x5a, 0x8e, 0xd8, 0x98, 0x2e, 0x72, 0x9d, 0xaa, 0x1e, 0x4f, + 0x6c, 0x54, 0x5e, 0x66, 0x23, 0xc1, 0x1d, 0x75, 0xf0, 0xc4, 0xf0, 0x97, 0x8f, 0x8c, 0x39, 0x48, 0x91, 0x99, 0xe4, + 0x89, 0x49, 0xc0, 0x3c, 0xc9, 0x72, 0xa9, 0x98, 0x6d, 0xa6, 0x6b, 0x6d, 0xcb, 0x21, 0xae, 0x77, 0xa4, 0x0b, 0x6e, + 0xac, 0x28, 0xa3, 0x74, 0x4a, 0x54, 0x4f, 0x1d, 0x75, 0xd2, 0x09, 0xe6, 0x09, 0x70, 0x7a, 0xef, 0x64, 0xcc, 0x1a, + 0xe5, 0xad, 0xe8, 0x0c, 0x1d, 0x4e, 0xb1, 0xa8, 0x2e, 0x51, 0x67, 0xe8, 0x70, 0x82, 0xf0, 0xac, 0x41, 0x72, 0x05, + 0x1e, 0xc3, 0x5c, 0xfc, 0x1f, 0x29, 0xff, 0xcd, 0x61, 0x43, 0xfc, 0xe8, 0xb7, 0xb0, 0x53, 0xd8, 0x28, 0x4a, 0x73, + 0x02, 0x5e, 0x8b, 0xed, 0x33, 0x9c, 0x91, 0x49, 0x33, 0xf7, 0x01, 0xf7, 0x4c, 0x2b, 0x8d, 0x5b, 0x8d, 0x0e, 0x33, + 0x3c, 0xda, 0x4c, 0x8a, 0xcd, 0x5c, 0x9b, 0x79, 0x9a, 0xc1, 0xf9, 0x5e, 0x8d, 0xc2, 0x95, 0x5f, 0x6c, 0x26, 0x85, + 0xe5, 0x1d, 0x70, 0x9b, 0x23, 0x2c, 0x9a, 0x14, 0xe7, 0x78, 0xd6, 0xfc, 0x8a, 0x67, 0xcd, 0x0f, 0x65, 0x46, 0x63, + 0x81, 0x05, 0x04, 0xef, 0x83, 0x44, 0x3c, 0xab, 0x92, 0x47, 0x58, 0x34, 0x4c, 0x79, 0x3c, 0x6b, 0x54, 0xa5, 0x9b, + 0x0b, 0x2c, 0x1a, 0xa6, 0x74, 0xe3, 0x03, 0x9e, 0x35, 0xbe, 0xfe, 0x8b, 0x49, 0x47, 0x29, 0xa0, 0xcb, 0x1c, 0x2d, + 0x33, 0x3b, 0xc4, 0xab, 0xdf, 0xde, 0xbe, 0x6b, 0x5f, 0x77, 0x0e, 0x27, 0xd8, 0xaf, 0x5f, 0x66, 0x70, 0x2c, 0xd3, + 0x31, 0x6b, 0x02, 0x44, 0x33, 0xdc, 0x39, 0x9c, 0xe2, 0xce, 0x61, 0xe6, 0x9a, 0x5a, 0xcf, 0x1a, 0xe4, 0x56, 0x87, + 0x50, 0xd4, 0x51, 0x1a, 0xc2, 0xc7, 0x4f, 0x36, 0x9d, 0xa0, 0x1a, 0x28, 0xd1, 0xe1, 0xa4, 0x06, 0x2a, 0xf8, 0x5e, + 0xd4, 0xbe, 0xab, 0x7a, 0x15, 0x06, 0x59, 0x28, 0xa1, 0x70, 0xcd, 0x0d, 0x78, 0x6a, 0x29, 0x06, 0x32, 0x61, 0x8a, + 0x05, 0xca, 0x77, 0x40, 0x61, 0x94, 0x27, 0x66, 0xe8, 0xc1, 0x74, 0x4c, 0xe2, 0xff, 0xcf, 0x93, 0x29, 0x87, 0x5e, + 0x6e, 0x99, 0xad, 0xe9, 0xb9, 0xc9, 0x84, 0xc3, 0x07, 0x1e, 0xeb, 0xff, 0xda, 0x81, 0x62, 0x03, 0x52, 0xfc, 0x7f, + 0xe9, 0xe8, 0x42, 0x30, 0x42, 0x56, 0x94, 0x16, 0x0e, 0xf1, 0xbf, 0x3f, 0xac, 0xa0, 0xfb, 0x62, 0xab, 0xfb, 0xc2, + 0x74, 0x1f, 0x36, 0x6d, 0x54, 0x39, 0x69, 0x55, 0xc9, 0x92, 0xff, 0x3a, 0xdd, 0xda, 0x02, 0x8d, 0xa8, 0xd1, 0xb3, + 0x49, 0xd8, 0xe0, 0x7e, 0x3b, 0xdd, 0x81, 0xcc, 0x6b, 0x6e, 0x9f, 0x19, 0x85, 0xc3, 0x37, 0xb8, 0x53, 0xbd, 0x6c, + 0x81, 0xf7, 0xa6, 0x32, 0xfa, 0xca, 0x38, 0xb4, 0x1c, 0x2c, 0x36, 0x4d, 0xb9, 0x8d, 0xb1, 0x74, 0x72, 0x8a, 0x8d, + 0x2b, 0x22, 0x54, 0xba, 0xbd, 0x04, 0xa5, 0xf8, 0x58, 0x37, 0x99, 0xf9, 0xba, 0xd0, 0x89, 0xb9, 0x84, 0x6a, 0x98, + 0xcf, 0xbb, 0x4b, 0x9d, 0x68, 0x39, 0xb7, 0x79, 0x77, 0x17, 0xd0, 0x27, 0x68, 0x58, 0x1b, 0x81, 0xdd, 0x3e, 0x2b, + 0x9c, 0x7e, 0xa7, 0x3a, 0x04, 0xc3, 0x03, 0xc8, 0x91, 0x16, 0xdb, 0x07, 0x36, 0xad, 0x61, 0xd7, 0x45, 0xb3, 0x4c, + 0xb4, 0xad, 0x36, 0x4d, 0xae, 0xdd, 0xc3, 0x7c, 0x1e, 0xf2, 0x14, 0xbc, 0xb0, 0xfa, 0xf1, 0x1d, 0xec, 0xc6, 0x6d, + 0x8d, 0x91, 0xa8, 0x2b, 0x99, 0x4a, 0xe8, 0x27, 0xb7, 0x98, 0x25, 0x77, 0xc6, 0x8b, 0x51, 0x19, 0x7f, 0x1f, 0x13, + 0x74, 0x3f, 0xaa, 0x24, 0x39, 0xb0, 0xec, 0x6f, 0xb0, 0xe4, 0x16, 0xcc, 0x13, 0xcb, 0x6a, 0x12, 0xeb, 0xe4, 0x2e, + 0x58, 0x44, 0x69, 0x1a, 0x59, 0x1b, 0x06, 0xd4, 0x34, 0x63, 0xd5, 0x83, 0xfb, 0x10, 0xe8, 0xa1, 0x57, 0x96, 0xd2, + 0xae, 0xb3, 0xb4, 0xd6, 0xbd, 0x36, 0xdd, 0x6f, 0x0e, 0x28, 0xe0, 0x0b, 0x03, 0xae, 0xe9, 0x5f, 0x4d, 0x22, 0x19, + 0xb2, 0xaf, 0x9c, 0x15, 0x8f, 0x17, 0x85, 0xc1, 0x34, 0xd1, 0xd3, 0x49, 0x36, 0x6f, 0x83, 0xa9, 0x5e, 0x36, 0xef, + 0xdc, 0x62, 0xf7, 0x7d, 0x67, 0xbf, 0xef, 0xb0, 0xe8, 0x31, 0x93, 0x91, 0x32, 0x53, 0xcc, 0x7f, 0xdf, 0xd9, 0xef, + 0x3b, 0xbc, 0x3d, 0x98, 0x1b, 0x7f, 0xa1, 0x58, 0xb2, 0x33, 0x5c, 0x82, 0x09, 0x79, 0xc0, 0xdd, 0xd4, 0xb2, 0x4c, + 0x10, 0xd8, 0x5a, 0x02, 0xc4, 0xf9, 0x7c, 0x1a, 0x57, 0xbc, 0x1a, 0x02, 0xee, 0xd3, 0xbb, 0xb6, 0x57, 0xa9, 0xc0, + 0x63, 0x82, 0x46, 0xc4, 0xc4, 0xb6, 0x31, 0x4f, 0x84, 0x01, 0x97, 0x47, 0x74, 0xa9, 0x27, 0x49, 0x80, 0x57, 0x35, + 0x2a, 0x6f, 0x53, 0xa4, 0xfc, 0x22, 0x41, 0x8e, 0x2f, 0xf6, 0x88, 0x2a, 0x06, 0xb0, 0x2a, 0x4b, 0xfa, 0x04, 0x52, + 0xcf, 0x0f, 0x26, 0xfa, 0x79, 0x13, 0x79, 0xec, 0x63, 0xb9, 0x9f, 0x99, 0x9e, 0x16, 0x72, 0x31, 0x99, 0x82, 0x0f, + 0x2d, 0xb0, 0x0c, 0x85, 0xa9, 0x57, 0xd9, 0xfa, 0xd7, 0x24, 0x37, 0x01, 0x14, 0x4e, 0x37, 0x65, 0x42, 0x33, 0xbd, + 0xa0, 0xb9, 0xb1, 0x24, 0xe5, 0x62, 0xf2, 0x48, 0xde, 0xbe, 0x04, 0xec, 0xa6, 0x44, 0x37, 0x76, 0xe4, 0xbd, 0x85, + 0x1d, 0x80, 0x33, 0xc2, 0x76, 0x55, 0x7c, 0xa8, 0x40, 0xe7, 0x8f, 0x73, 0xc2, 0x76, 0x55, 0x7d, 0xc2, 0x6c, 0xf6, + 0x94, 0x6c, 0x0c, 0xb7, 0x17, 0x67, 0x8d, 0x1c, 0x1d, 0x75, 0xd2, 0xbc, 0xeb, 0x89, 0x81, 0x05, 0x68, 0x00, 0xdc, + 0xad, 0xed, 0x59, 0xde, 0xdd, 0x10, 0xd0, 0xbb, 0x64, 0xd2, 0x5e, 0x97, 0x9b, 0x94, 0xd5, 0xaa, 0x53, 0x51, 0xc1, + 0x02, 0x4f, 0x83, 0xbd, 0x40, 0xed, 0xd7, 0x0e, 0x8a, 0x73, 0x95, 0x6d, 0x9a, 0x9e, 0x97, 0x7d, 0x77, 0x77, 0x2c, + 0x32, 0xb6, 0x69, 0x6f, 0x77, 0x10, 0x09, 0xcb, 0x09, 0xeb, 0x80, 0x13, 0xae, 0x6a, 0x07, 0x04, 0xe8, 0x3a, 0x10, + 0xb9, 0xb1, 0x24, 0xcb, 0x75, 0x65, 0x74, 0x1f, 0xf8, 0xdd, 0x52, 0x22, 0xdd, 0x68, 0x4b, 0x82, 0xe9, 0x13, 0x8c, + 0x9a, 0xce, 0xbc, 0xef, 0x5c, 0x7b, 0xba, 0x78, 0x53, 0xb4, 0xf5, 0x0f, 0x29, 0x63, 0xb3, 0x3d, 0x4c, 0x0c, 0x65, + 0x10, 0x03, 0xbd, 0x8f, 0x78, 0xb7, 0xd1, 0xc8, 0x10, 0x28, 0x64, 0xb2, 0x01, 0x96, 0x89, 0xd7, 0xa2, 0x1f, 0x1c, + 0x18, 0x78, 0x54, 0x09, 0x08, 0x53, 0x10, 0x42, 0xc2, 0xae, 0x0d, 0xc2, 0x86, 0xcb, 0x55, 0xcb, 0x85, 0x8d, 0x54, + 0x1b, 0x3a, 0xf8, 0x7f, 0x85, 0xcb, 0x56, 0xcf, 0x2c, 0x17, 0xc5, 0xe0, 0x66, 0x6e, 0xc0, 0x22, 0x41, 0x7a, 0xb4, + 0xd9, 0x1e, 0x8a, 0xbb, 0x73, 0xb1, 0xd9, 0x10, 0x90, 0x98, 0xc3, 0x04, 0x45, 0xc3, 0xb9, 0x31, 0xc6, 0x2a, 0xa9, + 0xb4, 0xac, 0x35, 0x89, 0x39, 0xf0, 0xa5, 0x0b, 0xd7, 0x7d, 0x79, 0x9b, 0x32, 0x7c, 0x97, 0x0a, 0x7c, 0x03, 0x9e, + 0x34, 0xa9, 0xc4, 0xee, 0xf1, 0x82, 0x62, 0x4d, 0x74, 0xd7, 0xb3, 0xb7, 0x05, 0xac, 0xb3, 0xd9, 0x23, 0x22, 0xf8, + 0x5d, 0xfd, 0x6a, 0x83, 0xef, 0x16, 0xfe, 0x0a, 0xd6, 0xcf, 0xc1, 0x49, 0x8a, 0x45, 0x43, 0x36, 0x0b, 0x77, 0x64, + 0x40, 0xb9, 0x8a, 0x5f, 0x0e, 0x53, 0xb7, 0x8a, 0xe1, 0xda, 0xc7, 0x57, 0xfc, 0x61, 0xa3, 0xdd, 0x86, 0x2a, 0x8b, + 0xdb, 0xbd, 0x29, 0x1a, 0xb2, 0x6a, 0x7a, 0x47, 0xe6, 0x46, 0x4a, 0xfd, 0xeb, 0x03, 0x6e, 0x6d, 0xb5, 0xef, 0xa7, + 0xf9, 0xd6, 0xa3, 0x73, 0xd5, 0xb4, 0x4f, 0xad, 0x15, 0xc1, 0xc1, 0xcf, 0x16, 0x6e, 0x6e, 0x0d, 0x38, 0x80, 0x9f, + 0xbf, 0xa3, 0x79, 0x9c, 0x41, 0x74, 0x7a, 0xab, 0x19, 0x5f, 0xc5, 0x7f, 0x8e, 0x1a, 0x71, 0x2f, 0xfd, 0x33, 0xf9, + 0x73, 0xd4, 0x40, 0x3d, 0x14, 0xcf, 0x6f, 0x57, 0x6c, 0xb6, 0x82, 0x60, 0x6b, 0xf7, 0x8e, 0xf0, 0xeb, 0xb0, 0x24, + 0xd7, 0x34, 0xe7, 0xd9, 0xca, 0xbd, 0xaa, 0xb7, 0x72, 0x4f, 0x0e, 0xad, 0xcc, 0x43, 0x51, 0xab, 0x58, 0x0e, 0x73, + 0x08, 0x2c, 0x1c, 0xef, 0x35, 0x7b, 0xfd, 0x56, 0xf3, 0xc1, 0xc0, 0xfe, 0x6b, 0x22, 0xdc, 0xa3, 0x5a, 0xc4, 0xb6, + 0x37, 0x1b, 0x5b, 0x3f, 0x06, 0xc3, 0x0e, 0x08, 0x05, 0x0e, 0x72, 0xe9, 0xe3, 0x0c, 0x59, 0xdf, 0x93, 0xd5, 0x8a, + 0xb9, 0x68, 0xd6, 0x4e, 0x83, 0x5f, 0xc6, 0x66, 0x3a, 0x6c, 0x27, 0x9d, 0xae, 0x17, 0x63, 0x49, 0x03, 0x22, 0x4d, + 0x63, 0x06, 0x81, 0xa4, 0x96, 0x86, 0xc3, 0x9a, 0xdf, 0x46, 0x69, 0x75, 0x7f, 0x04, 0x29, 0x3f, 0x44, 0x29, 0x3f, + 0x22, 0x10, 0x40, 0xdb, 0x32, 0x47, 0x65, 0x43, 0xde, 0x77, 0xe9, 0x9e, 0x71, 0x66, 0x68, 0xf0, 0xd5, 0xaa, 0x55, + 0x0d, 0x53, 0x14, 0xf5, 0x61, 0x2e, 0xd7, 0x58, 0x90, 0x37, 0xa0, 0x6b, 0x56, 0x44, 0xf4, 0x42, 0x57, 0x79, 0x78, + 0x54, 0x18, 0x4b, 0x02, 0x4e, 0xfa, 0x3d, 0xd1, 0x2b, 0xc8, 0xe5, 0xc3, 0x18, 0x7c, 0xcc, 0x30, 0xef, 0xeb, 0x7e, + 0x31, 0x18, 0xa0, 0xd4, 0x39, 0x9d, 0xa5, 0x26, 0xe2, 0x4a, 0xe0, 0x97, 0x5c, 0x80, 0x5f, 0xb2, 0x42, 0xac, 0x5f, + 0x0c, 0xc8, 0xbd, 0x2c, 0x96, 0xe0, 0x94, 0xbf, 0xc3, 0xe7, 0xf1, 0x61, 0x68, 0x60, 0x6a, 0x86, 0x65, 0x2e, 0xb2, + 0xc1, 0x62, 0xce, 0x5a, 0x02, 0xc1, 0xcd, 0x80, 0xbb, 0xd4, 0x86, 0x44, 0x63, 0x0d, 0x14, 0xdd, 0x46, 0xa1, 0x99, + 0xd1, 0xd3, 0xad, 0x36, 0xfa, 0x91, 0xc3, 0x0b, 0x73, 0x0d, 0x63, 0x11, 0xc8, 0x5c, 0xae, 0x7a, 0xec, 0x2f, 0x3f, + 0x6c, 0x56, 0x18, 0xbc, 0xc2, 0x98, 0xec, 0x94, 0x56, 0x89, 0x66, 0x7c, 0x95, 0x27, 0x8e, 0x21, 0xc8, 0xc4, 0x52, + 0xe9, 0x86, 0x63, 0xe2, 0x4a, 0xfa, 0x4c, 0x0c, 0xd9, 0x6e, 0x78, 0x66, 0x2e, 0x74, 0xb3, 0xfd, 0xc3, 0xb9, 0x9d, + 0x73, 0xc2, 0x8d, 0x56, 0xd2, 0x68, 0xa3, 0x9e, 0x19, 0xaa, 0xea, 0x82, 0xf9, 0x3d, 0x74, 0x5a, 0x5a, 0xec, 0x5c, + 0xbd, 0xbb, 0xe1, 0x13, 0x77, 0x65, 0xfc, 0x2d, 0x56, 0x85, 0x56, 0x64, 0xb8, 0xdd, 0x42, 0xde, 0x9c, 0xe9, 0xa1, + 0x57, 0xe4, 0x42, 0x75, 0xf8, 0x8b, 0xba, 0xc2, 0xbc, 0x7a, 0x19, 0x35, 0x84, 0x47, 0xbf, 0xd7, 0x19, 0x28, 0xff, + 0x60, 0x62, 0x32, 0x67, 0xc9, 0x0d, 0x2d, 0x44, 0xfc, 0xe3, 0x0b, 0x61, 0x62, 0x55, 0xed, 0xc1, 0x40, 0xf6, 0x4c, + 0xc5, 0x3d, 0xb8, 0x35, 0xe1, 0x63, 0xce, 0x46, 0xe9, 0x5e, 0xf4, 0x63, 0x43, 0x34, 0x7e, 0x8c, 0x7e, 0x04, 0x77, + 0x67, 0xf7, 0xc4, 0x62, 0x19, 0x17, 0xc2, 0xdf, 0x63, 0x3d, 0x2c, 0x55, 0xca, 0x58, 0x7b, 0xdd, 0x72, 0x78, 0x21, + 0xf5, 0x26, 0x8b, 0x1f, 0x3a, 0x62, 0x6d, 0x53, 0xb0, 0x0e, 0x29, 0x29, 0x3c, 0xbb, 0x62, 0x6e, 0xb5, 0x98, 0xbb, + 0xd4, 0x12, 0xfe, 0xfa, 0xea, 0x61, 0xa9, 0x82, 0x86, 0x83, 0xd0, 0x95, 0xb6, 0x90, 0x00, 0x03, 0x97, 0xd2, 0xa7, + 0xd3, 0x9d, 0x49, 0x64, 0x96, 0xc5, 0xf0, 0xee, 0x41, 0x05, 0xf3, 0xdf, 0xd9, 0x46, 0x58, 0x15, 0xb8, 0x5c, 0xa9, + 0xa2, 0x5e, 0x4a, 0x02, 0x01, 0xe8, 0x4b, 0xef, 0x41, 0x79, 0x51, 0x74, 0x1b, 0x0d, 0x09, 0x5a, 0x58, 0x6a, 0xae, + 0x55, 0x31, 0xdd, 0x0f, 0x9f, 0x06, 0x0c, 0x3e, 0xbc, 0x43, 0xda, 0xc6, 0xfb, 0x9c, 0x94, 0x50, 0xbb, 0x83, 0xf6, + 0xc1, 0x2a, 0x3b, 0x28, 0xff, 0x36, 0xa6, 0xc8, 0xe6, 0xf7, 0xd9, 0x0f, 0xd4, 0x75, 0x38, 0x70, 0x05, 0xab, 0x5e, + 0xca, 0x28, 0x18, 0xb0, 0x72, 0x0a, 0xd4, 0xde, 0x49, 0x46, 0xb3, 0x29, 0x03, 0x75, 0xbf, 0x2d, 0x5a, 0xcd, 0xed, + 0x49, 0xdd, 0x6f, 0xc8, 0x38, 0xfb, 0x08, 0xe3, 0xec, 0xa3, 0xc0, 0x8b, 0x45, 0x92, 0x3f, 0x64, 0xac, 0x71, 0xac, + 0x9a, 0x02, 0x1d, 0x75, 0x80, 0x3b, 0x03, 0x07, 0x1e, 0xb0, 0x45, 0x39, 0x38, 0xa0, 0xce, 0xe2, 0x9e, 0x36, 0x32, + 0xef, 0xed, 0x09, 0xb5, 0x8b, 0x58, 0xe0, 0x66, 0xcd, 0x4c, 0x0b, 0x5a, 0x2b, 0x8c, 0xf3, 0x78, 0xc0, 0xdb, 0x3c, + 0xab, 0xc5, 0x4f, 0xd8, 0xb0, 0xa6, 0xaa, 0xdf, 0x40, 0x73, 0x54, 0x0b, 0x72, 0xf3, 0xc4, 0x78, 0xab, 0x92, 0x7e, + 0x14, 0x0d, 0x2c, 0xa7, 0x42, 0x0c, 0xc9, 0xe8, 0xb7, 0x06, 0xc1, 0xad, 0xf6, 0x6a, 0xc5, 0x3d, 0xe2, 0x8b, 0x9a, + 0xb7, 0x9a, 0xb9, 0x05, 0xa0, 0x45, 0x1c, 0x95, 0xf7, 0x26, 0x11, 0x78, 0xdf, 0x96, 0x11, 0xd2, 0x96, 0x7d, 0xfb, + 0xfe, 0x63, 0xa9, 0xd8, 0x7c, 0x47, 0x27, 0x83, 0x34, 0xb2, 0x23, 0x8a, 0xf0, 0x75, 0x09, 0x49, 0xb8, 0x4a, 0xba, + 0x56, 0x99, 0x9c, 0x33, 0x95, 0x72, 0x7c, 0x5d, 0x48, 0xa9, 0xaf, 0xec, 0x97, 0xc4, 0xd5, 0x9d, 0x8c, 0xc0, 0xd7, + 0x13, 0xa6, 0xdf, 0xd1, 0x62, 0xc2, 0xc0, 0xaf, 0xc8, 0xdf, 0x8e, 0xa5, 0x94, 0x5c, 0x3e, 0x11, 0x71, 0x9f, 0x62, + 0x78, 0xbc, 0x74, 0x80, 0xb5, 0x09, 0x81, 0x52, 0xe2, 0x22, 0x5c, 0x10, 0xbd, 0x29, 0xe4, 0xed, 0x5d, 0x5c, 0x60, + 0xe7, 0x00, 0x58, 0x3a, 0x4d, 0x02, 0xfc, 0xcb, 0xc7, 0x7c, 0xac, 0xc6, 0x9c, 0x1a, 0x5d, 0xbf, 0xfb, 0x9d, 0x5c, + 0x03, 0xbd, 0x2d, 0x1d, 0x05, 0xfb, 0xad, 0x01, 0xe4, 0xc2, 0x5d, 0x18, 0x5c, 0x7c, 0x85, 0xb5, 0x65, 0x61, 0xbc, + 0xb1, 0x00, 0x7a, 0x7f, 0x67, 0x60, 0xc1, 0x86, 0x39, 0xa6, 0xf0, 0xf2, 0xeb, 0x84, 0xe9, 0x20, 0x2a, 0xc8, 0x93, + 0xf2, 0x6d, 0xcf, 0x5a, 0xed, 0xb7, 0x6c, 0x0c, 0x77, 0x18, 0xc9, 0xb7, 0x0b, 0x27, 0x0e, 0x3c, 0x20, 0xd3, 0x64, + 0xb6, 0xd9, 0x37, 0x3e, 0xf2, 0xc8, 0xeb, 0x71, 0xbc, 0xab, 0xa5, 0x30, 0xdf, 0xac, 0xe8, 0x1a, 0x43, 0x28, 0x8a, + 0xb0, 0xdf, 0x2f, 0x2a, 0xa6, 0xa8, 0x32, 0x68, 0x83, 0x86, 0xe5, 0x8d, 0xf8, 0x05, 0xce, 0x18, 0x5a, 0x2f, 0x64, + 0xef, 0xe8, 0xac, 0xc3, 0x99, 0xc3, 0x8c, 0x29, 0x81, 0x51, 0x69, 0x59, 0xd0, 0x09, 0x38, 0x3a, 0x57, 0x1f, 0x44, + 0xc5, 0xd5, 0xb1, 0x02, 0xf0, 0x24, 0x53, 0xf8, 0x27, 0xdf, 0x04, 0xeb, 0x7e, 0xab, 0x66, 0x98, 0xfa, 0x8b, 0xde, + 0x76, 0x2d, 0x5f, 0x86, 0x38, 0xd2, 0xc6, 0x10, 0x5a, 0xe7, 0xf6, 0x0e, 0x50, 0xc4, 0x05, 0xbd, 0x48, 0x35, 0xbe, + 0x56, 0x8b, 0xa1, 0x59, 0x5f, 0xe3, 0x3a, 0xa6, 0x0d, 0xa2, 0x58, 0x77, 0x4d, 0x7c, 0x5d, 0x3d, 0xa5, 0xaa, 0x52, + 0x05, 0x67, 0x90, 0x40, 0x58, 0x95, 0x97, 0x0d, 0xa9, 0x24, 0x97, 0xa6, 0x53, 0x69, 0x3a, 0xad, 0x10, 0xca, 0xa5, + 0x27, 0xe5, 0xfd, 0x2b, 0x84, 0x30, 0x30, 0x65, 0x76, 0x60, 0x95, 0xda, 0xc2, 0x2a, 0x78, 0xf5, 0x62, 0x03, 0xab, + 0x24, 0x1c, 0xcf, 0x25, 0x1a, 0x15, 0x15, 0x0e, 0x19, 0xd2, 0x17, 0x62, 0x11, 0x24, 0x00, 0x16, 0xbd, 0xcb, 0x5c, + 0xde, 0xf7, 0x70, 0x28, 0xec, 0x49, 0x26, 0xe1, 0x74, 0x13, 0x9a, 0xc3, 0x1b, 0xbb, 0xaa, 0xe7, 0x11, 0x02, 0x96, + 0x9e, 0x63, 0x78, 0x5b, 0xf9, 0xfb, 0x6f, 0xba, 0x3a, 0x0b, 0xf2, 0xf4, 0x5f, 0xa2, 0x24, 0x34, 0xf6, 0x9f, 0xe3, + 0xa1, 0x43, 0xc2, 0x70, 0xe0, 0x9b, 0x23, 0xac, 0x70, 0x70, 0xab, 0x88, 0xcf, 0xe0, 0x0e, 0x1f, 0xeb, 0xd0, 0x03, + 0xc0, 0x12, 0x8a, 0x43, 0x90, 0x6f, 0xa0, 0x98, 0xc1, 0x01, 0x4d, 0x96, 0xe1, 0x05, 0x2e, 0x58, 0x2d, 0x94, 0xf7, + 0xb7, 0x2d, 0x2f, 0xa5, 0xd5, 0x2e, 0x79, 0x8d, 0x39, 0x50, 0xf9, 0x19, 0x5e, 0xf8, 0x0a, 0xf3, 0xe8, 0xb3, 0xfb, + 0xc2, 0xd7, 0x0e, 0xe8, 0x29, 0x04, 0x8c, 0x74, 0xbf, 0xd7, 0x84, 0x7b, 0x8a, 0x5e, 0xe6, 0xe2, 0xb0, 0xed, 0xa0, + 0x7b, 0x81, 0xb9, 0xba, 0xaa, 0xb2, 0xe6, 0x60, 0x0a, 0x0d, 0x0e, 0xaa, 0x70, 0x46, 0x60, 0xae, 0x5e, 0x94, 0x05, + 0xe7, 0x20, 0xde, 0xf7, 0x84, 0xc9, 0x29, 0xa3, 0x01, 0xbc, 0xc8, 0xca, 0x47, 0xa7, 0x7a, 0x1c, 0x5c, 0xc6, 0x0d, + 0x9b, 0xf8, 0x42, 0xf8, 0x54, 0x60, 0x25, 0xad, 0x71, 0x68, 0x44, 0x47, 0x74, 0x0e, 0x66, 0x1b, 0x40, 0xc1, 0xdd, + 0xf9, 0xb0, 0xb1, 0x50, 0xc1, 0xbb, 0xb6, 0xb5, 0x67, 0xa8, 0x09, 0x71, 0x26, 0x4d, 0xc1, 0xdd, 0xb6, 0x41, 0x06, + 0x6f, 0x7e, 0xfb, 0x6f, 0x85, 0x45, 0x82, 0x01, 0x95, 0x9a, 0x24, 0x08, 0x4f, 0x50, 0x1a, 0xe9, 0x56, 0x6e, 0x26, + 0x90, 0x4e, 0x44, 0xcd, 0xa8, 0x7b, 0xe3, 0x7c, 0x75, 0xd4, 0x40, 0x54, 0xd4, 0x40, 0x05, 0xd4, 0x40, 0xd6, 0xb7, + 0x7f, 0x01, 0x0b, 0x61, 0x23, 0x54, 0x89, 0x20, 0x20, 0xc2, 0x5c, 0x1b, 0x3e, 0xa0, 0x48, 0x42, 0xc8, 0x1b, 0x40, + 0xc5, 0x94, 0xbc, 0x04, 0xa3, 0x71, 0x78, 0xbd, 0x07, 0xdc, 0x2f, 0x2d, 0xc3, 0xe0, 0x39, 0x05, 0x93, 0xff, 0xd6, + 0xe7, 0x43, 0xf5, 0x72, 0x75, 0x10, 0xc2, 0x2f, 0x20, 0x56, 0x84, 0xe3, 0x2f, 0x7e, 0x01, 0xb2, 0xa9, 0xb0, 0x3c, + 0x38, 0x90, 0x20, 0xf0, 0x43, 0x14, 0xe1, 0x80, 0x67, 0x78, 0x99, 0x6d, 0x10, 0x3d, 0x3f, 0x2b, 0x55, 0xcd, 0x4a, + 0x06, 0xb3, 0x2a, 0x3c, 0x8d, 0xa3, 0x6b, 0xc2, 0x40, 0x70, 0xa1, 0x76, 0xdf, 0x20, 0x04, 0xca, 0x96, 0x1b, 0x43, + 0x97, 0x9e, 0x82, 0xf9, 0x68, 0x1c, 0xbd, 0x65, 0xf0, 0x3a, 0xaf, 0x31, 0xf9, 0x67, 0x9a, 0x65, 0xda, 0x30, 0x8f, + 0x8d, 0xc0, 0x49, 0x9d, 0xa2, 0xe4, 0x6f, 0xc9, 0x45, 0x1c, 0x35, 0x2f, 0x23, 0xd4, 0x80, 0x7f, 0x1b, 0x1c, 0x75, + 0x69, 0x42, 0x47, 0x23, 0x1f, 0xfc, 0x26, 0x23, 0x66, 0x93, 0xad, 0x56, 0xa2, 0x22, 0xe8, 0x89, 0xdd, 0x60, 0xc0, + 0x4a, 0xbc, 0x00, 0xf6, 0xc1, 0x72, 0xb0, 0xe4, 0x9d, 0x88, 0x95, 0x3f, 0xa5, 0x30, 0x58, 0x3d, 0x67, 0x08, 0xe1, + 0x2c, 0x88, 0xd9, 0xf8, 0x9f, 0xcf, 0x34, 0x5c, 0x3f, 0x3f, 0x5f, 0xc7, 0x88, 0x48, 0x1f, 0x44, 0xae, 0xc6, 0x8e, + 0x88, 0x20, 0x6c, 0x99, 0xee, 0xbb, 0x32, 0x3f, 0x78, 0xeb, 0xea, 0x81, 0x0d, 0x17, 0x07, 0x06, 0xd4, 0x28, 0x30, + 0x5a, 0xc1, 0x39, 0x29, 0x07, 0x0e, 0x4a, 0x08, 0xcd, 0x8a, 0x78, 0x4a, 0x2e, 0x21, 0x12, 0x5e, 0x86, 0xba, 0x60, + 0x58, 0x10, 0x48, 0x50, 0x53, 0x90, 0xa0, 0x32, 0x5f, 0x7b, 0x04, 0xb3, 0xce, 0xcd, 0x6c, 0xa7, 0xa8, 0xeb, 0x82, + 0xfc, 0xfc, 0xa2, 0xe3, 0x11, 0xb0, 0xb4, 0x07, 0x07, 0x05, 0x44, 0x10, 0x03, 0x0a, 0x5e, 0x4a, 0x80, 0x81, 0x06, + 0xbc, 0xd8, 0xd0, 0x80, 0xcf, 0xb5, 0xf1, 0x3a, 0x30, 0xb6, 0x3e, 0x65, 0x90, 0x8b, 0x67, 0xd5, 0x9e, 0x26, 0x84, + 0xec, 0xb7, 0x7a, 0x3a, 0xdd, 0x8e, 0x90, 0xd8, 0xfb, 0xa8, 0x4d, 0xa0, 0x31, 0x47, 0xba, 0xab, 0x8d, 0xf9, 0xb5, + 0xa6, 0x47, 0xac, 0x26, 0x21, 0x5d, 0x90, 0x2e, 0xcf, 0xa7, 0x3d, 0x83, 0x2b, 0x56, 0x69, 0xe4, 0xe0, 0x02, 0xf4, + 0xd9, 0x80, 0x00, 0x05, 0x2a, 0x4d, 0x25, 0x8a, 0x22, 0x2e, 0x92, 0x92, 0x0d, 0xc3, 0x0c, 0xc2, 0x14, 0x56, 0x2b, + 0x41, 0x37, 0xd6, 0x00, 0x78, 0x67, 0x66, 0xff, 0x94, 0x3e, 0xd8, 0x74, 0xed, 0xcd, 0x23, 0x80, 0x80, 0xec, 0xb7, + 0x4b, 0x76, 0x5d, 0x6c, 0x54, 0x66, 0x61, 0x2d, 0x63, 0x2b, 0xb7, 0xed, 0x31, 0xf6, 0x4e, 0x6c, 0xf3, 0x09, 0x10, + 0xa2, 0xb6, 0x64, 0x1a, 0x21, 0x42, 0x62, 0x11, 0xeb, 0xda, 0x90, 0x8d, 0x36, 0xb4, 0x6f, 0x5e, 0xb7, 0x87, 0xd8, + 0x07, 0xa0, 0x78, 0x73, 0x5c, 0x82, 0x43, 0x78, 0xe1, 0x11, 0xfe, 0x16, 0x58, 0xa4, 0x02, 0x33, 0x2c, 0x57, 0x2b, + 0xa8, 0xe7, 0xf1, 0x3e, 0xdb, 0x0c, 0x4e, 0x2a, 0x37, 0xc6, 0x2e, 0xed, 0xc4, 0xe3, 0xb2, 0x09, 0x89, 0x33, 0xe8, + 0xd7, 0x57, 0x44, 0xbd, 0xfd, 0x76, 0xfa, 0xc4, 0xbf, 0x57, 0xe6, 0x76, 0x20, 0x36, 0xac, 0x37, 0x58, 0x7d, 0x00, + 0x2d, 0x7f, 0x95, 0xf9, 0x87, 0xca, 0x82, 0x9b, 0x04, 0xb5, 0xb9, 0x88, 0x5d, 0xd6, 0x45, 0x8c, 0xd4, 0x16, 0x77, + 0x87, 0x10, 0xff, 0x6a, 0x2b, 0x8a, 0x01, 0x4f, 0x2a, 0xfe, 0x39, 0x46, 0x5d, 0x08, 0x45, 0x6d, 0x3d, 0x6c, 0x80, + 0xd2, 0x2e, 0xd7, 0x95, 0x18, 0x19, 0x12, 0xc8, 0xb7, 0x2e, 0xbc, 0xa0, 0x39, 0x89, 0x14, 0xc8, 0xc9, 0x41, 0x54, + 0xd2, 0x6c, 0x43, 0x98, 0xeb, 0x6e, 0xe1, 0x98, 0xb9, 0xda, 0xa0, 0x45, 0xfc, 0x02, 0xd8, 0x19, 0x6e, 0x24, 0x4b, + 0x07, 0x3e, 0x55, 0x03, 0x9f, 0x5f, 0x73, 0x43, 0x51, 0x14, 0xea, 0xbd, 0xb3, 0x8f, 0xcc, 0xc1, 0xef, 0x34, 0x10, + 0x1f, 0xa9, 0xd3, 0x91, 0x6c, 0x84, 0x5a, 0x73, 0x76, 0xbc, 0x6c, 0x33, 0xc2, 0xa0, 0xb0, 0xd1, 0xfb, 0x2a, 0x64, + 0x15, 0x3b, 0x3b, 0x15, 0xc1, 0x9c, 0xbe, 0xa8, 0xca, 0x39, 0x95, 0x5b, 0x46, 0xb5, 0xd4, 0x34, 0x40, 0x84, 0x2b, + 0x9f, 0x48, 0xde, 0x67, 0x26, 0xfc, 0x83, 0xc1, 0xb8, 0x7a, 0xa4, 0xf0, 0xf7, 0xbb, 0x62, 0x87, 0x6c, 0x47, 0x87, + 0xdb, 0x08, 0x9a, 0x17, 0x2a, 0x78, 0xc0, 0x51, 0xc9, 0x12, 0x22, 0x45, 0x2e, 0xf7, 0x55, 0xcd, 0x94, 0xed, 0x3a, + 0x42, 0x08, 0x69, 0x8f, 0xb3, 0x6e, 0x68, 0xf5, 0xd0, 0x23, 0x55, 0x94, 0xc3, 0x2d, 0x9a, 0xeb, 0x02, 0x54, 0x18, + 0x81, 0x74, 0xf9, 0x99, 0xdd, 0xa5, 0x12, 0xa2, 0x97, 0xaf, 0x5d, 0x08, 0x63, 0x67, 0x65, 0x89, 0x0b, 0x33, 0x6a, + 0x1b, 0x46, 0xd7, 0x6d, 0x0c, 0x67, 0x03, 0x63, 0xa6, 0x41, 0x49, 0x0b, 0x42, 0x5d, 0x77, 0xe9, 0x45, 0x66, 0x02, + 0x3d, 0xe6, 0x84, 0x36, 0x18, 0x9e, 0x12, 0x0d, 0x96, 0x4d, 0x05, 0x58, 0xf0, 0x2d, 0x8b, 0xd4, 0xda, 0x6c, 0xb2, + 0xf8, 0xa3, 0x8e, 0xcd, 0xd3, 0x7e, 0x79, 0xc5, 0x3c, 0x17, 0x8e, 0xba, 0x3d, 0xcf, 0x7c, 0x3c, 0xba, 0xa7, 0x6f, + 0xae, 0x5e, 0xbc, 0x7c, 0xfd, 0x6a, 0xb5, 0x6a, 0xb3, 0x66, 0xfb, 0x04, 0xff, 0xa4, 0xcb, 0x78, 0xb0, 0x65, 0x14, + 0xa0, 0x83, 0x83, 0x7d, 0x6e, 0x5c, 0x78, 0x3e, 0xf3, 0x39, 0xc4, 0x0d, 0xd2, 0x03, 0x9c, 0x15, 0x65, 0x4c, 0x90, + 0xdb, 0xa8, 0x17, 0xdd, 0x45, 0xa0, 0x84, 0xaa, 0xc8, 0xdf, 0x87, 0xcd, 0xd9, 0xef, 0x41, 0x60, 0x22, 0xa8, 0x0f, + 0x11, 0x40, 0x20, 0x5e, 0x29, 0x2e, 0x08, 0xf3, 0x09, 0x10, 0xc5, 0x7b, 0x01, 0x9c, 0xa9, 0x89, 0x5a, 0xb5, 0x50, + 0x71, 0x01, 0x24, 0xd1, 0x86, 0xa3, 0xa4, 0x47, 0x26, 0x80, 0x37, 0x04, 0xa5, 0xb4, 0xbf, 0xba, 0xb9, 0x73, 0x97, + 0xca, 0x51, 0xaf, 0x95, 0xe6, 0x78, 0xea, 0x3e, 0xa7, 0xf0, 0x39, 0xed, 0xfa, 0xd3, 0x41, 0x1c, 0xe6, 0x78, 0x41, + 0xc4, 0xa1, 0x7f, 0x16, 0x71, 0x39, 0x2f, 0xd8, 0x17, 0x2e, 0x17, 0x2a, 0x5d, 0xde, 0xa6, 0x32, 0xb9, 0x6d, 0x8e, + 0x0e, 0xe3, 0x22, 0xb9, 0x6d, 0xaa, 0xe4, 0x16, 0xe1, 0xbb, 0x54, 0x26, 0x77, 0x36, 0xe5, 0xae, 0xa9, 0xe0, 0xe6, + 0x0b, 0x0b, 0x38, 0x14, 0x6d, 0xd1, 0xc6, 0x62, 0xb3, 0xa8, 0x4d, 0x71, 0x45, 0x03, 0x0c, 0xfe, 0x7d, 0xc7, 0xc6, + 0x0f, 0xc3, 0x97, 0xe0, 0xd2, 0xa4, 0x89, 0xfc, 0x04, 0xd2, 0x4f, 0xab, 0x32, 0x70, 0x9f, 0x92, 0x56, 0x77, 0x7a, + 0x21, 0x9a, 0xed, 0x6e, 0xa3, 0x31, 0x85, 0xbd, 0x9b, 0x91, 0xdc, 0x17, 0x9b, 0x36, 0x4c, 0x7c, 0x9d, 0xfd, 0x6c, + 0xb5, 0xda, 0xcf, 0x91, 0xd9, 0x70, 0x13, 0x16, 0xeb, 0xfe, 0x74, 0x80, 0x5b, 0xf8, 0x79, 0x86, 0xd0, 0x92, 0xf5, + 0xa7, 0x03, 0xc2, 0xfa, 0xd3, 0x46, 0x7b, 0x60, 0x0d, 0xed, 0xcc, 0x56, 0x5c, 0x43, 0x08, 0xcd, 0xe9, 0xe0, 0xc8, + 0x94, 0x94, 0x2e, 0xdf, 0x7e, 0xd1, 0x2a, 0xa0, 0x9f, 0xaa, 0x05, 0x2f, 0x93, 0xb8, 0x03, 0x7d, 0xd1, 0x0b, 0xfb, + 0x74, 0x6b, 0x41, 0x8e, 0x8f, 0x2a, 0x57, 0x7b, 0x8a, 0xb0, 0xe9, 0x49, 0x1d, 0x16, 0x87, 0xa6, 0x19, 0xd7, 0xa5, + 0x74, 0xdf, 0xa1, 0x66, 0xe4, 0xa3, 0x83, 0x05, 0x20, 0x48, 0x05, 0x8f, 0xac, 0x70, 0xe1, 0x94, 0x42, 0xb8, 0x38, + 0xa8, 0x6c, 0xc1, 0x24, 0x27, 0xad, 0x6e, 0x6e, 0x2c, 0xfd, 0x73, 0x17, 0xd1, 0x94, 0x62, 0x4a, 0x32, 0x5f, 0x32, + 0x37, 0x60, 0xa1, 0x9b, 0x94, 0x67, 0x0a, 0x7a, 0xa5, 0x01, 0x1e, 0x11, 0x88, 0x87, 0xd4, 0x2d, 0x8c, 0x81, 0x57, + 0x3c, 0x6d, 0x16, 0x7d, 0x36, 0x40, 0x47, 0xc7, 0x98, 0xf6, 0xff, 0xca, 0xe6, 0x6d, 0x78, 0x2c, 0xf0, 0xaf, 0x01, + 0x99, 0x36, 0x65, 0x99, 0x20, 0x20, 0x61, 0xd4, 0x94, 0x87, 0xb0, 0x97, 0x10, 0xce, 0x6c, 0xc5, 0xac, 0xcf, 0x06, + 0xcd, 0x69, 0x59, 0xb1, 0xe3, 0x2b, 0x36, 0x64, 0x99, 0x60, 0x2b, 0x36, 0x5c, 0xc5, 0xf0, 0x75, 0x06, 0x03, 0x82, + 0x10, 0x00, 0x0c, 0x00, 0xa0, 0x51, 0x10, 0xcd, 0x17, 0x2b, 0xe2, 0x37, 0xbb, 0xbd, 0xc7, 0x6f, 0x81, 0x05, 0x5a, + 0x6d, 0xff, 0xef, 0x42, 0x19, 0xb0, 0xa7, 0x2c, 0x4c, 0xcc, 0xdc, 0xc2, 0xaa, 0xe8, 0x00, 0x2a, 0x25, 0xc2, 0x14, + 0x06, 0x32, 0xfb, 0x99, 0x81, 0x5a, 0xa0, 0x35, 0xc8, 0xfb, 0x7a, 0xd0, 0xcc, 0xe0, 0x88, 0x81, 0x77, 0x68, 0xc8, + 0xd4, 0x18, 0x13, 0xc6, 0x39, 0x4c, 0x31, 0x33, 0xe0, 0x99, 0xa6, 0xad, 0xb5, 0x34, 0xb2, 0x5c, 0x2f, 0xef, 0xfd, + 0xa3, 0x63, 0xd5, 0x2f, 0x9a, 0xed, 0x01, 0xda, 0x27, 0xc4, 0x7e, 0x0c, 0x60, 0x93, 0xb9, 0xd4, 0x86, 0xf9, 0x3e, + 0xea, 0xa4, 0xf6, 0x13, 0xfe, 0x0c, 0xd6, 0x66, 0x07, 0x80, 0x8e, 0x0c, 0x9b, 0xf5, 0x97, 0x35, 0x95, 0xd7, 0xc7, + 0x9d, 0x51, 0x2a, 0x77, 0xbd, 0x3b, 0x1d, 0x68, 0x8a, 0x43, 0x6f, 0x3d, 0x5c, 0x3e, 0xd4, 0x43, 0xc0, 0x8c, 0xc1, + 0xdc, 0x32, 0xa3, 0xef, 0x85, 0x48, 0x2e, 0x88, 0xc4, 0xd2, 0x60, 0x0d, 0x83, 0xbd, 0x75, 0x70, 0x60, 0xaa, 0xb1, + 0x06, 0x3c, 0x4f, 0x8a, 0x40, 0x30, 0xf0, 0x11, 0x94, 0x01, 0x4d, 0x94, 0xb9, 0x0d, 0x27, 0x1f, 0x99, 0xfb, 0x85, + 0xcb, 0xdb, 0xc7, 0xc2, 0x69, 0x5b, 0xcd, 0xf5, 0x78, 0x59, 0xe0, 0xae, 0xbc, 0x97, 0xb4, 0x0a, 0x6e, 0x64, 0x6f, + 0xf2, 0x94, 0xb9, 0x5b, 0xf7, 0xa5, 0x3a, 0xbb, 0x9b, 0xe9, 0x94, 0xcd, 0x74, 0xb6, 0x9b, 0x09, 0x35, 0x33, 0xdf, + 0xb2, 0x8a, 0x34, 0x27, 0x6b, 0xa2, 0xe6, 0x54, 0xfc, 0x44, 0xe7, 0xa0, 0x1d, 0xe5, 0xf6, 0x5e, 0x15, 0x4e, 0xae, + 0x9c, 0x5c, 0xee, 0xe7, 0x86, 0xb8, 0x22, 0x73, 0xa1, 0x0e, 0x01, 0x5e, 0x5e, 0x94, 0x8f, 0x0f, 0x70, 0x29, 0x7e, + 0x95, 0x23, 0x17, 0xe5, 0x54, 0x48, 0x2d, 0x05, 0x8b, 0x90, 0x41, 0x55, 0x17, 0x03, 0x7b, 0x69, 0xf7, 0x9e, 0xe8, + 0xf1, 0x7e, 0x15, 0x31, 0x6f, 0x60, 0x9e, 0xfb, 0xf8, 0x9e, 0xa6, 0xd8, 0xa9, 0x89, 0x33, 0xf2, 0x21, 0x8b, 0x73, + 0x90, 0xcd, 0xfa, 0xd5, 0x6b, 0xbf, 0x8d, 0x36, 0x2e, 0x9a, 0xb1, 0xe8, 0x99, 0x27, 0x4e, 0x7e, 0x28, 0x8c, 0x71, + 0x80, 0x75, 0xf4, 0x47, 0x98, 0x5a, 0xb0, 0x67, 0x89, 0xa7, 0xd0, 0xc9, 0xad, 0x4d, 0xbb, 0x0b, 0xd3, 0xee, 0x4c, + 0x5a, 0x07, 0xca, 0x01, 0x69, 0x76, 0x65, 0x3a, 0x77, 0xfe, 0xfb, 0x0e, 0x5e, 0xba, 0x5d, 0x43, 0x24, 0xee, 0xf9, + 0x23, 0x63, 0x0c, 0xf1, 0x06, 0x6c, 0x44, 0xd5, 0xc1, 0xc1, 0x1f, 0xce, 0xfb, 0xb6, 0x92, 0xfb, 0xbe, 0x15, 0x0e, + 0x6c, 0x83, 0xa9, 0x74, 0x79, 0x23, 0x99, 0x2d, 0xc0, 0xae, 0x73, 0xff, 0x1b, 0xf1, 0xf0, 0x45, 0xc8, 0xb4, 0x58, + 0x57, 0xf1, 0x57, 0x72, 0x54, 0x7a, 0x88, 0x6a, 0x88, 0x40, 0x5a, 0x59, 0x97, 0x86, 0xa6, 0xa3, 0x57, 0x53, 0x3a, + 0x92, 0x37, 0x6f, 0xa5, 0xd4, 0x03, 0xfb, 0x22, 0xb7, 0x4e, 0xe0, 0xd1, 0xc2, 0x1a, 0x43, 0x73, 0x57, 0x7a, 0x27, + 0xd9, 0x80, 0xa8, 0xf5, 0x71, 0x87, 0x92, 0x48, 0x2c, 0xaa, 0xbb, 0x10, 0x0e, 0x77, 0x21, 0x98, 0x97, 0x41, 0xdb, + 0x20, 0x76, 0xbb, 0x0b, 0xda, 0x06, 0x4e, 0xdd, 0x36, 0x70, 0x7b, 0x30, 0x58, 0xd8, 0xfb, 0xf0, 0x72, 0x2c, 0xc7, + 0xc2, 0x5f, 0x93, 0xd9, 0x07, 0x80, 0x40, 0xed, 0xc3, 0x8a, 0x27, 0x0e, 0x04, 0x89, 0x33, 0x1c, 0x7d, 0xcf, 0xd9, + 0x8d, 0xb5, 0x1c, 0x9e, 0xcd, 0x17, 0x9a, 0x8d, 0xcc, 0x1d, 0x35, 0xa8, 0xf8, 0xea, 0x7e, 0x5e, 0x3f, 0x65, 0x35, + 0xdd, 0xf8, 0x3d, 0x08, 0x23, 0xe1, 0x94, 0x1d, 0x46, 0x21, 0x61, 0x83, 0x59, 0x95, 0xf1, 0xda, 0x7e, 0x83, 0x78, + 0x0f, 0xda, 0x84, 0x13, 0x2c, 0x6a, 0x17, 0x54, 0x11, 0xb6, 0xf1, 0xc6, 0x82, 0x28, 0x0f, 0x6f, 0xb6, 0x8c, 0xa6, + 0x97, 0x6b, 0x08, 0x74, 0xdc, 0x8b, 0x9a, 0x51, 0x83, 0xa5, 0x2e, 0x28, 0xb3, 0x8f, 0x30, 0xae, 0x2e, 0x4e, 0x4c, + 0x9c, 0xf6, 0x52, 0xaf, 0xfe, 0x5b, 0x06, 0x06, 0xf8, 0x02, 0xbc, 0xc4, 0xc2, 0xe8, 0xae, 0x7d, 0xdd, 0x80, 0xfa, + 0xb2, 0xc1, 0x06, 0x68, 0xb5, 0x6a, 0x95, 0xcf, 0x40, 0xb9, 0x6b, 0x2e, 0x61, 0xaf, 0xb9, 0x84, 0xbb, 0xe6, 0x12, + 0xfe, 0x9a, 0x4b, 0x98, 0x6b, 0x2e, 0xe1, 0xaf, 0xb9, 0x3c, 0x08, 0x3f, 0x05, 0x71, 0x1c, 0x63, 0x0e, 0x71, 0x15, + 0xb5, 0x8d, 0x8c, 0x07, 0x17, 0x9e, 0xfb, 0x2c, 0x51, 0xe5, 0xf2, 0x87, 0x31, 0xe4, 0xb6, 0x6c, 0x25, 0x8c, 0xdb, + 0x14, 0x53, 0x10, 0x39, 0xfd, 0xe0, 0xa0, 0x74, 0x77, 0x06, 0x1f, 0xf5, 0x94, 0xe3, 0xa5, 0x75, 0xa2, 0xfd, 0x03, + 0x74, 0xf2, 0xe6, 0xd7, 0xc7, 0x54, 0xae, 0x89, 0x70, 0x26, 0xf7, 0xfb, 0x6d, 0x4f, 0x29, 0x3e, 0x65, 0x26, 0x3c, + 0x39, 0x4f, 0xb4, 0x11, 0x41, 0x10, 0xa2, 0x44, 0xe1, 0x8c, 0x48, 0xbb, 0xdf, 0xbd, 0x2b, 0xbc, 0x51, 0x45, 0x79, + 0xb3, 0x92, 0xc7, 0x39, 0x38, 0xb1, 0x1b, 0x2b, 0x0c, 0xd4, 0x05, 0x17, 0x82, 0xcc, 0x24, 0xfc, 0xd1, 0xcc, 0x2d, + 0x39, 0xcb, 0xca, 0xa4, 0x8f, 0xcd, 0xdc, 0x10, 0xb0, 0x82, 0xec, 0x7b, 0x98, 0x2d, 0x6f, 0x53, 0x8a, 0xef, 0xd2, + 0x0c, 0x0f, 0xe5, 0x6d, 0x5a, 0x84, 0xb6, 0x20, 0xfe, 0xe2, 0xef, 0xff, 0x65, 0xef, 0x5d, 0x9b, 0xdb, 0x36, 0xb2, + 0x76, 0xd1, 0xbf, 0x22, 0xb1, 0x6c, 0x06, 0x30, 0x9b, 0x14, 0xe5, 0xbd, 0x67, 0xaa, 0x0e, 0xa8, 0x16, 0xcb, 0xb1, + 0xe3, 0x89, 0x33, 0xf1, 0x65, 0x2c, 0x4f, 0x26, 0x19, 0x16, 0x0f, 0x03, 0x01, 0x4d, 0x01, 0x0e, 0x08, 0x30, 0x00, + 0x28, 0x91, 0x26, 0xf1, 0xdf, 0x77, 0xad, 0xb5, 0xfa, 0x0a, 0x82, 0xb2, 0xe7, 0x7d, 0xf7, 0xfb, 0xe9, 0x9c, 0x2f, + 0xb6, 0xd8, 0x68, 0x34, 0xfa, 0xde, 0xab, 0xd7, 0xe5, 0x79, 0x96, 0x5e, 0x2f, 0x0f, 0x21, 0xde, 0xa7, 0x97, 0xe6, + 0x67, 0x69, 0x2b, 0x0a, 0x70, 0x1f, 0xa1, 0x47, 0x75, 0x20, 0xd8, 0x09, 0x4f, 0x78, 0x00, 0x27, 0xab, 0x59, 0xc5, + 0x9f, 0xa4, 0x20, 0x4e, 0x14, 0x1c, 0x02, 0xae, 0xb6, 0x37, 0xe9, 0x17, 0x30, 0x7c, 0xe9, 0x60, 0xcb, 0xe1, 0x6d, + 0xb1, 0xed, 0xb1, 0x92, 0x7f, 0x00, 0xf6, 0xad, 0x9e, 0x8c, 0xd5, 0xed, 0x81, 0xb3, 0x2e, 0xa5, 0xe8, 0x78, 0x53, + 0x1c, 0xde, 0x9e, 0xcf, 0xf6, 0xdb, 0x20, 0x62, 0xbb, 0x20, 0xc3, 0x5a, 0x27, 0x0d, 0xff, 0x89, 0xb6, 0x0e, 0x16, + 0x23, 0xec, 0xff, 0xb2, 0x1e, 0x78, 0x09, 0xa9, 0xa1, 0xc0, 0xc5, 0x60, 0xc3, 0xd1, 0xda, 0x2e, 0xd3, 0xc0, 0x4d, + 0x0d, 0x7a, 0x7d, 0x4f, 0x21, 0xca, 0x4b, 0x46, 0x73, 0x23, 0x58, 0x37, 0x86, 0x5c, 0x1c, 0x8e, 0x9b, 0xe5, 0x90, + 0x97, 0x34, 0x9d, 0x06, 0xa1, 0x74, 0x67, 0x59, 0x43, 0x12, 0x65, 0x1f, 0x84, 0xda, 0xb5, 0x65, 0xbf, 0x0d, 0x6c, + 0x5f, 0xfe, 0x68, 0x18, 0xfb, 0x17, 0xcb, 0x67, 0x42, 0xba, 0x88, 0xe7, 0x20, 0x88, 0xda, 0xcf, 0xb3, 0xe1, 0xc6, + 0xbf, 0x58, 0x3f, 0x13, 0xca, 0x6f, 0x3c, 0xb7, 0xe5, 0x90, 0x3a, 0x6b, 0xe1, 0x0b, 0xe3, 0xe1, 0xc1, 0x95, 0xa1, + 0xed, 0x70, 0x10, 0xfa, 0x6f, 0xb3, 0x46, 0x70, 0x63, 0x43, 0xfb, 0x7c, 0xe1, 0xc3, 0xd6, 0x46, 0x63, 0x4d, 0x31, + 0xdd, 0x42, 0xff, 0x26, 0xb3, 0xa5, 0x3d, 0x8d, 0x4a, 0x5e, 0x9c, 0x9a, 0x46, 0x2c, 0x84, 0x01, 0x43, 0x3f, 0x99, + 0x0f, 0xa0, 0x9a, 0x3b, 0x1e, 0x81, 0x4c, 0x3e, 0xd0, 0x83, 0x35, 0xa9, 0x55, 0x7f, 0x0d, 0x33, 0xf9, 0x7f, 0xa4, + 0xc2, 0x62, 0x74, 0xb7, 0x0d, 0x33, 0xf5, 0x47, 0x24, 0xff, 0x60, 0x39, 0xdf, 0xa5, 0x5e, 0xa8, 0xfd, 0x58, 0x58, + 0x81, 0x41, 0x89, 0xaa, 0x01, 0x3d, 0x10, 0x41, 0x55, 0x06, 0x69, 0x86, 0xd5, 0x39, 0xe8, 0x77, 0x4f, 0xab, 0x8e, + 0xe4, 0x90, 0xd6, 0x6a, 0x48, 0x05, 0x53, 0xa5, 0x06, 0xf9, 0xe1, 0x70, 0x97, 0x32, 0x5d, 0x06, 0x5c, 0xd2, 0xef, + 0x52, 0xa5, 0x14, 0xfe, 0x13, 0x01, 0xe8, 0x1c, 0xdc, 0xe3, 0xcb, 0x31, 0x90, 0x66, 0x58, 0xf8, 0xad, 0xd9, 0xf1, + 0x35, 0x09, 0xb7, 0x49, 0x70, 0x31, 0xc0, 0x39, 0xba, 0x0a, 0xcb, 0xbb, 0x14, 0x22, 0xa8, 0x4a, 0xa8, 0x6f, 0x65, + 0x1a, 0x94, 0xb6, 0x1a, 0x84, 0x35, 0x09, 0x75, 0x26, 0xd9, 0xa8, 0xb4, 0xdd, 0x28, 0xcc, 0x16, 0x71, 0x3d, 0x23, + 0xac, 0x39, 0x9b, 0xa9, 0x06, 0x26, 0x0d, 0xc7, 0x4d, 0xa3, 0xb5, 0xa8, 0x50, 0x53, 0x98, 0xd7, 0xb8, 0xaa, 0x54, + 0x75, 0x37, 0xa7, 0x96, 0xd2, 0xb2, 0xbd, 0xea, 0x26, 0xd9, 0x90, 0xcb, 0x50, 0x86, 0xc1, 0x46, 0x8e, 0x60, 0x02, + 0x49, 0x72, 0xe6, 0x6f, 0xe4, 0x1f, 0x6a, 0xd3, 0xb5, 0x80, 0x39, 0xc6, 0x2c, 0x1b, 0x16, 0xf4, 0x0a, 0xdc, 0x03, + 0xad, 0xf4, 0x7c, 0x9a, 0x5d, 0xe4, 0x41, 0x32, 0x2c, 0xf4, 0xb2, 0xc9, 0xf8, 0x9f, 0xc2, 0x48, 0x93, 0x19, 0x2b, + 0x59, 0x64, 0xbb, 0x3a, 0x25, 0xce, 0xe3, 0x04, 0xb6, 0x47, 0xd3, 0x5b, 0xbe, 0xcf, 0x20, 0x2a, 0x08, 0x14, 0xcc, + 0x98, 0x2f, 0xbb, 0x78, 0xee, 0xfb, 0xcc, 0x32, 0x75, 0x1f, 0x0e, 0xc6, 0x8c, 0xed, 0xf7, 0xfb, 0x79, 0xbf, 0xaf, + 0xe6, 0x5b, 0xbf, 0x9f, 0x5c, 0x9b, 0xbf, 0x3d, 0x60, 0x50, 0x90, 0x13, 0xd1, 0x54, 0x88, 0xe0, 0x1f, 0x92, 0x67, + 0x48, 0x46, 0x77, 0xdc, 0xe7, 0x96, 0xb3, 0x65, 0x75, 0x04, 0x82, 0x79, 0x38, 0x5c, 0x2a, 0xb0, 0x6b, 0x89, 0x22, + 0x21, 0xcb, 0x7f, 0x06, 0xc6, 0x33, 0xf7, 0x01, 0x96, 0x0c, 0x40, 0xd8, 0x2a, 0x4f, 0xd7, 0x7b, 0xbe, 0x0a, 0xde, + 0xe9, 0x78, 0xd7, 0x58, 0x91, 0x81, 0xb8, 0x05, 0x36, 0x62, 0xad, 0x3d, 0x20, 0x67, 0x0a, 0x70, 0xbc, 0x38, 0x1c, + 0xce, 0xe5, 0x2f, 0xdd, 0x6c, 0x9d, 0x40, 0xa5, 0xc0, 0xed, 0xd1, 0xc9, 0xc1, 0x7f, 0x07, 0x9a, 0x41, 0x39, 0xcc, + 0xeb, 0xed, 0xef, 0xcc, 0xc9, 0x4f, 0x4f, 0xf1, 0x4f, 0x78, 0x88, 0x4e, 0xbf, 0xdd, 0x9b, 0x3f, 0x28, 0x2a, 0x0f, + 0x07, 0xb5, 0xf8, 0xcf, 0x39, 0xaf, 0xe0, 0x17, 0xbe, 0x09, 0xcc, 0x26, 0x53, 0xef, 0xe4, 0x9b, 0x3c, 0x67, 0xea, + 0x35, 0x5e, 0x31, 0xf9, 0x0e, 0x87, 0x73, 0x31, 0xaa, 0xb7, 0x23, 0x27, 0xda, 0x29, 0xc7, 0x38, 0x18, 0xfc, 0x17, + 0xd1, 0x36, 0x21, 0xc0, 0x90, 0xba, 0x25, 0xcd, 0x6c, 0x5c, 0x59, 0xe2, 0x59, 0x3a, 0xbf, 0x9c, 0xd4, 0xe5, 0x4e, + 0x2b, 0x9e, 0xf6, 0xc0, 0xe2, 0xb6, 0x06, 0x2f, 0x80, 0x7b, 0x8b, 0xad, 0x2b, 0x05, 0x87, 0x0b, 0x88, 0x53, 0x9c, + 0x80, 0x08, 0xda, 0xef, 0x4b, 0xbc, 0x57, 0xd0, 0x27, 0xfd, 0x00, 0xc1, 0x90, 0x3f, 0x4b, 0xc0, 0x5d, 0xaf, 0x57, + 0x63, 0x7c, 0x2f, 0x85, 0xe0, 0xfa, 0x4c, 0x03, 0xd0, 0x82, 0xdf, 0xe5, 0x63, 0x39, 0xfd, 0x26, 0x02, 0xcf, 0x96, + 0xbd, 0x89, 0x72, 0xb7, 0xe1, 0x69, 0xff, 0x68, 0x21, 0x00, 0x4b, 0xf1, 0x4c, 0x09, 0x16, 0xe4, 0x14, 0x73, 0xf1, + 0xff, 0x82, 0x8f, 0x98, 0xef, 0x49, 0x17, 0xb1, 0xf5, 0xf6, 0xc9, 0x85, 0x81, 0x04, 0x9a, 0x0e, 0xc0, 0x8f, 0x57, + 0x01, 0x5d, 0x19, 0x3f, 0x3f, 0xcb, 0x7a, 0xac, 0x8f, 0xff, 0x14, 0xdc, 0xa7, 0x9f, 0x29, 0x7c, 0x74, 0x38, 0xae, + 0xd2, 0xd1, 0x8e, 0x52, 0x10, 0x1d, 0xdd, 0x3e, 0x9f, 0xf2, 0xec, 0x9b, 0x0a, 0xc8, 0x2d, 0x47, 0xed, 0xa9, 0x00, + 0x2c, 0xb6, 0x74, 0x04, 0x3e, 0xcd, 0xf2, 0x09, 0xf9, 0x5e, 0x4f, 0xc5, 0xd5, 0xa5, 0x4e, 0x17, 0xd7, 0xe3, 0x29, + 0xfc, 0x0f, 0xc4, 0x1e, 0x96, 0x29, 0xb2, 0x63, 0xd7, 0xc5, 0x0f, 0xe2, 0x6d, 0x6d, 0x47, 0x7f, 0xec, 0x20, 0xd2, + 0x71, 0x4f, 0x2e, 0xd4, 0x97, 0x90, 0x4a, 0x2e, 0xd4, 0x0d, 0xc4, 0x2e, 0xd4, 0x78, 0xc7, 0x45, 0xac, 0xf5, 0xb7, + 0x35, 0x0a, 0x56, 0x02, 0xce, 0xb4, 0xb7, 0x60, 0xb0, 0x81, 0x75, 0xcb, 0x32, 0xf8, 0x1b, 0xae, 0x69, 0x02, 0x37, + 0x2c, 0xb2, 0xde, 0x1b, 0x6c, 0xa5, 0xb7, 0xe0, 0x68, 0x99, 0x38, 0x97, 0x92, 0xac, 0x6c, 0x91, 0x71, 0xf5, 0x28, + 0xa4, 0x6a, 0xba, 0xbf, 0x15, 0xf5, 0x83, 0x10, 0x79, 0xb0, 0x4a, 0x59, 0x54, 0xac, 0x40, 0x66, 0x0f, 0xfe, 0x11, + 0x32, 0x72, 0x94, 0x03, 0x47, 0xa1, 0xbf, 0x35, 0x81, 0xce, 0xf3, 0x53, 0xa8, 0xf3, 0x48, 0xb0, 0x95, 0x7a, 0x28, + 0xac, 0xbc, 0x80, 0xe8, 0x60, 0x0b, 0x63, 0x95, 0x27, 0xa1, 0x62, 0x53, 0x26, 0xf2, 0x38, 0xa8, 0x25, 0x60, 0xac, + 0x20, 0x98, 0xb3, 0x5c, 0xba, 0x20, 0x55, 0x8d, 0x1e, 0x16, 0x99, 0xfb, 0xa9, 0xa0, 0xfc, 0x4f, 0x55, 0x4e, 0xb8, + 0xbe, 0x0c, 0x01, 0x8e, 0xf6, 0x29, 0x88, 0x12, 0x63, 0xfd, 0xa2, 0xc5, 0x3b, 0x99, 0x39, 0x9b, 0xda, 0x5e, 0x82, + 0x8c, 0xed, 0xf0, 0x2b, 0x84, 0x56, 0x0b, 0x45, 0x16, 0x0d, 0x17, 0x4c, 0xb7, 0xa7, 0xb4, 0xea, 0x1e, 0x36, 0x3c, + 0x2b, 0x3d, 0x54, 0xea, 0xdb, 0x98, 0xc0, 0xb2, 0x4a, 0x19, 0xbe, 0x9d, 0x50, 0x75, 0x62, 0x50, 0xb1, 0x6e, 0xd8, + 0x12, 0x0e, 0xb1, 0x98, 0x34, 0xd6, 0xd9, 0x80, 0x47, 0x2c, 0x81, 0x7f, 0x36, 0x7c, 0xcc, 0x96, 0x3c, 0x9a, 0x6c, + 0xae, 0x96, 0xfd, 0x7e, 0xe9, 0x85, 0x5e, 0x3d, 0xcb, 0x9e, 0x46, 0xf3, 0x59, 0x3e, 0xf7, 0x51, 0x71, 0x31, 0x19, + 0x0c, 0x36, 0x7e, 0x36, 0x1c, 0xb2, 0x64, 0x38, 0x9c, 0x64, 0x4f, 0xe1, 0xb5, 0xa7, 0x3c, 0x52, 0x4b, 0x2a, 0xb9, + 0xca, 0x60, 0x7f, 0x1f, 0xf0, 0xc8, 0x67, 0x9d, 0x9f, 0x96, 0x4d, 0x97, 0xee, 0x67, 0x76, 0xdc, 0x85, 0xee, 0x00, + 0x1b, 0x6f, 0x1b, 0x74, 0xe4, 0x5f, 0xef, 0x90, 0x52, 0x37, 0x19, 0x80, 0xdd, 0x68, 0x80, 0x43, 0xa6, 0x7a, 0x29, + 0xb2, 0x7a, 0x29, 0x53, 0xbd, 0x24, 0x2b, 0x97, 0x60, 0x21, 0x31, 0x55, 0x6e, 0x23, 0x2b, 0xb7, 0x6c, 0xb8, 0x1e, + 0x0e, 0xb6, 0x56, 0x5c, 0x36, 0x77, 0x70, 0x5f, 0x58, 0x51, 0xe0, 0xff, 0x2d, 0x5b, 0xb0, 0x7b, 0x79, 0x0c, 0xbc, + 0x45, 0xc7, 0x24, 0xb8, 0x40, 0xdc, 0xb3, 0x5b, 0xb0, 0xc3, 0xc2, 0x5f, 0x70, 0x9d, 0x1c, 0xb3, 0x1d, 0x3e, 0x0a, + 0xbd, 0x82, 0xdd, 0xfa, 0x04, 0xb4, 0x0b, 0xb6, 0x06, 0xc8, 0xc6, 0xb6, 0xf8, 0xe8, 0xee, 0x70, 0x78, 0xeb, 0xf9, + 0xec, 0x01, 0x7f, 0x9c, 0xdf, 0x1d, 0x0e, 0x3b, 0xcf, 0xa8, 0xf7, 0x6e, 0x78, 0xc2, 0xde, 0xf3, 0x64, 0x72, 0x73, + 0xc5, 0xe3, 0xc9, 0x60, 0x70, 0xe3, 0x2f, 0x78, 0x3d, 0xbb, 0x01, 0xed, 0xc0, 0xf9, 0x42, 0xea, 0x9a, 0xbd, 0x5b, + 0x9e, 0x79, 0x0b, 0x1c, 0x9b, 0x5b, 0x38, 0x7a, 0xfb, 0x7d, 0xef, 0x8e, 0x47, 0xde, 0x2d, 0xa9, 0x98, 0x56, 0x5c, + 0x71, 0xbc, 0x6d, 0x71, 0x3f, 0x5d, 0xf1, 0x10, 0x1e, 0x61, 0x55, 0xa6, 0x37, 0xc1, 0x7b, 0x9f, 0xad, 0x34, 0x0b, + 0xdc, 0x03, 0xe6, 0x58, 0x93, 0x9d, 0xd0, 0x4c, 0xfc, 0x15, 0xf6, 0xcf, 0x8d, 0xea, 0x1f, 0x9a, 0xff, 0xa5, 0xee, + 0x27, 0x70, 0xfb, 0x22, 0x0b, 0x12, 0x7b, 0xcf, 0x6f, 0xd8, 0x3d, 0x37, 0x6c, 0xb3, 0x67, 0xa6, 0xec, 0x13, 0xa5, + 0xc6, 0x8f, 0x94, 0xba, 0xb6, 0x0c, 0x2b, 0x99, 0xbb, 0x2f, 0x23, 0x70, 0x38, 0x20, 0x3f, 0xdd, 0x21, 0x0e, 0x42, + 0xeb, 0x26, 0xab, 0xb9, 0xa2, 0x9c, 0x0b, 0x6d, 0x99, 0x79, 0x39, 0xb0, 0x98, 0xa5, 0x14, 0x1a, 0x0b, 0x00, 0x04, + 0x93, 0x42, 0x6b, 0xef, 0x65, 0x00, 0x39, 0x41, 0xc3, 0x1f, 0x9b, 0xab, 0xa2, 0xac, 0x65, 0x4b, 0x42, 0x94, 0xed, + 0x7a, 0x78, 0x89, 0x90, 0x69, 0xfd, 0xfe, 0x39, 0x91, 0xac, 0x4d, 0xaa, 0xab, 0x1a, 0x2d, 0x01, 0x15, 0x59, 0x02, + 0x26, 0x7e, 0xa5, 0xf9, 0x04, 0xe0, 0x49, 0xc7, 0x83, 0xea, 0x29, 0xaf, 0x99, 0x20, 0xb2, 0x8d, 0xca, 0x9f, 0x14, + 0xd7, 0x48, 0x46, 0x50, 0x3c, 0xad, 0x55, 0xc6, 0xc2, 0x30, 0x0f, 0x14, 0x90, 0x77, 0xef, 0x4e, 0x7d, 0x6b, 0x7f, + 0xec, 0xd8, 0xb3, 0xb5, 0x0a, 0xb5, 0x50, 0x53, 0xb8, 0xe4, 0x10, 0x5d, 0x41, 0x06, 0x0a, 0x19, 0x4f, 0x5e, 0x0f, + 0x2e, 0x27, 0xd1, 0x15, 0x17, 0xe8, 0x8c, 0xaf, 0x6f, 0xba, 0xe9, 0x2c, 0x7a, 0x5a, 0xcd, 0x27, 0xa4, 0x24, 0x3b, + 0x1c, 0xb2, 0x51, 0x55, 0x17, 0xeb, 0x69, 0x28, 0x7f, 0x7a, 0x08, 0xbe, 0x5e, 0x50, 0xaf, 0xc9, 0x2a, 0xd5, 0x4f, + 0xa9, 0x52, 0x5e, 0x34, 0xbc, 0xf4, 0x9f, 0x56, 0x72, 0xdf, 0x03, 0xd2, 0x5a, 0x5e, 0x72, 0xf9, 0x7e, 0x84, 0x18, + 0x23, 0x7e, 0xe0, 0x95, 0x3c, 0x62, 0xa1, 0x9a, 0xc2, 0x35, 0x8f, 0x10, 0xe4, 0x2d, 0xd3, 0xc1, 0xdf, 0x7a, 0xe2, + 0x74, 0x7f, 0xa2, 0xb4, 0x8b, 0x2f, 0x2c, 0xea, 0x9e, 0xac, 0xad, 0x1b, 0x90, 0x83, 0x0d, 0xd3, 0x45, 0x41, 0xb6, + 0x29, 0x8d, 0xa0, 0x8d, 0x96, 0x03, 0x1b, 0x4e, 0xa5, 0x36, 0x9c, 0xb9, 0x86, 0xe0, 0x3e, 0x3f, 0x4f, 0x47, 0x0b, + 0xf8, 0x90, 0xea, 0xf6, 0x12, 0x3f, 0x1f, 0x36, 0x3c, 0x02, 0x32, 0x3b, 0xe2, 0x33, 0x9b, 0x48, 0x3a, 0xa9, 0x73, + 0x05, 0xec, 0x76, 0xf6, 0x16, 0xe4, 0x88, 0x99, 0xfb, 0x0a, 0xd5, 0xb7, 0x68, 0xc0, 0x95, 0xb1, 0xf6, 0x35, 0xc9, + 0x58, 0x78, 0x55, 0x4e, 0xc3, 0x01, 0xc0, 0xd0, 0x65, 0xf4, 0xb5, 0xe5, 0x26, 0xcb, 0x7e, 0x2e, 0x20, 0x08, 0xa2, + 0x24, 0x1e, 0x1f, 0xf0, 0xbe, 0xac, 0x86, 0x1a, 0x25, 0x1f, 0xcb, 0x46, 0x2a, 0xbd, 0x12, 0xfd, 0xdd, 0x98, 0x4b, + 0x0c, 0xf8, 0xb6, 0x6a, 0x0b, 0x0a, 0xe7, 0xf9, 0xe1, 0x70, 0x9e, 0x8f, 0x8c, 0x67, 0x19, 0xa8, 0x56, 0xa6, 0x75, + 0x10, 0x9b, 0xf9, 0x62, 0xe1, 0x2f, 0x76, 0x4e, 0x22, 0xa2, 0x20, 0xb0, 0x23, 0xe1, 0x41, 0xa4, 0x7e, 0x59, 0x79, + 0xba, 0x53, 0x7d, 0xb6, 0x5f, 0xd8, 0x44, 0x7a, 0x41, 0xc9, 0xe4, 0x93, 0x60, 0xaf, 0xfa, 0x3b, 0x08, 0x1b, 0xc2, + 0x9b, 0x57, 0xbd, 0xce, 0x32, 0x35, 0x2b, 0x41, 0xc2, 0x8c, 0x39, 0x82, 0xc7, 0x61, 0xa7, 0xb1, 0x0d, 0x8f, 0x2d, + 0x38, 0x3a, 0x6f, 0xcd, 0xee, 0xd8, 0x8a, 0xdd, 0xaa, 0x3a, 0x2d, 0x78, 0x38, 0x1d, 0x5e, 0x06, 0xb8, 0xfa, 0xd6, + 0xe7, 0x9c, 0xdf, 0xd1, 0x09, 0xb6, 0x1e, 0xf0, 0x68, 0x22, 0x66, 0xeb, 0xa7, 0x91, 0x5a, 0x3c, 0xeb, 0x21, 0x5f, + 0xd0, 0xfa, 0x13, 0xb3, 0x3b, 0x93, 0x7c, 0x37, 0xe0, 0x8b, 0xc9, 0xfa, 0x69, 0x04, 0xaf, 0x3e, 0x05, 0x2b, 0x46, + 0xe6, 0xcc, 0xb2, 0xf5, 0xd3, 0x08, 0xc7, 0xec, 0xee, 0x69, 0x44, 0xa3, 0xb6, 0x92, 0xfb, 0xd2, 0x6d, 0x03, 0xc2, + 0xca, 0x2d, 0x8b, 0xe1, 0x35, 0x10, 0xcf, 0xb4, 0x91, 0x74, 0x2d, 0x0d, 0xbd, 0x31, 0x0f, 0xa7, 0x71, 0xb0, 0xa6, + 0x56, 0xc8, 0x33, 0x43, 0xcc, 0xe2, 0xa7, 0xd1, 0x9c, 0xad, 0xb0, 0x22, 0x1b, 0x1e, 0x0f, 0x2e, 0x27, 0x9b, 0x2b, + 0xbe, 0x06, 0xf2, 0xb3, 0xc9, 0xc6, 0x6c, 0x51, 0xb7, 0x5c, 0xcc, 0x36, 0x4f, 0xa3, 0xf9, 0x64, 0x05, 0x3d, 0x6b, + 0x0f, 0x98, 0xf7, 0x1a, 0x44, 0x28, 0x09, 0xa9, 0x29, 0x37, 0xbd, 0x1e, 0x5b, 0x8f, 0x83, 0x3b, 0xb6, 0xbe, 0x0c, + 0x6e, 0xd9, 0x7a, 0x0c, 0x44, 0x1c, 0xd4, 0xef, 0xde, 0x06, 0x16, 0x5f, 0xc4, 0xd6, 0x97, 0x26, 0x6d, 0xf3, 0x34, + 0x62, 0xee, 0xe0, 0x34, 0x70, 0xc1, 0xda, 0x64, 0xde, 0x8a, 0xc1, 0x25, 0x64, 0xe9, 0xc5, 0x6c, 0x33, 0xbc, 0x64, + 0xeb, 0x11, 0x4e, 0xf5, 0xc4, 0x67, 0x77, 0xfc, 0x96, 0x25, 0x7c, 0xd5, 0xc4, 0x57, 0x1b, 0xd0, 0x88, 0x1e, 0x65, + 0xd0, 0x57, 0x50, 0x33, 0x73, 0x5e, 0x5a, 0x18, 0x95, 0xfb, 0x16, 0x1c, 0x50, 0x90, 0xb6, 0x01, 0x82, 0x24, 0x9e, + 0xdd, 0xcb, 0x70, 0x7d, 0x23, 0x85, 0x01, 0x37, 0x81, 0x19, 0x30, 0x30, 0xfd, 0x0c, 0x7e, 0x58, 0xe9, 0x12, 0x21, + 0xce, 0x7e, 0x4a, 0x49, 0x32, 0xcf, 0x4f, 0x45, 0x9a, 0xbb, 0x85, 0xeb, 0x14, 0x66, 0x45, 0x81, 0xea, 0xa7, 0xa4, + 0x34, 0xb0, 0x50, 0x89, 0x4c, 0xa5, 0xe0, 0x97, 0xcd, 0x79, 0x94, 0x1d, 0xa3, 0x73, 0x9d, 0x5f, 0x4e, 0x9c, 0xd3, + 0x49, 0xdf, 0x7f, 0xe0, 0x18, 0xb6, 0x90, 0x81, 0x0b, 0x7f, 0xea, 0x09, 0xe3, 0xd4, 0x0a, 0xc4, 0x54, 0xf2, 0xec, + 0x29, 0x7c, 0x26, 0xb4, 0x3a, 0xba, 0xf0, 0xfd, 0xa0, 0xd0, 0x26, 0xe9, 0x16, 0x24, 0x29, 0x78, 0x8a, 0x9e, 0x73, + 0xde, 0x06, 0x2a, 0xc5, 0x88, 0x16, 0x44, 0xda, 0x5a, 0x66, 0x0e, 0xd2, 0x96, 0xe6, 0xbb, 0x26, 0x7e, 0x0e, 0x0b, + 0xb8, 0x88, 0x16, 0xb6, 0x86, 0x47, 0x55, 0xac, 0xdc, 0x9b, 0x3c, 0x47, 0x38, 0xa3, 0x4b, 0x99, 0x00, 0xb8, 0xde, + 0xaf, 0xc2, 0x5a, 0xe1, 0x15, 0x35, 0x8b, 0xbc, 0xa8, 0xe9, 0x93, 0x2d, 0x70, 0x1f, 0x8b, 0x12, 0x05, 0xce, 0x5a, + 0x30, 0x60, 0x2b, 0x2c, 0xd9, 0x49, 0x61, 0x53, 0xb4, 0x84, 0xde, 0x1e, 0x3f, 0x1d, 0xd4, 0x4c, 0x06, 0xd0, 0x04, + 0xd0, 0x78, 0xfc, 0x0b, 0x40, 0x4d, 0x6f, 0x6a, 0xb1, 0xae, 0x82, 0x52, 0x29, 0x37, 0xe1, 0x67, 0x60, 0x98, 0xe1, + 0x87, 0x42, 0x6e, 0x13, 0x25, 0x72, 0x7e, 0x2c, 0x4a, 0xb1, 0x2c, 0x45, 0x95, 0xb4, 0x1b, 0x0a, 0x1e, 0x11, 0x6e, + 0x83, 0xc6, 0xcc, 0xed, 0x89, 0x2e, 0x5a, 0x11, 0xca, 0xb1, 0x59, 0xc7, 0x48, 0xa3, 0xcc, 0x4e, 0x76, 0x9d, 0x2c, + 0xb4, 0xdf, 0x57, 0x39, 0x64, 0x1d, 0xb0, 0x46, 0xf2, 0xf5, 0x9a, 0x43, 0xb7, 0x8d, 0xf2, 0xe2, 0xc1, 0xf3, 0x15, + 0x9c, 0xe6, 0x78, 0x62, 0x77, 0xbd, 0xee, 0x14, 0x89, 0x78, 0x85, 0x93, 0x2a, 0x1f, 0xc9, 0xc2, 0x71, 0xe7, 0x4e, + 0x6b, 0xb1, 0xaa, 0x5c, 0xd6, 0x53, 0x8b, 0x23, 0x02, 0x9f, 0xca, 0xa3, 0xbd, 0xd0, 0xb6, 0x28, 0x16, 0xc2, 0xe8, + 0xd1, 0x09, 0x3f, 0x29, 0x81, 0xf5, 0x75, 0x38, 0x2c, 0xfd, 0x88, 0xa3, 0xdf, 0x69, 0x34, 0x5a, 0x10, 0xd2, 0xf0, + 0xd4, 0x8b, 0x46, 0x8b, 0xba, 0xa8, 0xc3, 0xec, 0x3a, 0xd7, 0x03, 0x85, 0x61, 0x04, 0xea, 0x07, 0x57, 0x19, 0x7c, + 0x16, 0x21, 0x6a, 0x1e, 0x98, 0x66, 0x43, 0x38, 0xea, 0x02, 0x0f, 0xad, 0xa0, 0xc5, 0xcc, 0x7c, 0x14, 0x62, 0xf8, + 0x90, 0x2e, 0xce, 0x9f, 0x90, 0x95, 0x0f, 0xb0, 0x3b, 0x74, 0x17, 0xca, 0x39, 0x53, 0x31, 0xc0, 0x8f, 0x02, 0xf2, + 0x51, 0x02, 0x6e, 0x06, 0xc8, 0x1e, 0x59, 0x02, 0x88, 0x15, 0xa3, 0xa3, 0xc9, 0xe7, 0xbe, 0x17, 0x29, 0x78, 0x67, + 0x9f, 0xe5, 0x6a, 0xc2, 0x50, 0xf8, 0xc4, 0x40, 0x37, 0xbf, 0xf1, 0xdb, 0xf3, 0x16, 0x8c, 0xec, 0x92, 0x14, 0xaf, + 0x35, 0xc3, 0xfd, 0x06, 0xdc, 0x8e, 0x80, 0xb2, 0xa6, 0x3a, 0x26, 0xd9, 0xa6, 0x21, 0x92, 0x01, 0x33, 0x62, 0x44, + 0x50, 0x59, 0x2e, 0xfc, 0xef, 0x5e, 0x16, 0x05, 0x0e, 0xe0, 0x6a, 0x26, 0x83, 0xd7, 0x2e, 0x8c, 0x0a, 0x80, 0x73, + 0x1a, 0x3a, 0xa5, 0xbd, 0xaa, 0x3a, 0x24, 0xab, 0xe6, 0x07, 0xb3, 0x79, 0xd3, 0x30, 0x31, 0x22, 0x88, 0x2e, 0xc2, + 0x09, 0xa6, 0x57, 0xa4, 0xaf, 0x95, 0x9c, 0x8e, 0x56, 0x1d, 0xad, 0x25, 0x26, 0xe6, 0x8a, 0xe2, 0xaf, 0x01, 0x8f, + 0x1b, 0xbc, 0x3a, 0x49, 0xd3, 0x89, 0xea, 0xd1, 0xe3, 0xd7, 0x69, 0x3a, 0x29, 0x71, 0x57, 0xf8, 0x0d, 0xb8, 0x68, + 0xb6, 0xf9, 0xd0, 0x8f, 0x5f, 0x50, 0xc4, 0x45, 0x0d, 0xae, 0xbc, 0x53, 0x7d, 0xa5, 0xfa, 0x08, 0x6a, 0xe1, 0x89, + 0x91, 0xb5, 0xf0, 0xe4, 0x92, 0xb5, 0x16, 0x04, 0x33, 0x9b, 0x03, 0x17, 0xf2, 0x2b, 0xa5, 0x88, 0x37, 0x91, 0x50, + 0x8b, 0x41, 0xeb, 0x31, 0x73, 0x56, 0x8d, 0x16, 0x2a, 0x33, 0x42, 0xfb, 0xb6, 0x16, 0x9d, 0xdf, 0xc8, 0x4f, 0x79, + 0x6a, 0x5f, 0xb6, 0xc7, 0xf9, 0x78, 0x8f, 0xee, 0xaa, 0xb3, 0xcc, 0xa4, 0x8c, 0x4f, 0x66, 0x09, 0x0a, 0x77, 0x09, + 0x36, 0x20, 0xc9, 0x7e, 0xad, 0x03, 0x64, 0xd4, 0x5e, 0xfb, 0x5d, 0x67, 0xf9, 0xea, 0x66, 0x6b, 0x28, 0x2a, 0xb5, + 0x92, 0x14, 0x07, 0x19, 0xae, 0xdb, 0xca, 0x87, 0x8b, 0x0b, 0xe8, 0x19, 0x23, 0x91, 0x79, 0xfe, 0x44, 0xbe, 0x04, + 0xe7, 0x8c, 0xb3, 0x42, 0x60, 0xc2, 0x58, 0xbd, 0x6b, 0x2d, 0x95, 0x86, 0x14, 0x63, 0x47, 0xa3, 0x2c, 0xab, 0x2c, + 0x5d, 0x66, 0x6b, 0x09, 0x5b, 0x96, 0x93, 0x5b, 0xd8, 0x32, 0x93, 0xd5, 0x7c, 0x5f, 0x71, 0x07, 0xe5, 0x9b, 0xad, + 0x33, 0xbe, 0x97, 0xc8, 0xde, 0x6d, 0xa0, 0x84, 0xeb, 0xd1, 0x5f, 0x90, 0x7e, 0x9b, 0x61, 0x9c, 0x72, 0x5b, 0x49, + 0x0b, 0x70, 0xfa, 0x87, 0xc3, 0xfb, 0x0a, 0x83, 0x06, 0x47, 0x18, 0x47, 0xd6, 0xef, 0xdf, 0x56, 0x5e, 0x8d, 0x89, + 0x3a, 0x3e, 0xab, 0xdf, 0xaf, 0xe8, 0xe1, 0xb4, 0x1a, 0xad, 0xd2, 0x2d, 0xb2, 0x13, 0xda, 0x58, 0xf9, 0x41, 0xad, + 0x80, 0xd9, 0x5b, 0x9f, 0x4f, 0x07, 0xa0, 0x63, 0x01, 0x12, 0xcd, 0x66, 0x22, 0x31, 0x27, 0xdd, 0x93, 0xf0, 0xf8, + 0xc0, 0x02, 0x07, 0x98, 0x8a, 0xff, 0x53, 0x78, 0x33, 0xb0, 0x41, 0xa3, 0x44, 0x5f, 0xa3, 0xab, 0xda, 0xdc, 0xe8, + 0x78, 0xe9, 0x29, 0x24, 0xb2, 0x82, 0x55, 0x73, 0x5f, 0x6e, 0xe0, 0xb4, 0x87, 0x9a, 0x43, 0x65, 0x09, 0xfe, 0xf6, + 0xcb, 0xfc, 0x70, 0x58, 0x67, 0x50, 0xd8, 0x6e, 0x2d, 0xb4, 0x37, 0x66, 0xa9, 0x86, 0x8a, 0x70, 0xd0, 0xf9, 0x4a, + 0xcc, 0xea, 0x11, 0xfd, 0x3d, 0x3f, 0x1c, 0x56, 0x04, 0x06, 0x1c, 0x96, 0x32, 0x13, 0x2d, 0x14, 0x4b, 0xeb, 0x6c, + 0x46, 0x75, 0xe0, 0x81, 0x89, 0x39, 0x0b, 0x77, 0x00, 0xda, 0xa4, 0x56, 0x81, 0x5e, 0x45, 0xf4, 0x13, 0xf7, 0x6b, + 0xfb, 0xf5, 0x7a, 0x64, 0x96, 0x8e, 0xdc, 0x18, 0x0b, 0x00, 0x0e, 0x3c, 0xaf, 0x49, 0x9e, 0x93, 0xaf, 0xa1, 0xdd, + 0x93, 0x0b, 0xf9, 0x13, 0x94, 0x2d, 0x3c, 0x57, 0x4d, 0x2b, 0x8b, 0x15, 0x57, 0xd5, 0xab, 0x0b, 0x5e, 0x99, 0x4c, + 0xab, 0xb4, 0x12, 0x95, 0x12, 0x0c, 0xa8, 0x4b, 0xbc, 0xd6, 0x34, 0xa3, 0xd4, 0x46, 0x9d, 0x89, 0x1a, 0xb0, 0xc1, + 0x7e, 0xaa, 0x36, 0x3a, 0x39, 0x97, 0xcf, 0x2f, 0x8d, 0xc3, 0xa7, 0x5d, 0xbd, 0x99, 0xa9, 0x1c, 0xf8, 0x6b, 0xe5, + 0x43, 0xab, 0xc7, 0x40, 0x07, 0xe4, 0xf4, 0xc7, 0xb0, 0x98, 0xd8, 0x1d, 0x9a, 0xb7, 0xbb, 0xcb, 0xea, 0x22, 0xbd, + 0xd3, 0x94, 0xcc, 0xea, 0x2d, 0x9f, 0x59, 0x3d, 0x3a, 0xe0, 0xc5, 0x63, 0xbd, 0x57, 0x98, 0x49, 0x04, 0x17, 0x43, + 0x35, 0x89, 0xec, 0x0e, 0xb4, 0xe6, 0x51, 0xc5, 0x04, 0xf8, 0x41, 0xa9, 0x35, 0xbd, 0xb7, 0xbb, 0x42, 0x9d, 0x52, + 0x78, 0xdc, 0x5a, 0xf2, 0x03, 0x73, 0xa7, 0x5d, 0xeb, 0x7c, 0x3c, 0xbf, 0xf4, 0xfd, 0x46, 0x9e, 0xd0, 0x66, 0x67, + 0x72, 0xfa, 0x27, 0x6f, 0xf5, 0x0f, 0x53, 0x7d, 0x0b, 0xdd, 0x09, 0xfa, 0x0c, 0x5d, 0x55, 0xdd, 0x95, 0xd8, 0xc2, + 0x50, 0x4f, 0x2c, 0xf2, 0x42, 0x9e, 0xb4, 0xc6, 0x8e, 0x83, 0xbd, 0x01, 0x4e, 0xfc, 0xf2, 0x70, 0x10, 0x57, 0xb9, + 0xcf, 0xce, 0xbb, 0x46, 0x56, 0x0e, 0x60, 0x05, 0x51, 0x30, 0x6e, 0xcd, 0xc7, 0x36, 0x48, 0x97, 0xb8, 0x1a, 0x1f, + 0xbf, 0xa1, 0x58, 0x26, 0x9b, 0x88, 0x8b, 0x8b, 0xfc, 0xe9, 0x73, 0x20, 0x2d, 0xeb, 0xf7, 0xa3, 0xeb, 0xcb, 0xe9, + 0xf3, 0x61, 0x14, 0x80, 0x63, 0x97, 0xbd, 0xbc, 0x8c, 0xf9, 0xea, 0x92, 0x59, 0xa6, 0xb0, 0xc8, 0x37, 0x03, 0xaa, + 0x4b, 0x56, 0x4b, 0xd7, 0x2b, 0xc0, 0xd2, 0xe5, 0x37, 0x0f, 0x61, 0x6a, 0x40, 0x23, 0x6b, 0xee, 0x4e, 0x73, 0x2d, + 0x50, 0xea, 0x79, 0x3f, 0x33, 0xe4, 0xeb, 0x32, 0xe8, 0x0a, 0xd2, 0x3d, 0x8f, 0x48, 0x2f, 0xf7, 0xd2, 0xe9, 0x7e, + 0x5f, 0x0a, 0xb0, 0xd4, 0x97, 0xe2, 0x33, 0x28, 0x2c, 0x1a, 0xdf, 0x08, 0xd0, 0xd6, 0x50, 0x4d, 0x7b, 0xa5, 0xa8, + 0x7a, 0x41, 0xaf, 0x14, 0x9f, 0x7b, 0x7a, 0xa8, 0xcc, 0x97, 0xa5, 0xa3, 0xff, 0x09, 0x35, 0x17, 0x9c, 0x10, 0x33, + 0x31, 0x07, 0x50, 0x09, 0xda, 0xf8, 0x56, 0x47, 0x1b, 0x9f, 0xea, 0x55, 0xdc, 0xf4, 0x79, 0x6d, 0x2d, 0x73, 0x42, + 0xd8, 0x74, 0x2f, 0x01, 0x2a, 0xf2, 0x4a, 0x78, 0x04, 0xcb, 0x2f, 0x7f, 0xc8, 0xd3, 0x15, 0xa2, 0x75, 0xdc, 0xb3, + 0xcc, 0xa5, 0xb1, 0x7f, 0x6d, 0x30, 0x7d, 0x7d, 0xbb, 0x2d, 0xf2, 0x53, 0x13, 0x13, 0xd6, 0x63, 0x45, 0xdf, 0xbc, + 0x0b, 0x57, 0x02, 0x05, 0x0e, 0x25, 0x12, 0xdb, 0x54, 0xa1, 0x88, 0x07, 0x49, 0x9f, 0x2e, 0x5a, 0x9f, 0x06, 0x98, + 0x5a, 0xcb, 0x81, 0x39, 0x84, 0xab, 0xb8, 0xf0, 0xd1, 0xd3, 0xb7, 0x98, 0x85, 0xf3, 0x89, 0xf7, 0xd1, 0x2b, 0x46, + 0xe6, 0xe3, 0x3e, 0x2a, 0x95, 0xf4, 0xcf, 0xc3, 0x61, 0x56, 0xcd, 0x7d, 0x87, 0x3e, 0xd2, 0x43, 0x95, 0x0b, 0xca, + 0xde, 0x18, 0x93, 0x08, 0x94, 0xc6, 0x78, 0x1f, 0x07, 0xc7, 0x79, 0x9f, 0x06, 0x90, 0xda, 0x27, 0xde, 0x93, 0x92, + 0xc3, 0x73, 0x8e, 0x39, 0xa1, 0xb4, 0x22, 0xac, 0xe2, 0x8b, 0x0c, 0xe5, 0xba, 0x53, 0x0a, 0x26, 0x39, 0x24, 0x18, + 0xfe, 0xaa, 0x79, 0x13, 0x2b, 0x10, 0x76, 0xcd, 0xbc, 0x1a, 0x3d, 0xa9, 0x92, 0xb0, 0x14, 0x70, 0x54, 0x66, 0x9e, + 0x61, 0x6f, 0x78, 0x62, 0x18, 0x39, 0x58, 0xee, 0x8f, 0xea, 0x44, 0xe4, 0x1e, 0x5d, 0x60, 0x54, 0x16, 0x9e, 0x37, + 0x74, 0xa5, 0x41, 0x25, 0xd9, 0xf1, 0x57, 0x5c, 0x03, 0x6a, 0x6b, 0x8c, 0x18, 0x0a, 0x18, 0x05, 0xaf, 0xed, 0x0f, + 0x21, 0x8b, 0xb2, 0xf5, 0x1b, 0x1c, 0xf3, 0x59, 0xc9, 0x5d, 0xef, 0x70, 0x16, 0x5a, 0x42, 0x9e, 0xdc, 0x31, 0x48, + 0xd3, 0x58, 0x1a, 0x01, 0x27, 0x22, 0xd9, 0xc6, 0x52, 0x38, 0x02, 0x08, 0x08, 0x74, 0x53, 0x66, 0x18, 0xd3, 0xc1, + 0xc8, 0xf3, 0xa4, 0x67, 0xbc, 0x57, 0xe1, 0x29, 0xa4, 0xc9, 0xf6, 0xf5, 0xfc, 0xbd, 0x11, 0x64, 0xe5, 0x96, 0x73, + 0x3c, 0x2c, 0xbe, 0x71, 0xf6, 0x55, 0x4e, 0x9e, 0x62, 0x96, 0x91, 0xde, 0x29, 0xe6, 0x05, 0xfc, 0xa9, 0x2c, 0xf5, + 0x39, 0x4a, 0x6f, 0x99, 0x4f, 0x56, 0x91, 0x74, 0xe9, 0x6d, 0xfa, 0xfd, 0x78, 0xa4, 0x0e, 0x35, 0x7f, 0x1f, 0x8f, + 0xe4, 0x19, 0xb6, 0x61, 0x09, 0x0b, 0xad, 0x82, 0x31, 0x80, 0x24, 0x36, 0x22, 0x1a, 0x8c, 0xf6, 0xe6, 0x70, 0x38, + 0xdf, 0x98, 0xb3, 0x64, 0x0f, 0xae, 0xaf, 0x3c, 0x31, 0xef, 0xc0, 0x97, 0x79, 0x4c, 0x10, 0xb1, 0x99, 0xb7, 0x61, + 0x35, 0x78, 0xb0, 0x83, 0xeb, 0x23, 0xb6, 0x28, 0xd6, 0x3a, 0x96, 0xca, 0x3a, 0x38, 0xad, 0x63, 0xd3, 0x8c, 0x94, + 0x22, 0xfb, 0x1c, 0xfb, 0x7b, 0x37, 0xb8, 0xba, 0x36, 0x06, 0xb5, 0xc6, 0x1d, 0xe6, 0xce, 0xa9, 0x80, 0x7a, 0x4c, + 0x57, 0x50, 0x3d, 0xcb, 0xc9, 0x97, 0xdf, 0xda, 0x39, 0x20, 0x68, 0x04, 0x02, 0x17, 0x0d, 0xb4, 0x6a, 0x97, 0x72, + 0xde, 0x05, 0x84, 0xf8, 0x26, 0x05, 0x7d, 0x3a, 0x83, 0x4d, 0x6c, 0x3e, 0x81, 0x58, 0x34, 0xdd, 0xe7, 0x5a, 0x33, + 0x5f, 0x8c, 0x68, 0x67, 0xd6, 0xdd, 0x22, 0xb7, 0x5a, 0x88, 0x64, 0xf4, 0x6c, 0x33, 0xe1, 0xa2, 0x43, 0x39, 0x23, + 0x01, 0x13, 0xb4, 0xb6, 0x52, 0xf2, 0xb9, 0xee, 0x75, 0x82, 0xf6, 0x40, 0xd2, 0xba, 0x7f, 0xb3, 0xe8, 0x8c, 0x92, + 0x93, 0xeb, 0x4d, 0xce, 0x20, 0x05, 0x0b, 0xb6, 0x97, 0x39, 0xe1, 0x06, 0xf8, 0xc4, 0x66, 0xc9, 0x69, 0x1a, 0xe4, + 0xb1, 0x30, 0x1e, 0x79, 0x6d, 0x7e, 0x59, 0x40, 0x87, 0x92, 0x45, 0x23, 0xc4, 0x03, 0xec, 0x1c, 0x92, 0xab, 0x02, + 0x75, 0xd3, 0x40, 0x57, 0xae, 0x9c, 0x29, 0xa6, 0xc0, 0x85, 0x50, 0x10, 0xb5, 0xa3, 0x93, 0xa8, 0x9c, 0xf7, 0x49, + 0x75, 0x99, 0x4f, 0x0b, 0x69, 0x1a, 0xc8, 0xa7, 0x95, 0x63, 0x1e, 0xd8, 0xd9, 0xc6, 0x35, 0x81, 0x81, 0x4e, 0xed, + 0x6b, 0x51, 0xce, 0xb1, 0x8a, 0xe8, 0x7d, 0xfe, 0xa1, 0xb2, 0xa7, 0x0f, 0x22, 0x6c, 0x54, 0xa0, 0xb1, 0x94, 0x18, + 0x1b, 0x39, 0xfe, 0x2d, 0x51, 0x36, 0x64, 0x08, 0x08, 0x21, 0x6d, 0xe4, 0xf4, 0xc3, 0xfa, 0xf2, 0x36, 0xd3, 0xfe, + 0x9f, 0x24, 0x7e, 0x1b, 0xec, 0xe5, 0xd4, 0x9f, 0x7a, 0xc4, 0xe3, 0xb5, 0x46, 0x8f, 0x29, 0xe9, 0x36, 0xc8, 0x53, + 0xe5, 0x29, 0x48, 0x26, 0x8c, 0x25, 0x04, 0x8b, 0x72, 0xc1, 0x73, 0x5e, 0x71, 0x09, 0xf7, 0x51, 0xcb, 0x8a, 0x08, + 0x55, 0x89, 0x9c, 0x3e, 0x5f, 0x01, 0xcf, 0x04, 0x04, 0x3a, 0xc6, 0x48, 0xa3, 0x0a, 0xbe, 0x04, 0xc6, 0x3a, 0x50, + 0x76, 0x9a, 0x91, 0xe0, 0xb2, 0x7b, 0x83, 0x44, 0xa9, 0xaf, 0x48, 0x49, 0xfa, 0x56, 0xd4, 0x78, 0x25, 0x56, 0x11, + 0x09, 0x64, 0xa8, 0x21, 0x62, 0x55, 0x3d, 0x75, 0xaf, 0x8a, 0xc9, 0x60, 0x50, 0xf9, 0x72, 0x7a, 0xe2, 0x0d, 0x0d, + 0x95, 0x77, 0x5d, 0xd1, 0x4e, 0x4f, 0xb4, 0x52, 0xde, 0x42, 0x5a, 0x82, 0xa6, 0x61, 0xa4, 0x39, 0x94, 0xba, 0x92, + 0xee, 0xc6, 0x20, 0xbe, 0x64, 0xa2, 0x67, 0x3b, 0xb5, 0xa3, 0xb4, 0x25, 0xed, 0x21, 0xa4, 0xe7, 0x2e, 0xf9, 0x98, + 0x85, 0x5c, 0xdd, 0x29, 0x27, 0xe5, 0x55, 0x88, 0x4e, 0xee, 0x7b, 0x0c, 0x89, 0x40, 0x9f, 0x73, 0x0c, 0xeb, 0xa2, + 0xa1, 0xce, 0x61, 0x85, 0x98, 0x2d, 0x94, 0x30, 0x5f, 0x32, 0x9e, 0x4a, 0x06, 0x0d, 0x80, 0x0c, 0xf8, 0xec, 0x65, + 0x60, 0xf9, 0x2b, 0x88, 0x1f, 0x6d, 0x7c, 0x38, 0xfc, 0x59, 0x53, 0x88, 0xed, 0x9f, 0xb0, 0x19, 0xc2, 0xa3, 0x7a, + 0xc0, 0x33, 0xdf, 0xc4, 0x09, 0x5a, 0x01, 0x49, 0x99, 0x1d, 0x4d, 0x64, 0xaf, 0x7a, 0x08, 0xa7, 0xb2, 0x02, 0x75, + 0x94, 0x75, 0x56, 0xc2, 0x8f, 0x30, 0xd5, 0xad, 0xc4, 0x5a, 0xa0, 0xcd, 0xd5, 0x8a, 0xb5, 0x00, 0x0e, 0xfc, 0x1c, + 0x82, 0x27, 0xf2, 0x39, 0xb8, 0x18, 0x14, 0xe0, 0x73, 0x00, 0xbc, 0xc8, 0x5d, 0x78, 0x30, 0x8f, 0x2c, 0xab, 0x11, + 0x86, 0xa3, 0x8a, 0x58, 0xbf, 0x66, 0x3b, 0xf2, 0x81, 0xdb, 0x31, 0x3e, 0xd7, 0x1e, 0x4b, 0x96, 0x83, 0x51, 0xe6, + 0x5e, 0x2d, 0xd1, 0xf3, 0x26, 0x8d, 0x9b, 0xd1, 0x93, 0x7d, 0x2d, 0xff, 0x17, 0xf4, 0x32, 0xe8, 0x6f, 0xe1, 0x96, + 0xd7, 0xfc, 0x6e, 0x41, 0xa4, 0x99, 0x5e, 0x41, 0xa4, 0x8c, 0x1a, 0x91, 0x31, 0x84, 0x4d, 0xaa, 0x9b, 0xdb, 0xa4, + 0xba, 0x10, 0xf0, 0x74, 0x44, 0xaa, 0x6b, 0x21, 0x6d, 0xe4, 0xd3, 0x3a, 0x90, 0xb1, 0x48, 0xef, 0x7e, 0xf8, 0xdb, + 0x8b, 0x4f, 0x6f, 0x7e, 0xf9, 0x61, 0xf1, 0xe6, 0xdd, 0xeb, 0x37, 0xef, 0xde, 0x7c, 0xfa, 0x8d, 0x20, 0x3c, 0xa6, + 0x42, 0x65, 0xf8, 0xf0, 0xfe, 0xe6, 0x8d, 0x93, 0xc1, 0xf6, 0x66, 0xc8, 0xda, 0x37, 0x72, 0x30, 0x04, 0x22, 0x1b, + 0x84, 0x0c, 0xb2, 0x53, 0xdb, 0xfe, 0x4c, 0xcc, 0x31, 0xf6, 0x4e, 0x60, 0xb2, 0x05, 0x9c, 0x63, 0x99, 0x97, 0x8c, + 0xc8, 0x55, 0xa1, 0xf5, 0x03, 0x5a, 0xf0, 0x16, 0x5c, 0x64, 0xd2, 0xfc, 0xee, 0x17, 0x82, 0xd8, 0xa7, 0x95, 0x94, + 0xfb, 0x6a, 0x5b, 0xf3, 0x7c, 0x7b, 0xbf, 0x97, 0x70, 0xfe, 0x73, 0x69, 0x44, 0x2d, 0xc0, 0x01, 0xf8, 0x1c, 0xfe, + 0xb8, 0xd2, 0x96, 0x34, 0x99, 0x45, 0xfb, 0x19, 0x43, 0xd0, 0xa5, 0xc1, 0x07, 0xb1, 0x47, 0x5e, 0xea, 0x93, 0x85, + 0x04, 0xee, 0x88, 0xe1, 0xd3, 0x8a, 0xa0, 0x57, 0x8c, 0x28, 0x2e, 0xb9, 0x42, 0xa5, 0x94, 0xfc, 0x1b, 0x65, 0x17, + 0x15, 0x72, 0x56, 0xb0, 0x7b, 0x45, 0x8e, 0x8c, 0x1f, 0x04, 0x13, 0x5f, 0x0e, 0xee, 0xbf, 0xc4, 0x3b, 0x9c, 0x29, + 0x8e, 0xe4, 0x84, 0x3f, 0x64, 0x18, 0xd8, 0x9f, 0x83, 0xcf, 0xab, 0xc3, 0xbc, 0xbc, 0xd1, 0xa7, 0xdc, 0x92, 0x8f, + 0x27, 0xcb, 0x2b, 0x30, 0xd8, 0x2f, 0x55, 0x73, 0xd7, 0xbc, 0x9e, 0x2d, 0xe7, 0x6c, 0x3f, 0x8b, 0xe6, 0xc1, 0x1d, + 0x9b, 0x65, 0xf3, 0x60, 0xd5, 0xf0, 0x35, 0xbb, 0xe5, 0x6b, 0xab, 0x6a, 0x6b, 0xbb, 0x6a, 0x93, 0x0d, 0xbf, 0x05, + 0x09, 0xe1, 0x26, 0xf3, 0x80, 0xf7, 0xf8, 0xce, 0x67, 0x1b, 0x90, 0x68, 0x57, 0x6c, 0x03, 0x17, 0xb1, 0x35, 0xff, + 0xa1, 0xf2, 0x36, 0xac, 0x64, 0xe7, 0x63, 0x96, 0xe3, 0xfc, 0xf3, 0xe1, 0x01, 0xed, 0x85, 0xfa, 0xd9, 0xa5, 0x7a, + 0x36, 0x51, 0x76, 0xb3, 0xcd, 0x68, 0x71, 0x9f, 0x56, 0x9b, 0x30, 0x43, 0xcf, 0x72, 0xf8, 0x68, 0x2b, 0x05, 0x3f, + 0xbd, 0xc0, 0x2f, 0xd9, 0x51, 0x5b, 0x69, 0xdb, 0xae, 0x4a, 0x6c, 0x05, 0x2d, 0x8a, 0xac, 0x56, 0x78, 0x60, 0xce, + 0xaf, 0x61, 0x01, 0x63, 0xcf, 0x71, 0xce, 0x6b, 0x7f, 0x84, 0x8c, 0xf7, 0x0e, 0x00, 0x5a, 0xe6, 0x38, 0xc0, 0x23, + 0x56, 0x8c, 0xa2, 0xc1, 0x3b, 0xbf, 0x54, 0x56, 0x2b, 0xcd, 0x49, 0x68, 0x1b, 0xb1, 0x6a, 0x39, 0x52, 0x35, 0x23, + 0xd2, 0x07, 0xe9, 0x79, 0xdf, 0x23, 0xaa, 0xc1, 0x9e, 0xcc, 0xeb, 0xc0, 0x3e, 0xbd, 0x6c, 0xad, 0xea, 0xce, 0xef, + 0xa9, 0xd2, 0x25, 0x47, 0xb6, 0xfc, 0x74, 0x19, 0x3e, 0xa8, 0x3f, 0x25, 0xd7, 0x87, 0x02, 0x47, 0x78, 0xac, 0x02, + 0xce, 0xd7, 0x2b, 0xd1, 0xee, 0x44, 0xd8, 0x95, 0x4b, 0x40, 0x88, 0x2f, 0x69, 0x9a, 0xe3, 0x71, 0x44, 0x13, 0x11, + 0x36, 0x31, 0xfa, 0x0b, 0xbb, 0x0f, 0x25, 0x96, 0xf3, 0x5c, 0x83, 0x92, 0x4b, 0x06, 0xef, 0x49, 0x7b, 0x0d, 0x9a, + 0xe5, 0x55, 0xa9, 0xc9, 0x44, 0x0e, 0xca, 0x87, 0x43, 0x01, 0x7b, 0xa9, 0xf1, 0xd3, 0x84, 0x9f, 0xb0, 0xbc, 0xb5, + 0xb7, 0xa6, 0x14, 0x95, 0x34, 0x40, 0x05, 0x3e, 0x66, 0xf0, 0xbf, 0x3b, 0x43, 0x2c, 0x98, 0xa2, 0xe3, 0x87, 0x33, + 0x31, 0xb7, 0x9e, 0x5b, 0x65, 0x1d, 0x65, 0x6b, 0x94, 0x13, 0xf0, 0x6f, 0xa9, 0x8e, 0x93, 0x44, 0x38, 0xf5, 0x1e, + 0x71, 0x51, 0xf7, 0x72, 0x88, 0xba, 0x61, 0x6f, 0x2a, 0x1d, 0x6c, 0x39, 0x4d, 0x83, 0x23, 0xf1, 0x2b, 0xf5, 0xd9, + 0xfb, 0xcc, 0xe2, 0x51, 0x47, 0x36, 0xa2, 0x24, 0x8d, 0x63, 0x91, 0xc3, 0xf6, 0xbe, 0x90, 0xfb, 0x7f, 0xbf, 0x0f, + 0xe1, 0xa4, 0x55, 0x90, 0x94, 0x9e, 0x40, 0x44, 0x38, 0x3a, 0xfc, 0x88, 0xf0, 0x44, 0xaa, 0x0a, 0x9f, 0xd4, 0x27, + 0x6e, 0xcc, 0xee, 0x85, 0x39, 0xaa, 0xb7, 0x00, 0xc3, 0x58, 0x6f, 0x2d, 0x42, 0x12, 0xad, 0x34, 0xa3, 0xad, 0x07, + 0xc4, 0x88, 0xf7, 0x6b, 0x8b, 0x0c, 0xc6, 0xda, 0x92, 0x48, 0x00, 0xbf, 0x23, 0x21, 0x43, 0xdb, 0x46, 0x60, 0xc6, + 0xf0, 0x76, 0x56, 0x5c, 0xba, 0x0e, 0xdb, 0x9c, 0xc3, 0x17, 0xb2, 0xd0, 0xac, 0x23, 0x4a, 0x13, 0x84, 0xfc, 0x03, + 0x4e, 0x16, 0x0a, 0xa3, 0x79, 0x75, 0x94, 0x4e, 0x12, 0xeb, 0xfb, 0xae, 0x52, 0xc1, 0x66, 0x73, 0x83, 0xfa, 0xb2, + 0xa3, 0xe4, 0x97, 0xe0, 0xa4, 0xe3, 0x24, 0x8b, 0x1c, 0x44, 0x2d, 0x2a, 0xe7, 0x26, 0x09, 0x4b, 0xbb, 0x3a, 0xd5, + 0x66, 0xbd, 0x2e, 0xca, 0xba, 0x7a, 0x25, 0x22, 0x45, 0xef, 0xa3, 0x1e, 0x3d, 0x91, 0x90, 0x0a, 0xad, 0x4a, 0xed, + 0xf2, 0x08, 0xdc, 0x36, 0xb5, 0x62, 0x5b, 0x2e, 0x61, 0x89, 0x1a, 0xff, 0x09, 0xfa, 0x28, 0x17, 0x0f, 0x32, 0x40, + 0xa3, 0xe3, 0xa9, 0x79, 0xeb, 0x91, 0x57, 0x8e, 0xf2, 0x4b, 0xab, 0x4d, 0xfa, 0x05, 0x90, 0x19, 0xed, 0x1f, 0x2d, + 0x25, 0x90, 0x19, 0x98, 0x49, 0x4b, 0x43, 0x22, 0x47, 0x31, 0x4b, 0xf3, 0x3f, 0x70, 0xc5, 0x56, 0x88, 0x34, 0xac, + 0xe6, 0x1e, 0x7f, 0x51, 0x79, 0xb5, 0x5c, 0xcb, 0x4c, 0x73, 0xb3, 0xc4, 0xb1, 0x62, 0x71, 0x51, 0xaf, 0x2b, 0x91, + 0x05, 0x42, 0x1c, 0x61, 0x1a, 0xeb, 0xa9, 0x37, 0x4a, 0xab, 0x0f, 0x48, 0x28, 0xf3, 0x03, 0xf6, 0x76, 0xec, 0xf5, + 0x20, 0x0b, 0x71, 0x6c, 0x39, 0xd8, 0x6c, 0xbd, 0x4f, 0x65, 0x2a, 0xe2, 0xb3, 0xba, 0x38, 0xdb, 0x54, 0xe2, 0xac, + 0x4e, 0xc4, 0xd9, 0x77, 0x90, 0xf3, 0xbb, 0x33, 0x2a, 0xfa, 0xec, 0x21, 0xad, 0x93, 0x62, 0x53, 0xd3, 0x93, 0xd7, + 0x58, 0xc6, 0x77, 0x67, 0xc4, 0x55, 0x73, 0x46, 0x23, 0x19, 0x8f, 0xce, 0x3e, 0x64, 0x40, 0xf2, 0x7a, 0x96, 0xae, + 0x60, 0xf0, 0xce, 0xc2, 0x3c, 0x3e, 0x2b, 0xc5, 0x1d, 0x58, 0x9c, 0xca, 0xce, 0xf7, 0x20, 0xc3, 0x2a, 0xfc, 0x43, + 0x9c, 0x01, 0xb4, 0xeb, 0x59, 0x5a, 0x9f, 0xa5, 0xd5, 0x59, 0x5e, 0xd4, 0x67, 0x4a, 0x0a, 0x87, 0x30, 0x7e, 0x78, + 0x4f, 0x5f, 0xd9, 0xe5, 0x6d, 0x16, 0x77, 0x59, 0xe4, 0x4f, 0xd1, 0xab, 0x88, 0x98, 0x34, 0x2a, 0xe1, 0xb5, 0xfb, + 0xdb, 0xe6, 0xfe, 0xe1, 0x75, 0x63, 0xf7, 0xb3, 0x3b, 0x46, 0x74, 0x41, 0x3d, 0x5e, 0x49, 0x4a, 0x05, 0x05, 0x04, + 0x4e, 0x34, 0x6b, 0x3c, 0xb8, 0xe3, 0x80, 0x57, 0x03, 0x5b, 0xb2, 0xb5, 0xcf, 0xaf, 0x63, 0x19, 0xa6, 0xbd, 0x09, + 0xf0, 0xaf, 0xb2, 0x37, 0x5d, 0x07, 0x4b, 0xbc, 0x6f, 0x21, 0xdb, 0xd0, 0x9b, 0x57, 0xfc, 0x85, 0x97, 0xab, 0xbf, + 0xd9, 0x3f, 0x01, 0x08, 0x03, 0x62, 0x56, 0x7d, 0x34, 0x71, 0xef, 0xac, 0x2c, 0x3b, 0x27, 0xcb, 0xae, 0x87, 0x7e, + 0x4d, 0x62, 0x54, 0x5a, 0x59, 0x4a, 0x27, 0x4b, 0x09, 0x59, 0xc0, 0x27, 0x46, 0x53, 0x1b, 0x01, 0x84, 0xed, 0x28, + 0x95, 0x2f, 0x54, 0x5e, 0x44, 0xe1, 0x9c, 0xe0, 0x79, 0x22, 0x46, 0xf7, 0x56, 0x32, 0x60, 0x38, 0x84, 0x60, 0x0e, + 0xda, 0x62, 0x6f, 0xe8, 0x26, 0xe2, 0xaf, 0xd7, 0x45, 0xf9, 0x26, 0x26, 0x9f, 0x82, 0xdd, 0xc9, 0xc7, 0x25, 0x3c, + 0x2e, 0x4f, 0x3e, 0x0e, 0xd1, 0x23, 0xe1, 0xe4, 0x63, 0xf0, 0x3d, 0x92, 0xf3, 0xba, 0xeb, 0x71, 0x82, 0xdc, 0x42, + 0xba, 0xbf, 0x1d, 0x93, 0x00, 0xcd, 0x6b, 0x58, 0x8e, 0x9a, 0x8a, 0x6b, 0x66, 0xc6, 0x78, 0xde, 0xe8, 0xfd, 0xb1, + 0xe3, 0x2d, 0x53, 0x28, 0x66, 0x31, 0xaf, 0xe1, 0xf7, 0xac, 0x0a, 0xd4, 0x5d, 0x6f, 0x93, 0xdc, 0x32, 0xab, 0xe7, + 0x68, 0xf7, 0x7d, 0x5f, 0x27, 0x82, 0xda, 0xdf, 0x61, 0xcf, 0x33, 0xeb, 0x5d, 0x15, 0x03, 0x97, 0x2a, 0xd9, 0x21, + 0x53, 0xd5, 0xf4, 0x40, 0xa5, 0x34, 0x78, 0x7a, 0x69, 0x5d, 0xbe, 0x54, 0xda, 0xc8, 0x33, 0xcd, 0x6f, 0x00, 0x2f, + 0xa6, 0x2e, 0x8b, 0xdd, 0x57, 0xf7, 0x15, 0xdc, 0xc6, 0xfb, 0xfd, 0x65, 0xe5, 0x99, 0x9f, 0xb8, 0x00, 0xec, 0x4d, + 0x85, 0xd6, 0x09, 0x94, 0x1a, 0xd6, 0xe1, 0xcb, 0x44, 0x44, 0x7f, 0xb4, 0xcb, 0x75, 0xe6, 0x3a, 0x60, 0x44, 0x11, + 0xbf, 0x8d, 0x47, 0x7f, 0x80, 0xe2, 0xda, 0xd8, 0x03, 0xc2, 0x3a, 0x24, 0xf4, 0x19, 0x01, 0x48, 0x3d, 0xe6, 0x28, + 0x01, 0xcd, 0x8a, 0xe6, 0x8e, 0xc9, 0xcf, 0xf5, 0x95, 0xd2, 0xdf, 0x2f, 0x2b, 0x8f, 0xcc, 0x29, 0x6d, 0x33, 0x8d, + 0xd5, 0x9a, 0x4a, 0x20, 0xbc, 0xa2, 0x92, 0x55, 0xf8, 0x6c, 0xde, 0x88, 0x7e, 0x5f, 0x1e, 0xe1, 0x69, 0xf5, 0xc3, + 0x16, 0xe3, 0x5b, 0x01, 0xd1, 0x48, 0x00, 0xf4, 0x13, 0xc0, 0xbc, 0xc8, 0x66, 0x76, 0x1f, 0x07, 0x54, 0x29, 0xd1, + 0x34, 0xce, 0xe6, 0xf9, 0x2d, 0xbd, 0x29, 0x3b, 0xe8, 0xd4, 0xa9, 0x02, 0x17, 0x5c, 0x95, 0x8c, 0x57, 0xd6, 0x13, + 0xf9, 0xfc, 0xe6, 0x76, 0x93, 0x66, 0xf1, 0xfb, 0xf2, 0x9f, 0x38, 0xb6, 0xba, 0x0e, 0x8f, 0x4c, 0x9d, 0xae, 0x9d, + 0x47, 0x5a, 0x7b, 0x21, 0x20, 0xa2, 0x5d, 0x43, 0xad, 0x17, 0x16, 0x7a, 0xa4, 0x27, 0xc2, 0x39, 0x49, 0xd4, 0xb4, + 0x03, 0x2d, 0x8d, 0xd0, 0xd7, 0xd7, 0x9c, 0xfe, 0xc2, 0x60, 0xed, 0xf3, 0x31, 0x03, 0xb2, 0x12, 0xfd, 0x58, 0x3d, + 0x34, 0x36, 0x73, 0xe8, 0x59, 0xab, 0xf2, 0xcc, 0xab, 0x0e, 0x07, 0xc4, 0x87, 0xd1, 0x5f, 0xf2, 0xfb, 0xfd, 0x57, + 0x34, 0xff, 0x98, 0x50, 0xe3, 0x67, 0x9b, 0x01, 0xba, 0xf6, 0x5d, 0x79, 0x20, 0xea, 0xb9, 0x56, 0x09, 0x42, 0xbc, + 0x41, 0x4c, 0x34, 0x23, 0xe6, 0xe0, 0xb4, 0x43, 0xcd, 0x3f, 0x49, 0x0d, 0x08, 0x51, 0xe2, 0x75, 0x4c, 0x59, 0x90, + 0xd3, 0x26, 0x8e, 0xf4, 0xa3, 0x70, 0x22, 0x3f, 0x8a, 0xaa, 0xc8, 0xee, 0xe1, 0x82, 0xc1, 0xd4, 0x7b, 0xda, 0x2f, + 0xd1, 0x6f, 0x09, 0x47, 0xce, 0xd1, 0xaa, 0x10, 0x44, 0x4e, 0x08, 0x6b, 0x0d, 0x61, 0x82, 0xd8, 0x20, 0x5e, 0xf6, + 0x5d, 0x92, 0xe1, 0x48, 0xc1, 0x65, 0x1d, 0x3b, 0xc6, 0x5c, 0x1d, 0x55, 0xaf, 0x01, 0x8c, 0x57, 0x8e, 0xa0, 0xd9, + 0x28, 0xb2, 0x4b, 0x88, 0x2a, 0x72, 0x3c, 0x01, 0xb5, 0x83, 0xd2, 0xd8, 0x4c, 0xcf, 0xc7, 0x41, 0x3e, 0x5a, 0x54, + 0xa8, 0x73, 0x62, 0x19, 0xaf, 0x01, 0x58, 0x3b, 0x57, 0xfd, 0x3c, 0xab, 0xc1, 0x93, 0x86, 0xf8, 0x7c, 0x8c, 0xb6, + 0x57, 0x36, 0x07, 0xd5, 0x76, 0x3a, 0x2b, 0xaf, 0x98, 0x2e, 0x07, 0xc6, 0x7d, 0xc3, 0x2b, 0x8a, 0x33, 0xfc, 0xe8, + 0xc1, 0x16, 0xe7, 0x4f, 0x37, 0xd4, 0x7e, 0xcc, 0x8d, 0x7a, 0x18, 0x68, 0x2d, 0x78, 0x53, 0x10, 0xeb, 0xef, 0x87, + 0x8e, 0x6c, 0xef, 0xb5, 0xc8, 0x68, 0xf2, 0xd9, 0xcf, 0x3f, 0x94, 0xe9, 0x2a, 0x85, 0xfb, 0x92, 0x93, 0x45, 0x33, + 0x0f, 0x81, 0xbd, 0x21, 0x86, 0xeb, 0xa3, 0xc2, 0x23, 0xca, 0xfa, 0x7d, 0xf8, 0x7d, 0x95, 0x81, 0x29, 0x06, 0xae, + 0x2b, 0x04, 0xe3, 0x21, 0x10, 0xc4, 0xc3, 0x34, 0x3a, 0x19, 0xd4, 0xa0, 0x0d, 0xdf, 0x00, 0x64, 0x06, 0x78, 0x64, + 0x2e, 0x3d, 0x02, 0xee, 0x02, 0xd7, 0x9e, 0x8c, 0xc7, 0xfe, 0xc4, 0x34, 0x34, 0x6a, 0x4a, 0x33, 0x3d, 0x37, 0x7e, + 0xd3, 0x51, 0x2d, 0xd7, 0xce, 0x7f, 0x7c, 0xc9, 0x6f, 0xd0, 0x0b, 0x5a, 0x5e, 0xee, 0x23, 0x75, 0xb9, 0xcf, 0x28, + 0x2e, 0x13, 0xc9, 0x61, 0x41, 0x2c, 0x4b, 0x38, 0xf0, 0x18, 0x95, 0x2c, 0xb6, 0xf4, 0x58, 0x15, 0x2d, 0x5f, 0x94, + 0x1b, 0xa4, 0x43, 0x27, 0x04, 0x4b, 0x54, 0x10, 0x2c, 0x81, 0x71, 0x11, 0x6b, 0xbe, 0x19, 0xe4, 0x2c, 0x9e, 0x6d, + 0xe6, 0x1c, 0x09, 0xeb, 0x92, 0xc3, 0xa1, 0x90, 0x60, 0x33, 0xd9, 0x6c, 0x3d, 0x67, 0x6b, 0x9f, 0x81, 0x12, 0xa0, + 0x94, 0x69, 0x82, 0xd2, 0xb4, 0x62, 0x2b, 0x6e, 0x5a, 0x83, 0xd5, 0x6a, 0xca, 0x56, 0x35, 0x65, 0xe7, 0x34, 0xe5, + 0xa8, 0x82, 0x92, 0x13, 0x4a, 0x51, 0x86, 0x01, 0x8c, 0xd8, 0x24, 0xba, 0xca, 0xd0, 0xc7, 0x3b, 0xe1, 0x11, 0x54, + 0x11, 0x91, 0x4f, 0x18, 0x42, 0x60, 0x22, 0x8a, 0x0b, 0x55, 0x28, 0x06, 0xc8, 0x88, 0x04, 0x82, 0x89, 0x4a, 0x9d, + 0x02, 0xf3, 0xd1, 0x54, 0x31, 0x6c, 0xda, 0x13, 0xe5, 0x5b, 0xea, 0xb8, 0x47, 0xd9, 0xe6, 0xef, 0x62, 0x17, 0x84, + 0xc8, 0xdd, 0xb8, 0x53, 0x3f, 0x23, 0xde, 0xdb, 0x1d, 0x61, 0xfc, 0x64, 0xc7, 0x2d, 0xc2, 0x15, 0xc1, 0x96, 0x6a, + 0x0e, 0xb1, 0x98, 0x57, 0x93, 0x04, 0xb5, 0x2c, 0x89, 0xbf, 0xe1, 0xc9, 0x20, 0x67, 0x4b, 0xf0, 0xa0, 0x9d, 0xb3, + 0x0c, 0xf0, 0x57, 0xac, 0x16, 0xfd, 0x56, 0x7b, 0x4b, 0x90, 0x9f, 0x36, 0x76, 0xa3, 0x30, 0x31, 0x82, 0x44, 0xdd, + 0xae, 0x0c, 0xe4, 0x87, 0x0f, 0x38, 0x1d, 0x8f, 0x3d, 0x65, 0xcc, 0xad, 0x4c, 0x2f, 0xd3, 0xb9, 0x92, 0x6f, 0xe4, + 0x5e, 0xfa, 0xd8, 0x4b, 0xb0, 0x73, 0xc0, 0x1b, 0x48, 0x1b, 0x78, 0x03, 0xdb, 0x85, 0xd7, 0x06, 0x09, 0x33, 0x02, + 0x6c, 0x71, 0x7c, 0x8c, 0x94, 0xc0, 0x10, 0x8e, 0xb3, 0x14, 0x80, 0x69, 0xf4, 0x65, 0xb6, 0xb2, 0x2f, 0xb3, 0x5a, + 0xb3, 0xa5, 0x72, 0xba, 0x77, 0x6e, 0xdd, 0xce, 0x27, 0x12, 0x00, 0x4c, 0xea, 0x1c, 0x88, 0x33, 0x13, 0xec, 0xd2, + 0x24, 0xb2, 0x7c, 0x0c, 0xf3, 0x3b, 0xf1, 0xba, 0x2c, 0x56, 0xaa, 0x2b, 0xda, 0x3e, 0x33, 0xf9, 0x8c, 0x74, 0x12, + 0x2a, 0xa0, 0xa0, 0x90, 0x6b, 0x7d, 0xfa, 0x2e, 0x7c, 0x17, 0x14, 0x1a, 0x98, 0xad, 0xc2, 0x3d, 0x4d, 0xd6, 0x48, + 0xbd, 0x51, 0xf5, 0xfb, 0xe4, 0x1a, 0x48, 0x75, 0xe6, 0xd0, 0xb2, 0x27, 0x15, 0x06, 0x88, 0x1d, 0xf5, 0x19, 0x09, + 0x75, 0x20, 0xf5, 0x80, 0x21, 0x44, 0xdb, 0xf4, 0xf1, 0x27, 0x43, 0xa2, 0x0b, 0xb0, 0x85, 0x68, 0x03, 0x3f, 0xfe, + 0x04, 0xfb, 0x2c, 0x08, 0x8f, 0x69, 0xfe, 0x16, 0x92, 0x8e, 0x0d, 0x9c, 0x56, 0x9f, 0x82, 0x0f, 0x92, 0x1c, 0x4c, + 0xd4, 0xc1, 0xcb, 0xfd, 0xa5, 0xdf, 0x87, 0x2d, 0x3b, 0x97, 0x52, 0x1d, 0x2b, 0xf5, 0xb6, 0xad, 0xfd, 0x20, 0xda, + 0x82, 0x23, 0x8b, 0xf8, 0xfb, 0x0c, 0x11, 0xc1, 0xcc, 0x20, 0xc2, 0xae, 0x85, 0xba, 0xdb, 0x53, 0x6a, 0x59, 0xd4, + 0xdb, 0x9e, 0x52, 0xea, 0x36, 0x0c, 0xdf, 0x4d, 0x30, 0x53, 0xdc, 0xf0, 0x3f, 0x32, 0x2f, 0xd4, 0x1b, 0x8f, 0x45, + 0x81, 0xee, 0xf9, 0xfb, 0x25, 0xaf, 0x66, 0x1b, 0x65, 0xc2, 0xbc, 0xe3, 0xcb, 0x59, 0x28, 0xbb, 0x5a, 0x1a, 0x77, + 0x3e, 0x7b, 0x4b, 0x35, 0x1f, 0xfc, 0xc3, 0x21, 0x81, 0x78, 0xa3, 0xf8, 0xea, 0xae, 0x91, 0x5b, 0xd7, 0x64, 0x73, + 0x55, 0x02, 0xea, 0xf7, 0xf9, 0x1a, 0xf7, 0x5b, 0xac, 0x7f, 0xf7, 0x34, 0xc8, 0x58, 0xcd, 0x70, 0xc5, 0x14, 0x3e, + 0x05, 0x80, 0xc1, 0xe1, 0x54, 0x90, 0x16, 0x78, 0xc3, 0xcb, 0xe1, 0xe5, 0x64, 0x43, 0x26, 0xdd, 0x8d, 0x8f, 0xdc, + 0x59, 0xa0, 0xea, 0xfd, 0x86, 0xe2, 0xa4, 0x41, 0xa2, 0xb1, 0xd7, 0xe0, 0x8b, 0x2c, 0xa3, 0x5c, 0x34, 0x71, 0x1f, + 0x93, 0xaf, 0xf4, 0x00, 0xe6, 0x2a, 0x94, 0x00, 0xd1, 0x6f, 0x2c, 0x8b, 0x8d, 0x68, 0x5b, 0x6c, 0x60, 0x29, 0x55, + 0x73, 0xbd, 0x9a, 0x3e, 0x7b, 0x25, 0x9a, 0xf7, 0xd1, 0x8c, 0x53, 0x1a, 0x0d, 0x38, 0x4e, 0xa3, 0x70, 0xfb, 0xfe, + 0x5e, 0x94, 0xcb, 0x0c, 0x2c, 0xd9, 0x2a, 0x9c, 0xe2, 0xb2, 0x51, 0x67, 0xc4, 0x8b, 0x3c, 0x56, 0x00, 0x1d, 0x8f, + 0x09, 0x80, 0xea, 0x82, 0x80, 0x8a, 0x68, 0x29, 0xbd, 0x15, 0x5a, 0x2c, 0xd4, 0x1b, 0x8e, 0x52, 0xf8, 0x23, 0xfd, + 0x79, 0x90, 0x4f, 0x01, 0x88, 0x5d, 0x1f, 0x47, 0xaf, 0x8b, 0x92, 0x3e, 0x55, 0xcc, 0x72, 0x39, 0x98, 0xc0, 0xae, + 0x4e, 0x64, 0xa8, 0x15, 0xe4, 0xad, 0xba, 0xf2, 0x56, 0x26, 0x6f, 0x63, 0x9c, 0x92, 0x1f, 0xb9, 0xe9, 0x58, 0x23, + 0x06, 0x5e, 0x79, 0x5a, 0xa7, 0x09, 0xd2, 0xe4, 0x02, 0x18, 0x86, 0xf8, 0x36, 0xf3, 0x5e, 0x78, 0x8e, 0x54, 0x05, + 0xc9, 0x6c, 0x97, 0x79, 0xea, 0x22, 0xaa, 0xaf, 0x9c, 0x5a, 0x3a, 0x73, 0xfa, 0x11, 0xc0, 0x7b, 0x4c, 0x4d, 0x1a, + 0xf2, 0x11, 0x6e, 0x4b, 0xf1, 0xf5, 0x56, 0x5d, 0xe3, 0xa5, 0xd1, 0xb9, 0x7b, 0xf9, 0xd2, 0x9d, 0x06, 0xfd, 0x14, + 0x04, 0xe5, 0x7c, 0x51, 0x0a, 0xd8, 0x53, 0x66, 0x73, 0xbd, 0x5a, 0xb5, 0x42, 0xeb, 0x70, 0x18, 0x6b, 0x47, 0x21, + 0xad, 0xce, 0x02, 0xb6, 0x1a, 0xe9, 0x94, 0x00, 0x21, 0x38, 0x4e, 0xc3, 0x4e, 0x30, 0xee, 0xd2, 0x69, 0x44, 0xd6, + 0x2b, 0x25, 0xe9, 0xc2, 0x0c, 0x92, 0x7f, 0x92, 0xd7, 0x33, 0xa0, 0x25, 0x80, 0x43, 0x11, 0x4b, 0x78, 0x38, 0x49, + 0xae, 0x00, 0x3a, 0x1d, 0x0e, 0x2a, 0x0d, 0xcd, 0x59, 0xcd, 0x92, 0xf9, 0x24, 0x96, 0xaa, 0xca, 0xc3, 0xc1, 0x53, + 0x6e, 0x06, 0xfd, 0x7e, 0x36, 0x2d, 0x95, 0x0b, 0x40, 0x10, 0xeb, 0xc2, 0x00, 0xf1, 0x48, 0x0b, 0x4f, 0x16, 0x7d, + 0x4a, 0xe2, 0x97, 0xb3, 0x64, 0x6e, 0xb2, 0xe1, 0x1d, 0x18, 0xc1, 0x66, 0x5c, 0x97, 0x94, 0x69, 0x8f, 0xca, 0xef, + 0x19, 0x3d, 0xb5, 0x7d, 0xad, 0xd5, 0x16, 0xb1, 0xae, 0x83, 0xab, 0x12, 0xf5, 0x14, 0x1f, 0x94, 0x24, 0x78, 0xbf, + 0x72, 0x6e, 0x46, 0xca, 0xd7, 0x22, 0xf7, 0x83, 0x76, 0xa6, 0x56, 0x0e, 0x1c, 0x81, 0x1c, 0xab, 0xa8, 0xe4, 0xf5, + 0xae, 0x43, 0xf0, 0xe8, 0xae, 0x54, 0xa0, 0x1c, 0x7c, 0x0d, 0x62, 0x74, 0x7d, 0xd5, 0x59, 0x43, 0xcd, 0x34, 0xaa, + 0x3c, 0x82, 0x4e, 0x1d, 0xc0, 0x93, 0x82, 0x97, 0x5a, 0xfd, 0x78, 0x38, 0x78, 0xe6, 0x07, 0x7f, 0x99, 0xe9, 0x5b, + 0x88, 0x89, 0x72, 0xaa, 0x11, 0x12, 0x57, 0x4a, 0x12, 0xf1, 0xf1, 0xa2, 0x65, 0xc5, 0xa8, 0x0c, 0x1f, 0x78, 0xa5, + 0xca, 0x57, 0xa7, 0x2a, 0x2f, 0x46, 0xda, 0x96, 0xc0, 0x6b, 0xf2, 0x0f, 0x91, 0x6b, 0xde, 0xfa, 0xba, 0xab, 0x0c, + 0x7d, 0x2b, 0x2b, 0xd0, 0x11, 0x6c, 0x65, 0x29, 0x39, 0xe0, 0x93, 0xea, 0xae, 0x5a, 0xb5, 0x3e, 0xa7, 0x6c, 0x23, + 0xdc, 0xe4, 0xd7, 0xb1, 0x83, 0x23, 0xe5, 0x37, 0x78, 0x2e, 0x80, 0xbd, 0x06, 0xec, 0xcd, 0x39, 0x2b, 0x9a, 0x47, + 0x87, 0xb4, 0x2d, 0xd0, 0xc8, 0xcc, 0xed, 0x5c, 0xdd, 0xb7, 0xe5, 0x51, 0x1a, 0x43, 0x64, 0xda, 0x23, 0xd3, 0xc1, + 0x66, 0x94, 0xff, 0x96, 0xf2, 0x5b, 0x85, 0x63, 0xe0, 0xdb, 0xa9, 0x77, 0x00, 0x55, 0x4f, 0x1b, 0x64, 0xac, 0x19, + 0x86, 0x56, 0x76, 0xb9, 0x14, 0x5a, 0x82, 0x96, 0xba, 0x09, 0x82, 0xf3, 0x23, 0xa2, 0x1c, 0x01, 0xe8, 0x22, 0x05, + 0x4c, 0xf0, 0x53, 0xda, 0xee, 0x7e, 0x7f, 0x9d, 0x7a, 0xe4, 0xde, 0x15, 0x6a, 0x94, 0x50, 0x82, 0xb1, 0x9f, 0x68, + 0xcc, 0xa0, 0xa3, 0x2b, 0x72, 0xc2, 0xb3, 0x56, 0x87, 0x75, 0xdd, 0x94, 0x41, 0x59, 0x1c, 0xf3, 0x6a, 0x3a, 0xfb, + 0xfd, 0xc9, 0xbe, 0x6e, 0x90, 0x85, 0xfc, 0x77, 0xd6, 0x43, 0x32, 0xe8, 0x1e, 0x84, 0x42, 0xf4, 0xe6, 0xc1, 0x0c, + 0xff, 0x63, 0x1b, 0x9e, 0x7d, 0xc3, 0x8d, 0x3a, 0x01, 0xcc, 0x11, 0xd7, 0x4b, 0x4f, 0xd1, 0xd6, 0xc3, 0x2d, 0x90, + 0xad, 0xf1, 0xf2, 0xd6, 0x5e, 0x03, 0x39, 0xc5, 0xf1, 0xdf, 0xf1, 0x4c, 0xad, 0x6c, 0xf0, 0xd3, 0x53, 0xb6, 0x03, + 0x0f, 0x2f, 0x42, 0x40, 0x31, 0x2c, 0x1b, 0x7f, 0x67, 0x39, 0xce, 0xe8, 0xbf, 0x79, 0xc4, 0x30, 0x58, 0x44, 0x7e, + 0x7c, 0x59, 0x0a, 0xf1, 0x45, 0x78, 0x6f, 0x2a, 0xef, 0x8e, 0x9c, 0x32, 0xef, 0xf4, 0x30, 0xba, 0x2e, 0x49, 0xdf, + 0x24, 0x1f, 0x5b, 0xc3, 0xf6, 0xbb, 0x76, 0xbf, 0x19, 0x22, 0x08, 0xa1, 0x1c, 0x3f, 0x67, 0x74, 0x42, 0xe3, 0xc3, + 0x6a, 0x76, 0x7a, 0xfd, 0xde, 0x39, 0x5e, 0xb0, 0x35, 0x1a, 0xe0, 0xf1, 0xd0, 0xc5, 0x3c, 0x51, 0x43, 0xa7, 0xeb, + 0xda, 0x39, 0x78, 0x60, 0x90, 0xe5, 0xc9, 0x37, 0x0c, 0x4b, 0xec, 0x4f, 0x22, 0x9e, 0xb4, 0x55, 0x1b, 0x9b, 0x23, + 0xd5, 0x46, 0xcd, 0xc0, 0x0f, 0x5e, 0x41, 0x81, 0xd1, 0x05, 0x69, 0x05, 0xc6, 0xe1, 0x08, 0x40, 0x56, 0x8c, 0xe3, + 0x91, 0xc1, 0x04, 0x86, 0x74, 0x43, 0x51, 0x00, 0x1e, 0x1e, 0xc7, 0x83, 0x90, 0x01, 0xa4, 0x0b, 0x1e, 0x1a, 0xb6, + 0x49, 0x48, 0xf9, 0x79, 0x9e, 0xd7, 0x6a, 0x08, 0x7d, 0x67, 0xa1, 0x3a, 0xf6, 0x23, 0xed, 0x15, 0xeb, 0x5a, 0x95, + 0x8e, 0x6c, 0x75, 0x80, 0xbe, 0x21, 0x03, 0xdf, 0x3a, 0xb6, 0x00, 0x88, 0x96, 0xf8, 0x2d, 0xf5, 0x6a, 0x5f, 0xc6, + 0xac, 0x50, 0xaf, 0x2f, 0x4c, 0xbb, 0x5e, 0x49, 0x8b, 0x02, 0x2a, 0x6e, 0x5b, 0xb5, 0x3d, 0x92, 0xf3, 0x1f, 0xdf, + 0x75, 0xb4, 0xe3, 0xb3, 0x53, 0x63, 0x4b, 0x28, 0x73, 0x8b, 0x27, 0xb2, 0x3a, 0xda, 0x52, 0x9d, 0xea, 0x03, 0x2e, + 0x35, 0xa9, 0xce, 0x0c, 0x0c, 0xaf, 0x11, 0xa0, 0xdc, 0x42, 0x24, 0x8d, 0xc3, 0xde, 0xf9, 0x64, 0x50, 0x30, 0xb7, + 0x48, 0x40, 0x02, 0xdb, 0xd8, 0xda, 0x45, 0x73, 0xfd, 0xfa, 0x2d, 0xf5, 0xaa, 0x36, 0x55, 0x3d, 0x78, 0xe3, 0x05, + 0xce, 0xde, 0x69, 0x2d, 0x20, 0x80, 0xc2, 0xd6, 0xb2, 0x1c, 0x9c, 0xbb, 0x5d, 0xd5, 0x52, 0x51, 0x46, 0xfd, 0xfe, + 0xf9, 0x6f, 0x29, 0x2a, 0x62, 0x4f, 0x15, 0xa7, 0xac, 0xdf, 0x6e, 0x99, 0x8b, 0xca, 0x92, 0x37, 0xa8, 0xa2, 0xb5, + 0x3a, 0x6a, 0x2a, 0xd7, 0xcd, 0x55, 0x4b, 0x26, 0x88, 0xd1, 0x7d, 0xba, 0xd6, 0xb9, 0x53, 0xef, 0xbd, 0x8a, 0x23, + 0x06, 0x82, 0x9b, 0xee, 0xf1, 0xc1, 0x41, 0x68, 0x54, 0x94, 0x0b, 0x6e, 0x94, 0x56, 0x95, 0x94, 0x42, 0xde, 0xaa, + 0x68, 0xce, 0xf4, 0x11, 0x00, 0x11, 0x60, 0x95, 0xa8, 0xff, 0xc3, 0x97, 0xc6, 0x78, 0xf0, 0xc0, 0xd7, 0xe4, 0x3a, + 0xb6, 0xde, 0x3f, 0xad, 0x91, 0x56, 0x1b, 0xc7, 0xa4, 0x56, 0xbd, 0x6c, 0x15, 0x2f, 0xbb, 0xd7, 0xa9, 0x18, 0x3c, + 0xff, 0x9f, 0xfb, 0x00, 0x35, 0xa2, 0xa5, 0x0c, 0x6e, 0x5d, 0x0d, 0xd0, 0xf8, 0x70, 0x2c, 0x7c, 0xe3, 0x87, 0x8c, + 0xf3, 0xc1, 0x0c, 0x1d, 0xd5, 0xe6, 0xe0, 0x80, 0xe0, 0xa8, 0xee, 0xd1, 0x98, 0x30, 0x0b, 0xe7, 0x1e, 0x04, 0xaa, + 0x4f, 0xdc, 0x67, 0x5c, 0x7b, 0x41, 0x9b, 0xc0, 0x27, 0xeb, 0xba, 0xa6, 0x08, 0x70, 0x11, 0x1b, 0x13, 0x31, 0xc4, + 0x65, 0x93, 0x48, 0x7d, 0x33, 0x06, 0x05, 0x40, 0x71, 0x5d, 0x91, 0x5c, 0xba, 0x48, 0xf3, 0x4a, 0x94, 0xb5, 0x6e, + 0x46, 0xc5, 0x8a, 0x21, 0x00, 0x3c, 0x04, 0xc5, 0x55, 0x65, 0x26, 0x34, 0x62, 0x03, 0xa9, 0x2c, 0x05, 0xab, 0x86, + 0x85, 0xdf, 0xb4, 0xdf, 0x24, 0x27, 0xbd, 0xf3, 0x71, 0xeb, 0xdc, 0xb1, 0xef, 0x1d, 0x85, 0x94, 0xf6, 0x50, 0x4c, + 0x10, 0x04, 0x3f, 0xad, 0xc3, 0xf9, 0x33, 0x7e, 0x4d, 0x60, 0x2a, 0xb2, 0x19, 0x03, 0x0e, 0x42, 0x44, 0x66, 0xfc, + 0x9e, 0xc3, 0x6b, 0x5e, 0x4e, 0xc2, 0xe1, 0xd0, 0x07, 0x7d, 0x28, 0xcf, 0x66, 0xe1, 0x50, 0xcc, 0xa5, 0xf7, 0x3a, + 0x58, 0xeb, 0x42, 0x5e, 0x4f, 0x42, 0x44, 0x0b, 0x0d, 0x7d, 0x70, 0x5e, 0x77, 0xcd, 0x11, 0x96, 0x00, 0x34, 0x71, + 0xf4, 0x65, 0xfd, 0x7e, 0xe4, 0x69, 0x43, 0x8b, 0x14, 0x17, 0x8d, 0x32, 0x9b, 0xe5, 0xb2, 0x13, 0x36, 0xae, 0xdd, + 0x02, 0xa1, 0x78, 0x98, 0xb6, 0x50, 0xb5, 0x9e, 0xea, 0xf5, 0xdc, 0xb4, 0xfb, 0xee, 0x51, 0xb5, 0xca, 0x91, 0xce, + 0xda, 0x74, 0xa5, 0x56, 0xb7, 0x8c, 0xaa, 0x75, 0x96, 0x46, 0x54, 0xb9, 0x49, 0xee, 0x1a, 0xb5, 0xe0, 0x93, 0x0d, + 0x5d, 0xa6, 0xec, 0x6c, 0x0d, 0x4e, 0x1c, 0x79, 0x2e, 0xb9, 0xe5, 0xbb, 0xf3, 0x8a, 0xee, 0x4e, 0xb5, 0x6f, 0x01, + 0xee, 0xcd, 0xb0, 0x21, 0x73, 0x5e, 0x63, 0xa7, 0x41, 0x98, 0x04, 0x7e, 0xc4, 0x3e, 0x66, 0xc8, 0x06, 0x03, 0x3a, + 0x0a, 0xe9, 0x7f, 0x6d, 0x99, 0x23, 0x01, 0x93, 0xbf, 0x9e, 0xfb, 0xcd, 0xa2, 0xc8, 0x61, 0x31, 0x7e, 0xd8, 0x60, + 0xa4, 0xb1, 0x5a, 0x83, 0x61, 0x79, 0x87, 0xc8, 0x9f, 0xda, 0x1d, 0xd3, 0x54, 0xc7, 0x9b, 0xf5, 0x5a, 0xf3, 0xab, + 0xa7, 0x4f, 0x75, 0x7d, 0xfe, 0xdb, 0xf7, 0x97, 0x61, 0xcd, 0xec, 0x0f, 0x41, 0x28, 0xed, 0xde, 0x2d, 0xce, 0x1d, + 0x89, 0xde, 0xb1, 0xd2, 0xcc, 0x2e, 0xed, 0x92, 0x5d, 0x9a, 0xd2, 0x6e, 0xc8, 0xf5, 0xea, 0x2b, 0xe5, 0x8d, 0x9d, + 0x57, 0x4c, 0xf7, 0xef, 0x85, 0xde, 0x51, 0x4e, 0xd5, 0x04, 0x22, 0x9a, 0xb4, 0x23, 0x71, 0xbb, 0x57, 0x86, 0xcf, + 0x27, 0x79, 0xbb, 0x84, 0xa3, 0xae, 0x61, 0xb9, 0xf9, 0xf6, 0x3f, 0xf3, 0xaa, 0xb3, 0xc2, 0xed, 0x97, 0xc6, 0xac, + 0xfd, 0x29, 0x88, 0xab, 0xfa, 0xc3, 0x7b, 0x52, 0x33, 0x25, 0xff, 0x57, 0x3d, 0x06, 0xae, 0x7e, 0x32, 0xed, 0xe8, + 0x9e, 0x42, 0xd8, 0x60, 0xf6, 0xf3, 0xe3, 0x87, 0x16, 0xac, 0xaa, 0x0b, 0x14, 0xc9, 0x01, 0x74, 0xee, 0x92, 0x11, + 0xde, 0xef, 0x18, 0xe7, 0xfe, 0xd5, 0xf7, 0x6a, 0x72, 0x84, 0x88, 0x76, 0x11, 0x0e, 0x00, 0xe2, 0x4e, 0x53, 0x59, + 0x87, 0x1a, 0xa0, 0x0f, 0x08, 0xac, 0x43, 0xdf, 0x66, 0x00, 0x07, 0x7d, 0xb4, 0x79, 0x16, 0x81, 0xbc, 0xee, 0xdd, + 0xb3, 0xb7, 0x6c, 0xe7, 0xf3, 0xeb, 0x55, 0xea, 0xdd, 0xa3, 0x43, 0xf0, 0xf9, 0xd8, 0x9f, 0x5e, 0x06, 0x06, 0x17, + 0x9a, 0xbd, 0x7d, 0x26, 0xd8, 0x8e, 0xed, 0x9e, 0x21, 0x52, 0x51, 0x77, 0xfe, 0xe1, 0xa5, 0x89, 0x9e, 0x77, 0x5e, + 0xb8, 0xe3, 0x4b, 0x00, 0x0f, 0x64, 0x31, 0xa0, 0xf8, 0x2c, 0xbd, 0x7f, 0xb1, 0x04, 0xd4, 0xe4, 0xb7, 0x7c, 0xed, + 0x7d, 0xa1, 0xd4, 0x05, 0xfc, 0x39, 0xa0, 0xf4, 0x49, 0xce, 0xbd, 0xbb, 0xe1, 0xad, 0x7f, 0xf1, 0x1c, 0x9c, 0x27, + 0x56, 0xc3, 0x05, 0xfc, 0x55, 0xf0, 0xa1, 0x77, 0x37, 0xc0, 0xc4, 0x92, 0x0f, 0xbd, 0xd5, 0x00, 0x52, 0x15, 0x2e, + 0x24, 0xc6, 0x3e, 0xfc, 0x1a, 0xe4, 0x0c, 0xff, 0xf8, 0x4d, 0x63, 0xb0, 0xfe, 0x1a, 0x14, 0x1a, 0x8d, 0xb5, 0x54, + 0x21, 0x4b, 0xb1, 0x38, 0x13, 0x60, 0x13, 0x8e, 0xbb, 0x7d, 0xb1, 0xaa, 0xcd, 0x5a, 0xd0, 0x9f, 0x8f, 0xf8, 0x1e, + 0x8d, 0xd5, 0x55, 0x39, 0x17, 0xe5, 0x47, 0xa4, 0x4f, 0x75, 0x7c, 0x8c, 0x8a, 0x4d, 0xdd, 0x9d, 0x4e, 0xb5, 0xea, + 0x48, 0xfb, 0x4d, 0xb9, 0x06, 0x3b, 0x5e, 0x27, 0x47, 0x96, 0xc2, 0xb3, 0x0e, 0x3b, 0x2f, 0x9d, 0x12, 0x1d, 0x86, + 0xf1, 0x6e, 0xab, 0x9e, 0x31, 0x94, 0xe7, 0x06, 0x63, 0xba, 0xe0, 0x11, 0xbf, 0x1e, 0xe4, 0x32, 0x34, 0xe6, 0x03, + 0xb2, 0x61, 0x28, 0x1f, 0x5a, 0x64, 0x48, 0x88, 0x78, 0x0f, 0x95, 0x80, 0x6d, 0x0b, 0xca, 0xa4, 0x80, 0xb3, 0x68, + 0xf0, 0x5b, 0xed, 0xe5, 0xc0, 0x7b, 0x10, 0xf9, 0x8d, 0x74, 0x29, 0x97, 0xd8, 0xe8, 0xc4, 0xb1, 0x2c, 0xb4, 0xf3, + 0xb8, 0xfe, 0x3a, 0x06, 0xf5, 0x7b, 0xa5, 0xdf, 0xa0, 0x9c, 0xfd, 0x51, 0xb2, 0x4e, 0x1b, 0x4f, 0x8c, 0x7f, 0xb8, + 0xca, 0x3f, 0x45, 0x4b, 0x3d, 0xfc, 0x7f, 0xc6, 0x14, 0x4a, 0xff, 0x32, 0x2d, 0xa3, 0xcd, 0x6a, 0x29, 0x4a, 0x91, + 0x47, 0xe2, 0xe4, 0x6b, 0x91, 0x9d, 0xcb, 0x77, 0x3e, 0x85, 0x7e, 0x01, 0x68, 0xd9, 0x27, 0xc8, 0xe8, 0xef, 0x99, + 0xe0, 0xc3, 0xef, 0xb5, 0x73, 0x6d, 0xce, 0xc7, 0x93, 0xfc, 0xca, 0xda, 0xbb, 0x1d, 0x2f, 0x12, 0xa3, 0x18, 0xcb, + 0x7d, 0xd5, 0xcd, 0xca, 0x89, 0x4a, 0x0e, 0x8c, 0x74, 0x4d, 0xf6, 0x72, 0x25, 0xeb, 0x76, 0xba, 0x95, 0x40, 0x44, + 0x15, 0x78, 0x8f, 0x71, 0x15, 0xfb, 0x08, 0xa6, 0xeb, 0x8e, 0xcb, 0x68, 0xc7, 0x7b, 0xc6, 0xab, 0x13, 0x65, 0x05, + 0xb7, 0x1b, 0xd1, 0x9e, 0xd0, 0xd1, 0x4f, 0x93, 0xda, 0xb2, 0x70, 0x00, 0x72, 0x97, 0x30, 0x96, 0x0d, 0xc1, 0x8a, + 0x41, 0xe9, 0xeb, 0x35, 0x25, 0xcb, 0x02, 0x2c, 0x3a, 0xbb, 0x8c, 0x40, 0x0c, 0xeb, 0xa6, 0x39, 0xa1, 0xe3, 0xa5, + 0x8b, 0xf3, 0x5e, 0xab, 0x48, 0xc1, 0x33, 0x5a, 0x74, 0xcc, 0x4d, 0x47, 0xba, 0x31, 0xda, 0xdb, 0xef, 0x0d, 0x42, + 0x8a, 0xe7, 0x0f, 0x6c, 0xb5, 0x2e, 0x2e, 0x12, 0xaf, 0x90, 0x89, 0x16, 0xc4, 0x52, 0x04, 0x66, 0xbc, 0xd0, 0x34, + 0xc2, 0x04, 0x65, 0x4a, 0xb0, 0x68, 0x8d, 0x0e, 0xed, 0x0f, 0x4b, 0xd8, 0x3d, 0xc6, 0x08, 0x10, 0xa8, 0x32, 0x7d, + 0x0e, 0x5b, 0x13, 0x66, 0x53, 0x17, 0x1b, 0xa0, 0xad, 0x62, 0x68, 0x10, 0xd6, 0x86, 0x98, 0x8f, 0x69, 0x7e, 0xf7, + 0x2f, 0x2c, 0xc6, 0xf6, 0x04, 0x62, 0x7b, 0xb7, 0x6b, 0x12, 0xa6, 0x7b, 0x2d, 0x6e, 0xac, 0x97, 0xdb, 0x53, 0x8e, + 0xa9, 0x1d, 0x6b, 0xa3, 0x76, 0xac, 0xa5, 0xde, 0xb1, 0xd6, 0x7a, 0xc7, 0xba, 0x6b, 0xf8, 0x87, 0xcc, 0x8b, 0x59, + 0x02, 0xfa, 0xdd, 0x15, 0x57, 0x0d, 0x82, 0x66, 0x6c, 0xd8, 0x2d, 0xfc, 0x96, 0x58, 0xbb, 0xa5, 0x7f, 0xb1, 0x64, + 0x0b, 0xd3, 0x07, 0xba, 0x75, 0x80, 0x65, 0x44, 0x4d, 0xbe, 0x47, 0xde, 0x4d, 0x67, 0x45, 0xe1, 0xf6, 0xc4, 0x16, + 0x3e, 0x7b, 0x6b, 0xde, 0xbc, 0x7f, 0x16, 0x41, 0xee, 0x1d, 0xf7, 0xee, 0x87, 0x6f, 0xfd, 0x0b, 0xdd, 0x02, 0x39, + 0x99, 0xe5, 0x0c, 0xa4, 0x8e, 0xf8, 0x04, 0xd1, 0xca, 0x9e, 0xf2, 0x9d, 0x90, 0x3b, 0xdb, 0xfa, 0xd9, 0xbd, 0xbb, + 0xad, 0xdd, 0x3d, 0xbb, 0x67, 0xd5, 0x88, 0x62, 0xc5, 0x69, 0x8a, 0x84, 0x59, 0xb4, 0x01, 0x9e, 0x7a, 0xf9, 0x7e, + 0xc7, 0x8e, 0x39, 0xdc, 0x3d, 0xeb, 0xe8, 0x78, 0x39, 0x07, 0xec, 0xee, 0x3f, 0xda, 0x84, 0x8d, 0x95, 0xae, 0x55, + 0xe8, 0x70, 0xf7, 0x2c, 0xd3, 0x78, 0x0e, 0x47, 0xf2, 0xe9, 0x58, 0x63, 0x83, 0xa0, 0xae, 0xcf, 0x19, 0xd4, 0x8e, + 0xdd, 0xd7, 0x84, 0x5d, 0x76, 0xcc, 0x6b, 0x5d, 0xf3, 0xf6, 0xca, 0x53, 0xb1, 0x21, 0xa0, 0xc3, 0xd7, 0xea, 0x06, + 0xf9, 0x97, 0xc0, 0x29, 0x02, 0x40, 0x0e, 0xc7, 0x4b, 0x1e, 0xfb, 0x3e, 0xcd, 0xd2, 0x7a, 0x87, 0x5a, 0x8b, 0xca, + 0xb2, 0x0c, 0x6b, 0xef, 0x07, 0xad, 0x18, 0x96, 0x9a, 0xfe, 0xe9, 0x38, 0x70, 0x3b, 0xdb, 0xad, 0x8c, 0x5d, 0xc6, + 0xb3, 0xe2, 0xe2, 0xfb, 0xd3, 0x42, 0xb9, 0x76, 0xf3, 0x36, 0x7e, 0xd3, 0x6a, 0xc9, 0xd2, 0x5a, 0x0f, 0x79, 0x69, + 0x59, 0x44, 0x20, 0x80, 0xe1, 0x48, 0xd9, 0xc5, 0x12, 0xee, 0x11, 0x56, 0xf7, 0x20, 0x94, 0xcc, 0x0b, 0x17, 0xcf, + 0x59, 0x0c, 0x89, 0x00, 0xdb, 0x1d, 0x2a, 0xb6, 0x85, 0x8b, 0xe7, 0x6c, 0xc3, 0x8b, 0x7e, 0x3f, 0x53, 0x9d, 0x42, + 0xd6, 0x9d, 0x25, 0xdf, 0xa8, 0xe6, 0x58, 0x43, 0xcd, 0xd6, 0x26, 0xd9, 0x1a, 0xe7, 0xb6, 0xe2, 0xe3, 0xae, 0xad, + 0xf8, 0x58, 0x59, 0xeb, 0xd2, 0xbd, 0xde, 0xa3, 0xba, 0x00, 0xb6, 0xfe, 0xdb, 0xe3, 0x95, 0xeb, 0xf9, 0x8c, 0x00, + 0xbe, 0x16, 0x7c, 0x3c, 0x59, 0xa0, 0x57, 0xc9, 0xc2, 0xbf, 0x1d, 0xa8, 0xf1, 0x77, 0x3a, 0x77, 0x01, 0xd0, 0x95, + 0x94, 0x57, 0x40, 0xde, 0x41, 0x8e, 0xb9, 0x65, 0x57, 0xde, 0x9f, 0x7c, 0x87, 0xbd, 0xe5, 0xf5, 0x6c, 0x31, 0x67, + 0x3b, 0x70, 0x2a, 0x48, 0x06, 0xf6, 0xb2, 0x62, 0xbb, 0x20, 0xb6, 0x13, 0x7e, 0x23, 0x60, 0xca, 0x17, 0x10, 0xc4, + 0x15, 0xdc, 0x42, 0x1c, 0x9e, 0xfc, 0x73, 0x70, 0xdf, 0xda, 0xac, 0xef, 0x99, 0xd5, 0x39, 0xc1, 0x9a, 0x59, 0x3d, + 0x18, 0x2c, 0x9b, 0xc9, 0xaa, 0xdf, 0xf7, 0x76, 0xda, 0xf1, 0xe9, 0x4e, 0xea, 0xc4, 0x4e, 0x6b, 0xb5, 0x16, 0xec, + 0xad, 0xd4, 0xba, 0x18, 0x43, 0x0f, 0x10, 0x3f, 0xdd, 0x0e, 0xf8, 0x7d, 0xc7, 0xda, 0xf2, 0xde, 0xb2, 0x05, 0xdb, + 0xc1, 0x25, 0xa8, 0x69, 0x2f, 0xfb, 0x93, 0xca, 0x05, 0xed, 0xd8, 0x25, 0xf1, 0x70, 0xc6, 0xac, 0x52, 0x66, 0xd6, + 0x49, 0x75, 0x25, 0x3a, 0x63, 0x3a, 0x6b, 0x3d, 0x9f, 0xab, 0xf9, 0xa4, 0xd0, 0xa0, 0x7e, 0xe7, 0xc4, 0x47, 0x54, + 0x74, 0x9e, 0xc0, 0xd6, 0xb2, 0x82, 0x58, 0xed, 0x73, 0xb0, 0xd6, 0x6a, 0x97, 0x7e, 0x2f, 0x1f, 0x70, 0x9b, 0x72, + 0x58, 0x07, 0x06, 0x35, 0x27, 0x56, 0xd4, 0x63, 0xb6, 0x63, 0xdc, 0xfc, 0xf4, 0xf2, 0x07, 0x27, 0x2c, 0x59, 0xb1, + 0xda, 0x9f, 0x7e, 0xff, 0xcc, 0xd3, 0xdf, 0xa9, 0xfd, 0x0b, 0xe1, 0x07, 0xe3, 0xff, 0xd4, 0xee, 0x6b, 0x2d, 0x46, + 0x65, 0xab, 0x1c, 0xa1, 0x71, 0xb7, 0x92, 0x26, 0xcb, 0x4f, 0xc2, 0x13, 0xd6, 0x82, 0x67, 0xb9, 0x5e, 0xa2, 0x59, + 0x01, 0x2b, 0xac, 0x65, 0x12, 0xae, 0x30, 0x56, 0x4b, 0x5b, 0x7d, 0x8b, 0xa6, 0x39, 0x3e, 0x9c, 0x6b, 0x83, 0x32, + 0xe5, 0xec, 0x8c, 0x58, 0x0d, 0x97, 0x61, 0x69, 0x42, 0x11, 0xb2, 0x7b, 0x3b, 0xb8, 0xb1, 0x53, 0x96, 0x52, 0x86, + 0x73, 0x0c, 0x26, 0x3c, 0x12, 0xa3, 0x2a, 0xdf, 0xdf, 0x97, 0x14, 0x39, 0x6d, 0xcb, 0x41, 0x15, 0xc2, 0x3e, 0x92, + 0x28, 0x81, 0x5b, 0x91, 0x16, 0x8a, 0x94, 0xc5, 0xdf, 0x0e, 0xd0, 0x05, 0x5e, 0x40, 0x5d, 0x8d, 0xba, 0xfd, 0xe1, + 0x88, 0x87, 0x8f, 0x4c, 0x7d, 0x60, 0xc4, 0x92, 0x40, 0x6d, 0x2f, 0xb2, 0xf4, 0x0e, 0x54, 0xf8, 0x3d, 0x5c, 0x4d, + 0xc4, 0x7e, 0x6e, 0x49, 0x51, 0x91, 0x8d, 0xf4, 0x86, 0xd6, 0xe0, 0x11, 0x5a, 0x53, 0xbe, 0x77, 0x52, 0x6d, 0xd2, + 0x79, 0x47, 0xc8, 0xb1, 0xfa, 0xd6, 0x12, 0x46, 0xbb, 0xa2, 0x17, 0xf7, 0x8e, 0xde, 0xf3, 0x74, 0xd5, 0x73, 0x7f, + 0xe2, 0x8a, 0x79, 0x72, 0x1b, 0x81, 0xba, 0x15, 0x54, 0xb7, 0xf7, 0x2a, 0xc1, 0x82, 0x25, 0xed, 0x3e, 0x7e, 0x3b, + 0x6b, 0x07, 0xa2, 0x32, 0x56, 0xe9, 0x6b, 0x92, 0xb0, 0x27, 0x06, 0x9d, 0x42, 0x55, 0x6e, 0x77, 0x47, 0x5b, 0xe0, + 0x3a, 0x66, 0x29, 0x7a, 0x61, 0x8b, 0xdc, 0x2d, 0xff, 0xee, 0xb9, 0x22, 0x67, 0xbf, 0x04, 0x04, 0xa7, 0xe6, 0x2b, + 0xe2, 0xcb, 0x11, 0x1e, 0x55, 0xb7, 0xc0, 0x71, 0xfa, 0x0e, 0xe0, 0x1f, 0x0e, 0x97, 0xa0, 0x09, 0x88, 0x05, 0xeb, + 0xa5, 0x71, 0x8f, 0xf5, 0xe2, 0x62, 0x73, 0x97, 0xe4, 0x1b, 0x70, 0x66, 0xa0, 0x54, 0x4b, 0x3f, 0x70, 0xac, 0x16, + 0x50, 0xe1, 0x60, 0x76, 0x52, 0x2f, 0x2c, 0xa3, 0x1e, 0xd3, 0xe7, 0x67, 0xb0, 0x77, 0x84, 0x04, 0xc0, 0xfd, 0xb2, + 0x0f, 0x48, 0xc0, 0x43, 0x67, 0x76, 0x40, 0x38, 0x61, 0x16, 0x55, 0x81, 0x44, 0x72, 0xa4, 0x9f, 0x3d, 0x66, 0x22, + 0xf9, 0x83, 0x59, 0xcf, 0x39, 0x25, 0x7a, 0xac, 0xa7, 0x8e, 0x90, 0x1e, 0xeb, 0x59, 0x47, 0x44, 0x8f, 0xf5, 0xac, + 0xe3, 0xa3, 0xc7, 0x7a, 0xe6, 0xd8, 0xe9, 0x41, 0x60, 0x02, 0x44, 0x1e, 0xb0, 0x1e, 0x4d, 0xa6, 0x9e, 0xe2, 0x1e, + 0x20, 0x1a, 0x04, 0xd6, 0x93, 0xc2, 0x79, 0x0f, 0x90, 0xc7, 0x48, 0xac, 0x0e, 0x7a, 0x7f, 0x19, 0x3f, 0xed, 0x19, + 0x19, 0x79, 0xdc, 0x3a, 0xac, 0xfe, 0xd7, 0x5f, 0x21, 0x00, 0x0e, 0xcf, 0xa6, 0xde, 0xe5, 0x18, 0xb2, 0xca, 0x32, + 0x02, 0xc9, 0x4f, 0x0c, 0xbe, 0x7c, 0x01, 0x50, 0xf5, 0x99, 0xae, 0xd5, 0xe4, 0xa8, 0x3d, 0xe6, 0xd0, 0x15, 0x03, + 0xc0, 0x36, 0x2c, 0x51, 0x55, 0x0b, 0x9b, 0xb0, 0xb8, 0xfd, 0x0c, 0xa3, 0xb9, 0x6c, 0x7a, 0x41, 0x03, 0xf5, 0x08, + 0xc1, 0x2f, 0xad, 0x87, 0xd6, 0x5a, 0xa6, 0x1c, 0xba, 0x36, 0x8a, 0x2a, 0x1b, 0xea, 0x12, 0x56, 0x6b, 0x11, 0xd5, + 0x44, 0x91, 0x72, 0xc9, 0x28, 0x8a, 0xa5, 0x0a, 0xf6, 0x99, 0xb8, 0x83, 0xa8, 0x79, 0xda, 0x6a, 0xab, 0x60, 0x7f, + 0x07, 0x08, 0x6b, 0x61, 0x2d, 0xa4, 0x33, 0xa8, 0xbd, 0xd3, 0x8f, 0x94, 0xbf, 0xbc, 0x90, 0xdb, 0xb9, 0x85, 0x22, + 0xdc, 0x9e, 0x83, 0xf2, 0xa6, 0xae, 0x4a, 0x45, 0x34, 0x5a, 0x02, 0xa5, 0xcc, 0x09, 0x22, 0x0b, 0x10, 0xc0, 0x71, + 0x03, 0x81, 0xcf, 0x6b, 0x7c, 0x02, 0x8d, 0x42, 0x20, 0x3f, 0xb0, 0x0a, 0xd7, 0x1e, 0xd2, 0x52, 0x6b, 0x44, 0x94, + 0x88, 0x1f, 0x5d, 0x3d, 0xc7, 0xf6, 0xd5, 0xd3, 0x58, 0x5b, 0x4a, 0x13, 0xc4, 0x4f, 0x2c, 0xb6, 0x10, 0x13, 0x44, + 0x75, 0x88, 0x8e, 0x60, 0x39, 0x21, 0x44, 0xe1, 0x0f, 0xa1, 0x9f, 0x1a, 0xf8, 0x4b, 0xb6, 0x2c, 0xf2, 0x9a, 0x60, + 0x31, 0x2b, 0x06, 0x68, 0x55, 0x04, 0x9e, 0xe9, 0x6c, 0xa9, 0xcc, 0x69, 0x1e, 0x1d, 0xd9, 0xc1, 0x79, 0xd7, 0xc1, + 0x5e, 0xfa, 0x32, 0x76, 0xb2, 0x6c, 0x1a, 0xb5, 0xb1, 0x21, 0x12, 0x5e, 0x91, 0xbf, 0xcc, 0x52, 0xe3, 0x1c, 0x99, + 0xcb, 0xf5, 0x5d, 0x17, 0x77, 0x77, 0xb4, 0x4d, 0x58, 0x85, 0x08, 0x75, 0xdb, 0x50, 0xb9, 0x14, 0x66, 0x63, 0xd3, + 0x34, 0xc0, 0x17, 0x8a, 0x4a, 0xa5, 0x2a, 0xb5, 0x95, 0x4a, 0x4e, 0x78, 0xd7, 0x57, 0xb5, 0x48, 0x5d, 0x11, 0x6c, + 0x63, 0x86, 0x7a, 0x28, 0x37, 0x6a, 0xec, 0xeb, 0x8e, 0x55, 0x7a, 0x87, 0x09, 0x72, 0x46, 0x5e, 0xe4, 0xe0, 0xa2, + 0xa4, 0x20, 0x73, 0x35, 0x84, 0xf9, 0xa3, 0x86, 0x4f, 0x0b, 0xcb, 0x3d, 0x94, 0x80, 0xd9, 0x51, 0xc3, 0xcb, 0x08, + 0x81, 0x88, 0x4b, 0x65, 0x5f, 0x31, 0xf1, 0x7b, 0x0a, 0x66, 0xc9, 0x84, 0xee, 0x45, 0x2c, 0x8c, 0xd0, 0xc6, 0x27, + 0x49, 0x32, 0xf5, 0x34, 0x05, 0x37, 0x72, 0x19, 0xe6, 0x68, 0x84, 0x96, 0x7c, 0xe4, 0x40, 0xfa, 0x5a, 0x4e, 0x25, + 0xf8, 0x88, 0x3a, 0x05, 0x1c, 0xcf, 0xcf, 0x0b, 0xeb, 0x27, 0xcb, 0x25, 0xe6, 0xb2, 0x36, 0xff, 0x65, 0x47, 0xc7, + 0x60, 0x97, 0xa7, 0x89, 0xe3, 0xea, 0x3f, 0xaa, 0x92, 0xe2, 0xe1, 0xe7, 0x34, 0x07, 0x14, 0xc1, 0xcc, 0x9e, 0x62, + 0x7c, 0xec, 0xb3, 0x4c, 0x01, 0x7f, 0xbb, 0xde, 0x5a, 0x32, 0xb1, 0x4b, 0xda, 0xcd, 0x95, 0xf1, 0x4b, 0x6d, 0xd8, + 0x71, 0x70, 0x6e, 0x00, 0x8a, 0xb3, 0x46, 0x87, 0xe5, 0xb5, 0x6e, 0x5b, 0x15, 0x2a, 0x50, 0xeb, 0xff, 0xec, 0x16, + 0xa6, 0xbc, 0xcd, 0x4b, 0xe5, 0x6d, 0x1e, 0x9a, 0x00, 0x81, 0xc8, 0x0c, 0x79, 0xd6, 0x74, 0x4c, 0x12, 0xf7, 0x8e, + 0x94, 0xb4, 0xef, 0x48, 0xf1, 0xa3, 0x77, 0x24, 0xe4, 0x5b, 0x42, 0x47, 0xf6, 0x25, 0x27, 0x27, 0x50, 0x66, 0xb0, + 0x97, 0xd7, 0x4c, 0xf6, 0x0f, 0x68, 0x2f, 0x9c, 0xcb, 0xf2, 0x8a, 0xbf, 0x15, 0xde, 0xda, 0x9f, 0xae, 0x4f, 0xbb, + 0xaa, 0xde, 0x7e, 0x65, 0x66, 0x1e, 0x0e, 0xc5, 0xe1, 0x50, 0x99, 0xa0, 0xdd, 0x05, 0x17, 0x83, 0x9c, 0xdd, 0xbb, + 0xf1, 0xf1, 0x6f, 0x39, 0x8a, 0xd8, 0x4a, 0x79, 0x24, 0x5d, 0xa8, 0xc4, 0xf0, 0xd2, 0xc0, 0xc3, 0xec, 0xf8, 0x78, + 0xb2, 0xbb, 0xba, 0x9f, 0x0c, 0x06, 0x3b, 0xd5, 0xb7, 0x5b, 0x5e, 0xcf, 0x76, 0x73, 0xf6, 0xc0, 0x6f, 0xa7, 0xdb, + 0x60, 0xdf, 0xc0, 0xb6, 0xbb, 0xbb, 0x12, 0x87, 0xc3, 0xee, 0x9a, 0x2f, 0xfc, 0xfd, 0x03, 0x02, 0x3a, 0xf3, 0xf3, + 0x71, 0x1b, 0xe3, 0xe7, 0xa6, 0xed, 0xaa, 0xb5, 0x03, 0x78, 0xfa, 0x1f, 0xbc, 0x9b, 0xd9, 0x72, 0xee, 0xb3, 0x27, + 0xfc, 0x01, 0xfc, 0xf3, 0x71, 0x93, 0x44, 0xea, 0x13, 0xed, 0x32, 0x79, 0x03, 0x0e, 0xe4, 0x3b, 0x9f, 0xbd, 0xe1, + 0x0f, 0xb3, 0xe5, 0x9c, 0x17, 0x87, 0xc3, 0xfb, 0x69, 0x88, 0x64, 0x4d, 0x61, 0x45, 0x2c, 0x29, 0x9e, 0x1f, 0x84, + 0xc7, 0xef, 0x45, 0x64, 0x88, 0xb4, 0xdc, 0xbb, 0x43, 0x76, 0xc3, 0x22, 0x3f, 0x80, 0x0f, 0xb2, 0x9d, 0x3f, 0x91, + 0x35, 0xa5, 0xfb, 0xc5, 0x13, 0xff, 0x70, 0xa0, 0xbf, 0xde, 0xf8, 0x87, 0xc3, 0x7b, 0xf6, 0x80, 0xe0, 0xe8, 0x7c, + 0x07, 0xfd, 0xa3, 0x6f, 0x1d, 0x50, 0x95, 0xe1, 0xdb, 0xd9, 0x66, 0xee, 0x5f, 0xaf, 0xd8, 0x1d, 0x70, 0xa1, 0x28, + 0x2f, 0xb4, 0x1b, 0xf6, 0x80, 0x5e, 0x67, 0xe4, 0x44, 0x34, 0xdb, 0xcd, 0x7d, 0x16, 0xe3, 0x73, 0x75, 0x5f, 0x4c, + 0xbe, 0x7a, 0x5f, 0xdc, 0xb1, 0x6d, 0xf7, 0x7d, 0x51, 0xbe, 0xe9, 0xae, 0x9f, 0x2d, 0xdb, 0xb1, 0x07, 0x98, 0x61, + 0x6f, 0xf9, 0x4d, 0x73, 0xec, 0x18, 0xfb, 0xd5, 0x1b, 0x23, 0x80, 0x32, 0x5b, 0xb0, 0x58, 0x70, 0x50, 0xaa, 0x55, + 0xdb, 0x92, 0xc8, 0x2b, 0x1d, 0xa8, 0x36, 0x23, 0xb8, 0xaf, 0x16, 0x72, 0xe6, 0x99, 0x81, 0xbe, 0xad, 0x10, 0x2d, + 0x1c, 0x36, 0xe0, 0xaf, 0xb4, 0x75, 0x8c, 0x61, 0x9a, 0xd5, 0x4c, 0xdb, 0xa2, 0x2e, 0xbf, 0xed, 0x3d, 0x93, 0xdf, + 0xc8, 0xc0, 0x16, 0x22, 0x29, 0x1c, 0xc7, 0x17, 0xcf, 0x4f, 0xf8, 0xaf, 0x5a, 0x1e, 0xb5, 0xda, 0x2f, 0x94, 0xfa, + 0xf4, 0x25, 0x1d, 0xd1, 0xc4, 0xbd, 0x68, 0xcb, 0xb0, 0x46, 0x59, 0x53, 0x4b, 0x87, 0x61, 0x5c, 0xc3, 0xbe, 0x3c, + 0x70, 0xe8, 0x3b, 0x20, 0xd0, 0x56, 0xa9, 0x14, 0x68, 0xe1, 0x18, 0x46, 0x61, 0x16, 0x52, 0x1e, 0x17, 0x66, 0x29, + 0xef, 0xb1, 0x40, 0x8b, 0x5b, 0x75, 0x8f, 0xa9, 0xed, 0x16, 0x44, 0x58, 0xbd, 0x65, 0x9c, 0x5f, 0x36, 0xaa, 0x70, + 0x5b, 0x80, 0xa2, 0x08, 0xca, 0x60, 0x4f, 0x72, 0xdb, 0x42, 0x49, 0xb3, 0x51, 0x58, 0x8b, 0xbb, 0xa2, 0xdc, 0xf5, + 0x1a, 0xb6, 0xc0, 0x0b, 0xaa, 0x7e, 0x42, 0xd8, 0x96, 0x3d, 0xeb, 0x50, 0x2e, 0xd2, 0xff, 0xc8, 0xd2, 0xf3, 0xed, + 0xd6, 0x9c, 0xff, 0xe9, 0x2b, 0xfa, 0xa8, 0xfc, 0xcf, 0x2f, 0xe9, 0x27, 0x83, 0x65, 0xe4, 0x94, 0xfa, 0x3e, 0x1a, + 0xdd, 0xa6, 0x39, 0x61, 0x6c, 0xf9, 0xfa, 0xe9, 0x37, 0xc8, 0x14, 0x24, 0x87, 0x52, 0xaa, 0x72, 0xb2, 0x87, 0xbe, + 0xf0, 0xba, 0x0f, 0x33, 0xc1, 0x00, 0x84, 0xd7, 0x68, 0x53, 0x4d, 0x98, 0xc4, 0xa3, 0x2b, 0xf8, 0xbf, 0x11, 0xc4, + 0xa0, 0x7d, 0xa2, 0xa8, 0x63, 0xdb, 0x48, 0xd7, 0x6d, 0xe7, 0x20, 0xb9, 0x53, 0x57, 0xfe, 0xa8, 0x9c, 0xfc, 0x27, + 0x1a, 0x22, 0xaf, 0xb8, 0x42, 0xac, 0x2c, 0xb8, 0xc4, 0x62, 0xa8, 0x48, 0x01, 0xae, 0x21, 0x88, 0x94, 0x45, 0x49, + 0xe1, 0x96, 0x83, 0xaa, 0x08, 0xc0, 0xb8, 0x5a, 0x1d, 0x75, 0x22, 0x7c, 0xdc, 0x5a, 0x8b, 0x10, 0xac, 0x68, 0xd4, + 0xca, 0x5a, 0x81, 0x2f, 0x48, 0x5f, 0x3a, 0x14, 0xc4, 0xf4, 0x28, 0xa4, 0xaa, 0x74, 0x28, 0x90, 0xe6, 0x50, 0xf1, + 0x8d, 0xc1, 0x46, 0x51, 0x91, 0x9e, 0xbf, 0x34, 0x29, 0xb9, 0x34, 0x66, 0x7c, 0x10, 0x65, 0x24, 0xf2, 0x3a, 0xbc, + 0x13, 0xd3, 0x02, 0xf9, 0x46, 0x8f, 0x1f, 0x04, 0x97, 0xf0, 0x6e, 0xc8, 0xbd, 0x02, 0x6c, 0x09, 0xd8, 0x01, 0xee, + 0x95, 0x19, 0xe5, 0x3a, 0xad, 0xeb, 0xb7, 0xd6, 0x43, 0x31, 0x0c, 0x9f, 0x59, 0x02, 0xdb, 0xd1, 0x3a, 0x3a, 0xd2, + 0xc3, 0x87, 0xff, 0x75, 0x55, 0x73, 0xd4, 0xa9, 0x5c, 0xce, 0x8e, 0x27, 0x2c, 0x45, 0xcc, 0xa0, 0xfb, 0xeb, 0xf6, + 0xa5, 0x00, 0xba, 0x5d, 0x16, 0xf3, 0x6c, 0xb4, 0x93, 0x7f, 0x4b, 0x37, 0x56, 0x94, 0x36, 0xf1, 0x2e, 0xeb, 0x8d, + 0xfd, 0xe1, 0xe8, 0x2f, 0xcf, 0xbe, 0x4c, 0x08, 0x55, 0x67, 0xc3, 0xd6, 0x3a, 0xce, 0xe5, 0x7f, 0xfd, 0x75, 0x4c, + 0x56, 0x10, 0x14, 0x84, 0x65, 0xa7, 0x98, 0xa8, 0x60, 0x14, 0x29, 0xd6, 0x7c, 0x3c, 0x59, 0xa3, 0x4e, 0x78, 0xed, + 0x2f, 0xb5, 0x4e, 0x98, 0x18, 0x59, 0xa9, 0xfc, 0x35, 0xab, 0xd8, 0x9d, 0xca, 0x2c, 0x20, 0xf3, 0x20, 0x9f, 0xac, + 0x8d, 0x06, 0x73, 0xc5, 0xeb, 0xd9, 0x7a, 0x2e, 0x95, 0xcf, 0x60, 0xca, 0x59, 0x0e, 0x4e, 0x96, 0xc2, 0xee, 0x49, + 0xa0, 0x68, 0xcd, 0xd0, 0xb5, 0x3f, 0xc5, 0x56, 0xbd, 0x4a, 0xab, 0x1a, 0xe0, 0x01, 0x21, 0x06, 0x86, 0xda, 0xab, + 0x85, 0x87, 0xd6, 0x02, 0x58, 0xfb, 0xa3, 0xd2, 0x0f, 0xc6, 0x93, 0x25, 0x5f, 0x20, 0xff, 0x72, 0xe4, 0xa8, 0xdd, + 0xfb, 0x7d, 0xef, 0x1e, 0xa4, 0xe0, 0xc8, 0xb5, 0x50, 0x20, 0x11, 0xd0, 0x82, 0x6f, 0x7c, 0xe5, 0x83, 0xf1, 0x16, + 0xb5, 0xd5, 0xa0, 0xa0, 0x76, 0x74, 0xcb, 0x63, 0x47, 0xef, 0x7c, 0x7f, 0x42, 0x5f, 0xbd, 0xd0, 0xc2, 0xf1, 0x57, + 0xce, 0xc8, 0x35, 0x5b, 0x75, 0xc8, 0x11, 0xcd, 0xa4, 0x43, 0x88, 0x58, 0xb1, 0x35, 0x7b, 0x4b, 0x2a, 0xe7, 0xce, + 0x21, 0x3b, 0x7d, 0x84, 0x2a, 0xbd, 0xd6, 0xe3, 0xdb, 0x89, 0xd2, 0xdd, 0x1e, 0xef, 0x26, 0xdf, 0xb2, 0x89, 0x88, + 0xc1, 0x80, 0x36, 0x08, 0x67, 0x64, 0x1d, 0x22, 0x95, 0x0e, 0x10, 0x02, 0xc7, 0x04, 0x34, 0xfd, 0xc7, 0xd7, 0x24, + 0x0a, 0x38, 0xd2, 0x46, 0xc8, 0x5a, 0x76, 0x38, 0xe4, 0xa0, 0x51, 0x6e, 0xfe, 0xf0, 0x0a, 0x75, 0x9a, 0x03, 0xf3, + 0x74, 0x09, 0x7b, 0x0e, 0x1e, 0xe9, 0xc5, 0xf1, 0x91, 0xfe, 0xdf, 0xd1, 0x44, 0x8d, 0xff, 0x73, 0x4d, 0x94, 0xd2, + 0x22, 0x39, 0xaa, 0xa5, 0x6f, 0x52, 0x47, 0xc1, 0x45, 0xde, 0x51, 0x0b, 0xd9, 0xb3, 0x6c, 0xdc, 0xa8, 0xe6, 0xfd, + 0xff, 0x5a, 0x99, 0xff, 0xaf, 0x69, 0x65, 0x98, 0x92, 0x1d, 0x4b, 0x35, 0xf3, 0x40, 0xab, 0x18, 0x66, 0x3f, 0x93, + 0x84, 0xc8, 0x70, 0x69, 0xc0, 0x8f, 0x2a, 0xd8, 0xc7, 0x69, 0xb5, 0xce, 0xc2, 0x1d, 0x2a, 0x51, 0x6f, 0xc5, 0x5d, + 0x9a, 0xbf, 0xa8, 0xff, 0x2d, 0xca, 0x02, 0xa6, 0xf6, 0x5d, 0x99, 0xc6, 0x01, 0x59, 0xf8, 0xb3, 0xb0, 0xc4, 0xc9, + 0x8d, 0x6d, 0xfc, 0x59, 0x8e, 0xa7, 0xfd, 0xaa, 0x33, 0xf3, 0x40, 0x02, 0x35, 0x10, 0x7f, 0xe4, 0x5c, 0x56, 0x16, + 0x0f, 0x08, 0xdd, 0xfc, 0x43, 0x59, 0x16, 0xa5, 0xd7, 0xfb, 0x94, 0xa4, 0xd5, 0xd9, 0x4a, 0xd4, 0x49, 0x11, 0x2b, + 0x28, 0x9b, 0x14, 0x60, 0xf4, 0x61, 0xe5, 0x89, 0x38, 0x38, 0x43, 0xa0, 0x86, 0xb3, 0x3a, 0x09, 0x01, 0x68, 0x58, + 0x21, 0xec, 0x9f, 0x41, 0x0b, 0xcf, 0xc2, 0x38, 0x5c, 0x03, 0x4c, 0x4e, 0x5a, 0x9d, 0xad, 0xcb, 0xe2, 0x3e, 0x8d, + 0x45, 0x3c, 0xea, 0x29, 0x4a, 0x96, 0xd7, 0xb9, 0x2b, 0xe7, 0xfa, 0xfb, 0x3f, 0x28, 0x80, 0xdd, 0x80, 0xd9, 0xb6, + 0xc0, 0x0e, 0x00, 0x12, 0x14, 0xc8, 0x16, 0xea, 0x34, 0x3a, 0x53, 0x4b, 0x05, 0xde, 0x73, 0x3d, 0xc0, 0x5f, 0xe7, + 0x80, 0x65, 0x5c, 0x17, 0x32, 0x60, 0x04, 0x01, 0x8c, 0xc0, 0x41, 0x09, 0x18, 0x3a, 0x43, 0xdc, 0x56, 0xe5, 0xac, + 0x85, 0xe6, 0x4a, 0xb7, 0x25, 0x37, 0x8d, 0x72, 0xb6, 0x12, 0x01, 0xf4, 0xd5, 0x4d, 0x89, 0xd3, 0xe5, 0xb2, 0x95, + 0x84, 0x7d, 0xfb, 0xbe, 0x9d, 0x2a, 0xf2, 0xf8, 0x28, 0x0d, 0x79, 0x05, 0x9e, 0x64, 0x1c, 0x49, 0xa2, 0x44, 0xf0, + 0x3a, 0x6f, 0xcc, 0x38, 0xbc, 0x68, 0x53, 0x4e, 0xed, 0xcd, 0x7a, 0x01, 0x38, 0x4f, 0xd0, 0x96, 0x01, 0xc6, 0x02, + 0x06, 0xe7, 0x42, 0x2c, 0x79, 0x8a, 0xe0, 0x97, 0x4e, 0xa4, 0x30, 0xee, 0x72, 0x18, 0xe6, 0x41, 0xd1, 0xbb, 0xa4, + 0xfe, 0xe8, 0xf7, 0x51, 0x9b, 0x0c, 0x86, 0xa0, 0x12, 0x40, 0x65, 0xdd, 0x20, 0x31, 0xb0, 0x2a, 0x2d, 0x24, 0x2e, + 0x21, 0x5e, 0xe6, 0xab, 0x69, 0x1d, 0x05, 0xef, 0xeb, 0x09, 0x21, 0x9c, 0x60, 0x7c, 0x88, 0x1b, 0x20, 0x60, 0xb0, + 0x8a, 0x0b, 0x0c, 0x92, 0xe7, 0x12, 0xdd, 0x1f, 0xcf, 0x77, 0x0c, 0x70, 0xe5, 0xbc, 0xa7, 0xda, 0xd5, 0x03, 0x7b, + 0xb9, 0x4a, 0x97, 0x8c, 0x10, 0x56, 0xfc, 0x5f, 0x44, 0xde, 0xb7, 0xc3, 0x04, 0xd4, 0x36, 0xf2, 0xc7, 0x20, 0x31, + 0x97, 0x89, 0x22, 0x88, 0x47, 0x59, 0xc1, 0x92, 0x34, 0xd8, 0x8c, 0x92, 0x14, 0x34, 0x9a, 0x18, 0x43, 0xa6, 0x42, + 0x3b, 0x24, 0x8d, 0x66, 0x63, 0xb2, 0x8f, 0x21, 0xaf, 0xe1, 0x62, 0xb1, 0xc0, 0xfb, 0x7e, 0x16, 0xaa, 0x83, 0x6d, + 0x69, 0x0e, 0x01, 0x27, 0x09, 0xf6, 0xd4, 0x15, 0x29, 0x09, 0xb3, 0xd1, 0xa7, 0x90, 0x73, 0x03, 0x3a, 0x4e, 0x1a, + 0x43, 0xf5, 0x81, 0x49, 0x78, 0x15, 0xa1, 0x93, 0xb2, 0x42, 0x58, 0xc0, 0x7d, 0x23, 0xa3, 0xd1, 0x4a, 0x1a, 0x04, + 0xde, 0x66, 0xd8, 0x0a, 0x6c, 0x42, 0xc3, 0x5f, 0x64, 0x1e, 0xa6, 0xd5, 0xac, 0x04, 0x73, 0xbe, 0x81, 0x4a, 0x8c, + 0x27, 0xcb, 0x2b, 0xbe, 0x71, 0xb1, 0x12, 0x93, 0xd9, 0x72, 0x3e, 0x59, 0x4b, 0xaa, 0xb9, 0xdc, 0x5b, 0xb3, 0x8c, + 0x2d, 0x61, 0xff, 0x30, 0x30, 0x94, 0x0e, 0xec, 0x68, 0xaa, 0x69, 0x93, 0x00, 0x93, 0xe9, 0x9c, 0xf3, 0xe1, 0x25, + 0xa2, 0xc9, 0xea, 0xd4, 0x9d, 0x4c, 0x55, 0x3b, 0xb8, 0x26, 0x67, 0x72, 0x7a, 0xa4, 0x9e, 0x6a, 0xdd, 0x4b, 0x3e, + 0xda, 0x0e, 0xab, 0xd1, 0xd6, 0x0f, 0xc0, 0xad, 0x53, 0xd8, 0xe9, 0xbb, 0x61, 0x35, 0xda, 0xf9, 0x1a, 0x76, 0x97, + 0x14, 0x02, 0xd5, 0x9f, 0x65, 0x4d, 0xe6, 0xe2, 0x75, 0xf1, 0xe0, 0x15, 0xec, 0xb9, 0x3f, 0xd0, 0xbf, 0x4a, 0xf6, + 0xdc, 0xb7, 0x99, 0x5c, 0xff, 0x4c, 0xbb, 0x46, 0x63, 0xa6, 0xe3, 0xb5, 0x2b, 0xb0, 0x42, 0x03, 0xe4, 0x17, 0xec, + 0x68, 0x6f, 0x72, 0x10, 0x08, 0xd0, 0xbd, 0x04, 0x47, 0x51, 0x40, 0xd4, 0xb4, 0xaa, 0x3c, 0x3a, 0xdd, 0xfb, 0x7b, + 0x7c, 0xa3, 0x04, 0x6c, 0xf2, 0xd4, 0xba, 0xb7, 0x8c, 0xfd, 0xc3, 0x01, 0x42, 0xe8, 0xe5, 0xf4, 0x1b, 0x6d, 0x59, + 0x3d, 0xda, 0xb1, 0xdc, 0x37, 0x8c, 0x7a, 0x0a, 0xc6, 0x30, 0x74, 0x61, 0x15, 0x23, 0x79, 0x06, 0x64, 0x8d, 0xdf, + 0x20, 0xba, 0x80, 0x45, 0xaf, 0xf7, 0xea, 0x88, 0x06, 0x11, 0x50, 0xe9, 0x35, 0x7f, 0x29, 0xf2, 0xb9, 0x2a, 0x44, + 0xef, 0xbd, 0xb5, 0xf3, 0x66, 0x46, 0xb2, 0x4c, 0x1a, 0xa9, 0x76, 0x2b, 0x8b, 0x75, 0xe5, 0xcd, 0x4e, 0x48, 0x17, + 0x73, 0x0c, 0x95, 0xc1, 0xe3, 0x00, 0x94, 0x9e, 0x7f, 0x0b, 0xbd, 0x92, 0x21, 0xd3, 0x2c, 0xd1, 0xcc, 0xee, 0x1a, + 0x7f, 0xb2, 0x4a, 0xbd, 0x18, 0x11, 0xb3, 0x81, 0x2d, 0xc4, 0x6d, 0x51, 0xe9, 0xb6, 0x28, 0x94, 0x2d, 0x8a, 0xf4, + 0xa1, 0x76, 0xa6, 0x3b, 0xb3, 0xf0, 0x59, 0x65, 0xda, 0xf7, 0x26, 0x33, 0x63, 0x03, 0xb4, 0x5d, 0x84, 0x6f, 0xa0, + 0x03, 0x15, 0x42, 0xfe, 0x03, 0x22, 0x22, 0x11, 0xb0, 0xcb, 0xa9, 0x3b, 0xb1, 0xe9, 0x90, 0xcc, 0x43, 0xcc, 0x0a, + 0x35, 0xca, 0x4b, 0x9e, 0x1c, 0x0d, 0x48, 0x45, 0xa8, 0xdb, 0xfd, 0xfe, 0xf9, 0xd2, 0x05, 0xb5, 0x5f, 0x53, 0xec, + 0x18, 0xdd, 0x14, 0x70, 0x2e, 0x78, 0x94, 0xf7, 0xdc, 0x3b, 0x07, 0x34, 0xc7, 0xf6, 0x14, 0x59, 0x03, 0x4e, 0x6f, + 0xbb, 0x10, 0x60, 0xfb, 0xac, 0xd9, 0xda, 0x9f, 0xac, 0xae, 0xa2, 0xa9, 0x57, 0xf2, 0x99, 0xee, 0xa2, 0xc4, 0xed, + 0xa2, 0x58, 0x76, 0xd1, 0xa6, 0x81, 0x60, 0xc7, 0x95, 0x1f, 0x00, 0x6f, 0x68, 0xd4, 0xef, 0x97, 0xad, 0x9e, 0x3d, + 0xf9, 0xda, 0x71, 0xcf, 0x66, 0x3e, 0x2b, 0x4d, 0xcf, 0xfe, 0x9a, 0xba, 0x3d, 0x2b, 0x27, 0x7b, 0xd1, 0x39, 0xd9, + 0xa7, 0xb3, 0x79, 0x20, 0xb8, 0xdc, 0xb9, 0xcf, 0xf3, 0xa9, 0x9e, 0x76, 0x95, 0x1f, 0xb4, 0x86, 0xc8, 0x7c, 0xe1, + 0x53, 0xd5, 0xbd, 0xae, 0x60, 0x01, 0x4b, 0x70, 0xb7, 0x5e, 0x9a, 0xff, 0x8a, 0xdd, 0xdf, 0x0b, 0x7a, 0x69, 0xfe, + 0x1b, 0xfd, 0x49, 0x01, 0x1c, 0x80, 0xc6, 0xd4, 0x6e, 0x81, 0x87, 0x18, 0x2a, 0x28, 0xdc, 0xcd, 0xca, 0xb9, 0x57, + 0x03, 0x1c, 0x26, 0xe9, 0x1b, 0x5a, 0xbd, 0xd2, 0x62, 0xd7, 0xcb, 0x64, 0xaf, 0x00, 0x0f, 0x55, 0xc8, 0xc3, 0xc3, + 0x21, 0xea, 0x18, 0x76, 0x50, 0x47, 0xc0, 0xb0, 0x87, 0xd0, 0xd8, 0x02, 0xcf, 0xc7, 0x4f, 0x19, 0xdf, 0x0b, 0x50, + 0x1b, 0x21, 0x3c, 0x5e, 0x2d, 0xca, 0x10, 0x5b, 0xf6, 0x06, 0xa9, 0xa4, 0x7e, 0x16, 0x88, 0x32, 0x5a, 0x05, 0xb4, + 0xd5, 0x1e, 0xb3, 0x34, 0xde, 0x40, 0xa8, 0x58, 0xea, 0x63, 0x08, 0x0d, 0x1c, 0x7e, 0x87, 0x03, 0x48, 0xf0, 0x25, + 0xd7, 0x64, 0x73, 0x6f, 0xf2, 0x7b, 0xda, 0xe7, 0x0f, 0x87, 0xf3, 0x4b, 0x04, 0xa5, 0x4b, 0xe1, 0x23, 0x95, 0x88, + 0xea, 0x29, 0x6e, 0x4a, 0xc8, 0x66, 0xc9, 0x4a, 0x3f, 0xf8, 0x55, 0xfd, 0x02, 0x00, 0x59, 0x08, 0xb4, 0x89, 0xcc, + 0xfe, 0x74, 0xa6, 0xa2, 0x0b, 0x80, 0x43, 0xfc, 0xf1, 0x13, 0x44, 0xdf, 0xd0, 0x32, 0x2d, 0x1f, 0x27, 0x3c, 0x04, + 0xad, 0x2d, 0xe9, 0x24, 0x62, 0xa5, 0xc0, 0x86, 0x48, 0xf8, 0x7e, 0xff, 0x3c, 0x96, 0x74, 0xa0, 0x51, 0xab, 0x7b, + 0xe3, 0x56, 0xf7, 0xca, 0xd7, 0x75, 0x27, 0x37, 0x3e, 0x28, 0xda, 0x67, 0xf3, 0x46, 0xe5, 0xfb, 0xb6, 0xce, 0xd9, + 0x9d, 0xee, 0x1d, 0x39, 0x27, 0xbe, 0xbd, 0x87, 0x50, 0xf4, 0xd0, 0x14, 0x59, 0x96, 0x84, 0x01, 0xad, 0xb5, 0x6b, + 0xcf, 0x32, 0x3a, 0x78, 0xed, 0x1b, 0x42, 0x44, 0x9e, 0xe2, 0x93, 0x90, 0x5b, 0x1c, 0x1f, 0x14, 0xe8, 0x9f, 0x19, + 0x7f, 0xe6, 0xc4, 0x0f, 0x5b, 0xfd, 0x02, 0x38, 0x37, 0xdd, 0x7b, 0x77, 0x62, 0xd6, 0x63, 0x28, 0x65, 0xe3, 0xff, + 0x7e, 0x9f, 0xc8, 0x02, 0x9d, 0x8e, 0x68, 0x18, 0x08, 0xee, 0xa2, 0xfa, 0xbf, 0x57, 0xbc, 0xee, 0x59, 0xab, 0xf3, + 0xe5, 0xa7, 0x4e, 0x4f, 0x7a, 0xf5, 0x32, 0xee, 0x01, 0x15, 0x3a, 0x40, 0x38, 0xaf, 0xfb, 0x0d, 0xdb, 0x7d, 0xf3, + 0xcb, 0xbb, 0xa3, 0x97, 0x81, 0x4d, 0x8a, 0xc4, 0xb6, 0x92, 0xcf, 0x7a, 0xa0, 0xf0, 0xeb, 0xb1, 0x5e, 0x5d, 0xac, + 0x7b, 0xac, 0x87, 0x5a, 0x40, 0xf4, 0xb0, 0x00, 0xf5, 0x5f, 0xcf, 0x3e, 0x0d, 0x85, 0x83, 0x6c, 0x9c, 0x2a, 0x50, + 0x64, 0xc1, 0xaf, 0xc5, 0x68, 0x5d, 0x10, 0x20, 0xb2, 0x25, 0xa4, 0x55, 0x27, 0xb3, 0xc7, 0xa5, 0x96, 0x64, 0xf0, + 0x4d, 0x40, 0x66, 0x07, 0x56, 0x4e, 0x50, 0x3a, 0x6e, 0x0d, 0xb8, 0xb2, 0xc5, 0xa3, 0xdd, 0xfe, 0x34, 0xc8, 0xce, + 0x9a, 0x93, 0x46, 0xfb, 0xb0, 0x4f, 0xf3, 0x00, 0x81, 0x48, 0xa6, 0x22, 0xc8, 0x35, 0xf7, 0x96, 0xf4, 0xd1, 0xe1, + 0x9c, 0x17, 0xf2, 0xcf, 0xa9, 0xd4, 0x21, 0x0e, 0x25, 0xd6, 0x40, 0xa0, 0xf2, 0x0c, 0x55, 0x0e, 0x1b, 0xe4, 0xf8, + 0x67, 0x47, 0x32, 0x93, 0x98, 0x2c, 0x72, 0xb7, 0x66, 0x2a, 0xfc, 0x40, 0xf0, 0x31, 0xcb, 0x39, 0x70, 0x81, 0xcd, + 0xe6, 0xbe, 0x9a, 0xe2, 0xe2, 0x0a, 0xfc, 0x31, 0x85, 0x5f, 0xf1, 0x14, 0x76, 0xda, 0xfd, 0xba, 0xa8, 0x52, 0xd4, + 0x6d, 0x14, 0x16, 0x95, 0x2c, 0x98, 0xd6, 0x90, 0x26, 0x3a, 0x8c, 0xfe, 0x20, 0x67, 0xa0, 0x20, 0xe4, 0x97, 0x4d, + 0x03, 0x8c, 0x54, 0x72, 0x79, 0x50, 0x25, 0x81, 0x17, 0x60, 0x1b, 0x54, 0x6c, 0x5d, 0x40, 0x90, 0x6d, 0x52, 0x94, + 0xe9, 0x97, 0x22, 0xaf, 0xc3, 0x2c, 0xa8, 0x46, 0x69, 0xf5, 0xa3, 0xfe, 0x09, 0xcc, 0xdb, 0x54, 0x8c, 0x6a, 0x15, + 0x93, 0xdf, 0xe8, 0xf7, 0x8b, 0x41, 0xeb, 0x43, 0x06, 0x1f, 0xbd, 0x36, 0x0d, 0xfe, 0xe8, 0x34, 0xd8, 0x61, 0xa2, + 0x11, 0x00, 0xc9, 0x9c, 0x5a, 0xf2, 0x50, 0xf4, 0x47, 0x90, 0x63, 0x8d, 0x2a, 0xa7, 0x60, 0xb0, 0xfe, 0xe3, 0xd1, + 0x0e, 0x4c, 0xbd, 0x38, 0xda, 0x92, 0x1d, 0xb4, 0xf2, 0x0d, 0x70, 0xbf, 0x46, 0xb6, 0x98, 0xe5, 0x00, 0xcd, 0x5e, + 0x23, 0x32, 0x3e, 0x79, 0x01, 0x8c, 0xd9, 0x3a, 0x0b, 0x23, 0x11, 0x07, 0x63, 0xd5, 0x98, 0x31, 0x03, 0x03, 0x17, + 0xe8, 0x5a, 0x26, 0x25, 0x69, 0x48, 0x07, 0x03, 0x56, 0xca, 0x16, 0x0e, 0x78, 0xd1, 0x1c, 0xb7, 0xe3, 0x75, 0x8b, + 0xc6, 0x03, 0xdb, 0xc5, 0xf6, 0xf7, 0xdf, 0x17, 0xdb, 0xb7, 0xe1, 0x96, 0xf4, 0x0a, 0x39, 0x4b, 0xe8, 0xe7, 0x8f, + 0xb2, 0xcf, 0x1a, 0x4e, 0x4e, 0x85, 0x66, 0x68, 0x29, 0x12, 0x4a, 0xf1, 0x4e, 0x4f, 0x0a, 0x8c, 0x65, 0x2c, 0xfc, + 0x3d, 0x70, 0x4e, 0x17, 0x8a, 0xc8, 0x1d, 0x38, 0x8e, 0x6f, 0xa0, 0x82, 0x51, 0xc3, 0xc1, 0xcb, 0x18, 0xb6, 0x45, + 0x31, 0x0b, 0x09, 0xa7, 0x10, 0x2e, 0x56, 0x59, 0xbf, 0x2f, 0x7f, 0x51, 0x17, 0x5d, 0x64, 0xb2, 0xee, 0x93, 0x70, + 0x64, 0xc6, 0x72, 0xea, 0x85, 0xe4, 0x79, 0xcf, 0x93, 0x69, 0xf2, 0x2c, 0x0f, 0x22, 0x80, 0x7c, 0x0e, 0xef, 0xc3, + 0x34, 0x03, 0xab, 0x34, 0x29, 0x3f, 0x42, 0xe9, 0x8b, 0xcf, 0x2b, 0x3f, 0xd0, 0xd9, 0x73, 0x93, 0x0c, 0x6f, 0x56, + 0xad, 0x37, 0xa9, 0x75, 0x5d, 0x3c, 0xe0, 0x5f, 0x9c, 0xc1, 0xc6, 0xb9, 0xce, 0x04, 0x07, 0x5e, 0x24, 0xb5, 0x5e, + 0x33, 0x7e, 0x9d, 0xe1, 0xba, 0x54, 0x6d, 0xf4, 0x51, 0x88, 0xce, 0x21, 0x53, 0x01, 0x0a, 0x45, 0xda, 0x3f, 0x28, + 0xb5, 0x32, 0xa9, 0xb4, 0x91, 0x00, 0xba, 0x87, 0x49, 0x83, 0x2d, 0x86, 0x32, 0x96, 0x26, 0x51, 0xee, 0x34, 0x88, + 0x2b, 0xfb, 0x73, 0x25, 0x71, 0x68, 0x59, 0x24, 0xff, 0xde, 0xf5, 0xf4, 0x15, 0x52, 0x77, 0xb2, 0x40, 0x66, 0x8c, + 0x17, 0x79, 0xfc, 0x09, 0x08, 0xb3, 0x41, 0x1b, 0x15, 0x85, 0x10, 0xb2, 0x41, 0x0c, 0x1a, 0x2f, 0xf2, 0xf8, 0x7b, + 0x45, 0xe3, 0x21, 0x1f, 0x45, 0xbe, 0xfa, 0xab, 0xd4, 0x7f, 0x85, 0x3e, 0x33, 0xc1, 0x23, 0x54, 0x13, 0xfd, 0xbb, + 0xe7, 0xb3, 0x7b, 0x50, 0x1b, 0x46, 0x61, 0x66, 0xca, 0xaf, 0x7c, 0x53, 0x9c, 0xbd, 0xfe, 0x8a, 0xae, 0xb2, 0xad, + 0xfb, 0xd1, 0xc7, 0x23, 0x02, 0x6b, 0x63, 0x74, 0xc5, 0x8d, 0x01, 0xe4, 0x30, 0x79, 0xbf, 0xa2, 0xb4, 0x1c, 0xd2, + 0x20, 0x74, 0xd0, 0x10, 0xf4, 0x4a, 0xa2, 0x0f, 0x24, 0x16, 0x31, 0x86, 0x17, 0xe2, 0x19, 0xa9, 0xc9, 0x44, 0x43, + 0xbc, 0x22, 0xf6, 0x43, 0xb4, 0xe4, 0xd4, 0x44, 0x37, 0xc2, 0x14, 0x03, 0x89, 0x9d, 0x41, 0x72, 0x92, 0xd4, 0xca, + 0x2f, 0x9e, 0x49, 0xc2, 0x12, 0x3b, 0x0f, 0x31, 0x98, 0xd4, 0xd2, 0x9d, 0xde, 0x54, 0xe9, 0xdd, 0x91, 0x96, 0x83, + 0xf6, 0x01, 0xd8, 0xa5, 0xa4, 0xf7, 0x4f, 0x0a, 0x45, 0x7c, 0x08, 0xe3, 0x18, 0xc2, 0xb7, 0x88, 0xea, 0x0a, 0x9c, + 0x6b, 0x05, 0x1a, 0xab, 0x81, 0x87, 0x66, 0x56, 0xcd, 0x87, 0x9c, 0x7e, 0x2a, 0x2d, 0x7f, 0x8c, 0x68, 0x6c, 0xb4, + 0x6e, 0x0e, 0x87, 0x3d, 0xad, 0x7a, 0xe9, 0x1c, 0x74, 0xd9, 0x4c, 0x62, 0xe2, 0x06, 0xd2, 0xf5, 0xa3, 0xdf, 0x4c, + 0xd8, 0x8b, 0xa8, 0x90, 0x4b, 0x21, 0x28, 0x68, 0x75, 0x20, 0x70, 0x28, 0xbc, 0x45, 0x99, 0x2f, 0x62, 0xda, 0x40, + 0x18, 0x7c, 0x7e, 0x20, 0x3f, 0xdf, 0x14, 0xa4, 0x62, 0xc7, 0xba, 0xf6, 0xfb, 0x9b, 0xd2, 0x03, 0x3c, 0x39, 0x93, + 0xe4, 0x69, 0x33, 0x84, 0x15, 0x01, 0x34, 0x66, 0x35, 0x59, 0x9c, 0x70, 0x65, 0x0e, 0x3f, 0x56, 0x5e, 0xc9, 0x52, + 0xa6, 0xce, 0x53, 0xbd, 0x00, 0xa2, 0x8e, 0x37, 0x68, 0x45, 0xea, 0x57, 0xe8, 0xec, 0x35, 0x2b, 0x21, 0xe3, 0xe1, + 0x39, 0xe7, 0xe9, 0xe8, 0x81, 0x25, 0x3c, 0xc2, 0xbf, 0x92, 0x89, 0x3e, 0xfc, 0x1e, 0x38, 0xdc, 0x8c, 0x13, 0x1e, + 0xb9, 0xcd, 0xde, 0x57, 0xe1, 0x0a, 0x6e, 0xa6, 0x05, 0x20, 0xb9, 0x05, 0x49, 0x13, 0x50, 0x42, 0x22, 0x13, 0x32, + 0x6b, 0x4a, 0x7e, 0x6e, 0x69, 0x1b, 0xac, 0x61, 0xd2, 0x79, 0xc0, 0x8b, 0x56, 0x1f, 0xad, 0x26, 0xda, 0x65, 0x96, + 0xcf, 0x87, 0x38, 0x43, 0x35, 0xc7, 0xdd, 0x19, 0xfc, 0x1c, 0xf0, 0x8a, 0x55, 0x4d, 0x3a, 0xda, 0x0d, 0xb8, 0xf0, + 0xe4, 0x3a, 0x4f, 0x47, 0x5b, 0xfc, 0x25, 0xf7, 0x07, 0x80, 0x0e, 0xa6, 0x2e, 0x81, 0x3f, 0x55, 0x5b, 0x4d, 0xa5, + 0x7e, 0x69, 0xed, 0xd7, 0x75, 0x67, 0xb5, 0x72, 0xcf, 0xba, 0x0c, 0xed, 0x91, 0x21, 0x67, 0xcc, 0x80, 0x3f, 0x67, + 0x2c, 0xf9, 0x73, 0xc6, 0x8a, 0x3f, 0x67, 0xdc, 0x18, 0x19, 0x40, 0x09, 0xee, 0x25, 0xbf, 0xde, 0x23, 0x66, 0x88, + 0xd5, 0xa0, 0x12, 0x58, 0x59, 0xca, 0xb9, 0x8f, 0x9c, 0x62, 0xca, 0x29, 0xc3, 0x4b, 0xa7, 0x33, 0x77, 0x20, 0xe7, + 0xc1, 0xcc, 0x1d, 0x26, 0x67, 0x7d, 0x8a, 0x63, 0x69, 0x4c, 0x8a, 0x0a, 0xd2, 0x39, 0x1d, 0x6e, 0x5e, 0x1d, 0xe7, + 0x09, 0xcb, 0xf8, 0xb8, 0x7d, 0xa6, 0x40, 0x88, 0x2d, 0x9e, 0x21, 0x91, 0x52, 0x35, 0xcb, 0x6d, 0xfe, 0x70, 0xa8, + 0x47, 0x0f, 0x7a, 0xa7, 0x87, 0x5f, 0x09, 0xfb, 0x25, 0xf3, 0xec, 0x13, 0x04, 0x30, 0x49, 0xe4, 0x99, 0x84, 0xa3, + 0x1f, 0xcb, 0xd1, 0xdf, 0x34, 0xfc, 0x5d, 0x86, 0xea, 0xee, 0x10, 0x98, 0xd8, 0xb2, 0x03, 0x87, 0xe0, 0x74, 0x55, + 0x89, 0x04, 0x1c, 0x6c, 0x36, 0x2c, 0xd2, 0x7b, 0x3c, 0xc4, 0xf9, 0xa0, 0xf0, 0x11, 0x1a, 0x66, 0xf4, 0x7e, 0x7f, + 0x23, 0xbc, 0x4a, 0xb6, 0xf2, 0x70, 0x48, 0xac, 0xbb, 0xb0, 0xa3, 0x8f, 0xa3, 0x3d, 0x4a, 0xa8, 0xfd, 0xa8, 0xd6, + 0x9b, 0x4a, 0x3d, 0xc8, 0xcd, 0x2e, 0x24, 0x06, 0x15, 0x4b, 0xf5, 0xe9, 0x95, 0xea, 0x43, 0xcd, 0x3a, 0xbf, 0xab, + 0xe3, 0x3e, 0x15, 0xa3, 0xb5, 0x9c, 0x10, 0xe0, 0x3a, 0x48, 0x34, 0x3a, 0x00, 0xc6, 0xd9, 0x66, 0xcb, 0x4b, 0x6d, + 0x9d, 0x28, 0x1d, 0xc7, 0xb9, 0x3e, 0x8e, 0x0f, 0x07, 0x29, 0x66, 0x5c, 0x1e, 0x89, 0x19, 0x97, 0x0d, 0xc0, 0x9b, + 0x75, 0x1e, 0xd4, 0x87, 0xc3, 0x25, 0x5d, 0x8a, 0x4c, 0x67, 0x1b, 0xe5, 0x67, 0x3d, 0x7a, 0x78, 0x96, 0xa0, 0xb9, + 0xb7, 0xc2, 0xde, 0x8b, 0x64, 0x7b, 0x26, 0xeb, 0xd4, 0xcb, 0xc8, 0xa7, 0x17, 0xee, 0xd9, 0x25, 0x57, 0x3f, 0xac, + 0xbe, 0x9e, 0xfe, 0x2a, 0xbc, 0x88, 0x55, 0xb4, 0x5b, 0x97, 0x4c, 0xd8, 0x5b, 0x4a, 0x25, 0xad, 0xf2, 0xf2, 0xe9, + 0xc6, 0x0f, 0x30, 0x33, 0xed, 0xe9, 0x83, 0x6c, 0x44, 0xf5, 0x67, 0x25, 0x6a, 0x65, 0x98, 0x2c, 0x9c, 0x97, 0x4c, + 0x3d, 0x19, 0xf0, 0x98, 0x95, 0x3c, 0x92, 0x9d, 0xde, 0x18, 0x04, 0x01, 0xac, 0x73, 0xd2, 0xaa, 0x33, 0x8e, 0x46, + 0xab, 0xca, 0xc5, 0xe9, 0x2a, 0x17, 0x18, 0x6e, 0xb7, 0x66, 0x1b, 0x55, 0x67, 0xb9, 0xa9, 0x55, 0xca, 0x77, 0x00, + 0x1f, 0xcb, 0x2a, 0x17, 0x74, 0x4c, 0x99, 0x3a, 0x6f, 0x20, 0x18, 0x5b, 0xd5, 0xb8, 0x70, 0x6a, 0x5c, 0xf0, 0x88, + 0xda, 0xdd, 0x34, 0xf5, 0x68, 0x0b, 0x2c, 0xa5, 0xa3, 0x1d, 0x2f, 0x51, 0xa5, 0xf0, 0x77, 0xc1, 0xf7, 0x61, 0x1c, + 0x7f, 0x5f, 0x6c, 0xd5, 0x81, 0x78, 0x5b, 0x6c, 0x91, 0xf6, 0x45, 0xfe, 0x85, 0x38, 0xe0, 0xb5, 0xae, 0x29, 0xaf, + 0xad, 0x39, 0x0d, 0x6c, 0x0d, 0x23, 0x25, 0x85, 0x73, 0xf3, 0xe7, 0xe1, 0x40, 0x2b, 0xbb, 0x56, 0x77, 0x85, 0x5a, + 0x8f, 0x39, 0x6c, 0xd8, 0x8b, 0x2c, 0xdc, 0x89, 0x12, 0x1c, 0xb9, 0xe4, 0x5f, 0x87, 0x83, 0x56, 0x59, 0xaa, 0x23, + 0x7d, 0xb6, 0xff, 0x12, 0x8c, 0x19, 0xba, 0x34, 0x01, 0xcb, 0xc6, 0x48, 0xfe, 0xd5, 0x34, 0xf3, 0x86, 0xc9, 0x9a, + 0x29, 0x1c, 0x87, 0x86, 0x11, 0xd2, 0x80, 0x6e, 0x83, 0xda, 0xf0, 0x64, 0xbe, 0xa9, 0xca, 0xaf, 0xee, 0x48, 0xb5, + 0x1f, 0x0c, 0x2f, 0x27, 0xe2, 0x9c, 0x2e, 0x49, 0xea, 0xa9, 0x84, 0x92, 0x10, 0xec, 0xd2, 0x07, 0x72, 0x62, 0x05, + 0x64, 0x2d, 0x63, 0xf9, 0xad, 0x1e, 0x10, 0xfa, 0x4f, 0xbb, 0xf5, 0x42, 0xff, 0x69, 0x9a, 0x2d, 0xd4, 0xf5, 0x87, + 0xc9, 0x7d, 0x47, 0xaf, 0x3f, 0x38, 0xbc, 0x53, 0x57, 0x15, 0x57, 0xf1, 0xb0, 0x36, 0x4c, 0x72, 0xa3, 0x2c, 0xdc, + 0x15, 0x9b, 0x5a, 0x2d, 0x4f, 0xc7, 0x61, 0x04, 0x66, 0x04, 0x05, 0xc8, 0xba, 0x6e, 0x23, 0x62, 0x58, 0xc9, 0x65, + 0x42, 0x3e, 0x21, 0x20, 0x8b, 0x52, 0xe3, 0x7c, 0xdc, 0x02, 0x95, 0x08, 0x06, 0xa7, 0xa1, 0xb5, 0xea, 0x26, 0x3f, + 0xaa, 0x6c, 0xec, 0x0e, 0xc8, 0x21, 0xc9, 0x64, 0x71, 0x37, 0xba, 0x15, 0xcb, 0xa2, 0x14, 0x3f, 0x63, 0x3d, 0x5c, + 0xb3, 0x85, 0xfb, 0x0c, 0x08, 0xed, 0x27, 0x4a, 0x7b, 0x13, 0x69, 0x82, 0xee, 0x3b, 0xb6, 0x02, 0x90, 0x01, 0x14, + 0x75, 0xb5, 0x5b, 0x9f, 0xf3, 0x73, 0x24, 0xcd, 0x70, 0x18, 0xdd, 0x3e, 0xbd, 0x0b, 0xee, 0x06, 0x97, 0xa8, 0x95, + 0xbe, 0x64, 0x71, 0x0b, 0x83, 0x6a, 0x6f, 0x96, 0x70, 0x50, 0x33, 0x6b, 0x6d, 0x04, 0x82, 0xc9, 0x1e, 0x0a, 0x2a, + 0xe6, 0x0a, 0xf6, 0x41, 0xc1, 0x5a, 0xf2, 0x3a, 0x38, 0xdc, 0xda, 0x97, 0x95, 0xe2, 0xe2, 0xf9, 0x45, 0xd2, 0xba, + 0xb0, 0x94, 0x17, 0xcf, 0x1b, 0x30, 0xb8, 0x1c, 0x61, 0x53, 0x55, 0xfe, 0x64, 0x03, 0xa0, 0x5b, 0x21, 0x45, 0xbc, + 0x28, 0x85, 0x6d, 0x2b, 0x9f, 0x39, 0x61, 0x83, 0x0d, 0x7b, 0x80, 0x7b, 0x65, 0x50, 0x32, 0xb8, 0x10, 0xe3, 0x76, + 0xb3, 0x0b, 0x70, 0x05, 0x43, 0x61, 0x6c, 0xcd, 0x5f, 0x67, 0x5e, 0xa4, 0x04, 0xdc, 0x0c, 0x51, 0xbe, 0x36, 0x70, + 0x32, 0xe9, 0xc9, 0xb5, 0x64, 0x31, 0x60, 0x41, 0x83, 0xef, 0xa8, 0xf5, 0x77, 0x26, 0xff, 0xc6, 0xd3, 0x43, 0x3f, + 0xf8, 0x9c, 0x79, 0x4b, 0x9f, 0xbd, 0xae, 0x64, 0xb4, 0x26, 0x89, 0xf2, 0xea, 0xe1, 0x12, 0xe4, 0x86, 0xe5, 0xe8, + 0x81, 0x2d, 0x41, 0x9c, 0x58, 0x8e, 0x12, 0xca, 0xe8, 0x0a, 0xf7, 0x2a, 0xb3, 0x65, 0x22, 0x90, 0xe2, 0xc0, 0x52, + 0xca, 0xbd, 0xc5, 0x3a, 0x58, 0xe2, 0xfe, 0x44, 0x72, 0x01, 0x25, 0x0f, 0xa0, 0x5c, 0x29, 0x20, 0xe0, 0xd3, 0x01, + 0x94, 0x2f, 0xe5, 0x45, 0xf8, 0x13, 0x27, 0x6a, 0xb0, 0x1c, 0x3d, 0x34, 0xec, 0x47, 0x2f, 0xb4, 0xec, 0x0f, 0x77, + 0x5a, 0xd3, 0xb0, 0xe2, 0x77, 0x30, 0x2d, 0x26, 0x6e, 0x5f, 0xae, 0xec, 0xaa, 0xf8, 0x6c, 0xa5, 0xce, 0x6e, 0x6a, + 0x48, 0xc2, 0xbe, 0x22, 0xab, 0x00, 0x07, 0xab, 0x22, 0xee, 0x59, 0x96, 0xfb, 0x30, 0xfa, 0x73, 0x93, 0x96, 0xc2, + 0x42, 0x95, 0xf4, 0xf7, 0x4d, 0x29, 0x90, 0xca, 0x44, 0x27, 0x5a, 0x08, 0xae, 0xc0, 0x20, 0x70, 0x2f, 0xf2, 0x1a, + 0x00, 0x63, 0xc0, 0xa5, 0x40, 0x59, 0xb6, 0x25, 0x84, 0x54, 0xf7, 0x33, 0x50, 0xdb, 0x89, 0xfb, 0x34, 0x22, 0x6b, + 0x21, 0xfa, 0x2a, 0x18, 0x33, 0xe7, 0xa5, 0x74, 0x8b, 0x4d, 0x57, 0x9b, 0xd5, 0x0d, 0x3a, 0x97, 0xb6, 0xdc, 0xfc, + 0x84, 0x2d, 0xd6, 0x0a, 0x94, 0x4d, 0x48, 0xda, 0xce, 0x79, 0x8e, 0xb2, 0x09, 0x2d, 0xed, 0x3d, 0xf5, 0xa8, 0x50, + 0x9d, 0x6c, 0xbd, 0x54, 0x4d, 0x2d, 0xc2, 0x6a, 0x71, 0x51, 0xf9, 0x01, 0xe8, 0xa6, 0xd2, 0xea, 0x45, 0x5d, 0xa3, + 0x29, 0xd4, 0x6a, 0xe1, 0xb8, 0xd1, 0xce, 0xa6, 0xcb, 0xf4, 0x0e, 0x71, 0x56, 0xa5, 0x1d, 0xfa, 0xfb, 0x4c, 0xbb, + 0x5e, 0x76, 0xf4, 0x9b, 0x71, 0x75, 0x81, 0x0b, 0xb1, 0x01, 0x9f, 0x73, 0x7f, 0x79, 0xbd, 0xe7, 0x71, 0xcf, 0x3f, + 0x1c, 0x90, 0x3d, 0xa9, 0xfd, 0xa1, 0xfa, 0xd8, 0x15, 0x0c, 0x59, 0x18, 0xa5, 0xfe, 0x22, 0xe5, 0xbd, 0x27, 0x38, + 0xee, 0x9f, 0xab, 0x1e, 0xfb, 0x31, 0xe3, 0xfb, 0xba, 0xd8, 0x44, 0x09, 0x45, 0x35, 0xf4, 0x56, 0xc5, 0xa6, 0x12, + 0x71, 0xf1, 0x90, 0xf7, 0x18, 0x26, 0xc3, 0x58, 0xc8, 0x54, 0xf8, 0x53, 0xa6, 0x82, 0x47, 0x08, 0x25, 0x6e, 0xd6, + 0x3d, 0xd2, 0x6e, 0x42, 0x9c, 0x52, 0x2d, 0x4a, 0x99, 0x8c, 0x7f, 0xeb, 0x27, 0x50, 0x9e, 0x53, 0xb4, 0x4c, 0x3f, + 0x2a, 0x5c, 0xa6, 0x6f, 0xd6, 0xc7, 0xa5, 0x67, 0x22, 0xd4, 0x99, 0x8b, 0x4d, 0xad, 0xd3, 0x31, 0x76, 0x4a, 0xa7, + 0x36, 0xec, 0x4b, 0xa5, 0xb8, 0xac, 0x28, 0xfc, 0x1b, 0x89, 0xac, 0x7a, 0x46, 0x1c, 0xff, 0x57, 0xd6, 0x3e, 0xc3, + 0x2a, 0xf0, 0xcb, 0x40, 0xde, 0x2f, 0x00, 0x3e, 0xae, 0xeb, 0x32, 0xbd, 0xdd, 0x00, 0x6d, 0x08, 0x0d, 0x7f, 0xcf, + 0x47, 0x06, 0x4c, 0xf7, 0x11, 0xce, 0x90, 0x1e, 0xea, 0x9c, 0xd3, 0x59, 0x99, 0xce, 0xb9, 0x0a, 0x6b, 0x09, 0xf6, + 0x72, 0xd2, 0xe4, 0x72, 0x5d, 0x82, 0x9a, 0x09, 0xdc, 0x3e, 0xb4, 0x47, 0x84, 0x50, 0x9b, 0xb2, 0x9a, 0x5e, 0x42, + 0xcd, 0x3b, 0x39, 0xed, 0x68, 0x52, 0x82, 0xab, 0x86, 0xce, 0xca, 0xf5, 0x5f, 0x87, 0x43, 0xef, 0x36, 0x2b, 0xa2, + 0x3f, 0x7a, 0xe8, 0xef, 0xb8, 0xbd, 0x49, 0xbf, 0x40, 0xb4, 0x8c, 0xf5, 0x37, 0x64, 0x40, 0xc7, 0x93, 0xe1, 0x6d, + 0xb1, 0xed, 0xb1, 0x2f, 0xa8, 0xc1, 0xd2, 0xd7, 0x8f, 0x3f, 0x40, 0x42, 0xd5, 0xb5, 0x2f, 0x2c, 0x9e, 0x30, 0x4f, + 0x89, 0xb6, 0x85, 0x0f, 0x61, 0xa1, 0x5f, 0x20, 0x32, 0x12, 0xc2, 0x4d, 0x65, 0xf7, 0x28, 0x69, 0x17, 0xfa, 0xd2, + 0xd7, 0xb2, 0xaf, 0x7c, 0xe7, 0x02, 0x60, 0x65, 0x9f, 0xdb, 0x70, 0x4f, 0xfa, 0x53, 0xaa, 0x0f, 0xdb, 0xdf, 0x92, + 0x05, 0x14, 0x5a, 0x58, 0x4f, 0xe5, 0xec, 0x5c, 0x97, 0x3c, 0xcd, 0xa6, 0xfb, 0x35, 0xec, 0x51, 0xf7, 0xe8, 0x35, + 0x15, 0x9c, 0x5f, 0x9a, 0xd1, 0xfb, 0xa7, 0xa1, 0x50, 0x1d, 0x75, 0xee, 0x20, 0xeb, 0xd2, 0xba, 0xe4, 0xfc, 0x66, + 0xe5, 0x8e, 0xc2, 0xfc, 0x3e, 0x04, 0xcf, 0xb0, 0xee, 0xdd, 0xc5, 0x79, 0xef, 0xcf, 0xd6, 0x1c, 0xf9, 0x31, 0x9b, + 0xa5, 0x88, 0x45, 0x32, 0x07, 0xab, 0x1f, 0xfa, 0x79, 0xec, 0xb7, 0x41, 0x0e, 0xc7, 0x4d, 0x03, 0x3a, 0x6c, 0xc8, + 0xac, 0x7d, 0x89, 0xc0, 0xa9, 0x46, 0x90, 0xa6, 0x26, 0xa8, 0x59, 0x1e, 0x22, 0xb1, 0x5d, 0xca, 0xb6, 0x41, 0xae, + 0xbb, 0x60, 0x9a, 0x23, 0xed, 0x19, 0xbc, 0x6f, 0xd2, 0x24, 0x15, 0x9a, 0x45, 0xda, 0x2a, 0x19, 0xff, 0x8e, 0xb4, + 0x99, 0x92, 0x3d, 0xb6, 0x06, 0xde, 0x4b, 0x50, 0x4e, 0x86, 0x29, 0x86, 0xef, 0xf8, 0x7a, 0xe7, 0x31, 0xf7, 0x9c, + 0x63, 0xb6, 0x49, 0xd9, 0x11, 0x4c, 0x92, 0x8d, 0x6f, 0x28, 0xde, 0xf0, 0xfd, 0x6d, 0x25, 0x4a, 0x00, 0xbd, 0x2c, + 0xf8, 0xb5, 0xb4, 0xb9, 0x42, 0xb7, 0xbb, 0x77, 0x94, 0xc2, 0x2f, 0x79, 0x79, 0x38, 0x6c, 0x53, 0x2f, 0x84, 0xce, + 0x17, 0xf1, 0x3b, 0x30, 0x87, 0x31, 0xc4, 0x66, 0x04, 0x08, 0x73, 0x7c, 0x40, 0x1d, 0xac, 0x1f, 0x01, 0x68, 0x9c, + 0x40, 0x01, 0x46, 0x5f, 0x6d, 0x0b, 0xfa, 0x96, 0x17, 0x17, 0x11, 0xa2, 0x46, 0x01, 0x26, 0x4a, 0x9a, 0xc5, 0x30, + 0x1c, 0xe8, 0xfc, 0xbe, 0xb9, 0xad, 0x4b, 0x81, 0x43, 0xef, 0x58, 0x86, 0xff, 0xfe, 0x3f, 0xd6, 0x96, 0x56, 0x95, + 0xed, 0xd6, 0x38, 0xcd, 0xfc, 0x6f, 0xb7, 0x85, 0xbe, 0xff, 0x52, 0x28, 0x9e, 0x77, 0xbc, 0x6e, 0xbf, 0x83, 0xe8, + 0x7d, 0xdd, 0xca, 0xbb, 0x52, 0xbb, 0x61, 0xa6, 0xfc, 0x21, 0xcd, 0xe3, 0xe2, 0x61, 0x14, 0xb7, 0x8e, 0xbc, 0x49, + 0x7a, 0xce, 0xf9, 0xbb, 0xaa, 0xdf, 0xf7, 0xde, 0x01, 0x19, 0xef, 0x4b, 0x61, 0x1c, 0x31, 0x89, 0x83, 0x6f, 0x2f, + 0x46, 0xd1, 0xa6, 0x84, 0x0d, 0xb9, 0x7d, 0x5a, 0x82, 0x66, 0xa6, 0xdf, 0x47, 0x89, 0xd2, 0x9a, 0xef, 0x7f, 0x93, + 0xf3, 0xfd, 0xa5, 0x90, 0x37, 0x2b, 0xf9, 0xe1, 0xa3, 0x15, 0x06, 0xbe, 0xc7, 0xe9, 0x17, 0xd1, 0x63, 0x77, 0xa5, + 0x0f, 0xdf, 0x95, 0x96, 0x3e, 0xab, 0xa8, 0x7f, 0xa0, 0xa2, 0xe6, 0xa5, 0x18, 0x11, 0xf1, 0x20, 0x68, 0x67, 0xdb, + 0xa5, 0x76, 0x2d, 0x41, 0xbb, 0x60, 0x53, 0xd8, 0xbf, 0x1f, 0x1d, 0xf2, 0x7e, 0xff, 0x63, 0xee, 0xb5, 0x78, 0xdd, + 0x75, 0x68, 0xca, 0x4f, 0x85, 0x87, 0x10, 0xc0, 0x5a, 0x06, 0xca, 0x38, 0xc2, 0xa4, 0x8b, 0xbc, 0x46, 0xd9, 0x74, + 0x22, 0xf0, 0x31, 0xcb, 0xae, 0x9c, 0x64, 0x1a, 0x60, 0x46, 0x35, 0x85, 0x99, 0x00, 0x23, 0xf5, 0x11, 0xeb, 0xa6, + 0xa7, 0x55, 0x68, 0xf9, 0x1a, 0x82, 0x75, 0x91, 0x65, 0x1c, 0xc5, 0x4c, 0x00, 0xb0, 0xf9, 0x08, 0xf2, 0x15, 0x5d, + 0x1d, 0x92, 0x56, 0xaa, 0xbc, 0x5f, 0x67, 0x44, 0x46, 0x93, 0x10, 0xcd, 0x6f, 0xe1, 0x81, 0x7d, 0xdb, 0xcc, 0xa8, + 0x52, 0xcf, 0xa8, 0xca, 0x67, 0x38, 0x2c, 0x85, 0x63, 0xc4, 0xff, 0x7b, 0xaa, 0x7a, 0x44, 0xa0, 0x57, 0x65, 0x5a, + 0x45, 0x45, 0x9e, 0x8b, 0x08, 0x11, 0xaa, 0xa5, 0x73, 0x38, 0xf4, 0x63, 0xbf, 0x8f, 0x03, 0x61, 0x5e, 0xfc, 0xe9, + 0xb1, 0xae, 0xfc, 0xa9, 0xc0, 0xb5, 0x92, 0x02, 0xa7, 0xa2, 0x46, 0x88, 0x10, 0xde, 0x9f, 0xc0, 0xb3, 0x9a, 0xfa, + 0x7e, 0x63, 0x99, 0xe8, 0xfe, 0x99, 0x01, 0xe5, 0x0f, 0xc8, 0xd7, 0x95, 0x14, 0x67, 0xea, 0xe4, 0x31, 0x71, 0xc6, + 0x01, 0x88, 0xf9, 0xba, 0x44, 0xa3, 0xb1, 0xff, 0x01, 0x09, 0x86, 0xea, 0x07, 0x3b, 0xdd, 0xd4, 0xfb, 0x57, 0x26, + 0x71, 0x14, 0x7d, 0xda, 0x26, 0x8f, 0x25, 0x4b, 0xa3, 0x85, 0xa3, 0xf7, 0x88, 0x61, 0x1c, 0x4e, 0xe7, 0x63, 0x92, + 0x6d, 0x4c, 0x56, 0x01, 0xa4, 0x93, 0x99, 0x3a, 0xa6, 0xd4, 0xd1, 0x38, 0xd7, 0x0b, 0xaa, 0xd0, 0x63, 0x5d, 0xf2, + 0x1c, 0xac, 0x27, 0x3f, 0x78, 0xa5, 0x3f, 0x15, 0x72, 0x0e, 0x1b, 0x89, 0xa0, 0xf0, 0x03, 0x5c, 0x0d, 0x56, 0x0a, + 0x18, 0x4c, 0x7d, 0x0b, 0x5f, 0x13, 0xcf, 0x51, 0xf0, 0x28, 0xec, 0x62, 0x6c, 0xad, 0x7c, 0xe7, 0x93, 0x82, 0x72, + 0xcf, 0x8a, 0x39, 0xaf, 0x80, 0x73, 0x19, 0x14, 0xc2, 0x74, 0x3c, 0xcb, 0xff, 0x99, 0xe4, 0xf5, 0xc4, 0x86, 0x00, + 0x19, 0xfc, 0x29, 0x71, 0x5a, 0xba, 0x43, 0x77, 0x1e, 0x7a, 0x16, 0x71, 0xd8, 0xe8, 0xc9, 0xba, 0x2c, 0xb6, 0x29, + 0xea, 0x25, 0xcc, 0x0f, 0xe4, 0xe7, 0x2d, 0xf9, 0x3e, 0x44, 0xf1, 0x36, 0xf8, 0x35, 0x63, 0xb1, 0xc0, 0xbf, 0xfe, + 0x9e, 0x31, 0x9a, 0x68, 0xc1, 0xbf, 0xb3, 0x06, 0x89, 0x8a, 0x7f, 0xca, 0x26, 0x00, 0xeb, 0xc8, 0xd5, 0x87, 0x4f, + 0x89, 0xf1, 0xd6, 0x6c, 0x78, 0xe4, 0x9b, 0x15, 0xe8, 0xd4, 0xe7, 0xee, 0xca, 0xf6, 0x54, 0x35, 0xfe, 0x9e, 0xea, + 0x6a, 0xa4, 0xaa, 0x1a, 0x7f, 0x4f, 0xa9, 0x1a, 0xbf, 0x65, 0x14, 0xbf, 0x53, 0xf9, 0x0c, 0x99, 0x93, 0x4d, 0x4c, + 0xd2, 0xe9, 0x7b, 0xc3, 0x89, 0x5d, 0xf6, 0xab, 0xb7, 0x89, 0xcc, 0x44, 0x0a, 0xb9, 0x37, 0x00, 0x6d, 0xbf, 0xcb, + 0x0d, 0xa7, 0xc4, 0xf9, 0xb9, 0x87, 0x2b, 0x36, 0xad, 0x5e, 0xd2, 0x82, 0x05, 0x36, 0x2f, 0xb3, 0x3c, 0x45, 0x02, + 0xdb, 0xa6, 0xcc, 0xfa, 0x73, 0xee, 0x01, 0x04, 0x33, 0xa9, 0x09, 0x00, 0x69, 0x21, 0x2a, 0x85, 0xc8, 0x5f, 0xe2, + 0xac, 0x3e, 0xe7, 0xbd, 0x4d, 0x1e, 0x13, 0x69, 0x75, 0xaf, 0xdf, 0x4f, 0xcf, 0xd2, 0x9c, 0x82, 0x1a, 0x8e, 0xb3, + 0x4e, 0xbf, 0xcf, 0x82, 0x3a, 0x91, 0xab, 0xf4, 0x1f, 0x6e, 0x90, 0x97, 0xf1, 0x7d, 0xdd, 0xf6, 0xfc, 0x89, 0xfa, + 0x7b, 0x67, 0xfd, 0x6d, 0x81, 0xe0, 0x4e, 0x8e, 0xfd, 0x64, 0x55, 0xca, 0x13, 0xe3, 0xd2, 0xde, 0xf3, 0x9b, 0xba, + 0x28, 0xb2, 0x3a, 0x5d, 0x7f, 0x90, 0x7a, 0x1a, 0xdd, 0x17, 0x7b, 0x30, 0x06, 0xef, 0x00, 0xf0, 0x4c, 0x87, 0x06, + 0x48, 0xdf, 0x33, 0xf2, 0x70, 0x9f, 0x5b, 0xf2, 0x93, 0xca, 0xda, 0x24, 0x61, 0x45, 0xb1, 0x19, 0xc6, 0x08, 0x25, + 0xe3, 0x34, 0xb6, 0x7e, 0xbf, 0xaf, 0xfe, 0xde, 0x61, 0x14, 0x15, 0x15, 0x77, 0x8c, 0x46, 0x65, 0x55, 0x8f, 0xb6, + 0x83, 0xc3, 0xe1, 0x3c, 0xb7, 0x71, 0xb4, 0xf5, 0x0a, 0xd8, 0x5b, 0xa1, 0x52, 0xf6, 0x4a, 0x84, 0xe5, 0x87, 0x2b, + 0xbf, 0xdf, 0x87, 0x7f, 0x65, 0xa4, 0x85, 0xe7, 0x4f, 0xf1, 0xd7, 0xa2, 0x2e, 0x30, 0x3c, 0x83, 0xd6, 0x68, 0x05, + 0xc1, 0x04, 0xff, 0xe8, 0x40, 0xbd, 0xb4, 0xd2, 0x3e, 0x82, 0x6e, 0x05, 0x7a, 0x50, 0x0f, 0x7d, 0x9a, 0xb4, 0x2f, + 0x24, 0xea, 0xf6, 0x56, 0xa7, 0xd1, 0x1f, 0x15, 0x5c, 0x4e, 0x61, 0x72, 0xb8, 0xa1, 0x4f, 0xab, 0x70, 0xfb, 0x09, + 0x9e, 0xfe, 0x0c, 0x94, 0x5b, 0x87, 0x43, 0x0e, 0x62, 0x0b, 0xb8, 0x79, 0xac, 0xc2, 0xcf, 0x45, 0x29, 0x23, 0xea, + 0xe3, 0x69, 0x01, 0xda, 0xbb, 0x00, 0x1d, 0xb0, 0x34, 0x88, 0x57, 0x48, 0x9e, 0xb3, 0x11, 0xc0, 0xb2, 0x03, 0xcb, + 0x59, 0xc6, 0x29, 0xcc, 0xb3, 0xbc, 0x56, 0x2b, 0xed, 0xac, 0x4c, 0xbc, 0x9a, 0x65, 0xe0, 0x2c, 0x70, 0x51, 0xf9, + 0x2c, 0xd3, 0xaa, 0xa7, 0x2a, 0x41, 0x9f, 0x57, 0x72, 0x82, 0x2b, 0xc1, 0xc9, 0x06, 0xe4, 0x17, 0x20, 0x49, 0x53, + 0xca, 0x9a, 0xf2, 0xfa, 0x92, 0x6e, 0xc8, 0xe8, 0x39, 0xef, 0x79, 0xd1, 0x30, 0xf4, 0x2f, 0xbc, 0x12, 0xc2, 0x37, + 0x71, 0xdb, 0x46, 0x29, 0xec, 0x6f, 0x02, 0x8b, 0x4f, 0xd8, 0x0f, 0xde, 0xd2, 0x9f, 0x8e, 0x83, 0x70, 0x88, 0xdc, + 0x50, 0x31, 0x07, 0xf6, 0x34, 0x60, 0xb1, 0x89, 0xaf, 0x36, 0x93, 0x78, 0x30, 0xf0, 0x75, 0xc6, 0x62, 0x16, 0x03, + 0x0d, 0x72, 0x3c, 0xb8, 0x9c, 0xeb, 0x13, 0x42, 0x3f, 0x8c, 0xa8, 0x1c, 0x15, 0xe8, 0x1c, 0x44, 0x83, 0x25, 0xe0, + 0xa9, 0xb7, 0xb2, 0x41, 0x92, 0x31, 0xc9, 0x24, 0xae, 0x35, 0x49, 0x75, 0x38, 0xa1, 0x75, 0xa0, 0xe3, 0xea, 0x02, + 0x3a, 0x1f, 0xd7, 0xbd, 0x8f, 0x57, 0xc3, 0x05, 0x95, 0x7e, 0x21, 0x06, 0x5e, 0x3d, 0x1d, 0x07, 0x97, 0x74, 0x2b, + 0x5c, 0xac, 0xc2, 0xed, 0xcf, 0xf2, 0x81, 0xe3, 0x8e, 0x4a, 0x1a, 0x02, 0x83, 0xb7, 0x87, 0xee, 0x66, 0x86, 0x86, + 0x3a, 0x69, 0x1f, 0xc6, 0xa1, 0x1c, 0x62, 0xd5, 0x8a, 0x0b, 0xe9, 0x8d, 0xe0, 0xdb, 0x85, 0x62, 0x2c, 0x1b, 0xbb, + 0x34, 0x14, 0x85, 0xbf, 0x02, 0xd8, 0xa1, 0xf6, 0x57, 0x2a, 0xf9, 0x18, 0x19, 0xd5, 0x34, 0xd0, 0x31, 0x00, 0x4b, + 0x96, 0x26, 0x92, 0x2a, 0xd2, 0x48, 0xfc, 0x91, 0x19, 0xeb, 0xa8, 0xe9, 0xfa, 0x82, 0xa9, 0x6a, 0x91, 0x74, 0x3b, + 0x93, 0x58, 0x4e, 0x24, 0xa9, 0xed, 0x3e, 0x22, 0x06, 0x03, 0x1f, 0x6c, 0xc4, 0x34, 0x13, 0xe1, 0x88, 0x47, 0x25, + 0xb2, 0xe8, 0xf2, 0xdb, 0x28, 0x93, 0xb6, 0x2f, 0x2b, 0xb2, 0x05, 0xc1, 0xf4, 0x24, 0xfa, 0x20, 0x49, 0x39, 0x15, + 0x89, 0x34, 0x23, 0x04, 0xf8, 0xf1, 0xa4, 0xbc, 0xd2, 0x9f, 0x83, 0xa6, 0x95, 0xe0, 0x25, 0x83, 0xe4, 0x91, 0xf8, + 0x99, 0x14, 0xcc, 0x62, 0xac, 0x1a, 0x0c, 0xb0, 0x9c, 0xea, 0x99, 0x63, 0x92, 0xfe, 0x5b, 0xa7, 0x13, 0xf6, 0x0b, + 0x2f, 0xb7, 0xb5, 0xbc, 0x69, 0xee, 0xbd, 0xf0, 0x2a, 0x96, 0x6a, 0x58, 0x06, 0xfd, 0xd7, 0x44, 0xbb, 0x60, 0x6b, + 0xcb, 0x98, 0xb0, 0xea, 0x07, 0x90, 0xf6, 0x48, 0x97, 0x57, 0x0d, 0x73, 0x26, 0x78, 0x74, 0x61, 0xcd, 0x83, 0xe8, + 0x42, 0xf8, 0xc8, 0x65, 0x37, 0x49, 0xae, 0xc6, 0x13, 0x3f, 0x1c, 0x0c, 0x14, 0x00, 0x2d, 0xad, 0x93, 0x62, 0x10, + 0x3e, 0x13, 0x72, 0x20, 0x8d, 0x8e, 0xaa, 0x00, 0x8b, 0x65, 0x76, 0x55, 0x4e, 0xb2, 0xc1, 0xc0, 0x07, 0xb1, 0x31, + 0xb1, 0x1b, 0x9a, 0xcd, 0x7d, 0x76, 0xa2, 0x20, 0xab, 0xcd, 0x61, 0x6b, 0xa6, 0x5b, 0x60, 0x00, 0x30, 0x88, 0x08, + 0x96, 0xfb, 0xdc, 0xc8, 0x47, 0xd4, 0xe9, 0x29, 0x8c, 0x80, 0xe0, 0x97, 0x13, 0x81, 0xc8, 0x45, 0x02, 0xf5, 0x00, + 0x33, 0x01, 0x66, 0x54, 0x31, 0xbc, 0x04, 0x76, 0xf1, 0xdc, 0xbc, 0x62, 0xd0, 0xbf, 0x68, 0x92, 0x25, 0x9a, 0x4a, + 0x1c, 0x8d, 0x91, 0x53, 0x69, 0x8c, 0x0c, 0x88, 0x5d, 0x1c, 0xff, 0x9e, 0xd2, 0xa3, 0x20, 0x65, 0x9f, 0x2b, 0x43, + 0x1c, 0x8e, 0xe2, 0x2b, 0x58, 0x35, 0x0e, 0x87, 0xda, 0xbc, 0x9e, 0xce, 0xea, 0xf9, 0x40, 0x04, 0xf0, 0xdf, 0x50, + 0xb0, 0x5f, 0x34, 0x15, 0xb9, 0x41, 0xea, 0x3c, 0x1c, 0x52, 0x90, 0x4f, 0x75, 0x93, 0xbf, 0xaf, 0xdc, 0xfd, 0x74, + 0x36, 0xb7, 0xe6, 0xe8, 0x45, 0x8d, 0xeb, 0xd6, 0xea, 0x86, 0x42, 0xa2, 0x35, 0x4d, 0x8a, 0xab, 0x6a, 0x52, 0x0c, + 0x78, 0xee, 0x0b, 0xd5, 0xc5, 0xd6, 0x08, 0x16, 0xfe, 0xdc, 0x02, 0x61, 0x32, 0xee, 0xc5, 0x47, 0x0b, 0x39, 0xa5, + 0x5d, 0x5b, 0xed, 0xb6, 0x95, 0x0d, 0x29, 0x9a, 0x0f, 0x2f, 0x61, 0x97, 0x4e, 0x11, 0x6d, 0xbb, 0x24, 0xf8, 0x02, + 0xb4, 0xac, 0x2e, 0x44, 0x1e, 0xd3, 0xaf, 0x90, 0x5f, 0x8a, 0xe1, 0x5f, 0xa5, 0x7b, 0x73, 0x6a, 0x83, 0x1c, 0xc0, + 0x76, 0xef, 0xe1, 0x76, 0x8c, 0x1e, 0xc8, 0xe0, 0x8d, 0x90, 0x73, 0xce, 0x2f, 0xa7, 0xd6, 0x8c, 0x89, 0x86, 0x05, + 0x2b, 0x87, 0x91, 0x1f, 0x20, 0xe3, 0xe5, 0x14, 0x58, 0xd9, 0x8f, 0x8a, 0xb8, 0xf4, 0x87, 0x91, 0x7f, 0xf1, 0x3c, + 0xc8, 0xb8, 0x17, 0x0d, 0x3b, 0xbe, 0x00, 0x7b, 0xf5, 0xc5, 0x73, 0x16, 0x0d, 0x78, 0x75, 0x55, 0x4f, 0xb3, 0x60, + 0x98, 0xb1, 0xe8, 0xaa, 0x18, 0x82, 0x0f, 0xed, 0x75, 0x39, 0x08, 0x7d, 0xdf, 0xec, 0x1c, 0xba, 0x1b, 0x12, 0x79, + 0x84, 0xfd, 0x08, 0x6e, 0xbb, 0x5a, 0x62, 0x06, 0x93, 0xcd, 0x5d, 0xc4, 0x0c, 0xb6, 0xfc, 0xc5, 0x73, 0xc3, 0x25, + 0x54, 0x5d, 0x4b, 0xcd, 0x46, 0x81, 0xe6, 0xe4, 0x0a, 0xcd, 0xc9, 0x4a, 0xa8, 0x25, 0x9f, 0x54, 0x38, 0x61, 0xe7, + 0x93, 0x5c, 0xd9, 0x8d, 0xc6, 0x18, 0xb8, 0x68, 0xcf, 0x6d, 0x61, 0x64, 0xa6, 0xb3, 0x14, 0x0d, 0x58, 0x78, 0x26, + 0x4e, 0x69, 0x0c, 0x68, 0x5f, 0x0e, 0x2c, 0x6d, 0xc8, 0x8f, 0x72, 0x66, 0xa0, 0x6d, 0x48, 0x69, 0xd4, 0x0c, 0xfc, + 0x99, 0x9a, 0x30, 0xbf, 0x82, 0x95, 0x08, 0xa2, 0xba, 0x00, 0x93, 0x24, 0x27, 0xa3, 0x91, 0xb2, 0x12, 0xc9, 0x39, + 0xe0, 0x7d, 0x04, 0x4f, 0x16, 0xb1, 0xad, 0xfd, 0x29, 0xfd, 0xaf, 0x0e, 0x9f, 0x4b, 0xff, 0x99, 0x00, 0x16, 0x72, + 0x69, 0x10, 0x19, 0x28, 0x1c, 0x52, 0x53, 0x89, 0x38, 0x71, 0x3c, 0x03, 0x5f, 0xc3, 0x05, 0x9a, 0x02, 0xfa, 0x83, + 0x9a, 0x51, 0x44, 0x16, 0xfe, 0xea, 0xd9, 0x4d, 0xdd, 0xe8, 0x79, 0xe6, 0xbc, 0x06, 0xcd, 0x0c, 0x84, 0xf4, 0x38, + 0x55, 0x6f, 0x43, 0xa2, 0xf3, 0xf2, 0x52, 0xbf, 0x4c, 0x88, 0x64, 0x45, 0xe4, 0xe9, 0xfb, 0x1c, 0xcc, 0x23, 0x8a, + 0xd0, 0xc1, 0x95, 0x79, 0x38, 0x9c, 0x0b, 0x0a, 0xdf, 0x51, 0x9e, 0x0f, 0x38, 0xcd, 0xa2, 0x04, 0xb4, 0x81, 0x2c, + 0x37, 0x65, 0xae, 0x93, 0x96, 0xa9, 0x7b, 0x0f, 0x56, 0x82, 0x0a, 0xdd, 0x9c, 0x82, 0x42, 0x19, 0x09, 0x4a, 0x69, + 0x35, 0x08, 0xa5, 0x3a, 0x2c, 0x82, 0xc8, 0x21, 0x0b, 0x01, 0x37, 0x53, 0xd1, 0x68, 0x49, 0xc3, 0x23, 0x9c, 0x1b, + 0x28, 0x04, 0x20, 0xb1, 0xa7, 0x8a, 0x32, 0x2e, 0x87, 0x80, 0x8f, 0x12, 0x0e, 0x71, 0xd6, 0xa4, 0x2d, 0xcf, 0x41, + 0x1c, 0xcb, 0x25, 0x5f, 0x57, 0x08, 0x06, 0x11, 0xfa, 0x0c, 0xf9, 0x93, 0xe5, 0xfc, 0xbb, 0x75, 0x98, 0x76, 0x84, + 0x0f, 0xbb, 0xda, 0x82, 0x8b, 0xd9, 0xed, 0x7c, 0x02, 0xf1, 0x2d, 0xb7, 0xf3, 0x63, 0x0c, 0x91, 0x85, 0x3f, 0xb8, + 0x1b, 0x4a, 0xae, 0x28, 0x74, 0x59, 0x8f, 0x48, 0x91, 0x3d, 0x5d, 0x73, 0x04, 0xc1, 0x81, 0x56, 0x0d, 0x32, 0x34, + 0x12, 0x5f, 0x3c, 0x87, 0xac, 0xc1, 0x9a, 0x7f, 0xae, 0xc8, 0x59, 0xdd, 0x9f, 0x6c, 0xa0, 0x9a, 0x64, 0xb2, 0x56, + 0x54, 0xce, 0x5f, 0xaf, 0xca, 0xf2, 0x64, 0x55, 0x86, 0xab, 0x41, 0x57, 0x55, 0x96, 0x1c, 0xa9, 0x0d, 0xd0, 0x9a, + 0xae, 0x10, 0x43, 0x21, 0x6b, 0xb0, 0xb4, 0xaa, 0xb2, 0xa6, 0x3e, 0x81, 0x40, 0x1f, 0x60, 0x19, 0x35, 0xfb, 0xe9, + 0xf0, 0x5f, 0xc1, 0xbf, 0x54, 0xc8, 0x52, 0x9d, 0xd6, 0x99, 0xf8, 0x35, 0x58, 0x32, 0xfc, 0xe3, 0xb7, 0x60, 0x0d, + 0x58, 0x02, 0x64, 0xb9, 0xdb, 0xd8, 0x68, 0xbd, 0xf2, 0x0a, 0xf1, 0xa5, 0xd6, 0x17, 0xfd, 0xd6, 0x6d, 0xa2, 0x56, + 0x80, 0x11, 0x0a, 0x2d, 0x02, 0x6c, 0xf5, 0xc0, 0x3d, 0x05, 0x3f, 0x10, 0xc3, 0xb9, 0x26, 0xad, 0xa9, 0x13, 0x5e, + 0x67, 0xe3, 0x48, 0x44, 0xf5, 0x16, 0x2e, 0xee, 0xf5, 0xd6, 0xe2, 0x6f, 0x54, 0x20, 0x00, 0xb2, 0x98, 0x62, 0xed, + 0xbc, 0x21, 0xbd, 0x32, 0xec, 0x24, 0xf4, 0xde, 0xb0, 0x13, 0xc8, 0x8b, 0xc3, 0x4e, 0xa1, 0x4b, 0xb4, 0x9d, 0x22, + 0x35, 0xd1, 0x76, 0xd2, 0x62, 0x15, 0x96, 0x10, 0xfc, 0xaa, 0xbd, 0x75, 0x94, 0xed, 0x8b, 0x2c, 0x61, 0xda, 0x02, + 0x46, 0xb9, 0x55, 0x9f, 0x39, 0x45, 0xac, 0x94, 0xbd, 0xd3, 0x49, 0x95, 0xbb, 0xc8, 0xa7, 0x56, 0x53, 0x64, 0xf2, + 0xf7, 0xc7, 0x2d, 0x92, 0x4f, 0x7e, 0x6e, 0x37, 0x4c, 0xa6, 0x7f, 0x3c, 0xfa, 0x02, 0xba, 0x22, 0x3b, 0x7d, 0x02, + 0x01, 0x99, 0x0a, 0xaa, 0xd5, 0xad, 0x62, 0x9a, 0xb7, 0xab, 0xec, 0xf6, 0x42, 0x89, 0xe1, 0x74, 0x76, 0x12, 0x1e, + 0x6d, 0x86, 0x0c, 0x1c, 0x82, 0x40, 0x21, 0x54, 0x14, 0xc3, 0x23, 0x50, 0x6b, 0x24, 0x1f, 0xe0, 0x47, 0xbb, 0x53, + 0x41, 0xa4, 0x76, 0x53, 0x71, 0xe3, 0xe4, 0xa6, 0xeb, 0xa5, 0x40, 0xad, 0x53, 0xb2, 0x02, 0x28, 0x21, 0xea, 0x4f, + 0x62, 0x5b, 0xbf, 0x84, 0x2b, 0x36, 0xdf, 0x37, 0x8a, 0x9e, 0x5c, 0x9f, 0xa2, 0x6e, 0xc5, 0xd5, 0x69, 0xda, 0x6a, + 0x8e, 0x1d, 0x67, 0xc8, 0xc1, 0xb3, 0x82, 0x60, 0x3b, 0x2a, 0x51, 0xbe, 0x6d, 0x37, 0x1d, 0x13, 0x5b, 0xfd, 0xb3, + 0xa8, 0x36, 0x77, 0x50, 0x11, 0x11, 0x1f, 0x65, 0x37, 0x4f, 0xda, 0xef, 0x60, 0x8f, 0xb5, 0x1a, 0x44, 0xf6, 0x19, + 0x5c, 0xe5, 0x3a, 0x2d, 0x72, 0x5b, 0x06, 0xe7, 0x1f, 0x5e, 0xed, 0x2a, 0x6c, 0x72, 0xac, 0xab, 0xab, 0x99, 0xea, + 0xa4, 0x62, 0x03, 0x63, 0x4d, 0x6b, 0xa9, 0xe6, 0x31, 0x24, 0xdd, 0x95, 0xc5, 0x59, 0x95, 0x74, 0xd3, 0x73, 0xe3, + 0x4c, 0x21, 0x06, 0xce, 0x56, 0xa3, 0xe5, 0x0c, 0x43, 0x74, 0x7d, 0x98, 0x25, 0x7e, 0xab, 0xa7, 0xdc, 0xe7, 0xe1, + 0xd6, 0xef, 0xea, 0x05, 0x27, 0x93, 0xfd, 0xe4, 0x38, 0x77, 0xbb, 0x48, 0xfb, 0x89, 0x6f, 0xc3, 0xfc, 0xeb, 0x1b, + 0xc4, 0x9d, 0xa8, 0xff, 0x59, 0x01, 0xd0, 0xe0, 0x26, 0x8f, 0x25, 0x4a, 0xfd, 0x5e, 0x55, 0x3f, 0xa8, 0x99, 0xaa, + 0x69, 0x20, 0x98, 0x53, 0x29, 0xe0, 0x0f, 0xb7, 0x0b, 0x57, 0x3c, 0xe2, 0x86, 0x85, 0xf1, 0x4f, 0xaf, 0x66, 0xa7, + 0x82, 0xca, 0xc0, 0xcd, 0xf8, 0x4f, 0x4f, 0xb0, 0x53, 0x58, 0x2b, 0x20, 0x2b, 0xfc, 0xe9, 0xe5, 0x8f, 0xbc, 0x5f, + 0xf1, 0x3f, 0xbd, 0xea, 0x91, 0xf7, 0x11, 0xe7, 0xe5, 0x4f, 0x24, 0x75, 0x42, 0x54, 0x97, 0x3f, 0x09, 0x53, 0x6c, + 0x95, 0xe6, 0xaf, 0x48, 0xe1, 0x13, 0x7c, 0x06, 0xbe, 0xc3, 0x55, 0xb8, 0x35, 0xbf, 0xc1, 0x63, 0xc7, 0x62, 0xdb, + 0xa5, 0xbe, 0x80, 0x72, 0x04, 0x16, 0x91, 0xdb, 0x6f, 0x57, 0xf6, 0xab, 0x85, 0x51, 0xc6, 0xd8, 0x7d, 0xc9, 0x4a, + 0x94, 0xce, 0xfa, 0xfd, 0x42, 0x0a, 0x46, 0x76, 0x61, 0x8d, 0xf6, 0x28, 0x55, 0xaf, 0xbe, 0x0d, 0xeb, 0x28, 0x49, + 0xf3, 0x3b, 0x19, 0x7d, 0x24, 0xc3, 0x8e, 0xf4, 0x95, 0x94, 0x68, 0xaf, 0x55, 0x58, 0x8e, 0x66, 0xbf, 0x2e, 0x39, + 0x50, 0x5e, 0xb7, 0x82, 0xf2, 0x55, 0x13, 0x40, 0xaf, 0x54, 0xfb, 0x0c, 0xb4, 0x82, 0xc2, 0x52, 0x79, 0xb0, 0x12, + 0xe7, 0xa2, 0xcf, 0x8a, 0xc3, 0x41, 0x5d, 0x0c, 0x09, 0x05, 0xaa, 0xc4, 0x49, 0x68, 0xc4, 0x73, 0xb8, 0x10, 0x8a, + 0xeb, 0x1c, 0x63, 0x2b, 0x72, 0xe0, 0x40, 0x86, 0x1f, 0x10, 0x78, 0x2f, 0xfb, 0x57, 0x30, 0x18, 0x26, 0xb8, 0x91, + 0x51, 0x27, 0xe7, 0xec, 0x4f, 0x0c, 0xcc, 0xa0, 0x9e, 0xd4, 0xee, 0xb3, 0x7b, 0x15, 0xd8, 0x0b, 0x67, 0x40, 0x7b, + 0x37, 0x46, 0x3f, 0xab, 0x62, 0xed, 0xa4, 0x7f, 0x2a, 0xd6, 0x90, 0x4c, 0x87, 0xc5, 0xd1, 0x36, 0x0d, 0x8f, 0xe4, + 0xc9, 0x71, 0xbc, 0xe9, 0x1f, 0x0e, 0x63, 0xfc, 0x38, 0xca, 0xaf, 0x2d, 0xe0, 0x55, 0xdc, 0x42, 0x1a, 0x8b, 0x14, + 0xbd, 0x03, 0x31, 0x87, 0xa2, 0x97, 0xec, 0xb7, 0x8c, 0x97, 0x13, 0x41, 0x29, 0x49, 0x6c, 0x78, 0x47, 0x7a, 0x9a, + 0xd6, 0xa3, 0xad, 0x0c, 0xd8, 0xaf, 0x47, 0x3b, 0xfa, 0x0b, 0x14, 0x8f, 0x16, 0xfe, 0x92, 0xfe, 0x2e, 0xee, 0xe6, + 0x9e, 0xf3, 0x4d, 0xe3, 0x3b, 0xe2, 0x02, 0xc5, 0x9a, 0xdd, 0x5f, 0xd3, 0xd2, 0x59, 0x07, 0x82, 0x03, 0xde, 0x62, + 0x17, 0xed, 0xfb, 0x8d, 0xeb, 0xf4, 0xb4, 0xff, 0xd6, 0xad, 0x51, 0xbe, 0xf7, 0x4f, 0x89, 0x72, 0xb0, 0x7f, 0xe5, + 0xa2, 0xf9, 0xdb, 0x4f, 0x19, 0x92, 0x0a, 0xcd, 0x0d, 0xb6, 0x93, 0x2d, 0xc2, 0xda, 0x18, 0x07, 0x15, 0xbb, 0x2b, + 0xc3, 0x08, 0x18, 0xd4, 0xb1, 0xff, 0xd1, 0x67, 0xd3, 0x86, 0xec, 0x03, 0x40, 0xe5, 0x2a, 0x04, 0xec, 0x01, 0x38, + 0xd1, 0x08, 0x37, 0xc0, 0xad, 0x46, 0x4b, 0x3a, 0xa8, 0xdb, 0x82, 0x81, 0x68, 0x09, 0x1b, 0x79, 0xdb, 0xd5, 0xe9, + 0x2b, 0xc2, 0x87, 0xda, 0x49, 0xe9, 0x50, 0xfe, 0xea, 0x39, 0xfb, 0x9f, 0x1d, 0xd6, 0xd4, 0x94, 0x1b, 0xc0, 0xcc, + 0x59, 0x89, 0xbc, 0x42, 0xe8, 0x14, 0xf9, 0xbd, 0xaa, 0x2b, 0x31, 0x5c, 0xd6, 0xa2, 0xec, 0xcc, 0x6e, 0x9d, 0xe8, + 0x9d, 0x53, 0x50, 0x4b, 0x65, 0x83, 0x9c, 0xa4, 0xda, 0x7c, 0x64, 0xad, 0xa0, 0x44, 0x5d, 0xa3, 0xc0, 0xf1, 0x29, + 0xd7, 0xee, 0xff, 0x9d, 0x33, 0x41, 0xcd, 0x36, 0xaa, 0xfb, 0x2b, 0xfd, 0x54, 0xd5, 0x24, 0x16, 0xe0, 0x72, 0x92, + 0xe6, 0x1d, 0x8f, 0xb0, 0xfa, 0xc7, 0xc9, 0x52, 0x04, 0x7a, 0x15, 0xd1, 0xae, 0x04, 0x24, 0x68, 0x27, 0x67, 0xa1, + 0x22, 0x50, 0xa0, 0xaf, 0x7f, 0xbf, 0x49, 0xb3, 0x58, 0xae, 0x66, 0x7b, 0x98, 0x28, 0x8b, 0xf5, 0x10, 0x41, 0xce, + 0x4c, 0x1d, 0xec, 0xf7, 0x34, 0xa3, 0x59, 0x78, 0x65, 0x4a, 0x70, 0x29, 0xae, 0xa2, 0x22, 0x07, 0x9f, 0x43, 0x7c, + 0xe1, 0x53, 0x21, 0x37, 0x88, 0x68, 0xfa, 0xbd, 0x44, 0xb5, 0x23, 0x05, 0x72, 0x28, 0xf9, 0x09, 0xf1, 0x97, 0xac, + 0x8d, 0x71, 0xbf, 0x74, 0xaa, 0xfd, 0x52, 0x21, 0xb8, 0xff, 0x6c, 0x8b, 0x8d, 0x2a, 0x4f, 0xf4, 0xe8, 0x53, 0xac, + 0xff, 0xc9, 0x02, 0x4a, 0x75, 0xdf, 0x06, 0xa7, 0xe2, 0x51, 0xb8, 0xa9, 0x8b, 0x1b, 0x84, 0x16, 0x28, 0x47, 0x55, + 0xb1, 0x29, 0x23, 0xe2, 0x84, 0xdd, 0xd4, 0x45, 0x4f, 0x73, 0xa0, 0x53, 0x87, 0xa5, 0x89, 0x3c, 0x11, 0xda, 0x2d, + 0xe8, 0x9e, 0xe6, 0x58, 0x89, 0x17, 0xb2, 0x74, 0x90, 0x75, 0x22, 0x4d, 0xa8, 0xdc, 0xd5, 0x55, 0x47, 0xa5, 0x52, + 0x37, 0xbc, 0x4e, 0x35, 0xe3, 0xef, 0xd2, 0xfc, 0x89, 0x65, 0xbf, 0x6e, 0xfd, 0x56, 0xab, 0xbd, 0xb1, 0x7a, 0x54, + 0xb2, 0xe6, 0x38, 0x9b, 0x90, 0x94, 0x3e, 0x61, 0xbb, 0x99, 0x74, 0xad, 0x03, 0x4f, 0x82, 0xcb, 0xa1, 0x27, 0xa0, + 0x62, 0xd0, 0xc4, 0xdb, 0x5d, 0xa0, 0x1e, 0x81, 0x67, 0xa0, 0x7c, 0xa2, 0xd6, 0x01, 0x3f, 0xaf, 0xb5, 0x3c, 0x65, + 0x84, 0x61, 0xb5, 0xb3, 0x68, 0x39, 0x38, 0xef, 0x14, 0x81, 0x6b, 0x57, 0x02, 0xcf, 0x87, 0xea, 0xbd, 0x10, 0x30, + 0xdc, 0x3f, 0x15, 0x2a, 0x9b, 0xdd, 0x0c, 0xe7, 0x51, 0xe3, 0xf4, 0x40, 0x7b, 0xdb, 0xb5, 0x1e, 0xea, 0x5d, 0xb7, + 0x73, 0x5b, 0xe9, 0xde, 0xaf, 0x9d, 0x4c, 0xba, 0x80, 0xd6, 0xe6, 0xb3, 0xef, 0xec, 0x4a, 0xeb, 0xa6, 0xe7, 0xec, + 0xc1, 0xd6, 0x2d, 0xd1, 0xb9, 0x20, 0x9a, 0xfc, 0x7e, 0xe0, 0x59, 0xdb, 0x8e, 0x7e, 0x9b, 0x76, 0x6c, 0x73, 0x0f, + 0x75, 0xaf, 0xa0, 0xd6, 0x1b, 0x9a, 0xf7, 0xcf, 0x5c, 0xdb, 0x8e, 0xaf, 0x7e, 0x5d, 0x77, 0xb8, 0xce, 0x9b, 0xe0, + 0xb8, 0xe9, 0xda, 0x56, 0x3b, 0xfb, 0xb9, 0xbb, 0xb7, 0x16, 0x51, 0x98, 0x65, 0x3f, 0x16, 0xc5, 0x1f, 0x95, 0xbe, + 0x23, 0xd0, 0xd1, 0x9d, 0x17, 0x75, 0xba, 0xdc, 0x7d, 0x20, 0x8c, 0x27, 0xaf, 0x3e, 0x22, 0xba, 0xf5, 0x7d, 0xe6, + 0x7e, 0x05, 0xb8, 0x11, 0xdc, 0x41, 0xb4, 0x77, 0x4b, 0x7d, 0x52, 0xab, 0xaf, 0xf5, 0xda, 0x79, 0x7a, 0x7e, 0xd3, + 0xb9, 0xfd, 0xee, 0x9b, 0xa3, 0xad, 0xf7, 0xb8, 0xb0, 0x56, 0x96, 0x9e, 0xaa, 0x82, 0xbd, 0x59, 0x9e, 0xaa, 0x82, + 0xc9, 0x03, 0xaf, 0xd9, 0x2f, 0x68, 0x70, 0xa5, 0xa3, 0x8d, 0xf7, 0x44, 0x0d, 0xdc, 0xa2, 0xb0, 0x74, 0xf8, 0x25, + 0x37, 0x93, 0x97, 0xb8, 0xbf, 0x54, 0xe4, 0x62, 0xdf, 0x39, 0xa3, 0x3b, 0x33, 0xeb, 0x5e, 0x55, 0xb8, 0x5a, 0x90, + 0xab, 0x03, 0x5b, 0xcb, 0x2e, 0x0e, 0x37, 0x2c, 0xa2, 0x00, 0x81, 0x98, 0x5e, 0xa9, 0xb5, 0x3f, 0xa2, 0x41, 0xc8, + 0x07, 0x03, 0xbf, 0xc0, 0x60, 0x55, 0xa0, 0xf0, 0x81, 0x22, 0xf9, 0x2b, 0x4f, 0xc0, 0x2e, 0x9e, 0x01, 0xba, 0x15, + 0x9b, 0x15, 0x23, 0x44, 0xc8, 0x64, 0x39, 0xab, 0xe9, 0x0c, 0xf2, 0xa9, 0x2f, 0xbe, 0xb1, 0x55, 0xa7, 0xf3, 0xb6, + 0xa6, 0xca, 0xa9, 0x43, 0xa1, 0xbb, 0x9b, 0xba, 0x73, 0xeb, 0x22, 0x4f, 0x1d, 0x42, 0xae, 0x54, 0xac, 0xc4, 0x34, + 0xd4, 0x3c, 0x49, 0x33, 0xea, 0x2f, 0xf6, 0x7e, 0xaf, 0x51, 0x38, 0xe5, 0x4f, 0xc7, 0xa0, 0x0a, 0x57, 0x35, 0xc4, + 0xb1, 0x54, 0xc5, 0x23, 0x1b, 0x04, 0x9a, 0x57, 0xb7, 0x2a, 0x69, 0x42, 0x26, 0x37, 0xc2, 0xa7, 0x26, 0xa5, 0x3c, + 0x4d, 0x9b, 0xb4, 0x52, 0xa4, 0x0e, 0x3e, 0xa8, 0x53, 0x8d, 0xe7, 0x66, 0x75, 0x0d, 0x60, 0xc6, 0xf9, 0x15, 0xbf, + 0x54, 0x5c, 0x46, 0x6d, 0x65, 0x26, 0xed, 0x4f, 0x8e, 0xc6, 0x46, 0x5d, 0x4e, 0x1b, 0x65, 0x84, 0x95, 0xd2, 0x9c, + 0x14, 0xcb, 0xf1, 0xfc, 0x03, 0x06, 0x6b, 0x9e, 0xc0, 0x0e, 0x26, 0x2a, 0xe5, 0x7d, 0x04, 0xc4, 0xd7, 0x49, 0x7a, + 0x97, 0x40, 0x8a, 0xf4, 0x2f, 0x5d, 0x72, 0x97, 0xb1, 0x81, 0x18, 0xb3, 0x62, 0x66, 0xf4, 0x3f, 0xb8, 0x4b, 0xfa, + 0x93, 0x10, 0x00, 0x37, 0xd1, 0x14, 0x3a, 0x75, 0x9e, 0x5c, 0xe4, 0xc1, 0xf2, 0xc2, 0x43, 0x2b, 0x46, 0x3c, 0xf8, + 0xeb, 0x75, 0x88, 0x20, 0xe6, 0x98, 0xe2, 0xe9, 0x17, 0x46, 0x7f, 0x09, 0x2e, 0x31, 0x82, 0xd0, 0xdd, 0x3b, 0x87, + 0x21, 0xdc, 0xec, 0x41, 0x06, 0xf5, 0x87, 0x3a, 0x24, 0x6a, 0xf8, 0x63, 0xe5, 0x41, 0xff, 0xd7, 0x99, 0xb0, 0xd4, + 0x7e, 0x7a, 0x3a, 0x80, 0x0a, 0xde, 0x57, 0xbc, 0x8d, 0x88, 0xef, 0x13, 0x3f, 0x8b, 0x07, 0x9b, 0x67, 0x1b, 0xb0, + 0xd6, 0x3d, 0xc9, 0x8d, 0x75, 0x95, 0xb0, 0x81, 0x80, 0xaf, 0x51, 0xd4, 0x9e, 0xd7, 0x6e, 0xf7, 0xe0, 0xaf, 0xfe, + 0x45, 0xc8, 0x80, 0x89, 0xd3, 0xf7, 0x99, 0x93, 0x35, 0xba, 0xc8, 0x64, 0xfa, 0xd0, 0x49, 0xdf, 0xe8, 0x74, 0xdf, + 0x09, 0xff, 0xa8, 0x98, 0xc5, 0x87, 0x5b, 0xfa, 0x4a, 0x93, 0xe2, 0x0e, 0x58, 0xd9, 0x3c, 0x2a, 0x08, 0x75, 0x2e, + 0xa2, 0xaf, 0x4c, 0xf9, 0x96, 0x50, 0xb3, 0x6f, 0x2c, 0x29, 0xa5, 0x7b, 0x0d, 0xbd, 0x4e, 0x6b, 0xfd, 0x36, 0x4a, + 0x30, 0x26, 0x3a, 0x9e, 0xbc, 0x8c, 0xc7, 0xca, 0xfb, 0x78, 0xdc, 0x48, 0x85, 0x3c, 0x00, 0x11, 0xa8, 0x18, 0x7f, + 0xba, 0xf2, 0xe4, 0xa4, 0x17, 0xc6, 0xab, 0x50, 0x0a, 0x0a, 0x03, 0xba, 0x02, 0x29, 0xe0, 0x51, 0x7b, 0xa2, 0xb3, + 0xb0, 0x4b, 0xb8, 0x47, 0x37, 0x01, 0x63, 0x7d, 0xfe, 0x11, 0xd0, 0xdc, 0x85, 0x3b, 0xbc, 0x18, 0xa0, 0x36, 0xf5, + 0xea, 0xee, 0xe3, 0x5a, 0x9d, 0xc3, 0x21, 0x38, 0x58, 0x0d, 0x22, 0x38, 0x9d, 0x4f, 0x1d, 0xcd, 0xb2, 0x00, 0x95, + 0x93, 0xe5, 0x46, 0xde, 0x3c, 0x5a, 0xf4, 0xea, 0xbe, 0xb7, 0x4c, 0xcb, 0xaa, 0x0e, 0x32, 0x96, 0x85, 0x15, 0xe0, + 0xea, 0xd0, 0xfa, 0x41, 0xb8, 0x2c, 0x9c, 0x3f, 0x10, 0x82, 0xd8, 0xbd, 0xda, 0x96, 0x3c, 0x57, 0x73, 0xf8, 0xd9, + 0x73, 0xb6, 0xe6, 0x12, 0x75, 0xd2, 0x99, 0x08, 0x40, 0xec, 0xa9, 0x59, 0x45, 0xd7, 0x40, 0x52, 0xa7, 0x59, 0x45, + 0xd7, 0xd4, 0x6c, 0x63, 0x1c, 0xc8, 0x47, 0xab, 0x14, 0xb0, 0xef, 0xa6, 0xe3, 0x60, 0xf5, 0x2c, 0x96, 0xd7, 0xa1, + 0xbb, 0x67, 0x1b, 0xe5, 0x33, 0xa8, 0x5b, 0x6d, 0x8c, 0x89, 0xed, 0xe6, 0xcb, 0xb9, 0x7e, 0x3b, 0x58, 0xfa, 0x76, + 0xd0, 0x9c, 0x53, 0xf6, 0x9d, 0x2e, 0x7b, 0x65, 0x97, 0x4d, 0x3d, 0x77, 0x54, 0xb4, 0x1a, 0x03, 0x7a, 0x03, 0x0b, + 0xd6, 0xe7, 0x22, 0xcd, 0x56, 0xa5, 0x2a, 0x01, 0x2f, 0x8c, 0x15, 0xbb, 0xf3, 0x1b, 0x99, 0x21, 0x09, 0xf3, 0x38, + 0x13, 0x6f, 0xe9, 0x5e, 0x0b, 0x93, 0xe3, 0x58, 0x24, 0x53, 0x42, 0xa7, 0x74, 0x67, 0x1b, 0x3a, 0x57, 0x61, 0x14, + 0xd1, 0x5a, 0x49, 0xa5, 0x91, 0xc0, 0xd4, 0x0c, 0x50, 0x32, 0x57, 0xe0, 0x94, 0x2e, 0xf7, 0xbf, 0x23, 0x31, 0xce, + 0x7c, 0x51, 0x32, 0x03, 0xba, 0xe5, 0xd7, 0xc5, 0xba, 0x95, 0x22, 0x23, 0xcc, 0x9b, 0xe3, 0xf6, 0xba, 0x3e, 0x04, + 0x72, 0xb5, 0xec, 0x51, 0x34, 0x0e, 0x0a, 0x1d, 0x2e, 0x55, 0x02, 0xec, 0x8b, 0xc4, 0xcf, 0x08, 0x5b, 0xda, 0x03, + 0xb9, 0x3d, 0x3a, 0x13, 0xe6, 0x9c, 0x93, 0xb2, 0xec, 0x5c, 0x9a, 0xc1, 0xe5, 0xc4, 0x95, 0xe0, 0x22, 0xbd, 0x6d, + 0x4f, 0x93, 0x96, 0xb6, 0x8f, 0x0d, 0xe7, 0x68, 0x68, 0x1b, 0x74, 0xc7, 0xfe, 0xd0, 0x5c, 0x2c, 0x62, 0xeb, 0x62, + 0x31, 0xec, 0xcc, 0x7e, 0xb4, 0x58, 0x80, 0x1c, 0x00, 0x8e, 0xba, 0x0d, 0x1f, 0xb3, 0x25, 0x70, 0x5a, 0x4d, 0xb3, + 0xa9, 0xb7, 0xe1, 0xd5, 0x33, 0xd5, 0xd3, 0x4b, 0x9e, 0x3f, 0x13, 0x66, 0x2c, 0x36, 0x3c, 0x7f, 0x66, 0x1d, 0x39, + 0xd5, 0x33, 0xa1, 0x44, 0xeb, 0x02, 0x9a, 0x81, 0xd7, 0x14, 0x30, 0x62, 0xc9, 0x64, 0x4a, 0x15, 0x79, 0xdc, 0x9b, + 0x6e, 0xd4, 0xe0, 0x05, 0x85, 0x43, 0x20, 0xa5, 0xd3, 0x2f, 0x9e, 0x33, 0xfd, 0xde, 0xc5, 0xf3, 0x0e, 0x59, 0xdb, + 0x30, 0x5d, 0x6e, 0x86, 0xc9, 0xa0, 0xf4, 0x9f, 0x99, 0x89, 0x71, 0x61, 0x4d, 0x12, 0x40, 0xfc, 0x1b, 0xfb, 0x1d, + 0x52, 0xb8, 0x79, 0x7f, 0x39, 0x8c, 0x1f, 0x79, 0x3f, 0x46, 0xf6, 0x24, 0xcd, 0x10, 0x6b, 0x26, 0x15, 0x72, 0xf7, + 0xd5, 0xfa, 0xc7, 0xc4, 0x6e, 0xb2, 0x07, 0x16, 0x80, 0xd8, 0x9a, 0xb6, 0xba, 0xe5, 0xfd, 0xbe, 0x67, 0x8a, 0x00, + 0x3f, 0x28, 0xff, 0xe8, 0xce, 0x90, 0x0c, 0xca, 0xae, 0x1b, 0x42, 0x3c, 0x28, 0x9b, 0xa6, 0xbd, 0xde, 0xf6, 0xce, + 0x3c, 0x56, 0xd7, 0x69, 0x67, 0x71, 0xb5, 0xc8, 0x20, 0xad, 0x3e, 0x64, 0xc7, 0x99, 0x7d, 0x76, 0xb4, 0x54, 0xba, + 0xdf, 0x87, 0x88, 0xb8, 0xa3, 0xac, 0xed, 0xb7, 0x5b, 0x70, 0x0d, 0x47, 0x83, 0xd0, 0x95, 0xbd, 0x5d, 0x46, 0x1b, + 0x17, 0xe2, 0xb8, 0x67, 0x3a, 0x5f, 0xf0, 0xe5, 0x51, 0xda, 0x79, 0x70, 0xaa, 0x27, 0xfa, 0xdc, 0x74, 0x57, 0x99, + 0x5c, 0xeb, 0xb0, 0x1a, 0x83, 0xda, 0x2c, 0x6c, 0xe1, 0x2e, 0x6c, 0xa3, 0x83, 0xd6, 0xbe, 0x2c, 0xf8, 0xa7, 0x0c, + 0xc0, 0x97, 0x9e, 0x2d, 0xdb, 0x5e, 0x93, 0x56, 0xaf, 0x65, 0x14, 0x62, 0x4b, 0xdb, 0xab, 0x4f, 0x47, 0xf9, 0xb8, + 0x39, 0xa1, 0xb8, 0x90, 0xa3, 0xfc, 0xe8, 0x35, 0x44, 0x5d, 0xeb, 0x3a, 0x2e, 0x16, 0x1d, 0x6e, 0x5c, 0x75, 0xdb, + 0x8d, 0xeb, 0x07, 0xc4, 0x5b, 0xa3, 0x4d, 0x0a, 0xb5, 0x32, 0x76, 0x04, 0x2f, 0xcb, 0x87, 0x43, 0x26, 0x86, 0x43, + 0x09, 0x99, 0xfa, 0xd8, 0xbd, 0xa1, 0x69, 0x9f, 0x9f, 0xb6, 0x7e, 0xc4, 0x52, 0xe3, 0x28, 0x36, 0xbc, 0xd3, 0x77, + 0x1e, 0x5b, 0xe3, 0x4a, 0xbe, 0x0c, 0x66, 0xbb, 0x82, 0x6a, 0x6b, 0xbc, 0x61, 0x2f, 0xe7, 0xdf, 0x57, 0x52, 0xc9, + 0xdf, 0xfe, 0x0c, 0xd7, 0xf0, 0xd6, 0x96, 0x0e, 0x9a, 0x6a, 0x96, 0xb3, 0x5c, 0xdf, 0x0b, 0x8e, 0x3f, 0xee, 0x5e, + 0x11, 0x0c, 0x7e, 0x4f, 0x47, 0x41, 0x2e, 0x96, 0x6a, 0x0d, 0x28, 0x48, 0x47, 0x76, 0x4c, 0x65, 0x81, 0x61, 0x00, + 0x6f, 0xc8, 0x00, 0x79, 0x4c, 0xe1, 0x6e, 0xa8, 0xf0, 0xc2, 0x5f, 0x2a, 0xb2, 0x4b, 0x60, 0x5b, 0x33, 0x3e, 0x66, + 0xb8, 0x83, 0x90, 0x7f, 0x04, 0xbb, 0x63, 0x2b, 0x76, 0xcb, 0x16, 0x0c, 0xc9, 0xc6, 0x71, 0x18, 0x63, 0x3e, 0x9e, + 0xc4, 0x57, 0x62, 0x12, 0x0f, 0x78, 0x84, 0x8e, 0x11, 0x6b, 0x5e, 0xcf, 0x62, 0x39, 0x80, 0xec, 0x8e, 0x2b, 0x1d, + 0x10, 0x42, 0x63, 0x43, 0x4b, 0x5e, 0x17, 0x06, 0x17, 0x3b, 0xf6, 0x19, 0x89, 0x64, 0x1c, 0x82, 0x45, 0xab, 0x1a, + 0x58, 0x98, 0xd8, 0x2d, 0x2f, 0x66, 0xab, 0x39, 0xfe, 0x73, 0x38, 0x20, 0x00, 0x76, 0xb0, 0x6f, 0xd8, 0x5d, 0x84, + 0x48, 0x6f, 0x0b, 0x7e, 0x67, 0x79, 0xba, 0xb0, 0x7b, 0xfe, 0x96, 0x8f, 0xd9, 0xf9, 0x0f, 0x1e, 0x44, 0xce, 0x9e, + 0x7f, 0x04, 0x34, 0xc4, 0x7b, 0x7e, 0x9b, 0x7a, 0x15, 0xbb, 0x25, 0x0a, 0xc2, 0x5b, 0x70, 0x06, 0xba, 0x87, 0x08, + 0xd8, 0xb7, 0x7c, 0x81, 0xb1, 0x62, 0x67, 0xe9, 0xd2, 0xc3, 0x8c, 0x50, 0x7b, 0x3a, 0x5f, 0xd6, 0x6a, 0x12, 0x6e, + 0xae, 0x96, 0x93, 0xc1, 0x60, 0xe3, 0xef, 0xf8, 0x1a, 0xf8, 0x60, 0xce, 0x7f, 0xf0, 0x76, 0x54, 0x2e, 0xfc, 0xe7, + 0x75, 0x96, 0xbc, 0xf3, 0xd9, 0xdb, 0x01, 0x5f, 0x00, 0xde, 0x12, 0x3a, 0x70, 0xdd, 0xfb, 0x4c, 0xe2, 0xb5, 0xbd, + 0xd5, 0xd7, 0x08, 0x24, 0xf2, 0x05, 0x60, 0xc4, 0xc4, 0xfc, 0x7e, 0x0b, 0x11, 0x18, 0x09, 0xf8, 0xb6, 0x6a, 0x8f, + 0xf8, 0x2d, 0x37, 0x80, 0x5f, 0x99, 0xcf, 0x1e, 0x78, 0xa8, 0x7f, 0x26, 0x3e, 0xbb, 0xe1, 0xef, 0xf9, 0xb5, 0x27, + 0x25, 0xe9, 0x72, 0xf6, 0x7e, 0x0e, 0xd7, 0x43, 0x29, 0x4f, 0x87, 0xf4, 0xb3, 0x31, 0x18, 0x40, 0x28, 0x64, 0xde, + 0x78, 0xc0, 0x9a, 0x14, 0xe2, 0x5f, 0xc0, 0xb7, 0xa3, 0x84, 0xcd, 0x1b, 0x6f, 0xeb, 0x6b, 0x79, 0xf3, 0xc6, 0x7b, + 0xf0, 0x29, 0x0a, 0xb0, 0x0a, 0x4a, 0x59, 0x60, 0x15, 0x84, 0x8d, 0x36, 0xc2, 0x18, 0xb8, 0x7a, 0xd7, 0x18, 0xea, + 0x7a, 0x8e, 0xd8, 0xb6, 0xd2, 0x77, 0xe1, 0x3b, 0xc8, 0x80, 0x0f, 0x5e, 0x17, 0x25, 0xd1, 0xe7, 0xd4, 0x14, 0x49, + 0xeb, 0x9e, 0xfb, 0xad, 0x75, 0x47, 0x6b, 0x4a, 0x7d, 0xe4, 0x6a, 0x7c, 0x38, 0xd4, 0xd7, 0x42, 0x8b, 0x04, 0x53, + 0xd0, 0xb8, 0x06, 0x6d, 0x01, 0x82, 0x3e, 0x0f, 0x90, 0xb5, 0xa4, 0x58, 0xf0, 0xed, 0xaf, 0x10, 0x83, 0x57, 0xa6, + 0x77, 0x2e, 0x57, 0x19, 0x09, 0xdb, 0x0b, 0xbf, 0x1c, 0xd6, 0xfe, 0xc4, 0xa9, 0x85, 0xa5, 0xd5, 0x1c, 0xd4, 0xcf, + 0x6c, 0x39, 0x4e, 0x55, 0xed, 0xdf, 0x92, 0xa4, 0xda, 0x55, 0x5a, 0x4e, 0xef, 0xed, 0x9b, 0x2e, 0x13, 0x6c, 0xec, + 0x07, 0x54, 0x1d, 0x59, 0x0d, 0xbb, 0x2f, 0xd4, 0x17, 0x3d, 0x25, 0x13, 0x9a, 0x8f, 0x2a, 0x9a, 0x67, 0xf7, 0x9b, + 0x1d, 0xf5, 0x9f, 0x5e, 0x0e, 0x45, 0x80, 0x64, 0x95, 0x16, 0x4b, 0x91, 0xb3, 0xb1, 0x1f, 0x0f, 0x93, 0x4c, 0x85, + 0x17, 0xa4, 0xa3, 0xbb, 0xdf, 0xb8, 0xbf, 0xe5, 0x06, 0xb2, 0x42, 0xab, 0x36, 0x18, 0x2b, 0x45, 0xcb, 0x60, 0x7d, + 0x35, 0xee, 0xf7, 0xc5, 0xd5, 0x78, 0x2a, 0x82, 0x1a, 0x88, 0x8b, 0xc4, 0xf5, 0x78, 0x5a, 0x13, 0x4b, 0x6a, 0x57, + 0x60, 0x8c, 0x1e, 0x57, 0x45, 0xed, 0x53, 0x5f, 0x43, 0x28, 0x52, 0xad, 0x99, 0x63, 0x8d, 0x1b, 0x23, 0xe2, 0x0e, + 0x2b, 0xd7, 0x4e, 0xed, 0x75, 0x00, 0x96, 0x57, 0xe3, 0x82, 0xb0, 0x49, 0x8e, 0x9d, 0x0b, 0x58, 0x8d, 0x86, 0x54, + 0xbb, 0xe1, 0xd6, 0xcb, 0xce, 0x6f, 0x1e, 0x27, 0xb6, 0x36, 0xc2, 0x2d, 0x05, 0x94, 0x51, 0x7e, 0x63, 0x39, 0x61, + 0x77, 0xaa, 0x77, 0xa4, 0x6a, 0x47, 0x9c, 0xb8, 0x80, 0xe5, 0x86, 0xa7, 0x56, 0xdf, 0xc4, 0xe0, 0x44, 0xa8, 0x5a, + 0xe9, 0x78, 0xed, 0x47, 0xdc, 0xaf, 0xee, 0xeb, 0x5e, 0x09, 0x7e, 0x12, 0xf2, 0xfa, 0x2d, 0xef, 0x00, 0xb0, 0xe2, + 0x43, 0x5e, 0x4c, 0x0b, 0x47, 0xeb, 0x32, 0x28, 0x03, 0x44, 0x68, 0x06, 0x40, 0x27, 0x57, 0x07, 0x51, 0x1a, 0xb8, + 0xe2, 0x0e, 0x11, 0x7e, 0x1a, 0x3d, 0xcb, 0xaf, 0xc3, 0x67, 0xd5, 0x34, 0xbc, 0xc8, 0x83, 0xe8, 0xa2, 0x0a, 0xa2, + 0x67, 0xd5, 0x55, 0xf8, 0x2c, 0x9f, 0x46, 0x17, 0x79, 0x10, 0x5e, 0x54, 0x8d, 0x7d, 0xd7, 0xee, 0xee, 0x09, 0x79, + 0xdb, 0xd5, 0x1f, 0x39, 0x57, 0xf6, 0x94, 0xe9, 0xf9, 0x79, 0xad, 0x57, 0x6a, 0xb7, 0xb9, 0x5e, 0xa3, 0x66, 0xea, + 0xa3, 0xec, 0x6f, 0xb6, 0xb1, 0xf0, 0x68, 0x0e, 0xa1, 0xcf, 0x48, 0x8b, 0xb9, 0xc7, 0xb9, 0xde, 0xec, 0x49, 0x61, + 0x60, 0xc4, 0xa4, 0x92, 0x91, 0xd3, 0x0b, 0x5c, 0x84, 0x2a, 0xc4, 0xb0, 0x96, 0xae, 0xf6, 0x59, 0x97, 0xde, 0x40, + 0x5d, 0x53, 0xec, 0x6b, 0xc8, 0xc0, 0x8b, 0xa6, 0x97, 0xc1, 0x18, 0x90, 0x23, 0xf0, 0x8e, 0xcf, 0x96, 0x70, 0x60, + 0xae, 0x01, 0xfa, 0xe6, 0x51, 0x5f, 0x97, 0x3b, 0xbe, 0x56, 0x7d, 0x33, 0x5d, 0x8f, 0x94, 0xf2, 0x63, 0xc5, 0xef, + 0x2e, 0x9e, 0xb3, 0x5b, 0xae, 0x51, 0x51, 0x7e, 0xd1, 0x8b, 0xf5, 0x1e, 0xb8, 0xea, 0x7e, 0x81, 0xdb, 0x2c, 0x1e, + 0xbb, 0xf2, 0x80, 0x65, 0x5b, 0xf6, 0xc0, 0x6e, 0xd8, 0x7b, 0xf6, 0x84, 0xbd, 0x61, 0xef, 0xd8, 0x4f, 0xa8, 0xda, + 0x50, 0x42, 0x9e, 0xbf, 0xe0, 0xb7, 0xd2, 0xf4, 0x28, 0x51, 0xc9, 0x1e, 0x6c, 0x33, 0xcd, 0x70, 0xc3, 0xde, 0xf3, + 0xc5, 0x70, 0xc5, 0xde, 0x40, 0x36, 0x94, 0x89, 0x07, 0x2b, 0xf6, 0x13, 0x57, 0x20, 0x66, 0xfa, 0x2c, 0x2c, 0x2d, + 0x51, 0xd1, 0x94, 0x89, 0x32, 0xf4, 0x1b, 0x8e, 0x2f, 0xb2, 0x9f, 0xb0, 0x08, 0xf9, 0x99, 0xe1, 0x8a, 0x3d, 0xf0, + 0xc5, 0x60, 0xc5, 0xde, 0x6b, 0x03, 0xd1, 0x60, 0xe3, 0x96, 0x46, 0x48, 0x56, 0xba, 0x2c, 0x29, 0x4d, 0x6f, 0xed, + 0x6b, 0xe0, 0x86, 0xdd, 0x60, 0xed, 0x9e, 0x60, 0xd1, 0x28, 0xf0, 0x0f, 0x56, 0xec, 0x1d, 0x97, 0x00, 0x6a, 0x6e, + 0x79, 0xd2, 0x2b, 0x54, 0x17, 0x48, 0xf7, 0x83, 0x27, 0x9c, 0x5e, 0x64, 0xef, 0xb0, 0x0c, 0xfa, 0xca, 0x70, 0xc5, + 0xb6, 0x58, 0xbb, 0x1b, 0x63, 0xd9, 0xb2, 0xaa, 0x27, 0x11, 0x81, 0x51, 0x50, 0x29, 0x2d, 0xff, 0x46, 0x2c, 0x9b, + 0xba, 0x69, 0x50, 0x1b, 0xfa, 0xf3, 0xc1, 0xe8, 0x2f, 0xbe, 0x7e, 0xf7, 0x83, 0x57, 0xea, 0x6b, 0xef, 0x2f, 0x8e, + 0x6b, 0x65, 0x89, 0xae, 0x95, 0xbf, 0xf2, 0x72, 0xf6, 0xcb, 0x7c, 0xa2, 0x6b, 0x49, 0x3b, 0x0c, 0xf9, 0x9a, 0xce, + 0x7e, 0xe9, 0x70, 0xb6, 0xfc, 0xd5, 0xf7, 0x1b, 0xd3, 0xc5, 0xea, 0xb3, 0xba, 0x77, 0x1f, 0x06, 0x9b, 0xc6, 0xa9, + 0xf7, 0xee, 0x74, 0xbd, 0xb1, 0x99, 0xb5, 0xf6, 0xcc, 0xfc, 0x1f, 0xae, 0xf4, 0x16, 0x87, 0xee, 0x86, 0x6f, 0x87, + 0x1b, 0x7b, 0x14, 0xe4, 0xf7, 0xa5, 0xd2, 0x38, 0xab, 0xf9, 0x0b, 0xaf, 0x53, 0x8a, 0x05, 0x44, 0xa3, 0x4f, 0x46, + 0x12, 0xba, 0x64, 0x26, 0x9e, 0x21, 0xbe, 0xc8, 0x00, 0x99, 0x0b, 0x44, 0xb3, 0x7b, 0x3e, 0x9e, 0xdc, 0x5f, 0xc5, + 0x93, 0xfb, 0x01, 0xff, 0x64, 0x5a, 0xd0, 0x5e, 0x6c, 0xf7, 0x3e, 0xfb, 0x95, 0x17, 0xf6, 0x72, 0xfc, 0xc5, 0x67, + 0x5f, 0x84, 0xbb, 0x42, 0x7f, 0xf1, 0xd9, 0x3b, 0xc1, 0x7f, 0x1d, 0x69, 0xa2, 0x0c, 0xf6, 0xae, 0xe6, 0xbf, 0x8e, + 0x90, 0xf1, 0x83, 0x7d, 0x16, 0xfc, 0x0b, 0xf8, 0x7e, 0x57, 0x09, 0x5a, 0xc5, 0x3f, 0xd7, 0xea, 0xe7, 0x7b, 0x19, + 0x97, 0x03, 0x6f, 0x42, 0x2b, 0xe8, 0xcd, 0xdb, 0x5a, 0xfe, 0x24, 0x1e, 0x8e, 0x54, 0x3d, 0x35, 0xfc, 0xb3, 0x58, + 0xcc, 0xa2, 0x3e, 0x4a, 0xa7, 0xf2, 0x26, 0x6f, 0x79, 0x26, 0xad, 0xcb, 0xf7, 0x10, 0x0a, 0xfc, 0xd6, 0x86, 0x28, + 0xd8, 0x71, 0xdc, 0x08, 0xde, 0xb2, 0x77, 0xc2, 0x67, 0xd9, 0x74, 0xcb, 0x6f, 0xf8, 0x13, 0xfe, 0x8e, 0xef, 0x82, + 0x07, 0xfe, 0x9e, 0xbf, 0xe1, 0x3f, 0xf1, 0x1d, 0x5b, 0x4a, 0xb4, 0xd3, 0x7a, 0x7b, 0x19, 0x6c, 0x59, 0xbd, 0xbb, + 0x0c, 0x1e, 0x58, 0xbd, 0x7d, 0x1e, 0xdc, 0xb0, 0x7a, 0xf7, 0x3c, 0x78, 0xcf, 0xb6, 0x97, 0xc1, 0x13, 0xb6, 0xbb, + 0x0c, 0xde, 0xb0, 0xed, 0xf3, 0xe0, 0x1d, 0xdb, 0x3d, 0x0f, 0x7e, 0x92, 0x18, 0x0f, 0xef, 0x84, 0xe4, 0x38, 0x79, + 0x57, 0x33, 0xc3, 0xa7, 0x1b, 0x7c, 0x16, 0xd6, 0x2f, 0xaa, 0x63, 0xf0, 0xb9, 0x66, 0xba, 0xc5, 0x81, 0x10, 0x4c, + 0xb7, 0x37, 0xb8, 0xa5, 0x27, 0xa6, 0x55, 0x41, 0x2a, 0x58, 0x57, 0x3b, 0x83, 0x45, 0xdd, 0xb4, 0xce, 0x64, 0xc7, + 0x2f, 0x31, 0xee, 0xf0, 0x4b, 0x5c, 0xb0, 0x65, 0xd3, 0xe9, 0xa4, 0x73, 0xfa, 0x24, 0xd0, 0x9b, 0xbf, 0xde, 0xf5, + 0x2b, 0xe9, 0x3b, 0x53, 0x34, 0x3c, 0x57, 0x5a, 0xe3, 0xd6, 0x4e, 0x1f, 0x5a, 0x3b, 0x3d, 0x93, 0x2a, 0xb4, 0x88, + 0x45, 0x65, 0x51, 0x55, 0xc8, 0x24, 0x1e, 0x64, 0x5a, 0x9f, 0x96, 0x30, 0x52, 0x64, 0x02, 0x1a, 0x7d, 0x41, 0xc7, + 0x40, 0x4e, 0x16, 0x05, 0xb6, 0xe4, 0x9b, 0x41, 0xc2, 0xd6, 0x3c, 0x9e, 0x0e, 0x93, 0x60, 0xc9, 0xee, 0xf8, 0xb0, + 0x5b, 0x20, 0x58, 0xa9, 0x00, 0x26, 0x7d, 0x71, 0x6a, 0xef, 0xeb, 0xbc, 0xb7, 0x4a, 0xe3, 0x38, 0x13, 0xa8, 0x6c, + 0xab, 0xf4, 0x06, 0xbf, 0x75, 0xf6, 0xf3, 0xb5, 0xda, 0xdf, 0x41, 0x52, 0xf8, 0x15, 0x18, 0x76, 0x88, 0xf0, 0x0e, + 0x2a, 0x8c, 0x3c, 0x4b, 0x66, 0xd1, 0x57, 0xf6, 0x96, 0xbe, 0x35, 0xdb, 0xf4, 0x7f, 0x5a, 0x04, 0xed, 0xe3, 0xb2, + 0xf3, 0x3f, 0x99, 0x57, 0x7f, 0xeb, 0x78, 0x75, 0xe3, 0x4f, 0x1e, 0xf8, 0x27, 0x0c, 0x4b, 0xc0, 0x44, 0xb6, 0xe3, + 0x9f, 0x46, 0xdb, 0xc6, 0x29, 0x4f, 0xee, 0xe3, 0xff, 0x57, 0x0a, 0xb4, 0x77, 0xf2, 0xca, 0xde, 0x11, 0xb7, 0xbc, + 0x63, 0x1f, 0x5f, 0x5a, 0x1b, 0xa2, 0x81, 0x26, 0xf9, 0xc4, 0xdd, 0x68, 0x68, 0xd8, 0x10, 0x7f, 0xe1, 0xd5, 0xec, + 0xd3, 0x7c, 0xb2, 0xe5, 0xc7, 0xdb, 0xe1, 0xa7, 0x8e, 0xed, 0xf0, 0x17, 0x7f, 0xb0, 0x6c, 0xbe, 0xd6, 0xab, 0x9d, + 0xdb, 0xb8, 0x53, 0xe9, 0x1d, 0x3f, 0xde, 0xc4, 0x87, 0xff, 0x71, 0xa5, 0x77, 0xdf, 0x5c, 0x69, 0xbb, 0xca, 0xdd, + 0x9d, 0x6f, 0x3a, 0xbe, 0x91, 0xb5, 0xc6, 0x38, 0x33, 0xa3, 0x59, 0xfc, 0x89, 0x66, 0x69, 0x10, 0x59, 0x0a, 0xc5, + 0x9f, 0xcc, 0xb4, 0x53, 0x77, 0xaa, 0xac, 0xee, 0x96, 0x6f, 0x71, 0x8f, 0xbf, 0xe5, 0x63, 0xb6, 0x30, 0x9e, 0x9a, + 0xb7, 0x57, 0x8b, 0xc9, 0x60, 0x70, 0xeb, 0xef, 0xef, 0x79, 0x38, 0xbb, 0x9d, 0xb3, 0xb7, 0xfc, 0x9e, 0x16, 0xd3, + 0x44, 0x35, 0xbd, 0x78, 0x4c, 0xf0, 0xba, 0xf5, 0xfd, 0x89, 0xc5, 0xff, 0x6a, 0x5f, 0x34, 0x6f, 0xfd, 0x81, 0xb4, + 0x46, 0xcb, 0x5d, 0xfd, 0xfd, 0xe3, 0x8a, 0x89, 0x5b, 0x10, 0x2f, 0xde, 0xdb, 0x9a, 0x86, 0x37, 0xfc, 0xa3, 0xf7, + 0xd6, 0x9f, 0xbe, 0xd5, 0x31, 0x37, 0x13, 0x75, 0x24, 0xbd, 0xb9, 0x78, 0xce, 0x7e, 0xe5, 0x9f, 0xe4, 0x71, 0xf2, + 0x45, 0xc8, 0x49, 0x7b, 0x83, 0xdc, 0x4d, 0x74, 0x4a, 0xbc, 0x73, 0x13, 0x09, 0x0b, 0x02, 0x61, 0x38, 0x6a, 0xfe, + 0x30, 0x29, 0xa7, 0xde, 0x0e, 0xb8, 0x5d, 0xb9, 0xad, 0x7f, 0xbe, 0xe5, 0x9c, 0x2f, 0x86, 0x97, 0xd3, 0x77, 0xdd, + 0x2e, 0x3d, 0x2a, 0x9a, 0x4d, 0x05, 0xba, 0xdd, 0x62, 0xec, 0xd5, 0xc9, 0xcc, 0x32, 0x97, 0x7c, 0xe9, 0x5d, 0x6d, + 0x66, 0x1e, 0xd3, 0xfb, 0xcd, 0x34, 0x43, 0x22, 0x5f, 0x20, 0x64, 0x3a, 0x1c, 0xee, 0xce, 0xb1, 0x3c, 0x3e, 0x7c, + 0xf3, 0xec, 0xc9, 0xe0, 0x09, 0x46, 0x6e, 0x59, 0xd1, 0x20, 0xef, 0xf8, 0x30, 0xab, 0x5b, 0xb7, 0x8d, 0x8b, 0xe7, + 0xc3, 0x5f, 0x20, 0x6f, 0xd0, 0xf5, 0xd0, 0x14, 0xd1, 0x2a, 0xbf, 0xa3, 0xe8, 0x13, 0x25, 0x07, 0x1d, 0x4f, 0xa0, + 0x76, 0x48, 0x81, 0xfb, 0xee, 0x19, 0x07, 0xfd, 0x06, 0x96, 0xda, 0xef, 0x9f, 0x7f, 0x22, 0x1e, 0x69, 0x18, 0xef, + 0xef, 0xc3, 0xe8, 0x8f, 0xb8, 0x2c, 0xd6, 0x70, 0xba, 0x0e, 0xe0, 0x73, 0xcf, 0xf4, 0xed, 0xeb, 0xce, 0xf7, 0xfd, + 0xc0, 0xdb, 0xf2, 0x1b, 0xf6, 0x8e, 0x7b, 0x97, 0xc3, 0x37, 0xfe, 0xb3, 0x27, 0x20, 0x3a, 0xc1, 0xb8, 0x7c, 0xc6, + 0x48, 0xd8, 0x8e, 0x62, 0xd4, 0x2a, 0xfc, 0x5c, 0x43, 0x88, 0xd6, 0x27, 0x64, 0xec, 0x82, 0xf4, 0x0f, 0x0a, 0xd0, + 0x4f, 0x08, 0xac, 0x26, 0xa9, 0x51, 0x60, 0x12, 0xdf, 0xd6, 0x90, 0x40, 0x0a, 0x16, 0x08, 0xbd, 0x81, 0xe2, 0x53, + 0xc1, 0xdf, 0x0d, 0x3f, 0x93, 0xe4, 0xb7, 0xa8, 0xf9, 0x18, 0xfe, 0x86, 0xa1, 0x99, 0x54, 0x0f, 0x69, 0x1d, 0x25, + 0xde, 0x4f, 0xfe, 0x3e, 0x0a, 0x2b, 0xa1, 0x8e, 0x85, 0x20, 0x15, 0x43, 0x2e, 0xc4, 0xc5, 0xf3, 0xc9, 0x6d, 0x29, + 0xc2, 0x3f, 0x26, 0xf8, 0x4c, 0x2e, 0x34, 0xf9, 0x8c, 0x9e, 0x34, 0xf2, 0xfd, 0x07, 0xf9, 0xbe, 0xec, 0xd4, 0x60, + 0x51, 0x0f, 0xf9, 0x6d, 0xed, 0xbe, 0x2f, 0xa7, 0x04, 0x3d, 0xb2, 0x1f, 0xd0, 0x14, 0x0c, 0xd4, 0x04, 0xa4, 0x0c, + 0xc1, 0x2d, 0x5c, 0xf5, 0x3d, 0x55, 0x90, 0x2f, 0xbf, 0xf7, 0x59, 0xc8, 0x70, 0x95, 0x05, 0x21, 0xc9, 0xa5, 0x42, + 0x0a, 0x1b, 0xb7, 0xf5, 0xe0, 0xb3, 0xc6, 0x24, 0x91, 0x90, 0x53, 0x02, 0x92, 0xa4, 0xbd, 0x81, 0x24, 0x11, 0xd3, + 0x7f, 0xb8, 0x4e, 0x9a, 0x66, 0x25, 0xa5, 0x1b, 0xe2, 0x54, 0x7d, 0x8b, 0x34, 0x67, 0xc1, 0x7b, 0x06, 0x4b, 0x47, + 0x8a, 0x15, 0xef, 0x8c, 0xc1, 0x58, 0x07, 0x0b, 0xdd, 0xc9, 0xe2, 0x7e, 0x95, 0x84, 0x69, 0x24, 0xaa, 0x7c, 0x11, + 0xf2, 0xe7, 0xbf, 0x94, 0xf8, 0xa3, 0xb7, 0x34, 0x10, 0x81, 0xe0, 0x07, 0x68, 0x3d, 0x60, 0x8d, 0x07, 0x3f, 0xb1, + 0xba, 0x0c, 0xf3, 0x2a, 0xa3, 0xf2, 0x66, 0x3b, 0xb6, 0x9d, 0x33, 0x55, 0xb5, 0xe0, 0xb3, 0x30, 0xb4, 0x68, 0x67, + 0xab, 0xe6, 0xe4, 0x36, 0x6f, 0xf0, 0x9d, 0x49, 0x12, 0xa9, 0xa5, 0x24, 0xd2, 0x56, 0xd7, 0xa7, 0x4b, 0xaf, 0x5b, + 0x54, 0xd0, 0x18, 0x01, 0x7a, 0x49, 0xba, 0xab, 0x7c, 0x42, 0xf1, 0xca, 0x6a, 0x58, 0x0d, 0x2f, 0x1d, 0x8a, 0x30, + 0xd6, 0xde, 0x5c, 0xc9, 0xb3, 0x3b, 0xb0, 0x1e, 0xa1, 0xb5, 0xab, 0x52, 0x87, 0xb0, 0xfd, 0x44, 0xef, 0x39, 0x95, + 0xfa, 0x1b, 0x50, 0x05, 0x4e, 0x1d, 0x0d, 0xf5, 0x51, 0x3b, 0x85, 0x6c, 0xe7, 0xde, 0x92, 0xa0, 0x72, 0x25, 0x37, + 0x55, 0x5a, 0x94, 0x52, 0xa6, 0x7c, 0x2d, 0xb3, 0x95, 0xdd, 0x27, 0x03, 0x88, 0x67, 0x83, 0x02, 0xc9, 0x45, 0x6d, + 0x35, 0x07, 0xe9, 0xa3, 0x59, 0xe2, 0x58, 0x3b, 0x28, 0xbc, 0xac, 0x02, 0x33, 0x97, 0xb9, 0x5c, 0x0e, 0x0a, 0x96, + 0xeb, 0xad, 0x66, 0x9a, 0xa9, 0xbe, 0xc8, 0xed, 0x6d, 0xc6, 0xcb, 0xf4, 0xdf, 0x2c, 0x19, 0xf0, 0xe8, 0xe2, 0xb9, + 0x1f, 0x40, 0x9a, 0xe4, 0x75, 0x80, 0x24, 0xd8, 0x1c, 0xec, 0x62, 0x87, 0x61, 0xab, 0x58, 0xd9, 0x93, 0xa7, 0xcb, + 0x1d, 0x9a, 0x72, 0x09, 0x23, 0x39, 0x31, 0x97, 0x52, 0xdf, 0x97, 0x54, 0x37, 0x14, 0x9c, 0x6c, 0x9a, 0x80, 0x52, + 0x40, 0xbb, 0x05, 0xff, 0x85, 0x4f, 0x0d, 0x9d, 0x16, 0x60, 0xa9, 0xed, 0x06, 0xfc, 0x17, 0xfa, 0xc5, 0xf6, 0x11, + 0xf5, 0x03, 0xf3, 0x60, 0x6f, 0xd6, 0x56, 0xc6, 0x80, 0x88, 0xc4, 0x15, 0xe4, 0x91, 0xe0, 0x07, 0xc5, 0x9e, 0x2e, + 0x13, 0x07, 0xce, 0x14, 0x17, 0x4b, 0xa9, 0xcd, 0xcc, 0x6b, 0xbf, 0xa5, 0x26, 0xde, 0x44, 0x49, 0x54, 0xd8, 0x0e, + 0x69, 0xf4, 0x92, 0x32, 0xa6, 0x0a, 0x36, 0x44, 0xf7, 0x75, 0x13, 0x4c, 0x81, 0x37, 0x55, 0x15, 0x10, 0xa1, 0xf6, + 0x22, 0xcb, 0xf3, 0x9b, 0x2e, 0xb0, 0xba, 0xe0, 0x63, 0x63, 0x9a, 0x5d, 0xb0, 0x92, 0xab, 0x99, 0xf4, 0x99, 0xb7, + 0x03, 0x2d, 0xe4, 0x5d, 0x5e, 0x16, 0xad, 0xd0, 0xf5, 0x20, 0x5a, 0xf8, 0x7b, 0xcd, 0xf1, 0xe8, 0xd9, 0xb6, 0x9a, + 0xda, 0xec, 0x6b, 0x2d, 0x16, 0xc8, 0x40, 0x34, 0xf4, 0x85, 0x9c, 0x51, 0xb8, 0xab, 0x34, 0x57, 0xab, 0x7d, 0x55, + 0x06, 0x09, 0x4c, 0x04, 0x59, 0xcb, 0xc2, 0x7b, 0x74, 0xaf, 0x1e, 0x69, 0x5e, 0x49, 0xf0, 0xcc, 0xc5, 0x5f, 0x00, + 0x08, 0xe5, 0x49, 0x42, 0x0e, 0xc8, 0x01, 0xfc, 0x2d, 0x45, 0xa9, 0x34, 0xc0, 0x3f, 0xab, 0xcb, 0xb1, 0xad, 0xef, + 0xef, 0xb4, 0x8a, 0xc1, 0xf5, 0xe7, 0xeb, 0xae, 0x67, 0xed, 0x10, 0xe7, 0xca, 0x56, 0xaf, 0x2d, 0xd3, 0x3c, 0x46, + 0xea, 0x1a, 0x80, 0x3b, 0x91, 0x1e, 0x81, 0x48, 0x66, 0xa2, 0x41, 0xce, 0xae, 0xf9, 0x78, 0x2a, 0x1e, 0x93, 0xf6, + 0x2a, 0xdf, 0x37, 0x17, 0xfa, 0x60, 0x8c, 0x7d, 0x0b, 0x1a, 0xc4, 0x47, 0xab, 0xad, 0x15, 0x88, 0xf5, 0x56, 0xa9, + 0x0f, 0xdd, 0x18, 0x05, 0x1d, 0x3c, 0xe2, 0x46, 0x2e, 0x38, 0xb6, 0xbb, 0xb6, 0x9e, 0xd2, 0x57, 0x00, 0xe6, 0x3a, + 0x50, 0xc9, 0x30, 0x48, 0x9d, 0x27, 0x0a, 0x93, 0xfc, 0x3c, 0x21, 0x09, 0x11, 0xd5, 0xd9, 0x72, 0x94, 0x72, 0xd3, + 0x02, 0x2e, 0x33, 0x32, 0xc0, 0x6c, 0xd2, 0xac, 0x9f, 0x5c, 0xbe, 0x04, 0xa9, 0x34, 0x44, 0x70, 0xc3, 0xf6, 0x92, + 0xd1, 0xad, 0xa3, 0x6e, 0x50, 0x25, 0x99, 0xeb, 0x37, 0xb7, 0xb3, 0x48, 0x99, 0x37, 0x1f, 0x61, 0xac, 0xc9, 0x87, + 0xb0, 0x4e, 0xf0, 0xdb, 0x00, 0x95, 0xf4, 0xa9, 0xf0, 0xa2, 0x11, 0x40, 0xa8, 0xef, 0x54, 0x19, 0x9f, 0x0a, 0x2f, + 0x1b, 0x6d, 0x59, 0x46, 0x29, 0x54, 0x17, 0xcc, 0x6e, 0x4d, 0x17, 0xa2, 0x5b, 0x55, 0x03, 0x6d, 0xe0, 0xda, 0x75, + 0xa0, 0x80, 0x86, 0x6a, 0x57, 0x6e, 0x58, 0x00, 0x56, 0x33, 0x11, 0x18, 0x2e, 0xff, 0x3e, 0x7f, 0xa9, 0x62, 0x78, + 0xfa, 0xfd, 0xd0, 0xdb, 0x6f, 0x83, 0x68, 0xb4, 0xbd, 0x64, 0xbb, 0x20, 0x1a, 0xed, 0x2e, 0x1b, 0x46, 0xbf, 0x9f, + 0xd3, 0xef, 0xe7, 0x0d, 0xe8, 0x48, 0x84, 0x09, 0xb3, 0xd7, 0x6f, 0xd4, 0xf2, 0x95, 0x5a, 0xbf, 0x53, 0xcb, 0x97, + 0x6a, 0x78, 0x6b, 0x4f, 0x12, 0x41, 0x64, 0xa9, 0x6a, 0x1e, 0x24, 0x45, 0xaa, 0xa5, 0xcb, 0x31, 0x5a, 0x8c, 0xa8, + 0xa5, 0xac, 0x39, 0xd6, 0x89, 0xb4, 0x73, 0x50, 0x32, 0xc0, 0xd1, 0xe2, 0xaa, 0xc6, 0x74, 0xb3, 0xa2, 0x25, 0x10, + 0x23, 0xac, 0x6c, 0xcb, 0xc5, 0x4d, 0xea, 0xa3, 0x73, 0xf2, 0x6d, 0xab, 0x94, 0x6f, 0x5b, 0xc1, 0xf3, 0xaf, 0x28, + 0x94, 0x4b, 0xae, 0x5d, 0xcb, 0xa6, 0x85, 0x52, 0x28, 0xe3, 0x1a, 0x6c, 0xed, 0x9b, 0xc0, 0x90, 0xf9, 0x48, 0x51, + 0x63, 0x7b, 0xd1, 0x28, 0x87, 0x20, 0x5b, 0x07, 0xa3, 0x4e, 0x59, 0xb0, 0xf8, 0x76, 0x87, 0x0c, 0x64, 0xa0, 0xa3, + 0xaa, 0x8d, 0x57, 0x3b, 0x2b, 0xfd, 0x61, 0x79, 0xf1, 0x9c, 0x25, 0x56, 0x3a, 0xf9, 0x4d, 0x85, 0xfe, 0x20, 0x44, + 0xdf, 0x94, 0x0d, 0x07, 0x2f, 0xba, 0xd8, 0xca, 0x80, 0x78, 0xc3, 0xf4, 0xde, 0xc6, 0x4a, 0x96, 0xbb, 0xa6, 0x7c, + 0x31, 0xe3, 0x09, 0xc7, 0xd1, 0x97, 0xab, 0x45, 0x58, 0xab, 0x45, 0x76, 0x02, 0x3c, 0xb4, 0x56, 0x4b, 0x21, 0x57, + 0x8b, 0x70, 0x66, 0xba, 0x50, 0x33, 0x3d, 0x03, 0xcd, 0xa3, 0x50, 0xb3, 0x3c, 0x01, 0x2c, 0x78, 0x61, 0x66, 0xb8, + 0x30, 0x33, 0x1c, 0x87, 0xd4, 0x38, 0x3d, 0xe8, 0xbd, 0xce, 0x3d, 0xb7, 0xdc, 0x8d, 0x4e, 0xc3, 0xbc, 0x1d, 0x6d, + 0x30, 0xc7, 0x07, 0xe1, 0x04, 0xe2, 0x03, 0x4b, 0x04, 0xe8, 0xd1, 0xb0, 0x3a, 0x6a, 0xa8, 0x1c, 0xc5, 0x97, 0x05, + 0x20, 0x59, 0x12, 0x80, 0xe4, 0x5e, 0x8d, 0x73, 0x69, 0xf9, 0x75, 0x95, 0x84, 0x1c, 0x91, 0xf1, 0x52, 0xda, 0xdd, + 0x13, 0x5e, 0x8e, 0x8c, 0xd0, 0x3c, 0x59, 0xa4, 0x5e, 0xce, 0x32, 0x36, 0x46, 0xe0, 0xa2, 0xd0, 0x6f, 0xaa, 0x7e, + 0x3f, 0x2d, 0xbd, 0x9c, 0xda, 0xf9, 0x09, 0xfc, 0x2d, 0x4f, 0x9d, 0x45, 0x8e, 0x90, 0x57, 0x23, 0x93, 0xb0, 0xbc, + 0x54, 0xea, 0xe9, 0x4b, 0x98, 0x41, 0xdd, 0xbd, 0x51, 0x00, 0xae, 0x45, 0x2e, 0x9d, 0x6a, 0x4b, 0xb8, 0x32, 0xe5, + 0x06, 0xfb, 0x3c, 0xe4, 0x39, 0x09, 0xa1, 0x12, 0x79, 0xa4, 0xb0, 0xee, 0xdb, 0x17, 0xcf, 0x27, 0xae, 0x0f, 0x8b, + 0x8d, 0x46, 0x70, 0x38, 0x00, 0xcc, 0xc1, 0xd4, 0x8b, 0x06, 0xbc, 0x54, 0x73, 0xe6, 0xa3, 0x97, 0x13, 0x36, 0x06, + 0xa8, 0x29, 0x06, 0x4e, 0x59, 0xcf, 0xe4, 0x23, 0xe3, 0x5b, 0xe6, 0xfb, 0x01, 0xbe, 0x5b, 0x17, 0x12, 0xf2, 0x41, + 0xa1, 0x12, 0x64, 0x0a, 0x95, 0x20, 0x31, 0xa8, 0x04, 0xb1, 0x41, 0x25, 0xd8, 0x34, 0x7c, 0x2d, 0x95, 0xb7, 0x11, + 0x70, 0x44, 0xf8, 0xd0, 0xb3, 0xb0, 0xb1, 0x42, 0xf1, 0x6c, 0xcc, 0xc6, 0xac, 0x50, 0x3b, 0x4f, 0x2e, 0xa7, 0x62, + 0x67, 0x31, 0xd6, 0x4d, 0x64, 0x99, 0x78, 0x21, 0x41, 0xc7, 0x39, 0x17, 0x12, 0x75, 0xf5, 0x73, 0xef, 0x25, 0x19, + 0x4b, 0xe6, 0x0d, 0x8d, 0x1a, 0xcc, 0xcb, 0xae, 0x03, 0x98, 0x96, 0x7c, 0x5b, 0xd0, 0x60, 0x3a, 0x55, 0x1e, 0x91, + 0x26, 0x41, 0xed, 0x5c, 0x26, 0x45, 0x4e, 0x08, 0x93, 0xa0, 0x57, 0x82, 0xdf, 0x48, 0x68, 0xff, 0xaf, 0x7a, 0xbe, + 0x03, 0x06, 0x13, 0xad, 0x92, 0x2f, 0x60, 0xb5, 0xcc, 0xf9, 0x0b, 0xe9, 0x89, 0x8d, 0xf8, 0x8b, 0x65, 0x1a, 0x8f, + 0xbe, 0xb0, 0x21, 0xe2, 0x59, 0xbd, 0x40, 0xd3, 0x12, 0xd4, 0x01, 0x1e, 0xd1, 0x5f, 0xa3, 0x2f, 0x86, 0x37, 0xa5, + 0xab, 0x91, 0xba, 0x66, 0xe7, 0x9c, 0x7f, 0xa9, 0x0d, 0x11, 0x32, 0xa6, 0x4d, 0x81, 0x64, 0x40, 0x20, 0xc9, 0x40, + 0x00, 0x60, 0x6a, 0x3a, 0xb3, 0x57, 0x00, 0xd1, 0x40, 0x00, 0x8f, 0xf3, 0x8e, 0xc7, 0x8f, 0xf4, 0x57, 0x71, 0xdc, + 0x3b, 0x4d, 0xc3, 0xf6, 0x5f, 0x80, 0xa6, 0x18, 0xca, 0xf1, 0x7c, 0xa7, 0x20, 0xd9, 0xa3, 0x94, 0xa5, 0xab, 0x26, + 0xb2, 0x43, 0xb1, 0x3e, 0xcd, 0x29, 0x0b, 0x69, 0x5b, 0x8e, 0xd1, 0x16, 0xeb, 0xc7, 0xc8, 0x7b, 0x73, 0xa3, 0x22, + 0x1f, 0xf4, 0xe0, 0xf6, 0xf6, 0xe6, 0x55, 0x8f, 0xd9, 0x24, 0x2b, 0x16, 0xb9, 0x8a, 0x38, 0x71, 0x5a, 0x87, 0x1c, + 0x30, 0x20, 0x27, 0x21, 0x30, 0x8d, 0x71, 0xa9, 0x40, 0x07, 0x25, 0xcb, 0x79, 0x0d, 0xd4, 0xb2, 0x88, 0xac, 0x01, + 0xa2, 0x9a, 0xe6, 0x5f, 0x35, 0xe4, 0x27, 0x55, 0x73, 0x4a, 0xa1, 0xf6, 0x15, 0x0f, 0xab, 0xd3, 0x27, 0x56, 0x6d, + 0x62, 0xac, 0x7f, 0xad, 0x3d, 0x41, 0x5b, 0x49, 0x03, 0xf1, 0x9d, 0xaf, 0xd2, 0x3b, 0x0a, 0xdd, 0x71, 0x66, 0xe2, + 0xa9, 0x0a, 0x8c, 0x7d, 0x6b, 0x47, 0x50, 0x38, 0x34, 0x5d, 0x07, 0x1c, 0xa6, 0xd1, 0x09, 0x8b, 0x7f, 0x4a, 0xc7, + 0xc9, 0x8b, 0x5a, 0x21, 0x92, 0xfc, 0x43, 0xb8, 0x30, 0x24, 0x16, 0xe4, 0x25, 0xa1, 0x8e, 0xc8, 0x88, 0xd5, 0xa8, + 0x58, 0x0b, 0x15, 0x15, 0xa7, 0x78, 0xbc, 0x55, 0x50, 0x5c, 0x8a, 0x52, 0xa5, 0x54, 0xe4, 0x46, 0xa5, 0x80, 0x58, + 0x36, 0xf0, 0x6e, 0x01, 0x07, 0x40, 0xd0, 0x59, 0xee, 0xd6, 0xb6, 0xbb, 0x8d, 0xcc, 0x67, 0xa6, 0x79, 0x5a, 0x7d, + 0x50, 0x7f, 0xbf, 0x5f, 0x62, 0x6c, 0x8d, 0xa7, 0xbf, 0x6f, 0xd3, 0x82, 0x9b, 0xbf, 0x61, 0x88, 0xee, 0x00, 0x11, + 0xb3, 0xb4, 0x87, 0x42, 0x16, 0x4c, 0x58, 0x86, 0xaa, 0x3c, 0xe5, 0xa8, 0x97, 0x4f, 0x6e, 0x01, 0x42, 0x0d, 0xfd, + 0xda, 0xe8, 0x54, 0x57, 0x25, 0x08, 0xdf, 0x77, 0x85, 0x7a, 0x6c, 0x0e, 0x78, 0x32, 0x00, 0xfe, 0x8a, 0xbc, 0xd6, + 0x63, 0xfb, 0x07, 0xbd, 0x51, 0x6f, 0x80, 0x20, 0x3a, 0xe7, 0x85, 0x7f, 0xc4, 0xb9, 0x4e, 0xfd, 0x19, 0x17, 0x82, + 0xf8, 0xd6, 0x93, 0xf0, 0x5e, 0x9c, 0xa5, 0x71, 0x70, 0xd6, 0x1b, 0x98, 0x8b, 0x40, 0x71, 0x96, 0xe6, 0x67, 0x20, + 0x96, 0x23, 0x26, 0x62, 0xcd, 0xee, 0x00, 0x26, 0xb0, 0xd4, 0x71, 0xc8, 0xaa, 0x63, 0xfb, 0xfd, 0xd7, 0x23, 0x43, + 0x96, 0x8e, 0x30, 0x30, 0xfa, 0x77, 0x05, 0x02, 0x14, 0x2c, 0x33, 0xdb, 0x83, 0x49, 0x57, 0x7b, 0x56, 0xcf, 0x9b, + 0x4d, 0xde, 0xd5, 0x3b, 0x56, 0xd3, 0x72, 0x6a, 0x5a, 0x65, 0x35, 0x6d, 0x92, 0x43, 0xcd, 0x44, 0xbf, 0xaf, 0x41, + 0x51, 0xf3, 0x39, 0x80, 0xb1, 0x61, 0xf2, 0xeb, 0x59, 0x35, 0xef, 0xf7, 0x3d, 0xf9, 0x08, 0x7e, 0x21, 0x5b, 0x99, + 0x5b, 0x63, 0xf9, 0xf4, 0x15, 0x91, 0x98, 0x19, 0x98, 0xa3, 0xbb, 0x23, 0x7c, 0xaf, 0x1b, 0xe1, 0x75, 0xcc, 0x15, + 0x36, 0x13, 0xd3, 0xd7, 0x30, 0x78, 0x9e, 0xf0, 0xc1, 0x45, 0x8e, 0xfe, 0x46, 0x0e, 0x33, 0x85, 0x05, 0x39, 0xf7, + 0x27, 0xaf, 0x11, 0x2f, 0x19, 0xe1, 0x1d, 0x74, 0x3a, 0xe1, 0x41, 0xf6, 0xfb, 0x2b, 0xe8, 0xcc, 0x56, 0x2a, 0x65, + 0xab, 0xa2, 0x32, 0x5d, 0xd7, 0x45, 0x59, 0x41, 0xc7, 0xd2, 0xcf, 0x5b, 0x21, 0x33, 0xeb, 0x67, 0x16, 0xdc, 0xd3, + 0x4a, 0x02, 0x4c, 0xd9, 0xb6, 0x89, 0xda, 0xc0, 0xcb, 0xba, 0xf8, 0x5c, 0xe0, 0xd1, 0x59, 0x7b, 0xbd, 0x11, 0x6a, + 0x9f, 0xf3, 0xd1, 0xba, 0x58, 0x7b, 0xe0, 0x07, 0x33, 0x4b, 0xe7, 0x8a, 0x38, 0x23, 0xf7, 0x47, 0x9f, 0x8b, 0x34, + 0xa7, 0x3c, 0xc0, 0x7d, 0x28, 0xe6, 0xf6, 0x5b, 0x20, 0xfd, 0xd0, 0x5b, 0x20, 0xfb, 0xe8, 0x9c, 0x93, 0xd7, 0x80, + 0x48, 0x87, 0x30, 0xb8, 0x15, 0x09, 0x3a, 0x56, 0x0d, 0x6f, 0x2d, 0xb0, 0xd3, 0x5e, 0x1a, 0xf7, 0xd2, 0xfc, 0x2c, + 0xed, 0xf7, 0x0d, 0x6a, 0x66, 0x8a, 0x70, 0xf0, 0x38, 0x23, 0x17, 0x49, 0x0b, 0xb6, 0x94, 0xf6, 0x5f, 0x0d, 0x1c, + 0x41, 0xc8, 0xdf, 0xff, 0x10, 0xde, 0x13, 0x80, 0xd8, 0xa4, 0x0d, 0xb8, 0xea, 0x31, 0x1d, 0x8d, 0x2d, 0x89, 0x5a, + 0x75, 0x36, 0x40, 0xe2, 0x54, 0x69, 0x3d, 0xe5, 0x66, 0x4d, 0x61, 0x90, 0x2a, 0x0b, 0xf5, 0x1b, 0xeb, 0xc9, 0x64, + 0x95, 0x8b, 0x8c, 0x38, 0x2a, 0xd3, 0x97, 0x9a, 0x11, 0x4c, 0x97, 0x7e, 0xbe, 0x80, 0x25, 0x1b, 0x7f, 0xc4, 0xc9, + 0x5b, 0x02, 0x8e, 0xed, 0xac, 0x5d, 0x55, 0xbb, 0x1c, 0xb7, 0x76, 0x73, 0x80, 0xef, 0xf5, 0x46, 0xa3, 0x91, 0x76, + 0x8e, 0x13, 0x30, 0x54, 0x3d, 0xb5, 0x14, 0x7a, 0xac, 0x56, 0x80, 0xba, 0x1d, 0xb9, 0xcc, 0x92, 0xc1, 0x7c, 0x61, + 0x1c, 0xbf, 0x34, 0x1f, 0x7d, 0xbc, 0x54, 0xd6, 0xae, 0x23, 0xbe, 0xfe, 0x83, 0xac, 0xd6, 0xb7, 0xbc, 0xab, 0x9a, + 0x80, 0x2f, 0xaa, 0x80, 0xd2, 0x6f, 0x78, 0x4f, 0xf6, 0x2e, 0xbe, 0x76, 0x83, 0x5d, 0xf2, 0x2d, 0x6f, 0x51, 0xe7, + 0xf9, 0xca, 0xc1, 0x8d, 0x2a, 0xdd, 0xde, 0x4b, 0x16, 0xb8, 0xf6, 0x8e, 0x9a, 0xc6, 0x7a, 0xe6, 0x47, 0x0f, 0x8b, + 0x90, 0xed, 0x7c, 0xec, 0x7d, 0xd5, 0x3c, 0x3d, 0x6b, 0xe8, 0x4d, 0x6a, 0xe8, 0x63, 0x2f, 0xca, 0xf6, 0xa9, 0x69, + 0x44, 0xaf, 0x61, 0x43, 0x1f, 0x7b, 0x4b, 0x4e, 0x0e, 0x89, 0x00, 0xa7, 0xc6, 0xfc, 0xf1, 0xe1, 0x74, 0x86, 0xbf, + 0x63, 0x40, 0x25, 0x10, 0xf3, 0xe9, 0x31, 0xed, 0x28, 0xc0, 0x8c, 0x2a, 0xbd, 0x7d, 0x7a, 0x60, 0x3b, 0x5e, 0xd6, + 0x43, 0x4b, 0xef, 0x9e, 0x1c, 0xdd, 0x8e, 0x57, 0xd5, 0xf8, 0x52, 0x0e, 0x79, 0x9e, 0xcf, 0x46, 0xa3, 0x91, 0x30, + 0x90, 0xdc, 0x95, 0xde, 0xc0, 0x0a, 0xa4, 0x6d, 0x51, 0x7d, 0x28, 0x97, 0xde, 0x4e, 0x1d, 0xda, 0x95, 0x3f, 0xc9, + 0x0f, 0x87, 0x62, 0x64, 0x8e, 0x71, 0x00, 0x37, 0x29, 0x94, 0x1c, 0x25, 0x6b, 0x09, 0xa2, 0x53, 0x1a, 0x4f, 0x65, + 0xbd, 0xb6, 0x22, 0xf2, 0x6a, 0xc4, 0x79, 0x08, 0x7e, 0xf4, 0x40, 0x2d, 0x7e, 0xad, 0x05, 0xb1, 0xc7, 0x3e, 0x55, + 0x4a, 0x2f, 0x78, 0x55, 0x40, 0x88, 0xd8, 0xdf, 0x0d, 0xb4, 0x83, 0x12, 0x1c, 0x4a, 0xb8, 0x0f, 0x08, 0x0b, 0xfd, + 0xca, 0xcb, 0x67, 0x32, 0x46, 0xb9, 0x37, 0xa8, 0xe6, 0x0c, 0x60, 0x2a, 0x7d, 0x06, 0x7e, 0x97, 0x00, 0x75, 0x8a, + 0x4f, 0xd1, 0xa9, 0xde, 0x3c, 0x6c, 0xba, 0x3e, 0x2d, 0x51, 0x14, 0xd1, 0x9d, 0x9f, 0x8f, 0x01, 0xb1, 0xb3, 0x6b, + 0x33, 0xd2, 0xae, 0xfd, 0x06, 0x0d, 0x56, 0x4a, 0x12, 0xed, 0x9c, 0x12, 0x76, 0x3b, 0x1f, 0xd9, 0xd2, 0x8f, 0x52, + 0x20, 0xe6, 0x8e, 0x13, 0x89, 0xec, 0xc1, 0x46, 0x4e, 0xe0, 0x16, 0xed, 0x1d, 0x1d, 0x80, 0xca, 0x8d, 0x82, 0xfc, + 0x6a, 0x8e, 0xe4, 0x8e, 0xef, 0x7a, 0xdf, 0x0d, 0xea, 0xc1, 0x77, 0xbd, 0xb3, 0x94, 0xe4, 0x8e, 0xf0, 0x4c, 0x4d, + 0x09, 0x11, 0x9f, 0x7d, 0x37, 0xc8, 0x07, 0x78, 0x96, 0x68, 0x91, 0x16, 0x09, 0xd5, 0xea, 0x1a, 0x37, 0xe1, 0x45, + 0x22, 0xb9, 0x87, 0x76, 0x9d, 0x47, 0xc4, 0x02, 0x90, 0xb1, 0xf8, 0x6c, 0xde, 0x50, 0xa8, 0xbb, 0x89, 0xd9, 0xa2, + 0xbb, 0x2c, 0xf6, 0xfb, 0x9b, 0x3c, 0xad, 0x7b, 0x3a, 0x3e, 0x06, 0x5f, 0x90, 0x6a, 0x02, 0x3c, 0xda, 0x5f, 0x99, + 0xe3, 0xd5, 0xab, 0xcd, 0x91, 0xb2, 0x50, 0x25, 0xea, 0xb7, 0x58, 0xcd, 0x7a, 0x08, 0xc3, 0x9d, 0x65, 0xc6, 0xda, + 0x5e, 0xf0, 0x4a, 0xce, 0xaa, 0xd8, 0x2e, 0xc7, 0x57, 0x2c, 0xb5, 0x95, 0x44, 0xe5, 0x68, 0x3d, 0xd6, 0xa6, 0x18, + 0xf9, 0x95, 0x42, 0xa2, 0x2c, 0x3a, 0xb6, 0x16, 0x0a, 0x88, 0x17, 0xa0, 0x2f, 0xd9, 0x99, 0x06, 0x58, 0x6f, 0xf4, + 0x2a, 0x22, 0xb4, 0x7c, 0xa4, 0xc2, 0x9b, 0xdc, 0x54, 0x99, 0x95, 0xcd, 0xa2, 0xdd, 0x4f, 0x15, 0xaf, 0x10, 0xac, + 0xde, 0xa8, 0x3d, 0x0a, 0x50, 0x7b, 0x68, 0xa1, 0x0c, 0x20, 0xa5, 0x69, 0x06, 0x80, 0x0c, 0x00, 0x32, 0x55, 0xc4, + 0x67, 0x02, 0x54, 0xda, 0xea, 0x46, 0x81, 0x13, 0xe9, 0x15, 0xd0, 0x2c, 0xb0, 0xd2, 0x47, 0x0a, 0x32, 0x58, 0x6c, + 0x11, 0x80, 0x95, 0x23, 0x67, 0x98, 0xc6, 0x90, 0x6d, 0x34, 0x71, 0x49, 0x9a, 0xdf, 0x87, 0x59, 0x2a, 0xf1, 0x24, + 0x7e, 0x90, 0x35, 0x46, 0x00, 0x20, 0x7d, 0x9f, 0x5e, 0x14, 0x59, 0x4c, 0x38, 0x70, 0xd6, 0x53, 0x07, 0x45, 0x4d, + 0xce, 0xb5, 0xa6, 0xd5, 0xb3, 0xda, 0xe4, 0x21, 0x0b, 0x74, 0xf6, 0x60, 0x4c, 0x6a, 0xf9, 0x9e, 0x47, 0xf6, 0x57, + 0x8e, 0x67, 0x84, 0xef, 0xba, 0x83, 0x53, 0xff, 0xdd, 0xd4, 0xc0, 0xc4, 0x94, 0x00, 0x6c, 0x0c, 0x8e, 0x26, 0xc4, + 0xef, 0x74, 0x4c, 0xa6, 0x36, 0x29, 0x02, 0x81, 0x87, 0xe0, 0x15, 0x3c, 0x37, 0x5c, 0x6e, 0xb9, 0xb1, 0xb3, 0xc8, + 0xd3, 0x04, 0xe0, 0xc4, 0x0b, 0xbe, 0x05, 0x38, 0x4e, 0xbd, 0x2a, 0x64, 0xcf, 0x9e, 0x8b, 0xe9, 0x6c, 0x1e, 0x3c, + 0x24, 0xb4, 0x7f, 0x31, 0xe1, 0x37, 0xdd, 0x55, 0x72, 0x65, 0x6a, 0xdd, 0x9b, 0xe8, 0x2a, 0x97, 0x3b, 0x7d, 0x5a, + 0x71, 0x0c, 0x73, 0x06, 0xab, 0x80, 0x9c, 0xb3, 0x21, 0xbf, 0x3e, 0x07, 0xc0, 0x96, 0x95, 0xf0, 0x22, 0x7e, 0x1d, + 0xca, 0x6a, 0x01, 0xdc, 0x23, 0xe7, 0x91, 0xf9, 0xe5, 0xab, 0xed, 0x50, 0xce, 0x29, 0x0a, 0x63, 0x39, 0x35, 0x2d, + 0x29, 0x4e, 0x87, 0x9e, 0x82, 0xc9, 0xd4, 0x96, 0xbf, 0xb7, 0x89, 0xcb, 0xec, 0xcd, 0x24, 0x9c, 0xaf, 0x23, 0xdb, + 0xd6, 0xaa, 0x7b, 0xe8, 0x86, 0x60, 0xd0, 0xc7, 0x08, 0x5a, 0x36, 0xd7, 0x77, 0xeb, 0xc1, 0x40, 0x61, 0xfb, 0xd6, + 0x74, 0xd3, 0xa2, 0x53, 0x1c, 0x70, 0x66, 0xad, 0x6b, 0x54, 0xaa, 0x8a, 0x43, 0x2f, 0x79, 0xb7, 0xac, 0xca, 0x2e, + 0x4b, 0x2f, 0x04, 0xa9, 0x51, 0x57, 0x11, 0x22, 0xa5, 0x62, 0x87, 0xf7, 0xe4, 0xd7, 0xc0, 0xc4, 0x33, 0x2b, 0x47, + 0x69, 0x3c, 0x07, 0x98, 0x20, 0x85, 0xbe, 0x29, 0xbf, 0x02, 0xdc, 0xd0, 0x45, 0x14, 0x66, 0x6f, 0xe2, 0x2a, 0xa8, + 0xad, 0xa6, 0xdf, 0x3b, 0x38, 0xb1, 0xe7, 0x75, 0xbf, 0x9f, 0x12, 0x8d, 0x1f, 0x86, 0x5e, 0xe0, 0xdf, 0xe3, 0xe9, + 0xbe, 0x09, 0x52, 0xf3, 0xca, 0x03, 0xbc, 0xa2, 0xcb, 0xad, 0x4d, 0xb9, 0xa2, 0x71, 0x31, 0xaf, 0x11, 0x11, 0x3e, + 0x75, 0x14, 0xdb, 0x6d, 0x7e, 0x9c, 0xda, 0x18, 0x0c, 0x42, 0xb8, 0x6f, 0x65, 0xfc, 0x3e, 0xf1, 0xf2, 0x59, 0x34, + 0x07, 0x45, 0x69, 0xa6, 0x49, 0x42, 0x0a, 0xe9, 0x25, 0x40, 0x1f, 0x0d, 0x42, 0xad, 0xae, 0xfc, 0x23, 0xf1, 0x52, + 0x35, 0xad, 0xcd, 0x53, 0xac, 0x51, 0x20, 0x66, 0xd1, 0xbc, 0x61, 0x19, 0x1d, 0x92, 0xea, 0x72, 0x69, 0x9a, 0xf1, + 0x87, 0xd5, 0x0c, 0xd5, 0x8a, 0xa3, 0x26, 0xa8, 0x51, 0xba, 0x81, 0x0b, 0xe0, 0xdf, 0xe9, 0x8e, 0xa3, 0x1a, 0x45, + 0x8a, 0x06, 0x7c, 0x82, 0xc0, 0xb0, 0x66, 0xf3, 0x84, 0xb5, 0xa6, 0xae, 0x19, 0xfd, 0xbe, 0x8c, 0x13, 0x32, 0x49, + 0x48, 0xce, 0x87, 0xcb, 0xf5, 0x23, 0xa9, 0x2e, 0x80, 0x54, 0xb9, 0x62, 0xb3, 0x5e, 0x6f, 0x0e, 0x18, 0xbd, 0xb0, + 0x7e, 0x61, 0xe3, 0x0a, 0xce, 0x2f, 0x09, 0x73, 0x57, 0xfd, 0x08, 0xb3, 0x0c, 0xaa, 0x80, 0x34, 0x3f, 0x16, 0xbc, + 0x79, 0xee, 0x02, 0x51, 0xbf, 0x1e, 0xa9, 0x0b, 0xca, 0x2c, 0x9d, 0x5b, 0x44, 0x20, 0xe0, 0x35, 0xac, 0x9e, 0x40, + 0xb2, 0x2f, 0x1f, 0xfb, 0x34, 0xa3, 0x40, 0x75, 0x04, 0xa0, 0x6c, 0xd6, 0x0f, 0x61, 0xff, 0x80, 0x70, 0x42, 0xfd, + 0xcd, 0x1b, 0x39, 0x6b, 0x48, 0x1e, 0x48, 0x35, 0xe1, 0x31, 0x9c, 0x1a, 0x0b, 0x7c, 0x69, 0xd1, 0x9b, 0x0a, 0x5e, + 0x13, 0x1c, 0xf7, 0x02, 0xad, 0x7d, 0x0b, 0x38, 0x42, 0x04, 0x97, 0xa1, 0x89, 0xd3, 0xde, 0xae, 0x17, 0x20, 0xa1, + 0xb9, 0x85, 0x73, 0xfd, 0xd6, 0x05, 0x2d, 0x4e, 0x91, 0x93, 0x45, 0x17, 0x18, 0xe8, 0x82, 0xcc, 0x1b, 0xff, 0xaa, + 0x60, 0xe5, 0x02, 0x64, 0x2f, 0x15, 0x2b, 0x89, 0xd8, 0x76, 0xea, 0x8f, 0x52, 0xd9, 0x6f, 0xcf, 0xac, 0x09, 0xfc, + 0x32, 0xb1, 0x5f, 0x22, 0x93, 0x6f, 0x7a, 0x6c, 0xf2, 0x95, 0xb1, 0xd0, 0xa9, 0x65, 0x70, 0x4e, 0x8f, 0x0c, 0xce, + 0xbd, 0x9d, 0x55, 0x9b, 0x10, 0x86, 0x82, 0x24, 0xd0, 0x74, 0xe9, 0x61, 0xdd, 0xf4, 0xe7, 0x27, 0x2d, 0x7e, 0xad, + 0xda, 0xb7, 0xee, 0xc7, 0x21, 0x76, 0xf1, 0xcb, 0xc4, 0x33, 0xec, 0xa3, 0x3e, 0x70, 0x80, 0xc9, 0x88, 0x89, 0xcb, + 0x7e, 0x1f, 0x0a, 0x9b, 0x8d, 0xe7, 0xa3, 0xba, 0xf8, 0xb9, 0x78, 0x00, 0x28, 0x87, 0x0a, 0xec, 0x72, 0x28, 0x43, + 0x19, 0xb1, 0xa9, 0x2d, 0xf7, 0xfc, 0xfe, 0x32, 0xcc, 0x41, 0xde, 0xd1, 0x98, 0x38, 0x67, 0x20, 0x86, 0xc1, 0xd7, + 0xbf, 0x7b, 0xb2, 0x4f, 0x9b, 0xef, 0xce, 0xe0, 0xbb, 0xa3, 0xb3, 0x0f, 0xc8, 0x71, 0x73, 0xb6, 0x2e, 0x8b, 0xfb, + 0x34, 0x16, 0x67, 0xdf, 0x41, 0xea, 0x77, 0x67, 0x45, 0x79, 0xf6, 0x9d, 0xaa, 0xcc, 0x77, 0x67, 0xb4, 0xe0, 0x46, + 0xbf, 0x5b, 0x13, 0xef, 0x9f, 0x95, 0xa6, 0x3d, 0x5b, 0x42, 0x38, 0x96, 0x56, 0x3f, 0x82, 0x12, 0x51, 0x91, 0xa2, + 0xca, 0x50, 0x56, 0x6b, 0xc7, 0x79, 0x9f, 0x68, 0x78, 0x6c, 0x9a, 0x90, 0xb8, 0x5a, 0xc2, 0x3a, 0xd4, 0xb3, 0xd3, + 0x26, 0xd9, 0x71, 0x1e, 0xa8, 0x03, 0x22, 0xe7, 0xd7, 0xf9, 0x68, 0x4b, 0x5f, 0x83, 0x6f, 0x1d, 0x0e, 0xf9, 0x68, + 0x67, 0x7e, 0xfa, 0x64, 0xad, 0x94, 0xc1, 0x46, 0x8a, 0x51, 0x08, 0x89, 0xe2, 0xb6, 0x3d, 0x06, 0xc0, 0xff, 0xfe, + 0xe1, 0x40, 0xbf, 0x77, 0xf2, 0xb7, 0xda, 0x2d, 0xad, 0x7a, 0x7e, 0x68, 0x11, 0x66, 0xbc, 0xaa, 0x0d, 0x3b, 0xdb, + 0x5e, 0x02, 0x4a, 0xef, 0x9b, 0x06, 0x35, 0x45, 0xf4, 0x13, 0x56, 0x13, 0xab, 0x38, 0x2c, 0x48, 0x89, 0x43, 0x0c, + 0xc7, 0x68, 0x87, 0x1e, 0xa7, 0x8b, 0x9a, 0x27, 0xf7, 0x1d, 0x32, 0x6e, 0x7d, 0x1f, 0x90, 0x5c, 0x0a, 0xe7, 0x1f, + 0xbc, 0xd0, 0x60, 0xa2, 0x17, 0x79, 0x55, 0x64, 0x62, 0x24, 0x68, 0x94, 0xdf, 0x90, 0x38, 0x73, 0x86, 0xb5, 0x38, + 0x53, 0x08, 0x61, 0x21, 0xa1, 0x72, 0x17, 0x25, 0xa5, 0x07, 0x67, 0x4f, 0xf6, 0x65, 0xf3, 0x3b, 0x61, 0x42, 0x8c, + 0x16, 0x40, 0x83, 0xb3, 0x6b, 0x97, 0xf7, 0x10, 0x96, 0xb9, 0xf7, 0xfb, 0x9b, 0xbb, 0xbc, 0x80, 0xb8, 0xcc, 0x33, + 0xa9, 0x58, 0x2d, 0xcf, 0x80, 0x26, 0x4f, 0xc4, 0x67, 0x61, 0x25, 0xa7, 0x41, 0xd5, 0x51, 0xac, 0xde, 0xc6, 0x73, + 0x0f, 0x78, 0xbd, 0xdf, 0x27, 0x40, 0xe0, 0xee, 0xb3, 0xd7, 0xca, 0x2d, 0x95, 0xf4, 0xc8, 0x73, 0x0c, 0x91, 0x4c, + 0x80, 0xd7, 0x19, 0x82, 0x23, 0x85, 0xd5, 0x73, 0x13, 0xe4, 0x1f, 0x5f, 0x9f, 0x50, 0x7c, 0xd1, 0x3c, 0x8a, 0x1a, + 0x16, 0xb2, 0x04, 0x8e, 0x87, 0x64, 0x96, 0xcd, 0x91, 0x9a, 0x3c, 0x6d, 0x4f, 0x91, 0x8e, 0x4e, 0x2c, 0xf1, 0xdb, + 0x9a, 0x54, 0x2f, 0x52, 0x61, 0x97, 0xb4, 0xb3, 0x95, 0xb9, 0x17, 0xc2, 0x50, 0x25, 0xdc, 0x7b, 0x55, 0xcf, 0x42, + 0xb9, 0x29, 0x5a, 0x15, 0xb3, 0x87, 0x29, 0x31, 0xc3, 0x14, 0xeb, 0x2f, 0x6c, 0xf8, 0x4d, 0xe2, 0xc5, 0x60, 0xb8, + 0x5e, 0xf2, 0x72, 0xb6, 0x31, 0x0b, 0xe1, 0x70, 0xd8, 0x4c, 0x8a, 0xd9, 0x12, 0x62, 0x5b, 0x97, 0xf3, 0xc3, 0xa1, + 0xab, 0x65, 0x6b, 0xe1, 0xc1, 0x43, 0xd5, 0xc2, 0x4d, 0xc3, 0x72, 0xf8, 0x99, 0xcc, 0x62, 0x6c, 0x5f, 0xe3, 0x33, + 0xfb, 0xf3, 0x45, 0xf7, 0x2c, 0x41, 0xc6, 0x8d, 0x35, 0x70, 0x8d, 0xcd, 0xda, 0x1d, 0xae, 0x46, 0x40, 0xf2, 0xb8, + 0x1b, 0xfd, 0x5d, 0xd9, 0x49, 0x4e, 0x82, 0x84, 0xd1, 0x0a, 0xe1, 0x77, 0xdf, 0xf8, 0x13, 0x2d, 0xf6, 0xa0, 0xdd, + 0xc6, 0x96, 0x10, 0xd5, 0xb4, 0xe7, 0x72, 0xa5, 0x58, 0x9a, 0xb7, 0xd2, 0x86, 0xcc, 0x87, 0xf5, 0xb9, 0x6f, 0xe4, + 0x40, 0xc1, 0x18, 0xf1, 0xd4, 0x3a, 0x88, 0x66, 0x73, 0xe0, 0xbe, 0x40, 0xf3, 0x08, 0x4f, 0x2d, 0x48, 0x50, 0x66, + 0x6d, 0xd8, 0x4f, 0x92, 0x93, 0xe5, 0x71, 0xf8, 0x16, 0xfe, 0xe5, 0x33, 0x6c, 0x12, 0x53, 0x14, 0x8f, 0xbf, 0x55, + 0x8a, 0xff, 0x8e, 0x2d, 0x88, 0x60, 0xed, 0x46, 0xd4, 0x86, 0xbf, 0xe1, 0xdf, 0xc2, 0x3e, 0xc2, 0x7e, 0x43, 0x13, + 0x84, 0x01, 0xac, 0x3f, 0x13, 0x88, 0x0b, 0x0b, 0x41, 0x82, 0xbf, 0x55, 0x92, 0x7f, 0x4e, 0xf8, 0x6c, 0x51, 0x02, + 0x59, 0x1d, 0x46, 0xf1, 0x09, 0xc5, 0x44, 0x21, 0x0c, 0xb7, 0x84, 0xde, 0xd1, 0x7f, 0x23, 0x4a, 0xb2, 0x49, 0x6e, + 0xc5, 0x7a, 0x20, 0x93, 0x24, 0x98, 0x60, 0xe5, 0x85, 0xf2, 0x85, 0x7b, 0xa1, 0xd4, 0x5a, 0x0b, 0x5a, 0xbf, 0xfc, + 0x49, 0xe2, 0x19, 0xd0, 0x3d, 0x90, 0x31, 0xe8, 0x36, 0xa2, 0x9a, 0xe4, 0x98, 0x3e, 0x4a, 0xe7, 0x19, 0xa8, 0x80, + 0xce, 0xd6, 0x59, 0x58, 0x2f, 0x8b, 0x72, 0xd5, 0x0a, 0x0f, 0x95, 0xa5, 0x8f, 0xd4, 0x63, 0xcc, 0x0b, 0xf3, 0xe4, + 0x44, 0x3e, 0x78, 0x04, 0x68, 0x78, 0x94, 0xa7, 0x55, 0x47, 0x69, 0xfd, 0xc0, 0x32, 0x60, 0x04, 0x4e, 0x94, 0x01, + 0x8f, 0xb0, 0x0c, 0xcc, 0xd3, 0x2e, 0x43, 0x0d, 0x62, 0x8d, 0xaa, 0x2b, 0xb5, 0xc1, 0x9c, 0x28, 0x4a, 0x3e, 0xc5, + 0xd2, 0x0a, 0x63, 0x68, 0xea, 0xca, 0x23, 0xeb, 0x25, 0x27, 0xec, 0xc9, 0x6e, 0x20, 0xdd, 0xc2, 0x46, 0x81, 0x0b, + 0xba, 0x96, 0x25, 0xca, 0x45, 0xb7, 0x8c, 0x28, 0x13, 0x21, 0xf5, 0xb3, 0x87, 0x33, 0xad, 0xf6, 0x1b, 0x3b, 0x69, + 0xdf, 0x1e, 0x29, 0x7a, 0xc1, 0x40, 0x7c, 0xda, 0x23, 0xa5, 0x9e, 0x35, 0x72, 0x19, 0xd8, 0xd2, 0xa5, 0xaa, 0xe7, + 0xbf, 0x41, 0xf9, 0x0e, 0x66, 0xc6, 0xd9, 0xec, 0x77, 0xbd, 0xb9, 0x3d, 0xd9, 0xd7, 0xcd, 0xef, 0xac, 0xd7, 0x83, + 0xad, 0x41, 0x26, 0xbe, 0x50, 0xd4, 0x53, 0x56, 0x21, 0x56, 0x64, 0xf6, 0xbf, 0x85, 0xf7, 0x3b, 0xbc, 0x35, 0x42, + 0xb3, 0x32, 0x1e, 0xe6, 0xa3, 0x27, 0x7b, 0xd1, 0xfc, 0xde, 0x59, 0xb6, 0x95, 0xab, 0x92, 0xd9, 0x7e, 0x3f, 0x4a, + 0x9a, 0xb3, 0xc7, 0x6b, 0x24, 0x75, 0x80, 0x8f, 0xd7, 0x67, 0xf8, 0x48, 0x25, 0x94, 0x5a, 0x50, 0xd5, 0xa0, 0xf5, + 0xb1, 0xdf, 0x5b, 0xcf, 0xe9, 0xe3, 0xc7, 0x72, 0xba, 0x25, 0x45, 0x18, 0x3f, 0x30, 0x98, 0xb2, 0x13, 0xa7, 0x2e, + 0x79, 0x33, 0xa4, 0x77, 0xdd, 0x2a, 0xa9, 0xcb, 0x1e, 0x25, 0x82, 0x50, 0x07, 0xeb, 0x17, 0xfb, 0x21, 0xcc, 0x6c, + 0xd1, 0x1f, 0x36, 0xab, 0x39, 0xa1, 0x20, 0x02, 0x44, 0xab, 0xbc, 0x0f, 0x1c, 0x93, 0x84, 0x59, 0x73, 0x43, 0xba, + 0xf5, 0xe6, 0x4a, 0x7b, 0x25, 0x05, 0xf4, 0x73, 0x90, 0xb9, 0x7d, 0x74, 0xcb, 0x55, 0xcb, 0x3c, 0x97, 0xb6, 0x1c, + 0xb0, 0x68, 0x21, 0x3a, 0xb3, 0x73, 0xe9, 0x70, 0xf0, 0x1f, 0xd4, 0x95, 0xa8, 0x22, 0x82, 0x8e, 0xa2, 0x05, 0xa3, + 0xd5, 0xaa, 0x5d, 0x4e, 0x36, 0x15, 0xb2, 0x25, 0x11, 0x4e, 0x94, 0xec, 0x95, 0x50, 0x1f, 0xe5, 0x6a, 0xcf, 0x34, + 0xc4, 0x9f, 0x09, 0xd8, 0xb4, 0xc1, 0xdf, 0x02, 0xf7, 0x32, 0x38, 0x33, 0xed, 0xd3, 0x30, 0x02, 0x22, 0x73, 0x08, + 0xf6, 0xf3, 0xbb, 0x1e, 0x54, 0xf0, 0xa0, 0x23, 0xfd, 0x55, 0x3d, 0x2b, 0xf0, 0xcc, 0x3d, 0xf1, 0xfc, 0xf5, 0x89, + 0xf4, 0x22, 0x87, 0x07, 0x9a, 0xfb, 0x30, 0xe3, 0x2f, 0xca, 0x32, 0xdc, 0x8d, 0x96, 0x65, 0xb1, 0xf2, 0x22, 0xbd, + 0x8f, 0x67, 0x52, 0x0c, 0x24, 0x3a, 0xcc, 0x8c, 0xae, 0x62, 0x1d, 0xe7, 0x30, 0xee, 0xed, 0x49, 0x58, 0xa1, 0xfd, + 0xb3, 0xc4, 0x5e, 0x17, 0x00, 0xe0, 0x90, 0x35, 0x68, 0x85, 0x77, 0xba, 0xbd, 0xdd, 0xe3, 0x92, 0x12, 0xc5, 0x8d, + 0x9a, 0x9f, 0xd5, 0xd0, 0x32, 0x41, 0x2d, 0xb3, 0xee, 0x64, 0x32, 0x45, 0x12, 0xf8, 0x36, 0xec, 0x35, 0x2b, 0xf2, + 0x79, 0x23, 0xb7, 0x87, 0x77, 0xe1, 0x4a, 0xc4, 0xda, 0x82, 0x4e, 0x3a, 0x32, 0x0e, 0xf7, 0x42, 0x73, 0x23, 0xdd, + 0x3f, 0xa9, 0x92, 0xb0, 0x14, 0x31, 0xdc, 0x02, 0xd9, 0x5e, 0x6d, 0x2b, 0x41, 0x09, 0x24, 0xb0, 0x1f, 0x4a, 0xb1, + 0x4c, 0xb7, 0x02, 0xc0, 0x1c, 0xf8, 0x9f, 0x12, 0x86, 0xd0, 0xdd, 0x79, 0x88, 0x57, 0x8d, 0xbc, 0x6f, 0x10, 0x82, + 0xfd, 0x15, 0xc8, 0x69, 0xc0, 0x20, 0x52, 0x8c, 0x64, 0xc1, 0x40, 0x02, 0x90, 0xf3, 0x35, 0x98, 0xe4, 0xa6, 0xb9, + 0xe7, 0x07, 0xb9, 0xee, 0x60, 0xda, 0x07, 0xdd, 0x8b, 0x6b, 0xcd, 0x72, 0xf0, 0x8a, 0x89, 0xf8, 0xdf, 0x6b, 0xaf, + 0x64, 0x39, 0xcb, 0xfc, 0xc6, 0x5c, 0x74, 0x32, 0xb8, 0x6a, 0x08, 0xbf, 0x98, 0x65, 0x73, 0x1e, 0xcd, 0x32, 0x1d, + 0xea, 0x5f, 0x34, 0x47, 0xa5, 0x00, 0x86, 0x3a, 0x5e, 0x80, 0x35, 0xde, 0x95, 0x6e, 0x5a, 0xf1, 0x48, 0x63, 0x8c, + 0x82, 0x0a, 0x1d, 0x84, 0xfe, 0x5e, 0x03, 0xbc, 0x06, 0x93, 0xdc, 0x08, 0x95, 0x0f, 0x2e, 0xe8, 0x86, 0x6e, 0xb9, + 0x72, 0x09, 0x6a, 0x52, 0xb5, 0xfc, 0x72, 0x84, 0x7a, 0x57, 0x4b, 0x2e, 0xd5, 0xe6, 0x53, 0xa3, 0xac, 0x11, 0x64, + 0x72, 0x94, 0x7e, 0x9f, 0x72, 0xe1, 0x56, 0xc6, 0x64, 0x7d, 0x38, 0x78, 0x05, 0x37, 0x35, 0x7e, 0x95, 0x13, 0x8b, + 0xa8, 0x3d, 0x24, 0xc2, 0xd6, 0x6e, 0x85, 0xee, 0x3d, 0x6e, 0x94, 0xe6, 0x51, 0xb6, 0x89, 0x45, 0xe5, 0xf5, 0x12, + 0xb0, 0x16, 0xf7, 0x80, 0x0c, 0x95, 0x96, 0x7e, 0xc5, 0x0a, 0x80, 0x0c, 0x90, 0xc2, 0xc6, 0x0f, 0x48, 0x7b, 0xf5, + 0xc1, 0x4b, 0xfd, 0x7e, 0xdf, 0x98, 0xf2, 0xdf, 0x3f, 0xe4, 0xc0, 0x4c, 0x28, 0xca, 0x7a, 0x07, 0x13, 0x08, 0xae, + 0x9d, 0xa4, 0x3d, 0xab, 0xf9, 0xf5, 0xba, 0xf6, 0x80, 0xd4, 0xca, 0xb7, 0x98, 0xab, 0x5e, 0xd9, 0x17, 0x9b, 0x7d, + 0x5a, 0xdd, 0x18, 0x8d, 0x83, 0x60, 0x69, 0xf5, 0x46, 0xab, 0x1c, 0xf2, 0x86, 0x57, 0x20, 0x52, 0x59, 0x57, 0xd7, + 0xca, 0xb9, 0xba, 0x16, 0x1c, 0x09, 0x64, 0x4b, 0x9e, 0xc3, 0x7f, 0x21, 0xf7, 0xca, 0xc3, 0xa1, 0xf0, 0xfb, 0xfd, + 0x74, 0x46, 0x5a, 0x59, 0xa0, 0x4c, 0x5b, 0xd7, 0x5e, 0xe8, 0x1f, 0x0e, 0x3f, 0x80, 0xd7, 0x88, 0x7f, 0x38, 0x94, + 0xfd, 0xfe, 0x47, 0x73, 0x93, 0x39, 0x1f, 0x2b, 0xa5, 0xec, 0x25, 0x2a, 0xdd, 0xdf, 0x24, 0xbc, 0xf7, 0xbf, 0x47, + 0xff, 0x7b, 0x74, 0xd9, 0x53, 0x01, 0x60, 0x09, 0x9f, 0xe1, 0x0d, 0x9d, 0xa9, 0xcb, 0x39, 0x93, 0xee, 0xee, 0xca, + 0x0f, 0xbd, 0xa7, 0xf1, 0xe1, 0x7b, 0x73, 0xd3, 0xc6, 0x5f, 0xab, 0x23, 0x4d, 0x42, 0xc7, 0x45, 0xff, 0x70, 0xf8, + 0x94, 0x68, 0x7d, 0x5a, 0xaa, 0xf4, 0x69, 0x0a, 0x3c, 0xc9, 0xb0, 0xe1, 0xba, 0x85, 0xe9, 0x68, 0x7e, 0xdc, 0x7c, + 0x95, 0xbc, 0x38, 0x4b, 0xe1, 0xda, 0x9b, 0xcf, 0xd2, 0xf9, 0x14, 0xac, 0x2b, 0xc3, 0x7c, 0x56, 0xcf, 0x03, 0x48, + 0x1d, 0x42, 0x9a, 0x35, 0x0d, 0xff, 0x56, 0xb9, 0x82, 0xb7, 0xf6, 0x78, 0x37, 0x18, 0x51, 0xea, 0x48, 0x9f, 0xb4, + 0x21, 0x74, 0x49, 0x25, 0xff, 0x51, 0xe4, 0x31, 0xc6, 0x6c, 0xbc, 0x22, 0xb2, 0xcf, 0x22, 0x7f, 0x59, 0x00, 0x60, + 0x11, 0x20, 0x20, 0xa7, 0x73, 0x47, 0x12, 0xff, 0x39, 0xf9, 0xf6, 0x8f, 0xe9, 0xd2, 0x3e, 0x94, 0xc5, 0x5d, 0x29, + 0xaa, 0xea, 0xa8, 0xb4, 0x9d, 0x2d, 0xd7, 0x03, 0x7d, 0x68, 0xbf, 0x2f, 0xe9, 0x43, 0x53, 0x0c, 0x45, 0x81, 0x5b, + 0x63, 0x6f, 0x9a, 0x72, 0x45, 0x53, 0x3d, 0x32, 0xd6, 0xcf, 0xef, 0x77, 0x6f, 0x62, 0x2f, 0xf5, 0x83, 0x14, 0x04, + 0x61, 0x8d, 0x9f, 0x94, 0x22, 0x09, 0x9c, 0xcf, 0x30, 0x95, 0xf8, 0x74, 0x29, 0x55, 0xfe, 0x30, 0xd2, 0x7c, 0x98, + 0x82, 0x5e, 0xf6, 0x1f, 0x15, 0xcc, 0x7f, 0xdd, 0x1e, 0xac, 0x4f, 0xeb, 0x32, 0x8d, 0x2a, 0xa2, 0xca, 0x0b, 0x53, + 0x6d, 0x02, 0x11, 0xfc, 0x5a, 0x58, 0x24, 0xbf, 0x3e, 0x39, 0x12, 0x34, 0x66, 0xb2, 0x7c, 0x3c, 0x72, 0xbf, 0xb0, + 0xaf, 0x5c, 0xc7, 0xf3, 0x3f, 0x37, 0xf3, 0x7f, 0x80, 0xce, 0x90, 0xc5, 0x35, 0xb7, 0x0c, 0x16, 0x38, 0xfb, 0xa5, + 0xab, 0x07, 0xfc, 0xcd, 0x3c, 0x71, 0x0d, 0x1c, 0xcc, 0xd7, 0xe8, 0xaa, 0x98, 0xce, 0x8a, 0x01, 0x10, 0xd8, 0xfa, + 0x8d, 0x35, 0x27, 0x5e, 0x5b, 0x3c, 0x57, 0x72, 0x41, 0xe8, 0xeb, 0x2a, 0xcc, 0xc6, 0x55, 0xb1, 0xa9, 0x44, 0xb1, + 0xa9, 0x7b, 0xa4, 0x96, 0xcd, 0xa7, 0xb5, 0xad, 0x90, 0xfd, 0x49, 0xb4, 0x68, 0xbb, 0x0c, 0xd5, 0x64, 0x94, 0xa5, + 0xeb, 0x29, 0x90, 0xea, 0x05, 0x70, 0x16, 0x99, 0x57, 0xbe, 0x38, 0x7b, 0xc0, 0x16, 0x8d, 0xa7, 0xc0, 0x88, 0x4a, + 0x7f, 0xe4, 0x8d, 0xd1, 0xe9, 0x89, 0x7e, 0x3f, 0x9f, 0x52, 0xc8, 0xd7, 0x4f, 0x80, 0xc9, 0x55, 0xcb, 0x05, 0xe8, + 0xcb, 0x50, 0x07, 0x95, 0x28, 0xb5, 0x62, 0x18, 0xb1, 0xf0, 0x93, 0x40, 0xf6, 0x66, 0x0a, 0x6a, 0x56, 0x51, 0x12, + 0x2a, 0x51, 0x29, 0xd9, 0x9a, 0xa0, 0x96, 0xde, 0x17, 0x45, 0xbd, 0xaf, 0xc0, 0x51, 0x32, 0xd2, 0x66, 0x39, 0x65, + 0xc6, 0x45, 0x99, 0x8b, 0x7e, 0xb0, 0x7f, 0x57, 0x9e, 0xdf, 0xc8, 0x7c, 0x96, 0xfb, 0x8e, 0xce, 0x69, 0x3b, 0x2e, + 0x50, 0xe6, 0x96, 0xd3, 0x56, 0x4b, 0x1e, 0x93, 0xf7, 0x2c, 0xd8, 0xf6, 0x5f, 0x24, 0xc8, 0xab, 0x08, 0xf3, 0x09, + 0x55, 0x36, 0xff, 0x80, 0x30, 0x5b, 0x1c, 0xd8, 0x63, 0x17, 0x26, 0x22, 0xbd, 0x05, 0x4b, 0x62, 0x98, 0x95, 0x22, + 0x8c, 0x77, 0xe0, 0xfd, 0xb3, 0xa9, 0xc4, 0xe8, 0x0c, 0x9d, 0xdc, 0xcf, 0x1e, 0xd2, 0x3a, 0x39, 0x7b, 0xf3, 0xea, + 0xec, 0xbb, 0xde, 0xa0, 0x18, 0xa5, 0xf1, 0xa0, 0xf7, 0xdd, 0xd9, 0x6a, 0x03, 0x10, 0x99, 0xe2, 0x2c, 0x26, 0x53, + 0x9a, 0x88, 0xcf, 0xc8, 0x30, 0x78, 0x56, 0x27, 0xe2, 0x8c, 0x26, 0xa6, 0xfb, 0x1a, 0xa5, 0xc9, 0xb7, 0xa3, 0x30, + 0x87, 0x97, 0x4b, 0xb1, 0xa9, 0x44, 0x0c, 0x76, 0x4a, 0x35, 0xcf, 0xf2, 0xf6, 0x59, 0x9c, 0x8f, 0x3a, 0x64, 0x95, + 0x0e, 0xd0, 0xed, 0x89, 0xb4, 0xab, 0xd2, 0x15, 0x10, 0x7a, 0x00, 0x9c, 0x74, 0xe5, 0xcf, 0xc3, 0x41, 0x24, 0x10, + 0x6a, 0xc1, 0x9c, 0x4c, 0x23, 0xba, 0x21, 0xbd, 0xc4, 0x3e, 0x03, 0xb3, 0x90, 0xd2, 0x3c, 0xb8, 0xb9, 0x5a, 0xb4, + 0xdc, 0x15, 0x2b, 0x47, 0x61, 0xb5, 0x16, 0x51, 0x8d, 0x54, 0xc7, 0xe0, 0xbc, 0x03, 0x11, 0x00, 0x8a, 0x11, 0x3c, + 0xe3, 0x51, 0xbf, 0x1f, 0xa9, 0xa0, 0x9c, 0x84, 0x7e, 0x51, 0xe8, 0x97, 0xc6, 0xa0, 0x8c, 0xf9, 0xbb, 0x50, 0x13, + 0x03, 0xd4, 0x5b, 0x1e, 0x2a, 0x8e, 0x00, 0x5c, 0xce, 0x11, 0x33, 0xce, 0x7b, 0xdc, 0x45, 0xe3, 0x54, 0xbc, 0x13, + 0xea, 0x3a, 0x58, 0x2a, 0xd4, 0x79, 0x53, 0x1f, 0xe9, 0x39, 0x69, 0x12, 0x34, 0x88, 0x1b, 0x78, 0xbc, 0x1a, 0x02, + 0xaa, 0x95, 0x90, 0x7a, 0x0b, 0x9d, 0x52, 0xd5, 0x21, 0xb0, 0x06, 0xb8, 0x44, 0x61, 0x5b, 0x61, 0x72, 0x44, 0x9b, + 0xb2, 0x14, 0xf9, 0x11, 0x1b, 0xb4, 0x4b, 0x46, 0xa6, 0x0e, 0xae, 0xff, 0xc3, 0xdb, 0xb7, 0x70, 0xb7, 0x6d, 0x63, + 0xeb, 0xfe, 0x15, 0x8b, 0x37, 0x55, 0x89, 0x08, 0x92, 0x25, 0x37, 0xe9, 0xb4, 0x94, 0x21, 0x1d, 0x37, 0x8f, 0x36, + 0x9d, 0xe6, 0xd1, 0x38, 0xed, 0x74, 0x46, 0x57, 0xc7, 0xa5, 0x49, 0xd8, 0x62, 0x43, 0x03, 0x2a, 0x49, 0xf9, 0x51, + 0x89, 0xff, 0xfd, 0xae, 0xbd, 0xf1, 0x24, 0x45, 0x3b, 0x99, 0xb9, 0xe7, 0xde, 0x95, 0xb5, 0x62, 0x11, 0x04, 0xf1, + 0xc6, 0xc6, 0xc6, 0x7e, 0x7c, 0xbb, 0x19, 0xcb, 0x49, 0x85, 0xfe, 0x75, 0x75, 0x01, 0x9c, 0x17, 0xc6, 0xb7, 0x6e, + 0x66, 0xcb, 0x75, 0xb4, 0xeb, 0xd2, 0xc5, 0x2c, 0x29, 0x78, 0xb9, 0x96, 0xa2, 0xcc, 0xae, 0xf9, 0x4f, 0xf6, 0x65, + 0x33, 0x80, 0x14, 0xda, 0x91, 0xbe, 0x6e, 0x77, 0x47, 0x8b, 0x71, 0x6c, 0x39, 0xbe, 0xa5, 0xd2, 0x9d, 0x1e, 0x55, + 0x2f, 0x6e, 0xb6, 0xce, 0xb5, 0xca, 0xd2, 0x94, 0x8b, 0x57, 0x22, 0xcd, 0x12, 0x2f, 0x39, 0xd6, 0x01, 0xaa, 0x5d, + 0xe4, 0x2b, 0x17, 0x1b, 0xf9, 0x79, 0x56, 0x62, 0xc0, 0xe0, 0x46, 0xa3, 0x5a, 0xa1, 0xa6, 0x4c, 0xe0, 0x0b, 0xf9, + 0x1e, 0x23, 0x6e, 0xb3, 0x32, 0x01, 0x86, 0x1f, 0x13, 0xf5, 0x25, 0x3d, 0x85, 0x28, 0x0f, 0x2a, 0x1e, 0xf7, 0x73, + 0x8e, 0x88, 0xd7, 0x56, 0x65, 0x0e, 0x4c, 0xb6, 0x56, 0x41, 0x22, 0xd8, 0x5d, 0xb6, 0xd0, 0x8b, 0x68, 0xa9, 0xee, + 0x42, 0xbd, 0x78, 0xb7, 0xeb, 0x25, 0x8a, 0x0e, 0x38, 0xf9, 0x69, 0xf0, 0x32, 0xce, 0x72, 0x9e, 0x1e, 0x54, 0xf2, + 0x40, 0x6d, 0xa8, 0x03, 0xe5, 0xcc, 0x01, 0x3b, 0xef, 0xcb, 0xea, 0x40, 0xaf, 0xe9, 0x03, 0xdd, 0xce, 0x03, 0xb8, + 0x60, 0xe0, 0xce, 0xbd, 0xcc, 0xae, 0xb9, 0x38, 0x00, 0x65, 0xa0, 0x35, 0x1e, 0xa8, 0x45, 0x35, 0x52, 0x13, 0xa3, + 0x03, 0x57, 0x27, 0xfa, 0x60, 0x0e, 0xe8, 0xf7, 0x10, 0x2b, 0xbc, 0xf5, 0x76, 0xad, 0x0f, 0xda, 0x80, 0xfe, 0xb4, + 0x32, 0x7d, 0xd0, 0xd1, 0xe2, 0x55, 0x48, 0xe0, 0xc6, 0x90, 0x6a, 0xa4, 0x56, 0x23, 0xab, 0x40, 0xf1, 0x86, 0xb7, + 0x78, 0xf7, 0xae, 0x25, 0x5b, 0xef, 0x25, 0x02, 0x7b, 0x65, 0xa2, 0x8a, 0x33, 0x71, 0xe2, 0xa5, 0xf2, 0x5a, 0x3b, + 0xc9, 0x08, 0xe3, 0x5b, 0x56, 0x52, 0x7f, 0x87, 0x98, 0x5b, 0xa4, 0x39, 0x0c, 0x5e, 0x84, 0x15, 0x99, 0xf3, 0x7e, + 0x5f, 0xce, 0x65, 0x54, 0xce, 0xc5, 0x61, 0x19, 0x29, 0x84, 0xb6, 0xfb, 0x44, 0x40, 0x0f, 0x4a, 0x80, 0x7c, 0x01, + 0x50, 0xf5, 0x90, 0xf0, 0xe7, 0x21, 0xa9, 0x4f, 0xa7, 0xd0, 0xa7, 0xd0, 0xd6, 0x2b, 0x5e, 0x41, 0x55, 0xdd, 0x18, + 0xd9, 0x46, 0x05, 0x2d, 0x1e, 0xcb, 0xb3, 0xda, 0x30, 0x36, 0xa7, 0xd6, 0xbb, 0xde, 0x6c, 0x30, 0x65, 0x73, 0xa1, + 0x56, 0x61, 0x48, 0xa2, 0x9b, 0xd2, 0x0b, 0x1f, 0x62, 0xb1, 0xb2, 0x5a, 0x9b, 0xdf, 0xc4, 0xfe, 0xc8, 0x44, 0x8a, + 0xfb, 0xd9, 0x12, 0xe7, 0x2e, 0x1e, 0xcf, 0xab, 0xbe, 0xd6, 0xd2, 0x22, 0xd3, 0xe6, 0x3b, 0x7d, 0x19, 0xd2, 0x54, + 0xd4, 0x90, 0x46, 0x9d, 0x19, 0x74, 0xdf, 0x2e, 0xaf, 0xa8, 0x46, 0x98, 0x00, 0xaf, 0x74, 0x06, 0xdd, 0x68, 0x3c, + 0x10, 0x45, 0x35, 0x2a, 0x36, 0x42, 0x20, 0xda, 0x30, 0xe4, 0x98, 0x5b, 0x42, 0x92, 0xfd, 0xc5, 0xbf, 0x53, 0xc1, + 0x15, 0x8a, 0xf8, 0xc6, 0xc0, 0x79, 0x57, 0xd6, 0xb3, 0xbb, 0x8e, 0xfc, 0x9c, 0x58, 0x58, 0xed, 0x3f, 0x34, 0x8f, + 0x5a, 0xe3, 0x2c, 0xa0, 0xad, 0x69, 0x75, 0xc3, 0xe1, 0x1e, 0xd5, 0xb1, 0x28, 0x0d, 0x20, 0xb1, 0x47, 0x96, 0x8b, + 0xd6, 0x31, 0x83, 0x06, 0xf4, 0xb7, 0xd9, 0xd5, 0xe6, 0x0a, 0x51, 0xdb, 0x4a, 0x64, 0x9d, 0xa4, 0xf2, 0x2f, 0x69, + 0x8f, 0xba, 0xb6, 0xa7, 0xf2, 0xbf, 0x6d, 0x53, 0xe5, 0xd0, 0x02, 0xc9, 0x63, 0x37, 0xe7, 0x81, 0xea, 0x48, 0x10, + 0x05, 0x6a, 0xeb, 0x05, 0x53, 0xef, 0x94, 0x29, 0x3a, 0x90, 0x9f, 0x0b, 0x73, 0x86, 0x7d, 0xc6, 0x11, 0x63, 0x96, + 0x4a, 0x0c, 0xa6, 0x3e, 0xc6, 0xa8, 0xa6, 0xb5, 0x02, 0x74, 0xfd, 0x74, 0x0b, 0x7f, 0xa2, 0xa2, 0x46, 0x43, 0xad, + 0x91, 0x14, 0x8a, 0x26, 0x2a, 0xe8, 0x58, 0x5a, 0xe8, 0x60, 0x0a, 0x9d, 0x44, 0xc2, 0x12, 0xd0, 0x30, 0x21, 0x3a, + 0xa9, 0xc0, 0x5b, 0x03, 0x38, 0xf3, 0x71, 0x51, 0x6e, 0x0a, 0x6d, 0x30, 0xf7, 0x43, 0x7c, 0xcd, 0x5f, 0x3d, 0x77, + 0x46, 0xf5, 0x2d, 0x6b, 0x7d, 0x4f, 0x0b, 0xf2, 0x43, 0xc8, 0x29, 0x3a, 0x30, 0xb1, 0xd9, 0x16, 0x8d, 0x31, 0xca, + 0x5a, 0x87, 0xba, 0x78, 0xab, 0xe3, 0xaf, 0x68, 0x13, 0xbc, 0x07, 0x3c, 0x45, 0xb4, 0xe1, 0xa1, 0x30, 0x56, 0xd5, + 0xf8, 0x54, 0xb2, 0x96, 0x1e, 0xac, 0xe0, 0xe9, 0x26, 0xe1, 0x21, 0xe8, 0x91, 0x08, 0x9b, 0x85, 0xc5, 0x22, 0x5e, + 0xc2, 0x71, 0x52, 0x10, 0x50, 0x3b, 0xe8, 0x2b, 0xf8, 0x62, 0x89, 0xee, 0xaf, 0x12, 0x3d, 0xc0, 0xd0, 0x82, 0xb8, + 0x19, 0xfa, 0x74, 0x74, 0x15, 0xaf, 0x1b, 0x2a, 0x12, 0xbe, 0x28, 0xc0, 0x76, 0x48, 0xa9, 0xa7, 0x40, 0x0b, 0x95, + 0x28, 0xfd, 0x30, 0xf0, 0x1d, 0x1a, 0xf8, 0x5a, 0xeb, 0x00, 0x0d, 0xfd, 0x8c, 0x69, 0x6a, 0x9d, 0xa1, 0xf2, 0xb9, + 0x77, 0xcf, 0x8c, 0x56, 0x73, 0x0b, 0xc6, 0xa0, 0x6f, 0xa3, 0x29, 0x8a, 0x73, 0xf2, 0x79, 0x50, 0xc4, 0x69, 0x16, + 0xe7, 0xe0, 0xb7, 0x19, 0x17, 0x98, 0x31, 0x89, 0x2b, 0x7e, 0x29, 0x0b, 0xd0, 0x76, 0xe7, 0x2a, 0xb5, 0xae, 0x41, + 0x40, 0xf6, 0x03, 0x58, 0xbd, 0x34, 0x74, 0x54, 0xce, 0xbb, 0x4b, 0x9b, 0x42, 0xc4, 0x22, 0x04, 0x9b, 0x66, 0xba, + 0x62, 0x27, 0xa1, 0xd2, 0xe6, 0x40, 0x7c, 0x23, 0x34, 0xee, 0x9f, 0x86, 0xb1, 0xd5, 0x14, 0x5b, 0xbb, 0xb7, 0xdd, + 0xee, 0xb7, 0xd2, 0x4b, 0xa7, 0x39, 0xe9, 0x31, 0xf6, 0x5b, 0x19, 0x16, 0x23, 0xdb, 0x11, 0x02, 0x4b, 0xce, 0xfb, + 0xd4, 0x7f, 0x45, 0xcb, 0x45, 0x02, 0xa6, 0x23, 0x3a, 0x42, 0x2e, 0x50, 0x76, 0x8c, 0xe2, 0x0e, 0x0c, 0xae, 0xe8, + 0xf7, 0xc1, 0x2a, 0xc3, 0x5c, 0x48, 0x56, 0x24, 0x65, 0xf0, 0x3c, 0xf5, 0x30, 0xe0, 0x37, 0x4c, 0x99, 0xbb, 0x28, + 0xeb, 0xd3, 0x15, 0x99, 0xa6, 0xc8, 0x40, 0x6c, 0xc2, 0x6d, 0x96, 0x46, 0x89, 0x12, 0x91, 0xad, 0xd0, 0x3f, 0xd2, + 0x50, 0x2c, 0x1d, 0xae, 0x17, 0xa9, 0x12, 0xa1, 0x62, 0x91, 0xe2, 0x49, 0x9d, 0xd6, 0xe9, 0x08, 0xe3, 0x4d, 0x82, + 0x52, 0xae, 0x86, 0x81, 0x2a, 0xa9, 0x5e, 0x0a, 0xdb, 0x62, 0xb7, 0xd3, 0x17, 0x2b, 0xb1, 0x88, 0x97, 0xf8, 0x52, + 0xe0, 0x28, 0xfe, 0x9d, 0x7b, 0xb1, 0xa6, 0xd4, 0xf6, 0xa0, 0x76, 0x44, 0x09, 0xfd, 0x3b, 0x87, 0x8b, 0xc4, 0x77, + 0x52, 0xc7, 0xfd, 0x43, 0x8b, 0x90, 0x33, 0x75, 0x90, 0x1a, 0x6e, 0x68, 0x4f, 0xf8, 0x6f, 0xb8, 0x3e, 0xe3, 0x8c, + 0xde, 0x54, 0x33, 0x6a, 0xfc, 0x5e, 0x0f, 0xcf, 0x18, 0xf5, 0xd9, 0xc0, 0x61, 0x85, 0x28, 0xb4, 0x61, 0xb3, 0x52, + 0x89, 0x16, 0x86, 0x52, 0xfd, 0x25, 0x54, 0xcc, 0xb8, 0x33, 0xa3, 0x2c, 0x19, 0x9f, 0x96, 0xc7, 0x62, 0x3a, 0x18, + 0x94, 0xa4, 0x32, 0x16, 0x7a, 0x70, 0x3d, 0xf0, 0xfc, 0x7b, 0xe0, 0x16, 0xe2, 0xc1, 0x21, 0x8b, 0x21, 0x37, 0xe0, + 0xf8, 0x2d, 0x4e, 0xae, 0x1a, 0x95, 0x2a, 0x78, 0x35, 0x51, 0x2d, 0xf8, 0x7b, 0x19, 0x06, 0xe8, 0x93, 0x14, 0x80, + 0xc9, 0x60, 0xca, 0x6f, 0x41, 0xa2, 0x74, 0xa6, 0x6e, 0x48, 0xbf, 0x88, 0x82, 0x5f, 0xf0, 0x82, 0x8b, 0xc4, 0x15, + 0x60, 0x79, 0x07, 0xdb, 0xeb, 0xa8, 0xa2, 0x0a, 0x88, 0xd7, 0xf4, 0x38, 0xe2, 0xc6, 0xfb, 0xcf, 0xf4, 0xd8, 0x02, + 0xb5, 0x5a, 0xc7, 0x06, 0x9f, 0x39, 0x06, 0x17, 0x74, 0x2d, 0xb1, 0x35, 0x54, 0xc3, 0x8a, 0xc0, 0xc0, 0x05, 0x1c, + 0x84, 0x25, 0x8a, 0x63, 0x2b, 0x79, 0x45, 0x1a, 0x52, 0xda, 0x07, 0x86, 0xa3, 0x4d, 0x72, 0x7c, 0x9b, 0x65, 0x37, + 0x81, 0x8b, 0x65, 0xe7, 0xa4, 0x99, 0x58, 0x36, 0x78, 0x9f, 0x37, 0xe7, 0xd7, 0xfd, 0x43, 0x42, 0x55, 0xb0, 0x1b, + 0xde, 0x0e, 0x76, 0xe3, 0x84, 0x5f, 0x0b, 0xb1, 0xd4, 0xf1, 0x59, 0xcc, 0x25, 0xcb, 0x6f, 0xad, 0x77, 0x4b, 0x92, + 0x5a, 0x01, 0xed, 0xb3, 0x2c, 0xa8, 0x89, 0x00, 0x74, 0x3f, 0xfc, 0x05, 0x42, 0x67, 0xf8, 0xdb, 0x63, 0x70, 0x45, + 0x0a, 0xef, 0x1d, 0x02, 0x61, 0x4d, 0x37, 0xf7, 0x6a, 0x03, 0xbe, 0x18, 0xf7, 0x67, 0x4c, 0x3d, 0xfd, 0x36, 0x93, + 0xfb, 0xba, 0x6e, 0x8f, 0x2c, 0xc3, 0x47, 0xb8, 0x52, 0x00, 0x2c, 0x13, 0xfe, 0x62, 0x6c, 0x49, 0xf5, 0x09, 0xc0, + 0xa9, 0xe9, 0x88, 0x3e, 0x41, 0x60, 0xe0, 0x94, 0x68, 0x31, 0xba, 0x56, 0x8e, 0x68, 0x06, 0x69, 0x4d, 0xb7, 0xc2, + 0x78, 0xeb, 0x41, 0x0b, 0x3d, 0xd3, 0x70, 0xe2, 0x3f, 0x68, 0xe6, 0x55, 0x01, 0x01, 0xb4, 0x32, 0x82, 0xb7, 0xd6, + 0x47, 0x73, 0x84, 0xf8, 0x84, 0x25, 0xd1, 0x84, 0xc5, 0x33, 0xc5, 0x8f, 0x09, 0xdd, 0x36, 0xb5, 0x4d, 0x1f, 0x90, + 0xfe, 0xe2, 0x9a, 0xf5, 0x53, 0x56, 0xb5, 0x6f, 0x0f, 0x15, 0x2f, 0xa7, 0xcd, 0xe0, 0x87, 0x89, 0x2a, 0xc6, 0xff, + 0xa2, 0xf2, 0xa5, 0x56, 0x00, 0xc3, 0xdc, 0x55, 0x4f, 0xbf, 0xdf, 0xcc, 0x96, 0x03, 0xa1, 0xf2, 0x3b, 0x83, 0xa4, + 0x4f, 0xc7, 0xf3, 0x03, 0x9b, 0x44, 0x6d, 0xa1, 0xe7, 0x8f, 0x4b, 0xdd, 0x84, 0xca, 0x6b, 0x53, 0x23, 0x5a, 0x21, + 0x43, 0x65, 0xeb, 0x80, 0xf5, 0xfd, 0x43, 0xb8, 0xbf, 0xa8, 0x69, 0xa8, 0x75, 0xcf, 0x5d, 0x8b, 0x82, 0x13, 0x7f, + 0x80, 0xb1, 0xb8, 0x90, 0xd4, 0x3a, 0x08, 0x93, 0x7e, 0xb4, 0x38, 0xc9, 0x8d, 0xba, 0x3a, 0x39, 0x53, 0xcc, 0x13, + 0xb8, 0xa8, 0x96, 0x6d, 0x7f, 0x45, 0xa5, 0x2e, 0xe5, 0xf6, 0x8a, 0xd2, 0xf4, 0x90, 0xb6, 0x57, 0x71, 0xde, 0x16, + 0x5c, 0xf0, 0xcf, 0x14, 0x5c, 0x58, 0x07, 0xeb, 0x8e, 0x3b, 0x65, 0x4f, 0x78, 0xa2, 0x4c, 0x6b, 0x83, 0xbb, 0x69, + 0x30, 0x26, 0xc6, 0x7e, 0x77, 0xc5, 0x93, 0x8f, 0xc8, 0x82, 0x7f, 0x97, 0x09, 0xf0, 0x4c, 0x76, 0xaf, 0x54, 0xfe, + 0x1f, 0xfc, 0xab, 0xad, 0x7d, 0x67, 0xcd, 0x3f, 0x3d, 0xeb, 0xe1, 0xce, 0x61, 0xf2, 0x03, 0x74, 0x06, 0x74, 0x7b, + 0x25, 0x53, 0x0e, 0xc8, 0x00, 0xd6, 0x22, 0x19, 0x0d, 0xf8, 0xd0, 0xca, 0xb2, 0xed, 0x3b, 0xad, 0x2e, 0x08, 0xf7, + 0x12, 0xb8, 0xe9, 0xfd, 0xb5, 0x99, 0x99, 0xd3, 0xb5, 0x12, 0x4d, 0x97, 0xc6, 0xd6, 0xb2, 0x54, 0x01, 0xbb, 0xdf, + 0x7b, 0x92, 0x4d, 0xf3, 0xe3, 0xd5, 0x34, 0xb7, 0xd4, 0x6d, 0xeb, 0x96, 0x0d, 0x00, 0x21, 0x76, 0xad, 0xad, 0x1c, + 0x40, 0x72, 0x7b, 0x10, 0xc2, 0xd7, 0x8a, 0xd0, 0x53, 0x25, 0x42, 0x9f, 0xa6, 0xcd, 0x3e, 0xd8, 0x55, 0xb5, 0x69, + 0xc4, 0x39, 0x1a, 0xa4, 0x9a, 0x91, 0x7f, 0x7b, 0xcd, 0x8b, 0x8b, 0x5c, 0xde, 0x00, 0x06, 0x32, 0xa9, 0x8d, 0xc2, + 0xf2, 0x0a, 0xdc, 0xf9, 0xd1, 0x71, 0x9c, 0x89, 0x51, 0x8e, 0xc1, 0x5a, 0x11, 0x1e, 0x59, 0x27, 0xce, 0x01, 0x04, + 0xd9, 0x9f, 0x34, 0x1d, 0xcf, 0xb5, 0xc0, 0x98, 0xbe, 0xc0, 0x5d, 0xe5, 0x6c, 0xb6, 0xcd, 0xed, 0xa2, 0x6f, 0xce, + 0xb0, 0xee, 0x48, 0x69, 0x6d, 0x2c, 0xba, 0xee, 0x60, 0xad, 0x19, 0xb4, 0x45, 0x28, 0xf9, 0x90, 0x3b, 0x69, 0xff, + 0x0a, 0x68, 0x70, 0x96, 0xa5, 0xb7, 0xd6, 0x2a, 0x7f, 0xab, 0x85, 0x38, 0x51, 0x4c, 0x9d, 0xf8, 0x26, 0x4a, 0xf4, + 0xf9, 0x99, 0x18, 0x37, 0x10, 0x48, 0xfd, 0x01, 0x83, 0x6a, 0x14, 0x61, 0x02, 0xd7, 0x81, 0x28, 0xb6, 0x27, 0x6a, + 0x63, 0x39, 0x82, 0x4e, 0x08, 0xf1, 0x0e, 0xca, 0x30, 0x56, 0x17, 0x07, 0xda, 0x60, 0xe9, 0xeb, 0xd6, 0x3a, 0x37, + 0x84, 0xc2, 0x38, 0x81, 0x29, 0x06, 0x49, 0x9d, 0x75, 0x96, 0x09, 0xaa, 0xec, 0x98, 0x74, 0xde, 0x07, 0xe8, 0xfe, + 0x5a, 0x34, 0xc5, 0xd7, 0x9d, 0x3b, 0xe8, 0x3e, 0xae, 0x5f, 0x6b, 0x91, 0x1b, 0xfc, 0x79, 0x4b, 0x84, 0x45, 0xe0, + 0xac, 0x35, 0xf9, 0xaa, 0x11, 0x0e, 0x4c, 0x49, 0xa6, 0x61, 0x2f, 0x51, 0x36, 0xdd, 0xbb, 0x5d, 0xaf, 0x77, 0xaf, + 0x88, 0xab, 0xc7, 0x58, 0xe5, 0xdd, 0xcc, 0xed, 0x9d, 0x6a, 0x23, 0xf6, 0x6f, 0xda, 0x7e, 0x8a, 0x1d, 0xb5, 0xd6, + 0x6e, 0x37, 0x9c, 0x50, 0x43, 0xbe, 0x15, 0x55, 0x5a, 0x9d, 0x6e, 0x0c, 0xda, 0x21, 0x9e, 0xb5, 0xc8, 0xe0, 0x46, + 0xf9, 0xdc, 0x09, 0x9d, 0x54, 0x9c, 0x55, 0xa7, 0x2e, 0xd8, 0x5e, 0xf1, 0x6a, 0x25, 0xd3, 0x48, 0x50, 0xb4, 0x39, + 0x8f, 0x4a, 0x9a, 0xc8, 0x8d, 0xa8, 0x22, 0x59, 0xa3, 0x5e, 0xd4, 0x6a, 0x0c, 0x10, 0x90, 0xe9, 0xac, 0xe9, 0x41, + 0x15, 0xcc, 0x87, 0x32, 0x92, 0xd3, 0xf7, 0x60, 0x69, 0x8f, 0x1c, 0x6b, 0x7d, 0x5f, 0x9d, 0x2d, 0xbe, 0xd5, 0x13, + 0x82, 0x29, 0xcc, 0x1e, 0x08, 0x03, 0xd7, 0x34, 0x86, 0x9c, 0x76, 0x89, 0xcb, 0x9a, 0x6e, 0x09, 0xf7, 0x70, 0xbb, + 0x92, 0xcd, 0xdc, 0x3c, 0x69, 0x6e, 0xae, 0x60, 0xb3, 0x62, 0x31, 0x06, 0xed, 0x97, 0x54, 0xd7, 0x2e, 0xcd, 0xad, + 0xc7, 0x83, 0x80, 0x06, 0x83, 0xc2, 0xf0, 0xaf, 0x13, 0xe3, 0xe1, 0x49, 0x03, 0x82, 0xa4, 0x5c, 0x84, 0x63, 0xdf, + 0x88, 0x7e, 0x32, 0x95, 0xc7, 0x1c, 0x2d, 0xde, 0xa1, 0xd5, 0x09, 0x44, 0xf1, 0x12, 0xa1, 0x24, 0x46, 0x55, 0x68, + 0x44, 0x50, 0x9e, 0x96, 0xbf, 0x54, 0xd5, 0x21, 0xa0, 0x90, 0xf6, 0x15, 0x85, 0xb2, 0x4d, 0x62, 0x68, 0x86, 0x5f, + 0x2e, 0x26, 0x4b, 0x3d, 0x03, 0x03, 0xb9, 0x38, 0x5a, 0xea, 0x59, 0x18, 0xc8, 0xc5, 0x57, 0xcb, 0xda, 0xad, 0x03, + 0x4d, 0x40, 0x3c, 0x17, 0x8e, 0x4e, 0x4a, 0xab, 0xb2, 0x05, 0x74, 0xfb, 0x10, 0x41, 0xff, 0xbb, 0x3d, 0x04, 0x9d, + 0x5c, 0x68, 0x4f, 0x6e, 0x40, 0xdb, 0x71, 0x08, 0xec, 0x15, 0x93, 0x0a, 0x13, 0x80, 0xe8, 0x98, 0x8d, 0xc1, 0x10, + 0x5b, 0x7d, 0x70, 0xcc, 0xc6, 0x53, 0x9f, 0x04, 0x01, 0xa3, 0xfb, 0x83, 0x81, 0x04, 0xbf, 0xc5, 0xab, 0xf4, 0x6c, + 0x2b, 0xd0, 0x4d, 0xdf, 0xdd, 0x0d, 0xbd, 0x8b, 0x2b, 0x38, 0x55, 0xbb, 0x7b, 0x12, 0xba, 0xc9, 0xb4, 0x03, 0xf4, + 0x1a, 0xe2, 0x86, 0xfc, 0xca, 0x68, 0x34, 0xb2, 0x29, 0x21, 0x21, 0x86, 0x73, 0x68, 0xe6, 0xb4, 0x5c, 0xbe, 0xba, + 0xf5, 0x6c, 0x41, 0x86, 0x99, 0xde, 0x32, 0x59, 0x3f, 0x40, 0x59, 0xf5, 0x18, 0xda, 0xa1, 0xf7, 0xc8, 0xf1, 0xc3, + 0x83, 0x6f, 0x32, 0x7e, 0xe2, 0x70, 0xed, 0xe1, 0x5c, 0xf8, 0x2e, 0x6b, 0x46, 0xe6, 0xd0, 0x79, 0xf6, 0x71, 0xbc, + 0x87, 0x71, 0xf2, 0x69, 0x16, 0xca, 0x1b, 0xaf, 0xe9, 0x7f, 0x54, 0x7a, 0xb3, 0xc3, 0x21, 0xa7, 0x6b, 0x58, 0x71, + 0xf3, 0x2a, 0x34, 0xfc, 0x2c, 0xf2, 0xc6, 0x11, 0xaf, 0x49, 0x54, 0x75, 0x9f, 0xf7, 0x36, 0x4c, 0x69, 0xc7, 0x38, + 0x00, 0x38, 0x51, 0xab, 0x86, 0x7d, 0x69, 0x5c, 0xab, 0x83, 0x18, 0x86, 0x12, 0xb6, 0x4a, 0x1c, 0x09, 0xe5, 0x6f, + 0x00, 0xc2, 0x62, 0x28, 0x8e, 0xb7, 0x86, 0xf5, 0x01, 0xf6, 0x43, 0x17, 0x68, 0x9a, 0x53, 0xaa, 0x19, 0x00, 0x24, + 0x01, 0x7f, 0xf4, 0x74, 0xd3, 0x50, 0xd9, 0xe6, 0x79, 0x68, 0x59, 0x5d, 0xc1, 0x03, 0x3d, 0x75, 0x25, 0x03, 0xe3, + 0xaa, 0x8e, 0xbd, 0xed, 0xfd, 0xed, 0xd1, 0x2a, 0xf2, 0xbd, 0x4d, 0x6a, 0x9b, 0x55, 0xa1, 0xb1, 0x8f, 0x27, 0xf4, + 0x74, 0x02, 0xb4, 0x5e, 0x5b, 0x2a, 0xda, 0xef, 0xa3, 0x18, 0x35, 0x2e, 0x14, 0x58, 0x85, 0x89, 0x04, 0x87, 0x08, + 0x23, 0x84, 0x7e, 0x5f, 0x86, 0x5b, 0x5f, 0x90, 0x41, 0x34, 0x5c, 0x8b, 0x8e, 0x3f, 0xe4, 0x78, 0xd1, 0xb6, 0x54, + 0xd5, 0x9c, 0x34, 0x6d, 0x09, 0xbc, 0x09, 0x07, 0xd8, 0xce, 0x3f, 0x6d, 0x88, 0x5c, 0x85, 0x8b, 0x12, 0xbe, 0x27, + 0xae, 0x05, 0xd1, 0x4d, 0x6d, 0xea, 0x6d, 0xd8, 0x21, 0x3a, 0x9a, 0xe2, 0xd1, 0x21, 0xf7, 0xdc, 0x3d, 0xb7, 0x45, + 0x7c, 0xf3, 0x09, 0x72, 0xd7, 0x74, 0xf6, 0x52, 0x84, 0x41, 0xdd, 0xb2, 0x81, 0x62, 0x1d, 0x3b, 0x41, 0x01, 0x46, + 0x6d, 0xf9, 0x0b, 0xe8, 0xd8, 0x60, 0x50, 0x11, 0x7c, 0x52, 0xd8, 0x36, 0x0d, 0xf2, 0x47, 0xbc, 0x1b, 0x3a, 0xbc, + 0xb6, 0xe4, 0x81, 0x78, 0x85, 0x7d, 0xa2, 0x84, 0xfb, 0x17, 0x14, 0x74, 0x47, 0x79, 0xb9, 0x2a, 0x5c, 0x95, 0x06, + 0xa0, 0xca, 0x9e, 0xe7, 0x5a, 0x53, 0xd2, 0x02, 0x56, 0x4a, 0xea, 0xce, 0x6f, 0x22, 0xe2, 0x96, 0x4c, 0xc5, 0x6c, + 0xd5, 0x8d, 0x2a, 0x8f, 0x25, 0x8a, 0x74, 0xec, 0xd9, 0xce, 0xc1, 0x1a, 0x00, 0x4f, 0x61, 0x7b, 0x71, 0x86, 0x05, + 0x65, 0x5c, 0xb6, 0xcc, 0x25, 0x50, 0xd4, 0x0f, 0xe3, 0xbc, 0xec, 0xf9, 0x72, 0x77, 0xb4, 0xbd, 0x87, 0xde, 0x88, + 0x8d, 0xf1, 0xfa, 0x3c, 0x6a, 0xfa, 0xd9, 0x33, 0x5c, 0x59, 0x0a, 0xf2, 0x40, 0x53, 0x3d, 0xc2, 0xe8, 0x10, 0x98, + 0xa6, 0x7c, 0xc6, 0xc6, 0xd3, 0xe1, 0xd0, 0x90, 0x41, 0xaf, 0x99, 0x18, 0xff, 0xeb, 0x33, 0x68, 0x9d, 0x99, 0xb8, + 0xc6, 0xa7, 0xed, 0x2b, 0x68, 0x75, 0x8b, 0x32, 0xb9, 0x33, 0x30, 0x7c, 0xa0, 0x25, 0x53, 0x30, 0x55, 0x78, 0x43, + 0xa4, 0x92, 0xfd, 0xb5, 0xb2, 0x0e, 0xfb, 0x76, 0xa1, 0xd0, 0x42, 0x13, 0xbf, 0xca, 0x10, 0x3f, 0x75, 0x9d, 0xf9, + 0xb7, 0x69, 0x9f, 0x1a, 0xc4, 0xc2, 0x92, 0x18, 0x85, 0xf8, 0xc5, 0xa9, 0xb2, 0x9d, 0x10, 0x2a, 0x20, 0x1e, 0xba, + 0xd6, 0x8d, 0x23, 0xa9, 0x62, 0x4f, 0x0a, 0x8d, 0xa7, 0x86, 0xfb, 0x5e, 0xe8, 0x98, 0x75, 0x98, 0xc5, 0x6d, 0xd6, + 0x48, 0x6a, 0x8c, 0x53, 0x61, 0x82, 0x53, 0xca, 0x75, 0x24, 0x30, 0x3a, 0x9e, 0x2d, 0x0c, 0xa2, 0x4a, 0x62, 0x92, + 0xb1, 0xb5, 0x10, 0x26, 0x76, 0x9d, 0x2b, 0x4c, 0x53, 0x17, 0xa9, 0xdf, 0x0c, 0x4c, 0x16, 0x34, 0xe4, 0xf7, 0x68, + 0xb4, 0xa6, 0x6a, 0x0a, 0x30, 0x8c, 0xa3, 0x54, 0xe3, 0xdf, 0x22, 0xd4, 0x66, 0x18, 0x00, 0xd8, 0xe6, 0x9d, 0xcc, + 0x44, 0xf5, 0x4a, 0x20, 0x04, 0x9a, 0xb3, 0x9f, 0x2a, 0xaa, 0xbd, 0x59, 0x30, 0x8a, 0x76, 0x7b, 0xe5, 0xf3, 0x81, + 0x13, 0xca, 0x13, 0x75, 0x81, 0x7a, 0x29, 0x8b, 0xd7, 0x32, 0xe5, 0xad, 0xb8, 0x98, 0x07, 0x92, 0x7d, 0xc8, 0x47, + 0x70, 0x5e, 0xa1, 0x53, 0xb9, 0xd9, 0x26, 0xca, 0x2c, 0x49, 0x32, 0x16, 0x18, 0x9b, 0x97, 0x60, 0x2e, 0x35, 0x33, + 0x86, 0x5f, 0x43, 0x70, 0xb1, 0xbd, 0x93, 0x70, 0x7b, 0x3f, 0x0f, 0x0c, 0xa1, 0xc9, 0x45, 0x4b, 0x34, 0x6c, 0xed, + 0x78, 0x3d, 0xb9, 0x26, 0xdc, 0x87, 0x8d, 0x58, 0x93, 0x31, 0xc6, 0xb5, 0xb9, 0x91, 0xf5, 0xa3, 0x05, 0x1e, 0x8c, + 0x29, 0xeb, 0x4f, 0x20, 0xd3, 0x4a, 0xca, 0xba, 0x58, 0x1a, 0x31, 0x93, 0x4a, 0xf4, 0x6e, 0xdf, 0xf8, 0xac, 0xee, + 0x22, 0xea, 0xb7, 0xf6, 0x7b, 0x52, 0x0f, 0x77, 0xfe, 0x83, 0xc2, 0x1a, 0x54, 0x46, 0x5c, 0x46, 0x94, 0x67, 0x0e, + 0x74, 0xd3, 0xa4, 0x88, 0xd3, 0xb3, 0x75, 0x5c, 0x94, 0x3c, 0x85, 0x4a, 0x35, 0x75, 0x8b, 0x7a, 0x13, 0xb0, 0x37, + 0x44, 0x92, 0x64, 0x2d, 0x8d, 0xad, 0xd8, 0xa5, 0x41, 0x7a, 0xee, 0x0d, 0xb3, 0xf4, 0xb2, 0x42, 0x43, 0x5a, 0xea, + 0x9d, 0x85, 0x4a, 0xe6, 0xaf, 0xf8, 0xcf, 0xa0, 0x56, 0xa0, 0xa3, 0x4d, 0x8a, 0xf1, 0x0c, 0x18, 0xf1, 0xfd, 0x08, + 0x56, 0x0f, 0x10, 0x17, 0x4d, 0x50, 0xea, 0x3d, 0xb1, 0xe3, 0xa7, 0x26, 0x0f, 0xef, 0x42, 0xce, 0x19, 0x7c, 0xfa, + 0x30, 0x4b, 0xd4, 0x5a, 0x47, 0x62, 0xa4, 0x66, 0x00, 0x4d, 0x07, 0x65, 0xce, 0x63, 0x11, 0xcc, 0x7b, 0x26, 0x31, + 0xea, 0x71, 0xfd, 0x0b, 0x34, 0xd4, 0x7e, 0xb3, 0xb2, 0x3c, 0xab, 0xee, 0x3e, 0x87, 0x03, 0x9b, 0xda, 0x0a, 0x7a, + 0xbc, 0xae, 0xe4, 0xe5, 0xa5, 0xea, 0xb6, 0x5f, 0x88, 0x91, 0xd3, 0x35, 0xae, 0xa5, 0x8b, 0x6a, 0xc9, 0x7a, 0xdd, + 0xe9, 0x66, 0x71, 0x37, 0xcb, 0x68, 0x20, 0xac, 0xed, 0x7d, 0xa2, 0xf9, 0xb3, 0x66, 0xdb, 0x7d, 0xbc, 0x05, 0x31, + 0x0f, 0x00, 0x20, 0x3d, 0x88, 0x82, 0x55, 0x96, 0xf2, 0x80, 0xca, 0xfb, 0x38, 0xca, 0x42, 0xe9, 0xe5, 0x2c, 0xe3, + 0xa7, 0x4d, 0x63, 0xad, 0xb3, 0x42, 0x19, 0x5a, 0x1b, 0xdd, 0xe9, 0x3a, 0x43, 0x6c, 0x3f, 0x89, 0xb3, 0x05, 0xb8, + 0x3f, 0x66, 0x28, 0x34, 0x74, 0x96, 0x91, 0x26, 0x1a, 0xbe, 0xeb, 0x9e, 0x41, 0x46, 0x71, 0xb2, 0xce, 0x2b, 0xe9, + 0x56, 0x9f, 0xb5, 0x91, 0x30, 0xf7, 0x10, 0xfd, 0x2a, 0x06, 0x8f, 0x72, 0x9f, 0xd7, 0x46, 0x27, 0xd3, 0x32, 0xd2, + 0xee, 0xfc, 0xa4, 0x5e, 0x65, 0xa9, 0xd6, 0x61, 0xfb, 0x0c, 0x7b, 0x6b, 0x4c, 0x7a, 0x13, 0x52, 0xc3, 0x48, 0x7c, + 0x3a, 0xa3, 0x46, 0x08, 0x68, 0xcb, 0xf1, 0xf7, 0xf8, 0x0c, 0x43, 0x53, 0x60, 0xa9, 0xe2, 0x16, 0x76, 0xc3, 0xd7, + 0x7c, 0xb2, 0x6a, 0x01, 0x88, 0x60, 0xe5, 0xeb, 0x5d, 0xbc, 0x12, 0xea, 0x33, 0x6d, 0x06, 0x80, 0x2c, 0x28, 0xe5, + 0x8e, 0x9f, 0x52, 0xe9, 0x60, 0x89, 0xa2, 0xed, 0xe5, 0xf4, 0x8d, 0x8e, 0x8d, 0x1f, 0xd2, 0x73, 0x01, 0xdb, 0x85, + 0xfc, 0xd6, 0xbd, 0x7a, 0x89, 0x8a, 0xd4, 0xb6, 0x59, 0x0f, 0xf0, 0xe5, 0x06, 0x4d, 0xc2, 0x08, 0xca, 0x94, 0x29, + 0x80, 0xc1, 0x4d, 0x35, 0x0a, 0x26, 0xad, 0x46, 0xc2, 0x96, 0x7a, 0x92, 0xe5, 0xa6, 0x0f, 0x4e, 0x75, 0x8f, 0xa0, + 0x47, 0x3b, 0x9c, 0xb4, 0xec, 0xd7, 0x0a, 0x8e, 0x4e, 0xae, 0x86, 0xa8, 0x99, 0xf7, 0xda, 0x8e, 0x0c, 0x29, 0x97, + 0x61, 0x20, 0x98, 0x72, 0xcc, 0xd3, 0x63, 0xeb, 0x19, 0x11, 0x3d, 0x70, 0xf6, 0x99, 0x6e, 0xd5, 0x95, 0x04, 0x44, + 0xc7, 0xaf, 0x9f, 0xbc, 0xba, 0x8a, 0x2f, 0x0d, 0x8a, 0x52, 0xc3, 0x22, 0x46, 0x99, 0xf6, 0x55, 0x12, 0x06, 0xef, + 0x97, 0xf7, 0x3f, 0xa9, 0x2c, 0xb5, 0xdf, 0x83, 0xad, 0x15, 0x55, 0xfd, 0x52, 0xf2, 0xa2, 0x29, 0xc0, 0xba, 0xcf, + 0x12, 0x05, 0x72, 0xbf, 0xb7, 0x69, 0xe6, 0x9b, 0xa8, 0x71, 0xb3, 0x61, 0xbd, 0x71, 0xdd, 0x2e, 0xb5, 0x25, 0x3b, + 0xb2, 0x12, 0x39, 0xb3, 0x18, 0xcc, 0xf8, 0x51, 0x61, 0x50, 0x1a, 0xb6, 0xa8, 0x4a, 0xc5, 0xef, 0x8d, 0x08, 0x4e, + 0x1d, 0xab, 0x0a, 0x63, 0x1a, 0x30, 0xdb, 0x8a, 0x5a, 0x83, 0x3a, 0x28, 0xa5, 0xad, 0x89, 0xc2, 0xf6, 0x1b, 0x2b, + 0xa8, 0xf9, 0xfd, 0x4f, 0x63, 0xc8, 0xd7, 0x94, 0x82, 0x4a, 0x02, 0x76, 0x06, 0x8d, 0x9e, 0x2a, 0x61, 0x20, 0x05, + 0xc1, 0x13, 0xa0, 0x7c, 0x11, 0x35, 0x56, 0xfb, 0x7d, 0x75, 0x6a, 0x8c, 0xb6, 0x80, 0xd0, 0x42, 0x7a, 0x74, 0xd9, + 0xc7, 0x6d, 0xad, 0x03, 0x89, 0x07, 0x27, 0xd8, 0xce, 0xd5, 0x35, 0x1a, 0x09, 0xcd, 0x1f, 0x1a, 0x0d, 0x78, 0x4d, + 0x2b, 0x50, 0xa8, 0xe7, 0x38, 0x1a, 0x3a, 0x3b, 0xa4, 0x20, 0x62, 0x83, 0x16, 0xf6, 0xdd, 0xf3, 0xa1, 0xd9, 0xd7, + 0x8b, 0x64, 0x49, 0x6a, 0x2a, 0xdd, 0xe7, 0x6e, 0x09, 0x59, 0xab, 0x0e, 0x65, 0xe5, 0x01, 0x8e, 0x17, 0x4a, 0xe6, + 0xef, 0x30, 0xa9, 0x51, 0x1a, 0x13, 0x1a, 0x23, 0x16, 0xb0, 0x24, 0x68, 0xaf, 0x07, 0xea, 0x97, 0x41, 0xa8, 0x70, + 0xa6, 0x27, 0x12, 0x9f, 0x52, 0xae, 0x3e, 0x2d, 0x48, 0x3d, 0x2d, 0x98, 0x03, 0xbd, 0xf4, 0xad, 0xfc, 0xca, 0xc6, + 0x47, 0xfb, 0x7b, 0xd7, 0x5c, 0x58, 0xc7, 0x10, 0x0c, 0x5b, 0xf8, 0xcd, 0xa9, 0x29, 0x00, 0x1b, 0x9e, 0xe8, 0xb2, + 0x7c, 0xa3, 0x26, 0x32, 0x8f, 0x43, 0x12, 0x81, 0x64, 0xbb, 0xb9, 0xb9, 0x8d, 0x60, 0xdb, 0x5b, 0xa8, 0x0d, 0xf5, + 0x97, 0xb7, 0xdd, 0xef, 0x19, 0x5e, 0xee, 0xc9, 0xbd, 0x9b, 0x36, 0x94, 0x3f, 0xdc, 0xbf, 0x4a, 0xfe, 0xaf, 0x2a, + 0xb9, 0xdf, 0x2a, 0xb3, 0x6e, 0x8b, 0xf7, 0xbb, 0x8e, 0x5b, 0x8e, 0xd1, 0x20, 0xb0, 0xa6, 0xc0, 0x40, 0x7a, 0xd2, + 0x98, 0x26, 0x3a, 0xa4, 0x32, 0x63, 0x06, 0x8f, 0x2e, 0x40, 0x73, 0x98, 0xce, 0xf3, 0x18, 0x80, 0x03, 0xfc, 0x23, + 0x8f, 0x50, 0xff, 0x74, 0x5e, 0x04, 0x67, 0xc1, 0xa0, 0x1c, 0x04, 0xfa, 0x13, 0xd7, 0x9c, 0x60, 0x09, 0x3a, 0xb7, + 0x98, 0x41, 0xb0, 0x49, 0x6b, 0xe6, 0x10, 0x1f, 0x27, 0xd3, 0xc1, 0x20, 0x26, 0x5b, 0x00, 0xe9, 0x8b, 0x97, 0xd6, + 0x39, 0xa8, 0xd0, 0x0b, 0xb2, 0x55, 0x77, 0xd1, 0xac, 0xd8, 0xab, 0x76, 0x9a, 0xf7, 0xfb, 0xf9, 0xa2, 0x1c, 0x04, + 0x8d, 0x0a, 0x0b, 0xe3, 0xfd, 0x47, 0x9b, 0x5f, 0x1a, 0x9d, 0x34, 0xc1, 0x30, 0xb5, 0x27, 0xa8, 0x5e, 0xf1, 0x34, + 0xa3, 0x8d, 0xdb, 0xb1, 0x52, 0xbe, 0x80, 0x28, 0x1e, 0x18, 0xb2, 0x56, 0xde, 0xbd, 0x83, 0xd7, 0xe5, 0xc6, 0x9b, + 0x23, 0x0a, 0xb0, 0x9b, 0xc2, 0x38, 0xa9, 0xb9, 0xe8, 0xa2, 0x26, 0x9e, 0xc1, 0x4e, 0x57, 0x6f, 0x25, 0x5a, 0x8d, + 0xf7, 0xe2, 0x7d, 0xb3, 0xf1, 0x37, 0xf2, 0x40, 0x97, 0x79, 0x70, 0x01, 0x88, 0xb3, 0x07, 0x71, 0x75, 0x80, 0xa5, + 0x1e, 0x04, 0x03, 0x8b, 0x1c, 0xd2, 0xae, 0x56, 0x0f, 0x45, 0xa4, 0xce, 0x63, 0x30, 0x60, 0x32, 0x0d, 0xa9, 0xc9, + 0xb4, 0x57, 0x28, 0x48, 0x1b, 0x6b, 0x2d, 0xa0, 0x0d, 0x87, 0xc5, 0x9e, 0xdd, 0xb0, 0x3b, 0xdd, 0x3a, 0x14, 0x4a, + 0x18, 0xbd, 0xba, 0x6e, 0x1e, 0x6a, 0x0d, 0x4f, 0x04, 0x3d, 0xa8, 0x46, 0xfb, 0xe9, 0xa1, 0x3c, 0x69, 0x8f, 0x05, + 0xb8, 0xe8, 0xe1, 0xcb, 0x17, 0x02, 0x2f, 0xda, 0x7b, 0xc8, 0x73, 0xe6, 0x53, 0xe5, 0x83, 0xd8, 0x70, 0xcb, 0xf0, + 0xa1, 0x7d, 0x7c, 0x2b, 0x90, 0x49, 0xdd, 0xd1, 0xd4, 0xd6, 0xee, 0x68, 0x1c, 0x13, 0xe8, 0x37, 0xe5, 0x28, 0x65, + 0x62, 0x6a, 0x59, 0xb1, 0x59, 0x2f, 0x57, 0xde, 0x50, 0x29, 0x9b, 0xad, 0xda, 0x9c, 0x5f, 0xda, 0x48, 0xe8, 0xf7, + 0xb5, 0x3b, 0x10, 0xbe, 0x51, 0xeb, 0x0d, 0x79, 0xd9, 0x10, 0xb1, 0x1c, 0x62, 0x06, 0x8e, 0x17, 0x52, 0xb9, 0x76, + 0x17, 0x4d, 0x55, 0xdd, 0xde, 0x56, 0x2e, 0x68, 0x89, 0xb7, 0x52, 0x60, 0x15, 0xa9, 0xd3, 0xeb, 0xa9, 0xc4, 0xfb, + 0x3e, 0x8a, 0xed, 0x47, 0xc0, 0x36, 0x36, 0x8e, 0xc6, 0xc6, 0x2d, 0x62, 0x8b, 0xaf, 0xa2, 0x8a, 0x16, 0x1c, 0x20, + 0xb8, 0xdb, 0x92, 0x5a, 0x9a, 0x39, 0xc4, 0x7d, 0xc5, 0x03, 0xb4, 0xef, 0xe2, 0x88, 0x53, 0x01, 0xb6, 0x75, 0xad, + 0x73, 0x56, 0xcb, 0x01, 0x9b, 0x89, 0x9e, 0x7f, 0x5a, 0x35, 0x12, 0x31, 0xac, 0xb2, 0x91, 0xb2, 0x42, 0x7b, 0x50, + 0xba, 0x84, 0x8b, 0x2f, 0xc0, 0xcb, 0xf6, 0xfd, 0xca, 0xee, 0xb3, 0x15, 0xf6, 0x0f, 0xf3, 0xaa, 0x09, 0x1e, 0x79, + 0x8d, 0xb7, 0xf7, 0x30, 0xf1, 0xb9, 0x52, 0x08, 0xaf, 0x52, 0x1a, 0x4a, 0x00, 0x06, 0x49, 0x50, 0xc3, 0x95, 0xb6, + 0xcd, 0x20, 0x95, 0x31, 0xec, 0x7e, 0xf5, 0x56, 0xff, 0xa7, 0x55, 0xb8, 0xa8, 0x64, 0x31, 0x26, 0x81, 0xce, 0xa9, + 0x96, 0x9b, 0x98, 0x82, 0x67, 0xfb, 0xe4, 0x08, 0x14, 0x76, 0x02, 0xb8, 0xa1, 0x84, 0xfd, 0x82, 0xb7, 0xa1, 0x9c, + 0xbd, 0xb4, 0x92, 0x27, 0xb7, 0x2f, 0xa9, 0xa0, 0x09, 0x99, 0x0a, 0xbb, 0x7f, 0x5b, 0x1b, 0xf6, 0x79, 0x28, 0x47, + 0x52, 0xe0, 0xe2, 0xa0, 0x0b, 0x00, 0xfb, 0x83, 0x5c, 0xc6, 0xe6, 0x33, 0xe9, 0xf7, 0xd5, 0xfb, 0x67, 0x79, 0x96, + 0x7c, 0xdc, 0x7b, 0x6f, 0x78, 0x9a, 0x15, 0x03, 0x2a, 0x11, 0x53, 0xeb, 0xaa, 0x18, 0xae, 0xb4, 0x8b, 0x71, 0x83, + 0x64, 0xc4, 0xf7, 0x52, 0x87, 0x18, 0x31, 0xbe, 0xc8, 0x1e, 0x49, 0xc9, 0xe9, 0xaa, 0xee, 0xec, 0xb9, 0x16, 0xcd, + 0xa0, 0x31, 0xdc, 0x9e, 0xf7, 0x92, 0x5e, 0x01, 0x2a, 0x2a, 0x74, 0xcf, 0x02, 0xd7, 0xf0, 0xe6, 0x92, 0x68, 0x6c, + 0xe9, 0x69, 0x4b, 0x34, 0x70, 0xaf, 0x4c, 0x48, 0xaa, 0x8d, 0x03, 0x2c, 0x62, 0x5d, 0x7f, 0x0c, 0x25, 0x00, 0xb5, + 0x1a, 0xa4, 0x57, 0xfa, 0x92, 0x50, 0x95, 0x84, 0x60, 0x74, 0x22, 0xe1, 0x65, 0x40, 0xe3, 0xcc, 0x24, 0x5a, 0xd8, + 0xe0, 0x80, 0x3e, 0xaf, 0x4c, 0xa2, 0xb1, 0x21, 0x0f, 0x68, 0x65, 0xd3, 0x00, 0x06, 0x1f, 0x24, 0x49, 0xf4, 0xd5, + 0xca, 0x24, 0x81, 0xa0, 0x04, 0xe5, 0x1b, 0xf4, 0xe7, 0xd2, 0xf3, 0xb1, 0xfc, 0x97, 0x77, 0x28, 0xfd, 0x10, 0x4a, + 0x90, 0x29, 0xea, 0x8a, 0x69, 0xc6, 0x66, 0x59, 0xb7, 0x31, 0x89, 0xe7, 0x69, 0x77, 0x5b, 0x28, 0x97, 0x2e, 0xf0, + 0x2b, 0xcb, 0x10, 0xc7, 0xfa, 0x59, 0xbc, 0x66, 0x27, 0x21, 0xd7, 0x78, 0xe9, 0xcf, 0xe2, 0x35, 0xce, 0x10, 0xad, + 0x5a, 0x09, 0x44, 0xf9, 0xaf, 0xda, 0xc0, 0x21, 0xee, 0x13, 0x0c, 0x72, 0x51, 0x79, 0x0f, 0x04, 0xf2, 0xb6, 0x82, + 0x88, 0x34, 0xb3, 0xeb, 0x30, 0x22, 0xd5, 0x5e, 0x92, 0xf9, 0xf2, 0x47, 0x99, 0x09, 0xef, 0x1b, 0x78, 0x6c, 0x36, + 0xcb, 0xa6, 0x98, 0x2f, 0x54, 0x30, 0x07, 0xf7, 0x89, 0x8a, 0x4b, 0x51, 0xf9, 0x4f, 0xd8, 0x05, 0x2f, 0xc6, 0x83, + 0xd7, 0x6b, 0x04, 0xd8, 0xaf, 0xfc, 0x27, 0x6f, 0xcc, 0xde, 0x58, 0x37, 0xbe, 0xcc, 0x44, 0x7c, 0xe0, 0xa3, 0x5b, + 0xca, 0x47, 0x77, 0x5e, 0xa6, 0x3f, 0x1a, 0x50, 0x22, 0xa3, 0xb2, 0xe2, 0xeb, 0x35, 0x4f, 0xe7, 0xb7, 0x49, 0x94, + 0x8d, 0x2a, 0x2e, 0x60, 0x7a, 0xc1, 0xf1, 0x2e, 0xd9, 0x9c, 0x67, 0xc9, 0x2b, 0x88, 0x3d, 0xb0, 0x96, 0x0a, 0x8b, + 0x1f, 0x96, 0x99, 0x5a, 0xcc, 0x42, 0x56, 0x52, 0xf0, 0x60, 0x7e, 0x9d, 0x44, 0x6f, 0x56, 0x1e, 0x92, 0x9a, 0x99, + 0xb2, 0x6d, 0xed, 0x08, 0xb5, 0xf1, 0x75, 0xa4, 0x5b, 0x6d, 0x01, 0x00, 0xf7, 0x6c, 0x91, 0x46, 0x92, 0x89, 0xe1, + 0xa4, 0x66, 0xdc, 0xa4, 0x17, 0x98, 0x1a, 0xd7, 0xac, 0xa2, 0x89, 0xb3, 0x90, 0x01, 0xbd, 0x3f, 0xe0, 0xe5, 0xe0, + 0x73, 0x06, 0xf7, 0x1f, 0xb4, 0x06, 0x2e, 0x8f, 0x8b, 0x7e, 0x5f, 0x1e, 0x17, 0xbb, 0x5d, 0x39, 0x8b, 0xfb, 0x7d, + 0x39, 0x8b, 0x0d, 0xff, 0xa0, 0x14, 0xdb, 0xc6, 0xdc, 0x20, 0xa1, 0xb9, 0x84, 0xa8, 0x45, 0x23, 0xf8, 0x43, 0xb3, + 0x9c, 0x8b, 0x28, 0x3f, 0x4e, 0xfa, 0xfd, 0xde, 0x6a, 0x2e, 0x06, 0xf9, 0x30, 0x89, 0xf2, 0x61, 0xe2, 0x39, 0x21, + 0xfe, 0xea, 0x39, 0x21, 0x2a, 0x1a, 0xb8, 0x86, 0x33, 0x03, 0x10, 0x05, 0x7c, 0xfa, 0x47, 0x75, 0x2d, 0x85, 0xae, + 0x25, 0x56, 0xb5, 0x24, 0xba, 0x82, 0x9a, 0x5d, 0x17, 0x61, 0x89, 0xa5, 0xd0, 0x15, 0xfb, 0x63, 0x05, 0x3c, 0x51, + 0xce, 0xab, 0x2d, 0x30, 0xb0, 0x11, 0xde, 0x39, 0x4c, 0x38, 0x89, 0x4d, 0x0d, 0x68, 0xa7, 0xdb, 0x9a, 0x5e, 0xd0, + 0x35, 0xbd, 0x44, 0x7e, 0xf6, 0x02, 0x0c, 0x96, 0x8e, 0x59, 0x3e, 0x1d, 0x0c, 0x2e, 0xc8, 0x9a, 0x95, 0x8b, 0x30, + 0x1e, 0x84, 0x9b, 0x79, 0x3e, 0xbc, 0x88, 0x2e, 0x08, 0xf9, 0xa2, 0x58, 0xd2, 0xde, 0x7a, 0x54, 0x7e, 0xcc, 0x20, + 0xb8, 0x5f, 0x3a, 0x0f, 0x33, 0x13, 0xe7, 0x63, 0x3d, 0xba, 0xa5, 0x6b, 0x88, 0x5f, 0x03, 0x37, 0x12, 0x12, 0x41, + 0x47, 0x2e, 0xe9, 0x9a, 0x6e, 0xa8, 0x34, 0x33, 0x8c, 0x21, 0xba, 0xed, 0x71, 0x92, 0x80, 0x63, 0xb2, 0x2b, 0x3e, + 0x1a, 0xab, 0xc2, 0xbb, 0xbe, 0x23, 0xb4, 0xd7, 0x4b, 0xdc, 0x20, 0x7d, 0xd7, 0x1e, 0x24, 0x60, 0x44, 0x46, 0x6a, + 0xa0, 0xcc, 0xc8, 0x48, 0x6a, 0x26, 0x15, 0x87, 0x24, 0xf6, 0x87, 0x44, 0x8d, 0x43, 0xe2, 0x8f, 0x43, 0xae, 0xc7, + 0x01, 0xb9, 0xfb, 0x15, 0x1b, 0xd3, 0x94, 0x8d, 0xe9, 0x46, 0x8d, 0x0a, 0xbd, 0xa2, 0xe7, 0x9a, 0x3a, 0x9e, 0xb1, + 0xd7, 0x70, 0x60, 0x0f, 0xc2, 0x7c, 0x1e, 0x0f, 0x5f, 0x47, 0xaf, 0x09, 0xf9, 0x42, 0xd2, 0x6b, 0x75, 0x29, 0x83, + 0x30, 0x88, 0x57, 0xe0, 0x5c, 0xea, 0x42, 0x9d, 0x5c, 0x99, 0x1d, 0x87, 0x4f, 0x97, 0x8d, 0xa7, 0x73, 0x88, 0xe8, + 0x83, 0x56, 0x2a, 0xfd, 0x7e, 0x78, 0xc1, 0xca, 0xc5, 0x59, 0x38, 0x26, 0x80, 0xc3, 0xa3, 0x87, 0xf3, 0x62, 0x74, + 0x4b, 0x2f, 0x46, 0x77, 0x04, 0x2c, 0xbc, 0xc6, 0xd3, 0xcd, 0x31, 0x8b, 0xa7, 0x83, 0xc1, 0x06, 0xa9, 0xba, 0xca, + 0xbd, 0x21, 0x4b, 0x7a, 0x81, 0x13, 0x41, 0x80, 0xa1, 0xcf, 0xc4, 0xc6, 0xd0, 0xf0, 0xd7, 0x0c, 0x3e, 0xbe, 0x63, + 0x17, 0xa3, 0x3b, 0x7a, 0xcb, 0x5e, 0xef, 0xc6, 0x53, 0x60, 0xa6, 0xd6, 0xf3, 0xf0, 0xee, 0xf8, 0x72, 0x7e, 0xc9, + 0xee, 0xa2, 0xbb, 0x19, 0x34, 0xf4, 0x8a, 0xdd, 0x21, 0xe0, 0x52, 0xfa, 0x78, 0x35, 0x78, 0x4d, 0x0e, 0x07, 0x83, + 0x94, 0x44, 0xe1, 0x75, 0xe8, 0xb5, 0xf2, 0x35, 0xbd, 0x23, 0x74, 0xcd, 0x6e, 0x71, 0x34, 0x2e, 0x19, 0x7e, 0x70, + 0xce, 0xee, 0xea, 0xeb, 0xd0, 0xdb, 0xcd, 0x89, 0xe8, 0x04, 0x31, 0x42, 0x5f, 0x03, 0x47, 0xb3, 0x5c, 0x98, 0x09, + 0x78, 0x32, 0x17, 0x19, 0x2d, 0x0a, 0xcd, 0x40, 0x9c, 0x95, 0x80, 0x58, 0x12, 0x75, 0xbf, 0xd9, 0xe8, 0x0c, 0x96, + 0x73, 0xbf, 0xdf, 0xab, 0x0c, 0x3d, 0x40, 0xe4, 0xcc, 0x4e, 0x7a, 0xd0, 0xf3, 0xe9, 0x01, 0x7e, 0xa2, 0x57, 0x0d, + 0xe2, 0x64, 0x7e, 0xb7, 0x8a, 0x7e, 0xf5, 0xe8, 0xc3, 0x0f, 0xdd, 0x94, 0x47, 0xe4, 0xff, 0x3e, 0xe5, 0x29, 0xf3, + 0xe8, 0x75, 0xe5, 0x81, 0xe0, 0x79, 0x6b, 0x52, 0x69, 0x24, 0xaa, 0xd1, 0xd9, 0x3a, 0x06, 0x6d, 0x24, 0x6a, 0x1b, + 0xf4, 0x13, 0x5a, 0x58, 0x41, 0x84, 0x9c, 0xa3, 0xe7, 0x60, 0x90, 0x0a, 0xa1, 0x72, 0xd4, 0xa2, 0x44, 0x43, 0x90, + 0x5c, 0x96, 0x5c, 0x85, 0xcf, 0x21, 0x54, 0x9d, 0x3e, 0xce, 0x44, 0xd8, 0xd0, 0xe3, 0xd0, 0x07, 0x80, 0xff, 0x65, + 0x8f, 0x5c, 0x94, 0xfc, 0x12, 0xcf, 0xe6, 0x36, 0xc1, 0x28, 0x58, 0x22, 0x9a, 0xa1, 0x6d, 0x10, 0xfb, 0xb1, 0x24, + 0x58, 0x8f, 0xa4, 0xf1, 0xa8, 0x34, 0x47, 0x84, 0x1f, 0xc5, 0x47, 0xd1, 0xd3, 0xd8, 0x90, 0x48, 0x8e, 0x24, 0x92, + 0x0f, 0x80, 0x70, 0x12, 0xf4, 0x17, 0x77, 0x4d, 0x76, 0x2d, 0x24, 0x06, 0xfd, 0x69, 0xc5, 0xb4, 0xec, 0x5e, 0xf5, + 0xd8, 0x57, 0x04, 0xb9, 0x63, 0xfa, 0x4f, 0xaf, 0x0f, 0xff, 0x5c, 0xe1, 0x0c, 0x5a, 0xcf, 0x17, 0xd5, 0x99, 0xb9, + 0x37, 0xb8, 0x91, 0xd7, 0x65, 0xed, 0xba, 0x7c, 0xc1, 0x0f, 0xf8, 0x6d, 0xc5, 0x45, 0x5a, 0x1e, 0xfc, 0x5c, 0xb5, + 0xf1, 0x9c, 0xca, 0xcd, 0xda, 0xc5, 0x59, 0x51, 0xc6, 0xa9, 0x9e, 0xd4, 0xc5, 0x58, 0xc3, 0x36, 0xfc, 0x1e, 0x51, + 0x57, 0xd2, 0x72, 0xf4, 0x94, 0x72, 0xdd, 0x4c, 0xb9, 0xd8, 0xe4, 0xf9, 0x4f, 0x7b, 0xa9, 0x38, 0xc5, 0xcd, 0x14, + 0xa4, 0x4a, 0x2d, 0x17, 0x50, 0x3d, 0x47, 0x2d, 0x77, 0x4b, 0xb3, 0x03, 0x9c, 0xdb, 0xa6, 0xfa, 0x58, 0x99, 0x5d, + 0x78, 0xc9, 0x8d, 0xfb, 0x93, 0x29, 0xc3, 0x82, 0x51, 0x68, 0xb3, 0xea, 0x4a, 0xdb, 0x17, 0x5a, 0xa7, 0x61, 0xb8, + 0xf2, 0xe3, 0x05, 0xa4, 0x0b, 0x18, 0xc7, 0x8b, 0x92, 0x89, 0x71, 0x7b, 0xf4, 0x56, 0x10, 0x9f, 0xb3, 0x15, 0x08, + 0x98, 0x6b, 0x78, 0xbb, 0xae, 0xa3, 0xed, 0x9e, 0x38, 0x65, 0x54, 0xae, 0x63, 0xf1, 0x7d, 0xbc, 0x36, 0x90, 0xc9, + 0xea, 0x78, 0x6c, 0x8c, 0xe9, 0xf4, 0xef, 0x49, 0xe8, 0x17, 0x42, 0xc1, 0x67, 0xbd, 0xb4, 0xf2, 0xe4, 0xf6, 0xb0, + 0x8c, 0x6b, 0xf4, 0x4a, 0x5c, 0xeb, 0xbe, 0x19, 0x29, 0xa4, 0x1e, 0xf9, 0xaa, 0x29, 0xa0, 0x37, 0x63, 0xdf, 0x4c, + 0x85, 0x79, 0xbb, 0x67, 0xcc, 0x15, 0x82, 0x95, 0x2a, 0xbb, 0x7d, 0xa7, 0xc6, 0x54, 0xcc, 0x60, 0x8a, 0x6d, 0x67, + 0x31, 0xe9, 0x56, 0xfe, 0x69, 0xe7, 0x7e, 0x95, 0x77, 0xb8, 0x2b, 0xea, 0xb7, 0xc0, 0x85, 0x66, 0x45, 0x59, 0xb5, + 0x65, 0xc3, 0xb6, 0xf1, 0x46, 0x16, 0x8a, 0x0d, 0xb0, 0xec, 0xb9, 0x6f, 0xe1, 0x01, 0xe2, 0x26, 0xdc, 0xb3, 0xcb, + 0x1a, 0x6e, 0x0c, 0x9f, 0x57, 0x92, 0xef, 0x4a, 0x63, 0x2e, 0x7d, 0xaa, 0x34, 0x31, 0x9c, 0x2c, 0x47, 0x5c, 0xa4, + 0xcb, 0x3a, 0xb3, 0x6b, 0xe1, 0x13, 0x5e, 0x86, 0x0b, 0xbe, 0x34, 0xba, 0x29, 0x5d, 0x7a, 0xc1, 0x62, 0xdd, 0xe9, + 0xed, 0x5a, 0x63, 0xa5, 0x44, 0xdc, 0x9a, 0x65, 0x02, 0x65, 0x29, 0x6b, 0x25, 0xbc, 0x29, 0x5a, 0xb6, 0x92, 0x46, + 0xde, 0xb3, 0x00, 0xf7, 0xb1, 0x1f, 0x10, 0x13, 0xd9, 0x04, 0x26, 0x45, 0x43, 0x07, 0xb4, 0xab, 0x2e, 0x7c, 0x33, + 0xea, 0xc1, 0x20, 0xb7, 0x24, 0x11, 0x2b, 0x48, 0xb1, 0x82, 0x4d, 0xcd, 0x8a, 0x45, 0xbe, 0xa4, 0x17, 0x4c, 0x2e, + 0xd2, 0x25, 0x5d, 0x33, 0xb9, 0xd8, 0xe0, 0x4d, 0xe8, 0x02, 0x4e, 0x48, 0xb2, 0x8d, 0x95, 0x02, 0xf6, 0x02, 0x2f, + 0x6f, 0x78, 0xa6, 0x6a, 0x5a, 0x76, 0xa9, 0x38, 0xc0, 0xf8, 0xbc, 0x0c, 0xc3, 0x72, 0x78, 0x01, 0xd6, 0x12, 0x87, + 0xe1, 0x7a, 0xc1, 0x97, 0xea, 0x37, 0x04, 0x9c, 0x4f, 0x42, 0xc5, 0x2e, 0xd8, 0xbd, 0x40, 0xa6, 0x57, 0x0b, 0xbe, + 0x54, 0x23, 0xa1, 0x0b, 0xbe, 0xb2, 0xc6, 0x26, 0xb1, 0x27, 0x68, 0x99, 0xc7, 0x8b, 0xf1, 0x32, 0x8a, 0x6b, 0x58, + 0x86, 0xa7, 0x6a, 0x66, 0x5a, 0xf2, 0x9f, 0x44, 0x6d, 0x68, 0xa2, 0x6f, 0xb0, 0x8a, 0xfc, 0xe1, 0xf1, 0xd1, 0x25, + 0x90, 0xb1, 0xb3, 0x2b, 0x99, 0xf9, 0xd0, 0xf7, 0x91, 0xc1, 0x3d, 0x37, 0xe5, 0x8c, 0xab, 0x20, 0x51, 0x06, 0xee, + 0x5e, 0xcd, 0x92, 0xb1, 0x16, 0xe1, 0xfb, 0x47, 0x45, 0xd1, 0x67, 0xd2, 0x34, 0xa0, 0xfb, 0x48, 0x30, 0x07, 0x7a, + 0xaf, 0xd0, 0xe1, 0xb2, 0xda, 0x66, 0x02, 0xfe, 0x22, 0x41, 0x7e, 0x2b, 0xf4, 0xaa, 0xc6, 0xa0, 0x8a, 0x76, 0x11, + 0x4b, 0xff, 0x3e, 0xe2, 0x47, 0xd9, 0xfc, 0xd3, 0xdc, 0xe3, 0x95, 0x84, 0xc1, 0x0f, 0xa9, 0xd9, 0x24, 0xf3, 0xf6, + 0x8a, 0x7d, 0x0f, 0x1d, 0xf5, 0xa8, 0x35, 0xde, 0x57, 0x2f, 0x38, 0x85, 0x18, 0x25, 0x14, 0x9d, 0x04, 0x03, 0xb8, + 0x5d, 0x42, 0x8a, 0xbb, 0xc1, 0x6e, 0x9b, 0xd7, 0xbc, 0x28, 0x38, 0xdf, 0x54, 0x55, 0xe0, 0x07, 0x34, 0x5c, 0x2c, + 0xf7, 0x43, 0x18, 0x8e, 0x69, 0xeb, 0x1a, 0x06, 0x61, 0xc6, 0x30, 0x12, 0x82, 0xd7, 0xbf, 0xe8, 0x2b, 0x9a, 0xc4, + 0xeb, 0xef, 0xf8, 0x5f, 0x19, 0x2f, 0x14, 0x91, 0x06, 0x11, 0x52, 0x37, 0xf1, 0x8d, 0x4c, 0x93, 0x02, 0x0a, 0x01, + 0x46, 0x01, 0x95, 0xd8, 0xd0, 0x54, 0xfc, 0xad, 0x16, 0x1f, 0xfc, 0xd4, 0x74, 0x3c, 0x1a, 0xd7, 0xad, 0xce, 0xa8, + 0xa0, 0x33, 0xd0, 0xa3, 0x56, 0xd4, 0xd3, 0xa0, 0x95, 0x60, 0x1a, 0x69, 0xde, 0xba, 0x87, 0xc0, 0x2b, 0xd3, 0xe2, + 0x9d, 0x07, 0x74, 0x7b, 0xe6, 0x83, 0x27, 0x8f, 0xe9, 0x99, 0x43, 0x4f, 0xae, 0xd8, 0xac, 0xea, 0xa1, 0xf6, 0xde, + 0x8c, 0x50, 0xd0, 0xef, 0x63, 0x0a, 0x74, 0x23, 0xa8, 0xbd, 0xab, 0xfb, 0x8f, 0xe5, 0x3e, 0x87, 0xef, 0x38, 0xcb, + 0x2d, 0x60, 0xa9, 0xc8, 0x5a, 0x81, 0x47, 0x01, 0xea, 0x52, 0x19, 0xc2, 0x16, 0x73, 0x38, 0x54, 0x76, 0xab, 0x56, + 0x43, 0x49, 0x8e, 0xcb, 0x11, 0x38, 0x84, 0x6e, 0xca, 0x41, 0x39, 0x5a, 0x65, 0xd5, 0x7b, 0xfc, 0xad, 0x59, 0x87, + 0x24, 0xbb, 0x8f, 0x75, 0xe0, 0x96, 0x75, 0x98, 0x7e, 0x34, 0x48, 0x01, 0x68, 0xb2, 0x11, 0xb8, 0x04, 0xe0, 0xbd, + 0xfd, 0x47, 0x84, 0x5a, 0x99, 0xde, 0xcb, 0x58, 0xa8, 0xef, 0x1b, 0x49, 0x50, 0x42, 0x33, 0xa1, 0x72, 0x2c, 0x05, + 0xef, 0x3c, 0xd2, 0x39, 0xa9, 0x33, 0xf1, 0x1e, 0xc4, 0x69, 0xe1, 0x03, 0x7b, 0x0b, 0x82, 0x73, 0x16, 0xf4, 0x0e, + 0x6f, 0xb3, 0x5a, 0x6a, 0xa3, 0x07, 0x0a, 0xe0, 0x77, 0x83, 0x3b, 0x04, 0xf9, 0x6a, 0x0c, 0xd7, 0x5a, 0xde, 0x84, + 0x7c, 0x58, 0xd0, 0x23, 0x32, 0xb0, 0xcf, 0x62, 0x18, 0xd3, 0x23, 0x72, 0x6c, 0x9f, 0xa5, 0x1b, 0xc0, 0x81, 0xd4, + 0xa3, 0x4a, 0x8f, 0xa0, 0x41, 0xbf, 0xd9, 0x16, 0x59, 0x92, 0xf5, 0x63, 0x69, 0x14, 0x31, 0x50, 0x25, 0x88, 0xa8, + 0xc5, 0x3f, 0x1f, 0xcc, 0x75, 0x87, 0xb9, 0x40, 0x98, 0x83, 0x01, 0x07, 0x71, 0x1b, 0x84, 0xe6, 0x80, 0xd9, 0xde, + 0x46, 0x82, 0xde, 0x59, 0xc3, 0xcc, 0x8e, 0xfe, 0x70, 0x2b, 0xc1, 0x37, 0x59, 0x6b, 0xd4, 0x79, 0x71, 0x08, 0x04, + 0xc1, 0x9b, 0x42, 0x55, 0x7b, 0xd5, 0x03, 0x1b, 0x6f, 0xd5, 0x8f, 0xdd, 0x6e, 0x3c, 0x15, 0xee, 0xda, 0x2f, 0x28, + 0x9c, 0x7c, 0x4a, 0xfe, 0xf5, 0xde, 0x64, 0x70, 0x60, 0x64, 0xf8, 0xd2, 0xdb, 0xbf, 0xf0, 0xb5, 0x96, 0xee, 0x89, + 0x41, 0x49, 0x1e, 0x1f, 0x29, 0xfa, 0x77, 0xaf, 0xac, 0x7c, 0x6a, 0xa7, 0x7f, 0xb7, 0x33, 0xeb, 0xf3, 0x78, 0x34, + 0xd9, 0xed, 0x7a, 0xda, 0xc0, 0x95, 0x6a, 0x15, 0x02, 0x76, 0xa1, 0x24, 0x87, 0x47, 0x10, 0x15, 0xa1, 0x19, 0x77, + 0xb3, 0x6c, 0x48, 0x64, 0xfc, 0x38, 0x9d, 0x65, 0x43, 0xb0, 0xc3, 0xbd, 0xa8, 0xc4, 0xe5, 0xa8, 0xb5, 0xc1, 0xe9, + 0x59, 0x12, 0x42, 0x28, 0x07, 0xac, 0xec, 0x56, 0xfd, 0xb9, 0x53, 0x66, 0x42, 0x6a, 0xb2, 0xba, 0x9d, 0xd2, 0x3d, + 0x4c, 0xf3, 0x03, 0x33, 0x82, 0x03, 0xee, 0xed, 0xaf, 0xfa, 0x63, 0x98, 0x64, 0x9a, 0x9c, 0x22, 0xf9, 0x45, 0x7a, + 0x0a, 0x49, 0x7b, 0xf4, 0x54, 0x11, 0xc0, 0x09, 0xb5, 0x1f, 0xc3, 0x6f, 0x18, 0xf7, 0xef, 0x9a, 0xaf, 0xdd, 0x54, + 0x44, 0x4f, 0x28, 0x96, 0xa9, 0xc9, 0x69, 0x92, 0x15, 0x09, 0x44, 0x6d, 0x54, 0xcd, 0x88, 0xbe, 0x72, 0x31, 0x1f, + 0x15, 0xe1, 0xf3, 0x6a, 0xfd, 0x9f, 0x21, 0x7c, 0x46, 0xe1, 0x06, 0x70, 0x79, 0xc5, 0xe5, 0x79, 0xf8, 0xf4, 0x09, + 0x3d, 0x98, 0x7c, 0x7d, 0x44, 0x0f, 0x8e, 0xbe, 0x7a, 0x4a, 0x00, 0x16, 0xed, 0xf2, 0x3c, 0x3c, 0x7a, 0xfa, 0x94, + 0x1e, 0x7c, 0xfb, 0x2d, 0x3d, 0x98, 0x7c, 0x75, 0xd4, 0x48, 0x9b, 0x3c, 0xfd, 0x96, 0x1e, 0x7c, 0xfd, 0xa4, 0x91, + 0x76, 0x34, 0x7e, 0x4a, 0x0f, 0xbe, 0xf9, 0xda, 0xa4, 0xfd, 0x0d, 0xb2, 0x7d, 0x7b, 0x84, 0xff, 0x99, 0xb4, 0xc9, + 0xd3, 0xaf, 0xe8, 0xc1, 0x64, 0x0c, 0x95, 0x3c, 0x75, 0x95, 0x8c, 0x27, 0xf0, 0xf1, 0x57, 0xf0, 0xdf, 0xdf, 0x48, + 0xb0, 0xa4, 0x95, 0x64, 0xb9, 0x40, 0xfd, 0x19, 0x8a, 0x38, 0x51, 0x35, 0x91, 0xf0, 0x10, 0x33, 0xab, 0x6f, 0xe2, + 0x30, 0x20, 0x2e, 0x1d, 0x0a, 0xa2, 0x07, 0xe3, 0xd1, 0x53, 0x12, 0xf8, 0xf0, 0x74, 0x37, 0x3e, 0xc8, 0x58, 0x2e, + 0x16, 0xd9, 0x17, 0xb9, 0x89, 0xad, 0xe0, 0x01, 0x58, 0x7d, 0xf4, 0x73, 0x55, 0x72, 0x91, 0x7d, 0x51, 0xc9, 0xfd, + 0x5c, 0xbf, 0xb5, 0x00, 0xe5, 0xfd, 0x55, 0xcb, 0x6e, 0x0a, 0x15, 0x3a, 0xad, 0x35, 0xfa, 0xec, 0x23, 0xa6, 0x0f, + 0x06, 0xde, 0x0d, 0xfb, 0xef, 0x7b, 0xe5, 0xb4, 0xbe, 0xd1, 0x28, 0xd4, 0xa8, 0x3c, 0x24, 0x6c, 0x06, 0x45, 0x0f, + 0x06, 0xc0, 0x13, 0x78, 0xb8, 0x6f, 0xff, 0x66, 0x19, 0x1f, 0x3b, 0xca, 0xf8, 0x19, 0x65, 0x08, 0x68, 0xd4, 0xc3, + 0xec, 0xa6, 0x87, 0x8d, 0x6e, 0xf5, 0x92, 0xa5, 0x3a, 0x99, 0x9a, 0x9e, 0xc1, 0xbe, 0xd6, 0xb5, 0x3c, 0x30, 0xa2, + 0x68, 0x79, 0x71, 0x90, 0xf2, 0x79, 0xc5, 0xfe, 0xbe, 0x42, 0xf5, 0x56, 0xd4, 0x78, 0x23, 0xb3, 0x79, 0xc5, 0xbe, + 0x37, 0x6f, 0x80, 0x9b, 0x61, 0xbf, 0xa9, 0x27, 0x3f, 0x70, 0x06, 0x97, 0xb6, 0x3d, 0xca, 0xc4, 0x08, 0xb0, 0x02, + 0x32, 0x70, 0xe0, 0x01, 0xd0, 0x41, 0x7f, 0xb4, 0x77, 0x3b, 0x95, 0xd2, 0xec, 0xb3, 0x85, 0x01, 0x34, 0xcc, 0xdb, + 0xc4, 0x95, 0xfd, 0xaf, 0x86, 0xbc, 0x04, 0x85, 0x5b, 0xcd, 0xf2, 0xf6, 0x0a, 0x43, 0x08, 0xc1, 0x1f, 0x57, 0x0c, + 0x00, 0x07, 0x02, 0x0c, 0xc6, 0x5a, 0x06, 0xd4, 0x6c, 0xf9, 0x68, 0xcb, 0x95, 0x7a, 0x12, 0x38, 0x83, 0x0b, 0x59, + 0x24, 0xfc, 0xad, 0x16, 0xfb, 0xa3, 0xf5, 0xa3, 0xef, 0xdb, 0xe3, 0xc1, 0xda, 0xf7, 0xf8, 0x48, 0x7f, 0xd6, 0xb8, + 0x0e, 0x6c, 0x5b, 0xbe, 0xf1, 0xa2, 0xb6, 0x12, 0x8f, 0x12, 0x78, 0x03, 0x13, 0x91, 0xc2, 0x20, 0xd5, 0x02, 0xc7, + 0xa0, 0xbc, 0xb1, 0x10, 0x4b, 0xd5, 0xd5, 0x0d, 0xb6, 0x20, 0x32, 0x04, 0x0f, 0xb7, 0x7f, 0xad, 0x54, 0xe0, 0xa8, + 0x7e, 0x9f, 0x4b, 0xdf, 0xed, 0xc9, 0xd8, 0x91, 0xe3, 0xd4, 0x4f, 0x85, 0x83, 0xff, 0x26, 0x75, 0x6d, 0x2c, 0x57, + 0x52, 0x66, 0x59, 0x16, 0x36, 0x0b, 0xb5, 0xdc, 0xa3, 0xf2, 0x20, 0xf9, 0x42, 0x0e, 0x91, 0x2c, 0x30, 0x0a, 0x05, + 0x19, 0x4e, 0xa8, 0x18, 0x6d, 0x44, 0xb9, 0xca, 0x2e, 0xaa, 0x70, 0xab, 0x14, 0xca, 0x9c, 0xa2, 0x6f, 0x37, 0x38, + 0x90, 0x90, 0x28, 0x2b, 0xdf, 0xc4, 0x6f, 0x42, 0x04, 0xab, 0xe3, 0xda, 0x16, 0x8a, 0x7b, 0xfb, 0x93, 0xa7, 0x5d, + 0xfc, 0x91, 0x71, 0x01, 0x75, 0xb1, 0x98, 0x86, 0x13, 0x1b, 0xfb, 0xc6, 0x7d, 0x61, 0x35, 0x3d, 0x00, 0xf5, 0x5d, + 0x2a, 0x31, 0x82, 0xfa, 0xca, 0xd8, 0xc7, 0xf6, 0x18, 0x93, 0x73, 0x88, 0x35, 0xac, 0x72, 0x66, 0xaa, 0x6f, 0x84, + 0xcd, 0x00, 0xb8, 0x11, 0x5a, 0xa3, 0x20, 0xf0, 0x78, 0x15, 0xe2, 0x79, 0xa9, 0xc2, 0xb7, 0x66, 0x84, 0x8e, 0xc1, + 0x9b, 0xca, 0x36, 0x32, 0x93, 0xbe, 0x60, 0xd0, 0x1c, 0xdb, 0x3a, 0x0a, 0xab, 0xad, 0x2c, 0x9b, 0x01, 0xdc, 0x40, + 0x76, 0x6c, 0x2e, 0x9e, 0xf3, 0x6a, 0x91, 0x2d, 0x23, 0x13, 0x14, 0x70, 0x25, 0x2c, 0x83, 0xf6, 0xd7, 0x3d, 0xb2, + 0x1d, 0x87, 0xd0, 0x0d, 0xf7, 0x11, 0x8c, 0xa7, 0xdd, 0x14, 0xac, 0x20, 0x1a, 0x21, 0x1e, 0x66, 0xcc, 0xe2, 0x7b, + 0xa5, 0x29, 0x4f, 0x55, 0x4b, 0x20, 0x70, 0x14, 0x42, 0x5d, 0xec, 0x1b, 0x25, 0xb8, 0x4c, 0x8d, 0x60, 0x06, 0x7b, + 0x76, 0xa4, 0xb6, 0x4b, 0xce, 0xe9, 0x50, 0x4d, 0x69, 0xa9, 0xa7, 0x54, 0xfb, 0x1a, 0x8a, 0x45, 0x89, 0x1e, 0x7a, + 0xe0, 0x7a, 0xa0, 0x1d, 0xf2, 0x4a, 0x3a, 0x31, 0x11, 0x74, 0x5a, 0x6d, 0xc2, 0xce, 0x8d, 0x74, 0xcb, 0x6a, 0xe4, + 0x1d, 0x43, 0xb3, 0x23, 0x5e, 0xf8, 0x81, 0xba, 0x00, 0x22, 0xe4, 0xde, 0x16, 0x99, 0x23, 0x9a, 0x65, 0xe5, 0x4b, + 0x28, 0x8b, 0x23, 0xb6, 0xae, 0x80, 0x6b, 0x29, 0x98, 0x5c, 0xf2, 0x88, 0xa7, 0x88, 0x08, 0x78, 0xa2, 0xb4, 0xeb, + 0x7b, 0x2d, 0x21, 0x34, 0x4b, 0x81, 0xb8, 0xb9, 0x28, 0xce, 0xb5, 0x0d, 0x64, 0x01, 0xf4, 0xed, 0xa7, 0xec, 0xca, + 0x0b, 0x07, 0xbb, 0xbd, 0xca, 0xc4, 0x73, 0x7e, 0x91, 0x09, 0x9e, 0x22, 0xd8, 0xd5, 0xad, 0x79, 0xe0, 0x8e, 0x6d, + 0x03, 0xcb, 0xb7, 0xef, 0x60, 0xc1, 0x94, 0xa1, 0x56, 0x4a, 0x64, 0x22, 0x12, 0x90, 0xd9, 0x67, 0xee, 0x5e, 0x67, + 0xe2, 0x75, 0x7c, 0x0b, 0xde, 0x14, 0x0d, 0x7e, 0x7a, 0x74, 0x8e, 0x5f, 0x22, 0x92, 0x28, 0xc4, 0xb0, 0xc5, 0x88, + 0x58, 0x88, 0x1c, 0x3b, 0x26, 0x94, 0x2b, 0x41, 0x6b, 0x6b, 0x08, 0xbc, 0xf8, 0xd3, 0xaa, 0x7b, 0x57, 0x99, 0x30, + 0xf6, 0x19, 0x57, 0xf1, 0x2d, 0x2b, 0x15, 0x98, 0x05, 0xc6, 0xb9, 0x6f, 0x4b, 0x49, 0xae, 0x32, 0x61, 0x04, 0x24, + 0x57, 0xf1, 0x2d, 0x6d, 0xca, 0x38, 0xb4, 0x15, 0x9d, 0x17, 0xe7, 0x77, 0x7f, 0xf8, 0x25, 0x86, 0x5a, 0x19, 0xf7, + 0xfb, 0x20, 0x31, 0x93, 0xb6, 0x29, 0x73, 0x19, 0x49, 0x8d, 0x16, 0x52, 0x51, 0x3e, 0x98, 0x90, 0xfd, 0x95, 0x6a, + 0x19, 0x51, 0xfb, 0x55, 0x28, 0xe6, 0xe3, 0x68, 0x42, 0xe8, 0xa4, 0x63, 0xbd, 0x9b, 0xd6, 0x42, 0xa6, 0xd1, 0xd3, + 0xc8, 0xf3, 0xe9, 0x2c, 0x58, 0x35, 0x2d, 0x8e, 0x19, 0x9f, 0x16, 0x83, 0x01, 0xd1, 0x2e, 0x85, 0x5b, 0xac, 0x07, + 0x4c, 0x69, 0x5c, 0xbc, 0x35, 0xd3, 0xea, 0x97, 0x52, 0x85, 0xa4, 0xf7, 0x0c, 0x48, 0x32, 0xe9, 0x82, 0xdd, 0x82, + 0x44, 0xd1, 0xf3, 0xbf, 0x53, 0x5b, 0x70, 0xdf, 0x83, 0xb1, 0x19, 0xdd, 0xd7, 0x33, 0xfe, 0x43, 0x6d, 0x0b, 0xa2, + 0x3e, 0x95, 0xac, 0xd7, 0x91, 0xa8, 0x42, 0x2e, 0xc2, 0xcf, 0x8e, 0x86, 0x18, 0xa2, 0xda, 0x63, 0x81, 0xd8, 0x5c, + 0x9d, 0xf3, 0x02, 0xa7, 0x9f, 0xb9, 0xcb, 0x15, 0x6c, 0x0b, 0x5a, 0x19, 0x1a, 0xf5, 0x26, 0x7e, 0x13, 0xd9, 0xcb, + 0x82, 0x2e, 0xf2, 0x39, 0x0a, 0x59, 0xf3, 0x30, 0xac, 0x86, 0xed, 0x41, 0x24, 0x87, 0xed, 0x49, 0x68, 0x34, 0x06, + 0x16, 0xc8, 0x1e, 0x8d, 0xc0, 0x45, 0x68, 0xe5, 0x6f, 0xc7, 0xe0, 0xc2, 0x65, 0x11, 0x59, 0x86, 0x3a, 0x7e, 0x53, + 0xbb, 0x09, 0xaa, 0x57, 0xe8, 0x34, 0x85, 0x55, 0x29, 0x93, 0x7c, 0xf8, 0xf5, 0x52, 0x16, 0x98, 0xc9, 0xeb, 0xb2, + 0x47, 0x5f, 0xdb, 0xed, 0x1d, 0x98, 0x82, 0x75, 0x9f, 0xbc, 0xaf, 0x1f, 0x77, 0xf6, 0x04, 0x8c, 0x62, 0x55, 0x8e, + 0xa6, 0x90, 0x52, 0xfb, 0xa0, 0xd4, 0x1f, 0xc3, 0x95, 0xd0, 0x1c, 0xbb, 0x05, 0x4c, 0x02, 0xf6, 0x19, 0x52, 0x3d, + 0xa6, 0x1d, 0xfb, 0x1c, 0x6d, 0x61, 0x49, 0xc0, 0xe1, 0x1f, 0x65, 0xb2, 0xf6, 0xaf, 0xee, 0x22, 0x6d, 0x86, 0x6c, + 0x59, 0x2c, 0x81, 0xcf, 0x87, 0x5d, 0x1b, 0x95, 0x28, 0x9b, 0x88, 0x24, 0x85, 0x2d, 0x8f, 0x41, 0xda, 0xa3, 0x98, + 0xae, 0x0b, 0x9e, 0x64, 0x28, 0xa5, 0x48, 0xb4, 0x4f, 0x70, 0x0e, 0x6f, 0x70, 0x3f, 0xaa, 0x80, 0xf0, 0x2a, 0xe4, + 0x74, 0x94, 0x52, 0x6d, 0x01, 0xa3, 0xa8, 0x07, 0x88, 0xf2, 0x32, 0x90, 0xe3, 0xed, 0x76, 0x13, 0xba, 0x66, 0xab, + 0xe1, 0x84, 0x22, 0x29, 0xb9, 0xc4, 0x72, 0xaf, 0x40, 0xe7, 0x71, 0xce, 0x7a, 0x2f, 0x00, 0x8b, 0xe0, 0x0c, 0xfe, + 0xc6, 0x84, 0x5e, 0xc3, 0xdf, 0x9c, 0xd0, 0xd7, 0x2c, 0xbc, 0x1a, 0x5e, 0x92, 0xc3, 0x30, 0x1d, 0x4c, 0x94, 0x60, + 0xec, 0x8e, 0xad, 0xca, 0x50, 0x25, 0xae, 0x0f, 0x2f, 0xc8, 0xe3, 0x0b, 0x7a, 0x4b, 0x6f, 0xe8, 0x29, 0x7d, 0x0b, + 0x84, 0xff, 0xee, 0x78, 0xc2, 0x87, 0x93, 0x27, 0xfd, 0x7e, 0xef, 0xbc, 0xdf, 0xef, 0x9d, 0x19, 0x03, 0x0a, 0xbd, + 0x8b, 0x2e, 0x6b, 0xaa, 0x7f, 0x5d, 0xd5, 0xcb, 0xe9, 0x5b, 0xb5, 0x71, 0x13, 0x9e, 0xe5, 0xe1, 0xd5, 0xe1, 0x1d, + 0x19, 0xe2, 0xe3, 0x45, 0x2e, 0x65, 0x11, 0x5e, 0x1e, 0xde, 0x11, 0xfa, 0x76, 0x06, 0x7a, 0x53, 0xac, 0xef, 0xed, + 0xe3, 0x3b, 0x5d, 0x1b, 0xa1, 0x2f, 0xc2, 0x04, 0xb6, 0xc9, 0x2d, 0xb3, 0x77, 0xed, 0xc9, 0x18, 0x62, 0x99, 0xdc, + 0x79, 0xe5, 0xdd, 0x3d, 0xbe, 0x25, 0x87, 0xb7, 0xe0, 0x29, 0x6a, 0xc9, 0xdf, 0x3c, 0xbc, 0x61, 0xad, 0x1a, 0x1e, + 0xdf, 0xd1, 0xd3, 0x56, 0x23, 0x1e, 0xdf, 0x91, 0x28, 0xbc, 0x61, 0x97, 0xf4, 0x94, 0x5d, 0x11, 0x7a, 0xde, 0xef, + 0x9f, 0xf5, 0xfb, 0xb2, 0xdf, 0xff, 0x7b, 0x1c, 0x86, 0xf1, 0xb0, 0x20, 0x87, 0x92, 0xde, 0x1d, 0x4e, 0xf8, 0x57, + 0x64, 0x1e, 0xea, 0xe6, 0xab, 0x05, 0x67, 0x55, 0xde, 0x2a, 0xd7, 0x1d, 0x05, 0x6b, 0x85, 0x3b, 0xa6, 0x9e, 0xde, + 0xd2, 0x1b, 0x56, 0xd0, 0x53, 0x16, 0x93, 0xe8, 0x1a, 0x5a, 0x71, 0x3e, 0x2f, 0xa2, 0x1b, 0x7a, 0xca, 0xce, 0xe6, + 0x71, 0x74, 0x4a, 0xdf, 0xb2, 0x7c, 0x38, 0x81, 0xbc, 0xa7, 0xc3, 0x1b, 0x72, 0xf8, 0x96, 0x44, 0xe1, 0x5b, 0xfd, + 0xfb, 0x8e, 0x5e, 0xf2, 0xf0, 0x2d, 0xf5, 0xaa, 0x79, 0x4b, 0x4c, 0xf5, 0x8d, 0xda, 0xdf, 0x92, 0xc8, 0x1f, 0xcc, + 0xb7, 0xd6, 0x9e, 0xe6, 0x91, 0xa3, 0x8d, 0x69, 0x19, 0x82, 0xbe, 0xb9, 0x0c, 0x6f, 0x08, 0x99, 0x36, 0xc7, 0x0e, + 0x06, 0x74, 0xfe, 0x28, 0x4a, 0x08, 0xbd, 0xf1, 0x4b, 0xbd, 0xc1, 0x31, 0x34, 0x23, 0xa4, 0xd2, 0x4e, 0x31, 0x0d, + 0xd7, 0xc1, 0x2b, 0x0d, 0xd6, 0x71, 0xde, 0xef, 0x87, 0x9b, 0x7e, 0x1f, 0x22, 0xdd, 0x17, 0x73, 0x13, 0xdb, 0xcd, + 0x91, 0x4d, 0x7a, 0x03, 0xda, 0xff, 0x57, 0x83, 0x01, 0x74, 0xc6, 0x2b, 0x29, 0xbc, 0x19, 0xbc, 0x7a, 0x7c, 0x47, + 0x54, 0x1d, 0x05, 0x15, 0x32, 0x2c, 0xe8, 0x6b, 0x9a, 0x01, 0xe0, 0xd7, 0xab, 0xc1, 0x80, 0x44, 0xe6, 0x33, 0x32, + 0x7d, 0x75, 0xfc, 0x76, 0x3a, 0x18, 0xbc, 0x32, 0xdb, 0xe4, 0x0d, 0xbb, 0xa7, 0x14, 0x58, 0x7f, 0x67, 0xfd, 0xfe, + 0x9b, 0x59, 0x4c, 0xce, 0x0b, 0x1e, 0x7f, 0x9c, 0x36, 0xdb, 0xf2, 0xc6, 0x45, 0x55, 0x3b, 0xeb, 0xf7, 0x37, 0xfd, + 0xfe, 0x29, 0x60, 0x17, 0xcd, 0x9d, 0xaf, 0x27, 0x48, 0x5b, 0x16, 0x8e, 0x22, 0x69, 0x92, 0x43, 0x63, 0x68, 0x5b, + 0xac, 0xda, 0x36, 0xef, 0xc8, 0xc0, 0xe2, 0xa8, 0x59, 0x51, 0x5c, 0x93, 0x28, 0xec, 0x9d, 0xed, 0x76, 0xa7, 0x8c, + 0xb1, 0x98, 0x80, 0xf4, 0xc3, 0x7f, 0x7d, 0x5a, 0x37, 0x62, 0x88, 0x09, 0x89, 0xcc, 0xe6, 0x76, 0x65, 0x0f, 0x81, + 0x88, 0xc3, 0xa6, 0x7f, 0x6f, 0xee, 0xe5, 0xa2, 0x76, 0x7c, 0xeb, 0xcf, 0x00, 0x42, 0x24, 0x59, 0xc8, 0xe7, 0x38, + 0x06, 0x65, 0x06, 0x40, 0xe6, 0x91, 0x9a, 0x79, 0x09, 0x20, 0xc0, 0x64, 0xb7, 0x1b, 0x8d, 0xc7, 0x13, 0x5a, 0xb0, + 0xd1, 0xdf, 0x9e, 0x3e, 0xae, 0x1e, 0x87, 0x41, 0x30, 0xc8, 0x48, 0x4b, 0x4f, 0x61, 0x17, 0x6b, 0x75, 0x08, 0x46, + 0xf0, 0x9a, 0x7d, 0xbc, 0xce, 0x3e, 0x9b, 0x7d, 0x44, 0xc2, 0xda, 0x60, 0x1c, 0xb9, 0x48, 0x5b, 0x7a, 0xbb, 0x7b, + 0x18, 0x4c, 0x2e, 0xd2, 0x4f, 0xb0, 0x9d, 0x3e, 0xff, 0xe6, 0xc1, 0x78, 0xc2, 0xc1, 0xe8, 0x2e, 0x0a, 0xfa, 0x4c, + 0xdb, 0xed, 0x2a, 0xff, 0x12, 0xf8, 0x06, 0x53, 0x41, 0xc7, 0x66, 0x59, 0xb8, 0x41, 0x45, 0xd4, 0xd1, 0x32, 0xa8, + 0x6a, 0x65, 0x3b, 0x07, 0xd4, 0x12, 0xab, 0x32, 0x71, 0x0b, 0x0c, 0x43, 0x86, 0xba, 0xdc, 0x93, 0xea, 0x5f, 0xbc, + 0x90, 0x06, 0x3e, 0xc3, 0x89, 0x08, 0x3d, 0x6e, 0x8d, 0xfb, 0xdc, 0x9a, 0xf8, 0x04, 0xb7, 0x56, 0x22, 0x89, 0x35, + 0xb0, 0xa4, 0xe6, 0x72, 0x94, 0xb0, 0x59, 0xc9, 0xf8, 0xbc, 0x8c, 0x12, 0x1a, 0xc3, 0x83, 0x64, 0x62, 0x2e, 0xa3, + 0x04, 0xed, 0x13, 0x5d, 0x84, 0xc1, 0x3f, 0x01, 0xb3, 0x9f, 0xe6, 0xf0, 0x57, 0x92, 0x69, 0x72, 0x0c, 0x01, 0x21, + 0x8e, 0xc7, 0xf3, 0x38, 0x1c, 0x93, 0x28, 0x99, 0xc1, 0x13, 0xfc, 0x57, 0x84, 0x63, 0x52, 0xeb, 0x3b, 0x8c, 0x54, + 0x97, 0xdb, 0x84, 0x01, 0x5c, 0xd9, 0x78, 0x3e, 0x89, 0xac, 0x74, 0x57, 0x3e, 0x1e, 0x8d, 0x9f, 0x92, 0x69, 0x1c, + 0xca, 0x41, 0x42, 0x28, 0x78, 0xf7, 0x86, 0xe5, 0x30, 0xd1, 0xf0, 0x6c, 0xc0, 0xe6, 0x95, 0x8e, 0xcd, 0x93, 0x70, + 0x02, 0xc2, 0x30, 0x21, 0xc7, 0x7a, 0x0f, 0x52, 0x8a, 0x3e, 0xcf, 0xb1, 0x9f, 0xfa, 0x08, 0xc2, 0xec, 0xa8, 0xa5, + 0xe2, 0x6b, 0x00, 0xba, 0xc4, 0xc1, 0xa1, 0xf6, 0xcc, 0x17, 0xf3, 0xb0, 0xf4, 0xa8, 0x94, 0xa9, 0xee, 0x50, 0x34, + 0x28, 0xbf, 0x69, 0xd0, 0xa1, 0x20, 0x83, 0x09, 0x2d, 0x67, 0x13, 0xfe, 0x15, 0x04, 0xf0, 0x68, 0x44, 0xfc, 0x52, + 0x38, 0x31, 0x10, 0x5e, 0x05, 0x19, 0xa8, 0xb4, 0x56, 0x8d, 0x19, 0xd9, 0x8a, 0x0f, 0x20, 0x4c, 0xca, 0xc1, 0x8d, + 0xdc, 0xe4, 0x29, 0x44, 0x05, 0xdb, 0xe4, 0xd5, 0xc1, 0x25, 0x58, 0xb2, 0xc7, 0x15, 0xc4, 0x09, 0xdb, 0xac, 0x01, + 0x3b, 0xf7, 0xd1, 0xb6, 0xac, 0x0f, 0xd4, 0x77, 0x07, 0xd8, 0x72, 0x78, 0x55, 0xc9, 0x83, 0xc9, 0x78, 0x3c, 0x1e, + 0xfd, 0x0e, 0x47, 0x07, 0x10, 0x5a, 0x12, 0x19, 0x3e, 0x19, 0xa0, 0x71, 0x37, 0x15, 0xf7, 0xc6, 0x85, 0xa2, 0xac, + 0x74, 0x32, 0x21, 0x20, 0x7e, 0x36, 0x7d, 0x83, 0x7d, 0xc5, 0x75, 0xfc, 0x93, 0xfd, 0x4f, 0xcc, 0x8a, 0x56, 0x2b, + 0x75, 0xf4, 0xee, 0xed, 0xe9, 0xab, 0x0f, 0xaf, 0x7e, 0x7d, 0x71, 0xf6, 0xea, 0xcd, 0xcb, 0x57, 0x6f, 0x5e, 0x7d, + 0xf8, 0xe7, 0x03, 0x0c, 0xb6, 0x6f, 0x2b, 0x62, 0xc7, 0xde, 0xbb, 0xc7, 0x78, 0xb5, 0xf8, 0xc2, 0xd9, 0x23, 0x77, + 0x8b, 0x05, 0xd8, 0x04, 0xc3, 0x2d, 0x08, 0xaa, 0x19, 0x8d, 0x4a, 0xdf, 0x13, 0x90, 0xd1, 0xa8, 0x90, 0x8d, 0x87, + 0x15, 0x5b, 0x21, 0x17, 0xef, 0x18, 0x0e, 0x3e, 0xb2, 0xbf, 0x15, 0x67, 0xc2, 0xed, 0x68, 0x6b, 0x56, 0x04, 0x7c, + 0xbe, 0x36, 0xa2, 0xf2, 0xb8, 0x10, 0xb5, 0xb7, 0xed, 0x73, 0x48, 0xa8, 0x47, 0xe4, 0x3a, 0x78, 0xdf, 0x06, 0xd9, + 0xe3, 0x23, 0xef, 0x49, 0x79, 0x86, 0xfa, 0x1c, 0x0d, 0x1f, 0x35, 0x9e, 0xd1, 0x89, 0xb9, 0x36, 0x3a, 0xd4, 0xb3, + 0x02, 0xf6, 0xb7, 0x12, 0x63, 0x43, 0xb4, 0x87, 0x14, 0xb1, 0x3e, 0x9c, 0xee, 0x77, 0xff, 0x66, 0xf4, 0x3d, 0x1c, + 0x3f, 0x4a, 0x35, 0x81, 0xb4, 0x28, 0x50, 0xba, 0x32, 0xe4, 0xb6, 0xe7, 0x61, 0x61, 0x7e, 0x86, 0x0d, 0x02, 0x68, + 0x2f, 0x3b, 0x96, 0x04, 0x9a, 0xc5, 0x6b, 0x5d, 0xff, 0xbc, 0x7c, 0x99, 0x68, 0xe7, 0x8b, 0x6f, 0x21, 0xc4, 0xb0, + 0x7f, 0x45, 0x68, 0x4c, 0xb8, 0x9b, 0x64, 0x77, 0x69, 0x31, 0xf7, 0xaa, 0xab, 0x18, 0x8f, 0xbb, 0x7b, 0xae, 0x14, + 0xcd, 0x5b, 0x17, 0xd8, 0x03, 0x35, 0xaf, 0xe3, 0x25, 0x0b, 0x01, 0x9b, 0xf1, 0xd0, 0x2e, 0x12, 0xe7, 0xf7, 0x4e, + 0x27, 0xe4, 0xf0, 0x68, 0xca, 0x87, 0xac, 0xa4, 0x62, 0xc0, 0xca, 0x7a, 0x8f, 0x9a, 0xf3, 0x36, 0x21, 0x17, 0xfb, + 0x34, 0x5c, 0x0c, 0xf9, 0x43, 0x97, 0xa4, 0x0f, 0xbc, 0xe1, 0x50, 0x6d, 0x9b, 0x8b, 0x21, 0x4d, 0x39, 0xdd, 0xa7, + 0x32, 0x20, 0x44, 0xba, 0x8a, 0x2b, 0x52, 0xeb, 0xa3, 0x2a, 0x75, 0x92, 0x8e, 0xeb, 0x6c, 0xfb, 0x89, 0x4b, 0xb6, + 0xba, 0x5d, 0xfb, 0xd7, 0xea, 0xf6, 0x85, 0x19, 0xc8, 0xdf, 0x1f, 0x88, 0x6a, 0x62, 0x20, 0xba, 0x80, 0x0a, 0xfe, + 0x01, 0x5e, 0x9e, 0x3c, 0xd2, 0x0a, 0xd0, 0xfb, 0xce, 0x8e, 0xae, 0x3d, 0xde, 0x98, 0xc5, 0xd6, 0x12, 0xe7, 0xac, + 0xf2, 0x9d, 0xe5, 0x55, 0xd9, 0x0a, 0x5d, 0x47, 0xb0, 0x9f, 0xc3, 0x8e, 0xbe, 0x7b, 0xdb, 0x00, 0x88, 0x52, 0x58, + 0xb9, 0xb3, 0x5f, 0x78, 0x67, 0xbf, 0xb0, 0x67, 0xbf, 0xdd, 0x04, 0xca, 0x87, 0x15, 0x5a, 0xf6, 0x52, 0x8a, 0xca, + 0x34, 0x79, 0xdc, 0xd4, 0x65, 0x21, 0x2d, 0xe6, 0x87, 0x96, 0x76, 0x3d, 0x19, 0x53, 0x89, 0xea, 0x91, 0x1f, 0xb0, + 0x55, 0x87, 0x25, 0x79, 0xf8, 0x9e, 0xf9, 0x3f, 0x7b, 0x83, 0xbc, 0xef, 0x6e, 0xf7, 0x7f, 0x73, 0xa1, 0x83, 0xdb, + 0x5a, 0x2a, 0x3c, 0x75, 0x75, 0x5c, 0xe0, 0x5d, 0x2d, 0x7d, 0xf8, 0xae, 0xf6, 0x2e, 0xd3, 0xcb, 0xae, 0x02, 0xd4, + 0x20, 0xb1, 0xb9, 0xe2, 0x45, 0x96, 0xd4, 0x56, 0xa1, 0xf1, 0x96, 0x43, 0x68, 0x0f, 0xef, 0xe0, 0x02, 0x39, 0x2c, + 0x21, 0xf4, 0x63, 0x65, 0x04, 0x80, 0x3e, 0x8b, 0xfd, 0x96, 0x87, 0x19, 0x19, 0xf8, 0x12, 0xbf, 0x52, 0xfa, 0xe2, + 0xe2, 0xc3, 0xbd, 0xcc, 0x04, 0xbd, 0x4a, 0x6c, 0x76, 0x29, 0xdb, 0x31, 0x3f, 0xfc, 0x2f, 0x30, 0x1a, 0x84, 0xd7, + 0x96, 0xec, 0x50, 0x74, 0xcc, 0x72, 0x05, 0x47, 0x6d, 0xe9, 0xca, 0x2c, 0x5b, 0xd7, 0xcf, 0x6a, 0x98, 0xe9, 0x33, + 0xe5, 0x2d, 0xc8, 0xbe, 0x90, 0xbb, 0x9f, 0xea, 0x8a, 0x05, 0x99, 0x4d, 0xc6, 0x53, 0x22, 0x06, 0x83, 0x56, 0xf2, + 0x31, 0x26, 0x0f, 0x87, 0x7b, 0xcc, 0xa5, 0xd0, 0xfd, 0xf0, 0xfa, 0x00, 0xf5, 0x35, 0xb6, 0x24, 0xd9, 0x56, 0xec, + 0x4f, 0x30, 0x8b, 0x05, 0xe2, 0xe8, 0xe0, 0x17, 0x17, 0x4b, 0x00, 0x59, 0x86, 0x65, 0xa6, 0x85, 0x45, 0x65, 0xaa, + 0x7c, 0x64, 0x0b, 0x26, 0x8f, 0xc7, 0x73, 0xbf, 0xe7, 0x8e, 0xc1, 0x21, 0x24, 0x9a, 0x58, 0xe3, 0x17, 0x3f, 0x0b, + 0xc6, 0x71, 0x28, 0x67, 0xb2, 0xf1, 0x5d, 0x49, 0xa2, 0xb1, 0x31, 0x55, 0xd6, 0x57, 0x89, 0x6a, 0x98, 0x90, 0xc7, + 0x05, 0x39, 0x2c, 0xe8, 0xca, 0x1f, 0x4b, 0x4c, 0x3f, 0x8c, 0x0f, 0x27, 0x63, 0xf2, 0x38, 0x7e, 0x3c, 0x31, 0x70, + 0xc3, 0x7e, 0x8e, 0x7c, 0xb8, 0x22, 0x87, 0xcd, 0x2a, 0xc1, 0x14, 0xd5, 0xf4, 0xcc, 0xaf, 0x24, 0x19, 0xac, 0x06, + 0xe9, 0xe3, 0x56, 0x5e, 0xac, 0x55, 0x8f, 0xf7, 0xe6, 0x98, 0x4f, 0x89, 0x68, 0xdc, 0x18, 0x36, 0xf4, 0x2a, 0xfe, + 0x43, 0x16, 0x51, 0x29, 0x01, 0x91, 0x10, 0xd4, 0xdb, 0xd9, 0x45, 0x96, 0xc4, 0x22, 0x8d, 0xd2, 0x9a, 0xd0, 0x74, + 0xc6, 0x26, 0xe3, 0x79, 0xca, 0xd2, 0xe3, 0xc9, 0xd3, 0xf9, 0xe4, 0x69, 0x74, 0x34, 0x8e, 0xd2, 0xc1, 0x00, 0x92, + 0x8f, 0xc6, 0xe0, 0x62, 0x07, 0xbf, 0xd9, 0x11, 0x0c, 0xdd, 0x0c, 0x59, 0xc2, 0x02, 0x9a, 0xf6, 0x79, 0x4d, 0xd2, + 0xc3, 0x79, 0xa1, 0x7a, 0x12, 0xdf, 0xd2, 0x8d, 0xe7, 0xe0, 0xe2, 0xb7, 0xf0, 0xc2, 0xb5, 0xf0, 0x62, 0xbf, 0x85, + 0x42, 0x93, 0xed, 0x42, 0xfe, 0xff, 0xb8, 0x61, 0xdc, 0x77, 0x97, 0x30, 0x8b, 0xeb, 0x3a, 0x1b, 0xad, 0x0b, 0x59, + 0x49, 0xb8, 0x4d, 0x28, 0x51, 0xd8, 0x28, 0x5e, 0xaf, 0x73, 0xed, 0x22, 0xb6, 0xa8, 0x28, 0x80, 0xbb, 0x40, 0x9c, + 0x62, 0x60, 0xa1, 0x8d, 0x81, 0xdc, 0x5f, 0xbc, 0x90, 0xcc, 0xaa, 0x7d, 0xcc, 0x3d, 0xf2, 0x8f, 0x10, 0x8c, 0x51, + 0xc5, 0x6c, 0x3c, 0x57, 0x58, 0x17, 0x9f, 0x92, 0xf7, 0xfe, 0x1b, 0x47, 0x91, 0x3d, 0x9a, 0x41, 0x4f, 0x10, 0x39, + 0x8f, 0x38, 0x7b, 0x32, 0x79, 0x19, 0xb8, 0x9f, 0xc1, 0x4a, 0x7f, 0xdd, 0x6d, 0xc6, 0xda, 0xf6, 0xe8, 0x5e, 0x18, + 0xa1, 0xe8, 0x5f, 0xf8, 0xce, 0xd4, 0x0b, 0xb8, 0x84, 0x6a, 0x60, 0x37, 0x97, 0x97, 0xbc, 0x04, 0x10, 0xa1, 0x4c, + 0xf4, 0xfb, 0xbd, 0x3f, 0x0c, 0x34, 0x69, 0xc9, 0x8b, 0xd7, 0x99, 0xb0, 0xce, 0x38, 0xd0, 0x54, 0xa0, 0xfe, 0x1f, + 0x2b, 0xfb, 0x4c, 0xc7, 0x64, 0xee, 0x3f, 0x0e, 0x27, 0x24, 0x6a, 0xbe, 0x26, 0x9f, 0x38, 0x4d, 0x3f, 0x71, 0x45, + 0xfb, 0x0f, 0x64, 0xe6, 0x86, 0x43, 0x86, 0xfa, 0x4b, 0xc7, 0x3c, 0x19, 0xbd, 0x4e, 0xcc, 0x66, 0x82, 0x55, 0x73, + 0x88, 0xc2, 0x5e, 0xc0, 0x83, 0xba, 0x96, 0xc5, 0x53, 0x98, 0x7d, 0x50, 0x23, 0x8a, 0x63, 0x36, 0x9e, 0x87, 0x32, + 0x9c, 0x80, 0x7d, 0xef, 0x64, 0x0c, 0xf7, 0x01, 0x19, 0x7e, 0xac, 0x42, 0xec, 0x1c, 0xa4, 0x7d, 0xac, 0x50, 0x31, + 0x01, 0x10, 0x81, 0x90, 0xb7, 0xdf, 0x97, 0x2a, 0x09, 0x5f, 0x97, 0x98, 0x52, 0xa8, 0x0f, 0xfe, 0x13, 0xa9, 0xba, + 0x63, 0xfa, 0xd5, 0xfa, 0xf1, 0x67, 0x42, 0xf1, 0xe9, 0x2e, 0x25, 0xbe, 0x85, 0xe0, 0xce, 0x12, 0x74, 0x10, 0x15, + 0x9a, 0xb1, 0x3d, 0xcc, 0xef, 0x8a, 0xfb, 0xf9, 0x5d, 0xf1, 0xff, 0x8e, 0xdf, 0x15, 0x0f, 0x31, 0x86, 0x95, 0x85, + 0x86, 0x9f, 0x07, 0xe3, 0x20, 0xfa, 0xcf, 0xf9, 0xc4, 0x7b, 0x79, 0xea, 0xab, 0x4c, 0x4c, 0xef, 0x61, 0x9a, 0x7d, + 0x82, 0x82, 0xb0, 0x8a, 0xfb, 0xf4, 0x64, 0x53, 0xd9, 0x5b, 0x2b, 0x19, 0x62, 0x9e, 0x07, 0x58, 0xa3, 0xb0, 0xf2, + 0x80, 0xee, 0x51, 0xb5, 0x41, 0x9c, 0x08, 0x1e, 0xc6, 0xcc, 0x4a, 0xdf, 0x77, 0x3b, 0xa3, 0xc2, 0x7c, 0x90, 0x8b, + 0x82, 0xec, 0xe6, 0xe3, 0xf9, 0x38, 0x0a, 0xb1, 0x01, 0xff, 0x31, 0x63, 0xd5, 0x90, 0xcd, 0x77, 0x32, 0x52, 0x7b, + 0x26, 0x4f, 0x93, 0x7d, 0xd2, 0x3b, 0xe0, 0x1d, 0xf2, 0xf3, 0xfa, 0x63, 0x58, 0x48, 0xc3, 0x6f, 0xc9, 0xcb, 0xb8, + 0xc8, 0xaa, 0xd5, 0x55, 0x96, 0x20, 0xd3, 0x05, 0x2f, 0x3e, 0x9b, 0xe9, 0xf2, 0x3e, 0xd6, 0x07, 0x8c, 0xa7, 0x14, + 0xaf, 0x1b, 0xa2, 0xf4, 0x4d, 0xcb, 0xb3, 0x42, 0x5d, 0x9e, 0x54, 0xcc, 0xf6, 0xac, 0x04, 0xa7, 0x53, 0x30, 0xc1, + 0xd7, 0x3f, 0x5d, 0xef, 0x63, 0xc0, 0x05, 0x85, 0x9a, 0xd3, 0x42, 0xae, 0x0d, 0x96, 0x93, 0x85, 0xee, 0x04, 0xcc, + 0x50, 0x29, 0xf0, 0x02, 0x05, 0x7f, 0xd1, 0xc0, 0x88, 0xbe, 0x74, 0xbf, 0xc9, 0xc0, 0x20, 0x5d, 0x9a, 0x13, 0x61, + 0xec, 0xb8, 0x9d, 0x22, 0x6d, 0x45, 0x39, 0xe3, 0xec, 0xbd, 0xba, 0x52, 0x80, 0x01, 0xde, 0xf6, 0x26, 0x3a, 0x4f, + 0xd0, 0x6b, 0x41, 0xe9, 0xbc, 0x81, 0xbb, 0x59, 0x45, 0x46, 0xb8, 0xf8, 0xb8, 0xf2, 0x58, 0x70, 0xcf, 0x7e, 0x21, + 0x96, 0x46, 0x33, 0x0d, 0xc6, 0x6c, 0x5e, 0xb0, 0x40, 0xa1, 0x02, 0x05, 0x96, 0x73, 0x6d, 0x69, 0x5a, 0x0d, 0xf9, + 0xe1, 0x11, 0x5a, 0x9b, 0x56, 0x03, 0x7e, 0x78, 0x54, 0x47, 0xd9, 0x31, 0x64, 0x99, 0xf9, 0x19, 0xd4, 0xeb, 0x3a, + 0x32, 0x29, 0x26, 0xbb, 0x5f, 0x5f, 0xea, 0x8f, 0xea, 0x16, 0x5c, 0x3f, 0x00, 0x01, 0x6c, 0x00, 0x0e, 0x81, 0x6a, + 0xb0, 0x34, 0x22, 0x58, 0x94, 0x29, 0xb4, 0xaf, 0xa1, 0xf7, 0x46, 0xc3, 0x7f, 0x81, 0xbb, 0x88, 0x5c, 0xfb, 0x9f, + 0x20, 0xf0, 0x57, 0x94, 0x69, 0x65, 0x8a, 0xff, 0x89, 0x56, 0xaf, 0x50, 0xce, 0x9a, 0xd6, 0x7c, 0x10, 0xad, 0x89, + 0x50, 0xcd, 0x18, 0x82, 0x7f, 0x2b, 0xcb, 0xb4, 0xa5, 0xaa, 0x52, 0x1f, 0x1a, 0xaf, 0xb5, 0xc2, 0x59, 0x3e, 0x8e, + 0xbc, 0xd7, 0x18, 0x3a, 0x36, 0x71, 0x96, 0x72, 0x2a, 0x75, 0xfe, 0xd7, 0xa1, 0x8c, 0x1c, 0xe0, 0x74, 0xc2, 0xc6, + 0xd3, 0xe4, 0x58, 0x4e, 0x13, 0x07, 0x99, 0x9f, 0x33, 0x8c, 0xac, 0x6a, 0x40, 0x58, 0x94, 0x0d, 0xa5, 0x2d, 0xc0, + 0x24, 0x27, 0x84, 0x4c, 0x31, 0x14, 0x45, 0x3e, 0xd2, 0xfd, 0xb0, 0xde, 0xac, 0xee, 0x8b, 0x77, 0x1a, 0xe0, 0x34, + 0x4c, 0x20, 0x10, 0x78, 0x11, 0xdf, 0x64, 0xe2, 0x12, 0x3c, 0x86, 0x07, 0xf0, 0x25, 0xb8, 0xc9, 0xa5, 0xec, 0xb7, + 0x2a, 0xcc, 0x71, 0x6d, 0x01, 0x83, 0x06, 0xab, 0x07, 0xd1, 0xe1, 0x52, 0xda, 0xec, 0x2a, 0x40, 0x6c, 0x4c, 0x21, + 0x96, 0x05, 0xdb, 0x58, 0xf6, 0xec, 0x7b, 0xd5, 0x34, 0xb4, 0x4e, 0x38, 0x11, 0x97, 0x39, 0x44, 0x51, 0x19, 0xc4, + 0xe0, 0x8e, 0xe4, 0xf1, 0x79, 0x8f, 0x44, 0x78, 0x41, 0xc0, 0xad, 0x2c, 0x96, 0xe1, 0x9a, 0xae, 0x46, 0xb7, 0x74, + 0x33, 0xba, 0xa1, 0x63, 0x3a, 0xf9, 0x66, 0x0c, 0x16, 0xd9, 0x3a, 0xf5, 0x8e, 0x6e, 0x46, 0x2b, 0xfa, 0xed, 0x98, + 0x1e, 0xfd, 0x0d, 0x4c, 0xf8, 0xf0, 0x30, 0xa1, 0x17, 0xe0, 0xd8, 0x45, 0x6a, 0xf4, 0xd4, 0xf4, 0x0d, 0x0e, 0xab, + 0x51, 0x3e, 0xe4, 0xa3, 0x9c, 0xf2, 0x51, 0x31, 0xac, 0x46, 0xe0, 0xe9, 0x58, 0x0d, 0xf9, 0xa8, 0xa2, 0x7c, 0x74, + 0x3e, 0xac, 0x46, 0xe7, 0xa4, 0xd9, 0xf4, 0x57, 0x15, 0xbf, 0x2a, 0x59, 0x0a, 0xdb, 0x02, 0x96, 0xaf, 0xe7, 0x15, + 0x95, 0xfa, 0xab, 0xda, 0x9c, 0xcc, 0x96, 0xb3, 0xb7, 0xd7, 0x5d, 0x4e, 0x2c, 0x1e, 0xb7, 0x4d, 0x87, 0xab, 0x2f, + 0x27, 0xea, 0xa4, 0x57, 0xc8, 0x0f, 0xe3, 0xa9, 0x50, 0xe7, 0x10, 0x98, 0x49, 0xcc, 0xc3, 0x98, 0x61, 0x33, 0x75, + 0x1a, 0x28, 0x70, 0xb2, 0x91, 0xe7, 0xa2, 0x98, 0x8d, 0x72, 0x0a, 0xef, 0x63, 0x42, 0x22, 0x01, 0x67, 0xd5, 0xac, + 0x1a, 0x15, 0x10, 0x73, 0x84, 0x85, 0xf8, 0x08, 0xfd, 0x52, 0x1f, 0x79, 0x48, 0xe0, 0x19, 0xf6, 0xb5, 0x18, 0xc4, + 0x70, 0xc4, 0xdb, 0xca, 0xaa, 0x79, 0x98, 0x40, 0x65, 0xd5, 0xb0, 0x34, 0x95, 0x15, 0x34, 0x1b, 0x55, 0x7e, 0x65, + 0x15, 0x8e, 0x51, 0x42, 0x48, 0x54, 0xea, 0xca, 0x40, 0x7d, 0x92, 0xb0, 0xb0, 0xd4, 0x95, 0x9d, 0xab, 0x8f, 0xce, + 0xfd, 0xca, 0xce, 0xc1, 0x85, 0x74, 0x90, 0xf8, 0x57, 0xa9, 0x3c, 0x6d, 0x5f, 0x07, 0x1b, 0xab, 0x8a, 0x6e, 0xf9, + 0x6d, 0x55, 0xc4, 0x51, 0x49, 0x5d, 0x0c, 0x68, 0x5c, 0x18, 0x91, 0xa4, 0x7a, 0x8d, 0x82, 0x3f, 0x24, 0x88, 0x4a, + 0x63, 0xf0, 0xea, 0x4c, 0xba, 0x56, 0x6a, 0x45, 0xc5, 0xa0, 0x1c, 0x14, 0x70, 0x7f, 0xca, 0x5b, 0x0b, 0xe9, 0x7b, + 0x88, 0xa8, 0x0c, 0xe5, 0x0d, 0xfe, 0x81, 0xc1, 0x93, 0xd9, 0x3a, 0x0d, 0x93, 0xd1, 0x1d, 0x8d, 0x47, 0x2b, 0x84, + 0x83, 0x61, 0x9b, 0x54, 0xe1, 0xad, 0x5f, 0x40, 0xfa, 0x2d, 0x8d, 0x47, 0x37, 0x34, 0xb5, 0x36, 0xa7, 0x06, 0xea, + 0xaa, 0x37, 0xa6, 0xb7, 0x11, 0xbc, 0xbe, 0x8b, 0x56, 0x14, 0xb6, 0xd2, 0x49, 0x9e, 0x5d, 0x8a, 0x28, 0xa5, 0x88, + 0x40, 0xb8, 0x41, 0xe4, 0xc0, 0x95, 0x46, 0x1b, 0xdc, 0x0c, 0xa0, 0x0c, 0x0d, 0x17, 0xb8, 0x1a, 0xc4, 0xa3, 0x95, + 0x47, 0xa6, 0x56, 0xfa, 0x22, 0x8b, 0xf0, 0xd1, 0xce, 0x46, 0x4b, 0xf1, 0x8c, 0x58, 0x18, 0x57, 0x30, 0x84, 0xba, + 0xb0, 0xd2, 0x14, 0x24, 0x5d, 0xe0, 0xc8, 0x5e, 0x58, 0x54, 0xe1, 0x16, 0x4c, 0x8b, 0xee, 0xc0, 0x3c, 0x0a, 0x14, + 0x0e, 0x2e, 0x41, 0xfa, 0x09, 0x65, 0x3b, 0x47, 0x69, 0x72, 0x78, 0x13, 0x94, 0xee, 0x4d, 0x10, 0xd2, 0xae, 0x6e, + 0xb2, 0x25, 0x7d, 0x83, 0xed, 0x3d, 0x3a, 0x15, 0x15, 0x54, 0x9f, 0x5b, 0x30, 0x59, 0xb2, 0x41, 0xd8, 0x12, 0xa6, + 0x67, 0x7a, 0x03, 0xd8, 0xd3, 0x87, 0x47, 0x7b, 0xf3, 0x5d, 0xcc, 0xff, 0x3a, 0x2c, 0xa3, 0xb1, 0xb2, 0xe0, 0xcd, + 0x2d, 0xb1, 0x5b, 0xb1, 0xf1, 0x74, 0x75, 0x5c, 0x4e, 0x57, 0x48, 0xec, 0x0c, 0xdd, 0x62, 0x7c, 0xb1, 0x5a, 0xd2, + 0x04, 0xcf, 0x36, 0x56, 0x2d, 0x56, 0x06, 0x2d, 0x25, 0x65, 0xb8, 0xde, 0x56, 0xe8, 0xff, 0xaf, 0x2e, 0x7e, 0x29, + 0xc0, 0x4b, 0x30, 0x16, 0x00, 0xc2, 0x3d, 0x98, 0x16, 0xa4, 0x36, 0xca, 0xc6, 0x2a, 0x0d, 0x53, 0x5c, 0x04, 0x26, + 0xa5, 0xdf, 0x0f, 0x73, 0x96, 0x12, 0x0f, 0x3a, 0xd4, 0x9d, 0xda, 0xa9, 0x2f, 0x04, 0x01, 0x1e, 0x49, 0x9d, 0x63, + 0x93, 0x6f, 0xc6, 0xf3, 0x40, 0x0d, 0x44, 0x10, 0x65, 0xc7, 0xf8, 0x88, 0x81, 0x8b, 0x22, 0x1d, 0xb7, 0xd3, 0x15, + 0x71, 0xb1, 0x7f, 0xcc, 0x42, 0x9c, 0x24, 0xcc, 0x35, 0xcf, 0x86, 0xac, 0x8a, 0x30, 0x41, 0x17, 0x06, 0x66, 0x79, + 0x43, 0x56, 0x1d, 0x1e, 0x41, 0xa4, 0x56, 0x5b, 0xc6, 0xba, 0xab, 0x8c, 0x6f, 0x01, 0xc8, 0x9a, 0x31, 0x76, 0xf4, + 0xb7, 0xf1, 0x5c, 0x7d, 0x13, 0x85, 0x7c, 0x76, 0xf4, 0x37, 0x48, 0x3e, 0xfe, 0x16, 0x99, 0x39, 0x48, 0x6e, 0x14, + 0x74, 0xd9, 0x9c, 0x75, 0x0d, 0xa5, 0x89, 0x6b, 0xaf, 0xd4, 0x6b, 0x4f, 0x9a, 0xb5, 0x57, 0xa0, 0x3b, 0xb5, 0xe1, + 0x3d, 0x94, 0xed, 0x2c, 0x98, 0xa0, 0xa3, 0xd9, 0x1d, 0xe8, 0xe0, 0x9d, 0x22, 0xe8, 0x45, 0x12, 0x1a, 0x8f, 0x50, + 0x65, 0xd4, 0x0b, 0x3b, 0xb2, 0x9b, 0x75, 0xc9, 0x3c, 0x03, 0xe6, 0xd8, 0x9e, 0x43, 0x62, 0x98, 0xab, 0x83, 0x3a, + 0x65, 0xe5, 0x30, 0xc7, 0x03, 0x78, 0xc3, 0xe4, 0x50, 0x0c, 0x72, 0x8d, 0xf2, 0x7d, 0xc1, 0x8a, 0x61, 0x39, 0xc8, + 0x35, 0x37, 0x33, 0x6d, 0xc6, 0xa6, 0x4d, 0x74, 0x78, 0xe6, 0x35, 0x9b, 0xad, 0x7b, 0xc0, 0xc7, 0x82, 0x27, 0xb3, + 0xef, 0xf9, 0xf8, 0x1a, 0x38, 0x99, 0xed, 0x6d, 0xb4, 0xa2, 0x77, 0x51, 0x4a, 0x6f, 0xa2, 0x0d, 0x5d, 0x45, 0x17, + 0xc6, 0xc4, 0x38, 0xa9, 0xe1, 0x1c, 0x80, 0x56, 0x01, 0x24, 0x9e, 0xfa, 0xf5, 0x9e, 0x27, 0x55, 0xb8, 0xa2, 0x29, + 0xb8, 0x0d, 0xfb, 0xf6, 0x99, 0x57, 0xbe, 0x44, 0x6a, 0x8b, 0x18, 0x6b, 0xd6, 0x50, 0x71, 0xeb, 0xad, 0xfb, 0x48, + 0xd4, 0xb0, 0x73, 0x5d, 0x6c, 0xa2, 0x6a, 0x38, 0x99, 0x96, 0x80, 0xd8, 0x5a, 0x0e, 0x87, 0xee, 0x08, 0xd9, 0x3f, + 0x7e, 0x74, 0xa0, 0xe7, 0x9e, 0xb4, 0xd8, 0xb6, 0x2d, 0x7f, 0x60, 0x08, 0x53, 0xfa, 0xe9, 0x23, 0x1f, 0x10, 0x2b, + 0x2e, 0xe1, 0x6c, 0x04, 0xea, 0x68, 0x85, 0x4e, 0xbf, 0x55, 0x61, 0xa1, 0x0f, 0xf0, 0xed, 0x6d, 0x94, 0xd0, 0xbb, + 0x28, 0xf7, 0xc8, 0xda, 0xaa, 0x66, 0x72, 0x7a, 0x96, 0x85, 0xbc, 0x7d, 0xa0, 0x97, 0x4b, 0x00, 0xd1, 0x1a, 0xc4, + 0xbe, 0xd4, 0xf5, 0x08, 0x9c, 0x86, 0xd0, 0x24, 0x34, 0x82, 0xab, 0x0a, 0xc2, 0x08, 0xb8, 0x92, 0xf0, 0x37, 0x98, + 0xa8, 0xc0, 0x17, 0xe0, 0x22, 0x93, 0xa6, 0x39, 0x0f, 0x6a, 0x7f, 0x24, 0x4f, 0x8b, 0xb6, 0xb7, 0x2b, 0x8c, 0x26, + 0x18, 0x7b, 0xa2, 0x7d, 0x1e, 0x29, 0x47, 0x71, 0x91, 0x84, 0xd9, 0xe8, 0x56, 0x9d, 0xe7, 0x34, 0x1b, 0xdd, 0xe9, + 0x5f, 0x15, 0x1d, 0xd3, 0xef, 0x74, 0x40, 0x1b, 0x25, 0x7d, 0xeb, 0x38, 0x1b, 0xd0, 0x7a, 0xb1, 0x34, 0xfe, 0xd7, + 0x72, 0x74, 0x4b, 0xe5, 0xe8, 0xce, 0xb7, 0xa4, 0x9a, 0x4c, 0x8b, 0x63, 0x81, 0x86, 0x54, 0x9d, 0xdf, 0x17, 0xc0, + 0xcf, 0x95, 0xc6, 0x77, 0xda, 0x7c, 0xef, 0xb5, 0xff, 0xbc, 0x93, 0x27, 0x50, 0x2c, 0x51, 0xc1, 0xaa, 0x11, 0xd8, + 0xb1, 0x6f, 0xf2, 0xb8, 0x30, 0xa3, 0x14, 0x53, 0x6b, 0xd2, 0x8f, 0x81, 0x2b, 0xa6, 0xbd, 0x02, 0x5c, 0x2d, 0x77, + 0x3b, 0x15, 0x43, 0x13, 0xf6, 0xec, 0x18, 0xa2, 0x9e, 0x1b, 0xc7, 0x28, 0xd9, 0x70, 0x0f, 0x88, 0xb5, 0xcc, 0x5b, + 0xb9, 0x04, 0x24, 0xf0, 0xd6, 0xc3, 0xa4, 0x00, 0x8c, 0xc1, 0x72, 0x45, 0x74, 0x1e, 0x0f, 0x7d, 0x42, 0xbd, 0xd0, + 0xa8, 0x13, 0xb2, 0xb1, 0x25, 0x70, 0xfc, 0x61, 0x7d, 0x08, 0x04, 0xaf, 0xf2, 0x5c, 0x7f, 0xa5, 0x75, 0xfd, 0xa5, + 0xd2, 0x73, 0xc7, 0x72, 0x5d, 0x3f, 0x6b, 0x53, 0xa3, 0x97, 0x60, 0xe1, 0xbb, 0x55, 0xe6, 0x91, 0xdc, 0x22, 0xa4, + 0x2a, 0xb0, 0x52, 0xb7, 0x90, 0x60, 0xfe, 0x95, 0x9c, 0xad, 0xca, 0x7c, 0xf5, 0xc8, 0x83, 0x72, 0x36, 0x3d, 0xfd, + 0x0d, 0x09, 0xda, 0x5d, 0x47, 0x9a, 0xc7, 0x5b, 0x74, 0xf8, 0xec, 0x5a, 0x4b, 0xcc, 0xbd, 0x44, 0xc5, 0xf3, 0x29, + 0x60, 0xab, 0xe7, 0xd9, 0x95, 0xf2, 0xb1, 0xda, 0xc7, 0xf1, 0x33, 0xe7, 0x4f, 0x5c, 0x85, 0x1b, 0xd1, 0x50, 0x82, + 0x80, 0x37, 0x87, 0xb1, 0x2b, 0x54, 0x01, 0x0d, 0xcd, 0x0d, 0x1c, 0xe7, 0x6a, 0x58, 0x69, 0x02, 0xa6, 0xa5, 0x3c, + 0x3a, 0xc0, 0xa1, 0xc9, 0xa3, 0x76, 0xd3, 0xb0, 0x32, 0x74, 0xad, 0xd1, 0xe7, 0xb6, 0xd2, 0x19, 0x6f, 0x36, 0xfc, + 0xf0, 0x68, 0x50, 0xe1, 0x4f, 0xd2, 0x1c, 0x8d, 0x76, 0x6e, 0xb8, 0xd3, 0x08, 0xcc, 0x5c, 0xc9, 0x35, 0xd9, 0x1f, + 0x25, 0x2f, 0xbf, 0xa7, 0x17, 0x16, 0xd0, 0x9f, 0xff, 0x5c, 0x4c, 0x38, 0x69, 0x89, 0x09, 0xd1, 0xd2, 0x41, 0x8b, + 0x0e, 0xf6, 0x94, 0x57, 0xf6, 0x25, 0x5e, 0x3a, 0xc7, 0xff, 0xbe, 0x1e, 0x6b, 0x5f, 0x81, 0xd0, 0xea, 0xe4, 0x61, + 0x7b, 0xb2, 0x40, 0xd4, 0x80, 0x6a, 0x76, 0x55, 0x8e, 0x32, 0xed, 0xac, 0xc8, 0xb6, 0x21, 0x73, 0xdd, 0xcf, 0xd2, + 0xb0, 0x99, 0xec, 0x58, 0x58, 0x66, 0x18, 0xac, 0x9d, 0x2a, 0xfa, 0x1c, 0xb4, 0xfc, 0x08, 0x9e, 0x37, 0x95, 0x67, + 0x3e, 0x9b, 0x65, 0xc4, 0x0b, 0x74, 0xc1, 0xa9, 0x58, 0x36, 0xa5, 0x63, 0xe5, 0x6e, 0x57, 0xa2, 0xb1, 0x44, 0x19, + 0x05, 0x41, 0x6d, 0x83, 0xb0, 0xeb, 0xd2, 0x3d, 0xe9, 0xd3, 0x3e, 0x3e, 0xad, 0x40, 0xdf, 0xe3, 0xfb, 0x0c, 0x24, + 0xa6, 0x9e, 0xe4, 0xa1, 0x6a, 0x34, 0x47, 0x27, 0xcf, 0xe3, 0x54, 0xe3, 0xf3, 0x2b, 0xd9, 0x59, 0xf3, 0x6e, 0x35, + 0xa6, 0xf8, 0x8f, 0xd4, 0xed, 0x3b, 0x97, 0xa1, 0x89, 0xfe, 0x5a, 0x1e, 0xb4, 0x14, 0x16, 0x1c, 0xb7, 0x8d, 0xbf, + 0x7e, 0x9b, 0x39, 0xc4, 0xb0, 0x74, 0x39, 0xbc, 0x09, 0x1d, 0xba, 0xbb, 0xca, 0xde, 0x5c, 0x1f, 0x51, 0xa7, 0x2e, + 0xd6, 0x6d, 0x40, 0xc9, 0x92, 0x77, 0xeb, 0xf4, 0xc4, 0x4a, 0xdf, 0x1d, 0x86, 0x7b, 0xf3, 0xa8, 0xd9, 0xdd, 0xdd, + 0x6e, 0x42, 0xda, 0xf6, 0xc1, 0x78, 0x5f, 0xc2, 0x42, 0x9c, 0x77, 0xd8, 0xc1, 0xf7, 0x61, 0xf5, 0x98, 0x0f, 0x7e, + 0xc6, 0x71, 0x86, 0xd1, 0xcf, 0x94, 0xa1, 0xcf, 0xcb, 0x42, 0x5e, 0xa9, 0x4e, 0xf9, 0x42, 0xb7, 0x96, 0xa9, 0xf7, + 0x9b, 0xf8, 0x4d, 0x2b, 0x40, 0x8c, 0xd7, 0x15, 0x2b, 0xc5, 0x1b, 0x5a, 0x61, 0x5c, 0x03, 0xb7, 0xc9, 0xa1, 0x96, + 0x6a, 0x81, 0xa8, 0xcb, 0x4f, 0x1e, 0xf3, 0xc8, 0xa8, 0x33, 0xe1, 0xbb, 0xc7, 0xdc, 0x97, 0xae, 0xed, 0x37, 0xf1, + 0x53, 0x4d, 0x3b, 0xdc, 0x1f, 0xe8, 0x8e, 0xd6, 0x3d, 0xdc, 0x3c, 0x9b, 0x9f, 0x47, 0xe6, 0x8b, 0x01, 0x36, 0x6b, + 0x9f, 0x71, 0xd9, 0x33, 0xdc, 0xf7, 0xa6, 0x07, 0x63, 0x01, 0x81, 0xc4, 0x0c, 0xbd, 0x0c, 0x5c, 0xe0, 0x02, 0x77, + 0x85, 0x01, 0x43, 0x5c, 0xd3, 0x92, 0x33, 0x6d, 0x65, 0xeb, 0x23, 0x6f, 0xa3, 0x42, 0xb0, 0xae, 0x3b, 0x6e, 0x92, + 0x1c, 0x82, 0x13, 0xb6, 0xdc, 0xfb, 0xda, 0x6b, 0x67, 0xf8, 0x8f, 0x81, 0x70, 0x6e, 0x89, 0x9e, 0x51, 0xdb, 0x63, + 0xad, 0xee, 0x35, 0xbc, 0xca, 0x5d, 0xe4, 0x59, 0xbf, 0x99, 0x97, 0x86, 0x7d, 0xc1, 0x6b, 0x29, 0x38, 0x34, 0xb6, + 0x5b, 0xe1, 0x16, 0x8b, 0x77, 0xb4, 0x5a, 0x59, 0x6b, 0xab, 0xbd, 0x56, 0x2a, 0x7a, 0xff, 0x9a, 0xe3, 0xc4, 0x59, + 0x0a, 0xdb, 0x0f, 0x1f, 0x2e, 0xd8, 0x35, 0x01, 0x0c, 0x5a, 0x4c, 0x16, 0x28, 0x41, 0x25, 0x6b, 0x55, 0xbb, 0x9d, + 0x12, 0xbf, 0xdc, 0xcf, 0xba, 0xcc, 0x76, 0x1e, 0xbf, 0x6e, 0xd2, 0x3e, 0xf1, 0x39, 0xfa, 0x61, 0x7e, 0x67, 0x9d, + 0x94, 0x9c, 0x61, 0x5c, 0xcb, 0xff, 0xaf, 0xa2, 0x97, 0x45, 0x96, 0x46, 0x5b, 0xc3, 0x83, 0xd9, 0x50, 0x9b, 0x3e, + 0x34, 0x46, 0xe5, 0x96, 0x8d, 0x22, 0xa2, 0xd5, 0x2d, 0x08, 0x66, 0x14, 0xf7, 0x25, 0xda, 0xbc, 0x52, 0x65, 0xe1, + 0x1d, 0x3e, 0xb1, 0xd1, 0x1b, 0xb6, 0x27, 0x84, 0xf2, 0xfd, 0xd3, 0xc2, 0xac, 0x5a, 0x2a, 0x1a, 0x6c, 0x97, 0xf0, + 0x2e, 0x46, 0x95, 0x7e, 0xc2, 0x64, 0xcb, 0x82, 0xa9, 0xfe, 0x7f, 0x5f, 0x64, 0x69, 0x9b, 0xa2, 0x03, 0xd3, 0xd9, + 0xf4, 0xe9, 0xa4, 0x5b, 0x5c, 0x67, 0xc0, 0x22, 0x82, 0x2d, 0x15, 0x8e, 0x47, 0xa9, 0xdd, 0x20, 0x61, 0x22, 0xb8, + 0x89, 0x7a, 0xd9, 0xd1, 0x32, 0x25, 0xab, 0x02, 0x9e, 0x5f, 0xb9, 0xca, 0x74, 0x1c, 0x0d, 0xfd, 0xfe, 0x55, 0x6a, + 0x42, 0xbf, 0x52, 0x2f, 0x55, 0x71, 0x1e, 0x46, 0xd5, 0xa1, 0xc2, 0x18, 0xad, 0x68, 0x0a, 0xc7, 0x60, 0x76, 0x11, + 0xa6, 0x78, 0x39, 0xdb, 0x26, 0xec, 0x33, 0x06, 0x72, 0xa5, 0x0d, 0xea, 0x35, 0x25, 0xda, 0xb0, 0xf6, 0x66, 0x4e, + 0x09, 0xbd, 0x60, 0xa5, 0x7f, 0x17, 0xda, 0x80, 0x40, 0x51, 0x36, 0x53, 0xa6, 0xe7, 0xba, 0x9d, 0x17, 0x34, 0xa1, + 0x05, 0x5d, 0x93, 0x1a, 0xf4, 0xbd, 0x4e, 0xce, 0x8e, 0x4e, 0x76, 0x66, 0xd6, 0x63, 0x56, 0x0c, 0x27, 0xd3, 0x18, + 0xae, 0x69, 0xb1, 0xbb, 0xa6, 0xad, 0x9a, 0x37, 0xae, 0xc6, 0xc6, 0x69, 0xd0, 0x2e, 0x90, 0xb6, 0x69, 0x6e, 0x3f, + 0xf5, 0xb8, 0xfd, 0x4d, 0xcd, 0x56, 0xd3, 0xde, 0x66, 0xb7, 0xeb, 0xa5, 0x60, 0x23, 0xea, 0xf1, 0xf1, 0x1b, 0x25, + 0x5d, 0xb7, 0x5c, 0x7e, 0x0a, 0xcf, 0x1e, 0x5f, 0xbf, 0xf2, 0xc1, 0xe5, 0x68, 0xd5, 0xe6, 0xee, 0x57, 0xfb, 0xc8, + 0x72, 0x9f, 0x35, 0xb4, 0x5c, 0xcf, 0x50, 0x93, 0x3c, 0x1b, 0xed, 0x1d, 0x6a, 0xc1, 0x72, 0xd6, 0x4d, 0x78, 0x62, + 0xb0, 0x63, 0xaf, 0x1a, 0x9b, 0xa3, 0x32, 0x97, 0xac, 0x06, 0x09, 0xf4, 0x49, 0x9e, 0x69, 0xfa, 0x07, 0x19, 0xe6, + 0xa3, 0x5b, 0x9a, 0x03, 0xae, 0x58, 0x65, 0x2f, 0x19, 0xa4, 0xae, 0xda, 0x4b, 0x5c, 0xf9, 0x0a, 0x87, 0x64, 0x8b, + 0x4f, 0x86, 0xa9, 0xfa, 0xe4, 0x92, 0x07, 0xff, 0x6f, 0xab, 0x56, 0xe9, 0xb9, 0x49, 0x6e, 0x38, 0xfe, 0x75, 0xd2, + 0xf6, 0x31, 0x31, 0x48, 0xc0, 0x53, 0xbb, 0x18, 0xaa, 0x51, 0x55, 0xc4, 0xa2, 0xcc, 0x4d, 0xcc, 0xb1, 0x7b, 0xbb, + 0x86, 0x0e, 0xca, 0xe0, 0xd7, 0x0d, 0x9f, 0x98, 0x3b, 0xb0, 0x15, 0xe8, 0xe8, 0x44, 0x73, 0x19, 0x66, 0xe6, 0x32, + 0x4c, 0xbb, 0xb6, 0x0a, 0x0c, 0xaf, 0xda, 0x2a, 0x89, 0x72, 0x35, 0xea, 0x71, 0x33, 0x4b, 0xcd, 0x5e, 0xe4, 0xdd, + 0x6b, 0xd2, 0x93, 0xf8, 0xd3, 0x95, 0x27, 0xaf, 0x87, 0x01, 0x91, 0x9f, 0xb3, 0x34, 0x5c, 0xa3, 0x20, 0x38, 0xb5, + 0xda, 0x81, 0x34, 0x1f, 0x01, 0x32, 0x3f, 0x4e, 0xc3, 0x77, 0x5a, 0x9c, 0x43, 0xb6, 0x4a, 0xe3, 0xc4, 0x56, 0x46, + 0x3d, 0x04, 0x77, 0xde, 0x2b, 0x1e, 0x43, 0xe0, 0xc3, 0x0f, 0xb8, 0x19, 0x54, 0x74, 0x5b, 0x62, 0xa2, 0xb4, 0x79, + 0xd4, 0x2d, 0x1f, 0x35, 0x84, 0x4a, 0x56, 0x86, 0x17, 0x43, 0x7b, 0xf7, 0x04, 0x46, 0x95, 0x13, 0xc8, 0x0c, 0x8b, + 0xc3, 0xa3, 0x61, 0xaa, 0x04, 0x45, 0x43, 0x39, 0x5c, 0xa1, 0x1c, 0x10, 0x93, 0x40, 0x60, 0x54, 0x0c, 0x52, 0x5d, + 0x99, 0x7a, 0x31, 0x48, 0xf5, 0xad, 0x8a, 0xd4, 0x67, 0x59, 0x58, 0x51, 0xdd, 0x22, 0x3a, 0xa6, 0x43, 0x49, 0x57, + 0x66, 0xa7, 0xe6, 0x5a, 0x7a, 0xa1, 0x96, 0xe3, 0x33, 0x9d, 0x06, 0xa3, 0x78, 0xea, 0x52, 0xf4, 0x5b, 0xb5, 0x9f, + 0xfd, 0xb7, 0x98, 0x52, 0x23, 0x36, 0xb5, 0xb7, 0x88, 0x61, 0xd5, 0x7e, 0xc8, 0xaa, 0x1c, 0xb4, 0xbb, 0xa0, 0x6c, + 0xac, 0x8c, 0xf3, 0x7c, 0x23, 0x98, 0x39, 0x68, 0x1b, 0xab, 0xa6, 0x0f, 0xbd, 0x11, 0xa3, 0xf6, 0xc6, 0x54, 0xe3, + 0x9e, 0xc0, 0x4f, 0x1b, 0x34, 0xdd, 0x8b, 0x3c, 0x47, 0x3d, 0xf2, 0xee, 0x7f, 0xe6, 0xc8, 0xce, 0xe4, 0x93, 0x58, + 0x26, 0x75, 0xfb, 0x98, 0x04, 0x0b, 0x55, 0xc7, 0xe8, 0xc2, 0x8d, 0x4c, 0x69, 0x3f, 0xf7, 0xa6, 0x1f, 0xf1, 0x4c, + 0x1e, 0xb6, 0x43, 0xa3, 0xbe, 0x34, 0xac, 0x25, 0x45, 0xd4, 0x17, 0xf4, 0xd6, 0x54, 0x47, 0x47, 0xd4, 0xeb, 0x08, + 0xac, 0xae, 0x68, 0x8b, 0x1a, 0x80, 0xc9, 0xb8, 0xb6, 0xb5, 0xf9, 0x1c, 0x4c, 0x6d, 0x55, 0x05, 0x4f, 0xe9, 0xbe, + 0x50, 0xba, 0x37, 0xa9, 0xeb, 0xd6, 0x10, 0x5b, 0xc0, 0x80, 0xc0, 0x8d, 0x9e, 0x9a, 0xfe, 0xa0, 0x89, 0x0a, 0x40, + 0x83, 0xc6, 0xed, 0x4c, 0xe7, 0x48, 0xf4, 0x3b, 0xb5, 0x69, 0x9b, 0xa9, 0x5e, 0x55, 0x3e, 0x80, 0x8a, 0x3f, 0x4b, + 0x67, 0x17, 0x66, 0xc4, 0x02, 0x18, 0xf7, 0xc0, 0x99, 0xea, 0x9d, 0x64, 0x60, 0x3d, 0x91, 0xe7, 0x59, 0xc9, 0x13, + 0x29, 0x60, 0x46, 0xe4, 0xd5, 0x95, 0x14, 0x30, 0x0c, 0x6a, 0x00, 0xd0, 0xa2, 0xb9, 0x8c, 0x26, 0xfc, 0xab, 0x9a, + 0xde, 0x97, 0x87, 0x7f, 0xa5, 0x73, 0x7d, 0x3d, 0xae, 0xc1, 0x50, 0x79, 0x53, 0xf1, 0xbd, 0x4c, 0x5f, 0xf3, 0x27, + 0x5e, 0xa6, 0x95, 0xdc, 0x14, 0x7b, 0x59, 0xbe, 0xfa, 0x9a, 0x3f, 0xd5, 0x79, 0x8e, 0x9e, 0xd4, 0x34, 0x8d, 0xef, + 0xf6, 0xb2, 0x7c, 0xf3, 0xf5, 0x13, 0x9b, 0xe7, 0xab, 0x71, 0x4d, 0x6f, 0x38, 0xff, 0xe8, 0x32, 0x4d, 0x74, 0x55, + 0xe3, 0x27, 0xdf, 0xd8, 0x5c, 0x4f, 0x6a, 0x7a, 0x25, 0x45, 0xb5, 0xda, 0x2b, 0xea, 0xe8, 0xeb, 0xa3, 0x6f, 0xf8, + 0xd7, 0xa6, 0x7b, 0x47, 0x35, 0xfd, 0x73, 0x13, 0x17, 0x15, 0x2f, 0xf6, 0x8a, 0xfb, 0xdb, 0x37, 0xdf, 0x3c, 0xb1, + 0x19, 0x9f, 0xd4, 0xf4, 0x8e, 0xc7, 0x1d, 0x6d, 0x9f, 0x3c, 0x7d, 0xc2, 0xff, 0x56, 0xd7, 0xf4, 0x17, 0xe6, 0x07, + 0x47, 0x3d, 0xc9, 0x3c, 0x3d, 0x7c, 0x22, 0x9b, 0xa8, 0x01, 0x43, 0x0f, 0x0d, 0x20, 0x97, 0x56, 0x4d, 0x73, 0x8f, + 0x57, 0x2e, 0xb8, 0x7d, 0x9f, 0xc5, 0x69, 0xbc, 0x86, 0x83, 0x60, 0x8b, 0xc6, 0x59, 0x05, 0x70, 0xaa, 0xc0, 0x7b, + 0x46, 0x25, 0xcd, 0x4a, 0xf9, 0x0f, 0xce, 0x3f, 0xc2, 0xa0, 0x21, 0xa4, 0x8d, 0x8a, 0x0c, 0xf4, 0x76, 0xad, 0x23, + 0x1b, 0xa1, 0xff, 0x66, 0x33, 0x0e, 0x8e, 0x0f, 0xa3, 0xd7, 0xef, 0x87, 0x05, 0x13, 0x61, 0x41, 0x08, 0xfd, 0x23, + 0x2c, 0xc0, 0xa1, 0xa4, 0x60, 0x5e, 0x3e, 0xe3, 0x7b, 0xae, 0x8d, 0xc2, 0x42, 0x10, 0xdd, 0x45, 0xf6, 0x01, 0x55, + 0x8f, 0xbe, 0x43, 0x37, 0xc4, 0xcb, 0x0a, 0x0b, 0x86, 0x56, 0x35, 0x30, 0x43, 0x50, 0xfc, 0x6b, 0x1e, 0x4a, 0xf0, + 0x89, 0x07, 0xf8, 0xe8, 0x31, 0x99, 0x73, 0x75, 0xad, 0x7d, 0x7b, 0x11, 0x16, 0x34, 0xd0, 0x6d, 0x87, 0xa0, 0x03, + 0x91, 0xff, 0x02, 0x3c, 0x05, 0x06, 0x3e, 0x2c, 0xec, 0x4a, 0xee, 0xfb, 0xab, 0xff, 0x62, 0x58, 0x47, 0x17, 0x7e, + 0xf4, 0x17, 0xeb, 0xc2, 0x9e, 0x91, 0xa9, 0x3c, 0x2e, 0x87, 0x93, 0xe9, 0x60, 0x20, 0x5d, 0x1c, 0xb7, 0x93, 0x6c, + 0xf1, 0xcb, 0x42, 0x2e, 0x97, 0xa8, 0xfb, 0xc6, 0x79, 0x9d, 0xeb, 0xbf, 0x91, 0x76, 0x3e, 0x78, 0x7d, 0xf2, 0xdb, + 0xd9, 0xe9, 0xc9, 0x4b, 0x70, 0x3e, 0xf8, 0xf0, 0xe2, 0xfb, 0x17, 0xef, 0x55, 0x70, 0x77, 0x35, 0xe7, 0xfd, 0xbe, + 0x93, 0xfa, 0x84, 0x7c, 0x58, 0x91, 0xc3, 0x30, 0x7e, 0x5c, 0x28, 0xa3, 0x07, 0x72, 0xcc, 0x2c, 0x14, 0x32, 0x54, + 0x51, 0xdb, 0xdf, 0xe5, 0x70, 0xe2, 0x81, 0x59, 0xdc, 0x35, 0x44, 0xb8, 0x7e, 0xcb, 0x6d, 0x90, 0x35, 0x39, 0xf3, + 0xfa, 0xc1, 0xc9, 0x54, 0x3a, 0xb6, 0xb0, 0x60, 0x50, 0x36, 0xb4, 0xe9, 0x24, 0x5b, 0x14, 0x4b, 0xdb, 0x2e, 0xb7, + 0x40, 0x46, 0x69, 0x76, 0x71, 0x11, 0x2a, 0xe8, 0xea, 0x19, 0x68, 0x00, 0x4c, 0xa3, 0x0a, 0xd7, 0x22, 0x3e, 0xf7, + 0xcb, 0x8f, 0xc6, 0x5e, 0xf3, 0x6e, 0x51, 0xf7, 0x64, 0x9a, 0x55, 0x35, 0x06, 0x74, 0x30, 0xa1, 0xdc, 0x0d, 0xba, + 0x09, 0x26, 0xa3, 0xda, 0xf2, 0xcb, 0xa2, 0x5a, 0x9a, 0xe6, 0xb8, 0x61, 0xa8, 0xbc, 0x92, 0x37, 0xb2, 0x81, 0xc8, + 0x40, 0x32, 0x0c, 0x7b, 0x34, 0x46, 0x91, 0xfa, 0xc1, 0xbe, 0x77, 0xfc, 0x36, 0x97, 0x10, 0x4d, 0x31, 0x03, 0xe9, + 0xfc, 0x89, 0x50, 0xce, 0xe5, 0x92, 0xf1, 0x85, 0x58, 0xce, 0xc0, 0xed, 0x7c, 0x21, 0x96, 0x11, 0x06, 0xe5, 0xcb, + 0x20, 0x56, 0x09, 0xd8, 0xbd, 0x38, 0x08, 0xdf, 0x4e, 0x68, 0x03, 0xbb, 0x81, 0x24, 0x1b, 0x94, 0x76, 0xa5, 0x21, + 0xca, 0x9d, 0xf2, 0x68, 0x83, 0xc8, 0x43, 0xac, 0x5a, 0x54, 0x6d, 0x4f, 0x36, 0x73, 0x31, 0xc1, 0x55, 0x16, 0x33, + 0x39, 0x8d, 0x8f, 0x59, 0x31, 0x8d, 0xa1, 0x94, 0x38, 0x4d, 0xc3, 0x98, 0x4e, 0xa8, 0x20, 0x24, 0x61, 0x7c, 0x11, + 0x2f, 0x69, 0x82, 0x52, 0x82, 0x10, 0x42, 0x7e, 0x8c, 0xd0, 0x36, 0x07, 0x96, 0xbc, 0xdd, 0x7e, 0x9e, 0x7e, 0x6e, + 0xcf, 0x70, 0x19, 0x15, 0xa1, 0x5b, 0x74, 0xd6, 0xf0, 0x6f, 0x44, 0x05, 0x8d, 0xb1, 0x62, 0x08, 0x02, 0x5e, 0x60, + 0x54, 0xc2, 0x82, 0xc4, 0xac, 0x82, 0x28, 0x02, 0xe5, 0x22, 0x5e, 0xb2, 0x82, 0x36, 0x6d, 0x4e, 0x63, 0x6d, 0x12, + 0xd4, 0x73, 0x58, 0x6a, 0x07, 0x52, 0xa9, 0x10, 0x7b, 0x7c, 0x2e, 0xa2, 0x6b, 0x6d, 0x68, 0x00, 0x28, 0x50, 0x4a, + 0x2e, 0x7e, 0xf3, 0xf9, 0x1e, 0x6e, 0x0a, 0xfa, 0x9f, 0x6d, 0x4d, 0xb4, 0xb3, 0x5c, 0x1d, 0x7a, 0x8b, 0x25, 0x8d, + 0xf3, 0x1c, 0x42, 0xb1, 0x19, 0x04, 0x72, 0x91, 0x55, 0x10, 0xd1, 0xe2, 0x2e, 0x30, 0x21, 0xe1, 0xa0, 0x4d, 0xbf, + 0x40, 0x6a, 0x43, 0x4c, 0xae, 0x3c, 0x31, 0xb0, 0xdb, 0x2a, 0x41, 0xc0, 0x91, 0x9e, 0x67, 0x7f, 0x35, 0x31, 0xd6, + 0x34, 0x35, 0x33, 0xf1, 0x36, 0x14, 0xa2, 0x41, 0x0b, 0xa2, 0x19, 0xbc, 0x7f, 0xae, 0x38, 0x5e, 0x75, 0xe0, 0x07, + 0xbc, 0x73, 0x71, 0xe6, 0xd5, 0xcc, 0x23, 0x72, 0xea, 0xa3, 0x1c, 0xd1, 0x2f, 0x79, 0x58, 0x8d, 0x74, 0x32, 0xc6, + 0x4a, 0xe2, 0xa0, 0xb7, 0xc1, 0x82, 0x39, 0xa1, 0x6b, 0x1e, 0x5a, 0x3e, 0xfe, 0x25, 0x32, 0x19, 0x25, 0x35, 0x56, + 0x74, 0xa5, 0xc5, 0x88, 0xf3, 0x1a, 0x66, 0x69, 0xb2, 0xa2, 0x8b, 0x85, 0x26, 0xcd, 0x42, 0x99, 0x06, 0xf8, 0x04, + 0x5a, 0x8c, 0xdc, 0x43, 0x4d, 0x1b, 0x08, 0x0d, 0xfb, 0x43, 0xc0, 0x47, 0xee, 0xa1, 0xc3, 0xff, 0xcf, 0xb3, 0x0b, + 0x44, 0xda, 0x9b, 0x9b, 0xc8, 0x78, 0xa4, 0x6e, 0xe0, 0xa0, 0x18, 0x1f, 0xfb, 0x66, 0xe2, 0x67, 0xce, 0xe8, 0x43, + 0x52, 0xf9, 0x0e, 0x1f, 0x2c, 0x7f, 0xbc, 0xa9, 0x99, 0x95, 0x11, 0xac, 0x87, 0xdd, 0x0e, 0x17, 0x44, 0xdb, 0x05, + 0x90, 0x7a, 0xc6, 0xab, 0x85, 0x6f, 0xbc, 0x1a, 0xdf, 0x63, 0xbc, 0xea, 0xce, 0xd4, 0x30, 0x27, 0x5b, 0xd4, 0x67, + 0x29, 0x79, 0x7e, 0x8e, 0x32, 0xc1, 0xa6, 0xcb, 0x59, 0x49, 0x55, 0x2a, 0xa1, 0xbd, 0xd8, 0xcf, 0x18, 0xdf, 0x12, + 0x8c, 0xb3, 0xe2, 0x30, 0x12, 0xa8, 0x4a, 0x25, 0x75, 0xd8, 0x2b, 0x40, 0x3d, 0x06, 0xef, 0x0d, 0x86, 0xa8, 0x91, + 0xb1, 0x9b, 0x36, 0x10, 0x1a, 0x1a, 0xeb, 0xd1, 0x9e, 0xb5, 0x1e, 0xdd, 0xed, 0x2a, 0xe3, 0x6f, 0x27, 0x37, 0x45, + 0x82, 0xa8, 0xc2, 0x6a, 0x34, 0x01, 0xde, 0x34, 0xb1, 0xb7, 0x25, 0xa7, 0xb4, 0xc0, 0xf0, 0xd9, 0x7f, 0x84, 0xa5, + 0x53, 0x49, 0x94, 0x64, 0x5e, 0x46, 0x03, 0x77, 0x0e, 0x3e, 0x8f, 0x2b, 0x58, 0x03, 0x10, 0xc9, 0x11, 0x3d, 0x5c, + 0xff, 0x08, 0xa5, 0xcb, 0x2c, 0xc9, 0x5c, 0x42, 0x66, 0x2e, 0xd2, 0x76, 0xd6, 0xc1, 0xc4, 0x99, 0xd4, 0x7a, 0x63, + 0x21, 0x87, 0x06, 0xf9, 0x01, 0x94, 0x21, 0x0e, 0x9f, 0x7c, 0x30, 0xa1, 0x52, 0x85, 0x52, 0x6d, 0x74, 0xb3, 0x1b, + 0x78, 0xe5, 0x43, 0x76, 0xc5, 0xcb, 0x2a, 0xbe, 0x5a, 0x1b, 0x4b, 0x62, 0xce, 0xee, 0x73, 0xdb, 0xa3, 0xc2, 0xbc, + 0x7a, 0xf3, 0xe2, 0xfb, 0x93, 0xc6, 0xab, 0x7d, 0xc4, 0xd1, 0x10, 0x6c, 0x2b, 0xc6, 0x18, 0xbd, 0xc5, 0xa7, 0xc1, + 0x44, 0xb9, 0x46, 0xa0, 0x77, 0x29, 0xe8, 0xb7, 0x3f, 0xd7, 0x13, 0xf0, 0x8a, 0xeb, 0xe5, 0x97, 0x7c, 0x04, 0x2c, + 0x51, 0xa1, 0x67, 0x85, 0xb9, 0x59, 0x99, 0xdf, 0xdb, 0xad, 0xc8, 0x4c, 0xbb, 0xd2, 0xc8, 0x40, 0xbc, 0xda, 0x0e, + 0x63, 0xe1, 0xd2, 0x35, 0xdd, 0x0e, 0x76, 0xb5, 0xf2, 0x2c, 0x91, 0x77, 0xbb, 0x12, 0x3a, 0x64, 0x07, 0xdc, 0x7b, + 0x19, 0xdf, 0xc2, 0xcb, 0xd2, 0xeb, 0x66, 0x33, 0x78, 0x02, 0x98, 0x09, 0x17, 0xce, 0xb2, 0x38, 0x66, 0x3c, 0x09, + 0x55, 0x6c, 0xae, 0x86, 0xc8, 0x5b, 0x11, 0x5a, 0xb3, 0xbf, 0x42, 0x31, 0x02, 0xbb, 0x93, 0xd3, 0x8f, 0xd9, 0x7a, + 0xbe, 0x02, 0xd4, 0xfc, 0xab, 0x4c, 0x00, 0xcd, 0xb5, 0x6b, 0xc1, 0x36, 0x85, 0x36, 0xd7, 0xf5, 0xb3, 0x78, 0x1d, + 0x27, 0xa0, 0xba, 0x01, 0x6f, 0x91, 0x3b, 0x2d, 0xba, 0x32, 0xe8, 0xa2, 0xf4, 0x81, 0x72, 0x2c, 0x29, 0x74, 0xf4, + 0xbd, 0x27, 0xd4, 0xb9, 0x67, 0x00, 0x97, 0x34, 0x6a, 0x9e, 0x6a, 0x29, 0x63, 0x01, 0xb0, 0xd0, 0xc1, 0x5c, 0x91, + 0xad, 0xe8, 0xd6, 0x60, 0x52, 0xc0, 0x5b, 0x03, 0xfc, 0x21, 0xb2, 0x4a, 0xdd, 0x15, 0xcb, 0xb0, 0xf4, 0xec, 0xaf, + 0xfb, 0xfd, 0xd8, 0xb3, 0xbf, 0xbe, 0xd0, 0xb4, 0x2e, 0x6e, 0x37, 0x80, 0xd4, 0x18, 0x40, 0xe4, 0x44, 0x0f, 0x84, + 0x89, 0x28, 0xd6, 0xf4, 0xfd, 0x3b, 0x31, 0x59, 0x14, 0x08, 0xfd, 0x5e, 0xbd, 0x9e, 0x94, 0x04, 0x74, 0x6a, 0x15, + 0x9b, 0x0d, 0xb4, 0xd9, 0x07, 0x04, 0x44, 0xf5, 0x33, 0xb2, 0xc5, 0x52, 0x39, 0x17, 0xab, 0xf0, 0xe1, 0x63, 0x0a, + 0x01, 0x85, 0x3b, 0x6a, 0x74, 0xde, 0x86, 0x48, 0xa0, 0xac, 0x50, 0xc4, 0x9a, 0x17, 0x6b, 0x49, 0xc8, 0x62, 0xbc, + 0x44, 0xc1, 0x95, 0x03, 0x76, 0xe5, 0x6c, 0x32, 0x2c, 0x23, 0xce, 0xc2, 0xfb, 0xbf, 0x99, 0x2c, 0x09, 0x6a, 0xae, + 0xfc, 0x40, 0x8e, 0x7b, 0x99, 0x1a, 0x7b, 0xaa, 0x51, 0x83, 0x60, 0x32, 0x82, 0xc0, 0x70, 0xc3, 0xcf, 0xf8, 0xf8, + 0x68, 0x49, 0x40, 0x45, 0x66, 0xcd, 0x42, 0xcc, 0x8b, 0xe3, 0xaf, 0x00, 0x35, 0x66, 0x74, 0xf4, 0x14, 0x40, 0x61, + 0x21, 0x20, 0xfa, 0x18, 0x64, 0xb4, 0x02, 0x7e, 0x0b, 0xf5, 0xbb, 0x75, 0xe2, 0xfb, 0xd0, 0xaf, 0x82, 0x5e, 0xc4, + 0xc0, 0x70, 0x44, 0x93, 0xc3, 0x90, 0x0f, 0x26, 0x03, 0xd0, 0x96, 0x78, 0xbb, 0xaf, 0xa5, 0x15, 0x37, 0xa7, 0x4b, + 0xa7, 0xfb, 0x27, 0x6d, 0x82, 0x24, 0x52, 0xc9, 0x4a, 0x45, 0x0c, 0x20, 0x94, 0xa5, 0xda, 0x26, 0x2b, 0xb0, 0xac, + 0x30, 0x4b, 0x9a, 0x1b, 0x94, 0xc4, 0xfd, 0xcd, 0xc0, 0x31, 0x6a, 0xd6, 0x49, 0x58, 0xb6, 0xdc, 0xa8, 0x01, 0x3e, + 0x27, 0x61, 0x85, 0xbd, 0xe1, 0xcc, 0xa5, 0x77, 0xa6, 0xc3, 0xd5, 0x31, 0x67, 0xaf, 0x39, 0x82, 0x71, 0x24, 0x78, + 0xe3, 0xa1, 0x2b, 0xa6, 0xa1, 0x22, 0x53, 0xc6, 0xc1, 0xb4, 0x07, 0xb8, 0xf7, 0x1c, 0x8c, 0xc3, 0xd8, 0xa0, 0xb2, + 0xa4, 0x3e, 0xf5, 0xee, 0x42, 0x20, 0x48, 0x6b, 0xbd, 0xcc, 0xe7, 0x78, 0x7a, 0x46, 0x28, 0xfb, 0x43, 0x0e, 0x5f, + 0x80, 0x1d, 0x05, 0x99, 0x4d, 0xf8, 0xd3, 0xc7, 0xfb, 0x81, 0xaa, 0xf8, 0x20, 0x38, 0x88, 0x45, 0x7a, 0x10, 0x0c, + 0x04, 0xfc, 0x2a, 0xf8, 0x41, 0x25, 0xe5, 0xc1, 0x45, 0x5c, 0x1c, 0xc4, 0xeb, 0xb8, 0xa8, 0x0e, 0x6e, 0xb2, 0x6a, + 0x75, 0x60, 0x3a, 0x04, 0xd0, 0xbc, 0xc1, 0x20, 0x1e, 0x04, 0x07, 0xc1, 0xa0, 0x30, 0x53, 0xbb, 0x66, 0x65, 0xe3, + 0x38, 0x33, 0x21, 0xca, 0x82, 0x66, 0x80, 0xb0, 0xc6, 0x69, 0x00, 0x7c, 0xea, 0x86, 0xa5, 0xf4, 0x02, 0xc3, 0x0d, + 0x88, 0xe9, 0x06, 0xfa, 0x00, 0x3c, 0xf2, 0x86, 0xc6, 0xb0, 0x04, 0x2e, 0x06, 0x03, 0xb2, 0x81, 0xc8, 0x05, 0x1b, + 0x6a, 0x83, 0x38, 0x84, 0x1b, 0x65, 0xa7, 0xbd, 0x0f, 0xcc, 0xb4, 0xdb, 0x01, 0xa2, 0xf2, 0x84, 0xf4, 0xfb, 0xf6, + 0x1b, 0xea, 0x5f, 0xb0, 0x57, 0x60, 0x7f, 0x55, 0x54, 0x61, 0x22, 0x95, 0xe6, 0xfb, 0x92, 0xcd, 0x06, 0x2a, 0xe2, + 0xf0, 0x9e, 0x23, 0x45, 0x1b, 0x95, 0xcb, 0xb2, 0x27, 0xab, 0x86, 0xaf, 0xc4, 0x15, 0x77, 0x7e, 0x5c, 0x95, 0x94, + 0x79, 0x95, 0xad, 0x15, 0xfb, 0x37, 0xe7, 0x9a, 0xfb, 0x03, 0xeb, 0xcf, 0xe6, 0x2b, 0xb8, 0xb6, 0x7a, 0xef, 0x9a, + 0x5c, 0x23, 0x72, 0x96, 0x50, 0x2e, 0xa9, 0x6d, 0x1e, 0xde, 0xd2, 0xf7, 0xf9, 0xd5, 0xb7, 0x99, 0x4e, 0xe3, 0xb3, + 0x0a, 0x0b, 0x17, 0xa2, 0x15, 0xc1, 0xa1, 0x21, 0x97, 0xcd, 0x23, 0xc0, 0x5c, 0xfb, 0x6c, 0x05, 0x05, 0xa9, 0xcf, + 0x2a, 0xf4, 0x6e, 0x85, 0x84, 0x97, 0x9a, 0x5d, 0x7a, 0x18, 0x48, 0x19, 0xb7, 0x87, 0x96, 0x30, 0x69, 0x79, 0x11, + 0xde, 0x7b, 0xcd, 0x4d, 0xee, 0x79, 0x88, 0xd1, 0x8b, 0x3c, 0x3b, 0x01, 0x63, 0xdd, 0x25, 0x3b, 0x1b, 0x9e, 0xf8, + 0x0d, 0xcf, 0x59, 0x8b, 0x46, 0xd3, 0x15, 0x4b, 0xfa, 0xfd, 0x18, 0x4c, 0xbc, 0x53, 0x96, 0xc3, 0xaf, 0x7c, 0x49, + 0x37, 0x0c, 0x30, 0xc5, 0xe8, 0x05, 0x24, 0xa4, 0x88, 0x44, 0xb2, 0x51, 0x27, 0xc9, 0x27, 0xba, 0x0b, 0xc0, 0xe8, + 0x17, 0xf3, 0x34, 0x5a, 0xdd, 0x6b, 0x66, 0x81, 0xe4, 0x19, 0xfa, 0xae, 0x83, 0xed, 0x8d, 0x7d, 0x90, 0x72, 0x7e, + 0x2c, 0xa6, 0x83, 0x01, 0x27, 0x1a, 0x6e, 0xbc, 0x54, 0xe2, 0x5a, 0xdd, 0xe2, 0x8e, 0x61, 0x2c, 0xf5, 0x6d, 0x11, + 0x83, 0x03, 0x76, 0xd1, 0xca, 0x6e, 0x1f, 0x60, 0x5f, 0x39, 0xde, 0xa5, 0xca, 0xee, 0xf4, 0x98, 0x69, 0x2e, 0x5b, + 0x4d, 0x3a, 0xa9, 0xb8, 0x9f, 0xc8, 0x37, 0xb9, 0x83, 0x2e, 0x97, 0x63, 0xcd, 0x5b, 0x0e, 0x40, 0x45, 0x3f, 0x52, + 0x54, 0xf7, 0x33, 0x1c, 0x61, 0x1e, 0xac, 0xdb, 0x7c, 0x72, 0x68, 0x0a, 0x1c, 0x22, 0x4f, 0xda, 0x68, 0x0a, 0xe8, + 0xde, 0xc5, 0xe3, 0xae, 0x7e, 0x5b, 0xba, 0x0b, 0x94, 0x68, 0xaf, 0xe2, 0x86, 0x1f, 0x13, 0x75, 0x3a, 0xd3, 0x86, + 0xd0, 0xbf, 0x32, 0xe2, 0xfe, 0xd2, 0xb8, 0x8a, 0x37, 0xbd, 0xcb, 0xe7, 0x1c, 0xea, 0xec, 0x86, 0x50, 0x00, 0xae, + 0xda, 0xd3, 0xa9, 0x1b, 0x43, 0x7a, 0xa5, 0x44, 0xb7, 0xc1, 0xc1, 0xee, 0xf5, 0x19, 0x47, 0xd1, 0x8f, 0x51, 0x23, + 0xdf, 0x44, 0xe2, 0xb1, 0x1c, 0xc4, 0x8f, 0x0b, 0xba, 0x8a, 0xc4, 0xe3, 0x62, 0x10, 0x3f, 0x96, 0x75, 0xbd, 0x7f, + 0xae, 0xdc, 0xdf, 0x47, 0xe4, 0x59, 0xf7, 0xf6, 0x52, 0x09, 0x1b, 0x03, 0xcf, 0xae, 0x25, 0x84, 0x53, 0xf0, 0x44, + 0xb6, 0x96, 0x3e, 0x74, 0x6e, 0xf7, 0xb1, 0x65, 0x92, 0x20, 0xe8, 0x79, 0x9b, 0x4d, 0xa2, 0xd8, 0xd9, 0xe6, 0xd1, + 0x87, 0x53, 0x20, 0xa1, 0xdb, 0x6d, 0xb3, 0xae, 0xd6, 0x80, 0x62, 0x1a, 0x8e, 0xf9, 0x61, 0x31, 0xba, 0xf1, 0xdd, + 0xf5, 0x0f, 0x8b, 0xd1, 0x8a, 0x0c, 0x27, 0x66, 0xf2, 0xe3, 0xd9, 0x78, 0x1e, 0x47, 0x93, 0xba, 0xe3, 0xb4, 0xd0, + 0xf8, 0xa7, 0xde, 0x2d, 0x14, 0x81, 0x53, 0x31, 0x82, 0x23, 0xa7, 0x42, 0x39, 0x29, 0x35, 0x30, 0xfc, 0x0f, 0xaa, + 0x3d, 0x6d, 0xda, 0xeb, 0xb8, 0x4a, 0x56, 0x99, 0xb8, 0xd4, 0xe1, 0xc3, 0x75, 0x74, 0x71, 0x1b, 0xd0, 0xce, 0xbb, + 0x4c, 0x3b, 0x7e, 0x9d, 0x34, 0xe8, 0x89, 0xab, 0x99, 0x01, 0xb7, 0xee, 0x47, 0x68, 0x86, 0xc0, 0x68, 0x79, 0xfe, + 0x0e, 0x31, 0xb7, 0x7f, 0x55, 0x36, 0xbf, 0x8a, 0xf6, 0x39, 0x32, 0x52, 0xb6, 0xc9, 0x48, 0x05, 0x46, 0x98, 0x52, + 0x24, 0x71, 0x15, 0x42, 0x20, 0xfb, 0xcf, 0x29, 0xae, 0xc5, 0xd2, 0x7b, 0x0d, 0xc2, 0x04, 0xdb, 0x05, 0xed, 0x57, + 0xb7, 0x77, 0x5b, 0x69, 0xb1, 0x47, 0xea, 0xfb, 0xdc, 0xd9, 0xae, 0x68, 0xf2, 0xf7, 0x79, 0x03, 0xda, 0x00, 0xa2, + 0xbc, 0xaf, 0x8f, 0x4a, 0xe0, 0x64, 0xc4, 0x0d, 0x25, 0x46, 0x2f, 0xe8, 0xea, 0x44, 0xee, 0xd9, 0xa9, 0x79, 0x53, + 0x31, 0x57, 0x71, 0xe5, 0x9b, 0x3d, 0xf3, 0x1f, 0x0c, 0x05, 0x15, 0x60, 0xe0, 0x6d, 0xce, 0x78, 0x74, 0xa0, 0xbb, + 0x31, 0x3a, 0x2d, 0xd8, 0x2c, 0xa8, 0xcb, 0xba, 0x69, 0xe3, 0x41, 0x23, 0x0e, 0x8a, 0x62, 0x55, 0xa8, 0x91, 0xf0, + 0x44, 0x20, 0x60, 0xca, 0xae, 0x78, 0x64, 0x04, 0x35, 0xbd, 0x09, 0x85, 0x0d, 0x05, 0x7f, 0x95, 0xa8, 0xa6, 0x37, + 0xa1, 0x4d, 0x26, 0x4e, 0x33, 0x88, 0x60, 0x46, 0x6c, 0xf7, 0x5b, 0x40, 0x9b, 0x5b, 0x33, 0xda, 0xd6, 0xb5, 0xd5, + 0x56, 0x21, 0x97, 0x14, 0x29, 0xcb, 0x7f, 0xa7, 0xa6, 0x82, 0x92, 0x5a, 0x2e, 0x7a, 0x93, 0xa6, 0x8b, 0x1e, 0xcf, + 0x8c, 0x24, 0x50, 0xb9, 0xe5, 0x8e, 0xd1, 0x1f, 0xc2, 0x02, 0x8f, 0x98, 0x38, 0xb1, 0x60, 0x6e, 0x35, 0x63, 0xd9, + 0x42, 0x2c, 0x47, 0x6b, 0x09, 0x61, 0x83, 0x8f, 0x59, 0xb6, 0x28, 0xf5, 0x43, 0xe8, 0x0b, 0x4b, 0xdf, 0x82, 0x5d, + 0x6c, 0xb0, 0x96, 0x65, 0x00, 0xbe, 0x17, 0x74, 0xbb, 0x96, 0x65, 0x24, 0x55, 0xf7, 0xe3, 0x1a, 0x4b, 0x50, 0x69, + 0x85, 0x4a, 0x4b, 0x6a, 0x2c, 0x08, 0x7c, 0x55, 0x75, 0xf9, 0x90, 0xec, 0x2a, 0x50, 0x4f, 0x1d, 0x35, 0xe0, 0x14, + 0xa8, 0x2a, 0xb0, 0x20, 0x09, 0x2a, 0x43, 0x57, 0x05, 0xa6, 0x15, 0x98, 0x66, 0xaa, 0x70, 0x51, 0x66, 0x87, 0xd2, + 0xac, 0x97, 0x7c, 0x1e, 0x0f, 0xc2, 0x64, 0x18, 0x93, 0xc7, 0x08, 0xb5, 0x7f, 0x98, 0x47, 0xb1, 0x96, 0x4b, 0xae, + 0x9d, 0x5f, 0xfc, 0xcd, 0x27, 0xec, 0x75, 0xcf, 0x30, 0x58, 0x80, 0xb3, 0xb4, 0xbd, 0xca, 0xc4, 0x3b, 0xd9, 0x0a, + 0x8e, 0x83, 0x59, 0x94, 0xc3, 0xaa, 0x27, 0x47, 0x34, 0x17, 0xb9, 0xf6, 0x2e, 0x42, 0xe4, 0x20, 0xb3, 0xc7, 0x00, + 0xbb, 0x11, 0xbe, 0x0e, 0xad, 0xcd, 0xad, 0xae, 0x10, 0x7f, 0xa3, 0x44, 0xe2, 0x27, 0x29, 0x3f, 0x6e, 0xd6, 0x2a, + 0x57, 0x65, 0xf0, 0x58, 0x75, 0x33, 0x78, 0xa6, 0x7d, 0x8f, 0xb5, 0x7f, 0x6b, 0xbb, 0x39, 0xde, 0x7b, 0xf0, 0xa0, + 0xf5, 0xbf, 0xf5, 0x24, 0x84, 0xf6, 0xca, 0x49, 0xea, 0x8e, 0x1a, 0x3d, 0x33, 0x59, 0x23, 0x2a, 0x61, 0x6a, 0x77, + 0x2a, 0xc7, 0x40, 0x4d, 0x07, 0x70, 0x2d, 0x51, 0x13, 0xf4, 0xa4, 0x60, 0x63, 0x38, 0xe2, 0x2c, 0x0e, 0xda, 0x71, + 0x8c, 0xe2, 0xe5, 0x5c, 0x89, 0x97, 0xf3, 0x19, 0xe3, 0x00, 0xad, 0x05, 0x48, 0xf5, 0x1a, 0xf6, 0x33, 0x57, 0xb0, + 0xc0, 0xe6, 0xce, 0x77, 0x64, 0x81, 0x0c, 0x71, 0xb2, 0x39, 0x4e, 0xf6, 0xb8, 0xd6, 0x73, 0x2f, 0xf0, 0x71, 0x52, + 0x2f, 0xbd, 0xba, 0xca, 0x76, 0x5d, 0x2b, 0x56, 0x2e, 0x8a, 0xc1, 0x04, 0x82, 0xb2, 0x94, 0x8b, 0x62, 0x38, 0x59, + 0xd2, 0x1c, 0x7e, 0x2c, 0x1b, 0xe8, 0x10, 0xab, 0x41, 0x02, 0x97, 0xce, 0x1e, 0x03, 0xde, 0x50, 0x6a, 0x71, 0x37, + 0xd6, 0x91, 0x63, 0x1d, 0xc5, 0x61, 0x18, 0x03, 0xae, 0xac, 0x13, 0x78, 0xdf, 0x7f, 0x7d, 0x6c, 0x02, 0xb2, 0x6a, + 0x57, 0x78, 0x35, 0xca, 0x5d, 0x57, 0x1a, 0x7d, 0x49, 0xe9, 0x09, 0x2f, 0x78, 0x2a, 0xd9, 0xed, 0x7a, 0x06, 0xce, + 0x96, 0x78, 0x48, 0xbc, 0x63, 0x44, 0x2f, 0xa6, 0x8d, 0xcc, 0x9c, 0xc0, 0x99, 0xed, 0x2e, 0xdb, 0x98, 0x1f, 0x3b, + 0xc0, 0xc1, 0x22, 0x08, 0x89, 0x1b, 0xc2, 0x30, 0xb1, 0x59, 0x39, 0xd4, 0x42, 0xb8, 0xae, 0x85, 0xd7, 0x71, 0x5a, + 0xc6, 0xe0, 0x22, 0xad, 0x6d, 0x13, 0xef, 0xa1, 0xeb, 0x9e, 0x1f, 0x73, 0xab, 0x63, 0xb4, 0x85, 0xf4, 0xdb, 0xd1, + 0xe9, 0x03, 0x87, 0x01, 0x68, 0x7a, 0x30, 0xaf, 0xda, 0x67, 0x12, 0x37, 0xa7, 0x9d, 0x20, 0x24, 0x02, 0x51, 0x94, + 0xce, 0x08, 0xd3, 0xbf, 0xd7, 0x5c, 0x56, 0xd1, 0xea, 0x41, 0x9e, 0x39, 0xe4, 0x59, 0xe8, 0x6d, 0x0f, 0x5a, 0x35, + 0x77, 0x83, 0x71, 0xe2, 0x76, 0x7b, 0xe7, 0xff, 0x2d, 0xeb, 0xda, 0x6a, 0x8d, 0x78, 0xdc, 0xae, 0x7e, 0xd0, 0xd8, + 0xab, 0x3d, 0x15, 0x03, 0x66, 0x2d, 0xbd, 0x33, 0xaa, 0xe4, 0x45, 0xc6, 0x4b, 0x3c, 0xa9, 0xd6, 0x0d, 0x1f, 0xef, + 0x9b, 0x6c, 0x64, 0x1e, 0xc8, 0x14, 0x10, 0xcf, 0x6f, 0x52, 0xa3, 0x3e, 0x4e, 0x51, 0x02, 0xfe, 0x4e, 0xc7, 0x37, + 0xa2, 0x1f, 0xed, 0x8b, 0x4b, 0x5e, 0xbd, 0xbd, 0x11, 0xe6, 0xc5, 0x73, 0xab, 0xf3, 0xa7, 0xaf, 0x0b, 0x1f, 0x3a, + 0x1c, 0xb5, 0x77, 0x50, 0x64, 0xc9, 0xc4, 0x6c, 0x62, 0x64, 0x6d, 0x62, 0xfe, 0x51, 0xc1, 0xc5, 0x44, 0x15, 0x7a, + 0xd6, 0xd9, 0x13, 0xa6, 0x00, 0x7d, 0xe3, 0x18, 0x95, 0x8c, 0x61, 0xc1, 0x40, 0x9d, 0xa6, 0x84, 0xe8, 0xa1, 0x98, + 0x63, 0xbc, 0x62, 0x00, 0x85, 0x29, 0x14, 0x88, 0xa2, 0xb3, 0x0f, 0x07, 0x9a, 0xd0, 0xef, 0xdf, 0xa4, 0x3a, 0x03, + 0x2d, 0xeb, 0xa9, 0x04, 0x51, 0x1d, 0x44, 0x5b, 0xe5, 0x45, 0xf8, 0xe3, 0x8a, 0x96, 0x19, 0x5d, 0x09, 0x9a, 0x0a, + 0x9a, 0x64, 0xf4, 0x82, 0x2b, 0x51, 0xf1, 0x85, 0x60, 0x8a, 0xb6, 0x1b, 0xc2, 0xfe, 0xaf, 0x06, 0x5d, 0x6f, 0xc5, + 0x5a, 0x43, 0xbb, 0x13, 0x64, 0x84, 0x16, 0x4b, 0x1d, 0x84, 0x0c, 0x95, 0x93, 0xd0, 0xb5, 0x4a, 0xe3, 0x15, 0xb8, + 0x64, 0x9a, 0x8d, 0x56, 0x71, 0x19, 0x06, 0xf6, 0xab, 0xc0, 0x62, 0x72, 0x60, 0xd2, 0xe9, 0xe6, 0xfc, 0x99, 0xbc, + 0x5a, 0x4b, 0xc1, 0x45, 0xa5, 0x20, 0xfa, 0x0d, 0xee, 0xbb, 0x89, 0xab, 0xce, 0x9a, 0xb5, 0xd2, 0x87, 0xbe, 0xf5, + 0x59, 0x1b, 0xf7, 0x85, 0xc1, 0x31, 0xd8, 0xfb, 0x88, 0x18, 0x48, 0x83, 0x4a, 0xb7, 0x38, 0x34, 0x01, 0xba, 0x74, + 0x48, 0x21, 0x4b, 0xa6, 0x32, 0x55, 0x82, 0x8a, 0x6f, 0xfc, 0x5e, 0xca, 0x6a, 0xf4, 0xe7, 0x86, 0x17, 0x77, 0xa7, + 0x3c, 0xe7, 0x38, 0x46, 0x41, 0x12, 0x8b, 0xeb, 0xb8, 0x0c, 0x88, 0x6f, 0x79, 0x15, 0x1c, 0xa5, 0x26, 0x6c, 0xcc, + 0x5e, 0xd5, 0xa8, 0xf5, 0x92, 0xe8, 0x2b, 0xa3, 0x7c, 0x63, 0x30, 0x34, 0x11, 0x55, 0xd0, 0xf7, 0x5a, 0xdd, 0xd3, + 0xea, 0x86, 0x05, 0xc4, 0x5f, 0x28, 0xbd, 0x50, 0xeb, 0x75, 0x33, 0xe6, 0x86, 0x89, 0x10, 0x34, 0xfa, 0xaa, 0x5e, + 0xd6, 0x9e, 0x5b, 0x9a, 0x8a, 0x8c, 0x1b, 0x6d, 0x73, 0x7e, 0x09, 0x32, 0x3e, 0x67, 0x2e, 0x34, 0xa9, 0x6b, 0xaa, + 0xa0, 0x0a, 0xa3, 0xed, 0x6d, 0x23, 0x9d, 0xde, 0x81, 0x3b, 0x9b, 0x31, 0x3b, 0xd2, 0x2e, 0x8d, 0x35, 0x2d, 0x78, + 0xb9, 0x96, 0xa2, 0x84, 0x30, 0xce, 0xbd, 0x31, 0xbd, 0x8a, 0x33, 0x51, 0xc5, 0x99, 0x38, 0x29, 0xd7, 0x3c, 0xa9, + 0xde, 0xc3, 0x2d, 0x4e, 0x59, 0xdd, 0xd4, 0x25, 0x5c, 0xe9, 0x92, 0x03, 0x0c, 0xa6, 0xa6, 0xe2, 0x1e, 0x3b, 0x83, + 0x8b, 0xea, 0xf7, 0x68, 0x25, 0x31, 0x16, 0xaa, 0x2e, 0x3e, 0x3e, 0x2f, 0x65, 0xbe, 0xa9, 0x40, 0xbb, 0x7b, 0x51, + 0x45, 0x47, 0x4f, 0xd6, 0xb7, 0x53, 0x75, 0x83, 0x89, 0x9e, 0x1c, 0xad, 0x6f, 0x7b, 0xd9, 0xd5, 0x5a, 0x16, 0x55, + 0x2c, 0xaa, 0xa9, 0x42, 0x24, 0x4b, 0xe2, 0x3c, 0x09, 0x27, 0xe3, 0xf1, 0x17, 0x07, 0xc3, 0x03, 0xc8, 0x40, 0xa6, + 0x7f, 0x0d, 0x95, 0xcb, 0xd1, 0x70, 0x32, 0x1e, 0x4f, 0xa5, 0xba, 0xdb, 0x45, 0xa3, 0x49, 0x8d, 0xf5, 0x0c, 0x13, + 0x3d, 0x33, 0x23, 0x7e, 0xbb, 0x8e, 0x45, 0x0a, 0xf1, 0xeb, 0x74, 0xf1, 0x47, 0x4f, 0xc6, 0x8d, 0xf2, 0xed, 0xa7, + 0x4f, 0xeb, 0xdf, 0x6b, 0x13, 0xd6, 0xda, 0xb4, 0xfb, 0xd9, 0xef, 0xc7, 0x6a, 0xbe, 0x67, 0xc7, 0x87, 0xfa, 0xc7, + 0xef, 0x75, 0x3d, 0x7d, 0x5d, 0x84, 0x8b, 0x7f, 0x86, 0x6a, 0x3e, 0x4f, 0x8a, 0x22, 0xbe, 0xab, 0x21, 0x92, 0xa7, + 0x70, 0xde, 0x24, 0xd4, 0xdb, 0x06, 0xf4, 0x88, 0x4c, 0x2f, 0x04, 0x83, 0x6f, 0xde, 0x57, 0x61, 0xc0, 0xcb, 0xf5, + 0x90, 0x8b, 0x2a, 0xab, 0xee, 0x86, 0x98, 0x27, 0xc0, 0x4f, 0x0d, 0x6f, 0xf6, 0xac, 0x30, 0xc4, 0xe6, 0xa2, 0xe0, + 0xfc, 0x2f, 0x1e, 0x2a, 0xe3, 0xe8, 0x31, 0x1a, 0x47, 0x8f, 0xa9, 0x1a, 0x8c, 0xc9, 0xd7, 0x54, 0x77, 0x66, 0xf2, + 0x35, 0x98, 0x20, 0x65, 0xed, 0x6f, 0x94, 0x71, 0x62, 0x34, 0xa6, 0xd7, 0x2f, 0xf3, 0x6c, 0x0d, 0x4c, 0xf0, 0x4a, + 0xff, 0xa8, 0x09, 0x7d, 0xcf, 0xdb, 0xd9, 0x47, 0xa3, 0xd1, 0xb3, 0x82, 0x8e, 0x46, 0xa3, 0x8f, 0x59, 0x4d, 0xe8, + 0x5a, 0x74, 0xbc, 0x7f, 0xcf, 0xe9, 0xb9, 0x4c, 0xef, 0xa2, 0x20, 0xa0, 0xab, 0x2c, 0x4d, 0xb9, 0x50, 0x65, 0x9d, + 0xa6, 0xed, 0xbc, 0xaa, 0x85, 0x08, 0xfc, 0xa3, 0xdb, 0x88, 0x10, 0x44, 0x84, 0xbe, 0xdd, 0xeb, 0xd9, 0x68, 0x34, + 0x3a, 0x4d, 0x4d, 0xb5, 0x8e, 0x21, 0x7f, 0x8d, 0xe6, 0x03, 0xce, 0x2e, 0x1f, 0xac, 0x6f, 0x4c, 0xb4, 0x93, 0xc3, + 0xff, 0x1e, 0xce, 0x17, 0xe3, 0xe1, 0xb7, 0xa3, 0xe5, 0xe3, 0x43, 0x1a, 0x04, 0x3e, 0x68, 0x75, 0xa8, 0xad, 0x39, + 0xa6, 0xe5, 0xf1, 0x78, 0x4a, 0xca, 0x01, 0x7b, 0x62, 0x7d, 0x69, 0xbe, 0x78, 0x02, 0x48, 0xa4, 0x28, 0x42, 0x0d, + 0x9c, 0xf4, 0x0f, 0xaf, 0x22, 0xaf, 0x04, 0xe0, 0xa3, 0xd9, 0x48, 0x06, 0x46, 0x0b, 0x38, 0x8e, 0xa0, 0xbc, 0xda, + 0x9a, 0x46, 0xf4, 0x18, 0xcb, 0x4c, 0x54, 0xd0, 0xf1, 0xb4, 0xbc, 0xc9, 0xaa, 0x64, 0x85, 0x81, 0x8d, 0xe2, 0x92, + 0x07, 0x5f, 0x04, 0x51, 0xc9, 0x8e, 0x9e, 0x4e, 0x15, 0xbc, 0x2f, 0x26, 0xa5, 0xfc, 0x12, 0x12, 0xbf, 0x1d, 0x23, + 0x04, 0x2a, 0xd1, 0x1e, 0x8b, 0x58, 0xe3, 0xcb, 0x5c, 0xc6, 0xe0, 0xc1, 0x59, 0x6a, 0x9e, 0xc5, 0x9e, 0x04, 0xd6, + 0xfe, 0xa2, 0xd5, 0x1c, 0x09, 0xcd, 0x09, 0x25, 0x93, 0x87, 0x25, 0x95, 0x5f, 0x4c, 0xd0, 0x2b, 0x08, 0xdc, 0xaa, + 0x23, 0x38, 0xee, 0xac, 0x65, 0x83, 0x5e, 0x3e, 0x29, 0x3b, 0x5c, 0xfc, 0xef, 0x92, 0x2e, 0x07, 0x87, 0x6e, 0x68, + 0xde, 0x6a, 0xf7, 0xd5, 0x0a, 0x19, 0xa5, 0x2a, 0x7c, 0x96, 0x12, 0x6b, 0x7c, 0xca, 0xd9, 0x6c, 0x6b, 0xba, 0x33, + 0xaa, 0x8a, 0xec, 0x2a, 0x24, 0xba, 0x57, 0x0e, 0x14, 0x33, 0x88, 0xb2, 0x11, 0xae, 0x1f, 0xb0, 0x16, 0xf1, 0x3a, + 0x79, 0xcd, 0x8b, 0x2a, 0x4b, 0xd4, 0xfb, 0xeb, 0xc6, 0x7b, 0xa0, 0x06, 0xaa, 0x41, 0xef, 0x0a, 0x06, 0xf3, 0xfc, + 0xb6, 0x00, 0xd0, 0xce, 0x92, 0x17, 0xd7, 0xdc, 0xa7, 0x1b, 0x41, 0x50, 0xbb, 0x66, 0x5e, 0x36, 0x82, 0x4d, 0xc0, + 0x57, 0xef, 0x0a, 0xc0, 0xdc, 0x08, 0x41, 0x6a, 0x0a, 0xa1, 0x70, 0xe0, 0x02, 0x5f, 0x55, 0x45, 0x76, 0xbe, 0xa9, + 0x38, 0x06, 0xfb, 0xf0, 0xe2, 0x26, 0x2a, 0x27, 0x3c, 0x1e, 0x06, 0xf8, 0x23, 0xa0, 0x2a, 0xe0, 0x86, 0xf1, 0xb0, + 0x83, 0x17, 0xea, 0x97, 0x7b, 0xa3, 0xf6, 0x08, 0x7b, 0x9d, 0x86, 0x10, 0x5c, 0x07, 0x1f, 0x02, 0x58, 0x52, 0x84, + 0xbe, 0xc5, 0x53, 0x35, 0x0c, 0x2e, 0xf2, 0x6c, 0xad, 0x93, 0xaa, 0x51, 0x47, 0xf3, 0xa1, 0xd4, 0x8e, 0xe4, 0x80, + 0x7a, 0xe9, 0x31, 0xa6, 0x17, 0x2a, 0x5d, 0x15, 0xe5, 0x8c, 0x50, 0xde, 0xe9, 0x89, 0x71, 0x61, 0xfa, 0x38, 0x44, + 0x7e, 0x79, 0x57, 0xa8, 0xd0, 0x2f, 0x7c, 0x09, 0xe0, 0x57, 0x70, 0xbb, 0xdf, 0x8f, 0xef, 0xa2, 0xb2, 0x9f, 0x71, + 0x76, 0xf8, 0xdf, 0x8b, 0x78, 0xf8, 0xd7, 0x78, 0xf8, 0xed, 0x72, 0x10, 0x0e, 0xed, 0x4f, 0xf2, 0xf8, 0xd1, 0x21, + 0x7d, 0xc9, 0x2d, 0x57, 0x02, 0x0b, 0xbf, 0x11, 0xdc, 0x46, 0xad, 0x84, 0x20, 0x0a, 0xf0, 0x46, 0xe1, 0x56, 0xe3, + 0x04, 0x00, 0xfe, 0x82, 0xff, 0x0a, 0xd0, 0x48, 0xc8, 0x5d, 0x34, 0x40, 0x3f, 0xa0, 0x7e, 0xcf, 0xbe, 0x6a, 0x18, + 0xc8, 0x81, 0x78, 0x42, 0xc5, 0x40, 0x21, 0xba, 0x8c, 0x89, 0x82, 0xfd, 0xb5, 0xd9, 0x77, 0xbb, 0x5e, 0x5b, 0xf2, + 0x83, 0x5f, 0xfa, 0x99, 0x26, 0x66, 0xde, 0xe1, 0x86, 0xb2, 0x96, 0xeb, 0x10, 0xb1, 0xf1, 0xf4, 0xaf, 0x9c, 0x41, + 0xac, 0xc9, 0xeb, 0x0c, 0x7c, 0x18, 0xec, 0x17, 0xe3, 0x39, 0xb0, 0x0d, 0x70, 0xc7, 0x29, 0xf8, 0x45, 0x06, 0x6e, + 0xcd, 0x22, 0xc6, 0x0b, 0xb6, 0x5d, 0x12, 0xfd, 0x7e, 0x2f, 0xcf, 0xc2, 0x5c, 0xe3, 0x2c, 0xe7, 0xb5, 0x11, 0xbb, + 0xa3, 0x4e, 0x18, 0xc4, 0xed, 0x7a, 0x08, 0x86, 0x6a, 0x08, 0x8a, 0x8e, 0xb6, 0xb8, 0x7a, 0x6d, 0x3d, 0x85, 0xe9, + 0xad, 0xaa, 0xaf, 0x18, 0xfd, 0x21, 0x33, 0x81, 0x85, 0xb4, 0x6b, 0x8e, 0x75, 0xcd, 0x31, 0xd2, 0x9e, 0x7e, 0x5f, + 0x34, 0xc8, 0x4f, 0x67, 0xe1, 0x41, 0xa0, 0x4a, 0x95, 0x7b, 0x65, 0x51, 0x6e, 0x4b, 0xf3, 0xc6, 0xb0, 0xa6, 0x79, + 0x66, 0xe3, 0xba, 0xcc, 0x7b, 0xbd, 0x30, 0x44, 0x87, 0x46, 0x2c, 0x15, 0x6b, 0x83, 0xf0, 0x3e, 0x26, 0x61, 0x74, + 0x05, 0xb2, 0xba, 0xf0, 0x8c, 0x13, 0xe4, 0xcb, 0xc0, 0x64, 0x4d, 0x55, 0xeb, 0xe5, 0x84, 0xc7, 0x46, 0xbe, 0x6c, + 0x04, 0x0d, 0xf2, 0x92, 0xa2, 0xde, 0xc4, 0xed, 0xd8, 0x47, 0x2d, 0xa4, 0xc6, 0x6d, 0x3d, 0xed, 0x69, 0x52, 0xd1, + 0x63, 0xbd, 0x4a, 0xfd, 0x02, 0xcb, 0x02, 0x4b, 0x3e, 0x08, 0xed, 0x69, 0x5a, 0x81, 0x19, 0xae, 0x6d, 0x06, 0x43, + 0x3f, 0x1c, 0xda, 0x22, 0x74, 0x46, 0x6d, 0x4b, 0x08, 0xdb, 0x36, 0x08, 0x2b, 0xef, 0x89, 0x7c, 0xf1, 0xc4, 0x63, + 0x84, 0x43, 0x6e, 0x36, 0xb3, 0x68, 0x60, 0x98, 0x5f, 0xc9, 0x66, 0xf3, 0x74, 0x73, 0xbd, 0xa8, 0x98, 0x02, 0xb6, + 0xdb, 0x5a, 0x10, 0xfc, 0xfb, 0x31, 0x9b, 0xe3, 0xdf, 0xac, 0xdf, 0xef, 0x85, 0xf8, 0x8b, 0x63, 0xf0, 0x9e, 0x85, + 0x58, 0xb2, 0x8f, 0x20, 0x53, 0x21, 0x11, 0xa6, 0x2a, 0xe3, 0x37, 0x56, 0x81, 0x05, 0x9c, 0xf9, 0x40, 0xe5, 0xc2, + 0x4c, 0xf6, 0xf2, 0xe2, 0x1a, 0x72, 0xd2, 0x1a, 0xa7, 0x6c, 0x94, 0x25, 0xca, 0x75, 0x21, 0x1b, 0xc5, 0x79, 0x16, + 0x97, 0xbc, 0xdc, 0xed, 0xf4, 0xe1, 0x98, 0x14, 0x1c, 0xd8, 0x53, 0x45, 0xa5, 0x4a, 0xd6, 0x91, 0xea, 0xc6, 0x5f, + 0x86, 0x05, 0xee, 0x53, 0xbe, 0x28, 0x0c, 0x8d, 0x38, 0x80, 0xcb, 0x3b, 0x53, 0xb7, 0xd2, 0x5e, 0x58, 0x40, 0xf3, + 0x4a, 0x42, 0xb6, 0x98, 0xea, 0x59, 0xb4, 0xc6, 0x4c, 0x2c, 0x8a, 0x25, 0x84, 0x91, 0x29, 0x96, 0x60, 0x33, 0xc5, + 0x05, 0x78, 0x91, 0xc4, 0x00, 0x13, 0x17, 0x93, 0x29, 0xc4, 0x33, 0x57, 0xe5, 0xc4, 0x4b, 0x73, 0xbf, 0x4c, 0x1c, + 0x52, 0x06, 0xbc, 0xaa, 0x0d, 0x9a, 0x98, 0x6d, 0x38, 0xea, 0x04, 0x39, 0x31, 0xf9, 0xfd, 0x54, 0x41, 0x88, 0x3b, + 0x71, 0x24, 0x5c, 0x56, 0x6c, 0x17, 0x5e, 0x74, 0x20, 0xc6, 0xa8, 0xc1, 0x29, 0x3f, 0x31, 0x38, 0x1a, 0x83, 0x73, + 0xeb, 0x9d, 0x20, 0x45, 0x18, 0x93, 0xad, 0x64, 0x57, 0x32, 0x14, 0x8b, 0x78, 0x09, 0xca, 0xba, 0x78, 0x09, 0x96, + 0x35, 0xc6, 0x00, 0x13, 0xe4, 0x55, 0xdc, 0x0b, 0xfd, 0x44, 0x71, 0x85, 0x48, 0xcf, 0xca, 0xf5, 0x51, 0xd1, 0x0e, + 0x7d, 0x81, 0xd7, 0x7b, 0x65, 0x8e, 0x9b, 0xf5, 0x58, 0x20, 0xb1, 0x21, 0x60, 0x6c, 0xa4, 0xd3, 0x54, 0x6b, 0xdd, + 0x1b, 0x33, 0x0f, 0x7c, 0x9a, 0x8d, 0x84, 0xac, 0xce, 0x2e, 0x40, 0x84, 0xe2, 0xa3, 0xc1, 0x23, 0xbf, 0x88, 0x3b, + 0xcb, 0xbc, 0xb5, 0x2d, 0x2a, 0xd9, 0x6c, 0x0b, 0x20, 0x7d, 0x3a, 0x5a, 0x94, 0x92, 0x53, 0x94, 0xa4, 0x76, 0x9b, + 0x02, 0x56, 0x92, 0xbf, 0x80, 0x21, 0xd8, 0xd8, 0x81, 0x70, 0x3a, 0x45, 0x88, 0x4f, 0x34, 0x45, 0x64, 0xc5, 0xb0, + 0xa4, 0x38, 0xb6, 0x25, 0xa2, 0x7e, 0xda, 0xb2, 0xec, 0x60, 0x98, 0xa8, 0xb8, 0xcf, 0x53, 0x8f, 0x12, 0x05, 0x01, + 0xd5, 0x43, 0x0e, 0x12, 0x5b, 0xdb, 0x40, 0x78, 0x40, 0x1e, 0xd1, 0x1b, 0xeb, 0xef, 0xb3, 0xce, 0xb3, 0x0b, 0xcd, + 0x51, 0xb9, 0xde, 0x15, 0x66, 0x8c, 0xf0, 0x24, 0x33, 0x30, 0xf9, 0xde, 0x79, 0x66, 0xd4, 0x14, 0x3d, 0x0f, 0x9f, + 0xec, 0x04, 0x23, 0xd2, 0xdd, 0x33, 0xe8, 0x26, 0x78, 0x55, 0x87, 0x8d, 0x76, 0xa5, 0x20, 0x24, 0x4c, 0x2d, 0x9a, + 0x98, 0xf5, 0xac, 0x01, 0xf5, 0x6e, 0xd7, 0xd3, 0x73, 0x75, 0xff, 0xdc, 0xed, 0x76, 0x3d, 0xec, 0xd6, 0xf3, 0xb4, + 0xdb, 0x0a, 0xbc, 0x52, 0x1f, 0xb4, 0xc7, 0x9f, 0xbb, 0xf1, 0xe7, 0x06, 0xc9, 0xa3, 0x74, 0x34, 0xd3, 0xd6, 0x07, + 0xe1, 0x70, 0xd3, 0xbb, 0x46, 0x93, 0xbe, 0xcf, 0x42, 0x49, 0xd7, 0xa2, 0x51, 0x5d, 0xed, 0x4c, 0x2a, 0x1f, 0x5c, + 0xff, 0x0f, 0xaf, 0x02, 0x3c, 0xe2, 0xd4, 0xce, 0xbe, 0xb7, 0x41, 0x45, 0xa3, 0x2d, 0x1c, 0x29, 0x42, 0x0f, 0x48, + 0xc2, 0x7d, 0x2d, 0x6b, 0x71, 0x9b, 0xa7, 0xd9, 0xc3, 0xf4, 0xe9, 0x55, 0xea, 0x5b, 0xdd, 0xbb, 0x65, 0x96, 0x99, + 0x03, 0xaf, 0xa2, 0x38, 0xa0, 0x51, 0x17, 0xed, 0xbb, 0xca, 0xca, 0x12, 0xbc, 0x3c, 0xe0, 0xfa, 0x7c, 0xca, 0x7d, + 0xb8, 0xb9, 0xcb, 0xaa, 0xb9, 0x49, 0x4f, 0xb3, 0x45, 0xb6, 0xdc, 0xed, 0x42, 0xfc, 0xdb, 0xd5, 0x22, 0x47, 0x93, + 0x17, 0xa0, 0xc3, 0xc3, 0xc8, 0x3d, 0x4c, 0x37, 0xce, 0xdb, 0xfc, 0x7f, 0x89, 0x86, 0x93, 0xc0, 0x09, 0xd0, 0x8b, + 0xf9, 0x23, 0x90, 0xc1, 0x18, 0xa7, 0x7e, 0x31, 0xd7, 0x6b, 0x06, 0xa2, 0x6f, 0x89, 0x08, 0x70, 0x74, 0xb1, 0x91, + 0x68, 0x64, 0xc1, 0x49, 0x4d, 0xc0, 0x62, 0xd3, 0x96, 0xf7, 0xc1, 0xd2, 0xb6, 0xaa, 0xb8, 0xf3, 0x96, 0x34, 0xc7, + 0x75, 0xe0, 0x6c, 0xfb, 0xcd, 0x10, 0x9b, 0xb2, 0xab, 0x25, 0x72, 0xbf, 0xbc, 0xa6, 0xbd, 0x71, 0x9d, 0xc0, 0xac, + 0x6d, 0x6b, 0xcb, 0xf8, 0xd9, 0xd2, 0x7f, 0xd2, 0x83, 0xab, 0x8c, 0x9f, 0x16, 0xc6, 0x2a, 0xc1, 0xee, 0x1b, 0xcf, + 0x77, 0x00, 0xc2, 0xb1, 0xf9, 0xf4, 0xf8, 0x34, 0xf3, 0xe8, 0x31, 0x10, 0x1d, 0xf3, 0x51, 0xe9, 0x3e, 0xb2, 0xbb, + 0xd7, 0x0f, 0x80, 0xb7, 0xa8, 0xda, 0x05, 0x2d, 0xca, 0x25, 0x04, 0x12, 0xf5, 0xca, 0x2b, 0x2c, 0x9f, 0x19, 0xb3, + 0x4b, 0x20, 0x43, 0x05, 0x01, 0x9b, 0xa4, 0xae, 0x73, 0x21, 0x56, 0x1d, 0x56, 0xe6, 0x23, 0x09, 0x9b, 0x85, 0x68, + 0xce, 0x19, 0xcc, 0x83, 0xff, 0x0a, 0x06, 0xe5, 0x20, 0x88, 0x82, 0x28, 0x08, 0xc8, 0xa0, 0x80, 0x5f, 0x88, 0x33, + 0x46, 0x30, 0x46, 0x09, 0x74, 0xf8, 0x1d, 0x67, 0x3e, 0x23, 0xf2, 0xa2, 0x11, 0xc6, 0xd2, 0x0d, 0xc0, 0xb9, 0x94, + 0x39, 0x8f, 0xd1, 0xc7, 0xe2, 0x1d, 0x67, 0x19, 0xa1, 0xef, 0xbc, 0x53, 0xf9, 0x11, 0x6f, 0x04, 0xb7, 0xdb, 0x1f, + 0xb6, 0x97, 0x3c, 0xcc, 0x68, 0x6f, 0x4c, 0xdf, 0x71, 0x12, 0x65, 0x0d, 0xe7, 0x61, 0x0e, 0x3d, 0xab, 0x2c, 0x6b, + 0x45, 0x0d, 0xb9, 0x41, 0xb1, 0x2e, 0xb2, 0x4c, 0x4e, 0x86, 0xab, 0xe6, 0x54, 0xe0, 0xba, 0xb3, 0xeb, 0x05, 0x24, + 0x65, 0x42, 0xb3, 0x74, 0x36, 0x7c, 0xb5, 0x6d, 0xd9, 0xf3, 0xd6, 0x29, 0xe4, 0x35, 0x44, 0x45, 0x3f, 0x74, 0x04, + 0xd4, 0xd0, 0x8a, 0xcb, 0x0a, 0x5c, 0x76, 0x4d, 0x7b, 0xb8, 0x69, 0x8f, 0x69, 0xc6, 0x07, 0x88, 0x11, 0xc7, 0xb1, + 0x65, 0x60, 0x37, 0xe1, 0xf0, 0x6c, 0x9c, 0xef, 0xcb, 0x3e, 0xbd, 0x75, 0xb5, 0x78, 0x84, 0xb5, 0xe7, 0xad, 0x90, + 0x10, 0x20, 0x2d, 0x4d, 0xa5, 0xbb, 0x5d, 0x10, 0xc0, 0x00, 0xf7, 0xfb, 0x3d, 0xe0, 0x5a, 0x0d, 0x3b, 0x69, 0x6e, + 0xcd, 0x96, 0xd8, 0x2b, 0x0a, 0x8f, 0x81, 0x28, 0x35, 0xff, 0x19, 0x04, 0x14, 0xcf, 0xdd, 0x10, 0xec, 0x2b, 0xd9, + 0x6c, 0x5b, 0xf4, 0xfb, 0xcf, 0x0b, 0x7c, 0x40, 0x39, 0x28, 0x88, 0x75, 0x75, 0xdc, 0x0a, 0xc3, 0x3e, 0xa9, 0x0f, + 0x71, 0x2c, 0xf2, 0x2c, 0x74, 0x84, 0xa5, 0x32, 0x84, 0x85, 0x2b, 0x46, 0x3a, 0x88, 0x83, 0x9a, 0x74, 0x0e, 0x56, + 0xe5, 0x82, 0x0d, 0xf7, 0x7a, 0x7f, 0x01, 0x2c, 0x78, 0xe6, 0x0d, 0xcb, 0x7b, 0x0f, 0x00, 0xac, 0xd7, 0xc3, 0x85, + 0xe2, 0x5e, 0xbe, 0x6c, 0xa0, 0x4f, 0xe2, 0x4b, 0xcb, 0xae, 0xcf, 0xb5, 0xac, 0x64, 0x34, 0x1a, 0x55, 0xb5, 0x92, + 0x7c, 0x38, 0xf2, 0xd2, 0x42, 0xad, 0x94, 0x71, 0xca, 0x53, 0xb0, 0xf4, 0x36, 0x94, 0x6e, 0xb1, 0xa4, 0x6b, 0x2e, + 0x52, 0xf5, 0xd3, 0x43, 0x9b, 0x6c, 0x10, 0xd7, 0xac, 0xa9, 0xb3, 0xb0, 0xc3, 0x0f, 0x01, 0x1f, 0xed, 0xc3, 0xdc, + 0xa5, 0x6b, 0x58, 0x5a, 0x10, 0x47, 0xc6, 0x05, 0x0f, 0x5d, 0x1e, 0xc0, 0xfa, 0x33, 0x87, 0x24, 0x7e, 0x0a, 0x3f, + 0xe7, 0x26, 0xad, 0xe3, 0x33, 0x9c, 0xcd, 0xa8, 0x54, 0x37, 0x82, 0xf6, 0x6b, 0x48, 0x24, 0x06, 0xd9, 0xb8, 0xc1, + 0x50, 0xb4, 0xee, 0x36, 0x70, 0xe5, 0xb7, 0xf4, 0xce, 0xa7, 0x41, 0x80, 0x6d, 0x8d, 0xc5, 0x00, 0x60, 0x28, 0xfe, + 0x40, 0x55, 0x8d, 0xb9, 0xa2, 0x98, 0x86, 0xa9, 0x44, 0x7b, 0xc7, 0x71, 0x1d, 0x35, 0xae, 0xb2, 0x82, 0x95, 0xd6, + 0x96, 0xd7, 0xbd, 0xa5, 0x85, 0x2d, 0x01, 0xd5, 0x60, 0xb8, 0x13, 0xc0, 0x67, 0x44, 0xaa, 0x03, 0x41, 0x76, 0x1f, + 0x1c, 0x34, 0x67, 0x09, 0x9e, 0x87, 0x21, 0xfc, 0x81, 0x85, 0x03, 0xcb, 0x52, 0xf5, 0x73, 0x35, 0x8d, 0xe1, 0xdc, + 0xcd, 0xd5, 0x0e, 0x9f, 0xaf, 0x40, 0x91, 0xa7, 0xe6, 0xd4, 0x5c, 0xbe, 0xf2, 0xc6, 0x7e, 0x8f, 0x09, 0xe6, 0x31, + 0xb3, 0x0d, 0xbf, 0xf5, 0x74, 0x5b, 0x5f, 0x58, 0x37, 0x70, 0xd2, 0x5e, 0x38, 0xed, 0xc5, 0x76, 0x65, 0x20, 0xee, + 0xea, 0x86, 0x10, 0xe1, 0x95, 0x26, 0x16, 0x59, 0x43, 0xa6, 0x63, 0xb1, 0x31, 0x54, 0x9b, 0x8a, 0x67, 0x5a, 0x21, + 0x5e, 0x4e, 0xd5, 0x85, 0xa9, 0x95, 0xca, 0x84, 0x41, 0x98, 0x29, 0x61, 0x51, 0x65, 0xe0, 0xb3, 0x5f, 0x21, 0xc5, + 0xb5, 0xf5, 0xbc, 0xc1, 0xe5, 0x9b, 0x99, 0x36, 0xdb, 0x4f, 0x5f, 0xe6, 0xf1, 0xe5, 0x6e, 0x17, 0x76, 0xbf, 0x00, + 0xf3, 0xcb, 0x52, 0x69, 0xd4, 0xc0, 0xe9, 0x21, 0x44, 0x3f, 0xe7, 0x7b, 0x72, 0x4e, 0x1c, 0x27, 0xd7, 0x6e, 0xde, + 0x7c, 0x2f, 0xc5, 0x08, 0x2c, 0xe0, 0xc4, 0x45, 0x3a, 0xd0, 0x52, 0xc1, 0x69, 0xcb, 0x78, 0x6f, 0xd3, 0x3b, 0x4a, + 0x85, 0x57, 0x0b, 0x4d, 0x42, 0x2a, 0x77, 0x2f, 0xb1, 0xa3, 0x06, 0x9c, 0x93, 0xba, 0x83, 0x80, 0x93, 0x9a, 0x6e, + 0xac, 0x55, 0x9c, 0x9a, 0x04, 0xef, 0x95, 0x1e, 0xba, 0x44, 0x3b, 0x71, 0xbb, 0x6d, 0x55, 0xb6, 0x50, 0x1f, 0x0f, + 0x72, 0x96, 0xa8, 0xe3, 0x01, 0x85, 0x2e, 0xea, 0x68, 0xc8, 0x97, 0xa4, 0xd0, 0x2b, 0x47, 0xab, 0x56, 0xf7, 0x25, + 0x03, 0xa5, 0x5a, 0x05, 0x79, 0x4d, 0xac, 0xbb, 0x56, 0xd6, 0x58, 0x5c, 0x39, 0x21, 0x85, 0x4d, 0xf8, 0xdc, 0x52, + 0x2c, 0xac, 0x60, 0x6f, 0x4c, 0x7d, 0xe1, 0x12, 0xa1, 0xed, 0x6e, 0x43, 0x4c, 0x32, 0x58, 0x37, 0xbb, 0xdd, 0xab, + 0x22, 0x5c, 0x64, 0x4b, 0x2a, 0x47, 0x59, 0x8a, 0x10, 0x62, 0xc6, 0x43, 0xd7, 0x76, 0xc1, 0x4c, 0x0c, 0x75, 0xed, + 0xf1, 0x92, 0x4c, 0xb1, 0x36, 0x49, 0x8e, 0xe2, 0x73, 0x59, 0xa8, 0xb5, 0x46, 0x08, 0x1e, 0xee, 0x7f, 0xa4, 0x10, + 0xc3, 0xcd, 0xac, 0xbb, 0x5f, 0xf7, 0x6e, 0x88, 0x7f, 0x40, 0x20, 0x81, 0x92, 0xbd, 0x2a, 0x46, 0xe7, 0x99, 0x48, + 0x71, 0xa7, 0xaa, 0xa8, 0xb8, 0x6a, 0x1d, 0x34, 0x5b, 0x6e, 0xef, 0xc5, 0x96, 0x28, 0x40, 0x5c, 0x63, 0xa1, 0x19, + 0xcf, 0xca, 0x59, 0x8a, 0x64, 0x14, 0x1b, 0x12, 0x95, 0x5e, 0x54, 0x74, 0x9f, 0xa7, 0x31, 0x3d, 0x74, 0x6b, 0x10, + 0x5c, 0x35, 0xf7, 0x36, 0xd2, 0x62, 0x49, 0x88, 0x9a, 0x00, 0x09, 0x1b, 0xd5, 0x9c, 0x5a, 0x97, 0xe2, 0x61, 0x56, + 0xf9, 0x4c, 0x1f, 0xc4, 0x97, 0x02, 0x78, 0x58, 0x6f, 0x7b, 0x5f, 0x09, 0x8f, 0xb5, 0xc1, 0xb7, 0xbb, 0xdd, 0xa5, + 0x58, 0x04, 0x81, 0xc7, 0x68, 0x7e, 0xa7, 0x24, 0xe6, 0xbd, 0x31, 0x85, 0x15, 0xef, 0xbb, 0xb4, 0x75, 0x93, 0x5a, + 0x6b, 0x81, 0xba, 0xc7, 0xf5, 0x01, 0xcf, 0x53, 0xe2, 0x68, 0x47, 0xe5, 0x54, 0x5a, 0x5d, 0x39, 0x76, 0x45, 0x60, + 0x60, 0xe8, 0x1f, 0x52, 0xb6, 0x05, 0x73, 0x3c, 0xb0, 0xb6, 0x41, 0x3f, 0x25, 0xa5, 0x85, 0x19, 0xa3, 0x31, 0x8b, + 0xdc, 0x54, 0xd1, 0x11, 0xd7, 0xd1, 0xdb, 0x79, 0xf4, 0xb7, 0xa7, 0x63, 0x5a, 0xc4, 0x22, 0x95, 0x57, 0xa0, 0x82, + 0x00, 0x65, 0x08, 0x1a, 0xfe, 0x6b, 0x6a, 0x00, 0x1a, 0x04, 0x37, 0x00, 0xff, 0xe8, 0x74, 0x1a, 0xb4, 0x35, 0xf9, + 0x98, 0xa4, 0xaa, 0xc8, 0x79, 0x1b, 0xca, 0x4c, 0x25, 0x87, 0xe4, 0x71, 0x09, 0x78, 0x8e, 0xd8, 0x2c, 0x65, 0x73, + 0xa1, 0x36, 0x9b, 0x7a, 0xad, 0xd8, 0x91, 0xdb, 0x46, 0xd1, 0x66, 0x2d, 0x6a, 0x3b, 0x89, 0xc5, 0x72, 0x7a, 0x6b, + 0x85, 0x81, 0x53, 0xd3, 0x9a, 0x9b, 0x3d, 0xe8, 0x34, 0x5b, 0x9f, 0xc9, 0x4d, 0x80, 0x38, 0xc0, 0x70, 0xdd, 0x2e, + 0x6e, 0x96, 0x84, 0xde, 0xb2, 0x5b, 0x2b, 0x56, 0xbd, 0xb1, 0x72, 0x11, 0x93, 0x76, 0x33, 0x98, 0xc0, 0x65, 0x9c, + 0x15, 0xf6, 0x85, 0x56, 0x37, 0x14, 0x1d, 0x6d, 0x93, 0xf6, 0xf3, 0x8e, 0x76, 0xc3, 0x05, 0xdf, 0x8a, 0x75, 0x9c, + 0x1b, 0xd2, 0x54, 0xa1, 0x47, 0x07, 0x7a, 0x3b, 0x04, 0x34, 0x67, 0x63, 0xba, 0xa2, 0x29, 0x5e, 0xa0, 0xe9, 0x06, + 0xcc, 0x52, 0x2e, 0xa0, 0xaf, 0xdd, 0x3e, 0xc9, 0x17, 0xaa, 0x27, 0xc2, 0x5b, 0xa2, 0xe0, 0xcb, 0x91, 0x82, 0x57, + 0x56, 0xce, 0x63, 0x33, 0x87, 0x80, 0xc7, 0xa2, 0x4a, 0xf4, 0x4e, 0x8a, 0x4b, 0x50, 0xa6, 0xc2, 0x11, 0x68, 0xaa, + 0x46, 0x2c, 0xe1, 0x00, 0xb7, 0x17, 0x4f, 0x03, 0x42, 0x41, 0xaa, 0xbb, 0xb1, 0x2b, 0xf2, 0x96, 0xcd, 0xb6, 0xb7, + 0x60, 0x16, 0x5b, 0x6d, 0xca, 0xd6, 0x57, 0x36, 0xd9, 0x7d, 0x5c, 0x13, 0x6c, 0xbb, 0xb7, 0x41, 0xc2, 0x5b, 0x7a, + 0x43, 0xb6, 0x37, 0xfd, 0x7e, 0x08, 0xfd, 0x21, 0x54, 0x77, 0xe8, 0xb6, 0xb3, 0x43, 0xb7, 0x3e, 0xf3, 0x6b, 0xf5, + 0x7c, 0xca, 0x1b, 0xe2, 0x03, 0x9a, 0x68, 0xd1, 0x75, 0x7c, 0x07, 0x9b, 0x3a, 0xaa, 0xa8, 0xaa, 0x3c, 0x4a, 0x28, + 0xa8, 0x80, 0x33, 0x5e, 0x9e, 0x72, 0x8c, 0x6d, 0xaa, 0x9f, 0xde, 0x69, 0x5e, 0x6d, 0x63, 0xd6, 0x66, 0xb9, 0x39, + 0x07, 0x8b, 0x80, 0x73, 0x1e, 0x5d, 0x69, 0x5a, 0x72, 0xe9, 0x31, 0xf5, 0x67, 0x38, 0x2a, 0xc1, 0x45, 0x9c, 0xe5, + 0x3c, 0x0d, 0xe8, 0x45, 0xb3, 0xff, 0xa1, 0xb6, 0x95, 0x5a, 0x35, 0xce, 0xdc, 0xeb, 0x90, 0x6c, 0xff, 0xc7, 0x06, + 0xea, 0x75, 0x88, 0x11, 0x51, 0xcd, 0x82, 0x7e, 0xcb, 0x20, 0x36, 0x66, 0x50, 0x6e, 0x92, 0x84, 0x97, 0x65, 0x60, + 0x94, 0x5a, 0x1b, 0xb6, 0x31, 0xe7, 0xd9, 0x23, 0x36, 0x7b, 0xd4, 0x63, 0xec, 0x96, 0xd0, 0x44, 0xeb, 0x84, 0x4c, + 0x8d, 0x91, 0xa7, 0x05, 0xd2, 0x1d, 0x8a, 0xb2, 0x8b, 0xf0, 0x2d, 0x0a, 0x59, 0xda, 0xfb, 0xdc, 0x9c, 0xc8, 0xea, + 0x1b, 0x6d, 0x74, 0x11, 0xa9, 0x44, 0x90, 0x8d, 0xdf, 0x20, 0x60, 0x2f, 0x34, 0x3b, 0x20, 0xdb, 0x15, 0x3b, 0xa5, + 0x67, 0xd6, 0x04, 0x06, 0x5e, 0xbf, 0x55, 0x89, 0x66, 0x94, 0x15, 0xd1, 0x55, 0x46, 0x2e, 0x77, 0x21, 0x89, 0xce, + 0x42, 0xe2, 0xe7, 0x86, 0xa5, 0x75, 0x1d, 0xa2, 0x98, 0xd9, 0x6c, 0x78, 0xd5, 0xdd, 0x47, 0x8d, 0x6d, 0x65, 0x7c, + 0xaa, 0x6f, 0x6d, 0x1a, 0x99, 0x42, 0x5f, 0x87, 0x93, 0x7e, 0x1f, 0xfe, 0x6a, 0xfa, 0x81, 0xb7, 0x14, 0xfc, 0xc5, + 0x1e, 0x91, 0x3a, 0x61, 0x01, 0xc0, 0x11, 0xe6, 0xbc, 0x6a, 0x4e, 0xe0, 0x23, 0x36, 0xdb, 0x3e, 0x0a, 0x4f, 0x1b, + 0x33, 0x77, 0x17, 0xe2, 0xa5, 0x2a, 0xe9, 0x79, 0xf3, 0x64, 0x06, 0x62, 0x1d, 0x9a, 0xfd, 0x7a, 0xcb, 0xac, 0x3e, + 0x01, 0x88, 0xd4, 0xad, 0x75, 0x28, 0xc5, 0x8f, 0x4d, 0x97, 0xc9, 0x36, 0x65, 0x6d, 0x26, 0x4a, 0xa9, 0x48, 0x9a, + 0x8b, 0x00, 0xfa, 0x0d, 0xc3, 0x51, 0x03, 0xbc, 0xb7, 0x1e, 0x7b, 0x33, 0x34, 0xde, 0x98, 0x1a, 0x7a, 0xb6, 0xd5, + 0xcb, 0xdb, 0x51, 0x08, 0x33, 0x16, 0xd1, 0xad, 0x3b, 0x16, 0xc3, 0x53, 0xfa, 0x16, 0x2a, 0x7c, 0x1d, 0x62, 0x34, + 0x5d, 0x52, 0xd7, 0xd3, 0x8d, 0xda, 0x4a, 0x37, 0x84, 0xe6, 0x18, 0xc5, 0xc7, 0x6b, 0xdb, 0x1d, 0x35, 0x42, 0x7b, + 0x42, 0x79, 0x78, 0x4b, 0x2b, 0x7a, 0x63, 0x59, 0x04, 0x27, 0x3f, 0xf6, 0xf2, 0x13, 0x7a, 0xee, 0x09, 0x4c, 0x8a, + 0xb6, 0x06, 0xf0, 0x07, 0xd4, 0x0f, 0x67, 0xf5, 0xd4, 0x4a, 0x39, 0x3c, 0x85, 0x2f, 0xd9, 0x82, 0x5c, 0x41, 0x2f, + 0xd6, 0x98, 0xcd, 0x62, 0xd0, 0x41, 0xed, 0xed, 0x0e, 0x6f, 0x52, 0xca, 0x10, 0xad, 0xef, 0x1c, 0xc4, 0xd3, 0x3f, + 0x40, 0xd3, 0x07, 0x69, 0x61, 0x4a, 0x37, 0x28, 0xe0, 0x01, 0x7d, 0x53, 0xbf, 0x9f, 0xe3, 0x73, 0xed, 0x59, 0x62, + 0x61, 0x8f, 0x57, 0x84, 0xae, 0xbc, 0xb8, 0x51, 0x20, 0x6d, 0x76, 0xac, 0x02, 0xb0, 0x22, 0x09, 0x34, 0x22, 0x01, + 0x4b, 0x1d, 0x4f, 0x5c, 0xb6, 0x45, 0x03, 0x92, 0xa8, 0xa4, 0x90, 0x25, 0x92, 0xc0, 0x0f, 0x23, 0x08, 0x51, 0x14, + 0x83, 0xb8, 0x57, 0x2f, 0xaf, 0xb8, 0xa6, 0x06, 0x9c, 0x28, 0x82, 0x09, 0xd6, 0xe9, 0x14, 0x88, 0xad, 0xd8, 0xac, + 0xc1, 0xf3, 0xd2, 0x71, 0xe2, 0xc8, 0x12, 0xa0, 0x41, 0x9a, 0x2f, 0x9d, 0x76, 0xcb, 0xdb, 0x13, 0x2d, 0x55, 0x6c, + 0xe1, 0xbd, 0x58, 0x5a, 0xee, 0xb1, 0xf2, 0xb7, 0x03, 0xed, 0x85, 0xd5, 0x9e, 0x88, 0x1a, 0xac, 0xec, 0xda, 0x76, + 0x6d, 0x28, 0x0d, 0xd5, 0xbd, 0x72, 0x4c, 0x40, 0x45, 0xd7, 0x71, 0xb5, 0x8a, 0xb2, 0x11, 0xfc, 0xd9, 0xed, 0x82, + 0xc3, 0x00, 0x2c, 0x20, 0x7f, 0x79, 0xff, 0x53, 0x84, 0xe1, 0x99, 0x7e, 0x79, 0xff, 0xd3, 0x6e, 0xf7, 0x74, 0x3c, + 0x36, 0x5c, 0x81, 0x53, 0xeb, 0x00, 0x7f, 0x60, 0xd8, 0x06, 0xbb, 0x64, 0x77, 0xbb, 0xa7, 0xc0, 0x41, 0x28, 0xb6, + 0xc1, 0xec, 0x62, 0xe5, 0xc8, 0xa5, 0x58, 0x0d, 0xbd, 0x23, 0x01, 0xab, 0x6e, 0x8f, 0xa5, 0xd8, 0xa7, 0x3e, 0x2a, + 0x04, 0xa3, 0x5e, 0xf4, 0x2f, 0x3a, 0x05, 0x96, 0x14, 0x4c, 0x57, 0x83, 0x55, 0x55, 0xad, 0xcb, 0xe8, 0xf0, 0x30, + 0x5e, 0x67, 0xa3, 0x32, 0x83, 0x6d, 0x5e, 0x5e, 0x5f, 0x02, 0xa0, 0x42, 0x40, 0x1b, 0xef, 0x36, 0x22, 0x33, 0x2f, + 0x96, 0x74, 0x95, 0xe1, 0x9a, 0x04, 0xb3, 0x83, 0x9c, 0x5b, 0xdd, 0xe4, 0x94, 0xd8, 0x07, 0xb0, 0x39, 0xdc, 0xed, + 0x1a, 0xfc, 0xc2, 0x6c, 0xf4, 0x74, 0xbe, 0xca, 0xb4, 0x41, 0x27, 0x37, 0xfb, 0x9f, 0x44, 0x5e, 0x1a, 0x2a, 0x3e, + 0xc9, 0xf4, 0x45, 0x06, 0x7c, 0x1e, 0x7b, 0x23, 0x42, 0x9f, 0xe5, 0x6a, 0xb4, 0x06, 0xd8, 0xd8, 0xec, 0xe2, 0x6e, + 0x94, 0x72, 0x88, 0x48, 0x11, 0x58, 0x75, 0xcd, 0x2a, 0x23, 0xbe, 0x4d, 0xc5, 0x5d, 0x4b, 0x15, 0xf6, 0x46, 0x78, + 0xce, 0x2a, 0xdc, 0x38, 0xca, 0xf4, 0x26, 0x51, 0xf8, 0x02, 0x85, 0xa8, 0x1c, 0x8d, 0xe9, 0x9c, 0x40, 0x2a, 0xf3, + 0x98, 0x50, 0xcc, 0xe1, 0xde, 0xfd, 0x92, 0x3a, 0x73, 0x19, 0x5f, 0xb8, 0xf7, 0xc2, 0x97, 0x99, 0xdc, 0x4a, 0x00, + 0x45, 0x52, 0xb5, 0xff, 0xfc, 0x09, 0xa9, 0xf1, 0x3f, 0x53, 0xad, 0x01, 0xe8, 0xfd, 0x0c, 0x35, 0x39, 0x82, 0x80, + 0xad, 0x98, 0xfa, 0x68, 0xfa, 0x56, 0x32, 0xff, 0x01, 0x75, 0x3b, 0x82, 0x6d, 0x55, 0xfc, 0x9c, 0xa8, 0xa2, 0x05, + 0x4f, 0x37, 0x22, 0x8d, 0x45, 0x72, 0x17, 0xf1, 0x7a, 0x8a, 0x25, 0x31, 0x1b, 0x21, 0xeb, 0xe7, 0x66, 0x17, 0x7e, + 0x2a, 0x1a, 0x26, 0xe0, 0xb4, 0xf4, 0xb7, 0x95, 0xb7, 0x99, 0x2c, 0xe3, 0x8c, 0x4c, 0xb9, 0x42, 0xec, 0xb6, 0xfa, + 0x1e, 0x73, 0x82, 0x3f, 0x39, 0x7a, 0x42, 0xe8, 0xad, 0x9c, 0x96, 0x08, 0x4a, 0x27, 0x52, 0xeb, 0xaa, 0x89, 0xfd, + 0x9a, 0x42, 0x14, 0x07, 0xc1, 0x20, 0x74, 0xa7, 0x69, 0x9f, 0xe2, 0xfb, 0x6c, 0xd9, 0x6f, 0x4c, 0xd9, 0x92, 0x6c, + 0x05, 0x74, 0x4c, 0x3a, 0x6f, 0x4f, 0x6f, 0xcf, 0xce, 0xbd, 0xdf, 0xa0, 0x09, 0x07, 0xd5, 0x0d, 0xb4, 0xab, 0x20, + 0xd3, 0x18, 0xc5, 0x66, 0x31, 0xd6, 0x6e, 0x4d, 0x44, 0x10, 0x74, 0xba, 0x9c, 0x87, 0xed, 0x76, 0x42, 0x7c, 0x09, + 0x24, 0x50, 0xe0, 0xca, 0x45, 0x39, 0x09, 0x89, 0xba, 0x90, 0xe9, 0xc9, 0xba, 0x96, 0x2c, 0xd0, 0x6b, 0xec, 0x28, + 0xa0, 0x27, 0xdc, 0x3e, 0x05, 0xf4, 0x7d, 0xc1, 0x4e, 0xf8, 0x20, 0x18, 0x62, 0x7c, 0xd5, 0x80, 0xde, 0x48, 0xf5, + 0x08, 0x1e, 0xc2, 0xc0, 0x72, 0xd1, 0x97, 0x05, 0x43, 0x58, 0xa1, 0x3f, 0x53, 0x36, 0xf9, 0xfa, 0x1b, 0x37, 0xbf, + 0xe7, 0x5a, 0xcc, 0x0e, 0x42, 0x71, 0x7b, 0x3d, 0x01, 0xe2, 0x57, 0xf1, 0x2b, 0xb0, 0xae, 0xd6, 0x12, 0x6f, 0x37, + 0x3d, 0x7f, 0x08, 0x5f, 0x8e, 0x6e, 0x3f, 0x29, 0xcd, 0x27, 0x10, 0xa4, 0xc6, 0x49, 0xca, 0xdd, 0x77, 0x1f, 0xa5, + 0xab, 0x08, 0x46, 0x0b, 0x10, 0xeb, 0xee, 0xad, 0xe4, 0xac, 0x29, 0xfc, 0xc7, 0x3a, 0xdf, 0x63, 0xec, 0x10, 0x79, + 0x8a, 0xd3, 0xdf, 0x00, 0xc3, 0xbe, 0xf3, 0x6f, 0x65, 0xd6, 0x90, 0xe8, 0x5c, 0x7d, 0x04, 0xf4, 0x7f, 0xac, 0xc7, + 0xef, 0x04, 0x25, 0x7d, 0x49, 0x9c, 0x23, 0x5c, 0x11, 0x2f, 0xd1, 0x54, 0xaf, 0x37, 0xae, 0xe9, 0x5f, 0x85, 0x79, + 0xa1, 0x15, 0x1c, 0xf6, 0xad, 0x51, 0x78, 0xe0, 0x99, 0xf7, 0xab, 0x68, 0x08, 0xba, 0x7f, 0xc3, 0xbd, 0xf1, 0xab, + 0x60, 0x19, 0xde, 0x94, 0xb3, 0xcc, 0xdc, 0xe1, 0x6e, 0x32, 0x91, 0xca, 0x1b, 0xc6, 0x82, 0x8d, 0x50, 0xe6, 0xab, + 0x69, 0x30, 0xdf, 0xd6, 0x91, 0x4a, 0x76, 0xdf, 0xbf, 0x69, 0x9c, 0xb0, 0xd9, 0x20, 0x38, 0xad, 0x64, 0x11, 0x5f, + 0xf2, 0x60, 0xaa, 0x55, 0x14, 0x59, 0xd6, 0xef, 0x67, 0x80, 0x0c, 0xe3, 0xb4, 0x77, 0xf0, 0x64, 0xa9, 0x99, 0x09, + 0x71, 0x6d, 0x75, 0x16, 0xf0, 0xd6, 0x8c, 0xe6, 0x49, 0x05, 0xbb, 0xcc, 0x57, 0x52, 0xfc, 0xd1, 0x92, 0x64, 0x63, + 0xfd, 0x0d, 0x19, 0xb6, 0x95, 0xcf, 0x9c, 0x03, 0xc6, 0xcc, 0x8d, 0x54, 0x41, 0xee, 0x7a, 0xc0, 0x08, 0x21, 0x11, + 0x10, 0xce, 0x62, 0xe2, 0x4e, 0x98, 0xf0, 0x8f, 0x2e, 0x30, 0x4e, 0x8c, 0x81, 0x71, 0x3e, 0xca, 0x90, 0xd3, 0x13, + 0x3e, 0x48, 0x1a, 0xb3, 0xf5, 0x87, 0x2a, 0x91, 0x5e, 0x4b, 0x42, 0xcf, 0xe0, 0xf7, 0xb8, 0xc5, 0x03, 0x35, 0x82, + 0x53, 0xba, 0x9b, 0xd3, 0xe1, 0xcb, 0x82, 0x0c, 0xff, 0x04, 0xef, 0xae, 0xd8, 0x5e, 0x96, 0x13, 0x58, 0xdc, 0xb1, + 0x57, 0x3c, 0xcd, 0x55, 0x8b, 0x13, 0xe2, 0x11, 0x8b, 0xdc, 0x27, 0x16, 0x30, 0xa2, 0x86, 0xd1, 0xf8, 0xf1, 0xf4, + 0xed, 0x1b, 0x8d, 0xd9, 0x94, 0xfb, 0x1f, 0xc0, 0x88, 0x6a, 0x69, 0xbb, 0x1d, 0xf0, 0xd5, 0x08, 0x0d, 0xb6, 0x53, + 0x37, 0xd8, 0xfd, 0xbe, 0x49, 0x9b, 0x95, 0x5e, 0x36, 0x27, 0x06, 0xdd, 0x53, 0xda, 0xac, 0x94, 0x41, 0x6d, 0x57, + 0xe1, 0x68, 0x3e, 0x6b, 0xc4, 0xaa, 0xde, 0x87, 0xe1, 0x8a, 0xc6, 0x56, 0x56, 0x6e, 0x77, 0x13, 0x8e, 0x6c, 0x02, + 0x5c, 0x9f, 0x82, 0xb2, 0x6a, 0xce, 0x41, 0x0b, 0x3a, 0x13, 0x38, 0xa2, 0xdd, 0x2e, 0x84, 0x08, 0x1c, 0xc5, 0x70, + 0x32, 0x0f, 0x8b, 0xe1, 0x50, 0x0d, 0x7c, 0x41, 0x48, 0xf4, 0x57, 0xb1, 0xc8, 0x96, 0x0a, 0xb1, 0xc7, 0xdf, 0x49, + 0xbf, 0x16, 0x8a, 0x53, 0xee, 0xfd, 0x2a, 0xc8, 0xf6, 0xb7, 0x14, 0x63, 0x0e, 0x3a, 0xcd, 0x66, 0x06, 0x12, 0xd6, + 0x93, 0x8a, 0xa8, 0x75, 0x64, 0x67, 0x03, 0x54, 0xb1, 0x68, 0x0a, 0x0b, 0xea, 0x16, 0x4f, 0xac, 0x67, 0xf4, 0x1e, + 0x54, 0x82, 0xa8, 0x16, 0xec, 0xc6, 0x70, 0xad, 0xfd, 0x25, 0x42, 0x49, 0x39, 0x69, 0x32, 0x33, 0x56, 0x34, 0x58, + 0x80, 0x90, 0x34, 0x2e, 0xab, 0xd7, 0x32, 0xcd, 0x2e, 0x32, 0x40, 0x4c, 0x70, 0xfe, 0x73, 0xb2, 0xf1, 0xe6, 0x99, + 0x9a, 0x97, 0xae, 0xc4, 0xb9, 0x85, 0xf9, 0xe8, 0x7a, 0x4b, 0x0b, 0x12, 0x15, 0x40, 0xa3, 0x7c, 0x2d, 0xcf, 0xdf, + 0xf7, 0xac, 0x42, 0xf6, 0x3f, 0x9c, 0x2a, 0xdb, 0x21, 0x3e, 0x63, 0x15, 0xf1, 0x4e, 0xeb, 0x4a, 0x89, 0x34, 0x3a, + 0xda, 0x06, 0xc4, 0xb0, 0x65, 0xdf, 0xa2, 0x86, 0x0f, 0xc2, 0x0c, 0x3a, 0xc9, 0x0f, 0x7a, 0x46, 0xc7, 0xd6, 0x40, + 0xd2, 0xd7, 0x22, 0xf8, 0x1a, 0x1d, 0xe9, 0x44, 0x99, 0x46, 0x62, 0x0a, 0x89, 0x7e, 0xbd, 0xd0, 0x1a, 0xcb, 0x28, + 0xfb, 0x8a, 0xfc, 0xef, 0x75, 0xf7, 0x7e, 0x15, 0xbb, 0x1d, 0x4c, 0xb2, 0xe7, 0x71, 0x05, 0x9b, 0x1a, 0xb5, 0x42, + 0x38, 0x3b, 0x27, 0x15, 0x6a, 0xc7, 0x7a, 0x61, 0x09, 0xe4, 0x01, 0x6c, 0x45, 0x1a, 0x94, 0x41, 0xb2, 0xbf, 0x8a, + 0x85, 0x58, 0x3a, 0x51, 0x8e, 0x54, 0x78, 0x5f, 0x72, 0x94, 0x72, 0xb8, 0x8a, 0x85, 0x05, 0x43, 0x7e, 0x75, 0x74, + 0x51, 0xc8, 0x2b, 0x90, 0x94, 0x18, 0x86, 0xca, 0xf2, 0xba, 0xb8, 0x6a, 0x4b, 0x42, 0x7b, 0x67, 0x00, 0xc2, 0x52, + 0x80, 0xe0, 0xa5, 0x51, 0x43, 0xcc, 0xb6, 0x6a, 0x77, 0x45, 0xf7, 0x92, 0x03, 0xea, 0x74, 0xd7, 0x6e, 0xbd, 0x29, + 0xdb, 0x6c, 0x2b, 0x2e, 0xfc, 0x03, 0x4a, 0x3f, 0xe1, 0x83, 0xc2, 0xa7, 0x12, 0xb8, 0xf1, 0xd5, 0x26, 0xcb, 0x2e, + 0xee, 0x70, 0xe9, 0x57, 0x8d, 0xf1, 0xeb, 0xf7, 0x7b, 0x6a, 0x21, 0x34, 0x52, 0x81, 0xf9, 0xf6, 0x99, 0xa9, 0xca, + 0x68, 0x4a, 0xed, 0x25, 0xb8, 0x72, 0xf6, 0x23, 0xa8, 0x88, 0xeb, 0x8a, 0x4c, 0xa6, 0x06, 0xe8, 0xc0, 0xcb, 0x0a, + 0xb7, 0xb2, 0x00, 0x8f, 0x9d, 0x80, 0xec, 0x76, 0x3c, 0x0c, 0xf4, 0xa1, 0x13, 0xf8, 0x5b, 0xf2, 0x14, 0x99, 0x35, + 0xfb, 0xf8, 0xb3, 0x16, 0xfc, 0x63, 0x0b, 0x7e, 0x42, 0x71, 0xa7, 0x95, 0xf9, 0xb7, 0xd2, 0xba, 0xc5, 0xfd, 0x7b, + 0x99, 0x26, 0x14, 0x95, 0x09, 0xb5, 0x5f, 0xe9, 0xbf, 0x4c, 0xb0, 0x24, 0x95, 0xfd, 0x83, 0x84, 0x0f, 0xe6, 0x8d, + 0x27, 0xd6, 0x78, 0x32, 0x9c, 0x6e, 0xa5, 0x61, 0x08, 0x50, 0xe8, 0xe7, 0x65, 0xae, 0xa8, 0x7e, 0xfe, 0x79, 0xc3, + 0x37, 0xbc, 0xd9, 0x62, 0x9b, 0xf4, 0x40, 0x83, 0xbd, 0x3c, 0x9a, 0x52, 0x38, 0x89, 0x3a, 0x37, 0x12, 0x75, 0x51, + 0xb3, 0x0c, 0xd5, 0x09, 0x5e, 0xcd, 0x53, 0x3d, 0xec, 0xcd, 0x44, 0xb4, 0x56, 0x52, 0x96, 0x18, 0xb0, 0xd6, 0x91, + 0x87, 0xe4, 0x6e, 0xad, 0xe3, 0x4e, 0x43, 0x5d, 0x9a, 0x42, 0x09, 0xb0, 0xc2, 0x05, 0x38, 0x82, 0x7e, 0x2a, 0x42, + 0x0e, 0xd7, 0x54, 0xa5, 0x5f, 0xd0, 0x94, 0x3c, 0xf1, 0x14, 0xb5, 0x5a, 0x91, 0x6e, 0x3f, 0xca, 0xb1, 0x1b, 0xbe, + 0x71, 0x42, 0x4e, 0x8c, 0xd0, 0xdf, 0x1d, 0x4b, 0x39, 0x43, 0x8b, 0x07, 0x75, 0x82, 0xf5, 0xf2, 0x96, 0x02, 0xc5, + 0x1c, 0x5d, 0x56, 0x5d, 0xf3, 0x0a, 0x6d, 0x5f, 0x56, 0xfd, 0x7e, 0x6e, 0xeb, 0x49, 0xd9, 0x6c, 0xbb, 0x32, 0xfb, + 0x10, 0x15, 0x53, 0xb8, 0xeb, 0x13, 0xcd, 0x5f, 0x85, 0xfa, 0xaa, 0x2d, 0x73, 0x3e, 0xe2, 0x88, 0x8b, 0x91, 0x93, + 0xfa, 0x67, 0x35, 0xf5, 0x4a, 0xdc, 0xaf, 0x2a, 0xf9, 0x4e, 0x18, 0x2b, 0x46, 0x07, 0xd4, 0x9f, 0x2a, 0x95, 0xf7, + 0x8b, 0x02, 0xe0, 0x9e, 0x04, 0xfb, 0x0b, 0xec, 0x2b, 0x34, 0xc2, 0x6f, 0x4b, 0xc0, 0xbf, 0x55, 0xdc, 0x80, 0x55, + 0x60, 0x80, 0xd1, 0x64, 0x7b, 0x4e, 0x13, 0x38, 0xe0, 0x84, 0x56, 0x51, 0x50, 0x61, 0x86, 0x86, 0xda, 0xc2, 0xe8, + 0x29, 0xca, 0xb8, 0x55, 0x66, 0xef, 0xc6, 0xd8, 0x69, 0x81, 0xd7, 0xf0, 0xe7, 0xf3, 0x42, 0x0f, 0x1b, 0x75, 0x90, + 0x1e, 0x9d, 0xc4, 0xf4, 0xc7, 0x2d, 0x9c, 0xdc, 0x2c, 0x9c, 0x55, 0xcd, 0x12, 0xe8, 0x0e, 0x5c, 0x10, 0xe3, 0x7e, + 0x3f, 0x87, 0x23, 0xd3, 0x8c, 0x7c, 0xc1, 0x72, 0x1a, 0xb3, 0x15, 0xd5, 0x9e, 0x76, 0x97, 0x55, 0x98, 0xd3, 0x95, + 0x95, 0xf1, 0xa6, 0x0c, 0x54, 0x46, 0xbb, 0x5d, 0x08, 0x7f, 0xba, 0xad, 0x5d, 0xd2, 0xc5, 0x0a, 0x32, 0xc0, 0x1f, + 0x90, 0x88, 0x22, 0xf6, 0xf5, 0xbf, 0xd5, 0x38, 0xa5, 0x27, 0x4a, 0x6b, 0x96, 0xd0, 0x0d, 0xd3, 0xf5, 0xd3, 0x0b, + 0xb6, 0x69, 0x2c, 0x85, 0xdd, 0x2e, 0x6c, 0x26, 0x30, 0xcd, 0xb9, 0x92, 0xe9, 0x05, 0xea, 0xa4, 0x80, 0x8a, 0x85, + 0x17, 0xb8, 0xfc, 0x52, 0x42, 0xa1, 0xb9, 0x8b, 0xd5, 0xd2, 0x28, 0x31, 0xa1, 0x55, 0xf2, 0xf3, 0x87, 0xca, 0x7c, + 0x6d, 0x3c, 0xe2, 0xfe, 0x95, 0x86, 0x89, 0x29, 0x12, 0x15, 0xa2, 0xf3, 0x5f, 0x41, 0x96, 0x23, 0x00, 0xc7, 0xf2, + 0x54, 0xd6, 0xf4, 0xc7, 0x14, 0xe2, 0xa0, 0x43, 0x83, 0xde, 0x15, 0xf2, 0x2a, 0x2b, 0x79, 0x88, 0xf7, 0x04, 0x4f, + 0x33, 0x7a, 0xbf, 0xc1, 0x87, 0xb6, 0xf6, 0xe8, 0x09, 0xb2, 0xf5, 0x94, 0xfb, 0xf5, 0x77, 0x22, 0x5c, 0x40, 0xb4, + 0xca, 0x25, 0xd5, 0xea, 0x6a, 0x07, 0x40, 0xe5, 0xd9, 0x5e, 0x3d, 0x82, 0xd3, 0x4d, 0x5f, 0xdf, 0xaa, 0xd0, 0x99, + 0x03, 0x48, 0x7b, 0x48, 0xd6, 0x35, 0xd7, 0x3b, 0xc0, 0x1d, 0x89, 0xd5, 0x06, 0x68, 0xac, 0xdb, 0x9a, 0x9d, 0xf6, + 0x28, 0x1e, 0x13, 0x99, 0x19, 0x8b, 0x14, 0x63, 0xee, 0xd6, 0x69, 0x51, 0xb4, 0x45, 0x33, 0x84, 0xfd, 0xbb, 0x0e, + 0xdf, 0xb4, 0x22, 0xac, 0xdf, 0x6f, 0xfb, 0x02, 0xa3, 0x61, 0xcc, 0xb5, 0x7b, 0x9e, 0xa1, 0x1b, 0x36, 0xd8, 0x46, + 0xce, 0x43, 0xe4, 0xc3, 0x4c, 0x1d, 0x88, 0xb2, 0xb6, 0x06, 0x6c, 0x8f, 0xb8, 0xde, 0xb4, 0x8a, 0x9f, 0x57, 0x31, + 0x67, 0x7b, 0xd6, 0x38, 0xa5, 0xf5, 0x35, 0xae, 0x39, 0xae, 0x0a, 0x11, 0xb5, 0xf5, 0x8c, 0x87, 0x61, 0xe7, 0x4b, + 0xdc, 0x99, 0x15, 0x06, 0x2f, 0xc2, 0x52, 0xc9, 0x5e, 0xe5, 0xfa, 0x73, 0xd8, 0xe2, 0x20, 0x95, 0x2f, 0xbd, 0xfe, + 0xfe, 0xcb, 0x17, 0x5f, 0xa0, 0x9b, 0x9a, 0xf3, 0x23, 0x08, 0x32, 0x81, 0x0e, 0x59, 0x4a, 0xf5, 0xf8, 0x5d, 0x01, + 0xd4, 0x1e, 0xe6, 0xe1, 0xbb, 0x82, 0x89, 0xf8, 0x3a, 0xbb, 0x8c, 0x2b, 0x59, 0x8c, 0xae, 0xb9, 0x48, 0x65, 0x61, + 0xa5, 0xc6, 0xc1, 0xc9, 0x7a, 0x9d, 0xf3, 0x00, 0x4c, 0xe5, 0x2d, 0xa3, 0x6c, 0x2b, 0xcb, 0xf4, 0xe0, 0x6a, 0x79, + 0x7a, 0xa5, 0x45, 0xe7, 0xe5, 0xf5, 0x65, 0x10, 0xe1, 0xaf, 0x73, 0xf3, 0xe3, 0x2a, 0x2e, 0x3f, 0x06, 0x91, 0xb5, + 0xa9, 0x33, 0x3f, 0x50, 0x2a, 0x0f, 0xfe, 0x53, 0x20, 0xd3, 0xfd, 0xae, 0x00, 0xcb, 0x6c, 0x5b, 0xf1, 0x71, 0x8c, + 0xb5, 0x0e, 0x27, 0x64, 0xae, 0x4a, 0xf4, 0xde, 0x25, 0x9b, 0x02, 0xac, 0xfd, 0x14, 0x96, 0xb1, 0xca, 0x35, 0xc7, + 0xca, 0x54, 0x45, 0x66, 0x56, 0x36, 0xec, 0x30, 0xb4, 0x4e, 0x34, 0x0b, 0xf4, 0x16, 0xd0, 0x0f, 0xe4, 0xf0, 0x92, + 0x96, 0x1b, 0xe6, 0xf9, 0xd8, 0x34, 0x5e, 0x3f, 0x3a, 0xbc, 0x74, 0x0b, 0xf6, 0xd6, 0xde, 0xc9, 0x51, 0x98, 0x08, + 0x9e, 0xb5, 0x66, 0x7c, 0x91, 0x67, 0x05, 0xac, 0x9c, 0xc9, 0x78, 0x4c, 0xbd, 0xa5, 0xd5, 0xba, 0x39, 0x3a, 0x24, + 0xd7, 0xec, 0x71, 0xf5, 0x98, 0x93, 0x43, 0xde, 0x32, 0xb5, 0x6d, 0x5b, 0xc7, 0x79, 0x9a, 0x7c, 0x65, 0xba, 0x2f, + 0x36, 0x36, 0x22, 0xba, 0x72, 0xee, 0x73, 0x5e, 0xc1, 0xad, 0x6f, 0x4a, 0x43, 0xaf, 0x25, 0x00, 0xd1, 0x69, 0x03, + 0xfe, 0x82, 0x95, 0x9b, 0x51, 0xc5, 0xcb, 0x0a, 0x24, 0x2c, 0x28, 0xc2, 0x9b, 0x62, 0x6f, 0x0a, 0x77, 0xe3, 0xf4, + 0x1c, 0x76, 0xe0, 0x62, 0x8a, 0xee, 0x38, 0x31, 0x99, 0x97, 0x46, 0x2b, 0x1a, 0xe9, 0x5f, 0xae, 0x2f, 0xb1, 0xee, + 0x8b, 0x56, 0xe6, 0xd9, 0x9c, 0x0a, 0x8b, 0xdd, 0x55, 0x2e, 0x9d, 0xa8, 0xdf, 0x32, 0xe1, 0xca, 0x95, 0x20, 0x20, + 0xd3, 0x82, 0xf5, 0x0a, 0xb3, 0x8b, 0xe4, 0x06, 0x08, 0x19, 0x18, 0xbe, 0x06, 0x1b, 0x51, 0x72, 0x63, 0x05, 0xeb, + 0xdd, 0xf3, 0x75, 0x82, 0x90, 0x82, 0x07, 0x6e, 0x82, 0x7e, 0x68, 0xdd, 0xbc, 0x1d, 0x25, 0xca, 0x20, 0x1e, 0xb7, + 0x76, 0xca, 0x41, 0x02, 0x01, 0xb8, 0xa7, 0x2a, 0x04, 0x87, 0x02, 0x59, 0x07, 0x57, 0x33, 0x8e, 0xe0, 0xea, 0xca, + 0x99, 0x8b, 0x1b, 0x80, 0x75, 0xe5, 0xcf, 0x65, 0x83, 0x0b, 0xeb, 0x11, 0x55, 0xe6, 0x8c, 0x53, 0x0c, 0x62, 0x64, + 0x09, 0xfa, 0xda, 0x52, 0xda, 0x4b, 0xd0, 0x34, 0x5e, 0xb1, 0xb5, 0xf2, 0x01, 0xa0, 0xe7, 0x6c, 0xad, 0x8c, 0xfd, + 0xf1, 0xeb, 0x33, 0xb6, 0xd6, 0xd2, 0xe0, 0xe9, 0xd5, 0xfc, 0x7c, 0x7e, 0x36, 0x60, 0x47, 0x51, 0xa8, 0x0d, 0x18, + 0x02, 0x87, 0xc4, 0x1f, 0x0c, 0x42, 0x8d, 0x77, 0x32, 0x50, 0x01, 0xb1, 0x88, 0xc7, 0x63, 0x23, 0x6e, 0x56, 0x38, + 0x1e, 0x62, 0xf0, 0xab, 0xe6, 0x0b, 0x12, 0x10, 0x6a, 0x4a, 0x43, 0x97, 0xc7, 0x70, 0x38, 0x39, 0x98, 0x40, 0x2a, + 0x66, 0x66, 0xaa, 0x30, 0x36, 0x26, 0x11, 0xc4, 0x3b, 0xed, 0xac, 0x17, 0xca, 0xed, 0xae, 0xd1, 0x40, 0xae, 0x0c, + 0x3e, 0xab, 0xe2, 0xc9, 0xc1, 0xb0, 0xab, 0x62, 0x1c, 0x85, 0x6b, 0xa3, 0x7c, 0x3b, 0x3b, 0x06, 0xf0, 0xda, 0xb3, + 0xa1, 0x2f, 0x97, 0x38, 0x3b, 0x7c, 0x42, 0x1e, 0x3f, 0x21, 0xf4, 0x8c, 0x9d, 0x7d, 0xf1, 0x84, 0x9e, 0x29, 0x72, + 0x72, 0x30, 0x89, 0xae, 0x99, 0xc5, 0x7c, 0x39, 0x52, 0x4d, 0xa0, 0x97, 0xa3, 0x8d, 0x50, 0x0b, 0x4c, 0x3b, 0x34, + 0x85, 0xdf, 0x8e, 0x0f, 0x82, 0xc1, 0x75, 0xbb, 0xe9, 0xd7, 0xed, 0xb6, 0x7a, 0x5e, 0x5d, 0x07, 0x47, 0xd1, 0x7e, + 0x31, 0x93, 0x6f, 0xc6, 0x07, 0x6e, 0x0e, 0xb0, 0xbe, 0x87, 0xc7, 0xc4, 0x34, 0x69, 0x6f, 0x54, 0xfc, 0x9a, 0xbe, + 0xc2, 0x3e, 0x34, 0x8b, 0xec, 0xe8, 0xc3, 0xf0, 0xdf, 0xea, 0x44, 0x7d, 0xf6, 0xc5, 0x11, 0x90, 0x23, 0x90, 0x81, + 0x62, 0x89, 0x60, 0x86, 0x03, 0x4d, 0x01, 0x05, 0x99, 0x1e, 0x77, 0xaa, 0x87, 0x5f, 0x8d, 0x9a, 0x9a, 0x91, 0x6b, + 0x98, 0x1a, 0x6c, 0x0b, 0x7e, 0xa0, 0xba, 0xa1, 0xbf, 0xd1, 0xe8, 0x46, 0xda, 0xc9, 0xcc, 0xbc, 0xa4, 0x36, 0xae, + 0xdb, 0x35, 0x04, 0x30, 0x76, 0xf0, 0x82, 0x92, 0x7d, 0x79, 0x7c, 0x79, 0x80, 0xab, 0x08, 0x50, 0xb2, 0x58, 0xf0, + 0xe5, 0xe0, 0x52, 0x6f, 0xee, 0x83, 0x80, 0x0c, 0xbe, 0x0c, 0x66, 0x5f, 0x0e, 0xe4, 0x20, 0x38, 0x3e, 0xbc, 0x9c, + 0x05, 0xce, 0xb8, 0x1f, 0x42, 0x3c, 0xaa, 0x8a, 0x62, 0x26, 0x4c, 0x15, 0x89, 0xad, 0x3d, 0xb7, 0xf5, 0x2a, 0xe3, + 0x33, 0x9a, 0x4e, 0x2d, 0xf2, 0x77, 0x98, 0xb2, 0xd8, 0xfc, 0x0e, 0x26, 0xfc, 0x2a, 0x88, 0x5c, 0x10, 0xd4, 0x79, + 0x1e, 0xc5, 0x74, 0xc5, 0x6e, 0x45, 0x98, 0xd2, 0xe4, 0x30, 0x27, 0x24, 0x0a, 0x57, 0x0a, 0x3c, 0x4f, 0xbd, 0x4e, + 0x20, 0x8e, 0xab, 0xfb, 0xfc, 0x56, 0x84, 0x2b, 0x9a, 0x1f, 0x26, 0xa4, 0x55, 0x84, 0x8b, 0xc8, 0xb2, 0xad, 0xe9, + 0x05, 0x0b, 0xd7, 0xf4, 0x12, 0x98, 0x29, 0xb9, 0x09, 0x2f, 0x81, 0xcb, 0xdb, 0x2c, 0xd6, 0x4b, 0x76, 0xd9, 0x90, + 0xbe, 0x19, 0xbe, 0xf8, 0xc2, 0xfa, 0xe4, 0x01, 0x0f, 0xe9, 0xfc, 0xf0, 0x52, 0xb0, 0x01, 0xb8, 0xce, 0xf8, 0xcd, + 0x77, 0xf2, 0x56, 0xcf, 0x4b, 0x7b, 0x8a, 0x71, 0x66, 0xda, 0x89, 0x49, 0x3b, 0x21, 0xf7, 0xef, 0xdb, 0xdb, 0xd8, + 0x9c, 0xec, 0x65, 0xb4, 0x51, 0x2e, 0xab, 0x96, 0x21, 0x29, 0x36, 0x0c, 0xf9, 0x7b, 0x94, 0x9c, 0x5a, 0x81, 0x27, + 0xbb, 0xe0, 0x55, 0xb2, 0xf2, 0x0f, 0x2a, 0x6b, 0x35, 0x60, 0x8f, 0x11, 0xcb, 0x42, 0xe1, 0xd8, 0xbf, 0xce, 0x58, + 0xb1, 0xf1, 0x05, 0x1a, 0x31, 0x72, 0x6f, 0xaf, 0x33, 0xe6, 0xc5, 0x5c, 0x4d, 0x36, 0x5e, 0xa8, 0x3a, 0x2f, 0x3d, + 0x6f, 0xf1, 0x5e, 0x4e, 0xa9, 0x61, 0x24, 0xa2, 0x07, 0x63, 0x65, 0x46, 0xa9, 0x12, 0xb5, 0x06, 0x8d, 0x08, 0x36, + 0x76, 0xc1, 0x2f, 0xc1, 0x09, 0x95, 0x7b, 0xea, 0x6c, 0xdf, 0x4e, 0xa9, 0xf4, 0x80, 0x65, 0xa9, 0x51, 0x95, 0xbb, + 0x65, 0x26, 0x59, 0x35, 0x08, 0x46, 0x7f, 0x94, 0x52, 0xcc, 0xf1, 0xce, 0xc8, 0x82, 0x29, 0x58, 0x09, 0xaa, 0x5a, + 0x86, 0xe5, 0x90, 0xa3, 0x16, 0xcf, 0xf8, 0xa4, 0x4a, 0xfd, 0xa3, 0x23, 0x48, 0xee, 0x6a, 0xd3, 0x0a, 0x92, 0xfb, + 0x64, 0xfc, 0x44, 0x0f, 0x74, 0xba, 0xd1, 0x8e, 0x87, 0x3e, 0xbf, 0x8d, 0xf8, 0xda, 0xba, 0xf7, 0x54, 0x6b, 0x15, + 0xca, 0x40, 0x8b, 0x15, 0x95, 0x2b, 0xb5, 0xa4, 0xf7, 0xbb, 0x08, 0x80, 0x45, 0x6c, 0xcc, 0xc6, 0xfb, 0xb6, 0x59, + 0x21, 0x68, 0x74, 0xd9, 0x6c, 0x1b, 0x0f, 0x58, 0xa2, 0x5b, 0x3b, 0x98, 0xd0, 0x78, 0xc6, 0xca, 0x7e, 0x3f, 0x9f, + 0x01, 0x3d, 0xd5, 0x46, 0x4c, 0x05, 0x1c, 0xf9, 0x9f, 0x5b, 0x91, 0x29, 0x0a, 0x6c, 0xd6, 0xd4, 0xdd, 0x1a, 0xcb, + 0x48, 0xf4, 0x65, 0x4a, 0x97, 0x27, 0x3c, 0x03, 0xa6, 0xcd, 0xa6, 0xe5, 0xb8, 0xb2, 0xaf, 0x38, 0xf2, 0x54, 0x58, + 0x56, 0x9c, 0x57, 0xe1, 0x78, 0xeb, 0xf1, 0x0d, 0x0e, 0x0d, 0x9b, 0x76, 0xe1, 0x0f, 0x21, 0x2c, 0x84, 0xd7, 0x19, + 0xdc, 0x46, 0xb4, 0x9d, 0x04, 0x2a, 0x6f, 0xcc, 0x75, 0x42, 0xd9, 0xdc, 0x6e, 0x36, 0x9e, 0x41, 0x3a, 0x31, 0x07, + 0x4a, 0x35, 0x82, 0xd6, 0x68, 0x16, 0x54, 0x8d, 0x78, 0xe4, 0x78, 0x78, 0x67, 0x10, 0xab, 0xe5, 0x4b, 0x9a, 0x4a, + 0xd1, 0x00, 0x8c, 0x0b, 0xe0, 0xf2, 0xf4, 0xcb, 0xfb, 0x9f, 0x4e, 0x79, 0x5c, 0x24, 0xab, 0x77, 0x71, 0x11, 0x5f, + 0x95, 0xe1, 0x56, 0x8d, 0x51, 0x5c, 0x93, 0xa9, 0x18, 0x30, 0x69, 0x56, 0x52, 0x73, 0x57, 0x6a, 0x42, 0x8c, 0x75, + 0x26, 0x9b, 0xb2, 0x92, 0x57, 0x8d, 0x4a, 0x37, 0x45, 0x86, 0x1f, 0xb7, 0x7c, 0x4e, 0x0f, 0x01, 0xc8, 0xd3, 0xb8, + 0x90, 0x46, 0x52, 0x17, 0x62, 0xcc, 0x45, 0xbc, 0xae, 0x8f, 0xc7, 0x8d, 0xae, 0x97, 0xec, 0xe9, 0xf8, 0xab, 0xe9, + 0xeb, 0x2c, 0xcc, 0x06, 0x82, 0x8c, 0xaa, 0x15, 0x17, 0x2d, 0x53, 0x4e, 0x65, 0x12, 0x80, 0x3e, 0x9e, 0x3d, 0xc6, + 0x8e, 0xc6, 0x63, 0xb2, 0x6d, 0x8b, 0x07, 0x78, 0xb8, 0xda, 0x84, 0x05, 0x99, 0xeb, 0x3a, 0xa2, 0x40, 0xf0, 0xdb, + 0x2a, 0x00, 0x24, 0x47, 0x5b, 0x95, 0xe1, 0xd2, 0xd8, 0xd3, 0xf1, 0x84, 0x4a, 0xec, 0x76, 0x48, 0x6a, 0xaf, 0x42, + 0x37, 0xf3, 0xd2, 0xf7, 0x28, 0x92, 0xc6, 0x65, 0x69, 0xaf, 0x52, 0xa9, 0xf6, 0xcc, 0xdc, 0x75, 0x0d, 0x62, 0x30, + 0x84, 0xba, 0xee, 0xd2, 0xab, 0x7b, 0xbf, 0xb9, 0xd6, 0x6c, 0x07, 0xbc, 0xd7, 0xa0, 0x19, 0x4a, 0xde, 0x62, 0xde, + 0xba, 0x22, 0x6a, 0xba, 0xde, 0x80, 0x59, 0x31, 0xca, 0x96, 0xa2, 0x74, 0x43, 0x41, 0x29, 0x18, 0x5d, 0x6c, 0xbc, + 0x85, 0xfb, 0x5a, 0x36, 0x2e, 0x2c, 0x99, 0x5e, 0x2d, 0x4a, 0x4a, 0xa8, 0x6e, 0x2a, 0x46, 0x4a, 0x18, 0x29, 0x0d, + 0x4f, 0xe5, 0x7b, 0x81, 0xc7, 0x79, 0x1e, 0x44, 0x2d, 0x2f, 0xb0, 0x93, 0x8a, 0x9c, 0x80, 0xa3, 0x97, 0xc9, 0x69, + 0x28, 0xf0, 0x8f, 0x99, 0x02, 0x31, 0x1d, 0xaa, 0xfb, 0x0d, 0x6e, 0xfe, 0x7f, 0x14, 0x2c, 0xf0, 0xf8, 0xd6, 0x4b, + 0xdc, 0x46, 0xff, 0x28, 0x7c, 0x5a, 0xfa, 0x4c, 0xfa, 0xae, 0x2e, 0x9e, 0xb4, 0x37, 0x1b, 0x25, 0xab, 0x2c, 0x4f, + 0xdf, 0xc8, 0x94, 0x83, 0xc8, 0x0c, 0xad, 0x41, 0xd9, 0x4c, 0x34, 0x6e, 0x78, 0x60, 0xc4, 0xd8, 0xb8, 0xf1, 0xfd, + 0x98, 0x81, 0x6c, 0x18, 0xac, 0xbe, 0x59, 0x2a, 0x93, 0xcd, 0x15, 0x60, 0x8a, 0x28, 0xf9, 0xc9, 0x8b, 0x9c, 0xc3, + 0x53, 0xa8, 0xaf, 0x5f, 0xe0, 0x36, 0x57, 0xfa, 0x3e, 0xe7, 0x3f, 0x66, 0xf4, 0x47, 0x04, 0x3a, 0x89, 0xd7, 0x20, + 0xf7, 0x78, 0x06, 0x75, 0x23, 0x4c, 0x2d, 0xc7, 0xe0, 0x40, 0x88, 0x06, 0x22, 0x2a, 0x16, 0x28, 0xa8, 0x0b, 0x03, + 0xac, 0xa1, 0x2e, 0x98, 0xc3, 0xf3, 0x5c, 0x26, 0x1f, 0xa7, 0xc6, 0x67, 0x7e, 0x18, 0x63, 0xcc, 0xe4, 0x60, 0x10, + 0x56, 0xf3, 0x60, 0x38, 0x1e, 0x4d, 0x8e, 0x9e, 0xc2, 0xb9, 0x1d, 0x8c, 0x03, 0x32, 0x08, 0xea, 0x72, 0x1d, 0x0b, + 0x5a, 0x5e, 0x5f, 0xda, 0x32, 0xf0, 0xe3, 0x3a, 0x18, 0xfc, 0xa3, 0xf0, 0x14, 0xef, 0xa0, 0x39, 0x39, 0x93, 0x21, + 0xd8, 0xd8, 0x6f, 0x08, 0x48, 0xca, 0x7a, 0x9a, 0x9f, 0xd4, 0x87, 0x1b, 0x53, 0xda, 0x3f, 0x73, 0x78, 0xc1, 0x61, + 0x87, 0x04, 0x0a, 0xa4, 0xf1, 0x34, 0x1b, 0xbd, 0x52, 0x8a, 0xdc, 0x77, 0x05, 0x87, 0x3b, 0x73, 0xcf, 0x99, 0x1e, + 0x39, 0x85, 0x44, 0x33, 0x0b, 0xb8, 0x91, 0xbf, 0x12, 0xd7, 0x71, 0x9e, 0xa5, 0x07, 0xcd, 0x37, 0x07, 0xe5, 0x9d, + 0xa8, 0xe2, 0xdb, 0x51, 0x60, 0xac, 0x09, 0xb9, 0xaf, 0x7a, 0x02, 0xf4, 0x04, 0xd8, 0x02, 0x60, 0x40, 0xbc, 0x67, + 0x66, 0x32, 0xe7, 0x11, 0x78, 0x04, 0x36, 0x7d, 0x20, 0x8b, 0x3b, 0xe7, 0x92, 0xe4, 0x6f, 0xa6, 0xd2, 0x5e, 0xf5, + 0xca, 0xbd, 0x82, 0xac, 0x57, 0x5b, 0xb9, 0xef, 0xd6, 0x67, 0xdf, 0x74, 0x78, 0x05, 0x9e, 0x4b, 0x70, 0x8b, 0xec, + 0xf7, 0x9b, 0x82, 0x4a, 0x61, 0x54, 0xc4, 0x7b, 0xc9, 0x35, 0xfa, 0xb7, 0x7b, 0x63, 0xa3, 0x48, 0x6e, 0xf9, 0xf0, + 0x00, 0xea, 0x4c, 0xde, 0x15, 0xb7, 0x73, 0x88, 0xda, 0xba, 0x1b, 0x0f, 0xac, 0x36, 0x68, 0x97, 0xb5, 0x40, 0x70, + 0xe1, 0xe5, 0x41, 0x06, 0x63, 0x81, 0xb3, 0x32, 0x52, 0x6a, 0x5c, 0x43, 0x6a, 0xc1, 0x27, 0x79, 0x7a, 0x0f, 0x59, + 0xea, 0x49, 0x50, 0xe4, 0x78, 0x16, 0x43, 0xa6, 0xf1, 0x36, 0xf0, 0xf8, 0x9d, 0x0c, 0x41, 0x9a, 0xb6, 0xdb, 0x35, + 0x47, 0xa0, 0xec, 0x1e, 0x98, 0x92, 0xd4, 0xb5, 0x31, 0x35, 0xd0, 0x50, 0x7b, 0xa8, 0x91, 0x8a, 0x38, 0x9b, 0xbd, + 0x06, 0x1d, 0x22, 0xf8, 0x7e, 0xa7, 0x59, 0xd9, 0xf1, 0x62, 0x42, 0xf0, 0xe4, 0x7d, 0x71, 0x9b, 0x95, 0x55, 0x19, + 0xbd, 0x4f, 0xd1, 0x10, 0x2a, 0x91, 0x22, 0x7a, 0x09, 0xf1, 0xf4, 0x4a, 0xfc, 0x5d, 0x46, 0x3f, 0xa5, 0x34, 0x4e, + 0x53, 0x4c, 0x7f, 0x5e, 0xc0, 0xcf, 0x67, 0x80, 0xea, 0x88, 0x3b, 0x21, 0x3a, 0x97, 0x60, 0xaf, 0x06, 0xd1, 0xac, + 0x2a, 0x0e, 0x18, 0x9a, 0xd1, 0xad, 0xa0, 0x88, 0xd1, 0x86, 0xd9, 0x7f, 0x28, 0x50, 0x28, 0xa4, 0x8a, 0xf9, 0x4e, + 0xd8, 0x87, 0xe8, 0x47, 0x2c, 0xf2, 0xe4, 0xdd, 0x2b, 0x33, 0xa4, 0xd1, 0x9d, 0xa4, 0x7a, 0x6b, 0xe3, 0xb1, 0x85, + 0x81, 0xcb, 0xa2, 0xcb, 0x0d, 0x3d, 0x8b, 0xd7, 0x59, 0xb4, 0x05, 0xfc, 0x89, 0x77, 0xaf, 0x9e, 0x29, 0x0b, 0x93, + 0xe7, 0x19, 0x28, 0x0e, 0x4e, 0xde, 0xbd, 0x7a, 0x2d, 0xd3, 0x4d, 0xce, 0xa3, 0x33, 0x89, 0xa4, 0xf5, 0xe4, 0xdd, + 0xab, 0x9f, 0xd1, 0xdc, 0xeb, 0xa7, 0x02, 0xde, 0xbf, 0x04, 0xde, 0x32, 0x8a, 0x37, 0xd0, 0x27, 0xf5, 0x3b, 0xd9, + 0x60, 0xa7, 0xbc, 0x5a, 0xcb, 0xe8, 0x97, 0xb4, 0xf6, 0xa4, 0x55, 0xff, 0x2c, 0x7c, 0x6a, 0xe7, 0x09, 0x78, 0x6e, + 0xf3, 0x4c, 0x7c, 0x8c, 0xac, 0x68, 0x27, 0x88, 0xbe, 0x3c, 0xb8, 0xbd, 0xca, 0x45, 0x19, 0xe1, 0x0b, 0x86, 0x76, + 0x41, 0xd1, 0xe1, 0xe1, 0xcd, 0xcd, 0xcd, 0xe8, 0xe6, 0xab, 0x91, 0x2c, 0x2e, 0x0f, 0x27, 0xdf, 0x7e, 0xfb, 0xed, + 0x21, 0xbe, 0x0d, 0xbe, 0x6c, 0xbb, 0xbd, 0x57, 0x84, 0x0f, 0x58, 0x80, 0x08, 0xd5, 0x5f, 0xc2, 0x15, 0x05, 0xb4, + 0x70, 0x83, 0x2f, 0x83, 0x2f, 0xf5, 0xa1, 0xf3, 0xe5, 0x71, 0x79, 0x7d, 0xa9, 0xca, 0xef, 0x2a, 0xf9, 0x68, 0x3c, + 0x1e, 0x1f, 0x82, 0x04, 0xea, 0xcb, 0x01, 0x1f, 0x04, 0xb3, 0x60, 0x90, 0xc1, 0x85, 0xa6, 0xbc, 0xbe, 0x9c, 0x05, + 0x9e, 0x69, 0x6e, 0x83, 0x45, 0x74, 0x20, 0x2e, 0xc1, 0xe1, 0x25, 0x0d, 0xbe, 0x0c, 0x88, 0x4b, 0xf9, 0x02, 0x52, + 0xbe, 0x38, 0x7a, 0xea, 0xa7, 0xfd, 0x2f, 0x95, 0xf6, 0x95, 0x9f, 0x76, 0x8c, 0x69, 0x5f, 0x3d, 0xf3, 0xd3, 0x66, + 0x2a, 0xed, 0x85, 0x9f, 0xf6, 0xbf, 0xcb, 0x01, 0xa4, 0x1e, 0xf8, 0xd6, 0x7f, 0xe7, 0x5e, 0x6b, 0xf0, 0x14, 0x8a, + 0xb2, 0xab, 0xf8, 0x92, 0x43, 0xa3, 0x07, 0xb7, 0x57, 0x39, 0x0d, 0x06, 0xd8, 0x5e, 0xcf, 0x24, 0xc4, 0xfb, 0xe0, + 0xcb, 0x4d, 0x91, 0x87, 0xc1, 0x97, 0x03, 0x2c, 0x64, 0xf0, 0x65, 0x40, 0xbe, 0x34, 0x06, 0x32, 0x82, 0x6d, 0x03, + 0x17, 0x8a, 0x74, 0x68, 0x03, 0x84, 0xf9, 0xd2, 0xb8, 0x9a, 0xfe, 0x59, 0x74, 0x67, 0xc3, 0x5b, 0xa2, 0x72, 0xd3, + 0x0d, 0x6a, 0xfa, 0x16, 0xbc, 0x13, 0xa0, 0x51, 0x51, 0x70, 0x1d, 0x17, 0xe1, 0x70, 0x58, 0x5e, 0x5f, 0x12, 0xb0, + 0xcb, 0x5c, 0xf3, 0xb8, 0x8a, 0x02, 0x21, 0x87, 0xea, 0x67, 0xa0, 0x22, 0x5f, 0x05, 0x08, 0x88, 0x04, 0xff, 0x05, + 0x35, 0x7d, 0x27, 0xd9, 0x36, 0x18, 0xde, 0xf0, 0xf3, 0x8f, 0x59, 0x35, 0x54, 0xa2, 0xc5, 0x6b, 0x41, 0xe1, 0x07, + 0xfc, 0x75, 0x55, 0x47, 0x7f, 0x82, 0x1b, 0x77, 0x53, 0xc3, 0xfe, 0x4e, 0x3a, 0x16, 0xf5, 0x9d, 0x5c, 0x64, 0xcb, + 0x69, 0xeb, 0x40, 0x7f, 0x2b, 0x49, 0xb5, 0xc8, 0x06, 0xc1, 0x30, 0x18, 0xf0, 0x25, 0x7b, 0x2b, 0x17, 0xdc, 0x33, + 0x9f, 0x7a, 0x24, 0xfd, 0x69, 0x9e, 0x67, 0x03, 0xf0, 0x4d, 0x41, 0x7e, 0xe4, 0xf0, 0xbf, 0x17, 0x43, 0x14, 0x1e, + 0x0e, 0x1e, 0x1d, 0x92, 0x79, 0xb0, 0xbe, 0x45, 0x8f, 0xce, 0x28, 0xc8, 0xc4, 0x8a, 0x17, 0x59, 0xe5, 0x2d, 0x95, + 0xbb, 0x4d, 0xdb, 0xcb, 0xe3, 0xde, 0xb3, 0x79, 0x1d, 0x8b, 0x40, 0x9d, 0x73, 0xa0, 0x78, 0x43, 0xd9, 0x53, 0xd9, + 0x94, 0x90, 0x6a, 0x43, 0xde, 0xb0, 0x1c, 0xb0, 0xe0, 0xb8, 0x37, 0x1c, 0x1e, 0x04, 0x03, 0xa7, 0xce, 0x1d, 0x04, + 0x07, 0xc3, 0xe1, 0x2c, 0x70, 0xf7, 0xa1, 0x6c, 0xe4, 0xee, 0x8c, 0xb4, 0x60, 0xff, 0x2c, 0xc2, 0x92, 0x82, 0x78, + 0x4c, 0x6a, 0xf1, 0x97, 0x06, 0x97, 0x19, 0x00, 0xf4, 0x91, 0x92, 0x80, 0x19, 0x58, 0x99, 0x01, 0x84, 0xe6, 0xa6, + 0x31, 0x3b, 0x03, 0xe6, 0x11, 0x38, 0xe6, 0x11, 0x32, 0x0e, 0x80, 0x58, 0x12, 0xe0, 0xdc, 0x05, 0x51, 0xac, 0x0b, + 0x79, 0x04, 0xa0, 0xf7, 0xf8, 0x93, 0x98, 0x52, 0x30, 0x49, 0xc7, 0x2a, 0x04, 0x41, 0x1c, 0x9f, 0x5f, 0x8b, 0xd6, + 0xe4, 0xac, 0xd0, 0xc1, 0x8c, 0x24, 0xc0, 0x86, 0x18, 0xd8, 0x39, 0xb8, 0x9f, 0x83, 0xd2, 0xc3, 0xea, 0x9d, 0x90, + 0x0b, 0xbe, 0xe3, 0x9e, 0x6c, 0x16, 0xae, 0x9e, 0x70, 0x10, 0xdc, 0x71, 0xcd, 0x02, 0x8c, 0xaa, 0x62, 0x53, 0x56, + 0x3c, 0xfd, 0x70, 0xb7, 0x86, 0xd8, 0x77, 0x38, 0xa0, 0xef, 0x64, 0x9e, 0x25, 0x77, 0xa1, 0xb3, 0xe7, 0xda, 0xaa, + 0xf4, 0x1f, 0x3e, 0xbc, 0xfe, 0x29, 0x02, 0x91, 0x63, 0x6d, 0x28, 0xfd, 0x1d, 0xc7, 0xb3, 0xc9, 0x8f, 0xf0, 0xe4, + 0x6f, 0xec, 0x3b, 0x6e, 0x4f, 0x8f, 0x7e, 0x1f, 0xea, 0xa6, 0x77, 0x7c, 0x7e, 0xc7, 0x47, 0xae, 0x38, 0x54, 0x57, + 0xb8, 0xaf, 0x6f, 0x36, 0xbe, 0x11, 0xd2, 0xc3, 0xf3, 0x4c, 0x79, 0x63, 0x7e, 0xb4, 0x83, 0x61, 0x10, 0x4c, 0xb5, + 0x50, 0x12, 0xa2, 0x6e, 0x30, 0x25, 0x60, 0x88, 0x0e, 0xf4, 0xb2, 0x9a, 0x22, 0xe7, 0xa6, 0x46, 0x16, 0xde, 0x0f, + 0x98, 0x16, 0x3a, 0x34, 0x72, 0x28, 0x3f, 0x38, 0x9c, 0x30, 0x66, 0xe1, 0xb7, 0x4a, 0x98, 0x7e, 0xb5, 0xa8, 0x9c, + 0x83, 0xe8, 0x01, 0x18, 0xe3, 0x0a, 0x5e, 0x40, 0x57, 0xd8, 0xf5, 0x46, 0x45, 0xc5, 0x40, 0xf0, 0x38, 0xe4, 0x00, + 0x3d, 0xec, 0x82, 0x96, 0x95, 0xa5, 0xba, 0x55, 0x39, 0x4b, 0x15, 0x75, 0x19, 0xca, 0xca, 0x58, 0x61, 0xbe, 0x97, + 0xec, 0x87, 0x02, 0x3d, 0xcb, 0xa7, 0xa2, 0x0b, 0x5e, 0x08, 0x25, 0x58, 0xae, 0xeb, 0x9d, 0x08, 0x44, 0x9d, 0x1f, + 0x7a, 0x57, 0x7d, 0x8d, 0x63, 0xc7, 0xd3, 0xd7, 0x32, 0xe5, 0xda, 0x84, 0x42, 0xf3, 0xf9, 0xd2, 0x57, 0x4c, 0x14, + 0xec, 0x06, 0xfa, 0xd5, 0xb6, 0xd1, 0x67, 0x77, 0x1b, 0xbd, 0x19, 0x94, 0xe8, 0x98, 0xd7, 0x28, 0xb8, 0x56, 0x0a, + 0x05, 0xa3, 0xbd, 0x8d, 0x3f, 0xc1, 0x91, 0x5b, 0xdd, 0x1e, 0x7a, 0xbf, 0x55, 0xf1, 0xe5, 0x1b, 0xf4, 0xed, 0xb4, + 0x3f, 0x47, 0x95, 0xfc, 0x65, 0xbd, 0x06, 0x1f, 0x2a, 0x88, 0x2c, 0x62, 0x71, 0x69, 0xa1, 0x9e, 0xd3, 0x77, 0x27, + 0x6f, 0xc0, 0x8f, 0x12, 0x7f, 0xff, 0xfa, 0x7d, 0x50, 0x93, 0x69, 0x3c, 0x2f, 0xcc, 0x87, 0x36, 0x07, 0x84, 0x26, + 0x71, 0x69, 0xf6, 0xfd, 0x3c, 0x6e, 0xb2, 0xef, 0x9a, 0xad, 0xa7, 0x45, 0x13, 0x49, 0xca, 0x70, 0xfb, 0x60, 0x40, + 0xa0, 0x0f, 0x10, 0xc5, 0xd9, 0x17, 0x34, 0x86, 0x34, 0x9f, 0xd9, 0xf7, 0x23, 0xe2, 0xbd, 0xd8, 0x0b, 0x21, 0xc6, + 0x15, 0x16, 0x8d, 0x1e, 0xf2, 0x39, 0x8f, 0x94, 0x61, 0xd1, 0x7b, 0x4c, 0x20, 0xce, 0x70, 0x5a, 0xbd, 0x47, 0xcc, + 0x63, 0xbc, 0x1b, 0x68, 0xd9, 0x43, 0x94, 0x51, 0x97, 0xbd, 0x61, 0xf1, 0xfd, 0x71, 0x13, 0x66, 0xd6, 0xf2, 0x72, + 0x08, 0x7f, 0x03, 0x6d, 0x00, 0x4e, 0x39, 0xb2, 0x7c, 0x95, 0xd9, 0xe8, 0x6a, 0x89, 0xe9, 0x4d, 0x04, 0xb1, 0x78, + 0x74, 0x3a, 0xac, 0x5d, 0x9d, 0xaa, 0x77, 0xb5, 0xf3, 0x99, 0xe8, 0x55, 0xa0, 0x95, 0x6b, 0xdb, 0xe3, 0x21, 0xdc, + 0xa5, 0x96, 0x56, 0xd8, 0x88, 0x72, 0x2e, 0x9e, 0xee, 0x02, 0x9b, 0x13, 0xd0, 0xe0, 0x4a, 0xa6, 0x00, 0x9c, 0xa5, + 0xd5, 0x68, 0xd4, 0x08, 0xfb, 0xac, 0x9c, 0xcf, 0x61, 0x6b, 0x21, 0x9e, 0x16, 0x80, 0xe1, 0x36, 0x31, 0x28, 0x79, + 0x37, 0x06, 0xe5, 0xf4, 0xa3, 0x82, 0xb7, 0x0e, 0xce, 0xca, 0x55, 0x9c, 0xca, 0x1b, 0xc0, 0x62, 0x0c, 0xfc, 0x54, + 0x2c, 0xd5, 0x4b, 0x48, 0x56, 0x3c, 0xf9, 0x88, 0x56, 0x1b, 0x69, 0x00, 0x5c, 0xe5, 0xd4, 0x58, 0xee, 0x29, 0x90, + 0x50, 0x57, 0x8a, 0x4a, 0x88, 0xab, 0x2a, 0x4e, 0x56, 0xa7, 0x98, 0x1a, 0x6e, 0xa1, 0x17, 0x51, 0x20, 0xd7, 0x5c, + 0x00, 0x49, 0xcf, 0xd9, 0xbf, 0x32, 0x8d, 0x35, 0xfe, 0x4c, 0xa2, 0x80, 0x49, 0xa3, 0x06, 0x63, 0xa5, 0xec, 0x85, + 0x34, 0xd1, 0xde, 0x82, 0xa0, 0x76, 0x2f, 0xff, 0x84, 0xba, 0x9f, 0x41, 0x2b, 0xc2, 0x06, 0x18, 0xa2, 0x3c, 0xc7, + 0x1d, 0x9a, 0xda, 0x25, 0xe7, 0x01, 0x23, 0x3a, 0xef, 0xb3, 0xda, 0x6e, 0xf5, 0x67, 0x2b, 0xc0, 0x36, 0x4d, 0x8d, + 0x4f, 0x61, 0x98, 0x10, 0x13, 0x1b, 0xd8, 0x2a, 0x2b, 0xed, 0x86, 0x32, 0xed, 0xa4, 0x2b, 0xe6, 0xb5, 0x70, 0x9a, + 0xf7, 0x18, 0x5b, 0x8d, 0x54, 0xee, 0x7e, 0x3f, 0x34, 0x3f, 0x59, 0x4e, 0x9f, 0xe9, 0x90, 0xcd, 0xde, 0x78, 0xd0, + 0x9c, 0x68, 0x75, 0x55, 0x47, 0x3f, 0xa0, 0x03, 0x30, 0xd3, 0x16, 0x20, 0xd3, 0x05, 0x9b, 0xf6, 0x95, 0xa8, 0xb8, + 0x24, 0x61, 0xa9, 0x24, 0xb0, 0xb3, 0x9b, 0x92, 0x9d, 0x6d, 0x40, 0x3c, 0xc3, 0x5d, 0x4f, 0x8b, 0x9d, 0x90, 0x26, + 0xbc, 0xc5, 0x41, 0x02, 0xa2, 0x0e, 0x55, 0x5d, 0x42, 0xb6, 0xc6, 0xd0, 0xc5, 0xbf, 0x28, 0x85, 0x09, 0x6b, 0x99, + 0x54, 0x25, 0x26, 0x08, 0x52, 0xb9, 0xdf, 0x22, 0xb0, 0x44, 0xc1, 0x0e, 0x60, 0xef, 0xdd, 0xa8, 0x9b, 0x51, 0x53, + 0xd5, 0xa9, 0x97, 0xe0, 0xe3, 0x34, 0xef, 0x2a, 0xc8, 0x2c, 0xec, 0xaa, 0xd8, 0xf0, 0x40, 0xc7, 0xa6, 0x52, 0xc6, + 0xc4, 0x5d, 0x5a, 0x64, 0x88, 0x07, 0x8c, 0xb1, 0x74, 0x21, 0x90, 0x6f, 0xb6, 0x3f, 0x6e, 0x7a, 0x82, 0xd0, 0x4f, + 0xd8, 0x50, 0x02, 0x37, 0x9d, 0xed, 0xa9, 0x69, 0xe6, 0x03, 0x22, 0x0e, 0x03, 0x0a, 0x24, 0x1b, 0x87, 0x34, 0x47, + 0xfa, 0x82, 0xa4, 0x09, 0x03, 0x43, 0x2b, 0x9e, 0x13, 0x64, 0x45, 0xa1, 0x67, 0xeb, 0xaa, 0x8d, 0x73, 0x65, 0x98, + 0xa3, 0x25, 0xa7, 0xc2, 0xe7, 0x04, 0x99, 0xd8, 0x3d, 0x6d, 0x33, 0x93, 0xe1, 0x28, 0x59, 0x60, 0x7e, 0x05, 0x51, + 0xe2, 0xce, 0x34, 0xab, 0x72, 0x30, 0x2e, 0x60, 0x81, 0x56, 0xbe, 0x07, 0x75, 0x63, 0x0d, 0x6d, 0x35, 0x0c, 0xb1, + 0xdb, 0x9f, 0x60, 0xbf, 0xd6, 0x4e, 0xeb, 0x32, 0xc5, 0xf2, 0x32, 0x85, 0x68, 0x2f, 0x64, 0x7e, 0xa3, 0x48, 0x74, + 0xaf, 0x08, 0x43, 0xc2, 0x3a, 0xca, 0x9e, 0xb4, 0xa9, 0x01, 0xf4, 0xd4, 0x0b, 0x78, 0xde, 0xb9, 0x96, 0x61, 0x17, + 0xe9, 0xfe, 0xaa, 0xe0, 0x53, 0xba, 0x41, 0x90, 0xa2, 0x37, 0x29, 0x98, 0xf3, 0x7a, 0x94, 0xd4, 0x9b, 0xd3, 0x96, + 0x19, 0x55, 0x47, 0x45, 0x48, 0x39, 0xc1, 0x7f, 0xf2, 0x52, 0x6a, 0x62, 0x13, 0x26, 0x78, 0xe0, 0xc3, 0x3c, 0xc3, + 0x06, 0xde, 0xed, 0xde, 0xa5, 0x61, 0xd2, 0x66, 0x1b, 0x52, 0x90, 0x56, 0x98, 0xb8, 0x18, 0x50, 0xd9, 0x2b, 0xdc, + 0x2f, 0xd8, 0x4e, 0x9a, 0x82, 0x07, 0x61, 0xa3, 0x81, 0x89, 0x5b, 0x5d, 0x7c, 0x13, 0x26, 0x34, 0x5c, 0x51, 0xed, + 0xec, 0xa4, 0x25, 0xcd, 0xed, 0x75, 0x79, 0x61, 0xfb, 0xa0, 0x63, 0x87, 0x75, 0x0d, 0x0f, 0x34, 0xaf, 0xd9, 0xc5, + 0x35, 0xd3, 0x34, 0xd1, 0x58, 0x0f, 0x29, 0x4b, 0x8e, 0x4d, 0x3d, 0x5d, 0xe3, 0x6a, 0x99, 0x6b, 0x60, 0x77, 0x89, + 0x17, 0x7a, 0xc0, 0xc3, 0x0e, 0xd7, 0x24, 0xba, 0xc0, 0x66, 0xb3, 0x75, 0x4d, 0xa6, 0xf9, 0x7d, 0xd9, 0x72, 0x13, + 0x10, 0xce, 0x52, 0xdf, 0xdc, 0x27, 0xc7, 0x9a, 0xb6, 0xf9, 0x49, 0x80, 0xe3, 0xed, 0x15, 0x90, 0x74, 0x2c, 0x41, + 0x17, 0xdf, 0xd2, 0x1f, 0x44, 0x6a, 0xa6, 0x82, 0xde, 0x3b, 0x5f, 0xa4, 0x6e, 0x7e, 0x01, 0xb6, 0x51, 0x5b, 0x63, + 0x9a, 0x95, 0x6d, 0xc2, 0x44, 0x59, 0x58, 0x23, 0x0b, 0xb9, 0x02, 0x1f, 0xcc, 0xfd, 0xa6, 0x4e, 0x4f, 0x3a, 0x88, + 0xb0, 0xdf, 0x45, 0x8f, 0x47, 0x18, 0x2b, 0xd6, 0x20, 0x31, 0xac, 0xc2, 0x86, 0x36, 0x97, 0x43, 0x94, 0x53, 0xb3, + 0x64, 0xa2, 0x15, 0xf5, 0x29, 0x45, 0x94, 0x82, 0xb9, 0xf1, 0xb4, 0x6c, 0x98, 0x12, 0x22, 0x64, 0x85, 0x74, 0x40, + 0xb5, 0x16, 0x5a, 0xaa, 0x09, 0x7a, 0x1d, 0x7a, 0x59, 0x68, 0x4c, 0x41, 0xf4, 0x11, 0x19, 0x6e, 0xc4, 0x91, 0xd1, + 0xfd, 0x31, 0x8a, 0x09, 0x84, 0xaa, 0xf6, 0xf2, 0xc2, 0xea, 0xd3, 0xb2, 0xad, 0x0e, 0xe2, 0x0a, 0x91, 0xef, 0xbb, + 0x09, 0x6a, 0x8c, 0x82, 0x36, 0xa7, 0x1b, 0xfd, 0xa5, 0x08, 0x7d, 0xbb, 0x70, 0xec, 0x46, 0x41, 0x24, 0x44, 0x60, + 0xf5, 0x9a, 0x8a, 0x01, 0x59, 0x17, 0xb1, 0x8b, 0xd0, 0xa4, 0xbb, 0x85, 0x28, 0x6f, 0x54, 0xd6, 0x1f, 0x37, 0x21, + 0xd9, 0xed, 0xb0, 0x2c, 0xf0, 0x65, 0x3f, 0xdd, 0xdc, 0x03, 0xf9, 0xfd, 0x7a, 0xf3, 0x49, 0xc8, 0xef, 0x57, 0xd9, + 0xe7, 0x40, 0x7e, 0xbf, 0xde, 0xfc, 0x4f, 0x43, 0x7e, 0x9f, 0x6e, 0x3c, 0xc8, 0x6f, 0x35, 0x18, 0xbf, 0x15, 0x2c, + 0x78, 0xfb, 0x26, 0xa0, 0xcf, 0x25, 0x0b, 0xde, 0xbe, 0x7c, 0xe9, 0x1b, 0x81, 0x08, 0x8d, 0x5c, 0x6f, 0x64, 0xc1, + 0x88, 0xdb, 0x02, 0xaf, 0x50, 0xeb, 0xe4, 0x03, 0x15, 0x65, 0x00, 0xbc, 0x5e, 0xfe, 0x23, 0xab, 0x56, 0x61, 0x70, + 0x18, 0x90, 0xb9, 0x83, 0x04, 0x1d, 0x4e, 0xe0, 0xf6, 0x86, 0x46, 0x96, 0xd5, 0x67, 0xc1, 0x87, 0x8f, 0x46, 0xa3, + 0xb8, 0xb8, 0xc4, 0x4b, 0x9d, 0xd9, 0x48, 0x08, 0x78, 0x9c, 0xf1, 0xd2, 0x86, 0x88, 0x58, 0xc5, 0xe5, 0x99, 0x8e, + 0xcd, 0x52, 0xda, 0xad, 0x08, 0x11, 0xe7, 0xcf, 0x00, 0xa7, 0xde, 0xee, 0xcd, 0x18, 0xfb, 0xa1, 0x38, 0x62, 0x1d, + 0x40, 0xf6, 0xd9, 0x46, 0xbf, 0x3b, 0x8f, 0x4b, 0xfe, 0x2e, 0xae, 0x56, 0x0c, 0x7a, 0x09, 0x77, 0x11, 0xc1, 0x93, + 0xca, 0x63, 0x9b, 0x14, 0x50, 0x79, 0xa6, 0x81, 0xca, 0x3b, 0xde, 0xd3, 0xd0, 0x0e, 0x8b, 0xf6, 0x01, 0x36, 0xd2, + 0xe5, 0x0c, 0x8c, 0x16, 0x5f, 0x5c, 0x73, 0x51, 0xfd, 0x04, 0x78, 0xea, 0x82, 0x17, 0x70, 0x4b, 0x40, 0x2e, 0xb6, + 0xe1, 0x84, 0x40, 0x85, 0xef, 0xd9, 0xa1, 0xa2, 0xc6, 0x18, 0xd1, 0x44, 0xa3, 0xdf, 0x78, 0x13, 0x42, 0xef, 0x4e, + 0xd0, 0x15, 0x61, 0x24, 0xbc, 0x3f, 0x37, 0xfc, 0x2c, 0x03, 0xf3, 0x79, 0x01, 0x50, 0x1a, 0x08, 0x87, 0xca, 0x94, + 0xdc, 0x02, 0x13, 0xb6, 0xc6, 0x5c, 0x29, 0x4b, 0x3d, 0xa4, 0x52, 0xaa, 0xe0, 0x74, 0x05, 0x4d, 0x25, 0xe0, 0x70, + 0x47, 0x12, 0xc0, 0x4c, 0x6d, 0x61, 0x10, 0xdd, 0x36, 0xa5, 0x59, 0x1a, 0x39, 0x45, 0x9a, 0xc3, 0x27, 0xa5, 0x0a, + 0x74, 0xfa, 0x2c, 0x89, 0x2b, 0x7e, 0x29, 0x0b, 0x08, 0x85, 0xdb, 0x4a, 0xa1, 0x48, 0xbf, 0xcf, 0xc4, 0xe6, 0x8a, + 0x17, 0x59, 0x72, 0xb6, 0xca, 0xca, 0x0a, 0xf2, 0x2d, 0xf4, 0xe9, 0xb7, 0xac, 0xa7, 0x05, 0xce, 0x9b, 0x9a, 0x14, + 0x66, 0xe6, 0xf1, 0x44, 0xed, 0x74, 0x50, 0xaf, 0x7a, 0xaf, 0x0d, 0xf6, 0x7b, 0x73, 0xa2, 0xc7, 0xad, 0xf5, 0x60, + 0x35, 0xa9, 0xcd, 0x54, 0x05, 0x71, 0x04, 0xd4, 0x81, 0xcd, 0x70, 0x16, 0x53, 0xba, 0xb1, 0x08, 0x2a, 0x60, 0xed, + 0x80, 0x39, 0x32, 0x71, 0x79, 0x76, 0xa3, 0xe4, 0x27, 0x3d, 0x45, 0x61, 0xd2, 0x28, 0x56, 0xc8, 0x3e, 0x2b, 0x16, + 0x6e, 0x58, 0x72, 0x4f, 0xae, 0x4d, 0x94, 0x34, 0x30, 0xba, 0xe2, 0xf6, 0x40, 0x1c, 0x27, 0xed, 0x94, 0xf9, 0x70, + 0x12, 0xed, 0x65, 0x03, 0xe6, 0xa0, 0x9d, 0x0f, 0xae, 0xaa, 0xab, 0xb9, 0x6a, 0xc5, 0xa8, 0x92, 0x3f, 0xc9, 0x1b, + 0x73, 0xb1, 0x3d, 0x4e, 0x3a, 0x12, 0xa1, 0xdc, 0x49, 0x94, 0x1f, 0xaf, 0xd4, 0x0f, 0x80, 0x5e, 0xd1, 0xe4, 0xf0, + 0xcf, 0x0d, 0x37, 0xe0, 0xf4, 0xa1, 0xf6, 0x10, 0x74, 0xa2, 0x7c, 0x3d, 0x23, 0x9e, 0x51, 0x9d, 0x5e, 0x2e, 0x0b, + 0x30, 0xe8, 0xf2, 0x87, 0x12, 0x22, 0xc8, 0x74, 0x4e, 0xeb, 0x72, 0xaa, 0xcd, 0x8b, 0x0d, 0x6f, 0x43, 0x3f, 0xef, + 0x3b, 0x0d, 0x9c, 0x9b, 0xf0, 0x70, 0xf8, 0x74, 0x4c, 0x6a, 0x6d, 0xeb, 0x8e, 0x0b, 0xcf, 0xfe, 0x56, 0x8b, 0xd3, + 0x3d, 0xdb, 0x05, 0x4a, 0x9b, 0x5e, 0x0a, 0xed, 0x1a, 0xa9, 0xb8, 0xa7, 0xfb, 0x35, 0xa9, 0xdd, 0x42, 0xb3, 0x92, + 0xa7, 0xdf, 0xd5, 0x69, 0x77, 0xf6, 0x68, 0x9b, 0xe9, 0x2a, 0xeb, 0xdf, 0x33, 0x1b, 0xa7, 0xae, 0x41, 0x39, 0x6a, + 0xbd, 0x04, 0x7d, 0x98, 0xb8, 0x8e, 0x6c, 0x7a, 0x3a, 0x59, 0xd6, 0x49, 0x7e, 0x46, 0xea, 0x91, 0xab, 0xb0, 0x5d, + 0xdd, 0x59, 0xf8, 0x2d, 0x4f, 0xc2, 0xae, 0x86, 0xa9, 0x9b, 0x81, 0xe9, 0x02, 0x22, 0x67, 0x82, 0x3e, 0x20, 0xfc, + 0xfd, 0xd1, 0xb6, 0x49, 0xce, 0xea, 0x43, 0xef, 0x33, 0xfc, 0x9d, 0xa5, 0xf0, 0xb7, 0xaa, 0x7f, 0xa7, 0xdb, 0x2b, + 0x5e, 0xad, 0x64, 0x1a, 0x05, 0xef, 0xde, 0x9e, 0x7e, 0x08, 0x34, 0x22, 0x3b, 0xde, 0x4b, 0x8c, 0x26, 0xda, 0x60, + 0x3f, 0x81, 0x66, 0x26, 0x97, 0x97, 0x08, 0x4a, 0xa8, 0x51, 0xed, 0x4f, 0x57, 0xf2, 0xe6, 0x24, 0xcf, 0x7d, 0xe6, + 0xd9, 0x10, 0x5c, 0xcd, 0x4f, 0x36, 0xa8, 0x55, 0x08, 0x32, 0xc0, 0x51, 0x56, 0x9e, 0x69, 0xad, 0x4d, 0x7a, 0x76, + 0x7e, 0x77, 0xa6, 0x25, 0x43, 0x16, 0x15, 0xf2, 0xd9, 0xef, 0xc7, 0x69, 0x76, 0x7d, 0x80, 0xa7, 0x02, 0x0b, 0xc0, + 0xa4, 0x3e, 0xe7, 0xe7, 0x9b, 0xaa, 0x92, 0x62, 0x58, 0xc8, 0x9b, 0x60, 0x76, 0xac, 0x1e, 0x4c, 0x86, 0x58, 0x3d, + 0x06, 0x07, 0xff, 0x95, 0xe4, 0x59, 0xf2, 0x91, 0x05, 0x8f, 0xb6, 0x19, 0x9b, 0xb5, 0x68, 0xff, 0xb8, 0x0e, 0x66, + 0xd0, 0xd6, 0x83, 0x93, 0x3c, 0x3f, 0x3e, 0x54, 0x5f, 0xcc, 0x8e, 0x0f, 0xd3, 0xec, 0x7a, 0xe6, 0x01, 0xf4, 0x3b, + 0x7b, 0x5b, 0x84, 0x42, 0x73, 0xf7, 0x69, 0x70, 0xac, 0x4d, 0x78, 0x68, 0x39, 0x10, 0x90, 0xe2, 0x98, 0xf0, 0x26, + 0x28, 0xf9, 0x09, 0x63, 0x38, 0x6f, 0x77, 0xbb, 0xd0, 0x1a, 0x03, 0x25, 0x1e, 0x52, 0x4e, 0x01, 0x1c, 0x0a, 0x66, + 0xa1, 0x09, 0xa1, 0x49, 0x4d, 0x42, 0x83, 0xe7, 0x13, 0x13, 0x5a, 0xd4, 0x14, 0x8e, 0xa0, 0xd7, 0xf1, 0xda, 0x08, + 0xbf, 0xb4, 0x30, 0xc1, 0xb4, 0x7e, 0xde, 0x18, 0xc7, 0xa8, 0x3d, 0xaa, 0x06, 0x61, 0xab, 0x57, 0xde, 0x37, 0xb0, + 0x20, 0xef, 0x0c, 0x2b, 0x1a, 0xb4, 0xe8, 0x0a, 0xc8, 0x2b, 0x7d, 0x31, 0x1b, 0xa7, 0xe1, 0xa2, 0xa4, 0x72, 0x49, + 0xd8, 0x2c, 0xdc, 0x22, 0xbb, 0x5d, 0x2a, 0xea, 0x1d, 0xc9, 0xda, 0xc1, 0x5d, 0xaa, 0xd9, 0x99, 0x3d, 0xda, 0x0a, + 0x44, 0x58, 0x2c, 0xd9, 0xac, 0x39, 0x5f, 0x55, 0x7c, 0x3e, 0x5c, 0x71, 0xf0, 0xcb, 0x09, 0x0e, 0xfe, 0x2b, 0x3d, + 0xcf, 0xed, 0xa4, 0xa8, 0x15, 0xb9, 0x8a, 0x45, 0x9a, 0xf3, 0x0f, 0xf1, 0xf9, 0x0f, 0x98, 0xe7, 0xf9, 0x79, 0xfe, + 0x0c, 0x32, 0xd4, 0xc1, 0xec, 0xd1, 0x36, 0xa9, 0x46, 0x2f, 0xde, 0x7c, 0x78, 0xf5, 0xe1, 0x9f, 0x67, 0xcf, 0x4e, + 0x3e, 0xbc, 0xf8, 0xfe, 0xed, 0xfb, 0x57, 0x2f, 0x4e, 0x17, 0xd6, 0x11, 0x56, 0xe1, 0xab, 0x91, 0xe5, 0x6e, 0xe7, + 0xf2, 0xfd, 0xf2, 0xe6, 0xf9, 0x8b, 0x97, 0xaf, 0xde, 0xbc, 0x78, 0x5e, 0xab, 0xb9, 0x6c, 0x37, 0x04, 0x76, 0x68, + 0x9c, 0x09, 0x5e, 0x40, 0xf1, 0x3a, 0xca, 0x23, 0x36, 0x5b, 0xc3, 0x02, 0x36, 0x9b, 0xae, 0x23, 0x28, 0xc0, 0x22, + 0x3b, 0xd0, 0x9b, 0x05, 0x1a, 0x2e, 0xcd, 0xc6, 0xf1, 0x97, 0x98, 0xdf, 0x9b, 0x17, 0xf8, 0xdd, 0x7b, 0x79, 0x63, + 0xba, 0xa2, 0x47, 0x48, 0x21, 0x7e, 0xcd, 0x9f, 0xfd, 0x7e, 0xec, 0x4b, 0xd9, 0x50, 0x14, 0xa1, 0xca, 0x85, 0x5f, + 0x75, 0x70, 0xa0, 0x2d, 0xfe, 0x02, 0x08, 0x58, 0x11, 0xcc, 0x8e, 0x0f, 0xfd, 0xdc, 0xb3, 0xdf, 0xa3, 0x9f, 0xbc, + 0xce, 0x61, 0xa9, 0x30, 0x0e, 0xcd, 0xb4, 0xbd, 0x53, 0x11, 0x42, 0x2a, 0xb9, 0x73, 0x53, 0xad, 0x20, 0x43, 0xae, + 0x24, 0x89, 0xec, 0x24, 0x2a, 0x4b, 0x16, 0x53, 0xda, 0xef, 0xfa, 0xaf, 0xeb, 0x33, 0x4a, 0x06, 0xb8, 0x28, 0x65, + 0x11, 0x40, 0x3f, 0xda, 0x71, 0x26, 0x0e, 0xbc, 0x78, 0x2e, 0xd8, 0xa3, 0x4e, 0xf2, 0x0e, 0x23, 0x72, 0xd8, 0xfe, + 0xd4, 0xeb, 0xd8, 0xef, 0xc4, 0xfd, 0xf8, 0x3f, 0xcd, 0x3d, 0xeb, 0x76, 0xdb, 0x36, 0xd2, 0xff, 0xfb, 0x14, 0x0c, + 0x93, 0x4d, 0xc5, 0x84, 0xa4, 0x49, 0xc9, 0xb2, 0x15, 0xc9, 0xb2, 0xdb, 0xe6, 0x72, 0x36, 0xfb, 0xb9, 0x4d, 0x4f, + 0xe2, 0xe6, 0xdb, 0xad, 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, 0x20, 0x78, 0x93, 0x94, 0x26, 0xed, 0xee, 0x69, 0x93, + 0x88, 0xb8, 0x63, 0x00, 0x0c, 0x06, 0x73, 0xd5, 0xf8, 0x64, 0x4a, 0xe8, 0x45, 0x0e, 0xb0, 0x39, 0x1e, 0xc8, 0xe5, + 0x1b, 0xdf, 0xfc, 0xdf, 0xc8, 0x9c, 0x7b, 0xe6, 0xd2, 0x33, 0xdf, 0x85, 0x57, 0x59, 0xed, 0xea, 0xc8, 0x58, 0x33, + 0x26, 0x1b, 0xb4, 0xc0, 0x63, 0x05, 0x7f, 0x47, 0x70, 0xea, 0xda, 0xb7, 0xb9, 0x8c, 0xed, 0xc2, 0x8b, 0xe7, 0x4c, + 0x84, 0x78, 0x11, 0xb9, 0x29, 0x87, 0x8a, 0xa1, 0x80, 0x05, 0xdc, 0xb9, 0x3c, 0xe0, 0xaa, 0x08, 0xbe, 0x3d, 0x49, + 0xe3, 0xe0, 0x7f, 0xd8, 0x3d, 0x50, 0x7b, 0x49, 0x1a, 0xad, 0x80, 0xc6, 0xf7, 0xe6, 0x9c, 0x67, 0x63, 0xb6, 0xd8, + 0x7e, 0xdd, 0x7d, 0xfc, 0xc8, 0x6c, 0xdc, 0x92, 0x40, 0x28, 0xda, 0x69, 0x34, 0x9f, 0x07, 0xac, 0xa5, 0x8b, 0xa0, + 0x23, 0xba, 0x29, 0xbb, 0x39, 0x7b, 0xe0, 0x08, 0x4f, 0x9f, 0x46, 0xd6, 0x74, 0xb4, 0xc4, 0x8c, 0x99, 0x74, 0x85, + 0x47, 0x14, 0x2f, 0xf2, 0x74, 0x6f, 0x50, 0x2c, 0xc2, 0xd7, 0x25, 0x3f, 0xba, 0xd6, 0x34, 0x5a, 0x8f, 0x03, 0x66, + 0xe1, 0x76, 0x87, 0x2e, 0x37, 0xe3, 0xf5, 0x78, 0x0c, 0xd1, 0x5d, 0x1e, 0x38, 0x26, 0xf8, 0xab, 0x89, 0x12, 0x7c, + 0x47, 0x66, 0xc6, 0x00, 0x26, 0x65, 0xa7, 0xe5, 0xe1, 0x83, 0x8e, 0x09, 0xb0, 0x88, 0xa8, 0x83, 0x14, 0xde, 0x8c, + 0x35, 0xa7, 0x76, 0xa8, 0xbf, 0x83, 0xdd, 0x97, 0xe8, 0x83, 0xba, 0xa3, 0x3f, 0xbc, 0xd4, 0xdf, 0x21, 0x8c, 0x31, + 0xea, 0xf1, 0x73, 0xda, 0xbd, 0xba, 0xa9, 0x93, 0xb0, 0x7c, 0x8d, 0xf1, 0x0f, 0x80, 0x59, 0xfc, 0xc2, 0xf7, 0xe6, + 0x61, 0x94, 0xa4, 0xfe, 0x44, 0xbf, 0x1a, 0xbc, 0xf6, 0x5b, 0x97, 0xcb, 0xb4, 0x65, 0x5c, 0x99, 0x93, 0x54, 0x0d, + 0x9d, 0x22, 0x10, 0x26, 0x46, 0x4e, 0x69, 0x2a, 0xa4, 0x9e, 0xa0, 0xad, 0x05, 0x05, 0x6a, 0xc6, 0x42, 0x93, 0x74, + 0x08, 0xe5, 0x4a, 0x71, 0x58, 0x30, 0xa0, 0x94, 0x8e, 0x35, 0x8d, 0x01, 0xbd, 0x70, 0x9e, 0xaf, 0x37, 0x78, 0x95, + 0xa7, 0xf9, 0x6d, 0x89, 0xbe, 0x83, 0x85, 0xc1, 0x0d, 0x7d, 0x3f, 0x50, 0x95, 0x45, 0x0b, 0xf7, 0xee, 0xe8, 0xdb, + 0x22, 0x5d, 0x00, 0xf7, 0x37, 0x68, 0x6a, 0x84, 0x51, 0xaa, 0x81, 0x43, 0x1c, 0xe8, 0x71, 0x54, 0x56, 0x2e, 0xe3, + 0xad, 0xb6, 0x8c, 0x8c, 0x23, 0x83, 0xef, 0xf0, 0xf2, 0x6b, 0x71, 0xb7, 0x68, 0x05, 0xcf, 0x17, 0xf4, 0xc8, 0x08, + 0x61, 0x01, 0x0b, 0x14, 0xa5, 0x82, 0xfb, 0x77, 0xde, 0xbd, 0x2d, 0x41, 0x5e, 0x8b, 0x68, 0x80, 0x2d, 0x1e, 0xd0, + 0x54, 0x10, 0x3a, 0xa5, 0x33, 0x85, 0x0a, 0x23, 0x82, 0x86, 0x49, 0x41, 0xbf, 0x0c, 0xef, 0x02, 0x40, 0x49, 0xfc, + 0x9a, 0x1e, 0x65, 0xd7, 0x22, 0xe4, 0xb2, 0x08, 0x78, 0xac, 0x5c, 0xce, 0x80, 0x5d, 0xc3, 0xd5, 0x3a, 0x45, 0x17, + 0xbd, 0x30, 0x00, 0x96, 0xe9, 0x1a, 0xba, 0xfc, 0x04, 0x2c, 0x9d, 0x93, 0x89, 0x99, 0xae, 0xf9, 0xd3, 0x6a, 0x1a, + 0x27, 0x7a, 0x01, 0x79, 0x21, 0x7e, 0x47, 0x06, 0x17, 0x7c, 0xc6, 0x7c, 0x1a, 0x13, 0x33, 0xf7, 0x6f, 0xdf, 0x9a, + 0xa0, 0x20, 0xa8, 0x06, 0x33, 0x4c, 0xa8, 0x9d, 0x41, 0x2b, 0xa8, 0x9d, 0x2c, 0xb8, 0xed, 0x2c, 0x4c, 0x73, 0xf4, + 0x68, 0x13, 0x66, 0x67, 0x8f, 0x36, 0x49, 0x36, 0x7c, 0xb4, 0xf1, 0xa4, 0x8e, 0x81, 0x7e, 0xa1, 0x93, 0x82, 0xc1, + 0x08, 0xc1, 0x30, 0xca, 0xae, 0x73, 0x8b, 0x9f, 0x7c, 0xbe, 0xb0, 0xcb, 0x28, 0x5d, 0x43, 0x91, 0xff, 0x90, 0x0b, + 0xf6, 0x57, 0xb1, 0xbf, 0xf4, 0xe2, 0x7b, 0xd2, 0x03, 0x30, 0x55, 0x65, 0x01, 0x43, 0xd7, 0x08, 0xd1, 0x13, 0x00, + 0x08, 0xe7, 0xeb, 0xda, 0x37, 0x32, 0x8d, 0xf1, 0xd9, 0x4a, 0x61, 0x28, 0xf4, 0x75, 0xad, 0x3f, 0x65, 0xf6, 0x94, + 0xa5, 0x9e, 0x1f, 0x50, 0x95, 0x81, 0x88, 0x72, 0x5f, 0x99, 0x5e, 0x52, 0x9c, 0x5e, 0x58, 0xdc, 0x3f, 0x38, 0x19, + 0xba, 0x02, 0x68, 0xdc, 0x38, 0x33, 0x8c, 0x7e, 0x55, 0xbf, 0xa2, 0x94, 0xf7, 0xa7, 0x2e, 0x07, 0x83, 0xe5, 0x08, + 0x61, 0x39, 0x58, 0x38, 0x89, 0xa6, 0xec, 0xa7, 0xb7, 0xaf, 0x65, 0xb8, 0x2d, 0xe0, 0x1c, 0x8d, 0xf8, 0xc6, 0x4c, + 0x90, 0x7e, 0x88, 0x91, 0x76, 0xa0, 0xc0, 0x58, 0x9a, 0xdc, 0x42, 0x71, 0xa6, 0x6b, 0x67, 0x34, 0x76, 0x36, 0xa5, + 0x51, 0x0f, 0x23, 0xac, 0x15, 0x67, 0x27, 0x07, 0x54, 0x9a, 0x6e, 0x3b, 0x2a, 0x04, 0x60, 0x88, 0x61, 0x86, 0x39, + 0x14, 0x20, 0x32, 0xe8, 0xd0, 0xcd, 0x1f, 0x14, 0xf6, 0x12, 0xf9, 0xf3, 0xee, 0x59, 0x91, 0x54, 0xc1, 0x5a, 0xfa, + 0xe9, 0x09, 0xc6, 0xfa, 0x82, 0xfb, 0x1a, 0xbc, 0x83, 0x9c, 0x1c, 0xd0, 0xa7, 0x56, 0x3a, 0x11, 0x79, 0x23, 0xe2, + 0x69, 0xd7, 0xe7, 0x0d, 0x7c, 0xd2, 0x51, 0x81, 0xd0, 0xf2, 0x90, 0xea, 0x65, 0xba, 0xb6, 0xe4, 0xa4, 0x11, 0x77, + 0x43, 0x04, 0x3e, 0x0a, 0x1c, 0x38, 0xbb, 0xba, 0xb6, 0xf4, 0xee, 0x70, 0xe6, 0x22, 0xc7, 0xbb, 0x6b, 0xb9, 0x3c, + 0x2b, 0x3f, 0x6b, 0x49, 0xf1, 0xac, 0x4d, 0xf8, 0xe2, 0x82, 0x01, 0x82, 0x7c, 0x91, 0x2f, 0x50, 0xb0, 0x5b, 0xb3, + 0xb8, 0x0b, 0xb1, 0xb8, 0xd3, 0x86, 0xc5, 0x9d, 0x6e, 0x59, 0xdc, 0x80, 0x2f, 0xa4, 0x26, 0x41, 0x17, 0xa3, 0x51, + 0x99, 0x04, 0x1e, 0x27, 0x34, 0xfa, 0xfc, 0x9c, 0x21, 0x9c, 0xac, 0x24, 0x00, 0xa5, 0xaa, 0x06, 0x58, 0xd5, 0xc1, + 0x45, 0x01, 0x44, 0x75, 0xe2, 0xf2, 0xd4, 0x89, 0x79, 0x43, 0xec, 0xce, 0x56, 0x50, 0x9e, 0x2f, 0xec, 0x52, 0x8a, + 0x4b, 0xde, 0x5a, 0x34, 0xcc, 0x74, 0xb1, 0x65, 0xa6, 0x93, 0xc2, 0xd1, 0xe5, 0xd3, 0xa6, 0x43, 0xa8, 0x4e, 0x0a, + 0xf6, 0x20, 0x28, 0x9a, 0xe2, 0x96, 0x29, 0xee, 0xc3, 0x66, 0x1c, 0xab, 0xec, 0xa8, 0x95, 0x97, 0x24, 0xb7, 0x51, + 0x0c, 0x92, 0x1a, 0x68, 0xe6, 0xd3, 0xb6, 0xd4, 0xd2, 0x0f, 0xb9, 0x13, 0x98, 0xc6, 0xcd, 0x94, 0xe7, 0xab, 0x5b, + 0xaa, 0xdd, 0xed, 0x52, 0x89, 0x95, 0x97, 0xa6, 0x2c, 0x46, 0xa0, 0x7b, 0xe0, 0x2d, 0xfc, 0xbf, 0x64, 0x9b, 0xd5, + 0xe0, 0x90, 0x40, 0xc1, 0xea, 0x88, 0xa1, 0x57, 0x40, 0x5b, 0xc5, 0xe2, 0x22, 0x56, 0x1c, 0xca, 0xc5, 0x12, 0xf0, + 0x3f, 0xe0, 0x71, 0x6d, 0xc5, 0x8a, 0xc9, 0x93, 0x7b, 0x64, 0xd8, 0x2b, 0x6f, 0xfa, 0x0e, 0x04, 0x82, 0xad, 0xb6, + 0x09, 0xca, 0xbd, 0xaa, 0xfb, 0xb8, 0x98, 0x88, 0xbd, 0x49, 0x8e, 0x24, 0x11, 0x4b, 0x72, 0xd5, 0x29, 0xb0, 0xba, + 0xf4, 0xac, 0xd9, 0xd5, 0xa6, 0x9d, 0x1d, 0xcc, 0x7d, 0xa3, 0x82, 0x35, 0x01, 0xb5, 0x05, 0xc3, 0x53, 0xf9, 0xe6, + 0x0a, 0x4c, 0xf7, 0xc8, 0x00, 0x8e, 0xf1, 0x25, 0xc4, 0x41, 0x75, 0xc4, 0x83, 0x76, 0x14, 0xc3, 0xad, 0x75, 0xe9, + 0x5c, 0x65, 0x8f, 0xe7, 0xf8, 0xcb, 0xbd, 0xca, 0x1e, 0x8f, 0xf1, 0x57, 0xfb, 0x0a, 0x23, 0xde, 0xd5, 0x3c, 0xe4, + 0x95, 0x39, 0xeb, 0xa7, 0x85, 0xfd, 0x44, 0x7a, 0x6b, 0x9f, 0xb0, 0x6d, 0xf8, 0x02, 0x3f, 0x7c, 0xb4, 0x49, 0xc0, + 0x52, 0x53, 0x9d, 0x43, 0x68, 0xc7, 0x46, 0x56, 0x9b, 0x3e, 0x6f, 0x48, 0x1f, 0x1b, 0x7f, 0xf2, 0xc5, 0x8f, 0xbb, + 0x24, 0xca, 0xef, 0x94, 0x22, 0x1b, 0xe2, 0x7a, 0xec, 0x87, 0x5e, 0x7c, 0x7f, 0x4d, 0xcf, 0x8b, 0x96, 0xa0, 0xdd, + 0x25, 0x7b, 0x85, 0xc8, 0xcb, 0xa2, 0x98, 0x2c, 0x55, 0x18, 0xc3, 0xf7, 0xfc, 0xa2, 0x1f, 0xfe, 0x3d, 0x56, 0xc8, + 0xb6, 0xc2, 0x03, 0x94, 0x2f, 0x48, 0xa1, 0xa3, 0xeb, 0x47, 0x9b, 0x16, 0xab, 0x36, 0x53, 0x9a, 0x6d, 0x89, 0x2e, + 0x84, 0xe5, 0xc1, 0xc7, 0xec, 0x72, 0xea, 0xf7, 0x51, 0x0e, 0x36, 0x8e, 0xee, 0xac, 0x47, 0x9b, 0xf4, 0x4c, 0x5f, + 0x7a, 0xf1, 0x07, 0x36, 0xb5, 0x26, 0x7e, 0x3c, 0x09, 0x98, 0xde, 0xd7, 0xc7, 0x81, 0x17, 0x7e, 0xe0, 0x9f, 0x56, + 0xb4, 0x4e, 0x51, 0xb2, 0xbd, 0xf3, 0xed, 0x2b, 0x60, 0x42, 0x2c, 0x3b, 0x24, 0x56, 0x6b, 0xa0, 0xa0, 0x3d, 0x97, + 0x0c, 0xaf, 0x9c, 0x50, 0xcc, 0x4b, 0x99, 0xa0, 0x98, 0x09, 0xc2, 0x76, 0xb0, 0x74, 0x35, 0x75, 0x5c, 0x2f, 0xdd, + 0x54, 0xa7, 0x4a, 0xcc, 0x4a, 0x19, 0xaa, 0xf1, 0x1a, 0x5b, 0xf8, 0xfd, 0xdd, 0x51, 0x10, 0xed, 0xfd, 0xbb, 0x93, + 0xad, 0x7c, 0xde, 0x0c, 0x21, 0xd5, 0x22, 0x0b, 0x8b, 0x4f, 0xe8, 0x9c, 0x13, 0x98, 0xcd, 0x5d, 0xab, 0x95, 0xbd, + 0x24, 0x59, 0x2f, 0xd9, 0x94, 0x24, 0x8a, 0x67, 0xf9, 0xa0, 0x8a, 0x2f, 0x0b, 0x75, 0x60, 0xbf, 0xac, 0xdb, 0xc7, + 0x87, 0xcf, 0x41, 0xd3, 0x01, 0x08, 0xca, 0x68, 0x36, 0xd3, 0xf3, 0x37, 0xfe, 0x8e, 0x6a, 0xee, 0xe1, 0x2f, 0xeb, + 0x57, 0x2f, 0x9d, 0x57, 0xb2, 0x72, 0x08, 0x84, 0xb1, 0x10, 0xdb, 0x72, 0xba, 0x58, 0x19, 0xaf, 0x98, 0xd1, 0xcc, + 0x0b, 0x9b, 0xa7, 0x73, 0x59, 0xd8, 0xe2, 0x2b, 0xc6, 0xa6, 0x40, 0x70, 0x9b, 0x95, 0xd4, 0xeb, 0x80, 0xdd, 0x30, + 0x29, 0x12, 0xae, 0x76, 0x56, 0x53, 0x03, 0x7d, 0xd6, 0x71, 0x51, 0x33, 0xa7, 0xea, 0x94, 0x29, 0x8d, 0x70, 0x0e, + 0x7c, 0xe6, 0xea, 0x11, 0x2b, 0x1d, 0xa9, 0x91, 0xa9, 0x2b, 0x03, 0x68, 0x1c, 0xd9, 0x59, 0x43, 0x7a, 0x1f, 0x03, + 0x56, 0xd7, 0x8f, 0xcd, 0x74, 0x8d, 0x3e, 0xf8, 0xf8, 0xe6, 0x70, 0x0a, 0xe0, 0xe4, 0xb5, 0x72, 0x76, 0x48, 0x13, + 0xc4, 0xea, 0x98, 0x64, 0x3a, 0x71, 0x5f, 0x84, 0x96, 0x24, 0xaa, 0x0b, 0x0b, 0x3e, 0x54, 0xed, 0xda, 0x68, 0xc5, + 0x99, 0x8f, 0x31, 0x10, 0x6c, 0xc8, 0x92, 0xa4, 0x11, 0x60, 0x72, 0xd1, 0x4d, 0x3d, 0x2f, 0x5d, 0x84, 0x47, 0x9e, + 0x6e, 0x3a, 0x26, 0x90, 0x04, 0x38, 0xc1, 0x72, 0x5f, 0x78, 0xbd, 0x5c, 0x2f, 0xb9, 0x9e, 0x4b, 0x3c, 0x1f, 0xeb, + 0x5c, 0x07, 0xa1, 0x29, 0xff, 0x56, 0xe7, 0x83, 0x2a, 0x5c, 0xd3, 0xb5, 0x43, 0x6b, 0x15, 0x50, 0x6f, 0x85, 0x5d, + 0x84, 0x0d, 0x88, 0x31, 0x95, 0xf0, 0x2b, 0x9b, 0xcd, 0xd8, 0x24, 0x4d, 0x0c, 0xc1, 0x3c, 0x92, 0x5e, 0x67, 0xc1, + 0xda, 0xe8, 0xc1, 0x50, 0xff, 0x01, 0x6c, 0xef, 0x85, 0x73, 0x26, 0x3e, 0x20, 0xf1, 0x66, 0xaa, 0x07, 0x13, 0xb5, + 0x58, 0x04, 0x11, 0xef, 0x05, 0x82, 0x8a, 0xd7, 0xa4, 0xe3, 0xd0, 0xf8, 0xfd, 0x93, 0xef, 0x8b, 0x48, 0x6a, 0xc3, + 0x6c, 0x47, 0x45, 0xdb, 0x8e, 0xef, 0xc6, 0x7d, 0xd5, 0x75, 0x9d, 0x4c, 0x37, 0xc1, 0xe6, 0xeb, 0xc3, 0xbe, 0x87, + 0x1e, 0x6b, 0x75, 0xa0, 0xd6, 0x3a, 0xfc, 0x94, 0x7a, 0x6d, 0xf7, 0x99, 0xab, 0x9b, 0xa4, 0x6a, 0xa7, 0xe0, 0xb6, + 0x49, 0x74, 0xc3, 0xe2, 0xcf, 0x9e, 0x4a, 0xb1, 0xf1, 0xfd, 0xc6, 0x73, 0xe4, 0x3a, 0x80, 0x84, 0xd3, 0x68, 0xf5, + 0x09, 0x53, 0xe8, 0xe8, 0xa6, 0x3e, 0x09, 0xa2, 0x84, 0xa9, 0x73, 0x20, 0x26, 0xc8, 0x67, 0x4e, 0xe2, 0xc7, 0xb7, + 0x2f, 0xdf, 0xbd, 0xd3, 0x4d, 0x8c, 0x20, 0x9a, 0xa8, 0xad, 0xf3, 0x0d, 0xb5, 0x03, 0xfb, 0xd7, 0xee, 0x3b, 0xba, + 0x61, 0xe8, 0x51, 0x5b, 0xdc, 0x73, 0x94, 0x56, 0xd9, 0x72, 0xfc, 0xe6, 0xe1, 0x3d, 0xd3, 0x4b, 0x74, 0xaf, 0x79, + 0xd5, 0xe0, 0x86, 0xed, 0xd7, 0x5b, 0x21, 0x65, 0xe9, 0x87, 0xd7, 0x35, 0xa9, 0xde, 0x5d, 0x4d, 0x2a, 0x3c, 0xe5, + 0x2a, 0xb8, 0x6a, 0x1d, 0x2d, 0x15, 0xd2, 0x00, 0x02, 0x40, 0xef, 0x02, 0x97, 0xf2, 0x9e, 0xfa, 0x8c, 0x41, 0x73, + 0x0f, 0xf0, 0xe5, 0x51, 0xd7, 0x24, 0xf3, 0x47, 0x90, 0x84, 0xed, 0x24, 0x00, 0x45, 0x41, 0xa6, 0x4a, 0xe5, 0x8a, + 0x64, 0x23, 0x57, 0xf3, 0x1d, 0x96, 0x28, 0x74, 0xaa, 0x46, 0x02, 0x10, 0x8e, 0xdf, 0x57, 0xde, 0x14, 0xb4, 0xef, + 0xac, 0x71, 0x94, 0xa6, 0xd1, 0xb2, 0xef, 0x3a, 0xab, 0x3b, 0x5d, 0x1b, 0x08, 0xc6, 0x03, 0x57, 0x0e, 0xec, 0xff, + 0xf6, 0xef, 0x12, 0xca, 0xa5, 0xf4, 0xeb, 0x94, 0x2d, 0x57, 0x2c, 0xf6, 0xd2, 0x75, 0xcc, 0x32, 0xed, 0xb7, 0xff, + 0x7b, 0x5e, 0x7a, 0x64, 0x0f, 0xd4, 0x3a, 0x44, 0x5e, 0xab, 0x55, 0xae, 0x83, 0xe8, 0xf6, 0x41, 0x6e, 0x06, 0xb0, + 0xa3, 0xf0, 0xc2, 0x9f, 0x2f, 0x64, 0xe9, 0xb3, 0x74, 0xcb, 0xdc, 0xc4, 0xe8, 0x89, 0xe9, 0xae, 0x9d, 0x47, 0xb7, + 0xfd, 0xdf, 0xfe, 0x2d, 0x99, 0x27, 0x3b, 0x77, 0x5d, 0xfd, 0x40, 0x8b, 0x2b, 0x5a, 0x5f, 0xa6, 0xb2, 0xc4, 0x90, + 0x5f, 0x59, 0xe0, 0x4a, 0x22, 0xed, 0xca, 0xaa, 0x84, 0x6b, 0xcb, 0x9c, 0xfe, 0xea, 0xcf, 0x17, 0x9f, 0x3b, 0x29, + 0x00, 0xe8, 0xce, 0x59, 0x41, 0xa1, 0x2f, 0x30, 0xad, 0x51, 0x7f, 0xff, 0x05, 0xfb, 0xcc, 0x79, 0xed, 0x9a, 0xd2, + 0x97, 0x98, 0x0d, 0xe7, 0xa2, 0x3e, 0x1f, 0x8d, 0x64, 0x04, 0x3d, 0xb5, 0x3e, 0x18, 0x32, 0x9c, 0x55, 0x52, 0xf8, + 0x55, 0xdf, 0x77, 0x0c, 0xf2, 0x30, 0xb0, 0x07, 0x40, 0x50, 0x25, 0xaf, 0x06, 0x1c, 0xcd, 0xf8, 0x9a, 0x34, 0xeb, + 0x2b, 0x7d, 0x57, 0x90, 0x35, 0xa4, 0x62, 0xf4, 0x35, 0x29, 0x9a, 0x33, 0xeb, 0x87, 0x73, 0x1b, 0x7b, 0x2b, 0x62, + 0xd8, 0x6b, 0x28, 0x8f, 0x00, 0x06, 0x88, 0x78, 0xd1, 0x62, 0x70, 0x97, 0x37, 0x4d, 0x0a, 0x71, 0x3f, 0xee, 0x56, + 0x88, 0xbb, 0xd8, 0x4b, 0x21, 0xee, 0xc7, 0x2f, 0xae, 0x10, 0xf7, 0x46, 0x55, 0x88, 0x83, 0xb5, 0x7c, 0xc9, 0xf6, + 0xd2, 0x52, 0x13, 0xaa, 0x26, 0xd1, 0x6d, 0x32, 0x74, 0x39, 0x1d, 0x9e, 0x4c, 0x16, 0x0c, 0x18, 0x1b, 0x1c, 0xea, + 0x41, 0x34, 0x07, 0x8d, 0xb5, 0x3f, 0x5e, 0xb7, 0x2c, 0x88, 0xe6, 0xaa, 0x66, 0x59, 0xc8, 0xdd, 0xdb, 0xe6, 0x2e, + 0xab, 0x48, 0x9b, 0xcb, 0x31, 0x85, 0x83, 0x2b, 0xeb, 0xd0, 0x50, 0x42, 0x78, 0x4b, 0x55, 0xbd, 0xb6, 0xd0, 0xf7, + 0xea, 0xa3, 0xaa, 0x98, 0xac, 0xd8, 0x7e, 0x2a, 0x1c, 0x79, 0xa8, 0x2d, 0x48, 0x95, 0x68, 0x72, 0x8a, 0xb1, 0xd1, + 0x7f, 0xb9, 0x73, 0xbf, 0xbb, 0x74, 0x07, 0x1d, 0x17, 0x2c, 0xd1, 0xe1, 0x59, 0x8c, 0x09, 0xce, 0xa0, 0xd3, 0x81, + 0x84, 0x5b, 0x25, 0xa1, 0x0d, 0x09, 0xbe, 0x92, 0xd0, 0x85, 0x84, 0x89, 0x92, 0x70, 0x04, 0x09, 0x53, 0x25, 0xe1, + 0x18, 0x12, 0x6e, 0xf4, 0xec, 0x32, 0x94, 0xc3, 0x3d, 0x36, 0xae, 0x4c, 0x7a, 0x09, 0x89, 0xb4, 0x63, 0xd3, 0x05, + 0x15, 0x31, 0x6f, 0xde, 0x8f, 0x4c, 0x62, 0x89, 0xf6, 0x63, 0xf3, 0x76, 0xc1, 0xc8, 0x2b, 0xf6, 0x0b, 0xbc, 0x28, + 0xed, 0x34, 0x02, 0x25, 0x71, 0xe1, 0x6d, 0x42, 0xc0, 0x41, 0xd3, 0x0d, 0xe0, 0x72, 0x0d, 0xe4, 0xca, 0x09, 0x8f, + 0x1d, 0xca, 0x5a, 0xe6, 0x79, 0xd4, 0x9d, 0x25, 0xb7, 0x40, 0xae, 0x26, 0xd3, 0x52, 0x59, 0xa9, 0x5f, 0x42, 0x59, + 0xe2, 0x05, 0x1b, 0xaf, 0xe7, 0xda, 0x79, 0x34, 0xdf, 0xa9, 0xf7, 0xa0, 0x66, 0xc1, 0x28, 0x75, 0x92, 0x19, 0x59, + 0x62, 0x5b, 0xf2, 0xbe, 0xe8, 0x33, 0x2b, 0x96, 0x4f, 0x61, 0x6c, 0x5a, 0x4a, 0x18, 0x07, 0xfa, 0x01, 0x18, 0x29, + 0x8a, 0x07, 0xe7, 0x00, 0x67, 0xe5, 0xfb, 0xc2, 0x53, 0xc6, 0x73, 0xfa, 0x3d, 0x4b, 0x12, 0x6f, 0x2e, 0xca, 0x57, + 0xc7, 0x09, 0x9a, 0x46, 0xf2, 0xd1, 0x88, 0x00, 0x04, 0xf6, 0xa3, 0x5f, 0x51, 0x28, 0x89, 0xa3, 0x5b, 0x0d, 0x54, + 0x96, 0x60, 0x43, 0xe5, 0xca, 0x15, 0xbe, 0x0d, 0x4b, 0x58, 0x54, 0x83, 0x80, 0xc3, 0x7f, 0xc3, 0x82, 0x72, 0x62, + 0xea, 0xcd, 0xcb, 0x49, 0xb4, 0x0f, 0x32, 0x75, 0x6c, 0x52, 0x0b, 0xa1, 0x90, 0xf8, 0x39, 0x62, 0xf5, 0x20, 0x9a, + 0xff, 0xa1, 0x32, 0xf5, 0x2d, 0xba, 0x10, 0xef, 0x42, 0xd3, 0x4f, 0x47, 0x36, 0xc2, 0x58, 0xb3, 0x01, 0x84, 0xfd, + 0x30, 0x5d, 0x58, 0x68, 0x47, 0xd7, 0x6a, 0x87, 0x86, 0x69, 0xe3, 0xda, 0x6e, 0xca, 0xd6, 0xc3, 0xfd, 0x78, 0x3e, + 0xf6, 0x5a, 0x6e, 0xfb, 0xd8, 0x14, 0x7f, 0x6c, 0xa7, 0x6b, 0x64, 0xd8, 0x82, 0x36, 0xf5, 0x6f, 0x36, 0xb3, 0x28, + 0x4c, 0xad, 0x99, 0xb7, 0xf4, 0x83, 0xfb, 0xfe, 0x32, 0x0a, 0xa3, 0x64, 0xe5, 0x4d, 0xd8, 0x20, 0xe7, 0x02, 0x0c, + 0xd0, 0x2f, 0x05, 0x37, 0x8d, 0x74, 0xed, 0x76, 0xcc, 0x96, 0x54, 0x5b, 0xba, 0x9d, 0x98, 0x05, 0xec, 0x2e, 0xe3, + 0xdd, 0x17, 0x0a, 0x53, 0x51, 0xdc, 0x72, 0x54, 0x03, 0xc8, 0x68, 0xee, 0xd3, 0x02, 0x3c, 0x39, 0x0d, 0x38, 0x2d, + 0xda, 0xb7, 0xdb, 0xdd, 0x98, 0x2d, 0x35, 0xbb, 0xdb, 0xd8, 0x78, 0x1c, 0xdd, 0x9e, 0xc2, 0x68, 0xb1, 0xb2, 0x95, + 0xb0, 0x60, 0x86, 0x39, 0x16, 0x9a, 0xd1, 0x88, 0x76, 0x2c, 0xe4, 0x1e, 0x40, 0x6b, 0x6c, 0x39, 0x80, 0xec, 0x7e, + 0x5b, 0x73, 0x06, 0x4b, 0x3f, 0xb4, 0x68, 0x3a, 0xc7, 0xce, 0x4a, 0x69, 0x4b, 0x85, 0x9f, 0xb1, 0xc1, 0xe2, 0xae, + 0xe6, 0x0c, 0xe0, 0x85, 0x39, 0x0b, 0xa2, 0xdb, 0xfe, 0xc2, 0x9f, 0x4e, 0x59, 0x38, 0xc0, 0x31, 0xcb, 0x44, 0x16, + 0x04, 0xfe, 0x2a, 0xf1, 0x93, 0xc1, 0xd2, 0xbb, 0xe3, 0xad, 0x1e, 0x36, 0xb5, 0xda, 0xe1, 0xad, 0x76, 0xf6, 0x6e, + 0x55, 0x69, 0x06, 0x4c, 0x76, 0xa8, 0x1d, 0x3e, 0xb4, 0xae, 0xe6, 0x94, 0xe6, 0xb9, 0x77, 0xab, 0xab, 0x98, 0x6d, + 0x96, 0x5e, 0x3c, 0xf7, 0xc3, 0xbe, 0x93, 0xd9, 0x37, 0x1b, 0xda, 0x18, 0x0f, 0x7b, 0xbd, 0x5e, 0x66, 0x4f, 0xc5, + 0x97, 0x33, 0x9d, 0x66, 0xf6, 0x44, 0x7c, 0xcd, 0x66, 0x8e, 0x33, 0x9b, 0x65, 0xb6, 0x2f, 0x12, 0x3a, 0xed, 0xc9, + 0xb4, 0xd3, 0xce, 0xec, 0x5b, 0xa5, 0x44, 0x66, 0x33, 0xfe, 0x15, 0xb3, 0xe9, 0x00, 0x37, 0x12, 0xe9, 0xd0, 0xf6, + 0x8f, 0x1d, 0x27, 0x43, 0x0c, 0x70, 0x59, 0xc0, 0x4d, 0xc8, 0xa0, 0xba, 0xda, 0xec, 0x5d, 0x52, 0xcb, 0xbb, 0x9b, + 0x4c, 0x6a, 0xcb, 0x4d, 0xbd, 0xf8, 0xc3, 0x95, 0xa6, 0xcc, 0xc2, 0xf3, 0xa8, 0xd8, 0x46, 0x80, 0xc1, 0xba, 0xeb, + 0x83, 0x7f, 0xb2, 0xc1, 0x38, 0x8a, 0xe1, 0xcc, 0xc6, 0xde, 0xd4, 0x5f, 0x27, 0x7d, 0xb7, 0xbd, 0xba, 0x13, 0x49, + 0x7c, 0xaf, 0xe7, 0x09, 0x78, 0xf6, 0xfa, 0x49, 0x14, 0xf8, 0x53, 0x91, 0xd4, 0x74, 0x96, 0xdc, 0xb6, 0x31, 0x40, + 0xeb, 0x7c, 0x1f, 0x7d, 0x4c, 0x78, 0x41, 0xa0, 0xd9, 0x9d, 0x44, 0x63, 0x5e, 0x82, 0x4c, 0x71, 0xcd, 0x49, 0x08, + 0x2e, 0x68, 0x89, 0xef, 0x1e, 0xae, 0xee, 0xe4, 0x9e, 0x77, 0x8f, 0x56, 0x77, 0xd9, 0x37, 0x4b, 0x36, 0xf5, 0x3d, + 0xad, 0x95, 0xef, 0x26, 0xd7, 0x01, 0xc6, 0xb9, 0xb1, 0x69, 0xd8, 0xa6, 0xe2, 0x58, 0x80, 0x1f, 0xc7, 0x07, 0xfe, + 0x72, 0x15, 0xc5, 0xa9, 0x17, 0xa6, 0x59, 0x36, 0xba, 0xca, 0xb2, 0xc1, 0x85, 0xdf, 0xba, 0xfc, 0x47, 0x8b, 0xee, + 0x69, 0x12, 0x34, 0x65, 0xc6, 0x95, 0xf9, 0x92, 0xa9, 0x8a, 0x2e, 0x70, 0x8d, 0xa1, 0x92, 0x8b, 0x5a, 0x98, 0x6e, + 0xc9, 0x6a, 0x61, 0x02, 0xb2, 0x2c, 0x4e, 0x8a, 0x33, 0xc5, 0x22, 0x78, 0x03, 0x41, 0x81, 0x97, 0x6c, 0x78, 0xa1, + 0x28, 0xcd, 0x00, 0xb1, 0x82, 0x85, 0xc9, 0x88, 0xe2, 0x51, 0x13, 0xcd, 0xf8, 0xed, 0x6e, 0x9a, 0xf1, 0xe7, 0x74, + 0x1f, 0x9a, 0xf1, 0xdb, 0x2f, 0x4e, 0x33, 0x3e, 0xaa, 0x1a, 0x51, 0xbc, 0x8e, 0x86, 0xba, 0x14, 0x8b, 0xc0, 0xd5, + 0x14, 0x93, 0x7b, 0xa2, 0xd7, 0xbf, 0xdb, 0xe6, 0x41, 0xb4, 0x46, 0x01, 0xf7, 0xe8, 0xe6, 0x06, 0x26, 0xf2, 0x9b, + 0x70, 0xf8, 0xf7, 0x58, 0xfd, 0x9e, 0xcd, 0x86, 0x2f, 0x22, 0x25, 0x41, 0x7e, 0x71, 0x8d, 0x91, 0x82, 0x2b, 0x09, + 0xca, 0x11, 0xaa, 0xa3, 0x18, 0x6c, 0x03, 0x2c, 0xd1, 0x49, 0x55, 0x7a, 0x2a, 0x55, 0xe6, 0x06, 0xc5, 0x21, 0xb4, + 0xa4, 0x9e, 0xaa, 0xb0, 0x37, 0xaa, 0xf0, 0x3f, 0xe7, 0x2c, 0xe5, 0x06, 0xc2, 0xdf, 0xdd, 0xbf, 0x9e, 0xb6, 0x5e, + 0x47, 0x46, 0xe6, 0x27, 0x6f, 0xca, 0xd6, 0x3e, 0x5c, 0x60, 0x35, 0x54, 0xa7, 0x93, 0x71, 0xb5, 0x37, 0x35, 0x9a, + 0x36, 0x64, 0x53, 0xf5, 0xb3, 0xc2, 0x4c, 0xfb, 0x6a, 0x45, 0x1e, 0xd5, 0xab, 0x72, 0x19, 0x73, 0x53, 0x8b, 0x0d, + 0xa7, 0x00, 0x31, 0x50, 0x19, 0x1a, 0x49, 0x4f, 0xa9, 0xba, 0x3f, 0xcd, 0x32, 0x63, 0x20, 0x00, 0xa1, 0x5c, 0xb4, + 0x6c, 0x17, 0x11, 0x97, 0xe4, 0xef, 0x31, 0x2e, 0xd6, 0x24, 0x99, 0xe5, 0x6b, 0xd0, 0x02, 0xe0, 0x12, 0x4e, 0x0e, + 0x33, 0x5d, 0x23, 0xf0, 0x91, 0x76, 0x88, 0x32, 0x21, 0x10, 0x5b, 0x4b, 0xf8, 0x8b, 0x2c, 0x91, 0x50, 0x55, 0x3c, + 0x25, 0xe0, 0xa0, 0x1a, 0x03, 0xb8, 0x34, 0x10, 0xcc, 0x1a, 0x42, 0x3b, 0xbc, 0x0c, 0x7e, 0x64, 0xba, 0xa4, 0xfd, + 0x70, 0xfb, 0x9d, 0x9e, 0x1c, 0x40, 0x85, 0xd3, 0x12, 0x23, 0x66, 0x87, 0x5a, 0x25, 0x90, 0x12, 0xc9, 0xad, 0x69, + 0x27, 0xb7, 0xda, 0x93, 0x8d, 0x70, 0x07, 0x92, 0x7a, 0x2b, 0x0b, 0x5e, 0xff, 0x88, 0x7b, 0x39, 0xc6, 0x53, 0x3c, + 0x8f, 0x0c, 0xd6, 0x09, 0xe0, 0x46, 0x7c, 0x88, 0x22, 0xfe, 0x19, 0x4c, 0xd6, 0x71, 0x12, 0xc5, 0xfd, 0x55, 0xe4, + 0x87, 0x29, 0x8b, 0x33, 0x04, 0xd5, 0x25, 0xc2, 0x47, 0x80, 0xe7, 0x6a, 0x13, 0xad, 0xbc, 0x89, 0x9f, 0xde, 0xf7, + 0x1d, 0x4e, 0x52, 0x38, 0x03, 0x4e, 0x1d, 0x38, 0xb5, 0xe5, 0xfb, 0x1c, 0x9a, 0x4f, 0x91, 0xf0, 0x8b, 0xab, 0xe4, + 0x8c, 0xba, 0xcd, 0x07, 0x4a, 0x2e, 0x39, 0x44, 0x01, 0xf2, 0xc3, 0x8b, 0xad, 0x39, 0x60, 0x79, 0x58, 0x6a, 0x67, + 0xca, 0xe6, 0x26, 0x62, 0x6d, 0x10, 0x26, 0x88, 0x3f, 0x76, 0xd7, 0xd0, 0x9c, 0xfa, 0x64, 0xa0, 0x78, 0x8c, 0x7d, + 0x46, 0xd6, 0xf7, 0x20, 0x7c, 0x98, 0xb9, 0x4f, 0xc9, 0x31, 0x9b, 0x45, 0x31, 0x23, 0xe7, 0xb9, 0x6e, 0x6f, 0x75, + 0xb7, 0x7f, 0xf3, 0xdb, 0xa7, 0x5f, 0xdf, 0x4e, 0x18, 0xa5, 0x2d, 0xd1, 0x98, 0xb1, 0xa3, 0xb5, 0xea, 0x7d, 0x06, + 0xa4, 0x21, 0x41, 0x7e, 0x42, 0x7e, 0xca, 0xfa, 0xba, 0x3e, 0xa8, 0xf5, 0x51, 0xb6, 0x8a, 0xf8, 0x9d, 0x17, 0xb3, + 0xc0, 0x4b, 0xfd, 0x1b, 0x41, 0x33, 0x76, 0x8e, 0x56, 0x77, 0x62, 0x8d, 0xf1, 0xc2, 0xfb, 0x84, 0x45, 0x2a, 0x0d, + 0x45, 0x2c, 0x52, 0x39, 0x19, 0x17, 0x69, 0x50, 0x99, 0x8d, 0x70, 0xdb, 0x51, 0xba, 0xe9, 0xbb, 0xab, 0x3b, 0xf5, + 0x8a, 0xce, 0xab, 0xc9, 0x9b, 0xba, 0xec, 0x6f, 0x6d, 0xe9, 0x4f, 0xa7, 0x01, 0xcb, 0x0a, 0x0b, 0x5d, 0x5c, 0x4b, + 0x05, 0x38, 0x12, 0x0e, 0xde, 0x38, 0x89, 0x82, 0x75, 0xca, 0xea, 0xc1, 0x45, 0xc0, 0x69, 0x3b, 0x39, 0x70, 0xf0, + 0x77, 0x71, 0xac, 0x5d, 0x20, 0xb7, 0x61, 0x9b, 0x38, 0x03, 0x70, 0xaf, 0x6c, 0x75, 0x8a, 0x43, 0x87, 0x2c, 0x39, + 0x68, 0xb3, 0x66, 0x22, 0x26, 0x5c, 0x4b, 0x84, 0xbd, 0x35, 0xdb, 0xe5, 0x69, 0xd2, 0xc5, 0xac, 0x4c, 0xca, 0x8a, + 0x93, 0xf9, 0x63, 0xce, 0xd8, 0xb3, 0xfa, 0x33, 0xf6, 0x4c, 0x9c, 0xb1, 0xed, 0x3b, 0xf3, 0xe1, 0xcc, 0x85, 0xff, + 0x06, 0xf9, 0x84, 0xfa, 0x8e, 0xd6, 0x59, 0xdd, 0x69, 0xee, 0xea, 0x4e, 0xb3, 0xda, 0xab, 0x3b, 0x0d, 0x9b, 0x46, + 0x25, 0x16, 0xd3, 0x6e, 0x1b, 0xa6, 0xa3, 0x41, 0x22, 0xfc, 0x71, 0x0a, 0x59, 0xee, 0x21, 0xe4, 0x41, 0xad, 0x6e, + 0x35, 0xaf, 0xbd, 0xfd, 0xa8, 0xd3, 0x59, 0x12, 0x48, 0xdb, 0xb0, 0x53, 0x6f, 0x3c, 0x66, 0xd3, 0xfe, 0x2c, 0x9a, + 0xac, 0x93, 0x7f, 0xf1, 0xf1, 0x73, 0x20, 0x6e, 0x45, 0x04, 0xa5, 0x76, 0x44, 0x55, 0x90, 0xee, 0xdc, 0x30, 0xd1, + 0xc2, 0x46, 0xae, 0x53, 0x9f, 0x7c, 0x41, 0xb7, 0xed, 0xc3, 0x9a, 0x4d, 0x5e, 0x0f, 0xe8, 0x3f, 0x6c, 0x95, 0x9a, + 0x51, 0xcc, 0x67, 0x80, 0x65, 0x2b, 0x38, 0x3e, 0x1d, 0x1a, 0x7c, 0x35, 0x9d, 0x5e, 0xfd, 0x70, 0x2f, 0x45, 0x4f, + 0x57, 0xe2, 0x52, 0xe1, 0xf7, 0x16, 0xb7, 0xa6, 0xd9, 0xde, 0x6a, 0xd3, 0x1e, 0xa9, 0xb4, 0xba, 0xe5, 0x42, 0xc8, + 0xcb, 0xee, 0x89, 0xe5, 0x1f, 0x3e, 0x3b, 0x84, 0xff, 0x88, 0xaa, 0xff, 0x39, 0xad, 0x23, 0xd4, 0x5f, 0x17, 0xd5, + 0xd7, 0x89, 0x54, 0x42, 0x42, 0x7c, 0xff, 0xf2, 0xb3, 0xd9, 0xa7, 0x55, 0xd8, 0xbb, 0x34, 0xe9, 0x7f, 0x95, 0x4b, + 0x7f, 0x17, 0x45, 0x10, 0xa7, 0xb4, 0x5a, 0x5c, 0x80, 0x87, 0x34, 0xf4, 0xd3, 0x21, 0x54, 0x12, 0xef, 0x08, 0x52, + 0x3d, 0xd0, 0xb1, 0x0e, 0x3d, 0x25, 0x5e, 0x36, 0x3d, 0x25, 0x5e, 0xec, 0x7e, 0x4a, 0xfc, 0x6d, 0xaf, 0xa7, 0xc4, + 0x8b, 0x2f, 0xfe, 0x94, 0x78, 0x59, 0x7d, 0x4a, 0x5c, 0x44, 0x42, 0xe9, 0xd7, 0x7c, 0xbd, 0xe6, 0x3f, 0xdf, 0x93, + 0x24, 0xf1, 0x3c, 0x1a, 0x76, 0x1d, 0xf2, 0xef, 0x7c, 0xf1, 0xbb, 0x1f, 0x16, 0xb8, 0x11, 0xdf, 0xa2, 0x0e, 0x5c, + 0xfe, 0xb4, 0xe0, 0x98, 0x1d, 0xfb, 0x51, 0x92, 0x83, 0x28, 0x9c, 0xff, 0x08, 0x92, 0x64, 0x60, 0x07, 0xc6, 0x4a, + 0x86, 0x9f, 0xfc, 0x18, 0xad, 0xd6, 0xab, 0xd7, 0xd0, 0xd6, 0x7b, 0x3f, 0xf1, 0xc7, 0x01, 0x93, 0x66, 0xd7, 0xa4, + 0xb3, 0xc7, 0x79, 0xe2, 0xa0, 0x26, 0x2b, 0x7e, 0x7a, 0x77, 0xe2, 0x27, 0x2a, 0xd2, 0xf2, 0xdf, 0xa4, 0x0c, 0xa8, + 0xd7, 0x3f, 0x44, 0xc0, 0x41, 0x51, 0x69, 0xd0, 0x9f, 0xfe, 0x18, 0xb9, 0x88, 0x8c, 0x9a, 0x59, 0x0a, 0x25, 0x8d, + 0xc6, 0x76, 0x58, 0xe5, 0x51, 0xb3, 0x36, 0x4c, 0xe9, 0x6f, 0xac, 0xca, 0x86, 0x5f, 0x46, 0xeb, 0x84, 0x4d, 0xa3, + 0xdb, 0x50, 0x37, 0x43, 0x69, 0x19, 0x01, 0x62, 0x59, 0x59, 0x07, 0x23, 0x65, 0xbe, 0x43, 0x42, 0x39, 0x8a, 0x5b, + 0x3a, 0x04, 0x6a, 0x5d, 0xaf, 0x2c, 0x92, 0x8f, 0x5b, 0x38, 0x45, 0x5d, 0x86, 0x74, 0x7a, 0xd0, 0x6a, 0x45, 0xc3, + 0x4f, 0xab, 0x29, 0xf4, 0x4b, 0x22, 0x9b, 0x73, 0x85, 0x93, 0x56, 0x28, 0x98, 0x8b, 0xc2, 0xe9, 0x47, 0xcd, 0xc2, + 0xf1, 0x1c, 0xb2, 0xb7, 0xcd, 0x73, 0xc1, 0x65, 0x4a, 0xb6, 0xe6, 0xeb, 0xc1, 0x5d, 0x60, 0xd0, 0xe7, 0x73, 0x05, + 0x8c, 0x6f, 0x6e, 0x58, 0x1c, 0x78, 0xf7, 0x2d, 0x23, 0x8b, 0xc2, 0xef, 0x01, 0x00, 0x2f, 0xa2, 0xdb, 0x50, 0x2d, + 0x80, 0x91, 0x69, 0x6a, 0xf6, 0x52, 0xad, 0xb3, 0x16, 0xb0, 0xb6, 0x51, 0x46, 0x00, 0x31, 0x81, 0xe7, 0xec, 0xef, + 0x26, 0xfd, 0xfb, 0x0f, 0x23, 0x33, 0xcf, 0x23, 0xd9, 0xd1, 0x4f, 0xab, 0x3d, 0xba, 0x79, 0xfc, 0xf8, 0x41, 0xf3, + 0xb4, 0x8b, 0xb1, 0xe8, 0x6b, 0x6a, 0x1b, 0x8d, 0xa7, 0x00, 0x46, 0x71, 0x11, 0xad, 0x27, 0x0b, 0xd4, 0xce, 0xfd, + 0x72, 0xf3, 0x4d, 0xa1, 0x4d, 0x0c, 0xc9, 0x2a, 0xa7, 0x5e, 0x4a, 0xca, 0xa1, 0x80, 0xfd, 0xbf, 0x04, 0x6f, 0xa3, + 0xff, 0x41, 0x30, 0x54, 0x77, 0x0d, 0x7f, 0xc5, 0xfb, 0x9f, 0xb6, 0x79, 0x07, 0x10, 0x39, 0x94, 0xfb, 0xf1, 0x10, + 0xc2, 0xb5, 0x7a, 0x24, 0x93, 0x95, 0x81, 0xa6, 0xfa, 0xcc, 0x6b, 0x72, 0x07, 0x28, 0x7a, 0x61, 0x36, 0x3d, 0xd3, + 0xb9, 0x75, 0x84, 0xc9, 0x38, 0xb6, 0x2a, 0x21, 0x19, 0xae, 0xa7, 0xc1, 0x10, 0x7d, 0x95, 0xf3, 0x96, 0x7e, 0x68, + 0xa2, 0xcb, 0xfb, 0x6a, 0x8e, 0x77, 0x07, 0x4e, 0x9f, 0x01, 0xb9, 0x95, 0xb3, 0x20, 0xd1, 0x54, 0x8d, 0xfd, 0x20, + 0xae, 0x95, 0x5e, 0x0b, 0x09, 0x21, 0xc5, 0x1b, 0x7d, 0xa5, 0x69, 0x9a, 0x26, 0x9f, 0x11, 0x9a, 0x7c, 0x47, 0x60, + 0x3a, 0x3e, 0x07, 0x40, 0x5a, 0x92, 0xad, 0xee, 0x28, 0x05, 0x5e, 0x06, 0x28, 0x99, 0x15, 0x09, 0xdc, 0xaf, 0x61, + 0xd7, 0x11, 0x09, 0xe2, 0x41, 0x0f, 0x3e, 0xe9, 0xbc, 0x18, 0xdc, 0x1f, 0xf7, 0x35, 0x7c, 0xb0, 0x63, 0x2e, 0xe7, + 0x04, 0x6b, 0x0e, 0x7d, 0x8e, 0x06, 0xac, 0xde, 0x01, 0x5e, 0xa8, 0x60, 0x41, 0x90, 0x3a, 0x94, 0xfc, 0x59, 0x9b, + 0xac, 0x06, 0x37, 0xe2, 0xbb, 0xe8, 0x2e, 0x5d, 0xb2, 0x70, 0xad, 0x63, 0x00, 0x2c, 0x74, 0x48, 0x08, 0x65, 0x5e, + 0x10, 0xb1, 0x05, 0xd8, 0xa6, 0xbe, 0xe6, 0x82, 0xee, 0xc2, 0x84, 0xa3, 0x54, 0xcf, 0x9c, 0x70, 0xc1, 0x66, 0xc2, + 0x71, 0x5b, 0xf9, 0x86, 0xe0, 0x4b, 0x1a, 0x95, 0xad, 0xcf, 0x48, 0x7d, 0x1b, 0xda, 0x20, 0x57, 0x20, 0x9c, 0x5d, + 0x24, 0xc0, 0xde, 0xf2, 0xca, 0x8b, 0x26, 0x25, 0x32, 0x5e, 0x89, 0x51, 0x14, 0x1b, 0xd5, 0x66, 0xf8, 0x38, 0xc1, + 0x0b, 0x53, 0x63, 0x3b, 0x93, 0x4a, 0x3b, 0x0d, 0x93, 0xfe, 0xc0, 0xee, 0xe9, 0x22, 0x21, 0x50, 0x7d, 0x60, 0xf7, + 0xa0, 0xb0, 0xf8, 0x12, 0xb8, 0x29, 0xfa, 0x16, 0x74, 0x6d, 0x42, 0x5c, 0x83, 0x09, 0x78, 0xe6, 0xda, 0x72, 0x80, + 0x9c, 0x6c, 0x0b, 0x16, 0x47, 0x10, 0x43, 0x08, 0x6b, 0x71, 0x88, 0xb9, 0x5d, 0x42, 0xab, 0x16, 0xc6, 0x56, 0xcd, + 0xd1, 0x30, 0x9e, 0xb8, 0x8e, 0x73, 0x50, 0x29, 0x0f, 0x8c, 0xec, 0xba, 0xd2, 0x86, 0x99, 0x0e, 0x5d, 0xc7, 0xf2, + 0x9f, 0xd8, 0xed, 0x41, 0xe5, 0x8e, 0x56, 0x1c, 0x67, 0x8e, 0x90, 0xfd, 0x75, 0xfa, 0x68, 0xd3, 0xaa, 0x1c, 0x48, + 0xa3, 0xac, 0xe7, 0x8f, 0x63, 0xcb, 0x38, 0xff, 0x6b, 0x54, 0xbd, 0xfa, 0xc9, 0x6d, 0x27, 0x05, 0x71, 0x19, 0x81, + 0xeb, 0xe7, 0x16, 0x1c, 0xa3, 0xbf, 0x68, 0x4f, 0xb5, 0x16, 0x1d, 0x1f, 0xc3, 0x18, 0xc9, 0xd8, 0xe0, 0xc2, 0x10, + 0x4e, 0x6d, 0xa0, 0xd4, 0x63, 0x52, 0xc6, 0x70, 0xdc, 0xc9, 0x2c, 0xcb, 0x25, 0x7a, 0x5b, 0xa9, 0x05, 0x6c, 0xbf, + 0xe1, 0xfa, 0xb4, 0xc7, 0xe0, 0x4c, 0x01, 0x4a, 0x80, 0xa3, 0xf8, 0x9d, 0x0d, 0xae, 0x57, 0xc5, 0xe6, 0x8a, 0x97, + 0xe4, 0xfe, 0x8d, 0xe1, 0xa5, 0x83, 0x32, 0x34, 0xd9, 0x5e, 0xfd, 0x75, 0xf7, 0x89, 0x4d, 0xb2, 0x70, 0x5a, 0x6c, + 0xb0, 0x74, 0x7f, 0xed, 0xdf, 0x5c, 0x01, 0xa3, 0x40, 0x04, 0x85, 0xa8, 0x06, 0xa3, 0x64, 0x51, 0x88, 0x9b, 0x9f, + 0x8e, 0x9b, 0xbf, 0x17, 0x15, 0x83, 0x15, 0xc8, 0xfd, 0x99, 0xac, 0xa6, 0x30, 0xc5, 0xc1, 0x4d, 0x37, 0xda, 0x32, + 0xb7, 0x04, 0x21, 0xda, 0xb8, 0x13, 0x53, 0xa1, 0x08, 0x99, 0xd7, 0xf1, 0xb9, 0xe3, 0xcd, 0x7d, 0xb9, 0xd6, 0xfe, + 0x6e, 0xae, 0x75, 0xba, 0x8b, 0x6b, 0x4d, 0x36, 0x60, 0xa4, 0xbd, 0x23, 0x6d, 0xe1, 0x04, 0x71, 0xae, 0x5a, 0x13, + 0x16, 0x58, 0xdd, 0x68, 0x32, 0x26, 0x6a, 0x55, 0x5a, 0x23, 0xd5, 0x46, 0x64, 0x7f, 0x2b, 0x0f, 0x14, 0x21, 0x50, + 0x57, 0x79, 0xe3, 0x17, 0x39, 0x6f, 0x9c, 0x5e, 0x35, 0xb9, 0xf5, 0x8f, 0xa0, 0xfe, 0x15, 0xcb, 0x3a, 0xf9, 0x3a, + 0xc8, 0x2d, 0xec, 0xf2, 0x91, 0x2a, 0x36, 0x63, 0xf9, 0x43, 0x43, 0xb1, 0x44, 0x14, 0xaf, 0x8c, 0xa2, 0x41, 0x62, + 0xb1, 0x68, 0x6e, 0x32, 0x96, 0xa7, 0x03, 0xd7, 0x1d, 0x87, 0x2c, 0x93, 0xd5, 0x6d, 0x53, 0xb4, 0x19, 0x52, 0xb3, + 0x95, 0x4d, 0x22, 0x8d, 0x7b, 0x08, 0xc0, 0x82, 0x4d, 0x5f, 0x92, 0x6b, 0x4b, 0x1d, 0x08, 0x1c, 0x64, 0x8d, 0x2d, + 0xe2, 0x6e, 0xee, 0x3c, 0x05, 0x87, 0xc8, 0x45, 0xd7, 0x2e, 0xde, 0xee, 0x24, 0x09, 0x56, 0xf9, 0x11, 0x08, 0xeb, + 0x2b, 0x85, 0x83, 0xd0, 0x77, 0x34, 0x67, 0x50, 0x43, 0x00, 0xe0, 0xfd, 0x5f, 0xfe, 0xe6, 0xa4, 0x00, 0x70, 0x22, + 0x35, 0x47, 0x95, 0xf9, 0xe3, 0x21, 0xb6, 0x48, 0xfd, 0x18, 0x8b, 0x56, 0xfb, 0x24, 0x7e, 0xcf, 0x86, 0x5b, 0xfe, + 0x14, 0xd9, 0xf9, 0xbc, 0x44, 0x5f, 0x8c, 0x83, 0xef, 0xb2, 0x78, 0x1d, 0xa2, 0xcf, 0x7f, 0x2b, 0x8d, 0xbd, 0xc9, + 0x87, 0x8d, 0xd2, 0x1f, 0x67, 0x89, 0x02, 0xbb, 0xb8, 0x28, 0x54, 0x18, 0x78, 0xe8, 0x22, 0x93, 0xf5, 0xed, 0x76, + 0xa2, 0x30, 0x6a, 0xfa, 0x0f, 0x9d, 0x8e, 0xf7, 0x6c, 0x76, 0x58, 0xe2, 0x9f, 0xb6, 0xbb, 0x45, 0xee, 0xba, 0x1c, + 0xc7, 0x32, 0xfa, 0x95, 0xdb, 0x48, 0xfe, 0xf9, 0x5d, 0x27, 0xbc, 0xcf, 0xd2, 0x1a, 0x7d, 0xce, 0x10, 0xa0, 0x7e, + 0x41, 0x30, 0xad, 0x8a, 0x69, 0x2a, 0x29, 0x4d, 0xc3, 0x9a, 0xf9, 0x41, 0x60, 0x05, 0x60, 0xa9, 0xb2, 0xf9, 0xac, + 0xe9, 0x61, 0x3b, 0x6b, 0x70, 0xce, 0xfc, 0x19, 0xed, 0x14, 0x77, 0x4a, 0xba, 0x58, 0x2f, 0xc7, 0x1b, 0x95, 0x51, + 0xae, 0xf0, 0xcf, 0xab, 0x3c, 0x73, 0xb5, 0xdb, 0xd9, 0x6c, 0x56, 0xe4, 0x1a, 0x3b, 0xda, 0x21, 0x72, 0x7e, 0x1f, + 0x3a, 0x8e, 0x53, 0x86, 0x6f, 0xd3, 0x41, 0xa1, 0x83, 0x61, 0x21, 0x13, 0xbe, 0xb7, 0x7b, 0x4f, 0xfd, 0x49, 0xa3, + 0xa5, 0xa6, 0x9a, 0xce, 0x23, 0x6d, 0xb5, 0xff, 0x8a, 0xa1, 0x20, 0x6a, 0xd8, 0x75, 0xfc, 0xab, 0x7b, 0x65, 0x4b, + 0x4b, 0xe5, 0x03, 0xfc, 0x69, 0x95, 0x77, 0xec, 0xf5, 0x3d, 0xaa, 0x36, 0x6d, 0xef, 0xcc, 0xce, 0xaf, 0xdd, 0x82, + 0xce, 0xd2, 0x80, 0x34, 0x95, 0xfc, 0x94, 0x2d, 0x93, 0xfe, 0x84, 0xa1, 0x80, 0xd4, 0x56, 0x6e, 0x5b, 0xd4, 0xea, + 0xb1, 0xe6, 0xa0, 0xc7, 0xe5, 0x0a, 0x3c, 0xec, 0x68, 0x28, 0xac, 0xaa, 0x48, 0xd6, 0x44, 0x27, 0x78, 0x8b, 0x6d, + 0xaa, 0x02, 0x27, 0xdc, 0xa6, 0x5d, 0xe7, 0x2f, 0x85, 0x72, 0x1a, 0x50, 0xa7, 0x1b, 0xa1, 0x6d, 0x42, 0xc2, 0x13, + 0xfc, 0x5b, 0x0a, 0xe7, 0x9e, 0xad, 0xee, 0x8a, 0xca, 0x5d, 0x3d, 0x10, 0x37, 0xe5, 0x57, 0x19, 0x8d, 0xba, 0x0e, + 0xf5, 0x49, 0x15, 0xa0, 0x99, 0xaa, 0xdd, 0x02, 0x1a, 0x34, 0x85, 0x50, 0x44, 0x35, 0xb2, 0x31, 0x7c, 0xce, 0xc2, + 0xce, 0xcb, 0xf9, 0xfb, 0x79, 0x20, 0x4d, 0x98, 0x83, 0xf9, 0xb4, 0x87, 0xc2, 0xbd, 0xc2, 0x56, 0x45, 0x55, 0x19, + 0xdc, 0x03, 0xe2, 0x45, 0xaa, 0xad, 0xe3, 0xc0, 0xa2, 0x38, 0x3d, 0x2d, 0x63, 0x53, 0x9d, 0x77, 0x73, 0xf3, 0x6e, + 0x17, 0xe4, 0x1a, 0x55, 0x50, 0xed, 0x25, 0xda, 0x2b, 0xcb, 0xb0, 0xc5, 0x38, 0x61, 0x05, 0xc0, 0x92, 0x42, 0x43, + 0xa5, 0x21, 0xad, 0x84, 0xfb, 0x68, 0xd2, 0x32, 0x57, 0x45, 0xd6, 0x62, 0x9e, 0xd8, 0x5c, 0x7d, 0x11, 0x6a, 0x5b, + 0x48, 0x06, 0x01, 0x76, 0x1c, 0x3b, 0xe1, 0xb7, 0x05, 0x3b, 0x46, 0x45, 0x57, 0x2e, 0xee, 0x20, 0x3c, 0xa5, 0x16, + 0xd2, 0xc9, 0x09, 0x9d, 0x52, 0x94, 0x25, 0xfc, 0xad, 0x96, 0x79, 0x7f, 0x51, 0xe0, 0xc6, 0x73, 0x73, 0x96, 0xb6, + 0xb1, 0x57, 0xe9, 0xa5, 0x1f, 0xee, 0x5f, 0xd6, 0xbb, 0xdb, 0xbb, 0x2c, 0x10, 0x87, 0x7b, 0x17, 0x06, 0xea, 0x92, + 0xb4, 0x94, 0xd2, 0xe1, 0xdf, 0x14, 0xe1, 0x81, 0xea, 0x16, 0x41, 0xc7, 0x5a, 0xf4, 0xa2, 0xbf, 0x58, 0x0f, 0x47, + 0x27, 0x67, 0x77, 0xcb, 0x40, 0xbb, 0x61, 0x31, 0xc4, 0x2c, 0x1b, 0xea, 0xae, 0xed, 0xe8, 0x1a, 0x1a, 0xf9, 0xfb, + 0xe1, 0x7c, 0xa8, 0xff, 0x74, 0xf1, 0xca, 0xea, 0xe9, 0x67, 0xa0, 0x8e, 0x71, 0x33, 0x47, 0x12, 0xf7, 0xdc, 0xbb, + 0x67, 0xf1, 0x75, 0x5b, 0xd7, 0x30, 0x34, 0x19, 0x11, 0xb7, 0x98, 0xa6, 0xb5, 0xf5, 0x3d, 0x22, 0xe0, 0x68, 0x22, + 0x88, 0xa5, 0x0e, 0x88, 0xd5, 0x6d, 0xf7, 0x34, 0xb7, 0x7d, 0x68, 0x1f, 0xf5, 0xf4, 0xd3, 0xaf, 0x34, 0xed, 0x64, + 0xca, 0x66, 0xc9, 0x29, 0xb2, 0x63, 0x4e, 0x90, 0x1e, 0xa4, 0xdf, 0x9a, 0x66, 0x4f, 0x82, 0xc4, 0x72, 0xb5, 0x0d, + 0xff, 0xd4, 0x34, 0x40, 0x46, 0x7d, 0xed, 0xe1, 0xac, 0x3d, 0x3b, 0x9c, 0x3d, 0x1b, 0xf0, 0xe4, 0xec, 0xab, 0x42, + 0x71, 0x93, 0xfe, 0x6d, 0x2b, 0xd5, 0x92, 0x34, 0x8e, 0x3e, 0x30, 0x4e, 0x4b, 0x6a, 0x92, 0x51, 0x54, 0xae, 0xda, + 0xae, 0xf6, 0xe4, 0xf6, 0xc6, 0x93, 0x59, 0x3b, 0x2f, 0x8e, 0x63, 0x3c, 0x90, 0x83, 0x3c, 0x39, 0x10, 0x43, 0x3f, + 0x51, 0xc1, 0xe4, 0x5a, 0x75, 0x80, 0x72, 0x75, 0x3e, 0xc7, 0xb9, 0x98, 0xdf, 0x09, 0x38, 0x98, 0xcd, 0x0d, 0x10, + 0x12, 0xac, 0x36, 0xd4, 0xbf, 0x77, 0xdb, 0x3d, 0xd3, 0x75, 0x8f, 0xec, 0xa3, 0xde, 0xc4, 0x31, 0x0f, 0xed, 0x43, + 0xab, 0x63, 0x1f, 0x99, 0x3d, 0xab, 0x67, 0xf6, 0xfe, 0xda, 0x9b, 0x58, 0x87, 0xf6, 0xa1, 0xe9, 0x58, 0x3d, 0x48, + 0xb4, 0x7a, 0x56, 0xef, 0xc6, 0x3a, 0xec, 0x4d, 0x1c, 0x4c, 0x6d, 0xdb, 0xdd, 0xae, 0xe5, 0x3a, 0x76, 0xb7, 0x6b, + 0x76, 0xed, 0xa3, 0x23, 0xcb, 0xed, 0xd8, 0x47, 0x47, 0xe7, 0xdd, 0x9e, 0xdd, 0x81, 0xbc, 0x4e, 0x67, 0xd2, 0xb1, + 0x5d, 0xd7, 0x82, 0xbf, 0xcc, 0x9e, 0xdd, 0xa6, 0x1f, 0xae, 0x6b, 0x77, 0x5c, 0xd3, 0x09, 0xba, 0x6d, 0xfb, 0xe8, + 0x99, 0x89, 0x7f, 0x63, 0x31, 0x13, 0xff, 0x82, 0x66, 0xcc, 0x67, 0x76, 0xfb, 0x88, 0x7e, 0x61, 0x83, 0x37, 0x87, + 0xbd, 0x9f, 0xf5, 0x83, 0xc6, 0x39, 0xb8, 0x34, 0x87, 0x5e, 0xd7, 0xee, 0x74, 0xcc, 0x43, 0xd7, 0xee, 0x75, 0x16, + 0xd6, 0x61, 0xdb, 0x3e, 0x3a, 0x9e, 0x58, 0xae, 0x7d, 0x7c, 0x6c, 0x3a, 0x56, 0xc7, 0x6e, 0x9b, 0xae, 0x7d, 0xd8, + 0xc1, 0x1f, 0x1d, 0xbb, 0x7d, 0x73, 0xfc, 0xcc, 0x3e, 0xea, 0x2e, 0x8e, 0xec, 0xc3, 0xf7, 0x87, 0x3d, 0xbb, 0xdd, + 0x59, 0x74, 0x8e, 0xec, 0xf6, 0xf1, 0xcd, 0x91, 0x7d, 0xb8, 0xb0, 0xda, 0x47, 0x5b, 0x6b, 0xba, 0x6d, 0x1b, 0x60, + 0x84, 0xd9, 0x90, 0x61, 0xf2, 0x0c, 0xf8, 0xb3, 0xc0, 0xba, 0x7f, 0x62, 0x33, 0x49, 0xb5, 0xea, 0x33, 0xbb, 0x77, + 0x3c, 0xa1, 0xe2, 0x90, 0x60, 0x89, 0x12, 0x50, 0xe5, 0xc6, 0xa2, 0x6e, 0xb1, 0x39, 0x4b, 0x34, 0x24, 0xfe, 0xf0, + 0xce, 0x6e, 0x2c, 0xe8, 0x98, 0xfa, 0xfd, 0x8f, 0xb6, 0x23, 0x97, 0x1c, 0x42, 0xf2, 0x7e, 0xc5, 0xff, 0xa1, 0x68, + 0x56, 0x23, 0xf3, 0xbc, 0x49, 0x28, 0xf9, 0x76, 0xb7, 0x50, 0xf2, 0xd5, 0x7a, 0x1f, 0xa1, 0xe4, 0xdb, 0x2f, 0x2e, + 0x94, 0x3c, 0x2f, 0xdb, 0xc4, 0xbc, 0x2d, 0x07, 0xdd, 0xf8, 0x79, 0x53, 0x66, 0x39, 0xf8, 0x5e, 0xeb, 0xf2, 0x62, + 0x7d, 0x05, 0xee, 0xdf, 0xde, 0x46, 0xc3, 0x57, 0xeb, 0x82, 0xc2, 0x67, 0x04, 0x38, 0xf6, 0x6d, 0x44, 0x38, 0xf6, + 0xd7, 0xf5, 0x10, 0xb4, 0xcc, 0x38, 0x99, 0xe3, 0x4f, 0xad, 0x85, 0x17, 0xcc, 0x24, 0x89, 0x04, 0x29, 0x03, 0x4c, + 0x06, 0xbb, 0x2b, 0xb8, 0x9e, 0xe1, 0x25, 0xb3, 0x5e, 0x86, 0x09, 0x68, 0x04, 0x83, 0x26, 0xc7, 0x2c, 0xce, 0x4a, + 0x95, 0x6d, 0xe1, 0x30, 0xef, 0x9a, 0x5b, 0x40, 0x35, 0xe6, 0xa3, 0x02, 0x70, 0x7d, 0xeb, 0x6e, 0xb5, 0x5d, 0x0d, + 0x34, 0xeb, 0x84, 0x82, 0x34, 0x50, 0xfb, 0x75, 0xf9, 0x45, 0x35, 0xdc, 0x92, 0xe2, 0x75, 0xf3, 0x48, 0x61, 0x24, + 0xe5, 0xfa, 0x6e, 0x51, 0x8d, 0x77, 0xd7, 0x34, 0x6b, 0xba, 0x2f, 0x54, 0xdf, 0xa2, 0x43, 0x2c, 0x1b, 0x2e, 0x83, + 0xaa, 0x14, 0x32, 0xb2, 0x16, 0x20, 0xf9, 0x03, 0x35, 0x57, 0x34, 0xce, 0x29, 0x55, 0x47, 0x43, 0x7a, 0xc7, 0x51, + 0xf2, 0x0a, 0x6d, 0xaa, 0xca, 0xc9, 0x4f, 0x36, 0xf8, 0xae, 0xf0, 0x7f, 0x05, 0x4a, 0x94, 0x53, 0x3c, 0xe3, 0x48, + 0x85, 0xf3, 0x46, 0x69, 0x97, 0xb8, 0x11, 0xd9, 0xc2, 0xdd, 0x54, 0x69, 0xd1, 0x46, 0xb3, 0x04, 0x97, 0x2d, 0x05, + 0x15, 0x84, 0xdd, 0x93, 0x11, 0x40, 0x46, 0x86, 0x1a, 0x68, 0xe7, 0xb0, 0xad, 0x31, 0x51, 0xee, 0x21, 0x6c, 0x62, + 0x93, 0x7f, 0xa8, 0x8e, 0x4b, 0x36, 0xb3, 0x20, 0xf2, 0xd2, 0x3e, 0x92, 0x69, 0x0a, 0xc9, 0xdb, 0x46, 0x8b, 0x85, + 0xc1, 0x16, 0x65, 0x3a, 0xb5, 0x61, 0xde, 0x08, 0x5a, 0x3e, 0x6c, 0xd3, 0xbf, 0x93, 0x86, 0x62, 0x9b, 0x82, 0x3a, + 0x8a, 0xdb, 0x3d, 0x36, 0xdd, 0x23, 0xd3, 0x3e, 0xee, 0x1a, 0x99, 0x38, 0x70, 0x6a, 0x93, 0x05, 0x80, 0x80, 0x01, + 0x84, 0x1c, 0xa6, 0x1f, 0xfa, 0xa9, 0xef, 0x05, 0x19, 0xd0, 0xc3, 0xc5, 0x47, 0xca, 0x3f, 0xd7, 0x49, 0x0a, 0x73, + 0x14, 0x44, 0x2f, 0x1a, 0x7f, 0x58, 0x63, 0x96, 0xde, 0x32, 0x16, 0x36, 0x28, 0xc6, 0x94, 0x6d, 0x49, 0xfe, 0x38, + 0xcd, 0xfa, 0x8c, 0xb4, 0xd6, 0xc6, 0x69, 0xc8, 0xf7, 0x87, 0x30, 0x7c, 0xc8, 0x46, 0xe6, 0x0f, 0x4d, 0x08, 0xf7, + 0x9f, 0xbb, 0x11, 0x6e, 0xca, 0xf6, 0x41, 0xb8, 0xff, 0xfc, 0xe2, 0x08, 0xf7, 0x07, 0x15, 0xe1, 0x16, 0xec, 0xfe, + 0x72, 0x09, 0xd3, 0x3b, 0xfc, 0x6e, 0x81, 0xb7, 0xfa, 0xa7, 0xfa, 0x01, 0x11, 0xf0, 0xba, 0x12, 0x45, 0xfc, 0x7d, + 0x21, 0x2c, 0x1a, 0x32, 0x40, 0xd1, 0x4b, 0x36, 0x85, 0x60, 0x82, 0x08, 0xdb, 0x8e, 0x0c, 0xc3, 0xc4, 0x6e, 0xb5, + 0xd7, 0x61, 0x1a, 0xd8, 0x6f, 0xf9, 0x3b, 0x12, 0x04, 0xba, 0xaf, 0xa2, 0x78, 0xe9, 0xa1, 0x87, 0x50, 0x1d, 0xc3, + 0xa9, 0xc2, 0x87, 0x03, 0xb6, 0xa4, 0x93, 0x28, 0x9c, 0x4a, 0xa9, 0x24, 0x1b, 0x5e, 0x12, 0xc5, 0xad, 0xdf, 0x33, + 0x2f, 0xd6, 0x4d, 0xca, 0x86, 0xc5, 0x7d, 0xd2, 0x71, 0x9e, 0xb4, 0x0f, 0x9f, 0x1c, 0x39, 0xf0, 0xbf, 0xcb, 0x3a, + 0x99, 0xc9, 0x0b, 0x2e, 0xa3, 0x10, 0x22, 0x3a, 0x89, 0x92, 0x4d, 0xc5, 0x6e, 0x19, 0xfb, 0x90, 0x97, 0x3a, 0xae, + 0x2f, 0x34, 0xf5, 0xee, 0xf3, 0x32, 0xb5, 0x25, 0x16, 0xd1, 0x5a, 0x19, 0x56, 0xcd, 0x68, 0xfc, 0x70, 0x0d, 0x7c, + 0x76, 0xa5, 0x84, 0x9a, 0xcd, 0xa7, 0x9b, 0xcf, 0x8b, 0x75, 0xb2, 0xab, 0x3c, 0x6c, 0x9c, 0x08, 0x5f, 0xb5, 0x13, + 0x82, 0x5c, 0x44, 0xe9, 0x60, 0xd0, 0x09, 0x0c, 0x9c, 0xa6, 0x41, 0xd0, 0x56, 0xb1, 0x40, 0x1e, 0x2d, 0x50, 0x1a, + 0xaf, 0xc3, 0x49, 0x0b, 0x7f, 0x7a, 0xe3, 0xa4, 0xe5, 0x1f, 0x80, 0xfb, 0x68, 0xec, 0xd8, 0xc0, 0x55, 0xf3, 0x4e, + 0x9d, 0x3c, 0xc6, 0x4e, 0x22, 0x56, 0xc5, 0x7b, 0x92, 0x9a, 0x31, 0x45, 0xe6, 0xc6, 0xa5, 0xb5, 0x86, 0xde, 0x13, + 0x59, 0xf1, 0x49, 0x6a, 0x42, 0x74, 0x6c, 0x58, 0xee, 0xc7, 0x8f, 0xa9, 0x14, 0xc4, 0xab, 0xa5, 0x69, 0x9d, 0x4d, + 0x72, 0xff, 0x93, 0x9a, 0x37, 0x8f, 0xc8, 0x05, 0x65, 0x7f, 0x62, 0x46, 0x4f, 0x9f, 0x9e, 0x0e, 0x5d, 0x83, 0x47, + 0x5b, 0x2e, 0x84, 0x06, 0x3c, 0xdf, 0x4f, 0xd1, 0xc8, 0xa8, 0x35, 0x81, 0x5d, 0xc1, 0x9b, 0xc9, 0x11, 0xe6, 0x08, + 0x1c, 0x7b, 0x41, 0xa8, 0x1e, 0x52, 0x28, 0xf0, 0x84, 0xc2, 0x8f, 0x28, 0x23, 0x5f, 0x5d, 0x1d, 0xdb, 0xb1, 0x1d, + 0x5d, 0x56, 0x9c, 0xf9, 0xf3, 0xe1, 0x26, 0x4a, 0x3d, 0x08, 0x7a, 0x16, 0x44, 0x73, 0xb0, 0xa3, 0x4b, 0xfd, 0x34, + 0x80, 0x08, 0x5a, 0x60, 0x50, 0xb7, 0xa4, 0x77, 0x79, 0xc6, 0xad, 0x1b, 0xbc, 0xf8, 0x03, 0x46, 0x51, 0x15, 0x26, + 0xb4, 0xe8, 0x12, 0xed, 0x7b, 0xb8, 0x0c, 0x5b, 0x7a, 0x0b, 0x78, 0x03, 0x2c, 0x4e, 0x2c, 0xd5, 0x5a, 0xa8, 0xaf, + 0x41, 0x1d, 0x43, 0xe7, 0x93, 0x98, 0xc5, 0xde, 0x12, 0x82, 0x4d, 0x6c, 0x32, 0x93, 0x63, 0x5a, 0x9d, 0xa3, 0x5a, + 0xcd, 0x7d, 0x76, 0x64, 0x6a, 0x6d, 0xd7, 0xd4, 0x1c, 0x40, 0xb7, 0x7a, 0x66, 0x6e, 0xb2, 0xab, 0xc1, 0x2e, 0x85, + 0x07, 0xc2, 0x2f, 0x0f, 0x69, 0x1e, 0xa4, 0xea, 0xc0, 0x45, 0x49, 0x29, 0x79, 0xb8, 0x6d, 0x29, 0x61, 0x20, 0x7c, + 0x12, 0x7a, 0x5e, 0xb0, 0xbb, 0xd4, 0xc0, 0x08, 0x53, 0xbc, 0x88, 0x6f, 0x6c, 0xd0, 0xd0, 0xd7, 0x0f, 0x35, 0xff, + 0xe3, 0xc7, 0x96, 0x0f, 0xc6, 0x4c, 0x43, 0x05, 0x3e, 0xf0, 0x6d, 0x14, 0x00, 0xe6, 0xe7, 0x62, 0x7a, 0x04, 0x16, + 0x58, 0x1a, 0xc2, 0xbf, 0x79, 0xb2, 0xf8, 0xc1, 0xd5, 0x24, 0xec, 0xc0, 0x0b, 0xe7, 0x80, 0xd2, 0xbc, 0x70, 0x5e, + 0x51, 0xc7, 0x22, 0x5b, 0xe5, 0x52, 0x6a, 0xde, 0x54, 0xae, 0x2a, 0x95, 0x7c, 0x77, 0x7f, 0x41, 0x11, 0xf4, 0x5a, + 0x3a, 0xdc, 0x72, 0x68, 0x58, 0x9b, 0x4b, 0x72, 0x9f, 0x0e, 0xbf, 0x3e, 0x59, 0xb2, 0xd4, 0x23, 0x31, 0x10, 0x3c, + 0x7e, 0x81, 0x1c, 0xd0, 0x26, 0x22, 0xfa, 0x35, 0x5e, 0x0d, 0xc3, 0x29, 0xbb, 0xf1, 0x27, 0xfc, 0x5d, 0x6a, 0x6a, + 0xfc, 0x9e, 0xb2, 0x50, 0xe3, 0x73, 0xe8, 0x9a, 0x64, 0x70, 0x30, 0xf1, 0xd0, 0x0f, 0xee, 0x30, 0x8c, 0xf4, 0xd3, + 0xaf, 0xa5, 0x6d, 0x66, 0xd3, 0x22, 0x40, 0x18, 0xdb, 0xcb, 0x98, 0x05, 0xff, 0x1a, 0x7e, 0x0d, 0x17, 0xf7, 0xd7, + 0x57, 0xba, 0x31, 0x48, 0xed, 0x45, 0xcc, 0x66, 0xc3, 0xaf, 0x6b, 0xc2, 0xb9, 0xe2, 0xf3, 0x9e, 0xc6, 0xa2, 0x77, + 0xda, 0xb9, 0xf7, 0xb2, 0xce, 0x5e, 0x8f, 0xfa, 0x53, 0xfe, 0x5a, 0x87, 0x17, 0xe0, 0xa6, 0xf0, 0xc6, 0x76, 0x07, + 0xf8, 0x7e, 0x1e, 0x07, 0xde, 0xe4, 0xc3, 0x80, 0x72, 0x0a, 0x1f, 0x16, 0xdc, 0xd6, 0x13, 0x6f, 0xd5, 0xc7, 0xeb, + 0x55, 0x4d, 0x04, 0xd3, 0x6c, 0x4a, 0x95, 0x94, 0x5d, 0xed, 0x5e, 0xc6, 0xad, 0xbc, 0xc1, 0x9e, 0xb1, 0xab, 0xdb, + 0x85, 0x9f, 0x32, 0xd1, 0x15, 0x7e, 0x64, 0x99, 0x78, 0xa8, 0xd3, 0x13, 0x15, 0x1f, 0xd6, 0x76, 0x47, 0x73, 0x7b, + 0x7f, 0xed, 0xde, 0xb8, 0xce, 0xa2, 0xed, 0xda, 0xbd, 0xf7, 0x6e, 0x6f, 0xd1, 0xb1, 0x8f, 0x03, 0xab, 0x63, 0x1f, + 0xc3, 0x9f, 0xf7, 0xc7, 0x76, 0x6f, 0x61, 0xb5, 0xed, 0xc3, 0xf7, 0x6e, 0x3b, 0xb0, 0x7a, 0xf6, 0x31, 0xfc, 0x39, + 0xa7, 0x5a, 0xf0, 0x00, 0xa2, 0xf7, 0xce, 0xd7, 0x05, 0x2c, 0xa0, 0xfc, 0x96, 0x32, 0x59, 0xb3, 0x70, 0xbd, 0xd5, + 0xc8, 0x75, 0x01, 0x65, 0xe8, 0xa6, 0xf0, 0x52, 0x1b, 0x0e, 0x5a, 0xe1, 0x90, 0x47, 0x46, 0x11, 0xea, 0x6d, 0xc2, + 0x06, 0x5d, 0xc4, 0x08, 0xa9, 0x3d, 0x46, 0xbc, 0x4e, 0x7d, 0x44, 0x08, 0x11, 0x72, 0x8f, 0x04, 0xc1, 0x3f, 0xad, + 0xd0, 0xcb, 0x9a, 0x88, 0x61, 0xa1, 0x60, 0xa5, 0x3c, 0xec, 0x6b, 0xb6, 0x7b, 0xe0, 0x68, 0x85, 0xcf, 0x64, 0xd8, + 0xb1, 0x2f, 0xda, 0x36, 0x17, 0x0e, 0xcb, 0xd6, 0x7f, 0x6f, 0x3b, 0x18, 0x6d, 0x9b, 0xda, 0x11, 0xee, 0xa6, 0xa7, + 0x7e, 0x2c, 0x87, 0xa7, 0xa0, 0x68, 0xb7, 0x3e, 0x94, 0x86, 0x01, 0xf1, 0x99, 0x5e, 0x03, 0x95, 0x7c, 0xe3, 0x05, + 0x8a, 0x22, 0x9b, 0x52, 0xf3, 0x81, 0xc4, 0xfc, 0x8f, 0x1f, 0xe7, 0x83, 0xb3, 0x4a, 0xe3, 0x3e, 0x71, 0xbb, 0x70, + 0xed, 0x76, 0x59, 0x67, 0xab, 0x4e, 0xe5, 0x6e, 0x7f, 0xe5, 0xb9, 0x3f, 0x63, 0xa1, 0x37, 0x25, 0x34, 0x36, 0x1a, + 0x15, 0x3b, 0x2b, 0xfa, 0x1a, 0xe0, 0xe9, 0xbd, 0xf4, 0xd4, 0xd1, 0x8d, 0x41, 0x28, 0xd4, 0x0f, 0xc2, 0x2d, 0x3e, + 0xda, 0xf9, 0x5b, 0x4c, 0x07, 0xd0, 0x6c, 0x99, 0xc7, 0x0e, 0x03, 0xf1, 0xff, 0xf4, 0x24, 0xd0, 0x58, 0x13, 0xf4, + 0x25, 0x4a, 0xa7, 0xb5, 0x60, 0xbc, 0x27, 0xef, 0x55, 0xba, 0x50, 0x59, 0x72, 0xa6, 0x43, 0x12, 0x04, 0xee, 0xc3, + 0x58, 0x9d, 0x52, 0x59, 0x54, 0xde, 0x16, 0x79, 0x82, 0xe9, 0x43, 0x90, 0x82, 0x96, 0x30, 0x1c, 0x35, 0x1e, 0x3f, + 0x6e, 0xbc, 0x84, 0x48, 0x39, 0x47, 0x0d, 0x59, 0xac, 0xab, 0xf8, 0x4d, 0x57, 0x51, 0x8c, 0x6c, 0x17, 0xb1, 0x86, + 0xd0, 0x71, 0xa5, 0xbd, 0x87, 0x3f, 0xc7, 0xcc, 0x4b, 0x6d, 0x2e, 0x2c, 0x6d, 0x29, 0x97, 0xbb, 0xe9, 0xb2, 0x0e, + 0x68, 0xb7, 0x72, 0x67, 0x8c, 0xdc, 0xd9, 0xe9, 0xa3, 0xcd, 0xfb, 0x35, 0xf7, 0xea, 0x00, 0x6d, 0x7c, 0x74, 0x72, + 0xff, 0x59, 0x6f, 0x52, 0x8f, 0x9c, 0x2c, 0xa9, 0x57, 0x6e, 0x94, 0x7a, 0x22, 0x40, 0x16, 0xd0, 0xe5, 0x83, 0x5a, + 0xf5, 0x0b, 0xc5, 0xf3, 0xc3, 0xe9, 0x9b, 0x8b, 0x6f, 0x35, 0xbe, 0xff, 0x49, 0x5b, 0x00, 0x1f, 0x32, 0x14, 0x96, + 0x65, 0x48, 0x61, 0x59, 0x34, 0x1e, 0x1f, 0x09, 0x82, 0x9b, 0x64, 0x07, 0x04, 0x41, 0x64, 0x40, 0x93, 0x0e, 0xc5, + 0x72, 0x1d, 0xa4, 0xfe, 0xca, 0x8b, 0xd3, 0x03, 0xa8, 0x6a, 0x01, 0x92, 0xd3, 0x9b, 0xfc, 0x41, 0x90, 0x1a, 0x86, + 0xf0, 0x01, 0x9a, 0x86, 0x42, 0x0f, 0x63, 0xe6, 0x07, 0x52, 0x0d, 0x43, 0x74, 0xe0, 0x4d, 0x26, 0x6c, 0x95, 0x0e, + 0x75, 0x6f, 0x05, 0xe1, 0x79, 0xd0, 0xe1, 0xfe, 0x41, 0x34, 0x49, 0x59, 0x6a, 0x25, 0x69, 0xcc, 0xbc, 0xa5, 0x2e, + 0x7d, 0x4d, 0x57, 0xdb, 0x4b, 0xd6, 0xe3, 0xa5, 0x9f, 0x4a, 0x67, 0xad, 0x34, 0x41, 0x50, 0x88, 0x80, 0x21, 0x82, + 0x73, 0x18, 0x02, 0xe1, 0x79, 0x34, 0x2f, 0xed, 0xa8, 0x9c, 0x72, 0x39, 0x43, 0x57, 0xe0, 0x3c, 0x24, 0xcb, 0x14, + 0xed, 0x1b, 0xaf, 0xb9, 0x0f, 0x0b, 0xe9, 0x53, 0x56, 0x3f, 0x3d, 0xe1, 0xcf, 0x5b, 0x0d, 0xdd, 0xae, 0xe8, 0x5d, + 0x07, 0x9c, 0x9d, 0x37, 0x79, 0xb7, 0x38, 0xe0, 0x85, 0xe1, 0x6a, 0xa2, 0x96, 0x31, 0x10, 0x05, 0x8d, 0xe5, 0x02, + 0x08, 0xa1, 0x82, 0xc2, 0xcc, 0xc2, 0x3d, 0x95, 0xe6, 0x94, 0x38, 0x2a, 0xa4, 0x95, 0x3e, 0x7e, 0x7c, 0x3e, 0xfa, + 0xed, 0xdf, 0x10, 0x2d, 0x63, 0xe1, 0x0a, 0x9f, 0x12, 0x97, 0x6a, 0x29, 0x4e, 0x7d, 0x9a, 0x23, 0x54, 0x96, 0x62, + 0x53, 0xe1, 0x98, 0x47, 0x6c, 0xad, 0x6c, 0x74, 0x25, 0xbc, 0xfd, 0x41, 0x44, 0x1c, 0x43, 0x78, 0xbe, 0x18, 0xc1, + 0xf2, 0x8e, 0x84, 0xc3, 0x15, 0xed, 0x97, 0xbb, 0xef, 0x8e, 0xb5, 0xdc, 0x05, 0x4f, 0x9d, 0x46, 0x0f, 0xed, 0xa1, + 0xd3, 0x13, 0x4f, 0x43, 0xa2, 0x05, 0xc9, 0x8f, 0xa4, 0x7f, 0x00, 0xd3, 0x5c, 0x44, 0x4b, 0x66, 0xfb, 0xd1, 0xc1, + 0x2d, 0x1b, 0x5b, 0xde, 0xca, 0x27, 0xbd, 0x1c, 0xe4, 0xbb, 0x69, 0x44, 0xf9, 0x49, 0x75, 0x17, 0xa2, 0xaf, 0xb3, + 0x1c, 0x94, 0x51, 0xd1, 0xbd, 0x63, 0xb7, 0x9d, 0xcb, 0x01, 0xc1, 0x7f, 0x81, 0x02, 0xc7, 0xe8, 0xf4, 0xe4, 0xc0, + 0x3b, 0x2d, 0xfa, 0x97, 0xb5, 0x45, 0x84, 0x93, 0xe2, 0x25, 0x70, 0x46, 0x6e, 0x62, 0x85, 0x47, 0xd8, 0xfc, 0xc3, + 0x8a, 0x66, 0x33, 0xd5, 0x27, 0xac, 0x5d, 0x1c, 0x9e, 0x04, 0x5a, 0xbe, 0xa5, 0xa3, 0x15, 0xf5, 0x54, 0xed, 0x42, + 0xfe, 0x84, 0xa8, 0xf0, 0xdc, 0x7d, 0x30, 0x1c, 0xf7, 0x8a, 0x6f, 0x59, 0x09, 0xb1, 0x87, 0x54, 0x88, 0xe3, 0x91, + 0x72, 0x24, 0x83, 0x06, 0xca, 0xe5, 0xc1, 0x70, 0x48, 0x68, 0xae, 0x8c, 0xed, 0x00, 0x88, 0x35, 0xd1, 0x5e, 0x60, + 0xb2, 0x29, 0x54, 0xb4, 0xc8, 0x5c, 0x16, 0x2b, 0x95, 0xa7, 0x53, 0x1d, 0xe3, 0x81, 0x27, 0xb6, 0x5f, 0x61, 0x83, + 0xc2, 0xc6, 0xe3, 0xeb, 0x0e, 0xf8, 0x5d, 0xb4, 0x53, 0x42, 0xf3, 0xda, 0x37, 0x84, 0xd1, 0xad, 0xc0, 0xbb, 0x8f, + 0x14, 0x35, 0x26, 0xee, 0xd1, 0xe4, 0x1c, 0x53, 0x2f, 0x84, 0x25, 0x71, 0xe5, 0xa0, 0xc9, 0x88, 0x19, 0xd5, 0xc3, + 0xa6, 0x86, 0xb8, 0xd8, 0x75, 0xd6, 0xd4, 0xb2, 0xc5, 0xc9, 0x20, 0xf2, 0xcc, 0xf2, 0x73, 0x58, 0xc8, 0x44, 0xb4, + 0x90, 0x9d, 0x1c, 0xc0, 0xfc, 0xc0, 0x0b, 0x4b, 0x81, 0x70, 0xf2, 0x0d, 0x70, 0xf5, 0xe2, 0x25, 0xa4, 0x8a, 0xf5, + 0x7a, 0x2a, 0x68, 0x3e, 0x7c, 0x58, 0x4a, 0xe7, 0xd5, 0x85, 0x22, 0x55, 0x5a, 0xc6, 0xa9, 0x27, 0x02, 0x77, 0x47, + 0x98, 0xf9, 0x75, 0x8d, 0xe1, 0x65, 0xb2, 0x41, 0xca, 0x84, 0x93, 0x83, 0xf3, 0xb4, 0xc1, 0x0f, 0x42, 0x53, 0x11, + 0xa2, 0x67, 0xb7, 0x14, 0xc8, 0xf7, 0xf1, 0xb6, 0x52, 0x39, 0xe5, 0x24, 0x8b, 0xad, 0xbc, 0x96, 0xfe, 0x10, 0x77, + 0x7c, 0xa5, 0x34, 0xa6, 0x42, 0xb9, 0xf3, 0x74, 0x08, 0x45, 0x05, 0x2f, 0xde, 0x5b, 0xad, 0xa8, 0xb0, 0x31, 0x38, + 0x39, 0xa0, 0x67, 0xe9, 0x29, 0xed, 0xb0, 0xd3, 0x13, 0x50, 0xe5, 0xa6, 0x45, 0xf7, 0x56, 0x2b, 0xbe, 0xa4, 0xf4, + 0x8b, 0x72, 0x0e, 0x16, 0xe9, 0x32, 0x38, 0xfd, 0x7f, 0x8a, 0xa5, 0x7c, 0x2e, 0xc4, 0x61, 0x03, 0x00}; } // namespace web_server } // namespace esphome From 1f3754684adccccc54a4795d8a685d13ba59e352 Mon Sep 17 00:00:00 2001 From: dentra Date: Wed, 24 Jul 2024 06:50:59 +0300 Subject: [PATCH 0091/1052] [http_request] Allow configure buffer size on ESP-IDF (#7125) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/http_request/__init__.py | 11 +++++++++++ esphome/components/http_request/http_request_idf.cpp | 9 +++++++++ esphome/components/http_request/http_request_idf.h | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 161486fbb2..3ca97b9611 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -40,6 +40,8 @@ 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_MAX_RESPONSE_BUFFER_SIZE = "max_response_buffer_size" CONF_ON_RESPONSE = "on_response" @@ -110,6 +112,12 @@ CONFIG_SCHEMA = cv.All( cv.positive_not_null_time_period, cv.positive_time_period_milliseconds, ), + cv.SplitDefault(CONF_BUFFER_SIZE_RX, esp32_idf=512): cv.All( + cv.uint16_t, cv.only_with_esp_idf + ), + cv.SplitDefault(CONF_BUFFER_SIZE_TX, esp32_idf=512): cv.All( + cv.uint16_t, cv.only_with_esp_idf + ), } ).extend(cv.COMPONENT_SCHEMA), cv.require_framework_version( @@ -137,6 +145,9 @@ async def to_code(config): if CORE.is_esp32: if CORE.using_esp_idf: + cg.add(var.set_buffer_size_rx(config[CONF_BUFFER_SIZE_RX])) + cg.add(var.set_buffer_size_tx(config[CONF_BUFFER_SIZE_TX])) + esp32.add_idf_sdkconfig_option( "CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", config.get(CONF_VERIFY_SSL), diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 1f03b5f3bf..4fe5585723 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -18,6 +18,12 @@ namespace http_request { static const char *const TAG = "http_request.idf"; +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_); +} + std::shared_ptr HttpRequestIDF::start(std::string url, std::string method, std::string body, std::list
headers) { if (!network::is_connected()) { @@ -63,6 +69,9 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin config.user_agent = this->useragent_; } + config.buffer_size = this->buffer_size_rx_; + config.buffer_size_tx = this->buffer_size_tx_; + const uint32_t start = millis(); watchdog::WatchdogManager wdm(this->get_watchdog_timeout()); diff --git a/esphome/components/http_request/http_request_idf.h b/esphome/components/http_request/http_request_idf.h index 79f850a636..431794924b 100644 --- a/esphome/components/http_request/http_request_idf.h +++ b/esphome/components/http_request/http_request_idf.h @@ -24,8 +24,18 @@ class HttpContainerIDF : public HttpContainer { class HttpRequestIDF : public HttpRequestComponent { public: + void dump_config() override; + std::shared_ptr start(std::string url, std::string method, std::string body, std::list
headers) override; + + void set_buffer_size_rx(uint16_t buffer_size_rx) { this->buffer_size_rx_ = buffer_size_rx; } + void set_buffer_size_tx(uint16_t buffer_size_tx) { this->buffer_size_tx_ = buffer_size_tx; } + + protected: + // if zero ESP-IDF will use DEFAULT_HTTP_BUF_SIZE + uint16_t buffer_size_rx_{}; + uint16_t buffer_size_tx_{}; }; } // namespace http_request From 75635956cd707237f44aab63c652555d635e3428 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 25 Jul 2024 05:30:39 +1000 Subject: [PATCH 0092/1052] Give more info on import errors. (#7128) --- esphome/loader.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/esphome/loader.py b/esphome/loader.py index e0457eb425..9399c4cb31 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -1,17 +1,17 @@ -import logging -from typing import Callable, Optional, Any, ContextManager -from types import ModuleType -import importlib -import importlib.util -import importlib.resources -import importlib.abc -import sys -from pathlib import Path from dataclasses import dataclass +import importlib +import importlib.abc +import importlib.resources +import importlib.util +import logging +from pathlib import Path +import sys +from types import ModuleType +from typing import Any, Callable, ContextManager, Optional from esphome.const import SOURCE_FILE_EXTENSIONS -import esphome.core.config from esphome.core import CORE +import esphome.core.config from esphome.types import ConfigType _LOGGER = logging.getLogger(__name__) @@ -175,7 +175,11 @@ def _lookup_module(domain): try: module = importlib.import_module(f"esphome.components.{domain}") except ImportError as e: - if "No module named" not in str(e): + if "No module named" in str(e): + _LOGGER.error( + "Unable to import component %s: %s", domain, str(e), exc_info=False + ) + else: _LOGGER.error("Unable to import component %s:", domain, exc_info=True) return None except Exception: # pylint: disable=broad-except From 6e863305aae0589103fa9dfdf2d36d2ff50858c9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 23 Jul 2024 13:51:32 +1200 Subject: [PATCH 0093/1052] [http_request] Change default timeout to 4.5s (#7123) --- esphome/components/http_request/__init__.py | 2 +- esphome/components/http_request/http_request.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index ade7024bed..ef387553fe 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -99,7 +99,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_FOLLOW_REDIRECTS, True): cv.boolean, cv.Optional(CONF_REDIRECT_LIMIT, 3): cv.int_, cv.Optional( - CONF_TIMEOUT, default="5s" + CONF_TIMEOUT, default="4.5s" ): cv.positive_time_period_milliseconds, cv.SplitDefault(CONF_ESP8266_DISABLE_SSL_SUPPORT, esp8266=False): cv.All( cv.only_on_esp8266, cv.boolean diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 82b7392648..c01baf8644 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -80,7 +80,7 @@ class HttpRequestComponent : public Component { const char *useragent_{nullptr}; bool follow_redirects_; uint16_t redirect_limit_; - uint16_t timeout_{5000}; + uint16_t timeout_{4500}; uint32_t watchdog_timeout_{0}; }; From 7c24f1ba6dac36d839c5fe34f16d6b52f12264da Mon Sep 17 00:00:00 2001 From: dentra Date: Wed, 24 Jul 2024 03:12:59 +0300 Subject: [PATCH 0094/1052] [http_request] Fix ESP-IDF follow redirect (#7101) --- esphome/components/http_request/__init__.py | 14 ++-- .../http_request/http_request_idf.cpp | 66 +++++++++++++++---- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index ef387553fe..161486fbb2 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -1,17 +1,17 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg +from esphome.components import esp32 +import esphome.config_validation as cv from esphome.const import ( - __version__, + CONF_ESP8266_DISABLE_SSL_SUPPORT, CONF_ID, - CONF_TIMEOUT, CONF_METHOD, + CONF_TIMEOUT, CONF_TRIGGER_ID, CONF_URL, - CONF_ESP8266_DISABLE_SSL_SUPPORT, + __version__, ) -from esphome.core import Lambda, CORE -from esphome.components import esp32 +from esphome.core import CORE, Lambda DEPENDENCIES = ["network"] AUTO_LOAD = ["json"] diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 68e0639b99..1f03b5f3bf 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -77,7 +77,7 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin esp_http_client_set_header(client, header.name, header.value); } - int body_len = body.length(); + const int body_len = body.length(); esp_err_t err = esp_http_client_open(client, body_len); if (err != ESP_OK) { @@ -109,18 +109,62 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } - container->content_length = esp_http_client_fetch_headers(client); - const auto status_code = esp_http_client_get_status_code(client); - container->status_code = status_code; + auto is_ok = [](int code) { return code >= HttpStatus_Ok && code < HttpStatus_MultipleChoices; }; - if (status_code < 200 || status_code >= 300) { - ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), status_code); - this->status_momentary_error("failed", 1000); - esp_http_client_cleanup(client); - return nullptr; + container->content_length = esp_http_client_fetch_headers(client); + container->status_code = esp_http_client_get_status_code(client); + if (is_ok(container->status_code)) { + container->duration_ms = millis() - start; + return container; } - container->duration_ms = millis() - start; - return container; + + if (this->follow_redirects_) { + auto is_redirect = [](int code) { + return code == HttpStatus_MovedPermanently || code == HttpStatus_Found || code == HttpStatus_SeeOther || + code == HttpStatus_TemporaryRedirect || code == HttpStatus_PermanentRedirect; + }; + auto num_redirects = this->redirect_limit_; + while (is_redirect(container->status_code) && num_redirects > 0) { + err = esp_http_client_set_redirection(client); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_http_client_set_redirection failed: %s", esp_err_to_name(err)); + this->status_momentary_error("failed", 1000); + esp_http_client_cleanup(client); + return nullptr; + } +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE + char url[256]{}; + if (esp_http_client_get_url(client, url, sizeof(url) - 1) == ESP_OK) { + ESP_LOGV(TAG, "redirecting to url: %s", url); + } +#endif + err = esp_http_client_open(client, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_http_client_open failed: %s", esp_err_to_name(err)); + this->status_momentary_error("failed", 1000); + esp_http_client_cleanup(client); + return nullptr; + } + + container->content_length = esp_http_client_fetch_headers(client); + container->status_code = esp_http_client_get_status_code(client); + if (is_ok(container->status_code)) { + container->duration_ms = millis() - start; + return container; + } + + num_redirects--; + } + + if (num_redirects == 0) { + ESP_LOGW(TAG, "Reach redirect limit count=%d", this->redirect_limit_); + } + } + + ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code); + this->status_momentary_error("failed", 1000); + esp_http_client_cleanup(client); + return nullptr; } int HttpContainerIDF::read(uint8_t *buf, size_t max_len) { From ad0118dd4a47f1566ee6146d69004957d5555d12 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:13:05 +1200 Subject: [PATCH 0095/1052] Bump version to 2024.7.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ff5f7b699d..9abfafc4a4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.1" +__version__ = "2024.7.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 23ffc3ddfb1504a0f3f5ad7f3cc2f9ea1d3cddf6 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:12:04 +1000 Subject: [PATCH 0096/1052] [lvgl] base implementation (#7116) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/lvgl/__init__.py | 212 ++++++++++ esphome/components/lvgl/defines.py | 487 ++++++++++++++++++++++ esphome/components/lvgl/font.cpp | 76 ++++ esphome/components/lvgl/helpers.py | 70 ++++ esphome/components/lvgl/label.py | 34 ++ esphome/components/lvgl/lv_validation.py | 170 ++++++++ esphome/components/lvgl/lvcode.py | 237 +++++++++++ esphome/components/lvgl/lvgl_esphome.cpp | 129 ++++++ esphome/components/lvgl/lvgl_esphome.h | 119 ++++++ esphome/components/lvgl/lvgl_hal.h | 21 + esphome/components/lvgl/obj.py | 22 + esphome/components/lvgl/schemas.py | 260 ++++++++++++ esphome/components/lvgl/types.py | 64 +++ esphome/components/lvgl/widget.py | 347 +++++++++++++++ esphome/core/defines.h | 3 + platformio.ini | 1 + tests/components/lvgl/common.yaml | 0 tests/components/lvgl/lvgl-package.yaml | 24 ++ tests/components/lvgl/test.esp32-ard.yaml | 30 ++ tests/components/lvgl/test.esp32-idf.yaml | 52 +++ 21 files changed, 2359 insertions(+) create mode 100644 esphome/components/lvgl/__init__.py create mode 100644 esphome/components/lvgl/defines.py create mode 100644 esphome/components/lvgl/font.cpp create mode 100644 esphome/components/lvgl/helpers.py create mode 100644 esphome/components/lvgl/label.py create mode 100644 esphome/components/lvgl/lv_validation.py create mode 100644 esphome/components/lvgl/lvcode.py create mode 100644 esphome/components/lvgl/lvgl_esphome.cpp create mode 100644 esphome/components/lvgl/lvgl_esphome.h create mode 100644 esphome/components/lvgl/lvgl_hal.h create mode 100644 esphome/components/lvgl/obj.py create mode 100644 esphome/components/lvgl/schemas.py create mode 100644 esphome/components/lvgl/types.py create mode 100644 esphome/components/lvgl/widget.py create mode 100644 tests/components/lvgl/common.yaml create mode 100644 tests/components/lvgl/lvgl-package.yaml create mode 100644 tests/components/lvgl/test.esp32-ard.yaml create mode 100644 tests/components/lvgl/test.esp32-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index c5e144bdfa..2fc030453f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -217,6 +217,7 @@ esphome/components/lock/* @esphome/core esphome/components/logger/* @esphome/core esphome/components/ltr390/* @latonita @sjtrny esphome/components/ltr_als_ps/* @latonita +esphome/components/lvgl/* @clydebarrow esphome/components/m5stack_8angle/* @rnauber esphome/components/matrix_keypad/* @ssieb esphome/components/max31865/* @DAVe3283 diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py new file mode 100644 index 0000000000..2f3bd69546 --- /dev/null +++ b/esphome/components/lvgl/__init__.py @@ -0,0 +1,212 @@ +import logging + +import esphome.codegen as cg +from esphome.components.display import Display +import esphome.config_validation as cv +from esphome.const import ( + CONF_AUTO_CLEAR_ENABLED, + CONF_BUFFER_SIZE, + CONF_ID, + CONF_LAMBDA, + CONF_PAGES, +) +from esphome.core import CORE, ID, Lambda +from esphome.cpp_generator import MockObj +from esphome.final_validate import full_config +from esphome.helpers import write_file_if_changed + +from . import defines as df, helpers, lv_validation as lvalid +from .label import label_spec +from .lvcode import ConstantLiteral, LvContext + +# from .menu import menu_spec +from .obj import obj_spec +from .schemas import WIDGET_TYPES, any_widget_schema, obj_schema +from .types import FontEngine, LvglComponent, lv_disp_t_ptr, lv_font_t, lvgl_ns +from .widget import LvScrActType, Widget, add_widgets, set_obj_properties + +DOMAIN = "lvgl" +DEPENDENCIES = ("display",) +AUTO_LOAD = ("key_provider",) +CODEOWNERS = ("@clydebarrow",) +LOGGER = logging.getLogger(__name__) + +for widg in ( + label_spec, + obj_spec, +): + WIDGET_TYPES[widg.name] = widg + +lv_scr_act_spec = LvScrActType() +lv_scr_act = Widget.create( + None, ConstantLiteral("lv_scr_act()"), lv_scr_act_spec, {}, parent=None +) + +WIDGET_SCHEMA = any_widget_schema() + + +async def add_init_lambda(lv_component, init): + if init: + lamb = await cg.process_lambda(Lambda(init), [(lv_disp_t_ptr, "lv_disp")]) + cg.add(lv_component.add_init_lambda(lamb)) + + +lv_defines = {} # Dict of #defines to provide as build flags + + +def add_define(macro, value="1"): + if macro in lv_defines and lv_defines[macro] != value: + LOGGER.error( + "Redefinition of %s - was %s now %s", macro, lv_defines[macro], value + ) + lv_defines[macro] = value + + +def as_macro(macro, value): + if value is None: + return f"#define {macro}" + return f"#define {macro} {value}" + + +LV_CONF_FILENAME = "lv_conf.h" +LV_CONF_H_FORMAT = """\ +#pragma once +{} +""" + + +def generate_lv_conf_h(): + definitions = [as_macro(m, v) for m, v in lv_defines.items()] + definitions.sort() + return LV_CONF_H_FORMAT.format("\n".join(definitions)) + + +def final_validation(config): + global_config = full_config.get() + for display_id in config[df.CONF_DISPLAYS]: + path = global_config.get_path_for_id(display_id)[:-1] + display = global_config.get_config_for_path(path) + if CONF_LAMBDA in display: + raise cv.Invalid("Using lambda: in display config not compatible with LVGL") + if display[CONF_AUTO_CLEAR_ENABLED]: + raise cv.Invalid( + "Using auto_clear_enabled: true in display config not compatible with LVGL" + ) + buffer_frac = config[CONF_BUFFER_SIZE] + if not CORE.is_host and buffer_frac > 0.5 and "psram" not in global_config: + LOGGER.warning("buffer_size: may need to be reduced without PSRAM") + + +async def to_code(config): + cg.add_library("lvgl/lvgl", "8.4.0") + CORE.add_define("USE_LVGL") + # suppress default enabling of extra widgets + add_define("_LV_KCONFIG_PRESENT") + # Always enable - lots of things use it. + add_define("LV_DRAW_COMPLEX", "1") + add_define("LV_TICK_CUSTOM", "1") + add_define("LV_TICK_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"') + add_define("LV_TICK_CUSTOM_SYS_TIME_EXPR", "(lv_millis())") + add_define("LV_MEM_CUSTOM", "1") + add_define("LV_MEM_CUSTOM_ALLOC", "lv_custom_mem_alloc") + add_define("LV_MEM_CUSTOM_FREE", "lv_custom_mem_free") + add_define("LV_MEM_CUSTOM_REALLOC", "lv_custom_mem_realloc") + add_define("LV_MEM_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"') + + add_define("LV_LOG_LEVEL", f"LV_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}") + add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH]) + for font in helpers.lv_fonts_used: + add_define(f"LV_FONT_{font.upper()}") + + if config[df.CONF_COLOR_DEPTH] == 16: + add_define( + "LV_COLOR_16_SWAP", + "1" if config[df.CONF_BYTE_ORDER] == "big_endian" else "0", + ) + add_define( + "LV_COLOR_CHROMA_KEY", + await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]), + ) + CORE.add_build_flag("-Isrc") + + cg.add_global(lvgl_ns.using) + lv_component = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(lv_component, config) + Widget.create(config[CONF_ID], lv_component, WIDGET_TYPES[df.CONF_OBJ], config) + for display in config[df.CONF_DISPLAYS]: + cg.add(lv_component.add_display(await cg.get_variable(display))) + + frac = config[CONF_BUFFER_SIZE] + if frac >= 0.75: + frac = 1 + elif frac >= 0.375: + frac = 2 + elif frac > 0.19: + frac = 4 + else: + frac = 8 + cg.add(lv_component.set_buffer_frac(int(frac))) + cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH])) + + for font in helpers.esphome_fonts_used: + await cg.get_variable(font) + cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font)) + default_font = config[df.CONF_DEFAULT_FONT] + if default_font not in helpers.lv_fonts_used: + add_define( + "LV_FONT_CUSTOM_DECLARE", f"LV_FONT_DECLARE(*{df.DEFAULT_ESPHOME_FONT})" + ) + globfont_id = ID( + df.DEFAULT_ESPHOME_FONT, + True, + type=lv_font_t.operator("ptr").operator("const"), + ) + cg.new_variable(globfont_id, MockObj(default_font)) + add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT) + else: + add_define("LV_FONT_DEFAULT", default_font) + + with LvContext(): + await set_obj_properties(lv_scr_act, config) + await add_widgets(lv_scr_act, config) + Widget.set_completed() + await add_init_lambda(lv_component, LvContext.get_code()) + for comp in helpers.lvgl_components_required: + CORE.add_define(f"USE_LVGL_{comp.upper()}") + for use in helpers.lv_uses: + add_define(f"LV_USE_{use.upper()}") + lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME) + write_file_if_changed(lv_conf_h_file, generate_lv_conf_h()) + CORE.add_build_flag("-DLV_CONF_H=1") + CORE.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"') + + +def display_schema(config): + value = cv.ensure_list(cv.use_id(Display))(config) + return value or [cv.use_id(Display)(config)] + + +FINAL_VALIDATE_SCHEMA = final_validation + +CONFIG_SCHEMA = ( + cv.polling_component_schema("1s") + .extend(obj_schema("obj")) + .extend( + { + cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent), + cv.GenerateID(df.CONF_DISPLAYS): display_schema, + cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16), + cv.Optional(df.CONF_DEFAULT_FONT, default="montserrat_14"): lvalid.lv_font, + cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean, + cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage, + cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of( + *df.LOG_LEVELS, upper=True + ), + cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of( + "big_endian", "little_endian" + ), + cv.Optional(df.CONF_WIDGETS): cv.ensure_list(WIDGET_SCHEMA), + cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color, + } + ) +).add_extra(cv.has_at_least_one_key(CONF_PAGES, df.CONF_WIDGETS)) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py new file mode 100644 index 0000000000..50bdac3865 --- /dev/null +++ b/esphome/components/lvgl/defines.py @@ -0,0 +1,487 @@ +""" +This is the base of the import tree for LVGL. It contains constant definitions used elsewhere. +Constants already defined in esphome.const are not duplicated here and must be imported where used. + +""" + +from esphome import codegen as cg, config_validation as cv +from esphome.core import ID, Lambda +from esphome.cpp_types import uint32 +from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor + +from .lvcode import ConstantLiteral + + +class LValidator: + """ + A validator for a particular type used in LVGL. Usable in configs as a validator, also + has `process()` to convert a value during code generation + """ + + def __init__(self, validator, rtype, idtype=None, idexpr=None, retmapper=None): + self.validator = validator + self.rtype = rtype + self.idtype = idtype + self.idexpr = idexpr + self.retmapper = retmapper + + def __call__(self, value): + if isinstance(value, cv.Lambda): + return cv.returning_lambda(value) + if self.idtype is not None and isinstance(value, ID): + return cv.use_id(self.idtype)(value) + return self.validator(value) + + async def process(self, value, args=()): + if value is None: + return None + if isinstance(value, Lambda): + return cg.RawExpression( + f"{await cg.process_lambda(value, args, return_type=self.rtype)}()" + ) + if self.idtype is not None and isinstance(value, ID): + return cg.RawExpression(f"{value}->{self.idexpr}") + if self.retmapper is not None: + return self.retmapper(value) + return cg.safe_exp(value) + + +class LvConstant(LValidator): + """ + Allow one of a list of choices, mapped to upper case, and prepend the choice with the prefix. + It's also permitted to include the prefix in the value + The property `one_of` has the single case validator, and `several_of` allows a list of constants. + """ + + def __init__(self, prefix: str, *choices): + self.prefix = prefix + self.choices = choices + prefixed_choices = [prefix + v for v in choices] + prefixed_validator = cv.one_of(*prefixed_choices, upper=True) + + @schema_extractor("one_of") + def validator(value): + if value == SCHEMA_EXTRACT: + return self.choices + if isinstance(value, str) and value.startswith(self.prefix): + return prefixed_validator(value) + return self.prefix + cv.one_of(*choices, upper=True)(value) + + super().__init__(validator, rtype=uint32) + self.one_of = LValidator(validator, uint32, retmapper=self.mapper) + self.several_of = LValidator( + cv.ensure_list(self.one_of), uint32, retmapper=self.mapper + ) + + def mapper(self, value, args=()): + if isinstance(value, list): + value = "|".join(value) + return ConstantLiteral(value) + + def extend(self, *choices): + """ + Extend an LVCconstant with additional choices. + :param choices: The extra choices + :return: A new LVConstant instance + """ + return LvConstant(self.prefix, *(self.choices + choices)) + + +# Widgets +CONF_LABEL = "label" + +# Parts +CONF_MAIN = "main" +CONF_SCROLLBAR = "scrollbar" +CONF_INDICATOR = "indicator" +CONF_KNOB = "knob" +CONF_SELECTED = "selected" +CONF_ITEMS = "items" +CONF_TICKS = "ticks" +CONF_TICK_STYLE = "tick_style" +CONF_CURSOR = "cursor" +CONF_TEXTAREA_PLACEHOLDER = "textarea_placeholder" + +LV_FONTS = list(f"montserrat_{s}" for s in range(8, 50, 2)) + [ + "dejavu_16_persian_hebrew", + "simsun_16_cjk", + "unscii_8", + "unscii_16", +] + +LV_EVENT = { + "PRESS": "PRESSED", + "SHORT_CLICK": "SHORT_CLICKED", + "LONG_PRESS": "LONG_PRESSED", + "LONG_PRESS_REPEAT": "LONG_PRESSED_REPEAT", + "CLICK": "CLICKED", + "RELEASE": "RELEASED", + "SCROLL_BEGIN": "SCROLL_BEGIN", + "SCROLL_END": "SCROLL_END", + "SCROLL": "SCROLL", + "FOCUS": "FOCUSED", + "DEFOCUS": "DEFOCUSED", + "READY": "READY", + "CANCEL": "CANCEL", +} + +LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT) + + +LV_ANIM = LvConstant( + "LV_SCR_LOAD_ANIM_", + "NONE", + "OVER_LEFT", + "OVER_RIGHT", + "OVER_TOP", + "OVER_BOTTOM", + "MOVE_LEFT", + "MOVE_RIGHT", + "MOVE_TOP", + "MOVE_BOTTOM", + "FADE_IN", + "FADE_OUT", + "OUT_LEFT", + "OUT_RIGHT", + "OUT_TOP", + "OUT_BOTTOM", +) + +LOG_LEVELS = ( + "TRACE", + "INFO", + "WARN", + "ERROR", + "USER", + "NONE", +) + +LV_LONG_MODES = LvConstant( + "LV_LABEL_LONG_", + "WRAP", + "DOT", + "SCROLL", + "SCROLL_CIRCULAR", + "CLIP", +) + +STATES = ( + "default", + "checked", + "focused", + "focus_key", + "edited", + "hovered", + "pressed", + "scrolled", + "disabled", + "user_1", + "user_2", + "user_3", + "user_4", +) + +PARTS = ( + CONF_MAIN, + CONF_SCROLLBAR, + CONF_INDICATOR, + CONF_KNOB, + CONF_SELECTED, + CONF_ITEMS, + CONF_TICKS, + CONF_CURSOR, + CONF_TEXTAREA_PLACEHOLDER, +) + +KEYBOARD_MODES = LvConstant( + "LV_KEYBOARD_MODE_", + "TEXT_LOWER", + "TEXT_UPPER", + "SPECIAL", + "NUMBER", +) +ROLLER_MODES = LvConstant("LV_ROLLER_MODE_", "NORMAL", "INFINITE") +DIRECTIONS = LvConstant("LV_DIR_", "LEFT", "RIGHT", "BOTTOM", "TOP") +TILE_DIRECTIONS = DIRECTIONS.extend("HOR", "VER", "ALL") +CHILD_ALIGNMENTS = LvConstant( + "LV_ALIGN_", + "TOP_LEFT", + "TOP_MID", + "TOP_RIGHT", + "LEFT_MID", + "CENTER", + "RIGHT_MID", + "BOTTOM_LEFT", + "BOTTOM_MID", + "BOTTOM_RIGHT", +) + +SIBLING_ALIGNMENTS = LvConstant( + "LV_ALIGN_", + "OUT_LEFT_TOP", + "OUT_TOP_LEFT", + "OUT_TOP_MID", + "OUT_TOP_RIGHT", + "OUT_RIGHT_TOP", + "OUT_LEFT_MID", + "OUT_RIGHT_MID", + "OUT_LEFT_BOTTOM", + "OUT_BOTTOM_LEFT", + "OUT_BOTTOM_MID", + "OUT_BOTTOM_RIGHT", + "OUT_RIGHT_BOTTOM", +) +ALIGN_ALIGNMENTS = CHILD_ALIGNMENTS.extend(*SIBLING_ALIGNMENTS.choices) + +FLEX_FLOWS = LvConstant( + "LV_FLEX_FLOW_", + "ROW", + "COLUMN", + "ROW_WRAP", + "COLUMN_WRAP", + "ROW_REVERSE", + "COLUMN_REVERSE", + "ROW_WRAP_REVERSE", + "COLUMN_WRAP_REVERSE", +) + +OBJ_FLAGS = ( + "hidden", + "clickable", + "click_focusable", + "checkable", + "scrollable", + "scroll_elastic", + "scroll_momentum", + "scroll_one", + "scroll_chain_hor", + "scroll_chain_ver", + "scroll_chain", + "scroll_on_focus", + "scroll_with_arrow", + "snappable", + "press_lock", + "event_bubble", + "gesture_bubble", + "adv_hittest", + "ignore_layout", + "floating", + "overflow_visible", + "layout_1", + "layout_2", + "widget_1", + "widget_2", + "user_1", + "user_2", + "user_3", + "user_4", +) + +ARC_MODES = LvConstant("LV_ARC_MODE_", "NORMAL", "REVERSE", "SYMMETRICAL") +BAR_MODES = LvConstant("LV_BAR_MODE_", "NORMAL", "SYMMETRICAL", "RANGE") + +BTNMATRIX_CTRLS = ( + "HIDDEN", + "NO_REPEAT", + "DISABLED", + "CHECKABLE", + "CHECKED", + "CLICK_TRIG", + "POPOVER", + "RECOLOR", + "CUSTOM_1", + "CUSTOM_2", +) + +LV_BASE_ALIGNMENTS = ( + "START", + "CENTER", + "END", +) +LV_CELL_ALIGNMENTS = LvConstant( + "LV_GRID_ALIGN_", + *LV_BASE_ALIGNMENTS, +) +LV_GRID_ALIGNMENTS = LV_CELL_ALIGNMENTS.extend( + "STRETCH", + "SPACE_EVENLY", + "SPACE_AROUND", + "SPACE_BETWEEN", +) + +LV_FLEX_ALIGNMENTS = LvConstant( + "LV_FLEX_ALIGN_", + *LV_BASE_ALIGNMENTS, + "SPACE_EVENLY", + "SPACE_AROUND", + "SPACE_BETWEEN", +) + +LV_MENU_MODES = LvConstant( + "LV_MENU_HEADER_", + "TOP_FIXED", + "TOP_UNFIXED", + "BOTTOM_FIXED", +) + +LV_CHART_TYPES = ( + "NONE", + "LINE", + "BAR", + "SCATTER", +) +LV_CHART_AXES = ( + "PRIMARY_Y", + "SECONDARY_Y", + "PRIMARY_X", + "SECONDARY_X", +) + +CONF_ACCEPTED_CHARS = "accepted_chars" +CONF_ADJUSTABLE = "adjustable" +CONF_ALIGN = "align" +CONF_ALIGN_TO = "align_to" +CONF_ANGLE_RANGE = "angle_range" +CONF_ANIMATED = "animated" +CONF_ANIMATION = "animation" +CONF_ANTIALIAS = "antialias" +CONF_ARC_LENGTH = "arc_length" +CONF_AUTO_START = "auto_start" +CONF_BACKGROUND_STYLE = "background_style" +CONF_DECIMAL_PLACES = "decimal_places" +CONF_COLUMN = "column" +CONF_DIGITS = "digits" +CONF_DISP_BG_COLOR = "disp_bg_color" +CONF_DISP_BG_IMAGE = "disp_bg_image" +CONF_BODY = "body" +CONF_BUTTONS = "buttons" +CONF_BYTE_ORDER = "byte_order" +CONF_CHANGE_RATE = "change_rate" +CONF_CLOSE_BUTTON = "close_button" +CONF_COLOR_DEPTH = "color_depth" +CONF_COLOR_END = "color_end" +CONF_COLOR_START = "color_start" +CONF_CONTROL = "control" +CONF_DEFAULT = "default" +CONF_DEFAULT_FONT = "default_font" +CONF_DIR = "dir" +CONF_DISPLAYS = "displays" +CONF_END_ANGLE = "end_angle" +CONF_END_VALUE = "end_value" +CONF_ENTER_BUTTON = "enter_button" +CONF_ENTRIES = "entries" +CONF_FLAGS = "flags" +CONF_FLEX_FLOW = "flex_flow" +CONF_FLEX_ALIGN_MAIN = "flex_align_main" +CONF_FLEX_ALIGN_CROSS = "flex_align_cross" +CONF_FLEX_ALIGN_TRACK = "flex_align_track" +CONF_FLEX_GROW = "flex_grow" +CONF_FULL_REFRESH = "full_refresh" +CONF_GRID_CELL_ROW_POS = "grid_cell_row_pos" +CONF_GRID_CELL_COLUMN_POS = "grid_cell_column_pos" +CONF_GRID_CELL_ROW_SPAN = "grid_cell_row_span" +CONF_GRID_CELL_COLUMN_SPAN = "grid_cell_column_span" +CONF_GRID_CELL_X_ALIGN = "grid_cell_x_align" +CONF_GRID_CELL_Y_ALIGN = "grid_cell_y_align" +CONF_GRID_COLUMN_ALIGN = "grid_column_align" +CONF_GRID_COLUMNS = "grid_columns" +CONF_GRID_ROW_ALIGN = "grid_row_align" +CONF_GRID_ROWS = "grid_rows" +CONF_HEADER_MODE = "header_mode" +CONF_HOME = "home" +CONF_INDICATORS = "indicators" +CONF_KEY_CODE = "key_code" +CONF_LABEL_GAP = "label_gap" +CONF_LAYOUT = "layout" +CONF_LEFT_BUTTON = "left_button" +CONF_LINE_WIDTH = "line_width" +CONF_LOG_LEVEL = "log_level" +CONF_LONG_PRESS_TIME = "long_press_time" +CONF_LONG_PRESS_REPEAT_TIME = "long_press_repeat_time" +CONF_LVGL_ID = "lvgl_id" +CONF_LONG_MODE = "long_mode" +CONF_MAJOR = "major" +CONF_MSGBOXES = "msgboxes" +CONF_OBJ = "obj" +CONF_OFFSET_X = "offset_x" +CONF_OFFSET_Y = "offset_y" +CONF_ONE_LINE = "one_line" +CONF_ON_SELECT = "on_select" +CONF_ONE_CHECKED = "one_checked" +CONF_NEXT = "next" +CONF_PAGE_WRAP = "page_wrap" +CONF_PASSWORD_MODE = "password_mode" +CONF_PIVOT_X = "pivot_x" +CONF_PIVOT_Y = "pivot_y" +CONF_PLACEHOLDER_TEXT = "placeholder_text" +CONF_POINTS = "points" +CONF_PREVIOUS = "previous" +CONF_REPEAT_COUNT = "repeat_count" +CONF_R_MOD = "r_mod" +CONF_RECOLOR = "recolor" +CONF_RIGHT_BUTTON = "right_button" +CONF_ROLLOVER = "rollover" +CONF_ROOT_BACK_BTN = "root_back_btn" +CONF_ROWS = "rows" +CONF_SCALES = "scales" +CONF_SCALE_LINES = "scale_lines" +CONF_SCROLLBAR_MODE = "scrollbar_mode" +CONF_SELECTED_INDEX = "selected_index" +CONF_SHOW_SNOW = "show_snow" +CONF_SPIN_TIME = "spin_time" +CONF_SRC = "src" +CONF_START_ANGLE = "start_angle" +CONF_START_VALUE = "start_value" +CONF_STATES = "states" +CONF_STRIDE = "stride" +CONF_STYLE = "style" +CONF_STYLE_ID = "style_id" +CONF_SKIP = "skip" +CONF_SYMBOL = "symbol" +CONF_TAB_ID = "tab_id" +CONF_TABS = "tabs" +CONF_TEXT = "text" +CONF_TILE = "tile" +CONF_TILE_ID = "tile_id" +CONF_TILES = "tiles" +CONF_TITLE = "title" +CONF_TOP_LAYER = "top_layer" +CONF_TRANSPARENCY_KEY = "transparency_key" +CONF_THEME = "theme" +CONF_VISIBLE_ROW_COUNT = "visible_row_count" +CONF_WIDGET = "widget" +CONF_WIDGETS = "widgets" +CONF_X = "x" +CONF_Y = "y" +CONF_ZOOM = "zoom" + +# Keypad keys + +LV_KEYS = LvConstant( + "LV_KEY_", + "UP", + "DOWN", + "RIGHT", + "LEFT", + "ESC", + "DEL", + "BACKSPACE", + "ENTER", + "NEXT", + "PREV", + "HOME", + "END", +) + + +# list of widgets and the parts allowed +WIDGET_PARTS = { + CONF_LABEL: (CONF_MAIN, CONF_SCROLLBAR, CONF_SELECTED), + CONF_OBJ: (CONF_MAIN,), +} + +DEFAULT_ESPHOME_FONT = "esphome_lv_default_font" + + +def join_enums(enums, prefix=""): + return "|".join(f"(int){prefix}{e.upper()}" for e in enums) diff --git a/esphome/components/lvgl/font.cpp b/esphome/components/lvgl/font.cpp new file mode 100644 index 0000000000..9c172f07f5 --- /dev/null +++ b/esphome/components/lvgl/font.cpp @@ -0,0 +1,76 @@ +#include "lvgl_esphome.h" + +#ifdef USE_LVGL_FONT +namespace esphome { +namespace lvgl { + +static const uint8_t *get_glyph_bitmap(const lv_font_t *font, uint32_t unicode_letter) { + auto *fe = (FontEngine *) font->dsc; + const auto *gd = fe->get_glyph_data(unicode_letter); + if (gd == nullptr) + return nullptr; + // esph_log_d(TAG, "Returning bitmap @ %X", (uint32_t)gd->data); + + return gd->data; +} + +static bool get_glyph_dsc_cb(const lv_font_t *font, lv_font_glyph_dsc_t *dsc, uint32_t unicode_letter, uint32_t next) { + auto *fe = (FontEngine *) font->dsc; + const auto *gd = fe->get_glyph_data(unicode_letter); + if (gd == nullptr) + return false; + dsc->adv_w = gd->offset_x + gd->width; + dsc->ofs_x = gd->offset_x; + dsc->ofs_y = fe->height - gd->height - gd->offset_y - fe->baseline; + dsc->box_w = gd->width; + dsc->box_h = gd->height; + dsc->is_placeholder = 0; + dsc->bpp = fe->bpp; + return true; +} + +FontEngine::FontEngine(font::Font *esp_font) : font_(esp_font) { + this->bpp = esp_font->get_bpp(); + this->lv_font_.dsc = this; + this->lv_font_.line_height = this->height = esp_font->get_height(); + this->lv_font_.base_line = this->baseline = this->lv_font_.line_height - esp_font->get_baseline(); + this->lv_font_.get_glyph_dsc = get_glyph_dsc_cb; + this->lv_font_.get_glyph_bitmap = get_glyph_bitmap; + this->lv_font_.subpx = LV_FONT_SUBPX_NONE; + this->lv_font_.underline_position = -1; + this->lv_font_.underline_thickness = 1; +} + +const lv_font_t *FontEngine::get_lv_font() { return &this->lv_font_; } + +const font::GlyphData *FontEngine::get_glyph_data(uint32_t unicode_letter) { + if (unicode_letter == last_letter_) + return this->last_data_; + uint8_t unicode[5]; + memset(unicode, 0, sizeof unicode); + if (unicode_letter > 0xFFFF) { + unicode[0] = 0xF0 + ((unicode_letter >> 18) & 0x7); + unicode[1] = 0x80 + ((unicode_letter >> 12) & 0x3F); + unicode[2] = 0x80 + ((unicode_letter >> 6) & 0x3F); + unicode[3] = 0x80 + (unicode_letter & 0x3F); + } else if (unicode_letter > 0x7FF) { + unicode[0] = 0xE0 + ((unicode_letter >> 12) & 0xF); + unicode[1] = 0x80 + ((unicode_letter >> 6) & 0x3F); + unicode[2] = 0x80 + (unicode_letter & 0x3F); + } else if (unicode_letter > 0x7F) { + unicode[0] = 0xC0 + ((unicode_letter >> 6) & 0x1F); + unicode[1] = 0x80 + (unicode_letter & 0x3F); + } else { + unicode[0] = unicode_letter; + } + int match_length; + int glyph_n = this->font_->match_next_glyph(unicode, &match_length); + if (glyph_n < 0) + return nullptr; + this->last_data_ = this->font_->get_glyphs()[glyph_n].get_glyph_data(); + this->last_letter_ = unicode_letter; + return this->last_data_; +} +} // namespace lvgl +} // namespace esphome +#endif // USES_LVGL_FONT diff --git a/esphome/components/lvgl/helpers.py b/esphome/components/lvgl/helpers.py new file mode 100644 index 0000000000..c8d4948fb1 --- /dev/null +++ b/esphome/components/lvgl/helpers.py @@ -0,0 +1,70 @@ +import re + +from esphome import config_validation as cv +from esphome.config import Config +from esphome.const import CONF_ARGS, CONF_FORMAT +from esphome.core import CORE, ID +from esphome.yaml_util import ESPHomeDataBase + +lv_uses = { + "USER_DATA", + "LOG", + "STYLE", + "FONT_PLACEHOLDER", + "THEME_DEFAULT", +} + + +def add_lv_use(*names): + for name in names: + lv_uses.add(name) + + +lv_fonts_used = set() +esphome_fonts_used = set() +REQUIRED_COMPONENTS = {} +lvgl_components_required = set() + + +def validate_printf(value): + cfmt = r""" + ( # start of capture group 1 + % # literal "%" + (?:[-+0 #]{0,5}) # optional flags + (?:\d+|\*)? # width + (?:\.(?:\d+|\*))? # precision + (?:h|l|ll|w|I|I32|I64)? # size + [cCdiouxXeEfgGaAnpsSZ] # type + ) + """ # noqa + matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X) + if len(matches) != len(value[CONF_ARGS]): + raise cv.Invalid( + f"Found {len(matches)} printf-patterns ({', '.join(matches)}), but {len(value[CONF_ARGS])} args were given!" + ) + return value + + +def get_line_marks(value) -> list: + """ + If possible, return a preprocessor directive to identify the line number where the given id was defined. + :param id: The id in question + :return: A list containing zero or more line directives + """ + path = None + if isinstance(value, ESPHomeDataBase): + path = value.esp_range + elif isinstance(value, ID) and isinstance(CORE.config, Config): + path = CORE.config.get_path_for_id(value)[:-1] + path = CORE.config.get_deepest_document_range_for_path(path) + if path is None: + return [] + return [path.start_mark.as_line_directive] + + +def requires_component(comp): + def validator(value): + lvgl_components_required.add(comp) + return cv.requires_component(comp)(value) + + return validator diff --git a/esphome/components/lvgl/label.py b/esphome/components/lvgl/label.py new file mode 100644 index 0000000000..5c4ae6ab0d --- /dev/null +++ b/esphome/components/lvgl/label.py @@ -0,0 +1,34 @@ +import esphome.config_validation as cv + +from .defines import CONF_LABEL, CONF_LONG_MODE, CONF_RECOLOR, CONF_TEXT, LV_LONG_MODES +from .lv_validation import lv_bool, lv_text +from .schemas import TEXT_SCHEMA +from .types import lv_label_t +from .widget import Widget, WidgetType + + +class LabelType(WidgetType): + def __init__(self): + super().__init__( + CONF_LABEL, + TEXT_SCHEMA.extend( + { + cv.Optional(CONF_RECOLOR): lv_bool, + cv.Optional(CONF_LONG_MODE): LV_LONG_MODES.one_of, + } + ), + ) + + @property + def w_type(self): + return lv_label_t + + async def to_code(self, w: Widget, config): + """For a text object, create and set text""" + if value := config.get(CONF_TEXT): + w.set_property(CONF_TEXT, await lv_text.process(value)) + w.set_property(CONF_LONG_MODE, config) + w.set_property(CONF_RECOLOR, config) + + +label_spec = LabelType() diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py new file mode 100644 index 0000000000..1de63c30ce --- /dev/null +++ b/esphome/components/lvgl/lv_validation.py @@ -0,0 +1,170 @@ +import esphome.codegen as cg +from esphome.components.binary_sensor import BinarySensor +from esphome.components.color import ColorStruct +from esphome.components.font import Font +from esphome.components.sensor import Sensor +from esphome.components.text_sensor import TextSensor +import esphome.config_validation as cv +from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT +from esphome.core import HexInt +from esphome.cpp_generator import MockObj +from esphome.helpers import cpp_string_escape +from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor + +from . import types as ty +from .defines import LV_FONTS, LValidator, LvConstant +from .helpers import ( + esphome_fonts_used, + lv_fonts_used, + lvgl_components_required, + requires_component, +) +from .lvcode import ConstantLiteral, lv_expr +from .types import lv_font_t + + +@schema_extractor("one_of") +def color(value): + if value == SCHEMA_EXTRACT: + return ["hex color value", "color ID"] + if isinstance(value, int): + return value + return cv.use_id(ColorStruct)(value) + + +def color_retmapper(value): + if isinstance(value, cv.Lambda): + return cv.returning_lambda(value) + if isinstance(value, int): + hexval = HexInt(value) + return lv_expr.color_hex(hexval) + # Must be an id + lvgl_components_required.add(CONF_COLOR) + return lv_expr.color_from(MockObj(value)) + + +def pixels_or_percent(value): + """A length in one axis - either a number (pixels) or a percentage""" + if value == SCHEMA_EXTRACT: + return ["pixels", "..%"] + if isinstance(value, int): + return str(cv.int_(value)) + # Will throw an exception if not a percentage. + return f"lv_pct({int(cv.percentage(value) * 100)})" + + +def zoom(value): + value = cv.float_range(0.1, 10.0)(value) + return int(value * 256) + + +def angle(value): + """ + Validation for an angle in degrees, converted to an integer representing 0.1deg units + :param value: The input in the range 0..360 + :return: An angle in 1/10 degree units. + """ + return int(cv.float_range(0.0, 360.0)(cv.angle(value)) * 10) + + +@schema_extractor("one_of") +def size(value): + """A size in one axis - one of "size_content", a number (pixels) or a percentage""" + if value == SCHEMA_EXTRACT: + return ["size_content", "pixels", "..%"] + if isinstance(value, str) and value.lower().endswith("px"): + value = cv.int_(value[:-2]) + if isinstance(value, str) and not value.endswith("%"): + if value.upper() == "SIZE_CONTENT": + return "LV_SIZE_CONTENT" + raise cv.Invalid("must be 'size_content', a pixel position or a percentage") + if isinstance(value, int): + return str(cv.int_(value)) + # Will throw an exception if not a percentage. + return f"lv_pct({int(cv.percentage(value) * 100)})" + + +@schema_extractor("one_of") +def opacity(value): + consts = LvConstant("LV_OPA_", "TRANSP", "COVER") + if value == SCHEMA_EXTRACT: + return consts.choices + value = cv.Any(cv.percentage, consts.one_of)(value) + if isinstance(value, float): + return int(value * 255) + return value + + +def stop_value(value): + return cv.int_range(0, 255)(value) + + +lv_color = LValidator(color, ty.lv_color_t, retmapper=color_retmapper) +lv_bool = LValidator(cv.boolean, cg.bool_, BinarySensor, "get_state()") + + +def lvms_validator_(value): + if value == "never": + value = "2147483647ms" + return cv.positive_time_period_milliseconds(value) + + +lv_milliseconds = LValidator( + lvms_validator_, + cg.int32, + retmapper=lambda x: x.total_milliseconds, +) + + +class TextValidator(LValidator): + def __init__(self): + super().__init__( + cv.string, + cg.const_char_ptr, + TextSensor, + "get_state().c_str()", + lambda s: cg.safe_exp(f"{s}"), + ) + + def __call__(self, value): + if isinstance(value, dict): + return value + return super().__call__(value) + + async def process(self, value, args=()): + if isinstance(value, dict): + args = [str(x) for x in value[CONF_ARGS]] + arg_expr = cg.RawExpression(",".join(args)) + format_str = cpp_string_escape(value[CONF_FORMAT]) + return f"str_sprintf({format_str}, {arg_expr}).c_str()" + return await super().process(value, args) + + +lv_text = TextValidator() +lv_float = LValidator(cv.float_, cg.float_, Sensor, "get_state()") +lv_int = LValidator(cv.int_, cg.int_, Sensor, "get_state()") + + +class LvFont(LValidator): + def __init__(self): + def lv_builtin_font(value): + fontval = cv.one_of(*LV_FONTS, lower=True)(value) + lv_fonts_used.add(fontval) + return "&lv_font_" + fontval + + def validator(value): + if value == SCHEMA_EXTRACT: + return LV_FONTS + if isinstance(value, str) and value.lower() in LV_FONTS: + return lv_builtin_font(value) + fontval = cv.use_id(Font)(value) + esphome_fonts_used.add(fontval) + return requires_component("font")(f"{fontval}_engine->get_lv_font()") + + super().__init__(validator, lv_font_t) + + async def process(self, value, args=()): + return ConstantLiteral(value) + + +lv_font = LvFont() diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py new file mode 100644 index 0000000000..13b4862b4d --- /dev/null +++ b/esphome/components/lvgl/lvcode.py @@ -0,0 +1,237 @@ +import abc +import logging +from typing import Union + +from esphome import codegen as cg +from esphome.core import ID, Lambda +from esphome.cpp_generator import ( + AssignmentExpression, + CallExpression, + Expression, + LambdaExpression, + Literal, + MockObj, + RawExpression, + RawStatement, + SafeExpType, + Statement, + VariableDeclarationExpression, + statement, +) + +from .helpers import get_line_marks + +_LOGGER = logging.getLogger(__name__) + + +class CodeContext(abc.ABC): + """ + A class providing a context for code generation. Generated code will be added to the + current context. A new context will stack on the current context, and restore it + when done. Used with the `with` statement. + """ + + code_context = None + + @abc.abstractmethod + def add(self, expression: Union[Expression, Statement]): + pass + + @staticmethod + def append(expression: Union[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 + + def __enter__(self): + self.previous = CodeContext.code_context + CodeContext.code_context = self + + def __exit__(self, *args): + CodeContext.code_context = self.previous + + +class MainContext(CodeContext): + """ + Code generation into the main() function + """ + + def add(self, expression: Union[Expression, Statement]): + return cg.add(expression) + + +class LvContext(CodeContext): + """ + Code generation into the LVGL initialisation code (called in `setup()`) + """ + + lv_init_code: list["Statement"] = [] + + @staticmethod + def lv_add(expression: Union[Expression, Statement]): + if isinstance(expression, Expression): + expression = statement(expression) + if not isinstance(expression, Statement): + raise ValueError( + f"Add '{expression}' must be expression or statement, not {type(expression)}" + ) + LvContext.lv_init_code.append(expression) + _LOGGER.debug("LV Adding: %s", expression) + return expression + + @staticmethod + def get_code(): + code = [] + for exp in LvContext.lv_init_code: + text = str(statement(exp)) + text = text.rstrip() + code.append(text) + return "\n".join(code) + "\n\n" + + def add(self, expression: Union[Expression, Statement]): + return LvContext.lv_add(expression) + + def set_style(self, prop): + return MockObj("lv_set_style_{prop}", "") + + +class LambdaContext(CodeContext): + """ + A context that will accumlate code for use in a lambda. + """ + + def __init__( + self, + parameters: list[tuple[SafeExpType, str]], + return_type: SafeExpType = None, + ): + super().__init__() + self.code_list: list[Statement] = [] + self.parameters = parameters + self.return_type = return_type + + def add(self, expression: Union[Expression, Statement]): + self.code_list.append(expression) + return expression + + async def code(self) -> LambdaExpression: + code_text = [] + for exp in self.code_list: + text = str(statement(exp)) + text = text.rstrip() + code_text.append(text) + return await cg.process_lambda( + Lambda("\n".join(code_text) + "\n\n"), + self.parameters, + return_type=self.return_type, + ) + + +class LocalVariable(MockObj): + """ + Create a local variable and enclose the code using it within a block. + """ + + def __init__(self, name, type, modifier=None, rhs=None): + base = ID(name, True, type) + super().__init__(base, "") + self.modifier = modifier + self.rhs = rhs + + def __enter__(self): + CodeContext.append(RawStatement("{")) + CodeContext.append( + VariableDeclarationExpression(self.base.type, self.modifier, self.base.id) + ) + if self.rhs is not None: + CodeContext.append(AssignmentExpression(None, "", self.base, self.rhs)) + return self.base + + def __exit__(self, *args): + CodeContext.append(RawStatement("}")) + + +class MockLv: + """ + A mock object that can be used to generate LVGL calls. + """ + + def __init__(self, base): + self.base = base + + def __getattr__(self, attr: str) -> "MockLv": + return MockLv(f"{self.base}{attr}") + + def append(self, expression): + CodeContext.append(expression) + + def __call__(self, *args: SafeExpType) -> "MockObj": + call = CallExpression(self.base, *args) + result = MockObj(call, "") + self.append(result) + return result + + def __str__(self): + return str(self.base) + + def __repr__(self): + return f"MockLv<{str(self.base)}>" + + def call(self, prop, *args): + call = CallExpression(RawExpression(f"{self.base}{prop}"), *args) + result = MockObj(call, "") + self.append(result) + return result + + def cond_if(self, expression: Expression): + CodeContext.append(RawExpression(f"if({expression}) {{")) + + def cond_else(self): + CodeContext.append(RawExpression("} else {")) + + def cond_endif(self): + CodeContext.append(RawExpression("}")) + + +class LvExpr(MockLv): + def __getattr__(self, attr: str) -> "MockLv": + return LvExpr(f"{self.base}{attr}") + + def append(self, expression): + pass + + +# Top level mock for generic lv_ calls to be recorded +lv = MockLv("lv_") +# Just generate an expression +lv_expr = LvExpr("lv_") +# Mock for lv_obj_ calls +lv_obj = MockLv("lv_obj_") + + +# equivalent to cg.add() for the lvgl init context +def lv_add(expression: Union[Expression, Statement]): + return CodeContext.append(expression) + + +def add_line_marks(where): + for mark in get_line_marks(where): + lv_add(cg.RawStatement(mark)) + + +def lv_assign(target, expression): + lv_add(RawExpression(f"{target} = {expression}")) + + +class ConstantLiteral(Literal): + __slots__ = ("constant",) + + def __init__(self, constant: str): + super().__init__() + self.constant = constant + + def __str__(self): + return self.constant diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp new file mode 100644 index 0000000000..bdaf8a4f18 --- /dev/null +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -0,0 +1,129 @@ +#include "esphome/core/defines.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "esphome/core/hal.h" +#include "lvgl_hal.h" +#include "lvgl_esphome.h" + +namespace esphome { +namespace lvgl { +static const char *const TAG = "lvgl"; + +lv_event_code_t lv_custom_event; // NOLINT +void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); } +void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) { + for (auto *display : this->displays_) { + display->draw_pixels_at(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), ptr, + display::COLOR_ORDER_RGB, LV_BITNESS, LV_COLOR_16_SWAP); + } +} + +void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { + auto now = millis(); + this->draw_buffer_(area, (const uint8_t *) color_p); + ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area), + lv_area_get_height(area), (int) (millis() - now)); + lv_disp_flush_ready(disp_drv); +} + +void LvglComponent::setup() { + ESP_LOGCONFIG(TAG, "LVGL Setup starts"); +#if LV_USE_LOG + lv_log_register_print_cb(log_cb); +#endif + lv_init(); + lv_custom_event = static_cast(lv_event_register_id()); + auto *display = this->displays_[0]; + size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_; + auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; + auto *buf = lv_custom_mem_alloc(buf_bytes); + if (buf == nullptr) { + ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes); + this->mark_failed(); + this->status_set_error("Memory allocation failure"); + return; + } + lv_disp_draw_buf_init(&this->draw_buf_, buf, nullptr, buffer_pixels); + lv_disp_drv_init(&this->disp_drv_); + this->disp_drv_.draw_buf = &this->draw_buf_; + this->disp_drv_.user_data = this; + this->disp_drv_.full_refresh = this->full_refresh_; + this->disp_drv_.flush_cb = static_flush_cb; + this->disp_drv_.rounder_cb = rounder_cb; + switch (display->get_rotation()) { + case display::DISPLAY_ROTATION_0_DEGREES: + break; + case display::DISPLAY_ROTATION_90_DEGREES: + this->disp_drv_.sw_rotate = true; + this->disp_drv_.rotated = LV_DISP_ROT_90; + break; + case display::DISPLAY_ROTATION_180_DEGREES: + this->disp_drv_.sw_rotate = true; + this->disp_drv_.rotated = LV_DISP_ROT_180; + break; + case display::DISPLAY_ROTATION_270_DEGREES: + this->disp_drv_.sw_rotate = true; + this->disp_drv_.rotated = LV_DISP_ROT_270; + break; + } + display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES); + this->disp_drv_.hor_res = (lv_coord_t) display->get_width(); + this->disp_drv_.ver_res = (lv_coord_t) display->get_height(); + ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated); + this->disp_ = lv_disp_drv_register(&this->disp_drv_); + for (const auto &v : this->init_lambdas_) + v(this->disp_); + lv_disp_trig_activity(this->disp_); + ESP_LOGCONFIG(TAG, "LVGL Setup complete"); +} +} // namespace lvgl +} // namespace esphome + +size_t lv_millis(void) { return esphome::millis(); } + +#if defined(USE_HOST) || defined(USE_RP2040) || defined(USE_ESP8266) +void *lv_custom_mem_alloc(size_t size) { + auto *ptr = malloc(size); // NOLINT + if (ptr == nullptr) { + esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); + } + return ptr; +} +void lv_custom_mem_free(void *ptr) { return free(ptr); } // NOLINT +void *lv_custom_mem_realloc(void *ptr, size_t size) { return realloc(ptr, size); } // NOLINT +#else +static unsigned cap_bits = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT; // NOLINT + +void *lv_custom_mem_alloc(size_t size) { + void *ptr; + ptr = heap_caps_malloc(size, cap_bits); + if (ptr == nullptr) { + cap_bits = MALLOC_CAP_8BIT; + ptr = heap_caps_malloc(size, cap_bits); + } + if (ptr == nullptr) { + esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); + return nullptr; + } +#ifdef ESPHOME_LOG_HAS_VERBOSE + esphome::ESP_LOGV(esphome::lvgl::TAG, "allocate %zu - > %p", size, ptr); +#endif + return ptr; +} + +void lv_custom_mem_free(void *ptr) { +#ifdef ESPHOME_LOG_HAS_VERBOSE + esphome::ESP_LOGV(esphome::lvgl::TAG, "free %p", ptr); +#endif + if (ptr == nullptr) + return; + heap_caps_free(ptr); +} + +void *lv_custom_mem_realloc(void *ptr, size_t size) { +#ifdef ESPHOME_LOG_HAS_VERBOSE + esphome::ESP_LOGV(esphome::lvgl::TAG, "realloc %p: %zu", ptr, size); +#endif + return heap_caps_realloc(ptr, size, cap_bits); +} +#endif diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h new file mode 100644 index 0000000000..988c22917b --- /dev/null +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -0,0 +1,119 @@ +#pragma once +#include "esphome/core/defines.h" +#ifdef USE_LVGL + +// required for clang-tidy +#ifndef LV_CONF_H +#define LV_CONF_SKIP 1 // NOLINT +#endif + +#include "esphome/components/display/display.h" +#include "esphome/components/display/display_color_utils.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include +#include + +#ifdef USE_LVGL_FONT +#include "esphome/components/font/font.h" +#endif +namespace esphome { +namespace lvgl { + +extern lv_event_code_t lv_custom_event; // NOLINT +#ifdef USE_LVGL_COLOR +static lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } +#endif +#if LV_COLOR_DEPTH == 16 +static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565; +#elif LV_COLOR_DEPTH == 32 +static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_888; +#else +static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_332; +#endif + +// Parent class for things that wrap an LVGL object +class LvCompound { + public: + virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; } + lv_obj_t *obj{}; +}; + +using LvLambdaType = std::function; +using set_value_lambda_t = std::function; +using event_callback_t = void(_lv_event_t *); +using text_lambda_t = std::function; + +#ifdef USE_LVGL_FONT +class FontEngine { + public: + FontEngine(font::Font *esp_font); + const lv_font_t *get_lv_font(); + + const font::GlyphData *get_glyph_data(uint32_t unicode_letter); + uint16_t baseline{}; + uint16_t height{}; + uint8_t bpp{}; + + protected: + font::Font *font_{}; + uint32_t last_letter_{}; + const font::GlyphData *last_data_{}; + lv_font_t lv_font_{}; +}; +#endif // USE_LVGL_FONT + +class LvglComponent : public PollingComponent { + constexpr static const char *const TAG = "lvgl"; + + public: + static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { + reinterpret_cast(disp_drv->user_data)->flush_cb_(disp_drv, area, color_p); + } + + float get_setup_priority() const override { return setup_priority::PROCESSOR; } + static void log_cb(const char *buf) { + esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf); + } + static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { + // make sure all coordinates are even + if (area->x1 & 1) + area->x1--; + if (!(area->x2 & 1)) + area->x2++; + if (area->y1 & 1) + area->y1--; + if (!(area->y2 & 1)) + area->y2++; + } + + void loop() override { lv_timer_handler_run_in_period(5); } + void setup() override; + + void update() override {} + + void add_display(display::Display *display) { this->displays_.push_back(display); } + void add_init_lambda(const std::function &lamb) { this->init_lambdas_.push_back(lamb); } + void dump_config() override; + void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; } + void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; } + lv_disp_t *get_disp() { return this->disp_; } + + protected: + void draw_buffer_(const lv_area_t *area, const uint8_t *ptr); + void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); + std::vector displays_{}; + lv_disp_draw_buf_t draw_buf_{}; + lv_disp_drv_t disp_drv_{}; + lv_disp_t *disp_{}; + + std::vector> init_lambdas_; + size_t buffer_frac_{1}; + bool full_refresh_{}; +}; + +} // namespace lvgl +} // namespace esphome + +#endif diff --git a/esphome/components/lvgl/lvgl_hal.h b/esphome/components/lvgl/lvgl_hal.h new file mode 100644 index 0000000000..754cc70391 --- /dev/null +++ b/esphome/components/lvgl/lvgl_hal.h @@ -0,0 +1,21 @@ +// +// Created by Clyde Stubbs on 20/9/2023. +// + +#pragma once + +#ifdef __cplusplus +#define EXTERNC extern "C" +#include +namespace esphome { +namespace lvgl {} +} // namespace esphome +#else +#define EXTERNC extern +#include +#endif + +EXTERNC size_t lv_millis(void); +EXTERNC void *lv_custom_mem_alloc(size_t size); +EXTERNC void lv_custom_mem_free(void *ptr); +EXTERNC void *lv_custom_mem_realloc(void *ptr, size_t size); diff --git a/esphome/components/lvgl/obj.py b/esphome/components/lvgl/obj.py new file mode 100644 index 0000000000..fba20bef36 --- /dev/null +++ b/esphome/components/lvgl/obj.py @@ -0,0 +1,22 @@ +from .defines import CONF_OBJ +from .types import lv_obj_t +from .widget import WidgetType + + +class ObjType(WidgetType): + """ + The base LVGL object. All other widgets inherit from this. + """ + + def __init__(self): + super().__init__(CONF_OBJ, schema={}, modify_schema={}) + + @property + def w_type(self): + return lv_obj_t + + async def to_code(self, w, config): + return [] + + +obj_spec = ObjType() diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py new file mode 100644 index 0000000000..4ae5824151 --- /dev/null +++ b/esphome/components/lvgl/schemas.py @@ -0,0 +1,260 @@ +from esphome import config_validation as cv +from esphome.const import CONF_ARGS, CONF_FORMAT, CONF_ID, CONF_STATE, CONF_TYPE +from esphome.schema_extractors import SCHEMA_EXTRACT + +from . import defines as df, lv_validation as lvalid, types as ty +from .defines import WIDGET_PARTS +from .helpers import ( + REQUIRED_COMPONENTS, + add_lv_use, + requires_component, + validate_printf, +) +from .lv_validation import lv_font +from .types import WIDGET_TYPES, get_widget_type + +# A schema for text properties +TEXT_SCHEMA = cv.Schema( + { + cv.Optional(df.CONF_TEXT): cv.Any( + cv.All( + cv.Schema( + { + cv.Required(CONF_FORMAT): cv.string, + cv.Optional(CONF_ARGS, default=list): cv.ensure_list( + cv.lambda_ + ), + }, + ), + validate_printf, + ), + lvalid.lv_text, + ) + } +) + +# All LVGL styles and their validators +STYLE_PROPS = { + "align": df.CHILD_ALIGNMENTS.one_of, + "arc_opa": lvalid.opacity, + "arc_color": lvalid.lv_color, + "arc_rounded": lvalid.lv_bool, + "arc_width": cv.positive_int, + "anim_time": lvalid.lv_milliseconds, + "bg_color": lvalid.lv_color, + "bg_grad_color": lvalid.lv_color, + "bg_dither_mode": df.LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF").one_of, + "bg_grad_dir": df.LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER").one_of, + "bg_grad_stop": lvalid.stop_value, + "bg_img_opa": lvalid.opacity, + "bg_img_recolor": lvalid.lv_color, + "bg_img_recolor_opa": lvalid.opacity, + "bg_main_stop": lvalid.stop_value, + "bg_opa": lvalid.opacity, + "border_color": lvalid.lv_color, + "border_opa": lvalid.opacity, + "border_post": lvalid.lv_bool, + "border_side": df.LvConstant( + "LV_BORDER_SIDE_", "NONE", "TOP", "BOTTOM", "LEFT", "RIGHT", "INTERNAL" + ).several_of, + "border_width": cv.positive_int, + "clip_corner": lvalid.lv_bool, + "height": lvalid.size, + "img_recolor": lvalid.lv_color, + "img_recolor_opa": lvalid.opacity, + "line_width": cv.positive_int, + "line_dash_width": cv.positive_int, + "line_dash_gap": cv.positive_int, + "line_rounded": lvalid.lv_bool, + "line_color": lvalid.lv_color, + "opa": lvalid.opacity, + "opa_layered": lvalid.opacity, + "outline_color": lvalid.lv_color, + "outline_opa": lvalid.opacity, + "outline_pad": lvalid.size, + "outline_width": lvalid.size, + "pad_all": lvalid.size, + "pad_bottom": lvalid.size, + "pad_column": lvalid.size, + "pad_left": lvalid.size, + "pad_right": lvalid.size, + "pad_row": lvalid.size, + "pad_top": lvalid.size, + "shadow_color": lvalid.lv_color, + "shadow_ofs_x": cv.int_, + "shadow_ofs_y": cv.int_, + "shadow_opa": lvalid.opacity, + "shadow_spread": cv.int_, + "shadow_width": cv.positive_int, + "text_align": df.LvConstant( + "LV_TEXT_ALIGN_", "LEFT", "CENTER", "RIGHT", "AUTO" + ).one_of, + "text_color": lvalid.lv_color, + "text_decor": df.LvConstant( + "LV_TEXT_DECOR_", "NONE", "UNDERLINE", "STRIKETHROUGH" + ).several_of, + "text_font": lv_font, + "text_letter_space": cv.positive_int, + "text_line_space": cv.positive_int, + "text_opa": lvalid.opacity, + "transform_angle": lvalid.angle, + "transform_height": lvalid.pixels_or_percent, + "transform_pivot_x": lvalid.pixels_or_percent, + "transform_pivot_y": lvalid.pixels_or_percent, + "transform_zoom": lvalid.zoom, + "translate_x": lvalid.pixels_or_percent, + "translate_y": lvalid.pixels_or_percent, + "max_height": lvalid.pixels_or_percent, + "max_width": lvalid.pixels_or_percent, + "min_height": lvalid.pixels_or_percent, + "min_width": lvalid.pixels_or_percent, + "radius": cv.Any(lvalid.size, df.LvConstant("LV_RADIUS_", "CIRCLE").one_of), + "width": lvalid.size, + "x": lvalid.pixels_or_percent, + "y": lvalid.pixels_or_percent, +} + +# Complete object style schema +STYLE_SCHEMA = cv.Schema({cv.Optional(k): v for k, v in STYLE_PROPS.items()}).extend( + { + cv.Optional(df.CONF_SCROLLBAR_MODE): df.LvConstant( + "LV_SCROLLBAR_MODE_", "OFF", "ON", "ACTIVE", "AUTO" + ).one_of, + } +) + +# Object states. Top level properties apply to MAIN +STATE_SCHEMA = cv.Schema( + {cv.Optional(state): STYLE_SCHEMA for state in df.STATES} +).extend(STYLE_SCHEMA) +# Setting object states +SET_STATE_SCHEMA = cv.Schema( + {cv.Optional(state): lvalid.lv_bool for state in df.STATES} +) +# Setting object flags +FLAG_SCHEMA = cv.Schema({cv.Optional(flag): cv.boolean for flag in df.OBJ_FLAGS}) +FLAG_LIST = cv.ensure_list(df.LvConstant("LV_OBJ_FLAG_", *df.OBJ_FLAGS).one_of) + + +def part_schema(widget_type): + """ + Generate a schema for the various parts (e.g. main:, indicator:) of a widget type + :param widget_type: The type of widget to generate for + :return: + """ + parts = WIDGET_PARTS.get(widget_type) + if parts is None: + parts = (df.CONF_MAIN,) + return cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}).extend( + STATE_SCHEMA + ) + + +def obj_schema(widget_type: str): + """ + Create a schema for a widget type itself i.e. no allowance for children + :param widget_type: + :return: + """ + return ( + part_schema(widget_type) + .extend(FLAG_SCHEMA) + .extend(ALIGN_TO_SCHEMA) + .extend( + cv.Schema( + { + cv.Optional(CONF_STATE): SET_STATE_SCHEMA, + } + ) + ) + ) + + +ALIGN_TO_SCHEMA = { + cv.Optional(df.CONF_ALIGN_TO): cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(ty.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, + } + ) +} + + +# A style schema that can include text +STYLED_TEXT_SCHEMA = cv.maybe_simple_value( + STYLE_SCHEMA.extend(TEXT_SCHEMA), key=df.CONF_TEXT +) + + +ALL_STYLES = { + **STYLE_PROPS, +} + + +def container_validator(schema, widget_type): + """ + Create a validator for a container given the widget type + :param schema: Base schema to extend + :param widget_type: + :return: + """ + + def validator(value): + result = schema + if w_sch := WIDGET_TYPES[widget_type].schema: + result = result.extend(w_sch) + if value and (layout := value.get(df.CONF_LAYOUT)): + if not isinstance(layout, dict): + raise cv.Invalid("Layout value must be a dict") + ltype = layout.get(CONF_TYPE) + add_lv_use(ltype) + if value == SCHEMA_EXTRACT: + return result + return result(value) + + return validator + + +def container_schema(widget_type, extras=None): + """ + Create a schema for a container widget of a given type. All obj properties are available, plus + the extras passed in, plus any defined for the specific widget being specified. + :param widget_type: The widget type, e.g. "img" + :param extras: Additional options to be made available, e.g. layout properties for children + :return: The schema for this type of widget. + """ + lv_type = get_widget_type(widget_type) + schema = obj_schema(widget_type).extend({cv.GenerateID(): cv.declare_id(lv_type)}) + if extras: + schema = schema.extend(extras) + # Delayed evaluation for recursion + return container_validator(schema, widget_type) + + +def widget_schema(widget_type, extras=None): + """ + Create a schema for a given widget type + :param widget_type: The name of the widget + :param extras: + :return: + """ + validator = container_schema(widget_type, extras=extras) + if required := REQUIRED_COMPONENTS.get(widget_type): + validator = cv.All(validator, requires_component(required)) + return cv.Exclusive(widget_type, df.CONF_WIDGETS), validator + + +# All widget schemas must be defined before this is called. + + +def any_widget_schema(extras=None): + """ + Generate schemas for all possible LVGL widgets. This is what implements the ability to have a list of any kind of + widget under the widgets: key. + + :param extras: Additional schema to be applied to each generated one + :return: + """ + return cv.Any(dict(widget_schema(wt, extras) for wt in WIDGET_PARTS)) diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py new file mode 100644 index 0000000000..3c043d266d --- /dev/null +++ b/esphome/components/lvgl/types.py @@ -0,0 +1,64 @@ +from esphome import codegen as cg +from esphome.core import ID + +from .defines import CONF_LABEL, CONF_OBJ, CONF_TEXT + +uint16_t_ptr = cg.uint16.operator("ptr") +lvgl_ns = cg.esphome_ns.namespace("lvgl") +char_ptr = cg.global_ns.namespace("char").operator("ptr") +void_ptr = cg.void.operator("ptr") +LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent) +lv_event_code_t = cg.global_ns.namespace("lv_event_code_t") +FontEngine = lvgl_ns.class_("FontEngine") +LvCompound = lvgl_ns.class_("LvCompound") +lv_font_t = cg.global_ns.class_("lv_font_t") +lv_style_t = cg.global_ns.struct("lv_style_t") +lv_pseudo_button_t = lvgl_ns.class_("LvPseudoButton") +lv_obj_base_t = cg.global_ns.class_("lv_obj_t", lv_pseudo_button_t) +lv_obj_t_ptr = lv_obj_base_t.operator("ptr") +lv_disp_t_ptr = cg.global_ns.struct("lv_disp_t").operator("ptr") +lv_color_t = cg.global_ns.struct("lv_color_t") + + +# this will be populated later, in __init__.py to avoid circular imports. +WIDGET_TYPES: dict = {} + + +class LvType(cg.MockObjClass): + def __init__(self, *args, **kwargs): + parens = kwargs.pop("parents", ()) + super().__init__(*args, parents=parens + (lv_obj_base_t,)) + self.args = kwargs.pop("largs", [(lv_obj_t_ptr, "obj")]) + self.value = kwargs.pop("lvalue", lambda w: w.obj) + self.has_on_value = kwargs.pop("has_on_value", False) + self.value_property = None + + def get_arg_type(self): + return self.args[0][0] if len(self.args) else None + + +class LvText(LvType): + def __init__(self, *args, **kwargs): + super().__init__( + *args, + largs=[(cg.std_string, "text")], + lvalue=lambda w: w.get_property("text")[0], + **kwargs, + ) + self.value_property = CONF_TEXT + + +lv_obj_t = LvType("lv_obj_t") +lv_label_t = LvText("lv_label_t") + +LV_TYPES = { + CONF_LABEL: lv_label_t, + CONF_OBJ: lv_obj_t, +} + + +def get_widget_type(typestr: str) -> LvType: + return LV_TYPES[typestr] + + +CUSTOM_EVENT = ID("lv_custom_event", False, type=lv_event_code_t) diff --git a/esphome/components/lvgl/widget.py b/esphome/components/lvgl/widget.py new file mode 100644 index 0000000000..44f277f1c3 --- /dev/null +++ b/esphome/components/lvgl/widget.py @@ -0,0 +1,347 @@ +import sys +from typing import Any + +from esphome import codegen as cg, config_validation as cv +from esphome.config_validation import Invalid +from esphome.const import CONF_GROUP, CONF_ID, CONF_STATE +from esphome.core import ID, TimePeriod +from esphome.coroutine import FakeAwaitable +from esphome.cpp_generator import MockObjClass + +from .defines import ( + CONF_DEFAULT, + CONF_MAIN, + CONF_SCROLLBAR_MODE, + CONF_WIDGETS, + OBJ_FLAGS, + PARTS, + STATES, + LValidator, + join_enums, +) +from .helpers import add_lv_use +from .lvcode import ConstantLiteral, add_line_marks, lv, lv_add, lv_assign, lv_obj +from .schemas import ALL_STYLES +from .types import WIDGET_TYPES, LvCompound, lv_obj_t + +EVENT_LAMB = "event_lamb__" + + +class WidgetType: + """ + Describes a type of Widget, e.g. "bar" or "line" + """ + + def __init__(self, name, schema=None, modify_schema=None): + """ + :param name: The widget name, e.g. "bar" + :param schema: The config schema for defining a widget + :param modify_schema: A schema to update the widget + """ + self.name = name + self.schema = schema or {} + if modify_schema is None: + self.modify_schema = schema + else: + self.modify_schema = modify_schema + + @property + def animated(self): + return False + + @property + def w_type(self): + """ + Get the type associated with this widget + :return: + """ + return lv_obj_t + + def is_compound(self): + return self.w_type.inherits_from(LvCompound) + + async def to_code(self, w, config: dict): + """ + Generate code for a given widget + :param w: The widget + :param config: Its configuration + :return: Generated code as a list of text lines + """ + raise NotImplementedError(f"No to_code defined for {self.name}") + + def obj_creator(self, parent: MockObjClass, config: dict): + """ + Create an instance of the widget type + :param parent: The parent to which it should be attached + :param config: Its configuration + :return: Generated code as a single text line + """ + return f"lv_{self.name}_create({parent})" + + def get_uses(self): + """ + Get a list of other widgets used by this one + :return: + """ + return () + + +class LvScrActType(WidgetType): + """ + A "widget" representing the active screen. + """ + + def __init__(self): + super().__init__("lv_scr_act()") + + def obj_creator(self, parent: MockObjClass, config: dict): + return [] + + async def to_code(self, w, config: dict): + return [] + + +class Widget: + """ + Represents a Widget. + """ + + widgets_completed = False + + @staticmethod + def set_completed(): + Widget.widgets_completed = True + + def __init__(self, var, wtype: WidgetType, config: dict = None, parent=None): + self.var = var + self.type = wtype + self.config = config + self.scale = 1.0 + self.step = 1.0 + self.range_from = -sys.maxsize + self.range_to = sys.maxsize + self.parent = parent + + @staticmethod + def create(name, var, wtype: WidgetType, config: dict = None, parent=None): + w = Widget(var, wtype, config, parent) + if name is not None: + widget_map[name] = w + return w + + @property + def obj(self): + if self.type.is_compound(): + return f"{self.var}->obj" + return self.var + + def add_state(self, *args): + return lv_obj.add_state(self.obj, *args) + + def clear_state(self, *args): + return lv_obj.clear_state(self.obj, *args) + + def add_flag(self, *args): + return lv_obj.add_flag(self.obj, *args) + + def clear_flag(self, *args): + return lv_obj.clear_flag(self.obj, *args) + + def set_property(self, prop, value, animated: bool = None, ltype=None): + if isinstance(value, dict): + value = value.get(prop) + if value is None: + return + if isinstance(value, TimePeriod): + value = value.total_milliseconds + ltype = ltype or self.__type_base() + if animated is None or self.type.animated is not True: + lv.call(f"{ltype}_set_{prop}", self.obj, value) + else: + lv.call( + f"{ltype}_set_{prop}", + self.obj, + value, + "LV_ANIM_ON" if animated else "LV_ANIM_OFF", + ) + + def get_property(self, prop, ltype=None): + ltype = ltype or self.__type_base() + return f"lv_{ltype}_get_{prop}({self.obj})" + + def set_style(self, prop, value, state): + if value is None: + return [] + return lv.call(f"obj_set_style_{prop}", self.obj, value, state) + + def __type_base(self): + wtype = self.type.w_type + base = str(wtype) + if base.startswith("Lv"): + return f"{wtype}".removeprefix("Lv").removesuffix("Type").lower() + return f"{wtype}".removeprefix("lv_").removesuffix("_t") + + def __str__(self): + return f"({self.var}, {self.type})" + + +# Map of widgets to their config, used for trigger generation +widget_map: dict[Any, Widget] = {} + + +def get_widget_generator(wid): + """ + Used to wait for a widget during code generation. + :param wid: + :return: + """ + while True: + if obj := widget_map.get(wid): + return obj + if Widget.widgets_completed: + raise Invalid( + f"Widget {wid} not found, yet all widgets should be defined by now" + ) + yield + + +async def get_widget(wid: ID) -> Widget: + if obj := widget_map.get(wid): + return obj + return await FakeAwaitable(get_widget_generator(wid)) + + +def collect_props(config): + """ + Collect all properties from a configuration + :param config: + :return: + """ + props = {} + for prop in [*ALL_STYLES, *OBJ_FLAGS, CONF_GROUP]: + if prop in config: + props[prop] = config[prop] + return props + + +def collect_states(config): + """ + Collect prperties for each state of a widget + :param config: + :return: + """ + states = {CONF_DEFAULT: collect_props(config)} + for state in STATES: + if state in config: + states[state] = collect_props(config[state]) + return states + + +def collect_parts(config): + """ + Collect properties and states for all widget parts + :param config: + :return: + """ + parts = {CONF_MAIN: collect_states(config)} + for part in PARTS: + if part in config: + parts[part] = collect_states(config[part]) + return parts + + +async def set_obj_properties(w: Widget, config): + """Generate a list of C++ statements to apply properties to an lv_obj_t""" + parts = collect_parts(config) + for part, states in parts.items(): + for state, props in states.items(): + lv_state = ConstantLiteral( + f"(int)LV_STATE_{state.upper()}|(int)LV_PART_{part.upper()}" + ) + for prop, value in { + k: v for k, v in props.items() if k in ALL_STYLES + }.items(): + if isinstance(ALL_STYLES[prop], LValidator): + value = await ALL_STYLES[prop].process(value) + w.set_style(prop, value, lv_state) + flag_clr = set() + flag_set = set() + props = parts[CONF_MAIN][CONF_DEFAULT] + for prop, value in {k: v for k, v in props.items() if k in OBJ_FLAGS}.items(): + if value: + flag_set.add(prop) + else: + flag_clr.add(prop) + if flag_set: + adds = join_enums(flag_set, "LV_OBJ_FLAG_") + w.add_flag(adds) + if flag_clr: + clrs = join_enums(flag_clr, "LV_OBJ_FLAG_") + w.clear_flag(clrs) + + if states := config.get(CONF_STATE): + adds = set() + clears = set() + lambs = {} + for key, value in states.items(): + if isinstance(value, cv.Lambda): + lambs[key] = value + elif value == "true": + adds.add(key) + else: + clears.add(key) + if adds: + adds = ConstantLiteral(join_enums(adds, "LV_STATE_")) + w.add_state(adds) + if clears: + clears = ConstantLiteral(join_enums(clears, "LV_STATE_")) + w.clear_state(clears) + for key, value in lambs.items(): + lamb = await cg.process_lambda(value, [], return_type=cg.bool_) + state = ConstantLiteral(f"LV_STATE_{key.upper}") + lv.cond_if(lamb) + w.add_state(state) + lv.cond_else() + w.clear_state(state) + lv.cond_endif() + if scrollbar_mode := config.get(CONF_SCROLLBAR_MODE): + lv_obj.set_scrollbar_mode(w.obj, scrollbar_mode) + + +async def add_widgets(parent: Widget, config: dict): + """ + Add all widgets to an object + :param parent: The enclosing obj + :param config: The configuration + :return: + """ + for w in config.get(CONF_WIDGETS) or (): + w_type, w_cnfig = next(iter(w.items())) + await widget_to_code(w_cnfig, w_type, parent.obj) + + +async def widget_to_code(w_cnfig, w_type, parent): + """ + Converts a Widget definition to C code. + :param w_cnfig: The widget configuration + :param w_type: The Widget type + :param parent: The parent to which the widget should be added + :return: + """ + spec: WidgetType = WIDGET_TYPES[w_type] + creator = spec.obj_creator(parent, w_cnfig) + add_lv_use(spec.name) + add_lv_use(*spec.get_uses()) + wid = w_cnfig[CONF_ID] + add_line_marks(wid) + if spec.is_compound(): + var = cg.new_Pvariable(wid) + lv_add(var.set_obj(creator)) + else: + var = cg.Pvariable(wid, cg.nullptr, type_=lv_obj_t) + lv_assign(var, creator) + + widget = Widget.create(wid, var, spec, w_cnfig, parent) + await set_obj_properties(widget, w_cnfig) + await add_widgets(widget, w_cnfig) + await spec.to_code(widget, w_cnfig) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 4831ed2c9e..9d453260ab 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -38,6 +38,9 @@ #define USE_LIGHT #define USE_LOCK #define USE_LOGGER +#define USE_LVGL +#define USE_LVGL_FONT +#define USE_LVGL_IMAGE #define USE_MDNS #define USE_MEDIA_PLAYER #define USE_MQTT diff --git a/platformio.ini b/platformio.ini index fc7f35b6c3..baf0a85d73 100644 --- a/platformio.ini +++ b/platformio.ini @@ -42,6 +42,7 @@ lib_deps = pavlodn/HaierProtocol@0.9.31 ; haier ; This is using the repository until a new release is published to PlatformIO https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library + lvgl/lvgl@8.4.0 ; lvgl build_flags = -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE src_filter = diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml new file mode 100644 index 0000000000..856e7c3e9d --- /dev/null +++ b/tests/components/lvgl/lvgl-package.yaml @@ -0,0 +1,24 @@ +color: + - id: light_blue + hex: "3340FF" + +lvgl: + bg_color: light_blue + widgets: + - label: + text: Hello world + text_color: 0xFF8000 + align: center + text_font: montserrat_40 + border_post: true + + - label: + text: "Hello shiny day" + text_color: 0xFFFFFF + align: bottom_mid + text_font: space16 + +font: + - file: "gfonts://Roboto" + id: space16 + bpp: 4 diff --git a/tests/components/lvgl/test.esp32-ard.yaml b/tests/components/lvgl/test.esp32-ard.yaml new file mode 100644 index 0000000000..abfb324ea5 --- /dev/null +++ b/tests/components/lvgl/test.esp32-ard.yaml @@ -0,0 +1,30 @@ +spi: + clk_pin: 14 + mosi_pin: 13 + +i2c: + sda: GPIO18 + scl: GPIO19 + +display: + - platform: ili9xxx + model: st7789v + id: tft_display + dimensions: + width: 240 + height: 320 + transform: + swap_xy: false + mirror_x: true + mirror_y: true + data_rate: 80MHz + cs_pin: GPIO22 + dc_pin: GPIO21 + auto_clear_enabled: false + invert_colors: false + update_interval: never + +packages: + lvgl: !include lvgl-package.yaml + +<<: !include common.yaml diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml new file mode 100644 index 0000000000..f159431b99 --- /dev/null +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -0,0 +1,52 @@ +spi: + clk_pin: 14 + mosi_pin: 13 + +i2c: + sda: GPIO18 + scl: GPIO19 + +display: + - platform: ili9xxx + model: st7789v + id: second_display + dimensions: + width: 240 + height: 320 + transform: + swap_xy: false + mirror_x: true + mirror_y: true + data_rate: 80MHz + cs_pin: GPIO20 + dc_pin: GPIO15 + auto_clear_enabled: false + invert_colors: false + update_interval: never + + - platform: ili9xxx + model: st7789v + id: tft_display + dimensions: + width: 240 + height: 320 + transform: + swap_xy: false + mirror_x: true + mirror_y: true + data_rate: 80MHz + cs_pin: GPIO22 + dc_pin: GPIO21 + auto_clear_enabled: false + invert_colors: false + update_interval: never + +packages: + lvgl: !include lvgl-package.yaml + +lvgl: + displays: + - tft_display + - second_display + +<<: !include common.yaml From d3f2434c57034c0cac30c997f80850df59e40028 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 24 Jul 2024 19:45:42 -0500 Subject: [PATCH 0097/1052] Bump aioesphomeapi to 24.6.2 and cryptography to 43.0.0 (#7131) --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0cbe5e7265..3e658de8ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ async_timeout==4.0.3; python_version <= "3.10" -cryptography==42.0.2 +cryptography==43.0.0 voluptuous==0.14.2 PyYAML==6.0.1 paho-mqtt==1.6.1 @@ -13,7 +13,7 @@ platformio==6.1.15 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20240620.0 -aioesphomeapi==24.3.0 +aioesphomeapi==24.6.2 zeroconf==0.132.2 python-magic==0.4.27 ruamel.yaml==0.18.6 # dashboard_import From f61582f82600b26e5747afe5b5f1f5b642a47ee6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 25 Jul 2024 13:19:33 +1200 Subject: [PATCH 0098/1052] [dependabot] Group docker action bumps into single PR (#7133) --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3b6495653b..bb35f16048 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,13 @@ updates: schedule: interval: daily open-pull-requests-limit: 10 + groups: + docker-actions: + applies-to: version-updates + patterns: + - "docker/setup-qemu-action" + - "docker/login-action" + - "docker/setup-buildx-action" - package-ecosystem: github-actions directory: "/.github/actions/build-image" schedule: From 39c0019534f5e6aa67d2e71f69a04d8c3ff28343 Mon Sep 17 00:00:00 2001 From: thevogoncoder <6619878+thevogoncoder@users.noreply.github.com> Date: Thu, 25 Jul 2024 04:06:23 +0200 Subject: [PATCH 0099/1052] Add delay after sending REG_READ_START (#7130) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/pmwcs3/pmwcs3.cpp | 61 ++++++++++++++-------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/esphome/components/pmwcs3/pmwcs3.cpp b/esphome/components/pmwcs3/pmwcs3.cpp index 812018b52e..97ce4c9ae0 100644 --- a/esphome/components/pmwcs3/pmwcs3.cpp +++ b/esphome/components/pmwcs3/pmwcs3.cpp @@ -72,43 +72,44 @@ void PMWCS3Component::dump_config() { LOG_SENSOR(" ", "vwc", this->vwc_sensor_); } void PMWCS3Component::read_data_() { - uint8_t data[8]; - float e25, ec, temperature, vwc; - /////// Super important !!!! first activate reading PMWCS3_REG_READ_START (if not, return always the same values) //// - if (!this->write_bytes(PMWCS3_REG_READ_START, nullptr, 0)) { this->status_set_warning(); ESP_LOGVV(TAG, "Failed to write into REG_READ_START register !!!"); return; } - // NOLINT delay(100); - if (!this->read_bytes(PMWCS3_REG_GET_DATA, (uint8_t *) &data, 8)) { - ESP_LOGVV(TAG, "Error reading PMWCS3_REG_GET_DATA registers"); - this->mark_failed(); - return; - } - if (this->e25_sensor_ != nullptr) { - e25 = ((data[1] << 8) | data[0]) / 100.0; - this->e25_sensor_->publish_state(e25); - ESP_LOGVV(TAG, "e25: data[0]=%d, data[1]=%d, result=%f", data[0], data[1], e25); - } - if (this->ec_sensor_ != nullptr) { - ec = ((data[3] << 8) | data[2]) / 10.0; - this->ec_sensor_->publish_state(ec); - ESP_LOGVV(TAG, "ec: data[2]=%d, data[3]=%d, result=%f", data[2], data[3], ec); - } - if (this->temperature_sensor_ != nullptr) { - temperature = ((data[5] << 8) | data[4]) / 100.0; - this->temperature_sensor_->publish_state(temperature); - ESP_LOGVV(TAG, "temp: data[4]=%d, data[5]=%d, result=%f", data[4], data[5], temperature); - } - if (this->vwc_sensor_ != nullptr) { - vwc = ((data[7] << 8) | data[6]) / 10.0; - this->vwc_sensor_->publish_state(vwc); - ESP_LOGVV(TAG, "vwc: data[6]=%d, data[7]=%d, result=%f", data[6], data[7], vwc); - } + // Wait for the sensor to be ready. + // 80ms empirically determined (conservative). + this->set_timeout(80, [this] { + 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"); + this->mark_failed(); + return; + } + if (this->e25_sensor_ != nullptr) { + e25 = ((data[1] << 8) | data[0]) / 100.0; + this->e25_sensor_->publish_state(e25); + ESP_LOGVV(TAG, "e25: data[0]=%d, data[1]=%d, result=%f", data[0], data[1], e25); + } + if (this->ec_sensor_ != nullptr) { + ec = ((data[3] << 8) | data[2]) / 10.0; + this->ec_sensor_->publish_state(ec); + ESP_LOGVV(TAG, "ec: data[2]=%d, data[3]=%d, result=%f", data[2], data[3], ec); + } + if (this->temperature_sensor_ != nullptr) { + temperature = ((data[5] << 8) | data[4]) / 100.0; + this->temperature_sensor_->publish_state(temperature); + ESP_LOGVV(TAG, "temp: data[4]=%d, data[5]=%d, result=%f", data[4], data[5], temperature); + } + if (this->vwc_sensor_ != nullptr) { + vwc = ((data[7] << 8) | data[6]) / 10.0; + this->vwc_sensor_->publish_state(vwc); + ESP_LOGVV(TAG, "vwc: data[6]=%d, data[7]=%d, result=%f", data[6], data[7], vwc); + } + }); } } // namespace pmwcs3 From adfec578cfd301ee0295bd1dec6a649ff98fcb8b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:13:09 +1200 Subject: [PATCH 0100/1052] Add ``--version`` handler to cli (#7150) --- esphome/__main__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index b13f96daf7..9e7b7fa15b 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -747,7 +747,14 @@ def parse_args(argv): ) parser = argparse.ArgumentParser( - description=f"ESPHome v{const.__version__}", parents=[options_parser] + description=f"ESPHome {const.__version__}", parents=[options_parser] + ) + + parser.add_argument( + "--version", + action="version", + version=f"Version: {const.__version__}", + help="Print the ESPHome version and exit.", ) mqtt_options = argparse.ArgumentParser(add_help=False) From acf690c87d362b37dc3b8910bcc7ffd4882bc2f2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:05:41 +1200 Subject: [PATCH 0101/1052] [code-quality] Organise ethernet related imports (#7152) --- esphome/components/ethernet/__init__.py | 36 +++++++++---------- .../components/ethernet_info/text_sensor.py | 4 +-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 697436415b..1c6acda724 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,6 +1,4 @@ from esphome import pins -import esphome.config_validation as cv -import esphome.final_validate as fv import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant from esphome.components.esp32.const import ( @@ -8,31 +6,33 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, ) +from esphome.components.network import IPAddress +from esphome.components.spi import CONF_INTERFACE_INDEX, get_spi_interface +import esphome.config_validation as cv from esphome.const import ( - CONF_DOMAIN, - CONF_ID, - CONF_VALUE, - CONF_MANUAL_IP, - CONF_STATIC_IP, - CONF_TYPE, - CONF_USE_ADDRESS, - CONF_GATEWAY, - CONF_SUBNET, + CONF_ADDRESS, + CONF_CLK_PIN, + CONF_CS_PIN, CONF_DNS1, CONF_DNS2, - CONF_CLK_PIN, + CONF_DOMAIN, + CONF_GATEWAY, + CONF_ID, + CONF_INTERRUPT_PIN, + CONF_MANUAL_IP, CONF_MISO_PIN, CONF_MOSI_PIN, - CONF_CS_PIN, - CONF_INTERRUPT_PIN, + CONF_PAGE_ID, CONF_RESET_PIN, CONF_SPI, - CONF_PAGE_ID, - CONF_ADDRESS, + CONF_STATIC_IP, + CONF_SUBNET, + CONF_TYPE, + CONF_USE_ADDRESS, + CONF_VALUE, ) from esphome.core import CORE, coroutine_with_priority -from esphome.components.network import IPAddress -from esphome.components.spi import get_spi_interface, CONF_INTERFACE_INDEX +import esphome.final_validate as fv CONFLICTS_WITH = ["wifi"] DEPENDENCIES = ["esp32"] diff --git a/esphome/components/ethernet_info/text_sensor.py b/esphome/components/ethernet_info/text_sensor.py index a545475870..31da516e44 100644 --- a/esphome/components/ethernet_info/text_sensor.py +++ b/esphome/components/ethernet_info/text_sensor.py @@ -1,9 +1,9 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import text_sensor +import esphome.config_validation as cv from esphome.const import ( - CONF_IP_ADDRESS, CONF_DNS_ADDRESS, + CONF_IP_ADDRESS, CONF_MAC_ADDRESS, ENTITY_CATEGORY_DIAGNOSTIC, ) From 20c22465335231017b4300827ad3e68ce968bd4f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:06:08 +1200 Subject: [PATCH 0102/1052] [code-quality] Organise wifi related imports (#7153) --- esphome/components/wifi/__init__.py | 31 +++++++++++---------- esphome/components/wifi/wpa2_eap.py | 13 ++++----- esphome/components/wifi_info/text_sensor.py | 6 ++-- esphome/components/wifi_signal/sensor.py | 2 +- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 624bcdabdc..ea03cc16d1 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -1,15 +1,19 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -import esphome.final_validate as fv from esphome import automation from esphome.automation import Condition +import esphome.codegen as cg +from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant +from esphome.components.network import IPAddress +import esphome.config_validation as cv from esphome.const import ( CONF_AP, CONF_BSSID, + CONF_CERTIFICATE, + CONF_CERTIFICATE_AUTHORITY, CONF_CHANNEL, CONF_DNS1, CONF_DNS2, CONF_DOMAIN, + CONF_EAP, CONF_ENABLE_BTM, CONF_ENABLE_ON_BOOT, CONF_ENABLE_RRM, @@ -17,29 +21,26 @@ from esphome.const import ( CONF_GATEWAY, CONF_HIDDEN, CONF_ID, + CONF_IDENTITY, + CONF_KEY, CONF_MANUAL_IP, CONF_NETWORKS, + CONF_ON_CONNECT, + CONF_ON_DISCONNECT, CONF_PASSWORD, CONF_POWER_SAVE_MODE, + CONF_PRIORITY, CONF_REBOOT_TIMEOUT, CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, - CONF_USE_ADDRESS, - CONF_PRIORITY, - CONF_IDENTITY, - CONF_CERTIFICATE_AUTHORITY, - CONF_CERTIFICATE, - CONF_KEY, - CONF_USERNAME, - CONF_EAP, CONF_TTLS_PHASE_2, - CONF_ON_CONNECT, - CONF_ON_DISCONNECT, + CONF_USE_ADDRESS, + CONF_USERNAME, ) from esphome.core import CORE, HexInt, coroutine_with_priority -from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const -from esphome.components.network import IPAddress +import esphome.final_validate as fv + from . import wpa2_eap AUTO_LOAD = ["network"] diff --git a/esphome/components/wifi/wpa2_eap.py b/esphome/components/wifi/wpa2_eap.py index 3985dfef18..5d5bd8dca3 100644 --- a/esphome/components/wifi/wpa2_eap.py +++ b/esphome/components/wifi/wpa2_eap.py @@ -7,16 +7,15 @@ so that it doesn't crash if it's not installed. import logging from pathlib import Path -from esphome.core import CORE import esphome.config_validation as cv from esphome.const import ( - CONF_USERNAME, - CONF_IDENTITY, - CONF_PASSWORD, CONF_CERTIFICATE, + CONF_IDENTITY, CONF_KEY, + CONF_PASSWORD, + CONF_USERNAME, ) - +from esphome.core import CORE _LOGGER = logging.getLogger(__name__) @@ -49,8 +48,8 @@ def wrapped_load_pem_x509_certificate(value): def wrapped_load_pem_private_key(value, password): validate_cryptography_installed() - from cryptography.hazmat.primitives.serialization import load_pem_private_key from cryptography.hazmat.backends import default_backend + from cryptography.hazmat.primitives.serialization import load_pem_private_key if password: password = password.encode("UTF-8") @@ -91,7 +90,7 @@ def _validate_load_private_key(key, cert_pw): def _check_private_key_cert_match(key, cert): - from cryptography.hazmat.primitives.asymmetric import rsa, ec + from cryptography.hazmat.primitives.asymmetric import ec, rsa def check_match_a(): return key.public_key().public_numbers() == cert.public_key().public_numbers() diff --git a/esphome/components/wifi_info/text_sensor.py b/esphome/components/wifi_info/text_sensor.py index 75513712dd..4ceb73a695 100644 --- a/esphome/components/wifi_info/text_sensor.py +++ b/esphome/components/wifi_info/text_sensor.py @@ -1,13 +1,13 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import text_sensor +import esphome.config_validation as cv from esphome.const import ( CONF_BSSID, + CONF_DNS_ADDRESS, CONF_IP_ADDRESS, + CONF_MAC_ADDRESS, CONF_SCAN_RESULTS, CONF_SSID, - CONF_MAC_ADDRESS, - CONF_DNS_ADDRESS, ENTITY_CATEGORY_DIAGNOSTIC, ) diff --git a/esphome/components/wifi_signal/sensor.py b/esphome/components/wifi_signal/sensor.py index 77fabf272e..99b51adea0 100644 --- a/esphome/components/wifi_signal/sensor.py +++ b/esphome/components/wifi_signal/sensor.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import sensor +import esphome.config_validation as cv from esphome.const import ( DEVICE_CLASS_SIGNAL_STRENGTH, ENTITY_CATEGORY_DIAGNOSTIC, From e64709c37e22148572a182461f98430982e3228f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:07:44 +1200 Subject: [PATCH 0103/1052] [code-quality] Organise core imports (#7149) --- esphome/__main__.py | 12 ++-- esphome/automation.py | 2 +- esphome/codegen.py | 106 ++++++++++++++++---------------- esphome/config.py | 42 ++++++------- esphome/config_validation.py | 42 ++++++------- esphome/core/__init__.py | 19 +++--- esphome/core/config.py | 6 +- esphome/core/entity_helpers.py | 3 +- esphome/coroutine.py | 2 +- esphome/cpp_generator.py | 2 +- esphome/cpp_helpers.py | 8 +-- esphome/dashboard/core.py | 6 +- esphome/dashboard/dashboard.py | 6 +- esphome/dashboard/entries.py | 2 +- esphome/dashboard/util/file.py | 2 +- esphome/dashboard/web_server.py | 6 +- esphome/external_files.py | 10 +-- esphome/final_validate.py | 4 +- esphome/git.py | 10 +-- esphome/helpers.py | 12 ++-- esphome/mqtt.py | 6 +- esphome/pins.py | 12 ++-- esphome/platformio_api.py | 10 +-- esphome/storage_json.py | 3 +- esphome/types.py | 2 +- esphome/util.py | 5 +- esphome/voluptuous_schema.py | 1 + esphome/vscode.py | 9 +-- esphome/wizard.py | 2 +- esphome/writer.py | 16 ++--- esphome/yaml_util.py | 6 +- esphome/zeroconf.py | 2 +- 32 files changed, 190 insertions(+), 186 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 9e7b7fa15b..13f09e15ed 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -1,12 +1,12 @@ # PYTHON_ARGCOMPLETE_OK import argparse +from datetime import datetime import functools import logging import os import re import sys import time -from datetime import datetime import argcomplete @@ -39,14 +39,14 @@ from esphome.const import ( ) from esphome.core import CORE, EsphomeError, coroutine from esphome.helpers import indent, is_ip_address +from esphome.log import Fore, color, setup_log from esphome.util import ( + get_serial_ports, + list_yaml_files, run_external_command, run_external_process, safe_print, - list_yaml_files, - get_serial_ports, ) -from esphome.log import color, setup_log, Fore _LOGGER = logging.getLogger(__name__) @@ -116,6 +116,7 @@ def get_port_type(port): def run_miniterm(config, port): import serial + from esphome import platformio_api if CONF_LOGGER not in config: @@ -596,9 +597,10 @@ def command_update_all(args): def command_idedata(args, config): - from esphome import platformio_api import json + from esphome import platformio_api + logging.disable(logging.INFO) logging.disable(logging.WARNING) diff --git a/esphome/automation.py b/esphome/automation.py index b25ffa5abe..0bd6cf0af0 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -7,10 +7,10 @@ from esphome.const import ( CONF_ELSE, CONF_ID, CONF_THEN, + CONF_TIME, CONF_TIMEOUT, CONF_TRIGGER_ID, CONF_TYPE_ID, - CONF_TIME, CONF_UPDATE_INTERVAL, ) from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor diff --git a/esphome/codegen.py b/esphome/codegen.py index 6b000b53a1..bfa1683ce7 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -8,55 +8,78 @@ # want to break suddenly due to a rename (this file will get backports for features). # pylint: disable=unused-import -from esphome.cpp_generator import ( # noqa +from esphome.cpp_generator import ( # noqa: F401 + ArrayInitializer, Expression, + LineComment, + MockObj, + MockObjClass, + Pvariable, RawExpression, RawStatement, - TemplateArguments, - StructInitializer, - ArrayInitializer, - safe_exp, Statement, - LineComment, - progmem_array, - static_const_array, - statement, - variable, - with_local_variable, - new_variable, - Pvariable, - new_Pvariable, + StructInitializer, + TemplateArguments, add, - add_global, - add_library, add_build_flag, add_define, + add_global, + add_library, add_platformio_option, get_variable, get_variable_with_full_id, - process_lambda, is_template, + new_Pvariable, + new_variable, + process_lambda, + progmem_array, + safe_exp, + statement, + static_const_array, templatable, - MockObj, - MockObjClass, + variable, + with_local_variable, ) -from esphome.cpp_helpers import ( # noqa - gpio_pin_expression, - register_component, +from esphome.cpp_helpers import ( # noqa: F401 build_registry_entry, build_registry_list, extract_registry_entry_config, - register_parented, + gpio_pin_expression, past_safe_mode, + register_component, + register_parented, ) -from esphome.cpp_types import ( # noqa - global_ns, - void, - nullptr, - float_, - double, +from esphome.cpp_types import ( # noqa: F401 + NAN, + App, + Application, + Component, + ComponentPtr, + Controller, + EntityBase, + EntityCategory, + ESPTime, + GPIOPin, + InternalGPIOPin, + JsonObject, + JsonObjectConst, + Parented, + PollingComponent, + arduino_json_ns, bool_, + const_char_ptr, + double, + esphome_ns, + float_, + global_ns, + gpio_Flags, + int16, + int32, + int64, int_, + nullptr, + optional, + size_t, std_ns, std_shared_ptr, std_string, @@ -66,28 +89,5 @@ from esphome.cpp_types import ( # noqa uint16, uint32, uint64, - int16, - int32, - int64, - size_t, - const_char_ptr, - NAN, - esphome_ns, - App, - EntityBase, - Component, - ComponentPtr, - PollingComponent, - Application, - optional, - arduino_json_ns, - JsonObject, - JsonObjectConst, - Controller, - GPIOPin, - InternalGPIOPin, - gpio_Flags, - EntityCategory, - Parented, - ESPTime, + void, ) diff --git a/esphome/config.py b/esphome/config.py index 925a31fed0..a2d0d15477 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -1,40 +1,38 @@ from __future__ import annotations + import abc +from contextlib import contextmanager +import contextvars import functools import heapq import logging import re - -from typing import Union, Any - -from contextlib import contextmanager -import contextvars +from typing import Any, Union import voluptuous as vol -from esphome import core, yaml_util, loader, pins -import esphome.core.config as core_config +from esphome import core, loader, pins, yaml_util +from esphome.config_helpers import Extend, Remove +import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, - CONF_ID, - CONF_PLATFORM, - CONF_PACKAGES, - CONF_SUBSTITUTIONS, CONF_EXTERNAL_COMPONENTS, + CONF_ID, + CONF_PACKAGES, + CONF_PLATFORM, + CONF_SUBSTITUTIONS, TARGET_PLATFORMS, ) -from esphome.core import CORE, EsphomeError, DocumentRange -from esphome.helpers import indent -from esphome.util import safe_print, OrderedDict - -from esphome.config_helpers import Extend, Remove -from esphome.loader import get_component, get_platform, ComponentManifest -from esphome.yaml_util import is_secret, ESPHomeDataBase, ESPForceValue -from esphome.voluptuous_schema import ExtraKeysInvalid -from esphome.log import color, Fore +from esphome.core import CORE, DocumentRange, EsphomeError +import esphome.core.config as core_config import esphome.final_validate as fv -import esphome.config_validation as cv -from esphome.types import ConfigType, ConfigFragmentType +from esphome.helpers import indent +from esphome.loader import ComponentManifest, get_component, get_platform +from esphome.log import Fore, color +from esphome.types import ConfigFragmentType, ConfigType +from esphome.util import OrderedDict, safe_print +from esphome.voluptuous_schema import ExtraKeysInvalid +from esphome.yaml_util import ESPForceValue, ESPHomeDataBase, is_secret _LOGGER = logging.getLogger(__name__) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 3ef92ad460..1cd1d6aa31 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1,13 +1,13 @@ """Helpers for config validation using voluptuous.""" +from contextlib import contextmanager from dataclasses import dataclass +from datetime import datetime import logging import os import re -from contextlib import contextmanager -import uuid as uuid_ -from datetime import datetime from string import ascii_letters, digits +import uuid as uuid_ import voluptuous as vol @@ -17,37 +17,37 @@ from esphome.config_helpers import Extend, Remove from esphome.const import ( ALLOWED_NAME_CHARS, CONF_AVAILABILITY, - CONF_COMMAND_TOPIC, CONF_COMMAND_RETAIN, + CONF_COMMAND_TOPIC, + CONF_DAY, CONF_DISABLED_BY_DEFAULT, CONF_DISCOVERY, CONF_ENTITY_CATEGORY, + CONF_HOUR, CONF_ICON, CONF_ID, CONF_INTERNAL, + CONF_MINUTE, + CONF_MONTH, CONF_NAME, + CONF_PASSWORD, + CONF_PATH, CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, - CONF_RETAIN, CONF_QOS, + CONF_REF, + CONF_RETAIN, + CONF_SECOND, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, CONF_TOPIC, - CONF_YEAR, - CONF_MONTH, - CONF_DAY, - CONF_HOUR, - CONF_MINUTE, - CONF_SECOND, - CONF_VALUE, - CONF_UPDATE_INTERVAL, - CONF_TYPE_ID, CONF_TYPE, - CONF_REF, + CONF_TYPE_ID, + CONF_UPDATE_INTERVAL, CONF_URL, - CONF_PATH, CONF_USERNAME, - CONF_PASSWORD, + CONF_VALUE, + CONF_YEAR, ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_NONE, @@ -71,15 +71,15 @@ from esphome.core import ( TimePeriod, TimePeriodMicroseconds, TimePeriodMilliseconds, + TimePeriodMinutes, TimePeriodNanoseconds, TimePeriodSeconds, - TimePeriodMinutes, ) -from esphome.helpers import list_starts_with, add_class_to_obj +from esphome.helpers import add_class_to_obj, list_starts_with from esphome.schema_extractors import ( SCHEMA_EXTRACT, - schema_extractor_list, schema_extractor, + schema_extractor_list, schema_extractor_registry, schema_extractor_typed, ) @@ -1686,9 +1686,9 @@ class SplitDefault(Optional): if CORE.is_esp32: from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32.const import ( + VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3, - VARIANT_ESP32C3, ) variant = get_esp32_variant() diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index f25891965a..9d3d14492e 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -7,26 +7,29 @@ from typing import TYPE_CHECKING, Optional, Union from esphome.const import ( CONF_COMMENT, CONF_ESPHOME, - CONF_USE_ADDRESS, CONF_ETHERNET, + CONF_PORT, + CONF_USE_ADDRESS, CONF_WEB_SERVER, CONF_WIFI, - CONF_PORT, KEY_CORE, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, + PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, - PLATFORM_BK72XX, - PLATFORM_RTL87XX, - PLATFORM_RP2040, PLATFORM_HOST, + PLATFORM_RP2040, + PLATFORM_RTL87XX, ) -from esphome.coroutine import FakeAwaitable as _FakeAwaitable -from esphome.coroutine import FakeEventLoop as _FakeEventLoop # pylint: disable=unused-import -from esphome.coroutine import coroutine, coroutine_with_priority # noqa +from esphome.coroutine import ( # noqa: F401 + FakeAwaitable as _FakeAwaitable, + FakeEventLoop as _FakeEventLoop, + coroutine, + coroutine_with_priority, +) from esphome.helpers import ensure_unique_string, get_str_env, is_ha_addon from esphome.util import OrderedDict diff --git a/esphome/core/config.py b/esphome/core/config.py index 80b731b905..739a8a1aea 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -3,9 +3,9 @@ import multiprocessing import os import re +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation from esphome.const import ( CONF_ARDUINO_VERSION, CONF_AREA, @@ -16,11 +16,11 @@ from esphome.const import ( CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, CONF_FRAMEWORK, + CONF_FRIENDLY_NAME, CONF_INCLUDES, CONF_LIBRARIES, CONF_MIN_VERSION, CONF_NAME, - CONF_FRIENDLY_NAME, CONF_ON_BOOT, CONF_ON_LOOP, CONF_ON_SHUTDOWN, @@ -34,8 +34,8 @@ from esphome.const import ( CONF_TYPE, CONF_VERSION, KEY_CORE, - TARGET_PLATFORMS, PLATFORM_ESP8266, + TARGET_PLATFORMS, __version__ as ESPHOME_VERSION, ) from esphome.core import CORE, coroutine_with_priority diff --git a/esphome/core/entity_helpers.py b/esphome/core/entity_helpers.py index f921711ec2..7f6a9b48ab 100644 --- a/esphome/core/entity_helpers.py +++ b/esphome/core/entity_helpers.py @@ -1,6 +1,5 @@ -import esphome.final_validate as fv - from esphome.const import CONF_ID +import esphome.final_validate as fv def inherit_property_from(property_to_inherit, parent_id_property, transform=None): diff --git a/esphome/coroutine.py b/esphome/coroutine.py index 5f391dc7ad..30ebb8147e 100644 --- a/esphome/coroutine.py +++ b/esphome/coroutine.py @@ -43,13 +43,13 @@ the last `yield` expression defines what is returned. """ import collections +from collections.abc import Awaitable, Generator, Iterator import functools import heapq import inspect import logging import types from typing import Any, Callable -from collections.abc import Awaitable, Generator, Iterator _LOGGER = logging.getLogger(__name__) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 9a4cb2269a..7a82d5cba1 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -1,8 +1,8 @@ import abc +from collections.abc import Sequence import inspect import math import re -from collections.abc import Sequence from typing import Any, Callable, Optional, Union from esphome.core import ( diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 825224bb9d..9a775bad33 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -12,15 +12,13 @@ from esphome.const import ( CONF_UPDATE_INTERVAL, KEY_PAST_SAFE_MODE, ) - -from esphome.core import coroutine, ID, CORE +from esphome.core import CORE, ID, coroutine from esphome.coroutine import FakeAwaitable -from esphome.types import ConfigType, ConfigFragmentType from esphome.cpp_generator import add, get_variable from esphome.cpp_types import App +from esphome.helpers import sanitize, snake_case +from esphome.types import ConfigFragmentType, ConfigType from esphome.util import Registry, RegistryEntry -from esphome.helpers import snake_case, sanitize - _LOGGER = logging.getLogger(__name__) diff --git a/esphome/dashboard/core.py b/esphome/dashboard/core.py index 875ff6b91f..eec0777da6 100644 --- a/esphome/dashboard/core.py +++ b/esphome/dashboard/core.py @@ -1,13 +1,13 @@ from __future__ import annotations import asyncio +from collections.abc import Coroutine import contextlib -import logging -import threading from dataclasses import dataclass from functools import partial +import logging +import threading from typing import TYPE_CHECKING, Any, Callable -from collections.abc import Coroutine from ..zeroconf import DiscoveredImport from .dns import DNSCache diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 2be98ab3e4..9de2d39ce2 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -1,14 +1,14 @@ from __future__ import annotations import asyncio +from asyncio import events +from concurrent.futures import ThreadPoolExecutor import logging import os import socket import threading -import traceback -from asyncio import events -from concurrent.futures import ThreadPoolExecutor from time import monotonic +import traceback from typing import Any from esphome.storage_json import EsphomeStorageJSON, esphome_storage_path diff --git a/esphome/dashboard/entries.py b/esphome/dashboard/entries.py index 7a9bff4ec1..cb0d4a3772 100644 --- a/esphome/dashboard/entries.py +++ b/esphome/dashboard/entries.py @@ -1,9 +1,9 @@ from __future__ import annotations import asyncio +from collections import defaultdict import logging import os -from collections import defaultdict from typing import TYPE_CHECKING, Any from esphome import const, util diff --git a/esphome/dashboard/util/file.py b/esphome/dashboard/util/file.py index 661d5f34cf..bb263f9ad7 100644 --- a/esphome/dashboard/util/file.py +++ b/esphome/dashboard/util/file.py @@ -1,7 +1,7 @@ import logging import os -import tempfile from pathlib import Path +import tempfile _LOGGER = logging.getLogger(__name__) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 33c83ffb1a..e4b7b8d342 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -2,6 +2,7 @@ from __future__ import annotations import asyncio import base64 +from collections.abc import Iterable import datetime import functools import gzip @@ -9,13 +10,12 @@ import hashlib import json import logging import os +from pathlib import Path import secrets import shutil import subprocess import threading import time -from collections.abc import Iterable -from pathlib import Path from typing import TYPE_CHECKING, Any, Callable, TypeVar from urllib.parse import urlparse @@ -26,13 +26,13 @@ import tornado.httpserver import tornado.httputil import tornado.ioloop import tornado.iostream +from tornado.log import access_log import tornado.netutil import tornado.process import tornado.queues import tornado.web import tornado.websocket import yaml -from tornado.log import access_log from yaml.nodes import Node from esphome import const, platformio_api, yaml_util diff --git a/esphome/external_files.py b/esphome/external_files.py index f8eb1dcabe..baf62286e4 100644 --- a/esphome/external_files.py +++ b/esphome/external_files.py @@ -1,13 +1,15 @@ from __future__ import annotations -import logging -from pathlib import Path -import os from datetime import datetime +import logging +import os +from pathlib import Path + import requests + import esphome.config_validation as cv -from esphome.core import CORE, TimePeriodSeconds from esphome.const import __version__ +from esphome.core import CORE, TimePeriodSeconds _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@landonr"] diff --git a/esphome/final_validate.py b/esphome/final_validate.py index 5e9d2207b0..cebd2f1cda 100644 --- a/esphome/final_validate.py +++ b/esphome/final_validate.py @@ -1,9 +1,9 @@ from abc import ABC, abstractmethod -from typing import Any import contextvars +from typing import Any -from esphome.types import ConfigFragmentType, ID, ConfigPathType import esphome.config_validation as cv +from esphome.types import ID, ConfigFragmentType, ConfigPathType class FinalValidateConfig(ABC): diff --git a/esphome/git.py b/esphome/git.py index e41777f425..144c160b20 100644 --- a/esphome/git.py +++ b/esphome/git.py @@ -1,12 +1,12 @@ -import hashlib -import logging -import re -import subprocess -import urllib.parse from dataclasses import dataclass from datetime import datetime +import hashlib +import logging from pathlib import Path +import re +import subprocess from typing import Callable, Optional +import urllib.parse import esphome.config_validation as cv from esphome.core import CORE, TimePeriodSeconds diff --git a/esphome/helpers.py b/esphome/helpers.py index 4c8cb4e2cc..2a7e5cd9b6 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -1,14 +1,13 @@ import codecs from contextlib import suppress - import logging import os -import platform from pathlib import Path -from typing import Union -import tempfile -from urllib.parse import urlparse +import platform import re +import tempfile +from typing import Union +from urllib.parse import urlparse _LOGGER = logging.getLogger(__name__) @@ -129,9 +128,10 @@ def _resolve_with_zeroconf(host): def resolve_ip_address(host): - from esphome.core import EsphomeError import socket + from esphome.core import EsphomeError + errs = [] if host.endswith(".local"): diff --git a/esphome/mqtt.py b/esphome/mqtt.py index 667a20bcf8..d7e14a1d08 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -1,10 +1,10 @@ from datetime import datetime import hashlib +import json import logging import ssl import sys import time -import json import paho.mqtt.client as mqtt @@ -24,9 +24,9 @@ from esphome.const import ( CONF_USERNAME, ) from esphome.core import CORE, EsphomeError -from esphome.log import color, Fore +from esphome.helpers import get_int_env, get_str_env +from esphome.log import Fore, color from esphome.util import safe_print -from esphome.helpers import get_str_env, get_int_env _LOGGER = logging.getLogger(__name__) diff --git a/esphome/pins.py b/esphome/pins.py index 5ccb696738..724cd25d82 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -1,20 +1,20 @@ -import operator from functools import reduce -import esphome.config_validation as cv -from esphome.core import CORE +import operator +import esphome.config_validation as cv from esphome.const import ( + CONF_ALLOW_OTHER_USES, + CONF_IGNORE_STRAPPING_WARNING, CONF_INPUT, + CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_OPEN_DRAIN, CONF_OUTPUT, CONF_PULLDOWN, CONF_PULLUP, - CONF_IGNORE_STRAPPING_WARNING, - CONF_ALLOW_OTHER_USES, - CONF_INVERTED, ) +from esphome.core import CORE class PinRegistry(dict): diff --git a/esphome/platformio_api.py b/esphome/platformio_api.py index c46a3fc767..b81ec4ab37 100644 --- a/esphome/platformio_api.py +++ b/esphome/platformio_api.py @@ -1,12 +1,11 @@ from dataclasses import dataclass import json -from typing import Union -from pathlib import Path - import logging 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 @@ -20,9 +19,10 @@ def patch_structhash(): # removed/added. This might have unintended consequences, but this improves compile # times greatly when adding/removing components and a simple clean build solves # all issues - from platformio.run import helpers, cli - from os.path import join, isdir, getmtime from os import makedirs + from os.path import getmtime, isdir, join + + from platformio.run import cli, helpers def patched_clean_build_dir(build_dir, *args): from platformio import fs diff --git a/esphome/storage_json.py b/esphome/storage_json.py index 0a41a4f738..e2e7514904 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -1,10 +1,11 @@ from __future__ import annotations + import binascii import codecs +from datetime import datetime import json import logging import os -from datetime import datetime from esphome import const from esphome.const import CONF_DISABLED, CONF_MDNS diff --git a/esphome/types.py b/esphome/types.py index 27ec61ceff..4e69e3cbd7 100644 --- a/esphome/types.py +++ b/esphome/types.py @@ -2,7 +2,7 @@ from typing import Union -from esphome.core import ID, Lambda, EsphomeCore +from esphome.core import ID, EsphomeCore, Lambda ConfigFragmentType = Union[ str, diff --git a/esphome/util.py b/esphome/util.py index d5a4c60570..32fd90cd25 100644 --- a/esphome/util.py +++ b/esphome/util.py @@ -1,13 +1,12 @@ -from typing import Union - import collections import io import logging import os +from pathlib import Path import re import subprocess import sys -from pathlib import Path +from typing import Union from esphome import const diff --git a/esphome/voluptuous_schema.py b/esphome/voluptuous_schema.py index 9af6cb717c..7f1573b443 100644 --- a/esphome/voluptuous_schema.py +++ b/esphome/voluptuous_schema.py @@ -2,6 +2,7 @@ import difflib import itertools import voluptuous as vol + from esphome.schema_extractors import schema_extractor_extended diff --git a/esphome/vscode.py b/esphome/vscode.py index 8198d2659a..907ed88216 100644 --- a/esphome/vscode.py +++ b/esphome/vscode.py @@ -1,13 +1,14 @@ from __future__ import annotations + +from io import StringIO import json import os -from io import StringIO from typing import Any -from esphome.yaml_util import parse_yaml -from esphome.config import validate_config, _format_vol_invalid, Config -from esphome.core import CORE, DocumentRange +from esphome.config import Config, _format_vol_invalid, validate_config import esphome.config_validation as cv +from esphome.core import CORE, DocumentRange +from esphome.yaml_util import parse_yaml def _get_invalid_range(res: Config, invalid: cv.Invalid) -> DocumentRange | None: diff --git a/esphome/wizard.py b/esphome/wizard.py index f8911ae844..319fb31938 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -276,8 +276,8 @@ def wizard(path): from esphome.components.bk72xx import boards as bk72xx_boards from esphome.components.esp32 import boards as esp32_boards from esphome.components.esp8266 import boards as esp8266_boards - from esphome.components.rtl87xx import boards as rtl87xx_boards from esphome.components.rp2040 import boards as rp2040_boards + from esphome.components.rtl87xx import boards as rtl87xx_boards if not path.endswith(".yaml") and not path.endswith(".yml"): safe_print( diff --git a/esphome/writer.py b/esphome/writer.py index 3ad0e60d31..c6111cbe3f 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -1,27 +1,27 @@ import logging import os -import re from pathlib import Path +import re from typing import Union -from esphome.config import iter_components, iter_component_configs +from esphome import loader +from esphome.config import iter_component_configs, iter_components from esphome.const import ( + ENV_NOGITIGNORE, HEADER_FILE_EXTENSIONS, SOURCE_FILE_EXTENSIONS, __version__, - ENV_NOGITIGNORE, ) from esphome.core import CORE, EsphomeError from esphome.helpers import ( - mkdir_p, - read_file, - write_file_if_changed, - walk_files, copy_file_if_changed, get_bool_env, + mkdir_p, + read_file, + walk_files, + write_file_if_changed, ) from esphome.storage_json import StorageJSON, storage_path -from esphome import loader _LOGGER = logging.getLogger(__name__) diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index 06bfd8b217..d67511dfec 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -3,16 +3,16 @@ from __future__ import annotations import fnmatch import functools import inspect +from io import TextIOWrapper import logging import math import os -import uuid -from io import TextIOWrapper from typing import Any +import uuid import yaml -import yaml.constructor from yaml import SafeLoader as PurePythonLoader +import yaml.constructor try: from yaml import CSafeLoader as FastestAvailableSafeLoader diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index b67ea41323..b3ee64e259 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -1,8 +1,8 @@ from __future__ import annotations import asyncio -import logging from dataclasses import dataclass +import logging from typing import Callable from zeroconf import IPVersion, ServiceInfo, ServiceStateChange, Zeroconf From b3728697cc3997899bd7cb20fdeb9d8f3b1f06ae Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:13:57 +1200 Subject: [PATCH 0104/1052] Remove deprecated argument parser (#7151) * Remove deprecated argument parser * Add back removed argcomplete line --- esphome/__main__.py | 68 --------------------------------------------- 1 file changed, 68 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 13f09e15ed..7237a04717 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -957,67 +957,6 @@ def parse_args(argv): # a deprecation warning). arguments = argv[1:] - # On Python 3.9+ we can simply set exit_on_error=False in the constructor - def _raise(x): - raise argparse.ArgumentError(None, x) - - # First, try new-style parsing, but don't exit in case of failure - try: - # duplicate parser so that we can use the original one to raise errors later on - current_parser = argparse.ArgumentParser(add_help=False, parents=[parser]) - current_parser.set_defaults(deprecated_argv_suggestion=None) - current_parser.error = _raise - return current_parser.parse_args(arguments) - except argparse.ArgumentError: - pass - - # Second, try compat parsing and rearrange the command-line if it succeeds - # Disable argparse's built-in help option and add it manually to prevent this - # parser from printing the help messagefor the old format when invoked with -h. - compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False) - compat_parser.add_argument("-h", "--help", action="store_true") - compat_parser.add_argument("configuration", nargs="*") - compat_parser.add_argument( - "command", - choices=[ - "config", - "compile", - "upload", - "logs", - "run", - "clean-mqtt", - "wizard", - "mqtt-fingerprint", - "version", - "clean", - "dashboard", - "vscode", - "update-all", - ], - ) - - try: - compat_parser.error = _raise - result, unparsed = compat_parser.parse_known_args(argv[1:]) - last_option = len(arguments) - len(unparsed) - 1 - len(result.configuration) - unparsed = [ - "--device" if arg in ("--upload-port", "--serial-port") else arg - for arg in unparsed - ] - arguments = ( - arguments[0:last_option] - + [result.command] - + result.configuration - + unparsed - ) - deprecated_argv_suggestion = arguments - except argparse.ArgumentError: - # old-style parsing failed, don't suggest any argument - deprecated_argv_suggestion = None - - # Finally, run the new-style parser again with the possibly swapped arguments, - # and let it error out if the command is unparsable. - parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion) argcomplete.autocomplete(parser) return parser.parse_args(arguments) @@ -1032,13 +971,6 @@ def run_esphome(argv): # Show timestamp for dashboard access logs args.command == "dashboard", ) - if args.deprecated_argv_suggestion is not None and args.command != "vscode": - _LOGGER.warning( - "Calling ESPHome with the configuration before the command is deprecated " - "and will be removed in the future. " - ) - _LOGGER.warning("Please instead use:") - _LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion)) if sys.version_info < (3, 8, 0): _LOGGER.error( From 24515546fd25a265dbb704890a6d7c9285775b17 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:22:30 +1200 Subject: [PATCH 0105/1052] Move ``CONF_ON_ERROR`` to const.py (#7156) --- esphome/components/ota/__init__.py | 12 ++++++--- .../components/voice_assistant/__init__.py | 27 +++++++++---------- esphome/const.py | 1 + 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 4e447bfb2d..d9917a2aae 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,10 +1,15 @@ +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation +from esphome.const import ( + CONF_ESPHOME, + CONF_ON_ERROR, + CONF_OTA, + CONF_PLATFORM, + CONF_TRIGGER_ID, +) from esphome.core import CORE, coroutine_with_priority -from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM, CONF_TRIGGER_ID - CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5", "safe_mode"] @@ -13,7 +18,6 @@ IS_PLATFORM_COMPONENT = True CONF_ON_ABORT = "on_abort" CONF_ON_BEGIN = "on_begin" CONF_ON_END = "on_end" -CONF_ON_ERROR = "on_error" CONF_ON_PROGRESS = "on_progress" CONF_ON_STATE_CHANGE = "on_state_change" diff --git a/esphome/components/voice_assistant/__init__.py b/esphome/components/voice_assistant/__init__.py index c18f0a6850..031edbf27a 100644 --- a/esphome/components/voice_assistant/__init__.py +++ b/esphome/components/voice_assistant/__init__.py @@ -1,18 +1,18 @@ -import esphome.config_validation as cv -import esphome.codegen as cg - -from esphome.const import ( - CONF_ID, - CONF_MICROPHONE, - CONF_SPEAKER, - CONF_MEDIA_PLAYER, - CONF_ON_CLIENT_CONNECTED, - CONF_ON_CLIENT_DISCONNECTED, - CONF_ON_IDLE, -) from esphome import automation from esphome.automation import register_action, register_condition -from esphome.components import microphone, speaker, media_player +import esphome.codegen as cg +from esphome.components import media_player, microphone, speaker +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + CONF_MEDIA_PLAYER, + CONF_MICROPHONE, + CONF_ON_CLIENT_CONNECTED, + CONF_ON_CLIENT_DISCONNECTED, + CONF_ON_ERROR, + CONF_ON_IDLE, + CONF_SPEAKER, +) AUTO_LOAD = ["socket"] DEPENDENCIES = ["api", "microphone"] @@ -20,7 +20,6 @@ DEPENDENCIES = ["api", "microphone"] CODEOWNERS = ["@jesserockz"] CONF_ON_END = "on_end" -CONF_ON_ERROR = "on_error" CONF_ON_INTENT_END = "on_intent_end" CONF_ON_INTENT_START = "on_intent_start" CONF_ON_LISTENING = "on_listening" diff --git a/esphome/const.py b/esphome/const.py index faf6ce19fa..4357963384 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -539,6 +539,7 @@ CONF_ON_DOUBLE_CLICK = "on_double_click" CONF_ON_ENROLLMENT_DONE = "on_enrollment_done" CONF_ON_ENROLLMENT_FAILED = "on_enrollment_failed" CONF_ON_ENROLLMENT_SCAN = "on_enrollment_scan" +CONF_ON_ERROR = "on_error" CONF_ON_EVENT = "on_event" CONF_ON_FINGER_SCAN_INVALID = "on_finger_scan_invalid" CONF_ON_FINGER_SCAN_MATCHED = "on_finger_scan_matched" From 5b6b7c0d15098f7477bae68329fe76a1d8993cf5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Jul 2024 20:25:53 +1200 Subject: [PATCH 0106/1052] [code-quality] Organise esp32 imports (#7154) --- esphome/components/esp32/__init__.py | 19 ++++++++--------- esphome/components/esp32/boards.py | 2 +- esphome/components/esp32/gpio.py | 25 +++++++++++------------ esphome/components/esp32/gpio_esp32.py | 3 +-- esphome/components/esp32/gpio_esp32_c2.py | 3 +-- esphome/components/esp32/gpio_esp32_c3.py | 6 +----- esphome/components/esp32/gpio_esp32_c6.py | 3 +-- esphome/components/esp32/gpio_esp32_h2.py | 3 +-- esphome/components/esp32/gpio_esp32_s2.py | 3 +-- esphome/components/esp32/gpio_esp32_s3.py | 7 +------ 10 files changed, 29 insertions(+), 45 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 1effea708f..0a5dd46478 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -1,11 +1,12 @@ from dataclasses import dataclass -from typing import Union, Optional -from pathlib import Path import logging import os -import esphome.final_validate as fv +from pathlib import Path +from typing import Optional, Union -from esphome.helpers import copy_file_if_changed, write_file_if_changed, mkdir_p +from esphome import git +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( CONF_ADVANCED, CONF_BOARD, @@ -15,6 +16,7 @@ from esphome.const import ( CONF_IGNORE_EFUSE_MAC_CRC, CONF_NAME, CONF_PATH, + CONF_PLATFORM_VERSION, CONF_PLATFORMIO_OPTIONS, CONF_REF, CONF_REFRESH, @@ -32,13 +34,12 @@ from esphome.const import ( TYPE_GIT, TYPE_LOCAL, __version__, - CONF_PLATFORM_VERSION, ) from esphome.core import CORE, HexInt, TimePeriod -import esphome.config_validation as cv -import esphome.codegen as cg -from esphome import git +import esphome.final_validate as fv +from esphome.helpers import copy_file_if_changed, mkdir_p, write_file_if_changed +from .boards import BOARDS from .const import ( # noqa KEY_BOARD, KEY_COMPONENTS, @@ -54,12 +55,10 @@ from .const import ( # noqa VARIANT_FRIENDLY, VARIANTS, ) -from .boards import BOARDS # force import gpio to register pin schema from .gpio import esp32_pin_to_code # noqa - _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["preferences"] diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index cd85f3da97..60abcd447c 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -1,4 +1,4 @@ -from .const import VARIANT_ESP32, VARIANT_ESP32S2, VARIANT_ESP32C3, VARIANT_ESP32S3 +from .const import VARIANT_ESP32, VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3 ESP32_BASE_PINS = { "TX": 1, diff --git a/esphome/components/esp32/gpio.py b/esphome/components/esp32/gpio.py index 0d9cb5daf0..558ff51af8 100644 --- a/esphome/components/esp32/gpio.py +++ b/esphome/components/esp32/gpio.py @@ -1,22 +1,22 @@ from dataclasses import dataclass -from typing import Any import logging +from typing import Any +from esphome import pins +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_IGNORE_PIN_VALIDATION_ERROR, + CONF_IGNORE_STRAPPING_WARNING, CONF_INVERTED, CONF_MODE, CONF_NUMBER, CONF_OPEN_DRAIN, CONF_OUTPUT, - CONF_IGNORE_PIN_VALIDATION_ERROR, - CONF_IGNORE_STRAPPING_WARNING, PLATFORM_ESP32, ) -from esphome import pins from esphome.core import CORE -import esphome.config_validation as cv -import esphome.codegen as cg from . import boards from .const import ( @@ -24,22 +24,21 @@ from .const import ( KEY_ESP32, KEY_VARIANT, VARIANT_ESP32, - VARIANT_ESP32C3, - VARIANT_ESP32S2, - VARIANT_ESP32S3, VARIANT_ESP32C2, + VARIANT_ESP32C3, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32S2, + VARIANT_ESP32S3, esp32_ns, ) - from .gpio_esp32 import esp32_validate_gpio_pin, esp32_validate_supports -from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports -from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports -from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports from .gpio_esp32_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_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_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 ESP32InternalGPIOPin = esp32_ns.class_("ESP32InternalGPIOPin", cg.InternalGPIOPin) diff --git a/esphome/components/esp32/gpio_esp32.py b/esphome/components/esp32/gpio_esp32.py index d10b266c7a..e4d3b6aaf3 100644 --- a/esphome/components/esp32/gpio_esp32.py +++ b/esphome/components/esp32/gpio_esp32.py @@ -1,5 +1,6 @@ import logging +import esphome.config_validation as cv from esphome.const import ( CONF_INPUT, CONF_MODE, @@ -8,10 +9,8 @@ from esphome.const import ( CONF_PULLDOWN, CONF_PULLUP, ) -import esphome.config_validation as cv from esphome.pins import check_strapping_pin - _ESP_SDIO_PINS = { 6: "Flash Clock", 7: "Flash Data 0", diff --git a/esphome/components/esp32/gpio_esp32_c2.py b/esphome/components/esp32/gpio_esp32_c2.py index 0bee7d82bf..abdcb1b655 100644 --- a/esphome/components/esp32/gpio_esp32_c2.py +++ b/esphome/components/esp32/gpio_esp32_c2.py @@ -1,10 +1,9 @@ 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 -import esphome.config_validation as cv - _ESP32C2_STRAPPING_PINS = {8, 9} _LOGGER = logging.getLogger(__name__) diff --git a/esphome/components/esp32/gpio_esp32_c3.py b/esphome/components/esp32/gpio_esp32_c3.py index 6c70c09f9e..5b9ec0ebd9 100644 --- a/esphome/components/esp32/gpio_esp32_c3.py +++ b/esphome/components/esp32/gpio_esp32_c3.py @@ -1,11 +1,7 @@ import logging -from esphome.const import ( - CONF_INPUT, - CONF_MODE, - CONF_NUMBER, -) import esphome.config_validation as cv +from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER from esphome.pins import check_strapping_pin _ESP32C3_SPI_PSRAM_PINS = { diff --git a/esphome/components/esp32/gpio_esp32_c6.py b/esphome/components/esp32/gpio_esp32_c6.py index a1f777c625..bc735f85c4 100644 --- a/esphome/components/esp32/gpio_esp32_c6.py +++ b/esphome/components/esp32/gpio_esp32_c6.py @@ -1,8 +1,7 @@ import logging -from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER - import esphome.config_validation as cv +from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER from esphome.pins import check_strapping_pin _ESP32C6_SPI_PSRAM_PINS = { diff --git a/esphome/components/esp32/gpio_esp32_h2.py b/esphome/components/esp32/gpio_esp32_h2.py index d18ee8a2a6..7413bf4db5 100644 --- a/esphome/components/esp32/gpio_esp32_h2.py +++ b/esphome/components/esp32/gpio_esp32_h2.py @@ -1,8 +1,7 @@ import logging -from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER - import esphome.config_validation as cv +from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER _ESP32H2_SPI_FLASH_PINS = {6, 7, 15, 16, 17, 18, 19, 20, 21} diff --git a/esphome/components/esp32/gpio_esp32_s2.py b/esphome/components/esp32/gpio_esp32_s2.py index 82449532ec..331aeb9d94 100644 --- a/esphome/components/esp32/gpio_esp32_s2.py +++ b/esphome/components/esp32/gpio_esp32_s2.py @@ -1,5 +1,6 @@ import logging +import esphome.config_validation as cv from esphome.const import ( CONF_INPUT, CONF_MODE, @@ -8,8 +9,6 @@ from esphome.const import ( CONF_PULLDOWN, CONF_PULLUP, ) - -import esphome.config_validation as cv from esphome.pins import check_strapping_pin _ESP32S2_SPI_PSRAM_PINS = { diff --git a/esphome/components/esp32/gpio_esp32_s3.py b/esphome/components/esp32/gpio_esp32_s3.py index 8dcbf8c7bb..7120504693 100644 --- a/esphome/components/esp32/gpio_esp32_s3.py +++ b/esphome/components/esp32/gpio_esp32_s3.py @@ -1,12 +1,7 @@ import logging -from esphome.const import ( - CONF_INPUT, - CONF_MODE, - CONF_NUMBER, -) - import esphome.config_validation as cv +from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER from esphome.pins import check_strapping_pin _ESP_32S3_SPI_PSRAM_PINS = { From 341fc659589a592fef60321227209f1014ccf34e Mon Sep 17 00:00:00 2001 From: FreeBear-nc <67865163+FreeBear-nc@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:05:25 +0100 Subject: [PATCH 0107/1052] Add microAmp and milliAmp to defined units (#7157) --- esphome/const.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/const.py b/esphome/const.py index 4357963384..37844e1047 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1033,11 +1033,13 @@ UNIT_KILOWATT_HOURS = "kWh" UNIT_LUX = "lx" UNIT_METER = "m" UNIT_METER_PER_SECOND_SQUARED = "m/s²" +UNIT_MICROAMP = "µA" UNIT_MICROGRAMS_PER_CUBIC_METER = "µg/m³" UNIT_MICROMETER = "µm" UNIT_MICROSIEMENS_PER_CENTIMETER = "µS/cm" UNIT_MICROSILVERTS_PER_HOUR = "µSv/h" UNIT_MICROTESLA = "µT" +UNIT_MILLIAMP = "mA" UNIT_MILLIGRAMS_PER_CUBIC_METER = "mg/m³" UNIT_MILLIMETER = "mm" UNIT_MILLISECOND = "ms" From 25c8676d80cb9a6b75cd6413aa4f9a99f3a662e5 Mon Sep 17 00:00:00 2001 From: RubyBailey <60991881+RubyBailey@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:20:29 -0700 Subject: [PATCH 0108/1052] Fix for Mitsubishi units that only support cooling (#7143) --- esphome/components/mitsubishi/mitsubishi.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/esphome/components/mitsubishi/mitsubishi.cpp b/esphome/components/mitsubishi/mitsubishi.cpp index a02aabf14d..449c8fc712 100644 --- a/esphome/components/mitsubishi/mitsubishi.cpp +++ b/esphome/components/mitsubishi/mitsubishi.cpp @@ -110,7 +110,7 @@ void MitsubishiClimate::transmit_state() { // Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00 // Byte 16: Constant 0x00 // Byte 17: Checksum: SUM[Byte0...Byte16] - uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x00, 0x00, + uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; switch (this->mode) { @@ -136,6 +136,12 @@ void MitsubishiClimate::transmit_state() { break; case climate::CLIMATE_MODE_OFF: default: + remote_state[6] = MITSUBISHI_MODE_COOL; + remote_state[8] = MITSUBISHI_MODE_A_COOL; + if (this->supports_heat_) { + remote_state[6] = MITSUBISHI_MODE_HEAT; + remote_state[8] = MITSUBISHI_MODE_A_HEAT; + } remote_state[5] = MITSUBISHI_OFF; break; } From 12e840ee88705b063817e48c7f8a15f817103ad5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 11:36:09 +1200 Subject: [PATCH 0109/1052] Bump docker/setup-buildx-action from 3.5.0 to 3.6.1 in the docker-actions group (#7159) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 2b4539105b..91c02b0a17 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -46,7 +46,7 @@ jobs: with: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.5.0 + uses: docker/setup-buildx-action@v3.6.1 - name: Set up QEMU uses: docker/setup-qemu-action@v3.2.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index efec556059..d454076c84 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,7 +90,7 @@ jobs: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.5.0 + uses: docker/setup-buildx-action@v3.6.1 - name: Set up QEMU if: matrix.platform != 'linux/amd64' uses: docker/setup-qemu-action@v3.2.0 @@ -184,7 +184,7 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.5.0 + uses: docker/setup-buildx-action@v3.6.1 - name: Log in to docker hub if: matrix.registry == 'dockerhub' From 7c1aa771aaa9c9ea2f2c42a0b981ef8267a9d664 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 30 Jul 2024 09:41:34 +1000 Subject: [PATCH 0110/1052] LVGL stage 2 (#7129) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/lvgl/__init__.py | 38 +++++--- esphome/components/lvgl/btn.py | 25 +++++ esphome/components/lvgl/defines.py | 9 +- esphome/components/lvgl/helpers.py | 1 - esphome/components/lvgl/label.py | 21 ++-- esphome/components/lvgl/lv_validation.py | 79 ++++++++++++--- esphome/components/lvgl/lvgl_esphome.cpp | 6 ++ esphome/components/lvgl/lvgl_esphome.h | 59 ++++++++++-- esphome/components/lvgl/obj.py | 11 +-- esphome/components/lvgl/schemas.py | 64 +++++++------ esphome/components/lvgl/touchscreens.py | 46 +++++++++ esphome/components/lvgl/types.py | 112 +++++++++++++++++----- esphome/components/lvgl/widget.py | 73 ++------------ esphome/core/defines.h | 1 + tests/components/lvgl/common.yaml | 10 ++ tests/components/lvgl/logo-text.svg | 25 +++++ tests/components/lvgl/lvgl-package.yaml | 102 +++++++++++++++++++- tests/components/lvgl/test.esp32-idf.yaml | 4 +- 18 files changed, 503 insertions(+), 183 deletions(-) create mode 100644 esphome/components/lvgl/btn.py create mode 100644 esphome/components/lvgl/touchscreens.py create mode 100644 tests/components/lvgl/logo-text.svg diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 2f3bd69546..c454a61957 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -16,13 +16,20 @@ from esphome.final_validate import full_config from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid +from .btn import btn_spec from .label import label_spec from .lvcode import ConstantLiteral, LvContext - -# from .menu import menu_spec from .obj import obj_spec -from .schemas import WIDGET_TYPES, any_widget_schema, obj_schema -from .types import FontEngine, LvglComponent, lv_disp_t_ptr, lv_font_t, lvgl_ns +from .schemas import any_widget_schema, obj_schema +from .touchscreens import touchscreen_schema, touchscreens_to_code +from .types import ( + WIDGET_TYPES, + FontEngine, + LvglComponent, + lv_disp_t_ptr, + lv_font_t, + lvgl_ns, +) from .widget import LvScrActType, Widget, add_widgets, set_obj_properties DOMAIN = "lvgl" @@ -31,11 +38,8 @@ AUTO_LOAD = ("key_provider",) CODEOWNERS = ("@clydebarrow",) LOGGER = logging.getLogger(__name__) -for widg in ( - label_spec, - obj_spec, -): - WIDGET_TYPES[widg.name] = widg +for w_type in (label_spec, obj_spec, btn_spec): + WIDGET_TYPES[w_type.name] = w_type lv_scr_act_spec = LvScrActType() lv_scr_act = Widget.create( @@ -93,7 +97,7 @@ def final_validation(config): "Using auto_clear_enabled: true in display config not compatible with LVGL" ) buffer_frac = config[CONF_BUFFER_SIZE] - if not CORE.is_host and buffer_frac > 0.5 and "psram" not in global_config: + if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config: LOGGER.warning("buffer_size: may need to be reduced without PSRAM") @@ -132,7 +136,7 @@ async def to_code(config): cg.add_global(lvgl_ns.using) lv_component = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(lv_component, config) - Widget.create(config[CONF_ID], lv_component, WIDGET_TYPES[df.CONF_OBJ], config) + Widget.create(config[CONF_ID], lv_component, obj_spec, config) for display in config[df.CONF_DISPLAYS]: cg.add(lv_component.add_display(await cg.get_variable(display))) @@ -152,7 +156,7 @@ async def to_code(config): await cg.get_variable(font) cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font)) default_font = config[df.CONF_DEFAULT_FONT] - if default_font not in helpers.lv_fonts_used: + if not lvalid.is_lv_font(default_font): add_define( "LV_FONT_CUSTOM_DECLARE", f"LV_FONT_DECLARE(*{df.DEFAULT_ESPHOME_FONT})" ) @@ -161,12 +165,15 @@ async def to_code(config): True, type=lv_font_t.operator("ptr").operator("const"), ) - cg.new_variable(globfont_id, MockObj(default_font)) + cg.new_variable( + globfont_id, MockObj(await lvalid.lv_font.process(default_font)) + ) add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT) else: - add_define("LV_FONT_DEFAULT", default_font) + add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font)) with LvContext(): + await touchscreens_to_code(lv_component, config) await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) Widget.set_completed() @@ -190,7 +197,7 @@ FINAL_VALIDATE_SCHEMA = final_validation CONFIG_SCHEMA = ( cv.polling_component_schema("1s") - .extend(obj_schema("obj")) + .extend(obj_schema(obj_spec)) .extend( { cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent), @@ -207,6 +214,7 @@ CONFIG_SCHEMA = ( ), cv.Optional(df.CONF_WIDGETS): cv.ensure_list(WIDGET_SCHEMA), cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color, + cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema, } ) ).add_extra(cv.has_at_least_one_key(CONF_PAGES, df.CONF_WIDGETS)) diff --git a/esphome/components/lvgl/btn.py b/esphome/components/lvgl/btn.py new file mode 100644 index 0000000000..4f5f88d9e6 --- /dev/null +++ b/esphome/components/lvgl/btn.py @@ -0,0 +1,25 @@ +from esphome.const import CONF_BUTTON +from esphome.cpp_generator import MockObjClass + +from .defines import CONF_MAIN +from .types import LvBoolean, WidgetType + + +class BtnType(WidgetType): + def __init__(self): + super().__init__(CONF_BUTTON, LvBoolean("lv_btn_t"), (CONF_MAIN,)) + + async def to_code(self, w, config): + return [] + + def obj_creator(self, parent: MockObjClass, config: dict): + """ + LVGL 8 calls buttons `btn` + """ + return f"lv_btn_create({parent})" + + def get_uses(self): + return ("btn",) + + +btn_spec = BtnType() diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 50bdac3865..a2b4ac13fb 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -446,6 +446,7 @@ CONF_TILE_ID = "tile_id" CONF_TILES = "tiles" CONF_TITLE = "title" CONF_TOP_LAYER = "top_layer" +CONF_TOUCHSCREENS = "touchscreens" CONF_TRANSPARENCY_KEY = "transparency_key" CONF_THEME = "theme" CONF_VISIBLE_ROW_COUNT = "visible_row_count" @@ -474,14 +475,8 @@ LV_KEYS = LvConstant( ) -# list of widgets and the parts allowed -WIDGET_PARTS = { - CONF_LABEL: (CONF_MAIN, CONF_SCROLLBAR, CONF_SELECTED), - CONF_OBJ: (CONF_MAIN,), -} - DEFAULT_ESPHOME_FONT = "esphome_lv_default_font" def join_enums(enums, prefix=""): - return "|".join(f"(int){prefix}{e.upper()}" for e in enums) + return ConstantLiteral("|".join(f"(int){prefix}{e.upper()}" for e in enums)) diff --git a/esphome/components/lvgl/helpers.py b/esphome/components/lvgl/helpers.py index c8d4948fb1..d67739155c 100644 --- a/esphome/components/lvgl/helpers.py +++ b/esphome/components/lvgl/helpers.py @@ -22,7 +22,6 @@ def add_lv_use(*names): lv_fonts_used = set() esphome_fonts_used = set() -REQUIRED_COMPONENTS = {} lvgl_components_required = set() diff --git a/esphome/components/lvgl/label.py b/esphome/components/lvgl/label.py index 5c4ae6ab0d..0498f39474 100644 --- a/esphome/components/lvgl/label.py +++ b/esphome/components/lvgl/label.py @@ -1,16 +1,27 @@ import esphome.config_validation as cv -from .defines import CONF_LABEL, CONF_LONG_MODE, CONF_RECOLOR, CONF_TEXT, LV_LONG_MODES +from .defines import ( + CONF_LABEL, + CONF_LONG_MODE, + CONF_MAIN, + CONF_RECOLOR, + CONF_SCROLLBAR, + CONF_SELECTED, + CONF_TEXT, + LV_LONG_MODES, +) from .lv_validation import lv_bool, lv_text from .schemas import TEXT_SCHEMA -from .types import lv_label_t -from .widget import Widget, WidgetType +from .types import LvText, WidgetType +from .widget import Widget class LabelType(WidgetType): def __init__(self): super().__init__( CONF_LABEL, + LvText("lv_label_t"), + (CONF_MAIN, CONF_SCROLLBAR, CONF_SELECTED), TEXT_SCHEMA.extend( { cv.Optional(CONF_RECOLOR): lv_bool, @@ -19,10 +30,6 @@ class LabelType(WidgetType): ), ) - @property - def w_type(self): - return lv_label_t - async def to_code(self, w: Widget, config): """For a text object, create and set text""" if value := config.get(CONF_TEXT): diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 1de63c30ce..533dc582f0 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -8,6 +8,7 @@ import esphome.config_validation as cv from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT from esphome.core import HexInt from esphome.cpp_generator import MockObj +from esphome.cpp_types import uint32 from esphome.helpers import cpp_string_escape from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -23,6 +24,28 @@ from .lvcode import ConstantLiteral, lv_expr from .types import lv_font_t +def literal_mapper(value, args=()): + if isinstance(value, str): + return ConstantLiteral(value) + return value + + +opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") + + +@schema_extractor("one_of") +def opacity_validator(value): + if value == SCHEMA_EXTRACT: + return opacity_consts.choices + value = cv.Any(cv.percentage, opacity_consts.one_of)(value) + if isinstance(value, float): + return int(value * 255) + return value + + +opacity = LValidator(opacity_validator, uint32, retmapper=literal_mapper) + + @schema_extractor("one_of") def color(value): if value == SCHEMA_EXTRACT: @@ -43,16 +66,24 @@ def color_retmapper(value): return lv_expr.color_from(MockObj(value)) -def pixels_or_percent(value): +lv_color = LValidator(color, ty.lv_color_t, retmapper=color_retmapper) + + +def pixels_or_percent_validator(value): """A length in one axis - either a number (pixels) or a percentage""" if value == SCHEMA_EXTRACT: return ["pixels", "..%"] if isinstance(value, int): - return str(cv.int_(value)) + return cv.int_(value) # Will throw an exception if not a percentage. return f"lv_pct({int(cv.percentage(value) * 100)})" +pixels_or_percent = LValidator( + pixels_or_percent_validator, uint32, retmapper=literal_mapper +) + + def zoom(value): value = cv.float_range(0.1, 10.0)(value) return int(value * 256) @@ -68,7 +99,7 @@ def angle(value): @schema_extractor("one_of") -def size(value): +def size_validator(value): """A size in one axis - one of "size_content", a number (pixels) or a percentage""" if value == SCHEMA_EXTRACT: return ["size_content", "pixels", "..%"] @@ -79,28 +110,42 @@ def size(value): return "LV_SIZE_CONTENT" raise cv.Invalid("must be 'size_content', a pixel position or a percentage") if isinstance(value, int): - return str(cv.int_(value)) + return cv.int_(value) # Will throw an exception if not a percentage. return f"lv_pct({int(cv.percentage(value) * 100)})" +size = LValidator(size_validator, uint32, retmapper=literal_mapper) + +radius_consts = LvConstant("LV_RADIUS_", "CIRCLE") + + @schema_extractor("one_of") -def opacity(value): - consts = LvConstant("LV_OPA_", "TRANSP", "COVER") +def radius_validator(value): if value == SCHEMA_EXTRACT: - return consts.choices - value = cv.Any(cv.percentage, consts.one_of)(value) + return radius_consts.choices + value = cv.Any(size, cv.percentage, radius_consts.one_of)(value) if isinstance(value, float): return int(value * 255) return value +def id_name(value): + if value == SCHEMA_EXTRACT: + return "id" + return cv.validate_id_name(value) + + +radius = LValidator(radius_validator, uint32, retmapper=literal_mapper) + + def stop_value(value): return cv.int_range(0, 255)(value) -lv_color = LValidator(color, ty.lv_color_t, retmapper=color_retmapper) -lv_bool = LValidator(cv.boolean, cg.bool_, BinarySensor, "get_state()") +lv_bool = LValidator( + cv.boolean, cg.bool_, BinarySensor, "get_state()", retmapper=literal_mapper +) def lvms_validator_(value): @@ -145,26 +190,32 @@ lv_float = LValidator(cv.float_, cg.float_, Sensor, "get_state()") lv_int = LValidator(cv.int_, cg.int_, Sensor, "get_state()") +def is_lv_font(font): + return isinstance(font, str) and font.lower() in LV_FONTS + + class LvFont(LValidator): def __init__(self): def lv_builtin_font(value): fontval = cv.one_of(*LV_FONTS, lower=True)(value) lv_fonts_used.add(fontval) - return "&lv_font_" + fontval + return fontval def validator(value): if value == SCHEMA_EXTRACT: return LV_FONTS - if isinstance(value, str) and value.lower() in LV_FONTS: + if is_lv_font(value): return lv_builtin_font(value) fontval = cv.use_id(Font)(value) esphome_fonts_used.add(fontval) - return requires_component("font")(f"{fontval}_engine->get_lv_font()") + return requires_component("font")(fontval) super().__init__(validator, lv_font_t) async def process(self, value, args=()): - return ConstantLiteral(value) + if is_lv_font(value): + return ConstantLiteral(f"&lv_font_{value}") + return ConstantLiteral(f"{value}_engine->get_lv_font()") lv_font = LvFont() diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index bdaf8a4f18..74a1b0e7af 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -38,7 +38,9 @@ void LvglComponent::setup() { auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; auto *buf = lv_custom_mem_alloc(buf_bytes); if (buf == nullptr) { +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes); +#endif this->mark_failed(); this->status_set_error("Memory allocation failure"); return; @@ -85,7 +87,9 @@ size_t lv_millis(void) { return esphome::millis(); } void *lv_custom_mem_alloc(size_t size) { auto *ptr = malloc(size); // NOLINT if (ptr == nullptr) { +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); +#endif } return ptr; } @@ -102,7 +106,9 @@ void *lv_custom_mem_alloc(size_t size) { ptr = heap_caps_malloc(size, cap_bits); } if (ptr == nullptr) { +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); +#endif return nullptr; } #ifdef ESPHOME_LOG_HAS_VERBOSE diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 988c22917b..a884a27042 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -18,23 +18,27 @@ #ifdef USE_LVGL_FONT #include "esphome/components/font/font.h" #endif +#ifdef USE_LVGL_TOUCHSCREEN +#include "esphome/components/touchscreen/touchscreen.h" +#endif // USE_LVGL_TOUCHSCREEN + namespace esphome { namespace lvgl { extern lv_event_code_t lv_custom_event; // NOLINT #ifdef USE_LVGL_COLOR -static lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } -#endif +inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } +#endif // USE_LVGL_COLOR #if LV_COLOR_DEPTH == 16 static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565; #elif LV_COLOR_DEPTH == 32 static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_888; -#else +#else // LV_COLOR_DEPTH static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_332; -#endif +#endif // LV_COLOR_DEPTH // Parent class for things that wrap an LVGL object -class LvCompound { +class LvCompound final { public: virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; } lv_obj_t *obj{}; @@ -99,6 +103,14 @@ class LvglComponent : public PollingComponent { void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; } void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; } lv_disp_t *get_disp() { return this->disp_; } + void set_paused(bool paused, bool show_snow) { + this->paused_ = paused; + if (!paused && lv_scr_act() != nullptr) { + lv_disp_trig_activity(this->disp_); // resets the inactivity time + lv_obj_invalidate(lv_scr_act()); + } + } + bool is_paused() const { return this->paused_; } protected: void draw_buffer_(const lv_area_t *area, const uint8_t *ptr); @@ -107,13 +119,48 @@ class LvglComponent : public PollingComponent { lv_disp_draw_buf_t draw_buf_{}; lv_disp_drv_t disp_drv_{}; lv_disp_t *disp_{}; + bool paused_{}; std::vector> init_lambdas_; size_t buffer_frac_{1}; bool full_refresh_{}; }; +#ifdef USE_LVGL_TOUCHSCREEN +class LVTouchListener : public touchscreen::TouchListener, public Parented { + public: + LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) { + lv_indev_drv_init(&this->drv_); + this->drv_.long_press_repeat_time = long_press_repeat_time; + this->drv_.long_press_time = long_press_time; + this->drv_.type = LV_INDEV_TYPE_POINTER; + this->drv_.user_data = this; + this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) { + auto *l = static_cast(d->user_data); + if (l->touch_pressed_) { + data->point.x = l->touch_point_.x; + data->point.y = l->touch_point_.y; + data->state = LV_INDEV_STATE_PRESSED; + } else { + data->state = LV_INDEV_STATE_RELEASED; + } + }; + } + void update(const touchscreen::TouchPoints_t &tpoints) override { + this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty(); + if (this->touch_pressed_) + this->touch_point_ = tpoints[0]; + } + void release() override { touch_pressed_ = false; } + lv_indev_drv_t *get_drv() { return &this->drv_; } + + protected: + lv_indev_drv_t drv_{}; + touchscreen::TouchPoint touch_point_{}; + bool touch_pressed_{}; +}; +#endif // USE_LVGL_TOUCHSCREEN } // namespace lvgl } // namespace esphome -#endif +#endif // USE_LVGL diff --git a/esphome/components/lvgl/obj.py b/esphome/components/lvgl/obj.py index fba20bef36..92c4f63d2d 100644 --- a/esphome/components/lvgl/obj.py +++ b/esphome/components/lvgl/obj.py @@ -1,6 +1,5 @@ -from .defines import CONF_OBJ -from .types import lv_obj_t -from .widget import WidgetType +from .defines import CONF_MAIN, CONF_OBJ +from .types import WidgetType, lv_obj_t class ObjType(WidgetType): @@ -9,11 +8,7 @@ class ObjType(WidgetType): """ def __init__(self): - super().__init__(CONF_OBJ, schema={}, modify_schema={}) - - @property - def w_type(self): - return lv_obj_t + super().__init__(CONF_OBJ, lv_obj_t, (CONF_MAIN,), schema={}, modify_schema={}) async def to_code(self, w, config): return [] diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 4ae5824151..9f6d984545 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -3,15 +3,9 @@ from esphome.const import CONF_ARGS, CONF_FORMAT, CONF_ID, CONF_STATE, CONF_TYPE from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid, types as ty -from .defines import WIDGET_PARTS -from .helpers import ( - REQUIRED_COMPONENTS, - add_lv_use, - requires_component, - validate_printf, -) +from .helpers import add_lv_use, requires_component, validate_printf from .lv_validation import lv_font -from .types import WIDGET_TYPES, get_widget_type +from .types import WIDGET_TYPES, WidgetType # A schema for text properties TEXT_SCHEMA = cv.Schema( @@ -46,9 +40,9 @@ STYLE_PROPS = { "bg_dither_mode": df.LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF").one_of, "bg_grad_dir": df.LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER").one_of, "bg_grad_stop": lvalid.stop_value, - "bg_img_opa": lvalid.opacity, - "bg_img_recolor": lvalid.lv_color, - "bg_img_recolor_opa": lvalid.opacity, + "bg_image_opa": lvalid.opacity, + "bg_image_recolor": lvalid.lv_color, + "bg_image_recolor_opa": lvalid.opacity, "bg_main_stop": lvalid.stop_value, "bg_opa": lvalid.opacity, "border_color": lvalid.lv_color, @@ -60,8 +54,8 @@ STYLE_PROPS = { "border_width": cv.positive_int, "clip_corner": lvalid.lv_bool, "height": lvalid.size, - "img_recolor": lvalid.lv_color, - "img_recolor_opa": lvalid.opacity, + "image_recolor": lvalid.lv_color, + "image_recolor_opa": lvalid.opacity, "line_width": cv.positive_int, "line_dash_width": cv.positive_int, "line_dash_gap": cv.positive_int, @@ -108,12 +102,21 @@ STYLE_PROPS = { "max_width": lvalid.pixels_or_percent, "min_height": lvalid.pixels_or_percent, "min_width": lvalid.pixels_or_percent, - "radius": cv.Any(lvalid.size, df.LvConstant("LV_RADIUS_", "CIRCLE").one_of), + "radius": lvalid.radius, "width": lvalid.size, "x": lvalid.pixels_or_percent, "y": lvalid.pixels_or_percent, } +STYLE_REMAP = { + "bg_image_opa": "bg_img_opa", + "bg_image_recolor": "bg_img_recolor", + "bg_image_recolor_opa": "bg_img_recolor_opa", + "bg_image_src": "bg_img_src", + "image_recolor": "img_recolor", + "image_recolor_opa": "img_recolor_opa", +} + # Complete object style schema STYLE_SCHEMA = cv.Schema({cv.Optional(k): v for k, v in STYLE_PROPS.items()}).extend( { @@ -132,25 +135,23 @@ SET_STATE_SCHEMA = cv.Schema( {cv.Optional(state): lvalid.lv_bool for state in df.STATES} ) # Setting object flags -FLAG_SCHEMA = cv.Schema({cv.Optional(flag): cv.boolean for flag in df.OBJ_FLAGS}) +FLAG_SCHEMA = cv.Schema({cv.Optional(flag): lvalid.lv_bool for flag in df.OBJ_FLAGS}) FLAG_LIST = cv.ensure_list(df.LvConstant("LV_OBJ_FLAG_", *df.OBJ_FLAGS).one_of) -def part_schema(widget_type): +def part_schema(widget_type: WidgetType): """ Generate a schema for the various parts (e.g. main:, indicator:) of a widget type :param widget_type: The type of widget to generate for :return: """ - parts = WIDGET_PARTS.get(widget_type) - if parts is None: - parts = (df.CONF_MAIN,) + parts = widget_type.parts return cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}).extend( STATE_SCHEMA ) -def obj_schema(widget_type: str): +def obj_schema(widget_type: WidgetType): """ Create a schema for a widget type itself i.e. no allowance for children :param widget_type: @@ -187,13 +188,12 @@ STYLED_TEXT_SCHEMA = cv.maybe_simple_value( STYLE_SCHEMA.extend(TEXT_SCHEMA), key=df.CONF_TEXT ) - ALL_STYLES = { **STYLE_PROPS, } -def container_validator(schema, widget_type): +def container_validator(schema, widget_type: WidgetType): """ Create a validator for a container given the widget type :param schema: Base schema to extend @@ -203,13 +203,16 @@ def container_validator(schema, widget_type): def validator(value): result = schema - if w_sch := WIDGET_TYPES[widget_type].schema: + if w_sch := widget_type.schema: result = result.extend(w_sch) if value and (layout := value.get(df.CONF_LAYOUT)): if not isinstance(layout, dict): raise cv.Invalid("Layout value must be a dict") ltype = layout.get(CONF_TYPE) add_lv_use(ltype) + result = result.extend( + {cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema())} + ) if value == SCHEMA_EXTRACT: return result return result(value) @@ -217,7 +220,7 @@ def container_validator(schema, widget_type): return validator -def container_schema(widget_type, extras=None): +def container_schema(widget_type: WidgetType, extras=None): """ Create a schema for a container widget of a given type. All obj properties are available, plus the extras passed in, plus any defined for the specific widget being specified. @@ -225,15 +228,16 @@ def container_schema(widget_type, extras=None): :param extras: Additional options to be made available, e.g. layout properties for children :return: The schema for this type of widget. """ - lv_type = get_widget_type(widget_type) - schema = obj_schema(widget_type).extend({cv.GenerateID(): cv.declare_id(lv_type)}) + schema = obj_schema(widget_type).extend( + {cv.GenerateID(): cv.declare_id(widget_type.w_type)} + ) if extras: schema = schema.extend(extras) # Delayed evaluation for recursion return container_validator(schema, widget_type) -def widget_schema(widget_type, extras=None): +def widget_schema(widget_type: WidgetType, extras=None): """ Create a schema for a given widget type :param widget_type: The name of the widget @@ -241,9 +245,9 @@ def widget_schema(widget_type, extras=None): :return: """ validator = container_schema(widget_type, extras=extras) - if required := REQUIRED_COMPONENTS.get(widget_type): + if required := widget_type.required_component: validator = cv.All(validator, requires_component(required)) - return cv.Exclusive(widget_type, df.CONF_WIDGETS), validator + return cv.Exclusive(widget_type.name, df.CONF_WIDGETS), validator # All widget schemas must be defined before this is called. @@ -257,4 +261,4 @@ def any_widget_schema(extras=None): :param extras: Additional schema to be applied to each generated one :return: """ - return cv.Any(dict(widget_schema(wt, extras) for wt in WIDGET_PARTS)) + return cv.Any(dict(widget_schema(wt, extras) for wt in WIDGET_TYPES.values())) diff --git a/esphome/components/lvgl/touchscreens.py b/esphome/components/lvgl/touchscreens.py new file mode 100644 index 0000000000..a0d4a3e4ad --- /dev/null +++ b/esphome/components/lvgl/touchscreens.py @@ -0,0 +1,46 @@ +import esphome.codegen as cg +from esphome.components.touchscreen import CONF_TOUCHSCREEN_ID, Touchscreen +import esphome.config_validation as cv +from esphome.const import CONF_ID +from esphome.core import CORE, TimePeriod + +from .defines import ( + CONF_LONG_PRESS_REPEAT_TIME, + CONF_LONG_PRESS_TIME, + CONF_TOUCHSCREENS, +) +from .helpers import lvgl_components_required +from .lv_validation import lv_milliseconds +from .lvcode import lv +from .types import LVTouchListener + +PRESS_TIME = cv.All(lv_milliseconds, cv.Range(max=TimePeriod(milliseconds=65535))) +CONF_TOUCHSCREEN = "touchscreen" +TOUCHSCREENS_CONFIG = cv.maybe_simple_value( + { + cv.Required(CONF_TOUCHSCREEN_ID): cv.use_id(Touchscreen), + cv.Optional(CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME, + cv.Optional(CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME, + cv.GenerateID(): cv.declare_id(LVTouchListener), + }, + key=CONF_TOUCHSCREEN_ID, +) + + +def touchscreen_schema(config): + value = cv.ensure_list(TOUCHSCREENS_CONFIG)(config) + if value or CONF_TOUCHSCREEN not in CORE.loaded_integrations: + return value + return [TOUCHSCREENS_CONFIG(config)] + + +async def touchscreens_to_code(var, config): + for tconf in config.get(CONF_TOUCHSCREENS) or (): + lvgl_components_required.add(CONF_TOUCHSCREEN) + touchscreen = await cg.get_variable(tconf[CONF_TOUCHSCREEN_ID]) + lpt = tconf[CONF_LONG_PRESS_TIME].total_milliseconds + lprt = tconf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds + listener = cg.new_Pvariable(tconf[CONF_ID], lpt, lprt) + await cg.register_parented(listener, var) + lv.indev_drv_register(listener.get_drv()) + cg.add(touchscreen.register_listener(listener)) diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index 3c043d266d..60291ea54a 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -1,7 +1,22 @@ from esphome import codegen as cg from esphome.core import ID +from esphome.cpp_generator import MockObjClass + +from .defines import CONF_TEXT + + +class LvType(cg.MockObjClass): + def __init__(self, *args, **kwargs): + parens = kwargs.pop("parents", ()) + super().__init__(*args, parents=parens + (lv_obj_base_t,)) + self.args = kwargs.pop("largs", [(lv_obj_t_ptr, "obj")]) + self.value = kwargs.pop("lvalue", lambda w: w.obj) + self.has_on_value = kwargs.pop("has_on_value", False) + self.value_property = None + + def get_arg_type(self): + return self.args[0][0] if len(self.args) else None -from .defines import CONF_LABEL, CONF_OBJ, CONF_TEXT uint16_t_ptr = cg.uint16.operator("ptr") lvgl_ns = cg.esphome_ns.namespace("lvgl") @@ -18,25 +33,15 @@ lv_obj_base_t = cg.global_ns.class_("lv_obj_t", lv_pseudo_button_t) lv_obj_t_ptr = lv_obj_base_t.operator("ptr") lv_disp_t_ptr = cg.global_ns.struct("lv_disp_t").operator("ptr") lv_color_t = cg.global_ns.struct("lv_color_t") +LVTouchListener = lvgl_ns.class_("LVTouchListener") +LVEncoderListener = lvgl_ns.class_("LVEncoderListener") +lv_obj_t = LvType("lv_obj_t") # this will be populated later, in __init__.py to avoid circular imports. WIDGET_TYPES: dict = {} -class LvType(cg.MockObjClass): - def __init__(self, *args, **kwargs): - parens = kwargs.pop("parents", ()) - super().__init__(*args, parents=parens + (lv_obj_base_t,)) - self.args = kwargs.pop("largs", [(lv_obj_t_ptr, "obj")]) - self.value = kwargs.pop("lvalue", lambda w: w.obj) - self.has_on_value = kwargs.pop("has_on_value", False) - self.value_property = None - - def get_arg_type(self): - return self.args[0][0] if len(self.args) else None - - class LvText(LvType): def __init__(self, *args, **kwargs): super().__init__( @@ -48,17 +53,74 @@ class LvText(LvType): self.value_property = CONF_TEXT -lv_obj_t = LvType("lv_obj_t") -lv_label_t = LvText("lv_label_t") - -LV_TYPES = { - CONF_LABEL: lv_label_t, - CONF_OBJ: lv_obj_t, -} - - -def get_widget_type(typestr: str) -> LvType: - return LV_TYPES[typestr] +class LvBoolean(LvType): + def __init__(self, *args, **kwargs): + super().__init__( + *args, + largs=[(cg.bool_, "x")], + lvalue=lambda w: w.is_checked(), + has_on_value=True, + **kwargs, + ) CUSTOM_EVENT = ID("lv_custom_event", False, type=lv_event_code_t) + + +class WidgetType: + """ + Describes a type of Widget, e.g. "bar" or "line" + """ + + def __init__(self, name, w_type, parts, schema=None, modify_schema=None): + """ + :param name: The widget name, e.g. "bar" + :param w_type: The C type of the widget + :param parts: What parts this widget supports + :param schema: The config schema for defining a widget + :param modify_schema: A schema to update the widget + """ + self.name = name + self.w_type = w_type + self.parts = parts + self.schema = schema or {} + if modify_schema is None: + self.modify_schema = schema + else: + self.modify_schema = modify_schema + + @property + def animated(self): + return False + + @property + def required_component(self): + return None + + def is_compound(self): + return self.w_type.inherits_from(LvCompound) + + async def to_code(self, w, config: dict): + """ + Generate code for a given widget + :param w: The widget + :param config: Its configuration + :return: Generated code as a list of text lines + """ + raise NotImplementedError(f"No to_code defined for {self.name}") + + def obj_creator(self, parent: MockObjClass, config: dict): + """ + Create an instance of the widget type + :param parent: The parent to which it should be attached + :param config: Its configuration + :return: Generated code as a single text line + """ + return f"lv_{self.name}_create({parent})" + + def get_uses(self): + """ + Get a list of other widgets used by this one + :return: + """ + return () diff --git a/esphome/components/lvgl/widget.py b/esphome/components/lvgl/widget.py index 44f277f1c3..4755d8b21d 100644 --- a/esphome/components/lvgl/widget.py +++ b/esphome/components/lvgl/widget.py @@ -21,78 +21,19 @@ from .defines import ( ) from .helpers import add_lv_use from .lvcode import ConstantLiteral, add_line_marks, lv, lv_add, lv_assign, lv_obj -from .schemas import ALL_STYLES -from .types import WIDGET_TYPES, LvCompound, lv_obj_t +from .schemas import ALL_STYLES, STYLE_REMAP +from .types import WIDGET_TYPES, WidgetType, lv_obj_t EVENT_LAMB = "event_lamb__" -class WidgetType: - """ - Describes a type of Widget, e.g. "bar" or "line" - """ - - def __init__(self, name, schema=None, modify_schema=None): - """ - :param name: The widget name, e.g. "bar" - :param schema: The config schema for defining a widget - :param modify_schema: A schema to update the widget - """ - self.name = name - self.schema = schema or {} - if modify_schema is None: - self.modify_schema = schema - else: - self.modify_schema = modify_schema - - @property - def animated(self): - return False - - @property - def w_type(self): - """ - Get the type associated with this widget - :return: - """ - return lv_obj_t - - def is_compound(self): - return self.w_type.inherits_from(LvCompound) - - async def to_code(self, w, config: dict): - """ - Generate code for a given widget - :param w: The widget - :param config: Its configuration - :return: Generated code as a list of text lines - """ - raise NotImplementedError(f"No to_code defined for {self.name}") - - def obj_creator(self, parent: MockObjClass, config: dict): - """ - Create an instance of the widget type - :param parent: The parent to which it should be attached - :param config: Its configuration - :return: Generated code as a single text line - """ - return f"lv_{self.name}_create({parent})" - - def get_uses(self): - """ - Get a list of other widgets used by this one - :return: - """ - return () - - class LvScrActType(WidgetType): """ A "widget" representing the active screen. """ def __init__(self): - super().__init__("lv_scr_act()") + super().__init__("lv_scr_act()", lv_obj_t, ()) def obj_creator(self, parent: MockObjClass, config: dict): return [] @@ -263,7 +204,9 @@ async def set_obj_properties(w: Widget, config): }.items(): if isinstance(ALL_STYLES[prop], LValidator): value = await ALL_STYLES[prop].process(value) - w.set_style(prop, value, lv_state) + # Remapping for backwards compatibility of style names + prop_r = STYLE_REMAP.get(prop, prop) + w.set_style(prop_r, value, lv_state) flag_clr = set() flag_set = set() props = parts[CONF_MAIN][CONF_DEFAULT] @@ -291,10 +234,10 @@ async def set_obj_properties(w: Widget, config): else: clears.add(key) if adds: - adds = ConstantLiteral(join_enums(adds, "LV_STATE_")) + adds = join_enums(adds, "LV_STATE_") w.add_state(adds) if clears: - clears = ConstantLiteral(join_enums(clears, "LV_STATE_")) + clears = join_enums(clears, "LV_STATE_") w.clear_state(clears) for key, value in lambs.items(): lamb = await cg.process_lambda(value, [], return_type=cg.bool_) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 9d453260ab..6ba5b64761 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -41,6 +41,7 @@ #define USE_LVGL #define USE_LVGL_FONT #define USE_LVGL_IMAGE +#define USE_LVGL_TOUCHSCREEN #define USE_MDNS #define USE_MEDIA_PLAYER #define USE_MQTT diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index e69de29bb2..8b92f8caa7 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -0,0 +1,10 @@ +touchscreen: + - platform: ft63x6 + id: tft_touch + display: tft_display + update_interval: 50ms + threshold: 1 + calibration: + x_max: 240 + y_max: 320 + diff --git a/tests/components/lvgl/logo-text.svg b/tests/components/lvgl/logo-text.svg new file mode 100644 index 0000000000..4950806a36 --- /dev/null +++ b/tests/components/lvgl/logo-text.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 856e7c3e9d..696c749876 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -1,9 +1,10 @@ -color: - - id: light_blue - hex: "3340FF" - lvgl: + log_level: TRACE bg_color: light_blue + touchscreens: + - touchscreen_id: tft_touch + long_press_repeat_time: 200ms + long_press_time: 500ms widgets: - label: text: Hello world @@ -17,8 +18,101 @@ lvgl: text_color: 0xFFFFFF align: bottom_mid text_font: space16 + - obj: + align: center + arc_opa: COVER + arc_color: 0xFF0000 + arc_rounded: false + arc_width: 3 + anim_time: 1s + bg_color: light_blue + bg_grad_color: light_blue + bg_dither_mode: ordered + bg_grad_dir: hor + bg_grad_stop: 128 + bg_image_opa: transp + bg_image_recolor: light_blue + bg_image_recolor_opa: 50% + bg_main_stop: 0 + bg_opa: 20% + border_color: 0x00FF00 + border_opa: cover + border_post: true + border_side: [bottom, left] + border_width: 4 + clip_corner: false + height: 50% + image_recolor: light_blue + image_recolor_opa: cover + line_width: 10 + line_dash_width: 10 + line_dash_gap: 10 + line_rounded: false + line_color: light_blue + opa: cover + opa_layered: cover + outline_color: light_blue + outline_opa: cover + outline_pad: 10px + outline_width: 10px + pad_all: 10px + pad_bottom: 10px + pad_column: 10px + pad_left: 10px + pad_right: 10px + pad_row: 10px + pad_top: 10px + shadow_color: light_blue + shadow_ofs_x: 5 + shadow_ofs_y: 5 + shadow_opa: cover + shadow_spread: 5 + shadow_width: 10 + text_align: auto + text_color: light_blue + text_decor: [underline, strikethrough] + text_font: montserrat_18 + text_letter_space: 4 + text_line_space: 4 + text_opa: cover + transform_angle: 180 + transform_height: 100 + transform_pivot_x: 50% + transform_pivot_y: 50% + transform_zoom: 0.5 + translate_x: 10 + translate_y: 10 + max_height: 100 + max_width: 200 + min_height: 20% + min_width: 20% + radius: circle + width: 10px + x: 100 + y: 120 + - button: + width: 20% + height: 10% + pressed: + bg_color: light_blue + widgets: + - label: + text: Button font: - file: "gfonts://Roboto" id: space16 bpp: 4 + +image: + - id: cat_img + resize: 256x48 + file: $component_dir/logo-text.svg + - id: dog_img + file: $component_dir/logo-text.svg + resize: 256x48 + type: TRANSPARENT_BINARY + +color: + - id: light_blue + hex: "3340FF" diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml index f159431b99..eab75b05f3 100644 --- a/tests/components/lvgl/test.esp32-idf.yaml +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -19,7 +19,9 @@ display: mirror_y: true data_rate: 80MHz cs_pin: GPIO20 - dc_pin: GPIO15 + dc_pin: + number: GPIO15 + ignore_strapping_warning: true auto_clear_enabled: false invert_colors: false update_interval: never From 6e21d79bde02b90e4273b7a92f2caa8604a3d687 Mon Sep 17 00:00:00 2001 From: FreeBear-nc <67865163+FreeBear-nc@users.noreply.github.com> Date: Tue, 30 Jul 2024 02:15:27 +0100 Subject: [PATCH 0111/1052] [pid] Add get_min_integral() and get_max_integral() (#7162) --- esphome/components/pid/pid_climate.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/pid/pid_climate.h b/esphome/components/pid/pid_climate.h index 5ae97ee10b..b5275e9775 100644 --- a/esphome/components/pid/pid_climate.h +++ b/esphome/components/pid/pid_climate.h @@ -44,6 +44,8 @@ class PIDClimate : public climate::Climate, public Component { float get_kp() { return controller_.kp_; } float get_ki() { return controller_.ki_; } float get_kd() { return controller_.kd_; } + float get_min_integral() { return controller_.min_integral_; } + float get_max_integral() { return controller_.max_integral_; } float get_proportional_term() const { return controller_.proportional_term_; } float get_integral_term() const { return controller_.integral_term_; } float get_derivative_term() const { return controller_.derivative_term_; } From 83bb7d0266459ebae70d663addabc45a1f112092 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 30 Jul 2024 13:23:30 +1200 Subject: [PATCH 0112/1052] [code-quality] Organise bluetooth related imports (#7155) --- esphome/components/ble_client/__init__.py | 8 ++++---- esphome/components/ble_client/output/__init__.py | 2 +- esphome/components/ble_client/sensor/__init__.py | 7 ++++--- esphome/components/ble_client/switch/__init__.py | 3 ++- esphome/components/ble_client/text_sensor/__init__.py | 7 ++++--- esphome/components/ble_presence/binary_sensor.py | 6 +++--- esphome/components/ble_rssi/sensor.py | 4 ++-- esphome/components/ble_scanner/text_sensor.py | 2 +- esphome/components/bluetooth_proxy/__init__.py | 6 +++--- esphome/components/esp32_ble/__init__.py | 6 +++--- esphome/components/esp32_ble_beacon/__init__.py | 10 +++++----- esphome/components/esp32_ble_client/__init__.py | 1 - esphome/components/esp32_ble_server/__init__.py | 4 ++-- esphome/components/esp32_ble_tracker/__init__.py | 4 ++-- 14 files changed, 36 insertions(+), 34 deletions(-) diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 34b9868edc..6bf4ff739e 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -1,7 +1,8 @@ -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 esp32_ble_tracker, esp32_ble_client +import esphome.codegen as cg +from esphome.components import esp32_ble_client, esp32_ble_tracker +import esphome.config_validation as cv from esphome.const import ( CONF_CHARACTERISTIC_UUID, CONF_ID, @@ -13,7 +14,6 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_VALUE, ) -from esphome import automation AUTO_LOAD = ["esp32_ble_client"] CODEOWNERS = ["@buxtronix", "@clydebarrow"] diff --git a/esphome/components/ble_client/output/__init__.py b/esphome/components/ble_client/output/__init__.py index fd847d80b8..729885eb8b 100644 --- a/esphome/components/ble_client/output/__init__.py +++ b/esphome/components/ble_client/output/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import ble_client, esp32_ble_tracker, output +import esphome.config_validation as cv from esphome.const import CONF_CHARACTERISTIC_UUID, CONF_ID, CONF_SERVICE_UUID from .. import ble_client_ns diff --git a/esphome/components/ble_client/sensor/__init__.py b/esphome/components/ble_client/sensor/__init__.py index d0b27c30a9..0c48902a90 100644 --- a/esphome/components/ble_client/sensor/__init__.py +++ b/esphome/components/ble_client/sensor/__init__.py @@ -1,17 +1,18 @@ +from esphome import automation import esphome.codegen as cg +from esphome.components import ble_client, esp32_ble_tracker, sensor import esphome.config_validation as cv -from esphome.components import sensor, ble_client, esp32_ble_tracker from esphome.const import ( CONF_CHARACTERISTIC_UUID, CONF_LAMBDA, + CONF_SERVICE_UUID, CONF_TRIGGER_ID, CONF_TYPE, - CONF_SERVICE_UUID, DEVICE_CLASS_SIGNAL_STRENGTH, STATE_CLASS_MEASUREMENT, UNIT_DECIBEL_MILLIWATT, ) -from esphome import automation + from .. import ble_client_ns DEPENDENCIES = ["ble_client"] diff --git a/esphome/components/ble_client/switch/__init__.py b/esphome/components/ble_client/switch/__init__.py index 2304d65c01..70314e8f30 100644 --- a/esphome/components/ble_client/switch/__init__.py +++ b/esphome/components/ble_client/switch/__init__.py @@ -1,7 +1,8 @@ import esphome.codegen as cg +from esphome.components import ble_client, switch import esphome.config_validation as cv -from esphome.components import switch, ble_client from esphome.const import ICON_BLUETOOTH + from .. import ble_client_ns BLEClientSwitch = ble_client_ns.class_( diff --git a/esphome/components/ble_client/text_sensor/__init__.py b/esphome/components/ble_client/text_sensor/__init__.py index 7a93c4e4ae..479af1a57e 100644 --- a/esphome/components/ble_client/text_sensor/__init__.py +++ b/esphome/components/ble_client/text_sensor/__init__.py @@ -1,13 +1,14 @@ +from esphome import automation import esphome.codegen as cg +from esphome.components import ble_client, esp32_ble_tracker, text_sensor import esphome.config_validation as cv -from esphome.components import text_sensor, ble_client, esp32_ble_tracker from esphome.const import ( CONF_CHARACTERISTIC_UUID, CONF_ID, - CONF_TRIGGER_ID, CONF_SERVICE_UUID, + CONF_TRIGGER_ID, ) -from esphome import automation + from .. import ble_client_ns DEPENDENCIES = ["ble_client"] diff --git a/esphome/components/ble_presence/binary_sensor.py b/esphome/components/ble_presence/binary_sensor.py index 9c24a91a05..d1fdc80289 100644 --- a/esphome/components/ble_presence/binary_sensor.py +++ b/esphome/components/ble_presence/binary_sensor.py @@ -1,13 +1,13 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import binary_sensor, esp32_ble_tracker +import esphome.config_validation as cv from esphome.const import ( - CONF_MAC_ADDRESS, - CONF_SERVICE_UUID, CONF_IBEACON_MAJOR, CONF_IBEACON_MINOR, CONF_IBEACON_UUID, + CONF_MAC_ADDRESS, CONF_MIN_RSSI, + CONF_SERVICE_UUID, CONF_TIMEOUT, ) diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index 0543eb0578..e3ba1abfd7 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -1,12 +1,12 @@ import esphome.codegen as cg +from esphome.components import esp32_ble_tracker, sensor import esphome.config_validation as cv -from esphome.components import sensor, esp32_ble_tracker from esphome.const import ( CONF_IBEACON_MAJOR, CONF_IBEACON_MINOR, CONF_IBEACON_UUID, - CONF_SERVICE_UUID, CONF_MAC_ADDRESS, + CONF_SERVICE_UUID, DEVICE_CLASS_SIGNAL_STRENGTH, STATE_CLASS_MEASUREMENT, UNIT_DECIBEL_MILLIWATT, diff --git a/esphome/components/ble_scanner/text_sensor.py b/esphome/components/ble_scanner/text_sensor.py index 743403c6a4..96d71a0399 100644 --- a/esphome/components/ble_scanner/text_sensor.py +++ b/esphome/components/ble_scanner/text_sensor.py @@ -1,6 +1,6 @@ import esphome.codegen as cg +from esphome.components import esp32_ble_tracker, text_sensor import esphome.config_validation as cv -from esphome.components import text_sensor, esp32_ble_tracker DEPENDENCIES = ["esp32_ble_tracker"] diff --git a/esphome/components/bluetooth_proxy/__init__.py b/esphome/components/bluetooth_proxy/__init__.py index bec1579d8e..5a4ba36666 100644 --- a/esphome/components/bluetooth_proxy/__init__.py +++ b/esphome/components/bluetooth_proxy/__init__.py @@ -1,8 +1,8 @@ -from esphome.components import esp32_ble_tracker, esp32_ble_client -import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ACTIVE, CONF_ID +from esphome.components import esp32_ble_client, esp32_ble_tracker from esphome.components.esp32 import add_idf_sdkconfig_option +import esphome.config_validation as cv +from esphome.const import CONF_ACTIVE, CONF_ID AUTO_LOAD = ["esp32_ble_client", "esp32_ble_tracker"] DEPENDENCIES = ["api", "esp32"] diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 472669a381..75cf9d707d 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,9 +1,9 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg +from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant +import esphome.config_validation as cv from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID from esphome.core import CORE -from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant, const DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz", "@Rapsssito"] diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index d063209478..f97f289a0a 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -1,10 +1,10 @@ import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components.esp32_ble import CONF_BLE_ID -from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, CONF_TX_POWER -from esphome.core import CORE, TimePeriod -from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components import esp32_ble +from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.components.esp32_ble import CONF_BLE_ID +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_TX_POWER, CONF_TYPE, CONF_UUID +from esphome.core import CORE, TimePeriod AUTO_LOAD = ["esp32_ble"] DEPENDENCIES = ["esp32"] diff --git a/esphome/components/esp32_ble_client/__init__.py b/esphome/components/esp32_ble_client/__init__.py index 94a5576d0b..25957ed0da 100644 --- a/esphome/components/esp32_ble_client/__init__.py +++ b/esphome/components/esp32_ble_client/__init__.py @@ -1,5 +1,4 @@ import esphome.codegen as cg - from esphome.components import esp32_ble_tracker AUTO_LOAD = ["esp32_ble_tracker"] diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index ce9fdc2cf3..9da7d13999 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -1,9 +1,9 @@ import esphome.codegen as cg +from esphome.components import esp32_ble +from esphome.components.esp32 import add_idf_sdkconfig_option import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_MODEL -from esphome.components import esp32_ble from esphome.core import CORE -from esphome.components.esp32 import add_idf_sdkconfig_option AUTO_LOAD = ["esp32_ble"] CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"] diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 1edeaadbfd..0aa8eadd0a 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,10 +1,10 @@ import re -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import esp32_ble from esphome.components.esp32 import add_idf_sdkconfig_option +import esphome.config_validation as cv from esphome.const import ( CONF_ACTIVE, CONF_DURATION, From caa2ea64e35ca883105f1fefeb1aeee5dbf28510 Mon Sep 17 00:00:00 2001 From: Olivier ARCHER Date: Tue, 30 Jul 2024 03:45:19 +0200 Subject: [PATCH 0113/1052] http_request watchdog as a component (#7161) --- CODEOWNERS | 1 + esphome/components/http_request/__init__.py | 2 +- esphome/components/http_request/http_request_arduino.cpp | 4 ++-- esphome/components/http_request/http_request_idf.cpp | 4 ++-- esphome/components/http_request/ota/ota_http_request.cpp | 2 +- esphome/components/watchdog/__init__.py | 1 + esphome/components/{http_request => watchdog}/watchdog.cpp | 2 -- esphome/components/{http_request => watchdog}/watchdog.h | 2 -- 8 files changed, 8 insertions(+), 10 deletions(-) create mode 100644 esphome/components/watchdog/__init__.py rename esphome/components/{http_request => watchdog}/watchdog.cpp (96%) rename esphome/components/{http_request => watchdog}/watchdog.h (88%) diff --git a/CODEOWNERS b/CODEOWNERS index 2fc030453f..d94c34c019 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -427,6 +427,7 @@ esphome/components/veml7700/* @latonita esphome/components/version/* @esphome/core esphome/components/voice_assistant/* @jesserockz esphome/components/wake_on_lan/* @clydebarrow @willwill2will54 +esphome/components/watchdog/* @oarcher esphome/components/waveshare_epaper/* @clydebarrow esphome/components/web_server_base/* @OttoWinter esphome/components/web_server_idf/* @dentra diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 3ca97b9611..0407bbd326 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -14,7 +14,7 @@ from esphome.const import ( from esphome.core import CORE, Lambda DEPENDENCIES = ["network"] -AUTO_LOAD = ["json"] +AUTO_LOAD = ["json", "watchdog"] http_request_ns = cg.esphome_ns.namespace("http_request") HttpRequestComponent = http_request_ns.class_("HttpRequestComponent", cg.Component) diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index 95b1cdc38e..2148d92ad2 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -3,12 +3,12 @@ #ifdef USE_ARDUINO #include "esphome/components/network/util.h" +#include "esphome/components/watchdog/watchdog.h" + #include "esphome/core/application.h" #include "esphome/core/defines.h" #include "esphome/core/log.h" -#include "watchdog.h" - namespace esphome { namespace http_request { diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 4fe5585723..3819f5544e 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -3,6 +3,8 @@ #ifdef USE_ESP_IDF #include "esphome/components/network/util.h" +#include "esphome/components/watchdog/watchdog.h" + #include "esphome/core/application.h" #include "esphome/core/defines.h" #include "esphome/core/log.h" @@ -11,8 +13,6 @@ #include "esp_crt_bundle.h" #endif -#include "watchdog.h" - namespace esphome { namespace http_request { diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index dcc783ea47..1553de0bc1 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -1,11 +1,11 @@ #include "ota_http_request.h" -#include "../watchdog.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" #include "esphome/core/log.h" #include "esphome/components/md5/md5.h" +#include "esphome/components/watchdog/watchdog.h" #include "esphome/components/ota/ota_backend.h" #include "esphome/components/ota/ota_backend_arduino_esp32.h" #include "esphome/components/ota/ota_backend_arduino_esp8266.h" diff --git a/esphome/components/watchdog/__init__.py b/esphome/components/watchdog/__init__.py new file mode 100644 index 0000000000..6fb87e45a4 --- /dev/null +++ b/esphome/components/watchdog/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@oarcher"] diff --git a/esphome/components/http_request/watchdog.cpp b/esphome/components/watchdog/watchdog.cpp similarity index 96% rename from esphome/components/http_request/watchdog.cpp rename to esphome/components/watchdog/watchdog.cpp index a8519c59ed..3a94a658e8 100644 --- a/esphome/components/http_request/watchdog.cpp +++ b/esphome/components/watchdog/watchdog.cpp @@ -15,7 +15,6 @@ #endif namespace esphome { -namespace http_request { namespace watchdog { static const char *const TAG = "http_request.watchdog"; @@ -72,5 +71,4 @@ uint32_t WatchdogManager::get_timeout_() { } } // namespace watchdog -} // namespace http_request } // namespace esphome diff --git a/esphome/components/http_request/watchdog.h b/esphome/components/watchdog/watchdog.h similarity index 88% rename from esphome/components/http_request/watchdog.h rename to esphome/components/watchdog/watchdog.h index 9b54ae6c82..899ec3fde0 100644 --- a/esphome/components/http_request/watchdog.h +++ b/esphome/components/watchdog/watchdog.h @@ -5,7 +5,6 @@ #include namespace esphome { -namespace http_request { namespace watchdog { class WatchdogManager { @@ -22,5 +21,4 @@ class WatchdogManager { }; } // namespace watchdog -} // namespace http_request } // namespace esphome From d7231fadb1e174fe88f0d293c40265a26cd42ede Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:50:12 +1200 Subject: [PATCH 0114/1052] [touchscreen] Allow binary sensor to have multiple pages in config (#7112) * [touchscreen] Allow binary sensor to have multiple pages in config * Sort imports --- .../touchscreen/binary_sensor/__init__.py | 34 +++++++++++++------ .../touchscreen_binary_sensor.cpp | 5 +-- .../binary_sensor/touchscreen_binary_sensor.h | 6 ++-- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/esphome/components/touchscreen/binary_sensor/__init__.py b/esphome/components/touchscreen/binary_sensor/__init__.py index 800bc4c2a9..45fefbf814 100644 --- a/esphome/components/touchscreen/binary_sensor/__init__.py +++ b/esphome/components/touchscreen/binary_sensor/__init__.py @@ -1,10 +1,9 @@ import esphome.codegen as cg -import esphome.config_validation as cv - from esphome.components import binary_sensor, display -from esphome.const import CONF_PAGE_ID +import esphome.config_validation as cv +from esphome.const import CONF_PAGE_ID, CONF_PAGES -from .. import touchscreen_ns, CONF_TOUCHSCREEN_ID, Touchscreen, TouchListener +from .. import CONF_TOUCHSCREEN_ID, TouchListener, Touchscreen, touchscreen_ns DEPENDENCIES = ["touchscreen"] @@ -22,7 +21,7 @@ CONF_Y_MIN = "y_min" CONF_Y_MAX = "y_max" -def validate_coords(config): +def _validate_coords(config): if ( config[CONF_X_MAX] < config[CONF_X_MIN] or config[CONF_Y_MAX] < config[CONF_Y_MIN] @@ -33,6 +32,15 @@ def validate_coords(config): return config +def _set_pages(config: dict) -> dict: + if CONF_PAGES in config or CONF_PAGE_ID not in config: + return config + + config = config.copy() + config[CONF_PAGES] = [config.pop(CONF_PAGE_ID)] + return config + + CONFIG_SCHEMA = cv.All( binary_sensor.binary_sensor_schema(TouchscreenBinarySensor) .extend( @@ -42,11 +50,17 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_X_MAX): cv.int_range(min=0, max=2000), cv.Required(CONF_Y_MIN): cv.int_range(min=0, max=2000), cv.Required(CONF_Y_MAX): cv.int_range(min=0, max=2000), - cv.Optional(CONF_PAGE_ID): cv.use_id(display.DisplayPage), + cv.Exclusive(CONF_PAGE_ID, group_of_exclusion=CONF_PAGES): cv.use_id( + display.DisplayPage + ), + cv.Exclusive(CONF_PAGES, group_of_exclusion=CONF_PAGES): cv.ensure_list( + cv.use_id(display.DisplayPage) + ), } ) .extend(cv.COMPONENT_SCHEMA), - validate_coords, + _validate_coords, + _set_pages, ) @@ -64,6 +78,6 @@ async def to_code(config): ) ) - if CONF_PAGE_ID in config: - page = await cg.get_variable(config[CONF_PAGE_ID]) - cg.add(var.set_page(page)) + for page_id in config.get(CONF_PAGES, []): + page = await cg.get_variable(page_id) + cg.add(var.add_page(page)) diff --git a/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp b/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp index 6c26ae3626..6cd12d4d0d 100644 --- a/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp +++ b/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.cpp @@ -11,8 +11,9 @@ void TouchscreenBinarySensor::setup() { void TouchscreenBinarySensor::touch(TouchPoint tp) { bool touched = (tp.x >= this->x_min_ && tp.x <= this->x_max_ && tp.y >= this->y_min_ && tp.y <= this->y_max_); - if (this->page_ != nullptr) { - touched &= this->page_ == this->parent_->get_display()->get_active_page(); + if (!this->pages_.empty()) { + auto *current_page = this->parent_->get_display()->get_active_page(); + touched &= std::find(this->pages_.begin(), this->pages_.end(), current_page) != this->pages_.end(); } if (touched) { this->publish_state(true); diff --git a/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h b/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h index b56ae562b1..862f41064c 100644 --- a/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h +++ b/esphome/components/touchscreen/binary_sensor/touchscreen_binary_sensor.h @@ -6,6 +6,8 @@ #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include + namespace esphome { namespace touchscreen { @@ -30,14 +32,14 @@ class TouchscreenBinarySensor : public binary_sensor::BinarySensor, int16_t get_width() { return this->x_max_ - this->x_min_; } int16_t get_height() { return this->y_max_ - this->y_min_; } - void set_page(display::DisplayPage *page) { this->page_ = page; } + void add_page(display::DisplayPage *page) { this->pages_.push_back(page); } void touch(TouchPoint tp) override; void release() override; protected: int16_t x_min_, x_max_, y_min_, y_max_; - display::DisplayPage *page_{nullptr}; + std::vector pages_{}; }; } // namespace touchscreen From dff6884bedd1585b49d7864e52039f0f14e74fa8 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 30 Jul 2024 16:57:51 -0400 Subject: [PATCH 0115/1052] [micro_wake_word] Fix VAD detection and modify detection computation (#7164) --- esphome/components/micro_wake_word/streaming_model.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/esphome/components/micro_wake_word/streaming_model.cpp b/esphome/components/micro_wake_word/streaming_model.cpp index 013fa2ce6e..d0d2e2df05 100644 --- a/esphome/components/micro_wake_word/streaming_model.cpp +++ b/esphome/components/micro_wake_word/streaming_model.cpp @@ -148,7 +148,7 @@ WakeWordModel::WakeWordModel(const uint8_t *model_start, float probability_cutof }; bool WakeWordModel::determine_detected() { - int32_t sum = 0; + uint32_t sum = 0; for (auto &prob : this->recent_streaming_probabilities_) { sum += prob; } @@ -175,12 +175,14 @@ VADModel::VADModel(const uint8_t *model_start, float probability_cutoff, size_t }; bool VADModel::determine_detected() { - uint8_t max = 0; + uint32_t sum = 0; for (auto &prob : this->recent_streaming_probabilities_) { - max = std::max(prob, max); + sum += prob; } - return max > this->probability_cutoff_; + float sliding_window_average = static_cast(sum) / static_cast(255 * this->sliding_window_size_); + + return sliding_window_average > this->probability_cutoff_; } } // namespace micro_wake_word From dd3dd7a136b9f81a5d3ff7c7296edd031af38f4f Mon Sep 17 00:00:00 2001 From: Adam Allport Date: Tue, 30 Jul 2024 22:30:15 +0100 Subject: [PATCH 0116/1052] fix: Add `pin->setup();` to matrix_keypad.cpp (#7163) --- esphome/components/matrix_keypad/matrix_keypad.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/matrix_keypad/matrix_keypad.cpp b/esphome/components/matrix_keypad/matrix_keypad.cpp index 902e574846..f62c75c869 100644 --- a/esphome/components/matrix_keypad/matrix_keypad.cpp +++ b/esphome/components/matrix_keypad/matrix_keypad.cpp @@ -8,6 +8,7 @@ static const char *const TAG = "matrix_keypad"; void MatrixKeypad::setup() { for (auto *pin : this->rows_) { + pin->setup(); if (!has_diodes_) { pin->pin_mode(gpio::FLAG_INPUT); } else { @@ -15,6 +16,7 @@ void MatrixKeypad::setup() { } } for (auto *pin : this->columns_) { + pin->setup(); if (has_pulldowns_) { pin->pin_mode(gpio::FLAG_INPUT); } else { From 8849443bf6e9f56e5c0f682a25a78a6782007a1e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:08:11 +1200 Subject: [PATCH 0117/1052] [update] Implement ``update.perform`` action and ``update.is_available`` condition (#7165) * [update] Fix unimplemented yaml action/condition * Add/update tests --------- Co-authored-by: Keith Burzinski --- .../update/http_request_update.cpp | 4 +- .../http_request/update/http_request_update.h | 2 +- esphome/components/update/__init__.py | 48 +++++++++++++------ esphome/components/update/automation.h | 23 +++++++++ esphome/components/update/update_entity.h | 4 +- tests/components/update/common.yaml | 27 +++++++++++ tests/components/update/test.esp32-ard.yaml | 3 ++ tests/components/update/test.esp8266-ard.yaml | 3 ++ tests/components/update/test.rp2040-ard.yaml | 3 ++ 9 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 esphome/components/update/automation.h diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 0a14dfd933..059148e7e5 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -138,8 +138,8 @@ void HttpRequestUpdate::update() { this->publish_state(); } -void HttpRequestUpdate::perform() { - if (this->state_ != update::UPDATE_STATE_AVAILABLE) { +void HttpRequestUpdate::perform(bool force) { + if (this->state_ != update::UPDATE_STATE_AVAILABLE && !force) { return; } diff --git a/esphome/components/http_request/update/http_request_update.h b/esphome/components/http_request/update/http_request_update.h index a6bc97392b..943231a906 100644 --- a/esphome/components/http_request/update/http_request_update.h +++ b/esphome/components/http_request/update/http_request_update.h @@ -15,7 +15,7 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent { void setup() override; void update() override; - void perform() override; + void perform(bool force) override; void set_source_url(const std::string &source_url) { this->source_url_ = source_url; } diff --git a/esphome/components/update/__init__.py b/esphome/components/update/__init__.py index 45bf082fa4..ba3b2f20df 100644 --- a/esphome/components/update/__init__.py +++ b/esphome/components/update/__init__.py @@ -1,10 +1,11 @@ from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt, web_server import esphome.config_validation as cv -import esphome.codegen as cg from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, + CONF_FORCE_UPDATE, CONF_ID, CONF_MQTT_ID, CONF_WEB_SERVER_ID, @@ -23,8 +24,12 @@ UpdateEntity = update_ns.class_("UpdateEntity", cg.EntityBase) UpdateInfo = update_ns.struct("UpdateInfo") -PerformAction = update_ns.class_("PerformAction", automation.Action) -IsAvailableCondition = update_ns.class_("IsAvailableCondition", automation.Condition) +PerformAction = update_ns.class_( + "PerformAction", automation.Action, cg.Parented.template(UpdateEntity) +) +IsAvailableCondition = update_ns.class_( + "IsAvailableCondition", automation.Condition, cg.Parented.template(UpdateEntity) +) DEVICE_CLASSES = [ DEVICE_CLASS_EMPTY, @@ -92,24 +97,37 @@ async def to_code(config): cg.add_global(update_ns.using) -UPDATE_AUTOMATION_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.use_id(UpdateEntity), - } +@automation.register_action( + "update.perform", + PerformAction, + automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(UpdateEntity), + cv.Optional(CONF_FORCE_UPDATE, default=False): cv.templatable(cv.boolean), + } + ), ) - - -@automation.register_action("update.perform", PerformAction, UPDATE_AUTOMATION_SCHEMA) async def update_perform_action_to_code(config, action_id, template_arg, args): - paren = await cg.get_variable(config[CONF_ID]) - return cg.new_Pvariable(action_id, paren, paren) + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + + force = await cg.templatable(config[CONF_FORCE_UPDATE], args, cg.bool_) + cg.add(var.set_force(force)) + return var @automation.register_condition( - "update.is_available", IsAvailableCondition, UPDATE_AUTOMATION_SCHEMA + "update.is_available", + IsAvailableCondition, + automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(UpdateEntity), + } + ), ) async def update_is_available_condition_to_code( config, condition_id, template_arg, args ): - paren = await cg.get_variable(config[CONF_ID]) - return cg.new_Pvariable(condition_id, paren, paren) + var = cg.new_Pvariable(condition_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/esphome/components/update/automation.h b/esphome/components/update/automation.h new file mode 100644 index 0000000000..df50f86a0c --- /dev/null +++ b/esphome/components/update/automation.h @@ -0,0 +1,23 @@ +#pragma once + +#include "update_entity.h" + +#include "esphome/core/automation.h" + +namespace esphome { +namespace update { + +template class PerformAction : public Action, public Parented { + TEMPLATABLE_VALUE(bool, force) + + public: + void play(Ts... x) override { this->parent_->perform(this->force_.value(x...)); } +}; + +template class IsAvailableCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->state == UPDATE_STATE_AVAILABLE; } +}; + +} // namespace update +} // namespace esphome diff --git a/esphome/components/update/update_entity.h b/esphome/components/update/update_entity.h index 5984c8e35b..568fbe3bb0 100644 --- a/esphome/components/update/update_entity.h +++ b/esphome/components/update/update_entity.h @@ -32,7 +32,9 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass { void publish_state(); - virtual void perform() = 0; + void perform() { this->perform(false); } + + virtual void perform(bool force) = 0; const UpdateInfo &update_info = update_info_; const UpdateState &state = state_; diff --git a/tests/components/update/common.yaml b/tests/components/update/common.yaml index 91b8669505..dcb4f42527 100644 --- a/tests/components/update/common.yaml +++ b/tests/components/update/common.yaml @@ -1 +1,28 @@ +substitutions: + verify_ssl: "true" + +esphome: + on_boot: + then: + - if: + condition: + update.is_available: + then: + - logger.log: "Update available" + - update.perform: + force_update: true + +wifi: + ssid: MySSID + password: password1 + +http_request: + verify_ssl: ${verify_ssl} + +ota: + - platform: http_request + update: + - platform: http_request + name: Firmware Update + source: http://example.com/manifest.json diff --git a/tests/components/update/test.esp32-ard.yaml b/tests/components/update/test.esp32-ard.yaml index dade44d145..c1937b5a10 100644 --- a/tests/components/update/test.esp32-ard.yaml +++ b/tests/components/update/test.esp32-ard.yaml @@ -1 +1,4 @@ +substitutions: + verify_ssl: "false" + <<: !include common.yaml diff --git a/tests/components/update/test.esp8266-ard.yaml b/tests/components/update/test.esp8266-ard.yaml index dade44d145..c1937b5a10 100644 --- a/tests/components/update/test.esp8266-ard.yaml +++ b/tests/components/update/test.esp8266-ard.yaml @@ -1 +1,4 @@ +substitutions: + verify_ssl: "false" + <<: !include common.yaml diff --git a/tests/components/update/test.rp2040-ard.yaml b/tests/components/update/test.rp2040-ard.yaml index dade44d145..c1937b5a10 100644 --- a/tests/components/update/test.rp2040-ard.yaml +++ b/tests/components/update/test.rp2040-ard.yaml @@ -1 +1,4 @@ +substitutions: + verify_ssl: "false" + <<: !include common.yaml From 3920029aff9dd85fd5f0775945b8426c03da471c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:31:15 +1000 Subject: [PATCH 0118/1052] [lvgl] PR stage 3 (#7160) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/lvgl/__init__.py | 58 +++++-- esphome/components/lvgl/automation.py | 188 +++++++++++++++++++++ esphome/components/lvgl/btn.py | 6 +- esphome/components/lvgl/defines.py | 30 +++- esphome/components/lvgl/lv_validation.py | 46 ++--- esphome/components/lvgl/lvcode.py | 64 ++++--- esphome/components/lvgl/lvgl_esphome.cpp | 75 +++++++- esphome/components/lvgl/lvgl_esphome.h | 165 ++++++++++++++++-- esphome/components/lvgl/obj.py | 13 +- esphome/components/lvgl/rotary_encoders.py | 62 +++++++ esphome/components/lvgl/schemas.py | 80 ++++++++- esphome/components/lvgl/touchscreens.py | 5 +- esphome/components/lvgl/trigger.py | 61 +++++++ esphome/components/lvgl/types.py | 23 ++- esphome/components/lvgl/widget.py | 58 +++++-- esphome/core/defines.h | 3 + tests/components/lvgl/lvgl-package.yaml | 35 ++++ tests/components/lvgl/test.esp32-idf.yaml | 21 +++ 18 files changed, 895 insertions(+), 98 deletions(-) create mode 100644 esphome/components/lvgl/automation.py create mode 100644 esphome/components/lvgl/rotary_encoders.py create mode 100644 esphome/components/lvgl/trigger.py diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index c454a61957..182d04e038 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -1,5 +1,6 @@ import logging +from esphome.automation import build_automation, register_action, validate_automation import esphome.codegen as cg from esphome.components.display import Display import esphome.config_validation as cv @@ -8,7 +9,11 @@ from esphome.const import ( CONF_BUFFER_SIZE, CONF_ID, CONF_LAMBDA, + CONF_ON_IDLE, CONF_PAGES, + CONF_TIMEOUT, + CONF_TRIGGER_ID, + CONF_TYPE, ) from esphome.core import CORE, ID, Lambda from esphome.cpp_generator import MockObj @@ -16,21 +21,26 @@ from esphome.final_validate import full_config from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid +from .automation import update_to_code from .btn import btn_spec from .label import label_spec -from .lvcode import ConstantLiteral, LvContext +from .lv_validation import lv_images_used +from .lvcode import LvContext from .obj import obj_spec -from .schemas import any_widget_schema, obj_schema +from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code +from .schemas import any_widget_schema, create_modify_schema, obj_schema from .touchscreens import touchscreen_schema, touchscreens_to_code +from .trigger import generate_triggers from .types import ( WIDGET_TYPES, FontEngine, + IdleTrigger, LvglComponent, - lv_disp_t_ptr, + ObjUpdateAction, lv_font_t, lvgl_ns, ) -from .widget import LvScrActType, Widget, add_widgets, set_obj_properties +from .widget import Widget, add_widgets, lv_scr_act, set_obj_properties DOMAIN = "lvgl" DEPENDENCIES = ("display",) @@ -41,17 +51,21 @@ LOGGER = logging.getLogger(__name__) for w_type in (label_spec, obj_spec, btn_spec): WIDGET_TYPES[w_type.name] = w_type -lv_scr_act_spec = LvScrActType() -lv_scr_act = Widget.create( - None, ConstantLiteral("lv_scr_act()"), lv_scr_act_spec, {}, parent=None -) - WIDGET_SCHEMA = any_widget_schema() +for w_type in WIDGET_TYPES.values(): + register_action( + f"lvgl.{w_type.name}.update", + ObjUpdateAction, + create_modify_schema(w_type), + )(update_to_code) + async def add_init_lambda(lv_component, init): if init: - lamb = await cg.process_lambda(Lambda(init), [(lv_disp_t_ptr, "lv_disp")]) + lamb = await cg.process_lambda( + Lambda(init), [(LvglComponent.operator("ptr"), "lv_component")] + ) cg.add(lv_component.add_init_lambda(lamb)) @@ -99,6 +113,13 @@ def final_validation(config): buffer_frac = config[CONF_BUFFER_SIZE] if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config: LOGGER.warning("buffer_size: may need to be reduced without PSRAM") + for image_id in lv_images_used: + path = global_config.get_path_for_id(image_id)[:-1] + image_conf = global_config.get_config_for_path(path) + if image_conf[CONF_TYPE] in ("RGBA", "RGB24"): + raise cv.Invalid( + "Using RGBA or RGB24 in image config not compatible with LVGL", path + ) async def to_code(config): @@ -174,9 +195,15 @@ async def to_code(config): with LvContext(): await touchscreens_to_code(lv_component, config) + await rotary_encoders_to_code(lv_component, config) await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) - Widget.set_completed() + Widget.set_completed() + await generate_triggers(lv_component) + for conf in config.get(CONF_ON_IDLE, ()): + templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) + idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ) + await build_automation(idle_trigger, [], conf) await add_init_lambda(lv_component, LvContext.get_code()) for comp in helpers.lvgl_components_required: CORE.add_define(f"USE_LVGL_{comp.upper()}") @@ -212,9 +239,18 @@ CONFIG_SCHEMA = ( cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of( "big_endian", "little_endian" ), + cv.Optional(CONF_ON_IDLE): validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(IdleTrigger), + cv.Required(CONF_TIMEOUT): cv.templatable( + cv.positive_time_period_milliseconds + ), + } + ), cv.Optional(df.CONF_WIDGETS): cv.ensure_list(WIDGET_SCHEMA), cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color, cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema, + cv.GenerateID(df.CONF_ROTARY_ENCODERS): ROTARY_ENCODER_CONFIG, } ) ).add_extra(cv.has_at_least_one_key(CONF_PAGES, df.CONF_WIDGETS)) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py new file mode 100644 index 0000000000..4fd0be185e --- /dev/null +++ b/esphome/components/lvgl/automation.py @@ -0,0 +1,188 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_TIMEOUT +from esphome.core import Lambda +from esphome.cpp_generator import RawStatement +from esphome.cpp_types import nullptr + +from .defines import CONF_LVGL_ID, CONF_SHOW_SNOW, literal +from .lv_validation import lv_bool +from .lvcode import ( + LambdaContext, + ReturnStatement, + add_line_marks, + lv, + lv_add, + lv_obj, + lvgl_comp, +) +from .schemas import ACTION_SCHEMA, LVGL_SCHEMA +from .types import ( + LvglAction, + LvglComponent, + LvglComponentPtr, + LvglCondition, + ObjUpdateAction, + lv_obj_t, +) +from .widget import Widget, get_widget, lv_scr_act, set_obj_properties + + +async def action_to_code(action: list, action_id, widget: Widget, template_arg, args): + with LambdaContext() as context: + lv.cond_if(widget.obj == nullptr) + lv_add(RawStatement(" return;")) + lv.cond_endif() + code = context.get_code() + code.extend(action) + action = "\n".join(code) + "\n\n" + lamb = await cg.process_lambda(Lambda(action), args) + var = cg.new_Pvariable(action_id, template_arg, lamb) + return var + + +async def update_to_code(config, action_id, template_arg, args): + if config is not None: + widget = await get_widget(config) + with LambdaContext() as context: + add_line_marks(action_id) + await set_obj_properties(widget, config) + await widget.type.to_code(widget, config) + if ( + widget.type.w_type.value_property is not None + and widget.type.w_type.value_property in config + ): + lv.event_send(widget.obj, literal("LV_EVENT_VALUE_CHANGED"), nullptr) + return await action_to_code( + context.get_code(), action_id, widget, template_arg, args + ) + + +@automation.register_condition( + "lvgl.is_paused", + LvglCondition, + LVGL_SCHEMA, +) +async def lvgl_is_paused(config, condition_id, template_arg, args): + lvgl = config[CONF_LVGL_ID] + with LambdaContext( + [(LvglComponentPtr, "lvgl_comp")], return_type=cg.bool_ + ) as context: + lv_add(ReturnStatement(lvgl_comp.is_paused())) + var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, lvgl) + return var + + +@automation.register_condition( + "lvgl.is_idle", + LvglCondition, + LVGL_SCHEMA.extend( + { + cv.Required(CONF_TIMEOUT): cv.templatable( + cv.positive_time_period_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) + with LambdaContext( + [(LvglComponentPtr, "lvgl_comp")], return_type=cg.bool_ + ) as context: + lv_add(ReturnStatement(lvgl_comp.is_idle(timeout))) + var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, lvgl) + return var + + +@automation.register_action( + "lvgl.widget.redraw", + ObjUpdateAction, + cv.Schema( + { + cv.Optional(CONF_ID): cv.use_id(lv_obj_t), + cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), + } + ), +) +async def obj_invalidate_to_code(config, action_id, template_arg, args): + if CONF_ID in config: + w = await get_widget(config) + else: + w = lv_scr_act + with LambdaContext() as context: + add_line_marks(action_id) + lv_obj.invalidate(w.obj) + return await action_to_code(context.get_code(), action_id, w, template_arg, args) + + +@automation.register_action( + "lvgl.pause", + LvglAction, + { + cv.GenerateID(): cv.use_id(LvglComponent), + cv.Optional(CONF_SHOW_SNOW, default=False): lv_bool, + }, +) +async def pause_action_to_code(config, action_id, template_arg, args): + with LambdaContext([(LvglComponentPtr, "lvgl_comp")]) as context: + add_line_marks(action_id) + lv_add(lvgl_comp.set_paused(True, config[CONF_SHOW_SNOW])) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_action( + "lvgl.resume", + LvglAction, + { + cv.GenerateID(): cv.use_id(LvglComponent), + }, +) +async def resume_action_to_code(config, action_id, template_arg, args): + with LambdaContext([(LvglComponentPtr, "lvgl_comp")]) as context: + add_line_marks(action_id) + lv_add(lvgl_comp.set_paused(False, False)) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, config[CONF_ID]) + return var + + +@automation.register_action("lvgl.widget.disable", ObjUpdateAction, ACTION_SCHEMA) +async def obj_disable_to_code(config, action_id, template_arg, args): + w = await get_widget(config) + with LambdaContext() as context: + add_line_marks(action_id) + w.add_state("LV_STATE_DISABLED") + return await action_to_code(context.get_code(), action_id, w, template_arg, args) + + +@automation.register_action("lvgl.widget.enable", ObjUpdateAction, ACTION_SCHEMA) +async def obj_enable_to_code(config, action_id, template_arg, args): + w = await get_widget(config) + with LambdaContext() as context: + add_line_marks(action_id) + w.clear_state("LV_STATE_DISABLED") + return await action_to_code(context.get_code(), action_id, w, template_arg, args) + + +@automation.register_action("lvgl.widget.hide", ObjUpdateAction, ACTION_SCHEMA) +async def obj_hide_to_code(config, action_id, template_arg, args): + w = await get_widget(config) + with LambdaContext() as context: + add_line_marks(action_id) + w.add_flag("LV_OBJ_FLAG_HIDDEN") + return await action_to_code(context.get_code(), action_id, w, template_arg, args) + + +@automation.register_action("lvgl.widget.show", ObjUpdateAction, ACTION_SCHEMA) +async def obj_show_to_code(config, action_id, template_arg, args): + w = await get_widget(config) + with LambdaContext() as context: + add_line_marks(action_id) + w.clear_flag("LV_OBJ_FLAG_HIDDEN") + return await action_to_code(context.get_code(), action_id, w, template_arg, args) diff --git a/esphome/components/lvgl/btn.py b/esphome/components/lvgl/btn.py index 4f5f88d9e6..064d886d47 100644 --- a/esphome/components/lvgl/btn.py +++ b/esphome/components/lvgl/btn.py @@ -9,9 +9,6 @@ class BtnType(WidgetType): def __init__(self): super().__init__(CONF_BUTTON, LvBoolean("lv_btn_t"), (CONF_MAIN,)) - async def to_code(self, w, config): - return [] - def obj_creator(self, parent: MockObjClass, config: dict): """ LVGL 8 calls buttons `btn` @@ -21,5 +18,8 @@ class BtnType(WidgetType): def get_uses(self): return ("btn",) + async def to_code(self, w, config): + return [] + btn_spec = BtnType() diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index a2b4ac13fb..9f349e3943 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -4,12 +4,32 @@ Constants already defined in esphome.const are not duplicated here and must be i """ +from typing import Union + from esphome import codegen as cg, config_validation as cv from esphome.core import ID, Lambda +from esphome.cpp_generator import Literal from esphome.cpp_types import uint32 from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor -from .lvcode import ConstantLiteral +from .helpers import requires_component + + +class ConstantLiteral(Literal): + __slots__ = ("constant",) + + def __init__(self, constant: str): + super().__init__() + self.constant = constant + + def __str__(self): + return self.constant + + +def literal(arg: Union[str, ConstantLiteral]): + if isinstance(arg, str): + return ConstantLiteral(arg) + return arg class LValidator: @@ -18,14 +38,19 @@ class LValidator: has `process()` to convert a value during code generation """ - def __init__(self, validator, rtype, idtype=None, idexpr=None, retmapper=None): + def __init__( + self, validator, rtype, idtype=None, idexpr=None, retmapper=None, requires=None + ): self.validator = validator self.rtype = rtype self.idtype = idtype self.idexpr = idexpr self.retmapper = retmapper + self.requires = requires def __call__(self, value): + if self.requires: + value = requires_component(self.requires)(value) if isinstance(value, cv.Lambda): return cv.returning_lambda(value) if self.idtype is not None and isinstance(value, ID): @@ -422,6 +447,7 @@ CONF_RECOLOR = "recolor" CONF_RIGHT_BUTTON = "right_button" CONF_ROLLOVER = "rollover" CONF_ROOT_BACK_BTN = "root_back_btn" +CONF_ROTARY_ENCODERS = "rotary_encoders" CONF_ROWS = "rows" CONF_SCALES = "scales" CONF_SCALE_LINES = "scale_lines" diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 533dc582f0..818bde6aed 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -2,6 +2,7 @@ import esphome.codegen as cg from esphome.components.binary_sensor import BinarySensor from esphome.components.color import ColorStruct from esphome.components.font import Font +from esphome.components.image import Image_ from esphome.components.sensor import Sensor from esphome.components.text_sensor import TextSensor import esphome.config_validation as cv @@ -13,22 +14,15 @@ from esphome.helpers import cpp_string_escape from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from . import types as ty -from .defines import LV_FONTS, LValidator, LvConstant +from .defines import LV_FONTS, ConstantLiteral, LValidator, LvConstant, literal from .helpers import ( esphome_fonts_used, lv_fonts_used, lvgl_components_required, requires_component, ) -from .lvcode import ConstantLiteral, lv_expr -from .types import lv_font_t - - -def literal_mapper(value, args=()): - if isinstance(value, str): - return ConstantLiteral(value) - return value - +from .lvcode import lv_expr +from .types import lv_font_t, lv_img_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -43,7 +37,7 @@ def opacity_validator(value): return value -opacity = LValidator(opacity_validator, uint32, retmapper=literal_mapper) +opacity = LValidator(opacity_validator, uint32, retmapper=literal) @schema_extractor("one_of") @@ -79,9 +73,7 @@ def pixels_or_percent_validator(value): return f"lv_pct({int(cv.percentage(value) * 100)})" -pixels_or_percent = LValidator( - pixels_or_percent_validator, uint32, retmapper=literal_mapper -) +pixels_or_percent = LValidator(pixels_or_percent_validator, uint32, retmapper=literal) def zoom(value): @@ -115,7 +107,7 @@ def size_validator(value): return f"lv_pct({int(cv.percentage(value) * 100)})" -size = LValidator(size_validator, uint32, retmapper=literal_mapper) +size = LValidator(size_validator, uint32, retmapper=literal) radius_consts = LvConstant("LV_RADIUS_", "CIRCLE") @@ -130,21 +122,37 @@ def radius_validator(value): return value +radius = LValidator(radius_validator, uint32, retmapper=literal) + + def id_name(value): if value == SCHEMA_EXTRACT: return "id" return cv.validate_id_name(value) -radius = LValidator(radius_validator, uint32, retmapper=literal_mapper) - - def stop_value(value): return cv.int_range(0, 255)(value) +lv_images_used = set() + + +def image_validator(value): + value = requires_component("image")(value) + value = cv.use_id(Image_)(value) + lv_images_used.add(value) + return value + + +lv_image = LValidator( + image_validator, + lv_img_t, + retmapper=lambda x: lv_expr.img_from(MockObj(x)), + requires="image", +) lv_bool = LValidator( - cv.boolean, cg.bool_, BinarySensor, "get_state()", retmapper=literal_mapper + cv.boolean, cg.bool_, BinarySensor, "get_state()", retmapper=literal ) diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 13b4862b4d..3a8a958f2e 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -8,8 +8,8 @@ from esphome.cpp_generator import ( AssignmentExpression, CallExpression, Expression, + ExpressionStatement, LambdaExpression, - Literal, MockObj, RawExpression, RawStatement, @@ -19,7 +19,9 @@ from esphome.cpp_generator import ( statement, ) +from .defines import ConstantLiteral from .helpers import get_line_marks +from .types import lv_group_t _LOGGER = logging.getLogger(__name__) @@ -105,29 +107,40 @@ class LambdaContext(CodeContext): def __init__( self, - parameters: list[tuple[SafeExpType, str]], - return_type: SafeExpType = None, + parameters: list[tuple[SafeExpType, str]] = None, + return_type: SafeExpType = cg.void, + capture: str = "", ): super().__init__() self.code_list: list[Statement] = [] self.parameters = parameters self.return_type = return_type + self.capture = capture def add(self, expression: Union[Expression, Statement]): self.code_list.append(expression) return expression - async def code(self) -> LambdaExpression: + async def get_lambda(self) -> LambdaExpression: + code_text = self.get_code() + return await cg.process_lambda( + Lambda("\n".join(code_text) + "\n\n"), + self.parameters, + capture=self.capture, + return_type=self.return_type, + ) + + def get_code(self): code_text = [] for exp in self.code_list: text = str(statement(exp)) text = text.rstrip() code_text.append(text) - return await cg.process_lambda( - Lambda("\n".join(code_text) + "\n\n"), - self.parameters, - return_type=self.return_type, - ) + return code_text + + def __enter__(self): + super().__enter__() + return self class LocalVariable(MockObj): @@ -187,13 +200,18 @@ class MockLv: return result def cond_if(self, expression: Expression): - CodeContext.append(RawExpression(f"if({expression}) {{")) + CodeContext.append(RawStatement(f"if {expression} {{")) def cond_else(self): - CodeContext.append(RawExpression("} else {")) + CodeContext.append(RawStatement("} else {")) def cond_endif(self): - CodeContext.append(RawExpression("}")) + CodeContext.append(RawStatement("}")) + + +class ReturnStatement(ExpressionStatement): + def __str__(self): + return f"return {self.expression};" class LvExpr(MockLv): @@ -210,6 +228,7 @@ lv = MockLv("lv_") lv_expr = LvExpr("lv_") # Mock for lv_obj_ calls lv_obj = MockLv("lv_obj_") +lvgl_comp = MockObj("lvgl_comp", "->") # equivalent to cg.add() for the lvgl init context @@ -226,12 +245,19 @@ def lv_assign(target, expression): lv_add(RawExpression(f"{target} = {expression}")) -class ConstantLiteral(Literal): - __slots__ = ("constant",) +lv_groups = {} # Widget group names - def __init__(self, constant: str): - super().__init__() - self.constant = constant - def __str__(self): - return self.constant +def add_group(name): + if name is None: + return None + fullname = f"lv_esp_group_{name}" + if name not in lv_groups: + gid = ID(fullname, True, type=lv_group_t.operator("ptr")) + lv_add( + AssignmentExpression( + type_=gid.type, modifier="", name=fullname, rhs=lv_expr.group_create() + ) + ) + lv_groups[name] = ConstantLiteral(fullname) + return lv_groups[name] diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 74a1b0e7af..34f8eaf21f 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -19,13 +19,35 @@ void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) { } void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { - auto now = millis(); - this->draw_buffer_(area, (const uint8_t *) color_p); - ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area), - lv_area_get_height(area), (int) (millis() - now)); + if (!this->paused_) { + auto now = millis(); + this->draw_buffer_(area, (const uint8_t *) color_p); + ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area), + lv_area_get_height(area), (int) (millis() - now)); + } lv_disp_flush_ready(disp_drv); } +void LvglComponent::write_random_() { + // length of 2 lines in 32 bit units + // we write 2 lines for the benefit of displays that won't write one line at a time. + size_t line_len = this->disp_drv_.hor_res * LV_COLOR_DEPTH / 8 / 4 * 2; + for (size_t i = 0; i != line_len; i++) { + ((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32(); + } + lv_area_t area; + area.x1 = 0; + area.x2 = this->disp_drv_.hor_res - 1; + if (this->snow_line_ == this->disp_drv_.ver_res / 2) { + area.y1 = static_cast(random_uint32() % (this->disp_drv_.ver_res / 2) * 2); + } else { + area.y1 = this->snow_line_++ * 2; + } + // write 2 lines + area.y2 = area.y1 + 1; + this->draw_buffer_(&area, (const uint8_t *) this->draw_buf_.buf1); +} + void LvglComponent::setup() { ESP_LOGCONFIG(TAG, "LVGL Setup starts"); #if LV_USE_LOG @@ -74,10 +96,53 @@ void LvglComponent::setup() { ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated); this->disp_ = lv_disp_drv_register(&this->disp_drv_); for (const auto &v : this->init_lambdas_) - v(this->disp_); + v(this); lv_disp_trig_activity(this->disp_); ESP_LOGCONFIG(TAG, "LVGL Setup complete"); } + +#ifdef USE_LVGL_IMAGE +lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) { + if (img_dsc == nullptr) + img_dsc = new lv_img_dsc_t(); // NOLINT + img_dsc->header.always_zero = 0; + img_dsc->header.reserved = 0; + img_dsc->header.w = src->get_width(); + img_dsc->header.h = src->get_height(); + img_dsc->data = src->get_data_start(); + img_dsc->data_size = image_type_to_width_stride(img_dsc->header.w * img_dsc->header.h, src->get_type()); + switch (src->get_type()) { + case image::IMAGE_TYPE_BINARY: + img_dsc->header.cf = LV_IMG_CF_ALPHA_1BIT; + break; + + case image::IMAGE_TYPE_GRAYSCALE: + img_dsc->header.cf = LV_IMG_CF_ALPHA_8BIT; + break; + + case image::IMAGE_TYPE_RGB24: + img_dsc->header.cf = LV_IMG_CF_RGB888; + break; + + case image::IMAGE_TYPE_RGB565: +#if LV_COLOR_DEPTH == 16 + img_dsc->header.cf = src->has_transparency() ? LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED : LV_IMG_CF_TRUE_COLOR; +#else + img_dsc->header.cf = LV_IMG_CF_RGB565; +#endif + break; + + case image::IMAGE_TYPE_RGBA: +#if LV_COLOR_DEPTH == 32 + img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR; +#else + img_dsc->header.cf = LV_IMG_CF_RGBA8888; +#endif + break; + } + return img_dsc; +} +#endif } // namespace lvgl } // namespace esphome diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index a884a27042..a0d3d226ce 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -1,23 +1,32 @@ #pragma once #include "esphome/core/defines.h" -#ifdef USE_LVGL + +#ifdef USE_LVGL_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif // USE_LVGL_BINARY_SENSOR +#ifdef USE_LVGL_ROTARY_ENCODER +#include "esphome/components/rotary_encoder/rotary_encoder.h" +#endif // USE_LVGL_ROTARY_ENCODER // required for clang-tidy #ifndef LV_CONF_H #define LV_CONF_SKIP 1 // NOLINT -#endif +#endif // LV_CONF_H #include "esphome/components/display/display.h" #include "esphome/components/display/display_color_utils.h" #include "esphome/core/component.h" -#include "esphome/core/hal.h" #include "esphome/core/log.h" #include +#include #include +#ifdef USE_LVGL_IMAGE +#include "esphome/components/image/image.h" +#endif // USE_LVGL_IMAGE #ifdef USE_LVGL_FONT #include "esphome/components/font/font.h" -#endif +#endif // USE_LVGL_FONT #ifdef USE_LVGL_TOUCHSCREEN #include "esphome/components/touchscreen/touchscreen.h" #endif // USE_LVGL_TOUCHSCREEN @@ -40,7 +49,7 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT // Parent class for things that wrap an LVGL object class LvCompound final { public: - virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; } + void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; } lv_obj_t *obj{}; }; @@ -49,6 +58,15 @@ using set_value_lambda_t = std::function; using event_callback_t = void(_lv_event_t *); using text_lambda_t = std::function; +template class ObjUpdateAction : public Action { + public: + explicit ObjUpdateAction(std::function &&lamb) : lamb_(std::move(lamb)) {} + + void play(Ts... x) override { this->lamb_(x...); } + + protected: + std::function lamb_; +}; #ifdef USE_LVGL_FONT class FontEngine { public: @@ -67,6 +85,9 @@ class FontEngine { lv_font_t lv_font_{}; }; #endif // USE_LVGL_FONT +#ifdef USE_LVGL_IMAGE +lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc = nullptr); +#endif // USE_LVGL_IMAGE class LvglComponent : public PollingComponent { constexpr static const char *const TAG = "lvgl"; @@ -92,27 +113,54 @@ class LvglComponent : public PollingComponent { area->y2++; } - void loop() override { lv_timer_handler_run_in_period(5); } void setup() override; - void update() override {} + void update() override { + // update indicators + if (this->paused_) { + return; + } + this->idle_callbacks_.call(lv_disp_get_inactive_time(this->disp_)); + } + void loop() override { + if (this->paused_) { + if (this->show_snow_) + this->write_random_(); + } + lv_timer_handler_run_in_period(5); + } + + void add_on_idle_callback(std::function &&callback) { + this->idle_callbacks_.add(std::move(callback)); + } void add_display(display::Display *display) { this->displays_.push_back(display); } - void add_init_lambda(const std::function &lamb) { this->init_lambdas_.push_back(lamb); } + void add_init_lambda(const std::function &lamb) { this->init_lambdas_.push_back(lamb); } void dump_config() override; void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; } + bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; } void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; } lv_disp_t *get_disp() { return this->disp_; } void set_paused(bool paused, bool show_snow) { this->paused_ = paused; + this->show_snow_ = show_snow; + this->snow_line_ = 0; if (!paused && lv_scr_act() != nullptr) { lv_disp_trig_activity(this->disp_); // resets the inactivity time lv_obj_invalidate(lv_scr_act()); } } + + void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) { + lv_obj_add_event_cb(obj, callback, event, this); + if (event == LV_EVENT_VALUE_CHANGED) { + lv_obj_add_event_cb(obj, callback, lv_custom_event, this); + } + } bool is_paused() const { return this->paused_; } protected: + void write_random_(); void draw_buffer_(const lv_area_t *area, const uint8_t *ptr); void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); std::vector displays_{}; @@ -120,12 +168,52 @@ class LvglComponent : public PollingComponent { lv_disp_drv_t disp_drv_{}; lv_disp_t *disp_{}; bool paused_{}; + bool show_snow_{}; + lv_coord_t snow_line_{}; - std::vector> init_lambdas_; + std::vector> init_lambdas_; + CallbackManager idle_callbacks_{}; size_t buffer_frac_{1}; bool full_refresh_{}; }; +class IdleTrigger : public Trigger<> { + public: + explicit IdleTrigger(LvglComponent *parent, TemplatableValue timeout) : timeout_(std::move(timeout)) { + parent->add_on_idle_callback([this](uint32_t idle_time) { + if (!this->is_idle_ && idle_time > this->timeout_.value()) { + this->is_idle_ = true; + this->trigger(); + } else if (this->is_idle_ && idle_time < this->timeout_.value()) { + this->is_idle_ = false; + } + }); + } + + protected: + TemplatableValue timeout_; + bool is_idle_{}; +}; + +template class LvglAction : public Action, public Parented { + public: + explicit LvglAction(std::function &&lamb) : action_(std::move(lamb)) {} + void play(Ts... x) override { this->action_(this->parent_); } + + protected: + std::function action_{}; +}; + +template class LvglCondition : public Condition, public Parented { + public: + LvglCondition(std::function &&condition_lambda) + : condition_lambda_(std::move(condition_lambda)) {} + bool check(Ts... x) override { return this->condition_lambda_(this->parent_); } + + protected: + std::function condition_lambda_{}; +}; + #ifdef USE_LVGL_TOUCHSCREEN class LVTouchListener : public touchscreen::TouchListener, public Parented { public: @@ -160,7 +248,62 @@ class LVTouchListener : public touchscreen::TouchListener, public Parented { + public: + LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) { + lv_indev_drv_init(&this->drv_); + this->drv_.type = type; + this->drv_.user_data = this; + this->drv_.long_press_time = lpt; + this->drv_.long_press_repeat_time = lprt; + this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) { + auto *l = static_cast(d->user_data); + data->state = l->pressed_ ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + data->key = l->key_; + data->enc_diff = (int16_t) (l->count_ - l->last_count_); + l->last_count_ = l->count_; + data->continue_reading = false; + }; + } + + void set_left_button(binary_sensor::BinarySensor *left_button) { + left_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_LEFT, state); }); + } + void set_right_button(binary_sensor::BinarySensor *right_button) { + right_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_RIGHT, state); }); + } + + void set_enter_button(binary_sensor::BinarySensor *enter_button) { + enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); }); + } + + void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor) { + sensor->register_listener([this](int32_t count) { this->set_count(count); }); + } + + void event(int key, bool pressed) { + if (!this->parent_->is_paused()) { + this->pressed_ = pressed; + this->key_ = key; + } + } + + void set_count(int32_t count) { + if (!this->parent_->is_paused()) + this->count_ = count; + } + + lv_indev_drv_t *get_drv() { return &this->drv_; } + + protected: + lv_indev_drv_t drv_{}; + bool pressed_{}; + int32_t count_{}; + int32_t last_count_{}; + int key_{}; +}; +#endif // USE_LVGL_KEY_LISTENER } // namespace lvgl } // namespace esphome - -#endif // USE_LVGL diff --git a/esphome/components/lvgl/obj.py b/esphome/components/lvgl/obj.py index 92c4f63d2d..40d7e55381 100644 --- a/esphome/components/lvgl/obj.py +++ b/esphome/components/lvgl/obj.py @@ -1,5 +1,9 @@ +from esphome import automation + +from .automation import update_to_code from .defines import CONF_MAIN, CONF_OBJ -from .types import WidgetType, lv_obj_t +from .schemas import create_modify_schema +from .types import ObjUpdateAction, WidgetType, lv_obj_t class ObjType(WidgetType): @@ -15,3 +19,10 @@ class ObjType(WidgetType): obj_spec = ObjType() + + +@automation.register_action( + "lvgl.widget.update", ObjUpdateAction, create_modify_schema(obj_spec) +) +async def obj_update_to_code(config, action_id, template_arg, args): + return await update_to_code(config, action_id, template_arg, args) diff --git a/esphome/components/lvgl/rotary_encoders.py b/esphome/components/lvgl/rotary_encoders.py new file mode 100644 index 0000000000..77dc397c3e --- /dev/null +++ b/esphome/components/lvgl/rotary_encoders.py @@ -0,0 +1,62 @@ +import esphome.codegen as cg +from esphome.components.binary_sensor import BinarySensor +from esphome.components.rotary_encoder.sensor import RotaryEncoderSensor +import esphome.config_validation as cv +from esphome.const import CONF_GROUP, CONF_ID, CONF_SENSOR + +from .defines import ( + CONF_ENTER_BUTTON, + CONF_LEFT_BUTTON, + CONF_LONG_PRESS_REPEAT_TIME, + CONF_LONG_PRESS_TIME, + CONF_RIGHT_BUTTON, + CONF_ROTARY_ENCODERS, +) +from .helpers import lvgl_components_required +from .lvcode import add_group, lv, lv_add, lv_expr +from .schemas import ENCODER_SCHEMA +from .types import lv_indev_type_t + +ROTARY_ENCODER_CONFIG = cv.ensure_list( + ENCODER_SCHEMA.extend( + { + cv.Required(CONF_ENTER_BUTTON): cv.use_id(BinarySensor), + cv.Required(CONF_SENSOR): cv.Any( + cv.use_id(RotaryEncoderSensor), + cv.Schema( + { + cv.Required(CONF_LEFT_BUTTON): cv.use_id(BinarySensor), + cv.Required(CONF_RIGHT_BUTTON): cv.use_id(BinarySensor), + } + ), + ), + } + ) +) + + +async def rotary_encoders_to_code(var, config): + for enc_conf in config.get(CONF_ROTARY_ENCODERS, ()): + lvgl_components_required.add("KEY_LISTENER") + lvgl_components_required.add("ROTARY_ENCODER") + lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds + lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds + listener = cg.new_Pvariable( + enc_conf[CONF_ID], lv_indev_type_t.LV_INDEV_TYPE_ENCODER, lpt, lprt + ) + await cg.register_parented(listener, var) + if sensor_config := enc_conf.get(CONF_SENSOR): + if isinstance(sensor_config, dict): + b_sensor = await cg.get_variable(sensor_config[CONF_LEFT_BUTTON]) + cg.add(listener.set_left_button(b_sensor)) + b_sensor = await cg.get_variable(sensor_config[CONF_RIGHT_BUTTON]) + cg.add(listener.set_right_button(b_sensor)) + else: + sensor_config = await cg.get_variable(sensor_config) + lv_add(listener.set_sensor(sensor_config)) + b_sensor = await cg.get_variable(enc_conf[CONF_ENTER_BUTTON]) + cg.add(listener.set_enter_button(b_sensor)) + if group := add_group(enc_conf.get(CONF_GROUP)): + lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group) + else: + lv.indev_drv_register(listener.get_drv()) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 9f6d984545..ebef56a882 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -1,10 +1,21 @@ from esphome import config_validation as cv -from esphome.const import CONF_ARGS, CONF_FORMAT, CONF_ID, CONF_STATE, CONF_TYPE +from esphome.automation import Trigger, validate_automation +from esphome.const import ( + CONF_ARGS, + CONF_FORMAT, + CONF_GROUP, + CONF_ID, + CONF_ON_VALUE, + CONF_STATE, + CONF_TRIGGER_ID, + CONF_TYPE, +) +from esphome.core import TimePeriod from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid, types as ty from .helpers import add_lv_use, requires_component, validate_printf -from .lv_validation import lv_font +from .lv_validation import id_name, lv_font from .types import WIDGET_TYPES, WidgetType # A schema for text properties @@ -27,6 +38,28 @@ TEXT_SCHEMA = cv.Schema( } ) +ACTION_SCHEMA = cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(ty.lv_pseudo_button_t), + }, + key=CONF_ID, +) + +PRESS_TIME = cv.All( + lvalid.lv_milliseconds, cv.Range(max=TimePeriod(milliseconds=65535)) +) + +ENCODER_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.All( + cv.declare_id(ty.LVEncoderListener), requires_component("binary_sensor") + ), + cv.Optional(CONF_GROUP): lvalid.id_name, + cv.Optional(df.CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME, + cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME, + } +) + # All LVGL styles and their validators STYLE_PROPS = { "align": df.CHILD_ALIGNMENTS.one_of, @@ -43,6 +76,7 @@ STYLE_PROPS = { "bg_image_opa": lvalid.opacity, "bg_image_recolor": lvalid.lv_color, "bg_image_recolor_opa": lvalid.opacity, + "bg_image_src": lvalid.lv_image, "bg_main_stop": lvalid.stop_value, "bg_opa": lvalid.opacity, "border_color": lvalid.lv_color, @@ -151,6 +185,39 @@ def part_schema(widget_type: WidgetType): ) +def automation_schema(typ: ty.LvType): + if typ.has_on_value: + events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,) + else: + events = df.LV_EVENT_TRIGGERS + if isinstance(typ, ty.LvType): + template = Trigger.template(typ.get_arg_type()) + else: + template = Trigger.template() + return { + cv.Optional(event): validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(template), + } + ) + for event in events + } + + +def create_modify_schema(widget_type): + return ( + part_schema(widget_type) + .extend( + { + cv.Required(CONF_ID): cv.use_id(widget_type), + cv.Optional(CONF_STATE): SET_STATE_SCHEMA, + } + ) + .extend(FLAG_SCHEMA) + .extend(widget_type.modify_schema) + ) + + def obj_schema(widget_type: WidgetType): """ Create a schema for a widget type itself i.e. no allowance for children @@ -161,10 +228,12 @@ def obj_schema(widget_type: WidgetType): part_schema(widget_type) .extend(FLAG_SCHEMA) .extend(ALIGN_TO_SCHEMA) + .extend(automation_schema(widget_type.w_type)) .extend( cv.Schema( { cv.Optional(CONF_STATE): SET_STATE_SCHEMA, + cv.Optional(CONF_GROUP): id_name, } ) ) @@ -188,6 +257,13 @@ STYLED_TEXT_SCHEMA = cv.maybe_simple_value( STYLE_SCHEMA.extend(TEXT_SCHEMA), key=df.CONF_TEXT ) +# For use by platform components +LVGL_SCHEMA = cv.Schema( + { + cv.GenerateID(df.CONF_LVGL_ID): cv.use_id(ty.LvglComponent), + } +) + ALL_STYLES = { **STYLE_PROPS, } diff --git a/esphome/components/lvgl/touchscreens.py b/esphome/components/lvgl/touchscreens.py index a0d4a3e4ad..499b33aa02 100644 --- a/esphome/components/lvgl/touchscreens.py +++ b/esphome/components/lvgl/touchscreens.py @@ -2,7 +2,7 @@ import esphome.codegen as cg from esphome.components.touchscreen import CONF_TOUCHSCREEN_ID, Touchscreen import esphome.config_validation as cv from esphome.const import CONF_ID -from esphome.core import CORE, TimePeriod +from esphome.core import CORE from .defines import ( CONF_LONG_PRESS_REPEAT_TIME, @@ -10,11 +10,10 @@ from .defines import ( CONF_TOUCHSCREENS, ) from .helpers import lvgl_components_required -from .lv_validation import lv_milliseconds from .lvcode import lv +from .schemas import PRESS_TIME from .types import LVTouchListener -PRESS_TIME = cv.All(lv_milliseconds, cv.Range(max=TimePeriod(milliseconds=65535))) CONF_TOUCHSCREEN = "touchscreen" TOUCHSCREENS_CONFIG = cv.maybe_simple_value( { diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py new file mode 100644 index 0000000000..bf92bda5b0 --- /dev/null +++ b/esphome/components/lvgl/trigger.py @@ -0,0 +1,61 @@ +from esphome import automation +import esphome.codegen as cg +from esphome.const import CONF_ID, CONF_ON_VALUE, CONF_TRIGGER_ID + +from .defines import ( + CONF_ALIGN, + CONF_ALIGN_TO, + CONF_X, + CONF_Y, + LV_EVENT, + LV_EVENT_TRIGGERS, + literal, +) +from .lvcode import LambdaContext, add_line_marks, lv, lv_add +from .widget import widget_map + +lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr") + + +async def generate_triggers(lv_component): + """ + Generate LVGL triggers for all defined widgets + Must be done after all widgets completed + :param lv_component: The parent component + :return: + """ + + for w in widget_map.values(): + if w.config: + for event, conf in { + event: conf + for event, conf in w.config.items() + if event in LV_EVENT_TRIGGERS + }.items(): + conf = conf[0] + w.add_flag("LV_OBJ_FLAG_CLICKABLE") + event = "LV_EVENT_" + LV_EVENT[event[3:].upper()] + await add_trigger(conf, event, lv_component, w) + for conf in w.config.get(CONF_ON_VALUE, ()): + await add_trigger(conf, "LV_EVENT_VALUE_CHANGED", lv_component, w) + + # Generate align to directives while we're here + if align_to := w.config.get(CONF_ALIGN_TO): + target = widget_map[align_to[CONF_ID]].obj + align = align_to[CONF_ALIGN] + x = align_to[CONF_X] + y = align_to[CONF_Y] + lv.obj_align_to(w.obj, target, align, x, y) + + +async def add_trigger(conf, event, lv_component, w): + tid = conf[CONF_TRIGGER_ID] + add_line_marks(tid) + trigger = cg.new_Pvariable(tid) + args = w.get_args() + value = w.get_value() + await automation.build_automation(trigger, args, conf) + with LambdaContext([(lv_event_t_ptr, "event_data")]) as context: + add_line_marks(tid) + lv_add(trigger.trigger(value)) + lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), literal(event))) diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index 60291ea54a..6997207dac 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -1,4 +1,4 @@ -from esphome import codegen as cg +from esphome import automation, codegen as cg from esphome.core import ID from esphome.cpp_generator import MockObjClass @@ -23,8 +23,14 @@ lvgl_ns = cg.esphome_ns.namespace("lvgl") char_ptr = cg.global_ns.namespace("char").operator("ptr") void_ptr = cg.void.operator("ptr") LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent) +LvglComponentPtr = LvglComponent.operator("ptr") lv_event_code_t = cg.global_ns.namespace("lv_event_code_t") +lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t") FontEngine = lvgl_ns.class_("FontEngine") +IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template()) +ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action) +LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition) +LvglAction = lvgl_ns.class_("LvglAction", automation.Action) LvCompound = lvgl_ns.class_("LvCompound") lv_font_t = cg.global_ns.class_("lv_font_t") lv_style_t = cg.global_ns.struct("lv_style_t") @@ -33,9 +39,11 @@ lv_obj_base_t = cg.global_ns.class_("lv_obj_t", lv_pseudo_button_t) lv_obj_t_ptr = lv_obj_base_t.operator("ptr") lv_disp_t_ptr = cg.global_ns.struct("lv_disp_t").operator("ptr") lv_color_t = cg.global_ns.struct("lv_color_t") +lv_group_t = cg.global_ns.struct("lv_group_t") LVTouchListener = lvgl_ns.class_("LVTouchListener") LVEncoderListener = lvgl_ns.class_("LVEncoderListener") lv_obj_t = LvType("lv_obj_t") +lv_img_t = LvType("lv_img_t") # this will be populated later, in __init__.py to avoid circular imports. @@ -58,7 +66,7 @@ class LvBoolean(LvType): super().__init__( *args, largs=[(cg.bool_, "x")], - lvalue=lambda w: w.is_checked(), + lvalue=lambda w: w.has_state("LV_STATE_CHECKED"), has_on_value=True, **kwargs, ) @@ -83,11 +91,14 @@ class WidgetType: self.name = name self.w_type = w_type self.parts = parts - self.schema = schema or {} - if modify_schema is None: - self.modify_schema = schema + if schema is None: + self.schema = {} else: - self.modify_schema = modify_schema + self.schema = schema + if modify_schema is None: + self.modify_schema = self.schema + else: + self.modify_schema = self.schema @property def animated(self): diff --git a/esphome/components/lvgl/widget.py b/esphome/components/lvgl/widget.py index 4755d8b21d..83aed341e7 100644 --- a/esphome/components/lvgl/widget.py +++ b/esphome/components/lvgl/widget.py @@ -4,9 +4,9 @@ from typing import Any from esphome import codegen as cg, config_validation as cv from esphome.config_validation import Invalid from esphome.const import CONF_GROUP, CONF_ID, CONF_STATE -from esphome.core import ID, TimePeriod +from esphome.core import CORE, TimePeriod from esphome.coroutine import FakeAwaitable -from esphome.cpp_generator import MockObjClass +from esphome.cpp_generator import MockObj, MockObjClass, VariableDeclarationExpression from .defines import ( CONF_DEFAULT, @@ -16,13 +16,15 @@ from .defines import ( OBJ_FLAGS, PARTS, STATES, + ConstantLiteral, LValidator, join_enums, + literal, ) from .helpers import add_lv_use -from .lvcode import ConstantLiteral, add_line_marks, lv, lv_add, lv_assign, lv_obj +from .lvcode import add_group, add_line_marks, lv, lv_add, lv_assign, lv_expr, lv_obj from .schemas import ALL_STYLES, STYLE_REMAP -from .types import WIDGET_TYPES, WidgetType, lv_obj_t +from .types import WIDGET_TYPES, LvType, WidgetType, lv_obj_t, lv_obj_t_ptr EVENT_LAMB = "event_lamb__" @@ -76,17 +78,20 @@ class Widget: return f"{self.var}->obj" return self.var - def add_state(self, *args): - return lv_obj.add_state(self.obj, *args) + def add_state(self, state): + return lv_obj.add_state(self.obj, literal(state)) - def clear_state(self, *args): - return lv_obj.clear_state(self.obj, *args) + def clear_state(self, state): + return lv_obj.clear_state(self.obj, literal(state)) - def add_flag(self, *args): - return lv_obj.add_flag(self.obj, *args) + def has_state(self, state): + return lv_expr.obj_get_state(self.obj) & literal(state) != 0 - def clear_flag(self, *args): - return lv_obj.clear_flag(self.obj, *args) + def add_flag(self, flag): + return lv_obj.add_flag(self.obj, literal(flag)) + + def clear_flag(self, flag): + return lv_obj.clear_flag(self.obj, literal(flag)) def set_property(self, prop, value, animated: bool = None, ltype=None): if isinstance(value, dict): @@ -125,6 +130,16 @@ class Widget: def __str__(self): return f"({self.var}, {self.type})" + def get_args(self): + if isinstance(self.type.w_type, LvType): + return self.type.w_type.args + return [(lv_obj_t_ptr, "obj")] + + def get_value(self): + if isinstance(self.type.w_type, LvType): + return self.type.w_type.value(self) + return self.obj + # Map of widgets to their config, used for trigger generation widget_map: dict[Any, Widget] = {} @@ -146,7 +161,8 @@ def get_widget_generator(wid): yield -async def get_widget(wid: ID) -> Widget: +async def get_widget(config: dict, id: str = CONF_ID) -> Widget: + wid = config[id] if obj := widget_map.get(wid): return obj return await FakeAwaitable(get_widget_generator(wid)) @@ -204,9 +220,10 @@ async def set_obj_properties(w: Widget, config): }.items(): if isinstance(ALL_STYLES[prop], LValidator): value = await ALL_STYLES[prop].process(value) - # Remapping for backwards compatibility of style names prop_r = STYLE_REMAP.get(prop, prop) w.set_style(prop_r, value, lv_state) + if group := add_group(config.get(CONF_GROUP)): + lv.group_add_obj(group, w.obj) flag_clr = set() flag_set = set() props = parts[CONF_MAIN][CONF_DEFAULT] @@ -241,7 +258,7 @@ async def set_obj_properties(w: Widget, config): w.clear_state(clears) for key, value in lambs.items(): lamb = await cg.process_lambda(value, [], return_type=cg.bool_) - state = ConstantLiteral(f"LV_STATE_{key.upper}") + state = f"LV_STATE_{key.upper}" lv.cond_if(lamb) w.add_state(state) lv.cond_else() @@ -281,10 +298,19 @@ async def widget_to_code(w_cnfig, w_type, parent): var = cg.new_Pvariable(wid) lv_add(var.set_obj(creator)) else: - var = cg.Pvariable(wid, cg.nullptr, type_=lv_obj_t) + var = MockObj(wid, "->") + decl = VariableDeclarationExpression(lv_obj_t, "*", wid) + CORE.add_global(decl) + CORE.register_variable(wid, var) lv_assign(var, creator) widget = Widget.create(wid, var, spec, w_cnfig, parent) await set_obj_properties(widget, w_cnfig) await add_widgets(widget, w_cnfig) await spec.to_code(widget, w_cnfig) + + +lv_scr_act_spec = LvScrActType() +lv_scr_act = Widget.create( + None, ConstantLiteral("lv_scr_act()"), lv_scr_act_spec, {}, parent=None +) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 6ba5b64761..726db24592 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -39,9 +39,12 @@ #define USE_LOCK #define USE_LOGGER #define USE_LVGL +#define USE_LVGL_BINARY_SENSOR #define USE_LVGL_FONT #define USE_LVGL_IMAGE +#define USE_LVGL_KEY_LISTENER #define USE_LVGL_TOUCHSCREEN +#define USE_LVGL_ROTARY_ENCODER #define USE_MDNS #define USE_MEDIA_PLAYER #define USE_MQTT diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 696c749876..fde700e0bd 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -7,6 +7,7 @@ lvgl: long_press_time: 500ms widgets: - label: + id: hello_label text: Hello world text_color: 0xFF8000 align: center @@ -95,9 +96,43 @@ lvgl: height: 10% pressed: bg_color: light_blue + checkable: true + checked: + bg_color: 0x000000 widgets: - label: text: Button + on_click: + lvgl.label.update: + id: hello_label + bg_color: 0x123456 + text: clicked + on_value: + logger.log: + format: "state now %d" + args: [x] + on_short_click: + lvgl.widget.hide: hello_label + on_long_press: + lvgl.widget.show: hello_label + on_cancel: + lvgl.widget.enable: hello_label + on_ready: + lvgl.widget.disable: hello_label + on_defocus: + lvgl.widget.hide: hello_label + on_focus: + logger.log: Button clicked + on_scroll: + logger.log: Button clicked + on_scroll_end: + logger.log: Button clicked + on_scroll_begin: + logger.log: Button clicked + on_release: + logger.log: Button clicked + on_long_press_repeat: + logger.log: Button clicked font: - file: "gfonts://Roboto" diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml index eab75b05f3..0f740db980 100644 --- a/tests/components/lvgl/test.esp32-idf.yaml +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -6,6 +6,23 @@ i2c: sda: GPIO18 scl: GPIO19 +sensor: + - platform: rotary_encoder + name: "Rotary Encoder" + id: encoder + pin_a: 2 + pin_b: 1 + internal: true + +binary_sensor: + - platform: gpio + id: pushbutton + name: Pushbutton + pin: + number: 0 + inverted: true + ignore_strapping_warning: true + display: - platform: ili9xxx model: st7789v @@ -50,5 +67,9 @@ lvgl: displays: - tft_display - second_display + rotary_encoders: + sensor: encoder + enter_button: pushbutton + group: general <<: !include common.yaml From dfacf1bbfe3aef5ca6796f92e8a46cb62212e924 Mon Sep 17 00:00:00 2001 From: thevogoncoder <6619878+thevogoncoder@users.noreply.github.com> Date: Thu, 25 Jul 2024 04:06:23 +0200 Subject: [PATCH 0119/1052] Add delay after sending REG_READ_START (#7130) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/pmwcs3/pmwcs3.cpp | 61 ++++++++++++++-------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/esphome/components/pmwcs3/pmwcs3.cpp b/esphome/components/pmwcs3/pmwcs3.cpp index 812018b52e..97ce4c9ae0 100644 --- a/esphome/components/pmwcs3/pmwcs3.cpp +++ b/esphome/components/pmwcs3/pmwcs3.cpp @@ -72,43 +72,44 @@ void PMWCS3Component::dump_config() { LOG_SENSOR(" ", "vwc", this->vwc_sensor_); } void PMWCS3Component::read_data_() { - uint8_t data[8]; - float e25, ec, temperature, vwc; - /////// Super important !!!! first activate reading PMWCS3_REG_READ_START (if not, return always the same values) //// - if (!this->write_bytes(PMWCS3_REG_READ_START, nullptr, 0)) { this->status_set_warning(); ESP_LOGVV(TAG, "Failed to write into REG_READ_START register !!!"); return; } - // NOLINT delay(100); - if (!this->read_bytes(PMWCS3_REG_GET_DATA, (uint8_t *) &data, 8)) { - ESP_LOGVV(TAG, "Error reading PMWCS3_REG_GET_DATA registers"); - this->mark_failed(); - return; - } - if (this->e25_sensor_ != nullptr) { - e25 = ((data[1] << 8) | data[0]) / 100.0; - this->e25_sensor_->publish_state(e25); - ESP_LOGVV(TAG, "e25: data[0]=%d, data[1]=%d, result=%f", data[0], data[1], e25); - } - if (this->ec_sensor_ != nullptr) { - ec = ((data[3] << 8) | data[2]) / 10.0; - this->ec_sensor_->publish_state(ec); - ESP_LOGVV(TAG, "ec: data[2]=%d, data[3]=%d, result=%f", data[2], data[3], ec); - } - if (this->temperature_sensor_ != nullptr) { - temperature = ((data[5] << 8) | data[4]) / 100.0; - this->temperature_sensor_->publish_state(temperature); - ESP_LOGVV(TAG, "temp: data[4]=%d, data[5]=%d, result=%f", data[4], data[5], temperature); - } - if (this->vwc_sensor_ != nullptr) { - vwc = ((data[7] << 8) | data[6]) / 10.0; - this->vwc_sensor_->publish_state(vwc); - ESP_LOGVV(TAG, "vwc: data[6]=%d, data[7]=%d, result=%f", data[6], data[7], vwc); - } + // Wait for the sensor to be ready. + // 80ms empirically determined (conservative). + this->set_timeout(80, [this] { + 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"); + this->mark_failed(); + return; + } + if (this->e25_sensor_ != nullptr) { + e25 = ((data[1] << 8) | data[0]) / 100.0; + this->e25_sensor_->publish_state(e25); + ESP_LOGVV(TAG, "e25: data[0]=%d, data[1]=%d, result=%f", data[0], data[1], e25); + } + if (this->ec_sensor_ != nullptr) { + ec = ((data[3] << 8) | data[2]) / 10.0; + this->ec_sensor_->publish_state(ec); + ESP_LOGVV(TAG, "ec: data[2]=%d, data[3]=%d, result=%f", data[2], data[3], ec); + } + if (this->temperature_sensor_ != nullptr) { + temperature = ((data[5] << 8) | data[4]) / 100.0; + this->temperature_sensor_->publish_state(temperature); + ESP_LOGVV(TAG, "temp: data[4]=%d, data[5]=%d, result=%f", data[4], data[5], temperature); + } + if (this->vwc_sensor_ != nullptr) { + vwc = ((data[7] << 8) | data[6]) / 10.0; + this->vwc_sensor_->publish_state(vwc); + ESP_LOGVV(TAG, "vwc: data[6]=%d, data[7]=%d, result=%f", data[6], data[7], vwc); + } + }); } } // namespace pmwcs3 From a70f926971264501287ac93b8e39d533461990dc Mon Sep 17 00:00:00 2001 From: RubyBailey <60991881+RubyBailey@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:20:29 -0700 Subject: [PATCH 0120/1052] Fix for Mitsubishi units that only support cooling (#7143) --- esphome/components/mitsubishi/mitsubishi.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/esphome/components/mitsubishi/mitsubishi.cpp b/esphome/components/mitsubishi/mitsubishi.cpp index a02aabf14d..449c8fc712 100644 --- a/esphome/components/mitsubishi/mitsubishi.cpp +++ b/esphome/components/mitsubishi/mitsubishi.cpp @@ -110,7 +110,7 @@ void MitsubishiClimate::transmit_state() { // Byte 15: HVAC specfic, i.e. POWERFUL, SMART SET, PLASMA, always 0x00 // Byte 16: Constant 0x00 // Byte 17: Checksum: SUM[Byte0...Byte16] - uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x00, 0x00, + uint8_t remote_state[18] = {0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; switch (this->mode) { @@ -136,6 +136,12 @@ void MitsubishiClimate::transmit_state() { break; case climate::CLIMATE_MODE_OFF: default: + remote_state[6] = MITSUBISHI_MODE_COOL; + remote_state[8] = MITSUBISHI_MODE_A_COOL; + if (this->supports_heat_) { + remote_state[6] = MITSUBISHI_MODE_HEAT; + remote_state[8] = MITSUBISHI_MODE_A_HEAT; + } remote_state[5] = MITSUBISHI_OFF; break; } From 5ac9d301eaca1d46cc0db942e828a7995289640e Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 30 Jul 2024 16:57:51 -0400 Subject: [PATCH 0121/1052] [micro_wake_word] Fix VAD detection and modify detection computation (#7164) --- esphome/components/micro_wake_word/streaming_model.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/esphome/components/micro_wake_word/streaming_model.cpp b/esphome/components/micro_wake_word/streaming_model.cpp index 013fa2ce6e..d0d2e2df05 100644 --- a/esphome/components/micro_wake_word/streaming_model.cpp +++ b/esphome/components/micro_wake_word/streaming_model.cpp @@ -148,7 +148,7 @@ WakeWordModel::WakeWordModel(const uint8_t *model_start, float probability_cutof }; bool WakeWordModel::determine_detected() { - int32_t sum = 0; + uint32_t sum = 0; for (auto &prob : this->recent_streaming_probabilities_) { sum += prob; } @@ -175,12 +175,14 @@ VADModel::VADModel(const uint8_t *model_start, float probability_cutoff, size_t }; bool VADModel::determine_detected() { - uint8_t max = 0; + uint32_t sum = 0; for (auto &prob : this->recent_streaming_probabilities_) { - max = std::max(prob, max); + sum += prob; } - return max > this->probability_cutoff_; + float sliding_window_average = static_cast(sum) / static_cast(255 * this->sliding_window_size_); + + return sliding_window_average > this->probability_cutoff_; } } // namespace micro_wake_word From 0af10c58f53466da19d9e065f764124e3bd31810 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 1 Aug 2024 07:51:23 +1200 Subject: [PATCH 0122/1052] Bump version to 2024.7.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 9abfafc4a4..1f63497982 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.7.2" +__version__ = "2024.7.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From cb9906b9215f17903ac004af160cc1537998b5a6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:38:36 +1200 Subject: [PATCH 0123/1052] [api] ``homeassistant.action`` replaces ``homeassistant.service`` (#7171) --- esphome/components/api/__init__.py | 130 +++++++++++++-------- esphome/config_validation.py | 10 ++ esphome/const.py | 2 + tests/components/api/common.yaml | 16 +-- tests/components/homeassistant/common.yaml | 8 +- 5 files changed, 103 insertions(+), 63 deletions(-) diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index d6b4416af8..38b50d4b9d 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -1,25 +1,27 @@ import base64 -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( + CONF_ACTION, + CONF_ACTIONS, CONF_DATA, CONF_DATA_TEMPLATE, + CONF_EVENT, CONF_ID, CONF_KEY, + CONF_ON_CLIENT_CONNECTED, + CONF_ON_CLIENT_DISCONNECTED, CONF_PASSWORD, CONF_PORT, CONF_REBOOT_TIMEOUT, CONF_SERVICE, - CONF_VARIABLES, CONF_SERVICES, - CONF_TRIGGER_ID, - CONF_EVENT, CONF_TAG, - CONF_ON_CLIENT_CONNECTED, - CONF_ON_CLIENT_DISCONNECTED, + CONF_TRIGGER_ID, + CONF_VARIABLES, ) from esphome.core import coroutine_with_priority @@ -63,40 +65,51 @@ def validate_encryption_key(value): return value -CONFIG_SCHEMA = cv.Schema( +ACTIONS_SCHEMA = automation.validate_automation( { - cv.GenerateID(): cv.declare_id(APIServer), - cv.Optional(CONF_PORT, default=6053): cv.port, - cv.Optional(CONF_PASSWORD, default=""): cv.string_strict, - cv.Optional( - CONF_REBOOT_TIMEOUT, default="15min" - ): cv.positive_time_period_milliseconds, - cv.Optional(CONF_SERVICES): automation.validate_automation( + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger), + cv.Exclusive(CONF_SERVICE, group_of_exclusion=CONF_ACTION): cv.valid_name, + cv.Exclusive(CONF_ACTION, group_of_exclusion=CONF_ACTION): cv.valid_name, + cv.Optional(CONF_VARIABLES, default={}): cv.Schema( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger), - cv.Required(CONF_SERVICE): cv.valid_name, - cv.Optional(CONF_VARIABLES, default={}): cv.Schema( - { - cv.validate_id_name: cv.one_of( - *SERVICE_ARG_NATIVE_TYPES, lower=True - ), - } - ), + cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True), } ), - cv.Optional(CONF_ENCRYPTION): cv.Schema( - { - cv.Required(CONF_KEY): validate_encryption_key, - } - ), - cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( - single=True - ), - cv.Optional(CONF_ON_CLIENT_DISCONNECTED): automation.validate_automation( - single=True - ), - } -).extend(cv.COMPONENT_SCHEMA) + }, + cv.All( + cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION), + cv.rename_key(CONF_SERVICE, CONF_ACTION), + ), +) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(APIServer), + cv.Optional(CONF_PORT, default=6053): cv.port, + cv.Optional(CONF_PASSWORD, default=""): cv.string_strict, + cv.Optional( + CONF_REBOOT_TIMEOUT, default="15min" + ): cv.positive_time_period_milliseconds, + cv.Exclusive( + CONF_SERVICES, group_of_exclusion=CONF_ACTIONS + ): ACTIONS_SCHEMA, + cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA, + cv.Optional(CONF_ENCRYPTION): cv.Schema( + { + cv.Required(CONF_KEY): validate_encryption_key, + } + ), + cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( + single=True + ), + cv.Optional(CONF_ON_CLIENT_DISCONNECTED): automation.validate_automation( + single=True + ), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.rename_key(CONF_SERVICES, CONF_ACTIONS), +) @coroutine_with_priority(40.0) @@ -108,7 +121,7 @@ async def to_code(config): cg.add(var.set_password(config[CONF_PASSWORD])) cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT])) - for conf in config.get(CONF_SERVICES, []): + for conf in config.get(CONF_ACTIONS, []): template_args = [] func_args = [] service_arg_names = [] @@ -119,7 +132,7 @@ async def to_code(config): service_arg_names.append(name) templ = cg.TemplateArguments(*template_args) trigger = cg.new_Pvariable( - conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names + conf[CONF_TRIGGER_ID], templ, conf[CONF_ACTION], service_arg_names ) cg.add(var.register_user_service(trigger)) await automation.build_automation(trigger, func_args, conf) @@ -152,28 +165,43 @@ async def to_code(config): KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)}) -HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.use_id(APIServer), - cv.Required(CONF_SERVICE): cv.templatable(cv.string), - cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, - cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, - cv.Optional(CONF_VARIABLES, default={}): cv.Schema( - {cv.string: cv.returning_lambda} - ), - } + +HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.use_id(APIServer), + cv.Exclusive(CONF_SERVICE, group_of_exclusion=CONF_ACTION): cv.templatable( + cv.string + ), + cv.Exclusive(CONF_ACTION, group_of_exclusion=CONF_ACTION): cv.templatable( + cv.string + ), + cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, + cv.Optional(CONF_VARIABLES, default={}): cv.Schema( + {cv.string: cv.returning_lambda} + ), + } + ), + cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION), + cv.rename_key(CONF_SERVICE, CONF_ACTION), ) +@automation.register_action( + "homeassistant.action", + HomeAssistantServiceCallAction, + HOMEASSISTANT_ACTION_ACTION_SCHEMA, +) @automation.register_action( "homeassistant.service", HomeAssistantServiceCallAction, - HOMEASSISTANT_SERVICE_ACTION_SCHEMA, + HOMEASSISTANT_ACTION_ACTION_SCHEMA, ) async def homeassistant_service_to_code(config, action_id, template_arg, args): serv = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, serv, False) - templ = await cg.templatable(config[CONF_SERVICE], args, None) + templ = await cg.templatable(config[CONF_ACTION], args, None) cg.add(var.set_service(templ)) for key, value in config[CONF_DATA].items(): templ = await cg.templatable(value, args, None) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 1cd1d6aa31..d93f8aed9a 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -2181,3 +2181,13 @@ SOURCE_SCHEMA = Any( } ), ) + + +def rename_key(old_key, new_key): + def validator(config: dict) -> dict: + config = config.copy() + if old_key in config: + config[new_key] = config.pop(old_key) + return config + + return validator diff --git a/esphome/const.py b/esphome/const.py index 37844e1047..39dd48d3f8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -37,8 +37,10 @@ CONF_ACCELERATION_Y = "acceleration_y" CONF_ACCELERATION_Z = "acceleration_z" CONF_ACCURACY = "accuracy" CONF_ACCURACY_DECIMALS = "accuracy_decimals" +CONF_ACTION = "action" CONF_ACTION_ID = "action_id" CONF_ACTION_STATE_TOPIC = "action_state_topic" +CONF_ACTIONS = "actions" CONF_ACTIVE = "active" CONF_ACTIVE_POWER = "active_power" CONF_ACTUAL_GAIN = "actual_gain" diff --git a/tests/components/api/common.yaml b/tests/components/api/common.yaml index e0b900f92d..6c2a333598 100644 --- a/tests/components/api/common.yaml +++ b/tests/components/api/common.yaml @@ -5,8 +5,8 @@ esphome: event: esphome.button_pressed data: message: Button was pressed - - homeassistant.service: - service: notify.html5 + - homeassistant.action: + action: notify.html5 data: message: Button was pressed - homeassistant.tag_scanned: pulse @@ -21,8 +21,8 @@ api: reboot_timeout: 0min encryption: key: bOFFzzvfpg5DB94DuBGLXD/hMnhpDKgP9UQyBulwWVU= - services: - - service: hello_world + actions: + - action: hello_world variables: name: string then: @@ -30,10 +30,10 @@ api: format: Hello World %s! args: - name.c_str() - - service: empty_service + - action: empty_action then: - - logger.log: Service Called - - service: all_types + - logger.log: Action Called + - action: all_types variables: bool_: bool int_: int @@ -41,7 +41,7 @@ api: string_: string then: - logger.log: Something happened - - service: array_types + - action: array_types variables: bool_arr: bool[] int_arr: int[] diff --git a/tests/components/homeassistant/common.yaml b/tests/components/homeassistant/common.yaml index ae016a3bea..07a6e8090c 100644 --- a/tests/components/homeassistant/common.yaml +++ b/tests/components/homeassistant/common.yaml @@ -13,12 +13,12 @@ esphome: message: The humidity is {{ my_variable }}%. variables: my_variable: "return id(ha_hello_world_temperature).state;" - - homeassistant.service: - service: notify.html5 + - homeassistant.action: + action: notify.html5 data: message: Button was pressed - - homeassistant.service: - service: notify.html5 + - homeassistant.action: + action: notify.html5 data: title: New Humidity data_template: From a5f18dfe7fda26d0311cfeb2fde42d0bf528f576 Mon Sep 17 00:00:00 2001 From: SimoPk Date: Thu, 1 Aug 2024 12:39:54 +0200 Subject: [PATCH 0124/1052] ade7953_spi wrong size specified in read_array call (#7172) --- esphome/components/ade7953_spi/ade7953_spi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ade7953_spi/ade7953_spi.cpp b/esphome/components/ade7953_spi/ade7953_spi.cpp index cfd5d71d0a..77a2a8adc7 100644 --- a/esphome/components/ade7953_spi/ade7953_spi.cpp +++ b/esphome/components/ade7953_spi/ade7953_spi.cpp @@ -60,7 +60,7 @@ bool AdE7953Spi::ade_read_16(uint16_t reg, uint16_t *value) { this->write_byte16(reg); this->transfer_byte(0x80); uint8_t recv[2]; - this->read_array(recv, 4); + this->read_array(recv, 2); *value = encode_uint16(recv[0], recv[1]); this->disable(); return false; From aedfb32482cf11feaa6373b73cc8ce8a2da50658 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 2 Aug 2024 10:01:21 +1200 Subject: [PATCH 0125/1052] Bump improv library to 1.2.4 (#7174) --- esphome/components/improv_base/__init__.py | 5 ++--- platformio.ini | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/improv_base/__init__.py b/esphome/components/improv_base/__init__.py index 5c2853a5c6..aa75f4d89c 100644 --- a/esphome/components/improv_base/__init__.py +++ b/esphome/components/improv_base/__init__.py @@ -1,8 +1,7 @@ import re -import esphome.config_validation as cv import esphome.codegen as cg - +import esphome.config_validation as cv from esphome.const import __version__ CODEOWNERS = ["@esphome/core"] @@ -39,4 +38,4 @@ def _process_next_url(url: str): async def setup_improv_core(var, config): if CONF_NEXT_URL in config: cg.add(var.set_next_url(_process_next_url(config[CONF_NEXT_URL]))) - cg.add_library("esphome/Improv", "1.2.3") + cg.add_library("improv/Improv", "1.2.4") diff --git a/platformio.ini b/platformio.ini index baf0a85d73..e4f363d650 100644 --- a/platformio.ini +++ b/platformio.ini @@ -35,7 +35,7 @@ build_flags = lib_deps = esphome/noise-c@0.1.4 ; api makuna/NeoPixelBus@2.7.3 ; neopixelbus - esphome/Improv@1.2.3 ; improv_serial / esp32_improv + improv/Improv@1.2.4 ; improv_serial / esp32_improv bblanchon/ArduinoJson@6.18.5 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code functionpointer/arduino-MLX90393@1.0.0 ; mlx90393 From 4a7570770b0b36cfc2495ad3795d4ae3c7df66bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ebbinghaus?= Date: Fri, 2 Aug 2024 01:58:59 +0200 Subject: [PATCH 0126/1052] Implement 'round to nearest multiple' filter (#7142) --- esphome/components/sensor/__init__.py | 19 +++++++++++++++++++ esphome/components/sensor/filter.cpp | 8 ++++++++ esphome/components/sensor/filter.h | 9 +++++++++ esphome/config_validation.py | 1 + esphome/const.py | 1 + 5 files changed, 38 insertions(+) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 262e69d75b..3b76466dec 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -17,6 +17,7 @@ from esphome.const import ( CONF_ICON, CONF_ID, CONF_IGNORE_OUT_OF_RANGE, + CONF_MULTIPLE, CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, @@ -249,6 +250,7 @@ CalibratePolynomialFilter = sensor_ns.class_("CalibratePolynomialFilter", Filter SensorInRangeCondition = sensor_ns.class_("SensorInRangeCondition", Filter) ClampFilter = sensor_ns.class_("ClampFilter", Filter) RoundFilter = sensor_ns.class_("RoundFilter", Filter) +RoundMultipleFilter = sensor_ns.class_("RoundMultipleFilter", Filter) validate_unit_of_measurement = cv.string_strict validate_accuracy_decimals = cv.int_ @@ -734,6 +736,23 @@ async def round_filter_to_code(config, filter_id): ) +@FILTER_REGISTRY.register( + "round_to_multiple_of", + RoundMultipleFilter, + cv.maybe_simple_value( + { + cv.Required(CONF_MULTIPLE): cv.positive_not_null_float, + }, + key=CONF_MULTIPLE, + ), +) +async def round_multiple_filter_to_code(config, filter_id): + return cg.new_Pvariable( + filter_id, + config[CONF_MULTIPLE], + ) + + async def build_filters(config): return await cg.build_registry_list(FILTER_REGISTRY, config) diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index eaa909429b..bcf1fc8269 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -472,5 +472,13 @@ optional RoundFilter::new_value(float value) { return value; } +RoundMultipleFilter::RoundMultipleFilter(float multiple) : multiple_(multiple) {} +optional RoundMultipleFilter::new_value(float value) { + if (std::isfinite(value)) { + return value - remainderf(value, this->multiple_); + } + return value; +} + } // namespace sensor } // namespace esphome diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index c13cb3420a..92b1d8d240 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -431,5 +431,14 @@ class RoundFilter : public Filter { uint8_t precision_; }; +class RoundMultipleFilter : public Filter { + public: + explicit RoundMultipleFilter(float multiple); + optional new_value(float value) override; + + protected: + float multiple_; +}; + } // namespace sensor } // namespace esphome diff --git a/esphome/config_validation.py b/esphome/config_validation.py index d93f8aed9a..6e1d3ba2f9 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -464,6 +464,7 @@ zero_to_one_float = float_range(min=0, max=1) negative_one_to_one_float = float_range(min=-1, max=1) positive_int = int_range(min=0) positive_not_null_int = int_range(min=0, min_included=False) +positive_not_null_float = float_range(min=0, min_included=False) def validate_id_name(value): diff --git a/esphome/const.py b/esphome/const.py index 39dd48d3f8..fcb630badd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -503,6 +503,7 @@ CONF_MOTION = "motion" CONF_MOVEMENT_COUNTER = "movement_counter" CONF_MQTT = "mqtt" CONF_MQTT_ID = "mqtt_id" +CONF_MULTIPLE = "multiple" CONF_MULTIPLEXER = "multiplexer" CONF_MULTIPLY = "multiply" CONF_NAME = "name" From 61c65811233eb1b678fcfa077d6370e7f07ba091 Mon Sep 17 00:00:00 2001 From: Olivier ARCHER Date: Sat, 3 Aug 2024 01:00:18 +0200 Subject: [PATCH 0127/1052] git ignore managed_components (#7180) --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 0c9a878400..79820249ac 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,5 @@ sdkconfig.* .tests/ /components +/managed_components + From 81ac9391d1af48bc02b4df9886954474c1fd7d01 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 5 Aug 2024 12:04:06 +1000 Subject: [PATCH 0128/1052] [core] Eliminate nuisance messages from `build_codeowners` (#7185) --- esphome/loader.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/loader.py b/esphome/loader.py index 9399c4cb31..d808805119 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -1,3 +1,4 @@ +from contextlib import AbstractContextManager from dataclasses import dataclass import importlib import importlib.abc @@ -7,7 +8,7 @@ import logging from pathlib import Path import sys from types import ModuleType -from typing import Any, Callable, ContextManager, Optional +from typing import Any, Callable, Optional from esphome.const import SOURCE_FILE_EXTENSIONS from esphome.core import CORE @@ -22,7 +23,7 @@ class FileResource: package: str resource: str - def path(self) -> ContextManager[Path]: + def path(self) -> AbstractContextManager[Path]: return importlib.resources.as_file( importlib.resources.files(self.package) / self.resource ) @@ -176,7 +177,7 @@ def _lookup_module(domain): module = importlib.import_module(f"esphome.components.{domain}") except ImportError as e: if "No module named" in str(e): - _LOGGER.error( + _LOGGER.info( "Unable to import component %s: %s", domain, str(e), exc_info=False ) else: From 38c25dec93b17ede8999f0cc6f295ba4a730ab6d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:15:19 +1200 Subject: [PATCH 0129/1052] [code-quality] More portable shebangs (#7189) Co-authored-by: Keith Burzinski --- docker/docker_entrypoint.sh | 2 +- script/devcontainer-post-create | 2 +- script/quicklint | 2 +- script/setup | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/docker_entrypoint.sh b/docker/docker_entrypoint.sh index 397b1528c5..1b9224244c 100755 --- a/docker/docker_entrypoint.sh +++ b/docker/docker_entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # If /cache is mounted, use that as PIO's coredir # otherwise use path in /config (so that PIO packages aren't downloaded on each compile) diff --git a/script/devcontainer-post-create b/script/devcontainer-post-create index 272d350519..2d376786ac 100755 --- a/script/devcontainer-post-create +++ b/script/devcontainer-post-create @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e # set -x diff --git a/script/quicklint b/script/quicklint index a4fae98195..84e4c97667 100755 --- a/script/quicklint +++ b/script/quicklint @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -e diff --git a/script/setup b/script/setup index aeb1b39bc1..824840c392 100755 --- a/script/setup +++ b/script/setup @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Set up ESPHome dev environment set -e From 87944f0c1b5df3019407bc20f0f0c29f8cf13033 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:58:20 +1200 Subject: [PATCH 0130/1052] Add support for doing update entity refresh/check via API. (#7190) --- esphome/components/api/api.proto | 7 +++++- esphome/components/api/api_connection.cpp | 12 +++++++++- esphome/components/api/api_pb2.cpp | 22 +++++++++++++++---- esphome/components/api/api_pb2.h | 7 +++++- .../http_request/update/http_request_update.h | 1 + esphome/components/update/update_entity.h | 2 +- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 812a1d74ae..b62fddf815 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1872,6 +1872,11 @@ message UpdateStateResponse { string release_summary = 9; string release_url = 10; } +enum UpdateCommand { + UPDATE_COMMAND_NONE = 0; + UPDATE_COMMAND_UPDATE = 1; + UPDATE_COMMAND_CHECK = 2; +} message UpdateCommandRequest { option (id) = 118; option (source) = SOURCE_CLIENT; @@ -1879,5 +1884,5 @@ message UpdateCommandRequest { option (no_delay) = true; fixed32 key = 1; - bool install = 2; + UpdateCommand command = 2; } diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 2e73a8336e..81fa4cb339 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1328,7 +1328,17 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) { if (update == nullptr) return; - update->perform(); + switch (msg.command) { + case enums::UPDATE_COMMAND_UPDATE: + update->perform(); + break; + case enums::UPDATE_COMMAND_CHECK: + update->check(); + break; + default: + ESP_LOGW(TAG, "Unknown update command: %d", msg.command); + break; + } } #endif diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index e6e905c6d1..a57627a66c 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -567,6 +567,20 @@ template<> const char *proto_enum_to_string(enums::ValveO } } #endif +#ifdef HAS_PROTO_MESSAGE_DUMP +template<> const char *proto_enum_to_string(enums::UpdateCommand value) { + switch (value) { + case enums::UPDATE_COMMAND_NONE: + return "UPDATE_COMMAND_NONE"; + case enums::UPDATE_COMMAND_UPDATE: + return "UPDATE_COMMAND_UPDATE"; + case enums::UPDATE_COMMAND_CHECK: + return "UPDATE_COMMAND_CHECK"; + default: + return "UNKNOWN"; + } +} +#endif bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { @@ -8596,7 +8610,7 @@ void UpdateStateResponse::dump_to(std::string &out) const { bool UpdateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 2: { - this->install = value.as_bool(); + this->command = value.as_enum(); return true; } default: @@ -8615,7 +8629,7 @@ bool UpdateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { } void UpdateCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); - buffer.encode_bool(2, this->install); + buffer.encode_enum(2, this->command); } #ifdef HAS_PROTO_MESSAGE_DUMP void UpdateCommandRequest::dump_to(std::string &out) const { @@ -8626,8 +8640,8 @@ void UpdateCommandRequest::dump_to(std::string &out) const { out.append(buffer); out.append("\n"); - out.append(" install: "); - out.append(YESNO(this->install)); + out.append(" command: "); + out.append(proto_enum_to_string(this->command)); out.append("\n"); out.append("}"); } diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index ef051eecf1..bb5263cffa 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -227,6 +227,11 @@ enum ValveOperation : uint32_t { VALVE_OPERATION_IS_OPENING = 1, VALVE_OPERATION_IS_CLOSING = 2, }; +enum UpdateCommand : uint32_t { + UPDATE_COMMAND_NONE = 0, + UPDATE_COMMAND_UPDATE = 1, + UPDATE_COMMAND_CHECK = 2, +}; } // namespace enums @@ -2175,7 +2180,7 @@ class UpdateStateResponse : public ProtoMessage { class UpdateCommandRequest : public ProtoMessage { public: uint32_t key{0}; - bool install{false}; + enums::UpdateCommand command{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/http_request/update/http_request_update.h b/esphome/components/http_request/update/http_request_update.h index 943231a906..45c7e6a447 100644 --- a/esphome/components/http_request/update/http_request_update.h +++ b/esphome/components/http_request/update/http_request_update.h @@ -16,6 +16,7 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent { void update() override; void perform(bool force) override; + void check() override { this->update(); } void set_source_url(const std::string &source_url) { this->source_url_ = source_url; } diff --git a/esphome/components/update/update_entity.h b/esphome/components/update/update_entity.h index 568fbe3bb0..cc269e288f 100644 --- a/esphome/components/update/update_entity.h +++ b/esphome/components/update/update_entity.h @@ -33,8 +33,8 @@ class UpdateEntity : public EntityBase, public EntityBase_DeviceClass { void publish_state(); void perform() { this->perform(false); } - virtual void perform(bool force) = 0; + virtual void check() = 0; const UpdateInfo &update_info = update_info_; const UpdateState &state = state_; From d18bb34f87758cff3d1a6d881b1b167d1d6f79a1 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:07:05 +1000 Subject: [PATCH 0131/1052] [lvgl] Stage 4 (#7166) --- esphome/components/lvgl/__init__.py | 110 +++++-- esphome/components/lvgl/animimg.py | 117 +++++++ esphome/components/lvgl/arc.py | 78 +++++ esphome/components/lvgl/automation.py | 186 ++++++----- esphome/components/lvgl/btn.py | 11 +- esphome/components/lvgl/checkbox.py | 25 ++ esphome/components/lvgl/defines.py | 64 ++-- esphome/components/lvgl/helpers.py | 20 -- esphome/components/lvgl/img.py | 85 +++++ esphome/components/lvgl/label.py | 9 +- esphome/components/lvgl/led.py | 29 ++ esphome/components/lvgl/line.py | 51 +++ esphome/components/lvgl/lv_bar.py | 53 ++++ esphome/components/lvgl/lv_switch.py | 20 ++ esphome/components/lvgl/lv_validation.py | 60 +++- esphome/components/lvgl/lvcode.py | 236 +++++++++----- esphome/components/lvgl/lvgl_esphome.cpp | 202 ++++++++++++ esphome/components/lvgl/lvgl_esphome.h | 164 ++++------ esphome/components/lvgl/page.py | 113 +++++++ esphome/components/lvgl/rotary_encoders.py | 3 +- esphome/components/lvgl/schemas.py | 105 ++++++- esphome/components/lvgl/slider.py | 63 ++++ esphome/components/lvgl/spinner.py | 43 +++ esphome/components/lvgl/styles.py | 58 ++++ esphome/components/lvgl/trigger.py | 22 +- esphome/components/lvgl/types.py | 92 ++++-- esphome/components/lvgl/widget.py | 219 +++++++++---- tests/components/lvgl/lvgl-package.yaml | 343 +++++++++++++-------- 28 files changed, 2002 insertions(+), 579 deletions(-) create mode 100644 esphome/components/lvgl/animimg.py create mode 100644 esphome/components/lvgl/arc.py create mode 100644 esphome/components/lvgl/checkbox.py create mode 100644 esphome/components/lvgl/img.py create mode 100644 esphome/components/lvgl/led.py create mode 100644 esphome/components/lvgl/line.py create mode 100644 esphome/components/lvgl/lv_bar.py create mode 100644 esphome/components/lvgl/lv_switch.py create mode 100644 esphome/components/lvgl/page.py create mode 100644 esphome/components/lvgl/slider.py create mode 100644 esphome/components/lvgl/spinner.py create mode 100644 esphome/components/lvgl/styles.py diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 182d04e038..c154689199 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -15,44 +15,91 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_TYPE, ) -from esphome.core import CORE, ID, Lambda +from esphome.core import CORE, ID from esphome.cpp_generator import MockObj from esphome.final_validate import full_config from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid -from .automation import update_to_code +from .animimg import animimg_spec +from .arc import arc_spec +from .automation import disp_update, update_to_code from .btn import btn_spec +from .checkbox import checkbox_spec +from .defines import CONF_SKIP +from .img import img_spec from .label import label_spec -from .lv_validation import lv_images_used -from .lvcode import LvContext +from .led import led_spec +from .line import line_spec +from .lv_bar import bar_spec +from .lv_switch import switch_spec +from .lv_validation import lv_bool, lv_images_used +from .lvcode import LvContext, LvglComponent from .obj import obj_spec +from .page import add_pages, page_spec from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code -from .schemas import any_widget_schema, create_modify_schema, obj_schema +from .schemas import ( + DISP_BG_SCHEMA, + FLEX_OBJ_SCHEMA, + GRID_CELL_SCHEMA, + LAYOUT_SCHEMAS, + STYLE_SCHEMA, + WIDGET_TYPES, + any_widget_schema, + container_schema, + create_modify_schema, + grid_alignments, + obj_schema, +) +from .slider import slider_spec +from .spinner import spinner_spec +from .styles import add_top_layer, styles_to_code, theme_to_code from .touchscreens import touchscreen_schema, touchscreens_to_code from .trigger import generate_triggers from .types import ( - WIDGET_TYPES, FontEngine, IdleTrigger, - LvglComponent, ObjUpdateAction, lv_font_t, + lv_style_t, lvgl_ns, ) from .widget import Widget, add_widgets, lv_scr_act, set_obj_properties DOMAIN = "lvgl" -DEPENDENCIES = ("display",) -AUTO_LOAD = ("key_provider",) -CODEOWNERS = ("@clydebarrow",) +DEPENDENCIES = ["display"] +AUTO_LOAD = ["key_provider"] +CODEOWNERS = ["@clydebarrow"] LOGGER = logging.getLogger(__name__) -for w_type in (label_spec, obj_spec, btn_spec): +for w_type in ( + label_spec, + obj_spec, + btn_spec, + bar_spec, + slider_spec, + arc_spec, + line_spec, + spinner_spec, + led_spec, + animimg_spec, + checkbox_spec, + img_spec, + switch_spec, +): WIDGET_TYPES[w_type.name] = w_type WIDGET_SCHEMA = any_widget_schema() +LAYOUT_SCHEMAS[df.TYPE_GRID] = { + cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema(GRID_CELL_SCHEMA)) +} +LAYOUT_SCHEMAS[df.TYPE_FLEX] = { + cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema(FLEX_OBJ_SCHEMA)) +} +LAYOUT_SCHEMAS[df.TYPE_NONE] = { + cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema()) +} for w_type in WIDGET_TYPES.values(): register_action( f"lvgl.{w_type.name}.update", @@ -61,14 +108,6 @@ for w_type in WIDGET_TYPES.values(): )(update_to_code) -async def add_init_lambda(lv_component, init): - if init: - lamb = await cg.process_lambda( - Lambda(init), [(LvglComponent.operator("ptr"), "lv_component")] - ) - cg.add(lv_component.add_init_lambda(lamb)) - - lv_defines = {} # Dict of #defines to provide as build flags @@ -100,6 +139,9 @@ def generate_lv_conf_h(): def final_validation(config): + if pages := config.get(CONF_PAGES): + if all(p[CONF_SKIP] for p in pages): + raise cv.Invalid("At least one page must not be skipped") global_config = full_config.get() for display_id in config[df.CONF_DISPLAYS]: path = global_config.get_path_for_id(display_id)[:-1] @@ -193,18 +235,23 @@ async def to_code(config): else: add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font)) - with LvContext(): + async with LvContext(lv_component): await touchscreens_to_code(lv_component, config) await rotary_encoders_to_code(lv_component, config) + await theme_to_code(config) + await styles_to_code(config) await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) + await add_pages(lv_component, config) + await add_top_layer(config) + await disp_update(f"{lv_component}->get_disp()", config) Widget.set_completed() await generate_triggers(lv_component) for conf in config.get(CONF_ON_IDLE, ()): templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ) await build_automation(idle_trigger, [], conf) - await add_init_lambda(lv_component, LvContext.get_code()) + for comp in helpers.lvgl_components_required: CORE.add_define(f"USE_LVGL_{comp.upper()}") for use in helpers.lv_uses: @@ -239,6 +286,16 @@ CONFIG_SCHEMA = ( cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of( "big_endian", "little_endian" ), + cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list( + cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)}) + .extend(STYLE_SCHEMA) + .extend( + { + cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, + cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments, + } + ) + ), cv.Optional(CONF_ON_IDLE): validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(IdleTrigger), @@ -247,10 +304,19 @@ CONFIG_SCHEMA = ( ), } ), - cv.Optional(df.CONF_WIDGETS): cv.ensure_list(WIDGET_SCHEMA), + cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list(WIDGET_SCHEMA), + cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list( + container_schema(page_spec) + ), + cv.Optional(df.CONF_PAGE_WRAP, default=True): lv_bool, + cv.Optional(df.CONF_TOP_LAYER): container_schema(obj_spec), cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color, + cv.Optional(df.CONF_THEME): cv.Schema( + {cv.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()} + ), cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema, cv.GenerateID(df.CONF_ROTARY_ENCODERS): ROTARY_ENCODER_CONFIG, } ) + .extend(DISP_BG_SCHEMA) ).add_extra(cv.has_at_least_one_key(CONF_PAGES, df.CONF_WIDGETS)) diff --git a/esphome/components/lvgl/animimg.py b/esphome/components/lvgl/animimg.py new file mode 100644 index 0000000000..20b85b019c --- /dev/null +++ b/esphome/components/lvgl/animimg.py @@ -0,0 +1,117 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_DURATION, CONF_ID + +from ...cpp_generator import MockObj +from .automation import action_to_code +from .defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC +from .helpers import lvgl_components_required +from .img import CONF_IMAGE +from .label import CONF_LABEL +from .lv_validation import lv_image, lv_milliseconds +from .lvcode import lv, lv_expr +from .types import LvType, ObjUpdateAction, void_ptr +from .widget import Widget, WidgetType, get_widgets + +CONF_ANIMIMG = "animimg" +CONF_SRC_LIST_ID = "src_list_id" + + +def lv_repeat_count(value): + if isinstance(value, str) and value.lower() in ("forever", "infinite"): + value = 0xFFFF + return cv.int_range(min=0, max=0xFFFF)(value) + + +ANIMIMG_BASE_SCHEMA = cv.Schema( + { + cv.Optional(CONF_REPEAT_COUNT, default="forever"): lv_repeat_count, + cv.Optional(CONF_AUTO_START, default=True): cv.boolean, + } +) +ANIMIMG_SCHEMA = ANIMIMG_BASE_SCHEMA.extend( + { + cv.Required(CONF_DURATION): lv_milliseconds, + cv.Required(CONF_SRC): cv.ensure_list(lv_image), + cv.GenerateID(CONF_SRC_LIST_ID): cv.declare_id(void_ptr), + } +) + +ANIMIMG_MODIFY_SCHEMA = ANIMIMG_BASE_SCHEMA.extend( + { + cv.Optional(CONF_DURATION): lv_milliseconds, + } +) + +lv_animimg_t = LvType("lv_animimg_t") + + +class AnimimgType(WidgetType): + def __init__(self): + super().__init__( + CONF_ANIMIMG, + lv_animimg_t, + (CONF_MAIN,), + ANIMIMG_SCHEMA, + ANIMIMG_MODIFY_SCHEMA, + ) + + async def to_code(self, w: Widget, config): + lvgl_components_required.add(CONF_IMAGE) + lvgl_components_required.add(CONF_ANIMIMG) + if CONF_SRC in config: + for x in config[CONF_SRC]: + await cg.get_variable(x) + srcs = [lv_expr.img_from(MockObj(x)) for x in config[CONF_SRC]] + src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs) + count = len(config[CONF_SRC]) + lv.animimg_set_src(w.obj, src_id, count) + lv.animimg_set_repeat_count(w.obj, config[CONF_REPEAT_COUNT]) + lv.animimg_set_duration(w.obj, config[CONF_DURATION]) + if config.get(CONF_AUTO_START): + lv.animimg_start(w.obj) + + def get_uses(self): + return CONF_IMAGE, CONF_LABEL + + +animimg_spec = AnimimgType() + + +@automation.register_action( + "lvgl.animimg.start", + ObjUpdateAction, + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(lv_animimg_t), + }, + key=CONF_ID, + ), +) +async def animimg_start(config, action_id, template_arg, args): + widget = await get_widgets(config) + + async def do_start(w: Widget): + lv.animimg_start(w.obj) + + return await action_to_code(widget, do_start, action_id, template_arg, args) + + +@automation.register_action( + "lvgl.animimg.stop", + ObjUpdateAction, + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(lv_animimg_t), + }, + key=CONF_ID, + ), +) +async def animimg_stop(config, action_id, template_arg, args): + widget = await get_widgets(config) + + async def do_stop(w: Widget): + lv.animimg_stop(w.obj) + + return await action_to_code(widget, do_stop, action_id, template_arg, args) diff --git a/esphome/components/lvgl/arc.py b/esphome/components/lvgl/arc.py new file mode 100644 index 0000000000..d036464c7a --- /dev/null +++ b/esphome/components/lvgl/arc.py @@ -0,0 +1,78 @@ +import esphome.config_validation as cv +from esphome.const import ( + CONF_MAX_VALUE, + CONF_MIN_VALUE, + CONF_MODE, + CONF_ROTATION, + CONF_VALUE, +) +from esphome.cpp_types import nullptr + +from .defines import ( + ARC_MODES, + CONF_ADJUSTABLE, + CONF_CHANGE_RATE, + CONF_END_ANGLE, + CONF_INDICATOR, + CONF_KNOB, + CONF_MAIN, + CONF_START_ANGLE, + literal, +) +from .lv_validation import angle, get_start_value, lv_float +from .lvcode import lv, lv_obj +from .types import LvNumber, NumberType +from .widget import Widget + +CONF_ARC = "arc" +ARC_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_MIN_VALUE, default=0): cv.int_, + cv.Optional(CONF_MAX_VALUE, default=100): cv.int_, + cv.Optional(CONF_START_ANGLE, default=135): angle, + cv.Optional(CONF_END_ANGLE, default=45): angle, + cv.Optional(CONF_ROTATION, default=0.0): angle, + cv.Optional(CONF_ADJUSTABLE, default=False): bool, + cv.Optional(CONF_MODE, default="NORMAL"): ARC_MODES.one_of, + cv.Optional(CONF_CHANGE_RATE, default=720): cv.uint16_t, + } +) + +ARC_MODIFY_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VALUE): lv_float, + } +) + + +class ArcType(NumberType): + def __init__(self): + super().__init__( + CONF_ARC, + LvNumber("lv_arc_t"), + parts=(CONF_MAIN, CONF_INDICATOR, CONF_KNOB), + schema=ARC_SCHEMA, + modify_schema=ARC_MODIFY_SCHEMA, + ) + + async def to_code(self, w: Widget, config): + if CONF_MIN_VALUE in config: + lv.arc_set_range(w.obj, config[CONF_MIN_VALUE], config[CONF_MAX_VALUE]) + lv.arc_set_bg_angles( + w.obj, config[CONF_START_ANGLE] // 10, config[CONF_END_ANGLE] // 10 + ) + lv.arc_set_rotation(w.obj, config[CONF_ROTATION] // 10) + lv.arc_set_mode(w.obj, literal(config[CONF_MODE])) + lv.arc_set_change_rate(w.obj, config[CONF_CHANGE_RATE]) + + if config.get(CONF_ADJUSTABLE) is False: + lv_obj.remove_style(w.obj, nullptr, literal("LV_PART_KNOB")) + w.clear_flag("LV_OBJ_FLAG_CLICKABLE") + + value = await get_start_value(config) + if value is not None: + lv.arc_set_value(w.obj, value) + + +arc_spec = ArcType() diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 4fd0be185e..ffa25783ad 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -1,15 +1,26 @@ +from collections.abc import Awaitable +from typing import Callable + from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_TIMEOUT -from esphome.core import Lambda -from esphome.cpp_generator import RawStatement from esphome.cpp_types import nullptr -from .defines import CONF_LVGL_ID, CONF_SHOW_SNOW, literal -from .lv_validation import lv_bool +from .defines import ( + CONF_DISP_BG_COLOR, + CONF_DISP_BG_IMAGE, + CONF_LVGL_ID, + CONF_SHOW_SNOW, + literal, +) +from .lv_validation import lv_bool, lv_color, lv_image from .lvcode import ( + LVGL_COMP_ARG, LambdaContext, + LocalVariable, + LvConditional, + LvglComponent, ReturnStatement, add_line_marks, lv, @@ -17,46 +28,46 @@ from .lvcode import ( lv_obj, lvgl_comp, ) -from .schemas import ACTION_SCHEMA, LVGL_SCHEMA +from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA from .types import ( + LV_EVENT, + LV_STATE, LvglAction, - LvglComponent, - LvglComponentPtr, LvglCondition, ObjUpdateAction, + lv_disp_t, lv_obj_t, ) -from .widget import Widget, get_widget, lv_scr_act, set_obj_properties +from .widget import Widget, get_widgets, lv_scr_act, set_obj_properties -async def action_to_code(action: list, action_id, widget: Widget, template_arg, args): - with LambdaContext() as context: - lv.cond_if(widget.obj == nullptr) - lv_add(RawStatement(" return;")) - lv.cond_endif() - code = context.get_code() - code.extend(action) - action = "\n".join(code) + "\n\n" - lamb = await cg.process_lambda(Lambda(action), args) - var = cg.new_Pvariable(action_id, template_arg, lamb) +async def action_to_code( + widgets: list[Widget], + action: Callable[[Widget], Awaitable[None]], + action_id, + template_arg, + args, +): + async with LambdaContext(parameters=args, where=action_id) as context: + for widget in widgets: + with LvConditional(widget.obj != nullptr): + await action(widget) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) return var async def update_to_code(config, action_id, template_arg, args): - if config is not None: - widget = await get_widget(config) - with LambdaContext() as context: - add_line_marks(action_id) - await set_obj_properties(widget, config) - await widget.type.to_code(widget, config) - if ( - widget.type.w_type.value_property is not None - and widget.type.w_type.value_property in config - ): - lv.event_send(widget.obj, literal("LV_EVENT_VALUE_CHANGED"), nullptr) - return await action_to_code( - context.get_code(), action_id, widget, template_arg, args - ) + async def do_update(widget: Widget): + await set_obj_properties(widget, config) + await widget.type.to_code(widget, config) + if ( + widget.type.w_type.value_property is not None + and widget.type.w_type.value_property in config + ): + lv.event_send(widget.obj, LV_EVENT.VALUE_CHANGED, nullptr) + + widgets = await get_widgets(config[CONF_ID]) + return await action_to_code(widgets, do_update, action_id, template_arg, args) @automation.register_condition( @@ -66,9 +77,7 @@ async def update_to_code(config, action_id, template_arg, args): ) async def lvgl_is_paused(config, condition_id, template_arg, args): lvgl = config[CONF_LVGL_ID] - with LambdaContext( - [(LvglComponentPtr, "lvgl_comp")], return_type=cg.bool_ - ) as context: + async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context: lv_add(ReturnStatement(lvgl_comp.is_paused())) var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda()) await cg.register_parented(var, lvgl) @@ -89,15 +98,23 @@ async def lvgl_is_paused(config, condition_id, template_arg, args): 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) - with LambdaContext( - [(LvglComponentPtr, "lvgl_comp")], return_type=cg.bool_ - ) as context: + async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context: lv_add(ReturnStatement(lvgl_comp.is_idle(timeout))) var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda()) await cg.register_parented(var, lvgl) return var +async def disp_update(disp, config: dict): + if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config: + return + with LocalVariable("lv_disp_tmp", lv_disp_t, literal(disp)) as disp_temp: + if bg_color := config.get(CONF_DISP_BG_COLOR): + lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color)) + if bg_image := config.get(CONF_DISP_BG_IMAGE): + lv.disp_set_bg_image(disp_temp, await lv_image.process(bg_image)) + + @automation.register_action( "lvgl.widget.redraw", ObjUpdateAction, @@ -109,14 +126,32 @@ async def lvgl_is_idle(config, condition_id, template_arg, args): ), ) async def obj_invalidate_to_code(config, action_id, template_arg, args): - if CONF_ID in config: - w = await get_widget(config) - else: - w = lv_scr_act - with LambdaContext() as context: - add_line_marks(action_id) - lv_obj.invalidate(w.obj) - return await action_to_code(context.get_code(), action_id, w, template_arg, args) + widgets = await get_widgets(config) or [lv_scr_act] + + async def do_invalidate(widget: Widget): + lv_obj.invalidate(widget.obj) + + return await action_to_code(widgets, do_invalidate, action_id, template_arg, args) + + +@automation.register_action( + "lvgl.update", + LvglAction, + DISP_BG_SCHEMA.extend( + { + cv.GenerateID(): cv.use_id(LvglComponent), + } + ).add_extra(cv.has_at_least_one_key(CONF_DISP_BG_COLOR, CONF_DISP_BG_IMAGE)), +) +async def lvgl_update_to_code(config, action_id, template_arg, args): + widgets = await get_widgets(config) + w = widgets[0] + disp = f"{w.obj}->get_disp()" + async with LambdaContext(parameters=args, where=action_id) as context: + await disp_update(disp, config) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, w.var) + return var @automation.register_action( @@ -128,8 +163,8 @@ async def obj_invalidate_to_code(config, action_id, template_arg, args): }, ) async def pause_action_to_code(config, action_id, template_arg, args): - with LambdaContext([(LvglComponentPtr, "lvgl_comp")]) as context: - add_line_marks(action_id) + async with LambdaContext(LVGL_COMP_ARG) as context: + add_line_marks(where=action_id) lv_add(lvgl_comp.set_paused(True, config[CONF_SHOW_SNOW])) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) await cg.register_parented(var, config[CONF_ID]) @@ -144,45 +179,48 @@ async def pause_action_to_code(config, action_id, template_arg, args): }, ) async def resume_action_to_code(config, action_id, template_arg, args): - with LambdaContext([(LvglComponentPtr, "lvgl_comp")]) as context: - add_line_marks(action_id) + async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context: lv_add(lvgl_comp.set_paused(False, False)) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) await cg.register_parented(var, config[CONF_ID]) return var -@automation.register_action("lvgl.widget.disable", ObjUpdateAction, ACTION_SCHEMA) +@automation.register_action("lvgl.widget.disable", ObjUpdateAction, LIST_ACTION_SCHEMA) async def obj_disable_to_code(config, action_id, template_arg, args): - w = await get_widget(config) - with LambdaContext() as context: - add_line_marks(action_id) - w.add_state("LV_STATE_DISABLED") - return await action_to_code(context.get_code(), action_id, w, template_arg, args) + async def do_disable(widget: Widget): + widget.add_state(LV_STATE.DISABLED) + + return await action_to_code( + await get_widgets(config), do_disable, action_id, template_arg, args + ) -@automation.register_action("lvgl.widget.enable", ObjUpdateAction, ACTION_SCHEMA) +@automation.register_action("lvgl.widget.enable", ObjUpdateAction, LIST_ACTION_SCHEMA) async def obj_enable_to_code(config, action_id, template_arg, args): - w = await get_widget(config) - with LambdaContext() as context: - add_line_marks(action_id) - w.clear_state("LV_STATE_DISABLED") - return await action_to_code(context.get_code(), action_id, w, template_arg, args) + async def do_enable(widget: Widget): + widget.clear_state(LV_STATE.DISABLED) + + return await action_to_code( + await get_widgets(config), do_enable, action_id, template_arg, args + ) -@automation.register_action("lvgl.widget.hide", ObjUpdateAction, ACTION_SCHEMA) +@automation.register_action("lvgl.widget.hide", ObjUpdateAction, LIST_ACTION_SCHEMA) async def obj_hide_to_code(config, action_id, template_arg, args): - w = await get_widget(config) - with LambdaContext() as context: - add_line_marks(action_id) - w.add_flag("LV_OBJ_FLAG_HIDDEN") - return await action_to_code(context.get_code(), action_id, w, template_arg, args) + async def do_hide(widget: Widget): + widget.add_flag("LV_OBJ_FLAG_HIDDEN") + + return await action_to_code( + await get_widgets(config), do_hide, action_id, template_arg, args + ) -@automation.register_action("lvgl.widget.show", ObjUpdateAction, ACTION_SCHEMA) +@automation.register_action("lvgl.widget.show", ObjUpdateAction, LIST_ACTION_SCHEMA) async def obj_show_to_code(config, action_id, template_arg, args): - w = await get_widget(config) - with LambdaContext() as context: - add_line_marks(action_id) - w.clear_flag("LV_OBJ_FLAG_HIDDEN") - return await action_to_code(context.get_code(), action_id, w, template_arg, args) + async def do_show(widget: Widget): + widget.clear_flag("LV_OBJ_FLAG_HIDDEN") + + return await action_to_code( + await get_widgets(config), do_show, action_id, template_arg, args + ) diff --git a/esphome/components/lvgl/btn.py b/esphome/components/lvgl/btn.py index 064d886d47..2a2a53e1e2 100644 --- a/esphome/components/lvgl/btn.py +++ b/esphome/components/lvgl/btn.py @@ -1,19 +1,14 @@ from esphome.const import CONF_BUTTON -from esphome.cpp_generator import MockObjClass from .defines import CONF_MAIN from .types import LvBoolean, WidgetType +lv_btn_t = LvBoolean("lv_btn_t") + class BtnType(WidgetType): def __init__(self): - super().__init__(CONF_BUTTON, LvBoolean("lv_btn_t"), (CONF_MAIN,)) - - def obj_creator(self, parent: MockObjClass, config: dict): - """ - LVGL 8 calls buttons `btn` - """ - return f"lv_btn_create({parent})" + super().__init__(CONF_BUTTON, lv_btn_t, (CONF_MAIN,), lv_name="btn") def get_uses(self): return ("btn",) diff --git a/esphome/components/lvgl/checkbox.py b/esphome/components/lvgl/checkbox.py new file mode 100644 index 0000000000..7418d633cf --- /dev/null +++ b/esphome/components/lvgl/checkbox.py @@ -0,0 +1,25 @@ +from .defines import CONF_INDICATOR, CONF_MAIN, CONF_TEXT +from .lv_validation import lv_text +from .lvcode import lv +from .schemas import TEXT_SCHEMA +from .types import LvBoolean +from .widget import Widget, WidgetType + +CONF_CHECKBOX = "checkbox" + + +class CheckboxType(WidgetType): + def __init__(self): + super().__init__( + CONF_CHECKBOX, + LvBoolean("lv_checkbox_t"), + (CONF_MAIN, CONF_INDICATOR), + TEXT_SCHEMA, + ) + + async def to_code(self, w: Widget, config): + if value := config.get(CONF_TEXT): + lv.checkbox_set_text(w.obj, await lv_text.process(value)) + + +checkbox_spec = CheckboxType() diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 9f349e3943..16ec45ae8a 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -4,31 +4,20 @@ Constants already defined in esphome.const are not duplicated here and must be i """ -from typing import Union - from esphome import codegen as cg, config_validation as cv from esphome.core import ID, Lambda -from esphome.cpp_generator import Literal +from esphome.cpp_generator import MockObj from esphome.cpp_types import uint32 from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from .helpers import requires_component - -class ConstantLiteral(Literal): - __slots__ = ("constant",) - - def __init__(self, constant: str): - super().__init__() - self.constant = constant - - def __str__(self): - return self.constant +lvgl_ns = cg.esphome_ns.namespace("lvgl") -def literal(arg: Union[str, ConstantLiteral]): +def literal(arg): if isinstance(arg, str): - return ConstantLiteral(arg) + return MockObj(arg) return arg @@ -93,15 +82,23 @@ class LvConstant(LValidator): return self.prefix + cv.one_of(*choices, upper=True)(value) super().__init__(validator, rtype=uint32) + self.retmapper = self.mapper self.one_of = LValidator(validator, uint32, retmapper=self.mapper) self.several_of = LValidator( cv.ensure_list(self.one_of), uint32, retmapper=self.mapper ) def mapper(self, value, args=()): - if isinstance(value, list): - value = "|".join(value) - return ConstantLiteral(value) + if not isinstance(value, list): + value = [value] + return literal( + "|".join( + [ + str(v) if str(v).startswith(self.prefix) else self.prefix + str(v) + for v in value + ] + ).upper() + ) def extend(self, *choices): """ @@ -112,9 +109,6 @@ class LvConstant(LValidator): return LvConstant(self.prefix, *(self.choices + choices)) -# Widgets -CONF_LABEL = "label" - # Parts CONF_MAIN = "main" CONF_SCROLLBAR = "scrollbar" @@ -123,10 +117,15 @@ CONF_KNOB = "knob" CONF_SELECTED = "selected" CONF_ITEMS = "items" CONF_TICKS = "ticks" -CONF_TICK_STYLE = "tick_style" CONF_CURSOR = "cursor" CONF_TEXTAREA_PLACEHOLDER = "textarea_placeholder" +# Layout types + +TYPE_FLEX = "flex" +TYPE_GRID = "grid" +TYPE_NONE = "none" + LV_FONTS = list(f"montserrat_{s}" for s in range(8, 50, 2)) + [ "dejavu_16_persian_hebrew", "simsun_16_cjk", @@ -134,7 +133,7 @@ LV_FONTS = list(f"montserrat_{s}" for s in range(8, 50, 2)) + [ "unscii_16", ] -LV_EVENT = { +LV_EVENT_MAP = { "PRESS": "PRESSED", "SHORT_CLICK": "SHORT_CLICKED", "LONG_PRESS": "LONG_PRESSED", @@ -150,7 +149,7 @@ LV_EVENT = { "CANCEL": "CANCEL", } -LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT) +LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP) LV_ANIM = LvConstant( @@ -305,7 +304,8 @@ OBJ_FLAGS = ( ARC_MODES = LvConstant("LV_ARC_MODE_", "NORMAL", "REVERSE", "SYMMETRICAL") BAR_MODES = LvConstant("LV_BAR_MODE_", "NORMAL", "SYMMETRICAL", "RANGE") -BTNMATRIX_CTRLS = ( +BTNMATRIX_CTRLS = LvConstant( + "LV_BTNMATRIX_CTRL_", "HIDDEN", "NO_REPEAT", "DISABLED", @@ -366,7 +366,6 @@ CONF_ACCEPTED_CHARS = "accepted_chars" CONF_ADJUSTABLE = "adjustable" CONF_ALIGN = "align" CONF_ALIGN_TO = "align_to" -CONF_ANGLE_RANGE = "angle_range" CONF_ANIMATED = "animated" CONF_ANIMATION = "animation" CONF_ANTIALIAS = "antialias" @@ -384,8 +383,6 @@ CONF_BYTE_ORDER = "byte_order" CONF_CHANGE_RATE = "change_rate" CONF_CLOSE_BUTTON = "close_button" CONF_COLOR_DEPTH = "color_depth" -CONF_COLOR_END = "color_end" -CONF_COLOR_START = "color_start" CONF_CONTROL = "control" CONF_DEFAULT = "default" CONF_DEFAULT_FONT = "default_font" @@ -414,9 +411,7 @@ CONF_GRID_ROW_ALIGN = "grid_row_align" CONF_GRID_ROWS = "grid_rows" CONF_HEADER_MODE = "header_mode" CONF_HOME = "home" -CONF_INDICATORS = "indicators" CONF_KEY_CODE = "key_code" -CONF_LABEL_GAP = "label_gap" CONF_LAYOUT = "layout" CONF_LEFT_BUTTON = "left_button" CONF_LINE_WIDTH = "line_width" @@ -425,7 +420,6 @@ CONF_LONG_PRESS_TIME = "long_press_time" CONF_LONG_PRESS_REPEAT_TIME = "long_press_repeat_time" CONF_LVGL_ID = "lvgl_id" CONF_LONG_MODE = "long_mode" -CONF_MAJOR = "major" CONF_MSGBOXES = "msgboxes" CONF_OBJ = "obj" CONF_OFFSET_X = "offset_x" @@ -434,6 +428,7 @@ CONF_ONE_LINE = "one_line" CONF_ON_SELECT = "on_select" CONF_ONE_CHECKED = "one_checked" CONF_NEXT = "next" +CONF_PAGE = "page" CONF_PAGE_WRAP = "page_wrap" CONF_PASSWORD_MODE = "password_mode" CONF_PIVOT_X = "pivot_x" @@ -442,14 +437,12 @@ CONF_PLACEHOLDER_TEXT = "placeholder_text" CONF_POINTS = "points" CONF_PREVIOUS = "previous" CONF_REPEAT_COUNT = "repeat_count" -CONF_R_MOD = "r_mod" CONF_RECOLOR = "recolor" CONF_RIGHT_BUTTON = "right_button" CONF_ROLLOVER = "rollover" CONF_ROOT_BACK_BTN = "root_back_btn" CONF_ROTARY_ENCODERS = "rotary_encoders" CONF_ROWS = "rows" -CONF_SCALES = "scales" CONF_SCALE_LINES = "scale_lines" CONF_SCROLLBAR_MODE = "scrollbar_mode" CONF_SELECTED_INDEX = "selected_index" @@ -459,8 +452,9 @@ CONF_SRC = "src" CONF_START_ANGLE = "start_angle" CONF_START_VALUE = "start_value" CONF_STATES = "states" -CONF_STRIDE = "stride" CONF_STYLE = "style" +CONF_STYLES = "styles" +CONF_STYLE_DEFINITIONS = "style_definitions" CONF_STYLE_ID = "style_id" CONF_SKIP = "skip" CONF_SYMBOL = "symbol" @@ -505,4 +499,4 @@ DEFAULT_ESPHOME_FONT = "esphome_lv_default_font" def join_enums(enums, prefix=""): - return ConstantLiteral("|".join(f"(int){prefix}{e.upper()}" for e in enums)) + return literal("|".join(f"(int){prefix}{e.upper()}" for e in enums)) diff --git a/esphome/components/lvgl/helpers.py b/esphome/components/lvgl/helpers.py index d67739155c..e04a0105d5 100644 --- a/esphome/components/lvgl/helpers.py +++ b/esphome/components/lvgl/helpers.py @@ -1,10 +1,7 @@ import re from esphome import config_validation as cv -from esphome.config import Config from esphome.const import CONF_ARGS, CONF_FORMAT -from esphome.core import CORE, ID -from esphome.yaml_util import ESPHomeDataBase lv_uses = { "USER_DATA", @@ -44,23 +41,6 @@ def validate_printf(value): return value -def get_line_marks(value) -> list: - """ - If possible, return a preprocessor directive to identify the line number where the given id was defined. - :param id: The id in question - :return: A list containing zero or more line directives - """ - path = None - if isinstance(value, ESPHomeDataBase): - path = value.esp_range - elif isinstance(value, ID) and isinstance(CORE.config, Config): - path = CORE.config.get_path_for_id(value)[:-1] - path = CORE.config.get_deepest_document_range_for_path(path) - if path is None: - return [] - return [path.start_mark.as_line_directive] - - def requires_component(comp): def validator(value): lvgl_components_required.add(comp) diff --git a/esphome/components/lvgl/img.py b/esphome/components/lvgl/img.py new file mode 100644 index 0000000000..e9682def8c --- /dev/null +++ b/esphome/components/lvgl/img.py @@ -0,0 +1,85 @@ +import esphome.config_validation as cv +from esphome.const import CONF_ANGLE, CONF_MODE + +from .defines import ( + CONF_ANTIALIAS, + CONF_MAIN, + CONF_OFFSET_X, + CONF_OFFSET_Y, + CONF_PIVOT_X, + CONF_PIVOT_Y, + CONF_SRC, + CONF_ZOOM, + LvConstant, +) +from .label import CONF_LABEL +from .lv_validation import angle, lv_bool, lv_image, size, zoom +from .lvcode import lv +from .types import lv_img_t +from .widget import Widget, WidgetType + +CONF_IMAGE = "image" + +BASE_IMG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_PIVOT_X, default="50%"): size, + cv.Optional(CONF_PIVOT_Y, default="50%"): size, + cv.Optional(CONF_ANGLE): angle, + cv.Optional(CONF_ZOOM): zoom, + cv.Optional(CONF_OFFSET_X): size, + cv.Optional(CONF_OFFSET_Y): size, + cv.Optional(CONF_ANTIALIAS): lv_bool, + cv.Optional(CONF_MODE): LvConstant( + "LV_IMG_SIZE_MODE_", "VIRTUAL", "REAL" + ).one_of, + } +) + +IMG_SCHEMA = BASE_IMG_SCHEMA.extend( + { + cv.Required(CONF_SRC): lv_image, + } +) + +IMG_MODIFY_SCHEMA = BASE_IMG_SCHEMA.extend( + { + cv.Optional(CONF_SRC): lv_image, + } +) + + +class ImgType(WidgetType): + def __init__(self): + super().__init__( + CONF_IMAGE, + lv_img_t, + (CONF_MAIN,), + IMG_SCHEMA, + IMG_MODIFY_SCHEMA, + lv_name="img", + ) + + def get_uses(self): + return "img", CONF_LABEL + + async def to_code(self, w: Widget, config): + if src := config.get(CONF_SRC): + lv.img_set_src(w.obj, await lv_image.process(src)) + if cf_angle := config.get(CONF_ANGLE): + pivot_x = config[CONF_PIVOT_X] + pivot_y = config[CONF_PIVOT_Y] + lv.img_set_pivot(w.obj, pivot_x, pivot_y) + lv.img_set_angle(w.obj, cf_angle) + if img_zoom := config.get(CONF_ZOOM): + lv.img_set_zoom(w.obj, img_zoom) + if offset := config.get(CONF_OFFSET_X): + lv.img_set_offset_x(w.obj, offset) + if offset := config.get(CONF_OFFSET_Y): + lv.img_set_offset_y(w.obj, offset) + if CONF_ANTIALIAS in config: + lv.img_set_antialias(w.obj, config[CONF_ANTIALIAS]) + if mode := config.get(CONF_MODE): + lv.img_set_mode(w.obj, mode) + + +img_spec = ImgType() diff --git a/esphome/components/lvgl/label.py b/esphome/components/lvgl/label.py index 0498f39474..6c3e1f4a00 100644 --- a/esphome/components/lvgl/label.py +++ b/esphome/components/lvgl/label.py @@ -1,7 +1,6 @@ import esphome.config_validation as cv from .defines import ( - CONF_LABEL, CONF_LONG_MODE, CONF_MAIN, CONF_RECOLOR, @@ -15,6 +14,8 @@ from .schemas import TEXT_SCHEMA from .types import LvText, WidgetType from .widget import Widget +CONF_LABEL = "label" + class LabelType(WidgetType): def __init__(self): @@ -33,9 +34,9 @@ class LabelType(WidgetType): async def to_code(self, w: Widget, config): """For a text object, create and set text""" if value := config.get(CONF_TEXT): - w.set_property(CONF_TEXT, await lv_text.process(value)) - w.set_property(CONF_LONG_MODE, config) - w.set_property(CONF_RECOLOR, config) + await w.set_property(CONF_TEXT, await lv_text.process(value)) + await w.set_property(CONF_LONG_MODE, config) + await w.set_property(CONF_RECOLOR, config) label_spec = LabelType() diff --git a/esphome/components/lvgl/led.py b/esphome/components/lvgl/led.py new file mode 100644 index 0000000000..f920758efb --- /dev/null +++ b/esphome/components/lvgl/led.py @@ -0,0 +1,29 @@ +import esphome.config_validation as cv +from esphome.const import CONF_BRIGHTNESS, CONF_COLOR, CONF_LED + +from .defines import CONF_MAIN +from .lv_validation import lv_brightness, lv_color +from .lvcode import lv +from .types import LvType +from .widget import Widget, WidgetType + +LED_SCHEMA = cv.Schema( + { + cv.Optional(CONF_COLOR): lv_color, + cv.Optional(CONF_BRIGHTNESS): lv_brightness, + } +) + + +class LedType(WidgetType): + def __init__(self): + super().__init__(CONF_LED, LvType("lv_led_t"), (CONF_MAIN,), LED_SCHEMA) + + async def to_code(self, w: Widget, config): + if color := config.get(CONF_COLOR): + lv.led_set_color(w.obj, await lv_color.process(color)) + if brightness := config.get(CONF_BRIGHTNESS): + lv.led_set_brightness(w.obj, await lv_brightness.process(brightness)) + + +led_spec = LedType() diff --git a/esphome/components/lvgl/line.py b/esphome/components/lvgl/line.py new file mode 100644 index 0000000000..ab50832bbf --- /dev/null +++ b/esphome/components/lvgl/line.py @@ -0,0 +1,51 @@ +import functools + +import esphome.codegen as cg +import esphome.config_validation as cv + +from . import defines as df +from .defines import CONF_MAIN, literal +from .lvcode import lv +from .types import LvType +from .widget import Widget, WidgetType + +CONF_LINE = "line" +CONF_POINTS = "points" +CONF_POINT_LIST_ID = "point_list_id" + +lv_point_t = cg.global_ns.struct("lv_point_t") + + +def point_list(il): + il = cv.string(il) + nl = il.replace(" ", "").split(",") + return [int(n) for n in nl] + + +def cv_point_list(value): + if not isinstance(value, list): + raise cv.Invalid("List of points required") + values = [point_list(v) for v in value] + if not functools.reduce(lambda f, v: f and len(v) == 2, values, True): + raise cv.Invalid("Points must be a list of x,y integer pairs") + return values + + +LINE_SCHEMA = { + cv.Required(df.CONF_POINTS): cv_point_list, + cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t), +} + + +class LineType(WidgetType): + def __init__(self): + super().__init__(CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA) + + async def to_code(self, w: Widget, config): + """For a line object, create and add the points""" + data = literal(config[CONF_POINTS]) + points = cg.static_const_array(config[CONF_POINT_LIST_ID], data) + lv.line_set_points(w.obj, points, len(data)) + + +line_spec = LineType() diff --git a/esphome/components/lvgl/lv_bar.py b/esphome/components/lvgl/lv_bar.py new file mode 100644 index 0000000000..d5dcff0bf0 --- /dev/null +++ b/esphome/components/lvgl/lv_bar.py @@ -0,0 +1,53 @@ +import esphome.config_validation as cv +from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE + +from .defines import BAR_MODES, CONF_ANIMATED, CONF_INDICATOR, CONF_MAIN, literal +from .lv_validation import animated, get_start_value, lv_float +from .lvcode import lv +from .types import LvNumber, NumberType +from .widget import Widget + +CONF_BAR = "bar" +BAR_MODIFY_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_ANIMATED, default=True): animated, + } +) + +BAR_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_MIN_VALUE, default=0): cv.int_, + cv.Optional(CONF_MAX_VALUE, default=100): cv.int_, + cv.Optional(CONF_MODE, default="NORMAL"): BAR_MODES.one_of, + cv.Optional(CONF_ANIMATED, default=True): animated, + } +) + + +class BarType(NumberType): + def __init__(self): + super().__init__( + CONF_BAR, + LvNumber("lv_bar_t"), + parts=(CONF_MAIN, CONF_INDICATOR), + schema=BAR_SCHEMA, + modify_schema=BAR_MODIFY_SCHEMA, + ) + + async def to_code(self, w: Widget, config): + var = w.obj + if CONF_MIN_VALUE in config: + lv.bar_set_range(var, config[CONF_MIN_VALUE], config[CONF_MAX_VALUE]) + lv.bar_set_mode(var, literal(config[CONF_MODE])) + value = await get_start_value(config) + if value is not None: + lv.bar_set_value(var, value, literal(config[CONF_ANIMATED])) + + @property + def animated(self): + return True + + +bar_spec = BarType() diff --git a/esphome/components/lvgl/lv_switch.py b/esphome/components/lvgl/lv_switch.py new file mode 100644 index 0000000000..5db2c2ce38 --- /dev/null +++ b/esphome/components/lvgl/lv_switch.py @@ -0,0 +1,20 @@ +from .defines import CONF_INDICATOR, CONF_KNOB, CONF_MAIN +from .types import LvBoolean +from .widget import WidgetType + +CONF_SWITCH = "switch" + + +class SwitchType(WidgetType): + def __init__(self): + super().__init__( + CONF_SWITCH, + LvBoolean("lv_switch_t"), + (CONF_MAIN, CONF_INDICATOR, CONF_KNOB), + ) + + async def to_code(self, w, config): + return [] + + +switch_spec = SwitchType() diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 818bde6aed..b351b84af6 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,3 +1,5 @@ +from typing import Union + import esphome.codegen as cg from esphome.components.binary_sensor import BinarySensor from esphome.components.color import ColorStruct @@ -6,7 +8,7 @@ from esphome.components.image import Image_ from esphome.components.sensor import Sensor from esphome.components.text_sensor import TextSensor import esphome.config_validation as cv -from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT +from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_VALUE from esphome.core import HexInt from esphome.cpp_generator import MockObj from esphome.cpp_types import uint32 @@ -14,7 +16,14 @@ from esphome.helpers import cpp_string_escape from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from . import types as ty -from .defines import LV_FONTS, ConstantLiteral, LValidator, LvConstant, literal +from .defines import ( + CONF_END_VALUE, + CONF_START_VALUE, + LV_FONTS, + LValidator, + LvConstant, + literal, +) from .helpers import ( esphome_fonts_used, lv_fonts_used, @@ -60,6 +69,13 @@ def color_retmapper(value): return lv_expr.color_from(MockObj(value)) +def option_string(value): + value = cv.string(value).strip() + if value.find("\n") != -1: + raise cv.Invalid("Options strings must not contain newlines") + return value + + lv_color = LValidator(color, ty.lv_color_t, retmapper=color_retmapper) @@ -156,6 +172,12 @@ lv_bool = LValidator( ) +def lv_pct(value: Union[int, float]): + if isinstance(value, float): + value = int(value * 100) + return literal(f"lv_pct({value})") + + def lvms_validator_(value): if value == "never": value = "2147483647ms" @@ -189,13 +211,16 @@ class TextValidator(LValidator): args = [str(x) for x in value[CONF_ARGS]] arg_expr = cg.RawExpression(",".join(args)) format_str = cpp_string_escape(value[CONF_FORMAT]) - return f"str_sprintf({format_str}, {arg_expr}).c_str()" + return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") return await super().process(value, args) lv_text = TextValidator() lv_float = LValidator(cv.float_, cg.float_, Sensor, "get_state()") lv_int = LValidator(cv.int_, cg.int_, Sensor, "get_state()") +lv_brightness = LValidator( + cv.percentage, cg.float_, Sensor, "get_state()", retmapper=lambda x: int(x * 255) +) def is_lv_font(font): @@ -222,8 +247,33 @@ class LvFont(LValidator): async def process(self, value, args=()): if is_lv_font(value): - return ConstantLiteral(f"&lv_font_{value}") - return ConstantLiteral(f"{value}_engine->get_lv_font()") + return literal(f"&lv_font_{value}") + return literal(f"{value}_engine->get_lv_font()") lv_font = LvFont() + + +def animated(value): + if isinstance(value, bool): + value = "ON" if value else "OFF" + return LvConstant("LV_ANIM_", "OFF", "ON").one_of(value) + + +def key_code(value): + value = cv.Any(cv.All(cv.string_strict, cv.Length(min=1, max=1)), cv.uint8_t)(value) + if isinstance(value, str): + return ord(value[0]) + return value + + +async def get_end_value(config): + return await lv_int.process(config.get(CONF_END_VALUE)) + + +async def get_start_value(config): + if CONF_START_VALUE in config: + value = config[CONF_START_VALUE] + else: + value = config.get(CONF_VALUE) + return await lv_int.process(value) diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 3a8a958f2e..f54a032de2 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -1,9 +1,9 @@ import abc -import logging from typing import Union from esphome import codegen as cg -from esphome.core import ID, Lambda +from esphome.config import Config +from esphome.core import CORE, ID, Lambda from esphome.cpp_generator import ( AssignmentExpression, CallExpression, @@ -18,12 +18,47 @@ from esphome.cpp_generator import ( VariableDeclarationExpression, statement, ) +from esphome.yaml_util import ESPHomeDataBase -from .defines import ConstantLiteral -from .helpers import get_line_marks -from .types import lv_group_t +from .defines import literal, lvgl_ns -_LOGGER = logging.getLogger(__name__) +LVGL_COMP = "lv_component" # used as a lambda argument in lvgl_comp() + +# Argument tuple for use in lambdas +LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent) +LVGL_COMP_ARG = [(LvglComponent.operator("ptr"), LVGL_COMP)] +lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr") +EVENT_ARG = [(lv_event_t_ptr, "ev")] +CUSTOM_EVENT = literal("lvgl::lv_custom_event") + + +def get_line_marks(value) -> list: + """ + If possible, return a preprocessor directive to identify the line number where the given id was defined. + :param value: The id or other token to get the line number for + :return: A list containing zero or more line directives + """ + path = None + if isinstance(value, ESPHomeDataBase): + path = value.esp_range + elif isinstance(value, ID) and isinstance(CORE.config, Config): + path = CORE.config.get_path_for_id(value)[:-1] + path = CORE.config.get_deepest_document_range_for_path(path) + if path is None: + return [] + return [path.start_mark.as_line_directive] + + +class IndentedStatement(Statement): + def __init__(self, stmt: Statement, indent: int): + self.statement = stmt + self.indent = indent + + def __str__(self): + result = " " * self.indent * 4 + str(self.statement).strip() + if not isinstance(self.statement, RawStatement): + result += ";" + return result class CodeContext(abc.ABC): @@ -39,6 +74,16 @@ class CodeContext(abc.ABC): def add(self, expression: Union[Expression, Statement]): pass + @staticmethod + def start_block(): + CodeContext.append(RawStatement("{")) + CodeContext.code_context.indent() + + @staticmethod + def end_block(): + CodeContext.code_context.detent() + CodeContext.append(RawStatement("}")) + @staticmethod def append(expression: Union[Expression, Statement]): if CodeContext.code_context is not None: @@ -47,14 +92,25 @@ class CodeContext(abc.ABC): def __init__(self): self.previous: Union[CodeContext | None] = None + self.indent_level = 0 - def __enter__(self): + async def __aenter__(self): self.previous = CodeContext.code_context CodeContext.code_context = self + return self - def __exit__(self, *args): + async def __aexit__(self, *args): CodeContext.code_context = self.previous + def indent(self): + self.indent_level += 1 + + def detent(self): + self.indent_level -= 1 + + def indented_statement(self, stmt): + return IndentedStatement(stmt, self.indent_level) + class MainContext(CodeContext): """ @@ -62,42 +118,7 @@ class MainContext(CodeContext): """ def add(self, expression: Union[Expression, Statement]): - return cg.add(expression) - - -class LvContext(CodeContext): - """ - Code generation into the LVGL initialisation code (called in `setup()`) - """ - - lv_init_code: list["Statement"] = [] - - @staticmethod - def lv_add(expression: Union[Expression, Statement]): - if isinstance(expression, Expression): - expression = statement(expression) - if not isinstance(expression, Statement): - raise ValueError( - f"Add '{expression}' must be expression or statement, not {type(expression)}" - ) - LvContext.lv_init_code.append(expression) - _LOGGER.debug("LV Adding: %s", expression) - return expression - - @staticmethod - def get_code(): - code = [] - for exp in LvContext.lv_init_code: - text = str(statement(exp)) - text = text.rstrip() - code.append(text) - return "\n".join(code) + "\n\n" - - def add(self, expression: Union[Expression, Statement]): - return LvContext.lv_add(expression) - - def set_style(self, prop): - return MockObj("lv_set_style_{prop}", "") + return cg.add(self.indented_statement(expression)) class LambdaContext(CodeContext): @@ -110,21 +131,23 @@ class LambdaContext(CodeContext): parameters: list[tuple[SafeExpType, str]] = None, return_type: SafeExpType = cg.void, capture: str = "", + where=None, ): super().__init__() self.code_list: list[Statement] = [] - self.parameters = parameters + self.parameters = parameters or [] self.return_type = return_type self.capture = capture + self.where = where def add(self, expression: Union[Expression, Statement]): - self.code_list.append(expression) + self.code_list.append(self.indented_statement(expression)) return expression async def get_lambda(self) -> LambdaExpression: code_text = self.get_code() return await cg.process_lambda( - Lambda("\n".join(code_text) + "\n\n"), + Lambda("\n".join(code_text) + "\n"), self.parameters, capture=self.capture, return_type=self.return_type, @@ -138,33 +161,59 @@ class LambdaContext(CodeContext): code_text.append(text) return code_text - def __enter__(self): - super().__enter__() + async def __aenter__(self): + await super().__aenter__() + add_line_marks(self.where) return self +class LvContext(LambdaContext): + """ + Code generation into the LVGL initialisation code (called in `setup()`) + """ + + def __init__(self, lv_component, args=None): + self.args = args or LVGL_COMP_ARG + super().__init__(parameters=self.args) + self.lv_component = lv_component + + async def add_init_lambda(self): + cg.add(self.lv_component.add_init_lambda(await self.get_lambda())) + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await super().__aexit__(exc_type, exc_val, exc_tb) + await self.add_init_lambda() + + def add(self, expression: Union[Expression, Statement]): + self.code_list.append(self.indented_statement(expression)) + return expression + + def __call__(self, *args): + return self.add(*args) + + class LocalVariable(MockObj): """ Create a local variable and enclose the code using it within a block. """ - def __init__(self, name, type, modifier=None, rhs=None): - base = ID(name, True, type) + def __init__(self, name, type, rhs=None, modifier="*"): + base = ID(name + "_VAR_", True, type) super().__init__(base, "") self.modifier = modifier self.rhs = rhs def __enter__(self): - CodeContext.append(RawStatement("{")) + CodeContext.start_block() CodeContext.append( VariableDeclarationExpression(self.base.type, self.modifier, self.base.id) ) if self.rhs is not None: CodeContext.append(AssignmentExpression(None, "", self.base, self.rhs)) - return self.base + return MockObj(self.base) def __exit__(self, *args): - CodeContext.append(RawStatement("}")) + CodeContext.end_block() class MockLv: @@ -199,14 +248,27 @@ class MockLv: self.append(result) return result - def cond_if(self, expression: Expression): - CodeContext.append(RawStatement(f"if {expression} {{")) - def cond_else(self): +class LvConditional: + def __init__(self, condition): + self.condition = condition + + def __enter__(self): + if self.condition is not None: + CodeContext.append(RawStatement(f"if ({self.condition}) {{")) + CodeContext.code_context.indent() + return self + + def __exit__(self, *args): + if self.condition is not None: + CodeContext.code_context.detent() + CodeContext.append(RawStatement("}")) + + def else_(self): + assert self.condition is not None + CodeContext.code_context.detent() CodeContext.append(RawStatement("} else {")) - - def cond_endif(self): - CodeContext.append(RawStatement("}")) + CodeContext.code_context.indent() class ReturnStatement(ExpressionStatement): @@ -228,36 +290,56 @@ lv = MockLv("lv_") lv_expr = LvExpr("lv_") # Mock for lv_obj_ calls lv_obj = MockLv("lv_obj_") -lvgl_comp = MockObj("lvgl_comp", "->") +# Operations on the LVGL component +lvgl_comp = MockObj(LVGL_COMP, "->") -# equivalent to cg.add() for the lvgl init context +# equivalent to cg.add() for the current code context def lv_add(expression: Union[Expression, Statement]): return CodeContext.append(expression) def add_line_marks(where): + """ + Add line marks for the current code context + :param where: An object to identify the source of the line marks + :return: + """ for mark in get_line_marks(where): lv_add(cg.RawStatement(mark)) def lv_assign(target, expression): - lv_add(RawExpression(f"{target} = {expression}")) + lv_add(AssignmentExpression("", "", target, expression)) -lv_groups = {} # Widget group names +def lv_Pvariable(type, name): + """ + Create but do not initialise a pointer variable + :param type: Type of the variable target + :param name: name of the variable, or an ID + :return: A MockObj of the variable + """ + if isinstance(name, str): + name = ID(name, True, type) + decl = VariableDeclarationExpression(type, "*", name) + CORE.add_global(decl) + var = MockObj(name, "->") + CORE.register_variable(name, var) + return var -def add_group(name): - if name is None: - return None - fullname = f"lv_esp_group_{name}" - if name not in lv_groups: - gid = ID(fullname, True, type=lv_group_t.operator("ptr")) - lv_add( - AssignmentExpression( - type_=gid.type, modifier="", name=fullname, rhs=lv_expr.group_create() - ) - ) - lv_groups[name] = ConstantLiteral(fullname) - return lv_groups[name] +def lv_variable(type, name): + """ + Create but do not initialise a variable + :param type: Type of the variable target + :param name: name of the variable, or an ID + :return: A MockObj of the variable + """ + if isinstance(name, str): + name = ID(name, True, type) + decl = VariableDeclarationExpression(type, "", name) + CORE.add_global(decl) + var = MockObj(name, ".") + CORE.register_variable(name, var) + return var diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 34f8eaf21f..1221682d28 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -9,8 +9,72 @@ namespace esphome { namespace lvgl { static const char *const TAG = "lvgl"; +#if LV_USE_LOG +static void log_cb(const char *buf) { + esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf); +} +#endif // LV_USE_LOG + +static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { + // make sure all coordinates are even + if (area->x1 & 1) + area->x1--; + if (!(area->x2 & 1)) + area->x2++; + if (area->y1 & 1) + area->y1--; + if (!(area->y2 & 1)) + area->y2++; +} + lv_event_code_t lv_custom_event; // NOLINT void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); } +void LvglComponent::set_paused(bool paused, bool show_snow) { + this->paused_ = paused; + this->show_snow_ = show_snow; + this->snow_line_ = 0; + if (!paused && lv_scr_act() != nullptr) { + lv_disp_trig_activity(this->disp_); // resets the inactivity time + lv_obj_invalidate(lv_scr_act()); + } +} +void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) { + lv_obj_add_event_cb(obj, callback, event, this); + if (event == LV_EVENT_VALUE_CHANGED) { + lv_obj_add_event_cb(obj, callback, lv_custom_event, this); + } +} +void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, + lv_event_code_t event2) { + this->add_event_cb(obj, callback, event1); + this->add_event_cb(obj, callback, event2); +} +void LvglComponent::add_page(LvPageType *page) { + this->pages_.push_back(page); + page->setup(this->pages_.size() - 1); +} +void LvglComponent::show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time) { + if (index >= this->pages_.size()) + return; + this->current_page_ = index; + lv_scr_load_anim(this->pages_[this->current_page_]->obj, anim, time, 0, false); +} +void LvglComponent::show_next_page(lv_scr_load_anim_t anim, uint32_t time) { + if (this->pages_.empty() || (this->current_page_ == this->pages_.size() - 1 && !this->page_wrap_)) + return; + do { + this->current_page_ = (this->current_page_ + 1) % this->pages_.size(); + } while (this->pages_[this->current_page_]->skip); // skip empty pages() + this->show_page(this->current_page_, anim, time); +} +void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) { + if (this->pages_.empty() || (this->current_page_ == 0 && !this->page_wrap_)) + return; + do { + this->current_page_ = (this->current_page_ + this->pages_.size() - 1) % this->pages_.size(); + } while (this->pages_[this->current_page_]->skip); // skip empty pages() + this->show_page(this->current_page_, anim, time); +} void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) { for (auto *display : this->displays_) { display->draw_pixels_at(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), ptr, @@ -27,6 +91,116 @@ void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv } lv_disp_flush_ready(disp_drv); } +IdleTrigger::IdleTrigger(LvglComponent *parent, TemplatableValue timeout) : timeout_(std::move(timeout)) { + parent->add_on_idle_callback([this](uint32_t idle_time) { + if (!this->is_idle_ && idle_time > this->timeout_.value()) { + this->is_idle_ = true; + this->trigger(); + } else if (this->is_idle_ && idle_time < this->timeout_.value()) { + this->is_idle_ = false; + } + }); +} + +#ifdef USE_LVGL_TOUCHSCREEN +LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) { + lv_indev_drv_init(&this->drv_); + this->drv_.long_press_repeat_time = long_press_repeat_time; + this->drv_.long_press_time = long_press_time; + this->drv_.type = LV_INDEV_TYPE_POINTER; + this->drv_.user_data = this; + this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) { + auto *l = static_cast(d->user_data); + if (l->touch_pressed_) { + data->point.x = l->touch_point_.x; + data->point.y = l->touch_point_.y; + data->state = LV_INDEV_STATE_PRESSED; + } else { + data->state = LV_INDEV_STATE_RELEASED; + } + }; +} +void LVTouchListener::update(const touchscreen::TouchPoints_t &tpoints) { + this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty(); + if (this->touch_pressed_) + this->touch_point_ = tpoints[0]; +} +#endif // USE_LVGL_TOUCHSCREEN + +#ifdef USE_LVGL_ROTARY_ENCODER +LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) { + lv_indev_drv_init(&this->drv_); + this->drv_.type = type; + this->drv_.user_data = this; + this->drv_.long_press_time = lpt; + this->drv_.long_press_repeat_time = lprt; + this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) { + auto *l = static_cast(d->user_data); + data->state = l->pressed_ ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + data->key = l->key_; + data->enc_diff = (int16_t) (l->count_ - l->last_count_); + l->last_count_ = l->count_; + data->continue_reading = false; + }; +} +#endif // USE_LVGL_ROTARY_ENCODER + +#ifdef USE_LVGL_BUTTONMATRIX +void LvBtnmatrixType::set_obj(lv_obj_t *lv_obj) { + LvCompound::set_obj(lv_obj); + lv_obj_add_event_cb( + lv_obj, + [](lv_event_t *event) { + auto *self = static_cast(event->user_data); + if (self->key_callback_.size() == 0) + return; + auto key_idx = lv_btnmatrix_get_selected_btn(self->obj); + if (key_idx == LV_BTNMATRIX_BTN_NONE) + return; + if (self->key_map_.count(key_idx) != 0) { + self->send_key_(self->key_map_[key_idx]); + return; + } + const auto *str = lv_btnmatrix_get_btn_text(self->obj, key_idx); + auto len = strlen(str); + while (len--) + self->send_key_(*str++); + }, + LV_EVENT_PRESSED, this); +} +#endif // USE_LVGL_BUTTONMATRIX + +#ifdef USE_LVGL_KEYBOARD +static const char *const KB_SPECIAL_KEYS[] = { + "abc", "ABC", "1#", + // maybe add other special keys here +}; + +void LvKeyboardType::set_obj(lv_obj_t *lv_obj) { + LvCompound::set_obj(lv_obj); + lv_obj_add_event_cb( + lv_obj, + [](lv_event_t *event) { + auto *self = static_cast(event->user_data); + if (self->key_callback_.size() == 0) + return; + + auto key_idx = lv_btnmatrix_get_selected_btn(self->obj); + if (key_idx == LV_BTNMATRIX_BTN_NONE) + return; + const char *txt = lv_btnmatrix_get_btn_text(self->obj, key_idx); + if (txt == nullptr) + return; + for (const auto *kb_special_key : KB_SPECIAL_KEYS) { + if (strcmp(txt, kb_special_key) == 0) + return; + } + while (*txt != 0) + self->send_key_(*txt++); + }, + LV_EVENT_PRESSED, this); +} +#endif // USE_LVGL_KEYBOARD void LvglComponent::write_random_() { // length of 2 lines in 32 bit units @@ -97,9 +271,24 @@ void LvglComponent::setup() { this->disp_ = lv_disp_drv_register(&this->disp_drv_); for (const auto &v : this->init_lambdas_) v(this); + this->show_page(0, LV_SCR_LOAD_ANIM_NONE, 0); lv_disp_trig_activity(this->disp_); ESP_LOGCONFIG(TAG, "LVGL Setup complete"); } +void LvglComponent::update() { + // update indicators + if (this->paused_) { + return; + } + this->idle_callbacks_.call(lv_disp_get_inactive_time(this->disp_)); +} +void LvglComponent::loop() { + if (this->paused_) { + if (this->show_snow_) + this->write_random_(); + } + lv_timer_handler_run_in_period(5); +} #ifdef USE_LVGL_IMAGE lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) { @@ -142,7 +331,20 @@ lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) { } return img_dsc; } +#endif // USE_LVGL_IMAGE + +#ifdef USE_LVGL_ANIMIMG +void lv_animimg_stop(lv_obj_t *obj) { + auto *animg = (lv_animimg_t *) obj; + int32_t duration = animg->anim.time; + lv_animimg_set_duration(obj, 0); + lv_animimg_start(obj); + lv_animimg_set_duration(obj, duration); +} #endif +void LvglComponent::static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { + reinterpret_cast(disp_drv->user_data)->flush_cb_(disp_drv, area, color_p); +} } // namespace lvgl } // namespace esphome diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index a0d3d226ce..b92799addd 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -18,7 +18,6 @@ #include "esphome/core/component.h" #include "esphome/core/log.h" #include -#include #include #ifdef USE_LVGL_IMAGE #include "esphome/components/image/image.h" @@ -31,6 +30,10 @@ #include "esphome/components/touchscreen/touchscreen.h" #endif // USE_LVGL_TOUCHSCREEN +#if defined(USE_LVGL_BUTTONMATRIX) || defined(USE_LVGL_KEYBOARD) +#include "esphome/components/key_provider/key_provider.h" +#endif // USE_LVGL_BUTTONMATRIX + namespace esphome { namespace lvgl { @@ -47,12 +50,25 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT #endif // LV_COLOR_DEPTH // Parent class for things that wrap an LVGL object -class LvCompound final { +class LvCompound { public: - void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; } + virtual void set_obj(lv_obj_t *lv_obj) { this->obj = lv_obj; } lv_obj_t *obj{}; }; +class LvPageType { + public: + LvPageType(bool skip) : skip(skip) {} + + void setup(size_t index) { + this->index = index; + this->obj = lv_obj_create(nullptr); + } + lv_obj_t *obj{}; + size_t index{}; + bool skip; +}; + using LvLambdaType = std::function; using set_value_lambda_t = std::function; using event_callback_t = void(_lv_event_t *); @@ -89,48 +105,20 @@ class FontEngine { lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc = nullptr); #endif // USE_LVGL_IMAGE +#ifdef USE_LVGL_ANIMIMG +void lv_animimg_stop(lv_obj_t *obj); +#endif // USE_LVGL_ANIMIMG + class LvglComponent : public PollingComponent { constexpr static const char *const TAG = "lvgl"; public: - static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { - reinterpret_cast(disp_drv->user_data)->flush_cb_(disp_drv, area, color_p); - } + static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); float get_setup_priority() const override { return setup_priority::PROCESSOR; } - static void log_cb(const char *buf) { - esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf); - } - static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { - // make sure all coordinates are even - if (area->x1 & 1) - area->x1--; - if (!(area->x2 & 1)) - area->x2++; - if (area->y1 & 1) - area->y1--; - if (!(area->y2 & 1)) - area->y2++; - } - void setup() override; - - void update() override { - // update indicators - if (this->paused_) { - return; - } - this->idle_callbacks_.call(lv_disp_get_inactive_time(this->disp_)); - } - - void loop() override { - if (this->paused_) { - if (this->show_snow_) - this->write_random_(); - } - lv_timer_handler_run_in_period(5); - } - + void update() override; + void loop() override; void add_on_idle_callback(std::function &&callback) { this->idle_callbacks_.add(std::move(callback)); } @@ -141,23 +129,15 @@ class LvglComponent : public PollingComponent { bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; } void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; } lv_disp_t *get_disp() { return this->disp_; } - void set_paused(bool paused, bool show_snow) { - this->paused_ = paused; - this->show_snow_ = show_snow; - this->snow_line_ = 0; - if (!paused && lv_scr_act() != nullptr) { - lv_disp_trig_activity(this->disp_); // resets the inactivity time - lv_obj_invalidate(lv_scr_act()); - } - } - - void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) { - lv_obj_add_event_cb(obj, callback, event, this); - if (event == LV_EVENT_VALUE_CHANGED) { - lv_obj_add_event_cb(obj, callback, lv_custom_event, this); - } - } + void set_paused(bool paused, bool show_snow); + void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event); + void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2); bool is_paused() const { return this->paused_; } + void add_page(LvPageType *page); + void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time); + void show_next_page(lv_scr_load_anim_t anim, uint32_t time); + void show_prev_page(lv_scr_load_anim_t anim, uint32_t time); + void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; } protected: void write_random_(); @@ -168,8 +148,11 @@ class LvglComponent : public PollingComponent { lv_disp_drv_t disp_drv_{}; lv_disp_t *disp_{}; bool paused_{}; + std::vector pages_{}; + size_t current_page_{0}; bool show_snow_{}; lv_coord_t snow_line_{}; + bool page_wrap_{true}; std::vector> init_lambdas_; CallbackManager idle_callbacks_{}; @@ -179,16 +162,7 @@ class LvglComponent : public PollingComponent { class IdleTrigger : public Trigger<> { public: - explicit IdleTrigger(LvglComponent *parent, TemplatableValue timeout) : timeout_(std::move(timeout)) { - parent->add_on_idle_callback([this](uint32_t idle_time) { - if (!this->is_idle_ && idle_time > this->timeout_.value()) { - this->is_idle_ = true; - this->trigger(); - } else if (this->is_idle_ && idle_time < this->timeout_.value()) { - this->is_idle_ = false; - } - }); - } + explicit IdleTrigger(LvglComponent *parent, TemplatableValue timeout); protected: TemplatableValue timeout_; @@ -217,28 +191,8 @@ template class LvglCondition : public Condition, public P #ifdef USE_LVGL_TOUCHSCREEN class LVTouchListener : public touchscreen::TouchListener, public Parented { public: - LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) { - lv_indev_drv_init(&this->drv_); - this->drv_.long_press_repeat_time = long_press_repeat_time; - this->drv_.long_press_time = long_press_time; - this->drv_.type = LV_INDEV_TYPE_POINTER; - this->drv_.user_data = this; - this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) { - auto *l = static_cast(d->user_data); - if (l->touch_pressed_) { - data->point.x = l->touch_point_.x; - data->point.y = l->touch_point_.y; - data->state = LV_INDEV_STATE_PRESSED; - } else { - data->state = LV_INDEV_STATE_RELEASED; - } - }; - } - void update(const touchscreen::TouchPoints_t &tpoints) override { - this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty(); - if (this->touch_pressed_) - this->touch_point_ = tpoints[0]; - } + LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time); + void update(const touchscreen::TouchPoints_t &tpoints) override; void release() override { touch_pressed_ = false; } lv_indev_drv_t *get_drv() { return &this->drv_; } @@ -249,24 +203,10 @@ class LVTouchListener : public touchscreen::TouchListener, public Parented { public: - LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) { - lv_indev_drv_init(&this->drv_); - this->drv_.type = type; - this->drv_.user_data = this; - this->drv_.long_press_time = lpt; - this->drv_.long_press_repeat_time = lprt; - this->drv_.read_cb = [](lv_indev_drv_t *d, lv_indev_data_t *data) { - auto *l = static_cast(d->user_data); - data->state = l->pressed_ ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - data->key = l->key_; - data->enc_diff = (int16_t) (l->count_ - l->last_count_); - l->last_count_ = l->count_; - data->continue_reading = false; - }; - } + LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt); void set_left_button(binary_sensor::BinarySensor *left_button) { left_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_LEFT, state); }); @@ -304,6 +244,24 @@ class LVEncoderListener : public Parented { int32_t last_count_{}; int key_{}; }; -#endif // USE_LVGL_KEY_LISTENER +#endif // USE_LVGL_ROTARY_ENCODER +#ifdef USE_LVGL_BUTTONMATRIX +class LvBtnmatrixType : public key_provider::KeyProvider, public LvCompound { + public: + void set_obj(lv_obj_t *lv_obj) override; + uint16_t get_selected() { return lv_btnmatrix_get_selected_btn(this->obj); } + void set_key(size_t idx, uint8_t key) { this->key_map_[idx] = key; } + + protected: + std::map key_map_{}; +}; +#endif // USE_LVGL_BUTTONMATRIX + +#ifdef USE_LVGL_KEYBOARD +class LvKeyboardType : public key_provider::KeyProvider, public LvCompound { + public: + void set_obj(lv_obj_t *lv_obj) override; +}; +#endif // USE_LVGL_KEYBOARD } // namespace lvgl } // namespace esphome diff --git a/esphome/components/lvgl/page.py b/esphome/components/lvgl/page.py new file mode 100644 index 0000000000..4566b7eea4 --- /dev/null +++ b/esphome/components/lvgl/page.py @@ -0,0 +1,113 @@ +from esphome import automation, codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME + +from .defines import ( + CONF_ANIMATION, + CONF_LVGL_ID, + CONF_PAGE, + CONF_PAGE_WRAP, + CONF_SKIP, + LV_ANIM, +) +from .lv_validation import lv_bool, lv_milliseconds +from .lvcode import LVGL_COMP_ARG, LambdaContext, add_line_marks, lv_add, lvgl_comp +from .schemas import LVGL_SCHEMA +from .types import LvglAction, lv_page_t +from .widget import Widget, WidgetType, add_widgets, set_obj_properties + + +class PageType(WidgetType): + def __init__(self): + super().__init__( + CONF_PAGE, + lv_page_t, + (), + { + cv.Optional(CONF_SKIP, default=False): lv_bool, + }, + ) + + async def to_code(self, w: Widget, config: dict): + return [] + + +SHOW_SCHEMA = LVGL_SCHEMA.extend( + { + cv.Optional(CONF_ANIMATION, default="NONE"): LV_ANIM.one_of, + cv.Optional(CONF_TIME, default="50ms"): lv_milliseconds, + } +) + + +page_spec = PageType() + + +@automation.register_action( + "lvgl.page.next", + LvglAction, + SHOW_SCHEMA, +) +async def page_next_to_code(config, action_id, template_arg, args): + animation = await LV_ANIM.process(config[CONF_ANIMATION]) + time = await lv_milliseconds.process(config[CONF_TIME]) + async with LambdaContext(LVGL_COMP_ARG) as context: + add_line_marks(action_id) + lv_add(lvgl_comp.show_next_page(animation, time)) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, config[CONF_LVGL_ID]) + return var + + +@automation.register_action( + "lvgl.page.previous", + LvglAction, + SHOW_SCHEMA, +) +async def page_previous_to_code(config, action_id, template_arg, args): + animation = await LV_ANIM.process(config[CONF_ANIMATION]) + time = await lv_milliseconds.process(config[CONF_TIME]) + async with LambdaContext(LVGL_COMP_ARG) as context: + add_line_marks(action_id) + lv_add(lvgl_comp.show_prev_page(animation, time)) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, config[CONF_LVGL_ID]) + return var + + +@automation.register_action( + "lvgl.page.show", + LvglAction, + cv.maybe_simple_value( + SHOW_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.use_id(lv_page_t), + } + ), + key=CONF_ID, + ), +) +async def page_show_to_code(config, action_id, template_arg, args): + widget = await cg.get_variable(config[CONF_ID]) + animation = await LV_ANIM.process(config[CONF_ANIMATION]) + time = await lv_milliseconds.process(config[CONF_TIME]) + async with LambdaContext(LVGL_COMP_ARG) as context: + add_line_marks(action_id) + lv_add(lvgl_comp.show_page(widget.index, animation, time)) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) + await cg.register_parented(var, config[CONF_LVGL_ID]) + return var + + +async def add_pages(lv_component, config): + lv_add(lv_component.set_page_wrap(config[CONF_PAGE_WRAP])) + for pconf in config.get(CONF_PAGES, ()): + id = pconf[CONF_ID] + skip = pconf[CONF_SKIP] + var = cg.new_Pvariable(id, skip) + page = Widget.create(id, var, page_spec, pconf) + lv_add(lv_component.add_page(var)) + # Set outer config first + await set_obj_properties(page, config) + await set_obj_properties(page, pconf) + await add_widgets(page, pconf) diff --git a/esphome/components/lvgl/rotary_encoders.py b/esphome/components/lvgl/rotary_encoders.py index 77dc397c3e..ede6905a67 100644 --- a/esphome/components/lvgl/rotary_encoders.py +++ b/esphome/components/lvgl/rotary_encoders.py @@ -13,9 +13,10 @@ from .defines import ( CONF_ROTARY_ENCODERS, ) from .helpers import lvgl_components_required -from .lvcode import add_group, lv, lv_add, lv_expr +from .lvcode import lv, lv_add, lv_expr from .schemas import ENCODER_SCHEMA from .types import lv_indev_type_t +from .widget import add_group ROTARY_ENCODER_CONFIG = cv.ensure_list( ENCODER_SCHEMA.extend( diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index ebef56a882..796783890d 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -15,8 +15,12 @@ from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid, types as ty from .helpers import add_lv_use, requires_component, validate_printf -from .lv_validation import id_name, lv_font -from .types import WIDGET_TYPES, WidgetType +from .lv_validation import id_name, lv_color, lv_font, lv_image +from .lvcode import LvglComponent +from .types import WidgetType + +# this will be populated later, in __init__.py to avoid circular imports. +WIDGET_TYPES: dict = {} # A schema for text properties TEXT_SCHEMA = cv.Schema( @@ -38,11 +42,13 @@ TEXT_SCHEMA = cv.Schema( } ) -ACTION_SCHEMA = cv.maybe_simple_value( - { - cv.Required(CONF_ID): cv.use_id(ty.lv_pseudo_button_t), - }, - key=CONF_ID, +LIST_ACTION_SCHEMA = cv.ensure_list( + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(ty.lv_pseudo_button_t), + }, + key=CONF_ID, + ) ) PRESS_TIME = cv.All( @@ -154,6 +160,7 @@ STYLE_REMAP = { # Complete object style schema STYLE_SCHEMA = cv.Schema({cv.Optional(k): v for k, v in STYLE_PROPS.items()}).extend( { + cv.Optional(df.CONF_STYLES): cv.ensure_list(cv.use_id(ty.lv_style_t)), cv.Optional(df.CONF_SCROLLBAR_MODE): df.LvConstant( "LV_SCROLLBAR_MODE_", "OFF", "ON", "ACTIVE", "AUTO" ).one_of, @@ -209,7 +216,14 @@ def create_modify_schema(widget_type): part_schema(widget_type) .extend( { - cv.Required(CONF_ID): cv.use_id(widget_type), + 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, } ) @@ -227,6 +241,7 @@ def obj_schema(widget_type: WidgetType): return ( part_schema(widget_type) .extend(FLAG_SCHEMA) + .extend(LAYOUT_SCHEMA) .extend(ALIGN_TO_SCHEMA) .extend(automation_schema(widget_type.w_type)) .extend( @@ -240,6 +255,8 @@ def obj_schema(widget_type: WidgetType): ) +LAYOUT_SCHEMAS = {} + ALIGN_TO_SCHEMA = { cv.Optional(df.CONF_ALIGN_TO): cv.Schema( { @@ -252,6 +269,65 @@ ALIGN_TO_SCHEMA = { } +def grid_free_space(value): + value = cv.Upper(value) + if value.startswith("FR(") and value.endswith(")"): + value = value.removesuffix(")").removeprefix("FR(") + return f"LV_GRID_FR({cv.positive_int(value)})" + raise cv.Invalid("must be a size in pixels, CONTENT or FR(nn)") + + +grid_spec = cv.Any( + lvalid.size, df.LvConstant("LV_GRID_", "CONTENT").one_of, grid_free_space +) + +cell_alignments = df.LV_CELL_ALIGNMENTS.one_of +grid_alignments = df.LV_GRID_ALIGNMENTS.one_of +flex_alignments = df.LV_FLEX_ALIGNMENTS.one_of + +LAYOUT_SCHEMA = { + cv.Optional(df.CONF_LAYOUT): cv.typed_schema( + { + df.TYPE_GRID: { + cv.Required(df.CONF_GRID_ROWS): [grid_spec], + cv.Required(df.CONF_GRID_COLUMNS): [grid_spec], + cv.Optional(df.CONF_GRID_COLUMN_ALIGN): grid_alignments, + cv.Optional(df.CONF_GRID_ROW_ALIGN): grid_alignments, + }, + df.TYPE_FLEX: { + cv.Optional( + df.CONF_FLEX_FLOW, default="row_wrap" + ): df.FLEX_FLOWS.one_of, + cv.Optional(df.CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments, + cv.Optional(df.CONF_FLEX_ALIGN_CROSS, default="start"): flex_alignments, + cv.Optional(df.CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments, + }, + }, + lower=True, + ) +} + +GRID_CELL_SCHEMA = { + cv.Required(df.CONF_GRID_CELL_ROW_POS): cv.positive_int, + cv.Required(df.CONF_GRID_CELL_COLUMN_POS): cv.positive_int, + cv.Optional(df.CONF_GRID_CELL_ROW_SPAN, default=1): cv.positive_int, + cv.Optional(df.CONF_GRID_CELL_COLUMN_SPAN, default=1): cv.positive_int, + cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, + cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments, +} + +FLEX_OBJ_SCHEMA = { + cv.Optional(df.CONF_FLEX_GROW): cv.int_, +} + +DISP_BG_SCHEMA = cv.Schema( + { + cv.Optional(df.CONF_DISP_BG_IMAGE): lv_image, + cv.Optional(df.CONF_DISP_BG_COLOR): lv_color, + } +) + + # A style schema that can include text STYLED_TEXT_SCHEMA = cv.maybe_simple_value( STYLE_SCHEMA.extend(TEXT_SCHEMA), key=df.CONF_TEXT @@ -260,13 +336,11 @@ STYLED_TEXT_SCHEMA = cv.maybe_simple_value( # For use by platform components LVGL_SCHEMA = cv.Schema( { - cv.GenerateID(df.CONF_LVGL_ID): cv.use_id(ty.LvglComponent), + cv.GenerateID(df.CONF_LVGL_ID): cv.use_id(LvglComponent), } ) -ALL_STYLES = { - **STYLE_PROPS, -} +ALL_STYLES = {**STYLE_PROPS, **GRID_CELL_SCHEMA, **FLEX_OBJ_SCHEMA} def container_validator(schema, widget_type: WidgetType): @@ -281,16 +355,17 @@ def container_validator(schema, widget_type: WidgetType): result = schema if w_sch := widget_type.schema: result = result.extend(w_sch) + ltype = df.TYPE_NONE if value and (layout := value.get(df.CONF_LAYOUT)): if not isinstance(layout, dict): raise cv.Invalid("Layout value must be a dict") ltype = layout.get(CONF_TYPE) + if not ltype: + raise (cv.Invalid("Layout schema requires type:")) add_lv_use(ltype) - result = result.extend( - {cv.Optional(df.CONF_WIDGETS): cv.ensure_list(any_widget_schema())} - ) if value == SCHEMA_EXTRACT: return result + result = result.extend(LAYOUT_SCHEMAS[ltype.lower()]) return result(value) return validator diff --git a/esphome/components/lvgl/slider.py b/esphome/components/lvgl/slider.py new file mode 100644 index 0000000000..1886f79b44 --- /dev/null +++ b/esphome/components/lvgl/slider.py @@ -0,0 +1,63 @@ +import esphome.config_validation as cv +from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE + +from .defines import ( + BAR_MODES, + CONF_ANIMATED, + CONF_INDICATOR, + CONF_KNOB, + CONF_MAIN, + literal, +) +from .helpers import add_lv_use +from .lv_bar import CONF_BAR +from .lv_validation import animated, get_start_value, lv_float +from .lvcode import lv +from .types import LvNumber, NumberType +from .widget import Widget + +CONF_SLIDER = "slider" +SLIDER_MODIFY_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_ANIMATED, default=True): animated, + } +) + +SLIDER_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_MIN_VALUE, default=0): cv.int_, + cv.Optional(CONF_MAX_VALUE, default=100): cv.int_, + cv.Optional(CONF_MODE, default="NORMAL"): BAR_MODES.one_of, + cv.Optional(CONF_ANIMATED, default=True): animated, + } +) + + +class SliderType(NumberType): + def __init__(self): + super().__init__( + CONF_SLIDER, + LvNumber("lv_slider_t"), + parts=(CONF_MAIN, CONF_INDICATOR, CONF_KNOB), + schema=SLIDER_SCHEMA, + modify_schema=SLIDER_MODIFY_SCHEMA, + ) + + @property + def animated(self): + return True + + async def to_code(self, w: Widget, config): + add_lv_use(CONF_BAR) + if CONF_MIN_VALUE in config: + # not modify case + lv.slider_set_range(w.obj, config[CONF_MIN_VALUE], config[CONF_MAX_VALUE]) + lv.slider_set_mode(w.obj, literal(config[CONF_MODE])) + value = await get_start_value(config) + if value is not None: + lv.slider_set_value(w.obj, value, literal(config[CONF_ANIMATED])) + + +slider_spec = SliderType() diff --git a/esphome/components/lvgl/spinner.py b/esphome/components/lvgl/spinner.py new file mode 100644 index 0000000000..2f798d0fbf --- /dev/null +++ b/esphome/components/lvgl/spinner.py @@ -0,0 +1,43 @@ +import esphome.config_validation as cv +from esphome.cpp_generator import MockObjClass + +from .arc import CONF_ARC +from .defines import CONF_ARC_LENGTH, CONF_INDICATOR, CONF_MAIN, CONF_SPIN_TIME +from .lv_validation import angle +from .lvcode import lv_expr +from .types import LvType +from .widget import Widget, WidgetType + +CONF_SPINNER = "spinner" + +SPINNER_SCHEMA = cv.Schema( + { + cv.Required(CONF_ARC_LENGTH): angle, + cv.Required(CONF_SPIN_TIME): cv.positive_time_period_milliseconds, + } +) + + +class SpinnerType(WidgetType): + def __init__(self): + super().__init__( + CONF_SPINNER, + LvType("lv_spinner_t"), + (CONF_MAIN, CONF_INDICATOR), + SPINNER_SCHEMA, + {}, + ) + + async def to_code(self, w: Widget, config): + return [] + + def get_uses(self): + return (CONF_ARC,) + + def obj_creator(self, parent: MockObjClass, config: dict): + spin_time = config[CONF_SPIN_TIME].total_milliseconds + arc_length = config[CONF_ARC_LENGTH] // 10 + return lv_expr.call("spinner_create", parent, spin_time, arc_length) + + +spinner_spec = SpinnerType() diff --git a/esphome/components/lvgl/styles.py b/esphome/components/lvgl/styles.py new file mode 100644 index 0000000000..7a795bc99d --- /dev/null +++ b/esphome/components/lvgl/styles.py @@ -0,0 +1,58 @@ +import esphome.codegen as cg +from esphome.const import CONF_ID +from esphome.core import ID +from esphome.cpp_generator import MockObj + +from .defines import ( + CONF_STYLE_DEFINITIONS, + CONF_THEME, + CONF_TOP_LAYER, + LValidator, + literal, +) +from .helpers import add_lv_use +from .lvcode import LambdaContext, LocalVariable, lv, lv_assign, lv_variable +from .obj import obj_spec +from .schemas import ALL_STYLES +from .types import lv_lambda_t, lv_obj_t, lv_obj_t_ptr +from .widget import Widget, add_widgets, set_obj_properties, theme_widget_map + +TOP_LAYER = literal("lv_disp_get_layer_top(lv_component->get_disp())") + + +async def styles_to_code(config): + """Convert styles to C__ code.""" + for style in config.get(CONF_STYLE_DEFINITIONS, ()): + svar = cg.new_Pvariable(style[CONF_ID]) + lv.style_init(svar) + for prop, validator in ALL_STYLES.items(): + if value := style.get(prop): + if isinstance(validator, LValidator): + value = await validator.process(value) + if isinstance(value, list): + value = "|".join(value) + lv.call(f"style_set_{prop}", svar, literal(value)) + + +async def theme_to_code(config): + if theme := config.get(CONF_THEME): + add_lv_use(CONF_THEME) + for w_name, style in theme.items(): + if not isinstance(style, dict): + continue + + lname = "lv_theme_apply_" + w_name + apply = lv_variable(lv_lambda_t, lname) + theme_widget_map[w_name] = apply + ow = Widget.create("obj", MockObj(ID("obj")), obj_spec) + async with LambdaContext([(lv_obj_t_ptr, "obj")], where=w_name) as context: + await set_obj_properties(ow, style) + lv_assign(apply, await context.get_lambda()) + + +async def add_top_layer(config): + if top_conf := config.get(CONF_TOP_LAYER): + with LocalVariable("top_layer", lv_obj_t, TOP_LAYER) as top_layer_obj: + top_w = Widget(top_layer_obj, obj_spec, top_conf) + await set_obj_properties(top_w, top_conf) + await add_widgets(top_w, top_conf) diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index bf92bda5b0..c640c8abd9 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -7,15 +7,14 @@ from .defines import ( CONF_ALIGN_TO, CONF_X, CONF_Y, - LV_EVENT, + LV_EVENT_MAP, LV_EVENT_TRIGGERS, literal, ) -from .lvcode import LambdaContext, add_line_marks, lv, lv_add +from .lvcode import EVENT_ARG, LambdaContext, LvConditional, lv, lv_add +from .types import LV_EVENT from .widget import widget_map -lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr") - async def generate_triggers(lv_component): """ @@ -34,15 +33,15 @@ async def generate_triggers(lv_component): }.items(): conf = conf[0] w.add_flag("LV_OBJ_FLAG_CLICKABLE") - event = "LV_EVENT_" + LV_EVENT[event[3:].upper()] + event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()]) await add_trigger(conf, event, lv_component, w) for conf in w.config.get(CONF_ON_VALUE, ()): - await add_trigger(conf, "LV_EVENT_VALUE_CHANGED", lv_component, w) + await add_trigger(conf, LV_EVENT.VALUE_CHANGED, lv_component, w) # Generate align to directives while we're here if align_to := w.config.get(CONF_ALIGN_TO): target = widget_map[align_to[CONF_ID]].obj - align = align_to[CONF_ALIGN] + align = literal(align_to[CONF_ALIGN]) x = align_to[CONF_X] y = align_to[CONF_Y] lv.obj_align_to(w.obj, target, align, x, y) @@ -50,12 +49,11 @@ async def generate_triggers(lv_component): async def add_trigger(conf, event, lv_component, w): tid = conf[CONF_TRIGGER_ID] - add_line_marks(tid) trigger = cg.new_Pvariable(tid) args = w.get_args() value = w.get_value() await automation.build_automation(trigger, args, conf) - with LambdaContext([(lv_event_t_ptr, "event_data")]) as context: - add_line_marks(tid) - lv_add(trigger.trigger(value)) - lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), literal(event))) + async with LambdaContext(EVENT_ARG, where=tid) as context: + with LvConditional(w.is_selected()): + lv_add(trigger.trigger(value)) + lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), event)) diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index 6997207dac..b6f65c8c1b 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -1,8 +1,11 @@ -from esphome import automation, codegen as cg -from esphome.core import ID -from esphome.cpp_generator import MockObjClass +import sys -from .defines import CONF_TEXT +from esphome import automation, codegen as cg +from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_VALUE +from esphome.cpp_generator import MockObj, MockObjClass + +from .defines import CONF_TEXT, lvgl_ns +from .lvcode import lv_expr class LvType(cg.MockObjClass): @@ -18,36 +21,48 @@ class LvType(cg.MockObjClass): return self.args[0][0] if len(self.args) else None +class LvNumber(LvType): + def __init__(self, *args): + super().__init__( + *args, + largs=[(cg.float_, "x")], + lvalue=lambda w: w.get_number_value(), + has_on_value=True, + ) + self.value_property = CONF_VALUE + + uint16_t_ptr = cg.uint16.operator("ptr") -lvgl_ns = cg.esphome_ns.namespace("lvgl") char_ptr = cg.global_ns.namespace("char").operator("ptr") void_ptr = cg.void.operator("ptr") -LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent) -LvglComponentPtr = LvglComponent.operator("ptr") -lv_event_code_t = cg.global_ns.namespace("lv_event_code_t") +lv_coord_t = cg.global_ns.namespace("lv_coord_t") +lv_event_code_t = cg.global_ns.enum("lv_event_code_t") lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t") FontEngine = lvgl_ns.class_("FontEngine") IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template()) ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action) LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition) LvglAction = lvgl_ns.class_("LvglAction", automation.Action) +lv_lambda_t = lvgl_ns.class_("LvLambdaType") LvCompound = lvgl_ns.class_("LvCompound") lv_font_t = cg.global_ns.class_("lv_font_t") lv_style_t = cg.global_ns.struct("lv_style_t") +# fake parent class for first class widgets and matrix buttons lv_pseudo_button_t = lvgl_ns.class_("LvPseudoButton") lv_obj_base_t = cg.global_ns.class_("lv_obj_t", lv_pseudo_button_t) lv_obj_t_ptr = lv_obj_base_t.operator("ptr") -lv_disp_t_ptr = cg.global_ns.struct("lv_disp_t").operator("ptr") +lv_disp_t = cg.global_ns.struct("lv_disp_t") lv_color_t = cg.global_ns.struct("lv_color_t") lv_group_t = cg.global_ns.struct("lv_group_t") LVTouchListener = lvgl_ns.class_("LVTouchListener") LVEncoderListener = lvgl_ns.class_("LVEncoderListener") lv_obj_t = LvType("lv_obj_t") +lv_page_t = cg.global_ns.class_("LvPageType", LvCompound) lv_img_t = LvType("lv_img_t") - -# this will be populated later, in __init__.py to avoid circular imports. -WIDGET_TYPES: dict = {} +LV_EVENT = MockObj(base="LV_EVENT_", op="") +LV_STATE = MockObj(base="LV_STATE_", op="") +LV_BTNMATRIX_CTRL = MockObj(base="LV_BTNMATRIX_CTRL_", op="") class LvText(LvType): @@ -55,7 +70,8 @@ class LvText(LvType): super().__init__( *args, largs=[(cg.std_string, "text")], - lvalue=lambda w: w.get_property("text")[0], + lvalue=lambda w: w.get_property("text"), + has_on_value=True, **kwargs, ) self.value_property = CONF_TEXT @@ -66,13 +82,21 @@ class LvBoolean(LvType): super().__init__( *args, largs=[(cg.bool_, "x")], - lvalue=lambda w: w.has_state("LV_STATE_CHECKED"), + lvalue=lambda w: w.is_checked(), has_on_value=True, **kwargs, ) -CUSTOM_EVENT = ID("lv_custom_event", False, type=lv_event_code_t) +class LvSelect(LvType): + def __init__(self, *args, **kwargs): + super().__init__( + *args, + largs=[(cg.int_, "x")], + lvalue=lambda w: w.get_property("selected"), + has_on_value=True, + **kwargs, + ) class WidgetType: @@ -80,7 +104,15 @@ class WidgetType: Describes a type of Widget, e.g. "bar" or "line" """ - def __init__(self, name, w_type, parts, schema=None, modify_schema=None): + def __init__( + self, + name: str, + w_type: LvType, + parts: tuple, + schema=None, + modify_schema=None, + lv_name=None, + ): """ :param name: The widget name, e.g. "bar" :param w_type: The C type of the widget @@ -89,6 +121,7 @@ class WidgetType: :param modify_schema: A schema to update the widget """ self.name = name + self.lv_name = lv_name or name self.w_type = w_type self.parts = parts if schema is None: @@ -98,7 +131,8 @@ class WidgetType: if modify_schema is None: self.modify_schema = self.schema else: - self.modify_schema = self.schema + self.modify_schema = modify_schema + self.mock_obj = MockObj(f"lv_{self.lv_name}", "_") @property def animated(self): @@ -118,7 +152,7 @@ class WidgetType: :param config: Its configuration :return: Generated code as a list of text lines """ - raise NotImplementedError(f"No to_code defined for {self.name}") + return [] def obj_creator(self, parent: MockObjClass, config: dict): """ @@ -127,7 +161,7 @@ class WidgetType: :param config: Its configuration :return: Generated code as a single text line """ - return f"lv_{self.name}_create({parent})" + return lv_expr.call(f"{self.lv_name}_create", parent) def get_uses(self): """ @@ -135,3 +169,23 @@ class WidgetType: :return: """ return () + + def get_max(self, config: dict): + return sys.maxsize + + def get_min(self, config: dict): + return -sys.maxsize + + def get_step(self, config: dict): + return 1 + + def get_scale(self, config: dict): + return 1.0 + + +class NumberType(WidgetType): + def get_max(self, config: dict): + return int(config[CONF_MAX_VALUE] or 100) + + def get_min(self, config: dict): + return int(config[CONF_MIN_VALUE] or 0) diff --git a/esphome/components/lvgl/widget.py b/esphome/components/lvgl/widget.py index 83aed341e7..5734aec7dc 100644 --- a/esphome/components/lvgl/widget.py +++ b/esphome/components/lvgl/widget.py @@ -1,33 +1,63 @@ import sys -from typing import Any +from typing import Any, Union from esphome import codegen as cg, config_validation as cv from esphome.config_validation import Invalid -from esphome.const import CONF_GROUP, CONF_ID, CONF_STATE -from esphome.core import CORE, TimePeriod +from esphome.const import CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE +from esphome.core import ID, TimePeriod from esphome.coroutine import FakeAwaitable -from esphome.cpp_generator import MockObj, MockObjClass, VariableDeclarationExpression +from esphome.cpp_generator import AssignmentExpression, CallExpression, MockObj from .defines import ( CONF_DEFAULT, + CONF_FLEX_ALIGN_CROSS, + CONF_FLEX_ALIGN_MAIN, + CONF_FLEX_ALIGN_TRACK, + CONF_FLEX_FLOW, + CONF_GRID_COLUMN_ALIGN, + CONF_GRID_COLUMNS, + CONF_GRID_ROW_ALIGN, + CONF_GRID_ROWS, + CONF_LAYOUT, CONF_MAIN, CONF_SCROLLBAR_MODE, + CONF_STYLES, CONF_WIDGETS, OBJ_FLAGS, PARTS, STATES, - ConstantLiteral, + TYPE_FLEX, + TYPE_GRID, LValidator, join_enums, literal, ) from .helpers import add_lv_use -from .lvcode import add_group, add_line_marks, lv, lv_add, lv_assign, lv_expr, lv_obj -from .schemas import ALL_STYLES, STYLE_REMAP -from .types import WIDGET_TYPES, LvType, WidgetType, lv_obj_t, lv_obj_t_ptr +from .lvcode import ( + LvConditional, + add_line_marks, + lv, + lv_add, + lv_assign, + lv_expr, + lv_obj, + lv_Pvariable, +) +from .schemas import ALL_STYLES, STYLE_REMAP, WIDGET_TYPES +from .types import ( + LV_STATE, + LvType, + WidgetType, + lv_coord_t, + lv_group_t, + lv_obj_t, + lv_obj_t_ptr, +) EVENT_LAMB = "event_lamb__" +theme_widget_map = {} + class LvScrActType(WidgetType): """ @@ -37,9 +67,6 @@ class LvScrActType(WidgetType): def __init__(self): super().__init__("lv_scr_act()", lv_obj_t, ()) - def obj_creator(self, parent: MockObjClass, config: dict): - return [] - async def to_code(self, w, config: dict): return [] @@ -55,7 +82,7 @@ class Widget: def set_completed(): Widget.widgets_completed = True - def __init__(self, var, wtype: WidgetType, config: dict = None, parent=None): + def __init__(self, var, wtype: WidgetType, config: dict = None): self.var = var self.type = wtype self.config = config @@ -63,21 +90,18 @@ class Widget: self.step = 1.0 self.range_from = -sys.maxsize self.range_to = sys.maxsize - self.parent = parent + if wtype.is_compound(): + self.obj = MockObj(f"{self.var}->obj") + else: + self.obj = var @staticmethod - def create(name, var, wtype: WidgetType, config: dict = None, parent=None): - w = Widget(var, wtype, config, parent) + def create(name, var, wtype: WidgetType, config: dict = None): + w = Widget(var, wtype, config) if name is not None: widget_map[name] = w return w - @property - def obj(self): - if self.type.is_compound(): - return f"{self.var}->obj" - return self.var - def add_state(self, state): return lv_obj.add_state(self.obj, literal(state)) @@ -85,7 +109,13 @@ class Widget: return lv_obj.clear_state(self.obj, literal(state)) def has_state(self, state): - return lv_expr.obj_get_state(self.obj) & literal(state) != 0 + return (lv_expr.obj_get_state(self.obj) & literal(state)) != 0 + + def is_pressed(self): + return self.has_state(LV_STATE.PRESSED) + + def is_checked(self): + return self.has_state(LV_STATE.CHECKED) def add_flag(self, flag): return lv_obj.add_flag(self.obj, literal(flag)) @@ -93,32 +123,37 @@ class Widget: def clear_flag(self, flag): return lv_obj.clear_flag(self.obj, literal(flag)) - def set_property(self, prop, value, animated: bool = None, ltype=None): + async def set_property(self, prop, value, animated: bool = None): if isinstance(value, dict): value = value.get(prop) + if isinstance(ALL_STYLES.get(prop), LValidator): + value = await ALL_STYLES[prop].process(value) + else: + value = literal(value) if value is None: return if isinstance(value, TimePeriod): value = value.total_milliseconds - ltype = ltype or self.__type_base() + if isinstance(value, str): + value = literal(value) if animated is None or self.type.animated is not True: - lv.call(f"{ltype}_set_{prop}", self.obj, value) + lv.call(f"{self.type.lv_name}_set_{prop}", self.obj, value) else: lv.call( - f"{ltype}_set_{prop}", + f"{self.type.lv_name}_set_{prop}", self.obj, value, - "LV_ANIM_ON" if animated else "LV_ANIM_OFF", + literal("LV_ANIM_ON" if animated else "LV_ANIM_OFF"), ) def get_property(self, prop, ltype=None): ltype = ltype or self.__type_base() - return f"lv_{ltype}_get_{prop}({self.obj})" + return cg.RawExpression(f"lv_{ltype}_get_{prop}({self.obj})") def set_style(self, prop, value, state): if value is None: - return [] - return lv.call(f"obj_set_style_{prop}", self.obj, value, state) + return + lv.call(f"obj_set_style_{prop}", self.obj, value, state) def __type_base(self): wtype = self.type.w_type @@ -140,6 +175,32 @@ class Widget: return self.type.w_type.value(self) return self.obj + def get_number_value(self): + value = self.type.mock_obj.get_value(self.obj) + if self.scale == 1.0: + return value + return value / float(self.scale) + + def is_selected(self): + """ + Overridable property to determine if the widget is selected. Will be None except + for matrix buttons + :return: + """ + return None + + def get_max(self): + return self.type.get_max(self.config) + + def get_min(self): + return self.type.get_min(self.config) + + def get_step(self): + return self.type.get_step(self.config) + + def get_scale(self): + return self.type.get_scale(self.config) + # Map of widgets to their config, used for trigger generation widget_map: dict[Any, Widget] = {} @@ -161,13 +222,20 @@ def get_widget_generator(wid): yield -async def get_widget(config: dict, id: str = CONF_ID) -> Widget: - wid = config[id] +async def get_widget_(wid: Widget): if obj := widget_map.get(wid): return obj return await FakeAwaitable(get_widget_generator(wid)) +async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: + if not config: + return [] + if not isinstance(config, list): + config = [config] + return [await get_widget_(c[id]) for c in config if id in c] + + def collect_props(config): """ Collect all properties from a configuration @@ -175,7 +243,7 @@ def collect_props(config): :return: """ props = {} - for prop in [*ALL_STYLES, *OBJ_FLAGS, CONF_GROUP]: + for prop in [*ALL_STYLES, *OBJ_FLAGS, CONF_STYLES, CONF_GROUP]: if prop in config: props[prop] = config[prop] return props @@ -209,12 +277,39 @@ def collect_parts(config): async def set_obj_properties(w: Widget, config): """Generate a list of C++ statements to apply properties to an lv_obj_t""" + if layout := config.get(CONF_LAYOUT): + layout_type: str = layout[CONF_TYPE] + lv_obj.set_layout(w.obj, literal(f"LV_LAYOUT_{layout_type.upper()}")) + if layout_type == TYPE_GRID: + wid = config[CONF_ID] + rows = "{" + ",".join(layout[CONF_GRID_ROWS]) + ", LV_GRID_TEMPLATE_LAST}" + row_id = ID(f"{wid}_row_dsc", is_declaration=True, type=lv_coord_t) + row_array = cg.static_const_array(row_id, cg.RawExpression(rows)) + w.set_style("grid_row_dsc_array", row_array, 0) + columns = ( + "{" + ",".join(layout[CONF_GRID_COLUMNS]) + ", LV_GRID_TEMPLATE_LAST}" + ) + column_id = ID(f"{wid}_column_dsc", is_declaration=True, type=lv_coord_t) + column_array = cg.static_const_array(column_id, cg.RawExpression(columns)) + w.set_style("grid_column_dsc_array", column_array, 0) + w.set_style( + CONF_GRID_COLUMN_ALIGN, literal(layout.get(CONF_GRID_COLUMN_ALIGN)), 0 + ) + w.set_style( + CONF_GRID_ROW_ALIGN, literal(layout.get(CONF_GRID_ROW_ALIGN)), 0 + ) + if layout_type == TYPE_FLEX: + lv_obj.set_flex_flow(w.obj, literal(layout[CONF_FLEX_FLOW])) + main = literal(layout[CONF_FLEX_ALIGN_MAIN]) + cross = literal(layout[CONF_FLEX_ALIGN_CROSS]) + track = literal(layout[CONF_FLEX_ALIGN_TRACK]) + lv_obj.set_flex_align(w.obj, main, cross, track) parts = collect_parts(config) for part, states in parts.items(): for state, props in states.items(): - lv_state = ConstantLiteral( - f"(int)LV_STATE_{state.upper()}|(int)LV_PART_{part.upper()}" - ) + lv_state = join_enums((f"LV_STATE_{state}", f"LV_PART_{part}")) + for style_id in props.get(CONF_STYLES, ()): + lv_obj.add_style(w.obj, MockObj(style_id), lv_state) for prop, value in { k: v for k, v in props.items() if k in ALL_STYLES }.items(): @@ -258,14 +353,12 @@ async def set_obj_properties(w: Widget, config): w.clear_state(clears) for key, value in lambs.items(): lamb = await cg.process_lambda(value, [], return_type=cg.bool_) - state = f"LV_STATE_{key.upper}" - lv.cond_if(lamb) - w.add_state(state) - lv.cond_else() - w.clear_state(state) - lv.cond_endif() - if scrollbar_mode := config.get(CONF_SCROLLBAR_MODE): - lv_obj.set_scrollbar_mode(w.obj, scrollbar_mode) + state = f"LV_STATE_{key.upper()}" + with LvConditional(f"{lamb}()") as cond: + w.add_state(state) + cond.else_() + w.clear_state(state) + await w.set_property(CONF_SCROLLBAR_MODE, config) async def add_widgets(parent: Widget, config: dict): @@ -280,7 +373,7 @@ async def add_widgets(parent: Widget, config: dict): await widget_to_code(w_cnfig, w_type, parent.obj) -async def widget_to_code(w_cnfig, w_type, parent): +async def widget_to_code(w_cnfig, w_type: WidgetType, parent): """ Converts a Widget definition to C code. :param w_cnfig: The widget configuration @@ -298,19 +391,33 @@ async def widget_to_code(w_cnfig, w_type, parent): var = cg.new_Pvariable(wid) lv_add(var.set_obj(creator)) else: - var = MockObj(wid, "->") - decl = VariableDeclarationExpression(lv_obj_t, "*", wid) - CORE.add_global(decl) - CORE.register_variable(wid, var) + var = lv_Pvariable(lv_obj_t, wid) lv_assign(var, creator) - widget = Widget.create(wid, var, spec, w_cnfig, parent) - await set_obj_properties(widget, w_cnfig) - await add_widgets(widget, w_cnfig) - await spec.to_code(widget, w_cnfig) + w = Widget.create(wid, var, spec, w_cnfig) + if theme := theme_widget_map.get(w_type): + lv_add(CallExpression(theme, w.obj)) + await set_obj_properties(w, w_cnfig) + await add_widgets(w, w_cnfig) + await spec.to_code(w, w_cnfig) lv_scr_act_spec = LvScrActType() -lv_scr_act = Widget.create( - None, ConstantLiteral("lv_scr_act()"), lv_scr_act_spec, {}, parent=None -) +lv_scr_act = Widget.create(None, literal("lv_scr_act()"), lv_scr_act_spec, {}) + +lv_groups = {} # Widget group names + + +def add_group(name): + if name is None: + return None + fullname = f"lv_esp_group_{name}" + if name not in lv_groups: + gid = ID(fullname, True, type=lv_group_t.operator("ptr")) + lv_add( + AssignmentExpression( + type_=gid.type, modifier="", name=fullname, rhs=lv_expr.group_create() + ) + ) + lv_groups[name] = literal(fullname) + return lv_groups[name] diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index fde700e0bd..0cca45d376 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -5,142 +5,229 @@ lvgl: - touchscreen_id: tft_touch long_press_repeat_time: 200ms long_press_time: 500ms - widgets: - - label: - id: hello_label - text: Hello world - text_color: 0xFF8000 - align: center - text_font: montserrat_40 - border_post: true - - - label: - text: "Hello shiny day" - text_color: 0xFFFFFF - align: bottom_mid - text_font: space16 - - obj: - align: center - arc_opa: COVER - arc_color: 0xFF0000 - arc_rounded: false - arc_width: 3 - anim_time: 1s - bg_color: light_blue - bg_grad_color: light_blue - bg_dither_mode: ordered - bg_grad_dir: hor - bg_grad_stop: 128 - bg_image_opa: transp - bg_image_recolor: light_blue - bg_image_recolor_opa: 50% - bg_main_stop: 0 - bg_opa: 20% - border_color: 0x00FF00 - border_opa: cover - border_post: true - border_side: [bottom, left] - border_width: 4 - clip_corner: false - height: 50% - image_recolor: light_blue - image_recolor_opa: cover - line_width: 10 - line_dash_width: 10 - line_dash_gap: 10 - line_rounded: false - line_color: light_blue - opa: cover - opa_layered: cover - outline_color: light_blue - outline_opa: cover - outline_pad: 10px - outline_width: 10px - pad_all: 10px - pad_bottom: 10px - pad_column: 10px - pad_left: 10px - pad_right: 10px - pad_row: 10px - pad_top: 10px - shadow_color: light_blue - shadow_ofs_x: 5 - shadow_ofs_y: 5 - shadow_opa: cover - shadow_spread: 5 - shadow_width: 10 - text_align: auto - text_color: light_blue - text_decor: [underline, strikethrough] - text_font: montserrat_18 - text_letter_space: 4 - text_line_space: 4 - text_opa: cover - transform_angle: 180 - transform_height: 100 - transform_pivot_x: 50% - transform_pivot_y: 50% - transform_zoom: 0.5 - translate_x: 10 - translate_y: 10 - max_height: 100 - max_width: 200 - min_height: 20% - min_width: 20% - radius: circle - width: 10px - x: 100 - y: 120 - - button: - width: 20% - height: 10% - pressed: - bg_color: light_blue - checkable: true - checked: - bg_color: 0x000000 - widgets: - - label: - text: Button - on_click: - lvgl.label.update: + pages: + - id: page1 + skip: true + widgets: + - label: id: hello_label - bg_color: 0x123456 - text: clicked - on_value: - logger.log: - format: "state now %d" - args: [x] - on_short_click: - lvgl.widget.hide: hello_label - on_long_press: - lvgl.widget.show: hello_label - on_cancel: - lvgl.widget.enable: hello_label - on_ready: - lvgl.widget.disable: hello_label - on_defocus: - lvgl.widget.hide: hello_label - on_focus: - logger.log: Button clicked - on_scroll: - logger.log: Button clicked - on_scroll_end: - logger.log: Button clicked - on_scroll_begin: - logger.log: Button clicked - on_release: - logger.log: Button clicked - on_long_press_repeat: - logger.log: Button clicked + text: Hello world + text_color: 0xFF8000 + align: center + text_font: montserrat_40 + border_post: true + - label: + text: "Hello shiny day" + text_color: 0xFFFFFF + align: bottom_mid + text_font: space16 + - obj: + align: center + arc_opa: COVER + arc_color: 0xFF0000 + arc_rounded: false + arc_width: 3 + anim_time: 1s + bg_color: light_blue + bg_grad_color: light_blue + bg_dither_mode: ordered + bg_grad_dir: hor + bg_grad_stop: 128 + bg_image_opa: transp + bg_image_recolor: light_blue + bg_image_recolor_opa: 50% + bg_main_stop: 0 + bg_opa: 20% + border_color: 0x00FF00 + border_opa: cover + border_post: true + border_side: [bottom, left] + border_width: 4 + clip_corner: false + height: 50% + image_recolor: light_blue + image_recolor_opa: cover + line_width: 10 + line_dash_width: 10 + line_dash_gap: 10 + line_rounded: false + line_color: light_blue + opa: cover + opa_layered: cover + outline_color: light_blue + outline_opa: cover + outline_pad: 10px + outline_width: 10px + pad_all: 10px + pad_bottom: 10px + pad_column: 10px + pad_left: 10px + pad_right: 10px + pad_row: 10px + pad_top: 10px + shadow_color: light_blue + shadow_ofs_x: 5 + shadow_ofs_y: 5 + shadow_opa: cover + shadow_spread: 5 + shadow_width: 10 + text_align: auto + text_color: light_blue + text_decor: [underline, strikethrough] + text_font: montserrat_18 + text_letter_space: 4 + text_line_space: 4 + text_opa: cover + transform_angle: 180 + transform_height: 100 + transform_pivot_x: 50% + transform_pivot_y: 50% + transform_zoom: 0.5 + translate_x: 10 + translate_y: 10 + max_height: 100 + max_width: 200 + min_height: 20% + min_width: 20% + radius: circle + width: 10px + x: 100 + y: 120 + - button: + width: 20% + height: 10% + pressed: + bg_color: light_blue + checkable: true + checked: + bg_color: 0x000000 + widgets: + - label: + text: Button + on_click: + lvgl.label.update: + id: hello_label + bg_color: 0x123456 + text: clicked + on_value: + logger.log: + format: "state now %d" + args: [x] + on_short_click: + lvgl.widget.hide: hello_label + on_long_press: + lvgl.widget.show: hello_label + on_cancel: + lvgl.widget.enable: hello_label + on_ready: + lvgl.widget.disable: hello_label + on_defocus: + lvgl.widget.hide: hello_label + on_focus: + logger.log: Button clicked + on_scroll: + logger.log: Button clicked + on_scroll_end: + logger.log: Button clicked + on_scroll_begin: + logger.log: Button clicked + on_release: + logger.log: Button clicked + on_long_press_repeat: + logger.log: Button clicked + - led: + color: 0x00FF00 + brightness: 50% + align: right_mid + - spinner: + arc_length: 120 + spin_time: 2s + align: left_mid + - image: + src: cat_image + align: top_left + y: 50 + + - id: page2 + widgets: + - arc: + align: left_mid + id: lv_arc + adjustable: true + on_value: + then: + - logger.log: + format: "Arc value is %f" + args: [x] + group: general + scroll_on_focus: true + value: 75 + min_value: 1 + max_value: 100 + arc_color: 0xFF0000 + indicator: + arc_color: 0xF000FF + pressed: + arc_color: 0xFFFF00 + focused: + arc_color: 0x808080 + - bar: + id: bar_id + align: top_mid + y: 20 + value: 30 + max_value: 100 + min_value: 10 + mode: range + on_click: + then: + - lvgl.bar.update: + id: bar_id + value: !lambda return (int)((float)rand() / RAND_MAX * 100); + - logger.log: + format: "bar value %f" + args: [x] + - line: + align: center + points: + - 5, 5 + - 70, 70 + - 120, 10 + - 180, 60 + - 240, 10 + on_click: + lvgl.page.next: + - switch: + align: right_mid + - checkbox: + text: Checkbox + align: bottom_right + - slider: + id: slider_id + align: top_mid + y: 40 + value: 30 + max_value: 100 + min_value: 10 + mode: normal + on_value: + then: + - logger.log: + format: "slider value %f" + args: [x] + on_click: + then: + - lvgl.slider.update: + id: slider_id + value: !lambda return (int)((float)rand() / RAND_MAX * 100); font: - file: "gfonts://Roboto" id: space16 bpp: 4 image: - - id: cat_img + - id: cat_image resize: 256x48 file: $component_dir/logo-text.svg - id: dog_img From e02319dcff75a003b5551866c701dd81803022c8 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Mon, 5 Aug 2024 12:09:54 -0400 Subject: [PATCH 0132/1052] [esp32_improv] Update Improv library to reference new repo/version (#7195) --- esphome/components/esp32_improv/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 62d9cd376c..705dff0f1b 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,9 +1,8 @@ import esphome.codegen as cg +from esphome.components import binary_sensor, esp32_ble_server, output import esphome.config_validation as cv -from esphome.components import binary_sensor, output, esp32_ble_server from esphome.const import CONF_ID - AUTO_LOAD = ["esp32_ble_server"] CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["wifi", "esp32"] @@ -50,7 +49,7 @@ async def to_code(config): cg.add(ble_server.register_service_component(var)) cg.add_define("USE_IMPROV") - cg.add_library("esphome/Improv", "1.2.3") + cg.add_library("improv/Improv", "1.2.4") cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION])) cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION])) From f737ca6e286f36e243b5bae1bcb7515b9a4bf857 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Mon, 5 Aug 2024 23:17:02 +0200 Subject: [PATCH 0133/1052] hydreon_rgxx: Fix parsing of data line (#7192) --- esphome/components/hydreon_rgxx/hydreon_rgxx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp index 95702fe9e8..92d7774193 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp @@ -236,7 +236,7 @@ void HydreonRGxxComponent::process_line_() { } bool is_data_line = false; for (int i = 0; i < NUM_SENSORS; i++) { - if (this->sensors_[i] != nullptr && this->buffer_starts_with_(PROTOCOL_NAMES[i])) { + if (this->sensors_[i] != nullptr && this->buffer_.find(PROTOCOL_NAMES[i]) != std::string::npos) { is_data_line = true; break; } From acaec41bb765949f37c04cf34b77e0f73df26272 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 6 Aug 2024 01:40:34 +0200 Subject: [PATCH 0134/1052] Remove outdated version block (#7177) --- esphome/__main__.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 7237a04717..5c197ff486 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -972,13 +972,6 @@ def run_esphome(argv): args.command == "dashboard", ) - if sys.version_info < (3, 8, 0): - _LOGGER.error( - "You're running ESPHome with Python <3.8. ESPHome is no longer compatible " - "with this Python version. Please reinstall ESPHome with Python 3.8+" - ) - return 1 - if args.command in PRE_CONFIG_ACTIONS: try: return PRE_CONFIG_ACTIONS[args.command](args) From 6b141102d62930778552db1f2bdf23bdc20b1d86 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:17:29 +1000 Subject: [PATCH 0135/1052] [lvgl] Stage 5 (#7191) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/lvgl/__init__.py | 25 +- esphome/components/lvgl/animimg.py | 2 +- esphome/components/lvgl/automation.py | 2 +- esphome/components/lvgl/{btn.py => button.py} | 8 +- esphome/components/lvgl/buttonmatrix.py | 277 ++++++++++++++++ esphome/components/lvgl/checkbox.py | 2 +- esphome/components/lvgl/defines.py | 2 +- esphome/components/lvgl/dropdown.py | 76 +++++ esphome/components/lvgl/img.py | 8 +- esphome/components/lvgl/keyboard.py | 49 +++ esphome/components/lvgl/led.py | 4 +- esphome/components/lvgl/lvgl_esphome.cpp | 4 +- esphome/components/lvgl/lvgl_esphome.h | 2 +- esphome/components/lvgl/meter.py | 302 ++++++++++++++++++ esphome/components/lvgl/msgbox.py | 127 ++++++++ esphome/components/lvgl/roller.py | 77 +++++ esphome/components/lvgl/spinbox.py | 178 +++++++++++ esphome/components/lvgl/styles.py | 2 +- esphome/components/lvgl/tabview.py | 114 +++++++ esphome/components/lvgl/textarea.py | 67 ++++ esphome/components/lvgl/tileview.py | 128 ++++++++ esphome/components/lvgl/widget.py | 8 +- esphome/core/defines.h | 3 + tests/components/lvgl/.gitattributes | 2 + tests/components/lvgl/common.yaml | 46 +++ tests/components/lvgl/helvetica.ttf | Bin 0 -> 83644 bytes tests/components/lvgl/lvgl-package.yaml | 228 ++++++++++++- .../lvgl/materialdesignicons-webfont.ttf | Bin 0 -> 1307419 bytes tests/components/lvgl/roboto.ttf | Bin 0 -> 171676 bytes 29 files changed, 1716 insertions(+), 27 deletions(-) rename esphome/components/lvgl/{btn.py => button.py} (58%) create mode 100644 esphome/components/lvgl/buttonmatrix.py create mode 100644 esphome/components/lvgl/dropdown.py create mode 100644 esphome/components/lvgl/keyboard.py create mode 100644 esphome/components/lvgl/meter.py create mode 100644 esphome/components/lvgl/msgbox.py create mode 100644 esphome/components/lvgl/roller.py create mode 100644 esphome/components/lvgl/spinbox.py create mode 100644 esphome/components/lvgl/tabview.py create mode 100644 esphome/components/lvgl/textarea.py create mode 100644 esphome/components/lvgl/tileview.py create mode 100644 tests/components/lvgl/.gitattributes create mode 100644 tests/components/lvgl/helvetica.ttf create mode 100644 tests/components/lvgl/materialdesignicons-webfont.ttf create mode 100644 tests/components/lvgl/roboto.ttf diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index c154689199..a963fca98b 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -24,10 +24,13 @@ from . import defines as df, helpers, lv_validation as lvalid from .animimg import animimg_spec from .arc import arc_spec from .automation import disp_update, update_to_code -from .btn import btn_spec +from .button import button_spec +from .buttonmatrix import buttonmatrix_spec from .checkbox import checkbox_spec from .defines import CONF_SKIP +from .dropdown import dropdown_spec from .img import img_spec +from .keyboard import keyboard_spec from .label import label_spec from .led import led_spec from .line import line_spec @@ -35,8 +38,11 @@ from .lv_bar import bar_spec from .lv_switch import switch_spec from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent +from .meter import meter_spec +from .msgbox import MSGBOX_SCHEMA, msgboxes_to_code from .obj import obj_spec from .page import add_pages, page_spec +from .roller import roller_spec from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code from .schemas import ( DISP_BG_SCHEMA, @@ -52,8 +58,12 @@ from .schemas import ( obj_schema, ) from .slider import slider_spec +from .spinbox import spinbox_spec from .spinner import spinner_spec from .styles import add_top_layer, styles_to_code, theme_to_code +from .tabview import tabview_spec +from .textarea import textarea_spec +from .tileview import tileview_spec from .touchscreens import touchscreen_schema, touchscreens_to_code from .trigger import generate_triggers from .types import ( @@ -75,7 +85,7 @@ LOGGER = logging.getLogger(__name__) for w_type in ( label_spec, obj_spec, - btn_spec, + button_spec, bar_spec, slider_spec, arc_spec, @@ -86,6 +96,15 @@ for w_type in ( checkbox_spec, img_spec, switch_spec, + tabview_spec, + buttonmatrix_spec, + meter_spec, + dropdown_spec, + roller_spec, + textarea_spec, + spinbox_spec, + keyboard_spec, + tileview_spec, ): WIDGET_TYPES[w_type.name] = w_type @@ -244,6 +263,7 @@ async def to_code(config): await add_widgets(lv_scr_act, config) await add_pages(lv_component, config) await add_top_layer(config) + await msgboxes_to_code(config) await disp_update(f"{lv_component}->get_disp()", config) Widget.set_completed() await generate_triggers(lv_component) @@ -308,6 +328,7 @@ CONFIG_SCHEMA = ( cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list( container_schema(page_spec) ), + cv.Optional(df.CONF_MSGBOXES): cv.ensure_list(MSGBOX_SCHEMA), cv.Optional(df.CONF_PAGE_WRAP, default=True): lv_bool, cv.Optional(df.CONF_TOP_LAYER): container_schema(obj_spec), cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color, diff --git a/esphome/components/lvgl/animimg.py b/esphome/components/lvgl/animimg.py index 20b85b019c..ad84713d7f 100644 --- a/esphome/components/lvgl/animimg.py +++ b/esphome/components/lvgl/animimg.py @@ -2,8 +2,8 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_DURATION, CONF_ID +from esphome.cpp_generator import MockObj -from ...cpp_generator import MockObj from .automation import action_to_code from .defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC from .helpers import lvgl_components_required diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index ffa25783ad..7a862fb58b 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -109,7 +109,7 @@ async def disp_update(disp, config: dict): if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config: return with LocalVariable("lv_disp_tmp", lv_disp_t, literal(disp)) as disp_temp: - if bg_color := config.get(CONF_DISP_BG_COLOR): + if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None: lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color)) if bg_image := config.get(CONF_DISP_BG_IMAGE): lv.disp_set_bg_image(disp_temp, await lv_image.process(bg_image)) diff --git a/esphome/components/lvgl/btn.py b/esphome/components/lvgl/button.py similarity index 58% rename from esphome/components/lvgl/btn.py rename to esphome/components/lvgl/button.py index 2a2a53e1e2..96329b3fa9 100644 --- a/esphome/components/lvgl/btn.py +++ b/esphome/components/lvgl/button.py @@ -3,12 +3,12 @@ from esphome.const import CONF_BUTTON from .defines import CONF_MAIN from .types import LvBoolean, WidgetType -lv_btn_t = LvBoolean("lv_btn_t") +lv_button_t = LvBoolean("lv_btn_t") -class BtnType(WidgetType): +class ButtonType(WidgetType): def __init__(self): - super().__init__(CONF_BUTTON, lv_btn_t, (CONF_MAIN,), lv_name="btn") + super().__init__(CONF_BUTTON, lv_button_t, (CONF_MAIN,), lv_name="btn") def get_uses(self): return ("btn",) @@ -17,4 +17,4 @@ class BtnType(WidgetType): return [] -btn_spec = BtnType() +button_spec = ButtonType() diff --git a/esphome/components/lvgl/buttonmatrix.py b/esphome/components/lvgl/buttonmatrix.py new file mode 100644 index 0000000000..75ed43f909 --- /dev/null +++ b/esphome/components/lvgl/buttonmatrix.py @@ -0,0 +1,277 @@ +from esphome import automation +import esphome.codegen as cg +from esphome.components.key_provider import KeyProvider +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_WIDTH +from esphome.cpp_generator import MockObj + +from .automation import action_to_code +from .button import lv_button_t +from .defines import ( + BUTTONMATRIX_CTRLS, + CONF_BUTTONS, + CONF_CONTROL, + CONF_ITEMS, + CONF_KEY_CODE, + CONF_MAIN, + CONF_ONE_CHECKED, + CONF_ROWS, + CONF_SELECTED, + CONF_TEXT, +) +from .helpers import lvgl_components_required +from .lv_validation import key_code, lv_bool +from .lvcode import lv, lv_add, lv_expr +from .schemas import automation_schema +from .types import ( + LV_BTNMATRIX_CTRL, + LV_STATE, + LvBoolean, + LvCompound, + LvType, + ObjUpdateAction, + char_ptr, + lv_pseudo_button_t, +) +from .widget import Widget, WidgetType, get_widgets, widget_map + +CONF_BUTTONMATRIX = "buttonmatrix" +CONF_BUTTON_TEXT_LIST_ID = "button_text_list_id" + +LvButtonMatrixButton = LvBoolean( + str(cg.uint16), + parents=(lv_pseudo_button_t,), +) +BUTTONMATRIX_BUTTON_SCHEMA = cv.Schema( + { + cv.Optional(CONF_TEXT): cv.string, + cv.Optional(CONF_KEY_CODE): key_code, + cv.GenerateID(): cv.declare_id(LvButtonMatrixButton), + cv.Optional(CONF_WIDTH, default=1): cv.positive_int, + cv.Optional(CONF_CONTROL): cv.ensure_list( + cv.Schema( + {cv.Optional(k.lower()): cv.boolean for k in BUTTONMATRIX_CTRLS.choices} + ) + ), + } +).extend(automation_schema(lv_button_t)) + +BUTTONMATRIX_SCHEMA = cv.Schema( + { + cv.Optional(CONF_ONE_CHECKED, default=False): lv_bool, + cv.GenerateID(CONF_BUTTON_TEXT_LIST_ID): cv.declare_id(char_ptr), + cv.Required(CONF_ROWS): cv.ensure_list( + cv.Schema( + { + cv.Required(CONF_BUTTONS): cv.ensure_list( + BUTTONMATRIX_BUTTON_SCHEMA + ), + } + ) + ), + } +) + + +class ButtonmatrixButtonType(WidgetType): + """ + A pseudo-widget for the matrix buttons + """ + + def __init__(self): + super().__init__("btnmatrix_btn", LvButtonMatrixButton, (), {}, {}) + + async def to_code(self, w, config: dict): + return [] + + +btn_btn_spec = ButtonmatrixButtonType() + + +class MatrixButton(Widget): + """ + Describes a button within a button matrix. + """ + + @staticmethod + def create_button(id, parent, config: dict, index): + w = MatrixButton(id, parent, config, index) + widget_map[id] = w + return w + + def __init__(self, id, parent: Widget, config, index): + super().__init__(id, btn_btn_spec, config) + self.parent = parent + self.index = index + self.obj = parent.obj + + def is_selected(self): + return self.parent.var.get_selected() == MockObj(self.var) + + @staticmethod + def map_ctrls(state): + state = str(state).upper().removeprefix("LV_STATE_") + assert state in BUTTONMATRIX_CTRLS.choices + return getattr(LV_BTNMATRIX_CTRL, state) + + def has_state(self, state): + state = self.map_ctrls(state) + return lv_expr.btnmatrix_has_btn_ctrl(self.obj, self.index, state) + + def add_state(self, state): + state = self.map_ctrls(state) + return lv.btnmatrix_set_btn_ctrl(self.obj, self.index, state) + + def clear_state(self, state): + state = self.map_ctrls(state) + return lv.btnmatrix_clear_btn_ctrl(self.obj, self.index, state) + + def is_pressed(self): + return self.is_selected() & self.parent.has_state(LV_STATE.PRESSED) + + def is_checked(self): + return self.has_state(LV_STATE.CHECKED) + + def get_value(self): + return self.is_checked() + + def check_null(self): + return None + + +async def get_button_data(config, buttonmatrix: Widget): + """ + Process a button matrix button list + :param config: The row list + :param buttonmatrix: The parent variable + :return: text array id, control list, width list + """ + text_list = [] + ctrl_list = [] + width_list = [] + key_list = [] + for row in config: + for button_conf in row.get(CONF_BUTTONS) or (): + bid = button_conf[CONF_ID] + index = len(width_list) + MatrixButton.create_button(bid, buttonmatrix, button_conf, index) + cg.new_variable(bid, index) + text_list.append(button_conf.get(CONF_TEXT) or "") + key_list.append(button_conf.get(CONF_KEY_CODE) or 0) + width_list.append(button_conf[CONF_WIDTH]) + ctrl = ["LV_BTNMATRIX_CTRL_CLICK_TRIG"] + for item in button_conf.get(CONF_CONTROL, ()): + ctrl.extend([k for k, v in item.items() if v]) + ctrl_list.append(await BUTTONMATRIX_CTRLS.process(ctrl)) + text_list.append("\n") + text_list = text_list[:-1] + text_list.append(cg.nullptr) + return text_list, ctrl_list, width_list, key_list + + +lv_buttonmatrix_t = LvType( + "LvButtonMatrixType", + parents=(KeyProvider, LvCompound), + largs=[(cg.uint16, "x")], + lvalue=lambda w: w.var.get_selected(), +) + + +class ButtonMatrixType(WidgetType): + def __init__(self): + super().__init__( + CONF_BUTTONMATRIX, + lv_buttonmatrix_t, + (CONF_MAIN, CONF_ITEMS), + BUTTONMATRIX_SCHEMA, + {}, + lv_name="btnmatrix", + ) + + async def to_code(self, w: Widget, config): + lvgl_components_required.add("BUTTONMATRIX") + if CONF_ROWS not in config: + return [] + text_list, ctrl_list, width_list, key_list = await get_button_data( + config[CONF_ROWS], w + ) + text_id = config[CONF_BUTTON_TEXT_LIST_ID] + text_id = cg.static_const_array(text_id, text_list) + lv.btnmatrix_set_map(w.obj, text_id) + set_btn_data(w.obj, ctrl_list, width_list) + lv.btnmatrix_set_one_checked(w.obj, config[CONF_ONE_CHECKED]) + for index, key in enumerate(key_list): + if key != 0: + lv_add(w.var.set_key(index, key)) + + def get_uses(self): + return ("btnmatrix",) + + +def set_btn_data(obj, ctrl_list, width_list): + for index, ctrl in enumerate(ctrl_list): + lv.btnmatrix_set_btn_ctrl(obj, index, ctrl) + for index, width in enumerate(width_list): + lv.btnmatrix_set_btn_width(obj, index, width) + + +buttonmatrix_spec = ButtonMatrixType() + + +@automation.register_action( + "lvgl.matrix.button.update", + ObjUpdateAction, + cv.Schema( + { + cv.Optional(CONF_WIDTH): cv.positive_int, + cv.Optional(CONF_CONTROL): cv.ensure_list( + cv.Schema( + { + cv.Optional(k.lower()): cv.boolean + for k in BUTTONMATRIX_CTRLS.choices + } + ), + ), + cv.Required(CONF_ID): cv.ensure_list( + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(LvButtonMatrixButton), + }, + key=CONF_ID, + ) + ), + cv.Optional(CONF_SELECTED): lv_bool, + } + ), +) +async def button_update_to_code(config, action_id, template_arg, args): + widgets = await get_widgets(config[CONF_ID]) + assert all(isinstance(w, MatrixButton) for w in widgets) + + async def do_button_update(w: MatrixButton): + if (width := config.get(CONF_WIDTH)) is not None: + lv.btnmatrix_set_btn_width(w.obj, w.index, width) + if config.get(CONF_SELECTED): + lv.btnmatrix_set_selected_btn(w.obj, w.index) + if controls := config.get(CONF_CONTROL): + adds = [] + clrs = [] + for item in controls: + adds.extend( + [f"LV_BTNMATRIX_CTRL_{k.upper()}" for k, v in item.items() if v] + ) + clrs.extend( + [f"LV_BTNMATRIX_CTRL_{k.upper()}" for k, v in item.items() if not v] + ) + if adds: + lv.btnmatrix_set_btn_ctrl( + w.obj, w.index, await BUTTONMATRIX_CTRLS.process(adds) + ) + if clrs: + lv.btnmatrix_clear_btn_ctrl( + w.obj, w.index, await BUTTONMATRIX_CTRLS.process(clrs) + ) + + return await action_to_code( + widgets, do_button_update, action_id, template_arg, args + ) diff --git a/esphome/components/lvgl/checkbox.py b/esphome/components/lvgl/checkbox.py index 7418d633cf..be7b029269 100644 --- a/esphome/components/lvgl/checkbox.py +++ b/esphome/components/lvgl/checkbox.py @@ -18,7 +18,7 @@ class CheckboxType(WidgetType): ) async def to_code(self, w: Widget, config): - if value := config.get(CONF_TEXT): + if (value := config.get(CONF_TEXT)) is not None: lv.checkbox_set_text(w.obj, await lv_text.process(value)) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 16ec45ae8a..ac28f9ed5f 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -304,7 +304,7 @@ OBJ_FLAGS = ( ARC_MODES = LvConstant("LV_ARC_MODE_", "NORMAL", "REVERSE", "SYMMETRICAL") BAR_MODES = LvConstant("LV_BAR_MODE_", "NORMAL", "SYMMETRICAL", "RANGE") -BTNMATRIX_CTRLS = LvConstant( +BUTTONMATRIX_CTRLS = LvConstant( "LV_BTNMATRIX_CTRL_", "HIDDEN", "NO_REPEAT", diff --git a/esphome/components/lvgl/dropdown.py b/esphome/components/lvgl/dropdown.py new file mode 100644 index 0000000000..d7bdebaade --- /dev/null +++ b/esphome/components/lvgl/dropdown.py @@ -0,0 +1,76 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_OPTIONS + +from .defines import ( + CONF_DIR, + CONF_INDICATOR, + CONF_MAIN, + CONF_SELECTED_INDEX, + CONF_SYMBOL, + DIRECTIONS, + literal, +) +from .label import CONF_LABEL +from .lv_validation import lv_int, lv_text, option_string +from .lvcode import LocalVariable, lv, lv_expr +from .schemas import part_schema +from .types import LvSelect, LvType, lv_obj_t +from .widget import Widget, WidgetType, set_obj_properties + +CONF_DROPDOWN = "dropdown" +CONF_DROPDOWN_LIST = "dropdown_list" + +lv_dropdown_t = LvSelect("lv_dropdown_t") +lv_dropdown_list_t = LvType("lv_dropdown_list_t") +dropdown_list_spec = WidgetType(CONF_DROPDOWN_LIST, lv_dropdown_list_t, (CONF_MAIN,)) + +DROPDOWN_BASE_SCHEMA = cv.Schema( + { + cv.Optional(CONF_SYMBOL): lv_text, + cv.Optional(CONF_SELECTED_INDEX): cv.templatable(cv.int_), + cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of, + cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec), + } +) + +DROPDOWN_SCHEMA = DROPDOWN_BASE_SCHEMA.extend( + { + cv.Required(CONF_OPTIONS): cv.ensure_list(option_string), + } +) + + +class DropdownType(WidgetType): + def __init__(self): + super().__init__( + CONF_DROPDOWN, + lv_dropdown_t, + (CONF_MAIN, CONF_INDICATOR), + DROPDOWN_SCHEMA, + DROPDOWN_BASE_SCHEMA, + ) + + async def to_code(self, w: Widget, config): + if options := config.get(CONF_OPTIONS): + text = cg.safe_exp("\n".join(options)) + lv.dropdown_set_options(w.obj, text) + if symbol := config.get(CONF_SYMBOL): + lv.dropdown_set_symbol(w.obj, await lv_text.process(symbol)) + if (selected := config.get(CONF_SELECTED_INDEX)) is not None: + value = await lv_int.process(selected) + lv.dropdown_set_selected(w.obj, value) + if dirn := config.get(CONF_DIR): + lv.dropdown_set_dir(w.obj, literal(dirn)) + if dlist := config.get(CONF_DROPDOWN_LIST): + with LocalVariable( + "dropdown_list", lv_obj_t, lv_expr.dropdown_get_list(w.obj) + ) as dlist_obj: + dwid = Widget(dlist_obj, dropdown_list_spec, dlist) + await set_obj_properties(dwid, dlist) + + def get_uses(self): + return (CONF_LABEL,) + + +dropdown_spec = DropdownType() diff --git a/esphome/components/lvgl/img.py b/esphome/components/lvgl/img.py index e9682def8c..dd962fcf31 100644 --- a/esphome/components/lvgl/img.py +++ b/esphome/components/lvgl/img.py @@ -65,16 +65,16 @@ class ImgType(WidgetType): async def to_code(self, w: Widget, config): if src := config.get(CONF_SRC): lv.img_set_src(w.obj, await lv_image.process(src)) - if cf_angle := config.get(CONF_ANGLE): + if (cf_angle := config.get(CONF_ANGLE)) is not None: pivot_x = config[CONF_PIVOT_X] pivot_y = config[CONF_PIVOT_Y] lv.img_set_pivot(w.obj, pivot_x, pivot_y) lv.img_set_angle(w.obj, cf_angle) - if img_zoom := config.get(CONF_ZOOM): + if (img_zoom := config.get(CONF_ZOOM)) is not None: lv.img_set_zoom(w.obj, img_zoom) - if offset := config.get(CONF_OFFSET_X): + if (offset := config.get(CONF_OFFSET_X)) is not None: lv.img_set_offset_x(w.obj, offset) - if offset := config.get(CONF_OFFSET_Y): + if (offset := config.get(CONF_OFFSET_Y)) is not None: lv.img_set_offset_y(w.obj, offset) if CONF_ANTIALIAS in config: lv.img_set_antialias(w.obj, config[CONF_ANTIALIAS]) diff --git a/esphome/components/lvgl/keyboard.py b/esphome/components/lvgl/keyboard.py new file mode 100644 index 0000000000..7ce73d2170 --- /dev/null +++ b/esphome/components/lvgl/keyboard.py @@ -0,0 +1,49 @@ +from esphome.components.key_provider import KeyProvider +import esphome.config_validation as cv +from esphome.const import CONF_MODE +from esphome.cpp_types import std_string + +from .defines import CONF_ITEMS, CONF_MAIN, KEYBOARD_MODES, literal +from .helpers import add_lv_use, lvgl_components_required +from .textarea import CONF_TEXTAREA, lv_textarea_t +from .types import LvCompound, LvType +from .widget import Widget, WidgetType, get_widgets + +CONF_KEYBOARD = "keyboard" + +KEYBOARD_SCHEMA = { + cv.Optional(CONF_MODE, default="TEXT_UPPER"): KEYBOARD_MODES.one_of, + cv.Optional(CONF_TEXTAREA): cv.use_id(lv_textarea_t), +} + +lv_keyboard_t = LvType( + "LvKeyboardType", + parents=(KeyProvider, LvCompound), + largs=[(std_string, "text")], + has_on_value=True, + lvalue=lambda w: literal(f"lv_textarea_get_text({w.obj})"), +) + + +class KeyboardType(WidgetType): + def __init__(self): + super().__init__( + CONF_KEYBOARD, + lv_keyboard_t, + (CONF_MAIN, CONF_ITEMS), + KEYBOARD_SCHEMA, + ) + + def get_uses(self): + return CONF_KEYBOARD, CONF_TEXTAREA + + async def to_code(self, w: Widget, config: dict): + lvgl_components_required.add("KEY_LISTENER") + lvgl_components_required.add(CONF_KEYBOARD) + add_lv_use("btnmatrix") + await w.set_property(CONF_MODE, await KEYBOARD_MODES.process(config[CONF_MODE])) + if ta := await get_widgets(config, CONF_TEXTAREA): + await w.set_property(CONF_TEXTAREA, ta[0].obj) + + +keyboard_spec = KeyboardType() diff --git a/esphome/components/lvgl/led.py b/esphome/components/lvgl/led.py index f920758efb..9b6e819278 100644 --- a/esphome/components/lvgl/led.py +++ b/esphome/components/lvgl/led.py @@ -20,9 +20,9 @@ class LedType(WidgetType): super().__init__(CONF_LED, LvType("lv_led_t"), (CONF_MAIN,), LED_SCHEMA) async def to_code(self, w: Widget, config): - if color := config.get(CONF_COLOR): + if (color := config.get(CONF_COLOR)) is not None: lv.led_set_color(w.obj, await lv_color.process(color)) - if brightness := config.get(CONF_BRIGHTNESS): + if (brightness := config.get(CONF_BRIGHTNESS)) is not None: lv.led_set_brightness(w.obj, await lv_brightness.process(brightness)) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 1221682d28..544643d532 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -146,12 +146,12 @@ LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_ #endif // USE_LVGL_ROTARY_ENCODER #ifdef USE_LVGL_BUTTONMATRIX -void LvBtnmatrixType::set_obj(lv_obj_t *lv_obj) { +void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) { LvCompound::set_obj(lv_obj); lv_obj_add_event_cb( lv_obj, [](lv_event_t *event) { - auto *self = static_cast(event->user_data); + auto *self = static_cast(event->user_data); if (self->key_callback_.size() == 0) return; auto key_idx = lv_btnmatrix_get_selected_btn(self->obj); diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index b92799addd..71e0fd069f 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -246,7 +246,7 @@ class LVEncoderListener : public Parented { }; #endif // USE_LVGL_ROTARY_ENCODER #ifdef USE_LVGL_BUTTONMATRIX -class LvBtnmatrixType : public key_provider::KeyProvider, public LvCompound { +class LvButtonMatrixType : public key_provider::KeyProvider, public LvCompound { public: void set_obj(lv_obj_t *lv_obj) override; uint16_t get_selected() { return lv_btnmatrix_get_selected_btn(this->obj); } diff --git a/esphome/components/lvgl/meter.py b/esphome/components/lvgl/meter.py new file mode 100644 index 0000000000..1a6bef7c57 --- /dev/null +++ b/esphome/components/lvgl/meter.py @@ -0,0 +1,302 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import ( + CONF_COLOR, + CONF_COUNT, + CONF_ID, + CONF_LENGTH, + CONF_LOCAL, + CONF_RANGE_FROM, + CONF_RANGE_TO, + CONF_ROTATION, + CONF_VALUE, + CONF_WIDTH, +) + +from .arc import CONF_ARC +from .automation import action_to_code +from .defines import ( + CONF_END_VALUE, + CONF_MAIN, + CONF_PIVOT_X, + CONF_PIVOT_Y, + CONF_SRC, + CONF_START_VALUE, + CONF_TICKS, +) +from .helpers import add_lv_use +from .img import CONF_IMAGE +from .line import CONF_LINE +from .lv_validation import ( + angle, + get_end_value, + get_start_value, + lv_bool, + lv_color, + lv_float, + lv_image, + requires_component, + size, +) +from .lvcode import LocalVariable, lv, lv_assign, lv_expr +from .obj import obj_spec +from .types import LvType, ObjUpdateAction +from .widget import Widget, WidgetType, get_widgets + +CONF_ANGLE_RANGE = "angle_range" +CONF_COLOR_END = "color_end" +CONF_COLOR_START = "color_start" +CONF_INDICATORS = "indicators" +CONF_LABEL_GAP = "label_gap" +CONF_MAJOR = "major" +CONF_METER = "meter" +CONF_R_MOD = "r_mod" +CONF_SCALES = "scales" +CONF_STRIDE = "stride" +CONF_TICK_STYLE = "tick_style" + +lv_meter_t = LvType("lv_meter_t") +lv_meter_indicator_t = cg.global_ns.struct("lv_meter_indicator_t") +lv_meter_indicator_t_ptr = lv_meter_indicator_t.operator("ptr") + + +def pixels(value): + """A size in one axis in pixels""" + if isinstance(value, str) and value.lower().endswith("px"): + return cv.int_(value[:-2]) + return cv.int_(value) + + +INDICATOR_LINE_SCHEMA = cv.Schema( + { + cv.Optional(CONF_WIDTH, default=4): size, + cv.Optional(CONF_COLOR, default=0): lv_color, + cv.Optional(CONF_R_MOD, default=0): size, + cv.Optional(CONF_VALUE): lv_float, + } +) +INDICATOR_IMG_SCHEMA = cv.Schema( + { + cv.Required(CONF_SRC): lv_image, + cv.Required(CONF_PIVOT_X): pixels, + cv.Required(CONF_PIVOT_Y): pixels, + cv.Optional(CONF_VALUE): lv_float, + } +) +INDICATOR_ARC_SCHEMA = cv.Schema( + { + cv.Optional(CONF_WIDTH, default=4): size, + cv.Optional(CONF_COLOR, default=0): lv_color, + cv.Optional(CONF_R_MOD, default=0): size, + cv.Exclusive(CONF_VALUE, CONF_VALUE): lv_float, + cv.Exclusive(CONF_START_VALUE, CONF_VALUE): lv_float, + cv.Optional(CONF_END_VALUE): lv_float, + } +) +INDICATOR_TICKS_SCHEMA = cv.Schema( + { + cv.Optional(CONF_WIDTH, default=4): size, + cv.Optional(CONF_COLOR_START, default=0): lv_color, + cv.Optional(CONF_COLOR_END): lv_color, + cv.Exclusive(CONF_VALUE, CONF_VALUE): lv_float, + cv.Exclusive(CONF_START_VALUE, CONF_VALUE): lv_float, + cv.Optional(CONF_END_VALUE): lv_float, + cv.Optional(CONF_LOCAL, default=False): lv_bool, + } +) +INDICATOR_SCHEMA = cv.Schema( + { + cv.Exclusive(CONF_LINE, CONF_INDICATORS): INDICATOR_LINE_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(lv_meter_indicator_t), + } + ), + cv.Exclusive(CONF_IMAGE, CONF_INDICATORS): cv.All( + INDICATOR_IMG_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(lv_meter_indicator_t), + } + ), + requires_component("image"), + ), + cv.Exclusive(CONF_ARC, CONF_INDICATORS): INDICATOR_ARC_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(lv_meter_indicator_t), + } + ), + cv.Exclusive(CONF_TICK_STYLE, CONF_INDICATORS): INDICATOR_TICKS_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(lv_meter_indicator_t), + } + ), + } +) + +SCALE_SCHEMA = cv.Schema( + { + cv.Optional(CONF_TICKS): cv.Schema( + { + cv.Optional(CONF_COUNT, default=12): cv.positive_int, + cv.Optional(CONF_WIDTH, default=2): size, + cv.Optional(CONF_LENGTH, default=10): size, + cv.Optional(CONF_COLOR, default=0x808080): lv_color, + cv.Optional(CONF_MAJOR): cv.Schema( + { + cv.Optional(CONF_STRIDE, default=3): cv.positive_int, + cv.Optional(CONF_WIDTH, default=5): size, + cv.Optional(CONF_LENGTH, default="15%"): size, + cv.Optional(CONF_COLOR, default=0): lv_color, + cv.Optional(CONF_LABEL_GAP, default=4): size, + } + ), + } + ), + cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_, + cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_, + cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360), + cv.Optional(CONF_ROTATION): angle, + cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA), + } +) + +METER_SCHEMA = {cv.Optional(CONF_SCALES): cv.ensure_list(SCALE_SCHEMA)} + + +class MeterType(WidgetType): + def __init__(self): + super().__init__(CONF_METER, lv_meter_t, (CONF_MAIN,), METER_SCHEMA) + + async def to_code(self, w: Widget, config): + """For a meter object, create and set parameters""" + + var = w.obj + for scale_conf in config.get(CONF_SCALES) or (): + rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2 + if CONF_ROTATION in scale_conf: + rotation = scale_conf[CONF_ROTATION] // 10 + with LocalVariable( + "meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var) + ) as meter_var: + lv.meter_set_scale_range( + var, + meter_var, + scale_conf[CONF_RANGE_FROM], + scale_conf[CONF_RANGE_TO], + scale_conf[CONF_ANGLE_RANGE], + rotation, + ) + if ticks := scale_conf.get(CONF_TICKS): + color = await lv_color.process(ticks[CONF_COLOR]) + lv.meter_set_scale_ticks( + var, + meter_var, + ticks[CONF_COUNT], + ticks[CONF_WIDTH], + ticks[CONF_LENGTH], + color, + ) + if CONF_MAJOR in ticks: + major = ticks[CONF_MAJOR] + color = await lv_color.process(major[CONF_COLOR]) + lv.meter_set_scale_major_ticks( + var, + meter_var, + major[CONF_STRIDE], + major[CONF_WIDTH], + major[CONF_LENGTH], + color, + major[CONF_LABEL_GAP], + ) + for indicator in scale_conf.get(CONF_INDICATORS) or (): + (t, v) = next(iter(indicator.items())) + iid = v[CONF_ID] + ivar = cg.new_variable( + iid, cg.nullptr, type_=lv_meter_indicator_t_ptr + ) + # Enable getting the meter to which this belongs. + wid = Widget.create(iid, var, obj_spec, v) + wid.obj = ivar + if t == CONF_LINE: + color = await lv_color.process(v[CONF_COLOR]) + lv_assign( + ivar, + lv_expr.meter_add_needle_line( + var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD] + ), + ) + if t == CONF_ARC: + color = await lv_color.process(v[CONF_COLOR]) + lv_assign( + ivar, + lv_expr.meter_add_arc( + var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD] + ), + ) + if t == CONF_TICK_STYLE: + color_start = await lv_color.process(v[CONF_COLOR_START]) + color_end = await lv_color.process( + v.get(CONF_COLOR_END) or color_start + ) + lv_assign( + ivar, + lv_expr.meter_add_scale_lines( + var, + meter_var, + color_start, + color_end, + v[CONF_LOCAL], + v[CONF_WIDTH], + ), + ) + if t == CONF_IMAGE: + add_lv_use("img") + lv_assign( + ivar, + lv_expr.meter_add_needle_img( + var, + meter_var, + await lv_image.process(v[CONF_SRC]), + v[CONF_PIVOT_X], + v[CONF_PIVOT_Y], + ), + ) + start_value = await get_start_value(v) + end_value = await get_end_value(v) + set_indicator_values(var, ivar, start_value, end_value) + + +meter_spec = MeterType() + + +@automation.register_action( + "lvgl.indicator.update", + ObjUpdateAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(lv_meter_indicator_t), + cv.Exclusive(CONF_VALUE, CONF_VALUE): lv_float, + cv.Exclusive(CONF_START_VALUE, CONF_VALUE): lv_float, + cv.Optional(CONF_END_VALUE): lv_float, + } + ), +) +async def indicator_update_to_code(config, action_id, template_arg, args): + widget = await get_widgets(config) + start_value = await get_start_value(config) + end_value = await get_end_value(config) + + async def set_value(w: Widget): + set_indicator_values(w.var, w.obj, start_value, end_value) + + return await action_to_code(widget, set_value, action_id, template_arg, args) + + +def set_indicator_values(meter, indicator, start_value, end_value): + if start_value is not None: + if end_value is None: + lv.meter_set_indicator_value(meter, indicator, start_value) + else: + lv.meter_set_indicator_start_value(meter, indicator, start_value) + if end_value is not None: + lv.meter_set_indicator_end_value(meter, indicator, end_value) diff --git a/esphome/components/lvgl/msgbox.py b/esphome/components/lvgl/msgbox.py new file mode 100644 index 0000000000..6dd529d77f --- /dev/null +++ b/esphome/components/lvgl/msgbox.py @@ -0,0 +1,127 @@ +from esphome import config_validation as cv +from esphome.const import CONF_BUTTON, CONF_ID +from esphome.core import ID +from esphome.cpp_generator import new_Pvariable, static_const_array +from esphome.cpp_types import nullptr + +from .button import button_spec +from .buttonmatrix import ( + BUTTONMATRIX_BUTTON_SCHEMA, + CONF_BUTTON_TEXT_LIST_ID, + buttonmatrix_spec, + get_button_data, + lv_buttonmatrix_t, + set_btn_data, +) +from .defines import ( + CONF_BODY, + CONF_BUTTONS, + CONF_CLOSE_BUTTON, + CONF_MSGBOXES, + CONF_TEXT, + CONF_TITLE, + TYPE_FLEX, + literal, +) +from .helpers import add_lv_use +from .label import CONF_LABEL +from .lv_validation import lv_bool, lv_pct, lv_text +from .lvcode import ( + EVENT_ARG, + LambdaContext, + LocalVariable, + lv_add, + lv_assign, + lv_expr, + lv_obj, + lv_Pvariable, +) +from .obj import obj_spec +from .schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema +from .styles import TOP_LAYER +from .types import LV_EVENT, char_ptr, lv_obj_t +from .widget import Widget, set_obj_properties + +CONF_MSGBOX = "msgbox" +MSGBOX_SCHEMA = container_schema( + obj_spec, + STYLE_SCHEMA.extend( + { + cv.GenerateID(CONF_ID): cv.declare_id(lv_obj_t), + cv.Required(CONF_TITLE): STYLED_TEXT_SCHEMA, + cv.Optional(CONF_BODY): STYLED_TEXT_SCHEMA, + cv.Optional(CONF_BUTTONS): cv.ensure_list(BUTTONMATRIX_BUTTON_SCHEMA), + cv.Optional(CONF_CLOSE_BUTTON): lv_bool, + cv.GenerateID(CONF_BUTTON_TEXT_LIST_ID): cv.declare_id(char_ptr), + } + ), +) + + +async def msgbox_to_code(conf): + """ + Construct a message box. This consists of a full-screen translucent background enclosing a centered container + with an optional title, body, close button and a button matrix. And any other widgets the user cares to add + :param conf: The config data + :return: code to add to the init lambda + """ + add_lv_use( + TYPE_FLEX, + CONF_BUTTON, + CONF_LABEL, + CONF_MSGBOX, + *buttonmatrix_spec.get_uses(), + *button_spec.get_uses(), + ) + mbid = conf[CONF_ID] + outer = lv_Pvariable(lv_obj_t, mbid.id) + btnm = new_Pvariable( + ID(f"{mbid.id}_btnm_", is_declaration=True, type=lv_buttonmatrix_t) + ) + msgbox = lv_Pvariable(lv_obj_t, f"{mbid.id}_msgbox") + outer_w = Widget.create(mbid, outer, obj_spec, conf) + btnm_widg = Widget.create(str(btnm), btnm, buttonmatrix_spec, conf) + text_list, ctrl_list, width_list, _ = await get_button_data((conf,), btnm_widg) + text_id = conf[CONF_BUTTON_TEXT_LIST_ID] + text_list = static_const_array(text_id, text_list) + if (text := conf.get(CONF_BODY)) is not None: + text = await lv_text.process(text.get(CONF_TEXT)) + if (title := conf.get(CONF_TITLE)) is not None: + title = await lv_text.process(title.get(CONF_TEXT)) + close_button = conf[CONF_CLOSE_BUTTON] + lv_assign(outer, lv_expr.obj_create(TOP_LAYER)) + lv_obj.set_width(outer, lv_pct(100)) + lv_obj.set_height(outer, lv_pct(100)) + lv_obj.set_style_bg_opa(outer, 128, 0) + lv_obj.set_style_bg_color(outer, literal("lv_color_black()"), 0) + lv_obj.set_style_border_width(outer, 0, 0) + lv_obj.set_style_pad_all(outer, 0, 0) + lv_obj.set_style_radius(outer, 0, 0) + outer_w.add_flag("LV_OBJ_FLAG_HIDDEN") + lv_assign( + msgbox, lv_expr.msgbox_create(outer, title, text, text_list, close_button) + ) + lv_obj.set_style_align(msgbox, literal("LV_ALIGN_CENTER"), 0) + lv_add(btnm.set_obj(lv_expr.msgbox_get_btns(msgbox))) + await set_obj_properties(outer_w, conf) + if close_button: + async with LambdaContext(EVENT_ARG, where=mbid) as context: + outer_w.add_flag("LV_OBJ_FLAG_HIDDEN") + with LocalVariable( + "close_btn_", lv_obj_t, lv_expr.msgbox_get_close_btn(msgbox) + ) as close_btn: + lv_obj.remove_event_cb(close_btn, nullptr) + lv_obj.add_event_cb( + close_btn, + await context.get_lambda(), + LV_EVENT.CLICKED, + nullptr, + ) + + if len(ctrl_list) != 0 or len(width_list) != 0: + set_btn_data(btnm.obj, ctrl_list, width_list) + + +async def msgboxes_to_code(config): + for conf in config.get(CONF_MSGBOXES, ()): + await msgbox_to_code(conf) diff --git a/esphome/components/lvgl/roller.py b/esphome/components/lvgl/roller.py new file mode 100644 index 0000000000..7af3ef3c3d --- /dev/null +++ b/esphome/components/lvgl/roller.py @@ -0,0 +1,77 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_MODE, CONF_OPTIONS + +from .defines import ( + CONF_ANIMATED, + CONF_MAIN, + CONF_SELECTED, + CONF_SELECTED_INDEX, + CONF_VISIBLE_ROW_COUNT, + ROLLER_MODES, + literal, +) +from .label import CONF_LABEL +from .lv_validation import animated, lv_int, option_string +from .lvcode import lv +from .types import LvSelect +from .widget import WidgetType + +CONF_ROLLER = "roller" +lv_roller_t = LvSelect("lv_roller_t") + +ROLLER_BASE_SCHEMA = cv.Schema( + { + cv.Optional(CONF_SELECTED_INDEX): cv.templatable(cv.int_), + cv.Optional(CONF_VISIBLE_ROW_COUNT): lv_int, + } +) + +ROLLER_SCHEMA = ROLLER_BASE_SCHEMA.extend( + { + cv.Required(CONF_OPTIONS): cv.ensure_list(option_string), + cv.Optional(CONF_MODE, default="NORMAL"): ROLLER_MODES.one_of, + } +) + +ROLLER_MODIFY_SCHEMA = ROLLER_BASE_SCHEMA.extend( + { + cv.Optional(CONF_ANIMATED, default=True): animated, + } +) + + +class RollerType(WidgetType): + def __init__(self): + super().__init__( + CONF_ROLLER, + lv_roller_t, + (CONF_MAIN, CONF_SELECTED), + ROLLER_SCHEMA, + ROLLER_MODIFY_SCHEMA, + ) + + async def to_code(self, w, config): + if options := config.get(CONF_OPTIONS): + mode = await ROLLER_MODES.process(config[CONF_MODE]) + text = cg.safe_exp("\n".join(options)) + lv.roller_set_options(w.obj, text, mode) + animopt = literal(config.get(CONF_ANIMATED) or "LV_ANIM_OFF") + if CONF_SELECTED_INDEX in config: + if selected := config[CONF_SELECTED_INDEX]: + value = await lv_int.process(selected) + lv.roller_set_selected(w.obj, value, animopt) + await w.set_property( + CONF_VISIBLE_ROW_COUNT, + await lv_int.process(config.get(CONF_VISIBLE_ROW_COUNT)), + ) + + @property + def animated(self): + return True + + def get_uses(self): + return (CONF_LABEL,) + + +roller_spec = RollerType() diff --git a/esphome/components/lvgl/spinbox.py b/esphome/components/lvgl/spinbox.py new file mode 100644 index 0000000000..62c58c54a3 --- /dev/null +++ b/esphome/components/lvgl/spinbox.py @@ -0,0 +1,178 @@ +from esphome import automation +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_RANGE_FROM, CONF_RANGE_TO, CONF_STEP, CONF_VALUE + +from .automation import action_to_code, update_to_code +from .defines import ( + CONF_CURSOR, + CONF_DECIMAL_PLACES, + CONF_DIGITS, + CONF_MAIN, + CONF_ROLLOVER, + CONF_SCROLLBAR, + CONF_SELECTED, + CONF_TEXTAREA_PLACEHOLDER, +) +from .label import CONF_LABEL +from .lv_validation import lv_bool, lv_float +from .lvcode import lv +from .textarea import CONF_TEXTAREA +from .types import LvNumber, ObjUpdateAction +from .widget import Widget, WidgetType, get_widgets + +CONF_SPINBOX = "spinbox" + +lv_spinbox_t = LvNumber("lv_spinbox_t") + +SPIN_ACTIONS = ( + "INCREMENT", + "DECREMENT", + "STEP_NEXT", + "STEP_PREV", + "CLEAR", +) + + +def validate_spinbox(config): + max_val = 2**31 - 1 + min_val = -1 - max_val + range_from = int(config[CONF_RANGE_FROM]) + range_to = int(config[CONF_RANGE_TO]) + step = int(config[CONF_STEP]) + if ( + range_from > max_val + or range_from < min_val + or range_to > max_val + or range_to < min_val + ): + raise cv.Invalid("Range outside allowed limits") + if step <= 0 or step >= (range_to - range_from) / 2: + raise cv.Invalid("Invalid step value") + if config[CONF_DIGITS] <= config[CONF_DECIMAL_PLACES]: + raise cv.Invalid("Number of digits must exceed number of decimal places") + return config + + +SPINBOX_SCHEMA = cv.Schema( + { + cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_RANGE_FROM, default=0): cv.float_, + cv.Optional(CONF_RANGE_TO, default=100): cv.float_, + cv.Optional(CONF_DIGITS, default=4): cv.int_range(1, 10), + cv.Optional(CONF_STEP, default=1.0): cv.positive_float, + cv.Optional(CONF_DECIMAL_PLACES, default=0): cv.int_range(0, 6), + cv.Optional(CONF_ROLLOVER, default=False): lv_bool, + } +).add_extra(validate_spinbox) + + +SPINBOX_MODIFY_SCHEMA = { + cv.Required(CONF_VALUE): lv_float, +} + + +class SpinboxType(WidgetType): + def __init__(self): + super().__init__( + CONF_SPINBOX, + lv_spinbox_t, + ( + CONF_MAIN, + CONF_SCROLLBAR, + CONF_SELECTED, + CONF_CURSOR, + CONF_TEXTAREA_PLACEHOLDER, + ), + SPINBOX_SCHEMA, + SPINBOX_MODIFY_SCHEMA, + ) + + async def to_code(self, w: Widget, config): + if CONF_DIGITS in config: + digits = config[CONF_DIGITS] + scale = 10 ** config[CONF_DECIMAL_PLACES] + range_from = int(config[CONF_RANGE_FROM]) * scale + range_to = int(config[CONF_RANGE_TO]) * scale + step = int(config[CONF_STEP]) * scale + w.scale = scale + w.step = step + w.range_to = range_to + w.range_from = range_from + lv.spinbox_set_range(w.obj, range_from, range_to) + await w.set_property(CONF_STEP, step) + await w.set_property(CONF_ROLLOVER, config) + lv.spinbox_set_digit_format( + w.obj, digits, digits - config[CONF_DECIMAL_PLACES] + ) + if (value := config.get(CONF_VALUE)) is not None: + lv.spinbox_set_value(w.obj, await lv_float.process(value)) + + def get_scale(self, config): + return 10 ** config[CONF_DECIMAL_PLACES] + + def get_uses(self): + return CONF_TEXTAREA, CONF_LABEL + + def get_max(self, config: dict): + return config[CONF_RANGE_TO] + + def get_min(self, config: dict): + return config[CONF_RANGE_FROM] + + def get_step(self, config: dict): + return config[CONF_STEP] + + +spinbox_spec = SpinboxType() + + +@automation.register_action( + "lvgl.spinbox.increment", + ObjUpdateAction, + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(lv_spinbox_t), + }, + key=CONF_ID, + ), +) +async def spinbox_increment(config, action_id, template_arg, args): + widgets = await get_widgets(config) + + async def do_increment(w: Widget): + lv.spinbox_increment(w.obj) + + return await action_to_code(widgets, do_increment, action_id, template_arg, args) + + +@automation.register_action( + "lvgl.spinbox.decrement", + ObjUpdateAction, + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(lv_spinbox_t), + }, + key=CONF_ID, + ), +) +async def spinbox_decrement(config, action_id, template_arg, args): + widgets = await get_widgets(config) + + async def do_increment(w: Widget): + lv.spinbox_decrement(w.obj) + + return await action_to_code(widgets, do_increment, action_id, template_arg, args) + + +@automation.register_action( + "lvgl.spinbox.update", + ObjUpdateAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(lv_spinbox_t), + cv.Required(CONF_VALUE): lv_float, + } + ), +) +async def spinbox_update_to_code(config, action_id, template_arg, args): + return await update_to_code(config, action_id, template_arg, args) diff --git a/esphome/components/lvgl/styles.py b/esphome/components/lvgl/styles.py index 7a795bc99d..09f1c376d0 100644 --- a/esphome/components/lvgl/styles.py +++ b/esphome/components/lvgl/styles.py @@ -26,7 +26,7 @@ async def styles_to_code(config): svar = cg.new_Pvariable(style[CONF_ID]) lv.style_init(svar) for prop, validator in ALL_STYLES.items(): - if value := style.get(prop): + if (value := style.get(prop)) is not None: if isinstance(validator, LValidator): value = await validator.process(value) if isinstance(value, list): diff --git a/esphome/components/lvgl/tabview.py b/esphome/components/lvgl/tabview.py new file mode 100644 index 0000000000..7b6a864e21 --- /dev/null +++ b/esphome/components/lvgl/tabview.py @@ -0,0 +1,114 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INDEX, CONF_NAME, CONF_POSITION, CONF_SIZE +from esphome.cpp_generator import MockObjClass + +from . import buttonmatrix_spec +from .automation import action_to_code +from .defines import ( + CONF_ANIMATED, + CONF_MAIN, + CONF_TAB_ID, + CONF_TABS, + DIRECTIONS, + TYPE_FLEX, + literal, +) +from .lv_validation import animated, lv_int, size +from .lvcode import LocalVariable, lv, lv_assign, lv_expr +from .obj import obj_spec +from .schemas import container_schema, part_schema +from .types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr +from .widget import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties + +CONF_TABVIEW = "tabview" +CONF_TAB_STYLE = "tab_style" + +lv_tab_t = LvType("lv_obj_t") + +TABVIEW_SCHEMA = cv.Schema( + { + cv.Required(CONF_TABS): cv.ensure_list( + container_schema( + obj_spec, + { + cv.Required(CONF_NAME): cv.string, + cv.GenerateID(): cv.declare_id(lv_tab_t), + }, + ) + ), + cv.Optional(CONF_TAB_STYLE): part_schema(buttonmatrix_spec), + cv.Optional(CONF_POSITION, default="top"): DIRECTIONS.one_of, + cv.Optional(CONF_SIZE, default="10%"): size, + } +) + + +class TabviewType(WidgetType): + def __init__(self): + super().__init__( + CONF_TABVIEW, + LvType( + "lv_tabview_t", + largs=[(lv_obj_t_ptr, "tab")], + lvalue=lambda w: lv_expr.obj_get_child( + lv_expr.tabview_get_content(w.obj), + lv_expr.tabview_get_tab_act(w.obj), + ), + has_on_value=True, + ), + parts=(CONF_MAIN,), + schema=TABVIEW_SCHEMA, + modify_schema={}, + ) + + def get_uses(self): + return "btnmatrix", TYPE_FLEX + + async def to_code(self, w: Widget, config: dict): + for tab_conf in config[CONF_TABS]: + w_id = tab_conf[CONF_ID] + tab_obj = cg.Pvariable(w_id, cg.nullptr, type_=lv_tab_t) + tab_widget = Widget.create(w_id, tab_obj, obj_spec) + lv_assign(tab_obj, lv_expr.tabview_add_tab(w.obj, tab_conf[CONF_NAME])) + await set_obj_properties(tab_widget, tab_conf) + await add_widgets(tab_widget, tab_conf) + if button_style := config.get(CONF_TAB_STYLE): + with LocalVariable( + "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) + + def obj_creator(self, parent: MockObjClass, config: dict): + return lv_expr.call( + "tabview_create", + parent, + literal(config[CONF_POSITION]), + literal(config[CONF_SIZE]), + ) + + +tabview_spec = TabviewType() + + +@automation.register_action( + "lvgl.tabview.select", + ObjUpdateAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(tabview_spec.w_type), + cv.Optional(CONF_ANIMATED, default=False): animated, + cv.Required(CONF_INDEX): lv_int, + }, + ).add_extra(cv.has_at_least_one_key(CONF_INDEX, CONF_TAB_ID)), +) +async def tabview_select(config, action_id, template_arg, args): + widget = await get_widgets(config) + index = config[CONF_INDEX] + + async def do_select(w: Widget): + lv.tabview_set_act(w.obj, index, literal(config[CONF_ANIMATED])) + lv.event_send(w.obj, LV_EVENT.VALUE_CHANGED, cg.nullptr) + + return await action_to_code(widget, do_select, action_id, template_arg, args) diff --git a/esphome/components/lvgl/textarea.py b/esphome/components/lvgl/textarea.py new file mode 100644 index 0000000000..d383e1f098 --- /dev/null +++ b/esphome/components/lvgl/textarea.py @@ -0,0 +1,67 @@ +import esphome.config_validation as cv +from esphome.const import CONF_MAX_LENGTH + +from .defines import ( + CONF_ACCEPTED_CHARS, + CONF_CURSOR, + CONF_MAIN, + CONF_ONE_LINE, + CONF_PASSWORD_MODE, + CONF_PLACEHOLDER_TEXT, + CONF_SCROLLBAR, + CONF_SELECTED, + CONF_TEXT, + CONF_TEXTAREA_PLACEHOLDER, +) +from .lv_validation import lv_bool, lv_int, lv_text +from .schemas import TEXT_SCHEMA +from .types import LvText +from .widget import Widget, WidgetType + +CONF_TEXTAREA = "textarea" + +lv_textarea_t = LvText("lv_textarea_t") + +TEXTAREA_SCHEMA = TEXT_SCHEMA.extend( + { + cv.Optional(CONF_PLACEHOLDER_TEXT): lv_text, + cv.Optional(CONF_ACCEPTED_CHARS): lv_text, + cv.Optional(CONF_ONE_LINE): lv_bool, + cv.Optional(CONF_PASSWORD_MODE): lv_bool, + cv.Optional(CONF_MAX_LENGTH): lv_int, + } +) + + +class TextareaType(WidgetType): + def __init__(self): + super().__init__( + CONF_TEXTAREA, + lv_textarea_t, + ( + CONF_MAIN, + CONF_SCROLLBAR, + CONF_SELECTED, + CONF_CURSOR, + CONF_TEXTAREA_PLACEHOLDER, + ), + TEXTAREA_SCHEMA, + ) + + async def to_code(self, w: Widget, config: dict): + for prop in (CONF_TEXT, CONF_PLACEHOLDER_TEXT, CONF_ACCEPTED_CHARS): + if (value := config.get(prop)) is not None: + await w.set_property(prop, await lv_text.process(value)) + await w.set_property( + CONF_MAX_LENGTH, await lv_int.process(config.get(CONF_MAX_LENGTH)) + ) + await w.set_property( + CONF_PASSWORD_MODE, + await lv_bool.process(config.get(CONF_PASSWORD_MODE)), + ) + await w.set_property( + CONF_ONE_LINE, await lv_bool.process(config.get(CONF_ONE_LINE)) + ) + + +textarea_spec = TextareaType() diff --git a/esphome/components/lvgl/tileview.py b/esphome/components/lvgl/tileview.py new file mode 100644 index 0000000000..aa841fa23e --- /dev/null +++ b/esphome/components/lvgl/tileview.py @@ -0,0 +1,128 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_ON_VALUE, CONF_ROW, CONF_TRIGGER_ID + +from .automation import action_to_code +from .defines import ( + CONF_ANIMATED, + CONF_COLUMN, + CONF_DIR, + CONF_MAIN, + CONF_TILE_ID, + CONF_TILES, + TILE_DIRECTIONS, + literal, +) +from .lv_validation import animated, lv_int +from .lvcode import lv, lv_assign, lv_expr, lv_obj, lv_Pvariable +from .obj import obj_spec +from .schemas import container_schema +from .types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr +from .widget import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties + +CONF_TILEVIEW = "tileview" + +lv_tile_t = LvType("lv_tileview_tile_t") + +lv_tileview_t = LvType( + "lv_tileview_t", + largs=[(lv_obj_t_ptr, "tile")], + lvalue=lambda w: w.get_property("tile_act"), +) + +tile_spec = WidgetType("lv_tileview_tile_t", lv_tile_t, (CONF_MAIN,), {}) + +TILEVIEW_SCHEMA = cv.Schema( + { + cv.Required(CONF_TILES): cv.ensure_list( + container_schema( + obj_spec, + { + cv.Required(CONF_ROW): lv_int, + cv.Required(CONF_COLUMN): lv_int, + cv.GenerateID(): cv.declare_id(lv_tile_t), + cv.Optional(CONF_DIR, default="ALL"): TILE_DIRECTIONS.several_of, + }, + ) + ), + cv.Optional(CONF_ON_VALUE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + automation.Trigger.template(lv_obj_t_ptr) + ) + } + ), + } +) + + +class TileviewType(WidgetType): + def __init__(self): + super().__init__( + CONF_TILEVIEW, + lv_tileview_t, + (CONF_MAIN,), + schema=TILEVIEW_SCHEMA, + modify_schema={}, + ) + + async def to_code(self, w: Widget, config: dict): + for tile_conf in config.get(CONF_TILES) or (): + w_id = tile_conf[CONF_ID] + tile_obj = lv_Pvariable(lv_obj_t, w_id) + tile = Widget.create(w_id, tile_obj, tile_spec, tile_conf) + dirs = tile_conf[CONF_DIR] + if isinstance(dirs, list): + dirs = "|".join(dirs) + lv_assign( + tile_obj, + lv_expr.tileview_add_tile( + w.obj, tile_conf[CONF_COLUMN], tile_conf[CONF_ROW], literal(dirs) + ), + ) + await set_obj_properties(tile, tile_conf) + await add_widgets(tile, tile_conf) + + +tileview_spec = TileviewType() + + +def tile_select_validate(config): + row = CONF_ROW in config + column = CONF_COLUMN in config + tile = CONF_TILE_ID in config + if tile and (row or column) or not tile and not (row and column): + raise cv.Invalid("Specify either a tile id, or both a row and a column") + return config + + +@automation.register_action( + "lvgl.tileview.select", + ObjUpdateAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(lv_tileview_t), + cv.Optional(CONF_ANIMATED, default=False): animated, + cv.Optional(CONF_ROW): lv_int, + cv.Optional(CONF_COLUMN): lv_int, + cv.Optional(CONF_TILE_ID): cv.use_id(lv_tile_t), + }, + ).add_extra(tile_select_validate), +) +async def tileview_select(config, action_id, template_arg, args): + widgets = await get_widgets(config) + + async def do_select(w: Widget): + if tile := config.get(CONF_TILE_ID): + tile = await cg.get_variable(tile) + lv_obj.set_tile(w.obj, tile, literal(config[CONF_ANIMATED])) + else: + row = await lv_int.process(config[CONF_ROW]) + column = await lv_int.process(config[CONF_COLUMN]) + lv_obj.set_tile_id( + widgets[0].obj, column, row, literal(config[CONF_ANIMATED]) + ) + lv.event_send(w.obj, LV_EVENT.VALUE_CHANGED, cg.nullptr) + + return await action_to_code(widgets, do_select, action_id, template_arg, args) diff --git a/esphome/components/lvgl/widget.py b/esphome/components/lvgl/widget.py index 5734aec7dc..fcaee29085 100644 --- a/esphome/components/lvgl/widget.py +++ b/esphome/components/lvgl/widget.py @@ -282,13 +282,13 @@ async def set_obj_properties(w: Widget, config): lv_obj.set_layout(w.obj, literal(f"LV_LAYOUT_{layout_type.upper()}")) if layout_type == TYPE_GRID: wid = config[CONF_ID] - rows = "{" + ",".join(layout[CONF_GRID_ROWS]) + ", LV_GRID_TEMPLATE_LAST}" + rows = [str(x) for x in layout[CONF_GRID_ROWS]] + rows = "{" + ",".join(rows) + ", LV_GRID_TEMPLATE_LAST}" row_id = ID(f"{wid}_row_dsc", is_declaration=True, type=lv_coord_t) row_array = cg.static_const_array(row_id, cg.RawExpression(rows)) w.set_style("grid_row_dsc_array", row_array, 0) - columns = ( - "{" + ",".join(layout[CONF_GRID_COLUMNS]) + ", LV_GRID_TEMPLATE_LAST}" - ) + columns = [str(x) for x in layout[CONF_GRID_COLUMNS]] + columns = "{" + ",".join(columns) + ", LV_GRID_TEMPLATE_LAST}" column_id = ID(f"{wid}_column_dsc", is_declaration=True, type=lv_coord_t) column_array = cg.static_const_array(column_id, cg.RawExpression(columns)) w.set_style("grid_column_dsc_array", column_array, 0) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 726db24592..b7bdbb1f9d 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -39,9 +39,12 @@ #define USE_LOCK #define USE_LOGGER #define USE_LVGL +#define USE_LVGL_ANIMIMG #define USE_LVGL_BINARY_SENSOR +#define USE_LVGL_BUTTONMATRIX #define USE_LVGL_FONT #define USE_LVGL_IMAGE +#define USE_LVGL_KEYBOARD #define USE_LVGL_KEY_LISTENER #define USE_LVGL_TOUCHSCREEN #define USE_LVGL_ROTARY_ENCODER diff --git a/tests/components/lvgl/.gitattributes b/tests/components/lvgl/.gitattributes new file mode 100644 index 0000000000..75e7a44254 --- /dev/null +++ b/tests/components/lvgl/.gitattributes @@ -0,0 +1,2 @@ +*.ttf -text + diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 8b92f8caa7..6d0c1967b4 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -8,3 +8,49 @@ touchscreen: x_max: 240 y_max: 320 +font: + - file: "$component_dir/roboto.ttf" + id: roboto20 + size: 20 + extras: + - file: '$component_dir/materialdesignicons-webfont.ttf' + glyphs: [ + "\U000F004B", + "\U0000f0ed", + "\U000F006E", + "\U000F012C", + "\U000F179B", + "\U000F0748", + "\U000F1A1B", + "\U000F02DC", + "\U000F0A02", + "\U000F035F", + "\U000F0156", + "\U000F0C5F", + "\U000f0084", + "\U000f0091", + ] + - file: "$component_dir/helvetica.ttf" + id: helvetica20 + - file: "$component_dir/roboto.ttf" + id: roboto10 + size: 10 + bpp: 4 + extras: + - file: '$component_dir/materialdesignicons-webfont.ttf' + glyphs: [ + "\U000F004B", + "\U0000f0ed", + "\U000F006E", + "\U000F012C", + "\U000F179B", + "\U000F0748", + "\U000F1A1B", + "\U000F02DC", + "\U000F0A02", + "\U000F035F", + "\U000F0156", + "\U000F0C5F", + "\U000f0084", + "\U000f0091", + ] diff --git a/tests/components/lvgl/helvetica.ttf b/tests/components/lvgl/helvetica.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7aec6f3f3cc74a7138ce350def27a2209b44ad89 GIT binary patch literal 83644 zcmd?S2Y4LSy*_--%=S9lyR@rSv9z}3wJcl0vbDA(<3cgUCXxb)gk`V+n_|4l3Yj}864tp4Kc*DPz-_8EACAp&9+A0d(P~xMoF$YH;vl_5BwZ(;T03>_4_4lSGs9R&7vjZ%kcq{@Qh;)5%$=?q#g} zf{RvM(D>reVjMq!`XvYzZ>Dz;J#pwC)YsuHBHE|;7CB3BoAiR!pc4c^6m+aQk!c#19JkpR1?PI^$aU2FmZwUjrJp5%RgvsGCekmrlQfkohalyJW?>Rm;2cmyF_% zBuCYiE6%%UT0Oy+@jrnu`V-;>{jn#M5QAW0Cvc#ZumkOn5+T1=_$-th`KjG0;5&7A zTLUo?6EPDDu@W1xqqUtxA}-=49z54a{3JkvBt*g_LZT!_N=PY*lLRRvNm5QKNF}Kv z6G%0wAt^GEOd^v>Etx{nWGbm6(?~s;PG*n>(nyXYGf5MfMP`#`yxtr#m$Z;p5M~}Z zp3El;$U?G+oIp+_Cy|rMVsZ*uLQW-1$ue>p`37kx9i)?dlblY@Am1Y2CS7=?d&&Le zLGmzpk~~9RB7>x#Y$ZP++sO{Hle|h^A-l+r$!_u!@>B9U*+bqWZ;+pnx5!@d4%tWE zCGXLtDBZax{RJp7t>Q|JGqlQLT)FIl84A9@;&l6d5kUF0>gpL|L_ zBflh{lP}1x$-k3PN+_iQ6{(IIsf{|Qhx%y*H7=t`nxa!^n$DoJ@r)Buzi-lR{`Tk6 zwW#x5!cT;s3LlF@ac?{vFNv4ME8|n*$HiOY>*CKR$`X}jLYbk=S>`VDm4(Yn%2H+X z%eu-|RJ{J5|M-J&Pymk;@XjW**o(ZTJI2-Y$7rjun*NIXh76CZsU9`8QzvQ~0CibS zYv^RubUJNB3!H$OoyGLIeeP#3|^(ysB zb@j-G&l*3Q{#pHJ(>_aoHsQ1QXMsFEj>`!Oz@7jOH{$=}@esbRallCv#-@HG$ z-?rcU;rCrPxUMo>bCebUf&c&eClTXX<66T>`W^aT>o;M6rq$m7TeL&JPd8b25_I=p z1QC0a#bsi%FbhkyMy=7$(UR}09crCgN3%4p-yvM6zoJOVcgQ=!rMhY&gGZmlD)Z%M z&?-Za-hRm0cIco1NdG#r4SHz<wST1TI{X4w`Yyc7kO zJ%<+_cErCYvuNRh`Nz*|Yi*f3C(}H8R@2Pm8XIOzub);oH9e(v@}!BWn(7Hvl@;a5 zvP8VJBo>W?L&1RG=k>T(Gg#=L8iVeB|4X92JX-vh<9m8w#56i zC5gKh#`}`picVjdti*e69UWcTo=C_ff-C0r&FktrzVG<1ZaKc3Q;2pY+g7zsn%I_{ zm&DU5yJcNva{hdNq6^1eg=5rgL0s-#(ysJo`tW9FUcD-qY}*(~v~}Qlt;s|y-oLH2 ztrcG@2XVD*WX)qtCA(Lxz!eP*8hse?+mvc=DmgbXX<{~Rr*o67FT=T!;MnL|Ym~A? zUuzOIT69Ws(UMMlnYS(3n)Om|TNln!mpk4VuO-RmNfWn0(0kfWX-~>sr?$u2rR+>Hcvq1|AdNfQ|pB@z}?XF<<$Ttl=EcGTC0gTCb(aaOVED(0@2 zgDvfH90alTBzrb&&5$#bZE?A4aeE>Xmp9=PC1<+3Iyxe49ZIaWuQkH9AZ4o1 zd=WX8VilIyw!gV`rLuyxgI$?WrL{-(B5fuj7XahLJycEFkLfje9cC%1>B+Ae8Z72GhlCE0f7+^pGDy%lWKhu3OJcB65j zG1@_|ws^a2YFRFuTe>=0+xpu2+RuatG0yMM*q8A$+vDt(o}5bMR@7Ff=hnz+>`i5R zlP1n-CMyUtS!Y8jur1S+XA|afolR7}9pT$Ue0z{@5Af|j`1bF7yN_>w&bM##?OS}i zmv4W@w{P+-3vtfo>iKN)Z-mVG1CCzj=YGn!EQmWB=8tV(7xL{I28DuH^V-LBDZ?QSz8=l~29_QQd@$DwQeT;7(<=aR2_Hxwl@TXtQ&#&X#wS0RK z-y$T(mFM&AYJS&we19e1XSDBO-=y(tyF4u-*L85~SK!HHmVi53;(ZX1w$=!YRZZJ+ z=$=){zU54LGok6)N=m!>`ntE0cz0jCt#3o$M(l1#g8Nr>_sG?4iv zoHQ}5wsrK&Xh%l^I#6jeRU}jG1PZQCF|&Otm$6+~##BTxmeh2}{+4mq@j~r7c8lff zuH%Jc-}klGvG22teczGK1z~&7gkF22H3=V~6Lu^v-`bk&h|6E{{S*2A9em%;_v3Ij zKw<2&_v3`-YE=gv*aYpjYu7F`5iHMkqzbCYNo_q#=59yMpq?z&c0CCGPiDI>!`RNp zvJl$78^^ht#_Gnlxr{lw=Vt0W4c-~T{F&Z`NCV6h=g)UFyE4LpR`+6ZqU$8j$-4QA z+|!XW(DDWKhWU;9HYhu$Gvn=Xxw_>{xu#`#mt4XWy1wF)_{z8(M|;J)`ZjI|c67-4 z%0zq>8WK)GD|;q$&$h7?Mr0d_lhO{}$`g}wyW~m9x$->r-EDHJrK>OA(%G)SH{D96Kuc~KYx=;yU7b~`{!@+YBvSIfrsCuz03G_ z8mnGFyD5mY*Vs+0-t4BrCn&=A*(W<6#7!NAzezYLuo7ha4j{{BBs!U-%8+&8f9yOV zBR^p(mBPH>8gkHp&-)I-ujYhcQB0DY!0zW z_zDXRv9CY{m$Yvsz2S~*hWLlW=_wr^(N$OH5|e3ToiF*j+T}g9^($U~ot_~)M1QKz z7+G=qn|RaP#Q|YH;&eu(HSA3lN((P&D!w4)FKCb{e?fEc1r6*4x9FD|8|hN9v3a<8 zn7)i8q(O)og?qMe4_QpAOj0Dsyxr}1&1I=u_%=&_4or}cQ`B=VFH?t-il~-D%N@G#5!Y=uSri*#5$vn zuO5^MrL)!9*U$ci`xn(%XI(!_#rlE!gKDg^(-YKc{SE3OI-oAPPW|LMbt!$Gt=G}A z>*&D9>*^=845@xHcU00<8zv&pd>m;Lx-<##79=E9Xsmg~#!B(Va?2^+SjQI|%ZNpkhDuF#fGWoX9$&s zbCr!D1dNT1$`pz+12zM}0GTo+ppkWpKNO<1K@_5y&`7zqinNV2^gv~6Wuv_jr|?g= z!GJ#6AFHOJl(c?m*CDEu#-t&+RFX^gDe*YnNp>pnQiFrpu1CMb<;~>oe!WCPZhpC9 z(Bo=@BpdcAMkBx4Xkb?x$=&Sg45E)!r$SD<6nFCiBmf#K-6>YVfyPRSy0m;;$$01K^19Y^IrZH^sfQ*K*|=15?Q#>JAqt<`R$ zBvet^n8xc*VL=xEle7=8j3OPE+Kz6644ItkZEg#vQnIh5J?HkfHE$)8Gq&b7?75rq z;ciHE6tApL7p_dzrtA5Y^=a&-X5k|>cI9BN(T4v>{%fhhXyX?fZ4~U^gq7lA{>V^i zyn>g-D?%0V(%D!;DC?j;f50C|C)1Txl~pe6B6(yq7=6iloT#r)*QHsx3x$>S(`HPc zQSUMs4L&bB=b6qjQ7EZ$2`gV(-D`Tmy11!p?(WmSdHrQS>3-+JYZd(|bFT{T{r0!7 zzG&O(*~ibCby@e7Z6_SpxURx5ah<*MoNbq6(pFV+)pd0}`qN8Sn|J@zxnkX?*6%Nz zX%m9R`fr_a|Bee*ST9olW}4A_#gfK`68iqZC5GeXT-DV)yTm-AqK9Yk=qH9Z^qat{ zA@twWkQww@O{<9oL1`!oipT|}N*1}GG!_M=ifOfN=o0#&LhM_~5BnogLt_d%N_iUF zTr6C&sCj7Uis9fstYdq0FcOVKtHL69eeaM-iA0rHcq_Rq8wp2KB2~w|_#{~5lfhoQg#SXD8qEF>Z#RF)5b~Rwuo?<& zYPNa&TA2rX=4QXgW-gQwQS$II$wNGnI2$V|l!*qrq?OsRC(e@W2C-09UNvn7FPk=_ ze8#k@CamR!vZ|W&G_5R+y{hJE>6)rSSxI6thy|9OoGwXEPRzzyQYcH5rzUGBhL%_es=rqd-vXc`_F_)SMn>DUU}eS{VNA%M~^OG+P8CbzrI?( zjg+I0Zw`%Uyc8?&(%d31#W*kdv;rG#DKe9fF_RKr+^qN)H7hXO8FK{*S70GfCK>Z= zBF@Ib4oEH`R)?9Q%sHK67`m#K_E-B7Bqr^`$&9&{#A>lAI*L>W zurM8^RNHXEMyhRC_z5q&!b<{PEc`?wg%c@~NMYe>O)>bST^aLCVwj1Ae{Et0u9-m+ zGqCWREbJx=Nm;OHH%UHepX}VjjA6dzpe#HAD26)d1tU#X84O9*a5z&{SxKi(pE09K z%&QEK*B=OQrBRtIgKjds_Pu+yd~5z{)u*3q-DE$`?W6RUH=X{$b+7#Aw-$cSGWQhf zKWTH5<(%L3%xYh>$bVdI_1yG=(-$q8JXv?necN8VZN-{pbJg8p%SCJNx?^&c`jbuH zKJ(odpIG;Q%{_C!WlI+a6!rC;sAAU z@9cQ3l}VwMb;VXDyp_=z$u)+)*vJBFS$R;ziaRq6Oz z6*c8ptu#$Z@|tq2R)oK|wNd1?vRa6!tH^F((%=b98l{H zB3gL!&wrtBQeG`e7K;}OKh?hmCA>(Zm9x+q-Xg7>yfsAG$y>u$q?O1;j#z7JsqB<; zM$zhF9tY!5V|qrwm<|}L(#EP8>8crlbQS$K<0n;L+_~bW8&}-&Rezdduw$^Xu1w0Lwxce>6D)WCYJHbuYPR;-QYw;S_*yS-Q&Bd3wkAPQT_x}3>O zXf6EKLs)6fqBodvcF?L53HmqSd3=;F8#EeCUN4~wTE7|^g4?h2iWaMvNM8-%pS?+< zJ^}Vh_!6$9U{%}b%lFZj)wcWSR_#-5WA)CeZ`1YU7l=OdR4j6wt5|Q1rxCSrD!PmH z)}h}0!cwDi5oz8xoYS3wnoGMFU(cwotAmC%rt8xX-Wkbc_S|#MT-zM<=)`kZuO58p zq3E}*Gbf#n=XcUy2#bZgp-1yFRphNPnbLTR=6P#Orl{762>RA$MWMABBxC%IH}0f= zRV~8Z$Zp~dvALOifG3=IgiMV+;lGmFZRnZ^DI(r+cpI9)FEa<1y(8kd?7Q!N@ByLd z*?wR6rv3{&BQLi?ffw-%qZXD5oEM2K6f}cVJI^I#fQXF(h0oADL=}-B7!u)SgC8!( zo53|;?TKD~;hU=M7W$R`i+_A&upu@(Ve|uCmFCN!Vq9Ys=@%}xpGH4(o_>*H`j+>I?cgAMM6VmrB6F*Gy}c%lY-#KydiSzFAK=$HHk8}p58ch&9+(wr!o&CH2m zx8H}`zo&UC{ZiEIkKiVHm?t}!v3oL-f6PNEdKw0gWNyM*^iV`Y(L=$WILq%D^E8N9 z^lV7TFV4nV^ia&UqKAS#bF*aUo=u0ojJPCHH4ed*pazBTFYZ+D669b!xCZ3nu* z^a!4a&WRc($O~Ext%Vwv6?wzTYiN4jI77DWfEJW`A zlClITvi{O=#%v;16Bg!DFiwZ&U`ra3oSx>cn9d!%GMqrnfu;=jbLZNC^`;R z{*unPf6bGBco{@7l8YKdAH4OBhw= zL1rd_D)r<8jVh%Ds!T6ZrIb@8s1?`%04n5=f?Va;&}fm9o0-qIO^zVk^?D*)IFwYX zv346A!54;esi~={*@(@CXifwe$tE3AN+Sq{O*T;G*fHz_U-AOeSh#A^!c|a92?nLT z+mKv-U|5-4D(%Y#z077Imvc(HJnxbY*|`tBsD#|lbQ6U`tkG-CVEk*VWPA{bBQ+-@WXbg&C8mdsJ_*PHuvR zg8Qkf)jx)mXn>xs(YvS{gGG94y3t;scc@5jJEylE^oFp&ft4gXvTtrZ@&-EQ{SGN+ za0QN@}#4Y5Vw*GvpO-F!WE{hTZ|!g zquDOEXFPz<3;zxU*Cira>SjCNv(bxbP>_kvSE37ga;yS4MZ5joR=yv+F9 z!ii(vmI&jv#JJm5;Jz&J8F95@^hr(GjN8Y)-Eg34bWKFRpL%bzDa)`HN zs@Req&7Xp>`A=>pcrlh&+cKP!BtK!owhidk=ZCig9gA*e1j$r%nT9OE zE}90r9boS9xsFV{vNu^-@FXO}Fg#C?tdx)gXu>KLatI!OJgJq%vFB+{#{C|iLzt>% zuXsdhOoQrft``ND1RYpRC?MPvx_5kL%36BQeY*1`(@lH#{6qcY$HSu=`dXgo?R^64 zl2i1X-snGf-KJZHpHu(w-h1i+U}R6B7r*}cA^!$h-mpRGXoqy2z;)ma@D$G0+Qd+3 zld<^8z}v(m8}gn)!Qhy9n>b*HO^`4nQjb}KPOm9Dnsr!kVwa+GD1<4jti_@EiV3&J z4YsOJ_^ojWS@Ev!|1}1bG@S?Vt7B5zRufD0ip`r&~Z8pO!FA@d5>W%C#L5$4S;)0nrVDq38c4?EyTo}$StXq24J zqG2x%Gmb|J z&sW6N&(>_*xMt19dGprX`1I2^-nbbN{(*4S>fGyp{p;(wHI*T6@LLbQ_|7{oKKQLr z^VZC zgg8(5!4rgO6atL`!C(gK&4=x zs}!cy4`+c)WxO{TFSOhddSg~zZ}6P>5&C9!Uf&?fD0)@!q|ye281>WOssdAKG+cTbPa( zQ{#K@QG>cgeM?>S(MRI%iz)@}!lPPU5^`%Kmul@&jNHe13J;HK$9f8RHVXY9G?dl1 zB2&TQTh?C)(?=J?`xdvakTU=w8j}sOp8GB6Eiq6(oTaTS0G4Y9Fg(07hC389V(LY5@RatP&Vx{}NpqTx%2V*y z$>%CW;oNuMMYiJMhoaxI&YXM(e?N+G4w7!6_*WSD%eA*FW_rxUx6?8`rhKNyQhYnE z%?Ctc5{Rj`_M-(eGiGDPWW>TkpNv_T5p`I2YE@^ft9zaQ4` ze@-t0p~#`+=_O<^T0@{3z4RG_9t)?JUI4v7J7b<+qDc>Wfnv<_qV=E_Xr^0uC@2O& zvft9JYWouyD6#knwS5tz86aTdAN21*)5S@@Ml*du(6tLK`|ChNJTD=W6Fjqz>P z>n>T?nlm@X0WHXwkuQU~GxOTTNiCFL3E%;W+j~gSn^_R3mlRv+HtHv_eZx}59Lm^2 zSlIP)s8;bHsh=^sGce_D4?pRy#VxXapQ6)o7fPpRE|iY+e2H`m`bKz!JRJ@V>mD&0 z41r`N!aCmLCPGeymYZ*uWb2=PKJ?{Tmz^|Qly#lwFK(Ta`C|8OedB$bzxexy%F2P& z^X^%7&)((pEvHF33!nl1R378(|}0+*vv!ke-pQ6AgY5 z4anWk8PhBEb63BjX8)ufQ17GXQ_Focwq*So>LWi^2hIC{QjeYM!TC3io{t?S9*PZj^Zn_f#r{>X} z>P_l@eyBctd?4aH=j8`Kqw^ki-!}5VlosKvzdzM+#@%c0ewj%{3}Z%SqR&G@{BGcF zPo#oW1W8#w&m}Rb0Imm249HE07S3~Rt_Y3Ig8{!&FhHsAV$wMvVsHTus1cb1cSBYT zcsznLU}xT8?J&uCtR8FDDcDj2NNM|x9)$Y{5ENwiSa!@=nHrP;z>ugvHE6fm1JM2{ z#i-+j1{5+K3f-R?G+K;)FLtOxC222`15?5uh5L10FO?RyUh!2>2}tZq$*L@s5YSnr zSSstmMpXPd_Jw3+R@9-uqCT&VbndzLj`Tlx z*4e_{!}MpL>vtTOOP^Iw)BSp6ov@Ml*>JY?I#4t~%E%;|)#NEqpy_0i&}(_&0M|4w zIlyUJOG|9pc^CJoe2bdMfTfmw(uleA`R)XWKsq5lPZ|6oh4;qc2EY!&Q(?< z%GlL37YrrgB=_WKfClop$)O>1ftHZFawd}tu>N<^pbG_I()S8w^f%Paht1}-Bw+W+o})0?Qqwww!EdL2z{`h@;_oz3}d!KvksjPare#g#7x11ex zg{xL=f8#fLui9|&HEYfuxqoEz#@nufSwb(#===J$`d^b6ovZPSvA{1Sq(rCjg^}}% zT{d!lDJ}AgmO|4lMs6lVa>Ec)Bq1%+ap(azC=Lrt)EbQ}RpBR(C>GuNVTyd1O!g_U z7&kL9!puyJJc9Hj3LN_sr;`^r9jw5~3P?8YjG>KkBohPYfOpUiV^EgCUXLIDg?2Dn zib;O#8Ik0-7z@FX&R9(H>q34blKe*9_%?ESdYzua7g-!m4YT0%SUgTg6IM&%i-;~1 zOpm{dOy9CHvNNU;zQB* zp7~fk@aNw=_sa?)rv5?wH~Iv62bRt9Kwmxu>0SZpc7WIi zwYDnu3xtcZt@R6-^8Etnk;%)p7VYaBn-weM87+KP+a6*NIlj_L{s*e!NatEpI-_ zGQPC}-rj(>KxDnii)TX+ya9c{Yih#Eq=pAXCacSTJJbu+m*B!)^enB!TR17kvLj|By8i$@ zL*#E7HH%Ts7&Wz6!k6!#9HS-&R^-Sc*q>%OIJ6yI7Z{;{JUq+!ZifSZ77?UFh(U#X zddp5$;LtAFvjnzhi^u1dtag@S(H7`Nq$ z=G;~b7_=uRfH%feW!9{lW@hpLHV@*v`}NZX)gT zVS}B*Cp#hQ>JHr_#t(feUaH^G-mW%d^|Z5$6QkO*pqht#as<_YZOz*kjcQ_^YUmr! z^C0gV&(TH?d{|ioos4Xf0MH3S@6pVNjnTA>(r zn+#ksnb4u%gAT1C2Q-=#BccgK9jZk{ zmV8fXb&)1oZL_a6=q#p@1`x zLl#9Qhi)iX16hyDqg%0?K(E>`z>K?OizHk2DOM|2t5yqBt5&8~k5Hmccdyf35KGbE z1a%>nZYTD{S@i`14-taiVh?OZ=%=J zhCkA8mJCI|7<^CtE2Y2xe52nTOsI9jjaN;v4oslWVMzSxK&Afa=g@;W^-t<&>T6Gh zV^tfQ-J1{nb({SiX1y>BN1p;WM#xT$@{xk|iWa#s!mXE6j&SQ0qY1la)to$fu!)VX zFr3HcT{*vB!#yz+C%DjW^c%Bb=FFIkKIG|xqPyI*hiQ52&2n; zmiLU%U?VQi;Hpb`nLioz73s_%SCDJl^U|(8zYI)CxufO@J zdHcF^&$~vy#){``sHDZ#@j&!U%T~q5A)tx5yZ8l{m)nmZC%T!CQ(8 ztA+VS6p8`w!gTUDrZFKE4py=tyx<8N7G78pd|=SPc#ax$I&`Uz*!>B8$HPak?n zc<PUS7sv)f)0fL4A>KCK!TK$Szyrft^ zO^dtpTD-KVOBlQzIT`_qL@}b(om$HSnI4M@JZ*%~G=lN)AwVqIU(OP-hPY1An|S?= z;0YFl;#HWPCU`g`PrxqJE!*|t1(^hAa*|urFYLjf@ScrJ=4MSXjAH45N2fQ4QcF8V zjCE*AMNhN#0918PzvPw-?!(KnVYdj2yTux^WFn!kDJ<=NKMd*C*d%IVY+^A*Q`)g5 z!j2=+!f_DDM&=DtHy##Au2l6N-*xUuc06!GL89NslX`NM8d`?qDIfYv@7N z_y@c*rsXI?-8B71SNH9=(&TkY=ZQoA6QG2Q0}=ITB=Sgw0NFO^K=2gut; zv{LYES}AmRD{Zj~i=2&qw9-&B293ZM8Sr9;0yhSVus>y7dyH#@v8I7y7xh3{uUGsa z{5h;6QZd0iK;%G`bI>|ixPIMB6?7)y8%2{wMnA~9GHV4{70I1HXN8ZuGVAk3nL~CM zeKaM@2$QC#cXeHQneb^>*Xq?HR?uXb?m5upC3t>|;d}fJz6UVp$HHmlBeZyLA_Yr z@oO4ZfBoxUsrdZWm=Cg4eNz3tx=hB5Kx}rLMM#zXS^Y{K1pS#md;t9DhTaHi90?zQ z;TcVIOmDck-80GVBO+)mDr5FxTz(~r5tI4+iq|W$F-gd;*h%hC&TIGDvo7Y6SnXaf zS07^@hZ2RGp|I0biD?0wo90<@C}(#&7^r6#bVv{(8w=i8%ob#kHDb0Pi`jxP#B9NQ z%r~#7ny`+G*}Skutjufm22IWQU|o~r5*vnOxn?rZ ziJVVqq~+y8{M|qP@m=*dPb}~G3El9}9d|sWUaa5o-j271sW@_<=)Ykj%T_RMzk_qT zmsHUbMr|Z$3Zs#ViHk9(`|!lYnA5E#E|96%PD(&(3vgBudjh>8wY3V;8CfqWXMVU& z8V2r%x^z$t1AbpsK*!K%_@g&g?x z*;rXpqeEYnHWrwdRG zWWz%h)eZE{lsch6IR|^ z&s?psJRsW5(_vI+)8fPJa5A3k!b#)wS`8#7%mWP?Gu!$fwg?bGtf$ ziPLaj@A|vcq<+W0?b^9I;*(AsRKJ>goG<_psnFeC&@Lu>FSCBtpr!7zgMcMn_3*{mp{-(`qgM7{S*(Z%p^?$ za~Kpftb{yln4bwWdgtZP zJ|!IHo_{#RJpV9@aJK=TdRPAx$0U!$Z<3QkN_q-zNK0~hA23w>29jns5OUWMQ;e2K% zPFM~im(HO4h-7B2H&Id;Z6|q(LXSPEIZ?s^NX2n^MqS~`nn`Tr1K7x@!(PoSeAGY% z6m!o+*GqH-o|}{==zYM&1Q(NZ6V^$El$$3M)^t#~#8VOu&Bm(Xn$lpqJUT?LrKJ<` z!ic;g+_QpXO3Le|>H=w9)V}kH%`YBz+)I?aiR95+>WKR3SXxgp!eo{fE$?K4T{M44mO0(daglB#Om1_($z(9iP33G3DsDq2 z%7m;Z9KbP)O0x!LKzAt)h7s_(4GUVcCL}h$U=b)wuAxUrv0C^pVr1sPv7`~pb`-K| zfKWSBr|ucmiIB+Aj=uwc!kat}udNDi5+MJny-Cq{jd`(}@$%=h?qgo8pZC(35n=eS zii=^yoG?~EMk+?s1px$t9R*o%wu?j`;NBRB+0)OuZ6PzYJUUQ3Jbgy|#4qjTXY^U( zv4AhLZjS|HMsw&LK8igDd!07?7g$L&*x+Y?m2ALf+YGa@j!P32Q%IuIO`6?KElotx zaS5xWIky@jj_>QMkQVxb`YZMQ&#&?*`gpEz{h8}GdHhIkK)(=d~q zeiCTsB!A(wBgHsUA|>e1--?0yJdPxVkxVXHk{=PFq2qyh8?XqC2E9Ytv!{7T$D=I} z3(?jfz82Ra$cB@TEIA_Zap8F(Djx*n7)&S}aAICFjQM7f1A4EVb}P$1HY#@Kf*!eY zaeFRfuXIX419vETf`J#z*LM1DGhyKeaZCbN zQmP${%PI}BoGoP})|s&|5{IIkStFo-v;+$)4EeBN2Vhrp_hrmzb~f3QHuA3;6LQH@ z7=5d!5RJYiQFaxk4TxjWt}2Nau8L17;a4F)9A%@&(VL9fF(fS7l@6)UUQ(q)yRuxW zK)b@mn5m)zQ?1Mr${NlWWz|K)l^`4z^YL*7J5gOxh=(fxnOp@3W#QOzM7bAzoGY{O z@#u+b8-G$b*IT@F{!&54_y+g zx7w$Nb$th~iZs}*4Kdw~h&L~cHCXL4q6aZkFml~QUkmPv(R(!RDvnD^Bqig{qgo9WZmy9Js2GKno@UcP` zY_n9DBBHwkBCN-qfTapdatE1^0$Ti%}eI zq}w?d-vF4>%B;Y^_>#kvHE}S$R)l(@foV*OvPVntiJQAA^=Y{1R(XWCt; z2JVu8q(vqWU+CbewQQH!bd>0u%~mG*%t94JA37osJnF(gz{f5O2E$_)f-3+c$mi(_ zpZRH(q+#$cPZU;tF-Odg0}l3!ey|j=Unw9+@=a6HXpsJ`%@i zG@_+pO*LCpjBk&yx{8xI#W{q+g{TvD7wuE9yE&)CN&vU$x9YeBMsTHL@om;oLZvId z2qG=Q+aLM!ae2K#hRVY5xYVHDh=nz%8M$RNs1*%rHeq3fHU}2$P({E~FruiC&C+AA z8>AP5-7sa8DQgr!cD>dj_#U)~(}p6w5iMe4MIf}vcod;ccn%0{H0q0l2Azv!&Qse3 zgFk=dopl#OB|p7(?Z~^ZV<(6gjm!{!dT7H4=0OAM4PHzR^z@JqHF_3NuNYm*Bxm$s zRztl?dDJU<(DIVQoNJPEFESwyinN~FJvWtgqkoH>t+?^h7&C=TIU6sO^$-kZY0SGg zSrdcYorR1Z3t`w*7CQs~Wz80bi=kUACbMKf7bHr7fPxb=&cU%`^eZx`QYAbkv%u$5 z(TFEl7+q~eEr%&|a4f*QfiN>@-V^cxO{lp;^|j)LLqE{XI&`tvckt>LblvUkr+wq# zW7vW!*Qsj+qTf9>%C%_K$40pxo*f<=<;t^I1ESDq!3bsa1|HTY%F~s;#C7yqK%h}aW9Oa! z*nQ`*#$^_*tL|{?O2xc&#pu#}EAoEO0f9CWU}O2XOn7fY@In*;hURk3(4Yo-Oguo} z=HoIa7;OLeig8b)x`u8YPp4v*dW=q5wy!W^dW=rmcu=zi<3aOu(q#l47L87TcNnp7 zIvI^R9jDWPL2nif`dZLrJwwLnF|I<-E|E#CY~ct<^zHH9p!!4_(8udO{+M2?uDR3D za3|_J`h^e>Ki1!fF7ag=w--k!jA2@}5ehUvLSYQk%F&`ZQ=`7Mxj9EMZ!B{=fL9ew z)Bv_+`4m2OH4W2NCsfickEoBSk3T{O=jm@ebhq%z$ZT;nqZt_$bZbY?0Yd8kaCAK% zFWUprIwe06*oW~Cy0z*`HgR7}w*z>~Pw?&eQNZIlA&+9yP`l$eA%9-$5f_#bV3J|n zVYOro0hYzx{LSh-v|9aYe?J}&S3hO>uZ5$Z>t^dtf)1HWztHGeZ2y*G45YPxEZ0&ykA*=8PeOvj}NBI&_McfgB|>ZX58@`iz;!zO9PE zysTX@-~ix8FTjmc*{&{|@R=E!9%H0{O<*^%-8@b!n;CZoL~0Aur0KeXoy2`(CmMLxaj=(jKJfQPD)Y zfjzzfLqxK@4I+D|22>0$7)!GQuZG4%g|q1?4J0s^k8FM6slSv&l9ku5JO66?Q}$1O_UfKQ)KuyYALl)5 za*5p=UZ0&h>-*PVc4p^oXSVotf*9L;`yJ2vN=2i2dbRnq?z7L5m1iCw>5n9Iy6Y{r zhKklu;BxxobwP8`y=*~e_leV6?a>(c^HT69GEKx!D(T4@e-`n)Riq4~_*rzO;d!lj zJnsZr;?mCNqciZPDFYB$j6AED0^2a%w}XwXQN;RY)?I`C6zTnJCKh095(}^7-CmL9rCA%2EN^JBVM|9XaWK9BK^dq~y~$?;v- z5pzE9Nh`QG%)l1eUTc_raX5~|PB_!Rcmn(p_p_6}F|=+R;{la8d-~yM!Av058;%ut z!(?S1BqHZ<%oGb-o3Iv#@K_xIOx(lD0@$-Q(;wA-gWOS3l{=3@T(UDb^l-0->I)|@ZWgt zJ9~b*^B2RZaV zD8NVmD-<9m+B!B05CzDi9gPAUFMI!+P=HJzG3F2Om*RN9{Cq+)(&AguU;pSGl^Q?U z`D;v4)wlNMhwiv-)vW<~-Rt}4RQy_mNb7#SZS6H54()k!{Z){u`DhyzG8M!a+HW7B z^D%}tFH@S%m-0HlvWS!B9wXQH2G%4Y@|p|OeQ92>(Ah~LSO8UVr@aQ*Bcz%zc>^1|$z&`_HenbilQ9V+1GbX$*b$R4 z?-2Y3xok-GjgzsmD3`G)*S%3MWGIRU<%P^qc7%|XMR5T`4j&<7rJ>$vs32pafyL>N zqbP9yQE?VN#$~6XrCJ$q{!wu@*1WnET+@Ko1aMxvH4V?e%{TuJ!c*CaqWB7dx*B|MGr@cbzz}&kMw~0U9`Sxq?{`m0^zeFo8fo4oV z!a``pA06F_CQZh)RxHe?fsxR88RH&qp%s-HU}XB01d(FHDfB`kXsyB9JfkEG>GYjlvv+B`oCX!?oiC|KFC4%3krE#&{ zMO=1qHr70ska|AxhYu4;Fqk%n_tvEi1`iuo$)=OS==jFZ)kDYG%{Yv(|OW+k~6$J@xc+PF>HuO1OJL{m)^l zhBakytS{Nh!a`v6@q-rH+7nDsCAhk*GP38Nc!?4X zO1oGoN4Uo_OzVNaF*}pR>(dlzIeAR+`nabnlJl}&pHI`Ht|7UGyDLC=M#m8KQ6^q7 z@)*Rc=)u&mF%C+34eFRFw=4iahsk_iyLzP~v}+C2f>LvocI{1+#oj^W-hNXR+U?3V`bgd7`+4}C>xzrRI-@T(Ivo_$q#Y$jMuXEhaRWr=P>g9 z^_>^mpP_#`IQXuAt91Ew_iwm=!wsA6RNs2_4sMC4ba-qBtu{yy%f?v$8nlQqB_zT9)@nIYv~`!ALTUh7}PL zW?$TnZky$pe`XjNd}+4=OF|(kC9s4H-v^_}@O`Wo31=A+6mv1c{N?b&Pe_Jy&8X<3 zi1*!q{+yR%puVv#n5Y{qg{~$TxTY}%PK%q6Pjv|iU8t^)R zqt|@K0%niT0O9wse&e)D8#BxW?K_>Fr5DS}P-o{^Yj57v*$FRk|AbqHl#!$$f9K*K zZytG-$<-|&;~Y@Xj8JQfM#Cb;)KSz%MT{w4_3JREiV0-YH8%r0rjtI}!wFWV5sYC? zL9oNHrbr1$=)Ky9bPTYnHp}tJl_fNXnRH`fcsZPg!W~Hyem%~pp z=-l3E-T|}2X$b05Mf744^X2-I8#?hPY~8AxeDH1kjy*&e{Zw5>XX72&*y0zpcPx%Z z^A+EbjYeY*4MJPOG_F3caV5Q^FKS%HbPD?AfTVSM@rn$;idW?LRd_;{T{$9&dN#Hd z7*})}L4PYQ4}V2T?{STLMRxd4ykb5uVDQ$edNyTcn!Vx8ot?dRR40=s&Cp&mbMKA% zN7C~Qi}x^@K#S@q-ZnyY+PjX!O&6jxj+-8jZFOrt%9u*{|M+ImZ8C*5_?T6Go8UVMq#e=8G`Lkj(o zx_0E-LcCAKuT*e)(pg+W@-uc7<-=0soqtL`n5tniGLD?b5{&ee0kg$=1Y@J7ta9l5 z)3lzU3bt?>{Zwn=X$4DIU(|ZjxFrn8)40}~UetO4o+k=vqRO?Nhs6%1WVzx2US;`! z)u^kiL)Q%q6bsBTghXI-EwQ<<%kJu#%IF z`#W#2x~jUWDlm23MreWEt0c5SzVPeVC)7w zR0g-mM#HdTO#_%sC!EpYJSI(20@GpX#!e}?GOpl@;(g27yH0EG=NGS#+2x0oEp8u_ zoK@9H7)=?^h~Rnrh&}J0zA#eOq5U7J$fKpb(b9q<_xOu}iKjH`Z^FtD7<@pK)mcOf zStnLIffjP|#DJ#ced+u(KMdi;#%Uj=><`!c%TBAfeDfX4Dt)na-`l*Uv$Ln`;=az# zZ+_#l?{;?TCOF71sn`~;H+{YmEcb1(tL9?l=uekmJM$AX+HIusO)~mTmFkPp3_D>nDAj9FQO!LR8b49L zq@GVcrEc{j)ZGt#@`E4j-~WRjd;;&~Qnf>u1g(R> zjCN|eu-LaWp{NVBz9n0JQQKTmW7|TW3lq1+- z32UC7)Ny~mY?tgAmpX}-F@4u-i?U}#%XsRf;M?nX&SqSCGM_m)Iz1U0uZ7IEq82ij zMs3hSV{>`ioW-;X_H513kkeL7Px`p-Dds1+3SwS=-sMDs(tqi))6DCBs3j;3u&wzF zIXy*d!(zY3kvTBuSPtwkFP`;z95V;z zK^xl8hCBym{TFgzthXAimrs02z`Ya_Uy_rxo>P*Ui?85_CBCf4dhPn>vtG9U`&ln! zE7>Ibn&mh$>lJ+G;GcKBdXe4oOlPNg&C~bZa?dT-J$i2p*m-2Vs-JfIUObQzl2)X> zUU~b+TG|Vi0weT<&AjC{Y3-Ju`qD&0McE5-Kg1yixgQcP%ANy$2vr=2kO>PK6dTl= z&12F97Bx$|ce4p9kyA*-I+Pj!50($7vQEi??1kdPGvIDOs>THvbin4&8@(711CKID zF@Yct#dw#Z>|`C8MLUX46Qlrn;(^oR(PgIv!e`wZ3Rm3q7}MulQpsE3;Jx%Xw}o?& zpB=;-)*gkLoltPOrjU|+5`6-HRY{(}Uo~A+>3P)bg!Gg#)U0-(p=Ku(P_tm-R0bhv z6bNgEnhoSpv(JzeP_yM$5C547r8bpE%|6=iuS|I+9EO@@vkuAuNe=A8)So;l7Ywkh zK~Vdti&Fg5#bigSfSOGVDYcVvL#-s&j-h62*$w2VU%QZ4@;GNLmnN_d+5u}hTgFhc zveBwV48HcFaoUdP}1PEUe_otFF>0_M~Qf zuxa~_ftsC=DxzkwHvy>G)P%7s2LYMIf59(H)fQ2+DIKW=YL@@>1^3H5fuNRX4&sr) zz-+8Xp=RN`@*fr`WIFgy3!u*q|M_{?0%_s*rt4N;aB+3@726+fnft3(fBA{+1^fC9 zmtS37ebx5+Gnx0czWlLK_#bOgHTjpN+*!JAeMQ5Gf-oQ?j&&jO2Um4uQ z{QubKp9P1$6K#5vCU?dBVW`+o%=3rbxMUq(J z)Qv7908ZxuDZ7`?8?YSa3^Q}@qWyqDdNBB87ohSc_VXyaFz6VWME0{dZ1`XW;?v4{ zs1^nmV4}vb73jdM6C9`4T+)5dJ^jx-(>NtV9V@RDcHT`Vs{8I9X)fop<8Oe3{a!a2 zdA+Z+cL7{b%jzYHIwYFcAyF<^WyM&A=@6pCq28H_4?%q3b!J08VD{1Ht%KT7%m`B9 zVX~pTG30Wv8^^8#;eRqq_)uPSE+b*HUWudok{RNmoY!r}V6O9k9ku}_nAao-OX95a zI_EZ-(M{OYl(9IFHg{+6!v#Evb7Y4_ua4rsz+<5(#uu}6Gu4H3G&&or_NypbKQEhh zwJ>6?KFvCNHG%^1*Szv#=4mcF&Fx?N!r&m&Q6ndISc1KPosERU>~5A9C2LrMvw)p7 zV!+s%Y#3ulHB?y;KT5DBYc>{+k1V%g!jRQjf1pqvK;Po(tQ8PG?f$WHnl&2oIN59< z2%reZ&e}1JFvrtECu!JOPde?Xt7F?dVpdG2#UwpZ-Mf7G8*laWy!Syzr}|b`_s4H_ zfkmDdmmYdb>^%6k*m~#(Or`>msbTQSF_Ex&vwsv4HlIr?AYogNiG)S7emxQvg%}e2 zHAq+#emxQvh5rXg*wtT;gkAIXNZ8e1kAz+Ge-{Z`Y+sIm#fE(>4C@8@5j zK0w!9aH0Aa^#JDDnjjc3i_+C2e;fIbKA^7T?W~?8{toRNCVRLQBE???i4=7L|5XqY z&i^V%w5Sup+zJ^^X&!XgU51bgzbL`{Bi>7Z<~U{wRAE^&6e4Rj$l%Ta267EkwlLH( zb%dS#JG@SXjoDwFbzscCsMx~nMu-r_4?ksn;TV9^UQ^bN9Z|11(7Q?x!;e!6w1T0k z-XCB;j{~Cc(f#~W{GGb$TX)|*rK)=NtR>S#b?sSa(-K;C=5nOXU;5hv)b_NK_T1vq zWe#l+FNP#ELlSm^LdQh5GD#RcbTqOxF9}6tYwHon)^q;}Wb3*ABC@qo8@j`gt=K&_ zvUT+l$X4+G*CSh-h3iLN6kA5F5$-&6&8?z!(V}_BANmWnpvr0LIl^f`Y$nKcn)iYM z|C;wQ_FD-i?!6EhjsnI~j{Q~wM{MStjRe1OgIOGdCj2ss$;oH9*~1#2$|h-0w28J* z5M8hM0T*C2y_ii23&&`BYx@BcaK`vgyD&_l0Y9ULV4USs(7PJ&RibYd^T_ha##{i6 z%@9s|^104$e53QZC(k+l_{#b7D~~_^9Q7P~@A~0aU%%}2SBKa4+BcY{E`3w|<;sRo zZ2C&|mv1heYGU%SQzi5?16Y8Zq`hY`%K}7f9`C4SS-$2sjK(8kS!ecIq(9hC4zQmx z+Ig>vzU#lU=M?B!R-k9`ImsiQ6V7LI%8TPlxg6#^d3u^HVa&-8#;mPKH|OaYmIh1x zrGB#|QlO_J<9A@;^mNqrQ^2J41$uhw{U9VJ_nyHz{#_ugv3|PW$Iy2^)@_Y>+)f*# zqej`&mZ|4(64H{D(_^9kr@bqIkE*)*@4h!{GLxCiWG2fblgwlpNFX63A%rbM2y2iX zgvd_9zOUknD+mZy5fl;EDsJseCIL~g*rH;KwJNpRy0tE?O114*>qcI_|GDqY9;&vs z-`C%-3@`7^dvET$_rANFd(QuyfB(hIO;xxfrjGr7l=D)yMpGf~2V!JwLVe~Wl+&!1 zGs?fhFZJDBO@poL6ADk0}3#OZKp&4wvetELTInAv+y z*+~C}(bcP_mM`ty_ku$9-s}mUp6;TtBiGNKP*RcY9y^lkf`5@$u_rWr@M4nLQzOfb zD0`|B;1mWg-pHfusS3>3sj{b<>Hvxcd#V=V`A{MCG^kGj?~kyjLfYW$soGS|o(k$z zirCHBQ?-neO|0M_le~(Fd+Igk>RVQ8<#;Bir0F=0s3mjAd2O>m`j-IWRZH*`#p-BV{ty zm0)$akW3|j3qaE5>9{NPalbLwPYazqF4hHdD?;?G%?soZW}93P!67UDG*_Bg%w^2o zqbD*bs5F!6P6BKP;omrHCqr4cCs?h^%D0q70-1Y#5h^|$c|pbJ^Tw{g4P{#lW#QG4 z!<7bQ8dgUc$U=sr9y5}5tWGB4hLH;^K_oN9Gv^N+ zGjLPpBN?MJ^IrFruDRLyNbbka4(>PP{NXc(UB#^LRg}qRf1TsH;oh;iKCR&;SM{>T zH~1ZCXFoPxx#{T_nE8#rm5jd?P(2N+)}OsVGHYIc>iI<_*)%>i!i%~YoWhO5=Sn+K z$wv%qc%+?})$!iU+sTMXJK^Jf&;-|LH<9)E1h)?#8hV6ap${fw2^25j5kXv8K^lq= zT_WB*<3N1qOR#Y$0Nee|0D2t{m6w~y5gyTLEB)n_`0zojvLZysZ6-IsKh8{2?%-ZW zMxRPLVC@Lc=P+AOoIZialnSm=e3ZwO3z_BkC|@dtj7og?GyB8;FI4EQ02GZ3*;wnx z3@bh*Ot25#iz_?eAPiqM@a^#TK`eX-?yNF>W|HMf9h)@Gy$=k_pEPmgy!zT5y%(># z&Th1w-d$>QGVRN6eD$b)&lOv4_f$C96!~ptZz-!~S1&FZHF(5`TwimJztU=2TQ=doG_)(=hI__rH97?>*aARWG`r>~d|xxJx%xm9jEGC#)qd>f?37i<$u% z@02b7x3$8%-6bDo%l$)dy0;Q&y@Eg)~PA zJjEzNFZka|YA|UK30%LbelXdA8P4f&D)ewV>EH`{_x}9nd-uK|-xj=f$=qcN*u154 zmt7rfyK4E|B ziQo5n4u0h~774y0eE2V1xy401EqK3^FCgh+pr|8vFCKumR8y)1;3a1jNWyW>eJQCa zigO=L*?a-4L5_oIZb-d|^_P*In`3i}y?jN*f(Jno@oe?(WsC06|<9rj3QpEJ2ZR*&_eu0>{X>Z;^aXG z@2i~2YUB-z7qb@DvS_iqk@b_W)UYe%%Vy4!FJo6~#J%gq%`nN%=-j$q_wai82DWEzFB@ye5iA(MxejQ({%X?O}6%Zcy5dl-dFNe9&Uq8 zkv3S)`;{$F&imEaNWbE1e>K6E-zZVeP5 z0(hNZtHX!iIkFCSW;^N59zu2xeE6L<;-R4)ACLUTkj7*BcB2F)=}ToOr5%O}=!Bu% z!)Sq-3Z@8g@Iy~aKhQ5af5yri_Pq4SH6`V?*}r@^_&m<7oIQ8r&A0w_$2Go+l-Uax zvR_?&=aR7nc~e_{x@b&Z&Q$ID&-`HV+WF)AU*5E=V|8Ffex`lVg8ffE8(6w-{`jix z%{P2+)%}a|GfWGX?VmH}tFt37n^e;;&Ea1(a?Ykfb=C~9I093t`K35rd;d3TPdZf& zjkYJ1H=bqar3@yGX{3n%)Ov{UL7?#T;EaF$4A|y%4vpXt9q@2!q%+i>pdc>BHtDg) z*x1J&)87BmOD_rH**|r?Em^d0k>yQWHnkA#1j^c$5&;!DZ9hM^i z>V>t4K)qJMYL*6JBTz5b%QX3xq4F+h zOg<(*DGy`E5S|KuI|VEb*UGyPdg`z8ZnlP*AaBSMfwtSNISiepif@d>l7J_+I=(0Q zZm8$^Iw7Ee7#a!S97y3{;cp(lIgs~!H*OmN%k%$wpACQ_Nzwoz3YQyVgbI$xX1l$w z9XR^&$IspVi=1s4TNht(%Wa!mw`YsvZ+(Ore*S9L1AC^=d-S$FPqxky;PCx!*V{Tt z`xf&FOGR?E)Ch;v*TXFC6`pFvP9>8x=Rc3W7EO#++fGWD)i` z<}l*M7r6+b{U}Hh?{CYpkP{zZ=_X-Qi1^D@7d}|Z%TEV-9}n~d##wRU%QzhbcHvZL zXznmh45B#h3FS5Pz#2qb0&@a)9;rcA;Fy(LPKAnb+$-;01Cvas+~1oDMMJdeT31W) zX_Gzcu%_2j^HCnkzT-do^s&AP-ZDmSIisa1`OZ(LJTfV$?uKY-L7L%lA zjn3=nl{1EUdRmPs#@;q>2;4qQXenx7=JQn`BV(A41r-Tt}{>EgNCqIx=8e%Y})S7O+7pT$Z`= z9IFQ?&x2{{d?5ifFVez7AXp3&62e14ldi6FLXI3K^nDyMQ4opyX6A9pcuHT0zet~$O1!cG<=(#K=+awC>JIs1rAkMDIXcZ z@@}|i(%`vW6DGd5XT*q|$L3#f!Te*aNZNGynu|wNo8^na7A#-EJ{{52FeGM%DiqeJ zGt^q~1I$oss5fDT${QW5>VAf*qM4^Ey(TD%Q>F)`8iJx?s`8FrSn#ND9Qm*Ae#WY% z-&M!|4qT&{y_k@pmBhW;&6vmjS+9D8VpaIH;Z>g@Ua6HfZp1jZN*X1e(B6*KcxKqM zK7y)_^1BkM8to)tb$ zz3h=kF1z%RN7$H6)240QIBnV{?d|-;rI$S-|8m+UewN1I30-Hw$!g5J`n#;`M~Iaj!!Dy1-7PPB(2QVSiRA2vR7PxC=nW|jQ;N!_ zMdLMvUsoC1wV1bJS=;5|M5-m0?`5OqBYWi|lfazJz#}#oZ}*>Kcpe2UR&64~b3kEu z-iV-^|1iVzeud$AKLsxM`wY)mKE*OTLrTnaihGybcza!XD*I#S+M9klyRmt~%<0nA z;v=_RGj)pQmRT2#nb2U*ylndq7xkIwZx}Os;Z66C8ACOaBK8#bqed#i?9T4+h!)ii zxp`~MR4$kh947qA6-a%~7{E|!0i`b#o(SXsuVuI8@ToRfT1ul@nGiS=DlIE5Ys*pD zodcZR*`0$CO<{L-TLbRX!Tfv<+sbzn3M*f@4H{ltNM?6NO$EXK3mtKZaNC(+sT=Ih znRc6^iHT=--cO4zr8f{8c9`8+rJ;c!r21%u$1k{=V72P;OrLZ9oLMu@t2?i}T=RoL^^+%U+jGg1`9mG%i~(ydxpC(WGsd1ba?IGJ z9uMmEOU;ww^*nyYaHS_j>^a}iZbZ{W1#1&$fq(;Lu-mKHmxtewKUc$MFky#0h2w{^ zg@^eQga`&PYDwW3L_~~JFo;o0ih_E{29m>wEgkqF@Tat;TM*bej308QW8w&9XJr$n zFi2n-1tXMZQZPcKl#0a&2^=G2GJrUcV}w%uDL@HDF+xEn5~7z8!TnUF7ZT#fM?U`` zU`}WCYu0Q!*#6M^SC-Uev;*w3a`)~Nd&KEqy|n0Bu`W~gh<_CSNNu)QzDN2Jektj~ zUzEB9fq^m$N5w=9Z?ndLhL508F;Ve3Mx5+`;W#Knj)KN+#72sCZJk&T4{o?v>8;_( z_{P<&%}De=R)VEFfGc-|5Z?uSy(x&qu5p6`pVkyT<5Fh zCO>th+6KPyYLKG z-TF8h1wkr-Wl_N)Ciki#jXnXXWarSmv zPgh|*{kZn`TsM@*prJ3D&H=zmKBL-@crSZIvQYCGiH6>WS9DDEtI&ZR@b5cSkh|@L z!5kA7))WB2NYJNWP(L_;WbRM&uL!w}c`CJK9Qm}_4n0*^Nu@KYiYTjHXSW&$VT(~V zMHsGhcBce}tJt^(5r!)v+bMzJDpt9rD!!~{=)R2g*pYHeT?dm<1}=ngjus>-uuZo; z@W5@f&6q!b25s`oVsqzxQho65d)nIWxjVSzqSb3IzIe^*i%u^<+wOjK8X3}~!;YeY zwx}w@=&-}x3qw*Pyx{6XrbZs970XXap?9X$sfj|y0sG)j!Fl>_lmuFrDl1f)0bp3E z(K;O3;_$(59Gd!*~M_<;5Hq(|ON4rMu(%U;Bn$W&Rm(!8hNAMgQbVfAlL#Bbor_eBhz!6-uE3a$ zGXmxlSwc_?>TwJVeq*N9YStoTofal<4NWTuya`B4o5Uc|ZUo#I6pIB2qzZRnU?p`r ztwsZkG5upfo!NRCgU9fojyRdy{2ICu?7ZOO!egu0UF`aP{Vuy~>mN;$Zb-c-bpCnebI+}0L*<`ay1InDU8i*> zwCF zR`t#PcfA5FOho1Z`rtcRul#o${LdQ)XQH=UtoD}AcyHMzOya%eI_VSMTSVb8d4Z-@ zGXPRsfpBkvbROmCj^{6na&&Y4GN9Utqr0Ha2+_s=^&H(Mu$<+Q5j6kwSp=j`;OMsH z1L^a3IJvb%DM~Si6g?Ic9lk0j?8Y!0XFBa@?&6}|JI#mmfB981CwEu3l-!ynzrc(j zKZlx`$qFF9c zPDr%mWO;)*N<1N~T|gq^pApOgh#70YHIdgvWUr7zFR zccU99gl=FS=?2c3<6Ut%&USf&*$FvzCFd9ss?{9JuOP>z$gx2vRa%j*qC#jhXq)O^ zkA^TstXc+!b?fWL@t*`%J5eJdb8~)n&(^K?{A>J4#uEXeZ)3lj2} zmz+m;wmY212O+`DXIj{NJ(Nc%f(2yKPe@TfMx9nfb^u%y_%CFwI2JBi6^rYVAIzNF z>~1N}ZOSRENLi3QcY0<^FHe)Bu#YkG!i70Dv#p|6cpojtl7^%h#!oQ94i?2#A_iajGptCxB$aVFnJO-R{tt9f+?1 z)k=nBeYf4}FgodbJi1|v)r|lD7Yj!{SD<&da7-P)V#RpcdJh@Wo3?4n*YPVRD&Kn# ziMEK1j{1M zP*;{KtF71!TxFh%w0MM-^Yu1kElW!bC_P#`diFFI`*aE&eW<6Vr-t9(QQA}L4F#IQ zMjz$fJghic!Sh3>#fL-MQlVf7`izLBOTo)ijd1hO7{Man6Mw)Ykz*-@8VHyghuh)I zbGr+Td^v?+xilw%G!q{JH3=1My~{)_px~#81Z}XAfzpJN1ZO|WK$uGbd%%0vL@lgM>l`a00v&?`PL=Qw*OCq7eZy1{(4l*fL*2JfebxRQ)mQ#E*L-0F zXqKxuLptDz0{znqw~GHm2e=jhw(?^>KsJD9+d95h@XkiO^UUgmc;TK5Fj@UXx|vnq+yAnv_6I;SHJ+a+!kFdQ^7c<%XB3 z&ZyMnV=&Z>k4te^T#Bh?Z!k3>MRRhBDW()P#ja10;%GHRk-zO5Sbj_%Z5A&`s>8Zj zMZU2lr)W*JhVw|UV~?I{vJ7i+l9=`|H&Rq;QZjsP9>X=6VpVd9euICUCmzsjHki+f zETVpc?LCg8JFI?#?OkPrRHA#C!1`hXPdo@gOyG&PflUD8o&;(PK=u5R4j&prLUaTI z0uZlhD1v1!eM_FTfSGhmX9M#y*J-xAD5 zYnXu^eVrJF%k(4>^-He0$9QBsXNtSxQtY;`C#Q(<=-esZ5SL=N<56;o7?0vpq2y%_GkFi?DGGUPur^pMJvi~x5XE{^ zyFSMacBhuw@ls?#-pOGFO4qWa?U=SR+>h&78iyKOqv#JvG zvDH3SfWW4;B@$tIGvT@c3$Wlx2d69;Hd%?3C*Qu~RWQgdk{8Lp{?Q@-!w)jfG27!9 zjvpqPSt6KiWw){g+r>NNOKu`w+28N~YIFOe1CG7G8j1HlbY#F2zmOjz+F9*v;+uVM zKReE@xsjv}&K}2md2P6ti|rcDrg#P7$lt_!Oy6hD*JNr>OKvqx7|T49}HViG|Z7jYS==$$@H3?h+r0)T9Om zcrR?v@iV#;K{^s@((bTZZKz4iz5z&tWO2`c9;r>~QyJkt<%!I`8N5%W1~Pb`f*wg- ze{f4_NU8(P0m2XGfL8lZhe4;;OLj$uCp$vFOY#Tt0G42JM5YN!$(axs^DTnUfw5N-@0o-M|ba15kk#T;0SX$}lxz(^?*UTwd? zf)@6Z=RW)FIeGAc7U|LhyX6gR$L?|Cv`_w(o%zv|A2E5GRB-XNvM{UxW8QUL?`Xb< zDAxUj=assM;1B8|7Hz_h2-Wdf*r8Va=ne`Wzb%3x6APpQF& zz65Iuz}xG}w4OL(E%%lCd?}e!TjbEoYXza8DEexMB4(fhQ00CN9QQQ*w*(~(nh~SM zZ!Gj@6yl?#-`8*Mix0o2Zz0kz?F)TehBe^g?j4G^4xtPBYT(&HE{GJTjzBUsVWl(0 z_R+y|4ZU-%f(U?b41N3%>Aj=M9 zq(l7f^mfx@^mfTP?ug5A;|OoCF(Jnzl5;$4_%Joc{{p>TavqPw<*{j$H`tVr$5F|7 z9NjoNoX2~?@jer4gnv?RcQ5pIc`YTbCYP$WbGE8_yX>&OF1)ANMk&YBF|FfhTscNe z_6A2Jlw)#oIfhM8>-yiLw^MR`e@w0)=ebs9i)`U*TS!=f8#X$e@2=ThuL!R&9<@{W z57ygdo>Om^lc={V?N)Ec>sDuqsoP_5bvqj4#pr|<5jS3h>vqK05lY*ZsFmrSMHIb3 z!g!%s+^EbJ2qvsZqZcT%Mb~?6K0b4%1blqvoT&uR>w3P8OUS_@;PV7TX?J0-$G#NN zhT7Xau2IBzQUr077&9_m7|8{o_o|8Use z8|zF{-3LDdHCBVa568GQ21|Q`8mkmvRBEhxI@Gjz>UtAJMjiRo;1JKkVZ^5<=1iM^ zYBSwAwHb6B6a39Q**2lsjNbqzXXf9D)x%6N=7TmfZllx0>Y?03KcYNBA{Z{**(Ox` zAY7kmuk6z&#mD83z>w2{!N>i^G=HBoeE8%zBdy|is6fl_s0;Y4fx6Sd{{1;TwSOJK zQ^D5-6zYVDj|w-nrKY5S6@((Fu>fcb5P;C>hYz889qyyL z9F7vevnOo`eTnhx88Ub{dG-wP52oCMRAk}|LIzIsPoVFi<{1h>FJcpzoKzQ1Ke0nm zTFQwaF^nj{eZYe=YcZ$Wl9)Xr3|p$Xjpp=tzaf@Py;U4T2U78)7GIWl?K?m31RaQe z%!K_5Z%uFWeRTA#%L_7#YH#?({*DLk{$sC_e9u)1N!PF059>x8xNO6M3$C9x+@TRg z@iRli=$-q=)mk*;rh0lwcMx&NUCncdIOK++(waV-@85mn!x?UUilMSNRUz%_oN|>r z)oGj71dgu$qs-Yk?}A`t)p^@6J^#PcvksQ4I0uyvjgS86h2x@?4loCC_8cyuAn*v+ zlZC|dA`*&D@D+Q5zJw8{IC+I%(xW7-Ge}OcBQC`fSfff3QtXkOVzICI+$kQ3OR+b! z5WN#p>_!VwR#Fz9BF$mpQa{d9RJ9Po*XA)i&K8#`xr9Ux)DPQ)7){17jx%B%`IeFD zRVD`xuvFu7f8$8?KcPtV#Jzxb%Qe57`MZ54{B9XI6;n5g;H(z|^|)%-rr+(=P*x0>!Xzp*KX zSFN1hbWV$4x7T=& zBG@c^E$T4|)LGATIINZ|KBi)NP#B;{pQA(*&y}Fh3FW&49pcf1oR(bfRT#7Z@`|8a zxz?>AgRwaGEP_EGIA*5BnP$m!TAbHf2U!PgO*?t=NLHr9X|+)5%IApHoaM-sa7!9J zopXL_y#fyno=L;2PYX>9z|C9_lS!HwrqIfhV~<`Z4tNW}!lf4u0(pR>xF&TE`C?cxa=`qzxBu3vfHirFjA z$REis$cM!qhkx~}!-rp8zMMuR;H|(aLsIbswbdRK>mn^8)m_-f8!Sxd?R@}oj;@)w zy`rFZfhAwj!O+_)<85qWxF5u%_>bsrlT+-7OYv*9mvMt7T>;*q!%zrF5ONs;O? zT@!vw2LEiM1)+2}A#BhH zerP8*`_E9dGK%9GfwRNlekdZuX#*xs9Dq%(=Kfqd_3Ixe4y2=j6WRQI;^y!dtjz1W zUeYvaW}aLBtFjOz>3`9~C{C=Z|AnNT;ph5ag^l1F>wlH;;y+3M3oTD2d2d2n&h@{( zY5}o(NdHT^pLAj;$Yix8V#BW|ZQFIILjL09)tA*@yIcfU!E@JKQh)6#qAR$+oauiv ziT&pD7WU>7*Uvrs=4HF9Zn@2_ucS$eWg{j5Nt9c8IYl;?^XKa8YuKo)*@Z>dlfm)Ltq7bRcbaSpd z*;?<0?lhq$VI9k{DB51mEdgzBCire=E2Bch)*9uzRa_lXRab{|^4+?L?=~yKcN>yw zoCy0E)EdAwXGOmj`&qzC_o5! z2AV&ofH@VGR2TRqoMCN~?xFmiFj|2GOqrqbVX zC2S(SM04|oCz9DEKy3E=2fAgKKo3*e1+}$syLgYcsz+>zs*+hQ2%H?6z$nSj2nkpv6*fokz{iivZ__(TF7pR*WiG%>n}`N3NiK7K zQ9ds-a`=1XkZd(!#PYYx;kcw6CfDIdadr4DbBHp-cS+mI-*bujRz47SM&7eFHP@Rw zZ+j6LCOU5`qhf)}kjCfjvvSuvLOWXae>t@qAu$9T!>3bprT;@tZTaPI&8f}0Fvkkl zvsZ!nZAy^6(j(dqMfTFDvX?EQ?cni*LgExlT|IbQvD#um_~R28U#h99f-9^ZhFs`) zYby&YX3uWAXn-?ax`1t1@cp|sU?Bvg1!v&Og8X&5^^~MOaEo<&eT+!i(67)0XdS9X3k7a1Nd4A@~mkSY-Fnm*apo2 zTn$2h3g;q=C79O55=`?@Z7RWZ&;sNZgnk<^+gS*;R0#pYJlvNMz+qrM&Rd?>o3o}S(u)$ZX zSaHQyeJ(SD!|bV-o>4f=(9a3e^D&poed_%4n>Yk@PHwVaQ*JVXI?t6CMo=r(2a>PD zn*BH8Yz4(WZ?HH)a`7cwA9|GZP|<~5_h1b=OZWrU8wI=#izE|9Vp71O$AePv04YAy z)2zoH`@@SQPw=wo`ynUcvV9s?wr+KV$z==U%^?~7qd8IPj)$C`yUzJgGan$QO0#fGvDiznB;@6tE?g0HtKqw-94IUZr z@u0CL?Bnt2Kj)Dl9Kag}^bj@Ko1Ven@CJIr=;7CO-oiP~eZqO+YwTI|8j$yzv3x~Q zPS;S&yM(StEo%fzx;JP^sO9wJI!Uvp8CAcX>rl(NJjHRyg_7cj(GJP_RwGsMm-yf?HLj>2y_^&SdU1#TJlGQcy@}GQe*xkbx*q>EttqcwC77glb#X zZkC{wfaGI6ei4O`B1x*H&(1dR|zT0%OsBo&>?+DwR@a^i$PB{j{W70nXbn02cn zBppfBYSM5@vg$R!c@G+dIIysfz(oxlN`zmnOxM@cDnxm;4n6s+!8z@zr=D8S@<0D0 zejq-%Y~wOgm(*qPl2>{t3#RF0?BnK?DPDi*UD-4Fq#&8W{hy>DqK9 zqkyxgrKg3X;|oCpWIg!K22;@;m$jQj(LnLB)DmkEl^OCGO3dR?0+Wh(lz{Q4QEFgM zNKF$J%zH|TIZeY%qBcD}Qs@+oWX35mQfTnMr`zmlS=zE0RS5+yEZh+YG&j>Gb}ZPi zK-|;$3+&GRp5DLWaY8d&A9~-^?&Ca8y2Vg;<8hJ`@i@7L+d;|JK0!?}*2b9X_7r-9 zo`jxM*o__38?l2@pDyEVl;@GR`zzG8eNE>r(#L$(=1-$*>{rot4{w^-vV9O&ww!`w zDXk#6Y}xMYus2O|**=P~cBZ<$m}3(?=XxZU%~RqD+jzNu4j!o@@Bly^scM}2oJ_n)*>BYu9@>-tBv??K`j zD9Fm&D+#Y=2MKP3PA1!JiR#l+!cmliBmM{6rW?v{(vbgie!eRoUVl!Qq!tPJ7|YxB z8jS{L%&Fx6tRZJUyIqq{3tA#Jg8#GKYPYuKYXFdR*wd^uumua@AOt!FtVK$NLqVa> zQOpyCv^HjsVbZ=`C?N-Tn;w`<@>w=J&1nv&(^bM($=qkT)^D!Ghl35PYk}6!*TA#@O&`KtTb-H5F)|D_iX$wB$&@}J~acjwhqSBO3PEX}(aZ4U2TUyBi}Oz4l8 z?>m(@6d{U6%y-r5t`J3=RHA6ad{?dR3Lky3igV4w9=!C_$rS6mP>fC%EQMI!-tNik z>nc_;ui1HsAX+*E_||rhu~#lBCjf<|knqbw7E7^;Z?z1w3~MVUd~4mXG@>&HzIDhj z0+kLK2DE6Me;5Z&wh6;1a$_jXZ{gnwRB9pGr(lK!x6x?=l`1#Uk2t6_*rSIM7Edw! zTYFGWD_mznH8mz;iv~-P)n8-9hrj9uD{!S=t+fW1>W+e&=cYEZ$)uo18JtQ3*-`fD z>m(0511^_pTim~Xots-1O1uC8KtxQ**w!gfASMS#cWFLh0vd*^Q-DB zoCE) zuFT9R-1g=h@+a4Adt>K)_wC$y-{i^CQGhzH-!*kunr7UA+xn1ceDjGXRYx=0A*=6^BJ#NoqKnQKZ?kllDALalFL@rzv|p&`-sJst#5yCux~<& z{gca9*{_n94U&6D*IMBsHs(9l|N9d43k{KF{O_a7c&fj=&i$Hac-!trwT5+kFY$c2 z2aaHyK;EwZ*XJ#2n@G+Z=YRJJ)6~3`vtg7s$~RVe&+vB3^Nm%&2jZH@xM4_lAnK7L z;Z3+ecOKRB@OGP#onaxbN2I6&+x+jf$I+6eb$viJA$%e>OVDND8uu;bGH)&A1W)@- zoF{0vn1*CeP*=7q?6s-jmq_LfV}ss^LmPFw=14{x6?u!oXd|9SJw>7k=W%bGZ)vxY zXkzmig8+%kCHImE=kiz_g2{*RCK0n9huaFLSyY;kPm+h#e6pOle0IBj>n@)$KCq}w zjhl}-YCa)_5*qBEL0a7nVfjUW&0?W6F-?ppbK0`f0H&eX`VX9;wav1iJ=Q zwZMTOK5knvD?EPuL@*y=9s)Thf_e%r&y=6dbp;A$1ak^0xtu~ImlJbd;WVn0L=mn^ zwV5QF+Ce>npxJO#0`Gwys~TeYgcm;)i+;zx%qi-);R1iI zs4tp4ZlWjcFHG`wu9DxE-z&INoBcFP7su_~`->L~?nMNOt&dOM3G`7J;`U#Txc!DO zn@@y-2oWVz>!?CO1o~ON&P<6|-#O*)*G0u+;i!15T?LtF-&Dimb@TKHmr(TS3jc;q z;=q;`1}fpcN& zKzkK~tz%2$&~=)p!tgrE!+V-vvOBc*p_Kn@O#C?eBQg8E2S&g9UPRy9uh}EobS54P z?+?mqI8xFmJA+bEqgv7^JA=Ye@E;dBPoW;`E^;25mgs`3*^}&pi+8hFt`^`u{L>TLOY}`Dc{(R! zd3PZwrE`X$)SuInUGuB33wwBvwg#z28pN4K{`HzaVj&^T=75OePoX2>noYd7)m^{W z+$%jNc!f)qnJS`VO<=JXR60i!D7C^bR?k5v`W%F%uzy*P6kR3qJu96G*s`=*)p9_Z-6qxR$D0X zNT0QKnlV;iaM#UjQu9xHyXBV)bUmEzdjGKU+CA%S{RTETd@s*#S~XQ{zjGPWYVLhV z{%L5z$c<~}u#9&**Do2}e4)6!9>N{0OYD!Dwc-`u!HC(YVyDwFVrtfYwM)EWkCMJY z_|DTe2%jTSO8*+^Y1XX$IYa?9s^qt|7XxbsbMcvg=Z;Ce?S5>j* zWlQI;tIx7a;xbL&6^9SpmODFjVEHWMVujSs8nnlvxrnrysMY%KBi{5=#Jc*kJQo@e z=0tLlU{xD^gRbmXTy(gdO_z<<&p zCca8-;t5t5tyVTEdK~+nQP{{UM`>%Mc5xmO%3F7+V|}!=4jL+sWKFTx z@i%{_`_ekNZANfuVoU4D^{2;|){&m3Ayu(b+N35^ocIQ7q6LgCY;1dq6c$YjWtCqP zXY!Wi5w<1FYu~V}{uI30f+pU?(8BOP=B@riFkQ9*j4hSPsx{S zEbNE!#Qk42X*y!^?NXVv82pQr2pv%$NC&qqnF9_k^C$Q~BDPgJ<=bG)ftOE^5kFOn5zGx}Iw2m@|%c_=_U4b%t*zNLyJ3BADO`gvV-X?BC$&1*J4a#isE-zKssI zJ&vjo&On`(A-skoiXC`iLa&HET^RvV)Da;1bRHSys9kV(uhnyDKb7QGW}3hrk@-cpNGde$kn%)7YX%6tv`6a7Yu~*}th?z0M93ukg?ugeQgV$Pjep z2J>==^ymlpY|Yx%Y6+M?QO-ym#7;cLFq%4+3WC60Xc)s>~b@HMxm9Q6q& zyz)wSWw~bvHh1`%r>Lx4xmJcF&*1X1B2W0*kfGxc%LTt1H*v_saYF}T8xp=Yw6STN za;*tRL+i&iH4Y75>pyT5-VJ^?s;Pg|sDVSUfpbhL!@wcKM=95a<7nXEQNxD}#I<;C zD(+yb*xX7AC050XV*?q*U5#5{#a|hB)v;q&UG?OXSB+n{Zv2FG>n3cIidUZfjd&pO zy!b}q8Oe6+N;(J2aQY?Fre1Q%)M=M|`MCDSUk=GlzR>we@;Qtn?ZO-EZ=AiknR;?O zcXL#Ap(swIB*h6IN>uLV7}93eF2H|?Sl<-pz%VN_`&$AV^Eq-55h$WLP==E*z#~C< zqVjW54pd?;#*YQE8S@c0%)Hf}z_^7`@c)o$>%Z(R|6%McXs<%oYnmp^E0yeaW$ug2 zynQ2ci8AwIR^>8BRb(zvXje7k5znNz5IPeCXyrsOqd&gp35U)EeGnyDLp={d&ao)FW$+KeFe5BCt^;>zwEP^+vmkhn>QV+k+p9&67iYrB* z88Z;6%4()dbfJ*{iY`Qc*WE?E-t({fL&qoX zlL3$?V2>})=HZ(`{FO=d+ z&8Bf1kp?&_moQZjHwd6hY!CvTi_(fXv_ag*VFn6*6nI^K$ZC_cV_LRvAJejPQbU^| zkJcjXS_$5))?w2JxlYVb)*D*ZX3libHLK04v&CO)%dt_6pd+c-DSlUWjv>c-^3@z< zN8wy23(}Id6hm&A@;wF1h9zyT-0=4-bX|~On8V*w;f21WEjuf>3`UbIaOyd-M+4R| zdS0MlK|wIvwY`6#YAFFLJI4vzD-qKj?n6pQ*Gh(Xx0UjbM~_*%?&A;Ft{Xi@{-U;4 z{z6M3j)G~ z>8$}LXw;cAy#}o7@QV=OXQFvjyA8eY0{&(p=Blfx=!j;fRg?f|GcB)75O%`4KReg` zS>Z+;%GT$DAN zWXUYAd}RCfhiiLe&1Cyc+Z&|8C#NiZx#`8PG_w5c_|waWoSe+g-)utQ@k!-l_AoZ% z?iV$hpWi)Be*3EPmB7T%cdMOt_!bHs`Ru(D0;&A71 zLHCJZ6Q|lNMhD89gr=gvy|o1AqUtF0iW6C8Tc3IT9ZgenhuZUc_83%?SMa?t04U0DytC)nlhggn zZy7gk+0E>V57~+W?~`xWmDc;_*A1B_{}i8{a><7mtv$$EIxoCX4pNDWVbw0xuELR4 zXi-WWiTw^#tg4mgvEN~$RNuHhQexF$R9D}w)i4Sj@%nMlih@9)og73&^qs*73Lnxk zjaYH2;uHRQqh$%@kWjPNbdSaAXcI(1s9 zd*-q0(d7#19blS6S7Udwk7T!IPWg!Re!;ut6>P0MSTt?FyU{sfgZ#8KLTZ<9O!Mv9 z(3#t5T6jfIhg^gAw3a=p86b|sx_^Z7o+9J_x3Mv<4@x45GE~XX@2r(hp6xG=+l%!7 z9gNrg$DKCas%fA9UaDCFEedl9H|u^a{SID| zg+PR?NXSi}gxsja+$L5T$&CWMl^hs%dt0D%!rKn{j2?*}l)5DcO|2|cWXi(mV?7lr z1)~a`qV7mx!ZGmBUm{tC1y&h;l&Bme9qF_*@aXhB=m~Ru@W4gE^T*sj`_`ZQ_{JrN zs+Qh$*$@7-YJ1Cy2b$_{Ub^m}=C_;XPq}1J&+BK*UoX~${m~bK@OrT5nx3Z&T~WmgQ?{jQEq>N6T~`p_g!NLZ63+vpW7LeI9BbKH-!{3hv|+ zj=dB#*x4Stlv2uPm{aUXkuTS2w(3w^-uREV6_|BgaP^V297JSU2(tybgnnq-qMRdN zVI|TkuM&q|=fq?LxV29N{93IX!q?I$Y`57kTcNhZKh>5L_BFCJQ*~i>QG>~|dT+zb zii*-wt7tKwr)%t&>&aX+VdQ-IsnWq!MT3lH7P$6D?DWm-A+~?YplZx;DMJh<`73#! z{BGygA6|Ij6pYI3pzyx#25BBd-YQ{MLakIM)JoW1Sre%h9?{KjD#Qf5dJkaJ< zaMC=`=7Bnog?#25ZX=-g&)A7W9jrS!z4pn?jkot6bVIM= zK7ob#d&b^;@}HP_Lr=ZGqHdD1D2iu=0m(e2b6!l?L& z(i0-{QFK&P<|EY^%EY3>A~&D}Q$P_UOsMz6T}i7mr5g0c6e8hPCIP?2j>ZpSWs642 z&}HZ(igOp4AXtZ8&?DdpO&Py9*(U-#p%yC9QYQWb#XwYGsB`wvCn8ExT zYEsMK>}|&efe2k+qVCW3vmx@2SjU;pITC!h@0Fis{pG1=#C>N!m47^ot>4Nrwz5_7 z_qNJsw#qx{&EF*mx|=jFLxN2a)+%ovB09Bu1B;GJDO`_j45aY>5FM5j%z-|o;#4~m zBuxs;s}(2PMXin-g6itp3UpdRpzGi$h9%n!)*0-rku^!pr&dI!X0NfNo$Zk-ntmGOgOw zOe{6hnSlBNm5fjayMUyDfO87VDva_NV~k+u)@uYUtO2KJL#72}`Uv!3R%R8fs9>sN zs?{onnSlO@wv}v;ynLm+oc&C`hs|QMw7Wa|ch-r|imjcub>1p2!tD2+u9e-P?}ynn z5vvfLU6m10R7b=pf`@0fd*6B$+oJFHBA!vMn<(C=U;J;z@aQJSVtBxzCSI@m8Or&c zBb9VN15yd73f)9$kbVuG@PC1-&`k_GRAHX`|A>kaX3ABf4fW&-OQ5Q7VPH|4nZhfq z3z+y=6SbLfq}sQoJ}mw_)Q9ZaZ_a&)Jnz?jF52{~|BKF2++)ElT<&%?A8g-wiJhq1?VCgq!*Z30fUN+6>0090VNd zA^eY0qKL-6XG9iIH1-}=GJswC0 zDmz$Oe3WGgE82WMTx1nUi3E|bCJ`L$mBbL0>F{Cra8y<~>`Gt)P-;nNg{-x?dC#7m zJI9Z#?jL%HLgI^$J}TXMc9C@J&4)K#DBUd$8*=5%XBSbReC?B`Xs)H&-K5s;PZH|3 z_#Aaxa_+jN+T-;bO0~Lc>f@@oLqJ7RC6m+|%+06p1rjoQze%U~1iT}30|F*xR?)z_Re>BDl@NCl5 zn_j4zV;S_RkZQmQK?oLJJ~zBS`=`!Z3<4x(^e}!uUBK`3%jKbh!H_RMB0qv(QA#oY z*cIaK%2%-tLqVPJsQ4>kw`QGChpj=MCrr>z6PnppVYB$4umjr&3FkBq3lnf}8@@O5 zeO=d^xPL6R7qHb}8-c9~TLZRn*ytMVrz!UfZAfE2rNj5@gms2|)*H{t!*+}IS)m!* zeK-zi-Vp-2I$=J(AH;7T(+EN#&L2Qp2Xqe#dvX2%u2Vkgz6to=j^h^mE)U0dNUCit-B;d}+sxk5yqv9FV!>UtMx2`Eb*_G7SZ!ZQYAKMLC<+(YLlVY`MsD_qB( z?Rp#6u@|nx^RJ=v*y!F(v`0DzalJwP-3}aEk&is=Gu5pghMs9+hVT^aQT|JDFFpS+ z`2AvRjo7IEb#xE*({XK*Ae{Y^AjlK7?{&Q^{!OUF`DdyAa1Bsse(eaq_MngEapwj-F-HJb+7JCwcrin@PL-FK?j>u8TQXT@(mMIAKbdmwhhJ9r=Yc?|ncv3KDf0ULjx z^e$sJDv$bp@5VjRO=&aKP7p0geh%dvgS;J5_VO$0{$6$8sa~H=d$e!dD}RdjLhqa2 zab!b&exl}&_B?;MPVYJVzUer+QCoh8zqcJYAKmD^D{U$h&pN5pjeHX4=-DUK?| zJ?e43df!<(#(lTbca-xF*j`839oSN_J%-;Lz%#GI^#j!AWA|tm&*Qs*K0*1TK6y>z zp57VGk3!x*q+{%VN8iyd#-Lx0!u{gw)e2@$F^8}usC`Q4kMA!1XX2l*KZtD~w!3K4+$3CvbSa;dU&{9YY{)O~-;{Th ziRXQia?J1f5@o0HD6%0lu>Rw76L4fdkcMkkXysRR?GAuDX zW*lxjoAN?x$aII;|6}Z>GO)yT*P;#(GDtOLQ^e88~*#|Bmn8a23h@C`$@4tcTuhxMQNw+t;BI;-KrhF=b|4jVe`r;QsLpJ+TY ze8BLBMpTb@dt~~^U84#|O&t{)tsVXBn4&TB$Fw(H-*iXQpT}M_cISEh$EA-e9Cv8k z)8jrK?;byL{Oa+yjz2xVb3(<0857=^sGs*edS|j}a?Rw~lebNo zHRakVKb-RFR5rDA>XfMmrfr^f_q3l)`|J7c^GBZl^XXSlziawWr++!4aK_jf8)gJ% zyw;rBT+@8C`JI^uX1+V~FSA^;8fUGTwQttjvyHR+&c0ywHFE~eX_<5VoWpb8nroQb zckY3CtLNQ1@5l2#n(vrDWPxWv(}INyb}x8v!OIIeTS{6cv}|s9q~-CJPg*)#OIpXb zZfd==^yVShYwY2BbNlPzYdhgQbmzkHj zmd#kUXxREB3DV(TYE;_}fa&%HAud zt-O5Yp_L!6%33vR)z;PLueo&1eQRD=^TwKw)@H07ymsN*-D_`O`_S6cYdhBsUe~m4 z$GThB{dnCk)_t+wyMFBYjqC4R|I_uau77`nbwk~T`5Sg@Xy5S0M%~6IHh#5fz@}xJ z9@+HPg%@A=^k&EA3pT%ek#tejMH??VdeIjbufF*GOPVh^d}-CCt1mrq`T3XMa`|gp zGPnHcO5K&quNr*SjH_O~dg9f$Tw}Rr`85Zw`T4c>Yj50|yLJDz>}@Ny{rEc9b?05T z`?}-Xb=xOw|G|ziJFeUD+D^~TxjXmneD?Z{yY#yb?;c5#5xW9h4SGbD(qL6YqfQgX zRYBZ{6#&Iv?kDbw$WP=+uEhNr6u(#Q(cm8PZl!-(j9oXY$2y#E zL+VUJPcR9OtH&C_COpfJwK#9(IBllUA~QDO1Abn|@Bd0Y#(8+PQ#yLOpJl7ZbiOA) zhV?_($jG;pY2X}#^MK&+V^K(BKT(e*;Y#+RdW_$SlhtD_#+6p}Scmg>@M9pl1(T?x z16u^nALHj!aXw9aK|QAPZ}4LizyA~Un9eKtPowjanV*N_i(r$K{97p?#~D1GRq8RFznC8*KCWPuu2+xg{O$bM$?yN6dQ9gZ=f~MNpC&!8 z9@F{X@Z&sw{~y(3I{yVf&c}I!CRIJgd5xVP_on+b#p*GgucTuGf39YjdQ9iX)3J%y zr)HjdOy^hdV_t8X%hh8#zhl^{)tlEYS+sb4L5~GJ3o7gC`u47?Eg03hX?g4V^}Q#| zTd-{2+LrQyq05&S@Y~iEtZiM_x^`o0OZlkQRzkUt@legb!@|& zwz|f;;e}<5NBT}0II6KuXD9_KX^ubrmX^J(hg$AyeYE9p>&cd9S`F*`+EFc^wTSEe zdS~k`E$yw+hQQF-ev7_UzfiwOe@GwF->AP`zb;^$J?AKN%q_zk9>e&wL|8d|J3fyJ zi-$i3jqGyf;@=;~B;O+R!flNW!9F!*)5~0*83YB#37%>Fp{0*5J-Jj`=Pw@FI&$I2 zMI#T542@i{uyx_Wg^Ly*S{PdRz{2)L3${M6wf+7F?r*mtU;CEtJGk#AX~P^Sf`>0D zoL{hg!S?M7pa*8I?SYXSmoyeGX+*@t#exmt^_K9zEBN1)_#1?0;^cq2Ba;x1oz2dfYH_BMc9h2si1vVQoY?EPA zFR?GP@4$SU5wtus#RK>|VA{<_?YL{NxyC%j-s>6nokx)LhV8g@CQIwTIsdo5+g6*897g}&Zgyj? zPP>tGOZUI0EI%e~#LINslx*Pt)E_fx4ya#cTMOy`|*Ds)puCKvA&oeM)%szZr9iQAN%juf5c~|>#wQnndy24x}JfqXQ1mD=z0dao`J4spz9gv zdIq|lfv#tu>lx^J2D+Ysu4ka@8Tjw^40QcDYS%OW-|d<2`t^1_^IgwC*E7)d4E&$Z zK)nxiy%YSOUze_DtBEW{jbq~hv%s4Ip}&0{{EbIJq!Oj&%%Gj?`7@yuh{eV=d(-y zIiAn|{kiCRM*i2|x2~_p|N8al`un=;>(TWLbUg!I&p_8R(De*-Jp*0OK-V+y&+-g( zz03c;rF%cukl~=->d%qtp8oRexLs}f1kU4U%P%^ zyPkorXQ1mD=z0dao`J4spz9gvdItU@pMn4C|NQV@z3*>dEB+^z{Jc?q`fD;P@H0uf zt?)m8Zs;xFF>~qrv2NO zq3^_L_}Ucpb66X`W<`DV8or)IebpPjrbT__8-6E>`Wa6(sJF9dyi8?!e zcJk6WBymWDix&(d+^3o+G>QeZn$V-=&sLSA&Auk;uQJ2LpOI|uq z_<6|aauRiU{PN6{t{_oY#IHzRx{^d)8NV`l=_(R+5PlGO>8cWSHT-JirK?NS!T7=C zrE3U3EgM}^qOOHsi<#23g&#tVt|L*`#jndu>3R}%2!058>G~3N1N;W$r5j4rq4=TX zr5j1qVfbO>r5j7s;rQX?rJG395%>}0rJG9Bk@%71rJG6A&GDO)mu?|Zx5RHrUb>Y; z9fco7Ub?kJ-3Gr6dFi$ibvyiaVO?Hu}yW)2x zFWpU|?vCdtpF@*9B7`~l>p z2TIg~@CT8X9xPE0!5>0idZEROf2z>qY_R=FI>QVTk$V-owsK?-sAul~v zq8^7oj=c1EiFyM51oF}oCF)7|lgLX?mZ+!TPa!WoRid7TKaITfbcuQf{tWWcGbQR- z__N4M&z7j?;Ljm1Jy)Whhd+Q(rw$V;!5sMp}HAuqjFqF#r;j=c1GiFyP62J+GyCF)K1o5)LV zmZ-PjZy_(eRifU8zm2@~c8Pii{toieJ0ctOCOe~kKi96FMU*^K8Amcy!3I2`UL(7^3o?I>Qnfq$V;D=sL$Y^ zAuoMaqCSUzj=c1FiTVQm1@h7tCF)D~m&i+BmZ-1bUm-7jRieIze~rBKb&2{0{tfcd zHzn#@__xSQ-(hnu-NBED(OFx#VpWr_sFa1=a zeun>yy!3O4`UUR0%$$V&q`i8n?#)*KRbEp91?X-{G8;ab4k>>@pF@x&LdIh#m`G# z+Fzp1ho6tUbbg7t0Db}T(gh{zLimNqOBa@?i{KX_FI`lkE{0!>ymWDix&(d+^3o+G z>QeZn$V-=&sLSA&Auk;uQJ2LpOI|uqqArJDj=XeviMj%Q1@h7rCF)A}mB>q1mZ+=X zS0OJQBvDtzuS#CJnnYb4zdCv8V2Qd0ehu={H6`j=__fGO*OsX3;MXB9U00&6hhLAp zbcjS+9(0f-5k&RKQ!I8kf>YYdH;u|+g1{F6rT5gXu54JQMbYK{tr#JZ6)e< zc;5e^>9)N@-2u=0KQ!HTl&Cx5cOoy{S)%TO--Wz%SBbhCemC;c-6iTC_&vx=_mrr6 z;rAjh-CLrL#*Zd1-AAJCi{F>LbU%r@KYoAm(gP&wf%pT-OAnH$2jdSWFFiz}9*RGd zy!0@MdN}@Y^3o$D>XG;($xDxts7K?ECNDikq8^Jsmb~;hiF!Q#c=FN{BLjT{&e!vGbHMn_%q2%&yuKT4}OD~eB7vnD`FTF&fUW&hzy!0}OdO7}b^3p3L>XrB_$xE-2s8{2! zCNI54qF#%?mb~;jiF!T$dh*g6Bf7Hi>#W{&w=x zJ0$9z_&doXY~<$xEM-s88dcCNF(PqCSg%mb~;iiTXVLdGgX1BX-O0$xFYI zs9)p1CNKR)qJE42mb~;kiTXYMd-BpBB=@|B|R<@MFl)u_95s;k)ri{-t9{)E0aTd1W|X@^AZ#CMXH_L8W*@x94Q$CjuKz9BCiN1~34AD6szJc&9!eth!M2_))- z_zB5NCz7ZW<0mFBokXHeil3CcbTWzB2j7RhbaIK>7vGn>bP9>u58scxbV`Xj6@Du6 z(y1ltH27)AOQ)5n)8VHhFP&bZ&VZkRymUs1Ium{-^3s_l>MZzK$V+FHsI%c`BQKp@ zqRxSzgS>Q3i8>d4F7nd3CF(r*dB{uWm8kvk{mDz`lc@9K=O-^+K%y>)Uy!_XA&I&$ zeqr*`MI`E?_(jP}7n7)q;}<6{T|%NRiC>bubSa6tG=6FF(q$y-0Q>;*(q$#;K>R@R z(&Z%T^7!S+OIMJnE8R|j}^3pXV z>YDg9$xGLgsB7cbCNEt_qOOZym%Ma6i8=&7guHZpiMj!P1M<=hCF)T8Q1a4^BYYweBBFWpC??u*}-ymUW_x<7t@^3nq&>VfzJ$x9ECs0ZT@ zCNDiiq8^Grl)UsXiF!ExaPrb4B9Pia(XS^fZZjI{tL>(laFLnfNowOV5(1XXDQ%FFi-1 zo{K-1y!1SYdOrSq^3n?=>V^0V$xAPis2AfeCNI52qF#!>l)UsZiF!Hya`MtEBi-iyDNy!1YadO!Yt^3n$+>VxWBCb$xA4lBi$fza}sJMxuU;|CYS;JBj)|{(JJ$A0+CJ_#eqjf0C#_<9{YE{Y9ew zivN|o^f!t6JN|d_(my2XpZGt?OaGFnWAJ0h(UwTmZuo9|n@{Oj614@-05mPF615HA zMqb)oqV~Y|ATRAHQQPtDQwlt$V;b|sMFx5AupX)qE3gOj=Xewi8=#*2J+GwCF)H0naE3LmZ-DfXCW`0 zRie&@pN+h9c8NL%eh%`|IVI{`__@eS=a#7R;O8MPomZmv$M+{Molm09kDs5sbODLF zAbvse(uE}I!uW;BOBa!-i{ck0FI`NcE{eBe7$xD}!r~~i= z$V-=%r~~l>$xD}$sLSJ*Cof$=qOOQvk-T&ziMld=W%ANhB|o*Ca1pOQNogUz@yi9f`UweqHj?^(5*L{1Ece^(E>C_zlQQHTdYm$V+#ZsC(e|ATQlhqV9#?i@bDii8>lTn!I!$iMlU-U-HuZB~#F; z%v5%UgwIXOnG&hGN%-8foG&rE0Dl29m0c*|bJKE> z#Oz}H#mrQ8iGV{PoOKc7ue^P0NiEvzzcYF;m&i5hP$Ol1#A_}sKS zEHQfo{|GacJu2aI)AE?a>~Z|#%vAP-gwIXOlM=J1@J}&Q+0zm}H!aUd%$~(R%S>g@ zN%-8fJTEbO0sjIsmAxq8bJOyY#O!7K%gj{viiFQi%c~Nz*YK|~Q`zehJ~u6INX*{E zzsXExZ%O#vw7e}bdk6mxGnKt7;d9gSp2X~Z{QJyQ_JM@YP0NQ8vybo}F;m&c5DdBU|@|DEwYy8*DRQ8R8&rQp>60`5{-!W6! z_YyugEk8)ie#HOCOl3bw_}sMoEHV29{|hsf{VL&e)AF0d?05X{%vAP=gwIXOpAxgb z@O=M|*%wcuOum9kJaJ8S#AokJ(K4nedg( zEa7w0I*Y_?R{X5YR5qK0&rR#>60Ol9*(_}sM4D>3Vj z@6SwS^GW#Jw9YRvTL8ZRGnFkU;d9fvki=|Z{KCvswupq!P3xlK_a?P2hUfc#JYO8Y zIKIx8ka)f%p7(#wn%1Qxo-d7Gn!L`Jk$64;KY+Z>mz8)v5I>N-&X<#TzC3<;@;YBZ z;`xer-v2pkT33>IzA~Qof6kiLRV1Dd!Ve;^^Hn9DuZHLSpR=ZQb&2PL@x1?Y*0io6 z@qA7En&fr9mc;Y5@x1?Y*0io8@qAr8@Bf@Nt?NlVAA%o3UgzseJl_D%`#)z*>xL4~ zhvJ8l*ZD>g&xhf8|L3e}-B{xJaQtxcI^RU%`3U?7@;cvC;`vDYNb)-0Oyc?G_|3`d zd<%)^TjF{D=d5YnO5*t_Jn#RUHLY7qJl_Vt4SAh!EAf0gJn#RUHLcrAJl_G&`#)z* z>y8r7cf#*PUgtYYJl_S+`#)z*>#h>dcf<4k&so#DyTtQ7@OzNg`JNKb_rml3&so#D zx5V?&_|fEbzK_K7eet~ibJn!(C-HoL{Ql&1et^XD1Mvrv*ZDyb&kx2QOkU@QNIX9j ze<*pKA13kqaQxxqb$*1z^CR&`lGpiB63>suA5C88$4ERs7Jn>xogXLh{CNEFEv~OhQ#wT@n@3P`B@Ur&&Hok zUgzgXJU%WXHDyE63=hP-%eiVcSt_$KOw0=MP9ce-Qs5d7VEb@%&-@!{l}Th{W?p@sE<%`C}5#AICpV zUgu9pJbx1ZBzc`bCGq@e{L|!h{*1))XYtRH*ZFf2&!5LXPhRIQNIZWL{~~#vza;Ve zW&F$Jb^eOP^H=e&lGpib63<`9zfNA~Z%90U6aOZ8oxdgV{B8W(Q~ zdlJvz$G=Zr=O0Ks{}BHnd7XbG@%&@_$K-YXiNy0y@t=~{`DYT(KgWMgUguv(JpU5^ zC3&5HCGq@g{MY1l{*A=*Z}H!f*ZFr6&%eiiPhRIgNId@$|08*w|0MDJXZ+9Pb^eRQ z^I!45lGpif63>6f|4v@#e@Hz46aOc9o&P2Ad<=dJIp=MW)OlMs31>~)SQ5`$@GXhw zt$6+eg6C~`1~_Zlx=T1~+ImPhYub8BJa5Oh^WQu0>uT$eINOQuq^D+kNtkQedP|%g z8_)OuGuN~=5@*N3^ZozMj*A}`U$f&$oE;xOK6%YfAYra)n^5BHM0mdcpSh-OVu`bp z;CcUdc2fML_?n$e;%py$AM%==T*6$_)>q=}6nNhMnQPkmNt~S$KP7q1P9@@gkn5o%mCC*NVpN_m{rh46g;zq1SD7sl7@A`)j8#V<-;vx`ZXYuXl*`+1SHEqjCoE?B4z)a09D{*!pejs_xE+=8GX{r}Fc zjb9sIv+GElT^GMDdCjgTVXkQ#B5`(oJm3G%T+_CJ#Muq;8}L4Q$ZK|U33E-`77}N-#Ba$=&2A-e zb`*XTdChJuVXkT0M&j(Y_-&b~+3h6GZjaxdyk>WhFxRy0C~d#GgoBvnNTIYuZkhIC~2I z6lQAnREe{v;ZGy4+0!M=HEm}|oIMkNCNnjAmc-e!@n@6Q>^TzVnznN#&Yp)qkC~c1 zU*hZq_zTEu_Cg7BP1{8hXD`NI%uLN*B60Ro{H5eIdzpl}rtNZxvsd7+V5VlTlsJ18 z{wngCy;{Ot({_!-*=zCFGE=kHNu0eNe?584-XLMFX}eM4>`i$7{GYj|?PiIyx8QFf zui0BA%r$MdNu0eMe>*cZdxyl?JMnjt*X&&q=9;#eqlDgMDlDgNuo22e_A4|fW zrhAK|?sadKc&`oLmUyo_p3r+e@ICN#ucyR&?Reh*xzlv-kZ`Bz-YMZu)4i9(d%f|! znXh|eOT5?M8}hn0j>LQ8;(7n)PSbrniTB3G^Zw7BruzgE?@frGki70qB=O$F_=(Bu z-Xs$5O^WCJpF2(W$t2$EgXjI9J5Be=CEn|c?@M0yrjU5AAHE-X-J4S4y{Yh1k=MPc zCEl9`KMi@^n^xkz>G0E$*S+Z_-kSkG19{z>QR2Os@H3Iuy_qH6n+4DNKX;n$vr4=- z8=m)n?lj$Jmw0au{2b(UZ%&E#=EBcKUiapfcyAs&@BiFsy3Z@|UVnUl^13&l#C!AO z=O?dw3rM`TAbvsey0?(Tdkf=v|L0EAeG!TG7R4`0UiTK0cyDq1;^cL235oZX#4kx+ z_m+})Z)yC}WL&@vjMiTE0!w(~`dmBr< zHyl5lyzXrx@!kmh2=cnOsl7d#6dfcRK!b^1641#CvDr&m^yVXGy$wHvVk#x_6Gmd*|ZMC9iwuNxXMH{(SPf zcY(xv7ve7@uX`6symvAFV)D9oiNt%C;x8qydzVSPcRBuY^1640#CuobuOzQ~S4q5g zHU4Vyx_6Dld)MNxC9iweNxXMG{(ADdcZ0-xH{x$3uX{I1ymvGHX7ajsi^O}k;%_Cd zd$&ovcRT)e^1642#Cvz*?^1Ang#CuQTpCqq)Pf5J@H2!Jw zy7!F4d(YyZC9iwWNxb(w{(17c_kzTGFXCS$uX`^^y!SHxW%9cBio|=b;$J1Nd#_2n z_d5P{^1Ani#Cvbz-z2YlZ%MrOHvVn$y7!L6d+*}kC9iw$Nxb(y{(bVg_kqNFAL2hG zuX`U!y!SEwWAeK9iNt%K;y)#?d!I?X_c{J^^1Anh#Cu=jza+1FUrD_8HU4Yzy7!I5 zd*9-}C9iwmNxb(x{(JJe_k+ZHKjMERuX{g9y!SKyXY#uDi^O}s;(sNtd%sD%_dEV~ z^1Anj#Cw0@|0J(_e@VPI20wJ0IUiZe6aHr`pzJxnXj|n8+n-D)C^L1|`iT5VPPfT9-CXsk=Qv9Ukb#F3>_xj-b zkk`G*CEn|c?@M0yrjU5AAHE-X-J4S4y{Yh1k=MPcCEl9`KMi@^n^xkz>G0E$*S+Z_ z-kSkG19{z>QR2Os@H3Iuy_qH6n*~1$dEJ{;;=S4Mvys=m*(Kha13w3O-J4V5y}9sn zk=MPsCEl9{KM#4`n^)q!{`mgnb#Fe2_vXjXPhR&Hka%xF{DS0lZy|~I7RE13UiTJ} zcyCesqU3dNF^TsU$1hG^_m+@&Z%O=;x-Z!L-U*2b?*Uia3KcyC?&y5x0lJ&E^*;D?acz4ayD+W@}- zdEMJk;=Q5xq2zUMBZ>Eh;fImey^ST_8;&1NUiUVUcy9!LMAEIEt4+o4_3AMaKa%JRd*SyYufDw{ z_Kn7mCa=DIB=+r#-N`MU-+}l8$*b=miG2s-4<@g^LnQVcia(UR z`VN!WcR2oV^6EQ6V&9SYBgw1pD2aVXbpQ<--Y-K$*b=oiG3I2FD9?POCU%(9--GxE$*b=niG2^_A11H9M6`W}^6Gm+V&9Ya zC&{btDT#egia-q--q}Q$*b=piG3gAKPIofPbBtzivN_n`aYA`_c{J^^6L9SV&9keFUhO#D~Wwy z?WbB={c69`g*oV=xTblN~*7C zn}n{WXLpHxJ@7pe`+DLD?Q6%k>C$9F7wqlp2WWK@#B+M-vko-Cd5xjUVRfu?3)-rF?sb(BC&5${G{a7H<`q~KKMT5 z)i=4szP|Xr1S9nea1_SKrJM`)0w{}eaIC=FgA+c{s{F3C=x0J-brSVIXSKl%c`v%|#kXPTb68i??2a;FcauWNN z$1hJ_eJe=pTM@q^dG)O%v2SJk%H-9zip0J__(9~=x2nXx)$ps4SKsOq`v&6&lULsw z68qM~uSs5gYf0=|8^1Pr^{peZZ(aPl`G!zQn!_@Eed<--Z(VhT?~k zSKmew`-b6%kyqcw68nbZhm%*|CKCHb;75>G-=-4#M&d`3SKnq5`!>gKPF{UmNbK7Z zza@F~Z6&d96n+$W^=&P&ZyWqJtHi$D@Vk*$-|iCo_Q3B!UVVE??Ar^!7kTyVEwOJjel&UY?IW>oU;Mu0 z)wiF-zWwq0lULsX68jFsA4pz(2TANZ7=JK%^&KLy?@;`q`I~sp9dG#G5vF}*?vE^m8MGI{l# zBC+pO{Hf&Ccbde$)A6U1SKk>D`_9ClNnU+tN$fite>Qpbog=aDT>QD@)pwr6zVq?t zlULsb68kR1Ur1hk7fI~97=JN&^<5&d?^67w^wdB=zoy5ND@z;}A-whJ`Zp7b6UVS%7?7JC%GkNvhBC+pQ{H^5G zcbmk%+wr%PSKl2H`|iZwNnU+-N$k5De>Zvc-6OH@Ui`h})pwu7zWed_lULsZ68j#+ zKS*AE4@vBM82>PN^*ti7?@|1t#JvP5hhW)%}*l?zi!8lUMgU61(5Uze`@-?@8!wdcH5Q`vd$3%vAS> z61zXbe?(s0A4}~11pf(nb$=?c`!hV>|6}*(_|Ngx{e{HtFY#ZJSNB&EyT8VNOG{3H?jP_!FjL(>O6>j#{}Xw2|17cl7d+qpLvPdbSBc%f;rad_ zyMM?3j<4=NBzFIa|C7AB|B~1}20w{|F^rrH~8uvM?!DY zKCXn`rhPn#-Q(lOXTG{8kk~yTenRr}4p?n&^IFjL)=O6;BtKN)#-_mS8= zIev2T>h3GCdkQ??|8I9cd_R13Pbsl`D*ROB)jhSu?rHGTkXQG#61%6vPe)$e(@W@W z+Gmj1JtKZbW~zH8iQO~fXC|-iStNGPil3Fdx@VKvJv)AO^6H*LLT}SPr^N2L@N+R! z-E&Lqo(De?jiUgB2c)tIi-lly!iQU`d`Tl=;oAw5BwhF)xD?0?!EAPkyrQL z61zv^N0V3gJ`#GH_I)LG?}y)ynd;tOV)p^~1IVlUK#AQ4;SVCO?t>+EAA&!Gyt)sS z(A%^hCb9c){Nc=0_Yo4ikHjBIUfoAY>^>TQG^=d1 z0(o_xD6#t_{7K~1eX_*vQ}CydSNEwBdYkssBzB*UKb@KCK0{*nnfNowtNSd8-Dl&^ zCa>;uBzB*R=l$RA^YG{4tNVP3-5205Ag}HVC3atgzlglLFP7MS3H}oD>b_J$Z_|F6 z#O}-Smorn{S4ix>5`QImbzdd1`)d5v*nKXxA@b^eSYr1h_(#aA`%wwKP5WaKyC26t z&P;VbA+h^O{FCI>{glM+r}0mdSNAg#yPw5BOJ3d2N$73bpO@JE0{#VNs{2KW-7n!^ zBCqb3C3e4pe}%ldUzOPX8vZr%>V929Z`1yU#O^ooZ!%NeZ%OQa8~-+Wb-yFA`&~Tm z|MWKP?@8=_AOAjib$=kCw`u=SV)sXQ-v8-s+CP@q{R#dP^6LInV)tkG&&aF$bBWzw z;J+ZR?k^?uHtkC zcW-=e^6DO2Vt0dY$g6uC3B66nxDtAsj`1Y)HXY+j?4AHW0e7o=LW$iI;U^-m?ujLK zPlBI>yt*fq*gYAZ_kX+l;QQdKdvb~0eer$Ct9uHG-Tm6ltV zZ__c2#O`VF(=t=t(@E@}9zQ*KbQImoMfPKn)f;pZZ+?zts)&x7av-|l(w^Wv+!zr^nO@bi&Z_xuvO7r-w- zUfl~y>|O}Z`#-%+$HEeNn~p^!b}x!wl$q*YOk(%q_{GVqdkKl%OX8O#ukNKJb}x3SCr7(bgU$?du9B}%vAR( z61xZC2a#9zsuH_b!>>kO-K$IN9*iGMUfpX*=xsXIl-Ru%el2FIdu@r`>)_WRukLjv zcCUwDkG#5vNbFu8zdm_&Zy=$!>DW+W_fY&$W~zH5iQU8S!^o?9V~O3v@x#fhdlQM> zBk&{0t9w%ky-mkRiQSvwH)E!{H<#GG1%3fTaf_g46=$g6vl#O|%}Ta#D!HWGT9 zj%_7&Z-?KGnd;tNV)qXC9muPDM~U4#;ddgh?wuue?}Fcjyt;Rl(A#wECb4^W{O-(D z_Z||v_r&i>Ufp|1?A{x{H+glBme{=yejoDc-d93z)3Kk#?)~xmGgI9MNbEike;|2v zA0)B+VEn=4)qRM>?nCj1l2`X(5_+4C!zFeffj@$o>ON9p_fhzx$gBHkiQUKG`QQK2 z+jJZ&vHLhY@Belmk3Sw?-6u%wJ`sN+d3B#8vHN8F$>i02ip1_y@%;Hey-mky5_+4C z(ONCq_gVO}$gBHoiQVVm&mphwb0v14hv)s@?(^~I?u@i&uK_bn2;Z^iTH|MWH;w@K)2I&PQP zeFy#yW~%#6iQRYM?;@}6yCrttgTIHoy6=_PeIK6pf4lF;-;b~E2PAesh<}j0x*w9* z{V@Jv^6Gv>V)vu?N6D-EF$uj*$Kw*apTIxCOm#mgvHL0fQ{>hCw8ZXb@XwG}_p=ha zpTj>#Ufs`2=xsV)kl6hq{zYc0`z49pFXLY(ukKeQcE5^$mAtxNli2+_{&n)|enUcU z)A6Rn?zix7F;m@dOYD9J{|i$Gx_ow(z$*cP_iQS*$KPRv5FC=z>{sI33d3FCNvHK_dPvq78vxMHJ;}?nDzv6#orn-NV*!?^Hck=4~Lt^)z_&>?3 z`!9*zWAMEHS2wTSMd)ohyGg3Mb1VtHO=pXwx;tAX^fsMslIrg4E}^&S>>;tcC%$K5 zcRRivU)>!NyF2j=+uaM_3t!#6C3cUEADg_o8wtHl=Qt92o6d11c8`Z2kNN5zUqWxw zIf2CP3Gov$Q{59u?4B4uF?n@QBC&f?{G{a7J(Yhqs_tf~Q$*X%B3B66{v=Y0g!%xRdbx$v`dj|XrQdp?QX^W*0yukHmTb}xuukY|;@Z#ox}u%qc*SmK^V@QZM__ADx4N7K2O#664S zdH-ie)47DiJxk(w|7S zdGgw`g2X*5;#VZEJu6AvvofCdfA_3{Uj<)#21(e_bgn9K&uVzy|Jl)Wt}b!UU_9^t z>}WdIkho_}{F>yoXDtakn$EQ)?pX)V`@ehE#jlI6J?lx_GXy_`y!NawanA<$4ajTH zh7$J-#q<8}o{jJu;cL$@2|Jq3jV10GjvvlU?b$@)o)P#F(d_Uwd|t zu%qeRQR1GR@H;V6dv=z%XBYe~D)u&o;~qi#VIYQ!|Bk@O)*Pf#!>}WcVmbm8_{4vbbo?|8KXgZIRxaW90 z@Bi#*I!}AXndo{RApGgEsm zk+|nl{H5fz=Q4?VF2`R^UVE;Pu%qd`QsSPg@K-TYd#;wSqv^ax;+|{q*D_Oku9LXu zdOYv{>}Wb~khteY{Eg(b=Ozg|n$DXg?zshj3p2InRtY&?s*#jG~fdF^>Y;+_}rFOt`umn80a8UHeQ?RiDwo>%d&lGmQsB<^_~|2lc?c|+o! zH}P+h*Pgc|?s*&kHhJxNN8+A$@$Zt?p7$i~c_05idF}Z?;+_xjAClLek0kE-82>SO z?fFFFo=@?glGmQkB<}ef|2cW>`9k8JFY#ZJ*PgE=?)e)3HF@p%M&h1t@!yiyp6?{? z`5ym0dF}Z@;+`MzKa$s;pCs=28UHhR?fFIGo?r35lGmQ!B<}eg|2ui@`9tEKKkA-j3Yfq=dJ-zS@yQeq4H@^0aEpbnSZ^&!UI1=}aiyxP~_KYWCN7HM3 ziF+o%^XLEUXnIX3VMo(zB8ht@#!t*l?U_X4o=NeOlGmQeB<|^h??YaDCYP|I>D5=_ zo+Js-1 z#t$a1J!?qZvnGB`^4hbO#64@{*Cww$>qy+QE`D9|+OwX-JwxzA$ZOB~68CI?-+;XK zY$$QhQ2bEx+Ov_wJ;U(B$ZOBW688+p4=1lZn@HR<0zZPh_G~I~&q(}8^4hbR#66qi zHz%(>TS(lqC4Nit+Ow6!J)`iW$ZOBm68CI_--f*QY%6ikcKGecYtQx)_w0b*fxPzY zC~?nD_?^gW&(0F}?1JBgy!PxWanEk}-Nw^4fEj{O#Xg&c>fj z-k|45+k}C$IfiNZfxV{z~%Nf0e}jSL3fHul?6Z++1pTzz5xM|CGf2Pvf5^ul>(R-2W{8S@PQdoW%Xl{!Q}Q|CYr4Z{y!4ul?^x-2X2A zUGm!hp2Yp{o+TXjIgxyW= zu_U#>cZ-DGP48Ao?eEA79%i5WX|1={=!@)lKh-B(9$r zKQZIAei8|*o8FU3Tt69pGG=OhABpQH$Mc8(tZsVum9V<$J%z;e{qX&msr^$*+&>k5 zD)QPtwZ#3?;HM$4{nJX=-SnPL;{NIJy!^Af={}7lS@5$k zQ~PI?xPLbMY~;0nc8UAvz|TQm`{$IfyXifb#Qk&Q`NMy9H@)YPxPM+efB5hI{`mg* z+CQJf{qy7JC$IerNZh|5enIluzmUZJ3*#3iul~)FC}sR()gvxYyUD5_Yc4iAg}$)O58sXKajlkFDGGl(|dV|`&YoPz)bC5 zQNr$~_ev7?uZ&-rncBaK#QlTtgUDP@z;@2gw{p(5GKLkI7y!NjzasLMR4ajT%h7$J=#SbN~{ToTx z-Si$NVRzGeV~P8R(v_ZQHhO z+qP|2+qP}nwsFQ95pTSjF=Kbnv2MH<@!kLQY4&h)jvi^Tn1$-C0m{%#WY zcPH;oU;BGV+~1SDCw=YjC2@am^4|2dzmLTIeaZXM*ZzJI_xC67Pha~7NZdb=d?0=8 zA0%=AVDiEAwSS1j{X@xz(%1fB688@$A5LHUM@Za1l6)k6?H?s^|7h~j^tFGC#QkH* z$I{pSaT52BCm&B=`zJ`;KaqSQeeItlasOoU$@H~$*0oS{%I2TPbZ&FU;AfB z+&`0iCVlOnC2{|3^4avYe~!fcbIIq@*Zz4D_s=JvPha~NNZh}Wd?9`9UnFt=V)DiG zwSS4k{Y%N0(%1fF68A4BUrt~9S4iByl6)n7?O!Ev|7!Bp^tFGD#Qovq;q4J~-z{_J@orVY>|( zO;YmucoO%= zCy!5G`x8jqpO8EueeF*qaerd+#Pqd4iNyU$$&=F8{$vvOCnrx%U;9%?+@F#>C4KEr zC2@ah^3?RTKaIrwY01;l*Zy=8_opXMPha~pNZg;1JR^PW&m?hwX7bGRwLgo*{aMMg z(%1fM68C2(&rV(bZ$dJ^~7C$CRm`x{8y-;lf^eeG`~ zaerg-#`LwniNyU)$(z#G{$>*QHz#jSU;A4~+~1PCC4KF0C2@ai^49dVzm3HGZOPlx z*Zy`A_qQi+Pha~xNZj9%yd!<>?<8@5XY$VUwZDtR{awks(%1fO68Co}?@nL)dq~{h zle{N=?e8UVe{b^M^tHc_#QlBA`_k9`eiHZhC+|;R`v*weKahMNeeEA4asOcQ!SuC% zh{XLv$%oR{{$UdL4<{c^U;9T$+&_|hBz^54C2{{~^3n9Qe~iTaW68(T*Zy%5_m3wZ zPha~dNZdb>d?J1ApCobrWb(=MwSS7l{Zq-O(%1fJ68BFhpH5%UiF^}%?cXeM{}%Es^tFGh#QodIx6#-B?GpFzAm2e>`*%v*zl(eqeeK^Z zasM9jJ@mDIuf+ZP$oJ9L{{0g7A0R(KU;7VA+<%Dt5Pj`GEOGx4@+0)M|ER?M$H{}u8p^tJ!0#QoREuhG~3>k{|hAiqIh`)^9ze~bJUeeJ(3asM6iJM^{x zuEhQK$nVkD{`(U5KOlcVU;7_Q-2aID5q<4{EOGx6@+b7Q|Ea|N&&Z$A*Z$`c_rD;2 zL0|h{O5Fd7{1tuee=TwU8}c{wwg0We{qM-%(bxX>68C=~|3F{+KT6#HiTo3N?f)!s z{}=Kv^tJ!1#QopMztPwJ?-KX_Apb#M`+rK@|BL(=eeM4(asMCkKlHW#uf+ZT$p6u^ z-y*5~HiCrh)<%@nej7=`c55R`YQK#lVY{{eNov21Dq*{|(ImCsMwhVN+87e|$0U!5 zNbQd$aer*`*z~nOMB;uU*I!us9TN9D$({7I-z9Oso7_!b`#s`+`p|mGz4W!;C;X=( z{5e$kUy}H)6&=ebQ1TcCr?jb`!h(~pOHKxeeKUAaerp=%=EQCi^Tm|$+Obe{%jKWXD822 zU;A@N+@F&?Cw=YDC2@al^4#>bKaa%ydCBwA*ZzDG_va_iPha~BNZenLydZt;FC=k) zVe-QCwZDkO{YA-((%1fC689G;FHT?kOGw;blDs5+?Jp&9e`)g4^tHc?#QkN-%hK2W zauWBKCofN5`zuJ?Uy-~beeJI#aerm<%Jj9rip2d@$*a=W{%R8US0}GdU;ArF++UNt zCVlO%C2@ak^4j#ZzmCNHb;;|}*Zz7E_tz({Pha~RNZj9$ydiz&h)jvi^Tn1$-C0m{%#WYcPH;oU;BGV+~1SDCw=YjC2@am z^4|2dzmLTIeaZXM*ZzJI_xC67Pha~7NZdb=d?0=8A0%=AVDiEAwSS1j{X@xz(%1fB z688@$A5LHUM@Za1l6)k6?H?s^|7h~j^tFGC#QkH*$I{pSaT52BCm&B=`zJ`;KaqSQ zeeItlasOoU$@H~$*0oS{%I2TPbZ&FU;AfB+&`0iCVlOnC2{|3^4avYe~!fc zbIIq@*Zz4D_s=JvPha~NNZh}Wd?9`9UnFt=V)DiGwSS4k{Y%N0(%1fF68A4BUrt~9 zS4iByl6)n7?O!Ev|7!Bp^tFGD#Qovq;q4J~-z{-e9f_B%$Eu-!UFlhl64=n}SD#~2d#$0U!LxIY$o zEOPCSEpdMcng6iB{YGx&+V7CK-%0MIul+8G``zSj`r7Z2xZg|erLX-y3EQn>sKot# zazAfse?Y=^>ll={Ka4z#H?=>G#Qkx}mucoO%=Cy!5G`x8jqpO8EueeF*qVY_uq zEOCDlGXL@)_a`M!O0NCMB<@d6o}9k+r;xZmC3#Bv+Mi0|{?z2D>1%%)iTl%%r=_p` z=_G8oj_Db_vaG7|TfB`-@~`^!n(U!J@? zeeJIxaeqbfiuAR=lEnR$$t%;>{wfmpS0%4XU;C>`++Ur%I(_Z0A#s0A@|yIuzm~-P zwaIJK*Zw*Z_tzz_OJDozN!(wbygq&HZy<4hL-L07wZDo{4W<0<4*_-o7YREdtKkx!#9$I~VFwT?3+I-W^BlQ-pfmPE(1$!F7- z<2e%iTF1E(9nT}5$D49IU!vm$Yqzju(?J=1n0Uc$oyh z)^WK+$1BKJ@TMHEl<0UB`6~Kyyjp@^>$pau<8bnD-jw6D5*@E2Uq@e#*Guqg9XCjH zypen(Z_4o|iHBHDTO|0kj$0)<-bTKSH|2P{M8`YGchHyPof7<7$6XQ~?;b50D?=O*uX&(eWYjL-ghNumr!>@rXpnN6C-!rW_xW z==eDKar$z6LV{oGcv7O{Q{<<3Q;ttdbbN;V41GC1E5WaIJSWlddGhnTDaRKiI=)DL zk-i*XlHk`mUY6+i3i%b@l;f)s9bY5AMqiGvOYmzQZ%A}}ll&%c%JD6Uj&GCSrZ2~L zB>1(CcO^Q$M}Cht<@mlt#}CLK(3j(f68u`nM-m-BCi5@zLezcYj( z4{yrxUx|+Yk^iH|u`?3 zt#f<{eywu?iH;MJC*(~zP9)KBV)DfFWfJDay$qUk#<3bW07bY)EUyh4N@N1omN_1R|ycloFadC-`OOThKFUKV%__fZZ zBswll=KEjAWys5r%W+wWj?0mkqc6whB|5G^UV*+GSCr_u5_u*1a$H%0U+Y{&qT{OM zRe4j6t4VZRoxD1IIj$kWuXU~|!LN0$CDCzhGT;CBwa#@UI<8CR`yap7xt>JF^~vkg zm*WN!9XBLzNMDW{N$_i(8%uQDguDrF%5hVPj+>D;qc6wJB|2_F-h#dyx0L9(6?rTA za@<;?<2K}N=*w|iiH_Tmx1%q|?Ik+yK;D7A9Cwu9*E)BS=(sbP?|=MS=PnW*cO~yi zUyi#;bljc1JAFCsA<=P9@}BhNxR*r7y~%w4` zM@w`(hRpXrey#IZiH^sSkE1Wg<0bgD&J!d$o=E2VAHUXll0?Uo$tTm7<0%pyPbHs9 zUyi3qbUdAWI(<2wA<^+n@|pDIc$P%Rv&nq_pWkg;{{~C z|M6>`7fN)zhm(iEw%kf5ujyI8SqA$mrB|6?hzJ)X@1ifqyCwLw&U+*}-b?2DAHUXlpG3#|$@kNj z;{y^MA0+cH|G}?yJ|xlcVe-TD<@kt1$4AMJ(wF075*;5WKTcncPe^oplKdonIX)%9 zuXR2x(eW8F-~afv&Sxb$K1Y6zz8s&I==cKp1^RM)QKI8ZwHV1`??`lfm;5e$Ild>+@qP09^yT=0 zM8^-wAJUiOM-m-BCVxy{j-N<${FMADeK~$6(eZQg=k(?Hg#^FW`K3h1ugHA=*5?E0Ui9J@x9;MclFlayoE=o0)|*BFv=>>5*oU+Wr6QjT3? zOLQDU=KEjAM&|op#}0A_xg0wsI(Cuy3w7)!cazJpN1|gdxtG2i`y@IJB@d-9$9{>9 z1LOhvavYTCIE*}uz8uGq;McmwmEhO9#*^qcK6!lJm*WHy{94z95*;TZPsE#YoLHjc zB;-lx%W+bPj+2omqc6wFB|1()o`Sv{r?aY2cW3y~M1FUN%?Ixa$9guWaX zmFTz_c`^EOTwJ2#667W5%W+AGj!Ti3qA$m#B|0ucUWUFLmzChxx|WmZxIB4z-jw4C z5*=40uSj2xD@k-*nY=Q6Ij$nnaaHoF^yRpkM90<1tJ9a`8WJ7XB(F(dj%!JDT${W$ zeL1cp!LN0#E75U1@_M`}$Mq#TZb06Ez8p7{=(rJiBl>dOSfb-5c`N#I+**QP>)J-5FX?@C{eyGeB1oxD4JIqo6RaZmD|^yRph1i#j`w?xN%$ouf7 z9QT#zxF2~x`f}V~qT>PN1L(`~K#7hAkq@FT$Acw09zs5Zz8nvg=y(|UF#2*lT%zL< zjz^P^=1n;sBhm3#^0D;gc$`GX;Zay(n2<2mGW=*#h3iH_%y z&!aEL^Cdc7K)!&!950mUcoF#``f|KjqT?mxOX$n-Qi+b2kuRe!$IB)7wXQ29I$lY> zk~igel|;v@$yd{t<24c;hm(iXm*ce(9j_x_M_-QDOLV+}d;@(s-YC)WCh|@6<#@A1 z$6Ls^(3j(_68u`%Z4w=CC-X1=#IJSTA<^+p@}2bMc$Y-SyUBOcm*YJW9q%RIOJ9!n zNp!rQd_R3TJ|NNYLGpw2<@k_9$A`%e)0g8T68u`%qY@n-BR|HQa(rB(;}hg3=*#g* ziH=W^pQ10vrzJW*Lw<(79G{iw_#F8;`f_|;qT>tX7wF6JMTw3tkzb-O$CoAewXRnr zI=)JNl{e-1nncIf$*df z|4Ltuze#lboy_n5__eM-Bs%^{=KCMN*7cV}$G^#c)0g8v5*_~~|4Uzv|4GWRJCbtj z9zlX%>mE^3j@=_k@N3;8OUki(6bXK<`+t&h>>gEuU+W%CQjXoDOYm#mV@S%edrS#_ zt$Qpbx$V2uXRr@!LM~sA<=P4@|3(U$EhSbPEDSgz8t5K z=r}ETTKaOFPNL)VC$3@AD(wF065*-&OFHT>MOGtEFlDs5+IW8s9acT0>^yRpWM8{>x z%hH$QauWPn_wo`QS0Jyzn{r%HqT@>BmFUZHWr>cfkXNBE$5kadu0~#sz8qJV=(q-X z4f=9iQ=;QqyX!>FUNHy__gl!Bs#87=J$X6TK5JL9XBNN`#*lIdn1XC z81)NZ6!Kx zN8XM%<+!~>#~sK!(3j(m5*>FU??hjYJ4?oa0TfBaha0TLY#Bp*m$jt5C}JeYhi zeK{T?(eY67q4ec=m_*0J$%oUI;}H@ak0c*SUyesf@N3;iOLRPj%C5pF ziH?_&FQqTX%Oqp`$e))>^u2<71#imtN(rv5`zndPSCg;iP5E9U(RVm`IDPqEE7A8l z@^$p(d%Z;88^|}%m+y@deQzS)L|?u)OZ2^k%=f>(w~}uqm+x&7eQzh@_kUE??dE==*#zE39haC z5sAK!k{{(w`93Dm_i^&$^yT}61lQL6q(tAR$WQU6e4m!+`waOR`tp5NqVIF$=jhA# zd5OL+kYAuL-xnqNzC?bBzI`BmPO?`sl$Unjp#U%qchaBbahO7wk; z{1$J@_ic&3?~vc2FW+}1`o2egkG_20m+1Qe`2+g${ZOLsN92#_%lBgmuC4nMiN2qb zKjlsNekRfPbMoi(<@<#M*Vg@|MBlH-U-71Vzn19x4fz}T^8Hq#?|0{fAskFL{h#zBS^}(XG96Et!E@j`Sy$~!L{{_A}QaV|4DFd zJ)=s>w`Vj-`Sy%1!L{{_At~RUF(tURp0On5+cUNV*VZ#cqHiO&MBff_2f2JZCHi)e zyXeceTcU3dnZJp?z2shU`SwZl9ZDWbU%vekeFw;V|LZ$Q=KEjYVdP=t@*PK_@3`b~ z>C1OK39hYYe2Kmjkoo?{we?IW(RU*9MD*o5u>{xFGl@jsNy(G)rhF%p=sP)ia{BU} zLV|1SnNosl>zPWT@6_a}d0)QMNc5eSJS~0sPA9>&^-M3(cLwqdyeZ!qCHl@po{7GE zXO`gFdS;R6J1cos-jwfb5`AYU&rV;yb4c`^lRPJV`OYQLcW(0B^yNE`MBjPI^U{~^ zd=gw+&-@a77a%XdoAO;yqVGcFh3Lz7VF|9SXAz0Mi;@@RP5CY+(RXn&-~YI_o+Tvu zE=lJ5AJ^8iltka9$xG9h?=lj7mnAPtU%typaBV%yOY~iVyaI2^cSVW5E0I^CFW;3V zxVD~EB>JvOUX?fHyP8Db)yb>Vm+u-9eb*$fNngHeN%UQtyf%ILt|QTRUGlp0<-48) z*VePXMBfd_8}O!lHuB~SaiN0Hs z`TobX^=u{4cWd(2^yRyaMBi=6+tQcsb`o4$&-N01cOdV;oATXJqVG=Ro#@MVX9=#Y zXBUaSyOMY1P5JI7(RX+9?)2rmheY2!$$Qe5?_Lsp_a^U6U%vZD^xc=dFMav$C&9J# z>@U&x0P+F6Dc=Jn`W{3+h`xLemf+fY4w2}4DEUy{l<#2@eGez|{f}$wIYOfEkz~IA zacw>_j>a6yeZ!sB>LV+zLCCsZ<65JdTy5Jdkgs% z-jwgH5`AwY-$q})w@dWBgM0^l`Q9nf_b&2X^yPcEMBjVJ_t2N`y%Jno&wUbo?41M`NE5Wt(JSWljdGhnTDc=_)`o2hhk-mIilHl5UUY6+l3i%b@l<%t& zeP1KLMqj?KOZ0t%{04pbzA4f7E%ICR<@>fo-*?FG(3kJK5?ou)dlG%$C%?~|^8G-f z?}y|M>C5*c39hZ@V~M_>kU!x~`F<+V_cQWm^yT}xMBgvSU(lEDmlA!yB7a3+zF$l9 z{f7JveffSX!L{{#C(-wN^7p(c-ybCU{z(3jzI=a@;M#hAmgxHn`4`@l@2?VleY@?Z4j`?o~jf5`vPm+!w4TwBk7lJf111lQI(f~0(VN0gLr z??@6{Tkpt{^6ec(f@|yjpQLyF!C^R`HmyecU)Dc@-%`c6xpmcD$ali=EV zrC1OM39hYoeu=&dkQd-h`7S8YcOmjZ^yRy-MBhcoi_n+v zq7r=efcga!L{`+CDC_j^3uF1-(@8FE=yjPzI>OH;M#hZm*~3! zc?I5-?}`$AS0b-OU%o3#^j(F#3Vr#mD$#c}@@n+uyShZ*HOOnwm+zVqTwCv25`EVu zug#nCT}Ptty5x1~%Xd8quB~@{iM|_HefK8s&71Pw zN22e(% zOY}X0d<1>@9x1`K^&TbB_h|CbyeZ#fB>EmpK9;_GkCWiqdXJardjk0c-jwf&5`9l1 zpG052Crk7_g?tKq`JO7#_cZcp^yPcHMBg*WXV91LnG#%E?^zOk&nBPEoANzJqVKun zbLq?XJPEF?_k4-I7mzRDP5E9Z(f1m>SKPrjZv<$Hrf-y6v{(wFZ| z5`Awb-%MY=w@CE8m3%9G`Q9ec_jdB_^yPbp1lQJkr$pbo$anFkeD9X%dk^^@`trS3 zf@|x&PonSrP?_&~uA16OfU%pRB zaBaO$O7wk-{1k7>_i2f~&yb&?FW+Y+xVGNsB>Fy2ex5hw`+`K@7s)Tum+wmweP1TO zOkcjQNc4S`{3?C~zF$aiZM|Pg z^!rM-Yl*(!kiVfX-)|+jw%+d~`hHLTo;T(DgGApS$v@JU?@tnae-|qszI~D4 z+WJP2lyBdN5?ou~NRsmH8(D&D>l;Nl;H-zI|g# zaBY2KNy@ixY)Sd{4Uy>E$bA3n+d=Limv5&;-!5_&eff4v^z9+{(3fwoMBhF#e{+3@ zl82JZw_l>~0C|AEdGNCo|3+Nr;_M9HF;|K@|{Ma@3iD; z>C1OI39hYgdWpU>kZ0gc`OYZOcP8>o^yNFVMBiD+v(T6CtP)&X-)s_nXD83joARAQ zqVJsKIg?R_^XFU=yj$Pg65Zz^^Uwd_-TLO0=sq8rfBr}J`N{K>%Y6Zf?hBF^q%Ze{ zB)Tt5UYNez7m?_`D0xx(a$ih>ck5eRqWco$C3sWrOG1r|yj$Pe65ZD!^Uwd_-TKy*=)N9#J^FHAUxIh*+d!iGhU5)-Q|=o{ zbl;e~F@3pjBGG+Q@}~6VzL`Y#&B>e7m-`kHyj$Ov65Y2V^Uwe5zBPGka=C9K(S2L; zw)Ew`okaKT$=lPH`wkMkTi=cn-FG7I#G7*8S)%(c;#pH|0<$j4o z_e;r_(wF;X65TH+Urt}{S4ecfl6)n7xnCvG{c7^n^yPkyMEBw3;q>Kxtwi_h$k)-A z`}GptZy?`5U+yIJ%l#gS?)Q@Kr7!pUB)Z>EzMsC_ACTz&Aerxf-5(-9L@xJ-CAvRCeuTc< zAC>6-82K^!a(`T+`xE3R=*#^{iSAF4pQ11KrzN^SLw<(7+@F=`{v7!^`f`6>qWcTv z7wF6VMG4-m?kl_sQ?mm-`12-9IGr{jd8+&PspFpm;0v@-9ICL zMqloqOLYH&`~`ive<{)ZEAm(L<^Huq_ixDG(3ktS65YQee@9>L-%E7=f&2q~x&J7^ zyY>Af(fw!g&%7!3UnIK!O8%9;+<%kk{yX`1`f~q6qWhoZKk3W;FNyAdlmDhK_kSe1 z|4aUtzTE$llslifMeuGzN05~J&=Do&K6E4r-fif}l5!t9iUjXA^na3aA3CZ8?>2Na zNx2UlU4nNTI))AklqB@{IK5K9fZEnaMNLm-{Rd z-Df4wN?-1?N$_q%XP4+c2YC+Ol>3|#-RC0DMPKf7OLU)yJP&=j&nwY=KJt9@2NRiSApIx8_Z` zZzIutTk^K_<-VOn_wC8s)0g`W65V$s??_+nJ4tlknY=T7x$h#;eOL0X^yR*rMEBjv zyVIBZ9unR6B=1RI?t4k_ZbSE$=)MnmAKsMvz7pN{BkxCF?)yu0KY)AyeYqbf(fuIu zLGK=*#_9iSD9nQar$z9LZbVVKob$^%qF1g&_lj#0F`F;9w|3ISqhvX0G%l#vX?jMssrZ4wTB)We} z{*=DlKa=SGIr($?a{of2`C62qiSA#MzoswuZzOoPq2Ee$|Bn0}Z_539iS9p; zf1oe-A0@i~ME;4s+<%tn{tNjR`f~qOqWf>;-{{NzcZu$QkpG}B_dg}N|3&_bzTE$o z=>89x-~V<0m;5ie-2ao5JD;{i@NWGhNXor`LWyY-JEDfj;WN$_s{ zqe{xXe>4f+t$%b$x%ZDD!MpX3DJl2#co)03y?O}Wn?(S1hpjP&I`lSKEK$urZJ z`z#XOXC=={U+%L>bf29(JAJv&A<=zK@|^VLK9@xIxyf_Wm-{>t-RC9GOJDBuN$_s{ z^GkGJfV=>2%6&nJ?hBC@qA&M_CAu#{UWC5f7nSI~7`kDxF2BPF^YMLvqY+>e&%ehm2- z`f@*3qWf`V{`pw-J`>Etpc~kDENpwG* zd^&x(pCQrxO!ArZ<$jh#_p`}o)0g`>65Y=wpG#lv=Sg%wpL{-jxnCd|@fH5OP~sgI zkuTy+-EpzRJ1!w#LSJ`WD&dCKf0@KPE+_N*KR2}gDp1jFs7t#8W>B$J#AoYN!>IsM8Z97ph@bcfes1xw1G~EH+7M_ z5^w4zca!U;9*H;gl6&dvrap-`4J8kyubcWM-ZVhwZ|+TlxeDe6@x@iK5H%&;MkiKr3Na9TslP9LHnZX|`-ZTq&7W%qrR*5&wMxKqnZkk=fJ#AnPi8swjo|89q(_9j7nwvZ~ecd#V z#GB?N&r4r7%_rfWHZZ@$n-(DR{m(sZU_psDEkx$~-!!sd z-n2M*ar(Mx35ho?N#^%|Z(54H6uE9%TH;O1koo@So;I+o#G95Q^Zm~~ZD4tcH?2Tk zfxd29QNlfKU?qt+txV?opL^QCDiZE#1FK5BX*Kd{ys4X3mw3|}HqIOkX!`BH^Ak zu&KnGHY0Dwo4RRp3HP*tEhOHwC3#EU)J_L6W<8`xXoP5Y4f{hxc+eciOb#G4KvA3$F>9Vp?RHgJ%{n+_%) z%$vID5Q#S(N~oika*LP(*}-~c+)XtzW=@HSn{#t zy6HHHHyuwtp1y86LE=p(l24?sn@*B=)5+wM>FcIbB;3;mPL+7mY2?#*Q#YM1@uoA# zXVBM8XG*;3Eb>|Ob<^1rZ#sv34t?Epu7rEqzsl`|0bZ z2PEF~Ao)T1y6GW_H$6<|`=5K-z#|fGdX)Sqeckk!#G4)`^Zm~~ZQu!sH$6#ylD=+w zO5#mVlb@!qo1T$yPaAkv;!V$ypW{v4^t{BIULf=R&pmD6MTs}PM1G0BZhBebO|Ovo z{^y=H@T$a{UL(IoUpKuj@uoM(Z_w9GZ%VxBE%ICRb<^7tZ+eIP4t?G9uEd+(Bfm#q zH@z?6o;L7-#G5`Of5@A<=_83ZeN6tCzHa(N;!U5DKc%mmK9hLU=VZSBxu*?$A@Qa! z$$bBFPaF73;!R(Z`TpmgHt>zao4zG~OJ6sAC-J85$=}o0O+QGy=|?i(|K9Wy`6qJS z^s~g9ej)!tUpM_K@uuI%ztPuCze~L75Aq-Mb<>{`Z~BY;7k%CIx5S(NA^$^PH~lN| zrvJ$Q(R0&aBz4o^2ommTgCk1nrooXU+|vd}mT*rS97R$$4gOEUJ#BDQN!>I!nuL4W z;OLUNX>be)_q4$=C3Vx_SQ74OgJVnTrokZ+?rDQfQa25DNVumBc1padi_G`GH+7S{ z$#qkY#G87_eE)k>AGwcQHw~3|Q$M+%zHS!$G} z-ZVaWeEPa+0*N_gHuSn zX-e{xys4X}l6ce93CB&O)v4L8OSrx*G)4@ylEyf-~Zgx z24|LVPaB*?;!U%XXXQ=ZG@HbmW+%^1UpLJm@uoS+bJEvMb4j?T4bCm`rg_No@TP8> zSK>|ck>{hYo935rPa9l7;!O*Z7vxRdw2;J`7AEuk&pmB$5s5b~O6L3Dn-(K4My{I{ zmw3|>HbDLSHv+D&d|s zxS7P8HYab+o4RQWi8pOY=KJ59wjysuuA8=&c+)oIZRqQ!Z6)5c9eF$Yx@mifH|;>? z``?>(B=1PBn|6|T)6V3b>FcIlB;K?uc~|C?M~jEzHZt>!aZ$pPl-3}Mc#`y zb<^Gw?rDSjNW5uZ^1i&OoA#4<)BfcB>FcHgB;Ir&`9S)*=^zRBw84WV-gF505Z=^H zhf2KZF!Evab<^PzZ#sf}1by9fq{N$!A|FLxHythEo;G-l#G8&K^ZoBl$B~aC*Gc+;)qTj}ej+a%s}JNb6{y6FxH z_q4$~CEj!w`7YknO?OMY=^pYu^mWs{5^uVXd>?(?bic%#9w0wJUpGA{@ur8!57F06 z4@DAU{E0H$5rwrl-jK{_jmslb^tH@zkCrnkv&)7MS!NVumBzAN#j_sH+@rfzy);!PirKcKIhK9qRVN92#_ z>!yz--t-Ci6Z*R8Q;9cyM*fVxZu(r}O<$0|ps$<0lz7uuw#N?$kqCh?}<$-mRr zO@Bzd=}+>X^mWr;5^wsO{5O5w^pC`w{w4oQUpM_HshfsH!aZ%+2$H&K*ocz4Y1l{- z?rFnDmeftdMv-t&8}>g*-85`e3HP*Nqe<$fVWUg9rwtoJQa247Q^GxM*jSRfY1r5j z?rFn@Nb06xO~O5GScjx;8rCWCrY>?<@_(kzKdiQ~Tf@(sYyMnfg%GVx2qA$h2qAppYL@s9C~@pPT*tamNv z_>8AjiMjt9Pie$y#F3}f3^JZpCtjU=H4QSJ)*|NqZ#-oXGt_uW z5+{ixPm2sPp4KK_n|$PH9fORgOyW%Pk*9SHGM?5W=KgOytxvo@apWn>Ap9gUHZTZ3 ziHr>mGM+Xf-iZ3hQ?^0I)5gTy|M8Q^*u)^?X;WhE|M*E{0($avb4cuVq;r>zV!p0+05ntbFb&mjCHGPW_uc-oeDTWTUt+Zkj$OTao_06Lc-n)Q`@iwDC-I)dk*8vVjHkVb_aYy8+S?%GX&>T!$VZ+^ z3^JbfCFcH*pG3xf1{qKL6LbH^Pa>n#Amix(V($O=Nn{*oknwa7@j>JxPh|!fPX`kp zOg{2-h(X5Fp~Q!hk35wdgr7vlVFnpbhZ7%8P2}kagN&yN;tKMSry~tAo{l0uihShh zXoHNWN@DK+_(^0OW03K5EHU?g{3J4#7-T$E5p(~?Pa@+ugN&!+iH|2Ac{;%$RkR-9*g&A3uqVn+-CaZXv#feB`OaAmiy)V($O=No3q+knwao@$KXzPn`zg zCy{Z7LB`Xa#CK8?dAiFW5rfiHrvfGM;*fx&PxQk@28G#?wQ@-2d^D$avTw76{3J5oG{|^*i02v`#*jX z8DAP?JbgvX{U1Mxj46YRr>}{*|Klf-@r^;o)3?Oj|M8Q^m^KJMiHz?IGM>IC{+^o1 z)Bg-Io@R(=$VZ-jFvxiNk@!dQk*A*wGM;9MXURvNem2N>`h}SLKYkJ!zZzsb%@K3| z$4?^TH-n6)--)^Z<0p~vhe5{EJn=mF$kU$&8Bc!^|3yCX^tVCyNn|V-WIX*t{0}ve zr+*DHp8g~Lk361|GAQztv<$*eB54~Gc}m6%!cQXU7!-L*x(4AVk@O6TJSBaD@RLXe z21TBdp+WdbB;y7}o{}pVgr7t*#h}Pjaz%q8Psx=G!cQWZFevhrOf?8UiR8)#8BePa zbN@GdCD}%cv_cuUGkBq^$aqe)+grvkDo*`%OLzDk{cLg zJZ(t4AvKYwjSMoLvWc_FN1iq|$ava>coXuGr%eqqo^ptD$VZ+wGst+_oOpBck*6&T zGM;jYbIC`Zwlv6i+KPB9@{y;l4Kkkci1Wxtp0+W_c-oej`@iwD9r1R=k*9ovjHm61 zwX-DE6$w!_F3^JZ}BHoF7^Jq$9Q_9W*1kDo-c*dY8Ql6x6sJnc=qH#L!`eGD?5N{CCy zN1paI$avb1ct7%yr~M5wo=S;J$w!_JFvxg1koZ9Ik*9+UGM>ta%g9Hb4mQYmI)wNT z@{y-Q4Kkj}iOb1Ho(?m}csiVz`@ivY1o08Xk*5lSjHe@sk0c*?I?5p9>1g7k$w!_l z4KkjNAwGtDDm9U((+o16YKd#fN1jeM$ap$~_zd!qr!x&Q zp6ZC}$VZ;eGRSy3oA_+&Zu+mKtO{olAT!`N-2UgN&yJ;s)}Or}GRl zp3WyepM2!$0)z0ANH!W|JY7h9AvKYwiwrWJE+)R1eB`OgAmiy0;!DU!o-Q@Wcv?=p zoP6Y|*&yTTGUCg~N1iS>$auPf_zLoorxt^Zrz?s1{U1Mx+1H=!Ik397lWIR1c{2=+r z(?bRsPY)A6Og{3|Ymo8u2=OE2BTtVSWIR1a%>5rfiDaKa#?#}(-2aWICy1XQjyyeS zknz+{+)qC8^pruy)6>LHlaD+-W03JQKs-P`^7O1h#?y1e&ykNjJ#UclG)O#1KJxT} zLB`XI#4nPMJiTO)@ias{L_YHLvO&hvE5zLY@smitY7l-B$zg+xr`L#IqbBn7x!`G01qDAfBKm^7N@e#?xoS zpOKF|eQuEPG)X*3KJxU1LB`XU#9xw+Jbh)5@iav|MLzQMwL!+yH^kqNk34;AknuE4 zJWW3G^qoP*)Az*O|M8PZ{?8!eX@;2lzwz_~@ejn2rymV6o_-?!iG1W~)*$2QXX2m9 zN1lE$$awmd_*e3gr#XX+r{9QwBOiJC-5}%X58^+_N1o;lGM@e<{*!#<=`Vwfr@x8+ zCLeiPFvxiNhxi}zk*9wR!cQXkpFxqQMKTCKiA9z{k*7trLHJ25iWwAnTI3jnpTr{9 zpvcoA&mjCH7WoE6o)!fL;U}>uG$``4C~gpb5{p(aDDt!@#UT777OiMd{k*7r~8)Q7KLcEG(JgrL1{oi;>BTgfZJgsJs@w7Ve>f|F&YZ!#5|Nme7 z>Sa|+Hr;jw@V}K}dBFd+aZ-U#2{#kSdj-I|&rL4Khj}UFmJp6g@k$txRw#loDTQYW z{}a06ptKTw2{aQ6QYyWvebUM)(8hmqa}p-_Pi<@FKhc3Wtpx`7x4Uw_S_Lp~wQ>GU zPV`0phA;kYHPQcvx6)hrzw{~v; z+F?rCxEuzhO&lP<33Z#W(i&wrzkJX}cQWnV$poK>zmWY|q^7yJ1e+p%7YNT-q@g z8evo_pr?R21yj;a)a=v&^U}^mK)f^a7ZWe;kqYx*P}&9EUHYV5dEa$H+N~F6rJ`yW zkao|4R%wrPKx5B*=#+|6phnuO80vtYy_=+c&?*toUos=@i^hJ;+>crN7eb3v%6VxO z^h*bze?Xg5M($w#+o5&RVZ?{ge^`%ncmU-vC>=rL5$!N6Rir=#@Ls{}Bl7|6Bgdqp zI$=UOx?8HG_n1uSSn7|hm6lMqq)V!*k&ep)bdP7p6Via(3FA^V`&ReBv~*%NRKtjL zQVul0oOCj~oZJD#HLcPqDS)R_7Nk?DKaKa(sI6s2?U-~r&(oWMUC-#2&cyebJnPb- zO*%_Z3Dlf50MpXhsZatF(mBjNhq`*^){ja{@wF7Ka~q{)%vy%0hBRPi10K#}m-8m2 z^J{_n3pj7=l`f?3LbNYp&c)e)c2mA|NfKIMPP(*ST8`Fow43Q^CU;pO%uAOSK`W5E zq6W}zDT99LN@}j8=gK~ql&(sH3LtmYAS_5%GwbSh;Jg)2tqm|OT~h$)UNb9QTMk3g zb?ny`Q3>?4jY!uEvY;5Mp&978p+mYc6B?yTe3`(77ceVkwcW}m~S9g{`k8~G4@8aw(`nrg_$laX@Bho!Zz?^$ap;zjr zr@INLy)O^wyKhpuzYw}4F1UK29GKIS4b(lD5AD)JNucMUM(JVd9wzrNJNDMWxbz6x zj|@qVW4}+^j?iL#@X1Q^nNSMNFS8Ifb?M&)B<(m%>9V>kLdeoO!`>R zEPcYfPdNL8{)r@XOP?0Ng7g_)KF@(EX|fo2e$gp?Sr5^ZztvZ%!24H2(iHJjwe)ow z3`pOQ|F%e)7Eu3vAnz+tdf3mq}go1&n$nBpQ-t| zN&1D{FVy}rCjE-$ubj=X>m1MD$p6+Y{az#eLERtKFf7eyK^sg-f8y^?;y>r4zv%m` zOZuCce>cN~w1CFKxb#mx5dWJ8qtbuW{x>8`0hB|JEGrdiU`&=>4O6mWyvO=wIcd-= z%cb5ekmYB|3Q}Z+WwPSr;0K_T=3`|*pE#kXLKo36De7l_xUW^L-%9+I_A9@Ie}%*o2Eg+W>Cra}QU%UUl0 zW^u={)<c}LFmsb6 zG{TguP0`x44yez`gArMq(YJYutStgyW^N`>pF1yW%XV2?Il$bl8)W67xea~WjLO=U z=e8BHwqu9wILj}EZdu%0tR1p}zuS)WvI@{HAl`|&J0}6H#l0{stFQy+W$i-mF7)lX zAZxcW7?xGUtlbM_?GZqitUW7%p5k&~@4e96I}KW3PS!qXmN2WNS=PSnyl*v3$=a_L zsNWyG{dtz=13Mg00Xz@Pf(BrZgQzbrna*Z31e_nOjbOd8e$y z=s%1ZhoOBq`NNreI9f-P!H}$qRA`lTWH!{nxU8cD%s4s|W@S}UTiGk?82XQ?f(cp2 zb^x_YcrHP&isx}?9p3=cvQ8)jcH#bFRrkp{F&Eloom2qqa8kRhlS_c!8fMop@01)_ zr{eF_VrZ3hS}CAWOME)H)8}NJkq1q(&g5Aa06Uyj4qZU)+325LC+i#sDxeK!WYssq zpsb~wFGX+Zw5)Tpp#er@Ekl19=MA|)&v~4k*CFeCb~>Nh3wmWW(%V=o>q4Fv4#>KQ z9WLsZb#W1($^FG@B5oRzbx9^P0KJ!%K&Pzb?6kZdnAgml<|$d1HNw2C%e!S=fsZTb zZApVt=#_OPI#*JE74=sQ!-A}SpRDT(VNTW!yx-6Uy}QjoAUvUTj;rEOjbu0 zFsox!)~)E@iq5TQ+?EaW-ZlXA-Ol^%yx-m@tCPCUQXsyA_zs?TmH<26)gr5lxm{hd z?w*x(PcAgVl&pKHxwjL>WOY}-u&n!d-iPP=GoclxWj%oQ1Cz3P%3(y-gVjL(A$EVL zUDm@{&<69edTRmgM;c&3)}zHRAnUO-=#kY&@8bb*{zN*^|712y$m*{HG@eRAxvZyi zfc!IkvYst~W?9eG!%US}kj?1}0_whSqPK|6VWa55b76c?Sxi4%l^`Ie+Fs2h7X*i@pAuko9*e z49Qxkl=V*&49fa99nktO4aogBCtIyBFWbroVrx>iod;FWB|FA5)+gJk0nWLj*lrPY z%J!)9c>3wkCYw8p9W=`hv!NcwWyk4{56fPmQFaRF+*@q!EB1;5vR6t%8BEDeFh9{H zJGB}HWv`qA)UG@sdzDOR2I{!8*sD&;PGg@m_F64h_Uh=aJ|mld8>*dN0AnyOd(9!) zYcXRjG%^NcCp%^H>zBQ#LH648ufy!jh+aT*-4fu8-&pMR%7Om%3!w!DWoM;95ulTW z<_7G%0efvQEPKN|=!QAj8_}~7H5;LmEyxD6H>PG2o}1FQX)CZ}4$sZVZ_zG0w^a6) z0d&EP?5)ax{8r4`8ttul=GDvIrcw5`oNYTTdpmaHcM?0lPWJX_Z%@q*`7k1TM<_tM zpc>FAn3KH|?>nKf)3EHFJ7h0T1?m^q0J|*4b73V!&q>+4WJ8neU29=M_HNACts9tA z#Gbp;yL$_uy$5G|ra-yu;!L2gct-YKWiTdt?-Cf7y$^N!Ov^5*0%q)6325$@4xKPB zd;fmfr8&?h`vCF>49GqZodd^ZA2cMpjJ>&w*artd|H15jNG8<4tn5SUVN!NEdmmN+ zEwT?U2K0}h_Xz5az(Ykf@H~>bBd25^h2Bxr9!*VUwruVl_OT9B$X=2J>XtCSid+>w zs+e(H9(2h*p57CvI|03F-cKw5YEMGvq;?pSeKPUMy+B_L8a4FRlmJ?%Fq7X;>{FR@ zYPamuieW@{Z6(ahKAqjq$buf(XA0V6*X6*l?6cBjpPdW*ea@lgoMsr3U5{3M8MMe= znlJm@BEZABbF!DAyA1t?dYG1dUXASYsXw3G1;mXhFeCfI9H8eS-Y=?_eQ_NyrwP5L z2B78=YPfgUmyo}d{G~O(zL)a8oZXhwx19Iold_xhpb6NoncmCLyKGAKy{MR9oe#PO_P0F7L3cj9lcKK@8Il?QQ3F4$iAyyHg^NNt4;RZ zWwP&~|K42amfcOhyIJ;q>~J5?`_qA04^aESi0qzhD1{mz|6m%F$>z5W`(bJyM*Csv zdfDp{_ItDn7Gyt`5A4#H18DV)%YM9C_7miv=#~8>^PVKvUjfuT#hj<;eTv-EmCz3p zvY#P-rbqTbG0ez*wng@H1<(Zed%g(h9~7W9I4%1H_Ioi4=zodbUz(FWM9&a9FH`?= zx9nGvP%Zma^4t~d;Q*>+zg7W*vR`kI{RXvfv;uQSs2iD({U-C@MDMM1*>5xR?LOI~ zsX+ZZc`z*dU3A}NxAzKx_xI*yk7dbzpBdZ@?DxlHf6yfR!(Q3rl`tjyBk~_H=i^d9 z>*H?OpX38N6KOCjo4bMi8FN3Q=d(^=pU;y(?IiihHrZdK%Kno0%Pv5d-!AO03ZNRu zeTCjs5iH35dRq23*)T8r+hN($#n3JLyC&J+m&1_k8Tx1HfmuIL`=cNW%Ai^HPY!VY z6B@I;&(6vInf_nM|I#J<*L>jp*GbuP^v{jR{;dKy`)yYC@0|b6-oJDH2YdcO{0AEI z#Pid#|15+a*?-adR|C-hH@Ux=$Nj+mrx^NV|CoiXpy$7N#Z&-|Fs7JQ0v(Fk zY0w2Tip9!dNHHf5Iu&!NcUxgvF^`-_uP>m^pHeKCRV+j&UIg7Rr`QTvfNn}AkV_%A zVuNBUQJ0`UH65CO{L1L85`q4zEikEAS`sRN-qo_97e*9Yo!sgRimg$jSUNkTcPX}J z3edmixMFKn1DYAliY4hwGHVfgEkbkc0-$f5OqfNUHoxRh^0rA1JiXBo4U9h0op+kz5*C=)vyByZ9*x@BW{SnMLVpOq; zEI_Y<-Xrs20Ol1tsuCJuM6si@fxe^L0sYEUsDOSLSL_%xj-mFLX~no}#Fntj5^9$$ zC{|So>~I|Y$HNKqo6{U6&7>x0S${V%M|h^_<;Muh@;$--w6yRA9gM1;uU} z0nTqp!i-`a)ZdE6t?Yd3xMH{UDRz4fR6-k&>qM(_RkLALcV%$4okLN(2VowY!_9Qw_@^|QWfV2Ju#hz+{ImNhh#GV;aY=GW@ ze#M^U`D~A3&!qtR&$TP|JbOJqsn{SrFEIbbR6yq?YF}c;P&Tx}jAAdBDE3O3Vy_As z0q?`S57YZvDU2xgI`8~5lh_+oK+i}Wu+y8=yp;lNioK2I+r5g7GJlk_cRCe&ml^Nn z0J-<*8zUa8hattdU&P)g_dy|$`(RYD5A&cIrW70J$!`#`k4hB#n3|87^)Wkqk_+f^ z*N9E<{4^EV`LitG{qsr1COcqGu`h~&J-+1Z%SOe%;`tRdQ%NX;Va2|#R_vP`;QZT4 z=!QwfrW+LdE(NIhz5wPG`(F)=D>jo2>^w7|*bjm>#eSskr+&p|(VDG;Ud4WPpc>Hl zB^}xU?O%&wRI#~Q7*p)GQkYQe_a4RmNJ0mUC^paYPxSsmn|~$}`R~ZRe*NtjGUEuUnLEgzbbQAZ36OXg}{u}s^qMm3d~x4 zK+YP}tU=EjqjJ(6$b}&}YgWivi?g+Q)*N0Q%O?fqpqz%;k>YY%nWl!&*Q;n||&DPIfn-xiRM(56anuJpZi1*|Z4I z$f*LJo3Yns)NjsnivXzEg1+2Rn3uC<8g#;foUJN>8C#=~$KKoIL5rMiB`1GI&i3tc zc5s0F4qb9~%mj8UNJ5*Oop|zD@9cy=pY_hp=r5j>Q%GDmEN2($cfrT5Ja_Grvm1LA z)d2Oo56ao20vaKTr{wIJ1;x+|oxrT(07?Lzy@>a!h7mb?XG0M*%h@Ld>g1GAQ^F4W z67M@BXTK^yWB)cerPP*^JAm8)-Et1hf-X4+IWQ%stVYhkB|!Zl#lZYS&^j~+&^?qL z%c(6VJ`CT7RRXn#H^PXVBMM%*Z(!?W0>@QcfkiAA{C0 z6LO9%1@=6azs(YQm!Pqv59qJzlyh7T@P2#&49Pj61jgi4SHP5<6Kmw0MBhn`upsB; zVL3I^a!%p>6rQKn$vKVl)2iguMlkbq-cK(BbkE3#898S%x2{3XSpl>IGx%p1&N)eF zl~d0i^+R%&Rs(0}Rzk0wWz;VtUWNw09XRI|$T>d~x?mU< z5gA(YGb4|6JYf}Kd>zH}ntemz^IoDUfh@2bn zal?R|8y#qq)1H)bQ-hqF(Yu-aEd@YtM;372F$^pXQYk}uzgPeDHzSk{h z%mH@zka%2B2WWo8{Es^2e4K71O;sQrxE&#C>qUCv|yQ1b=* ze@Wk$)PB_R?RH_ssczTF(D!VP4Km39#D? z@eiE+Fe&FpdVi_{>Spvy#N;Qfy- zIr9P31O4+8a{lD(FV6mAzrV|1R?Y(Xf2v?g&cA8U0PM+Uzw=+0TnVaRTCP^vGQq?Ug&_u95>hSIv@}mIl3YSEFY&G*%mzyLur|zXtVdw8~9S0rdFYz+JNi z=H;$c3zKp)*eh8I6LJ^T%UzpY*5+*OUb*X(!GPS%9GH^3ZY501<@W-2y7&rbP$FfVs|{tnw$K{E`> z-LVh`o_uM3)u`CPvp8=1{r!o9Fd?nQYJJ%?ak?!{;|q2IJ1_mVyl}?mzD$Za{8OA{pb6&XzJta))V#~tyB)yH_sG3B zDtC;!G0xu40_s2D@A5%4kmEN5_ro@rmOD=UN7*nZ_v1Q1_mdjnIYIpdHJ_6IEFD_q zejWh5lc{pQC<5xfa-d)CRD;~F$$icIZ))X!TMQF&r^}#M?svIB-S-tRCHH^jz;mV) z#^nA$&yRw7xj&)#Q>Wb70%(`}Gx?vX|Czd9ssOLQHp!i1pSf1KzZJr`+~2$8{=xfv zHjK*slO6tKhrfE|{@pBh0iA#IU|8;dZSs_aVR=@qJiAU_ECt%-Icd-#&#i!Qd0q~* z$nzUvLS9e^ee%K@=$04f87IF&3DBQH--=lyRxzNF(IGF%{z>{4 zF?-RRytV7)t%Jro^sK{sCUY~T^frZJ7c!^0wl9EB4&FPF`L% zG{Shdz0HwtKsxyBp`babConBKmiyfA?W|d(_I?6V2iz@V*!Cd$Z%- z)AIH~yTpMWdHZGpy8D#^XZttGD(rTRspjPssYZ*O5`0J0Q(=j zAn%Ytd55;iE9d;Ma(Raf&^}^LUIk}IqJI?o9n~!FX!bg~31;P0QhN;N#}>kvJU(Z= zDhFER9ajdE@{VWT3G7|X%oC|Qaa7((DNqdPpIj@C&snc#Sl%f$@=itjv;ex~)e3l? z&iNUfpUJ*;)$-0Fe^xzo!jwF|r+a5l%R46>sHx8b<}78urOoorb%5Gs`9S|NdK!2( z&~qMpo;N1%eDuyAmv;gCH0HvBybJr~UEC^<&rt7D_FqoT@;Q0U&rR2;#lNqi@D zcecvADJ@<6TySEUi<@3?IF93GBuMzs?-CrT^0Y_d>0Z{)S`G?SY zXk6aI&GLHLr?(9j@`lI_ z(Km$l%N6oorRUWVdBey$t5$y-^KA@u)L4bp+(-u^nc9r zlN{)lH&F`An3$ILX*mqZ`z#mQ{w0OD#;u`;{D@h2C7Ayx)j_Yk*mKzoYqk9Zbvnqg&p5wY)#k{!73ff2RSt zzX#+kFl(V1=H&f@?my`MTMQlY{=?6I)ABVg->QH~`E~(x$&V#rLcYV(sexhnZaz@! zl|Y|-zgd2e1)PPPg)J~6KVAr(fHt3-{tAPDR*C~f@>k3OdiW0QuQVq=Q7k`|+{&fE z`zr17SIvfUh~gpnX~b#$@>fd-^j7bHF<6klMlMu93sAEL8tF;-YX&eXe=T-di@J;& zpf_0pUGf(R+T^dzthIaO^L^USOo3kc>sGnP%4wS>J{LPwx{Webp>NanapUZh}1yH|bDWJPm9`wrJ zIv>!@D+6}frd$5D>GHQjKcD{mM)})QvpqdK(7R(M49hQ=mA?}jJGIK+8U3A`XW~F9gN85Gt=LLd3!KxPv-B5ZgCcHwpTSw$=|z1 z{ywSlOPISay8EHKUx)ntdG619X*Mw9fHC<87Rx^<9VX9Wf=pA|KE?vQIvrqyAC!A5GuUr7$S}81ly! z!;t(X^eriaarsrv@{jA4e*(4D=vNQOKau@U?3aHMH7935kNlb>G{C(4Q<~(TipHrm zFf0Ew@~2J9=eKXac255374pwWfe!g+7Qv+ax^kedZcP4J`SQ;eP{Bx2pB)>it zsHrDjivCjSmR8F@m;AZZ@!9GxqragGrsSVjFaLbbFW}iYEdQb+`4`X2zhqqga`Mge zHIu)rPX6UpFeLwqTfpDzp4;=Tl>EyJJZHoy@zlPySuxy6EewghBatGw1FG!1F!K zzlZo9^7l;2zn9v3nRV}&{O(d<=6%%Phvxlh^7+j5ALy6g(=7i%^dB6O|4^;`hqHm5 zAD))qi`FC5K2ixY@*i!5dHIjk0PlVE@*jsM1oS*f?~?=a`y1syRSq36A^&M+JX0lq z0G($8V5jFZVNm`cGhd+Q#VPsx_UymJj3Lg3(0e%*nDsJsujE6Y{8vkX{$XYf)AL#Z zFo*A){_D(qo%nU~Z=m%Cy>HMnk^=ONw8?*y`ERoGTWQcM|7~Wy-2n6QM;l>M{yUt% z(+Oz4%h`Kqz1J##jNY+I7?b~gK6K0f0Bt^t{SRj3f5?mv$&I7&QM>$)1=N2$BL9ZJk6fparS+q{Qq^xpJDb7Y0w~_@1y>YE%Nz3>i<*^GxBF^fWOJ? zg8ZN9`-S*dJk3!%NBrBQ{NM5M`>gyw*n!Vo|Ibul&R^*NO%10-!EPg9aE^5YiKJ7SDnv1uF#5 zq97$5dKIjg1fDCAU#S|!c{iY*nys8VoB~uK@-WtWRxL7W6CFfZaCAhA{=%T?#g4 z&yA_wBncHjylFY`%wbMW4WO}ED%2|2T+ppx3!Yn4Dag$SX68;R;IlH=a!|omrwsN=IT*dZ7C6zoV`;6R6hozkIK!OjIh&(0GH7NfDa z9LN`D0=pGXD%b^2yOzSJg58D`6cO*93jGT9$bwDly-4om?w4(w8JPyqEXs-P?%1{EA! zt>6%J4`IJUi-5W1jSBc@%fVsv98TR4X;2C5a0KTSrO*y@3XU9Aa1`}NQ*$(Dm1tCU zD>!CI!Leu@+oxa&JMld<;PW#$E)VECp8N@DpD>`H8ola%1t(?!f1?xU6`X|T$^0Eo zo={Lz2Ga^o;q26OsDUvBr?LBK3kqt>0iDxxVNk&tNoY`TCc1Tnz}Z>t3eF~fcBg`K zQh@XNVg-D521_|RHyb7tENfNJ02j0=Xhi?Q5(O6(05uoQDY%%vCTg3|xrF>BQwlC+ z{&F;z)7Q*dbC-h4*!l8wpzjKHxS|QzxurwFm9+}4N`qbnSM$8ON5M6Czh+dywe<5D z8eBV|;JO(FZRFdiZzH~*=k;?6ZfI6;V^TqTje?udiKYQIjrr?26m{QQwrr^N}1rIUvA>xP274)Lj+pge|q=H8UGYTGKufA+(1T-Hn z17r-FWZ`&)tYr_g^2{io4*nzLtkAE*Ly&lbXf0=|m|&vgL(&*uU&2OR}3 zM06{7kr^+}DR_zJ5IRHb@p8R_S5lx~!K*x9tp@ss>tIm9YefoPC;xh*f;Z54gMHpu zP%uJ0zsUw~!Q0FkErw|Y?{q16w?x5v=)X6wV2oYg&x1(?AE5Ukwd3fG4=DJkLczxY z^eXtIQo%$T3@iAwK*49}Py=HMKCe|U$=M`lUo1%-CC!dMB+D0GH^C!dF*%hPLu z359;G!XO>GVOn8W1Y-*Mt{KL=6!KXZuFwGU3RBt@u9%{5r3kcF8dR9bhfam5nF?2? zcI9D(tDvz8&s9r+8EGvFSEGKlDTS-o0DWuFw?@Ch^gLkKHR)MP3Ny%OFh5ECA_ua7 z{zd4lU8Qgx=BzWLFtbx3pON9Z)UMaAaDD36rzVSM)`-Fl3Smeg-zCEhM-^^V0h0=| z(a7exah<|V1k`NWq%bE71{7}ArEv2Ch@Qg=w;vX)I!JIRyfci5lVM1YDyTY>rh3C`&ef(Y<^4&75 zUr@M|=TiO-=N2nmM$Iy68i#$y|GSVJG0w|72edY@aAIZ zR(MN`!j2Rme`}$_+e%k#@G(J$!anBqQS*2f zv?zQcfI^s7_++2L{xabC6uzIT2If883g|qO2DLDzaG)N@J=>u0xh93r4=Wr*`-Oa9 z&WmWiNbaRJg+r;p%$J$@@_@ouc)r3rpPAus5%ep3tw!PNc?#d4?~MtCBhJv7D~VArueh3}^TH6Ii}BTOp%ung!Q&w@H2 z_YpHb>Qwk~5}Fl$g62e^@Kf@ijw<|&S)Yw4{G9X88(=}tbq!Ga4YR+QQur;;Z+V}tSNL53Jqo|igK>p3)e3*eSNLO&!k>l} z^8GOUSug_g3V$g9=KaE6zcTMv@^j@dqwu$S7*qIrkHSBgF`oi$z`p!e8~(}ppZyB| zN&@-6^PpAX0=q6WEBq%N#uffs0(kh3=YQjhs}W`ux9b&m3ZP$cw+u!U_ljUZaX$}w z6c5T_O!2TuaXusCD^w|-5z$E0w{F;t9?YJXcN!v{o5XJS_{FVNUVYDuDXc z(OkU^CKX>JAG#DzPXhI8wkXbLV|=YDm{L50XEFuYIXR*DqC)6VeC-_QP<$OUGqa&n z@pZ|qJE-`2^sLX!tOCV1Ah!YWhU~Oqi{cwGd!v5Ev(d?>c4Kzmgm}|jV8Cr z`1|m=7~i4<$mJHpjN)6iE6(R(d@JIun-tGWfnLS8sZ@O1BrtQ^8pXFmbGvTE^U=-c zxjp&q(b<8z9UaJlC>~b4fZBo%#dm_8nYlO>hzofZ(#!Y7_^wro?^X&kiWdNdm*NLz0`CX0*FnvIZdn>s0Ow`s9?bp+qj4~Gd`89(DFL(&nOFSKDi~3`JRhh( zj5&u8mI%cm4?&I2@?XB5Az7SO#Mt;;7BzoG(owlL$$d_e!IY(V2``mUzG6`j^u z#joM~nhwRU6_mlC;@6>b-Mr#$^tZ9Y4e5&C$oY-TXrEC0CT8B8tN1NwcBCqPE8IqW zd%5DB#W1b-9rcRenFald-_@XaS0>CWemDB}bSZu>xo+n0c^JR1Me+L`K>LAl#e3-M zsa5>Je8nGP?n6_GKg@eCb9&KxBnKuHf3!*Q$C%rf0zHa9PX6&mVEz;IK0)o1Ng&ss z4!l3r4nvAR&7M#9!JOjHQ~>cn8Vo4@EOVaiRs1>LpQHY{dBvZvgAv6C3t?387iwT! z@fYcRk@uG>6(33m>R)D$m&X);B@?O?f0Z3xrDnJU1{HrTU-8%3o$r!yJ|E+6q5U>z zqiDR70#k~=JFNJ7U5byPHP)~A`$a(hg9gPvWX^{JijP+TUis{cf0FrstbGe$lV`dA z^CnHwymi4K+Hp$rujbslkW6cs(FobdlW-WpMloT5dP-~!oxzX3Q#wmXtDS$LWNH2u+D+yQ+oFXY)NmBJ7NfF#5M@Xva1>jx_ zx=}nwagQwm&XQDzxb?_;{dtn&ZNO&WG)WB+U=whHq{cpA4@n6bI7m`*JxNV#Noo!N zpxd&Aq}B`o8g1K1YM)P1hX*)JQfebfouJ#rz;kyefHcyepFTlS55i6Z{pp}ReHTeH zvcNMW^&;%d6(r3n2QHB`8}w(NAt|#7I8M?vpmPoAU%QH=>sA6sN$Oim(j3s8vll>| zx$v8Z_j!mj?h;$ZSCTS69+yKADpuPAINlWm&1ox$t z0Lr!W0!ho@wk$`|jc~hhJ4wrt@8zJg0{0ag0OYOz2uTCakaW`yl2-Nr$ji<1fnC5^ zl3ueEI84$l4J5%HR$7$-4w7{13IOT6wi7r_(rtYt-HtG~UnFUDmZaAOfM-ZrGf2`M zh<68`F_%f}ngGxqLY{`UlC&Ov>$j71C*1EmL(-ezcNd=TI!4llJtVyuVX$tK-kKq4 zBjRklMAF?SNV?|`N%tb}n~<0L5a#~%B)x4jNt;3I0fc|B4M5npBX8MLBt3MLq=$1P zVXl$3;Q5^h|40A;jdyJzX)9=BE|K27o22(3pYK7uNBaQ8*|rpTiKNF6?r{fjkfisn zAZa_&ecuI=-jDF_-vOK@=>y2;2jCC=rL^M^NyAG4(AbIho!bHA>4O^p+;{CI=|k|x zd?I~#5pbBKkAT)k4wAIHiKLItC+TCz&l8mZ(%-X!q>qFClL-4H+&@tcYy?h|w08$d zpG2Hbo+0T|p#NzPaEzpVTSyuyBk40Sl0NGI4wLjb#5uT%B&@BZ&mSP^3#f-LYzC0V z7m?-{k^UisJ+uipMbgtq^XU^LeQ5)5nWQfx&NF*S`pO=XzKV3PwvxVvw7-V*pqG>m zBh1%9|Ley|dKT`_?k4FQGO&xJ9Nu&LNO}(RpW8~(HyePnBz+6%963$W^KgG2`S=cS zbO%Y_MP9$Rl%y9x?-=49J4Vv?@%;UZB>i9;Nk0q#pm)3vK%9RA%^!6Fp!M%~KG6#t zB54$57(GhT$u+l7pxJc4z(EC{faG0c@ZzSm#$kQ)& zkaT7xaG9iEBK$9rk6*1P>DTc4^)n>BG#@xc(r-ZTw;qzt21xoH{9is#()mLq{b!D( zKOpP{(EVeUq(33OKOZ6K;!%?Rim?Bc0YK+c8-O@}+eOmf*OK%PguRR~|9y<4D-M#W zhh(vhWW#2XjfY9bKA3E70CtdUSxa*HQj)E0B-`L-gPSCiECY%M*aW;pvf2wAAlVLD z_9G-a;N~g=c9HDv1P+nxfuH9f$zFu@A{}1_KwADaBnJrCLvpYQI7)KGAaIG~&?*4$ zm55XM49QiXQI#V(44PrQS0i85pdHyta?N&H&6;+_n?|o%U_OMUp$#0(eg$uc@;n zcOu_in@R2ljqW2PrxyW;m%c#qG?Z;R0f;*z1K^Hziac{W$+Hk<7Q)ZEO!92d%^)Aw ztRVT?GGIOM63N$XBDoKFngh&5UgsjMd7Z!+lIJ7-^`MD0iVR*ZFKhx1le`FdUj$k= z;Qa=KUyOK*Hv-7>l0M)F$x9JtDbiiGndBSG0OV_V4A@HY3IdSs3i$W$C3#>KfVekp zA$eslfajax_8NqH&2Ex!K^|^7Px2~+y%l-8^%TjkMcS`L8D9(c+ZF-H&mjB;Hv_1% z+u^sm5;#rr>+rk=wASn*`Ho%ybY749>v4Yr^1c@F-?*0Kbtv!92H+^k>wAF1B;Sc} zcfLgOn?U0((7)?A$r}*%&E>#plHU>oHUkLzmWw36wGDv(TlWEHNZuF$Rse{<@hHi6 zBi`MhdG}V}7|HhpfK>p(--EK;i*)Y=y?YUMlMJi`b^~WgzOM<`1RMq~lYBqY!W<&s zzlY?v0h>3H`~ch@*a0B?gIfU5d3zbK6abyKpC&oG2mpde(i+D5PQ>4Np5za%B6*hs*iQ0?BEUhC zKaBVvhTliF0!U*w{5}fWA4U6oY!}H-GyrEv-qQxa@8c-0m?z)_N+>y!6w1}>2NRF35ReZVD>N4AlC0DcER_cL2a{;UVs1R&h!mI4TK z5PA4~C&^#fNb(nZfdeETLV8c1C;3ajmsbFY|I8thzmg&Ot1*(lCIc5qK8$jFeH+Qo zc9Q%Jq?1GZ+%b}$1I_1l0|@iY^(23bfwYd`{%sjJNb>V*0p$Jp%OrmXVUNQ7=mC5&p$9B%j(s@{bn*$4LH(2f+JJ1HdK#`8kdI&+z`Ujlgk| zf4&B|L^9Si@-L2(d}cj>aKGF^@~=7pr19$*06H%<0XdR?gZRJMMe={Z?Y9wND{!9V zvxxJ%Z6v>32EhO2izJ^L1Wu8B9%Vd_`|lC{4{*PL@?1c9{|K6YM7%!*fK>p}{_{SP zFXH_o^7ofTzyXr~iah)k@BallmpXwg$$#r5`R}0jcZC1PdXg{0{qjMQ{~K<3gwMnM zN&}E1h1LKUND)22E>aBTz$)MbDMt8}l>u8xF%htX6muu=3@Mh~q?E^iOQcv4&w3s@ z7c6Z_k$M17ln;`k$iOz>G%4yL05RCuKK2kgp0C#UMaDo)f zii!`Z`5nM^QUU>BFDXH|2NA9U;VaIN5;{amUNQW)s9ksl$7`yQX0wt&~EeqJAjL%B;cMvI!X8?I{}odDFZx1N;C4< zj4&;rgB6d`<^k~Dfv~BSzy(q|HObwc2cl%QCLGz700Q}b>-VnkL!GHZqQtm|BcOsoT z&y(_|&7|D52tc?E2>0ftq`U=rdCM_UFoP*~A0_3U7_f_!dqMNwJ)~?x+)X(0u9`Df>5(GSUMe-282^=NmX{7V?E>gY}0O0;* zxPSQ=DbH*mYA0Uk%g4PcYk#ZdO;|EFkH>CF?4}d)X=prfqE(0+Dc{u_9 z(I#LIDVWWaleoWln3Pj+JM|1HKi*2pPd1S9Q^fh{K2lC252sI&^0O={Kgato0>EBU z&LFS9ECW`O@+$(ilk)2`q`b5gKpMY6UVd|wl>dPHZx4`i_B1KKTLIwyvIAH{%DFaR z4=LyIeje%mC+`1=bbb%Izek?_fHGVF?F&7m{E>mMe<}wy0H;X#GwA*KB~mUTFMnB2 z%3o22|B3*cfgCB9@P27KDSyNLZ^--KSCR6M0C1j^%LhpL??F=XmB0y7u52R}D>RkP zl8PBiHS__8NHyYaJVI*OTHqq7rcJqj{KzKWzF*m7>HsB1Y&V8i1wvp;yN~$LYz|XsxR9_RYgH(SdfOr9f4L(C^1!z|w z{SeZugkL4%Vop-4@E+bsYV|GvVI#f3F;Z*rUK=1aDw7&Rnst4^VN&bo1II~?uL52o zwP6FPjZFaJCKdtDkeZAE+kwlZHmwKX*SrEi{1*7N>?XC<10apIN&qz4a-_B&CA9-# zQ=pT=bLUC`lmrCPm!8w0Q!Kn0MgFD?V4WTG^y8aB=x!oaD>#pO{C62{^sl>b?zXk^U8ojq|S%? ze9*mq3#kiwNL>i`gu4HB6SJEFNNPSgu9V|&7>}O0J}+DkpYg9 z+P?-kL+Sw1#{8w;w3pPCOG&*MY2SQ+)Yq&8&XRh|c2ZZ#z#dX>MH-mT)Y~e7eWYRs zNgdop>g|K1u0}pqBi+~Gc}*K|oYXsZkotOrfBhv=-_Qqa15T5=whX}ijYxA{1VFr@ zEu^mZ056ex=Vnsh1R8J30vAcWYXyM&h9jiD8R@-c6>x&ox0VA3N!^IB8Hab*cB~NPQd1xY+?9?q=li0fc|xIH?cz0q}qO z3Scj(SZ%4efzR7pdwmdjPr&^X7fIc_fz(e{0*6V(9H)K?`TO*C zQupC`-)T~x+6){ab$eHa{C8YZ$(Ec*~FrTT<1OSA8<|3(IIY{bP*8@jL{n}De53eBg>xlm>o}WES>NgPg z8^~X7FR9PXC-s|Uz&299)dax*$SUA8sozHUZyzP~`IP|Nzq1xNPU=yle-!C__W-Hi zL)aH$0Ky$Z8sE>5`UB+U2Z;Z}PT(M^#}V(}Jf!{z?mxo&za!lfpg9Wn(Nm zNc+VTq@HR3wv+l}y#E+9e}el@;Qv#&o!$x{-Jfj$;P>-30JMIAFuy?E{Ng;RXOR9G zxcw68{c=5UoYY_S00&9^bsvBF0bC^YcWVLM zUq)VEMn2E=0`NZ{0rrskpJf2j{e1v9M(Q8h0HkvP@h)5<^^ZAH|8#`ZKc6M_;$~9+ z5(5zaFYx>8N&tTUwTRS9NcR%x{cS0!e?ONlknBeMUqB=N*Jas8vYVEY-P{WtAiJd!z`Y!9<>$$6-9dI+4}kyK zDD48ykX>E@AdE7f>?*>mn}O40x8vP@k?f8LkOlC68=auGTg!kwWN%wR_V%-6?<8OlK-!q|>|OJLy<~^B z(%!umc!}(3570yQ9>ncAMfPbs$UYskre7fYjDx^wviG9AGXrFwh5M|nWS_kVI7;@+ zDzaaLG_F|#oFF?im-g!rt`F{gh(8xJ=RQOBd5AY}FWKkIz#+07X`-(-tX|nfk0WOn$U=P`ELY$Qz z;1bzy2Hn?y?rScR{gws*v{tPr`>hTjOZL|yKd(g?XeaHrfj;Is`|VrFj=9dh8sSzW z{OdsLbqC14W{~W6lmo}e{(8iH1JYTGyuK0X50#O9eG}R5M3^`20pNBQ%68XTvTp#r zH}{hLEq%ZRvcGj7**7Bo-D}B?dCYzf!ru%3d*QbUxNi-Bc=sdT{m9eX<^vmn9N9Nl z0*JTy2-zQS0LbG5XUP8GCID%^9cjEBdCf+Ey#VMuv=qQI<}~}mtAK+5(s>8MzGEAJ z_*?M21!3Na{Je7!fO>f6DY8G(1MDOFyUKx00MghBdRuXS_g1pMhkz{EA1woR0~g7@ z4QXsUME1ujfz7~6WPcoPj~^%ddshH^$i5x%wr>C~ll^^of4>9RP4*9Tl6?oBcMJlT z$UeLj0Ii)!e36C%LJbq7Fhaz@w`U*BU?+?#b-_Y$BmuL}Gt3xj&g7O$tip_w!W@7yPoCS91SA5%O7T^YFBG#3kb{$hP8{U zrL#30j0W+S?N_V`tD;@{iJ%B0LK$xwSH}Nc313x7#7*JF1B=7qz^u8m0;L5u!9NnDsjnMUViflh!b(WX<-s?$0eE~7Cf(GhK3+>#$@SuDyD ziNsZelS5hb9x`gQpA%W=A$?R&l*cq>n*VkIi7IT$I#kqHKFBQZ2lOi#W;Ej0$c2{oD zQ(4(iS(y}xq@Ei+)%H zq?)c(4W6)}W0s+-vjsgO-sdeNre|=!X`6IxPID7DH*Yo5F@vI&NtZ@l>s< z_f5`;x~!_J&m1w$d-Ju`O--|!n)1KmXWjW1jeQLw(aTaH>ClbM>(1#5Ib?l@HQ58`h6sZoMA1p`bKBi*->(Z($%>lT&&LxvU zOT>#R33(%yUYjI&BuPk-`edj&KT;h^)JOD2LBOOtP?wJR-LhN8-+)zedPB)%$m^7> z$zm&wZzmUpwAv~3z8S)wPU-#KQfw(d#%yVHc*RJ5zcUzg4)pgA;C?tzTiaG!yXy*- zT2BlIomU8Bp&$K^oWb@Q1Zb;y$vrkUxQ+BNcn)==66b>I3Y{Ivao6Wb!e8q48I-mn zA_gKA$@=`$BUzn+3|l3aw<4LW@VX=`PYZ=27J0{;Ksij!gicZCt?A-Y@NY`1Xm1=H zPc?)E3hYPMKzf|6_FKzygAX2^^`ajm&S18xu8Qi!wed>Hw4kD~IbwCy`Kz0*_xMG> zFqkZg-5=`l^MnA`rv602D%tFkQf~D*DrDOW{yLX6(%e|lG~j3sCKBZ~n`%?bt>02A z`zjT59@GcsF^$K+N#pSmO3^I30r~FkjB_{fn4`6E49u`W>pE$@%ha0vSC=b_GDNk? zv~ZUnS1#c%WU$(QXsi%PcZC~&Pfspq!sx5&idFbz8~%&cxN`ogkm-e`QdKKVzqG6AO)EYV(h)6cgN+z{Ff-!@1d2Y3m7aWCWS1%-+_$*Ovu*8vGcRHRH z-gp>%EZPwduQ9jQW$Qwjf6Pno3W@%;Ez>b2#;RM*Yr+jDv$4udvZ}?hE0q67aIFo( zls<0sx!Xw*jGS@vcacq5CbB7it);*`(d|kzyDkd3v{9Si&!aXHj#M^?vWCh?aaK<1 zvd*xLnN%jjOv=_+UhcE_{T5$&IqG>lkEnorIhjYq#|f2F^NKTCBTvaLw8kv4j0tJ^ zwX`Odv&4=uNm?*fVzNY4>wKwDLY*mHY7RH!uO-~l5}v@I3fw6LpJEV{5p{m$`{L&s zAM{gTR(OSIFx@j^346&?I?tJq?xqYs1&tdzSVjs}Z27`ea9}GGOx>z%wCe`|1OtBGeF_ zR1Y;**LjgATm12KtFQ~drBDkxcjmbWvfURt)z(@5z^qpLRCN%F^fs8LO*1w0a(d{~ z=fntB5DnA?DG%%p^13Q&TLfkvpC4rmiw+3yEtoVd^|EJdT~)rE`(hyQR8@ObRr_?lh>tk5fV0p#086of zfkH?GM-wr>(#HD;d8iSbxtHef$Y62N@2eJ=&eICL5eoi*?K|kw9lpX2ed8qzO&9>f@^@#ykqu+O?>&^A)ZX+0PmCzTN7$~Uc zbjlJ<7uKG&^)s>jdo0jiXI5pOHRKKY+;V4~rJ}XQSQchyMVahe23bDI0(9c-O|zRu zGWbFN5npgCsX9xrtr)RD&+n2uW93ZG7!GvA%&Ow$Wb(&@u52=y1u6WDj3oO(u-_Hr zzQp65dH4u;cr7HtYl-}v(G(bY)Yt(@gvYq0FqNRR%+$O6;PPe*9=bZ~~pQ#9n zda94Nh;y!BHt6#Dd~Tn~Bq8^%pky-P?YW@OAF^AmE_;r~Xu&61yQ2V0~hWgHn+kXoAi@0BWeWns# zJY6R{{f24E^&#=ejmxd>kahX$Pu2A*J&R}P#HTL~D+@yTTmHJ-R^hfT_kBuB=VkDl zhwy|M)L#>KmxSN0sI$h2<-aZgX9Z<*c{C#0u3u#P(SSj3R(ON1D-dfFg04^^=oAoj zxhj}PurB07(5(gw+7QfFgW1RDApi_EBVWn<(H zNB`?Wt3%?wt3#{3jeX1JG&Iav*4HQoz^-mirEUh38_4!nbo_hd#t8l}g2v^;gqqZ~ zGK6Ig8pZO8x#6njjQq02*O&D#m@+rpiWzxNGY7&-XsYZe#hgH%E!2k*Usdy9W=nu? zk{k1;39yW3-hQ3!q;w{4NqM;|q1&7B04)xtfGD#5{L0|XA|ALoxE5>tmc_U7r6t%J z^LS#d!RUY5t)ngGuAMP= zMs4Kw>Z%4`Lth}-5^%?Q9gRv^%i{6%Xhe&0J@VXyI&y27!OVnb)T*CHp|*%XpJ)F# zO2*SShN?DU`MXEL?&{|oyHTsPOF~tFaKPycqLri9RJ4W^wCq>mW36$+si~nRDDDs2 z!};$=8~Yl&67LLExvM?S;5E_Cn^OUQYe;0!%%O%k4HXrUh9*SuM6Bc5T%rKgVUA+S zZA{@7edU(-Bga}7^kR;{A9~poJ@MnnW&Zcv0fT$#4LpzGjWy~WiOX}H+wDZ5nU5!m_m9e^Ev1u zxxd+v9>j3W%qdfiQFx&YN?>(x^4#!HE;%HIi?Su_VeHTmK>y*cfq3Nv6Rv>>@5F3^ z37auArrrr2W~CAs?U({)G^=Tp#+R4V4~(Thv6Na)yb}vIl0BIe-bqE$W#630YUQ!( z^e{A{?x@Ak{i5#CV5j1Sf-3P=_8LkFqd|xrkL3?U-e` z%cn8(u*MWxvX1Vwc-Su2D6zUqOz=)D8_31PwHzzx9SMLe2@vbc+L!>VpUBVypBR)6Z_^ZG#>3hZnaJ&wG& zTxMPOU?wxv-`}s($NbOp9M2O~+6uCbH58^e>q*n`8YjoZQQRZLr^e(eLPxP z2pCaNf48wXpv2c@M^nTZ%jX*~5B5ma?zmf3KV~re+86RGM#b!dydQ}y?0RHw+0Fc1 zU0o+RBTbH&Ff?Lv{8GSPZZjz#wNxp-C%oSIier?+H$k$^%r)oF=`YB;-bSpm;hOP6 z*Pcf2C@u)nm^n?wmpVT$hj}rqU%BVhpuviJ0MRay)wKrAUAN`Gq(6$DOcLT@7C_^O zWKLsAUG9*}6>{gg9@QVt^bbM&tzbax8AxgzDZ|Z%H6wUV4*lK_jZzHji6-K@g^cCa zmgvq2d2(WzZh`*9R8)1LOKGjkEnHF)mK33*(CBBA;6f*XWLCaifTci556-j^(zVx{ z^m>Q%)z4zxSzsF=p9NpDG6bG7{rz6q#c>cEk$9;zw_`|mD##|B{}!%Up$#omB)S<3 zd025`s$re!Ow8Aij?I=X^n|WiSSO7wiv)%0{GLpv!W*cQYWib6eVM*#(f%5#F5s=m zWS}4n>6}&!*M&SamSFN(lhqb>)nqc&=?1^QAzhux)VRVntLa%N9&0?Ix&)Wer7%y| z#%MeEt5;j8GAmED>*}e}<80C0BCt$_`Wm@bMKxflLH@;RizHc~{W`{X;7HV9FquWH z>Gnj7A4IfedYRZ?HofHmo84CW8=7e}8x6);9``JR0gp3-H+OfhtkBw<*N!_eJ2#-E zyCGrp(E=Jkuk4Jcc(v@)=V4v+0>=UiOPekA@f2if2-4AR3&vc^?D1PtK7WfyM|n~0 zkFulB!|JnWJvRWB7^rFt^g-i%v%%*!PFuZk^|WcLr@Pz^gCtifOWiTUV5M@S@@}(v zzR{STExW?4t+O4iVb|N%7>#R;hH$H6Hja7m^qF%7{T@j+N^Tc4)*2~eWsODhIUAd5 zo$jwRHZ>jaH~6ugl1SFr;@=L%Z8amB!xwGwP-9+GYGm{|=YseW%3p&yAKGQ!d1H>q z`&wNotQo3Bt1#Eb?P9iwV+hW`rWd$2T+&76aV{Y#Zfn%#Nk&4~85*vM-Wa{6!LZrt zR;1dB=@k(O>;7VYNw}4T)|sx>iu&sU8(S;h>~y}lqV+}VLdE?(S?;!4-#D%x|4w`c z`AX4jo(iVOi6i#x?`i(3m;2)t2`8g2fx!~n1{ga z0_b6L>2}OX`t+kuJZX%)TI|rUIyOB{c)+faPo(N0ax*7~+E(~|sdRms=g@W(I2{$v zsM(@5mf7E(Zs#Btt^xGe5pWNuqCCSFr?M{S)K%^R6I(v}O{2lp(de_84H%TpaGgVl zxXq5@DLci$$J{o&2obWIohnqJN+4`Bj*cl6eZ@^h<*oS~Pm3ahnJ@oYXGjE8#q5%V z!|D_j)eb3C)l-+xiA>v|T@s;6o6};DozN+`%EO-0T%7<%;CUcS=!|KwqI8k{m%7)O zwgx&JUcI&oR_kD61A8$VgR4f#d5(xTN25-wBsnGNy-i(RH+Ob6S4JY~NaRtGygpf3 z?RPtUQJ;v4#_N(j6&{Dbu0p(Kr(1SNl0&ZV;c?UxhR}?^O_epZRsK-9sm!K0oNB11 zCe$RGO=fau1Z;w{Dm6c&`RibuZG}2}ZBeprVLmUlty8LM>W*8 z7&>PeuOQeN&9NklMPaMaya z<#MTZo713{VRn$qOhyR3+|aDbW^r69iE+G;qdJ~7fjGcIqon{y#@ai(i~a%pWXJs# zAH4$!{0^|Zr(r*)o5IwXgV@OFfqS@XyfuDo>|!La;EE08jL7D*UNMm0e~-zWHJhMP z@a4~qn#&@QGOU(0vAfWBj4ZOd5xWjM!)!DW?ZUP$HfZ+ePbRQfz%DSJ`jaQId#q0b z`clhUSjwRDVC90V2@iVwINp+2`y~6te!h+F-!F#y@sP;zb>05)bh!S;=Rh;rDCJ-@ zKYFs5h0%eNCpizr<-WfBD%LkoNKs8LL_WBtvHjVFJ)2ZAmm3}$5?aDBHT2n+J(rYepXPTP^)gktT2XEM!d@wwi+<8NuFN6F(C<-o` zZ(eiC=QMBBx1OJ;G*KM2?9?mfT;iVaQ%{BO5kpt#ZhpF(+h$VOLM4t4Ea-gdsm=vr zD7PT>)KjSi4$>ST-z#m=?M9> zqgwd9aV(^YqG_ zw0@Z`_Rnevj_eZeD)!G8!h?&3weC5*=)hodc+nug4?czd3J1;efabDJ^DOqa5%ESI zQ`mUd2dfw<_Q}ygFGcYSeX|s{FtEi)p--MGbkfOvv#vCWqA!^+q`I)6oYXV4ZkU_Y zm$-j%x>xnh*|0`~pr0O3*a8h+NohO$Py1f(pLg7=`(-J;SL&CXUhe8%sU;*8d*kG8 zIia4=FDKL!RDF}{2%T_J4WXw_tRDxI6cJE@j;^Gyi|gBS`tHyaWGD8#NhwV1v`9?n zPy6)#lJ?f1ql0F@D6p_ncbOC}RR}k^t4yc^@Ub*RtrEwn(^U}3iA+SWc4l%mwXSki zKdHkMf$nA*Pc&U(VCXGk=>Myul=|j)9;T=HPk0!o`AVJTg;(kfSp5|G3F?A16nby*m-Y{#zrUK~tLr9(eo~l&>xvAkFjrr%Pt|%KDTJAgtwC*y z9pY*HSJ(5L{;OhtdrIIFz3A6bXHkeKuq9~ouAVy!VT%GsvcQ+TuP}F-bO~SYc1G}6 zOnj5)gamgm?-U(z;`l4fozO&pEkg&WEgf{iaec!?XHoxZ?i9&?+ApW-cZD=f|6=YG z$*b9vmf9=Mp5tX*t?x~!FO2@lbp@_8siySla~vNp^gmOn^PwZA$M1MeL3Sc5nv}xC z-ii9*@iK}n&z;yQq`^N8L+m}tZ212=o%o!Xr@uDv1A>bpkO@S5swFNdN5fX z4@7Rtpl@Lzb;q_l?wC6?bklg90!k$Gio*NNpPi>9MrU1&?A` zi8TNgnc7Yg_RO&fV*UtMw70tpn*W87UO7Q-cV4$V1(cWio=v(;m%b9hVyYJ3?Ve#Voa*0GP|8g?9 z?w)(b>Xpq698eMxDl_iY)DC`Dcg({21X@y<6n0tsU789)P=7zP0ikMp?Tjz-C91s( z^yGmIJlYf0l@aW=)>T(k<5qO#v~>BB&8o|>*~?}!RI*caf&+#PoD_y6bg-BV(3;~c zd;EHb?&z*7nl_%(_xuvE88zD8c+JM}`(oH0*DZUpx~}mG>GyutQ7Ve4?Qb+UX6m$E z%*L+9w}#&*Dweer_tbN`CX?0i&}eg>F@K>}p3~M*9_VWz$rlXP3%y;_ZKvZ=SRe2& zYlg6PP({6XRNq#~pTp*bG7Qb3x;ni07Aw@Lo?u17Wi!t#lY#*;#P(QCk^Fvr_rlkI zOS!FFw&2h48988;ih0P1>rrpK`9=C-&Qjy;0?UW`xofCD{O)&$`$hlg=;+7*KMy2^ zd6Nm9RvSCG2-Le;;7c&ZC>gFBv6qgW1JpPywMO$fukzNnDqc+dh_IETUwTH``H&i+O9YYu%^-->gb^lOfDlvhLXwtojb2kCbttm#cjQlB7+tmuMgt2bM%#fEktbVKszzu zg^~jIOurX3$J=^SqoDsoTTIl0L^SpM4CMY5QbUDm>Ul40|1^UpJ{+U(bebT-F!Sxg zwqnNTgYLiI=VNm&pD(`?+hph!*xT&SkFbG?7}Z=}DT~Pg@WTYGRt5&3O&ZW1G$-c! z;D`5!Ptz*&KQ=pp9v*8!ZQtqY7wG7DC#5@$P|){jW5WYOQ0T9r?8EPrFHw-I-z}9H zqOgj#R5|>ZB9lzXlCJj9E2yq;$*1b zSskjK7{Yc3R}WaXRzYSlQm!cFJ|AE6`^TPj7cro@42SBh=CIpaX|~plfI(&T4FGVu z(qpEs!ikL;UmzkW&Wbu+@1~ng)gqTEY#UxjOE7e>1!PG9suyv<*zm zpojI>@odiAEv4nvZ%#uW3Uxi26T5b;q8nCMuqzhnZuA3jDt}ApE=wTt*k`c26$)Bf zs-OYyYcp7F4!g0n#_edyzZi^0JEGBV1fzz9*4y6~`TQHB?IK_Zd;H~tx7m%3^7dA% zqpZ^=J{h{p8HvodluJ@&Jp_Q}S~E1iHO=MCGh?Y}G!=^l%gYxS_eHuoVwQ5hx60@@ z^bf|%%Z(0)(NeyZn?>i_y!GeTHEk$GtAWK6Xqc-}K%du+ZO15N@6t~0-Ll8rV^FNO zzs~HltiIii?5I!3Jpms!#lnX~6{JDq6ZiBU_lj`CO8&LrA%=>i6CCx((J6t+sddMXhfbpt4Vr-tv3}xOD&Bk~Uxr=vg7?6)5uF!uoKI&ID?kAI!^lvMp%DPT=Cf$qO?Qj^ErPL!fB5nz*nX|oj{XivqT zc+06k+ndNHafHq`YLn&akrA#Ou3*^)o(pHuZ%#|O3HxMqkkieUDCAS^T(LJ=*r-S6yPC~`gKDVL7C;J$=sqn#X3b$yMHvjPvVG)&W)SO$gtl-lMk*kJUI^DA#qT zyfu8fA>Cz$h+1ogQeD8J22yDi4m!dd3r;W?(ex7{vtVI8XFc|W;<6ubTh>=q5E3Q|2(CkLa{a{HkaWn@}yEH z^^&}J>#Z-E=T2Q%O+Qp97pLFBYpPe52PiRM6jM~t{PP?QRW*KTT63A%j0*lA?HVTD0H-A#$Lr1snb}MQ~$aL z3J-RIs=~6C51m!$R&AnuKbXJBCOa%6P}vUaN74rLrEX>f3Mwp9VjUx}vM=`8QO%@R z6PvVS+~5!;E`f9VI4gxQB5+hEIinTqsddIWT$4UuBHOVZtX^3NunYhYb{otA{#wG@`e!Mf>4 zqps!-*ihg1`chgZIsb6bg;PO#I=qj^B3hJahGyu79ZpVdLK*lhI@D^g6dD~s=Fg!l z)7$XSHa(#oMdaOtnOBu48*Zl{ckn zeWArHl+&o&?$^h~kY;Q>>4vr1I)*=7x!#H-(R~zz{nf$JXhjHas2< zg_E)T;w0|1u~%qOu{j)?7jM1kk@&n&xaKBw8dE6`dcCt=lB zAf54Ogi)}U4=BN0FRfXQV>hKrU`wg1^V=QWt;tkPMMGkGTWwh=)*Ps)N{6q%N4&PB zJ6T`RQZ6jbu|(GsjmD_2sk-v8v$6u`7K-B5h?Z7F_*4eAVaW{WYqn+3seT%q*{kg=WaxU7si?q;W8{=} z;8`aE{S_M`;$RAnFyONeY4?;tOJ7wP;^X(pWOd<)FvirOoBJ9~POHPVL~_lqZ|iul znvdW&Ap|_NL&Uc_{Qii~C$aOa9tsVcW-(3^WKw>-vutjJSbi>%NXk~o9Lav!>cxQ+tK6Sd;2?W5A~U>` z1*M_fuvN(=^TS!iI-ElYSzND)>iV%5-WTKpF~y@5ofzHdRAWbQaBK<{UUZ8Vy;x5V zYduX*hMYvfY?v*x-p!TCXV>wav5s}@nVmS4l)&Uy39HyNqTsBN-o7*;;+* zOTPuQly0U$dIR#~(X^cwuh!LJca4r-58i@33J90H_cUAZ4x&Y4glXtN)4CNdj}A7Q z2mgmbvL$S&gQYMnq@}LP0z*&P>=8LtD(0t~Xh0ra7I{YOJREZgZ?n z2wP&NQPlR-I%8FyfYV;xQxUwjMnCmpHPnPe^pT3Fp)&SH zYgywoqXBfLmB%^^B3w}s7KV;k`Lz17@-pMJ#xiT|w5X_>GsB@aS9s(0YKPq(_f|Bk zju~^XaWL&xr_;ZYKgLnvZV$J~)~e>^a$DHa3`vru?U; ztvd9-)6C`5h0P*-rgAY|SWF+iIq-G3)^~-W*iLa?6X@X=9 z;g!z}vQf9lmYKZGUTln*njda{*!?-Dv+Og>;P~K%$Yvg9-!fZS$P~{uKg`{U#ekQ^ z{g^WOgeDv6S#W+4b_V!Jgm$bLDaFOihK7ba?)R<^-+OO(wfFv2YZ{h)8BX_o{C3Ud z$@{QZSKKD$Wt~nd1o3=IdTH&8cio*ho7@=}Zcth?j`(LoUT?5wd%Mkcbuw?RjIWAU zblx|sBiMnXt;IB1v^8RSw(8oOIvC{H#nt7AvW4I45nj9e!rM?AzUF>!;>qx1Tu-ao!#f*9Uqdu}&3!}3^_t_8 zx3q_?%NjLNB8Pg<73zw$PHbR4MeVb#1cyH@!p1|+$8ruiALHK zUj5|#tMo4uf?2T`s+;sEnvrmYW`Kd#g_2X-kOzPCrg0`xUpA$2G#)I|E2ghnRcaP* zLj$-cHhb}dRa51vRrhg9DCU#PmNh=pxa`N+oW4Q>Fy`Si$5;L$UJ#F>zI5|*R&30o zdD`T-nucg2JwT7pdubPaoc7Zf=`ekpen2mxW{d09(q38VAzt(RwTNdc93w1rk)B0P#&QE#67=Htr8p)5Z|d z(6sukikUi3a;VZm7bpbi*R^y5dNmcQexY8I1A3+1jgOO_Db$}HdZ8|l_3MrKm*l8k zt%%Jj4C-wKiGJ$gtJmdbdM76g^GP^6ID1{YxsP(nD#em0V24z+*$iUkHT* z=6?PrDuh74E<^EB5N2?qG%TS7(T#dawOx8BHP6*9nwbQr>n%J%rz_G$Qke;3VUgY# zg+WvpAR?`Y`2#nEqCBfl^n2BGW3=iv?dlyEAyZHsXiE&O=z=1(>Q|J&{gA0dUPh*eXZVUC6DU6$!b;kvIV@eZ({9~Emyjcxpy#`m%?T~u$j!t7J zTNVc=D1CdSQvAK>4e#P7&@LFd^TwT zEr-R<9rPx;m$LM3jJd0tdh*9V#=f~x>K|j>qS!tquTl$_!i;$ple*ePu*#JyLa*&s z$nu4AIxQ0qWL#6=qWZ0`&?{<9nW=<2#z9aF0dOD_yTirX*BL5Dxx4eYa0v5EN^GB8n|c>t$j=7Fl_T!1w8&c zD*1E!_wQ$s357maI<=__XHXch(!dK8ztuEG-wUvzL?LI{6FIxgA56{Nh+C~I(sjAB-EM+3Cl;hI}PG>m| ztSb5MmSK%8{Y7=z{whKG*V~BOxlFTh#F<}9-K46_4t3+K8rMI!2d-jhBoe_FO~y`# z7y9K+Eb(oqN3D;f{cN8zbP{&Z!&r-;Hb!vTvS0sb71BjGJv{7Awav}Ag*Anctg+FB ztxJyP9C&B=4L#xUvCqA;dTmd*P?sed5JAV&(D?(~X~Sx}Lk#Pja=`~9w)QF8Qo=)bU z(SbU_4-;Li3oKB@>$M*2?5s|us^6i&9t&E0L*+wkI$3`R0yWsL zU9;dF`|+B@XMJUS&Phpsc(|#LA5g^HI}XQ4{Kg7mfUGL^-*Ka6J|4fftrqc)0PG ze8jNE4^cc66}6x>&WQE~D;=#GP)P1V+Lgm-KM9mMksL*-6a1Zge&ru%j1#DtE37Zm zg!NM2(2XqgPr~ts`t|nIPQyb#%X#J8VhySp zp*Y2Hq;)Ya^M1_;hoATd-A(Ig{9GugHYT|Nxl|}+vM|VasBvMr&{?#ZzB4e9|7gZM z+)sF{^H&fA!2E&x66ZXpWeL0oUp3JBJ4|nQuqP6uT$8#hXNpsJmHmSICYPO4;x^~H z^P?|ty)cS($6>h6*p0*SJ3n*$bDR~8-)LO{odPvDf;{2}*&5MYi(cB`D|7^KG=9n^ z!OPIkb>4(J)XM?Of+=_`e*}z}lW=a>#E0+29MwT8!+{65a9biHJhXJSsP$B|R_TYU z4pI=Dkld>pOU%PLRjwN@Kc`*?KE}Bz)(iV_oif;s!x#P7t&qR{IX{+dO##rAt?51t3Y`pv(uX)-b-D6Rw@Smy**p3t9a;+Y21EPHQ!w z=LM;s6Ek5g(1dm5ZIG1Od5!OF*E9jVJFIV-)yLbh699Rq-CNio!#0^WIxeuOosSG4 z0!*Vg8tf?A)A4rRK){y*{n!tIKfXycKY~+-&5^q9`e0I)lR=nlNOmbuU3lw$UiTN7 zvT~EjYHDo2K@i2*+G=i~Sj zHN14|{IJb%&(p9Hm7~g^HxK>MtgRBiX|s1c4VSQ@ z>C@ZVrzIEn*3|Sa##f4}@Kv~~%7V%PWz^%d)zuX7b2Zi1E?<6abxlDdB8IKr4Ucc| zTF2t^y6^|$PvQ&csk}|Wd)U|r!${P2%CJF)U0bgOd+s=b+=Z`>R0eAr8*74Fo`$S_4{Q99Y5r>5_$2Usemau|(%`JCBvYf^8zK$n?Cw0_llV1!;6AkmNq9=a;QP>QKo=HPi$}*VT1{ z{a*g3L*q&?u6~49{}kisSV1P#lHOnSbh#Wj|6Lc4^0&revN67jy3(dpT(sk0ZsVN9 zoMq#Z$p5t?I0VCn-0pGJuP!du(PeR1Jc9KK^uSoB;A?Pk@J}{6z`0Fq8Gkf2<UYcSZok@gcY{@S+r)00*v2J7Am9hs83f7jCO~i%n-IV_36LswU?u@J zBtztElAQ^eOp=|d?2^e2kRMBaUCC~S2?_aaM!o<4Irr*ORc(_@b?fTt>gwKe&-u=G zzQ_OjzNR&H*Wq?+gBp=fj#d}u14L*bHw@KZHlobmUZ?wdn-@t+Pk6I^bv&IOM?!I; zhY<>fnqg~bM`ppl6_}Uu0?|Hnu`yvU_i?Nky#`B=B=?=eMyEFh(e z4ue(muyg|!@*mXOeP{yuZVMgi4J4P!pD8cxwD**QLT!`TM43%W0<9eImJ`{XakGE|%}(u{#U6!Y1M>LTpG&ECfFr?r;QTbPsK zi44B%@5lsFa*ZGo_sh^^3y@Q6z^hB4X@BwHW_1kF2Rxz6D z^Y^_*G^hDob}jVt(?^X&uahv2PG4>6$q897BiXyA!nr5Jmz=di#O027y{SuL&CK zUlp!#rYMlf_;3@NNlyuoidv`@Wx_x$t<+4t$vhG?CTdetTKX-9p`X?bBbh@1DH)s? zH*%hKwVf%Yi~jk!z?6S}-d{|Y1R56bojKzR#L_$X5q}eWgmm&qaJyD3WePyU<5Zju zXncX(!-m$f-CPT;l**&pu#`18ORZQEBFTt>~HLHPN@yGE~*iOt<1S0OI9%# z1oHM#d&Qrr*-I(jlC#j4FJT-VCMW>2jY-?&C=w828J#h_h$t92Av z95nkD<8^b!x!SchJ7&1ypO;@K-9^#33zEzcTN8JZS^67=0ixJtIToxDqX0~(sRU#p zRhVb98;neZ6)CLrHxui<^7-~ z3B3T`NBTirSk?eW-VrqpZEr_1ZD_E)4f;JeTanBg*$Oc=Wibe>3z$S~3>1Bu=R2)^ z_bvcD_Okb8f={F2vRZK1-Rln>GbV5Aeamdaq4)p%L&uYox9!PBSZlUL)8I~gdX*%2 zbUoydQNY$E<>28#+t(C@^aqCEO#H81y9V_BFqPD5@8=hIz)^bS{a%J19hSWtkQhbr zqg46Lm6fFYZ>+2wLYuvvl@)xW{;RR>&jn3d+909O`t&k0g0r}`xw(FDN}Sx?-KaJX z&WH`FWFybXdm;HPa&5lMotW&>cJMBH?bg9t;QG)Tu(7gDVrT$yT&{ON_=DUJs-jiJ z+Xvx&vv|sTQG|suf~`dF0hU0Bq>w?|x!ZH^yzrJ>=fc%s?zUX_={M&(@40GT8>B0J zM8q7S7wYi$zxd+Qk8C3>Mbut=y0-nuM%QWw;_Lt^8p@8jjbsT&@MtqA|(efTr<2J*>Npd zr;qtANw8v_A|-^o4dAlV(M~KqzIZEq$H<#J30EpBB$bf)2Y-O!Z6G|%lHqWA&&;l{ zs|q6N@VQjmH3-bmSMUTZJgW2+mACVZ*yoM|f@FKUg&MU|D#X%~Zl621b?zMM3k-a8 zyp&q631M}DcS_5Ep(8H=P#0=ZF&Yl|3v>)IU3Hzv$G0zKYCb=B%86>G+WW`WLg4mR zTIe4Si)N7Jy&U>_+B)ctLQ2KBy;?D z1}`#9={o1y5KY&5-AJxdtK=dwm_R>~89Q-e4DL!7)+>N2;kOEcQp#8}7TFj2pct$k zHydW9Ug!g#;+SceanNfj8@q{U2KnMM(F9$kN;rB>Sqo3Cch}dMZ}*!>B%-;IKRFl` z{$X~$O{3ye8r6;9`cyiOYS%W}MT=P$EkpcZ9Khk>RRq$wG@GrGSHX!aYai-S=>lt1 zI?`vY=El#ek#Sd{3BY0iD-9Y1dAPSGC8;eGvr;3|hEKNl{ZKlD|H4m0v1*T{?=gE< z03LHfUb(i*nw^b`3+QmvrX#N3fa^-4`a#-At}S8WK% z%8uL_rj;JtA|{cL&mmWnNh4Sj5738Yr-@_10hNwWgd$GIK}s;IMAECN#@^!3o3L+y zo0hPARY-A2NOefRHaizCbS~sad-2KD)e9H!f+u2_GYs=;>Q*Sr8mEqpd|y;L4J5dN zUROa?2$dujdS*_iLjxCKhnNH87P9evsNO z%WB`X+8{bD89-#Q^e0LSu2dSZ71bM9 z6lrK~>|m(e7i%efKBzm-#8!Xql6x)}K}FtTx+TFd)p+94uXyeX2TrHPUUO+S;JYjR z<+b};e@$j#Uz4v2K;|2peA~HtbzM0s)OLq@WM_L|d)St=4C>tz9ciVrR5Jt7R% z3CLf^zL-@#^Wn+UX_SRuT8N?O`eZSk3r+@e>5Z$uKhi-XokN)FSH@DgSUip&jkU($ zn8?983C>8}=Oyul{urKtpZURm&PWo+TS0&jk_KRO6>mda{@^peT{J)bki*{FLDde_ z^7gvD@n?c@4`NI`KDUSaIPCShJ+bYpSKA6YtF}zp;p=Qc!1L-v`Fgjy@r)7hvQP|q zmE~g0-N8|cyO>6=!8AgwVu^g0l-hsigsGEV(_m9sK^STo4l5)+qit=R!m^ znkVXWXd!7?TP(?%8mk@&qf%lbVR{}+bO!KR>=J7_3Zk&%q1uCkVw1qQ-Or*P6z4`{Z8UIT7nZyJ`O$N#X1UTr)v(UC?aQq z8%TI~<9SL^MaZaHN~%3v*eq5DR=~9%UcC^ee^^;NsX3!&07)z zz+T6lu0-HdapAE4J*z0X#_wX8OrG(_Cwt#p8ni9wb-Lf@3lvfJ2V$b%TYs@fu7>~6 zrOf5+n)qKKR{JHek;Xv@T_HyRu*T|^Woris&1Jta&J8CFcj+FrR|Hb1rv8p<&$r{B z^e0@eo_h7>m0N3f<(#TVQKzm6_5p9OYK+>RvzPq7jpSyx`r=B!%Py5Hh3?)C!~&Y5 zRS6KJ(N@+$U<`?iu2=Y(b~$P1q6^vuo-*VwgdB_$QH=y&LANW4?Rcu@@FG8UDvn)j z>5his7?IgZ8Ik2FkQ)H`AzQ7DR9p{u5Lq2J+NpRvwE;=XR&DK(D6tZN|6vK-Zb}Q7 zDav$3Q?d*gP*{Qj;7BFbRnRUSEkUsMGNMcCO|n+j%Z3nr38H^`mm8)>>?-L^hgF-0 zu#vU7q*}!#g^E=XW~7q{ zlaPS0hB_H4e?hX}u3cr;MyU+~dlwZ$%`1fQExMjeqR@M{wzmgcPn*H0u=uY_3mG>j z-Od{4dd=ys9(J(R_Uk@NmEG(2-{dRwpbPm=^g>(qqtaI|@zy)sz>tI%bq?>&sw(`U zL?N1>u(iZL+8rP~8mLohj&&QU>&J7AYO>YLNE>sr2}q!7p%jNy8(1H< zXq%^~eaOvRR&zwDphFK>E?Ec6_H~DE-Md+}4fPLq&pecPrq>y2BqZIT7HpWUY!>wg z!7hfHN|OQB7>)y#HX4HlR%@7I4Ahwb2vgB)(VXp=_ndrCaA1>eUc+*-E?V9|H2|>$ zf5>=+Z!wot5VWv`{pGSBwiP)b;y(eptWuIZhF;JJRp#<*K>ENN{e3jSAonDZ%Y>EU zg_Drgv9T&E3-TmuVuQV?84h=uat@+&7*?9fqO{!7odQ-x@3JvgDY!L<%i#z)ybhO` zag`he-$bldePgv2oA4DJCD)8_;k%FnXKC(2Wz2XDBou@KyjQ*5Sq($h3B{mt`V($< z0t=8o7J^VHbbB_Q^M@lwYJA)k)Oc!88?F+>7b0LdD90>RVZ)vxYGgj6nTsyGC zNL{#cxDa!73&h>Gpv2)iPRCzTZowGwr!=D-q<~cYNIyR8L{=1nWsBhQC6L4s3Vb@o z&2MgPb&$Nmw4l?P-s_{OZ_uBRAQ0UE{QHgjv-XyWn}7FrZ-$C#T5jw%SnD{xdDSj* zJ>`Q{9xhs;c?4V$4aBOLM8q<}mqC%ehsk|mk`cAt)@C)}-QhRTLFfqCYc_cgYSMp! zdwc@Ec^&td!jzMDFhB-?kC`_yXw*}8K_y^dfW;neQ)|sPo$^NC`OYi~`PIbRmNKE< zZ^Bd)@Ls9?_-0eyuJj#oTc#2S?=_b);;dSZmDS}P(5U%rie3(-VD>xZ1HMN3zXGh9 z#C5{@+`=$yONQUVFi<1nU6hu&3)Y^HPsWO6u)mt!r$R)em#|1W2Pebmz}t`KCNc8W zBJpfPBr@L_s)`g1?KX5QEN$u7PAt9NY_7{4Xk2%o5^1e=A)miP@;~&iVxPO>8Rd6F zPF)hWA=P;dCAe0DIcNbisD}j%+dJ!btQ0OSBz`@4t$Zxerw! z*0u?5-SWDBUvqf8pKYVSmGZ`1mxx>qe(Q1C7)#KdnJu7##NYt2WTfRHGocF?ItGtMv8awmWO+5)u-O~ zK*hVXQhTN~QT_r$S7_E+tu+c6ot@5Ft226i=}%z0Afm95qQz!CCS((T_ZRe+ zE`1RTc<=YW4(cRN<9yb@A;D$iQCdm`_~JYk-P88Q-s*izOZQ#vx>oL4Ub?4_f^NMv z*NF$;@Zbqocln-`@5nFu?Yx~->6QL4(Phz~d%rCpaPyt_LjV1PxE*`4Mj2N&)B=G` z4=5bN`*iMU@vGmuGVzSCy*2lqi5Kztz2ClqgSOmz$bVJ;tB56=09Q~pcu1QJlQGLS z$S5TlanX0-ntfwFCu3#$#c*Wt>d~mSgGe%q#y4TFq}6H|Wi?tM+6Ft&;jwc1jpN$P zHOh*_I`CwK!Hhf|x^?Cu zKkP`*bQ+4$D`Pr0i*xvzAse%2<#gCN1`jQnz_A7aFamM{gZ)lSLMq&D6K zB(nn!r=--{U&&uHyus>=t%SPZNc$!3)T%tma4V|Qj(!jxg1Rgp67)DO&sD7E%6C8s zgYkC&tguf8bB}8TR5nx$j22AUAINQ8eV^*9 z4owQu(*m2{*Uo-$V(aSP4fPq;z>SG(d7n9)Ros=gJn# zK7wVM+g6KwOl&3$cCyaN-6QDs9h9!s1K%J|2rW zR-&$z@N~=wWtMxNh|YvpT+tN=k^-%ag#?XO_}>&0_GmCWHJiN^Wj+d#_lc=&Flra7 z*t1O6a?;`WFTPSD7twP;r!TZTAAHW|4Ca#IT%zO)2Ip7mpc7bQVu5&M+&2}dKb_EH zn$`#u>VYZWc*LAP9q?%vWnM;JH74w=v?LzMbvn66)E2c-_#r2^q-Vu{-Yc5s?>NgA z?3`-iy+fyp$)Sr3ez$4;4iy@;${9QF`t%KdrtZ~&m0PxvyiQxN^Ov6-x_V>i_jnNA z3|4tBeq)Qvh5M88S%g=<$*<++cG(t48Q>CzxLtnx;J$O#)H#?sxa(JjPqFTkrHzcp*_|kVi}PiX>TS`$7U<;--`Z6Yh7*>mxdCsd2VjTcCoi9R&sajq~+9 zWx;&?e0fW0n_GTmaK&@bGOltZhOr)`q298x3o<5}S(a42dM~k^#;Q?R0d#y!Pvm(1+F+;=^q znXd-XN8Iqw+<}rFuq-n41nCSg^*OE_lrxYQ zP0eeuaYZXk7qlzZ0+Rl$(vne)C6z*!gTZ>LV$@CbpVy}@I$@$hB^nZyX$%y#w65K^ z+btj32BJb!iTwEdcs`Lr8d;j`%);=W+~25LE)E1Dtn$HV{M&?8)XQch{Qg!BwNKTu zfjOk_btJTa9UectK{oa10omXCn;^{08(k!R6Z(PMnsOogg$R@qMjOBuFz|kN+@Ke1 z3{+sAJ6I?VJU8P9c2vh7yrZdmO2nXbPvweyrF*toEZ28~dv==b^(In7FeAYsH+4uF zx9E$#duV6&DGVwh;>QpqemG6~!%Rr|aF zuOK|PNGYJc#NrMvp$0_RB&KL6HZ13ah@l6}tlT3nFb|W52I->4!B|{YZLP^)F z<@P7Wr>Dm+@cRo1TsS;lO8kU8XL>^O`{Eh5C@d@#ggX=W`LzkR5Q%Iyfe0_O56(Dl zI98^N6#yUP3I*@EN^&cm<}zPkQPu)OA#|2UMsfp7BFDR_5>$^raty+G{c7ZmhiN~@ z9y!hv*WY+7bFM=g1p%a%qbDGHd*<9_oOb^5xtaOwk)sPloF=kIveS1@XLBd7 z!9mn&ZOgS2_a{<5fR%Fs2$e+7H`9i~2M z?;=wBi{Pbk@KTtGVC4iS(e^>GB)HdZjp8_^l=?!hRc=?Fvx<66pPNB9hKb0PZ|Fwx zguN`xq- zYieE+a|tndgX!VS|0rNS_|TV4Ir;um)v)^ZBe~#=v&;+l7neifu24( zw+6ie=LI;)F@)z~^X{Qb;5pSne6v1u+!rl@D&M zWeUvHg22E@JYRg{8((G!wkEd}gh*cywAnccglgck$G{XPk}FG$0S=oaXOcq#|wqyci*Sp+NF9)Ow=-Cj+xW7&fqw|<{Y!0{|QD^_MabF zF{O14_Kx9x2;4k7nQOIjla1R4y-_>i{(ON)3!nGA`k2)(iVvxNElH>B!9FQL9W&t= zuy0CHt1;T&T<|F?<Xp)AAgSZ{$D~mP2+i1871ke zU@l@b0N4R<6{Tfp&gQ)gI3VEJlhI=ATYT&fd_{cb=wB?JU!Hc@#eF&Pp5FiVzkFT@ zw&5M$obYrhPGyWcLwBBbxH&o$X^4Kak70qMS9S?=3C% zHulu$C1ZcRwbhpq(KIYq3vuC-Ra102xcS{XMj!B(hh zoJMCnG~;Nac)?dI&F#`G(1d5NQ*E^n5WR3+RwEvW^_#YK_L=Yiwl( zIi&PDzwp~#A!ub5c{tA zf58rS?i`-e8WS-ok32VeHQ1W*bi5Ynov40B&dIq{IrO?hXG`BHujLzDeH%@^>yR6$ ze(blES;P>TfaSxYA^HH!rpd;dbaGf@U%sudjqdLQ)z;JKhukCk+bR!sM>^x5H+I0n zY~Zh58wyyAb!uzqkhKn=QiFw;U2V-Nj-6l16-8DQ}KmM+f%Tv~5L^kCyARvGWg$*A}dj%e9nM0L&JQB}8lgeaLMLlUG z{zEZg7>Oc2i_X-eZ}`ztGI#%v-k(dFZ#eWQ+H8#>`2bdo{ak1d?T{$r}c4DQlidO;qGZ}>9^p${^5O&vPr}MJI{VMF(?U(Ex zpp4jlMMIRkIcLEV0K6?oyvqRGZh}7O0q-EX2XSCaQ=Q@xK@c?<0CuL9mZqAr!iBuv z_3w&*3m!n5D6GN_1Qw$!D-AGK9d=q%l(!_)rl^&2SFYqrHHoRHEfv>r$>qV@AyBLWqAwY`s{eyjQZy*l@T@$m!5bbpIofkC^z%is}iAu zHG=|XH-4ao-r!;eqJe21hWUY6{$M+NXP-xY#_rIVBp zxdxk`oEcJSFIXG_GlPT#8&XATN|ETG=c*TLJqAibmqZ+H#DBL2{ikYxJzva(pb;h^@=#YUBRHTKOZfGy6R{btJW zBB~_h@n%gptsC~(&X8?uA1`|)Isqi7&R{Zj z&%q{KMViXpt*xDdcNXhb62wuuRJ8Jnl!B}p15LeQ?!nudB+>=_4x|El ziX+Mw4F#)0Wm&S!BJx%#od)y*?MrN|;}5IhpQtyeZjc0Oy}b^kcxp_&F83&IBU#Px zcsgVN4uY#-ed?pIfQsv9j28=Excr6AD9Q`9vx13W@FSPMaJe;rUlE`6H6BmMyiNle zXI$PmeU4}BT0cQzWq;YJzE5D|{7250L9r;K>DhPRfpNC=k@Fup##18x-yUrOVliK1 zqVObNVg5ZzgGAxxAzxu>->+lEvD_^nFe-_tC;m2XKxzveEGJpr7;aEgh3{Oq>4`|lt7+a)B+>4$ zZJfiDG!+UQK7_&{c>0Q5r6K-^jceDgQBdUORT7#d;B^2Ji#b$4R1j|^I9!bKRHun; zfD4GKLA@V#+TU?dBjUq17zIZWk3e0Gwks*!A|4jV6C}K@ZR{X!2Spfu8aITP2fGE| zPHHk0Tr9e@6phKeaN5#FlgN%u{p+d(!wWob3YApVyQhENQvbIbG-$~+RrN`PE4j1} zN%@wr**cUQ7&agVDr>Wu-U#*@Y&jrg{e1Kjaf`XuOjc z6_~V)fAoZ^%pSqNQ_1Ao3%VV3N?`*aEA1gd89(F^Kh**JVUKMQ?fC0(pTuE zW#$2Z)|qqwAp!k+Hint9jM1rk$AS}9+ig8#d@psn2xbYZ z8&7&+ltcD*^JoN=GLt4IasL&_Ago7X8+b+~2n;q0z7KDUn#H>MJN@(#a;Lb6@m*Y5 z?SCz72ohVl?{)XBI2@>iSo6DFd?kHuS6zivFR8a@RGc;9h~q)LUZ;+Vj{GgI5e;j0 zyB>7glR=lGhF4R8-OTblNfjAbm6niTys5$l@tB}dJQ?%C(6iY>)s=%84uF%7M~g{q z5z$p#4jKrzs)H+Oz=3XQn3Ird z0DT&K`9k5u>X^`{rX2o2+~e{fU#=JD3E^pg_fyydF5`dvr0Qf6b8{bsC{+xPaCLpk~hsf-L z!3QPsVQkD1pDJo|ac|re^w{m8$T%!UE?;UTIQ*p=G7_2%f zZ)h%o_eAcJSs8|5q+a)i;+AT=VN?wK&l;1pF_&*5rMc}WjvUleal_{bgbm&2(~WSz z;WOeXJ%~cecDI(A@VUlnlSWvrGu1$9f`*-mRNya7mP?YOA&MqP7*+sI#J^E%w30U{ zb0~R3k`8KPAmhRI@Ze+Ilt@L#n7U>jSA~MhG z*QF?^BbWU8I_s>+MI9RnAK$~hq_YR23}f0=MLM(O08@<9$sp?rVBJFKMN#IBs22<1 znRG}Lk>SbAUkDcko)=LFdL8mO_&RU9)q={~I|p747&boOSPwjMkYz?mT5U)hYaQUW zk+yRe8~84wYeNBQSbD3(x0%a0!6r=4ZEJAV#tI;{DezLR2+G&Afr~-IuHveQd)me{ zU90XwkU`|lwoGPZu?hxJSkH~c5*Onom<~riA0Ct!N8WKR-0Tk|%A^{vo`r$0_$Z$i zVmJ>%yEnfT@cSTF71Vi*o=VdJ%cV@VhMuBtM+GUUkQS;b)*75{?V{OeuiTi=eH~yQ zCEjAmq6fy(3zVv`KR^<#7+jD|Q0<^IhaxhCk%JW4QG7?XgAzd#>&iaT92$d+5~b?P z2p+;hh5#)r#vud~73=eMtY-t{#Z9xbu7x{htFw13xCXml9l(CJ_kE?#+8tniEU}hj z?}6C>$xbj66u+Xr{)$aNR%FQ{eqnI`!<%9y8D4+oCYgQOMW4Dsk$^fHFma*qtvNjl z?b?$UE<9QD`>xx3eo<{-Tkmw%uOUfjz}FW`RsIGlEr2Yd9yT(Aa%QVar?5#vxE5Xi zuVCr;6~Fn`ef!(rel_TR(CK{89qd{=x8e`utat|j)`M`w8(h}~+rA5W^I_NT{Z*$U zs-44c^Q*Ui__hyy=(Z1&uE0Mq7UWef8SE965OlcE5OfZ`fDW`0CJ%~yBLk3D=RP%c z(c0M9+`N4Ga_=8E5RkEhzTK+`H{d8~Hn%7aVrLtB!X{mg8yn3Xe762@FU%LLoeHQ9 zJy#Seg%UE`(v8!Ii+A_F=YR~>xS+2Iag%;E@!Hc#>@a)oRCX9W%<45RmYPK#%tmdIg{eu3KZy6|4h5TuWdAp4O1w6`5!Z z5vu48^gg8*Bt#Omi6ickulz*be&K$X;dIv~Qf1F{@MI}p&wJfrd%kY>XvkjUEsn>h z%L~W}?JN31k%`$%GFqtSSM#Z4FzKxM{E1j7;MH8Uew#Xk@5NzEM3HT+h!`LlEej7m zB+SM#Q!;Ju=(j|)b9&8rd^+4&?W|_2j<+XY>zzujVljyuwV9I$!rVZrUVOmp;hw<?b?|xPyG*n#g9rq%{<=@#^A&trpTj)`0uP zg_^?k>{Sr+5>lKzh%|*b`iAMH>E&rSdo2D(6_}x?(Kk2b_h zH#t&~tw_po)AF&cV}IB>ckUcKg&&F}5)ttxr2m8PwAX4qj>EjJT0c3@G+Y(30u!0R z)~`Ifby`yHNUkUEz(T$!1M9Jtwh_~B7PjLhOjT?bfKY;E^9VUR&O%)!u&O$w7bV&$ z!4BwhiU<;&UN+s2hkVnwE{UCHr?WE>BJu%zs-tM3lC67xuS9L+VysocP?}K6$DscM zL)at^t_cj`p+^Bcri{+^EA^_|Z$D=|;_K1+8@GK(IYbLbgIM13icK2aT=oC=MHA~k zAU^#(M~Evn=QrH^Nc@;Iq<9I#kZ>11dfB*ZZsxWlTGey%{*|Lw$4#Hy8$LR@T&k6t z)7sMI`39J##qXaNKP%-yI(tm(m8=AfRm{4qqk47X`R6CD>dL*>*}0l~{`uV1;cF;a zdT@<6V)W5W0|;ZVR5Z;oatX0@?drrwKRR*s8ZKhV{sb_jps*jsv-Yh74B?RIcm;CR zlr6Qss6*KTq7VTNIu4x>h?3_Aun}qb=nU+sKPj!I$(fr%WhkBW3_|%{UIH5Y9n@PmN`;4lLNhfIK@y6Ti!ydHB4MGsD+5W3QFhw;Gw=fJQ$*c0fFe%CZr z->A2HMm~|RL2meEtVuAxl_p@ULZqMMKQuL*5Qw>x(I%o4y5bLtxLpr@H0tZbJwbO1 z6!l%loFfr%{Zu&Se|yps|0ZSex5`Jub-yG2OnhAGm{`q$mH{VLxq)SJ#Ugl`-hSYL z_AL)QaEtjan!}%Xl-Htc^Ih?2$cP~+!!vUOa?h-uu~$H;ZYcDKV3i1fC8G|VAGBp4 z*!5365$9rM$>H|67r$73l_%<0sJo+qK-B$x&FZh7Kvuv)8RRJc z!IK8&591rMSHWaSL5%+xlqGRHuxWd>6=xu938 z=9@`7c$y~2oVE#DocD%I(Cu)t+9*d@JnS8yFYB2gsVKagghRpfj)ZA-OPbi;B9*HR z+9%zX0~-m%&NZZUAKaVzmh4kVSB{K$&)+Z$(?D6k(mt% zON#YI5e;l1+bA~z5kMPlyp#iUMP>cp$WiCw@K8 zL}gJV_!O~MW7&UkOmx7NIREi%isoP_j04Otj75a(A*W4wn0P*`R<2QE*-!>CUQY2} z>zaj^x$2>pKM9=!vFc`CztOvx>1B8Ncp4>#4DJPzyWunR&5(!Q)_kg;e3`qO&$zDc z-fslv^iUtNbO3aa8@8f<^L}%*!#a7i7ozBL^vQQs<6f>$%vM>AvrPWvDNNMBw!u;N z&uQHc-%K-iwb_+FL~GX-@wEHHYT6Qp$Cd3q|4(Z9!4Cf6A7Qk^W3SxrZS@EyZ5((Y zuCvPhM;p1l- zmoGJ%Po`31`Mf@s3arh}S@6!?g%i6cwbzfQ$Ivwv|LBUFaA#(o1l%0_dc;y{Kp6xK z#?o-H+=1&~yKeIYgC1b6f~{8YcF75=Lf_h|1wFg`=0fXQ>w+h^0sdz~TySj$<*78n z(jwdUee@k%40#jP4aM|KkMwr6Gs*-8d`KmBDXB4rZZgbHm-~~yPl>SBN>#wL(VD(q zuX6crqo(Gt_1284H{;FCu?NVR^Mq-`3$%vDt+A)`lA7Z(>>;otmZA45MP~mO8zy-> zdt2211cJ>1y>A_I5N-pR)3t+?&rsNylFiCM+ffPHc%1E0vJ8?Ip>G*}HV9i$rHqfE ze@wxIc?P(s@m0YKgWZ4a{|pce!5kfFz1Ze=9%XdZJ)rIbJMe%@fI(;Etvf;#CT(!OfUatCKGKge&`|rXw*HQ# z2AaP$-%MsQdNXsarrp88*e9C<-O5mwWHt_$Iiyq{aPB!)@igX$ysiB@W;d z42`5}H^Z;NH@}ps6=In$cJoN&lFvguj}>Z0E#LiOMw-TkfH20H`T3g`lF#V_Pfxn}Tyo*2`S~+OcbJ8sU0jP8sJe|=2|6gu+mR66(31wZ z1oEjs9forjmL{Wtj!_^T;B}CC4_I)-OEBhsV?$5ayWORG8{RiGm%Wk zrhL^594x+@uFO4g_Uv;GU(o2FjMwq@T_L+G@$s>-wMrOqczSAl>DDFlIoX!1Z3W#X z>6tWb`LvLu2A;$ubmg*kqZWba&H7l~c<=>L9gC(bhqX!Z4b>ar?6ro||E^g2>uEGT zj3RMt(JeF_y3F;iV{)wp+fC$FNS1>ayTC#KP*|=sQ0@mvQE4TjWx{M|3Yo^l&{10@wuM3Aa9 zGBFYd0m3$|>`Ief0#dcHy1Z&6aRyF<*;}3l&%dlODKQll@g+|%;CFhR{-E0*4tS#J zU^0>k#j4r3?$<+ZUnrnWXaRS~<9=13=8xoEUhjB1pPJ2%U3v7rbjC5G7vAm986FK+ zaCwF1@Q2-gUtm04_PIkohf8z7K7f4cUqyCWe<`UWyfQkza!Y--y0|biyLzsk(fc$= zTOC4Ppob})r-4DkaB8p%0OueR=JOelcwU+i;KJb}R%ACUz`BEvUsCXu7y>lX`spLn z830z0^D-PyjZbx=iAX9K^|~`SG@gj~+zt_*3OY_9xF^H93r$$CGxK40DiLz~6KNx# znusRs4o@JFE+rEFvNs&|`yzpmU$eWbSU{C-OI?*#P{jE#cNPCJz8n5WzVHuWtY69H z?)=J~6FJPgTy8Em^695?6Zj6taGc){e;8e91CkPcEnCTPdwGBMJCBp0HXJ75g=Vp1qEIW}?{+t%H_!O;VR5q;xoYuDbFYrWMwB~tJI|fX1!TYe6?pK+ zus&4CUmE2KXbY?tv?eh}6?eI^1p6DzQ-Igt3s`Z%c@V%REmb(C=<>8AN3HDs%aqgS z_6DcNC#%K07IFFfi$9SHIJ}<1t4{j^!SHL(6iOnM{%HQ5%b6JP+YCMt7CSm~( zvi*B&A%-goIUa`Tx>e{^Z#>c3gQX5s3*dHw$HP(>N5R1`GNo#tmQy$`>`_Ttrr zOMfK9#d-W_0cre9TA?4-jr%dfKRBXPA6yDN=mL#ESO#^Nz7@zr8?fXwp^ahN+p$0x zKw=R2A^(72kyy_TSa7E^cz)_)G8h#}An;5F@ju)68`!l_Eo^0KGDJV~j2i-ooW~dqwkr&< zBe6*erqh=r9GEZ#w3Qhl2Q8~^Zxf`1ZIf5DaB1!Kb=%JRI{u%CF{@#MJ-EK!%_mWEJ|FR3@JA!Q3s^IP;jq+6taY6< zKx>HNXY|76Q1!{gu7aOQi!ieCOF>O3xXAemu935=QsTl^IuZOv@I+Z*7k^PP{Zj+i z$~-jN0RZ#DOrbFIK7~wF+wMTc79BGyl6k(_r^;5423KQo8?3j|43E4aTxE;kTheF2 zLb6~F1=N94HIBf^AZr46O<*aW3~{bvV=T2Wm7iKjjYZt@zV2UMxZ4oZ#@(M8w2?^u zoy?I?PRmc{wOr`Po*s?lljz4NQj44Zp=4*FQd!s@<=Q{5$B+gd`Q2j#vZy}(CgvHF z56(fR18<8b4M`5G4J@YQ&oZG?qwMNun1cTW^(r{?l-$%9d4zoSRQCLx=dl&@Q)Zi| z=EeI4z4&#IL>I5-)!}ClAI-*$blQkzmuKo{&1P56)@NQA4Am73UQiZ33g&-f3xi8_wBxVa;^{;p9pA(Yd+p(80IA#{ zzpy{R-^7FJ?cdy}?M$D|w!*03U7O-x7aFJ-3hIX9n)cpY}9 zmoY24FNktHPFE~ACW?MweTDrYVHd`@d&)4fuF3c46V9Ar6z%pL@{Orp- z%Ek+cvL5loJep%H>37>jESB(lobb|l(;mC?MMrsY!tN*^nGhnsP_o+#sdULcWe-Ks zfT&*t;DQT8Lyx1WZZu0cToZv@E->MeJ{428`k`pQZ#B~_`hoduP+IF{X#ahRe4kJ= zpq5?ev!I0=lYUK|omap8F+DmF2!{g`QGG}zWDwOfCDswDr?kg~CV|+0hS#yVG&}(~ z07hzkxu5VqQIs}kgA9yRR( z#K<9-6&^E~A288q34;Mf;diM0vPpFm|B;4;CVXCS-glZvuEDZ;+xV~skQ%HHraZ*! zxv`$H*adXdQ-~0IxeIS^tpnn<@8Wygwe`&#Tz^wAP#iu!VpbV0u56;TIqkuL*q1M8 zhg4x#(!vc9V8r_A!lUi8cM98X8*V;**hI0R@J84FtN3H-3r%7SWuTTsL`poQLzYCZ zi2K)r!QV;Wn!XSWemhwF+dBir;Dsl|Tb>9OaTrI7INbYhcLoA?K4Hn6lvUF3y1~|< zbn_De;);y-LHs$Sz=XSxC=uJnb=w(^XLbrz%)EhLU{S8#Kd5zIAbSfaV2Tw}yxCvC zZc{08=nvXhxQ5gI0}D3B#WJFSF!C#-(k1e z?22Yi@BIm1n?*fy`oDaAYDSrC32Q<$@#i0)EAl>nr&6A_Oq&To8}EXoFZoctsEaF3y7e zJKTT;cwFhs-LARFvfJYeM^he`h`q|Kp`?`-PE1UtV_rmqqk_)ZgbW0k)3mV1bXSZ9?M{zBn#>g!9$L&qbNAgsQcx;J5>rCC@WD1v?HGiE zKhfTU+0{CSQ(Hz;iULJ_MTCpo2fkwcEMIAO0a- zfh|A}2#;4^_5OAOUmYb2QEgGI4Afl$8G?-=ANnc`ozTr-2gEI{H1lF_tqV0#g%HRn z8B=@ewg6y~A@r;Vg^EnnT==YtT=&Go(oL4WC*0R27_=W z5K2TiEOc05NUM`(s7xSu|HpHs%tHlxDWhox6kA#xUq9k&q_drii?g?7dhhxwj(AQl zrEp+&WkHE@)b&WG}GTZ0duH-q|y^UWwhB5pM%{ay$_~J#+HbE8%&M$wK~j z@lik|qi7`nh!rG9L}V~g=M0XmI2b1^J~5yBrAP9WU(U^4jn&KXnfrgwsDCo|dvm#8 zuH+y2rQCcmUarSx?(g;L#wRg&YAn@qfUC+hl?b6yc^8ofj8>`DmFH(ZKl8RFcsFmJ z$>qNG=x^n6GdH_yIQXU|w>4nOyv+nbTrcZBp zPCl@LUyxQHp5h)VikNN-9peX=fx!HTf&{@VVMUS#eMIjfhE&c7a!@)Y5zcGdk$=rw zZ|&TAYYSE{uy$tRLb)3x1;LW3{1$NT!HWUwK}(rLuKy+O$%G{^%c~~@1Ja@9V>HN# zWKLcK;xNrZa2c1+K!k<(im#KxcH)tv#XRE9!rrReHD18SqmP_FqhG(84YPZBT!q*MU(2pcpU|fP?6>QCmjF;18W8X~w*V z$JPWTTC)?52;k1ZaH&O7atldOpEHI=XOvw43#Ne?+!-cV^q8DGa8zJjMnsu?Qjhtf zv5Y6;tK4-Y;4e;kLlrHkJIW>bO?-^s+{teWzTmW*-yn@Rf7_L3o5%WRhyCj8NpJA5 z>yUI5{eN9FvFE~AMjI~Vdey(&d*7G8%o>4!zjk<@hZ~GJUH1VQ$}}k>GodtdF$KRP zOrLmhpt~&{t*@*dJc3T6$Jg}tZsZ903fLG!uH?ub8y3&#oPKoeXJ2~ZGF$y#<84D*c(C9Iiv z9@u&yps=ABNy9Z+fy#x&RmTr-GGna3T05Otkv0;ssBRoF^k^($q{TGqLuJ6!JK*RU zug4+YJ~vZ&O=V`TTAi&<-#?A5Xx=k3^HRVckNcsyar-l#sNMToB$H{TW3Tnvqn=;S zW-{3t(g`Nj+6J22#+ z0y7r~2C=*#mWM;289ztt1z))0Qybe@x1=o%6~Evqr5n-qWF9eB6g3$Q*aLlRZxfh6 zG4k)wJ{MMBx{?VVZeZn7HnlSBY>-ImTzSR!*DvI2cc`jipN+vCh`U*we{MQ zPnzqi)qeKJt@n#hP9fZ{tiL`A)SmozXK(Titx>Wb83x|PPo5ZtCdfmmRtKsS6sKeTSqf$q+KZ2r_K7}D5YbMAR0^jJCF7&!_rt95n6 z@i6_n^w7}Dq;-|hwbwCeB;72kzZmQA*}n%Uk|g7KC>F_8YL#3B z?zKDyQ^{o(-LNXhA#?*Sq|vsj8Z}Bju`X0^O0dAf>?iupHVWDeU~Fps@?Om9a%jIv zFMDOA-_D$x``Fycef_p@Y7X6req;M-a^Hl!Hh~O;$K_hN-=Yq5864(PY+hM)sQ$!5 zQG15LeTZvxjJ{>vS27qv|KB)<9o=CcUl@yyaUi$zBL?+kek@FzX;`4lGW+=Qc#Jlr z>}<+&8(E8_^)A}@lH{upzYp+4lsJ<~Ay94;Omz!`B^ly56%#FLa&AguLQey-Lsg

y})F$tjs}{ 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�Gn-^3|F$D~HG0GN#m0p3RY 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

f5LEo{ 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


C~-^J9kuf1f~B&vG+GBd zx-&Uj&&Qcj&1!!c%UMy+t7XOOi6BPFty+aAg7HWo5b;MTL839gStAsTMgs20r;SQh^Tcd36iF50vcyLZ zjUpGwlqJC=m#O~ikdf6C`PIm17gs$2e*$E_Qb~NCl49*gH%SQXF0^c>A?;J6Hi+zP{ z1-@2A9HtM>bPE3#TPeTIHquhr3}re=MRS=f>-1k_%J1R__cw2#&%N^5GpA0Sd3JpJ z4A;*FbZ{!9lyD8A6h;<#Iv0etPss|%V9xa{S{mYT85tqz9MdkNT9T!$Uja0S*S=VV zl&*!tg#o%BB2mAGqiYVtLISzmsTnnR;e=g{pceQen#cz7BKv>XGdRWe!Q$(ROK@5X za2j;?D#3N=6|xNgy+8mS4#N}A5nO{;#OGuf!t?mQY#3h%#8bhu#tDST#UxvjZ2!IH zSz~Exr#Iec+cuk~n~TPh@%cbH7I@IO*Kf-*zkvUQ`Jgeiq<>-CH@<}L;Rk(zPZ;}? za5OjpVNG(=am!p}0fGSrjvm~;5>N2aF}<6zU=2Sl_7%1)Z9f$H9DJ3&L{|`r&PUf7 zTn;ZI0Wz*hV=3m>2MWjnM^3f{6Djf`&JBEnLYux$Zu6^GVFTgF8>@Ng`AhKctcY2I zB8|Bl`MA5JjRI)_?2jySOx@IRD&zKu0Zm4l`!dsulM-j52{HuxF0^pC`5%J@_z zoeFL_b7;}vF1a?tdWCg>$ACjpwqh^DihF+pvQ7`nm0~;p_>sL<;odd(A`a948zk_itN;|`KBQ-*ceOs0 zRcV}xpoDadt)oQHWUJSzlz>?$e>D8ZU5p}j2=MZ{Q(WqM#awU#81ioWg!G{7#SJ=wQ1HRZo zom#AXfX)a72$cjc;&f?26vH|PG6erfUu9X#l_<)bAtQ*t6sV`6-S@vwYQ3gT6b^d5 zz@`yIt=He^J!4rbm8dh7Q-VoK;QFE!`0iXxDPh6;@y@H<}8D4P|gAHB>on%&D z93CuX4)jHyF;a^3s<{;p%!aOLq8BNzwxf4-h@%C9RzQRMKSRVS*~R!i-TT!|V%LG1 zO(zDZ;NK-Q@DLiz-VP1QNUQ*fbR*qZHejl8Q1`RL5BAhSw!&Up z=DIImU$n+B&EQx(%>d3Ad}VEIb#-lR2$=){qL1{=%~7P0##oQ{=mH?LoBdb=ccm>p znky<|x6KvGo7dbjGjuiNmfCJU*6LS>lWE{-U2`Gm2eiWqaA0gnSpVMU2!4GM4+g^kjtwOjB7tnvC9rqs z(j(1<8217Y-$ay_uWnBW5%CiEFp<)2qAHlH8#aKSwZ^F#r=-@Mo9p(ktVfbo+!r*$ znsr5|>LQx}Kn3!ET z9SVm-r&net3TXqmf&2gJ*!>4*oJ7KzIe7n10eZrPT<4Mct{{lLbelGT4*^os*s;lMwcDNXg|}}q8$5UJ1rrWUNW7d4 zu5fQkmDWv}L|c|yT-a0IsB%q-dZ)OD=e_QW z*PQ-UM;KxM1}+eCW@}oHQuXxKxIZb>KX*Y`M0Jr^`2PYnx1pswphc|T z>sY@YZp)^+>b!s+iA7{+BdL$XeLBXuT!e5ku4T)lLb`>$i)Xilj2@zYpueY zw2BA6tz;94tS)J@g~Dv%sCsnes7g1iOBs!jGLCj)9gL24qh|fA!V(!>&!E5neIZ$$ zovl{q=7biy!B)WTRP1^eCP$cI2M39Vo?}k|SjD}%l%b|bxED%}9SU^)$V#k4Wo865 z)x+TX8~Eeku|u-Az#|b&OU79w5Uo|rxd346v@iY?qS+T*Pr;A*0jw6{tdtLy0ziS< zG9ct4+rLEV{&Ae}O@kF=>cD-qu%-wVjeN#(1!6H3`It-Kldx>tAd|rVBs&viqlHWD zg|W&8^1T9PfExA~Snvs4W^?^vUUY&&m7(F&swIW z1Z+zRC2};U*A`;Ydp|EFOw+7`~I>WRO zq^OdF`~|p@f7v?QqXr;q(CaHZi(3qLgK#SM;VK^FX=xW4=N8`PB%ZSs=QXcz- z8|RtN`j&~_5*$_~UP%jM%eAvj)__kA^d~{f2 z&Jl(+oL)%uU}rOllcvY>`}UU^edyISKtA! zV_Y_)*ou_Hfv6k0IBwKLNn0Rck#rB}BSc4kgZQmbJs~cV?zCBLmuOglpA|__@dLS0 zhC8Ti#(5-c6^;j-9`>veFp}>;L)Xwkr@Y;1A!F#y8*#0Jx5)%PW_fZ7d|T}9DMURN?>6+A%381YHr4%6_#y^Hz@Nyat0(uok%p6u zsFba3vfO!aPD&qzbtsn)hL(w+ijR-zy*4&h4*+|8`_b}MH$mn_2-Ay@cE`}^I5W4R z(?`j`MmE+Rrk_;=-duP4QcRJ2K%z5mAdnh^qU%gb4M6J5D=Yl1Hw^FtJFS$T&~U9G z{ygLJ0hTV%>QYb=FUuZ!*(1}-Oq@0tA3A6LBY6EMU`$JJpVkuAR+U?#Xx1oeVh#e= zG$3&N>>>F|!ertMkP(c@#an=?*O`3@`gsj$~t`RE%X;quJSr?bjzrDUcP zsm=M3IPEEgTKw4{>mi`}B3OlVFkkI~;XlJSn!S3w&)B zS`8rxxN(R~Lqi+hFbyhf6*zRs6w~4XIj_(Y#m|7q}FvYk9G%o-DQ0? zsGsa1Bz8#YItcCcx=pJf7F4TtqTP&HA*isYMr24C2GIjremmjA$1{O17BMA!le|L6K?t;qu2C(tv|Aa+o1)SCQ^*6XXGvoV;U@xFPq9jlPccMG}ut&@mGp zE&z!I1V*egAT|xIcM9Na55UXSRgUOfU8mUq>c2;+b4kk_t`kJiB7hb*41xR08${xW zjZk%QkzT+cbyJjWz`|X~gFOJiPybDGhBh2x=K%S_>fohn8a^~(?)|IB%s2FUwZn(s z{=YEEmHO!(m8EF}f;239{tB%vA3F3aU=u&Un?sn}OW)nH{mG7cJ3$o|ngakq0OGsD zmVZW_Su&GLGiqzcjsC@b6TaPT@8mvyERx1`ja@?p`mAf&^^oiJpyCGW2*I%kx@}NV zdW7qYohC0M*2zcaSz;8U2hWgQg=a>7QmW?vjdKl(L>~5*;L8qu>VFT1Od9DFJ%ht$ znanZ7dJ#Q4aU^l>gj;@XAhi1OLZNiJWIEm&;oC^mT8Dji1^brLUkJD?SRzV}app!0 z&V!K}F~ot&laP2htu5o4AK9NsixE*eHRl?yoPc(}FKkRd7}}rsI58K$;|>!TYR(NU zO@czyNEmgSXJ}r-`XM2MITpAmZOe)oZJUD}Q z1kh5)!;3 z`CKG%I5c=E7HcBUP<`_oR+mlXytHNZ@At}{A|Mz=kL*1T9GV`HcX@q2?=DF>|8dla zRnN(*MRmSyL!jFO@qqRxcl?#q zVzdzsH==9_fij6`QhQ}SyyKYVB>vfjR25$h7LYsV;lSn)>R5?4jG!-c{;wNW?9ID& z`E3}FE0uiafQDEbe=_)=oSTPKT%n!|=BxOmVf%OOa%bj?a^PX|kI}li22q|;d)yG5 z4P}B`S{EyFzM1!M=^dq}c`=CJcNh2Gl_hICTbUK2dsmp}6$OQn0Cuo0cluWexFAyX ziZ%?09}dpQMV!`o{f5K3;sN{?p$||JnC|{FhWL1#&2Xn#u|IQfu;u<5+sm7(`@=om z|8v6wA&eR`!=^YaRD@ejV)bdT%&x2;~qES)4U-Dz$+FHqQ_ugL98R6LN}-5Fx?u z$*;(btyiwp`MJl@7)whBv|aaPGxxNCyP|`Ztqd%zh*yhQ*GE+)&@SimgLY#$&-1h3GYFs}$Q08$DeK@|q`gc1z2N}&>IjpoV}zH91hmfT zE53dEeDwqQ{DJpyQw7<1aS(3|p$!z0d0 zxkf&OD(v}&4*CI$z|%7S|G}>n zA}~^!Rt`7z2*bh0d z&IQ_hv?H4A=cesnJnvLLZ8y%vnw9wE*{_++Pui;-2=ujv{pqT6{uk}(V!YCfO`h%d zo8~9&FA_+IENFsPpj6RE2W_Tq(PnX{wwBj+Xy<8;`{w)zZT$T0`!(E}*rq%5>$QVW zOGH~Xc4+lyHUlyVkvP^zDkqSoaixG88c>1~3OFI+A%|lPsR({swmx}w5(9cI7q;nl z`niVvYngYS!%+X|GwENl&zenE!ACHzi}Zn9#BMw{ZTI`>cmF7c{M@tY@7QOXK$Q&I zr_e$Q`I;g~E{YHeMOq#{h{GmL@$@8_34a$?KuEQQKM4{U@C5}}e>vQ55Rdd76s=Uh z5>O&PuUW{=D5H2~z4jmW&pooD$mPj^E-crvfdTsgMN0gof|K%#0mUQ5BEev8%Iz*c z@<>?Lz$V2x|KAWBxyBu-G)l-@hEd`nd~gQ>DOinj#}>sI0u}_+90XbzZ&1}BSMNOL z(KL@SHMv?>GqsFbXLY^llO$g-Xy$TeQ`h^y6O`rPkq^FY{k>jY_r4cxqG9m*OW8>+%E{sh%dpg0}!jMu=D4DN30{0_qp>-k=swA3a-@ur)9D-px}Kd z6kJBidFjk^!~O_)nB;d{eO%XLRQt0k?D1F(O;aTQ+TCupyY785{eF^S6z?bQ1`uRZ=B74)!5MF(j5a)@HkxQ=Yb#IxYm!3P_Y_yzC$7wZ; z2kMY9%?$g{9<~0fkDQJr>aQ5CV=CuGW$H7O3}jQHL1x7=pgBXChO~T7(_H3a0;1$r z$S1sMIIzCd8_6+a-eS_>QD^LX>*uJyLpuVMFEij=+U@FK=K#{o*98R-01XZoQ?&(N zPe#6$hZZU2*?^3}MN^okuz@K3o(F3QTZmnZ7t`wQle;465{j1vUZqxE6+w6iYsIYT zef7@=Q886h_f1>-twJcOtf}c@9JcTY`j*2j-2U9F0_C*Ec3YYpKDp4V8L14u)|FOCI)=t3gjFN?bl@p{YsG26GN(npr6cG7e=F~8^Bk%&CO8^29(V- zvs6x-hr4n6-e`)4LzKnwIGpLY4rr&aS)zGtC*F8h42VGlSXU@*{YN{D%CiF*lq69$F-F~X z!^8Q{1`{+j_{p*(P1}tXcEwJjj$F0mwpxSynt?tt z3p!s9-oeYdQb3^4y7Tj#adA6JoAOkWJl!7LLsy487p6JM15gT&x=#T0hTYF^V6WoN zeB@%h;T~VRucRdHvPx;X;Bn9NPk(rK80d<9Cn$Nz3Gc%&B!xeudv$01L~6hH2ibrV zyKGMZ;j5Ura?rC|q7NLcAEmnw)PI7$bvdB1-U+K3Lz-5rYN;8!p{g0S_Vt7r^Ykgi zvmUnqWq4O-$;}?KBe8}Zi^yj@EGH-V2TSX+>haQgizY#0=HxH5pL}J& zh!$HOL?MOUVY^mR3YLF)#)~*95nlNpa2|aE+?i6NR9tr<$nxId8Cc` z!~tb4n0mz|s2t5CPiM|G1aLW&;v|qy0h$Ho8oNp_wlJ&w#Qbqzvsp!HK{cSpja=DE zL|@bYJKfaM3HxD>5{kzg(iy7?{I5|-GT_XO+^%_db~a;9s`09$J&Y9QIK|(aQjJ*6 z+EtBZB2ir{J;*eu5}NfP-L&+E#U#{StLycJl?Ab!!1i~A9Ou9z(gta3%tSNl5zCQPAixE zf;Rn%T&*!VQ7{c}l~q$2Gn^%(QMYtFGj$ILxNDEoa;BEP0C?zxy=Sg|XlYuDRVUt1 zs#*iN?0N9IC?Qa>tZ1JB2_|BCX`V>%fQrQ3rtByYHWeC2rE zFbfl`J5laVpgsIebjuSp-WBN2d5lGGBGHUJW8>|zVsLpH-!{`L~EW2dkJxxiTd#q&4dg;;88*QnGE~I{y(Fr zb3PW01eLj5zF>E#P)s~jddzG2gC2LsJCTj~HJ`_bAC4OyNrMa1)Sk)oA7uLqNVSS= z=!uYNWfJA^=^(EXvt_M>!Sm1)VQX20D%WP7=FmAs@(N>LO2witxzc6jp^t!^=Yk4P z&dB^FD+uRV!=i;-ghE?4U7o!9y}U@k<^8 zq{FcRcLo*5{>Vd0In7*&NNV3puAt&Q`&;aChc~PQ-~8r!jn?XiKNW!kFKF!!1-<@{ ze~I}zkclk~7U#;pee*pJZJ9Gl4@Zfnwxm zCJkH1Awwx9c4!}`A|kl&d@vpexA*+Uq~BOL_Qpn&U%vMZTY8wx(G^Dx1;#KM;65L}BLHUn@W?j=5Szn})3#{5xJ zRfC4Qq|FzziB|vb!r+hQPXd(bzI57|`C=-Gf-vrAvgnj#kJpcYN6m;Qf{{7dAE43w zR7v&D-ZgI)cIOqpvp<P37ieMwG0_rW`72xayeI!mnOcMHb#cb0lfb=y|8)z)3MTqbv zuEvjY%8J}Vye~={$ca%Y_hUIv$Pa*atX=R}j^?xuWSke|Qb|r!r0jy`_3xGAI3Kc$ zDK*z}{9k;Rl{@&hJiy=nP`Ex-roJ96&-jy;s#~6ks*^d;!d2cEiI!5)d`fPh-WjT$ z9hRa|W=d*)TNc)`rDkKAcIA*z($NGP!S!!sP&s-p- zAGw&H*{4~%&%bH+$-rfy(o?TGsY6JYr=L@3V=9$@@n5Z2*zmQ(Dy+Epp7M+pLjo@6 zWY`|T(>B+q1(}*)bNAfX3vwC}r8905hWOD+h3yGS|4<0QOz|lLkhqFuc2&X&4;JUB zGu~J1rDBdKtQ9HE1WzE$pAdIP zUN5LvWSE8vhj0e?#qJW^@p*4Ds2y+3-{V{QJ+!$yysWKj@i6nr-iRH(>;YC5ga5pL z+N#-gBb|)x{|t3)kM~8#X}Yzr90?>BbC<)|A7k_cVSj?|MDzd)45VR(QhNG>_Y^V(%hLBw zpPyr1XGE?uwXZzqKA*CEau88}un|E5xym{?VF2;O-VuBeSpjGaCBYiOALYS2p!tS( zm}}W^>WU(vj%=#9tUEP44!LzT_0!`|iu_YSImGZWU55-F{_@hRR!}{|g&tEo5Q`W!AYKRm=nD$lC^kx_j8tz{iw8|G3_p@l|c^SJ7 zR!#thxD|%nA`n+2aC;?q$$Ox)OvudO7>L5Av=0#QiYOwE!x3ss-BA$9w83vz6UPIg zqv_SjyW+?Fwk9D3gCpM`yQcvMIO6wvIf|(gx9bx{i2ey}e&VYW1w2~bfQ(^ zJuplIw0%ePY+PBt4JDLw@%ChJI{pW2N$-Jm7w*3;v-)xf+A^JgcpKWb%q5H8Zm>vp zA)h48VFG++f|s&x!yiF;WRNJ&Hw-v{yR|w{d<2xMxKJ%)P^(SIVO%hS2(H7#orVp@ z?ObJ)G!QK1El-pK3WO&;W1FD80>pf>}qIE%fE9TOj;_$H#rTe_9>g(~@As_kLl z#>qZCM&A|h6Xjq~5Pv-y?H&D^=+joZ*;Xp|j&)$q>dB*{#3+o9HTi=u>g^)zs-ElJ z)$VGK)z|tPV%=_Ve~}NpL&2K`c+}$n@WqSQPgPEx!biK`%b&@^oKIf|Hs&(N zk-#HDUKr>%avP>$!l^WilvRTW#;iixge1~=Hs@S)o_Pj0xo0~J5HG6t&|#~~FPv^a zm200q-OfGLKK+7TU!@~`8O`v$NwNj4BS^I4CznU)O_0rSU;&RNEF~nu5h9v=2#BfS z-ryciCU3@p9o}tLOGZjhMzxTm23b0{`}~kyP2WGG#Z`PJ!Gf_6@`t;bW5-o0lfPD+dLS=W$+WgGeOCVlrlO9f2MbwFP_fR=8~k&r&` zkS=h~=56U=<+VGa1ppzA1*WrYIB$^M$Iq1EyxnfR*iEx{i}BiK0&hKNw4C{xrcyFc zALDtT>G0cOJ-v0Ur9&R4B%kyVF|Ois;CI7M=8JIzYz;nrpx^k#!B<||`W*!pa#tK7 zw&dnVF-I6f+C1{42$U@pQEK-^Bp>t1ze>0Mlh!B0k({B1-=!hM9_%6O^QoHe5w91w zUwqGlvZWe1|2?Pc_5Qlx58vkDHl)+L@&jQ7#z2xII6wy$FvzXgHneGoHJL2)YouP# z2^~+Cv-a`U@n3R(5_@D&;?frHS+QS}6O-pU=Oi_m6uGYX2L}Cv>_TC99`b%84<_jrskuGDHIX#O1|>_mg+MWqEb-yzL81spd9e~qTxU!7K{dz7?Qzx-4?FY?N;=A zr`wY>wpfh=JE8_TXFsfnCtYV;uYtynC3ntl2gaA4w? ztlN1MK~Y(@R{J@plw%>qoin4w`M;9_7t-ZQ>Vg{h1DHM>=MP0(ae0piNh+lv(jKL+ zr~427FP7BoVz$bdg%ScdQ+4;uOmUB@R?>mOq%(Kad9*f+GUa?$ z*i%*7L*x}e!cBB)GWfj?%?|sA<@qHE*c#nYmZ@v3<9}g;@#VcoW<$HH| zy$?y@^{^B;0-D&9$98>O3gPKPUhgyxZ($!9wv~VukBJc0BiN3xkWj_UBvc268kTfG zi0~TTK{5{7Qyl+$1m$w6xkv8p|MHV+ZQqA;XE-^*{Mp>=-c1geBLPkV+T3;TM?Tb1 z|Br|E^>fpLvXGSjsfFCGV?+z|RWPToVEwD01iKn(7Rdoa;qkP&bl<@J(uNw(=Nu=? zyusFyPvq5$C?bX$4gIx=yHI`0=wAs3F05P#gs&qB$if4Q3CxmTpFA>&KL{UCP3q+0 z=3{-0{YS_%26>^%6uFK?h5ZAz#p`^`gKPO9asi@)cNXysNFdsRCi~^AnkzXcolMT- z%di+#(3sckhx-4FVsn91jD=zWIT2_w$z#a!ui7W?HIaii*0dX07Mn*Ij+h;MOCTW! zVj&hw1^gcGLcpW@`7j9Go#;wJhCYgM;Z7frLBF&dDDTD+rUag;8>q$0U=*J1X+D|J z%MuH#rgLBd^Q8Oas^m7}dKxz3t=%Z_-u=UNePP{*RoAN#(<@E-aE*vOu}?3BBDKWU z-sO3p`#-*YCxzXHFcu+r$;pDUNA#3u;uCfV1ait1c@UNb6G{tyInoM7c@ePy1f+~$ z%a|ZKTPm14z^8_GiQVU))>2+Xs;9I>Hd{@*;SNq$v)P}CAfH1b@r=sv?*0Dv@Am|J z*(~z~+|DOP#U!3Ozx%)Hnk+^2%tSU74+LYW>_jF)-yHtc)F*v#t_7LTcLr;U`X%;M z2rD@bi`d~!FEN%d*+Eez5e^zkkOwm!8IncZTZjzBr@f0$(#i@gORFWcLtYPKfLH&0H zd-aREr|U%VG_uLS9C+$&Fw(u_Qdq^~0ry-HDr!pG2inMYF`S1O$I}Q)bQZpM%5M!I zZa)=`(@Mv3Hhgr)$x)XM(-ER-@)04+JirKsnG$ zKOQ)C`duGJf~R!!qIcpXV>9&%+7)usd%(xy#J?$BFO+kb`vwg|l~dhCGzLVW4kP0V zu72XeiF9-ct+`)dlxUq_6OEj>7)^g@DRAsO+@>VAk$icLeUIy?gsTJ_HrrJW3*!n= z7MdjvJD&Ruh(c~&gib^U}Fvv;=?mG`>S0rl>&rFvuFwMP)Zm4C!j>#4-^EN7^eVY5?$9 z0M5&lk??K!I)^`w5sUW?S}2rJdO&O<>Cu8t7x1b_)ceDna&n9tC$^rWzZW!RPyhSuNy+|?1l_N{%jbr z(HSsa9@fVX0Fo#GoUIO4c6%Uw%#ri~`>&8b@S%|&zp38C|H8g(Y4LhqH>d2=@f1|} zs(LyY4aJTAf5$oQWtMh&&MD_YK}aLXXledKvVWk<=ZD(8yPWjMYJrVij}>~$b>)w zbf?bsEgvmD*b=*-KL?B&_fP^oJ0mQdB;)~B%N+pw`N4rD*BauC@{kANuxySFlMzQ^ z`>YRJD}&5wNS{Vd0P;Urn#i9r12*9c{R6A}=*(Sv#`RGqGd zT7$c~h-bSX;_t*-BaG`Bc2F1=K8%04x&SvZmKkRlFsoo1+5q?oD^Zbk9(_~~1&~U0 z=ELcaKKJ%?|I1-K3$1_k_4+cnWS}zhVEUu~@@;8_o{%wxc(h1S>j5I25aEO^biuyW zBn3f7`g|G{0!?TbEArOtXT#j6z13Hgo2A8d-Xd>^Q4|0StXHqDANsMwf3QrA_CLj& zUhYyJRKcUjzMCU&P#M8^q`6eNZv>m#q%dmk`{pbQ2RBw4h`~w6T#-obu5>*ChrE-S zkHzLQ@JnaYS+Ji~9_!NSVAaYoUX=e(8!;b(ziMie!d*P|rapVUP zj))oI2Zrzm10rry}g3;}cc~$?$)SGX-h8xe2-_i%yMd zsMZ`ipPjJwxLNXTe6~6Rn#Fwa^xZhqc33U9pczy6GH9K+a_4mJ;f9^fIsT!?b#vC+ zFNfAEo2y{@&Z&Nq$ApOZE(|g0R@@gzDJ4<^wB-N!^W4jaxGq8TJ zB*H0Txh$loEMzT6-<3P_>hxFL?v6DP*Tdg@Uo9LOZP|oYv2&_ccZ`YPtUt^CReH*q ztX2zC*t#bkNKd=nd#s6U-cH8;^zUn)!7eV9@)LF;l}$M{6X+JATi67yvoE0sA=sQr z!rbNRfP0aDut?H0T$F8ad1OBn&;CE!-UZB&>nayj=~a45rBYSt{eE<-x?8PQt2Hy- zGb7vcusvfBV_WTK%-D%-JPqVw#w4*5WXB{N98O5M-64=0e9yT+Xpw|hfD;HbiYA%(XrgHR|`t3S+%m&WsRiF#&*236`=7e);txF* z9{x*8c--T&Q$#iQ2VR3No4kP`wYi}XLx>ZUV;c0;@G_yD5J4}%J2-F{LUMIt9s0SB z>@%b(bL2aPtTOB?(tCqYiG_lnNc@mzjVZkUg=@>6AkEny6%LmiP^h23!vn2aLVWlI zWvxs5CXi>IwJH4t!300H*9a5V*>z#j2iJiE;co^ESN{?^?i<95bsb$7mQPz^qpkzh zW0puOHfQ|opkNOHlZO_j1eJ#|b674=Nx`#`<RSF@u!6;26kD`1~31a^i9AyP00| zF|95sQ;_JsNwXzCUQ>quc{%URi7u~HIa7V%uy%~8I1j}W=i^hPrMVRomWVvQrLRO? zIj0mR83+6aat_H+eyjnTRj&$TE!J<#ft1BsOK!ETN>#TKn(_@hKPuO>%UxwcrTWYI zDXlTwc}%;6m?m~sBA@j&tg?hHPr4A9>1d&D=%5G*EGwiBp@4d%EN5L!P-u^Iw%LS| zANr;~k}?1>~!M!ZvcdaIJwL?hNb+P zU}?3l2ZKJiR{xwR>CDJ!@&@o=p~&mOV5P@vK{G*&n0n$NoF3ztotjCHD1EetB=;dk zttS7d`vV3&<|-jZj}PX`M2q$2h!!ttkL5mKP-9*_#i{YmkC(S}y<|`#?StH=8Gs~d z8<+2d2}mS+AGGWi#mizUpHEp;DOdVO4pFes4Yt$B=g&!#?L0CIKjVpZ(C!306hEO% z=z*I3{DsL@E?lP#nRF8qLn^*~vW?-_j=lX+9L28<3p{amwG-~W%Q~L^H!z z)6J)!zg{~;GrQnoN-xNPJ2B71m{GG}0-8pCRXRNZ&y{9s>XH6#c=pmT zYs)&YMt&)soNVe*snMN;Y4<$QxF#E!Y=HnVWirx#63<0meF72?1F< zFu;yrW7SAdc?7Rx^=7||Wu_P=ReUJ)4L>XBE}wgA|L~#ReHE_RSCjPxqTjc(lURz8 z13J6Xe;BKBa$GPoTw`1~dD6t~+tW|?4;s{$^8E|D$8_y}V6iy3pYYoc>S&n4;>J|n z$e0jUeQ;m>RQJHXzAAwO?CFa%CI{06@OJ?AP9D_H#mnFkH#A33G9K6qxW@s+aPh4M zBe{{js4Fa-ed*!kQ(!R%boRF^*(Nd18|jN^@4#_AyLv!R7qN%z?&sR;X~drP9~I+N z2Xr=KT9f0-Uxy}$haH4O;${c*cCpVG+hkwYraz$Yz>W@@tDP6P-s|^;Z$d{-$=Z`- zGdLh!J4(qHm%Wx9X??iYE^+^ljdQnyoX_8hX3QQUQ}oEGZovV5#uKNV@W zJ+awjf~EKzr*Vg4(y-s_lis(UDa}_e#wd3I*OigKj|PrR)(oamL>_fGZDFvG_bKj{ zS7m4^m5~X9(gB%okZBj@cUY?+6PdvSUxhQf-6J8L?{cG@9$tomTA%=agkA**W>mYR zYoC4^fs4J<;qYl_S97zuoZS`lgfFVavjl0sx5IbJf^K??bYSr{Qj zTwt%!l;uBw-tQF{gWhrY5mLUAtZeqft5)ppsRkn+Me&g)+3hg9Wd1_rSr3jp+4pZ^ zh2-y=ec;mE53(=`7#MJ;{*u08#z=5L4&(0P++Fo&^Fo&*BHnR0U!y`@2wyhfqO%O; z4anH;u{{jhY9m6-D8@xL38*_Uk)(&LVNzwJ217PcR&bFiUKNN*@KS*VT$#)$y3~e2 z97gdaeA%2?VXMaj{XgV$#1fU1JE$DWSDx73{*dOftL|hF{t@=N=!8kmuGlq=o}EdL z*Xgp??GEF%{Yhw(ktFVmPax4DW&YFR?t+r4xU@L( zbQCylK^n$6dVq5-#qcuw8P4*g{u)1|LZz(wvLDqiKQ$38`|4E9>AS*C zD1}#M?dg{ZLZ4!Pm}!e7%7qFm*eQ*u5Ui>KT`(=mv zJk5#^9E=Z3QaTNqu@=nyaM%)6mJ8xKsDYAi7ha%1CKC#?{utHh$kMz&y58*dl+%k6 zWt@{16E%0!{+YCr@g6$lP(n&N zFUw9R%)u(H&!8oS1 zNht}RBkX}-df?|Z@;bwDr1q_wp|nOr&AGK$C=<;X7fGejXuocXdvCtc$mTQzqGe9W z;g@r$D5_;n%He#291=$OIS-GrCqRwwBTFX;1uLIYA%6R)PP--w|MlBReS`gRtbALj zuyif_gFbO;=cWH-$a{OT^Fnb6eLi-elD0-HpYMW+dp0*W+ue4LQoizpxbz_^n?vDE zjt7uIjCLJ1AKHceloS(iPlH-P*%SYTtUZIxUa#H#tfdcZF+E-1K+5?p!WzcCAcLYn z@&v6dQ|A={UxzQ2T(+; zwxAo}(C5cz|0584jFUefM6b6w?va-%HENa88sPGp7g-MSh`oVFKa0&&q(WP(X<(7< zMY!%u5ZNdgOog}zS%+dX;bTgc3E0({R|Yw5@W9a{lF(iG#!8@^Sij=z20{yEsSvnT z@OuJ2f6(JsBbVzRE-j$u9Z7IUVwvIPndNV+gcH&5J5eQG40Zzrsk{&hAPOTG4j^rZ z9E>D{ZGEAvAR@&g%Zk{sfhFvJh-rcI4g%{S$dQAmhU^(+Cg&lo%{J0Ob$$K|{z|1% zkh6|*0^)%Z$$UEXV>9`Mg#2A8A?%9zS1!1fwj$?!@qFEj(Ch3wQ$H5(9o17I=lxGX z#cs;ce}Q}-VZ=ToQVCulW8XVy|0}5HR7Fi36$u`Zm1K!H3G=<9WD!V<@tZ$^0Nw?N zC9=!&aZ0>!S@kW+k~bRk_!7Q|Z^`wq@Ug4p@6OK#f2LUem`9ZGWkh!eKYPjUqKzg4Ol{! z;3s^W4IW1_RtaPVq7I^4_(hV7H2)`)Lj&R+kyQhw?nl`~fC*Ww!l_mjI}oxKJIFUk zzZg!RsM+B8Og{#SVnGeqZdy$vdk92G*mbEo48ok7IE{5;Vk`B4OPDEY$x5n~IF6vEccwW=_o_uN{CG*ox|OTxT5$2 z%FczmkYDf(V26(9OD^mZA|BfmI2H`riwZ(kqbX0=Upw3Mm5Scb6wY&wY8fB+BsuAc z_-be81AcrGSFZ2!~QqN01N=PNk$> zPVk~o`gq1Zf41fidy=x`iz>xR)u9B%ny*mu2jP&|9RRovn?3X0*pY2_%1l1`VH10%m$I#-@l>0S`BYf?uP8$$%aC<-2fYz9}pWP~8^Uf6)qMyJJTR93ys6 z>cWEq#jQZYXtON9AcRRY9*9sJ4+vSRATP6G%S*KBva*=01~b*ZWTjZ3iTU#(;H5v| zGJoCiWm7&U0!>0rUn=XPcP>Ovgk5;I&Q(Mg>lF2jCkRzUrJ3^VT%Rl+n z6bbp!F#$*3DHL1BF^;mWD=<7VUs|r`=g&MTJC1W5(ty=EC<0He^^{W*k&D$@%PnkE zZDxSr?3A@yFjUpb9j88gRn4v0bHAF?bN032Kxv%zyyQ9EP=5JDIQxmLpkG{AT3WcM zmz%N#vAzsPzxte#SyJ;EW$v^3(b?If`dTGL=O{9%T!HHnrBe05AVEpOaIAL0Vp1!} zlm<-9!L0~qDR>@+5&d%9&R*>u{#MwcN~}1HzvvG3@4rv;`c%}PoSuorX7IQLZ|Fwb zcvb4E@Mxghrh9m7vM8_h0BP zWGjsiPrK`4+GAPf*p^X1Ub$*>PiNEOZMlVRL-=2G*ZduN>%Q3h#boM<<)ufL-R#pc zKCHy*YC`JLlPV=HGS}PgMb`K;S!x~Q6Gr2Ma49G}-!{hJ#wm}iStR$j*%Jhrxl`+i`1`13ztX|sg24vji(!lQpw_XI++^&b`l z%eqe)_l^JcJ$JSgRqb{M16^NT-P-E+Rr;pd?{96bLYf~8x?L*U$wVaM$ikfAJ7&=- znMz_nvyTMQ(5xU#it|tyHbFz6B+wJDj8R8fj1aWGq7%tEdbB5S$7#i=K!w5>Zp77I zwS|06dyrMfK<9Au1d1$2CaQ9lrF94M4UWj8lt@V8<}!^h+HF3-egF9Q{m@L zQjowV$WJRvoi^+wC7jT-BKQ$S4JvfEMlY5g+(J*0olCp;4u{)9G@zS?HV1K6j69N;C3BD z6)u+AQ;o6u%8!I|{^1M4%&{3kC8~ML*)5AQ;o>`sgT+OkGR}^Rj`Y>nVFn# zGZA0LVFiS{H9M3 zG+E)LaR=R?94Qn~4qE!T&{XbGuN08n9}?A&lz3KU1>A}1pe?{4w=5N{zyztfjRaR& z{t&urJBVi5+QMcn=v!O$;f|p2hxvORS;Q-1F`sj-C9&LP9pDH?NI27A^W5s8M$8VX zujxozwtwHKNFz~I9V)n4z})tPwRObOBOMAJJ?gZ+g+CkoAqeiv7P!rtc09XUgK}@M z*3b%OO1GoNCqlODNbHC9Dfie>loBBLC}y74GQ=9C3syZp|F`q=cz$SpUZ?*L&$EkH z@ghX#0R{BkJ$;p}-ab|^uIhDM3}`iD8{Y@6F(e!sRx6D9(*@KDLiJDj!?R#p&^#D9TdAHmxDMrnS3os% z1RvpMSHHZvS~qH>qfq)PV?VSF+JwO#Tr#L#gdIRi)J`+fHXb1E;T0NrT~KIBwzfFr z;Awq^8j{wS_^^w<_&jlpplzeHDs5s)3vH8Em^tg<&)Bp_Gk@mHnM-%lpMK^Hfz0u; zfo;yL#%2==QUT_AM!O}@AnMs*wg3qx2~$L@nWE-c_Byh5?SYMtnP8cOXSl_I+1%u0 zcx7vA1js&Xh|_VQ?ZO->h+60}(Tw*z!V`zdWdB=2*BA%K4FtamehFOY=7JMY)JU*x z)6Pbq^L}aVQT#Nc3&HT;ppBA>EeOB`sxhz!Nr;Rb=y~|pjNpjo)`H&QWr~6mP#qCj zO~9@AgZi-36I*9)T5}XW4vVJ7=s?_BUq`GVo)C1_>(v35B`XnWp4o3G`vgt-pq=BBY1u8%lDmo{D8RLbFRt61FRVj-1n}#P-8{?hmjdnl|$1zH> zfWrWI2u?jDanb&x(L%-$g@MsKQf)GDkg@q|z_PHq)?b6qO@DY9B_}a{2FIE|<~~Uq z#wl1?kP{z7iZ!pGP8cg>4l&TcJN9YheuD!JGLoV*bOrHE(Kq`f3*&jvuMe=~20Dpw z#&6)Si{B84<6i+t0i=U74E=5u%K-(I*bj*bjDL%OBI<9?k6EwdpHsi12CRJ(cjA5T zX@b@i!LH~RITVoRb%JBu5WzC0If`R~_X(n@D5k0nEMWA&0CO7yF2;ptg)QAFCi6)# zxk=;epI~pCx^juDX9r0AD1`?Y^?>TK*88hLWOa+OZDibE4hKb6!{UG*X_{YqpI|tX zonq$AUiaV|qXWL!6cag_v%FfT;Sk!{JTvAxIoIT$W6t$t3+9ZQZJ2XzHZnQyeBO+C z-_wvW`{Sm}`5!fArGdf-CQbu;nzK-X*`$>g#*I$WLxDtd3q1g5j3j7Yu1ytvj>px5 z=z?tz*Q54dLo;sXq)Y{X+K!P$09Bea|*)-448B-xmmHU>S_Z8ax9V4r8$sG%=nFf}cM)UGUcM%=U|Mdt>@A8?@30 zU-K3k*$o~VG-$=z*De%ah~jUk)+V2`ZF(ZIwWKi+fq?~#pn+~>Mw-H!U~ z*Slt)80=)HP}>^4j>l;1BOJ+m8BY!qo$eax{@v<|+3#RH!NzF8(f1oKr#Ewc4<7Tb zac_al#uPRhIh%D828?~r=rQHy_)Qsm6%zJ5WyrPcr28RZMD-)=BM}o2XxR~o6%Z>f_R{KD8aW6d-^O({0(1YBH9lvr znpuD*B-(33JV`}w-1?dXdW7B43WJ{G>b9LKW^)ZANkK?RRC5$im!r)zQq0_BNCkqv zRDXF#$cQSFdYG19pE>FK#&8V=^7`s1zXQ{sSlT5Cdr!OYvzTaa2aXftwgwO-NDl)y zh=0B&d{(E1iT3`=l25_f@{dhA9hf+HH#8-r?Sb@0x=U>l{=hK0aa>?~HWy#J)ZT$r z_}72rfr#>+Z2^9S*p3(~F4gl+=Rf>-$n$5z4@qC_`Jvase=KXRPXa#7K5%N-zmL}5 zw!4E}?VcgbjB#VOc@W&NG$COp>`S%I9=N|?LB+0K6btqY9nwcJ4<5+q9YD_$=Pju4 zVS_52LPStXf)wl&o&$RxqIlqf+FCdUTPjP3xPa^;)d5dCwe6V8J(Hb7_SXm^cpO5+ z_lz$hC_*IgR)_XJx0KZa_K*7%_gP0uJ?VCx!IDeK@;?-GJ+B2|O4kCBNZ{)sC7g>% zp+rgk9BSUaEBaP13|>~uZqlt3U?<+SWParHV zdnfYbMiX7$qSt$1i&E0KHzVcj#3F=llV@YgkZZkDi&_-DPA zf!JU3@VO}g>g;h--SLQ&*M_q-cH%IW$whmQZGROU`vBV zJy517^Ki=jKSr^hSScOzq~K!qV{AvvPl$B(IyCS-#1#YVZG>f%GT4-LJyV)H_dd)$ zihq@nF&fHyTn#^l41qlXu_)1mutnE8tcjp&{H}INJ5R=;^NABRyxrk$LNJZg`4xQ+ zUq&xIC?ty(P$U+A z{Az7HRFo$;Hz+#n+k?R^&HvgIn3&Lo`3#vSsSQ}q496fkOWum;g8RX#$z_2J8m4l{ zQb--i7CSi#DTu7V>bKkh(_kEz5|KGfzNwiPqD7VwltwxxN(xee$7MNQZC5lo->#|= zx1{1;BIcIUzk<~)!HmUUz5A^mNdUvl#` zWxm%v-XkOc(tjlJyXletiZvbJUiyCX8#~V$giU1UqkMrZ7RQWwe%-hT?ic2K(?}U4 z5IY@)tVOZTrxuu_;ueeDHtFJF+u9q98HB4rG~Kn-~kYoxQ z?(4=$=t#VHSK_9zF#LQM?I3yu(jv>LFfgj=y_QVdETJP+b)^k;BBi71&dB>E-OeK^ zcXY$n*M%v|j>rVh zUmEa`uJgv>vGURD!evdZcU=hYvmUQO%Q%?nqkvqPuTDw14KnmuA? z*UYP~2)~1(*)xjMnR3p(IP>D%M?;bQX87|CRaYE7N7_+&VdjOWKI++Tf&mlZ@4qwr zDR4}-xtPl?uMcnkevNqI(2-*@33yc15AKaD(Fy#$!DYMeh@aK#1aKaGaS{}%2jAXfZG zrg9@Ce7q2ZTNZv%5L75c_!u}%dTlGfs#arO-AE`ouNbRBgMRgTsAYo^*4v@Rv3UGw zBm8oui4|fOJ&Evs7zRvoZe_}K=#Xn_<>zs%TP{IUAKVYmtGUs--T;>G1)fK^#90r% zI2^r5k9`cP6*DQl61!$@dv>z6N_oix^VIK~*FO3nTl~sFN$Qc|WZ%5@fnX^EUpTJ! zGWh|G>&zQDE^vt(9ZzELy2gT25vL&@jP=IOkCnUJ=y*PE$t(N1M)vX~12Xx~2ID_> z@8v%Sv7a$fA^ffd7~_Edd`>u! zAmc@6rcj?4P$pjZPG1p~rR4xvsN$E?C?f%Nr%^*;--!Rq}md;q>j5n@i0|V_t8I)6cte8|7wfa{S7a5Fb%}^h$WH;+H@tv zOY?L8<@}2|;WoEB9E~UP*-Bx#=yH2!z45?3M~hTENO1mSfYqaae>Qoy-*^8?_Q~86 z_lu6Z66d42v!&&^Pa>}u6pNBlJkkrq@l&@ecdSs!<`eN~_~jrpMy15d*^k`i5bu8? z`$YDBpC3HJ#?(JFE{d(91Q`oR1#uL#cbZY3IWh$Kuucf$r888}{mJ z8D+%pat0!er90=cOH=iHu2x%U@ZkoL*kLqYXGnVIwTJwEAzy6Hb<(qE+Eegbj3^C0 z^q^xr@_^I%QwI$mv;^9r*b<5>qf~dWKgiJCaZ}N8sxN|QH`EtFZupCkrn~yZ;p)YU z7X{KUK{MCw!m+GQxnGDbM{S8_TV?~KJ!&beJ!(r>qh@$}Mq|{D-c@*8WYD?-r}GV# zey&NPn@}R2OisQy<}ta9)P*EkkXt0OT#-2k=Rhi02$KlZ@1%Oedsf>Emk2?tmRc(O z+;LFtl1pP9>X8XDjV`ur72i>0Oi%sjZ17g$-f}jT%9iJI@dba?Hyw;00j=oHpO6pxrVr2W{_t2l zIPI$(Lmu_ik80jq5&T0cU^9hA68vTs;{72MB?d!crS3FPKPAaPdPFMBWgN({v|*{L zF+Hb&!~f1; z?7^z*-)EW`cg0@Rs?l&V(P|~$Rq^P)4<(Ygd@2}wpyHyUQujE{r&PPck&7R0WEn1gE$NL;Y!{`20JpPde&RI86s!a}T~8 zZL;PdoTh`AfD!JFLc(Hp`F0a8IF>N>vI~1aiqv0fD^hZMHA3k@z-93FNoNY^qXQzL z7{ME=lvvN_*OQU{<$5xrl81jCz~Vo#BU*pkIFfGQDvT9arsESPvNJ{|f)*IoBp4xT zbm}A9gz!7+ z))Z`h2)-a{8e@bogX1Y;$Y?T6*2`GtBuJ>?iZCON8H6lI*x!?g?`24ugzpQ4j^XAA zJHQyQr4PvFZz78DL?v0f%-r0hXlSHmr5Hz8#wqd75;w~MRa`Pgh*7O5R@)_Bp$j%X zC@V+U`>HP@`7ik*5np+(l7``X_^U!+O=ywrL^PV%5Uy-NrQWVo`jY=n`YN7K;4_DV zFj>X(TesQ+!I+d-MaHZA7B@Va>IH%^%l7E~A%u3}4Fz#c@}KdABY29m2}Hl2Kx}XC z3!+6f^Ptd55{7$7uoXfXEyr<28DbZ7uLhkK@l2Mp+(DiP$gqRTyGTnz$_CCkNvLA- z1|{wl{Dio6l{PS}+802P5NuA2lbLg^39NDgow!sx1kAQ*~NAKAQ_a)m9kA45I5EB=UM7IsN*K(OHj)nC5)7uxV&gaid*eT!SM zzhW4(yWW7`*B>y;EVIuJ*18*O3Yp57)eFz0h5|fa2c5(aqlu6WS%T^Y(gUmg-XiG% zCVggF(pZiw7@e4q-0A*>*iT4O;&MV#)GfB-2V5Rs#9scDdotPOw^4+Zg5?9cloEFF z(Qaj_EVyE!XjmXkY)XY3)UH@yGgM(;JFa@B#)EQz_e%usvLb?N#5x&ZS@06E8tqM&XLsI96A2Yn5{J zfv>RkC{Kih5Gr!lHo3$o?^K1nYhf_%@MCtzRca%uS?KdQ4`LqRdNBOgdiU02^-eud z)6!~b_>Uerpj_gW;PO}xSQY(l{gKbD#Wd-;D=(!HP>VmBi?0c!UUyMiLl=|1T;{$x z+Lo9W4b=pnVA}5CXK9aAmfG086%~p&2_gK@UOx^lyUMqlg!Fr!((*~o+1ZEVM!%a5}L8=ah?w%6Ki{Y6LM!_*(|L%5xq-Wtr$4~Cm+jQ7aa+-MvWhQ^fB zyeDL$p4?-r#8Wiuu5y&5aLTbr+pamtOQQ`oa|%Oz-m=NoIV}Heyt#f=qS2cBE6D=b zW-)d;i<sg=526s2Twx|lAgY9Wn_q$G;=`bz4gg3qUm#p%?^ z($Q=@lQ=YWp)*}xDaKN%bn-}*uKb~>Du_;@gKUqYN0L0EA8hddbc%wCipb!B{(tgC zA5h!{SF|RT6<6MM%tfyhn1^#xm&su^xuZ!ijg_!j?@;1Lc)XyXkTP5+cExxITZ@mt zq|bsVQA846IT$cDd5C8nq_0rPrEm8=mOXw@uh$w=4^1^{_4-V``e+r;^|>kBnVK{2 ztP)4q(kY9Bp2q0<<4a^4rTM^mWH4;NwE(;TG1ll0JOz|Vp}wSZGZP9>$w^cA5Z0K1 zy^Hu{I)4oi5V=qq;Pi#V9DRbw%>jL|x=JT9e89S4Pvo(~To|yIqVW*9`mu7@$B+jU zX9E1^FG7EG$?h1i^52)Ri%`AAT`j;HVGJ-fn8_A1w1aHY)9$u)<32uQG|F(o1Gptf zV3;f!TvtQ5GMFR@$Y>i9G|RH5!>wn)5?5SsihZ~96^MMoT35Yl+j2Yed8ZqpUMv?L z-=}%-eGuH290_^5OcaNhG|JouN`Q-pRA6miT64Plq7X*A!!>;y4s&RJ)!O#f_V!i2 zws;O5&fDjhQ>B@R0IhRIzbAjJRpc4vEqIkXl=0{H3uonYS|0uip8pdQLRhtyyZNUV zCPac4jql0y8%wIz0sj_gfc8!^O{_=`gWPjVDmbi=2;jE}00#kYgX0&M!4r$ih+^MF zZV=q<8Fz4-$jS`lC%s7&nlLK52$`8QC!C~PZ61JJYB3iuY;Yp>H+sOyjUI^CMfGa2 zL?S+69|sJi{Gg5Cd1o=}N+`~{E9=rN*WRo%JN!+sHbLLmK=oGSts&nb{E+>z7lcdF zAB`E~90<=bYNC0kY>oJ*UWH_72&x+ zAe)&k*0Zqv04dpeaXOPd)TjES_~Uy!^&w+tuh@{MLS{-a5gHgK)3U^v5>p0HZbeej zz$8fJ5M};?fvZ0#XHK$_-MR} zS~y%bcmQu7OO=<3M#eKX94I~Z=?N*)c}K%v{RT|PnyWnfoZtZ#_+TQFRJQ(!Uz2-y+Gt)!_qc+DRUtC7fCwZ0}G!O3O) z-zL`+k@b&8@C`Mx!C%xq)UTL##;=~k=>g0JrGHDK6$>33;U;9)NYR7M6U+luYDCm? zD6(}MOCS+^u+YgHm3|=(mXbf#>!f9;n(?gy|;5us!}NMC5)Uc^esN#ZwkMUxwyu7p&K3fgY7%sai!nvYMfOKA^L{F5p7J?B9 zv_S=_br5Yzq8ij3Oc74vDG`WUfx|udNtz*eujFYjak#?vLsR7?*Gw~vD6!^@YpEO# z+nHJt27aUlN(h&r*7#Mf17wA_6~7b^#hhMFH^Oy2+<+R^Zl_Ojs_0Be@b$tu9nO;y z(j(B`dR6#!$XF>z*j4NRfYgChMU)hZm!QBE0u>yPxkV%zL7ooU_jZD+>yq1}h2}%5 zGjwOjsp3lWxG%ZXV7>nAeEj|K`IlQy3jJ_kxE)MO;b6HO3`^;tpa;TGb9KA_@PY9j zL^6&aQd0>#v8@7G5~_a4>@Yqt%)#RnYno*q?1bz}lX52^6(RKW$S-c&es|!OSvA(U zdv05e&7Ffw(B^Z&TOJBvhR)99^w45SOA3LIsOYn|`rUJPKkJ^WY6!QSy|r5C$o9Fr z=B7`UmcqF^YRK&i_R4U=&;NNOBv=BktFgmajb;kwR@fxyQZ=tTLQTWe)yRaA6KJ|? z%t==G{BPK!iJ~WwjmBcRd`in^3Ny!{(toMwi$$}ETXOkACaa|?pG!pTMM*9tvQi^i zL7YRUbS$3D-mTw$_&9X=^lT$aq`9 zNkPMEBF!0&@Tjj6Qz2}uZhAcL6}`9S177jH9?$TP;l1z6i*vb1{C-J`zwgKU{U7&) zU8h}&f~W8=C{m`D*CKv!90)Qvm!QnbWfo3AtCmvhP*NkMKzbQh3*`|soWnso%*rAI zGM?%PGy`^}T)MQr2{j$77xhP5EE&-n5;xIdw|gny=XIng%ij=ZR_6RN?sE$Xgk)wh z$4Vs@O4LcQN^QwOIow(m6AlVsU~2c?CNWY<&sM)`TW1FXf@5G_D|CtfEK&4V;W7)*t~96H((@ zVt#1R_~PQgJ4RIx?`RMkA(p4KBn%((o>%s4OuvN{YFwXY$ ziK(d*_t4XyB-r}oYxT#euylaQhB+tz( zKGl6n6rol%2txF7!W#<7k;rj+r>Ygai-l1d6a_wcV&wtT zB~IhCc^kCrJ(P%g%Jw$fyKFD+htC@LJ{Dt%Wtrt$Cfg_}loMO2aU60nl#|sZ=5}zy z8TPT!1?N8C*rp=Cv-ghq(Ik3`rzdlc89!H>J6Lb_U7fZs%? zNo5z@MW|Ic?(uPRpU+>RV7^>#lfOJW)(}|gxgOyk`kcX_6N!+$9>lci?stk0qru!CbmBk4#{_>W9yGN9ZJFfV{xJdV%wcGyL80M76vS2|XE#ER>^#fUA1=gNLhv zjc=pOjvNgL+TsI?_>btMV(|zHwi9Ml{_;D(8wK9*SkEAT_wMvUNiGBe1-Y~k4z{b) z(~u-_F0;~I64Ko~{Bll#Z<`1hp=5-K(S_qc+T&lLeA5aK1?%${9SaWs*Nk`j64i28 z3FA9vrfS$S>D%88L#G@GHzD2-H;e8|IN$KoFYRDtCN1=_va-;Ent>7&f=Z=%EGnAg zLIH7-BW(%F`z4HUnG8M1M2w#WAtq#q~Rd6EADx++4}(TR6Y z@t!DoAnf#8kfC@GKTlXl)S9?U$~c30T!cRi>;i&|z=HtfF)bP1(Ti|C?c5NG{`M9;B9u!`M-B&b4Y@q%xek{ypF8jNZME)SLI(4x6ZNJ0TUX|)*@gRbq#@6I=$2--`eEIb z$xb5|6{^LAd~RQmPDvyypde7%6P!oRa+mIE+_`#Z1Fy@Eeu91fKT)3Yzvrz`gBTmm zPnaRfP3(YOe#(^VJ7Iw=f=o+LZP-b&>@30k1rp_GDxh5vb_N4M$w2?>`D7sGK4cGi zAA7S~6(4`h8{DFWTTVb@gDD$`cfuH0R2AQse4gM3z1|N7pW#dUBM(2k3yc5`z_a{# zQ5)^)*o+W`0a_RXf?BaaVnJ)gYONX|>GB5spA2cvB_Zg2@=c;DzUfJC5W|QChd(m+ z%K3M$02YH;52C>@+MNb49{1URV|G*BdB{A9@387)1%?p#av|F&{JA&=@RrHj23~3% z8Rd;|Hpi(An4J0CO1GSC8d#81HMeVc#YB$lU8!$&0w}^3RCmNreD2{^?j+&jf2L}7 zh8gi)so{3VsUpxGNjeR!0D%(4IkAOZpFGNF)f?#&I$EbvYmI8g;Vt={AvE3sZY%Y? zgRrpV#P?LESW6B6*SY8BI26uP?-le?_GSIhN-@_k5%!a*=bb^1)BeR^)Lu)qH|ID+ zfs%nik$) znbl)ZiDv5hhX z2@56ozrm%jbd4IO@Wv(C* zY*V>1zH($%OQp0~5>uGqQ?FFI+?|RuF2hbpFto^5_xf-+762*EimGhtp+<+7>p-^B z@EL{TDDL7zFM>&U^uonH)F2KmL2*6f1qXJAJ4C$!|FwdBbt&#=idL9#TV-Dvlm&y?h(o$lQ0ggj*olXP^ND zZ?7=l1#)>E7rN)Fnxg_-&zPhHp$~E)^fS!)LWM=&P5Lr-H{iQU=P55@LIS;S<@G%N z_hT^H1Y$d@PWMH3_zZn~nV!z{u6*gliOmxyemE#^%0a`nM?MP$+IXRjXHq5YXRM|Y zoLj zg$q+8SG7=daRi_+sFy^KyD@i%%HRrm&Uk_hWE^LQzn-^y5+RIBPZkxs2?PMX6m&P@ zcg!@L0avX`u7eDE_TyKzJiKQAha;5m2vd`wGoHlt_z_B|CD)kvio&Vnjsb{e#1knf zm}zI0^c|wZ^Hpeu{~m7hpVU-$Y4Na@@INRd>r8$7;qxr!VEDfYKb`vIY;^HZLlq@C z{{Yj6@{)`scSmsMAxk<|5UYwiTA7AQ^e83|fRL^Vm;rA1Nd@jc#AAg|le$i#Btcc+~oj{xFINb^9IecDu(!$IRU#)u%C8g9|on4>$s0(WVS9E^>$cW~kpg@Mbd z%qk8?+By5;?2E(y0l-+w>>lX}FY{5rAs$YYD0c#wahry%AJdNgmQ$To9e(_@)}MLt zk7;J*MIQe_$}OK z!|G*=)qGi9HwL^mK<40jZ&0W2uF~+K!`tjpM;P8s_p$w>2aHW$AKh5S zXH?V(HwWdko-VVeKDV}@jx2wVn_Yg3!3BK>`P+mMQ#6H`5*zV$wh06CWH%paSs?UU z@7G2j49uR&3ml^VF}jgfI5?jtcnLfPeZhdpHXaK;n=*icPZD6F90TxK?$iYQtFk0Q z?2Jn1RO7L<_^Kv^vcvyRL$ZR|@Wj8!TzD>UpZReB?;;bQhi3z2&?5P+QB75{Ifcmw zs)HD7c%RxkqE-_-2*GAA+Fuuo&pd;N+G{j=tBpptT<&(EIQmDr`+eV6e2Lv%rJ{c9 z6QXmp4ZgJ_2+Xq-K47FH!aQ>i1e$d|_sstl$8i4X$Q!<59;^^tk{4U4Y|I3Xi-XeMLRa#;V)j}yK2b(36 z3MPXxaQ?$;61KX%)h-H;b-Nd_rgQ~U&x%-n4f{N5Sm6V1Hb3NT(Zz z=F{nU_ld}!9_Lc?hvrl4sdlcG&O$FaqOZ}o3@lEBG#KORv&n)>k9AlaX+m%Jz|1+C z9NT`a?>DwRUF{m$-d^_^9HSAe%NIrxo{jVnV^1v*A-Fn8arBtT4g59JxI3BdceuT= zarrVtL!70t7O8D6YeZm+LD{--YZP;n*xlWh*{V?+kcgTB_7gqUEDV;zKN0S?=Gmog zcwd*9c_7Qy7iqS!J#o3SrBKyS+U?AdA^9zNsxs3y79Z8ufrB*kB4o5+9b&CPgFylp zKk86~EOsHD-K-0o!9GZ9*etne@@DLCzlJV@iHP*}fs zbXHJxqLZDqqhDZAAf%f>z3O|>>L0(iar&Q1M@snLyGGlTB^G5*_i1}ar=_*EtLfpN zqV1pC+cu>-+kh@Lgk1@{fIy8RuXm^9z_Ay{4sHlIimYuI5MNFV^0}jHYs>@xA`J&6 z2d~<|jEKyPD}`d)e@bbT1ghyA$!GpULs5$F`eHNj1kT z^dt}AK_wj-<*;Y+Zot8U&QuUlEUt@8o+0lC5_!11!c1;`P_POK4Yv>0 zXv5B*g%>M?0v725yLbmFRuJB#3Eli+)jvPEJs0`BnkhtJsMrq%x6K-ZW zlrot118KNDb#iL66j~pHR=bnIwimPoe04kf!AStIKJW*9K_6aRpduw~uN}7|wA&rF zm*9Q{Z9v$8JJ2d2rx*p4kZzj&gZ>6OW2!V@gr8DfUwpJkmK4yU@H0}{llH~LutN~N zj;Px%I^gpV^p*ls{ya+diek`X_qx3SQT9lFkJI6Z`IM;7?RCoeShgfT8a>lPepN(d@NU*)c3R5_Uq7lQGCBj`pVb*J4K@&~X^EE3{KQ9JS$99@W}K4o-gba9Q3euRt=(D0Y)WWF#p{LDV_J-z^mMxBY5 ze>d^Y_-x;s|Jg8u1OwhXEEbYl)#v|yE^=9)yVf`SdY@ZFI`C%&-4G5bOTpM9@L}6q zY~N#h*7gIS_8YU6I83n-R;69;zv7-dqwiefqZ=@|^d`n?4A~G#jd$0&`5J9BSw3st ziI+Pj-Wi|mdy{ytkw?MDcSWAPNTl>jsG&h=;%Xh*4Ult5_|xGd>F2-eQt_pi+{eD> z{CVY6dnKu_eC)g9tw+m#;b+s&d!AZ9cP@GCVzc?kF=sRR@sS8ZpqGvAB! z1#IsyY}8Rmv#?6-*K$cjk>dhwTYEZ?o;q>jDPi@CcV=fE-_y_VGLp{O{(yb9YYghX zJ;9KPy0#M|cnT$ucY(i!4TSK{^>zd|?dtnr#DIHmqXyKgd)ww9Z=i=soC8_=^ZF=MKufl7sBRGOxfu(QeZA2V*lmlLINMOoBjP>^-##tQre7X0eQEttEVyjF;c5 z#u4tGm<@+#6S#=08R+f;-!gu?8xG6`(;{GQtZjVC zqJ1B=wqm&nkl6VCP52uo(@T>u;c$nRtt~$PWcqAAIcn?PC&|`orBemFr)69sm<~iw zFe5i2u}G~2i5d#5QJaZaKk&gEZjr zM3O9YuJFhZ=?TVvdcbuBq*%a%!-7;m5bN*(MT|jdMN%DmQ*-g~B}y1Zw~&94d}xKO zY7%=HcC=)b*o2N#D_%bXhZAKj_-g@g&l_A*go^<$t3i(lf>roYK((5zN)o^XIiHW&#SPOASRSNCOD8GSbPC>6@{1> zc;JP5$G653L(1CaxG)#R_Djl1iEJQ1?*E)449}rR$jnTv;0MaPbjDgE#|QGdSc{VJ z*4*#bu-DBZPRYb7IvaOLNaYAcG!h4o{ab4(U4&1zzvJGkpyik z8oF@t;)PIDmxCA9){tgobYalb4savGii$iRW^Nj8oS<2RZxN|)===tkE|$|zK@g~M zkNtq`7abKdeVn#>qEtIx@DH*Q^JR!uZ4$g-HfqJ-7jK=j?R0@-z2>Qd^?Fc%wKRQ|AW%P{& z$xt`Wo(kvLj-=&WTV&*ue=-vRJ?NyVj$*Laku43t?D)pdO*}^9QuTBd( ziL9^z?*>EyCKYUK&?jmo)rIHND*qAMKm%-jYzw~u-FpgUq{wFL2HRr{j#U6!7Tl&M zG~67pmq2WD;|NKvR!oZy_G3wrM^%NGOB+SY*Kqh$$v?jn5`<7V2Cwx@>CluN2_jH< z&+UnQWjV5(SWaaZ1Cd#aVrIHAf#OgxV_$whs9si zyQh53+|;3b5lQQUvLp3@crCG<=-Tt?%AH|{J(gG~xxD_6AMzCbtF9o;fP8x z*`0}HmSZP_k`r=+G5!I@pTW7=7(bIuYi$DoA}I`*=?h~s2Sr*KI-AVdkHJ*IFoEN= z6%`^-1c^^u>fq->C=i6YFrU}s8DPNf6$k}(U-1#QTJ(AY(fMR2Ra4rIlrDP<@c=`G z5D+;ow2TkoCw3N+aKO=(zoby@yG0(+Wts$rd$%FqubJ~+y8 zC7pJ~u4(k_OnTsMYOmWJ#%=qP+^h8iU!)yEfEG?shy7m^TdL%=m+d}}J&lajX`jzt zcG?k?>9kkuP{E|&v6}LG9c95`=dpKfo(3)<{cgD zHLYFifLC`^ERq@&P7R^0naq`IpgZDHAoMMgk$w)<}$jv!oIw5Hbu?#-RQ34~2^K_QQ6 z;qM&IsT7>XXA#!pNar0sQS>=K>=bs^U=h_fxy2Fc09ZdxIR){k%ZCN%SxGuPt}T~n zf6nReK!Xn3JjuIc9?`GdQvPWsni+Bq@rf`Zadp_eDYA{iWmyhuS?YT}}Nq zb`=bwTEp6bkr+z|M-6@;pruyCJ`ZgtPTm?K{*bzIE1Ez8KffPzl6X@reY$D}95{TQ z+{~1QzuA|x*;!57@Ym*Q{<2p-?uj7xUoa8yc+SStmF1vdOGJHYBqm9*i0Z=#V&!7vb$xoE5|=v~_I|2l&*r#8}Z zVGIi`t~TI%Dk9vD39e!d!#DN(wQ4PEM*&=%32JU)DU?tP$v9YI8nTk0C@PE3pd1lJ z5n6^?6@f`Go8Sy9ew>Cmd#@nKtL@^kxHAYuv>sgXdXvKU27*q+W3*RgyA<|`-jxSi zKoPLxb`j1{G0W$FhVY|(iX$kvTuH~zdOU$d(2W2)@p#ntmnx0@(EM^zi)!)Y?75~t zn0Lttznxp2_qx3E=VlR+3QWD?@p2RzU$5H#hp5FGW=d@52pUc-rB2zatu12)3M1iMyUJtqad3>uL4q^wiY! ziiTefH`MlY_Da*8m#gy|8#$NglBOjA zEEK|$a2(#McPX7}G+dU$QY@^2vVn8*z2rC-AqO;VU7QS1>ckw0whO%7BV%Noqm%1v zofva`caNd}v}LGs-i(y22zv92*PStQ%>Qv>Og}eE7?>5SX(VHT1*LXm_^oSCrJ*}} z0c({a!dXt1#afdcP)iUBVMfXbv<1{CF0j1_!zA7ykdc+y01?*A0Th%cCHU$q^b)BB}NHWIDR9u?Z25F~N@4|yO*eKMy;5~zGE#iEIb zn(J@+k?32DB50R<i68h+h8+$hp>R6GNq^)C zxuHO)Ta&|n)>W#tH-O=;~#3r(od`kYsd?^x>{}?`i?_-_4wK2)j>BY zb-7OvY}sVT_b}TO%LG{{N!TPIB=IOzm4!epblOstM85+39U$*WJg)8-*$Yu(vtExu zU85t9FpMP&Wi^(b%jfe#9afWaTGq-7vUY)PAl9G0hcUzcqL&u@f{;^Q#wWO=4F4*y zP}0M*BHr`;-Gpr#`F8H6bL|K=t=WcP!VNxH~T~4C1aw1CiXc%ln3LtzH4|6;i)@Vq9x<4VE zV3m{=g_Tu4Atn5EJ~T2oDXaJ;{h2Zh>E6fQz&55)#*oNtO+_Z~%oiF?38u1?*a@ZR zA}$CuI*JNK|9|S<1x${sIv4F--Bs1y)$i)6>euvRdb)e2Yu+@&M8jaQ(|8ewXqD5eHoXR7Fixu}M8@gkNUWqbb zpc*><&kMNigT;k`WSwpuyXf4ORQ>HJYji$$>5`)#t${AXsWig-t?1|r1jA$#DZFzB zSy=c-$%>9|8LD3mK?eMTV*ps_EPH#l5(stqg?zzO3pM>(QO-7WRXL2mv6#_fugG?O zI$M!@tou$io%r3>>ki@Df`u=6-d=pFQCy7uPo5Mj^f^H<)rG9{>=RTdA4D&3Phc2Q zFi53_>0bk=yM|^WNVVKB0OINvs!tcqKQUsJV&!n*P?1<&f6^8MI6a8&zj=y(y|Gw1 zQYgHx_ymT5|KKiz28q(1Uy8CI=S9vH-|uAIxh1WfDvlie)NN@XLoRe}#+uq->##s7 z;Tiw1NQrq6symXs z-flC`PJBI$pBpgEKQwHigovXajhZ8xgbRD@>2rBNv#xc(*{F$cWfkt{4rT8ZpTiyJ_`ZK`^BZs3{Kjv> z58fi#0UW5~dvbl=|DW*?=N1A-|Nr6P(=HE5PZH}e0Hk1+XNQ5gfVRWRF|h2L4Lks? zkKUs21d;ExS>~l7DHe_#EjV1n2dDuKWHv&dP?F}Wmu=KvMBPuIn}_VmUx`_Nb=@~& zwIYdje)LyFuxF-ajri)`(R@3xf(otm(Zd#*CE3b(G3%<8*TV}lftkx*NDQiU!=FcmT@9QfjkSxrUfrDNcb}I%n}3 zQMIq3WX34Fr}76>#=fYslCdBKj+u9)3yDOrP${J6lL7lyw)fT`jJV93GR3I?75g9U z{A%UF;XOAOis2B-=o^N=kX$#CtaNMVi?^mSxY)U3jN}UTGvRClI8!S);T?sUFb%!z z3j$>lFiZrSv6@yEkwmqK+zx~fMEXVg?Yv&QD1*=@90s}+I@uSL4+5B`Q~D&2Y)A0N zxo;Ht`*dey*s)D_#32s;l;KZ+93XkK4`GqgMmqWk=zQ^@V*bVbrgHEsX5=iZzrK&6 zO*t>C^;80o0D-<(FK(cX&c<)De`L?XnmF!x4d_~+P776nszbEl5fM`A2~GAS>bQ$O zX%syK9H0!uFs$$H14SCE2jKc3WH-JEmlR-4VleOsQJFVfi2Y?k@`hUpD-juK4khEk zS4a690Hr6@8;+;r5jh?T$W|yA0?OA2YGxpG{}EMbDN3#$jkgk!^UBDxK`W?g>6_uF zZYfNmi?!$~Bau+@)r(R7Iu=lOM>JgyX;CE^*U3Nu)B?DQBMSbLnq8_7BVd5PCq_6R zrWMf=GxQOrOX!13h+$Dgn)1sjV~5%_l9d@Jk1M%gsbV9eObYsoAL;_pL4c60Eg?Ux zh46Gs)Gz%y`x+{~QT>p-W4FPxtJiqUM8&DCplT<4MFrfA;Xl(#ef(nXZMJ=D zy?#tj+3a8(VG@)TP9*T*;@FL_Th`RAwAr)G42r@xp(MA-eJim$XnP@Ps1#8vG8|O#zmm-Olju~$~q9vtj)Kp$uu-C~-Kc3H9{4YUn+x=psv&!l>6yI08yLiN=@4hjg zH{CCTOz@G@+IA|MOhcm0BKEIRomdM!X{gyvH}K!h)J5Bp8dpzD1Hh2wsPfD+^;;&w zh6Ry2Le-+t${2;WBat8#LyAXp(L^A%Ef>bfM=M+sX(BRmmHV@%K@kOMgQ9}fzbx~2J zZjTkV>=G4C_|vF_nuw|n;fZDur0@GG${_b;!>Yvd`Vmg%@FGDrM|*`l&*D>W!jX$O_#Ij zYGR(h2LGpwlZ#_`D~?anm_uqZEi-s1fd&F;?DVnx#4CpW&$YGnd~;WR7ydT$&8P8_ z-p>!)_vf2hAgk7BoF&v6s1sQUmx{#; zeL6c`gQTSxDQc{XVo@p*5wJUur$=ymajAIciy0)!FE4DkUgu(lWiGC+7HN6+XcIYr z{Hg?o;D9I~btrsE2X>E4BvBv?=~quY-bXE^Q6TYIIWdst6i3l}u-I^@!NZ82&V=?s zJ|@mLRRPwdiKb}r3Jm~_7kK{)(Z%BN6RJOW@I&YhhG8K53D+WJYKXjz6H5EwLFx!l z=cDPZ2?eJBv=6?N&vY2R%YLz#jt4M1{hBFmMhCx63Zu)zFV~&K8f|F2+ih- z*Cq%PhP2Hs>YWSigJT2kgO&;h4|2N`^LvpIrZ7bj$|iUwoQs5)z&S<=Dxs+;kl=PP z;OT<&Ehw(e*O7R_QMy3=hV#7RD}P{%a4UB%5k_?_B2Uoo2|03>&^l}INw12&8}u6B z$m38`@jGeZT`P%aLr|_<+1R5IWOr{1bMD%xk?znAck`zP?2|o+rU&{b3-SkY78!e35)JPQ?LzC#gRiLxhE1jHM*N(83Toa{}{n` zq)NMx64Vd}96$sGS-NZ`mN;V@EFsO#7lgi#2xQf5Ey-IUW+<=>(r)+k9 z7bPkSnKAkP`{gkfLrSh5NTf50zd|;XN2X$5fCu>H?uk-Dlf( z6~>v2!LttmpNQVVzHza-oZiBJjo!lFXPK?Ng};EIuoa||1>3+>1K0|U5%B8egC9I= z2oIfj1d7bo2E5=ej|^k51^M%B4P%7r-NG48!+j;+wk5m=o1q<{{3Unwu2gyc#T*(blm6L zggaiLyx_*zX@&76C7VFr{k3nWrZaP&C-HlPuBEva{ z#4e=lz{2EUpSdP=%$>vbfx-h!Hob{a&^n_fg}QshZk!FR*0E!Ys8CMBQ3%W|HS*let-C~y(}8}I&J2ciPJ)l?$Jtp z24{KI?(M0?ipNO-0@=F^y{Uz`oM|b@O@VU(EmcH%%;XokV!jf>r(-O@{JiJDH zCjFfFybS4Hg?^4;2;_CD3f?@>c7nkNj&UCdkN4mUa>Hg#OPf`fB+)ySM zBCfkee@DqC&;r6tWR-p2GpWBs;TbMG3>4VGHztR(J{AAvj$JK8bL5Lfcj7_7J3lHzgH4zitZ&+ z1yj#IAuFfPiDL>H{^`W+&Wqj}rhJed?NoREWmE6){5r-!60iM-7|82@tf7Ixcub_x zA93hiKK1k?VrkfXAkGUeu4$c6Oq#|*>jd~XXtuOq0fQv*Va-saev+vXi6*1w^1fR! zvnx8D`|xZzHtMsYr%vU|Gs=(n$5MJujTJ^q4Z(FbykSM#MtSx(qq_YrtvO<*e7+pk zXG#|wCvOcoY{V<9XJLl2)2ApbDc%qvNFr6_Id^(U6!Pbi_Ac^FFWdwu1Fko5k!i4{H8u zpqP*Ye%&&0!bg2Es0`_p5h(=YiZ`Ta<=G*be1g50i=X$o>h-pf4af`|SsT|nj^={! zzGGAvkl+Tk_Bz00DB{dKrj$=gzL*$dYyn$%@DQ_k8ls$xCi6ux&blS3VV#{5y=YF| z>esVI{E4$tw0`mqO|1nAI+P>0w2);J@x?LXR5}?g1QM!Olk>3*0RPD%MxImj6G(LM z9Zf`>xm$%^fGiIjL72M&G=TjQCmzL04*KHEyqK2j2S!$hqdGG#n2)G(;ERi&9NAr( z^Hc5)=HKxNx9|dEMg?A6XWKPEP`ho`032~_9W&Zp1h3VNUApAeBXPb6STx+X2n2^{ z5g5?L6&;A9d5r6LJ_V-4>UW;-2kOCmf|wG5<83Qbz>HRl)4z`1!>eRPS9&}^Pe|%` zj716em>grti$>s7Gu5uQz=*XK^L47?|M$y3(m2v?b4F<3h1dr@pG*%SGQc+j&ncJf zB;OHt8Tg!hzHTNqH|09tfx8Gl=QjKz^>DLOrjB&a`h~tnd(o-kKzmi-MTpXV8gbU+ z#Mwg@dK3SU+E8kuN(;4VcPim>qZ9g*aPr}|+V^~|)jG}pQZ*i%dQsCMB@)+{-OsvT zTC?$k5=l_D)DK$PVDcY6?}0H|MyQ7oJ|=+J%P4 zF5l?$`!ly@itmf%M&D9E8rJx(>Zl%3CzM1uoCv*;iUzaU!ubP_&i|;auwW!#$mnT9 z;i8z|jXzvudp)njt%u7vC~8p#pgGVhyfS_Fky2oV1DcXfNz>eiK*$ciK`j1IvsAXG z`dMiqQuL*Lqkh#^1LJDQ)?r?`7#Y&NjpO*K(hGeLfUYk$nm?n1q&d zikb0;Mgxi+P$pz`+$W97-jTE~uIX`P@(>mVCc@xH$*XD!^QKe+H9iEpY3y?zGDqPN zZ^9O%JV5y4EbzJa2weREzzflwg*>2O4_JX(GWZhxfgWb#(>a@Y{hFRm6{nPuKzKBy z*&%g2pxSwj6d!jU#pVOqEma~%em@kl{&& zlTpPo#vk}0h-tM2Eb0ZSMT3>q#QRDUUjtj=(FQk7e82ludUSEclvz(OeiY8`cW;3!aFFV z^GA0yPMnxOIk!|WBf;SCZT5eWvgyUq;e6?N->WN?VARAHZY z<%a?8ch}A`TrO0;8V3qN-5?;qd5AYc5nxxhDYqNOAPP}lr01yNL@FAU(W26ZPAgTW z7BEi$X2Vbwadj*AjT9d|R(}(Rw8qYfU+k^?e^~RTo3%1o-yIKgz`>+9x__)PCLNv}*8hr{WTRa+ ze{|&@t`{A{c+)806|4Rb?9H_7bo8y(L| zzlMqF9}c^46|>Su|L*nmtC|ugtxwF!BwPj5hL6CgS4a8IlY)HJGJ4Q1uq%(|$}{tQ zJw48r#?x%`2(Ny6o=EHCL6;)kk0MhGwA-gGS@bV@T^q-!_kN8uabp@4qm zE*KcGq#n@y)^Yj+&G9DE2FO^Wb_>WO&&}+a$t`Cq?UDaRWH?BY)~y*Y$bkgQu)yM23+EePawy}taC z=gd>92&8c`R@)ct%bg3~d8iKWHF1elCvKhy$U+G66y*(g`6qFyrd2C7nil$6SO_e@ z|8H?v!d(_!Q4i(!z2pgg|HB_fz<+fvPw0cYEV^k4DQes~Drw|68vz6twSy48M_8rG=zwfMjIx!pXYJv! z>|w(!hk5&&vd?_>0wrk`&HE+ z@BA6GsbCn-A_afr+Rjhi=lsu|y1-kJqbsq3&uKIUq?|;J0l2d_@j=WsJi*oJZpP z*;yabs}v?#d$Z=DvZ2M3*@{p0`>pKpiX4ppbP#p-E+6l9;&r#$Tx7a<~tYeuDxP(sk3&C6vK}4K(1VYZoyYd zQHco{GqNlx_BD(HoQJjNN|2a53zO%O0O$wRSV(^a!CHu47juRB477(AfS! z>J#IlYkG0^Q5hHtgKoNVS4*TTZ-7a;S0_ufSD%&;Qp@fga7iK_@(J0yb9 z!xZhkK-xH7&G79Ho^nITdF8wyIl$HsEsPmqVzh1sQdQj!5h{0^K z7!Ap)mt6bcz2mA$aV|L=+8U-NiOwV7h*F0FZj`#OSRO!1Zjc(zc<|r3t{2xWi_?#K z=TKOBOVn6(!n%tWF0|8e3QeOr0rwQpp~u6zA6mC@J|@y96r{WKV$}y>kkS~$nMeRc zkBo@phI;^l4dGBO!9rkP%RV^Oy}X`f&%zn;MRo@mlYP*=w4`S{e?=R<^Jygs9?@9b zGq*rNYQ5&f*s0byz)qWQrz6k9Xg+1Ly$EFmEAfeqH~qiA);`FVfu4BDgI1jAJ>7Zl zrklU`B8`jgORW!2LxW2KjN++~u?j~SVJM(lxlt?^e-3LKX1PVV@DRn^7NU{>3fs{6 z5E={I%tC2FP*b*2<$@N-wV!V1a_tjQ6^N+*#)m&EoYJd2APP+<5)UR4#?gE4J$m$B z=2cUcyopY1zHR8FlMYc`*sE7b<;mVlDAP=&n$5fEbiwK|_j|N_+*PHqvXF2U7_q3O4y_Y4 zLXhNcd21zj8RwcT_Y*&o9bH{!o_*OrzUK>AK<|0gPussiZ+b7Z?bN<(mX)X>E3~o(O1~It9GO(esKPG(ne0uWXe4yfWT{xT z^VOC$W-{CO%E!^1j=e2=lWas*fTphI(W^20#{A0YPoWB%(fQ6-T6_7~-`fv)1hHWK z=@gnrK0dj*ountD25&bp7;6#EUo!XwTd{UwO=0a4o?@Z-n}t-qlpQJ00?26Be^S;K z)A0q>O0jfyk98TY>D1Be6Ag)M1rlB=yQ2^{W=BxA=>zu3>f2c&9n?C%oZVf&FgudA zgGaN!Pwl-W*$+4u(J4xCQ<=#lkkuIfoS}%qa6|ztY+j*=2ioApLUa)E9s+YsG9X(- zba)Qlg}wy==-IJj@WUygn`vEG)4L_47&a=FE}7n$Q?ETWW8c#Gdl4yqvC^vCG;7b^ zRB5rhZX|C#suzVy5AEf7g*>ZpdMqBDxodj*u9?Z(=Q@8XRui&5=Wh3Z;uHSc=kS!) zFX@_>*dMe10PQ>uI|u8k+q983= z%-|N%SFZqk}x3*f3 zPZ)bo-thh8{*A-9*(-Tr{%C>5vvn*XpYpT85O$NuW?d#d+2J+T3fayvfYx#(@gZ_) zwZfo)hECOWL7Rp81vu`ppj}}5qbH+Dt5mk{vCAbZ89n(#u~jS{X&UoJ^Nb;)dY?L( zOeJ%%WHOe+lao(;7j?oPZN}rxGY^P(p_pr~S5PTOvbX_C2aE5AI&nvcP@q}G z9|F=jr1c6RtJwF@{YHliX)6{9ekN)jy7%5gX7n?`NX$wXhDVRT^{spJm4&x7s`;k{ zX31GapTbXsWP`1Ksz4TQ#9gDV<8IMQ3xWnmv1%O6|hMzx_4g zbZKBymsx9_gakTP;n#D7EWvCJyg{qoUgdNP-vQmn%hRSye5SD;d5=F_*O6KP)xj5Q zs=!dw>cs6DtH_1}TA>YvkoOI-*Vkbz*zM)^#_`bj&=#xRCO-A#ri%L!kdE;S8&Dw> z?h+MJlJd4`|3WMMr%)6uyD2*t#?8RBQ`u)nrTUv$E{H3?H#-+`SSOvzetitA>$^u# ztX#-uBAzl)x88;!w+ap+WA3RUzft|+zB{p9=j&1mQSu^g_5=GXo9@^7g_VQL){D+P zd-G4Fk+)d^afF7jEc;hM{NhDe{R8IdJVPf3F&=SkO7Ej0{&(PwK zvkc3tahfVlpKj19J4q=-Voq?AbZ6OJX&06p-x1BfxHg~vD_@_q0)Vs1)BqIsq0*YC z7ibaO*=k+F0wAvjU(lOxcD^kP=iZt#_{t_Q(6;<)&}u{Dw>T_Dq!MDJ8&>%*pkL2o z8|_{AjkoTl^JkZ^9KlB$4LC4QbcP6h!GJ%i1ld9@+{uH2QSola_>Cl&CV8q5^o5Ru z#>=#R=CY@VuV%wegy*uIe`}>i*M1gUeb(bMcB*;Abk?c!&R_ZdcHF8fPMW2fHx zk#zjNM}xR6=d-wP#8ZSVFb1uGbOv`_QkwVHwTUc43(FHnK;QaYs{fC^75YFbwPdz{ zP+00N(nsx+o7U;4twm$vth9JH-y7~ay^>TBY>&@@?;FE5EEvSR=nMM1(`MeEv&ulV zXG$Zp_s@=$GDGEDbxg^y*pSA~iAzk1R@E!hmNyiVy@5nfLnFb27A$uX_-QC{06VLSh|Ju3yg9VJ_;H$wOk{8j*=ZQ`flbr((MH!x=)aJ zvFV-eN05lyegq?vJQDyC_5FI&hF8vKY~y#FSB~4GExaGjFO=Zr*^yWCE)5qx*lot# z8?o*V;)gQOYabz;^L$L#IyUkv{W z4vBPb^ul+r45>#JC(e%MER|0Ks-0_Rdl=8MVV74I%R^8mcoT=`rShJZZ7(OD!`%>^ zp=!M_X*XRDPCL4u4VRkHZ4Xa=SX^@+7aV(@Et$#QHE~7e`9)u-chPvC&qjrg4NZtl zfOt|R3)b=JBBl7Noj;?PM85NAwiJPRGO;H+!*&y8{t|m4TMFq!<8C;8db(8*<0Yx< z_-LRA<7^>gBR&t!na$K#IsQc+>Ohn;yQlM2L_kVe_Qb1S@_11QhF@GA`&Q?3S=@-? zCZ%lW(J@MfaQF(J%s0VT4QCl|!|XU@ETo1G;O}SqI#b`ak<&Ilaz8z_mF1jA?!BM` zQ_E-fUeHAc@xK3NU*0qe{dY@knGazr--|ff7@+&Nz~VttryHjL)`iC~ct`_YrWplc z4iVExT1gSSAj1fM$c$6UD0Do#0RINrp^e{;CKr`JO!HIqUWLU25!oLLr(>GTWJyA# zArh5*cS}CW3!h{_@yaYHLIX&qP1)fhZgK`(hwaWG6arkG5J-J_oMOfcp0YlBXO|G6ID~NUj1{U{0F|hTeOl zmR?NdEGw5P>elF?P@@r@+HYC=r`XEqp|R?GeY#xEX3OR2`g|4NzEmvk8T0Mlr9ctv z*LNuajp7`LR)XbpV9yA9Pb9~=bif|OAEE+1AAx-!R6eJonj1IcE)iq!-1$@^Gc%Jh zR3Bc2b7N zY^Zod_1?Js)~DX~w&AzJdRby~Z~buRJ1qWTJiXA>x5>|6z$q>Y0Krvh@dyL-Y8AZX zS-EcZD6B5%q&$pF;zd*}L%Jzx;YRs;x4y-ii@mbBf2z6CEFOC+bX1aC(EAr{VQKQ0 zOTcHd_Tu6A{J}ANF*9_zO=f7DK5VzKw0EWIM1AhQ15 z53cQhIR68b|ZCpbWaN+-KdV7`cVwmt#qwh)gZwh)cSqbB_-AgL$1LdX2(uCjv{%8>fJtl*thSLDA5Wh?T^A;ccy#tJ$)_HW)uUru z@T8h1(#Od7suBrd0E;)RD}ZCK)k)sfET=QXm>gOsOXSE_=S3X z4T+O=+#E&5Rx+`##LIgQ zp5IsGZ-P4XS#>BOYG@V+g1C6l8*<2?*fffuXXD5O;}j7|%Wd>K?_5ZvTv9+GsMCDU z!w*HIp@cYzI8GO(%Jg^ypzpQvNG9_flo6p<;uR1^EtFi@kzweL+rrfG%zC3>RQMxlf zH;}s{SHICuFZq=J3))nD4EhD^4`xEIgbyCQNtza(ksb_Ec;8d#aMa|q#=aB^?R{=< zctoqV@~y@E-if{WJ;lA@a21VAt9W~Fo)z(H&&SaTwT7=e%fGTWzc*YCiSLB>!o$d0 zLtaJ;@AIC2WgeDeRe*JFULgUlR2nvFRdn2>{Mag>#z_UQQ5~58(!*3Gnaf%$ ziO}*8N|@laV(_L9Nfc+DBZTmrX;?}4=jiFIIj1I(oIrgc==U5xL00XoK`7z{M1_Dm zIv@d(4d2gf!!j3|jRopmfOqh;Y0d5fbLZ%B9^Xd7Qv)b&;MS{U8f(pflCvMUZRAfP zDHM1HfR|`GNMd0|^2iOrnV?KkL=R0FYe?$h&sC0df})0U353vs=azz=bST&GXNoNl z;vKwMz!VZ}91$xh)_}j7(0FTAs8o4q6^O-sRgX%N1YeQF5P3qOe931@X!U7GUPaO+ z*(XVHS-~4D?$hw8<^v*1k`y_v!k&;Az9LD6;=@x@@f%)mNQ%RFh@xY~mMcqA3SZmq z;;>-4SNA3GiZR@r>3AmN20mO~!u6$)q~iwhxZ?W>S&{tcY^wSEfuW;$*f^Q+=4E9XAu3##5=Q2q$h0zR)6^!{m&0h~n61BF}P^ckqpLQ zsX08rSYUGLKEYr=5cw<$1}jpy%i!Mi9UgDJJ}!qM`I{yXw?MjrWPm$5tT5n9BQzq+T&(=5@p0uJ;7K z6PO@kv@N!GO$_rdvsJ3uGn%D*Xc{iiG?bfyzX;2t{SzA>gL|eMdsv0V7~&ZZZtQ_T z3UTIznIKTB;{I0G(>IqLrK?o4oeyG}5E#p$VW}!eK>T5hXsBcxOzLzH_52oGQ-YdB zClPzo+!B&?2X5nZ5kMQNgtN^YFY=a~R4@xIH@BJW&cF4XJP3Pio_ECYccR3ZcQWi< znzp4#%>OOCC8wyXwJlwIA-rRv=Y!&N^pEVjSpO9Bo(@Npc6;^Hmg=YzJ7 zCK8T)rITq#AOxV>2$|#nCpwvC`V~ z6YW!V8MuiE28| zW=)%cz>vFJS8e-G`}Xt%GJ*(bLa{F~*LAGU8f?$^2d8K8*?QR;=r-3F_ zuo5QWZK6ixkP0e|jE@U^<)$KKT5{MFSNXV+=!C*euK)uME5N2Guqr9=0Jk9&Pyr5N z%i0SxzBkbD9+HC5Ptt*Pua-+q2e38u6xMtj&1uMI5MuuP!Bkf4wH$5C@dER-Q~EfD z_L zW-5sfml+3Lp(&8Mrgfe&Tyx|=9VeK~x2VM8je(%9O>EYY1cWj@2u$3A<4pGU^1S&&F@s%8K zIt!4N$EgJ&MF61_@cayDU=k!I5Y?1uNB!s&b+*PsQ#`AHTh<&kXuz%pv=xU!%_2N_ z6=4Acur}1(W~cYXju+oMdU&jIW70l4#wBTO3Ypf*_qx)wD>~!zh2&TwT0NM0r6SFZ zm^!-m5@6khbZ!u>@!V9-><`e zU4xei;XinMHXZ78QW8=LDEw|N5^D3(xSc~ZiX~6^NnC08gEt5YKe=!>8V#Y(UOimz>nE^;NW z%};+V8h0gfq^BeV;z~&Jr82cc%iX!|Y6stf4Mo7RWf(U37BE_$<7!Tx$**)4FMc7W z7T_W5-@>Fd)fDfb-3u=%t#A>7r;roZck}Q>dH66FZZ(Irx%N~@mF-LApwX&UG zO%ETHFq=Zfh3;$3j=rNbwHsfsOUdx?>?jJX$0M}wkHZ^uFc=_xJ`!$ae-Ke`PDJ19 z%%n6MW2NB)KKn|Y75#y|x;l#GjkAb0yx75*ACgk8m4XQO%e7GY`$^o{l}+3x^)cKcsbrl?K86vdoe{;o-CEUvm^AY;o`9H@Ia&*5o}sS-V+~khiKU zYsq}xR?zBA$BTuMq+GjzfQ1J}NLZg5L1R{afKCF-#2iVZX-UHvC|^Hbjlpy_!=`^E zU}q^^HJuhBc8>Yp+!G>t%2}Qn-8)FUcD_-Kq1d1nObwyVb2^QNZ?xwK8TP}TU#LZ{ zi}7WcFYj|fZE!AL?<}wP{E~1?yPluN;3wMyzrmfDZE``vn6^lv#Svn`rrvsf!0R(W z``gA}dAXQ1Gb1zd?xJ<$h*W;fm^`5yy(o5B)U+aQm~YXberkxK9bIV#XK`%O?Kf;WGH6Hj!!DdfJ9 zD@>ladI|Lbl{sWHw)6>P- z8|VQYq^%I@Gl4banZ=xwW=XM=N%&=v4a{H%Qj-#H0(vP)(aB!iN1sstk_~SliH@f( zv9{wTgRgA0=QNw@Jw;|#o!Ke!h)vN$r-Na%#8Bwh^2V1yjkViij|wlC;TeX17I~pa z`BO1Fv`?M9O6c%_hVq`5I9Kte#XskSlET-Rw*X#)lbNjy?~W&vTsOyTDDpGz)5#Nu z)fj(4eih!=eswq}_0gnhtnQEw`acACCZ`_GuV4|Lm6o7Yc(p<fsJL?LB?8qTU@G-k`=I?hTsO$5r^_&i0e4;gG9~NFmQ+ zrMY+bdOfsSJeS7kYdCd9S}(%+clERe;b%lljmv4l*O#>hP-Kw;I9Iy{+I07;UjxU` zpbWH9^wagS+(6mETfHlAlh7(}D4$lY*2F;E_R!$3t(A(6xq-|R`bWfzln-8y#E z)`t?2DaS`$$fnFtZ3-2QwsD%Y@{=gwfPCl4p_U03LN11-G0A<@kh3fhi;g629`Ey! z2;K`l+>3aw!S^4?Yk*G6IV1?Ex}PCtL!z&M(su4z-mnWvWoN&boE@7qQrKs^r&gLb zBW3)cylZl%es~mb0X8>M7h8}k=nw10o{y(SwMi`>BjJ!Z@1J--so|M=aMo*8Oj_nR z37w5din+^ws@#+XDh|c?ORy0{gag_oX_x}0N4e{BF#dr95U60n@ezP;*5uI*3S&wOt1f#J)WfC>&8mN0tH6swUpu+~Gg5%P`OVMM&SX?_6h!x3v zEykw9(P+5n=+1(!0?>MdI}@p(5KICIfalXp|8 zfi%ox@DVK*t+qfRYYXD2?4`YKtDz5~oM8PWk3(MIm==!(k7=GQ4skY4Inydg6B z=%K1uea*0VHVr&leT!m8ir6o+B}=~=l2l(?RbwD($^K7Uh$1;g5yWK_Xs7b*9+aBu zMhmGK$PfTjw24p`g>z$Kw07*#Fv0=H02P;MQPzs7Y$ zw4mGvH)?}NR3Y-IUYquU%dvD$16{|kzS?F%NkwMM)A%uqiWQwbmdm(2I~Q!VDy>%* z%}~fP(<8G`^p;84gMp}7Mury(A@P}ppgggx7zxK;BK>#nHRE~0+LXbzJq z61!gTOMk@qcYc7*+G-oYb1wIi7V7kVd3$Y?fTU}ZhpVDl&$Q=3&zm3+`jscJ)KNXf zu9)ATOYH%Y5ZiW^=LyxKyiGgd32Q1nq6b?IU`hZJ53qU44+eq}hyy$VbKVVR_bD;I zCW&)MQMzH9XyO(-_$q+hP~JN3^D-OQvVcGB^BQo4rU1@Ep$i$OiPxuw(<;3=rKgk0 zwBE1peAPLtOe6Rn#`@H>>eOewtRe-p94?SU8Cz+@>x&z4Ngk0jErQFRpzN`X9$qU&4OIkw> zWP*UIDA{e!HF3oAoEcObv=A>@z?WTU4)eGMkCqI%8mpo>woXtE8|l;lizk=@U{6hl zRbnf^m7J+5dJ|0b2Y?bJw#G9)$z<#yG5Qt98k(1+hrv|*B^%K+9AhY}`_=~7!(TGu zGMK0ZB+h4)cps4^u-WIun|{Pfz-N3|K^YJ+c_f}CtW9xL`QgCpCKUn`Va0)gZL&Y? ze45u}>YUB$i(h*%dzjrJsxYxTvU=yI79Y%V5ihP0;-wF}iG=V^I>nw)pMlLxvG~Rc zPhVR^IjL8^=4&Vcu&K1uB6}Par9NiA*y-@XSUASWHJ9MK97Znc^K=ypNF6j>0r&@g zHnw6KZC=&lH~i&2H=jGVwz}$nRa+pV3=@f&zjbbvY-h+d=bDu43Ehg)Gg0@wt58`+ zmE3k^aG{0-P22w)wB6U+hPkq`cJ3VXXTzil98i$jukstHhPHMNmn3;9+)P9(r63NZ zb%WPvGtEEK=M36H{n`dEPv8BLILyj8JP65UY`Up#ajnCJ=Pj@F-O@aFuK$LBW~~@~ zEMtvxn{LVX=05`T`54N?nc@J276bP+u*VY2R~3pAq<_Og+ZY5L5J)3c%8fYy0LM!* zWRDzepqM|0)vTO*$umkIHG!cS71%Z^RQG5mo@m_6&$Yz5aqgVCYcyx!dvvC)IEXfJ zgc3b8lLT~v`bqS_MGhnuJU&JlWs9zgDm}G=&C+iB4TAezfi7Tp9+iS&RDA-tXFJN= zVzB=eDmx`nQn$1mrJSL|qSfRTOAu}}_;IBo=14V%a7@9TXOHg93?EPb$JT^5)=FAY z<%2JN&-(2bzKj&X^_H)d9zLFb&-vuggENXWvDV74JQc!FyV#?#|EV*spYc3OOe)oH_mR)=0E<_r$}e z?C4Cc^U8lYf4+{UXQw~@C=iYt@8OJR>phy-l1M~!?^Ee~h#h>uzTdged+f7se#ZXF z3ulUdXOE275A1s2{_d@w+>M0lh2r1-=+Qpe5_UMpm1nn6W`N2}iJ}-VeGFa#=4;uVJ#C>p%J=()OtzlqeL%+k`c*_M~O!?2qw+0HvAE;Hlz z?Qg|1HoX@=ykx&J+sec*#WTR(Ub@sbz62m%1ZYH70TQW52POLrrb*u|VMXCXGOfhz zYmM^y=qQquXe4`APU7Z^DSF=ao?2RgbOHuAwUDw3PT_)X|}6T%s_ffm$uCtC1N*f0B_BfHa}W1h<@g41O~rj=GUT(Y#_$-U3j zPcAX9uxNtlT9sPN{!V>VvZeY>EH%+ybm?10X|0Kq%XVE@Rfkb~eHr##w)0QYnAx(I z&rW#F7HDtM8@D*C{z73{)ZG&SXrdRbKP`^(Y|ZI$p))_zWSNM z>|XRtwnq<-;uOvvX}0h)bU4c@?+OGHX*rJ;tF2Z%B~76M6gyQY&)qx!0sG9@p)piU zIkG_CzSNvLobCL|Ps^yb7RZ;X@1+0n&t_~&N)03HpJ=^=g16^EYaXj=8taOO$A}J4 z0&01kF&KtIAw&gzr&cJ^LWJ(YJfM;8)oRV(^BTH-%NRX;$KlZn8)_i16;OAHJ_S{1 z?A-YS@zr;NT>Le0exV*1P~P;5<{Y%~Gmr`%koJs39v$WNWaJM+6oFp=ijuilldFL>t(=`mhE;(8!Z*Bz|it}RM&B?@95^@`eX)gdwrb0 zblV2GTnF8UGR@%L8*nrJKJZ(d*CP4f3vL&wyq^xOpEKGi1IbEst`9@jDyWr)TT#v; zzuoupJzRI^@7WSu71dJ7VrxdLjhp^=4hF?XNyC5eU0h(Q}FNoUNOJCiJdH6T_!0;Bco?l(-*>yQE10DO(nNKH$Ggei3_H1gW8xpdiYqy ztEt{0pKl25J-ij4H!cIx;q_G{lw8Q68B=g-@Zz8wCcb&zT1 z>cxC%Xf(YW-9)zIXSrDKeD?ZS%J=sg59x^HeVoydOg1I`s`#ga!^B{>%tL><2~;Ve zup6oxywF`XXnvhNTyWU|BsxkStY9tisjm%>V?Fei1no6$$8Wg*L63)&OLA^ftVl!@ zVN?#-11iF4Tb4kR!#9xhj09*1&6SNL^;US@E^eAfsg)kpQT~dEF4i_v>x`mU-!LoE z?)2yoOcE9Q_P??JgFVd?i=y1B%8>N0SX7l`u4w`MHpS-X2yK?}o(1nDOu>5>o_uoQ z{hw|&Kl2w0#+`K|J8XXaaQNZm++6Zkn&J2T>g(U|iPwMP6CWCFJ$mDM`ryAG9o>VBDGK%)XdC13`Jh$OK(8itmAJWTS;UAd0J>+|${FV>z-TUF%%UQ!~QftP0$N2}t z?|WbPyVtoy`^IVlba^-IF`PUIoVw$s)@=>c9C_J0`|s{QYTOZ1Wq0U@rmIKqA9-0q z@W1=_-8U1A=q?lXNJG*-|5E46*U1dheQ3Xmh7J^9*|=Loaz%&iC)X-t|yk2K$do0NQ`Jlv`ECt?Saf%y<2&ZI|e>e3iV*0H;QXfBY`1 zbbUVSzj+^qyF^@Yir7#{L!+X9+&chu0VV$$qzwrVD5>M5vIun@u57kmUpjjhMdx{~ zb~n0WBe-$_Fzh;_XlG$>^aWKYs^;8_k|=|8f;Z`!2)QG6LR~pTThQK(sD~J8zS{NW z#l_`1JKMR4OLoI0@2syNWV%Qy;36VCBp-zHL-;%#Uyll35BYLAd~8Okky;5+L7`E2Kv*=JC{DiIw$ zoEpPeX}n(yeBRKK1a${jh`xsXLejc4NFfGWSf?v-V~ zTGXPcP%v3~?Q2U(Efk7s5(9sPo=U}K(+KCkFL^2pBd3kx0ilq1MJArmOUTY!#cr$!dsFCWZXIfb z3W4Z?d{(HLch|t{M#R?2p@kBP8ySi8=TBDVE6mL1MNP4ELOPrMmu3jHS;8heRL)oN zHB{XA!0)5=Ou6&nY##NW#+0J^?N1eZ-{q&?nd_hOWa{+P$qo@QcnVf@2+7LT4 zN_}+MXY2u#vE%oOn}%+hK#hVv`sxqWw@~h$G)f7v#Y$SKR>XG+#$0DQzzul?{N zrE*eh?VI_p0abF{WT%G@za^DQMen|Ps_%M5=#95T z2UE@U6X?tT%$|a_K%k}{LkXo%de-qCM3bUIXCfdQDSM(}4ERm*i#o-C&G@(meTW^x-G=EpFmEWofM-yU31Gs;XWtGOdOd;O zGypG5j|a7hsfeb?%$uksjDX?`gvZU0X8N^|p7eRGh>SX-QOoPIjA+PjY9VtRRk=Gq z@09~X0Tfg~Z}*g{cu)C!0aKQ|0bjtve|U$_49I>wL0vfX$9VwXm1UHCPX!g!FL<3l zmelmo1ezQM)tK%NGf9sH!?EE+AYw%W@!?oli|8z(qOpeL^XFJnOB8i2i2#O|q1lfv z>j6!dQZdvp)zq+@1FSrj3i!2nAZU(747E}yj`)+&Q8S=K1O7lNUehyqUoe$L^u=qa zNSOgBTrcNSL0>)t34ji@I{NWNybRS%ty@swAZX~sfUB$o;>+JRZ!X0%`<}N+XF8zZ_Di=7pm9)S+T5n26lX0_cB>VtFd_qbfxp=8Xik zjCXiMvO=KTQoSlAUgDhmXZBH?GzS5ke7EQ0p5Gd@`jB6aqj7<+94N^s>p=^!Z?QpY zZnv6du$(Fumh*;ni~fY}!lDm0EiDc+LFi!HrG*+L*vfoG^VNnXjNcBJsP495bukDQ zEDsB1Ey|L2NDXXUOaE@$wIa(h>)dmDdfeYzmO)MS7yW2)vE9N^m3e3fzyA(~m6MeQ zuAIP|6ii8P{SI0@QbCbaEP!BOL{f7IVRcu_?(G)DM^6Ox?ot~n`g}!ya^TwVfkgJP z|Md2#g2S4J0zwK&ffo>VjZsv*qdIR?41-!O z;xvnL>mGin2$swO4mx0UxQkMpeMXXfCaDIHRxHP2GJc`?ZY*iOa@W}5u~ahS2l$@d z6Ssm|@-4T;=11Z&KgvT!RXsD38J@|{4%Kc%xcx>+i-p56wb@kZwrqR7Yx!kReD*A>UlY}NTe}9jIb2Z`sJ@A;#%>D;uHSOKmczMd zop80x(J9d|gc#r8?Kb!_g7cOjf4XT zViJ7HKq`6wZsk!k7=l|l6g2T1Z`yWlW@rhpIXD{X_BrwY_r`&l-VXZ|s%g!&!EM8mBy(fnCsU z0jN+neO!y(JT6PH@j@17pfmc~P05F|owqNfD%ue&PU)36H}$+Gw<4z7eb#P5TILCXHK2&(lyki2q>`;s^5E;oFX50fY%v z1hW`o9pn8)J4q0kr!>FkV!5}$V(ivTkZ7z^Q+2aIJH zF4KD7^>-YHbAv(SSjH^|jssymj)K1WIdOr`Tjzvhng+d)Y2T46>90rj) za3ITc6a3Jg6cB0hRn)n!q8kC)vRB*)pDIvuv+x8E;CO*~%=5HA#qQw%%ga>x=Z;;i zy8{4wPF6EeWdn`T;r`9^DPN^kp zd-iOh%BO@lS+v~}5!J`{+wuUy3<7D2?e{4k`=r=@#Q~&o`S^!86DV=NL{Q9)kaALr zcm)?nP*U3dHrVle!+5yIv!QYyQv41|uV13G&FJUXM~oH;cL!1K`uB+z6W;Jq0GUbO zfx6ZUq>+NpHMklNBU0a`CTv7Ra>^DUd?JL4G>95>L7cX$0$!O;JZv=bl-_@{X_N{p zc4Ig{5!SA@Czh8B5NFF8 zaD}w@xv%*OQu$?p2bm}OWRJC>Ac*_Hb$|u!;e0>)e@z-aeB_#ef!JeoriyWAJSQlP z5!wwu7DRQ}K5Q!X(#sooo0j9kg4QTC zq#42bRD0g&?j}KX%sFU9R5g=X$PZ>ue-=mzSDyIak1`A=awx)Jj3<46``LZ{v+{1= zm+i;L{W&i_M4bkbX}x=k$Jo>VG=XZmQ{_`!%(=)oV)QzTh#TNRWIavA9Wl*LOdX1 zG(#g|Rz80p>L2GSlOw@cEO-vd0!k$8QuHvumyX`h2%#6KrU8{^Hh4Zk6iEf~KE62V z7`^>BfI69U8l6^J*buT8Ob`nNAnptaKlu+x;uorgeLMr5FqmMzUDf;!$^W=g*6=oqw~B zfRW}ox+(45zeTL~qtF7vI495$90mvN*I{IZz$CZeyXMA1lRf!lTlxJ3IQD<_uMp0G zSD!e$kA3jT51fP95$}BGxu@uT-cMOuIbe2q#&si0#!8LTy+PgL|T zq|O1%<5$N{PAO2;U%!veN3>ao_vJXXl7GT!GIDZCo?bQf^F|}|;XBc2#O0ruABxFs zKR*?)(uETZnkN_ugc=$=SprYw%-26~zkix=TfO<(vUeJ4HwzviA1r0cf=5q@En3`q zI*o42<*XeT@gvP0Qy8lb(&CoVX(X->p>QpxGKRs@+7Yy5xlXxx?EM>N8)^n>Z;Jy6 z8mzr-A<3cXn|#0QFdtX|h=UUn3oxVsM%!!hlT;IwW3UF!%1!R403cy%xK2xH-haf? zoP_N=$g(DI2!)1K{OhS>La**z>ZW6_bXNJ<07Rs)v3cYa8%SV2=svEEjWd^RXJxw? z;t1iES(`FzT+|j>+KuJQF4BYQ!uDT~jcrvPE>MfNBgu z11Kx9DmZ49T%a6?%_Fsx_y+YEq1swgbPir0;DX{lDhisDkE-*$iMkGVD%nrPv&x;= zA1edgPUMa1b4)Grk`nu3x*prdx1Cr&qS5UPs6XMk_j*PEF$YfsJOtbc2e%u7sDN)` z|I=_#PK87P#>jRYRUR0Do~P|tIv%sU2ZGxA0r+g=Y3(Y;h={-GI@^Nrv>u&M^z=j& zlN!>oUkLLf#v4Tx(?Y?;yF9P*yoPjVazF{NG5kZK2?|Blufg945T!{TN7+>xx)E&v zf?);O4Ovm-ph-V<(`;YC=@`@=gjX)yxg=_6u8CjxPi)yxtaVJy}iMj4Br6K}rXSe~jyCo<%O&*_$O*=*tl=SQVGl(GHay`2Q zK3g~O#6jWmSJ`OS<>-_1@F;RqTfw_}4S^$RC_v8RzT}dqKl0xCQ%wqqJNq z=Xz%q`cD$r1`jXG>;7LY{Fvflh5r^WfBXs?VUYLz=iH0XrSRiiGIYhwu*D7EXSd7% z1w^=jYnR=`EC5>YXkEoT{w-!SLah*F*6jS?%Do z;j4juy5nlxD5E>yo1=yG=d-l1u3}96^10!RjYf#^^`E6JoJH3&9|Ssu(n*+CfS;(} zN8ihx$JY5@vBH!RPAIXMk_i7^|BI^_TOnGUQWIe{8dbvywf_az(|YIZU7puo&*%ot zU5&Rl=?)U*D*MO0^N2Uj=HIF66pdev#uWXdiZV5&hIIuo)x=c{zkh1$N&;7Mn4$K+ z5ITp5f0dy#9r92qJBSukoF*URFvYQNGYY|M|on`$GeTG+gIk@FZX3s?a0CU=G-bl<8|^7DoxkpvLg?_+~
-lP`}JtxJl{3Z9DdfL7+lUDHqFpz~n6xfe8UIv2K^d_m`y6Jo>k z_t7fO3h=bIi>~slkO!=RE1j^V0juDO=WKscB=`$o8Ek1$axcz3!sIIxzHF7ly1f<( z4%I9w5!xURx9s9xK0Uqfrp5zf)tPgxS6w)Me0b^2wYm0Oc+cVb2{uCLl*Q?6sW4X= z?R@LZnauIIYBdfo>d*I=(fc;=cvOF)jHtVZvh8q3Abf!mRuKklQa)>yz_&Q~Db)dq ztsFywN{-^j?Q=8Lu?HIV@Ntu6Qg;NyxzdTdPLyhMts8!f!!1~gQ!rYYE0oqUXY#&4 zeK;_T>KdtRHZ@mW2<|Q*ifCdp6#c>NUBm(r9HMF-A;crL&K4zscIT*%MJ`GGvo+g4OE)-7 z`p;B{uWzoN?oLt5WzwPpJGsu;;z>WX9cODlz#^?Ha|YAd5>9677ECO;mf51lW;nv# z`j0!!)?G9Mje4=s*pVpP#Yzztnq)hXM)Bqer@UML{<4s>i;92Fw(6U*?yY@T6?yIsXL;5m;wPVno< zB1vDFnXe{O1oe2GP=Zqiie_1)OkV{tFAu`dT@}IdQ1$#Kp(4xa@c+CHJcBdluoR7N zkU@%`n5cMv{F;FcB(KO#Aa+0rZYgvn+aSa(IxmFWUVu0!XMzQ4J* z&c%6tNdL%j1ATmKW^M_w_b;*gawOLSr5`(!YnZw+vrVAAU!bJ`V#0>rnQ{~#5Gv3% zV1*5&*CwU}>6vu!X9ej$*o<80C;Vib2v>*C3BrwS@C(kGbdM`4S2RskB{xt{nfv_s z1QOX{f3s>4#(NN0U<293poYy{jT?e^)5_H5Ke8o0-%t4SQs&HDW$3*`zC0XEzjsiW z9{dK4?phh<>6urti^u@$?uBj6*Rj*F@Ewc^+`F*-!^JTlQ0H|c{KZi)^ms+_xKc)fmHkOCZR#% z`4JRn-x$V5B-xb;(dY)DAjGrQAJ9fHnP#y^w>bC+|92Hr*)wgLJyN%s(ogmR%ad20 z+@KSGCYwzZZdwCNsiaZ_0Ga~Wih=`3nVm%~Hqv0}!~+MZk__}ryre$p-B_~+O6>5r zNsHwg-A}Z*dU=-XxT|j5*?1_OJ_BVIuH?*9qa*W$QmHUMGTMQSj~}kZ^_JQ&s%N&Y zu-Hf-I$QM00pTsHFRKLO&0_ukrR`1NBe|-3VclM;dhf38uBzVD`yzE|ms&le+1i6= zJf0bEW4FBGOa{l;BMcar0TKg^*%A*~fJl%>Ad|cRLE^oH#SQ_31%!me`9cEG07*#v z_&t_bNq8^$g>2u~{{H9Qs$Qg)X2vFxy47plbI(2Z+_Rr+ZWJ4}`0%!>j9Un?7>&f@ zj@guRjCShUZWS6WV}J-k0(aROwgDG*C)Nj%{k%*Mxrzsrkj#37C!69@s6z?G!Z39k zus!h$D~|srf?{?9Q5Yy@WZeSM~%@@16lyb10RDz_4C_<=qO%>$N#{qVh+<<=EKl*vpisp zwalTqfpfnbme%+bsq&9G7zK+w%l;jwX`F`+%;+j5Mo_U4p@_g{2Q#cbf7w$=i~9oV z4X@Z=YrVZy8&_kg+y8L!q0+x^J$%;Wv?mq3CVBe8iP5pK(FLC^KR5RdrLn`o(}hn2 z^&voDFnpw>?S4*PpXdcp(+R*M^@F94Z3XdfQ-#EK7@L{aWnoNlcDe%w0U_$zTn5O= zUKj-uM~|G`zmOUEjjdo_9025OCE=O5?Xqx2xPf~soK$w_YQRg>CsUV$Dwr6%X$Nq4 zP>`U#Ft7HDYMX-C>Z)v?=Hz!7aQ>Z*t9o+rD~q2|)qf+x3v5LaGX#ZwKZ#OZ^PsrD z+Xm}~LFr3F%6sN3x^I1rm6;G>3?Z&cvTx-U!vU7{#Cy@)LC=}tX?x^M~P%N_4P zr)zKL6^i?*!;Xcz)E>!6H`VKuP`zvJjtB3UMFOP5-hKud`H2XAaEBV(;+YNqP*C+= zQUCLe#Qir_zhu+=PP>8I?41wXIg9dMs#mtz8lrsXIq}sWh-YW1Y-&7O70*=TQmaKl zALiU$FreRoaF4SnJ^L!~U`sQrBV8|GwnU(gsW9xD|Mnp{Sv*4yAL;;f?~v6;%Se|% z^E~88o6Zthg9k-KEzXrYC7U`Xo~D58plueo07B*@q#moda~k|kb``V!2>!hO$n;U! zro@0B#b%QZRD(cNz&Cbs{GvdUE^r5Lk6~O8FJ13dQSywV3Ekx-x54jG&sK7)Hd`fv zA_<FVX4%A}yc8V1! zO@Sq0-}9RwlU^DUk1a|XjY`8TwRG=4C@?lEdiMUWj z4#mN!+7-X197RGkmt#cWO;qcI5|Njl&;#;7x3rm~vR%Q$NFvN^(*9}yAP2o_5EB+y zBAU{ivRyLxo$RvPF=xg=V5*jMp{&LU6Ki6=f6bj!o8f>b;?Ky(ragYVuJ}|h$i$-% zk%1O%2nDfm{0$&72qah2jxz%d+#}g;hIV-+}rh$(=y2I(N_!ym4B!il@v9Tn-Bg z{9n(b=J99j_Q8~jvd1nguE#aLpb83YitWE7(EHk}E_+Ngz0M6W)M4*|p${OxA9{zy zi6mi*Xksl*hTHy+t2P;leMIU}cOTu( z<4gXl_;Zho>VqK@htgXF=5) z1T_Y#`z0F-gID@dN>M&K!_*vlH?TlmK+3*QQ{EEkW*zl@8 zg;>e-F&Tv#af&R8i<%tz>y|l1kQTWLTac+KRObdX<2=I=dNrp$td#RO)`Z}=%gNv8 zRs_~Aclt*oU)kmS|D9f^^W{W>&TM2 zMQ_l=Aq+qqpnQUdL1cb+^aYaHaX|{yh!xE-{=LT)`r#iBt$}gVx%Ead&Vjys21uG* zQ19hrcZ1yi>o{5M$H2kXUO4v>85X9JVo8{Cam#4==O1P}_z!QQXl^|K(IwHH#K-OY0 z5Z3@57?knVT||Z~&VsR%rvy$82ufKJGka!`G7PLKAKFb$fTC$!SNWE>1c|KJ*?Yw0 z2}7`F0!`F_!o__ALLG+IpPSKCr9i16a2D{rh6(dvUP@9YyDr5uy@hg{Zddh@YfoJ} zGIH%G<%XdmPkd7ZJE4yMb34JkdUsys4$tEjmElF5=7)Eg?7oP#ExIckfK~d9TCBu4XDGaUa7Ts7}cZE zc`B8lM`2$#RvWC^zQn5OJn|s|FL^dKl}b(RnM|c7RIr+|TJgPr8gu-3;LzRbZ9Sdo4w1kmZED{et zs@A@P%r*tdZU5FS2?3Jo;yHx?=GxF<9x~2x%EK)NAlxE7JGFs9bf<~$OtGB=8k~dyOGl>poP01@?O? zaEOW(l!QjOU*T!r2hc(C#-Y0@39W>asqcU)OilCSiXTzbqZM{WtMUdFek2=HC(^eo zSk*!ul##VU4AmD%oWx?1dAzOsUtDs4&N$#RD#PB>8%=x5I*0XMM~W zy0~Tf-;>e)X$v_J>#)|Dj;#O! zX73+*HTF}7D;8*X)J^h42TGXJc0RGxG;@*@Z5|Fu!je8;7p65XQEjtrlzQp;aWVT3 zWN!!^xWMP4PTevAvoo2{RM-=wD!-Fm4qLj={${{u<5=@#M{sO`NlsrU*%cCa(3;UE z?_-tjWSJ^(+FY7Q6eU7=+EHsxV-W_NRj3Df&2hI9@C+w7Dt!qj)X42eSE9Q%ghyep zC4+#awcS2+4+%d&1ax2Eq+CFuvHs(WTWUFp)#|_zNk1q%YazdGJ?Pl@v3HPTgW8=@ zY$KTZ4+Ao%H0$=;?GydB+VLBw>~?-iw8UA_ZzcQRQ7W*3zFfDW62_Y($se)JxKWd_ z-&Q*}eQQms*y#}S+g~EivVH@Yg9>%vGH&PCqec%7NCdhUwDd}9KM=7PgKin5V41>R z*;%_c&*#e{jT#lyqlmBsJL;Ak;q^bBHjhg2FBaHA#m9LaNDf9|hDDCqMkS{|@{!)c zA<$m9`(#NVzz6HYTWcwMrRRJBFieMLt^t}Gp^T{-$bNRnhv0rB*cPrvj7 z>j`KL1tAA8CySCgv_*Lpltp1>Qdr5Sv~4w&T}Z`uu)zjok2L>nK~n==0G~tE02EJj zErFM(8^F;<$T!dpz-|l1-TCpVK;u9YK-Gh+AI;-;H*qoC(e@6QOpB_|wZODUb9>!T z3r=kUorE{C{nMVi0(@PWt*I-v?;7*9A-GJ-4Oj5a=-bucWLg5A^|_Y_huN`=+egBC z<8guU7kT||?@stMb~iYg;``?Fp8|~nj>&un>x5bUkOH4K_3{)^+-?%c&ePva+OBs-{WPh|(A z14)gRHl_zlK+7)`are3|YFPGGCo%0?QI>hTD{7t{&FqMzOs2q(Gx9Y#lp?UTQo^Mk z?_#X|(OYq@NX}c+xj~*NNDKmh&+r{M4WWTLYrF(I(DV#}x$MrWsA(qmCDkouYXHw0 z&>tAxg*IIQr|A1tJu+G$(O;w-XbA*n{i9 z^jiR$D%4Xwx)bhPHjm_zC1=33({XumK+P6YbTh3gBRKb%4fVGNq7?mVQH=-9YFs-V zn@?;l_CnE1VyjBL$ra+@>MHgcehWH!Mo^DB6-R!JH?OO~Pupss;e}RhXv$05UrDyc z4Yi>e#zP~hIjoSh8iHEP;wp#*sV5PbE#Vei~ZBukvQ1!(a^8fh|~F z_x2&2g?taW8FH4Rb1MQaD)quViosI?FppA&=7uWRh{aQNv!Vm4^^;_mX0b+$%8|ZL z`=1o5#aJfUKKbct_1RzgnQPtQ>UhZKi)4docaC`#AM-oMO34o)#p^8+SLGOMT@w#i z-F|gViQ}v6Q~zkZeC?NhsTDU=mlg^oSP(Qtg8Dyk3>Qx8H0+#orE zy6>Un;QFxac8n7$)dUlH7}sa27nZ} zFtC4WRH{#}h^Yry1U>06S(oHT#rsSkVzY$;`Yd9AXvSIibKdlrh%yYh-EVg&H07Xk z;+NH59$Q(F@h|A&NN_9K$~W_}2VZqD3B~sl5N33@53>&LaVd`Lkt-KhUY-o*?>D~? zDlV25zq+=!S!ym&-El%?`wJgjM4Gx8)%6V8Z1dQ~-cnflELz)}H?<7wP92j1RjQ(+ z9asR`S0Hd2w!H#*66fKP!LHHh4&%e37I;}6;X z2Q5G!dbpMAc~#a#h3g({eg^iJtFNrtoqrbbarf>ve-raBu(;+R6ht+w*#EHoIFR76 zkDvIFcjT%A3xqY7TFUdXVms3xXq|Y|j+71){dOuxL3Od8!2M|q-llTrF)SM2X&jTn zw+|(#n?|GkB+hOx;(>=yae>0D72X*YMZya>J;aTDGr5nK+{-1|MH|j&zG+cDJ-I;C z74hYg?G@R@kQ}zSnCB7A)^$VR_d^j`=LTTW7+0Uw?Oo2GKyrsQI+^3u1NWgbZh263n%N_6j&>%n}p-ec`sygA3JtcNK=)i*bcG&TIEo_q`tdwCwH&*c@j| ztadVVt9rt9;(Yj&S;pbPlkO9T(Ec$z8huxXSMw@s?8p`JpXx%aibS4qs5_r*& zrk8`$?0*X#hrOtIVLZ&5MIfIhz=`cV+?^16@OeZQb>@67xPV!5j(A&l`u}v* zyIGvsUHpi(>c`KHtMvjIoQ0o3tNa;{{Xbp^e)?#_X7>=bq8j?~lx%KqirLYa6#Wa3 zB&?{4%~`a0y(x#CrSPV$Xmcv6UxXTCUkRCJ)1)6N50^j=1X32vouwno4&g)8wXj3g z;Cm2SDxLbm*5+#epSZH}#gBK1;L+Yv!=2qV4`2^`X|%hpf48OPa6e38UXVMF80O9n zbLQ$|OVtXr;mpmp6)bB3+Mi$()%F_!+e*(`^uuOtwsQu6t3J0gY+~3H-b}egI=rsq!4WLuOvh0R|r%4h@yE zNV;t35S~I;0svqiUxV_pii2a7y$&qs5P0Hu1QUD^>-KU{!uHZBpF>|`qu@S1aG|PT zfGV)^CvCSSeR*uVKC3*M@n$ewUq|8uFj-9D_Kl^Iqem}E02T_?;l;9F>4_p3MqM6Ql# z)egRk4#&JP2(>LOL$9E|-vt$wV4*}2phdrUAp_3+#4roF^?Ti<`@t8>+p_&C-YNg=pOBjEBa*?g7AGT`q_R^$EtRk@1OMW&jGXc_A*V-b$9$LoO>_390Y?o z_uaq{7%25YLiW~mPRF7F7|#Fx24g)IGb<>O^TZ9oZTbyRn^rI;dX8}x(?ewlF%_(a z%YN3FLiQwGp<_jWH9^FIiM9gvTuskWU36INc+4nrBTd5sEQ1=^z=$DN+pA?ZwccUJ zOQiZQE?WI_n9<(&(FrRoQQ)8W9j$-)a`fJ1C4(?eJNr_HhYVW#Eqp_a%lYf&X@2*y z%~judc(LZe9tcDxxm8m25Vt*FefeJQYM`)~8RXv~2n3D?>)GW?r?&rDPGl1@x=duA zl#V;x8RkcZE;aofsz(v$)qzV?dg+{~-1|F+ zx z5gh0g9S~r4D{v9+f7+MuhdmQd;lRr8E7U3z6Thlaj6;K(KNk1p5Pa&B0!~+4Db5)F zP$2B}=k^svXrC4j7iOB_c+Q7!FX473`l1_OFw@woe2%eli@NDR!b(DM(v`9DEqI=AS|t_Mu1}4VYmnqgK?^*O{6$qlmGdr-|_b zQhE++R^e~fd0Zyd&au!y!43s-C)F$P1!AT=+_qjqxB$d3m4dqHFLCy*ItBc8CG+9g}{n4mDRw#VQX7}10X&Z9| z+>+bnaj@&)lJ*Z*6PflOQ$%OG6=#~)gGRDJWF7^=RVAXhgW;KwJNX;QSjLkopeVn( z_bx@RYf(+3j~TTPYkv{8BYNH#a=hUX6!2jnGyR zU1Z4TPpRxfm@XwJimuq8aBaR$&jXagEfSFHbJ?czshRMm!-GV#YmTHJ?ztDW}6Pg#)QW!?vlD6>rddYh3sje~toi z(cB3~23yAa-LIP0L`O(m)R#z%75uU{Xm@_^{aIIHZK2Vbd-6L?$a}!4nG0qhoF49>&2Ra}RDb??1t@X^c3kCETNoN}*G+lq* z{|{=r9gdUvp?%MHs@1oyE7%rq8y-vF0KlIH-Nuj4&-@8@csm|g417KSU9>+C=9C+edg$l9#Mx2 zt%Wmt7pAnzg%s{D* zeQ!PFW)>{>Yu$iCP2~NB8QZNBH%;Kg*?VT;&{Q`6^U1L>4th4L@;SNk@v!4!>Rr(! zzT(c$6x@za;~OKDYz^??q{^no7mh7(*eB*Evf1mz)coCtF)JqbW*aYlhXu*Xe2+mfHUY9(1F5V9bta=~p{Q z6KRhVDJU-4W%I?HXxEOEOedZ@?Wv#m2zFNNx8Hm0F6FKgK4_QhHn+zoJDCIJ8*C1+ zSOgl=$J%$Z$71jG;&VjdBz%95XZcW3rXhx1Zg!ZNRbi0;2ZK?f23{s2C&Wsk^NmCDqrSs9#i z4Snl1Me!Czqflm&w6}5 zkE*7M_=3l~Zd8qpVoFtJfg@s7UDzp2V?V;}PhxxR0zDw4s1uYyNAT~Undoz1UE~ZX zS7s3uRTw;t3rqMhd>))wo!L|y?oSp|uXd{l99h~vng5I2l7`3k53;X!PKyZzlT%(1 zvg@?&WOOEldIh;|G@~*(DV!!s)o7jS%IrgB5>)X8A12fz)d6M~tWiN%>#Y3QRPpa^ z%4a!xs{k!E`50k%{j3VKNx}RXpdF7?LhHwzxpz~-ORK?;9Wky0jMay6rx3(5gvBS) zs_~n^8E`@(b>HgisGTBkR#)rmOV0Z(>?nyYc3RICQ=e59&n_3GCYBKp{JGchv7Y5^ zfJ{@x^cjF-viUV;@ZMM0pb?@iPHF^<883vYKeN~in0#~52*+DZgLEyNrB$5CAfvdl zNOySa61>N2mpFcDb9JseMYZ7JG}wM^C(W5(J4bJmR4MxA;AI+@(uHV9+#x|{6Qfnz zo6RPqM}$u`@q7>f04*K{kcM%|;5waZLiTm8cNB89wm%rd!@wG3C0KzaF&W%4>_rVjS_+2Zugn6J|RAUz20cBrT|Rbzte);XXjth!Kn!PUlDH;jsZRK>#M{a*#^*udRN@V z=;FKLj*$D~#+?b-RpM@(V7)rrUE?(1`*ZHb& z_uSC)#vRxb9Co|nE_Rtcdx;bMj~#acva7`%Dn4Hw-I*K*S{-=pCU`HY6=YU3<(gSy zG^DZOo>Vhpy~JWUn_X{1G1ACp11{r-^MfDL+`hH6)LL9zURv5dH!%?QF6Pq3;STenLwn$S zzQ4A%v9`AT>NE+`x`hlZK^Ck*ni`@SXh=t(L{`#IM3yg32B<1L6N93x<_;m$ld>;5 zc^MH@$v`e68%maxyTWnwuSsVj8VoP#S~Q(uHtM@djU(l9;+kW{d?bprU*{dkuRerN zvPNkxe3wE^Z>pJe6fMQC9nm70bo&DD%IbGttHpykCoUF^+?j0u$3r%JQpoSmL2haR z@-&AWN*`%ZRK*vX2mL~ta&2HjAWPx zp9!AVxNQi!C2Stp+I$w-BxZ5qkU4<`z$i4@o2toh)NTNNuixQzgj|xNN?MEG>rw)H zkRpvw<`1eg8~coyNPDBO?+97dU`>9;6VN?&hcD7CWhH- zk}2rnxZ(CK`8*rCBkW5-UGew~<7n|{N~@=rm8i#Ok0C<Q>Z8~obBk-`qdH;}Er z)LC~LC(hNF=fED{jM(*KXft;Y(uGhYieW)zl41!jw9~QIsZThuDd6rPI{=$1-#3-A z6F$r`vS?)~&r^)aqydcwpT@V4Aqxf}k&X+wyiO^ysE^EFtEU;Ri^z9e3FZ&lm1G8i zX_qahB1Od&twvo+F_H=@x<8yz$vRynBej|d`*o%LhpJ0*dtq>NM*Ti}NS3m}Xdvbf z9FK-VBcae?&Fgmt?E$Y8v;}J7wIg0w4^&$!9*BlAvK+K~3Qnh=wOk===`0VA+dp(FGrVATyOhs|<0t7qED?Oq z1IA6(;LT)*A}FtcP%|D%Eu)<*&QVm~kY}50(Qw{>##i(G7H((mcN#xhaeR=$kzFNA z0JL#ji%c6|XB$`xJ7#~3ml~xgiP=aVsZ0E?Qq$<1p5cG6DylfhDCMcZU#|ohDWZB* zmB@%F$!ToCH0+D?i|qz#vRg_kJFU)+NAv07BlsK6)(}e^PF&AbXgCwg6atcA3$diAcp~tz z!TuijaDfM2H*|ux#k4g2dI6p-9QttWfKScVU`LHg#MI?Nr5t2PMgxa|p{;(9W>1{RRt<;U=7}-p^lPE8p2!t$ zE94S-IE0eCjJ1C&qi0!J&uC2na)VbQ7#=$oURVep`?wEDH#YlyZ*#fcb{{x`%kR6z z=5Tv`PRZ%8;|JTr>GL8py&=0!o^;8`HsSm6>k!j^9lZMR&~bP{hnz_Lhwn=e08GZJ zW)VQWmdAW)$~wZ|Dby02Sr!IlT6i$&N{zZu%PJ%u9qh%*@?;x)y^p0*$M|~JvY>-B z=s-=)`Xg@TwZ~)ojR}w2=B=bB?2ch&RM93v0p!MoLbpssj-DBFFx~xowCn}gYWf!2 z7Um@IkH&C37jSBGg;60XGAw3ezCFK=!wZ)a zdkW{@5=?w{B+ld_zLOTujJnNwz=b0O5aV+pS3Y7kjXI~?rv++zfX3->KCX!q^aC%mP`ha~4& zr_^eJJw!kHREH74pCL9J1FgGOPPMtVD zgJ(LpL@XS&#Ix|&wWt%6h_J51zKVX4?bWZi&;+%Mv@EYVy=Hq^dsEdQyjf^!|CuNw zEn?Pb9HKh~m`PKtGJ@4}6(NA$mfTBu`7uSw7>H_a6z;{@n$W)b5-mi?A#LZ2vit{MlGCfDxrY1mfqS((0Nxr z>3j*em&IKxy+x1y_jG%6+Fg%WLR zFckZi+@zM_a(>-J>Wb|l*66U4R&R3B=O##L5eU;ZN9-J5n_Kddto#Dn7a>pu!832} z40PobI|H!Q&Cn!C?bKUBNC2wKL4eR?t27JaEByaC-H))vOm5HJC`%?#wT6f(R2+D1 z=*Z&HVDRYTk$ouWgF-Yg9<<*d@H6$i@4#%R#qoB5Dhz~uYI$m^T%MdPLt?;Zx12>H zLnMupo=Sy)$9K*m(644uFU(HjNU{#hK`@Kc=ov$WbY-&TZyqonaXWu&2cXUKX#V73 z+nu1n=Z4ld{ejY8sD)fcv}OmL)rJlT+@f+d0HzWy;%uV6;#8A^i0?8z;5dr^1>_eP zPk6oU)i1DsPdX>D()QMGMsrPa_@XiUcze_4I1x-i$yb2mw-0XoCTw^DR^B6qpDTS3 zasXjOfwTx_-gONV>C#PDr^t0GNWrimWUs`Qk?etq+qd^VX|`;-1z^Gp_#A3td4~ol z-M#C^sY}ulmqc`l?^`m1(}H){*X4k$Np`OtpdDW}z90@pE`{vh3gm_(90g$DJ2uCE zV9nQe0MB%-3t5h2(In^-Q9&gEA35_mU*X%@f)(O7P93Os6#Xc0I-~LOP~gd|;l%28 z*bN7-MbKW_w%$+4e!hWfCQ;fP*AHH3>?KQIAiC5M0KhG_Bt;1B$4gzAQWLkT59*D_ zDe(g@0dOu`&Il^))gMl-Z*PZZG&ca2JnWooZyrJM%0VsgIw1)pXOaauhMi3vUa8Hm@&X?n1^cVD6^Ohz$tgx_d%fnBZ1_mBdEG`xyQ& z@)O}gXWGp*gXG4R4H9HkoB}k}O>Umy>rX+KEoe|tV4Gsjp_B?ac^+YZX5a}w1XaX) z@Om@EuGr}l#B_|FGf=I9m1i`c=YfYB?M=HYKX;rHBGifv;~aw3#xjmqSm8%BrqI4A!(@5Yn@znUX>XmP=rv2MmmE zxxVbceFR#0C8wh7k2*~XH+Tpv(e=MvWc$@8pKSkZa%5Z`XD=!xaaJ|8&(_)N@QT*n zEKzBno@ucG1V0yj46cAy z`VukuhT2LithaUgAdQT!@k5ZW>4XpmZnlqMKTXu!VC zYz}-M2{B+E%MYaL8>C$}ws*2*) zw51}k#vrgbT z_Cp;uibmHde?r(1g_({bzc^1I9V0TG!osdz($w%H(pRUZw5+5DXNsK;r9cf-j8r&^ zb3Z?toQdE-sb#08vKh_q)zw5{=0b0yQb8NyOp~~jM|r)I6pBdLqRy$71!lIsg`KLq zgWNLEK=l@W&^_W*TGkYh=)>O zI5HA|MX5kmEkb2!ehzMoJp7^T$xr(C2S1%+zuEqLvJ~_O0+X@AOp&=IDJ3h?Bm0Y= z4DJs;o@(Fy!D1@n^G5ZVLaMkXDRN4Zej4#OPSBO`ook@xJg)&g1YaRK4nhvIvJ&^P z>2k%c8x+06g)}JWtaM6~K;u7%hyl>C6}b9PiEoUieq?*A2@ zN#+z$z-q{^1hr7q=SP8`kQP)vjWh$*xZCAIj%^j>7}XD$Rjjav;CjRZ1&r{au&aZS zt5jDG;rTUodFYn7!qwF|Wb6vq$6>J|wZCHO2=MO)k>6$FOV7apCn|#4L3rX9tF1F0 z0$FLk?f&+6lMMuO*w`J_hbUC$OVxH8I?9m8Gn#~x*F^FB!LgTb07=cy-p`WBMnFNl zkAdd~kmwjqGLI~o*bTqD=L`|}gI(=v@z<*dTsa=SLNmDnTv}=ZhgHa%&lgriV02~A zD~HwSkw&jz^|#8&)}DTI&z?57G(|z7%_2wuK@jqedphUSzeRhRyCISI8kn0u&dzSz z*Nrq1!Gx>wb%Bx0wT;uYL5$DhdvAsnU>MpsQ6}uZg2|W=!T+q(fh=-GBHs(K2!pIa zh9}s8E2_6IJ%s`G%7ZYfG(%(9F?#W)q$fjz}V&s#7 z*Pgqp-!CF)kjA39i24UOTKa}7V0x&%g;+8;>pM;v_LijI#9GVtD53r#ctE~pjl7-= z*g6B-G1-gfPNAj5&sf8Q1iE~g?2Zs&povkOvr?(M!H?yNiXxKb_FshKBT4r2?caa0 zl|AQ4mO#K`qsjJ1*?!R3Ib4V4Ai}BL(D%$5EQp>W5dr}%1!D8~m32oN!S*%jL(NvQv_zq|Xra(-Zpc;FQYqP5 zG@5RIwegt9jR#6d7Q`MSd!Mk>bBiv-Q~(0~PpEzXjbbho92{Ktq<{?aMCQ8TJzQt4R0`SC?!RuzY zWxhj9GJJ@%A+_0F+7 z$Ieb%UvRok?OV8e0$r_dy}L1!E=~T@s8p3xH%O!DVP_*f)0ilKPc<~VJ!$vcde``! zXe=bvZ<|=SRW{@oJ+Lr+YR)N*W``x?hUsf2OK-#_$`h|19+j&Swm0dR&oY9aYzf2A zZL1=g?aIJK>>~w1BJ7Z$k$J%i{^nJwX)( zp)m!J1zjd?P4XZqPj-|)s2AXrhP#%vRSW%y=RL z?hhU&ajIoN(wf}2=N?s(*l6&&=5>Jp?)GQKGx)0^kul=)p}q!%-61pOo%w^a!C;VF zmZhnw5?q$}+!%c(;^VwQz?Fcfnfq6_!NvC|GU*Y_cV)1tYJ2rzo9l{z3%Jj1FbCTa zK&(}l50YmUXQ9aX=BXV>3)aC$5OXK;6e$IjeWQI(s-fx#+$tVU>_^0Y^`5=2LW23n zm8zG0B-Kb9rYs6w-S_%CjQ8D*boE#5{WwnEE}snGRG}!XZ6i?}B;*Wm1zoc9Is~xL zL7|{LUy4WFGW3(kUC5rV-Z1gDEC(Q~;f1PN-2%p5ygD~=?vC*rvaCVr^RL>QtyJ-% zuzQ17nYy-B34y9;ffA(g5 zAf)Rd<_YQTA6&vutJOPhdlP`*Z5MqAp^b<*qkZYdT!|i6utz4rMfM{)^B4+m+{rta z70PqC5l4cc1i}NylM~WgyQA6#WIvNDl5k0?C zkXq$VyB$HK(YXB>EES9@DIncuAt<3He9AxPZXNPQ(vxpZ`A_+OPLhtHR^=gII6XOe zC^VT4|Hbxbwq<=pkB5Ws^vL139(6c!PJ`iqaFU6H6V-tIhb7;OIc0^P-9|Q9 ze=6l);)M0mpe|*#sj1@3;?z`OCJ>V{+8_90%;&MW>Mx$>+RQ272s(bxfhRlaEd+Dwi*L0sx%Mz}Lul#`TH5X1Lik&Uk!wL^Ro>OeL#1NKUzGa!T=hRDA4fDmr%BY`3FFYp>f<;UZMH}jaGv#A;t1Fb0oFlN2SE@YPGS^T6zE}C6+!QeA8+2O-FD^ip<50 zE=M;=Yum(>S3nsV9!tdPjm+mXd7RyPB->0y8QK%%I`vb@v@Tdf#>Gk<5L@OVUT~yz@xkadhr|&gf6AKeA!*$)g*8!K9vneYFA<^e>GV&q_0o33ji5*~| zD2GWEv(^KY;pHKn2iO{FO(52=v4ILTM`vcfE&lzfFRDd-aHqUdih5Pm8iIjW|;0L567if}m2pG~=38 zO34P5FliKLBIfgiqnJuhr;M@EFh~^jil>K*X?b4-g_LVW1G{eKQ`R%TJ6S^R#V~ZI zqY+--12Q|-bdwEnIft;h54GT^uPi5EsI-EjEy@=E6^p>>f3IK6@{g_4IWEG-`Mr3! z#-q-KTMmKhY=}NXxqQ}-cmCpMFnUS4u)Uw$c ztlkZ0NWu>?QGGt?Ek17{Z+;Gcqq;bYfb7Q-T3NSMLNj4 zfCvst)+e-*nn}BdZc+B(;zY?43VF_Xl~9TL2fqJ}K1J~rT18(dKj{rDWhpNq8}gj+ zhJv_Lvc6!@cQNE+*ZGucRq@>~*K$rzDtH2&0tF2cTs-->M4BuOM>Mq^yj{T3r#1HB zy8%tI2fX!u$QWB19BbC<0(Udc#{>ZzFNm_lSPv z=>$YB2f8`V*MM^cPeg$kz=r$+vhtx^(}j^ehtucIrw^A8mBDuQjJ*DE`aGV# z9?xmH`1jX2|Fyn9E92nd-(J6%8CxW`_i-3Bn`4>U%1RB7JKuX+j9U`pVI^y`oXgeO z)925n5APYdZ~?;{zQ6|@*<;|z>kpe_3Z6d{5p0nUNf&qq*T>njH4-+dPF(wRy<%!y@jp@=lxMt3;Y>KRbrr#nxW^)M#3 z9pI(Xpw^hyDgyML6^7z&(bAfjXPtjb3`VWIY5A@+UO$wG=cDjv;8) zqP%%xoqWS>p?njLX8;KHI;|21m0wHHKj7)3|L&F6JIiE3&<|l}y)F<36W0I;925&K zS}+W~ZH)*wy$jh{uN7J^>kNso9TQNBgE7`+!N zv1PD)0wn8NV&4EB>>Kj}*4DvQw-O97#U6UDt>H*(Rs);jhE;K}jp6yRbouL3?p3)VOOR_Ls2boW;WB4Sfq8O3@%O{Z07 zY0mwNIIVhD?|c_OByV87NCy;G5HRtHg4y*f&&4go)V&7bNAUK3?9lm%VNJ}8&?4ag zvrd@q_X#J&+k0^WU7(AN46TcGl!-H!a6n_yGSEpzk#Xb55uoZM2ZpX4qP*~1DK`x^0)j;bIw^CI z-*~M>6;7N2oqmIg5zOu^3lhuaPAcY}*qzn@eRUqQ`CD?=HGS&t5AGpv^F2P1GIQjeHhHu$V^Z#?3|Ar_Y5%+-Me<1V?*VLYKH)N?_NVy=z9>~k62rfq7^zCRP(C6V zuKoP&H%Uf0kxEIq;l#*?d%ir9s2EZ{&%Y&xz~yl{J|_{QMP>C=XznHMdElBJ#kBEd z%;5o6CSN8_gsf9*Ms>1h)0%N}G6ZeDh{S<4dX=p~h;FpkQG!c}uP!d0>46BE9_5VV zeI$^t@=rWBw77^sYQ)9hwYY-R;-1g@pzvMdZ_%4^l7)pH^C`#)E?A~``6m%ThJX_! zq(Dfaas;N9iqIXnI;b|lPkCIyaE-L${gY5dn`zX2;komVdYtam^zh7$!S;7st@dps zj0pX5g&A0J%50$Wi-Zg)7etqI;L!^+^UybD84N+lj7D*vW))=|K{d>{1h5wu*Puph z;B{`l7T0XCM}Y4l6|Q7ZL+RHvhd|$_kQo`KLzuBGOQI;U%dJvmOr%JeSTq%i@^Zk@ zP%0XW7G_`?%xIH3zn?73ut(5tez=;irV&Mt@-1mR98TW}6W?$+<__qY?4+i90xuaeKk*g40F6Q|$S9bs1D`YVY@hKb#?95nB3oQ+Xp2DUMzguHw6?Ym z{1S`6H6S00ngKHL7bs@n@A51e!GJd81finjnQOxjTwg`~04g7V6~9!aHFeSj*b{6K zSF!Tpq3I#+7T-fqV!edDM252^xF0WF`p;6jjGn>e^dDClj;p5ej&i!3#$Tk-7<{d| zc`d4@;)FenLjT3JO1M`0IesmaY+E$$>A93SrxlJD%9o}&k*1Y%((-d^V8y97OfEDR zku$%J-1!!3py=wlw%Rz`1B<28k}VbXfP&hAV@5}9V)8u9*Qvlk4*nvhRzH?5W!nE% z&XgX{r=!EJe{u~+)A{k6C)nb|&9LayFx4{tzNmPMs#^3a20TLe+ctNyKmd=kwGIy9 zB<7}(N+~C-aGQKE=E7k6X_#wiKvrz8@)}&M*&Xd68i_^}qtTLK-MCJ)0;&;t7uYLx zm?5EL*I;;@N>j2Yh?UKE{wGg=bBOH{K21nbncFkd8d7{_UPZmqj1K3NQ>Wp z&{4ea{>70S(?!Qye|colqN_)--$m><>Pr;J{W=?=Ky3KFXCthdOedEwH5Nw?RS!)p zO)N2Z)T_(wP0-jfgLjL51bvczLDv!ViM6y-TsM#GX7#;)yXT#&An^4nDaZ`OjT|d= ziIb@5%O>B7_&#NsU^Nk}LKM-VaWPPC_}ccTamx=6~5-=vlLSufD0HMfDf{0_2c78US}w&?|<1dgv`fzcBRPq2Cx<8+u~sDT1HQ zUQ+?SqDSO>+XpFa(Clh+kU%j%F}unDI@GW5HRlz6jy=uGnrpn;xys2y?Y`m}h|~7t zKMAD1iBP(43t8S72)jLTk0;_s$~-9PE`P*>=k9O-X##lO{_V?r;A?SSj$Meny!}W+ zJm&Xt$>-j>W^P~Z5EgBO*6h!=knPP8LDrEn?ID+3zRKssI<|`uCc7u60?yo7W~9=o z6)<~^S0Um&bW@Ze5@O8va9q1#PA-64M4TT*rb1(dnUpdBRYVBz5;`JCg8eS4;K%KD z)%f|=cx$}+-uBnoA_P%UCvfGf4o3pn)_C{P@kdA1_cnAx562TI<)@%3pydMl0mgJ- zOwmS~#;WUD6yw4J0U0LOKcpS;7)-_W#y^$Bz~idZ^{o{ad!RGu3Z*UK?MJ_wvb!<1 z>UdAD1>q8C9x5HB_@E}p90=La{_6%(t+74y1PaxC*<;ma@ znSc-1Om$#O*u!wH{hB1H_w3m>iL(NK;QAC@^oR@M_=+K7ih{xGsyYuCYKVZLVlX&+ zF#k>n!1P&I!=DYuz3jBHK2;I{Mi)dB5^zRhg9Up8F4+Bju-6B`H3>r^Z{WPkd@R^K z+aP>OdMR5>quTh^$(W!wwt!d(KKoj~_ZO9fm`Xx8!7@~lFX z-;c7WqU5C&+zSFjF&*JB=jSsaxcdsRTyTtX$0yqPOp}mDow(x4rchkoE&hE88&&c9 z@5TC1Nec1rqzDvbDqkdiR~3cO#JxtJ9rS1IGddc4Fg8I<>~o<-=$Pe-J3J+%#`gNl zl9KdpNm^ai;3t6`Z+g)Xw$lC%dB@ypi&QzFNw~zt7}RbPul0@6S4s!6BE~^I5o*mq zF3;W}dy2xog%7KJ5D*~9t#@?PgXMiqaEA(G0HIX{G+=|YWHx@hJcar4*;2m(k&!}r z9Z3zmz8D}N4-(70jf=W1NW4&>4k3WwP`jS1Uw~dUU4~Z(;%WD~F%ky{To*PsWmX$- zWmQ|f$340A+VVTv8?in7;@xZS3NuA6Abq+YX0$y;1A{Fv@4rfzw_lmV{M%cv&0)T= z2j-`NXD0<$IAJOTJB0ZWau^P5(wVIAv@-jtg;@TE_|rfq^a?_@;(c4r-oT6LJn;St z!i(AdJzuEUT|;qg3icD%rXXu7L9xTtfB&9UP3>M@5tqM#8SjvLu1{6N%(WzXrBXwK z){<(_QPBPtWN)l)#q?2<<;D8Z^{6f2pzE2mvE_;!$zSffCdV+^r1o4>Lvr1qYnm|P zn*6-nO3WJ#+8W#8#fyzbKg>Y-&h!i&Z_o$=_HARB@QXnNUjBsRd6zlJuoWWSmgr{- zzW*dMj!+Ex%S>#Yi6b*HVZGg1QyMEq1(SB3&km#U`8;V|aLagnR$AYHg6FMZJRuDr zr50_eaK;vpxvIkyh*)gQi#z!76qc&FGQ5b0nWz=MoDC1#ymG8)&xErjM0J!C9^ZIo zHXiZS$w0}nk&H)jSF+m)&`zZInT~hE7{>zIJ(NTh; zo3J(m#ry!{7vSIiJD?c#PrbHq(v??af65=pX%BEVeL~|6xQ`BUw)@ zOJ4>(3zJLB#NRB+NTDEmhr%rM^&!1XfgAC%E&k!C=p`_Jq6Hf z4pHWmVFB_qX%m)m6a}`3u~Lsz2x8Xocu#vvrBaRvhNw&p4gYKM0SmbIB>(L;M>3jY zw59i-meg8j+2v@u{q^KaZ|@LE>^;f$*KV61$!eC2^#!&L6I=#i`quz1O6i8VjZ|NR zee)BSf?-iZls(KkNfReWwXVrZ@*E$d0dzP)XH(Pln=?}G6L;j=!12sEah2^x-hz9gw>OMHE^XM+( zU1ASWx`HSf1t|`EopuXSB9U8z>V?jNjzOrD2OWy+b3gMazxn~Rsk68Lo57Ut|4J#W zukg`JKWz?TJ4o&6&cPA$E3tH?{e8r3nY!90=0hi-70O7u8i_7+d!E6J5N6@IzM_yu zxVKtx4v;T#_qax;p%Js7;SIP^8r&s^&_)ETabR&KBe!8zMIsmO;3DE4DJ$+0wmYgc z0esa6EETmBs(RE=Z90uyh3oX$2HMfUIk8l&cpb5r!&|v2Tk&||tr{{G7pselE7-KW ztS=~g9pe)XSa?GM0Tv&(3I;F%8jL}_Pfi=2Zi4?X8R7?_o!n}Ohvt6-$>BxB-8IQH z&P%W_E_N{laQKgP-hCYVPUqGY-d=*yj-#w75miaTKfDbGnbWdkaIuj@N8H#)J^8?2 z`=w;b<6ZT7z7g`Yzlt_wiAdy@>D2V(Olo=@|5+>K_Ph~+v+G{!G~xZ!&v^2mjm9I9 zc=VvAXbDwO>1MhC1UwX+ScMEjDf>G>1crcG@s%rt>xYd%F_677;MyPhIW&?S0HKw3 zcM0w^ascHxlyk%l2Ss{9C*y&r&d@2Ix_ueA024ZI zaCpEu0bPZ_WPe_oC|c3604y19KXEBw|MK6r+trPY)X{raS6813+gEo04yA?&urd9T z(V&LDq?2s$rnb?3-H+P_xSkf(iK28iPleKzOG;c-FSYL97tf!hWIER3r;+Ou?Mu^< zMjR?E@gHvIT7!*6fz&_=$zv1}OyOK2mbL| zfu)XAWHqv^sj8atXHeo-FTxZvs*AwY0dqD1|R?+8^ScT zQIPsU9DpMFB46%+Nu3oam5rR~gTc~U94;*Oa51%*Dh^M7`f>oOu=ow|>ND6{!led4a&(Ez3s5W}=ck0@a zyQQ#%Se39(DpgAo2Ls~c76gu5n?Svxl;jIb#WQD$Qn&{m4X!LuqcGTQTSYnuF?KI7 zg_=MufFy)l_^q*_{>8OvB^uBKP9rp;oOtmaVj6?i`Cn%VHeUx)6OA0HE8Gpo-MK$t z-(>H@PC`Nu1<}wAyWTvbR=jC+sQdMe6M?m%|>aZO( zW)&5lS@0=fo5JiUkb)AyhZr*Ita>5T_Q8&H0lP?X5sbCtLTAf84I9GLkzkqHHtj>_ zRSE&(*;U<|B2>kMihiK@nfL@gwm}J4M7B?n`<_LVy_HR8Z3tqZwH*A36tMXV_aUAYVtLKJ5D9C=DDPWlbFK?H?N7 z`-51V=9FUu_Gc_G{|eK2Soy6{no(wyN?l;1Le3$2_VE&E!bfy1k|=LO@%sA+qrK;U z?AxRIpaDR8bxi=x1PBzKSFCoe1%J+Z1Pf|a}n0@p^ zk5Z}Xe6RDJ$N!uY|4l@e8TniCKh=Hy3>}QKrAY^%A2r0K_VFuyl|PPkuP$ICVtm7}3!WS}3rOjk2i}5#2TS8PIgFxLA&j30t3_|@ zc!gk7s+0vR=H@H^6abj8a(b|K@|g_H0R~36M{$rsPWbG`23znjg~U0$?)%YE#OYpl z;LuCx$XqrSH=+?Ugw6`wq^i^|&$i4&GKW4I8OmkHX1$o9g=6w57BIpLA6O_5J~)#9 zL~iCV`X5#7$Io4SRze?YR1*H^0Ti@9FnWl5TeD-?+|Wqgz{L`#W#dATh^pQcKwqMg zJiZY_Z2myl2mls*6Btg+3(5CIB~y!!QC5J1hi9_oi|59_ZCad~M4a<+(U%r^_eu^{ z1GYw3)aw9%G{8OyzewhwVjhsQUOo2nP_mQ5-20E>cmq!i^$W89?0G2I$$uy6vTs7F zdiRKN>pHFrKoSB`$)t@Pu8u@R6ATNK3_HLmQI28u5?$zgzXbd>BTaUQ!w|}~3X*~S ztK|8g?bm@HI&%E@GF9D1aroLYu}ESB;FHH_KLu2^*M^>J+hNOsdQoV;Nx)%#@sPeo zU#0z1&}?uyAJzxB0?BC1!T$d41rZy>Ub^S6_*eZ+5OocI`GfD2?h#jPlDR?U7yk}j zm-evUbt#1_pR-&;C9x85xu6wY!Cj5KD=Kw9$C`dl-Cy0)`D6d`Nb?8$U6tlXlV5!8 zo@bzFiPz88?j`#B?T1c9dxY@m!$8xeom3>MbI32nszms8moKyK^#u9r)R$u4U4{M4 z<<^Ft4;%FMWMO_upk%nQ0itOh$|8aaozR4LzKW}?Wy9W zE@S`>_YHt!k#7|MUzh>}4;N`g0^EB(4){0%@Ediff(+4pVHi6$fw>?`(z-+ec~V`; zY2HAbF2K~|K4R}u#s{1QSRwZJudsFH?YzrI1EzLGGXtYs6ClkH!HQRMS90t<7vxZK zI9g3t6&V%nZ}W%zuAjYuDhz6<*+wX;l{S@oZ!dCXD=t*=3kwT>eZedxnM6Wg0@hPl!2r2fJ=mA1kQj_RM`*tkA)GKwOk32Cb3RxNpeLis z2>#crE5LiU9lkSlu5c{>_+c2Y508`isY(gt>1SnSe*DI9{E@+Hp4}QcH8DRwaVpfo zJHZ=zuy{&N)zf2xWT4;%C!TngKs{h+MeHfC`qTQOD3)pb!NAUQA}piRs2*QsXv#`b ziMcu1mkb)}bBdCd?693`CM;cHfbAi|v?XxpE5Dj3T1lTgN9E~~K~4TuNy$rUDrUm4 zK7+QwLK%E}=geDOa(B_%cfaVV`)c69mUNVLtZh&|Q?ho9hK zf|(PiYyho=lLVyG>01=oh2hrxA{kmHA~q5ktwa;xIw}3%rSDFEW~G#@BI}lrZ{EZF z4o+xA6BmqH+$D>R8?QarYPHtZI%_U$41-l6vGn?s)=wTYkgR!_b$#Kx4tO^3wq!9N zsDW596r4g!yuY|8_GFz1W3_pt=JjIpwXm`p(pTFsOAxXS;2j{~yTG!lF*dlkI5=jY zDkbRsc9TN*|9y`h!Q&}Rhm!4d?REfWifKok#3vl)JzpQ3-?tIgU&94Z-4>paRuvdF zDDxXlYA9_4m~r!f6``I(ZI$Mc&^LPbsE}QRLc+l@u6iSGAw;{UX@6O7?Y$*P?jIvZR3Bv3M5Z;3Cml!Le`e( zCaX9K<25!Q2h&4FzlA>-Ec zZcF3y!g`H_9Y9`D@nGjlb8)eib0Op9Oa+nnJSIP<8-~76t*)@@lL|Kvb}kdi)CIgB zP}-|k0+52LINvML9qFI6nmkXMYfW3k=-P04EiQJ}78e=z+zxzH%k8CFjk>4X0f;q$ zVuv|TkE{GX#4r96`h!Y%T1a@q!!ChEn5~xxl!J^DP6N9_#z%#@IhzA}+w<9_ASd`0 z`NwPnn$Yn6&T0Pi=-q{Q{KUmO3LlpZH}rbr>5%D$9y2~l?>=fgLhoL%{hn#t@@;t2 zg3^`SKYUp{9k{!2$Hf!zc;VwA#|*s=Uv>@pa>zCLmt8~tOyZblM(gx#7ugOmce`yG z8n}S;Pm5$(>9+v}0Fa1B6gmd95%vM%-FWn9TQ(SJO-Nx$OdRjLt2{^8xNo)*gvDWd zXI(l2#(wQxcNN>F=?{iXGayS+Sg)A=fH`W`{Q+al@GFx4gl%U$J^UeheRrl!Pwfn9 zRZISNWl+2p?~owdF44UrzDmPx$tMS%9C!{ZSt1bxGh61xhFt`c;SH8C`BTXm4MD3?CWmcf7Rv#{g$Fu7 zb-}R$=*#$slCDGIqjPYe0oky7gpSdvv$!(97_U1GJsQS0et;}){JW?*F6Itqra4_r(gcVb9!j2m8`z$RX$5jh;vI9oY3Mz`Og>)lqyCENn49j*{4hDSz zzZ8*i3M__UDPS@y5K?446qTZCM1pAMNC+tP2clY9O-Z48OgC}cNK}>lk}ptIdG+yt zDFx&p3Y=dfvKDbMgXR!e@nO^zzK97#TneKGDV)sKkY};HfW{#A1eb}5-15J965Q27 z2$;5xuo6_CuD*o8K(TjYwqs>tMfwIDP1C|5O;c;OuG!X|cbYNXuxqO3XsV`%w6Jm+ z;=*Mm-1)8J$L+Xz(Tp>vU7jhoTbEm`78#7FVLfcQvEUumNE{&aA??T^y%dXsksH-J zg0b+HQ#${QWrNhE45i^mM@MzXOe9RF^H)_aXF=%dE+4qVs7kN~=ojL+DDn|?0T8zw zp{WU17}BOer}L&;g zwpKYgvb;QUG7;Us-}d_@XUO&A?!n@yZ@qux`T5SO*1Rx&|MBDZk6&oM-?a=cpfP{E zQi}x@V9!ocn}xWuifx`0n}Y+_@JwoaTzi}fW5&n7|IpLP;Ps&sWe5ZQ9^-B7dVwrj zsUak_T~YMrP)hvqcQ~3FT0-VBp~ULjo;Kq0c$%9u-1N8{&y0oQc;?X{349U{jlDny zVlj0Wwmor|##h)cvaiA|2$2O{38OSwl87H}&cp5RVPz|J!TSZNIdSvC%@fFrk6+U< z`M;5^IpskO2maer`);0?xOv~YW}4bh{d;=+wcn@5-~VL-#{5}eGB;TMNxT#+8C6@p z4O)g_TpehD7%1J~TD12Q)55BP=FRmq8LvPgJ@#bW{(9=ItKKtlKFmr=z))|F-#F2@ z7;6NW6iP%)L(91#ROF7CNl6a5SwH@3h_>1G?*vV)VV)ep-o7+(b8+gc7?s# z1@28=kSzA>X^aTsps4|1D8!|&+EU(P1lVWMDE!M((u}1cc(^$f)QBXIt2NMwU2LEc zWTqcEX*M(f8@DwUjb}C7BkX2<2AYOeJ+6WGBc91sKQeU${fB-SuRIMIss1uG?|IQw z(d;E^c7H5a(M}TUw#}@)8;pVU1V4nVjJUN*Z}hJ6EZj%+=H&S|o}bLQvLF9wuLVS2 zV9P)zOSPfYjsYz-&ooOfFHN4G#NQXUxAg`ZMKo%ml!Pz!x9H_lU!*pD@e&_a-o7uE znkD=ZPaqTI8u(zhoovs+|2@!BEJ6Iws5C!?fKueE3iAcFx9KyZ&+F zADt_cbsM7-)88?x5nFkx4s465akYZzUE_Hn1*Mhl5-QI^(4kdC7&XD!vhYG|V|m_e z_Y;_ohvFxOXXazgU{ts4P{#ZyOvs-HU9-zwuJ>5rWxT4FhBEoNeJ|)S%oO{~g8+8k z!M;G$T&h3xJ$e@W@2ihltU$!)(S4-S2|9K3DY9;C?@#iN9A@ZK>Q{&nepI`KwT~W< z6PFvHdH+I;qX0D$<`9vnJ_y1=ET82z!HXyH~y4$?B0qzh_tnPaEf9+ z4ek(`+A^_dBxa208b)Hga?k`1`I>WK%!xmpA7&4gBhlR)TCutgeu=*id zL~p59=!v)eD|}8lFpbfy$OHAZAJ3@OW-3SIlRI%q~y7@S9$e-98d^w{8q;QLFN0 zYP48pFV;RuZ$4C;6sxN@4+N~FNLdi~2yWoPAV~WI6FQt=m;;m_37Z4FT$tHA!YbPP z_StEG%A(3de#^_95f-0By4d(o)Jq+yeT%=eyyQ`cSz=xiJ#n}bU`E@6o?bB2YoUmT zUOc>okS#B_(!`@Mesyo6Hce@MiZE`VG_?)uqKCv54ew!=Gdi7P@0~AX<~rY=%M{kv z*!*?*%-mcC4jgI^SNGa@MOEtukWy9lqr5_~Qi4SvjtH+JXHjhCm$h$dAHMUD@ArM| zXr{mj@L9;5G?>q5BL9GMFqxDxh45{+srl^jZ+?^algJPm!W=H+9uS&%W=a`rQec8f zMCEBskST&fAB%FM$d;RyVOZzx1WOsTTl4U0M8kKUYs2)oIQ?d*u+gllW}`2?mDz)E z9R!z_f^Z=W+U%`wq|~t-+9k}}8AwYfkWKN1ftw&TVXrMg5^hlPV!|p*J(thXbID`lt92TN~T#d ztdL?=T_+q4XXEnVpd8=0D^!aQg@)p_&|OM+Wqu{x`4a+vw)x-u{D$8bQX)oZ#@3~P zMaX4K)$N&(6AcC4luG$y8Pm+f!k(5zKy^rWfHl-Z!b%p>JL^p}9jO;;P2TTRJ$99c zZbE_QRnXA9$!_CC7%XazZDzlu&6Wzh@fbe#RCcsv+uO>GUGrOAFrZ&H&C5@lt7DmB zBjv=?uz+O7-fYIG#@ItWc;IT_1D(r&c68@}fLc>H0nZp#vdA+iS0J=eY$91f`<3N{ zQczz*u5^m_Y_(v_?UulbsP9(cF2)0Eu3jc{B$8Sp$3IROp%1>J>MCw2B5&xH6dO$z zU&2IFf>Pmj;9SNS!d8jMGMuuC4=(qpPP~~07@xBNIu-Q6oz_K7s3;fV0>4ku)IEGH7#LY` z;0=^#8^EQaWN97IR364^mXv2C1VvUAnCQ!torvD?_))fU;O@_qq0oE``c3(7vR1Gb z9IDm+Je$S~86F|RCmmd0hbR6-5?}+OufmDlMkQn&65_5#tx1IbI^DI^qjr4-t~GlPk{{&MX89-PCe5pa zxj(&?a9GN^eG5!~2^mP?nU@_I#@y)!!onz36(j+yc<`923#51xX!McRd!6cH?7i=e zEmocPwwA=P)(AgC926(CxJoEk3-oWy;QqBSMI6L`;9?sww!9eI;NRYG(3iAbi0|Yv z8^`cH52<5hFcuO?h({N}T^oQQgF0}hO1V;kX$tSKd3i3oM8uURPfYL6pe~~I+zEc^ z6VC=lq@d5QnYGT}PVKvH-}z5{_|_3M#Ew!cDm7Dsl}Q>2JbQvub>cZobq6#1r%z0d z${}^+)?fJ0d3=e+TJ+gpvd_Z{L#5Cqc0T0aD!-owZFENA^?L)v9_A5T$@qZn)6zjVCjRy}uVBBxdo0ej{ zc#N@M427m|H4A2IIy9QK%v=9iF%1RWM~>yo4t~=i)rH8H8)oWCrXGfQd57wNQP~EbBds*A?~%5*3f;a<_GUm|==t zjil2NJ)QoydM2Z9`DX)k(r7lyPt)eNeW>ei<6qJ#l!R0$F?WK*H;}3pC1QX%X|R^k z*?_G9$#jsBiC$%v=G)!5Mzs4O+!NjKeX?n?zfE>W6Ip!nMNBX8OJfQKx6!Gx%bjKm z#<*c!lH3aEsh8<52ktMA~zQ5*h$yVd#+(^DOIOsTogQfgPt_%odEY(Li ziu+T7tM2_R+!CIi?p@aC%4_^$tleqT92D(b;Ffw9-xBuunORSm=ex%DUXHW5fEUR{ z6S*^p$`?-M55vJ6edKw5Eirz?|81?5HROeI4WUA)GV#ugW zfkJGcU2S(gl7xVq9(d!x0|Rg8?d@5ZrwPjm(Su;}Bw5G^WQ$=4lgLd5=0gkL7W-`q`_o*Kmue_w|sHM$RuTfVz8+ynwyA)Ekjau&(bVIHDWo-a8d_xj-gs!b|e}`)RhmyiSYH}{co#~64m=?1Vt z_ksdvXM$yDh)1xFWs*UmjAP!hHNV8diDKAv_uDVU0+vZ{075+Ce$VYZ3rqh?u>0To zePz&AzON*0cG4GQ;bJ0eT7lS0_I}r-H%xJy@4H97ti*77Ah_^-)lTAcd$7MmFJWpP zqM%mTMZnl7~@6yf&BxZU&2PBoZpsXI5ZvP5;Kgvv)CSlcfR|EZ5%z^c(`%+ zXl2+A=u@YrjIfzMk~hN!Uh4sSxYAk2dQl^0%Ayw_IbpEn6n?6ah@@K^w4rVww+^5| zkUhm)OP3;%CS~b}ZYUaj5L;gMRxYi!G$C3j&1Df;Ma#%R4zbulVUapL7ah>bYsQg) z=LlaKrYfZA_3HI{y;;|X<=Pe4 z^xbBY5`SY$cBV}{jCcqyDw!^P?O9OdBW+4%qIt@PiZtQsQiP5=# zqO+zEfg3uz@cAv28PRO=+_79eH#hqJ;5FT)Cg`EwIfZR=f{7Y>e7XXyMukvBU-TkS zuxXx?D-KXzJqRg>wRy>|GY5~24K;oIse;v~$ zRSvS&qxInNF?8qLXg%s6JN1!Km47Q6HFzhQ&k*gOUpR|~$TLh!D{9EUh{}CReRK}3 z7`rA|*G7-jYFU!duw!(`Iu3=gK5%^C48~i4$l$HmYznlMapEDpHqiJx9<>xZ3r!7| zf?0$$lO*__(6AH}lc3w4;`FF!2>fu__;0vKjYYEoRf&7^Bgfk`%G&*DAzFm0deg<9 z7p)1(YCNMZ%iSr$1~#Y78a3?*LbnHi)$2(RBGy%;FBCcl#Tbg@wnxzB*A3i_^^2tg zB@nrzpj$Z8Wti5){|pv%1qZQ;k&{q_tU^PFl3=&(NE%RnjC@7IiKkMt_ zSu~$MK%4MW1?C5@03||e=so>)5I%~+kM!kLG!CC5pcV22a;1cLd)n+LAzO$-nPXg2 zo8v8TDM}v$2U0-C*3p4bC?p8-ZSi&pZcQVM!DOvsXeMF=kp05Ehv=xqZRBA^R#q4{ zYw;+KS5-?HT1MDF8$q}r=~SxCiAA2)cqTm}QnS`-H^P(w>dCjishtZfWWFb%QOX=j zT}ItVoyI$pmskO63B60mDVkH5k#&;H$ok^PS!}&sxVM0AXs@&h3Wj)T{H(9kypi^Z zCpwThZSu6y891$q4(zN0B-yzB`o_vUug22FiqJkrbE0QA;N}Y4dm8&1MjkJA&`0m- zbE1SYLZc7xgOORm_z9Y-$hL|fSOHL30`~=JwUu_4mUbJ_(m0)_WmogqPQ#By!PW2n zYPar+Bt40F96M-FMn)1NZX`p~hz-)#y?JDMszUuyms`6e;`T0?*|)URTHX|m-`E$D zFE^`AxXm}UWRluJ1EKKxH}4AVWb)W~jMAfG${-u_t4QcwnfxtptT6e9wFjDy%9{e)P*{u)1bZbvE$F|L3^eQ1Oy_Kkr!em5#3PbBmWBf?;-ntWd z@QGI%z|-^;y9%64u-QWou_1zUg@}+o~@P+CZh2v zO;L@8mQbIDi|#j~k+?c!PMfN7?avzNCr+I1YOfSa(c3v88Dm-EzgL3OK|Yfu#6mE) zb7k|NwEzmm-p}`k8kc9u3Umq4DbU@5kg0e{3K6CRnE@^BC{I7jWit?JalJ?&X9$KGuG?qQ1@7jUF4zD>=a7VD4;G?6_dGzOg5*WrDFwO!Z79=YY(RN@?x z@{X(b7`?~MR50K{1|{#`EF97hF3YG}z2%PZmH-wT&6R7(?6>5=$A^>oOwf_ST5cN+ zHDT>rR@JDJY(|nO_bn^FK@~#fUNr6O%^ykAwjRDZP22kX)oB9Hxddx0l~*W2(jBlN zMfN}~WGo_hbdWYNH7!cBl=trJ< z>LZ;?>^3f>)kYafZUtRwLUv;aMPH+H@Nn+bsi9M+*8D*~s9Du=ctpmIDokcVp6t>U z!>s6y<_^jbI|Xl5CI4g4*4cRKBOiH+Ij@1fjZdCBRot6CF=s^?#{$OCg5l1BWwF>Z zwNS^vy9lfgI7$!!ENl&h1Y36??hIvDr~`n!58Kimh!R;t2@X^pT&pry4U=b*xHw%XRBv`YpnxEBxr4P2CqR)9jms19iXi?l|ua3UlCL2~RRv-44Q{2||R&2xOw z#C;H~ew%IzmAFeuI3|5iEt%DagDp2<`R^m-Q#P!Ax%LAqLBxdBpQ7;)Uby#RjY1ND z?n42A6#spc!SaCLMiIZ$90@Hmhw3(r9r4*um#WnX_w+uv9V#()WX!2n-xoMBuBeZm zo*iprhoNDS_E4;FaR<^C4=ZVS4r-9}1uZOY>dJ}FjEe4 zr5vf8`rI7{$T&dSI$LWk)Xyv+uwfH#(G8rN>A^0lm5J)@m8z~>SzNrL=$9d$wXlXs zzY%zfF8`#tD2n7@jfkA;;Gg_9(6_pID|#6!7GcNc@p}}p1TPhjo`UHL^MU{iIEuDz z>P;H#bY#1R6@WDCZu3wIhD2Ya)rw$;hwi~^Ds!_c4q_)kaTz3cSZ*RHtN9nv1jF_i z|ARK%Avm;*9hOiFXboiUW{q^CVgeJSg~h>v3S1`wJd=G9dc-g+fd>&Ca}y}w8xaM? z^ewpg{wtWiDU((i4&l7l>vF6>f#UHah#A&g1hTqtzLUR}UXtJLGzBX2uw^#Tk6u2V z_|TPH=Q3piL-6uH0>y(^o=9wb-+0V&_aEHvmMo{~Sdmh>glHQJz5E=ofBa`KLp_j-uV~5r6Lg9KL#8y@Zd?ilbb$n7AOogIRv_yldxxT&AIqFSFT3RvUj6*1 zGhaWiqz;ogjyoQNSE84#}p6cP_ZZY`bpy6a)4aN9|0%jb(pJy^^bLi?+&w>MrSCdl=hr`fo~|EFD(63Swh(XLv#Do& z6)~oq$H2YSkF>+x2uVim;b|^Jl9z4i(Ye-#+ouMN^Kh5(#_*fD@_8peeDda#!^0;> zHdBN}b7xI}uZ|Ic+j^xsQU~MVUm*6>%awJ{9ZTcLdCm2~H3X?pU!3N-c=qm%DU5_v ztQb761G0xAkcA$P!hKvu0;Xy2D4XFMk>`4iDaS)H9j7nvu$ywR@~&R|8e^_6FlYt* ztmX>@l~5w!cYMfI=yUvmgcXD@!5@(IgiHq~M0D5}tn{f}eg2A{m3)ep!s#i0Nb?Q( z1D0d?f@#ieqlX9z8rd2>=>k(SM{@yra9w`FLS}D(0mJhvM+8 z==Sui?U+)$61}ZyuwQFNL>lrupzQ(_hV>&48bF1zO){|(PY-mmt~$MjFw-?26H-w+|28Xno7 z$Vdi>c5|^qOhYWHOa?Vbx2VU#EtchPr|ltg=OX{X@P6ZX=6F0wwQC7Ylg<9!{(^n& zc{Lf`y1wiChf^L-G*Gs+3M>r}HQaW)#=gYoHf5sEVRShaJ7zd!2kyfnKNkBuSm~{| zggmg)nOljKs!09#pKCQZrC?5HuRs6w?5B53r_Kd3=-0YPzE#|B47j+X0Gi&6a5utq zJc1GLg|EJqe|O;HRFeup(eag)$_6v8z^kA2oVM`Y%uc zUVPZMtFk9^uBD^bbj!_Ik%*OZ%?RE`O!4+f(WMkYkH2{+1o(sVkzVW<2T6g!A8DKT zn-`z`nwwPu_^umJvaXqsWq@6G9%<2Fjb{HIx(KdN5MP`YmuZTFO7}Bt;<$SEGvGAI zHcS0TvD<^-N@SfqGO&Q2eCNOu15Xe9?7%NUZ|${z(+=C?4*hVkt{WMh+?!>xm~054 ziOC|4P{0$Y%pj#zf*Qfa7056+HH%m|!rDl{H9{s9qvdgwFkOdh>5fO`b|``oj`i*k z7BS$H6Kz^!`t3$gd{s)M#g)0-z5sw`V}AS#Dgx4#?W9%L859W#P9@hCnaGe16g z#EaT)suJ})qw9pH5~p+S#HV3? z9u^)SQ{wl8?Zes!t`J?yaW+1nCeY203t;aPA-a_=APuJ(jIC#`iA5W;xzq^-tNywJ z-kGu*`h5A&;>8pHdr(4-U$*#uG7+S1g+BP8JsR> zZ!2H7-%RF*#%7YUE;87YEC>SESQ{LyZ^dTFI)?;6P_j1vp;9hEqK6q|aekisFfeMj zI~y(xK=ebGfnR~|AzQCSHWqTnK#3#BZV^`$+RC)!u**<(R+~$(wm~!}{2O$owY4h* zFUC*bj`d;CrUAi5avHkH51~|&XIrk^ zGVe(HrrtgIwkPos3mhInjZ7!6jXd;F_3+gDn^SL_eD~D8ejiX?1_OI?hR}i-Cny_$ z8q-~%==y1mi2K2A=kZ)(av3pnPiC{#b6GZ$PLHt3?76SR6}b$)fQ$xnQ>|2b-*CE~ z#Yumh%MxWcCD-}=;eF}U(Rwz2Ln%y6C`U?S2yD+YDg!8u!i_y?zn*< z$GIioUXW~P6B39oRM**(R`8}mJd%lo^YM5-d?9{va`NPk_d5~{{uTc?UWnuD$rF>? z-*Gv!OWzPQ4nm=WXa7i=8UPz1H^(MC9+Q(TkIXr`r)xRS%W3Cghek#Y#dI$mruX3+ zow*Sxmzxc#k>*_5JA3_j^q{$KV_f1|JJCG=(tsT0TSWJ<9_;STF(fjC*n`cS2xe{A zZffwVK7#+*&gYa=G@6S3C4>%&bs0UymezP$1_~&@^f{I5jf!~s3~?M`=l(HE#gi6{<%i}sBXY8a-7unNJpZ-_vjLO;O0z6iWs4SBtq;Gkq=A+|}K%JGnB zUvEe5LLj?=*1+6gVt~t%INh%e*N!GG14orL&Us^oopm)7Y(S9#B-iOWA*q-H?S*_n zuRf2U&#e0`*F5KrAy(GS27VrUV`w3ofQ_ZaLpZvrANjofbM_640asj#0 zkaomJkXxRV0|JKUkSy#qgiZzSgB*n$n3{1{F3#+qaAixm0fjI1|7+4XbjX<04vmc+ zQe*g8U7~Y$(V`fJclY%^skI@d)V#8}vA+&-mck{tUh`ows6ExsoawF{4no z%1YT#hp%${=W{1!2bGcLiP@w&^8fzm^S52+OFI9i_&&9T>x*1JSJBJBgd-G<7oWwx z0E4~kL4wDy1EjRPT)Aa=e)*PmWqJB7BPZUx0j9IQ-iGU9{kj*H9CQTMOjo{4V6^H; zO93t{L*Iv2tsoV7EJnQ>(NeC6G;GjVxGk&9O|D#61P}nwPj1j^g~CZIhUm~iIc6op zp~?iCpZFJ^W>$+Fz3FH~`$HeF$-dmZ6V->S;h7UNPbJbL!*3q;`G((IuBHdWm3XTa zulyGy9*M*$SuXsOHZ&P9Od7zlu4G;o`Rc<&I-vML#o)G(kVYjBqOtjRHUN;o+AH0jZi0f+_gki96` zop2BUFp4K65lO<1#jWWuP`g(lud~thvw}&&>v{8)Y_eL;X&IGLwWRZ@;TJ-CQv4qT zoo(R+MZ{myiK=Q)OZpV}K|Rg_`1Sy|&OF4q8BFwRG1Xx=CnG}^$4wT3#BUrpJzxPBun4|spfDGlLHVBDx47Jy(E(6T%Jad|}+yp2P zdrqx(g%E<ffaK%gygj|@aE@M+*?U2;v%!e9V?#iIesjY;TAS%A!Ga40^tQp88aN#Lq!_+XTu zZuHD&4%v~XW~XUb9h}APR!AO8E5oym5yOhusz0EpRvW53Q|*Xlj5KDea)>^tMslX^ zApNE*xh%l!KtRWU>XMB&mIlGgGXH+i;*jOspv7ixoMJ2>DY6uz+rs)_3@IRPHQ2t8BL300yD!V*nbi+7!(7+lNo=7+$i?BV4 z4eu+M25T^a#1R@q0S9mp!|Mqc-)kiWnQEyHJgctL)Tf7AEW-swgszYz+)9#gkS%o} z9RZES6Y23)(t?|`Pni(k1ZA`V3AzY6o`P_;yKjL03ihalc@HxeB!hnd3R&S?q=sZ& z2=!R*lNDWG@2m=9*P?=`BD&7)5z5vm#b2K?ICiUDlCg9(qO8}8pj8M909w%4*<@Va zHF}_s(2-XoY;y#y%a@l>6pY5LuyrhV3}TzjBj+z4ydEL&<;Tdm*2j+KwN6`1Hk%HW6_fia)AWpk}!zFmE6_9S8VXgwTO z3tBW~Bdxk1BwV=4A|--y5l;}597ZJMuAwCK?M|MtZ78ugv4!|x{vcyLPKB(y_t^d% zN}p>5RR?)t(N8iW%uff z+7s{)c#z;LAOVdhiKDbmMS6$XR2Nb7+`28prUxOdvau<1k+d{CoKPKo&WM|4+?dlH zRZk2LBOY+FN}t+B%9k*Pha!5RY7F{P266yMEkAOO^~9{8?3WT1C!2~V%tIE199xIX zL_C#sDhbIipQP-FsYs^ij+AVxP;*P!ND8?TxfMfH0;8lagRlVeu10o!$X(^4f7*gQ zMVKHFIzaX$XBPGYZpt7?0@54+;WB8g)oL8+vbebT?jiN3$3`=w_#>z;FDUZssCv}l zxEBcGYwcF;Tr|=-Y1?D>H?%8^eTn|`51;gJ$w;FltFdH*&%>T^s!E){dh zcnU8uGfBYMR903Q^D9~`Q5vr#QuWbB#t8e7?oth_k&rsyQbWl42-!EJBDy^@ls5Ii z*xaByaj0t%s!6h`+nTPqa%8lTn<$Ov@`KDTV-lqu2PyFs+0S&-bfd0eNryZ$5m7Nx zu5hYsB755KWsP+!G7|Qae1TX;+-gQvt<6`EGK=VNsA&{w0G$R{A6Z0>5$dO1Bb^%7 zt0<+t#-o+E(p+h-C9K*8z&*(2xCTE0uhs@g44^XN5t&m45Ilx_*e#?Xfj<*}#F5eG z=;_O>1{46IBT9%JcL2EO_%cjo7t*%@e`J^TLwcw$$G>S5h^Q(Hq#4QiX zrY0e0uWhwAa5Z7Tqf16$uqoi8xFVDzZllGV3PK4jpRhU^`(^!Yx-+RyPwI~qF14N6k<7-VoU=>&mX|Z(pfzmyBL0a-u!Hot1Uc`!ZW0?t zK;#cX3}SscNR}?lQI4X)bL(=oex`b+US*dunOZrQtJZV5S{bh~ixHXCetu%&xV`1S zXEdVg%aAIQq>_|&Topakza#X0k3u2QO181%9-Y7J-9C5bOs>{`YRir2ra$B)U~pPB zAuEaDppNkb_l4Zl*RG_`QsfM-eUhDatk z7}g7weAJDl?3!b{rlDtsa-(Uj7%t{emMNR_qTygPwhu5D#M&EfxRbV{IT>V_PU7^-`8$K?C4RCEsq;zyeJ>Y|Xc8jw?yh&%x2gcb;BGRqlF2 zdO3g~c}~?Ju+ngT9A%So1!+i-KB1dO6*yq^V;XZ0OidkdSGFZ>UCSvf^a6b#!u zt^X?LlaZgLfK=)@O|aQ@ILZi&jwQb}D=n<%n7Ht_A1?uby<#UrC&ugh=BHsBE1xaoDky$f$khwT51dl3S5lU8vJ~)7+&Wn&-a<|u0tPL0 zGdglmo((?C-3)LwAgd5MtO4zu9K6H@YOcH9LOt45pzkRw{Su5!thNNae!E>;+GwpJ z&ab@+<}2-SZRixcuU={7y{p{eV>&Z*cdy%^uiCvk^?2CJXonxR)dVF+poB1T5s96l z3S-c54~Mo|>^cq1YiM66sKe&(9CCeMgR^?xaoqAl?e>4_=Vln%MA z@h<2O{RIO%-G}mDQg0Rcrc$rud8d&ZxjsEheb#Fgs;V_ep>!Rmv*I{k^SMJg+~{Lr zW`;c?p?(il99<2im(Yp2QqB#zOD*2K&Y#}Xj2F&|aeCz|+(|&0>j>~O)^8z+C|J7` z-Gv=Nv^2vgzZWG%fPGDb>BF6j(b9k-Jp2?UWT{yVe$I7S%b7UTX(0(&d(%6J0GulT z4-N%!Ug6*nve7Ndk#nim(xw9xG+}&q&@=?FX|Lb+rmBQUHUcBPX3CDS5xw)b@@t{* zP0h7!6w+9&q0eYe5S;m@f!n*(k>lUtcE?_Sm1$mh|6>qHKA+r7-SMHi;OE7h!Xo*X z{|*o&=HIG|>(r4ZgQlP))mqd49T-RfeBROrS!4%9j!nKcuF?w)Oy%JJlAicjxbrt* z_RRmJ{(!;x*MICT5$l)uou9AZEa&Io6n>d=Zg2^}a%w*EVD=r%^hR)tcJmUAD~e8D zI|hdLgB~jMG9aQDv(Eq5v@u-A{o1vyH}n9Z9|Ag*s~`kMrTe;2L59fCPouEyzUcY9 z{2FeqE7Xx*?bT@rPc~~i$mr5RMPmH0A9@32a_I8e5aR$IVw2#KBp9RBWJKBvp~`3a zCHFG*g%S61vpJ{TUf%^!MUVVLuIi#k&S<&WR(&sIYL~Z>GnZ&0o=O0HK@;)r-msL5 z=pAy{ygF6yPIbw0;{kNi$F4%xYQEZ|%t0^hN!sS#DCnYcM}n@scNL-!+veW;3ZMIb zw7P3!!7Oe=Qz1iz2zDmWK0!LE3}%|j7FFQ??biAOZ^yPm`8U2^WtQW7XW6=td=A49 zZzX>hED~;(Kbxy9U9x^A`P>C`dn<`F{(Q{EwKZ-v=E2l9`ciE3*qabVLNFvG&Zr;~ zs4|UCcTnWdnNGIiD2e@Cvh#ed`2hN|ZPjiHf1vsu$N2#WV4p~0_itZ#F3HqpyJcC> z&z5R)e}?wZIwMeUZ%os@`F@oh8GT1;6Iw=s$E)=rJ4#YgbI*>X54dNG-=BcOKhFIj z?DkatR~bY0$QJPMUR2z-6+2AhO2A=C(dG#_cmo>`nmS$Y%OzzA)zI^h6JRyr_!*dB z`pEiD3~NjK4ce*g^389eJ^XE5)bLIFjg&y5em6FqJkwH>gV?HHZ$gRCWBcJLs<(njW;9-;>7 zJ(+&`LL3A-X!=kz5nF=UvOZlX6rZ*dEB;_f1z=ZQC32B{;iW_{X(uEo_H7 z-`v@d3`mCe{PR28^Xq5?n&j4phEkFE2N0PQR-~P+StTd^Y^l82s7?MZz)h6$bpWY% zkQ9-RL?7NJ)&Rn#`V$e8F>m({bFkZLv$Ime_1pNbb(RGf;MYmxt0L=7w_-;DW*KL` zbA=#p^uP3!h~)Z*OZ2na`Frv9sXEuqNWQ+p{t~<+1no=&2NJ9j`ULg3D?^JdLU|?` zmOx>E1t6;tW-4DUq>E11VJa$;d5bdlu>Z#ya{_80DPMkK?1{0ecN^04)KaoqZGVn3 z_^j7>vLH;@pLWMwzdt3n-#_-mXw;(=Kewf1|717M50a_yQ~P^2*^X#e5h09J{Bx5g zOz#~uO{0wFCRKS*%N*YWM2nP>d2*v_`vKolpBlEbS~Daf;sKxUfi4X)J12<1qVCcm zf>3&NP*43hbRZ*vU^JliN6^DJpysR4B=(_Zm#&wgFK+hFk4+bu;<$+q8}+OA(*k8( z=o+P8g*uO$ti_qscft!n;8Yp&3;;A(_b{8hJV#lfJ_4Uo^7J}(*H&kh@+saN-iOyW ztuHODaP#XQ#+MgY=~}ek2|h=lZVGWga$;nl#+*cg_Tr8!vU{uFs@;SuG2~xxHm|!n zKfk=Rva&|YT3lLr<&{^iK#y5Pi0mx*&b0_Yp~Bnu4?GACM3^L0iH2#H+s?_i$_?fm zL`ggqHYR*Z@pxpTD?_ZUpr(L0>Q!V4-^4sE@=qH$v*h7I2Ew0lo$jYW^a-t~BAgm( zrK|@0<0H8w>O(IFW!P3!4djQFuojYnmoy`m%n#q5jNU$b({1tCcsb>!3@wXlO%vy< zu-6CW9PG@UpU=stM$c*i>1mXp(NVmcA)6hnDNP_%v`kJ#HX6SbL7_Zz8Re?fz$j`o zQJ;ik!O?&!E1_k4`VC!#wfeOCg92+K2^47#qxr3=$my7l{lBm`-pHAp8Jr7Y5sL@*z`wa!jBZ#J`Y% z<5Oeso2aayn89c!ZAUaWtLgr9rW^`6IS05r_ZyNw9yArxb^S>*Vzd1|9Z@7sCe8wZ zbkf7Pq?3xQ#5}l@Wjm4#83x=d0Uch={*W4ZQIP>8^XnSmy%9U@NofgJ zbsRiM>45qT%mz2>elKY@4 z-u1eDT+WDYagCraor&_!wzu?k%=AZq>k_>kjM|F6+24LikD;MP)J4Q%au$rI!5GJyRs2w&ea1fCE4fe(SA1?9~# z@EN+13@`&FmuzVU2g|74o&GugOBQ`b#*VknemSWuvh zqegr35~CanrDUy^EaAn-iS3l07)cK1%+aY)GdGwNL^HBGR*y|K8k5JW^m6#bNO`8Z zuSk>^_f==gxX6m8+kV?|@Stn7m*B5Er%8kgLSz-uxH1`kWdX58NHfKHkfn!U5>#jl zm^Y+<;8m!I8M5EKC7qdj_%dqc`HHZgQENPOR?|^KBtK)w!GKwIiJoP zdg~OZKvW+2S6ETnW&h--MJ#kEqB^->7G`0udW3==@pbUnZ}8{`19@> zyPy6l?OlWnjJchazl??>?Hs{qbEbNl>g(bm7dFlCQ-qcStT2QK@eZ11M-`L&m|dTuDy!$f@*+UR^|Wp5HcFw{;Bx${IM zRp;=r5s~MO;z`w7cSLtFBijR^Mk5x%YrRY69 z^05UESp$7!4Cl%yOWa(2j0%AhZYp;X@YlNjwWK?ZFSbf z?K#@w6^PzcH=1G#;ryh021w*fg(-olrfON9Xy*XaqX2vR*iQzrKeL*w`+`6DZgR^c z);_E0_(*-vFUY{4N9AAWU7z@~xV|kMPNm)rwp_o0JTvu1w^fcW5|vsZcJJm*-ux@K z)^0t-ZxqLU=4JIKo)GtW;@uBCQv1cFUT+GwX3va=4FcH^30Y9IXsbk?ZXa4l~aTr$CQMbE?w4nJ#E-JWP zCs-*6mO9NIzF5424R_kl9f+}ge|$%Au^r#Vts{OSt}pT&pn zO?RCer_a5K#Qn#3KE=1b4$~5_gKj@ioDBLB8yVybA@qyQ71A9mdVUUX!UrDTaU1NV z?;468?&4zVED#DigXQ-nS&}{FuQOC>Htf{COw2^(*wS~A&OfYJS|l8GA(1$N_a!@j z`VppTs6(KJ**=K8aSO=Vo*R}-;JvtTa+*&ID^L-t2jp!q)*xvhgr-4MASE=LV>a=! zC4`|=n;k^fS_uY&GP3ThHBpzoxk1@X*-|1(S_R<5q(h50NTD;Z(sR*nzb}UJwcNP@ zy`9XKSHJt(%KEF^ABBRdpkPz(oW~xWf_g_WOWfd%I3F-cVfvcumB2(qB*_tA>RizD zxa5&#TQfPaW^TqjagA~6!bua-0{+fLuNy^*l!KlBL`E+yEX@rMjv5e(d>`G6qFgGcF170kjnn>qj z@piJLUI2sYeBoR$z!DmZR}ho^iKIX1>R@$oR!W|BVrE2>P#yvxW z<4b%^Z)wvUa%Lc+?X|VXj~ugPgF(|FP?5n{#u&#boFvIQ&_tY4Idoy89z?D z;AKFLr8gPC;?*>@kg4rzooL@HJRZHrv)+LA^@M!Pb{+|g1>AkHnYN7Zj$mR!A1Cul zPb)_Nu?#*8)59D$hlAHO5L-b5PKI%=th1YhZS^jc;TcY)-Xz{#BP*y-u6$fo%!D2F zGNVS~P{J6^Aj3n#R8*WBV^}d}Qi-G@3Z6@h(FHt;e)m4>6+HZXR=oRjx>7{BDPZfV zJPV|vF6z;tBApve1z0|%+X37-6v|ITO`p$^T822 zfGJCUe;ih_!{x4dAaek!zcbGyuU;cS+k48q=OIi#>zj z_1ts?XDs&13L7}rPNmxCjF2xF3G)pBabZ5ktK<|3$3^(wJ+MHsh1*Y^x}9lCG#a1@ zPg@7dX*iZ~>h=ZUJ|L`Eb1PlMuK?{O1|{g?WKp6Ugcm}E`wg= zH+mI%Vqtc4cJ}DeStE$@L|3C_VS69(*sY_;=+$S2m@>Qi$R-`B4&i%WWU&spy zg^8O625ndT4`=Zy-{% z$=lt%2f2KQNSY#Z`tA#(rXD7!xWCM{Uwswn`{}MhHn8LRZ(Vq?Mz@9t+mpF3L*|ws zg+S)!=`;nJnhmTsS+)6c{Y;&;*H%~CtxNOt9WgHAJ7l6qJU{OOAW@KYj{~unn(OP$ z<)zDy;=5?)+A5n`c=5#-#RYKVu8BVk96M%y4`2py-CwAXcZ&ZK_u{`KTnnLs%zpG? z@1w10@E=4BKq6p;opfzWaDjHu)f_@!V-_fmsHsiN&|(K|+AL%0_0pXpYN+et4BTU)6m z182o1B_z_7H@Ep^KNr!gc)Dsib~2U-2ZMoNNHL;Q@!@DP7?eU`Bj%;@LF`4h^^&UN zxtHSoz};_TL(z=wsy- zo}4S>4-}Me@gYiinT(#@Kb&^2Ngs!I%eQxfluBBKof&j0<(Qgna~cqDcKM=ZaZxs8Sj7vPJg%%!T_d z%ErQI;|+Nj`VNfm6wle8VSiIyuZv;|t=eYh$-$z2fWcdtK4X-ZD1R zy#SuW1{RY)m0~L9sSibW1=+mYz7TW>7hH3k=T9Z?fROEE zXboUx4esa#n0vDULB0)6bNRhd;krC_=$To>;Di4cGi3K-4y7hRA~7UDzl8ry;ycgV z*Il^I9*@28u{Xj&((Q>NJF(^P8iom9SDLiv(8D8JsK2E=F;PBn>ePYP_50xBEnn#! z6zhezt&Nc@!+~@T^A5T;CK9cft?ok{%~Sa<9(MqVG`M|;C7HXI&`|!REfxzl&};(D zprE#1LQ|VfHo(V|4^L;?03V#p6!`cRGLr}SKyOqzmf`;l_R`D*udU7Sp%mjYvu$+u z-x;5Z9V_uM94;M;O~vor&qtiYH@3{tEcRoH)j0qi>s7LvgBw8Ll(AhyO7#$$fsHyy z{+>$EgR&?!5M=5hK#qGGIN`YU<`nw_uN2E#nLr>TN$k&KB`;@nuCdE_Vh?&{yZ=$E z`&CTb+U6WDc%@0T`Z;&pRg$yG<;jwlt^S*RWjtQ*mG^Z&NzDK!2WSdfvB55avy%gx zyd`;4ICy9A%ywS0Ec9BiQbA+}*aknx5zc8n8J9|&!j6n3OfaEcfyVnSWRS)=dz zq%{0}rHr+USybDN`iF-lV=yx~n5<>IrcXkKZ_HO;Ga48fk&Q&Aku8qjc-M{NOE8D< z6#Y~?2kG?MBd6R3zLu(H;`n;hkJBSfAGsx>Qn1-Q-f&Ni@J;VUvcDkD{)rQEo^?aW zxFEyfpA^zQ!16-R?DznB=W0@A_Z7p4Y*?T&_jl7Eh@JDjB5HPeYMfYN5VhNH1V$gX&eVf9|vk9 zd?KjI*8Qsf6hbPMw*)2seUFVCZ1R`MZJ)k4a?pRDkb8cQ+r1QkelzI7f$2P4Mm!^4 zu!2DYXok2EOmjSwkxU8;s+&v$YRsBpltzZ1@A-w!8cS6Fx{#fESBCxaKi87+tie9D zR9g&w+pRbh!<~A)zgn9tWVCl>Iv0Pdiu5$j3JkF8m92Iv1-mccCQTDec(ZI2Hs(atEn@kop@>ZIl!6UO;$a=fi%Zr2pSCvvZ|l0w zMY%DYgBdsg&N-NIFp~hm2?RljGzH1BWQnq6Nd_%DilaErU?FpCC(hu&Y2qev(!^gw zC7snaO*e-#NWb^)?StRH_C5y$2gyV3 zr9=z|;Mr@hJ+1Ly)lH6b)&~>XYz4M*+7j3*{9Sm>MxLYfKhSdsvC+@NQZc(xHyL%N z75GRHRfk{NYHL*l#jY^i`zqk*@q}Z3wco3xhzys}zwc19n4P^Toe_U?2A3%U18=-g0`24Zj zv;1)bLFFCfIP>%4uYfkQj$qUF$TFlGc)D?d&|?;<&Jtj}8$gZBvM8+$zg;w*kaE;Y2)%T(u$Z+m(=TL0pbG zfRJOislrzG(0AE-J_DEix-;oOwbPQ#p=gTDQM7sN5CSW;Jn4|{UcMqueL~X{y1|Z;LI{uG2P3R+5L7ESz$DicIuB_L z7EF~@YUn4ltfU8L3X}K|FIjhYZJ=IO54dA`tTKw@NydS|!x#8jLDy-W%PsmkrLbes@KlL5s(|)tQ62Wc zDm$gZzTm?4@0KEg5R_jf9(t>$wWHCPrbo2P_*sSV;-+f#CMg$1e&ie=qNEE1x_CkI z!8)M@o+k0;YkvK;+B;;)P-Pk4m-FlJ#qqw7Z-n9svamH$HNTr_|AP&g{2)-=u^O zW1a(dyX?kzFX@X+mFv3Uj!Tn9J2&Z^_SWK(|46ETDF%DHq$}fD=Bfu_i85K@gRzeG zh;LzBe-^Lu*qu*OdSVHia|T6t$lA}94>I{9T9IN{2pSA;F|yQ8!2&`+#CK(#Phv13 z0&ANJOSA^m*d6R(SDm)I3(aqrfngR5h7y_5*lgDCG7K=~P_>X^Y?v1Z#*}E%rpz7% zH@m#30NEv2W5`ABFrzLB0q|La(5c0Ye_FppH{0FBG-`S8y1S z2nU^Vn{AY3&(*lOh_%^b4Zt!1QH5Lt=C1XWb#cK%QvZsY`%2sjNl1uYnb^> z!7U|WoZ@W5i^s9;U^;s+YiKD1gU74Fc$5mdTwafJG9ty5aA3*ra%YGg9~Wz6ixvVq zN!;?gz3^7SyytiOWLN*HSRL)Dl2{*)iN(d&2Gy+D;HaR4S|ZTmzzkaOmerMvUL+@A zTeIVtWCpG>ts1H~GkfFgosGtwZ@9Cet&-G0+ZBTM*C8~tGMXz>JxdR7+(p%skj;ju zs+nKIcbRWDfju~tDf0jXigTa*#bo2W3l+T)Z}p?pFQNEU2$I-#bd+xehiS2K7QX zY8m!=c{?itxCL^}9x2GfWgwf&lz{*+M7Q%#mM?!`+&5dwsP2ra&7QySe51^MnvRIU zx)J<1jMMJ5KQc!kic-hz6E-p-G~mv{65GR@m&KRIXwXlWZYV;GYn=AS^xX^!ykzd& zgY-uYi8s#bVTuM_oV2@la7uu$ih3}lX+b(3R3XKn4^|X57L5W#@k^2dWCfBI*tC`u zMcIq>ycXwY@C}X<~ z+WkIO^`RDlxA2E7Ah|sf>KNcY z;;tK_VaH^q@up_3PDG5A02s;V7aFAl_8}s<1uef-X~@8XuE?+k z(0qJLIq^iY9PEAVqB5^sOjUy43zqBS&XXsd<2Te@@wjXJWF-Ffc!U`%2p2jPnVXAb zSK4hnW+VR=dCOZO&!x)cR40A$Vp{EB+Wx3?A=01I^{?e5=nD0|{%WNazKB=f4OS)u zT}$JjOCDd2#NUyKe4?|m@}kJ1J&n&f@~e|o;jX}^hq4mQy zpf)xU9t)S%LZPtm`!t^YO>PAcx*W_A_-Pjo9y?gz4-61=+m!+~fcT3DiJ$0QTIGAJ zuy_F52XQY0ssLff>0lh{s=vu=4fQYjq zYz4S4MdkFbZqvI-^c|bg1cfjc^AstnF+ep-DZzIU^7JV{K5VB-L98D5qjtD22VoNw zaMFUJrVHyng2{ygpj8IEKYRf2A9E+;i~YJ@250 zgF=ET`7)Fc6}cn@{r;fTlj%vmfZ8o|bt$MQLH$d~!^vR&Z0=Ed&YjJF78heN_LH19 z-F@Al8={bSNrF4ecLIqP(TOM?trs7j9vP;rj7aNtF&5g7y8ORd)Ct=D9K1RXD-*nEFs+thKLXMf>_=tFp~BRdV22R& z1w{^d4cK$BdLdG@=Ew;OwVPw{Tbi!&E?? zcfe!6<72nO;d42F-@G(#OQfgYhD35MXFbQLpQiz%^16{rBX8cPufSuwepd-%Lq>WU z1bH~Pap*(ZK#rQ;0xKGt>4fLzsQ}}S8=6rfF#Bb(0#Ow8P$N-s8LmpA5vu$B_0Vjh z?9$yXyOVEoEHRc4xg?kNqna?o$il1N!n&CgP0?CkGvjpWN-g|#2Et1&h%iF z40-ePm(~Ak&yJ&ehVRDR40^dU<4b!co(fl&jqkbEKC+i@FLxJ#DDb0Aj12%>?Ajk* z>h?i;&bliU6p6}X=#=Z9%MLQQgo~dgtkIU(E zdgHEu90(?FONRV%z#RuyUAoJFD( zME+hnfe%NI%qmAw9R>*>C`SZ2v&v;yG5;u@K;trE8C*p09}Z*{vK*j>EFT(jAZs|F zMT8I1PMS>rbZ=D6QJKFU<#vaE)h9jp;3dd1$h2f<*VW!=E|k{^ph1W(QTE^i^jRUk zAs`yI+jhYFRiGXN+Ed!DA-L>jo^D^K|2?t}kft?Ei{Jskw_Y3% zFa^K|GDTdQ7q8`Ji0kp=5sMPyiKwIcl6XQ9g77Pl9DIGA+&qEWsRWFisZU&oGrv|B zC06O1e4GSLFO4@OJVA7Vrrf$VZ+^|9CNnW{Jr2E-lZ-Ece2kYkkGA0VqNpN7YLK=m zL)YZ_2^8=>w=L~&l75Ne-Ot!k)9G;T9iq|;t8JjF7V9Z-iPFb?uM+X**S|NQp_E*6 z-+SAhK?d#X_uM-pJwNgr_Scy=r}6y{?3F#(gUG`?2HG)l)<2Q-38x95DzQyA$#6^n zoOZ6^u-_>b@Q*(f;>wp5wP;kPO3z)syg_i4#|}bciL(pE?H{UNkuFq>qG~+bU+*Co z?H{>X!^iaBI2~TB_ZorCl`vR?Su~(Xu3U>I_WA6@M3#mH|7WgC*~joT>&yLZ>WX{F z<9n~jgA|^9=u))SF@>1DS%45;k=d8u+v}XVl474d@;|EU<<&^>Jx6-C{Cuka3U&e( z^vk>mk)@N7O@Qy=*fS~UiX1gOg;C~P$lg7ic~3l`%_w)yFHOy_wzA8h4+uUUTh_Ur>ON8(YaL+!)^g`VdMXfQWc9^s)2ahq*8oPYqb+mlY< z|9rr)jvuco|0X^EI(q-QQGyiBWB`^o}h?v;n&iXpxL-WU-RIxGmFZ$1M!j3Qx4?7Mfj{k)Bd>-foP)&dh}?|tV!aMWjHD=LWSDOvVubUp1%(#u+silbrUBl_cqUaof~^f9B!ZiO#@1-ASp2l( z3()xyWzuz;97L&j!6s+Y<8m-()rr9Rq!Z4UA${P^e~W(Prr8@@KoxQZ75kif!65}hj(P2&x+CJ7 z;BRjCI&5B#E5basM1pw|x0c@TvAKQz;6c|XCFWJ6H+p_d#FVd5OFBAyd zrZjDb>Crw7(~pSCZO+RL0-_tC)UfWCFGJDFl{N(YNE?h)6v10aSQ1j>h zpi91!eZBt+$zn94<${4|oYNF+ZCb9nH+otcQ{3yWi&8OZrZ~JT8uXpk}fzy8$do%dVH8X;SsQ~{0nhs_2 z^56!FY#?w9^b7suY;O_c5pX`l`d`jBPVp`s^15qhstTugtiim=YGx*PjQZ#pPtK!z z1=}kNI&&S3YvMb{*sBY0)S@H*ameocsOG40YWt5R$AXg8L$`N4*?$(jyIb}?d(<4A z?RcUH*PPO@{}-?~0vs3H<1~(wY1BypVm>s`re2X-DB}U>hYDOd>>C?$NZybn@w9Q_ z(F+&U_r33ZZ=e_7pzBf?zw~r$=|U!R;WgO2PV-m74=?7af*4!!7?BqQ0cH^ZD?sR_ z_5=+OfT=jJyiE(q=rV#2z(>F{w)CqvbZUo|1RYD6qZclGC;8e_;_az>lKnrF!WW3L z_EI#EV7D}uQUQFMleUyeT*!QkzRlm_+lZ%?zH%W`s;W!(E;$mWUE>+7;}9fes<8sz z@Rcml0SnFmu3iN-Ez~mB**7<^cs86)&xYM&-|#s9?{|LeLm&FkTi?0DnpiAPyCRNd z#+DtC?>|0y_w4NKosVx|GST`Fya-^;8t@1W)~qB?GPTmgqUCu)wA*6Mwh%!{JP~Vk z!|8Axxxe~@*eqt5%@pFI4|=C96NZm8im*uSn)J)yXwjp}VSP`pkQ zz9AKAjeppfy#c*345!oWD;lM9OEu*UE%ad=Km~^m+>Fo07n5vUcgo>2zNmD@7x@KH z%~}R98Vub0hFL@QI_yO$>Y6dfRH# zTjOWp_fgvKr$EaB2Wb}(aM%(lG(9SnDVhzVh<_B)K08;T!-{{56mUphIIQ?*l_q6E zn_top4F;bsal7(;#;;20oiYJO;-Oe1JRm({~P~vPrnN0-3$UGF+Z9Bis z&Z(nXYBpU;mkl}ObRcMV=LOqRGFPdlw6yMZA*q<9jnT2RmWlyz?$@sQN7h}2)_NVh zI00E6k#WOZK#<>_w}J_DZZO%7-qV?Z%?CWAlf*QyO*X115$td%D^nAAtvxT&R&LQT>$paY3K#1g)O)68%{mT765)K#NC;WN;B4UX8{|OltN_-U_8UCUt z{5|WR@Z;mWUpIIRs=;v#{*oa^n-{mZ2<_?ZBkv#iASfwgTP>S8ybbspMU;t1pTMHf zTdM5jWx{CtAmIc8D%mmv4F7eCXCyN%{==NfkINa@PaxLfKM&R$gyprY%)+1zXDNa? zAq#4H<&cd*zhMliwL!Zx9E{oAKs)UJ*FZEH_*>`K;aOsNRByuY1Ok#I14B~=uwTG# zcfdyL5Knf10Qhlwc|$(gE(HSkvJ*G8*_^LVmMV>M$tgm&9A00ofy5lI$Kem0t-AcS z&qj1+0~SCK?m~~vxkRmgGZ2;jfLr%QT|iKfa9wsekzL`!X%qBe(fI)M>5u{*Mj!Zm z5Hrl{=gW->+B+gFmmfKB*4Guk7Ib3>g1Q{|gx(2wdrOijvLQws`*CPUk28XEWT=M%HmE zh|(@1e~8B~*?~-@Li?ZRg9?cs;Jh3iG+#z^POHf^D8z#S1_dElgp3DA z4M!72MjiM3<3mKWm<7NCzFZNpBLJN9KIjRC+@*<93hVQN2PQ5NC-a41(vu-Ew^myA zgcR?xJA{A&fTVhT!Juz0L@XeQ&7w(x41J8^h=BV_8|xrC$0}`(%H~uQFpn*Z=%ny4 z+FL;+&k43--b66j=8pR2i<{YGt$b+IUd&N(^Ur_z0%%QvW_Kv)dC(gQZtSgVjtvI& z`63>dOOthdj)?XGMK-I=h1|=8in#3Mec0BNMHbWuc}sHmCEHpDv1qV)Jkf5eNI60x z($ZGNATu!Je>#53FMuUy(fLe?5XSTO$VX)*?pwHx!~t31L&2L z0ZuaD(DRXEwr$3uE;a(p_RxH9^I!1tEk zp=S2nvC7kpyK_yI69RK0pQa;3#q%SZtA>I0uohtrA}v#t=TlK01?Fc;t)i_5D_OCV zl!TX*(+sQb_zmNitIr`z3b}^}@&^i!w?+j@oh8acyyA;SeTFi`E4nY0NxSx)v#iuC z?YTR50$ryGM`s1%I_+2g%D%=v4lOxH(P}Vy25b|yC7T9S6EI&DJd!l1L1#fS3&(}g zNNs@&9|kT$1wu{7Xdp1wYK`&7H=tB$z?qCVolf15qG~LhCE7F9Gv01zJmJTnN_Cj+X*d5`Xeiyk>;IG&-Jh3VSXp3n|86zMvDPw~T z2DxIoRBl0-Z*iWtra8Z537^0ikg*}}6N8|khkT&q0~jAlIU@8ijxP3bPTWVX`d!kQ zaN&Xkrl#$|pv!3wl+ZzVcb)61f!k(J|+mUk8}2X_>2Ccaz>J$h(_b#bk6Ox zv3OXH3*I!!9{>2kbTQ=*{oa0`^AQs>jdDP%$c`IXsqXYtne0FCYMUPU zyqT8y?Le5KF6`q=OXm+A$`KkOQ-PZZq+y?@w?>KOD$nEoZ20DUYC6&tx|4Y9(%+n4 zGSLvZLW5qAG#Y~Kbi(x1oyBND+LClgq&$@kx;aT;1P(|Z4;#s8K{kUdelCA$1C(s2 z`=VciD_Gv*%4uL*doXW|up0eJG*k82LkQ53d|~N7>Cr?!5gijGZiuIg!({(|Qrw#l zD1|XCAfL-bSu}mSx1KJBq@c?$NnxM#kJ70~G7-L2`gg${zj$hhrymnuJoFTs_z3I4 zj0FQ0kc^OUkRwnIIJl^~8p8dXr2QhYrr<7W7um?MkUUv)+&B^X3DL2Dm3#=-Q}5a$-Bb8KIJL-PlyUN z{jG8W#}uWs3)oHz0+P~Gmnhk}WCagJtQU6c#Q?T*AbUf?5JNY(GWZTh0w**PoRN5X zvv`^iPiMswGe0!nnjhALeFLXC{;mi}T=8taHJ_dz+3zY19UWl8tm?tG0tL!h0eiVz zK?oLPBqWO}Kwe2asOw*HLKb{=x z{{{B{Kj?JkV81v%|1*%k{^@kAx4 zFLdlPG1`7GuSjqwD3u&G)H+x?fcH1KWjr{pu%3rQ~RSW6R;Q**MKOUa01d*qy#?nyf5{VU+kR$Iro=Tz4go&Q0 zdV|DDnn&Ao6JVY-*{?x2@IJ_TMmgG4ixM$`K$W7@RiqYJH2=&#G;i8$7D%Fv*`fe1 zCGr8@XtP~|=%6g{5>b#w@~4RY<9G7m7sBVW*s29|I>M2Js(3ETlPi*Y4v{A75(i0>_5af zb&Gc&5ACKd6!d!7pMWOterWH|MUW@kXXhYX7D^AMBalEAD0XJghVgv_a&)TPnuScL zy*ou`Yinx-IeGvAGQ&KDfsLGC9Nd%hgnSW2EiSU8iBTio{&*7`n`IgVQRb?9XxMsOLB~p4&;U1jA5TOs> zVZVVro)kcmuL4NRkr418Lf7CH$b@saH(-w8r5=Y+8s(rcr~hi z5|xJ7gC5Vj+!@dRro_}tJm!n3+2ViprV%qnb5yir$5}Q)=nbvwHU>K+^WKl2;B{Mk zkC&Z$>y;}%w{NrW^6+!?IVhj^!irALJr1%1QHu*ow6T~H6RakBgk1IXi(s49danmj z=cBGiQ!(1q7Q(M{jXrWmXgVYqe}e`uS*xEyi*6(Rd~HUVMvq zE7ri*urBv$Tenk_X6rgh59GENzoPBmd1Tb}y6^(E9!ovydjE82dO=wXeYg@G?NQt3 z*kUMpzK-T^bVbJ0O7tD0vC3U$+b~jq%M{-2^D2NyE70#f_4|l*8+dSCJ7NN9Mk!JZ@K@HPg z3noOzSZHrwHnT)^wy-qjJa)`Ewp93lHyHGO&=U-LmUstwS3TG16ox_U&!5LRmlee= zhLVwy7srt(U^@^yAhd2;eb@vM%3qv8#D2n?PTzX|)^s`;#Rg|fyw0Cd5-V(^J5xj` z@e<0zWphR*9rU$P=OG;bp3`}T%Nl(A36d>>^L2#nvVW095c3@1dmH^8m(2KMzsGuy zot!%fvtV}^1`jr}k?zIzMRdp1J!qT=q7{L>OdK+B@7c%&NVSQe&)^?N&Dk6GuxXdQ ziPt~;`h*no1K&ry^v8UEUp^9>TW#ov%jLs*V|6Zeghe2cw2mBU0m0mRmsgeC{0)kz zo%gDD11b!VVf{7nY#QbSU1C4JK4r3ssReH($yfVCXIzWQY~i(UeQkjre(t5wYurUe zspz4*i*i0kz(;}vh+(~s@EsUFYDoLUM~-G9U;*+n_6ohs{$SlF?H5je|MY>+9=N!OI8L9_495W%upJ2U2zPi4-NohC`X3%^%PJ09TMvfMPfE37J2+JCH%(ZulJEX`%^oTZP zU6YYMD-;~mEF98sKCYjrPrCiZQ5uEU+-q_O(hax=_p-}BGj;a%*U+Dh7X9vgDFh&9pC^`qG82z?Xib4W(gDc?H9=6Sr96^@ z2$8wMz4fNv$1OIXo3o$+(-eo#V9h zf8VeH$P3orS|YDj_vyj|erYmxVtmr!LqMSD^tq{Nev@Z)BT?)BhaJFR-`?FFnpd3P zSHSN{0+)V4j<=dS0OAmt0CN<@oN)NK>o9(53fOD0z)V3i2tuwnHFa&iztfnSGP0WA ztE-8^%wvY;^8r5&fO1n1=!G2dRd&T3gB$p{_|itfe%)TYl)>2Btrs^ItZ}_QigmEX zR&YjgjUK8y;fIihOr=lgxd4A9sq_mBh7KI{u%2Q276uqd(}d)UvxdK7>tvDabGu}< zO0U@pJyxq)^%lDlRqVG^wV{_BcohkMil1;-#fuIMnph&Ls%jcw&pa9&q2~M{I_;1r z3z$nFiX5q!`Meq)EPH&q^Q!H18e;1tCo6el$(HIj*cyMnLc)94$2#ryTDJ@1D$a&v znA6H1aBDtSw*a+5?NI(Ak{bb8NYTDLzKie86!)bH-GT=co;%X%Ol4E5R;=DzEo!PB zFH=1%hQclB!o2sUmCiwTB$Nz~Ecilgm!>807$ts)JkJd-L6mkR^5z?W) zWCKj%b=~xU@7$QVqt&`&rn}cczrNBt*u5nXxTSlr_r3jG^IqaB+6Ay6+SSQPL~@@z zfp%NEotlRw36#8m6P}qgY;0^m7g|MG%X@b_-Zux7tgQ6cJ%{hC-*@E5ef2vJKYdM) zJJui0B9bepAQ2UJYJxt#?=_(~T1Nm^+%OG?pJAaZP)e0<{!;Ow9uhflvLH2qzp4WL z5GA)cjLdqiMiso;y&lPOcrj#eQLktp$p3N>Ktur>3p7tLyDfbq(;T_^8W68+*gg z2p%AK>vm_MHaS^az}LhX{vGh`k&yuC!x;|TH5?N#7122XRbQCsQOFHm(;7@Gl>5O8 zaNxwFSc5C0^T8t-REe@m3S<%O84qtIl=bz^s1CCd__#^X{rFZ8`ui#lF9QgBDj6F? z$<5AtBeUseA>Ep?VGm>I1~ptFcIFVgi7kdlMmGyO0j-jrt`i<5(r59z+35h_c#T-1 z_MnpE_lZtt`3s%pWdqnm7z?pCZ?U#{Um6EMP8TW!M2nbjl+1$n7afCkMW{a53-;dV z!!?DFr+2j30iR7C(%f6e+#^Q`iWg$s;C>WR2Z;#<=x1D6$9S6XT#;=+eqr^WLD}5P z&5uecMbBx4nGVdDX)W4L)RUQM-{bwiCCfmwRK?y$+aF>`P6Q)4{pOj%M!EpS&3*sP ziHT$`+5enrMM&Vt#dj?^jp#Ea?h>bsjMD`bJ2)3)5IPIexZ?u@i#?jq#F_)YzyOkx zeerSMbS7C(w4)kgOX|*GR~yx9IbBIfA8lUlU&0oSR$Wjoo28PW8)TOi7jSk+^0{wXKtAG zTt&FwRnLsfMvtB`5%8Wz?OR@02oD>VCgY`7(AuVb@Ovm7MQJc4E}xi&N5FWJJVZ9i z*5DEsz;MX{XEdTJkuU+nRz=Q|k6Lg2NibMQDOt(y@X8Tau9PZf-Vlw9rxeZM%21wJ zsE``3bQYIA({S%Z;vUJtrai%h%$Qaf9sBa$uU^`F!^%R?GYy2yc*HG*U6Y>W#l^95 zJ~Bpqfueyu{cD&T1anM4NfX#Pj|lks2$Yz-4wJTXwuLa&B8o*bPzZQw38 zTU3!x$3KjiWq_K>x2h=ym>J+1J5=v!*}v$t9Y5ZWDGI+!cezK0iE*xRbRn zT;+Ci0^z%L3tE{}%Sgz{Lan|)$Ymc~b9xjh<7J*t zH)|KK3V$Cn(FIBIze*+=`t5cO+8{O|@hcdxdDF-#lQT7!%NcOtnk#^7d1uJ$OjjN9 zT}|M6!7p!O^X?9mq2O)k4%8L&6j*SiF%Zsri|k9-FNM}gs7Ww@@Kr}!1@;Rn+KbZ6 z9F~eJrP%En>@jaJ6bacv-t2fJphY8EATs`6YB)XP%Co9lQn>R4#!qh~I-196T04>` zmH@pj=bc5J-4#^n&tEs5jTIAVT~8;9vF!LJR5OafG4eLPI4g#ndRsVN<)t6TvueuR zM<^JFf`6JwE`5=G!f^mRw}y&DC$l#kQxg3lKvVRX1YYVdd_4R2t$Y*jTgdtJ`SM-m z^I9~n+*EFhZYpl(@VRNgw1P7l_O`CyKNQp*snzb16zbe1-c=i_r3W3DJ}EWiG~jHs z_Vx{SnD-S>@U3f(9?ffz&ofB44v5;p=x5;pZy*@OAo(&hND-hlN5&7ieTk$oXLuh# z70r&%vp=alpZgX+z7NY@}RF7g5(wH=ds(#B_Y3-Lhks$-2~-zG;A5@*R11IH4!b670NoJk8==fWc% zn0!M(ma|gGpAH8+vQvqsrB}IipJ&_`_xfDk9=0=Yq$yx9o`9V2pO^e0uh*UP1(cNQ zZl~<@dgg8MNG4&o*@JQSPx0@oDwJ&RPc4eo-sr*uV8szUZPJdMo7T&soQ@m)Ux;c$ z3I;&6o2bq>U*(E{YHxyC?lGczPWV=Sn?nb@47&eazzGPsi}qI+EZiy*c1SiFxKSWq zIITA&h+f2AV8if7E?lZO`c>gRhfLNJJgGNd)&GLI8P2x{`zhqi+6Ezawwr?Zjpk$G z#j~9*8M^8BD*2w;19*>Z`c&?L;(!ORVeNAQI4v=l+!BM39aw-7&WA8WXbRTG-d`2O zeRyrHD_-TB2;W@6er=J_H7UDNds>kASQE<1&hv;MPX%n}3(y1ih=BNj%x)2RJ-Xh{ z!5IjmAv^I)FF@dj1+oUma`?hFsY2(y%*M&PR)Ri2B?)U!K6&YmPOa0$5lWpwY83w* ziyPmNx3)hvKYt_ibNoHLX;@R0%lOkvSHHrZ9Q^&@bEroRnScK2;mL0;AIekITdHVl@D=mC`8%99 zWk}c*QvTX4X z55wE-e{V21aSy|`ezA-qJaK5La}5U>O!Pk*|?TW8!15TJKb@g z;?;725Kx(2N<0`-i}BHKziaxThXT#Wygx8KVfQ*vg386*%xm{1bSlKJ~aJMPV&!%Tew#prK}uYyW4}jcX3V< z498^-!%@Zv!8{5x?f`%Tb_~-Ste44j3O!pzRQgT~j=!2aKXc%ox8J>hgIrY(c8t64 z-idi}$+_^#Ipk5!zM|z&4m94CvT(X$0I%KtU)IMdZ(*DupRP^GN3E}9>O6@@@Zz5^CJtERrno%5 z@2LW?2AnufDzUBtBEZx@c^(SYMZ!dDA+a0gyB7N_w*Pngo6&iHaC#C6jv2e%<-#e{ zKfBrm7CCpIW4iQU9`G9DK0`H@;IaPvyQkl6$iBH~3x8I(+kv=_asjA-5Q5O z`6Z$P-4bJy#RHdG(=fmj{gkA(f*F;C4d^uO=};Kx*9!KAZUy}crU0HSH%I7(uvx(~ z!b1ivhaoShjIVOlc>xwyu4dv}tfvoB(L5#HA8ALvm7Oz;x$L`b@1>N*XSHm=@6+Nl zg(pzcLcbW&9wi^hm32ciw$st@CK2=-hCld6#@y1J;R(vfDMt09xXb2G9G_`t$FrIT zFw6!W1;V#Lwn7(KNlRD^SR@c#5N<*lfRwpK_*h78VZClz?+TLp>AqsT{~hD)#Qs6CvLhu3u82q+m=v01HC4HF6xfD9Bq;Z^2OlKc_ob6Y^&~UI0A=#p`YOUkY;Wut4|IDw+ccw!cDIRr zh$a?1JHi!~rCV=6_+8Rvj)KB3fA=?8LwZmytG8uP` z)X72jDSnasvQ)c4bf4`_qZDlDsdig>GNB}h4F~tlqhz-Uo}5XCQ3o@X9FMyj;EFZA zg1(uTK%L+dQFd*zdaGT~bbW%VV3`V9OkUx%ruYU0+97sj2J^d^4z#{0FopawO@!FO z0M#Nz9iJ9R-T)Q=Tu%}M>=@!}kRXOs`S^-qphi5ZlEK7f0CG=u0?C54;l&p@TTs)U zA2EUv=(#R{QbGLildKW&!kp>h)5Rpm&dEVGYzZiP%{@fPqH$2Nhb|d&SQM=sCafYm zRELy8M!DNAp?F#^+^oBvOPqx*ZcUjjw@G-awP{~yuaK)0>87LxZ-8H{hFUhY6)gQ~ zhhCY8h+%{V)9{FjnzY6FVdzf4u??Avd>X1^-5nJWnvL8p0xW{qy48^K=ZUNR)xDGe zeUTs-slm;}|FM%8h)jb%`!ODI1h%-D$@*ou`=!1WJqq1qBKGLTKHX0~8X3a}{!qPC;ZSeAuI%bQNwwN(g-) zEGC^UqcVy#5eaUN#3&^Wmm1|b&~YHzagl?2*KvTIldsRAV*%O9-AkEPt(CdD-AZ_4 zgJE+JXG0euHQVhVyArdC_eZ2}P>p-EL}?WG2frMWCFr1#xztN9NROtIqrx-WK6QKj z$i@1(pZ;2AaV`_9XO_n^KxN7pnflF({!Ds-^45OxT>Z!*zVuU9GKIFSI=*xl ze(NU}f|<-BKb}PUv=t>O*or6+<|mQH(F7I}yoFS)gyRXA(Ldq|uvo-zPSMJdYVDXW z*n9l@v1;^883M-&cBcp|_gbh{snv)g046?l%&z%YVvm0oM&2_BA&iGb=s_rMOuHX7#61X5{mcNAj$limp?@$ zr`oGM90lw%eA*l9zkA}0`(dAaw*$~wsQykbyuJ)Oa!kLu|E&|BI8iy4SVnvlH%md( zA+gn>gA`a;W%eaxCmom|poJ#MKJ+BE$$=LgLUi#H`_aTM4wLkjfjCVni4bdt>AD74 zJNSc~O0GS@e6bH%!)#jOVl8A=h`29?Vej50<`Ru!3g zcYXaGcKed#uK1jaYtCh(fp$3NU5e9gf4~}UjPueh+Ntr69qa#)R0X0}jI~8C!mmm% znO+K=9~$|WLN~Y0wqFwIR|Zpp)Bh~{4(95QNWV2dNJ4!{e7{J*x28t)l6vtcLsM;_ zUJ&wk7EU=B>Rw2_uyfUe(0W=;a#fMQDK;m1O(jsw(8q?Z0@`CA?c$02G?WXk8VVI= zpcIfwfTx)fdoUEbmQKMO&_ke=IPwx#NV3`!TnD*A8p#udnM?PFq=5;H)QSLh*Bqr_ z414ek4PAKeg-=mev5hqmK1%4D*9o*_=SFX&Qgv|bAFDE!A+J7n?YrRG?^C#?)9Wk% z4akbObe{xqc34f=nCat36?^k<@P}%5GEuznWs1B+xT7!Ny4e-rp_gu-*Hf-|tyF~# z%&$1q7?McfMTgsS@K2!w%lfBK05=t1Mg$B0Wmk;-e>i|f|Ej<~BftUtJ}~rE#H9`W zAWrQvt8Vh>ByL(VO*3IGCsFk!AY}6M3$}5Tt7;sAX|_?(>1d#n6P2@Y6<%$WtTO9a z@sq0?M1?9tU8*~lmmPKG-2an^9Irc4wxC0$+EsV9J@fY&kuN06N{3Ma!<}X?!7*}cknFnG$KE7;546=NAZxD!y*=b)X}343 z)eX4C@l+FL#A|rQS85$kvu5mcdRy7eWcDPPE(Em3H0VMNa!aN|&Vq=_p}eOTa8n`7 z*oZJ|VRu!@IGgxVC+ zK6lUKOBY6Oy3gn7c;HopC%eX0Q9rQUj!u)IqNa8hB|pk^T;HJUus&b5!qnL5~j(`BXmB zNf%PnzqE(2Xqze$_PZ)-8hhCTFiz%X{f&kQzqAMy<_waFh|c0jo946`{An>|504-T zRj!k}v%Zto`#7mRcGB<|7(br96A8+G%#!PH@KUBHZp>Y1UnM|+lSQR6SK>INaR0$XsY`I%qU<`(3p<_1Dqx-GSTC4OYKFJ} zyUAA?$!swC+u2g5(;B}k`Hbycr&cy%8`*TWI!&+sE&0mv)^s(SGD_Lsj@I_3b~Tcjyp>+T{~dB6MfWMyTL9&I)t^x_ww9S%maUhSbBjsk>{?jarh zAraivx#`~5rG0SsJ+xc*-%YyN0IiQdHC=uwuGR12ikUDjqA#Fr^G@te9N%iMAeI6h z*_}J?nloyK+H4w@UN72;iNv~VCy-yeInlxW8{~b=4>vimS(x)`pB6FCP&<^A`#t&a zyp`?w#Ixb^ZLW}8wh&5%&tlc=<(imd``vn|3&S_()Vsv(hsOPS>w!C@V1(VZ9$*cx zw)f1b@53o&uJ?a8*BNXp`%n&!w1n47Z}JtlhcPR}<7U-pHg^wrgC@a>Sa@RZrM6yZ zyv=q@U)zr61hY1%t&dWxI~wFH%TRM&wEGg)i|8IWw`aXryxX;2IP=hewU>9fE>@u+ z`F%J@Sn~sn8$`WQ?$HdY0a9&|IX2?>EizMc*oFLPxm~)7NO=5j-{CGL!xQC1IBc{4RUTg+|^3$GD7O{tykR<2q((zWXXL;fcm>lPi!Oy{R2UN`_N;~MGB`Pk?^_{ z5l4Q5y&y?G>CR1uyf0DKncIaVormr{0>1{lUc);%0OTzv5~-%Zra;(zg8(rv4_GsC zlot+0?p^8#j*Dtoghn;Q0Hb%1ht`?g)=Rj)YZIUUp(Yd_c zWEDknE)QBoG6(q!%t4r_*#~WQ+Z$+;_5jOHsX|;Ga&gm9PL6Lj$DR@XYrm0XzZ6&2 zYYXgv%_AJtVLu}De}w|Z`hP6%w0j2W6y!+Y0zak&p&Bwh-0ED7CmAMb_~GlQFC_c# zt8d+hJPugSZKFW%2D}*AZx1F+D5Pr1sZ-!TVocn4o;RyP2r&PmQ4G@8L3g1oj8w z(clcCR2%_>N~Va0ggDwkN@rn^getQU?-lNvg%LiHEa!qqBfrcx<8hQGE+lL2q6=B5 z+3a|z+)O6GCwwg)zT%BWkyh@ISK@J`oTg(Q#B&=V1X1xdDCm(fd!gBv;Fd#dMN3B2 z7#>D#p@&k;cH64RkFMivyx-Iu=rII5k;_ zBzDmF*f9BmUhE(OZLV0^*Y zi9@=gXXy)@;t3yx9_V5?z#V-1N@=8r{|VE zM5Hp1XNq)5ta`Jb70XBkGhf9lktq_PcIvd|oqtO-A48u$y4@_o0Wa`EJf|n#BYJv{ z1}LlDhwpXwt$rcs`a1g_`yGUd5M5(rJBtiP-+ICQbUO91mdkoo;g!M|KCpYoquSU4LvSuQWrtGCa4mg@B-&wMI1 zcm?D={9!V3Sp?g+@uv@-EiW&Z&-xb@1Lw-iC(CC8iwh5@m)y0Qdnr9XKX^q`3wu1~ zlL&b+@XBJUPQDld|ASV}12fDjg6s;fGCiIRQREu*@HGB^(2<=3NG~c_FO63n9~2kq zGn6Qp%kF3cO5S!GO&G9tNGLowh*I--s1R8s%;pY-Lf%qxjckLx;RdW$*RYW3Lwo~} zZD?6ghQ@gRR`7(L0(*FU4SAER+t)R^-fC2Ey@$)(hw^pq?^9uYTjI%JBTgP2EYe>n zLpVlC+|~I5x|7VQC}n# z_U1kPpRYkQ2EeY8P&EYbD36B{Jj9c%snuz(+X(2Dl-q?mVxxM%aI-U24V4iLV>J=_ zKV5}|o6?DpRt4-FzTMx#)6PB!$R~mo+qVyPL^lywFW-6xRCAvtx@#V*O#}&={kcXb zS_ntF!{IeaM`Q;gT&X`Gi2v-Z0wtO^@Ll%UW3xcOw(ifHX5& z58U&8;r2W1D_9N^-0>VpfHX(A3GfDz2I>>Si9xBnaB1M`W4@@@9W|o5 zv0Qsk(=c2mmocR}yZtMzd!~aLl`#I_dAEAL}Zu1(a3Yq_$9yik-NvF3x5ICDAR|%DL?T`}?DV@GMa(5;lk5?PN3x?#j9o$XWS^`5 zi@(9t4b+of?PbQ?Q@!NySj&J4}x*@J1U4c z-=OaCnDf_x1c@WFEgM0??(O$s$rW&EPLyR7w5cGCy)Hjpy z_yH&iC%`)W_3U;^`y-H%YS51eG-K$v-yR7B4h}0O6&~KG!hXa(rKl^pvm;xaa#jn+ zHMKZt)2`5P{IGj^8D%%T;JHB%sfIF1RQ{ zKl)okblG%aYb4v7;2F)^X{wmAAJ?{L?TaAHTujYt6K0O)G?g1Mo*Nkb2kAOKu8r44QA4q8o^7H1oqPDrl9nWJsAy( z(=9r#*qly&x&?8%g>bq#-^!$jhfcSUCy1(f*5URFj>9g{*i`^#z=`OGNCNaDSQR;& zz?#df1{Ym~!v09&CK;&4fZI%g(a;(w5>Z@(BZK_P2nH@N6^7zcvfbwQ+U$_oqPBt% z{TixUloKn7a?S4oKzMH**OfS6Wbi-=!1I>KPoX3G3d+C*Fr)u1X`g2_K)v5jrvEmz ztj;xi*rG_L-z)zrB5a%F__*?vjpTUh`!Fp~cK4@+9|k114-4-^9dK#)p(aoJWEZZB zPdmJyBf|Dl!@fjB*q&OSnnHmNO5CTIa?pb%5B{&io$N>GQiagf7$j}vLIRZ&Qx5oY ze7czkfHH6rkhDb`V~$hVXhJCAk{QK|BUEz^`K+`gStk)Y7E4rta1Sj2cxdG|pwx`9 z(Uh@hq(+(Joeqd}3FdIv9c&9(j}C^6NZ@~rjK)+oHX7BD5!!Ai)Uel$d8H;$0K)CY zgU;S4IoeI{cpP=oWUm*~-xeSDHro!$m;-+Y{UMVu8501q(}HZ+1ag0d2&eE0)3dJlVade@aIcry3q`J7Ju*LD!T?SHaz3 zTf^-q1<)3ZSvaqWA?7-5&FLn;aMo1HtXH?0E#qV;#zXT5TFfBM2qqaB&Kz***^9cz zLlk*DM3SM>Y2Hd_QXlkq6%}hBsiI!fn5fkxYc3ZT>HZ!FR+mS-7)uhRMoJKsVX1^V zBcT`w6=0VEUC|^0=LYr_-88sF*+_c!=-C@@JbQHZU8>f)r&776rKyzpg&?z#-`@ma z4bJ^fCMW%$^3Ke7Kjoi%z5Th-VsZ32yMJL}0eeRvyn0a`EU4nwbt-uu87~y{5*_x(Oi(#Bb(B`CVTA!{=qZBUdgyL)>qS~~v+yA-y zbK&gC%z=5&_;kss#}-3c{PotyQR~IqQ8)1{$Klm{?izL&zbkgGe|3d^jzk}LQ}@x^ zf9_4sbpQEwHlisLESZbFy~F;wi#VJ4m@XBk$32U-n1b!1W`1lw_5YRqO7blC(3n_n zZr{28T?68q8d=9(S%=@2L89TNi%qQ6bso|s!h%Oo9&D$XlG_TEhJv_wUgn(rydTl; zN+Q8kcB&UXvbhFzrn|C&j}l71$3N>S{kNcaD1Cf&>oR$_`Che&eVW=cFh6x9 z49e^f9ver8WFPU1T&XPphG#+UIT>J+tIBX#>QegD?{KrbzekPMO9is#%#=nJtAo{$q0ky>~rPN49 za$lv;QB9-A{@V6yafR_Z8(MXXj#~b(mME1Hir;I*w7l0Jg4}@Giv)+&8+zH}Z|Fg& zbx*SAu^;IODL9W6k1$^m1OvD?$YuDY!31WD%>BuP)`>vzLIuV*2m^Ui^krZXl?j@l zf=8t9(;+j9NdVoS-ry<7QVfPd<|sOCVMObCgLFp?!%L#J z;LSWtA@_`20&f;k85@WSWtfvGY#s~%P7A#OH^|H^lVsRvq2GiR25z);dIXECEOlVV z?cggiHmyn%h)CthrqChFP4->Qjy^h))NRfYbUWb$CoGmQDdBMmj>z7mlc{#c>jd$? z{8a7W6nHyzP!**p-h@5c2h!@RkJxvB(AIM@d= ztKMi%4Wjsx6>3X`m}Q)90&kEKMu6wfp(Yw?&DNv}i4980P%zyoMu8g(;aNuRJIa!= zHHw8;Uq{|>XMH`S8mi01!Ti^2HDs$HZ3hO{Ud0zdM}sIsQ0WN-vA>2(vIqHyTmjpG z(R8}<*0*uAu;GFLL2L&5*SO*Rap zY8YL^c-9!Y883M!ZtB6j-Gtec3i1*jFJYup80(>tqg2(4O--9BH8=9}m|7C)m*8WV z1B6;r!;~g$aT*lb!bq+}3>;V7AmytKwcNyx8f7ZBkcq{`3I4=xfVx$UPDF9q;z(8L z6`p%|xvH|Cw9y`AlaEiXPaT)7H&g4A$KN?0&;FawcM`{4?^XQyYx6Aor}MFFYd&kr zzJxStVONe|=xF}nSDZYS>(5T9lKAHYp=?#fAVK@HtPi5L3`4S4x63faDDiQ-)=VGc zT5&nr%N^|<>4+NHD9YBxht=r&hj#!_HPCwBt~z*LdJRQt}>1%HzP z)rR86f3s1<6t6zl}AtW*)%&qogBE4$&tvu z87tkf)VR$*Hgj}_jZ_Zw|K5jEQsMAJ;_3bI$Ky|=9Ea{$nsOhmP8~f^DZN&qN>s|P z)3-1e6OW^^saW%Z2CB++fLxdRQ2}EjCLPIB;t&Qyq9x0#JP7rIm8r6avW1}b{0O{+ zSn>8si`ED@6CJ&2Dk3E%}Rd7uu)L5f-#H zvbM${TVv8DlMe&{vV}^MdA+qu)w!lu^+Mt8R=pS3r%wIW6CPkxtX5|ZY+w_wZ5)`X zuCBM&0GDODd7i`?xD9;3^n-f=bp|stq9t>w>r_be%!Xd!| zVQe|xXv`K3L=Ts74~jXSJ^zL~8}(aZNdU+ALk~xlf;VvbQ3xpJJJuTgvffqjrh}7@ zb!z>qJQJ)s-hTIhL3b@Q_B@tisF&5C!*#{k+41Lm&SbVT*2#)$4-sP zVSJ{z@tN159;Rf4!oU3gyuAyYBUgDR+ND=XDwU*?R8@Ml-j8loKc-u));xMM zcsw4DZMALen2Ev09$}0_W{4jUVaO6<2r*8#7Xrzc1W39e5Lnpk=A|_ucY$PqknSaq zOD1eK*-U4X-`!oaH}@v;{=ajoB=yrn?*4Aiw4|0wRp0sQobP? zP$pe@U$N*?>AU`d^<5vGmwgxKb*O&dxa`Ge0Y?_q5h%{sN2iGfUL6?}MPdB5x=mYr zgd#5-+19ovX-n73a2eELIFkB`sv@Bc?+K#1Fy~CyLaz%Cf~q0_h+85+JakEDE~(~% zGJP9i&cZ_kKjz>%L5PO})cVaQrP6k8%hHZXmqjk$@R_O5mS41EnrlO+2K*Yo$`1FB%utiW@>3pR#1V{m z3{eoCb~|KIFzC~#g_d6tpYP02Gse<8KIUEeWl8oJUjI8jCPw`Ji1;zKmGNsw;2gtN zMs+a!>dhJJ`em}g*t?{k&(2q&#aBc?Sy$_fi2H9t^x5M%_K-*TF^||=qf5WZ;cbaZ zPk>e`m(%wi{$%n?enUbYoC#**{i$}&yqNjG+8X&{NuB!^ zTC;w}Qh_M{cmIfq2;7|%m!&E@4>UGShw(-DZU{lb{?cKNv06J0?gr>s!gzzFf{*ct zzYwLRmq>}>FJQ*>Ut4dn7J*g2-;qoTej`x{#&D1-*rD)`CQIHhz-UWK{oE;V1if4SZA`0*G&>KfbGjH^| z#jsa#`3@b)9AvNWe3U(1CcZ&VbTKEnTFn7iNEx1^Sd*~XC4=U*5T|N&9{#F%Y-VVG zSUn238NS`>4c&vndk>7?&BJP&lJ;{2!ZW#mlm7Eap{V;G7 z=DJOLv)I(C8m6Bu`t8x82}gB%j@lASmOw5=;ZT(4EmU#ykhu`OTg&;j z43aX~dK)YkgFQ?*rQ8(0o6jjTxlkQ{DSXgJ?Kgw4AT9aZ6|TU93}L=gHZu3%Yh$f6E=K zAo1XT?tDs~>%`-KojKIKV4S;1w;pODi}ZEz$`5Yv){2NKQY{pq0Rc{$udE9hyL4cZR9!g#X6~?rp8eO#HTM18x#)9eFCnLd`^053wP{MY21IrUvp&i!00o@Xh%Cxyz|s3j%S~6 z9r3#GO{nKS+(Uz4;oAeSu5?>e<|Qtv>yYOvX1_3IvNOEq35QRCi32Jfg$v(o34#MXp=* zJX#;i-kI33GE~Eya&~Is%#~=Ys5y!SL4RXBti(r8H$}ncRwu_2PC?pCdkyj#xpD{> zbCQAcL9G}WHYO|pD7lPsC`#7gDp_H+Q8A+86A47XFT_P+#C5|DS z7%ODhkP6VAlgr0VtwV)OK9TnWcr7!hAvlwq<8sm2{*31sjuVCSZ>6;bJk z9RY*zaXCd-lZz!(5eEO0A>aE1P;gcnX7EPgv4_e^If zoRyipSYxtbEB&K(Y#**OYJ3VcmD=sOZE}l{*Gw0-1-) zX74&D{ACc=7@{1+7@!>gK{yV6&|I$7u5+?4*ie`Q8G}D~!@r$P7q35((R=oyBsq>|JU!s10gBNN#~7LT|DmD4dq!Ofe2&1dgoJ z2MjSS8V63#-QzNIsT6+Al@(#Be(&7jH^k;Akjgb5d&A+md+V4J{PWupWhLL!b%Z*X zp$AbmGF72*17piY#bH+xZEZS~c5HG@p$i|_(Df-?$GR4eoqWg2%0p%tSaGpc~<^X9+LJq+lB- zF#;I6HA6**!_o>Kp475X;UW>hG-i*27i|P|YO|#^+6321v%R&f8j`%JSS;lY1l8cu zrauRfH!=@5MY%$cm?SKbggg@jdm}QX`N?KNQD%=FPc+}&`-CSD-k*tQ(3R)-EWFd> zc!e&Nq$fYswBBo8d5=1wG&6T)$E`2c@Cq#|*_`2Exs){tOM)7+(QMf~V-W2kdtY@z z!?OLd!k1H(q|fj7-RJfDlg+TE{9dO6kh9bNXgVFm`?q=}$$Kk~AhZs_MV~RBE&0Uw zH6@y*(V=r9XJVI2Q^SwB$_BeaN5QNMc<;f3crq9Zswttx&%UOItCBeHl>>MPRDI~` z;RO~w3&FnnDng;lu;mia)$Sj;i1eKiHcXaViIbyI{Qwf!iC8n7#msdK1RrYS%^byQ zl|Z}@n4Uibi-CCwd4iwy2Q)nUXqv^~7P;fekDw$pApA)FM8qc>^0`7h6bS#(X`PN%TU}Zr z@b_0=LF&yiG|Uqt=a4e>`jN*lv&b7y8VrS$xGNs7sEv^h4;%B!lP7+@c>tUyURweI z+=H~jJ9J<_?9_h&2Qha=(b@420*%ftyvZF%A{HKtj7Or96va~vk?~}}Ed<5jYzb8q zZja9&43=i`_e(KZaRx#W3D#cFC5m2AOk)y70>J5Ef4PK^6bS{K2zE%%IfJEYvzg6j zQrT)Xo66*~&1Mxne1(7%g#`6D+0SfAjY>#7FHBAZMOQ@2j_q?jg4H1V4u$<5k3W2f z{qg2~V_7ZY5`zzM+}aS=Q2Lm zL^%^v6x;zc)G1H6e3{&M?{5N5+2`^2WM?3!MWVi#ptwb}+!12FXhh2`B3Toimf2%} zU!y(4ja|Mrf_>f%M-O~H@HyH4Y;-xAtJp>0%AZA1uoamVeuDMuIh))1lk#{!_NB;d zN>f?#8YQr)W}N)NmiwG9oe@?~W=^ErJx#*Z-%Y z91PhOF~lKJtZaviiFRFp^04iPYU5UxKIT=v)Q7!mCm^v7daQ$mmErIFH}6VsJ^Bb- zMo=M)Kh2&iB+N~Gb0VRW(k+mYkAlNK&&2B9Tr*d1h-2{C38*LQ|Lnr9A$~dGv*OK2 z8AaG!8P|5?SreMUVZlZTG*`d58^7|3_hUA@K+?gzSOw2o3!vp(A0c~y(q6d_8x#%p zSYC|FZ|UB=LJ$g3QK`=a`^6433^*Ee@GJHqc;1a=jAu4|V~8Q~=B+~%{s6q=B>v2e z@XsRgsT=0up9_DniH9_`p<RdYJUKJ4wL(PH^S9na__hy)^1AOLd?!iS%a5ZwbZ7e47J{BTY(%`+E)5i zrz+<)EgbV#I}is<*Bnkqv(9FxpwT(|sb-%;s@dmozM6F|mR&|Rh;<&S_U~ZLRN=z{ z_D5eSEs7MEp4G95!I*GB*ATHYGR=*tA5Bsb=Iit({S)AeN2W!sjhJytH2R?#beo_jg z5~Xw@q5~&h(Ncvm%Pu2&OVEgpleb>ikY~RWjZ}1wPERcHo>3U0U7!84Ag`f}HHyOH zNk7VXb^kVsPap8rPo%}0N()nS3lo)O48Zn|IFyt`G8zf;(F!fc9mlql9#dXCGae1r z^hhCHN~H9CV}+Ea1ias5!{wUE9|+%t-N;U}m?XD1-L1nDlho>lW6;m zxW6pT7{-iL?!WKII9ZRrLVQArW{+gEO<63)ab!iBjc50jGO1Liv@aW{bB)XKBbz^R zlx!Q9Y@IUfs|6~HDTDggpq7qc(JS;5rpiIfA|JMigt;@NOd6MO7y(8PTmp;=V-94c zGo>>>+nAqkyaG!HRTN0SAVcSWPP`V)7>;pwmWKMT63X+@G*wBBMxIv^D$npH*+KW^ z+bAD+2_C6qSV3InFH|7hbttM7wTD7Gji#^2$V7sir72^LH>%AHY{vpVWYQpnB$x(* zEE2cF-xE*P6&>YZ4pc*j{El0qlg6DX)$12sE;&3Yt4E5N8CeTzJ|!?aW>gBoWq1}~ z!uNhJ8#|_=YlKTif|DoUm^)!y%*k%Fxo~)45}Ao~ZB#F)z;;*Ocqa6#JYRvMl-O~P zLE~}gMop}29eHB3&hvOpzHJOgEF*SCrX=#3fW3<(r6N-MPyf<#_m_gppZQF;x7hv6 zXKJ;FvXhzKUrl8uZ-P(FG|dhs`}*h%CvNjX2Eh8*+dc{zYNn}>mYP>Ug3^4a zxw4%mhRjpWi}@;YUy)TxFmy#oXx+IxWmHo{^jUem=e$qXp7tv5dHef{ zK}mIYZH2@;B@bh{Zl#v`Gct-X4{-OXGB!*2h!oqbP!1fToD?9!oo0JTy}^V1%#qaA z5(=`9sMZZ48|BpO#jm4;#6)Bh@l6(p2cwWVgs?uj5=mwcT?cygja-SqHMm0y5Wl-# zb$OkfX!Nk#_17q5bBbK?%o^nWVybedbmfH+>>%j&K;|%pPHqP~?8hoTXu)psH)8f6 zWw{`tW3;I0NYp?Cp0>Vcb9cuAUQt6#axTyCUm0Haq5D{UI|J*E9#PKaE-S^kdf^3G zy{GE*R9**L>i;?AQ{qb0MJT-)K=672xe8p(V))_+b+POl*LiFOizv- z>ixgvlrrkgOriZTP+06jeumR}ITf$h6F(PF@2$Aqm3!6XKb-a|dRTR{s{33c;Iqex zHtyk&G?np={_YmQ0BgjG_x9v%7es}{D9BFxGvP0VUl_?FLun2gi;4NfkaqxKjn$6S z6MN_2C_D&U;0<{HaFZ$UjARu6MIeexDgg?@njRG6#mb4mLQ$wy>&5W&q*{oTlhaZr z5MGcnQUHIy(EGX6qoGK;IVOre`H_?8HhloTxoG^Jzs;N&)sn)`tCLgVLcNN{O>(jv zEvOSRzIsRrWCBz8JA&^&BDuVwv9vxFee`yvhEC6w0?;nkYlt8ElwDJe7{Y3 zz{$R&Q)wKi|1kgfE1Y`nhyFI?7@=m>FfT?Btbzti&I1)1D!d20K8m-EA(0D6L%X3T zwuIu^|Zk1*M)=XgzEC4KIRYKHSw;Au}Q-@Rg0&>E_AEr#|eh< zBdVb}ooT24DI86W+?|2Vp z>sqw;(zTGExyQAbEkM3frrCgsov!=eB`FcsSn0ABPV9PpQl zHSz$s4ijaaAqOfm(z3}yZp^M zQ<;!*@+j`vcD~;KajbUl;odiXW%GHL+PG(fQ_~uPd{`HrW^o!~b+RRy-sivwR1mq_>76Gqu9_XeJ)djE)y-8K{*s zuNXa-89n=8A@4y^mAUPy9Kq7uu(D{`G2)o_oi8%byp_zb0!TJFQaA+c^=Kfa#FVh$ zP{Ofb2r!_bpc+zkp?gkI6NzfFn&sMX7B3SCb;jY0>Z6%_gjCZ=J~OIEZJF>ztdSVJ zw?J58OM(_OPmx1}h9&{RLP%ccfdtfiVS6{y;lrm76>9}|Bt|(?-X~Ls9(12R>p^vT zF`>Whd`fs6|CA~oqG;}!9Xqfm}@Vkk8QEF#MuRm3VXQm}bIn89L%ZZ(DM zKCRX1nC-UFZvW@F?>@<|1oP4@a&*QW{H&Ueg)c@z2mSvvO3FX1y zSV(fO%eoh8tr&p8S3@D!cfIx zDXKZT@4;X&kb9Ghoghis7FN2&_h9C7bLk=z5I%z89uUO4zziuE4c;P$q^pb< z9>faKCB$OEcu;cpzWl>3m*a!NQDpz2qFO%VbfJ{!_V=QS_D$|XGY*S0bqpT(3h$3p z{h|!!-0TT()_{1Q>+u0U9!NVp2rvbBdG?*BPdX{8^Dn+B2#!bGp}UDM+=OUDm8OL6 z3kWdYH*ygZ6a$(T=87Vi!>mFTdNsI%F{2Cs<74H=Ea|EN5x_K4bSAXJhcuZ`ccf5t zUlBk_xJF=V$>ZU7#HZ|Ep9_9SIM%Q$L?C?%=bTG#2(mGt^tZb!}GwBB|+%mwN9 z(AIFhhH^*48FGd^Pn|N&pUi~rJMVWMuWO3SQF8dbjx@%YMma&rG33UfAR9MG8!@;FN*hi};5y4}RD-3IVqYwJ`=o zw#dTyFh^mfK!!CDY(iQpA+2KpB7hIy222<%L;^mlz^m|T5rW4pl`1jBUSsKm?wBp! zG<9&*Y+SBL!DLXXtR9@2MfKP0$>?Yp04ol`?G2X?7ia&>ZzNl-)`uPV`EV;~_|LN~ z{Tg#+Q2VDbQmLa1hlf4jcEUeE7CMJGhZxhL#YRbqZJ_)FOsOJEO-bN?X$G=g`AtfY zUQE1cvD<2ym+*@!J%IHjB3JTclO!V75yepw@L2R-$pL`h#P2-r1*!I&4g#Vbhta-? z4hxm~h6stodn=!D26m@J@!fx&@PcA9x$p_f;C~jV0j8G|g4Vn?W=6{ia46U%QNI|J=MuEh$ErNyV>4jXH|BQXjUP zDhSDd#iUlsnA?zp@I~N?ML~B3K1D|BDq(`Omk<=P8mOo?*i;8_Zg;nPD<(C)usW#F zI$smM|Jvgsdl?sls|V|8+qkY#Kb8iKVe7y)Orirr=Huem+H+jwp(O_`1RF%{)^TkR z@vKkCwsBFz4!cnZPe38li1u1PoIB*AKv^wVhqH_IX;hOAm!IQmW$c;4D*U(CFJE3> zMP)yQSQ6jr(yoDqs-2E;IoDzDG3+kDMG#Dco$O*?i}tQjcGs+H5n=&YS9J2J!T}9{ zxQ=_5XO}~S5ZyE(g;B~f0;Obi`I2?PF8sX02%jhPVW-yK;C#H!?wK27qWM8a%y6f3 zmc0Qb5(7dS45edlZgl>sh~vdig%aNYTz@RkL6`5%jmJl^)Qx1=&>fhi)YaQIHEEu> zG5eX@dh1nRv)B2j0xtAArz>_=RxWM6WK6Srov&G3wZ} zO@PDFD%dtcldu~2g4;SeS_4$lN!Q`%A}kmSBg|UbJC1B)b+xy|F12Y`gX?S?lGD=p zmRZ3O6+8{3$JW81C#%SoH&z0stYEBe8`bg^iKR=>-o*sT(74pgm#ypr>)zSv4^~S_ zU~@62EiJUy8mDQ{Wp*4ZzrMxS9mi{@2~?m%u(TdKpR^_71|t8{Bsu=x>OPEUD@J+YHssA6UL}5l{j4-KG9yZ^nJyfW+ZuEk7r@I!D~( z$%2b>cz>vCq!kO*;a%ZH*Ed&JRVtZN683hW9m5&<+`JAQxCve2Ufio{6K&gIia_rr zQv^P7GW5w5ffWM0r_II)tQL-i)Hg*4z6)JJRU8d?Xj-jsw9X^aBm9*v2)fgu0S<}~ zj%sef<@a)gBTeHlQFvd#2W~*JyJ43+%B_&z7kJUQ!)v(1o`A|r$xDjlaogBNX}3G= z@^Bz1kKeQXZGjsh!bPese}^Au5?rI%@lu^C&bgdh6Kp=*LoT)d&zx{>Do@v$Hxc}* z@Cx;RK08#qmi=MD+j6>tTI@}{nEju}k^A};?@mW1KMDnFQ1#CApRVv}%nxqU=P>6W zjjPsgw2uHV(?3>a>ogS5JFKljG3jS$sQ%Gd^T3MD> zK6JIF4L8ug7HniR^=&iGf7 ztuISTc&G=I5pwG*4FW5cBmypOg#Blwa0m~;5E<-&L=OQxZ)*{Rgm2*Z3<^LI=CFtx zfCK|Qos^3{LEZIjpw-B8u$mSf3%e%QLR)~IIW#cnfjou$B3*|{xS39c(lw3ZJgiI* znnyHG8zml-&Du+1pm0U+V$%uJv!VRPe`E1oK%>{5h|Tck*y=00eJs zG3CuCwec%SIVo8?M7W zuT0QR*srmzLW=J^-XP0BGIt#`%5vxkKL2@0kn=cCVU<0v&c+SD$ZL^6<3}Gqy3Vd- zfC=lI5ZXKc$I!igO&(xx$Ht276<|s`^WgUE)Kr%84e@_h3VQz&hu8;Vx{*8>rsD?r z0fX_i$RU0_W?yM9lE9Qd-$axTH1T`10QdOK@ZYsYehB!AkFp{G*sLf6LP9Uk;bAY? z?Z4u_q0C0gZlP)%)Y$D^jf1}PmYPk4?0OS%C@XF(d}~N# zR!qpy-j_%RTNPH&R?H)#p*oQA+Z?@oAU1OZWBv*c5<6T6(OJwJPx;;!vA~v@s?TYw zQ|0gvKph+^u%_Uppj3xCA}~;D_8?NIG5QYG4*rxg;`Y1-p=F_ViKzTNbRSDboMIS> ze1vjhO)~;M=74{bEc@apWyA?g)WTf?uu-Ug6+~f>k=WFWbJcnVs2BKC#S1((@EX0l zoAgA7z^K46c)&|ab7ICPl)}W7)S@0U7E`BU`uY>8MI`DkK9Q>Fu~VrI|KJIFvb9pK zN!BX+l9QqJeU;GUZ&vozLK8Tay~Q4C`zkej%$6}tF|nB}9j2}K zVW1BRBkTCtLcn@waS@$)HeG|8{G}bZWar(H_Cgn9t7!N_pPF2l#Q*N{G7=`1H(%>A zgcm-2ls;Le-H^nSJucc1sY+nmxWwU5EZMR4AU82zVX2|)p|@g=2+OxQBkxlaCpV{05@WrzZ~uB* zXQF@>8quNOO~dSc!#*EQym@?EDZvG~%0Xyaq-DG+jh7K4Ku(3(p?4cybRsZ(ANh!PVzfBRG6|`|?p;=81yu?V zfFB|9+<^)(+J>owh(qL)>c{^oR+td8*CE+*G<%V9xZ^VtjLCs-;hhZsA^h?va)q6a zd50639EfkHBEs!>?ENYHQWVNy-Vvr(BL$L2tG)msj-Zv$s! zCaHG4>k!!#;In^QNaP@}3$_Q?ufXpB<}>F3SM>+0bppJ2JwU@pD8J{QH$Ir8H^4sv z`&@d>0l%^{t3ot`&7pzPfOj0Mxi`bsR+t*dt6L@3F$&cKge-(05PpFx1P5gUh0tJz z@LS*f%(Le4xCwBff2k;)e>6VZ>6DRmZkT`<`g7$1A>1MWg&zUFF3e@9@=9G$QC=%R z>0>56J~P>82oEBRcZgQe2EXGRQWoFBF>UJ@X>b&+HDoVva|8o2Q;*3P?@-3o)t{H2f46+)5&6DzGQ2_<6XfSL!9cbF<%S|}2Vr0q=!Jqrkl9t9 zkBXUth#aL#!5P8wWDlXP0EZ%{j1ux5Ohnr>@2_4Zt)??uyryLX{%AzOD>TW=&gb6l z6yH^x6`r}ZkT~?vRY}DX%VFuD+%c!gNCOddV5-0m-^hi-vnAAunGZ4gz=#ctKI+Kw zgVIN}`!`J8E32dwKPa5Qp7185E9h22v#8}=7iTl zQ44fOro0Ti#}aSrLVG6nFd3lfTw!{)_{rRNQxE+*xHg{uq13aoZ_)s&@4Iv7&|@iB z%2Z~H8&oJXkxqKja>Oq@P|@==SKlQ%-_CF7wc@N2iTVRs z4XC-PP4BJ*JqLunq8tB}t_ z1f_o9`|R%f718ndhG*k)-^G}jdaYZ3uwUAhK>?@D*K4R|8At9Y=B8VtB3RsQI0I>W zLE;Ev|0YXMxuOw(^ye^B_|z2PU1K&3io_FcAE~;+QS@-i)PinynXa zNEUI7;3s&6ua6pztoHS<(*|;l7~cDSX;wJ>5Sp5~J_)_nS zvn9b#R9qu^zW#MB>o7*YPIUIZk3&T{GHRX>{h>D0-54Tt~hccyxOLex8JkCx)acRxD3eCWn6kuc^r+P8HMe0yls5UuT@d%eqS=zV6>>LC-J5F7>wUQI}* zqv(Ej8uA_+JTgYHvBSBBUx;u>Dd_DMau7bIO$h?3pIQtn7TB%;q$lHwR03$HMWNkA z;tYO(a%5^=d1;BkuF;dW+&8+QiZ8FM03sc~!U!^Zq2_JWV30HO+$W<89;t9#orBZ# z)sZf8S{4V@)2j?c&kD>TsS34dRw)UhuiKa$9VLYaySjm^z6^tN?M%D#QSdAF~eicD3Kk>K4v zvMqk9S`Y?=W~YxxW%HZM7g?6}{@33fj3g_tMSOP$H`6Rah7-ld2fTFJTqN8M9l3JD zca}-X=5DlapbQDr0}qdI?aZ1x9&=TQ{=|_yA5TyWxc_>mAuxvb(8UAXEgUKdz9ZAw z!Hhsp*W6A+VY)KuakQQ&Kn&Gx7v573D8_wSUDg%^RvV#6WD~1nnufBbVy*UhwgC}u zJNdM8h_{#(UUcaF?sApyI^?pnX%OKshwP+0OPZG!FHzCKoj5x%)6-pHdkMmAg`3DC z3#QXqBLK+@0l!}z@M`k5eWgSBm0|jpI?Kiy7-i1-U7*aGRzP8&cN^}S`PR*3ZYGrA zsmR;Pr$(*EBYzwtDNlQ%wsy#q}&BX$@<&?ifL2*0^C@I)hcCAobB zO3ay@cw)J>YanWP3{_0rTlm7-#D1ij49^!#7ZOnoT71#H2Z9w>ll)v(RgSB&Ci{Ye zX@vVDh1{59&u<%!+Igpg$*EEn#T1G?p8(14kORUefU>{Kh=vycjprBvF0&MV=*!G3 zVT*B(5$!p-h6glpCK3!sByEQQt!|%U{Sob}Iy{Bc_7Qz!msw`76|T@wrX=;A*i8gm z+usZ%uuf1z2-^}u5p9B`0h78*v&)z(RyHjUUD9;uWVP!kt&3^F=)~cPnQ$~5(!w;g zzBYGraA;kOmi)Q-<>g#qe>$d*CQEU&tjPPrXxl`-?Y`rbD_}KHyVKSO#LgY>0t|#* z2p^W;59KJI>dh9CipJyIFiBI=;td_ZXmR-ZTmGz zd0Q2WLKQ=&mF>Gr2&sZhrmn88Zrf1O$lEPy(8qd9WYG++P0l(7VEhAZc+ka$*|qC- z*Iea#n57o%veiLd!+j;1xMIy+@UT?Njrxw7`%>a|}|6tlkhV-sewkx?>~& z491itbTEONBH(}IF{3bae;lDuVQvnLLDS^RFs>%DuE&RIA^wbyARNV%5>n5HSNe|R zH32!q*8&V^IFLIX_}2`7fD0;gBb(SxC14)mMVZLeXgyr-^tgChnBGe3)yG}rebO%5 zO8nRRh6u?cmg`2EvXeG4rYxawZkS?HtD(!#Mkibx8@p-jxel<)NMG8nMy=Lr7lFCf z7S0;~C)B0FHYKXF++UAGN0C7{i4@a%3~;DX5zwrvX!EWrxKMRno`i6WCQ_ z#9Z%O+P3PUMp|akBa9?#jd7iV_<=fs8&b9*?{TAWpZI*<5xTLp0rxEc!SnZCF_ECXZ|q8NdGAx`(tk!d1EpzlIINhRF6psu3%aGl;B2 zco8w9_Am>(i3E@jiYdca@@g^(gD{%T?rIh;CV+G~I~w;(kOyJR>rJ$h%)%Vp!p}OU z)oupi&NL4*c6W2|TRV|H%;%lW!RZVcY&{2X=x?YVG&f5`u{*DO7}$>%df z7ngCAp0JZlBLj6v?p19GH%yJO>UK*kk2FVr)=j}_s4#~RjY$BHS(eJ`( zZu5|30qy9_*evrbw0ym~x9D3x5|1CA4mBFVz4&*hXa9cB*g`J1z<6iVL+c!+3R~vo z-o|yGuHnhJtH$xY0eQnSaF>-nIUtkKd0n7=?0>U9_FQ`}ee8?ariQNVwDwSLRdAbP zPyR4F36dGyQSBXJ=MAIT>Eznb$2!)Yu%p{S;CmIvbVv0BD zkFd0pTJ_`|C#&*o>VQ{haOAOTwZ+BStzSxgN_wC+9tY?F7!@2XNn%LODrg};q4-1K zQY1@_H1L1?WVL$oXgzf>g1JY5iyrpfD|XF8zEDN`QTktPXc&Qo$e?RYHR?u>51`yp zND|m9!tdm>qbD4`-yuM^sV{}V7|N)bXc%xt1NdlIWmHhQfNWw^a(?aQ1fB)Hla}G^?Xxk zuN$34CJvleS&oQdwfBl9)X)wAy*pP6#{i^8pwb?M(TH%3ig2Zp)qMNl0)f`lL_VeTW;!6_zyG!__z`%-lxO2I}!;U<8dL9IxaP|Re% zY6JDkKx2lE^}O|Oz*%km3P|DpFUoJWTc#llDik4Gs?d4K4UYIc>cvNCa>DY&BdR!P z%3{sHnO9|vmhq}i>8O-QCoC<5^g?DTlnJ5DOgywO=`ak(U z%;n)ZV9)6rq3?!f^d98yz+|kkJ;>gJ%eG7-NLcc%`Kj%m0DuPcgI9E_uZuZ}Wq?5BJaIMiQWcUskJYovUkqbz^Wo zwI(dq{=qsGUt9KhBxqJ6O(csVbDRA}(V_^lN^cg;0U5o{8hI4xw^b7^5xD{?Brchl zNDd+n*br^Jg3OHSqEjh%zW#MXSQIJ`KP(_}ze$KKwtr-I41(=0OcVP)rD%6@V~!1GpyeK%!7g_(@vhRN z?+-q#eMLsj2$DZP&f8MP5JJ;H6#n=;c@9mml;PT$_bJNEb~>Xi?9-~ z0W_dPP-;=X_OCx4a_vH^+cGBhbz!wbrTUtNG(lAbHs(j5#$Y5r#-C)A*ZzksgE`|u zj*(hUEv3q;HZh^`m)D2KVRn9a%Sg`5mM|&k{`{%%Ji3f0CM?zH+G=crMlZv}@ITXsq&~Ur1ErKR2dU zzc&+}5gt#?KQVJYJo;Qp_$gP_xHA;K|3lbSnGVeThbio~u$s#aTmyS2_r-}S*vLXJ zw9O+rKmi!+yA6WrMK{KN)5A`iB~-RsU5vO}YXft%z1V6ku5$hBH%_7eA3wRq$;t|9 zGXlQWN@pTfsiY>}RJqwOZmztM*FMsG~zJ>teM0jZ%P3l(5>kY9fG$IE<156kqcfYHy=WSv#=4+e=`3A{Kjqcf@G{S*F#0 z33+JKLtuYI3$+GBjFDZgiq<(H~L)sIB8gIF_b2N2gG+$>lxKH3hmFh~pAvD4${w!+3$rC!5OtivhVf3ZNZq zOBnDuPh_HD4d{_BUX+NWG%$8S^fDISa;m&xEY?<5)=VrwQ$UV%Brz#Yfu?6s37?q| zrxOvPyhYi3t(QGhqYoc2jIKfOzXruwI4^lTf!Y8f8May^7)U7DxWV-Xnrf4ZT2#dk z{8h9oNNoRHc{U1_IJ$=Fz1sClm#(+5V;PgMg!=mADrP?67a;M5(tz!A(24kY4S}xI zq-Z>ypUG}v6v;*m^4AX#xsj!#;zRgXte%}?r$R;QDqn-J>T|UFnfpfX}paLvdW06)I?p8;g3bFkKo`i+Xm5g8q^Kj zPrL=k@e#5~u&R-80(nBeEYq6ON7=n>(4>O}hjot49m2mJ9fEX)LkAGE4S+A&ngDgN zZ$%Qt*@W`;WqLvSh0|lBnp@71y9n?4Qy+cNl=J z>l!D|<>sa3$HWOa#rb4oV zFk#=tdBB8_L*!T_=e#(#t73_HwWn(T463NtVEYGrmnF>txihzQL z(o0ib@4v8R2YP)6VzIzkGur#FbiXlEfMkYOaK0$aCZ8?hE%M!TqOvJRO<*tE5WkiFGy!D>-EUJ%_GyeO6FXB+vOKeD09ch@nLdw`_(aa zTyGhAk`pPz0FeckervPwFV z+19xw8zYGHf07trXK@&#fNDO-zeHER#=dQSkzasRfZF=HNdlt#B=7AADD6#f&T>@^*?<)_@QV;f4R$yFa~L zKq~84ao(K^xb_t;ygK3@RnTrxS5o4U$Boyu9Ao=~QU;|EGLU^G;Zx})B+O10qHRGmKxF6WhilI@8C&IC<&8~f^Q~6zc z?0jDNAsN!NGRdcmX{y(oI^6Vdc0yI;B4KtOJ;3S%GpN;BjKA>7mhrZ8D#?DokT5EjI;oJ+CK||2B|V$4C`+)m z_mf5oICx$nPa+7e490w_joY+n2ND$Wlol9#`?fGOiSIBR5ro4tE#@}fISl~|1z*Na zkaUn?z%3d8WijY8qeXpf2_-ec@I$(hgk>15l^RuSFBkjf0i*j*h5w2*sbHTfqL6|# zagd1MA<;rn#0I9gt=>S~HuBZ{tcHfy!-J!wU2G2w1pTl87>RZ`AzW}tAkR^7Bu{Y) z?=pISZwL!0YP~rv$!uhfzeyAGD0*DtQ$c6TtEP@KN^Au5!bFG|Uv`}eY{Yc%!x z)ZzF-G5#I5&mRi>Os)1VmmZ2F0`EguC?(uJFS@lP1YBx9^3xGdaI8UznD(3_d`&y^ z3k}tQ&d-)I#1PH9Dr8jECPZh1>Ex!ust~ab{<;`H;ZzdQ;N7>aFTURII$_cBMn>D= z$NZXnPz7KBBIY>&>Ezim@q&gqLBnB2=t- zrtrkzTEiNkznJz+CN)Q&p*e@D!MsDy!shQWAA?+I6PQVw9*L0OFEB4Lk=7?jjo zV>_HNC+UkFK-49E3%>e}l*?U`XkmoXURO|exuONX%NNEZlmW}f7D;U}8D0dE?VB+f z*A@Xmz-1`EzcOp$j3NY*BM^%uOAwtZSLT}ZCpg=rzdh3e7KvJpZ&A9uupr825?xb8!Kp8#YW@Ug932@xmkKrjrs_BeoJuXMZJix;h} zgzYCi;Olle);M__gJN173y9a{^0N&pF5o^QKN~TBWN_+(um`8k-%k&-7cb&Eo31hh zFTYBOmu!u);*Jik2F)c;`3MX#giOMr{ix0xTMCjSECVPcl&_?c_TD3eSzCi-N^}7j zBo>pdl4PXKMztCm*gPWrOvI=P*a6YLq>9DoRO)>sPL%q?T?19zNsB-iT%J@c1gMYP zvct>HNh)YGo@AG6#cM$oxE{htZ#@nrl|D;Eu6iBIy=8t`t~;#@AH=MeA^UMvWFR%s z?FJRPI;>vours1wft#R-*>gmIKmd$dMJb%fD4$ieyU{1cn{gd%g@xDm+HcEbV$EjE z9PRZWJP)=4qcNXycSw}pOUK9V0+8iBxmYk5Gc&z^q;-a?4PP4`gd4c}QR=1PiQNDZ zldp@_qmkDp<&pa%ktixrZ27t@JcSu66J{P2pQA1dmL^FfBULw2mqUCrY!DD4D%iypVK;EEK57*r1(P zNJJy591O~8B)YK?q)yU76#1b9P&M0d+aaV6Y%G(;?O^*qtxsz4JOW^Il5y~+K)@Y! zI_>RK_=V09zxM^DFr&QS6{JoUPVh)vQG_edG4TFO0q^^ccIJEkH@%%u=-jmSzlZxw zZ91dar74=hPYvTYD^RqQGJK9YLW1#;_tClE_wiKkiJu+!g#wW`W&NG`Y)AMJe1fy% z6XAnj8<){YCzW+{vhyA0QwGHw!ZFJOOHq(Bcn3xw7)u_ZMxH$~cG~I+v?pj)SXb~Y zvlvLBg87a^2p%5sBlcx{497IFJ(3{!eMsLTUi7}{3j*0a=qMgN`sf?o{%8H~q(7s< zhTmQAy5yfdKOG823U2=ip+l{vnHocaz6<4&9~noAjO_KM(dQT6gcCIc&~ovXv4}h^ zd(j6PKLUH$Kw)?YJcGAscqD-MvB`n2iO$V768Z}6CF2KuPv8DcnC4_>)rZcoT!UkA z_%z*1mzFl22NHWL1vt;9bHFAaBBZ-yt#9(xQKm3uQBgJ~ZH2U!Xr|Nerct3S4L&A; zZT|`ztLlons%eAqYQ2uCF1OoD?Y5~x=K-WT{&$!)OiaKo(J}Eun}y9olkGWFMvKXo zXOBdFz9DSTiC<;+6*jbBZrHqpPnrQCF<#R5^?qI-jik?L9h#4ji<@{kJffjnLA2R1k~Vf1;v7NS9LE_T*Wf1yrWjkMq@!$}3-fUD zTNSb_YFV6bP(uNPQSCJ>Y{1E3u0hrr%EDkc3yNhgTs=AdmJ1(OI-k4!$KpI3u?oNb zBJj7dC6L^3zUqIqSKSa}s$JJwamCg(gPrQY2@$`?KcH2Fra#)VW1NkuK% zCLpJBex&aKePP5f(r;uFPNh69#dzZzv3gat4J)kA!$t6ruD3G}w~T5#|Aq#aDHJk; zJ!g1;T;JOOu#PRyunv)txP^yR2uI3apl90shHeu81KN}gfmRpDtQssLRa?1K(};(* zETa7fvS_QMC^H#Eiyn?M0LW1Ue^`1TK;OYUhn_=^L$YKEdw^olC%7uE!)*$R!BK_) z!Xst)NQx8~hCX3g|GE|e3^0-!sEe@%M>0a77%bKSg746;gjL!1VC#;3GXdXOuN-js zruCaAr~QF))h93Ba#_8S>uBuh>{(x6X5SsH2Px^XT9&=jQ#b3=K372ggUi~LvmG#(_50OtlC zC|p#*Wf1PKQq}_Y3``z`{?Mdo=-T95ir~U8{@sEeYi)D^cYr>{abN2GE!P%m3-`IC zXIf^f)hW-HGxL!^(6Gp$d4*MIk3EOi{qVsKV|UUl+HK70$`0dw72Ykngl&7-8*bdwrg2O$`E~O-50v1hiPIA zC<#We*vH2l+@}Ux+-I`#hYr>b{SM_&+m`s}y~gVEdKqW_pio=k#4tNtgBr=kMPN*? zF)lVNpz>gR7O?!*alMBPEBmj9%5b)h?j{=C_=B5jZ*1Aw#$Es!H{dFKlMrCwD?A-K zO*z%^GVLS`T$y@#0GP!Gmn3Bcz}NZ=`hLh5&)|Htj(0jA!*S-IN5iL#>b;eh9a~Ns zaJ_jASsvK5_}nGCHiUaINqNQgDj}IpK{jU*ZgPr%G$CsTdZ#6sNxxTtAxo^c?)g9F zBnhZb?D2CsEvM!Zkp>*8CN@w@YVadbX-g9VzZcJ*y@+TvLe`y&?B4yA@LkMn1$qI+ z0BI);v2Z0Eb?|5se(D^(LUcxpHIe3~;qxl$8;TmjH~b)UhkaeRIyN4BS`CG2^G)?B zcr0cY&OH!&(yQxnf3zhEVIguTw-9TlyrN1+sKKBr374rJ;O%O&S;L=C#hUYPSUCGY zKKzvE_j%R1csT8e)|G|aAytz-IJf#F4pm)D?sXw=z6|?VgZ~=X@6mY#XsGf6jmJdx zS})=yOEDqg%k#H)dahP53LQaN46ZWm!m&6Lz&!Z#5!l;TJ&JfFdSasD8TYy( zGaf{~Yk+I2s(kbytoc6hlQDu-3`-O0kKZo~`hr6;$nW2buYY$+?YY12@Vh*OQ`z z4w|dPn74B#afV%_cQ&-FFCX-_j055&#;uT;&WznC$CfTTJV|K`5p#9uH>*7!h`k30 zPL+no_Za6~Nc5ghjeP7j*qwx1;wD$UiMi$su60ewyjAPYF(^_nar-Orr(- zxtz=A{qJby_Vdo*@zvFFw-#KbqgELPGAE1NKykutLLmh-AY^D_GQ*1s7tAPbBuYe- zJ{{PLm}+Y2h^w){6(kpiJd$e$i3^E4y#Wp&j7&MCXW&Yb9QD3V@szrLN%CKGOgj~? zaGYJ=K%*NL%V(ks+uwhJ=;AN|Y}*BQEHr2vmmKdB=A zhaYAp}RZZ6-*gMzYVQ{eoTR)e$Ian@=p%@Fwek ziSy8yoJ?KlyPznUgc~6=!p)--;Sy3X>&VSO$6=EyE+M&MWQQTHpwO0v#go-GjSlHS zz4f&<`nW^79>3?LQj!al=3NOZU`TUikPlF$XD&urj`4wL$GWkoti5?G9eg-n1UOE=T{joPnC8X~#X`h>jwgpy6qDsBNxnwDk+gxC85 zfU50)%>b)2ki|;(opNq~=**s=jiA85;2Ev#Gd$2|1|_<}IoYY<2T}@6Az^g<|1}1s z$yV_VjsYIP8yrKg%LjoBKFCs%=@g19vl6*`M}B1FIq;q?5SjP!%2i!mNb`3=3bUZx)8%qA$ZDB@4tpA6_!0hn47=xCboP z*cVU(ewB6y)sH3u$VH~t-GI-aXTulRpf?xkVU>MCLM?`VKC%Z{B2-$nghaat{B5Eb zr~HLJ`25JQ#_JA#k@Dd8fd(0`x%tLnZc-FZ%3Ed^R6;gidHxu75aes{z!l$H zDfmGfyuHaoNf$=Q-x=wPw%+>ETF8Gt5*N@krtCvEm@874tK2kk6OoSMoVXs6!t>PX zfcK4=5BtzEChX_#7@Mvvl}c{iL0D)U?nR23!V8*@^0@)squ)EGAM2V$UFu+4K|6Y+ zvyqfXflOGs;tz#=pfC+e@Aro=XBl;5N*HUpD8p~ms`WnN4=d7HUwEF-g(Zndo)1gR z9sTNcw6LT(TZBXRC~Ks_lJSrN#S`%mHAj^s!pS`Ah~r8kNJWM8l+1CRz+4Db{OHAE{P_v#Z9DML?4<4k(*U@N3JF8`)=#O3CbL0D< zUs%dzM&pSjI!Wg)X2w}~dTPSR%pc_$SjUbQ4;GVCkxU|yiA*K&hry>%tKETI8MxUg z4N}2coZ=x0B=L~owz7A~dE&NOW>bbR9~m-U>|sFS@nM)?rhqI$Wn;B>eGX8W_1f4; zpe8JVos-$@9}&secp=rWo6|;T&fN=~%sJD-el$%wY%*(6XVXV%u}(N1ijdR|mY#;| z!PNy9omdcy40ecV@dSQ?+D|+o21@Ii)_%Xhr;2n_%2pV`_M5piQ?8uJXg_`a{LBGV z!A@kglK&}RSwpc$?|VFR=YR00-w!LgX?l22rP;2QC>>D=aoEn{zkAB)L!51C5Puhc zOmfHMCw~3;b91f7oNz}5y{G)8iL(E)Cy&a2a_HKHD%8W3=Nh1~$(N|z#^I0v zO~JiQdXUM0s5sphFcFYp$*|qp0e`FtZyJb@5SpkYqYDnl+NDcp0{V+2bjV3481423 z41N5!qzGD2<>XKZTxU8{Uw;#=5aNiflkt6Rcl~XH=H*IXvT>ghiI29iy10Sax|6y# zd`+$gSK!*%CdX_|xUVZB#jM^i9BQ5LDF#w&a8U#!4XDU)SPaW5OR{oRafJ?{Bdq)K zx6oUYECg{FdnrN;%wtRg9t@fUssM$RL=P~!L~i9#vv%9ctCL057LbBAkn)JrLOHFq z|HkbYOF3QcNteUxOy^MNhs6Bn(It9hvh+zT{yvX@&d*9e%E81_4DzN~rRFN!H@JC|$~Qo~@D0KS^}ABeKb&~iL=Pn< zLwgeXWz>EVJ+qz8l*{+j#Jg+HVkE>@PTl{a?Og!dxXv?CoEcySgZFDNcn}~+yvP|o z#0LmTq$o(1EX%Sj$1*58b{yIDBNdYPCXF38%>#Cl=DA7ZmWtCPy>7O38Zzl_+fABo z-G<4{-rOX;yEpEV?KWxa+s$^nVjs7?x7*t`h4=fgO3ZXf@lHvemVL?8520D8;#}w5?zNmoG4+vH}not!QK$&c^aEs>(DUv zp0#k@KzUZdKgk$JJy^)|ffUp}3T;^ERHE}(kX>2y%-vZ-fp+K;OdzE z)?qw0amtY61r-rtN8=Kg@CzjCeNArrSDo^WNWo&wL|`l!91A3}2_qaf64{oc#n~vK zweM^_pG-wcfj}ve8Xt`?9Oy+&jCvF>88;azhPuPh5ThVisk2~%kLYoAGVDo(=>l9plB!> zO=i-(K6UtHz9A~uY+8}bT0aMgr-+sA zpjDsEInY!a+mqI-A8SH#+p&w8-h>yYwP}wI=6tvH9-CYd=-a?S!eFtW18%j^w|!}U zkn#Z6FUf{<+K*CmZB&TSp*isfP`))OFSutBp zo;znY&gr2TC8~9x=fnM-W~TrZo;Rh*#`v;HAW=$8(Rxbvm*PD4jgm3D{|P(}Bl{9$ zEiVter4$trLlK9#M{3?QjQZqAC95xl#wz(jV8NhFV4k!uvN}l}5(ep~woS2hi{&*%)&`wEV$b8-wir=F(6yPqVJHPS)2qR(B ztp#uNXS_?z%tP7eqfbrZJKmuBE9WwP9+InukDogyXznWPO%%$V$KIqA2E9&wFnC>G zxEx5$5pLfoDZrI(+90sa`(=<nep|DlY%U$!#4KVzkES^spSMKg%*UgH z4aNhgCh7Sltpxyf(z|>!6S1hnM)8L zct9YZ;6+H%2Z579w+L38F|aH2!}>}f1z{f?%758z9}q;q$Ndz%tWFpXP}gp2C9Ji( zY?am8LTjg;a=TC<%Ov%@a_pW6H;uzq#Tz(yH8d=0na% zs+cof196SUqrvb*X#>4^TBHeWQu5>$N{?8SR^I!NE~IofQA*&uu*p*bs0Ds@%yk~^ zMky!*?h^7;I91drN|-ZdQbCAE7w3qdRV&9KUJ?I3h753gL|TB37CWfIhEI9M4I*K9 zI*0N4P)bgE6W&Bk*AFLjWbGY}E4nx7CY@Q1CPOUPF@tVBsA_V{j2PidW~&6kWyfrPhN5cPe8Mj~<-ZoQq*Udr?9bqrpa7~xSi;V_?s36Q73U%9SYs8&46LmR z_c$i1&ZuJfsFWTYWLS5I4hn@Z9Y@f#IC~NBN>bJW#YgCKReX!ozhjh{%?SokA$52Z zNIkqGfS4sDxFluCpZ0rYV?4-0K95H(`ZP`gx?TfGc$4z&W>VJFpzeOa41|ryoo2)r zib!uo--d|acMmLn$xJpugadUlDanc_>t=7&Kn9_RM^=2H``9DCup}4xQUgb7X7s*L z`ZXcHFa5|R*&mDg9|TFngCBqrx+>o@mX2N436&w;N=l5mhh(3i368HPZEW3&?u_^D zqKO)GKbomMTcbY1%O_DSnxu*(Td`9-`|l74fN-n{{TW)7*ciH!jkKkwJh_;jgT_?T zy}lBvlK8MK#tqpE>`5~1mCo-E+$0kdPg$DmgBtDDC;0X#W)u+2m@EiJ^Au$Xn}gjK z@}b&E1bB;Cdv{9bAAp|QWEt3yb-^QmkMDwhve+eL(Ia0IVZ@;yay#=4-=i4#>_JVl z3rO(XmvX@W9kr&4stc6D)FYuT)p&I!Zu5#Rp|A%z5hbIY|2B|h3Zwuv8nD>wQJ6Ud z*$7erO6=Vu$m4NNG5%dg?%tVIiozK2OYg= z6CDI4P@|k-W>4&q_{U4T*8imI z&V6QWbBu#b7TVAcdEGy_2KUbY#F9U2ntRt<%)PhX=k}TTD_Cz*pnnMKTv&TWON`k% z>-+!hTHhbW{_gLQ{zG2p2m1P|qtMU)#ESoDU+49O9b296b%B@O+W#>3dS77S4}k`r z#6)Hy{*AnO~9Lz;9Djkdyzbco{KybpwB<}6V zoL!0QmC2je0^^!;B^wCjP9ZXY20IWUL$OG})(I)bLx%qiy8j&NQB}AvivpMsvT3b# zwsDYAEguxD1-UALv?{n@IgRO4&-9`{EOSOP@-ZI+xdWNn1&fXh$wlXZUW#1g@ z^U@3KuV7aTao$~Ty-$-j`R}nU5cOKkqqS^=rP0;wH)2{GBFJmIK9(pbEaiL!r z!bSw`gbU{!>!`bH`wl%bh+(060C3+zn3!Ma`aJ*7q4dWKS0O)|?uh@YKjNN1iljGq zZ}_Ch3u2=Wj2(tq?_?@VWjo-oLV@eojU0(zx-Fo;ja>h29uSz!6_L}8h<9qrS%Bd$GlwKw35BxqsoK109#<=SiIX674v+eT- zw6|pf#~Z-`!N{gumCz-O+-~2l)0mL zOg}w0{x1I^P!wC$W|wDu+ioSW?VC9^tDXO3{$HHS!?h-8b(?(-w)1iLAYn5o*0||W z)MKC}wDWCABA^-t7^#M3g#O8gvnG#?<`EUB1#`t?X}F4zQPI|o@8>^E1Ap#Ef7E+s zy0{p9o>uAk=wfmD%tr7lUkUohj6&+R+foH%?8FXOzJ@(W#V=N{gBMVEfJ^HXO3R46 zAMm*Tz1J zxs`BB3Brii&D>Rw+pDD{cf!F{f_YMiIrUWWq1T({mlCQ_`nyEodOK+(R&z5$Lo51tx-W@bQH&W#-kxuL+djIcmt1*9g zsGOe0LepF}K}KH%m(78%&tV4;^9FTOtqVBL5RW)lgTZE*s{QMUvIvpEjF6L{il8i1 z;Q86ph54B%nj0b1Ey+VhUUJ!R0@xOUrt0PL-&W5r<8y~1vc;|`ndL7lo z?Ti1AuB(^qbCv9v1r8J9K`bg7fs++hOfsY^Hz zh7U!CdzTyRVb$Ur%fDvXQ zh(`Rl=4KP_tnQU(BlB@z9jGQ`pJ=jy9A0?0%n0fq05p0NmgzJcw-xg=Rhk=rO9n-# z(ou>rp$I054W|$Yl9P@L*Uq;KjSbV}CFdNOf@GR1EEvbIk6m%@vZQhV+-aktc0b~s zA(q!GRcP+Kz6M{hhB0u5aSB>bE7((O^(zewPYp{$rN;5N3hi@ z-Y1{d=AlCy8>^>#>FFeMlFglL#f}%c3fcsxp%irbrL=SkEOuV^Kcnvim0Y64G`7-c ztdMyaA)XC(rP1hIrY&lJ=z@sWXf$-#vC*jlbOMJmux0B6ZP-c?kyr*Df(2F~`-1<^ z09gwlLK3|I0vBv}-XrW|Xe3fatQ1*_aj@fStbVqH3jNk_DHhHLBB_`WO=X^YA{Xm> z+aGn_-n5Lwn2}5daIG)o4J0F3)K2Za>G#`j+P;Eq*6doc;r+x>u2$hf#EA|@Le1{L zMBqDw6eHxWZrEAZ^@$2PqE-ZZRv-19K4c6XNm$1b3#oWim3x2Li3WJdMCVJ$)2@}v zECLdR2eR{f8kfh`_1NKP$vc%UB|To%@H6gP_NLJD1YdiczEZ0QSIku{6N_cyT=pVf zu@27wVq3a>Cy39Hi>N=kg7WT0iC51h^aOiWlo9>t*J+#B{^Dp@b$7NlTA~tZOGJ0j zj!(zF?rvXotH|vaa#?Ro2x`4iDMV~UpCz_3D8i;9gT|bZe|m$NxMQ?CIt_yNI$QSm z=T!G*%MJJh1muK>vQs9P^Mwu*ga4l5dP*R&DG9B!B_xPM$)#ja&1%q$_(f?ucvxA( zT|HLJ&R2A%gR@R7IC3e z9*cGljR*2X<^>reyT39Y$^#JSJgiDCcXg?hosS#Z6W9&-S^YjAurt%T+2hN1 zra%CIdG-4(8;<2K2HLEX7u)d@|0lo`0q&^~ z5vY#oc?p|D)t9B}l!Q%-T@B12xl=_&pbBIb1W{C~OrJVi^my-BJbKTR;+em2Ph&P; zp8lzE#Z-rGRmSrouMb@YO2sW-gt=*%7S9%a0cQ$}Y_Oqbt@FIA>~ zEH|#23M%{(em0?Vw(!aa9v$Nxj&?$?Kp@5J+Y1Cf4ZPKh=msF7eTt~n(^nO+^QRP^ zxbFV!M?d;(0XZm$x5@h@yBOg2Eocx0f5Y0OI*=WWKL=B7VArWjg>gh6l}f0$bTA=93g=MY zIAvZYgU(99i@G|6=#p)~jfTOoBs6W3h57-IA*!u%I|DK)`04mr6h53B4<}Mt94ncP zZMMk)5)Z4aB>-!S!*LzZg=bLwT!ZMo3(U?Y9K3>`_Ti*F5G#YA+JTuK5Rbqdat-8& zQEi`GcU*xZijUljCB81;4p2?`>4UW@4}b?j6}vDM!;)=pWRgHsDiN-*NEcC0;|zw7 zu@hG-w33#pD;g@rPe-@bz*(BZ1 z%5$#7MP@w0OF{%Lsqr4qpKngYlUvTD; zN*r&J2s(!*yBt7dm)D9EbBSj}$&w&TpmXL%vCkMhn77alM9HhDEa8i)1h+5pvm9{p zQqvCr+~jkB-`aw;>l|zUo+L4;W9saL`I}KI^6zj7T4=b;_3lJ~ckr%lHW=@aMPCu# zJ;3?P;TvHXFOO`CzAWjHP>%WR45ruw;#3ij zr;Qwz+0#@x7)T=n@!*=RI_ub|L2o-)zQ$50`TpSdWtOyvb)$nJBo5ioQk4im~k zJ-#r-{A>gsboRy0l|a;^e9;q*ggswWJkhWI0=u#E*MIN^){Ypy&eyz}=4H!1BhvXI zD-<@kEG{vzNnF}ej^d_Yo$MteO|w|$|f(MzVA2JQKv=os2(@+=Dophxc#b6h}0r2Gk`+? zY7*7v1gFOcup5?ZhN9N1;;7;!Vesw@+0US*P$&Vi`T^f4OrWp}@RGlGnkw2?7~k*w zUO5?_FIMvg8X{t-;8-@OD4IW#m<)R32&#~Rk)e=Q&_Y=%*myy9#*Y%R- zd(&vp>j}Voru+Tt8m8r*bStvzIpVL4_#&gWShkv)kXaHG-f9vjL3!` zTLkNQ1hQN&w1!Tr0o4X2gLWgGE3^H@W)3(b1(_3u-3?fYMRqnEY^l!tKLtm?Fgn=D%v03?RH> zD9j%DVDx*@U_r;p*?A*tSFAs>*fg2fsLx6(n$8D9;P za%K5@FQU#FidlF!)~Mjw+A8_aR@sx}Is6iOD{IPCdjIMsy^q2%Fc#yzpm)m~1o%Wl zj`>DZ{RGa0f{JQDLPOhkLPA~FATH)+Q-;HK-1=7Z!#*P-_VXtBa zBVM21?+utz)pRVp&1NP_mfe*=HW@Nhk6%MyS5GMB*EHkjc%-rIrXlxw45^OPkA<(| zE{G`B8hH@Z0PmoHbw0sX9yxU?a}f9_X8R@AsZ;-Pe>k}bfTv_?-LeL*GN-o@?%6<@ zY?9NUe~@Oy(EKZ!M*A=H14fJBRcKAyurxqSTd^9g1}r+@K?mimTOX=;08yB^5Ha1ns#6lM}Y69~uRR%2SnA=@$JY-$sM%crzy%>sRDk%S^ zT|}vfKG-M}p3;{N@$`&7vjObShQb9fEIw-RBU^67v2XGmC$doUyoV2oA;$R4%Y2~D zw^mxTq4h0b>hsKf3KdfYxX%he3mod#K(lIbyv0^!m_@fKq`}Si%8Pt`$aoKn7vao+ zyo+V-VvR6HS`joNpvOdQ88~&wZWR1xv-vl-jHVYK0yZ{~QqHkqNV-+Mbu`s@2t(`` z!#1YYX^L~$Z+#R%)x|JvGQ@KxU0+>kecFq0_`JX`AZsCXUFD;3>%T6@AZAFCVKrZH z1_T|yw870Z)a}JU*feH3XjT*#=b1&RDy3Kri|SVJAH#u;M+m%d3qTw*j}*t)<*;9DS-Dq~5>or${P?vhZJ3Ql2Jm)xn8q|5F!^aVJ< zkZ+b&>CI`86b1nf4*zum_UU4+ z&E%{U62$zBbr&LzR(_D6`(mCKFn+U@WnnP2*s8hRY{6+rgW@yIgWze}LztJCr*N_j zq-efRiF@wdr7fht?A)PE98bOb3P;B%X2gwYUH1x~l)dmYEiyl~nG5^T-kBTTPh%~u z71*3MXeah95T?-(XCR%)l=A;Q#Ye<>nxlMD~cjzd5kh(qF`K7xqztj8<{<4-g5GWrlb}#~k2+*Oa z;S{|BD!=?W>>gkWGfYB;f)ku3(z~zBoslENb*_1SLR%epc&=X_aBgn48_r(o@fXrG z0OvqCfR98o%v3TD4s zGF(Zg;bF?9e2D!iL_ml;Vaf=539TzU%Y#NIF_jCl4yV%5vlts=b>->+2;S}{5tdVM zXR`t7w5)TxKq$Z&Pr@th&DIr&NJM8;eU{Er+LPCy0VGj$EzARGfCc&r&+aCLW87Fg zm7l`@9b=MgNb~?4TuV0W8+ss-vyr~R2hjVU?H>P+`p#ACt^MfUGmX9^yz1bf87G%h zsbkaO8sbNH9kUmd!-tizr9xrJUUM;?;7KGwT>HF5NHa{c$B}S8kovz9s@1~N$5N@~ z0UO|98g-1ucj!&t+XH3+?#g+& z;2Q$=3LMnCcQB%%gl-470jWz|uR}EyR9Q7@q*Wtq0Q{I%Z^iv}k|F}AU|Bg{ff%s? zt3oO?TReoHWI-{)v$Tg_3`em+0={zNcB@@T1_DMhU79T%I@H2%VK^D|8+xV)J#z{!gqnVLFOie8kxGE%{S$d_Ej91w|C=m}$J=CJjNyU=|?_2R-< zb;zsUCF44c(~$#L7S=ATF!2JC0jU#C&sLD*RTv0`YyfbpE7p}axWmo?4)8{Q41_y2 z5I7*O(Ml$4HVE*N&Cc@!q3g*LR{*IGXMBXhG0JoBiO5k31vOCE5-n3lr=5sqRuR#q-A-r-%awrKj%q;yQh^_ZeLx_yy>k z&dWH0g19xST!H!t*^6vfFkDcY#yq0fc$C;8g5kNb4jwd;%7DHidm&Z6xIA}zaD4U@ zsJeP&F`RtKMFFM0<98iL-03DcyuUc)K7Miekaog6bm~a8d~;-C*_t?hd;<9cBS;%3 zx*>b=Cen3VkYEvsYF7v6)-#gTsR#xa-mqXO^n=<^(uXnPc<}bQ<%{K1)Ki*^9cRxAESiXy6W7#i1wfqZMJhHs{Vy@5>hB)3u(NS zUlcvuW?&yi^A+UsZ_id>(A2DAsKSjR2U z^nfXjHV`Xe(~8i`uVWp>c-LOXcVXQ9S8>fA{=n-9c^`r$BF6^Jk#5!)Fv%b+2HiOl ztsH!g_ITHvP>A0u|yMNZ&XHo$V_K{Ca>4NBN!x%|Ff#1g$$wTO519 zEy84XT4;%{IeQ){Z2Q0qJd*u$A5wXkOrYrjv@$_f(0m^~mPp1OIL7WhzwdZ`TYjH$ zyL&z+hVSaa6jn*W@XIJXigJdt2xfp&Uz{d5!TQgxsuO5fC5s@mj^5D%d+8US%~n`B z>pqmvPWba<*5#c?+r*g%AN{#Sh^q)x#O#~)zoj3t=#j(@lEy_mRu*V2@wzB=4Y~VHd6c3gM-<5);^{SA+KA5fmr$IK>)Y-**zSNGkir)HU`Xt z@cI?@5_p$^ScSU=P$Y5zIrD&MQAIoj+3EU><0y#CO9#vu2n_*oP`O2z$MJ}+7CH*C z^i1R~>8*WCM$V4{s2}J|$@nOt%JLv@%9l~Vfn~JzG-OGaJQ+=MGmnLjRWxnresq?C z*t0Rq7wBU~?(o>@b7k4D$~m_?C#!zB?3SY*zb7WU%Mz-247p)MSA7!Rj(I%(s5~sv zvp8)D9F$Nr03LDBShC7e)d-Bhl&{`Yx6z-??os)><1p|#svgZ z_9=qc>d*~I72tKM*>i{g^UUeFFK>CNf$-Ju}|W(6i;PeCLUI zRtScm2Go%?#_uoWhZKd#4V)KeOZ`U3y>sG<1{wbboXFIt7XKYxgXpLvT7q0t%dq9} zYmeK9%PTa<-tl|*bikl9WrvP;3iG}W9j`t(USMJNAd27FXbkPO`yv}kN3W>~{%SZC zPJ2B;EgJKA!ij*-mxh^J;PQ_Vw?jGwZ?7@{m$YtnD)|n(Dtr^Lm<|#}+sGo=X<2Qi z(*tDDL;a>zm^u65DZ?MSA06#t?biWZzX*CTD0?#K`909O=q&1^0g{>zN&)az)Vua# zq-A15g5kaF+P^2=tVEDg3X`?Bq*+<2hgT%<1`R@cewEIW~28j3Qhp1gda+Vq`uze#C;tGNE%` zd1(2rW%tq1>JbY??Ey)7$lI70rVB(n67q9C2}qoTwVeqQ%Ot0I>P4A6QNm_K2QwNB zp|LtCz`K5(?6Us%sUi?_HP-NqG`G+1E-mWDkq3^RnCJZv3d>&J`ky;l%xrCKh}GU|wfZy65pGLqx%gQr)L_E3Qw+K9pW3wKFcw7spuZ+1@{1M_aE{h- zI&@R|_Zc2*ICWn1rq~aXmVE)PGLJmKLB+!!px_(E_-)pPTs!0{Kmx!<;zgRLdD(W6 z_T}+gQk|rv(C(&UU4MN~a164PYNo!4A6&{S!7%zaER-AP+e1U`pKA|EeknMcbBEpPVwD&B%_&N=fDN&y z8NxZEsNkFB-@N#C%aI2 z@rOb0kj}_^oj`F1VO>i;XUnLH7^K!V$&tFE2vA2Bf>HlS&4=hJKAEBR`R8}_jS2s5 zu%dei@br2Rm4czeSB5?1oUHk27H96R4d@~RC1dA&GWNA?WwH-1O1xKX5Y<4{k>_^Lq#zKl_?39xTb($*uyEYt z`#NT(coJn)jb7hxCXg$+EzY5_6R(iQDzLb~*diu;Q0fXLhJNG;dvXUQthB$jB&NsD z1QEmQ<@^c0(=tyMiq1uu9nQ;LAH3Bkf-1;Fp~4%0b-+J5XZc?u{Sw*eJXjU=1t{c# zTz}Z4Z?~+97DBL^+X{tzvKyt~)v;(gRR~9Q)NPo|ySp!4Ucfa-S3?qEds zs){=ma4T9i;KLXEeI_hZ2)LWO6--zryx{ab|!+vQIxDF5Yei_o&fz#|EPii zKXTV06#$wZ+-(ZGHUA(tGLD#Mf56RDm^%CjHB>{IKN>!2wJ6fJJIiQi&6!Lilhzb> zAmNo2x8JM8f|BZ15q+HwhF3+HmQY1DAWFzQ+#JUi#TY;^rrP@aOa+#KS%67x+`zKx zm>~rTQ#iA;BHN~^7iUdyIxj+@)qv^q6ja~rn|Wo2UdvS^I?qyB@u*MnhkS^*3wnpN zFp9J5bRnH^-D`VXhWZB2xNZk(0f&fH1c!l9rjN&Pl#2K&s)-?hTZDJ++PtRWck8J7 zf_^oqYNQEXQ>y#J-k?7=5==)MNCs^Xyc=LQI}I2c(Tq-9@_&s5m?$-FjViJ`;6uc% ziNk>wq_N*d?Q&2mj((U&ZjxHft1F=whl`!~-*#z<@vNuYY0pB+G)or7~36)%a{Ba5~G#g>X8VXqm$a+1tER~_pv)4aH+?=?eGakt~5YiY!wWna>ac`hFskWA0jaR8BCL3>&i>@p zk5=?remFVY`M(~0b){y^Lmzk`@wGS$X-f6>s-ny)D}R3bpC5kpqh-2I&;P`uuSWV_ zEuT*&5?_t8lelUOJ$7f5Q@G5A=PIK2)**Y5<$n$(e<*T76iy;z-VU9hDo$gxxw8{C zyE&LN1w{Ap1=j_8!Q%^{`nF$$_`X#Rl0fNBkijsRYjY2(F4|b#1vYgYb?AKEE;2(MsKF`rAkG#j z5s^s1?uSM!-Uclj?RNyB5Kqj_{1Xi4u)sfoCxm0aBmeO{TgQcamY9It+5>x+7DX`` z!lh{9GLX_p9e6p|kwEh79+1ut1pTI$24M3q_5*%nYl~-X^!ANtTC&(jmwSM7c21lk zVI7xi*R2UdNPVB(!!uh}MdLTS1)S%Z{4@J+AuRWTO-5oV9>V3sdxRBf16DX0`Et7 zw@v+(7L~%MZiOJZ=<(j_b}zZn)2vDtArIUIvl;$xQN<1|npxBosVg#4aTl@^96|wn z6g%QtUIGnqcY-OAP}qa6w{1uwhs0)Y(ql}UNxy|ZY35{ZQ7wC zscz0M;rXaj-K{b;I-&|4bk0+lwE@c$RO;4?n(`giOzIsI#L52LLS-TxyvWU8y@-P1 z9qLiKVOnhzRO06=rxRrNyzKowLarEaf3}MFoc#tM&*)3M(UfTW=c3TdFhc=-QvM+V z_jSDBp}15C>Az&NM~~vyZbU7}9<9xo`J4RxquCdC_58C?^~mNiTWl4S;0Lx+IG-tN z4dHG`*+EYsJc?pR#tcxnpz3wu>$by^aUvfFWVEZF8&@Wj+?!6!pyH9qOBnrBE{DQL z>S*r1!?SlU`6fd5E>7Q`G+QG#-#mg}c4oFQU2dT)(hw>gQE{YE)IDmnFON>vCT=@a zKRaDD8)rx9YHWW1#UHj^)cgxsfml1y)^m<+cToBS@;banP5j_GNnDFH;yJ6(b3|)* zEO+;75VsJ1z|D{rAy`P@40PqwQ9 zav!J=H1g)*h=y$eMk^BLpthn$lYueS;?ogBj>lyq@~KEl3Jh7+P(VsWKJ}^4m%kkP z6uQBM-~8rqA`w0V`{kK%!dx*?fz{!s>+A%gBuiwmDnlD`?oBKNN+Rm?W`FOUzC-KB3}&01OC&h3h-3* zv_D|BT6a~XmD5o(dYa?NmanOB83Nlqv#7|`Q^!!B%Qj945x5@4enQ0$=E^1`9xcvo zdQ<7-=wyl8^cM3ce?7ms#`n|WqOK%j5;y6oqhW5(doh2*m-Cw@?Kb@G9v`IY=^I;h z#)gR|$-{T3oLY^|B(r?5qov8wWLkg_*E~<;FKy<3y}2G%_)y_dl@Be&zE2-~`BhYw z9u0C}RoP%0c-6C?7I;(-s1Bzs>R;N{QxzX1%>u?;91dX;Uo8;v$-4o1C@J5tm16qqAyIiNM)rOvfa^LVFa)AkDo}0C7n8d9tT6@BvzjWp`rG#Y(H<*9)G+> zzxwpFj^7QN$OI`ZE<%9eA0wciKGt;$6h=7ed~z|Hp`pPOd>sE2ph^-E;KiIs=d}sJCI}*mK4QQ!w>LK#GNvtj*m(Ry91Ta0GE@ZM z#wCdTmmZ_JAH}cD2e}S(1-RD9?_U-eMv7VY=1`hkyM+OuV*zvIk)RA|(o+%B5Dy_0FS(5L2OYs(#SlX@Rkf&qzNqMTwU|iC z@KJ!PeV82{FE5R-Cv&Ad3S0l&_+c#i$Wl2Jm-3}jV{;QN3C+WKHo84Xs6j(A>NTpt2U8rZ>T?L@E9P(<8FTZMxWLUEJ2F-RZrVd-O!hyI9l^&VOQm33 z8ZL)8YE1L+@aSTxm}jM-Q2fMUv$Qzc03Jg_rOvvI7=f3aFc4l23I)f@%Ys5^d0A)# zQm|IU2d}axK{KNH8tfRNq5=sSwC`qlHD4-5$eO&0Fz2m zx?n;I{aQYI@d1&{E9>E>talTwk(^0(siz^qRJh+*9fpjlLeO-^gvvwWomveuPd9fN z+FZSQ0XIHrWsYa?ztO-0%Y_m#0S)`IlgYe=wBilCnVpK-m(k^NJ|s;D*dBLso8N~- zXB($~$zh@z3VpK|f&DmXs;F5D-qoFY360DUY~#sXEdJZ|?wl`XJjiN9SF6&+88R%> zT8aBoE4f|wg<>q`2%8w49rh?##2uq(Zq1o2Jtx`TFH2} z86JX!brrhxpTVr}24#K_8z4I+zB2P1Q7uz zBY0#{CzXIsz~gLG<*6eWl{&bQC{68=$^ES(Lo4BjFdoUCyL?XdNUG1TdK;0zH&EU! zP;77e;z2l6S2B^uZ*DYh{_m{r*8Jf}^nHyS1rM?_fv9LCc=yTEHwiW;^*$ANWpIv63mCg*k8HE|z zMFQy?frz=j!NO|X$02$=k|FqhXZZn!p9jvM@Q!Il1D)R>@N(u6hP?sa3O=y2;4L!M zSuHOaXgjFZ$j^fF4oxngZlG8j6hdAFT(8e0VYogn2?bD}xqvdZQ}-N2qLHU~_FFye z&NHr9o*XH6O-J`@nV$JOW{$q@22{YkaCGX%$BRvI>a7dAPSHKsi z$ch3ZHvT1{b@AwHILgt@xAza8eH1XJcn#Pi@|XuC9<@=x&!`MNHaC81+(^)$&JTM> zG+WhCCjXc@bZF=uh53k1AC0(G-9R*UZbAQ=b16M{w)AQ?iYv`yL(|szrD2FApz|(_ zChEmCfX6>>EU@m{MJh3Pg*?Xf;X`0#|_|aj9<%)EA_`)|<`EO@D!{^?%g) z$4i}mq&FJ6zCtw#dSg(zXBSSmXq9%nE|Aq5QLfWCtX(J|l3}&~o_JlOu^8U%{G&WmgW*g^KG6Y?)`1a@it`S{8T?!PUypLI)zE&5h$>Ns z>FOAyBaSn$bq-oK+m8bAT2FSi)<&XKalu|tf=#in>?15({x{ywcV39T4xvp=;H3|IsFXlNJ6eXQVjbP5^r%20IZ|s|L;RvznhFgi|1S&nW^> z$oMWeeiU01QMxb_UfKZ{*eZ-s^cDg?H#eKjy$4I7jE#4hUjpk?yVY9Jb-^Px`S^6c z*zKbSaD2{ZjVM+V#|*;%YZPEdb>RkZu2mR@S8?KSn7|(TQD}3}!ZMrsRh@Y{4M4Hd zLPa{D0|9upu0_qYhLHDmBkdsU4>BS^z=BkztH^yL(G%9&TD8mraOyCnQc81jSW2oq z=mke|XQDYjx;IH4EjGF~8q++ivnEm7-?))q%Nubtwe>J2 z%f3J&U0N%p5jHQ&=y3z{-kNpP6G>%q6Mp|hE|Wq82FlE8ng?xgkujIcma`+_@CcrA zDb4Q-`9rFvsd)1F**vU7moMYc!w+m0E#k~|!lb9MzRp&4%)PzNX~F(nf(5~kYobA@ zm=MPFnhwiI=gJR$&~EE&WxdsEodYdyeF#<_=e(xk)DgrnB(aX5tU82po|EtRH$5t9 z0@hJfM>x&Mw#k(z@c8Wwv$c(AW&H2FcxmJ8l)-McQ%iBenT^ej4Xf3vA>bL;{Q8N1C~~YH;718Q9BU%e~y_EDSrt56@b*?SR$7sYmYA8xH!rmC@de9S^hmm zDVv=Ysxj}Y zqxHO;oz1SmOnXq=Qxz79vVE88?z;na=CW`1mcAikluoAClvQj3BIi z3!>@(&*Eeb)W1A-=Xt6Fa$Y?BxC8imBi;Atqt1(6(Cz&~c&GCF^nw?B8+)i znxB%;7X_`ioH@+w958kK)bT@^%pvjQ%;&%E8vTV-_Y%4iB+Kb~g(Zm^9Uq+IXZxao21(K2wK=}q2hk0MPC>Ap#uEe*Xof+jZ1#M^V>Rze}jQt(R^{E zuR=ejz`ZhF zXe=`jh^Aje&={I|ak{q0^YZZ^50{;6TbYM@krFP#2H_!FfI*qyS(pQ#nuBnH^P5r2 zMBMI|5LiP*i4q&Lvwx325{N}c*aig&J|@OvezdFNkZ_oq_&gB6m78fq2=vLB5yU_r zb=?Fk?pWnGI-gSGDK1Xn1tRTL7#npor83~s;UWf#CRUXMq(C%f(Tj-4M{xcwx*Hk4 zZ#U342vZ! z&@g->3ZB9q`kCKO*7R>8RkIp-CKQc&uf>~K09S;bp^HE8a_nixzM-6cXpXz~4R05~ zK_mPiembF47v^c7K+D*(KLW<#rNh8G{@X9+N$^^rhu!<&|A00d&z`#Jh9P<}bi+-j z+>>vdn0VvQPP}n)@{Nv;_5%2s3?Jn%I2tm_;^djaD#CsYA2n&X6d_C1>4e=J^>m)~UqlJ+9Zfe#G^NtL1vs^<%ELx_-j-F4wzV?{j@fOt+3H5Bl}& z^FCes&+!?0%ry4=N7>4MmUqdi-1n30du<|Pr~l_#e_=Vu)h)JL7Wlu}b)W0UUH`)M zIoEIDK$e6k%vDK{ECghp2Z3#fGXVZ*a0wWxY+ixSMUCZ!I#>$~ya;Y5Rs#N^&^su2 zT|{*O`+JbQi2)%pOI(P1igmS`ED#)vXLZe&8u5GZ;~!0Fnx08`3gJXh)znf6&8LETSeA<^J)(F#N@QwO z_N1bd!Qf=ncwjUbMd-a!C@4Nviv}A5Z#EV(6_iP+vt zkM6n-9W!=KU@RCIk7vhsP2;}7Uo#1;_CR`@|!6R9ENTG`_l8y<>G{^$x3e$M-LkZfLGF+M73h zM&I0QQzg%K`%>q{M#DZX(H;&Xah0V!QisB2khix=;YOddZeLiqe0k%8ryKwNNb4hK zKJt-!iG9%V;E3^bu;3odKzi?N?SU6q+^u*Jy9T_(v-O=&KMmfIY$TJr zA>V>IjGF-ZVGm-3Mu;Q!-%ZA0-!J^o43M|M?|ON_9Qci?%y^%+V^5@aEK@)X^1(gO z&ANITKX-sKGW?;yZC?&y8N;n4F%7+!sfS*Hh=MRFimdO5rQ*(100tepVb|r;TtG|UitCg7dMGS}Yk*n=MnPUg0=Qj2eWp;Pd+&OKiE=#v+!V$m%J48q z$Wq7=jMktPp5#;kx2r9n5jZ~Sr{pgvC~X#sETJtKT5v~#O!a%@s4p}k$&%#uBk(q& zxbb0PC>YpDH}|TMAijGUAEH`=4@dG4y0%M9LqMiSmSk0ueMcd-1w1|}u4#mt9({Qs z(DDRhh6Ew5j;qvAfeNK7Ndg=p!&GCPW&tiubD&Bx^8>e#qDDf1WI7laN&p&PME1J< z9s+A5gckE2zsiCUnjVAKW#>LCsrX_ra<8VvQT8P;cGM@so+iQaVrWcCWygjfYxwvK zzdDGD#rlZzE&%7t;%+*=zUlQ7dgH*e>fZ@R4nCp&y+Z4;NNU`5uE+kH32S%IRbe7> zj_`QWDRFhXl!9< zCYP+{BXgBv8-DABGlp5u!vj}b`k3TCHik5SNWPk^E~?fA)4Wr=P|w#*@;R1Ia5IbG zZ~qD7n!xNSj9goWSWJd=8X1xXVs1Edpdfpg2|?nB#|AJIgxLcI+LEK?qrv(TYD|sM zK&XQfK2loOJuo0PU_69!-@Fj1%ti8JNDUcU82f)jl7oK1ZJwa&RRn#?@N2`Ca%bbB zsz@Gm^YX4-Hs3BGotGwx8I2utpS0}_!bGwGb-Ax>cIYhf}G*Ftmc zUG1Fby)W5^xcyx^z43j4tN+4237&9<()2u#35ZkK4Z6tbju23UxFFDF7!EXKLoFjr zj}i-j#Tl3y47fK93NTZrQFDz{VA7=Ug48J9PIbf`Lf++#SxpJKN308Ti}@QGV|SY6I#mO5K~T%9`l{L-*+~Ruy{pPJ)VFseb#sXDc`Zt)1KI={8H#l$Z#zm8J^Ei;w}MnIo8SAWST3EWsA-QuE({I{${rYI93Y$fwKoxMpl2Qx%dMqqi}*rDxFC%Vb&FH#zV90 zr*kPUrIvg1qsxKxTArvPA1w&AUU>NRGV-A{@ zKH6YLilrXPcJ9f(Ns(vdO2)6HpGilf|8(XD*4xw32)l@r?j01~+-6x%*B5WhjT-BI`!)rjLnf7`+8t(kPjwl#+P>Ru#SP0pHYzMWtB;{_`AQ{Hm>Bl^QaLIsBhE1alMa{) zND(7rnXpj621#`p5S3%psxo=vv5cUoePJU3VKv}+&uYsbjR|6oq4OVx%t6l zJDKvL>0UB&mw4Ks081Tjf5eM7BgwnO6Kx?lbND`LgF;9e;Uzl2jR)MG9A;u{h}Y-# z401Cc{Bo7&?Lhj~Prf;te8YLSG zpX8CKG9wfTA9JI@Pt1oJjP4!v2;V?AA5Id<3;AISE*sL|vDPCvFA!QbkE0Kbj9|{A zlLNX{e;z9~+Mc3=n^#c{efwYCpUkv3(J7|U_^W&1d&!oj%1+V2mOr6*N<;1Y1L!oj zW$s_dkL+Oo#Qp$9@BH9NW$cb4u9J{$?gnpCVPE1w!HP;T*bAhX@h+q|w{ZwTpTjCq zUt92Tyo94fyjQ_mig)U`5Pa4yuh6ZV`Qpb@uoYEaUo0gO=cmpF-O6Y*olBY{ldsZ} zKLXW2IjSU5Ob&(>BQ$P7_rsdqlu-gPWi0#K`Q=QSyHmqM!UfA3AI}^)Y*s2IC6uXK zQUClzG!Qf<$F;~KYBZ=GnSI)jys{QhQr@Pi#)E3sxFe%PgUYy^$S;SK*DT_6a?PA46I(pyH zg{YQ5g>>Mrg2M`x2H+Rqe@Br$hc3Mkn*^6eA&&+sF+htgrYRZGgd~GBWqR5FCjuDg zW)0HQ5zh?TvSFr_kx{@5If`_8a&nNq?j$qxK}0DyXl}G(ZSER*6Tt=R=BB0ZH-;bd zOD-p}`>QC}16w*nu}o}v?yndy`@c(Pv*}SinbhApnlOySXeyg!AMK9YY|@PN!`Nwo z$XtCc91Mo>6bb0{rNgMj&_C7gT%Q2n^iwE%9P%Z?kLkBMVC=W%Po2s?@JA0kfM@3F zj>)ksK6`+F1{V*FLrsYYrQQ8{1!o~|(OV|bbWTM!G|ExXf^#aM2l8>z4gptoN?5?H zI0a1#{w|OROg|ouUhoZ5T5B!jtPmK|$ju%k^W)YShSbe!)P82jF-ppfrfNzSI zRC$ygg`aV=zC!-!FG$iCHo}v0lVNOdqA&(^vV8^d^ly}9#E+5mkG`e|R*9v3hR>wk zu}yTiy-iUxdb`!|*HQ2BC|Iwh41Q?3-XA^$B962sNjj-hCy)&cV1S zSebsQU099*;^JyF4Hkn8R9y>f1@IUI)Jc?t0hpbuTWES@kwX*a(U)Af1XqYhQYs*9 zuR%rPp-6zU2iud}E?Y)m4FUkbKQUAfnBFU5r5qwcAmOj*fyCFwjgc{nE@b=>BLkFn zy9u8XyNsw7n6$6*G}@$iyc$cxF$87?5H@;Y@qdqV?MYZTljH#A1<*)(MY<&PP$v-` zB8ctofh_~;Mx8E%c?Fz|k~=AykbTNIBbM_Y6{kcM{l^Q1Q`w2QF<6)pK{Kq<@}O3r7pO=foyhp$SPK=g@xgiuBV0<3Q#gpx~wv~RB>F_FLIra zO5dB%5N-rlr{j~1k5DIRJCmV+_|06`;?jK)r!$DGG#uZgR6AclC~s4!b1Su)CmE7L zNl!ecH=Y&67JeFM3{-~~^f(&XCXy=)3m%G}odeqZlFzh3qqL6%jZ$fmVaSXNJUGqKV)p?V47P6qap!7Sph&kSS!n^n zVUpQZSw4V$0vMJGI0&QZ!izTe&kBg|E!>=n^H09L3*c2y7>EincLH$WhEJu@UR^Va z-f?`2IS5^7gEhcm$?5@{5gGi6xgy4Jq>Mwys6+Wdq&Nlj>iJu4$+y7i*Q`~uoxk%`iATyjEb3#YgAwsE&hr0`_B25HN?0UNa&rv8cT&j&`j6i@{z1R2z z5RZ(Gr-wb32W8!}Df9+v>Uh+`M@NdsnLDZv(gOIZJ_T=!bw$tTGBAg2wt zn~<;CZU!4h8`x~Qjc&7OJL}6vR^P1zpo;6Al{NUXHbu}~`=W+!urX>}TmC7eeNwWJBhNu2F5DGLluimK+G> zcs;{~axkBqO)fcVpm?VQe#A=d>`y-;i0Hci2X@ zA0c|lzdu6q0-X_`KxZxj9CRLn)ej!3!#G%DfxrXw5DVzkYj1e?viR&K@x*G^iN4C- z1!xB10sMcF+)0R)ZHqhq|Kfydh5$$;^hI$^kB3uPj3l(e8xbdQ8!!*7BjyLVLjg=2 zm}u!oe~4OcqzLJ>ld)ImHE0Q&h4O@akQLQx%GjeHbRG&HK9 z*CKI+nzKWEfs8=@On8um{pt64DIq6F44hdn%Ag^bZz0d)>>n9b$e&&Wh#tfxuCJ}N zLw@!)Uzmmco%j1gYdT~O)}RpFrpfY;gc6AmJ)m}+)o^^gz!t^*IC2_nY1hOJ*^YUz zT(ySqbwI>h-s?#%u(}%1R>V{Cv;Ghud4e8)pkH=?Shp}$pC3Ad7O>*shr@CHfZ!Z* zMH8=pt`B&AANGf^9USMx9zk2&f99UgHKL8$v&_VEaK|G6fT~OWFwK=%s}i)G=0AYlTJMQbsbkCl{p!}#XDf6 zp!FI&FYsg85+C^q5hFhbJy@L~w+hTNV1{LQ3qummR@@2aH#dTSDh-#)uytkQ%A`9p z$zf4C&ubB{JD7<1VvqC#`^^oShc}{_c@tlla%ZM!rZF}8v@ezjy1fz9OZwgph_RNy z>0h%ac%#}Pd>KcGgOKEuM@I$XzVrw=6{bLP8Z7`=Y`=xF^TI<#h$wK6xCvW&OZXcx z(__sKyWc^N9@8)9qUNV>o_+J1g1RT1NxH*Kb6$165oK#{9v#h{JS~;jTN8H{e^kxn zH7jFehEW(^JswNAl}vC_f2|bG>h5qhF)c^v7MbLiV`kg$<90ieWxtC#dJ`N~c z2v0@An%w!{(T9LT!%o&}x5!GyneJ_G@#Zk&4>|KhhvRn`_SDpqh-gT{%oK@e(r>6@ zTArsC3^b&%XM9s)EQCqVIdc_sg>~6rC*a%4fnOm?or*SdW)1*>9s=GTNu>C{&DsQ~}>hE@H*Zza}5|WE>Tsa9H-(2R6TV6Gb#vLhLz8s!R=Vyi}er8%3NkAkS zN{lEKaTw}-ol1;+!hjPx2`dEBE+PIH30<6{J76n=P8tJYgAh&@UXoe3XtKx>2L}^k zRv14yfIXwt%`yfqCGHDwI0e$^Fq|qnTO6DU!Vfh z3!sfYUg?K4&C|xp9Me$Xz>B$b=8y%6llEs=AxxZGP*pH&*!`<)Q}AVqy+T$&^xFmH zO}h1bWT`k?Bx4+fGQreD7qJFJ-`V1J6c@sSW$C$M0|m+<3@4j#rqVQeJlt=`)S|o} zQNSR$DDEH9J5MDPiX|l?^aDtIY7c;6|F2m3o80R2kl~S(U^sA+31SzVu@r10K0lXQ zZd7&wD`4Hgd8~knDgGCG4!|09>?(jPOvzl^;CinBmLv$WrWAEv*cA@VL=`h2feM9P zR|!?WZCE7#;0JOKktzM5^iZ=y{>8TS6#E~ir8Xzi`^~Q-C}o( z-QC@Z-QC@t+fMw(TniN4p4jJquj@yFhs9#fF>~}i!V9Ssj^0oe_Hr!xefGezkZFqE zAgo3ZABpY+6}We}hj;&yQGp^FA^#DqA;`Uh0}|Z&3B6OD&WkeI`CXnkYlF$rU}FR?{~qot6R;XHsbC zf@t6}UfQC;PV2P`${x!gSOgRPIoTK@8x!qKnD?gp$D^HvhlIkv;BahdctSkd1*`;U zV-Q)4?(_g;c7Yuhia`C~KLJ|}vD0P2!Rdq3)=UdugEOQHsvVTBcLZ=m;Qp3rS|Q1N zeUrt-M#RL#$GH95HX)Q9ahLz5&8n!$`16C(#0jvTr9;s)$a_F|q7adJg#m#92nG|( z5AZz4YjXi!pYVV|>TWpru!v+S1O2y4dD2J{rRs*qt2NM@SvU`_WKfa7-w+L+cSBe0 zl<5dfPnnGH_{pL@8WBn}gm=L`n6O6>T_`3kIq*;qWnkS8$_9t{hm{|^vR+#b6ZRXv zANaUYu!q-a=OHmo9%*}8NviIpWr&xu>JA~q(+VY52*XZ5`3n!pkuGJ@48$-mkMMqt z#cjh&vS%0q;UvUgd*;#$;~>CYl*OaJCDLC1@tlGi3H|@?=GF7o|9);0==3Iz7DjW6 zHltUf{@>>~p5%Wx&mP+U`CQ+`Zo|I)IoA$&iIIK`>exS>>*fiyx>97wQ0l*$@Xm>< zX<_;Pn=S3x(+M>HocBajGym(}{)PO1Hu;IiA^oH64tI9~-y%V?l)(ysfK)>~)|H|@ zRefqg=2b(GT_Cv@kX4b<9rAAj5&z8w{1{|gCmZnJY`}NXcgA+$WP_s}!7f8tMsM^S zGT9+qtyD~+G)dfmOAcLAC;;NTOs8Yg`6+&MI7j;_BMBlkj$0U(6^D2rEHyz)SNu5I zrezt-;GCaw1RO~5`0&vv<`{THi1%CcdCX%`1Ir(1nkt_R#kToo7A!&1MZpwhMTX+O z@ME~Ipu#)gpC4B_-mcD2o?XQQ})!uze7B8=H+kyKg<~% z!5{4Z|9Z~q{V3&`BE$dX%zXoCBcW-y!;pXfK9szP_S|8>F^rTNV7EfTdl-sI0Hgu{ z$N~3QbTraC04(HCqeh!&mn``X^p*s>sSWaw1Nyj67}#;WR$b)LM6ofzNr8JnO;1nt zLa_Hjj(>irCFrrgUL9jKFl z=tjO3r0WDAAgn)WI}BKFQB(lpU>3wv0X`ox6=e7NZ2zRNq{=Egl%Jg$KD>{KHKNX0 zQzf+=L9+e)GiP23pZ-5y@X+(!1Nn%2JzzcY$Qr4@e1&|L`7mVXhS*mJOl<*xh4q6o zg0TyzLke10P$~!6FaaK7lH;`WL@HTX(<}_kYT4g?{Jna^B^1E~cDQ9;;Q#ZWxp_jC z0?lOSvzfANX0vGDr(UfA5DYmf5?SpcC6AtP6}u0-DmlzoNW48B>?5cghKXVd!8ie? z_G~ac9E$_Dw^>A4p2l zKqk|VHAc`Ip`KD-If6n_tam09;f(-40pObPRHZ_f7c4eJ#-flE&#+K1IMPckBMV9` z3!@ZN))TJS(uQ2#r$2Yb>0FXeNn` zi;aZ3c^m~vQXD))Mn+;;S(Q&A6TFhqCJBPTc}Pu3p~xI7syJzg0gx&cf6i_}>{(_2 z6GbJOVGASE1 zH|~s#kcgGY-gq*I%z-sSTPi=E4^@_db%0-i8pTlX zi-byTCdz@sx+@?IL(DQTVu0fD_p=3{gBNU-VGSjNQ$Fr$^cC6@=x{3<%dFVQ+r!n@ zlf)n@N!92#EV2 z@I!2ir%nS>DNsrRh{I^!EimVS_YEw2KOO*T9QL2*x99;X2qC+;$BG{j5s;Az%N`tJ zW;CD_U5+Z5Q+DP#*G-u$SxVP60O!^Y1nFNi&`W7EyDECc@by>u?pdG_!0XH-rnX3^ zpU7(@B)dfrP@9krrbrq-NGjobut1@oY4`@?g3ow6AA%(-0C7rXkfqOn(}JLjL6@@A9GB480FrzYx0=3LZPJ>nkk50~QsNG%zGK%7Sci zxX**VnXpm1ogqAftqW+AL`oVj1W!b9BCunIqAw6O5|hT)3Yi!Qp-5?bm-?o;O2b}~ zw1DKAu4~C-0F}oVEvPq4)ECftmQ}5~%=@Qs^Pq=#)6H36V@N4X1ump8Sj3;^U$|WX zR?zEJ@CHQ$dJqX2z@~%41V&k8Z;H3-U?(Blg9J!J;!Jw*l7~d1ZG=*&q43UyRz#y5 z4Ryt}^klRWm^^u4@|nn;9s*wK)$nG=`_3eKJCy$uuWwC$^euiK+Oa)z~P7(7DPt;Oyx?%FW@|+a40Vc(h$c?$j@FV5|d&KL{Rca zOUL;Jc>9f=1ciY-#{k@6rh^4f_0W;5>96js1X@D>5iLKAH{hK8Pc-%R1WSbfN5TS; zW6tAxLoEIsu+#>yo|%v-jzUgYRLC+^^DGz~yoB$N9s$PhwHm`IGkN^yrt2D5c6IWO?<1(D-K;XOQ}wMEA*G0jCm-LCQfjdB!3=NSYO`iH0g zaa?wa^Z^j!8I;N|B*66bO_$OyB!!ziNr>M+8SoB)@~1cledS$&PD%0c=MbS~d27)k z3}X-rb%!47nVs0z<*0;6NOuj0FLQ$%tXGanYRUu}zC{ zBOtjl3Qt=&Eyg5cF!+u~MLi?4r;)P6rO8hEWx=Ar1)95IZulR`N#K@Mn6yRGVivZJG1Vk7l7I7Xa|7RaEC};r5l+Xu~tkC{I{XocI ze3YZN7ld1Gh3lS|qH50ofRZqXh&)e%Wsi*!j5F9HF7`KdveS%Z7knBJ$zkyl6p$ zEri%&5pl~>dp;+i5AmGes4YBC(xabpMDWalMgq`rrCv1sjIpJr&uvj?X9$C$M|NK}ZZ!54o z=tu`Kzz|SN5cCRH5VY-SG6V~bUmnzGX=ogEVnO545ebV^ECzKD=@$kysUZ0U<|`2& zNT?|1gQm;V5dt_s+y{0-9y~M^>;cqt_*39q@BxMp1Y?Wtq~Kt94L&@?dsQSvdjgyy zN|u6T4lg4^+X|^Wp6lLoLB+357#5Y#utz;hyys9D9hhiPPfySn4^P<&cNJm=VD0tJ zm-kKu*E5>*L~IIm2gLv2P_fH^@asSL6KViHezxHOaQow(4-Fv>^6P~ky=)%)_v_wc zgict;i7?Zk&cpuC258aVRRn$;1(gjjI|6O?9HAIO#E!-6xaj!p9g%SFAV?ha_i%U( z)Ig~5+l~%_R{UY>qK3l5;nUkva9sDF`!)DjNb1xf#}dYg)(4%-pXZAyX7q%q|Dg;g!_A3_-NS`7-{0C!ih7`Z@;)V$? zXGjnCsb>t{_Pt|B+2iNGmP#b&!?v*3k>PUaefMIJ_U*Mmp%0ql5>NDqhQTZxfLW= zAe|-}MvA}`1JHQmS$HB5)QBe*%CC3|za;aDvp)`e)%czWuT*#O{(m)|_)f4tjmOi|zaG#Nk3j70FX4W* zA<#a4g@4Fl3G0kf9GD=q+DLQ_ur0LW8$GgS{JMJuSoXSi>6dziP1r*}`|yqlct#o| z6UUz!EB+iA@f*S05#|HRoL8XrO3cfCbZ0?F35RB>S9nH?1iK<$c!oYHbOwINxG13< zHcLGG4D7$3@+Pov59kkcZv0K!jNAT0*0j=wmlKWZ7cRv9CTW)XDQ4P9gC_!V6^tMG zWjw-Y?kt|P%y?P|u(~CI^kT5NA@E1$PjWY4<9Gy6WQ>8E7J)YYB8W#pV)*Z}xaSC` z2msN$dM;TV_){_m)jawS!a0Z3fkraaPw^}22f$@NNcGIy6U-|QqaE&`6O|yZf=p~6 z*%5sLuSYfZ5@h3_qVW`HCG0Plm_K$BD#ai~wmO2Bc>;S%Q#^?isXR-hRC@ za!RBxC-j?GP7Xs02AEa0iH!B=;`qC~dYq*| zU!r>sNj4y1f|Jk7(>>z`u9xO_$udFIjKP3CBX<830PrH?p7H_+nN__4E;7niO^`1^ z()2-EeKaIEghU}3HwStG8kiq6Ao)s#x%UGb38e9ZvjPF&f(gPSC2A>=pa^&H4`LFC zivNy9BN`%~cnN~InWy2QIpWTHNCJ5qhyrmNk%S2OA(Xs7h6LY_h?uHK>_sVy9{K6# z`Ssp;z+1gs;BR*Xa?<^FL;T_N*)kJ<5-aOYE+B4og>us0wz#SmCU5^~+SuJSL z42&QmNF?UB1tEnAPwjhOGx_T%@d2K6Hl z)AVxo2n(%-g@kNiVIY#mOZ5Qe;FYi88c3|0d)F6E{J&gZ7!xdmNA~-b69AtE4*5q; z0Kg_5DG-e#K?X#;8jSpZlEr_n_s`D$yzbsU{~!~3*B*?LKl)9O3(@NT>+ylg{6l>E zZ69MkiKMqb4s%3e@ePeNK~_u{>%ZZ7i97xy=liAmf6V}Ykyt%;9*-QzgY*R!DbK1R zu>`~*`06c@gvJ>)dw9f!3*dr-deI&cZenoJSD+*;_0&82kKV(V|Y_(Sob>fW+ zNCSt93HBtqlRfffOoEmH+alsZRFqf7^o6>9X!D^`Ls$$PCXcbyyEm3W-5d(IjxXr< zo@$jAxH3?gH}Kj91R9~pH4O*~-~tVUGNQpn(#BnLlVVjpbzsUkW||y4os~^)Vm?c# z%q2rUxdj5imxv0*VA>L>`<*Yy2F{RLD3Tx_p#E4mxk-TAz*UEZdR^TXWJQ9c4OU*% zZ$>-_Lfbv=*OX8i8NwH>pIyabK4z-SSu>|HL*lE^#4Vi&gaHYOJ6r}yKJXrRYZ4DV znSrtp50NmpNUuH*p(S2gY|qI+cJbe+Nj&&J(UzF3h@TXO|AE5as0;azw0hV72aSez zeSV!Sm>Dq5|5&YG$blKj`eUX3*86XZh4ve|TfK9F^kbxNdDaJFEmQrn3;sM2&|U0* zoB`D9e;zo{@^f!W32S4(U=b)V@&$ut<#k*HL3ve+O0-vyyB8&BX3g;wD4K{kFF;Vi z7udK%Ys`sYXC%|j`TIDsL2e<|Z`LF7{ z8c2{rps+M5mEiGgqgVs4zZ&~*s=t<@*s>&I;%?wt$IzsD?b^tPi6mw~$ilq70@#c;YOU1(oG4Wm$CSIFg;5uAQdkpX7G0z9#(jNtQwiAUCspK}rdHocT6Vj(OW zU~{4vad5OJg#0;)!v84rG75czBp3r-C3T6#S75ffh?#+yzdOF&4di0eyjj zo}N%}3xYZTJR}0ecBpby1ZbjysmN;sLa`Hwj=+CG1ti!Z^$mhQyp;j|(EY_i01C-u z%ZAV#3o+_%-EJ)m!9;ZS>p$+Zb|KVfwrr@+ zLJW*l^*MmNp6~%Ke$Q9*jDrSc#lKF#Q?mGH5}vcfKO+#sV)4&3oW6?j&phy&#_`Vr zygnNCB1UrXO0dt|_-7or(y{nw0$#ro|4jM>V`Tg@yAxYwru0ywtbt(CXG5~ zY~HbB+uWHlHI08gW1}{$e9A)bc}JfnKJ9&4z*9@$|3%=Z4)9YmpVshGNhq1r2A(?j zRD#!=`E-K!G=O*i_kZ`_zq6``4%9d5(I3_`Blr`4|NF%B{iLoq{B3K`-+RZ?82-K? zlpyj4$~yzySz(kN0cO`9KHEe4t>CGcryn%>mhi6)ywcY5yGVGY5xhI2Pjk;-wS{oj zOz;nZ^tOgq65gNDGdHc!`S5&rcv1XwqHoC=s6atrBnD{x7=y7Ghw+$ziI@by915ml z8m40gW@3O`jyaf%CBc$nzF0Cqq)P!&n*LZyfayqurN+`=fe@;h7E1@E;X|z*0SZ*v2zys$4B#;8&q$r3L!U|(Wu%cKotTR1h|CRPipjn%>GV)d~4SOcsf)(C27G{Kr; z&9LTJ3oH_g!dhaju+~@`tS#0KYmarnI%1u$&R7?$E7lF`j`hHLV!g25SRaUW?u+%q z`eOsIf!H8yFg64miVeetV55R${BL)z}(rEw&C@k8Qv>>6DdyJuEj%V0&>;?7`dj&uyudz4S zTkIXA!hFC!VxO?j*ca?8_6>`}zT-Y9q84hBKy?wE#u=Q&Ih@A@T*M_@#uZ$}HC)FH z+ysGM%i|UBig+cwGF}Cbz^mfb@alLCye3`?uZ`Ei>*DqB`gjApA>IgYj5ooX;?3~p zcndrdkHTBxt?<@(8@w&v4sVZlz&qld@XmM_yer-f?~eDtd*Z$D-gqB88t;qu!~5d{ z@PYUsd@w!)ABqpdhvOsgk@zTlG(H9&i;u&{;}h^0d?G#xpNvnzr{dG_>G%wMCO!+F zjnBd7;`8wN_yT+(z6f88FTt1M%kbs+3VbEL3SW({!PnyJ@b&lxd?UUI-;8g;x8mFI z?f4FSC%y}h#dqU-@V)pxd_R5wKZqZ~593GhqxdoWIDP^@iJ!tx<7e=*_&NMMegVIT zU&1frSMaO&HT*h$1HXyi!f)eu@Vodu{678we~3TAALCE(r}#7cIsO8FiNC`C!e8TW z@VEFo{5}2w|A>FWKjUBUulP4S4*yR05D*>>kChy+3;Btj+>LM1dp zCk(8X}MgBGMA+h+rawNKa%SG7_1H z%z)>gmB>b9CqjuFL>Q5i$VKEP@(_86d_;bt01-|UBnlCQi6TT%q8L$}C_$7YN)e@r zGDKOT98sRAKvX0u5tWH5L?F_)M}%qJEQ3yDR;NPo^XT$W&x%G7T9>29asWbYw6YLZ&A(kQvEL zWM(o8nU%~&W+y|*9Ap@olgvftCi8&qm5PtU=Z!Ymv3dI%Hk49$BAkKsF>Bk&VeFWK*&k z*_>=aMv_rvOR^Q&nruV1CEJnh$qr;kvJ=^v>_T=WyOG_=9%N6l7ulQaLq?N*$$n&i zasWAy97GN#hmb?bVdQXf1UZr%MUE!NkYmYl`lmF#xr}Q9LD3A|+8WrBEuR zQ95N%CS_4J4=b)-5`ovAKVSE?J;o$5jLqnVLdPrKVBSsTtHv zY8Ew{nnTT{=27#h1=K=n5w)0FLM^41QOl_n)Jkd!}UYMrsqanc6~a zrM6MqsU6f#Y8Mqt?WXond#QcYe(C^qkUB&irjAfYsbkb}>I8L?Iz^qP&QNEmbJTh2 z0(FtPL|vw?P*ILe|Gd+G!Ak@`e^roK>Lsc%#q^_}*iF&d`{NJyX{ErFp~nxlCr2r1GMEz=6E z(i*MP1|Z&BkTC7gE}eu?oIchqv^hM zKe|6XfF4K>q6gDM=%Ms5dN@6T9!Za)N7G~IvGh24JUxMqp(oOl=*jdHdMZ7Qo=(r8 zXVSCi+4LNGEkJBgUlk_S2G<}9XOP{09 z(--KA^dpP)IX76UyX(bdH=%E+#jVhsn$2WAZZvm~f^bsQ87MB1}=H7*m`n!IWf5 zF{PO@Oj)KJQ=X~7RAeeKm6<9`1XGo%##Co&Fg2N4Ol_tPQQ%ur?+Gn^U0jATYJqnRRm^H;4YQV6$E;^IFdLao%w}c_ zvz6J#Y-e^bJDFWfEVG;0!|Y}DG5eVV%t7W5bC@~89A%C%$C(q%N#+!DnmNOqWzI3@ znG4KC<`Q$6xx!p!t})k{8_Z4S7IT}q!`x-=G547V%tPi8^O$+UJY}9S&zTp@OXd~x z7xS8V!@OnQG4GiV%tz)E^O^a=d}Y2dam;tths9VZmcf!>5T#j$MTSt68z-_7E3*o# zvKp(i25YhwAhbKI%O+uyvc7CGHaVMu^<(|nlxzT-icQU?VFTG9HZ7Zu4Q4~w^lSz; zBb$lM%w}P;vf0?|Y$%(94P$e%x!Bxn9yTwVkIl~(V8hvhY$3KVTZApj7GsOECD@W| zDYi6QhAqpMW6QG@*otf=wlZ6VjbN*?)!6E64Ynp*i>=MpVe7K>*!pY(wjtYyZOk@d zo3hQ==4=Z#l8s_pvaQ(GY#X*M+m3C|c3?ZQo!HK77q%)7?|26iL6iQUX@ zVYjl|*zN2Nb|<@wjb(SUd)U3~K6XEQfIY|_Vh^)N*rV(*_BeZjJ;|P8PqSy(v+Ozc zJlNnavX|J)>=pJZdyT!$-e7OCx7ge49riAJkG;=6U>~xN*vIS>_9^>}ea^mMU$U>* zzu4F88}=>xj(yL5U_Y{-*w5@2_AC31jbp!aJ{-p390c=n6i0Im$8u0vk`p+QlQ@}E zIF-{loijKS3I*7l!?|1%E-B~BCF1~EiSy(9xs+T0mx@cxrQrg(ATBMJjtk~Oxb$2G zE+dzT%gklrvU1tD>|7|9gA3zwa=EzNTplhjmygTO6@V&%1-U|8VXg>Qlq<#+=SpxT zxl&wdt_)X}E60`RDsUCKN?c{G3Ks!@xYfAoTn(-!SBtC7)#2)L^|<<61Fj+0h-=I> zftuaTxaM37E|QDlT5_$p)?6E|E!U1~&voEBa-F!&Toa|5`6+#qf+H-sC?4daG$Be;>=C~h=2h8xR`g$x}SdGd#<4JkJZf$n!F< z@G7tII&bhMZ}B#it8n=wd{W+*PsS(bQ}BMgKcA8h;8XFb`80eWAH=8S(}A-ogip_B z;4|`>_{@A3J}aM%&(4SPIruO>C!dSY&FA6s^7;7ud;vb3FUS|-3-d+zqI@yFIA4M< z$(Q0w^JVz5d^x^6UxBa4SK=%4Rrmy_nUyrZPH{cucjrhiV z6TT_mjBn1j;3N4cz9rv^Z_T&i+w$%B_IwAvBj1Vd%y;3t^4<9Ed=I`S-;3|f_u-@Y zzI;EvKR24{{xW}szsg_Zuk$zfoBS>QHh+h|%irVg^AGrk{3HG`|Ac?aKjWYCFZh@I zEB-J3HUEZx%fI8_^B?$+{3rf1|Aqg`f8*o$?}Cqj3AjKAq(BL@zzD3s3A`XcJxNKB z1w~K=P0$4c0&^_E797D9k_br!Um=;0Tu34K3I0M#AwWnaq!!W$fkKdwR!AoV3n4;! zA%l=n$RuPIvItoL>@K?yD&!Etgq%VyA-9l6$SdR%@(TroaG{`3NGL265sC`MgyKR8 zp`=hsC@qu`$_nL#@^XqgtfvtVZE?H*eGlg zHVa#Xt->~8yRbvpDeMwrh26p)VXv@H*e@Iq4hn~a!@?2asBla;E}Rff3a5nA!WrSL za85WcTo5h_mxRm072&FIO}H-H5N-;$gxkU$;jVB`xGy{q9tw|y$HEigsqjpAF1!$4 z3a^B}gxA6w;jQpacrSbqJ_?_N&%zhstME;T6TXW+A|~P@A(A2`(jp_WA}8{qAc~?S z%Az8wq6U6qLo^{>!WJFT6_bcbMPD(Qm|RRD`icHxN-;o8C8ieBh=F2|m{v?D215oz zdNG5TQOqP}7PE+1#cX1BF;vVUhKV`FTw-o9kC<1?C*~Im06K6%v5;6;EFu;ai;2a> z5@JcQlvr9UBbF7*iRHx#Vnwl%SXrzhMu=6#YGQSMy#Npxyailm(94(F!$BN^`@!|wA29U2OiIc@C;#6^( zI9;3}&J<^fv&A{$TydT_UtAzA6c>q$#Ul ziJQeO;#P5+xLw>K?i6>4vEpuVkGNOdC+-&yhzG?(;$iWKcvL(l9v4rDC&g3ZY4MDB zRy-%3hhjY!#Y^I4@rrm=ye3{3Z-_U=TjFi;j(AtRC*Btyh!4d_;$!iN_*8r*J{Mnz zFU42lU*c==jrdl4C%zXyh#$pI;%D)T_*MKS#);o09|@CiiI7N%l4yyMSc&5$K@ufN zk|hPadYYt5hGa^XWJ`|ZN=c-olCP9ZN-m|4{3L%Vr4%5gl2S`)q(CW1N-L$4f~62C zy_7-9C}ol|OIf6>QZ^~O6e{JA!lay1E-AN^N6IVZlk!Uiq;RRAR7ff;6_JWc#iZg= z38|!1N-8atk;+Qtr1DY)siIU#sw`CjUt?9N8ld#mkZMY`q}oy)sjgH{sxLK=8cL0% z#!?fhsnkqrF13Ih_9&^P)JkeCwUOFN?WFcn2dSgfN$MMiww zf9BM}Iw~ELj!P$`lhP^av~)%~ zE1i?hOBbYz(k1D#bVa%8`=E$Oy&N4hKBlkQ6oq=(WY>9O=gdMZ7Wo=Y#J zm(nZgFX^@PMtUo~lio`oq>s`k>9h1j`YL^s;-v4gkBmXtA3`Q&N~UE-W@S$1WkD8Y zNtOZIOqDfRmkrsJE!mbG*_D&XNo8L-nVeisA^XYxa!NTsP9>+7)5w8xkepUd2e92C za(X#~oKemsXO^?bS>-ZIggxI&L`)W3&`PeLAj7zSS}(Lm5a&6 zC3UWocl3ZD?B1gzo&SKGdUAcaf!t7T zBsZ3u$W7&Da&x(b94SZ1E#+2nYq^cwR&FP^mpjNEZe-a*RAto+M9}r^r*~ zY4UV=hCEZACC`@U$aCd+@_c!Lyii^wFP4|cOXX$qa(RWkQeGvmme%(ud|tjFUz9J&m*p$+Rr#8HUA`gTlyAwmR$ zLX`AM1|_4CNy)5aQL-x8lPiizrcz6(t<+KKD)p55N&}^# z(nx8nG=T~j&6MU!3nfyCQd%mll-5ccrLEFVX|HrpIx3x%&Po@htI|#BuJllPD!r86 zN*^U!>8tcp`YQvJfyy9durfp$sti+xDx8S*@&5)++0i^~wfi zqq0fatZY%XD%+Ip$_{0xvP+3ob}M_7y~;jizj8o1s2oxbD@T;0$}#1*azZ(&oKj9J zXOy$bIpw@^LAj`0QZ6f3l&i`$<+^f1xvAVzZYy_`yUIP~zVbkMs60|0D^HZC$}{D; z@zO{=C; zgVhi>y!rfM^_x!OXFRHM|EYAdz1+D2`wwo}`y9n_9$C$+QMMeV9~Q@g7@)ShZD zwYSN0h?xJ{~>dQH8q-cWCy~)W_-*^{M(y zeXhPxU#hRvztq?28}+UFPJOR_P(P}l)X(Y{^{e_#jZ?pCJ{qRs8UdxgDGiFEYplj; zye4R(CTX&!XsV`Zx@KsmWrDX_>VwT2=^6&#nO`h!&>h)N*OLwLDs0EuWTOE1-pI1+_w2VXcT( zR4b+x*Ggz5wNhGXt&CPyE2ov$Drgn8N?K*DiWZ?&)v9ULwHjJYt(I0>tE1J`>S^`0 z23kX{k=9siqBYf;Y0b43TBH`GwbWW^t+h5!J13 zdTG72K3cTaSL>(s*9K?S7{Mrb3oQQBy2j5byqr;XPpXfaSaWs){o zo1#tCrfJi)8QM&3mNr|Pqs`UkY4f!O+CpuSwpd%DE!CE3%e58SN^O<4T3e&7)z)e2 zwGG-vZIiZH+oEmNwrSh79okN9mlmt-*7j(7wSC%t?SOVrJER@fj%Y`eUDmE>SG8-}b?t_BQ@f?z*6wI`wR_rq?Sb}Cd!#+qo@h_C zXWDb^h4xZ=rTwM7*4}7uwRhTk?SuAF`=ou=zGz>yZ(5x8UH8#39oGq+)G3|T8J*QR zC;}tsfN84Bx}vK(VEpKYZt9k9>yGZ~N%W+;ubxa#uBXubbbmdi9-ybvQ|oE;Ks`uL ztEbb0^$rM2gdNaMb-a?Pmqx6<~E4{VeMsKUP)7$GE^p1Kby|dm$@2YpxyX!sl zo_a66x86sO*8A%H^#1w)eV{%_AFL12hw8)h;ra-Dq&`X?t&h>i>f`kB`UE{jpQumL zC+k!6srod1x;{gnsn619>vQzE`aFHUzCd57FVYw5OZ27sGJUzeLSLz`(pT$i^tJjr zeZ9Ux->7fWH|tyUt@<{7yS_u;sqfNb_1*d&eXqVx->)Cg59){X!}<~ZsD4a8uAk6P z>ZkP6`WgMKeojBHU(he=m-Nf}75%DyO~0<+&~NIu^xOI!{jPpbzpp>gAL@_v$NCff zss2oVuD{S<>aX;_^w;_u{jL5^f3JVgKkA?K&-xertNu-o)4v-&24>&}VUPx8&<10$ z250bwV2Flf$cAF5hGyu7VVH(x*oI?3eMJC|^EHwg$&D0-pW$z$Gy;rNMrtFC5oiP% zX^nJ7un}UUH!>I*jZ8*nBa4yM$Yx|WLX8|on32=SW#l&U7hrQQoLvR5U6Xm5nM!gi+O~W>hz77&VPrMs1^xQP-$v z)HfO!4UI-dW21@D)M#cjH(D5xMwHRgXl1lE+8Aw(c1C-ngVE9GWOO#V7+sBSMt7r! z(bMQ<^fvk!(MDgRpV8kKU<@<{8H0@>#!zFJG29qoj5J0Wqm41fSYw1XCFsgMl+L{+00^QHM5!7%}_Ik8D{1*a{*Le9y70*&&+QYFvHD)W+Ah% zS;Q=A7Bh>RCCrj$DYLX$#w=@=Gs~M5%!+0uv$9#mj4-R3)y(Q<4YQ_M%dBnIG3%Q3 z%=%^nv!U6@Y-~0$o0`qc=4J~s(u^`&nyt*%W*f7u+0JZlb}&1doy^W=7qhF`&FpUW zFngN4%-&`nGurHH_A~pN1I&TuAak%e#2ji4Gl!ca%#r3ObF?|e9BYm<$D0$(7;~aI z$((FXF{hf-%<1L~bEY}VoNdlA=bH1(`Q`$1p}ELhY%VdEn#;`P<_dGAxyoE^t})k| z>&*4$26Lmi$=qyiF}IrA%j5T+gd(6G&K6Af$z&vOkG7pSwIxE-;vC>-^tc+GBE3=iw%4%h^vRk264lB&cY2|{77kR9_Rz54gRlo|j3R;D% z!d4Nhs8!4=Zk4b~TBWSgRvD|TRn97JRj?{rm8{BE6)VE3YE`qUTQ#hjRxPWxRmZAp z)wAkb4XlP%Bdf91#A<3avzl8itVk=$YH78yT3cqqpZ=^7;CIG&Khq`uwtx< z)+B4PHN~20O|zz3Gpw1`ENiwk$C_)+v*ue1tcBJhYq7P&T52t`mRl>VmDVb2wYA1t zYpt`^TN|v6)+TGSwZ+!NkZx@=vsu3Fcu>(&kHrgh7@ZQZf%TKBB`)&uLI^~ic` zJ+Yoz&#dRx3+tuz%KFQCZN0JHTJNm))(7jO^~w5deX+h;->f+6yX|9RHf|F(X;U_B zGd62;Hg5~IXiK(iE4FHDwr(3x_1>~=z)^JVBz98U*G^_9w^P`Dw!fXy4zN?%sqHiX zju>R8wbKDSTZoGufH#EKnIVo1NVbwR6~Ec1}B&o!ic1=e6_M`RxLBxLwdL zWEZxJ*hTGPc5%CeUD7UPm$u8;W$kiydAovL(XM1ywyW3?c2&EYUEQu>*R*Tdwe31~ zUAvxL-)>+xv>Vxt?Iw0pyP4hGZed5-QFcqamEGEIW4E>2+3oEPc1OFD-P!J9ceT6O z-R&NBPrH}h+wNmW+kNeRc7J<-Jbd!9YtUSKb@7uk#LCH7K#nZ4XzVXw4T*{kg} z_F8+Lz24qnZ?rero9!+3R(qSh-QHpEw0GID_HKKRz1QAn@3#-w2kk@lVf%=E)IMe( zw@=t7?Njz?`;2|oK4+h|FW49DOZH{^ihb3-W?#2&*f;H4_HFx)eb>Hc-?tyw5A8?x zWBZBy)P80^w_n&V?N|0+_G|l%{nmbGzqdcwAMH=}XZwr&)&6G3+20)>2Xk8og_|D$Ja^bBzICcevZGB(g|=U5J+-c!NI#EtbrlhfJh;&gSo zIo+KePEV(o)7$CeL_2+*eolX9fHTk;_oh8mvXPL9yS>dd7RynJk zHO^XRowMHA;B0g@Ih&m=&Q@oev)$R@>~wZHvCeL1kF(d==j?Y5I0v0W&SB?>bJRKJ z9CuDQC!JHyY3Gb{);Z^#cP=;=olDMT=ZbUHx#nDVZa6ocTh49gj&s+!=iGN5I1imi z&SU3^^VE6fJa=9=FP&G;U(Rdijq}!d=e&15I3Jx)&S&R~^VRw0#5vzx9~X0RmvBj! za%q=wS(kGGE6^2P$(3EjRb9>1UBfk9%e7s{b=@RxQrFi_<|cPjxPGp`o6-$%Q@N?# zG;W|9M;mYq_=E zI&NLJo?G8-;5Kv{xsBZ>Zd13J+uUv8M!HdMOShHV+HK>ub=$e^-41R?x0Bo1?c#QI zySd%n9&S&!m)qOz<3_uE-F|L=cYr(49pnynhqy!CVeW8uggeq5<&Jj8xMSUM?s#{C z8{2L+)Ysh;i~H66=Ek|-llUaT{vTs! z8Q4hD9PH!_vMft7?+)$V%aAC!+ufruGczZ$G?uj%oklWjn3eVV zMzZ(v{jk+l4b#)pP~5>o!Ah_itOe^qCD;h6!Ddhkwt{-l2%5omz-|lK4=B65WHgWXz*BY zJGc|v4IU4k2%Zd{3Z4#LDR|}JRf1OyUM+a_;5CBR3|=dE?cjBS*9~4Tc>UlFf;SA_ zD0t)GO@cQK-Yj_Y;4Ol;4Bjeu>)>sIx8=gGw-4SSc*o$Kf_DzyC3x51-GX-y-XmxS zM?oj(25E2{^n#P1AKVL0gF!G1M!`6k1kVK1APZ)}JU9!^gNxuYxF5V{@Ls`t2k#TS zZ}5J>`v)Hod|>cF!3PH)5`1XzVZnz79}#?H@KM1>2OkrBZ18cx#|NJfd}8oP!6yfw z5`1d#X~CxlpAmd!@L9oU2cHvsZt!`*=LcU9d|~iK!50T#5`1a!WxZSZx$*9YGad}HuU!8Zrr5`1g$ZNaw(-w}Lg@Lj=o2j3HXZ}5G=_Xj@^{9y1y z!4C&N68vcJW5JIHKN0+7@KeE02R{@1Z18iz&j-H{{9^D+!7p~{G9M}!_NypKm3C53&Sr8 zzc~Do@Jqul3%@-4itsDLuL{39{F?AnV+@JGWR3x7QPiSQ@Gp9+6E{F(4)!=DR(KKzC7 z7sFo)e>wb>@K?iM3x7TQjqo?a-wJ;_{GIT3!`};kKm3F655qqS|2X`U@K3`(3;#U) zi|{YQzY704{G0G^!@mpvKKzI9AH#nN|2h1Z@L$7!3;#X*kMKXk{|f&*{GafDqnC+Z zHhL&piB_YvXg#V#8&NgdjB3$VRF4`_Gun=JqTOgO+K&#R!{|nIGYXql=8ydeKSL zkM2dM(I6T|qi7sWqGzIMltr^>9-T$!(M5C_-H+ZgdavlcqxXs4H+sM5{i6?vJ}~;A z=!2sVi9R&?u;|01kBB}p`l#rmqmPL`Hu|{eH-iM}-Yvgpf+ko}d>S4CePeNFVW(bq*^AALjg zjnOwn-yD5Q^sUjiMc*EMNA#W1cSYYFeNXhg(f38)AN@e|gV7H~KOFr?^rO*_ML!<> zMD&x<;(a%LcAN@k~i_tGdza0Ha^sCXYMZX^XM)aG}Z$-Zy{Z90|(eFjS zAN@h}htVHJe;oZu^rz9EMSmXsMf8`^Uqyc%{Y~_@(ceXXAN@o0kI_Fx{~Y~G^smvs zMgJcCNA#c3e?|Wt{ZI72@yo<78$T4U#H;aIydGELjkp?b#uTI@pI$n#m|pl5Wg^fQT*cgCGkt+my2IMZpE(< zzheAo{8)TDz7yY#ACI4ipNyZ1pN?NCe&zU8;#ZAdEq?X*HR9KdUn_p?_;upfjbAT* z{rC;yH;msXe&hH};x~=oEPnI&E#kL~-zt9V_-*31jo&VQ`}iH=cZ}aDe&_gI;&+YT zEq?d-J>qtJ6nElooW{p-FFuL;@xAyo9>l|V6p!Oc{7gKJvv?NIx@sGwo7XNts6Y)>RKNbIU{4?>-#y=PTeEbXXFUG$V|8o2*@vp|e z7XNzu8}V<(zZL&>{5$dQ#=jT;e*6dVAI5(a|8e{$@t?+j7XNwt7x7=le-;0A{5SF6 z#(x+8ef$scKgRzQ|8x8=@xR9Z7XN$vAMt<2{}umt{6F#kCNGn`Z1PaDlB_0c$$C;r zHj-+xnbeZ4q@FaAX0n~^B)iF8vY#9zhsllPW)dV}5+!kxB)5`>lSh*0B+pHrmpngt zLGr@nMahekmn1JuUM_k0q?Nov@`}l$$z#dw4*GOJ7d9CENlh;XJH+j9}^^-S9-Y|Ki@ z$ur3`$&y(zPtKC_cX$`Wn|xmK z`N7Ld{^?_$@e7Rn|xpL{mBm`KbZVb^25oGBtM$`Sn}h^Pb5E?{8aMO$$THTk`M8e+8(JexJ!{b>s? z>~4RCOLaQk1U_oxBt0H&kem$Kqjbv>EjqQ;X=k(6SsOR*(QK`arZBEAnnmih`yQ%cI+8!R^UOwuiq$Y#@aX+1|1D=ehmG-DR9rwE%=jo9j zQ-^3}vw62aUTaVIpj-OCb=)6y(`nTK{c+~e{mx)K?|L9>A2Tjn;!oacpQqV)n66SG ztH>wmpQQ)w>9jrSrLCiBf7ELokEiGDY4>muI~%v~Rv*ps!dW`%wvO7JQz^V}g%wL# z$*6)HpR*1IJ1%uJp3TO?)*wBe?Ji)Z{oaY8It)~MlBH5_)0OIu&qv!uvNb#DcTS~$ zv@Uskr$M*nKGCh~(Kg*5Ud}#AFXUTVez{e?!sMD(OK7X*JC|Ne_;#g^%bWT*VDB) zsgFf_C8V`iZD(1ZO>Z=Ns6E?k&t~n;$uJ$wHjl>RQ?{k7-`Qx-WsUVOYMPi&2IF?O zD(NYqBhEM4XRTR(m`+!Z+DDh0vc8Vy(;jPIlG&u)NgI;lC8ud7l_)qmtsJq)$K7hA{TU4EK0O}^M2j=RWk?tFxAoYg zdGdCVyi+9a7Rkqpqv=A+XQ z8{XPc$^=?F9`LPd`oEP9C$r1FQrf0o-QoQ6wvT9{b(D53d-W(yr?o}IV6bsSL=rt$ zUBVr4RRyG{E+xmsj^gy-@Q>J3Og2c<360O%ZjN`R<1AY}>Yt{KVPAkT`{5A{Z1Qj1 zkx(ny%29t@}cL$GH!Bgc3389)^21c{glmqnjSOFPqvQ+978Eo8qIMK zrRzsfRo!gsXfUS6NjjLM(``8&Igf>uXr+)8)uX{Yg?^fyH1kxeEobXa0YiO^paAOy zhSjlKKv_DQ(fzE1nsJO~$Ff8RbHQu&^Lz;q`bf-H#Qix-GJl*jCR89Xr#6vqT+#hBAZd&vCvYA_Y z#HjEB+6Y~dV%DZ^@&;tH%Ry@0;6$K@T1NKM+z(Q2W_Pbr6+5ermyZ+kI zbUYsNf1aJJ9L;+T=*0narBFMpjtv1B*AaQydWY>G?XGs(rzw>1Y1$gJFVkt-b%38A zn~aJqG!BOik}RC|v|$N1uL{tS&3P{LWao4z2aS-vaRuYXvYz8*N;PxHO`almmR`(? zXU@jm_GOjAgVqo_S^7&V<&>+rWIxN&uGAi|0d!J!#;z-rvA}249CmaPRL__-5TWCX zdY+QqjHN%8tZqL<>L04qjnd@ zz_%(D<#WMl`Q0(?(uHg`kPb8c92%1o)^DAi@UmqzR5Qo(I#4=|0`4-anx(?B@JiXN z-_zZqw1D#AN+{cfYaLnk$9=lN7d#R6F@u>J52`{f!1qi$+m=e(GF?Wg&w>hSx~vmO zv~h3R9&S3gRA|yq*K}y81fwqdUSn~n4i-VrI3(9%Z9or~a{V~d9iAZ{LH6IYTNHD! zF(pgIJlYz+v7GH&FX6p#zWAqOE|eXr@1n8%Q!h7_Ls`(Zn?9BoKNIgzPa;J4oCxa%h8&AVpncHS?`?8 z;e@vxi{8pknL{$oRy+L}+tHvu(T?xrDU-Q0OBj(C?3du2IlY3EzLeVXU!_shl})kK zG#%}nu1oghvab8EjVpZfGp4@$mkpAh_>{G} z7!UFgd2MmF-b@1=B)YsD&GvH4rL6O5Rummx_(XGgs?Si{+pV$+n_7VBi-| z(k#tff3uko-Bry*9FW)4c?y}jLt#go{?{fr z9ap@;Hj<2z$IG-oSb(mS(fON~QOL^Nw3>GOSVrCv|FQS2V3|VQ&5_fw2xl1%zKQD? zu@pVZn#`Z|%pCq$SOMQQ!|v)h;>6GlkVDII%%3lSm5@=B&*FYF$Q*(B=DuO@qij4# zp%JD?g}jt>*zON#o0yM?<2vJsOg|6I*%y4e%PiHyjG=b9v@A!PhqCrr%EWM40?3K9 zA&4pEw3sAhh0`sE^{bc(GCu7~uMyg&)0NKne6Ito*rz+vQQ!Nxj9dObH_MqKQm|U9 zuy&KG{6k!rrVUNOv2-akN$Uzu##4IPXIqkDPQJcLI?LqT&ZLe{wtACn4e)%l+L<5m z_WZLaRTU6#o{8+8Bc={_J4ANRD?$ry zwvSpfS#hJ@dbi(6TaUS)+wSL%`?>3W9(O-aYvPr(vlQXd@Yp_u{+GXGbhhrRtcHfC@^p(>4pQx)3sFgn@#=S5=lbcC!IvcX9yXw zjlu@Tx!4yEas(R&!XQ`UFvrRVzXpARwB^!D-^@#o(m9JhFHbl8(QOxxC)w-fv#x<_uf@a zi!REQX9I+1S7d)E1LF$FG;`0f^TZWbvDB6p_p{6a_a;4gk^?5)V*#34Y$98a?nZk{ zu#`D)I0S^eid!aNT_5DYa8K1;#351U`T>xXG~w_+HIe?ilzilMAWj(> z8X*gP@1=}@j-Zg<6p)p%Dqc3jV9D22dX*!lk&1vVIW#3?ZGi^ zp?PNNi^hS+I6x&P>&FA;{H6TxiPrT@W_;vmMq21-pr!|H*WL4QZ#<@>t?m@T4(dv+ z;-``Wbf5AW%KFHTR`Uos>#Sv)Uk8?F9IZBYV;Sp5(YonDYgi7Ny078|SRQh$#Om>Q z%$(w%Yy~pXk~E<#z)V!vyrfGo-pr)-fX_mAJq}6rFr86m%I3mpM;P)Jn*j4>e*q`T z@&57U&H_?6%H0KwvbgkLTN9-hebO{m1?{QTb}RtXQ#z;Fd^H~L9&Du?PPx{FQX;6e zr@AuwRNhIOI!O`XOVkP z&InS#t#F5&K)=kkeF+$jHX^FYi4FZTbIzp}tZ&AS>nddZ`+1A9X~D{#Ovm%yNo)nm zNSmqlglvA4b=c|hDV5DR!yL+FF~|2hZkMJEecGKK6R40$@00khXQ z=bfOLub>=zU~^GR&17$~FXJ#{QSFLx!5W>K4=el^vjns&=bT&9eo2VA?!Zltg^;cL zq-$aG7{NMki{(bMNVep#-#DI+lqjwAkl{|+-KPJwA*}V<8C0(xIjboA(zRZD4$;#%! z5VBZOPNywj+?^gCP%_$b^j)l%qLw?l+;gmi)7kn#Z5H~;S=t_EHQyFSN0&uDNkH9StuaGFb&78MoA zPcJu4kXFGx#4($m^rwuaVS8@@bqp%a z{44kepLC}?3x$LPxUqnlLF=~QhLP7V;Ln(<@B|HqwD((g+`)E##0>HWh!BOfKEfP1 zVd3caN@*L9D(YjJql+~@lwqS{;qo-)`PjCxqnW1DdS5Ll*>?c5$~}w|M(O41z5cYl zaW6aWcf07YPSYNLmz(?v=frZVO43PMBqfxf<~lO4COD(>Nb5GZYY8Qe)i9@?qY#2+ zjRKNGdn*T7Rm~ijrn5P-JBN`0ZRdbZ_tSB=zc#?!a8TF(HWDjH+-m&sx@yGGkhI8A z9H?1P&bl1P>dpBytq$V1>4b)IIJ+YiUGB+jICy+72k*7d;8KN3*vfH}lS#7xE@x;0 zk;n4gJ17V_fhO%s;Vn%JiIpFRZv$4beP1Z2Y5#bTHgj+y%DBOP{>}^l3>AEMmhQ+$ zM}F2G+6%7_+8oU%{Gj&EHrf&``Nep=&EMN;q~b^q(u$d*C!;`{pcUU6g#er4F6Z1U$6o70^l>1V}W31gL* zB340OzFbKxX)u3aC#Uy2`Q($GUG@<=g#HxGxv0<@j^gPsEF!V2vAyW6Ppx^MalBfeYY*yPzOp^RoI7xOdsuvDzH%BqSMn=MkY^ zALS!p4PYlvwfsPDIgFb~n+_0hVzWr9T!p7#AJLL5H|_-}UI-!$ zD3!?iP|9qmC)*|DIh0bmK5BD%q;>bxnxtdI5}Tv;SzoLW$D{R8nsVSZvLUn?%s-^%E?Q{k^QYfozO>j!LogpBN7wkmzzavgU&N>u^b z1a|V2jfcyVsdz|UU(Gh?7E}@>Ba35Dfd^)BcIy7O^{Djb)&i7&U~BbYG214&VINy) zLZJsY;j)*TxV_MXLJw}jWiK~zXQ2s&9^8b>UT)&9G?BloLN)~X2t6)k9^A0YUT#>i zc@wT-+l1{2`4@R&;YAdB@bg^u^7EctXhNX}H{r6En|NxW356cqgv(xT;^~DZ6nbzI zF55S;Yg6lKr3|H==UqizX)L^^jBl|zLQA4PFBNY=(Oj~msE@m&@u@JAqyA}sJKsoU zzd*aTGVTxdL|=m{j#FUB%GIr44UVbTc0071-a@%6r(6>eY747EM3;G*RlhFyes_%@ zNM!5`VF!>xS;CfoqfENbCoAw#6(nG%9Clm&SC7X{1Ll*aCGCt@f*r5ar5V(#Fwp+D z)$X2&eO`A>oGjX8kGf;J#xFm^HsxHRB@%NqE>Vss)P6RLgFMgFdX`hvI>&~v1^ElJ zXWv_QLNO%GwSgGxGC*RSLx-lP@RxLtlZBU;^TqA>`(?lyt2?Bb%`8AxW@XYoU*S8f z^VdO*=YC|K?G{NZT=OU)>#QV`q9Xm{)9rE$ii}}`E|yo4jd@K^_@+DObIazsgS`lC z%R#~o%_lyK*#dig+HhIoKEO%b+jO8jzG}&yuRc`EO<#f0>>;|aJbm-7&of%WH+DAe zPac}|*C%~>>6J;pw~9=0Kw?zkPbPhp-xMuJn%47_6c#)OCU5)P2Qwiyc59RV{rl}z z5%FxxKWV`7YKm}Qa<7!O{7uE_TO7J1CYc@SW{TIhHvi>Ek@xP}-V4(l@9j*SENozOv`g@w}jQd`oejeA>WZA34#eck`U{ z7vsq>YYm|x>Hxn^Hzp7ZqRAkEwLTtdt0V@wYamOm0s-$aLEbbeY3+8(fDS@M6=e7W`rp=_ZrzefQ}06{AB!!}c!U7ROOvV~ zMQsjon5TF{5%I1~=7TILT&#)*PtD6D|#ZEIuBJmwBg5OLq%B_5?k^l+WL< zh&?R~d)@!GRM=h>hK25^mRQ10Qhdh_hh}DFOhj=LJ z4jNP6LS?&XF2k}kAK`AXI;9s|^3S@xj-43OZigicJIHjhjGD&?N4Hl> zTRyF54SnE#8L$$#d?InjP#Z8?9;R~`!HwyB)Dl(xYQ|VqGdeP0 zqNO`|%9>o$(QN2{F%3u3J|3<+L+7TO&@Jr>)4wM0xt{R^{#H?qE&TnZqAl68AIiF` zc^7r@M+Bq)4*ILj-P!zV{YT9LvM@V1EI} z)R}XZySmIRL>6heuAyArjV#58iM2bKh z0-)-YMX}|6oCJf)g0t4?UKy16NqdTtsp^xmjn;kI?b=NbS#Pni>tS!F)(o47*(Uz{ z6^UC~*Z=m3*PSVm`8hFVWN0=rB-ti; z1km%5+e6?&00-#}{V!UD`EUe<0*!?zE1llFia)}dXPWLUWVqd8cL4|6F+16GC^5>d zxg-=sk!bK|HBA`2AK>5sfem74O`u^2f=9 zCH0o1Ocp?wSYqze(=0YdEM7yW{BC}p+azc?b+>e<|?RgNg9K$fD8CyYnUKPhNjh9eY7cy&f!$FTd zT5*XxJ3hN`m)LZe47yZX(dkgf0ZKStw=8xRAl+$w*pV#@KO$3L(z5%b9gA{mtfP|78&tF$$qoLp|XAA)Oew((5C zSrG&M?sVvLJ!qwPAB=!0%YWpt11Q(&<=zZh0QHX0aaM5KvNpRXY&fv8Q+=lhWs+L?QKg~ zcUCzI$6NBxzUHB`)+dSsgmAGz8(JjxC3J-yU5!5^B8uO#ShpRCg)2`fIXtw~*u^ZrgNeahJlS7Bpr*qCpwBLhb-;XigWK zQQhY^Ja~yaE;YpD#nokd?aB){ya+~IE4a_X54lG@gSm!BW5UKU;tHRq=_#EarsMS^ z2wq|X2w#nEnT{j)2DUz zIP{RVu=DU4LLHnPVf2RZ^L&O$-DB9EFbrEcZ;#gS zRv|u;{uiSK$WsqE6A8)3s$KufE71Zl!aSAjZF*=voHVfRkfTX9zR_Y3k2$df&fDHNe&GVBq zg`^hmV2EN76CL$Mq_eBWG(t0ogjo%u!$lNAPUe|%05h6_smV*@2JSQV7BhH_(dFtn z>a7j=C!cMLffnnwF;ank^&AQQgxS>Ik~e~=W0147UIw~N4-Fxwh6p-xxIa?3=JQ2d z-|%U~A3`SY6v_dYkyVAt6`@)YAWY1|kWI~2#gKCSTz0h+bu2?Fm3y+4oKP;1+5|PiA!%O2%zJ#+rf$a5#k*k+dVO7xz8!nGEN8GI+7W8d}1pMReEBJ!Egg|gYSnPDn( zb`e}uc73Rvrc5@>c{pr6Dc~ZN2!Th~4NlJSg~q4IkO^a^ITY2AKe&n4py3Z>G3Y>> zj-(9O+Y?-1gr1s$YDqXh5|*pHnbV4ut%%PbFdng!OO?u*6P%Ek4dz~xAd@ovEAJHJ58 zZ=9T4!+_nq2Cox9M^l+oCgeIoC6?}uVuynNR3WBD)#L1tZn_gzB=F9C=>*HbbhIaa z9cJs{Kdz542IZsd*OoJX=jdk25{bvzldcog1>5tR<@!pB_(oQ-E1lpQt!ko{`th1t zq#`kburaUeiZdzIEk8jDnwedta27EG|0~%d#!enoR|m_gns~&w>*VSW$aDs$X6?UJ z6QK?EZnAr_im@@yjeTu-IN$YqXt=Q38yLOC;7R zQ*fd+NLfxT1aK;7V|(ePtFt+dmXHO+06E>Z9n;1(4`J!IQ2oAXE4MWiGtEQ!W@Q}P z7@zhkdIU>1I9Mu;#)x&kzl4SM!#+%oOk}PZF=dMcx z1{CW~*S+P=Q0Gx`=l}{xlq4K>ojkDV(8?x=e5GE3@h5Q5kfU(H(AZbTR1{s2d)Rid z)4CHDTh-hyQ8rmIL*xX{&H4oAjOtXLSrZ2}PC-#u?UmBjQ49!R^AQ3&<+&cWUJWiS zp+)sZuV`0i^x#7D59|^60ZeB*POIP-isi1QFFy46ybHJw{tNDw5a zF?7^H4XsV9-RS}MKRM$jNM_+<+> ze3=b(G;#Q7!oap5rdEicgRKIVE;o!d%K0}MfCnoWE8)Q$ZiAhqb;*0xRO>7%gAO8Q z*+S)5Px0v2NCs~9;5FZ_o=ALJ_R(nt!9y2)|C$>5>`vL3#fhmnV73iq+kedq8iYDg z0kV=VXmd~>tPu~R-z7+Hn`pdniR1feUE)~^YtCH8L4dvq6>R6@5yWH>cFaO|Skx@K zNUY#I1d6lsz2O%@zES$NT(}9RsU^q)jJMb}!5Xj!V=Tg$%6OxVf}EFBzK%uHQ8>eQ zWk>K$7VhoPDDZ@NhPjSPRpTVsC<)M@#HiosyclQ)Cbioa64{2(IkotQY@3MM+z4}9 zUPDN4S4Z>HbkmtqFz5mU6~ME3A?D>GWG2CtF`HvL*5+oye9kUM=Nq{3z6p3 zE8B&%xTUcsov>9r;6><)JZw$;xh~h|6F4!LSdnM6yqJlQ#yyGfxW-I6x?DvLa=FTp zKiPpXaOTrCqD{pV!tuJ=j@qX@Sb(e!CNb#+dQJ@tXQtwZI%1j@ z#4^hhK`VTYcJ3a*tMjN$$}u`nNGAcC4pd@EU{CSUSAY#jVs_)~6w(jrzDy+OwwXBk zPW<`qkCalNC(*mrmFKrtfmxE^JqBt(7h%MWU(u2G!6A5RU6_IsdA>Zc<~R_2*BYCJ zwbImd!71&R!v(FcwBLMe0r_bwfuwCxD?fnPw|$E9w6ZSV=X1R$ zj;idl`lF#Io+FgaGWRMhQNG-b<2i*Z+f=pf7y(U+SD5VG;(4x0NO5KJ@NSW`H`z8H zGMhDcg^f?~AVWpUC~e?RgzRK?S@^af?!?F=-}uEL*BMN37>JLGRKpkuh>GAYqNCEqxcLQ3pdLsiQGwSJ!R3bw=CMT3|&ceM_hT@VH*gAViAO7!w{GN zLT66-fnFG3q{q>!^WLc;oh_T6sA@!}7B7&an0%?sJ-0W*oQNfby^_ql89EISj^)EQ z<=Dd5py_`NkGT(rS4! ziS5>AxXZ|U=ZpY7v{G<&6v~*zvRndy9C*-CV?p zvBkB8bx3Y47FHZH#j0$Z!GcNBQSgf!&d>wfCg1>*9KTz$|~HA04KKa&K)7dbndK9v3tat4}C*gdf& zuAo9k1D(~&!q&OpzGYc3aR%MJh0UIj!px@V8l24lU*DewdHozN)nC9_{q4o$W1unf z&0$`9BrW(T;&#ZmZdns`*My!pc%v>rQaQMSv6>=CLzV6*UC#~g_84y&cprEX$BrDu zj);wtjMWlKUTx1Rx>DNL?M2*tf`7Yfon{a7^!d2-v}PGiG2zS*@L}sCLciK6@=%?7 z5TR^aL>aj#Nwkp`2{E9$6(kI5t*X)`3*zB*pQiH@5^J8lsF40FNv_#yW#dM!B<4&1 zALaBYe&PS!o>D#1V zTVAcE=bB{cd{Q^hWbG#q{ zMTNAGbBno$K)$={Fiw)?P#Z=_N^Cc!C~ch)`b?x7KH7{IEI*bDmW?ftF*2WjIx8_< zIu$vr>Bpw33OJq8*%}+zz4o?s*B){}ZM*^ek!e^g5JhLqi6DXyos(oLxy9(v%YgLQQ(gzF zLOtN_jp3^Xd*xhI@T@3_0U!YK4Wp1M&Yb>kwNAAyupF>u#z^&|>BJHmvJ&?`);qz z%jvNAEjdTdCm!R(!rIs(Mw;?>q4+Y|kUwlZYCduL6`2_t+g%NIm8RZ5TBFPzg8)t#&%IvzlA8-7z#Rv4mwsYuYH3@|yN+8F8K229RqIJX2F% z&F=w76(2rUY+aaPP67&XlS!~m3G=B|V5{Qxz#eJDzs<5Dykci`SpXP27QnqpDd%(w zYiP0{Swls+*?AZFg^R)H^)CcTB$B3D_Ym-KgdqEHDd3^sE%q1U9(rUAQWh~8^3qXn zgI$ChjJTRab;vq(I8sqpu`Kc8AqQ>M^pq=$W~;~ZbWri9xCJ_CL(X0@AF~``SD%!_ zj<6jxMUBO}Q*MlHtD)*F3^$?hd5Y6+No+EwgmghaoNPw|7`U(my>TJ1n^p(R0FIp@ ziU*d0gg%3;I9dAB6H^=Cwt2}*cIq*jeQg@lgSjC$O*vTidE$4A^B*{Y#x z`-pUrfNyZPsWra%;%dUad^4v)-S-9-g?>vrqW zWz?P4W6P+=ucDp=RWj=IQ57@oVD(}+nIxtYvcpdu%^f*%ua20d21aVi&LkJv($E%S+I%Y5?LGM{?3%%>lH_`!pqwE1%$obOk1g_~nKco^;! z*LV1w(#-+(0g0>*Z#u>1#hEFXjG2(nC9SNlZ)OBuS+Lq2kEb>H=S+9HRZR05Hgw}9WYPI) zn!)&Hr%qPv=j*2Kp-OUBILbtYvU)Nf%&S6{iq2wKRU2I)5!X*SF=uIoE9ou?K)fOB zl<1nh%ccuJgG5=x@tlHK;EUsmc&oq~`ko>4;NVfSD%7vyUV%aZCpXN+g_DC2!Y+Qt z0pnvi=fxyDLoFcLsQ<;Po)m2ZWHK_CdjlnV6YQ!ACz&-0KCTN-*y>$(jE4F#9>!8)7(RE+~{o` zjj~ufn-dM8f{sW|(}P00NDpMYMIAS2nAe@V&o2kvp^|SXYVwLUScxikcG$32M9M85 zEK5Xap_RF@lw-`1$9uxEC@MMaYd=+r`!Q4;C*&2gPp)CW1X$cW#2iSHYJTYPs6d>XOJQ zHeDbc`NS1eSCf-x275~ZR1ZXn}S3k4*_xIu~oAbakncywlL z=G@NbS!TaeZ!d2RP(l`cMMke8Bo9`cW&_Uj=yNM3GSKNvtd{(G+;@65)-AoZ&*_oQ za@dfjk#klNxvec%pFl41dilPirw6y0qLEx{kBISo^-8_RuDJs5@oR8TK6>-YVePjA zZjggnl}qb({l#5uCDvEGt5wVF! z+P-Ydrq^)sGi>82Ob!O~wvz;9oFp?B=c8m8;>b&fkj2xmC>gl8!7w)_%>267_BCu`8I{S1@;OEZ6b_ zxx%FmH{A>pc;*>)P5pvU31HY@xU1H#`n75$JB(FP%&hRbdS#l}pdumRbgPPx zgIxk4ia%BTS~Q`Dl13_ZASgOgIW{AY3To{kNd5zqMHXtx5l=ZZ#q^XX6o>CFkCL=P(t_`xl&3HW+>QYh; zeU1!*K$h5bjBR0F;c$^q_lXzQ?W+aU%BY>~`qd_^KfEA=_1Mj;D0j?VEqnWF+1n2) zd*^D|I}a**_iEX@uI&Dm&Oy$`*CA~VTy4s)E*FJF{fFyvE@`+C`Orkj6gwY{-hd66 zqt666vVx<_^=bPE#%jN;3AM?$U4$xt1jnM8=mTm!BF~D{ zf|c*f^0ST3-e=Ftgv%=d{Uc}D6}L2dmiP~=o3XU*84X8ZskMif)Cf#Fp!L=~_H1FaxJ_~96ja?QQ6}9vkZYM#z~J{2yqQGYpxcS( zI+(KJYI0wV#%dyk@Jg%gQd*-f1Z9MCSn%f+t{-@j6jS?P3HEbp87m8Esp{>es(za9 zFIK&+LZhXscb2O9S-({E&UIDqE>*Qfu=v!wS~Xfk>Xu-0CS1(ZjlzG*oy9D^b#Mh^ z%`ElCx6mZYf=nzcRa4j*83^yoaqmmYxuilV?QkwJf_BSid45=VV*y_?zw;g<9i3wh zVJ0jSrUiu1bLsKX4CgwZp>#|m_r?$b!R4Xj*Yw`Yk};mwN;tG}aa<@(7#hc1yS^EkT;B&bkia4>W<-aOfF)}F}8c; zIbK6ryzo|+uRFw)pnW=R7gv^g-mN&(oiPJyLp$>!^GXzeOvh3tQ>g@T#+Pox#Li;W zmHVwSZn=_r{-Qp8J+E$XkC$us#NgW5ocL*k>GiZbK5KK`)}^}?M|@!9`d8F%ki()1 z+jI;m`j1Y*jylGmfF1Wx6-rR0=QWPlj!rYSIcHSnyHN&g9c)N-5u0pTLiiWQfSM7K zyOG79yrxjkB0WIdr3w@G6uY!z@og8bNKl%Or=W_!1sU>i(}z7j?h_#&-iT$TSMME^ z0Z0~i)1C%J7uC`}%#II=UYZEX0hz+23|Pk>8V?;0tA~xFY=jE0d*Pb5Npg@Mx-wwx z)Pq9cP>7^*@e=(&#Hh97DpdjcZO7~&6y==qt=k$vMY5AL7&7A4zyn_n{DYjgd&jyJOhu|3f0hW6c* zS;ZH;()}y1mALLH{(+spn&EpUlj};8o{-4%^liOynjo*X<|W8QrdIJ-Z?a}D7`hIF zz4DIh@@>bnhWoJ!l_PE$#09#z*Af!PBU?nPO@v$dO zxn7;aqwvAd&AyNe$ubd}6XC?u{d`3@af0@pOQnnJhGn_QEy?-)!-B9!<|X}D7bOj0 zCG$&-ZICZ;YjKU{7%MB*EBZJc;S0Gbx&&IOx=BT0v*yDs4(h8jvB49{mLM07cn~An9jHyG z*f8->a0O$1T0@s|oHq17D+K)w*B0!;JrISW%;R7Q?@;b#5y+sCM=uXyRwOZdR}z6h ze;ljMb3)aoh7-`=3xG`KYj%*khISlXHI;IBkbybittcqiZI)3gWpD>;<0M-4Oec2(*I zDidWldwR~^Mz-z`Sy$y?0kSe%9;?f&iWiMKb!R(yDlh1CpiXKXTnV{k#SWHo+{aou zJIOGUmIKn?v9he0^dBQGj7Aq6pOFA^$>ABIU~Df%tmoP-#dr|=`0=?2K~)3$(xRaU z+0dF{!-m9O;1i#25zACQ17Ed2Uc=)HyCL1-$D{gDn=3C=)`An!_5#!r9ez1mdeg6rDMZ|-Gq8WPlztn_L}`NU=KemG?==9<$Y2{(Bz^>>)oD;kUQUxMc|zm z_xbM4^Q*2GiF+7HWuo*ZYLT_G!0R5Tboy+yI~cV^T?qlV(jE7>Fqa#QnRU&Q>6mOO zn~o8cKw}d+z(S2wHJ>Pp3PYRG+!9k2nW?M`*iSKwa8uZ;Q(> zw@}N5tlJV!jUdLTUNmSBZoLq|yNcALeGiYa+qLBESwhLa=bF#&;F?dwybZafNjMJ| zLF*OFA~)DD2U4#Abk55hP?vj7CFl|E$6q<+alQU@;NymHcEibx2?8OvR3V@QlR5+2by0V_ToW;{q0`SUdcoRvc@Z`2<=h|yb)Q*W-3IR+9*kk5u2m;rm+cr0& z5b`uPY!*k`369fMVuIpwBO$BN6L6-x5S9=G z+sXx%C<7mLQRx%6C%YX4BNZ&!bDg6^eQ_@*DV%c=b&IgrrED~6rRZ%N7PYN;AEjKI z@RA%;Q~VLO44!fkUflyUhat^&4sqUzRjFn7MA6*e^x1=SY>nu>L{YG)78_XHRs03^3VDWd*N}QgBt4#E2<<;xdCmOP|t>4Xt#RP~6u745R$tKl_ltryZ)C3b{LgwYVH<2J??U95{y zDNBW#;O#~5&LVhs5qx|Rd}0xNauIwA>Kp$#EC^Z<{n)k@fDLH~Q7ZaK zQEW0A(rwFXtaPBCI&@8f2stLNR+uk6hbMkQyj9F_7c1a0U?-?2lzLT1k7o zP5xREyNM!>Zr9fLN@?rBL(ssgLC1e|FxV}UR={iJQL@nZPEPXMCl0P)tfEtt3S~{S z7!^XQ=nQFN0a#h?4OX3LDL{ozE^hX8zE0>0(7hYv2>6>hs6JH%A)0$dVcUgaiIJnL z5}geXSLTO?ozJgC%-oDwf^RRucb4G0OYq}M@DsRFaP^}2d|JX@7jtUXl&;e}WqEan z@ss{w0kSfOxs~>Gj*CBZyfEDv28SB<4#^|3Fs#)dUeNblmD>?iOjL))!+Pf$dy0=` zFGjKPU6=5rgc4k>&V2P2y{@l@t=cA+58ZZ0FN zsU6KAB0S#Xu4r%tV`VGZdVcj|vL|2y-)bN_qhf6x8zh5x3D46Q90Us;joE93 zM&%?vJuj6xE2Ym%>5EcY?@aM^?+cz|axmx;;RB|Q6dPY27_HE*iOPuUD{{WcsiyGR zDGAZs(vGgd*_6x6t3YC@dyTC>m&qY#bH#I+T#m7_SdWTzF=8MT za5{ANz+Y# zKIy$l4h1E`p?qD&e8M7U8~2Vha-nC_y;)8JRm}$Du{?Dw4l)qCm=YY|S`p>sG#kAW&pBF(Q|wuugrZ*of7S+hBXTL?fy z+YJkpmEK#-(&-glgR}Cvfq)-*|1JgPtpt`epp=PK&AY31;)cybotcE-rd2kCmE43` zWgd!k6*3lFt#O^taye6h?ukr461 zNLeX$8Wc-wto={}3wsG)C2oTf_f3&TEv^a)jAREo*h1_OgQx__xVU@QEnhJ9B#q#f z*EB5MDz{)XL=xm49r*`^j0d}z91<^*>upe9GSVCD zZpWhXMzAEN1i3`r52Cj)SQMeGOE?;0>MJ#b78FRNOk#Rneqr;X@a5NnhOoebEI5=YKNZesOjM>66n2b`0Dr@S{W7+p4s;8kPbRujB zUXt$D@UHPcNy52w1vg7OgBrUUut>PQo62XLVfb~@FnzFL{j+;?!6mjfSyV(_M z;F7)erbz7E4zOD!t%V|Do`2eoo(+7Ld}26Jm?Oim_oOdMs8uy7^lD90u7CxHw~fKK ziub>AJZ19MEHM@dJS(a6rLVn?d;un4rH8BqWZasloJb@f*a18X@FVCTMjN>hrxNaDM}yXA;1l1kx?<4F=mj+g3y}4PJNrglaVR92 z-bkgg!V;`!g4|lDOAx#5QAs(Wc&R}zV3$M>!%c!*x94@Vbie_cwJy+nq9u%dB+%Dz zf@S%JEWY{VUdHuMa!t2f2PL& zy;!R_&8gWD(|%4Ya0(Qz4LR0{s{Lbf8m9F9Z`o#HN2Hgk`6@RoJAb4Xz!|>uin$zd zVf(smz+;d`;p>s6mXcX`q$zHI=n9 zoUhqgtZAT?@->waG@P$#L00`U4YX3erm~HO^EK^o@HGvzQog1#nTGQ_oUb`te5Qd`%GXq;-f+Gq z`$&0g4YX3eriuUz=WC7^YZ_>!d`%S?7|z!;hS$HRfmX`bRPlo0e9dPTpJ||#@-unh8J98^HSDE~MGmWAeHTtNscPt&?29uA{P@?SK$R=-$g9oT#g@h?;!4p2InAdnrf>Q6m}3fG2xDz zxRg`GZS_s>E@#ds;Z>A9WFbr%&J{(;vJ#S8Jj|(FM_=-yYjD;oeZcHmM4`DvjXo@+ zth5hzzeGXZfVD1rpIYgHbw;DI@7Z9z&B97-2=goVc;I&K5|&dSJco5NUnWaW-|WIn zpv%XDdqccMQAv#&mcUD}H@!Su1nn9194(j8<3!*p!$Dla5f{MK@)BRUcm{Er`-APg zh7Yq`&j^Sb$j;m&J*Ow9&iiy(Pqs(wl-{JdWS{jjI_NX7C!k+THCI zwlCK9Y9H(V?OgDEB8Lcp@^I`whW}?As^Y+?CIaP<|GIghv&5;iGtOKB!J13pA&Dm{ z?6LUV9Jn)NX%K2Tj1aAqwr(|ZoR@y)!gqgm-dsjl&27uLyCGRem zG{lvXk1v-r#FdgyESEIIm6A^`mo&tcl20v{G{lvXPcN4=gq1w7@qF4FLMhRcT#L0j z$ZMF_C)hY4HS#IT!x?`h@f!Ua-q$l_>1!9aPiY*fLn)&!yb6}8q6lBATAz$zr4fho zpZni(7ZfK3&Os+B(7Wl!EiOe9tv5ayL)>~LGOFHEPvgFmTY58j%bBpsz2GzM$wq?b zvk3{=o^j^S2ns~3?i*h5ZNb;A7#B{cdVGPl$uA-lVNzZ~0IO!2c9jD9CmK!^B36!= z$dDc;CX}4RD;O04TJU7_X=5&==K7$}%l`^Yenzh#v*IG=wmk(AkJ_lT&{S}}tcIo3 z7ph{xIKn-BfESIWtkOCd6#0jq+FSZ$c(<_QiLosKbm7<-P7 zCRx&P`m!aAmGyBZm3;FJdq<2i%1ZkXl(}}(JEi2Xm8%(9#$^^eI_H$)@t+N53KCRo z;TPhh9yqi{HpMPvvG8)d(@o+B$lU^*p1H(oVOgxZOCu^+GRPe<)g$6)A%SW4>MF3L zKLet`^=lHR9w$n%quDE^?eV2>Vg8+hg3!0O^j+O02TEG5B*%_aFJa_DN|Dwn5r`+>9p6ML;D{fM2hg?pDSaiVM594V#5l2UuEFN-CF+@6A1!=Oa0O#! z_njpuAMCh|p$)nl+@s>_kXED6+sJi5g;S|xTy}EHF;jTe(3K3^oV3|#R&~k|5!b8U zL->Q8JY`QH>>^QLk({0jopI+;@8ze`Cj5eMBrALSZF%pEiMVr^O_w@}nN61h*0|9} z()nDmAvb{g0nb(E=&2tu+&Yw)5}_=XMo7u{5O&YjVh+29UbKFFJ!!yh zP>^~I34M3ItxPbkO)3FIrJRW8s_JYKEF&ZW^g47M?oFJB@*_htx}Or?LLoxvbuGu) z39k{anm{Q&{{Lf-61a)=OJG^K0H)>urGE<}39)0tV7YU;CI4*UH^hV)bAEJR`0-~P zlxn+-jpcZ{Qy4EpWJ(05{2-Ij^}`kUb5e(KX$ZMEhyHLa4T=$YZI_bw%>(h9P`;uR zKsqG?Hg^hg%;BGzrMoB~B#s4SEBCo*gy`@p%DUo858<5HZkvgd`v&poBTtlH`PIV~G$rT_29c#%+UTqf)$;_H4My6{m!5 z=ARQZG>fDf)K(RiV}46h7p~Y&8PI|^JjTcQ(ph)TNZeY&%aUU6mKG&@&u8a%3+7Mq z826-dwtEuSEXjps$mOjm_oM>9^d#5+`{n+7pw$2S4`{W(FSWXB4L?u z_kck^6NebwS~`$na!9u?(FoHidLrsxvGBx3U@_uS?ww$JCNo@^Rbnfhu@*MF#3DwQ z2i3Nzd_7b*fr=N7r^g;$L)Sjdz5etN(CH!TN&3EU0OUCmz>F{mdd9h%w{ojuwGYlO zSipn{X(u5zRXV^O)3QZ#OIM3Zhy3L(w$cLlNVS&;qqak z7wS2|k%>Xi@?z|nxThJnFo z4|`Z=KgWGeCs+Xq zdTLCOUv$d5!et{a^#bcJd3_@-<>XDeAa7bj3kobw{{PP2};C}KbVhce8n z99g}H5zC)Nj8F&RLM_4M{BOgui0g)A%n2;jCu!gWmM(3*b5crN1Lv~8wuHS1b!%Ec zS!u5IW(Xniw=H2gMyl)iWu*E7En(z&S#x8!vY}zbHY%a*RDO~h=s8gakxn_T&|I-7 z((>N*=s{^G9Hm6KlSAS$7v@DxEZ6T5{|r}^l3B#6L<8NBf3hFc{bzIILwGu#U>lB7 zEe6OA!uEso)Ly>n_*n_TtTLz75!+d1%%Z_TfQzfyRkd>*r zNQp9FKIL|Ef>g@EmezCGmyA;bDig|=S*&4Dst#mMAG*}g-TjmoxUqRl7hrZQ*j+_7AO@>yDO}tB$5CrfF>X@>bIT#bYx#gE9Go6`P=?i9 zX`g3AkM0R0#VyeI=9dBKFKrjOg0QNDEGuWY8MHwFOpHdwpc#fyR%5=Y1y?ZEQi&9X zTs$chvaD8Q{r>eKIQ8HlDmrRbi^}aFE29?1VoI$VR~cs(3VVwXw~X)K@@VLvh5toMp%@ zU=?QY0c<=C^?8?>s)2u+1*nY?9gRLQrD{&ub`0O2?qI_yl3e``Na_i!!$IgGk(AaY z0gN@2h;Bw))1rXxLq6@!g1UZ0&SrH3cGn>oluxz%xa0(YNGEJUU3_MRgX3xh0t}3f zJCN^s4Wpj|O+l4*Gl?Ijmm1_70S7wTUJY*YFj0|pqlTrkq&M2tad?p-ACPYyjjt(} zhe3M&vt?QfL=xkiVY9u%b)Dn;_XlZk1!HAvmct0ZfpnnVpl^5bl$BeXLcnl#ajCdU zu~6a=!BqZP2@BMWBP5M5vVP#WqZ_U7Iu41s2A z)={*`mg1E*9?}9^a&4V`EI1#0YegD?7mrWbD3|g;VR!()#W-L3 z1>)P?KqjP_^XyS^u`lj?<=skjKKFiA`h2N7Y)R1QW}b2c&y6M6Iv_6ju13p^h73h~ z9nxw@uY8UCC31|7sJP-TG|RC1(j4&KvPv`og`Jf^MHytcj%H<_A<~x!0+96(+(r&@ z`y~E$F+5z<8BH#s=X{s4xna3qY2vRVXqHjkk)nKaF6z!9=jdKnxgu6uI9ap@&hf}t zX_0$a|3tu6FxlGC@G2@VZB2B>=L8ZF-=I2*9(Vh^INh09Q7TdTt(IKjJ-BR||CA3p zS@FVc@G9iC(p4^Nc$d$wVa#jMY+Y9@5mo_jQWYz#BryW zPIVWTQ_QqQi`*(qp6=^SPv?mCNak1w-+?S@G{ZGYV^ZfK(zBfWahz^W);Q35e!~cZ z{FHQ58B_()?43WLm2LP)WT;StlJys~L8t5eZmTl&oPtO7cBFn0U!2)C*+?226I2(>aP0r4!=u^^6)l1>- zbU6xHDo`w#GL)9)ei^Waxjs`bzA;+`-3rZCVPRjiaG81RGNw>|XOV1@6;#&}V#{AD z#0l?5zTv216Fm{OOM-YAXg%4nReClS;)gfl;++n(Lca~SpOs5Oyjwj<|<%Gi#YEHW(&f7U)B}AwuNFCKu+VW`=83X$jdj(n+ zn17xvS52J{5S+P$m8C5ajiBM^r3mQqbdgWowi9>W?jc99be``t$(rNDU7`-?`p8p5 zW;=3lQ2+*|P*VnW%4f_eU;KX+u>Mu= zaj0C%Tkqs4%UhF>bmP!5pK?stXS4!k3qD=9H)Wd=(Ey{ru#gFUm1mf*baV~QT2_U& zZ%B3=xaLx6hq3Y5K<|1gZv7bfokLwC?{Jf&QNS`44$a1lpy#ER$Fw*v-Ps_bF!~SfDsgm{ej<0#lHa=4P$3hT zQvchxz<8}cm~wxzQ!By@P2_wm{17=41Y<|eRBX+Tk8fR3&UGZx7?jP zWlbDpr&yK>HLXS|zTthvdM%z6-T8lgz2|ac$C9r7#iurO07=ldk+wVMA0xI}W@avj z1~h6C-~eQsx%%_GY0501M-ggPWo4OFD3g`efUOy+S57n1#M@oY=3m)`(&CrW)PBv! zjj_wlnG)S6cl*g8pDk@Ye}9^OMLQo=04by%4qsVn9{EQ6_Mw(k;rn6nQXs@cBxpd8g{$7*X9=-BkcaQ)<-SvPRx zZ@iVgOk@iqu34mIQcvM0c!e_6*SE-caZ$Y3jlYS?B^ZkK$m74xs;0hW6N|W2I+PV? z7hNbNErKI_x004@F1)8An5p2j(4`5ydKpD)yd#i&I*?`yUgXa zz0jZH971Com{y)vuW>O`-OOZ^X~oZ3w+qA6brStaI$>!O8qB3l?zdP9WCes|>Z?Cy zT>rpm(}`Q3m_h=dA}R-e+Vy=y%(!X$;KAv`(>RsOkn-cWER5VEb}9pV=HUZJ5KgqB zBH)`MtlW(jZqhj;8@gd}2s9;Bk|(lc$0bykibAj$dq(aLv|G)^B-2c+r$@L7aA)jn zu9p7lu3Gvlm(3pcRT8{kz)2pStw0j@zz)0c@)2j1!(4?S@rwVScmpIVdJv2GHz;H# z`@09gI9UVJI&!w3F1LFb0r2TRp^Jwu7&1z8{$$!*?zEGY@N7~lC8Sd&_PKcaY=A}0 z`x3+9mY7qnJtoUOyen}#nqWKv)z}S+S)9bm9Y3BA8U* z_cljeG=+hs+Cf{M-XXoia~{?3w(g(rEbzJ15!_K1zbR9-Of9g0O4R-A_`kHGt)d@h z+#n9Aud`$r8Ea}#{_^yXwYBX&%n)dtv({U-D=X%D-`{Pb7tSxP|H47n zjx-{YZ}C>z^|R%4%%U)wnWA=&C$Arc#3Mr1N>TjPK&5qFb7O4^xBK;w+Q?1!Lf1&t!IL(Mj3n(;1#;uPr=r z7HuQ|Cb=YcV%KIFyDmXZXdH|G4sY|z{Asx-Q3U?yOIV1F1Z=S5pXZ3zWN1?&q2}oh zCrKQBnzvD9hX>L=fH5rTkCfT$P#eJF*lwI7b1xHOd}*wJc&zMqIlG^1ZCA=K7;!(Z z2j_s?Sxc^&sPn<<41ie!iPB7uaWkwzx9yvWM5PsfG(aOE~d|_Vl>^--A$s*=r(7Cgr_ogT&{9}58o0!j*ve#!E~}&AD44o?ua3bI8cBGw?L4S zI*#(p=3F2SYeL^9l%AMg3oh8izZ1`!>z|J|KF&z0AbJdAZSjoaOqL zl8dA07j4{T_jq#-j=HWAIiU26cIYWR@5667-&76}Nu9lVWSZnaB-B5QjV&f>0^eH~ z=Fi?WmzzFrgTdGKgo#uIs3aHF$oFOz}gLXOz&lY@Dn!zQHyDx)%199P95y_MST? zGY}I`)8(O;cqWIw>p<+erqV}7Rn=Ov_XTeWcs?N)d*rOKDACKM6zrewM)q&EgJt{z z{da;Y>flGqCUQR4{u~IYZ!RVnkBrgwOG*IW+;cV0uHlko+#k7A+kb!*0GHD}PNGTP zVO4o`)0nB+_d9eT-Q|rmsxs~BblX}=#+4k^X4FYU8M3ew*AFvTrNDcE*2Le zXbCaLS%Yh8IbmDkNX=E+Uw^s4<=>9|t3jeyz2zC5uMtDe9wrfn*U~wREq^h7MZ7Sn z$x^9sjJj`!jKl007k8NFIDmW#s;NtV{!luAB0HpJ$LH@Tv{`W!t&q4k_2KoOS`yi>0sIxYcJx^e6 zhA-3v?Hfp~Cqy#Q+qh*CmT!Hraq_KiES=a2xvrZ7AYHRGplt2UJ-)v#Zr!hlezwDW zj<)02afz2(4{YJR1e!&geo~X!;C=(*mt!rStw0iYVwbpCd1ahTj=B|klO$`_t727i+B`BD#7maDL7 z{VAgo^St{liTc7a+zTgrkP?dN)xzqjX+V4O*DGxe`-wL~p(uIC}n1Dr%0D=QdD>Z6>?g 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 ZcAgz$Pp~K2ZnnEU$(~HkLK9l*e*jr~ZsGs{ literal 0 HcmV?d00001 From 3ba9caa1184687611affb4ce5b4b7378a92c98b3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 6 Aug 2024 02:50:36 +0100 Subject: [PATCH 0136/1052] socket: socket::set_sockaddr() for IPv4 addresses in IPv6 builds (#7196) --- esphome/components/socket/socket.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/esphome/components/socket/socket.cpp b/esphome/components/socket/socket.cpp index b200046d7f..5d3528dad8 100644 --- a/esphome/components/socket/socket.cpp +++ b/esphome/components/socket/socket.cpp @@ -19,24 +19,22 @@ std::unique_ptr socket_ip(int type, int protocol) { socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::string &ip_address, uint16_t port) { #if USE_NETWORK_IPV6 - if (addrlen < sizeof(sockaddr_in6)) { - errno = EINVAL; - return 0; - } - auto *server = reinterpret_cast(addr); - memset(server, 0, sizeof(sockaddr_in6)); - server->sin6_family = AF_INET6; - server->sin6_port = htons(port); + if (ip_address.find(':') != std::string::npos) { + if (addrlen < sizeof(sockaddr_in6)) { + errno = EINVAL; + return 0; + } + auto *server = reinterpret_cast(addr); + memset(server, 0, sizeof(sockaddr_in6)); + server->sin6_family = AF_INET6; + server->sin6_port = htons(port); - if (ip_address.find('.') != std::string::npos) { - server->sin6_addr.un.u32_addr[3] = inet_addr(ip_address.c_str()); - } else { ip6_addr_t ip6; inet6_aton(ip_address.c_str(), &ip6); memcpy(server->sin6_addr.un.u32_addr, ip6.addr, sizeof(ip6.addr)); + return sizeof(sockaddr_in6); } - return sizeof(sockaddr_in6); -#else +#endif /* USE_NETWORK_IPV6 */ if (addrlen < sizeof(sockaddr_in)) { errno = EINVAL; return 0; @@ -47,7 +45,6 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri server->sin_addr.s_addr = inet_addr(ip_address.c_str()); server->sin_port = htons(port); return sizeof(sockaddr_in); -#endif /* USE_NETWORK_IPV6 */ } socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port) { From 7074fa06ae98c8f42821269b3fcfcce7f052854e Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Mon, 5 Aug 2024 23:53:52 -0400 Subject: [PATCH 0137/1052] Adds MQTT component to Alarm Control panel component (#7188) --- .../alarm_control_panel/__init__.py | 141 ++++++++++-------- esphome/components/mqtt/__init__.py | 21 +-- .../mqtt/mqtt_alarm_control_panel.cpp | 128 ++++++++++++++++ .../mqtt/mqtt_alarm_control_panel.h | 39 +++++ tests/components/mqtt/common.yaml | 6 + 5 files changed, 260 insertions(+), 75 deletions(-) create mode 100644 esphome/components/mqtt/mqtt_alarm_control_panel.cpp create mode 100644 esphome/components/mqtt/mqtt_alarm_control_panel.h diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index 7ad4358011..8987d708fd 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -1,16 +1,17 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import web_server from esphome import automation from esphome.automation import maybe_simple_id -from esphome.core import CORE, coroutine_with_priority +import esphome.codegen as cg +from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( + CONF_CODE, CONF_ID, + CONF_MQTT_ID, CONF_ON_STATE, CONF_TRIGGER_ID, - CONF_CODE, CONF_WEB_SERVER_ID, ) +from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@grahambrown11", "@hwstar"] @@ -77,67 +78,72 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_( "AlarmControlPanelCondition", automation.Condition ) -ALARM_CONTROL_PANEL_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( - web_server.WEBSERVER_SORTING_SCHEMA -).extend( - { - cv.GenerateID(): cv.declare_id(AlarmControlPanel), - cv.Optional(CONF_ON_STATE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), - } - ), - cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger), - } - ), - cv.Optional(CONF_ON_ARMING): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger), - } - ), - cv.Optional(CONF_ON_PENDING): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger), - } - ), - cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger), - } - ), - cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger), - } - ), - cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger), - } - ), - cv.Optional(CONF_ON_DISARMED): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger), - } - ), - cv.Optional(CONF_ON_CLEARED): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger), - } - ), - cv.Optional(CONF_ON_CHIME): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger), - } - ), - cv.Optional(CONF_ON_READY): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger), - } - ), - } +ALARM_CONTROL_PANEL_SCHEMA = ( + cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) + .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) + .extend( + { + cv.GenerateID(): cv.declare_id(AlarmControlPanel), + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id( + mqtt.MQTTAlarmControlPanelComponent + ), + cv.Optional(CONF_ON_STATE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), + } + ), + cv.Optional(CONF_ON_TRIGGERED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TriggeredTrigger), + } + ), + cv.Optional(CONF_ON_ARMING): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmingTrigger), + } + ), + cv.Optional(CONF_ON_PENDING): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PendingTrigger), + } + ), + cv.Optional(CONF_ON_ARMED_HOME): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedHomeTrigger), + } + ), + cv.Optional(CONF_ON_ARMED_NIGHT): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedNightTrigger), + } + ), + cv.Optional(CONF_ON_ARMED_AWAY): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ArmedAwayTrigger), + } + ), + cv.Optional(CONF_ON_DISARMED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DisarmedTrigger), + } + ), + cv.Optional(CONF_ON_CLEARED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClearedTrigger), + } + ), + cv.Optional(CONF_ON_CHIME): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ChimeTrigger), + } + ), + cv.Optional(CONF_ON_READY): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReadyTrigger), + } + ), + } + ) ) ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id( @@ -192,6 +198,9 @@ async def setup_alarm_control_panel_core_(var, config): if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: web_server_ = await cg.get_variable(webserver_id) web_server.add_entity_to_sorting_list(web_server_, var, config) + if mqtt_id := config.get(CONF_MQTT_ID): + mqtt_ = cg.new_Pvariable(mqtt_id, var) + await mqtt.register_mqtt_component(mqtt_, config) async def register_alarm_control_panel(var, config): diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index f4bd34bfd3..240b407819 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -1,10 +1,11 @@ import re -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition +import esphome.codegen as cg from esphome.components import logger +from esphome.components.esp32 import add_idf_sdkconfig_option +import esphome.config_validation as cv from esphome.const import ( CONF_AVAILABILITY, CONF_BIRTH_MESSAGE, @@ -13,21 +14,21 @@ from esphome.const import ( CONF_CLIENT_CERTIFICATE, CONF_CLIENT_CERTIFICATE_KEY, CONF_CLIENT_ID, - CONF_COMMAND_TOPIC, CONF_COMMAND_RETAIN, + CONF_COMMAND_TOPIC, CONF_DISCOVERY, + CONF_DISCOVERY_OBJECT_ID_GENERATOR, CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_DISCOVERY_UNIQUE_ID_GENERATOR, - CONF_DISCOVERY_OBJECT_ID_GENERATOR, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, CONF_LOG_TOPIC, - CONF_ON_JSON_MESSAGE, - CONF_ON_MESSAGE, CONF_ON_CONNECT, CONF_ON_DISCONNECT, + CONF_ON_JSON_MESSAGE, + CONF_ON_MESSAGE, CONF_PASSWORD, CONF_PAYLOAD, CONF_PAYLOAD_AVAILABLE, @@ -45,12 +46,11 @@ from esphome.const import ( CONF_USE_ABBREVIATIONS, CONF_USERNAME, CONF_WILL_MESSAGE, + PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, - PLATFORM_BK72XX, ) -from esphome.core import coroutine_with_priority, CORE -from esphome.components.esp32 import add_idf_sdkconfig_option +from esphome.core import CORE, coroutine_with_priority DEPENDENCIES = ["network"] @@ -110,6 +110,9 @@ MQTTDisconnectTrigger = mqtt_ns.class_( MQTTComponent = mqtt_ns.class_("MQTTComponent", cg.Component) MQTTConnectedCondition = mqtt_ns.class_("MQTTConnectedCondition", Condition) +MQTTAlarmControlPanelComponent = mqtt_ns.class_( + "MQTTAlarmControlPanelComponent", MQTTComponent +) MQTTBinarySensorComponent = mqtt_ns.class_("MQTTBinarySensorComponent", MQTTComponent) MQTTClimateComponent = mqtt_ns.class_("MQTTClimateComponent", MQTTComponent) MQTTCoverComponent = mqtt_ns.class_("MQTTCoverComponent", MQTTComponent) diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp new file mode 100644 index 0000000000..660a030d11 --- /dev/null +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -0,0 +1,128 @@ +#include "mqtt_alarm_control_panel.h" +#include "esphome/core/log.h" + +#include "mqtt_const.h" + +#ifdef USE_MQTT +#ifdef USE_ALARM_CONTROL_PANEL + +namespace esphome { +namespace mqtt { + +static const char *const TAG = "mqtt.alarm_control_panel"; + +using namespace esphome::alarm_control_panel; + +MQTTAlarmControlPanelComponent::MQTTAlarmControlPanelComponent(AlarmControlPanel *alarm_control_panel) + : alarm_control_panel_(alarm_control_panel) {} +void MQTTAlarmControlPanelComponent::setup() { + this->alarm_control_panel_->add_on_state_callback([this]() { this->publish_state(); }); + this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) { + auto call = this->alarm_control_panel_->make_call(); + if (strcasecmp(payload.c_str(), "ARM_AWAY") == 0) { + call.arm_away(); + } else if (strcasecmp(payload.c_str(), "ARM_HOME") == 0) { + call.arm_home(); + } else if (strcasecmp(payload.c_str(), "ARM_NIGHT") == 0) { + call.arm_night(); + } else if (strcasecmp(payload.c_str(), "ARM_VACATION") == 0) { + call.arm_vacation(); + } else if (strcasecmp(payload.c_str(), "ARM_CUSTOM_BYPASS") == 0) { + call.arm_custom_bypass(); + } else if (strcasecmp(payload.c_str(), "DISARM") == 0) { + call.disarm(); + } else if (strcasecmp(payload.c_str(), "PENDING") == 0) { + call.pending(); + } else if (strcasecmp(payload.c_str(), "TRIGGERED") == 0) { + call.triggered(); + } else { + ESP_LOGW(TAG, "'%s': Received unknown command payload %s", this->friendly_name().c_str(), payload.c_str()); + } + call.perform(); + }); +} + +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())); +} + +void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + JsonArray supported_features = root.createNestedArray(MQTT_SUPPORTED_FEATURES); + const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features(); + if (acp_supported_features & ACP_FEAT_ARM_AWAY) { + supported_features.add("arm_away"); + } + if (acp_supported_features & ACP_FEAT_ARM_HOME) { + supported_features.add("arm_home"); + } + if (acp_supported_features & ACP_FEAT_ARM_NIGHT) { + supported_features.add("arm_night"); + } + if (acp_supported_features & ACP_FEAT_ARM_VACATION) { + supported_features.add("arm_vacation"); + } + if (acp_supported_features & ACP_FEAT_ARM_CUSTOM_BYPASS) { + supported_features.add("arm_custom_bypass"); + } + if (acp_supported_features & ACP_FEAT_TRIGGER) { + supported_features.add("trigger"); + } + root[MQTT_CODE_DISARM_REQUIRED] = this->alarm_control_panel_->get_requires_code(); + root[MQTT_CODE_ARM_REQUIRED] = this->alarm_control_panel_->get_requires_code_to_arm(); +} + +std::string MQTTAlarmControlPanelComponent::component_type() const { return "alarm_control_panel"; } +const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return this->alarm_control_panel_; } + +bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); } +bool MQTTAlarmControlPanelComponent::publish_state() { + bool success = true; + const char *state_s = ""; + switch (this->alarm_control_panel_->get_state()) { + case ACP_STATE_DISARMED: + state_s = "disarmed"; + break; + case ACP_STATE_ARMED_HOME: + state_s = "armed_home"; + break; + case ACP_STATE_ARMED_AWAY: + state_s = "armed_away"; + break; + case ACP_STATE_ARMED_NIGHT: + state_s = "armed_night"; + break; + case ACP_STATE_ARMED_VACATION: + state_s = "armed_vacation"; + break; + case ACP_STATE_ARMED_CUSTOM_BYPASS: + state_s = "armed_custom_bypass"; + break; + case ACP_STATE_PENDING: + state_s = "pending"; + break; + case ACP_STATE_ARMING: + state_s = "arming"; + break; + case ACP_STATE_DISARMING: + state_s = "disarming"; + break; + case ACP_STATE_TRIGGERED: + state_s = "triggered"; + break; + default: + state_s = "unknown"; + } + if (!this->publish(this->get_state_topic_(), state_s)) + success = false; + return success; +} + +} // namespace mqtt +} // namespace esphome + +#endif +#endif // USE_MQTT diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.h b/esphome/components/mqtt/mqtt_alarm_control_panel.h new file mode 100644 index 0000000000..4ad37b7314 --- /dev/null +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_MQTT +#ifdef USE_ALARM_CONTROL_PANEL + +#include "mqtt_component.h" +#include "esphome/components/alarm_control_panel/alarm_control_panel.h" + +namespace esphome { +namespace mqtt { + +class MQTTAlarmControlPanelComponent : public mqtt::MQTTComponent { + public: + explicit MQTTAlarmControlPanelComponent(alarm_control_panel::AlarmControlPanel *alarm_control_panel); + + void setup() override; + + void send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) override; + + bool send_initial_state() override; + + bool publish_state(); + + void dump_config() override; + + protected: + std::string component_type() const override; + const EntityBase *get_entity() const override; + + alarm_control_panel::AlarmControlPanel *alarm_control_panel_; +}; + +} // namespace mqtt +} // namespace esphome + +#endif +#endif // USE_MQTT diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index a2a751df63..b7d1655ec9 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -426,3 +426,9 @@ valve: } else { return VALVE_CLOSED; } + +alarm_control_panel: + - platform: template + name: Alarm Control Panel + binary_sensors: + - input: some_binary_sensor From 71ea2cec1f21d5ea66c84f7bbe2c063a705d063b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 6 Aug 2024 13:56:48 +1000 Subject: [PATCH 0138/1052] [lvgl] Final stage (#7184) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/lvgl/__init__.py | 50 ++++++------- esphome/components/lvgl/automation.py | 2 +- .../components/lvgl/binary_sensor/__init__.py | 43 +++++++++++ esphome/components/lvgl/light/__init__.py | 32 +++++++++ esphome/components/lvgl/light/lvgl_light.h | 48 +++++++++++++ esphome/components/lvgl/lvgl_esphome.h | 13 ++-- esphome/components/lvgl/number/__init__.py | 52 ++++++++++++++ esphome/components/lvgl/number/lvgl_number.h | 33 +++++++++ esphome/components/lvgl/rotary_encoders.py | 2 +- esphome/components/lvgl/select/__init__.py | 46 ++++++++++++ esphome/components/lvgl/select/lvgl_select.h | 62 ++++++++++++++++ esphome/components/lvgl/sensor/__init__.py | 35 +++++++++ esphome/components/lvgl/styles.py | 4 +- esphome/components/lvgl/switch/__init__.py | 54 ++++++++++++++ esphome/components/lvgl/switch/lvgl_switch.h | 33 +++++++++ esphome/components/lvgl/text/__init__.py | 39 ++++++++++ esphome/components/lvgl/text/lvgl_text.h | 33 +++++++++ .../components/lvgl/text_sensor/__init__.py | 40 +++++++++++ esphome/components/lvgl/touchscreens.py | 2 +- esphome/components/lvgl/trigger.py | 2 +- .../lvgl/{widget.py => widgets/__init__.py} | 12 ++-- .../components/lvgl/{ => widgets}/animimg.py | 14 ++-- esphome/components/lvgl/{ => widgets}/arc.py | 10 +-- .../components/lvgl/{ => widgets}/button.py | 4 +- .../lvgl/{ => widgets}/buttonmatrix.py | 20 +++--- .../components/lvgl/{ => widgets}/checkbox.py | 12 ++-- .../components/lvgl/{ => widgets}/dropdown.py | 12 ++-- esphome/components/lvgl/{ => widgets}/img.py | 10 +-- .../components/lvgl/{ => widgets}/keyboard.py | 8 +-- .../components/lvgl/{ => widgets}/label.py | 10 +-- esphome/components/lvgl/{ => widgets}/led.py | 10 +-- esphome/components/lvgl/{ => widgets}/line.py | 11 ++- .../components/lvgl/{ => widgets}/lv_bar.py | 12 ++-- .../components/lvgl/{ => widgets}/meter.py | 24 +++---- .../components/lvgl/{ => widgets}/msgbox.py | 72 ++++++++++--------- esphome/components/lvgl/{ => widgets}/obj.py | 8 +-- esphome/components/lvgl/{ => widgets}/page.py | 12 ++-- .../components/lvgl/{ => widgets}/roller.py | 10 +-- .../components/lvgl/{ => widgets}/slider.py | 12 ++-- .../components/lvgl/{ => widgets}/spinbox.py | 12 ++-- .../components/lvgl/{ => widgets}/spinner.py | 10 +-- .../lvgl/{lv_switch.py => widgets/switch.py} | 6 +- .../components/lvgl/{ => widgets}/tabview.py | 16 ++--- .../components/lvgl/{ => widgets}/textarea.py | 10 +-- .../components/lvgl/{ => widgets}/tileview.py | 16 ++--- tests/components/lvgl/common.yaml | 72 +++++++++++++++++++ 46 files changed, 840 insertions(+), 210 deletions(-) create mode 100644 esphome/components/lvgl/binary_sensor/__init__.py create mode 100644 esphome/components/lvgl/light/__init__.py create mode 100644 esphome/components/lvgl/light/lvgl_light.h create mode 100644 esphome/components/lvgl/number/__init__.py create mode 100644 esphome/components/lvgl/number/lvgl_number.h create mode 100644 esphome/components/lvgl/select/__init__.py create mode 100644 esphome/components/lvgl/select/lvgl_select.h create mode 100644 esphome/components/lvgl/sensor/__init__.py create mode 100644 esphome/components/lvgl/switch/__init__.py create mode 100644 esphome/components/lvgl/switch/lvgl_switch.h create mode 100644 esphome/components/lvgl/text/__init__.py create mode 100644 esphome/components/lvgl/text/lvgl_text.h create mode 100644 esphome/components/lvgl/text_sensor/__init__.py rename esphome/components/lvgl/{widget.py => widgets/__init__.py} (98%) rename esphome/components/lvgl/{ => widgets}/animimg.py (89%) rename esphome/components/lvgl/{ => widgets}/arc.py (92%) rename esphome/components/lvgl/{ => widgets}/button.py (82%) rename esphome/components/lvgl/{ => widgets}/buttonmatrix.py (95%) rename esphome/components/lvgl/{ => widgets}/checkbox.py (68%) rename esphome/components/lvgl/{ => widgets}/dropdown.py (89%) rename esphome/components/lvgl/{ => widgets}/img.py (92%) rename esphome/components/lvgl/{ => widgets}/keyboard.py (86%) rename esphome/components/lvgl/{ => widgets}/label.py (85%) rename esphome/components/lvgl/{ => widgets}/led.py (80%) rename esphome/components/lvgl/{ => widgets}/line.py (85%) rename esphome/components/lvgl/{ => widgets}/lv_bar.py (81%) rename esphome/components/lvgl/{ => widgets}/meter.py (96%) rename esphome/components/lvgl/{ => widgets}/msgbox.py (72%) rename esphome/components/lvgl/{ => widgets}/obj.py (76%) rename esphome/components/lvgl/{ => widgets}/page.py (91%) rename esphome/components/lvgl/{ => widgets}/roller.py (92%) rename esphome/components/lvgl/{ => widgets}/slider.py (88%) rename esphome/components/lvgl/{ => widgets}/spinbox.py (95%) rename esphome/components/lvgl/{ => widgets}/spinner.py (82%) rename esphome/components/lvgl/{lv_switch.py => widgets/switch.py} (72%) rename esphome/components/lvgl/{ => widgets}/tabview.py (88%) rename esphome/components/lvgl/{ => widgets}/textarea.py (90%) rename esphome/components/lvgl/{ => widgets}/tileview.py (89%) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index a963fca98b..9eb4665874 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -21,28 +21,10 @@ from esphome.final_validate import full_config from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid -from .animimg import animimg_spec -from .arc import arc_spec from .automation import disp_update, update_to_code -from .button import button_spec -from .buttonmatrix import buttonmatrix_spec -from .checkbox import checkbox_spec from .defines import CONF_SKIP -from .dropdown import dropdown_spec -from .img import img_spec -from .keyboard import keyboard_spec -from .label import label_spec -from .led import led_spec -from .line import line_spec -from .lv_bar import bar_spec -from .lv_switch import switch_spec from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent -from .meter import meter_spec -from .msgbox import MSGBOX_SCHEMA, msgboxes_to_code -from .obj import obj_spec -from .page import add_pages, page_spec -from .roller import roller_spec from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code from .schemas import ( DISP_BG_SCHEMA, @@ -57,13 +39,7 @@ from .schemas import ( grid_alignments, obj_schema, ) -from .slider import slider_spec -from .spinbox import spinbox_spec -from .spinner import spinner_spec from .styles import add_top_layer, styles_to_code, theme_to_code -from .tabview import tabview_spec -from .textarea import textarea_spec -from .tileview import tileview_spec from .touchscreens import touchscreen_schema, touchscreens_to_code from .trigger import generate_triggers from .types import ( @@ -74,7 +50,31 @@ from .types import ( lv_style_t, lvgl_ns, ) -from .widget import Widget, add_widgets, lv_scr_act, set_obj_properties +from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties +from .widgets.animimg import animimg_spec +from .widgets.arc import arc_spec +from .widgets.button import button_spec +from .widgets.buttonmatrix import buttonmatrix_spec +from .widgets.checkbox import checkbox_spec +from .widgets.dropdown import dropdown_spec +from .widgets.img import img_spec +from .widgets.keyboard import keyboard_spec +from .widgets.label import label_spec +from .widgets.led import led_spec +from .widgets.line import line_spec +from .widgets.lv_bar import bar_spec +from .widgets.meter import meter_spec +from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code +from .widgets.obj import obj_spec +from .widgets.page import add_pages, page_spec +from .widgets.roller import roller_spec +from .widgets.slider import slider_spec +from .widgets.spinbox import spinbox_spec +from .widgets.spinner import spinner_spec +from .widgets.switch import switch_spec +from .widgets.tabview import tabview_spec +from .widgets.textarea import textarea_spec +from .widgets.tileview import tileview_spec DOMAIN = "lvgl" DEPENDENCIES = ["display"] diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 7a862fb58b..556e286208 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -38,7 +38,7 @@ from .types import ( lv_disp_t, lv_obj_t, ) -from .widget import Widget, get_widgets, lv_scr_act, set_obj_properties +from .widgets import Widget, get_widgets, lv_scr_act, set_obj_properties async def action_to_code( diff --git a/esphome/components/lvgl/binary_sensor/__init__.py b/esphome/components/lvgl/binary_sensor/__init__.py new file mode 100644 index 0000000000..8789a06375 --- /dev/null +++ b/esphome/components/lvgl/binary_sensor/__init__.py @@ -0,0 +1,43 @@ +import esphome.codegen as cg +from esphome.components.binary_sensor import ( + BinarySensor, + binary_sensor_schema, + new_binary_sensor, +) +import esphome.config_validation as cv + +from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..lvcode import EVENT_ARG, LambdaContext, LvContext +from ..schemas import LVGL_SCHEMA +from ..types import LV_EVENT, lv_pseudo_button_t +from ..widgets import Widget, get_widgets + +CONFIG_SCHEMA = ( + binary_sensor_schema(BinarySensor) + .extend(LVGL_SCHEMA) + .extend( + { + cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t), + } + ) +) + + +async def to_code(config): + sensor = await new_binary_sensor(config) + paren = await cg.get_variable(config[CONF_LVGL_ID]) + widget = await get_widgets(config, CONF_WIDGET) + widget = widget[0] + assert isinstance(widget, Widget) + async with LambdaContext(EVENT_ARG) as pressed_ctx: + pressed_ctx.add(sensor.publish_state(widget.is_pressed())) + async with LvContext(paren) as ctx: + ctx.add(sensor.publish_initial_state(widget.is_pressed())) + ctx.add( + paren.add_event_cb( + widget.obj, + await pressed_ctx.get_lambda(), + LV_EVENT.PRESSING, + LV_EVENT.RELEASED, + ) + ) diff --git a/esphome/components/lvgl/light/__init__.py b/esphome/components/lvgl/light/__init__.py new file mode 100644 index 0000000000..27c160dff6 --- /dev/null +++ b/esphome/components/lvgl/light/__init__.py @@ -0,0 +1,32 @@ +import esphome.codegen as cg +from esphome.components import light +from esphome.components.light import LightOutput +import esphome.config_validation as cv +from esphome.const import CONF_GAMMA_CORRECT, CONF_LED, CONF_OUTPUT_ID + +from ..defines import CONF_LVGL_ID +from ..lvcode import LvContext +from ..schemas import LVGL_SCHEMA +from ..types import LvType, lvgl_ns +from ..widgets import get_widgets + +lv_led_t = LvType("lv_led_t") +LVLight = lvgl_ns.class_("LVLight", LightOutput) +CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( + { + cv.Optional(CONF_GAMMA_CORRECT, default=0.0): cv.positive_float, + cv.Required(CONF_LED): cv.use_id(lv_led_t), + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(LVLight), + } +).extend(LVGL_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) + await light.register_light(var, config) + + paren = await cg.get_variable(config[CONF_LVGL_ID]) + widget = await get_widgets(config, CONF_LED) + widget = widget[0] + async with LvContext(paren) as ctx: + ctx.add(var.set_obj(widget.obj)) diff --git a/esphome/components/lvgl/light/lvgl_light.h b/esphome/components/lvgl/light/lvgl_light.h new file mode 100644 index 0000000000..67372d89dd --- /dev/null +++ b/esphome/components/lvgl/light/lvgl_light.h @@ -0,0 +1,48 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/light/light_output.h" +#include "../lvgl_esphome.h" + +namespace esphome { +namespace lvgl { + +class LVLight : public light::LightOutput { + public: + light::LightTraits get_traits() override { + auto traits = light::LightTraits(); + traits.set_supported_color_modes({light::ColorMode::RGB}); + return traits; + } + void write_state(light::LightState *state) override { + float red, green, blue; + state->current_values_as_rgb(&red, &green, &blue, false); + auto color = lv_color_make(red * 255, green * 255, blue * 255); + if (this->obj_ != nullptr) { + this->set_value_(color); + } else { + this->initial_value_ = color; + } + } + + void set_obj(lv_obj_t *obj) { + this->obj_ = obj; + if (this->initial_value_) { + lv_led_set_color(obj, this->initial_value_.value()); + lv_led_on(obj); + this->initial_value_.reset(); + } + } + + protected: + void set_value_(lv_color_t value) { + lv_led_set_color(this->obj_, value); + lv_led_on(this->obj_); + lv_event_send(this->obj_, lv_custom_event, nullptr); + } + lv_obj_t *obj_{}; + optional initial_value_{}; +}; + +} // namespace lvgl +} // namespace esphome diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 71e0fd069f..5f2f0ea8df 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -1,13 +1,6 @@ #pragma once #include "esphome/core/defines.h" -#ifdef USE_LVGL_BINARY_SENSOR -#include "esphome/components/binary_sensor/binary_sensor.h" -#endif // USE_LVGL_BINARY_SENSOR -#ifdef USE_LVGL_ROTARY_ENCODER -#include "esphome/components/rotary_encoder/rotary_encoder.h" -#endif // USE_LVGL_ROTARY_ENCODER - // required for clang-tidy #ifndef LV_CONF_H #define LV_CONF_SKIP 1 // NOLINT @@ -19,6 +12,12 @@ #include "esphome/core/log.h" #include #include + +#ifdef USE_LVGL_ROTARY_ENCODER +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/rotary_encoder/rotary_encoder.h" +#endif // USE_LVGL_ROTARY_ENCODER + #ifdef USE_LVGL_IMAGE #include "esphome/components/image/image.h" #endif // USE_LVGL_IMAGE diff --git a/esphome/components/lvgl/number/__init__.py b/esphome/components/lvgl/number/__init__.py new file mode 100644 index 0000000000..53aef2790d --- /dev/null +++ b/esphome/components/lvgl/number/__init__.py @@ -0,0 +1,52 @@ +import esphome.codegen as cg +from esphome.components import number +import esphome.config_validation as cv +from esphome.cpp_generator import MockObj + +from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET +from ..lv_validation import animated +from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add +from ..schemas import LVGL_SCHEMA +from ..types import LV_EVENT, LvNumber, lvgl_ns +from ..widgets import get_widgets + +LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number) + +CONFIG_SCHEMA = ( + number.number_schema(LVGLNumber) + .extend(LVGL_SCHEMA) + .extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvNumber), + cv.Optional(CONF_ANIMATED, default=True): animated, + } + ) +) + + +async def to_code(config): + paren = await cg.get_variable(config[CONF_LVGL_ID]) + widget = await get_widgets(config, CONF_WIDGET) + widget = widget[0] + var = await number.new_number( + config, + max_value=widget.get_max(), + min_value=widget.get_min(), + step=widget.get_step(), + ) + + async with LambdaContext([(cg.float_, "v")]) as control: + await widget.set_property( + "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] + ) + lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) + async with LambdaContext(EVENT_ARG) as event: + event.add(var.publish_state(widget.get_value())) + async with LvContext(paren): + lv_add(var.set_control_lambda(await control.get_lambda())) + lv_add( + paren.add_event_cb( + widget.obj, await event.get_lambda(), LV_EVENT.VALUE_CHANGED + ) + ) + lv_add(var.publish_state(widget.get_value())) diff --git a/esphome/components/lvgl/number/lvgl_number.h b/esphome/components/lvgl/number/lvgl_number.h new file mode 100644 index 0000000000..461ea51be4 --- /dev/null +++ b/esphome/components/lvgl/number/lvgl_number.h @@ -0,0 +1,33 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace lvgl { + +class LVGLNumber : public number::Number { + public: + void set_control_lambda(std::function control_lambda) { + this->control_lambda_ = control_lambda; + if (this->initial_state_.has_value()) { + this->control_lambda_(this->initial_state_.value()); + this->initial_state_.reset(); + } + } + + protected: + void control(float value) { + if (this->control_lambda_ != nullptr) + this->control_lambda_(value); + else + this->initial_state_ = value; + } + std::function control_lambda_{}; + optional initial_state_{}; +}; + +} // namespace lvgl +} // namespace esphome diff --git a/esphome/components/lvgl/rotary_encoders.py b/esphome/components/lvgl/rotary_encoders.py index ede6905a67..d8a82dbc78 100644 --- a/esphome/components/lvgl/rotary_encoders.py +++ b/esphome/components/lvgl/rotary_encoders.py @@ -16,7 +16,7 @@ from .helpers import lvgl_components_required from .lvcode import lv, lv_add, lv_expr from .schemas import ENCODER_SCHEMA from .types import lv_indev_type_t -from .widget import add_group +from .widgets import add_group ROTARY_ENCODER_CONFIG = cv.ensure_list( ENCODER_SCHEMA.extend( diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py new file mode 100644 index 0000000000..34a70a23f7 --- /dev/null +++ b/esphome/components/lvgl/select/__init__.py @@ -0,0 +1,46 @@ +import esphome.codegen as cg +from esphome.components import select +import esphome.config_validation as cv +from esphome.const import CONF_OPTIONS + +from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET +from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add +from ..schemas import LVGL_SCHEMA +from ..types import LV_EVENT, LvSelect, lvgl_ns +from ..widgets import get_widgets + +LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) + +CONFIG_SCHEMA = ( + select.select_schema(LVGLSelect) + .extend(LVGL_SCHEMA) + .extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvSelect), + cv.Optional(CONF_ANIMATED, default=False): cv.boolean, + } + ) +) + + +async def to_code(config): + widget = await get_widgets(config, CONF_WIDGET) + widget = widget[0] + options = widget.config.get(CONF_OPTIONS, []) + selector = await select.new_select(config, options=options) + paren = await cg.get_variable(config[CONF_LVGL_ID]) + async with LambdaContext(EVENT_ARG) as pub_ctx: + pub_ctx.add(selector.publish_index(widget.get_value())) + async with LambdaContext([(cg.uint16, "v")]) as control: + await widget.set_property("selected", "v", animated=config[CONF_ANIMATED]) + lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) + async with LvContext(paren) as ctx: + lv_add(selector.set_control_lambda(await control.get_lambda())) + ctx.add( + paren.add_event_cb( + widget.obj, + await pub_ctx.get_lambda(), + LV_EVENT.VALUE_CHANGED, + ) + ) + lv_add(selector.publish_index(widget.get_value())) diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h new file mode 100644 index 0000000000..407045d605 --- /dev/null +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -0,0 +1,62 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace lvgl { + +static std::vector split_string(const std::string &str) { + std::vector strings; + auto delimiter = std::string("\n"); + + std::string::size_type pos; + std::string::size_type prev = 0; + while ((pos = str.find(delimiter, prev)) != std::string::npos) { + strings.push_back(str.substr(prev, pos - prev)); + prev = pos + delimiter.size(); + } + + // To get the last substring (or only, if delimiter is not found) + strings.push_back(str.substr(prev)); + + return strings; +} + +class LVGLSelect : public select::Select { + public: + void set_control_lambda(std::function lambda) { + this->control_lambda_ = lambda; + if (this->initial_state_.has_value()) { + this->control(this->initial_state_.value()); + this->initial_state_.reset(); + } + } + + void publish_index(size_t index) { + auto value = this->at(index); + if (value) + this->publish_state(value.value()); + } + + void set_options(const char *str) { this->traits.set_options(split_string(str)); } + + protected: + void control(const std::string &value) override { + if (this->control_lambda_ != nullptr) { + auto index = index_of(value); + if (index) + this->control_lambda_(index.value()); + } else { + this->initial_state_ = value.c_str(); + } + } + + std::function control_lambda_{}; + optional initial_state_{}; +}; + +} // namespace lvgl +} // namespace esphome diff --git a/esphome/components/lvgl/sensor/__init__.py b/esphome/components/lvgl/sensor/__init__.py new file mode 100644 index 0000000000..6e495eb685 --- /dev/null +++ b/esphome/components/lvgl/sensor/__init__.py @@ -0,0 +1,35 @@ +import esphome.codegen as cg +from esphome.components.sensor import Sensor, new_sensor, sensor_schema +import esphome.config_validation as cv + +from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..lvcode import EVENT_ARG, LVGL_COMP_ARG, LambdaContext, LvContext, lv_add +from ..schemas import LVGL_SCHEMA +from ..types import LV_EVENT, LvNumber +from ..widgets import Widget, get_widgets + +CONFIG_SCHEMA = ( + sensor_schema(Sensor) + .extend(LVGL_SCHEMA) + .extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvNumber), + } + ) +) + + +async def to_code(config): + sensor = await new_sensor(config) + paren = await cg.get_variable(config[CONF_LVGL_ID]) + widget = await get_widgets(config, CONF_WIDGET) + widget = widget[0] + assert isinstance(widget, Widget) + async with LambdaContext(EVENT_ARG) as lamb: + lv_add(sensor.publish_state(widget.get_value())) + async with LvContext(paren, LVGL_COMP_ARG): + lv_add( + paren.add_event_cb( + widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED + ) + ) diff --git a/esphome/components/lvgl/styles.py b/esphome/components/lvgl/styles.py index 09f1c376d0..26c2694a52 100644 --- a/esphome/components/lvgl/styles.py +++ b/esphome/components/lvgl/styles.py @@ -12,10 +12,10 @@ from .defines import ( ) from .helpers import add_lv_use from .lvcode import LambdaContext, LocalVariable, lv, lv_assign, lv_variable -from .obj import obj_spec from .schemas import ALL_STYLES from .types import lv_lambda_t, lv_obj_t, lv_obj_t_ptr -from .widget import Widget, add_widgets, set_obj_properties, theme_widget_map +from .widgets import Widget, add_widgets, set_obj_properties, theme_widget_map +from .widgets.obj import obj_spec TOP_LAYER = literal("lv_disp_get_layer_top(lv_component->get_disp())") diff --git a/esphome/components/lvgl/switch/__init__.py b/esphome/components/lvgl/switch/__init__.py new file mode 100644 index 0000000000..831fa9308b --- /dev/null +++ b/esphome/components/lvgl/switch/__init__.py @@ -0,0 +1,54 @@ +import esphome.codegen as cg +from esphome.components.switch import Switch, new_switch, switch_schema +import esphome.config_validation as cv +from esphome.cpp_generator import MockObj + +from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..lvcode import ( + CUSTOM_EVENT, + EVENT_ARG, + LambdaContext, + LvConditional, + LvContext, + lv, + lv_add, +) +from ..schemas import LVGL_SCHEMA +from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns +from ..widgets import get_widgets + +LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch) +CONFIG_SCHEMA = ( + switch_schema(LVGLSwitch) + .extend(LVGL_SCHEMA) + .extend( + { + cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t), + } + ) +) + + +async def to_code(config): + switch = await new_switch(config) + paren = await cg.get_variable(config[CONF_LVGL_ID]) + widget = await get_widgets(config, CONF_WIDGET) + widget = widget[0] + async with LambdaContext(EVENT_ARG) as checked_ctx: + checked_ctx.add(switch.publish_state(widget.get_value())) + async with LambdaContext([(cg.bool_, "v")]) as control: + with LvConditional(MockObj("v")) as cond: + widget.add_state(LV_STATE.CHECKED) + cond.else_() + widget.clear_state(LV_STATE.CHECKED) + lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) + async with LvContext(paren) as ctx: + lv_add(switch.set_control_lambda(await control.get_lambda())) + ctx.add( + paren.add_event_cb( + widget.obj, + await checked_ctx.get_lambda(), + LV_EVENT.VALUE_CHANGED, + ) + ) + lv_add(switch.publish_state(widget.get_value())) diff --git a/esphome/components/lvgl/switch/lvgl_switch.h b/esphome/components/lvgl/switch/lvgl_switch.h new file mode 100644 index 0000000000..f20f4ed960 --- /dev/null +++ b/esphome/components/lvgl/switch/lvgl_switch.h @@ -0,0 +1,33 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace lvgl { + +class LVGLSwitch : public switch_::Switch { + public: + void set_control_lambda(std::function state_lambda) { + this->state_lambda_ = state_lambda; + if (this->initial_state_.has_value()) { + this->state_lambda_(this->initial_state_.value()); + this->initial_state_.reset(); + } + } + + protected: + void write_state(bool value) { + if (this->state_lambda_ != nullptr) + this->state_lambda_(value); + else + this->initial_state_ = value; + } + std::function state_lambda_{}; + optional initial_state_{}; +}; + +} // namespace lvgl +} // namespace esphome diff --git a/esphome/components/lvgl/text/__init__.py b/esphome/components/lvgl/text/__init__.py new file mode 100644 index 0000000000..55f1b2b3fc --- /dev/null +++ b/esphome/components/lvgl/text/__init__.py @@ -0,0 +1,39 @@ +import esphome.codegen as cg +from esphome.components import text +from esphome.components.text import new_text +import esphome.config_validation as cv + +from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add +from ..schemas import LVGL_SCHEMA +from ..types import LV_EVENT, LvText, lvgl_ns +from ..widgets import get_widgets + +LVGLText = lvgl_ns.class_("LVGLText", text.Text) + +CONFIG_SCHEMA = text.TEXT_SCHEMA.extend(LVGL_SCHEMA).extend( + { + cv.GenerateID(): cv.declare_id(LVGLText), + cv.Required(CONF_WIDGET): cv.use_id(LvText), + } +) + + +async def to_code(config): + textvar = await new_text(config) + paren = await cg.get_variable(config[CONF_LVGL_ID]) + widget = await get_widgets(config, CONF_WIDGET) + widget = widget[0] + async with LambdaContext([(cg.std_string, "text_value")]) as control: + await widget.set_property("text", "text_value.c_str())") + lv.event_send(widget.obj, CUSTOM_EVENT, None) + async with LambdaContext(EVENT_ARG) as lamb: + lv_add(textvar.publish_state(widget.get_value())) + async with LvContext(paren): + widget.var.set_control_lambda(await control.get_lambda()) + lv_add( + paren.add_event_cb( + widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED + ) + ) + lv_add(textvar.publish_state(widget.get_value())) diff --git a/esphome/components/lvgl/text/lvgl_text.h b/esphome/components/lvgl/text/lvgl_text.h new file mode 100644 index 0000000000..8dc0281364 --- /dev/null +++ b/esphome/components/lvgl/text/lvgl_text.h @@ -0,0 +1,33 @@ +#pragma once + +#include "esphome/components/text/text.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace lvgl { + +class LVGLText : public text::Text { + public: + void set_control_lambda(std::function control_lambda) { + this->control_lambda_ = control_lambda; + if (this->initial_state_.has_value()) { + this->control_lambda_(this->initial_state_.value()); + this->initial_state_.reset(); + } + } + + protected: + void control(const std::string &value) { + if (this->control_lambda_ != nullptr) + this->control_lambda_(value); + else + this->initial_state_ = value; + } + std::function control_lambda_{}; + optional initial_state_{}; +}; + +} // namespace lvgl +} // namespace esphome diff --git a/esphome/components/lvgl/text_sensor/__init__.py b/esphome/components/lvgl/text_sensor/__init__.py new file mode 100644 index 0000000000..c0f0bc36a8 --- /dev/null +++ b/esphome/components/lvgl/text_sensor/__init__.py @@ -0,0 +1,40 @@ +import esphome.codegen as cg +from esphome.components.text_sensor import ( + TextSensor, + new_text_sensor, + text_sensor_schema, +) +import esphome.config_validation as cv + +from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..lvcode import EVENT_ARG, LambdaContext, LvContext +from ..schemas import LVGL_SCHEMA +from ..types import LV_EVENT, LvText +from ..widgets import get_widgets + +CONFIG_SCHEMA = ( + text_sensor_schema(TextSensor) + .extend(LVGL_SCHEMA) + .extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvText), + } + ) +) + + +async def to_code(config): + sensor = await new_text_sensor(config) + paren = await cg.get_variable(config[CONF_LVGL_ID]) + widget = await get_widgets(config, CONF_WIDGET) + widget = widget[0] + async with LambdaContext(EVENT_ARG) as pressed_ctx: + pressed_ctx.add(sensor.publish_state(widget.get_value())) + async with LvContext(paren) as ctx: + ctx.add( + paren.add_event_cb( + widget.obj, + await pressed_ctx.get_lambda(), + LV_EVENT.VALUE_CHANGED, + ) + ) diff --git a/esphome/components/lvgl/touchscreens.py b/esphome/components/lvgl/touchscreens.py index 499b33aa02..292b0873f3 100644 --- a/esphome/components/lvgl/touchscreens.py +++ b/esphome/components/lvgl/touchscreens.py @@ -34,7 +34,7 @@ def touchscreen_schema(config): async def touchscreens_to_code(var, config): - for tconf in config.get(CONF_TOUCHSCREENS) or (): + for tconf in config.get(CONF_TOUCHSCREENS, ()): lvgl_components_required.add(CONF_TOUCHSCREEN) touchscreen = await cg.get_variable(tconf[CONF_TOUCHSCREEN_ID]) lpt = tconf[CONF_LONG_PRESS_TIME].total_milliseconds diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index c640c8abd9..df87be718b 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -13,7 +13,7 @@ from .defines import ( ) from .lvcode import EVENT_ARG, LambdaContext, LvConditional, lv, lv_add from .types import LV_EVENT -from .widget import widget_map +from .widgets import widget_map async def generate_triggers(lv_component): diff --git a/esphome/components/lvgl/widget.py b/esphome/components/lvgl/widgets/__init__.py similarity index 98% rename from esphome/components/lvgl/widget.py rename to esphome/components/lvgl/widgets/__init__.py index fcaee29085..dff43cf257 100644 --- a/esphome/components/lvgl/widget.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -8,7 +8,7 @@ from esphome.core import ID, TimePeriod from esphome.coroutine import FakeAwaitable from esphome.cpp_generator import AssignmentExpression, CallExpression, MockObj -from .defines import ( +from ..defines import ( CONF_DEFAULT, CONF_FLEX_ALIGN_CROSS, CONF_FLEX_ALIGN_MAIN, @@ -32,8 +32,8 @@ from .defines import ( join_enums, literal, ) -from .helpers import add_lv_use -from .lvcode import ( +from ..helpers import add_lv_use +from ..lvcode import ( LvConditional, add_line_marks, lv, @@ -43,8 +43,8 @@ from .lvcode import ( lv_obj, lv_Pvariable, ) -from .schemas import ALL_STYLES, STYLE_REMAP, WIDGET_TYPES -from .types import ( +from ..schemas import ALL_STYLES, STYLE_REMAP, WIDGET_TYPES +from ..types import ( LV_STATE, LvType, WidgetType, @@ -368,7 +368,7 @@ async def add_widgets(parent: Widget, config: dict): :param config: The configuration :return: """ - for w in config.get(CONF_WIDGETS) or (): + for w in config.get(CONF_WIDGETS, ()): w_type, w_cnfig = next(iter(w.items())) await widget_to_code(w_cnfig, w_type, parent.obj) diff --git a/esphome/components/lvgl/animimg.py b/esphome/components/lvgl/widgets/animimg.py similarity index 89% rename from esphome/components/lvgl/animimg.py rename to esphome/components/lvgl/widgets/animimg.py index ad84713d7f..a973ca0702 100644 --- a/esphome/components/lvgl/animimg.py +++ b/esphome/components/lvgl/widgets/animimg.py @@ -4,15 +4,15 @@ import esphome.config_validation as cv from esphome.const import CONF_DURATION, CONF_ID from esphome.cpp_generator import MockObj -from .automation import action_to_code -from .defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC -from .helpers import lvgl_components_required +from ..automation import action_to_code +from ..defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC +from ..helpers import lvgl_components_required +from ..lv_validation import lv_image, lv_milliseconds +from ..lvcode import lv, lv_expr +from ..types import LvType, ObjUpdateAction, void_ptr +from . import Widget, WidgetType, get_widgets from .img import CONF_IMAGE from .label import CONF_LABEL -from .lv_validation import lv_image, lv_milliseconds -from .lvcode import lv, lv_expr -from .types import LvType, ObjUpdateAction, void_ptr -from .widget import Widget, WidgetType, get_widgets CONF_ANIMIMG = "animimg" CONF_SRC_LIST_ID = "src_list_id" diff --git a/esphome/components/lvgl/arc.py b/esphome/components/lvgl/widgets/arc.py similarity index 92% rename from esphome/components/lvgl/arc.py rename to esphome/components/lvgl/widgets/arc.py index d036464c7a..a6f8918e2f 100644 --- a/esphome/components/lvgl/arc.py +++ b/esphome/components/lvgl/widgets/arc.py @@ -8,7 +8,7 @@ from esphome.const import ( ) from esphome.cpp_types import nullptr -from .defines import ( +from ..defines import ( ARC_MODES, CONF_ADJUSTABLE, CONF_CHANGE_RATE, @@ -19,10 +19,10 @@ from .defines import ( CONF_START_ANGLE, literal, ) -from .lv_validation import angle, get_start_value, lv_float -from .lvcode import lv, lv_obj -from .types import LvNumber, NumberType -from .widget import Widget +from ..lv_validation import angle, get_start_value, lv_float +from ..lvcode import lv, lv_obj +from ..types import LvNumber, NumberType +from . import Widget CONF_ARC = "arc" ARC_SCHEMA = cv.Schema( diff --git a/esphome/components/lvgl/button.py b/esphome/components/lvgl/widgets/button.py similarity index 82% rename from esphome/components/lvgl/button.py rename to esphome/components/lvgl/widgets/button.py index 96329b3fa9..b59884ee67 100644 --- a/esphome/components/lvgl/button.py +++ b/esphome/components/lvgl/widgets/button.py @@ -1,7 +1,7 @@ from esphome.const import CONF_BUTTON -from .defines import CONF_MAIN -from .types import LvBoolean, WidgetType +from ..defines import CONF_MAIN +from ..types import LvBoolean, WidgetType lv_button_t = LvBoolean("lv_btn_t") diff --git a/esphome/components/lvgl/buttonmatrix.py b/esphome/components/lvgl/widgets/buttonmatrix.py similarity index 95% rename from esphome/components/lvgl/buttonmatrix.py rename to esphome/components/lvgl/widgets/buttonmatrix.py index 75ed43f909..274b4de5ab 100644 --- a/esphome/components/lvgl/buttonmatrix.py +++ b/esphome/components/lvgl/widgets/buttonmatrix.py @@ -5,9 +5,8 @@ import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_WIDTH from esphome.cpp_generator import MockObj -from .automation import action_to_code -from .button import lv_button_t -from .defines import ( +from ..automation import action_to_code +from ..defines import ( BUTTONMATRIX_CTRLS, CONF_BUTTONS, CONF_CONTROL, @@ -19,11 +18,11 @@ from .defines import ( CONF_SELECTED, CONF_TEXT, ) -from .helpers import lvgl_components_required -from .lv_validation import key_code, lv_bool -from .lvcode import lv, lv_add, lv_expr -from .schemas import automation_schema -from .types import ( +from ..helpers import lvgl_components_required +from ..lv_validation import key_code, lv_bool +from ..lvcode import lv, lv_add, lv_expr +from ..schemas import automation_schema +from ..types import ( LV_BTNMATRIX_CTRL, LV_STATE, LvBoolean, @@ -33,7 +32,8 @@ from .types import ( char_ptr, lv_pseudo_button_t, ) -from .widget import Widget, WidgetType, get_widgets, widget_map +from . import Widget, WidgetType, get_widgets, widget_map +from .button import lv_button_t CONF_BUTTONMATRIX = "buttonmatrix" CONF_BUTTON_TEXT_LIST_ID = "button_text_list_id" @@ -151,7 +151,7 @@ async def get_button_data(config, buttonmatrix: Widget): width_list = [] key_list = [] for row in config: - for button_conf in row.get(CONF_BUTTONS) or (): + for button_conf in row.get(CONF_BUTTONS, ()): bid = button_conf[CONF_ID] index = len(width_list) MatrixButton.create_button(bid, buttonmatrix, button_conf, index) diff --git a/esphome/components/lvgl/checkbox.py b/esphome/components/lvgl/widgets/checkbox.py similarity index 68% rename from esphome/components/lvgl/checkbox.py rename to esphome/components/lvgl/widgets/checkbox.py index be7b029269..6299a2a6a2 100644 --- a/esphome/components/lvgl/checkbox.py +++ b/esphome/components/lvgl/widgets/checkbox.py @@ -1,9 +1,9 @@ -from .defines import CONF_INDICATOR, CONF_MAIN, CONF_TEXT -from .lv_validation import lv_text -from .lvcode import lv -from .schemas import TEXT_SCHEMA -from .types import LvBoolean -from .widget import Widget, WidgetType +from ..defines import CONF_INDICATOR, CONF_MAIN, CONF_TEXT +from ..lv_validation import lv_text +from ..lvcode import lv +from ..schemas import TEXT_SCHEMA +from ..types import LvBoolean +from . import Widget, WidgetType CONF_CHECKBOX = "checkbox" diff --git a/esphome/components/lvgl/dropdown.py b/esphome/components/lvgl/widgets/dropdown.py similarity index 89% rename from esphome/components/lvgl/dropdown.py rename to esphome/components/lvgl/widgets/dropdown.py index d7bdebaade..dc0346b080 100644 --- a/esphome/components/lvgl/dropdown.py +++ b/esphome/components/lvgl/widgets/dropdown.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_OPTIONS -from .defines import ( +from ..defines import ( CONF_DIR, CONF_INDICATOR, CONF_MAIN, @@ -11,12 +11,12 @@ from .defines import ( DIRECTIONS, literal, ) +from ..lv_validation import lv_int, lv_text, option_string +from ..lvcode import LocalVariable, lv, lv_expr +from ..schemas import part_schema +from ..types import LvSelect, LvType, lv_obj_t +from . import Widget, WidgetType, set_obj_properties from .label import CONF_LABEL -from .lv_validation import lv_int, lv_text, option_string -from .lvcode import LocalVariable, lv, lv_expr -from .schemas import part_schema -from .types import LvSelect, LvType, lv_obj_t -from .widget import Widget, WidgetType, set_obj_properties CONF_DROPDOWN = "dropdown" CONF_DROPDOWN_LIST = "dropdown_list" diff --git a/esphome/components/lvgl/img.py b/esphome/components/lvgl/widgets/img.py similarity index 92% rename from esphome/components/lvgl/img.py rename to esphome/components/lvgl/widgets/img.py index dd962fcf31..59b2c97c63 100644 --- a/esphome/components/lvgl/img.py +++ b/esphome/components/lvgl/widgets/img.py @@ -1,7 +1,7 @@ import esphome.config_validation as cv from esphome.const import CONF_ANGLE, CONF_MODE -from .defines import ( +from ..defines import ( CONF_ANTIALIAS, CONF_MAIN, CONF_OFFSET_X, @@ -12,11 +12,11 @@ from .defines import ( CONF_ZOOM, LvConstant, ) +from ..lv_validation import angle, lv_bool, lv_image, size, zoom +from ..lvcode import lv +from ..types import lv_img_t +from . import Widget, WidgetType from .label import CONF_LABEL -from .lv_validation import angle, lv_bool, lv_image, size, zoom -from .lvcode import lv -from .types import lv_img_t -from .widget import Widget, WidgetType CONF_IMAGE = "image" diff --git a/esphome/components/lvgl/keyboard.py b/esphome/components/lvgl/widgets/keyboard.py similarity index 86% rename from esphome/components/lvgl/keyboard.py rename to esphome/components/lvgl/widgets/keyboard.py index 7ce73d2170..cff322f5af 100644 --- a/esphome/components/lvgl/keyboard.py +++ b/esphome/components/lvgl/widgets/keyboard.py @@ -3,11 +3,11 @@ import esphome.config_validation as cv from esphome.const import CONF_MODE from esphome.cpp_types import std_string -from .defines import CONF_ITEMS, CONF_MAIN, KEYBOARD_MODES, literal -from .helpers import add_lv_use, lvgl_components_required +from ..defines import CONF_ITEMS, CONF_MAIN, KEYBOARD_MODES, literal +from ..helpers import add_lv_use, lvgl_components_required +from ..types import LvCompound, LvType +from . import Widget, WidgetType, get_widgets from .textarea import CONF_TEXTAREA, lv_textarea_t -from .types import LvCompound, LvType -from .widget import Widget, WidgetType, get_widgets CONF_KEYBOARD = "keyboard" diff --git a/esphome/components/lvgl/label.py b/esphome/components/lvgl/widgets/label.py similarity index 85% rename from esphome/components/lvgl/label.py rename to esphome/components/lvgl/widgets/label.py index 6c3e1f4a00..38f688f2b0 100644 --- a/esphome/components/lvgl/label.py +++ b/esphome/components/lvgl/widgets/label.py @@ -1,6 +1,6 @@ import esphome.config_validation as cv -from .defines import ( +from ..defines import ( CONF_LONG_MODE, CONF_MAIN, CONF_RECOLOR, @@ -9,10 +9,10 @@ from .defines import ( CONF_TEXT, LV_LONG_MODES, ) -from .lv_validation import lv_bool, lv_text -from .schemas import TEXT_SCHEMA -from .types import LvText, WidgetType -from .widget import Widget +from ..lv_validation import lv_bool, lv_text +from ..schemas import TEXT_SCHEMA +from ..types import LvText, WidgetType +from . import Widget CONF_LABEL = "label" diff --git a/esphome/components/lvgl/led.py b/esphome/components/lvgl/widgets/led.py similarity index 80% rename from esphome/components/lvgl/led.py rename to esphome/components/lvgl/widgets/led.py index 9b6e819278..647973c9b7 100644 --- a/esphome/components/lvgl/led.py +++ b/esphome/components/lvgl/widgets/led.py @@ -1,11 +1,11 @@ import esphome.config_validation as cv from esphome.const import CONF_BRIGHTNESS, CONF_COLOR, CONF_LED -from .defines import CONF_MAIN -from .lv_validation import lv_brightness, lv_color -from .lvcode import lv -from .types import LvType -from .widget import Widget, WidgetType +from ..defines import CONF_MAIN +from ..lv_validation import lv_brightness, lv_color +from ..lvcode import lv +from ..types import LvType +from . import Widget, WidgetType LED_SCHEMA = cv.Schema( { diff --git a/esphome/components/lvgl/line.py b/esphome/components/lvgl/widgets/line.py similarity index 85% rename from esphome/components/lvgl/line.py rename to esphome/components/lvgl/widgets/line.py index ab50832bbf..8ce4b1965f 100644 --- a/esphome/components/lvgl/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -3,11 +3,10 @@ import functools import esphome.codegen as cg import esphome.config_validation as cv -from . import defines as df -from .defines import CONF_MAIN, literal -from .lvcode import lv -from .types import LvType -from .widget import Widget, WidgetType +from ..defines import CONF_MAIN, literal +from ..lvcode import lv +from ..types import LvType +from . import Widget, WidgetType CONF_LINE = "line" CONF_POINTS = "points" @@ -32,7 +31,7 @@ def cv_point_list(value): LINE_SCHEMA = { - cv.Required(df.CONF_POINTS): cv_point_list, + cv.Required(CONF_POINTS): cv_point_list, cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t), } diff --git a/esphome/components/lvgl/lv_bar.py b/esphome/components/lvgl/widgets/lv_bar.py similarity index 81% rename from esphome/components/lvgl/lv_bar.py rename to esphome/components/lvgl/widgets/lv_bar.py index d5dcff0bf0..57209370c0 100644 --- a/esphome/components/lvgl/lv_bar.py +++ b/esphome/components/lvgl/widgets/lv_bar.py @@ -1,11 +1,13 @@ import esphome.config_validation as cv from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE -from .defines import BAR_MODES, CONF_ANIMATED, CONF_INDICATOR, CONF_MAIN, literal -from .lv_validation import animated, get_start_value, lv_float -from .lvcode import lv -from .types import LvNumber, NumberType -from .widget import Widget +from ..defines import BAR_MODES, CONF_ANIMATED, CONF_INDICATOR, CONF_MAIN, literal +from ..lv_validation import animated, get_start_value, lv_float +from ..lvcode import lv +from ..types import LvNumber, NumberType +from . import Widget + +# Note this file cannot be called "bar.py" because that name is disallowed. CONF_BAR = "bar" BAR_MODIFY_SCHEMA = cv.Schema( diff --git a/esphome/components/lvgl/meter.py b/esphome/components/lvgl/widgets/meter.py similarity index 96% rename from esphome/components/lvgl/meter.py rename to esphome/components/lvgl/widgets/meter.py index 1a6bef7c57..7cf154d6f3 100644 --- a/esphome/components/lvgl/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -14,9 +14,8 @@ from esphome.const import ( CONF_WIDTH, ) -from .arc import CONF_ARC -from .automation import action_to_code -from .defines import ( +from ..automation import action_to_code +from ..defines import ( CONF_END_VALUE, CONF_MAIN, CONF_PIVOT_X, @@ -25,10 +24,8 @@ from .defines import ( CONF_START_VALUE, CONF_TICKS, ) -from .helpers import add_lv_use -from .img import CONF_IMAGE -from .line import CONF_LINE -from .lv_validation import ( +from ..helpers import add_lv_use +from ..lv_validation import ( angle, get_end_value, get_start_value, @@ -39,10 +36,13 @@ from .lv_validation import ( requires_component, size, ) -from .lvcode import LocalVariable, lv, lv_assign, lv_expr +from ..lvcode import LocalVariable, lv, lv_assign, lv_expr +from ..types import LvType, ObjUpdateAction +from . import Widget, WidgetType, get_widgets +from .arc import CONF_ARC +from .img import CONF_IMAGE +from .line import CONF_LINE from .obj import obj_spec -from .types import LvType, ObjUpdateAction -from .widget import Widget, WidgetType, get_widgets CONF_ANGLE_RANGE = "angle_range" CONF_COLOR_END = "color_end" @@ -171,7 +171,7 @@ class MeterType(WidgetType): """For a meter object, create and set parameters""" var = w.obj - for scale_conf in config.get(CONF_SCALES) or (): + for scale_conf in config.get(CONF_SCALES, ()): rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2 if CONF_ROTATION in scale_conf: rotation = scale_conf[CONF_ROTATION] // 10 @@ -208,7 +208,7 @@ class MeterType(WidgetType): color, major[CONF_LABEL_GAP], ) - for indicator in scale_conf.get(CONF_INDICATORS) or (): + for indicator in scale_conf.get(CONF_INDICATORS, ()): (t, v) = next(iter(indicator.items())) iid = v[CONF_ID] ivar = cg.new_variable( diff --git a/esphome/components/lvgl/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py similarity index 72% rename from esphome/components/lvgl/msgbox.py rename to esphome/components/lvgl/widgets/msgbox.py index 6dd529d77f..4ae5be7701 100644 --- a/esphome/components/lvgl/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -4,16 +4,7 @@ from esphome.core import ID from esphome.cpp_generator import new_Pvariable, static_const_array from esphome.cpp_types import nullptr -from .button import button_spec -from .buttonmatrix import ( - BUTTONMATRIX_BUTTON_SCHEMA, - CONF_BUTTON_TEXT_LIST_ID, - buttonmatrix_spec, - get_button_data, - lv_buttonmatrix_t, - set_btn_data, -) -from .defines import ( +from ..defines import ( CONF_BODY, CONF_BUTTONS, CONF_CLOSE_BUTTON, @@ -23,10 +14,9 @@ from .defines import ( TYPE_FLEX, literal, ) -from .helpers import add_lv_use -from .label import CONF_LABEL -from .lv_validation import lv_bool, lv_pct, lv_text -from .lvcode import ( +from ..helpers import add_lv_use +from ..lv_validation import lv_bool, lv_pct, lv_text +from ..lvcode import ( EVENT_ARG, LambdaContext, LocalVariable, @@ -36,11 +26,21 @@ from .lvcode import ( lv_obj, lv_Pvariable, ) +from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema +from ..styles import TOP_LAYER +from ..types import LV_EVENT, char_ptr, lv_obj_t +from . import Widget, set_obj_properties +from .button import button_spec +from .buttonmatrix import ( + BUTTONMATRIX_BUTTON_SCHEMA, + CONF_BUTTON_TEXT_LIST_ID, + buttonmatrix_spec, + get_button_data, + lv_buttonmatrix_t, + set_btn_data, +) +from .label import CONF_LABEL from .obj import obj_spec -from .schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema -from .styles import TOP_LAYER -from .types import LV_EVENT, char_ptr, lv_obj_t -from .widget import Widget, set_obj_properties CONF_MSGBOX = "msgbox" MSGBOX_SCHEMA = container_schema( @@ -73,15 +73,23 @@ async def msgbox_to_code(conf): *buttonmatrix_spec.get_uses(), *button_spec.get_uses(), ) - mbid = conf[CONF_ID] - outer = lv_Pvariable(lv_obj_t, mbid.id) - btnm = new_Pvariable( - ID(f"{mbid.id}_btnm_", is_declaration=True, type=lv_buttonmatrix_t) + messagebox_id = conf[CONF_ID] + outer = lv_Pvariable(lv_obj_t, messagebox_id.id) + buttonmatrix = new_Pvariable( + ID( + f"{messagebox_id.id}_buttonmatrix_", + is_declaration=True, + type=lv_buttonmatrix_t, + ) + ) + msgbox = lv_Pvariable(lv_obj_t, f"{messagebox_id.id}_msgbox") + outer_widget = Widget.create(messagebox_id, outer, obj_spec, conf) + buttonmatrix_widget = Widget.create( + str(buttonmatrix), buttonmatrix, buttonmatrix_spec, conf + ) + text_list, ctrl_list, width_list, _ = await get_button_data( + (conf,), buttonmatrix_widget ) - msgbox = lv_Pvariable(lv_obj_t, f"{mbid.id}_msgbox") - outer_w = Widget.create(mbid, outer, obj_spec, conf) - btnm_widg = Widget.create(str(btnm), btnm, buttonmatrix_spec, conf) - text_list, ctrl_list, width_list, _ = await get_button_data((conf,), btnm_widg) text_id = conf[CONF_BUTTON_TEXT_LIST_ID] text_list = static_const_array(text_id, text_list) if (text := conf.get(CONF_BODY)) is not None: @@ -97,16 +105,16 @@ async def msgbox_to_code(conf): lv_obj.set_style_border_width(outer, 0, 0) lv_obj.set_style_pad_all(outer, 0, 0) lv_obj.set_style_radius(outer, 0, 0) - outer_w.add_flag("LV_OBJ_FLAG_HIDDEN") + outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN") lv_assign( msgbox, lv_expr.msgbox_create(outer, title, text, text_list, close_button) ) lv_obj.set_style_align(msgbox, literal("LV_ALIGN_CENTER"), 0) - lv_add(btnm.set_obj(lv_expr.msgbox_get_btns(msgbox))) - await set_obj_properties(outer_w, conf) + lv_add(buttonmatrix.set_obj(lv_expr.msgbox_get_btns(msgbox))) + await set_obj_properties(outer_widget, conf) if close_button: - async with LambdaContext(EVENT_ARG, where=mbid) as context: - outer_w.add_flag("LV_OBJ_FLAG_HIDDEN") + async with LambdaContext(EVENT_ARG, where=messagebox_id) as context: + outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN") with LocalVariable( "close_btn_", lv_obj_t, lv_expr.msgbox_get_close_btn(msgbox) ) as close_btn: @@ -119,7 +127,7 @@ async def msgbox_to_code(conf): ) if len(ctrl_list) != 0 or len(width_list) != 0: - set_btn_data(btnm.obj, ctrl_list, width_list) + set_btn_data(buttonmatrix.obj, ctrl_list, width_list) async def msgboxes_to_code(config): diff --git a/esphome/components/lvgl/obj.py b/esphome/components/lvgl/widgets/obj.py similarity index 76% rename from esphome/components/lvgl/obj.py rename to esphome/components/lvgl/widgets/obj.py index 40d7e55381..20a24c86f6 100644 --- a/esphome/components/lvgl/obj.py +++ b/esphome/components/lvgl/widgets/obj.py @@ -1,9 +1,9 @@ from esphome import automation -from .automation import update_to_code -from .defines import CONF_MAIN, CONF_OBJ -from .schemas import create_modify_schema -from .types import ObjUpdateAction, WidgetType, lv_obj_t +from ..automation import update_to_code +from ..defines import CONF_MAIN, CONF_OBJ +from ..schemas import create_modify_schema +from ..types import ObjUpdateAction, WidgetType, lv_obj_t class ObjType(WidgetType): diff --git a/esphome/components/lvgl/page.py b/esphome/components/lvgl/widgets/page.py similarity index 91% rename from esphome/components/lvgl/page.py rename to esphome/components/lvgl/widgets/page.py index 4566b7eea4..f80d802b33 100644 --- a/esphome/components/lvgl/page.py +++ b/esphome/components/lvgl/widgets/page.py @@ -2,7 +2,7 @@ from esphome import automation, codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME -from .defines import ( +from ..defines import ( CONF_ANIMATION, CONF_LVGL_ID, CONF_PAGE, @@ -10,11 +10,11 @@ from .defines import ( CONF_SKIP, LV_ANIM, ) -from .lv_validation import lv_bool, lv_milliseconds -from .lvcode import LVGL_COMP_ARG, LambdaContext, add_line_marks, lv_add, lvgl_comp -from .schemas import LVGL_SCHEMA -from .types import LvglAction, lv_page_t -from .widget import Widget, WidgetType, add_widgets, set_obj_properties +from ..lv_validation import lv_bool, lv_milliseconds +from ..lvcode import LVGL_COMP_ARG, LambdaContext, add_line_marks, lv_add, lvgl_comp +from ..schemas import LVGL_SCHEMA +from ..types import LvglAction, lv_page_t +from . import Widget, WidgetType, add_widgets, set_obj_properties class PageType(WidgetType): diff --git a/esphome/components/lvgl/roller.py b/esphome/components/lvgl/widgets/roller.py similarity index 92% rename from esphome/components/lvgl/roller.py rename to esphome/components/lvgl/widgets/roller.py index 7af3ef3c3d..50fdf6113c 100644 --- a/esphome/components/lvgl/roller.py +++ b/esphome/components/lvgl/widgets/roller.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_MODE, CONF_OPTIONS -from .defines import ( +from ..defines import ( CONF_ANIMATED, CONF_MAIN, CONF_SELECTED, @@ -11,11 +11,11 @@ from .defines import ( ROLLER_MODES, literal, ) +from ..lv_validation import animated, lv_int, option_string +from ..lvcode import lv +from ..types import LvSelect +from . import WidgetType from .label import CONF_LABEL -from .lv_validation import animated, lv_int, option_string -from .lvcode import lv -from .types import LvSelect -from .widget import WidgetType CONF_ROLLER = "roller" lv_roller_t = LvSelect("lv_roller_t") diff --git a/esphome/components/lvgl/slider.py b/esphome/components/lvgl/widgets/slider.py similarity index 88% rename from esphome/components/lvgl/slider.py rename to esphome/components/lvgl/widgets/slider.py index 1886f79b44..d5017668e4 100644 --- a/esphome/components/lvgl/slider.py +++ b/esphome/components/lvgl/widgets/slider.py @@ -1,7 +1,7 @@ import esphome.config_validation as cv from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE -from .defines import ( +from ..defines import ( BAR_MODES, CONF_ANIMATED, CONF_INDICATOR, @@ -9,12 +9,12 @@ from .defines import ( CONF_MAIN, literal, ) -from .helpers import add_lv_use +from ..helpers import add_lv_use +from ..lv_validation import animated, get_start_value, lv_float +from ..lvcode import lv +from ..types import LvNumber, NumberType +from . import Widget from .lv_bar import CONF_BAR -from .lv_validation import animated, get_start_value, lv_float -from .lvcode import lv -from .types import LvNumber, NumberType -from .widget import Widget CONF_SLIDER = "slider" SLIDER_MODIFY_SCHEMA = cv.Schema( diff --git a/esphome/components/lvgl/spinbox.py b/esphome/components/lvgl/widgets/spinbox.py similarity index 95% rename from esphome/components/lvgl/spinbox.py rename to esphome/components/lvgl/widgets/spinbox.py index 62c58c54a3..b84dc7cd23 100644 --- a/esphome/components/lvgl/spinbox.py +++ b/esphome/components/lvgl/widgets/spinbox.py @@ -2,8 +2,8 @@ from esphome import automation import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_RANGE_FROM, CONF_RANGE_TO, CONF_STEP, CONF_VALUE -from .automation import action_to_code, update_to_code -from .defines import ( +from ..automation import action_to_code, update_to_code +from ..defines import ( CONF_CURSOR, CONF_DECIMAL_PLACES, CONF_DIGITS, @@ -13,12 +13,12 @@ from .defines import ( CONF_SELECTED, CONF_TEXTAREA_PLACEHOLDER, ) +from ..lv_validation import lv_bool, lv_float +from ..lvcode import lv +from ..types import LvNumber, ObjUpdateAction +from . import Widget, WidgetType, get_widgets from .label import CONF_LABEL -from .lv_validation import lv_bool, lv_float -from .lvcode import lv from .textarea import CONF_TEXTAREA -from .types import LvNumber, ObjUpdateAction -from .widget import Widget, WidgetType, get_widgets CONF_SPINBOX = "spinbox" diff --git a/esphome/components/lvgl/spinner.py b/esphome/components/lvgl/widgets/spinner.py similarity index 82% rename from esphome/components/lvgl/spinner.py rename to esphome/components/lvgl/widgets/spinner.py index 2f798d0fbf..2940feb594 100644 --- a/esphome/components/lvgl/spinner.py +++ b/esphome/components/lvgl/widgets/spinner.py @@ -1,12 +1,12 @@ import esphome.config_validation as cv from esphome.cpp_generator import MockObjClass +from ..defines import CONF_ARC_LENGTH, CONF_INDICATOR, CONF_MAIN, CONF_SPIN_TIME +from ..lv_validation import angle +from ..lvcode import lv_expr +from ..types import LvType +from . import Widget, WidgetType from .arc import CONF_ARC -from .defines import CONF_ARC_LENGTH, CONF_INDICATOR, CONF_MAIN, CONF_SPIN_TIME -from .lv_validation import angle -from .lvcode import lv_expr -from .types import LvType -from .widget import Widget, WidgetType CONF_SPINNER = "spinner" diff --git a/esphome/components/lvgl/lv_switch.py b/esphome/components/lvgl/widgets/switch.py similarity index 72% rename from esphome/components/lvgl/lv_switch.py rename to esphome/components/lvgl/widgets/switch.py index 5db2c2ce38..a7c1356bf2 100644 --- a/esphome/components/lvgl/lv_switch.py +++ b/esphome/components/lvgl/widgets/switch.py @@ -1,6 +1,6 @@ -from .defines import CONF_INDICATOR, CONF_KNOB, CONF_MAIN -from .types import LvBoolean -from .widget import WidgetType +from ..defines import CONF_INDICATOR, CONF_KNOB, CONF_MAIN +from ..types import LvBoolean +from . import WidgetType CONF_SWITCH = "switch" diff --git a/esphome/components/lvgl/tabview.py b/esphome/components/lvgl/widgets/tabview.py similarity index 88% rename from esphome/components/lvgl/tabview.py rename to esphome/components/lvgl/widgets/tabview.py index 7b6a864e21..226fc3f286 100644 --- a/esphome/components/lvgl/tabview.py +++ b/esphome/components/lvgl/widgets/tabview.py @@ -4,9 +4,8 @@ import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_INDEX, CONF_NAME, CONF_POSITION, CONF_SIZE from esphome.cpp_generator import MockObjClass -from . import buttonmatrix_spec -from .automation import action_to_code -from .defines import ( +from ..automation import action_to_code +from ..defines import ( CONF_ANIMATED, CONF_MAIN, CONF_TAB_ID, @@ -15,12 +14,13 @@ from .defines import ( TYPE_FLEX, literal, ) -from .lv_validation import animated, lv_int, size -from .lvcode import LocalVariable, lv, lv_assign, lv_expr +from ..lv_validation import animated, lv_int, size +from ..lvcode import LocalVariable, lv, lv_assign, lv_expr +from ..schemas import container_schema, part_schema +from ..types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr +from . import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties +from .buttonmatrix import buttonmatrix_spec from .obj import obj_spec -from .schemas import container_schema, part_schema -from .types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr -from .widget import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties CONF_TABVIEW = "tabview" CONF_TAB_STYLE = "tab_style" diff --git a/esphome/components/lvgl/textarea.py b/esphome/components/lvgl/widgets/textarea.py similarity index 90% rename from esphome/components/lvgl/textarea.py rename to esphome/components/lvgl/widgets/textarea.py index d383e1f098..61d83dee9c 100644 --- a/esphome/components/lvgl/textarea.py +++ b/esphome/components/lvgl/widgets/textarea.py @@ -1,7 +1,7 @@ import esphome.config_validation as cv from esphome.const import CONF_MAX_LENGTH -from .defines import ( +from ..defines import ( CONF_ACCEPTED_CHARS, CONF_CURSOR, CONF_MAIN, @@ -13,10 +13,10 @@ from .defines import ( CONF_TEXT, CONF_TEXTAREA_PLACEHOLDER, ) -from .lv_validation import lv_bool, lv_int, lv_text -from .schemas import TEXT_SCHEMA -from .types import LvText -from .widget import Widget, WidgetType +from ..lv_validation import lv_bool, lv_int, lv_text +from ..schemas import TEXT_SCHEMA +from ..types import LvText +from . import Widget, WidgetType CONF_TEXTAREA = "textarea" diff --git a/esphome/components/lvgl/tileview.py b/esphome/components/lvgl/widgets/tileview.py similarity index 89% rename from esphome/components/lvgl/tileview.py rename to esphome/components/lvgl/widgets/tileview.py index aa841fa23e..9a426c7daf 100644 --- a/esphome/components/lvgl/tileview.py +++ b/esphome/components/lvgl/widgets/tileview.py @@ -3,8 +3,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_ON_VALUE, CONF_ROW, CONF_TRIGGER_ID -from .automation import action_to_code -from .defines import ( +from ..automation import action_to_code +from ..defines import ( CONF_ANIMATED, CONF_COLUMN, CONF_DIR, @@ -14,12 +14,12 @@ from .defines import ( TILE_DIRECTIONS, literal, ) -from .lv_validation import animated, lv_int -from .lvcode import lv, lv_assign, lv_expr, lv_obj, lv_Pvariable +from ..lv_validation import animated, lv_int +from ..lvcode import lv, lv_assign, lv_expr, lv_obj, lv_Pvariable +from ..schemas import container_schema +from ..types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr +from . import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties from .obj import obj_spec -from .schemas import container_schema -from .types import LV_EVENT, LvType, ObjUpdateAction, lv_obj_t, lv_obj_t_ptr -from .widget import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties CONF_TILEVIEW = "tileview" @@ -68,7 +68,7 @@ class TileviewType(WidgetType): ) async def to_code(self, w: Widget, config: dict): - for tile_conf in config.get(CONF_TILES) or (): + for tile_conf in config.get(CONF_TILES, ()): w_id = tile_conf[CONF_ID] tile_obj = lv_Pvariable(lv_obj_t, w_id) tile = Widget.create(w_id, tile_obj, tile_spec, tile_conf) diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 6d0c1967b4..35d924d939 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -54,3 +54,75 @@ font: "\U000f0084", "\U000f0091", ] + +sensor: + - platform: lvgl + id: lvgl_sensor_id + name: "LVGL Arc Sensor" + widget: lv_arc + - platform: lvgl + widget: slider_id + name: LVGL Slider + - platform: lvgl + widget: bar_id + id: lvgl_bar_sensor + name: LVGL Bar + - platform: lvgl + widget: spinbox_id + name: LVGL Spinbox + +number: + - platform: lvgl + widget: slider_id + name: LVGL Slider + - platform: lvgl + widget: lv_arc + id: lvgl_arc_number + name: LVGL Arc + - platform: lvgl + widget: bar_id + id: lvgl_bar_number + name: LVGL Bar + - platform: lvgl + widget: spinbox_id + id: lvgl_spinbox_number + name: LVGL Spinbox + +light: + - platform: lvgl + name: LVGL LED + id: lv_light + led: lv_led + +binary_sensor: + - platform: lvgl + id: lvgl_pressbutton + name: Pressbutton + widget: spin_up + publish_initial_state: true + - platform: lvgl + name: ButtonMatrix button + widget: button_a + - platform: lvgl + id: switch_d + name: Matrix switch D + widget: button_d + on_click: + then: + - lvgl.page.previous: + animation: move_right + time: 600ms + - platform: lvgl + id: button_checker + name: LVGL button + widget: spin_up + on_state: + then: + - lvgl.checkbox.update: + id: checkbox_id + state: + checked: !lambda return x; + text: Unchecked + - platform: lvgl + name: LVGL checkbox + widget: checkbox_id From e6b1780a31d54b46628c919f90433db4e7681ec4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Aug 2024 21:39:47 +1200 Subject: [PATCH 0139/1052] Move ``CONF_BACKGROUND_COLOR`` and ``CONF_FOREGROUND_COLOR`` to const.py (#7202) --- .../graphical_display_menu/__init__.py | 17 ++++++++++------- esphome/components/nextion/base_component.py | 13 +++++-------- esphome/const.py | 2 ++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/esphome/components/graphical_display_menu/__init__.py b/esphome/components/graphical_display_menu/__init__.py index 1b3ed7f8cd..d7146a7381 100644 --- a/esphome/components/graphical_display_menu/__init__.py +++ b/esphome/components/graphical_display_menu/__init__.py @@ -1,19 +1,22 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import display, font, color -from esphome.const import CONF_DISPLAY, CONF_ID, CONF_TRIGGER_ID from esphome import automation, core - +import esphome.codegen as cg +from esphome.components import color, display, font from esphome.components.display_menu_base import ( DISPLAY_MENU_BASE_SCHEMA, DisplayMenuComponent, display_menu_to_code, ) +import esphome.config_validation as cv +from esphome.const import ( + CONF_BACKGROUND_COLOR, + CONF_DISPLAY, + CONF_FOREGROUND_COLOR, + CONF_ID, + CONF_TRIGGER_ID, +) CONF_FONT = "font" CONF_MENU_ITEM_VALUE = "menu_item_value" -CONF_FOREGROUND_COLOR = "foreground_color" -CONF_BACKGROUND_COLOR = "background_color" CONF_ON_REDRAW = "on_redraw" graphical_display_menu_ns = cg.esphome_ns.namespace("graphical_display_menu") diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index 784da35371..d12434ec8f 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -1,12 +1,11 @@ from string import ascii_letters, digits -import esphome.config_validation as cv + import esphome.codegen as cg from esphome.components import color -from esphome.const import ( - CONF_VISIBLE, -) -from . import CONF_NEXTION_ID -from . import Nextion +import esphome.config_validation as cv +from esphome.const import CONF_BACKGROUND_COLOR, CONF_FOREGROUND_COLOR, CONF_VISIBLE + +from . import CONF_NEXTION_ID, Nextion CONF_VARIABLE_NAME = "variable_name" CONF_COMPONENT_NAME = "component_name" @@ -24,9 +23,7 @@ CONF_WAKE_UP_PAGE = "wake_up_page" CONF_START_UP_PAGE = "start_up_page" CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch" CONF_WAVE_MAX_LENGTH = "wave_max_length" -CONF_BACKGROUND_COLOR = "background_color" CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color" -CONF_FOREGROUND_COLOR = "foreground_color" CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" CONF_FONT_ID = "font_id" CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" diff --git a/esphome/const.py b/esphome/const.py index fcb630badd..d7b1f558a1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -74,6 +74,7 @@ CONF_AWAY = "away" CONF_AWAY_COMMAND_TOPIC = "away_command_topic" CONF_AWAY_CONFIG = "away_config" CONF_AWAY_STATE_TOPIC = "away_state_topic" +CONF_BACKGROUND_COLOR = "background_color" CONF_BACKLIGHT_PIN = "backlight_pin" CONF_BASELINE = "baseline" CONF_BATTERY_LEVEL = "battery_level" @@ -309,6 +310,7 @@ CONF_FLOW = "flow" CONF_FLOW_CONTROL_PIN = "flow_control_pin" CONF_FOR = "for" CONF_FORCE_UPDATE = "force_update" +CONF_FOREGROUND_COLOR = "foreground_color" CONF_FORMALDEHYDE = "formaldehyde" CONF_FORMAT = "format" CONF_FORWARD_ACTIVE_ENERGY = "forward_active_energy" From b0d9800817921fe98f888b296596f3a4ccf39e18 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 6 Aug 2024 22:02:08 +1200 Subject: [PATCH 0140/1052] [helpers] Set default flags of ExternalRAMAllocator to ALLOW_FAILURE (#7201) --- esphome/core/helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index b4ad22b083..3e6fe9433e 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -680,7 +680,7 @@ template class ExternalRAMAllocator { } private: - Flags flags_{Flags::NONE}; + Flags flags_{Flags::ALLOW_FAILURE}; }; /// @} From 9188836f707db9924c95233cb604dbc51a8fa24f Mon Sep 17 00:00:00 2001 From: guillempages Date: Tue, 6 Aug 2024 13:08:06 +0200 Subject: [PATCH 0141/1052] Add runtime online image support (#4710) --- CODEOWNERS | 1 + esphome/components/online_image/__init__.py | 161 ++++++++++ .../components/online_image/image_decoder.cpp | 44 +++ .../components/online_image/image_decoder.h | 112 +++++++ .../components/online_image/online_image.cpp | 275 ++++++++++++++++++ .../components/online_image/online_image.h | 184 ++++++++++++ esphome/components/online_image/png_image.cpp | 68 +++++ esphome/components/online_image/png_image.h | 33 +++ esphome/core/defines.h | 1 + platformio.ini | 1 + .../components/online_image/common-esp32.yaml | 18 ++ .../online_image/common-esp8266.yaml | 18 ++ tests/components/online_image/common.yaml | 37 +++ .../online_image/test.esp32-ard.yaml | 4 + .../online_image/test.esp32-idf.yaml | 4 + 15 files changed, 961 insertions(+) create mode 100644 esphome/components/online_image/__init__.py create mode 100644 esphome/components/online_image/image_decoder.cpp create mode 100644 esphome/components/online_image/image_decoder.h create mode 100644 esphome/components/online_image/online_image.cpp create mode 100644 esphome/components/online_image/online_image.h create mode 100644 esphome/components/online_image/png_image.cpp create mode 100644 esphome/components/online_image/png_image.h create mode 100644 tests/components/online_image/common-esp32.yaml create mode 100644 tests/components/online_image/common-esp8266.yaml create mode 100644 tests/components/online_image/common.yaml create mode 100644 tests/components/online_image/test.esp32-ard.yaml create mode 100644 tests/components/online_image/test.esp32-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index d94c34c019..82e6e0ea4b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -276,6 +276,7 @@ esphome/components/nfc/* @jesserockz @kbx81 esphome/components/noblex/* @AGalfra esphome/components/number/* @esphome/core esphome/components/one_wire/* @ssieb +esphome/components/online_image/* @guillempages esphome/components/ota/* @esphome/core esphome/components/output/* @esphome/core esphome/components/pca6416a/* @Mat931 diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py new file mode 100644 index 0000000000..ee5357457a --- /dev/null +++ b/esphome/components/online_image/__init__.py @@ -0,0 +1,161 @@ +import logging + +from esphome import automation +import esphome.codegen as cg +from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent +from esphome.components.image import ( + CONF_USE_TRANSPARENCY, + IMAGE_TYPE, + Image_, + validate_cross_dependencies, +) +import esphome.config_validation as cv +from esphome.const import ( + CONF_BUFFER_SIZE, + CONF_FORMAT, + CONF_ID, + CONF_ON_ERROR, + CONF_RESIZE, + CONF_TRIGGER_ID, + CONF_TYPE, + CONF_URL, +) + +AUTO_LOAD = ["image"] +DEPENDENCIES = ["display", "http_request"] +CODEOWNERS = ["@guillempages"] +MULTI_CONF = True + +CONF_ON_DOWNLOAD_FINISHED = "on_download_finished" + +_LOGGER = logging.getLogger(__name__) + +online_image_ns = cg.esphome_ns.namespace("online_image") + +ImageFormat = online_image_ns.enum("ImageFormat") + +FORMAT_PNG = "PNG" + +IMAGE_FORMAT = {FORMAT_PNG: ImageFormat.PNG} # Add new supported formats here + +OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_) + +# Actions +SetUrlAction = online_image_ns.class_( + "OnlineImageSetUrlAction", automation.Action, cg.Parented.template(OnlineImage) +) +ReleaseImageAction = online_image_ns.class_( + "OnlineImageReleaseAction", automation.Action, cg.Parented.template(OnlineImage) +) + +# Triggers +DownloadFinishedTrigger = online_image_ns.class_( + "DownloadFinishedTrigger", automation.Trigger.template() +) +DownloadErrorTrigger = online_image_ns.class_( + "DownloadErrorTrigger", automation.Trigger.template() +) + +ONLINE_IMAGE_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(OnlineImage), + cv.GenerateID(CONF_HTTP_REQUEST_ID): cv.use_id(HttpRequestComponent), + # + # Common image options + # + cv.Optional(CONF_RESIZE): cv.dimensions, + cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(IMAGE_TYPE, upper=True), + # Not setting default here on purpose; the default depends on the image type, + # and thus will be set in the "validate_cross_dependencies" validator. + cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean, + # + # Online Image specific options + # + cv.Required(CONF_URL): cv.url, + cv.Required(CONF_FORMAT): cv.enum(IMAGE_FORMAT, upper=True), + cv.Optional(CONF_BUFFER_SIZE, default=2048): cv.int_range(256, 65536), + cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadFinishedTrigger), + } + ), + cv.Optional(CONF_ON_ERROR): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadErrorTrigger), + } + ), + } +).extend(cv.polling_component_schema("never")) + +CONFIG_SCHEMA = cv.Schema( + cv.All( + ONLINE_IMAGE_SCHEMA, + validate_cross_dependencies, + cv.require_framework_version( + # esp8266 not supported yet; if enabled in the future, minimum version of 2.7.0 is needed + # esp8266_arduino=cv.Version(2, 7, 0), + esp32_arduino=cv.Version(0, 0, 0), + esp_idf=cv.Version(4, 0, 0), + ), + ) +) + +SET_URL_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(OnlineImage), + cv.Required(CONF_URL): cv.templatable(cv.url), + } +) + +RELEASE_IMAGE_SCHEMA = automation.maybe_simple_id( + { + cv.GenerateID(): cv.use_id(OnlineImage), + } +) + + +@automation.register_action("online_image.set_url", SetUrlAction, SET_URL_SCHEMA) +@automation.register_action( + "online_image.release", ReleaseImageAction, RELEASE_IMAGE_SCHEMA +) +async def online_image_action_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + if CONF_URL in config: + template_ = await cg.templatable(config[CONF_URL], args, cg.const_char_ptr) + cg.add(var.set_url(template_)) + return var + + +async def to_code(config): + format = config[CONF_FORMAT] + if format in [FORMAT_PNG]: + cg.add_define("USE_ONLINE_IMAGE_PNG_SUPPORT") + cg.add_library("pngle", "1.0.2") + + url = config[CONF_URL] + width, height = config.get(CONF_RESIZE, (0, 0)) + transparent = config[CONF_USE_TRANSPARENCY] + + var = cg.new_Pvariable( + config[CONF_ID], + url, + width, + height, + format, + config[CONF_TYPE], + config[CONF_BUFFER_SIZE], + ) + await cg.register_component(var, config) + await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID]) + + cg.add(var.set_transparency(transparent)) + + for conf in config.get(CONF_ON_DOWNLOAD_FINISHED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_ERROR, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) diff --git a/esphome/components/online_image/image_decoder.cpp b/esphome/components/online_image/image_decoder.cpp new file mode 100644 index 0000000000..50ec39dfcc --- /dev/null +++ b/esphome/components/online_image/image_decoder.cpp @@ -0,0 +1,44 @@ +#include "image_decoder.h" +#include "online_image.h" + +#include "esphome/core/log.h" + +namespace esphome { +namespace online_image { + +static const char *const TAG = "online_image.decoder"; + +void ImageDecoder::set_size(int width, int height) { + this->image_->resize_(width, height); + this->x_scale_ = static_cast(this->image_->buffer_width_) / width; + this->y_scale_ = static_cast(this->image_->buffer_height_) / height; +} + +void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { + auto width = std::min(this->image_->buffer_width_, static_cast(std::ceil((x + w) * this->x_scale_))); + auto height = std::min(this->image_->buffer_height_, static_cast(std::ceil((y + h) * this->y_scale_))); + for (int i = x * this->x_scale_; i < width; i++) { + for (int j = y * this->y_scale_; j < height; j++) { + this->image_->draw_pixel_(i, j, color); + } + } +} + +uint8_t *DownloadBuffer::data(size_t offset) { + if (offset > this->size_) { + ESP_LOGE(TAG, "Tried to access beyond download buffer bounds!!!"); + return this->buffer_; + } + return this->buffer_ + offset; +} + +size_t DownloadBuffer::read(size_t len) { + this->unread_ -= len; + if (this->unread_ > 0) { + memmove(this->data(), this->data(len), this->unread_); + } + return this->unread_; +} + +} // namespace online_image +} // namespace esphome diff --git a/esphome/components/online_image/image_decoder.h b/esphome/components/online_image/image_decoder.h new file mode 100644 index 0000000000..908efab987 --- /dev/null +++ b/esphome/components/online_image/image_decoder.h @@ -0,0 +1,112 @@ +#pragma once +#include "esphome/core/defines.h" +#include "esphome/core/color.h" + +namespace esphome { +namespace online_image { + +class OnlineImage; + +/** + * @brief Class to abstract decoding different image formats. + */ +class ImageDecoder { + public: + /** + * @brief Construct a new Image Decoder object + * + * @param image The image to decode the stream into. + */ + ImageDecoder(OnlineImage *image) : image_(image) {} + virtual ~ImageDecoder() = default; + + /** + * @brief Initialize the decoder. + * + * @param download_size The total number of bytes that need to be download for the image. + */ + virtual void prepare(uint32_t download_size) { this->download_size_ = download_size; } + + /** + * @brief Decode a part of the image. It will try reading from the buffer. + * There is no guarantee that the whole available buffer will be read/decoded; + * the method will return the amount of bytes actually decoded, so that the + * unread content can be moved to the beginning. + * + * @param buffer The buffer to read from. + * @param size The maximum amount of bytes that can be read from the buffer. + * @return int The amount of bytes read. It can be 0 if the buffer does not have enough content to meaningfully + * decode anything, or negative in case of a decoding error. + */ + virtual int decode(uint8_t *buffer, size_t size); + + /** + * @brief Request the image to be resized once the actual dimensions are known. + * Called by the callback functions, to be able to access the parent Image class. + * + * @param width The image's width. + * @param height The image's height. + */ + void set_size(int width, int height); + + /** + * @brief Draw a rectangle on the display_buffer using the defined color. + * Will check the given coordinates for out-of-bounds, and clip the rectangle accordingly. + * In case of binary displays, the color will be converted to binary as well. + * Called by the callback functions, to be able to access the parent Image class. + * + * @param x The left-most coordinate of the rectangle. + * @param y The top-most coordinate of the rectangle. + * @param w The width of the rectangle. + * @param h The height of the rectangle. + * @param color The color to draw the rectangle with. + */ + void draw(int x, int y, int w, int h, const Color &color); + + bool is_finished() const { return this->decoded_bytes_ == this->download_size_; } + + protected: + OnlineImage *image_; + // Initializing to 1, to ensure it is different than initial "decoded_bytes_". + // Will be overwritten anyway once the download size is known. + uint32_t download_size_ = 1; + uint32_t decoded_bytes_ = 0; + double x_scale_ = 1.0; + double y_scale_ = 1.0; +}; + +class DownloadBuffer { + public: + DownloadBuffer(size_t size) : size_(size) { + this->buffer_ = this->allocator_.allocate(size); + this->reset(); + } + + virtual ~DownloadBuffer() { this->allocator_.deallocate(this->buffer_, this->size_); } + + uint8_t *data(size_t offset = 0); + + uint8_t *append() { return this->data(this->unread_); } + + size_t unread() const { return this->unread_; } + size_t size() const { return this->size_; } + size_t free_capacity() const { return this->size_ - this->unread_; } + + size_t read(size_t len); + size_t write(size_t len) { + this->unread_ += len; + return this->unread_; + } + + void reset() { this->unread_ = 0; } + + protected: + ExternalRAMAllocator allocator_; + uint8_t *buffer_; + size_t size_; + /** Total number of downloaded bytes not yet read. */ + size_t unread_; +}; + +} // namespace online_image +} // namespace esphome diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp new file mode 100644 index 0000000000..a4cf0158aa --- /dev/null +++ b/esphome/components/online_image/online_image.cpp @@ -0,0 +1,275 @@ +#include "online_image.h" + +#include "esphome/core/log.h" + +static const char *const TAG = "online_image"; + +#include "image_decoder.h" + +#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT +#include "png_image.h" +#endif + +namespace esphome { +namespace online_image { + +using image::ImageType; + +inline bool is_color_on(const Color &color) { + // This produces the most accurate monochrome conversion, but is slightly slower. + // return (0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b) > 127; + + // Approximation using fast integer computations; produces acceptable results + // Equivalent to 0.25 * R + 0.5 * G + 0.25 * B + return ((color.r >> 2) + (color.g >> 1) + (color.b >> 2)) & 0x80; +} + +OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type, + uint32_t download_buffer_size) + : Image(nullptr, 0, 0, type), + buffer_(nullptr), + download_buffer_(download_buffer_size), + format_(format), + fixed_width_(width), + fixed_height_(height) { + this->set_url(url); +} + +void OnlineImage::release() { + if (this->buffer_) { + ESP_LOGD(TAG, "Deallocating old buffer..."); + this->allocator_.deallocate(this->buffer_, this->get_buffer_size_()); + this->data_start_ = nullptr; + this->buffer_ = nullptr; + this->width_ = 0; + this->height_ = 0; + this->buffer_width_ = 0; + this->buffer_height_ = 0; + this->end_connection_(); + } +} + +bool OnlineImage::resize_(int width_in, int height_in) { + int width = this->fixed_width_; + int height = this->fixed_height_; + if (this->auto_resize_()) { + width = width_in; + height = height_in; + if (this->width_ != width && this->height_ != height) { + this->release(); + } + } + if (this->buffer_) { + return false; + } + auto new_size = this->get_buffer_size_(width, height); + ESP_LOGD(TAG, "Allocating new buffer of %d Bytes...", new_size); + delay_microseconds_safe(2000); + this->buffer_ = this->allocator_.allocate(new_size); + if (this->buffer_) { + this->buffer_width_ = width; + this->buffer_height_ = height; + this->width_ = width; + ESP_LOGD(TAG, "New size: (%d, %d)", width, height); + } else { +#if defined(USE_ESP8266) + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + int max_block = ESP.getMaxFreeBlockSize(); +#elif defined(USE_ESP32) + int max_block = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); +#else + int max_block = -1; +#endif + ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %d Bytes", max_block); + this->end_connection_(); + return false; + } + return true; +} + +void OnlineImage::update() { + if (this->decoder_) { + ESP_LOGW(TAG, "Image already being updated."); + return; + } else { + ESP_LOGI(TAG, "Updating image"); + } + + this->downloader_ = this->parent_->get(this->url_); + + if (this->downloader_ == nullptr) { + ESP_LOGE(TAG, "Download failed."); + this->end_connection_(); + this->download_error_callback_.call(); + return; + } + + int http_code = this->downloader_->status_code; + if (http_code == HTTP_CODE_NOT_MODIFIED) { + // Image hasn't changed on server. Skip download. + this->end_connection_(); + return; + } + if (http_code != HTTP_CODE_OK) { + ESP_LOGE(TAG, "HTTP result: %d", http_code); + this->end_connection_(); + this->download_error_callback_.call(); + return; + } + + ESP_LOGD(TAG, "Starting download"); + size_t total_size = this->downloader_->content_length; + +#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT + if (this->format_ == ImageFormat::PNG) { + this->decoder_ = esphome::make_unique(this); + } +#endif // ONLINE_IMAGE_PNG_SUPPORT + + if (!this->decoder_) { + ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported."); + this->end_connection_(); + this->download_error_callback_.call(); + return; + } + this->decoder_->prepare(total_size); + ESP_LOGI(TAG, "Downloading image"); +} + +void OnlineImage::loop() { + if (!this->decoder_) { + // Not decoding at the moment => nothing to do. + return; + } + if (!this->downloader_ || this->decoder_->is_finished()) { + ESP_LOGD(TAG, "Image fully downloaded"); + this->data_start_ = buffer_; + this->width_ = buffer_width_; + this->height_ = buffer_height_; + this->end_connection_(); + this->download_finished_callback_.call(); + return; + } + if (this->downloader_ == nullptr) { + ESP_LOGE(TAG, "Downloader not instantiated; cannot download"); + return; + } + size_t available = this->download_buffer_.free_capacity(); + if (available) { + auto len = this->downloader_->read(this->download_buffer_.append(), available); + if (len > 0) { + this->download_buffer_.write(len); + auto fed = this->decoder_->decode(this->download_buffer_.data(), this->download_buffer_.unread()); + if (fed < 0) { + ESP_LOGE(TAG, "Error when decoding image."); + this->end_connection_(); + this->download_error_callback_.call(); + return; + } + this->download_buffer_.read(fed); + } + } +} + +void OnlineImage::draw_pixel_(int x, int y, Color color) { + if (!this->buffer_) { + ESP_LOGE(TAG, "Buffer not allocated!"); + return; + } + if (x < 0 || y < 0 || x >= this->buffer_width_ || y >= this->buffer_height_) { + ESP_LOGE(TAG, "Tried to paint a pixel (%d,%d) outside the image!", x, y); + return; + } + uint32_t pos = this->get_position_(x, y); + switch (this->type_) { + case ImageType::IMAGE_TYPE_BINARY: { + const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; + const uint32_t pos = x + y * width_8; + if ((this->has_transparency() && color.w > 127) || is_color_on(color)) { + this->buffer_[pos / 8u] |= (0x80 >> (pos % 8u)); + } else { + this->buffer_[pos / 8u] &= ~(0x80 >> (pos % 8u)); + } + break; + } + case ImageType::IMAGE_TYPE_GRAYSCALE: { + uint8_t gray = static_cast(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b); + if (this->has_transparency()) { + if (gray == 1) { + gray = 0; + } + if (color.w < 0x80) { + gray = 1; + } + } + this->buffer_[pos] = gray; + break; + } + case ImageType::IMAGE_TYPE_RGB565: { + uint16_t col565 = display::ColorUtil::color_to_565(color); + if (this->has_transparency()) { + if (col565 == 0x0020) { + col565 = 0; + } + if (color.w < 0x80) { + col565 = 0x0020; + } + } + this->buffer_[pos + 0] = static_cast((col565 >> 8) & 0xFF); + this->buffer_[pos + 1] = static_cast(col565 & 0xFF); + break; + } + case ImageType::IMAGE_TYPE_RGBA: { + this->buffer_[pos + 0] = color.r; + this->buffer_[pos + 1] = color.g; + this->buffer_[pos + 2] = color.b; + this->buffer_[pos + 3] = color.w; + break; + } + case ImageType::IMAGE_TYPE_RGB24: + default: { + if (this->has_transparency()) { + if (color.b == 1 && color.r == 0 && color.g == 0) { + color.b = 0; + } + if (color.w < 0x80) { + color.r = 0; + color.g = 0; + color.b = 1; + } + } + this->buffer_[pos + 0] = color.r; + this->buffer_[pos + 1] = color.g; + this->buffer_[pos + 2] = color.b; + break; + } + } +} + +void OnlineImage::end_connection_() { + if (this->downloader_) { + this->downloader_->end(); + this->downloader_ = nullptr; + } + this->decoder_.reset(); + this->download_buffer_.reset(); +} + +bool OnlineImage::validate_url_(const std::string &url) { + if ((url.length() < 8) || (url.find("http") != 0) || (url.find("://") == std::string::npos)) { + ESP_LOGE(TAG, "URL is invalid and/or must be prefixed with 'http://' or 'https://'"); + return false; + } + return true; +} + +void OnlineImage::add_on_finished_callback(std::function &&callback) { + this->download_finished_callback_.add(std::move(callback)); +} + +void OnlineImage::add_on_error_callback(std::function &&callback) { + this->download_error_callback_.add(std::move(callback)); +} + +} // namespace online_image +} // namespace esphome diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h new file mode 100644 index 0000000000..30e97760ea --- /dev/null +++ b/esphome/components/online_image/online_image.h @@ -0,0 +1,184 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/components/http_request/http_request.h" +#include "esphome/components/image/image.h" + +#include "image_decoder.h" + +namespace esphome { +namespace online_image { + +using t_http_codes = enum { + HTTP_CODE_OK = 200, + HTTP_CODE_NOT_MODIFIED = 304, + HTTP_CODE_NOT_FOUND = 404, +}; + +/** + * @brief Format that the image is encoded with. + */ +enum ImageFormat { + /** Automatically detect from MIME type. Not supported yet. */ + AUTO, + /** JPEG format. Not supported yet. */ + JPEG, + /** PNG format. */ + PNG, +}; + +/** + * @brief Download an image from a given URL, and decode it using the specified decoder. + * The image will then be stored in a buffer, so that it can be re-displayed without the + * need to re-download or re-decode. + */ +class OnlineImage : public PollingComponent, + public image::Image, + public Parented { + public: + /** + * @brief Construct a new OnlineImage object. + * + * @param url URL to download the image from. + * @param width Desired width of the target image area. + * @param height Desired height of the target image area. + * @param format Format that the image is encoded in (@see ImageFormat). + * @param buffer_size Size of the buffer used to download the image. + */ + OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type, + uint32_t buffer_size); + + void update() override; + void loop() override; + + /** Set the URL to download the image from. */ + void set_url(const std::string &url) { + if (this->validate_url_(url)) { + this->url_ = url; + } + } + + /** + * Release the buffer storing the image. The image will need to be downloaded again + * to be able to be displayed. + */ + void release(); + + void add_on_finished_callback(std::function &&callback); + void add_on_error_callback(std::function &&callback); + + protected: + bool validate_url_(const std::string &url); + + using Allocator = ExternalRAMAllocator; + Allocator allocator_{Allocator::Flags::ALLOW_FAILURE}; + + uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); } + int get_buffer_size_(int width, int height) const { + return std::ceil(image::image_type_to_bpp(this->type_) * width * height / 8.0); + } + + int get_position_(int x, int y) const { + return ((x + y * this->buffer_width_) * image::image_type_to_bpp(this->type_)) / 8; + } + + ESPHOME_ALWAYS_INLINE bool auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } + + bool resize_(int width, int height); + + /** + * @brief Draw a pixel into the buffer. + * + * This is used by the decoder to fill the buffer that will later be displayed + * by the `draw` method. This will internally convert the supplied 32 bit RGBA + * color into the requested image storage format. + * + * @param x Horizontal pixel position. + * @param y Vertical pixel position. + * @param color 32 bit color to put into the pixel. + */ + void draw_pixel_(int x, int y, Color color); + + void end_connection_(); + + CallbackManager download_finished_callback_{}; + CallbackManager download_error_callback_{}; + + std::shared_ptr downloader_{nullptr}; + std::unique_ptr decoder_{nullptr}; + + uint8_t *buffer_; + DownloadBuffer download_buffer_; + + const ImageFormat format_; + + std::string url_{""}; + + /** width requested on configuration, or 0 if non specified. */ + const int fixed_width_; + /** height requested on configuration, or 0 if non specified. */ + const int fixed_height_; + /** + * Actual width of the current image. If fixed_width_ is specified, + * this will be equal to it; otherwise it will be set once the decoding + * starts and the original size is known. + * This needs to be separate from "BaseImage::get_width()" because the latter + * must return 0 until the image has been decoded (to avoid showing partially + * decoded images). + */ + int buffer_width_; + /** + * Actual height of the current image. If fixed_height_ is specified, + * this will be equal to it; otherwise it will be set once the decoding + * starts and the original size is known. + * This needs to be separate from "BaseImage::get_height()" because the latter + * must return 0 until the image has been decoded (to avoid showing partially + * decoded images). + */ + int buffer_height_; + + friend void ImageDecoder::set_size(int width, int height); + friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color); +}; + +template class OnlineImageSetUrlAction : public Action { + public: + OnlineImageSetUrlAction(OnlineImage *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(const char *, url) + void play(Ts... x) override { + this->parent_->set_url(this->url_.value(x...)); + this->parent_->update(); + } + + protected: + OnlineImage *parent_; +}; + +template class OnlineImageReleaseAction : public Action { + public: + OnlineImageReleaseAction(OnlineImage *parent) : parent_(parent) {} + TEMPLATABLE_VALUE(const char *, url) + void play(Ts... x) override { this->parent_->release(); } + + protected: + OnlineImage *parent_; +}; + +class DownloadFinishedTrigger : public Trigger<> { + public: + explicit DownloadFinishedTrigger(OnlineImage *parent) { + parent->add_on_finished_callback([this]() { this->trigger(); }); + } +}; + +class DownloadErrorTrigger : public Trigger<> { + public: + explicit DownloadErrorTrigger(OnlineImage *parent) { + parent->add_on_error_callback([this]() { this->trigger(); }); + } +}; + +} // namespace online_image +} // namespace esphome diff --git a/esphome/components/online_image/png_image.cpp b/esphome/components/online_image/png_image.cpp new file mode 100644 index 0000000000..c8e215a91d --- /dev/null +++ b/esphome/components/online_image/png_image.cpp @@ -0,0 +1,68 @@ +#include "png_image.h" +#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT + +#include "esphome/components/display/display_buffer.h" +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +static const char *const TAG = "online_image.png"; + +namespace esphome { +namespace online_image { + +/** + * @brief Callback method that will be called by the PNGLE engine when the basic + * data of the image is received (i.e. width and height); + * + * @param pngle The PNGLE object, including the context data. + * @param w The width of the image. + * @param h The height of the image. + */ +static void init_callback(pngle_t *pngle, uint32_t w, uint32_t h) { + PngDecoder *decoder = (PngDecoder *) pngle_get_user_data(pngle); + decoder->set_size(w, h); +} + +/** + * @brief Callback method that will be called by the PNGLE engine when a chunk + * of the image is decoded. + * + * @param pngle The PNGLE object, including the context data. + * @param x The X coordinate to draw the rectangle on. + * @param y The Y coordinate to draw the rectangle on. + * @param w The width of the rectangle to draw. + * @param h The height of the rectangle to draw. + * @param rgba The color to paint the rectangle in. + */ +static void draw_callback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint8_t rgba[4]) { + PngDecoder *decoder = (PngDecoder *) pngle_get_user_data(pngle); + Color color(rgba[0], rgba[1], rgba[2], rgba[3]); + decoder->draw(x, y, w, h, color); +} + +void PngDecoder::prepare(uint32_t download_size) { + ImageDecoder::prepare(download_size); + pngle_set_user_data(this->pngle_, this); + pngle_set_init_callback(this->pngle_, init_callback); + pngle_set_draw_callback(this->pngle_, draw_callback); +} + +int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { + if (size < 256 && size < this->download_size_ - this->decoded_bytes_) { + ESP_LOGD(TAG, "Waiting for data"); + return 0; + } + auto fed = pngle_feed(this->pngle_, buffer, size); + if (fed < 0) { + ESP_LOGE(TAG, "Error decoding image: %s", pngle_error(this->pngle_)); + } else { + this->decoded_bytes_ += fed; + } + return fed; +} + +} // namespace online_image +} // namespace esphome + +#endif // USE_ONLINE_IMAGE_PNG_SUPPORT diff --git a/esphome/components/online_image/png_image.h b/esphome/components/online_image/png_image.h new file mode 100644 index 0000000000..a928276dcc --- /dev/null +++ b/esphome/components/online_image/png_image.h @@ -0,0 +1,33 @@ +#pragma once + +#include "image_decoder.h" +#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT +#include + +namespace esphome { +namespace online_image { + +/** + * @brief Image decoder specialization for PNG images. + */ +class PngDecoder : public ImageDecoder { + public: + /** + * @brief Construct a new PNG Decoder object. + * + * @param display The image to decode the stream into. + */ + PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {} + ~PngDecoder() override { pngle_destroy(this->pngle_); } + + void prepare(uint32_t download_size) override; + int HOT decode(uint8_t *buffer, size_t size) override; + + protected: + pngle_t *pngle_; +}; + +} // namespace online_image +} // namespace esphome + +#endif // USE_ONLINE_IMAGE_PNG_SUPPORT diff --git a/esphome/core/defines.h b/esphome/core/defines.h index b7bdbb1f9d..61a4940d01 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -53,6 +53,7 @@ #define USE_MQTT #define USE_NEXTION_TFT_UPLOAD #define USE_NUMBER +#define USE_ONLINE_IMAGE_PNG_SUPPORT #define USE_OTA #define USE_OTA_PASSWORD #define USE_OTA_STATE_CALLBACK diff --git a/platformio.ini b/platformio.ini index e4f363d650..87a239207f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -40,6 +40,7 @@ lib_deps = wjtje/qr-code-generator-library@1.7.0 ; qr_code functionpointer/arduino-MLX90393@1.0.0 ; mlx90393 pavlodn/HaierProtocol@0.9.31 ; haier + kikuchan98/pngle@1.0.2 ; online_image ; This is using the repository until a new release is published to PlatformIO https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library lvgl/lvgl@8.4.0 ; lvgl diff --git a/tests/components/online_image/common-esp32.yaml b/tests/components/online_image/common-esp32.yaml new file mode 100644 index 0000000000..8cc50fc3e0 --- /dev/null +++ b/tests/components/online_image/common-esp32.yaml @@ -0,0 +1,18 @@ +<<: !include common.yaml + +spi: + - id: spi_main_lcd + clk_pin: 16 + mosi_pin: 17 + miso_pin: 15 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 12 + dc_pin: 13 + reset_pin: 21 + lambda: |- + it.fill(Color(0, 0, 0)); + it.image(0, 0, id(online_rgba_image)); diff --git a/tests/components/online_image/common-esp8266.yaml b/tests/components/online_image/common-esp8266.yaml new file mode 100644 index 0000000000..01e3467413 --- /dev/null +++ b/tests/components/online_image/common-esp8266.yaml @@ -0,0 +1,18 @@ +<<: !include common.yaml + +spi: + - id: spi_main_lcd + clk_pin: 14 + mosi_pin: 13 + miso_pin: 12 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 15 + dc_pin: 3 + reset_pin: 1 + lambda: |- + it.fill(Color(0, 0, 0)); + it.image(0, 0, id(online_rgba_image)); diff --git a/tests/components/online_image/common.yaml b/tests/components/online_image/common.yaml new file mode 100644 index 0000000000..8f7ea6238b --- /dev/null +++ b/tests/components/online_image/common.yaml @@ -0,0 +1,37 @@ +wifi: + ssid: MySSID + password: password1 + +# Purposely test that `online_image:` does auto-load `image:` +# Keep the `image:` undefined. +# image: +online_image: + - id: online_binary_image + url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png + format: PNG + type: BINARY + resize: 50x50 + - id: online_binary_transparent_image + url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png + type: TRANSPARENT_BINARY + format: png + - id: online_rgba_image + url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png + format: PNG + type: RGBA + - id: online_rgb24_image + url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png + format: PNG + type: RGB24 + use_transparency: true + +# Check the set_url action +time: + - platform: sntp + on_time: + - at: "13:37:42" + then: + - online_image.set_url: + id: online_rgba_image + url: http://www.example.org/example.png + diff --git a/tests/components/online_image/test.esp32-ard.yaml b/tests/components/online_image/test.esp32-ard.yaml new file mode 100644 index 0000000000..4111cbd0ad --- /dev/null +++ b/tests/components/online_image/test.esp32-ard.yaml @@ -0,0 +1,4 @@ +<<: !include common-esp32.yaml + +http_request: + verify_ssl: false diff --git a/tests/components/online_image/test.esp32-idf.yaml b/tests/components/online_image/test.esp32-idf.yaml new file mode 100644 index 0000000000..3f01009812 --- /dev/null +++ b/tests/components/online_image/test.esp32-idf.yaml @@ -0,0 +1,4 @@ +<<: !include common-esp32.yaml + +http_request: + From 455df35e50c5c83cdaf46e79156ee203b03dce3a Mon Sep 17 00:00:00 2001 From: Mimoja Date: Tue, 6 Aug 2024 13:17:02 +0200 Subject: [PATCH 0142/1052] Update i2s_audio_speaker.cppi2s_audio/speaker: Fix fallthrough compiler warning (#7167) --- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 6b07ecb1b6..1c6c50d8c9 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -233,6 +233,7 @@ void I2SAudioSpeaker::loop() { switch (this->state_) { case speaker::STATE_STARTING: this->start_(); + [[fallthrough]]; case speaker::STATE_RUNNING: case speaker::STATE_STOPPING: this->watch_(); From 8667f51cf08f1147cf02f56bd882beab837317ba Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 7 Aug 2024 07:15:15 +1200 Subject: [PATCH 0143/1052] Move CONF_ITEMS/CONF_FONT/CONF_TEXT to const.py (#7204) --- .../components/display_menu_base/__init__.py | 29 ++++++++++--------- .../graphical_display_menu/__init__.py | 2 +- esphome/components/lvgl/defines.py | 3 +- esphome/components/lvgl/schemas.py | 5 ++-- esphome/components/lvgl/types.py | 4 +-- .../components/lvgl/widgets/buttonmatrix.py | 4 +-- esphome/components/lvgl/widgets/checkbox.py | 4 ++- esphome/components/lvgl/widgets/keyboard.py | 4 +-- esphome/components/lvgl/widgets/label.py | 2 +- esphome/components/lvgl/widgets/msgbox.py | 3 +- esphome/components/lvgl/widgets/textarea.py | 3 +- esphome/const.py | 3 ++ 12 files changed, 34 insertions(+), 32 deletions(-) diff --git a/esphome/components/display_menu_base/__init__.py b/esphome/components/display_menu_base/__init__.py index 0c738ba838..8ae9cbc2a4 100644 --- a/esphome/components/display_menu_base/__init__.py +++ b/esphome/components/display_menu_base/__init__.py @@ -1,23 +1,26 @@ import re -import esphome.codegen as cg -import esphome.config_validation as cv + from esphome import automation, core +from esphome.automation import maybe_simple_id +import esphome.codegen as cg +from esphome.components.number import Number +from esphome.components.select import Select +from esphome.components.switch import Switch +import esphome.config_validation as cv from esphome.const import ( - CONF_ID, - CONF_TYPE, - CONF_TRIGGER_ID, - CONF_ON_VALUE, + CONF_ACTIVE, CONF_COMMAND, CONF_CUSTOM, - CONF_NUMBER, CONF_FORMAT, + CONF_ID, + CONF_ITEMS, CONF_MODE, - CONF_ACTIVE, + CONF_NUMBER, + CONF_ON_VALUE, + CONF_TEXT, + CONF_TRIGGER_ID, + CONF_TYPE, ) -from esphome.automation import maybe_simple_id -from esphome.components.select import Select -from esphome.components.number import Number -from esphome.components.switch import Switch CODEOWNERS = ["@numo68"] @@ -29,10 +32,8 @@ CONF_JOYSTICK = "joystick" CONF_LABEL = "label" CONF_MENU = "menu" CONF_BACK = "back" -CONF_TEXT = "text" CONF_SELECT = "select" CONF_SWITCH = "switch" -CONF_ITEMS = "items" CONF_ON_TEXT = "on_text" CONF_OFF_TEXT = "off_text" CONF_VALUE_LAMBDA = "value_lambda" diff --git a/esphome/components/graphical_display_menu/__init__.py b/esphome/components/graphical_display_menu/__init__.py index d7146a7381..f4d59b22b8 100644 --- a/esphome/components/graphical_display_menu/__init__.py +++ b/esphome/components/graphical_display_menu/__init__.py @@ -10,12 +10,12 @@ import esphome.config_validation as cv from esphome.const import ( CONF_BACKGROUND_COLOR, CONF_DISPLAY, + CONF_FONT, CONF_FOREGROUND_COLOR, CONF_ID, CONF_TRIGGER_ID, ) -CONF_FONT = "font" CONF_MENU_ITEM_VALUE = "menu_item_value" CONF_ON_REDRAW = "on_redraw" diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index ac28f9ed5f..1b41b32c90 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -5,6 +5,7 @@ Constants already defined in esphome.const are not duplicated here and must be i """ from esphome import codegen as cg, config_validation as cv +from esphome.const import CONF_ITEMS from esphome.core import ID, Lambda from esphome.cpp_generator import MockObj from esphome.cpp_types import uint32 @@ -115,7 +116,6 @@ CONF_SCROLLBAR = "scrollbar" CONF_INDICATOR = "indicator" CONF_KNOB = "knob" CONF_SELECTED = "selected" -CONF_ITEMS = "items" CONF_TICKS = "ticks" CONF_CURSOR = "cursor" CONF_TEXTAREA_PLACEHOLDER = "textarea_placeholder" @@ -460,7 +460,6 @@ CONF_SKIP = "skip" CONF_SYMBOL = "symbol" CONF_TAB_ID = "tab_id" CONF_TABS = "tabs" -CONF_TEXT = "text" CONF_TILE = "tile" CONF_TILE_ID = "tile_id" CONF_TILES = "tiles" diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 796783890d..62536bf4d5 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -7,6 +7,7 @@ from esphome.const import ( CONF_ID, CONF_ON_VALUE, CONF_STATE, + CONF_TEXT, CONF_TRIGGER_ID, CONF_TYPE, ) @@ -25,7 +26,7 @@ WIDGET_TYPES: dict = {} # A schema for text properties TEXT_SCHEMA = cv.Schema( { - cv.Optional(df.CONF_TEXT): cv.Any( + cv.Optional(CONF_TEXT): cv.Any( cv.All( cv.Schema( { @@ -330,7 +331,7 @@ DISP_BG_SCHEMA = cv.Schema( # A style schema that can include text STYLED_TEXT_SCHEMA = cv.maybe_simple_value( - STYLE_SCHEMA.extend(TEXT_SCHEMA), key=df.CONF_TEXT + STYLE_SCHEMA.extend(TEXT_SCHEMA), key=CONF_TEXT ) # For use by platform components diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index b6f65c8c1b..be17cf62c2 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -1,10 +1,10 @@ import sys from esphome import automation, codegen as cg -from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_VALUE +from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_TEXT, CONF_VALUE from esphome.cpp_generator import MockObj, MockObjClass -from .defines import CONF_TEXT, lvgl_ns +from .defines import lvgl_ns from .lvcode import lv_expr diff --git a/esphome/components/lvgl/widgets/buttonmatrix.py b/esphome/components/lvgl/widgets/buttonmatrix.py index 274b4de5ab..e61c5e3477 100644 --- a/esphome/components/lvgl/widgets/buttonmatrix.py +++ b/esphome/components/lvgl/widgets/buttonmatrix.py @@ -2,7 +2,7 @@ from esphome import automation import esphome.codegen as cg from esphome.components.key_provider import KeyProvider import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_WIDTH +from esphome.const import CONF_ID, CONF_ITEMS, CONF_TEXT, CONF_WIDTH from esphome.cpp_generator import MockObj from ..automation import action_to_code @@ -10,13 +10,11 @@ from ..defines import ( BUTTONMATRIX_CTRLS, CONF_BUTTONS, CONF_CONTROL, - CONF_ITEMS, CONF_KEY_CODE, CONF_MAIN, CONF_ONE_CHECKED, CONF_ROWS, CONF_SELECTED, - CONF_TEXT, ) from ..helpers import lvgl_components_required from ..lv_validation import key_code, lv_bool diff --git a/esphome/components/lvgl/widgets/checkbox.py b/esphome/components/lvgl/widgets/checkbox.py index 6299a2a6a2..79c60a8669 100644 --- a/esphome/components/lvgl/widgets/checkbox.py +++ b/esphome/components/lvgl/widgets/checkbox.py @@ -1,4 +1,6 @@ -from ..defines import CONF_INDICATOR, CONF_MAIN, CONF_TEXT +from esphome.const import CONF_TEXT + +from ..defines import CONF_INDICATOR, CONF_MAIN from ..lv_validation import lv_text from ..lvcode import lv from ..schemas import TEXT_SCHEMA diff --git a/esphome/components/lvgl/widgets/keyboard.py b/esphome/components/lvgl/widgets/keyboard.py index cff322f5af..ba7edb302e 100644 --- a/esphome/components/lvgl/widgets/keyboard.py +++ b/esphome/components/lvgl/widgets/keyboard.py @@ -1,9 +1,9 @@ from esphome.components.key_provider import KeyProvider import esphome.config_validation as cv -from esphome.const import CONF_MODE +from esphome.const import CONF_ITEMS, CONF_MODE from esphome.cpp_types import std_string -from ..defines import CONF_ITEMS, CONF_MAIN, KEYBOARD_MODES, literal +from ..defines import CONF_MAIN, KEYBOARD_MODES, literal from ..helpers import add_lv_use, lvgl_components_required from ..types import LvCompound, LvType from . import Widget, WidgetType, get_widgets diff --git a/esphome/components/lvgl/widgets/label.py b/esphome/components/lvgl/widgets/label.py index 38f688f2b0..6b04235674 100644 --- a/esphome/components/lvgl/widgets/label.py +++ b/esphome/components/lvgl/widgets/label.py @@ -1,4 +1,5 @@ import esphome.config_validation as cv +from esphome.const import CONF_TEXT from ..defines import ( CONF_LONG_MODE, @@ -6,7 +7,6 @@ from ..defines import ( CONF_RECOLOR, CONF_SCROLLBAR, CONF_SELECTED, - CONF_TEXT, LV_LONG_MODES, ) from ..lv_validation import lv_bool, lv_text diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index 4ae5be7701..63c4326c7c 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -1,5 +1,5 @@ from esphome import config_validation as cv -from esphome.const import CONF_BUTTON, CONF_ID +from esphome.const import CONF_BUTTON, CONF_ID, CONF_TEXT from esphome.core import ID from esphome.cpp_generator import new_Pvariable, static_const_array from esphome.cpp_types import nullptr @@ -9,7 +9,6 @@ from ..defines import ( CONF_BUTTONS, CONF_CLOSE_BUTTON, CONF_MSGBOXES, - CONF_TEXT, CONF_TITLE, TYPE_FLEX, literal, diff --git a/esphome/components/lvgl/widgets/textarea.py b/esphome/components/lvgl/widgets/textarea.py index 61d83dee9c..23d50b3894 100644 --- a/esphome/components/lvgl/widgets/textarea.py +++ b/esphome/components/lvgl/widgets/textarea.py @@ -1,5 +1,5 @@ import esphome.config_validation as cv -from esphome.const import CONF_MAX_LENGTH +from esphome.const import CONF_MAX_LENGTH, CONF_TEXT from ..defines import ( CONF_ACCEPTED_CHARS, @@ -10,7 +10,6 @@ from ..defines import ( CONF_PLACEHOLDER_TEXT, CONF_SCROLLBAR, CONF_SELECTED, - CONF_TEXT, CONF_TEXTAREA_PLACEHOLDER, ) from ..lv_validation import lv_bool, lv_int, lv_text diff --git a/esphome/const.py b/esphome/const.py index d7b1f558a1..13559ecf95 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -308,6 +308,7 @@ CONF_FLASH_LENGTH = "flash_length" CONF_FLASH_TRANSITION_LENGTH = "flash_transition_length" CONF_FLOW = "flow" CONF_FLOW_CONTROL_PIN = "flow_control_pin" +CONF_FONT = "font" CONF_FOR = "for" CONF_FORCE_UPDATE = "force_update" CONF_FOREGROUND_COLOR = "foreground_color" @@ -407,6 +408,7 @@ CONF_INVERTED = "inverted" CONF_IP_ADDRESS = "ip_address" CONF_IRQ_PIN = "irq_pin" CONF_IS_RGBW = "is_rgbw" +CONF_ITEMS = "items" CONF_JS_INCLUDE = "js_include" CONF_JS_URL = "js_url" CONF_JVC = "jvc" @@ -841,6 +843,7 @@ CONF_TEMPERATURE = "temperature" CONF_TEMPERATURE_OFFSET = "temperature_offset" CONF_TEMPERATURE_SOURCE = "temperature_source" CONF_TEMPERATURE_STEP = "temperature_step" +CONF_TEXT = "text" CONF_TEXT_SENSORS = "text_sensors" CONF_THEN = "then" CONF_THRESHOLD = "threshold" From eccc5a3ea31853986aeae1b66ceaca438aede161 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 7 Aug 2024 05:15:28 +1000 Subject: [PATCH 0144/1052] [lvgl] Fix compile error when using encoder with buttons only. (#7203) --- esphome/components/lvgl/__init__.py | 6 ++-- esphome/components/lvgl/defines.py | 2 +- .../lvgl/{rotary_encoders.py => encoders.py} | 24 +++++++------- esphome/components/lvgl/lvgl_esphome.cpp | 4 +-- esphome/components/lvgl/lvgl_esphome.h | 21 +++++++----- esphome/components/lvgl/schemas.py | 8 ++--- esphome/components/lvgl/widgets/__init__.py | 32 +++---------------- tests/components/lvgl/test.esp32-ard.yaml | 27 ++++++++++++++++ tests/components/lvgl/test.esp32-idf.yaml | 2 +- 9 files changed, 68 insertions(+), 58 deletions(-) rename esphome/components/lvgl/{rotary_encoders.py => encoders.py} (77%) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 9eb4665874..87fbcab4dc 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -23,9 +23,9 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, update_to_code from .defines import CONF_SKIP +from .encoders import ENCODERS_CONFIG, encoders_to_code from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent -from .rotary_encoders import ROTARY_ENCODER_CONFIG, rotary_encoders_to_code from .schemas import ( DISP_BG_SCHEMA, FLEX_OBJ_SCHEMA, @@ -256,7 +256,7 @@ async def to_code(config): async with LvContext(lv_component): await touchscreens_to_code(lv_component, config) - await rotary_encoders_to_code(lv_component, config) + await encoders_to_code(lv_component, config) await theme_to_code(config) await styles_to_code(config) await set_obj_properties(lv_scr_act, config) @@ -336,7 +336,7 @@ CONFIG_SCHEMA = ( {cv.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()} ), cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema, - cv.GenerateID(df.CONF_ROTARY_ENCODERS): ROTARY_ENCODER_CONFIG, + cv.GenerateID(df.CONF_ENCODERS): ENCODERS_CONFIG, } ) .extend(DISP_BG_SCHEMA) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 1b41b32c90..d0047b59f7 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -388,6 +388,7 @@ CONF_DEFAULT = "default" CONF_DEFAULT_FONT = "default_font" CONF_DIR = "dir" CONF_DISPLAYS = "displays" +CONF_ENCODERS = "encoders" CONF_END_ANGLE = "end_angle" CONF_END_VALUE = "end_value" CONF_ENTER_BUTTON = "enter_button" @@ -441,7 +442,6 @@ CONF_RECOLOR = "recolor" CONF_RIGHT_BUTTON = "right_button" CONF_ROLLOVER = "rollover" CONF_ROOT_BACK_BTN = "root_back_btn" -CONF_ROTARY_ENCODERS = "rotary_encoders" CONF_ROWS = "rows" CONF_SCALE_LINES = "scale_lines" CONF_SCROLLBAR_MODE = "scrollbar_mode" diff --git a/esphome/components/lvgl/rotary_encoders.py b/esphome/components/lvgl/encoders.py similarity index 77% rename from esphome/components/lvgl/rotary_encoders.py rename to esphome/components/lvgl/encoders.py index d8a82dbc78..caddc2e47f 100644 --- a/esphome/components/lvgl/rotary_encoders.py +++ b/esphome/components/lvgl/encoders.py @@ -5,25 +5,26 @@ import esphome.config_validation as cv from esphome.const import CONF_GROUP, CONF_ID, CONF_SENSOR from .defines import ( + CONF_ENCODERS, CONF_ENTER_BUTTON, CONF_LEFT_BUTTON, CONF_LONG_PRESS_REPEAT_TIME, CONF_LONG_PRESS_TIME, CONF_RIGHT_BUTTON, - CONF_ROTARY_ENCODERS, ) -from .helpers import lvgl_components_required -from .lvcode import lv, lv_add, lv_expr +from .helpers import lvgl_components_required, requires_component +from .lvcode import lv, lv_add, lv_assign, lv_expr, lv_Pvariable from .schemas import ENCODER_SCHEMA -from .types import lv_indev_type_t -from .widgets import add_group +from .types import lv_group_t, lv_indev_type_t -ROTARY_ENCODER_CONFIG = cv.ensure_list( +ENCODERS_CONFIG = cv.ensure_list( ENCODER_SCHEMA.extend( { cv.Required(CONF_ENTER_BUTTON): cv.use_id(BinarySensor), cv.Required(CONF_SENSOR): cv.Any( - cv.use_id(RotaryEncoderSensor), + cv.All( + cv.use_id(RotaryEncoderSensor), requires_component("rotary_encoder") + ), cv.Schema( { cv.Required(CONF_LEFT_BUTTON): cv.use_id(BinarySensor), @@ -36,10 +37,9 @@ ROTARY_ENCODER_CONFIG = cv.ensure_list( ) -async def rotary_encoders_to_code(var, config): - for enc_conf in config.get(CONF_ROTARY_ENCODERS, ()): +async def encoders_to_code(var, config): + for enc_conf in config.get(CONF_ENCODERS, ()): lvgl_components_required.add("KEY_LISTENER") - lvgl_components_required.add("ROTARY_ENCODER") lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds listener = cg.new_Pvariable( @@ -57,7 +57,9 @@ async def rotary_encoders_to_code(var, config): lv_add(listener.set_sensor(sensor_config)) b_sensor = await cg.get_variable(enc_conf[CONF_ENTER_BUTTON]) cg.add(listener.set_enter_button(b_sensor)) - if group := add_group(enc_conf.get(CONF_GROUP)): + if group := enc_conf.get(CONF_GROUP): + group = lv_Pvariable(lv_group_t, group) + lv_assign(group, lv_expr.group_create()) lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group) else: lv.indev_drv_register(listener.get_drv()) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 544643d532..6f23c2421b 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -127,7 +127,7 @@ void LVTouchListener::update(const touchscreen::TouchPoints_t &tpoints) { } #endif // USE_LVGL_TOUCHSCREEN -#ifdef USE_LVGL_ROTARY_ENCODER +#ifdef USE_LVGL_KEY_LISTENER LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt) { lv_indev_drv_init(&this->drv_); this->drv_.type = type; @@ -143,7 +143,7 @@ LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_ data->continue_reading = false; }; } -#endif // USE_LVGL_ROTARY_ENCODER +#endif // USE_LVGL_KEY_LISTENER #ifdef USE_LVGL_BUTTONMATRIX void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) { diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 5f2f0ea8df..45841b99d9 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -1,6 +1,13 @@ #pragma once #include "esphome/core/defines.h" +#ifdef USE_LVGL_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif // USE_LVGL_BINARY_SENSOR +#ifdef USE_LVGL_ROTARY_ENCODER +#include "esphome/components/rotary_encoder/rotary_encoder.h" +#endif // USE_LVGL_ROTARY_ENCODER + // required for clang-tidy #ifndef LV_CONF_H #define LV_CONF_SKIP 1 // NOLINT @@ -12,12 +19,7 @@ #include "esphome/core/log.h" #include #include - -#ifdef USE_LVGL_ROTARY_ENCODER -#include "esphome/components/binary_sensor/binary_sensor.h" -#include "esphome/components/rotary_encoder/rotary_encoder.h" -#endif // USE_LVGL_ROTARY_ENCODER - +#include #ifdef USE_LVGL_IMAGE #include "esphome/components/image/image.h" #endif // USE_LVGL_IMAGE @@ -202,7 +204,7 @@ class LVTouchListener : public touchscreen::TouchListener, public Parented { public: LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt); @@ -218,9 +220,11 @@ class LVEncoderListener : public Parented { enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); }); } +#ifdef USE_LVGL_ROTARY_ENCODER void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor) { sensor->register_listener([this](int32_t count) { this->set_count(count); }); } +#endif // USE_LVGL_ROTARY_ENCODER void event(int key, bool pressed) { if (!this->parent_->is_paused()) { @@ -243,7 +247,8 @@ class LVEncoderListener : public Parented { int32_t last_count_{}; int key_{}; }; -#endif // USE_LVGL_ROTARY_ENCODER +#endif // USE_LVGL_KEY_LISTENER + #ifdef USE_LVGL_BUTTONMATRIX class LvButtonMatrixType : public key_provider::KeyProvider, public LvCompound { public: diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 62536bf4d5..f172ba9f2b 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -16,9 +16,9 @@ from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid, types as ty from .helpers import add_lv_use, requires_component, validate_printf -from .lv_validation import id_name, lv_color, lv_font, lv_image +from .lv_validation import lv_color, lv_font, lv_image from .lvcode import LvglComponent -from .types import WidgetType +from .types import WidgetType, lv_group_t # this will be populated later, in __init__.py to avoid circular imports. WIDGET_TYPES: dict = {} @@ -61,7 +61,7 @@ ENCODER_SCHEMA = cv.Schema( cv.GenerateID(): cv.All( cv.declare_id(ty.LVEncoderListener), requires_component("binary_sensor") ), - cv.Optional(CONF_GROUP): lvalid.id_name, + cv.Optional(CONF_GROUP): cv.declare_id(lv_group_t), cv.Optional(df.CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME, cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME, } @@ -249,7 +249,7 @@ def obj_schema(widget_type: WidgetType): cv.Schema( { cv.Optional(CONF_STATE): SET_STATE_SCHEMA, - cv.Optional(CONF_GROUP): id_name, + cv.Optional(CONF_GROUP): cv.use_id(lv_group_t), } ) ) diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index dff43cf257..f1946015bc 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -6,7 +6,7 @@ from esphome.config_validation import Invalid from esphome.const import CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE from esphome.core import ID, TimePeriod from esphome.coroutine import FakeAwaitable -from esphome.cpp_generator import AssignmentExpression, CallExpression, MockObj +from esphome.cpp_generator import CallExpression, MockObj from ..defines import ( CONF_DEFAULT, @@ -44,15 +44,7 @@ from ..lvcode import ( lv_Pvariable, ) from ..schemas import ALL_STYLES, STYLE_REMAP, WIDGET_TYPES -from ..types import ( - LV_STATE, - LvType, - WidgetType, - lv_coord_t, - lv_group_t, - lv_obj_t, - lv_obj_t_ptr, -) +from ..types import LV_STATE, LvType, WidgetType, lv_coord_t, lv_obj_t, lv_obj_t_ptr EVENT_LAMB = "event_lamb__" @@ -317,7 +309,8 @@ async def set_obj_properties(w: Widget, config): value = await ALL_STYLES[prop].process(value) prop_r = STYLE_REMAP.get(prop, prop) w.set_style(prop_r, value, lv_state) - if group := add_group(config.get(CONF_GROUP)): + if group := config.get(CONF_GROUP): + group = await cg.get_variable(group) lv.group_add_obj(group, w.obj) flag_clr = set() flag_set = set() @@ -404,20 +397,3 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent): lv_scr_act_spec = LvScrActType() lv_scr_act = Widget.create(None, literal("lv_scr_act()"), lv_scr_act_spec, {}) - -lv_groups = {} # Widget group names - - -def add_group(name): - if name is None: - return None - fullname = f"lv_esp_group_{name}" - if name not in lv_groups: - gid = ID(fullname, True, type=lv_group_t.operator("ptr")) - lv_add( - AssignmentExpression( - type_=gid.type, modifier="", name=fullname, rhs=lv_expr.group_create() - ) - ) - lv_groups[name] = literal(fullname) - return lv_groups[name] diff --git a/tests/components/lvgl/test.esp32-ard.yaml b/tests/components/lvgl/test.esp32-ard.yaml index abfb324ea5..2d6a6871ba 100644 --- a/tests/components/lvgl/test.esp32-ard.yaml +++ b/tests/components/lvgl/test.esp32-ard.yaml @@ -24,6 +24,33 @@ display: invert_colors: false update_interval: never +binary_sensor: + - platform: gpio + internal: true + id: up_button + pin: + number: GPIO38 + inverted: true + - platform: gpio + internal: true + id: down_button + pin: + number: GPIO37 + inverted: true + - platform: gpio + internal: true + id: select_button + pin: + number: GPIO39 + inverted: true +lvgl: + encoders: + group: switches + enter_button: select_button + sensor: + left_button: up_button + right_button: down_button + packages: lvgl: !include lvgl-package.yaml diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml index 0f740db980..927d72d15c 100644 --- a/tests/components/lvgl/test.esp32-idf.yaml +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -67,7 +67,7 @@ lvgl: displays: - tft_display - second_display - rotary_encoders: + encoders: sensor: encoder enter_button: pushbutton group: general From da0dbe8753645b753a9dc64fb6ad21ccd29a867c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 7 Aug 2024 07:29:05 +1200 Subject: [PATCH 0145/1052] Revert "Add null GPIO pin " (#6621) --- .../cst226/touchscreen/cst226_touchscreen.cpp | 18 +++++++++++------- .../cst226/touchscreen/cst226_touchscreen.h | 2 +- esphome/core/gpio.h | 18 ------------------ 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp b/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp index 69728dc666..d4e43d30f5 100644 --- a/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +++ b/esphome/components/cst226/touchscreen/cst226_touchscreen.cpp @@ -5,13 +5,17 @@ namespace cst226 { void CST226Touchscreen::setup() { esph_log_config(TAG, "Setting up CST226 Touchscreen..."); - this->reset_pin_->setup(); - this->reset_pin_->digital_write(true); - delay(5); - this->reset_pin_->digital_write(false); - delay(5); - this->reset_pin_->digital_write(true); - this->set_timeout(30, [this] { this->continue_setup_(); }); + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(true); + delay(5); + this->reset_pin_->digital_write(false); + delay(5); + this->reset_pin_->digital_write(true); + this->set_timeout(30, [this] { this->continue_setup_(); }); + } else { + this->continue_setup_(); + } } void CST226Touchscreen::update_touches() { diff --git a/esphome/components/cst226/touchscreen/cst226_touchscreen.h b/esphome/components/cst226/touchscreen/cst226_touchscreen.h index 1b15b952c4..9f518e5068 100644 --- a/esphome/components/cst226/touchscreen/cst226_touchscreen.h +++ b/esphome/components/cst226/touchscreen/cst226_touchscreen.h @@ -35,7 +35,7 @@ class CST226Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice void continue_setup_(); InternalGPIOPin *interrupt_pin_{}; - GPIOPin *reset_pin_{NULL_PIN}; + GPIOPin *reset_pin_{}; uint8_t chip_id_{}; bool setup_complete_{}; }; diff --git a/esphome/core/gpio.h b/esphome/core/gpio.h index b3f6b00196..1b6f2ba1e6 100644 --- a/esphome/core/gpio.h +++ b/esphome/core/gpio.h @@ -62,24 +62,6 @@ class GPIOPin { virtual bool is_internal() { return false; } }; -/** - * A pin to replace those that don't exist. - */ -class NullPin : public GPIOPin { - public: - void setup() override {} - - void pin_mode(gpio::Flags _) override {} - - bool digital_read() override { return false; } - - void digital_write(bool _) override {} - - std::string dump_summary() const override { return {"Not used"}; } -}; - -static GPIOPin *const NULL_PIN = new NullPin(); - /// Copy of GPIOPin that is safe to use from ISRs (with no virtual functions) class ISRInternalGPIOPin { public: From 1e63fddf36390794c4aa70e6a9e508b23b4fa852 Mon Sep 17 00:00:00 2001 From: iannisimo <11798717+iannisimo@users.noreply.github.com> Date: Wed, 7 Aug 2024 01:02:30 +0200 Subject: [PATCH 0146/1052] [remote_transmitter] Change default carrier_frequency to valid value (#7176) set current_carrier_frequency_ default value to esp-idf's default (38000) --- esphome/components/remote_transmitter/remote_transmitter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index b897fa8fab..a5896796c0 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -49,7 +49,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #ifdef USE_ESP32 void configure_rmt_(); - uint32_t current_carrier_frequency_{UINT32_MAX}; + uint32_t current_carrier_frequency_{38000}; bool initialized_{false}; std::vector rmt_temp_; esp_err_t error_code_{ESP_OK}; From 73f786c606dd3ad913e4d27553acd620548a0349 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:46:37 +1200 Subject: [PATCH 0147/1052] [code-quality] Organise script imports (#7198) --- script/build_codeowners.py | 8 ++++---- script/build_language_schema.py | 14 +++++++------- script/bump-version.py | 2 +- script/clang-format | 12 +++--------- script/clang-tidy | 29 ++++++++++++++--------------- script/helpers.py | 2 +- script/lint-python | 19 ++++++++++--------- script/list-components.py | 9 +++++---- 8 files changed, 45 insertions(+), 50 deletions(-) diff --git a/script/build_codeowners.py b/script/build_codeowners.py index 6bc558d351..db34ad7702 100755 --- a/script/build_codeowners.py +++ b/script/build_codeowners.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -from pathlib import Path -import sys import argparse from collections import defaultdict +from pathlib import Path +import sys -from esphome.helpers import write_file_if_changed from esphome.config import get_component, get_platform -from esphome.core import CORE from esphome.const import KEY_CORE, KEY_TARGET_FRAMEWORK +from esphome.core import CORE +from esphome.helpers import write_file_if_changed parser = argparse.ArgumentParser() parser.add_argument( diff --git a/script/build_language_schema.py b/script/build_language_schema.py index cb3dc1832d..8b2c28b06b 100644 --- a/script/build_language_schema.py +++ b/script/build_language_schema.py @@ -1,9 +1,10 @@ +import argparse +import glob import inspect import json -import argparse import os -import glob import re + import voluptuous as vol # NOTE: Cannot import other esphome components globally as a modification in vol_schema @@ -94,13 +95,12 @@ load_components() # Import esphome after loading components (so schema is tracked) # pylint: disable=wrong-import-position -import esphome.core as esphome_core -import esphome.config_validation as cv -from esphome import automation -from esphome import pins +from esphome import automation, pins from esphome.components import remote_base -from esphome.loader import get_platform, CORE_COMPONENTS_PATH +import esphome.config_validation as cv +import esphome.core as esphome_core from esphome.helpers import write_file_if_changed +from esphome.loader import CORE_COMPONENTS_PATH, get_platform from esphome.util import Registry # pylint: enable=wrong-import-position diff --git a/script/bump-version.py b/script/bump-version.py index a55bb65cd6..8389d482b8 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import argparse -import re from dataclasses import dataclass +import re import sys diff --git a/script/clang-format b/script/clang-format index b065d80795..d922c5b6f1 100755 --- a/script/clang-format +++ b/script/clang-format @@ -1,15 +1,6 @@ #!/usr/bin/env python3 -from helpers import ( - print_error_for_file, - get_output, - git_ls_files, - filter_changed, - get_binary, -) import argparse -import click -import colorama import multiprocessing import os import queue @@ -18,6 +9,9 @@ import subprocess import sys import threading +import click +import colorama +from helpers import filter_changed, get_binary, git_ls_files, print_error_for_file def run_format(executable, args, queue, lock, failed_files): diff --git a/script/clang-tidy b/script/clang-tidy index bd919825fd..ea522157c5 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -1,21 +1,6 @@ #!/usr/bin/env python3 -from helpers import ( - print_error_for_file, - get_output, - filter_grep, - build_all_include, - temp_header_file, - git_ls_files, - filter_changed, - load_idedata, - root_path, - basepath, - get_binary, -) import argparse -import click -import colorama import multiprocessing import os import queue @@ -26,6 +11,20 @@ import sys import tempfile import threading +import click +import colorama +from helpers import ( + basepath, + build_all_include, + filter_changed, + filter_grep, + get_binary, + git_ls_files, + load_idedata, + print_error_for_file, + root_path, + temp_header_file, +) def clang_options(idedata): diff --git a/script/helpers.py b/script/helpers.py index 52b0658fb6..56349b6052 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -1,8 +1,8 @@ import json import os.path +from pathlib import Path import re import subprocess -from pathlib import Path import colorama diff --git a/script/lint-python b/script/lint-python index 7de1de80b0..01e5e76190 100755 --- a/script/lint-python +++ b/script/lint-python @@ -1,19 +1,20 @@ #!/usr/bin/env python3 -from helpers import ( - styled, - print_error_for_file, - get_output, - get_err, - git_ls_files, - filter_changed, -) import argparse -import colorama import os import re import sys +import colorama +from helpers import ( + filter_changed, + get_err, + get_output, + git_ls_files, + print_error_for_file, + styled, +) + curfile = None diff --git a/script/list-components.py b/script/list-components.py index 559919bb8a..0d4777436b 100755 --- a/script/list-components.py +++ b/script/list-components.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 +import argparse from pathlib import Path import sys -import argparse -from helpers import git_ls_files, changed_files -from esphome.loader import get_component, get_platform -from esphome.core import CORE +from helpers import changed_files, git_ls_files + from esphome.const import ( KEY_CORE, KEY_TARGET_FRAMEWORK, @@ -13,6 +12,8 @@ from esphome.const import ( PLATFORM_ESP32, PLATFORM_ESP8266, ) +from esphome.core import CORE +from esphome.loader import get_component, get_platform def filter_component_files(str): From 9b0c2234d89c4f6a642a9fefc77f2e7f0a1c5ef7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:47:46 +1200 Subject: [PATCH 0148/1052] [max31856] Use cv.frequency as validator (#7212) --- esphome/components/max31856/sensor.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/max31856/sensor.py b/esphome/components/max31856/sensor.py index 71f1f3bfa5..bf9741aeed 100644 --- a/esphome/components/max31856/sensor.py +++ b/esphome/components/max31856/sensor.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import sensor, spi +import esphome.config_validation as cv from esphome.const import ( CONF_MAINS_FILTER, DEVICE_CLASS_TEMPERATURE, @@ -15,8 +15,8 @@ MAX31856Sensor = max31856_ns.class_( MAX31865ConfigFilter = max31856_ns.enum("MAX31856ConfigFilter") FILTER = { - "50HZ": MAX31865ConfigFilter.FILTER_50HZ, - "60HZ": MAX31865ConfigFilter.FILTER_60HZ, + 50: MAX31865ConfigFilter.FILTER_50HZ, + 60: MAX31865ConfigFilter.FILTER_60HZ, } CONFIG_SCHEMA = ( @@ -29,8 +29,8 @@ CONFIG_SCHEMA = ( ) .extend( { - cv.Optional(CONF_MAINS_FILTER, default="60HZ"): cv.enum( - FILTER, upper=True, space="" + cv.Optional(CONF_MAINS_FILTER, default="60Hz"): cv.All( + cv.frequency, cv.enum(FILTER, int=True) ), } ) From c348efa401ea64b6a07ae6671fc16f62f3dcb1e2 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 7 Aug 2024 05:49:51 +0200 Subject: [PATCH 0149/1052] [code-quality] Organise base entities imports (#7208) --- esphome/components/binary_sensor/__init__.py | 10 +++--- esphome/components/button/__init__.py | 8 ++--- esphome/components/climate/__init__.py | 10 +++--- esphome/components/cover/__init__.py | 16 +++++----- esphome/components/datetime/__init__.py | 26 +++++++-------- esphome/components/event/__init__.py | 10 +++--- esphome/components/fan/__init__.py | 28 ++++++++--------- esphome/components/light/__init__.py | 33 ++++++++++---------- esphome/components/lock/__init__.py | 6 ++-- esphome/components/media_player/__init__.py | 8 ++--- esphome/components/number/__init__.py | 17 +++++----- esphome/components/select/__init__.py | 14 ++++----- esphome/components/sensor/__init__.py | 20 ++++++------ esphome/components/switch/__init__.py | 6 ++-- esphome/components/text/__init__.py | 10 +++--- esphome/components/text_sensor/__init__.py | 14 ++++----- esphome/components/valve/__init__.py | 6 ++-- 17 files changed, 119 insertions(+), 123 deletions(-) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 11a1887206..95fd17bcc0 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -1,10 +1,8 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.cpp_generator import MockObjClass -from esphome.cpp_helpers import setup_entity from esphome import automation, core from esphome.automation import Condition, maybe_simple_id +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_DELAY, CONF_DEVICE_CLASS, @@ -16,6 +14,7 @@ from esphome.const import ( CONF_INVERTED, CONF_MAX_LENGTH, CONF_MIN_LENGTH, + CONF_MQTT_ID, CONF_ON_CLICK, CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, @@ -26,7 +25,6 @@ from esphome.const import ( CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, - CONF_MQTT_ID, CONF_WEB_SERVER_ID, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, @@ -59,6 +57,8 @@ from esphome.const import ( DEVICE_CLASS_WINDOW, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_generator import MockObjClass +from esphome.cpp_helpers import setup_entity from esphome.util import Registry CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/button/__init__.py b/esphome/components/button/__init__.py index 773ab9d37f..3010d3006a 100644 --- a/esphome/components/button/__init__.py +++ b/esphome/components/button/__init__.py @@ -1,16 +1,16 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, CONF_ICON, CONF_ID, + CONF_MQTT_ID, CONF_ON_PRESS, CONF_TRIGGER_ID, - CONF_MQTT_ID, CONF_WEB_SERVER_ID, DEVICE_CLASS_EMPTY, DEVICE_CLASS_IDENTIFY, @@ -18,8 +18,8 @@ from esphome.const import ( DEVICE_CLASS_UPDATE, ) from esphome.core import CORE, coroutine_with_priority -from esphome.cpp_helpers import setup_entity from esphome.cpp_generator import MockObjClass +from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@esphome/core"] IS_PLATFORM_COMPONENT = True diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index ccd7a3da4e..c7e4ce7745 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -1,8 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.cpp_helpers import setup_entity from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_ACTION_STATE_TOPIC, CONF_AWAY, @@ -21,6 +20,7 @@ from esphome.const import ( CONF_MODE, CONF_MODE_COMMAND_TOPIC, CONF_MODE_STATE_TOPIC, + CONF_MQTT_ID, CONF_ON_CONTROL, CONF_ON_STATE, CONF_PRESET, @@ -33,20 +33,20 @@ from esphome.const import ( CONF_TARGET_HUMIDITY_STATE_TOPIC, CONF_TARGET_TEMPERATURE, CONF_TARGET_TEMPERATURE_COMMAND_TOPIC, - CONF_TARGET_TEMPERATURE_STATE_TOPIC, CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_HIGH_COMMAND_TOPIC, CONF_TARGET_TEMPERATURE_HIGH_STATE_TOPIC, CONF_TARGET_TEMPERATURE_LOW, CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC, CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC, + CONF_TARGET_TEMPERATURE_STATE_TOPIC, CONF_TEMPERATURE_STEP, CONF_TRIGGER_ID, CONF_VISUAL, - CONF_MQTT_ID, CONF_WEB_SERVER_ID, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_helpers import setup_entity IS_PLATFORM_COMPONENT = True diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index 313b2c5928..d25dd91148 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -1,23 +1,23 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation -from esphome.automation import maybe_simple_id, Condition +from esphome.automation import Condition, maybe_simple_id +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( - CONF_ID, CONF_DEVICE_CLASS, - CONF_STATE, + CONF_ID, + CONF_MQTT_ID, CONF_ON_OPEN, CONF_POSITION, CONF_POSITION_COMMAND_TOPIC, CONF_POSITION_STATE_TOPIC, + CONF_STATE, + CONF_STOP, CONF_TILT, CONF_TILT_COMMAND_TOPIC, CONF_TILT_STATE_TOPIC, - CONF_STOP, - CONF_MQTT_ID, - CONF_WEB_SERVER_ID, CONF_TRIGGER_ID, + CONF_WEB_SERVER_ID, DEVICE_CLASS_AWNING, DEVICE_CLASS_BLIND, DEVICE_CLASS_CURTAIN, diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index c118216a2d..4fda97c5bc 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -1,32 +1,30 @@ -import esphome.codegen as cg - -import esphome.config_validation as cv from esphome import automation -from esphome.components import mqtt, web_server, time +import esphome.codegen as cg +from esphome.components import mqtt, time, web_server +import esphome.config_validation as cv from esphome.const import ( + CONF_DATE, + CONF_DATETIME, + CONF_DAY, + CONF_HOUR, CONF_ID, + CONF_MINUTE, + CONF_MONTH, + CONF_MQTT_ID, CONF_ON_TIME, CONF_ON_VALUE, + CONF_SECOND, + CONF_TIME, CONF_TIME_ID, CONF_TRIGGER_ID, CONF_TYPE, - CONF_MQTT_ID, CONF_WEB_SERVER_ID, - CONF_DATE, - CONF_DATETIME, - CONF_TIME, CONF_YEAR, - CONF_MONTH, - CONF_DAY, - CONF_SECOND, - CONF_HOUR, - CONF_MINUTE, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity - CODEOWNERS = ["@rfdarter", "@jesserockz"] DEPENDENCIES = ["time"] diff --git a/esphome/components/event/__init__.py b/esphome/components/event/__init__.py index 241e884386..031a4c0de8 100644 --- a/esphome/components/event/__init__.py +++ b/esphome/components/event/__init__.py @@ -1,24 +1,24 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt +import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, + CONF_EVENT_TYPE, CONF_ICON, CONF_ID, + CONF_MQTT_ID, CONF_ON_EVENT, CONF_TRIGGER_ID, - CONF_MQTT_ID, - CONF_EVENT_TYPE, DEVICE_CLASS_BUTTON, DEVICE_CLASS_DOORBELL, DEVICE_CLASS_EMPTY, DEVICE_CLASS_MOTION, ) from esphome.core import CORE, coroutine_with_priority -from esphome.cpp_helpers import setup_entity from esphome.cpp_generator import MockObjClass +from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@nohat"] IS_PLATFORM_COMPONENT = True diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 847a59baa1..62624ec6e3 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -1,31 +1,31 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( + CONF_DIRECTION, CONF_ID, CONF_MQTT_ID, - CONF_WEB_SERVER_ID, - CONF_OSCILLATING, - CONF_OSCILLATION_COMMAND_TOPIC, - CONF_OSCILLATION_STATE_TOPIC, - CONF_SPEED, - CONF_SPEED_LEVEL_COMMAND_TOPIC, - CONF_SPEED_LEVEL_STATE_TOPIC, - CONF_SPEED_COMMAND_TOPIC, - CONF_SPEED_STATE_TOPIC, CONF_OFF_SPEED_CYCLE, CONF_ON_DIRECTION_SET, CONF_ON_OSCILLATING_SET, + CONF_ON_PRESET_SET, CONF_ON_SPEED_SET, CONF_ON_STATE, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, - CONF_ON_PRESET_SET, - CONF_TRIGGER_ID, - CONF_DIRECTION, + CONF_OSCILLATING, + CONF_OSCILLATION_COMMAND_TOPIC, + CONF_OSCILLATION_STATE_TOPIC, CONF_RESTORE_MODE, + CONF_SPEED, + CONF_SPEED_COMMAND_TOPIC, + CONF_SPEED_LEVEL_COMMAND_TOPIC, + CONF_SPEED_LEVEL_STATE_TOPIC, + CONF_SPEED_STATE_TOPIC, + CONF_TRIGGER_ID, + CONF_WEB_SERVER_ID, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 161b4d8cd9..d9f139d2f4 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -1,8 +1,9 @@ -import esphome.codegen as cg -import esphome.config_validation as cv import esphome.automation as auto +import esphome.codegen as cg from esphome.components import mqtt, power_supply, web_server +import esphome.config_validation as cv from esphome.const import ( + CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_COLOR_CORRECT, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, @@ -10,36 +11,36 @@ from esphome.const import ( CONF_GAMMA_CORRECT, CONF_ID, CONF_MQTT_ID, - CONF_WEB_SERVER_ID, - CONF_POWER_SUPPLY, - CONF_RESTORE_MODE, + CONF_ON_STATE, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, - CONF_ON_STATE, + CONF_POWER_SUPPLY, + CONF_RESTORE_MODE, CONF_TRIGGER_ID, - CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE, + CONF_WEB_SERVER_ID, ) from esphome.core import coroutine_with_priority from esphome.cpp_helpers import setup_entity + from .automation import light_control_to_code # noqa from .effects import ( - validate_effects, + ADDRESSABLE_EFFECTS, BINARY_EFFECTS, + EFFECTS_REGISTRY, MONOCHROMATIC_EFFECTS, RGB_EFFECTS, - ADDRESSABLE_EFFECTS, - EFFECTS_REGISTRY, + validate_effects, ) from .types import ( # noqa - LightState, - AddressableLightState, - light_ns, - LightOutput, AddressableLight, - LightTurnOnTrigger, - LightTurnOffTrigger, + AddressableLightState, + LightOutput, + LightState, LightStateTrigger, + LightTurnOffTrigger, + LightTurnOnTrigger, + light_ns, ) CODEOWNERS = ["@esphome/core"] diff --git a/esphome/components/lock/__init__.py b/esphome/components/lock/__init__.py index c2d6054ed9..6b92bc264b 100644 --- a/esphome/components/lock/__init__.py +++ b/esphome/components/lock/__init__.py @@ -1,14 +1,14 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition, maybe_simple_id +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_MQTT_ID, CONF_ON_LOCK, CONF_ON_UNLOCK, CONF_TRIGGER_ID, - CONF_MQTT_ID, CONF_WEB_SERVER_ID, ) from esphome.core import CORE, coroutine_with_priority diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index 320014e355..423cb065dc 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -1,20 +1,18 @@ from esphome import automation -import esphome.config_validation as cv -import esphome.codegen as cg - from esphome.automation import maybe_simple_id +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_ON_IDLE, CONF_ON_STATE, CONF_TRIGGER_ID, CONF_VOLUME, - CONF_ON_IDLE, ) from esphome.core import CORE from esphome.coroutine import coroutine_with_priority from esphome.cpp_helpers import setup_entity - CODEOWNERS = ["@jesserockz"] IS_PLATFORM_COMPONENT = True diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index d9c16fd7a9..ece738af49 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -1,24 +1,23 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation -from esphome.components import mqtt -from esphome.components import web_server +import esphome.codegen as cg +from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_ABOVE, CONF_BELOW, + CONF_CYCLE, CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, - CONF_ID, CONF_ICON, + CONF_ID, CONF_MODE, + CONF_MQTT_ID, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, + CONF_OPERATION, CONF_TRIGGER_ID, CONF_UNIT_OF_MEASUREMENT, - CONF_MQTT_ID, CONF_VALUE, - CONF_OPERATION, - CONF_CYCLE, CONF_WEB_SERVER_ID, DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, @@ -72,8 +71,8 @@ from esphome.const import ( DEVICE_CLASS_WIND_SPEED, ) from esphome.core import CORE, coroutine_with_priority -from esphome.cpp_helpers import setup_entity from esphome.cpp_generator import MockObjClass +from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@esphome/core"] DEVICE_CLASSES = [ diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index 073fbef1d4..2bc68d43ec 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -1,20 +1,20 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( + CONF_CYCLE, CONF_ENTITY_CATEGORY, CONF_ICON, CONF_ID, + CONF_INDEX, + CONF_MODE, + CONF_MQTT_ID, CONF_ON_VALUE, + CONF_OPERATION, CONF_OPTION, CONF_TRIGGER_ID, - CONF_MQTT_ID, CONF_WEB_SERVER_ID, - CONF_CYCLE, - CONF_MODE, - CONF_OPERATION, - CONF_INDEX, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_generator import MockObjClass diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 3b76466dec..867cdc1f48 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -1,22 +1,27 @@ import math -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( - CONF_DEVICE_CLASS, CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, + CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, CONF_EXPIRE_AFTER, CONF_FILTERS, + CONF_FORCE_UPDATE, CONF_FROM, CONF_ICON, CONF_ID, CONF_IGNORE_OUT_OF_RANGE, + CONF_MAX_VALUE, + CONF_METHOD, + CONF_MIN_VALUE, + CONF_MQTT_ID, CONF_MULTIPLE, CONF_ON_RAW_VALUE, CONF_ON_VALUE, @@ -30,14 +35,9 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, - CONF_WINDOW_SIZE, - CONF_MQTT_ID, - CONF_WEB_SERVER_ID, - CONF_FORCE_UPDATE, CONF_VALUE, - CONF_MIN_VALUE, - CONF_MAX_VALUE, - CONF_METHOD, + CONF_WEB_SERVER_ID, + CONF_WINDOW_SIZE, DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 3539d0e34e..fef4f7f007 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -1,8 +1,8 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import Condition, maybe_simple_id +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, @@ -10,11 +10,11 @@ from esphome.const import ( CONF_ID, CONF_INVERTED, CONF_MQTT_ID, - CONF_WEB_SERVER_ID, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, CONF_RESTORE_MODE, CONF_TRIGGER_ID, + CONF_WEB_SERVER_ID, DEVICE_CLASS_EMPTY, DEVICE_CLASS_OUTLET, DEVICE_CLASS_SWITCH, diff --git a/esphome/components/text/__init__.py b/esphome/components/text/__init__.py index 5a8e763495..386baaf756 100644 --- a/esphome/components/text/__init__.py +++ b/esphome/components/text/__init__.py @@ -1,18 +1,18 @@ from typing import Optional -import esphome.codegen as cg -import esphome.config_validation as cv + from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_ID, CONF_MODE, + CONF_MQTT_ID, CONF_ON_VALUE, CONF_TRIGGER_ID, - CONF_MQTT_ID, - CONF_WEB_SERVER_ID, CONF_VALUE, + CONF_WEB_SERVER_ID, ) - from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index f4e795924c..ba8a2def41 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -1,21 +1,21 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, CONF_ENTITY_CATEGORY, CONF_FILTERS, + CONF_FROM, CONF_ICON, CONF_ID, - CONF_ON_VALUE, - CONF_ON_RAW_VALUE, - CONF_TRIGGER_ID, CONF_MQTT_ID, - CONF_WEB_SERVER_ID, + CONF_ON_RAW_VALUE, + CONF_ON_VALUE, CONF_STATE, - CONF_FROM, CONF_TO, + CONF_TRIGGER_ID, + CONF_WEB_SERVER_ID, DEVICE_CLASS_DATE, DEVICE_CLASS_EMPTY, DEVICE_CLASS_TIMESTAMP, diff --git a/esphome/components/valve/__init__.py b/esphome/components/valve/__init__.py index c03d13fec8..3c03bab857 100644 --- a/esphome/components/valve/__init__.py +++ b/esphome/components/valve/__init__.py @@ -1,8 +1,8 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation -from esphome.automation import maybe_simple_id, Condition +from esphome.automation import Condition, maybe_simple_id +import esphome.codegen as cg from esphome.components import mqtt, web_server +import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, CONF_ID, From ddd80272386c923db6043e3659997c1e34398b3f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:33:12 +1200 Subject: [PATCH 0150/1052] [spi] Remove ``SPIDelegateDummy`` (#7215) --- esphome/components/spi/spi.cpp | 6 ------ esphome/components/spi/spi.h | 21 ++------------------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index b13826c443..f9435b0424 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -7,10 +7,6 @@ namespace spi { const char *const TAG = "spi"; -SPIDelegate *const SPIDelegate::NULL_DELEGATE = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - new SPIDelegateDummy(); -// https://bugs.llvm.org/show_bug.cgi?id=48040 - bool SPIDelegate::is_ready() { return true; } GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -79,8 +75,6 @@ void SPIComponent::dump_config() { } } -void SPIDelegateDummy::begin_transaction() { ESP_LOGE(TAG, "SPIDevice not initialised - did you call spi_setup()?"); } - uint8_t SPIDelegateBitBash::transfer(uint8_t data) { return this->transfer_(data, 8); } void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_(data, num_bits); } diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index f581dc3f56..4cd8d3383c 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -163,8 +163,6 @@ class Utility { } }; -class SPIDelegateDummy; - // represents a device attached to an SPI bus, with a defined clock rate, mode and bit order. On Arduino this is // a thin wrapper over SPIClass. class SPIDelegate { @@ -250,21 +248,6 @@ class SPIDelegate { uint32_t data_rate_{1000000}; SPIMode mode_{MODE0}; GPIOPin *cs_pin_{NullPin::NULL_PIN}; - static SPIDelegate *const NULL_DELEGATE; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -}; - -/** - * A dummy SPIDelegate that complains if it's used. - */ - -class SPIDelegateDummy : public SPIDelegate { - public: - SPIDelegateDummy() = default; - - uint8_t transfer(uint8_t data) override { return 0; } - void end_transaction() override{}; - - void begin_transaction() override; }; /** @@ -382,7 +365,7 @@ class SPIClient { virtual void spi_teardown() { this->parent_->unregister_device(this); - this->delegate_ = SPIDelegate::NULL_DELEGATE; + this->delegate_ = nullptr; } bool spi_is_ready() { return this->delegate_->is_ready(); } @@ -393,7 +376,7 @@ class SPIClient { uint32_t data_rate_{1000000}; SPIComponent *parent_{nullptr}; GPIOPin *cs_{nullptr}; - SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE}; + SPIDelegate *delegate_{nullptr}; }; /** From 132269c5b84c6c7502db239094bb14b27615de7e Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 7 Aug 2024 09:31:44 +0200 Subject: [PATCH 0151/1052] [code-quality] Apply ruff linting suggestions (#7206) --- esphome/config_validation.py | 2 +- esphome/mqtt.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 6e1d3ba2f9..ef60d6e0d6 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -91,7 +91,7 @@ _LOGGER = logging.getLogger(__name__) # pylint: disable=consider-using-f-string VARIABLE_PROG = re.compile( - "\\$([{0}]+|\\{{[{0}]*\\}})".format(VALID_SUBSTITUTIONS_CHARACTERS) + f"\\$([{VALID_SUBSTITUTIONS_CHARACTERS}]+|\\{{[{VALID_SUBSTITUTIONS_CHARACTERS}]*\\}})" ) # pylint: disable=invalid-name diff --git a/esphome/mqtt.py b/esphome/mqtt.py index d7e14a1d08..c1c45799cc 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -3,7 +3,6 @@ import hashlib import json import logging import ssl -import sys import time import paho.mqtt.client as mqtt @@ -103,10 +102,7 @@ def prepare( if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS) or config[CONF_MQTT].get( CONF_CERTIFICATE_AUTHORITY ): - if sys.version_info >= (2, 7, 13): - tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member - else: - tls_version = ssl.PROTOCOL_SSLv23 + tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member client.tls_set( ca_certs=None, certfile=None, From 2a8424a7f2022dced130a80139729015792b7f39 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 7 Aug 2024 09:32:06 +0200 Subject: [PATCH 0152/1052] [code-quality] Organise logger imports (#7205) --- esphome/components/logger/__init__.py | 33 ++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 99aa39c4ba..f30bc23e38 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -1,9 +1,21 @@ import re -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation from esphome.automation import LambdaAction +import esphome.codegen as cg +from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant +from esphome.components.esp32.const import ( + VARIANT_ESP32, + VARIANT_ESP32C2, + VARIANT_ESP32C3, + VARIANT_ESP32C6, + VARIANT_ESP32H2, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +) +from esphome.components.libretiny import get_libretiny_component, get_libretiny_family +from esphome.components.libretiny.const import COMPONENT_BK72XX, COMPONENT_RTL87XX +import esphome.config_validation as cv from esphome.const import ( CONF_ARGS, CONF_BAUD_RATE, @@ -18,27 +30,12 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_TX_BUFFER_SIZE, PLATFORM_BK72XX, - PLATFORM_RTL87XX, PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, + PLATFORM_RTL87XX, ) from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority -from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant -from esphome.components.esp32.const import ( - VARIANT_ESP32, - VARIANT_ESP32S2, - VARIANT_ESP32C3, - VARIANT_ESP32S3, - VARIANT_ESP32C2, - VARIANT_ESP32C6, - VARIANT_ESP32H2, -) -from esphome.components.libretiny import get_libretiny_component, get_libretiny_family -from esphome.components.libretiny.const import ( - COMPONENT_BK72XX, - COMPONENT_RTL87XX, -) CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") From 4b91ef5123e064d33975c928fa40e7cffaa5b468 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 7 Aug 2024 09:33:41 +0200 Subject: [PATCH 0153/1052] [code-quality] Apply ruff linting suggestions to core (#7207) --- esphome/core/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 9d3d14492e..a97c3b18c9 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -336,7 +336,7 @@ class ID: else: self.is_manual = is_manual self.is_declaration = is_declaration - self.type: Optional["MockObjClass"] = type + self.type: Optional[MockObjClass] = type def resolve(self, registered_ids): from esphome.config_validation import RESERVED_IDS @@ -500,7 +500,7 @@ class EsphomeCore: # The relative path to where all build files are stored self.build_path: Optional[str] = None # The validated configuration, this is None until the config has been validated - self.config: Optional["ConfigType"] = None + self.config: Optional[ConfigType] = 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) @@ -508,17 +508,17 @@ class EsphomeCore: # Task counter for pending tasks self.task_counter = 0 # The variable cache, for each ID this holds a MockObj of the variable obj - self.variables: dict[str, "MockObj"] = {} + self.variables: dict[str, MockObj] = {} # A list of statements that go in the main setup() block - self.main_statements: list["Statement"] = [] + self.main_statements: list[Statement] = [] # A list of statements to insert in the global block (includes and global variables) - self.global_statements: list["Statement"] = [] + self.global_statements: list[Statement] = [] # A set of platformio libraries to add to the project self.libraries: list[Library] = [] # A set of build flags to set in the platformio project self.build_flags: set[str] = set() # A set of defines to set for the compile process in esphome/core/defines.h - self.defines: set["Define"] = set() + self.defines: set[Define] = set() # A map of all platformio options to apply self.platformio_options: dict[str, Union[str, list[str]]] = {} # A set of strings of names of loaded integrations, used to find namespace ID conflicts From 9a9757ddebfc025d19c65c6c0c3467eeb887061f Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 8 Aug 2024 02:29:32 +0200 Subject: [PATCH 0154/1052] [code-quality] fix clang-tidy sprinkler (#7222) * fix clang-tidy * fix build error * clang-tidy * clang-tidy --- esphome/components/sprinkler/sprinkler.cpp | 4 ++-- esphome/components/sprinkler/sprinkler.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 982d9add1a..59565251c3 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -647,7 +647,7 @@ void Sprinkler::set_valve_run_duration(const optional valve_number, cons return; } auto call = this->valve_[valve_number.value()].run_duration_number->make_call(); - if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement() == min_str) { + if (this->valve_[valve_number.value()].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) { call.set_value(run_duration.value() / 60.0); } else { call.set_value(run_duration.value()); @@ -729,7 +729,7 @@ uint32_t Sprinkler::valve_run_duration(const size_t valve_number) { return 0; } if (this->valve_[valve_number].run_duration_number != nullptr) { - if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() == min_str) { + if (this->valve_[valve_number].run_duration_number->traits.get_unit_of_measurement() == MIN_STR) { return static_cast(roundf(this->valve_[valve_number].run_duration_number->state * 60)); } else { return static_cast(roundf(this->valve_[valve_number].run_duration_number->state)); diff --git a/esphome/components/sprinkler/sprinkler.h b/esphome/components/sprinkler/sprinkler.h index 5311ae4c05..c4a8b8aeb8 100644 --- a/esphome/components/sprinkler/sprinkler.h +++ b/esphome/components/sprinkler/sprinkler.h @@ -11,7 +11,7 @@ namespace esphome { namespace sprinkler { -const std::string min_str = "min"; +const std::string MIN_STR = "min"; enum SprinklerState : uint8_t { // NOTE: these states are used by both SprinklerValveOperator and Sprinkler (the controller)! From 24b6c1d3eb85e1c50552a2aff040e0250d749e26 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 8 Aug 2024 02:30:49 +0200 Subject: [PATCH 0155/1052] [code-quality] __attribute__((packed)) (#7221) --- esphome/components/climate/climate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 7c2a0b1ed3..d81702fb0c 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -141,7 +141,7 @@ struct ClimateDeviceRestoreState { float target_temperature_low; float target_temperature_high; }; - }; + } __attribute__((packed)); float target_humidity; /// Convert this struct to a climate call that can be performed. From 7fd65987d33f21433975ef252c29f20a33ef33a0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 8 Aug 2024 03:29:49 +0100 Subject: [PATCH 0156/1052] hx711: Check for DOUT going high after a reading (#7214) --- esphome/components/hx711/hx711.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/esphome/components/hx711/hx711.cpp b/esphome/components/hx711/hx711.cpp index dbbf4c91f4..1a7169eed7 100644 --- a/esphome/components/hx711/hx711.cpp +++ b/esphome/components/hx711/hx711.cpp @@ -39,8 +39,8 @@ bool HX711Sensor::read_sensor_(uint32_t *result) { return false; } - this->status_clear_warning(); uint32_t data = 0; + bool final_dout; { InterruptLock lock; @@ -59,8 +59,17 @@ bool HX711Sensor::read_sensor_(uint32_t *result) { this->sck_pin_->digital_write(false); delayMicroseconds(1); } + final_dout = this->dout_pin_->digital_read(); } + if (!final_dout) { + ESP_LOGW(TAG, "HX711 DOUT pin not high after reading (data 0x%" PRIx32 ")!", data); + this->status_set_warning(); + return false; + } + + this->status_clear_warning(); + if (data & 0x800000ULL) { data |= 0xFF000000ULL; } From 3f1d2c0cafe246c88e4d257b36e70c5e3be4ec71 Mon Sep 17 00:00:00 2001 From: dentra Date: Thu, 8 Aug 2024 07:49:37 +0300 Subject: [PATCH 0157/1052] [mqtt] Add extended device info (#7194) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mqtt/mqtt_component.cpp | 36 +++++++++++++++++++--- esphome/components/mqtt/mqtt_const.h | 2 ++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index bb46ce732d..295fbba5e5 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -150,12 +150,40 @@ bool MQTTComponent::send_discovery_() { const std::string &node_area = App.get_area(); JsonObject device_info = root.createNestedObject(MQTT_DEVICE); - device_info[MQTT_DEVICE_IDENTIFIERS] = get_mac_address(); + const auto mac = get_mac_address(); + device_info[MQTT_DEVICE_IDENTIFIERS] = mac; device_info[MQTT_DEVICE_NAME] = node_friendly_name; - device_info[MQTT_DEVICE_SW_VERSION] = "esphome v" ESPHOME_VERSION " " + App.get_compilation_time(); +#ifdef ESPHOME_PROJECT_NAME + device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_PROJECT_VERSION " (ESPHome " ESPHOME_VERSION ")"; + const char *model = std::strchr(ESPHOME_PROJECT_NAME, '.'); + if (model == nullptr) { // must never happen but check anyway + device_info[MQTT_DEVICE_MODEL] = ESPHOME_BOARD; + device_info[MQTT_DEVICE_MANUFACTURER] = ESPHOME_PROJECT_NAME; + } else { + device_info[MQTT_DEVICE_MODEL] = model + 1; + device_info[MQTT_DEVICE_MANUFACTURER] = std::string(ESPHOME_PROJECT_NAME, model - ESPHOME_PROJECT_NAME); + } +#else + device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_VERSION " (" + App.get_compilation_time() + ")"; device_info[MQTT_DEVICE_MODEL] = ESPHOME_BOARD; - device_info[MQTT_DEVICE_MANUFACTURER] = "espressif"; - device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area; +#if defined(USE_ESP8266) || defined(USE_ESP32) + device_info[MQTT_DEVICE_MANUFACTURER] = "Espressif"; +#elif defined(USE_RP2040) + device_info[MQTT_DEVICE_MANUFACTURER] = "Raspberry Pi"; +#elif defined(USE_BK72XX) + device_info[MQTT_DEVICE_MANUFACTURER] = "Beken"; +#elif defined(USE_RTL87XX) + device_info[MQTT_DEVICE_MANUFACTURER] = "Realtek"; +#elif defined(USE_HOST) + device_info[MQTT_DEVICE_MANUFACTURER] = "Host"; +#endif +#endif + if (!node_area.empty()) { + device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area; + } + + device_info[MQTT_DEVICE_CONNECTIONS][0][0] = "mac"; + device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac; }, this->qos_, discovery_info.retain); } diff --git a/esphome/components/mqtt/mqtt_const.h b/esphome/components/mqtt/mqtt_const.h index 0e063c66d2..71f169fbe8 100644 --- a/esphome/components/mqtt/mqtt_const.h +++ b/esphome/components/mqtt/mqtt_const.h @@ -62,6 +62,7 @@ constexpr const char *const MQTT_DEVICE_MODEL = "mdl"; constexpr const char *const MQTT_DEVICE_NAME = "name"; constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa"; constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw"; +constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw"; constexpr const char *const MQTT_DOCKED_TEMPLATE = "dock_tpl"; constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t"; constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "fx_cmd_t"; @@ -322,6 +323,7 @@ constexpr const char *const MQTT_DEVICE_MODEL = "model"; constexpr const char *const MQTT_DEVICE_NAME = "name"; constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area"; constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version"; +constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw_version"; constexpr const char *const MQTT_DOCKED_TEMPLATE = "docked_template"; constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic"; constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "effect_command_topic"; From a3d5b69a9c7690efcfb3441e2d69166bb8cc703e Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 8 Aug 2024 07:02:41 +0200 Subject: [PATCH 0158/1052] [code-quality] NOLINT readability-identifier-naming (#7220) --- esphome/core/entity_base.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index 434111de79..4ca21f9ee5 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -63,7 +63,7 @@ class EntityBase { EntityCategory entity_category_{ENTITY_CATEGORY_NONE}; }; -class EntityBase_DeviceClass { +class EntityBase_DeviceClass { // NOLINT(readability-identifier-naming) public: /// Get the device class, using the manual override if set. std::string get_device_class(); @@ -74,7 +74,7 @@ class EntityBase_DeviceClass { const char *device_class_{nullptr}; ///< Device class override }; -class EntityBase_UnitOfMeasurement { +class EntityBase_UnitOfMeasurement { // NOLINT(readability-identifier-naming) public: /// Get the unit of measurement, using the manual override if set. std::string get_unit_of_measurement(); From b71c03424ecf1aaa74a12bdc86e28bdbed9488dd Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 8 Aug 2024 07:02:55 +0200 Subject: [PATCH 0159/1052] [code-quality] Organise time imports (#7219) --- esphome/components/time/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/components/time/__init__.py b/esphome/components/time/__init__.py index c888705ba2..6a3368ca73 100644 --- a/esphome/components/time/__init__.py +++ b/esphome/components/time/__init__.py @@ -1,32 +1,32 @@ -import logging from importlib import resources +import logging from typing import Optional import tzlocal +from esphome import automation +from esphome.automation import Condition import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation from esphome.const import ( - CONF_ID, + CONF_AT, CONF_CRON, CONF_DAYS_OF_MONTH, CONF_DAYS_OF_WEEK, + CONF_HOUR, CONF_HOURS, + CONF_ID, + CONF_MINUTE, CONF_MINUTES, CONF_MONTHS, CONF_ON_TIME, CONF_ON_TIME_SYNC, + CONF_SECOND, CONF_SECONDS, CONF_TIMEZONE, CONF_TRIGGER_ID, - CONF_AT, - CONF_SECOND, - CONF_HOUR, - CONF_MINUTE, ) from esphome.core import coroutine_with_priority -from esphome.automation import Condition _LOGGER = logging.getLogger(__name__) From a47a17d7e7d4649d7a92c3fb96c921374b14d2d6 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 8 Aug 2024 18:24:10 +1000 Subject: [PATCH 0160/1052] [lvgl] Fix set state on updates (#7227) --- esphome/components/lvgl/lvgl_esphome.h | 4 ++-- esphome/components/lvgl/widgets/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 45841b99d9..1497e1004a 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -1,9 +1,9 @@ #pragma once #include "esphome/core/defines.h" -#ifdef USE_LVGL_BINARY_SENSOR +#ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" -#endif // USE_LVGL_BINARY_SENSOR +#endif // USE_BINARY_SENSOR #ifdef USE_LVGL_ROTARY_ENCODER #include "esphome/components/rotary_encoder/rotary_encoder.h" #endif // USE_LVGL_ROTARY_ENCODER diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index f1946015bc..603de6aa3e 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -271,6 +271,7 @@ async def set_obj_properties(w: Widget, config): """Generate a list of C++ statements to apply properties to an lv_obj_t""" if layout := config.get(CONF_LAYOUT): layout_type: str = layout[CONF_TYPE] + add_lv_use(layout_type) lv_obj.set_layout(w.obj, literal(f"LV_LAYOUT_{layout_type.upper()}")) if layout_type == TYPE_GRID: wid = config[CONF_ID] @@ -334,7 +335,7 @@ async def set_obj_properties(w: Widget, config): for key, value in states.items(): if isinstance(value, cv.Lambda): lambs[key] = value - elif value == "true": + elif value: adds.add(key) else: clears.add(key) From b43c5b851aaaf15f170dbaf23a6ec5feaf4ee664 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Fri, 9 Aug 2024 13:15:25 +0200 Subject: [PATCH 0161/1052] add missing overrides (#7231) --- esphome/components/lvgl/number/lvgl_number.h | 2 +- esphome/components/lvgl/switch/lvgl_switch.h | 2 +- esphome/components/lvgl/text/lvgl_text.h | 2 +- esphome/components/spi_led_strip/spi_led_strip.h | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/lvgl/number/lvgl_number.h b/esphome/components/lvgl/number/lvgl_number.h index 461ea51be4..659fc615c9 100644 --- a/esphome/components/lvgl/number/lvgl_number.h +++ b/esphome/components/lvgl/number/lvgl_number.h @@ -19,7 +19,7 @@ class LVGLNumber : public number::Number { } protected: - void control(float value) { + void control(float value) override { if (this->control_lambda_ != nullptr) this->control_lambda_(value); else diff --git a/esphome/components/lvgl/switch/lvgl_switch.h b/esphome/components/lvgl/switch/lvgl_switch.h index f20f4ed960..67be11faba 100644 --- a/esphome/components/lvgl/switch/lvgl_switch.h +++ b/esphome/components/lvgl/switch/lvgl_switch.h @@ -19,7 +19,7 @@ class LVGLSwitch : public switch_::Switch { } protected: - void write_state(bool value) { + void write_state(bool value) override { if (this->state_lambda_ != nullptr) this->state_lambda_(value); else diff --git a/esphome/components/lvgl/text/lvgl_text.h b/esphome/components/lvgl/text/lvgl_text.h index 8dc0281364..4bd5b76744 100644 --- a/esphome/components/lvgl/text/lvgl_text.h +++ b/esphome/components/lvgl/text/lvgl_text.h @@ -19,7 +19,7 @@ class LVGLText : public text::Text { } protected: - void control(const std::string &value) { + void control(const std::string &value) override { if (this->control_lambda_ != nullptr) this->control_lambda_(value); else diff --git a/esphome/components/spi_led_strip/spi_led_strip.h b/esphome/components/spi_led_strip/spi_led_strip.h index 0d8c1c1e1c..8b713378ec 100644 --- a/esphome/components/spi_led_strip/spi_led_strip.h +++ b/esphome/components/spi_led_strip/spi_led_strip.h @@ -13,7 +13,7 @@ class SpiLedStrip : public light::AddressableLight, public spi::SPIDevice { public: - void setup() { this->spi_setup(); } + void setup() override { this->spi_setup(); } int32_t size() const override { return this->num_leds_; } @@ -43,7 +43,7 @@ class SpiLedStrip : public light::AddressableLight, memset(this->buf_, 0, 4); } - void dump_config() { + void dump_config() override { esph_log_config(TAG, "SPI LED Strip:"); esph_log_config(TAG, " LEDs: %d", this->num_leds_); if (this->data_rate_ >= spi::DATA_RATE_1MHZ) From 15602b0664b71ac6362e5cf4ff3c0e94cf066c38 Mon Sep 17 00:00:00 2001 From: Michael Davidson Date: Mon, 12 Aug 2024 06:06:29 +1000 Subject: [PATCH 0162/1052] Add text_align_to_string (#7243) --- esphome/components/display/display.cpp | 31 ++++++++++++++++++++++++++ esphome/components/display/display.h | 3 +++ 2 files changed, 34 insertions(+) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 75205292f7..63c74e09ca 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -675,5 +675,36 @@ void DisplayPage::set_prev(DisplayPage *prev) { this->prev_ = prev; } void DisplayPage::set_next(DisplayPage *next) { this->next_ = next; } const display_writer_t &DisplayPage::get_writer() const { return this->writer_; } +const LogString *text_align_to_string(TextAlign textalign) { + switch (textalign) { + case TextAlign::TOP_LEFT: + return LOG_STR("TOP_LEFT"); + case TextAlign::TOP_CENTER: + return LOG_STR("TOP_CENTER"); + case TextAlign::TOP_RIGHT: + return LOG_STR("TOP_RIGHT"); + case TextAlign::CENTER_LEFT: + return LOG_STR("CENTER_LEFT"); + case TextAlign::CENTER: + return LOG_STR("CENTER"); + case TextAlign::CENTER_RIGHT: + return LOG_STR("CENTER_RIGHT"); + case TextAlign::BASELINE_LEFT: + return LOG_STR("BASELINE_LEFT"); + case TextAlign::BASELINE_CENTER: + return LOG_STR("BASELINE_CENTER"); + case TextAlign::BASELINE_RIGHT: + return LOG_STR("BASELINE_RIGHT"); + case TextAlign::BOTTOM_LEFT: + return LOG_STR("BOTTOM_LEFT"); + case TextAlign::BOTTOM_CENTER: + return LOG_STR("BOTTOM_CENTER"); + case TextAlign::BOTTOM_RIGHT: + return LOG_STR("BOTTOM_RIGHT"); + default: + return LOG_STR("UNKNOWN"); + } +} + } // namespace display } // namespace esphome diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 4ee7ef93cb..34feafea6e 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -8,6 +8,7 @@ #include "esphome/core/color.h" #include "esphome/core/automation.h" #include "esphome/core/time.h" +#include "esphome/core/log.h" #include "display_color_utils.h" #ifdef USE_GRAPH @@ -737,5 +738,7 @@ class DisplayOnPageChangeTrigger : public Trigger DisplayPage *to_{nullptr}; }; +const LogString *text_align_to_string(TextAlign textalign); + } // namespace display } // namespace esphome From 442e765187e73aeff3e24f727de5662359943493 Mon Sep 17 00:00:00 2001 From: Nis Wechselberg Date: Mon, 12 Aug 2024 04:18:11 +0200 Subject: [PATCH 0163/1052] [sml] Fixed crashing sml parser (#7235) --- esphome/components/sml/sml_parser.cpp | 72 ++++++++++++++++++--------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/esphome/components/sml/sml_parser.cpp b/esphome/components/sml/sml_parser.cpp index c782c0fc5e..2cc71e87fa 100644 --- a/esphome/components/sml/sml_parser.cpp +++ b/esphome/components/sml/sml_parser.cpp @@ -10,7 +10,7 @@ SmlFile::SmlFile(bytes buffer) : buffer_(std::move(buffer)) { this->pos_ = 0; while (this->pos_ < this->buffer_.size()) { if (this->buffer_[this->pos_] == 0x00) - break; // fill byte detected -> no more messages + break; // EndOfSmlMsg SmlNode message = SmlNode(); if (!this->setup_node(&message)) @@ -20,40 +20,66 @@ SmlFile::SmlFile(bytes buffer) : buffer_(std::move(buffer)) { } bool SmlFile::setup_node(SmlNode *node) { - uint8_t type = this->buffer_[this->pos_] >> 4; // type including overlength info - uint8_t length = this->buffer_[this->pos_] & 0x0f; // length including TL bytes - bool is_list = (type & 0x07) == SML_LIST; - bool has_extended_length = type & 0x08; // we have a long list/value (>15 entries) - uint8_t parse_length = length; - if (has_extended_length) { - length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f); - parse_length = length; + // If the TL field is 0x00, this is the end of the message + // (see 6.3.1 of SML protocol definition) + if (this->buffer_[this->pos_] == 0x00) { + // Increment past this byte and signal that the message is done this->pos_ += 1; + return true; } - if (this->pos_ + parse_length >= this->buffer_.size()) + // Extract data from initial TL field + uint8_t type = (this->buffer_[this->pos_] >> 4) & 0x07; // type without overlength info + bool overlength = (this->buffer_[this->pos_] >> 4) & 0x08; // overlength information + uint8_t length = this->buffer_[this->pos_] & 0x0f; // length (including TL bytes) + + // Check if we need additional length bytes + if (overlength) { + // Shift the current length to the higher nibble + // and add the lower nibble of the next byte to the length + length = (length << 4) + (this->buffer_[this->pos_ + 1] & 0x0f); + // We are basically done with the first TL field now, + // so increment past that, we now point to the second TL field + this->pos_ += 1; + // Decrement the length for value fields (not lists), + // since the byte we just handled is counted as part of the field + // in case of values but not for lists + if (type != SML_LIST) + length -= 1; + + // Technically, this is not enough, the standard allows for more than two length fields. + // However I don't think it is very common to have more than 255 entries in a list + } + + // We are done with the last TL field(s), so advance the position + this->pos_ += 1; + // and decrement the length for non-list fields + if (type != SML_LIST) + length -= 1; + + // Check if the buffer length is long enough + if (this->pos_ + length > this->buffer_.size()) return false; - node->type = type & 0x07; + node->type = type; node->nodes.clear(); node->value_bytes.clear(); - // if the list is a has_extended_length list with e.g. 16 elements this is a 0x00 byte but not the end of message - if (!has_extended_length && this->buffer_[this->pos_] == 0x00) { // end of message - this->pos_ += 1; - } else if (is_list) { // list - this->pos_ += 1; - node->nodes.reserve(parse_length); - for (size_t i = 0; i != parse_length; i++) { + if (type == SML_LIST) { + node->nodes.reserve(length); + for (size_t i = 0; i != length; i++) { SmlNode child_node = SmlNode(); if (!this->setup_node(&child_node)) return false; node->nodes.emplace_back(child_node); } - } else { // value - node->value_bytes = - bytes(this->buffer_.begin() + this->pos_ + 1, this->buffer_.begin() + this->pos_ + parse_length); - this->pos_ += parse_length; + } else { + // Value starts at the current position + // Value ends "length" bytes later, + // (since the TL field is counted but already subtracted from length) + node->value_bytes = bytes(this->buffer_.begin() + this->pos_, this->buffer_.begin() + this->pos_ + length); + // Increment the pointer past all consumed bytes + this->pos_ += length; } return true; } @@ -101,7 +127,7 @@ int64_t bytes_to_int(const bytes &buffer) { // see https://stackoverflow.com/questions/42534749/signed-extension-from-24-bit-to-32-bit-in-c if (buffer.size() < 8) { const int bits = buffer.size() * 8; - const uint64_t m = 1u << (bits - 1); + const uint64_t m = 1ull << (bits - 1); tmp = (tmp ^ m) - m; } From d04e706295a495c0b8dbbf08c6ad95bbfa623712 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 12 Aug 2024 04:20:51 +0200 Subject: [PATCH 0164/1052] Allow project name and version as improv_serial identity (#7248) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/improv_serial/__init__.py | 14 ++++---------- .../improv_serial/improv_serial_component.cpp | 4 ++++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/esphome/components/improv_serial/__init__.py b/esphome/components/improv_serial/__init__.py index 2b377d77b8..544af212e0 100644 --- a/esphome/components/improv_serial/__init__.py +++ b/esphome/components/improv_serial/__init__.py @@ -1,12 +1,10 @@ +import esphome.codegen as cg from esphome.components import improv_base from esphome.components.esp32 import get_esp32_variant -from esphome.components.esp32.const import ( - VARIANT_ESP32S3, -) +from esphome.components.esp32.const import VARIANT_ESP32S3 from esphome.components.logger import USB_CDC -from esphome.const import CONF_BAUD_RATE, CONF_HARDWARE_UART, CONF_ID, CONF_LOGGER -import esphome.codegen as cg import esphome.config_validation as cv +from esphome.const import CONF_BAUD_RATE, CONF_HARDWARE_UART, CONF_ID, CONF_LOGGER from esphome.core import CORE import esphome.final_validate as fv @@ -19,11 +17,7 @@ improv_serial_ns = cg.esphome_ns.namespace("improv_serial") ImprovSerialComponent = improv_serial_ns.class_("ImprovSerialComponent", cg.Component) CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(ImprovSerialComponent), - } - ) + cv.Schema({cv.GenerateID(): cv.declare_id(ImprovSerialComponent)}) .extend(improv_base.IMPROV_SCHEMA) .extend(cv.COMPONENT_SCHEMA) ) diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 12809e38cb..425a5c8576 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -170,7 +170,11 @@ std::vector ImprovSerialComponent::build_rpc_settings_response_(improv: } std::vector ImprovSerialComponent::build_version_info_() { +#ifdef ESPHOME_PROJECT_NAME + std::vector infos = {ESPHOME_PROJECT_NAME, ESPHOME_PROJECT_VERSION, ESPHOME_VARIANT, App.get_name()}; +#else std::vector infos = {"ESPHome", ESPHOME_VERSION, ESPHOME_VARIANT, App.get_name()}; +#endif std::vector data = improv::build_rpc_response(improv::GET_DEVICE_INFO, infos, false); return data; }; From 34d435c99643280c297e0741a14a58574e7c879e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:56:54 +1000 Subject: [PATCH 0165/1052] [lvgl] Implement default group for encoders (#7242) Co-authored-by: clydeps --- esphome/components/lvgl/__init__.py | 6 ++++-- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/encoders.py | 10 +++++++--- esphome/components/lvgl/touchscreens.py | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 87fbcab4dc..6bf6e287f8 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -47,6 +47,7 @@ from .types import ( IdleTrigger, ObjUpdateAction, lv_font_t, + lv_group_t, lv_style_t, lvgl_ns, ) @@ -335,8 +336,9 @@ CONFIG_SCHEMA = ( cv.Optional(df.CONF_THEME): cv.Schema( {cv.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()} ), - cv.GenerateID(df.CONF_TOUCHSCREENS): touchscreen_schema, - cv.GenerateID(df.CONF_ENCODERS): ENCODERS_CONFIG, + cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, + cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, + cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t), } ) .extend(DISP_BG_SCHEMA) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index d0047b59f7..1c6fd2678c 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -386,6 +386,7 @@ CONF_COLOR_DEPTH = "color_depth" CONF_CONTROL = "control" CONF_DEFAULT = "default" CONF_DEFAULT_FONT = "default_font" +CONF_DEFAULT_GROUP = "default_group" CONF_DIR = "dir" CONF_DISPLAYS = "displays" CONF_ENCODERS = "encoders" diff --git a/esphome/components/lvgl/encoders.py b/esphome/components/lvgl/encoders.py index caddc2e47f..cfd0e42996 100644 --- a/esphome/components/lvgl/encoders.py +++ b/esphome/components/lvgl/encoders.py @@ -5,6 +5,7 @@ import esphome.config_validation as cv from esphome.const import CONF_GROUP, CONF_ID, CONF_SENSOR from .defines import ( + CONF_DEFAULT_GROUP, CONF_ENCODERS, CONF_ENTER_BUTTON, CONF_LEFT_BUTTON, @@ -38,7 +39,10 @@ ENCODERS_CONFIG = cv.ensure_list( async def encoders_to_code(var, config): - for enc_conf in config.get(CONF_ENCODERS, ()): + default_group = lv_Pvariable(lv_group_t, config[CONF_DEFAULT_GROUP]) + lv_assign(default_group, lv_expr.group_create()) + lv.group_set_default(default_group) + for enc_conf in config[CONF_ENCODERS]: lvgl_components_required.add("KEY_LISTENER") lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds @@ -60,6 +64,6 @@ async def encoders_to_code(var, config): if group := enc_conf.get(CONF_GROUP): group = lv_Pvariable(lv_group_t, group) lv_assign(group, lv_expr.group_create()) - lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group) else: - lv.indev_drv_register(listener.get_drv()) + group = default_group + lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group) diff --git a/esphome/components/lvgl/touchscreens.py b/esphome/components/lvgl/touchscreens.py index 292b0873f3..4d430a428e 100644 --- a/esphome/components/lvgl/touchscreens.py +++ b/esphome/components/lvgl/touchscreens.py @@ -34,7 +34,7 @@ def touchscreen_schema(config): async def touchscreens_to_code(var, config): - for tconf in config.get(CONF_TOUCHSCREENS, ()): + for tconf in config[CONF_TOUCHSCREENS]: lvgl_components_required.add(CONF_TOUCHSCREEN) touchscreen = await cg.get_variable(tconf[CONF_TOUCHSCREEN_ID]) lpt = tconf[CONF_LONG_PRESS_TIME].total_milliseconds From f2e99fa3192f3792189c73326be5fa6563cdf032 Mon Sep 17 00:00:00 2001 From: "David K." <142583+neffs@users.noreply.github.com> Date: Mon, 12 Aug 2024 06:14:58 +0200 Subject: [PATCH 0166/1052] [bme68x_bsec2_i2c] BME68X Temperature+Pressure+Humidity+Gas Sensor via BSEC2 (#4585) * Added initial bme68x component * Initialize all child sensors to nullptr This was added to all other sensors in #3808 * Update BSEC2 and BME68x Libraries Current versions from Bosch Sensortec * Add myself to codeowners for bme68x_bsec * Move constants to const.py, according to ci-custom checks Move constants to const.py, according to ci-custom checks * Update library dependencies We'll stick with 1.4.2200 for now. 1.4.2200 is not on platform.io registry, use tag instead. Update to 1.5.2400 needs some work due to multi instance support. * Update BSEC2 to 1.6.2400 * Add consts to bme680x_bsec Enable inclusion with external_components * Update device class for pressure * Update to use multisensor API * Tidy up some constants * Add tests * Remove scd30 changes * Import CONF_SAMPLE_RATE * Pull BSEC config blob from repo based on config * Rename component to `bme68x_bsec_i2c` * Fix tests + codeowners * Cleanup for review * Rename using `bsec2` * Apply suggestions from code review Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Download file during validation stage, instead * Make `dump_config()` only dump stuff * Compile safely without sensor and text sensor headers * Use `intf_ptr` * Save state if measuring static IAQ, too * Update CODEOWNERS * Simplify esphome/components/bme68x_bsec2_i2c/__init__.py Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> * Remove extraneous colon & imports * Track & save the maximum accuracy value * Polish up accuracy sensor handling * Log static sensor, update `defines.h` * Walruses make it better * Add some logging of setup failures * Update esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp Co-authored-by: Trevor North * Break out some things * Update CODEOWNERS * Update CODEOWNERS take 2 * Use `add_extra` in base schema * Another walrus in the sensor Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --------- Co-authored-by: Keith Burzinski Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Trevor North --- CODEOWNERS | 2 + esphome/components/bme68x_bsec2/__init__.py | 196 +++++++ .../components/bme68x_bsec2/bme68x_bsec2.cpp | 523 ++++++++++++++++++ .../components/bme68x_bsec2/bme68x_bsec2.h | 163 ++++++ esphome/components/bme68x_bsec2/sensor.py | 130 +++++ .../components/bme68x_bsec2/text_sensor.py | 33 ++ .../components/bme68x_bsec2_i2c/__init__.py | 28 + .../bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp | 53 ++ .../bme68x_bsec2_i2c/bme68x_bsec2_i2c.h | 28 + esphome/core/defines.h | 3 +- tests/components/bme68x_bsec2_i2c/common.yaml | 34 ++ .../bme68x_bsec2_i2c/test.esp32-ard.yaml | 5 + .../bme68x_bsec2_i2c/test.esp32-c3-ard.yaml | 5 + .../bme68x_bsec2_i2c/test.esp32-s2-ard.yaml | 5 + .../bme68x_bsec2_i2c/test.esp32-s3-ard.yaml | 5 + .../bme68x_bsec2_i2c/test.esp8266-ard.yaml | 5 + 16 files changed, 1217 insertions(+), 1 deletion(-) create mode 100644 esphome/components/bme68x_bsec2/__init__.py create mode 100644 esphome/components/bme68x_bsec2/bme68x_bsec2.cpp create mode 100644 esphome/components/bme68x_bsec2/bme68x_bsec2.h create mode 100644 esphome/components/bme68x_bsec2/sensor.py create mode 100644 esphome/components/bme68x_bsec2/text_sensor.py create mode 100644 esphome/components/bme68x_bsec2_i2c/__init__.py create mode 100644 esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp create mode 100644 esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.h create mode 100644 tests/components/bme68x_bsec2_i2c/common.yaml create mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-ard.yaml create mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-c3-ard.yaml create mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-s2-ard.yaml create mode 100644 tests/components/bme68x_bsec2_i2c/test.esp32-s3-ard.yaml create mode 100644 tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 82e6e0ea4b..999449a3df 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -65,6 +65,8 @@ esphome/components/bluetooth_proxy/* @jesserockz esphome/components/bme280_base/* @esphome/core esphome/components/bme280_spi/* @apbodrov esphome/components/bme680_bsec/* @trvrnrth +esphome/components/bme68x_bsec2/* @kbx81 @neffs +esphome/components/bme68x_bsec2_i2c/* @kbx81 @neffs esphome/components/bmi160/* @flaviut esphome/components/bmp3xx/* @latonita esphome/components/bmp3xx_base/* @latonita @martgras diff --git a/esphome/components/bme68x_bsec2/__init__.py b/esphome/components/bme68x_bsec2/__init__.py new file mode 100644 index 0000000000..1930c7c9e3 --- /dev/null +++ b/esphome/components/bme68x_bsec2/__init__.py @@ -0,0 +1,196 @@ +import hashlib +from pathlib import Path + +from esphome import core, external_files +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + CONF_MODEL, + CONF_RAW_DATA_ID, + CONF_SAMPLE_RATE, + CONF_TEMPERATURE_OFFSET, +) + +CODEOWNERS = ["@neffs", "@kbx81"] + +DOMAIN = "bme68x_bsec2" + +BSEC2_LIBRARY_VERSION = "v1.7.2502" + +CONF_ALGORITHM_OUTPUT = "algorithm_output" +CONF_BME68X_BSEC2_ID = "bme68x_bsec2_id" +CONF_IAQ_MODE = "iaq_mode" +CONF_OPERATING_AGE = "operating_age" +CONF_STATE_SAVE_INTERVAL = "state_save_interval" +CONF_SUPPLY_VOLTAGE = "supply_voltage" + +bme68x_bsec2_ns = cg.esphome_ns.namespace("bme68x_bsec2") +BME68xBSEC2Component = bme68x_bsec2_ns.class_("BME68xBSEC2Component", cg.Component) + + +MODEL_OPTIONS = ["bme680", "bme688"] + +AlgorithmOutput = bme68x_bsec2_ns.enum("AlgorithmOutput") +ALGORITHM_OUTPUT_OPTIONS = { + "classification": AlgorithmOutput.ALGORITHM_OUTPUT_CLASSIFICATION, + "regression": AlgorithmOutput.ALGORITHM_OUTPUT_REGRESSION, +} + +OperatingAge = bme68x_bsec2_ns.enum("OperatingAge") +OPERATING_AGE_OPTIONS = { + "4d": OperatingAge.OPERATING_AGE_4D, + "28d": OperatingAge.OPERATING_AGE_28D, +} + +SampleRate = bme68x_bsec2_ns.enum("SampleRate") +SAMPLE_RATE_OPTIONS = { + "LP": SampleRate.SAMPLE_RATE_LP, + "ULP": SampleRate.SAMPLE_RATE_ULP, +} + +Voltage = bme68x_bsec2_ns.enum("Voltage") +VOLTAGE_OPTIONS = { + "1.8V": Voltage.VOLTAGE_1_8V, + "3.3V": Voltage.VOLTAGE_3_3V, +} + +ALGORITHM_OUTPUT_FILE_NAME = { + "classification": "sel", + "regression": "reg", +} + +SAMPLE_RATE_FILE_NAME = { + "LP": "3s", + "ULP": "300s", +} + +VOLTAGE_FILE_NAME = { + "1.8V": "18v", + "3.3V": "33v", +} + + +def _compute_local_file_path(url: str) -> Path: + h = hashlib.new("sha256") + h.update(url.encode()) + key = h.hexdigest()[:8] + base_dir = external_files.compute_local_file_dir(DOMAIN) + return base_dir / key + + +def _compute_url(config: dict) -> str: + model = config.get(CONF_MODEL) + operating_age = config.get(CONF_OPERATING_AGE) + sample_rate = SAMPLE_RATE_FILE_NAME[config.get(CONF_SAMPLE_RATE)] + volts = VOLTAGE_FILE_NAME[config.get(CONF_SUPPLY_VOLTAGE)] + if model == "bme688": + algo = ALGORITHM_OUTPUT_FILE_NAME[ + config.get(CONF_ALGORITHM_OUTPUT, "classification") + ] + filename = "bsec_selectivity" + else: + algo = "iaq" + filename = "bsec_iaq" + return f"https://raw.githubusercontent.com/boschsensortec/Bosch-BSEC2-Library/{BSEC2_LIBRARY_VERSION}/src/config/{model}/{model}_{algo}_{volts}_{sample_rate}_{operating_age}/{filename}.txt" + + +def download_bme68x_blob(config): + url = _compute_url(config) + path = _compute_local_file_path(url) + external_files.download_content(url, path) + + return config + + +def validate_bme68x(config): + if CONF_ALGORITHM_OUTPUT not in config: + return config + + if config[CONF_MODEL] != "bme688": + raise cv.Invalid(f"{CONF_ALGORITHM_OUTPUT} is only valid for BME688") + + if config[CONF_ALGORITHM_OUTPUT] == "regression" and ( + config[CONF_OPERATING_AGE] != "4d" + or config[CONF_SAMPLE_RATE] != "ULP" + or config[CONF_SUPPLY_VOLTAGE] != "1.8V" + ): + raise cv.Invalid( + f" To use '{CONF_ALGORITHM_OUTPUT}: regression', {CONF_OPERATING_AGE} must be '4d', {CONF_SAMPLE_RATE} must be 'ULP' and {CONF_SUPPLY_VOLTAGE} must be '1.8V'" + ) + return config + + +CONFIG_SCHEMA_BASE = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BME68xBSEC2Component), + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + cv.Required(CONF_MODEL): cv.one_of(*MODEL_OPTIONS, lower=True), + cv.Optional(CONF_ALGORITHM_OUTPUT): cv.enum( + ALGORITHM_OUTPUT_OPTIONS, lower=True + ), + cv.Optional(CONF_OPERATING_AGE, default="28d"): cv.enum( + OPERATING_AGE_OPTIONS, lower=True + ), + cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum( + SAMPLE_RATE_OPTIONS, upper=True + ), + cv.Optional(CONF_SUPPLY_VOLTAGE, default="3.3V"): cv.enum( + VOLTAGE_OPTIONS, upper=True + ), + cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, + cv.Optional( + CONF_STATE_SAVE_INTERVAL, default="6hours" + ): cv.positive_time_period_minutes, + }, + ) + .add_extra(cv.only_with_arduino) + .add_extra(validate_bme68x) + .add_extra(download_bme68x_blob) +) + + +async def to_code_base(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + if algo_output := config.get(CONF_ALGORITHM_OUTPUT): + cg.add(var.set_algorithm_output(algo_output)) + cg.add(var.set_operating_age(config[CONF_OPERATING_AGE])) + cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) + cg.add(var.set_voltage(config[CONF_SUPPLY_VOLTAGE])) + cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET])) + cg.add( + var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds) + ) + + path = _compute_local_file_path(_compute_url(config)) + + try: + with open(path, encoding="utf-8") as f: + bsec2_iaq_config = f.read() + except Exception as e: + raise core.EsphomeError(f"Could not open binary configuration file {path}: {e}") + + # Convert retrieved BSEC2 config to an array of ints + rhs = [int(x) for x in bsec2_iaq_config.split(",")] + # Create an array which will reside in program memory and configure the sensor instance to use it + 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) + cg.add_library( + "BME68x Sensor library", + "1.1.40407", + ) + cg.add_library( + "BSEC2 Software Library", + None, + f"https://github.com/boschsensortec/Bosch-BSEC2-Library.git#{BSEC2_LIBRARY_VERSION}", + ) + + cg.add_define("USE_BSEC2") + + return var diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp new file mode 100644 index 0000000000..5425bbd5b7 --- /dev/null +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp @@ -0,0 +1,523 @@ +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_BSEC2 +#include "bme68x_bsec2.h" + +#include + +namespace esphome { +namespace bme68x_bsec2 { + +#define BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(a) (a == ALGORITHM_OUTPUT_CLASSIFICATION ? "Classification" : "Regression") +#define BME68X_BSEC2_OPERATING_AGE_LOG(o) (o == OPERATING_AGE_4D ? "4 days" : "28 days") +#define BME68X_BSEC2_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP")) +#define BME68X_BSEC2_VOLTAGE_LOG(v) (v == VOLTAGE_3_3V ? "3.3V" : "1.8V") + +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..."); + + this->bsec_status_ = bsec_init_m(&this->bsec_instance_); + if (this->bsec_status_ != BSEC_OK) { + this->mark_failed(); + ESP_LOGE(TAG, "bsec_init_m failed: status %d", this->bsec_status_); + return; + } + + bsec_get_version_m(&this->bsec_instance_, &this->version_); + + this->bme68x_status_ = bme68x_init(&this->bme68x_); + if (this->bme68x_status_ != BME68X_OK) { + this->mark_failed(); + ESP_LOGE(TAG, "bme68x_init failed: status %d", this->bme68x_status_); + return; + } + if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) { + this->set_config_(this->bsec2_configuration_, this->bsec2_configuration_length_); + if (this->bsec_status_ != BSEC_OK) { + this->mark_failed(); + ESP_LOGE(TAG, "bsec_set_configuration_m failed: status %d", this->bsec_status_); + return; + } + } + + this->update_subscription_(); + if (this->bsec_status_ != BSEC_OK) { + this->mark_failed(); + ESP_LOGE(TAG, "bsec_update_subscription_m failed: status %d", this->bsec_status_); + return; + } + + this->load_state_(); +} + +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_)); + if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) { + ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_); + } + + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication failed (BSEC2 status: %d, BME68X status: %d)", this->bsec_status_, + this->bme68x_status_); + } + + 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_); + +#ifdef USE_SENSOR + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->temperature_sample_rate_)); + LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); + ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->pressure_sample_rate_)); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); + ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->humidity_sample_rate_)); + LOG_SENSOR(" ", "Gas resistance", this->gas_resistance_sensor_); + LOG_SENSOR(" ", "CO2 equivalent", this->co2_equivalent_sensor_); + LOG_SENSOR(" ", "Breath VOC equivalent", this->breath_voc_equivalent_sensor_); + LOG_SENSOR(" ", "IAQ", this->iaq_sensor_); + LOG_SENSOR(" ", "IAQ static", this->iaq_static_sensor_); + LOG_SENSOR(" ", "Numeric IAQ accuracy", this->iaq_accuracy_sensor_); +#endif +#ifdef USE_TEXT_SENSOR + LOG_TEXT_SENSOR(" ", "IAQ accuracy", this->iaq_accuracy_text_sensor_); +#endif +} + +float BME68xBSEC2Component::get_setup_priority() const { return setup_priority::DATA; } + +void BME68xBSEC2Component::loop() { + this->run_(); + + if (this->bsec_status_ < BSEC_OK || this->bme68x_status_ < BME68X_OK) { + this->status_set_error(); + } else { + this->status_clear_error(); + } + if (this->bsec_status_ > BSEC_OK || this->bme68x_status_ > BME68X_OK) { + this->status_set_warning(); + } else { + this->status_clear_warning(); + } + // Process a single action from the queue. These are primarily sensor state publishes + // that in totality take too long to send in a single call. + if (this->queue_.size()) { + auto action = std::move(this->queue_.front()); + this->queue_.pop(); + action(); + } +} + +void BME68xBSEC2Component::set_config_(const uint8_t *config, uint32_t len) { + if (len > BSEC_MAX_PROPERTY_BLOB_SIZE) { + ESP_LOGE(TAG, "Configuration is larger than BSEC_MAX_PROPERTY_BLOB_SIZE"); + this->mark_failed(); + return; + } + uint8_t work_buffer[BSEC_MAX_PROPERTY_BLOB_SIZE]; + this->bsec_status_ = bsec_set_configuration_m(&this->bsec_instance_, config, len, work_buffer, sizeof(work_buffer)); + if (this->bsec_status_ == BSEC_OK) { + this->bsec2_blob_configured_ = true; + } +} + +float BME68xBSEC2Component::calc_sensor_sample_rate_(SampleRate sample_rate) { + if (sample_rate == SAMPLE_RATE_DEFAULT) { + sample_rate = this->sample_rate_; + } + return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP; +} + +void BME68xBSEC2Component::update_subscription_() { + bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS]; + uint8_t num_virtual_sensors = 0; +#ifdef USE_SENSOR + if (this->iaq_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_IAQ; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT); + num_virtual_sensors++; + } + + if (this->iaq_static_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_STATIC_IAQ; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT); + num_virtual_sensors++; + } + + if (this->co2_equivalent_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT); + num_virtual_sensors++; + } + + if (this->breath_voc_equivalent_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT); + num_virtual_sensors++; + } + + if (this->pressure_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_); + num_virtual_sensors++; + } + + if (this->gas_resistance_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT); + num_virtual_sensors++; + } + + if (this->temperature_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_); + num_virtual_sensors++; + } + + if (this->humidity_sensor_) { + virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY; + virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_); + num_virtual_sensors++; + } +#endif + bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR]; + uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR; + this->bsec_status_ = bsec_update_subscription_m(&this->bsec_instance_, virtual_sensors, num_virtual_sensors, + sensor_settings, &num_sensor_settings); +} + +void BME68xBSEC2Component::run_() { + int64_t curr_time_ns = this->get_time_ns_(); + if (curr_time_ns < this->next_call_ns_) { + return; + } + this->op_mode_ = this->bsec_settings_.op_mode; + uint8_t status; + + ESP_LOGV(TAG, "Performing sensor run"); + + struct bme68x_conf bme68x_conf; + this->bsec_status_ = bsec_sensor_control_m(&this->bsec_instance_, curr_time_ns, &this->bsec_settings_); + if (this->bsec_status_ < BSEC_OK) { + ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_); + return; + } + this->next_call_ns_ = this->bsec_settings_.next_call; + + if (this->bsec_settings_.trigger_measurement) { + bme68x_get_conf(&bme68x_conf, &this->bme68x_); + + bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; + bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; + bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; + bme68x_set_conf(&bme68x_conf, &this->bme68x_); + + switch (this->bsec_settings_.op_mode) { + case BME68X_FORCED_MODE: + this->bme68x_heatr_conf_.enable = BME68X_ENABLE; + this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature; + this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration; + + status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_); + status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); + status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_); + this->op_mode_ = BME68X_FORCED_MODE; + this->sleep_mode_ = false; + ESP_LOGV(TAG, "Using forced mode"); + + break; + case BME68X_PARALLEL_MODE: + if (this->op_mode_ != this->bsec_settings_.op_mode) { + this->bme68x_heatr_conf_.enable = BME68X_ENABLE; + this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile; + this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile; + this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len; + this->bme68x_heatr_conf_.shared_heatr_dur = + BSEC_TOTAL_HEAT_DUR - + (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000)); + + status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); + + status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_); + this->op_mode_ = BME68X_PARALLEL_MODE; + this->sleep_mode_ = false; + ESP_LOGV(TAG, "Using parallel mode"); + } + break; + case BME68X_SLEEP_MODE: + if (!this->sleep_mode_) { + bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_); + this->sleep_mode_ = true; + ESP_LOGV(TAG, "Using sleep mode"); + } + break; + } + + uint32_t meas_dur = 0; + meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_); + ESP_LOGV(TAG, "Queueing read in %uus", meas_dur); + this->set_timeout("read", meas_dur / 1000, [this, curr_time_ns]() { this->read_(curr_time_ns); }); + } else { + ESP_LOGV(TAG, "Measurement not required"); + this->read_(curr_time_ns); + } +} + +void BME68xBSEC2Component::read_(int64_t trigger_time_ns) { + ESP_LOGV(TAG, "Reading data"); + + if (this->bsec_settings_.trigger_measurement) { + uint8_t current_op_mode; + this->bme68x_status_ = bme68x_get_op_mode(¤t_op_mode, &this->bme68x_); + + if (current_op_mode == BME68X_SLEEP_MODE) { + ESP_LOGV(TAG, "Still in sleep mode, doing nothing"); + return; + } + } + + if (!this->bsec_settings_.process_data) { + ESP_LOGV(TAG, "Data processing not required"); + return; + } + + struct bme68x_data data[3]; + uint8_t nFields = 0; + this->bme68x_status_ = bme68x_get_data(this->op_mode_, &data[0], &nFields, &this->bme68x_); + + if (this->bme68x_status_ != BME68X_OK) { + ESP_LOGW(TAG, "Failed to get sensor data (BME68X error code %d)", this->bme68x_status_); + return; + } + if (nFields < 1) { + ESP_LOGD(TAG, "BME68X did not provide new data"); + return; + } + + for (uint8_t i = 0; i < nFields; i++) { + bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance + uint8_t num_inputs = 0; + + if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_TEMPERATURE)) { + inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE; + inputs[num_inputs].signal = data[i].temperature; + inputs[num_inputs].time_stamp = trigger_time_ns; + num_inputs++; + } + if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HEATSOURCE)) { + inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE; + inputs[num_inputs].signal = this->temperature_offset_; + inputs[num_inputs].time_stamp = trigger_time_ns; + num_inputs++; + } + if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_HUMIDITY)) { + inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY; + inputs[num_inputs].signal = data[i].humidity; + inputs[num_inputs].time_stamp = trigger_time_ns; + num_inputs++; + } + if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PRESSURE)) { + inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE; + inputs[num_inputs].signal = data[i].pressure; + inputs[num_inputs].time_stamp = trigger_time_ns; + num_inputs++; + } + if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_GASRESISTOR)) { + if (data[i].status & BME68X_GASM_VALID_MSK) { + inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR; + inputs[num_inputs].signal = data[i].gas_resistance; + inputs[num_inputs].time_stamp = trigger_time_ns; + num_inputs++; + } else { + ESP_LOGD(TAG, "BME68X did not report gas data"); + } + } + if (BSEC_CHECK_INPUT(this->bsec_settings_.process_data, BSEC_INPUT_PROFILE_PART) && + (data[i].status & BME68X_GASM_VALID_MSK)) { + inputs[num_inputs].sensor_id = BSEC_INPUT_PROFILE_PART; + inputs[num_inputs].signal = (this->op_mode_ == BME68X_FORCED_MODE) ? 0 : data[i].gas_index; + inputs[num_inputs].time_stamp = trigger_time_ns; + num_inputs++; + } + + if (num_inputs < 1) { + ESP_LOGD(TAG, "No signal inputs available for BSEC2"); + return; + } + + bsec_output_t outputs[BSEC_NUMBER_OUTPUTS]; + uint8_t num_outputs = BSEC_NUMBER_OUTPUTS; + this->bsec_status_ = bsec_do_steps_m(&this->bsec_instance_, inputs, num_inputs, outputs, &num_outputs); + if (this->bsec_status_ != BSEC_OK) { + ESP_LOGW(TAG, "BSEC2 failed to process signals (BSEC2 error code %d)", this->bsec_status_); + return; + } + if (num_outputs < 1) { + ESP_LOGD(TAG, "No signal outputs provided by BSEC2"); + return; + } + + this->publish_(outputs, num_outputs); + } +} + +void BME68xBSEC2Component::publish_(const bsec_output_t *outputs, uint8_t num_outputs) { + ESP_LOGV(TAG, "Publishing sensor states"); + bool update_accuracy = false; + uint8_t max_accuracy = 0; + for (uint8_t i = 0; i < num_outputs; i++) { + float signal = outputs[i].signal; + switch (outputs[i].sensor_id) { + case BSEC_OUTPUT_IAQ: + max_accuracy = std::max(outputs[i].accuracy, max_accuracy); + update_accuracy = true; +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_sensor_, signal); }); +#endif + break; + case BSEC_OUTPUT_STATIC_IAQ: + max_accuracy = std::max(outputs[i].accuracy, max_accuracy); + update_accuracy = true; +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->iaq_static_sensor_, signal); }); +#endif + break; + case BSEC_OUTPUT_CO2_EQUIVALENT: +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->co2_equivalent_sensor_, signal); }); +#endif + break; + case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT: +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->breath_voc_equivalent_sensor_, signal); }); +#endif + break; + case BSEC_OUTPUT_RAW_PRESSURE: +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->pressure_sensor_, signal / 100.0f); }); +#endif + break; + case BSEC_OUTPUT_RAW_GAS: +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->gas_resistance_sensor_, signal); }); +#endif + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE: +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->temperature_sensor_, signal); }); +#endif + break; + case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY: +#ifdef USE_SENSOR + this->queue_push_([this, signal]() { this->publish_sensor_(this->humidity_sensor_, signal); }); +#endif + break; + } + } + if (update_accuracy) { +#ifdef USE_SENSOR + this->queue_push_( + [this, max_accuracy]() { this->publish_sensor_(this->iaq_accuracy_sensor_, max_accuracy, true); }); +#endif +#ifdef USE_TEXT_SENSOR + this->queue_push_([this, max_accuracy]() { + this->publish_sensor_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[max_accuracy]); + }); +#endif + // Queue up an opportunity to save state + this->queue_push_([this, max_accuracy]() { this->save_state_(max_accuracy); }); + } +} + +int64_t BME68xBSEC2Component::get_time_ns_() { + int64_t time_ms = millis(); + if (this->last_time_ms_ > time_ms) { + this->millis_overflow_counter_++; + } + this->last_time_ms_ = time_ms; + + return (time_ms + ((int64_t) this->millis_overflow_counter_ << 32)) * INT64_C(1000000); +} + +#ifdef USE_SENSOR +void BME68xBSEC2Component::publish_sensor_(sensor::Sensor *sensor, float value, bool change_only) { + if (!sensor || (change_only && sensor->has_state() && sensor->state == value)) { + return; + } + sensor->publish_state(value); +} +#endif + +#ifdef USE_TEXT_SENSOR +void BME68xBSEC2Component::publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value) { + if (!sensor || (sensor->has_state() && sensor->state == value)) { + return; + } + sensor->publish_state(value); +} +#endif + +void BME68xBSEC2Component::load_state_() { + uint32_t hash = this->get_hash(); + this->bsec_state_ = global_preferences->make_preference(hash, true); + + uint8_t state[BSEC_MAX_STATE_BLOB_SIZE]; + if (this->bsec_state_.load(&state)) { + ESP_LOGV(TAG, "Loading state"); + uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE]; + this->bsec_status_ = + bsec_set_state_m(&this->bsec_instance_, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, sizeof(work_buffer)); + if (this->bsec_status_ != BSEC_OK) { + ESP_LOGW(TAG, "Failed to load state (BSEC2 error code %d)", this->bsec_status_); + } + ESP_LOGI(TAG, "Loaded state"); + } +} + +void BME68xBSEC2Component::save_state_(uint8_t accuracy) { + if (accuracy < 3 || (millis() - this->last_state_save_ms_ < this->state_save_interval_ms_)) { + return; + } + + ESP_LOGV(TAG, "Saving state"); + + uint8_t state[BSEC_MAX_STATE_BLOB_SIZE]; + uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE]; + uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE; + + this->bsec_status_ = bsec_get_state_m(&this->bsec_instance_, 0, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, + BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state); + if (this->bsec_status_ != BSEC_OK) { + ESP_LOGW(TAG, "Failed fetch state for save (BSEC2 error code %d)", this->bsec_status_); + return; + } + + if (!this->bsec_state_.save(&state)) { + ESP_LOGW(TAG, "Failed to save state"); + return; + } + this->last_state_save_ms_ = millis(); + + ESP_LOGI(TAG, "Saved state"); +} + +} // namespace bme68x_bsec2 +} // namespace esphome +#endif diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.h b/esphome/components/bme68x_bsec2/bme68x_bsec2.h new file mode 100644 index 0000000000..7b9db2b7bf --- /dev/null +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.h @@ -0,0 +1,163 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/preferences.h" + +#ifdef USE_BSEC2 + +#ifdef USE_SENSOR +#include "esphome/components/sensor/sensor.h" +#endif + +#ifdef USE_TEXT_SENSOR +#include "esphome/components/text_sensor/text_sensor.h" +#endif + +#include +#include + +#include + +namespace esphome { +namespace bme68x_bsec2 { + +enum AlgorithmOutput { + ALGORITHM_OUTPUT_IAQ, + ALGORITHM_OUTPUT_CLASSIFICATION, + ALGORITHM_OUTPUT_REGRESSION, +}; + +enum OperatingAge { + OPERATING_AGE_4D, + OPERATING_AGE_28D, +}; + +enum SampleRate { + SAMPLE_RATE_LP = 0, + SAMPLE_RATE_ULP = 1, + SAMPLE_RATE_DEFAULT = 2, +}; + +enum Voltage { + VOLTAGE_1_8V, + VOLTAGE_3_3V, +}; + +class BME68xBSEC2Component : public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void loop() override; + + void set_algorithm_output(AlgorithmOutput algorithm_output) { this->algorithm_output_ = algorithm_output; } + void set_operating_age(OperatingAge operating_age) { this->operating_age_ = operating_age; } + void set_temperature_offset(float offset) { this->temperature_offset_ = offset; } + void set_voltage(Voltage voltage) { this->voltage_ = voltage; } + + void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; } + void set_temperature_sample_rate(SampleRate sample_rate) { this->temperature_sample_rate_ = sample_rate; } + void set_pressure_sample_rate(SampleRate sample_rate) { this->pressure_sample_rate_ = sample_rate; } + void set_humidity_sample_rate(SampleRate sample_rate) { this->humidity_sample_rate_ = sample_rate; } + + void set_bsec2_configuration(const uint8_t *data, const uint32_t len) { + this->bsec2_configuration_ = data; + this->bsec2_configuration_length_ = len; + } + + void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; } + +#ifdef USE_SENSOR + void set_temperature_sensor(sensor::Sensor *sensor) { this->temperature_sensor_ = sensor; } + void set_pressure_sensor(sensor::Sensor *sensor) { this->pressure_sensor_ = sensor; } + void set_humidity_sensor(sensor::Sensor *sensor) { this->humidity_sensor_ = sensor; } + void set_gas_resistance_sensor(sensor::Sensor *sensor) { this->gas_resistance_sensor_ = sensor; } + void set_iaq_sensor(sensor::Sensor *sensor) { this->iaq_sensor_ = sensor; } + void set_iaq_static_sensor(sensor::Sensor *sensor) { this->iaq_static_sensor_ = sensor; } + void set_iaq_accuracy_sensor(sensor::Sensor *sensor) { this->iaq_accuracy_sensor_ = sensor; } + void set_co2_equivalent_sensor(sensor::Sensor *sensor) { this->co2_equivalent_sensor_ = sensor; } + void set_breath_voc_equivalent_sensor(sensor::Sensor *sensor) { this->breath_voc_equivalent_sensor_ = sensor; } +#endif +#ifdef USE_TEXT_SENSOR + void set_iaq_accuracy_text_sensor(text_sensor::TextSensor *sensor) { this->iaq_accuracy_text_sensor_ = sensor; } +#endif + virtual uint32_t get_hash() = 0; + + protected: + void set_config_(const uint8_t *config, u_int32_t len); + float calc_sensor_sample_rate_(SampleRate sample_rate); + void update_subscription_(); + + void run_(); + void read_(int64_t trigger_time_ns); + void publish_(const bsec_output_t *outputs, uint8_t num_outputs); + int64_t get_time_ns_(); + +#ifdef USE_SENSOR + void publish_sensor_(sensor::Sensor *sensor, float value, bool change_only = false); +#endif +#ifdef USE_TEXT_SENSOR + void publish_sensor_(text_sensor::TextSensor *sensor, const std::string &value); +#endif + + void load_state_(); + void save_state_(uint8_t accuracy); + + void queue_push_(std::function &&f) { this->queue_.push(std::move(f)); } + + struct bme68x_dev bme68x_; + bsec_bme_settings_t bsec_settings_; + bsec_version_t version_; + uint8_t bsec_instance_[BSEC_INSTANCE_SIZE]; + + struct bme68x_heatr_conf bme68x_heatr_conf_; + uint8_t op_mode_; // operating mode of sensor + bool sleep_mode_; + bsec_library_return_t bsec_status_{BSEC_OK}; + int8_t bme68x_status_{BME68X_OK}; + + int64_t last_time_ms_{0}; + uint32_t millis_overflow_counter_{0}; + int64_t next_call_ns_{0}; + + std::queue> queue_; + + uint8_t const *bsec2_configuration_{nullptr}; + uint32_t bsec2_configuration_length_{0}; + bool bsec2_blob_configured_{false}; + + ESPPreferenceObject bsec_state_; + uint32_t state_save_interval_ms_{21600000}; // 6 hours - 4 times a day + uint32_t last_state_save_ms_ = 0; + + float temperature_offset_{0}; + + AlgorithmOutput algorithm_output_{ALGORITHM_OUTPUT_IAQ}; + OperatingAge operating_age_{OPERATING_AGE_28D}; + Voltage voltage_{VOLTAGE_3_3V}; + + SampleRate sample_rate_{SAMPLE_RATE_LP}; // Core/gas sample rate + SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT}; + SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT}; + SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT}; + +#ifdef USE_SENSOR + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *pressure_sensor_{nullptr}; + sensor::Sensor *humidity_sensor_{nullptr}; + sensor::Sensor *gas_resistance_sensor_{nullptr}; + sensor::Sensor *iaq_sensor_{nullptr}; + sensor::Sensor *iaq_static_sensor_{nullptr}; + sensor::Sensor *iaq_accuracy_sensor_{nullptr}; + sensor::Sensor *co2_equivalent_sensor_{nullptr}; + sensor::Sensor *breath_voc_equivalent_sensor_{nullptr}; +#endif +#ifdef USE_TEXT_SENSOR + text_sensor::TextSensor *iaq_accuracy_text_sensor_{nullptr}; +#endif +}; + +} // namespace bme68x_bsec2 +} // namespace esphome +#endif diff --git a/esphome/components/bme68x_bsec2/sensor.py b/esphome/components/bme68x_bsec2/sensor.py new file mode 100644 index 0000000000..419f47b248 --- /dev/null +++ b/esphome/components/bme68x_bsec2/sensor.py @@ -0,0 +1,130 @@ +import esphome.codegen as cg +from esphome.components import sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_GAS_RESISTANCE, + CONF_HUMIDITY, + CONF_IAQ_ACCURACY, + CONF_PRESSURE, + CONF_SAMPLE_RATE, + CONF_TEMPERATURE, + DEVICE_CLASS_ATMOSPHERIC_PRESSURE, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_TEMPERATURE, + ICON_GAS_CYLINDER, + ICON_GAUGE, + ICON_THERMOMETER, + ICON_WATER_PERCENT, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_HECTOPASCAL, + UNIT_OHM, + UNIT_PARTS_PER_MILLION, + UNIT_PERCENT, +) + +from . import CONF_BME68X_BSEC2_ID, SAMPLE_RATE_OPTIONS, BME68xBSEC2Component + +DEPENDENCIES = ["bme68x_bsec2"] + +CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent" +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 = [ + CONF_TEMPERATURE, + CONF_PRESSURE, + CONF_HUMIDITY, + CONF_GAS_RESISTANCE, + CONF_IAQ, + CONF_IAQ_STATIC, + CONF_IAQ_ACCURACY, + CONF_CO2_EQUIVALENT, + CONF_BREATH_VOC_EQUIVALENT, +] + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_BME68X_BSEC2_ID): cv.use_id(BME68xBSEC2Component), + 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, + ).extend( + {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)} + ), + 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, + ).extend( + {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)} + ), + 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, + ).extend( + {cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)} + ), + cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema( + unit_of_measurement=UNIT_OHM, + icon=ICON_GAS_CYLINDER, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_IAQ): sensor.sensor_schema( + unit_of_measurement=UNIT_IAQ, + icon=ICON_GAUGE, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_IAQ_STATIC): sensor.sensor_schema( + unit_of_measurement=UNIT_IAQ, + icon=ICON_GAUGE, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_IAQ_ACCURACY): sensor.sensor_schema( + icon=ICON_ACCURACY, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + icon=ICON_TEST_TUBE, + accuracy_decimals=1, + 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, + state_class=STATE_CLASS_MEASUREMENT, + ), + } +) + + +async def setup_conf(config, key, hub): + if conf := config.get(key): + sens = await sensor.new_sensor(conf) + cg.add(getattr(hub, f"set_{key}_sensor")(sens)) + if sample_rate := conf.get(CONF_SAMPLE_RATE): + cg.add(getattr(hub, f"set_{key}_sample_rate")(sample_rate)) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_BME68X_BSEC2_ID]) + for key in TYPES: + await setup_conf(config, key, hub) diff --git a/esphome/components/bme68x_bsec2/text_sensor.py b/esphome/components/bme68x_bsec2/text_sensor.py new file mode 100644 index 0000000000..fce00afe34 --- /dev/null +++ b/esphome/components/bme68x_bsec2/text_sensor.py @@ -0,0 +1,33 @@ +import esphome.codegen as cg +from esphome.components import text_sensor +import esphome.config_validation as cv +from esphome.const import CONF_IAQ_ACCURACY + +from . import CONF_BME68X_BSEC2_ID, BME68xBSEC2Component + +DEPENDENCIES = ["bme68x_bsec2"] + +ICON_ACCURACY = "mdi:checkbox-marked-circle-outline" + +TYPES = [CONF_IAQ_ACCURACY] + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_BME68X_BSEC2_ID): cv.use_id(BME68xBSEC2Component), + cv.Optional(CONF_IAQ_ACCURACY): text_sensor.text_sensor_schema( + icon=ICON_ACCURACY + ), + } +) + + +async def setup_conf(config, key, hub): + if conf := config.get(key): + sens = await text_sensor.new_text_sensor(conf) + cg.add(getattr(hub, f"set_{key}_text_sensor")(sens)) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_BME68X_BSEC2_ID]) + for key in TYPES: + await setup_conf(config, key, hub) diff --git a/esphome/components/bme68x_bsec2_i2c/__init__.py b/esphome/components/bme68x_bsec2_i2c/__init__.py new file mode 100644 index 0000000000..d6fb7fa9be --- /dev/null +++ b/esphome/components/bme68x_bsec2_i2c/__init__.py @@ -0,0 +1,28 @@ +import esphome.codegen as cg +from esphome.components import i2c +from esphome.components.bme68x_bsec2 import ( + CONFIG_SCHEMA_BASE, + BME68xBSEC2Component, + to_code_base, +) +import esphome.config_validation as cv + +CODEOWNERS = ["@neffs", "@kbx81"] + +AUTO_LOAD = ["bme68x_bsec2"] +DEPENDENCIES = ["i2c"] + +bme68x_bsec2_i2c_ns = cg.esphome_ns.namespace("bme68x_bsec2_i2c") +BME68xBSEC2I2CComponent = bme68x_bsec2_i2c_ns.class_( + "BME68xBSEC2I2CComponent", BME68xBSEC2Component, i2c.I2CDevice +) + + +CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend( + cv.Schema({cv.GenerateID(): cv.declare_id(BME68xBSEC2I2CComponent)}) +).extend(i2c.i2c_device_schema(0x76)) + + +async def to_code(config): + var = await to_code_base(config) + await i2c.register_i2c_device(var, config) diff --git a/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp b/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp new file mode 100644 index 0000000000..874c8bf388 --- /dev/null +++ b/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.cpp @@ -0,0 +1,53 @@ +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_BSEC2 +#include "bme68x_bsec2_i2c.h" +#include "esphome/components/i2c/i2c.h" + +#include + +namespace esphome { +namespace bme68x_bsec2_i2c { + +static const char *const TAG = "bme68x_bsec2_i2c.sensor"; + +void BME68xBSEC2I2CComponent::setup() { + // must set up our bme68x_dev instance before calling setup() + this->bme68x_.intf_ptr = (void *) this; + this->bme68x_.intf = BME68X_I2C_INTF; + this->bme68x_.read = BME68xBSEC2I2CComponent::read_bytes_wrapper; + this->bme68x_.write = BME68xBSEC2I2CComponent::write_bytes_wrapper; + this->bme68x_.delay_us = BME68xBSEC2I2CComponent::delay_us; + this->bme68x_.amb_temp = 25; + + BME68xBSEC2Component::setup(); +} + +void BME68xBSEC2I2CComponent::dump_config() { + LOG_I2C_DEVICE(this); + BME68xBSEC2Component::dump_config(); +} + +uint32_t BME68xBSEC2I2CComponent::get_hash() { return fnv1_hash("bme68x_bsec_state_" + to_string(this->address_)); } + +int8_t BME68xBSEC2I2CComponent::read_bytes_wrapper(uint8_t a_register, uint8_t *data, uint32_t len, void *intfPtr) { + ESP_LOGVV(TAG, "read_bytes_wrapper: reg = %u", a_register); + return static_cast(intfPtr)->read_bytes(a_register, data, len) ? 0 : -1; +} + +int8_t BME68xBSEC2I2CComponent::write_bytes_wrapper(uint8_t a_register, const uint8_t *data, uint32_t len, + void *intfPtr) { + ESP_LOGVV(TAG, "write_bytes_wrapper: reg = %u", a_register); + return static_cast(intfPtr)->write_bytes(a_register, data, len) ? 0 : -1; +} + +void BME68xBSEC2I2CComponent::delay_us(uint32_t period, void *intfPtr) { + ESP_LOGVV(TAG, "Delaying for %" PRIu32 "us", period); + delayMicroseconds(period); +} + +} // namespace bme68x_bsec2_i2c +} // namespace esphome +#endif diff --git a/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.h b/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.h new file mode 100644 index 0000000000..a21a123f7b --- /dev/null +++ b/esphome/components/bme68x_bsec2_i2c/bme68x_bsec2_i2c.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/preferences.h" + +#ifdef USE_BSEC2 + +#include "esphome/components/bme68x_bsec2/bme68x_bsec2.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace bme68x_bsec2_i2c { + +class BME68xBSEC2I2CComponent : public bme68x_bsec2::BME68xBSEC2Component, public i2c::I2CDevice { + void setup() override; + void dump_config() override; + + uint32_t get_hash() override; + + static int8_t read_bytes_wrapper(uint8_t a_register, uint8_t *data, uint32_t len, void *intfPtr); + static int8_t write_bytes_wrapper(uint8_t a_register, const uint8_t *data, uint32_t len, void *intfPtr); + static void delay_us(uint32_t period, void *intfPtr); +}; + +} // namespace bme68x_bsec2_i2c +} // namespace esphome +#endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 61a4940d01..a711148ec8 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -158,6 +158,7 @@ #endif // Disabled feature flags -// #define USE_BSEC // Requires a library with proprietary license. +// #define USE_BSEC // Requires a library with proprietary license +// #define USE_BSEC2 // Requires a library with proprietary license #define USE_DASHBOARD_IMPORT diff --git a/tests/components/bme68x_bsec2_i2c/common.yaml b/tests/components/bme68x_bsec2_i2c/common.yaml new file mode 100644 index 0000000000..b8a16ee7bb --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/common.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_bme68x + scl: ${scl_pin} + sda: ${sda_pin} + +bme68x_bsec2_i2c: + address: 0x76 + model: bme688 + algorithm_output: classification + operating_age: 28d + sample_rate: LP + supply_voltage: 3.3V + +sensor: + - platform: bme68x_bsec2 + temperature: + name: BME68X Temperature + pressure: + name: BME68X Pressure + humidity: + name: BME68X Humidity + gas_resistance: + name: BME68X Gas Sensor + iaq: + name: BME68X IAQ + co2_equivalent: + name: BME68X eCO2 + breath_voc_equivalent: + name: BME68X Breath eVOC + +text_sensor: + - platform: bme68x_bsec2 + iaq_accuracy: + name: BME68X Accuracy diff --git a/tests/components/bme68x_bsec2_i2c/test.esp32-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-ard.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-c3-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..84a9dd4bb4 --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-c3-ard.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-s2-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s2-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-s2-ard.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-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp32-s3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml b/tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/bme68x_bsec2_i2c/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml From e769804fe6f2502e98ddd8ced9db78898ac8ff1e Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 12 Aug 2024 06:27:22 +0200 Subject: [PATCH 0167/1052] [code-quality] clang-tidy media_player (#7238) --- esphome/components/media_player/automation.h | 58 +++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/esphome/components/media_player/automation.h b/esphome/components/media_player/automation.h index fc3ce7a764..f0e0a5dd31 100644 --- a/esphome/components/media_player/automation.h +++ b/esphome/components/media_player/automation.h @@ -7,30 +7,24 @@ namespace esphome { namespace media_player { -#define MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(ACTION_CLASS, ACTION_COMMAND) \ - template class ACTION_CLASS : public Action, public Parented { \ - void play(Ts... x) override { \ - this->parent_->make_call().set_command(MediaPlayerCommand::MEDIA_PLAYER_COMMAND_##ACTION_COMMAND).perform(); \ - } \ - }; +template +class MediaPlayerCommandAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->make_call().set_command(Command).perform(); } +}; -#define MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(TRIGGER_CLASS, TRIGGER_STATE) \ - class TRIGGER_CLASS : public Trigger<> { \ - public: \ - explicit TRIGGER_CLASS(MediaPlayer *player) { \ - player->add_on_state_callback([this, player]() { \ - if (player->state == MediaPlayerState::MEDIA_PLAYER_STATE_##TRIGGER_STATE) \ - this->trigger(); \ - }); \ - } \ - }; - -MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(PlayAction, PLAY) -MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(PauseAction, PAUSE) -MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(StopAction, STOP) -MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(ToggleAction, TOGGLE) -MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeUpAction, VOLUME_UP) -MEDIA_PLAYER_SIMPLE_COMMAND_ACTION(VolumeDownAction, VOLUME_DOWN) +template +using PlayAction = MediaPlayerCommandAction; +template +using PauseAction = MediaPlayerCommandAction; +template +using StopAction = MediaPlayerCommandAction; +template +using ToggleAction = MediaPlayerCommandAction; +template +using VolumeUpAction = MediaPlayerCommandAction; +template +using VolumeDownAction = MediaPlayerCommandAction; template class PlayMediaAction : public Action, public Parented { TEMPLATABLE_VALUE(std::string, media_url) @@ -49,10 +43,20 @@ class StateTrigger : public Trigger<> { } }; -MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(IdleTrigger, IDLE) -MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PlayTrigger, PLAYING) -MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(PauseTrigger, PAUSED) -MEDIA_PLAYER_SIMPLE_STATE_TRIGGER(AnnouncementTrigger, ANNOUNCING) +template class MediaPlayerStateTrigger : public Trigger<> { + public: + explicit MediaPlayerStateTrigger(MediaPlayer *player) { + player->add_on_state_callback([this, player]() { + if (player->state == State) + this->trigger(); + }); + } +}; + +using IdleTrigger = MediaPlayerStateTrigger; +using PlayTrigger = MediaPlayerStateTrigger; +using PauseTrigger = MediaPlayerStateTrigger; +using AnnouncementTrigger = MediaPlayerStateTrigger; template class IsIdleCondition : public Condition, public Parented { public: From 82c5cd18de86307cd6a047abc0f5489caf1af137 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:30:27 +1200 Subject: [PATCH 0168/1052] Bump docker/build-push-action from 6.5.0 to 6.6.1 in /.github/actions/build-image (#7232) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index bd9ceb8072..f9c44cfb63 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: context: . file: ./docker/Dockerfile @@ -69,7 +69,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.5.0 + uses: docker/build-push-action@v6.6.1 with: context: . file: ./docker/Dockerfile From 8a076cc9064612f51e2a356259544890c66d82a5 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 12 Aug 2024 06:49:35 +0200 Subject: [PATCH 0169/1052] fix build error (#7229) --- esphome/components/api/api_connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 81fa4cb339..8e4c6faaee 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1336,7 +1336,7 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) { update->check(); break; default: - ESP_LOGW(TAG, "Unknown update command: %d", msg.command); + ESP_LOGW(TAG, "Unknown update command: %" PRIu32, msg.command); break; } } From f13cf1f7a02c6fbe3d867151aac276b306e0820a Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 12 Aug 2024 06:52:09 +0200 Subject: [PATCH 0170/1052] adjust to new python pre-commit hooks (#7178) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- script/clang-tidy | 35 +++++++++++++++++++++++++---------- script/helpers.py | 9 ++++----- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/script/clang-tidy b/script/clang-tidy index ea522157c5..5bb93846b2 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -115,9 +115,10 @@ def clang_options(idedata): pids = set() -def run_tidy(executable, args, options, tmpdir, queue, lock, failed_files): + +def run_tidy(executable, args, options, tmpdir, path_queue, lock, failed_files): while True: - path = queue.get() + path = path_queue.get() invocation = [executable] if tmpdir is not None: @@ -139,17 +140,20 @@ def run_tidy(executable, args, options, tmpdir, queue, lock, failed_files): invocation.append("--") invocation.extend(options) - proc = subprocess.run(invocation, capture_output=True, encoding="utf-8") + proc = subprocess.run( + invocation, capture_output=True, encoding="utf-8", check=False + ) if proc.returncode != 0: with lock: print_error_for_file(path, proc.stdout) failed_files.append(path) - queue.task_done() + path_queue.task_done() def progress_bar_show(value): if value is None: return "" + return None def split_list(a, n): @@ -237,7 +241,15 @@ def main(): for _ in range(args.jobs): t = threading.Thread( target=run_tidy, - args=(executable, args, options, tmpdir, task_queue, lock, failed_files), + args=( + executable, + args, + options, + tmpdir, + task_queue, + lock, + failed_files, + ), ) t.daemon = True t.start() @@ -245,14 +257,14 @@ def main(): # Fill the queue with files. with click.progressbar( files, width=30, file=sys.stderr, item_show_func=progress_bar_show - ) as bar: - for name in bar: + ) as progress_bar: + for name in progress_bar: task_queue.put(name) # Wait for all threads to be done. task_queue.join() - except FileNotFoundError as ex: + except FileNotFoundError: return 1 except KeyboardInterrupt: print() @@ -262,7 +274,7 @@ def main(): # Kill subprocesses (and ourselves!) # No simple, clean alternative appears to be available. os.kill(0, 9) - return 2 # Will not execute. + return 2 # Will not execute. if args.fix and failed_files: print("Applying fixes ...") @@ -272,7 +284,10 @@ def main(): except FileNotFoundError: subprocess.call(["clang-apply-replacements", tmpdir]) except FileNotFoundError: - print("Error please install clang-apply-replacements-14 or clang-apply-replacements.\n", file=sys.stderr) + print( + "Error please install clang-apply-replacements-14 or clang-apply-replacements.\n", + file=sys.stderr, + ) except: print("Error applying fixes.\n", file=sys.stderr) raise diff --git a/script/helpers.py b/script/helpers.py index 56349b6052..6f36faaeb1 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -159,20 +159,19 @@ def get_binary(name: str, version: str) -> str: binary_file = f"{name}-{version}" try: result = subprocess.check_output([binary_file, "-version"]) - if result.returncode == 0: - return binary_file - except Exception: + return binary_file + except FileNotFoundError: pass binary_file = name try: result = subprocess.run( - [binary_file, "-version"], text=True, capture_output=True + [binary_file, "-version"], text=True, capture_output=True, check=False ) if result.returncode == 0 and (f"version {version}") in result.stdout: return binary_file raise FileNotFoundError(f"{name} not found") - except FileNotFoundError as ex: + except FileNotFoundError: print( f""" Oops. It looks like {name} is not installed. It should be available under venv/bin From 8148eae1340c5de43c1333761b27d01f8e9d9ba9 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Tue, 13 Aug 2024 01:16:42 +0200 Subject: [PATCH 0171/1052] add windows script/setup.bat (#7140) Co-authored-by: Keith Burzinski --- script/setup.bat | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 script/setup.bat diff --git a/script/setup.bat b/script/setup.bat new file mode 100644 index 0000000000..0b49768139 --- /dev/null +++ b/script/setup.bat @@ -0,0 +1,28 @@ +@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 +call venv/Scripts/activate +echo Running the Virtual Environment + +:install + +echo Installing required packages... + +python.exe -m pip install --upgrade pip + +pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt -r requirements_dev.txt +pip3 install setuptools wheel +pip3 install -e ".[dev,test,displays]" --config-settings editable_mode=compat + +pre-commit install + +python script/platformio_install_deps.py platformio.ini --libraries --tools --platforms + +echo . +echo . +echo Virtual environment created. Run 'venv/Scripts/activate' to use it. From 5f3f10628318d01d4a01e8fc23161f2ce075052b Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 01:29:09 +0200 Subject: [PATCH 0172/1052] [code-quality] add NOLINT haier_base (#7236) --- esphome/components/haier/haier_base.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/haier/haier_base.h b/esphome/components/haier/haier_base.h index c0bf878519..7d92a6611c 100644 --- a/esphome/components/haier/haier_base.h +++ b/esphome/components/haier/haier_base.h @@ -80,8 +80,8 @@ class HaierClimateBase : public esphome::Component, const char *phase_to_string_(ProtocolPhases phase); virtual void set_handlers() = 0; virtual void process_phase(std::chrono::steady_clock::time_point now) = 0; - virtual haier_protocol::HaierMessage get_control_message() = 0; - virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; + virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming) + virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; // NOLINT(readability-identifier-naming) virtual void initialization(){}; virtual bool prepare_pending_action(); virtual void process_protocol_reset(); From 64ee40d3704a4e40ffea6f962ada64fc803d4f62 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 01:33:51 +0200 Subject: [PATCH 0173/1052] [code-quality] clang-tidy bedjet (#7251) --- esphome/components/bedjet/bedjet_codec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/bedjet/bedjet_codec.h b/esphome/components/bedjet/bedjet_codec.h index 527e757d7f..07aee32d54 100644 --- a/esphome/components/bedjet/bedjet_codec.h +++ b/esphome/components/bedjet/bedjet_codec.h @@ -90,7 +90,7 @@ struct BedjetStatusPacket { int unused_6 : 1; // 0x4 bool is_dual_zone : 1; /// Is part of a Dual Zone configuration int unused_7 : 1; // 0x1 - } dual_zone_flags; + } dual_zone_flags; // NOLINT(clang-diagnostic-unaligned-access) uint8_t unused_4 : 8; // Unknown 23-24 = 0x1310 uint8_t unused_5 : 8; // Unknown 23-24 = 0x1310 From f24fd34d860ab39fa66d100aff96dc0f0ec43ee2 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 01:38:13 +0200 Subject: [PATCH 0174/1052] fix name conflict with zephyr macro (#7252) --- esphome/components/fingerprint_grow/fingerprint_grow.cpp | 2 +- esphome/components/fingerprint_grow/fingerprint_grow.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index c2cab368c9..0dfea49b8b 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -307,7 +307,7 @@ void FingerprintGrowComponent::delete_fingerprint(uint16_t finger_id) { void FingerprintGrowComponent::delete_all_fingerprints() { ESP_LOGI(TAG, "Deleting all stored fingerprints"); - this->data_ = {EMPTY}; + this->data_ = {DELETE_ALL}; switch (this->send_command_()) { case OK: ESP_LOGI(TAG, "Deleted all fingerprints"); diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.h b/esphome/components/fingerprint_grow/fingerprint_grow.h index 20ff60997b..1c3098ef14 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.h +++ b/esphome/components/fingerprint_grow/fingerprint_grow.h @@ -36,7 +36,7 @@ enum GrowCommand { LOAD = 0x07, UPLOAD = 0x08, DELETE = 0x0C, - EMPTY = 0x0D, + DELETE_ALL = 0x0D, // aka EMPTY READ_SYS_PARAM = 0x0F, SET_PASSWORD = 0x12, VERIFY_PASSWORD = 0x13, From 8d5be27746ed6a510b87f53cff6811a277a2589d Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 02:47:18 +0200 Subject: [PATCH 0175/1052] [code-quality] Apply ruff linting suggestions (#7239) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/network/__init__.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 9ef75e0fb9..f5ddbc0da7 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -1,8 +1,6 @@ -from esphome.core import CORE import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components.esp32 import add_idf_sdkconfig_option - +import esphome.config_validation as cv from esphome.const import ( CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT, @@ -10,6 +8,7 @@ from esphome.const import ( PLATFORM_ESP8266, PLATFORM_RP2040, ) +from esphome.core import CORE CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["mdns"] @@ -42,11 +41,10 @@ async def to_code(config): if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_LWIP_IPV6", enable_ipv6) add_idf_sdkconfig_option("CONFIG_LWIP_IPV6_AUTOCONFIG", enable_ipv6) - else: - if enable_ipv6: - cg.add_build_flag("-DCONFIG_LWIP_IPV6") - cg.add_build_flag("-DCONFIG_LWIP_IPV6_AUTOCONFIG") - if CORE.is_rp2040: - cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV6") - if CORE.is_esp8266: - cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_LOW_MEMORY") + elif enable_ipv6: + cg.add_build_flag("-DCONFIG_LWIP_IPV6") + cg.add_build_flag("-DCONFIG_LWIP_IPV6_AUTOCONFIG") + if CORE.is_rp2040: + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV6") + if CORE.is_esp8266: + cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_LOW_MEMORY") From fc146dabed431ff1c7f314d329cad62ea9d06731 Mon Sep 17 00:00:00 2001 From: juanluss31 <40864809+juanluss31@users.noreply.github.com> Date: Tue, 13 Aug 2024 03:12:48 +0200 Subject: [PATCH 0176/1052] Add support for LYWSD02MMC Xiaomi device (#7080) --- CODEOWNERS | 1 + esphome/components/xiaomi_ble/xiaomi_ble.cpp | 23 +++++- esphome/components/xiaomi_ble/xiaomi_ble.h | 1 + .../components/xiaomi_lywsd02mmc/__init__.py | 0 .../components/xiaomi_lywsd02mmc/sensor.py | 77 +++++++++++++++++++ .../xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp | 73 ++++++++++++++++++ .../xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h | 37 +++++++++ .../components/xiaomi_lywsd02mmc/common.yaml | 12 +++ .../xiaomi_lywsd02mmc/test.esp32-ard.yaml | 1 + .../xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml | 1 + .../xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml | 1 + .../xiaomi_lywsd02mmc/test.esp32-idf.yaml | 1 + 12 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 esphome/components/xiaomi_lywsd02mmc/__init__.py create mode 100644 esphome/components/xiaomi_lywsd02mmc/sensor.py create mode 100644 esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp create mode 100644 esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h create mode 100644 tests/components/xiaomi_lywsd02mmc/common.yaml create mode 100644 tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml create mode 100644 tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml create mode 100644 tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml create mode 100644 tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 999449a3df..9865e51f11 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -453,6 +453,7 @@ esphome/components/wl_134/* @hobbypunk90 esphome/components/x9c/* @EtienneMD esphome/components/xgzp68xx/* @gcormier esphome/components/xiaomi_hhccjcy10/* @fariouche +esphome/components/xiaomi_lywsd02mmc/* @juanluss31 esphome/components/xiaomi_lywsd03mmc/* @ahpohl esphome/components/xiaomi_mhoc303/* @drug123 esphome/components/xiaomi_mhoc401/* @vevsvevs diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 95faea0446..85434341cc 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -49,8 +49,8 @@ bool parse_xiaomi_value(uint16_t value_type, const uint8_t *data, uint8_t value_ const uint16_t conductivity = encode_uint16(data[1], data[0]); result.conductivity = conductivity; } - // battery, 1 byte, 8-bit unsigned integer, 1 % - else if ((value_type == 0x100A) && (value_length == 1)) { + // battery / MiaoMiaoce battery, 1 byte, 8-bit unsigned integer, 1 % + else if ((value_type == 0x100A || value_type == 0x4803) && (value_length == 1)) { result.battery_level = data[0]; } // temperature + humidity, 4 bytes, 16-bit signed integer (LE) each, 0.1 °C, 0.1 % @@ -80,6 +80,17 @@ bool parse_xiaomi_value(uint16_t value_type, const uint8_t *data, uint8_t value_ result.has_motion = !idle_time; } else if ((value_type == 0x1018) && (value_length == 1)) { result.is_light = data[0]; + } + // MiaoMiaoce temperature, 4 bytes, float, 0.1 °C + else if ((value_type == 0x4C01) && (value_length == 4)) { + const uint32_t int_number = encode_uint32(data[3], data[2], data[1], data[0]); + float temperature; + std::memcpy(&temperature, &int_number, sizeof(temperature)); + result.temperature = temperature; + } + // MiaoMiaoce humidity, 1 byte, 8-bit unsigned integer, 1 % + else if ((value_type == 0x4C02) && (value_length == 1)) { + result.humidity = data[0]; } else { return false; } @@ -111,7 +122,8 @@ bool parse_xiaomi_message(const std::vector &message, XiaomiParseResult } while (payload_length > 3) { - if (payload[payload_offset + 1] != 0x10 && payload[payload_offset + 1] != 0x00) { + if (payload[payload_offset + 1] != 0x10 && payload[payload_offset + 1] != 0x00 && + payload[payload_offset + 1] != 0x4C && payload[payload_offset + 1] != 0x48) { ESP_LOGVV(TAG, "parse_xiaomi_message(): fixed byte not found, stop parsing residual data."); break; } @@ -190,6 +202,11 @@ optional parse_xiaomi_header(const esp32_ble_tracker::Service } else if (device_uuid == 0x045b) { // rectangular body, e-ink display result.type = XiaomiParseResult::TYPE_LYWSD02; result.name = "LYWSD02"; + } else if (device_uuid == 0x2542) { // rectangular body, e-ink display — with bindkeys + result.type = XiaomiParseResult::TYPE_LYWSD02MMC; + result.name = "LYWSD02MMC"; + if (raw.size() == 19) + result.raw_offset -= 6; } else if (device_uuid == 0x040a) { // Mosquito Repellent Smart Version result.type = XiaomiParseResult::TYPE_WX08ZM; result.name = "WX08ZM"; diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.h b/esphome/components/xiaomi_ble/xiaomi_ble.h index c1086605d1..6978be97f4 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.h +++ b/esphome/components/xiaomi_ble/xiaomi_ble.h @@ -17,6 +17,7 @@ struct XiaomiParseResult { TYPE_HHCCPOT002, TYPE_LYWSDCGQ, TYPE_LYWSD02, + TYPE_LYWSD02MMC, TYPE_CGG1, TYPE_LYWSD03MMC, TYPE_CGD1, diff --git a/esphome/components/xiaomi_lywsd02mmc/__init__.py b/esphome/components/xiaomi_lywsd02mmc/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/xiaomi_lywsd02mmc/sensor.py b/esphome/components/xiaomi_lywsd02mmc/sensor.py new file mode 100644 index 0000000000..43784ef698 --- /dev/null +++ b/esphome/components/xiaomi_lywsd02mmc/sensor.py @@ -0,0 +1,77 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, esp32_ble_tracker +from esphome.const import ( + CONF_BATTERY_LEVEL, + CONF_HUMIDITY, + CONF_MAC_ADDRESS, + CONF_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_PERCENT, + DEVICE_CLASS_HUMIDITY, + DEVICE_CLASS_BATTERY, + CONF_ID, + CONF_BINDKEY, +) + +AUTO_LOAD = ["xiaomi_ble"] +CODEOWNERS = ["@juanluss31"] +DEPENDENCIES = ["esp32_ble_tracker"] + +xiaomi_lywsd02mmc_ns = cg.esphome_ns.namespace("xiaomi_lywsd02mmc") +XiaomiLYWSD02MMC = xiaomi_lywsd02mmc_ns.class_( + "XiaomiLYWSD02MMC", esp32_ble_tracker.ESPBTDeviceListener, cg.Component +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(XiaomiLYWSD02MMC), + cv.Required(CONF_MAC_ADDRESS): cv.mac_address, + cv.Required(CONF_BINDKEY): cv.bind_key, + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_HUMIDITY, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema( + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + device_class=DEVICE_CLASS_BATTERY, + state_class=STATE_CLASS_MEASUREMENT, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + } + ) + .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await esp32_ble_tracker.register_ble_device(var, config) + + cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex)) + cg.add(var.set_bindkey(config[CONF_BINDKEY])) + + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature(sens)) + if humidity_config := config.get(CONF_HUMIDITY): + sens = await sensor.new_sensor(humidity_config) + cg.add(var.set_humidity(sens)) + if battery_level_config := config.get(CONF_BATTERY_LEVEL): + sens = await sensor.new_sensor(battery_level_config) + cg.add(var.set_battery_level(sens)) diff --git a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp new file mode 100644 index 0000000000..cc122f2264 --- /dev/null +++ b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.cpp @@ -0,0 +1,73 @@ +#include "xiaomi_lywsd02mmc.h" +#include "esphome/core/log.h" + +#ifdef USE_ESP32 + +namespace esphome { +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()); + LOG_SENSOR(" ", "Temperature", this->temperature_); + LOG_SENSOR(" ", "Humidity", this->humidity_); + LOG_SENSOR(" ", "Battery Level", this->battery_level_); +} + +bool XiaomiLYWSD02MMC::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { + if (device.address_uint64() != this->address_) { + ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); + return false; + } + ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); + + bool success = false; + for (auto &service_data : device.get_service_datas()) { + auto res = xiaomi_ble::parse_xiaomi_header(service_data); + if (!res.has_value()) { + continue; + } + if (res->is_duplicate) { + continue; + } + if (res->has_encryption && + (!(xiaomi_ble::decrypt_xiaomi_payload(const_cast &>(service_data.data), this->bindkey_, + this->address_)))) { + continue; + } + if (!(xiaomi_ble::parse_xiaomi_message(service_data.data, *res))) { + continue; + } + if (!(xiaomi_ble::report_xiaomi_results(res, device.address_str()))) { + continue; + } + if (res->temperature.has_value() && this->temperature_ != nullptr) + this->temperature_->publish_state(*res->temperature); + if (res->humidity.has_value() && this->humidity_ != nullptr) + this->humidity_->publish_state(*res->humidity); + if (res->battery_level.has_value() && this->battery_level_ != nullptr) + this->battery_level_->publish_state(*res->battery_level); + success = true; + } + + return success; +} + +void XiaomiLYWSD02MMC::set_bindkey(const std::string &bindkey) { + memset(this->bindkey_, 0, 16); + if (bindkey.size() != 32) { + return; + } + char temp[3] = {0}; + for (int i = 0; i < 16; i++) { + strncpy(temp, &(bindkey.c_str()[i * 2]), 2); + this->bindkey_[i] = std::strtoul(temp, nullptr, 16); + } +} + +} // namespace xiaomi_lywsd02mmc +} // namespace esphome + +#endif diff --git a/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h new file mode 100644 index 0000000000..19092aa2a9 --- /dev/null +++ b/esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h @@ -0,0 +1,37 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/components/xiaomi_ble/xiaomi_ble.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace xiaomi_lywsd02mmc { + +class XiaomiLYWSD02MMC : public Component, public esp32_ble_tracker::ESPBTDeviceListener { + public: + void set_address(uint64_t address) { this->address_ = address; } + void set_bindkey(const std::string &bindkey); + + bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; + + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; } + void set_humidity(sensor::Sensor *humidity) { this->humidity_ = humidity; } + void set_battery_level(sensor::Sensor *battery_level) { this->battery_level_ = battery_level; } + + protected: + uint64_t address_; + uint8_t bindkey_[16]; + sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *humidity_{nullptr}; + sensor::Sensor *battery_level_{nullptr}; +}; + +} // namespace xiaomi_lywsd02mmc +} // namespace esphome + +#endif diff --git a/tests/components/xiaomi_lywsd02mmc/common.yaml b/tests/components/xiaomi_lywsd02mmc/common.yaml new file mode 100644 index 0000000000..e63f585830 --- /dev/null +++ b/tests/components/xiaomi_lywsd02mmc/common.yaml @@ -0,0 +1,12 @@ +esp32_ble_tracker: + +sensor: + - platform: xiaomi_lywsd02mmc + mac_address: A4:C1:38:54:5E:18 + bindkey: 2529d8e0d23150a588675cc54ad48400 + temperature: + name: Xiaomi LYWSD02MMC Temperature + humidity: + name: Xiaomi LYWSD02MMC Humidity + battery_level: + name: Xiaomi LYWSD02MMC Battery Level diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_lywsd02mmc/test.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_lywsd02mmc/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml b/tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/xiaomi_lywsd02mmc/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 8d106e97a2bd03239e11609fb6a53b1e493c634b Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 03:14:25 +0200 Subject: [PATCH 0177/1052] [code-quality] fix clang-tidy web server (#7230) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/web_server/web_server.h | 2 +- esphome/components/web_server_base/web_server_base.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 5b98806af1..d4ab592b7b 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -334,7 +334,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { /// Override the web handler's handleRequest method. void handleRequest(AsyncWebServerRequest *request) override; /// This web handle is not trivial. - bool isRequestHandlerTrivial() override; + bool isRequestHandlerTrivial() override; // NOLINT(readability-identifier-naming) void add_entity_to_sorting_list(EntityBase *entity, float weight); diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index c312126472..2282d55ec1 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -134,6 +134,7 @@ class OTARequestHandler : public AsyncWebHandler { return request->url() == "/update" && request->method() == HTTP_POST; } + // NOLINTNEXTLINE(readability-identifier-naming) bool isRequestHandlerTrivial() override { return false; } protected: From 390d5f2f9361c00c558d64fb00c9ce66b92703e1 Mon Sep 17 00:00:00 2001 From: RFDarter Date: Tue, 13 Aug 2024 03:26:39 +0200 Subject: [PATCH 0178/1052] [test][web_server] Rejig test for v3 (#7110) --- tests/components/web_server/common_v1.yaml | 3 ++- tests/components/web_server/common_v2.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/components/web_server/common_v1.yaml b/tests/components/web_server/common_v1.yaml index bf5aab4ce6..3c51f894b8 100644 --- a/tests/components/web_server/common_v1.yaml +++ b/tests/components/web_server/common_v1.yaml @@ -1,4 +1,5 @@ -<<: !include common.yaml +packages: + device_base: !include common.yaml web_server: port: 8080 diff --git a/tests/components/web_server/common_v2.yaml b/tests/components/web_server/common_v2.yaml index 564c43e553..2af5ceca44 100644 --- a/tests/components/web_server/common_v2.yaml +++ b/tests/components/web_server/common_v2.yaml @@ -1,4 +1,5 @@ -<<: !include common.yaml +packages: + device_base: !include common.yaml web_server: port: 8080 From ab51bbd8f7a9ca8648f7a245cdec02f8fa08e14b Mon Sep 17 00:00:00 2001 From: Olivier ARCHER Date: Tue, 13 Aug 2024 03:52:31 +0200 Subject: [PATCH 0179/1052] [api] Error log when NONE Update command is sent (#7247) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/api/api_connection.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 8e4c6faaee..bd438265d4 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1335,6 +1335,9 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) { case enums::UPDATE_COMMAND_CHECK: update->check(); break; + case enums::UPDATE_COMMAND_NONE: + ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled. Check client is sending the correct command"); + break; default: ESP_LOGW(TAG, "Unknown update command: %" PRIu32, msg.command); break; From 2b25daa199b175e1f2d3f1db94b6516f37b90748 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:12:06 +1200 Subject: [PATCH 0180/1052] [api] Add new flag to request state/attribute once from HA only (#7258) --- esphome/components/api/api.proto | 1 + esphome/components/api/api_pb2.cpp | 15 +++++++++++++++ esphome/components/api/api_pb2.h | 2 ++ esphome/components/api/api_server.cpp | 10 ++++++++++ esphome/components/api/api_server.h | 3 +++ 5 files changed, 31 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index b62fddf815..72eaeed6d7 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -686,6 +686,7 @@ message SubscribeHomeAssistantStateResponse { option (source) = SOURCE_SERVER; string entity_id = 1; string attribute = 2; + bool once = 3; } message HomeAssistantStateResponse { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index a57627a66c..bb37824403 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -3109,6 +3109,16 @@ void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const { out.append("SubscribeHomeAssistantStatesRequest {}"); } #endif +bool SubscribeHomeAssistantStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 3: { + this->once = value.as_bool(); + return true; + } + default: + return false; + } +} bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { @@ -3126,6 +3136,7 @@ bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, Proto void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->entity_id); buffer.encode_string(2, this->attribute); + buffer.encode_bool(3, this->once); } #ifdef HAS_PROTO_MESSAGE_DUMP void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const { @@ -3138,6 +3149,10 @@ void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const { out.append(" attribute: "); out.append("'").append(this->attribute).append("'"); out.append("\n"); + + out.append(" once: "); + out.append(YESNO(this->once)); + out.append("\n"); out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index bb5263cffa..3eb945fd8d 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -836,6 +836,7 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage { public: std::string entity_id{}; std::string attribute{}; + bool once{false}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; @@ -843,6 +844,7 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage { protected: bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class HomeAssistantStateResponse : public ProtoMessage { public: diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index a61ae89243..0fde3e47af 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -359,8 +359,18 @@ void APIServer::subscribe_home_assistant_state(std::string entity_id, optional attribute, + std::function f) { + this->state_subs_.push_back(HomeAssistantStateSubscription{ + .entity_id = std::move(entity_id), + .attribute = std::move(attribute), + .callback = std::move(f), + .once = true, + }); +}; const std::vector &APIServer::get_state_subs() const { return this->state_subs_; } diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 43bc8a7348..899eaede49 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -112,10 +112,13 @@ class APIServer : public Component, public Controller { std::string entity_id; optional attribute; std::function callback; + bool once; }; void subscribe_home_assistant_state(std::string entity_id, optional attribute, std::function f); + void get_home_assistant_state(std::string entity_id, optional attribute, + std::function f); const std::vector &get_state_subs() const; const std::vector &get_user_services() const { return this->user_services_; } From 8696f922d120e787f7231d9d4b8a00f74eec0125 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:33:16 +1200 Subject: [PATCH 0181/1052] [homeassistant] Add ``HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA`` (#7259) --- CODEOWNERS | 2 +- esphome/components/homeassistant/__init__.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 9865e51f11..663a942cb4 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -168,7 +168,7 @@ esphome/components/he60r/* @clydebarrow esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hm3301/* @freekode -esphome/components/homeassistant/* @OttoWinter +esphome/components/homeassistant/* @OttoWinter @esphome/core esphome/components/honeywell_hih_i2c/* @Benichou34 esphome/components/honeywellabp/* @RubyBailey esphome/components/honeywellabp2_i2c/* @jpfaff diff --git a/esphome/components/homeassistant/__init__.py b/esphome/components/homeassistant/__init__.py index 776aa7fd7b..6d997e48ca 100644 --- a/esphome/components/homeassistant/__init__.py +++ b/esphome/components/homeassistant/__init__.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID, CONF_INTERNAL -CODEOWNERS = ["@OttoWinter"] +CODEOWNERS = ["@OttoWinter", "@esphome/core"] homeassistant_ns = cg.esphome_ns.namespace("homeassistant") HOME_ASSISTANT_IMPORT_SCHEMA = cv.Schema( @@ -13,6 +13,13 @@ HOME_ASSISTANT_IMPORT_SCHEMA = cv.Schema( } ) +HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA = cv.Schema( + { + cv.Required(CONF_ENTITY_ID): cv.entity_id, + cv.Optional(CONF_INTERNAL, default=True): cv.boolean, + } +) + def setup_home_assistant_entity(var, config): cg.add(var.set_entity_id(config[CONF_ENTITY_ID])) From 2a70ef05d17c0645fd4e9023d6b1bc5c2ea078ca Mon Sep 17 00:00:00 2001 From: nkinnan Date: Mon, 12 Aug 2024 23:48:12 -0700 Subject: [PATCH 0182/1052] [const] Add some units for future use and adjust case (#7260) --- esphome/const.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/const.py b/esphome/const.py index 13559ecf95..c5d0e8f838 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1034,8 +1034,10 @@ UNIT_KELVIN = "K" UNIT_KILOGRAM = "kg" UNIT_KILOMETER = "km" UNIT_KILOMETER_PER_HOUR = "km/h" -UNIT_KILOVOLT_AMPS_REACTIVE = "kVAr" -UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kVArh" +UNIT_KILOVOLT_AMPS = "kVA" +UNIT_KILOVOLT_AMPS_HOURS = "kVAh" +UNIT_KILOVOLT_AMPS_REACTIVE = "kVAR" +UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kVARh" UNIT_KILOWATT = "kW" UNIT_KILOWATT_HOURS = "kWh" UNIT_LUX = "lx" @@ -1066,6 +1068,7 @@ UNIT_SECOND = "s" UNIT_STEPS = "steps" UNIT_VOLT = "V" UNIT_VOLT_AMPS = "VA" +UNIT_VOLT_AMPS_HOURS = "VAh" UNIT_VOLT_AMPS_REACTIVE = "VAR" UNIT_VOLT_AMPS_REACTIVE_HOURS = "VARh" UNIT_WATT = "W" From 506e69addf81fc8cc542520d7db17937a8b74c5b Mon Sep 17 00:00:00 2001 From: guillempages Date: Tue, 13 Aug 2024 09:44:43 +0200 Subject: [PATCH 0183/1052] [online_image] add option to show placeholder while downloading (#7083) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/online_image/__init__.py | 6 ++++++ esphome/components/online_image/online_image.cpp | 8 ++++++++ esphome/components/online_image/online_image.h | 11 +++++++++++ 3 files changed, 25 insertions(+) diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index ee5357457a..d9a7609543 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -27,6 +27,7 @@ CODEOWNERS = ["@guillempages"] MULTI_CONF = True CONF_ON_DOWNLOAD_FINISHED = "on_download_finished" +CONF_PLACEHOLDER = "placeholder" _LOGGER = logging.getLogger(__name__) @@ -73,6 +74,7 @@ ONLINE_IMAGE_SCHEMA = cv.Schema( # cv.Required(CONF_URL): cv.url, cv.Required(CONF_FORMAT): cv.enum(IMAGE_FORMAT, upper=True), + cv.Optional(CONF_PLACEHOLDER): cv.use_id(Image_), cv.Optional(CONF_BUFFER_SIZE, default=2048): cv.int_range(256, 65536), cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation( { @@ -152,6 +154,10 @@ async def to_code(config): cg.add(var.set_transparency(transparent)) + 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) diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index a4cf0158aa..480bad6aca 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -35,6 +35,14 @@ OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFor this->set_url(url); } +void OnlineImage::draw(int x, int y, display::Display *display, Color color_on, Color color_off) { + if (this->data_start_) { + Image::draw(x, y, display, color_on, color_off); + } else if (this->placeholder_) { + this->placeholder_->draw(x, y, display, color_on, color_off); + } +} + void OnlineImage::release() { if (this->buffer_) { ESP_LOGD(TAG, "Deallocating old buffer..."); diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 30e97760ea..775cc46e0b 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -50,6 +50,8 @@ class OnlineImage : public PollingComponent, OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type, uint32_t buffer_size); + void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; + void update() override; void loop() override; @@ -60,6 +62,14 @@ class OnlineImage : public PollingComponent, } } + /** + * @brief Set the image that needs to be shown as long as the downloaded image + * is not available. + * + * @param placeholder Pointer to the (@link Image) to show as placeholder. + */ + void set_placeholder(image::Image *placeholder) { this->placeholder_ = placeholder; } + /** * Release the buffer storing the image. The image will need to be downloaded again * to be able to be displayed. @@ -113,6 +123,7 @@ class OnlineImage : public PollingComponent, DownloadBuffer download_buffer_; const ImageFormat format_; + image::Image *placeholder_{nullptr}; std::string url_{""}; From 3598560472b844c172a1ed2783f13ba258be378d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:06:01 +1000 Subject: [PATCH 0184/1052] [lvgl] Add initial_focus for encoders (#7256) --- esphome/components/lvgl/__init__.py | 3 ++- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/encoders.py | 8 ++++++++ esphome/components/lvgl/schemas.py | 25 +++++++++++++++-------- tests/components/lvgl/test.esp32-ard.yaml | 1 + 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 6bf6e287f8..7c51d9c70d 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -23,7 +23,7 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, update_to_code from .defines import CONF_SKIP -from .encoders import ENCODERS_CONFIG, encoders_to_code +from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent from .schemas import ( @@ -272,6 +272,7 @@ async def to_code(config): templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ) await build_automation(idle_trigger, [], conf) + await initial_focus_to_code(config) for comp in helpers.lvgl_components_required: CORE.add_define(f"USE_LVGL_{comp.upper()}") diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 1c6fd2678c..e48679996b 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -413,6 +413,7 @@ CONF_GRID_ROW_ALIGN = "grid_row_align" CONF_GRID_ROWS = "grid_rows" CONF_HEADER_MODE = "header_mode" CONF_HOME = "home" +CONF_INITIAL_FOCUS = "initial_focus" CONF_KEY_CODE = "key_code" CONF_LAYOUT = "layout" CONF_LEFT_BUTTON = "left_button" diff --git a/esphome/components/lvgl/encoders.py b/esphome/components/lvgl/encoders.py index cfd0e42996..81bcda95b4 100644 --- a/esphome/components/lvgl/encoders.py +++ b/esphome/components/lvgl/encoders.py @@ -8,6 +8,7 @@ from .defines import ( CONF_DEFAULT_GROUP, CONF_ENCODERS, CONF_ENTER_BUTTON, + CONF_INITIAL_FOCUS, CONF_LEFT_BUTTON, CONF_LONG_PRESS_REPEAT_TIME, CONF_LONG_PRESS_TIME, @@ -67,3 +68,10 @@ async def encoders_to_code(var, config): else: group = default_group lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group) + + +async def initial_focus_to_code(config): + for enc_conf in config[CONF_ENCODERS]: + if default_focus := enc_conf.get(CONF_INITIAL_FOCUS): + obj = await cg.get_variable(default_focus) + lv.group_focus_obj(obj) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index f172ba9f2b..e4b1c3f8fa 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -14,11 +14,19 @@ from esphome.const import ( from esphome.core import TimePeriod from esphome.schema_extractors import SCHEMA_EXTRACT -from . import defines as df, lv_validation as lvalid, types as ty +from . import defines as df, lv_validation as lvalid from .helpers import add_lv_use, requires_component, validate_printf from .lv_validation import lv_color, lv_font, lv_image from .lvcode import LvglComponent -from .types import WidgetType, lv_group_t +from .types import ( + LVEncoderListener, + LvType, + WidgetType, + lv_group_t, + lv_obj_t, + lv_pseudo_button_t, + lv_style_t, +) # this will be populated later, in __init__.py to avoid circular imports. WIDGET_TYPES: dict = {} @@ -46,7 +54,7 @@ TEXT_SCHEMA = cv.Schema( LIST_ACTION_SCHEMA = cv.ensure_list( cv.maybe_simple_value( { - cv.Required(CONF_ID): cv.use_id(ty.lv_pseudo_button_t), + cv.Required(CONF_ID): cv.use_id(lv_pseudo_button_t), }, key=CONF_ID, ) @@ -59,9 +67,10 @@ PRESS_TIME = cv.All( ENCODER_SCHEMA = cv.Schema( { cv.GenerateID(): cv.All( - cv.declare_id(ty.LVEncoderListener), requires_component("binary_sensor") + cv.declare_id(LVEncoderListener), requires_component("binary_sensor") ), cv.Optional(CONF_GROUP): cv.declare_id(lv_group_t), + cv.Optional(df.CONF_INITIAL_FOCUS): cv.use_id(lv_obj_t), cv.Optional(df.CONF_LONG_PRESS_TIME, default="400ms"): PRESS_TIME, cv.Optional(df.CONF_LONG_PRESS_REPEAT_TIME, default="100ms"): PRESS_TIME, } @@ -161,7 +170,7 @@ STYLE_REMAP = { # Complete object style schema STYLE_SCHEMA = cv.Schema({cv.Optional(k): v for k, v in STYLE_PROPS.items()}).extend( { - cv.Optional(df.CONF_STYLES): cv.ensure_list(cv.use_id(ty.lv_style_t)), + cv.Optional(df.CONF_STYLES): cv.ensure_list(cv.use_id(lv_style_t)), cv.Optional(df.CONF_SCROLLBAR_MODE): df.LvConstant( "LV_SCROLLBAR_MODE_", "OFF", "ON", "ACTIVE", "AUTO" ).one_of, @@ -193,12 +202,12 @@ def part_schema(widget_type: WidgetType): ) -def automation_schema(typ: ty.LvType): +def automation_schema(typ: LvType): if typ.has_on_value: events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,) else: events = df.LV_EVENT_TRIGGERS - if isinstance(typ, ty.LvType): + if isinstance(typ, LvType): template = Trigger.template(typ.get_arg_type()) else: template = Trigger.template() @@ -261,7 +270,7 @@ LAYOUT_SCHEMAS = {} ALIGN_TO_SCHEMA = { cv.Optional(df.CONF_ALIGN_TO): cv.Schema( { - cv.Required(CONF_ID): cv.use_id(ty.lv_obj_t), + 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, diff --git a/tests/components/lvgl/test.esp32-ard.yaml b/tests/components/lvgl/test.esp32-ard.yaml index 2d6a6871ba..51593e7967 100644 --- a/tests/components/lvgl/test.esp32-ard.yaml +++ b/tests/components/lvgl/test.esp32-ard.yaml @@ -46,6 +46,7 @@ binary_sensor: lvgl: encoders: group: switches + initial_focus: button_button enter_button: select_button sensor: left_button: up_button From c9979ad90c950198d3cc624e88d43975e5af497a Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 21:46:23 +0200 Subject: [PATCH 0185/1052] [code-quality] fix order in esphome/const.py (#7267) --- esphome/const.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/const.py b/esphome/const.py index c5d0e8f838..55f1c23b40 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -7,22 +7,22 @@ VALID_SUBSTITUTIONS_CHARACTERS = ( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" ) +PLATFORM_BK72XX = "bk72xx" PLATFORM_ESP32 = "esp32" PLATFORM_ESP8266 = "esp8266" -PLATFORM_RP2040 = "rp2040" PLATFORM_HOST = "host" -PLATFORM_BK72XX = "bk72xx" -PLATFORM_RTL87XX = "rtl87xx" PLATFORM_LIBRETINY_OLDSTYLE = "libretiny" +PLATFORM_RP2040 = "rp2040" +PLATFORM_RTL87XX = "rtl87xx" TARGET_PLATFORMS = [ + PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, - PLATFORM_RP2040, PLATFORM_HOST, - PLATFORM_BK72XX, - PLATFORM_RTL87XX, PLATFORM_LIBRETINY_OLDSTYLE, + PLATFORM_RP2040, + PLATFORM_RTL87XX, ] SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} From b082a64d3248d4a272924f5887915109427b5d68 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 21:48:27 +0200 Subject: [PATCH 0186/1052] [code-quality] fix clang-tidy network (#7266) --- esphome/components/network/__init__.py | 1 + esphome/components/network/ip_address.h | 3 +++ esphome/components/network/util.cpp | 3 ++- esphome/components/network/util.h | 4 +++- esphome/core/defines.h | 1 + 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index f5ddbc0da7..96db322bde 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -32,6 +32,7 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): + cg.add_define("USE_NETWORK") if (enable_ipv6 := config.get(CONF_ENABLE_IPV6, None)) is not None: cg.add_define("USE_NETWORK_IPV6", enable_ipv6) if enable_ipv6: diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 30a426e458..941934cf0a 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -1,4 +1,6 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_NETWORK #include #include #include @@ -140,3 +142,4 @@ using IPAddresses = std::array; } // namespace network } // namespace esphome +#endif diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index 445485b644..ed519f738a 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -1,6 +1,6 @@ #include "util.h" #include "esphome/core/defines.h" - +#ifdef USE_NETWORK #ifdef USE_WIFI #include "esphome/components/wifi/wifi_component.h" #endif @@ -63,3 +63,4 @@ std::string get_use_address() { } // namespace network } // namespace esphome +#endif diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h index 5377d44f2f..b518696e68 100644 --- a/esphome/components/network/util.h +++ b/esphome/components/network/util.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_NETWORK #include #include "ip_address.h" @@ -16,3 +17,4 @@ IPAddresses get_ip_addresses(); } // namespace network } // namespace esphome +#endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index a711148ec8..a4d473b76e 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -51,6 +51,7 @@ #define USE_MDNS #define USE_MEDIA_PLAYER #define USE_MQTT +#define USE_NETWORK #define USE_NEXTION_TFT_UPLOAD #define USE_NUMBER #define USE_ONLINE_IMAGE_PNG_SUPPORT From 9663b7d67ccf2c2e188a709a512ffe0aaae45865 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 21:53:42 +0200 Subject: [PATCH 0187/1052] [code-quality] fix clang-tidy core optional (#7265) --- esphome/core/optional.h | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/core/optional.h b/esphome/core/optional.h index 5b96781e63..770b77081e 100644 --- a/esphome/core/optional.h +++ b/esphome/core/optional.h @@ -104,7 +104,6 @@ template class optional { // NOLINT has_value_ = true; } - private: bool has_value_{false}; // NOLINT value_type value_; // NOLINT }; From 4bd7ba0d30dca6cb3c13cdd7a0cfab64253e9720 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 21:54:37 +0200 Subject: [PATCH 0188/1052] [code-quality] Fix variable naming in base_light_effects (#7237) --- esphome/components/light/base_light_effects.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index f7829a3f44..9e02e889c9 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -25,7 +25,7 @@ class PulseLightEffect : public LightEffect { return; } auto call = this->state_->turn_on(); - float out = this->on_ ? this->max_brightness : this->min_brightness; + float out = this->on_ ? this->max_brightness_ : this->min_brightness_; call.set_brightness_if_supported(out); call.set_transition_length_if_supported(this->on_ ? this->transition_on_length_ : this->transition_off_length_); this->on_ = !this->on_; @@ -43,8 +43,8 @@ class PulseLightEffect : public LightEffect { void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } void set_min_max_brightness(float min, float max) { - this->min_brightness = min; - this->max_brightness = max; + this->min_brightness_ = min; + this->max_brightness_ = max; } protected: @@ -53,8 +53,8 @@ class PulseLightEffect : public LightEffect { uint32_t transition_on_length_{}; uint32_t transition_off_length_{}; uint32_t update_interval_{}; - float min_brightness{0.0}; - float max_brightness{1.0}; + float min_brightness_{0.0}; + float max_brightness_{1.0}; }; /// Random effect. Sets random colors every 10 seconds and slowly transitions between them. From f81ce2c70743098796493ce703c7c388de10f783 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 21:56:09 +0200 Subject: [PATCH 0189/1052] [code-quality] fix clang-tidy mqtt (#7253) --- esphome/components/mqtt/mqtt_backend.h | 4 +++- esphome/components/mqtt/mqtt_backend_esp32.cpp | 5 ++++- esphome/components/mqtt/mqtt_backend_esp32.h | 4 +++- esphome/components/mqtt/mqtt_backend_esp8266.h | 4 +++- esphome/components/mqtt/mqtt_backend_libretiny.h | 4 +++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/esphome/components/mqtt/mqtt_backend.h b/esphome/components/mqtt/mqtt_backend.h index d23cda578d..3962c40a42 100644 --- a/esphome/components/mqtt/mqtt_backend.h +++ b/esphome/components/mqtt/mqtt_backend.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_MQTT #include #include #include "esphome/components/network/ip_address.h" @@ -67,3 +68,4 @@ class MQTTBackend { } // namespace mqtt } // namespace esphome +#endif diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 9c2e487ae7..ed500c6d44 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -1,7 +1,9 @@ +#include "mqtt_backend_esp32.h" + +#ifdef USE_MQTT #ifdef USE_ESP32 #include -#include "mqtt_backend_esp32.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" @@ -189,3 +191,4 @@ void MQTTBackendESP32::mqtt_event_handler(void *handler_args, esp_event_base_t b } // namespace mqtt } // namespace esphome #endif // USE_ESP32 +#endif diff --git a/esphome/components/mqtt/mqtt_backend_esp32.h b/esphome/components/mqtt/mqtt_backend_esp32.h index b1f672da10..9054702115 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.h +++ b/esphome/components/mqtt/mqtt_backend_esp32.h @@ -1,5 +1,7 @@ #pragma once +#include "mqtt_backend.h" +#ifdef USE_MQTT #ifdef USE_ESP32 #include @@ -7,7 +9,6 @@ #include #include "esphome/components/network/ip_address.h" #include "esphome/core/helpers.h" -#include "mqtt_backend.h" namespace esphome { namespace mqtt { @@ -174,3 +175,4 @@ class MQTTBackendESP32 final : public MQTTBackend { } // namespace esphome #endif +#endif diff --git a/esphome/components/mqtt/mqtt_backend_esp8266.h b/esphome/components/mqtt/mqtt_backend_esp8266.h index 06d4993bdf..a979634bf4 100644 --- a/esphome/components/mqtt/mqtt_backend_esp8266.h +++ b/esphome/components/mqtt/mqtt_backend_esp8266.h @@ -1,8 +1,9 @@ #pragma once +#include "mqtt_backend.h" +#ifdef USE_MQTT #ifdef USE_ESP8266 -#include "mqtt_backend.h" #include namespace esphome { @@ -70,3 +71,4 @@ class MQTTBackendESP8266 final : public MQTTBackend { } // namespace esphome #endif // defined(USE_ESP8266) +#endif diff --git a/esphome/components/mqtt/mqtt_backend_libretiny.h b/esphome/components/mqtt/mqtt_backend_libretiny.h index ac4d4298fc..2578ae9941 100644 --- a/esphome/components/mqtt/mqtt_backend_libretiny.h +++ b/esphome/components/mqtt/mqtt_backend_libretiny.h @@ -1,8 +1,9 @@ #pragma once +#include "mqtt_backend.h" +#ifdef USE_MQTT #ifdef USE_LIBRETINY -#include "mqtt_backend.h" #include namespace esphome { @@ -70,3 +71,4 @@ class MQTTBackendLibreTiny final : public MQTTBackend { } // namespace esphome #endif // defined(USE_LIBRETINY) +#endif From 2e58297a16e2ea0e94235b5268fa98e19744f954 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 13 Aug 2024 21:58:30 +0200 Subject: [PATCH 0190/1052] [code-quality] fix clang-tidy wifi related (#7254) --- esphome/components/wifi/wifi_component.cpp | 2 ++ esphome/components/wifi/wifi_component.h | 4 +++- esphome/components/wifi/wifi_component_esp32_arduino.cpp | 2 ++ esphome/components/wifi/wifi_component_esp8266.cpp | 2 ++ esphome/components/wifi/wifi_component_esp_idf.cpp | 2 ++ esphome/components/wifi/wifi_component_libretiny.cpp | 2 ++ esphome/components/wifi/wifi_component_pico_w.cpp | 2 ++ esphome/components/wifi_info/wifi_info_text_sensor.cpp | 2 ++ esphome/components/wifi_info/wifi_info_text_sensor.h | 2 ++ esphome/components/wifi_signal/wifi_signal_sensor.cpp | 2 ++ esphome/components/wifi_signal/wifi_signal_sensor.h | 3 ++- 11 files changed, 23 insertions(+), 2 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 8c40f87879..583a27466a 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1,4 +1,5 @@ #include "wifi_component.h" +#ifdef USE_WIFI #include #include @@ -856,3 +857,4 @@ WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-con } // namespace wifi } // namespace esphome +#endif diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index d79cde0b18..dde0d1d5a5 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -1,9 +1,10 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_WIFI #include "esphome/components/network/ip_address.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" -#include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include @@ -442,3 +443,4 @@ template class WiFiDisableAction : public Action { } // namespace wifi } // namespace esphome +#endif diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 71548b7a3e..b8724838c8 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -1,5 +1,6 @@ #include "wifi_component.h" +#ifdef USE_WIFI #ifdef USE_ESP32_FRAMEWORK_ARDUINO #include @@ -802,3 +803,4 @@ network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return network::IPAddr } // namespace esphome #endif // USE_ESP32_FRAMEWORK_ARDUINO +#endif diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 997457e2d2..92f80c1e52 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -1,6 +1,7 @@ #include "wifi_component.h" #include "esphome/core/defines.h" +#ifdef USE_WIFI #ifdef USE_ESP8266 #include @@ -834,3 +835,4 @@ void WiFiComponent::wifi_loop_() {} } // namespace esphome #endif +#endif diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index a8d67ed44d..6008acb95d 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -1,5 +1,6 @@ #include "wifi_component.h" +#ifdef USE_WIFI #ifdef USE_ESP_IDF #include @@ -1010,3 +1011,4 @@ network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { } // namespace esphome #endif // USE_ESP_IDF +#endif diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index f6b0fb2699..19ade84a88 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -1,5 +1,6 @@ #include "wifi_component.h" +#ifdef USE_WIFI #ifdef USE_LIBRETINY #include @@ -468,3 +469,4 @@ void WiFiComponent::wifi_loop_() {} } // namespace esphome #endif // USE_LIBRETINY +#endif diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index 4afcf2d78b..bac986d899 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -1,6 +1,7 @@ #include "wifi_component.h" +#ifdef USE_WIFI #ifdef USE_RP2040 #include "lwip/dns.h" @@ -218,3 +219,4 @@ void WiFiComponent::wifi_pre_setup_() {} } // namespace esphome #endif +#endif diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.cpp b/esphome/components/wifi_info/wifi_info_text_sensor.cpp index eeb4985398..150c7229f8 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.cpp +++ b/esphome/components/wifi_info/wifi_info_text_sensor.cpp @@ -1,4 +1,5 @@ #include "wifi_info_text_sensor.h" +#ifdef USE_WIFI #include "esphome/core/log.h" namespace esphome { @@ -15,3 +16,4 @@ void DNSAddressWifiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo DNS Addre } // namespace wifi_info } // namespace esphome +#endif diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.h b/esphome/components/wifi_info/wifi_info_text_sensor.h index 0f31a57cc5..0aa44a0894 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.h +++ b/esphome/components/wifi_info/wifi_info_text_sensor.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/text_sensor/text_sensor.h" #include "esphome/components/wifi/wifi_component.h" +#ifdef USE_WIFI #include namespace esphome { @@ -131,3 +132,4 @@ class MacAddressWifiInfo : public Component, public text_sensor::TextSensor { } // namespace wifi_info } // namespace esphome +#endif diff --git a/esphome/components/wifi_signal/wifi_signal_sensor.cpp b/esphome/components/wifi_signal/wifi_signal_sensor.cpp index ba22138e2a..4347295421 100644 --- a/esphome/components/wifi_signal/wifi_signal_sensor.cpp +++ b/esphome/components/wifi_signal/wifi_signal_sensor.cpp @@ -1,4 +1,5 @@ #include "wifi_signal_sensor.h" +#ifdef USE_WIFI #include "esphome/core/log.h" namespace esphome { @@ -10,3 +11,4 @@ void WiFiSignalSensor::dump_config() { LOG_SENSOR("", "WiFi Signal", this); } } // namespace wifi_signal } // namespace esphome +#endif diff --git a/esphome/components/wifi_signal/wifi_signal_sensor.h b/esphome/components/wifi_signal/wifi_signal_sensor.h index f797aaa590..fbe03a6404 100644 --- a/esphome/components/wifi_signal/wifi_signal_sensor.h +++ b/esphome/components/wifi_signal/wifi_signal_sensor.h @@ -4,7 +4,7 @@ #include "esphome/core/helpers.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/wifi/wifi_component.h" - +#ifdef USE_WIFI namespace esphome { namespace wifi_signal { @@ -19,3 +19,4 @@ class WiFiSignalSensor : public sensor::Sensor, public PollingComponent { } // namespace wifi_signal } // namespace esphome +#endif From 9ec61cbff3ec79a7fb3d5fe7720ced35dd69989f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 08:12:56 +1200 Subject: [PATCH 0191/1052] Bump docker/build-push-action from 6.6.1 to 6.7.0 in /.github/actions/build-image (#7269) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index f9c44cfb63..56be20bd87 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: . file: ./docker/Dockerfile @@ -69,7 +69,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.6.1 + uses: docker/build-push-action@v6.7.0 with: context: . file: ./docker/Dockerfile From 0c567adf639585474881d8ee8aff8e316b501a80 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 14 Aug 2024 08:13:09 +1200 Subject: [PATCH 0192/1052] [CI] Dont run full CI on ``build-image`` action changes (#7270) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8e93248bb..126a541b3d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: paths: - "**" - "!.github/workflows/*.yml" + - "!.github/actions/build-image/*" - ".github/workflows/ci.yml" - "!.yamllint" - "!.github/dependabot.yml" From 68c56b3e03bffb47c4f4481ff534d8d3ac3bc38e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 14 Aug 2024 07:29:31 +1000 Subject: [PATCH 0193/1052] Implement ByteBuffer (#6878) --- esphome/core/bytebuffer.cpp | 134 ++++++++++++++++++++++++++++++++++++ esphome/core/bytebuffer.h | 96 ++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 esphome/core/bytebuffer.cpp create mode 100644 esphome/core/bytebuffer.h diff --git a/esphome/core/bytebuffer.cpp b/esphome/core/bytebuffer.cpp new file mode 100644 index 0000000000..fb2ade3166 --- /dev/null +++ b/esphome/core/bytebuffer.cpp @@ -0,0 +1,134 @@ +#include "bytebuffer.h" +#include + +namespace esphome { + +ByteBuffer ByteBuffer::create(size_t capacity) { + std::vector data(capacity); + return {data}; +} + +ByteBuffer ByteBuffer::wrap(uint8_t *ptr, size_t len) { + std::vector data(ptr, ptr + len); + return {data}; +} + +ByteBuffer ByteBuffer::wrap(std::vector data) { return {std::move(data)}; } + +void ByteBuffer::set_limit(size_t limit) { + assert(limit <= this->get_capacity()); + this->limit_ = limit; +} +void ByteBuffer::set_position(size_t position) { + assert(position <= this->get_limit()); + this->position_ = position; +} +void ByteBuffer::clear() { + this->limit_ = this->get_capacity(); + this->position_ = 0; +} +uint16_t ByteBuffer::get_uint16() { + assert(this->get_remaining() >= 2); + uint16_t value; + if (endianness_ == LITTLE) { + value = this->data_[this->position_++]; + value |= this->data_[this->position_++] << 8; + } else { + value = this->data_[this->position_++] << 8; + value |= this->data_[this->position_++]; + } + return value; +} + +uint32_t ByteBuffer::get_uint32() { + assert(this->get_remaining() >= 4); + uint32_t value; + if (endianness_ == LITTLE) { + value = this->data_[this->position_++]; + value |= this->data_[this->position_++] << 8; + value |= this->data_[this->position_++] << 16; + value |= this->data_[this->position_++] << 24; + } else { + value = this->data_[this->position_++] << 24; + value |= this->data_[this->position_++] << 16; + value |= this->data_[this->position_++] << 8; + value |= this->data_[this->position_++]; + } + return value; +} +uint32_t ByteBuffer::get_uint24() { + assert(this->get_remaining() >= 3); + uint32_t value; + if (endianness_ == LITTLE) { + value = this->data_[this->position_++]; + value |= this->data_[this->position_++] << 8; + value |= this->data_[this->position_++] << 16; + } else { + value = this->data_[this->position_++] << 16; + value |= this->data_[this->position_++] << 8; + value |= this->data_[this->position_++]; + } + return value; +} +uint32_t ByteBuffer::get_int24() { + auto value = this->get_uint24(); + uint32_t mask = (~(uint32_t) 0) << 23; + if ((value & mask) != 0) + value |= mask; + return value; +} +uint8_t ByteBuffer::get_uint8() { + assert(this->get_remaining() >= 1); + return this->data_[this->position_++]; +} +float ByteBuffer::get_float() { + auto value = this->get_uint32(); + return *(float *) &value; +} +void ByteBuffer::put_uint8(uint8_t value) { + assert(this->get_remaining() >= 1); + this->data_[this->position_++] = value; +} + +void ByteBuffer::put_uint16(uint16_t value) { + assert(this->get_remaining() >= 2); + if (this->endianness_ == LITTLE) { + this->data_[this->position_++] = (uint8_t) value; + this->data_[this->position_++] = (uint8_t) (value >> 8); + } else { + this->data_[this->position_++] = (uint8_t) (value >> 8); + this->data_[this->position_++] = (uint8_t) value; + } +} +void ByteBuffer::put_uint24(uint32_t value) { + assert(this->get_remaining() >= 3); + if (this->endianness_ == LITTLE) { + this->data_[this->position_++] = (uint8_t) value; + this->data_[this->position_++] = (uint8_t) (value >> 8); + this->data_[this->position_++] = (uint8_t) (value >> 16); + } else { + this->data_[this->position_++] = (uint8_t) (value >> 16); + this->data_[this->position_++] = (uint8_t) (value >> 8); + this->data_[this->position_++] = (uint8_t) value; + } +} +void ByteBuffer::put_uint32(uint32_t value) { + assert(this->get_remaining() >= 4); + if (this->endianness_ == LITTLE) { + this->data_[this->position_++] = (uint8_t) value; + this->data_[this->position_++] = (uint8_t) (value >> 8); + this->data_[this->position_++] = (uint8_t) (value >> 16); + this->data_[this->position_++] = (uint8_t) (value >> 24); + } else { + this->data_[this->position_++] = (uint8_t) (value >> 24); + this->data_[this->position_++] = (uint8_t) (value >> 16); + this->data_[this->position_++] = (uint8_t) (value >> 8); + this->data_[this->position_++] = (uint8_t) value; + } +} +void ByteBuffer::put_float(float value) { this->put_uint32(*(uint32_t *) &value); } +void ByteBuffer::flip() { + this->limit_ = this->position_; + this->position_ = 0; +} +} // namespace esphome diff --git a/esphome/core/bytebuffer.h b/esphome/core/bytebuffer.h new file mode 100644 index 0000000000..f242e5e333 --- /dev/null +++ b/esphome/core/bytebuffer.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include + +namespace esphome { + +enum Endian { LITTLE, BIG }; + +/** + * A class modelled on the Java ByteBuffer class. It wraps a vector of bytes and permits putting and getting + * items of various sizes, with an automatically incremented position. + * + * There are three variables maintained pointing into the buffer: + * + * 0 <= position <= limit <= capacity + * + * capacity: the maximum amount of data that can be stored + * limit: the limit of the data currently available to get or put + * position: the current insert or extract position + * + * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore + * the position to the mark. + * + * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. + * + */ +class ByteBuffer { + public: + /** + * Create a new Bytebuffer with the given capacity + */ + static ByteBuffer create(size_t capacity); + /** + * Wrap an existing vector in a Bytebufffer + */ + static ByteBuffer wrap(std::vector data); + /** + * Wrap an existing array in a Bytebufffer + */ + static ByteBuffer wrap(uint8_t *ptr, size_t len); + + // Get one byte from the buffer, increment position by 1 + uint8_t get_uint8(); + // Get a 16 bit unsigned value, increment by 2 + uint16_t get_uint16(); + // Get a 24 bit unsigned value, increment by 3 + uint32_t get_uint24(); + // Get a 32 bit unsigned value, increment by 4 + uint32_t get_uint32(); + // signed versions of the get functions + uint8_t get_int8() { return (int8_t) this->get_uint8(); }; + int16_t get_int16() { return (int16_t) this->get_uint16(); } + uint32_t get_int24(); + int32_t get_int32() { return (int32_t) this->get_uint32(); } + // Get a float value, increment by 4 + float get_float(); + + // put values into the buffer, increment the position accordingly + void put_uint8(uint8_t value); + void put_uint16(uint16_t value); + void put_uint24(uint32_t value); + void put_uint32(uint32_t value); + void put_float(float value); + + inline size_t get_capacity() const { return this->data_.size(); } + inline size_t get_position() const { return this->position_; } + inline size_t get_limit() const { return this->limit_; } + inline size_t get_remaining() const { return this->get_limit() - this->get_position(); } + inline Endian get_endianness() const { return this->endianness_; } + inline void mark() { this->mark_ = this->position_; } + inline void big_endian() { this->endianness_ = BIG; } + inline void little_endian() { this->endianness_ = LITTLE; } + void set_limit(size_t limit); + void set_position(size_t position); + // set position to 0, limit to capacity. + void clear(); + // set limit to current position, postition to zero. Used when swapping from write to read operations. + void flip(); + // retrieve a pointer to the underlying data. + uint8_t *array() { return this->data_.data(); }; + void rewind() { this->position_ = 0; } + void reset() { this->position_ = this->mark_; } + + protected: + ByteBuffer(std::vector data) : data_(std::move(data)) { this->limit_ = this->get_capacity(); } + std::vector data_; + Endian endianness_{LITTLE}; + size_t position_{0}; + size_t mark_{0}; + size_t limit_{0}; +}; + +} // namespace esphome From c5b1a8eb81c4187c3c0f7ca4da40232425845113 Mon Sep 17 00:00:00 2001 From: PaoloTK <60204407+PaoloTK@users.noreply.github.com> Date: Tue, 13 Aug 2024 23:29:55 +0200 Subject: [PATCH 0194/1052] Add min and max brightness parameters for Light dim_relative Action (#6971) --- esphome/components/light/automation.h | 17 ++++++++++++++- esphome/components/light/automation.py | 21 +++++++++++++++++++ esphome/components/light/types.py | 7 +++++++ esphome/const.py | 2 ++ tests/components/light/test.esp32-ard.yaml | 2 ++ tests/components/light/test.esp32-c3-ard.yaml | 2 ++ tests/components/light/test.esp32-c3-idf.yaml | 2 ++ tests/components/light/test.esp32-idf.yaml | 2 ++ tests/components/light/test.esp8266-ard.yaml | 2 ++ tests/components/light/test.rp2040-ard.yaml | 2 ++ 10 files changed, 58 insertions(+), 1 deletion(-) diff --git a/esphome/components/light/automation.h b/esphome/components/light/automation.h index b63fc93dc5..6e055741da 100644 --- a/esphome/components/light/automation.h +++ b/esphome/components/light/automation.h @@ -7,6 +7,8 @@ namespace esphome { namespace light { +enum class LimitMode { CLAMP, DO_NOTHING }; + template class ToggleAction : public Action { public: explicit ToggleAction(LightState *state) : state_(state) {} @@ -77,7 +79,10 @@ template class DimRelativeAction : public Action { float rel = this->relative_brightness_.value(x...); float cur; this->parent_->remote_values.as_brightness(&cur); - float new_brightness = clamp(cur + rel, 0.0f, 1.0f); + if ((limit_mode_ == LimitMode::DO_NOTHING) && ((cur < min_brightness_) || (cur > max_brightness_))) { + return; + } + float new_brightness = clamp(cur + rel, min_brightness_, max_brightness_); call.set_state(new_brightness != 0.0f); call.set_brightness(new_brightness); @@ -85,8 +90,18 @@ template class DimRelativeAction : public Action { call.perform(); } + void set_min_max_brightness(float min, float max) { + this->min_brightness_ = min; + this->max_brightness_ = max; + } + + void set_limit_mode(LimitMode limit_mode) { this->limit_mode_ = limit_mode; } + protected: LightState *parent_; + float min_brightness_{0.0}; + float max_brightness_{1.0}; + LimitMode limit_mode_{LimitMode::CLAMP}; }; template class LightIsOnCondition : public Condition { diff --git a/esphome/components/light/automation.py b/esphome/components/light/automation.py index cfba273565..ec0375f54a 100644 --- a/esphome/components/light/automation.py +++ b/esphome/components/light/automation.py @@ -19,10 +19,15 @@ from esphome.const import ( CONF_WARM_WHITE, CONF_RANGE_FROM, CONF_RANGE_TO, + CONF_BRIGHTNESS_LIMITS, + CONF_LIMIT_MODE, + CONF_MIN_BRIGHTNESS, + CONF_MAX_BRIGHTNESS, ) from .types import ( ColorMode, COLOR_MODES, + LIMIT_MODES, DimRelativeAction, ToggleAction, LightState, @@ -167,6 +172,15 @@ LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema( cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable( cv.positive_time_period_milliseconds ), + cv.Optional(CONF_BRIGHTNESS_LIMITS): cv.Schema( + { + cv.Optional(CONF_MIN_BRIGHTNESS, default="0%"): cv.percentage, + cv.Optional(CONF_MAX_BRIGHTNESS, default="100%"): cv.percentage, + cv.Optional(CONF_LIMIT_MODE, default="CLAMP"): cv.enum( + LIMIT_MODES, upper=True, space="_" + ), + } + ), } ) @@ -182,6 +196,13 @@ async def light_dim_relative_to_code(config, action_id, template_arg, args): if CONF_TRANSITION_LENGTH in config: templ = await cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32) cg.add(var.set_transition_length(templ)) + if conf := config.get(CONF_BRIGHTNESS_LIMITS): + cg.add( + var.set_min_max_brightness( + conf[CONF_MIN_BRIGHTNESS], conf[CONF_MAX_BRIGHTNESS] + ) + ) + cg.add(var.set_limit_mode(conf[CONF_LIMIT_MODE])) return var diff --git a/esphome/components/light/types.py b/esphome/components/light/types.py index a453debd94..64483bcc9c 100644 --- a/esphome/components/light/types.py +++ b/esphome/components/light/types.py @@ -26,6 +26,13 @@ COLOR_MODES = { "RGB_COLD_WARM_WHITE": ColorMode.RGB_COLD_WARM_WHITE, } +# Limit modes +LimitMode = light_ns.enum("LimitMode", is_class=True) +LIMIT_MODES = { + "CLAMP": LimitMode.CLAMP, + "DO_NOTHING": LimitMode.DO_NOTHING, +} + # Actions ToggleAction = light_ns.class_("ToggleAction", automation.Action) LightControlAction = light_ns.class_("LightControlAction", automation.Action) diff --git a/esphome/const.py b/esphome/const.py index 55f1c23b40..37f20796b5 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -95,6 +95,7 @@ CONF_BOARD_FLASH_MODE = "board_flash_mode" CONF_BORDER = "border" CONF_BRANCH = "branch" CONF_BRIGHTNESS = "brightness" +CONF_BRIGHTNESS_LIMITS = "brightness_limits" CONF_BROKER = "broker" CONF_BSSID = "bssid" CONF_BUFFER_SIZE = "buffer_size" @@ -429,6 +430,7 @@ CONF_LIGHT = "light" CONF_LIGHT_ID = "light_id" CONF_LIGHTNING_ENERGY = "lightning_energy" CONF_LIGHTNING_THRESHOLD = "lightning_threshold" +CONF_LIMIT_MODE = "limit_mode" CONF_LINE_THICKNESS = "line_thickness" CONF_LINE_TYPE = "line_type" CONF_LOADED_INTEGRATIONS = "loaded_integrations" diff --git a/tests/components/light/test.esp32-ard.yaml b/tests/components/light/test.esp32-ard.yaml index 7e5718d8d4..1d0b4cd8f0 100644 --- a/tests/components/light/test.esp32-ard.yaml +++ b/tests/components/light/test.esp32-ard.yaml @@ -15,6 +15,8 @@ esphome: - light.dim_relative: id: test_monochromatic_light relative_brightness: 5% + brightness_limits: + max_brightness: 90% output: - platform: gpio diff --git a/tests/components/light/test.esp32-c3-ard.yaml b/tests/components/light/test.esp32-c3-ard.yaml index 8e1709838a..79171805a6 100644 --- a/tests/components/light/test.esp32-c3-ard.yaml +++ b/tests/components/light/test.esp32-c3-ard.yaml @@ -15,6 +15,8 @@ esphome: - light.dim_relative: id: test_monochromatic_light relative_brightness: 5% + brightness_limits: + max_brightness: 90% output: - platform: gpio diff --git a/tests/components/light/test.esp32-c3-idf.yaml b/tests/components/light/test.esp32-c3-idf.yaml index 8e1709838a..79171805a6 100644 --- a/tests/components/light/test.esp32-c3-idf.yaml +++ b/tests/components/light/test.esp32-c3-idf.yaml @@ -15,6 +15,8 @@ esphome: - light.dim_relative: id: test_monochromatic_light relative_brightness: 5% + brightness_limits: + max_brightness: 90% output: - platform: gpio diff --git a/tests/components/light/test.esp32-idf.yaml b/tests/components/light/test.esp32-idf.yaml index 7e5718d8d4..1d0b4cd8f0 100644 --- a/tests/components/light/test.esp32-idf.yaml +++ b/tests/components/light/test.esp32-idf.yaml @@ -15,6 +15,8 @@ esphome: - light.dim_relative: id: test_monochromatic_light relative_brightness: 5% + brightness_limits: + max_brightness: 90% output: - platform: gpio diff --git a/tests/components/light/test.esp8266-ard.yaml b/tests/components/light/test.esp8266-ard.yaml index 4611fb374a..555e1a1b67 100644 --- a/tests/components/light/test.esp8266-ard.yaml +++ b/tests/components/light/test.esp8266-ard.yaml @@ -15,6 +15,8 @@ esphome: - light.dim_relative: id: test_monochromatic_light relative_brightness: 5% + brightness_limits: + max_brightness: 90% output: - platform: gpio diff --git a/tests/components/light/test.rp2040-ard.yaml b/tests/components/light/test.rp2040-ard.yaml index 0215a17e71..a509bc85c9 100644 --- a/tests/components/light/test.rp2040-ard.yaml +++ b/tests/components/light/test.rp2040-ard.yaml @@ -15,6 +15,8 @@ esphome: - light.dim_relative: id: test_monochromatic_light relative_brightness: 5% + brightness_limits: + max_brightness: 90% output: - platform: gpio From 1d25db491c26b7400a8483ab2199fa8d05badfbc Mon Sep 17 00:00:00 2001 From: Markus <974709+Links2004@users.noreply.github.com> Date: Wed, 14 Aug 2024 04:03:12 +0200 Subject: [PATCH 0195/1052] [homeassistant] Native switch entity import and control (#7018) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + .../homeassistant/switch/__init__.py | 30 ++++++++++ .../switch/homeassistant_switch.cpp | 59 +++++++++++++++++++ .../switch/homeassistant_switch.h | 22 +++++++ tests/components/homeassistant/common.yaml | 5 ++ 5 files changed, 117 insertions(+) create mode 100644 esphome/components/homeassistant/switch/__init__.py create mode 100644 esphome/components/homeassistant/switch/homeassistant_switch.cpp create mode 100644 esphome/components/homeassistant/switch/homeassistant_switch.h diff --git a/CODEOWNERS b/CODEOWNERS index 663a942cb4..0c36cda527 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -169,6 +169,7 @@ esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hm3301/* @freekode esphome/components/homeassistant/* @OttoWinter @esphome/core +esphome/components/homeassistant/switch/* @Links2004 esphome/components/honeywell_hih_i2c/* @Benichou34 esphome/components/honeywellabp/* @RubyBailey esphome/components/honeywellabp2_i2c/* @jpfaff diff --git a/esphome/components/homeassistant/switch/__init__.py b/esphome/components/homeassistant/switch/__init__.py new file mode 100644 index 0000000000..3d7c80682a --- /dev/null +++ b/esphome/components/homeassistant/switch/__init__.py @@ -0,0 +1,30 @@ +import esphome.codegen as cg +from esphome.components import switch +import esphome.config_validation as cv +from esphome.const import CONF_ID + +from .. import ( + HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA, + homeassistant_ns, + setup_home_assistant_entity, +) + +CODEOWNERS = ["@Links2004"] +DEPENDENCIES = ["api"] + +HomeassistantSwitch = homeassistant_ns.class_( + "HomeassistantSwitch", switch.Switch, cg.Component +) + +CONFIG_SCHEMA = ( + switch.switch_schema(HomeassistantSwitch) + .extend(cv.COMPONENT_SCHEMA) + .extend(HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await switch.register_switch(var, config) + setup_home_assistant_entity(var, config) diff --git a/esphome/components/homeassistant/switch/homeassistant_switch.cpp b/esphome/components/homeassistant/switch/homeassistant_switch.cpp new file mode 100644 index 0000000000..05ef46e30e --- /dev/null +++ b/esphome/components/homeassistant/switch/homeassistant_switch.cpp @@ -0,0 +1,59 @@ +#include "homeassistant_switch.h" +#include "esphome/components/api/api_server.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace homeassistant { + +static const char *const TAG = "homeassistant.switch"; + +using namespace esphome::switch_; + +void HomeassistantSwitch::setup() { + api::global_api_server->subscribe_home_assistant_state(this->entity_id_, nullopt, [this](const std::string &state) { + auto val = parse_on_off(state.c_str()); + switch (val) { + case PARSE_NONE: + case PARSE_TOGGLE: + ESP_LOGW(TAG, "Can't convert '%s' to binary state!", state.c_str()); + break; + case PARSE_ON: + case PARSE_OFF: + bool new_state = val == PARSE_ON; + ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), ONOFF(new_state)); + this->publish_state(new_state); + break; + } + }); +} + +void HomeassistantSwitch::dump_config() { + LOG_SWITCH("", "Homeassistant Switch", this); + ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str()); +} + +float HomeassistantSwitch::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } + +void HomeassistantSwitch::write_state(bool state) { + if (!api::global_api_server->is_connected()) { + ESP_LOGE(TAG, "No clients connected to API server"); + return; + } + + api::HomeassistantServiceResponse resp; + if (state) { + resp.service = "switch.turn_on"; + } else { + resp.service = "switch.turn_off"; + } + + api::HomeassistantServiceMap entity_id_kv; + entity_id_kv.key = "entity_id"; + entity_id_kv.value = this->entity_id_; + resp.data.push_back(entity_id_kv); + + api::global_api_server->send_homeassistant_service_call(resp); +} + +} // namespace homeassistant +} // namespace esphome diff --git a/esphome/components/homeassistant/switch/homeassistant_switch.h b/esphome/components/homeassistant/switch/homeassistant_switch.h new file mode 100644 index 0000000000..a4da257960 --- /dev/null +++ b/esphome/components/homeassistant/switch/homeassistant_switch.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace homeassistant { + +class HomeassistantSwitch : public switch_::Switch, public Component { + public: + void set_entity_id(const std::string &entity_id) { this->entity_id_ = entity_id; } + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + + protected: + void write_state(bool state) override; + std::string entity_id_; +}; + +} // namespace homeassistant +} // namespace esphome diff --git a/tests/components/homeassistant/common.yaml b/tests/components/homeassistant/common.yaml index 07a6e8090c..2b2805d06e 100644 --- a/tests/components/homeassistant/common.yaml +++ b/tests/components/homeassistant/common.yaml @@ -32,6 +32,11 @@ wifi: api: +switch: + - platform: homeassistant + entity_id: switch.my_cool_switch + id: my_cool_switch + binary_sensor: - platform: homeassistant entity_id: binary_sensor.hello_world From a5fdcb31fc56d928aa7a3b340829970f6ce0bbc0 Mon Sep 17 00:00:00 2001 From: Landon Rohatensky Date: Tue, 13 Aug 2024 19:04:12 -0700 Subject: [PATCH 0196/1052] [homeassistant] Native number entity import and control (#6455) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + .../homeassistant/number/__init__.py | 33 ++++++ .../number/homeassistant_number.cpp | 100 ++++++++++++++++++ .../number/homeassistant_number.h | 31 ++++++ tests/components/homeassistant/common.yaml | 5 + 5 files changed, 170 insertions(+) create mode 100644 esphome/components/homeassistant/number/__init__.py create mode 100644 esphome/components/homeassistant/number/homeassistant_number.cpp create mode 100644 esphome/components/homeassistant/number/homeassistant_number.h diff --git a/CODEOWNERS b/CODEOWNERS index 0c36cda527..3ea9c75ac2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -169,6 +169,7 @@ esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hm3301/* @freekode esphome/components/homeassistant/* @OttoWinter @esphome/core +esphome/components/homeassistant/number/* @landonr esphome/components/homeassistant/switch/* @Links2004 esphome/components/honeywell_hih_i2c/* @Benichou34 esphome/components/honeywellabp/* @RubyBailey diff --git a/esphome/components/homeassistant/number/__init__.py b/esphome/components/homeassistant/number/__init__.py new file mode 100644 index 0000000000..a6cc615a64 --- /dev/null +++ b/esphome/components/homeassistant/number/__init__.py @@ -0,0 +1,33 @@ +import esphome.codegen as cg +from esphome.components import number +import esphome.config_validation as cv + +from .. import ( + HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA, + homeassistant_ns, + setup_home_assistant_entity, +) + +CODEOWNERS = ["@landonr"] +DEPENDENCIES = ["api"] + +HomeassistantNumber = homeassistant_ns.class_( + "HomeassistantNumber", number.Number, cg.Component +) + +CONFIG_SCHEMA = ( + number.number_schema(HomeassistantNumber) + .extend(HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = await number.new_number( + config, + min_value=0, + max_value=0, + step=0, + ) + await cg.register_component(var, config) + setup_home_assistant_entity(var, config) diff --git a/esphome/components/homeassistant/number/homeassistant_number.cpp b/esphome/components/homeassistant/number/homeassistant_number.cpp new file mode 100644 index 0000000000..d3e285f4ac --- /dev/null +++ b/esphome/components/homeassistant/number/homeassistant_number.cpp @@ -0,0 +1,100 @@ +#include "homeassistant_number.h" + +#include "esphome/components/api/api_pb2.h" +#include "esphome/components/api/api_server.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace homeassistant { + +static const char *const TAG = "homeassistant.number"; + +void HomeassistantNumber::state_changed_(const std::string &state) { + auto number_value = parse_number(state); + if (!number_value.has_value()) { + ESP_LOGW(TAG, "'%s': Can't convert '%s' to number!", this->entity_id_.c_str(), state.c_str()); + this->publish_state(NAN); + return; + } + if (this->state == number_value.value()) { + return; + } + ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), state.c_str()); + this->publish_state(number_value.value()); +} + +void HomeassistantNumber::min_retrieved_(const std::string &min) { + auto min_value = parse_number(min); + if (!min_value.has_value()) { + ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_.c_str(), min.c_str()); + } + ESP_LOGD(TAG, "'%s': Min retrieved: %s", get_name().c_str(), min.c_str()); + this->traits.set_min_value(min_value.value()); +} + +void HomeassistantNumber::max_retrieved_(const std::string &max) { + auto max_value = parse_number(max); + if (!max_value.has_value()) { + ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_.c_str(), max.c_str()); + } + ESP_LOGD(TAG, "'%s': Max retrieved: %s", get_name().c_str(), max.c_str()); + this->traits.set_max_value(max_value.value()); +} + +void HomeassistantNumber::step_retrieved_(const std::string &step) { + auto step_value = parse_number(step); + if (!step_value.has_value()) { + ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_.c_str(), step.c_str()); + } + ESP_LOGD(TAG, "'%s': Step Retrieved %s", get_name().c_str(), step.c_str()); + this->traits.set_step(step_value.value()); +} + +void HomeassistantNumber::setup() { + api::global_api_server->subscribe_home_assistant_state( + this->entity_id_, nullopt, std::bind(&HomeassistantNumber::state_changed_, this, std::placeholders::_1)); + + api::global_api_server->get_home_assistant_state( + this->entity_id_, optional("min"), + std::bind(&HomeassistantNumber::min_retrieved_, this, std::placeholders::_1)); + api::global_api_server->get_home_assistant_state( + this->entity_id_, optional("max"), + std::bind(&HomeassistantNumber::max_retrieved_, this, std::placeholders::_1)); + api::global_api_server->get_home_assistant_state( + this->entity_id_, optional("step"), + std::bind(&HomeassistantNumber::step_retrieved_, this, std::placeholders::_1)); +} + +void HomeassistantNumber::dump_config() { + LOG_NUMBER("", "Homeassistant Number", this); + ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str()); +} + +float HomeassistantNumber::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } + +void HomeassistantNumber::control(float value) { + if (!api::global_api_server->is_connected()) { + ESP_LOGE(TAG, "No clients connected to API server"); + return; + } + + this->publish_state(value); + + api::HomeassistantServiceResponse resp; + resp.service = "number.set_value"; + + api::HomeassistantServiceMap entity_id; + entity_id.key = "entity_id"; + entity_id.value = this->entity_id_; + resp.data.push_back(entity_id); + + api::HomeassistantServiceMap entity_value; + entity_value.key = "value"; + entity_value.value = to_string(value); + resp.data.push_back(entity_value); + + api::global_api_server->send_homeassistant_service_call(resp); +} + +} // namespace homeassistant +} // namespace esphome diff --git a/esphome/components/homeassistant/number/homeassistant_number.h b/esphome/components/homeassistant/number/homeassistant_number.h new file mode 100644 index 0000000000..0860b4e91c --- /dev/null +++ b/esphome/components/homeassistant/number/homeassistant_number.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "esphome/components/number/number.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace homeassistant { + +class HomeassistantNumber : public number::Number, public Component { + public: + void set_entity_id(const std::string &entity_id) { this->entity_id_ = entity_id; } + + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + + protected: + void state_changed_(const std::string &state); + void min_retrieved_(const std::string &min); + void max_retrieved_(const std::string &max); + void step_retrieved_(const std::string &step); + + void control(float value) override; + + std::string entity_id_; +}; +} // namespace homeassistant +} // namespace esphome diff --git a/tests/components/homeassistant/common.yaml b/tests/components/homeassistant/common.yaml index 2b2805d06e..8c9a4ad75f 100644 --- a/tests/components/homeassistant/common.yaml +++ b/tests/components/homeassistant/common.yaml @@ -46,6 +46,11 @@ binary_sensor: attribute: world id: ha_hello_world_binary_attribute +number: + - platform: homeassistant + entity_id: number.hello_world + id: ha_hello_world_number + sensor: - platform: homeassistant entity_id: sensor.hello_world From a0eff08f39c69059d74929e6c91b8b61fb7ee51f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 14 Aug 2024 12:05:25 +1000 Subject: [PATCH 0197/1052] [lvgl] Rework events to avoid feedback loops (#7262) --- esphome/components/lvgl/automation.py | 4 +-- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/light/lvgl_light.h | 2 +- esphome/components/lvgl/lvcode.py | 6 ++++- esphome/components/lvgl/lvgl_esphome.cpp | 15 +++++++---- esphome/components/lvgl/lvgl_esphome.h | 5 +++- esphome/components/lvgl/number/__init__.py | 22 +++++++++++++--- esphome/components/lvgl/select/__init__.py | 13 ++++++++-- esphome/components/lvgl/sensor/__init__.py | 16 ++++++++++-- esphome/components/lvgl/switch/__init__.py | 6 +++-- esphome/components/lvgl/text/__init__.py | 17 ++++++++++--- .../components/lvgl/text_sensor/__init__.py | 4 ++- esphome/components/lvgl/trigger.py | 25 +++++++++++++++---- tests/components/lvgl/common.yaml | 1 + 14 files changed, 108 insertions(+), 29 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 556e286208..a39f589136 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -17,6 +17,7 @@ from .defines import ( from .lv_validation import lv_bool, lv_color, lv_image from .lvcode import ( LVGL_COMP_ARG, + UPDATE_EVENT, LambdaContext, LocalVariable, LvConditional, @@ -30,7 +31,6 @@ from .lvcode import ( ) from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA from .types import ( - LV_EVENT, LV_STATE, LvglAction, LvglCondition, @@ -64,7 +64,7 @@ async def update_to_code(config, action_id, template_arg, args): widget.type.w_type.value_property is not None and widget.type.w_type.value_property in config ): - lv.event_send(widget.obj, LV_EVENT.VALUE_CHANGED, nullptr) + lv.event_send(widget.obj, UPDATE_EVENT, nullptr) widgets = await get_widgets(config[CONF_ID]) return await action_to_code(widgets, do_update, action_id, template_arg, args) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index e48679996b..8f7a973722 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -470,6 +470,7 @@ CONF_TOP_LAYER = "top_layer" CONF_TOUCHSCREENS = "touchscreens" CONF_TRANSPARENCY_KEY = "transparency_key" CONF_THEME = "theme" +CONF_UPDATE_ON_RELEASE = "update_on_release" CONF_VISIBLE_ROW_COUNT = "visible_row_count" CONF_WIDGET = "widget" CONF_WIDGETS = "widgets" diff --git a/esphome/components/lvgl/light/lvgl_light.h b/esphome/components/lvgl/light/lvgl_light.h index 67372d89dd..50ae4c5327 100644 --- a/esphome/components/lvgl/light/lvgl_light.h +++ b/esphome/components/lvgl/light/lvgl_light.h @@ -38,7 +38,7 @@ class LVLight : public light::LightOutput { void set_value_(lv_color_t value) { lv_led_set_color(this->obj_, value); lv_led_on(this->obj_); - lv_event_send(this->obj_, lv_custom_event, nullptr); + lv_event_send(this->obj_, lv_api_event, nullptr); } lv_obj_t *obj_{}; optional initial_value_{}; diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index f54a032de2..6d7e364e5d 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -29,7 +29,11 @@ LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent) LVGL_COMP_ARG = [(LvglComponent.operator("ptr"), LVGL_COMP)] lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr") EVENT_ARG = [(lv_event_t_ptr, "ev")] -CUSTOM_EVENT = literal("lvgl::lv_custom_event") +# Two custom events; API_EVENT is fired when an entity is updated remotely by an API interaction; +# UPDATE_EVENT is fired when an entity is programmatically updated locally. +# VALUE_CHANGED is the event generated by LVGL when an entity's value changes through user interaction. +API_EVENT = literal("lvgl::lv_api_event") +UPDATE_EVENT = literal("lvgl::lv_update_event") def get_line_marks(value) -> list: diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 6f23c2421b..92f7a880c3 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -27,7 +27,8 @@ static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { area->y2++; } -lv_event_code_t lv_custom_event; // NOLINT +lv_event_code_t lv_api_event; // NOLINT +lv_event_code_t lv_update_event; // NOLINT void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); } void LvglComponent::set_paused(bool paused, bool show_snow) { this->paused_ = paused; @@ -40,15 +41,18 @@ void LvglComponent::set_paused(bool paused, bool show_snow) { } void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) { lv_obj_add_event_cb(obj, callback, event, this); - if (event == LV_EVENT_VALUE_CHANGED) { - lv_obj_add_event_cb(obj, callback, lv_custom_event, this); - } } void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2) { this->add_event_cb(obj, callback, event1); this->add_event_cb(obj, callback, event2); } +void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, + lv_event_code_t event2, lv_event_code_t event3) { + this->add_event_cb(obj, callback, event1); + this->add_event_cb(obj, callback, event2); + this->add_event_cb(obj, callback, event3); +} void LvglComponent::add_page(LvPageType *page) { this->pages_.push_back(page); page->setup(this->pages_.size() - 1); @@ -228,7 +232,8 @@ void LvglComponent::setup() { lv_log_register_print_cb(log_cb); #endif lv_init(); - lv_custom_event = static_cast(lv_event_register_id()); + lv_update_event = static_cast(lv_event_register_id()); + lv_api_event = static_cast(lv_event_register_id()); auto *display = this->displays_[0]; size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_; auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 1497e1004a..3a3d1aa6c5 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -38,7 +38,8 @@ namespace esphome { namespace lvgl { -extern lv_event_code_t lv_custom_event; // NOLINT +extern lv_event_code_t lv_api_event; // NOLINT +extern lv_event_code_t lv_update_event; // NOLINT #ifdef USE_LVGL_COLOR inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } #endif // USE_LVGL_COLOR @@ -133,6 +134,8 @@ class LvglComponent : public PollingComponent { void set_paused(bool paused, bool show_snow); void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event); void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2); + void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2, + lv_event_code_t event3); bool is_paused() const { return this->paused_; } void add_page(LvPageType *page); void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time); diff --git a/esphome/components/lvgl/number/__init__.py b/esphome/components/lvgl/number/__init__.py index 53aef2790d..6336bb0632 100644 --- a/esphome/components/lvgl/number/__init__.py +++ b/esphome/components/lvgl/number/__init__.py @@ -3,9 +3,17 @@ from esphome.components import number import esphome.config_validation as cv from esphome.cpp_generator import MockObj -from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET +from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_UPDATE_ON_RELEASE, CONF_WIDGET from ..lv_validation import animated -from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add +from ..lvcode import ( + API_EVENT, + EVENT_ARG, + UPDATE_EVENT, + LambdaContext, + LvContext, + lv, + lv_add, +) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber, lvgl_ns from ..widgets import get_widgets @@ -19,6 +27,7 @@ CONFIG_SCHEMA = ( { cv.Required(CONF_WIDGET): cv.use_id(LvNumber), cv.Optional(CONF_ANIMATED, default=True): animated, + cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean, } ) ) @@ -39,14 +48,19 @@ async def to_code(config): await widget.set_property( "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] ) - lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) + lv.event_send(widget.obj, API_EVENT, cg.nullptr) async with LambdaContext(EVENT_ARG) as event: event.add(var.publish_state(widget.get_value())) + event_code = ( + LV_EVENT.VALUE_CHANGED + if not config[CONF_UPDATE_ON_RELEASE] + else LV_EVENT.RELEASED + ) async with LvContext(paren): lv_add(var.set_control_lambda(await control.get_lambda())) lv_add( paren.add_event_cb( - widget.obj, await event.get_lambda(), LV_EVENT.VALUE_CHANGED + widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code ) ) lv_add(var.publish_state(widget.get_value())) diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py index 34a70a23f7..b55bde13bc 100644 --- a/esphome/components/lvgl/select/__init__.py +++ b/esphome/components/lvgl/select/__init__.py @@ -4,7 +4,15 @@ import esphome.config_validation as cv from esphome.const import CONF_OPTIONS from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET -from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add +from ..lvcode import ( + API_EVENT, + EVENT_ARG, + UPDATE_EVENT, + LambdaContext, + LvContext, + lv, + lv_add, +) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvSelect, lvgl_ns from ..widgets import get_widgets @@ -33,7 +41,7 @@ async def to_code(config): pub_ctx.add(selector.publish_index(widget.get_value())) async with LambdaContext([(cg.uint16, "v")]) as control: await widget.set_property("selected", "v", animated=config[CONF_ANIMATED]) - lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) + lv.event_send(widget.obj, API_EVENT, cg.nullptr) async with LvContext(paren) as ctx: lv_add(selector.set_control_lambda(await control.get_lambda())) ctx.add( @@ -41,6 +49,7 @@ async def to_code(config): widget.obj, await pub_ctx.get_lambda(), LV_EVENT.VALUE_CHANGED, + UPDATE_EVENT, ) ) lv_add(selector.publish_index(widget.get_value())) diff --git a/esphome/components/lvgl/sensor/__init__.py b/esphome/components/lvgl/sensor/__init__.py index 6e495eb685..82e21d5e95 100644 --- a/esphome/components/lvgl/sensor/__init__.py +++ b/esphome/components/lvgl/sensor/__init__.py @@ -3,7 +3,15 @@ from esphome.components.sensor import Sensor, new_sensor, sensor_schema import esphome.config_validation as cv from ..defines import CONF_LVGL_ID, CONF_WIDGET -from ..lvcode import EVENT_ARG, LVGL_COMP_ARG, LambdaContext, LvContext, lv_add +from ..lvcode import ( + API_EVENT, + EVENT_ARG, + LVGL_COMP_ARG, + UPDATE_EVENT, + LambdaContext, + LvContext, + lv_add, +) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber from ..widgets import Widget, get_widgets @@ -30,6 +38,10 @@ async def to_code(config): async with LvContext(paren, LVGL_COMP_ARG): lv_add( paren.add_event_cb( - widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED + widget.obj, + await lamb.get_lambda(), + LV_EVENT.VALUE_CHANGED, + API_EVENT, + UPDATE_EVENT, ) ) diff --git a/esphome/components/lvgl/switch/__init__.py b/esphome/components/lvgl/switch/__init__.py index 831fa9308b..957fce17ff 100644 --- a/esphome/components/lvgl/switch/__init__.py +++ b/esphome/components/lvgl/switch/__init__.py @@ -5,8 +5,9 @@ from esphome.cpp_generator import MockObj from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..lvcode import ( - CUSTOM_EVENT, + API_EVENT, EVENT_ARG, + UPDATE_EVENT, LambdaContext, LvConditional, LvContext, @@ -41,7 +42,7 @@ async def to_code(config): widget.add_state(LV_STATE.CHECKED) cond.else_() widget.clear_state(LV_STATE.CHECKED) - lv.event_send(widget.obj, CUSTOM_EVENT, cg.nullptr) + lv.event_send(widget.obj, API_EVENT, cg.nullptr) async with LvContext(paren) as ctx: lv_add(switch.set_control_lambda(await control.get_lambda())) ctx.add( @@ -49,6 +50,7 @@ async def to_code(config): widget.obj, await checked_ctx.get_lambda(), LV_EVENT.VALUE_CHANGED, + UPDATE_EVENT, ) ) lv_add(switch.publish_state(widget.get_value())) diff --git a/esphome/components/lvgl/text/__init__.py b/esphome/components/lvgl/text/__init__.py index 55f1b2b3fc..9ee494d8a0 100644 --- a/esphome/components/lvgl/text/__init__.py +++ b/esphome/components/lvgl/text/__init__.py @@ -4,7 +4,15 @@ from esphome.components.text import new_text import esphome.config_validation as cv from ..defines import CONF_LVGL_ID, CONF_WIDGET -from ..lvcode import CUSTOM_EVENT, EVENT_ARG, LambdaContext, LvContext, lv, lv_add +from ..lvcode import ( + API_EVENT, + EVENT_ARG, + UPDATE_EVENT, + LambdaContext, + LvContext, + lv, + lv_add, +) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvText, lvgl_ns from ..widgets import get_widgets @@ -26,14 +34,17 @@ async def to_code(config): widget = widget[0] async with LambdaContext([(cg.std_string, "text_value")]) as control: await widget.set_property("text", "text_value.c_str())") - lv.event_send(widget.obj, CUSTOM_EVENT, None) + lv.event_send(widget.obj, API_EVENT, None) async with LambdaContext(EVENT_ARG) as lamb: lv_add(textvar.publish_state(widget.get_value())) async with LvContext(paren): widget.var.set_control_lambda(await control.get_lambda()) lv_add( paren.add_event_cb( - widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED + widget.obj, + await lamb.get_lambda(), + LV_EVENT.VALUE_CHANGED, + UPDATE_EVENT, ) ) lv_add(textvar.publish_state(widget.get_value())) diff --git a/esphome/components/lvgl/text_sensor/__init__.py b/esphome/components/lvgl/text_sensor/__init__.py index c0f0bc36a8..cab715dce0 100644 --- a/esphome/components/lvgl/text_sensor/__init__.py +++ b/esphome/components/lvgl/text_sensor/__init__.py @@ -7,7 +7,7 @@ from esphome.components.text_sensor import ( import esphome.config_validation as cv from ..defines import CONF_LVGL_ID, CONF_WIDGET -from ..lvcode import EVENT_ARG, LambdaContext, LvContext +from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvText from ..widgets import get_widgets @@ -36,5 +36,7 @@ async def to_code(config): widget.obj, await pressed_ctx.get_lambda(), LV_EVENT.VALUE_CHANGED, + API_EVENT, + UPDATE_EVENT, ) ) diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index df87be718b..ba93aabb2d 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -11,7 +11,15 @@ from .defines import ( LV_EVENT_TRIGGERS, literal, ) -from .lvcode import EVENT_ARG, LambdaContext, LvConditional, lv, lv_add +from .lvcode import ( + API_EVENT, + EVENT_ARG, + UPDATE_EVENT, + LambdaContext, + LvConditional, + lv, + lv_add, +) from .types import LV_EVENT from .widgets import widget_map @@ -34,9 +42,16 @@ async def generate_triggers(lv_component): conf = conf[0] w.add_flag("LV_OBJ_FLAG_CLICKABLE") event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()]) - await add_trigger(conf, event, lv_component, w) + await add_trigger(conf, lv_component, w, event) for conf in w.config.get(CONF_ON_VALUE, ()): - await add_trigger(conf, LV_EVENT.VALUE_CHANGED, lv_component, w) + await add_trigger( + conf, + lv_component, + w, + LV_EVENT.VALUE_CHANGED, + API_EVENT, + UPDATE_EVENT, + ) # Generate align to directives while we're here if align_to := w.config.get(CONF_ALIGN_TO): @@ -47,7 +62,7 @@ async def generate_triggers(lv_component): lv.obj_align_to(w.obj, target, align, x, y) -async def add_trigger(conf, event, lv_component, w): +async def add_trigger(conf, lv_component, w, *events): tid = conf[CONF_TRIGGER_ID] trigger = cg.new_Pvariable(tid) args = w.get_args() @@ -56,4 +71,4 @@ async def add_trigger(conf, event, lv_component, w): async with LambdaContext(EVENT_ARG, where=tid) as context: with LvConditional(w.is_selected()): lv_add(trigger.trigger(value)) - lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), event)) + lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events)) diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 35d924d939..002c7a118d 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -75,6 +75,7 @@ number: - platform: lvgl widget: slider_id name: LVGL Slider + update_on_release: true - platform: lvgl widget: lv_arc id: lvgl_arc_number From bec2d42c793c59b18e15a0fbb5cbe22761858290 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 14 Aug 2024 12:06:13 +1000 Subject: [PATCH 0198/1052] Add `color_filter_opa` style property (#7276) --- esphome/components/lvgl/schemas.py | 1 + tests/components/lvgl/lvgl-package.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index e4b1c3f8fa..f1c7ff4df6 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -103,6 +103,7 @@ STYLE_PROPS = { ).several_of, "border_width": cv.positive_int, "clip_corner": lvalid.lv_bool, + "color_filter_opa": lvalid.opacity, "height": lvalid.size, "image_recolor": lvalid.lv_color, "image_recolor_opa": lvalid.opacity, diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 09ff9c9d39..54022354f5 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -101,6 +101,7 @@ lvgl: border_side: [bottom, left] border_width: 4 clip_corner: false + color_filter_opa: transp height: 50% image_recolor: light_blue image_recolor_opa: cover From 56e05998efb14b7e91fea7e4ef3cfe76a7128c61 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 14 Aug 2024 04:08:10 +0200 Subject: [PATCH 0199/1052] [code-quality] fix clang-tidy wake_on_lan (#7275) --- esphome/components/wake_on_lan/wake_on_lan.cpp | 2 ++ esphome/components/wake_on_lan/wake_on_lan.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/wake_on_lan/wake_on_lan.cpp b/esphome/components/wake_on_lan/wake_on_lan.cpp index 080e1bbac8..d18cdf89c8 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.cpp +++ b/esphome/components/wake_on_lan/wake_on_lan.cpp @@ -1,4 +1,5 @@ #include "wake_on_lan.h" +#ifdef USE_NETWORK #include "esphome/core/log.h" #include "esphome/components/network/ip_address.h" #include "esphome/components/network/util.h" @@ -85,3 +86,4 @@ void WakeOnLanButton::setup() { } // namespace wake_on_lan } // namespace esphome +#endif diff --git a/esphome/components/wake_on_lan/wake_on_lan.h b/esphome/components/wake_on_lan/wake_on_lan.h index 42cb3a9268..f516c4d669 100644 --- a/esphome/components/wake_on_lan/wake_on_lan.h +++ b/esphome/components/wake_on_lan/wake_on_lan.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_NETWORK #include "esphome/components/button/button.h" #include "esphome/core/component.h" #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) @@ -32,3 +33,4 @@ class WakeOnLanButton : public button::Button, public Component { } // namespace wake_on_lan } // namespace esphome +#endif From 4cb174585c0521801d6e8ffda189d37b5a40aa30 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 14 Aug 2024 04:14:29 +0200 Subject: [PATCH 0200/1052] [code-quality] fix readability-braces-around-statements (#7273) --- .../binary/light/binary_light_output.h | 5 +- esphome/components/demo/demo_sensor.h | 5 +- .../light/addressable_light_effect.h | 20 +++--- esphome/components/lvgl/number/lvgl_number.h | 5 +- esphome/components/lvgl/switch/lvgl_switch.h | 5 +- esphome/components/lvgl/text/lvgl_text.h | 5 +- esphome/components/rgbct/rgbct_light_output.h | 5 +- esphome/components/rgbw/rgbw_light_output.h | 5 +- esphome/components/rgbww/rgbww_light_output.h | 5 +- .../components/spi_led_strip/spi_led_strip.h | 5 +- esphome/core/application.h | 63 ++++++++++++------- esphome/core/base_automation.h | 5 +- esphome/core/color.h | 40 +++++++----- 13 files changed, 108 insertions(+), 65 deletions(-) diff --git a/esphome/components/binary/light/binary_light_output.h b/esphome/components/binary/light/binary_light_output.h index 86c83aff5c..8346a82cf0 100644 --- a/esphome/components/binary/light/binary_light_output.h +++ b/esphome/components/binary/light/binary_light_output.h @@ -18,10 +18,11 @@ class BinaryLightOutput : public light::LightOutput { void write_state(light::LightState *state) override { bool binary; state->current_values_as_binary(&binary); - if (binary) + if (binary) { this->output_->turn_on(); - else + } else { this->output_->turn_off(); + } } protected: diff --git a/esphome/components/demo/demo_sensor.h b/esphome/components/demo/demo_sensor.h index b4afa03e11..d965d987de 100644 --- a/esphome/components/demo/demo_sensor.h +++ b/esphome/components/demo/demo_sensor.h @@ -16,10 +16,11 @@ class DemoSensor : public sensor::Sensor, public PollingComponent { float base = std::isnan(this->state) ? 0.0f : this->state; this->publish_state(base + val * 10); } else { - if (val < 0.1) + if (val < 0.1) { this->publish_state(NAN); - else + } else { this->publish_state(val * 100); + } } } }; diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index 73083a58b7..d622ec0375 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -114,10 +114,11 @@ class AddressableColorWipeEffect : public AddressableLightEffect { if (now - this->last_add_ < this->add_led_interval_) return; this->last_add_ = now; - if (this->reverse_) + if (this->reverse_) { it.shift_left(1); - else + } else { it.shift_right(1); + } const AddressableColorWipeEffectColor &color = this->colors_[this->at_color_]; Color esp_color = Color(color.r, color.g, color.b, color.w); if (color.gradient) { @@ -127,10 +128,11 @@ class AddressableColorWipeEffect : public AddressableLightEffect { uint8_t gradient = 255 * ((float) this->leds_added_ / color.num_leds); esp_color = esp_color.gradient(next_esp_color, gradient); } - if (this->reverse_) + if (this->reverse_) { it[-1] = esp_color; - else + } else { it[0] = esp_color; + } if (++this->leds_added_ >= color.num_leds) { this->leds_added_ = 0; this->at_color_ = (this->at_color_ + 1) % this->colors_.size(); @@ -207,10 +209,11 @@ class AddressableTwinkleEffect : public AddressableLightEffect { const uint8_t sine = half_sin8(view.get_effect_data()); view = current_color * sine; const uint8_t new_pos = view.get_effect_data() + pos_add; - if (new_pos < view.get_effect_data()) + if (new_pos < view.get_effect_data()) { view.set_effect_data(0); - else + } else { view.set_effect_data(new_pos); + } } else { view = Color::BLACK; } @@ -254,10 +257,11 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect { view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine); } const uint8_t new_x = x + pos_add; - if (new_x > 0b11111) + if (new_x > 0b11111) { view.set_effect_data(0); - else + } else { view.set_effect_data((new_x << 3) | color); + } } else { view = Color(0, 0, 0, 0); } diff --git a/esphome/components/lvgl/number/lvgl_number.h b/esphome/components/lvgl/number/lvgl_number.h index 659fc615c9..a70c9eab9c 100644 --- a/esphome/components/lvgl/number/lvgl_number.h +++ b/esphome/components/lvgl/number/lvgl_number.h @@ -20,10 +20,11 @@ class LVGLNumber : public number::Number { protected: void control(float value) override { - if (this->control_lambda_ != nullptr) + if (this->control_lambda_ != nullptr) { this->control_lambda_(value); - else + } else { this->initial_state_ = value; + } } std::function control_lambda_{}; optional initial_state_{}; diff --git a/esphome/components/lvgl/switch/lvgl_switch.h b/esphome/components/lvgl/switch/lvgl_switch.h index 67be11faba..dbc885219b 100644 --- a/esphome/components/lvgl/switch/lvgl_switch.h +++ b/esphome/components/lvgl/switch/lvgl_switch.h @@ -20,10 +20,11 @@ class LVGLSwitch : public switch_::Switch { protected: void write_state(bool value) override { - if (this->state_lambda_ != nullptr) + if (this->state_lambda_ != nullptr) { this->state_lambda_(value); - else + } else { this->initial_state_ = value; + } } std::function state_lambda_{}; optional initial_state_{}; diff --git a/esphome/components/lvgl/text/lvgl_text.h b/esphome/components/lvgl/text/lvgl_text.h index 4bd5b76744..d3513c3697 100644 --- a/esphome/components/lvgl/text/lvgl_text.h +++ b/esphome/components/lvgl/text/lvgl_text.h @@ -20,10 +20,11 @@ class LVGLText : public text::Text { protected: void control(const std::string &value) override { - if (this->control_lambda_ != nullptr) + if (this->control_lambda_ != nullptr) { this->control_lambda_(value); - else + } else { this->initial_state_ = value; + } } std::function control_lambda_{}; optional initial_state_{}; diff --git a/esphome/components/rgbct/rgbct_light_output.h b/esphome/components/rgbct/rgbct_light_output.h index 9257d67cd1..9e23f783ae 100644 --- a/esphome/components/rgbct/rgbct_light_output.h +++ b/esphome/components/rgbct/rgbct_light_output.h @@ -23,10 +23,11 @@ class RGBCTLightOutput : public light::LightOutput { light::LightTraits get_traits() override { auto traits = light::LightTraits(); - if (this->color_interlock_) + if (this->color_interlock_) { traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLOR_TEMPERATURE}); - else + } else { traits.set_supported_color_modes({light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE}); + } traits.set_min_mireds(this->cold_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_); return traits; diff --git a/esphome/components/rgbw/rgbw_light_output.h b/esphome/components/rgbw/rgbw_light_output.h index 0f55775608..a2ab17b75d 100644 --- a/esphome/components/rgbw/rgbw_light_output.h +++ b/esphome/components/rgbw/rgbw_light_output.h @@ -16,10 +16,11 @@ class RGBWLightOutput : public light::LightOutput { void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); - if (this->color_interlock_) + if (this->color_interlock_) { traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::WHITE}); - else + } else { traits.set_supported_color_modes({light::ColorMode::RGB_WHITE}); + } return traits; } void write_state(light::LightState *state) override { diff --git a/esphome/components/rgbww/rgbww_light_output.h b/esphome/components/rgbww/rgbww_light_output.h index 5a86b88595..9687360059 100644 --- a/esphome/components/rgbww/rgbww_light_output.h +++ b/esphome/components/rgbww/rgbww_light_output.h @@ -20,10 +20,11 @@ class RGBWWLightOutput : public light::LightOutput { void set_color_interlock(bool color_interlock) { color_interlock_ = color_interlock; } light::LightTraits get_traits() override { auto traits = light::LightTraits(); - if (this->color_interlock_) + if (this->color_interlock_) { traits.set_supported_color_modes({light::ColorMode::RGB, light::ColorMode::COLD_WARM_WHITE}); - else + } else { traits.set_supported_color_modes({light::ColorMode::RGB_COLD_WARM_WHITE}); + } traits.set_min_mireds(this->cold_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_); return traits; diff --git a/esphome/components/spi_led_strip/spi_led_strip.h b/esphome/components/spi_led_strip/spi_led_strip.h index 8b713378ec..1b317cdd69 100644 --- a/esphome/components/spi_led_strip/spi_led_strip.h +++ b/esphome/components/spi_led_strip/spi_led_strip.h @@ -46,10 +46,11 @@ class SpiLedStrip : public light::AddressableLight, void dump_config() override { esph_log_config(TAG, "SPI LED Strip:"); esph_log_config(TAG, " LEDs: %d", this->num_leds_); - if (this->data_rate_ >= spi::DATA_RATE_1MHZ) + if (this->data_rate_ >= spi::DATA_RATE_1MHZ) { esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000)); - else + } else { esph_log_config(TAG, " Data rate: %ukHz", (unsigned) (this->data_rate_ / 1000)); + } } void write_state(light::LightState *state) override { diff --git a/esphome/core/application.h b/esphome/core/application.h index 2697357456..462beb1f25 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -246,162 +246,180 @@ class Application { #ifdef USE_BINARY_SENSOR const std::vector &get_binary_sensors() { return this->binary_sensors_; } binary_sensor::BinarySensor *get_binary_sensor_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->binary_sensors_) + for (auto *obj : this->binary_sensors_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_SWITCH const std::vector &get_switches() { return this->switches_; } switch_::Switch *get_switch_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->switches_) + for (auto *obj : this->switches_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_BUTTON const std::vector &get_buttons() { return this->buttons_; } button::Button *get_button_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->buttons_) + for (auto *obj : this->buttons_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_SENSOR const std::vector &get_sensors() { return this->sensors_; } sensor::Sensor *get_sensor_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->sensors_) + for (auto *obj : this->sensors_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_TEXT_SENSOR const std::vector &get_text_sensors() { return this->text_sensors_; } text_sensor::TextSensor *get_text_sensor_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->text_sensors_) + for (auto *obj : this->text_sensors_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_FAN const std::vector &get_fans() { return this->fans_; } fan::Fan *get_fan_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->fans_) + for (auto *obj : this->fans_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_COVER const std::vector &get_covers() { return this->covers_; } cover::Cover *get_cover_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->covers_) + for (auto *obj : this->covers_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_LIGHT const std::vector &get_lights() { return this->lights_; } light::LightState *get_light_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->lights_) + for (auto *obj : this->lights_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_CLIMATE const std::vector &get_climates() { return this->climates_; } climate::Climate *get_climate_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->climates_) + for (auto *obj : this->climates_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_NUMBER const std::vector &get_numbers() { return this->numbers_; } number::Number *get_number_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->numbers_) + for (auto *obj : this->numbers_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_DATETIME_DATE const std::vector &get_dates() { return this->dates_; } datetime::DateEntity *get_date_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->dates_) + for (auto *obj : this->dates_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_DATETIME_TIME const std::vector &get_times() { return this->times_; } datetime::TimeEntity *get_time_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->times_) + for (auto *obj : this->times_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_DATETIME_DATETIME const std::vector &get_datetimes() { return this->datetimes_; } datetime::DateTimeEntity *get_datetime_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->datetimes_) + for (auto *obj : this->datetimes_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_TEXT const std::vector &get_texts() { return this->texts_; } text::Text *get_text_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->texts_) + for (auto *obj : this->texts_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_SELECT const std::vector &get_selects() { return this->selects_; } select::Select *get_select_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->selects_) + for (auto *obj : this->selects_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_LOCK const std::vector &get_locks() { return this->locks_; } lock::Lock *get_lock_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->locks_) + for (auto *obj : this->locks_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_VALVE const std::vector &get_valves() { return this->valves_; } valve::Valve *get_valve_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->valves_) + for (auto *obj : this->valves_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif #ifdef USE_MEDIA_PLAYER const std::vector &get_media_players() { return this->media_players_; } media_player::MediaPlayer *get_media_player_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->media_players_) + for (auto *obj : this->media_players_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif @@ -411,9 +429,10 @@ class Application { return this->alarm_control_panels_; } alarm_control_panel::AlarmControlPanel *get_alarm_control_panel_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->alarm_control_panels_) + for (auto *obj : this->alarm_control_panels_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif @@ -421,9 +440,10 @@ class Application { #ifdef USE_EVENT const std::vector &get_events() { return this->events_; } event::Event *get_event_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->events_) + for (auto *obj : this->events_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif @@ -431,9 +451,10 @@ class Application { #ifdef USE_UPDATE const std::vector &get_updates() { return this->updates_; } update::UpdateEntity *get_update_by_key(uint32_t key, bool include_internal = false) { - for (auto *obj : this->updates_) + for (auto *obj : this->updates_) { if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) return obj; + } return nullptr; } #endif diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index 1bf0efb9a4..dcf7da2f21 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -278,10 +278,11 @@ template class RepeatAction : public Action { this->then_.add_actions(actions); this->then_.add_action(new LambdaAction([this](uint32_t iteration, Ts... x) { iteration++; - if (iteration >= this->count_.value(x...)) + if (iteration >= this->count_.value(x...)) { this->play_next_tuple_(this->var_); - else + } else { this->then_.play(iteration, x...); + } })); } diff --git a/esphome/core/color.h b/esphome/core/color.h index 8965d9fc83..1c43fd9d3e 100644 --- a/esphome/core/color.h +++ b/esphome/core/color.h @@ -85,22 +85,26 @@ struct Color { } inline Color operator+(const Color &add) const ESPHOME_ALWAYS_INLINE { Color ret; - if (uint8_t(add.r + this->r) < this->r) + if (uint8_t(add.r + this->r) < this->r) { ret.r = 255; - else + } else { ret.r = this->r + add.r; - if (uint8_t(add.g + this->g) < this->g) + } + if (uint8_t(add.g + this->g) < this->g) { ret.g = 255; - else + } else { ret.g = this->g + add.g; - if (uint8_t(add.b + this->b) < this->b) + } + if (uint8_t(add.b + this->b) < this->b) { ret.b = 255; - else + } else { ret.b = this->b + add.b; - if (uint8_t(add.w + this->w) < this->w) + } + if (uint8_t(add.w + this->w) < this->w) { ret.w = 255; - else + } else { ret.w = this->w + add.w; + } return ret; } inline Color &operator+=(const Color &add) ESPHOME_ALWAYS_INLINE { return *this = (*this) + add; } @@ -108,22 +112,26 @@ struct Color { inline Color &operator+=(uint8_t add) ESPHOME_ALWAYS_INLINE { return *this = (*this) + add; } inline Color operator-(const Color &subtract) const ESPHOME_ALWAYS_INLINE { Color ret; - if (subtract.r > this->r) + if (subtract.r > this->r) { ret.r = 0; - else + } else { ret.r = this->r - subtract.r; - if (subtract.g > this->g) + } + if (subtract.g > this->g) { ret.g = 0; - else + } else { ret.g = this->g - subtract.g; - if (subtract.b > this->b) + } + if (subtract.b > this->b) { ret.b = 0; - else + } else { ret.b = this->b - subtract.b; - if (subtract.w > this->w) + } + if (subtract.w > this->w) { ret.w = 0; - else + } else { ret.w = this->w - subtract.w; + } return ret; } inline Color &operator-=(const Color &subtract) ESPHOME_ALWAYS_INLINE { return *this = (*this) - subtract; } From 8756b41b6344a61a49a3b4cdb66437b1b6064036 Mon Sep 17 00:00:00 2001 From: Olivier ARCHER Date: Wed, 14 Aug 2024 04:19:46 +0200 Subject: [PATCH 0201/1052] [mqtt] fix missing initializer in MQTTClientComponent::disable_discovery (#7271) --- esphome/components/mqtt/mqtt_client.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 876367aaea..c19b24c0cf 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -632,6 +632,7 @@ void MQTTClientComponent::disable_discovery() { this->discovery_info_ = MQTTDiscoveryInfo{ .prefix = "", .retain = false, + .discover_ip = false, .clean = false, .unique_id_generator = MQTT_LEGACY_UNIQUE_ID_GENERATOR, .object_id_generator = MQTT_NONE_OBJECT_ID_GENERATOR, From b2b23f2a4f2fbbba0ded4b77104df871a2f27260 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 14 Aug 2024 04:21:19 +0200 Subject: [PATCH 0202/1052] [code-quality] fix readability-named-parameter (#7272) --- esphome/core/automation.h | 8 +++++--- esphome/core/optional.h | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/esphome/core/automation.h b/esphome/core/automation.h index 5a0a17ea1a..e77e453431 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -82,7 +82,7 @@ template class Condition { } protected: - template bool check_tuple_(const std::tuple &tuple, seq) { + template bool check_tuple_(const std::tuple &tuple, seq /*unused*/) { return this->check(std::get(tuple)...); } }; @@ -156,7 +156,7 @@ template class Action { } } } - template void play_next_tuple_(const std::tuple &tuple, seq) { + template void play_next_tuple_(const std::tuple &tuple, seq /*unused*/) { this->play_next_(std::get(tuple)...); } void play_next_tuple_(const std::tuple &tuple) { @@ -223,7 +223,9 @@ template class ActionList { } protected: - template void play_tuple_(const std::tuple &tuple, seq) { this->play(std::get(tuple)...); } + template void play_tuple_(const std::tuple &tuple, seq /*unused*/) { + this->play(std::get(tuple)...); + } Action *actions_begin_{nullptr}; Action *actions_end_{nullptr}; diff --git a/esphome/core/optional.h b/esphome/core/optional.h index 770b77081e..1e28ef1354 100644 --- a/esphome/core/optional.h +++ b/esphome/core/optional.h @@ -24,7 +24,7 @@ namespace esphome { struct nullopt_t { // NOLINT struct init {}; // NOLINT - nullopt_t(init) {} + nullopt_t(init /*unused*/) {} }; // extra parenthesis to prevent the most vexing parse: @@ -42,13 +42,13 @@ template class optional { // NOLINT optional() {} - optional(nullopt_t) {} + optional(nullopt_t /*unused*/) {} optional(T const &arg) : has_value_(true), value_(arg) {} // NOLINT template optional(optional const &other) : has_value_(other.has_value()), value_(other.value()) {} - optional &operator=(nullopt_t) { + optional &operator=(nullopt_t /*unused*/) { reset(); return *this; } @@ -130,29 +130,29 @@ template inline bool operator>=(optional const &x, op // Comparison with nullopt -template inline bool operator==(optional const &x, nullopt_t) { return (!x); } +template inline bool operator==(optional const &x, nullopt_t /*unused*/) { return (!x); } -template inline bool operator==(nullopt_t, optional const &x) { return (!x); } +template inline bool operator==(nullopt_t /*unused*/, optional const &x) { return (!x); } -template inline bool operator!=(optional const &x, nullopt_t) { return bool(x); } +template inline bool operator!=(optional const &x, nullopt_t /*unused*/) { return bool(x); } -template inline bool operator!=(nullopt_t, optional const &x) { return bool(x); } +template inline bool operator!=(nullopt_t /*unused*/, optional const &x) { return bool(x); } -template inline bool operator<(optional const &, nullopt_t) { return false; } +template inline bool operator<(optional const & /*unused*/, nullopt_t /*unused*/) { return false; } -template inline bool operator<(nullopt_t, optional const &x) { return bool(x); } +template inline bool operator<(nullopt_t /*unused*/, optional const &x) { return bool(x); } -template inline bool operator<=(optional const &x, nullopt_t) { return (!x); } +template inline bool operator<=(optional const &x, nullopt_t /*unused*/) { return (!x); } -template inline bool operator<=(nullopt_t, optional const &) { return true; } +template inline bool operator<=(nullopt_t /*unused*/, optional const & /*unused*/) { return true; } -template inline bool operator>(optional const &x, nullopt_t) { return bool(x); } +template inline bool operator>(optional const &x, nullopt_t /*unused*/) { return bool(x); } -template inline bool operator>(nullopt_t, optional const &) { return false; } +template inline bool operator>(nullopt_t /*unused*/, optional const & /*unused*/) { return false; } -template inline bool operator>=(optional const &, nullopt_t) { return true; } +template inline bool operator>=(optional const & /*unused*/, nullopt_t /*unused*/) { return true; } -template inline bool operator>=(nullopt_t, optional const &x) { return (!x); } +template inline bool operator>=(nullopt_t /*unused*/, optional const &x) { return (!x); } // Comparison with T From 8f093823672a49d63d9276c4072d2a3189d194d6 Mon Sep 17 00:00:00 2001 From: Philippe Wechsler <29612400+MadMonkey87@users.noreply.github.com> Date: Wed, 14 Aug 2024 04:25:45 +0200 Subject: [PATCH 0203/1052] support illuminance for airthings wave plus device (#5203) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../airthings_wave_plus/airthings_wave_plus.cpp | 7 +++++-- .../airthings_wave_plus/airthings_wave_plus.h | 2 ++ esphome/components/airthings_wave_plus/sensor.py | 12 ++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp index a32128e992..8c8c514fdb 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.cpp @@ -14,8 +14,6 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) { ESP_LOGD(TAG, "version = %d", value->version); if (value->version == 1) { - ESP_LOGD(TAG, "ambient light = %d", value->ambientLight); - if (this->humidity_sensor_ != nullptr) { this->humidity_sensor_->publish_state(value->humidity / 2.0f); } @@ -43,6 +41,10 @@ void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) { if ((this->tvoc_sensor_ != nullptr) && this->is_valid_voc_value_(value->voc)) { this->tvoc_sensor_->publish_state(value->voc); } + + if (this->illuminance_sensor_ != nullptr) { + this->illuminance_sensor_->publish_state(value->ambientLight); + } } else { ESP_LOGE(TAG, "Invalid payload version (%d != 1, newer version or not a Wave Plus?)", value->version); } @@ -68,6 +70,7 @@ void AirthingsWavePlus::dump_config() { LOG_SENSOR(" ", "Radon", this->radon_sensor_); LOG_SENSOR(" ", "Radon Long Term", this->radon_long_term_sensor_); LOG_SENSOR(" ", "CO2", this->co2_sensor_); + LOG_SENSOR(" ", "Illuminance", this->illuminance_sensor_); } AirthingsWavePlus::AirthingsWavePlus() { diff --git a/esphome/components/airthings_wave_plus/airthings_wave_plus.h b/esphome/components/airthings_wave_plus/airthings_wave_plus.h index 23c8cbb166..bd7a40ef8b 100644 --- a/esphome/components/airthings_wave_plus/airthings_wave_plus.h +++ b/esphome/components/airthings_wave_plus/airthings_wave_plus.h @@ -22,6 +22,7 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase { void set_radon(sensor::Sensor *radon) { radon_sensor_ = radon; } void set_radon_long_term(sensor::Sensor *radon_long_term) { radon_long_term_sensor_ = radon_long_term; } void set_co2(sensor::Sensor *co2) { co2_sensor_ = co2; } + void set_illuminance(sensor::Sensor *illuminance) { illuminance_sensor_ = illuminance; } protected: bool is_valid_radon_value_(uint16_t radon); @@ -32,6 +33,7 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase { sensor::Sensor *radon_sensor_{nullptr}; sensor::Sensor *radon_long_term_sensor_{nullptr}; sensor::Sensor *co2_sensor_{nullptr}; + sensor::Sensor *illuminance_sensor_{nullptr}; struct WavePlusReadings { uint8_t version; diff --git a/esphome/components/airthings_wave_plus/sensor.py b/esphome/components/airthings_wave_plus/sensor.py index 643a2bfb68..d28c7e2abc 100644 --- a/esphome/components/airthings_wave_plus/sensor.py +++ b/esphome/components/airthings_wave_plus/sensor.py @@ -12,6 +12,9 @@ from esphome.const import ( CONF_CO2, UNIT_BECQUEREL_PER_CUBIC_METER, UNIT_PARTS_PER_MILLION, + CONF_ILLUMINANCE, + UNIT_LUX, + DEVICE_CLASS_ILLUMINANCE, ) DEPENDENCIES = airthings_wave_base.DEPENDENCIES @@ -45,6 +48,12 @@ CONFIG_SCHEMA = airthings_wave_base.BASE_SCHEMA.extend( device_class=DEVICE_CLASS_CARBON_DIOXIDE, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema( + unit_of_measurement=UNIT_LUX, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), } ) @@ -62,3 +71,6 @@ async def to_code(config): if config_co2 := config.get(CONF_CO2): sens = await sensor.new_sensor(config_co2) cg.add(var.set_co2(sens)) + if config_illuminance := config.get(CONF_ILLUMINANCE): + sens = await sensor.new_sensor(config_illuminance) + cg.add(var.set_illuminance(sens)) From d6f130e35ab2ecd0d2c09ef31459c205852aa751 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 13 Aug 2024 23:40:07 -0400 Subject: [PATCH 0204/1052] [micro_wake_word] Bump ESPMicroSpeechFeatures version to 1.1.0 (#7249) --- .../components/micro_wake_word/__init__.py | 47 +++++++++---------- platformio.ini | 2 +- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index c2faca25f4..cd45f75b01 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -1,39 +1,34 @@ -import logging - -import json import hashlib -from urllib.parse import urljoin +import json +import logging from pathlib import Path +from urllib.parse import urljoin + import requests -import esphome.config_validation as cv -import esphome.codegen as cg - -from esphome.core import CORE, HexInt - -from esphome.components import esp32, microphone -from esphome import automation, git, external_files +from esphome import automation, external_files, git from esphome.automation import register_action, register_condition - - +import esphome.codegen as cg +from esphome.components import esp32, microphone +import esphome.config_validation as cv from esphome.const import ( - __version__, + CONF_FILE, CONF_ID, CONF_MICROPHONE, CONF_MODEL, - CONF_URL, - CONF_FILE, + CONF_PASSWORD, CONF_PATH, + CONF_RAW_DATA_ID, CONF_REF, CONF_REFRESH, CONF_TYPE, + CONF_URL, CONF_USERNAME, - CONF_PASSWORD, - CONF_RAW_DATA_ID, TYPE_GIT, TYPE_LOCAL, + __version__, ) - +from esphome.core import CORE, HexInt _LOGGER = logging.getLogger(__name__) @@ -174,12 +169,12 @@ def _convert_manifest_v1_to_v2(v1_manifest): CONF_SLIDING_WINDOW_AVERAGE_SIZE ] del v2_manifest[KEY_MICRO][CONF_SLIDING_WINDOW_AVERAGE_SIZE] - v2_manifest[KEY_MICRO][ - CONF_TENSOR_ARENA_SIZE - ] = 45672 # Original Inception-based V1 manifest models require a minimum of 45672 bytes - v2_manifest[KEY_MICRO][ - CONF_FEATURE_STEP_SIZE - ] = 20 # Original Inception-based V1 manifest models use a 20 ms feature step size + + # Original Inception-based V1 manifest models require a minimum of 45672 bytes + v2_manifest[KEY_MICRO][CONF_TENSOR_ARENA_SIZE] = 45672 + + # Original Inception-based V1 manifest models use a 20 ms feature step size + v2_manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE] = 20 return v2_manifest @@ -502,7 +497,7 @@ async def to_code(config): ) cg.add(var.set_features_step_size(manifest[KEY_MICRO][CONF_FEATURE_STEP_SIZE])) - cg.add_library("kahrendt/ESPMicroSpeechFeatures", "1.0.0") + cg.add_library("kahrendt/ESPMicroSpeechFeatures", "1.1.0") MICRO_WAKE_WORD_ACTION_SCHEMA = cv.Schema({cv.GenerateID(): cv.use_id(MicroWakeWord)}) diff --git a/platformio.ini b/platformio.ini index 87a239207f..4a0a3f2ef4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -145,7 +145,7 @@ framework = espidf lib_deps = ${common:idf.lib_deps} droscy/esp_wireguard@0.4.2 ; wireguard - kahrendt/ESPMicroSpeechFeatures@1.0.0 ; micro_wake_word + kahrendt/ESPMicroSpeechFeatures@1.1.0 ; micro_wake_word build_flags = ${common:idf.build_flags} -Wno-nonnull-compare From cf6ea7cb2cf1636cb18e60ecf010fa6bb559c464 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Wed, 14 Aug 2024 05:42:43 +0200 Subject: [PATCH 0205/1052] Implement the finish() method and action. implement the is_stopped condition (#7255) --- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 12 ++++++++++-- .../i2s_audio/speaker/i2s_audio_speaker.h | 2 ++ esphome/components/speaker/__init__.py | 19 ++++++++++++++----- esphome/components/speaker/automation.h | 10 ++++++++++ esphome/components/speaker/speaker.h | 5 +++++ tests/components/speaker/test.esp32-ard.yaml | 11 +++++++++-- .../components/speaker/test.esp32-c3-ard.yaml | 11 +++++++++-- .../components/speaker/test.esp32-c3-idf.yaml | 11 +++++++++-- tests/components/speaker/test.esp32-idf.yaml | 11 +++++++++-- 9 files changed, 77 insertions(+), 15 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 1c6c50d8c9..cf5a2c2766 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -180,7 +180,11 @@ void I2SAudioSpeaker::player_task(void *params) { } } -void I2SAudioSpeaker::stop() { +void I2SAudioSpeaker::stop() { this->stop_(false); } + +void I2SAudioSpeaker::finish() { this->stop_(true); } + +void I2SAudioSpeaker::stop_(bool wait_on_empty) { if (this->is_failed()) return; if (this->state_ == speaker::STATE_STOPPED) @@ -192,7 +196,11 @@ void I2SAudioSpeaker::stop() { this->state_ = speaker::STATE_STOPPING; DataEvent data; data.stop = true; - xQueueSendToFront(this->buffer_queue_, &data, portMAX_DELAY); + if (wait_on_empty) { + xQueueSend(this->buffer_queue_, &data, portMAX_DELAY); + } else { + xQueueSendToFront(this->buffer_queue_, &data, portMAX_DELAY); + } } void I2SAudioSpeaker::watch_() { diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 1800feaeec..0bdb67ceba 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -53,6 +53,7 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud void start() override; void stop() override; + void finish() override; size_t play(const uint8_t *data, size_t length) override; @@ -60,6 +61,7 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud protected: void start_(); + void stop_(bool wait_on_empty); void watch_(); static void player_task(void *params); diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index 79d5df8c5a..d28b726d1f 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -1,13 +1,11 @@ from esphome import automation -import esphome.config_validation as cv -import esphome.codegen as cg - from esphome.automation import maybe_simple_id -from esphome.const import CONF_ID, CONF_DATA +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_DATA, CONF_ID from esphome.core import CORE from esphome.coroutine import coroutine_with_priority - CODEOWNERS = ["@jesserockz"] IS_PLATFORM_COMPONENT = True @@ -22,8 +20,12 @@ PlayAction = speaker_ns.class_( StopAction = speaker_ns.class_( "StopAction", automation.Action, cg.Parented.template(Speaker) ) +FinishAction = speaker_ns.class_( + "FinishAction", automation.Action, cg.Parented.template(Speaker) +) IsPlayingCondition = speaker_ns.class_("IsPlayingCondition", automation.Condition) +IsStoppedCondition = speaker_ns.class_("IsStoppedCondition", automation.Condition) async def setup_speaker_core_(var, config): @@ -75,11 +77,18 @@ async def speaker_play_action(config, action_id, template_arg, args): automation.register_action("speaker.stop", StopAction, SPEAKER_AUTOMATION_SCHEMA)( speaker_action ) +automation.register_action("speaker.finish", FinishAction, SPEAKER_AUTOMATION_SCHEMA)( + speaker_action +) automation.register_condition( "speaker.is_playing", IsPlayingCondition, SPEAKER_AUTOMATION_SCHEMA )(speaker_action) +automation.register_condition( + "speaker.is_stopped", IsStoppedCondition, SPEAKER_AUTOMATION_SCHEMA +)(speaker_action) + @coroutine_with_priority(100.0) async def to_code(config): diff --git a/esphome/components/speaker/automation.h b/esphome/components/speaker/automation.h index e28991a0d1..2716fe6100 100644 --- a/esphome/components/speaker/automation.h +++ b/esphome/components/speaker/automation.h @@ -39,10 +39,20 @@ template class StopAction : public Action, public Parente void play(Ts... x) override { this->parent_->stop(); } }; +template class FinishAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->finish(); } +}; + template class IsPlayingCondition : public Condition, public Parented { public: bool check(Ts... x) override { return this->parent_->is_running(); } }; +template class IsStoppedCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->is_stopped(); } +}; + } // namespace speaker } // namespace esphome diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index b494873160..142231881c 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -17,10 +17,15 @@ class Speaker { virtual void start() = 0; virtual void stop() = 0; + // In compare between *STOP()* and *FINISH()*; *FINISH()* will stop after emptying the play buffer, + // while *STOP()* will break directly. + // When finish() is not implemented on the plateform component it should just do a normal stop. + virtual void finish() { this->stop(); } virtual bool has_buffered_data() const = 0; bool is_running() const { return this->state_ == STATE_RUNNING; } + bool is_stopped() const { return this->state_ == STATE_STOPPED; } protected: State state_{STATE_STOPPED}; diff --git a/tests/components/speaker/test.esp32-ard.yaml b/tests/components/speaker/test.esp32-ard.yaml index 416e203d7b..e10c3e88c1 100644 --- a/tests/components/speaker/test.esp32-ard.yaml +++ b/tests/components/speaker/test.esp32-ard.yaml @@ -1,8 +1,15 @@ esphome: on_boot: then: - - speaker.play: [0, 1, 2, 3] - - speaker.stop + - if: + condition: speaker.is_stopped + then: + - speaker.play: [0, 1, 2, 3] + - if: + condition: speaker.is_playing + then: + - speaker.finish: + - speaker.stop: i2s_audio: i2s_lrclk_pin: 16 diff --git a/tests/components/speaker/test.esp32-c3-ard.yaml b/tests/components/speaker/test.esp32-c3-ard.yaml index c7809baace..08699d8b22 100644 --- a/tests/components/speaker/test.esp32-c3-ard.yaml +++ b/tests/components/speaker/test.esp32-c3-ard.yaml @@ -1,8 +1,15 @@ esphome: on_boot: then: - - speaker.play: [0, 1, 2, 3] - - speaker.stop + - if: + condition: speaker.is_stopped + then: + - speaker.play: [0, 1, 2, 3] + - if: + condition: speaker.is_playing + then: + - speaker.finish: + - speaker.stop: i2s_audio: i2s_lrclk_pin: 6 diff --git a/tests/components/speaker/test.esp32-c3-idf.yaml b/tests/components/speaker/test.esp32-c3-idf.yaml index c7809baace..08699d8b22 100644 --- a/tests/components/speaker/test.esp32-c3-idf.yaml +++ b/tests/components/speaker/test.esp32-c3-idf.yaml @@ -1,8 +1,15 @@ esphome: on_boot: then: - - speaker.play: [0, 1, 2, 3] - - speaker.stop + - if: + condition: speaker.is_stopped + then: + - speaker.play: [0, 1, 2, 3] + - if: + condition: speaker.is_playing + then: + - speaker.finish: + - speaker.stop: i2s_audio: i2s_lrclk_pin: 6 diff --git a/tests/components/speaker/test.esp32-idf.yaml b/tests/components/speaker/test.esp32-idf.yaml index 416e203d7b..e10c3e88c1 100644 --- a/tests/components/speaker/test.esp32-idf.yaml +++ b/tests/components/speaker/test.esp32-idf.yaml @@ -1,8 +1,15 @@ esphome: on_boot: then: - - speaker.play: [0, 1, 2, 3] - - speaker.stop + - if: + condition: speaker.is_stopped + then: + - speaker.play: [0, 1, 2, 3] + - if: + condition: speaker.is_playing + then: + - speaker.finish: + - speaker.stop: i2s_audio: i2s_lrclk_pin: 16 From ccf57488c5432372dcdb2e87072ed15a9c7953a4 Mon Sep 17 00:00:00 2001 From: Mike La Spina Date: Tue, 13 Aug 2024 23:43:35 -0500 Subject: [PATCH 0206/1052] Correct offset calibration (#7228) Co-authored-by: descipher Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/atm90e32/__init__.py | 7 ++ esphome/components/atm90e32/atm90e32.cpp | 84 +++++++++++++++---- esphome/components/atm90e32/atm90e32.h | 27 ++++-- esphome/components/atm90e32/atm90e32_reg.h | 2 + .../components/atm90e32/button/__init__.py | 43 ++++++++++ .../atm90e32/button/atm90e32_button.cpp | 20 +++++ .../atm90e32/button/atm90e32_button.h | 27 ++++++ esphome/components/atm90e32/sensor.py | 26 +++--- tests/components/atm90e32/test.esp32-ard.yaml | 9 ++ .../atm90e32/test.esp32-c3-ard.yaml | 9 ++ .../atm90e32/test.esp32-c3-idf.yaml | 9 ++ tests/components/atm90e32/test.esp32-idf.yaml | 9 ++ .../components/atm90e32/test.esp8266-ard.yaml | 40 +++++++++ .../components/atm90e32/test.rp2040-ard.yaml | 9 ++ 15 files changed, 288 insertions(+), 34 deletions(-) create mode 100644 esphome/components/atm90e32/button/__init__.py create mode 100644 esphome/components/atm90e32/button/atm90e32_button.cpp create mode 100644 esphome/components/atm90e32/button/atm90e32_button.h diff --git a/CODEOWNERS b/CODEOWNERS index 3ea9c75ac2..1236c8d842 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -46,6 +46,7 @@ esphome/components/async_tcp/* @OttoWinter esphome/components/at581x/* @X-Ryl669 esphome/components/atc_mithermometer/* @ahpohl esphome/components/atm90e26/* @danieltwagner +esphome/components/atm90e32/* @circuitsetup @descipher esphome/components/b_parasite/* @rbaron esphome/components/ballu/* @bazuchan esphome/components/bang_bang/* @OttoWinter diff --git a/esphome/components/atm90e32/__init__.py b/esphome/components/atm90e32/__init__.py index e69de29bb2..8ce95be489 100644 --- a/esphome/components/atm90e32/__init__.py +++ b/esphome/components/atm90e32/__init__.py @@ -0,0 +1,7 @@ +import esphome.codegen as cg + +CODEOWNERS = ["@circuitsetup", "@descipher"] + +atm90e32_ns = cg.esphome_ns.namespace("atm90e32") + +CONF_ATM90E32_ID = "atm90e32_id" diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp index e27459b18a..43647b1855 100644 --- a/esphome/components/atm90e32/atm90e32.cpp +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -132,10 +132,77 @@ void ATM90E32Component::update() { this->status_clear_warning(); } +void ATM90E32Component::restore_calibrations_() { + if (enable_offset_calibration_) { + this->pref_.load(&this->offset_phase_); + } +}; + +void ATM90E32Component::run_offset_calibrations() { + // Run the calibrations and + // Setup voltage and current calibration offsets for PHASE A + this->offset_phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA); + this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_; + this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // C Voltage offset + this->offset_phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA); + this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_; + this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // C Current offset + // Setup voltage and current calibration offsets for PHASE B + this->offset_phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB); + this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_; + this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // C Voltage offset + this->offset_phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB); + this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_; + this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // C Current offset + // Setup voltage and current calibration offsets for PHASE C + this->offset_phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC); + this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_; + this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset + this->offset_phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC); + this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_; + this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset + this->pref_.save(&this->offset_phase_); + ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_, + this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_); + ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_, + this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_); +} + +void ATM90E32Component::clear_offset_calibrations() { + // Clear the calibrations and + this->offset_phase_[PHASEA].voltage_offset_ = 0; + this->phase_[PHASEA].voltage_offset_ = this->offset_phase_[PHASEA].voltage_offset_; + this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // C Voltage offset + this->offset_phase_[PHASEA].current_offset_ = 0; + this->phase_[PHASEA].current_offset_ = this->offset_phase_[PHASEA].current_offset_; + this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // C Current offset + this->offset_phase_[PHASEB].voltage_offset_ = 0; + this->phase_[PHASEB].voltage_offset_ = this->offset_phase_[PHASEB].voltage_offset_; + this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // C Voltage offset + this->offset_phase_[PHASEB].current_offset_ = 0; + this->phase_[PHASEB].current_offset_ = this->offset_phase_[PHASEB].current_offset_; + this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // C Current offset + this->offset_phase_[PHASEC].voltage_offset_ = 0; + this->phase_[PHASEC].voltage_offset_ = this->offset_phase_[PHASEC].voltage_offset_; + this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset + this->offset_phase_[PHASEC].current_offset_ = 0; + this->phase_[PHASEC].current_offset_ = this->offset_phase_[PHASEC].current_offset_; + this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset + this->pref_.save(&this->offset_phase_); + ESP_LOGI(TAG, "PhaseA Vo=%5d PhaseB Vo=%5d PhaseC Vo=%5d", this->offset_phase_[PHASEA].voltage_offset_, + this->offset_phase_[PHASEB].voltage_offset_, this->offset_phase_[PHASEC].voltage_offset_); + ESP_LOGI(TAG, "PhaseA Io=%5d PhaseB Io=%5d PhaseC Io=%5d", this->offset_phase_[PHASEA].current_offset_, + this->offset_phase_[PHASEB].current_offset_, this->offset_phase_[PHASEC].current_offset_); +} + void ATM90E32Component::setup() { ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component..."); this->spi_setup(); - + if (this->enable_offset_calibration_) { + uint32_t hash = fnv1_hash(App.get_friendly_name()); + this->pref_ = global_preferences->make_preference(hash, true); + this->restore_calibrations_(); + } uint16_t mmode0 = 0x87; // 3P4W 50Hz if (line_freq_ == 60) { mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz @@ -167,27 +234,12 @@ void ATM90E32Component::setup() { this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50% this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750 this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10% - // Setup voltage and current calibration offsets for PHASE A - this->phase_[PHASEA].voltage_offset_ = calibrate_voltage_offset_phase(PHASEA); - this->write16_(ATM90E32_REGISTER_UOFFSETA, this->phase_[PHASEA].voltage_offset_); // A Voltage offset - this->phase_[PHASEA].current_offset_ = calibrate_current_offset_phase(PHASEA); - this->write16_(ATM90E32_REGISTER_IOFFSETA, this->phase_[PHASEA].current_offset_); // A Current offset // Setup voltage and current gain for PHASE A this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[PHASEA].voltage_gain_); // A Voltage rms gain this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[PHASEA].ct_gain_); // A line current gain - // Setup voltage and current calibration offsets for PHASE B - this->phase_[PHASEB].voltage_offset_ = calibrate_voltage_offset_phase(PHASEB); - this->write16_(ATM90E32_REGISTER_UOFFSETB, this->phase_[PHASEB].voltage_offset_); // B Voltage offset - this->phase_[PHASEB].current_offset_ = calibrate_current_offset_phase(PHASEB); - this->write16_(ATM90E32_REGISTER_IOFFSETB, this->phase_[PHASEB].current_offset_); // B Current offset // Setup voltage and current gain for PHASE B this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[PHASEB].voltage_gain_); // B Voltage rms gain this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[PHASEB].ct_gain_); // B line current gain - // Setup voltage and current calibration offsets for PHASE C - this->phase_[PHASEC].voltage_offset_ = calibrate_voltage_offset_phase(PHASEC); - this->write16_(ATM90E32_REGISTER_UOFFSETC, this->phase_[PHASEC].voltage_offset_); // C Voltage offset - this->phase_[PHASEC].current_offset_ = calibrate_current_offset_phase(PHASEC); - this->write16_(ATM90E32_REGISTER_IOFFSETC, this->phase_[PHASEC].current_offset_); // C Current offset // Setup voltage and current gain for PHASE C this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[PHASEC].voltage_gain_); // C Voltage rms gain this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[PHASEC].ct_gain_); // C line current gain diff --git a/esphome/components/atm90e32/atm90e32.h b/esphome/components/atm90e32/atm90e32.h index 0a334dbe8b..35c61d1e05 100644 --- a/esphome/components/atm90e32/atm90e32.h +++ b/esphome/components/atm90e32/atm90e32.h @@ -1,9 +1,12 @@ #pragma once -#include "esphome/core/component.h" +#include "atm90e32_reg.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/spi/spi.h" -#include "atm90e32_reg.h" +#include "esphome/core/application.h" +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" +#include "esphome/core/preferences.h" namespace esphome { namespace atm90e32 { @@ -20,7 +23,6 @@ class ATM90E32Component : public PollingComponent, void dump_config() override; float get_setup_priority() const override; void update() override; - void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; } void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; } void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; } @@ -48,9 +50,11 @@ class ATM90E32Component : public PollingComponent, void set_line_freq(int freq) { line_freq_ = freq; } void set_current_phases(int phases) { current_phases_ = phases; } void set_pga_gain(uint16_t gain) { pga_gain_ = gain; } + void run_offset_calibrations(); + void clear_offset_calibrations(); + void set_enable_offset_calibration(bool flag) { enable_offset_calibration_ = flag; } uint16_t calibrate_voltage_offset_phase(uint8_t /*phase*/); uint16_t calibrate_current_offset_phase(uint8_t /*phase*/); - int32_t last_periodic_millis = millis(); protected: @@ -83,10 +87,11 @@ class ATM90E32Component : public PollingComponent, float get_chip_temperature_(); bool get_publish_interval_flag_() { return publish_interval_flag_; }; void set_publish_interval_flag_(bool flag) { publish_interval_flag_ = flag; }; + void restore_calibrations_(); struct ATM90E32Phase { - uint16_t voltage_gain_{7305}; - uint16_t ct_gain_{27961}; + uint16_t voltage_gain_{0}; + uint16_t ct_gain_{0}; uint16_t voltage_offset_{0}; uint16_t current_offset_{0}; float voltage_{0}; @@ -114,13 +119,21 @@ class ATM90E32Component : public PollingComponent, uint32_t cumulative_reverse_active_energy_{0}; } phase_[3]; + struct Calibration { + uint16_t voltage_offset_{0}; + uint16_t current_offset_{0}; + } offset_phase_[3]; + + ESPPreferenceObject pref_; + sensor::Sensor *freq_sensor_{nullptr}; sensor::Sensor *chip_temperature_sensor_{nullptr}; uint16_t pga_gain_{0x15}; int line_freq_{60}; int current_phases_{3}; - bool publish_interval_flag_{true}; + bool publish_interval_flag_{false}; bool peak_current_signed_{false}; + bool enable_offset_calibration_{false}; }; } // namespace atm90e32 diff --git a/esphome/components/atm90e32/atm90e32_reg.h b/esphome/components/atm90e32/atm90e32_reg.h index dac62aa6b4..954fb42e79 100644 --- a/esphome/components/atm90e32/atm90e32_reg.h +++ b/esphome/components/atm90e32/atm90e32_reg.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace esphome { namespace atm90e32 { diff --git a/esphome/components/atm90e32/button/__init__.py b/esphome/components/atm90e32/button/__init__.py new file mode 100644 index 0000000000..931346b386 --- /dev/null +++ b/esphome/components/atm90e32/button/__init__.py @@ -0,0 +1,43 @@ +import esphome.codegen as cg +from esphome.components import button +import esphome.config_validation as cv +from esphome.const import CONF_ID, ENTITY_CATEGORY_CONFIG, ICON_CHIP, ICON_SCALE + +from .. import atm90e32_ns +from ..sensor import ATM90E32Component + +CONF_RUN_OFFSET_CALIBRATION = "run_offset_calibration" +CONF_CLEAR_OFFSET_CALIBRATION = "clear_offset_calibration" + +ATM90E32CalibrationButton = atm90e32_ns.class_( + "ATM90E32CalibrationButton", + button.Button, +) +ATM90E32ClearCalibrationButton = atm90e32_ns.class_( + "ATM90E32ClearCalibrationButton", + button.Button, +) + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_ID): cv.use_id(ATM90E32Component), + cv.Optional(CONF_RUN_OFFSET_CALIBRATION): button.button_schema( + ATM90E32CalibrationButton, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_SCALE, + ), + cv.Optional(CONF_CLEAR_OFFSET_CALIBRATION): button.button_schema( + ATM90E32ClearCalibrationButton, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_CHIP, + ), +} + + +async def to_code(config): + parent = await cg.get_variable(config[CONF_ID]) + if run_offset := config.get(CONF_RUN_OFFSET_CALIBRATION): + b = await button.new_button(run_offset) + await cg.register_parented(b, parent) + if clear_offset := config.get(CONF_CLEAR_OFFSET_CALIBRATION): + b = await button.new_button(clear_offset) + await cg.register_parented(b, parent) diff --git a/esphome/components/atm90e32/button/atm90e32_button.cpp b/esphome/components/atm90e32/button/atm90e32_button.cpp new file mode 100644 index 0000000000..00715b61dd --- /dev/null +++ b/esphome/components/atm90e32/button/atm90e32_button.cpp @@ -0,0 +1,20 @@ +#include "atm90e32_button.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace atm90e32 { + +static const char *const TAG = "atm90e32.button"; + +void ATM90E32CalibrationButton::press_action() { + ESP_LOGI(TAG, "Running offset calibrations, Note: CTs and ACVs must be 0 during this process..."); + this->parent_->run_offset_calibrations(); +} + +void ATM90E32ClearCalibrationButton::press_action() { + ESP_LOGI(TAG, "Offset calibrations cleared."); + this->parent_->clear_offset_calibrations(); +} + +} // namespace atm90e32 +} // namespace esphome diff --git a/esphome/components/atm90e32/button/atm90e32_button.h b/esphome/components/atm90e32/button/atm90e32_button.h new file mode 100644 index 0000000000..0617099457 --- /dev/null +++ b/esphome/components/atm90e32/button/atm90e32_button.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/atm90e32/atm90e32.h" +#include "esphome/components/button/button.h" + +namespace esphome { +namespace atm90e32 { + +class ATM90E32CalibrationButton : public button::Button, public Parented { + public: + ATM90E32CalibrationButton() = default; + + protected: + void press_action() override; +}; + +class ATM90E32ClearCalibrationButton : public button::Button, public Parented { + public: + ATM90E32ClearCalibrationButton() = default; + + protected: + void press_action() override; +}; + +} // namespace atm90e32 +} // namespace esphome diff --git a/esphome/components/atm90e32/sensor.py b/esphome/components/atm90e32/sensor.py index 2bc7f0498d..be2196223c 100644 --- a/esphome/components/atm90e32/sensor.py +++ b/esphome/components/atm90e32/sensor.py @@ -1,21 +1,21 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import sensor, spi +import esphome.config_validation as cv from esphome.const import ( - CONF_ID, - CONF_REACTIVE_POWER, - CONF_VOLTAGE, + CONF_APPARENT_POWER, CONF_CURRENT, + CONF_FORWARD_ACTIVE_ENERGY, + CONF_FREQUENCY, + CONF_ID, CONF_PHASE_A, + CONF_PHASE_ANGLE, CONF_PHASE_B, CONF_PHASE_C, - CONF_PHASE_ANGLE, CONF_POWER, CONF_POWER_FACTOR, - CONF_APPARENT_POWER, - CONF_FREQUENCY, - CONF_FORWARD_ACTIVE_ENERGY, + CONF_REACTIVE_POWER, CONF_REVERSE_ACTIVE_ENERGY, + CONF_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, @@ -23,13 +23,13 @@ from esphome.const import ( DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_VOLTAGE, ENTITY_CATEGORY_DIAGNOSTIC, - ICON_LIGHTBULB, ICON_CURRENT_AC, + ICON_LIGHTBULB, STATE_CLASS_MEASUREMENT, STATE_CLASS_TOTAL_INCREASING, UNIT_AMPERE, - UNIT_DEGREES, UNIT_CELSIUS, + UNIT_DEGREES, UNIT_HERTZ, UNIT_VOLT, UNIT_VOLT_AMPS_REACTIVE, @@ -37,6 +37,8 @@ from esphome.const import ( UNIT_WATT_HOURS, ) +from . import atm90e32_ns + CONF_LINE_FREQUENCY = "line_frequency" CONF_CHIP_TEMPERATURE = "chip_temperature" CONF_GAIN_PGA = "gain_pga" @@ -46,6 +48,7 @@ CONF_GAIN_CT = "gain_ct" CONF_HARMONIC_POWER = "harmonic_power" CONF_PEAK_CURRENT = "peak_current" CONF_PEAK_CURRENT_SIGNED = "peak_current_signed" +CONF_ENABLE_OFFSET_CALIBRATION = "enable_offset_calibration" UNIT_DEG = "degrees" LINE_FREQS = { "50HZ": 50, @@ -61,7 +64,6 @@ PGA_GAINS = { "4X": 0x2A, } -atm90e32_ns = cg.esphome_ns.namespace("atm90e32") ATM90E32Component = atm90e32_ns.class_( "ATM90E32Component", cg.PollingComponent, spi.SPIDevice ) @@ -164,6 +166,7 @@ CONFIG_SCHEMA = ( ), cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True), cv.Optional(CONF_PEAK_CURRENT_SIGNED, default=False): cv.boolean, + cv.Optional(CONF_ENABLE_OFFSET_CALIBRATION, default=False): cv.boolean, } ) .extend(cv.polling_component_schema("60s")) @@ -227,3 +230,4 @@ async def to_code(config): cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES])) cg.add(var.set_pga_gain(config[CONF_GAIN_PGA])) cg.add(var.set_peak_current_signed(config[CONF_PEAK_CURRENT_SIGNED])) + cg.add(var.set_enable_offset_calibration(config[CONF_ENABLE_OFFSET_CALIBRATION])) diff --git a/tests/components/atm90e32/test.esp32-ard.yaml b/tests/components/atm90e32/test.esp32-ard.yaml index 131270f8ad..3bdc2bcec6 100644 --- a/tests/components/atm90e32/test.esp32-ard.yaml +++ b/tests/components/atm90e32/test.esp32-ard.yaml @@ -7,6 +7,7 @@ spi: sensor: - platform: atm90e32 cs_pin: 13 + id: chip1 phase_a: voltage: name: EMON Line Voltage A @@ -49,3 +50,11 @@ sensor: line_frequency: 60Hz current_phases: 3 gain_pga: 2X + enable_offset_calibration: True +button: + - platform: atm90e32 + id: chip1 + run_offset_calibration: + name: "Chip1 - Run Offset Calibration" + clear_offset_calibration: + name: "Chip1 - Clear Offset Calibration" diff --git a/tests/components/atm90e32/test.esp32-c3-ard.yaml b/tests/components/atm90e32/test.esp32-c3-ard.yaml index 263fb6d24e..9ec0037a61 100644 --- a/tests/components/atm90e32/test.esp32-c3-ard.yaml +++ b/tests/components/atm90e32/test.esp32-c3-ard.yaml @@ -7,6 +7,7 @@ spi: sensor: - platform: atm90e32 cs_pin: 8 + id: chip1 phase_a: voltage: name: EMON Line Voltage A @@ -49,3 +50,11 @@ sensor: line_frequency: 60Hz current_phases: 3 gain_pga: 2X + enable_offset_calibration: True +button: + - platform: atm90e32 + id: chip1 + run_offset_calibration: + name: "Chip1 - Run Offset Calibration" + clear_offset_calibration: + name: "Chip1 - Clear Offset Calibration" diff --git a/tests/components/atm90e32/test.esp32-c3-idf.yaml b/tests/components/atm90e32/test.esp32-c3-idf.yaml index 263fb6d24e..9ec0037a61 100644 --- a/tests/components/atm90e32/test.esp32-c3-idf.yaml +++ b/tests/components/atm90e32/test.esp32-c3-idf.yaml @@ -7,6 +7,7 @@ spi: sensor: - platform: atm90e32 cs_pin: 8 + id: chip1 phase_a: voltage: name: EMON Line Voltage A @@ -49,3 +50,11 @@ sensor: line_frequency: 60Hz current_phases: 3 gain_pga: 2X + enable_offset_calibration: True +button: + - platform: atm90e32 + id: chip1 + run_offset_calibration: + name: "Chip1 - Run Offset Calibration" + clear_offset_calibration: + name: "Chip1 - Clear Offset Calibration" diff --git a/tests/components/atm90e32/test.esp32-idf.yaml b/tests/components/atm90e32/test.esp32-idf.yaml index 131270f8ad..3bdc2bcec6 100644 --- a/tests/components/atm90e32/test.esp32-idf.yaml +++ b/tests/components/atm90e32/test.esp32-idf.yaml @@ -7,6 +7,7 @@ spi: sensor: - platform: atm90e32 cs_pin: 13 + id: chip1 phase_a: voltage: name: EMON Line Voltage A @@ -49,3 +50,11 @@ sensor: line_frequency: 60Hz current_phases: 3 gain_pga: 2X + enable_offset_calibration: True +button: + - platform: atm90e32 + id: chip1 + run_offset_calibration: + name: "Chip1 - Run Offset Calibration" + clear_offset_calibration: + name: "Chip1 - Clear Offset Calibration" diff --git a/tests/components/atm90e32/test.esp8266-ard.yaml b/tests/components/atm90e32/test.esp8266-ard.yaml index e8e2abc1a9..fbb3368efa 100644 --- a/tests/components/atm90e32/test.esp8266-ard.yaml +++ b/tests/components/atm90e32/test.esp8266-ard.yaml @@ -7,6 +7,7 @@ spi: sensor: - platform: atm90e32 cs_pin: 5 + id: chip1 phase_a: voltage: name: EMON Line Voltage A @@ -49,3 +50,42 @@ sensor: line_frequency: 60Hz current_phases: 3 gain_pga: 2X + enable_offset_calibration: True + - platform: atm90e32 + cs_pin: 4 + id: chip2 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + voltage: + name: EMON Line Voltage C + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + line_frequency: 60Hz + current_phases: 2 +button: + - platform: atm90e32 + id: chip1 + run_offset_calibration: + name: "Chip1 - Run Offset Calibration" + clear_offset_calibration: + name: "Chip1 - Clear Offset Calibration" diff --git a/tests/components/atm90e32/test.rp2040-ard.yaml b/tests/components/atm90e32/test.rp2040-ard.yaml index 525e0b801a..a6b7956da7 100644 --- a/tests/components/atm90e32/test.rp2040-ard.yaml +++ b/tests/components/atm90e32/test.rp2040-ard.yaml @@ -7,6 +7,7 @@ spi: sensor: - platform: atm90e32 cs_pin: 5 + id: chip1 phase_a: voltage: name: EMON Line Voltage A @@ -49,3 +50,11 @@ sensor: line_frequency: 60Hz current_phases: 3 gain_pga: 2X + enable_offset_calibration: True +button: + - platform: atm90e32 + id: chip1 + run_offset_calibration: + name: "Chip1 - Run Offset Calibration" + clear_offset_calibration: + name: "Chip1 - Clear Offset Calibration" From 350f17e48f60cdd7100945c82a14e284adf517b5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:56:53 +1200 Subject: [PATCH 0207/1052] Bump version to 2024.9.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 37f20796b5..6157ce32f7 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.0-dev" +__version__ = "2024.9.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 7b233d6871eef2a9860b994940ee6669560e576c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:56:53 +1200 Subject: [PATCH 0208/1052] Bump version to 2024.8.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 37f20796b5..47aacd6452 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.0-dev" +__version__ = "2024.8.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 7133e08755fb9c65abc62af9dc0df47900caa0a3 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 14 Aug 2024 00:55:23 -0700 Subject: [PATCH 0209/1052] remove extra number from pronto (#7263) --- esphome/components/remote_base/pronto_protocol.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index 625af76235..35fd782248 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -201,9 +201,6 @@ std::string ProntoProtocol::compensate_and_dump_sequence_(const RawTimings &data out += dump_duration_(t_duration, timebase); } - // append minimum gap - out += dump_duration_(PRONTO_DEFAULT_GAP, timebase, true); - return out; } From 80a0f137224c82fdc3cf17dfdd5dd3683ad9e796 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 14 Aug 2024 23:05:16 +0200 Subject: [PATCH 0210/1052] [code-quality] fix performance-unnecessary-value-param (#7274) --- esphome/components/lvgl/number/lvgl_number.h | 4 +++- esphome/components/lvgl/select/lvgl_select.h | 4 +++- esphome/components/lvgl/switch/lvgl_switch.h | 4 +++- esphome/components/lvgl/text/lvgl_text.h | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/esphome/components/lvgl/number/lvgl_number.h b/esphome/components/lvgl/number/lvgl_number.h index a70c9eab9c..77fadd2a29 100644 --- a/esphome/components/lvgl/number/lvgl_number.h +++ b/esphome/components/lvgl/number/lvgl_number.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/components/number/number.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" @@ -11,7 +13,7 @@ namespace lvgl { class LVGLNumber : public number::Number { public: void set_control_lambda(std::function control_lambda) { - this->control_lambda_ = control_lambda; + this->control_lambda_ = std::move(control_lambda); if (this->initial_state_.has_value()) { this->control_lambda_(this->initial_state_.value()); this->initial_state_.reset(); diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h index 407045d605..97cc8697eb 100644 --- a/esphome/components/lvgl/select/lvgl_select.h +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/components/select/select.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" @@ -28,7 +30,7 @@ static std::vector split_string(const std::string &str) { class LVGLSelect : public select::Select { public: void set_control_lambda(std::function lambda) { - this->control_lambda_ = lambda; + this->control_lambda_ = std::move(lambda); if (this->initial_state_.has_value()) { this->control(this->initial_state_.value()); this->initial_state_.reset(); diff --git a/esphome/components/lvgl/switch/lvgl_switch.h b/esphome/components/lvgl/switch/lvgl_switch.h index dbc885219b..af839b8892 100644 --- a/esphome/components/lvgl/switch/lvgl_switch.h +++ b/esphome/components/lvgl/switch/lvgl_switch.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/components/switch/switch.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" @@ -11,7 +13,7 @@ namespace lvgl { class LVGLSwitch : public switch_::Switch { public: void set_control_lambda(std::function state_lambda) { - this->state_lambda_ = state_lambda; + this->state_lambda_ = std::move(state_lambda); if (this->initial_state_.has_value()) { this->state_lambda_(this->initial_state_.value()); this->initial_state_.reset(); diff --git a/esphome/components/lvgl/text/lvgl_text.h b/esphome/components/lvgl/text/lvgl_text.h index d3513c3697..4c380d69a2 100644 --- a/esphome/components/lvgl/text/lvgl_text.h +++ b/esphome/components/lvgl/text/lvgl_text.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/components/text/text.h" #include "esphome/core/automation.h" #include "esphome/core/component.h" @@ -11,7 +13,7 @@ namespace lvgl { class LVGLText : public text::Text { public: void set_control_lambda(std::function control_lambda) { - this->control_lambda_ = control_lambda; + this->control_lambda_ = std::move(control_lambda); if (this->initial_state_.has_value()) { this->control_lambda_(this->initial_state_.value()); this->initial_state_.reset(); From 5646ec7f9c601b5b3a0c1917edeae5b4487eff79 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 14 Aug 2024 23:41:29 +0200 Subject: [PATCH 0211/1052] [code-quality] fix clang-tidy prometheus (#7284) --- esphome/components/prometheus/prometheus_handler.cpp | 2 ++ esphome/components/prometheus/prometheus_handler.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 09913bd713..3e9cf81e6e 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -1,4 +1,5 @@ #include "prometheus_handler.h" +#ifdef USE_NETWORK #include "esphome/core/application.h" namespace esphome { @@ -350,3 +351,4 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) } // namespace prometheus } // namespace esphome +#endif diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index a9505a3572..f5e49a1419 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_NETWORK #include #include @@ -117,3 +118,4 @@ class PrometheusHandler : public AsyncWebHandler, public Component { } // namespace prometheus } // namespace esphome +#endif From 1bc3ccd96907ff392d4e321e5cc1e5833e107ccd Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 15 Aug 2024 00:30:29 +0200 Subject: [PATCH 0212/1052] [code-quality] fix clang-tidy ota (#7282) --- esphome/components/esphome/ota/ota_esphome.cpp | 3 ++- esphome/components/esphome/ota/ota_esphome.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 9d5044aaeb..7e2ef42a97 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -1,5 +1,5 @@ #include "ota_esphome.h" - +#ifdef USE_OTA #include "esphome/components/md5/md5.h" #include "esphome/components/network/util.h" #include "esphome/components/ota/ota_backend.h" @@ -410,3 +410,4 @@ float ESPHomeOTAComponent::get_setup_priority() const { return setup_priority::A uint16_t ESPHomeOTAComponent::get_port() const { return this->port_; } void ESPHomeOTAComponent::set_port(uint16_t port) { this->port_ = port; } } // namespace esphome +#endif diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index 42629b4346..e0d09ff37e 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/defines.h" +#ifdef USE_OTA #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" #include "esphome/components/ota/ota_backend.h" @@ -41,3 +42,4 @@ class ESPHomeOTAComponent : public ota::OTAComponent { }; } // namespace esphome +#endif From ce7adbae995ecd4dbb3368678d6de5be9d1d4855 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 15 Aug 2024 00:31:19 +0200 Subject: [PATCH 0213/1052] [code-quality] fix clang-tidy e131 (#7281) --- esphome/components/e131/e131.cpp | 2 ++ esphome/components/e131/e131.h | 4 +++- esphome/components/e131/e131_addressable_light_effect.cpp | 2 ++ esphome/components/e131/e131_addressable_light_effect.h | 3 ++- esphome/components/e131/e131_packet.cpp | 2 ++ 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index c3ff21c1a0..a74fc9be4a 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -1,4 +1,5 @@ #include "e131.h" +#ifdef USE_NETWORK #include "e131_addressable_light_effect.h" #include "esphome/core/log.h" @@ -118,3 +119,4 @@ bool E131Component::process_(int universe, const E131Packet &packet) { } // namespace e131 } // namespace esphome +#endif diff --git a/esphome/components/e131/e131.h b/esphome/components/e131/e131.h index 91b67f62eb..d0e38fa98c 100644 --- a/esphome/components/e131/e131.h +++ b/esphome/components/e131/e131.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_NETWORK #include "esphome/components/socket/socket.h" #include "esphome/core/component.h" @@ -53,3 +54,4 @@ class E131Component : public esphome::Component { } // namespace e131 } // namespace esphome +#endif diff --git a/esphome/components/e131/e131_addressable_light_effect.cpp b/esphome/components/e131/e131_addressable_light_effect.cpp index be3144f590..4d1f98ab6c 100644 --- a/esphome/components/e131/e131_addressable_light_effect.cpp +++ b/esphome/components/e131/e131_addressable_light_effect.cpp @@ -1,5 +1,6 @@ #include "e131_addressable_light_effect.h" #include "e131.h" +#ifdef USE_NETWORK #include "esphome/core/log.h" namespace esphome { @@ -90,3 +91,4 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet } // namespace e131 } // namespace esphome +#endif diff --git a/esphome/components/e131/e131_addressable_light_effect.h b/esphome/components/e131/e131_addressable_light_effect.h index 56df9cd80f..17d7bd2829 100644 --- a/esphome/components/e131/e131_addressable_light_effect.h +++ b/esphome/components/e131/e131_addressable_light_effect.h @@ -2,7 +2,7 @@ #include "esphome/core/component.h" #include "esphome/components/light/addressable_light_effect.h" - +#ifdef USE_NETWORK namespace esphome { namespace e131 { @@ -42,3 +42,4 @@ class E131AddressableLightEffect : public light::AddressableLightEffect { } // namespace e131 } // namespace esphome +#endif diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index e1ae41cbaf..b8fa73b707 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -1,5 +1,6 @@ #include #include "e131.h" +#ifdef USE_NETWORK #include "esphome/components/network/ip_address.h" #include "esphome/core/log.h" #include "esphome/core/util.h" @@ -137,3 +138,4 @@ bool E131Component::packet_(const std::vector &data, int &universe, E13 } // namespace e131 } // namespace esphome +#endif From ecd3d838c937d59bd9ee71d0ea714034033d8c4c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 15 Aug 2024 13:35:03 +1000 Subject: [PATCH 0214/1052] [api] Bump noise-c library version (#7288) --- .github/workflows/ci.yml | 4 ++-- esphome/components/api/__init__.py | 2 +- esphome/components/host/__init__.py | 10 +++------- tests/components/api/common.yaml | 4 ---- tests/components/api/test.esp32-ard.yaml | 4 ++++ tests/components/api/test.esp32-c3-ard.yaml | 4 ++++ tests/components/api/test.esp32-c3-idf.yaml | 4 ++++ tests/components/api/test.esp32-idf.yaml | 4 ++++ tests/components/api/test.esp8266-ard.yaml | 4 ++++ tests/components/api/test.host.yaml | 3 +++ tests/components/api/test.rp2040-ard.yaml | 4 ++++ 11 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 tests/components/api/test.host.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 126a541b3d..2437dd5b8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -397,7 +397,7 @@ jobs: file: ${{ fromJson(needs.list-components.outputs.components) }} steps: - name: Install dependencies - run: sudo apt-get install libsodium-dev libsdl2-dev + run: sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 @@ -451,7 +451,7 @@ jobs: run: echo ${{ matrix.components }} - name: Install dependencies - run: sudo apt-get install libsodium-dev libsdl2-dev + run: sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 38b50d4b9d..27de5c873b 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -155,7 +155,7 @@ async def to_code(config): decoded = base64.b64decode(encryption_config[CONF_KEY]) cg.add(var.set_noise_psk(list(decoded))) cg.add_define("USE_API_NOISE") - cg.add_library("esphome/noise-c", "0.1.4") + cg.add_library("esphome/noise-c", "0.1.6") else: cg.add_define("USE_API_PLAINTEXT") diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index 39e418c9ea..e83bf2dba8 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -1,15 +1,14 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( + CONF_MAC_ADDRESS, KEY_CORE, KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, PLATFORM_HOST, - CONF_MAC_ADDRESS, ) from esphome.core import CORE -from esphome.helpers import IS_MACOS -import esphome.config_validation as cv -import esphome.codegen as cg from .const import KEY_HOST @@ -42,8 +41,5 @@ async def to_code(config): cg.add_build_flag("-DUSE_HOST") cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts) cg.add_build_flag("-std=c++17") - cg.add_build_flag("-lsodium") - if IS_MACOS: - cg.add_build_flag("-L/opt/homebrew/lib") cg.add_define("ESPHOME_BOARD", "host") cg.add_platformio_option("platform", "platformio/native") diff --git a/tests/components/api/common.yaml b/tests/components/api/common.yaml index 6c2a333598..7ac11e4da6 100644 --- a/tests/components/api/common.yaml +++ b/tests/components/api/common.yaml @@ -11,10 +11,6 @@ esphome: message: Button was pressed - homeassistant.tag_scanned: pulse -wifi: - ssid: MySSID - password: password1 - api: port: 8000 password: pwd diff --git a/tests/components/api/test.esp32-ard.yaml b/tests/components/api/test.esp32-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-ard.yaml +++ b/tests/components/api/test.esp32-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp32-c3-ard.yaml b/tests/components/api/test.esp32-c3-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-c3-ard.yaml +++ b/tests/components/api/test.esp32-c3-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp32-c3-idf.yaml b/tests/components/api/test.esp32-c3-idf.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-c3-idf.yaml +++ b/tests/components/api/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp32-idf.yaml b/tests/components/api/test.esp32-idf.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-idf.yaml +++ b/tests/components/api/test.esp32-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp8266-ard.yaml b/tests/components/api/test.esp8266-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp8266-ard.yaml +++ b/tests/components/api/test.esp8266-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.host.yaml b/tests/components/api/test.host.yaml new file mode 100644 index 0000000000..1ecafeab77 --- /dev/null +++ b/tests/components/api/test.host.yaml @@ -0,0 +1,3 @@ +<<: !include common.yaml + +network: diff --git a/tests/components/api/test.rp2040-ard.yaml b/tests/components/api/test.rp2040-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.rp2040-ard.yaml +++ b/tests/components/api/test.rp2040-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 From 965141fad75618afea619e9d4b7dfd8ac4007c89 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 15 Aug 2024 06:38:49 +0200 Subject: [PATCH 0215/1052] [code-quality] fix clang-tidy wireguard (#7287) --- esphome/components/wireguard/__init__.py | 15 +++++++++------ esphome/components/wireguard/wireguard.cpp | 3 ++- esphome/components/wireguard/wireguard.h | 4 +++- esphome/core/defines.h | 1 + 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index 16d0d0226e..5e34a8a19b 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -1,19 +1,20 @@ -import re import ipaddress +import re + +from esphome import automation import esphome.codegen as cg +from esphome.components import time +from esphome.components.esp32 import CORE, add_idf_sdkconfig_option import esphome.config_validation as cv from esphome.const import ( - CONF_ID, - CONF_TIME_ID, CONF_ADDRESS, + CONF_ID, CONF_REBOOT_TIMEOUT, + CONF_TIME_ID, KEY_CORE, KEY_FRAMEWORK_VERSION, ) -from esphome.components.esp32 import CORE, add_idf_sdkconfig_option -from esphome.components import time from esphome.core import TimePeriod -from esphome import automation CONF_NETMASK = "netmask" CONF_PRIVATE_KEY = "private_key" @@ -91,6 +92,8 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) + cg.add_define("USE_WIREGUARD") + cg.add(var.set_address(str(config[CONF_ADDRESS]))) cg.add(var.set_netmask(str(config[CONF_NETMASK]))) cg.add(var.set_private_key(config[CONF_PRIVATE_KEY])) diff --git a/esphome/components/wireguard/wireguard.cpp b/esphome/components/wireguard/wireguard.cpp index 17ebc701e3..7b4011cb79 100644 --- a/esphome/components/wireguard/wireguard.cpp +++ b/esphome/components/wireguard/wireguard.cpp @@ -1,5 +1,5 @@ #include "wireguard.h" - +#ifdef USE_WIREGUARD #include #include #include @@ -289,3 +289,4 @@ std::string mask_key(const std::string &key) { return (key.substr(0, 5) + "[...] } // namespace wireguard } // namespace esphome +#endif diff --git a/esphome/components/wireguard/wireguard.h b/esphome/components/wireguard/wireguard.h index a0e9e27a1b..5db9a48c90 100644 --- a/esphome/components/wireguard/wireguard.h +++ b/esphome/components/wireguard/wireguard.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_WIREGUARD #include #include #include @@ -170,3 +171,4 @@ template class WireguardDisableAction : public Action, pu } // namespace wireguard } // namespace esphome +#endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index a4d473b76e..52cf7d4dd0 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -75,6 +75,7 @@ #define USE_VALVE #define USE_WIFI #define USE_WIFI_AP +#define USE_WIREGUARD // Arduino-specific feature flags #ifdef USE_ARDUINO From 5c31ab40607a9418ada87ad19f59abb85eb5db83 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Thu, 15 Aug 2024 06:51:44 +0200 Subject: [PATCH 0216/1052] fix some small rtttl issues (#6817) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/rtttl/rtttl.cpp | 127 ++++++++++++++++++++++++----- esphome/components/rtttl/rtttl.h | 21 +++-- 2 files changed, 121 insertions(+), 27 deletions(-) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index 0bdf65b7bd..a97120499d 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -29,6 +29,13 @@ inline double deg2rad(double degrees) { void Rtttl::dump_config() { ESP_LOGCONFIG(TAG, "Rtttl"); } 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, "RTTL Component is already playing: %s", name.c_str()); + return; + } + this->rtttl_ = std::move(rtttl); this->default_duration_ = 4; @@ -98,13 +105,20 @@ void Rtttl::play(std::string rtttl) { this->note_duration_ = 1; #ifdef USE_SPEAKER - this->samples_sent_ = 0; - this->samples_count_ = 0; + if (this->speaker_ != nullptr) { + this->set_state_(State::STATE_INIT); + this->samples_sent_ = 0; + this->samples_count_ = 0; + } +#endif +#ifdef USE_OUTPUT + if (this->output_ != nullptr) { + this->set_state_(State::STATE_RUNNING); + } #endif } void Rtttl::stop() { - this->note_duration_ = 0; #ifdef USE_OUTPUT if (this->output_ != nullptr) { this->output_->set_level(0.0); @@ -117,16 +131,35 @@ void Rtttl::stop() { } } #endif + this->note_duration_ = 0; + this->set_state_(STATE_STOPPING); } void Rtttl::loop() { - if (this->note_duration_ == 0) + if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) return; #ifdef USE_SPEAKER if (this->speaker_ != nullptr) { + if (this->state_ == State::STATE_STOPPING) { + if (this->speaker_->is_stopped()) { + this->set_state_(State::STATE_STOPPED); + } + } else if (this->state_ == State::STATE_INIT) { + if (this->speaker_->is_stopped()) { + this->speaker_->start(); + this->set_state_(State::STATE_STARTING); + } + } else if (this->state_ == State::STATE_STARTING) { + if (this->speaker_->is_running()) { + this->set_state_(State::STATE_RUNNING); + } + } + if (!this->speaker_->is_running()) { + return; + } if (this->samples_sent_ != this->samples_count_) { - SpeakerSample sample[SAMPLE_BUFFER_SIZE + 1]; + SpeakerSample sample[SAMPLE_BUFFER_SIZE + 2]; int x = 0; double rem = 0.0; @@ -136,7 +169,7 @@ void Rtttl::loop() { if (this->samples_per_wave_ != 0 && this->samples_sent_ >= this->samples_gap_) { // Play note// rem = ((this->samples_sent_ << 10) % this->samples_per_wave_) * (360.0 / this->samples_per_wave_); - int16_t val = (49152 * this->gain_) * sin(deg2rad(rem)); + int16_t val = (127 * this->gain_) * sin(deg2rad(rem)); // 16bit = 49152 sample[x].left = val; sample[x].right = val; @@ -153,9 +186,9 @@ void Rtttl::loop() { x++; } if (x > 0) { - int send = this->speaker_->play((uint8_t *) (&sample), x * 4); + int send = this->speaker_->play((uint8_t *) (&sample), x * 2); if (send != x * 4) { - this->samples_sent_ -= (x - (send / 4)); + this->samples_sent_ -= (x - (send / 2)); } return; } @@ -167,14 +200,7 @@ void Rtttl::loop() { return; #endif if (!this->rtttl_[position_]) { - this->note_duration_ = 0; -#ifdef USE_OUTPUT - if (this->output_ != nullptr) { - this->output_->set_level(0.0); - } -#endif - ESP_LOGD(TAG, "Playback finished"); - this->on_finished_playback_callback_.call(); + this->finish_(); return; } @@ -213,6 +239,7 @@ void Rtttl::loop() { case 'a': note = 10; break; + case 'h': case 'b': note = 12; break; @@ -238,14 +265,21 @@ void Rtttl::loop() { uint8_t scale = get_integer_(); if (scale == 0) 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); + this->finish_(); + return; + } bool need_note_gap = false; // Now play the note 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"); - this->note_duration_ = 0; + ESP_LOGE(TAG, "Note out of valid range (note: %d, scale: %d, index: %d, max: %d)", note, scale, note_index, + (int) sizeof(NOTES)); + this->finish_(); return; } auto freq = NOTES[note_index]; @@ -285,14 +319,17 @@ void Rtttl::loop() { this->samples_gap_ = (this->sample_rate_ * DOUBLE_NOTE_GAP_MS) / 1600; //(ms); } if (this->output_freq_ != 0) { + // make sure there is enough samples to add a full last sinus. + + uint16_t samples_wish = this->samples_count_; this->samples_per_wave_ = (this->sample_rate_ << 10) / this->output_freq_; - // make sure there is enough samples to add a full last sinus. uint16_t division = ((this->samples_count_ << 10) / this->samples_per_wave_) + 1; - uint16_t x = this->samples_count_; + this->samples_count_ = (division * this->samples_per_wave_); - ESP_LOGD(TAG, "play time old: %d div: %d new: %d %d", x, division, this->samples_count_, this->samples_per_wave_); this->samples_count_ = this->samples_count_ >> 10; + ESP_LOGVV(TAG, "- Calc play time: wish: %d gets: %d (div: %d spw: %d)", samples_wish, this->samples_count_, + division, this->samples_per_wave_); } // Convert from frequency in Hz to high and low samples in fixed point } @@ -301,5 +338,53 @@ void Rtttl::loop() { this->last_note_ = millis(); } +void Rtttl::finish_() { +#ifdef USE_OUTPUT + if (this->output_ != nullptr) { + this->output_->set_level(0.0); + } +#endif +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + SpeakerSample sample[2]; + sample[0].left = 0; + sample[0].right = 0; + sample[1].left = 0; + sample[1].right = 0; + this->speaker_->play((uint8_t *) (&sample), 8); + + this->speaker_->finish(); + } +#endif + this->set_state_(State::STATE_STOPPING); + this->note_duration_ = 0; + this->on_finished_playback_callback_.call(); + ESP_LOGD(TAG, "Playback finished"); +} + +static const LogString *state_to_string(State state) { + switch (state) { + case STATE_STOPPED: + return LOG_STR("STATE_STOPPED"); + case STATE_STARTING: + return LOG_STR("STATE_STARTING"); + case STATE_RUNNING: + return LOG_STR("STATE_RUNNING"); + case STATE_STOPPING: + return LOG_STR("STATE_STOPPING"); + case STATE_INIT: + return LOG_STR("STATE_INIT"); + default: + return LOG_STR("UNKNOWN"); + } +}; + +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)), + LOG_STR_ARG(state_to_string(state))); +} + } // namespace rtttl } // namespace esphome diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index bf089ce980..3cb6e3f5fb 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -14,12 +14,20 @@ namespace esphome { namespace rtttl { +enum State : uint8_t { + STATE_STOPPED = 0, + STATE_INIT, + STATE_STARTING, + STATE_RUNNING, + STATE_STOPPING, +}; + #ifdef USE_SPEAKER -static const size_t SAMPLE_BUFFER_SIZE = 512; +static const size_t SAMPLE_BUFFER_SIZE = 2048; struct SpeakerSample { - int16_t left{0}; - int16_t right{0}; + int8_t left{0}; + int8_t right{0}; }; #endif @@ -42,7 +50,7 @@ class Rtttl : public Component { void stop(); void dump_config() override; - bool is_playing() { return this->note_duration_ != 0; } + bool is_playing() { return this->state_ != State::STATE_STOPPED; } void loop() override; void add_on_finished_playback_callback(std::function callback) { @@ -57,6 +65,8 @@ class Rtttl : public Component { } return ret; } + void finish_(); + void set_state_(State state); std::string rtttl_{""}; size_t position_{0}; @@ -68,13 +78,12 @@ class Rtttl : public Component { uint32_t output_freq_; float gain_{0.6f}; + State state_{State::STATE_STOPPED}; #ifdef USE_OUTPUT output::FloatOutput *output_; #endif - void play_output_(); - #ifdef USE_SPEAKER speaker::Speaker *speaker_{nullptr}; int sample_rate_{16000}; From 9713458368dfb9fd9aab8016cfe8c85d77b04887 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 15 Aug 2024 07:17:38 +0200 Subject: [PATCH 0217/1052] [code-quality] fix clang-tidy improv_serial (#7283) --- esphome/components/improv_serial/improv_serial_component.cpp | 3 ++- esphome/components/improv_serial/improv_serial_component.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 425a5c8576..c3a0f2eacc 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -1,5 +1,5 @@ #include "improv_serial_component.h" - +#ifdef USE_WIFI #include "esphome/core/application.h" #include "esphome/core/defines.h" #include "esphome/core/hal.h" @@ -313,3 +313,4 @@ ImprovSerialComponent *global_improv_serial_component = // NOLINT(cppcoreguidel } // namespace improv_serial } // namespace esphome +#endif diff --git a/esphome/components/improv_serial/improv_serial_component.h b/esphome/components/improv_serial/improv_serial_component.h index f737f93d86..5d2534c2fc 100644 --- a/esphome/components/improv_serial/improv_serial_component.h +++ b/esphome/components/improv_serial/improv_serial_component.h @@ -5,7 +5,7 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" - +#ifdef USE_WIFI #include #include @@ -78,3 +78,4 @@ extern ImprovSerialComponent } // namespace improv_serial } // namespace esphome +#endif From abb2669f0fb94c72deb55781bb087c86fe2e382c Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 15 Aug 2024 23:16:06 +0200 Subject: [PATCH 0218/1052] [code-quality] fix clang-tidy captive_portal (#7280) --- esphome/components/captive_portal/captive_portal.cpp | 2 ++ esphome/components/captive_portal/captive_portal.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 630e00f0b7..d1960e9a93 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -1,4 +1,5 @@ #include "captive_portal.h" +#ifdef USE_CAPTIVE_PORTAL #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/components/wifi/wifi_component.h" @@ -91,3 +92,4 @@ CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avo } // namespace captive_portal } // namespace esphome +#endif diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index df45d40d12..24d1295e6a 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_CAPTIVE_PORTAL #include #ifdef USE_ARDUINO #include @@ -71,3 +72,4 @@ extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid- } // namespace captive_portal } // namespace esphome +#endif From 9001d1c0d46cf214f989baac9c1f05f4ed321804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Kiss?= <70820303+g-kiss@users.noreply.github.com> Date: Fri, 16 Aug 2024 00:35:00 +0200 Subject: [PATCH 0219/1052] Fix overflow in ESPColorCorrection object (#7268) --- esphome/components/light/esp_color_correction.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/components/light/esp_color_correction.h b/esphome/components/light/esp_color_correction.h index eedd71ab27..979a1acb07 100644 --- a/esphome/components/light/esp_color_correction.h +++ b/esphome/components/light/esp_color_correction.h @@ -41,29 +41,29 @@ class ESPColorCorrection { if (this->max_brightness_.red == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } inline uint8_t color_uncorrect_green(uint8_t green) const ESPHOME_ALWAYS_INLINE { if (this->max_brightness_.green == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } inline uint8_t color_uncorrect_blue(uint8_t blue) const ESPHOME_ALWAYS_INLINE { if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } inline uint8_t color_uncorrect_white(uint8_t white) const ESPHOME_ALWAYS_INLINE { if (this->max_brightness_.white == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.white) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.white) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } protected: From c3668b9a4de8408c28089c8c0652e0c36119cdcd Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:05:26 +1200 Subject: [PATCH 0220/1052] [validation] Allow ``maybe_simple_value`` to not have default key in complex value (#7294) --- esphome/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index ef60d6e0d6..1c00e0699b 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1850,7 +1850,7 @@ def maybe_simple_value(*validators, **kwargs): if value == SCHEMA_EXTRACT: return (validator, key) - if isinstance(value, dict) and key in value: + if isinstance(value, dict): return validator(value) return validator({key: value}) From a0c54504cdc54521309364609883c002f88ae137 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Aug 2024 00:27:23 +0100 Subject: [PATCH 0221/1052] Add HMAC-MD5 support for authenticating OTA updates (#7200) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/hmac_md5/__init__.py | 2 + esphome/components/hmac_md5/hmac_md5.cpp | 56 ++++++++++++++++++++++++ esphome/components/hmac_md5/hmac_md5.h | 48 ++++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 esphome/components/hmac_md5/__init__.py create mode 100644 esphome/components/hmac_md5/hmac_md5.cpp create mode 100644 esphome/components/hmac_md5/hmac_md5.h diff --git a/CODEOWNERS b/CODEOWNERS index 1236c8d842..9159f5f843 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -169,6 +169,7 @@ esphome/components/he60r/* @clydebarrow esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hm3301/* @freekode +esphome/components/hmac_md5/* @dwmw2 esphome/components/homeassistant/* @OttoWinter @esphome/core esphome/components/homeassistant/number/* @landonr esphome/components/homeassistant/switch/* @Links2004 diff --git a/esphome/components/hmac_md5/__init__.py b/esphome/components/hmac_md5/__init__.py new file mode 100644 index 0000000000..fe245c0cfd --- /dev/null +++ b/esphome/components/hmac_md5/__init__.py @@ -0,0 +1,2 @@ +AUTO_LOAD = ["md5"] +CODEOWNERS = ["@dwmw2"] diff --git a/esphome/components/hmac_md5/hmac_md5.cpp b/esphome/components/hmac_md5/hmac_md5.cpp new file mode 100644 index 0000000000..90bf91882f --- /dev/null +++ b/esphome/components/hmac_md5/hmac_md5.cpp @@ -0,0 +1,56 @@ +#include +#include +#include "hmac_md5.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace hmac_md5 { +void HmacMD5::init(const uint8_t *key, size_t len) { + uint8_t ipad[64], opad[64]; + + memset(ipad, 0, sizeof(ipad)); + if (len > 64) { + md5::MD5Digest keymd5; + keymd5.init(); + keymd5.add(key, len); + keymd5.calculate(); + keymd5.get_bytes(ipad); + } else { + memcpy(ipad, key, len); + } + memcpy(opad, ipad, sizeof(opad)); + + for (int i = 0; i < 64; i++) { + ipad[i] ^= 0x36; + opad[i] ^= 0x5c; + } + + this->ihash_.init(); + this->ihash_.add(ipad, sizeof(ipad)); + + this->ohash_.init(); + this->ohash_.add(opad, sizeof(opad)); +} + +void HmacMD5::add(const uint8_t *data, size_t len) { this->ihash_.add(data, len); } + +void HmacMD5::calculate() { + uint8_t ibytes[16]; + + this->ihash_.calculate(); + this->ihash_.get_bytes(ibytes); + + this->ohash_.add(ibytes, sizeof(ibytes)); + this->ohash_.calculate(); +} + +void HmacMD5::get_bytes(uint8_t *output) { this->ohash_.get_bytes(output); } + +void HmacMD5::get_hex(char *output) { this->ohash_.get_hex(output); } + +bool HmacMD5::equals_bytes(const uint8_t *expected) { return this->ohash_.equals_bytes(expected); } + +bool HmacMD5::equals_hex(const char *expected) { return this->ohash_.equals_hex(expected); } + +} // namespace hmac_md5 +} // namespace esphome diff --git a/esphome/components/hmac_md5/hmac_md5.h b/esphome/components/hmac_md5/hmac_md5.h new file mode 100644 index 0000000000..e6a97ad2e3 --- /dev/null +++ b/esphome/components/hmac_md5/hmac_md5.h @@ -0,0 +1,48 @@ +#pragma once + +#include "esphome/core/defines.h" +#include "esphome/components/md5/md5.h" + +#include + +namespace esphome { +namespace hmac_md5 { + +class HmacMD5 { + public: + HmacMD5() = default; + ~HmacMD5() = default; + + /// Initialize a new MD5 digest computation. + void init(const uint8_t *key, size_t len); + void init(const char *key, size_t len) { this->init((const uint8_t *) key, len); } + void init(const std::string &key) { this->init(key.c_str(), key.length()); } + + /// Add bytes of data for the digest. + void add(const uint8_t *data, size_t len); + void add(const char *data, size_t len) { this->add((const uint8_t *) data, len); } + + /// Compute the digest, based on the provided data. + void calculate(); + + /// Retrieve the HMAC-MD5 digest as bytes. + /// The output must be able to hold 16 bytes or more. + void get_bytes(uint8_t *output); + + /// Retrieve the HMAC-MD5 digest as hex characters. + /// The output must be able to hold 32 bytes or more. + void get_hex(char *output); + + /// Compare the digest against a provided byte-encoded digest (16 bytes). + bool equals_bytes(const uint8_t *expected); + + /// Compare the digest against a provided hex-encoded digest (32 bytes). + bool equals_hex(const char *expected); + + protected: + md5::MD5Digest ihash_; + md5::MD5Digest ohash_; +}; + +} // namespace hmac_md5 +} // namespace esphome From a7167ec3bf1adaec467944586c71d442a93a68d2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Aug 2024 02:32:00 +0100 Subject: [PATCH 0222/1052] [network] Always allow ``enable_ipv6: false`` (#7291) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/network/__init__.py | 6 +++++- esphome/config_validation.py | 14 ++++++++++++++ tests/components/network/test-ipv6.bk72xx-ard.yaml | 4 ++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/components/network/test-ipv6.bk72xx-ard.yaml diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 96db322bde..caa873a746 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -24,7 +24,11 @@ CONFIG_SCHEMA = cv.Schema( esp32=False, rp2040=False, ): cv.All( - cv.boolean, cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]) + cv.boolean, + cv.Any( + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), + cv.boolean_false, + ), ), cv.Optional(CONF_MIN_IPV6_ADDR_COUNT, default=0): cv.positive_int, } diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 1c00e0699b..6d6cb451d6 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -370,6 +370,20 @@ def boolean(value): ) +def boolean_false(value): + """Validate the given config option to be a boolean, set to False. + + This option allows a bunch of different ways of expressing boolean values: + - instance of boolean + - 'true'/'false' + - 'yes'/'no' + - 'enable'/disable + """ + if boolean(value): + raise Invalid("Expected boolean value to be false") + return False + + @schema_extractor_list def ensure_list(*validators): """Validate this configuration option to be a list. diff --git a/tests/components/network/test-ipv6.bk72xx-ard.yaml b/tests/components/network/test-ipv6.bk72xx-ard.yaml new file mode 100644 index 0000000000..361ca09977 --- /dev/null +++ b/tests/components/network/test-ipv6.bk72xx-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + network_enable_ipv6: "false" + +<<: !include common.yaml From bc20fd57fe3bdd52a5ed5000d29b90063f76dd0a Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 14 Aug 2024 00:55:23 -0700 Subject: [PATCH 0223/1052] remove extra number from pronto (#7263) --- esphome/components/remote_base/pronto_protocol.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/components/remote_base/pronto_protocol.cpp b/esphome/components/remote_base/pronto_protocol.cpp index 625af76235..35fd782248 100644 --- a/esphome/components/remote_base/pronto_protocol.cpp +++ b/esphome/components/remote_base/pronto_protocol.cpp @@ -201,9 +201,6 @@ std::string ProntoProtocol::compensate_and_dump_sequence_(const RawTimings &data out += dump_duration_(t_duration, timebase); } - // append minimum gap - out += dump_duration_(PRONTO_DEFAULT_GAP, timebase, true); - return out; } From e3bfbebb8fd3368ac3c351af33407cf77956910d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 15 Aug 2024 13:35:03 +1000 Subject: [PATCH 0224/1052] [api] Bump noise-c library version (#7288) --- .github/workflows/ci.yml | 4 ++-- esphome/components/api/__init__.py | 2 +- esphome/components/host/__init__.py | 10 +++------- tests/components/api/common.yaml | 4 ---- tests/components/api/test.esp32-ard.yaml | 4 ++++ tests/components/api/test.esp32-c3-ard.yaml | 4 ++++ tests/components/api/test.esp32-c3-idf.yaml | 4 ++++ tests/components/api/test.esp32-idf.yaml | 4 ++++ tests/components/api/test.esp8266-ard.yaml | 4 ++++ tests/components/api/test.host.yaml | 3 +++ tests/components/api/test.rp2040-ard.yaml | 4 ++++ 11 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 tests/components/api/test.host.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 126a541b3d..2437dd5b8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -397,7 +397,7 @@ jobs: file: ${{ fromJson(needs.list-components.outputs.components) }} steps: - name: Install dependencies - run: sudo apt-get install libsodium-dev libsdl2-dev + run: sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 @@ -451,7 +451,7 @@ jobs: run: echo ${{ matrix.components }} - name: Install dependencies - run: sudo apt-get install libsodium-dev libsdl2-dev + run: sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 38b50d4b9d..27de5c873b 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -155,7 +155,7 @@ async def to_code(config): decoded = base64.b64decode(encryption_config[CONF_KEY]) cg.add(var.set_noise_psk(list(decoded))) cg.add_define("USE_API_NOISE") - cg.add_library("esphome/noise-c", "0.1.4") + cg.add_library("esphome/noise-c", "0.1.6") else: cg.add_define("USE_API_PLAINTEXT") diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index 39e418c9ea..e83bf2dba8 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -1,15 +1,14 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( + CONF_MAC_ADDRESS, KEY_CORE, KEY_FRAMEWORK_VERSION, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, PLATFORM_HOST, - CONF_MAC_ADDRESS, ) from esphome.core import CORE -from esphome.helpers import IS_MACOS -import esphome.config_validation as cv -import esphome.codegen as cg from .const import KEY_HOST @@ -42,8 +41,5 @@ async def to_code(config): cg.add_build_flag("-DUSE_HOST") cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts) cg.add_build_flag("-std=c++17") - cg.add_build_flag("-lsodium") - if IS_MACOS: - cg.add_build_flag("-L/opt/homebrew/lib") cg.add_define("ESPHOME_BOARD", "host") cg.add_platformio_option("platform", "platformio/native") diff --git a/tests/components/api/common.yaml b/tests/components/api/common.yaml index 6c2a333598..7ac11e4da6 100644 --- a/tests/components/api/common.yaml +++ b/tests/components/api/common.yaml @@ -11,10 +11,6 @@ esphome: message: Button was pressed - homeassistant.tag_scanned: pulse -wifi: - ssid: MySSID - password: password1 - api: port: 8000 password: pwd diff --git a/tests/components/api/test.esp32-ard.yaml b/tests/components/api/test.esp32-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-ard.yaml +++ b/tests/components/api/test.esp32-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp32-c3-ard.yaml b/tests/components/api/test.esp32-c3-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-c3-ard.yaml +++ b/tests/components/api/test.esp32-c3-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp32-c3-idf.yaml b/tests/components/api/test.esp32-c3-idf.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-c3-idf.yaml +++ b/tests/components/api/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp32-idf.yaml b/tests/components/api/test.esp32-idf.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp32-idf.yaml +++ b/tests/components/api/test.esp32-idf.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.esp8266-ard.yaml b/tests/components/api/test.esp8266-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.esp8266-ard.yaml +++ b/tests/components/api/test.esp8266-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 diff --git a/tests/components/api/test.host.yaml b/tests/components/api/test.host.yaml new file mode 100644 index 0000000000..1ecafeab77 --- /dev/null +++ b/tests/components/api/test.host.yaml @@ -0,0 +1,3 @@ +<<: !include common.yaml + +network: diff --git a/tests/components/api/test.rp2040-ard.yaml b/tests/components/api/test.rp2040-ard.yaml index dade44d145..46c01d926f 100644 --- a/tests/components/api/test.rp2040-ard.yaml +++ b/tests/components/api/test.rp2040-ard.yaml @@ -1 +1,5 @@ <<: !include common.yaml + +wifi: + ssid: MySSID + password: password1 From e17c7124f48ac3fc2fbc2c45bbe01b23a203f65a Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Thu, 15 Aug 2024 06:51:44 +0200 Subject: [PATCH 0225/1052] fix some small rtttl issues (#6817) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/rtttl/rtttl.cpp | 127 ++++++++++++++++++++++++----- esphome/components/rtttl/rtttl.h | 21 +++-- 2 files changed, 121 insertions(+), 27 deletions(-) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index 0bdf65b7bd..a97120499d 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -29,6 +29,13 @@ inline double deg2rad(double degrees) { void Rtttl::dump_config() { ESP_LOGCONFIG(TAG, "Rtttl"); } 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, "RTTL Component is already playing: %s", name.c_str()); + return; + } + this->rtttl_ = std::move(rtttl); this->default_duration_ = 4; @@ -98,13 +105,20 @@ void Rtttl::play(std::string rtttl) { this->note_duration_ = 1; #ifdef USE_SPEAKER - this->samples_sent_ = 0; - this->samples_count_ = 0; + if (this->speaker_ != nullptr) { + this->set_state_(State::STATE_INIT); + this->samples_sent_ = 0; + this->samples_count_ = 0; + } +#endif +#ifdef USE_OUTPUT + if (this->output_ != nullptr) { + this->set_state_(State::STATE_RUNNING); + } #endif } void Rtttl::stop() { - this->note_duration_ = 0; #ifdef USE_OUTPUT if (this->output_ != nullptr) { this->output_->set_level(0.0); @@ -117,16 +131,35 @@ void Rtttl::stop() { } } #endif + this->note_duration_ = 0; + this->set_state_(STATE_STOPPING); } void Rtttl::loop() { - if (this->note_duration_ == 0) + if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) return; #ifdef USE_SPEAKER if (this->speaker_ != nullptr) { + if (this->state_ == State::STATE_STOPPING) { + if (this->speaker_->is_stopped()) { + this->set_state_(State::STATE_STOPPED); + } + } else if (this->state_ == State::STATE_INIT) { + if (this->speaker_->is_stopped()) { + this->speaker_->start(); + this->set_state_(State::STATE_STARTING); + } + } else if (this->state_ == State::STATE_STARTING) { + if (this->speaker_->is_running()) { + this->set_state_(State::STATE_RUNNING); + } + } + if (!this->speaker_->is_running()) { + return; + } if (this->samples_sent_ != this->samples_count_) { - SpeakerSample sample[SAMPLE_BUFFER_SIZE + 1]; + SpeakerSample sample[SAMPLE_BUFFER_SIZE + 2]; int x = 0; double rem = 0.0; @@ -136,7 +169,7 @@ void Rtttl::loop() { if (this->samples_per_wave_ != 0 && this->samples_sent_ >= this->samples_gap_) { // Play note// rem = ((this->samples_sent_ << 10) % this->samples_per_wave_) * (360.0 / this->samples_per_wave_); - int16_t val = (49152 * this->gain_) * sin(deg2rad(rem)); + int16_t val = (127 * this->gain_) * sin(deg2rad(rem)); // 16bit = 49152 sample[x].left = val; sample[x].right = val; @@ -153,9 +186,9 @@ void Rtttl::loop() { x++; } if (x > 0) { - int send = this->speaker_->play((uint8_t *) (&sample), x * 4); + int send = this->speaker_->play((uint8_t *) (&sample), x * 2); if (send != x * 4) { - this->samples_sent_ -= (x - (send / 4)); + this->samples_sent_ -= (x - (send / 2)); } return; } @@ -167,14 +200,7 @@ void Rtttl::loop() { return; #endif if (!this->rtttl_[position_]) { - this->note_duration_ = 0; -#ifdef USE_OUTPUT - if (this->output_ != nullptr) { - this->output_->set_level(0.0); - } -#endif - ESP_LOGD(TAG, "Playback finished"); - this->on_finished_playback_callback_.call(); + this->finish_(); return; } @@ -213,6 +239,7 @@ void Rtttl::loop() { case 'a': note = 10; break; + case 'h': case 'b': note = 12; break; @@ -238,14 +265,21 @@ void Rtttl::loop() { uint8_t scale = get_integer_(); if (scale == 0) 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); + this->finish_(); + return; + } bool need_note_gap = false; // Now play the note 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"); - this->note_duration_ = 0; + ESP_LOGE(TAG, "Note out of valid range (note: %d, scale: %d, index: %d, max: %d)", note, scale, note_index, + (int) sizeof(NOTES)); + this->finish_(); return; } auto freq = NOTES[note_index]; @@ -285,14 +319,17 @@ void Rtttl::loop() { this->samples_gap_ = (this->sample_rate_ * DOUBLE_NOTE_GAP_MS) / 1600; //(ms); } if (this->output_freq_ != 0) { + // make sure there is enough samples to add a full last sinus. + + uint16_t samples_wish = this->samples_count_; this->samples_per_wave_ = (this->sample_rate_ << 10) / this->output_freq_; - // make sure there is enough samples to add a full last sinus. uint16_t division = ((this->samples_count_ << 10) / this->samples_per_wave_) + 1; - uint16_t x = this->samples_count_; + this->samples_count_ = (division * this->samples_per_wave_); - ESP_LOGD(TAG, "play time old: %d div: %d new: %d %d", x, division, this->samples_count_, this->samples_per_wave_); this->samples_count_ = this->samples_count_ >> 10; + ESP_LOGVV(TAG, "- Calc play time: wish: %d gets: %d (div: %d spw: %d)", samples_wish, this->samples_count_, + division, this->samples_per_wave_); } // Convert from frequency in Hz to high and low samples in fixed point } @@ -301,5 +338,53 @@ void Rtttl::loop() { this->last_note_ = millis(); } +void Rtttl::finish_() { +#ifdef USE_OUTPUT + if (this->output_ != nullptr) { + this->output_->set_level(0.0); + } +#endif +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + SpeakerSample sample[2]; + sample[0].left = 0; + sample[0].right = 0; + sample[1].left = 0; + sample[1].right = 0; + this->speaker_->play((uint8_t *) (&sample), 8); + + this->speaker_->finish(); + } +#endif + this->set_state_(State::STATE_STOPPING); + this->note_duration_ = 0; + this->on_finished_playback_callback_.call(); + ESP_LOGD(TAG, "Playback finished"); +} + +static const LogString *state_to_string(State state) { + switch (state) { + case STATE_STOPPED: + return LOG_STR("STATE_STOPPED"); + case STATE_STARTING: + return LOG_STR("STATE_STARTING"); + case STATE_RUNNING: + return LOG_STR("STATE_RUNNING"); + case STATE_STOPPING: + return LOG_STR("STATE_STOPPING"); + case STATE_INIT: + return LOG_STR("STATE_INIT"); + default: + return LOG_STR("UNKNOWN"); + } +}; + +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)), + LOG_STR_ARG(state_to_string(state))); +} + } // namespace rtttl } // namespace esphome diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index bf089ce980..3cb6e3f5fb 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -14,12 +14,20 @@ namespace esphome { namespace rtttl { +enum State : uint8_t { + STATE_STOPPED = 0, + STATE_INIT, + STATE_STARTING, + STATE_RUNNING, + STATE_STOPPING, +}; + #ifdef USE_SPEAKER -static const size_t SAMPLE_BUFFER_SIZE = 512; +static const size_t SAMPLE_BUFFER_SIZE = 2048; struct SpeakerSample { - int16_t left{0}; - int16_t right{0}; + int8_t left{0}; + int8_t right{0}; }; #endif @@ -42,7 +50,7 @@ class Rtttl : public Component { void stop(); void dump_config() override; - bool is_playing() { return this->note_duration_ != 0; } + bool is_playing() { return this->state_ != State::STATE_STOPPED; } void loop() override; void add_on_finished_playback_callback(std::function callback) { @@ -57,6 +65,8 @@ class Rtttl : public Component { } return ret; } + void finish_(); + void set_state_(State state); std::string rtttl_{""}; size_t position_{0}; @@ -68,13 +78,12 @@ class Rtttl : public Component { uint32_t output_freq_; float gain_{0.6f}; + State state_{State::STATE_STOPPED}; #ifdef USE_OUTPUT output::FloatOutput *output_; #endif - void play_output_(); - #ifdef USE_SPEAKER speaker::Speaker *speaker_{nullptr}; int sample_rate_{16000}; From 033ab552068374f4ad06dca122a250f18ba2a979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Kiss?= <70820303+g-kiss@users.noreply.github.com> Date: Fri, 16 Aug 2024 00:35:00 +0200 Subject: [PATCH 0226/1052] Fix overflow in ESPColorCorrection object (#7268) --- esphome/components/light/esp_color_correction.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/components/light/esp_color_correction.h b/esphome/components/light/esp_color_correction.h index eedd71ab27..979a1acb07 100644 --- a/esphome/components/light/esp_color_correction.h +++ b/esphome/components/light/esp_color_correction.h @@ -41,29 +41,29 @@ class ESPColorCorrection { if (this->max_brightness_.red == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } inline uint8_t color_uncorrect_green(uint8_t green) const ESPHOME_ALWAYS_INLINE { if (this->max_brightness_.green == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } inline uint8_t color_uncorrect_blue(uint8_t blue) const ESPHOME_ALWAYS_INLINE { if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } inline uint8_t color_uncorrect_white(uint8_t white) const ESPHOME_ALWAYS_INLINE { if (this->max_brightness_.white == 0 || this->local_brightness_ == 0) return 0; uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.white) * 255UL) / this->local_brightness_; - return res; + uint16_t res = ((uncorrected / this->max_brightness_.white) * 255UL) / this->local_brightness_; + return (uint8_t) std::min(res, uint16_t(255)); } protected: From 2c47eb62a7f1060be9fc727c8a5fc70ed92c1fd8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:05:26 +1200 Subject: [PATCH 0227/1052] [validation] Allow ``maybe_simple_value`` to not have default key in complex value (#7294) --- esphome/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index ef60d6e0d6..1c00e0699b 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1850,7 +1850,7 @@ def maybe_simple_value(*validators, **kwargs): if value == SCHEMA_EXTRACT: return (validator, key) - if isinstance(value, dict) and key in value: + if isinstance(value, dict): return validator(value) return validator({key: value}) From 343650e37d29058a5f2ef7caf8c7f868c9b1746c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 16 Aug 2024 02:32:00 +0100 Subject: [PATCH 0228/1052] [network] Always allow ``enable_ipv6: false`` (#7291) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/network/__init__.py | 6 +++++- esphome/config_validation.py | 14 ++++++++++++++ tests/components/network/test-ipv6.bk72xx-ard.yaml | 4 ++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/components/network/test-ipv6.bk72xx-ard.yaml diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 96db322bde..caa873a746 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -24,7 +24,11 @@ CONFIG_SCHEMA = cv.Schema( esp32=False, rp2040=False, ): cv.All( - cv.boolean, cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]) + cv.boolean, + cv.Any( + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), + cv.boolean_false, + ), ), cv.Optional(CONF_MIN_IPV6_ADDR_COUNT, default=0): cv.positive_int, } diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 1c00e0699b..6d6cb451d6 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -370,6 +370,20 @@ def boolean(value): ) +def boolean_false(value): + """Validate the given config option to be a boolean, set to False. + + This option allows a bunch of different ways of expressing boolean values: + - instance of boolean + - 'true'/'false' + - 'yes'/'no' + - 'enable'/disable + """ + if boolean(value): + raise Invalid("Expected boolean value to be false") + return False + + @schema_extractor_list def ensure_list(*validators): """Validate this configuration option to be a list. diff --git a/tests/components/network/test-ipv6.bk72xx-ard.yaml b/tests/components/network/test-ipv6.bk72xx-ard.yaml new file mode 100644 index 0000000000..361ca09977 --- /dev/null +++ b/tests/components/network/test-ipv6.bk72xx-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + network_enable_ipv6: "false" + +<<: !include common.yaml From e779a09586e16f03f4544dff36044e6e1318e4a3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 16 Aug 2024 13:38:06 +1200 Subject: [PATCH 0229/1052] Bump version to 2024.8.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 47aacd6452..39d2ee74a1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.0b1" +__version__ = "2024.8.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 56aa58780da5fe73637ed9f583fafbd0b3a26db7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sun, 18 Aug 2024 20:27:03 +1200 Subject: [PATCH 0230/1052] Revert "[validation] Allow ``maybe_simple_value`` to not have default key in complex value" (#7305) --- esphome/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 6d6cb451d6..719cc43b31 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1864,7 +1864,7 @@ def maybe_simple_value(*validators, **kwargs): if value == SCHEMA_EXTRACT: return (validator, key) - if isinstance(value, dict): + if isinstance(value, dict) and key in value: return validator(value) return validator({key: value}) From ac9417d4694a69d843457ce3fa40f6e9c959c64a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:43:23 +1000 Subject: [PATCH 0231/1052] [lvgl] Bug fixes (#7300) --- esphome/components/lvgl/defines.py | 32 ++++---- esphome/components/lvgl/lv_validation.py | 86 +++++++++++++-------- esphome/components/lvgl/schemas.py | 32 +++++--- esphome/components/lvgl/widgets/__init__.py | 23 +++++- tests/components/lvgl/common.yaml | 8 ++ tests/components/lvgl/lvgl-package.yaml | 58 ++++++++++++-- 6 files changed, 173 insertions(+), 66 deletions(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 8f7a973722..6a8b20b505 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -6,8 +6,8 @@ Constants already defined in esphome.const are not duplicated here and must be i from esphome import codegen as cg, config_validation as cv from esphome.const import CONF_ITEMS -from esphome.core import ID, Lambda -from esphome.cpp_generator import MockObj +from esphome.core import Lambda +from esphome.cpp_generator import LambdaExpression, MockObj from esphome.cpp_types import uint32 from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -22,19 +22,22 @@ def literal(arg): return arg +def call_lambda(lamb: LambdaExpression): + expr = lamb.content.strip() + if expr.startswith("return") and expr.endswith(";"): + return expr[7:][:-1] + return f"{lamb}()" + + class LValidator: """ A validator for a particular type used in LVGL. Usable in configs as a validator, also has `process()` to convert a value during code generation """ - def __init__( - self, validator, rtype, idtype=None, idexpr=None, retmapper=None, requires=None - ): + def __init__(self, validator, rtype, retmapper=None, requires=None): self.validator = validator self.rtype = rtype - self.idtype = idtype - self.idexpr = idexpr self.retmapper = retmapper self.requires = requires @@ -43,8 +46,6 @@ class LValidator: value = requires_component(self.requires)(value) if isinstance(value, cv.Lambda): return cv.returning_lambda(value) - if self.idtype is not None and isinstance(value, ID): - return cv.use_id(self.idtype)(value) return self.validator(value) async def process(self, value, args=()): @@ -52,10 +53,10 @@ class LValidator: return None if isinstance(value, Lambda): return cg.RawExpression( - f"{await cg.process_lambda(value, args, return_type=self.rtype)}()" + call_lambda( + await cg.process_lambda(value, args, return_type=self.rtype) + ) ) - if self.idtype is not None and isinstance(value, ID): - return cg.RawExpression(f"{value}->{self.idexpr}") if self.retmapper is not None: return self.retmapper(value) return cg.safe_exp(value) @@ -89,7 +90,7 @@ class LvConstant(LValidator): cv.ensure_list(self.one_of), uint32, retmapper=self.mapper ) - def mapper(self, value, args=()): + def mapper(self, value): if not isinstance(value, list): value = [value] return literal( @@ -103,7 +104,7 @@ class LvConstant(LValidator): def extend(self, *choices): """ - Extend an LVCconstant with additional choices. + Extend an LVconstant with additional choices. :param choices: The extra choices :return: A new LVConstant instance """ @@ -431,6 +432,8 @@ CONF_ONE_LINE = "one_line" CONF_ON_SELECT = "on_select" CONF_ONE_CHECKED = "one_checked" CONF_NEXT = "next" +CONF_PAD_ROW = "pad_row" +CONF_PAD_COLUMN = "pad_column" CONF_PAGE = "page" CONF_PAGE_WRAP = "page_wrap" CONF_PASSWORD_MODE = "password_mode" @@ -462,6 +465,7 @@ CONF_SKIP = "skip" CONF_SYMBOL = "symbol" CONF_TAB_ID = "tab_id" CONF_TABS = "tabs" +CONF_TIME_FORMAT = "time_format" CONF_TILE = "tile" CONF_TILE_ID = "tile_id" CONF_TILES = "tiles" diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index b351b84af6..a2be4a2abe 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,17 +1,14 @@ from typing import Union import esphome.codegen as cg -from esphome.components.binary_sensor import BinarySensor from esphome.components.color import ColorStruct from esphome.components.font import Font from esphome.components.image import Image_ -from esphome.components.sensor import Sensor -from esphome.components.text_sensor import TextSensor import esphome.config_validation as cv -from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_VALUE -from esphome.core import HexInt +from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_TIME, CONF_VALUE +from esphome.core import HexInt, Lambda from esphome.cpp_generator import MockObj -from esphome.cpp_types import uint32 +from esphome.cpp_types import ESPTime, uint32 from esphome.helpers import cpp_string_escape from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -19,9 +16,11 @@ from . import types as ty from .defines import ( CONF_END_VALUE, CONF_START_VALUE, + CONF_TIME_FORMAT, LV_FONTS, LValidator, LvConstant, + call_lambda, literal, ) from .helpers import ( @@ -110,13 +109,13 @@ def angle(value): def size_validator(value): """A size in one axis - one of "size_content", a number (pixels) or a percentage""" if value == SCHEMA_EXTRACT: - return ["size_content", "pixels", "..%"] + return ["SIZE_CONTENT", "number of pixels", "percentage"] if isinstance(value, str) and value.lower().endswith("px"): value = cv.int_(value[:-2]) if isinstance(value, str) and not value.endswith("%"): if value.upper() == "SIZE_CONTENT": return "LV_SIZE_CONTENT" - raise cv.Invalid("must be 'size_content', a pixel position or a percentage") + raise cv.Invalid("must be 'size_content', a percentage or an integer (pixels)") if isinstance(value, int): return cv.int_(value) # Will throw an exception if not a percentage. @@ -125,6 +124,15 @@ def size_validator(value): size = LValidator(size_validator, uint32, retmapper=literal) + +def pixels_validator(value): + if isinstance(value, str) and value.lower().endswith("px"): + return cv.int_(value[:-2]) + return cv.int_(value) + + +pixels = LValidator(pixels_validator, uint32, retmapper=literal) + radius_consts = LvConstant("LV_RADIUS_", "CIRCLE") @@ -167,9 +175,7 @@ lv_image = LValidator( retmapper=lambda x: lv_expr.img_from(MockObj(x)), requires="image", ) -lv_bool = LValidator( - cv.boolean, cg.bool_, BinarySensor, "get_state()", retmapper=literal -) +lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) def lv_pct(value: Union[int, float]): @@ -185,42 +191,60 @@ def lvms_validator_(value): lv_milliseconds = LValidator( - lvms_validator_, - cg.int32, - retmapper=lambda x: x.total_milliseconds, + lvms_validator_, cg.int32, retmapper=lambda x: x.total_milliseconds ) class TextValidator(LValidator): def __init__(self): - super().__init__( - cv.string, - cg.const_char_ptr, - TextSensor, - "get_state().c_str()", - lambda s: cg.safe_exp(f"{s}"), - ) + super().__init__(cv.string, cg.std_string, lambda s: cg.safe_exp(f"{s}")) def __call__(self, value): - if isinstance(value, dict): + if isinstance(value, dict) and CONF_FORMAT in value: return value return super().__call__(value) async def process(self, value, args=()): if isinstance(value, dict): - args = [str(x) for x in value[CONF_ARGS]] - arg_expr = cg.RawExpression(",".join(args)) - format_str = cpp_string_escape(value[CONF_FORMAT]) - return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") + if format_str := value.get(CONF_FORMAT): + args = [str(x) for x in value[CONF_ARGS]] + arg_expr = cg.RawExpression(",".join(args)) + format_str = cpp_string_escape(format_str) + return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") + if time_format := value.get(CONF_TIME_FORMAT): + source = value[CONF_TIME] + if isinstance(source, Lambda): + time_format = cpp_string_escape(time_format) + return cg.RawExpression( + call_lambda( + await cg.process_lambda(source, args, return_type=ESPTime) + ) + + f".strftime({time_format}).c_str()" + ) + # must be an ID + source = await cg.get_variable(source) + return source.now().strftime(time_format).c_str() + if isinstance(value, Lambda): + value = call_lambda( + await cg.process_lambda(value, args, return_type=self.rtype) + ) + + # Was the lambda call reduced to a string? + if value.endswith("c_str()") or ( + value.endswith('"') and value.startswith('"') + ): + pass + else: + # Either a std::string or a lambda call returning that. We need const char* + value = f"({value}).c_str()" + return cg.RawExpression(value) return await super().process(value, args) lv_text = TextValidator() -lv_float = LValidator(cv.float_, cg.float_, Sensor, "get_state()") -lv_int = LValidator(cv.int_, cg.int_, Sensor, "get_state()") -lv_brightness = LValidator( - cv.percentage, cg.float_, Sensor, "get_state()", retmapper=lambda x: int(x * 255) -) +lv_float = LValidator(cv.float_, cg.float_) +lv_int = LValidator(cv.int_, cg.int_) +lv_brightness = LValidator(cv.percentage, cg.float_, retmapper=lambda x: int(x * 255)) def is_lv_font(font): diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index f1c7ff4df6..e9714e3b1a 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -1,5 +1,6 @@ from esphome import config_validation as cv from esphome.automation import Trigger, validate_automation +from esphome.components.time import RealTimeClock from esphome.const import ( CONF_ARGS, CONF_FORMAT, @@ -8,6 +9,7 @@ from esphome.const import ( CONF_ON_VALUE, CONF_STATE, CONF_TEXT, + CONF_TIME, CONF_TRIGGER_ID, CONF_TYPE, ) @@ -15,6 +17,7 @@ from esphome.core import TimePeriod from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid +from .defines import CONF_TIME_FORMAT from .helpers import add_lv_use, requires_component, validate_printf from .lv_validation import lv_color, lv_font, lv_image from .lvcode import LvglComponent @@ -46,7 +49,13 @@ TEXT_SCHEMA = cv.Schema( ), validate_printf, ), - lvalid.lv_text, + cv.Schema( + { + cv.Required(CONF_TIME_FORMAT): cv.string, + cv.GenerateID(CONF_TIME): cv.templatable(cv.use_id(RealTimeClock)), + } + ), + cv.templatable(cv.string), ) } ) @@ -116,15 +125,13 @@ STYLE_PROPS = { "opa_layered": lvalid.opacity, "outline_color": lvalid.lv_color, "outline_opa": lvalid.opacity, - "outline_pad": lvalid.size, - "outline_width": lvalid.size, - "pad_all": lvalid.size, - "pad_bottom": lvalid.size, - "pad_column": lvalid.size, - "pad_left": lvalid.size, - "pad_right": lvalid.size, - "pad_row": lvalid.size, - "pad_top": lvalid.size, + "outline_pad": lvalid.pixels, + "outline_width": lvalid.pixels, + "pad_all": lvalid.pixels, + "pad_bottom": lvalid.pixels, + "pad_left": lvalid.pixels, + "pad_right": lvalid.pixels, + "pad_top": lvalid.pixels, "shadow_color": lvalid.lv_color, "shadow_ofs_x": cv.int_, "shadow_ofs_y": cv.int_, @@ -304,6 +311,8 @@ LAYOUT_SCHEMA = { cv.Required(df.CONF_GRID_COLUMNS): [grid_spec], cv.Optional(df.CONF_GRID_COLUMN_ALIGN): grid_alignments, cv.Optional(df.CONF_GRID_ROW_ALIGN): grid_alignments, + cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, + cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, }, df.TYPE_FLEX: { cv.Optional( @@ -312,6 +321,8 @@ LAYOUT_SCHEMA = { cv.Optional(df.CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments, cv.Optional(df.CONF_FLEX_ALIGN_CROSS, default="start"): flex_alignments, cv.Optional(df.CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments, + cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, + cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, }, }, lower=True, @@ -338,7 +349,6 @@ DISP_BG_SCHEMA = cv.Schema( } ) - # A style schema that can include text STYLED_TEXT_SCHEMA = cv.maybe_simple_value( STYLE_SCHEMA.extend(TEXT_SCHEMA), key=CONF_TEXT diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 603de6aa3e..4abb25c61d 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -20,6 +20,8 @@ from ..defines import ( CONF_GRID_ROWS, CONF_LAYOUT, CONF_MAIN, + CONF_PAD_COLUMN, + CONF_PAD_ROW, CONF_SCROLLBAR_MODE, CONF_STYLES, CONF_WIDGETS, @@ -29,6 +31,7 @@ from ..defines import ( TYPE_FLEX, TYPE_GRID, LValidator, + call_lambda, join_enums, literal, ) @@ -273,6 +276,10 @@ async def set_obj_properties(w: Widget, config): layout_type: str = layout[CONF_TYPE] add_lv_use(layout_type) lv_obj.set_layout(w.obj, literal(f"LV_LAYOUT_{layout_type.upper()}")) + if (pad_row := layout.get(CONF_PAD_ROW)) is not None: + w.set_style(CONF_PAD_ROW, pad_row, 0) + if (pad_column := layout.get(CONF_PAD_COLUMN)) is not None: + w.set_style(CONF_PAD_COLUMN, pad_column, 0) if layout_type == TYPE_GRID: wid = config[CONF_ID] rows = [str(x) for x in layout[CONF_GRID_ROWS]] @@ -316,8 +323,13 @@ async def set_obj_properties(w: Widget, config): flag_clr = set() flag_set = set() props = parts[CONF_MAIN][CONF_DEFAULT] + lambs = {} + flag_set = set() + flag_clr = set() for prop, value in {k: v for k, v in props.items() if k in OBJ_FLAGS}.items(): - if value: + if isinstance(value, cv.Lambda): + lambs[prop] = value + elif value: flag_set.add(prop) else: flag_clr.add(prop) @@ -327,6 +339,13 @@ async def set_obj_properties(w: Widget, config): if flag_clr: clrs = join_enums(flag_clr, "LV_OBJ_FLAG_") w.clear_flag(clrs) + for key, value in lambs.items(): + lamb = await cg.process_lambda(value, [], return_type=cg.bool_) + flag = f"LV_OBJ_FLAG_{key.upper()}" + with LvConditional(call_lambda(lamb)) as cond: + w.add_flag(flag) + cond.else_() + w.clear_flag(flag) if states := config.get(CONF_STATE): adds = set() @@ -348,7 +367,7 @@ async def set_obj_properties(w: Widget, config): for key, value in lambs.items(): lamb = await cg.process_lambda(value, [], return_type=cg.bool_) state = f"LV_STATE_{key.upper()}" - with LvConditional(f"{lamb}()") as cond: + with LvConditional(call_lambda(lamb)) as cond: w.add_state(state) cond.else_() w.clear_state(state) diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 002c7a118d..7ef7772ac9 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -127,3 +127,11 @@ binary_sensor: - platform: lvgl name: LVGL checkbox widget: checkbox_id + +wifi: + ssid: SSID + password: PASSWORD123 + +time: + platform: sntp + id: time_id diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 54022354f5..800d6eff27 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -16,8 +16,6 @@ lvgl: border_width: 0 radius: 0 pad_all: 0 - pad_row: 0 - pad_column: 0 border_color: 0x0077b3 text_color: 0xFFFFFF width: 100% @@ -55,6 +53,13 @@ lvgl: pages: - id: page1 skip: true + layout: + type: flex + pad_row: 4 + pad_column: 4px + flex_align_main: center + flex_align_cross: start + flex_align_track: end widgets: - animimg: height: 60 @@ -118,10 +123,8 @@ lvgl: outline_width: 10px pad_all: 10px pad_bottom: 10px - pad_column: 10px pad_left: 10px pad_right: 10px - pad_row: 10px pad_top: 10px shadow_color: light_blue shadow_ofs_x: 5 @@ -221,10 +224,47 @@ lvgl: - label: text: Button on_click: - lvgl.label.update: - id: hello_label - bg_color: 0x123456 - text: clicked + - lvgl.label.update: + id: hello_label + bg_color: 0x123456 + text: clicked + - lvgl.label.update: + id: hello_label + text: !lambda return "hello world"; + - lvgl.label.update: + id: hello_label + text: !lambda |- + ESP_LOGD("label", "multi-line lambda"); + return "hello world"; + - lvgl.label.update: + id: hello_label + text: !lambda 'return str_sprintf("Hello space");' + - lvgl.label.update: + id: hello_label + text: + format: "sprintf format %s" + args: ['x ? "checked" : "unchecked"'] + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + time: time_id + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + time: !lambda return id(time_id).now(); + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + time: !lambda |- + ESP_LOGD("label", "multi-line lambda"); + return id(time_id).now(); on_value: logger.log: format: "state now %d" @@ -396,6 +436,8 @@ lvgl: grid_row_align: end grid_rows: [25px, fr(1), content] grid_columns: [40, fr(1), fr(1)] + pad_row: 6px + pad_column: 0 widgets: - image: grid_cell_row_pos: 0 From 8b6d6fe6616f0c83c72c9ff395f74134a8956dd4 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 19 Aug 2024 00:45:10 +0200 Subject: [PATCH 0232/1052] [speaker] Fix header includes (#7304) --- esphome/components/speaker/speaker.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 142231881c..193049402d 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + namespace esphome { namespace speaker { From baedd74c7a5f54a871eb413a629d6dd94c15510a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:45:22 +1200 Subject: [PATCH 0233/1052] [microphone] Fix header includes (#7310) --- esphome/components/microphone/microphone.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/microphone/microphone.h b/esphome/components/microphone/microphone.h index e01a10e15c..883ca97710 100644 --- a/esphome/components/microphone/microphone.h +++ b/esphome/components/microphone/microphone.h @@ -1,6 +1,9 @@ #pragma once -#include "esphome/core/entity_base.h" +#include +#include +#include +#include #include "esphome/core/helpers.h" namespace esphome { From 7464b440c078794c0dec88c3a991e2c081695855 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sun, 18 Aug 2024 20:27:03 +1200 Subject: [PATCH 0234/1052] Revert "[validation] Allow ``maybe_simple_value`` to not have default key in complex value" (#7305) --- esphome/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 6d6cb451d6..719cc43b31 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1864,7 +1864,7 @@ def maybe_simple_value(*validators, **kwargs): if value == SCHEMA_EXTRACT: return (validator, key) - if isinstance(value, dict): + if isinstance(value, dict) and key in value: return validator(value) return validator({key: value}) From 5c7d070307c7a04e452cd641cdc3fa4baa477e18 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:43:23 +1000 Subject: [PATCH 0235/1052] [lvgl] Bug fixes (#7300) --- esphome/components/lvgl/defines.py | 32 ++++---- esphome/components/lvgl/lv_validation.py | 86 +++++++++++++-------- esphome/components/lvgl/schemas.py | 32 +++++--- esphome/components/lvgl/widgets/__init__.py | 23 +++++- tests/components/lvgl/common.yaml | 8 ++ tests/components/lvgl/lvgl-package.yaml | 58 ++++++++++++-- 6 files changed, 173 insertions(+), 66 deletions(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 8f7a973722..6a8b20b505 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -6,8 +6,8 @@ Constants already defined in esphome.const are not duplicated here and must be i from esphome import codegen as cg, config_validation as cv from esphome.const import CONF_ITEMS -from esphome.core import ID, Lambda -from esphome.cpp_generator import MockObj +from esphome.core import Lambda +from esphome.cpp_generator import LambdaExpression, MockObj from esphome.cpp_types import uint32 from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -22,19 +22,22 @@ def literal(arg): return arg +def call_lambda(lamb: LambdaExpression): + expr = lamb.content.strip() + if expr.startswith("return") and expr.endswith(";"): + return expr[7:][:-1] + return f"{lamb}()" + + class LValidator: """ A validator for a particular type used in LVGL. Usable in configs as a validator, also has `process()` to convert a value during code generation """ - def __init__( - self, validator, rtype, idtype=None, idexpr=None, retmapper=None, requires=None - ): + def __init__(self, validator, rtype, retmapper=None, requires=None): self.validator = validator self.rtype = rtype - self.idtype = idtype - self.idexpr = idexpr self.retmapper = retmapper self.requires = requires @@ -43,8 +46,6 @@ class LValidator: value = requires_component(self.requires)(value) if isinstance(value, cv.Lambda): return cv.returning_lambda(value) - if self.idtype is not None and isinstance(value, ID): - return cv.use_id(self.idtype)(value) return self.validator(value) async def process(self, value, args=()): @@ -52,10 +53,10 @@ class LValidator: return None if isinstance(value, Lambda): return cg.RawExpression( - f"{await cg.process_lambda(value, args, return_type=self.rtype)}()" + call_lambda( + await cg.process_lambda(value, args, return_type=self.rtype) + ) ) - if self.idtype is not None and isinstance(value, ID): - return cg.RawExpression(f"{value}->{self.idexpr}") if self.retmapper is not None: return self.retmapper(value) return cg.safe_exp(value) @@ -89,7 +90,7 @@ class LvConstant(LValidator): cv.ensure_list(self.one_of), uint32, retmapper=self.mapper ) - def mapper(self, value, args=()): + def mapper(self, value): if not isinstance(value, list): value = [value] return literal( @@ -103,7 +104,7 @@ class LvConstant(LValidator): def extend(self, *choices): """ - Extend an LVCconstant with additional choices. + Extend an LVconstant with additional choices. :param choices: The extra choices :return: A new LVConstant instance """ @@ -431,6 +432,8 @@ CONF_ONE_LINE = "one_line" CONF_ON_SELECT = "on_select" CONF_ONE_CHECKED = "one_checked" CONF_NEXT = "next" +CONF_PAD_ROW = "pad_row" +CONF_PAD_COLUMN = "pad_column" CONF_PAGE = "page" CONF_PAGE_WRAP = "page_wrap" CONF_PASSWORD_MODE = "password_mode" @@ -462,6 +465,7 @@ CONF_SKIP = "skip" CONF_SYMBOL = "symbol" CONF_TAB_ID = "tab_id" CONF_TABS = "tabs" +CONF_TIME_FORMAT = "time_format" CONF_TILE = "tile" CONF_TILE_ID = "tile_id" CONF_TILES = "tiles" diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index b351b84af6..a2be4a2abe 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,17 +1,14 @@ from typing import Union import esphome.codegen as cg -from esphome.components.binary_sensor import BinarySensor from esphome.components.color import ColorStruct from esphome.components.font import Font from esphome.components.image import Image_ -from esphome.components.sensor import Sensor -from esphome.components.text_sensor import TextSensor import esphome.config_validation as cv -from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_VALUE -from esphome.core import HexInt +from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_TIME, CONF_VALUE +from esphome.core import HexInt, Lambda from esphome.cpp_generator import MockObj -from esphome.cpp_types import uint32 +from esphome.cpp_types import ESPTime, uint32 from esphome.helpers import cpp_string_escape from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -19,9 +16,11 @@ from . import types as ty from .defines import ( CONF_END_VALUE, CONF_START_VALUE, + CONF_TIME_FORMAT, LV_FONTS, LValidator, LvConstant, + call_lambda, literal, ) from .helpers import ( @@ -110,13 +109,13 @@ def angle(value): def size_validator(value): """A size in one axis - one of "size_content", a number (pixels) or a percentage""" if value == SCHEMA_EXTRACT: - return ["size_content", "pixels", "..%"] + return ["SIZE_CONTENT", "number of pixels", "percentage"] if isinstance(value, str) and value.lower().endswith("px"): value = cv.int_(value[:-2]) if isinstance(value, str) and not value.endswith("%"): if value.upper() == "SIZE_CONTENT": return "LV_SIZE_CONTENT" - raise cv.Invalid("must be 'size_content', a pixel position or a percentage") + raise cv.Invalid("must be 'size_content', a percentage or an integer (pixels)") if isinstance(value, int): return cv.int_(value) # Will throw an exception if not a percentage. @@ -125,6 +124,15 @@ def size_validator(value): size = LValidator(size_validator, uint32, retmapper=literal) + +def pixels_validator(value): + if isinstance(value, str) and value.lower().endswith("px"): + return cv.int_(value[:-2]) + return cv.int_(value) + + +pixels = LValidator(pixels_validator, uint32, retmapper=literal) + radius_consts = LvConstant("LV_RADIUS_", "CIRCLE") @@ -167,9 +175,7 @@ lv_image = LValidator( retmapper=lambda x: lv_expr.img_from(MockObj(x)), requires="image", ) -lv_bool = LValidator( - cv.boolean, cg.bool_, BinarySensor, "get_state()", retmapper=literal -) +lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) def lv_pct(value: Union[int, float]): @@ -185,42 +191,60 @@ def lvms_validator_(value): lv_milliseconds = LValidator( - lvms_validator_, - cg.int32, - retmapper=lambda x: x.total_milliseconds, + lvms_validator_, cg.int32, retmapper=lambda x: x.total_milliseconds ) class TextValidator(LValidator): def __init__(self): - super().__init__( - cv.string, - cg.const_char_ptr, - TextSensor, - "get_state().c_str()", - lambda s: cg.safe_exp(f"{s}"), - ) + super().__init__(cv.string, cg.std_string, lambda s: cg.safe_exp(f"{s}")) def __call__(self, value): - if isinstance(value, dict): + if isinstance(value, dict) and CONF_FORMAT in value: return value return super().__call__(value) async def process(self, value, args=()): if isinstance(value, dict): - args = [str(x) for x in value[CONF_ARGS]] - arg_expr = cg.RawExpression(",".join(args)) - format_str = cpp_string_escape(value[CONF_FORMAT]) - return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") + if format_str := value.get(CONF_FORMAT): + args = [str(x) for x in value[CONF_ARGS]] + arg_expr = cg.RawExpression(",".join(args)) + format_str = cpp_string_escape(format_str) + return literal(f"str_sprintf({format_str}, {arg_expr}).c_str()") + if time_format := value.get(CONF_TIME_FORMAT): + source = value[CONF_TIME] + if isinstance(source, Lambda): + time_format = cpp_string_escape(time_format) + return cg.RawExpression( + call_lambda( + await cg.process_lambda(source, args, return_type=ESPTime) + ) + + f".strftime({time_format}).c_str()" + ) + # must be an ID + source = await cg.get_variable(source) + return source.now().strftime(time_format).c_str() + if isinstance(value, Lambda): + value = call_lambda( + await cg.process_lambda(value, args, return_type=self.rtype) + ) + + # Was the lambda call reduced to a string? + if value.endswith("c_str()") or ( + value.endswith('"') and value.startswith('"') + ): + pass + else: + # Either a std::string or a lambda call returning that. We need const char* + value = f"({value}).c_str()" + return cg.RawExpression(value) return await super().process(value, args) lv_text = TextValidator() -lv_float = LValidator(cv.float_, cg.float_, Sensor, "get_state()") -lv_int = LValidator(cv.int_, cg.int_, Sensor, "get_state()") -lv_brightness = LValidator( - cv.percentage, cg.float_, Sensor, "get_state()", retmapper=lambda x: int(x * 255) -) +lv_float = LValidator(cv.float_, cg.float_) +lv_int = LValidator(cv.int_, cg.int_) +lv_brightness = LValidator(cv.percentage, cg.float_, retmapper=lambda x: int(x * 255)) def is_lv_font(font): diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index f1c7ff4df6..e9714e3b1a 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -1,5 +1,6 @@ from esphome import config_validation as cv from esphome.automation import Trigger, validate_automation +from esphome.components.time import RealTimeClock from esphome.const import ( CONF_ARGS, CONF_FORMAT, @@ -8,6 +9,7 @@ from esphome.const import ( CONF_ON_VALUE, CONF_STATE, CONF_TEXT, + CONF_TIME, CONF_TRIGGER_ID, CONF_TYPE, ) @@ -15,6 +17,7 @@ from esphome.core import TimePeriod from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid +from .defines import CONF_TIME_FORMAT from .helpers import add_lv_use, requires_component, validate_printf from .lv_validation import lv_color, lv_font, lv_image from .lvcode import LvglComponent @@ -46,7 +49,13 @@ TEXT_SCHEMA = cv.Schema( ), validate_printf, ), - lvalid.lv_text, + cv.Schema( + { + cv.Required(CONF_TIME_FORMAT): cv.string, + cv.GenerateID(CONF_TIME): cv.templatable(cv.use_id(RealTimeClock)), + } + ), + cv.templatable(cv.string), ) } ) @@ -116,15 +125,13 @@ STYLE_PROPS = { "opa_layered": lvalid.opacity, "outline_color": lvalid.lv_color, "outline_opa": lvalid.opacity, - "outline_pad": lvalid.size, - "outline_width": lvalid.size, - "pad_all": lvalid.size, - "pad_bottom": lvalid.size, - "pad_column": lvalid.size, - "pad_left": lvalid.size, - "pad_right": lvalid.size, - "pad_row": lvalid.size, - "pad_top": lvalid.size, + "outline_pad": lvalid.pixels, + "outline_width": lvalid.pixels, + "pad_all": lvalid.pixels, + "pad_bottom": lvalid.pixels, + "pad_left": lvalid.pixels, + "pad_right": lvalid.pixels, + "pad_top": lvalid.pixels, "shadow_color": lvalid.lv_color, "shadow_ofs_x": cv.int_, "shadow_ofs_y": cv.int_, @@ -304,6 +311,8 @@ LAYOUT_SCHEMA = { cv.Required(df.CONF_GRID_COLUMNS): [grid_spec], cv.Optional(df.CONF_GRID_COLUMN_ALIGN): grid_alignments, cv.Optional(df.CONF_GRID_ROW_ALIGN): grid_alignments, + cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, + cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, }, df.TYPE_FLEX: { cv.Optional( @@ -312,6 +321,8 @@ LAYOUT_SCHEMA = { cv.Optional(df.CONF_FLEX_ALIGN_MAIN, default="start"): flex_alignments, cv.Optional(df.CONF_FLEX_ALIGN_CROSS, default="start"): flex_alignments, cv.Optional(df.CONF_FLEX_ALIGN_TRACK, default="start"): flex_alignments, + cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, + cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, }, }, lower=True, @@ -338,7 +349,6 @@ DISP_BG_SCHEMA = cv.Schema( } ) - # A style schema that can include text STYLED_TEXT_SCHEMA = cv.maybe_simple_value( STYLE_SCHEMA.extend(TEXT_SCHEMA), key=CONF_TEXT diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 603de6aa3e..4abb25c61d 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -20,6 +20,8 @@ from ..defines import ( CONF_GRID_ROWS, CONF_LAYOUT, CONF_MAIN, + CONF_PAD_COLUMN, + CONF_PAD_ROW, CONF_SCROLLBAR_MODE, CONF_STYLES, CONF_WIDGETS, @@ -29,6 +31,7 @@ from ..defines import ( TYPE_FLEX, TYPE_GRID, LValidator, + call_lambda, join_enums, literal, ) @@ -273,6 +276,10 @@ async def set_obj_properties(w: Widget, config): layout_type: str = layout[CONF_TYPE] add_lv_use(layout_type) lv_obj.set_layout(w.obj, literal(f"LV_LAYOUT_{layout_type.upper()}")) + if (pad_row := layout.get(CONF_PAD_ROW)) is not None: + w.set_style(CONF_PAD_ROW, pad_row, 0) + if (pad_column := layout.get(CONF_PAD_COLUMN)) is not None: + w.set_style(CONF_PAD_COLUMN, pad_column, 0) if layout_type == TYPE_GRID: wid = config[CONF_ID] rows = [str(x) for x in layout[CONF_GRID_ROWS]] @@ -316,8 +323,13 @@ async def set_obj_properties(w: Widget, config): flag_clr = set() flag_set = set() props = parts[CONF_MAIN][CONF_DEFAULT] + lambs = {} + flag_set = set() + flag_clr = set() for prop, value in {k: v for k, v in props.items() if k in OBJ_FLAGS}.items(): - if value: + if isinstance(value, cv.Lambda): + lambs[prop] = value + elif value: flag_set.add(prop) else: flag_clr.add(prop) @@ -327,6 +339,13 @@ async def set_obj_properties(w: Widget, config): if flag_clr: clrs = join_enums(flag_clr, "LV_OBJ_FLAG_") w.clear_flag(clrs) + for key, value in lambs.items(): + lamb = await cg.process_lambda(value, [], return_type=cg.bool_) + flag = f"LV_OBJ_FLAG_{key.upper()}" + with LvConditional(call_lambda(lamb)) as cond: + w.add_flag(flag) + cond.else_() + w.clear_flag(flag) if states := config.get(CONF_STATE): adds = set() @@ -348,7 +367,7 @@ async def set_obj_properties(w: Widget, config): for key, value in lambs.items(): lamb = await cg.process_lambda(value, [], return_type=cg.bool_) state = f"LV_STATE_{key.upper()}" - with LvConditional(f"{lamb}()") as cond: + with LvConditional(call_lambda(lamb)) as cond: w.add_state(state) cond.else_() w.clear_state(state) diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 002c7a118d..7ef7772ac9 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -127,3 +127,11 @@ binary_sensor: - platform: lvgl name: LVGL checkbox widget: checkbox_id + +wifi: + ssid: SSID + password: PASSWORD123 + +time: + platform: sntp + id: time_id diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 54022354f5..800d6eff27 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -16,8 +16,6 @@ lvgl: border_width: 0 radius: 0 pad_all: 0 - pad_row: 0 - pad_column: 0 border_color: 0x0077b3 text_color: 0xFFFFFF width: 100% @@ -55,6 +53,13 @@ lvgl: pages: - id: page1 skip: true + layout: + type: flex + pad_row: 4 + pad_column: 4px + flex_align_main: center + flex_align_cross: start + flex_align_track: end widgets: - animimg: height: 60 @@ -118,10 +123,8 @@ lvgl: outline_width: 10px pad_all: 10px pad_bottom: 10px - pad_column: 10px pad_left: 10px pad_right: 10px - pad_row: 10px pad_top: 10px shadow_color: light_blue shadow_ofs_x: 5 @@ -221,10 +224,47 @@ lvgl: - label: text: Button on_click: - lvgl.label.update: - id: hello_label - bg_color: 0x123456 - text: clicked + - lvgl.label.update: + id: hello_label + bg_color: 0x123456 + text: clicked + - lvgl.label.update: + id: hello_label + text: !lambda return "hello world"; + - lvgl.label.update: + id: hello_label + text: !lambda |- + ESP_LOGD("label", "multi-line lambda"); + return "hello world"; + - lvgl.label.update: + id: hello_label + text: !lambda 'return str_sprintf("Hello space");' + - lvgl.label.update: + id: hello_label + text: + format: "sprintf format %s" + args: ['x ? "checked" : "unchecked"'] + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + time: time_id + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + time: !lambda return id(time_id).now(); + - lvgl.label.update: + id: hello_label + text: + time_format: "%c" + time: !lambda |- + ESP_LOGD("label", "multi-line lambda"); + return id(time_id).now(); on_value: logger.log: format: "state now %d" @@ -396,6 +436,8 @@ lvgl: grid_row_align: end grid_rows: [25px, fr(1), content] grid_columns: [40, fr(1), fr(1)] + pad_row: 6px + pad_column: 0 widgets: - image: grid_cell_row_pos: 0 From 0f82114e64f50a167c85374ec299c43fd2cd84ff Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 19 Aug 2024 00:45:10 +0200 Subject: [PATCH 0236/1052] [speaker] Fix header includes (#7304) --- esphome/components/speaker/speaker.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 142231881c..193049402d 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + namespace esphome { namespace speaker { From c96784f59108b476597a6f09a83de358d18ef3b2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:45:22 +1200 Subject: [PATCH 0237/1052] [microphone] Fix header includes (#7310) --- esphome/components/microphone/microphone.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/microphone/microphone.h b/esphome/components/microphone/microphone.h index e01a10e15c..883ca97710 100644 --- a/esphome/components/microphone/microphone.h +++ b/esphome/components/microphone/microphone.h @@ -1,6 +1,9 @@ #pragma once -#include "esphome/core/entity_base.h" +#include +#include +#include +#include #include "esphome/core/helpers.h" namespace esphome { From 409e84090eff4d3c8aa536e2077f036cd51cda54 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 19 Aug 2024 13:09:59 +1200 Subject: [PATCH 0238/1052] Bump version to 2024.8.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 39d2ee74a1..a321ddd19f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.0b2" +__version__ = "2024.8.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From b425912a80aea1ed71ebf5e3515963f09f559832 Mon Sep 17 00:00:00 2001 From: Roving Ronin <108674933+Roving-Ronin@users.noreply.github.com> Date: Tue, 20 Aug 2024 07:18:06 +1000 Subject: [PATCH 0239/1052] Update const.py - Add missing UNIT_LITRE (#7317) --- esphome/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/const.py b/esphome/const.py index 6157ce32f7..b9c37a53a8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1042,6 +1042,7 @@ UNIT_KILOVOLT_AMPS_REACTIVE = "kVAR" UNIT_KILOVOLT_AMPS_REACTIVE_HOURS = "kVARh" UNIT_KILOWATT = "kW" UNIT_KILOWATT_HOURS = "kWh" +UNIT_LITRE = "L" UNIT_LUX = "lx" UNIT_METER = "m" UNIT_METER_PER_SECOND_SQUARED = "m/s²" From 1ffee9c4d2d89823346b48585a987e9ab7233a93 Mon Sep 17 00:00:00 2001 From: Ali Jafri Date: Tue, 20 Aug 2024 03:12:41 +0530 Subject: [PATCH 0240/1052] Fix RP2040 Neopixel flickering issue (#7307) --- .../rp2040_pio_led_strip/led_strip.cpp | 36 ++++++++++++++++--- .../rp2040_pio_led_strip/led_strip.h | 5 +++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.cpp b/esphome/components/rp2040_pio_led_strip/led_strip.cpp index 3e5e82898d..2aaa2ceb19 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.cpp +++ b/esphome/components/rp2040_pio_led_strip/led_strip.cpp @@ -7,8 +7,10 @@ #include #include +#include #include #include +#include namespace esphome { namespace rp2040_pio_led_strip { @@ -23,6 +25,19 @@ static std::map conf_count_ = { {CHIPSET_WS2812, false}, {CHIPSET_WS2812B, false}, {CHIPSET_SK6812, false}, {CHIPSET_SM16703, false}, {CHIPSET_CUSTOM, false}, }; +static bool dma_chan_active_[12]; +static struct semaphore dma_write_complete_sem_[12]; + +// DMA interrupt service routine +void RP2040PIOLEDStripLightOutput::dma_write_complete_handler_() { + uint32_t channel = dma_hw->ints0; + for (uint dma_chan = 0; dma_chan < 12; ++dma_chan) { + if (RP2040PIOLEDStripLightOutput::dma_chan_active_[dma_chan] && (channel & (1u << dma_chan))) { + dma_hw->ints0 = (1u << dma_chan); // Clear the interrupt + sem_release(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[dma_chan]); // Handle the interrupt + } + } +} void RP2040PIOLEDStripLightOutput::setup() { ESP_LOGCONFIG(TAG, "Setting up RP2040 LED Strip..."); @@ -57,22 +72,22 @@ void RP2040PIOLEDStripLightOutput::setup() { // but there are only 4 state machines on each PIO so we can only have 4 strips per PIO uint offset = 0; - if (num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) { + if (RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) { ESP_LOGE(TAG, "Too many instances of PIO program"); this->mark_failed(); return; } // keep track of how many instances of the PIO program are running on each PIO - num_instance_[this->pio_ == pio0 ? 0 : 1]++; + RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1]++; // if there are multiple strips of the same chipset, we can reuse the same PIO program and save space if (this->conf_count_[this->chipset_]) { - offset = chipset_offsets_[this->chipset_]; + offset = RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_]; } else { // Load the assembled program into the PIO and get its location in the PIO's instruction memory and save it offset = pio_add_program(this->pio_, this->program_); - chipset_offsets_[this->chipset_] = offset; - conf_count_[this->chipset_] = true; + RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_] = offset; + RP2040PIOLEDStripLightOutput::conf_count_[this->chipset_] = true; } // Configure the state machine's PIO, and start it @@ -93,6 +108,9 @@ void RP2040PIOLEDStripLightOutput::setup() { return; } + // Mark the DMA channel as active + RP2040PIOLEDStripLightOutput::dma_chan_active_[this->dma_chan_] = true; + this->dma_config_ = dma_channel_get_default_config(this->dma_chan_); channel_config_set_transfer_data_size( &this->dma_config_, @@ -109,6 +127,13 @@ void RP2040PIOLEDStripLightOutput::setup() { false // don't start yet ); + // Initialize the semaphore for this DMA channel + sem_init(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_], 1, 1); + + irq_set_exclusive_handler(DMA_IRQ_0, dma_write_complete_handler_); // after DMA all data, raise an interrupt + dma_channel_set_irq0_enabled(this->dma_chan_, true); // map DMA channel to interrupt + irq_set_enabled(DMA_IRQ_0, true); // enable interrupt + this->init_(this->pio_, this->sm_, offset, this->pin_, this->max_refresh_rate_); } @@ -126,6 +151,7 @@ void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) { } // the bits are already in the correct order for the pio program so we can just copy the buffer using DMA + sem_acquire_blocking(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_]); dma_channel_transfer_from_buffer_now(this->dma_chan_, this->buf_, this->get_buffer_size_()); } diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.h b/esphome/components/rp2040_pio_led_strip/led_strip.h index 9976842f02..7b62648974 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.h +++ b/esphome/components/rp2040_pio_led_strip/led_strip.h @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace esphome { @@ -95,6 +96,8 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight { size_t get_buffer_size_() const { return this->num_leds_ * (3 + this->is_rgbw_); } + static void dma_write_complete_handler_(); + uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; @@ -120,6 +123,8 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight { inline static int num_instance_[2]; inline static std::map conf_count_; inline static std::map chipset_offsets_; + inline static bool dma_chan_active_[12]; + inline static struct semaphore dma_write_complete_sem_[12]; }; } // namespace rp2040_pio_led_strip From 30414667d023c18767852d12f3da7bc119c6ae1e Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Tue, 20 Aug 2024 00:22:19 +0200 Subject: [PATCH 0241/1052] add the ability to add more idf components to an existing setup (#7302) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 0a5dd46478..b630c7638e 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -172,6 +172,19 @@ def add_idf_component( KEY_COMPONENTS: components, KEY_SUBMODULES: submodules, } + else: + component_config = CORE.data[KEY_ESP32][KEY_COMPONENTS][name] + if components is not None: + component_config[KEY_COMPONENTS] = list( + set(component_config[KEY_COMPONENTS] + components) + ) + if submodules is not None: + if component_config[KEY_SUBMODULES] is None: + component_config[KEY_SUBMODULES] = submodules + else: + component_config[KEY_SUBMODULES] = list( + set(component_config[KEY_SUBMODULES] + submodules) + ) def add_extra_script(stage: str, filename: str, path: str): From 3cbdf63f567621bf559f7cf82b05d078b56a8e28 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 20 Aug 2024 00:53:15 +0200 Subject: [PATCH 0242/1052] [code-quality] fix clang-tidy socket (#7285) --- esphome/components/socket/socket.cpp | 2 ++ esphome/components/socket/socket.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/esphome/components/socket/socket.cpp b/esphome/components/socket/socket.cpp index 5d3528dad8..e260fce05e 100644 --- a/esphome/components/socket/socket.cpp +++ b/esphome/components/socket/socket.cpp @@ -1,4 +1,5 @@ #include "socket.h" +#if defined(USE_SOCKET_IMPL_LWIP_TCP) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || defined(USE_SOCKET_IMPL_BSD_SOCKETS) #include #include #include @@ -74,3 +75,4 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po } } // namespace socket } // namespace esphome +#endif diff --git a/esphome/components/socket/socket.h b/esphome/components/socket/socket.h index 5c12210d15..cefdb51e0d 100644 --- a/esphome/components/socket/socket.h +++ b/esphome/components/socket/socket.h @@ -5,6 +5,7 @@ #include "esphome/core/optional.h" #include "headers.h" +#if defined(USE_SOCKET_IMPL_LWIP_TCP) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) || defined(USE_SOCKET_IMPL_BSD_SOCKETS) namespace esphome { namespace socket { @@ -57,3 +58,4 @@ socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t po } // namespace socket } // namespace esphome +#endif From fa497d06b047334de87267ade785c9394b0074f5 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 21 Aug 2024 00:01:50 +0200 Subject: [PATCH 0243/1052] [code-quality] fix clang-tidy cstddef (#7324) --- esphome/components/microphone/microphone.h | 2 +- esphome/components/speaker/speaker.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/microphone/microphone.h b/esphome/components/microphone/microphone.h index 883ca97710..914ad80bea 100644 --- a/esphome/components/microphone/microphone.h +++ b/esphome/components/microphone/microphone.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 193049402d..375ccc4e8c 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include From bd3d065a23693340fffdc887178e76ca93830be2 Mon Sep 17 00:00:00 2001 From: Sung-jin Brian Hong Date: Wed, 21 Aug 2024 08:44:21 +0900 Subject: [PATCH 0244/1052] Fix waveshare 2.13" epaper stride calculation error (#7303) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/waveshare_epaper/waveshare_epaper.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 24df428e6f..7c1d436673 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -480,7 +480,7 @@ void HOT WaveshareEPaperTypeA::display() { this->start_data_(); switch (this->model_) { case TTGO_EPAPER_2_13_IN_B1: { // block needed because of variable initializations - int16_t wb = ((this->get_width_internal()) >> 3); + int16_t wb = ((this->get_width_controller()) >> 3); for (int i = 0; i < this->get_height_internal(); i++) { for (int j = 0; j < wb; j++) { int idx = j + (this->get_height_internal() - 1 - i) * wb; @@ -766,7 +766,7 @@ void WaveshareEPaper2P7InV2::initialize() { // XRAM_START_AND_END_POSITION this->command(0x44); this->data(0x00); - this->data(((get_width_internal() - 1) >> 3) & 0xFF); + this->data(((this->get_width_controller() - 1) >> 3) & 0xFF); // YRAM_START_AND_END_POSITION this->command(0x45); this->data(0x00); @@ -928,8 +928,8 @@ void HOT WaveshareEPaper2P7InB::display() { // TCON_RESOLUTION this->command(0x61); - this->data(this->get_width_internal() >> 8); - this->data(this->get_width_internal() & 0xff); // 176 + this->data(this->get_width_controller() >> 8); + this->data(this->get_width_controller() & 0xff); // 176 this->data(this->get_height_internal() >> 8); this->data(this->get_height_internal() & 0xff); // 264 @@ -994,7 +994,7 @@ void WaveshareEPaper2P7InBV2::initialize() { // self.SetWindows(0, 0, self.width-1, self.height-1) // SetWindows(self, Xstart, Ystart, Xend, Yend): - uint32_t xend = this->get_width_internal() - 1; + uint32_t xend = this->get_width_controller() - 1; uint32_t yend = this->get_height_internal() - 1; this->command(0x44); this->data(0x00); From 848fd0442d67dece75bf8eddf6a5242ead5b6dc4 Mon Sep 17 00:00:00 2001 From: NewoPL <27411874+NewoPL@users.noreply.github.com> Date: Wed, 21 Aug 2024 01:46:15 +0200 Subject: [PATCH 0245/1052] [rtttl] fix STOPPED state (#7323) --- esphome/components/rtttl/rtttl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index a97120499d..495b5c1c8a 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -32,7 +32,7 @@ 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, "RTTL Component is already playing: %s", name.c_str()); + ESP_LOGW(TAG, "RTTTL Component is already playing: %s", name.c_str()); return; } @@ -122,6 +122,7 @@ void Rtttl::stop() { #ifdef USE_OUTPUT if (this->output_ != nullptr) { this->output_->set_level(0.0); + this->set_state_(STATE_STOPPED); } #endif #ifdef USE_SPEAKER @@ -129,10 +130,10 @@ void Rtttl::stop() { if (this->speaker_->is_running()) { this->speaker_->stop(); } + this->set_state_(STATE_STOPPING); } #endif this->note_duration_ = 0; - this->set_state_(STATE_STOPPING); } void Rtttl::loop() { @@ -342,6 +343,7 @@ void Rtttl::finish_() { #ifdef USE_OUTPUT if (this->output_ != nullptr) { this->output_->set_level(0.0); + this->set_state_(State::STATE_STOPPED); } #endif #ifdef USE_SPEAKER @@ -354,9 +356,9 @@ void Rtttl::finish_() { this->speaker_->play((uint8_t *) (&sample), 8); this->speaker_->finish(); + this->set_state_(State::STATE_STOPPING); } #endif - this->set_state_(State::STATE_STOPPING); this->note_duration_ = 0; this->on_finished_playback_callback_.call(); ESP_LOGD(TAG, "Playback finished"); From 8fae60931622771feb31af6ee788e4cf8ac96f9c Mon Sep 17 00:00:00 2001 From: Ali Jafri Date: Tue, 20 Aug 2024 03:12:41 +0530 Subject: [PATCH 0246/1052] Fix RP2040 Neopixel flickering issue (#7307) --- .../rp2040_pio_led_strip/led_strip.cpp | 36 ++++++++++++++++--- .../rp2040_pio_led_strip/led_strip.h | 5 +++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.cpp b/esphome/components/rp2040_pio_led_strip/led_strip.cpp index 3e5e82898d..2aaa2ceb19 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.cpp +++ b/esphome/components/rp2040_pio_led_strip/led_strip.cpp @@ -7,8 +7,10 @@ #include #include +#include #include #include +#include namespace esphome { namespace rp2040_pio_led_strip { @@ -23,6 +25,19 @@ static std::map conf_count_ = { {CHIPSET_WS2812, false}, {CHIPSET_WS2812B, false}, {CHIPSET_SK6812, false}, {CHIPSET_SM16703, false}, {CHIPSET_CUSTOM, false}, }; +static bool dma_chan_active_[12]; +static struct semaphore dma_write_complete_sem_[12]; + +// DMA interrupt service routine +void RP2040PIOLEDStripLightOutput::dma_write_complete_handler_() { + uint32_t channel = dma_hw->ints0; + for (uint dma_chan = 0; dma_chan < 12; ++dma_chan) { + if (RP2040PIOLEDStripLightOutput::dma_chan_active_[dma_chan] && (channel & (1u << dma_chan))) { + dma_hw->ints0 = (1u << dma_chan); // Clear the interrupt + sem_release(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[dma_chan]); // Handle the interrupt + } + } +} void RP2040PIOLEDStripLightOutput::setup() { ESP_LOGCONFIG(TAG, "Setting up RP2040 LED Strip..."); @@ -57,22 +72,22 @@ void RP2040PIOLEDStripLightOutput::setup() { // but there are only 4 state machines on each PIO so we can only have 4 strips per PIO uint offset = 0; - if (num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) { + if (RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) { ESP_LOGE(TAG, "Too many instances of PIO program"); this->mark_failed(); return; } // keep track of how many instances of the PIO program are running on each PIO - num_instance_[this->pio_ == pio0 ? 0 : 1]++; + RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1]++; // if there are multiple strips of the same chipset, we can reuse the same PIO program and save space if (this->conf_count_[this->chipset_]) { - offset = chipset_offsets_[this->chipset_]; + offset = RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_]; } else { // Load the assembled program into the PIO and get its location in the PIO's instruction memory and save it offset = pio_add_program(this->pio_, this->program_); - chipset_offsets_[this->chipset_] = offset; - conf_count_[this->chipset_] = true; + RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_] = offset; + RP2040PIOLEDStripLightOutput::conf_count_[this->chipset_] = true; } // Configure the state machine's PIO, and start it @@ -93,6 +108,9 @@ void RP2040PIOLEDStripLightOutput::setup() { return; } + // Mark the DMA channel as active + RP2040PIOLEDStripLightOutput::dma_chan_active_[this->dma_chan_] = true; + this->dma_config_ = dma_channel_get_default_config(this->dma_chan_); channel_config_set_transfer_data_size( &this->dma_config_, @@ -109,6 +127,13 @@ void RP2040PIOLEDStripLightOutput::setup() { false // don't start yet ); + // Initialize the semaphore for this DMA channel + sem_init(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_], 1, 1); + + irq_set_exclusive_handler(DMA_IRQ_0, dma_write_complete_handler_); // after DMA all data, raise an interrupt + dma_channel_set_irq0_enabled(this->dma_chan_, true); // map DMA channel to interrupt + irq_set_enabled(DMA_IRQ_0, true); // enable interrupt + this->init_(this->pio_, this->sm_, offset, this->pin_, this->max_refresh_rate_); } @@ -126,6 +151,7 @@ void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) { } // the bits are already in the correct order for the pio program so we can just copy the buffer using DMA + sem_acquire_blocking(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_]); dma_channel_transfer_from_buffer_now(this->dma_chan_, this->buf_, this->get_buffer_size_()); } diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.h b/esphome/components/rp2040_pio_led_strip/led_strip.h index 9976842f02..7b62648974 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.h +++ b/esphome/components/rp2040_pio_led_strip/led_strip.h @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace esphome { @@ -95,6 +96,8 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight { size_t get_buffer_size_() const { return this->num_leds_ * (3 + this->is_rgbw_); } + static void dma_write_complete_handler_(); + uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; @@ -120,6 +123,8 @@ class RP2040PIOLEDStripLightOutput : public light::AddressableLight { inline static int num_instance_[2]; inline static std::map conf_count_; inline static std::map chipset_offsets_; + inline static bool dma_chan_active_[12]; + inline static struct semaphore dma_write_complete_sem_[12]; }; } // namespace rp2040_pio_led_strip From c043bbe598f7b3d92c2a12969b1f2fbecc1f4fb6 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Tue, 20 Aug 2024 00:22:19 +0200 Subject: [PATCH 0247/1052] add the ability to add more idf components to an existing setup (#7302) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 0a5dd46478..b630c7638e 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -172,6 +172,19 @@ def add_idf_component( KEY_COMPONENTS: components, KEY_SUBMODULES: submodules, } + else: + component_config = CORE.data[KEY_ESP32][KEY_COMPONENTS][name] + if components is not None: + component_config[KEY_COMPONENTS] = list( + set(component_config[KEY_COMPONENTS] + components) + ) + if submodules is not None: + if component_config[KEY_SUBMODULES] is None: + component_config[KEY_SUBMODULES] = submodules + else: + component_config[KEY_SUBMODULES] = list( + set(component_config[KEY_SUBMODULES] + submodules) + ) def add_extra_script(stage: str, filename: str, path: str): From 436c6282da1a1c784ab7365f99e59fc00f88cf0f Mon Sep 17 00:00:00 2001 From: Sung-jin Brian Hong Date: Wed, 21 Aug 2024 08:44:21 +0900 Subject: [PATCH 0248/1052] Fix waveshare 2.13" epaper stride calculation error (#7303) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/waveshare_epaper/waveshare_epaper.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 24df428e6f..7c1d436673 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -480,7 +480,7 @@ void HOT WaveshareEPaperTypeA::display() { this->start_data_(); switch (this->model_) { case TTGO_EPAPER_2_13_IN_B1: { // block needed because of variable initializations - int16_t wb = ((this->get_width_internal()) >> 3); + int16_t wb = ((this->get_width_controller()) >> 3); for (int i = 0; i < this->get_height_internal(); i++) { for (int j = 0; j < wb; j++) { int idx = j + (this->get_height_internal() - 1 - i) * wb; @@ -766,7 +766,7 @@ void WaveshareEPaper2P7InV2::initialize() { // XRAM_START_AND_END_POSITION this->command(0x44); this->data(0x00); - this->data(((get_width_internal() - 1) >> 3) & 0xFF); + this->data(((this->get_width_controller() - 1) >> 3) & 0xFF); // YRAM_START_AND_END_POSITION this->command(0x45); this->data(0x00); @@ -928,8 +928,8 @@ void HOT WaveshareEPaper2P7InB::display() { // TCON_RESOLUTION this->command(0x61); - this->data(this->get_width_internal() >> 8); - this->data(this->get_width_internal() & 0xff); // 176 + this->data(this->get_width_controller() >> 8); + this->data(this->get_width_controller() & 0xff); // 176 this->data(this->get_height_internal() >> 8); this->data(this->get_height_internal() & 0xff); // 264 @@ -994,7 +994,7 @@ void WaveshareEPaper2P7InBV2::initialize() { // self.SetWindows(0, 0, self.width-1, self.height-1) // SetWindows(self, Xstart, Ystart, Xend, Yend): - uint32_t xend = this->get_width_internal() - 1; + uint32_t xend = this->get_width_controller() - 1; uint32_t yend = this->get_height_internal() - 1; this->command(0x44); this->data(0x00); From aaae8f4a87d2ed38c35e16705cea0186120e876e Mon Sep 17 00:00:00 2001 From: NewoPL <27411874+NewoPL@users.noreply.github.com> Date: Wed, 21 Aug 2024 01:46:15 +0200 Subject: [PATCH 0249/1052] [rtttl] fix STOPPED state (#7323) --- esphome/components/rtttl/rtttl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index a97120499d..495b5c1c8a 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -32,7 +32,7 @@ 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, "RTTL Component is already playing: %s", name.c_str()); + ESP_LOGW(TAG, "RTTTL Component is already playing: %s", name.c_str()); return; } @@ -122,6 +122,7 @@ void Rtttl::stop() { #ifdef USE_OUTPUT if (this->output_ != nullptr) { this->output_->set_level(0.0); + this->set_state_(STATE_STOPPED); } #endif #ifdef USE_SPEAKER @@ -129,10 +130,10 @@ void Rtttl::stop() { if (this->speaker_->is_running()) { this->speaker_->stop(); } + this->set_state_(STATE_STOPPING); } #endif this->note_duration_ = 0; - this->set_state_(STATE_STOPPING); } void Rtttl::loop() { @@ -342,6 +343,7 @@ void Rtttl::finish_() { #ifdef USE_OUTPUT if (this->output_ != nullptr) { this->output_->set_level(0.0); + this->set_state_(State::STATE_STOPPED); } #endif #ifdef USE_SPEAKER @@ -354,9 +356,9 @@ void Rtttl::finish_() { this->speaker_->play((uint8_t *) (&sample), 8); this->speaker_->finish(); + this->set_state_(State::STATE_STOPPING); } #endif - this->set_state_(State::STATE_STOPPING); this->note_duration_ = 0; this->on_finished_playback_callback_.call(); ESP_LOGD(TAG, "Playback finished"); From 4ed6a648699c47c135ac992171757d49c75fbf74 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Aug 2024 11:46:56 +1200 Subject: [PATCH 0250/1052] Bump version to 2024.8.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a321ddd19f..788eca10a1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.0b3" +__version__ = "2024.8.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 5d4bf5f8e5431cb3e08ccfddba9ce4bc269ab263 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 21 Aug 2024 14:20:29 +1200 Subject: [PATCH 0251/1052] Bump version to 2024.8.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 788eca10a1..f99d442be3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.0b4" +__version__ = "2024.8.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 68272c39c0176ce33c13646ac29ccb3d3d0ca981 Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Thu, 22 Aug 2024 02:58:11 +0200 Subject: [PATCH 0252/1052] Add output source priority "hybrid" (#7322) --- esphome/components/pipsolar/pipsolar.cpp | 3 +++ esphome/components/pipsolar/pipsolar.h | 1 + esphome/components/pipsolar/switch/__init__.py | 2 ++ tests/components/pipsolar/test.esp32-ard.yaml | 2 ++ tests/components/pipsolar/test.esp32-c3-ard.yaml | 2 ++ tests/components/pipsolar/test.esp32-c3-idf.yaml | 2 ++ tests/components/pipsolar/test.esp32-idf.yaml | 2 ++ tests/components/pipsolar/test.esp8266-ard.yaml | 2 ++ tests/components/pipsolar/test.rp2040-ard.yaml | 2 ++ 9 files changed, 18 insertions(+) diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index 2cd1aeba44..c4bc018b75 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -136,6 +136,9 @@ void Pipsolar::loop() { if (this->output_source_priority_battery_switch_) { this->output_source_priority_battery_switch_->publish_state(value_output_source_priority_ == 2); } + if (this->output_source_priority_hybrid_switch_) { + this->output_source_priority_hybrid_switch_->publish_state(value_output_source_priority_ == 3); + } if (this->charger_source_priority_) { this->charger_source_priority_->publish_state(value_charger_source_priority_); } diff --git a/esphome/components/pipsolar/pipsolar.h b/esphome/components/pipsolar/pipsolar.h index f20f44f095..373911b2d7 100644 --- a/esphome/components/pipsolar/pipsolar.h +++ b/esphome/components/pipsolar/pipsolar.h @@ -174,6 +174,7 @@ class Pipsolar : public uart::UARTDevice, public PollingComponent { PIPSOLAR_SWITCH(output_source_priority_utility_switch, QPIRI) PIPSOLAR_SWITCH(output_source_priority_solar_switch, QPIRI) PIPSOLAR_SWITCH(output_source_priority_battery_switch, QPIRI) + PIPSOLAR_SWITCH(output_source_priority_hybrid_switch, QPIRI) PIPSOLAR_SWITCH(input_voltage_range_switch, QPIRI) PIPSOLAR_SWITCH(pv_ok_condition_for_parallel_switch, QPIRI) PIPSOLAR_SWITCH(pv_power_balance_switch, QPIRI) diff --git a/esphome/components/pipsolar/switch/__init__.py b/esphome/components/pipsolar/switch/__init__.py index 7658c7d4f8..80bcdad62e 100644 --- a/esphome/components/pipsolar/switch/__init__.py +++ b/esphome/components/pipsolar/switch/__init__.py @@ -9,6 +9,7 @@ DEPENDENCIES = ["uart"] CONF_OUTPUT_SOURCE_PRIORITY_UTILITY = "output_source_priority_utility" CONF_OUTPUT_SOURCE_PRIORITY_SOLAR = "output_source_priority_solar" CONF_OUTPUT_SOURCE_PRIORITY_BATTERY = "output_source_priority_battery" +CONF_OUTPUT_SOURCE_PRIORITY_HYBRID = "output_source_priority_hybrid" CONF_INPUT_VOLTAGE_RANGE = "input_voltage_range" CONF_PV_OK_CONDITION_FOR_PARALLEL = "pv_ok_condition_for_parallel" CONF_PV_POWER_BALANCE = "pv_power_balance" @@ -17,6 +18,7 @@ TYPES = { CONF_OUTPUT_SOURCE_PRIORITY_UTILITY: ("POP00", None), CONF_OUTPUT_SOURCE_PRIORITY_SOLAR: ("POP01", None), CONF_OUTPUT_SOURCE_PRIORITY_BATTERY: ("POP02", None), + CONF_OUTPUT_SOURCE_PRIORITY_HYBRID: ("POP03", None), CONF_INPUT_VOLTAGE_RANGE: ("PGR01", "PGR00"), CONF_PV_OK_CONDITION_FOR_PARALLEL: ("PPVOKC1", "PPVOKC0"), CONF_PV_POWER_BALANCE: ("PSPB1", "PSPB0"), diff --git a/tests/components/pipsolar/test.esp32-ard.yaml b/tests/components/pipsolar/test.esp32-ard.yaml index fcd4575739..b7a7e0cbd9 100644 --- a/tests/components/pipsolar/test.esp32-ard.yaml +++ b/tests/components/pipsolar/test.esp32-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp32-c3-ard.yaml b/tests/components/pipsolar/test.esp32-c3-ard.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.esp32-c3-ard.yaml +++ b/tests/components/pipsolar/test.esp32-c3-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp32-c3-idf.yaml b/tests/components/pipsolar/test.esp32-c3-idf.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.esp32-c3-idf.yaml +++ b/tests/components/pipsolar/test.esp32-c3-idf.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp32-idf.yaml b/tests/components/pipsolar/test.esp32-idf.yaml index fcd4575739..b7a7e0cbd9 100644 --- a/tests/components/pipsolar/test.esp32-idf.yaml +++ b/tests/components/pipsolar/test.esp32-idf.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.esp8266-ard.yaml b/tests/components/pipsolar/test.esp8266-ard.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.esp8266-ard.yaml +++ b/tests/components/pipsolar/test.esp8266-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: diff --git a/tests/components/pipsolar/test.rp2040-ard.yaml b/tests/components/pipsolar/test.rp2040-ard.yaml index 12e9266343..83d7070669 100644 --- a/tests/components/pipsolar/test.rp2040-ard.yaml +++ b/tests/components/pipsolar/test.rp2040-ard.yaml @@ -220,6 +220,8 @@ switch: name: inverter0_output_source_priority_solar output_source_priority_battery: name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid input_voltage_range: name: inverter0_input_voltage_range pv_ok_condition_for_parallel: From 11e155d86657c24014694cca23200d5a80b75cb2 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Wed, 21 Aug 2024 17:58:43 -0700 Subject: [PATCH 0253/1052] Enable verbose mode from env ESPHOME_VERBOSE or --verbose (#6987) --- esphome/__main__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 5c197ff486..cf2741dbdb 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -38,7 +38,7 @@ from esphome.const import ( SECRETS_FILES, ) from esphome.core import CORE, EsphomeError, coroutine -from esphome.helpers import indent, is_ip_address +from esphome.helpers import indent, is_ip_address, get_bool_env from esphome.log import Fore, color, setup_log from esphome.util import ( get_serial_ports, @@ -731,7 +731,11 @@ POST_CONFIG_ACTIONS = { def parse_args(argv): options_parser = argparse.ArgumentParser(add_help=False) options_parser.add_argument( - "-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true" + "-v", + "--verbose", + help="Enable verbose ESPHome logs.", + action="store_true", + default=get_bool_env("ESPHOME_VERBOSE"), ) options_parser.add_argument( "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true" From ab620acd4f086242dd6ecbe6f5763962982dc303 Mon Sep 17 00:00:00 2001 From: Piotr Szulc Date: Thu, 22 Aug 2024 02:59:31 +0200 Subject: [PATCH 0254/1052] Tuya Number: allow to set hidden datapoints (#7024) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/tuya/__init__.py | 1 + esphome/components/tuya/number/__init__.py | 38 ++++++++++++++++++- .../components/tuya/number/tuya_number.cpp | 19 ++++++++++ esphome/components/tuya/number/tuya_number.h | 6 ++- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/esphome/components/tuya/__init__.py b/esphome/components/tuya/__init__.py index 2eaaa2a625..0738f9b6a4 100644 --- a/esphome/components/tuya/__init__.py +++ b/esphome/components/tuya/__init__.py @@ -15,6 +15,7 @@ CONF_DATAPOINT_TYPE = "datapoint_type" CONF_STATUS_PIN = "status_pin" tuya_ns = cg.esphome_ns.namespace("tuya") +TuyaDatapointType = tuya_ns.enum("TuyaDatapointType", is_class=True) Tuya = tuya_ns.class_("Tuya", cg.Component, uart.UARTDevice) DPTYPE_ANY = "any" diff --git a/esphome/components/tuya/number/__init__.py b/esphome/components/tuya/number/__init__.py index 4dae6d8d60..25be6329ab 100644 --- a/esphome/components/tuya/number/__init__.py +++ b/esphome/components/tuya/number/__init__.py @@ -8,18 +8,36 @@ from esphome.const import ( CONF_MIN_VALUE, CONF_MULTIPLY, CONF_STEP, + CONF_INITIAL_VALUE, ) -from .. import tuya_ns, CONF_TUYA_ID, Tuya +from .. import tuya_ns, CONF_TUYA_ID, Tuya, TuyaDatapointType DEPENDENCIES = ["tuya"] CODEOWNERS = ["@frankiboy1"] +CONF_DATAPOINT_HIDDEN = "datapoint_hidden" +CONF_DATAPOINT_TYPE = "datapoint_type" + TuyaNumber = tuya_ns.class_("TuyaNumber", number.Number, cg.Component) +DATAPOINT_TYPES = { + "int": TuyaDatapointType.INTEGER, + "uint": TuyaDatapointType.INTEGER, + "enum": TuyaDatapointType.ENUM, +} + def validate_min_max(config): - if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]: + max_value = config[CONF_MAX_VALUE] + min_value = config[CONF_MIN_VALUE] + if max_value <= min_value: raise cv.Invalid("max_value must be greater than min_value") + if hidden_config := config.get(CONF_DATAPOINT_HIDDEN): + if (initial_value := hidden_config.get(CONF_INITIAL_VALUE, None)) is not None: + if (initial_value > max_value) or (initial_value < min_value): + raise cv.Invalid( + f"{CONF_INITIAL_VALUE} must be a value between {CONF_MAX_VALUE} and {CONF_MIN_VALUE}" + ) return config @@ -33,6 +51,16 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_MIN_VALUE): cv.float_, cv.Required(CONF_STEP): cv.positive_float, cv.Optional(CONF_MULTIPLY, default=1.0): cv.float_, + cv.Optional(CONF_DATAPOINT_HIDDEN): cv.All( + cv.Schema( + { + cv.Required(CONF_DATAPOINT_TYPE): cv.enum( + DATAPOINT_TYPES, lower=True + ), + cv.Optional(CONF_INITIAL_VALUE): cv.float_, + } + ) + ), } ) .extend(cv.COMPONENT_SCHEMA), @@ -56,3 +84,9 @@ async def to_code(config): cg.add(var.set_tuya_parent(parent)) cg.add(var.set_number_id(config[CONF_NUMBER_DATAPOINT])) + if hidden_config := config.get(CONF_DATAPOINT_HIDDEN): + cg.add(var.set_datapoint_type(hidden_config[CONF_DATAPOINT_TYPE])) + if ( + hidden_init_value := hidden_config.get(CONF_INITIAL_VALUE, None) + ) is not None: + cg.add(var.set_datapoint_initial_value(hidden_init_value)) diff --git a/esphome/components/tuya/number/tuya_number.cpp b/esphome/components/tuya/number/tuya_number.cpp index e883c72d3d..7eeb08fde2 100644 --- a/esphome/components/tuya/number/tuya_number.cpp +++ b/esphome/components/tuya/number/tuya_number.cpp @@ -15,8 +15,18 @@ void TuyaNumber::setup() { ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum); this->publish_state(datapoint.value_enum); } + if ((this->type_) && (this->type_ != datapoint.type)) { + ESP_LOGW(TAG, "Reported type (%d) different than previously set (%d)!", static_cast(datapoint.type), + static_cast(*this->type_)); + } this->type_ = datapoint.type; }); + + this->parent_->add_on_initialized_callback([this] { + if ((this->initial_value_) && (this->type_)) { + this->control(*this->initial_value_); + } + }); } void TuyaNumber::control(float value) { @@ -33,6 +43,15 @@ void TuyaNumber::control(float value) { void TuyaNumber::dump_config() { LOG_NUMBER("", "Tuya Number", this); ESP_LOGCONFIG(TAG, " Number has datapoint ID %u", this->number_id_); + if (this->type_) { + ESP_LOGCONFIG(TAG, " Datapoint type is %d", static_cast(*this->type_)); + } else { + ESP_LOGCONFIG(TAG, " Datapoint type is unknown"); + } + + if (this->initial_value_) { + ESP_LOGCONFIG(TAG, " Initial Value: %f", *this->initial_value_); + } } } // namespace tuya diff --git a/esphome/components/tuya/number/tuya_number.h b/esphome/components/tuya/number/tuya_number.h index f64dac8957..545584128e 100644 --- a/esphome/components/tuya/number/tuya_number.h +++ b/esphome/components/tuya/number/tuya_number.h @@ -3,6 +3,7 @@ #include "esphome/core/component.h" #include "esphome/components/tuya/tuya.h" #include "esphome/components/number/number.h" +#include "esphome/core/optional.h" namespace esphome { namespace tuya { @@ -13,6 +14,8 @@ class TuyaNumber : public number::Number, public Component { void dump_config() override; void set_number_id(uint8_t number_id) { this->number_id_ = number_id; } void set_write_multiply(float factor) { multiply_by_ = factor; } + void set_datapoint_type(TuyaDatapointType type) { type_ = type; } + void set_datapoint_initial_value(float value) { this->initial_value_ = value; } void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } @@ -22,7 +25,8 @@ class TuyaNumber : public number::Number, public Component { Tuya *parent_; uint8_t number_id_{0}; float multiply_by_{1.0}; - TuyaDatapointType type_{}; + optional type_{}; + optional initial_value_{}; }; } // namespace tuya From 5cc8dbace41f3f5863473d60367f0d32c876ac7c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 23 Aug 2024 04:56:53 +1000 Subject: [PATCH 0255/1052] [lvgl] Bug fixes (#7338) --- esphome/components/lvgl/automation.py | 16 +++++++++++++--- esphome/components/lvgl/lvgl_esphome.cpp | 7 +++++++ esphome/components/lvgl/lvgl_esphome.h | 1 + esphome/components/lvgl/widgets/__init__.py | 6 ++++++ esphome/components/lvgl/widgets/line.py | 12 +++++++----- esphome/components/lvgl/widgets/msgbox.py | 3 ++- tests/components/lvgl/lvgl-package.yaml | 6 +++++- 7 files changed, 41 insertions(+), 10 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index a39f589136..efcac977ab 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -5,6 +5,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_TIMEOUT +from esphome.cpp_generator import RawExpression from esphome.cpp_types import nullptr from .defines import ( @@ -26,6 +27,7 @@ from .lvcode import ( add_line_marks, lv, lv_add, + lv_expr, lv_obj, lvgl_comp, ) @@ -38,7 +40,13 @@ from .types import ( lv_disp_t, lv_obj_t, ) -from .widgets import Widget, get_widgets, lv_scr_act, set_obj_properties +from .widgets import ( + Widget, + get_widgets, + lv_scr_act, + set_obj_properties, + wait_for_widgets, +) async def action_to_code( @@ -48,10 +56,12 @@ async def action_to_code( template_arg, args, ): + await wait_for_widgets() async with LambdaContext(parameters=args, where=action_id) as context: + with LvConditional(lv_expr.is_pre_initialise()): + context.add(RawExpression("return")) for widget in widgets: - with LvConditional(widget.obj != nullptr): - await action(widget) + await action(widget) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) return var diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 92f7a880c3..6882986e7c 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -294,6 +294,13 @@ void LvglComponent::loop() { } lv_timer_handler_run_in_period(5); } +bool lv_is_pre_initialise() { + if (!lv_is_initialized()) { + ESP_LOGE(TAG, "LVGL call before component is initialised"); + return true; + } + return false; +} #ifdef USE_LVGL_IMAGE lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) { diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 3a3d1aa6c5..df3d4aa68c 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -40,6 +40,7 @@ namespace lvgl { extern lv_event_code_t lv_api_event; // NOLINT extern lv_event_code_t lv_update_event; // NOLINT +extern bool lv_is_pre_initialise(); #ifdef USE_LVGL_COLOR inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } #endif // USE_LVGL_COLOR diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 4abb25c61d..50da6e131d 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -1,3 +1,4 @@ +import asyncio import sys from typing import Any, Union @@ -223,6 +224,11 @@ async def get_widget_(wid: Widget): return await FakeAwaitable(get_widget_generator(wid)) +async def wait_for_widgets(): + while not Widget.widgets_completed: + await asyncio.sleep(0) + + async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: if not config: return [] diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 8ce4b1965f..4c6439fde4 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -3,7 +3,7 @@ import functools import esphome.codegen as cg import esphome.config_validation as cv -from ..defines import CONF_MAIN, literal +from ..defines import CONF_MAIN from ..lvcode import lv from ..types import LvType from . import Widget, WidgetType @@ -38,13 +38,15 @@ LINE_SCHEMA = { class LineType(WidgetType): def __init__(self): - super().__init__(CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA) + super().__init__( + CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, modify_schema={} + ) async def to_code(self, w: Widget, config): """For a line object, create and add the points""" - data = literal(config[CONF_POINTS]) - points = cg.static_const_array(config[CONF_POINT_LIST_ID], data) - lv.line_set_points(w.obj, points, len(data)) + if data := config.get(CONF_POINTS): + points = cg.static_const_array(config[CONF_POINT_LIST_ID], data) + lv.line_set_points(w.obj, points, len(data)) line_spec = LineType() diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index 63c4326c7c..c377af6bde 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -13,7 +13,7 @@ from ..defines import ( TYPE_FLEX, literal, ) -from ..helpers import add_lv_use +from ..helpers import add_lv_use, lvgl_components_required from ..lv_validation import lv_bool, lv_pct, lv_text from ..lvcode import ( EVENT_ARG, @@ -72,6 +72,7 @@ async def msgbox_to_code(conf): *buttonmatrix_spec.get_uses(), *button_spec.get_uses(), ) + lvgl_components_required.add("BUTTONMATRIX") messagebox_id = conf[CONF_ID] outer = lv_Pvariable(lv_obj_t, messagebox_id.id) buttonmatrix = new_Pvariable( diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 800d6eff27..1479ce7358 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -379,6 +379,7 @@ lvgl: format: "bar value %f" args: [x] - line: + id: lv_line_id align: center points: - 5, 5 @@ -387,7 +388,10 @@ lvgl: - 180, 60 - 240, 10 on_click: - lvgl.page.next: + - lvgl.widget.update: + id: lv_line_id + line_color: 0xFFFF + - lvgl.page.next: - switch: align: right_mid - checkbox: From 3c65cabe1dc7a1d94cb889617427f2239e6fb734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mart=C3=ADn?= Date: Thu, 22 Aug 2024 23:30:22 +0200 Subject: [PATCH 0256/1052] feat: Expand ByteBuffer (#7316) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/core/bytebuffer.cpp | 225 +++++++++++++++++++++--------------- esphome/core/bytebuffer.h | 90 +++++++++++---- 2 files changed, 201 insertions(+), 114 deletions(-) diff --git a/esphome/core/bytebuffer.cpp b/esphome/core/bytebuffer.cpp index fb2ade3166..65525ecfcf 100644 --- a/esphome/core/bytebuffer.cpp +++ b/esphome/core/bytebuffer.cpp @@ -1,19 +1,64 @@ #include "bytebuffer.h" #include +#include namespace esphome { -ByteBuffer ByteBuffer::create(size_t capacity) { - std::vector data(capacity); - return {data}; -} - -ByteBuffer ByteBuffer::wrap(uint8_t *ptr, size_t len) { +ByteBuffer ByteBuffer::wrap(const uint8_t *ptr, size_t len, Endian endianness) { + // there is a double copy happening here, could be optimized but at cost of clarity. std::vector data(ptr, ptr + len); - return {data}; + ByteBuffer buffer = {data}; + buffer.endianness_ = endianness; + return buffer; } -ByteBuffer ByteBuffer::wrap(std::vector data) { return {std::move(data)}; } +ByteBuffer ByteBuffer::wrap(std::vector const &data, Endian endianness) { + ByteBuffer buffer = {data}; + buffer.endianness_ = endianness; + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint8_t value) { + ByteBuffer buffer = ByteBuffer(1); + buffer.put_uint8(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint16_t value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(2, endianness); + buffer.put_uint16(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint32_t value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(4, endianness); + buffer.put_uint32(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(uint64_t value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(8, endianness); + buffer.put_uint64(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(float value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(sizeof(float), endianness); + buffer.put_float(value); + buffer.flip(); + return buffer; +} + +ByteBuffer ByteBuffer::wrap(double value, Endian endianness) { + ByteBuffer buffer = ByteBuffer(sizeof(double), endianness); + buffer.put_double(value); + buffer.flip(); + return buffer; +} void ByteBuffer::set_limit(size_t limit) { assert(limit <= this->get_capacity()); @@ -27,108 +72,102 @@ void ByteBuffer::clear() { this->limit_ = this->get_capacity(); this->position_ = 0; } -uint16_t ByteBuffer::get_uint16() { - assert(this->get_remaining() >= 2); - uint16_t value; - if (endianness_ == LITTLE) { - value = this->data_[this->position_++]; - value |= this->data_[this->position_++] << 8; - } else { - value = this->data_[this->position_++] << 8; - value |= this->data_[this->position_++]; - } - return value; +void ByteBuffer::flip() { + this->limit_ = this->position_; + this->position_ = 0; } -uint32_t ByteBuffer::get_uint32() { - assert(this->get_remaining() >= 4); - uint32_t value; - if (endianness_ == LITTLE) { - value = this->data_[this->position_++]; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++] << 16; - value |= this->data_[this->position_++] << 24; - } else { - value = this->data_[this->position_++] << 24; - value |= this->data_[this->position_++] << 16; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++]; - } - return value; -} -uint32_t ByteBuffer::get_uint24() { - assert(this->get_remaining() >= 3); - uint32_t value; - if (endianness_ == LITTLE) { - value = this->data_[this->position_++]; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++] << 16; - } else { - value = this->data_[this->position_++] << 16; - value |= this->data_[this->position_++] << 8; - value |= this->data_[this->position_++]; - } - return value; -} -uint32_t ByteBuffer::get_int24() { - auto value = this->get_uint24(); - uint32_t mask = (~(uint32_t) 0) << 23; - if ((value & mask) != 0) - value |= mask; - return value; -} +/// Getters uint8_t ByteBuffer::get_uint8() { assert(this->get_remaining() >= 1); return this->data_[this->position_++]; } -float ByteBuffer::get_float() { - auto value = this->get_uint32(); - return *(float *) &value; +uint64_t ByteBuffer::get_uint(size_t length) { + assert(this->get_remaining() >= length); + uint64_t value = 0; + if (this->endianness_ == LITTLE) { + this->position_ += length; + auto index = this->position_; + while (length-- != 0) { + value <<= 8; + value |= this->data_[--index]; + } + } else { + while (length-- != 0) { + value <<= 8; + value |= this->data_[this->position_++]; + } + } + return value; } + +uint32_t ByteBuffer::get_int24() { + auto value = this->get_uint24(); + uint32_t mask = (~static_cast(0)) << 23; + if ((value & mask) != 0) + value |= mask; + return value; +} +float ByteBuffer::get_float() { + assert(this->get_remaining() >= sizeof(float)); + auto ui_value = this->get_uint32(); + float value; + memcpy(&value, &ui_value, sizeof(float)); + return value; +} +double ByteBuffer::get_double() { + assert(this->get_remaining() >= sizeof(double)); + auto ui_value = this->get_uint64(); + double value; + memcpy(&value, &ui_value, sizeof(double)); + return value; +} +std::vector ByteBuffer::get_vector(size_t length) { + assert(this->get_remaining() >= length); + auto start = this->data_.begin() + this->position_; + this->position_ += length; + return {start, start + length}; +} + +/// Putters void ByteBuffer::put_uint8(uint8_t value) { assert(this->get_remaining() >= 1); this->data_[this->position_++] = value; } -void ByteBuffer::put_uint16(uint16_t value) { - assert(this->get_remaining() >= 2); +void ByteBuffer::put_uint(uint64_t value, size_t length) { + assert(this->get_remaining() >= length); if (this->endianness_ == LITTLE) { - this->data_[this->position_++] = (uint8_t) value; - this->data_[this->position_++] = (uint8_t) (value >> 8); + while (length-- != 0) { + this->data_[this->position_++] = static_cast(value); + value >>= 8; + } } else { - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) value; + this->position_ += length; + auto index = this->position_; + while (length-- != 0) { + this->data_[--index] = static_cast(value); + value >>= 8; + } } } -void ByteBuffer::put_uint24(uint32_t value) { - assert(this->get_remaining() >= 3); - if (this->endianness_ == LITTLE) { - this->data_[this->position_++] = (uint8_t) value; - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) (value >> 16); - } else { - this->data_[this->position_++] = (uint8_t) (value >> 16); - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) value; - } +void ByteBuffer::put_float(float value) { + static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported"); + assert(this->get_remaining() >= sizeof(float)); + uint32_t ui_value; + memcpy(&ui_value, &value, sizeof(float)); // this work-around required to silence compiler warnings + this->put_uint32(ui_value); } -void ByteBuffer::put_uint32(uint32_t value) { - assert(this->get_remaining() >= 4); - if (this->endianness_ == LITTLE) { - this->data_[this->position_++] = (uint8_t) value; - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) (value >> 16); - this->data_[this->position_++] = (uint8_t) (value >> 24); - } else { - this->data_[this->position_++] = (uint8_t) (value >> 24); - this->data_[this->position_++] = (uint8_t) (value >> 16); - this->data_[this->position_++] = (uint8_t) (value >> 8); - this->data_[this->position_++] = (uint8_t) value; - } +void ByteBuffer::put_double(double value) { + static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported"); + assert(this->get_remaining() >= sizeof(double)); + uint64_t ui_value; + memcpy(&ui_value, &value, sizeof(double)); + this->put_uint64(ui_value); } -void ByteBuffer::put_float(float value) { this->put_uint32(*(uint32_t *) &value); } -void ByteBuffer::flip() { - this->limit_ = this->position_; - this->position_ = 0; +void ByteBuffer::put_vector(const std::vector &value) { + assert(this->get_remaining() >= value.size()); + std::copy(value.begin(), value.end(), this->data_.begin() + this->position_); + this->position_ += value.size(); } } // namespace esphome diff --git a/esphome/core/bytebuffer.h b/esphome/core/bytebuffer.h index f242e5e333..d44d01f275 100644 --- a/esphome/core/bytebuffer.h +++ b/esphome/core/bytebuffer.h @@ -15,55 +15,103 @@ enum Endian { LITTLE, BIG }; * * There are three variables maintained pointing into the buffer: * - * 0 <= position <= limit <= capacity - * - * capacity: the maximum amount of data that can be stored + * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed * limit: the limit of the data currently available to get or put * position: the current insert or extract position * + * 0 <= position <= limit <= capacity + * * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore * the position to the mark. * * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. * + * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading + * data from a buffer after it has been written. + * */ class ByteBuffer { public: + // Default constructor (compatibility with TEMPLATABLE_VALUE) + ByteBuffer() : ByteBuffer(std::vector()) {} /** * Create a new Bytebuffer with the given capacity */ - static ByteBuffer create(size_t capacity); + ByteBuffer(size_t capacity, Endian endianness = LITTLE) + : data_(std::vector(capacity)), endianness_(endianness), limit_(capacity){}; /** - * Wrap an existing vector in a Bytebufffer + * Wrap an existing vector in a ByteBufffer */ - static ByteBuffer wrap(std::vector data); + static ByteBuffer wrap(std::vector const &data, Endian endianness = LITTLE); /** - * Wrap an existing array in a Bytebufffer + * Wrap an existing array in a ByteBuffer. Note that this will create a copy of the data. */ - static ByteBuffer wrap(uint8_t *ptr, size_t len); + static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE); + // Convenience functions to create a ByteBuffer from a value + static ByteBuffer wrap(uint8_t value); + static ByteBuffer wrap(uint16_t value, Endian endianness = LITTLE); + static ByteBuffer wrap(uint32_t value, Endian endianness = LITTLE); + static ByteBuffer wrap(uint64_t value, Endian endianness = LITTLE); + static ByteBuffer wrap(int8_t value) { return wrap(static_cast(value)); } + static ByteBuffer wrap(int16_t value, Endian endianness = LITTLE) { + return wrap(static_cast(value), endianness); + } + static ByteBuffer wrap(int32_t value, Endian endianness = LITTLE) { + return wrap(static_cast(value), endianness); + } + static ByteBuffer wrap(int64_t value, Endian endianness = LITTLE) { + return wrap(static_cast(value), endianness); + } + static ByteBuffer wrap(float value, Endian endianness = LITTLE); + static ByteBuffer wrap(double value, Endian endianness = LITTLE); + static ByteBuffer wrap(bool value) { return wrap(static_cast(value)); } + // Get an integral value from the buffer, increment position by length + uint64_t get_uint(size_t length); // Get one byte from the buffer, increment position by 1 uint8_t get_uint8(); // Get a 16 bit unsigned value, increment by 2 - uint16_t get_uint16(); + uint16_t get_uint16() { return static_cast(this->get_uint(sizeof(uint16_t))); }; // Get a 24 bit unsigned value, increment by 3 - uint32_t get_uint24(); + uint32_t get_uint24() { return static_cast(this->get_uint(3)); }; // Get a 32 bit unsigned value, increment by 4 - uint32_t get_uint32(); - // signed versions of the get functions - uint8_t get_int8() { return (int8_t) this->get_uint8(); }; - int16_t get_int16() { return (int16_t) this->get_uint16(); } + uint32_t get_uint32() { return static_cast(this->get_uint(sizeof(uint32_t))); }; + // Get a 64 bit unsigned value, increment by 8 + uint64_t get_uint64() { return this->get_uint(sizeof(uint64_t)); }; + // Signed versions of the get functions + uint8_t get_int8() { return static_cast(this->get_uint8()); }; + int16_t get_int16() { return static_cast(this->get_uint(sizeof(int16_t))); } uint32_t get_int24(); - int32_t get_int32() { return (int32_t) this->get_uint32(); } + int32_t get_int32() { return static_cast(this->get_uint(sizeof(int32_t))); } + int64_t get_int64() { return static_cast(this->get_uint(sizeof(int64_t))); } // Get a float value, increment by 4 float get_float(); + // Get a double value, increment by 8 + double get_double(); + // Get a bool value, increment by 1 + bool get_bool() { return this->get_uint8(); } + // Get vector of bytes, increment by length + std::vector get_vector(size_t length); - // put values into the buffer, increment the position accordingly + // Put values into the buffer, increment the position accordingly + // put any integral value, length represents the number of bytes + void put_uint(uint64_t value, size_t length); void put_uint8(uint8_t value); - void put_uint16(uint16_t value); - void put_uint24(uint32_t value); - void put_uint32(uint32_t value); + void put_uint16(uint16_t value) { this->put_uint(value, sizeof(uint16_t)); } + void put_uint24(uint32_t value) { this->put_uint(value, 3); } + void put_uint32(uint32_t value) { this->put_uint(value, sizeof(uint32_t)); } + void put_uint64(uint64_t value) { this->put_uint(value, sizeof(uint64_t)); } + // Signed versions of the put functions + void put_int8(int8_t value) { this->put_uint8(static_cast(value)); } + void put_int16(int32_t value) { this->put_uint(static_cast(value), sizeof(uint16_t)); } + void put_int24(int32_t value) { this->put_uint(static_cast(value), 3); } + void put_int32(int32_t value) { this->put_uint(static_cast(value), sizeof(uint32_t)); } + void put_int64(int64_t value) { this->put_uint(static_cast(value), sizeof(uint64_t)); } + // Extra put functions void put_float(float value); + void put_double(double value); + void put_bool(bool value) { this->put_uint8(value); } + void put_vector(const std::vector &value); inline size_t get_capacity() const { return this->data_.size(); } inline size_t get_position() const { return this->position_; } @@ -80,12 +128,12 @@ class ByteBuffer { // set limit to current position, postition to zero. Used when swapping from write to read operations. void flip(); // retrieve a pointer to the underlying data. - uint8_t *array() { return this->data_.data(); }; + std::vector get_data() { return this->data_; }; void rewind() { this->position_ = 0; } void reset() { this->position_ = this->mark_; } protected: - ByteBuffer(std::vector data) : data_(std::move(data)) { this->limit_ = this->get_capacity(); } + ByteBuffer(std::vector const &data) : data_(data), limit_(data.size()) {} std::vector data_; Endian endianness_{LITTLE}; size_t position_{0}; From 43f8f2fd2ef30802025e078c4be0f0afcc08308f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 23 Aug 2024 18:09:40 +1000 Subject: [PATCH 0257/1052] [core] Clean build if the loaded integrations changed (#7344) --- esphome/writer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/writer.py b/esphome/writer.py index c6111cbe3f..57435d3463 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -106,6 +106,8 @@ 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: + return True return False @@ -117,7 +119,9 @@ def update_storage_json(): return if storage_should_clean(old, new): - _LOGGER.info("Core config or version changed, cleaning build files...") + _LOGGER.info( + "Core config, version or integrations changed, cleaning build files..." + ) clean_build() new.save(path) From a01fea54a03e26ce265cd44780c6166d73973a28 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sat, 24 Aug 2024 02:32:08 -0500 Subject: [PATCH 0258/1052] [ledc] Tweak fix in #6997 (#7336) --- esphome/components/ledc/ledc_output.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 90e11fe4ad..7f91eb2d7a 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -8,6 +8,8 @@ #endif #include +#include + #define CLOCK_FREQUENCY 80e6f #ifdef USE_ARDUINO @@ -120,13 +122,17 @@ void LEDCOutput::write_state(float state) { ledcWrite(this->channel_, duty); #endif #ifdef USE_ESP_IDF +#if !defined(USE_ESP32_VARIANT_ESP32C3) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)) // ensure that 100% on is not 99.975% on + // note: on the C3, this tweak will result in the outputs turning off at 100%, so it has been omitted if ((duty == max_duty) && (max_duty != 1)) { duty = max_duty + 1; } +#endif auto speed_mode = get_speed_mode(channel_); auto chan_num = static_cast(channel_ % 8); int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_); + ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_); ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint); ledc_update_duty(speed_mode, chan_num); #endif From caaae59ea9db397bc80e6e51504bd698ece059f3 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sat, 24 Aug 2024 19:56:13 +1000 Subject: [PATCH 0259/1052] [ledc] Fix maximum brightness on ESP-IDF 5.1 (#7342) Co-authored-by: Keith Burzinski --- esphome/components/ledc/ledc_output.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index 7f91eb2d7a..4ced4b8f76 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -117,24 +117,22 @@ void LEDCOutput::write_state(float state) { const uint32_t max_duty = (uint32_t(1) << this->bit_depth_) - 1; const float duty_rounded = roundf(state * max_duty); auto duty = static_cast(duty_rounded); + ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_); #ifdef USE_ARDUINO - ESP_LOGV(TAG, "Setting duty: %u on channel %u", duty, this->channel_); ledcWrite(this->channel_, duty); #endif #ifdef USE_ESP_IDF -#if !defined(USE_ESP32_VARIANT_ESP32C3) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)) - // ensure that 100% on is not 99.975% on - // note: on the C3, this tweak will result in the outputs turning off at 100%, so it has been omitted - if ((duty == max_duty) && (max_duty != 1)) { - duty = max_duty + 1; - } -#endif auto speed_mode = get_speed_mode(channel_); auto chan_num = static_cast(channel_ % 8); int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_); - ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_); - ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint); - ledc_update_duty(speed_mode, chan_num); + if (duty == max_duty) { + ledc_stop(speed_mode, chan_num, 1); + } else if (duty == 0) { + ledc_stop(speed_mode, chan_num, 0); + } else { + ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint); + ledc_update_duty(speed_mode, chan_num); + } #endif } From 71d6bbc7e6e7389d00609bad87e6fbab37ad34a4 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:03:25 +1000 Subject: [PATCH 0260/1052] [lvgl] Fix race condition involving numbers, switches etc. (#7345) --- esphome/components/lvgl/__init__.py | 5 ++++- esphome/components/lvgl/binary_sensor/__init__.py | 3 ++- esphome/components/lvgl/light/__init__.py | 3 ++- esphome/components/lvgl/lvcode.py | 3 +++ esphome/components/lvgl/number/__init__.py | 3 ++- esphome/components/lvgl/select/__init__.py | 3 ++- esphome/components/lvgl/sensor/__init__.py | 3 ++- esphome/components/lvgl/switch/__init__.py | 3 ++- esphome/components/lvgl/text/__init__.py | 3 ++- esphome/components/lvgl/text_sensor/__init__.py | 3 ++- esphome/components/lvgl/widgets/__init__.py | 13 ++++++++++--- 11 files changed, 33 insertions(+), 12 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 7c51d9c70d..ea020435dc 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -266,7 +266,10 @@ async def to_code(config): await add_top_layer(config) await msgboxes_to_code(config) await disp_update(f"{lv_component}->get_disp()", config) - Widget.set_completed() + # At this point only the setup code should be generated + assert LvContext.added_lambda_count == 1 + Widget.set_completed() + async with LvContext(lv_component): await generate_triggers(lv_component) for conf in config.get(CONF_ON_IDLE, ()): templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) diff --git a/esphome/components/lvgl/binary_sensor/__init__.py b/esphome/components/lvgl/binary_sensor/__init__.py index 8789a06375..56984405aa 100644 --- a/esphome/components/lvgl/binary_sensor/__init__.py +++ b/esphome/components/lvgl/binary_sensor/__init__.py @@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..lvcode import EVENT_ARG, LambdaContext, LvContext from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, lv_pseudo_button_t -from ..widgets import Widget, get_widgets +from ..widgets import Widget, get_widgets, wait_for_widgets CONFIG_SCHEMA = ( binary_sensor_schema(BinarySensor) @@ -29,6 +29,7 @@ async def to_code(config): widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] assert isinstance(widget, Widget) + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pressed_ctx: pressed_ctx.add(sensor.publish_state(widget.is_pressed())) async with LvContext(paren) as ctx: diff --git a/esphome/components/lvgl/light/__init__.py b/esphome/components/lvgl/light/__init__.py index 27c160dff6..a0eeded349 100644 --- a/esphome/components/lvgl/light/__init__.py +++ b/esphome/components/lvgl/light/__init__.py @@ -8,7 +8,7 @@ from ..defines import CONF_LVGL_ID from ..lvcode import LvContext from ..schemas import LVGL_SCHEMA from ..types import LvType, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets lv_led_t = LvType("lv_led_t") LVLight = lvgl_ns.class_("LVLight", LightOutput) @@ -28,5 +28,6 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_LED) widget = widget[0] + await wait_for_widgets() async with LvContext(paren) as ctx: ctx.add(var.set_obj(widget.obj)) diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 6d7e364e5d..8d029ce0ca 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -176,6 +176,8 @@ class LvContext(LambdaContext): Code generation into the LVGL initialisation code (called in `setup()`) """ + added_lambda_count = 0 + def __init__(self, lv_component, args=None): self.args = args or LVGL_COMP_ARG super().__init__(parameters=self.args) @@ -183,6 +185,7 @@ class LvContext(LambdaContext): async def add_init_lambda(self): cg.add(self.lv_component.add_init_lambda(await self.get_lambda())) + LvContext.added_lambda_count += 1 async def __aexit__(self, exc_type, exc_val, exc_tb): await super().__aexit__(exc_type, exc_val, exc_tb) diff --git a/esphome/components/lvgl/number/__init__.py b/esphome/components/lvgl/number/__init__.py index 6336bb0632..3e1ede0dec 100644 --- a/esphome/components/lvgl/number/__init__.py +++ b/esphome/components/lvgl/number/__init__.py @@ -16,7 +16,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number) @@ -44,6 +44,7 @@ async def to_code(config): step=widget.get_step(), ) + await wait_for_widgets() async with LambdaContext([(cg.float_, "v")]) as control: await widget.set_property( "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py index b55bde13bc..e77d0cfb32 100644 --- a/esphome/components/lvgl/select/__init__.py +++ b/esphome/components/lvgl/select/__init__.py @@ -15,7 +15,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvSelect, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) @@ -37,6 +37,7 @@ async def to_code(config): options = widget.config.get(CONF_OPTIONS, []) selector = await select.new_select(config, options=options) paren = await cg.get_variable(config[CONF_LVGL_ID]) + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pub_ctx: pub_ctx.add(selector.publish_index(widget.get_value())) async with LambdaContext([(cg.uint16, "v")]) as control: diff --git a/esphome/components/lvgl/sensor/__init__.py b/esphome/components/lvgl/sensor/__init__.py index 82e21d5e95..a2a2298c27 100644 --- a/esphome/components/lvgl/sensor/__init__.py +++ b/esphome/components/lvgl/sensor/__init__.py @@ -14,7 +14,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber -from ..widgets import Widget, get_widgets +from ..widgets import Widget, get_widgets, wait_for_widgets CONFIG_SCHEMA = ( sensor_schema(Sensor) @@ -33,6 +33,7 @@ async def to_code(config): widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] assert isinstance(widget, Widget) + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as lamb: lv_add(sensor.publish_state(widget.get_value())) async with LvContext(paren, LVGL_COMP_ARG): diff --git a/esphome/components/lvgl/switch/__init__.py b/esphome/components/lvgl/switch/__init__.py index 957fce17ff..f855c2a034 100644 --- a/esphome/components/lvgl/switch/__init__.py +++ b/esphome/components/lvgl/switch/__init__.py @@ -16,7 +16,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch) CONFIG_SCHEMA = ( @@ -35,6 +35,7 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as checked_ctx: checked_ctx.add(switch.publish_state(widget.get_value())) async with LambdaContext([(cg.bool_, "v")]) as control: diff --git a/esphome/components/lvgl/text/__init__.py b/esphome/components/lvgl/text/__init__.py index 9ee494d8a0..56fa42e131 100644 --- a/esphome/components/lvgl/text/__init__.py +++ b/esphome/components/lvgl/text/__init__.py @@ -15,7 +15,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvText, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLText = lvgl_ns.class_("LVGLText", text.Text) @@ -32,6 +32,7 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] + await wait_for_widgets() async with LambdaContext([(cg.std_string, "text_value")]) as control: await widget.set_property("text", "text_value.c_str())") lv.event_send(widget.obj, API_EVENT, None) diff --git a/esphome/components/lvgl/text_sensor/__init__.py b/esphome/components/lvgl/text_sensor/__init__.py index cab715dce0..ae39eec291 100644 --- a/esphome/components/lvgl/text_sensor/__init__.py +++ b/esphome/components/lvgl/text_sensor/__init__.py @@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvText -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets CONFIG_SCHEMA = ( text_sensor_schema(TextSensor) @@ -28,6 +28,7 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pressed_ctx: pressed_ctx.add(sensor.publish_state(widget.get_value())) async with LvContext(paren) as ctx: diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 50da6e131d..17d73c1714 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -1,4 +1,3 @@ -import asyncio import sys from typing import Any, Union @@ -224,9 +223,17 @@ async def get_widget_(wid: Widget): return await FakeAwaitable(get_widget_generator(wid)) +def widgets_wait_generator(): + while True: + if Widget.widgets_completed: + return + yield + + async def wait_for_widgets(): - while not Widget.widgets_completed: - await asyncio.sleep(0) + if Widget.widgets_completed: + return + await FakeAwaitable(widgets_wait_generator()) async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: From 60fced53c214b5ccdb7fb0907cd01e06ba899456 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:08:30 +1000 Subject: [PATCH 0261/1052] [lvgl] Bug fixes: (#7341) --- esphome/components/lvgl/automation.py | 2 +- esphome/components/lvgl/defines.py | 8 ++++++- esphome/components/lvgl/number/__init__.py | 1 + esphome/components/lvgl/select/__init__.py | 1 + esphome/components/lvgl/switch/__init__.py | 3 ++- esphome/components/lvgl/text/__init__.py | 1 + esphome/components/lvgl/widgets/__init__.py | 25 ++++++++++++++++----- tests/components/lvgl/lvgl-package.yaml | 16 +++++++++++++ 8 files changed, 49 insertions(+), 8 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index efcac977ab..eb1b54e3ec 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -157,7 +157,7 @@ async def lvgl_update_to_code(config, action_id, template_arg, args): widgets = await get_widgets(config) w = widgets[0] disp = f"{w.obj}->get_disp()" - async with LambdaContext(parameters=args, where=action_id) as context: + async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context: await disp_update(disp, config) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) await cg.register_parented(var, w.var) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 6a8b20b505..7bb1667e77 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -505,4 +505,10 @@ DEFAULT_ESPHOME_FONT = "esphome_lv_default_font" def join_enums(enums, prefix=""): - return literal("|".join(f"(int){prefix}{e.upper()}" for e in enums)) + enums = list(enums) + enums.sort() + # If a prefix is provided, prepend each constant with the prefix, and assume that all the constants are within the + # same namespace, otherwise cast to int to avoid triggering warnings about mixing enum types. + if prefix: + return literal("|".join(f"{prefix}{e.upper()}" for e in enums)) + return literal("|".join(f"(int){e.upper()}" for e in enums)) diff --git a/esphome/components/lvgl/number/__init__.py b/esphome/components/lvgl/number/__init__.py index 3e1ede0dec..07f92635b5 100644 --- a/esphome/components/lvgl/number/__init__.py +++ b/esphome/components/lvgl/number/__init__.py @@ -50,6 +50,7 @@ async def to_code(config): "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] ) lv.event_send(widget.obj, API_EVENT, cg.nullptr) + control.add(var.publish_state(widget.get_value())) async with LambdaContext(EVENT_ARG) as event: event.add(var.publish_state(widget.get_value())) event_code = ( diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py index e77d0cfb32..73ac50aa55 100644 --- a/esphome/components/lvgl/select/__init__.py +++ b/esphome/components/lvgl/select/__init__.py @@ -43,6 +43,7 @@ async def to_code(config): async with LambdaContext([(cg.uint16, "v")]) as control: await widget.set_property("selected", "v", animated=config[CONF_ANIMATED]) lv.event_send(widget.obj, API_EVENT, cg.nullptr) + control.add(selector.publish_index(widget.get_value())) async with LvContext(paren) as ctx: lv_add(selector.set_control_lambda(await control.get_lambda())) ctx.add( diff --git a/esphome/components/lvgl/switch/__init__.py b/esphome/components/lvgl/switch/__init__.py index f855c2a034..8c090543f9 100644 --- a/esphome/components/lvgl/switch/__init__.py +++ b/esphome/components/lvgl/switch/__init__.py @@ -3,7 +3,7 @@ from esphome.components.switch import Switch, new_switch, switch_schema import esphome.config_validation as cv from esphome.cpp_generator import MockObj -from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..defines import CONF_LVGL_ID, CONF_WIDGET, literal from ..lvcode import ( API_EVENT, EVENT_ARG, @@ -44,6 +44,7 @@ async def to_code(config): cond.else_() widget.clear_state(LV_STATE.CHECKED) lv.event_send(widget.obj, API_EVENT, cg.nullptr) + control.add(switch.publish_state(literal("v"))) async with LvContext(paren) as ctx: lv_add(switch.set_control_lambda(await control.get_lambda())) ctx.add( diff --git a/esphome/components/lvgl/text/__init__.py b/esphome/components/lvgl/text/__init__.py index 56fa42e131..540591d24b 100644 --- a/esphome/components/lvgl/text/__init__.py +++ b/esphome/components/lvgl/text/__init__.py @@ -36,6 +36,7 @@ async def to_code(config): async with LambdaContext([(cg.std_string, "text_value")]) as control: await widget.set_property("text", "text_value.c_str())") lv.event_send(widget.obj, API_EVENT, None) + control.add(textvar.publish_state(widget.get_value())) async with LambdaContext(EVENT_ARG) as lamb: lv_add(textvar.publish_state(widget.get_value())) async with LvContext(paren): diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 17d73c1714..062c268135 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -118,7 +118,14 @@ class Widget: def clear_flag(self, flag): return lv_obj.clear_flag(self.obj, literal(flag)) - async def set_property(self, prop, value, animated: bool = None): + async def set_property(self, prop, value, animated: bool = None, lv_name=None): + """ + Set a property of the widget. + :param prop: The property name + :param value: The value + :param animated: If the change should be animated + :param lv_name: The base type of the widget e.g. "obj" + """ if isinstance(value, dict): value = value.get(prop) if isinstance(ALL_STYLES.get(prop), LValidator): @@ -131,11 +138,12 @@ class Widget: value = value.total_milliseconds if isinstance(value, str): value = literal(value) + lv_name = lv_name or self.type.lv_name if animated is None or self.type.animated is not True: - lv.call(f"{self.type.lv_name}_set_{prop}", self.obj, value) + lv.call(f"{lv_name}_set_{prop}", self.obj, value) else: lv.call( - f"{self.type.lv_name}_set_{prop}", + f"{lv_name}_set_{prop}", self.obj, value, literal("LV_ANIM_ON" if animated else "LV_ANIM_OFF"), @@ -319,8 +327,15 @@ async def set_obj_properties(w: Widget, config): lv_obj.set_flex_align(w.obj, main, cross, track) parts = collect_parts(config) for part, states in parts.items(): + part = "LV_PART_" + part.upper() for state, props in states.items(): - lv_state = join_enums((f"LV_STATE_{state}", f"LV_PART_{part}")) + state = "LV_STATE_" + state.upper() + if state == "LV_STATE_DEFAULT": + lv_state = literal(part) + elif part == "LV_PART_MAIN": + lv_state = literal(state) + else: + lv_state = join_enums((state, part)) for style_id in props.get(CONF_STYLES, ()): lv_obj.add_style(w.obj, MockObj(style_id), lv_state) for prop, value in { @@ -384,7 +399,7 @@ async def set_obj_properties(w: Widget, config): w.add_state(state) cond.else_() w.clear_state(state) - await w.set_property(CONF_SCROLLBAR_MODE, config) + await w.set_property(CONF_SCROLLBAR_MODE, config, lv_name="obj") async def add_widgets(parent: Widget, config: dict): diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 1479ce7358..0e2c37048b 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -1,6 +1,8 @@ lvgl: log_level: TRACE bg_color: light_blue + disp_bg_color: 0xffff00 + disp_bg_image: cat_image theme: obj: border_width: 1 @@ -78,6 +80,9 @@ lvgl: on_click: then: - lvgl.animimg.stop: anim_img + - lvgl.update: + disp_bg_color: 0xffff00 + disp_bg_image: cat_image - label: text: "Hello shiny day" text_color: 0xFFFFFF @@ -304,6 +309,17 @@ lvgl: src: cat_image align: top_left y: 50 + - tileview: + id: tileview_id + scrollbar_mode: active + tiles: + - id: page_1 + row: 0 + column: 0 + dir: HOR + widgets: + - obj: + bg_color: 0x000000 - id: page2 widgets: From dc9c00105666276540fd3a634a4609c777f9b158 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:07:18 +1200 Subject: [PATCH 0262/1052] [const] Move ``CONF_LINE_FREQUENCY`` to const.py (#7351) --- esphome/components/atm90e26/sensor.py | 20 ++++++++++---------- esphome/components/atm90e32/sensor.py | 2 +- esphome/const.py | 1 + 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/esphome/components/atm90e26/sensor.py b/esphome/components/atm90e26/sensor.py index a702e23cf0..42ef259100 100644 --- a/esphome/components/atm90e26/sensor.py +++ b/esphome/components/atm90e26/sensor.py @@ -1,34 +1,34 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import sensor, spi +import esphome.config_validation as cv from esphome.const import ( - CONF_ID, - CONF_REACTIVE_POWER, - CONF_VOLTAGE, CONF_CURRENT, + CONF_FORWARD_ACTIVE_ENERGY, + CONF_FREQUENCY, + CONF_ID, + CONF_LINE_FREQUENCY, CONF_POWER, CONF_POWER_FACTOR, - CONF_FREQUENCY, - CONF_FORWARD_ACTIVE_ENERGY, + CONF_REACTIVE_POWER, CONF_REVERSE_ACTIVE_ENERGY, + CONF_VOLTAGE, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_VOLTAGE, - ICON_LIGHTBULB, ICON_CURRENT_AC, + ICON_LIGHTBULB, STATE_CLASS_MEASUREMENT, STATE_CLASS_TOTAL_INCREASING, + UNIT_AMPERE, UNIT_HERTZ, UNIT_VOLT, - UNIT_AMPERE, - UNIT_WATT, UNIT_VOLT_AMPS_REACTIVE, + UNIT_WATT, UNIT_WATT_HOURS, ) -CONF_LINE_FREQUENCY = "line_frequency" CONF_METER_CONSTANT = "meter_constant" CONF_PL_CONST = "pl_const" CONF_GAIN_PGA = "gain_pga" diff --git a/esphome/components/atm90e32/sensor.py b/esphome/components/atm90e32/sensor.py index be2196223c..0dc3bfdc4f 100644 --- a/esphome/components/atm90e32/sensor.py +++ b/esphome/components/atm90e32/sensor.py @@ -7,6 +7,7 @@ from esphome.const import ( CONF_FORWARD_ACTIVE_ENERGY, CONF_FREQUENCY, CONF_ID, + CONF_LINE_FREQUENCY, CONF_PHASE_A, CONF_PHASE_ANGLE, CONF_PHASE_B, @@ -39,7 +40,6 @@ from esphome.const import ( from . import atm90e32_ns -CONF_LINE_FREQUENCY = "line_frequency" CONF_CHIP_TEMPERATURE = "chip_temperature" CONF_GAIN_PGA = "gain_pga" CONF_CURRENT_PHASES = "current_phases" diff --git a/esphome/const.py b/esphome/const.py index b9c37a53a8..6e29667887 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -431,6 +431,7 @@ CONF_LIGHT_ID = "light_id" CONF_LIGHTNING_ENERGY = "lightning_energy" CONF_LIGHTNING_THRESHOLD = "lightning_threshold" CONF_LIMIT_MODE = "limit_mode" +CONF_LINE_FREQUENCY = "line_frequency" CONF_LINE_THICKNESS = "line_thickness" CONF_LINE_TYPE = "line_type" CONF_LOADED_INTEGRATIONS = "loaded_integrations" From 0f2064193f91a609ab64dc763aa17a194231be1e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:20:26 +1200 Subject: [PATCH 0263/1052] [api] Fix sending the ``once`` flag on ha entity subscription (#7357) --- esphome/components/api/api_connection.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index bd438265d4..195fcab0ab 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -179,6 +179,7 @@ void APIConnection::loop() { SubscribeHomeAssistantStateResponse resp; resp.entity_id = it.entity_id; resp.attribute = it.attribute.value(); + resp.once = it.once; if (this->send_subscribe_home_assistant_state_response(resp)) { state_subs_at_++; } From e10f8128c8f48fd63b1d09d4e3f059da0a9a573a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 26 Aug 2024 23:41:09 +0100 Subject: [PATCH 0264/1052] bl0942: Fix init sequence, add address and line_frequency options (#7250) --- esphome/components/bl0942/bl0942.cpp | 109 ++++++++++++++----- esphome/components/bl0942/bl0942.h | 15 ++- esphome/components/bl0942/sensor.py | 28 ++++- tests/components/bl0942/test.bk72xx-ard.yaml | 22 ++++ 4 files changed, 138 insertions(+), 36 deletions(-) create mode 100644 tests/components/bl0942/test.bk72xx-ard.yaml diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp index 38b1c89036..606d3629da 100644 --- a/esphome/components/bl0942/bl0942.cpp +++ b/esphome/components/bl0942/bl0942.cpp @@ -2,6 +2,8 @@ #include "esphome/core/log.h" #include +// Datasheet: https://www.belling.com.cn/media/file_object/bel_product/BL0942/datasheet/BL0942_V1.06_en.pdf + namespace esphome { namespace bl0942 { @@ -12,33 +14,41 @@ static const uint8_t BL0942_FULL_PACKET = 0xAA; static const uint8_t BL0942_PACKET_HEADER = 0x55; static const uint8_t BL0942_WRITE_COMMAND = 0xA8; -static const uint8_t BL0942_REG_I_FAST_RMS_CTRL = 0x10; -static const uint8_t BL0942_REG_MODE = 0x18; -static const uint8_t BL0942_REG_SOFT_RESET = 0x19; -static const uint8_t BL0942_REG_USR_WRPROT = 0x1A; + +static const uint8_t BL0942_REG_I_RMSOS = 0x12; +static const uint8_t BL0942_REG_WA_CREEP = 0x14; +static const uint8_t BL0942_REG_I_FAST_RMS_TH = 0x15; +static const uint8_t BL0942_REG_I_FAST_RMS_CYC = 0x16; +static const uint8_t BL0942_REG_FREQ_CYC = 0x17; +static const uint8_t BL0942_REG_OT_FUNX = 0x18; +static const uint8_t BL0942_REG_MODE = 0x19; +static const uint8_t BL0942_REG_SOFT_RESET = 0x1C; +static const uint8_t BL0942_REG_USR_WRPROT = 0x1D; static const uint8_t BL0942_REG_TPS_CTRL = 0x1B; -// TODO: Confirm insialisation works as intended -const uint8_t BL0942_INIT[5][6] = { - // Reset to default - {BL0942_WRITE_COMMAND, BL0942_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38}, - // Enable User Operation Write - {BL0942_WRITE_COMMAND, BL0942_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0}, - // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS - {BL0942_WRITE_COMMAND, BL0942_REG_MODE, 0x00, 0x10, 0x00, 0x37}, - // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS - {BL0942_WRITE_COMMAND, BL0942_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE}, - // 0x181C = Half cycle, Fast RMS threshold 6172 - {BL0942_WRITE_COMMAND, BL0942_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}}; +static const uint32_t BL0942_REG_MODE_RESV = 0x03; +static const uint32_t BL0942_REG_MODE_CF_EN = 0x04; +static const uint32_t BL0942_REG_MODE_RMS_UPDATE_SEL = 0x08; +static const uint32_t BL0942_REG_MODE_FAST_RMS_SEL = 0x10; +static const uint32_t BL0942_REG_MODE_AC_FREQ_SEL = 0x20; +static const uint32_t BL0942_REG_MODE_CF_CNT_CLR_SEL = 0x40; +static const uint32_t BL0942_REG_MODE_CF_CNT_ADD_SEL = 0x80; +static const uint32_t BL0942_REG_MODE_UART_RATE_19200 = 0x200; +static const uint32_t BL0942_REG_MODE_UART_RATE_38400 = 0x300; +static const uint32_t BL0942_REG_MODE_DEFAULT = + BL0942_REG_MODE_RESV | BL0942_REG_MODE_CF_EN | BL0942_REG_MODE_CF_CNT_ADD_SEL; + +static const uint32_t BL0942_REG_SOFT_RESET_MAGIC = 0x5a5a5a; +static const uint32_t BL0942_REG_USR_WRPROT_MAGIC = 0x55; void BL0942::loop() { DataPacket buffer; if (!this->available()) { return; } - if (read_array((uint8_t *) &buffer, sizeof(buffer))) { - if (validate_checksum(&buffer)) { - received_package_(&buffer); + if (this->read_array((uint8_t *) &buffer, sizeof(buffer))) { + if (this->validate_checksum_(&buffer)) { + this->received_package_(&buffer); } } else { ESP_LOGW(TAG, "Junk on wire. Throwing away partial message"); @@ -47,8 +57,8 @@ void BL0942::loop() { } } -bool BL0942::validate_checksum(DataPacket *data) { - uint8_t checksum = BL0942_READ_COMMAND; +bool BL0942::validate_checksum_(DataPacket *data) { + uint8_t checksum = BL0942_READ_COMMAND | this->address_; // Whole package but checksum uint8_t *raw = (uint8_t *) data; for (uint32_t i = 0; i < sizeof(*data) - 1; i++) { @@ -61,17 +71,58 @@ bool BL0942::validate_checksum(DataPacket *data) { return checksum == data->checksum; } -void BL0942::update() { +void BL0942::write_reg_(uint8_t reg, uint32_t val) { + uint8_t pkt[6]; + this->flush(); - this->write_byte(BL0942_READ_COMMAND); + pkt[0] = BL0942_WRITE_COMMAND | this->address_; + pkt[1] = reg; + pkt[2] = (val & 0xff); + pkt[3] = (val >> 8) & 0xff; + pkt[4] = (val >> 16) & 0xff; + pkt[5] = (pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4]) ^ 0xff; + this->write_array(pkt, 6); + delay(1); +} + +int BL0942::read_reg_(uint8_t reg) { + union { + uint8_t b[4]; + uint32_le_t le32; + } resp; + + this->write_byte(BL0942_READ_COMMAND | this->address_); + this->write_byte(reg); + this->flush(); + if (this->read_array(resp.b, 4) && + resp.b[3] == + (uint8_t) ((BL0942_READ_COMMAND + this->address_ + reg + resp.b[0] + resp.b[1] + resp.b[2]) ^ 0xff)) { + resp.b[3] = 0; + return resp.le32; + } + return -1; +} + +void BL0942::update() { + this->write_byte(BL0942_READ_COMMAND | this->address_); this->write_byte(BL0942_FULL_PACKET); } void BL0942::setup() { - for (auto *i : BL0942_INIT) { - this->write_array(i, 6); - delay(1); - } + this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC); + this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC); + + uint32_t mode = BL0942_REG_MODE_DEFAULT; + mode |= BL0942_REG_MODE_RMS_UPDATE_SEL; /* 800ms refresh time */ + if (this->line_freq_ == LINE_FREQUENCY_60HZ) + mode |= BL0942_REG_MODE_AC_FREQ_SEL; + this->write_reg_(BL0942_REG_MODE, mode); + + this->write_reg_(BL0942_REG_USR_WRPROT, 0); + + if (this->read_reg_(BL0942_REG_MODE) != mode) + this->status_set_warning("BL0942 setup failed!"); + this->flush(); } @@ -104,13 +155,15 @@ void BL0942::received_package_(DataPacket *data) { if (frequency_sensor_ != nullptr) { frequency_sensor_->publish_state(frequency); } - + this->status_clear_warning(); ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, frequency %fHz, status 0x%08X", v_rms, i_rms, watt, cf_cnt, total_energy_consumption, frequency, data->status); } void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity) ESP_LOGCONFIG(TAG, "BL0942:"); + ESP_LOGCONFIG(TAG, " Address: %d", this->address_); + ESP_LOGCONFIG(TAG, " Nominal line frequency: %d Hz", this->line_freq_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); diff --git a/esphome/components/bl0942/bl0942.h b/esphome/components/bl0942/bl0942.h index 12489915e1..52347c1bc3 100644 --- a/esphome/components/bl0942/bl0942.h +++ b/esphome/components/bl0942/bl0942.h @@ -28,6 +28,11 @@ struct DataPacket { uint8_t checksum; } __attribute__((packed)); +enum LineFrequency : uint8_t { + LINE_FREQUENCY_50HZ = 50, + LINE_FREQUENCY_60HZ = 60, +}; + class BL0942 : public PollingComponent, public uart::UARTDevice { public: void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; } @@ -35,9 +40,10 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; } void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; } + void set_line_freq(LineFrequency freq) { this->line_freq_ = freq; } + void set_address(uint8_t address) { this->address_ = address; } void loop() override; - void update() override; void setup() override; void dump_config() override; @@ -59,9 +65,12 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { float current_reference_ = BL0942_IREF; // Divide by this to turn into kWh float energy_reference_ = BL0942_EREF; + uint8_t address_ = 0; + LineFrequency line_freq_ = LINE_FREQUENCY_50HZ; - static bool validate_checksum(DataPacket *data); - + bool validate_checksum_(DataPacket *data); + int read_reg_(uint8_t reg); + void write_reg_(uint8_t reg, uint32_t val); void received_package_(DataPacket *data); }; } // namespace bl0942 diff --git a/esphome/components/bl0942/sensor.py b/esphome/components/bl0942/sensor.py index 9612df6d4c..c47da45b8c 100644 --- a/esphome/components/bl0942/sensor.py +++ b/esphome/components/bl0942/sensor.py @@ -1,25 +1,27 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import sensor, uart +import esphome.config_validation as cv from esphome.const import ( + CONF_ADDRESS, CONF_CURRENT, CONF_ENERGY, + CONF_FREQUENCY, CONF_ID, + CONF_LINE_FREQUENCY, CONF_POWER, CONF_VOLTAGE, - CONF_FREQUENCY, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, + DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, - DEVICE_CLASS_FREQUENCY, STATE_CLASS_MEASUREMENT, + STATE_CLASS_TOTAL_INCREASING, UNIT_AMPERE, + UNIT_HERTZ, UNIT_KILOWATT_HOURS, UNIT_VOLT, UNIT_WATT, - UNIT_HERTZ, - STATE_CLASS_TOTAL_INCREASING, ) DEPENDENCIES = ["uart"] @@ -27,6 +29,12 @@ DEPENDENCIES = ["uart"] bl0942_ns = cg.esphome_ns.namespace("bl0942") BL0942 = bl0942_ns.class_("BL0942", cg.PollingComponent, uart.UARTDevice) +LineFrequency = bl0942_ns.enum("LineFrequency") +LINE_FREQS = { + 50: LineFrequency.LINE_FREQUENCY_50HZ, + 60: LineFrequency.LINE_FREQUENCY_60HZ, +} + CONFIG_SCHEMA = ( cv.Schema( { @@ -61,6 +69,14 @@ CONFIG_SCHEMA = ( device_class=DEVICE_CLASS_FREQUENCY, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_LINE_FREQUENCY, default="50HZ"): cv.All( + cv.frequency, + cv.enum( + LINE_FREQS, + int=True, + ), + ), + cv.Optional(CONF_ADDRESS, default=0): cv.int_range(min=0, max=3), } ) .extend(cv.polling_component_schema("60s")) @@ -88,3 +104,5 @@ async def to_code(config): if frequency_config := config.get(CONF_FREQUENCY): sens = await sensor.new_sensor(frequency_config) cg.add(var.set_frequency_sensor(sens)) + cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) + cg.add(var.set_address(config[CONF_ADDRESS])) diff --git a/tests/components/bl0942/test.bk72xx-ard.yaml b/tests/components/bl0942/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..4ed3eb391d --- /dev/null +++ b/tests/components/bl0942/test.bk72xx-ard.yaml @@ -0,0 +1,22 @@ +uart: + - id: uart_bl0942 + tx_pin: + number: TX1 + rx_pin: + number: RX1 + baud_rate: 2400 + +sensor: + - platform: bl0942 + address: 0 + line_frequency: 50Hz + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency From 5a707b558dbb9e1e0e2184fd8a0c5da2ec7f5514 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Mon, 26 Aug 2024 18:38:49 -0500 Subject: [PATCH 0265/1052] Add supported formats to media player (#7318) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/api/api.proto | 15 ++++ esphome/components/api/api_connection.cpp | 9 ++ esphome/components/api/api_pb2.cpp | 83 +++++++++++++++++++ esphome/components/api/api_pb2.h | 20 +++++ esphome/components/api/api_pb2_service.cpp | 19 +++++ esphome/components/api/api_pb2_service.h | 4 + .../components/media_player/media_player.h | 15 ++++ 7 files changed, 165 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 72eaeed6d7..84183357dc 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1107,6 +1107,19 @@ enum MediaPlayerCommand { MEDIA_PLAYER_COMMAND_MUTE = 3; MEDIA_PLAYER_COMMAND_UNMUTE = 4; } +enum MediaPlayerFormatPurpose { + MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0; + MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1; +} +message MediaPlayerSupportedFormat { + option (id) = 119; + option (ifdef) = "USE_MEDIA_PLAYER"; + + string format = 1; + uint32 sample_rate = 2; + uint32 num_channels = 3; + MediaPlayerFormatPurpose purpose = 4; +} message ListEntitiesMediaPlayerResponse { option (id) = 63; option (source) = SOURCE_SERVER; @@ -1122,6 +1135,8 @@ message ListEntitiesMediaPlayerResponse { EntityCategory entity_category = 7; bool supports_pause = 8; + + repeated MediaPlayerSupportedFormat supported_formats = 9; } message MediaPlayerStateResponse { option (id) = 64; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 195fcab0ab..a655d06e66 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1026,6 +1026,15 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play auto traits = media_player->get_traits(); msg.supports_pause = traits.get_supports_pause(); + for (auto &supported_format : traits.get_supported_formats()) { + MediaPlayerSupportedFormat media_format; + media_format.format = supported_format.format; + media_format.sample_rate = supported_format.sample_rate; + media_format.num_channels = supported_format.num_channels; + media_format.purpose = static_cast(supported_format.purpose); + msg.supported_formats.push_back(media_format); + } + return this->send_list_entities_media_player_response(msg); } void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index bb37824403..c944d0dae8 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -387,6 +387,18 @@ template<> const char *proto_enum_to_string(enums::Me } #endif #ifdef HAS_PROTO_MESSAGE_DUMP +template<> const char *proto_enum_to_string(enums::MediaPlayerFormatPurpose value) { + switch (value) { + case enums::MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT: + return "MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT"; + case enums::MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT: + return "MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT"; + default: + return "UNKNOWN"; + } +} +#endif +#ifdef HAS_PROTO_MESSAGE_DUMP template<> const char *proto_enum_to_string(enums::BluetoothDeviceRequestType value) { switch (value) { @@ -5123,6 +5135,64 @@ void ButtonCommandRequest::dump_to(std::string &out) const { out.append("}"); } #endif +bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->sample_rate = value.as_uint32(); + return true; + } + case 3: { + this->num_channels = value.as_uint32(); + return true; + } + case 4: { + this->purpose = value.as_enum(); + return true; + } + default: + return false; + } +} +bool MediaPlayerSupportedFormat::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->format = value.as_string(); + return true; + } + default: + return false; + } +} +void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->format); + buffer.encode_uint32(2, this->sample_rate); + buffer.encode_uint32(3, this->num_channels); + buffer.encode_enum(4, this->purpose); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void MediaPlayerSupportedFormat::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("MediaPlayerSupportedFormat {\n"); + out.append(" format: "); + out.append("'").append(this->format).append("'"); + out.append("\n"); + + out.append(" sample_rate: "); + sprintf(buffer, "%" PRIu32, this->sample_rate); + out.append(buffer); + out.append("\n"); + + out.append(" num_channels: "); + sprintf(buffer, "%" PRIu32, this->num_channels); + out.append(buffer); + out.append("\n"); + + out.append(" purpose: "); + out.append(proto_enum_to_string(this->purpose)); + out.append("\n"); + out.append("}"); +} +#endif bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 6: { @@ -5159,6 +5229,10 @@ bool ListEntitiesMediaPlayerResponse::decode_length(uint32_t field_id, ProtoLeng this->icon = value.as_string(); return true; } + case 9: { + this->supported_formats.push_back(value.as_message()); + return true; + } default: return false; } @@ -5182,6 +5256,9 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(6, this->disabled_by_default); buffer.encode_enum(7, this->entity_category); buffer.encode_bool(8, this->supports_pause); + for (auto &it : this->supported_formats) { + buffer.encode_message(9, it, true); + } } #ifdef HAS_PROTO_MESSAGE_DUMP void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { @@ -5219,6 +5296,12 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { out.append(" supports_pause: "); out.append(YESNO(this->supports_pause)); out.append("\n"); + + for (const auto &it : this->supported_formats) { + out.append(" supported_formats: "); + it.dump_to(out); + out.append("\n"); + } out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 3eb945fd8d..3f609c793c 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -156,6 +156,10 @@ enum MediaPlayerCommand : uint32_t { MEDIA_PLAYER_COMMAND_MUTE = 3, MEDIA_PLAYER_COMMAND_UNMUTE = 4, }; +enum MediaPlayerFormatPurpose : uint32_t { + MEDIA_PLAYER_FORMAT_PURPOSE_DEFAULT = 0, + MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1, +}; enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT = 0, BLUETOOTH_DEVICE_REQUEST_TYPE_DISCONNECT = 1, @@ -1267,6 +1271,21 @@ class ButtonCommandRequest : public ProtoMessage { protected: bool decode_32bit(uint32_t field_id, Proto32Bit value) override; }; +class MediaPlayerSupportedFormat : public ProtoMessage { + public: + std::string format{}; + uint32_t sample_rate{0}; + uint32_t num_channels{0}; + enums::MediaPlayerFormatPurpose purpose{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; class ListEntitiesMediaPlayerResponse : public ProtoMessage { public: std::string object_id{}; @@ -1277,6 +1296,7 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage { bool disabled_by_default{false}; enums::EntityCategory entity_category{}; bool supports_pause{false}; + std::vector supported_formats{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 269a755e9e..16c0e5654f 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -311,6 +311,14 @@ bool APIServerConnectionBase::send_list_entities_button_response(const ListEntit #ifdef USE_BUTTON #endif #ifdef USE_MEDIA_PLAYER +bool APIServerConnectionBase::send_media_player_supported_format(const MediaPlayerSupportedFormat &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_media_player_supported_format: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 119); +} +#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()); @@ -1135,6 +1143,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_update_command_request: %s", msg.dump().c_str()); #endif this->on_update_command_request(msg); +#endif + break; + } + case 119: { +#ifdef USE_MEDIA_PLAYER + MediaPlayerSupportedFormat msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_media_player_supported_format: %s", msg.dump().c_str()); +#endif + this->on_media_player_supported_format(msg); #endif break; } diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 83bfc2ed98..83b5e3a444 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -145,6 +145,10 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_BUTTON virtual void on_button_command_request(const ButtonCommandRequest &value){}; #endif +#ifdef USE_MEDIA_PLAYER + bool send_media_player_supported_format(const MediaPlayerSupportedFormat &msg); + virtual void on_media_player_supported_format(const MediaPlayerSupportedFormat &value){}; +#endif #ifdef USE_MEDIA_PLAYER bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg); #endif diff --git a/esphome/components/media_player/media_player.h b/esphome/components/media_player/media_player.h index 77746e1808..26bef55afc 100644 --- a/esphome/components/media_player/media_player.h +++ b/esphome/components/media_player/media_player.h @@ -27,6 +27,18 @@ enum MediaPlayerCommand : uint8_t { }; const char *media_player_command_to_string(MediaPlayerCommand command); +enum class MediaPlayerFormatPurpose : uint8_t { + PURPOSE_DEFAULT = 0, + PURPOSE_ANNOUNCEMENT = 1, +}; + +struct MediaPlayerSupportedFormat { + std::string format; + uint32_t sample_rate; + uint32_t num_channels; + MediaPlayerFormatPurpose purpose; +}; + class MediaPlayer; class MediaPlayerTraits { @@ -37,8 +49,11 @@ class MediaPlayerTraits { bool get_supports_pause() const { return this->supports_pause_; } + std::vector &get_supported_formats() { return this->supported_formats_; } + protected: bool supports_pause_{false}; + std::vector supported_formats_{}; }; class MediaPlayerCall { From 7e18a5c44f70a5eea4bff4d94e4c05a2311f8f0e Mon Sep 17 00:00:00 2001 From: Angel Nunez Mencias Date: Tue, 27 Aug 2024 03:26:01 +0200 Subject: [PATCH 0266/1052] Add reset to esp32_rmt_led_strip (#7354) --- .../esp32_rmt_led_strip/led_strip.cpp | 16 +++++++++++-- .../esp32_rmt_led_strip/led_strip.h | 5 ++-- .../components/esp32_rmt_led_strip/light.py | 24 +++++++++++++++---- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index 7727b64f29..71ab099de5 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -38,7 +38,8 @@ void ESP32RMTLEDStripLightOutput::setup() { } ExternalRAMAllocator rmt_allocator(ExternalRAMAllocator::ALLOW_FAILURE); - this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8); // 8 bits per byte, 1 rmt_item32_t per bit + this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + + 1); // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset rmt_config_t config; memset(&config, 0, sizeof(config)); @@ -66,7 +67,7 @@ void ESP32RMTLEDStripLightOutput::setup() { } void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, - uint32_t bit1_low) { + uint32_t bit1_low, uint32_t reset_time_high, uint32_t reset_time_low) { float ratio = (float) RMT_CLK_FREQ / RMT_CLK_DIV / 1e09f; // 0-bit @@ -79,6 +80,11 @@ void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bi this->bit1_.level0 = 1; this->bit1_.duration1 = (uint32_t) (ratio * bit1_low); this->bit1_.level1 = 0; + // reset + this->reset_.duration0 = (uint32_t) (ratio * reset_time_high); + this->reset_.level0 = 1; + this->reset_.duration1 = (uint32_t) (ratio * reset_time_low); + this->reset_.level1 = 0; } void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { @@ -118,6 +124,12 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { psrc++; } + if (this->reset_.duration0 > 0 || this->reset_.duration1 > 0) { + pdest->val = this->reset_.val; + pdest++; + len++; + } + if (rmt_write_items(this->channel_, this->rmt_buf_, len, false) != ESP_OK) { ESP_LOGE(TAG, "RMT TX error"); this->status_set_warning(); diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index e9b19c9399..43215cf12b 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -49,7 +49,8 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { /// Set a maximum refresh rate in µs as some lights do not like being updated too often. void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; } - void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low); + void set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, uint32_t bit1_low, + uint32_t reset_time_high, uint32_t reset_time_low); void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; } void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; } @@ -75,7 +76,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { bool is_rgbw_; bool is_wrgb_; - rmt_item32_t bit0_, bit1_; + rmt_item32_t bit0_, bit1_, reset_; RGBOrder rgb_order_; rmt_channel_t channel_; diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 4c8472b8d2..4a04918275 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -43,13 +43,15 @@ class LEDStripTimings: bit0_low: int bit1_high: int bit1_low: int + reset_high: int + reset_low: int CHIPSETS = { - "WS2812": LEDStripTimings(400, 1000, 1000, 400), - "SK6812": LEDStripTimings(300, 900, 600, 600), - "APA106": LEDStripTimings(350, 1360, 1360, 350), - "SM16703": LEDStripTimings(300, 900, 900, 300), + "WS2812": LEDStripTimings(400, 1000, 1000, 400, 0, 0), + "SK6812": LEDStripTimings(300, 900, 600, 600, 0, 0), + "APA106": LEDStripTimings(350, 1360, 1360, 350, 0, 0), + "SM16703": LEDStripTimings(300, 900, 900, 300, 0, 0), } @@ -58,6 +60,8 @@ CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_LOW = "bit0_low" CONF_BIT1_HIGH = "bit1_high" CONF_BIT1_LOW = "bit1_low" +CONF_RESET_HIGH = "reset_high" +CONF_RESET_LOW = "reset_low" CONFIG_SCHEMA = cv.All( @@ -88,6 +92,14 @@ CONFIG_SCHEMA = cv.All( CONF_BIT1_LOW, "custom", ): cv.positive_time_period_nanoseconds, + cv.Optional( + CONF_RESET_HIGH, + default="0 us", + ): cv.positive_time_period_nanoseconds, + cv.Optional( + CONF_RESET_LOW, + default="0 us", + ): cv.positive_time_period_nanoseconds, } ), cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH), @@ -113,6 +125,8 @@ async def to_code(config): chipset.bit0_low, chipset.bit1_high, chipset.bit1_low, + chipset.reset_high, + chipset.reset_low, ) ) else: @@ -122,6 +136,8 @@ async def to_code(config): config[CONF_BIT0_LOW], config[CONF_BIT1_HIGH], config[CONF_BIT1_LOW], + config[CONF_RESET_HIGH], + config[CONF_RESET_LOW], ) ) From 34cce0e9201ab5408cfcd18ee7d2d7c5a24ffdd5 Mon Sep 17 00:00:00 2001 From: Gilles van den Hoven Date: Tue, 27 Aug 2024 14:07:32 +0200 Subject: [PATCH 0267/1052] [ili9xxx] Make `invert_colors` required (#7292) Co-authored-by: Gilles van den Hoven Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/ili9xxx/display.py | 35 +++++++++--------- .../components/ili9xxx/ili9xxx_display.cpp | 2 +- esphome/components/ili9xxx/ili9xxx_display.h | 36 +++++++++---------- esphome/components/ili9xxx/ili9xxx_init.h | 3 -- tests/components/ili9xxx/test.esp32-ard.yaml | 1 + .../components/ili9xxx/test.esp32-c3-ard.yaml | 1 + .../components/ili9xxx/test.esp32-c3-idf.yaml | 1 + .../components/ili9xxx/test.esp8266-ard.yaml | 1 + tests/components/ili9xxx/test.rp2040-ard.yaml | 1 + 9 files changed, 41 insertions(+), 40 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 483f2b886c..2182ca9a6d 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -1,31 +1,31 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import core, pins -from esphome.components import display, spi, font +import esphome.codegen as cg +from esphome.components import display, font, spi from esphome.components.display import validate_rotation -from esphome.core import CORE, HexInt +import esphome.config_validation as cv from esphome.const import ( + CONF_COLOR_ORDER, CONF_COLOR_PALETTE, CONF_DC_PIN, - CONF_ID, - CONF_LAMBDA, - CONF_MODEL, - CONF_RAW_DATA_ID, - CONF_PAGES, - CONF_RESET_PIN, CONF_DIMENSIONS, - CONF_WIDTH, CONF_HEIGHT, - CONF_ROTATION, + CONF_ID, + CONF_INVERT_COLORS, + CONF_LAMBDA, CONF_MIRROR_X, CONF_MIRROR_Y, - CONF_SWAP_XY, - CONF_COLOR_ORDER, + CONF_MODEL, CONF_OFFSET_HEIGHT, CONF_OFFSET_WIDTH, + CONF_PAGES, + CONF_RAW_DATA_ID, + CONF_RESET_PIN, + CONF_ROTATION, + CONF_SWAP_XY, CONF_TRANSFORM, - CONF_INVERT_COLORS, + CONF_WIDTH, ) +from esphome.core import CORE, HexInt DEPENDENCIES = ["spi"] @@ -177,7 +177,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_INVERT_DISPLAY): cv.invalid( "'invert_display' has been replaced by 'invert_colors'" ), - cv.Optional(CONF_INVERT_COLORS): cv.boolean, + cv.Required(CONF_INVERT_COLORS): cv.boolean, cv.Optional(CONF_COLOR_ORDER): cv.one_of(*COLOR_ORDERS.keys(), upper=True), cv.Exclusive(CONF_ROTATION, CONF_ROTATION): validate_rotation, cv.Exclusive(CONF_TRANSFORM, CONF_ROTATION): cv.Schema( @@ -287,5 +287,4 @@ async def to_code(config): prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) cg.add(var.set_palette(prog_arr)) - if CONF_INVERT_COLORS in config: - cg.add(var.invert_colors(config[CONF_INVERT_COLORS])) + cg.add(var.invert_colors(config[CONF_INVERT_COLORS])) diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index 4f035edbb0..81976dd2c9 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -118,6 +118,7 @@ void ILI9XXXDisplay::dump_config() { 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_)); if (this->is_failed()) { ESP_LOGCONFIG(TAG, " => Failed to init Memory: YES!"); @@ -154,7 +155,6 @@ void ILI9XXXDisplay::fill(Color color) { } } return; - break; default: new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB); break; diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 6121488d15..5033f702de 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -28,8 +28,8 @@ class ILI9XXXDisplay : public display::DisplayBuffer, spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_40MHZ> { public: ILI9XXXDisplay() = default; - ILI9XXXDisplay(uint8_t const *init_sequence, int16_t width, int16_t height, bool invert_colors) - : init_sequence_{init_sequence}, width_{width}, height_{height}, pre_invertcolors_{invert_colors} { + ILI9XXXDisplay(uint8_t const *init_sequence, int16_t width, int16_t height) + : init_sequence_{init_sequence}, width_{width}, height_{height} { uint8_t cmd, num_args, bits; const uint8_t *addr = init_sequence; while ((cmd = *addr++) != 0) { @@ -144,7 +144,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, bool need_update_ = false; bool is_18bitdisplay_ = false; PixelMode pixel_mode_{}; - bool pre_invertcolors_ = false; + bool pre_invertcolors_{}; display::ColorOrder color_order_{display::COLOR_ORDER_BGR}; bool swap_xy_{}; bool mirror_x_{}; @@ -154,54 +154,54 @@ class ILI9XXXDisplay : public display::DisplayBuffer, //----------- M5Stack display -------------- class ILI9XXXM5Stack : public ILI9XXXDisplay { public: - ILI9XXXM5Stack() : ILI9XXXDisplay(INITCMD_M5STACK, 320, 240, true) {} + ILI9XXXM5Stack() : ILI9XXXDisplay(INITCMD_M5STACK, 320, 240) {} }; //----------- M5Stack display -------------- class ILI9XXXM5CORE : public ILI9XXXDisplay { public: - ILI9XXXM5CORE() : ILI9XXXDisplay(INITCMD_M5CORE, 320, 240, true) {} + ILI9XXXM5CORE() : ILI9XXXDisplay(INITCMD_M5CORE, 320, 240) {} }; //----------- ST7789V display -------------- class ILI9XXXST7789V : public ILI9XXXDisplay { public: - ILI9XXXST7789V() : ILI9XXXDisplay(INITCMD_ST7789V, 240, 320, false) {} + ILI9XXXST7789V() : ILI9XXXDisplay(INITCMD_ST7789V, 240, 320) {} }; //----------- ILI9XXX_24_TFT display -------------- class ILI9XXXILI9341 : public ILI9XXXDisplay { public: - ILI9XXXILI9341() : ILI9XXXDisplay(INITCMD_ILI9341, 240, 320, false) {} + ILI9XXXILI9341() : ILI9XXXDisplay(INITCMD_ILI9341, 240, 320) {} }; //----------- ILI9XXX_24_TFT rotated display -------------- class ILI9XXXILI9342 : public ILI9XXXDisplay { public: - ILI9XXXILI9342() : ILI9XXXDisplay(INITCMD_ILI9341, 320, 240, false) {} + ILI9XXXILI9342() : ILI9XXXDisplay(INITCMD_ILI9341, 320, 240) {} }; //----------- ILI9XXX_??_TFT rotated display -------------- class ILI9XXXILI9481 : public ILI9XXXDisplay { public: - ILI9XXXILI9481() : ILI9XXXDisplay(INITCMD_ILI9481, 480, 320, false) {} + ILI9XXXILI9481() : ILI9XXXDisplay(INITCMD_ILI9481, 480, 320) {} }; //----------- ILI9481 in 18 bit mode -------------- class ILI9XXXILI948118 : public ILI9XXXDisplay { public: - ILI9XXXILI948118() : ILI9XXXDisplay(INITCMD_ILI9481_18, 320, 480, true) {} + ILI9XXXILI948118() : ILI9XXXDisplay(INITCMD_ILI9481_18, 320, 480) {} }; //----------- ILI9XXX_35_TFT rotated display -------------- class ILI9XXXILI9486 : public ILI9XXXDisplay { public: - ILI9XXXILI9486() : ILI9XXXDisplay(INITCMD_ILI9486, 480, 320, false) {} + ILI9XXXILI9486() : ILI9XXXDisplay(INITCMD_ILI9486, 480, 320) {} }; class ILI9XXXILI9488 : public ILI9XXXDisplay { public: - ILI9XXXILI9488(const uint8_t *seq = INITCMD_ILI9488) : ILI9XXXDisplay(seq, 480, 320, true) {} + ILI9XXXILI9488(const uint8_t *seq = INITCMD_ILI9488) : ILI9XXXDisplay(seq, 480, 320) {} protected: void set_madctl() override { @@ -246,34 +246,34 @@ class WAVESHARERES35 : public ILI9XXXILI9488 { //----------- ILI9XXX_35_TFT origin colors rotated display -------------- class ILI9XXXILI9488A : public ILI9XXXDisplay { public: - ILI9XXXILI9488A() : ILI9XXXDisplay(INITCMD_ILI9488_A, 480, 320, true) {} + ILI9XXXILI9488A() : ILI9XXXDisplay(INITCMD_ILI9488_A, 480, 320) {} }; //----------- ILI9XXX_35_TFT rotated display -------------- class ILI9XXXST7796 : public ILI9XXXDisplay { public: - ILI9XXXST7796() : ILI9XXXDisplay(INITCMD_ST7796, 320, 480, false) {} + ILI9XXXST7796() : ILI9XXXDisplay(INITCMD_ST7796, 320, 480) {} }; class ILI9XXXS3Box : public ILI9XXXDisplay { public: - ILI9XXXS3Box() : ILI9XXXDisplay(INITCMD_S3BOX, 320, 240, false) {} + ILI9XXXS3Box() : ILI9XXXDisplay(INITCMD_S3BOX, 320, 240) {} }; class ILI9XXXS3BoxLite : public ILI9XXXDisplay { public: - ILI9XXXS3BoxLite() : ILI9XXXDisplay(INITCMD_S3BOXLITE, 320, 240, true) {} + ILI9XXXS3BoxLite() : ILI9XXXDisplay(INITCMD_S3BOXLITE, 320, 240) {} }; class ILI9XXXGC9A01A : public ILI9XXXDisplay { public: - ILI9XXXGC9A01A() : ILI9XXXDisplay(INITCMD_GC9A01A, 240, 240, true) {} + ILI9XXXGC9A01A() : ILI9XXXDisplay(INITCMD_GC9A01A, 240, 240) {} }; //----------- ILI9XXX_24_TFT display -------------- class ILI9XXXST7735 : public ILI9XXXDisplay { public: - ILI9XXXST7735() : ILI9XXXDisplay(INITCMD_ST7735, 128, 160, false) {} + ILI9XXXST7735() : ILI9XXXDisplay(INITCMD_ST7735, 128, 160) {} }; } // namespace ili9xxx diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h index 5a67812bc1..b176680f43 100644 --- a/esphome/components/ili9xxx/ili9xxx_init.h +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -101,7 +101,6 @@ static const uint8_t PROGMEM INITCMD_ILI9481[] = { ILI9XXX_MADCTL , 1, MADCTL_MV | MADCTL_BGR, // Memory Access Control ILI9XXX_CSCON , 1, 0x01, ILI9XXX_PIXFMT, 1, 0x55, // 16 bit mode - ILI9XXX_INVON, 0, ILI9XXX_DISPON, 0x80, // Set display on 0x00 // end }; @@ -121,7 +120,6 @@ static const uint8_t PROGMEM INITCMD_ILI9481_18[] = { ILI9XXX_MADCTL , 1, MADCTL_MX| MADCTL_BGR, // Memory Access Control ILI9XXX_CSCON , 1, 0x01, ILI9XXX_PIXFMT, 1, 0x66, // 18 bit mode - ILI9XXX_INVON, 0, ILI9XXX_DISPON, 0x80, // Set display on 0x00 // end }; @@ -204,7 +202,6 @@ static const uint8_t PROGMEM INITCMD_ILI9488_A[] = { ILI9XXX_SLPOUT, 0x80, // Exit sleep mode - //ILI9XXX_INVON , 0, ILI9XXX_DISPON, 0x80, // Set display on 0x00 // end }; diff --git a/tests/components/ili9xxx/test.esp32-ard.yaml b/tests/components/ili9xxx/test.esp32-ard.yaml index ecee21686e..850273230a 100644 --- a/tests/components/ili9xxx/test.esp32-ard.yaml +++ b/tests/components/ili9xxx/test.esp32-ard.yaml @@ -19,6 +19,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9xxx + invert_colors: false dimensions: width: 320 height: 240 diff --git a/tests/components/ili9xxx/test.esp32-c3-ard.yaml b/tests/components/ili9xxx/test.esp32-c3-ard.yaml index 9526ae1f6b..fd03bd54b7 100644 --- a/tests/components/ili9xxx/test.esp32-c3-ard.yaml +++ b/tests/components/ili9xxx/test.esp32-c3-ard.yaml @@ -20,6 +20,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9xxx + invert_colors: false dimensions: width: 320 height: 240 diff --git a/tests/components/ili9xxx/test.esp32-c3-idf.yaml b/tests/components/ili9xxx/test.esp32-c3-idf.yaml index 9526ae1f6b..fd03bd54b7 100644 --- a/tests/components/ili9xxx/test.esp32-c3-idf.yaml +++ b/tests/components/ili9xxx/test.esp32-c3-idf.yaml @@ -20,6 +20,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9xxx + invert_colors: false dimensions: width: 320 height: 240 diff --git a/tests/components/ili9xxx/test.esp8266-ard.yaml b/tests/components/ili9xxx/test.esp8266-ard.yaml index 0791c25aca..b8192e69d1 100644 --- a/tests/components/ili9xxx/test.esp8266-ard.yaml +++ b/tests/components/ili9xxx/test.esp8266-ard.yaml @@ -20,6 +20,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9xxx + invert_colors: false dimensions: width: 320 height: 240 diff --git a/tests/components/ili9xxx/test.rp2040-ard.yaml b/tests/components/ili9xxx/test.rp2040-ard.yaml index 54083ebce8..0423f41a1c 100644 --- a/tests/components/ili9xxx/test.rp2040-ard.yaml +++ b/tests/components/ili9xxx/test.rp2040-ard.yaml @@ -20,6 +20,7 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9xxx + invert_colors: false dimensions: width: 320 height: 240 From 92ae506ffb0c04d91cad1318fa13847325ed05a6 Mon Sep 17 00:00:00 2001 From: Angel Nunez Mencias Date: Wed, 28 Aug 2024 01:40:21 +0200 Subject: [PATCH 0268/1052] Add WS2811 to esp32_rmt_led_strip (#7353) --- esphome/components/esp32_rmt_led_strip/light.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 4a04918275..1e3c2d4f72 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -48,6 +48,7 @@ class LEDStripTimings: CHIPSETS = { + "WS2811": LEDStripTimings(300, 1090, 1090, 320, 0, 300000), "WS2812": LEDStripTimings(400, 1000, 1000, 400, 0, 0), "SK6812": LEDStripTimings(300, 900, 600, 600, 0, 0), "APA106": LEDStripTimings(350, 1360, 1360, 350, 0, 0), From 388abaf09f8a5d2da7e4b8355738dad8852b752f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 23 Aug 2024 04:56:53 +1000 Subject: [PATCH 0269/1052] [lvgl] Bug fixes (#7338) --- esphome/components/lvgl/automation.py | 16 +++++++++++++--- esphome/components/lvgl/lvgl_esphome.cpp | 7 +++++++ esphome/components/lvgl/lvgl_esphome.h | 1 + esphome/components/lvgl/widgets/__init__.py | 6 ++++++ esphome/components/lvgl/widgets/line.py | 12 +++++++----- esphome/components/lvgl/widgets/msgbox.py | 3 ++- tests/components/lvgl/lvgl-package.yaml | 6 +++++- 7 files changed, 41 insertions(+), 10 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index a39f589136..efcac977ab 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -5,6 +5,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_TIMEOUT +from esphome.cpp_generator import RawExpression from esphome.cpp_types import nullptr from .defines import ( @@ -26,6 +27,7 @@ from .lvcode import ( add_line_marks, lv, lv_add, + lv_expr, lv_obj, lvgl_comp, ) @@ -38,7 +40,13 @@ from .types import ( lv_disp_t, lv_obj_t, ) -from .widgets import Widget, get_widgets, lv_scr_act, set_obj_properties +from .widgets import ( + Widget, + get_widgets, + lv_scr_act, + set_obj_properties, + wait_for_widgets, +) async def action_to_code( @@ -48,10 +56,12 @@ async def action_to_code( template_arg, args, ): + await wait_for_widgets() async with LambdaContext(parameters=args, where=action_id) as context: + with LvConditional(lv_expr.is_pre_initialise()): + context.add(RawExpression("return")) for widget in widgets: - with LvConditional(widget.obj != nullptr): - await action(widget) + await action(widget) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) return var diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 92f7a880c3..6882986e7c 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -294,6 +294,13 @@ void LvglComponent::loop() { } lv_timer_handler_run_in_period(5); } +bool lv_is_pre_initialise() { + if (!lv_is_initialized()) { + ESP_LOGE(TAG, "LVGL call before component is initialised"); + return true; + } + return false; +} #ifdef USE_LVGL_IMAGE lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) { diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 3a3d1aa6c5..df3d4aa68c 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -40,6 +40,7 @@ namespace lvgl { extern lv_event_code_t lv_api_event; // NOLINT extern lv_event_code_t lv_update_event; // NOLINT +extern bool lv_is_pre_initialise(); #ifdef USE_LVGL_COLOR inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } #endif // USE_LVGL_COLOR diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 4abb25c61d..50da6e131d 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -1,3 +1,4 @@ +import asyncio import sys from typing import Any, Union @@ -223,6 +224,11 @@ async def get_widget_(wid: Widget): return await FakeAwaitable(get_widget_generator(wid)) +async def wait_for_widgets(): + while not Widget.widgets_completed: + await asyncio.sleep(0) + + async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: if not config: return [] diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 8ce4b1965f..4c6439fde4 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -3,7 +3,7 @@ import functools import esphome.codegen as cg import esphome.config_validation as cv -from ..defines import CONF_MAIN, literal +from ..defines import CONF_MAIN from ..lvcode import lv from ..types import LvType from . import Widget, WidgetType @@ -38,13 +38,15 @@ LINE_SCHEMA = { class LineType(WidgetType): def __init__(self): - super().__init__(CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA) + super().__init__( + CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, modify_schema={} + ) async def to_code(self, w: Widget, config): """For a line object, create and add the points""" - data = literal(config[CONF_POINTS]) - points = cg.static_const_array(config[CONF_POINT_LIST_ID], data) - lv.line_set_points(w.obj, points, len(data)) + if data := config.get(CONF_POINTS): + points = cg.static_const_array(config[CONF_POINT_LIST_ID], data) + lv.line_set_points(w.obj, points, len(data)) line_spec = LineType() diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index 63c4326c7c..c377af6bde 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -13,7 +13,7 @@ from ..defines import ( TYPE_FLEX, literal, ) -from ..helpers import add_lv_use +from ..helpers import add_lv_use, lvgl_components_required from ..lv_validation import lv_bool, lv_pct, lv_text from ..lvcode import ( EVENT_ARG, @@ -72,6 +72,7 @@ async def msgbox_to_code(conf): *buttonmatrix_spec.get_uses(), *button_spec.get_uses(), ) + lvgl_components_required.add("BUTTONMATRIX") messagebox_id = conf[CONF_ID] outer = lv_Pvariable(lv_obj_t, messagebox_id.id) buttonmatrix = new_Pvariable( diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 800d6eff27..1479ce7358 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -379,6 +379,7 @@ lvgl: format: "bar value %f" args: [x] - line: + id: lv_line_id align: center points: - 5, 5 @@ -387,7 +388,10 @@ lvgl: - 180, 60 - 240, 10 on_click: - lvgl.page.next: + - lvgl.widget.update: + id: lv_line_id + line_color: 0xFFFF + - lvgl.page.next: - switch: align: right_mid - checkbox: From 86777634922a9c1b695d2f3da30630a827ccd2cf Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 23 Aug 2024 18:09:40 +1000 Subject: [PATCH 0270/1052] [core] Clean build if the loaded integrations changed (#7344) --- esphome/writer.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/writer.py b/esphome/writer.py index c6111cbe3f..57435d3463 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -106,6 +106,8 @@ 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: + return True return False @@ -117,7 +119,9 @@ def update_storage_json(): return if storage_should_clean(old, new): - _LOGGER.info("Core config or version changed, cleaning build files...") + _LOGGER.info( + "Core config, version or integrations changed, cleaning build files..." + ) clean_build() new.save(path) From c1774c42c25350cebc4f649184ba9b1374c6397c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:03:25 +1000 Subject: [PATCH 0271/1052] [lvgl] Fix race condition involving numbers, switches etc. (#7345) --- esphome/components/lvgl/__init__.py | 5 ++++- esphome/components/lvgl/binary_sensor/__init__.py | 3 ++- esphome/components/lvgl/light/__init__.py | 3 ++- esphome/components/lvgl/lvcode.py | 3 +++ esphome/components/lvgl/number/__init__.py | 3 ++- esphome/components/lvgl/select/__init__.py | 3 ++- esphome/components/lvgl/sensor/__init__.py | 3 ++- esphome/components/lvgl/switch/__init__.py | 3 ++- esphome/components/lvgl/text/__init__.py | 3 ++- esphome/components/lvgl/text_sensor/__init__.py | 3 ++- esphome/components/lvgl/widgets/__init__.py | 13 ++++++++++--- 11 files changed, 33 insertions(+), 12 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 7c51d9c70d..ea020435dc 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -266,7 +266,10 @@ async def to_code(config): await add_top_layer(config) await msgboxes_to_code(config) await disp_update(f"{lv_component}->get_disp()", config) - Widget.set_completed() + # At this point only the setup code should be generated + assert LvContext.added_lambda_count == 1 + Widget.set_completed() + async with LvContext(lv_component): await generate_triggers(lv_component) for conf in config.get(CONF_ON_IDLE, ()): templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) diff --git a/esphome/components/lvgl/binary_sensor/__init__.py b/esphome/components/lvgl/binary_sensor/__init__.py index 8789a06375..56984405aa 100644 --- a/esphome/components/lvgl/binary_sensor/__init__.py +++ b/esphome/components/lvgl/binary_sensor/__init__.py @@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..lvcode import EVENT_ARG, LambdaContext, LvContext from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, lv_pseudo_button_t -from ..widgets import Widget, get_widgets +from ..widgets import Widget, get_widgets, wait_for_widgets CONFIG_SCHEMA = ( binary_sensor_schema(BinarySensor) @@ -29,6 +29,7 @@ async def to_code(config): widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] assert isinstance(widget, Widget) + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pressed_ctx: pressed_ctx.add(sensor.publish_state(widget.is_pressed())) async with LvContext(paren) as ctx: diff --git a/esphome/components/lvgl/light/__init__.py b/esphome/components/lvgl/light/__init__.py index 27c160dff6..a0eeded349 100644 --- a/esphome/components/lvgl/light/__init__.py +++ b/esphome/components/lvgl/light/__init__.py @@ -8,7 +8,7 @@ from ..defines import CONF_LVGL_ID from ..lvcode import LvContext from ..schemas import LVGL_SCHEMA from ..types import LvType, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets lv_led_t = LvType("lv_led_t") LVLight = lvgl_ns.class_("LVLight", LightOutput) @@ -28,5 +28,6 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_LED) widget = widget[0] + await wait_for_widgets() async with LvContext(paren) as ctx: ctx.add(var.set_obj(widget.obj)) diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 6d7e364e5d..8d029ce0ca 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -176,6 +176,8 @@ class LvContext(LambdaContext): Code generation into the LVGL initialisation code (called in `setup()`) """ + added_lambda_count = 0 + def __init__(self, lv_component, args=None): self.args = args or LVGL_COMP_ARG super().__init__(parameters=self.args) @@ -183,6 +185,7 @@ class LvContext(LambdaContext): async def add_init_lambda(self): cg.add(self.lv_component.add_init_lambda(await self.get_lambda())) + LvContext.added_lambda_count += 1 async def __aexit__(self, exc_type, exc_val, exc_tb): await super().__aexit__(exc_type, exc_val, exc_tb) diff --git a/esphome/components/lvgl/number/__init__.py b/esphome/components/lvgl/number/__init__.py index 6336bb0632..3e1ede0dec 100644 --- a/esphome/components/lvgl/number/__init__.py +++ b/esphome/components/lvgl/number/__init__.py @@ -16,7 +16,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number) @@ -44,6 +44,7 @@ async def to_code(config): step=widget.get_step(), ) + await wait_for_widgets() async with LambdaContext([(cg.float_, "v")]) as control: await widget.set_property( "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py index b55bde13bc..e77d0cfb32 100644 --- a/esphome/components/lvgl/select/__init__.py +++ b/esphome/components/lvgl/select/__init__.py @@ -15,7 +15,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvSelect, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) @@ -37,6 +37,7 @@ async def to_code(config): options = widget.config.get(CONF_OPTIONS, []) selector = await select.new_select(config, options=options) paren = await cg.get_variable(config[CONF_LVGL_ID]) + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pub_ctx: pub_ctx.add(selector.publish_index(widget.get_value())) async with LambdaContext([(cg.uint16, "v")]) as control: diff --git a/esphome/components/lvgl/sensor/__init__.py b/esphome/components/lvgl/sensor/__init__.py index 82e21d5e95..a2a2298c27 100644 --- a/esphome/components/lvgl/sensor/__init__.py +++ b/esphome/components/lvgl/sensor/__init__.py @@ -14,7 +14,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber -from ..widgets import Widget, get_widgets +from ..widgets import Widget, get_widgets, wait_for_widgets CONFIG_SCHEMA = ( sensor_schema(Sensor) @@ -33,6 +33,7 @@ async def to_code(config): widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] assert isinstance(widget, Widget) + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as lamb: lv_add(sensor.publish_state(widget.get_value())) async with LvContext(paren, LVGL_COMP_ARG): diff --git a/esphome/components/lvgl/switch/__init__.py b/esphome/components/lvgl/switch/__init__.py index 957fce17ff..f855c2a034 100644 --- a/esphome/components/lvgl/switch/__init__.py +++ b/esphome/components/lvgl/switch/__init__.py @@ -16,7 +16,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch) CONFIG_SCHEMA = ( @@ -35,6 +35,7 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as checked_ctx: checked_ctx.add(switch.publish_state(widget.get_value())) async with LambdaContext([(cg.bool_, "v")]) as control: diff --git a/esphome/components/lvgl/text/__init__.py b/esphome/components/lvgl/text/__init__.py index 9ee494d8a0..56fa42e131 100644 --- a/esphome/components/lvgl/text/__init__.py +++ b/esphome/components/lvgl/text/__init__.py @@ -15,7 +15,7 @@ from ..lvcode import ( ) from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvText, lvgl_ns -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets LVGLText = lvgl_ns.class_("LVGLText", text.Text) @@ -32,6 +32,7 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] + await wait_for_widgets() async with LambdaContext([(cg.std_string, "text_value")]) as control: await widget.set_property("text", "text_value.c_str())") lv.event_send(widget.obj, API_EVENT, None) diff --git a/esphome/components/lvgl/text_sensor/__init__.py b/esphome/components/lvgl/text_sensor/__init__.py index cab715dce0..ae39eec291 100644 --- a/esphome/components/lvgl/text_sensor/__init__.py +++ b/esphome/components/lvgl/text_sensor/__init__.py @@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvText -from ..widgets import get_widgets +from ..widgets import get_widgets, wait_for_widgets CONFIG_SCHEMA = ( text_sensor_schema(TextSensor) @@ -28,6 +28,7 @@ async def to_code(config): paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] + await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pressed_ctx: pressed_ctx.add(sensor.publish_state(widget.get_value())) async with LvContext(paren) as ctx: diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 50da6e131d..17d73c1714 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -1,4 +1,3 @@ -import asyncio import sys from typing import Any, Union @@ -224,9 +223,17 @@ async def get_widget_(wid: Widget): return await FakeAwaitable(get_widget_generator(wid)) +def widgets_wait_generator(): + while True: + if Widget.widgets_completed: + return + yield + + async def wait_for_widgets(): - while not Widget.widgets_completed: - await asyncio.sleep(0) + if Widget.widgets_completed: + return + await FakeAwaitable(widgets_wait_generator()) async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: From 9975e8b544d0d2943503b90e75a4723d130e68b9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 27 Aug 2024 10:20:26 +1200 Subject: [PATCH 0272/1052] [api] Fix sending the ``once`` flag on ha entity subscription (#7357) --- esphome/components/api/api_connection.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index bd438265d4..195fcab0ab 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -179,6 +179,7 @@ void APIConnection::loop() { SubscribeHomeAssistantStateResponse resp; resp.entity_id = it.entity_id; resp.attribute = it.attribute.value(); + resp.once = it.once; if (this->send_subscribe_home_assistant_state_response(resp)) { state_subs_at_++; } From 28eda4b220519edfd761d65c3d24f0df0aab4f3c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:54:31 +1200 Subject: [PATCH 0273/1052] Bump version to 2024.8.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index f99d442be3..b27949bf29 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.0" +__version__ = "2024.8.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From d6df466237142c542d4ea19ddabe0a7a2f7bb898 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 28 Aug 2024 14:29:41 +1000 Subject: [PATCH 0274/1052] [lvgl] Add lvgl.widget.focus action and related triggers. (#7315) --- esphome/components/lvgl/__init__.py | 15 +++- esphome/components/lvgl/automation.py | 81 ++++++++++++++++++++- esphome/components/lvgl/defines.py | 5 +- esphome/components/lvgl/lvcode.py | 6 +- esphome/components/lvgl/lvgl_esphome.cpp | 54 ++++++++++++++ esphome/components/lvgl/lvgl_esphome.h | 9 +++ esphome/components/lvgl/schemas.py | 10 +-- esphome/components/lvgl/trigger.py | 5 +- esphome/components/lvgl/types.py | 2 +- esphome/components/lvgl/widgets/__init__.py | 4 +- esphome/components/lvgl/widgets/arc.py | 6 +- esphome/components/lvgl/widgets/page.py | 58 +++++++++++++-- tests/components/lvgl/lvgl-package.yaml | 25 +++++++ 13 files changed, 253 insertions(+), 27 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index ea020435dc..cab6462d1a 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -21,8 +21,8 @@ from esphome.final_validate import full_config from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid -from .automation import disp_update, update_to_code -from .defines import CONF_SKIP +from .automation import disp_update, focused_widgets, update_to_code +from .defines import CONF_ADJUSTABLE, CONF_SKIP from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent @@ -67,7 +67,7 @@ from .widgets.lv_bar import bar_spec from .widgets.meter import meter_spec from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code from .widgets.obj import obj_spec -from .widgets.page import add_pages, page_spec +from .widgets.page import add_pages, generate_page_triggers, page_spec from .widgets.roller import roller_spec from .widgets.slider import slider_spec from .widgets.spinbox import spinbox_spec @@ -182,6 +182,14 @@ def final_validation(config): raise cv.Invalid( "Using RGBA or RGB24 in image config not compatible with LVGL", path ) + for w in focused_widgets: + path = global_config.get_path_for_id(w) + widget_conf = global_config.get_config_for_path(path[:-1]) + if CONF_ADJUSTABLE in widget_conf and not widget_conf[CONF_ADJUSTABLE]: + raise cv.Invalid( + "A non adjustable arc may not be focused", + path, + ) async def to_code(config): @@ -271,6 +279,7 @@ async def to_code(config): Widget.set_completed() async with LvContext(lv_component): await generate_triggers(lv_component) + await generate_page_triggers(lv_component, config) for conf in config.get(CONF_ON_IDLE, ()): templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index eb1b54e3ec..8138551c30 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -4,13 +4,15 @@ from typing import Callable from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_TIMEOUT -from esphome.cpp_generator import RawExpression +from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT +from esphome.cpp_generator import RawExpression, get_variable from esphome.cpp_types import nullptr from .defines import ( CONF_DISP_BG_COLOR, CONF_DISP_BG_IMAGE, + CONF_EDITING, + CONF_FREEZE, CONF_LVGL_ID, CONF_SHOW_SNOW, literal, @@ -30,6 +32,7 @@ from .lvcode import ( lv_expr, lv_obj, lvgl_comp, + static_cast, ) from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA from .types import ( @@ -38,7 +41,9 @@ from .types import ( LvglCondition, ObjUpdateAction, lv_disp_t, + lv_group_t, lv_obj_t, + lv_pseudo_button_t, ) from .widgets import ( Widget, @@ -48,6 +53,9 @@ from .widgets import ( wait_for_widgets, ) +# Record widgets that are used in a focused action here +focused_widgets = set() + async def action_to_code( widgets: list[Widget], @@ -234,3 +242,72 @@ async def obj_show_to_code(config, action_id, template_arg, args): return await action_to_code( await get_widgets(config), do_show, action_id, template_arg, args ) + + +def focused_id(value): + value = cv.use_id(lv_pseudo_button_t)(value) + focused_widgets.add(value) + return value + + +@automation.register_action( + "lvgl.widget.focus", + ObjUpdateAction, + cv.Any( + cv.maybe_simple_value( + { + cv.Optional(CONF_GROUP): cv.use_id(lv_group_t), + cv.Required(CONF_ACTION): cv.one_of( + "MARK", "RESTORE", "NEXT", "PREVIOUS", upper=True + ), + cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), + cv.Optional(CONF_FREEZE, default=False): cv.boolean, + }, + key=CONF_ACTION, + ), + cv.maybe_simple_value( + { + cv.Required(CONF_ID): focused_id, + cv.Optional(CONF_FREEZE, default=False): cv.boolean, + cv.Optional(CONF_EDITING, default=False): cv.boolean, + }, + key=CONF_ID, + ), + ), +) +async def widget_focus(config, action_id, template_arg, args): + widget = await get_widgets(config) + if widget: + widget = widget[0] + group = static_cast( + lv_group_t.operator("ptr"), lv_expr.obj_get_group(widget.obj) + ) + elif group := config.get(CONF_GROUP): + group = await get_variable(group) + else: + group = lv_expr.group_get_default() + + async with LambdaContext(parameters=args, where=action_id) as context: + if widget: + lv.group_focus_freeze(group, False) + lv.group_focus_obj(widget.obj) + if config[CONF_EDITING]: + lv.group_set_editing(group, True) + else: + action = config[CONF_ACTION] + lv_comp = await get_variable(config[CONF_LVGL_ID]) + if action == "MARK": + context.add(lv_comp.set_focus_mark(group)) + else: + lv.group_focus_freeze(group, False) + if action == "RESTORE": + context.add(lv_comp.restore_focus_mark(group)) + elif action == "NEXT": + lv.group_focus_next(group) + else: + lv.group_focus_prev(group) + + if config[CONF_FREEZE]: + lv.group_focus_freeze(group, True) + var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) + return var diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 7bb1667e77..e05bf52120 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -148,6 +148,7 @@ LV_EVENT_MAP = { "DEFOCUS": "DEFOCUSED", "READY": "READY", "CANCEL": "CANCEL", + "ALL_EVENTS": "ALL", } LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP) @@ -390,6 +391,7 @@ CONF_DEFAULT_FONT = "default_font" CONF_DEFAULT_GROUP = "default_group" CONF_DIR = "dir" CONF_DISPLAYS = "displays" +CONF_EDITING = "editing" CONF_ENCODERS = "encoders" CONF_END_ANGLE = "end_angle" CONF_END_VALUE = "end_value" @@ -401,6 +403,7 @@ CONF_FLEX_ALIGN_MAIN = "flex_align_main" CONF_FLEX_ALIGN_CROSS = "flex_align_cross" CONF_FLEX_ALIGN_TRACK = "flex_align_track" CONF_FLEX_GROW = "flex_grow" +CONF_FREEZE = "freeze" CONF_FULL_REFRESH = "full_refresh" CONF_GRID_CELL_ROW_POS = "grid_cell_row_pos" CONF_GRID_CELL_COLUMN_POS = "grid_cell_column_pos" @@ -428,9 +431,9 @@ CONF_MSGBOXES = "msgboxes" CONF_OBJ = "obj" CONF_OFFSET_X = "offset_x" CONF_OFFSET_Y = "offset_y" +CONF_ONE_CHECKED = "one_checked" CONF_ONE_LINE = "one_line" CONF_ON_SELECT = "on_select" -CONF_ONE_CHECKED = "one_checked" CONF_NEXT = "next" CONF_PAD_ROW = "pad_row" CONF_PAD_COLUMN = "pad_column" diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 8d029ce0ca..a3d13f7f8c 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -28,7 +28,7 @@ LVGL_COMP = "lv_component" # used as a lambda argument in lvgl_comp() LvglComponent = lvgl_ns.class_("LvglComponent", cg.PollingComponent) LVGL_COMP_ARG = [(LvglComponent.operator("ptr"), LVGL_COMP)] lv_event_t_ptr = cg.global_ns.namespace("lv_event_t").operator("ptr") -EVENT_ARG = [(lv_event_t_ptr, "ev")] +EVENT_ARG = [(lv_event_t_ptr, "event")] # Two custom events; API_EVENT is fired when an entity is updated remotely by an API interaction; # UPDATE_EVENT is fired when an entity is programmatically updated locally. # VALUE_CHANGED is the event generated by LVGL when an entity's value changes through user interaction. @@ -291,6 +291,10 @@ class LvExpr(MockLv): pass +def static_cast(type, value): + return literal(f"static_cast<{type}>({value})") + + # Top level mock for generic lv_ calls to be recorded lv = MockLv("lv_") # Just generate an expression diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 6882986e7c..89c9828740 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -15,6 +15,60 @@ static void log_cb(const char *buf) { } #endif // LV_USE_LOG +static const char *const EVENT_NAMES[] = { + "NONE", + "PRESSED", + "PRESSING", + "PRESS_LOST", + "SHORT_CLICKED", + "LONG_PRESSED", + "LONG_PRESSED_REPEAT", + "CLICKED", + "RELEASED", + "SCROLL_BEGIN", + "SCROLL_END", + "SCROLL", + "GESTURE", + "KEY", + "FOCUSED", + "DEFOCUSED", + "LEAVE", + "HIT_TEST", + "COVER_CHECK", + "REFR_EXT_DRAW_SIZE", + "DRAW_MAIN_BEGIN", + "DRAW_MAIN", + "DRAW_MAIN_END", + "DRAW_POST_BEGIN", + "DRAW_POST", + "DRAW_POST_END", + "DRAW_PART_BEGIN", + "DRAW_PART_END", + "VALUE_CHANGED", + "INSERT", + "REFRESH", + "READY", + "CANCEL", + "DELETE", + "CHILD_CHANGED", + "CHILD_CREATED", + "CHILD_DELETED", + "SCREEN_UNLOAD_START", + "SCREEN_LOAD_START", + "SCREEN_LOADED", + "SCREEN_UNLOADED", + "SIZE_CHANGED", + "STYLE_CHANGED", + "LAYOUT_CHANGED", + "GET_SELF_SIZE", +}; + +std::string lv_event_code_name_for(uint8_t event_code) { + if (event_code < sizeof(EVENT_NAMES) / sizeof(EVENT_NAMES[0])) { + return EVENT_NAMES[event_code]; + } + return str_sprintf("%2d", event_code); +} static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { // make sure all coordinates are even if (area->x1 & 1) diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index df3d4aa68c..e248530971 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -40,6 +40,7 @@ namespace lvgl { extern lv_event_code_t lv_api_event; // NOLINT extern lv_event_code_t lv_update_event; // NOLINT +extern std::string lv_event_code_name_for(uint8_t event_code); extern bool lv_is_pre_initialise(); #ifdef USE_LVGL_COLOR inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } @@ -143,6 +144,13 @@ class LvglComponent : public PollingComponent { void show_next_page(lv_scr_load_anim_t anim, uint32_t time); void show_prev_page(lv_scr_load_anim_t anim, uint32_t time); void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; } + void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); } + void restore_focus_mark(lv_group_t *group) { + auto *mark = this->focus_marks_[group]; + if (mark != nullptr) { + lv_group_focus_obj(mark); + } + } protected: void write_random_(); @@ -158,6 +166,7 @@ class LvglComponent : public PollingComponent { bool show_snow_{}; lv_coord_t snow_line_{}; bool page_wrap_{true}; + std::map focus_marks_{}; std::vector> init_lambdas_; CallbackManager idle_callbacks_{}; diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index e9714e3b1a..548ebda6dc 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -20,7 +20,7 @@ from . import defines as df, lv_validation as lvalid from .defines import CONF_TIME_FORMAT from .helpers import add_lv_use, requires_component, validate_printf from .lv_validation import lv_color, lv_font, lv_image -from .lvcode import LvglComponent +from .lvcode import LvglComponent, lv_event_t_ptr from .types import ( LVEncoderListener, LvType, @@ -215,14 +215,12 @@ def automation_schema(typ: LvType): events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,) else: events = df.LV_EVENT_TRIGGERS - if isinstance(typ, LvType): - template = Trigger.template(typ.get_arg_type()) - else: - template = Trigger.template() + args = [typ.get_arg_type()] if isinstance(typ, LvType) else [] + args.append(lv_event_t_ptr) return { cv.Optional(event): validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(template), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger.template(*args)), } ) for event in events diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index ba93aabb2d..5288745fab 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -19,6 +19,7 @@ from .lvcode import ( LvConditional, lv, lv_add, + lv_event_t_ptr, ) from .types import LV_EVENT from .widgets import widget_map @@ -65,10 +66,10 @@ async def generate_triggers(lv_component): async def add_trigger(conf, lv_component, w, *events): tid = conf[CONF_TRIGGER_ID] trigger = cg.new_Pvariable(tid) - args = w.get_args() + args = w.get_args() + [(lv_event_t_ptr, "event")] value = w.get_value() await automation.build_automation(trigger, args, conf) async with LambdaContext(EVENT_ARG, where=tid) as context: with LvConditional(w.is_selected()): - lv_add(trigger.trigger(value)) + lv_add(trigger.trigger(value, literal("event"))) lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events)) diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index be17cf62c2..e4735ea58d 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -57,7 +57,7 @@ lv_group_t = cg.global_ns.struct("lv_group_t") LVTouchListener = lvgl_ns.class_("LVTouchListener") LVEncoderListener = lvgl_ns.class_("LVEncoderListener") lv_obj_t = LvType("lv_obj_t") -lv_page_t = cg.global_ns.class_("LvPageType", LvCompound) +lv_page_t = LvType("LvPageType", parents=(LvCompound,)) lv_img_t = LvType("lv_img_t") LV_EVENT = MockObj(base="LV_EVENT_", op="") diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 062c268135..ae06bf20b0 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -225,7 +225,7 @@ def get_widget_generator(wid): yield -async def get_widget_(wid: Widget): +async def get_widget_(wid): if obj := widget_map.get(wid): return obj return await FakeAwaitable(get_widget_generator(wid)) @@ -348,8 +348,6 @@ async def set_obj_properties(w: Widget, config): if group := config.get(CONF_GROUP): group = await cg.get_variable(group) lv.group_add_obj(group, w.obj) - flag_clr = set() - flag_set = set() props = parts[CONF_MAIN][CONF_DEFAULT] lambs = {} flag_set = set() diff --git a/esphome/components/lvgl/widgets/arc.py b/esphome/components/lvgl/widgets/arc.py index a6f8918e2f..dc120e4cbb 100644 --- a/esphome/components/lvgl/widgets/arc.py +++ b/esphome/components/lvgl/widgets/arc.py @@ -1,5 +1,6 @@ import esphome.config_validation as cv from esphome.const import ( + CONF_GROUP, CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, @@ -20,7 +21,7 @@ from ..defines import ( literal, ) from ..lv_validation import angle, get_start_value, lv_float -from ..lvcode import lv, lv_obj +from ..lvcode import lv, lv_expr, lv_obj from ..types import LvNumber, NumberType from . import Widget @@ -69,6 +70,9 @@ class ArcType(NumberType): if config.get(CONF_ADJUSTABLE) is False: lv_obj.remove_style(w.obj, nullptr, literal("LV_PART_KNOB")) w.clear_flag("LV_OBJ_FLAG_CLICKABLE") + elif CONF_GROUP not in config: + # For some reason arc does not get automatically added to the default group + lv.group_add_obj(lv_expr.group_get_default(), w.obj) value = await get_start_value(config) if value is not None: diff --git a/esphome/components/lvgl/widgets/page.py b/esphome/components/lvgl/widgets/page.py index f80d802b33..0e84ab6791 100644 --- a/esphome/components/lvgl/widgets/page.py +++ b/esphome/components/lvgl/widgets/page.py @@ -1,6 +1,7 @@ from esphome import automation, codegen as cg +from esphome.automation import Trigger import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME +from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME, CONF_TRIGGER_ID from ..defines import ( CONF_ANIMATION, @@ -9,12 +10,39 @@ from ..defines import ( CONF_PAGE_WRAP, CONF_SKIP, LV_ANIM, + literal, ) from ..lv_validation import lv_bool, lv_milliseconds -from ..lvcode import LVGL_COMP_ARG, LambdaContext, add_line_marks, lv_add, lvgl_comp +from ..lvcode import ( + EVENT_ARG, + LVGL_COMP_ARG, + LambdaContext, + add_line_marks, + lv_add, + lvgl_comp, +) from ..schemas import LVGL_SCHEMA from ..types import LvglAction, lv_page_t -from . import Widget, WidgetType, add_widgets, set_obj_properties +from . import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties + +CONF_ON_LOAD = "on_load" +CONF_ON_UNLOAD = "on_unload" + +PAGE_SCHEMA = cv.Schema( + { + cv.Optional(CONF_SKIP, default=False): lv_bool, + cv.Optional(CONF_ON_LOAD): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger.template()), + } + ), + cv.Optional(CONF_ON_UNLOAD): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger.template()), + } + ), + } +) class PageType(WidgetType): @@ -23,9 +51,8 @@ class PageType(WidgetType): CONF_PAGE, lv_page_t, (), - { - cv.Optional(CONF_SKIP, default=False): lv_bool, - }, + PAGE_SCHEMA, + modify_schema={}, ) async def to_code(self, w: Widget, config: dict): @@ -39,7 +66,6 @@ SHOW_SCHEMA = LVGL_SCHEMA.extend( } ) - page_spec = PageType() @@ -111,3 +137,21 @@ async def add_pages(lv_component, config): await set_obj_properties(page, config) await set_obj_properties(page, pconf) await add_widgets(page, pconf) + + +async def generate_page_triggers(lv_component, config): + for pconf in config.get(CONF_PAGES, ()): + page = (await get_widgets(pconf))[0] + for ev in (CONF_ON_LOAD, CONF_ON_UNLOAD): + for loaded in pconf.get(ev, ()): + trigger = cg.new_Pvariable(loaded[CONF_TRIGGER_ID]) + await automation.build_automation(trigger, [], loaded) + async with LambdaContext(EVENT_ARG, where=id) as context: + lv_add(trigger.trigger()) + lv_add( + lv_component.add_event_cb( + page.obj, + await context.get_lambda(), + literal(f"LV_EVENT_SCREEN_{ev[3:].upper()}_START"), + ) + ) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 0e2c37048b..737d8703b0 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -54,6 +54,17 @@ lvgl: long_press_time: 500ms pages: - id: page1 + on_load: + - logger.log: page loaded + - lvgl.widget.focus: + action: restore + on_unload: + - logger.log: page unloaded + - lvgl.widget.focus: mark + on_all_events: + logger.log: + format: "Event %s" + args: ['lv_event_code_name_for(event->code).c_str()'] skip: true layout: type: flex @@ -70,6 +81,10 @@ lvgl: repeat_count: 10 duration: 1s auto_start: true + on_all_events: + logger.log: + format: "Event %s" + args: ['lv_event_code_name_for(event->code).c_str()'] - label: id: hello_label text: Hello world @@ -229,6 +244,16 @@ lvgl: - label: text: Button on_click: + - lvgl.widget.focus: spin_up + - lvgl.widget.focus: next + - lvgl.widget.focus: previous + - lvgl.widget.focus: + action: previous + freeze: true + - lvgl.widget.focus: + id: spin_up + freeze: true + editing: true - lvgl.label.update: id: hello_label bg_color: 0x123456 From 4b2032a98e7b93c359da92af00d0926bb2d9551d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 29 Aug 2024 05:07:31 +1200 Subject: [PATCH 0275/1052] [datetime] Fix templated args (#7368) --- esphome/components/datetime/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 4fda97c5bc..5429121d56 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -186,7 +186,7 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args): date_config = config[CONF_DATE] if cg.is_template(date_config): - template_ = await cg.templatable(date_config, [], cg.ESPTime) + template_ = await cg.templatable(date_config, args, cg.ESPTime) cg.add(action_var.set_date(template_)) else: date_struct = cg.StructInitializer( @@ -217,7 +217,7 @@ async def datetime_time_set_to_code(config, action_id, template_arg, args): time_config = config[CONF_TIME] if cg.is_template(time_config): - template_ = await cg.templatable(time_config, [], cg.ESPTime) + template_ = await cg.templatable(time_config, args, cg.ESPTime) cg.add(action_var.set_time(template_)) else: time_struct = cg.StructInitializer( @@ -248,7 +248,7 @@ async def datetime_datetime_set_to_code(config, action_id, template_arg, args): datetime_config = config[CONF_DATETIME] if cg.is_template(datetime_config): - template_ = await cg.templatable(datetime_config, [], cg.ESPTime) + template_ = await cg.templatable(datetime_config, args, cg.ESPTime) cg.add(action_var.set_datetime(template_)) else: datetime_struct = cg.StructInitializer( From b3f03c07c615ae072de8fc1c9f7e6171497a7e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Kry=C5=84ski?= Date: Thu, 29 Aug 2024 02:52:13 +0200 Subject: [PATCH 0276/1052] esp32_can: suppress compiler warning (#7372) --- esphome/components/esp32_can/esp32_can.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/esp32_can/esp32_can.cpp b/esphome/components/esp32_can/esp32_can.cpp index 79e4b70f97..214b72e864 100644 --- a/esphome/components/esp32_can/esp32_can.cpp +++ b/esphome/components/esp32_can/esp32_can.cpp @@ -111,6 +111,7 @@ canbus::Error ESP32Can::send_message(struct canbus::CanFrame *frame) { .flags = flags, .identifier = frame->can_id, .data_length_code = frame->can_data_length_code, + .data = {}, // to suppress warning, data is initialized properly below }; if (!frame->remote_transmission_request) { memcpy(message.data, frame->data, frame->can_data_length_code); From 0375072bdfe57d5f8eb50b622e9ad9d6d298fd4a Mon Sep 17 00:00:00 2001 From: Aiden <37043404+tarontop@users.noreply.github.com> Date: Thu, 29 Aug 2024 08:52:49 +0800 Subject: [PATCH 0277/1052] Add support for BL0906 energy meter (#7339) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/bl0906/__init__.py | 1 + esphome/components/bl0906/bl0906.cpp | 238 ++++++++++++++++++ esphome/components/bl0906/bl0906.h | 96 +++++++ esphome/components/bl0906/const.py | 4 + esphome/components/bl0906/constants.h | 122 +++++++++ esphome/components/bl0906/sensor.py | 184 ++++++++++++++ tests/components/bl0906/common.yaml | 62 +++++ tests/components/bl0906/test.esp32-ard.yaml | 5 + .../components/bl0906/test.esp32-c3-ard.yaml | 5 + .../components/bl0906/test.esp32-c3-idf.yaml | 5 + tests/components/bl0906/test.esp32-idf.yaml | 5 + tests/components/bl0906/test.esp8266-ard.yaml | 5 + tests/components/bl0906/test.rp2040-ard.yaml | 5 + 14 files changed, 738 insertions(+) create mode 100644 esphome/components/bl0906/__init__.py create mode 100644 esphome/components/bl0906/bl0906.cpp create mode 100644 esphome/components/bl0906/bl0906.h create mode 100644 esphome/components/bl0906/const.py create mode 100644 esphome/components/bl0906/constants.h create mode 100644 esphome/components/bl0906/sensor.py create mode 100644 tests/components/bl0906/common.yaml create mode 100644 tests/components/bl0906/test.esp32-ard.yaml create mode 100644 tests/components/bl0906/test.esp32-c3-ard.yaml create mode 100644 tests/components/bl0906/test.esp32-c3-idf.yaml create mode 100644 tests/components/bl0906/test.esp32-idf.yaml create mode 100644 tests/components/bl0906/test.esp8266-ard.yaml create mode 100644 tests/components/bl0906/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 9159f5f843..40511e2f41 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -58,6 +58,7 @@ esphome/components/beken_spi_led_strip/* @Mat931 esphome/components/bh1750/* @OttoWinter esphome/components/binary_sensor/* @esphome/core esphome/components/bk72xx/* @kuba2k2 +esphome/components/bl0906/* @athom-tech @jesserockz @tarontop esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @tobias- esphome/components/bl0942/* @dbuezas diff --git a/esphome/components/bl0906/__init__.py b/esphome/components/bl0906/__init__.py new file mode 100644 index 0000000000..b077792604 --- /dev/null +++ b/esphome/components/bl0906/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@athom-tech", "@tarontop", "@jesserockz"] diff --git a/esphome/components/bl0906/bl0906.cpp b/esphome/components/bl0906/bl0906.cpp new file mode 100644 index 0000000000..bddb62ff64 --- /dev/null +++ b/esphome/components/bl0906/bl0906.cpp @@ -0,0 +1,238 @@ +#include "bl0906.h" +#include "constants.h" + +#include "esphome/core/log.h" + +namespace esphome { +namespace bl0906 { + +static const char *const TAG = "bl0906"; + +constexpr uint32_t to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; } + +constexpr int32_t to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; } + +// The SUM byte is (Addr+Data_L+Data_M+Data_H)&0xFF negated; +constexpr uint8_t bl0906_checksum(const uint8_t address, const DataPacket *data) { + return (address + data->l + data->m + data->h) ^ 0xFF; +} + +void BL0906::loop() { + if (this->current_channel_ == UINT8_MAX) { + return; + } + + while (this->available()) + this->flush(); + + if (this->current_channel_ == 0) { + // Temperature + this->read_data_(BL0906_TEMPERATURE, BL0906_TREF, this->temperature_sensor_); + } else if (this->current_channel_ == 1) { + this->read_data_(BL0906_I_1_RMS, BL0906_IREF, this->current_1_sensor_); + this->read_data_(BL0906_WATT_1, BL0906_PREF, this->power_1_sensor_); + this->read_data_(BL0906_CF_1_CNT, BL0906_EREF, this->energy_1_sensor_); + } else if (this->current_channel_ == 2) { + this->read_data_(BL0906_I_2_RMS, BL0906_IREF, this->current_2_sensor_); + this->read_data_(BL0906_WATT_2, BL0906_PREF, this->power_2_sensor_); + this->read_data_(BL0906_CF_2_CNT, BL0906_EREF, this->energy_2_sensor_); + } else if (this->current_channel_ == 3) { + this->read_data_(BL0906_I_3_RMS, BL0906_IREF, this->current_3_sensor_); + this->read_data_(BL0906_WATT_3, BL0906_PREF, this->power_3_sensor_); + this->read_data_(BL0906_CF_3_CNT, BL0906_EREF, this->energy_3_sensor_); + } else if (this->current_channel_ == 4) { + this->read_data_(BL0906_I_4_RMS, BL0906_IREF, this->current_4_sensor_); + this->read_data_(BL0906_WATT_4, BL0906_PREF, this->power_4_sensor_); + this->read_data_(BL0906_CF_4_CNT, BL0906_EREF, this->energy_4_sensor_); + } else if (this->current_channel_ == 5) { + this->read_data_(BL0906_I_5_RMS, BL0906_IREF, this->current_5_sensor_); + this->read_data_(BL0906_WATT_5, BL0906_PREF, this->power_5_sensor_); + this->read_data_(BL0906_CF_5_CNT, BL0906_EREF, this->energy_5_sensor_); + } else if (this->current_channel_ == 6) { + this->read_data_(BL0906_I_6_RMS, BL0906_IREF, this->current_6_sensor_); + this->read_data_(BL0906_WATT_6, BL0906_PREF, this->power_6_sensor_); + this->read_data_(BL0906_CF_6_CNT, BL0906_EREF, this->energy_6_sensor_); + } else if (this->current_channel_ == UINT8_MAX - 2) { + // Frequency + this->read_data_(BL0906_FREQUENCY, BL0906_FREF, frequency_sensor_); + // Voltage + this->read_data_(BL0906_V_RMS, BL0906_UREF, voltage_sensor_); + } else if (this->current_channel_ == UINT8_MAX - 1) { + // Total power + this->read_data_(BL0906_WATT_SUM, BL0906_WATT, this->total_power_sensor_); + // Total Energy + this->read_data_(BL0906_CF_SUM_CNT, BL0906_CF, this->total_energy_sensor_); + } else { + this->current_channel_ = UINT8_MAX - 2; // Go to frequency and voltage + return; + } + this->current_channel_++; + this->handle_actions_(); +} + +void BL0906::setup() { + while (this->available()) + this->flush(); + this->write_array(USR_WRPROT_WITABLE, sizeof(USR_WRPROT_WITABLE)); + // Calibration (1: register address; 2: value before calibration; 3: value after calibration) + this->bias_correction_(BL0906_RMSOS_1, 0.01600, 0); // Calibration current_1 + this->bias_correction_(BL0906_RMSOS_2, 0.01500, 0); + this->bias_correction_(BL0906_RMSOS_3, 0.01400, 0); + this->bias_correction_(BL0906_RMSOS_4, 0.01300, 0); + this->bias_correction_(BL0906_RMSOS_5, 0.01200, 0); + this->bias_correction_(BL0906_RMSOS_6, 0.01200, 0); // Calibration current_6 + + this->write_array(USR_WRPROT_ONLYREAD, sizeof(USR_WRPROT_ONLYREAD)); +} + +void BL0906::update() { this->current_channel_ = 0; } + +size_t BL0906::enqueue_action_(ActionCallbackFuncPtr function) { + this->action_queue_.push_back(function); + return this->action_queue_.size(); +} + +void BL0906::handle_actions_() { + if (this->action_queue_.empty()) { + return; + } + ActionCallbackFuncPtr ptr_func = nullptr; + 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); + (this->*ptr_func)(); + } + } + + while (this->available()) { + this->read(); + } + + this->action_queue_.clear(); +} + +// Reset energy +void BL0906::reset_energy_() { + this->write_array(BL0906_INIT[0], 6); + delay(1); + this->flush(); + + ESP_LOGW(TAG, "RMSOS:%02X%02X%02X%02X%02X%02X", BL0906_INIT[0][0], BL0906_INIT[0][1], BL0906_INIT[0][2], + BL0906_INIT[0][3], BL0906_INIT[0][4], BL0906_INIT[0][5]); +} + +// Read data +void BL0906::read_data_(const uint8_t address, const float reference, sensor::Sensor *sensor) { + if (sensor == nullptr) { + return; + } + DataPacket buffer; + ube24_t data_u24; + sbe24_t data_s24; + float value = 0; + + bool signed_result = reference == BL0906_TREF || reference == BL0906_WATT || reference == BL0906_PREF; + + this->write_byte(BL0906_READ_COMMAND); + this->write_byte(address); + if (this->read_array((uint8_t *) &buffer, sizeof(buffer) - 1)) { + if (bl0906_checksum(address, &buffer) == buffer.checksum) { + if (signed_result) { + data_s24.l = buffer.l; + data_s24.m = buffer.m; + data_s24.h = buffer.h; + } else { + data_u24.l = buffer.l; + data_u24.m = buffer.m; + data_u24.h = buffer.h; + } + } else { + ESP_LOGW(TAG, "Junk on wire. Throwing away partial message"); + while (read() >= 0) + ; + return; + } + } + // Power + if (reference == BL0906_PREF) { + value = (float) to_int32_t(data_s24) * reference; + } + + // Total power + if (reference == BL0906_WATT) { + value = (float) to_int32_t(data_s24) * reference; + } + + // Voltage, current, power, total power + if (reference == BL0906_UREF || reference == BL0906_IREF || reference == BL0906_EREF || reference == BL0906_CF) { + value = (float) to_uint32_t(data_u24) * reference; + } + + // Frequency + if (reference == BL0906_FREF) { + value = reference / (float) to_uint32_t(data_u24); + } + // Chip temperature + if (reference == BL0906_TREF) { + value = (float) to_int32_t(data_s24); + value = (value - 64) * 12.5 / 59 - 40; + } + sensor->publish_state(value); +} + +// RMS offset correction +void BL0906::bias_correction_(uint8_t address, float measurements, float correction) { + DataPacket data; + float ki = 12875 * 1 * (5.1 + 5.1) * 1000 / 2000 / 1.097; // Current coefficient + float i_rms0 = measurements * ki; + float i_rms = correction * ki; + int32_t value = (i_rms * i_rms - i_rms0 * i_rms0) / 256; + data.l = value << 24 >> 24; + data.m = value << 16 >> 24; + if (value < 0) { + data.h = (value << 8 >> 24) | 0b10000000; + } + data.address = bl0906_checksum(address, &data); + ESP_LOGV(TAG, "RMSOS:%02X%02X%02X%02X%02X%02X", BL0906_WRITE_COMMAND, address, data.l, data.m, data.h, data.address); + this->write_byte(BL0906_WRITE_COMMAND); + this->write_byte(address); + this->write_byte(data.l); + this->write_byte(data.m); + this->write_byte(data.h); + this->write_byte(data.address); +} + +void BL0906::dump_config() { + ESP_LOGCONFIG(TAG, "BL0906:"); + LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); + + LOG_SENSOR(" ", "Current1", this->current_1_sensor_); + LOG_SENSOR(" ", "Current2", this->current_2_sensor_); + LOG_SENSOR(" ", "Current3", this->current_3_sensor_); + LOG_SENSOR(" ", "Current4", this->current_4_sensor_); + LOG_SENSOR(" ", "Current5", this->current_5_sensor_); + LOG_SENSOR(" ", "Current6", this->current_6_sensor_); + + LOG_SENSOR(" ", "Power1", this->power_1_sensor_); + LOG_SENSOR(" ", "Power2", this->power_2_sensor_); + LOG_SENSOR(" ", "Power3", this->power_3_sensor_); + LOG_SENSOR(" ", "Power4", this->power_4_sensor_); + LOG_SENSOR(" ", "Power5", this->power_5_sensor_); + LOG_SENSOR(" ", "Power6", this->power_6_sensor_); + + LOG_SENSOR(" ", "Energy1", this->energy_1_sensor_); + LOG_SENSOR(" ", "Energy2", this->energy_2_sensor_); + LOG_SENSOR(" ", "Energy3", this->energy_3_sensor_); + LOG_SENSOR(" ", "Energy4", this->energy_4_sensor_); + LOG_SENSOR(" ", "Energy5", this->energy_5_sensor_); + LOG_SENSOR(" ", "Energy6", this->energy_6_sensor_); + + LOG_SENSOR(" ", "Total Power", this->total_power_sensor_); + LOG_SENSOR(" ", "Total Energy", this->total_energy_sensor_); + LOG_SENSOR(" ", "Frequency", this->frequency_sensor_); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); +} + +} // namespace bl0906 +} // namespace esphome diff --git a/esphome/components/bl0906/bl0906.h b/esphome/components/bl0906/bl0906.h new file mode 100644 index 0000000000..5a9ad0f028 --- /dev/null +++ b/esphome/components/bl0906/bl0906.h @@ -0,0 +1,96 @@ +#pragma once + +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/uart/uart.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/datatypes.h" + +// https://www.belling.com.cn/media/file_object/bel_product/BL0906/datasheet/BL0906_V1.02_cn.pdf +// https://www.belling.com.cn/media/file_object/bel_product/BL0906/guide/BL0906%20APP%20Note_V1.02.pdf + +namespace esphome { +namespace bl0906 { + +struct DataPacket { // NOLINT(altera-struct-pack-align) + uint8_t l{0}; + uint8_t m{0}; + uint8_t h{0}; + uint8_t checksum; // checksum + uint8_t address; +} __attribute__((packed)); + +struct ube24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align) + uint8_t l{0}; + uint8_t m{0}; + uint8_t h{0}; +} __attribute__((packed)); + +struct sbe24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align) + uint8_t l{0}; + uint8_t m{0}; + int8_t h{0}; +} __attribute__((packed)); + +template class ResetEnergyAction; + +class BL0906; + +using ActionCallbackFuncPtr = void (BL0906::*)(); + +class BL0906 : public PollingComponent, public uart::UARTDevice { + SUB_SENSOR(voltage) + SUB_SENSOR(current_1) + SUB_SENSOR(current_2) + SUB_SENSOR(current_3) + SUB_SENSOR(current_4) + SUB_SENSOR(current_5) + SUB_SENSOR(current_6) + SUB_SENSOR(power_1) + SUB_SENSOR(power_2) + SUB_SENSOR(power_3) + SUB_SENSOR(power_4) + SUB_SENSOR(power_5) + SUB_SENSOR(power_6) + SUB_SENSOR(total_power) + SUB_SENSOR(energy_1) + SUB_SENSOR(energy_2) + SUB_SENSOR(energy_3) + SUB_SENSOR(energy_4) + SUB_SENSOR(energy_5) + SUB_SENSOR(energy_6) + SUB_SENSOR(total_energy) + SUB_SENSOR(frequency) + SUB_SENSOR(temperature) + + public: + void loop() override; + + void update() override; + void setup() override; + void dump_config() override; + + protected: + template friend class ResetEnergyAction; + + void reset_energy_(); + + void read_data_(uint8_t address, float reference, sensor::Sensor *sensor); + + void bias_correction_(uint8_t address, float measurements, float correction); + + uint8_t current_channel_{0}; + size_t enqueue_action_(ActionCallbackFuncPtr function); + void handle_actions_(); + + private: + std::vector action_queue_{}; +}; + +template class ResetEnergyAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->enqueue_action_(&BL0906::reset_energy_); } +}; + +} // namespace bl0906 +} // namespace esphome diff --git a/esphome/components/bl0906/const.py b/esphome/components/bl0906/const.py new file mode 100644 index 0000000000..67f21d35b0 --- /dev/null +++ b/esphome/components/bl0906/const.py @@ -0,0 +1,4 @@ +# const.py +ICON_ENERGY = "mdi:lightning-bolt" +ICON_FREQUENCY = "mdi:cosine-wave" +ICON_VOLTAGE = "mdi:sine-wave" diff --git a/esphome/components/bl0906/constants.h b/esphome/components/bl0906/constants.h new file mode 100644 index 0000000000..546916aa3c --- /dev/null +++ b/esphome/components/bl0906/constants.h @@ -0,0 +1,122 @@ +#pragma once +#include + +namespace esphome { +namespace bl0906 { + +// Total power conversion +static const float BL0906_WATT = 16 * 1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000) / + (40.41259 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000); +// Total Energy conversion +static const float BL0906_CF = 16 * 4194304 * 0.032768 * 16 / + (3600000 * 16 * + (40.4125 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000 / + (1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000)))); +// Frequency conversion +static const float BL0906_FREF = 10000000; +// Temperature conversion +static const float BL0906_TREF = 12.5 / 59 - 40; +// Current conversion +static const float BL0906_IREF = 1.097 / (12875 * 1 * (5.1 + 5.1) * 1000 / 2000); +// Voltage conversion +static const float BL0906_UREF = 1.097 * (20000 + 20000 + 20000 + 20000 + 20000) / (13162 * 1 * 100 * 1000); +// Power conversion +static const float BL0906_PREF = 1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000) / + (40.41259 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000); +// Energy conversion +static const float BL0906_EREF = 4194304 * 0.032768 * 16 / + (3600000 * 16 * + (40.4125 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000 / + (1.097 * 1.097 * (20000 + 20000 + 20000 + 20000 + 20000)))); +// Current coefficient +static const float BL0906_KI = 12875 * 1 * (5.1 + 5.1) * 1000 / 2000 / 1.097; +// Power coefficient +static const float BL0906_KP = 40.4125 * ((5.1 + 5.1) * 1000 / 2000) * 1 * 100 * 1 * 1000 / 1.097 / 1.097 / + (20000 + 20000 + 20000 + 20000 + 20000); + +static const uint8_t USR_WRPROT_WITABLE[6] = {0xCA, 0x9E, 0x55, 0x55, 0x00, 0xB7}; +static const uint8_t USR_WRPROT_ONLYREAD[6] = {0xCA, 0x9E, 0x00, 0x00, 0x00, 0x61}; + +static const uint8_t BL0906_READ_COMMAND = 0x35; +static const uint8_t BL0906_WRITE_COMMAND = 0xCA; + +// Register address +// Voltage +static const uint8_t BL0906_V_RMS = 0x16; + +// Total power +static const uint8_t BL0906_WATT_SUM = 0X2C; + +// Current1~6 +static const uint8_t BL0906_I_1_RMS = 0x0D; // current_1 +static const uint8_t BL0906_I_2_RMS = 0x0E; +static const uint8_t BL0906_I_3_RMS = 0x0F; +static const uint8_t BL0906_I_4_RMS = 0x10; +static const uint8_t BL0906_I_5_RMS = 0x13; +static const uint8_t BL0906_I_6_RMS = 0x14; // current_6 + +// Power1~6 +static const uint8_t BL0906_WATT_1 = 0X23; // power_1 +static const uint8_t BL0906_WATT_2 = 0X24; +static const uint8_t BL0906_WATT_3 = 0X25; +static const uint8_t BL0906_WATT_4 = 0X26; +static const uint8_t BL0906_WATT_5 = 0X29; +static const uint8_t BL0906_WATT_6 = 0X2A; // power_6 + +// Active pulse count, unsigned +static const uint8_t BL0906_CF_1_CNT = 0X30; // Channel_1 +static const uint8_t BL0906_CF_2_CNT = 0X31; +static const uint8_t BL0906_CF_3_CNT = 0X32; +static const uint8_t BL0906_CF_4_CNT = 0X33; +static const uint8_t BL0906_CF_5_CNT = 0X36; +static const uint8_t BL0906_CF_6_CNT = 0X37; // Channel_6 + +// Total active pulse count, unsigned +static const uint8_t BL0906_CF_SUM_CNT = 0X39; + +// Voltage frequency cycle +static const uint8_t BL0906_FREQUENCY = 0X4E; + +// Internal temperature +static const uint8_t BL0906_TEMPERATURE = 0X5E; + +// Calibration register +// RMS gain adjustment register +static const uint8_t BL0906_RMSGN_1 = 0x6D; // Channel_1 +static const uint8_t BL0906_RMSGN_2 = 0x6E; +static const uint8_t BL0906_RMSGN_3 = 0x6F; +static const uint8_t BL0906_RMSGN_4 = 0x70; +static const uint8_t BL0906_RMSGN_5 = 0x73; +static const uint8_t BL0906_RMSGN_6 = 0x74; // Channel_6 + +// RMS offset correction register +static const uint8_t BL0906_RMSOS_1 = 0x78; // Channel_1 +static const uint8_t BL0906_RMSOS_2 = 0x79; +static const uint8_t BL0906_RMSOS_3 = 0x7A; +static const uint8_t BL0906_RMSOS_4 = 0x7B; +static const uint8_t BL0906_RMSOS_5 = 0x7E; +static const uint8_t BL0906_RMSOS_6 = 0x7F; // Channel_6 + +// Active power gain adjustment register +static const uint8_t BL0906_WATTGN_1 = 0xB7; // Channel_1 +static const uint8_t BL0906_WATTGN_2 = 0xB8; +static const uint8_t BL0906_WATTGN_3 = 0xB9; +static const uint8_t BL0906_WATTGN_4 = 0xBA; +static const uint8_t BL0906_WATTGN_5 = 0xBD; +static const uint8_t BL0906_WATTGN_6 = 0xBE; // Channel_6 + +// User write protection setting register, +// You must first write 0x5555 to the write protection setting register before writing to other registers. +static const uint8_t BL0906_USR_WRPROT = 0x9E; + +// Reset Register +static const uint8_t BL0906_SOFT_RESET = 0x9F; + +const uint8_t BL0906_INIT[2][6] = { + // Reset to default + {BL0906_WRITE_COMMAND, BL0906_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x52}, + // Enable User Operation Write + {BL0906_WRITE_COMMAND, BL0906_USR_WRPROT, 0x55, 0x55, 0x00, 0xB7}}; + +} // namespace bl0906 +} // namespace esphome diff --git a/esphome/components/bl0906/sensor.py b/esphome/components/bl0906/sensor.py new file mode 100644 index 0000000000..bc370c9252 --- /dev/null +++ b/esphome/components/bl0906/sensor.py @@ -0,0 +1,184 @@ +from esphome import automation +from esphome.automation import maybe_simple_id +import esphome.codegen as cg +from esphome.components import sensor, uart +import esphome.config_validation as cv +from esphome.const import ( + CONF_CHANNEL, + CONF_CURRENT, + CONF_ENERGY, + CONF_FREQUENCY, + CONF_ID, + CONF_NAME, + CONF_POWER, + CONF_TEMPERATURE, + CONF_TOTAL_POWER, + CONF_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_FREQUENCY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + ICON_CURRENT_AC, + ICON_POWER, + ICON_THERMOMETER, + STATE_CLASS_MEASUREMENT, + STATE_CLASS_TOTAL_INCREASING, + UNIT_AMPERE, + UNIT_CELSIUS, + UNIT_HERTZ, + UNIT_KILOWATT_HOURS, + UNIT_VOLT, + UNIT_WATT, +) + +# Import ICONS not included in esphome's const.py, from the local components const.py +from .const import ICON_ENERGY, ICON_FREQUENCY, ICON_VOLTAGE + +DEPENDENCIES = ["uart"] +AUTO_LOAD = ["bl0906"] +CONF_TOTAL_ENERGY = "total_energy" + +bl0906_ns = cg.esphome_ns.namespace("bl0906") +BL0906 = bl0906_ns.class_("BL0906", cg.PollingComponent, uart.UARTDevice) +ResetEnergyAction = bl0906_ns.class_("ResetEnergyAction", automation.Action) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BL0906), + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( + icon=ICON_FREQUENCY, + accuracy_decimals=0, + device_class=DEVICE_CLASS_FREQUENCY, + unit_of_measurement=UNIT_HERTZ, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + icon=ICON_THERMOMETER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_TEMPERATURE, + unit_of_measurement=UNIT_CELSIUS, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema( + icon=ICON_VOLTAGE, + accuracy_decimals=1, + device_class=DEVICE_CLASS_VOLTAGE, + unit_of_measurement=UNIT_VOLT, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TOTAL_POWER): sensor.sensor_schema( + icon=ICON_POWER, + accuracy_decimals=3, + device_class=DEVICE_CLASS_POWER, + unit_of_measurement=UNIT_WATT, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_TOTAL_ENERGY): sensor.sensor_schema( + icon=ICON_ENERGY, + accuracy_decimals=3, + device_class=DEVICE_CLASS_ENERGY, + state_class=STATE_CLASS_TOTAL_INCREASING, + unit_of_measurement=UNIT_KILOWATT_HOURS, + ), + } + ) + .extend( + cv.Schema( + { + cv.Optional(f"{CONF_CHANNEL}_{i + 1}"): cv.Schema( + { + cv.Optional(CONF_CURRENT): cv.maybe_simple_value( + sensor.sensor_schema( + icon=ICON_CURRENT_AC, + accuracy_decimals=3, + device_class=DEVICE_CLASS_CURRENT, + unit_of_measurement=UNIT_AMPERE, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + cv.Optional(CONF_POWER): cv.maybe_simple_value( + sensor.sensor_schema( + icon=ICON_POWER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_POWER, + unit_of_measurement=UNIT_WATT, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + cv.Optional(CONF_ENERGY): cv.maybe_simple_value( + sensor.sensor_schema( + icon=ICON_ENERGY, + accuracy_decimals=3, + device_class=DEVICE_CLASS_ENERGY, + unit_of_measurement=UNIT_KILOWATT_HOURS, + state_class=STATE_CLASS_TOTAL_INCREASING, + ), + key=CONF_NAME, + ), + } + ) + for i in range(6) + } + ) + ) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.polling_component_schema("60s")) +) + +FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( + "bl0906", baud_rate=19200, require_tx=True, require_rx=True +) + + +@automation.register_action( + "bl0906.reset_energy", + ResetEnergyAction, + maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(BL0906), + } + ), +) +async def reset_energy_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + if frequency_config := config.get(CONF_FREQUENCY): + sens = await sensor.new_sensor(frequency_config) + cg.add(var.set_frequency_sensor(sens)) + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature_sensor(sens)) + if voltage_config := config.get(CONF_VOLTAGE): + sens = await sensor.new_sensor(voltage_config) + cg.add(var.set_voltage_sensor(sens)) + + for i in range(6): + if channel_config := config.get(f"{CONF_CHANNEL}_{i + 1}"): + if current_config := channel_config.get(CONF_CURRENT): + sens = await sensor.new_sensor(current_config) + cg.add(getattr(var, f"set_current_{i + 1}_sensor")(sens)) + if power_config := channel_config.get(CONF_POWER): + sens = await sensor.new_sensor(power_config) + cg.add(getattr(var, f"set_power_{i + 1}_sensor")(sens)) + if energy_config := channel_config.get(CONF_ENERGY): + sens = await sensor.new_sensor(energy_config) + cg.add(getattr(var, f"set_energy_{i + 1}_sensor")(sens)) + + if total_power_config := config.get(CONF_TOTAL_POWER): + sens = await sensor.new_sensor(total_power_config) + cg.add(var.set_total_power_sensor(sens)) + + if total_energy_config := config.get(CONF_TOTAL_ENERGY): + sens = await sensor.new_sensor(total_energy_config) + cg.add(var.set_total_energy_sensor(sens)) diff --git a/tests/components/bl0906/common.yaml b/tests/components/bl0906/common.yaml new file mode 100644 index 0000000000..944791369c --- /dev/null +++ b/tests/components/bl0906/common.yaml @@ -0,0 +1,62 @@ +uart: + - id: uart_bl0906 + tx_pin: + number: ${tx_pin} + rx_pin: + number: ${rx_pin} + baud_rate: 19200 + +sensor: + - platform: bl0906 + frequency: + name: 'Frequency' + temperature: + name: 'Temperature' + voltage: + name: 'Voltage' + channel_1: + current: + name: 'Current_1' + power: + name: 'Power_1' + energy: + name: 'Energy_1' + channel_2: + current: + name: 'Current_2' + power: + name: 'Power_2' + energy: + name: 'Energy_2' + channel_3: + current: + name: 'Current_3' + power: + name: 'Power_3' + energy: + name: 'Energy_3' + channel_4: + current: + name: 'Current_4' + power: + name: 'Power_4' + energy: + name: 'Energy_4' + channel_5: + current: + name: 'Current_5' + power: + name: 'Power_5' + energy: + name: 'Energy_5' + channel_6: + current: + name: 'Current_6' + power: + name: 'Power_6' + energy: + name: 'Energy_6' + total_energy: + name: 'Total_Energy' + total_power: + name: 'Total_Power' diff --git a/tests/components/bl0906/test.esp32-ard.yaml b/tests/components/bl0906/test.esp32-ard.yaml new file mode 100644 index 0000000000..811f6b72a6 --- /dev/null +++ b/tests/components/bl0906/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/bl0906/test.esp32-c3-ard.yaml b/tests/components/bl0906/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..c79d14c740 --- /dev/null +++ b/tests/components/bl0906/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 + +<<: !include common.yaml diff --git a/tests/components/bl0906/test.esp32-c3-idf.yaml b/tests/components/bl0906/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..c79d14c740 --- /dev/null +++ b/tests/components/bl0906/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 + +<<: !include common.yaml diff --git a/tests/components/bl0906/test.esp32-idf.yaml b/tests/components/bl0906/test.esp32-idf.yaml new file mode 100644 index 0000000000..811f6b72a6 --- /dev/null +++ b/tests/components/bl0906/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/bl0906/test.esp8266-ard.yaml b/tests/components/bl0906/test.esp8266-ard.yaml new file mode 100644 index 0000000000..3b44f9c9c3 --- /dev/null +++ b/tests/components/bl0906/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/bl0906/test.rp2040-ard.yaml b/tests/components/bl0906/test.rp2040-ard.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/bl0906/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml From 1922f2bbee79ba1c4830ec286ef7032cd8b7d613 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 29 Aug 2024 10:55:37 +1000 Subject: [PATCH 0278/1052] [platformio] Add environments for ESP-IDF 5.3 for development (#7371) --- platformio.ini | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/platformio.ini b/platformio.ini index 4a0a3f2ef4..147159a841 100644 --- a/platformio.ini +++ b/platformio.ini @@ -153,6 +153,13 @@ build_flags = -DUSE_ESP32_FRAMEWORK_ESP_IDF extra_scripts = post:esphome/components/esp32/post_build.py.script +; This are common settings for the ESP32 using the latest ESP-IDF version. +[common:esp32-idf-5_3] +extends = common:esp32-idf +platform = platformio/espressif32@6.8.0 +platform_packages = + platformio/framework-espidf@~3.50300.0 + ; These are common settings for the RP2040 using Arduino. [common:rp2040-arduino] extends = common:arduino @@ -229,6 +236,15 @@ build_flags = ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32 +[env:esp32-idf-5_3] +extends = common:esp32-idf-5_3 +board = esp32dev +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32-idf +build_flags = + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + -DUSE_ESP32_VARIANT_ESP32 + [env:esp32-idf-tidy] extends = common:esp32-idf board = esp32dev @@ -265,6 +281,15 @@ build_flags = ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32C3 +[env:esp32c3-idf-5_3] +extends = common:esp32-idf-5_3 +board = esp32-c3-devkitm-1 +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32c3-idf +build_flags = + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + -DUSE_ESP32_VARIANT_ESP32C3 + [env:esp32c3-idf-tidy] extends = common:esp32-idf board = esp32-c3-devkitm-1 @@ -301,6 +326,15 @@ build_flags = ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S2 +[env:esp32s2-idf-5_3] +extends = common:esp32-idf-5_3 +board = esp32-s2-kaluga-1 +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32s2-idf +build_flags = + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + -DUSE_ESP32_VARIANT_ESP32S2 + [env:esp32s2-idf-tidy] extends = common:esp32-idf board = esp32-s2-kaluga-1 @@ -337,6 +371,15 @@ build_flags = ${flags:runtime.build_flags} -DUSE_ESP32_VARIANT_ESP32S3 +[env:esp32s3-idf-5_3] +extends = common:esp32-idf-5_3 +board = esp32-s3-devkitc-1 +board_build.esp-idf.sdkconfig_path = .temp/sdkconfig-esp32s3-idf +build_flags = + ${common:esp32-idf.build_flags} + ${flags:runtime.build_flags} + -DUSE_ESP32_VARIANT_ESP32S3 + [env:esp32s3-idf-tidy] extends = common:esp32-idf board = esp32-s3-devkitc-1 From f28418d0b472e9c641cb4ebcda206f65916fa3bc Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:34:41 +1000 Subject: [PATCH 0279/1052] [lvgl] Bug fixes (#7370) --- esphome/components/lvgl/__init__.py | 2 ++ esphome/components/lvgl/lv_validation.py | 15 +++++---------- esphome/components/lvgl/schemas.py | 8 +++++++- .../components/lvgl/widgets/buttonmatrix.py | 6 +++++- esphome/components/lvgl/widgets/checkbox.py | 11 ++++++++--- esphome/components/lvgl/widgets/tileview.py | 10 ++-------- tests/components/lvgl/lvgl-package.yaml | 18 ++++++++++++++++-- 7 files changed, 45 insertions(+), 25 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index cab6462d1a..a4ca9d56f3 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -327,6 +327,8 @@ CONFIG_SCHEMA = ( { cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments, + cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, + cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, } ) ), diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index a2be4a2abe..d8af9f7aa9 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -52,9 +52,7 @@ opacity = LValidator(opacity_validator, uint32, retmapper=literal) def color(value): if value == SCHEMA_EXTRACT: return ["hex color value", "color ID"] - if isinstance(value, int): - return value - return cv.use_id(ColorStruct)(value) + return cv.Any(cv.int_, cv.use_id(ColorStruct))(value) def color_retmapper(value): @@ -82,10 +80,10 @@ def pixels_or_percent_validator(value): """A length in one axis - either a number (pixels) or a percentage""" if value == SCHEMA_EXTRACT: return ["pixels", "..%"] + value = cv.Any(cv.int_, cv.percentage)(value) if isinstance(value, int): - return cv.int_(value) - # Will throw an exception if not a percentage. - return f"lv_pct({int(cv.percentage(value) * 100)})" + return value + return f"lv_pct({int(value * 100)})" pixels_or_percent = LValidator(pixels_or_percent_validator, uint32, retmapper=literal) @@ -116,10 +114,7 @@ def size_validator(value): if value.upper() == "SIZE_CONTENT": return "LV_SIZE_CONTENT" raise cv.Invalid("must be 'size_content', a percentage or an integer (pixels)") - if isinstance(value, int): - return cv.int_(value) - # Will throw an exception if not a percentage. - return f"lv_pct({int(cv.percentage(value) * 100)})" + return pixels_or_percent_validator(value) size = LValidator(size_validator, uint32, retmapper=literal) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 548ebda6dc..9ff0fec5bc 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -359,7 +359,13 @@ LVGL_SCHEMA = cv.Schema( } ) -ALL_STYLES = {**STYLE_PROPS, **GRID_CELL_SCHEMA, **FLEX_OBJ_SCHEMA} +ALL_STYLES = { + **STYLE_PROPS, + **GRID_CELL_SCHEMA, + **FLEX_OBJ_SCHEMA, + cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, + cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, +} def container_validator(schema, widget_type: WidgetType): diff --git a/esphome/components/lvgl/widgets/buttonmatrix.py b/esphome/components/lvgl/widgets/buttonmatrix.py index e61c5e3477..c65bb4b354 100644 --- a/esphome/components/lvgl/widgets/buttonmatrix.py +++ b/esphome/components/lvgl/widgets/buttonmatrix.py @@ -13,11 +13,13 @@ from ..defines import ( CONF_KEY_CODE, CONF_MAIN, CONF_ONE_CHECKED, + CONF_PAD_COLUMN, + CONF_PAD_ROW, CONF_ROWS, CONF_SELECTED, ) from ..helpers import lvgl_components_required -from ..lv_validation import key_code, lv_bool +from ..lv_validation import key_code, lv_bool, pixels from ..lvcode import lv, lv_add, lv_expr from ..schemas import automation_schema from ..types import ( @@ -57,6 +59,8 @@ BUTTONMATRIX_BUTTON_SCHEMA = cv.Schema( BUTTONMATRIX_SCHEMA = cv.Schema( { cv.Optional(CONF_ONE_CHECKED, default=False): lv_bool, + cv.Optional(CONF_PAD_ROW): pixels, + cv.Optional(CONF_PAD_COLUMN): pixels, cv.GenerateID(CONF_BUTTON_TEXT_LIST_ID): cv.declare_id(char_ptr), cv.Required(CONF_ROWS): cv.ensure_list( cv.Schema( diff --git a/esphome/components/lvgl/widgets/checkbox.py b/esphome/components/lvgl/widgets/checkbox.py index 79c60a8669..75f4142eb1 100644 --- a/esphome/components/lvgl/widgets/checkbox.py +++ b/esphome/components/lvgl/widgets/checkbox.py @@ -1,7 +1,8 @@ +from esphome.config_validation import Optional from esphome.const import CONF_TEXT -from ..defines import CONF_INDICATOR, CONF_MAIN -from ..lv_validation import lv_text +from ..defines import CONF_INDICATOR, CONF_MAIN, CONF_PAD_COLUMN +from ..lv_validation import lv_text, pixels from ..lvcode import lv from ..schemas import TEXT_SCHEMA from ..types import LvBoolean @@ -16,7 +17,11 @@ class CheckboxType(WidgetType): CONF_CHECKBOX, LvBoolean("lv_checkbox_t"), (CONF_MAIN, CONF_INDICATOR), - TEXT_SCHEMA, + TEXT_SCHEMA.extend( + { + Optional(CONF_PAD_COLUMN): pixels, + } + ), ) async def to_code(self, w: Widget, config): diff --git a/esphome/components/lvgl/widgets/tileview.py b/esphome/components/lvgl/widgets/tileview.py index 9a426c7daf..05259fbd3c 100644 --- a/esphome/components/lvgl/widgets/tileview.py +++ b/esphome/components/lvgl/widgets/tileview.py @@ -1,7 +1,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_ON_VALUE, CONF_ROW, CONF_TRIGGER_ID +from esphome.const import CONF_ID, CONF_ROW from ..automation import action_to_code from ..defines import ( @@ -29,6 +29,7 @@ lv_tileview_t = LvType( "lv_tileview_t", largs=[(lv_obj_t_ptr, "tile")], lvalue=lambda w: w.get_property("tile_act"), + has_on_value=True, ) tile_spec = WidgetType("lv_tileview_tile_t", lv_tile_t, (CONF_MAIN,), {}) @@ -46,13 +47,6 @@ TILEVIEW_SCHEMA = cv.Schema( }, ) ), - cv.Optional(CONF_ON_VALUE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - automation.Trigger.template(lv_obj_t_ptr) - ) - } - ), } ) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 737d8703b0..0feb6d6ce6 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -337,11 +337,25 @@ lvgl: - tileview: id: tileview_id scrollbar_mode: active + on_value: + then: + - if: + condition: + lambda: return tile == id(tile_1); + then: + - logger.log: "tile 1 is now showing" tiles: - - id: page_1 + - id: tile_1 row: 0 column: 0 - dir: HOR + dir: ALL + widgets: + - obj: + bg_color: 0x000000 + - id: tile_2 + row: 1 + column: 0 + dir: [VER, HOR] widgets: - obj: bg_color: 0x000000 From c09df3c05d4012da3b815ddccc5149a714094274 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 30 Aug 2024 06:16:16 +1000 Subject: [PATCH 0280/1052] [bytebuffer] Use existing bit_cast operations. (#7374) --- esphome/core/bytebuffer.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/esphome/core/bytebuffer.cpp b/esphome/core/bytebuffer.cpp index 65525ecfcf..9dd32bf87a 100644 --- a/esphome/core/bytebuffer.cpp +++ b/esphome/core/bytebuffer.cpp @@ -1,6 +1,9 @@ #include "bytebuffer.h" #include -#include +#include "esphome/core/helpers.h" + +#include +#include namespace esphome { @@ -110,18 +113,13 @@ uint32_t ByteBuffer::get_int24() { } float ByteBuffer::get_float() { assert(this->get_remaining() >= sizeof(float)); - auto ui_value = this->get_uint32(); - float value; - memcpy(&value, &ui_value, sizeof(float)); - return value; + return bit_cast(this->get_uint32()); } double ByteBuffer::get_double() { assert(this->get_remaining() >= sizeof(double)); - auto ui_value = this->get_uint64(); - double value; - memcpy(&value, &ui_value, sizeof(double)); - return value; + return bit_cast(this->get_uint64()); } + std::vector ByteBuffer::get_vector(size_t length) { assert(this->get_remaining() >= length); auto start = this->data_.begin() + this->position_; @@ -154,16 +152,12 @@ void ByteBuffer::put_uint(uint64_t value, size_t length) { void ByteBuffer::put_float(float value) { static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported"); assert(this->get_remaining() >= sizeof(float)); - uint32_t ui_value; - memcpy(&ui_value, &value, sizeof(float)); // this work-around required to silence compiler warnings - this->put_uint32(ui_value); + this->put_uint32(bit_cast(value)); } void ByteBuffer::put_double(double value) { static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported"); assert(this->get_remaining() >= sizeof(double)); - uint64_t ui_value; - memcpy(&ui_value, &value, sizeof(double)); - this->put_uint64(ui_value); + this->put_uint64(bit_cast(value)); } void ByteBuffer::put_vector(const std::vector &value) { assert(this->get_remaining() >= value.size()); From bb6693a2552f94b1b375a4e5d195e51e27523751 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 08:17:28 +1200 Subject: [PATCH 0281/1052] Bump actions/setup-python from 5.1.0 to 5.2.0 (#7375) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-api-proto.yml | 2 +- .github/workflows/ci-docker.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/sync-device-classes.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-api-proto.yml b/.github/workflows/ci-api-proto.yml index ee08a0246d..8112c4e0ff 100644 --- a/.github/workflows/ci-api-proto.yml +++ b/.github/workflows/ci-api-proto.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.2.0 with: python-version: "3.11" diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 91c02b0a17..891367d16a 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.2.0 with: python-version: "3.9" - name: Set up Docker Buildx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2437dd5b8d..7c4fa65695 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.2.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d454076c84..937c7aac90 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.2.0 with: python-version: "3.x" - name: Set up python environment @@ -85,7 +85,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.2.0 with: python-version: "3.9" diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 89a3627c64..7677425236 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -22,7 +22,7 @@ jobs: path: lib/home-assistant - name: Setup Python - uses: actions/setup-python@v5.1.0 + uses: actions/setup-python@v5.2.0 with: python-version: 3.12 From acb00c9c59bd79847fdab7602e06486511bd6dc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Aug 2024 08:17:34 +1200 Subject: [PATCH 0282/1052] Bump actions/setup-python from 5.1.1 to 5.2.0 in /.github/actions/restore-python (#7376) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index d3fe2a89dc..c618a5ca97 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -17,7 +17,7 @@ runs: steps: - name: Set up Python ${{ inputs.python-version }} id: python - uses: actions/setup-python@v5.1.1 + uses: actions/setup-python@v5.2.0 with: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment From 725e50348b240c00c2c287d09d9ba8dec138561d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 30 Aug 2024 06:20:12 +1000 Subject: [PATCH 0283/1052] [gt911] Add reset pin config (#7373) --- .../components/gt911/touchscreen/__init__.py | 12 +++++---- .../gt911/touchscreen/gt911_touchscreen.cpp | 17 +++++++++++++ .../gt911/touchscreen/gt911_touchscreen.h | 2 ++ tests/components/gt911/common.yaml | 25 +++++++++++++++++++ tests/components/gt911/test.esp32-ard.yaml | 25 +------------------ tests/components/gt911/test.esp32-c3-ard.yaml | 25 +------------------ tests/components/gt911/test.esp32-c3-idf.yaml | 25 +------------------ tests/components/gt911/test.esp32-idf.yaml | 25 +------------------ tests/components/gt911/test.rp2040-ard.yaml | 25 +------------------ 9 files changed, 56 insertions(+), 125 deletions(-) create mode 100644 tests/components/gt911/common.yaml diff --git a/esphome/components/gt911/touchscreen/__init__.py b/esphome/components/gt911/touchscreen/__init__.py index 9a0d5cc169..6c80ff280f 100644 --- a/esphome/components/gt911/touchscreen/__init__.py +++ b/esphome/components/gt911/touchscreen/__init__.py @@ -1,11 +1,10 @@ -import esphome.codegen as cg -import esphome.config_validation as cv - from esphome import pins +import esphome.codegen as cg from esphome.components import i2c, touchscreen -from esphome.const import CONF_INTERRUPT_PIN, CONF_ID -from .. import gt911_ns +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN +from .. import gt911_ns GT911ButtonListener = gt911_ns.class_("GT911ButtonListener") GT911Touchscreen = gt911_ns.class_( @@ -18,6 +17,7 @@ CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(GT911Touchscreen), cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, } ).extend(i2c.i2c_device_schema(0x5D)) @@ -29,3 +29,5 @@ async def to_code(config): if interrupt_pin := config.get(CONF_INTERRUPT_PIN): cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin))) + if reset_pin := config.get(CONF_RESET_PIN): + cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin))) diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp index 99dba66c22..84811b818f 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.cpp @@ -26,6 +26,23 @@ 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..."); + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(false); + if (this->interrupt_pin_ != nullptr) { + // The interrupt pin is used as an input during reset to select the I2C address. + this->interrupt_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->interrupt_pin_->setup(); + this->interrupt_pin_->digital_write(false); + } + delay(2); + this->reset_pin_->digital_write(true); + delay(50); // NOLINT + if (this->interrupt_pin_ != nullptr) { + this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT); + this->interrupt_pin_->setup(); + } + } // check the configuration of the int line. uint8_t data[4]; diff --git a/esphome/components/gt911/touchscreen/gt911_touchscreen.h b/esphome/components/gt911/touchscreen/gt911_touchscreen.h index a9e1279ed3..17636a2ada 100644 --- a/esphome/components/gt911/touchscreen/gt911_touchscreen.h +++ b/esphome/components/gt911/touchscreen/gt911_touchscreen.h @@ -19,12 +19,14 @@ class GT911Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice void dump_config() override; void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } + void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; } void register_button_listener(GT911ButtonListener *listener) { this->button_listeners_.push_back(listener); } protected: void update_touches() override; InternalGPIOPin *interrupt_pin_{}; + GPIOPin *reset_pin_{}; std::vector button_listeners_; uint8_t button_state_{0xFF}; // last button state. Initial FF guarantees first update. }; diff --git a/tests/components/gt911/common.yaml b/tests/components/gt911/common.yaml new file mode 100644 index 0000000000..7bb88108da --- /dev/null +++ b/tests/components/gt911/common.yaml @@ -0,0 +1,25 @@ +i2c: + - id: i2c_gt911 + scl: 5 + sda: 4 + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: 10 + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: gt911 + display: ssd1306_display + interrupt_pin: 20 + reset_pin: 21 + +binary_sensor: + - platform: gt911 + id: touch_key_911 + index: 0 diff --git a/tests/components/gt911/test.esp32-ard.yaml b/tests/components/gt911/test.esp32-ard.yaml index a47f7bf260..dade44d145 100644 --- a/tests/components/gt911/test.esp32-ard.yaml +++ b/tests/components/gt911/test.esp32-ard.yaml @@ -1,24 +1 @@ -i2c: - - id: i2c_gt911 - scl: 16 - sda: 17 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: gt911 - display: ssd1306_display - interrupt_pin: 14 - -binary_sensor: - - platform: gt911 - id: touch_key_911 - index: 0 +<<: !include common.yaml diff --git a/tests/components/gt911/test.esp32-c3-ard.yaml b/tests/components/gt911/test.esp32-c3-ard.yaml index 43f7ac5902..dade44d145 100644 --- a/tests/components/gt911/test.esp32-c3-ard.yaml +++ b/tests/components/gt911/test.esp32-c3-ard.yaml @@ -1,24 +1 @@ -i2c: - - id: i2c_gt911 - scl: 5 - sda: 4 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: gt911 - display: ssd1306_display - interrupt_pin: 6 - -binary_sensor: - - platform: gt911 - id: touch_key_911 - index: 0 +<<: !include common.yaml diff --git a/tests/components/gt911/test.esp32-c3-idf.yaml b/tests/components/gt911/test.esp32-c3-idf.yaml index 43f7ac5902..dade44d145 100644 --- a/tests/components/gt911/test.esp32-c3-idf.yaml +++ b/tests/components/gt911/test.esp32-c3-idf.yaml @@ -1,24 +1 @@ -i2c: - - id: i2c_gt911 - scl: 5 - sda: 4 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: gt911 - display: ssd1306_display - interrupt_pin: 6 - -binary_sensor: - - platform: gt911 - id: touch_key_911 - index: 0 +<<: !include common.yaml diff --git a/tests/components/gt911/test.esp32-idf.yaml b/tests/components/gt911/test.esp32-idf.yaml index a47f7bf260..dade44d145 100644 --- a/tests/components/gt911/test.esp32-idf.yaml +++ b/tests/components/gt911/test.esp32-idf.yaml @@ -1,24 +1 @@ -i2c: - - id: i2c_gt911 - scl: 16 - sda: 17 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: gt911 - display: ssd1306_display - interrupt_pin: 14 - -binary_sensor: - - platform: gt911 - id: touch_key_911 - index: 0 +<<: !include common.yaml diff --git a/tests/components/gt911/test.rp2040-ard.yaml b/tests/components/gt911/test.rp2040-ard.yaml index 43f7ac5902..dade44d145 100644 --- a/tests/components/gt911/test.rp2040-ard.yaml +++ b/tests/components/gt911/test.rp2040-ard.yaml @@ -1,24 +1 @@ -i2c: - - id: i2c_gt911 - scl: 5 - sda: 4 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: gt911 - display: ssd1306_display - interrupt_pin: 6 - -binary_sensor: - - platform: gt911 - id: touch_key_911 - index: 0 +<<: !include common.yaml From d754bdde1b74b39f6d78a40a5fa92b721ce4cd93 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 30 Aug 2024 06:27:35 +1000 Subject: [PATCH 0284/1052] [st7701s] Add delay feature in init sequences (#7343) --- esphome/components/st7701s/display.py | 88 +++++++++++++------------- esphome/components/st7701s/st7701s.cpp | 17 +++-- esphome/components/st7701s/st7701s.h | 1 + tests/components/st7701s/common.yaml | 7 +- 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/esphome/components/st7701s/display.py b/esphome/components/st7701s/display.py index 516d770f8b..9310e9d760 100644 --- a/esphome/components/st7701s/display.py +++ b/esphome/components/st7701s/display.py @@ -1,47 +1,39 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins -from esphome.components import ( - spi, - display, -) -from esphome.const import ( - CONF_DC_PIN, - CONF_HSYNC_PIN, - CONF_RESET_PIN, - CONF_DATA_PINS, - CONF_ID, - CONF_DIMENSIONS, - CONF_VSYNC_PIN, - CONF_WIDTH, - CONF_HEIGHT, - CONF_LAMBDA, - CONF_MIRROR_X, - CONF_MIRROR_Y, - CONF_COLOR_ORDER, - CONF_TRANSFORM, - CONF_OFFSET_HEIGHT, - CONF_OFFSET_WIDTH, - CONF_INVERT_COLORS, - CONF_RED, - CONF_GREEN, - CONF_BLUE, - CONF_NUMBER, - CONF_IGNORE_STRAPPING_WARNING, -) - -from esphome.components.esp32 import ( - only_on_variant, - const, -) +import esphome.codegen as cg +from esphome.components import display, spi +from esphome.components.esp32 import const, only_on_variant from esphome.components.rpi_dpi_rgb.display import ( CONF_PCLK_FREQUENCY, CONF_PCLK_INVERTED, ) -from .init_sequences import ( - ST7701S_INITS, - cmd, +import esphome.config_validation as cv +from esphome.const import ( + CONF_BLUE, + CONF_COLOR_ORDER, + CONF_DATA_PINS, + CONF_DC_PIN, + CONF_DIMENSIONS, + CONF_GREEN, + CONF_HEIGHT, + CONF_HSYNC_PIN, + CONF_ID, + CONF_IGNORE_STRAPPING_WARNING, + CONF_INVERT_COLORS, + CONF_LAMBDA, + CONF_MIRROR_X, + CONF_MIRROR_Y, + CONF_NUMBER, + CONF_OFFSET_HEIGHT, + CONF_OFFSET_WIDTH, + CONF_RED, + CONF_RESET_PIN, + CONF_TRANSFORM, + CONF_VSYNC_PIN, + CONF_WIDTH, ) +from esphome.core import TimePeriod + +from .init_sequences import ST7701S_INITS, cmd CONF_INIT_SEQUENCE = "init_sequence" CONF_DE_PIN = "de_pin" @@ -59,6 +51,7 @@ DEPENDENCIES = ["spi", "esp32"] st7701s_ns = cg.esphome_ns.namespace("st7701s") ST7701S = st7701s_ns.class_("ST7701S", display.Display, cg.Component, spi.SPIDevice) ColorOrder = display.display_ns.enum("ColorMode") +ST7701S_DELAY_FLAG = 0xFF COLOR_ORDERS = { "RGB": ColorOrder.COLOR_ORDER_RGB, @@ -93,18 +86,23 @@ def map_sequence(value): """ An initialisation sequence can be selected from one of the pre-defined sequences in init_sequences.py, or can be a literal array of data bytes. - The format is a repeated sequence of [CMD, LEN, ] where is LEN bytes. + The format is a repeated sequence of [CMD, ] where is s a sequence of bytes. The length is inferred + from the length of the sequence and should not be explicit. + A delay can be inserted by specifying "- delay N" where N is in ms """ + if isinstance(value, str) and value.lower().startswith("delay "): + value = value.lower()[6:] + delay = cv.All( + cv.positive_time_period_milliseconds, + cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)), + )(value) + return [delay, ST7701S_DELAY_FLAG] if not isinstance(value, list): value = cv.int_(value) value = cv.one_of(*ST7701S_INITS)(value) return ST7701S_INITS[value] - # value = cv.ensure_list(cv.uint8_t)(value) - data_length = len(value) - if data_length == 0: - raise cv.Invalid("Empty sequence") - value = cmd(*value) - return value + value = cv.Length(min=1, max=254)(value) + return cmd(*value) CONFIG_SCHEMA = cv.All( diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 43d8653709..7f02fe1774 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -138,11 +138,16 @@ void ST7701S::write_init_sequence_() { for (size_t i = 0; i != this->init_sequence_.size();) { uint8_t cmd = this->init_sequence_[i++]; size_t len = this->init_sequence_[i++]; - this->write_sequence_(cmd, len, &this->init_sequence_[i]); - i += len; - esph_log_v(TAG, "Command %X, %d bytes", cmd, len); - if (cmd == SW_RESET_CMD) - delay(6); + if (len == ST7701S_DELAY_FLAG) { + ESP_LOGV(TAG, "Delay %dms", cmd); + delay(cmd); + } else { + this->write_sequence_(cmd, len, &this->init_sequence_[i]); + i += len; + ESP_LOGV(TAG, "Command %X, %d bytes", cmd, len); + if (cmd == SW_RESET_CMD) + delay(6); + } } // st7701 does not appear to support axis swapping this->write_sequence_(CMD2_BKSEL, sizeof(CMD2_BK0), CMD2_BK0); @@ -153,7 +158,7 @@ void ST7701S::write_init_sequence_() { val |= 0x10; this->write_command_(MADCTL_CMD); this->write_data_(val); - esph_log_d(TAG, "write MADCTL %X", val); + ESP_LOGD(TAG, "write MADCTL %X", val); this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF); this->set_timeout(120, [this] { this->write_command_(SLEEP_OUT); diff --git a/esphome/components/st7701s/st7701s.h b/esphome/components/st7701s/st7701s.h index 2328bca965..80e5b81f4a 100644 --- a/esphome/components/st7701s/st7701s.h +++ b/esphome/components/st7701s/st7701s.h @@ -25,6 +25,7 @@ const uint8_t INVERT_ON = 0x21; const uint8_t DISPLAY_ON = 0x29; const uint8_t CMD2_BKSEL = 0xFF; const uint8_t CMD2_BK0[5] = {0x77, 0x01, 0x00, 0x00, 0x10}; +const uint8_t ST7701S_DELAY_FLAG = 0xFF; class ST7701S : public display::Display, public spi::SPIDevice Date: Fri, 30 Aug 2024 09:20:01 +1000 Subject: [PATCH 0285/1052] Add now required `invert_colors` option to test files referencing ili9xxx (#7367) --- tests/components/animation/test.esp32-ard.yaml | 1 + tests/components/animation/test.esp32-c3-ard.yaml | 1 + tests/components/animation/test.esp32-c3-idf.yaml | 1 + tests/components/animation/test.esp32-idf.yaml | 1 + tests/components/animation/test.esp8266-ard.yaml | 1 + tests/components/animation/test.rp2040-ard.yaml | 1 + tests/components/cst226/common.yaml | 1 + tests/components/cst816/common.yaml | 1 + tests/components/display/common.yaml | 1 + tests/components/ft63x6/test.esp32-ard.yaml | 1 + tests/components/image/test.esp32-ard.yaml | 1 + tests/components/image/test.esp32-c3-ard.yaml | 1 + tests/components/image/test.esp32-c3-idf.yaml | 1 + tests/components/image/test.esp32-idf.yaml | 1 + tests/components/image/test.esp8266-ard.yaml | 1 + tests/components/image/test.rp2040-ard.yaml | 1 + tests/components/online_image/common-esp32.yaml | 1 + tests/components/online_image/common-esp8266.yaml | 1 + tests/components/qr_code/test.esp32-ard.yaml | 1 + tests/components/qr_code/test.esp32-c3-ard.yaml | 1 + tests/components/qr_code/test.esp32-c3-idf.yaml | 1 + tests/components/qr_code/test.esp32-idf.yaml | 1 + tests/components/qr_code/test.esp8266-ard.yaml | 1 + tests/components/qr_code/test.rp2040-ard.yaml | 1 + tests/components/tt21100/test.esp32-s2-ard.yaml | 1 + tests/components/xpt2046/test.esp32-ard.yaml | 1 + tests/components/xpt2046/test.esp32-c3-ard.yaml | 1 + tests/components/xpt2046/test.esp32-c3-idf.yaml | 1 + tests/components/xpt2046/test.esp32-idf.yaml | 1 + tests/components/xpt2046/test.esp32-s2-ard.yaml | 1 + tests/components/xpt2046/test.esp8266-ard.yaml | 1 + tests/components/xpt2046/test.rp2040-ard.yaml | 1 + 32 files changed, 32 insertions(+) diff --git a/tests/components/animation/test.esp32-ard.yaml b/tests/components/animation/test.esp32-ard.yaml index 5dc132eb2d..af6cd202dd 100644 --- a/tests/components/animation/test.esp32-ard.yaml +++ b/tests/components/animation/test.esp32-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: false # Purposely test that `animation:` does auto-load `image:` # Keep the `image:` undefined. diff --git a/tests/components/animation/test.esp32-c3-ard.yaml b/tests/components/animation/test.esp32-c3-ard.yaml index 9bcfbdb118..10e8ccb47e 100644 --- a/tests/components/animation/test.esp32-c3-ard.yaml +++ b/tests/components/animation/test.esp32-c3-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: false # Purposely test that `animation:` does auto-load `image:` # Keep the `image:` undefined. diff --git a/tests/components/animation/test.esp32-c3-idf.yaml b/tests/components/animation/test.esp32-c3-idf.yaml index 9bcfbdb118..10e8ccb47e 100644 --- a/tests/components/animation/test.esp32-c3-idf.yaml +++ b/tests/components/animation/test.esp32-c3-idf.yaml @@ -11,6 +11,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: false # Purposely test that `animation:` does auto-load `image:` # Keep the `image:` undefined. diff --git a/tests/components/animation/test.esp32-idf.yaml b/tests/components/animation/test.esp32-idf.yaml index 5dc132eb2d..af6cd202dd 100644 --- a/tests/components/animation/test.esp32-idf.yaml +++ b/tests/components/animation/test.esp32-idf.yaml @@ -11,6 +11,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: false # Purposely test that `animation:` does auto-load `image:` # Keep the `image:` undefined. diff --git a/tests/components/animation/test.esp8266-ard.yaml b/tests/components/animation/test.esp8266-ard.yaml index ef0f483a79..ced4996f25 100644 --- a/tests/components/animation/test.esp8266-ard.yaml +++ b/tests/components/animation/test.esp8266-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 5 dc_pin: 15 reset_pin: 16 + invert_colors: false # Purposely test that `animation:` does auto-load `image:` # Keep the `image:` undefined. diff --git a/tests/components/animation/test.rp2040-ard.yaml b/tests/components/animation/test.rp2040-ard.yaml index 6ee29a3347..0e33959cc6 100644 --- a/tests/components/animation/test.rp2040-ard.yaml +++ b/tests/components/animation/test.rp2040-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 20 dc_pin: 21 reset_pin: 22 + invert_colors: false # Purposely test that `animation:` does auto-load `image:` # Keep the `image:` undefined. diff --git a/tests/components/cst226/common.yaml b/tests/components/cst226/common.yaml index 4cbf38ef50..7e1c5dde36 100644 --- a/tests/components/cst226/common.yaml +++ b/tests/components/cst226/common.yaml @@ -12,6 +12,7 @@ display: dc_pin: GPIO4 reset_pin: number: GPIO21 + invert_colors: false i2c: scl: GPIO18 diff --git a/tests/components/cst816/common.yaml b/tests/components/cst816/common.yaml index f8deea6e98..91abbfd4f6 100644 --- a/tests/components/cst816/common.yaml +++ b/tests/components/cst816/common.yaml @@ -26,6 +26,7 @@ display: mirror_x: true mirror_y: true auto_clear_enabled: false + invert_colors: false spi: clk_pin: 14 diff --git a/tests/components/display/common.yaml b/tests/components/display/common.yaml index a22aa76780..1df2665067 100644 --- a/tests/components/display/common.yaml +++ b/tests/components/display/common.yaml @@ -11,6 +11,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: false lambda: |- // Draw an analog clock in the center of the screen int centerX = it.get_width() / 2; diff --git a/tests/components/ft63x6/test.esp32-ard.yaml b/tests/components/ft63x6/test.esp32-ard.yaml index 32d6634dae..5c43cdff45 100644 --- a/tests/components/ft63x6/test.esp32-ard.yaml +++ b/tests/components/ft63x6/test.esp32-ard.yaml @@ -19,6 +19,7 @@ display: mirror_x: true mirror_y: true auto_clear_enabled: false + invert_colors: false touchscreen: - platform: ft63x6 diff --git a/tests/components/image/test.esp32-ard.yaml b/tests/components/image/test.esp32-ard.yaml index ff9adde6b1..34c7914976 100644 --- a/tests/components/image/test.esp32-ard.yaml +++ b/tests/components/image/test.esp32-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: true image: - id: binary_image diff --git a/tests/components/image/test.esp32-c3-ard.yaml b/tests/components/image/test.esp32-c3-ard.yaml index c083a97c94..91ff0a0579 100644 --- a/tests/components/image/test.esp32-c3-ard.yaml +++ b/tests/components/image/test.esp32-c3-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: true image: - id: binary_image diff --git a/tests/components/image/test.esp32-c3-idf.yaml b/tests/components/image/test.esp32-c3-idf.yaml index c083a97c94..91ff0a0579 100644 --- a/tests/components/image/test.esp32-c3-idf.yaml +++ b/tests/components/image/test.esp32-c3-idf.yaml @@ -11,6 +11,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: true image: - id: binary_image diff --git a/tests/components/image/test.esp32-idf.yaml b/tests/components/image/test.esp32-idf.yaml index ff9adde6b1..34c7914976 100644 --- a/tests/components/image/test.esp32-idf.yaml +++ b/tests/components/image/test.esp32-idf.yaml @@ -11,6 +11,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: true image: - id: binary_image diff --git a/tests/components/image/test.esp8266-ard.yaml b/tests/components/image/test.esp8266-ard.yaml index 3632b95485..5a96ed9497 100644 --- a/tests/components/image/test.esp8266-ard.yaml +++ b/tests/components/image/test.esp8266-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 5 dc_pin: 15 reset_pin: 16 + invert_colors: true image: - id: binary_image diff --git a/tests/components/image/test.rp2040-ard.yaml b/tests/components/image/test.rp2040-ard.yaml index b79c8a9195..4c40ca464f 100644 --- a/tests/components/image/test.rp2040-ard.yaml +++ b/tests/components/image/test.rp2040-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 20 dc_pin: 21 reset_pin: 22 + invert_colors: true image: - id: binary_image diff --git a/tests/components/online_image/common-esp32.yaml b/tests/components/online_image/common-esp32.yaml index 8cc50fc3e0..d3a304cdc0 100644 --- a/tests/components/online_image/common-esp32.yaml +++ b/tests/components/online_image/common-esp32.yaml @@ -13,6 +13,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: true lambda: |- it.fill(Color(0, 0, 0)); it.image(0, 0, id(online_rgba_image)); diff --git a/tests/components/online_image/common-esp8266.yaml b/tests/components/online_image/common-esp8266.yaml index 01e3467413..ba15b5025c 100644 --- a/tests/components/online_image/common-esp8266.yaml +++ b/tests/components/online_image/common-esp8266.yaml @@ -13,6 +13,7 @@ display: cs_pin: 15 dc_pin: 3 reset_pin: 1 + invert_colors: true lambda: |- it.fill(Color(0, 0, 0)); it.image(0, 0, id(online_rgba_image)); diff --git a/tests/components/qr_code/test.esp32-ard.yaml b/tests/components/qr_code/test.esp32-ard.yaml index 3e70d3258f..8689d4d73f 100644 --- a/tests/components/qr_code/test.esp32-ard.yaml +++ b/tests/components/qr_code/test.esp32-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: false lambda: |- // Draw a QR code in the center of the screen auto scale = 2; diff --git a/tests/components/qr_code/test.esp32-c3-ard.yaml b/tests/components/qr_code/test.esp32-c3-ard.yaml index 63973b1aa2..3690d2598c 100644 --- a/tests/components/qr_code/test.esp32-c3-ard.yaml +++ b/tests/components/qr_code/test.esp32-c3-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: false lambda: |- // Draw a QR code in the center of the screen auto scale = 2; diff --git a/tests/components/qr_code/test.esp32-c3-idf.yaml b/tests/components/qr_code/test.esp32-c3-idf.yaml index 63973b1aa2..3690d2598c 100644 --- a/tests/components/qr_code/test.esp32-c3-idf.yaml +++ b/tests/components/qr_code/test.esp32-c3-idf.yaml @@ -11,6 +11,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: false lambda: |- // Draw a QR code in the center of the screen auto scale = 2; diff --git a/tests/components/qr_code/test.esp32-idf.yaml b/tests/components/qr_code/test.esp32-idf.yaml index 3e70d3258f..8689d4d73f 100644 --- a/tests/components/qr_code/test.esp32-idf.yaml +++ b/tests/components/qr_code/test.esp32-idf.yaml @@ -11,6 +11,7 @@ display: cs_pin: 12 dc_pin: 13 reset_pin: 21 + invert_colors: false lambda: |- // Draw a QR code in the center of the screen auto scale = 2; diff --git a/tests/components/qr_code/test.esp8266-ard.yaml b/tests/components/qr_code/test.esp8266-ard.yaml index 3c304d7575..02dc183440 100644 --- a/tests/components/qr_code/test.esp8266-ard.yaml +++ b/tests/components/qr_code/test.esp8266-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 5 dc_pin: 15 reset_pin: 16 + invert_colors: false lambda: |- // Draw a QR code in the center of the screen auto scale = 2; diff --git a/tests/components/qr_code/test.rp2040-ard.yaml b/tests/components/qr_code/test.rp2040-ard.yaml index 94cb772ba3..0d86f8d213 100644 --- a/tests/components/qr_code/test.rp2040-ard.yaml +++ b/tests/components/qr_code/test.rp2040-ard.yaml @@ -11,6 +11,7 @@ display: cs_pin: 20 dc_pin: 21 reset_pin: 22 + invert_colors: false lambda: |- // Draw a QR code in the center of the screen auto scale = 2; diff --git a/tests/components/tt21100/test.esp32-s2-ard.yaml b/tests/components/tt21100/test.esp32-s2-ard.yaml index 7ebabcb130..86b9e7530d 100644 --- a/tests/components/tt21100/test.esp32-s2-ard.yaml +++ b/tests/components/tt21100/test.esp32-s2-ard.yaml @@ -18,6 +18,7 @@ display: data_rate: 40MHz dimensions: 320x240 update_interval: never + invert_colors: false transform: mirror_y: false mirror_x: false diff --git a/tests/components/xpt2046/test.esp32-ard.yaml b/tests/components/xpt2046/test.esp32-ard.yaml index bb166866f4..f15d1f9b41 100644 --- a/tests/components/xpt2046/test.esp32-ard.yaml +++ b/tests/components/xpt2046/test.esp32-ard.yaml @@ -12,6 +12,7 @@ display: cs_pin: 13 dc_pin: 14 reset_pin: 21 + invert_colors: false lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/xpt2046/test.esp32-c3-ard.yaml b/tests/components/xpt2046/test.esp32-c3-ard.yaml index f3a2cf9aae..ef4daa800d 100644 --- a/tests/components/xpt2046/test.esp32-c3-ard.yaml +++ b/tests/components/xpt2046/test.esp32-c3-ard.yaml @@ -12,6 +12,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: false lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/xpt2046/test.esp32-c3-idf.yaml b/tests/components/xpt2046/test.esp32-c3-idf.yaml index f3a2cf9aae..ef4daa800d 100644 --- a/tests/components/xpt2046/test.esp32-c3-idf.yaml +++ b/tests/components/xpt2046/test.esp32-c3-idf.yaml @@ -12,6 +12,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: false lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/xpt2046/test.esp32-idf.yaml b/tests/components/xpt2046/test.esp32-idf.yaml index bb166866f4..f15d1f9b41 100644 --- a/tests/components/xpt2046/test.esp32-idf.yaml +++ b/tests/components/xpt2046/test.esp32-idf.yaml @@ -12,6 +12,7 @@ display: cs_pin: 13 dc_pin: 14 reset_pin: 21 + invert_colors: false lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/xpt2046/test.esp32-s2-ard.yaml b/tests/components/xpt2046/test.esp32-s2-ard.yaml index 6232ca957b..df2a99b4f5 100644 --- a/tests/components/xpt2046/test.esp32-s2-ard.yaml +++ b/tests/components/xpt2046/test.esp32-s2-ard.yaml @@ -14,6 +14,7 @@ display: data_rate: 40MHz dimensions: 320x240 update_interval: never + invert_colors: false transform: mirror_y: false mirror_x: false diff --git a/tests/components/xpt2046/test.esp8266-ard.yaml b/tests/components/xpt2046/test.esp8266-ard.yaml index a917290e8e..0daa25ad60 100644 --- a/tests/components/xpt2046/test.esp8266-ard.yaml +++ b/tests/components/xpt2046/test.esp8266-ard.yaml @@ -12,6 +12,7 @@ display: cs_pin: 15 dc_pin: 4 reset_pin: 5 + invert_colors: false lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/xpt2046/test.rp2040-ard.yaml b/tests/components/xpt2046/test.rp2040-ard.yaml index a7a49309ac..8afc45d04d 100644 --- a/tests/components/xpt2046/test.rp2040-ard.yaml +++ b/tests/components/xpt2046/test.rp2040-ard.yaml @@ -12,6 +12,7 @@ display: cs_pin: 8 dc_pin: 9 reset_pin: 10 + invert_colors: false lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); From 69f98e0f87eacdad0be79fc33287e869c37587ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20Kry=C5=84ski?= Date: Fri, 30 Aug 2024 01:43:47 +0200 Subject: [PATCH 0286/1052] esp32_can: make queue lengths configurable (#7361) --- esphome/components/esp32_can/canbus.py | 25 ++++++++++++++++------ esphome/components/esp32_can/esp32_can.cpp | 7 ++++++ esphome/components/esp32_can/esp32_can.h | 4 ++++ esphome/const.py | 2 ++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/esphome/components/esp32_can/canbus.py b/esphome/components/esp32_can/canbus.py index f4ba032009..37bdfa3962 100644 --- a/esphome/components/esp32_can/canbus.py +++ b/esphome/components/esp32_can/canbus.py @@ -1,18 +1,23 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import canbus -from esphome.const import CONF_ID, CONF_RX_PIN, CONF_TX_PIN -from esphome.components.canbus import CanbusComponent, CanSpeed, CONF_BIT_RATE - +from esphome.components.canbus import CONF_BIT_RATE, CanbusComponent, CanSpeed from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32.const import ( VARIANT_ESP32, - VARIANT_ESP32S2, - VARIANT_ESP32S3, VARIANT_ESP32C3, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +) +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + CONF_RX_PIN, + CONF_RX_QUEUE_LEN, + CONF_TX_PIN, + CONF_TX_QUEUE_LEN, ) CODEOWNERS = ["@Sympatron"] @@ -77,6 +82,8 @@ CONFIG_SCHEMA = canbus.CANBUS_SCHEMA.extend( cv.Optional(CONF_BIT_RATE, default="125KBPS"): validate_bit_rate, cv.Required(CONF_RX_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_TX_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_RX_QUEUE_LEN): cv.uint32_t, + cv.Optional(CONF_TX_QUEUE_LEN): cv.uint32_t, } ) @@ -87,3 +94,7 @@ async def to_code(config): cg.add(var.set_rx(config[CONF_RX_PIN])) cg.add(var.set_tx(config[CONF_TX_PIN])) + if (rx_queue_len := config.get(CONF_RX_QUEUE_LEN)) is not None: + cg.add(var.set_rx_queue_len(rx_queue_len)) + if (tx_queue_len := config.get(CONF_TX_QUEUE_LEN)) is not None: + cg.add(var.set_tx_queue_len(tx_queue_len)) diff --git a/esphome/components/esp32_can/esp32_can.cpp b/esphome/components/esp32_can/esp32_can.cpp index 214b72e864..5a45859b1f 100644 --- a/esphome/components/esp32_can/esp32_can.cpp +++ b/esphome/components/esp32_can/esp32_can.cpp @@ -69,6 +69,13 @@ static bool get_bitrate(canbus::CanSpeed bitrate, twai_timing_config_t *t_config bool ESP32Can::setup_internal() { twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t) this->tx_, (gpio_num_t) this->rx_, TWAI_MODE_NORMAL); + if (this->tx_queue_len_.has_value()) { + g_config.tx_queue_len = this->tx_queue_len_.value(); + } + if (this->rx_queue_len_.has_value()) { + g_config.rx_queue_len = this->rx_queue_len_.value(); + } + twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); twai_timing_config_t t_config; diff --git a/esphome/components/esp32_can/esp32_can.h b/esphome/components/esp32_can/esp32_can.h index a428834f65..b3086f9a48 100644 --- a/esphome/components/esp32_can/esp32_can.h +++ b/esphome/components/esp32_can/esp32_can.h @@ -12,6 +12,8 @@ class ESP32Can : public canbus::Canbus { public: void set_rx(int rx) { rx_ = rx; } void set_tx(int tx) { tx_ = tx; } + void set_tx_queue_len(uint32_t tx_queue_len) { this->tx_queue_len_ = tx_queue_len; } + void set_rx_queue_len(uint32_t rx_queue_len) { this->rx_queue_len_ = rx_queue_len; } ESP32Can(){}; protected: @@ -21,6 +23,8 @@ class ESP32Can : public canbus::Canbus { int rx_{-1}; int tx_{-1}; + optional tx_queue_len_{}; + optional rx_queue_len_{}; }; } // namespace esp32_can diff --git a/esphome/const.py b/esphome/const.py index 6e29667887..95773630d0 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -730,6 +730,7 @@ CONF_RW_PIN = "rw_pin" CONF_RX_BUFFER_SIZE = "rx_buffer_size" CONF_RX_ONLY = "rx_only" CONF_RX_PIN = "rx_pin" +CONF_RX_QUEUE_LEN = "rx_queue_len" CONF_SAFE_MODE = "safe_mode" CONF_SAMPLE_RATE = "sample_rate" CONF_SAMSUNG = "samsung" @@ -881,6 +882,7 @@ CONF_TVOC = "tvoc" CONF_TX_BUFFER_SIZE = "tx_buffer_size" CONF_TX_PIN = "tx_pin" CONF_TX_POWER = "tx_power" +CONF_TX_QUEUE_LEN = "tx_queue_len" CONF_TYPE = "type" CONF_TYPE_ID = "type_id" CONF_UART_ID = "uart_id" From f8e8bd2c24666fe1efaf60a44ea46dfae3093341 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Fri, 30 Aug 2024 02:03:44 +0200 Subject: [PATCH 0287/1052] [code-quality] fix clang-tidy web_server and web_server_base (#7286) --- esphome/components/web_server/__init__.py | 26 +++++++++---------- .../components/web_server/list_entities.cpp | 2 ++ esphome/components/web_server/list_entities.h | 4 ++- esphome/components/web_server/web_server.cpp | 3 ++- esphome/components/web_server/web_server.h | 2 ++ .../web_server_base/web_server_base.cpp | 2 ++ .../web_server_base/web_server_base.h | 4 ++- 7 files changed, 27 insertions(+), 16 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 232ab40d10..02074dcf11 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -1,35 +1,36 @@ from __future__ import annotations import gzip + import esphome.codegen as cg -import esphome.config_validation as cv -import esphome.final_validate as fv from esphome.components import web_server_base from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID +import esphome.config_validation as cv from esphome.const import ( + CONF_AUTH, CONF_CSS_INCLUDE, CONF_CSS_URL, + CONF_ENABLE_PRIVATE_NETWORK_ACCESS, CONF_ID, + CONF_INCLUDE_INTERNAL, CONF_JS_INCLUDE, CONF_JS_URL, - CONF_ENABLE_PRIVATE_NETWORK_ACCESS, - CONF_PORT, - CONF_AUTH, - CONF_USERNAME, - CONF_PASSWORD, - CONF_INCLUDE_INTERNAL, - CONF_OTA, - CONF_LOG, - CONF_VERSION, CONF_LOCAL, + CONF_LOG, + CONF_OTA, + CONF_PASSWORD, + CONF_PORT, + CONF_USERNAME, + CONF_VERSION, CONF_WEB_SERVER_ID, CONF_WEB_SERVER_SORTING_WEIGHT, + PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, - PLATFORM_BK72XX, PLATFORM_RTL87XX, ) from esphome.core import CORE, coroutine_with_priority +import esphome.final_validate as fv AUTO_LOAD = ["json", "web_server_base"] @@ -208,7 +209,6 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID], paren) await cg.register_component(var, config) - cg.add_define("USE_WEBSERVER") version = config[CONF_VERSION] cg.add(paren.set_port(config[CONF_PORT])) diff --git a/esphome/components/web_server/list_entities.cpp b/esphome/components/web_server/list_entities.cpp index 332f358352..a02f84c34b 100644 --- a/esphome/components/web_server/list_entities.cpp +++ b/esphome/components/web_server/list_entities.cpp @@ -1,4 +1,5 @@ #include "list_entities.h" +#ifdef USE_WEBSERVER #include "esphome/core/application.h" #include "esphome/core/log.h" #include "esphome/core/util.h" @@ -188,3 +189,4 @@ bool ListEntitiesIterator::on_update(update::UpdateEntity *update) { } // namespace web_server } // namespace esphome +#endif diff --git a/esphome/components/web_server/list_entities.h b/esphome/components/web_server/list_entities.h index 5ff6ec0412..53e5bc3355 100644 --- a/esphome/components/web_server/list_entities.h +++ b/esphome/components/web_server/list_entities.h @@ -1,8 +1,9 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_WEBSERVER #include "esphome/core/component.h" #include "esphome/core/component_iterator.h" -#include "esphome/core/defines.h" namespace esphome { namespace web_server { @@ -78,3 +79,4 @@ class ListEntitiesIterator : public ComponentIterator { } // namespace web_server } // namespace esphome +#endif diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 6fb04f558a..1bb7c6c249 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1,5 +1,5 @@ #include "web_server.h" - +#ifdef USE_WEBSERVER #include "esphome/components/json/json_util.h" #include "esphome/components/network/util.h" #include "esphome/core/application.h" @@ -1659,3 +1659,4 @@ void WebServer::schedule_(std::function &&f) { } // namespace web_server } // namespace esphome +#endif diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index d4ab592b7b..3195fa7109 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -3,6 +3,7 @@ #include "list_entities.h" #include "esphome/components/web_server_base/web_server_base.h" +#ifdef USE_WEBSERVER #include "esphome/core/component.h" #include "esphome/core/controller.h" #include "esphome/core/entity_base.h" @@ -366,3 +367,4 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { } // namespace web_server } // namespace esphome +#endif diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index f90c7e56a3..7c09022f27 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -1,4 +1,5 @@ #include "web_server_base.h" +#ifdef USE_NETWORK #include "esphome/core/log.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" @@ -121,3 +122,4 @@ float WebServerBase::get_setup_priority() const { } // namespace web_server_base } // namespace esphome +#endif diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 2282d55ec1..f876d163bc 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -1,5 +1,6 @@ #pragma once - +#include "esphome/core/defines.h" +#ifdef USE_NETWORK #include #include #include @@ -145,3 +146,4 @@ class OTARequestHandler : public AsyncWebHandler { } // namespace web_server_base } // namespace esphome +#endif From a5d46ae9e553d27e7f66dc3bd87c64dc86b2cf36 Mon Sep 17 00:00:00 2001 From: Trevor Schirmer <24777085+TrevorSchirmer@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:36:32 -0400 Subject: [PATCH 0288/1052] Update MiCS Values (#7173) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mics_4514/mics_4514.cpp | 62 +++++++++------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/esphome/components/mics_4514/mics_4514.cpp b/esphome/components/mics_4514/mics_4514.cpp index a14d7f2f80..ed2fc6c826 100644 --- a/esphome/components/mics_4514/mics_4514.cpp +++ b/esphome/components/mics_4514/mics_4514.cpp @@ -70,72 +70,62 @@ void MICS4514Component::update() { if (this->carbon_monoxide_sensor_ != nullptr) { float co = 0.0f; - if (red_f <= 0.425f) { - co = (0.425f - red_f) / 0.000405f; - if (co < 1.0f) - co = 0.0f; - if (co > 1000.0f) - co = 1000.0f; + if (red_f > 3.4f) { + co = 0.0; + } else if (red_f < 0.01) { + co = 1000.0; + } else { + co = 4.2 / pow(red_f, 1.2); } this->carbon_monoxide_sensor_->publish_state(co); } if (this->nitrogen_dioxide_sensor_ != nullptr) { float nitrogendioxide = 0.0f; - if (ox_f >= 1.1f) { - nitrogendioxide = (ox_f - 0.045f) / 6.13f; - if (nitrogendioxide < 0.1f) - nitrogendioxide = 0.0f; - if (nitrogendioxide > 10.0f) - nitrogendioxide = 10.0f; + if (ox_f < 0.3f) { + nitrogendioxide = 0.0; + } else { + nitrogendioxide = 0.164 * pow(ox_f, 0.975); } this->nitrogen_dioxide_sensor_->publish_state(nitrogendioxide); } if (this->methane_sensor_ != nullptr) { float methane = 0.0f; - if (red_f <= 0.786f) { - methane = (0.786f - red_f) / 0.000023f; - if (methane < 1000.0f) - methane = 0.0f; - if (methane > 25000.0f) - methane = 25000.0f; + if (red_f > 0.9f || red_f < 0.5) { // outside the range->unlikely + methane = 0.0; + } else { + methane = 630 / pow(red_f, 4.4); } this->methane_sensor_->publish_state(methane); } if (this->ethanol_sensor_ != nullptr) { float ethanol = 0.0f; - if (red_f <= 0.306f) { - ethanol = (0.306f - red_f) / 0.00057f; - if (ethanol < 10.0f) - ethanol = 0.0f; - if (ethanol > 500.0f) - ethanol = 500.0f; + if (red_f > 1.0f || red_f < 0.02) { // outside the range->unlikely + ethanol = 0.0; + } else { + ethanol = 1.52 / pow(red_f, 1.55); } this->ethanol_sensor_->publish_state(ethanol); } if (this->hydrogen_sensor_ != nullptr) { float hydrogen = 0.0f; - if (red_f <= 0.279f) { - hydrogen = (0.279f - red_f) / 0.00026f; - if (hydrogen < 1.0f) - hydrogen = 0.0f; - if (hydrogen > 1000.0f) - hydrogen = 1000.0f; + if (red_f > 0.9f || red_f < 0.02) { // outside the range->unlikely + hydrogen = 0.0; + } else { + hydrogen = 0.85 / pow(red_f, 1.75); } this->hydrogen_sensor_->publish_state(hydrogen); } if (this->ammonia_sensor_ != nullptr) { float ammonia = 0.0f; - if (red_f <= 0.8f) { - ammonia = (0.8f - red_f) / 0.0015f; - if (ammonia < 1.0f) - ammonia = 0.0f; - if (ammonia > 500.0f) - ammonia = 500.0f; + if (red_f > 0.98f || red_f < 0.2532) { // outside the ammonia range->unlikely + ammonia = 0.0; + } else { + ammonia = 0.9 / pow(red_f, 4.6); } this->ammonia_sensor_->publish_state(ammonia); } From 721b532d71b4ca82c811e3db3b3e189e134704d8 Mon Sep 17 00:00:00 2001 From: Piotr Szulc Date: Fri, 30 Aug 2024 02:53:34 +0200 Subject: [PATCH 0289/1052] Tuya Number: allow restoring value of hidden datapoints (#7346) --- esphome/components/tuya/number/__init__.py | 3 ++ .../components/tuya/number/tuya_number.cpp | 45 +++++++++++++++++-- esphome/components/tuya/number/tuya_number.h | 9 +++- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/esphome/components/tuya/number/__init__.py b/esphome/components/tuya/number/__init__.py index 25be6329ab..c00ea08d23 100644 --- a/esphome/components/tuya/number/__init__.py +++ b/esphome/components/tuya/number/__init__.py @@ -9,6 +9,7 @@ from esphome.const import ( CONF_MULTIPLY, CONF_STEP, CONF_INITIAL_VALUE, + CONF_RESTORE_VALUE, ) from .. import tuya_ns, CONF_TUYA_ID, Tuya, TuyaDatapointType @@ -58,6 +59,7 @@ CONFIG_SCHEMA = cv.All( DATAPOINT_TYPES, lower=True ), cv.Optional(CONF_INITIAL_VALUE): cv.float_, + cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, } ) ), @@ -90,3 +92,4 @@ async def to_code(config): hidden_init_value := hidden_config.get(CONF_INITIAL_VALUE, None) ) is not None: cg.add(var.set_datapoint_initial_value(hidden_init_value)) + cg.add(var.set_restore_value(hidden_config[CONF_RESTORE_VALUE])) diff --git a/esphome/components/tuya/number/tuya_number.cpp b/esphome/components/tuya/number/tuya_number.cpp index 7eeb08fde2..68a7f8f2a7 100644 --- a/esphome/components/tuya/number/tuya_number.cpp +++ b/esphome/components/tuya/number/tuya_number.cpp @@ -7,14 +7,28 @@ namespace tuya { static const char *const TAG = "tuya.number"; void TuyaNumber::setup() { + if (this->restore_value_) { + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); + } + this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) { if (datapoint.type == TuyaDatapointType::INTEGER) { ESP_LOGV(TAG, "MCU reported number %u is: %d", datapoint.id, datapoint.value_int); - this->publish_state(datapoint.value_int / multiply_by_); + float value = datapoint.value_int / multiply_by_; + this->publish_state(value); + if (this->restore_value_) + this->pref_.save(&value); } else if (datapoint.type == TuyaDatapointType::ENUM) { ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum); - this->publish_state(datapoint.value_enum); + float value = datapoint.value_enum; + this->publish_state(value); + if (this->restore_value_) + this->pref_.save(&value); + } else { + ESP_LOGW(TAG, "Reported type (%d) is not a number!", static_cast(datapoint.type)); + return; } + if ((this->type_) && (this->type_ != datapoint.type)) { ESP_LOGW(TAG, "Reported type (%d) different than previously set (%d)!", static_cast(datapoint.type), static_cast(*this->type_)); @@ -23,8 +37,26 @@ void TuyaNumber::setup() { }); this->parent_->add_on_initialized_callback([this] { - if ((this->initial_value_) && (this->type_)) { - this->control(*this->initial_value_); + if (this->type_) { + float value; + if (!this->restore_value_) { + if (this->initial_value_) { + value = *this->initial_value_; + } else { + return; + } + } else { + if (!this->pref_.load(&value)) { + if (this->initial_value_) { + value = *this->initial_value_; + } else { + value = this->traits.get_min_value(); + ESP_LOGW(TAG, "Failed to restore and there is no initial value defined. Setting min_value (%f)", value); + } + } + } + + this->control(value); } }); } @@ -38,6 +70,9 @@ void TuyaNumber::control(float value) { this->parent_->set_enum_datapoint_value(this->number_id_, value); } this->publish_state(value); + + if (this->restore_value_) + this->pref_.save(&value); } void TuyaNumber::dump_config() { @@ -52,6 +87,8 @@ void TuyaNumber::dump_config() { if (this->initial_value_) { ESP_LOGCONFIG(TAG, " Initial Value: %f", *this->initial_value_); } + + ESP_LOGCONFIG(TAG, " Restore Value: %s", YESNO(this->restore_value_)); } } // namespace tuya diff --git a/esphome/components/tuya/number/tuya_number.h b/esphome/components/tuya/number/tuya_number.h index 545584128e..53137d6f66 100644 --- a/esphome/components/tuya/number/tuya_number.h +++ b/esphome/components/tuya/number/tuya_number.h @@ -1,9 +1,10 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/components/tuya/tuya.h" #include "esphome/components/number/number.h" +#include "esphome/components/tuya/tuya.h" +#include "esphome/core/component.h" #include "esphome/core/optional.h" +#include "esphome/core/preferences.h" namespace esphome { namespace tuya { @@ -16,6 +17,7 @@ class TuyaNumber : public number::Number, public Component { void set_write_multiply(float factor) { multiply_by_ = factor; } void set_datapoint_type(TuyaDatapointType type) { type_ = type; } void set_datapoint_initial_value(float value) { this->initial_value_ = value; } + void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } @@ -27,6 +29,9 @@ class TuyaNumber : public number::Number, public Component { float multiply_by_{1.0}; optional type_{}; optional initial_value_{}; + bool restore_value_{false}; + + ESPPreferenceObject pref_; }; } // namespace tuya From ba6963cf72811cd75d4cd5510d1b81378c049ded Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:59:55 +1000 Subject: [PATCH 0290/1052] [udp] Implement UDP sensor broadcast (#6865) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: clydebarrow <366188+clydebarrow@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/udp/__init__.py | 158 +++++ esphome/components/udp/binary_sensor.py | 27 + esphome/components/udp/sensor.py | 27 + esphome/components/udp/udp_component.cpp | 616 ++++++++++++++++++++ esphome/components/udp/udp_component.h | 158 +++++ tests/components/udp/common.yaml | 35 ++ tests/components/udp/test.bk72xx-ard.yaml | 1 + tests/components/udp/test.esp32-ard.yaml | 1 + tests/components/udp/test.esp32-c3-ard.yaml | 1 + tests/components/udp/test.esp32-c3-idf.yaml | 1 + tests/components/udp/test.esp32-idf.yaml | 1 + tests/components/udp/test.esp8266-ard.yaml | 1 + tests/components/udp/test.host.yaml | 4 + tests/components/udp/test.rp2040-ard.yaml | 1 + 15 files changed, 1033 insertions(+) create mode 100644 esphome/components/udp/__init__.py create mode 100644 esphome/components/udp/binary_sensor.py create mode 100644 esphome/components/udp/sensor.py create mode 100644 esphome/components/udp/udp_component.cpp create mode 100644 esphome/components/udp/udp_component.h create mode 100644 tests/components/udp/common.yaml create mode 100644 tests/components/udp/test.bk72xx-ard.yaml create mode 100644 tests/components/udp/test.esp32-ard.yaml create mode 100644 tests/components/udp/test.esp32-c3-ard.yaml create mode 100644 tests/components/udp/test.esp32-c3-idf.yaml create mode 100644 tests/components/udp/test.esp32-idf.yaml create mode 100644 tests/components/udp/test.esp8266-ard.yaml create mode 100644 tests/components/udp/test.host.yaml create mode 100644 tests/components/udp/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 40511e2f41..807829eafd 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -423,6 +423,7 @@ esphome/components/tuya/switch/* @jesserockz esphome/components/tuya/text_sensor/* @dentra esphome/components/uart/* @esphome/core esphome/components/uart/button/* @ssieb +esphome/components/udp/* @clydebarrow esphome/components/ufire_ec/* @pvizeli esphome/components/ufire_ise/* @pvizeli esphome/components/ultrasonic/* @OttoWinter diff --git a/esphome/components/udp/__init__.py b/esphome/components/udp/__init__.py new file mode 100644 index 0000000000..ca15be2a80 --- /dev/null +++ b/esphome/components/udp/__init__.py @@ -0,0 +1,158 @@ +import hashlib + +import esphome.codegen as cg +from esphome.components.api import CONF_ENCRYPTION +from esphome.components.binary_sensor import BinarySensor +from esphome.components.sensor import Sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_BINARY_SENSORS, + CONF_ID, + CONF_INTERNAL, + CONF_KEY, + CONF_NAME, + CONF_PORT, + CONF_SENSORS, +) +from esphome.cpp_generator import MockObjClass + +CODEOWNERS = ["@clydebarrow"] +DEPENDENCIES = ["network"] +AUTO_LOAD = ["socket"] +MULTI_CONF = True + +udp_ns = cg.esphome_ns.namespace("udp") +UDPComponent = udp_ns.class_("UDPComponent", cg.PollingComponent) + +CONF_BROADCAST = "broadcast" +CONF_BROADCAST_ID = "broadcast_id" +CONF_ADDRESSES = "addresses" +CONF_PROVIDER = "provider" +CONF_PROVIDERS = "providers" +CONF_REMOTE_ID = "remote_id" +CONF_UDP_ID = "udp_id" +CONF_PING_PONG_ENABLE = "ping_pong_enable" +CONF_PING_PONG_RECYCLE_TIME = "ping_pong_recycle_time" +CONF_ROLLING_CODE_ENABLE = "rolling_code_enable" + + +def sensor_validation(cls: MockObjClass): + return cv.maybe_simple_value( + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(cls), + cv.Optional(CONF_BROADCAST_ID): cv.validate_id_name, + } + ), + key=CONF_ID, + ) + + +ENCRYPTION_SCHEMA = { + cv.Optional(CONF_ENCRYPTION): cv.maybe_simple_value( + cv.Schema( + { + cv.Required(CONF_KEY): cv.string, + } + ), + key=CONF_KEY, + ) +} + +PROVIDER_SCHEMA = cv.Schema( + { + cv.Required(CONF_NAME): cv.valid_name, + } +).extend(ENCRYPTION_SCHEMA) + + +def validate_(config): + if CONF_ENCRYPTION in config: + if CONF_SENSORS not in config and CONF_BINARY_SENSORS not in config: + raise cv.Invalid("No sensors or binary sensors to encrypt") + elif config[CONF_ROLLING_CODE_ENABLE]: + raise cv.Invalid("Rolling code requires an encryption key") + if config[CONF_PING_PONG_ENABLE]: + if not any(CONF_ENCRYPTION in p for p in config.get(CONF_PROVIDERS) or ()): + raise cv.Invalid("Ping-pong requires at least one encrypted provider") + return config + + +CONFIG_SCHEMA = cv.All( + cv.polling_component_schema("15s") + .extend( + { + cv.GenerateID(): cv.declare_id(UDPComponent), + cv.Optional(CONF_PORT, default=18511): cv.port, + cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list( + cv.ipv4 + ), + cv.Optional(CONF_ROLLING_CODE_ENABLE, default=False): cv.boolean, + cv.Optional(CONF_PING_PONG_ENABLE, default=False): cv.boolean, + cv.Optional( + CONF_PING_PONG_RECYCLE_TIME, default="600s" + ): cv.positive_time_period_seconds, + cv.Optional(CONF_SENSORS): cv.ensure_list(sensor_validation(Sensor)), + cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list( + sensor_validation(BinarySensor) + ), + cv.Optional(CONF_PROVIDERS): cv.ensure_list(PROVIDER_SCHEMA), + }, + ) + .extend(ENCRYPTION_SCHEMA), + validate_, +) + +SENSOR_SCHEMA = cv.Schema( + { + cv.Optional(CONF_REMOTE_ID): cv.string_strict, + cv.Required(CONF_PROVIDER): cv.valid_name, + cv.GenerateID(CONF_UDP_ID): cv.use_id(UDPComponent), + } +) + + +def require_internal_with_name(config): + if CONF_NAME in config and CONF_INTERNAL not in config: + raise cv.Invalid("Must provide internal: config when using name:") + return config + + +def hash_encryption_key(config: dict): + return list(hashlib.sha256(config[CONF_KEY].encode()).digest()) + + +async def to_code(config): + cg.add_define("USE_UDP") + cg.add_global(udp_ns.using) + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + cg.add(var.set_port(config[CONF_PORT])) + cg.add(var.set_rolling_code_enable(config[CONF_ROLLING_CODE_ENABLE])) + cg.add(var.set_ping_pong_enable(config[CONF_PING_PONG_ENABLE])) + cg.add( + var.set_ping_pong_recycle_time( + config[CONF_PING_PONG_RECYCLE_TIME].total_seconds + ) + ) + for sens_conf in config.get(CONF_SENSORS, ()): + sens_id = sens_conf[CONF_ID] + sensor = await cg.get_variable(sens_id) + bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id) + cg.add(var.add_sensor(bcst_id, sensor)) + for sens_conf in config.get(CONF_BINARY_SENSORS, ()): + sens_id = sens_conf[CONF_ID] + sensor = await cg.get_variable(sens_id) + bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id) + cg.add(var.add_binary_sensor(bcst_id, sensor)) + for address in config[CONF_ADDRESSES]: + cg.add(var.add_address(str(address))) + + if encryption := config.get(CONF_ENCRYPTION): + cg.add(var.set_encryption_key(hash_encryption_key(encryption))) + + for provider in config.get(CONF_PROVIDERS, ()): + name = provider[CONF_NAME] + cg.add(var.add_provider(name)) + if encryption := provider.get(CONF_ENCRYPTION): + cg.add(var.set_provider_encryption(name, hash_encryption_key(encryption))) diff --git a/esphome/components/udp/binary_sensor.py b/esphome/components/udp/binary_sensor.py new file mode 100644 index 0000000000..d90e495527 --- /dev/null +++ b/esphome/components/udp/binary_sensor.py @@ -0,0 +1,27 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +from esphome.config_validation import All, has_at_least_one_key +from esphome.const import CONF_ID + +from . import ( + CONF_PROVIDER, + CONF_REMOTE_ID, + CONF_UDP_ID, + SENSOR_SCHEMA, + require_internal_with_name, +) + +DEPENDENCIES = ["udp"] + +CONFIG_SCHEMA = All( + binary_sensor.binary_sensor_schema().extend(SENSOR_SCHEMA), + has_at_least_one_key(CONF_ID, CONF_REMOTE_ID), + require_internal_with_name, +) + + +async def to_code(config): + var = await binary_sensor.new_binary_sensor(config) + comp = await cg.get_variable(config[CONF_UDP_ID]) + remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID)) + cg.add(comp.add_remote_binary_sensor(config[CONF_PROVIDER], remote_id, var)) diff --git a/esphome/components/udp/sensor.py b/esphome/components/udp/sensor.py new file mode 100644 index 0000000000..860c277c44 --- /dev/null +++ b/esphome/components/udp/sensor.py @@ -0,0 +1,27 @@ +import esphome.codegen as cg +from esphome.components.sensor import new_sensor, sensor_schema +from esphome.config_validation import All, has_at_least_one_key +from esphome.const import CONF_ID + +from . import ( + CONF_PROVIDER, + CONF_REMOTE_ID, + CONF_UDP_ID, + SENSOR_SCHEMA, + require_internal_with_name, +) + +DEPENDENCIES = ["udp"] + +CONFIG_SCHEMA = All( + sensor_schema().extend(SENSOR_SCHEMA), + has_at_least_one_key(CONF_ID, CONF_REMOTE_ID), + require_internal_with_name, +) + + +async def to_code(config): + var = await new_sensor(config) + comp = await cg.get_variable(config[CONF_UDP_ID]) + remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID)) + cg.add(comp.add_remote_sensor(config[CONF_PROVIDER], remote_id, var)) diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp new file mode 100644 index 0000000000..799ed813d3 --- /dev/null +++ b/esphome/components/udp/udp_component.cpp @@ -0,0 +1,616 @@ +#include "esphome/core/log.h" +#include "esphome/core/application.h" +#include "esphome/components/network/util.h" +#include "udp_component.h" + +namespace esphome { +namespace udp { + +/** + * Structure of a data packet; everything is little-endian + * + * --- In clear text --- + * MAGIC_NUMBER: 16 bits + * host name length: 1 byte + * host name: (length) bytes + * padding: 0 or more null bytes to a 4 byte boundary + * + * --- Encrypted (if key set) ---- + * DATA_KEY: 1 byte: OR ROLLING_CODE_KEY: + * Rolling code (if enabled): 8 bytes + * Ping keys: if any + * repeat: + * PING_KEY: 1 byte + * ping code: 4 bytes + * Sensors: + * repeat: + * SENSOR_KEY: 1 byte + * float value: 4 bytes + * name length: 1 byte + * name + * Binary Sensors: + * repeat: + * BINARY_SENSOR_KEY: 1 byte + * bool value: 1 bytes + * name length: 1 byte + * name + * + * Padded to a 4 byte boundary with nulls + * + * Structure of a ping request packet: + * --- In clear text --- + * MAGIC_PING: 16 bits + * host name length: 1 byte + * host name: (length) bytes + * Ping key (4 bytes) + * + */ +static const char *const TAG = "udp"; + +/** + * XXTEA implementation, using 256 bit key. + */ + +static const uint32_t DELTA = 0x9e3779b9; +#define MX ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p ^ e) & 7] ^ z))) + +/** + * Encrypt a block of data in-place + */ + +static void xxtea_encrypt(uint32_t *v, size_t n, const uint32_t *k) { + uint32_t z, y, sum, e; + size_t p; + size_t q = 6 + 52 / n; + sum = 0; + z = v[n - 1]; + while (q-- != 0) { + sum += DELTA; + e = (sum >> 2); + for (p = 0; p != n - 1; p++) { + y = v[p + 1]; + z = v[p] += MX; + } + y = v[0]; + z = v[n - 1] += MX; + } +} + +static void xxtea_decrypt(uint32_t *v, size_t n, const uint32_t *k) { + uint32_t z, y, sum, e; + size_t p; + size_t q = 6 + 52 / n; + sum = q * DELTA; + y = v[0]; + while (q-- != 0) { + e = (sum >> 2); + for (p = n - 1; p != 0; p--) { + z = v[p - 1]; + y = v[p] -= MX; + } + z = v[n - 1]; + y = v[0] -= MX; + sum -= DELTA; + } +} + +inline static size_t round4(size_t value) { return (value + 3) & ~3; } + +union FuData { + uint32_t u32; + float f32; +}; + +static const size_t MAX_PACKET_SIZE = 508; +static const uint16_t MAGIC_NUMBER = 0x4553; +static const uint16_t MAGIC_PING = 0x5048; +static const uint32_t PREF_HASH = 0x45535043; +enum DataKey { + ZERO_FILL_KEY, + DATA_KEY, + SENSOR_KEY, + BINARY_SENSOR_KEY, + PING_KEY, + ROLLING_CODE_KEY, +}; + +static const size_t MAX_PING_KEYS = 4; + +static inline void add(std::vector &vec, uint32_t data) { + vec.push_back(data & 0xFF); + vec.push_back((data >> 8) & 0xFF); + vec.push_back((data >> 16) & 0xFF); + vec.push_back((data >> 24) & 0xFF); +} + +static inline uint32_t get_uint32(uint8_t *&buf) { + uint32_t data = *buf++; + data += *buf++ << 8; + data += *buf++ << 16; + data += *buf++ << 24; + return data; +} + +static inline uint16_t get_uint16(uint8_t *&buf) { + uint16_t data = *buf++; + data += *buf++ << 8; + return data; +} + +static inline void add(std::vector &vec, uint8_t data) { vec.push_back(data); } +static inline void add(std::vector &vec, uint16_t data) { + vec.push_back((uint8_t) data); + vec.push_back((uint8_t) (data >> 8)); +} +static inline void add(std::vector &vec, DataKey data) { vec.push_back(data); } +static void add(std::vector &vec, const char *str) { + auto len = strlen(str); + vec.push_back(len); + for (size_t i = 0; i != len; i++) { + vec.push_back(*str++); + } +} + +void UDPComponent::setup() { + this->name_ = App.get_name().c_str(); + if (strlen(this->name_) > 255) { + this->mark_failed(); + this->status_set_error("Device name exceeds 255 chars"); + return; + } + this->resend_ping_key_ = this->ping_pong_enable_; + // restore the upper 32 bits of the rolling code, increment and save. + this->pref_ = global_preferences->make_preference(PREF_HASH, true); + this->pref_.load(&this->rolling_code_[1]); + this->rolling_code_[1]++; + this->pref_.save(&this->rolling_code_[1]); + this->ping_key_ = random_uint32(); + ESP_LOGV(TAG, "Rolling code incremented, upper part now %u", (unsigned) this->rolling_code_[1]); +#ifdef USE_SENSOR + for (auto &sensor : this->sensors_) { + sensor.sensor->add_on_state_callback([this, &sensor](float x) { + this->updated_ = true; + sensor.updated = true; + }); + } +#endif +#ifdef USE_BINARY_SENSOR + for (auto &sensor : this->binary_sensors_) { + sensor.sensor->add_on_state_callback([this, &sensor](bool value) { + this->updated_ = true; + sensor.updated = true; + }); + } +#endif + this->should_send_ = this->ping_pong_enable_; +#ifdef USE_SENSOR + this->should_send_ |= !this->sensors_.empty(); +#endif +#ifdef USE_BINARY_SENSOR + this->should_send_ |= !this->binary_sensors_.empty(); +#endif + this->should_listen_ = !this->providers_.empty() || this->is_encrypted_(); + // initialise the header. This is invariant. + add(this->header_, MAGIC_NUMBER); + add(this->header_, this->name_); + // pad to a multiple of 4 bytes + while (this->header_.size() & 0x3) + this->header_.push_back(0); +#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) + for (const auto &address : this->addresses_) { + struct sockaddr saddr {}; + socket::set_sockaddr(&saddr, sizeof(saddr), address, this->port_); + this->sockaddrs_.push_back(saddr); + } + // set up broadcast socket + if (this->should_send_) { + this->broadcast_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (this->broadcast_socket_ == nullptr) { + this->mark_failed(); + this->status_set_error("Could not create socket"); + return; + } + int enable = 1; + auto err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); + if (err != 0) { + this->status_set_warning("Socket unable to set reuseaddr"); + // we can still continue + } + err = this->broadcast_socket_->setsockopt(SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int)); + if (err != 0) { + this->status_set_warning("Socket unable to set broadcast"); + } + } + // create listening socket if we either want to subscribe to providers, or need to listen + // for ping key broadcasts. + if (this->should_listen_) { + this->listen_socket_ = socket::socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (this->listen_socket_ == nullptr) { + this->mark_failed(); + this->status_set_error("Could not create socket"); + return; + } + auto err = this->listen_socket_->setblocking(false); + if (err < 0) { + ESP_LOGE(TAG, "Unable to set nonblocking: errno %d", errno); + this->mark_failed(); + this->status_set_error("Unable to set nonblocking"); + return; + } + int enable = 1; + err = this->listen_socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); + if (err != 0) { + this->status_set_warning("Socket unable to set reuseaddr"); + // we can still continue + } + struct sockaddr_in server {}; + + socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_); + if (sl == 0) { + ESP_LOGE(TAG, "Socket unable to set sockaddr: errno %d", errno); + this->mark_failed(); + this->status_set_error("Unable to set sockaddr"); + return; + } + + err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server)); + if (err != 0) { + ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); + this->mark_failed(); + this->status_set_error("Unable to bind socket"); + return; + } + } +#else + // 8266 and RP2040 `Duino + for (const auto &address : this->addresses_) { + auto ipaddr = IPAddress(); + ipaddr.fromString(address.c_str()); + this->ipaddrs_.push_back(ipaddr); + } + if (this->should_listen_) + this->udp_client_.begin(this->port_); +#endif +} + +void UDPComponent::init_data_() { + this->data_.clear(); + if (this->rolling_code_enable_) { + add(this->data_, ROLLING_CODE_KEY); + add(this->data_, this->rolling_code_[0]); + add(this->data_, this->rolling_code_[1]); + this->increment_code_(); + } else { + add(this->data_, DATA_KEY); + } + for (auto pkey : this->ping_keys_) { + add(this->data_, PING_KEY); + add(this->data_, pkey.second); + } +} + +void UDPComponent::flush_() { + if (!network::is_connected() || this->data_.empty()) + return; + uint32_t buffer[MAX_PACKET_SIZE / 4]; + memset(buffer, 0, sizeof buffer); + // len must be a multiple of 4 + auto header_len = round4(this->header_.size()) / 4; + auto len = round4(data_.size()) / 4; + memcpy(buffer, this->header_.data(), this->header_.size()); + memcpy(buffer + header_len, this->data_.data(), this->data_.size()); + if (this->is_encrypted_()) { + xxtea_encrypt(buffer + header_len, len, (uint32_t *) this->encryption_key_.data()); + } + auto total_len = (header_len + len) * 4; + this->send_packet_(buffer, total_len); +} + +void UDPComponent::add_binary_data_(uint8_t key, const char *id, bool data) { + auto len = 1 + 1 + 1 + strlen(id); + if (len + this->header_.size() + this->data_.size() > MAX_PACKET_SIZE) { + this->flush_(); + } + add(this->data_, key); + add(this->data_, (uint8_t) data); + add(this->data_, id); +} +void UDPComponent::add_data_(uint8_t key, const char *id, float data) { + FuData udata{.f32 = data}; + this->add_data_(key, id, udata.u32); +} + +void UDPComponent::add_data_(uint8_t key, const char *id, uint32_t data) { + auto len = 4 + 1 + 1 + strlen(id); + if (len + this->header_.size() + this->data_.size() > MAX_PACKET_SIZE) { + this->flush_(); + } + add(this->data_, key); + add(this->data_, data); + add(this->data_, id); +} +void UDPComponent::send_data_(bool all) { + if (!this->should_send_ || !network::is_connected()) + return; + this->init_data_(); +#ifdef USE_SENSOR + for (auto &sensor : this->sensors_) { + if (all || sensor.updated) { + sensor.updated = false; + this->add_data_(SENSOR_KEY, sensor.id, sensor.sensor->get_state()); + } + } +#endif +#ifdef USE_BINARY_SENSOR + for (auto &sensor : this->binary_sensors_) { + if (all || sensor.updated) { + sensor.updated = false; + this->add_binary_data_(BINARY_SENSOR_KEY, sensor.id, sensor.sensor->state); + } + } +#endif + this->flush_(); + this->updated_ = false; + this->resend_data_ = false; +} + +void UDPComponent::update() { + this->updated_ = true; + this->resend_data_ = this->should_send_; + auto now = millis() / 1000; + if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) { + this->resend_ping_key_ = this->ping_pong_enable_; + this->last_key_time_ = now; + } +} + +void UDPComponent::loop() { + uint8_t buf[MAX_PACKET_SIZE]; + if (this->should_listen_) { + for (;;) { +#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) + auto len = this->listen_socket_->read(buf, sizeof(buf)); +#else + auto len = this->udp_client_.parsePacket(); + if (len > 0) + len = this->udp_client_.read(buf, sizeof(buf)); +#endif + if (len > 0) { + this->process_(buf, len); + continue; + } + break; + } + } + if (this->resend_ping_key_) + this->send_ping_pong_request_(); + if (this->updated_) { + this->send_data_(this->resend_data_); + } +} + +void UDPComponent::add_key_(const char *name, uint32_t key) { + if (!this->is_encrypted_()) + return; + if (this->ping_keys_.count(name) == 0 && this->ping_keys_.size() == MAX_PING_KEYS) { + ESP_LOGW(TAG, "Ping key from %s discarded", name); + return; + } + this->ping_keys_[name] = key; + this->resend_data_ = true; + ESP_LOGV(TAG, "Ping key from %s now %X", name, (unsigned) key); +} + +void UDPComponent::process_ping_request_(const char *name, uint8_t *ptr, size_t len) { + if (len != 4) { + ESP_LOGW(TAG, "Bad ping request"); + return; + } + auto key = get_uint32(ptr); + this->add_key_(name, key); + ESP_LOGV(TAG, "Updated ping key for %s to %08X", name, (unsigned) key); +} + +static bool process_rolling_code(Provider &provider, uint8_t *&buf, const uint8_t *end) { + if (end - buf < 8) + return false; + auto code0 = get_uint32(buf); + auto code1 = get_uint32(buf); + if (code1 < provider.last_code[1] || (code1 == provider.last_code[1] && code0 <= provider.last_code[0])) { + ESP_LOGW(TAG, "Rolling code for %s %08lX:%08lX is old", provider.name, (unsigned long) code1, + (unsigned long) code0); + return false; + } + provider.last_code[0] = code0; + provider.last_code[1] = code1; + return true; +} + +/** + * Process a received packet + */ +void UDPComponent::process_(uint8_t *buf, const size_t len) { + auto ping_key_seen = !this->ping_pong_enable_; + if (len < 8) { + return ESP_LOGV(TAG, "Bad length %zu", len); + } + char namebuf[256]{}; + uint8_t byte; + uint8_t *start_ptr = buf; + const uint8_t *end = buf + len; + FuData rdata{}; + auto magic = get_uint16(buf); + if (magic != MAGIC_NUMBER && magic != MAGIC_PING) + return ESP_LOGV(TAG, "Bad magic %X", magic); + + auto hlen = *buf++; + if (hlen > len - 3) { + return ESP_LOGV(TAG, "Bad hostname length %u > %zu", hlen, len - 3); + } + memcpy(namebuf, buf, hlen); + if (strcmp(this->name_, namebuf) == 0) { + return ESP_LOGV(TAG, "Ignoring our own data"); + } + buf += hlen; + if (magic == MAGIC_PING) + return this->process_ping_request_(namebuf, buf, end - buf); + if (round4(len) != len) { + return ESP_LOGW(TAG, "Bad length %zu", len); + } + hlen = round4(hlen + 3); + buf = start_ptr + hlen; + if (buf == end) { + return ESP_LOGV(TAG, "No data after header"); + } + + if (this->providers_.count(namebuf) == 0) { + return ESP_LOGVV(TAG, "Unknown hostname %s", namebuf); + } + auto &provider = this->providers_[namebuf]; + // if encryption not used with this host, ping check is pointless since it would be easily spoofed. + if (provider.encryption_key.empty()) + ping_key_seen = true; + + ESP_LOGV(TAG, "Found hostname %s", namebuf); +#ifdef USE_SENSOR + auto &sensors = this->remote_sensors_[namebuf]; +#endif +#ifdef USE_BINARY_SENSOR + auto &binary_sensors = this->remote_binary_sensors_[namebuf]; +#endif + + if (!provider.encryption_key.empty()) { + xxtea_decrypt((uint32_t *) buf, (end - buf) / 4, (uint32_t *) provider.encryption_key.data()); + } + byte = *buf++; + if (byte == ROLLING_CODE_KEY) { + if (!process_rolling_code(provider, buf, end)) + return; + } else if (byte != DATA_KEY) { + return ESP_LOGV(TAG, "Expected rolling_key or data_key, got %X", byte); + } + while (buf < end) { + byte = *buf++; + if (byte == ZERO_FILL_KEY) + continue; + if (byte == PING_KEY) { + if (end - buf < 4) { + return ESP_LOGV(TAG, "PING_KEY requires 4 more bytes"); + } + auto key = get_uint32(buf); + if (key == this->ping_key_) { + ping_key_seen = true; + ESP_LOGV(TAG, "Found good ping key %X", (unsigned) key); + } else { + ESP_LOGV(TAG, "Unknown ping key %X", (unsigned) key); + } + continue; + } + if (!ping_key_seen) { + ESP_LOGW(TAG, "Ping key not seen"); + this->resend_ping_key_ = true; + break; + } + if (byte == BINARY_SENSOR_KEY) { + if (end - buf < 3) { + return ESP_LOGV(TAG, "Binary sensor key requires at least 3 more bytes"); + } + rdata.u32 = *buf++; + } else if (byte == SENSOR_KEY) { + if (end - buf < 6) { + return ESP_LOGV(TAG, "Sensor key requires at least 6 more bytes"); + } + rdata.u32 = get_uint32(buf); + } else { + return ESP_LOGW(TAG, "Unknown key byte %X", byte); + } + + hlen = *buf++; + if (end - buf < hlen) { + return ESP_LOGV(TAG, "Name length of %u not available", hlen); + } + memset(namebuf, 0, sizeof namebuf); + memcpy(namebuf, buf, hlen); + ESP_LOGV(TAG, "Found sensor key %d, id %s, data %lX", byte, namebuf, (unsigned long) rdata.u32); + buf += hlen; +#ifdef USE_SENSOR + if (byte == SENSOR_KEY && sensors.count(namebuf) != 0) + sensors[namebuf]->publish_state(rdata.f32); +#endif +#ifdef USE_BINARY_SENSOR + if (byte == BINARY_SENSOR_KEY && binary_sensors.count(namebuf) != 0) + binary_sensors[namebuf]->publish_state(rdata.u32 != 0); +#endif + } +} + +void UDPComponent::dump_config() { + ESP_LOGCONFIG(TAG, "UDP:"); + ESP_LOGCONFIG(TAG, " Port: %u", this->port_); + ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(this->is_encrypted_())); + ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_)); + for (const auto &address : this->addresses_) + ESP_LOGCONFIG(TAG, " Address: %s", address.c_str()); +#ifdef USE_SENSOR + for (auto sensor : this->sensors_) + ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id); +#endif +#ifdef USE_BINARY_SENSOR + for (auto sensor : this->binary_sensors_) + ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.id); +#endif + for (const auto &host : this->providers_) { + ESP_LOGCONFIG(TAG, " Remote host: %s", host.first.c_str()); + ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(!host.second.encryption_key.empty())); +#ifdef USE_SENSOR + for (const auto &sensor : this->remote_sensors_[host.first.c_str()]) + ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.first.c_str()); +#endif +#ifdef USE_BINARY_SENSOR + for (const auto &sensor : this->remote_binary_sensors_[host.first.c_str()]) + ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.first.c_str()); +#endif + } +} +void UDPComponent::increment_code_() { + if (this->rolling_code_enable_) { + if (++this->rolling_code_[0] == 0) { + this->rolling_code_[1]++; + this->pref_.save(&this->rolling_code_[1]); + } + } +} +void UDPComponent::send_packet_(void *data, size_t len) { +#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) + for (const auto &saddr : this->sockaddrs_) { + auto result = this->broadcast_socket_->sendto(data, len, 0, &saddr, sizeof(saddr)); + if (result < 0) + ESP_LOGW(TAG, "sendto() error %d", errno); + } +#else + auto iface = IPAddress(0, 0, 0, 0); + for (const auto &saddr : this->ipaddrs_) { + if (this->udp_client_.beginPacketMulticast(saddr, this->port_, iface, 128) != 0) { + this->udp_client_.write((const uint8_t *) data, len); + auto result = this->udp_client_.endPacket(); + if (result == 0) + ESP_LOGW(TAG, "udp.write() error"); + } + } +#endif +} + +void UDPComponent::send_ping_pong_request_() { + if (!this->ping_pong_enable_ || !network::is_connected()) + return; + this->ping_key_ = random_uint32(); + this->ping_header_.clear(); + add(this->ping_header_, MAGIC_PING); + add(this->ping_header_, this->name_); + add(this->ping_header_, this->ping_key_); + this->send_packet_(this->ping_header_.data(), this->ping_header_.size()); + this->resend_ping_key_ = false; + ESP_LOGV(TAG, "Sent new ping request %08X", (unsigned) this->ping_key_); +} +} // namespace udp +} // namespace esphome diff --git a/esphome/components/udp/udp_component.h b/esphome/components/udp/udp_component.h new file mode 100644 index 0000000000..69bf335a90 --- /dev/null +++ b/esphome/components/udp/udp_component.h @@ -0,0 +1,158 @@ +#pragma once + +#include "esphome/core/component.h" +#ifdef USE_SENSOR +#include "esphome/components/sensor/sensor.h" +#endif +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif +#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) +#include "esphome/components/socket/socket.h" +#else +#include +#endif +#include +#include + +namespace esphome { +namespace udp { + +struct Provider { + std::vector encryption_key; + const char *name; + uint32_t last_code[2]; +}; + +#ifdef USE_SENSOR +struct Sensor { + sensor::Sensor *sensor; + const char *id; + bool updated; +}; +#endif +#ifdef USE_BINARY_SENSOR +struct BinarySensor { + binary_sensor::BinarySensor *sensor; + const char *id; + bool updated; +}; +#endif + +class UDPComponent : public PollingComponent { + public: + void setup() override; + void loop() override; + void update() override; + void dump_config() override; + +#ifdef USE_SENSOR + void add_sensor(const char *id, sensor::Sensor *sensor) { + Sensor st{sensor, id, true}; + this->sensors_.push_back(st); + } + void add_remote_sensor(const char *hostname, const char *remote_id, sensor::Sensor *sensor) { + this->add_provider(hostname); + this->remote_sensors_[hostname][remote_id] = sensor; + } +#endif +#ifdef USE_BINARY_SENSOR + void add_binary_sensor(const char *id, binary_sensor::BinarySensor *sensor) { + BinarySensor st{sensor, id, true}; + this->binary_sensors_.push_back(st); + } + + void add_remote_binary_sensor(const char *hostname, const char *remote_id, binary_sensor::BinarySensor *sensor) { + this->add_provider(hostname); + this->remote_binary_sensors_[hostname][remote_id] = sensor; + } +#endif + void add_address(const char *addr) { this->addresses_.emplace_back(addr); } + void set_port(uint16_t port) { this->port_ = port; } + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + + void add_provider(const char *hostname) { + if (this->providers_.count(hostname) == 0) { + Provider provider; + provider.encryption_key = std::vector{}; + provider.last_code[0] = 0; + provider.last_code[1] = 0; + provider.name = hostname; + this->providers_[hostname] = provider; +#ifdef USE_SENSOR + this->remote_sensors_[hostname] = std::map(); +#endif +#ifdef USE_BINARY_SENSOR + this->remote_binary_sensors_[hostname] = std::map(); +#endif + } + } + + void set_encryption_key(std::vector key) { this->encryption_key_ = std::move(key); } + void set_rolling_code_enable(bool enable) { this->rolling_code_enable_ = enable; } + void set_ping_pong_enable(bool enable) { this->ping_pong_enable_ = enable; } + void set_ping_pong_recycle_time(uint32_t recycle_time) { this->ping_pong_recyle_time_ = recycle_time; } + void set_provider_encryption(const char *name, std::vector key) { + this->providers_[name].encryption_key = std::move(key); + } + + protected: + void send_data_(bool all); + void process_(uint8_t *buf, size_t len); + void flush_(); + void add_data_(uint8_t key, const char *id, float data); + void add_data_(uint8_t key, const char *id, uint32_t data); + void increment_code_(); + void add_binary_data_(uint8_t key, const char *id, bool data); + void init_data_(); + + bool updated_{}; + uint16_t port_{18511}; + uint32_t ping_key_{}; + uint32_t rolling_code_[2]{}; + bool rolling_code_enable_{}; + bool ping_pong_enable_{}; + uint32_t ping_pong_recyle_time_{}; + uint32_t last_key_time_{}; + bool resend_ping_key_{}; + bool resend_data_{}; + bool should_send_{}; + const char *name_{}; + bool should_listen_{}; + ESPPreferenceObject pref_; + +#if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) + std::unique_ptr broadcast_socket_ = nullptr; + std::unique_ptr listen_socket_ = nullptr; + std::vector sockaddrs_{}; +#else + std::vector ipaddrs_{}; + WiFiUDP udp_client_{}; +#endif + std::vector encryption_key_{}; + std::vector addresses_{}; + +#ifdef USE_SENSOR + std::vector sensors_{}; + std::map> remote_sensors_{}; +#endif +#ifdef USE_BINARY_SENSOR + std::vector binary_sensors_{}; + std::map> remote_binary_sensors_{}; +#endif + + std::map providers_{}; + std::vector ping_header_{}; + std::vector header_{}; + std::vector data_{}; + std::map ping_keys_{}; + void add_key_(const char *name, uint32_t key); + void send_ping_pong_request_(); + void send_packet_(void *data, size_t len); + void process_ping_request_(const char *name, uint8_t *ptr, size_t len); + + inline bool is_encrypted_() { return !this->encryption_key_.empty(); } +}; + +} // namespace udp +} // namespace esphome diff --git a/tests/components/udp/common.yaml b/tests/components/udp/common.yaml new file mode 100644 index 0000000000..3bdc19ece5 --- /dev/null +++ b/tests/components/udp/common.yaml @@ -0,0 +1,35 @@ +wifi: + ssid: MySSID + password: password1 + +udp: + update_interval: 5s + encryption: "our key goes here" + rolling_code_enable: true + ping_pong_enable: true + binary_sensors: + - binary_sensor_id1 + - id: binary_sensor_id1 + broadcast_id: other_id + sensors: + - sensor_id1 + - id: sensor_id1 + broadcast_id: other_id + providers: + - name: some-device-name + encryption: "their key goes here" + +sensor: + - platform: template + id: sensor_id1 + - platform: udp + provider: some-device-name + id: our_id + remote_id: some_sensor_id + +binary_sensor: + - platform: udp + provider: unencrypted-device + id: other_binary_sensor_id + - platform: template + id: binary_sensor_id1 diff --git a/tests/components/udp/test.bk72xx-ard.yaml b/tests/components/udp/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/udp/test.bk72xx-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/udp/test.esp32-ard.yaml b/tests/components/udp/test.esp32-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/udp/test.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/udp/test.esp32-c3-ard.yaml b/tests/components/udp/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/udp/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/udp/test.esp32-c3-idf.yaml b/tests/components/udp/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/udp/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/udp/test.esp32-idf.yaml b/tests/components/udp/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/udp/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/udp/test.esp8266-ard.yaml b/tests/components/udp/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/udp/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/udp/test.host.yaml b/tests/components/udp/test.host.yaml new file mode 100644 index 0000000000..e735c37e4d --- /dev/null +++ b/tests/components/udp/test.host.yaml @@ -0,0 +1,4 @@ +packages: + common: !include common.yaml + +wifi: !remove diff --git a/tests/components/udp/test.rp2040-ard.yaml b/tests/components/udp/test.rp2040-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/udp/test.rp2040-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From ca2f25e73bc04858d488540373475e600542d415 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 1 Sep 2024 13:20:31 +0200 Subject: [PATCH 0291/1052] update logs for bluetooth proxy (#7382) --- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index f188439d0e..bd1c8b7ea4 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -54,6 +54,9 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p } resp.advertisements.push_back(std::move(adv)); + + ESP_LOGV(TAG, "Proxying raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0], + result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi); } ESP_LOGV(TAG, "Proxying %d packets", count); this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp); @@ -87,6 +90,8 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi 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_)); } int BluetoothProxy::get_bluetooth_connections_free() { From 61223a3cc9df12d07e9c8e288a691ef5cc1b30fd Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 2 Sep 2024 06:45:40 +1000 Subject: [PATCH 0292/1052] [font] Make display an auto-load, not a dependency (#7366) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/display/__init__.py | 9 +++---- esphome/components/font/__init__.py | 33 +++++++++----------------- esphome/components/font/font.cpp | 5 ++-- esphome/components/font/font.h | 15 +++++++++--- esphome/core/defines.h | 1 + tests/components/font/test.host.yaml | 23 ++++++++++++++++++ 6 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 tests/components/font/test.host.yaml diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index c4bb12b75d..32a8b3b090 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -1,15 +1,15 @@ +from esphome import automation, core +from esphome.automation import maybe_simple_id import esphome.codegen as cg import esphome.config_validation as cv -from esphome import core, automation -from esphome.automation import maybe_simple_id from esphome.const import ( CONF_AUTO_CLEAR_ENABLED, + CONF_FROM, CONF_ID, CONF_LAMBDA, - CONF_PAGES, CONF_PAGE_ID, + CONF_PAGES, CONF_ROTATION, - CONF_FROM, CONF_TO, CONF_TRIGGER_ID, ) @@ -195,3 +195,4 @@ async def display_is_displaying_page_to_code(config, condition_id, template_arg, @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(display_ns.using) + cg.add_define("USE_DISPLAY") diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 7e4674ffda..b5ed02e89a 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -1,43 +1,35 @@ +import functools import hashlib import logging - -import functools -from pathlib import Path import os +from pathlib import Path import re + from packaging import version import requests -from esphome import core -from esphome import external_files -import esphome.config_validation as cv +from esphome import core, external_files import esphome.codegen as cg -from esphome.helpers import ( - copy_file_if_changed, - cpp_string_escape, -) +import esphome.config_validation as cv from esphome.const import ( CONF_FAMILY, CONF_FILE, CONF_GLYPHS, CONF_ID, + CONF_PATH, CONF_RAW_DATA_ID, - CONF_TYPE, CONF_REFRESH, CONF_SIZE, - CONF_PATH, - CONF_WEIGHT, + CONF_TYPE, CONF_URL, + CONF_WEIGHT, ) -from esphome.core import ( - CORE, - HexInt, -) +from esphome.core import CORE, HexInt +from esphome.helpers import copy_file_if_changed, cpp_string_escape _LOGGER = logging.getLogger(__name__) DOMAIN = "font" -DEPENDENCIES = ["display"] MULTI_CONF = True CODEOWNERS = ["@esphome/core", "@clydebarrow"] @@ -400,10 +392,7 @@ class EFont: def convert_bitmap_to_pillow_font(filepath): - from PIL import ( - PcfFontFile, - BdfFontFile, - ) + from PIL import BdfFontFile, PcfFontFile local_bitmap_font_file = external_files.compute_local_file_dir( DOMAIN, diff --git a/esphome/components/font/font.cpp b/esphome/components/font/font.cpp index 3b62b8ca66..aeca0f5cc0 100644 --- a/esphome/components/font/font.cpp +++ b/esphome/components/font/font.cpp @@ -1,9 +1,8 @@ #include "font.h" +#include "esphome/core/color.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/color.h" -#include "esphome/components/display/display_buffer.h" namespace esphome { namespace font { @@ -68,6 +67,7 @@ int Font::match_next_glyph(const uint8_t *str, int *match_length) { return -1; return lo; } +#ifdef USE_DISPLAY void Font::measure(const char *str, int *width, int *x_offset, int *baseline, int *height) { *baseline = this->baseline_; *height = this->height_; @@ -164,6 +164,7 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo i += match_length; } } +#endif } // namespace font } // namespace esphome diff --git a/esphome/components/font/font.h b/esphome/components/font/font.h index 57002cf510..5cde694d91 100644 --- a/esphome/components/font/font.h +++ b/esphome/components/font/font.h @@ -1,8 +1,11 @@ #pragma once -#include "esphome/core/datatypes.h" #include "esphome/core/color.h" -#include "esphome/components/display/display_buffer.h" +#include "esphome/core/datatypes.h" +#include "esphome/core/defines.h" +#ifdef USE_DISPLAY +#include "esphome/components/display/display.h" +#endif namespace esphome { namespace font { @@ -38,7 +41,11 @@ class Glyph { const GlyphData *glyph_data_; }; -class Font : public display::BaseFont { +class Font +#ifdef USE_DISPLAY + : public display::BaseFont +#endif +{ public: /** Construct the font with the given glyphs. * @@ -50,9 +57,11 @@ class Font : public display::BaseFont { int match_next_glyph(const uint8_t *str, int *match_length); +#ifdef USE_DISPLAY void print(int x_start, int y_start, display::Display *display, Color color, const char *text, Color background) override; void measure(const char *str, int *width, int *x_offset, int *baseline, int *height) override; +#endif inline int get_baseline() { return this->baseline_; } inline int get_height() { return this->height_; } inline int get_bpp() { return this->bpp_; } diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 52cf7d4dd0..ffd5cc6f1b 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -28,6 +28,7 @@ #define USE_DATETIME_DATETIME #define USE_DATETIME_TIME #define USE_DEEP_SLEEP +#define USE_DISPLAY #define USE_EVENT #define USE_FAN #define USE_GRAPH diff --git a/tests/components/font/test.host.yaml b/tests/components/font/test.host.yaml new file mode 100644 index 0000000000..017328ec83 --- /dev/null +++ b/tests/components/font/test.host.yaml @@ -0,0 +1,23 @@ +font: + - file: "gfonts://Roboto" + id: roboto + size: 20 + glyphs: "0123456789." + extras: + - file: "gfonts://Roboto" + glyphs: ["\u00C4", "\u00C5", "\U000000C7"] + - file: "gfonts://Roboto" + id: roboto_web + size: 20 + - file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" + id: monocraft + size: 20 + - file: + type: web + url: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" + id: monocraft2 + size: 24 + - file: $component_dir/Monocraft.ttf + id: monocraft3 + size: 28 + From 3a7aabb2eb3fe58c2925cd3f5e1f605e15797b1a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:36:18 +1200 Subject: [PATCH 0293/1052] Bump Dockerfile dependencies (#7386) --- docker/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 16f37274c6..4393d5a447 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -34,8 +34,8 @@ RUN \ python3-wheel=0.38.4-2 \ iputils-ping=3:20221126-1 \ git=1:2.39.2-1.1 \ - curl=7.88.1-10+deb12u6 \ - openssh-client=1:9.2p1-2+deb12u2 \ + curl=7.88.1-10+deb12u7 \ + openssh-client=1:9.2p1-2+deb12u3 \ python3-cffi=1.15.1-5 \ libcairo2=1.16.0-7 \ libmagic1=1:5.44-3 \ @@ -49,7 +49,7 @@ RUN \ zlib1g-dev=1:1.2.13.dfsg-1 \ libjpeg-dev=1:2.1.5-2 \ libfreetype-dev=2.12.1+dfsg-5+deb12u3 \ - libssl-dev=3.0.13-1~deb12u1 \ + libssl-dev=3.0.14-1~deb12u1 \ libffi-dev=3.4.4-1 \ libopenjp2-7=2.5.0-2 \ libtiff6=4.5.0-6+deb12u1 \ From 6490fc9c620d602fae2957f31e2756be886354ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Skowro=C5=84ski?= Date: Mon, 2 Sep 2024 03:56:35 +0200 Subject: [PATCH 0294/1052] CH422G support (#7356) --- CODEOWNERS | 1 + esphome/components/ch422g/__init__.py | 67 ++++++++++ esphome/components/ch422g/ch422g.cpp | 122 ++++++++++++++++++ esphome/components/ch422g/ch422g.h | 70 ++++++++++ tests/components/ch422g/common.yaml | 20 +++ tests/components/ch422g/test.esp32-ard.yaml | 6 + .../components/ch422g/test.esp32-c3-ard.yaml | 6 + .../components/ch422g/test.esp32-c3-idf.yaml | 6 + tests/components/ch422g/test.esp32-idf.yaml | 6 + tests/components/ch422g/test.esp8266-ard.yaml | 6 + tests/components/ch422g/test.rp2040-ard.yaml | 6 + 11 files changed, 316 insertions(+) create mode 100644 esphome/components/ch422g/__init__.py create mode 100644 esphome/components/ch422g/ch422g.cpp create mode 100644 esphome/components/ch422g/ch422g.h create mode 100644 tests/components/ch422g/common.yaml create mode 100644 tests/components/ch422g/test.esp32-ard.yaml create mode 100644 tests/components/ch422g/test.esp32-c3-ard.yaml create mode 100644 tests/components/ch422g/test.esp32-c3-idf.yaml create mode 100644 tests/components/ch422g/test.esp32-idf.yaml create mode 100644 tests/components/ch422g/test.esp8266-ard.yaml create mode 100644 tests/components/ch422g/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 807829eafd..ab11086980 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -83,6 +83,7 @@ esphome/components/cap1188/* @mreditor97 esphome/components/captive_portal/* @OttoWinter esphome/components/ccs811/* @habbie esphome/components/cd74hc4067/* @asoehlke +esphome/components/ch422g/* @jesterret esphome/components/climate/* @esphome/core esphome/components/climate_ir/* @glmnet esphome/components/color_temperature/* @jesserockz diff --git a/esphome/components/ch422g/__init__.py b/esphome/components/ch422g/__init__.py new file mode 100644 index 0000000000..cf8b5f65d3 --- /dev/null +++ b/esphome/components/ch422g/__init__.py @@ -0,0 +1,67 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components import i2c +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_INVERTED, + CONF_MODE, + CONF_NUMBER, + CONF_OUTPUT, + CONF_RESTORE_VALUE, +) + +CODEOWNERS = ["@jesterret"] +DEPENDENCIES = ["i2c"] +MULTI_CONF = True +ch422g_ns = cg.esphome_ns.namespace("ch422g") + +CH422GComponent = ch422g_ns.class_("CH422GComponent", cg.Component, i2c.I2CDevice) +CH422GGPIOPin = ch422g_ns.class_( + "CH422GGPIOPin", cg.GPIOPin, cg.Parented.template(CH422GComponent) +) + +CONF_CH422G = "ch422g" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(CH422GComponent), + cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x24)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + +CH422G_PIN_SCHEMA = pins.gpio_base_schema( + CH422GGPIOPin, + cv.int_range(min=0, max=7), + modes=[CONF_INPUT, CONF_OUTPUT], +).extend( + { + cv.Required(CONF_CH422G): cv.use_id(CH422GComponent), + } +) + + +@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA) +async def ch422g_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + parent = await cg.get_variable(config[CONF_CH422G]) + + cg.add(var.set_parent(parent)) + + num = config[CONF_NUMBER] + cg.add(var.set_pin(num)) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/ch422g/ch422g.cpp b/esphome/components/ch422g/ch422g.cpp new file mode 100644 index 0000000000..25038991ed --- /dev/null +++ b/esphome/components/ch422g/ch422g.cpp @@ -0,0 +1,122 @@ +#include "ch422g.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace ch422g { + +const uint8_t CH422G_REG_IN = 0x26; +const uint8_t CH422G_REG_OUT = 0x38; +const uint8_t OUT_REG_DEFAULT_VAL = 0xdf; + +static const char *const TAG = "ch422g"; + +void CH422GComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up CH422G..."); + // Test to see if device exists + if (!this->read_inputs_()) { + ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_); + this->mark_failed(); + return; + } + + // restore defaults over whatever got saved on last boot + if (!this->restore_value_) { + this->write_output_(OUT_REG_DEFAULT_VAL); + } + + ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(), + this->status_has_error()); +} + +void CH422GComponent::loop() { + // Clear all the previously read flags. + this->pin_read_cache_ = 0x00; +} + +void CH422GComponent::dump_config() { + ESP_LOGCONFIG(TAG, "CH422G:"); + LOG_I2C_DEVICE(this) + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with CH422G failed!"); + } +} + +// ch422g doesn't have any flag support (needs docs?) +void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) {} + +bool CH422GComponent::digital_read(uint8_t pin) { + if (this->pin_read_cache_ == 0 || this->pin_read_cache_ & (1 << pin)) { + // Read values on first access or in case it's being read again in the same loop + this->read_inputs_(); + } + + this->pin_read_cache_ |= (1 << pin); + return this->state_mask_ & (1 << pin); +} + +void CH422GComponent::digital_write(uint8_t pin, bool value) { + if (value) { + this->write_output_(this->state_mask_ | (1 << pin)); + } else { + this->write_output_(this->state_mask_ & ~(1 << pin)); + } +} + +bool CH422GComponent::read_inputs_() { + if (this->is_failed()) { + return false; + } + + uint8_t temp = 0; + if ((this->last_error_ = this->read(&temp, 1)) != esphome::i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str()); + return false; + } + + uint8_t output = 0; + if ((this->last_error_ = this->bus_->read(CH422G_REG_IN, &output, 1)) != esphome::i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str()); + return false; + } + + this->state_mask_ = output; + this->status_clear_warning(); + + return true; +} + +bool CH422GComponent::write_output_(uint8_t value) { + const uint8_t temp = 1; + if ((this->last_error_ = this->write(&temp, 1, false)) != esphome::i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("write_output_(): I2C I/O error: %d", (int) this->last_error_).c_str()); + return false; + } + + uint8_t write_mask = value; + if ((this->last_error_ = this->bus_->write(CH422G_REG_OUT, &write_mask, 1)) != esphome::i2c::ERROR_OK) { + this->status_set_warning( + str_sprintf("write_output_(): I2C I/O error: %d for write_mask: %d", (int) this->last_error_, (int) write_mask) + .c_str()); + return false; + } + + this->state_mask_ = value; + this->status_clear_warning(); + return true; +} + +float CH422GComponent::get_setup_priority() const { return setup_priority::IO; } + +// Run our loop() method very early in the loop, so that we cache read values +// before other components call our digital_read() method. +float CH422GComponent::get_loop_priority() const { return 9.0f; } // Just after WIFI + +void CH422GGPIOPin::setup() { pin_mode(flags_); } +void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } +bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } + +void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); } + +} // namespace ch422g +} // namespace esphome diff --git a/esphome/components/ch422g/ch422g.h b/esphome/components/ch422g/ch422g.h new file mode 100644 index 0000000000..781df65437 --- /dev/null +++ b/esphome/components/ch422g/ch422g.h @@ -0,0 +1,70 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace ch422g { + +class CH422GComponent : public Component, public i2c::I2CDevice { + public: + CH422GComponent() = default; + + /// Check i2c availability and setup masks + void setup() override; + /// Poll for input changes periodically + void loop() override; + /// Helper function to read the value of a pin. + bool digital_read(uint8_t pin); + /// Helper function to write the value of a pin. + void digital_write(uint8_t pin, bool value); + /// Helper function to set the pin mode of a pin. + void pin_mode(uint8_t pin, gpio::Flags flags); + + float get_setup_priority() const override; + + float get_loop_priority() const override; + + void dump_config() override; + + void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } + + protected: + bool read_inputs_(); + + bool write_output_(uint8_t value); + + /// The mask to write as output state - 1 means HIGH, 0 means LOW + uint8_t state_mask_{0x00}; + /// Flags to check if read previously during this loop + uint8_t pin_read_cache_ = {0x00}; + /// Storage for last I2C error seen + esphome::i2c::ErrorCode last_error_; + /// Whether we want to override stored values on expander + bool restore_value_{false}; +}; + +/// Helper class to expose a CH422G pin as an internal input GPIO pin. +class CH422GGPIOPin : public GPIOPin { + public: + void setup() override; + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_parent(CH422GComponent *parent) { parent_ = parent; } + void set_pin(uint8_t pin) { pin_ = pin; } + void set_inverted(bool inverted) { inverted_ = inverted; } + void set_flags(gpio::Flags flags) { flags_ = flags; } + + protected: + CH422GComponent *parent_; + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; +}; + +} // namespace ch422g +} // namespace esphome diff --git a/tests/components/ch422g/common.yaml b/tests/components/ch422g/common.yaml new file mode 100644 index 0000000000..02061bda59 --- /dev/null +++ b/tests/components/ch422g/common.yaml @@ -0,0 +1,20 @@ +ch422g: + - id: ch422g_hub + address: 0x24 + +binary_sensor: + - platform: gpio + id: ch422g_input + name: CH422G Binary Sensor + pin: + ch422g: ch422g_hub + number: 1 + mode: INPUT + inverted: true + - platform: gpio + id: ch422g_output + pin: + ch422g: ch422g_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/ch422g/test.esp32-ard.yaml b/tests/components/ch422g/test.esp32-ard.yaml new file mode 100644 index 0000000000..cd3f1bbeef --- /dev/null +++ b/tests/components/ch422g/test.esp32-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ch422g + scl: 16 + sda: 17 + +<<: !include common.yaml diff --git a/tests/components/ch422g/test.esp32-c3-ard.yaml b/tests/components/ch422g/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..cd822cb308 --- /dev/null +++ b/tests/components/ch422g/test.esp32-c3-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ch422g + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/ch422g/test.esp32-c3-idf.yaml b/tests/components/ch422g/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..cd822cb308 --- /dev/null +++ b/tests/components/ch422g/test.esp32-c3-idf.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ch422g + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/ch422g/test.esp32-idf.yaml b/tests/components/ch422g/test.esp32-idf.yaml new file mode 100644 index 0000000000..cd3f1bbeef --- /dev/null +++ b/tests/components/ch422g/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ch422g + scl: 16 + sda: 17 + +<<: !include common.yaml diff --git a/tests/components/ch422g/test.esp8266-ard.yaml b/tests/components/ch422g/test.esp8266-ard.yaml new file mode 100644 index 0000000000..cd822cb308 --- /dev/null +++ b/tests/components/ch422g/test.esp8266-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ch422g + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/ch422g/test.rp2040-ard.yaml b/tests/components/ch422g/test.rp2040-ard.yaml new file mode 100644 index 0000000000..cd822cb308 --- /dev/null +++ b/tests/components/ch422g/test.rp2040-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ch422g + scl: 5 + sda: 4 + +<<: !include common.yaml From fc930327b48989eb31e1cb38f45b5aeafb09c042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Mon, 2 Sep 2024 04:30:13 +0200 Subject: [PATCH 0295/1052] [rpi_dpi_rgb] Add enable_pin and reset_display method to driver (#7383) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/rpi_dpi_rgb/display.py | 6 ++++++ esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp | 18 ++++++++++++++++++ esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h | 3 +++ 3 files changed, 27 insertions(+) diff --git a/esphome/components/rpi_dpi_rgb/display.py b/esphome/components/rpi_dpi_rgb/display.py index 969b9db78e..6cc8d2c27b 100644 --- a/esphome/components/rpi_dpi_rgb/display.py +++ b/esphome/components/rpi_dpi_rgb/display.py @@ -3,6 +3,7 @@ import esphome.config_validation as cv from esphome import pins from esphome.components import display from esphome.const import ( + CONF_ENABLE_PIN, CONF_HSYNC_PIN, CONF_RESET_PIN, CONF_DATA_PINS, @@ -112,6 +113,7 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_PCLK_PIN): pins.internal_gpio_output_pin_schema, cv.Required(CONF_HSYNC_PIN): pins.internal_gpio_output_pin_schema, cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_HSYNC_PULSE_WIDTH, default=10): cv.int_, cv.Optional(CONF_HSYNC_BACK_PORCH, default=10): cv.int_, @@ -164,6 +166,10 @@ async def to_code(config): cg.add(var.add_data_pin(data_pin, index)) index += 1 + if enable_pin := config.get(CONF_ENABLE_PIN): + enable = await cg.gpio_pin_expression(enable_pin) + cg.add(var.set_enable_pin(enable)) + if reset_pin := config.get(CONF_RESET_PIN): reset = await cg.gpio_pin_expression(reset_pin) cg.add(var.set_reset_pin(reset)) diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index 2ffdb3272a..f173a2ec44 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -104,12 +104,30 @@ void RpiDpiRgb::dump_config() { ESP_LOGCONFIG(TAG, " Height: %u", this->height_); ESP_LOGCONFIG(TAG, " Width: %u", this->width_); LOG_PIN(" DE Pin: ", this->de_pin_); + LOG_PIN(" Enable Pin: ", this->enable_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_); size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); for (size_t i = 0; i != data_pin_count; i++) ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str()); } +void RpiDpiRgb::reset_display_() const { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(false); + if (this->enable_pin_ != nullptr) { + this->enable_pin_->setup(); + this->enable_pin_->digital_write(false); + } + delay(1); + this->reset_pin_->digital_write(true); + if (this->enable_pin_ != nullptr) { + delay(11); + this->enable_pin_->digital_write(true); + } + } +} + } // namespace rpi_dpi_rgb } // namespace esphome diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h index 0319b46391..6d9d6d4ae9 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h @@ -36,6 +36,7 @@ class RpiDpiRgb : public display::Display { void set_pclk_pin(InternalGPIOPin *pclk_pin) { this->pclk_pin_ = pclk_pin; } void set_vsync_pin(InternalGPIOPin *vsync_pin) { this->vsync_pin_ = vsync_pin; } void set_hsync_pin(InternalGPIOPin *hsync_pin) { this->hsync_pin_ = hsync_pin; } + void set_enable_pin(GPIOPin *enable_pin) { this->enable_pin_ = enable_pin; } void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } void set_width(uint16_t width) { this->width_ = width; } void set_dimensions(uint16_t width, uint16_t height) { @@ -62,10 +63,12 @@ class RpiDpiRgb : public display::Display { protected: int get_width_internal() override { return this->width_; } int get_height_internal() override { return this->height_; } + void reset_display_() const; InternalGPIOPin *de_pin_{nullptr}; InternalGPIOPin *pclk_pin_{nullptr}; InternalGPIOPin *hsync_pin_{nullptr}; InternalGPIOPin *vsync_pin_{nullptr}; + GPIOPin *enable_pin_{nullptr}; GPIOPin *reset_pin_{nullptr}; InternalGPIOPin *data_pins_[16] = {}; uint16_t hsync_front_porch_ = 8; From 094c867fba5e329af1f6795318dee1936ccf5b35 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Mon, 2 Sep 2024 04:32:34 +0200 Subject: [PATCH 0296/1052] Enable IPv6 when manual IPv4 is enabled (#7381) --- esphome/components/ethernet/ethernet_component.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 962a864a29..fdb6eb2da0 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -472,13 +472,13 @@ void EthernetComponent::start_connect_() { if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) { ESPHL_ERROR_CHECK(err, "DHCPC start error"); } -#if USE_NETWORK_IPV6 - err = esp_netif_create_ip6_linklocal(this->eth_netif_); - if (err != ESP_OK) { - ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed"); - } -#endif /* USE_NETWORK_IPV6 */ } +#if USE_NETWORK_IPV6 + err = esp_netif_create_ip6_linklocal(this->eth_netif_); + if (err != ESP_OK) { + ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed"); + } +#endif /* USE_NETWORK_IPV6 */ this->connect_begin_ = millis(); this->status_set_warning(); From 854bafbd4a86316168717af4c1ebc72b55da48a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:33:58 +1200 Subject: [PATCH 0297/1052] Bump actions/upload-artifact from 4.3.4 to 4.4.0 (#7379) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 937c7aac90..7895e7624a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT - name: Upload digests - uses: actions/upload-artifact@v4.3.4 + uses: actions/upload-artifact@v4.4.0 with: name: digests-${{ steps.sanitize.outputs.name }} path: /tmp/digests From ca8e45cf4c35785ac4417a3349825d26e21ad305 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:11:21 +1200 Subject: [PATCH 0298/1052] [core] Only clean build files with esp-idf (#7388) --- esphome/storage_json.py | 16 ++++++++++++++++ esphome/writer.py | 6 +++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/esphome/storage_json.py b/esphome/storage_json.py index e2e7514904..2d12ee01a0 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -48,6 +48,8 @@ class StorageJSON: firmware_bin_path: str, loaded_integrations: set[str], no_mdns: bool, + framework: str | None = None, + core_platform: str | None = None, ) -> None: # Version of the storage JSON schema assert storage_version is None or isinstance(storage_version, int) @@ -78,6 +80,10 @@ class StorageJSON: self.loaded_integrations = loaded_integrations # Is mDNS disabled self.no_mdns = no_mdns + # The framework used to compile the firmware + self.framework = framework + # The core platform of this firmware. Like "esp32", "rp2040", "host" etc. + self.core_platform = core_platform def as_dict(self): return { @@ -94,6 +100,8 @@ class StorageJSON: "firmware_bin_path": self.firmware_bin_path, "loaded_integrations": sorted(self.loaded_integrations), "no_mdns": self.no_mdns, + "framework": self.framework, + "core_platform": self.core_platform, } def to_json(self): @@ -127,6 +135,8 @@ class StorageJSON: and CONF_DISABLED in esph.config[CONF_MDNS] and esph.config[CONF_MDNS][CONF_DISABLED] is True ), + framework=esph.target_framework, + core_platform=esph.target_platform, ) @staticmethod @@ -147,6 +157,8 @@ class StorageJSON: firmware_bin_path=None, loaded_integrations=set(), no_mdns=False, + framework=None, + core_platform=platform.lower(), ) @staticmethod @@ -168,6 +180,8 @@ class StorageJSON: firmware_bin_path = storage.get("firmware_bin_path") loaded_integrations = set(storage.get("loaded_integrations", [])) no_mdns = storage.get("no_mdns", False) + framework = storage.get("framework") + core_platform = storage.get("core_platform") return StorageJSON( storage_version, name, @@ -182,6 +196,8 @@ class StorageJSON: firmware_bin_path, loaded_integrations, no_mdns, + framework, + core_platform, ) @staticmethod diff --git a/esphome/writer.py b/esphome/writer.py index 57435d3463..79ee72996c 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -9,6 +9,7 @@ from esphome.config import iter_component_configs, iter_components from esphome.const import ( ENV_NOGITIGNORE, HEADER_FILE_EXTENSIONS, + PLATFORM_ESP32, SOURCE_FILE_EXTENSIONS, __version__, ) @@ -107,7 +108,10 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool: if old.build_path != new.build_path: return True if old.loaded_integrations != new.loaded_integrations: - return True + if new.core_platform == PLATFORM_ESP32: + from esphome.components.esp32 import FRAMEWORK_ESP_IDF + + return new.framework == FRAMEWORK_ESP_IDF return False From 91c7c436827ef7c538b8ee1be74dd00a2179f2d6 Mon Sep 17 00:00:00 2001 From: Mathieu Rene Date: Mon, 2 Sep 2024 17:26:10 -0400 Subject: [PATCH 0299/1052] Fix build for esp32h2 using esp-idf 5.3 (#7393) --- esphome/components/debug/debug_esp32.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index cfdfdd2a61..34aea9e26b 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -16,6 +16,8 @@ #include #elif defined(USE_ESP32_VARIANT_ESP32S3) #include +#elif defined(USE_ESP32_VARIANT_ESP32H2) +#include #endif #ifdef USE_ARDUINO #include @@ -61,7 +63,7 @@ std::string DebugComponent::get_reset_reason_() { case RTCWDT_SYS_RESET: reset_reason = "RTC Watch Dog Reset Digital Core"; break; -#if !defined(USE_ESP32_VARIANT_ESP32C6) +#if !defined(USE_ESP32_VARIANT_ESP32C6) && !defined(USE_ESP32_VARIANT_ESP32H2) case INTRUSION_RESET: reset_reason = "Intrusion Reset CPU"; break; From 816b060edcdf5c579b7df0c5a7fecf329c48759d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 29 Aug 2024 05:07:31 +1200 Subject: [PATCH 0300/1052] [datetime] Fix templated args (#7368) --- esphome/components/datetime/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 4fda97c5bc..5429121d56 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -186,7 +186,7 @@ async def datetime_date_set_to_code(config, action_id, template_arg, args): date_config = config[CONF_DATE] if cg.is_template(date_config): - template_ = await cg.templatable(date_config, [], cg.ESPTime) + template_ = await cg.templatable(date_config, args, cg.ESPTime) cg.add(action_var.set_date(template_)) else: date_struct = cg.StructInitializer( @@ -217,7 +217,7 @@ async def datetime_time_set_to_code(config, action_id, template_arg, args): time_config = config[CONF_TIME] if cg.is_template(time_config): - template_ = await cg.templatable(time_config, [], cg.ESPTime) + template_ = await cg.templatable(time_config, args, cg.ESPTime) cg.add(action_var.set_time(template_)) else: time_struct = cg.StructInitializer( @@ -248,7 +248,7 @@ async def datetime_datetime_set_to_code(config, action_id, template_arg, args): datetime_config = config[CONF_DATETIME] if cg.is_template(datetime_config): - template_ = await cg.templatable(datetime_config, [], cg.ESPTime) + template_ = await cg.templatable(datetime_config, args, cg.ESPTime) cg.add(action_var.set_datetime(template_)) else: datetime_struct = cg.StructInitializer( From 04ec6c5677201b47e74049509487e61b13bef246 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Mon, 2 Sep 2024 04:32:34 +0200 Subject: [PATCH 0301/1052] Enable IPv6 when manual IPv4 is enabled (#7381) --- esphome/components/ethernet/ethernet_component.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 962a864a29..fdb6eb2da0 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -472,13 +472,13 @@ void EthernetComponent::start_connect_() { if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) { ESPHL_ERROR_CHECK(err, "DHCPC start error"); } -#if USE_NETWORK_IPV6 - err = esp_netif_create_ip6_linklocal(this->eth_netif_); - if (err != ESP_OK) { - ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed"); - } -#endif /* USE_NETWORK_IPV6 */ } +#if USE_NETWORK_IPV6 + err = esp_netif_create_ip6_linklocal(this->eth_netif_); + if (err != ESP_OK) { + ESPHL_ERROR_CHECK(err, "Enable IPv6 link local failed"); + } +#endif /* USE_NETWORK_IPV6 */ this->connect_begin_ = millis(); this->status_set_warning(); From c9c5ca28d20f68f6412c0997c345217da2f971fd Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 2 Sep 2024 19:11:21 +1200 Subject: [PATCH 0302/1052] [core] Only clean build files with esp-idf (#7388) --- esphome/storage_json.py | 16 ++++++++++++++++ esphome/writer.py | 6 +++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/esphome/storage_json.py b/esphome/storage_json.py index e2e7514904..2d12ee01a0 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -48,6 +48,8 @@ class StorageJSON: firmware_bin_path: str, loaded_integrations: set[str], no_mdns: bool, + framework: str | None = None, + core_platform: str | None = None, ) -> None: # Version of the storage JSON schema assert storage_version is None or isinstance(storage_version, int) @@ -78,6 +80,10 @@ class StorageJSON: self.loaded_integrations = loaded_integrations # Is mDNS disabled self.no_mdns = no_mdns + # The framework used to compile the firmware + self.framework = framework + # The core platform of this firmware. Like "esp32", "rp2040", "host" etc. + self.core_platform = core_platform def as_dict(self): return { @@ -94,6 +100,8 @@ class StorageJSON: "firmware_bin_path": self.firmware_bin_path, "loaded_integrations": sorted(self.loaded_integrations), "no_mdns": self.no_mdns, + "framework": self.framework, + "core_platform": self.core_platform, } def to_json(self): @@ -127,6 +135,8 @@ class StorageJSON: and CONF_DISABLED in esph.config[CONF_MDNS] and esph.config[CONF_MDNS][CONF_DISABLED] is True ), + framework=esph.target_framework, + core_platform=esph.target_platform, ) @staticmethod @@ -147,6 +157,8 @@ class StorageJSON: firmware_bin_path=None, loaded_integrations=set(), no_mdns=False, + framework=None, + core_platform=platform.lower(), ) @staticmethod @@ -168,6 +180,8 @@ class StorageJSON: firmware_bin_path = storage.get("firmware_bin_path") loaded_integrations = set(storage.get("loaded_integrations", [])) no_mdns = storage.get("no_mdns", False) + framework = storage.get("framework") + core_platform = storage.get("core_platform") return StorageJSON( storage_version, name, @@ -182,6 +196,8 @@ class StorageJSON: firmware_bin_path, loaded_integrations, no_mdns, + framework, + core_platform, ) @staticmethod diff --git a/esphome/writer.py b/esphome/writer.py index 57435d3463..79ee72996c 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -9,6 +9,7 @@ from esphome.config import iter_component_configs, iter_components from esphome.const import ( ENV_NOGITIGNORE, HEADER_FILE_EXTENSIONS, + PLATFORM_ESP32, SOURCE_FILE_EXTENSIONS, __version__, ) @@ -107,7 +108,10 @@ def storage_should_clean(old: StorageJSON, new: StorageJSON) -> bool: if old.build_path != new.build_path: return True if old.loaded_integrations != new.loaded_integrations: - return True + if new.core_platform == PLATFORM_ESP32: + from esphome.components.esp32 import FRAMEWORK_ESP_IDF + + return new.framework == FRAMEWORK_ESP_IDF return False From e5e06a12ef22aeec591880ec53de572bad1f8a3b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:57:28 +1200 Subject: [PATCH 0303/1052] Bump version to 2024.8.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index b27949bf29..00f2d6a13b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.1" +__version__ = "2024.8.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 01c50432c91b871a91a403778250d5b21121cd2a Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Tue, 3 Sep 2024 00:16:59 +0200 Subject: [PATCH 0304/1052] Bump mDNS and follow ruff's suggestions (#7308) --- esphome/components/mdns/__init__.py | 12 ++++++------ esphome/idf_component.yml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index fb90986314..dd68fbb93c 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -1,17 +1,17 @@ +import esphome.codegen as cg +from esphome.components.esp32 import add_idf_component +import esphome.config_validation as cv from esphome.const import ( + CONF_DISABLED, CONF_ID, CONF_PORT, CONF_PROTOCOL, - CONF_SERVICES, CONF_SERVICE, + CONF_SERVICES, KEY_CORE, KEY_FRAMEWORK_VERSION, - CONF_DISABLED, ) -import esphome.codegen as cg -import esphome.config_validation as cv from esphome.core import CORE, coroutine_with_priority -from esphome.components.esp32 import add_idf_component CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] @@ -91,7 +91,7 @@ async def to_code(config): add_idf_component( name="mdns", repo="https://github.com/espressif/esp-protocols.git", - ref="mdns-v1.2.5", + ref="mdns-v1.3.2", path="components/mdns", ) diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 5f4701b5a3..c79ba1b0ed 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -7,7 +7,7 @@ dependencies: version: v2.0.9 mdns: git: https://github.com/espressif/esp-protocols.git - version: mdns-v1.2.5 + version: mdns-v1.3.2 path: components/mdns rules: - if: "idf_version >=5.0" From 29f0b504b9b5944d6b95d135d8ef973c57056604 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Tue, 3 Sep 2024 00:28:18 +0200 Subject: [PATCH 0305/1052] Bump rp2040 Arduino platform and framework (#7134) --- esphome/components/rp2040/__init__.py | 15 +++++++-------- platformio.ini | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index f5c3b8bda2..8f1d26f780 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -1,6 +1,5 @@ import logging import os - from string import ascii_letters, digits import esphome.codegen as cg @@ -8,6 +7,7 @@ import esphome.config_validation as cv from esphome.const import ( CONF_BOARD, CONF_FRAMEWORK, + CONF_PLATFORM_VERSION, CONF_SOURCE, CONF_VERSION, KEY_CORE, @@ -15,10 +15,9 @@ from esphome.const import ( KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, PLATFORM_RP2040, - CONF_PLATFORM_VERSION, ) -from esphome.core import CORE, coroutine_with_priority, EsphomeError -from esphome.helpers import mkdir_p, write_file, copy_file_if_changed +from esphome.core import CORE, EsphomeError, coroutine_with_priority +from esphome.helpers import copy_file_if_changed, mkdir_p, write_file from .const import KEY_BOARD, KEY_PIO_FILES, KEY_RP2040, rp2040_ns @@ -81,19 +80,19 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: # The default/recommended arduino framework version # - https://github.com/earlephilhower/arduino-pico/releases # - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico -RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 7, 2) +RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 9, 4) # The platformio/raspberrypi version to use for arduino frameworks # - https://github.com/platformio/platform-raspberrypi/releases # - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi -ARDUINO_PLATFORM_VERSION = cv.Version(1, 12, 0) +ARDUINO_PLATFORM_VERSION = cv.Version(1, 13, 0) def _arduino_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(3, 4, 0), "https://github.com/earlephilhower/arduino-pico"), - "latest": (cv.Version(3, 4, 0), None), + "dev": (cv.Version(3, 9, 4), "https://github.com/earlephilhower/arduino-pico"), + "latest": (cv.Version(3, 9, 4), None), "recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None), } diff --git a/platformio.ini b/platformio.ini index 147159a841..ee18068a29 100644 --- a/platformio.ini +++ b/platformio.ini @@ -168,7 +168,7 @@ board_build.filesystem_size = 0.5m platform = https://github.com/maxgerhardt/platform-raspberrypi.git platform_packages = ; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted - earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.7.2/rp2040-3.7.2.zip + earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.9.4/rp2040-3.9.4.zip framework = arduino lib_deps = From 3b14b0efce1e9be58f5da6364713ff3d7a12d06d Mon Sep 17 00:00:00 2001 From: Dan Greco <21044725+dangreco@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:35:54 -0400 Subject: [PATCH 0306/1052] [gree] Add support for YX1FF remote (#7298) --- esphome/components/gree/climate.py | 3 +- esphome/components/gree/gree.cpp | 57 ++++++++++++++++++++++++++++-- esphome/components/gree/gree.h | 12 +++++-- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/esphome/components/gree/climate.py b/esphome/components/gree/climate.py index c88a428391..75436f2cf5 100644 --- a/esphome/components/gree/climate.py +++ b/esphome/components/gree/climate.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import climate_ir +import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_MODEL CODEOWNERS = ["@orestismers"] @@ -17,6 +17,7 @@ MODELS = { "yaa": Model.GREE_YAA, "yac": Model.GREE_YAC, "yac1fb9": Model.GREE_YAC1FB9, + "yx1ff": Model.GREE_YX1FF, } CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( diff --git a/esphome/components/gree/gree.cpp b/esphome/components/gree/gree.cpp index cce2a8ffee..6d179a947b 100644 --- a/esphome/components/gree/gree.cpp +++ b/esphome/components/gree/gree.cpp @@ -6,7 +6,15 @@ namespace gree { static const char *const TAG = "gree.climate"; -void GreeClimate::set_model(Model model) { this->model_ = model; } +void GreeClimate::set_model(Model model) { + if (model == GREE_YX1FF) { + this->fan_modes_.insert(climate::CLIMATE_FAN_QUIET); // YX1FF 4 speed + this->presets_.insert(climate::CLIMATE_PRESET_NONE); // YX1FF sleep mode + this->presets_.insert(climate::CLIMATE_PRESET_SLEEP); // YX1FF sleep mode + } + + this->model_ = model; +} void GreeClimate::transmit_state() { uint8_t remote_state[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00}; @@ -14,7 +22,7 @@ void GreeClimate::transmit_state() { remote_state[0] = this->fan_speed_() | this->operation_mode_(); remote_state[1] = this->temperature_(); - if (this->model_ == GREE_YAN) { + if (this->model_ == GREE_YAN || this->model_ == GREE_YX1FF) { remote_state[2] = 0x60; remote_state[3] = 0x50; remote_state[4] = this->vertical_swing_(); @@ -36,8 +44,18 @@ void GreeClimate::transmit_state() { } } + if (this->model_ == GREE_YX1FF) { + if (this->fan_speed_() == GREE_FAN_TURBO) { + remote_state[2] |= GREE_FAN_TURBO_BIT; + } + + if (this->preset_() == GREE_PRESET_SLEEP) { + remote_state[0] |= GREE_PRESET_SLEEP_BIT; + } + } + // Calculate the checksum - if (this->model_ == GREE_YAN) { + if (this->model_ == GREE_YAN || this->model_ == GREE_YX1FF) { remote_state[7] = ((remote_state[0] << 4) + (remote_state[1] << 4) + 0xC0); } else { remote_state[7] = @@ -124,6 +142,23 @@ uint8_t GreeClimate::operation_mode_() { } uint8_t GreeClimate::fan_speed_() { + // YX1FF has 4 fan speeds -- we treat low as quiet and turbo as high + if (this->model_ == GREE_YX1FF) { + switch (this->fan_mode.value()) { + case climate::CLIMATE_FAN_QUIET: + return GREE_FAN_1; + case climate::CLIMATE_FAN_LOW: + return GREE_FAN_2; + case climate::CLIMATE_FAN_MEDIUM: + return GREE_FAN_3; + case climate::CLIMATE_FAN_HIGH: + return GREE_FAN_TURBO; + case climate::CLIMATE_FAN_AUTO: + default: + return GREE_FAN_AUTO; + } + } + switch (this->fan_mode.value()) { case climate::CLIMATE_FAN_LOW: return GREE_FAN_1; @@ -161,5 +196,21 @@ uint8_t GreeClimate::temperature_() { return (uint8_t) roundf(clamp(this->target_temperature, GREE_TEMP_MIN, GREE_TEMP_MAX)); } +uint8_t GreeClimate::preset_() { + // YX1FF has sleep preset + if (this->model_ == GREE_YX1FF) { + switch (this->preset.value()) { + case climate::CLIMATE_PRESET_NONE: + return GREE_PRESET_NONE; + case climate::CLIMATE_PRESET_SLEEP: + return GREE_PRESET_SLEEP; + default: + return GREE_PRESET_NONE; + } + } + + return GREE_PRESET_NONE; +} + } // namespace gree } // namespace esphome diff --git a/esphome/components/gree/gree.h b/esphome/components/gree/gree.h index 524a95aebd..6762b41eb0 100644 --- a/esphome/components/gree/gree.h +++ b/esphome/components/gree/gree.h @@ -25,7 +25,6 @@ const uint8_t GREE_FAN_AUTO = 0x00; const uint8_t GREE_FAN_1 = 0x10; const uint8_t GREE_FAN_2 = 0x20; const uint8_t GREE_FAN_3 = 0x30; -const uint8_t GREE_FAN_TURBO = 0x80; // IR Transmission const uint32_t GREE_IR_FREQUENCY = 38000; @@ -70,8 +69,16 @@ const uint8_t GREE_HDIR_MIDDLE = 0x04; const uint8_t GREE_HDIR_MRIGHT = 0x05; const uint8_t GREE_HDIR_RIGHT = 0x06; +// Only available on YX1FF +// Turbo (high) fan mode + sleep preset mode +const uint8_t GREE_FAN_TURBO = 0x80; +const uint8_t GREE_FAN_TURBO_BIT = 0x10; +const uint8_t GREE_PRESET_NONE = 0x00; +const uint8_t GREE_PRESET_SLEEP = 0x01; +const uint8_t GREE_PRESET_SLEEP_BIT = 0x80; + // Model codes -enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC, GREE_YAC1FB9 }; +enum Model { GREE_GENERIC, GREE_YAN, GREE_YAA, GREE_YAC, GREE_YAC1FB9, GREE_YX1FF }; class GreeClimate : public climate_ir::ClimateIR { public: @@ -93,6 +100,7 @@ class GreeClimate : public climate_ir::ClimateIR { uint8_t horizontal_swing_(); uint8_t vertical_swing_(); uint8_t temperature_(); + uint8_t preset_(); Model model_{}; }; From d6eeac06199e562d2d25deaf8bd7cfe1ec90605f Mon Sep 17 00:00:00 2001 From: Tercio Filho Date: Mon, 2 Sep 2024 20:56:19 -0300 Subject: [PATCH 0307/1052] [modbus_controller] Allow duplicate command config (#7311) --- .../components/modbus_controller/__init__.py | 3 +++ esphome/components/modbus_controller/const.py | 1 + .../modbus_controller/modbus_controller.cpp | 22 ++++++++++--------- .../modbus_controller/modbus_controller.h | 8 +++++++ .../modbus_controller/test.esp32-ard.yaml | 1 + .../modbus_controller/test.esp32-idf.yaml | 1 + 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 1d0f406783..8146124c28 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -13,6 +13,7 @@ from esphome.const import ( ) from esphome.cpp_helpers import logging from .const import ( + CONF_ALLOW_DUPLICATE_COMMANDS, CONF_BITMASK, CONF_BYTE_OFFSET, CONF_COMMAND_THROTTLE, @@ -126,6 +127,7 @@ CONFIG_SCHEMA = cv.All( cv.Schema( { cv.GenerateID(): cv.declare_id(ModbusController), + cv.Optional(CONF_ALLOW_DUPLICATE_COMMANDS, default=False): cv.boolean, cv.Optional( CONF_COMMAND_THROTTLE, default="0ms" ): cv.positive_time_period_milliseconds, @@ -253,6 +255,7 @@ async def add_modbus_base_properties( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_allow_duplicate_commands(config[CONF_ALLOW_DUPLICATE_COMMANDS])) cg.add(var.set_command_throttle(config[CONF_COMMAND_THROTTLE])) cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES])) if CONF_SERVER_REGISTERS in config: diff --git a/esphome/components/modbus_controller/const.py b/esphome/components/modbus_controller/const.py index 1f5c39895c..5d9a61dee7 100644 --- a/esphome/components/modbus_controller/const.py +++ b/esphome/components/modbus_controller/const.py @@ -1,3 +1,4 @@ +CONF_ALLOW_DUPLICATE_COMMANDS = "allow_duplicate_commands" CONF_BITMASK = "bitmask" CONF_BYTE_OFFSET = "byte_offset" CONF_COMMAND_THROTTLE = "command_throttle" diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 378e5c06c0..8f48847a4f 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -175,16 +175,18 @@ void ModbusController::on_register_data(ModbusRegisterType register_type, uint16 } void ModbusController::queue_command(const ModbusCommandItem &command) { - // check if this command is already qeued. - // not very effective but the queue is never really large - for (auto &item : command_queue_) { - if (item->is_equal(command)) { - ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u", - static_cast(command.register_type), command.register_address, command.register_count); - // update the payload of the queued command - // replaces a previous command - item->payload = command.payload; - return; + if (!this->allow_duplicate_commands_) { + // check if this command is already qeued. + // not very effective but the queue is never really large + for (auto &item : command_queue_) { + if (item->is_equal(command)) { + ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u", + static_cast(command.register_type), command.register_address, command.register_count); + // update the payload of the queued command + // replaces a previous command + item->payload = command.payload; + return; + } } } command_queue_.push_back(make_unique(command)); diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h index 3bc11da879..e88f4c07f7 100644 --- a/esphome/components/modbus_controller/modbus_controller.h +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -448,6 +448,12 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { /// incoming queue void on_write_register_response(ModbusRegisterType register_type, uint16_t start_address, const std::vector &data); + /// Allow a duplicate command to be sent + void set_allow_duplicate_commands(bool allow_duplicate_commands) { + this->allow_duplicate_commands_ = allow_duplicate_commands; + } + /// get if a duplicate command can be sent + bool get_allow_duplicate_commands() { return this->allow_duplicate_commands_; } /// called by esphome generated code to set the command_throttle period void set_command_throttle(uint16_t command_throttle) { this->command_throttle_ = command_throttle; } /// called by esphome generated code to set the offline_skip_updates @@ -482,6 +488,8 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { std::list> command_queue_; /// modbus response data waiting to get processed std::queue> incoming_queue_; + /// if duplicate commands can be sent + bool allow_duplicate_commands_; /// when was the last send operation uint32_t last_command_timestamp_; /// min time in ms between sending modbus commands diff --git a/tests/components/modbus_controller/test.esp32-ard.yaml b/tests/components/modbus_controller/test.esp32-ard.yaml index 3e022b10ab..b6e38aeb9c 100644 --- a/tests/components/modbus_controller/test.esp32-ard.yaml +++ b/tests/components/modbus_controller/test.esp32-ard.yaml @@ -20,6 +20,7 @@ modbus_controller: - id: modbus_controller1 address: 0x2 modbus_id: mod_bus1 + allow_duplicate_commands: false - id: modbus_controller2 address: 0x2 modbus_id: mod_bus2 diff --git a/tests/components/modbus_controller/test.esp32-idf.yaml b/tests/components/modbus_controller/test.esp32-idf.yaml index c5fe3fd057..d5407ac406 100644 --- a/tests/components/modbus_controller/test.esp32-idf.yaml +++ b/tests/components/modbus_controller/test.esp32-idf.yaml @@ -12,3 +12,4 @@ modbus_controller: - id: modbus_controller1 address: 0x2 modbus_id: mod_bus1 + allow_duplicate_commands: true From f8ec5242c98649cb590e24667dd3791a5f6b6df1 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Mon, 2 Sep 2024 20:47:54 -0400 Subject: [PATCH 0308/1052] Better support for task blocking ring buffer reads and writes (#7390) --- esphome/core/ring_buffer.cpp | 15 ++++++++-- esphome/core/ring_buffer.h | 56 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index 9bd3d9d853..d8ca831de0 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -20,13 +20,20 @@ std::unique_ptr RingBuffer::create(size_t len) { return nullptr; } - rb->handle_ = xStreamBufferCreateStatic(len + 1, 0, rb->storage_, &rb->structure_); + rb->handle_ = xStreamBufferCreateStatic(len + 1, 1, rb->storage_, &rb->structure_); ESP_LOGD(TAG, "Created ring buffer with size %u", len); return rb; } size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { - return xStreamBufferReceive(this->handle_, data, len, ticks_to_wait); + if (ticks_to_wait > 0) + xStreamBufferSetTriggerLevel(this->handle_, len); + + size_t bytes_read = xStreamBufferReceive(this->handle_, data, len, ticks_to_wait); + + xStreamBufferSetTriggerLevel(this->handle_, 1); + + return bytes_read; } size_t RingBuffer::write(void *data, size_t len) { @@ -39,6 +46,10 @@ size_t RingBuffer::write(void *data, size_t len) { return xStreamBufferSend(this->handle_, data, len, 0); } +size_t RingBuffer::write_without_replacement(void *data, size_t len, TickType_t ticks_to_wait) { + return xStreamBufferSend(this->handle_, data, len, ticks_to_wait); +} + size_t RingBuffer::available() const { return xStreamBufferBytesAvailable(this->handle_); } size_t RingBuffer::free() const { return xStreamBufferSpacesAvailable(this->handle_); } diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h index e602068844..97ffefcefa 100644 --- a/esphome/core/ring_buffer.h +++ b/esphome/core/ring_buffer.h @@ -12,13 +12,69 @@ namespace esphome { class RingBuffer { public: + /** + * @brief Reads from the ring buffer, waiting up to a specified number of ticks if necessary. + * + * Available bytes are read into the provided data pointer. If not enough bytes are available, + * the function will wait up to `ticks_to_wait` FreeRTOS ticks before reading what is available. + * + * @param data Pointer to copy read data into + * @param len Number of bytes to read + * @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0) + * @return Number of bytes read + */ size_t read(void *data, size_t len, TickType_t ticks_to_wait = 0); + /** + * @brief Writes to the ring buffer, overwriting oldest data if necessary. + * + * The provided data is written to the ring buffer. If not enough space is available, + * the function will overwrite the oldest data in the ring buffer. + * + * @param data Pointer to data for writing + * @param len Number of bytes to write + * @return Number of bytes written + */ size_t write(void *data, size_t len); + /** + * @brief Writes to the ring buffer without overwriting oldest data. + * + * The provided data is written to the ring buffer. If not enough space is available, + * the function will wait up to `ticks_to_wait` FreeRTOS ticks before writing as much as possible. + * + * @param data Pointer to data for writing + * @param len Number of bytes to write + * @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0) + * @return Number of bytes written + */ + size_t write_without_replacement(void *data, size_t len, TickType_t ticks_to_wait = 0); + + /** + * @brief Returns the number of available bytes in the ring buffer. + * + * This function provides the number of bytes that can be read from the ring buffer + * without blocking the calling FreeRTOS task. + * + * @return Number of available bytes + */ size_t available() const; + + /** + * @brief Returns the number of free bytes in the ring buffer. + * + * This function provides the number of bytes that can be written to the ring buffer + * without overwriting data or blocking the calling FreeRTOS task. + * + * @return Number of free bytes + */ size_t free() const; + /** + * @brief Resets the ring buffer, discarding all stored data. + * + * @return pdPASS if successful, pdFAIL otherwise + */ BaseType_t reset(); static std::unique_ptr create(size_t len); From 39b2f30b16d263864a9d5fb8444d4814d8658cc4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 2 Sep 2024 10:36:18 +1200 Subject: [PATCH 0309/1052] Bump Dockerfile dependencies (#7386) --- docker/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 16f37274c6..4393d5a447 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -34,8 +34,8 @@ RUN \ python3-wheel=0.38.4-2 \ iputils-ping=3:20221126-1 \ git=1:2.39.2-1.1 \ - curl=7.88.1-10+deb12u6 \ - openssh-client=1:9.2p1-2+deb12u2 \ + curl=7.88.1-10+deb12u7 \ + openssh-client=1:9.2p1-2+deb12u3 \ python3-cffi=1.15.1-5 \ libcairo2=1.16.0-7 \ libmagic1=1:5.44-3 \ @@ -49,7 +49,7 @@ RUN \ zlib1g-dev=1:1.2.13.dfsg-1 \ libjpeg-dev=1:2.1.5-2 \ libfreetype-dev=2.12.1+dfsg-5+deb12u3 \ - libssl-dev=3.0.13-1~deb12u1 \ + libssl-dev=3.0.14-1~deb12u1 \ libffi-dev=3.4.4-1 \ libopenjp2-7=2.5.0-2 \ libtiff6=4.5.0-6+deb12u1 \ From cb4bede6d8790937e287cd69649c81871b1ac6b4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:06:54 +1200 Subject: [PATCH 0310/1052] Bump version to 2024.8.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 00f2d6a13b..05be1877b3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.8.2" +__version__ = "2024.8.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From c6e64a9ed344dc8d34071fd3cfaefa9cd47d1f92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 19:22:56 +1200 Subject: [PATCH 0311/1052] Bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.0 (#7395) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7895e7624a..9e932a3dfc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.0 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From 10ccc5f12584c498c179974877a6b15f327066d7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:55:41 +1200 Subject: [PATCH 0312/1052] [api] Remove id from ``MediaPlayerSupportedFormat`` (#7406) --- esphome/components/api/api.proto | 1 - esphome/components/api/api_pb2_service.cpp | 19 ------------------- esphome/components/api/api_pb2_service.h | 4 ---- 3 files changed, 24 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 84183357dc..ad6fc79cf3 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1112,7 +1112,6 @@ enum MediaPlayerFormatPurpose { MEDIA_PLAYER_FORMAT_PURPOSE_ANNOUNCEMENT = 1; } message MediaPlayerSupportedFormat { - option (id) = 119; option (ifdef) = "USE_MEDIA_PLAYER"; string format = 1; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 16c0e5654f..269a755e9e 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -311,14 +311,6 @@ bool APIServerConnectionBase::send_list_entities_button_response(const ListEntit #ifdef USE_BUTTON #endif #ifdef USE_MEDIA_PLAYER -bool APIServerConnectionBase::send_media_player_supported_format(const MediaPlayerSupportedFormat &msg) { -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "send_media_player_supported_format: %s", msg.dump().c_str()); -#endif - return this->send_message_(msg, 119); -} -#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()); @@ -1143,17 +1135,6 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_update_command_request: %s", msg.dump().c_str()); #endif this->on_update_command_request(msg); -#endif - break; - } - case 119: { -#ifdef USE_MEDIA_PLAYER - MediaPlayerSupportedFormat msg; - msg.decode(msg_data, msg_size); -#ifdef HAS_PROTO_MESSAGE_DUMP - ESP_LOGVV(TAG, "on_media_player_supported_format: %s", msg.dump().c_str()); -#endif - this->on_media_player_supported_format(msg); #endif break; } diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 83b5e3a444..83bfc2ed98 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -145,10 +145,6 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_BUTTON virtual void on_button_command_request(const ButtonCommandRequest &value){}; #endif -#ifdef USE_MEDIA_PLAYER - bool send_media_player_supported_format(const MediaPlayerSupportedFormat &msg); - virtual void on_media_player_supported_format(const MediaPlayerSupportedFormat &value){}; -#endif #ifdef USE_MEDIA_PLAYER bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg); #endif From 1a71cc304740f7823ca77eb3bbab7128f7dd612a Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Wed, 4 Sep 2024 04:02:33 +0200 Subject: [PATCH 0313/1052] Drop max BLE client connections limitation (#7088) --- esphome/components/ble_client/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/esphome/components/ble_client/__init__.py b/esphome/components/ble_client/__init__.py index 6bf4ff739e..bc7d517695 100644 --- a/esphome/components/ble_client/__init__.py +++ b/esphome/components/ble_client/__init__.py @@ -65,9 +65,7 @@ CONF_ON_PASSKEY_NOTIFICATION = "on_passkey_notification" CONF_ON_NUMERIC_COMPARISON_REQUEST = "on_numeric_comparison_request" CONF_AUTO_CONNECT = "auto_connect" -# Espressif platformio framework is built with MAX_BLE_CONN to 3, so -# enforce this in yaml checks. -MULTI_CONF = 3 +MULTI_CONF = True CONFIG_SCHEMA = ( cv.Schema( From 188faa6530cd7d2a2f6a5a24eca6db1fcc9943f3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 4 Sep 2024 04:38:47 +0100 Subject: [PATCH 0314/1052] [bl0942] loop and overflow cleanup (#7358) --- esphome/components/bl0942/bl0942.cpp | 32 ++++++++++++++++++++++------ esphome/components/bl0942/bl0942.h | 2 ++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp index 606d3629da..c70b5f1775 100644 --- a/esphome/components/bl0942/bl0942.cpp +++ b/esphome/components/bl0942/bl0942.cpp @@ -41,20 +41,33 @@ static const uint32_t BL0942_REG_MODE_DEFAULT = static const uint32_t BL0942_REG_SOFT_RESET_MAGIC = 0x5a5a5a; static const uint32_t BL0942_REG_USR_WRPROT_MAGIC = 0x55; +// 23-byte packet, 11 bits per byte, 2400 baud: about 105ms +static const uint32_t PKT_TIMEOUT_MS = 200; + void BL0942::loop() { DataPacket buffer; - if (!this->available()) { + int avail = this->available(); + + if (!avail) { return; } + if (avail < sizeof(buffer)) { + if (!this->rx_start_) { + this->rx_start_ = millis(); + } else if (millis() > this->rx_start_ + PKT_TIMEOUT_MS) { + ESP_LOGW(TAG, "Junk on wire. Throwing away partial message (%d bytes)", avail); + this->read_array((uint8_t *) &buffer, avail); + this->rx_start_ = 0; + } + return; + } + if (this->read_array((uint8_t *) &buffer, sizeof(buffer))) { if (this->validate_checksum_(&buffer)) { this->received_package_(&buffer); } - } else { - ESP_LOGW(TAG, "Junk on wire. Throwing away partial message"); - while (read() >= 0) - ; } + this->rx_start_ = 0; } bool BL0942::validate_checksum_(DataPacket *data) { @@ -133,10 +146,17 @@ void BL0942::received_package_(DataPacket *data) { return; } + // cf_cnt is only 24 bits, so track overflows + uint32_t cf_cnt = (uint24_t) data->cf_cnt; + cf_cnt |= this->prev_cf_cnt_ & 0xff000000; + if (cf_cnt < this->prev_cf_cnt_) { + cf_cnt += 0x1000000; + } + this->prev_cf_cnt_ = cf_cnt; + float v_rms = (uint24_t) data->v_rms / voltage_reference_; float i_rms = (uint24_t) data->i_rms / current_reference_; float watt = (int24_t) data->watt / power_reference_; - uint32_t cf_cnt = (uint24_t) data->cf_cnt; float total_energy_consumption = cf_cnt / energy_reference_; float frequency = 1000000.0f / data->frequency; diff --git a/esphome/components/bl0942/bl0942.h b/esphome/components/bl0942/bl0942.h index 52347c1bc3..a5e48bdf1d 100644 --- a/esphome/components/bl0942/bl0942.h +++ b/esphome/components/bl0942/bl0942.h @@ -67,6 +67,8 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { float energy_reference_ = BL0942_EREF; uint8_t address_ = 0; LineFrequency line_freq_ = LINE_FREQUENCY_50HZ; + uint32_t rx_start_ = 0; + uint32_t prev_cf_cnt_ = 0; bool validate_checksum_(DataPacket *data); int read_reg_(uint8_t reg); From a96de54d46f54a25a15eb889abeea4e53bb3d18f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:45:40 +1200 Subject: [PATCH 0315/1052] Bump peter-evans/create-pull-request from 6.1.0 to 7.0.0 (#7405) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 7677425236..e834ff3793 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -36,7 +36,7 @@ jobs: python ./script/sync-device_class.py - name: Commit changes - uses: peter-evans/create-pull-request@v6.1.0 + uses: peter-evans/create-pull-request@v7.0.0 with: commit-message: "Synchronise Device Classes from Home Assistant" committer: esphomebot From a7fd3b34aae0b9465a6bea66c00b2746d8cfea12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:47:59 +1200 Subject: [PATCH 0316/1052] Bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.1 (#7404) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9e932a3dfc..522de63360 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.10.0 + uses: pypa/gh-action-pypi-publish@v1.10.1 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From e882cea47e76c50db674baf8dae0fa8744aabc4c Mon Sep 17 00:00:00 2001 From: Jeff Cooper Date: Tue, 3 Sep 2024 23:48:13 -0400 Subject: [PATCH 0317/1052] Voice assist improvement - configurable conversation_id timeout (#7385) --- esphome/components/voice_assistant/__init__.py | 6 ++++++ esphome/components/voice_assistant/voice_assistant.cpp | 8 +++++++- esphome/components/voice_assistant/voice_assistant.h | 3 +++ tests/components/voice_assistant/test.esp32-ard.yaml | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/esphome/components/voice_assistant/__init__.py b/esphome/components/voice_assistant/__init__.py index 031edbf27a..a4fb572208 100644 --- a/esphome/components/voice_assistant/__init__.py +++ b/esphome/components/voice_assistant/__init__.py @@ -43,6 +43,8 @@ CONF_VOLUME_MULTIPLIER = "volume_multiplier" CONF_WAKE_WORD = "wake_word" +CONF_CONVERSATION_TIMEOUT = "conversation_timeout" + CONF_ON_TIMER_STARTED = "on_timer_started" CONF_ON_TIMER_UPDATED = "on_timer_updated" CONF_ON_TIMER_CANCELLED = "on_timer_cancelled" @@ -100,6 +102,9 @@ CONFIG_SCHEMA = cv.All( cv.float_with_unit("decibel full scale", "(dBFS|dbfs|DBFS)"), cv.int_range(0, 31), ), + cv.Optional( + CONF_CONVERSATION_TIMEOUT, default="300s" + ): cv.positive_time_period_milliseconds, cv.Optional(CONF_VOLUME_MULTIPLIER, default=1.0): cv.float_range( min=0.0, min_included=False ), @@ -182,6 +187,7 @@ async def to_code(config): cg.add(var.set_noise_suppression_level(config[CONF_NOISE_SUPPRESSION_LEVEL])) cg.add(var.set_auto_gain(config[CONF_AUTO_GAIN])) cg.add(var.set_volume_multiplier(config[CONF_VOLUME_MULTIPLIER])) + cg.add(var.set_conversation_timeout(config[CONF_CONVERSATION_TIMEOUT])) if CONF_ON_LISTENING in config: await automation.build_automation( diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index e4f388db68..43c7428858 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -171,6 +171,11 @@ void VoiceAssistant::deallocate_buffers_() { #endif } +void VoiceAssistant::reset_conversation_id() { + this->conversation_id_ = ""; + ESP_LOGD(TAG, "reset conversation ID"); +} + int VoiceAssistant::read_microphone_() { size_t bytes_read = 0; if (this->mic_->is_running()) { // Read audio into input buffer @@ -299,7 +304,8 @@ void VoiceAssistant::loop() { break; } this->set_state_(State::STARTING_PIPELINE); - this->set_timeout("reset-conversation_id", 5 * 60 * 1000, [this]() { this->conversation_id_ = ""; }); + this->set_timeout("reset-conversation_id", this->conversation_timeout_, + [this]() { this->reset_conversation_id(); }); break; } case State::STARTING_PIPELINE: { diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index a160972e22..88cb0dd413 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -147,6 +147,8 @@ class VoiceAssistant : public Component { } void set_auto_gain(uint8_t auto_gain) { this->auto_gain_ = auto_gain; } void set_volume_multiplier(float volume_multiplier) { this->volume_multiplier_ = volume_multiplier; } + void set_conversation_timeout(uint32_t conversation_timeout) { this->conversation_timeout_ = conversation_timeout; } + void reset_conversation_id(); Trigger<> *get_intent_end_trigger() const { return this->intent_end_trigger_; } Trigger<> *get_intent_start_trigger() const { return this->intent_start_trigger_; } @@ -262,6 +264,7 @@ class VoiceAssistant : public Component { uint8_t noise_suppression_level_; uint8_t auto_gain_; float volume_multiplier_; + uint32_t conversation_timeout_; uint8_t *send_buffer_; int16_t *input_buffer_; diff --git a/tests/components/voice_assistant/test.esp32-ard.yaml b/tests/components/voice_assistant/test.esp32-ard.yaml index 2e0209311d..7f6fd85303 100644 --- a/tests/components/voice_assistant/test.esp32-ard.yaml +++ b/tests/components/voice_assistant/test.esp32-ard.yaml @@ -33,6 +33,7 @@ speaker: voice_assistant: microphone: mic_id_external speaker: speaker_id + conversation_timeout: 60s on_listening: - logger.log: "Voice assistant microphone listening" on_start: From 71a7f6383f66d7e0730a7854d1f5739406310395 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 5 Sep 2024 01:08:39 +0100 Subject: [PATCH 0318/1052] Support BL0942 calibration (#7299) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 2 +- esphome/components/bl0942/__init__.py | 2 +- esphome/components/bl0942/bl0942.cpp | 20 +++++- esphome/components/bl0942/bl0942.h | 71 ++++++++++++++++++++ esphome/components/bl0942/sensor.py | 17 +++++ tests/components/bl0942/test.bk72xx-ard.yaml | 4 ++ tests/components/bl0942/test.rp2040-ard.yaml | 2 + 7 files changed, 115 insertions(+), 3 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index ab11086980..8c706fa2d6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -61,7 +61,7 @@ esphome/components/bk72xx/* @kuba2k2 esphome/components/bl0906/* @athom-tech @jesserockz @tarontop esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @tobias- -esphome/components/bl0942/* @dbuezas +esphome/components/bl0942/* @dbuezas @dwmw2 esphome/components/ble_client/* @buxtronix @clydebarrow esphome/components/bluetooth_proxy/* @jesserockz esphome/components/bme280_base/* @esphome/core diff --git a/esphome/components/bl0942/__init__.py b/esphome/components/bl0942/__init__.py index 8ef7857b7b..38b68d84b5 100644 --- a/esphome/components/bl0942/__init__.py +++ b/esphome/components/bl0942/__init__.py @@ -1 +1 @@ -CODEOWNERS = ["@dbuezas"] +CODEOWNERS = ["@dbuezas", "@dwmw2"] diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp index c70b5f1775..af56e77de6 100644 --- a/esphome/components/bl0942/bl0942.cpp +++ b/esphome/components/bl0942/bl0942.cpp @@ -122,6 +122,20 @@ void BL0942::update() { } void BL0942::setup() { + // If either current or voltage references are set explicitly by the user, + // calculate the power reference from it unless that is also explicitly set. + if ((this->current_reference_set_ || this->voltage_reference_set_) && !this->power_reference_set_) { + this->power_reference_ = (this->voltage_reference_ * this->current_reference_ * 3537.0 / 305978.0) / 73989.0; + this->power_reference_set_ = true; + } + + // Similarly for energy reference, if the power reference was set by the user + // either implicitly or explicitly. + if (this->power_reference_set_ && !this->energy_reference_set_) { + this->energy_reference_ = this->power_reference_ * 3600000 / 419430.4; + this->energy_reference_set_ = true; + } + this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC); this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC); @@ -184,11 +198,15 @@ void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexit ESP_LOGCONFIG(TAG, "BL0942:"); 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_); LOG_SENSOR("", "Voltage", this->voltage_sensor_); LOG_SENSOR("", "Current", this->current_sensor_); LOG_SENSOR("", "Power", this->power_sensor_); LOG_SENSOR("", "Energy", this->energy_sensor_); - LOG_SENSOR("", "frequency", this->frequency_sensor_); + LOG_SENSOR("", "Frequency", this->frequency_sensor_); } } // namespace bl0942 diff --git a/esphome/components/bl0942/bl0942.h b/esphome/components/bl0942/bl0942.h index a5e48bdf1d..1dc930183f 100644 --- a/esphome/components/bl0942/bl0942.h +++ b/esphome/components/bl0942/bl0942.h @@ -8,6 +8,57 @@ namespace esphome { namespace bl0942 { +// The BL0942 IC is "calibration-free", which means that it doesn't care +// at all about calibration, and that's left to software. It measures a +// voltage differential on its IP/IN pins which linearly proportional to +// the current flow, and another on its VP pin which is proportional to +// the line voltage. It never knows the actual calibration; the values +// it reports are solely in terms of those inputs. +// +// The datasheet refers to the input voltages as I(A) and V(V), both +// in millivolts. It measures them against a reference voltage Vref, +// which is typically 1.218V (but that absolute value is meaningless +// without the actual calibration anyway). +// +// The reported I_RMS value is 305978 I(A)/Vref, and the reported V_RMS +// value is 73989 V(V)/Vref. So we can calibrate those by applying a +// simple meter with a resistive load. +// +// The chip also measures the phase difference between voltage and +// current, and uses it to calculate the power factor (cos φ). It +// reports the WATT value of 3537 * I_RMS * V_RMS * cos φ). +// +// It also integrates total energy based on the WATT value. The time for +// one CF_CNT pulse is 1638.4*256 / WATT. +// +// So... how do we calibrate that? +// +// Using a simple resistive load and an external meter, we can measure +// the true voltage and current for a given V_RMS and I_RMS reading, +// to calculate BL0942_UREF and BL0942_IREF. Those are in units of +// "305978 counts per amp" or "73989 counts per volt" respectively. +// +// We can derive BL0942_PREF from those. Let's eliminate the weird +// factors and express the calibration in plain counts per volt/amp: +// UREF1 = UREF/73989, IREF1 = IREF/305978. +// +// Next... the true power in Watts is V * I * cos φ, so that's equal +// to WATT/3537 * IREF1 * UREF1. Which means +// BL0942_PREF = BL0942_UREF * BL0942_IREF * 3537 / 305978 / 73989. +// +// Finally the accumulated energy. The period of a CF_CNT count is +// 1638.4*256 / WATT seconds, or 419230.4 / WATT seconds. Which means +// the energy represented by a CN_CNT pulse is 419230.4 WATT-seconds. +// Factoring in the calibration, that's 419230.4 / BL0942_PREF actual +// Watt-seconds (or Joules, as the physicists like to call them). +// +// But we're not being physicists today; we we're being engineers, so +// we want to convert to kWh instead. Which we do by dividing by 1000 +// and then by 3600, so the energy in kWh is +// CF_CNT * 419230.4 / BL0942_PREF / 3600000 +// +// Which makes BL0952_EREF = BL0942_PREF * 3600000 / 419430.4 + static const float BL0942_PREF = 596; // taken from tasmota static const float BL0942_UREF = 15873.35944299; // should be 73989/1.218 static const float BL0942_IREF = 251213.46469622; // 305978/1.218 @@ -42,6 +93,22 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; } void set_line_freq(LineFrequency freq) { this->line_freq_ = freq; } void set_address(uint8_t address) { this->address_ = address; } + void set_current_reference(float current_ref) { + this->current_reference_ = current_ref; + this->current_reference_set_ = true; + } + void set_energy_reference(float energy_ref) { + this->energy_reference_ = energy_ref; + this->energy_reference_set_ = true; + } + void set_power_reference(float power_ref) { + this->power_reference_ = power_ref; + this->power_reference_set_ = true; + } + void set_voltage_reference(float voltage_ref) { + this->voltage_reference_ = voltage_ref; + this->voltage_reference_set_ = true; + } void loop() override; void update() override; @@ -59,12 +126,16 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { // Divide by this to turn into Watt float power_reference_ = BL0942_PREF; + bool power_reference_set_ = false; // Divide by this to turn into Volt float voltage_reference_ = BL0942_UREF; + bool voltage_reference_set_ = false; // Divide by this to turn into Ampere float current_reference_ = BL0942_IREF; + bool current_reference_set_ = false; // Divide by this to turn into kWh float energy_reference_ = BL0942_EREF; + bool energy_reference_set_ = false; uint8_t address_ = 0; LineFrequency line_freq_ = LINE_FREQUENCY_50HZ; uint32_t rx_start_ = 0; diff --git a/esphome/components/bl0942/sensor.py b/esphome/components/bl0942/sensor.py index c47da45b8c..3574443636 100644 --- a/esphome/components/bl0942/sensor.py +++ b/esphome/components/bl0942/sensor.py @@ -24,6 +24,11 @@ from esphome.const import ( UNIT_WATT, ) +CONF_CURRENT_REFERENCE = "current_reference" +CONF_ENERGY_REFERENCE = "energy_reference" +CONF_POWER_REFERENCE = "power_reference" +CONF_VOLTAGE_REFERENCE = "voltage_reference" + DEPENDENCIES = ["uart"] bl0942_ns = cg.esphome_ns.namespace("bl0942") @@ -77,6 +82,10 @@ CONFIG_SCHEMA = ( ), ), cv.Optional(CONF_ADDRESS, default=0): cv.int_range(min=0, max=3), + cv.Optional(CONF_CURRENT_REFERENCE): cv.float_, + cv.Optional(CONF_ENERGY_REFERENCE): cv.float_, + cv.Optional(CONF_POWER_REFERENCE): cv.float_, + cv.Optional(CONF_VOLTAGE_REFERENCE): cv.float_, } ) .extend(cv.polling_component_schema("60s")) @@ -106,3 +115,11 @@ async def to_code(config): cg.add(var.set_frequency_sensor(sens)) cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) cg.add(var.set_address(config[CONF_ADDRESS])) + if (current_reference := config.get(CONF_CURRENT_REFERENCE, None)) is not None: + cg.add(var.set_current_reference(current_reference)) + if (voltage_reference := config.get(CONF_VOLTAGE_REFERENCE, None)) is not None: + cg.add(var.set_voltage_reference(voltage_reference)) + if (power_reference := config.get(CONF_POWER_REFERENCE, None)) is not None: + cg.add(var.set_power_reference(power_reference)) + if (energy_reference := config.get(CONF_ENERGY_REFERENCE, None)) is not None: + cg.add(var.set_energy_reference(energy_reference)) diff --git a/tests/components/bl0942/test.bk72xx-ard.yaml b/tests/components/bl0942/test.bk72xx-ard.yaml index 4ed3eb391d..12772f9375 100644 --- a/tests/components/bl0942/test.bk72xx-ard.yaml +++ b/tests/components/bl0942/test.bk72xx-ard.yaml @@ -20,3 +20,7 @@ sensor: name: BL0942 Energy frequency: name: BL0942 Frequency + voltage_reference: 15968 + current_reference: 124180 + power_reference: 309.1 + energy_reference: 2653 diff --git a/tests/components/bl0942/test.rp2040-ard.yaml b/tests/components/bl0942/test.rp2040-ard.yaml index 8d16efed4f..d07e0c4402 100644 --- a/tests/components/bl0942/test.rp2040-ard.yaml +++ b/tests/components/bl0942/test.rp2040-ard.yaml @@ -18,3 +18,5 @@ sensor: name: BL0942 Energy frequency: name: BL0942 Frequency + voltage_reference: 15968 + current_reference: 124180 From dc4e60526cd63525363cf36be8625fd52840fea8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 5 Sep 2024 12:49:01 +1200 Subject: [PATCH 0319/1052] [micro_wake_word] Remove duplicated download code (#7401) --- .../components/micro_wake_word/__init__.py | 27 ++----------------- esphome/external_files.py | 8 +++--- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index cd45f75b01..a8aa590951 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -4,8 +4,6 @@ import logging from pathlib import Path from urllib.parse import urljoin -import requests - from esphome import automation, external_files, git from esphome.automation import register_action, register_condition import esphome.codegen as cg @@ -26,7 +24,6 @@ from esphome.const import ( CONF_USERNAME, TYPE_GIT, TYPE_LOCAL, - __version__, ) from esphome.core import CORE, HexInt @@ -179,26 +176,6 @@ def _convert_manifest_v1_to_v2(v1_manifest): return v2_manifest -def _download_file(url: str, path: Path) -> bytes: - if not external_files.has_remote_file_changed(url, path): - _LOGGER.debug("Remote file has not changed, skipping download") - return path.read_bytes() - - try: - req = requests.get( - url, - timeout=external_files.NETWORK_TIMEOUT, - headers={"User-agent": f"ESPHome/{__version__} (https://esphome.io)"}, - ) - req.raise_for_status() - except requests.exceptions.RequestException as e: - raise cv.Invalid(f"Could not download file from {url}: {e}") from e - - path.parent.mkdir(parents=True, exist_ok=True) - path.write_bytes(req.content) - return req.content - - def _validate_manifest_version(manifest_data): if manifest_version := manifest_data.get(KEY_VERSION): if manifest_version == 1: @@ -223,7 +200,7 @@ def _process_http_source(config): json_path = path / "manifest.json" - json_contents = _download_file(url, json_path) + json_contents = external_files.download_content(url, json_path) manifest_data = json.loads(json_contents) if not isinstance(manifest_data, dict): @@ -234,7 +211,7 @@ def _process_http_source(config): model_path = path / model - _download_file(str(model_url), model_path) + external_files.download_content(str(model_url), model_path) return config diff --git a/esphome/external_files.py b/esphome/external_files.py index baf62286e4..057ff52f3f 100644 --- a/esphome/external_files.py +++ b/esphome/external_files.py @@ -80,10 +80,10 @@ def compute_local_file_dir(domain: str) -> Path: return base_directory -def download_content(url: str, path: Path, timeout=NETWORK_TIMEOUT) -> None: +def download_content(url: str, path: Path, timeout=NETWORK_TIMEOUT) -> bytes: if not has_remote_file_changed(url, path): _LOGGER.debug("Remote file has not changed %s", url) - return + return path.read_bytes() _LOGGER.debug( "Remote file has changed, downloading from %s to %s", @@ -102,4 +102,6 @@ def download_content(url: str, path: Path, timeout=NETWORK_TIMEOUT) -> None: raise cv.Invalid(f"Could not download from {url}: {e}") path.parent.mkdir(parents=True, exist_ok=True) - path.write_bytes(req.content) + data = req.content + path.write_bytes(data) + return data From b4962334251ee8920d46c912588a0adc4bd58201 Mon Sep 17 00:00:00 2001 From: Markus <974709+Links2004@users.noreply.github.com> Date: Thu, 5 Sep 2024 02:57:44 +0200 Subject: [PATCH 0320/1052] Add StatsD component (#6642) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/statsd/__init__.py | 65 ++++++++ esphome/components/statsd/statsd.cpp | 156 ++++++++++++++++++ esphome/components/statsd/statsd.h | 86 ++++++++++ tests/components/statsD/common.yaml | 29 ++++ tests/components/statsD/test.bk72xx-ard.yaml | 2 + tests/components/statsD/test.esp32-ard.yaml | 2 + .../components/statsD/test.esp32-c3-ard.yaml | 2 + .../components/statsD/test.esp32-c3-idf.yaml | 2 + tests/components/statsD/test.esp32-idf.yaml | 2 + tests/components/statsD/test.esp8266-ard.yaml | 2 + tests/components/statsD/test.rp2040-ard.yaml | 2 + 12 files changed, 351 insertions(+) create mode 100644 esphome/components/statsd/__init__.py create mode 100644 esphome/components/statsd/statsd.cpp create mode 100644 esphome/components/statsd/statsd.h create mode 100644 tests/components/statsD/common.yaml create mode 100644 tests/components/statsD/test.bk72xx-ard.yaml create mode 100644 tests/components/statsD/test.esp32-ard.yaml create mode 100644 tests/components/statsD/test.esp32-c3-ard.yaml create mode 100644 tests/components/statsD/test.esp32-c3-idf.yaml create mode 100644 tests/components/statsD/test.esp32-idf.yaml create mode 100644 tests/components/statsD/test.esp8266-ard.yaml create mode 100644 tests/components/statsD/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 8c706fa2d6..52b5f48a34 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -385,6 +385,7 @@ esphome/components/st7701s/* @clydebarrow esphome/components/st7735/* @SenexCrenshaw esphome/components/st7789v/* @kbx81 esphome/components/st7920/* @marsjan155 +esphome/components/statsd/* @Links2004 esphome/components/substitutions/* @esphome/core esphome/components/sun/* @OttoWinter esphome/components/sun_gtil2/* @Mat931 diff --git a/esphome/components/statsd/__init__.py b/esphome/components/statsd/__init__.py new file mode 100644 index 0000000000..3623338aec --- /dev/null +++ b/esphome/components/statsd/__init__.py @@ -0,0 +1,65 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, binary_sensor +from esphome.const import ( + CONF_ID, + CONF_PORT, + CONF_NAME, + CONF_SENSORS, + CONF_BINARY_SENSORS, +) + +AUTO_LOAD = ["socket"] +CODEOWNERS = ["@Links2004"] +DEPENDENCIES = ["network"] + +CONF_HOST = "host" +CONF_PREFIX = "prefix" + +statsd_component_ns = cg.esphome_ns.namespace("statsd") +StatsdComponent = statsd_component_ns.class_("StatsdComponent", cg.PollingComponent) + +CONFIG_SENSORS_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(sensor.Sensor), + cv.Required(CONF_NAME): cv.string_strict, + } +) + +CONFIG_BINARY_SENSORS_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(binary_sensor.BinarySensor), + cv.Required(CONF_NAME): cv.string_strict, + } +) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(StatsdComponent), + cv.Required(CONF_HOST): cv.string_strict, + cv.Optional(CONF_PORT, default=8125): cv.port, + cv.Optional(CONF_PREFIX, default=""): cv.string_strict, + cv.Optional(CONF_SENSORS): cv.ensure_list(CONFIG_SENSORS_SCHEMA), + cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list(CONFIG_BINARY_SENSORS_SCHEMA), + } +).extend(cv.polling_component_schema("10s")) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + cg.add( + var.configure( + config.get(CONF_HOST), + config.get(CONF_PORT), + config.get(CONF_PREFIX), + ) + ) + + for sensor_cfg in config.get(CONF_SENSORS, []): + s = await cg.get_variable(sensor_cfg[CONF_ID]) + cg.add(var.register_sensor(sensor_cfg[CONF_NAME], s)) + + for sensor_cfg in config.get(CONF_BINARY_SENSORS, []): + s = await cg.get_variable(sensor_cfg[CONF_ID]) + cg.add(var.register_binary_sensor(sensor_cfg[CONF_NAME], s)) diff --git a/esphome/components/statsd/statsd.cpp b/esphome/components/statsd/statsd.cpp new file mode 100644 index 0000000000..68b24908d2 --- /dev/null +++ b/esphome/components/statsd/statsd.cpp @@ -0,0 +1,156 @@ +#include "esphome/core/log.h" + +#include "statsd.h" + +namespace esphome { +namespace statsd { + +// send UDP packet if we reach 1Kb packed size +// this is needed since statsD does not support fragmented UDP packets +static const uint16_t SEND_THRESHOLD = 1024; + +static const char *const TAG = "statsD"; + +void StatsdComponent::setup() { +#ifndef USE_ESP8266 + this->sock_ = esphome::socket::socket(AF_INET, SOCK_DGRAM, 0); + + struct sockaddr_in source; + source.sin_family = AF_INET; + source.sin_addr.s_addr = htonl(INADDR_ANY); + source.sin_port = htons(this->port_); + this->sock_->bind((struct sockaddr *) &source, sizeof(source)); + + this->destination_.sin_family = AF_INET; + this->destination_.sin_port = htons(this->port_); + this->destination_.sin_addr.s_addr = inet_addr(this->host_); +#endif +} + +StatsdComponent::~StatsdComponent() { +#ifndef USE_ESP8266 + if (!this->sock_) { + return; + } + this->sock_->close(); +#endif +} + +void StatsdComponent::dump_config() { + ESP_LOGCONFIG(TAG, "statsD:"); + ESP_LOGCONFIG(TAG, " host: %s", this->host_); + ESP_LOGCONFIG(TAG, " port: %d", 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); + } +} + +float StatsdComponent::get_setup_priority() const { return esphome::setup_priority::AFTER_WIFI; } + +#ifdef USE_SENSOR +void StatsdComponent::register_sensor(const char *name, esphome::sensor::Sensor *sensor) { + sensors_t s; + s.name = name; + s.sensor = sensor; + s.type = TYPE_SENSOR; + this->sensors_.push_back(s); +} +#endif + +#ifdef USE_BINARY_SENSOR +void StatsdComponent::register_binary_sensor(const char *name, esphome::binary_sensor::BinarySensor *binary_sensor) { + sensors_t s; + s.name = name; + s.binary_sensor = binary_sensor; + s.type = TYPE_BINARY_SENSOR; + this->sensors_.push_back(s); +} +#endif + +void StatsdComponent::update() { + std::string out; + out.reserve(SEND_THRESHOLD); + + for (sensors_t s : this->sensors_) { + double val = 0; + switch (s.type) { +#ifdef USE_SENSOR + case TYPE_SENSOR: + if (!s.sensor->has_state()) { + continue; + } + val = s.sensor->state; + break; +#endif +#ifdef USE_BINARY_SENSOR + case TYPE_BINARY_SENSOR: + if (!s.binary_sensor->has_state()) { + continue; + } + // map bool to double + if (s.binary_sensor->state) { + val = 1; + } + break; +#endif + default: + ESP_LOGE(TAG, "type not known, name: %s type: %d", s.name, s.type); + continue; + } + + // statsD gauge: + // https://github.com/statsd/statsd/blob/master/docs/metric_types.md + // This implies you can't explicitly set a gauge to a negative number without first setting it to zero. + if (val < 0) { + if (this->prefix_) { + out.append(str_sprintf("%s.", this->prefix_)); + } + out.append(str_sprintf("%s:0|g\n", s.name)); + } + if (this->prefix_) { + out.append(str_sprintf("%s.", this->prefix_)); + } + out.append(str_sprintf("%s:%f|g\n", s.name, val)); + + if (out.length() > SEND_THRESHOLD) { + this->send_(&out); + out.clear(); + } + } + + this->send_(&out); +} + +void StatsdComponent::send_(std::string *out) { + if (out->empty()) { + return; + } +#ifdef USE_ESP8266 + IPAddress ip; + ip.fromString(this->host_); + + this->sock_.beginPacket(ip, this->port_); + this->sock_.write((const uint8_t *) out->c_str(), out->length()); + this->sock_.endPacket(); + +#else + if (!this->sock_) { + return; + } + + int n_bytes = this->sock_->sendto(out->c_str(), out->length(), 0, reinterpret_cast(&this->destination_), + sizeof(this->destination_)); + if (n_bytes != out->length()) { + ESP_LOGE(TAG, "Failed to send UDP packed (%d of %d)", n_bytes, out->length()); + } +#endif +} + +} // namespace statsd +} // namespace esphome diff --git a/esphome/components/statsd/statsd.h b/esphome/components/statsd/statsd.h new file mode 100644 index 0000000000..ef42579587 --- /dev/null +++ b/esphome/components/statsd/statsd.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include "esphome/core/defines.h" +#include "esphome/core/component.h" +#include "esphome/components/socket/socket.h" +#include "esphome/components/network/ip_address.h" + +#ifdef USE_SENSOR +#include "esphome/components/sensor/sensor.h" +#endif + +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif + +#ifdef USE_LOGGER +#include "esphome/components/logger/logger.h" +#endif + +#ifdef USE_ESP8266 +#include "WiFiUdp.h" +#include "IPAddress.h" +#endif + +namespace esphome { +namespace statsd { + +using sensor_type_t = enum { TYPE_SENSOR, TYPE_BINARY_SENSOR }; + +using sensors_t = struct { + const char *name; + sensor_type_t type; + union { +#ifdef USE_SENSOR + esphome::sensor::Sensor *sensor; +#endif +#ifdef USE_BINARY_SENSOR + esphome::binary_sensor::BinarySensor *binary_sensor; +#endif + }; +}; + +class StatsdComponent : public PollingComponent { + public: + ~StatsdComponent(); + + void setup() override; + void dump_config() override; + void update() override; + float get_setup_priority() const override; + + void configure(const char *host, uint16_t port, const char *prefix) { + this->host_ = host; + this->port_ = port; + this->prefix_ = prefix; + } + +#ifdef USE_SENSOR + void register_sensor(const char *name, esphome::sensor::Sensor *sensor); +#endif + +#ifdef USE_BINARY_SENSOR + void register_binary_sensor(const char *name, esphome::binary_sensor::BinarySensor *binary_sensor); +#endif + + private: + const char *host_; + const char *prefix_; + uint16_t port_; + + std::vector sensors_; + +#ifdef USE_ESP8266 + WiFiUDP sock_; +#else + std::unique_ptr sock_; + struct sockaddr_in destination_; +#endif + + void send_(std::string *out); +}; + +} // namespace statsd +} // namespace esphome diff --git a/tests/components/statsD/common.yaml b/tests/components/statsD/common.yaml new file mode 100644 index 0000000000..5878101de8 --- /dev/null +++ b/tests/components/statsD/common.yaml @@ -0,0 +1,29 @@ +wifi: + ssid: MySSID + password: password1 + +statsd: + host: "192.168.1.1" + port: 8125 + prefix: esphome + update_interval: 60s + sensors: + id: s + name: sensors + binary_sensors: + id: bs + name: binary_sensors + +sensor: + - platform: template + id: s + name: "42.1" + lambda: |- + return 42.1f; + +binary_sensor: + - platform: template + id: bs + name: "On" + lambda: |- + return true; diff --git a/tests/components/statsD/test.bk72xx-ard.yaml b/tests/components/statsD/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/statsD/test.bk72xx-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/statsD/test.esp32-ard.yaml b/tests/components/statsD/test.esp32-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/statsD/test.esp32-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/statsD/test.esp32-c3-ard.yaml b/tests/components/statsD/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/statsD/test.esp32-c3-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/statsD/test.esp32-c3-idf.yaml b/tests/components/statsD/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/statsD/test.esp32-c3-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/statsD/test.esp32-idf.yaml b/tests/components/statsD/test.esp32-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/statsD/test.esp32-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/statsD/test.esp8266-ard.yaml b/tests/components/statsD/test.esp8266-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/statsD/test.esp8266-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/statsD/test.rp2040-ard.yaml b/tests/components/statsD/test.rp2040-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/statsD/test.rp2040-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml From 1548fa0811144302fc9700a50bf8966b7a7d9157 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:09:49 +1200 Subject: [PATCH 0321/1052] [homeassistant-switch] Support different entity domains (#7331) --- esphome/components/homeassistant/__init__.py | 13 ++++++++++++ .../homeassistant/switch/__init__.py | 17 +++++++++++++-- .../switch/homeassistant_switch.cpp | 4 ++-- tests/components/homeassistant/common.yaml | 21 +++++++++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/esphome/components/homeassistant/__init__.py b/esphome/components/homeassistant/__init__.py index 6d997e48ca..223d6c18c3 100644 --- a/esphome/components/homeassistant/__init__.py +++ b/esphome/components/homeassistant/__init__.py @@ -5,6 +5,19 @@ from esphome.const import CONF_ATTRIBUTE, CONF_ENTITY_ID, CONF_INTERNAL CODEOWNERS = ["@OttoWinter", "@esphome/core"] homeassistant_ns = cg.esphome_ns.namespace("homeassistant") + +def validate_entity_domain(platform, supported_domains): + def validator(config): + domain = config[CONF_ENTITY_ID].split(".", 1)[0] + if domain not in supported_domains: + raise cv.Invalid( + f"Entity ID {config[CONF_ENTITY_ID]} is not supported by the {platform} platform." + ) + return config + + return validator + + HOME_ASSISTANT_IMPORT_SCHEMA = cv.Schema( { cv.Required(CONF_ENTITY_ID): cv.entity_id, diff --git a/esphome/components/homeassistant/switch/__init__.py b/esphome/components/homeassistant/switch/__init__.py index 3d7c80682a..384f82bbad 100644 --- a/esphome/components/homeassistant/switch/__init__.py +++ b/esphome/components/homeassistant/switch/__init__.py @@ -7,19 +7,32 @@ from .. import ( HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA, homeassistant_ns, setup_home_assistant_entity, + validate_entity_domain, ) CODEOWNERS = ["@Links2004"] DEPENDENCIES = ["api"] +SUPPORTED_DOMAINS = [ + "automation", + "fan", + "humidifier", + "input_boolean", + "light", + "remote", + "siren", + "switch", +] + HomeassistantSwitch = homeassistant_ns.class_( "HomeassistantSwitch", switch.Switch, cg.Component ) -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( switch.switch_schema(HomeassistantSwitch) - .extend(cv.COMPONENT_SCHEMA) .extend(HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + validate_entity_domain("switch", SUPPORTED_DOMAINS), ) diff --git a/esphome/components/homeassistant/switch/homeassistant_switch.cpp b/esphome/components/homeassistant/switch/homeassistant_switch.cpp index 05ef46e30e..0451c95069 100644 --- a/esphome/components/homeassistant/switch/homeassistant_switch.cpp +++ b/esphome/components/homeassistant/switch/homeassistant_switch.cpp @@ -42,9 +42,9 @@ void HomeassistantSwitch::write_state(bool state) { api::HomeassistantServiceResponse resp; if (state) { - resp.service = "switch.turn_on"; + resp.service = "homeassistant.turn_on"; } else { - resp.service = "switch.turn_off"; + resp.service = "homeassistant.turn_off"; } api::HomeassistantServiceMap entity_id_kv; diff --git a/tests/components/homeassistant/common.yaml b/tests/components/homeassistant/common.yaml index 8c9a4ad75f..9c6cb71b8b 100644 --- a/tests/components/homeassistant/common.yaml +++ b/tests/components/homeassistant/common.yaml @@ -33,6 +33,27 @@ wifi: api: switch: + - platform: homeassistant + entity_id: automation.my_cool_automation + id: my_cool_automation + - platform: homeassistant + entity_id: fan.my_cool_fan + id: my_cool_fan + - platform: homeassistant + entity_id: humidifier.my_cool_humidifier + id: my_cool_humidifier + - platform: homeassistant + entity_id: input_boolean.my_cool_input_boolean + id: my_cool_input_boolean + - platform: homeassistant + entity_id: light.my_cool_light + id: my_cool_light + - platform: homeassistant + entity_id: remote.my_cool_remote + id: my_cool_remote + - platform: homeassistant + entity_id: siren.my_cool_siren + id: my_cool_siren - platform: homeassistant entity_id: switch.my_cool_switch id: my_cool_switch From 18a1191e03578fb395156cb2578dc2f41d2a44f5 Mon Sep 17 00:00:00 2001 From: Adam DeMuri Date: Wed, 4 Sep 2024 23:08:02 -0600 Subject: [PATCH 0322/1052] Add support for using BMP280 with SPI (#7053) Co-authored-by: Keith Burzinski --- CODEOWNERS | 3 + esphome/components/bmp280/sensor.py | 97 +------------------ esphome/components/bmp280_base/__init__.py | 88 +++++++++++++++++ .../bmp280_base.cpp} | 15 ++- .../bmp280.h => bmp280_base/bmp280_base.h} | 14 ++- esphome/components/bmp280_i2c/__init__.py | 0 esphome/components/bmp280_i2c/bmp280_i2c.cpp | 27 ++++++ esphome/components/bmp280_i2c/bmp280_i2c.h | 22 +++++ esphome/components/bmp280_i2c/sensor.py | 22 +++++ esphome/components/bmp280_spi/__init__.py | 0 esphome/components/bmp280_spi/bmp280_spi.cpp | 65 +++++++++++++ esphome/components/bmp280_spi/bmp280_spi.h | 20 ++++ esphome/components/bmp280_spi/sensor.py | 22 +++++ tests/components/bmp280/test.esp32-ard.yaml | 15 --- .../components/bmp280/test.esp32-c3-ard.yaml | 15 --- tests/components/bmp280/test.esp32-idf.yaml | 15 --- tests/components/bmp280/test.esp8266-ard.yaml | 15 --- tests/components/bmp280/test.rp2040-ard.yaml | 15 --- .../common.yaml} | 10 +- .../components/bmp280_i2c/test.esp32-ard.yaml | 5 + .../bmp280_i2c/test.esp32-c3-ard.yaml | 5 + .../bmp280_i2c/test.esp32-c3-idf.yaml | 5 + .../components/bmp280_i2c/test.esp32-idf.yaml | 5 + .../bmp280_i2c/test.esp8266-ard.yaml | 5 + .../bmp280_i2c/test.rp2040-ard.yaml | 5 + tests/components/bmp280_spi/common.yaml | 18 ++++ .../components/bmp280_spi/test.esp32-ard.yaml | 7 ++ .../bmp280_spi/test.esp32-c3-ard.yaml | 7 ++ .../bmp280_spi/test.esp32-c3-idf.yaml | 7 ++ .../components/bmp280_spi/test.esp32-idf.yaml | 7 ++ .../bmp280_spi/test.esp8266-ard.yaml | 7 ++ .../bmp280_spi/test.rp2040-ard.yaml | 7 ++ 32 files changed, 388 insertions(+), 182 deletions(-) create mode 100644 esphome/components/bmp280_base/__init__.py rename esphome/components/{bmp280/bmp280.cpp => bmp280_base/bmp280_base.cpp} (95%) rename esphome/components/{bmp280/bmp280.h => bmp280_base/bmp280_base.h} (88%) create mode 100644 esphome/components/bmp280_i2c/__init__.py create mode 100644 esphome/components/bmp280_i2c/bmp280_i2c.cpp create mode 100644 esphome/components/bmp280_i2c/bmp280_i2c.h create mode 100644 esphome/components/bmp280_i2c/sensor.py create mode 100644 esphome/components/bmp280_spi/__init__.py create mode 100644 esphome/components/bmp280_spi/bmp280_spi.cpp create mode 100644 esphome/components/bmp280_spi/bmp280_spi.h create mode 100644 esphome/components/bmp280_spi/sensor.py delete mode 100644 tests/components/bmp280/test.esp32-ard.yaml delete mode 100644 tests/components/bmp280/test.esp32-c3-ard.yaml delete mode 100644 tests/components/bmp280/test.esp32-idf.yaml delete mode 100644 tests/components/bmp280/test.esp8266-ard.yaml delete mode 100644 tests/components/bmp280/test.rp2040-ard.yaml rename tests/components/{bmp280/test.esp32-c3-idf.yaml => bmp280_i2c/common.yaml} (56%) create mode 100644 tests/components/bmp280_i2c/test.esp32-ard.yaml create mode 100644 tests/components/bmp280_i2c/test.esp32-c3-ard.yaml create mode 100644 tests/components/bmp280_i2c/test.esp32-c3-idf.yaml create mode 100644 tests/components/bmp280_i2c/test.esp32-idf.yaml create mode 100644 tests/components/bmp280_i2c/test.esp8266-ard.yaml create mode 100644 tests/components/bmp280_i2c/test.rp2040-ard.yaml create mode 100644 tests/components/bmp280_spi/common.yaml create mode 100644 tests/components/bmp280_spi/test.esp32-ard.yaml create mode 100644 tests/components/bmp280_spi/test.esp32-c3-ard.yaml create mode 100644 tests/components/bmp280_spi/test.esp32-c3-idf.yaml create mode 100644 tests/components/bmp280_spi/test.esp32-idf.yaml create mode 100644 tests/components/bmp280_spi/test.esp8266-ard.yaml create mode 100644 tests/components/bmp280_spi/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 52b5f48a34..1d4df3ccb8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -70,6 +70,9 @@ esphome/components/bme680_bsec/* @trvrnrth esphome/components/bme68x_bsec2/* @kbx81 @neffs esphome/components/bme68x_bsec2_i2c/* @kbx81 @neffs esphome/components/bmi160/* @flaviut +esphome/components/bmp280_base/* @ademuri +esphome/components/bmp280_i2c/* @ademuri +esphome/components/bmp280_spi/* @ademuri esphome/components/bmp3xx/* @latonita esphome/components/bmp3xx_base/* @latonita @martgras esphome/components/bmp3xx_i2c/* @latonita diff --git a/esphome/components/bmp280/sensor.py b/esphome/components/bmp280/sensor.py index a23bc0766a..a624889982 100644 --- a/esphome/components/bmp280/sensor.py +++ b/esphome/components/bmp280/sensor.py @@ -1,96 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import i2c, sensor -from esphome.const import ( - CONF_ID, - CONF_PRESSURE, - CONF_TEMPERATURE, - DEVICE_CLASS_PRESSURE, - DEVICE_CLASS_TEMPERATURE, - STATE_CLASS_MEASUREMENT, - UNIT_CELSIUS, - UNIT_HECTOPASCAL, - CONF_IIR_FILTER, - CONF_OVERSAMPLING, + +CONFIG_SCHEMA = cv.invalid( + "The bmp280 sensor component has been renamed to bmp280_i2c." ) - -DEPENDENCIES = ["i2c"] - -bmp280_ns = cg.esphome_ns.namespace("bmp280") -BMP280Oversampling = bmp280_ns.enum("BMP280Oversampling") -OVERSAMPLING_OPTIONS = { - "NONE": BMP280Oversampling.BMP280_OVERSAMPLING_NONE, - "1X": BMP280Oversampling.BMP280_OVERSAMPLING_1X, - "2X": BMP280Oversampling.BMP280_OVERSAMPLING_2X, - "4X": BMP280Oversampling.BMP280_OVERSAMPLING_4X, - "8X": BMP280Oversampling.BMP280_OVERSAMPLING_8X, - "16X": BMP280Oversampling.BMP280_OVERSAMPLING_16X, -} - -BMP280IIRFilter = bmp280_ns.enum("BMP280IIRFilter") -IIR_FILTER_OPTIONS = { - "OFF": BMP280IIRFilter.BMP280_IIR_FILTER_OFF, - "2X": BMP280IIRFilter.BMP280_IIR_FILTER_2X, - "4X": BMP280IIRFilter.BMP280_IIR_FILTER_4X, - "8X": BMP280IIRFilter.BMP280_IIR_FILTER_8X, - "16X": BMP280IIRFilter.BMP280_IIR_FILTER_16X, -} - -BMP280Component = bmp280_ns.class_( - "BMP280Component", cg.PollingComponent, i2c.I2CDevice -) - -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(BMP280Component), - cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( - unit_of_measurement=UNIT_CELSIUS, - accuracy_decimals=1, - device_class=DEVICE_CLASS_TEMPERATURE, - state_class=STATE_CLASS_MEASUREMENT, - ).extend( - { - cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( - OVERSAMPLING_OPTIONS, upper=True - ), - } - ), - cv.Optional(CONF_PRESSURE): sensor.sensor_schema( - unit_of_measurement=UNIT_HECTOPASCAL, - accuracy_decimals=1, - device_class=DEVICE_CLASS_PRESSURE, - state_class=STATE_CLASS_MEASUREMENT, - ).extend( - { - cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( - OVERSAMPLING_OPTIONS, upper=True - ), - } - ), - cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( - IIR_FILTER_OPTIONS, upper=True - ), - } - ) - .extend(cv.polling_component_schema("60s")) - .extend(i2c.i2c_device_schema(0x77)) -) - - -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 temperature_config := config.get(CONF_TEMPERATURE): - sens = await sensor.new_sensor(temperature_config) - cg.add(var.set_temperature_sensor(sens)) - cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING])) - - if pressure_config := config.get(CONF_PRESSURE): - sens = await sensor.new_sensor(pressure_config) - cg.add(var.set_pressure_sensor(sens)) - cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING])) - - cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) diff --git a/esphome/components/bmp280_base/__init__.py b/esphome/components/bmp280_base/__init__.py new file mode 100644 index 0000000000..c0f9af9dd7 --- /dev/null +++ b/esphome/components/bmp280_base/__init__.py @@ -0,0 +1,88 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import ( + CONF_ID, + CONF_IIR_FILTER, + CONF_OVERSAMPLING, + CONF_PRESSURE, + CONF_TEMPERATURE, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, + UNIT_HECTOPASCAL, +) + +CODEOWNERS = ["@ademuri"] + +bmp280_ns = cg.esphome_ns.namespace("bmp280_base") +BMP280Oversampling = bmp280_ns.enum("BMP280Oversampling") +OVERSAMPLING_OPTIONS = { + "NONE": BMP280Oversampling.BMP280_OVERSAMPLING_NONE, + "1X": BMP280Oversampling.BMP280_OVERSAMPLING_1X, + "2X": BMP280Oversampling.BMP280_OVERSAMPLING_2X, + "4X": BMP280Oversampling.BMP280_OVERSAMPLING_4X, + "8X": BMP280Oversampling.BMP280_OVERSAMPLING_8X, + "16X": BMP280Oversampling.BMP280_OVERSAMPLING_16X, +} + +BMP280IIRFilter = bmp280_ns.enum("BMP280IIRFilter") +IIR_FILTER_OPTIONS = { + "OFF": BMP280IIRFilter.BMP280_IIR_FILTER_OFF, + "2X": BMP280IIRFilter.BMP280_IIR_FILTER_2X, + "4X": BMP280IIRFilter.BMP280_IIR_FILTER_4X, + "8X": BMP280IIRFilter.BMP280_IIR_FILTER_8X, + "16X": BMP280IIRFilter.BMP280_IIR_FILTER_16X, +} + +CONFIG_SCHEMA_BASE = cv.Schema( + { + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_PRESSURE): sensor.sensor_schema( + unit_of_measurement=UNIT_HECTOPASCAL, + accuracy_decimals=1, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } + ), + cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum( + IIR_FILTER_OPTIONS, upper=True + ), + } +).extend(cv.polling_component_schema("60s")) + + +async def to_code_base(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + if temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature_sensor(sens)) + cg.add(var.set_temperature_oversampling(temperature_config[CONF_OVERSAMPLING])) + + if pressure_config := config.get(CONF_PRESSURE): + sens = await sensor.new_sensor(pressure_config) + cg.add(var.set_pressure_sensor(sens)) + cg.add(var.set_pressure_oversampling(pressure_config[CONF_OVERSAMPLING])) + + cg.add(var.set_iir_filter(config[CONF_IIR_FILTER])) + + return var diff --git a/esphome/components/bmp280/bmp280.cpp b/esphome/components/bmp280_base/bmp280_base.cpp similarity index 95% rename from esphome/components/bmp280/bmp280.cpp rename to esphome/components/bmp280_base/bmp280_base.cpp index c92daa07fb..f94456f6e6 100644 --- a/esphome/components/bmp280/bmp280.cpp +++ b/esphome/components/bmp280_base/bmp280_base.cpp @@ -1,9 +1,9 @@ -#include "bmp280.h" +#include "bmp280_base.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" namespace esphome { -namespace bmp280 { +namespace bmp280_base { static const char *const TAG = "bmp280.sensor"; @@ -59,6 +59,14 @@ static const char *iir_filter_to_str(BMP280IIRFilter filter) { void BMP280Component::setup() { ESP_LOGCONFIG(TAG, "Setting up BMP280..."); uint8_t chip_id = 0; + + // Read the chip id twice, to work around a bug where the first read is 0. + // https://community.st.com/t5/stm32-mcus-products/issue-with-reading-bmp280-chip-id-using-spi/td-p/691855 + if (!this->read_byte(0xD0, &chip_id)) { + this->error_code_ = COMMUNICATION_FAILED; + this->mark_failed(); + return; + } if (!this->read_byte(0xD0, &chip_id)) { this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(); @@ -122,7 +130,6 @@ void BMP280Component::setup() { } void BMP280Component::dump_config() { ESP_LOGCONFIG(TAG, "BMP280:"); - LOG_I2C_DEVICE(this); switch (this->error_code_) { case COMMUNICATION_FAILED: ESP_LOGE(TAG, "Communication with BMP280 failed!"); @@ -262,5 +269,5 @@ uint16_t BMP280Component::read_u16_le_(uint8_t a_register) { } int16_t BMP280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); } -} // namespace bmp280 +} // namespace bmp280_base } // namespace esphome diff --git a/esphome/components/bmp280/bmp280.h b/esphome/components/bmp280_base/bmp280_base.h similarity index 88% rename from esphome/components/bmp280/bmp280.h rename to esphome/components/bmp280_base/bmp280_base.h index 96eb470155..4b22e98f13 100644 --- a/esphome/components/bmp280/bmp280.h +++ b/esphome/components/bmp280_base/bmp280_base.h @@ -2,10 +2,9 @@ #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" -#include "esphome/components/i2c/i2c.h" namespace esphome { -namespace bmp280 { +namespace bmp280_base { /// Internal struct storing the calibration values of an BMP280. struct BMP280CalibrationData { @@ -50,8 +49,8 @@ enum BMP280IIRFilter { BMP280_IIR_FILTER_16X = 0b100, }; -/// This class implements support for the BMP280 Temperature+Pressure i2c sensor. -class BMP280Component : public PollingComponent, public i2c::I2CDevice { +/// This class implements support for the BMP280 Temperature+Pressure sensor. +class BMP280Component : public PollingComponent { public: void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } @@ -68,6 +67,11 @@ class BMP280Component : public PollingComponent, public i2c::I2CDevice { float get_setup_priority() const override; void update() override; + virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0; + virtual bool write_byte(uint8_t a_register, uint8_t data) = 0; + virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; + virtual bool read_byte_16(uint8_t a_register, uint16_t *data) = 0; + protected: /// Read the temperature value and store the calculated ambient temperature in t_fine. float read_temperature_(int32_t *t_fine); @@ -90,5 +94,5 @@ class BMP280Component : public PollingComponent, public i2c::I2CDevice { } error_code_{NONE}; }; -} // namespace bmp280 +} // namespace bmp280_base } // namespace esphome diff --git a/esphome/components/bmp280_i2c/__init__.py b/esphome/components/bmp280_i2c/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/bmp280_i2c/bmp280_i2c.cpp b/esphome/components/bmp280_i2c/bmp280_i2c.cpp new file mode 100644 index 0000000000..04b8bd8b10 --- /dev/null +++ b/esphome/components/bmp280_i2c/bmp280_i2c.cpp @@ -0,0 +1,27 @@ +#include "bmp280_i2c.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace bmp280_i2c { + +bool BMP280I2CComponent::read_byte(uint8_t a_register, uint8_t *data) { + return I2CDevice::read_byte(a_register, data); +}; +bool BMP280I2CComponent::write_byte(uint8_t a_register, uint8_t data) { + return I2CDevice::write_byte(a_register, data); +}; +bool BMP280I2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { + return I2CDevice::read_bytes(a_register, data, len); +}; +bool BMP280I2CComponent::read_byte_16(uint8_t a_register, uint16_t *data) { + return I2CDevice::read_byte_16(a_register, data); +}; + +void BMP280I2CComponent::dump_config() { + LOG_I2C_DEVICE(this); + BMP280Component::dump_config(); +} + +} // namespace bmp280_i2c +} // namespace esphome diff --git a/esphome/components/bmp280_i2c/bmp280_i2c.h b/esphome/components/bmp280_i2c/bmp280_i2c.h new file mode 100644 index 0000000000..66d78d788b --- /dev/null +++ b/esphome/components/bmp280_i2c/bmp280_i2c.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esphome/components/bmp280_base/bmp280_base.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace bmp280_i2c { + +static const char *const TAG = "bmp280_i2c.sensor"; + +/// This class implements support for the BMP280 Temperature+Pressure i2c sensor. +class BMP280I2CComponent : public esphome::bmp280_base::BMP280Component, public i2c::I2CDevice { + public: + bool read_byte(uint8_t a_register, uint8_t *data) override; + bool write_byte(uint8_t a_register, uint8_t data) override; + bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; + bool read_byte_16(uint8_t a_register, uint16_t *data) override; + void dump_config() override; +}; + +} // namespace bmp280_i2c +} // namespace esphome diff --git a/esphome/components/bmp280_i2c/sensor.py b/esphome/components/bmp280_i2c/sensor.py new file mode 100644 index 0000000000..991bb827a3 --- /dev/null +++ b/esphome/components/bmp280_i2c/sensor.py @@ -0,0 +1,22 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c +from ..bmp280_base import to_code_base, CONFIG_SCHEMA_BASE + +AUTO_LOAD = ["bmp280_base"] +CODEOWNERS = ["@ademuri"] +DEPENDENCIES = ["i2c"] + +bmp280_ns = cg.esphome_ns.namespace("bmp280_i2c") +BMP280I2CComponent = bmp280_ns.class_( + "BMP280I2CComponent", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend( + i2c.i2c_device_schema(default_address=0x77) +).extend({cv.GenerateID(): cv.declare_id(BMP280I2CComponent)}) + + +async def to_code(config): + var = await to_code_base(config) + await i2c.register_i2c_device(var, config) diff --git a/esphome/components/bmp280_spi/__init__.py b/esphome/components/bmp280_spi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/bmp280_spi/bmp280_spi.cpp b/esphome/components/bmp280_spi/bmp280_spi.cpp new file mode 100644 index 0000000000..a35e829432 --- /dev/null +++ b/esphome/components/bmp280_spi/bmp280_spi.cpp @@ -0,0 +1,65 @@ +#include +#include + +#include "bmp280_spi.h" +#include + +namespace esphome { +namespace bmp280_spi { + +uint8_t set_bit(uint8_t num, uint8_t position) { + uint8_t mask = 1 << position; + return num | mask; +} + +uint8_t clear_bit(uint8_t num, uint8_t position) { + uint8_t mask = 1 << position; + return num & ~mask; +} + +void BMP280SPIComponent::setup() { + this->spi_setup(); + BMP280Component::setup(); +}; + +// In SPI mode, only 7 bits of the register addresses are used; the MSB of register address is not used +// and replaced by a read/write bit (RW = ‘0’ for write and RW = ‘1’ for read). +// Example: address 0xF7 is accessed by using SPI register address 0x77. For write access, the byte +// 0x77 is transferred, for read access, the byte 0xF7 is transferred. +// https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf + +bool BMP280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) { + this->enable(); + this->transfer_byte(set_bit(a_register, 7)); + *data = this->transfer_byte(0); + this->disable(); + return true; +} + +bool BMP280SPIComponent::write_byte(uint8_t a_register, uint8_t data) { + this->enable(); + this->transfer_byte(clear_bit(a_register, 7)); + this->transfer_byte(data); + this->disable(); + return true; +} + +bool BMP280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { + this->enable(); + this->transfer_byte(set_bit(a_register, 7)); + this->read_array(data, len); + this->disable(); + return true; +} + +bool BMP280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) { + this->enable(); + this->transfer_byte(set_bit(a_register, 7)); + ((uint8_t *) data)[1] = this->transfer_byte(0); + ((uint8_t *) data)[0] = this->transfer_byte(0); + this->disable(); + return true; +} + +} // namespace bmp280_spi +} // namespace esphome diff --git a/esphome/components/bmp280_spi/bmp280_spi.h b/esphome/components/bmp280_spi/bmp280_spi.h new file mode 100644 index 0000000000..dd226502f6 --- /dev/null +++ b/esphome/components/bmp280_spi/bmp280_spi.h @@ -0,0 +1,20 @@ +#pragma once + +#include "esphome/components/bmp280_base/bmp280_base.h" +#include "esphome/components/spi/spi.h" + +namespace esphome { +namespace bmp280_spi { + +class BMP280SPIComponent : public esphome::bmp280_base::BMP280Component, + public spi::SPIDevice { + void setup() override; + bool read_byte(uint8_t a_register, uint8_t *data) override; + bool write_byte(uint8_t a_register, uint8_t data) override; + bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; + bool read_byte_16(uint8_t a_register, uint16_t *data) override; +}; + +} // namespace bmp280_spi +} // namespace esphome diff --git a/esphome/components/bmp280_spi/sensor.py b/esphome/components/bmp280_spi/sensor.py new file mode 100644 index 0000000000..511d45b24e --- /dev/null +++ b/esphome/components/bmp280_spi/sensor.py @@ -0,0 +1,22 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import spi +from ..bmp280_base import to_code_base, CONFIG_SCHEMA_BASE + +AUTO_LOAD = ["bmp280_base"] +CODEOWNERS = ["@ademuri"] +DEPENDENCIES = ["spi"] + +bmp280_ns = cg.esphome_ns.namespace("bmp280_spi") +BMP280SPIComponent = bmp280_ns.class_( + "BMP280SPIComponent", cg.PollingComponent, spi.SPIDevice +) + +CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend( + spi.spi_device_schema(default_mode="mode3") +).extend({cv.GenerateID(): cv.declare_id(BMP280SPIComponent)}) + + +async def to_code(config): + var = await to_code_base(config) + await spi.register_spi_device(var, config) diff --git a/tests/components/bmp280/test.esp32-ard.yaml b/tests/components/bmp280/test.esp32-ard.yaml deleted file mode 100644 index aeb1cb262b..0000000000 --- a/tests/components/bmp280/test.esp32-ard.yaml +++ /dev/null @@ -1,15 +0,0 @@ -i2c: - - id: i2c_bmp280 - scl: 16 - sda: 17 - -sensor: - - platform: bmp280 - address: 0x77 - temperature: - name: Outside Temperature - oversampling: 16x - pressure: - name: Outside Pressure - iir_filter: 16x - update_interval: 15s diff --git a/tests/components/bmp280/test.esp32-c3-ard.yaml b/tests/components/bmp280/test.esp32-c3-ard.yaml deleted file mode 100644 index 5f7f85d3e2..0000000000 --- a/tests/components/bmp280/test.esp32-c3-ard.yaml +++ /dev/null @@ -1,15 +0,0 @@ -i2c: - - id: i2c_bmp280 - scl: 5 - sda: 4 - -sensor: - - platform: bmp280 - address: 0x77 - temperature: - name: Outside Temperature - oversampling: 16x - pressure: - name: Outside Pressure - iir_filter: 16x - update_interval: 15s diff --git a/tests/components/bmp280/test.esp32-idf.yaml b/tests/components/bmp280/test.esp32-idf.yaml deleted file mode 100644 index aeb1cb262b..0000000000 --- a/tests/components/bmp280/test.esp32-idf.yaml +++ /dev/null @@ -1,15 +0,0 @@ -i2c: - - id: i2c_bmp280 - scl: 16 - sda: 17 - -sensor: - - platform: bmp280 - address: 0x77 - temperature: - name: Outside Temperature - oversampling: 16x - pressure: - name: Outside Pressure - iir_filter: 16x - update_interval: 15s diff --git a/tests/components/bmp280/test.esp8266-ard.yaml b/tests/components/bmp280/test.esp8266-ard.yaml deleted file mode 100644 index 5f7f85d3e2..0000000000 --- a/tests/components/bmp280/test.esp8266-ard.yaml +++ /dev/null @@ -1,15 +0,0 @@ -i2c: - - id: i2c_bmp280 - scl: 5 - sda: 4 - -sensor: - - platform: bmp280 - address: 0x77 - temperature: - name: Outside Temperature - oversampling: 16x - pressure: - name: Outside Pressure - iir_filter: 16x - update_interval: 15s diff --git a/tests/components/bmp280/test.rp2040-ard.yaml b/tests/components/bmp280/test.rp2040-ard.yaml deleted file mode 100644 index 5f7f85d3e2..0000000000 --- a/tests/components/bmp280/test.rp2040-ard.yaml +++ /dev/null @@ -1,15 +0,0 @@ -i2c: - - id: i2c_bmp280 - scl: 5 - sda: 4 - -sensor: - - platform: bmp280 - address: 0x77 - temperature: - name: Outside Temperature - oversampling: 16x - pressure: - name: Outside Pressure - iir_filter: 16x - update_interval: 15s diff --git a/tests/components/bmp280/test.esp32-c3-idf.yaml b/tests/components/bmp280_i2c/common.yaml similarity index 56% rename from tests/components/bmp280/test.esp32-c3-idf.yaml rename to tests/components/bmp280_i2c/common.yaml index 5f7f85d3e2..edf52b2cd4 100644 --- a/tests/components/bmp280/test.esp32-c3-idf.yaml +++ b/tests/components/bmp280_i2c/common.yaml @@ -1,15 +1,17 @@ i2c: - id: i2c_bmp280 - scl: 5 - sda: 4 + scl: ${scl_pin} + sda: ${sda_pin} sensor: - - platform: bmp280 + - platform: bmp280_i2c + i2c_id: i2c_bmp280 address: 0x77 temperature: + id: bmp280_temperature name: Outside Temperature - oversampling: 16x pressure: name: Outside Pressure + id: bmp280_pressure iir_filter: 16x update_interval: 15s diff --git a/tests/components/bmp280_i2c/test.esp32-ard.yaml b/tests/components/bmp280_i2c/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bmp280_i2c/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp32-c3-ard.yaml b/tests/components/bmp280_i2c/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/bmp280_i2c/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml b/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/bmp280_i2c/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp32-idf.yaml b/tests/components/bmp280_i2c/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/bmp280_i2c/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.esp8266-ard.yaml b/tests/components/bmp280_i2c/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/bmp280_i2c/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/bmp280_i2c/test.rp2040-ard.yaml b/tests/components/bmp280_i2c/test.rp2040-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/bmp280_i2c/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/bmp280_spi/common.yaml b/tests/components/bmp280_spi/common.yaml new file mode 100644 index 0000000000..798804de5b --- /dev/null +++ b/tests/components/bmp280_spi/common.yaml @@ -0,0 +1,18 @@ +spi: + - id: spi_bmp280 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: bmp280_spi + spi_id: spi_bmp280 + cs_pin: ${cs_pin} + temperature: + id: bmp280_temperature + name: Outside Temperature + pressure: + name: Outside Pressure + id: bmp280_pressure + iir_filter: 16x + update_interval: 15s diff --git a/tests/components/bmp280_spi/test.esp32-ard.yaml b/tests/components/bmp280_spi/test.esp32-ard.yaml new file mode 100644 index 0000000000..54e027a614 --- /dev/null +++ b/tests/components/bmp280_spi/test.esp32-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp32-c3-ard.yaml b/tests/components/bmp280_spi/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..2415ba5dc6 --- /dev/null +++ b/tests/components/bmp280_spi/test.esp32-c3-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + +<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp32-c3-idf.yaml b/tests/components/bmp280_spi/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..2415ba5dc6 --- /dev/null +++ b/tests/components/bmp280_spi/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + +<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp32-idf.yaml b/tests/components/bmp280_spi/test.esp32-idf.yaml new file mode 100644 index 0000000000..54e027a614 --- /dev/null +++ b/tests/components/bmp280_spi/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.esp8266-ard.yaml b/tests/components/bmp280_spi/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dbd158d030 --- /dev/null +++ b/tests/components/bmp280_spi/test.esp8266-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 + +<<: !include common.yaml diff --git a/tests/components/bmp280_spi/test.rp2040-ard.yaml b/tests/components/bmp280_spi/test.rp2040-ard.yaml new file mode 100644 index 0000000000..f6c3f1eeca --- /dev/null +++ b/tests/components/bmp280_spi/test.rp2040-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + +<<: !include common.yaml From 8bd46a43b94cb230298cf8d591af386f0022fcca Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 8 Sep 2024 19:54:20 -0500 Subject: [PATCH 0323/1052] Add voice assistant announce (#7377) --- esphome/components/api/api.proto | 17 ++++++ esphome/components/api/api_connection.cpp | 10 ++++ esphome/components/api/api_connection.h | 1 + esphome/components/api/api_pb2.cpp | 53 +++++++++++++++++++ esphome/components/api/api_pb2.h | 23 ++++++++ esphome/components/api/api_pb2_service.cpp | 21 ++++++++ esphome/components/api/api_pb2_service.h | 6 +++ .../voice_assistant/voice_assistant.cpp | 16 ++++++ .../voice_assistant/voice_assistant.h | 1 + 9 files changed, 148 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index ad6fc79cf3..1c40e8014e 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1553,6 +1553,23 @@ message VoiceAssistantTimerEventResponse { bool is_active = 6; } +message VoiceAssistantAnnounceRequest { + option (id) = 119; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + string media_id = 1; + string text = 2; +} + +message VoiceAssistantAnnounceFinished { + option (id) = 120; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + bool success = 1; +} + // ==================== ALARM CONTROL PANEL ==================== enum AlarmControlPanelState { ALARM_STATE_DISARMED = 0; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index a655d06e66..6b7051a704 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1213,6 +1213,16 @@ void APIConnection::on_voice_assistant_timer_event_response(const VoiceAssistant } }; +void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) { + if (voice_assistant::global_voice_assistant != nullptr) { + if (voice_assistant::global_voice_assistant->get_api_connection() != this) { + return; + } + + voice_assistant::global_voice_assistant->on_announce(msg); + } +} + #endif #ifdef USE_ALARM_CONTROL_PANEL diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 714e806470..e8d66a5e07 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -151,6 +151,7 @@ class APIConnection : public APIServerConnection { void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override; void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override; void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override; + void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override; #endif #ifdef USE_ALARM_CONTROL_PANEL diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index c944d0dae8..2a1552d6fc 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -7061,6 +7061,59 @@ void VoiceAssistantTimerEventResponse::dump_to(std::string &out) const { out.append("}"); } #endif +bool VoiceAssistantAnnounceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->media_id = value.as_string(); + return true; + } + case 2: { + this->text = value.as_string(); + return true; + } + default: + return false; + } +} +void VoiceAssistantAnnounceRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->media_id); + buffer.encode_string(2, this->text); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantAnnounceRequest::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantAnnounceRequest {\n"); + out.append(" media_id: "); + out.append("'").append(this->media_id).append("'"); + out.append("\n"); + + out.append(" text: "); + out.append("'").append(this->text).append("'"); + out.append("\n"); + out.append("}"); +} +#endif +bool VoiceAssistantAnnounceFinished::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->success = value.as_bool(); + return true; + } + default: + return false; + } +} +void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->success); } +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantAnnounceFinished {\n"); + out.append(" success: "); + out.append(YESNO(this->success)); + out.append("\n"); + out.append("}"); +} +#endif bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 6: { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 3f609c793c..6fab1f57e0 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1825,6 +1825,29 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class VoiceAssistantAnnounceRequest : public ProtoMessage { + public: + std::string media_id{}; + std::string text{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class VoiceAssistantAnnounceFinished : public ProtoMessage { + public: + bool success{false}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { public: std::string object_id{}; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 269a755e9e..faa977389a 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -486,6 +486,16 @@ bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAud #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_ALARM_CONTROL_PANEL bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response( const ListEntitiesAlarmControlPanelResponse &msg) { @@ -1135,6 +1145,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_update_command_request: %s", msg.dump().c_str()); #endif this->on_update_command_request(msg); +#endif + break; + } + case 119: { +#ifdef USE_VOICE_ASSISTANT + VoiceAssistantAnnounceRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_voice_assistant_announce_request: %s", msg.dump().c_str()); +#endif + this->on_voice_assistant_announce_request(msg); #endif break; } diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index 83bfc2ed98..f3803ad628 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -247,6 +247,12 @@ class APIServerConnectionBase : public ProtoService { #ifdef USE_VOICE_ASSISTANT virtual void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &value){}; #endif +#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_ALARM_CONTROL_PANEL bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg); #endif diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 43c7428858..577de630fb 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -396,6 +396,10 @@ void VoiceAssistant::loop() { this->set_timeout("playing", 2000, [this]() { this->cancel_timeout("speaker-timeout"); this->set_state_(State::IDLE, State::IDLE); + + api::VoiceAssistantAnnounceFinished msg; + msg.success = true; + this->api_client_->send_voice_assistant_announce_finished(msg); }); } break; @@ -866,6 +870,18 @@ void VoiceAssistant::timer_tick_() { this->timer_tick_trigger_->trigger(res); } +void VoiceAssistant::on_announce(const api::VoiceAssistantAnnounceRequest &msg) { +#ifdef USE_MEDIA_PLAYER + if (this->media_player_ != nullptr) { + this->tts_start_trigger_->trigger(msg.text); + this->media_player_->make_call().set_media_url(msg.media_id).set_announcement(true).perform(); + this->set_state_(State::STREAMING_RESPONSE, State::STREAMING_RESPONSE); + this->tts_end_trigger_->trigger(msg.media_id); + this->end_trigger_->trigger(); + } +#endif +} + VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace voice_assistant diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 88cb0dd413..b0a172332f 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -132,6 +132,7 @@ class VoiceAssistant : public Component { void on_event(const api::VoiceAssistantEventResponse &msg); void on_audio(const api::VoiceAssistantAudio &msg); void on_timer_event(const api::VoiceAssistantTimerEventResponse &msg); + void on_announce(const api::VoiceAssistantAnnounceRequest &msg); bool is_running() const { return this->state_ != State::IDLE; } void set_continuous(bool continuous) { this->continuous_ = continuous; } From 9722876ef667a628e1df3d7623e73db14c8d2d2f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:59:09 +1000 Subject: [PATCH 0324/1052] [lvgl] Msgbox fixes and enhancements (#7380) --- esphome/components/lvgl/automation.py | 16 +++++--- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/widgets/__init__.py | 2 + esphome/components/lvgl/widgets/msgbox.py | 41 +++++++++++++-------- tests/components/lvgl/lvgl-package.yaml | 24 ++++++++++++ 5 files changed, 63 insertions(+), 21 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 8138551c30..cdc7553e81 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -229,19 +229,23 @@ async def obj_hide_to_code(config, action_id, template_arg, args): async def do_hide(widget: Widget): widget.add_flag("LV_OBJ_FLAG_HIDDEN") - return await action_to_code( - await get_widgets(config), do_hide, action_id, template_arg, args - ) + widgets = [ + widget.outer if widget.outer else widget for widget in await get_widgets(config) + ] + return await action_to_code(widgets, do_hide, action_id, template_arg, args) @automation.register_action("lvgl.widget.show", ObjUpdateAction, LIST_ACTION_SCHEMA) async def obj_show_to_code(config, action_id, template_arg, args): async def do_show(widget: Widget): widget.clear_flag("LV_OBJ_FLAG_HIDDEN") + if widget.move_to_foreground: + lv_obj.move_foreground(widget.obj) - return await action_to_code( - await get_widgets(config), do_show, action_id, template_arg, args - ) + widgets = [ + widget.outer if widget.outer else widget for widget in await get_widgets(config) + ] + return await action_to_code(widgets, do_show, action_id, template_arg, args) def focused_id(value): diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index e05bf52120..ee8472f90d 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -374,6 +374,7 @@ CONF_ANTIALIAS = "antialias" CONF_ARC_LENGTH = "arc_length" CONF_AUTO_START = "auto_start" CONF_BACKGROUND_STYLE = "background_style" +CONF_BUTTON_STYLE = "button_style" CONF_DECIMAL_PLACES = "decimal_places" CONF_COLUMN = "column" CONF_DIGITS = "digits" diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index ae06bf20b0..e093cebd16 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -89,6 +89,8 @@ class Widget: self.obj = MockObj(f"{self.var}->obj") else: self.obj = var + self.outer = None + self.move_to_foreground = False @staticmethod def create(name, var, wtype: WidgetType, config: dict = None): diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index c377af6bde..1af4ed6e05 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -1,11 +1,12 @@ from esphome import config_validation as cv -from esphome.const import CONF_BUTTON, CONF_ID, CONF_TEXT +from esphome.const import CONF_BUTTON, CONF_ID, CONF_ITEMS, CONF_TEXT from esphome.core import ID from esphome.cpp_generator import new_Pvariable, static_const_array from esphome.cpp_types import nullptr from ..defines import ( CONF_BODY, + CONF_BUTTON_STYLE, CONF_BUTTONS, CONF_CLOSE_BUTTON, CONF_MSGBOXES, @@ -25,7 +26,7 @@ from ..lvcode import ( lv_obj, lv_Pvariable, ) -from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema +from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema from ..styles import TOP_LAYER from ..types import LV_EVENT, char_ptr, lv_obj_t from . import Widget, set_obj_properties @@ -48,9 +49,10 @@ MSGBOX_SCHEMA = container_schema( { cv.GenerateID(CONF_ID): cv.declare_id(lv_obj_t), cv.Required(CONF_TITLE): STYLED_TEXT_SCHEMA, - cv.Optional(CONF_BODY): STYLED_TEXT_SCHEMA, + cv.Optional(CONF_BODY, default=""): STYLED_TEXT_SCHEMA, cv.Optional(CONF_BUTTONS): cv.ensure_list(BUTTONMATRIX_BUTTON_SCHEMA), - cv.Optional(CONF_CLOSE_BUTTON): lv_bool, + cv.Optional(CONF_BUTTON_STYLE): part_schema(buttonmatrix_spec), + cv.Optional(CONF_CLOSE_BUTTON, default=True): lv_bool, cv.GenerateID(CONF_BUTTON_TEXT_LIST_ID): cv.declare_id(char_ptr), } ), @@ -74,7 +76,8 @@ async def msgbox_to_code(conf): ) lvgl_components_required.add("BUTTONMATRIX") messagebox_id = conf[CONF_ID] - outer = lv_Pvariable(lv_obj_t, messagebox_id.id) + outer_id = f"{messagebox_id.id}_outer" + outer = lv_Pvariable(lv_obj_t, messagebox_id.id + "_outer") buttonmatrix = new_Pvariable( ID( f"{messagebox_id.id}_buttonmatrix_", @@ -82,8 +85,11 @@ async def msgbox_to_code(conf): type=lv_buttonmatrix_t, ) ) - msgbox = lv_Pvariable(lv_obj_t, f"{messagebox_id.id}_msgbox") - outer_widget = Widget.create(messagebox_id, outer, obj_spec, conf) + msgbox = lv_Pvariable(lv_obj_t, messagebox_id.id) + outer_widget = Widget.create(outer_id, outer, obj_spec, conf) + outer_widget.move_to_foreground = True + msgbox_widget = Widget.create(messagebox_id, msgbox, obj_spec, conf) + msgbox_widget.outer = outer_widget buttonmatrix_widget = Widget.create( str(buttonmatrix), buttonmatrix, buttonmatrix_spec, conf ) @@ -92,10 +98,8 @@ async def msgbox_to_code(conf): ) text_id = conf[CONF_BUTTON_TEXT_LIST_ID] text_list = static_const_array(text_id, text_list) - if (text := conf.get(CONF_BODY)) is not None: - text = await lv_text.process(text.get(CONF_TEXT)) - if (title := conf.get(CONF_TITLE)) is not None: - title = await lv_text.process(title.get(CONF_TEXT)) + text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, "")) + title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, "")) close_button = conf[CONF_CLOSE_BUTTON] lv_assign(outer, lv_expr.obj_create(TOP_LAYER)) lv_obj.set_width(outer, lv_pct(100)) @@ -111,20 +115,27 @@ async def msgbox_to_code(conf): ) lv_obj.set_style_align(msgbox, literal("LV_ALIGN_CENTER"), 0) lv_add(buttonmatrix.set_obj(lv_expr.msgbox_get_btns(msgbox))) - await set_obj_properties(outer_widget, conf) + if button_style := conf.get(CONF_BUTTON_STYLE): + button_style = {CONF_ITEMS: button_style} + await set_obj_properties(buttonmatrix_widget, button_style) + await set_obj_properties(msgbox_widget, conf) + async with LambdaContext(EVENT_ARG, where=messagebox_id) as close_action: + outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN") if close_button: - async with LambdaContext(EVENT_ARG, where=messagebox_id) as context: - outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN") with LocalVariable( "close_btn_", lv_obj_t, lv_expr.msgbox_get_close_btn(msgbox) ) as close_btn: lv_obj.remove_event_cb(close_btn, nullptr) lv_obj.add_event_cb( close_btn, - await context.get_lambda(), + await close_action.get_lambda(), LV_EVENT.CLICKED, nullptr, ) + else: + lv_obj.add_event_cb( + outer, await close_action.get_lambda(), LV_EVENT.CLICKED, nullptr + ) if len(ctrl_list) != 0 or len(width_list) != 0: set_btn_data(buttonmatrix.obj, ctrl_list, width_list) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 0feb6d6ce6..0db6a6a995 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -52,6 +52,29 @@ lvgl: - touchscreen_id: tft_touch long_press_repeat_time: 200ms long_press_time: 500ms + + msgboxes: + - id: message_box + close_button: true + title: Messagebox + bg_color: 0xffff + body: + text: This is a sample messagebox + bg_color: 0x808080 + button_style: + bg_color: 0xff00 + border_width: 4 + buttons: + - id: msgbox_button + text: Button + - id: msgbox_apply + text: "Close" + on_click: + then: + - lvgl.widget.hide: message_box + - id: simple_msgbox + title: Simple + pages: - id: page1 on_load: @@ -98,6 +121,7 @@ lvgl: - lvgl.update: disp_bg_color: 0xffff00 disp_bg_image: cat_image + - lvgl.widget.show: message_box - label: text: "Hello shiny day" text_color: 0xFFFFFF From 32995a352bc224da579056969192c250717b8b51 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 9 Sep 2024 06:05:09 +0100 Subject: [PATCH 0325/1052] libretiny: Allow specifying version of explicitly imported sources (#7408) --- esphome/components/libretiny/__init__.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index a8034f8fab..a640e2e9b9 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -1,10 +1,6 @@ import json import logging -from os.path import ( - dirname, - isfile, - join, -) +from os.path import dirname, isfile, join import esphome.codegen as cg import esphome.config_validation as cv @@ -282,10 +278,10 @@ async def component_to_code(config): # if platform version is a valid version constraint, prefix the default package framework = config[CONF_FRAMEWORK] cv.platformio_version_constraint(framework[CONF_VERSION]) - if str(framework[CONF_VERSION]) != "0.0.0": - cg.add_platformio_option("platform", f"libretiny @ {framework[CONF_VERSION]}") - elif framework[CONF_SOURCE]: + if framework[CONF_SOURCE]: cg.add_platformio_option("platform", framework[CONF_SOURCE]) + elif str(framework[CONF_VERSION]) != "0.0.0": + cg.add_platformio_option("platform", f"libretiny @ {framework[CONF_VERSION]}") else: cg.add_platformio_option("platform", "libretiny") From 7a93dde5d4611c22ee9471640f9c7e848bbe7d7e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 9 Sep 2024 06:05:19 +0100 Subject: [PATCH 0326/1052] [libretiny] Report version 1.7.0 for 'dev' and 'latest' (#7415) --- esphome/components/libretiny/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index a640e2e9b9..9ba889f493 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -172,9 +172,10 @@ def _notify_old_style(config): # NOTE: Keep this in mind when updating the recommended version: # * For all constants below, update platformio.ini (in this repo) +# The dev and latest branches will be at *least* this version, which is what matters. ARDUINO_VERSIONS = { - "dev": (cv.Version(0, 0, 0), "https://github.com/libretiny-eu/libretiny.git"), - "latest": (cv.Version(0, 0, 0), None), + "dev": (cv.Version(1, 7, 0), "https://github.com/libretiny-eu/libretiny.git"), + "latest": (cv.Version(1, 7, 0), "libretiny"), "recommended": (cv.Version(1, 5, 1), None), } From c90dcfc0ca681ffeb025cf520a2d9ac458c0e510 Mon Sep 17 00:00:00 2001 From: Anton Viktorov Date: Mon, 9 Sep 2024 19:25:37 +0200 Subject: [PATCH 0327/1052] LTR-501, LTR-301, LTR-558 Series of Lite-On Light (ALS) and Proximity(PS) sensors (#6262) Co-authored-by: root Co-authored-by: Keith Burzinski --- CODEOWNERS | 1 + esphome/components/ltr501/__init__.py | 1 + esphome/components/ltr501/ltr501.cpp | 542 ++++++++++++++++++ esphome/components/ltr501/ltr501.h | 184 ++++++ .../components/ltr501/ltr_definitions_501.h | 260 +++++++++ esphome/components/ltr501/sensor.py | 274 +++++++++ esphome/components/ltr_als_ps/sensor.py | 4 +- esphome/components/veml7700/sensor.py | 4 +- esphome/const.py | 2 + tests/components/ltr501/common.yaml | 9 + tests/components/ltr501/test.esp32-ard.yaml | 6 + .../components/ltr501/test.esp32-c3-ard.yaml | 6 + .../components/ltr501/test.esp32-c3-idf.yaml | 6 + tests/components/ltr501/test.esp32-idf.yaml | 6 + tests/components/ltr501/test.esp8266-ard.yaml | 6 + tests/components/ltr501/test.rp2040-ard.yaml | 6 + 16 files changed, 1313 insertions(+), 4 deletions(-) create mode 100644 esphome/components/ltr501/__init__.py create mode 100644 esphome/components/ltr501/ltr501.cpp create mode 100644 esphome/components/ltr501/ltr501.h create mode 100644 esphome/components/ltr501/ltr_definitions_501.h create mode 100644 esphome/components/ltr501/sensor.py create mode 100644 tests/components/ltr501/common.yaml create mode 100644 tests/components/ltr501/test.esp32-ard.yaml create mode 100644 tests/components/ltr501/test.esp32-c3-ard.yaml create mode 100644 tests/components/ltr501/test.esp32-c3-idf.yaml create mode 100644 tests/components/ltr501/test.esp32-idf.yaml create mode 100644 tests/components/ltr501/test.esp8266-ard.yaml create mode 100644 tests/components/ltr501/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 1d4df3ccb8..0b1b88fbc8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -227,6 +227,7 @@ esphome/components/lilygo_t5_47/touchscreen/* @jesserockz esphome/components/lock/* @esphome/core esphome/components/logger/* @esphome/core esphome/components/ltr390/* @latonita @sjtrny +esphome/components/ltr501/* @latonita esphome/components/ltr_als_ps/* @latonita esphome/components/lvgl/* @clydebarrow esphome/components/m5stack_8angle/* @rnauber diff --git a/esphome/components/ltr501/__init__.py b/esphome/components/ltr501/__init__.py new file mode 100644 index 0000000000..dd06cfffea --- /dev/null +++ b/esphome/components/ltr501/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@latonita"] diff --git a/esphome/components/ltr501/ltr501.cpp b/esphome/components/ltr501/ltr501.cpp new file mode 100644 index 0000000000..4f4e26f44f --- /dev/null +++ b/esphome/components/ltr501/ltr501.cpp @@ -0,0 +1,542 @@ +#include "ltr501.h" +#include "esphome/core/application.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +using esphome::i2c::ErrorCode; + +namespace esphome { +namespace ltr501 { + +static const char *const TAG = "ltr501"; + +static const uint8_t MAX_TRIES = 5; +static const uint8_t MAX_SENSITIVITY_ADJUSTMENTS = 10; + +struct GainTimePair { + AlsGain501 gain; + IntegrationTime501 time; +}; + +bool operator==(const GainTimePair &lhs, const GainTimePair &rhs) { + return lhs.gain == rhs.gain && lhs.time == rhs.time; +} + +bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs) { + return !(lhs.gain == rhs.gain && lhs.time == rhs.time); +} + +template T get_next(const T (&array)[size], const T val) { + size_t i = 0; + size_t idx = -1; + while (idx == -1 && i < size) { + if (array[i] == val) { + idx = i; + break; + } + i++; + } + if (idx == -1 || i + 1 >= size) + return val; + return array[i + 1]; +} + +template T get_prev(const T (&array)[size], const T val) { + size_t i = size - 1; + size_t idx = -1; + while (idx == -1 && i > 0) { + if (array[i] == val) { + idx = i; + break; + } + i--; + } + if (idx == -1 || i == 0) + return val; + return array[i - 1]; +} + +static uint16_t get_itime_ms(IntegrationTime501 time) { + static const uint16_t ALS_INT_TIME[4] = {100, 50, 200, 400}; + return ALS_INT_TIME[time & 0b11]; +} + +static uint16_t get_meas_time_ms(MeasurementRepeatRate rate) { + static const uint16_t ALS_MEAS_RATE[8] = {50, 100, 200, 500, 1000, 2000, 2000, 2000}; + return ALS_MEAS_RATE[rate & 0b111]; +} + +static float get_gain_coeff(AlsGain501 gain) { return gain == AlsGain501::GAIN_1 ? 1.0f : 150.0f; } + +static float get_ps_gain_coeff(PsGain501 gain) { + static const float PS_GAIN[4] = {1, 4, 8, 16}; + return PS_GAIN[gain & 0b11]; +} + +void LTRAlsPs501Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up LTR-501/301/558"); + // 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; }); +} + +void LTRAlsPs501Component::dump_config() { + auto get_device_type = [](LtrType typ) { + switch (typ) { + case LtrType::LTR_TYPE_ALS_ONLY: + return "ALS only"; + case LtrType::LTR_TYPE_PS_ONLY: + return "PS only"; + case LtrType::LTR_TYPE_ALS_AND_PS: + return "Als + PS"; + default: + return "Unknown"; + } + }; + + 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_); + + LOG_UPDATE_INTERVAL(this); + + 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_failed()) { + ESP_LOGE(TAG, "Communication with I2C LTR-501/301/558 failed!"); + } +} + +void LTRAlsPs501Component::update() { + if (!this->is_als_()) { + ESP_LOGW(TAG, "Update. ALS data not available. Change configuration to ALS or ALS_PS."); + return; + } + if (this->is_ready() && this->is_als_() && this->state_ == State::IDLE) { + ESP_LOGV(TAG, "Update. Initiating new ALS data collection."); + + this->state_ = this->automatic_mode_enabled_ ? State::COLLECTING_DATA_AUTO : State::WAITING_FOR_DATA; + + this->als_readings_.ch0 = 0; + this->als_readings_.ch1 = 0; + this->als_readings_.gain = this->gain_; + this->als_readings_.integration_time = this->integration_time_; + this->als_readings_.lux = 0; + this->als_readings_.number_of_adjustments = 0; + + } else { + ESP_LOGV(TAG, "Update. Component not ready yet."); + } +} + +void LTRAlsPs501Component::loop() { + ErrorCode err = i2c::ERROR_OK; + static uint8_t tries{0}; + + switch (this->state_) { + case State::DELAYED_SETUP: + err = this->write(nullptr, 0); + if (err != i2c::ERROR_OK) { + ESP_LOGW(TAG, "i2c connection failed"); + this->mark_failed(); + } + this->configure_reset_(); + if (this->is_als_()) { + this->configure_als_(); + this->configure_integration_time_(this->integration_time_); + } + if (this->is_ps_()) { + this->configure_ps_(); + } + + this->state_ = State::IDLE; + break; + + case State::IDLE: + if (this->is_ps_()) { + this->check_and_trigger_ps_(); + } + break; + + case State::WAITING_FOR_DATA: + if (this->is_als_data_ready_(this->als_readings_) == DataAvail::DATA_OK) { + tries = 0; + ESP_LOGV(TAG, "Reading sensor data assuming gain = %.0fx, time = %d ms", + get_gain_coeff(this->als_readings_.gain), get_itime_ms(this->als_readings_.integration_time)); + this->read_sensor_data_(this->als_readings_); + this->apply_lux_calculation_(this->als_readings_); + this->state_ = State::DATA_COLLECTED; + } else if (tries >= MAX_TRIES) { + ESP_LOGW(TAG, "Can't get data after several tries. Aborting."); + tries = 0; + this->status_set_warning(); + this->state_ = State::IDLE; + return; + } else { + tries++; + } + break; + + case State::COLLECTING_DATA_AUTO: + case State::DATA_COLLECTED: + // first measurement in auto mode (COLLECTING_DATA_AUTO state) require device reconfiguration + if (this->state_ == State::COLLECTING_DATA_AUTO || this->are_adjustments_required_(this->als_readings_)) { + this->state_ = State::ADJUSTMENT_IN_PROGRESS; + ESP_LOGD(TAG, "Reconfiguring sensitivity: gain = %.0fx, time = %d ms", get_gain_coeff(this->als_readings_.gain), + get_itime_ms(this->als_readings_.integration_time)); + this->configure_integration_time_(this->als_readings_.integration_time); + this->configure_gain_(this->als_readings_.gain); + // if sensitivity adjustment needed - need to wait for first data samples after setting new parameters + this->set_timeout(2 * get_meas_time_ms(this->repeat_rate_), + [this]() { this->state_ = State::WAITING_FOR_DATA; }); + } else { + this->state_ = State::READY_TO_PUBLISH; + } + break; + + case State::ADJUSTMENT_IN_PROGRESS: + // nothing to be done, just waiting for the timeout + break; + + case State::READY_TO_PUBLISH: + this->publish_data_part_1_(this->als_readings_); + this->state_ = State::KEEP_PUBLISHING; + break; + + case State::KEEP_PUBLISHING: + this->publish_data_part_2_(this->als_readings_); + this->status_clear_warning(); + this->state_ = State::IDLE; + break; + + default: + break; + } +} + +void LTRAlsPs501Component::check_and_trigger_ps_() { + static uint32_t last_high_trigger_time{0}; + static uint32_t last_low_trigger_time{0}; + uint16_t ps_data = this->read_ps_data_(); + uint32_t now = millis(); + + if (ps_data != this->ps_readings_) { + this->ps_readings_ = ps_data; + // Higher values - object is closer to sensor + if (ps_data > this->ps_threshold_high_ && now - last_high_trigger_time >= this->ps_cooldown_time_s_ * 1000) { + last_high_trigger_time = now; + ESP_LOGD(TAG, "Proximity high threshold triggered. Value = %d, Trigger level = %d", ps_data, + this->ps_threshold_high_); + this->on_ps_high_trigger_callback_.call(); + } else if (ps_data < this->ps_threshold_low_ && now - last_low_trigger_time >= this->ps_cooldown_time_s_ * 1000) { + last_low_trigger_time = now; + ESP_LOGD(TAG, "Proximity low threshold triggered. Value = %d, Trigger level = %d", ps_data, + this->ps_threshold_low_); + this->on_ps_low_trigger_callback_.call(); + } + } +} + +bool LTRAlsPs501Component::check_part_number_() { + uint8_t manuf_id = this->reg((uint8_t) CommandRegisters::MANUFAC_ID).get(); + if (manuf_id != 0x05) { // 0x05 is Lite-On Semiconductor Corp. ID + ESP_LOGW(TAG, "Unknown manufacturer ID: 0x%02X", manuf_id); + this->mark_failed(); + return false; + } + + // Things getting not really funny here, we can't identify device type by part number ID + // ======================== ========= ===== ================= + // Device Part ID Rev Capabilities + // ======================== ========= ===== ================= + // ltr-558als 0x08 0 als + ps + // ltr-501als 0x08 0 als + ps + // ltr-301als - 0x08 0 als only + + PartIdRegister part_id{0}; + part_id.raw = this->reg((uint8_t) CommandRegisters::PART_ID).get(); + if (part_id.part_number_id != 0x08) { + ESP_LOGW(TAG, "Unknown part number ID: 0x%02X. LTR-501/301 shall have 0x08. It might not work properly.", + part_id.part_number_id); + this->status_set_warning(); + return true; + } + return true; +} + +void LTRAlsPs501Component::configure_reset_() { + ESP_LOGV(TAG, "Resetting"); + + AlsControlRegister501 als_ctrl{0}; + als_ctrl.sw_reset = true; + this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw; + delay(2); + + uint8_t tries = MAX_TRIES; + do { + ESP_LOGV(TAG, "Waiting chip to reset"); + delay(2); + als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get(); + } while (als_ctrl.sw_reset && tries--); // while sw reset bit is on - keep waiting + + if (als_ctrl.sw_reset) { + ESP_LOGW(TAG, "Reset failed"); + } +} + +void LTRAlsPs501Component::configure_als_() { + AlsControlRegister501 als_ctrl{0}; + als_ctrl.sw_reset = false; + als_ctrl.als_mode_active = true; + als_ctrl.gain = this->gain_; + + ESP_LOGV(TAG, "Setting active mode and gain reg 0x%02X", als_ctrl.raw); + this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw; + delay(5); + + uint8_t tries = MAX_TRIES; + do { + 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 + + if (!als_ctrl.als_mode_active) { + ESP_LOGW(TAG, "Failed to activate ALS device"); + } +} + +void LTRAlsPs501Component::configure_ps_() { + PsMeasurementRateRegister ps_meas{0}; + ps_meas.ps_measurement_rate = PsMeasurementRate::PS_MEAS_RATE_50MS; + this->reg((uint8_t) CommandRegisters::PS_MEAS_RATE) = ps_meas.raw; + + PsControlRegister501 ps_ctrl{0}; + ps_ctrl.ps_mode_active = true; + ps_ctrl.ps_mode_xxx = true; + this->reg((uint8_t) CommandRegisters::PS_CONTR) = ps_ctrl.raw; +} + +uint16_t LTRAlsPs501Component::read_ps_data_() { + AlsPsStatusRegister als_status{0}; + als_status.raw = this->reg((uint8_t) CommandRegisters::ALS_PS_STATUS).get(); + if (!als_status.ps_new_data) { + return this->ps_readings_; + } + + uint8_t ps_low = this->reg((uint8_t) CommandRegisters::PS_DATA_0).get(); + PsData1Register ps_high; + ps_high.raw = this->reg((uint8_t) CommandRegisters::PS_DATA_1).get(); + + uint16_t val = encode_uint16(ps_high.ps_data_high, ps_low); + return val; +} + +void LTRAlsPs501Component::configure_gain_(AlsGain501 gain) { + AlsControlRegister501 als_ctrl{0}; + als_ctrl.als_mode_active = true; + als_ctrl.gain = gain; + this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw; + delay(2); + + AlsControlRegister501 read_als_ctrl{0}; + read_als_ctrl.raw = this->reg((uint8_t) CommandRegisters::ALS_CONTR).get(); + if (read_als_ctrl.gain != gain) { + ESP_LOGW(TAG, "Failed to set gain. We will try one more time."); + this->reg((uint8_t) CommandRegisters::ALS_CONTR) = als_ctrl.raw; + delay(2); + } +} + +void LTRAlsPs501Component::configure_integration_time_(IntegrationTime501 time) { + MeasurementRateRegister501 meas{0}; + meas.measurement_repeat_rate = this->repeat_rate_; + meas.integration_time = time; + this->reg((uint8_t) CommandRegisters::MEAS_RATE) = meas.raw; + delay(2); + + MeasurementRateRegister501 read_meas{0}; + read_meas.raw = this->reg((uint8_t) CommandRegisters::MEAS_RATE).get(); + if (read_meas.integration_time != time) { + ESP_LOGW(TAG, "Failed to set integration time. We will try one more time."); + this->reg((uint8_t) CommandRegisters::MEAS_RATE) = meas.raw; + delay(2); + } +} + +DataAvail LTRAlsPs501Component::is_als_data_ready_(AlsReadings &data) { + AlsPsStatusRegister als_status{0}; + als_status.raw = this->reg((uint8_t) CommandRegisters::ALS_PS_STATUS).get(); + if (!als_status.als_new_data) + return DataAvail::NO_DATA; + ESP_LOGV(TAG, "Data ready, reported gain is %.0fx", get_gain_coeff(als_status.gain)); + if (data.gain != als_status.gain) { + ESP_LOGW(TAG, "Actual gain differs from requested (%.0f)", get_gain_coeff(data.gain)); + return DataAvail::BAD_DATA; + } + data.gain = als_status.gain; + return DataAvail::DATA_OK; +} + +void LTRAlsPs501Component::read_sensor_data_(AlsReadings &data) { + data.ch1 = 0; + data.ch0 = 0; + uint8_t ch1_0 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH1_0).get(); + uint8_t ch1_1 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH1_1).get(); + uint8_t ch0_0 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH0_0).get(); + uint8_t ch0_1 = this->reg((uint8_t) CommandRegisters::ALS_DATA_CH0_1).get(); + data.ch1 = encode_uint16(ch1_1, ch1_0); + data.ch0 = encode_uint16(ch0_1, ch0_0); + + ESP_LOGD(TAG, "Got sensor data: CH1 = %d, CH0 = %d", data.ch1, data.ch0); +} + +bool LTRAlsPs501Component::are_adjustments_required_(AlsReadings &data) { + if (!this->automatic_mode_enabled_) + return false; + + // sometimes sensors fail to change sensitivity. this prevents us from infinite loop + if (data.number_of_adjustments++ > MAX_SENSITIVITY_ADJUSTMENTS) { + ESP_LOGW(TAG, "Too many sensitivity adjustments done. Something wrong with the sensor. Stopping."); + return false; + } + + ESP_LOGV(TAG, "Adjusting sensitivity, run #%d", data.number_of_adjustments); + + // available combinations of gain and integration times: + static const GainTimePair GAIN_TIME_PAIRS[] = { + {AlsGain501::GAIN_1, INTEGRATION_TIME_50MS}, {AlsGain501::GAIN_1, INTEGRATION_TIME_100MS}, + {AlsGain501::GAIN_150, INTEGRATION_TIME_100MS}, {AlsGain501::GAIN_150, INTEGRATION_TIME_200MS}, + {AlsGain501::GAIN_150, INTEGRATION_TIME_400MS}, + }; + + GainTimePair current_pair = {data.gain, data.integration_time}; + + // Here comes funky business with this sensor. it has no internal error checking mechanism + // as in later versions (LTR-303/329/559/..) and sensor gets overwhelmed when saturated + // and readings are strange. We only check high sensitivity mode for now. + // Nothing is documented and it is a result of real-world testing. + if (data.gain == AlsGain501::GAIN_150) { + // when sensor is saturated it returns various crazy numbers + // CH1 = 1, CH0 = 0 + if (data.ch1 == 1 && data.ch0 == 0) { + ESP_LOGV(TAG, "Looks like sensor got saturated (?) CH1 = 1, CH0 = 0, Gain 150x"); + // fake saturation + data.ch0 = 0xffff; + data.ch1 = 0xffff; + } else if (data.ch1 == 65535 && data.ch0 == 0) { + ESP_LOGV(TAG, "Looks like sensor got saturated (?) CH1 = 65535, CH0 = 0, Gain 150x"); + data.ch0 = 0xffff; + } else if (data.ch1 > 1000 && data.ch0 == 0) { + ESP_LOGV(TAG, "Looks like sensor got saturated (?) CH1 = %d, CH0 = 0, Gain 150x", data.ch1); + data.ch0 = 0xffff; + } + } + + static const uint16_t LOW_INTENSITY_THRESHOLD_1 = 100; + static const uint16_t LOW_INTENSITY_THRESHOLD_200 = 2000; + static const uint16_t HIGH_INTENSITY_THRESHOLD = 25000; + + if (data.ch0 <= (data.gain == AlsGain501::GAIN_1 ? LOW_INTENSITY_THRESHOLD_1 : LOW_INTENSITY_THRESHOLD_200) || + (data.gain == AlsGain501::GAIN_1 && data.lux < 320)) { + GainTimePair next_pair = get_next(GAIN_TIME_PAIRS, current_pair); + if (next_pair != current_pair) { + data.gain = next_pair.gain; + data.integration_time = next_pair.time; + ESP_LOGV(TAG, "Low illuminance. Increasing sensitivity."); + return true; + } + + } else if (data.ch0 >= HIGH_INTENSITY_THRESHOLD || data.ch1 >= HIGH_INTENSITY_THRESHOLD) { + GainTimePair prev_pair = get_prev(GAIN_TIME_PAIRS, current_pair); + if (prev_pair != current_pair) { + data.gain = prev_pair.gain; + data.integration_time = prev_pair.time; + ESP_LOGV(TAG, "High illuminance. Decreasing sensitivity."); + return true; + } + } else { + ESP_LOGD(TAG, "Illuminance is good enough."); + return false; + } + ESP_LOGD(TAG, "Can't adjust sensitivity anymore."); + return false; +} + +void LTRAlsPs501Component::apply_lux_calculation_(AlsReadings &data) { + if ((data.ch0 == 0xFFFF) || (data.ch1 == 0xFFFF)) { + ESP_LOGW(TAG, "Sensors got saturated"); + data.lux = 0.0f; + return; + } + + if ((data.ch0 == 0x0000) && (data.ch1 == 0x0000)) { + ESP_LOGW(TAG, "Sensors blacked out"); + data.lux = 0.0f; + return; + } + + float ch0 = data.ch0; + float ch1 = data.ch1; + float ratio = ch1 / (ch0 + ch1); + float als_gain = get_gain_coeff(data.gain); + float als_time = ((float) get_itime_ms(data.integration_time)) / 100.0f; + float inv_pfactor = this->glass_attenuation_factor_; + float lux = 0.0f; + + // method from + // https://github.com/fards/Ainol_fire_kernel/blob/83832cf8a3082fd8e963230f4b1984479d1f1a84/customer/drivers/lightsensor/ltr501als.c#L295 + + if (ratio < 0.45) { + lux = 1.7743 * ch0 + 1.1059 * ch1; + } else if (ratio < 0.64) { + lux = 3.7725 * ch0 - 1.3363 * ch1; + } else if (ratio < 0.85) { + lux = 1.6903 * ch0 - 0.1693 * ch1; + } else { + ESP_LOGW(TAG, "Impossible ch1/(ch0 + ch1) ratio"); + lux = 0.0f; + } + + lux = inv_pfactor * lux / als_gain / als_time; + data.lux = lux; + + ESP_LOGD(TAG, "Lux calculation: ratio %.3f, gain %.0fx, int time %.1f, inv_pfactor %.3f, lux %.3f", ratio, als_gain, + als_time, inv_pfactor, lux); +} + +void LTRAlsPs501Component::publish_data_part_1_(AlsReadings &data) { + if (this->proximity_counts_sensor_ != nullptr) { + this->proximity_counts_sensor_->publish_state(this->ps_readings_); + } + if (this->ambient_light_sensor_ != nullptr) { + this->ambient_light_sensor_->publish_state(data.lux); + } + if (this->infrared_counts_sensor_ != nullptr) { + this->infrared_counts_sensor_->publish_state(data.ch1); + } + if (this->full_spectrum_counts_sensor_ != nullptr) { + this->full_spectrum_counts_sensor_->publish_state(data.ch0); + } +} + +void LTRAlsPs501Component::publish_data_part_2_(AlsReadings &data) { + if (this->actual_gain_sensor_ != nullptr) { + this->actual_gain_sensor_->publish_state(get_gain_coeff(data.gain)); + } + if (this->actual_integration_time_sensor_ != nullptr) { + this->actual_integration_time_sensor_->publish_state(get_itime_ms(data.integration_time)); + } +} +} // namespace ltr501 +} // namespace esphome diff --git a/esphome/components/ltr501/ltr501.h b/esphome/components/ltr501/ltr501.h new file mode 100644 index 0000000000..07b69fa0d0 --- /dev/null +++ b/esphome/components/ltr501/ltr501.h @@ -0,0 +1,184 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/optional.h" +#include "esphome/core/automation.h" + +#include "ltr_definitions_501.h" + +namespace esphome { +namespace ltr501 { + +enum DataAvail : uint8_t { NO_DATA, BAD_DATA, DATA_OK }; + +enum LtrType : uint8_t { + LTR_TYPE_UNKNOWN = 0, + LTR_TYPE_ALS_ONLY = 1, + LTR_TYPE_PS_ONLY = 2, + LTR_TYPE_ALS_AND_PS = 3, +}; + +class LTRAlsPs501Component : public PollingComponent, public i2c::I2CDevice { + public: + // + // EspHome framework functions + // + float get_setup_priority() const override { return setup_priority::DATA; } + void setup() override; + void dump_config() override; + void update() override; + void loop() override; + + // Configuration setters : General + // + void set_ltr_type(LtrType type) { this->ltr_type_ = type; } + + // Configuration setters : ALS + // + void set_als_auto_mode(bool enable) { this->automatic_mode_enabled_ = enable; } + void set_als_gain(AlsGain501 gain) { this->gain_ = gain; } + void set_als_integration_time(IntegrationTime501 time) { this->integration_time_ = time; } + void set_als_meas_repeat_rate(MeasurementRepeatRate rate) { this->repeat_rate_ = rate; } + void set_als_glass_attenuation_factor(float factor) { this->glass_attenuation_factor_ = factor; } + + // Configuration setters : PS + // + void set_ps_high_threshold(uint16_t threshold) { this->ps_threshold_high_ = threshold; } + void set_ps_low_threshold(uint16_t threshold) { this->ps_threshold_low_ = threshold; } + void set_ps_cooldown_time_s(uint16_t time) { this->ps_cooldown_time_s_ = time; } + void set_ps_gain(PsGain501 gain) { this->ps_gain_ = gain; } + + // Sensors setters + // + void set_ambient_light_sensor(sensor::Sensor *sensor) { this->ambient_light_sensor_ = sensor; } + void set_full_spectrum_counts_sensor(sensor::Sensor *sensor) { this->full_spectrum_counts_sensor_ = sensor; } + void set_infrared_counts_sensor(sensor::Sensor *sensor) { this->infrared_counts_sensor_ = sensor; } + void set_actual_gain_sensor(sensor::Sensor *sensor) { this->actual_gain_sensor_ = sensor; } + void set_actual_integration_time_sensor(sensor::Sensor *sensor) { this->actual_integration_time_sensor_ = sensor; } + void set_proximity_counts_sensor(sensor::Sensor *sensor) { this->proximity_counts_sensor_ = sensor; } + + protected: + // + // Internal state machine, used to split all the actions into + // small steps in loop() to make sure we are not blocking execution + // + enum class State : uint8_t { + NOT_INITIALIZED, + DELAYED_SETUP, + IDLE, + WAITING_FOR_DATA, + COLLECTING_DATA_AUTO, + DATA_COLLECTED, + ADJUSTMENT_IN_PROGRESS, + READY_TO_PUBLISH, + KEEP_PUBLISHING + } state_{State::NOT_INITIALIZED}; + + LtrType ltr_type_{LtrType::LTR_TYPE_ALS_ONLY}; + + // + // Current measurements data + // + struct AlsReadings { + uint16_t ch0{0}; + uint16_t ch1{0}; + AlsGain501 gain{AlsGain501::GAIN_1}; + IntegrationTime501 integration_time{IntegrationTime501::INTEGRATION_TIME_100MS}; + float lux{0.0f}; + uint8_t number_of_adjustments{0}; + } als_readings_; + uint16_t ps_readings_{0xfffe}; + + inline bool is_als_() const { + return this->ltr_type_ == LtrType::LTR_TYPE_ALS_ONLY || this->ltr_type_ == LtrType::LTR_TYPE_ALS_AND_PS; + } + inline bool is_ps_() const { + return this->ltr_type_ == LtrType::LTR_TYPE_PS_ONLY || this->ltr_type_ == LtrType::LTR_TYPE_ALS_AND_PS; + } + + // + // Device interaction and data manipulation + // + bool check_part_number_(); + + void configure_reset_(); + void configure_als_(); + void configure_integration_time_(IntegrationTime501 time); + void configure_gain_(AlsGain501 gain); + DataAvail is_als_data_ready_(AlsReadings &data); + void read_sensor_data_(AlsReadings &data); + bool are_adjustments_required_(AlsReadings &data); + void apply_lux_calculation_(AlsReadings &data); + void publish_data_part_1_(AlsReadings &data); + void publish_data_part_2_(AlsReadings &data); + + void configure_ps_(); + uint16_t read_ps_data_(); + void check_and_trigger_ps_(); + + // + // Component configuration + // + bool automatic_mode_enabled_{false}; + AlsGain501 gain_{AlsGain501::GAIN_1}; + IntegrationTime501 integration_time_{IntegrationTime501::INTEGRATION_TIME_100MS}; + MeasurementRepeatRate repeat_rate_{MeasurementRepeatRate::REPEAT_RATE_500MS}; + float glass_attenuation_factor_{1.0}; + + uint16_t ps_cooldown_time_s_{5}; + PsGain501 ps_gain_{PsGain501::PS_GAIN_1}; + uint16_t ps_threshold_high_{0xffff}; + uint16_t ps_threshold_low_{0x0000}; + + // + // Sensors for publishing data + // + sensor::Sensor *infrared_counts_sensor_{nullptr}; // direct reading CH1, infrared only + sensor::Sensor *full_spectrum_counts_sensor_{nullptr}; // direct reading CH0, infrared + visible light + sensor::Sensor *ambient_light_sensor_{nullptr}; // calculated lux + sensor::Sensor *actual_gain_sensor_{nullptr}; // actual gain of reading + sensor::Sensor *actual_integration_time_sensor_{nullptr}; // actual integration time + sensor::Sensor *proximity_counts_sensor_{nullptr}; // proximity sensor + + bool is_any_als_sensor_enabled_() const { + return this->ambient_light_sensor_ != nullptr || this->full_spectrum_counts_sensor_ != nullptr || + this->infrared_counts_sensor_ != nullptr || this->actual_gain_sensor_ != nullptr || + this->actual_integration_time_sensor_ != nullptr; + } + bool is_any_ps_sensor_enabled_() const { return this->proximity_counts_sensor_ != nullptr; } + + // + // Trigger section for the automations + // + friend class LTRPsHighTrigger; + friend class LTRPsLowTrigger; + + CallbackManager on_ps_high_trigger_callback_; + CallbackManager on_ps_low_trigger_callback_; + + void add_on_ps_high_trigger_callback_(std::function callback) { + this->on_ps_high_trigger_callback_.add(std::move(callback)); + } + + void add_on_ps_low_trigger_callback_(std::function callback) { + this->on_ps_low_trigger_callback_.add(std::move(callback)); + } +}; + +class LTRPsHighTrigger : public Trigger<> { + public: + explicit LTRPsHighTrigger(LTRAlsPs501Component *parent) { + parent->add_on_ps_high_trigger_callback_([this]() { this->trigger(); }); + } +}; + +class LTRPsLowTrigger : public Trigger<> { + public: + explicit LTRPsLowTrigger(LTRAlsPs501Component *parent) { + parent->add_on_ps_low_trigger_callback_([this]() { this->trigger(); }); + } +}; +} // namespace ltr501 +} // namespace esphome diff --git a/esphome/components/ltr501/ltr_definitions_501.h b/esphome/components/ltr501/ltr_definitions_501.h new file mode 100644 index 0000000000..604bd92b68 --- /dev/null +++ b/esphome/components/ltr501/ltr_definitions_501.h @@ -0,0 +1,260 @@ +#pragma once + +#include + +namespace esphome { +namespace ltr501 { + +enum class CommandRegisters : uint8_t { + ALS_CONTR = 0x80, // ALS operation mode control and SW reset + PS_CONTR = 0x81, // PS operation mode control + PS_LED = 0x82, // PS LED pulse frequency control + PS_N_PULSES = 0x83, // PS number of pulses control + PS_MEAS_RATE = 0x84, // PS measurement rate in active mode + MEAS_RATE = 0x85, // ALS measurement rate in active mode + PART_ID = 0x86, // Part Number ID and Revision ID + MANUFAC_ID = 0x87, // Manufacturer ID + ALS_DATA_CH1_0 = 0x88, // ALS measurement CH1 data, lower byte - infrared only + ALS_DATA_CH1_1 = 0x89, // ALS measurement CH1 data, upper byte - infrared only + ALS_DATA_CH0_0 = 0x8A, // ALS measurement CH0 data, lower byte - visible + infrared + ALS_DATA_CH0_1 = 0x8B, // ALS measurement CH0 data, upper byte - visible + infrared + ALS_PS_STATUS = 0x8C, // ALS PS new data status + PS_DATA_0 = 0x8D, // PS measurement data, lower byte + PS_DATA_1 = 0x8E, // PS measurement data, upper byte + ALS_PS_INTERRUPT = 0x8F, // Interrupt status + PS_THRES_UP_0 = 0x90, // PS interrupt upper threshold, lower byte + PS_THRES_UP_1 = 0x91, // PS interrupt upper threshold, upper byte + PS_THRES_LOW_0 = 0x92, // PS interrupt lower threshold, lower byte + PS_THRES_LOW_1 = 0x93, // PS interrupt lower threshold, upper byte + PS_OFFSET_1 = 0x94, // PS offset, upper byte + PS_OFFSET_0 = 0x95, // PS offset, lower byte + // 0x96 - reserved + ALS_THRES_UP_0 = 0x97, // ALS interrupt upper threshold, lower byte + ALS_THRES_UP_1 = 0x98, // ALS interrupt upper threshold, upper byte + ALS_THRES_LOW_0 = 0x99, // ALS interrupt lower threshold, lower byte + ALS_THRES_LOW_1 = 0x9A, // ALS interrupt lower threshold, upper byte + // 0x9B - reserved + // 0x9C - reserved + // 0x9D - reserved + INTERRUPT_PERSIST = 0x9E // Interrupt persistence filter +}; + +// ALS Sensor gain levels +enum AlsGain501 : uint8_t { + GAIN_1 = 0, // GAIN_RANGE_2 // default + GAIN_150 = 1, // GAIN_RANGE_1 +}; +static const uint8_t GAINS_COUNT = 2; + +// ALS Sensor integration times +enum IntegrationTime501 : uint8_t { + INTEGRATION_TIME_100MS = 0, // default + INTEGRATION_TIME_50MS = 1, // only in Dynamic GAIN_RANGE_2 + INTEGRATION_TIME_200MS = 2, // only in Dynamic GAIN_RANGE_1 + INTEGRATION_TIME_400MS = 3, // only in Dynamic GAIN_RANGE_1 +}; +static const uint8_t TIMES_COUNT = 4; + +// ALS Sensor measurement repeat rate +enum MeasurementRepeatRate { + REPEAT_RATE_50MS = 0, + REPEAT_RATE_100MS = 1, + REPEAT_RATE_200MS = 2, + REPEAT_RATE_500MS = 3, // default + REPEAT_RATE_1000MS = 4, + REPEAT_RATE_2000MS = 5 +}; + +// PS Sensor gain levels +enum PsGain501 : uint8_t { + PS_GAIN_1 = 0, // default + PS_GAIN_4 = 1, + PS_GAIN_8 = 2, + PS_GAIN_16 = 3, +}; + +// LED Pulse Modulation Frequency +enum PsLedFreq : uint8_t { + PS_LED_FREQ_30KHZ = 0, + PS_LED_FREQ_40KHZ = 1, + PS_LED_FREQ_50KHZ = 2, + PS_LED_FREQ_60KHZ = 3, // default + PS_LED_FREQ_70KHZ = 4, + PS_LED_FREQ_80KHZ = 5, + PS_LED_FREQ_90KHZ = 6, + PS_LED_FREQ_100KHZ = 7, +}; + +// LED current duty +enum PsLedDuty : uint8_t { + PS_LED_DUTY_25 = 0, + PS_LED_DUTY_50 = 1, // default + PS_LED_DUTY_75 = 2, + PS_LED_DUTY_100 = 3, +}; + +// LED pulsed current level +enum PsLedCurrent : uint8_t { + PS_LED_CURRENT_5MA = 0, + PS_LED_CURRENT_10MA = 1, + PS_LED_CURRENT_20MA = 2, + PS_LED_CURRENT_50MA = 3, // default + PS_LED_CURRENT_100MA = 4, + PS_LED_CURRENT_100MA1 = 5, + PS_LED_CURRENT_100MA2 = 6, + PS_LED_CURRENT_100MA3 = 7, +}; + +// PS measurement rate +enum PsMeasurementRate : uint8_t { + PS_MEAS_RATE_50MS = 0, + PS_MEAS_RATE_70MS = 1, + PS_MEAS_RATE_100MS = 2, // default + PS_MEAS_RATE_200MS = 3, + PS_MEAS_RATE_500MS = 4, + PS_MEAS_RATE_1000MS = 5, + PS_MEAS_RATE_2000MS = 6, + PS_MEAS_RATE_2000MS1 = 7, +}; + +// +// ALS_CONTR Register (0x80) +// +union AlsControlRegister501 { + uint8_t raw; + struct { + bool asl_mode_xxx : 1; + bool als_mode_active : 1; + bool sw_reset : 1; + AlsGain501 gain : 1; + uint8_t reserved : 4; + } __attribute__((packed)); +}; + +// +// PS_CONTR Register (0x81) +// +union PsControlRegister501 { + uint8_t raw; + struct { + bool ps_mode_xxx : 1; + bool ps_mode_active : 1; + PsGain501 ps_gain : 2; + bool reserved_4 : 1; + bool reserved_5 : 1; + bool reserved_6 : 1; + bool reserved_7 : 1; + } __attribute__((packed)); +}; + +// +// PS_LED Register (0x82) +// +union PsLedRegister { + uint8_t raw; + struct { + PsLedCurrent ps_led_current : 3; + PsLedDuty ps_led_duty : 2; + PsLedFreq ps_led_freq : 3; + } __attribute__((packed)); +}; + +// +// PS_N_PULSES Register (0x83) +// +union PsNPulsesRegister501 { + uint8_t raw; + uint8_t number_of_pulses; +}; + +// +// PS_MEAS_RATE Register (0x84) +// +union PsMeasurementRateRegister { + uint8_t raw; + struct { + PsMeasurementRate ps_measurement_rate : 4; + uint8_t reserved : 4; + } __attribute__((packed)); +}; + +// +// ALS_MEAS_RATE Register (0x85) +// +union MeasurementRateRegister501 { + uint8_t raw; + struct { + MeasurementRepeatRate measurement_repeat_rate : 3; + IntegrationTime501 integration_time : 2; + bool reserved_5 : 1; + bool reserved_6 : 1; + bool reserved_7 : 1; + } __attribute__((packed)); +}; + +// +// PART_ID Register (0x86) (Read Only) +// +union PartIdRegister { + uint8_t raw; + struct { + uint8_t part_number_id : 4; + uint8_t revision_id : 4; + } __attribute__((packed)); +}; + +// +// ALS_PS_STATUS Register (0x8C) (Read Only) +// +union AlsPsStatusRegister { + uint8_t raw; + struct { + bool ps_new_data : 1; // 0 - old data, 1 - new data + bool ps_interrupt : 1; // 0 - interrupt signal not active, 1 - interrupt signal active + bool als_new_data : 1; // 0 - old data, 1 - new data + bool als_interrupt : 1; // 0 - interrupt signal not active, 1 - interrupt signal active + AlsGain501 gain : 1; // current ALS gain + bool reserved_5 : 1; + bool reserved_6 : 1; + bool reserved_7 : 1; + } __attribute__((packed)); +}; + +// +// PS_DATA_1 Register (0x8E) (Read Only) +// +union PsData1Register { + uint8_t raw; + struct { + uint8_t ps_data_high : 3; + uint8_t reserved : 4; + bool ps_saturation_flag : 1; + } __attribute__((packed)); +}; + +// +// INTERRUPT Register (0x8F) (Read Only) +// +union InterruptRegister { + uint8_t raw; + struct { + bool ps_interrupt : 1; + bool als_interrupt : 1; + bool interrupt_polarity : 1; // 0 - active low (default), 1 - active high + uint8_t reserved : 5; + } __attribute__((packed)); +}; + +// +// INTERRUPT_PERSIST Register (0x9E) +// +union InterruptPersistRegister { + uint8_t raw; + struct { + uint8_t als_persist : 4; // 0 - every ALS cycle, 1 - every 2 ALS cycles, ... 15 - every 16 ALS cycles + uint8_t ps_persist : 4; // 0 - every PS cycle, 1 - every 2 PS cycles, ... 15 - every 16 PS cycles + } __attribute__((packed)); +}; + +} // namespace ltr501 +} // namespace esphome diff --git a/esphome/components/ltr501/sensor.py b/esphome/components/ltr501/sensor.py new file mode 100644 index 0000000000..153d1b3ad1 --- /dev/null +++ b/esphome/components/ltr501/sensor.py @@ -0,0 +1,274 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.components import i2c, sensor +from esphome.const import ( + CONF_ACTUAL_GAIN, + CONF_ACTUAL_INTEGRATION_TIME, + CONF_AMBIENT_LIGHT, + CONF_AUTO_MODE, + CONF_FULL_SPECTRUM_COUNTS, + CONF_GAIN, + CONF_GLASS_ATTENUATION_FACTOR, + CONF_ID, + CONF_INTEGRATION_TIME, + CONF_NAME, + CONF_REPEAT, + CONF_TRIGGER_ID, + CONF_TYPE, + DEVICE_CLASS_DISTANCE, + DEVICE_CLASS_ILLUMINANCE, + ICON_BRIGHTNESS_5, + ICON_BRIGHTNESS_6, + ICON_TIMER, + STATE_CLASS_MEASUREMENT, + UNIT_LUX, + UNIT_MILLISECOND, +) + +CODEOWNERS = ["@latonita"] +DEPENDENCIES = ["i2c"] + +CONF_INFRARED_COUNTS = "infrared_counts" +CONF_ON_PS_HIGH_THRESHOLD = "on_ps_high_threshold" +CONF_ON_PS_LOW_THRESHOLD = "on_ps_low_threshold" +CONF_PS_COOLDOWN = "ps_cooldown" +CONF_PS_COUNTS = "ps_counts" +CONF_PS_GAIN = "ps_gain" +CONF_PS_HIGH_THRESHOLD = "ps_high_threshold" +CONF_PS_LOW_THRESHOLD = "ps_low_threshold" +ICON_BRIGHTNESS_7 = "mdi:brightness-7" +ICON_GAIN = "mdi:multiplication" +ICON_PROXIMITY = "mdi:hand-wave-outline" +UNIT_COUNTS = "#" + +ltr501_ns = cg.esphome_ns.namespace("ltr501") + +LTRAlsPsComponent = ltr501_ns.class_( + "LTRAlsPs501Component", cg.PollingComponent, i2c.I2CDevice +) + +LtrType = ltr501_ns.enum("LtrType") +LTR_TYPES = { + "ALS": LtrType.LTR_TYPE_ALS_ONLY, + "PS": LtrType.LTR_TYPE_PS_ONLY, + "ALS_PS": LtrType.LTR_TYPE_ALS_AND_PS, +} + +AlsGain = ltr501_ns.enum("AlsGain501") +ALS_GAINS = { + "1X": AlsGain.GAIN_1, + "150X": AlsGain.GAIN_150, +} + +IntegrationTime = ltr501_ns.enum("IntegrationTime501") +INTEGRATION_TIMES = { + 50: IntegrationTime.INTEGRATION_TIME_50MS, + 100: IntegrationTime.INTEGRATION_TIME_100MS, + 200: IntegrationTime.INTEGRATION_TIME_200MS, + 400: IntegrationTime.INTEGRATION_TIME_400MS, +} + +MeasurementRepeatRate = ltr501_ns.enum("MeasurementRepeatRate") +MEASUREMENT_REPEAT_RATES = { + 50: MeasurementRepeatRate.REPEAT_RATE_50MS, + 100: MeasurementRepeatRate.REPEAT_RATE_100MS, + 200: MeasurementRepeatRate.REPEAT_RATE_200MS, + 500: MeasurementRepeatRate.REPEAT_RATE_500MS, + 1000: MeasurementRepeatRate.REPEAT_RATE_1000MS, + 2000: MeasurementRepeatRate.REPEAT_RATE_2000MS, +} + +PsGain = ltr501_ns.enum("PsGain501") +PS_GAINS = { + "1X": PsGain.PS_GAIN_1, + "4X": PsGain.PS_GAIN_4, + "8X": PsGain.PS_GAIN_8, + "16X": PsGain.PS_GAIN_16, +} + +LTRPsHighTrigger = ltr501_ns.class_("LTRPsHighTrigger", automation.Trigger.template()) +LTRPsLowTrigger = ltr501_ns.class_("LTRPsLowTrigger", automation.Trigger.template()) + + +def validate_integration_time(value): + value = cv.positive_time_period_milliseconds(value).total_milliseconds + return cv.enum(INTEGRATION_TIMES, int=True)(value) + + +def validate_repeat_rate(value): + value = cv.positive_time_period_milliseconds(value).total_milliseconds + return cv.enum(MEASUREMENT_REPEAT_RATES, int=True)(value) + + +def validate_time_and_repeat_rate(config): + integraton_time = config[CONF_INTEGRATION_TIME] + repeat_rate = config[CONF_REPEAT] + if integraton_time > repeat_rate: + raise cv.Invalid( + f"Measurement repeat rate ({repeat_rate}ms) shall be greater or equal to integration time ({integraton_time}ms)" + ) + return config + + +def validate_als_gain_and_integration_time(config): + integraton_time = config[CONF_INTEGRATION_TIME] + if config[CONF_GAIN] == "1X" and integraton_time > 100: + raise cv.Invalid( + "ALS gain 1X can only be used with integration time 50ms or 100ms" + ) + if config[CONF_GAIN] == "200X" and integraton_time == 50: + raise cv.Invalid("ALS gain 200X can not be used with integration time 50ms") + return config + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(LTRAlsPsComponent), + cv.Optional(CONF_TYPE, default="ALS_PS"): cv.enum(LTR_TYPES, upper=True), + cv.Optional(CONF_AUTO_MODE, default=True): cv.boolean, + cv.Optional(CONF_GAIN, default="1X"): cv.enum(ALS_GAINS, upper=True), + cv.Optional( + CONF_INTEGRATION_TIME, default="100ms" + ): validate_integration_time, + cv.Optional(CONF_REPEAT, default="500ms"): validate_repeat_rate, + cv.Optional(CONF_GLASS_ATTENUATION_FACTOR, default=1.0): cv.float_range( + min=1.0 + ), + cv.Optional( + CONF_PS_COOLDOWN, default="5s" + ): cv.positive_time_period_seconds, + cv.Optional(CONF_PS_GAIN, default="1X"): cv.enum(PS_GAINS, upper=True), + cv.Optional(CONF_PS_HIGH_THRESHOLD, default=65535): cv.int_range( + min=0, max=65535 + ), + cv.Optional(CONF_PS_LOW_THRESHOLD, default=0): cv.int_range( + min=0, max=65535 + ), + cv.Optional(CONF_ON_PS_HIGH_THRESHOLD): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsHighTrigger), + } + ), + cv.Optional(CONF_ON_PS_LOW_THRESHOLD): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LTRPsLowTrigger), + } + ), + cv.Optional(CONF_AMBIENT_LIGHT): cv.maybe_simple_value( + sensor.sensor_schema( + unit_of_measurement=UNIT_LUX, + icon=ICON_BRIGHTNESS_6, + accuracy_decimals=1, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + cv.Optional(CONF_INFRARED_COUNTS): cv.maybe_simple_value( + sensor.sensor_schema( + unit_of_measurement=UNIT_COUNTS, + icon=ICON_BRIGHTNESS_5, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + cv.Optional(CONF_FULL_SPECTRUM_COUNTS): cv.maybe_simple_value( + sensor.sensor_schema( + unit_of_measurement=UNIT_COUNTS, + icon=ICON_BRIGHTNESS_7, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + cv.Optional(CONF_PS_COUNTS): cv.maybe_simple_value( + sensor.sensor_schema( + unit_of_measurement=UNIT_COUNTS, + icon=ICON_PROXIMITY, + accuracy_decimals=0, + device_class=DEVICE_CLASS_DISTANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + cv.Optional(CONF_ACTUAL_GAIN): cv.maybe_simple_value( + sensor.sensor_schema( + icon=ICON_GAIN, + accuracy_decimals=0, + device_class=DEVICE_CLASS_ILLUMINANCE, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + cv.Optional(CONF_ACTUAL_INTEGRATION_TIME): cv.maybe_simple_value( + sensor.sensor_schema( + unit_of_measurement=UNIT_MILLISECOND, + icon=ICON_TIMER, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + key=CONF_NAME, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x23)), + validate_time_and_repeat_rate, + validate_als_gain_and_integration_time, +) + + +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 als_config := config.get(CONF_AMBIENT_LIGHT): + sens = await sensor.new_sensor(als_config) + cg.add(var.set_ambient_light_sensor(sens)) + + if infrared_cnt_config := config.get(CONF_INFRARED_COUNTS): + sens = await sensor.new_sensor(infrared_cnt_config) + cg.add(var.set_infrared_counts_sensor(sens)) + + if full_spect_cnt_config := config.get(CONF_FULL_SPECTRUM_COUNTS): + sens = await sensor.new_sensor(full_spect_cnt_config) + cg.add(var.set_full_spectrum_counts_sensor(sens)) + + if act_gain_config := config.get(CONF_ACTUAL_GAIN): + sens = await sensor.new_sensor(act_gain_config) + cg.add(var.set_actual_gain_sensor(sens)) + + if act_itime_config := config.get(CONF_ACTUAL_INTEGRATION_TIME): + sens = await sensor.new_sensor(act_itime_config) + cg.add(var.set_actual_integration_time_sensor(sens)) + + if prox_cnt_config := config.get(CONF_PS_COUNTS): + sens = await sensor.new_sensor(prox_cnt_config) + cg.add(var.set_proximity_counts_sensor(sens)) + + for prox_high_tr in config.get(CONF_ON_PS_HIGH_THRESHOLD, []): + trigger = cg.new_Pvariable(prox_high_tr[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], prox_high_tr) + + for prox_low_tr in config.get(CONF_ON_PS_LOW_THRESHOLD, []): + trigger = cg.new_Pvariable(prox_low_tr[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], prox_low_tr) + + cg.add(var.set_ltr_type(config[CONF_TYPE])) + + cg.add(var.set_als_auto_mode(config[CONF_AUTO_MODE])) + cg.add(var.set_als_gain(config[CONF_GAIN])) + cg.add(var.set_als_integration_time(config[CONF_INTEGRATION_TIME])) + cg.add(var.set_als_meas_repeat_rate(config[CONF_REPEAT])) + cg.add(var.set_als_glass_attenuation_factor(config[CONF_GLASS_ATTENUATION_FACTOR])) + + cg.add(var.set_ps_cooldown_time_s(config[CONF_PS_COOLDOWN])) + cg.add(var.set_ps_gain(config[CONF_PS_GAIN])) + cg.add(var.set_ps_high_threshold(config[CONF_PS_HIGH_THRESHOLD])) + cg.add(var.set_ps_low_threshold(config[CONF_PS_LOW_THRESHOLD])) diff --git a/esphome/components/ltr_als_ps/sensor.py b/esphome/components/ltr_als_ps/sensor.py index ac9f7e6788..e9a5264941 100644 --- a/esphome/components/ltr_als_ps/sensor.py +++ b/esphome/components/ltr_als_ps/sensor.py @@ -4,8 +4,10 @@ from esphome import automation from esphome.components import i2c, sensor from esphome.const import ( CONF_ACTUAL_GAIN, + CONF_ACTUAL_INTEGRATION_TIME, CONF_AMBIENT_LIGHT, CONF_AUTO_MODE, + CONF_FULL_SPECTRUM_COUNTS, CONF_GAIN, CONF_GLASS_ATTENUATION_FACTOR, CONF_ID, @@ -27,8 +29,6 @@ from esphome.const import ( CODEOWNERS = ["@latonita"] DEPENDENCIES = ["i2c"] -CONF_ACTUAL_INTEGRATION_TIME = "actual_integration_time" -CONF_FULL_SPECTRUM_COUNTS = "full_spectrum_counts" CONF_INFRARED_COUNTS = "infrared_counts" CONF_ON_PS_HIGH_THRESHOLD = "on_ps_high_threshold" CONF_ON_PS_LOW_THRESHOLD = "on_ps_low_threshold" diff --git a/esphome/components/veml7700/sensor.py b/esphome/components/veml7700/sensor.py index 7b0f75e70c..308f1c1c00 100644 --- a/esphome/components/veml7700/sensor.py +++ b/esphome/components/veml7700/sensor.py @@ -3,9 +3,11 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import ( CONF_ACTUAL_GAIN, + CONF_ACTUAL_INTEGRATION_TIME, CONF_AMBIENT_LIGHT, CONF_AUTO_MODE, CONF_FULL_SPECTRUM, + CONF_FULL_SPECTRUM_COUNTS, CONF_GAIN, CONF_GLASS_ATTENUATION_FACTOR, CONF_ID, @@ -28,9 +30,7 @@ UNIT_COUNTS = "#" ICON_MULTIPLICATION = "mdi:multiplication" ICON_BRIGHTNESS_7 = "mdi:brightness-7" -CONF_ACTUAL_INTEGRATION_TIME = "actual_integration_time" CONF_AMBIENT_LIGHT_COUNTS = "ambient_light_counts" -CONF_FULL_SPECTRUM_COUNTS = "full_spectrum_counts" CONF_LUX_COMPENSATION = "lux_compensation" veml7700_ns = cg.esphome_ns.namespace("veml7700") diff --git a/esphome/const.py b/esphome/const.py index 95773630d0..169b11a715 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -44,6 +44,7 @@ CONF_ACTIONS = "actions" CONF_ACTIVE = "active" CONF_ACTIVE_POWER = "active_power" CONF_ACTUAL_GAIN = "actual_gain" +CONF_ACTUAL_INTEGRATION_TIME = "actual_integration_time" CONF_ADDRESS = "address" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" CONF_ADVANCED = "advanced" @@ -323,6 +324,7 @@ CONF_FREQUENCY = "frequency" CONF_FRIENDLY_NAME = "friendly_name" CONF_FROM = "from" CONF_FULL_SPECTRUM = "full_spectrum" +CONF_FULL_SPECTRUM_COUNTS = "full_spectrum_counts" CONF_FULL_UPDATE_EVERY = "full_update_every" CONF_GAIN = "gain" CONF_GAMMA_CORRECT = "gamma_correct" diff --git a/tests/components/ltr501/common.yaml b/tests/components/ltr501/common.yaml new file mode 100644 index 0000000000..b7074f52f2 --- /dev/null +++ b/tests/components/ltr501/common.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: ltr501 + address: 0x23 + i2c_id: i2c_ltr501 + type: ALS_PS + gain: 1X + integration_time: 100ms + ambient_light: "Ambient light" + ps_counts: "Proximity counts" diff --git a/tests/components/ltr501/test.esp32-ard.yaml b/tests/components/ltr501/test.esp32-ard.yaml new file mode 100644 index 0000000000..4c710c74fe --- /dev/null +++ b/tests/components/ltr501/test.esp32-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ltr501 + scl: 16 + sda: 17 + +<<: !include common.yaml diff --git a/tests/components/ltr501/test.esp32-c3-ard.yaml b/tests/components/ltr501/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..9e7de2768d --- /dev/null +++ b/tests/components/ltr501/test.esp32-c3-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ltr501 + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/ltr501/test.esp32-c3-idf.yaml b/tests/components/ltr501/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..9e7de2768d --- /dev/null +++ b/tests/components/ltr501/test.esp32-c3-idf.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ltr501 + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/ltr501/test.esp32-idf.yaml b/tests/components/ltr501/test.esp32-idf.yaml new file mode 100644 index 0000000000..4c710c74fe --- /dev/null +++ b/tests/components/ltr501/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ltr501 + scl: 16 + sda: 17 + +<<: !include common.yaml diff --git a/tests/components/ltr501/test.esp8266-ard.yaml b/tests/components/ltr501/test.esp8266-ard.yaml new file mode 100644 index 0000000000..9e7de2768d --- /dev/null +++ b/tests/components/ltr501/test.esp8266-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ltr501 + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/ltr501/test.rp2040-ard.yaml b/tests/components/ltr501/test.rp2040-ard.yaml new file mode 100644 index 0000000000..9e7de2768d --- /dev/null +++ b/tests/components/ltr501/test.rp2040-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_ltr501 + scl: 5 + sda: 4 + +<<: !include common.yaml From 198bd3b41afc46c499ccfacf32b90866e33a98d9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:35:39 +1200 Subject: [PATCH 0328/1052] Bump libssl-dev to 3.0.14-1~deb12u2 (#7426) --- docker/Dockerfile | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 4393d5a447..e255f4e2fc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -49,7 +49,7 @@ RUN \ zlib1g-dev=1:1.2.13.dfsg-1 \ libjpeg-dev=1:2.1.5-2 \ libfreetype-dev=2.12.1+dfsg-5+deb12u3 \ - libssl-dev=3.0.14-1~deb12u1 \ + libssl-dev=3.0.14-1~deb12u2 \ libffi-dev=3.4.4-1 \ libopenjp2-7=2.5.0-2 \ libtiff6=4.5.0-6+deb12u1 \ @@ -96,14 +96,19 @@ RUN \ # First install requirements to leverage caching when requirements don't change # tmpfs is for https://github.com/rust-lang/cargo/issues/8719 -COPY requirements.txt requirements_optional.txt script/platformio_install_deps.py platformio.ini / +COPY requirements.txt requirements_optional.txt / RUN --mount=type=tmpfs,target=/root/.cargo if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ + curl -L https://www.piwheels.org/cp311/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl -o /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl \ + && pip3 install --break-system-packages --no-cache-dir /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl \ + && rm /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl \ + && export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ fi; \ CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo \ pip3 install \ - --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \ - && /platformio_install_deps.py /platformio.ini --libraries + --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt + +COPY script/platformio_install_deps.py platformio.ini / +RUN /platformio_install_deps.py /platformio.ini --libraries # Avoid unsafe git error when container user and file config volume permissions don't match RUN git config --system --add safe.directory '*' From 9f42b76de3679a1ef2fd5945766ccf9f78ae4fd2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:57:42 +1200 Subject: [PATCH 0329/1052] [gh-actions] Don't produce docker build summaries (#7430) --- .github/actions/build-image/action.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index 56be20bd87..d277ec06c7 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -47,6 +47,9 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr uses: docker/build-push-action@v6.7.0 + env: + DOCKER_BUILD_SUMMARY: false + DOCKER_BUILD_RECORD_UPLOAD: false with: context: . file: ./docker/Dockerfile @@ -70,6 +73,9 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub uses: docker/build-push-action@v6.7.0 + env: + DOCKER_BUILD_SUMMARY: false + DOCKER_BUILD_RECORD_UPLOAD: false with: context: . file: ./docker/Dockerfile From d10feafa9bb68301c060e190309ba6dbf6d7b483 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 Sep 2024 00:58:57 +0100 Subject: [PATCH 0330/1052] Add BK72xx support to require_framework_version() (#7409) --- esphome/config_validation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 719cc43b31..e55879e37e 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -2045,6 +2045,7 @@ def require_framework_version( esp32_arduino=None, esp8266_arduino=None, rp2040_arduino=None, + bk72xx_libretiny=None, host=None, max_version=False, extra_message=None, @@ -2059,6 +2060,13 @@ def require_framework_version( msg += f". {extra_message}" raise Invalid(msg) required = esp_idf + elif CORE.is_bk72xx and framework == "arduino": + if bk72xx_libretiny is None: + msg = "This feature is incompatible with BK72XX" + if extra_message: + msg += f". {extra_message}" + raise Invalid(msg) + required = bk72xx_libretiny elif CORE.is_esp32 and framework == "arduino": if esp32_arduino is None: msg = "This feature is incompatible with ESP32 using arduino framework" From b5e5741ffdeb10f75285b45e5998a8cafcec768d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 Sep 2024 00:59:46 +0100 Subject: [PATCH 0331/1052] Switch IPv6 platform check to use require_framework_version() (#7410) --- esphome/components/network/__init__.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index caa873a746..772ba230d9 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -1,13 +1,7 @@ import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option import esphome.config_validation as cv -from esphome.const import ( - CONF_ENABLE_IPV6, - CONF_MIN_IPV6_ADDR_COUNT, - PLATFORM_ESP32, - PLATFORM_ESP8266, - PLATFORM_RP2040, -) +from esphome.const import CONF_ENABLE_IPV6, CONF_MIN_IPV6_ADDR_COUNT from esphome.core import CORE CODEOWNERS = ["@esphome/core"] @@ -26,7 +20,12 @@ CONFIG_SCHEMA = cv.Schema( ): cv.All( cv.boolean, cv.Any( - cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040]), + cv.require_framework_version( + esp_idf=cv.Version(0, 0, 0), + esp32_arduino=cv.Version(0, 0, 0), + esp8266_arduino=cv.Version(0, 0, 0), + rp2040_arduino=cv.Version(0, 0, 0), + ), cv.boolean_false, ), ), From f5c2921b85dca99cd9591e968bba4f3b4d9c75ca Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 Sep 2024 02:11:26 +0100 Subject: [PATCH 0332/1052] [bl0942] Improve energy reporting (#7428) --- esphome/components/bl0942/bl0942.cpp | 4 +++- esphome/components/bl0942/bl0942.h | 2 ++ esphome/components/bl0942/sensor.py | 9 ++++++--- tests/components/bl0942/test.bk72xx-ard.yaml | 1 + tests/components/bl0942/test.esp32-ard.yaml | 1 + 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/esphome/components/bl0942/bl0942.cpp b/esphome/components/bl0942/bl0942.cpp index af56e77de6..e6f96c1b19 100644 --- a/esphome/components/bl0942/bl0942.cpp +++ b/esphome/components/bl0942/bl0942.cpp @@ -137,7 +137,8 @@ void BL0942::setup() { } this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC); - this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC); + if (this->reset_) + this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC); uint32_t mode = BL0942_REG_MODE_DEFAULT; mode |= BL0942_REG_MODE_RMS_UPDATE_SEL; /* 800ms refresh time */ @@ -196,6 +197,7 @@ 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_); diff --git a/esphome/components/bl0942/bl0942.h b/esphome/components/bl0942/bl0942.h index 1dc930183f..37b884e6ca 100644 --- a/esphome/components/bl0942/bl0942.h +++ b/esphome/components/bl0942/bl0942.h @@ -93,6 +93,7 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; } void set_line_freq(LineFrequency freq) { this->line_freq_ = freq; } void set_address(uint8_t address) { this->address_ = address; } + void set_reset(bool reset) { this->reset_ = reset; } void set_current_reference(float current_ref) { this->current_reference_ = current_ref; this->current_reference_set_ = true; @@ -137,6 +138,7 @@ class BL0942 : public PollingComponent, public uart::UARTDevice { float energy_reference_ = BL0942_EREF; bool energy_reference_set_ = false; uint8_t address_ = 0; + bool reset_ = false; LineFrequency line_freq_ = LINE_FREQUENCY_50HZ; uint32_t rx_start_ = 0; uint32_t prev_cf_cnt_ = 0; diff --git a/esphome/components/bl0942/sensor.py b/esphome/components/bl0942/sensor.py index 3574443636..550f534b74 100644 --- a/esphome/components/bl0942/sensor.py +++ b/esphome/components/bl0942/sensor.py @@ -27,6 +27,7 @@ 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"] @@ -58,19 +59,19 @@ CONFIG_SCHEMA = ( ), cv.Optional(CONF_POWER): sensor.sensor_schema( unit_of_measurement=UNIT_WATT, - accuracy_decimals=0, + accuracy_decimals=1, device_class=DEVICE_CLASS_POWER, state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_ENERGY): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT_HOURS, - accuracy_decimals=0, + accuracy_decimals=3, device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( unit_of_measurement=UNIT_HERTZ, - accuracy_decimals=0, + accuracy_decimals=2, device_class=DEVICE_CLASS_FREQUENCY, state_class=STATE_CLASS_MEASUREMENT, ), @@ -82,6 +83,7 @@ CONFIG_SCHEMA = ( ), ), cv.Optional(CONF_ADDRESS, default=0): cv.int_range(min=0, max=3), + cv.Optional(CONF_RESET, default=True): cv.boolean, cv.Optional(CONF_CURRENT_REFERENCE): cv.float_, cv.Optional(CONF_ENERGY_REFERENCE): cv.float_, cv.Optional(CONF_POWER_REFERENCE): cv.float_, @@ -115,6 +117,7 @@ async def to_code(config): cg.add(var.set_frequency_sensor(sens)) cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) cg.add(var.set_address(config[CONF_ADDRESS])) + cg.add(var.set_reset(config[CONF_RESET])) if (current_reference := config.get(CONF_CURRENT_REFERENCE, None)) is not None: cg.add(var.set_current_reference(current_reference)) if (voltage_reference := config.get(CONF_VOLTAGE_REFERENCE, None)) is not None: diff --git a/tests/components/bl0942/test.bk72xx-ard.yaml b/tests/components/bl0942/test.bk72xx-ard.yaml index 12772f9375..ea61734441 100644 --- a/tests/components/bl0942/test.bk72xx-ard.yaml +++ b/tests/components/bl0942/test.bk72xx-ard.yaml @@ -10,6 +10,7 @@ sensor: - platform: bl0942 address: 0 line_frequency: 50Hz + reset: false voltage: name: BL0942 Voltage current: diff --git a/tests/components/bl0942/test.esp32-ard.yaml b/tests/components/bl0942/test.esp32-ard.yaml index 45ac85aa2a..4138543967 100644 --- a/tests/components/bl0942/test.esp32-ard.yaml +++ b/tests/components/bl0942/test.esp32-ard.yaml @@ -8,6 +8,7 @@ uart: sensor: - platform: bl0942 + reset: true voltage: name: BL0942 Voltage current: From dcfad31770b18fe917c9017325bc3a019a55e3b7 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:15:56 +1000 Subject: [PATCH 0333/1052] [rpi_dpi_rgb] Add bounce_buffer config for ESP-IDF 5.x (#7423) --- esphome/components/rpi_dpi_rgb/display.py | 33 +++++++++---------- .../components/rpi_dpi_rgb/rpi_dpi_rgb.cpp | 24 ++++++++++---- esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h | 1 + 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/esphome/components/rpi_dpi_rgb/display.py b/esphome/components/rpi_dpi_rgb/display.py index 6cc8d2c27b..c26143d63e 100644 --- a/esphome/components/rpi_dpi_rgb/display.py +++ b/esphome/components/rpi_dpi_rgb/display.py @@ -1,31 +1,28 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import display +from esphome.components.esp32 import const, only_on_variant +import esphome.config_validation as cv from esphome.const import ( - CONF_ENABLE_PIN, - CONF_HSYNC_PIN, - CONF_RESET_PIN, + CONF_BLUE, + CONF_COLOR_ORDER, CONF_DATA_PINS, + CONF_DIMENSIONS, + CONF_ENABLE_PIN, + CONF_GREEN, + CONF_HEIGHT, + CONF_HSYNC_PIN, CONF_ID, CONF_IGNORE_STRAPPING_WARNING, - CONF_DIMENSIONS, - CONF_VSYNC_PIN, - CONF_WIDTH, - CONF_HEIGHT, + CONF_INVERT_COLORS, CONF_LAMBDA, - CONF_COLOR_ORDER, - CONF_RED, - CONF_GREEN, - CONF_BLUE, CONF_NUMBER, CONF_OFFSET_HEIGHT, CONF_OFFSET_WIDTH, - CONF_INVERT_COLORS, -) -from esphome.components.esp32 import ( - only_on_variant, - const, + CONF_RED, + CONF_RESET_PIN, + CONF_VSYNC_PIN, + CONF_WIDTH, ) DEPENDENCIES = ["esp32"] diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index f173a2ec44..655b469b91 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -6,9 +6,14 @@ namespace esphome { namespace rpi_dpi_rgb { void RpiDpiRgb::setup() { - esph_log_config(TAG, "Setting up RPI_DPI_RGB"); + ESP_LOGCONFIG(TAG, "Setting up RPI_DPI_RGB"); + this->reset_display_(); esp_lcd_rgb_panel_config_t config{}; config.flags.fb_in_psram = 1; +#if ESP_IDF_VERSION_MAJOR >= 5 + config.bounce_buffer_size_px = this->width_ * 10; + config.num_fbs = 1; +#endif // ESP_IDF_VERSION_MAJOR config.timings.h_res = this->width_; config.timings.v_res = this->height_; config.timings.hsync_pulse_width = this->hsync_pulse_width_; @@ -20,7 +25,6 @@ void RpiDpiRgb::setup() { config.timings.flags.pclk_active_neg = this->pclk_inverted_; config.timings.pclk_hz = this->pclk_frequency_; config.clk_src = LCD_CLK_SRC_PLL160M; - config.sram_trans_align = 64; config.psram_trans_align = 64; size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); for (size_t i = 0; i != data_pin_count; i++) { @@ -34,11 +38,19 @@ void RpiDpiRgb::setup() { config.pclk_gpio_num = this->pclk_pin_->get_pin(); esp_err_t err = esp_lcd_new_rgb_panel(&config, &this->handle_); if (err != ESP_OK) { - esph_log_e(TAG, "lcd_new_rgb_panel failed: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "lcd_new_rgb_panel failed: %s", esp_err_to_name(err)); + this->mark_failed(); + return; } ESP_ERROR_CHECK(esp_lcd_panel_reset(this->handle_)); ESP_ERROR_CHECK(esp_lcd_panel_init(this->handle_)); - esph_log_config(TAG, "RPI_DPI_RGB setup complete"); + ESP_LOGCONFIG(TAG, "RPI_DPI_RGB setup complete"); +} +void RpiDpiRgb::loop() { +#if ESP_IDF_VERSION_MAJOR >= 5 + if (this->handle_ != nullptr) + esp_lcd_rgb_panel_restart(this->handle_); +#endif // ESP_IDF_VERSION_MAJOR } void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, @@ -53,7 +65,7 @@ void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uin } x_start += this->offset_x_; y_start += this->offset_y_; - esp_err_t err; + esp_err_t err = ESP_OK; // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. if (x_offset == 0 && x_pad == 0 && y_offset == 0) { // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother @@ -69,7 +81,7 @@ void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uin } } if (err != ESP_OK) - esph_log_e(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err)); } void RpiDpiRgb::draw_pixel_at(int x, int y, Color color) { diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h index 6d9d6d4ae9..10f77a2624 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h @@ -23,6 +23,7 @@ class RpiDpiRgb : public display::Display { public: void update() override { this->do_update_(); } void setup() override; + void loop() override; void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; void draw_pixel_at(int x, int y, Color color) override; From c8aed151571ba10b5ef679aef271f1c2089e84cd Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 10 Sep 2024 11:24:18 +1000 Subject: [PATCH 0334/1052] [LVGL] Add color gradients (#7427) --- esphome/components/lvgl/__init__.py | 22 +++------ esphome/components/lvgl/defines.py | 17 +++++++ esphome/components/lvgl/gradient.py | 61 ++++++++++++++++++++++++ esphome/components/lvgl/lv_validation.py | 57 +++++++++++++++------- esphome/components/lvgl/lvcode.py | 5 +- esphome/components/lvgl/lvgl_esphome.h | 3 -- esphome/components/lvgl/schemas.py | 9 ++-- esphome/components/lvgl/types.py | 1 + esphome/components/lvgl/widgets/meter.py | 9 +++- tests/components/lvgl/lvgl-package.yaml | 50 ++++++++++++++++++- 10 files changed, 190 insertions(+), 44 deletions(-) create mode 100644 esphome/components/lvgl/gradient.py diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index a4ca9d56f3..64f254cde8 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -22,8 +22,9 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, focused_widgets, update_to_code -from .defines import CONF_ADJUSTABLE, CONF_SKIP +from .defines import add_define from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code +from .gradient import GRADIENT_SCHEMA, gradients_to_code from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent from .schemas import ( @@ -128,17 +129,6 @@ for w_type in WIDGET_TYPES.values(): )(update_to_code) -lv_defines = {} # Dict of #defines to provide as build flags - - -def add_define(macro, value="1"): - if macro in lv_defines and lv_defines[macro] != value: - LOGGER.error( - "Redefinition of %s - was %s now %s", macro, lv_defines[macro], value - ) - lv_defines[macro] = value - - def as_macro(macro, value): if value is None: return f"#define {macro}" @@ -153,14 +143,14 @@ LV_CONF_H_FORMAT = """\ def generate_lv_conf_h(): - definitions = [as_macro(m, v) for m, v in lv_defines.items()] + definitions = [as_macro(m, v) for m, v in df.lv_defines.items()] definitions.sort() return LV_CONF_H_FORMAT.format("\n".join(definitions)) def final_validation(config): if pages := config.get(CONF_PAGES): - if all(p[CONF_SKIP] for p in pages): + if all(p[df.CONF_SKIP] for p in pages): raise cv.Invalid("At least one page must not be skipped") global_config = full_config.get() for display_id in config[df.CONF_DISPLAYS]: @@ -185,7 +175,7 @@ def final_validation(config): for w in focused_widgets: path = global_config.get_path_for_id(w) widget_conf = global_config.get_config_for_path(path[:-1]) - if CONF_ADJUSTABLE in widget_conf and not widget_conf[CONF_ADJUSTABLE]: + if df.CONF_ADJUSTABLE in widget_conf and not widget_conf[df.CONF_ADJUSTABLE]: raise cv.Invalid( "A non adjustable arc may not be focused", path, @@ -268,6 +258,7 @@ async def to_code(config): await encoders_to_code(lv_component, config) await theme_to_code(config) await styles_to_code(config) + await gradients_to_code(config) await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) await add_pages(lv_component, config) @@ -351,6 +342,7 @@ CONFIG_SCHEMA = ( cv.Optional(df.CONF_THEME): cv.Schema( {cv.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()} ), + cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA, cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t), diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index ee8472f90d..3db49d26a4 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -4,6 +4,8 @@ Constants already defined in esphome.const are not duplicated here and must be i """ +import logging + from esphome import codegen as cg, config_validation as cv from esphome.const import CONF_ITEMS from esphome.core import Lambda @@ -13,8 +15,19 @@ from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from .helpers import requires_component +LOGGER = logging.getLogger(__name__) lvgl_ns = cg.esphome_ns.namespace("lvgl") +lv_defines = {} # Dict of #defines to provide as build flags + + +def add_define(macro, value="1"): + if macro in lv_defines and lv_defines[macro] != value: + LOGGER.error( + "Redefinition of %s - was %s now %s", macro, lv_defines[macro], value + ) + lv_defines[macro] = value + def literal(arg): if isinstance(arg, str): @@ -173,6 +186,9 @@ LV_ANIM = LvConstant( "OUT_BOTTOM", ) +LV_GRAD_DIR = LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER") +LV_DITHER = LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF") + LOG_LEVELS = ( "TRACE", "INFO", @@ -406,6 +422,7 @@ CONF_FLEX_ALIGN_TRACK = "flex_align_track" CONF_FLEX_GROW = "flex_grow" CONF_FREEZE = "freeze" CONF_FULL_REFRESH = "full_refresh" +CONF_GRADIENTS = "gradients" CONF_GRID_CELL_ROW_POS = "grid_cell_row_pos" CONF_GRID_CELL_COLUMN_POS = "grid_cell_column_pos" CONF_GRID_CELL_ROW_SPAN = "grid_cell_row_span" diff --git a/esphome/components/lvgl/gradient.py b/esphome/components/lvgl/gradient.py new file mode 100644 index 0000000000..bc89470d47 --- /dev/null +++ b/esphome/components/lvgl/gradient.py @@ -0,0 +1,61 @@ +from esphome import config_validation as cv +import esphome.codegen as cg +from esphome.const import ( + CONF_COLOR, + CONF_DIRECTION, + CONF_DITHER, + CONF_ID, + CONF_POSITION, +) +from esphome.cpp_generator import MockObj + +from .defines import CONF_GRADIENTS, LV_DITHER, LV_GRAD_DIR, add_define +from .lv_validation import lv_color, lv_fraction +from .lvcode import lv_assign +from .types import lv_gradient_t + +CONF_STOPS = "stops" + + +def min_stops(value): + if len(value) < 2: + raise cv.Invalid("Must have at least 2 stops") + return value + + +GRADIENT_SCHEMA = cv.ensure_list( + cv.Schema( + { + cv.GenerateID(CONF_ID): cv.declare_id(lv_gradient_t), + cv.Optional(CONF_DIRECTION, default="NONE"): LV_GRAD_DIR.one_of, + cv.Optional(CONF_DITHER, default="NONE"): LV_DITHER.one_of, + cv.Required(CONF_STOPS): cv.All( + [ + cv.Schema( + { + cv.Required(CONF_COLOR): lv_color, + cv.Required(CONF_POSITION): lv_fraction, + } + ) + ], + min_stops, + ), + } + ) +) + + +async def gradients_to_code(config): + max_stops = 2 + for gradient in config.get(CONF_GRADIENTS, ()): + var = MockObj(cg.new_Pvariable(gradient[CONF_ID]), "->") + max_stops = max(max_stops, len(gradient[CONF_STOPS])) + lv_assign(var.dir, await LV_GRAD_DIR.process(gradient[CONF_DIRECTION])) + lv_assign(var.dither, await LV_DITHER.process(gradient[CONF_DITHER])) + lv_assign(var.stops_count, len(gradient[CONF_STOPS])) + for index, stop in enumerate(gradient[CONF_STOPS]): + lv_assign(var.stops[index].color, await lv_color.process(stop[CONF_COLOR])) + lv_assign( + var.stops[index].frac, await lv_fraction.process(stop[CONF_POSITION]) + ) + add_define("LV_GRADIENT_MAX_STOPS", max_stops) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index d8af9f7aa9..8593deb869 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,12 +1,19 @@ from typing import Union import esphome.codegen as cg -from esphome.components.color import ColorStruct +from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw from esphome.components.font import Font from esphome.components.image import Image_ import esphome.config_validation as cv -from esphome.const import CONF_ARGS, CONF_COLOR, CONF_FORMAT, CONF_TIME, CONF_VALUE -from esphome.core import HexInt, Lambda +from esphome.const import ( + CONF_ARGS, + CONF_COLOR, + CONF_FORMAT, + CONF_ID, + CONF_TIME, + CONF_VALUE, +) +from esphome.core import CORE, ID, Lambda from esphome.cpp_generator import MockObj from esphome.cpp_types import ESPTime, uint32 from esphome.helpers import cpp_string_escape @@ -23,14 +30,9 @@ from .defines import ( call_lambda, literal, ) -from .helpers import ( - esphome_fonts_used, - lv_fonts_used, - lvgl_components_required, - requires_component, -) +from .helpers import esphome_fonts_used, lv_fonts_used, requires_component from .lvcode import lv_expr -from .types import lv_font_t, lv_img_t +from .types import lv_font_t, lv_gradient_t, lv_img_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -59,11 +61,17 @@ def color_retmapper(value): if isinstance(value, cv.Lambda): return cv.returning_lambda(value) if isinstance(value, int): - hexval = HexInt(value) - return lv_expr.color_hex(hexval) - # Must be an id - lvgl_components_required.add(CONF_COLOR) - return lv_expr.color_from(MockObj(value)) + return literal( + f"lv_color_make({(value >> 16) & 0xFF}, {(value >> 8) & 0xFF}, {value & 0xFF})" + ) + if isinstance(value, ID): + cval = [x for x in CORE.config[CONF_COLOR] if x[CONF_ID] == value][0] + if CONF_HEX in cval: + r, g, b = cval[CONF_HEX] + else: + r, g, b, _ = from_rgbw(cval) + return literal(f"lv_color_make({r}, {g}, {b})") + assert False def option_string(value): @@ -132,7 +140,7 @@ radius_consts = LvConstant("LV_RADIUS_", "CIRCLE") @schema_extractor("one_of") -def radius_validator(value): +def fraction_validator(value): if value == SCHEMA_EXTRACT: return radius_consts.choices value = cv.Any(size, cv.percentage, radius_consts.one_of)(value) @@ -141,7 +149,7 @@ def radius_validator(value): return value -radius = LValidator(radius_validator, uint32, retmapper=literal) +lv_fraction = LValidator(fraction_validator, uint32, retmapper=literal) def id_name(value): @@ -242,6 +250,21 @@ lv_int = LValidator(cv.int_, cg.int_) lv_brightness = LValidator(cv.percentage, cg.float_, retmapper=lambda x: int(x * 255)) +def gradient_mapper(value): + return MockObj(value) + + +def gradient_validator(value): + return cv.use_id(lv_gradient_t)(value) + + +lv_gradient = LValidator( + validator=gradient_validator, + rtype=lv_gradient_t, + retmapper=gradient_mapper, +) + + def is_lv_font(font): return isinstance(font, str) and font.lower() in LV_FONTS diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index a3d13f7f8c..3a080d63e9 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -184,8 +184,9 @@ class LvContext(LambdaContext): self.lv_component = lv_component async def add_init_lambda(self): - cg.add(self.lv_component.add_init_lambda(await self.get_lambda())) - LvContext.added_lambda_count += 1 + if self.code_list: + cg.add(self.lv_component.add_init_lambda(await self.get_lambda())) + LvContext.added_lambda_count += 1 async def __aexit__(self, exc_type, exc_val, exc_tb): await super().__aexit__(exc_type, exc_val, exc_tb) diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index e248530971..d5cff51de2 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -42,9 +42,6 @@ extern lv_event_code_t lv_api_event; // NOLINT extern lv_event_code_t lv_update_event; // NOLINT extern std::string lv_event_code_name_for(uint8_t event_code); extern bool lv_is_pre_initialise(); -#ifdef USE_LVGL_COLOR -inline lv_color_t lv_color_from(Color color) { return lv_color_make(color.red, color.green, color.blue); } -#endif // USE_LVGL_COLOR #if LV_COLOR_DEPTH == 16 static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565; #elif LV_COLOR_DEPTH == 32 diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 9ff0fec5bc..780057623a 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -17,9 +17,9 @@ from esphome.core import TimePeriod from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid -from .defines import CONF_TIME_FORMAT +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_image +from .lv_validation import lv_color, lv_font, lv_gradient, lv_image from .lvcode import LvglComponent, lv_event_t_ptr from .types import ( LVEncoderListener, @@ -94,9 +94,10 @@ STYLE_PROPS = { "arc_width": cv.positive_int, "anim_time": lvalid.lv_milliseconds, "bg_color": lvalid.lv_color, + "bg_grad": lv_gradient, "bg_grad_color": lvalid.lv_color, "bg_dither_mode": df.LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF").one_of, - "bg_grad_dir": df.LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER").one_of, + "bg_grad_dir": LV_GRAD_DIR.one_of, "bg_grad_stop": lvalid.stop_value, "bg_image_opa": lvalid.opacity, "bg_image_recolor": lvalid.lv_color, @@ -160,7 +161,7 @@ STYLE_PROPS = { "max_width": lvalid.pixels_or_percent, "min_height": lvalid.pixels_or_percent, "min_width": lvalid.pixels_or_percent, - "radius": lvalid.radius, + "radius": lvalid.lv_fraction, "width": lvalid.size, "x": lvalid.pixels_or_percent, "y": lvalid.pixels_or_percent, diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index e4735ea58d..b452ab5fb3 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -59,6 +59,7 @@ LVEncoderListener = lvgl_ns.class_("LVEncoderListener") lv_obj_t = LvType("lv_obj_t") lv_page_t = LvType("LvPageType", parents=(LvCompound,)) lv_img_t = LvType("lv_img_t") +lv_gradient_t = LvType("lv_grad_dsc_t") LV_EVENT = MockObj(base="LV_EVENT_", op="") LV_STATE = MockObj(base="LV_STATE_", op="") diff --git a/esphome/components/lvgl/widgets/meter.py b/esphome/components/lvgl/widgets/meter.py index 7cf154d6f3..36f6643022 100644 --- a/esphome/components/lvgl/widgets/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -5,6 +5,7 @@ from esphome.const import ( CONF_COLOR, CONF_COUNT, CONF_ID, + CONF_ITEMS, CONF_LENGTH, CONF_LOCAL, CONF_RANGE_FROM, @@ -17,6 +18,7 @@ from esphome.const import ( from ..automation import action_to_code from ..defines import ( CONF_END_VALUE, + CONF_INDICATOR, CONF_MAIN, CONF_PIVOT_X, CONF_PIVOT_Y, @@ -165,7 +167,12 @@ METER_SCHEMA = {cv.Optional(CONF_SCALES): cv.ensure_list(SCALE_SCHEMA)} class MeterType(WidgetType): def __init__(self): - super().__init__(CONF_METER, lv_meter_t, (CONF_MAIN,), METER_SCHEMA) + super().__init__( + CONF_METER, + lv_meter_t, + (CONF_MAIN, CONF_INDICATOR, CONF_TICKS, CONF_ITEMS), + METER_SCHEMA, + ) async def to_code(self, w: Widget, config): """For a meter object, create and set parameters""" diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 0db6a6a995..9d157ea5b0 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -1,12 +1,32 @@ lvgl: log_level: TRACE bg_color: light_blue - disp_bg_color: 0xffff00 + disp_bg_color: color_id disp_bg_image: cat_image theme: obj: border_width: 1 + gradients: + - id: color_bar + direction: hor + dither: err_diff + stops: + - color: 0xFF0000 + position: 0 + - color: 0xFFFF00 + position: 42 + - color: 0x00FF00 + position: 84 + - color: 0x00FFFF + position: 127 + - color: 0x0000FF + position: 169 + - color: 0xFF00FF + position: 212 + - color: 0xFF0000 + position: 255 + style_definitions: - id: style_test bg_color: 0x2F8CD8 @@ -31,7 +51,7 @@ lvgl: - id: date_style text_font: roboto10 align: center - text_color: 0x000000 + text_color: color_id2 bg_opa: cover radius: 4 pad_all: 2 @@ -386,6 +406,22 @@ lvgl: - id: page2 widgets: + - slider: + min_value: 0 + max_value: 255 + bg_opa: cover + bg_grad: color_bar + radius: 0 + indicator: + bg_opa: transp + knob: + radius: 1 + width: 4 + height: 10% + bg_color: 0x000000 + width: 100% + height: 10% + align: top_mid - button: styles: spin_button id: spin_up @@ -586,3 +622,13 @@ image: color: - id: light_blue hex: "3340FF" + - id: color_id + red: 0.5 + green: 0.5 + blue: 0.5 + white: 0.5 + - id: color_id2 + red_int: 0xFF + green_int: 123 + blue_int: 64 + white_int: 255 From de7d2c33e18246668a64993ce3badc4e1b053359 Mon Sep 17 00:00:00 2001 From: marcovaneck <67060615+marcovaneck@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:22:58 +0200 Subject: [PATCH 0335/1052] [dsmr] Add internal 'telegram' text_sensor to support bridging (#6841) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/dsmr/dsmr.cpp | 6 ++++++ esphome/components/dsmr/dsmr.h | 6 ++++++ esphome/components/dsmr/text_sensor.py | 9 +++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index f382730912..193ea1d4e5 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -256,6 +256,7 @@ bool Dsmr::parse_telegram() { MyData data; ESP_LOGV(TAG, "Trying to parse telegram"); this->stop_requesting_data_(); + ::dsmr::ParseResult res = ::dsmr::P1Parser::parse(&data, this->telegram_, this->bytes_read_, false, this->crc_check_); // Parse telegram according to data definition. Ignore unknown values. @@ -267,6 +268,11 @@ bool Dsmr::parse_telegram() { } else { this->status_clear_warning(); this->publish_sensors(data); + + // publish the telegram, after publishing the sensors so it can also trigger action based on latest values + if (this->s_telegram_ != nullptr) { + this->s_telegram_->publish_state(std::string(this->telegram_, this->bytes_read_)); + } return true; } } diff --git a/esphome/components/dsmr/dsmr.h b/esphome/components/dsmr/dsmr.h index 6621d02cae..7304737b50 100644 --- a/esphome/components/dsmr/dsmr.h +++ b/esphome/components/dsmr/dsmr.h @@ -85,6 +85,9 @@ class Dsmr : public Component, public uart::UARTDevice { void set_##s(text_sensor::TextSensor *sensor) { s_##s##_ = sensor; } DSMR_TEXT_SENSOR_LIST(DSMR_SET_TEXT_SENSOR, ) + // handled outside dsmr + void set_telegram(text_sensor::TextSensor *sensor) { s_telegram_ = sensor; } + protected: void receive_telegram_(); void receive_encrypted_telegram_(); @@ -124,6 +127,9 @@ class Dsmr : public Component, public uart::UARTDevice { bool header_found_{false}; bool footer_found_{false}; + // handled outside dsmr + text_sensor::TextSensor *s_telegram_{nullptr}; + // Sensor member pointers #define DSMR_DECLARE_SENSOR(s) sensor::Sensor *s_##s##_{nullptr}; DSMR_SENSOR_LIST(DSMR_DECLARE_SENSOR, ) diff --git a/esphome/components/dsmr/text_sensor.py b/esphome/components/dsmr/text_sensor.py index 202cc07020..7c13fe7d58 100644 --- a/esphome/components/dsmr/text_sensor.py +++ b/esphome/components/dsmr/text_sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import text_sensor - +from esphome.const import CONF_INTERNAL from . import Dsmr, CONF_DSMR_ID AUTO_LOAD = ["dsmr"] @@ -22,6 +22,9 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional("water_equipment_id"): text_sensor.text_sensor_schema(), cv.Optional("sub_equipment_id"): text_sensor.text_sensor_schema(), cv.Optional("gas_delivered_text"): text_sensor.text_sensor_schema(), + cv.Optional("telegram"): text_sensor.text_sensor_schema().extend( + {cv.Optional(CONF_INTERNAL, default=True): cv.boolean} + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -37,7 +40,9 @@ async def to_code(config): if id and id.type == text_sensor.TextSensor: var = await text_sensor.new_text_sensor(conf) cg.add(getattr(hub, f"set_{key}")(var)) - text_sensors.append(f"F({key})") + if key != "telegram": + # telegram is not handled by dsmr + text_sensors.append(f"F({key})") if text_sensors: cg.add_define( From 7abbb0fb97de87469520b45909fb54e83a411b57 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 Sep 2024 20:42:46 +0100 Subject: [PATCH 0336/1052] Pull in new AsyncTCP for IPv6 on BK72xx (#7431) --- esphome/components/async_tcp/__init__.py | 6 +++--- platformio.ini | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index eae8c0e2df..99e250b6fc 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -1,13 +1,13 @@ # Dummy integration to allow relying on AsyncTCP import esphome.codegen as cg import esphome.config_validation as cv -from esphome.core import CORE, coroutine_with_priority from esphome.const import ( + PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, - PLATFORM_BK72XX, PLATFORM_RTL87XX, ) +from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@OttoWinter"] @@ -22,7 +22,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): if CORE.is_esp32 or CORE.is_libretiny: # https://github.com/esphome/AsyncTCP/blob/master/library.json - cg.add_library("esphome/AsyncTCP-esphome", "2.1.3") + cg.add_library("esphome/AsyncTCP-esphome", "2.1.4") elif CORE.is_esp8266: # https://github.com/esphome/ESPAsyncTCP cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0") diff --git a/platformio.ini b/platformio.ini index ee18068a29..7d912aaf54 100644 --- a/platformio.ini +++ b/platformio.ini @@ -119,7 +119,7 @@ lib_deps = WiFi ; wifi,web_server_base,ethernet (Arduino built-in) Update ; ota,web_server_base (Arduino built-in) ${common:arduino.lib_deps} - esphome/AsyncTCP-esphome@2.1.3 ; async_tcp + esphome/AsyncTCP-esphome@2.1.4 ; async_tcp WiFiClientSecure ; http_request,nextion (Arduino built-in) HTTPClient ; http_request,nextion (Arduino built-in) ESPmDNS ; mdns (Arduino built-in) From 7b90bfaec69bf685f75ac44ec7e50b56bc614a0d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 Sep 2024 20:43:19 +0100 Subject: [PATCH 0337/1052] Bump LibreTiny recommended version to 1.7.0 (#7432) --- esphome/components/libretiny/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 9ba889f493..cc7fae7e70 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -170,13 +170,11 @@ def _notify_old_style(config): return config -# NOTE: Keep this in mind when updating the recommended version: -# * For all constants below, update platformio.ini (in this repo) # The dev and latest branches will be at *least* this version, which is what matters. ARDUINO_VERSIONS = { "dev": (cv.Version(1, 7, 0), "https://github.com/libretiny-eu/libretiny.git"), "latest": (cv.Version(1, 7, 0), "libretiny"), - "recommended": (cv.Version(1, 5, 1), None), + "recommended": (cv.Version(1, 7, 0), None), } From 39ad358b51c105c78e7b060303aa0fb78372f6b3 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 10 Sep 2024 23:02:05 +0100 Subject: [PATCH 0338/1052] Enable IPv6 support for BK72xx (#7398) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/network/__init__.py | 4 ++++ .../components/wifi/wifi_component_libretiny.cpp | 16 +++++++++++++++- .../components/network/test-ipv6.bk72xx-ard.yaml | 6 +++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 772ba230d9..be4e102930 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -17,6 +17,7 @@ CONFIG_SCHEMA = cv.Schema( esp8266=False, esp32=False, rp2040=False, + bk72xx=False, ): cv.All( cv.boolean, cv.Any( @@ -25,6 +26,7 @@ CONFIG_SCHEMA = cv.Schema( esp32_arduino=cv.Version(0, 0, 0), esp8266_arduino=cv.Version(0, 0, 0), rp2040_arduino=cv.Version(0, 0, 0), + bk72xx_libretiny=cv.Version(1, 7, 0), ), cv.boolean_false, ), @@ -52,3 +54,5 @@ async def to_code(config): cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV6") if CORE.is_esp8266: cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_LOW_MEMORY") + if CORE.is_bk72xx: + cg.add_build_flag("-DCONFIG_IPV6") diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 19ade84a88..afb30c3bcf 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -85,7 +85,16 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { if (!this->has_sta()) return {}; - return {WiFi.localIP()}; + network::IPAddresses addresses; + addresses[0] = WiFi.localIP(); +#if USE_NETWORK_IPV6 + int i = 1; + auto v6_addresses = WiFi.allLocalIPv6(); + for (auto address : v6_addresses) { + addresses[i++] = network::IPAddress(address.toString().c_str()); + } +#endif /* USE_NETWORK_IPV6 */ + return addresses; } bool WiFiComponent::wifi_apply_hostname_() { @@ -321,6 +330,11 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ s_sta_connecting = false; break; } + case ESPHOME_EVENT_ID_WIFI_STA_GOT_IP6: { + // auto it = info.got_ip.ip_info; + ESP_LOGV(TAG, "Event: Got IPv6"); + break; + } case ESPHOME_EVENT_ID_WIFI_STA_LOST_IP: { ESP_LOGV(TAG, "Event: Lost IP"); break; diff --git a/tests/components/network/test-ipv6.bk72xx-ard.yaml b/tests/components/network/test-ipv6.bk72xx-ard.yaml index 361ca09977..d0c4bbfcb9 100644 --- a/tests/components/network/test-ipv6.bk72xx-ard.yaml +++ b/tests/components/network/test-ipv6.bk72xx-ard.yaml @@ -1,4 +1,8 @@ substitutions: - network_enable_ipv6: "false" + network_enable_ipv6: "true" + +bk72xx: + framework: + version: 1.7.0 <<: !include common.yaml From ffc2b587141d9984698214b2a4858df1756e275b Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Wed, 11 Sep 2024 01:30:46 +0200 Subject: [PATCH 0339/1052] Move I2S config settings the the base i2sAudio files. Phase 1 (#7183) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/i2s_audio/__init__.py | 77 ++++++++++++++++--- esphome/components/i2s_audio/i2s_audio.h | 18 ++++- .../i2s_audio/microphone/__init__.py | 69 ++++------------- .../microphone/i2s_audio_microphone.h | 10 +-- .../components/i2s_audio/speaker/__init__.py | 50 +++++------- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 10 +-- .../i2s_audio/speaker/i2s_audio_speaker.h | 4 +- 7 files changed, 129 insertions(+), 109 deletions(-) diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index 05e44696d8..90dc8a24ee 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -1,16 +1,16 @@ -import esphome.config_validation as cv -import esphome.final_validate as fv -import esphome.codegen as cg - from esphome import pins -from esphome.const import CONF_ID +import esphome.codegen as cg from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32.const import ( VARIANT_ESP32, + VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3, - VARIANT_ESP32C3, ) +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE +from esphome.cpp_generator import MockObjClass +import esphome.final_validate as fv CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["esp32"] @@ -25,16 +25,22 @@ CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin" CONF_I2S_AUDIO = "i2s_audio" CONF_I2S_AUDIO_ID = "i2s_audio_id" +CONF_BITS_PER_SAMPLE = "bits_per_sample" CONF_I2S_MODE = "i2s_mode" CONF_PRIMARY = "primary" CONF_SECONDARY = "secondary" +CONF_LEFT = "left" +CONF_RIGHT = "right" +CONF_STEREO = "stereo" + i2s_audio_ns = cg.esphome_ns.namespace("i2s_audio") I2SAudioComponent = i2s_audio_ns.class_("I2SAudioComponent", cg.Component) -I2SAudioIn = i2s_audio_ns.class_("I2SAudioIn", cg.Parented.template(I2SAudioComponent)) -I2SAudioOut = i2s_audio_ns.class_( - "I2SAudioOut", cg.Parented.template(I2SAudioComponent) +I2SAudioBase = i2s_audio_ns.class_( + "I2SAudioBase", cg.Parented.template(I2SAudioComponent) ) +I2SAudioIn = i2s_audio_ns.class_("I2SAudioIn", I2SAudioBase) +I2SAudioOut = i2s_audio_ns.class_("I2SAudioOut", I2SAudioBase) i2s_mode_t = cg.global_ns.enum("i2s_mode_t") I2S_MODE_OPTIONS = { @@ -50,6 +56,59 @@ I2S_PORTS = { VARIANT_ESP32C3: 1, } +i2s_channel_fmt_t = cg.global_ns.enum("i2s_channel_fmt_t") +I2S_CHANNELS = { + CONF_LEFT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT, + CONF_RIGHT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT, + CONF_STEREO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_RIGHT_LEFT, +} + +i2s_bits_per_sample_t = cg.global_ns.enum("i2s_bits_per_sample_t") +I2S_BITS_PER_SAMPLE = { + 8: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_8BIT, + 16: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_16BIT, + 32: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_32BIT, +} + +INTERNAL_ADC_VARIANTS = [VARIANT_ESP32] +PDM_VARIANTS = [VARIANT_ESP32, VARIANT_ESP32S3] + +_validate_bits = cv.float_with_unit("bits", "bit") + + +def i2s_audio_component_schema( + class_: MockObjClass, + default_sample_rate: int, + default_channel: str, + default_bits_per_sample: str, +): + return cv.Schema( + { + cv.GenerateID(): cv.declare_id(class_), + cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), + cv.Optional(CONF_CHANNEL, default=default_channel): cv.enum(I2S_CHANNELS), + cv.Optional(CONF_SAMPLE_RATE, default=default_sample_rate): cv.int_range( + min=1 + ), + cv.Optional(CONF_BITS_PER_SAMPLE, default=default_bits_per_sample): cv.All( + _validate_bits, cv.enum(I2S_BITS_PER_SAMPLE) + ), + cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.enum( + I2S_MODE_OPTIONS, lower=True + ), + } + ) + + +async def register_i2s_audio_component(var, config): + await cg.register_parented(var, config[CONF_I2S_AUDIO_ID]) + + cg.add(var.set_i2s_mode(config[CONF_I2S_MODE])) + cg.add(var.set_channel(config[CONF_CHANNEL])) + cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) + cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) + + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(I2SAudioComponent), diff --git a/esphome/components/i2s_audio/i2s_audio.h b/esphome/components/i2s_audio/i2s_audio.h index d8d4a23dde..727fb6c4e1 100644 --- a/esphome/components/i2s_audio/i2s_audio.h +++ b/esphome/components/i2s_audio/i2s_audio.h @@ -11,9 +11,23 @@ namespace i2s_audio { class I2SAudioComponent; -class I2SAudioIn : public Parented {}; +class I2SAudioBase : public Parented { + public: + void set_i2s_mode(i2s_mode_t mode) { this->i2s_mode_ = mode; } + void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; } + void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } + void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } -class I2SAudioOut : public Parented {}; + protected: + i2s_mode_t i2s_mode_{}; + i2s_channel_fmt_t channel_; + uint32_t sample_rate_; + i2s_bits_per_sample_t bits_per_sample_; +}; + +class I2SAudioIn : public I2SAudioBase {}; + +class I2SAudioOut : public I2SAudioBase {}; class I2SAudioComponent : public Component { public: diff --git a/esphome/components/i2s_audio/microphone/__init__.py b/esphome/components/i2s_audio/microphone/__init__.py index 844f176bea..e2ece03e68 100644 --- a/esphome/components/i2s_audio/microphone/__init__.py +++ b/esphome/components/i2s_audio/microphone/__init__.py @@ -1,20 +1,19 @@ -import esphome.config_validation as cv -import esphome.codegen as cg - from esphome import pins -from esphome.const import CONF_CHANNEL, CONF_ID, CONF_NUMBER, CONF_SAMPLE_RATE -from esphome.components import microphone, esp32 +import esphome.codegen as cg +from esphome.components import esp32, microphone from esphome.components.adc import ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, validate_adc_pin +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_NUMBER from .. import ( - CONF_I2S_MODE, - CONF_PRIMARY, - I2S_MODE_OPTIONS, - i2s_audio_ns, - I2SAudioComponent, - I2SAudioIn, - CONF_I2S_AUDIO_ID, CONF_I2S_DIN_PIN, + CONF_RIGHT, + INTERNAL_ADC_VARIANTS, + PDM_VARIANTS, + I2SAudioIn, + i2s_audio_component_schema, + i2s_audio_ns, + register_i2s_audio_component, ) CODEOWNERS = ["@jesserockz"] @@ -23,29 +22,13 @@ DEPENDENCIES = ["i2s_audio"] CONF_ADC_PIN = "adc_pin" CONF_ADC_TYPE = "adc_type" CONF_PDM = "pdm" -CONF_BITS_PER_SAMPLE = "bits_per_sample" + CONF_USE_APLL = "use_apll" I2SAudioMicrophone = i2s_audio_ns.class_( "I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component ) -i2s_channel_fmt_t = cg.global_ns.enum("i2s_channel_fmt_t") -CHANNELS = { - "left": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT, - "right": i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT, -} -i2s_bits_per_sample_t = cg.global_ns.enum("i2s_bits_per_sample_t") -BITS_PER_SAMPLE = { - 16: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_16BIT, - 32: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_32BIT, -} - -INTERNAL_ADC_VARIANTS = [esp32.const.VARIANT_ESP32] -PDM_VARIANTS = [esp32.const.VARIANT_ESP32, esp32.const.VARIANT_ESP32S3] - -_validate_bits = cv.float_with_unit("bits", "bit") - def validate_esp32_variant(config): variant = esp32.get_esp32_variant() @@ -62,19 +45,7 @@ def validate_esp32_variant(config): BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(I2SAudioMicrophone), - cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), - cv.Optional(CONF_CHANNEL, default="right"): cv.enum(CHANNELS), - cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1), - cv.Optional(CONF_BITS_PER_SAMPLE, default="32bit"): cv.All( - _validate_bits, cv.enum(BITS_PER_SAMPLE) - ), - cv.Optional(CONF_USE_APLL, default=False): cv.boolean, - cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.enum( - I2S_MODE_OPTIONS, lower=True - ), - } + i2s_audio_component_schema(I2SAudioMicrophone, 16000, CONF_RIGHT, "32bit") ).extend(cv.COMPONENT_SCHEMA) CONFIG_SCHEMA = cv.All( @@ -89,6 +60,7 @@ CONFIG_SCHEMA = cv.All( { cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_PDM): cv.boolean, + cv.Optional(CONF_USE_APLL, default=False): cv.boolean, } ), }, @@ -101,8 +73,8 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - - await cg.register_parented(var, config[CONF_I2S_AUDIO_ID]) + await register_i2s_audio_component(var, config) + await microphone.register_microphone(var, config) if config[CONF_ADC_TYPE] == "internal": variant = esp32.get_esp32_variant() @@ -112,11 +84,4 @@ async def to_code(config): else: cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN])) cg.add(var.set_pdm(config[CONF_PDM])) - - cg.add(var.set_i2s_mode(config[CONF_I2S_MODE])) - cg.add(var.set_channel(config[CONF_CHANNEL])) - cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) - cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) - cg.add(var.set_use_apll(config[CONF_USE_APLL])) - - await microphone.register_microphone(var, config) + cg.add(var.set_use_apll(config[CONF_USE_APLL])) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index 07ca0528aa..56fb4c252a 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -30,11 +30,6 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub } #endif - void set_i2s_mode(i2s_mode_t mode) { this->i2s_mode_ = mode; } - - void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; } - void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } - void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } void set_use_apll(uint32_t use_apll) { this->use_apll_ = use_apll; } protected: @@ -48,10 +43,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub bool adc_{false}; #endif bool pdm_{false}; - i2s_mode_t i2s_mode_{}; - i2s_channel_fmt_t channel_; - uint32_t sample_rate_; - i2s_bits_per_sample_t bits_per_sample_; + bool use_apll_; HighFrequencyLoopRequester high_freq_; diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index 72455af1b7..11fdae0436 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -1,15 +1,18 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_ID, CONF_MODE +import esphome.codegen as cg from esphome.components import esp32, speaker +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ID from .. import ( - CONF_I2S_AUDIO_ID, CONF_I2S_DOUT_PIN, - I2SAudioComponent, + CONF_LEFT, + CONF_RIGHT, + CONF_STEREO, I2SAudioOut, + i2s_audio_component_schema, i2s_audio_ns, + register_i2s_audio_component, ) CODEOWNERS = ["@jesserockz"] @@ -19,18 +22,16 @@ I2SAudioSpeaker = i2s_audio_ns.class_( "I2SAudioSpeaker", cg.Component, speaker.Speaker, I2SAudioOut ) -i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t") -CONF_MUTE_PIN = "mute_pin" CONF_DAC_TYPE = "dac_type" +i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t") INTERNAL_DAC_OPTIONS = { - "left": i2s_dac_mode_t.I2S_DAC_CHANNEL_LEFT_EN, - "right": i2s_dac_mode_t.I2S_DAC_CHANNEL_RIGHT_EN, - "stereo": i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN, + CONF_LEFT: i2s_dac_mode_t.I2S_DAC_CHANNEL_LEFT_EN, + CONF_RIGHT: i2s_dac_mode_t.I2S_DAC_CHANNEL_RIGHT_EN, + CONF_STEREO: i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN, } -EXTERNAL_DAC_OPTIONS = ["mono", "stereo"] NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2] @@ -44,28 +45,21 @@ def validate_esp32_variant(config): return config +BASE_SCHEMA = speaker.SPEAKER_SCHEMA.extend( + i2s_audio_component_schema(I2SAudioSpeaker, 16000, "stereo", "16bit") +).extend(cv.COMPONENT_SCHEMA) + CONFIG_SCHEMA = cv.All( cv.typed_schema( { - "internal": speaker.SPEAKER_SCHEMA.extend( + "internal": BASE_SCHEMA, + "external": BASE_SCHEMA.extend( { - cv.GenerateID(): cv.declare_id(I2SAudioSpeaker), - cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), - cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True), - } - ).extend(cv.COMPONENT_SCHEMA), - "external": speaker.SPEAKER_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(I2SAudioSpeaker), - cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent), cv.Required( CONF_I2S_DOUT_PIN ): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_MODE, default="mono"): cv.one_of( - *EXTERNAL_DAC_OPTIONS, lower=True - ), } - ).extend(cv.COMPONENT_SCHEMA), + ), }, key=CONF_DAC_TYPE, ), @@ -76,12 +70,10 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + await register_i2s_audio_component(var, config) await speaker.register_speaker(var, config) - await cg.register_parented(var, config[CONF_I2S_AUDIO_ID]) - if config[CONF_DAC_TYPE] == "internal": - cg.add(var.set_internal_dac_mode(config[CONF_MODE])) + cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL])) else: cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN])) - cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1)) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index cf5a2c2766..ab26edd8cf 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -64,17 +64,17 @@ void I2SAudioSpeaker::player_task(void *params) { xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); i2s_driver_config_t config = { - .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX), - .sample_rate = 16000, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .mode = (i2s_mode_t) (this_speaker->i2s_mode_ | I2S_MODE_TX), + .sample_rate = this_speaker->sample_rate_, + .bits_per_sample = this_speaker->bits_per_sample_, + .channel_format = this_speaker->channel_, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, .dma_buf_len = 128, .use_apll = false, .tx_desc_auto_clear = true, - .fixed_mclk = I2S_PIN_NO_CHANGE, + .fixed_mclk = 0, .mclk_multiple = I2S_MCLK_MULTIPLE_256, .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT, }; diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 0bdb67ceba..8d602e1ead 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -38,7 +38,7 @@ struct DataEvent { uint8_t data[BUFFER_SIZE]; }; -class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAudioOut { +class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Component { public: float get_setup_priority() const override { return esphome::setup_priority::LATE; } @@ -49,7 +49,6 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud #if SOC_I2S_SUPPORTS_DAC void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; } #endif - void set_external_dac_channels(uint8_t channels) { this->external_dac_channels_ = channels; } void start() override; void stop() override; @@ -76,7 +75,6 @@ class I2SAudioSpeaker : public Component, public speaker::Speaker, public I2SAud #if SOC_I2S_SUPPORTS_DAC i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE}; #endif - uint8_t external_dac_channels_; }; } // namespace i2s_audio From dbecade12215d3db7362c2f003c12177561cfd02 Mon Sep 17 00:00:00 2001 From: ArkanStasarik <103874616+ArkanStasarik@users.noreply.github.com> Date: Wed, 11 Sep 2024 12:53:09 +0800 Subject: [PATCH 0340/1052] Implement all supported thermocouple types for MAX31856 (#7218) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/max31856/max31856.cpp | 13 +++++++++++- esphome/components/max31856/max31856.h | 7 +++++-- esphome/components/max31856/sensor.py | 20 +++++++++++++++++++ esphome/components/mcp9600/sensor.py | 4 ++-- esphome/const.py | 1 + tests/components/max31856/test.esp32-ard.yaml | 1 + .../max31856/test.esp32-c3-ard.yaml | 2 ++ .../max31856/test.esp32-c3-idf.yaml | 2 ++ tests/components/max31856/test.esp32-idf.yaml | 1 + .../components/max31856/test.esp8266-ard.yaml | 2 ++ .../components/max31856/test.rp2040-ard.yaml | 2 ++ 11 files changed, 50 insertions(+), 5 deletions(-) diff --git a/esphome/components/max31856/max31856.cpp b/esphome/components/max31856/max31856.cpp index 8ae4be6657..6a4d34b430 100644 --- a/esphome/components/max31856/max31856.cpp +++ b/esphome/components/max31856/max31856.cpp @@ -32,6 +32,12 @@ void MAX31856Sensor::dump_config() { LOG_PIN(" CS Pin: ", this->cs_); ESP_LOGCONFIG(TAG, " Mains Filter: %s", (filter_ == FILTER_60HZ ? "60 Hz" : (filter_ == FILTER_50HZ ? "50 Hz" : "Unknown!"))); + if (this->thermocouple_type_ < 0 || this->thermocouple_type_ > 7) { + ESP_LOGCONFIG(TAG, " Thermocouple Type: Unknown"); + } else { + ESP_LOGCONFIG(TAG, " Thermocouple Type: %c", "BEJKNRST"[this->thermocouple_type_]); + } + LOG_UPDATE_INTERVAL(this); } @@ -129,7 +135,12 @@ void MAX31856Sensor::clear_fault_() { } void MAX31856Sensor::set_thermocouple_type_() { - MAX31856ThermocoupleType type = MAX31856_TCTYPE_K; + MAX31856ThermocoupleType type; + if (this->thermocouple_type_ < 0 || this->thermocouple_type_ > 7) { + type = MAX31856_TCTYPE_K; + } else { + type = this->thermocouple_type_; + } ESP_LOGCONFIG(TAG, "set_thermocouple_type_: 0x%02X", type); uint8_t t = this->read_register_(MAX31856_CR1_REG); t &= 0xF0; // mask off bottom 4 bits diff --git a/esphome/components/max31856/max31856.h b/esphome/components/max31856/max31856.h index 4deb6bc855..8d64cfe8bc 100644 --- a/esphome/components/max31856/max31856.h +++ b/esphome/components/max31856/max31856.h @@ -50,7 +50,6 @@ enum MAX31856Registers { /** * Multiple types of thermocouples supported by the chip. - * Currently only K type implemented here. */ enum MAX31856ThermocoupleType { MAX31856_TCTYPE_B = 0b0000, // 0x00 @@ -78,11 +77,15 @@ class MAX31856Sensor : public sensor::Sensor, void setup() override; void dump_config() override; float get_setup_priority() const override; - void set_filter(MAX31856ConfigFilter filter) { filter_ = filter; } + void set_filter(MAX31856ConfigFilter filter) { this->filter_ = filter; } + void set_thermocouple_type(MAX31856ThermocoupleType thermocouple_type) { + this->thermocouple_type_ = thermocouple_type; + } void update() override; protected: MAX31856ConfigFilter filter_; + MAX31856ThermocoupleType thermocouple_type_; uint8_t read_register_(uint8_t reg); uint32_t read_register24_(uint8_t reg); diff --git a/esphome/components/max31856/sensor.py b/esphome/components/max31856/sensor.py index bf9741aeed..679e02b11d 100644 --- a/esphome/components/max31856/sensor.py +++ b/esphome/components/max31856/sensor.py @@ -3,6 +3,7 @@ from esphome.components import sensor, spi import esphome.config_validation as cv from esphome.const import ( CONF_MAINS_FILTER, + CONF_THERMOCOUPLE_TYPE, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, @@ -18,6 +19,17 @@ FILTER = { 50: MAX31865ConfigFilter.FILTER_50HZ, 60: MAX31865ConfigFilter.FILTER_60HZ, } +MAX31856ThermocoupleType = max31856_ns.enum("MAX31856ThermocoupleType") +THERMOCOUPLE_TYPE = { + "B": MAX31856ThermocoupleType.MAX31856_TCTYPE_B, + "E": MAX31856ThermocoupleType.MAX31856_TCTYPE_E, + "J": MAX31856ThermocoupleType.MAX31856_TCTYPE_J, + "K": MAX31856ThermocoupleType.MAX31856_TCTYPE_K, + "N": MAX31856ThermocoupleType.MAX31856_TCTYPE_N, + "R": MAX31856ThermocoupleType.MAX31856_TCTYPE_R, + "S": MAX31856ThermocoupleType.MAX31856_TCTYPE_S, + "T": MAX31856ThermocoupleType.MAX31856_TCTYPE_T, +} CONFIG_SCHEMA = ( sensor.sensor_schema( @@ -34,6 +46,13 @@ CONFIG_SCHEMA = ( ), } ) + .extend( + { + cv.Optional(CONF_THERMOCOUPLE_TYPE, default="K"): cv.enum( + THERMOCOUPLE_TYPE, upper=True, space="" + ), + } + ) .extend(cv.polling_component_schema("60s")) .extend(spi.spi_device_schema()) ) @@ -44,3 +63,4 @@ async def to_code(config): await cg.register_component(var, config) await spi.register_spi_device(var, config) cg.add(var.set_filter(config[CONF_MAINS_FILTER])) + cg.add(var.set_thermocouple_type(config[CONF_THERMOCOUPLE_TYPE])) diff --git a/esphome/components/mcp9600/sensor.py b/esphome/components/mcp9600/sensor.py index 8557c7205e..65ae5f2eec 100644 --- a/esphome/components/mcp9600/sensor.py +++ b/esphome/components/mcp9600/sensor.py @@ -1,14 +1,14 @@ 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_THERMOCOUPLE_TYPE, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, ) -CONF_THERMOCOUPLE_TYPE = "thermocouple_type" CONF_HOT_JUNCTION = "hot_junction" CONF_COLD_JUNCTION = "cold_junction" diff --git a/esphome/const.py b/esphome/const.py index 169b11a715..7ad31f276f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -852,6 +852,7 @@ CONF_TEMPERATURE_STEP = "temperature_step" CONF_TEXT = "text" CONF_TEXT_SENSORS = "text_sensors" CONF_THEN = "then" +CONF_THERMOCOUPLE_TYPE = "thermocouple_type" CONF_THRESHOLD = "threshold" CONF_THROTTLE = "throttle" CONF_TILT = "tilt" diff --git a/tests/components/max31856/test.esp32-ard.yaml b/tests/components/max31856/test.esp32-ard.yaml index 5561903207..9a4da6b2a2 100644 --- a/tests/components/max31856/test.esp32-ard.yaml +++ b/tests/components/max31856/test.esp32-ard.yaml @@ -10,3 +10,4 @@ sensor: cs_pin: 12 update_interval: 15s mains_filter: 50Hz + thermocouple_type: N diff --git a/tests/components/max31856/test.esp32-c3-ard.yaml b/tests/components/max31856/test.esp32-c3-ard.yaml index 2794866c59..71bbfffb7b 100644 --- a/tests/components/max31856/test.esp32-c3-ard.yaml +++ b/tests/components/max31856/test.esp32-c3-ard.yaml @@ -10,3 +10,5 @@ sensor: cs_pin: 8 update_interval: 15s mains_filter: 50Hz + thermocouple_type: N + diff --git a/tests/components/max31856/test.esp32-c3-idf.yaml b/tests/components/max31856/test.esp32-c3-idf.yaml index 2794866c59..71bbfffb7b 100644 --- a/tests/components/max31856/test.esp32-c3-idf.yaml +++ b/tests/components/max31856/test.esp32-c3-idf.yaml @@ -10,3 +10,5 @@ sensor: cs_pin: 8 update_interval: 15s mains_filter: 50Hz + thermocouple_type: N + diff --git a/tests/components/max31856/test.esp32-idf.yaml b/tests/components/max31856/test.esp32-idf.yaml index 5561903207..9a4da6b2a2 100644 --- a/tests/components/max31856/test.esp32-idf.yaml +++ b/tests/components/max31856/test.esp32-idf.yaml @@ -10,3 +10,4 @@ sensor: cs_pin: 12 update_interval: 15s mains_filter: 50Hz + thermocouple_type: N diff --git a/tests/components/max31856/test.esp8266-ard.yaml b/tests/components/max31856/test.esp8266-ard.yaml index dfd9572ca9..b9c42542fd 100644 --- a/tests/components/max31856/test.esp8266-ard.yaml +++ b/tests/components/max31856/test.esp8266-ard.yaml @@ -10,3 +10,5 @@ sensor: cs_pin: 15 update_interval: 15s mains_filter: 50Hz + thermocouple_type: N + diff --git a/tests/components/max31856/test.rp2040-ard.yaml b/tests/components/max31856/test.rp2040-ard.yaml index 0abc8a081b..8607eb18cf 100644 --- a/tests/components/max31856/test.rp2040-ard.yaml +++ b/tests/components/max31856/test.rp2040-ard.yaml @@ -10,3 +10,5 @@ sensor: cs_pin: 6 update_interval: 15s mains_filter: 50Hz + thermocouple_type: N + From 04248b6840211a9752e74252400230b68620a87a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=5Bp=CA=B2=C9=B5s=5D?= Date: Wed, 11 Sep 2024 07:12:20 +0200 Subject: [PATCH 0341/1052] [i2s_audio] Add more options to speakers and microphones (#7306) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/i2s_audio/__init__.py | 24 +++++- esphome/components/i2s_audio/i2s_audio.h | 4 + .../i2s_audio/media_player/__init__.py | 12 ++- .../media_player/i2s_audio_media_player.h | 2 +- .../i2s_audio/microphone/__init__.py | 19 +++-- .../microphone/i2s_audio_microphone.cpp | 33 ++++---- .../microphone/i2s_audio_microphone.h | 4 - .../components/i2s_audio/speaker/__init__.py | 32 ++++++-- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 76 +++++++++++-------- .../i2s_audio/speaker/i2s_audio_speaker.h | 3 +- tests/components/speaker/test.esp32-ard.yaml | 1 - .../components/speaker/test.esp32-c3-ard.yaml | 1 - .../components/speaker/test.esp32-c3-idf.yaml | 1 - tests/components/speaker/test.esp32-idf.yaml | 1 - .../voice_assistant/test.esp32-ard.yaml | 1 - .../voice_assistant/test.esp32-c3-ard.yaml | 1 - .../voice_assistant/test.esp32-c3-idf.yaml | 1 - .../voice_assistant/test.esp32-idf.yaml | 1 - 18 files changed, 136 insertions(+), 81 deletions(-) diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index 90dc8a24ee..d376907925 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -30,6 +30,10 @@ CONF_I2S_MODE = "i2s_mode" CONF_PRIMARY = "primary" CONF_SECONDARY = "secondary" +CONF_USE_APLL = "use_apll" +CONF_BITS_PER_SAMPLE = "bits_per_sample" +CONF_BITS_PER_CHANNEL = "bits_per_channel" +CONF_MONO = "mono" CONF_LEFT = "left" CONF_RIGHT = "right" CONF_STEREO = "stereo" @@ -58,6 +62,7 @@ I2S_PORTS = { i2s_channel_fmt_t = cg.global_ns.enum("i2s_channel_fmt_t") I2S_CHANNELS = { + CONF_MONO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ALL_LEFT, CONF_LEFT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_LEFT, CONF_RIGHT: i2s_channel_fmt_t.I2S_CHANNEL_FMT_ONLY_RIGHT, CONF_STEREO: i2s_channel_fmt_t.I2S_CHANNEL_FMT_RIGHT_LEFT, @@ -67,17 +72,25 @@ i2s_bits_per_sample_t = cg.global_ns.enum("i2s_bits_per_sample_t") I2S_BITS_PER_SAMPLE = { 8: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_8BIT, 16: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_16BIT, + 24: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_24BIT, 32: i2s_bits_per_sample_t.I2S_BITS_PER_SAMPLE_32BIT, } -INTERNAL_ADC_VARIANTS = [VARIANT_ESP32] -PDM_VARIANTS = [VARIANT_ESP32, VARIANT_ESP32S3] +i2s_bits_per_chan_t = cg.global_ns.enum("i2s_bits_per_chan_t") +I2S_BITS_PER_CHANNEL = { + "default": i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_DEFAULT, + 8: i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_8BIT, + 16: i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_16BIT, + 24: i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_24BIT, + 32: i2s_bits_per_chan_t.I2S_BITS_PER_CHAN_32BIT, +} _validate_bits = cv.float_with_unit("bits", "bit") def i2s_audio_component_schema( class_: MockObjClass, + *, default_sample_rate: int, default_channel: str, default_bits_per_sample: str, @@ -96,6 +109,11 @@ def i2s_audio_component_schema( cv.Optional(CONF_I2S_MODE, default=CONF_PRIMARY): cv.enum( I2S_MODE_OPTIONS, lower=True ), + cv.Optional(CONF_USE_APLL, default=False): cv.boolean, + cv.Optional(CONF_BITS_PER_CHANNEL, default="default"): cv.All( + cv.Any(cv.float_with_unit("bits", "bit"), "default"), + cv.enum(I2S_BITS_PER_CHANNEL), + ), } ) @@ -107,6 +125,8 @@ async def register_i2s_audio_component(var, config): cg.add(var.set_channel(config[CONF_CHANNEL])) cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) cg.add(var.set_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) + cg.add(var.set_bits_per_channel(config[CONF_BITS_PER_CHANNEL])) + cg.add(var.set_use_apll(config[CONF_USE_APLL])) CONFIG_SCHEMA = cv.Schema( diff --git a/esphome/components/i2s_audio/i2s_audio.h b/esphome/components/i2s_audio/i2s_audio.h index 727fb6c4e1..7e2798c33d 100644 --- a/esphome/components/i2s_audio/i2s_audio.h +++ b/esphome/components/i2s_audio/i2s_audio.h @@ -17,12 +17,16 @@ class I2SAudioBase : public Parented { void set_channel(i2s_channel_fmt_t channel) { this->channel_ = channel; } void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } void set_bits_per_sample(i2s_bits_per_sample_t bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } + void set_bits_per_channel(i2s_bits_per_chan_t bits_per_channel) { this->bits_per_channel_ = bits_per_channel; } + void set_use_apll(uint32_t use_apll) { this->use_apll_ = use_apll; } protected: i2s_mode_t i2s_mode_{}; i2s_channel_fmt_t channel_; uint32_t sample_rate_; i2s_bits_per_sample_t bits_per_sample_; + i2s_bits_per_chan_t bits_per_channel_; + bool use_apll_; }; class I2SAudioIn : public I2SAudioBase {}; diff --git a/esphome/components/i2s_audio/media_player/__init__.py b/esphome/components/i2s_audio/media_player/__init__.py index 600a308e6c..dfa69ecadd 100644 --- a/esphome/components/i2s_audio/media_player/__init__.py +++ b/esphome/components/i2s_audio/media_player/__init__.py @@ -12,6 +12,10 @@ from .. import ( I2SAudioOut, CONF_I2S_AUDIO_ID, CONF_I2S_DOUT_PIN, + CONF_LEFT, + CONF_RIGHT, + CONF_MONO, + CONF_STEREO, ) CODEOWNERS = ["@jesserockz"] @@ -30,12 +34,12 @@ CONF_DAC_TYPE = "dac_type" CONF_I2S_COMM_FMT = "i2s_comm_fmt" INTERNAL_DAC_OPTIONS = { - "left": i2s_dac_mode_t.I2S_DAC_CHANNEL_LEFT_EN, - "right": i2s_dac_mode_t.I2S_DAC_CHANNEL_RIGHT_EN, - "stereo": i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN, + CONF_LEFT: i2s_dac_mode_t.I2S_DAC_CHANNEL_LEFT_EN, + CONF_RIGHT: i2s_dac_mode_t.I2S_DAC_CHANNEL_RIGHT_EN, + CONF_STEREO: i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN, } -EXTERNAL_DAC_OPTIONS = ["mono", "stereo"] +EXTERNAL_DAC_OPTIONS = [CONF_MONO, CONF_STEREO] NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2] diff --git a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h index 5afe778122..4672f94d7e 100644 --- a/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h +++ b/esphome/components/i2s_audio/media_player/i2s_audio_media_player.h @@ -23,7 +23,7 @@ enum I2SState : uint8_t { I2S_STATE_STOPPING, }; -class I2SAudioMediaPlayer : public Component, public media_player::MediaPlayer, public I2SAudioOut { +class I2SAudioMediaPlayer : public Component, public Parented, public media_player::MediaPlayer { public: void setup() override; float get_setup_priority() const override { return esphome::setup_priority::LATE; } diff --git a/esphome/components/i2s_audio/microphone/__init__.py b/esphome/components/i2s_audio/microphone/__init__.py index e2ece03e68..161046e962 100644 --- a/esphome/components/i2s_audio/microphone/__init__.py +++ b/esphome/components/i2s_audio/microphone/__init__.py @@ -8,8 +8,6 @@ from esphome.const import CONF_ID, CONF_NUMBER from .. import ( CONF_I2S_DIN_PIN, CONF_RIGHT, - INTERNAL_ADC_VARIANTS, - PDM_VARIANTS, I2SAudioIn, i2s_audio_component_schema, i2s_audio_ns, @@ -23,12 +21,13 @@ CONF_ADC_PIN = "adc_pin" CONF_ADC_TYPE = "adc_type" CONF_PDM = "pdm" -CONF_USE_APLL = "use_apll" - I2SAudioMicrophone = i2s_audio_ns.class_( "I2SAudioMicrophone", I2SAudioIn, microphone.Microphone, cg.Component ) +INTERNAL_ADC_VARIANTS = [esp32.const.VARIANT_ESP32] +PDM_VARIANTS = [esp32.const.VARIANT_ESP32, esp32.const.VARIANT_ESP32S3] + def validate_esp32_variant(config): variant = esp32.get_esp32_variant() @@ -45,9 +44,15 @@ def validate_esp32_variant(config): BASE_SCHEMA = microphone.MICROPHONE_SCHEMA.extend( - i2s_audio_component_schema(I2SAudioMicrophone, 16000, CONF_RIGHT, "32bit") + i2s_audio_component_schema( + I2SAudioMicrophone, + default_sample_rate=16000, + default_channel=CONF_RIGHT, + default_bits_per_sample="32bit", + ) ).extend(cv.COMPONENT_SCHEMA) + CONFIG_SCHEMA = cv.All( cv.typed_schema( { @@ -59,8 +64,7 @@ CONFIG_SCHEMA = cv.All( "external": BASE_SCHEMA.extend( { cv.Required(CONF_I2S_DIN_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_PDM): cv.boolean, - cv.Optional(CONF_USE_APLL, default=False): cv.boolean, + cv.Optional(CONF_PDM, default=False): cv.boolean, } ), }, @@ -84,4 +88,3 @@ async def to_code(config): else: cg.add(var.set_din_pin(config[CONF_I2S_DIN_PIN])) cg.add(var.set_pdm(config[CONF_PDM])) - cg.add(var.set_use_apll(config[CONF_USE_APLL])) diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index cb49a744fc..23689afb91 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -58,7 +58,7 @@ void I2SAudioMicrophone::start_() { .tx_desc_auto_clear = false, .fixed_mclk = 0, .mclk_multiple = I2S_MCLK_MULTIPLE_256, - .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT, + .bits_per_chan = this->bits_per_channel_, }; esp_err_t err; @@ -167,21 +167,24 @@ size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) { return 0; } this->status_clear_warning(); - if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_16BIT) { - return bytes_read; - } else if (this->bits_per_sample_ == I2S_BITS_PER_SAMPLE_32BIT) { - std::vector samples; - size_t samples_read = bytes_read / sizeof(int32_t); - samples.resize(samples_read); - for (size_t i = 0; i < samples_read; i++) { - int32_t temp = reinterpret_cast(buf)[i] >> 14; - samples[i] = clamp(temp, INT16_MIN, INT16_MAX); + // ESP-IDF I2S implementation right-extends 8-bit data to 16 bits, + // and 24-bit data to 32 bits. + switch (this->bits_per_sample_) { + case I2S_BITS_PER_SAMPLE_8BIT: + case I2S_BITS_PER_SAMPLE_16BIT: + return bytes_read; + case I2S_BITS_PER_SAMPLE_24BIT: + case I2S_BITS_PER_SAMPLE_32BIT: { + size_t samples_read = bytes_read / sizeof(int32_t); + for (size_t i = 0; i < samples_read; i++) { + int32_t temp = reinterpret_cast(buf)[i] >> 14; + buf[i] = clamp(temp, INT16_MIN, INT16_MAX); + } + return samples_read * sizeof(int16_t); } - memcpy(buf, samples.data(), samples_read * sizeof(int16_t)); - return samples_read * sizeof(int16_t); - } else { - ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_); - return 0; + default: + ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_); + return 0; } } diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index 56fb4c252a..ea3f357624 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -30,8 +30,6 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub } #endif - void set_use_apll(uint32_t use_apll) { this->use_apll_ = use_apll; } - protected: void start_(); void stop_(); @@ -44,8 +42,6 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub #endif bool pdm_{false}; - bool use_apll_; - HighFrequencyLoopRequester high_freq_; }; diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index 11fdae0436..22a5af259d 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -2,11 +2,12 @@ from esphome import pins import esphome.codegen as cg from esphome.components import esp32, speaker import esphome.config_validation as cv -from esphome.const import CONF_CHANNEL, CONF_ID +from esphome.const import CONF_CHANNEL, CONF_ID, CONF_MODE, CONF_TIMEOUT from .. import ( CONF_I2S_DOUT_PIN, CONF_LEFT, + CONF_MONO, CONF_RIGHT, CONF_STEREO, I2SAudioOut, @@ -32,7 +33,6 @@ INTERNAL_DAC_OPTIONS = { CONF_STEREO: i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN, } - NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2] @@ -45,14 +45,33 @@ def validate_esp32_variant(config): return config -BASE_SCHEMA = speaker.SPEAKER_SCHEMA.extend( - i2s_audio_component_schema(I2SAudioSpeaker, 16000, "stereo", "16bit") -).extend(cv.COMPONENT_SCHEMA) +BASE_SCHEMA = ( + speaker.SPEAKER_SCHEMA.extend( + i2s_audio_component_schema( + I2SAudioSpeaker, + default_sample_rate=16000, + default_channel=CONF_MONO, + default_bits_per_sample="16bit", + ) + ) + .extend( + { + cv.Optional( + CONF_TIMEOUT, default="100ms" + ): cv.positive_time_period_milliseconds, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) CONFIG_SCHEMA = cv.All( cv.typed_schema( { - "internal": BASE_SCHEMA, + "internal": BASE_SCHEMA.extend( + { + cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True), + } + ), "external": BASE_SCHEMA.extend( { cv.Required( @@ -77,3 +96,4 @@ async def to_code(config): cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL])) else: cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN])) + cg.add(var.set_timeout(config[CONF_TIMEOUT])) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index ab26edd8cf..4b427898a2 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -56,6 +56,21 @@ void I2SAudioSpeaker::start_() { this->task_created_ = true; } +template const uint8_t *convert_data_format(const a *from, b *to, size_t &bytes, bool repeat) { + if (sizeof(a) == sizeof(b) && !repeat) { + return reinterpret_cast(from); + } + const b *result = to; + for (size_t i = 0; i < bytes; i += sizeof(a)) { + b value = static_cast(*from++) << (sizeof(b) - sizeof(a)) * 8; + *to++ = value; + if (repeat) + *to++ = value; + } + bytes *= (sizeof(b) / sizeof(a)) * (repeat ? 2 : 1); // NOLINT + return reinterpret_cast(result); +} + void I2SAudioSpeaker::player_task(void *params) { I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params; @@ -71,12 +86,12 @@ void I2SAudioSpeaker::player_task(void *params) { .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, - .dma_buf_len = 128, - .use_apll = false, + .dma_buf_len = 256, + .use_apll = this_speaker->use_apll_, .tx_desc_auto_clear = true, .fixed_mclk = 0, .mclk_multiple = I2S_MCLK_MULTIPLE_256, - .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT, + .bits_per_chan = this_speaker->bits_per_channel_, }; #if SOC_I2S_SUPPORTS_DAC if (this_speaker->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) { @@ -114,10 +129,11 @@ void I2SAudioSpeaker::player_task(void *params) { event.type = TaskEventType::STARTED; xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); - int16_t buffer[BUFFER_SIZE / 2]; + int32_t buffer[BUFFER_SIZE]; while (true) { - if (xQueueReceive(this_speaker->buffer_queue_, &data_event, 100 / portTICK_PERIOD_MS) != pdTRUE) { + if (xQueueReceive(this_speaker->buffer_queue_, &data_event, this_speaker->timeout_ / portTICK_PERIOD_MS) != + pdTRUE) { break; // End of audio from main thread } if (data_event.stop) { @@ -125,17 +141,28 @@ void I2SAudioSpeaker::player_task(void *params) { xQueueReset(this_speaker->buffer_queue_); // Flush queue break; } - size_t bytes_written; - memmove(buffer, data_event.data, data_event.len); - size_t remaining = data_event.len / 2; - size_t current = 0; + const uint8_t *data = data_event.data; + size_t remaining = data_event.len; + switch (this_speaker->bits_per_sample_) { + case I2S_BITS_PER_SAMPLE_8BIT: + case I2S_BITS_PER_SAMPLE_16BIT: { + data = convert_data_format(reinterpret_cast(data), reinterpret_cast(buffer), + remaining, this_speaker->channel_ == I2S_CHANNEL_FMT_ALL_LEFT); + break; + } + case I2S_BITS_PER_SAMPLE_24BIT: + case I2S_BITS_PER_SAMPLE_32BIT: { + data = convert_data_format(reinterpret_cast(data), reinterpret_cast(buffer), + remaining, this_speaker->channel_ == I2S_CHANNEL_FMT_ALL_LEFT); + break; + } + } - while (remaining > 0) { - uint32_t sample = (buffer[current] << 16) | (buffer[current] & 0xFFFF); - - esp_err_t err = i2s_write(this_speaker->parent_->get_port(), &sample, sizeof(sample), &bytes_written, - (10 / portTICK_PERIOD_MS)); + while (remaining != 0) { + size_t bytes_written; + esp_err_t err = + i2s_write(this_speaker->parent_->get_port(), data, remaining, &bytes_written, (32 / portTICK_PERIOD_MS)); if (err != ESP_OK) { event = {.type = TaskEventType::WARNING, .err = err}; if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) { @@ -143,21 +170,8 @@ void I2SAudioSpeaker::player_task(void *params) { } continue; } - if (bytes_written != sizeof(sample)) { - event = {.type = TaskEventType::WARNING, .err = ESP_FAIL}; - if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) { - ESP_LOGW(TAG, "Failed to send WARNING event"); - } - continue; - } - remaining--; - current++; - } - - event.type = TaskEventType::PLAYING; - event.err = current; - if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) { - ESP_LOGW(TAG, "Failed to send PLAYING event"); + data += bytes_written; + remaining -= bytes_written; } } @@ -213,13 +227,11 @@ void I2SAudioSpeaker::watch_() { case TaskEventType::STARTED: ESP_LOGD(TAG, "Started I2S Audio Speaker"); this->state_ = speaker::STATE_RUNNING; + this->status_clear_warning(); break; case TaskEventType::STOPPING: ESP_LOGD(TAG, "Stopping I2S Audio Speaker"); break; - case TaskEventType::PLAYING: - this->status_clear_warning(); - break; case TaskEventType::STOPPED: this->state_ = speaker::STATE_STOPPED; vTaskDelete(this->player_task_handle_); diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 8d602e1ead..7adc4e8a24 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -21,7 +21,6 @@ static const size_t BUFFER_SIZE = 1024; enum class TaskEventType : uint8_t { STARTING = 0, STARTED, - PLAYING, STOPPING, STOPPED, WARNING = 255, @@ -45,6 +44,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp void setup() override; void loop() override; + void set_timeout(uint32_t ms) { this->timeout_ = ms; } void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; } #if SOC_I2S_SUPPORTS_DAC void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; } @@ -69,6 +69,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp QueueHandle_t buffer_queue_; QueueHandle_t event_queue_; + uint32_t timeout_{0}; uint8_t dout_pin_{0}; bool task_created_{false}; diff --git a/tests/components/speaker/test.esp32-ard.yaml b/tests/components/speaker/test.esp32-ard.yaml index e10c3e88c1..ab20f36eb6 100644 --- a/tests/components/speaker/test.esp32-ard.yaml +++ b/tests/components/speaker/test.esp32-ard.yaml @@ -21,4 +21,3 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 13 - mode: mono diff --git a/tests/components/speaker/test.esp32-c3-ard.yaml b/tests/components/speaker/test.esp32-c3-ard.yaml index 08699d8b22..c966f9daa7 100644 --- a/tests/components/speaker/test.esp32-c3-ard.yaml +++ b/tests/components/speaker/test.esp32-c3-ard.yaml @@ -21,4 +21,3 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 3 - mode: mono diff --git a/tests/components/speaker/test.esp32-c3-idf.yaml b/tests/components/speaker/test.esp32-c3-idf.yaml index 08699d8b22..c966f9daa7 100644 --- a/tests/components/speaker/test.esp32-c3-idf.yaml +++ b/tests/components/speaker/test.esp32-c3-idf.yaml @@ -21,4 +21,3 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 3 - mode: mono diff --git a/tests/components/speaker/test.esp32-idf.yaml b/tests/components/speaker/test.esp32-idf.yaml index e10c3e88c1..ab20f36eb6 100644 --- a/tests/components/speaker/test.esp32-idf.yaml +++ b/tests/components/speaker/test.esp32-idf.yaml @@ -21,4 +21,3 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 13 - mode: mono diff --git a/tests/components/voice_assistant/test.esp32-ard.yaml b/tests/components/voice_assistant/test.esp32-ard.yaml index 7f6fd85303..cbf9460087 100644 --- a/tests/components/voice_assistant/test.esp32-ard.yaml +++ b/tests/components/voice_assistant/test.esp32-ard.yaml @@ -28,7 +28,6 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 12 - mode: mono voice_assistant: microphone: mic_id_external diff --git a/tests/components/voice_assistant/test.esp32-c3-ard.yaml b/tests/components/voice_assistant/test.esp32-c3-ard.yaml index 248ae4d0dc..86357fad36 100644 --- a/tests/components/voice_assistant/test.esp32-c3-ard.yaml +++ b/tests/components/voice_assistant/test.esp32-c3-ard.yaml @@ -28,7 +28,6 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 2 - mode: mono voice_assistant: microphone: mic_id_external diff --git a/tests/components/voice_assistant/test.esp32-c3-idf.yaml b/tests/components/voice_assistant/test.esp32-c3-idf.yaml index 248ae4d0dc..86357fad36 100644 --- a/tests/components/voice_assistant/test.esp32-c3-idf.yaml +++ b/tests/components/voice_assistant/test.esp32-c3-idf.yaml @@ -28,7 +28,6 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 2 - mode: mono voice_assistant: microphone: mic_id_external diff --git a/tests/components/voice_assistant/test.esp32-idf.yaml b/tests/components/voice_assistant/test.esp32-idf.yaml index 2e0209311d..da9b50721f 100644 --- a/tests/components/voice_assistant/test.esp32-idf.yaml +++ b/tests/components/voice_assistant/test.esp32-idf.yaml @@ -28,7 +28,6 @@ speaker: id: speaker_id dac_type: external i2s_dout_pin: 12 - mode: mono voice_assistant: microphone: mic_id_external From e3ae8cd31ec501541dbe5f93366ca0b6cac3d2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Trevi=C3=B1o?= Date: Wed, 11 Sep 2024 07:16:52 +0200 Subject: [PATCH 0342/1052] [uponor_smatrix] Modifies sending algorithm (#7326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rafa Treviño --- esphome/components/uponor_smatrix/uponor_smatrix.cpp | 10 ++++------ esphome/components/uponor_smatrix/uponor_smatrix.h | 1 - 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.cpp b/esphome/components/uponor_smatrix/uponor_smatrix.cpp index a7014dc96c..e058de2852 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.cpp +++ b/esphome/components/uponor_smatrix/uponor_smatrix.cpp @@ -45,11 +45,8 @@ void UponorSmatrixComponent::loop() { // Read incoming data while (this->available()) { - // The controller polls devices every 10 seconds, with around 200 ms between devices. - // Remember timestamps so we can send our own packets when the bus is expected to be silent. - if (now - this->last_rx_ > 500) { - this->last_poll_start_ = now; - } + // The controller polls devices every 10 seconds in some units or continuously in others with around 200 ms between + // devices. Remember timestamps so we can send our own packets when the bus is expected to be silent. this->last_rx_ = now; uint8_t byte; @@ -60,7 +57,8 @@ void UponorSmatrixComponent::loop() { } // Send packets during bus silence - if ((now - this->last_rx_ > 300) && (now - this->last_poll_start_ < 9500) && (now - this->last_tx_ > 200)) { + if (this->rx_buffer_.empty() && (now - this->last_rx_ > 50) && (now - this->last_rx_ < 100) && + (now - this->last_tx_ > 200)) { #ifdef USE_TIME // Only build time packet when bus is silent and queue is empty to make sure we can send it right away if (this->send_time_requested_ && this->tx_queue_.empty() && this->do_send_time_()) diff --git a/esphome/components/uponor_smatrix/uponor_smatrix.h b/esphome/components/uponor_smatrix/uponor_smatrix.h index b7667b5b87..e3e19a12fc 100644 --- a/esphome/components/uponor_smatrix/uponor_smatrix.h +++ b/esphome/components/uponor_smatrix/uponor_smatrix.h @@ -93,7 +93,6 @@ class UponorSmatrixComponent : public uart::UARTDevice, public Component { std::queue> tx_queue_; uint32_t last_rx_; uint32_t last_tx_; - uint32_t last_poll_start_; #ifdef USE_TIME time::RealTimeClock *time_id_{nullptr}; From 955a909846c45d93fa49e91079683a9eef053cdd Mon Sep 17 00:00:00 2001 From: ajwahab <1449672+ajwahab@users.noreply.github.com> Date: Wed, 11 Sep 2024 01:20:30 -0400 Subject: [PATCH 0343/1052] User configurable frame buffer. (#7360) --- esphome/components/esp32_camera/__init__.py | 4 ++++ esphome/components/esp32_camera/esp32_camera.cpp | 10 +++++++++- esphome/components/esp32_camera/esp32_camera.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index 4187429412..2f1f9b90bb 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -140,6 +140,8 @@ CONF_TEST_PATTERN = "test_pattern" # framerates CONF_MAX_FRAMERATE = "max_framerate" CONF_IDLE_FRAMERATE = "idle_framerate" +# frame buffer +CONF_FRAME_BUFFER_COUNT = "frame_buffer_count" # stream trigger CONF_ON_STREAM_START = "on_stream_start" @@ -213,6 +215,7 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( cv.framerate, cv.Range(min=0, max=1) ), + cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( @@ -285,6 +288,7 @@ async def to_code(config): cg.add(var.set_idle_update_interval(0)) else: cg.add(var.set_idle_update_interval(1000 / config[CONF_IDLE_FRAMERATE])) + cg.add(var.set_frame_buffer_count(config[CONF_FRAME_BUFFER_COUNT])) cg.add(var.set_frame_size(config[CONF_RESOLUTION])) cg.add_define("USE_ESP32_CAMERA") diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 555f6ca5f1..e9e9d3cffb 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -127,7 +127,7 @@ 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, " 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); @@ -212,6 +212,8 @@ ESP32Camera::ESP32Camera() { this->config_.frame_size = FRAMESIZE_VGA; // 640x480 this->config_.jpeg_quality = 10; this->config_.fb_count = 1; + this->config_.grab_mode = CAMERA_GRAB_WHEN_EMPTY; + this->config_.fb_location = CAMERA_FB_IN_PSRAM; global_esp32_camera = this; } @@ -333,6 +335,12 @@ void ESP32Camera::set_max_update_interval(uint32_t max_update_interval) { void ESP32Camera::set_idle_update_interval(uint32_t idle_update_interval) { this->idle_update_interval_ = idle_update_interval; } +/* set frame buffer parameters */ +void ESP32Camera::set_frame_buffer_mode(camera_grab_mode_t mode) { this->config_.grab_mode = mode; } +void ESP32Camera::set_frame_buffer_count(uint8_t fb_count) { + this->config_.fb_count = fb_count; + this->set_frame_buffer_mode(fb_count > 1 ? CAMERA_GRAB_LATEST : CAMERA_GRAB_WHEN_EMPTY); +} /* ---------------- public API (specific) ---------------- */ void ESP32Camera::add_image_callback(std::function)> &&callback) { diff --git a/esphome/components/esp32_camera/esp32_camera.h b/esphome/components/esp32_camera/esp32_camera.h index 0c25381039..71f47d3c06 100644 --- a/esphome/components/esp32_camera/esp32_camera.h +++ b/esphome/components/esp32_camera/esp32_camera.h @@ -145,6 +145,9 @@ class ESP32Camera : public Component, public EntityBase { /* -- framerates */ void set_max_update_interval(uint32_t max_update_interval); void set_idle_update_interval(uint32_t idle_update_interval); + /* -- frame buffer */ + void set_frame_buffer_mode(camera_grab_mode_t mode); + void set_frame_buffer_count(uint8_t fb_count); /* public API (derivated) */ void setup() override; From 625726c65074530e06b74c31254db700956bc23b Mon Sep 17 00:00:00 2001 From: Tercio Filho Date: Wed, 11 Sep 2024 02:21:31 -0300 Subject: [PATCH 0344/1052] [Modbus Controller] Added preference to change command retries (#7312) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../components/modbus_controller/__init__.py | 3 ++ esphome/components/modbus_controller/const.py | 1 + .../modbus_controller/modbus_controller.cpp | 37 ++++++++++--------- .../modbus_controller/modbus_controller.h | 18 +++++++-- .../modbus_controller/test.esp32-ard.yaml | 1 + .../modbus_controller/test.esp32-idf.yaml | 1 + 6 files changed, 39 insertions(+), 22 deletions(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 8146124c28..6917807b07 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -21,6 +21,7 @@ from .const import ( CONF_CUSTOM_COMMAND, CONF_FORCE_NEW_RANGE, CONF_MODBUS_CONTROLLER_ID, + CONF_MAX_CMD_RETRIES, CONF_ON_COMMAND_SENT, CONF_REGISTER_COUNT, CONF_REGISTER_TYPE, @@ -131,6 +132,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional( CONF_COMMAND_THROTTLE, default="0ms" ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_MAX_CMD_RETRIES, default=4): cv.positive_int, cv.Optional(CONF_OFFLINE_SKIP_UPDATES, default=0): cv.positive_int, cv.Optional( CONF_SERVER_REGISTERS, @@ -257,6 +259,7 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) cg.add(var.set_allow_duplicate_commands(config[CONF_ALLOW_DUPLICATE_COMMANDS])) cg.add(var.set_command_throttle(config[CONF_COMMAND_THROTTLE])) + cg.add(var.set_max_cmd_retries(config[CONF_MAX_CMD_RETRIES])) cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES])) if CONF_SERVER_REGISTERS in config: for server_register in config[CONF_SERVER_REGISTERS]: diff --git a/esphome/components/modbus_controller/const.py b/esphome/components/modbus_controller/const.py index 5d9a61dee7..5cf7d230f1 100644 --- a/esphome/components/modbus_controller/const.py +++ b/esphome/components/modbus_controller/const.py @@ -5,6 +5,7 @@ CONF_COMMAND_THROTTLE = "command_throttle" CONF_OFFLINE_SKIP_UPDATES = "offline_skip_updates" CONF_CUSTOM_COMMAND = "custom_command" CONF_FORCE_NEW_RANGE = "force_new_range" +CONF_MAX_CMD_RETRIES = "max_cmd_retries" CONF_MODBUS_CONTROLLER_ID = "modbus_controller_id" CONF_MODBUS_FUNCTIONCODE = "modbus_functioncode" CONF_ON_COMMAND_SENT = "on_command_sent" diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 8f48847a4f..1dcb533629 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -18,11 +18,11 @@ void ModbusController::setup() { this->create_register_ranges_(); } bool ModbusController::send_next_command_() { uint32_t last_send = millis() - this->last_command_timestamp_; - if ((last_send > this->command_throttle_) && !waiting_for_response() && !command_queue_.empty()) { - auto &command = command_queue_.front(); + if ((last_send > this->command_throttle_) && !waiting_for_response() && !this->command_queue_.empty()) { + auto &command = this->command_queue_.front(); // remove from queue if command was sent too often - if (command->send_countdown < 1) { + if (!command->should_retry(this->max_cmd_retries_)) { if (!this->module_offline_) { ESP_LOGW(TAG, "Modbus device=%d set offline", this->address_); @@ -34,11 +34,9 @@ bool ModbusController::send_next_command_() { } } this->module_offline_ = true; - ESP_LOGD( - TAG, - "Modbus command to device=%d register=0x%02X countdown=%d no response received - removed from send queue", - this->address_, command->register_address, command->send_countdown); - command_queue_.pop_front(); + ESP_LOGD(TAG, "Modbus command to device=%d register=0x%02X no response received - removed from send queue", + this->address_, command->register_address); + this->command_queue_.pop_front(); } else { ESP_LOGV(TAG, "Sending next modbus command to device %d register 0x%02X count %d", this->address_, command->register_address, command->register_count); @@ -50,11 +48,11 @@ bool ModbusController::send_next_command_() { // remove from queue if no handler is defined if (!command->on_data_func) { - command_queue_.pop_front(); + this->command_queue_.pop_front(); } } } - return (!command_queue_.empty()); + return (!this->command_queue_.empty()); } // Queue incoming response @@ -77,7 +75,7 @@ void ModbusController::on_modbus_data(const std::vector &data) { current_command->payload = data; this->incoming_queue_.push(std::move(current_command)); ESP_LOGV(TAG, "Modbus response queued"); - command_queue_.pop_front(); + this->command_queue_.pop_front(); } } @@ -99,7 +97,7 @@ void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_ "payload size=%zu", function_code, current_command->register_address, current_command->register_count, current_command->payload.size()); - command_queue_.pop_front(); + this->command_queue_.pop_front(); } } @@ -178,7 +176,7 @@ void ModbusController::queue_command(const ModbusCommandItem &command) { if (!this->allow_duplicate_commands_) { // check if this command is already qeued. // not very effective but the queue is never really large - for (auto &item : command_queue_) { + for (auto &item : this->command_queue_) { if (item->is_equal(command)) { ESP_LOGW(TAG, "Duplicate modbus command found: type=0x%x address=%u count=%u", static_cast(command.register_type), command.register_address, command.register_count); @@ -189,7 +187,7 @@ void ModbusController::queue_command(const ModbusCommandItem &command) { } } } - command_queue_.push_back(make_unique(command)); + this->command_queue_.push_back(make_unique(command)); } void ModbusController::update_range_(RegisterRange &r) { @@ -224,8 +222,8 @@ void ModbusController::update_range_(RegisterRange &r) { // Once we get a response to the command it is removed from the queue and the next command is send // void ModbusController::update() { - if (!command_queue_.empty()) { - ESP_LOGV(TAG, "%zu modbus commands already in queue", command_queue_.size()); + if (!this->command_queue_.empty()) { + ESP_LOGV(TAG, "%zu modbus commands already in queue", this->command_queue_.size()); } else { ESP_LOGV(TAG, "Updating modbus component"); } @@ -346,6 +344,8 @@ 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_); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE ESP_LOGCONFIG(TAG, "sensormap"); for (auto &it : sensorset_) { @@ -560,8 +560,9 @@ bool ModbusCommandItem::send() { } else { modbusdevice->send_raw(this->payload); } - ESP_LOGV(TAG, "Command sent %d 0x%X %d", uint8_t(this->function_code), this->register_address, this->register_count); - send_countdown--; + this->send_count_++; + ESP_LOGV(TAG, "Command sent %d 0x%X %d send_count: %d", uint8_t(this->function_code), this->register_address, + this->register_count, this->send_count_); return true; } diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h index e88f4c07f7..1fa35e1535 100644 --- a/esphome/components/modbus_controller/modbus_controller.h +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -312,7 +312,6 @@ struct RegisterRange { class ModbusCommandItem { public: static const size_t MAX_PAYLOAD_BYTES = 240; - static const uint8_t MAX_SEND_REPEATS = 5; ModbusController *modbusdevice; uint16_t register_address; uint16_t register_count; @@ -322,9 +321,9 @@ class ModbusCommandItem { on_data_func; std::vector payload = {}; bool send(); - // wrong commands (esp. custom commands) can block the send queue - // limit the number of repeats - uint8_t send_countdown{MAX_SEND_REPEATS}; + /// Check if the command should be retried based on the max_retries parameter + bool should_retry(uint8_t max_retries) { return this->send_count_ <= max_retries; }; + /// factory methods /** Create modbus read command * Function code 02-04 @@ -413,6 +412,11 @@ class ModbusCommandItem { &&handler = nullptr); bool is_equal(const ModbusCommandItem &other); + + protected: + // wrong commands (esp. custom commands) can block the send queue, limit the number of repeats. + /// How many times this command has been sent + uint8_t send_count_{0}; }; /** Modbus controller class. @@ -464,6 +468,10 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { bool get_module_offline() { return module_offline_; } /// Set callback for commands void add_on_command_sent_callback(std::function &&callback); + /// called by esphome generated code to set the max_cmd_retries. + void set_max_cmd_retries(uint8_t max_cmd_retries) { this->max_cmd_retries_ = max_cmd_retries; } + /// get how many times a command will be (re)sent if no response is received + uint8_t get_max_cmd_retries() { return this->max_cmd_retries_; } protected: /// parse sensormap_ and create range of sequential addresses @@ -498,6 +506,8 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { bool module_offline_; /// how many updates to skip if module is offline uint16_t offline_skip_updates_; + /// How many times we will retry a command if we get no response + uint8_t max_cmd_retries_{4}; CallbackManager command_sent_callback_{}; }; diff --git a/tests/components/modbus_controller/test.esp32-ard.yaml b/tests/components/modbus_controller/test.esp32-ard.yaml index b6e38aeb9c..cd95d149cb 100644 --- a/tests/components/modbus_controller/test.esp32-ard.yaml +++ b/tests/components/modbus_controller/test.esp32-ard.yaml @@ -29,3 +29,4 @@ modbus_controller: value_type: S_DWORD_R read_lambda: |- return 42.3; + max_cmd_retries: 0 diff --git a/tests/components/modbus_controller/test.esp32-idf.yaml b/tests/components/modbus_controller/test.esp32-idf.yaml index d5407ac406..ba28e94d73 100644 --- a/tests/components/modbus_controller/test.esp32-idf.yaml +++ b/tests/components/modbus_controller/test.esp32-idf.yaml @@ -13,3 +13,4 @@ modbus_controller: address: 0x2 modbus_id: mod_bus1 allow_duplicate_commands: true + max_cmd_retries: 10 From 63cda412f9e658b6c031ce47bdfb18a7a2e92d0a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:37:40 +1200 Subject: [PATCH 0345/1052] Bump version to 2024.9.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7ad31f276f..fb745fd506 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.0-dev" +__version__ = "2024.9.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 664b219387b350197becdc552a2f68c62962dce4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:37:41 +1200 Subject: [PATCH 0346/1052] Bump version to 2024.10.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7ad31f276f..6e7bbdec98 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.0-dev" +__version__ = "2024.10.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From cb7b4d93653f61a93e9416641df73c7e3d9a5b49 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:08:41 +1200 Subject: [PATCH 0347/1052] [voice-assistant] Dont error on ``no_wake_word`` timeout error with streaming wake word (#7435) --- esphome/components/voice_assistant/voice_assistant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 577de630fb..a2210f188d 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -755,7 +755,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { message = std::move(arg.value); } } - if (code == "wake-word-timeout" || code == "wake_word_detection_aborted") { + if (code == "wake-word-timeout" || code == "wake_word_detection_aborted" || code == "no_wake_word") { // Don't change state here since either the "tts-end" or "run-end" events will do it. return; } else if (code == "wake-provider-missing" || code == "wake-engine-missing") { From 43f6793ad9abe54d2f87a9d07b0514c85911130a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 11 Sep 2024 23:58:15 -0400 Subject: [PATCH 0348/1052] Create codeql.yml --- .github/workflows/codeql.yml | 90 ++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..71ca55e907 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,90 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + schedule: + - cron: "30 18 * * 4" + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: c-cpp + build-mode: autobuild + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" From 6f9e725a2c86385339392751e235a3e7bb66f06f Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 11 Sep 2024 23:58:57 -0400 Subject: [PATCH 0349/1052] Update codeql.yml --- .github/workflows/codeql.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 71ca55e907..d4cda309db 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,6 +12,7 @@ name: "CodeQL Advanced" on: + workflow_dispatch: schedule: - cron: "30 18 * * 4" From 95a340d7a329a1a116b06cd7e479630ff4b53d80 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 12 Sep 2024 00:04:25 -0400 Subject: [PATCH 0350/1052] Update codeql.yml --- .github/workflows/codeql.yml | 80 ++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d4cda309db..e9f212d7a8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -40,10 +40,10 @@ jobs: fail-fast: false matrix: include: - - language: c-cpp - build-mode: autobuild - - language: python - build-mode: none + # - language: c-cpp + # build-mode: autobuild + - language: python + build-mode: none # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' # Use `c-cpp` to analyze code written in C, C++ or both # Use 'java-kotlin' to analyze code written in Java, Kotlin or both @@ -53,39 +53,39 @@ jobs: # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - build-mode: ${{ matrix.build-mode }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - if: matrix.build-mode == 'manual' - shell: bash - run: | - echo 'If you are using a "manual" build mode for one or more of the' \ - 'languages you are analyzing, replace this with the commands to build' \ - 'your code, for example:' - echo ' make bootstrap' - echo ' make release' - exit 1 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" From 6207510279673db4be7dc0612febb35d802e6543 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 12 Sep 2024 00:05:40 -0400 Subject: [PATCH 0351/1052] Update codeql.yml --- .github/workflows/codeql.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e9f212d7a8..7f510782a8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -55,7 +55,7 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 @@ -65,10 +65,10 @@ jobs: # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. - + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - + # If the analyze step fails for one of the languages you are analyzing with # "We were unable to automatically build your code", modify the matrix above # to set the build mode to "manual" for that language. Then modify this step @@ -84,7 +84,7 @@ jobs: echo ' make bootstrap' echo ' make release' exit 1 - + - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: From 323c641ecdfaeb3769c3e9aae86d0079800ac474 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 12 Sep 2024 00:09:25 -0400 Subject: [PATCH 0352/1052] Update codeql.yml --- .github/workflows/codeql.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7f510782a8..ddeb0a99d2 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -44,14 +44,14 @@ jobs: # build-mode: autobuild - language: python build-mode: none - # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' - # Use `c-cpp` to analyze code written in C, C++ or both - # Use 'java-kotlin' to analyze code written in Java, Kotlin or both - # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both - # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, - # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. - # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how - # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository uses: actions/checkout@v4 From 78d0e0baae5efa3f918596be88192a45e7e10829 Mon Sep 17 00:00:00 2001 From: Tomer <57483589+tomer-w@users.noreply.github.com> Date: Fri, 13 Sep 2024 03:56:04 +0300 Subject: [PATCH 0353/1052] =?UTF-8?q?Improve=20manufacturer=20data=20traci?= =?UTF-8?q?ng=20to=20identify=20BLE=20devices=20a=20bit=20easie=E2=80=A6?= =?UTF-8?q?=20(#7332)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ble_presence/binary_sensor.py | 4 ++-- esphome/components/esp32_ble/ble_uuid.cpp | 7 +++++++ esphome/components/esp32_ble/ble_uuid.h | 1 + .../esp32_ble_tracker/esp32_ble_tracker.cpp | 20 ++++++++++--------- .../esp32_ble_tracker/esp32_ble_tracker.h | 6 +++--- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/esphome/components/ble_presence/binary_sensor.py b/esphome/components/ble_presence/binary_sensor.py index d1fdc80289..3a0f1ade98 100644 --- a/esphome/components/ble_presence/binary_sensor.py +++ b/esphome/components/ble_presence/binary_sensor.py @@ -41,7 +41,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t, cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t, - cv.Optional(CONF_IBEACON_UUID): cv.uuid, + cv.Optional(CONF_IBEACON_UUID): esp32_ble_tracker.bt_uuid, cv.Optional(CONF_TIMEOUT, default="5min"): cv.positive_time_period, cv.Optional(CONF_MIN_RSSI): cv.All( cv.decibel, cv.int_range(min=-100, max=-30) @@ -83,7 +83,7 @@ async def to_code(config): cg.add(var.set_service_uuid128(uuid128)) if ibeacon_uuid := config.get(CONF_IBEACON_UUID): - ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid)) + ibeacon_uuid = esp32_ble_tracker.as_reversed_hex_array(ibeacon_uuid) cg.add(var.set_ibeacon_uuid(ibeacon_uuid)) if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None: diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp index 57c2f9df94..07ac719434 100644 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ b/esphome/components/esp32_ble/ble_uuid.cpp @@ -31,6 +31,13 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) { memcpy(ret.uuid_.uuid.uuid128, data, ESP_UUID_LEN_128); return ret; } +ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) { + ESPBTUUID ret; + ret.uuid_.len = ESP_UUID_LEN_128; + for (int i = 0; i < ESP_UUID_LEN_128; i++) + ret.uuid_.uuid.uuid128[ESP_UUID_LEN_128 - 1 - i] = data[i]; + return ret; +} ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { ESPBTUUID ret; if (data.length() == 4) { diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index 790a57c59d..d90db3a599 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -20,6 +20,7 @@ class ESPBTUUID { static ESPBTUUID from_uint32(uint32_t uuid); static ESPBTUUID from_raw(const uint8_t *data); + static ESPBTUUID from_raw_reversed(const uint8_t *data); static ESPBTUUID from_raw(const std::string &data); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index d154d4e519..74b4b9aa89 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -462,14 +462,16 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str()); } for (auto &data : this->manufacturer_datas_) { - ESP_LOGVV(TAG, " Manufacturer data: %s", format_hex_pretty(data.data).c_str()); - if (this->get_ibeacon().has_value()) { - auto ibeacon = this->get_ibeacon().value(); - ESP_LOGVV(TAG, " iBeacon data:"); - ESP_LOGVV(TAG, " UUID: %s", ibeacon.get_uuid().to_string().c_str()); - ESP_LOGVV(TAG, " Major: %u", ibeacon.get_major()); - ESP_LOGVV(TAG, " Minor: %u", ibeacon.get_minor()); - ESP_LOGVV(TAG, " TXPower: %d", ibeacon.get_signal_power()); + auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(data); + if (ibeacon.has_value()) { + ESP_LOGVV(TAG, " Manufacturer iBeacon:"); + ESP_LOGVV(TAG, " UUID: %s", ibeacon.value().get_uuid().to_string().c_str()); + ESP_LOGVV(TAG, " Major: %u", ibeacon.value().get_major()); + ESP_LOGVV(TAG, " Minor: %u", ibeacon.value().get_minor()); + ESP_LOGVV(TAG, " TXPower: %d", ibeacon.value().get_signal_power()); + } else { + ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", data.uuid.to_string().c_str(), + format_hex_pretty(data.data).c_str()); } } for (auto &data : this->service_datas_) { @@ -478,7 +480,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str()); } - ESP_LOGVV(TAG, "Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str()); + ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str()); #endif } void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 3db7a54f6e..d2bb6a6e6d 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -44,10 +44,10 @@ class ESPBLEiBeacon { ESPBLEiBeacon(const uint8_t *data); static optional from_manufacturer_data(const ServiceData &data); - uint16_t get_major() { return ((this->beacon_data_.major & 0xFF) << 8) | (this->beacon_data_.major >> 8); } - uint16_t get_minor() { return ((this->beacon_data_.minor & 0xFF) << 8) | (this->beacon_data_.minor >> 8); } + uint16_t get_major() { return byteswap(this->beacon_data_.major); } + uint16_t get_minor() { return byteswap(this->beacon_data_.minor); } int8_t get_signal_power() { return this->beacon_data_.signal_power; } - ESPBTUUID get_uuid() { return ESPBTUUID::from_raw(this->beacon_data_.proximity_uuid); } + ESPBTUUID get_uuid() { return ESPBTUUID::from_raw_reversed(this->beacon_data_.proximity_uuid); } protected: struct { From e315b4d939e003e587d63a7e7bf67ea2beae51d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:22:28 +1200 Subject: [PATCH 0354/1052] Bump peter-evans/create-pull-request from 7.0.0 to 7.0.2 (#7437) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index e834ff3793..1418867240 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -36,7 +36,7 @@ jobs: python ./script/sync-device_class.py - name: Commit changes - uses: peter-evans/create-pull-request@v7.0.0 + uses: peter-evans/create-pull-request@v7.0.2 with: commit-message: "Synchronise Device Classes from Home Assistant" committer: esphomebot From 0df44b5df194a5b4b163ef2691e684dee89037f6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 13 Sep 2024 04:06:50 +0200 Subject: [PATCH 0355/1052] Bump recommended ESP-IDF to 4.4.8 (#7349) --- esphome/components/esp32/__init__.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index b630c7638e..9cb9ac257a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -239,7 +239,7 @@ 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(4, 4, 7) +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(4, 4, 8) # 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 diff --git a/platformio.ini b/platformio.ini index 7d912aaf54..e3593bf43f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -139,7 +139,7 @@ extra_scripts = post:esphome/components/esp32/post_build.py.script extends = common:idf platform = platformio/espressif32@5.4.0 platform_packages = - platformio/framework-espidf@~3.40407.0 + platformio/framework-espidf@~3.40408.0 framework = espidf lib_deps = From 08c0715a30669fe76d43415ea76ce2d33a9939db Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:15:00 +1200 Subject: [PATCH 0356/1052] [tm1638] Fix linting and formatting issues (#7443) --- esphome/components/tm1638/binary_sensor/__init__.py | 5 +++-- esphome/components/tm1638/display.py | 10 +++++----- esphome/components/tm1638/output/__init__.py | 5 +++-- esphome/components/tm1638/switch/__init__.py | 5 +++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/esphome/components/tm1638/binary_sensor/__init__.py b/esphome/components/tm1638/binary_sensor/__init__.py index 6623228555..de6ea35e54 100644 --- a/esphome/components/tm1638/binary_sensor/__init__.py +++ b/esphome/components/tm1638/binary_sensor/__init__.py @@ -1,8 +1,9 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import binary_sensor +import esphome.config_validation as cv from esphome.const import CONF_KEY -from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID + +from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns TM1638Key = tm1638_ns.class_("TM1638Key", binary_sensor.BinarySensor) diff --git a/esphome/components/tm1638/display.py b/esphome/components/tm1638/display.py index 2fb8dc7a55..14b70be94d 100644 --- a/esphome/components/tm1638/display.py +++ b/esphome/components/tm1638/display.py @@ -1,13 +1,13 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import display +import esphome.config_validation as cv from esphome.const import ( + CONF_CLK_PIN, + CONF_DIO_PIN, CONF_ID, CONF_INTENSITY, CONF_LAMBDA, - CONF_CLK_PIN, - CONF_DIO_PIN, CONF_STB_PIN, ) @@ -51,4 +51,4 @@ async def to_code(config): config[CONF_LAMBDA], [(TM1638ComponentRef, "it")], return_type=cg.void ) - cg.add(var.set_writer(lambda_)) + cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/tm1638/output/__init__.py b/esphome/components/tm1638/output/__init__.py index 2d982e409d..b16b08d504 100644 --- a/esphome/components/tm1638/output/__init__.py +++ b/esphome/components/tm1638/output/__init__.py @@ -1,8 +1,9 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import output +import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_LED -from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID + +from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns TM1638OutputLed = tm1638_ns.class_("TM1638OutputLed", output.BinaryOutput, cg.Component) diff --git a/esphome/components/tm1638/switch/__init__.py b/esphome/components/tm1638/switch/__init__.py index ed6aa91d03..8832cf8b92 100644 --- a/esphome/components/tm1638/switch/__init__.py +++ b/esphome/components/tm1638/switch/__init__.py @@ -1,8 +1,9 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import switch +import esphome.config_validation as cv from esphome.const import CONF_LED -from ..display import tm1638_ns, TM1638Component, CONF_TM1638_ID + +from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns TM1638SwitchLed = tm1638_ns.class_("TM1638SwitchLed", switch.Switch, cg.Component) From e4c90489f725b22f4c2abc8821b103a9fc37dce2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:16:24 +1200 Subject: [PATCH 0357/1052] [image] Fix linting and formatting issues (#7440) --- esphome/components/image/__init__.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index e5a205f1e0..e80ba4498f 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -1,18 +1,17 @@ from __future__ import annotations -import logging - import hashlib import io +import logging from pathlib import Path import re + from magic import Magic -from esphome import core -from esphome.components import font -from esphome import external_files -import esphome.config_validation as cv +from esphome import core, external_files import esphome.codegen as cg +from esphome.components import font +import esphome.config_validation as cv from esphome.const import ( CONF_DITHER, CONF_FILE, @@ -239,12 +238,11 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA) def load_svg_image(file: bytes, resize: tuple[int, int]): # Local import only to allow "validate_pillow_installed" to run *before* importing it - from PIL import Image - # This import is only needed in case of SVG images; adding it # to the top would force configurations not using SVG to also have it # installed for no reason. from cairosvg import svg2png + from PIL import Image if resize: req_width, req_height = resize @@ -274,6 +272,9 @@ async def to_code(config): elif conf_file[CONF_SOURCE] == SOURCE_WEB: path = compute_local_image_path(conf_file).as_posix() + else: + raise core.EsphomeError(f"Unknown image source: {conf_file[CONF_SOURCE]}") + try: with open(path, "rb") as f: file_contents = f.read() From c702a3f3ee09f779efc3d18b44e618628003141e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:16:57 +1200 Subject: [PATCH 0358/1052] [animation] Fix linting and formatting issues (#7439) --- esphome/components/animation/__init__.py | 28 ++++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index dbfc82c891..eb3d09ac96 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -1,26 +1,26 @@ import logging from esphome import automation, core +import esphome.codegen as cg from esphome.components import font import esphome.components.image as espImage from esphome.components.image import ( CONF_USE_TRANSPARENCY, LOCAL_SCHEMA, - WEB_SCHEMA, - SOURCE_WEB, SOURCE_LOCAL, + SOURCE_WEB, + WEB_SCHEMA, ) import esphome.config_validation as cv -import esphome.codegen as cg from esphome.const import ( CONF_FILE, CONF_ID, + CONF_PATH, CONF_RAW_DATA_ID, CONF_REPEAT, CONF_RESIZE, - CONF_TYPE, CONF_SOURCE, - CONF_PATH, + CONF_TYPE, CONF_URL, ) from esphome.core import CORE, HexInt @@ -172,6 +172,9 @@ async def to_code(config): path = CORE.relative_config_path(conf_file[CONF_PATH]) elif conf_file[CONF_SOURCE] == SOURCE_WEB: path = espImage.compute_local_image_path(conf_file).as_posix() + else: + raise core.EsphomeError(f"Unknown animation source: {conf_file[CONF_SOURCE]}") + try: image = Image.open(path) except Exception as e: @@ -183,13 +186,12 @@ async def to_code(config): new_width_max, new_height_max = config[CONF_RESIZE] ratio = min(new_width_max / width, new_height_max / height) width, height = int(width * ratio), int(height * ratio) - else: - if width > 500 or height > 500: - _LOGGER.warning( - 'The image "%s" you requested is very big. Please consider' - " using the resize parameter.", - path, - ) + elif width > 500 or height > 500: + _LOGGER.warning( + 'The image "%s" you requested is very big. Please consider' + " using the resize parameter.", + path, + ) transparent = config[CONF_USE_TRANSPARENCY] @@ -306,6 +308,8 @@ async def to_code(config): if transparent: alpha = image.split()[-1] has_alpha = alpha.getextrema()[0] < 0xFF + else: + has_alpha = False frame = image.convert("1", dither=Image.Dither.NONE) if CONF_RESIZE in config: frame = frame.resize([width, height]) From cf4bfcdce8b9492872ff22fbbd449d4079c1be9c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 13 Sep 2024 22:03:25 +1200 Subject: [PATCH 0359/1052] [thermostat] Fix linting and formatting issues (#7442) --- esphome/components/thermostat/climate.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/esphome/components/thermostat/climate.py b/esphome/components/thermostat/climate.py index 89d6b13376..a529bbd474 100644 --- a/esphome/components/thermostat/climate.py +++ b/esphome/components/thermostat/climate.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import climate, sensor +import esphome.config_validation as cv from esphome.const import ( CONF_AUTO_MODE, CONF_AWAY_CONFIG, @@ -15,15 +15,15 @@ from esphome.const import ( CONF_DRY_ACTION, CONF_DRY_MODE, CONF_FAN_MODE, - CONF_FAN_MODE_ON_ACTION, - CONF_FAN_MODE_OFF_ACTION, CONF_FAN_MODE_AUTO_ACTION, + CONF_FAN_MODE_DIFFUSE_ACTION, + CONF_FAN_MODE_FOCUS_ACTION, + CONF_FAN_MODE_HIGH_ACTION, CONF_FAN_MODE_LOW_ACTION, CONF_FAN_MODE_MEDIUM_ACTION, - CONF_FAN_MODE_HIGH_ACTION, CONF_FAN_MODE_MIDDLE_ACTION, - CONF_FAN_MODE_FOCUS_ACTION, - CONF_FAN_MODE_DIFFUSE_ACTION, + CONF_FAN_MODE_OFF_ACTION, + CONF_FAN_MODE_ON_ACTION, CONF_FAN_MODE_QUIET_ACTION, CONF_FAN_ONLY_ACTION, CONF_FAN_ONLY_ACTION_USES_FAN_MODE_TIMER, @@ -50,8 +50,8 @@ from esphome.const import ( CONF_MIN_HEATING_RUN_TIME, CONF_MIN_IDLE_TIME, CONF_MIN_TEMPERATURE, - CONF_NAME, CONF_MODE, + CONF_NAME, CONF_OFF_MODE, CONF_PRESET, CONF_SENSOR, @@ -892,7 +892,7 @@ async def to_code(config): if name.upper() in climate.CLIMATE_PRESETS: standard_preset = climate.CLIMATE_PRESETS[name.upper()] - if two_points_available is True: + if two_points_available: preset_target_config = ThermostatClimateTargetTempConfig( preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH], @@ -905,6 +905,8 @@ async def to_code(config): preset_target_config = ThermostatClimateTargetTempConfig( preset_config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW] ) + else: + preset_target_config = None preset_target_variable = cg.new_variable( preset_config[CONF_ID], preset_target_config From de19d25a3c7f3993df2d51e89a7589eeaa6786b5 Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Mon, 16 Sep 2024 00:59:10 +0300 Subject: [PATCH 0360/1052] Add OpenTherm component (part 1: communication layer and hub) (#6645) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/opentherm/__init__.py | 57 ++ esphome/components/opentherm/hub.cpp | 277 +++++++++ esphome/components/opentherm/hub.h | 110 ++++ esphome/components/opentherm/opentherm.cpp | 568 ++++++++++++++++++ esphome/components/opentherm/opentherm.h | 347 +++++++++++ tests/components/opentherm/common.yaml | 3 + .../components/opentherm/test.esp32-ard.yaml | 1 + .../opentherm/test.esp32-c3-ard.yaml | 1 + .../opentherm/test.esp32-c3-idf.yaml | 1 + .../components/opentherm/test.esp32-idf.yaml | 1 + .../opentherm/test.esp8266-ard.yaml | 1 + 12 files changed, 1368 insertions(+) create mode 100644 esphome/components/opentherm/__init__.py create mode 100644 esphome/components/opentherm/hub.cpp create mode 100644 esphome/components/opentherm/hub.h create mode 100644 esphome/components/opentherm/opentherm.cpp create mode 100644 esphome/components/opentherm/opentherm.h create mode 100644 tests/components/opentherm/common.yaml create mode 100644 tests/components/opentherm/test.esp32-ard.yaml create mode 100644 tests/components/opentherm/test.esp32-c3-ard.yaml create mode 100644 tests/components/opentherm/test.esp32-c3-idf.yaml create mode 100644 tests/components/opentherm/test.esp32-idf.yaml create mode 100644 tests/components/opentherm/test.esp8266-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 0b1b88fbc8..f7fbbf9374 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -289,6 +289,7 @@ esphome/components/noblex/* @AGalfra esphome/components/number/* @esphome/core esphome/components/one_wire/* @ssieb esphome/components/online_image/* @guillempages +esphome/components/opentherm/* @olegtarasov esphome/components/ota/* @esphome/core esphome/components/output/* @esphome/core esphome/components/pca6416a/* @Mat931 diff --git a/esphome/components/opentherm/__init__.py b/esphome/components/opentherm/__init__.py new file mode 100644 index 0000000000..23443a4028 --- /dev/null +++ b/esphome/components/opentherm/__init__.py @@ -0,0 +1,57 @@ +from typing import Any + +from esphome import pins +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266 + +CODEOWNERS = ["@olegtarasov"] +MULTI_CONF = True + +CONF_IN_PIN = "in_pin" +CONF_OUT_PIN = "out_pin" +CONF_CH_ENABLE = "ch_enable" +CONF_DHW_ENABLE = "dhw_enable" +CONF_COOLING_ENABLE = "cooling_enable" +CONF_OTC_ACTIVE = "otc_active" +CONF_CH2_ACTIVE = "ch2_active" +CONF_SYNC_MODE = "sync_mode" + +opentherm_ns = cg.esphome_ns.namespace("opentherm") +OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(OpenthermHub), + cv.Required(CONF_IN_PIN): pins.internal_gpio_input_pin_schema, + cv.Required(CONF_OUT_PIN): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_CH_ENABLE, True): cv.boolean, + cv.Optional(CONF_DHW_ENABLE, True): cv.boolean, + cv.Optional(CONF_COOLING_ENABLE, False): cv.boolean, + cv.Optional(CONF_OTC_ACTIVE, False): cv.boolean, + cv.Optional(CONF_CH2_ACTIVE, False): cv.boolean, + cv.Optional(CONF_SYNC_MODE, False): cv.boolean, + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]), +) + + +async def to_code(config: dict[str, Any]) -> None: + # Create the hub, passing the two callbacks defined below + # Since the hub is used in the callbacks, we need to define it first + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + # Set pins + in_pin = await cg.gpio_pin_expression(config[CONF_IN_PIN]) + cg.add(var.set_in_pin(in_pin)) + + out_pin = await cg.gpio_pin_expression(config[CONF_OUT_PIN]) + cg.add(var.set_out_pin(out_pin)) + + non_sensors = {CONF_ID, CONF_IN_PIN, CONF_OUT_PIN} + for key, value in config.items(): + if key not in non_sensors: + cg.add(getattr(var, f"set_{key}")(value)) diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp new file mode 100644 index 0000000000..c26fbced32 --- /dev/null +++ b/esphome/components/opentherm/hub.cpp @@ -0,0 +1,277 @@ +#include "hub.h" +#include "esphome/core/helpers.h" + +#include + +namespace esphome { +namespace opentherm { + +static const char *const TAG = "opentherm"; + +OpenthermData OpenthermHub::build_request_(MessageId request_id) { + OpenthermData data; + data.type = 0; + data.id = 0; + data.valueHB = 0; + data.valueLB = 0; + + // First, handle the status request. This requires special logic, because we + // wouldn't want to inadvertently disable domestic hot water, for example. + // It is also included in the macro-generated code below, but that will + // never be executed, because we short-circuit it here. + if (request_id == MessageId::STATUS) { + bool const ch_enabled = this->ch_enable; + bool dhw_enabled = this->dhw_enable; + bool cooling_enabled = this->cooling_enable; + bool otc_enabled = this->otc_active; + bool ch2_enabled = this->ch2_active; + + data.type = MessageType::READ_DATA; + data.id = MessageId::STATUS; + data.valueHB = ch_enabled | (dhw_enabled << 1) | (cooling_enabled << 2) | (otc_enabled << 3) | (ch2_enabled << 4); + +// Disable incomplete switch statement warnings, because the cases in each +// switch are generated based on the configured sensors and inputs. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" + + // TODO: This is a placeholder for an auto-generated switch statement which builds request structure based on + // which sensors are enabled in config. + +#pragma GCC diagnostic pop + + return data; + } + return OpenthermData(); +} + +OpenthermHub::OpenthermHub() : Component() {} + +void OpenthermHub::process_response(OpenthermData &data) { + ESP_LOGD(TAG, "Received OpenTherm response with id %d (%s)", data.id, + this->opentherm_->message_id_to_str((MessageId) data.id)); + ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(data).c_str()); +} + +void OpenthermHub::setup() { + ESP_LOGD(TAG, "Setting up OpenTherm component"); + this->opentherm_ = make_unique(this->in_pin_, this->out_pin_); + if (!this->opentherm_->initialize()) { + ESP_LOGE(TAG, "Failed to initialize OpenTherm protocol. See previous log messages for details."); + this->mark_failed(); + return; + } + + // Ensure that there is at least one request, as we are required to + // communicate at least once every second. Sending the status request is + // good practice anyway. + this->add_repeating_message(MessageId::STATUS); + + this->current_message_iterator_ = this->initial_messages_.begin(); +} + +void OpenthermHub::on_shutdown() { this->opentherm_->stop(); } + +void OpenthermHub::loop() { + if (this->sync_mode_) { + this->sync_loop_(); + return; + } + + auto cur_time = millis(); + auto const cur_mode = this->opentherm_->get_mode(); + switch (cur_mode) { + case OperationMode::WRITE: + case OperationMode::READ: + case OperationMode::LISTEN: + if (!this->check_timings_(cur_time)) { + break; + } + this->last_mode_ = cur_mode; + break; + case OperationMode::ERROR_PROTOCOL: + if (this->last_mode_ == OperationMode::WRITE) { + this->handle_protocol_write_error_(); + } else if (this->last_mode_ == OperationMode::READ) { + this->handle_protocol_read_error_(); + } + + this->stop_opentherm_(); + break; + case OperationMode::ERROR_TIMEOUT: + this->handle_timeout_error_(); + this->stop_opentherm_(); + break; + case OperationMode::IDLE: + if (this->should_skip_loop_(cur_time)) { + break; + } + this->start_conversation_(); + break; + case OperationMode::SENT: + // Message sent, now listen for the response. + this->opentherm_->listen(); + break; + case OperationMode::RECEIVED: + this->read_response_(); + break; + } +} + +void OpenthermHub::sync_loop_() { + if (!this->opentherm_->is_idle()) { + ESP_LOGE(TAG, "OpenTherm is not idle at the start of the loop"); + return; + } + + auto cur_time = millis(); + + this->check_timings_(cur_time); + + if (this->should_skip_loop_(cur_time)) { + return; + } + + this->start_conversation_(); + + if (!this->spin_wait_(1150, [&] { return this->opentherm_->is_active(); })) { + ESP_LOGE(TAG, "Hub timeout triggered during send"); + this->stop_opentherm_(); + return; + } + + if (this->opentherm_->is_error()) { + this->handle_protocol_write_error_(); + this->stop_opentherm_(); + return; + } else if (!this->opentherm_->is_sent()) { + ESP_LOGW(TAG, "Unexpected state after sending request: %s", + this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode())); + this->stop_opentherm_(); + return; + } + + // Listen for the response + this->opentherm_->listen(); + if (!this->spin_wait_(1150, [&] { return this->opentherm_->is_active(); })) { + ESP_LOGE(TAG, "Hub timeout triggered during receive"); + this->stop_opentherm_(); + return; + } + + if (this->opentherm_->is_timeout()) { + this->handle_timeout_error_(); + this->stop_opentherm_(); + return; + } else if (this->opentherm_->is_protocol_error()) { + this->handle_protocol_read_error_(); + this->stop_opentherm_(); + return; + } else if (!this->opentherm_->has_message()) { + ESP_LOGW(TAG, "Unexpected state after receiving response: %s", + this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode())); + this->stop_opentherm_(); + return; + } + + this->read_response_(); +} + +bool OpenthermHub::check_timings_(uint32_t cur_time) { + if (this->last_conversation_start_ > 0 && (cur_time - this->last_conversation_start_) > 1150) { + ESP_LOGW(TAG, + "%d ms elapsed since the start of the last convo, but 1150 ms are allowed at maximum. Look at other " + "components that might slow the loop down.", + (int) (cur_time - this->last_conversation_start_)); + this->stop_opentherm_(); + return false; + } + + return true; +} + +bool OpenthermHub::should_skip_loop_(uint32_t cur_time) const { + if (this->last_conversation_end_ > 0 && (cur_time - this->last_conversation_end_) < 100) { + ESP_LOGV(TAG, "Less than 100 ms elapsed since last convo, skipping this iteration"); + return true; + } + + return false; +} + +void OpenthermHub::start_conversation_() { + if (this->sending_initial_ && this->current_message_iterator_ == this->initial_messages_.end()) { + this->sending_initial_ = false; + this->current_message_iterator_ = this->repeating_messages_.begin(); + } else if (this->current_message_iterator_ == this->repeating_messages_.end()) { + this->current_message_iterator_ = this->repeating_messages_.begin(); + } + + auto request = this->build_request_(*this->current_message_iterator_); + + ESP_LOGD(TAG, "Sending request with id %d (%s)", request.id, + this->opentherm_->message_id_to_str((MessageId) request.id)); + ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(request).c_str()); + // Send the request + this->last_conversation_start_ = millis(); + this->opentherm_->send(request); +} + +void OpenthermHub::read_response_() { + OpenthermData response; + if (!this->opentherm_->get_message(response)) { + ESP_LOGW(TAG, "Couldn't get the response, but flags indicated success. This is a bug."); + this->stop_opentherm_(); + return; + } + + this->stop_opentherm_(); + + this->process_response(response); + + this->current_message_iterator_++; +} + +void OpenthermHub::stop_opentherm_() { + this->opentherm_->stop(); + this->last_conversation_end_ = millis(); +} + +void OpenthermHub::handle_protocol_write_error_() { + ESP_LOGW(TAG, "Error while sending request: %s", + this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode())); + ESP_LOGW(TAG, "%s", this->opentherm_->debug_data(this->last_request_).c_str()); +} + +void OpenthermHub::handle_protocol_read_error_() { + OpenThermError error; + this->opentherm_->get_protocol_error(error); + ESP_LOGW(TAG, "Protocol error occured while receiving response: %s", this->opentherm_->debug_error(error).c_str()); +} + +void OpenthermHub::handle_timeout_error_() { + ESP_LOGW(TAG, "Receive response timed out at a protocol level"); + this->stop_opentherm_(); +} + +#define ID(x) x +#define SHOW2(x) #x +#define SHOW(x) SHOW2(x) + +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: %d", this->sync_mode_); + ESP_LOGCONFIG(TAG, " Initial requests:"); + for (auto type : this->initial_messages_) { + ESP_LOGCONFIG(TAG, " - %d", type); + } + ESP_LOGCONFIG(TAG, " Repeating requests:"); + for (auto type : this->repeating_messages_) { + ESP_LOGCONFIG(TAG, " - %d", type); + } +} + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/hub.h b/esphome/components/opentherm/hub.h new file mode 100644 index 0000000000..ce9f09fe33 --- /dev/null +++ b/esphome/components/opentherm/hub.h @@ -0,0 +1,110 @@ +#pragma once + +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" +#include "esphome/core/component.h" +#include "esphome/core/log.h" + +#include "opentherm.h" + +#include +#include +#include +#include + +namespace esphome { +namespace opentherm { + +// OpenTherm component for ESPHome +class OpenthermHub : public Component { + protected: + // Communication pins for the OpenTherm interface + InternalGPIOPin *in_pin_, *out_pin_; + // The OpenTherm interface + std::unique_ptr opentherm_; + + // The set of initial messages to send on starting communication with the boiler + std::unordered_set initial_messages_; + // and the repeating messages which are sent repeatedly to update various sensors + // and boiler parameters (like the setpoint). + std::unordered_set repeating_messages_; + // Indicates if we are still working on the initial requests or not + bool sending_initial_ = true; + // Index for the current request in one of the _requests sets. + std::unordered_set::const_iterator current_message_iterator_; + + uint32_t last_conversation_start_ = 0; + uint32_t last_conversation_end_ = 0; + OperationMode last_mode_ = IDLE; + OpenthermData last_request_; + + // Synchronous communication mode prevents other components from disabling interrupts while + // we are talking to the boiler. Enable if you experience random intermittent invalid response errors. + // Very likely to happen while using Dallas temperature sensors. + bool sync_mode_ = false; + + // Create OpenTherm messages based on the message id + OpenthermData build_request_(MessageId request_id); + void handle_protocol_write_error_(); + void handle_protocol_read_error_(); + void handle_timeout_error_(); + void stop_opentherm_(); + void start_conversation_(); + void read_response_(); + bool check_timings_(uint32_t cur_time); + bool should_skip_loop_(uint32_t cur_time) const; + void sync_loop_(); + + template bool spin_wait_(uint32_t timeout, F func) { + auto start_time = millis(); + while (func()) { + yield(); + auto cur_time = millis(); + if (cur_time - start_time >= timeout) { + return false; + } + } + return true; + } + + public: + // Constructor with references to the global interrupt handlers + OpenthermHub(); + + // Handle responses from the OpenTherm interface + void process_response(OpenthermData &data); + + // Setters for the input and output OpenTherm interface pins + void set_in_pin(InternalGPIOPin *in_pin) { this->in_pin_ = in_pin; } + void set_out_pin(InternalGPIOPin *out_pin) { this->out_pin_ = out_pin; } + + // Add a request to the set of initial requests + void add_initial_message(MessageId message_id) { this->initial_messages_.insert(message_id); } + // Add a request to the set of repeating requests. Note that a large number of repeating + // requests will slow down communication with the boiler. Each request may take up to 1 second, + // so with all sensors enabled, it may take about half a minute before a change in setpoint + // will be processed. + void add_repeating_message(MessageId message_id) { this->repeating_messages_.insert(message_id); } + + // There are five status variables, which can either be set as a simple variable, + // or using a switch. ch_enable and dhw_enable default to true, the others to false. + bool ch_enable = true, dhw_enable = true, cooling_enable = false, otc_active = false, ch2_active = false; + + // Setters for the status variables + void set_ch_enable(bool value) { this->ch_enable = value; } + void set_dhw_enable(bool value) { this->dhw_enable = value; } + void set_cooling_enable(bool value) { this->cooling_enable = value; } + void set_otc_active(bool value) { this->otc_active = value; } + void set_ch2_active(bool value) { this->ch2_active = value; } + void set_sync_mode(bool sync_mode) { this->sync_mode_ = sync_mode; } + + float get_setup_priority() const override { return setup_priority::HARDWARE; } + + void setup() override; + void on_shutdown() override; + void loop() override; + void dump_config() override; +}; + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp new file mode 100644 index 0000000000..b830cc01d3 --- /dev/null +++ b/esphome/components/opentherm/opentherm.cpp @@ -0,0 +1,568 @@ +/* + * OpenTherm protocol implementation. Originally taken from https://github.com/jpraus/arduino-opentherm, but + * heavily modified to comply with ESPHome coding standards and provide better logging. + * Original code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International + * Public License, which is compatible with GPLv3 license, which covers C++ part of ESPHome project. + */ + +#include "opentherm.h" +#include "esphome/core/helpers.h" +#if defined(ESP32) || defined(USE_ESP_IDF) +#include "driver/timer.h" +#include "esp_err.h" +#endif +#ifdef ESP8266 +#include "Arduino.h" +#endif +#include +#include +#include + +namespace esphome { +namespace opentherm { + +using std::string; +using std::bitset; +using std::stringstream; +using std::to_string; + +static const char *const TAG = "opentherm"; + +#ifdef ESP8266 +OpenTherm *OpenTherm::instance_ = nullptr; +#endif + +OpenTherm::OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout) + : in_pin_(in_pin), + out_pin_(out_pin), +#if defined(ESP32) || defined(USE_ESP_IDF) + timer_group_(TIMER_GROUP_0), + timer_idx_(TIMER_0), +#endif + mode_(OperationMode::IDLE), + error_type_(ProtocolErrorType::NO_ERROR), + capture_(0), + clock_(0), + data_(0), + bit_pos_(0), + timeout_counter_(-1), + device_timeout_(device_timeout) { + this->isr_in_pin_ = in_pin->to_isr(); + this->isr_out_pin_ = out_pin->to_isr(); +} + +bool OpenTherm::initialize() { +#ifdef ESP8266 + OpenTherm::instance_ = this; +#endif + this->in_pin_->pin_mode(gpio::FLAG_INPUT); + this->out_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->out_pin_->digital_write(true); + +#if defined(ESP32) || defined(USE_ESP_IDF) + return this->init_esp32_timer_(); +#else + return true; +#endif +} + +void OpenTherm::listen() { + this->stop_timer_(); + this->timeout_counter_ = this->device_timeout_ * 5; // timer_ ticks at 5 ticks/ms + + this->mode_ = OperationMode::LISTEN; + this->data_ = 0; + this->bit_pos_ = 0; + + this->start_read_timer_(); +} + +void OpenTherm::send(OpenthermData &data) { + this->stop_timer_(); + this->data_ = data.type; + this->data_ = (this->data_ << 12) | data.id; + this->data_ = (this->data_ << 8) | data.valueHB; + this->data_ = (this->data_ << 8) | data.valueLB; + if (!check_parity_(this->data_)) { + this->data_ = this->data_ | 0x80000000; + } + + this->clock_ = 1; // clock starts at HIGH + this->bit_pos_ = 33; // count down (33 == start bit, 32-1 data, 0 == stop bit) + this->mode_ = OperationMode::WRITE; + + this->start_write_timer_(); +} + +bool OpenTherm::get_message(OpenthermData &data) { + if (this->mode_ == OperationMode::RECEIVED) { + data.type = (this->data_ >> 28) & 0x7; + data.id = (this->data_ >> 16) & 0xFF; + data.valueHB = (this->data_ >> 8) & 0xFF; + data.valueLB = this->data_ & 0xFF; + return true; + } + return false; +} + +bool OpenTherm::get_protocol_error(OpenThermError &error) { + if (this->mode_ != OperationMode::ERROR_PROTOCOL) { + return false; + } + + error.error_type = this->error_type_; + error.bit_pos = this->bit_pos_; + error.capture = this->capture_; + error.clock = this->clock_; + error.data = this->data_; + + return true; +} + +void OpenTherm::stop() { + this->stop_timer_(); + this->mode_ = OperationMode::IDLE; +} + +void IRAM_ATTR OpenTherm::read_() { + this->data_ = 0; + this->bit_pos_ = 0; + this->mode_ = OperationMode::READ; + this->capture_ = 1; // reset counter and add as if read start bit + this->clock_ = 1; // clock is high at the start of comm + this->start_read_timer_(); // get us into 1/4 of manchester code. 5 timer ticks constitute 1 ms, which is 1 bit + // period in OpenTherm. +} + +bool IRAM_ATTR OpenTherm::timer_isr(OpenTherm *arg) { + if (arg->mode_ == OperationMode::LISTEN) { + if (arg->timeout_counter_ == 0) { + arg->mode_ = OperationMode::ERROR_TIMEOUT; + arg->stop_timer_(); + return false; + } + bool const value = arg->isr_in_pin_.digital_read(); + if (value) { // incoming data (rising signal) + arg->read_(); + } + if (arg->timeout_counter_ > 0) { + arg->timeout_counter_--; + } + } else if (arg->mode_ == OperationMode::READ) { + bool const value = arg->isr_in_pin_.digital_read(); + uint8_t const last = (arg->capture_ & 1); + if (value != last) { + // transition of signal from last sampling + if (arg->clock_ == 1 && arg->capture_ > 0xF) { + // no transition in the middle of the bit + arg->mode_ = OperationMode::ERROR_PROTOCOL; + arg->error_type_ = ProtocolErrorType::NO_TRANSITION; + arg->stop_timer_(); + return false; + } else if (arg->clock_ == 1 || arg->capture_ > 0xF) { + // transition in the middle of the bit OR no transition between two bit, both are valid data points + if (arg->bit_pos_ == BitPositions::STOP_BIT) { + // expecting stop bit + auto stop_bit_error = arg->verify_stop_bit_(last); + if (stop_bit_error == ProtocolErrorType::NO_ERROR) { + arg->mode_ = OperationMode::RECEIVED; + arg->stop_timer_(); + return false; + } else { + // end of data not verified, invalid data + arg->mode_ = OperationMode::ERROR_PROTOCOL; + arg->error_type_ = stop_bit_error; + arg->stop_timer_(); + return false; + } + } else { + // normal data point at clock high + arg->bit_read_(last); + arg->clock_ = 0; + } + } else { + // clock low, not a data point, switch clock + arg->clock_ = 1; + } + arg->capture_ = 1; // reset counter + } else if (arg->capture_ > 0xFF) { + // no change for too long, invalid mancheter encoding + arg->mode_ = OperationMode::ERROR_PROTOCOL; + arg->error_type_ = ProtocolErrorType::NO_CHANGE_TOO_LONG; + arg->stop_timer_(); + return false; + } + arg->capture_ = (arg->capture_ << 1) | value; + } else if (arg->mode_ == OperationMode::WRITE) { + // write data to pin + if (arg->bit_pos_ == 33 || arg->bit_pos_ == 0) { // start bit + arg->write_bit_(1, arg->clock_); + } else { // data bits + arg->write_bit_(read_bit(arg->data_, arg->bit_pos_ - 1), arg->clock_); + } + if (arg->clock_ == 0) { + if (arg->bit_pos_ <= 0) { // check termination + arg->mode_ = OperationMode::SENT; // all data written + arg->stop_timer_(); + } + arg->bit_pos_--; + arg->clock_ = 1; + } else { + arg->clock_ = 0; + } + } + + return false; +} + +#ifdef ESP8266 +void IRAM_ATTR OpenTherm::esp8266_timer_isr() { OpenTherm::timer_isr(OpenTherm::instance_); } +#endif + +void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) { + this->data_ = (this->data_ << 1) | value; + this->bit_pos_++; +} + +ProtocolErrorType OpenTherm::verify_stop_bit_(uint8_t value) { + if (value) { // stop bit detected + return check_parity_(this->data_) ? ProtocolErrorType::NO_ERROR : ProtocolErrorType::PARITY_ERROR; + } else { // no stop bit detected, error + return ProtocolErrorType::INVALID_STOP_BIT; + } +} + +void IRAM_ATTR OpenTherm::write_bit_(uint8_t high, uint8_t clock) { + if (clock == 1) { // left part of manchester encoding + this->isr_out_pin_.digital_write(!high); // low means logical 1 to protocol + } else { // right part of manchester encoding + this->isr_out_pin_.digital_write(high); // high means logical 0 to protocol + } +} + +#if defined(ESP32) || defined(USE_ESP_IDF) + +bool OpenTherm::init_esp32_timer_() { + // Search for a free timer. Maybe unstable, we'll see. + int cur_timer = 0; + timer_group_t timer_group = TIMER_GROUP_0; + timer_idx_t timer_idx = TIMER_0; + bool timer_found = false; + + for (; cur_timer < SOC_TIMER_GROUP_TOTAL_TIMERS; cur_timer++) { + timer_config_t temp_config; + timer_group = cur_timer < 2 ? TIMER_GROUP_0 : TIMER_GROUP_1; + timer_idx = cur_timer < 2 ? (timer_idx_t) cur_timer : (timer_idx_t) (cur_timer - 2); + + auto err = timer_get_config(timer_group, timer_idx, &temp_config); + if (err == ESP_ERR_INVALID_ARG) { + // Error means timer was not initialized (or other things, but we are careful with our args) + timer_found = true; + break; + } + + ESP_LOGD(TAG, "Timer %d:%d seems to be occupied, will try another", timer_group, timer_idx); + } + + if (!timer_found) { + ESP_LOGE(TAG, "No free timer was found! OpenTherm cannot function without a timer."); + return false; + } + + ESP_LOGD(TAG, "Found free timer %d:%d", timer_group, timer_idx); + this->timer_group_ = timer_group; + this->timer_idx_ = timer_idx; + + timer_config_t const config = { + .alarm_en = TIMER_ALARM_EN, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL, + .counter_dir = TIMER_COUNT_UP, + .auto_reload = TIMER_AUTORELOAD_EN, +#if ESP_IDF_VERSION_MAJOR >= 5 + .clk_src = TIMER_SRC_CLK_DEFAULT, +#endif + .divider = 80, + }; + + esp_err_t result; + + result = timer_init(this->timer_group_, this->timer_idx_, &config); + if (result != ESP_OK) { + const auto *error = esp_err_to_name(result); + ESP_LOGE(TAG, "Failed to init timer. Error: %s", error); + return false; + } + + result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0); + if (result != ESP_OK) { + const auto *error = esp_err_to_name(result); + ESP_LOGE(TAG, "Failed to set counter value. Error: %s", error); + return false; + } + + result = timer_isr_callback_add(this->timer_group_, this->timer_idx_, reinterpret_cast(timer_isr), + this, 0); + if (result != ESP_OK) { + const auto *error = esp_err_to_name(result); + ESP_LOGE(TAG, "Failed to register timer interrupt. Error: %s", error); + return false; + } + + return true; +} + +void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) { + esp_err_t result; + + result = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value); + if (result != ESP_OK) { + const auto *error = esp_err_to_name(result); + ESP_LOGE(TAG, "Failed to set alarm value. Error: %s", error); + return; + } + + result = timer_start(this->timer_group_, this->timer_idx_); + if (result != ESP_OK) { + const auto *error = esp_err_to_name(result); + ESP_LOGE(TAG, "Failed to start the timer. Error: %s", error); + return; + } +} + +// 5 kHz timer_ +void IRAM_ATTR OpenTherm::start_read_timer_() { + InterruptLock const lock; + this->start_esp32_timer_(200); +} + +// 2 kHz timer_ +void IRAM_ATTR OpenTherm::start_write_timer_() { + InterruptLock const lock; + this->start_esp32_timer_(500); +} + +void IRAM_ATTR OpenTherm::stop_timer_() { + InterruptLock const lock; + + esp_err_t result; + + result = timer_pause(this->timer_group_, this->timer_idx_); + if (result != ESP_OK) { + const auto *error = esp_err_to_name(result); + ESP_LOGE(TAG, "Failed to pause the timer. Error: %s", error); + return; + } + + result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0); + if (result != ESP_OK) { + const auto *error = esp_err_to_name(result); + ESP_LOGE(TAG, "Failed to set timer counter to 0 after pausing. Error: %s", error); + return; + } +} + +#endif // END ESP32 + +#ifdef ESP8266 +// 5 kHz timer_ +void OpenTherm::start_read_timer_() { + InterruptLock const lock; + timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); + timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) + timer1_write(1000); // 5kHz +} + +// 2 kHz timer_ +void OpenTherm::start_write_timer_() { + InterruptLock const lock; + timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); + timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) + timer1_write(2500); // 2kHz +} + +void OpenTherm::stop_timer_() { + InterruptLock const lock; + timer1_disable(); + timer1_detachInterrupt(); +} + +#endif // END ESP8266 + +// https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd +bool OpenTherm::check_parity_(uint32_t val) { + val ^= val >> 16; + val ^= val >> 8; + val ^= val >> 4; + val ^= val >> 2; + val ^= val >> 1; + return (~val) & 1; +} + +#define TO_STRING_MEMBER(name) \ + case name: \ + return #name; + +const char *OpenTherm::operation_mode_to_str(OperationMode mode) { + switch (mode) { + TO_STRING_MEMBER(IDLE) + TO_STRING_MEMBER(LISTEN) + TO_STRING_MEMBER(READ) + TO_STRING_MEMBER(RECEIVED) + TO_STRING_MEMBER(WRITE) + TO_STRING_MEMBER(SENT) + TO_STRING_MEMBER(ERROR_PROTOCOL) + TO_STRING_MEMBER(ERROR_TIMEOUT) + default: + return ""; + } +} +const char *OpenTherm::protocol_error_to_to_str(ProtocolErrorType error_type) { + switch (error_type) { + TO_STRING_MEMBER(NO_ERROR) + TO_STRING_MEMBER(NO_TRANSITION) + TO_STRING_MEMBER(INVALID_STOP_BIT) + TO_STRING_MEMBER(PARITY_ERROR) + TO_STRING_MEMBER(NO_CHANGE_TOO_LONG) + default: + return ""; + } +} +const char *OpenTherm::message_type_to_str(MessageType message_type) { + switch (message_type) { + TO_STRING_MEMBER(READ_DATA) + TO_STRING_MEMBER(READ_ACK) + TO_STRING_MEMBER(WRITE_DATA) + TO_STRING_MEMBER(WRITE_ACK) + TO_STRING_MEMBER(INVALID_DATA) + TO_STRING_MEMBER(DATA_INVALID) + TO_STRING_MEMBER(UNKNOWN_DATAID) + default: + return ""; + } +} + +const char *OpenTherm::message_id_to_str(MessageId id) { + switch (id) { + TO_STRING_MEMBER(STATUS) + TO_STRING_MEMBER(CH_SETPOINT) + TO_STRING_MEMBER(CONTROLLER_CONFIG) + TO_STRING_MEMBER(DEVICE_CONFIG) + TO_STRING_MEMBER(COMMAND_CODE) + TO_STRING_MEMBER(FAULT_FLAGS) + TO_STRING_MEMBER(REMOTE) + TO_STRING_MEMBER(COOLING_CONTROL) + TO_STRING_MEMBER(CH2_SETPOINT) + TO_STRING_MEMBER(CH_SETPOINT_OVERRIDE) + TO_STRING_MEMBER(TSP_COUNT) + TO_STRING_MEMBER(TSP_COMMAND) + TO_STRING_MEMBER(FHB_SIZE) + TO_STRING_MEMBER(FHB_COMMAND) + TO_STRING_MEMBER(MAX_MODULATION_LEVEL) + TO_STRING_MEMBER(MAX_BOILER_CAPACITY) + TO_STRING_MEMBER(ROOM_SETPOINT) + TO_STRING_MEMBER(MODULATION_LEVEL) + TO_STRING_MEMBER(CH_WATER_PRESSURE) + TO_STRING_MEMBER(DHW_FLOW_RATE) + TO_STRING_MEMBER(DAY_TIME) + TO_STRING_MEMBER(DATE) + TO_STRING_MEMBER(YEAR) + TO_STRING_MEMBER(ROOM_SETPOINT_CH2) + TO_STRING_MEMBER(ROOM_TEMP) + TO_STRING_MEMBER(FEED_TEMP) + TO_STRING_MEMBER(DHW_TEMP) + TO_STRING_MEMBER(OUTSIDE_TEMP) + TO_STRING_MEMBER(RETURN_WATER_TEMP) + TO_STRING_MEMBER(SOLAR_STORE_TEMP) + TO_STRING_MEMBER(SOLAR_COLLECT_TEMP) + TO_STRING_MEMBER(FEED_TEMP_CH2) + TO_STRING_MEMBER(DHW2_TEMP) + TO_STRING_MEMBER(EXHAUST_TEMP) + TO_STRING_MEMBER(FAN_SPEED) + TO_STRING_MEMBER(FLAME_CURRENT) + TO_STRING_MEMBER(DHW_BOUNDS) + TO_STRING_MEMBER(CH_BOUNDS) + TO_STRING_MEMBER(OTC_CURVE_BOUNDS) + TO_STRING_MEMBER(DHW_SETPOINT) + TO_STRING_MEMBER(MAX_CH_SETPOINT) + TO_STRING_MEMBER(OTC_CURVE_RATIO) + TO_STRING_MEMBER(HVAC_STATUS) + TO_STRING_MEMBER(REL_VENT_SETPOINT) + TO_STRING_MEMBER(DEVICE_VENT) + TO_STRING_MEMBER(REL_VENTILATION) + TO_STRING_MEMBER(REL_HUMID_EXHAUST) + TO_STRING_MEMBER(SUPPLY_INLET_TEMP) + TO_STRING_MEMBER(SUPPLY_OUTLET_TEMP) + TO_STRING_MEMBER(EXHAUST_INLET_TEMP) + TO_STRING_MEMBER(EXHAUST_OUTLET_TEMP) + TO_STRING_MEMBER(NOM_REL_VENTILATION) + TO_STRING_MEMBER(OVERRIDE_FUNC) + TO_STRING_MEMBER(OEM_DIAGNOSTIC) + TO_STRING_MEMBER(BURNER_STARTS) + TO_STRING_MEMBER(CH_PUMP_STARTS) + TO_STRING_MEMBER(DHW_PUMP_STARTS) + TO_STRING_MEMBER(DHW_BURNER_STARTS) + TO_STRING_MEMBER(BURNER_HOURS) + TO_STRING_MEMBER(CH_PUMP_HOURS) + TO_STRING_MEMBER(DHW_PUMP_HOURS) + TO_STRING_MEMBER(DHW_BURNER_HOURS) + TO_STRING_MEMBER(OT_VERSION_CONTROLLER) + TO_STRING_MEMBER(OT_VERSION_DEVICE) + TO_STRING_MEMBER(VERSION_CONTROLLER) + TO_STRING_MEMBER(VERSION_DEVICE) + default: + return ""; + } +} + +string OpenTherm::debug_data(OpenthermData &data) { + stringstream result; + result << bitset<8>(data.type) << " " << bitset<8>(data.id) << " " << bitset<8>(data.valueHB) << " " + << bitset<8>(data.valueLB) << "\n"; + result << "type: " << this->message_type_to_str((MessageType) data.type) << "; "; + result << "id: " << to_string(data.id) << "; "; + result << "HB: " << to_string(data.valueHB) << "; "; + result << "LB: " << to_string(data.valueLB) << "; "; + result << "uint_16: " << to_string(data.u16()) << "; "; + result << "float: " << to_string(data.f88()); + + return result.str(); +} +std::string OpenTherm::debug_error(OpenThermError &error) { + stringstream result; + result << "type: " << this->protocol_error_to_to_str(error.error_type) << "; "; + result << "data: "; + result << format_hex(error.data); + result << "; clock: " << to_string(clock_); + result << "; capture: " << bitset<32>(error.capture); + result << "; bit_pos: " << to_string(error.bit_pos); + + return result.str(); +} + +float OpenthermData::f88() { return ((float) this->s16()) / 256.0; } + +void OpenthermData::f88(float value) { this->s16((int16_t) (value * 256)); } + +uint16_t OpenthermData::u16() { + uint16_t const value = this->valueHB; + return (value << 8) | this->valueLB; +} + +void OpenthermData::u16(uint16_t value) { + this->valueLB = value & 0xFF; + this->valueHB = (value >> 8) & 0xFF; +} + +int16_t OpenthermData::s16() { + int16_t const value = this->valueHB; + return (value << 8) | this->valueLB; +} + +void OpenthermData::s16(int16_t value) { + this->valueLB = value & 0xFF; + this->valueHB = (value >> 8) & 0xFF; +} + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h new file mode 100644 index 0000000000..609cfb6243 --- /dev/null +++ b/esphome/components/opentherm/opentherm.h @@ -0,0 +1,347 @@ +/* + * OpenTherm protocol implementation. Originally taken from https://github.com/jpraus/arduino-opentherm, but + * heavily modified to comply with ESPHome coding standards and provide better logging. + * Original code is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International + * Public License, which is compatible with GPLv3 license, which covers C++ part of ESPHome project. + */ + +#pragma once + +#include +#include +#include +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +#if defined(ESP32) || defined(USE_ESP_IDF) +#include "driver/timer.h" +#endif + +namespace esphome { +namespace opentherm { + +// TODO: Account for immutable semantics change in hub.cpp when doing later installments of OpenTherm PR +template constexpr T read_bit(T value, uint8_t bit) { return (value >> bit) & 0x01; } + +template constexpr T set_bit(T value, uint8_t bit) { return value |= (1UL << bit); } + +template constexpr T clear_bit(T value, uint8_t bit) { return value &= ~(1UL << bit); } + +template constexpr T write_bit(T value, uint8_t bit, uint8_t bit_value) { + return bit_value ? setBit(value, bit) : clearBit(value, bit); +} + +enum OperationMode { + IDLE = 0, // no operation + + LISTEN = 1, // waiting for transmission to start + READ = 2, // reading 32-bit data frame + RECEIVED = 3, // data frame received with valid start and stop bit + + WRITE = 4, // writing data with timer_ + SENT = 5, // all data written to output + + ERROR_PROTOCOL = 8, // manchester protocol data transfer error + ERROR_TIMEOUT = 9 // read timeout +}; + +enum ProtocolErrorType { + NO_ERROR = 0, // No error + NO_TRANSITION = 1, // No transition in the middle of the bit + INVALID_STOP_BIT = 2, // Stop bit wasn't present when expected + PARITY_ERROR = 3, // Parity check didn't pass + NO_CHANGE_TOO_LONG = 4, // No level change for too much timer ticks +}; + +enum MessageType { + READ_DATA = 0, + READ_ACK = 4, + WRITE_DATA = 1, + WRITE_ACK = 5, + INVALID_DATA = 2, + DATA_INVALID = 6, + UNKNOWN_DATAID = 7 +}; + +enum MessageId { + STATUS = 0, + CH_SETPOINT = 1, + CONTROLLER_CONFIG = 2, + DEVICE_CONFIG = 3, + COMMAND_CODE = 4, + FAULT_FLAGS = 5, + REMOTE = 6, + COOLING_CONTROL = 7, + CH2_SETPOINT = 8, + CH_SETPOINT_OVERRIDE = 9, + TSP_COUNT = 10, + TSP_COMMAND = 11, + FHB_SIZE = 12, + FHB_COMMAND = 13, + MAX_MODULATION_LEVEL = 14, + MAX_BOILER_CAPACITY = 15, // u8_hb - u8_lb gives min modulation level + ROOM_SETPOINT = 16, + MODULATION_LEVEL = 17, + CH_WATER_PRESSURE = 18, + DHW_FLOW_RATE = 19, + DAY_TIME = 20, + DATE = 21, + YEAR = 22, + ROOM_SETPOINT_CH2 = 23, + ROOM_TEMP = 24, + FEED_TEMP = 25, + DHW_TEMP = 26, + OUTSIDE_TEMP = 27, + RETURN_WATER_TEMP = 28, + SOLAR_STORE_TEMP = 29, + SOLAR_COLLECT_TEMP = 30, + FEED_TEMP_CH2 = 31, + DHW2_TEMP = 32, + EXHAUST_TEMP = 33, + FAN_SPEED = 35, + FLAME_CURRENT = 36, + DHW_BOUNDS = 48, + CH_BOUNDS = 49, + OTC_CURVE_BOUNDS = 50, + DHW_SETPOINT = 56, + MAX_CH_SETPOINT = 57, + OTC_CURVE_RATIO = 58, + + // HVAC Specific Message IDs + HVAC_STATUS = 70, + REL_VENT_SETPOINT = 71, + DEVICE_VENT = 74, + REL_VENTILATION = 77, + REL_HUMID_EXHAUST = 78, + SUPPLY_INLET_TEMP = 80, + SUPPLY_OUTLET_TEMP = 81, + EXHAUST_INLET_TEMP = 82, + EXHAUST_OUTLET_TEMP = 83, + NOM_REL_VENTILATION = 87, + + OVERRIDE_FUNC = 100, + OEM_DIAGNOSTIC = 115, + BURNER_STARTS = 116, + CH_PUMP_STARTS = 117, + DHW_PUMP_STARTS = 118, + DHW_BURNER_STARTS = 119, + BURNER_HOURS = 120, + CH_PUMP_HOURS = 121, + DHW_PUMP_HOURS = 122, + DHW_BURNER_HOURS = 123, + OT_VERSION_CONTROLLER = 124, + OT_VERSION_DEVICE = 125, + VERSION_CONTROLLER = 126, + VERSION_DEVICE = 127 +}; + +enum BitPositions { STOP_BIT = 33 }; + +/** + * Structure to hold Opentherm data packet content. + * Use f88(), u16() or s16() functions to get appropriate value of data packet accoridng to id of message. + */ +struct OpenthermData { + uint8_t type; + uint8_t id; + uint8_t valueHB; + uint8_t valueLB; + + OpenthermData() : type(0), id(0), valueHB(0), valueLB(0) {} + + /** + * @return float representation of data packet value + */ + float f88(); + + /** + * @param float number to set as value of this data packet + */ + void f88(float value); + + /** + * @return unsigned 16b integer representation of data packet value + */ + uint16_t u16(); + + /** + * @param unsigned 16b integer number to set as value of this data packet + */ + void u16(uint16_t value); + + /** + * @return signed 16b integer representation of data packet value + */ + int16_t s16(); + + /** + * @param signed 16b integer number to set as value of this data packet + */ + void s16(int16_t value); +}; + +struct OpenThermError { + ProtocolErrorType error_type; + uint32_t capture; + uint8_t clock; + uint32_t data; + uint8_t bit_pos; +}; + +/** + * Opentherm static class that supports either listening or sending Opentherm data packets in the same time + */ +class OpenTherm { + public: + OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout = 800); + + /** + * Setup pins. + */ + bool initialize(); + + /** + * Start listening for Opentherm data packet comming from line connected to given pin. + * If data packet is received then has_message() function returns true and data packet can be retrieved by calling + * get_message() function. If timeout > 0 then this function waits for incomming data package for timeout millis and + * if no data packet is recevived, error state is indicated by is_error() function. If either data packet is received + * or timeout is reached listening is stopped. + */ + void listen(); + + /** + * Use this function to check whether listen() function already captured a valid data packet. + * + * @return true if data packet has been captured from line by listen() function. + */ + bool has_message() { return mode_ == OperationMode::RECEIVED; } + + /** + * Use this to retrive data packed captured by listen() function. Data packet is ready when has_message() function + * returns true. This function can be called multiple times until stop() is called. + * + * @param data reference to data structure to which fill the data packet data. + * @return true if packet was ready and was filled into data structure passed, false otherwise. + */ + bool get_message(OpenthermData &data); + + /** + * Immediately send out Opentherm data packet to line connected on given pin. + * Completed data transfer is indicated by is_sent() function. + * Error state is indicated by is_error() function. + * + * @param data Opentherm data packet. + */ + void send(OpenthermData &data); + + /** + * Stops listening for data packet or sending out data packet and resets internal state of this class. + * Stops all timers and unattaches all interrupts. + */ + void stop(); + + /** + * Get protocol error details in case a protocol error occured. + * @param error reference to data structure to which fill the error details + * @return true if protocol error occured during last conversation, false otherwise. + */ + bool get_protocol_error(OpenThermError &error); + + /** + * Use this function to check whether send() function already finished sending data packed to line. + * + * @return true if data packet has been sent, false otherwise. + */ + bool is_sent() { return mode_ == OperationMode::SENT; } + + /** + * Indicates whether listinig or sending is not in progress. + * That also means that no timers are running and no interrupts are attached. + * + * @return true if listening nor sending is in progress. + */ + bool is_idle() { return mode_ == OperationMode::IDLE; } + + /** + * Indicates whether last listen() or send() operation ends up with an error. Includes both timeout and + * protocol errors. + * + * @return true if last listen() or send() operation ends up with an error. + */ + bool is_error() { return mode_ == OperationMode::ERROR_TIMEOUT || mode_ == OperationMode::ERROR_PROTOCOL; } + + /** + * Indicates whether last listen() or send() operation ends up with a *timeout* error + * @return true if last listen() or send() operation ends up with a *timeout* error. + */ + bool is_timeout() { return mode_ == OperationMode::ERROR_TIMEOUT; } + + /** + * Indicates whether last listen() or send() operation ends up with a *protocol* error + * @return true if last listen() or send() operation ends up with a *protocol* error. + */ + bool is_protocol_error() { return mode_ == OperationMode::ERROR_PROTOCOL; } + + bool is_active() { return mode_ == LISTEN || mode_ == READ || mode_ == WRITE; } + + OperationMode get_mode() { return mode_; } + + std::string debug_data(OpenthermData &data); + std::string debug_error(OpenThermError &error); + + const char *protocol_error_to_to_str(ProtocolErrorType error_type); + const char *message_type_to_str(MessageType message_type); + const char *operation_mode_to_str(OperationMode mode); + const char *message_id_to_str(MessageId id); + + static bool timer_isr(OpenTherm *arg); + +#ifdef ESP8266 + static void esp8266_timer_isr(); +#endif + + private: + InternalGPIOPin *in_pin_; + InternalGPIOPin *out_pin_; + ISRInternalGPIOPin isr_in_pin_; + ISRInternalGPIOPin isr_out_pin_; + +#if defined(ESP32) || defined(USE_ESP_IDF) + timer_group_t timer_group_; + timer_idx_t timer_idx_; +#endif + + OperationMode mode_; + ProtocolErrorType error_type_; + uint32_t capture_; + uint8_t clock_; + uint32_t data_; + uint8_t bit_pos_; + int32_t timeout_counter_; // <0 no timeout + + int32_t device_timeout_; + +#if defined(ESP32) || defined(USE_ESP_IDF) + bool init_esp32_timer_(); + void start_esp32_timer_(uint64_t alarm_value); +#endif + + void stop_timer_(); + + void read_(); // data detected start reading + void start_read_timer_(); // reading timer_ to sample at 1/5 of manchester code bit length (at 5kHz) + void start_write_timer_(); // writing timer_ to send manchester code (at 2kHz) + bool check_parity_(uint32_t val); + + void bit_read_(uint8_t value); + ProtocolErrorType verify_stop_bit_(uint8_t value); + void write_bit_(uint8_t high, uint8_t clock); + +#ifdef ESP8266 + // ESP8266 timer can accept callback with no parameters, so we have this hack to save a static instance of OpenTherm + static OpenTherm *instance_; +#endif +}; + +} // namespace opentherm +} // namespace esphome diff --git a/tests/components/opentherm/common.yaml b/tests/components/opentherm/common.yaml new file mode 100644 index 0000000000..4148b280d0 --- /dev/null +++ b/tests/components/opentherm/common.yaml @@ -0,0 +1,3 @@ +opentherm: + in_pin: 1 + out_pin: 2 diff --git a/tests/components/opentherm/test.esp32-ard.yaml b/tests/components/opentherm/test.esp32-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/opentherm/test.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/opentherm/test.esp32-c3-ard.yaml b/tests/components/opentherm/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/opentherm/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/opentherm/test.esp32-c3-idf.yaml b/tests/components/opentherm/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/opentherm/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/opentherm/test.esp32-idf.yaml b/tests/components/opentherm/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/opentherm/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/opentherm/test.esp8266-ard.yaml b/tests/components/opentherm/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/opentherm/test.esp8266-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 7f00b5eb658e2871afb73c5988d49b8da0fbde8b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:08:41 +1200 Subject: [PATCH 0361/1052] [voice-assistant] Dont error on ``no_wake_word`` timeout error with streaming wake word (#7435) --- esphome/components/voice_assistant/voice_assistant.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 577de630fb..a2210f188d 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -755,7 +755,7 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { message = std::move(arg.value); } } - if (code == "wake-word-timeout" || code == "wake_word_detection_aborted") { + if (code == "wake-word-timeout" || code == "wake_word_detection_aborted" || code == "no_wake_word") { // Don't change state here since either the "tts-end" or "run-end" events will do it. return; } else if (code == "wake-provider-missing" || code == "wake-engine-missing") { From 80e3de94d3d581f91f0197ea2ab49c2c5b6fca45 Mon Sep 17 00:00:00 2001 From: Tomer <57483589+tomer-w@users.noreply.github.com> Date: Fri, 13 Sep 2024 03:56:04 +0300 Subject: [PATCH 0362/1052] =?UTF-8?q?Improve=20manufacturer=20data=20traci?= =?UTF-8?q?ng=20to=20identify=20BLE=20devices=20a=20bit=20easie=E2=80=A6?= =?UTF-8?q?=20(#7332)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ble_presence/binary_sensor.py | 4 ++-- esphome/components/esp32_ble/ble_uuid.cpp | 7 +++++++ esphome/components/esp32_ble/ble_uuid.h | 1 + .../esp32_ble_tracker/esp32_ble_tracker.cpp | 20 ++++++++++--------- .../esp32_ble_tracker/esp32_ble_tracker.h | 6 +++--- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/esphome/components/ble_presence/binary_sensor.py b/esphome/components/ble_presence/binary_sensor.py index d1fdc80289..3a0f1ade98 100644 --- a/esphome/components/ble_presence/binary_sensor.py +++ b/esphome/components/ble_presence/binary_sensor.py @@ -41,7 +41,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t, cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t, - cv.Optional(CONF_IBEACON_UUID): cv.uuid, + cv.Optional(CONF_IBEACON_UUID): esp32_ble_tracker.bt_uuid, cv.Optional(CONF_TIMEOUT, default="5min"): cv.positive_time_period, cv.Optional(CONF_MIN_RSSI): cv.All( cv.decibel, cv.int_range(min=-100, max=-30) @@ -83,7 +83,7 @@ async def to_code(config): cg.add(var.set_service_uuid128(uuid128)) if ibeacon_uuid := config.get(CONF_IBEACON_UUID): - ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid)) + ibeacon_uuid = esp32_ble_tracker.as_reversed_hex_array(ibeacon_uuid) cg.add(var.set_ibeacon_uuid(ibeacon_uuid)) if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None: diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp index 57c2f9df94..07ac719434 100644 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ b/esphome/components/esp32_ble/ble_uuid.cpp @@ -31,6 +31,13 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) { memcpy(ret.uuid_.uuid.uuid128, data, ESP_UUID_LEN_128); return ret; } +ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) { + ESPBTUUID ret; + ret.uuid_.len = ESP_UUID_LEN_128; + for (int i = 0; i < ESP_UUID_LEN_128; i++) + ret.uuid_.uuid.uuid128[ESP_UUID_LEN_128 - 1 - i] = data[i]; + return ret; +} ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { ESPBTUUID ret; if (data.length() == 4) { diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index 790a57c59d..d90db3a599 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -20,6 +20,7 @@ class ESPBTUUID { static ESPBTUUID from_uint32(uint32_t uuid); static ESPBTUUID from_raw(const uint8_t *data); + static ESPBTUUID from_raw_reversed(const uint8_t *data); static ESPBTUUID from_raw(const std::string &data); diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index d154d4e519..74b4b9aa89 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -462,14 +462,16 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str()); } for (auto &data : this->manufacturer_datas_) { - ESP_LOGVV(TAG, " Manufacturer data: %s", format_hex_pretty(data.data).c_str()); - if (this->get_ibeacon().has_value()) { - auto ibeacon = this->get_ibeacon().value(); - ESP_LOGVV(TAG, " iBeacon data:"); - ESP_LOGVV(TAG, " UUID: %s", ibeacon.get_uuid().to_string().c_str()); - ESP_LOGVV(TAG, " Major: %u", ibeacon.get_major()); - ESP_LOGVV(TAG, " Minor: %u", ibeacon.get_minor()); - ESP_LOGVV(TAG, " TXPower: %d", ibeacon.get_signal_power()); + auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(data); + if (ibeacon.has_value()) { + ESP_LOGVV(TAG, " Manufacturer iBeacon:"); + ESP_LOGVV(TAG, " UUID: %s", ibeacon.value().get_uuid().to_string().c_str()); + ESP_LOGVV(TAG, " Major: %u", ibeacon.value().get_major()); + ESP_LOGVV(TAG, " Minor: %u", ibeacon.value().get_minor()); + ESP_LOGVV(TAG, " TXPower: %d", ibeacon.value().get_signal_power()); + } else { + ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", data.uuid.to_string().c_str(), + format_hex_pretty(data.data).c_str()); } } for (auto &data : this->service_datas_) { @@ -478,7 +480,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str()); } - ESP_LOGVV(TAG, "Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str()); + ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str()); #endif } void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 3db7a54f6e..d2bb6a6e6d 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -44,10 +44,10 @@ class ESPBLEiBeacon { ESPBLEiBeacon(const uint8_t *data); static optional from_manufacturer_data(const ServiceData &data); - uint16_t get_major() { return ((this->beacon_data_.major & 0xFF) << 8) | (this->beacon_data_.major >> 8); } - uint16_t get_minor() { return ((this->beacon_data_.minor & 0xFF) << 8) | (this->beacon_data_.minor >> 8); } + uint16_t get_major() { return byteswap(this->beacon_data_.major); } + uint16_t get_minor() { return byteswap(this->beacon_data_.minor); } int8_t get_signal_power() { return this->beacon_data_.signal_power; } - ESPBTUUID get_uuid() { return ESPBTUUID::from_raw(this->beacon_data_.proximity_uuid); } + ESPBTUUID get_uuid() { return ESPBTUUID::from_raw_reversed(this->beacon_data_.proximity_uuid); } protected: struct { From 5d8fb7cdf4a21b649427318cd9d883bb679c8d80 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:01:34 +1200 Subject: [PATCH 0363/1052] Bump version to 2024.9.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index fb745fd506..ce6b9f3d01 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.0b1" +__version__ = "2024.9.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From f652cd3851d17a4434876ab546021c6f698cb47f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 16 Sep 2024 08:42:45 +1000 Subject: [PATCH 0364/1052] [st7701s] Make use of IDF5.x to speed up display operations (#7447) --- esphome/components/st7701s/st7701s.cpp | 32 +++++++++++++++++++------- esphome/components/st7701s/st7701s.h | 2 ++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 7f02fe1774..7248bc044e 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -8,8 +8,22 @@ namespace st7701s { void ST7701S::setup() { esph_log_config(TAG, "Setting up ST7701S"); this->spi_setup(); + this->write_init_sequence_(); +} + +// called after a delay after writing the init sequence +void ST7701S::complete_setup_() { + this->write_command_(SLEEP_OUT); + this->write_command_(DISPLAY_ON); + this->spi_teardown(); // SPI not needed after this + delay(10); + esp_lcd_rgb_panel_config_t config{}; config.flags.fb_in_psram = 1; +#if ESP_IDF_VERSION_MAJOR >= 5 + config.bounce_buffer_size_px = this->width_ * 10; + config.num_fbs = 1; +#endif // ESP_IDF_VERSION_MAJOR config.timings.h_res = this->width_; config.timings.v_res = this->height_; config.timings.hsync_pulse_width = this->hsync_pulse_width_; @@ -21,7 +35,6 @@ void ST7701S::setup() { config.timings.flags.pclk_active_neg = this->pclk_inverted_; config.timings.pclk_hz = this->pclk_frequency_; config.clk_src = LCD_CLK_SRC_PLL160M; - config.sram_trans_align = 64; config.psram_trans_align = 64; size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]); for (size_t i = 0; i != data_pin_count; i++) { @@ -34,15 +47,21 @@ void ST7701S::setup() { config.de_gpio_num = this->de_pin_->get_pin(); config.pclk_gpio_num = this->pclk_pin_->get_pin(); esp_err_t err = esp_lcd_new_rgb_panel(&config, &this->handle_); + ESP_ERROR_CHECK(esp_lcd_panel_reset(this->handle_)); + ESP_ERROR_CHECK(esp_lcd_panel_init(this->handle_)); if (err != ESP_OK) { esph_log_e(TAG, "lcd_new_rgb_panel failed: %s", esp_err_to_name(err)); } - ESP_ERROR_CHECK(esp_lcd_panel_reset(this->handle_)); - ESP_ERROR_CHECK(esp_lcd_panel_init(this->handle_)); - this->write_init_sequence_(); esph_log_config(TAG, "ST7701S setup complete"); } +void ST7701S::loop() { +#if ESP_IDF_VERSION_MAJOR >= 5 + if (this->handle_ != nullptr) + esp_lcd_rgb_panel_restart(this->handle_); +#endif // ESP_IDF_VERSION_MAJOR +} + void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { if (w <= 0 || h <= 0) @@ -160,10 +179,7 @@ void ST7701S::write_init_sequence_() { this->write_data_(val); ESP_LOGD(TAG, "write MADCTL %X", val); this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF); - this->set_timeout(120, [this] { - this->write_command_(SLEEP_OUT); - this->write_command_(DISPLAY_ON); - }); + this->set_timeout(120, [this] { this->complete_setup_(); }); } void ST7701S::dump_config() { diff --git a/esphome/components/st7701s/st7701s.h b/esphome/components/st7701s/st7701s.h index 80e5b81f4a..a1e3c2e54a 100644 --- a/esphome/components/st7701s/st7701s.h +++ b/esphome/components/st7701s/st7701s.h @@ -33,6 +33,8 @@ class ST7701S : public display::Display, public: void update() override { this->do_update_(); } void setup() override; + void complete_setup_(); + void loop() override; void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; From 857d79dc714f123dabafe46eacbbbae0e979a19c Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 15 Sep 2024 18:46:54 -0500 Subject: [PATCH 0365/1052] Add sample_bytes to media player supported format (#7451) --- esphome/components/api/api.proto | 1 + esphome/components/api/api_connection.cpp | 1 + esphome/components/api/api_pb2.cpp | 10 ++++++++++ esphome/components/api/api_pb2.h | 1 + esphome/components/media_player/media_player.h | 1 + 5 files changed, 14 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 1c40e8014e..a966643d30 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1118,6 +1118,7 @@ message MediaPlayerSupportedFormat { uint32 sample_rate = 2; uint32 num_channels = 3; MediaPlayerFormatPurpose purpose = 4; + uint32 sample_bytes = 5; } message ListEntitiesMediaPlayerResponse { option (id) = 63; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 6b7051a704..20e9a45314 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1032,6 +1032,7 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play media_format.sample_rate = supported_format.sample_rate; media_format.num_channels = supported_format.num_channels; media_format.purpose = static_cast(supported_format.purpose); + media_format.sample_bytes = supported_format.sample_bytes; msg.supported_formats.push_back(media_format); } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 2a1552d6fc..955f17612a 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -5149,6 +5149,10 @@ bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt va this->purpose = value.as_enum(); return true; } + case 5: { + this->sample_bytes = value.as_uint32(); + return true; + } default: return false; } @@ -5168,6 +5172,7 @@ void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->sample_rate); buffer.encode_uint32(3, this->num_channels); buffer.encode_enum(4, this->purpose); + buffer.encode_uint32(5, this->sample_bytes); } #ifdef HAS_PROTO_MESSAGE_DUMP void MediaPlayerSupportedFormat::dump_to(std::string &out) const { @@ -5190,6 +5195,11 @@ void MediaPlayerSupportedFormat::dump_to(std::string &out) const { out.append(" purpose: "); out.append(proto_enum_to_string(this->purpose)); out.append("\n"); + + out.append(" sample_bytes: "); + sprintf(buffer, "%" PRIu32, this->sample_bytes); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 6fab1f57e0..1ce6482b09 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1277,6 +1277,7 @@ class MediaPlayerSupportedFormat : public ProtoMessage { uint32_t sample_rate{0}; uint32_t num_channels{0}; enums::MediaPlayerFormatPurpose purpose{}; + uint32_t sample_bytes{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/media_player/media_player.h b/esphome/components/media_player/media_player.h index 26bef55afc..78b3ed6216 100644 --- a/esphome/components/media_player/media_player.h +++ b/esphome/components/media_player/media_player.h @@ -37,6 +37,7 @@ struct MediaPlayerSupportedFormat { uint32_t sample_rate; uint32_t num_channels; MediaPlayerFormatPurpose purpose; + uint32_t sample_bytes; }; class MediaPlayer; From bfde7fd9d796a2e65f92e6d074e432de00a8989b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:32:39 +1200 Subject: [PATCH 0366/1052] [docker] Bump git from 1:2.39.2-1.1 to 1:2.39.5-0+deb12u1 (#7452) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index e255f4e2fc..85823687c2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -33,7 +33,7 @@ RUN \ python3-venv=3.11.2-1+b1 \ python3-wheel=0.38.4-2 \ iputils-ping=3:20221126-1 \ - git=1:2.39.2-1.1 \ + git=1:2.39.5-0+deb12u1 \ curl=7.88.1-10+deb12u7 \ openssh-client=1:9.2p1-2+deb12u3 \ python3-cffi=1.15.1-5 \ From e7fe9b374fb8ac9c1e7c5ab5869f8114509fbe6a Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 15 Sep 2024 18:46:54 -0500 Subject: [PATCH 0367/1052] Add sample_bytes to media player supported format (#7451) --- esphome/components/api/api.proto | 1 + esphome/components/api/api_connection.cpp | 1 + esphome/components/api/api_pb2.cpp | 10 ++++++++++ esphome/components/api/api_pb2.h | 1 + esphome/components/media_player/media_player.h | 1 + 5 files changed, 14 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 1c40e8014e..a966643d30 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1118,6 +1118,7 @@ message MediaPlayerSupportedFormat { uint32 sample_rate = 2; uint32 num_channels = 3; MediaPlayerFormatPurpose purpose = 4; + uint32 sample_bytes = 5; } message ListEntitiesMediaPlayerResponse { option (id) = 63; diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 6b7051a704..20e9a45314 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1032,6 +1032,7 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play media_format.sample_rate = supported_format.sample_rate; media_format.num_channels = supported_format.num_channels; media_format.purpose = static_cast(supported_format.purpose); + media_format.sample_bytes = supported_format.sample_bytes; msg.supported_formats.push_back(media_format); } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 2a1552d6fc..955f17612a 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -5149,6 +5149,10 @@ bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt va this->purpose = value.as_enum(); return true; } + case 5: { + this->sample_bytes = value.as_uint32(); + return true; + } default: return false; } @@ -5168,6 +5172,7 @@ void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(2, this->sample_rate); buffer.encode_uint32(3, this->num_channels); buffer.encode_enum(4, this->purpose); + buffer.encode_uint32(5, this->sample_bytes); } #ifdef HAS_PROTO_MESSAGE_DUMP void MediaPlayerSupportedFormat::dump_to(std::string &out) const { @@ -5190,6 +5195,11 @@ void MediaPlayerSupportedFormat::dump_to(std::string &out) const { out.append(" purpose: "); out.append(proto_enum_to_string(this->purpose)); out.append("\n"); + + out.append(" sample_bytes: "); + sprintf(buffer, "%" PRIu32, this->sample_bytes); + out.append(buffer); + out.append("\n"); out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 6fab1f57e0..1ce6482b09 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1277,6 +1277,7 @@ class MediaPlayerSupportedFormat : public ProtoMessage { uint32_t sample_rate{0}; uint32_t num_channels{0}; enums::MediaPlayerFormatPurpose purpose{}; + uint32_t sample_bytes{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/media_player/media_player.h b/esphome/components/media_player/media_player.h index 26bef55afc..78b3ed6216 100644 --- a/esphome/components/media_player/media_player.h +++ b/esphome/components/media_player/media_player.h @@ -37,6 +37,7 @@ struct MediaPlayerSupportedFormat { uint32_t sample_rate; uint32_t num_channels; MediaPlayerFormatPurpose purpose; + uint32_t sample_bytes; }; class MediaPlayer; From 6483ceb6eb0b4aeee7f2fe1dc9c2cf8d5f3d448d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:32:39 +1200 Subject: [PATCH 0368/1052] [docker] Bump git from 1:2.39.2-1.1 to 1:2.39.5-0+deb12u1 (#7452) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index e255f4e2fc..85823687c2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -33,7 +33,7 @@ RUN \ python3-venv=3.11.2-1+b1 \ python3-wheel=0.38.4-2 \ iputils-ping=3:20221126-1 \ - git=1:2.39.2-1.1 \ + git=1:2.39.5-0+deb12u1 \ curl=7.88.1-10+deb12u7 \ openssh-client=1:9.2p1-2+deb12u3 \ python3-cffi=1.15.1-5 \ From a63b9a9e0c58b27279ead5d3238273fce45cd6ba Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:17:06 +1200 Subject: [PATCH 0369/1052] Bump version to 2024.9.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ce6b9f3d01..24deba1f15 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.0b2" +__version__ = "2024.9.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 3835ad8c1f793f19d21cf2a158744ae6228ae0b4 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 15 Sep 2024 20:40:45 -0500 Subject: [PATCH 0370/1052] Add voice assistant configuration messages (#7445) --- esphome/components/api/api.proto | 30 +++++ esphome/components/api/api_pb2.cpp | 143 +++++++++++++++++++++ esphome/components/api/api_pb2.h | 48 +++++++ esphome/components/api/api_pb2_service.cpp | 35 +++++ esphome/components/api/api_pb2_service.h | 9 ++ 5 files changed, 265 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index a966643d30..92fb57b711 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1571,6 +1571,36 @@ message VoiceAssistantAnnounceFinished { bool success = 1; } +message VoiceAssistantWakeWord { + uint32 id = 1; + string wake_word = 2; + repeated string trained_languages = 3; +} + +message VoiceAssistantConfigurationRequest { + option (id) = 121; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; +} + +message VoiceAssistantConfigurationResponse { + option (id) = 122; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + repeated VoiceAssistantWakeWord available_wake_words = 1; + repeated uint32 active_wake_words = 2; + uint32 max_active_wake_words = 3; +} + +message VoiceAssistantSetConfiguration { + option (id) = 123; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + repeated uint32 active_wake_words = 1; +} + // ==================== ALARM CONTROL PANEL ==================== enum AlarmControlPanelState { ALARM_STATE_DISARMED = 0; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 955f17612a..791de29511 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -7124,6 +7124,149 @@ void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { out.append("}"); } #endif +bool VoiceAssistantWakeWord::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->id = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->wake_word = value.as_string(); + return true; + } + case 3: { + this->trained_languages.push_back(value.as_string()); + return true; + } + default: + return false; + } +} +void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint32(1, this->id); + buffer.encode_string(2, this->wake_word); + for (auto &it : this->trained_languages) { + buffer.encode_string(3, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantWakeWord::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantWakeWord {\n"); + out.append(" id: "); + sprintf(buffer, "%" PRIu32, this->id); + out.append(buffer); + out.append("\n"); + + out.append(" wake_word: "); + out.append("'").append(this->wake_word).append("'"); + out.append("\n"); + + for (const auto &it : this->trained_languages) { + out.append(" trained_languages: "); + out.append("'").append(it).append("'"); + out.append("\n"); + } + out.append("}"); +} +#endif +void VoiceAssistantConfigurationRequest::encode(ProtoWriteBuffer buffer) const {} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { + out.append("VoiceAssistantConfigurationRequest {}"); +} +#endif +bool VoiceAssistantConfigurationResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->active_wake_words.push_back(value.as_uint32()); + return true; + } + case 3: { + this->max_active_wake_words = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->available_wake_words.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const { + for (auto &it : this->available_wake_words) { + buffer.encode_message(1, it, true); + } + for (auto &it : this->active_wake_words) { + buffer.encode_uint32(2, it, true); + } + buffer.encode_uint32(3, this->max_active_wake_words); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantConfigurationResponse {\n"); + for (const auto &it : this->available_wake_words) { + out.append(" available_wake_words: "); + it.dump_to(out); + out.append("\n"); + } + + for (const auto &it : this->active_wake_words) { + out.append(" active_wake_words: "); + sprintf(buffer, "%" PRIu32, it); + out.append(buffer); + out.append("\n"); + } + + out.append(" max_active_wake_words: "); + sprintf(buffer, "%" PRIu32, this->max_active_wake_words); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool VoiceAssistantSetConfiguration::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->active_wake_words.push_back(value.as_uint32()); + return true; + } + default: + return false; + } +} +void VoiceAssistantSetConfiguration::encode(ProtoWriteBuffer buffer) const { + for (auto &it : this->active_wake_words) { + buffer.encode_uint32(1, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantSetConfiguration::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantSetConfiguration {\n"); + for (const auto &it : this->active_wake_words) { + out.append(" active_wake_words: "); + sprintf(buffer, "%" PRIu32, it); + out.append(buffer); + out.append("\n"); + } + out.append("}"); +} +#endif bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 6: { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 1ce6482b09..8631884f94 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1849,6 +1849,54 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class VoiceAssistantWakeWord : public ProtoMessage { + public: + uint32_t id{0}; + std::string wake_word{}; + std::vector trained_languages{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class VoiceAssistantConfigurationRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: +}; +class VoiceAssistantConfigurationResponse : public ProtoMessage { + public: + std::vector available_wake_words{}; + std::vector active_wake_words{}; + uint32_t max_active_wake_words{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class VoiceAssistantSetConfiguration : public ProtoMessage { + public: + std::vector active_wake_words{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { public: std::string object_id{}; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index faa977389a..454f20d50a 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -496,6 +496,19 @@ bool APIServerConnectionBase::send_voice_assistant_announce_finished(const Voice 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) { @@ -1156,6 +1169,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_voice_assistant_announce_request: %s", msg.dump().c_str()); #endif this->on_voice_assistant_announce_request(msg); +#endif + break; + } + case 121: { +#ifdef USE_VOICE_ASSISTANT + VoiceAssistantConfigurationRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_voice_assistant_configuration_request: %s", msg.dump().c_str()); +#endif + this->on_voice_assistant_configuration_request(msg); +#endif + break; + } + case 123: { +#ifdef USE_VOICE_ASSISTANT + VoiceAssistantSetConfiguration msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str()); +#endif + this->on_voice_assistant_set_configuration(msg); #endif break; } diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index f3803ad628..e69954f7ab 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -253,6 +253,15 @@ class APIServerConnectionBase : public ProtoService { #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 From 73e469ae528cb4a522f8125837416f97a91e50de Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:43:45 +1200 Subject: [PATCH 0371/1052] [modbus_controller] Fix linting and formatting issues (#7441) --- .../components/modbus_controller/__init__.py | 12 ++++++---- .../binary_sensor/__init__.py | 14 +++++------ .../modbus_controller/number/__init__.py | 11 ++++----- .../modbus_controller/output/__init__.py | 24 ++++++++----------- .../modbus_controller/select/__init__.py | 2 +- .../modbus_controller/sensor/__init__.py | 16 ++++++------- .../modbus_controller/switch/__init__.py | 15 ++++++------ .../modbus_controller/text_sensor/__init__.py | 19 +++++++-------- 8 files changed, 54 insertions(+), 59 deletions(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 6917807b07..488baa245a 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -1,27 +1,29 @@ import binascii -import esphome.codegen as cg -import esphome.config_validation as cv + from esphome import automation +import esphome.codegen as cg from esphome.components import modbus +import esphome.config_validation as cv from esphome.const import ( CONF_ADDRESS, CONF_ID, - CONF_NAME, CONF_LAMBDA, + CONF_NAME, CONF_OFFSET, CONF_TRIGGER_ID, ) from esphome.cpp_helpers import logging + from .const import ( CONF_ALLOW_DUPLICATE_COMMANDS, CONF_BITMASK, CONF_BYTE_OFFSET, CONF_COMMAND_THROTTLE, - CONF_OFFLINE_SKIP_UPDATES, CONF_CUSTOM_COMMAND, CONF_FORCE_NEW_RANGE, - CONF_MODBUS_CONTROLLER_ID, CONF_MAX_CMD_RETRIES, + CONF_MODBUS_CONTROLLER_ID, + CONF_OFFLINE_SKIP_UPDATES, CONF_ON_COMMAND_SENT, CONF_REGISTER_COUNT, CONF_REGISTER_TYPE, diff --git a/esphome/components/modbus_controller/binary_sensor/__init__.py b/esphome/components/modbus_controller/binary_sensor/__init__.py index 5315167479..2ae008f630 100644 --- a/esphome/components/modbus_controller/binary_sensor/__init__.py +++ b/esphome/components/modbus_controller/binary_sensor/__init__.py @@ -1,16 +1,16 @@ +import esphome.codegen as cg from esphome.components import binary_sensor import esphome.config_validation as cv -import esphome.codegen as cg - from esphome.const import CONF_ADDRESS, CONF_ID + from .. import ( - add_modbus_base_properties, - modbus_controller_ns, - modbus_calc_properties, - validate_modbus_register, + MODBUS_REGISTER_TYPE, ModbusItemBaseSchema, SensorItem, - MODBUS_REGISTER_TYPE, + add_modbus_base_properties, + modbus_calc_properties, + modbus_controller_ns, + validate_modbus_register, ) from ..const import ( CONF_BITMASK, diff --git a/esphome/components/modbus_controller/number/__init__.py b/esphome/components/modbus_controller/number/__init__.py index fe99b28a00..b5efd7abf0 100644 --- a/esphome/components/modbus_controller/number/__init__.py +++ b/esphome/components/modbus_controller/number/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import number +import esphome.config_validation as cv from esphome.const import ( CONF_ADDRESS, CONF_ID, @@ -12,14 +12,13 @@ from esphome.const import ( from .. import ( MODBUS_WRITE_REGISTER_TYPE, - add_modbus_base_properties, - modbus_controller_ns, - modbus_calc_properties, + SENSOR_VALUE_TYPE, ModbusItemBaseSchema, SensorItem, - SENSOR_VALUE_TYPE, + add_modbus_base_properties, + modbus_calc_properties, + modbus_controller_ns, ) - from ..const import ( CONF_BITMASK, CONF_CUSTOM_COMMAND, diff --git a/esphome/components/modbus_controller/output/__init__.py b/esphome/components/modbus_controller/output/__init__.py index 1bf989ce8b..1800a90d57 100644 --- a/esphome/components/modbus_controller/output/__init__.py +++ b/esphome/components/modbus_controller/output/__init__.py @@ -1,20 +1,15 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import output -from esphome.const import ( - CONF_ADDRESS, - CONF_ID, - CONF_MULTIPLY, -) +import esphome.config_validation as cv +from esphome.const import CONF_ADDRESS, CONF_ID, CONF_MULTIPLY from .. import ( - modbus_controller_ns, - modbus_calc_properties, + SENSOR_VALUE_TYPE, ModbusItemBaseSchema, SensorItem, - SENSOR_VALUE_TYPE, + modbus_calc_properties, + modbus_controller_ns, ) - from ..const import ( CONF_MODBUS_CONTROLLER_ID, CONF_REGISTER_TYPE, @@ -65,6 +60,7 @@ CONFIG_SCHEMA = cv.typed_schema( async def to_code(config): byte_offset, reg_count = modbus_calc_properties(config) # Binary Output + write_template = None if config[CONF_REGISTER_TYPE] == "coil": var = cg.new_Pvariable( config[CONF_ID], @@ -72,7 +68,7 @@ async def to_code(config): byte_offset, ) if CONF_WRITE_LAMBDA in config: - template_ = await cg.process_lambda( + write_template = await cg.process_lambda( config[CONF_WRITE_LAMBDA], [ (ModbusBinaryOutput.operator("ptr"), "item"), @@ -92,7 +88,7 @@ async def to_code(config): ) cg.add(var.set_write_multiply(config[CONF_MULTIPLY])) if CONF_WRITE_LAMBDA in config: - template_ = await cg.process_lambda( + write_template = await cg.process_lambda( config[CONF_WRITE_LAMBDA], [ (ModbusFloatOutput.operator("ptr"), "item"), @@ -105,5 +101,5 @@ async def to_code(config): parent = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID]) cg.add(var.set_use_write_mutiple(config[CONF_USE_WRITE_MULTIPLE])) cg.add(var.set_parent(parent)) - if CONF_WRITE_LAMBDA in config: - cg.add(var.set_write_template(template_)) + if write_template: + cg.add(var.set_write_template(write_template)) diff --git a/esphome/components/modbus_controller/select/__init__.py b/esphome/components/modbus_controller/select/__init__.py index 5692fea3e3..c94532da51 100644 --- a/esphome/components/modbus_controller/select/__init__.py +++ b/esphome/components/modbus_controller/select/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import select +import esphome.config_validation as cv from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_OPTIMISTIC from .. import ( diff --git a/esphome/components/modbus_controller/sensor/__init__.py b/esphome/components/modbus_controller/sensor/__init__.py index 0e4588cfef..d8fce54ece 100644 --- a/esphome/components/modbus_controller/sensor/__init__.py +++ b/esphome/components/modbus_controller/sensor/__init__.py @@ -1,17 +1,17 @@ +import esphome.codegen as cg from esphome.components import sensor import esphome.config_validation as cv -import esphome.codegen as cg +from esphome.const import CONF_ADDRESS, CONF_ID -from esphome.const import CONF_ID, CONF_ADDRESS from .. import ( - add_modbus_base_properties, - modbus_controller_ns, - modbus_calc_properties, - validate_modbus_register, - ModbusItemBaseSchema, - SensorItem, MODBUS_REGISTER_TYPE, SENSOR_VALUE_TYPE, + ModbusItemBaseSchema, + SensorItem, + add_modbus_base_properties, + modbus_calc_properties, + modbus_controller_ns, + validate_modbus_register, ) from ..const import ( CONF_BITMASK, diff --git a/esphome/components/modbus_controller/switch/__init__.py b/esphome/components/modbus_controller/switch/__init__.py index 9490325968..258d87fd25 100644 --- a/esphome/components/modbus_controller/switch/__init__.py +++ b/esphome/components/modbus_controller/switch/__init__.py @@ -1,17 +1,16 @@ +import esphome.codegen as cg from esphome.components import switch import esphome.config_validation as cv -import esphome.codegen as cg +from esphome.const import CONF_ADDRESS, CONF_ID - -from esphome.const import CONF_ID, CONF_ADDRESS from .. import ( - add_modbus_base_properties, - modbus_controller_ns, - modbus_calc_properties, - validate_modbus_register, + MODBUS_REGISTER_TYPE, ModbusItemBaseSchema, SensorItem, - MODBUS_REGISTER_TYPE, + add_modbus_base_properties, + modbus_calc_properties, + modbus_controller_ns, + validate_modbus_register, ) from ..const import ( CONF_BITMASK, diff --git a/esphome/components/modbus_controller/text_sensor/__init__.py b/esphome/components/modbus_controller/text_sensor/__init__.py index 81d6453c6f..35cae645e1 100644 --- a/esphome/components/modbus_controller/text_sensor/__init__.py +++ b/esphome/components/modbus_controller/text_sensor/__init__.py @@ -1,26 +1,25 @@ +import esphome.codegen as cg from esphome.components import text_sensor import esphome.config_validation as cv -import esphome.codegen as cg - - from esphome.const import CONF_ADDRESS, CONF_ID + from .. import ( - add_modbus_base_properties, - modbus_controller_ns, - modbus_calc_properties, - validate_modbus_register, + MODBUS_REGISTER_TYPE, ModbusItemBaseSchema, SensorItem, - MODBUS_REGISTER_TYPE, + add_modbus_base_properties, + modbus_calc_properties, + modbus_controller_ns, + validate_modbus_register, ) from ..const import ( CONF_FORCE_NEW_RANGE, CONF_MODBUS_CONTROLLER_ID, + CONF_RAW_ENCODE, CONF_REGISTER_COUNT, + CONF_REGISTER_TYPE, CONF_RESPONSE_SIZE, CONF_SKIP_UPDATES, - CONF_RAW_ENCODE, - CONF_REGISTER_TYPE, ) DEPENDENCIES = ["modbus_controller"] From 435789a96020435b6d0c345bebb6ef19a98fd818 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:21:42 +1200 Subject: [PATCH 0372/1052] Bump pylint from 3.1.0 to 3.2.7 (#7438) --- requirements_test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_test.txt b/requirements_test.txt index 94abe1cd76..5d94f7f640 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,4 +1,4 @@ -pylint==3.1.0 +pylint==3.2.7 flake8==7.0.0 # also change in .pre-commit-config.yaml when updating black==24.4.2 # also change in .pre-commit-config.yaml when updating pyupgrade==3.15.2 # also change in .pre-commit-config.yaml when updating From 857a3dcf72d74fa53fba2f6c63f2ceaaec3e5e59 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:03:51 +1200 Subject: [PATCH 0373/1052] Dont replace project name spaces with underlines (#7455) --- esphome/core/config.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/core/config.py b/esphome/core/config.py index 739a8a1aea..f4253bee87 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -100,9 +100,6 @@ def valid_include(value): def valid_project_name(value: str): if value.count(".") != 1: raise cv.Invalid("project name needs to have a namespace") - - value = value.replace(" ", "_") - return value From cb86749545ef26b543118aa2b349376add89829f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:47:16 +1200 Subject: [PATCH 0374/1052] Bump peter-evans/create-pull-request from 7.0.2 to 7.0.3 (#7457) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 1418867240..eeb8386e74 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -36,7 +36,7 @@ jobs: python ./script/sync-device_class.py - name: Commit changes - uses: peter-evans/create-pull-request@v7.0.2 + uses: peter-evans/create-pull-request@v7.0.3 with: commit-message: "Synchronise Device Classes from Home Assistant" committer: esphomebot From 5a3e1d57921a839a5f383350e45cdd3304efa311 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Tue, 17 Sep 2024 18:38:39 -0500 Subject: [PATCH 0375/1052] Add voice assistant methods for configuration (#7459) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/api/api.proto | 8 ++-- esphome/components/api/api_connection.cpp | 33 +++++++++++++++ esphome/components/api/api_connection.h | 3 ++ esphome/components/api/api_pb2.cpp | 41 ++++++++----------- esphome/components/api/api_pb2.h | 9 ++-- esphome/components/api/api_pb2_service.cpp | 29 +++++++++++++ esphome/components/api/api_pb2_service.h | 13 ++++++ .../voice_assistant/voice_assistant.h | 16 ++++++++ 8 files changed, 119 insertions(+), 33 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 92fb57b711..684540ffa6 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -62,6 +62,8 @@ service APIConnection { rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {} rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {} + rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {} + rpc voice_assistant_set_configuration(VoiceAssistantSetConfiguration) returns (void) {} rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {} } @@ -1572,7 +1574,7 @@ message VoiceAssistantAnnounceFinished { } message VoiceAssistantWakeWord { - uint32 id = 1; + string id = 1; string wake_word = 2; repeated string trained_languages = 3; } @@ -1589,7 +1591,7 @@ message VoiceAssistantConfigurationResponse { option (ifdef) = "USE_VOICE_ASSISTANT"; repeated VoiceAssistantWakeWord available_wake_words = 1; - repeated uint32 active_wake_words = 2; + repeated string active_wake_words = 2; uint32 max_active_wake_words = 3; } @@ -1598,7 +1600,7 @@ message VoiceAssistantSetConfiguration { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_VOICE_ASSISTANT"; - repeated uint32 active_wake_words = 1; + repeated string active_wake_words = 1; } // ==================== ALARM CONTROL PANEL ==================== diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 20e9a45314..7ea52e9a9e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1224,6 +1224,39 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno } } +VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration( + const VoiceAssistantConfigurationRequest &msg) { + VoiceAssistantConfigurationResponse resp; + if (voice_assistant::global_voice_assistant != nullptr) { + if (voice_assistant::global_voice_assistant->get_api_connection() != this) { + return resp; + } + + auto &config = voice_assistant::global_voice_assistant->get_configuration(); + for (auto &wake_word : config.available_wake_words) { + VoiceAssistantWakeWord resp_wake_word; + resp_wake_word.id = wake_word.id; + resp_wake_word.wake_word = wake_word.wake_word; + for (const auto &lang : wake_word.trained_languages) { + resp_wake_word.trained_languages.push_back(lang); + } + resp.available_wake_words.push_back(std::move(resp_wake_word)); + } + resp.max_active_wake_words = config.max_active_wake_words; + } + return resp; +} + +void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) { + if (voice_assistant::global_voice_assistant != nullptr) { + if (voice_assistant::global_voice_assistant->get_api_connection() != this) { + return; + } + + voice_assistant::global_voice_assistant->on_set_configuration(msg.active_wake_words); + } +} + #endif #ifdef USE_ALARM_CONTROL_PANEL diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index e8d66a5e07..f176cf7c56 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -152,6 +152,9 @@ class APIConnection : public APIServerConnection { void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override; void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override; void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override; + VoiceAssistantConfigurationResponse voice_assistant_get_configuration( + const VoiceAssistantConfigurationRequest &msg) override; + void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override; #endif #ifdef USE_ALARM_CONTROL_PANEL diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 791de29511..8df152881c 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -7124,18 +7124,12 @@ void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { out.append("}"); } #endif -bool VoiceAssistantWakeWord::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->id = value.as_uint32(); - return true; - } - default: - return false; - } -} bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { + case 1: { + this->id = value.as_string(); + return true; + } case 2: { this->wake_word = value.as_string(); return true; @@ -7149,7 +7143,7 @@ bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimit } } void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, this->id); + buffer.encode_string(1, this->id); buffer.encode_string(2, this->wake_word); for (auto &it : this->trained_languages) { buffer.encode_string(3, it, true); @@ -7160,8 +7154,7 @@ void VoiceAssistantWakeWord::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("VoiceAssistantWakeWord {\n"); out.append(" id: "); - sprintf(buffer, "%" PRIu32, this->id); - out.append(buffer); + out.append("'").append(this->id).append("'"); out.append("\n"); out.append(" wake_word: "); @@ -7184,10 +7177,6 @@ void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { #endif bool VoiceAssistantConfigurationResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { - case 2: { - this->active_wake_words.push_back(value.as_uint32()); - return true; - } case 3: { this->max_active_wake_words = value.as_uint32(); return true; @@ -7202,6 +7191,10 @@ bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, Proto this->available_wake_words.push_back(value.as_message()); return true; } + case 2: { + this->active_wake_words.push_back(value.as_string()); + return true; + } default: return false; } @@ -7211,7 +7204,7 @@ void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const buffer.encode_message(1, it, true); } for (auto &it : this->active_wake_words) { - buffer.encode_uint32(2, it, true); + buffer.encode_string(2, it, true); } buffer.encode_uint32(3, this->max_active_wake_words); } @@ -7227,8 +7220,7 @@ void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const { for (const auto &it : this->active_wake_words) { out.append(" active_wake_words: "); - sprintf(buffer, "%" PRIu32, it); - out.append(buffer); + out.append("'").append(it).append("'"); out.append("\n"); } @@ -7239,10 +7231,10 @@ void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const { out.append("}"); } #endif -bool VoiceAssistantSetConfiguration::decode_varint(uint32_t field_id, ProtoVarInt value) { +bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { - this->active_wake_words.push_back(value.as_uint32()); + this->active_wake_words.push_back(value.as_string()); return true; } default: @@ -7251,7 +7243,7 @@ bool VoiceAssistantSetConfiguration::decode_varint(uint32_t field_id, ProtoVarIn } void VoiceAssistantSetConfiguration::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->active_wake_words) { - buffer.encode_uint32(1, it, true); + buffer.encode_string(1, it, true); } } #ifdef HAS_PROTO_MESSAGE_DUMP @@ -7260,8 +7252,7 @@ void VoiceAssistantSetConfiguration::dump_to(std::string &out) const { out.append("VoiceAssistantSetConfiguration {\n"); for (const auto &it : this->active_wake_words) { out.append(" active_wake_words: "); - sprintf(buffer, "%" PRIu32, it); - out.append(buffer); + out.append("'").append(it).append("'"); out.append("\n"); } out.append("}"); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 8631884f94..063c217bf7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1851,7 +1851,7 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage { }; class VoiceAssistantWakeWord : public ProtoMessage { public: - uint32_t id{0}; + std::string id{}; std::string wake_word{}; std::vector trained_languages{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1861,7 +1861,6 @@ class VoiceAssistantWakeWord : public ProtoMessage { protected: bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class VoiceAssistantConfigurationRequest : public ProtoMessage { public: @@ -1875,7 +1874,7 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage { class VoiceAssistantConfigurationResponse : public ProtoMessage { public: std::vector available_wake_words{}; - std::vector active_wake_words{}; + std::vector active_wake_words{}; uint32_t max_active_wake_words{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1888,14 +1887,14 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage { }; class VoiceAssistantSetConfiguration : public ProtoMessage { public: - std::vector active_wake_words{}; + std::vector active_wake_words{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { public: diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 454f20d50a..6e11d7169d 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -1681,6 +1681,35 @@ void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVo this->subscribe_voice_assistant(msg); } #endif +#ifdef USE_VOICE_ASSISTANT +void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg); + if (!this->send_voice_assistant_configuration_response(ret)) { + this->on_fatal_error(); + } +} +#endif +#ifdef USE_VOICE_ASSISTANT +void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->voice_assistant_set_configuration(msg); +} +#endif #ifdef USE_ALARM_CONTROL_PANEL void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) { if (!this->is_connection_setup()) { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index e69954f7ab..51b94bf530 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -434,6 +434,13 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_VOICE_ASSISTANT virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0; #endif +#ifdef USE_VOICE_ASSISTANT + virtual VoiceAssistantConfigurationResponse voice_assistant_get_configuration( + const VoiceAssistantConfigurationRequest &msg) = 0; +#endif +#ifdef USE_VOICE_ASSISTANT + virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0; +#endif #ifdef USE_ALARM_CONTROL_PANEL virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0; #endif @@ -535,6 +542,12 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_VOICE_ASSISTANT void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override; #endif +#ifdef USE_VOICE_ASSISTANT + void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override; +#endif +#ifdef USE_VOICE_ASSISTANT + void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override; +#endif #ifdef USE_ALARM_CONTROL_PANEL void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override; #endif diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index b0a172332f..56ada0e75a 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -77,6 +77,18 @@ struct Timer { } }; +struct WakeWord { + std::string id; + std::string wake_word; + std::vector trained_languages; +}; + +struct Configuration { + std::vector available_wake_words; + std::vector active_wake_words; + uint32_t max_active_wake_words; +}; + class VoiceAssistant : public Component { public: void setup() override; @@ -133,6 +145,8 @@ class VoiceAssistant : public Component { void on_audio(const api::VoiceAssistantAudio &msg); void on_timer_event(const api::VoiceAssistantTimerEventResponse &msg); void on_announce(const api::VoiceAssistantAnnounceRequest &msg); + void on_set_configuration(const std::vector &active_wake_words){}; + const Configuration &get_configuration() { return this->config_; }; bool is_running() const { return this->state_ != State::IDLE; } void set_continuous(bool continuous) { this->continuous_ = continuous; } @@ -279,6 +293,8 @@ class VoiceAssistant : public Component { AudioMode audio_mode_{AUDIO_MODE_UDP}; bool udp_socket_running_{false}; bool start_udp_socket_(); + + Configuration config_{}; }; template class StartAction : public Action, public Parented { From f87d9be60dc74208ce18cdcb1dd9cf87c43fe7aa Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 15 Sep 2024 20:40:45 -0500 Subject: [PATCH 0376/1052] Add voice assistant configuration messages (#7445) --- esphome/components/api/api.proto | 30 +++++ esphome/components/api/api_pb2.cpp | 143 +++++++++++++++++++++ esphome/components/api/api_pb2.h | 48 +++++++ esphome/components/api/api_pb2_service.cpp | 35 +++++ esphome/components/api/api_pb2_service.h | 9 ++ 5 files changed, 265 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index a966643d30..92fb57b711 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1571,6 +1571,36 @@ message VoiceAssistantAnnounceFinished { bool success = 1; } +message VoiceAssistantWakeWord { + uint32 id = 1; + string wake_word = 2; + repeated string trained_languages = 3; +} + +message VoiceAssistantConfigurationRequest { + option (id) = 121; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; +} + +message VoiceAssistantConfigurationResponse { + option (id) = 122; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + repeated VoiceAssistantWakeWord available_wake_words = 1; + repeated uint32 active_wake_words = 2; + uint32 max_active_wake_words = 3; +} + +message VoiceAssistantSetConfiguration { + option (id) = 123; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_VOICE_ASSISTANT"; + + repeated uint32 active_wake_words = 1; +} + // ==================== ALARM CONTROL PANEL ==================== enum AlarmControlPanelState { ALARM_STATE_DISARMED = 0; diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 955f17612a..791de29511 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -7124,6 +7124,149 @@ void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { out.append("}"); } #endif +bool VoiceAssistantWakeWord::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->id = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 2: { + this->wake_word = value.as_string(); + return true; + } + case 3: { + this->trained_languages.push_back(value.as_string()); + return true; + } + default: + return false; + } +} +void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint32(1, this->id); + buffer.encode_string(2, this->wake_word); + for (auto &it : this->trained_languages) { + buffer.encode_string(3, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantWakeWord::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantWakeWord {\n"); + out.append(" id: "); + sprintf(buffer, "%" PRIu32, this->id); + out.append(buffer); + out.append("\n"); + + out.append(" wake_word: "); + out.append("'").append(this->wake_word).append("'"); + out.append("\n"); + + for (const auto &it : this->trained_languages) { + out.append(" trained_languages: "); + out.append("'").append(it).append("'"); + out.append("\n"); + } + out.append("}"); +} +#endif +void VoiceAssistantConfigurationRequest::encode(ProtoWriteBuffer buffer) const {} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { + out.append("VoiceAssistantConfigurationRequest {}"); +} +#endif +bool VoiceAssistantConfigurationResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 2: { + this->active_wake_words.push_back(value.as_uint32()); + return true; + } + case 3: { + this->max_active_wake_words = value.as_uint32(); + return true; + } + default: + return false; + } +} +bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->available_wake_words.push_back(value.as_message()); + return true; + } + default: + return false; + } +} +void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const { + for (auto &it : this->available_wake_words) { + buffer.encode_message(1, it, true); + } + for (auto &it : this->active_wake_words) { + buffer.encode_uint32(2, it, true); + } + buffer.encode_uint32(3, this->max_active_wake_words); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantConfigurationResponse {\n"); + for (const auto &it : this->available_wake_words) { + out.append(" available_wake_words: "); + it.dump_to(out); + out.append("\n"); + } + + for (const auto &it : this->active_wake_words) { + out.append(" active_wake_words: "); + sprintf(buffer, "%" PRIu32, it); + out.append(buffer); + out.append("\n"); + } + + out.append(" max_active_wake_words: "); + sprintf(buffer, "%" PRIu32, this->max_active_wake_words); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif +bool VoiceAssistantSetConfiguration::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->active_wake_words.push_back(value.as_uint32()); + return true; + } + default: + return false; + } +} +void VoiceAssistantSetConfiguration::encode(ProtoWriteBuffer buffer) const { + for (auto &it : this->active_wake_words) { + buffer.encode_uint32(1, it, true); + } +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void VoiceAssistantSetConfiguration::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("VoiceAssistantSetConfiguration {\n"); + for (const auto &it : this->active_wake_words) { + out.append(" active_wake_words: "); + sprintf(buffer, "%" PRIu32, it); + out.append(buffer); + out.append("\n"); + } + out.append("}"); +} +#endif bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { case 6: { diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 1ce6482b09..8631884f94 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1849,6 +1849,54 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class VoiceAssistantWakeWord : public ProtoMessage { + public: + uint32_t id{0}; + std::string wake_word{}; + std::vector trained_languages{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class VoiceAssistantConfigurationRequest : public ProtoMessage { + public: + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: +}; +class VoiceAssistantConfigurationResponse : public ProtoMessage { + public: + std::vector available_wake_words{}; + std::vector active_wake_words{}; + uint32_t max_active_wake_words{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class VoiceAssistantSetConfiguration : public ProtoMessage { + public: + std::vector active_wake_words{}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { public: std::string object_id{}; diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index faa977389a..454f20d50a 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -496,6 +496,19 @@ bool APIServerConnectionBase::send_voice_assistant_announce_finished(const Voice 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) { @@ -1156,6 +1169,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, ESP_LOGVV(TAG, "on_voice_assistant_announce_request: %s", msg.dump().c_str()); #endif this->on_voice_assistant_announce_request(msg); +#endif + break; + } + case 121: { +#ifdef USE_VOICE_ASSISTANT + VoiceAssistantConfigurationRequest msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_voice_assistant_configuration_request: %s", msg.dump().c_str()); +#endif + this->on_voice_assistant_configuration_request(msg); +#endif + break; + } + case 123: { +#ifdef USE_VOICE_ASSISTANT + VoiceAssistantSetConfiguration msg; + msg.decode(msg_data, msg_size); +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str()); +#endif + this->on_voice_assistant_set_configuration(msg); #endif break; } diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index f3803ad628..e69954f7ab 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -253,6 +253,15 @@ class APIServerConnectionBase : public ProtoService { #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 From 749f6643308109cae5387bf7e5114fa16a0d78b0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:03:51 +1200 Subject: [PATCH 0377/1052] Dont replace project name spaces with underlines (#7455) --- esphome/core/config.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/core/config.py b/esphome/core/config.py index 739a8a1aea..f4253bee87 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -100,9 +100,6 @@ def valid_include(value): def valid_project_name(value: str): if value.count(".") != 1: raise cv.Invalid("project name needs to have a namespace") - - value = value.replace(" ", "_") - return value From 571c0eb82703e29de70f5c82ed018b4dde2d9dc3 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Tue, 17 Sep 2024 18:38:39 -0500 Subject: [PATCH 0378/1052] Add voice assistant methods for configuration (#7459) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/api/api.proto | 8 ++-- esphome/components/api/api_connection.cpp | 33 +++++++++++++++ esphome/components/api/api_connection.h | 3 ++ esphome/components/api/api_pb2.cpp | 41 ++++++++----------- esphome/components/api/api_pb2.h | 9 ++-- esphome/components/api/api_pb2_service.cpp | 29 +++++++++++++ esphome/components/api/api_pb2_service.h | 13 ++++++ .../voice_assistant/voice_assistant.h | 16 ++++++++ 8 files changed, 119 insertions(+), 33 deletions(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 92fb57b711..684540ffa6 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -62,6 +62,8 @@ service APIConnection { rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {} rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {} + rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {} + rpc voice_assistant_set_configuration(VoiceAssistantSetConfiguration) returns (void) {} rpc alarm_control_panel_command (AlarmControlPanelCommandRequest) returns (void) {} } @@ -1572,7 +1574,7 @@ message VoiceAssistantAnnounceFinished { } message VoiceAssistantWakeWord { - uint32 id = 1; + string id = 1; string wake_word = 2; repeated string trained_languages = 3; } @@ -1589,7 +1591,7 @@ message VoiceAssistantConfigurationResponse { option (ifdef) = "USE_VOICE_ASSISTANT"; repeated VoiceAssistantWakeWord available_wake_words = 1; - repeated uint32 active_wake_words = 2; + repeated string active_wake_words = 2; uint32 max_active_wake_words = 3; } @@ -1598,7 +1600,7 @@ message VoiceAssistantSetConfiguration { option (source) = SOURCE_CLIENT; option (ifdef) = "USE_VOICE_ASSISTANT"; - repeated uint32 active_wake_words = 1; + repeated string active_wake_words = 1; } // ==================== ALARM CONTROL PANEL ==================== diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 20e9a45314..7ea52e9a9e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1224,6 +1224,39 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno } } +VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configuration( + const VoiceAssistantConfigurationRequest &msg) { + VoiceAssistantConfigurationResponse resp; + if (voice_assistant::global_voice_assistant != nullptr) { + if (voice_assistant::global_voice_assistant->get_api_connection() != this) { + return resp; + } + + auto &config = voice_assistant::global_voice_assistant->get_configuration(); + for (auto &wake_word : config.available_wake_words) { + VoiceAssistantWakeWord resp_wake_word; + resp_wake_word.id = wake_word.id; + resp_wake_word.wake_word = wake_word.wake_word; + for (const auto &lang : wake_word.trained_languages) { + resp_wake_word.trained_languages.push_back(lang); + } + resp.available_wake_words.push_back(std::move(resp_wake_word)); + } + resp.max_active_wake_words = config.max_active_wake_words; + } + return resp; +} + +void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) { + if (voice_assistant::global_voice_assistant != nullptr) { + if (voice_assistant::global_voice_assistant->get_api_connection() != this) { + return; + } + + voice_assistant::global_voice_assistant->on_set_configuration(msg.active_wake_words); + } +} + #endif #ifdef USE_ALARM_CONTROL_PANEL diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index e8d66a5e07..f176cf7c56 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -152,6 +152,9 @@ class APIConnection : public APIServerConnection { void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override; void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override; void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override; + VoiceAssistantConfigurationResponse voice_assistant_get_configuration( + const VoiceAssistantConfigurationRequest &msg) override; + void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override; #endif #ifdef USE_ALARM_CONTROL_PANEL diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 791de29511..8df152881c 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -7124,18 +7124,12 @@ void VoiceAssistantAnnounceFinished::dump_to(std::string &out) const { out.append("}"); } #endif -bool VoiceAssistantWakeWord::decode_varint(uint32_t field_id, ProtoVarInt value) { - switch (field_id) { - case 1: { - this->id = value.as_uint32(); - return true; - } - default: - return false; - } -} bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { + case 1: { + this->id = value.as_string(); + return true; + } case 2: { this->wake_word = value.as_string(); return true; @@ -7149,7 +7143,7 @@ bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimit } } void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const { - buffer.encode_uint32(1, this->id); + buffer.encode_string(1, this->id); buffer.encode_string(2, this->wake_word); for (auto &it : this->trained_languages) { buffer.encode_string(3, it, true); @@ -7160,8 +7154,7 @@ void VoiceAssistantWakeWord::dump_to(std::string &out) const { __attribute__((unused)) char buffer[64]; out.append("VoiceAssistantWakeWord {\n"); out.append(" id: "); - sprintf(buffer, "%" PRIu32, this->id); - out.append(buffer); + out.append("'").append(this->id).append("'"); out.append("\n"); out.append(" wake_word: "); @@ -7184,10 +7177,6 @@ void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const { #endif bool VoiceAssistantConfigurationResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { switch (field_id) { - case 2: { - this->active_wake_words.push_back(value.as_uint32()); - return true; - } case 3: { this->max_active_wake_words = value.as_uint32(); return true; @@ -7202,6 +7191,10 @@ bool VoiceAssistantConfigurationResponse::decode_length(uint32_t field_id, Proto this->available_wake_words.push_back(value.as_message()); return true; } + case 2: { + this->active_wake_words.push_back(value.as_string()); + return true; + } default: return false; } @@ -7211,7 +7204,7 @@ void VoiceAssistantConfigurationResponse::encode(ProtoWriteBuffer buffer) const buffer.encode_message(1, it, true); } for (auto &it : this->active_wake_words) { - buffer.encode_uint32(2, it, true); + buffer.encode_string(2, it, true); } buffer.encode_uint32(3, this->max_active_wake_words); } @@ -7227,8 +7220,7 @@ void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const { for (const auto &it : this->active_wake_words) { out.append(" active_wake_words: "); - sprintf(buffer, "%" PRIu32, it); - out.append(buffer); + out.append("'").append(it).append("'"); out.append("\n"); } @@ -7239,10 +7231,10 @@ void VoiceAssistantConfigurationResponse::dump_to(std::string &out) const { out.append("}"); } #endif -bool VoiceAssistantSetConfiguration::decode_varint(uint32_t field_id, ProtoVarInt value) { +bool VoiceAssistantSetConfiguration::decode_length(uint32_t field_id, ProtoLengthDelimited value) { switch (field_id) { case 1: { - this->active_wake_words.push_back(value.as_uint32()); + this->active_wake_words.push_back(value.as_string()); return true; } default: @@ -7251,7 +7243,7 @@ bool VoiceAssistantSetConfiguration::decode_varint(uint32_t field_id, ProtoVarIn } void VoiceAssistantSetConfiguration::encode(ProtoWriteBuffer buffer) const { for (auto &it : this->active_wake_words) { - buffer.encode_uint32(1, it, true); + buffer.encode_string(1, it, true); } } #ifdef HAS_PROTO_MESSAGE_DUMP @@ -7260,8 +7252,7 @@ void VoiceAssistantSetConfiguration::dump_to(std::string &out) const { out.append("VoiceAssistantSetConfiguration {\n"); for (const auto &it : this->active_wake_words) { out.append(" active_wake_words: "); - sprintf(buffer, "%" PRIu32, it); - out.append(buffer); + out.append("'").append(it).append("'"); out.append("\n"); } out.append("}"); diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 8631884f94..063c217bf7 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1851,7 +1851,7 @@ class VoiceAssistantAnnounceFinished : public ProtoMessage { }; class VoiceAssistantWakeWord : public ProtoMessage { public: - uint32_t id{0}; + std::string id{}; std::string wake_word{}; std::vector trained_languages{}; void encode(ProtoWriteBuffer buffer) const override; @@ -1861,7 +1861,6 @@ class VoiceAssistantWakeWord : public ProtoMessage { protected: bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; class VoiceAssistantConfigurationRequest : public ProtoMessage { public: @@ -1875,7 +1874,7 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage { class VoiceAssistantConfigurationResponse : public ProtoMessage { public: std::vector available_wake_words{}; - std::vector active_wake_words{}; + std::vector active_wake_words{}; uint32_t max_active_wake_words{0}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP @@ -1888,14 +1887,14 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage { }; class VoiceAssistantSetConfiguration : public ProtoMessage { public: - std::vector active_wake_words{}; + std::vector active_wake_words{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; #endif protected: - bool decode_varint(uint32_t field_id, ProtoVarInt value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; }; class ListEntitiesAlarmControlPanelResponse : public ProtoMessage { public: diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 454f20d50a..6e11d7169d 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -1681,6 +1681,35 @@ void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVo this->subscribe_voice_assistant(msg); } #endif +#ifdef USE_VOICE_ASSISTANT +void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg); + if (!this->send_voice_assistant_configuration_response(ret)) { + this->on_fatal_error(); + } +} +#endif +#ifdef USE_VOICE_ASSISTANT +void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->voice_assistant_set_configuration(msg); +} +#endif #ifdef USE_ALARM_CONTROL_PANEL void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) { if (!this->is_connection_setup()) { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index e69954f7ab..51b94bf530 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -434,6 +434,13 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_VOICE_ASSISTANT virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0; #endif +#ifdef USE_VOICE_ASSISTANT + virtual VoiceAssistantConfigurationResponse voice_assistant_get_configuration( + const VoiceAssistantConfigurationRequest &msg) = 0; +#endif +#ifdef USE_VOICE_ASSISTANT + virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0; +#endif #ifdef USE_ALARM_CONTROL_PANEL virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0; #endif @@ -535,6 +542,12 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_VOICE_ASSISTANT void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override; #endif +#ifdef USE_VOICE_ASSISTANT + void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override; +#endif +#ifdef USE_VOICE_ASSISTANT + void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override; +#endif #ifdef USE_ALARM_CONTROL_PANEL void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override; #endif diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index b0a172332f..56ada0e75a 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -77,6 +77,18 @@ struct Timer { } }; +struct WakeWord { + std::string id; + std::string wake_word; + std::vector trained_languages; +}; + +struct Configuration { + std::vector available_wake_words; + std::vector active_wake_words; + uint32_t max_active_wake_words; +}; + class VoiceAssistant : public Component { public: void setup() override; @@ -133,6 +145,8 @@ class VoiceAssistant : public Component { void on_audio(const api::VoiceAssistantAudio &msg); void on_timer_event(const api::VoiceAssistantTimerEventResponse &msg); void on_announce(const api::VoiceAssistantAnnounceRequest &msg); + void on_set_configuration(const std::vector &active_wake_words){}; + const Configuration &get_configuration() { return this->config_; }; bool is_running() const { return this->state_ != State::IDLE; } void set_continuous(bool continuous) { this->continuous_ = continuous; } @@ -279,6 +293,8 @@ class VoiceAssistant : public Component { AudioMode audio_mode_{AUDIO_MODE_UDP}; bool udp_socket_running_{false}; bool start_udp_socket_(); + + Configuration config_{}; }; template class StartAction : public Action, public Parented { From a930b377b0cc7d6c2b60611fba7361cbbd8040e9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Sep 2024 12:57:27 +1200 Subject: [PATCH 0379/1052] Bump version to 2024.9.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 24deba1f15..929a6bea84 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.0b3" +__version__ = "2024.9.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 34229af38abf12bb09bce8a58cb5820ed8e2fc36 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Sep 2024 16:56:07 +1200 Subject: [PATCH 0380/1052] Bump version to 2024.9.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 929a6bea84..8d5996a548 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.0b4" +__version__ = "2024.9.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 969971930523fc7ed4e6cb61587115e91142c1dc Mon Sep 17 00:00:00 2001 From: Andrey Bodrov Date: Thu, 19 Sep 2024 07:07:39 +0300 Subject: [PATCH 0381/1052] openeth ethernet / qemu support (#7020) --- esphome/components/ethernet/__init__.py | 5 +++++ esphome/components/ethernet/ethernet_component.cpp | 13 +++++++++++++ esphome/components/ethernet/ethernet_component.h | 1 + tests/components/ethernet/common-openeth.yaml | 2 ++ .../components/ethernet/test-openeth.esp32-idf.yaml | 1 + 5 files changed, 22 insertions(+) create mode 100644 tests/components/ethernet/common-openeth.yaml create mode 100644 tests/components/ethernet/test-openeth.esp32-idf.yaml diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 1c6acda724..475d60df53 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -59,6 +59,7 @@ ETHERNET_TYPES = { "KSZ8081": EthernetType.ETHERNET_TYPE_KSZ8081, "KSZ8081RNA": EthernetType.ETHERNET_TYPE_KSZ8081RNA, "W5500": EthernetType.ETHERNET_TYPE_W5500, + "OPENETH": EthernetType.ETHERNET_TYPE_OPENETH, } SPI_ETHERNET_TYPES = ["W5500"] @@ -171,6 +172,7 @@ CONFIG_SCHEMA = cv.All( "KSZ8081": RMII_SCHEMA, "KSZ8081RNA": RMII_SCHEMA, "W5500": SPI_SCHEMA, + "OPENETH": BASE_SCHEMA, }, upper=True, ), @@ -240,6 +242,9 @@ async def to_code(config): if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_ETH_USE_SPI_ETHERNET", True) add_idf_sdkconfig_option("CONFIG_ETH_SPI_ETHERNET_W5500", True) + elif config[CONF_TYPE] == "OPENETH": + cg.add_define("USE_ETHERNET_OPENETH") + add_idf_sdkconfig_option("CONFIG_ETH_USE_OPENETH", True) else: cg.add(var.set_phy_addr(config[CONF_PHY_ADDR])) cg.add(var.set_mdc_pin(config[CONF_MDC_PIN])) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index fdb6eb2da0..00c7ae4ab8 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -120,6 +120,8 @@ void EthernetComponent::setup() { phy_config.reset_gpio_num = this->reset_pin_; esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); +#elif defined(USE_ETHERNET_OPENETH) + esp_eth_mac_t *mac = esp_eth_mac_new_openeth(&mac_config); #else phy_config.phy_addr = this->phy_addr_; phy_config.reset_gpio_num = this->power_pin_; @@ -143,6 +145,13 @@ void EthernetComponent::setup() { #endif switch (this->type_) { +#ifdef USE_ETHERNET_OPENETH + case ETHERNET_TYPE_OPENETH: { + phy_config.autonego_timeout_ms = 1000; + this->phy_ = esp_eth_phy_new_dp83848(&phy_config); + break; + } +#endif #if CONFIG_ETH_USE_ESP32_EMAC case ETHERNET_TYPE_LAN8720: { this->phy_ = esp_eth_phy_new_lan87xx(&phy_config); @@ -302,6 +311,10 @@ void EthernetComponent::dump_config() { eth_type = "W5500"; break; + case ETHERNET_TYPE_OPENETH: + eth_type = "OPENETH"; + break; + default: eth_type = "Unknown"; break; diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index f0fe6cab87..5ee430c046 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -25,6 +25,7 @@ enum EthernetType { ETHERNET_TYPE_KSZ8081, ETHERNET_TYPE_KSZ8081RNA, ETHERNET_TYPE_W5500, + ETHERNET_TYPE_OPENETH, }; struct ManualIP { diff --git a/tests/components/ethernet/common-openeth.yaml b/tests/components/ethernet/common-openeth.yaml new file mode 100644 index 0000000000..fbb7579598 --- /dev/null +++ b/tests/components/ethernet/common-openeth.yaml @@ -0,0 +1,2 @@ +ethernet: + type: OPENETH diff --git a/tests/components/ethernet/test-openeth.esp32-idf.yaml b/tests/components/ethernet/test-openeth.esp32-idf.yaml new file mode 100644 index 0000000000..220316f3ee --- /dev/null +++ b/tests/components/ethernet/test-openeth.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common-openeth.yaml From d0dc275e3020a9f3e719f28e3f82c8e58743e03e Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 19 Sep 2024 06:08:15 +0200 Subject: [PATCH 0382/1052] [nextion] Optionally skip connection handshake (#6905) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/nextion/base_component.py | 1 + esphome/components/nextion/display.py | 4 ++++ esphome/components/nextion/nextion.cpp | 23 ++++++++++++++++---- esphome/components/nextion/nextion.h | 16 ++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index d12434ec8f..2924f66d3c 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -27,6 +27,7 @@ CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color" CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" CONF_FONT_ID = "font_id" CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start" +CONF_SKIP_CONNECTION_HANDSHAKE = "skip_connection_handshake" def NextionName(value): diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index ce45d25e7b..e403ba7ae8 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -23,6 +23,7 @@ from .base_component import ( CONF_START_UP_PAGE, CONF_AUTO_WAKE_ON_TOUCH, CONF_EXIT_REPARSE_ON_START, + CONF_SKIP_CONNECTION_HANDSHAKE, ) CODEOWNERS = ["@senexcrenshaw", "@edwardtfn"] @@ -72,6 +73,7 @@ CONFIG_SCHEMA = ( 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, } ) .extend(cv.polling_component_schema("5s")) @@ -118,6 +120,8 @@ async def to_code(config): cg.add(var.set_exit_reparse_on_start_internal(config[CONF_EXIT_REPARSE_ON_START])) + cg.add(var.set_skip_connection_handshake(config[CONF_SKIP_CONNECTION_HANDSHAKE])) + 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 ddbd3328ef..a80f6efc91 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -43,6 +43,16 @@ bool Nextion::check_connect_() { if (this->get_is_connected_()) return true; + // 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"); + // Set the connection status to true + this->is_connected_ = true; + // Return true indicating the connection is set + return true; + } + if (this->comok_sent_ == 0) { this->reset_(false); @@ -126,10 +136,14 @@ void Nextion::reset_(bool reset_nextion) { void Nextion::dump_config() { ESP_LOGCONFIG(TAG, "Nextion:"); - 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()); + if (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, " Wake On Touch: %s", YESNO(this->auto_wake_on_touch_)); ESP_LOGCONFIG(TAG, " Exit reparse: %s", YESNO(this->exit_reparse_on_start_)); @@ -262,6 +276,7 @@ void Nextion::loop() { this->goto_page(this->start_up_page_); } + // This could probably be removed from the loop area, as those are redundant. this->set_auto_wake_on_touch(this->auto_wake_on_touch_); this->set_exit_reparse_on_start(this->exit_reparse_on_start_); diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 4546baa4d8..732ee9b455 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -926,6 +926,21 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ void set_exit_reparse_on_start(bool exit_reparse); + /** + * Sets whether the Nextion display should skip the connection handshake process. + * @param skip_handshake True or false. When skip_connection_handshake is true, + * the connection will be established without performing the handshake. + * This can be useful when using Nextion Simulator. + * + * Example: + * ```cpp + * it.set_skip_connection_handshake(true); + * ``` + * + * When set to true, the display will be marked as connected without performing a handshake. + */ + void set_skip_connection_handshake(bool skip_handshake) { this->skip_connection_handshake_ = skip_handshake; } + /** * Sets Nextion mode between sleep and awake * @param True or false. Sleep=true to enter sleep mode or sleep=false to exit sleep mode. @@ -1221,6 +1236,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe int16_t start_up_page_ = -1; bool auto_wake_on_touch_ = true; bool exit_reparse_on_start_ = false; + bool skip_connection_handshake_ = false; /** * Manually send a raw command to the display and don't wait for an acknowledgement packet. From 446f7e0a7ea55e0c2b7a17088087df5063e0335c Mon Sep 17 00:00:00 2001 From: Pavlo Dudnytskyi Date: Thu, 19 Sep 2024 06:09:27 +0200 Subject: [PATCH 0383/1052] Haier climate integration update (#7416) Co-authored-by: Pavlo Dudnytskyi --- CODEOWNERS | 1 + esphome/components/haier/climate.py | 7 +- esphome/components/haier/haier_base.cpp | 77 ++++++-- esphome/components/haier/haier_base.h | 32 ++- esphome/components/haier/hon_climate.cpp | 187 ++++++++++++------ esphome/components/haier/hon_climate.h | 20 +- .../components/haier/smartair2_climate.cpp | 42 ++-- esphome/components/haier/switch/__init__.py | 91 +++++++++ esphome/components/haier/switch/beeper.cpp | 14 ++ esphome/components/haier/switch/beeper.h | 18 ++ esphome/components/haier/switch/display.cpp | 14 ++ esphome/components/haier/switch/display.h | 18 ++ .../components/haier/switch/health_mode.cpp | 14 ++ esphome/components/haier/switch/health_mode.h | 18 ++ .../components/haier/switch/quiet_mode.cpp | 14 ++ esphome/components/haier/switch/quiet_mode.h | 18 ++ tests/components/haier/common.yaml | 14 +- 17 files changed, 492 insertions(+), 107 deletions(-) create mode 100644 esphome/components/haier/switch/__init__.py create mode 100644 esphome/components/haier/switch/beeper.cpp create mode 100644 esphome/components/haier/switch/beeper.h create mode 100644 esphome/components/haier/switch/display.cpp create mode 100644 esphome/components/haier/switch/display.h create mode 100644 esphome/components/haier/switch/health_mode.cpp create mode 100644 esphome/components/haier/switch/health_mode.h create mode 100644 esphome/components/haier/switch/quiet_mode.cpp create mode 100644 esphome/components/haier/switch/quiet_mode.h diff --git a/CODEOWNERS b/CODEOWNERS index f7fbbf9374..c95a94c509 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -166,6 +166,7 @@ esphome/components/haier/* @paveldn esphome/components/haier/binary_sensor/* @paveldn esphome/components/haier/button/* @paveldn esphome/components/haier/sensor/* @paveldn +esphome/components/haier/switch/* @paveldn esphome/components/haier/text_sensor/* @paveldn esphome/components/havells_solar/* @sourabhjaiswal esphome/components/hbridge/fan/* @WeekendWarrior diff --git a/esphome/components/haier/climate.py b/esphome/components/haier/climate.py index f7423a1356..f2dc7174cb 100644 --- a/esphome/components/haier/climate.py +++ b/esphome/components/haier/climate.py @@ -114,7 +114,6 @@ SUPPORTED_CLIMATE_PRESETS_SMARTAIR2_OPTIONS = { SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS = { "AWAY": ClimatePreset.CLIMATE_PRESET_AWAY, "BOOST": ClimatePreset.CLIMATE_PRESET_BOOST, - "ECO": ClimatePreset.CLIMATE_PRESET_ECO, "SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP, } @@ -240,7 +239,9 @@ CONFIG_SCHEMA = cv.All( ): cv.ensure_list( cv.enum(SUPPORTED_HON_CONTROL_METHODS, upper=True) ), - cv.Optional(CONF_BEEPER, default=True): cv.boolean, + cv.Optional(CONF_BEEPER): cv.invalid( + f"The {CONF_BEEPER} option is deprecated, use beeper_on/beeper_off actions or beeper switch for a haier platform instead" + ), cv.Optional( CONF_CONTROL_PACKET_SIZE, default=PROTOCOL_CONTROL_PACKET_SIZE ): cv.int_range(min=PROTOCOL_CONTROL_PACKET_SIZE, max=50), @@ -254,7 +255,7 @@ CONFIG_SCHEMA = cv.All( ): cv.int_range(min=PROTOCOL_STATUS_MESSAGE_HEADER_SIZE), cv.Optional( CONF_SUPPORTED_PRESETS, - default=["BOOST", "ECO", "SLEEP"], # No AWAY by default + default=["BOOST", "SLEEP"], # No AWAY by default ): cv.ensure_list( cv.enum(SUPPORTED_CLIMATE_PRESETS_HON_OPTIONS, upper=True) ), diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index 0bd3863160..ba80c1ca1b 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -52,8 +52,6 @@ bool check_timeout(std::chrono::steady_clock::time_point now, std::chrono::stead HaierClimateBase::HaierClimateBase() : haier_protocol_(*this), protocol_phase_(ProtocolPhases::SENDING_INIT_1), - display_status_(true), - health_mode_(false), force_send_control_(false), forced_request_status_(false), reset_protocol_request_(false), @@ -127,21 +125,34 @@ haier_protocol::HaierMessage HaierClimateBase::get_wifi_signal_message_() { } #endif -bool HaierClimateBase::get_display_state() const { return this->display_status_; } - -void HaierClimateBase::set_display_state(bool state) { - if (this->display_status_ != state) { - this->display_status_ = state; - this->force_send_control_ = true; +void HaierClimateBase::save_settings() { + HaierBaseSettings settings{this->get_health_mode(), this->get_display_state()}; + if (!this->base_rtc_.save(&settings)) { + ESP_LOGW(TAG, "Failed to save settings"); } } -bool HaierClimateBase::get_health_mode() const { return this->health_mode_; } +bool HaierClimateBase::get_display_state() const { + return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON); +} + +void HaierClimateBase::set_display_state(bool state) { + if (state != this->get_display_state()) { + this->display_status_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; + this->force_send_control_ = true; + this->save_settings(); + } +} + +bool HaierClimateBase::get_health_mode() const { + return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON); +} void HaierClimateBase::set_health_mode(bool state) { - if (this->health_mode_ != state) { - this->health_mode_ = state; + if (state != this->get_health_mode()) { + this->health_mode_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; this->force_send_control_ = true; + this->save_settings(); } } @@ -287,6 +298,14 @@ void HaierClimateBase::loop() { } this->process_phase(now); this->haier_protocol_.loop(); +#ifdef USE_SWITCH + if ((this->display_switch_ != nullptr) && (this->display_switch_->state != this->get_display_state())) { + this->display_switch_->publish_state(this->get_display_state()); + } + if ((this->health_mode_switch_ != nullptr) && (this->health_mode_switch_->state != this->get_health_mode())) { + this->health_mode_switch_->publish_state(this->get_health_mode()); + } +#endif // USE_SWITCH } void HaierClimateBase::process_protocol_reset() { @@ -329,6 +348,26 @@ bool HaierClimateBase::prepare_pending_action() { ClimateTraits HaierClimateBase::traits() { return traits_; } +void HaierClimateBase::initialization() { + constexpr uint32_t restore_settings_version = 0xA77D21EF; + this->base_rtc_ = + global_preferences->make_preference(this->get_object_id_hash() ^ restore_settings_version); + HaierBaseSettings recovered; + if (!this->base_rtc_.load(&recovered)) { + recovered = {false, true}; + } + this->display_status_ = recovered.display_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; + this->health_mode_ = recovered.health_mode ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; +#ifdef USE_SWITCH + if (this->display_switch_ != nullptr) { + this->display_switch_->publish_state(this->get_display_state()); + } + if (this->health_mode_switch_ != nullptr) { + this->health_mode_switch_->publish_state(this->get_health_mode()); + } +#endif +} + void HaierClimateBase::control(const ClimateCall &call) { ESP_LOGD("Control", "Control call"); if (!this->valid_connection()) { @@ -353,6 +392,22 @@ void HaierClimateBase::control(const ClimateCall &call) { } } +#ifdef USE_SWITCH +void HaierClimateBase::set_display_switch(switch_::Switch *sw) { + this->display_switch_ = sw; + if ((this->display_switch_ != nullptr) && (this->valid_connection())) { + this->display_switch_->publish_state(this->get_display_state()); + } +} + +void HaierClimateBase::set_health_mode_switch(switch_::Switch *sw) { + this->health_mode_switch_ = sw; + if ((this->health_mode_switch_ != nullptr) && (this->valid_connection())) { + this->health_mode_switch_->publish_state(this->get_health_mode()); + } +} +#endif + void HaierClimateBase::HvacSettings::reset() { this->valid = false; this->mode.reset(); diff --git a/esphome/components/haier/haier_base.h b/esphome/components/haier/haier_base.h index 7d92a6611c..f0597c49ff 100644 --- a/esphome/components/haier/haier_base.h +++ b/esphome/components/haier/haier_base.h @@ -8,6 +8,10 @@ // HaierProtocol #include +#ifdef USE_SWITCH +#include "esphome/components/switch/switch.h" +#endif + namespace esphome { namespace haier { @@ -20,10 +24,24 @@ enum class ActionRequest : uint8_t { START_STERI_CLEAN = 5, // only hOn }; +struct HaierBaseSettings { + bool health_mode; + bool display_state; +}; + class HaierClimateBase : public esphome::Component, public esphome::climate::Climate, public esphome::uart::UARTDevice, public haier_protocol::ProtocolStream { +#ifdef USE_SWITCH + public: + void set_display_switch(switch_::Switch *sw); + void set_health_mode_switch(switch_::Switch *sw); + + protected: + switch_::Switch *display_switch_{nullptr}; + switch_::Switch *health_mode_switch_{nullptr}; +#endif public: HaierClimateBase(); HaierClimateBase(const HaierClimateBase &) = delete; @@ -82,7 +100,8 @@ class HaierClimateBase : public esphome::Component, virtual void process_phase(std::chrono::steady_clock::time_point now) = 0; virtual haier_protocol::HaierMessage get_control_message() = 0; // NOLINT(readability-identifier-naming) virtual haier_protocol::HaierMessage get_power_message(bool state) = 0; // NOLINT(readability-identifier-naming) - virtual void initialization(){}; + virtual void save_settings(); + virtual void initialization(); virtual bool prepare_pending_action(); virtual void process_protocol_reset(); esphome::climate::ClimateTraits traits() override; @@ -127,13 +146,19 @@ class HaierClimateBase : public esphome::Component, ActionRequest action; esphome::optional message; }; + enum class SwitchState { + OFF = 0b00, + ON = 0b01, + PENDING_OFF = 0b10, + PENDING_ON = 0b11, + }; haier_protocol::ProtocolHandler haier_protocol_; ProtocolPhases protocol_phase_; esphome::optional action_request_; uint8_t fan_mode_speed_; uint8_t other_modes_fan_speed_; - bool display_status_; - bool health_mode_; + SwitchState display_status_{SwitchState::ON}; + SwitchState health_mode_{SwitchState::OFF}; bool force_send_control_; bool forced_request_status_; bool reset_protocol_request_; @@ -148,6 +173,7 @@ class HaierClimateBase : public esphome::Component, std::chrono::steady_clock::time_point last_status_request_; // To request AC status std::chrono::steady_clock::time_point last_signal_request_; // To send WiFI signal level CallbackManager status_message_callback_{}; + ESPPreferenceObject base_rtc_; }; class StatusMessageTrigger : public Trigger { diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index a1c5098cec..e7be1fa418 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -31,9 +31,32 @@ HonClimate::HonClimate() HonClimate::~HonClimate() {} -void HonClimate::set_beeper_state(bool state) { this->beeper_status_ = state; } +void HonClimate::set_beeper_state(bool state) { + if (state != this->settings_.beeper_state) { + this->settings_.beeper_state = state; +#ifdef USE_SWITCH + this->beeper_switch_->publish_state(state); +#endif + this->hon_rtc_.save(&this->settings_); + } +} -bool HonClimate::get_beeper_state() const { return this->beeper_status_; } +bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state; } + +void HonClimate::set_quiet_mode_state(bool state) { + if (state != this->get_quiet_mode_state()) { + this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; + this->settings_.quiet_mode_state = state; +#ifdef USE_SWITCH + this->quiet_mode_switch_->publish_state(state); +#endif + this->hon_rtc_.save(&this->settings_); + } +} + +bool HonClimate::get_quiet_mode_state() const { + return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON); +} esphome::optional HonClimate::get_vertical_airflow() const { return this->current_vertical_swing_; @@ -474,16 +497,19 @@ haier_protocol::HaierMessage HonClimate::get_power_message(bool state) { } void HonClimate::initialization() { - constexpr uint32_t restore_settings_version = 0xE834D8DCUL; - this->rtc_ = global_preferences->make_preference(this->get_object_id_hash() ^ restore_settings_version); + HaierClimateBase::initialization(); + constexpr uint32_t restore_settings_version = 0x57EB59DDUL; + this->hon_rtc_ = + global_preferences->make_preference(this->get_object_id_hash() ^ restore_settings_version); HonSettings recovered; - if (this->rtc_.load(&recovered)) { + if (this->hon_rtc_.load(&recovered)) { this->settings_ = recovered; } else { - this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER}; + this->settings_ = {hon_protocol::VerticalSwingMode::CENTER, hon_protocol::HorizontalSwingMode::CENTER, true, false}; } this->current_vertical_swing_ = this->settings_.last_vertiacal_swing; this->current_horizontal_swing_ = this->settings_.last_horizontal_swing; + this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; } haier_protocol::HaierMessage HonClimate::get_control_message() { @@ -519,8 +545,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { out_data->ac_power = 1; out_data->ac_mode = (uint8_t) hon_protocol::ConditioningMode::FAN; out_data->fan_mode = this->fan_mode_speed_; // Auto doesn't work in fan only mode - // Disabling boost and eco mode for Fan only - out_data->quiet_mode = 0; + // Disabling boost for Fan only out_data->fast_mode = 0; break; case CLIMATE_MODE_COOL: @@ -582,47 +607,34 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { } if (out_data->ac_power == 0) { // If AC is off - no presets allowed - out_data->quiet_mode = 0; out_data->fast_mode = 0; out_data->sleep_mode = 0; } else if (climate_control.preset.has_value()) { switch (climate_control.preset.value()) { case CLIMATE_PRESET_NONE: - out_data->quiet_mode = 0; - out_data->fast_mode = 0; - out_data->sleep_mode = 0; - out_data->ten_degree = 0; - break; - case CLIMATE_PRESET_ECO: - // Eco is not supported in Fan only mode - out_data->quiet_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; out_data->fast_mode = 0; out_data->sleep_mode = 0; out_data->ten_degree = 0; break; case CLIMATE_PRESET_BOOST: - out_data->quiet_mode = 0; // Boost is not supported in Fan only mode out_data->fast_mode = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 1 : 0; out_data->sleep_mode = 0; out_data->ten_degree = 0; break; case CLIMATE_PRESET_AWAY: - out_data->quiet_mode = 0; out_data->fast_mode = 0; out_data->sleep_mode = 0; // 10 degrees allowed only in heat mode out_data->ten_degree = (this->mode == CLIMATE_MODE_HEAT) ? 1 : 0; break; case CLIMATE_PRESET_SLEEP: - out_data->quiet_mode = 0; out_data->fast_mode = 0; out_data->sleep_mode = 1; out_data->ten_degree = 0; break; default: ESP_LOGE("Control", "Unsupported preset"); - out_data->quiet_mode = 0; out_data->fast_mode = 0; out_data->sleep_mode = 0; out_data->ten_degree = 0; @@ -638,10 +650,23 @@ haier_protocol::HaierMessage HonClimate::get_control_message() { out_data->horizontal_swing_mode = (uint8_t) this->pending_horizontal_direction_.value(); this->pending_horizontal_direction_.reset(); } - out_data->beeper_status = ((!this->beeper_status_) || (!has_hvac_settings)) ? 1 : 0; + { + // Quiet mode + if ((out_data->ac_power == 0) || (out_data->ac_mode == (uint8_t) hon_protocol::ConditioningMode::FAN)) { + // If AC is off or in fan only mode - no quiet mode allowed + out_data->quiet_mode = 0; + } else { + out_data->quiet_mode = this->get_quiet_mode_state() ? 1 : 0; + } + // Clean quiet mode state pending flag + this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01); + } + out_data->beeper_status = ((!this->get_beeper_state()) || (!has_hvac_settings)) ? 1 : 0; control_out_buffer[4] = 0; // This byte should be cleared before setting values - out_data->display_status = this->display_status_ ? 1 : 0; - out_data->health_mode = this->health_mode_ ? 1 : 0; + out_data->display_status = this->get_display_state() ? 1 : 0; + this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01); + out_data->health_mode = this->get_health_mode() ? 1 : 0; + this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_GROUP_PARAMETERS, control_out_buffer, this->real_control_packet_size_); @@ -765,6 +790,22 @@ void HonClimate::update_sub_text_sensor_(SubTextSensorType type, const std::stri } #endif // USE_TEXT_SENSOR +#ifdef USE_SWITCH +void HonClimate::set_beeper_switch(switch_::Switch *sw) { + this->beeper_switch_ = sw; + if (this->beeper_switch_ != nullptr) { + this->beeper_switch_->publish_state(this->get_beeper_state()); + } +} + +void HonClimate::set_quiet_mode_switch(switch_::Switch *sw) { + this->quiet_mode_switch_ = sw; + if (this->quiet_mode_switch_ != nullptr) { + this->quiet_mode_switch_->publish_state(this->settings_.quiet_mode_state); + } +} +#endif // USE_SWITCH + haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *packet_buffer, uint8_t size) { size_t expected_size = 2 + this->status_message_header_size_ + this->real_control_packet_size_ + this->real_sensors_packet_size_; @@ -827,9 +868,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * { // Extra modes/presets optional old_preset = this->preset; - if (packet.control.quiet_mode != 0) { - this->preset = CLIMATE_PRESET_ECO; - } else if (packet.control.fast_mode != 0) { + if (packet.control.fast_mode != 0) { this->preset = CLIMATE_PRESET_BOOST; } else if (packet.control.sleep_mode != 0) { this->preset = CLIMATE_PRESET_SLEEP; @@ -883,28 +922,26 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * } should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value()); } - { - // Display status - // should be before "Climate mode" because it is changing this->mode - if (packet.control.ac_power != 0) { - // if AC is off display status always ON so process it only when AC is on - bool disp_status = packet.control.display_status != 0; - if (disp_status != this->display_status_) { - // Do something only if display status changed - if (this->mode == CLIMATE_MODE_OFF) { - // AC just turned on from remote need to turn off display - this->force_send_control_ = true; - } else { - this->display_status_ = disp_status; - } + // Display status + // should be before "Climate mode" because it is changing this->mode + if (packet.control.ac_power != 0) { + // if AC is off display status always ON so process it only when AC is on + bool disp_status = packet.control.display_status != 0; + if (disp_status != this->get_display_state()) { + // Do something only if display status changed + if (this->mode == CLIMATE_MODE_OFF) { + // AC just turned on from remote need to turn off display + this->force_send_control_ = true; + } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) { + this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; } } } - { - // Health mode - bool old_health_mode = this->health_mode_; - this->health_mode_ = packet.control.health_mode == 1; - should_publish = should_publish || (old_health_mode != this->health_mode_); + // Health mode + if ((((uint8_t) this->health_mode_) & 0b10) == 0) { + bool old_health_mode = this->get_health_mode(); + this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF; + should_publish = should_publish || (old_health_mode != this->get_health_mode()); } { CleaningState new_cleaning; @@ -958,17 +995,36 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * } should_publish = should_publish || (old_mode != this->mode); } + { + // Quiet mode, should be after climate mode + if ((this->mode != CLIMATE_MODE_FAN_ONLY) && (this->mode != CLIMATE_MODE_OFF) && + ((((uint8_t) this->quiet_mode_state_) & 0b10) == 0)) { + // In proper mode and not in pending state + bool new_quiet_mode = packet.control.quiet_mode != 0; + if (new_quiet_mode != this->get_quiet_mode_state()) { + this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF; + this->settings_.quiet_mode_state = new_quiet_mode; + this->hon_rtc_.save(&this->settings_); + } + } + } { // Swing mode ClimateSwingMode old_swing_mode = this->swing_mode; - if (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO) { - if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) { + const std::set &swing_modes = traits_.get_supported_swing_modes(); + bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end(); + bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end(); + if (horizontal_swing_supported && + (packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) { + if (vertical_swing_supported && + (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) { this->swing_mode = CLIMATE_SWING_BOTH; } else { this->swing_mode = CLIMATE_SWING_HORIZONTAL; } } else { - if (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO) { + if (vertical_swing_supported && + (packet.control.vertical_swing_mode == (uint8_t) hon_protocol::VerticalSwingMode::AUTO)) { this->swing_mode = CLIMATE_SWING_VERTICAL; } else { this->swing_mode = CLIMATE_SWING_OFF; @@ -985,7 +1041,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * if (save_settings) { this->settings_.last_vertiacal_swing = this->current_vertical_swing_.value(); this->settings_.last_horizontal_swing = this->current_horizontal_swing_.value(); - this->rtc_.save(&this->settings_); + this->hon_rtc_.save(&this->settings_); } should_publish = should_publish || (old_swing_mode != this->swing_mode); } @@ -1017,7 +1073,7 @@ void HonClimate::fill_control_messages_queue_() { haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS, - this->beeper_status_ ? ZERO_BUF : ONE_BUF, 2)); + this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2)); } // Health mode { @@ -1025,13 +1081,16 @@ void HonClimate::fill_control_messages_queue_() { haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + (uint8_t) hon_protocol::DataParameters::HEALTH_MODE, - this->health_mode_ ? ONE_BUF : ZERO_BUF, 2)); + this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2)); + this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); } // Climate mode + ClimateMode climate_mode = this->mode; bool new_power = this->mode != CLIMATE_MODE_OFF; uint8_t fan_mode_buf[] = {0x00, 0xFF}; uint8_t quiet_mode_buf[] = {0x00, 0xFF}; if (climate_control.mode.has_value()) { + climate_mode = climate_control.mode.value(); uint8_t buffer[2] = {0x00, 0x00}; switch (climate_control.mode.value()) { case CLIMATE_MODE_OFF: @@ -1076,8 +1135,6 @@ void HonClimate::fill_control_messages_queue_() { (uint8_t) hon_protocol::DataParameters::AC_MODE, buffer, 2)); fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode - // Disabling eco mode for Fan only - quiet_mode_buf[1] = 0; break; case CLIMATE_MODE_COOL: new_power = true; @@ -1108,30 +1165,20 @@ void HonClimate::fill_control_messages_queue_() { uint8_t away_mode_buf[] = {0x00, 0xFF}; if (!new_power) { // If AC is off - no presets allowed - quiet_mode_buf[1] = 0x00; fast_mode_buf[1] = 0x00; away_mode_buf[1] = 0x00; } else if (climate_control.preset.has_value()) { switch (climate_control.preset.value()) { case CLIMATE_PRESET_NONE: - quiet_mode_buf[1] = 0x00; - fast_mode_buf[1] = 0x00; - away_mode_buf[1] = 0x00; - break; - case CLIMATE_PRESET_ECO: - // Eco is not supported in Fan only mode - quiet_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; fast_mode_buf[1] = 0x00; away_mode_buf[1] = 0x00; break; case CLIMATE_PRESET_BOOST: - quiet_mode_buf[1] = 0x00; // Boost is not supported in Fan only mode fast_mode_buf[1] = (this->mode != CLIMATE_MODE_FAN_ONLY) ? 0x01 : 0x00; away_mode_buf[1] = 0x00; break; case CLIMATE_PRESET_AWAY: - quiet_mode_buf[1] = 0x00; fast_mode_buf[1] = 0x00; away_mode_buf[1] = (this->mode == CLIMATE_MODE_HEAT) ? 0x01 : 0x00; break; @@ -1140,8 +1187,18 @@ void HonClimate::fill_control_messages_queue_() { break; } } + { + // Quiet mode + if (new_power && (climate_mode != CLIMATE_MODE_FAN_ONLY) && this->get_quiet_mode_state()) { + quiet_mode_buf[1] = 0x01; + } else { + quiet_mode_buf[1] = 0x00; + } + // Clean quiet mode state pending flag + this->quiet_mode_state_ = (SwitchState) ((uint8_t) this->quiet_mode_state_ & 0b01); + } auto presets = this->traits_.get_supported_presets(); - if ((quiet_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_ECO) != presets.end()))) { + if (quiet_mode_buf[1] != 0xFF) { this->control_messages_queue_.push( haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + diff --git a/esphome/components/haier/hon_climate.h b/esphome/components/haier/hon_climate.h index 64c54186ed..58173f8154 100644 --- a/esphome/components/haier/hon_climate.h +++ b/esphome/components/haier/hon_climate.h @@ -10,6 +10,9 @@ #ifdef USE_TEXT_SENSOR #include "esphome/components/text_sensor/text_sensor.h" #endif +#ifdef USE_SWITCH +#include "esphome/components/switch/switch.h" +#endif #include "esphome/core/automation.h" #include "haier_base.h" #include "hon_packet.h" @@ -28,6 +31,8 @@ enum class HonControlMethod { MONITOR_ONLY = 0, SET_GROUP_PARAMETERS, SET_SINGLE struct HonSettings { hon_protocol::VerticalSwingMode last_vertiacal_swing; hon_protocol::HorizontalSwingMode last_horizontal_swing; + bool beeper_state; + bool quiet_mode_state; }; class HonClimate : public HaierClimateBase { @@ -86,6 +91,15 @@ class HonClimate : public HaierClimateBase { protected: void update_sub_text_sensor_(SubTextSensorType type, const std::string &value); text_sensor::TextSensor *sub_text_sensors_[(size_t) SubTextSensorType::SUB_TEXT_SENSOR_TYPE_COUNT]{nullptr}; +#endif +#ifdef USE_SWITCH + public: + void set_beeper_switch(switch_::Switch *sw); + void set_quiet_mode_switch(switch_::Switch *sw); + + protected: + switch_::Switch *beeper_switch_{nullptr}; + switch_::Switch *quiet_mode_switch_{nullptr}; #endif public: HonClimate(); @@ -95,6 +109,8 @@ class HonClimate : public HaierClimateBase { void dump_config() override; void set_beeper_state(bool state); bool get_beeper_state() const; + void set_quiet_mode_state(bool state); + bool get_quiet_mode_state() const; esphome::optional get_vertical_airflow() const; void set_vertical_airflow(hon_protocol::VerticalSwingMode direction); esphome::optional get_horizontal_airflow() const; @@ -153,7 +169,6 @@ class HonClimate : public HaierClimateBase { bool functions_[5]; }; - bool beeper_status_; CleaningState cleaning_status_; bool got_valid_outdoor_temp_; esphome::optional pending_vertical_direction_{}; @@ -175,7 +190,8 @@ class HonClimate : public HaierClimateBase { esphome::optional current_vertical_swing_{}; esphome::optional current_horizontal_swing_{}; HonSettings settings_; - ESPPreferenceObject rtc_; + ESPPreferenceObject hon_rtc_; + SwitchState quiet_mode_state_{SwitchState::OFF}; }; class HaierAlarmStartTrigger : public Trigger { diff --git a/esphome/components/haier/smartair2_climate.cpp b/esphome/components/haier/smartair2_climate.cpp index 028e8a4087..63c22821b3 100644 --- a/esphome/components/haier/smartair2_climate.cpp +++ b/esphome/components/haier/smartair2_climate.cpp @@ -376,8 +376,10 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() { } } } - out_data->display_status = this->display_status_ ? 0 : 1; - out_data->health_mode = this->health_mode_ ? 1 : 0; + out_data->display_status = this->get_display_state() ? 0 : 1; + this->display_status_ = (SwitchState) ((uint8_t) this->display_status_ & 0b01); + out_data->health_mode = this->get_health_mode() ? 1 : 0; + this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); return haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, 0x4D5F, control_out_buffer, sizeof(smartair2_protocol::HaierPacketControl)); } @@ -446,28 +448,26 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin } should_publish = should_publish || (!old_fan_mode.has_value()) || (old_fan_mode.value() != fan_mode.value()); } - { - // Display status - // should be before "Climate mode" because it is changing this->mode - if (packet.control.ac_power != 0) { - // if AC is off display status always ON so process it only when AC is on - bool disp_status = packet.control.display_status == 0; - if (disp_status != this->display_status_) { - // Do something only if display status changed - if (this->mode == CLIMATE_MODE_OFF) { - // AC just turned on from remote need to turn off display - this->force_send_control_ = true; - } else { - this->display_status_ = disp_status; - } + // Display status + // should be before "Climate mode" because it is changing this->mode + if (packet.control.ac_power != 0) { + // if AC is off display status always ON so process it only when AC is on + bool disp_status = packet.control.display_status == 0; + if (disp_status != this->get_display_state()) { + // Do something only if display status changed + if (this->mode == CLIMATE_MODE_OFF) { + // AC just turned on from remote need to turn off display + this->force_send_control_ = true; + } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) { + this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; } } } - { - // Health mode - bool old_health_mode = this->health_mode_; - this->health_mode_ = packet.control.health_mode == 1; - should_publish = should_publish || (old_health_mode != this->health_mode_); + // Health mode + if ((((uint8_t) this->health_mode_) & 0b10) == 0) { + bool old_health_mode = this->get_health_mode(); + this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF; + should_publish = should_publish || (old_health_mode != this->get_health_mode()); } { // Climate mode diff --git a/esphome/components/haier/switch/__init__.py b/esphome/components/haier/switch/__init__.py new file mode 100644 index 0000000000..6076cb0bd5 --- /dev/null +++ b/esphome/components/haier/switch/__init__.py @@ -0,0 +1,91 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +import esphome.final_validate as fv +from esphome.components import switch +from esphome.const import ( + CONF_BEEPER, + CONF_DISPLAY, + ENTITY_CATEGORY_CONFIG, +) +from ..climate import ( + CONF_HAIER_ID, + CONF_PROTOCOL, + HaierClimateBase, + haier_ns, + PROTOCOL_HON, +) + +CODEOWNERS = ["@paveldn"] +BeeperSwitch = haier_ns.class_("BeeperSwitch", switch.Switch) +HealthModeSwitch = haier_ns.class_("HealthModeSwitch", switch.Switch) +DisplaySwitch = haier_ns.class_("DisplaySwitch", switch.Switch) +QuietModeSwitch = haier_ns.class_("QuietModeSwitch", switch.Switch) + +# Haier switches +CONF_HEALTH_MODE = "health_mode" +CONF_QUIET_MODE = "quiet_mode" + +# Additional icons +ICON_LEAF = "mdi:leaf" +ICON_LED_ON = "mdi:led-on" +ICON_VOLUME_HIGH = "mdi:volume-high" +ICON_VOLUME_OFF = "mdi:volume-off" + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_HAIER_ID): cv.use_id(HaierClimateBase), + cv.Optional(CONF_DISPLAY): switch.switch_schema( + DisplaySwitch, + icon=ICON_LED_ON, + entity_category=ENTITY_CATEGORY_CONFIG, + default_restore_mode="DISABLED", + ), + cv.Optional(CONF_HEALTH_MODE): switch.switch_schema( + HealthModeSwitch, + icon=ICON_LEAF, + default_restore_mode="DISABLED", + ), + # Beeper switch is only supported for HonClimate + cv.Optional(CONF_BEEPER): switch.switch_schema( + BeeperSwitch, + icon=ICON_VOLUME_HIGH, + entity_category=ENTITY_CATEGORY_CONFIG, + default_restore_mode="DISABLED", + ), + # Quiet mode is only supported for HonClimate + cv.Optional(CONF_QUIET_MODE): switch.switch_schema( + QuietModeSwitch, + icon=ICON_VOLUME_OFF, + entity_category=ENTITY_CATEGORY_CONFIG, + default_restore_mode="DISABLED", + ), + } +) + + +def _final_validate(config): + full_config = fv.full_config.get() + for switch_type in [CONF_BEEPER, CONF_QUIET_MODE]: + # Check switches that are only supported for HonClimate + if config.get(switch_type): + climate_path = full_config.get_path_for_id(config[CONF_HAIER_ID])[:-1] + climate_conf = full_config.get_config_for_path(climate_path) + protocol_type = climate_conf.get(CONF_PROTOCOL) + if protocol_type.casefold() != PROTOCOL_HON.casefold(): + raise cv.Invalid( + f"{switch_type} switch is only supported for hon climate" + ) + return config + + +FINAL_VALIDATE_SCHEMA = _final_validate + + +async def to_code(config): + parent = await cg.get_variable(config[CONF_HAIER_ID]) + + for switch_type in [CONF_DISPLAY, CONF_HEALTH_MODE, CONF_BEEPER, CONF_QUIET_MODE]: + if conf := config.get(switch_type): + sw_var = await switch.new_switch(conf) + await cg.register_parented(sw_var, parent) + cg.add(getattr(parent, f"set_{switch_type}_switch")(sw_var)) diff --git a/esphome/components/haier/switch/beeper.cpp b/esphome/components/haier/switch/beeper.cpp new file mode 100644 index 0000000000..1ce64d0848 --- /dev/null +++ b/esphome/components/haier/switch/beeper.cpp @@ -0,0 +1,14 @@ +#include "beeper.h" + +namespace esphome { +namespace haier { + +void BeeperSwitch::write_state(bool state) { + if (this->parent_->get_beeper_state() != state) { + this->parent_->set_beeper_state(state); + } + this->publish_state(state); +} + +} // namespace haier +} // namespace esphome diff --git a/esphome/components/haier/switch/beeper.h b/esphome/components/haier/switch/beeper.h new file mode 100644 index 0000000000..7396a7a0dd --- /dev/null +++ b/esphome/components/haier/switch/beeper.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "../hon_climate.h" + +namespace esphome { +namespace haier { + +class BeeperSwitch : public switch_::Switch, public Parented { + public: + BeeperSwitch() = default; + + protected: + void write_state(bool state) override; +}; + +} // namespace haier +} // namespace esphome diff --git a/esphome/components/haier/switch/display.cpp b/esphome/components/haier/switch/display.cpp new file mode 100644 index 0000000000..5e24843dcf --- /dev/null +++ b/esphome/components/haier/switch/display.cpp @@ -0,0 +1,14 @@ +#include "display.h" + +namespace esphome { +namespace haier { + +void DisplaySwitch::write_state(bool state) { + if (this->parent_->get_display_state() != state) { + this->parent_->set_display_state(state); + } + this->publish_state(state); +} + +} // namespace haier +} // namespace esphome diff --git a/esphome/components/haier/switch/display.h b/esphome/components/haier/switch/display.h new file mode 100644 index 0000000000..f93ccfcdb7 --- /dev/null +++ b/esphome/components/haier/switch/display.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "../haier_base.h" + +namespace esphome { +namespace haier { + +class DisplaySwitch : public switch_::Switch, public Parented { + public: + DisplaySwitch() = default; + + protected: + void write_state(bool state) override; +}; + +} // namespace haier +} // namespace esphome diff --git a/esphome/components/haier/switch/health_mode.cpp b/esphome/components/haier/switch/health_mode.cpp new file mode 100644 index 0000000000..3715759bdd --- /dev/null +++ b/esphome/components/haier/switch/health_mode.cpp @@ -0,0 +1,14 @@ +#include "health_mode.h" + +namespace esphome { +namespace haier { + +void HealthModeSwitch::write_state(bool state) { + if (this->parent_->get_health_mode() != state) { + this->parent_->set_health_mode(state); + } + this->publish_state(state); +} + +} // namespace haier +} // namespace esphome diff --git a/esphome/components/haier/switch/health_mode.h b/esphome/components/haier/switch/health_mode.h new file mode 100644 index 0000000000..cfd2aa2f22 --- /dev/null +++ b/esphome/components/haier/switch/health_mode.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "../haier_base.h" + +namespace esphome { +namespace haier { + +class HealthModeSwitch : public switch_::Switch, public Parented { + public: + HealthModeSwitch() = default; + + protected: + void write_state(bool state) override; +}; + +} // namespace haier +} // namespace esphome diff --git a/esphome/components/haier/switch/quiet_mode.cpp b/esphome/components/haier/switch/quiet_mode.cpp new file mode 100644 index 0000000000..056312b5f0 --- /dev/null +++ b/esphome/components/haier/switch/quiet_mode.cpp @@ -0,0 +1,14 @@ +#include "quiet_mode.h" + +namespace esphome { +namespace haier { + +void QuietModeSwitch::write_state(bool state) { + if (this->parent_->get_quiet_mode_state() != state) { + this->parent_->set_quiet_mode_state(state); + } + this->publish_state(state); +} + +} // namespace haier +} // namespace esphome diff --git a/esphome/components/haier/switch/quiet_mode.h b/esphome/components/haier/switch/quiet_mode.h new file mode 100644 index 0000000000..bad5289500 --- /dev/null +++ b/esphome/components/haier/switch/quiet_mode.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "../hon_climate.h" + +namespace esphome { +namespace haier { + +class QuietModeSwitch : public switch_::Switch, public Parented { + public: + QuietModeSwitch() = default; + + protected: + void write_state(bool state) override; +}; + +} // namespace haier +} // namespace esphome diff --git a/tests/components/haier/common.yaml b/tests/components/haier/common.yaml index b8a23bac5a..368b88b69c 100644 --- a/tests/components/haier/common.yaml +++ b/tests/components/haier/common.yaml @@ -16,7 +16,6 @@ climate: name: Haier AC wifi_signal: true answer_timeout: 200ms - beeper: true visual: min_temperature: 16 °C max_temperature: 30 °C @@ -38,7 +37,6 @@ climate: supported_presets: - AWAY - BOOST - - ECO - SLEEP on_alarm_start: then: @@ -112,3 +110,15 @@ text_sensor: name: Haier cleaning status protocol_version: name: Haier protocol version + +switch: + - platform: haier + haier_id: haier_ac + beeper: + name: Haier beeper + display: + name: Haier display + health_mode: + name: Haier health mode + quiet_mode: + name: Haier quiet mode From ddde64a48dceb0cb407fb420f67fc99345534123 Mon Sep 17 00:00:00 2001 From: Pietro Date: Thu, 19 Sep 2024 06:16:39 +0200 Subject: [PATCH 0384/1052] Added i2s_comm_fmt parameter to i2s speaker component (#7449) Co-authored-by: PxPert --- .../components/i2s_audio/speaker/__init__.py | 19 +++++++++++++++++++ .../i2s_audio/speaker/i2s_audio_speaker.cpp | 2 +- .../i2s_audio/speaker/i2s_audio_speaker.h | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index 22a5af259d..bba886b39b 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -25,6 +25,7 @@ I2SAudioSpeaker = i2s_audio_ns.class_( CONF_DAC_TYPE = "dac_type" +CONF_I2S_COMM_FMT = "i2s_comm_fmt" i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t") INTERNAL_DAC_OPTIONS = { @@ -33,6 +34,20 @@ INTERNAL_DAC_OPTIONS = { CONF_STEREO: i2s_dac_mode_t.I2S_DAC_CHANNEL_BOTH_EN, } +i2s_comm_format_t = cg.global_ns.enum("i2s_comm_format_t") +I2C_COMM_FMT_OPTIONS = { + "stand_i2s": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_I2S, + "stand_msb": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_MSB, + "stand_pcm_short": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_PCM_SHORT, + "stand_pcm_long": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_PCM_LONG, + "stand_max": i2s_comm_format_t.I2S_COMM_FORMAT_STAND_MAX, + "i2s_msb": i2s_comm_format_t.I2S_COMM_FORMAT_I2S_MSB, + "i2s_lsb": i2s_comm_format_t.I2S_COMM_FORMAT_I2S_LSB, + "pcm": i2s_comm_format_t.I2S_COMM_FORMAT_PCM, + "pcm_short": i2s_comm_format_t.I2S_COMM_FORMAT_PCM_SHORT, + "pcm_long": i2s_comm_format_t.I2S_COMM_FORMAT_PCM_LONG, +} + NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2] @@ -77,6 +92,9 @@ CONFIG_SCHEMA = cv.All( cv.Required( CONF_I2S_DOUT_PIN ): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_I2S_COMM_FMT, default="stand_i2s"): cv.enum( + I2C_COMM_FMT_OPTIONS, lower=True + ), } ), }, @@ -96,4 +114,5 @@ async def to_code(config): cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL])) else: cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN])) + cg.add(var.set_i2s_comm_fmt(config[CONF_I2S_COMM_FMT])) cg.add(var.set_timeout(config[CONF_TIMEOUT])) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 4b427898a2..97c1d86c36 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -83,7 +83,7 @@ void I2SAudioSpeaker::player_task(void *params) { .sample_rate = this_speaker->sample_rate_, .bits_per_sample = this_speaker->bits_per_sample_, .channel_format = this_speaker->channel_, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .communication_format = this_speaker->i2s_comm_fmt_, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, .dma_buf_len = 256, diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 7adc4e8a24..9d1817c86f 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -49,6 +49,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp #if SOC_I2S_SUPPORTS_DAC void set_internal_dac_mode(i2s_dac_mode_t mode) { this->internal_dac_mode_ = mode; } #endif + void set_i2s_comm_fmt(i2s_comm_format_t mode) { this->i2s_comm_fmt_ = mode; } void start() override; void stop() override; @@ -76,6 +77,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp #if SOC_I2S_SUPPORTS_DAC i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE}; #endif + i2s_comm_format_t i2s_comm_fmt_; }; } // namespace i2s_audio From 6d24e9ebb5de546f529c7cf981718a1368e5d023 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:17:04 +1000 Subject: [PATCH 0385/1052] [lvgl] Enhancements (#7453) --- esphome/components/lvgl/__init__.py | 17 ++- esphome/components/lvgl/hello_world.py | 64 +++++++++ esphome/components/lvgl/lv_validation.py | 157 ++++++++++++++++++++++- tests/components/lvgl/lvgl-package.yaml | 4 +- tests/components/lvgl/test.host.yaml | 11 ++ 5 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 esphome/components/lvgl/hello_world.py create mode 100644 tests/components/lvgl/test.host.yaml diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 64f254cde8..a3a6f7ddaf 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -22,9 +22,10 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, focused_widgets, update_to_code -from .defines import add_define +from .defines import CONF_WIDGETS, add_define from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code from .gradient import GRADIENT_SCHEMA, gradients_to_code +from .hello_world import get_hello_world from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent from .schemas import ( @@ -32,7 +33,7 @@ from .schemas import ( FLEX_OBJ_SCHEMA, GRID_CELL_SCHEMA, LAYOUT_SCHEMAS, - STYLE_SCHEMA, + STATE_SCHEMA, WIDGET_TYPES, any_widget_schema, container_schema, @@ -292,6 +293,13 @@ def display_schema(config): return value or [cv.use_id(Display)(config)] +def add_hello_world(config): + if CONF_WIDGETS not in config and CONF_PAGES not in config: + LOGGER.info("No pages or widgets configured, creating default hello_world page") + config[CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world()) + return config + + FINAL_VALIDATE_SCHEMA = final_validation CONFIG_SCHEMA = ( @@ -313,7 +321,7 @@ CONFIG_SCHEMA = ( ), cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list( cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)}) - .extend(STYLE_SCHEMA) + .extend(STATE_SCHEMA) .extend( { cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, @@ -349,4 +357,5 @@ CONFIG_SCHEMA = ( } ) .extend(DISP_BG_SCHEMA) -).add_extra(cv.has_at_least_one_key(CONF_PAGES, df.CONF_WIDGETS)) + .add_extra(add_hello_world) +) diff --git a/esphome/components/lvgl/hello_world.py b/esphome/components/lvgl/hello_world.py new file mode 100644 index 0000000000..2c2ec6732c --- /dev/null +++ b/esphome/components/lvgl/hello_world.py @@ -0,0 +1,64 @@ +from io import StringIO + +from esphome.yaml_util import parse_yaml + +CONFIG = """ +- obj: + radius: 0 + pad_all: 12 + bg_color: 0xFFFFFF + height: 100% + width: 100% + widgets: + - spinner: + id: hello_world_spinner_ + align: center + indicator: + arc_color: tomato + height: 100 + width: 100 + spin_time: 2s + arc_length: 60deg + - label: + id: hello_world_label_ + text: "Hello World!" + align: center + on_click: + lvgl.spinner.update: + id: hello_world_spinner_ + arc_color: springgreen + - checkbox: + pad_all: 8 + text: Checkbox + align: top_right + on_click: + lvgl.label.update: + id: hello_world_label_ + text: "Checked!" + - button: + pad_all: 8 + checkable: true + align: top_left + text_font: montserrat_20 + on_click: + lvgl.label.update: + id: hello_world_label_ + text: "Clicked!" + widgets: + - label: + text: "Button" + - slider: + width: 80% + align: bottom_mid + on_value: + lvgl.label.update: + id: hello_world_label_ + text: + format: "%.0f%%" + args: [x] +""" + + +def get_hello_world(): + with StringIO(CONFIG) as fp: + return parse_yaml("hello_world", fp) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 8593deb869..3dee0189fb 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -49,17 +49,172 @@ def opacity_validator(value): opacity = LValidator(opacity_validator, uint32, retmapper=literal) +COLOR_NAMES = { + "aliceblue": 0xF0F8FF, + "antiquewhite": 0xFAEBD7, + "aqua": 0x00FFFF, + "aquamarine": 0x7FFFD4, + "azure": 0xF0FFFF, + "beige": 0xF5F5DC, + "bisque": 0xFFE4C4, + "black": 0x000000, + "blanchedalmond": 0xFFEBCD, + "blue": 0x0000FF, + "blueviolet": 0x8A2BE2, + "brown": 0xA52A2A, + "burlywood": 0xDEB887, + "cadetblue": 0x5F9EA0, + "chartreuse": 0x7FFF00, + "chocolate": 0xD2691E, + "coral": 0xFF7F50, + "cornflowerblue": 0x6495ED, + "cornsilk": 0xFFF8DC, + "crimson": 0xDC143C, + "cyan": 0x00FFFF, + "darkblue": 0x00008B, + "darkcyan": 0x008B8B, + "darkgoldenrod": 0xB8860B, + "darkgray": 0xA9A9A9, + "darkgreen": 0x006400, + "darkgrey": 0xA9A9A9, + "darkkhaki": 0xBDB76B, + "darkmagenta": 0x8B008B, + "darkolivegreen": 0x556B2F, + "darkorange": 0xFF8C00, + "darkorchid": 0x9932CC, + "darkred": 0x8B0000, + "darksalmon": 0xE9967A, + "darkseagreen": 0x8FBC8F, + "darkslateblue": 0x483D8B, + "darkslategray": 0x2F4F4F, + "darkslategrey": 0x2F4F4F, + "darkturquoise": 0x00CED1, + "darkviolet": 0x9400D3, + "deeppink": 0xFF1493, + "deepskyblue": 0x00BFFF, + "dimgray": 0x696969, + "dimgrey": 0x696969, + "dodgerblue": 0x1E90FF, + "firebrick": 0xB22222, + "floralwhite": 0xFFFAF0, + "forestgreen": 0x228B22, + "fuchsia": 0xFF00FF, + "gainsboro": 0xDCDCDC, + "ghostwhite": 0xF8F8FF, + "goldenrod": 0xDAA520, + "gold": 0xFFD700, + "gray": 0x808080, + "green": 0x008000, + "greenyellow": 0xADFF2F, + "grey": 0x808080, + "honeydew": 0xF0FFF0, + "hotpink": 0xFF69B4, + "indianred": 0xCD5C5C, + "indigo": 0x4B0082, + "ivory": 0xFFFFF0, + "khaki": 0xF0E68C, + "lavenderblush": 0xFFF0F5, + "lavender": 0xE6E6FA, + "lawngreen": 0x7CFC00, + "lemonchiffon": 0xFFFACD, + "lightblue": 0xADD8E6, + "lightcoral": 0xF08080, + "lightcyan": 0xE0FFFF, + "lightgoldenrodyellow": 0xFAFAD2, + "lightgray": 0xD3D3D3, + "lightgreen": 0x90EE90, + "lightgrey": 0xD3D3D3, + "lightpink": 0xFFB6C1, + "lightsalmon": 0xFFA07A, + "lightseagreen": 0x20B2AA, + "lightskyblue": 0x87CEFA, + "lightslategray": 0x778899, + "lightslategrey": 0x778899, + "lightsteelblue": 0xB0C4DE, + "lightyellow": 0xFFFFE0, + "lime": 0x00FF00, + "limegreen": 0x32CD32, + "linen": 0xFAF0E6, + "magenta": 0xFF00FF, + "maroon": 0x800000, + "mediumaquamarine": 0x66CDAA, + "mediumblue": 0x0000CD, + "mediumorchid": 0xBA55D3, + "mediumpurple": 0x9370DB, + "mediumseagreen": 0x3CB371, + "mediumslateblue": 0x7B68EE, + "mediumspringgreen": 0x00FA9A, + "mediumturquoise": 0x48D1CC, + "mediumvioletred": 0xC71585, + "midnightblue": 0x191970, + "mintcream": 0xF5FFFA, + "mistyrose": 0xFFE4E1, + "moccasin": 0xFFE4B5, + "navajowhite": 0xFFDEAD, + "navy": 0x000080, + "oldlace": 0xFDF5E6, + "olive": 0x808000, + "olivedrab": 0x6B8E23, + "orange": 0xFFA500, + "orangered": 0xFF4500, + "orchid": 0xDA70D6, + "palegoldenrod": 0xEEE8AA, + "palegreen": 0x98FB98, + "paleturquoise": 0xAFEEEE, + "palevioletred": 0xDB7093, + "papayawhip": 0xFFEFD5, + "peachpuff": 0xFFDAB9, + "peru": 0xCD853F, + "pink": 0xFFC0CB, + "plum": 0xDDA0DD, + "powderblue": 0xB0E0E6, + "purple": 0x800080, + "rebeccapurple": 0x663399, + "red": 0xFF0000, + "rosybrown": 0xBC8F8F, + "royalblue": 0x4169E1, + "saddlebrown": 0x8B4513, + "salmon": 0xFA8072, + "sandybrown": 0xF4A460, + "seagreen": 0x2E8B57, + "seashell": 0xFFF5EE, + "sienna": 0xA0522D, + "silver": 0xC0C0C0, + "skyblue": 0x87CEEB, + "slateblue": 0x6A5ACD, + "slategray": 0x708090, + "slategrey": 0x708090, + "snow": 0xFFFAFA, + "springgreen": 0x00FF7F, + "steelblue": 0x4682B4, + "tan": 0xD2B48C, + "teal": 0x008080, + "thistle": 0xD8BFD8, + "tomato": 0xFF6347, + "turquoise": 0x40E0D0, + "violet": 0xEE82EE, + "wheat": 0xF5DEB3, + "white": 0xFFFFFF, + "whitesmoke": 0xF5F5F5, + "yellow": 0xFFFF00, + "yellowgreen": 0x9ACD32, +} + @schema_extractor("one_of") def color(value): if value == SCHEMA_EXTRACT: return ["hex color value", "color ID"] - return cv.Any(cv.int_, cv.use_id(ColorStruct))(value) + return cv.Any(cv.int_, cv.one_of(*COLOR_NAMES, lower=True), cv.use_id(ColorStruct))( + value + ) def color_retmapper(value): if isinstance(value, cv.Lambda): return cv.returning_lambda(value) + if isinstance(value, str) and value in COLOR_NAMES: + value = COLOR_NAMES[value] if isinstance(value, int): return literal( f"lv_color_make({(value >> 16) & 0xFF}, {(value >> 8) & 0xFF}, {value & 0xFF})" diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 9d157ea5b0..a3ed3047be 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -38,8 +38,8 @@ lvgl: border_width: 0 radius: 0 pad_all: 0 - border_color: 0x0077b3 - text_color: 0xFFFFFF + border_color: tomato + text_color: springgreen width: 100% height: 30 border_side: [left, top] diff --git a/tests/components/lvgl/test.host.yaml b/tests/components/lvgl/test.host.yaml new file mode 100644 index 0000000000..3a490bbe15 --- /dev/null +++ b/tests/components/lvgl/test.host.yaml @@ -0,0 +1,11 @@ +display: + - platform: sdl + auto_clear_enabled: false + dimensions: + width: 480 + height: 480 + +touchscreen: + - platform: sdl + +lvgl: From fb7e7eb80b6e6ee3c3b871831b1d3e910bceb274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Obrembski?= Date: Thu, 19 Sep 2024 06:17:22 +0200 Subject: [PATCH 0386/1052] Add tca9555 GPIO driver (#7146) Co-authored-by: Michal Obrembski Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/gpio_expander/__init__.py | 0 .../components/gpio_expander/cached_gpio.h | 38 +++++ esphome/components/tca9555/__init__.py | 72 +++++++++ esphome/components/tca9555/tca9555.cpp | 140 ++++++++++++++++++ esphome/components/tca9555/tca9555.h | 64 ++++++++ tests/components/tca9555/test.esp32-ard.yaml | 27 ++++ .../components/tca9555/test.esp32-c3-ard.yaml | 27 ++++ .../components/tca9555/test.esp32-c3-idf.yaml | 27 ++++ tests/components/tca9555/test.esp32-idf.yaml | 27 ++++ .../components/tca9555/test.esp8266-ard.yaml | 27 ++++ tests/components/tca9555/test.rp2040-ard.yaml | 27 ++++ 12 files changed, 477 insertions(+) create mode 100644 esphome/components/gpio_expander/__init__.py create mode 100644 esphome/components/gpio_expander/cached_gpio.h create mode 100644 esphome/components/tca9555/__init__.py create mode 100644 esphome/components/tca9555/tca9555.cpp create mode 100644 esphome/components/tca9555/tca9555.h create mode 100644 tests/components/tca9555/test.esp32-ard.yaml create mode 100644 tests/components/tca9555/test.esp32-c3-ard.yaml create mode 100644 tests/components/tca9555/test.esp32-c3-idf.yaml create mode 100644 tests/components/tca9555/test.esp32-idf.yaml create mode 100644 tests/components/tca9555/test.esp8266-ard.yaml create mode 100644 tests/components/tca9555/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index c95a94c509..a2fe77dc84 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -398,6 +398,7 @@ esphome/components/sun_gtil2/* @Mat931 esphome/components/switch/* @esphome/core esphome/components/t6615/* @tylermenezes esphome/components/tca9548a/* @andreashergert1984 +esphome/components/tca9555/* @mobrembski esphome/components/tcl112/* @glmnet esphome/components/tee501/* @Stock-M esphome/components/teleinfo/* @0hax diff --git a/esphome/components/gpio_expander/__init__.py b/esphome/components/gpio_expander/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/gpio_expander/cached_gpio.h b/esphome/components/gpio_expander/cached_gpio.h new file mode 100644 index 0000000000..784c5f0f4a --- /dev/null +++ b/esphome/components/gpio_expander/cached_gpio.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include "esphome/core/hal.h" + +namespace esphome { +namespace gpio_expander { + +/// @brief A class to cache the read state of a GPIO expander. +template class CachedGpioExpander { + public: + bool digital_read(T pin) { + if (!this->read_cache_invalidated_[pin]) { + this->read_cache_invalidated_[pin] = true; + return this->digital_read_cache(pin); + } + return this->digital_read_hw(pin); + } + + void digital_write(T pin, bool value) { this->digital_write_hw(pin, value); } + + protected: + virtual bool digital_read_hw(T pin) = 0; + virtual bool digital_read_cache(T pin) = 0; + virtual void digital_write_hw(T pin, bool value) = 0; + + void reset_pin_cache_() { + for (T i = 0; i < N; i++) { + this->read_cache_invalidated_[i] = false; + } + } + + std::array read_cache_invalidated_{}; +}; + +} // namespace gpio_expander +} // namespace esphome diff --git a/esphome/components/tca9555/__init__.py b/esphome/components/tca9555/__init__.py new file mode 100644 index 0000000000..db0451d4e6 --- /dev/null +++ b/esphome/components/tca9555/__init__.py @@ -0,0 +1,72 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components import i2c +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + CONF_INPUT, + CONF_INVERTED, + CONF_MODE, + CONF_NUMBER, + CONF_OUTPUT, +) + +CODEOWNERS = ["@mobrembski"] + +AUTO_LOAD = ["gpio_expander"] +DEPENDENCIES = ["i2c"] +MULTI_CONF = True + +tca9555_ns = cg.esphome_ns.namespace("tca9555") + +TCA9555Component = tca9555_ns.class_("TCA9555Component", cg.Component, i2c.I2CDevice) +TCA9555GPIOPin = tca9555_ns.class_("TCA9555GPIOPin", cg.GPIOPin) + +CONF_TCA9555 = "tca9555" +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(TCA9555Component), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x21)) +) + + +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) + + +def validate_mode(value): + if not (value[CONF_INPUT] or value[CONF_OUTPUT]): + raise cv.Invalid("Mode must be either input or output") + if value[CONF_INPUT] and value[CONF_OUTPUT]: + raise cv.Invalid("Mode must be either input or output") + return value + + +TCA9555_PIN_SCHEMA = pins.gpio_base_schema( + TCA9555GPIOPin, + cv.int_range(min=0, max=15), + modes=[CONF_INPUT, CONF_OUTPUT], + mode_validator=validate_mode, + invertable=True, +).extend( + { + cv.Required(CONF_TCA9555): cv.use_id(TCA9555Component), + } +) + + +@pins.PIN_SCHEMA_REGISTRY.register(CONF_TCA9555, TCA9555_PIN_SCHEMA) +async def tca9555_pin_to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_parented(var, config[CONF_TCA9555]) + + cg.add(var.set_pin(config[CONF_NUMBER])) + cg.add(var.set_inverted(config[CONF_INVERTED])) + cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE]))) + return var diff --git a/esphome/components/tca9555/tca9555.cpp b/esphome/components/tca9555/tca9555.cpp new file mode 100644 index 0000000000..cf0894427f --- /dev/null +++ b/esphome/components/tca9555/tca9555.cpp @@ -0,0 +1,140 @@ +#include "tca9555.h" +#include "esphome/core/log.h" + +static const uint8_t TCA9555_INPUT_PORT_REGISTER_0 = 0x00; +static const uint8_t TCA9555_INPUT_PORT_REGISTER_1 = 0x01; +static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_0 = 0x02; +static const uint8_t TCA9555_OUTPUT_PORT_REGISTER_1 = 0x03; +static const uint8_t TCA9555_POLARITY_REGISTER_0 = 0x04; +static const uint8_t TCA9555_POLARITY_REGISTER_1 = 0x05; +static const uint8_t TCA9555_CONFIGURATION_PORT_0 = 0x06; +static const uint8_t TCA9555_CONFIGURATION_PORT_1 = 0x07; + +namespace esphome { +namespace tca9555 { + +static const char *const TAG = "tca9555"; + +void TCA9555Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up TCA9555..."); + if (!this->read_gpio_modes_()) { + this->mark_failed(); + return; + } + if (!this->read_gpio_outputs_()) { + this->mark_failed(); + return; + } +} +void TCA9555Component::dump_config() { + ESP_LOGCONFIG(TAG, "TCA9555:"); + LOG_I2C_DEVICE(this) + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with TCA9555 failed!"); + } +} +void TCA9555Component::pin_mode(uint8_t pin, gpio::Flags flags) { + if (flags == gpio::FLAG_INPUT) { + // Set mode mask bit + this->mode_mask_ |= 1 << pin; + } else if (flags == gpio::FLAG_OUTPUT) { + // Clear mode mask bit + this->mode_mask_ &= ~(1 << pin); + } + // Write GPIO to enable input mode + this->write_gpio_modes_(); +} +void TCA9555Component::loop() { this->reset_pin_cache_(); } + +bool TCA9555Component::read_gpio_outputs_() { + if (this->is_failed()) + return false; + uint8_t data[2]; + if (!this->read_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) { + this->status_set_warning("Failed to read output register"); + return false; + } + this->output_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0); + this->status_clear_warning(); + return true; +} + +bool TCA9555Component::read_gpio_modes_() { + if (this->is_failed()) + return false; + uint8_t data[2]; + bool success = this->read_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2); + if (!success) { + this->status_set_warning("Failed to read mode register"); + return false; + } + this->mode_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0); + + this->status_clear_warning(); + return true; +} +bool TCA9555Component::digital_read_hw(uint8_t pin) { + if (this->is_failed()) + return false; + bool success; + uint8_t data[2]; + success = this->read_bytes(TCA9555_INPUT_PORT_REGISTER_0, data, 2); + this->input_mask_ = (uint16_t(data[1]) << 8) | (uint16_t(data[0]) << 0); + + if (!success) { + this->status_set_warning("Failed to read input register"); + return false; + } + + this->status_clear_warning(); + return true; +} + +void TCA9555Component::digital_write_hw(uint8_t pin, bool value) { + if (this->is_failed()) + return; + + if (value) { + this->output_mask_ |= (1 << pin); + } else { + this->output_mask_ &= ~(1 << pin); + } + + uint8_t data[2]; + data[0] = this->output_mask_; + data[1] = this->output_mask_ >> 8; + if (!this->write_bytes(TCA9555_OUTPUT_PORT_REGISTER_0, data, 2)) { + this->status_set_warning("Failed to write output register"); + return; + } + + this->status_clear_warning(); +} + +bool TCA9555Component::write_gpio_modes_() { + if (this->is_failed()) + return false; + uint8_t data[2]; + + data[0] = this->mode_mask_; + data[1] = this->mode_mask_ >> 8; + if (!this->write_bytes(TCA9555_CONFIGURATION_PORT_0, data, 2)) { + this->status_set_warning("Failed to write mode register"); + return false; + } + this->status_clear_warning(); + return true; +} + +bool TCA9555Component::digital_read_cache(uint8_t pin) { return this->input_mask_ & (1 << pin); } + +float TCA9555Component::get_setup_priority() const { return setup_priority::IO; } + +void TCA9555GPIOPin::setup() { this->pin_mode(this->flags_); } +void TCA9555GPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } +bool TCA9555GPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } +void TCA9555GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +std::string TCA9555GPIOPin::dump_summary() const { return str_sprintf("%u via TCA9555", this->pin_); } + +} // namespace tca9555 +} // namespace esphome diff --git a/esphome/components/tca9555/tca9555.h b/esphome/components/tca9555/tca9555.h new file mode 100644 index 0000000000..ea464db043 --- /dev/null +++ b/esphome/components/tca9555/tca9555.h @@ -0,0 +1,64 @@ +#pragma once + +#include "esphome/components/gpio_expander/cached_gpio.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace tca9555 { + +class TCA9555Component : public Component, + public i2c::I2CDevice, + public gpio_expander::CachedGpioExpander { + public: + TCA9555Component() = default; + + /// Check i2c availability and setup masks + void setup() override; + void pin_mode(uint8_t pin, gpio::Flags flags); + + float get_setup_priority() const override; + + void dump_config() override; + + void loop() override; + + protected: + bool digital_read_hw(uint8_t pin) override; + bool digital_read_cache(uint8_t pin) override; + void digital_write_hw(uint8_t pin, bool value) override; + + /// Mask for the pin mode - 1 means output, 0 means input + uint16_t mode_mask_{0x00}; + /// The mask to write as output state - 1 means HIGH, 0 means LOW + uint16_t output_mask_{0x00}; + /// The state read in digital_read_hw - 1 means HIGH, 0 means LOW + uint16_t input_mask_{0x00}; + + bool read_gpio_modes_(); + bool write_gpio_modes_(); + bool read_gpio_outputs_(); +}; + +/// Helper class to expose a TCA9555 pin as an internal input GPIO pin. +class TCA9555GPIOPin : public GPIOPin, public Parented { + public: + void setup() override; + void pin_mode(gpio::Flags flags) override; + bool digital_read() override; + void digital_write(bool value) override; + std::string dump_summary() const override; + + void set_pin(uint8_t pin) { this->pin_ = pin; } + void set_inverted(bool inverted) { this->inverted_ = inverted; } + void set_flags(gpio::Flags flags) { this->flags_ = flags; } + + protected: + uint8_t pin_; + bool inverted_; + gpio::Flags flags_; +}; + +} // namespace tca9555 +} // namespace esphome diff --git a/tests/components/tca9555/test.esp32-ard.yaml b/tests/components/tca9555/test.esp32-ard.yaml new file mode 100644 index 0000000000..e0c046b443 --- /dev/null +++ b/tests/components/tca9555/test.esp32-ard.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_tca9555 + scl: 16 + sda: 17 + +tca9555: + - id: tca9555_hub + address: 0x21 + +binary_sensor: + - platform: gpio + id: tca9555_binary_sensor + name: TCA9555 Binary Sensor + pin: + tca9555: tca9555_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: tca9555_output + pin: + tca9555: tca9555_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/tca9555/test.esp32-c3-ard.yaml b/tests/components/tca9555/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..5c49b27640 --- /dev/null +++ b/tests/components/tca9555/test.esp32-c3-ard.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_tca9555 + scl: 5 + sda: 4 + +tca9555: + - id: tca9555_hub + address: 0x21 + +binary_sensor: + - platform: gpio + id: tca9555_binary_sensor + name: TCA9555 Binary Sensor + pin: + tca9555: tca9555_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: tca9555_output + pin: + tca9555: tca9555_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/tca9555/test.esp32-c3-idf.yaml b/tests/components/tca9555/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5c49b27640 --- /dev/null +++ b/tests/components/tca9555/test.esp32-c3-idf.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_tca9555 + scl: 5 + sda: 4 + +tca9555: + - id: tca9555_hub + address: 0x21 + +binary_sensor: + - platform: gpio + id: tca9555_binary_sensor + name: TCA9555 Binary Sensor + pin: + tca9555: tca9555_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: tca9555_output + pin: + tca9555: tca9555_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/tca9555/test.esp32-idf.yaml b/tests/components/tca9555/test.esp32-idf.yaml new file mode 100644 index 0000000000..e0c046b443 --- /dev/null +++ b/tests/components/tca9555/test.esp32-idf.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_tca9555 + scl: 16 + sda: 17 + +tca9555: + - id: tca9555_hub + address: 0x21 + +binary_sensor: + - platform: gpio + id: tca9555_binary_sensor + name: TCA9555 Binary Sensor + pin: + tca9555: tca9555_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: tca9555_output + pin: + tca9555: tca9555_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/tca9555/test.esp8266-ard.yaml b/tests/components/tca9555/test.esp8266-ard.yaml new file mode 100644 index 0000000000..5c49b27640 --- /dev/null +++ b/tests/components/tca9555/test.esp8266-ard.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_tca9555 + scl: 5 + sda: 4 + +tca9555: + - id: tca9555_hub + address: 0x21 + +binary_sensor: + - platform: gpio + id: tca9555_binary_sensor + name: TCA9555 Binary Sensor + pin: + tca9555: tca9555_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: tca9555_output + pin: + tca9555: tca9555_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/tca9555/test.rp2040-ard.yaml b/tests/components/tca9555/test.rp2040-ard.yaml new file mode 100644 index 0000000000..5c49b27640 --- /dev/null +++ b/tests/components/tca9555/test.rp2040-ard.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_tca9555 + scl: 5 + sda: 4 + +tca9555: + - id: tca9555_hub + address: 0x21 + +binary_sensor: + - platform: gpio + id: tca9555_binary_sensor + name: TCA9555 Binary Sensor + pin: + tca9555: tca9555_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: tca9555_output + pin: + tca9555: tca9555_hub + number: 0 + mode: OUTPUT + inverted: false From 8e5d7337c8e9490c901cd17f59b72ed300f775b1 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:18:51 +1000 Subject: [PATCH 0387/1052] [st7701s] Fix initialisation race (#7462) --- esphome/components/st7701s/st7701s.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 7248bc044e..403bff789d 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -9,14 +9,6 @@ void ST7701S::setup() { esph_log_config(TAG, "Setting up ST7701S"); this->spi_setup(); this->write_init_sequence_(); -} - -// called after a delay after writing the init sequence -void ST7701S::complete_setup_() { - this->write_command_(SLEEP_OUT); - this->write_command_(DISPLAY_ON); - this->spi_teardown(); // SPI not needed after this - delay(10); esp_lcd_rgb_panel_config_t config{}; config.flags.fb_in_psram = 1; @@ -179,7 +171,12 @@ void ST7701S::write_init_sequence_() { this->write_data_(val); ESP_LOGD(TAG, "write MADCTL %X", val); this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF); - this->set_timeout(120, [this] { this->complete_setup_(); }); + // can't avoid this inline delay due to the need to complete setup before anything else tries to draw. + delay(120); // NOLINT + this->write_command_(SLEEP_OUT); + this->write_command_(DISPLAY_ON); + this->spi_teardown(); // SPI not needed after this + delay(10); } void ST7701S::dump_config() { From 5f7bde2a2cb7d66023f4cdb3687248458f1666e0 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 22 Sep 2024 14:44:53 -0500 Subject: [PATCH 0388/1052] Copy active wake words to message (#7481) --- esphome/components/api/api_connection.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 7ea52e9a9e..e28b244722 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1242,6 +1242,9 @@ VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configura } resp.available_wake_words.push_back(std::move(resp_wake_word)); } + for (auto &wake_word_id : config.active_wake_words) { + resp.active_wake_words.push_back(wake_word_id); + } resp.max_active_wake_words = config.max_active_wake_words; } return resp; From c2876739474e0e6a5f226c29339856d6040e13b1 Mon Sep 17 00:00:00 2001 From: Tarik2142 <31830530+Tarik2142@users.noreply.github.com> Date: Mon, 23 Sep 2024 00:35:57 +0300 Subject: [PATCH 0389/1052] add "fan_mode" and "swing_mode" to REST API (#7476) --- esphome/components/web_server/web_server.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 1bb7c6c249..3bb7eee8f1 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1012,6 +1012,16 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url call.set_mode(mode.c_str()); } + if (request->hasParam("fan_mode")) { + auto mode = request->getParam("fan_mode")->value(); + call.set_fan_mode(mode.c_str()); + } + + if (request->hasParam("swing_mode")) { + auto mode = request->getParam("swing_mode")->value(); + call.set_swing_mode(mode.c_str()); + } + if (request->hasParam("target_temperature_high")) { auto target_temperature_high = parse_number(request->getParam("target_temperature_high")->value().c_str()); if (target_temperature_high.has_value()) From 66f9597d9e977410802a2dab0ed0743e9f776d2f Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Sun, 22 Sep 2024 14:44:53 -0500 Subject: [PATCH 0390/1052] Copy active wake words to message (#7481) --- esphome/components/api/api_connection.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 7ea52e9a9e..e28b244722 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1242,6 +1242,9 @@ VoiceAssistantConfigurationResponse APIConnection::voice_assistant_get_configura } resp.available_wake_words.push_back(std::move(resp_wake_word)); } + for (auto &wake_word_id : config.active_wake_words) { + resp.active_wake_words.push_back(wake_word_id); + } resp.max_active_wake_words = config.max_active_wake_words; } return resp; From f314ad8a5bf26711683c20bdff75824cfddf807a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:40:47 +1200 Subject: [PATCH 0391/1052] Bump version to 2024.9.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 8d5996a548..29084c8955 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.0" +__version__ = "2024.9.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 402a6a9edb6f50a1e7199392160326205d23ac4f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 22 Sep 2024 23:54:31 -0500 Subject: [PATCH 0392/1052] [esp32_improv] Add triggers for various states (#7461) Co-authored-by: NP v/d Spek --- esphome/components/esp32_improv/__init__.py | 83 ++++++++++++++++++- esphome/components/esp32_improv/automation.h | 72 ++++++++++++++++ .../esp32_improv/esp32_improv_component.cpp | 18 ++-- .../esp32_improv/esp32_improv_component.h | 15 ++++ esphome/core/defines.h | 5 +- 5 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 esphome/components/esp32_improv/automation.h diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 705dff0f1b..ecc07d4c91 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,7 +1,8 @@ +from esphome import automation import esphome.codegen as cg from esphome.components import binary_sensor, esp32_ble_server, output import esphome.config_validation as cv -from esphome.const import CONF_ID +from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID AUTO_LOAD = ["esp32_ble_server"] CODEOWNERS = ["@jesserockz"] @@ -11,13 +12,36 @@ CONF_AUTHORIZED_DURATION = "authorized_duration" CONF_AUTHORIZER = "authorizer" CONF_BLE_SERVER_ID = "ble_server_id" CONF_IDENTIFY_DURATION = "identify_duration" +CONF_ON_PROVISIONED = "on_provisioned" +CONF_ON_PROVISIONING = "on_provisioning" +CONF_ON_START = "on_start" +CONF_ON_STOP = "on_stop" CONF_STATUS_INDICATOR = "status_indicator" CONF_WIFI_TIMEOUT = "wifi_timeout" +improv_ns = cg.esphome_ns.namespace("improv") +Error = improv_ns.enum("Error") +State = improv_ns.enum("State") + esp32_improv_ns = cg.esphome_ns.namespace("esp32_improv") ESP32ImprovComponent = esp32_improv_ns.class_( "ESP32ImprovComponent", cg.Component, esp32_ble_server.BLEServiceComponent ) +ESP32ImprovProvisionedTrigger = esp32_improv_ns.class_( + "ESP32ImprovProvisionedTrigger", automation.Trigger.template() +) +ESP32ImprovProvisioningTrigger = esp32_improv_ns.class_( + "ESP32ImprovProvisioningTrigger", automation.Trigger.template() +) +ESP32ImprovStartTrigger = esp32_improv_ns.class_( + "ESP32ImprovStartTrigger", automation.Trigger.template() +) +ESP32ImprovStateTrigger = esp32_improv_ns.class_( + "ESP32ImprovStateTrigger", automation.Trigger.template() +) +ESP32ImprovStoppedTrigger = esp32_improv_ns.class_( + "ESP32ImprovStoppedTrigger", automation.Trigger.template() +) CONFIG_SCHEMA = cv.Schema( @@ -37,6 +61,37 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional( CONF_WIFI_TIMEOUT, default="1min" ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_ON_PROVISIONED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovProvisionedTrigger + ), + } + ), + cv.Optional(CONF_ON_PROVISIONING): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovProvisioningTrigger + ), + } + ), + cv.Optional(CONF_ON_START): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStartTrigger), + } + ), + cv.Optional(CONF_ON_STATE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32ImprovStateTrigger), + } + ), + cv.Optional(CONF_ON_STOP): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + ESP32ImprovStoppedTrigger + ), + } + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -63,3 +118,29 @@ async def to_code(config): if CONF_STATUS_INDICATOR in config: status_indicator = await cg.get_variable(config[CONF_STATUS_INDICATOR]) cg.add(var.set_status_indicator(status_indicator)) + + use_state_callback = False + for conf in config.get(CONF_ON_PROVISIONED, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_PROVISIONING, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_START, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_STATE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(State, "state"), (Error, "error")], conf + ) + use_state_callback = True + for conf in config.get(CONF_ON_STOP, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + if use_state_callback: + cg.add_define("USE_ESP32_IMPROV_STATE_CALLBACK") diff --git a/esphome/components/esp32_improv/automation.h b/esphome/components/esp32_improv/automation.h new file mode 100644 index 0000000000..52c5da125b --- /dev/null +++ b/esphome/components/esp32_improv/automation.h @@ -0,0 +1,72 @@ +#pragma once +#ifdef USE_ESP32 +#ifdef USE_ESP32_IMPROV_STATE_CALLBACK +#include "esp32_improv_component.h" + +#include "esphome/core/automation.h" + +#include + +namespace esphome { +namespace esp32_improv { + +class ESP32ImprovProvisionedTrigger : public Trigger<> { + public: + explicit ESP32ImprovProvisionedTrigger(ESP32ImprovComponent *parent) { + parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) { + if (state == improv::STATE_PROVISIONED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class ESP32ImprovProvisioningTrigger : public Trigger<> { + public: + explicit ESP32ImprovProvisioningTrigger(ESP32ImprovComponent *parent) { + parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) { + if (state == improv::STATE_PROVISIONING && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class ESP32ImprovStartTrigger : public Trigger<> { + public: + explicit ESP32ImprovStartTrigger(ESP32ImprovComponent *parent) { + parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) { + if ((state == improv::STATE_AUTHORIZED || state == improv::STATE_AWAITING_AUTHORIZATION) && + !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class ESP32ImprovStateTrigger : public Trigger { + public: + explicit ESP32ImprovStateTrigger(ESP32ImprovComponent *parent) { + parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) { + if (!parent->is_failed()) { + trigger(state, error); + } + }); + } +}; + +class ESP32ImprovStoppedTrigger : public Trigger<> { + public: + explicit ESP32ImprovStoppedTrigger(ESP32ImprovComponent *parent) { + parent->add_on_state_callback([this, parent](improv::State state, improv::Error error) { + if (state == improv::STATE_STOPPED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +} // namespace esp32_improv +} // namespace esphome +#endif +#endif diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index d90eaac3b6..d36b50feb0 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -68,7 +68,12 @@ void ESP32ImprovComponent::setup_characteristics() { void ESP32ImprovComponent::loop() { if (!global_ble_server->is_running()) { - this->state_ = improv::STATE_STOPPED; + if (this->state_ != improv::STATE_STOPPED) { + this->state_ = improv::STATE_STOPPED; +#ifdef USE_ESP32_IMPROV_STATE_CALLBACK + this->state_callback_.call(this->state_, this->error_state_); +#endif + } this->incoming_data_.clear(); return; } @@ -217,6 +222,9 @@ void ESP32ImprovComponent::set_state_(improv::State state) { service_data[7] = 0x00; // Reserved esp32_ble::global_ble->advertising_set_service_data(service_data); +#ifdef USE_ESP32_IMPROV_STATE_CALLBACK + this->state_callback_.call(this->state_, this->error_state_); +#endif } void ESP32ImprovComponent::set_error_(improv::Error error) { @@ -270,7 +278,7 @@ void ESP32ImprovComponent::dump_config() { void ESP32ImprovComponent::process_incoming_data_() { uint8_t length = this->incoming_data_[1]; - ESP_LOGD(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str()); + ESP_LOGV(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str()); if (this->incoming_data_.size() - 3 == length) { this->set_error_(improv::ERROR_NONE); improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_); @@ -295,7 +303,7 @@ void ESP32ImprovComponent::process_incoming_data_() { wifi::global_wifi_component->set_sta(sta); wifi::global_wifi_component->start_connecting(sta, false); this->set_state_(improv::STATE_PROVISIONING); - ESP_LOGD(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(), + ESP_LOGD(TAG, "Received Improv Wi-Fi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(), command.password.c_str()); auto f = std::bind(&ESP32ImprovComponent::on_wifi_connect_timeout_, this); @@ -313,7 +321,7 @@ void ESP32ImprovComponent::process_incoming_data_() { this->incoming_data_.clear(); } } else if (this->incoming_data_.size() - 2 > length) { - ESP_LOGV(TAG, "Too much data came in, or 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..."); @@ -327,7 +335,7 @@ void ESP32ImprovComponent::on_wifi_connect_timeout_() { if (this->authorizer_ != nullptr) this->authorized_start_ = millis(); #endif - ESP_LOGW(TAG, "Timed out trying to connect to given WiFi network"); + ESP_LOGW(TAG, "Timed out while connecting to Wi-Fi network"); wifi::global_wifi_component->clear_sta(); } diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 3ed377a6ad..062b3f585b 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -9,6 +9,10 @@ #include "esphome/components/esp32_ble_server/ble_server.h" #include "esphome/components/wifi/wifi_component.h" +#ifdef USE_ESP32_IMPROV_STATE_CALLBACK +#include "esphome/core/automation.h" +#endif + #ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" #endif @@ -42,6 +46,11 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent { void stop() override; bool is_active() const { return this->state_ != improv::STATE_STOPPED; } +#ifdef USE_ESP32_IMPROV_STATE_CALLBACK + void add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); + } +#endif #ifdef USE_BINARY_SENSOR void set_authorizer(binary_sensor::BinarySensor *authorizer) { this->authorizer_ = authorizer; } #endif @@ -54,6 +63,9 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent { void set_wifi_timeout(uint32_t wifi_timeout) { this->wifi_timeout_ = wifi_timeout; } uint32_t get_wifi_timeout() const { return this->wifi_timeout_; } + improv::State get_improv_state() const { return this->state_; } + improv::Error get_improv_error_state() const { return this->error_state_; } + protected: bool should_start_{false}; bool setup_complete_{false}; @@ -84,6 +96,9 @@ class ESP32ImprovComponent : public Component, public BLEServiceComponent { improv::State state_{improv::STATE_STOPPED}; improv::Error error_state_{improv::ERROR_NONE}; +#ifdef USE_ESP32_IMPROV_STATE_CALLBACK + CallbackManager state_callback_{}; +#endif bool status_indicator_state_{false}; void set_status_indicator_state_(bool state); diff --git a/esphome/core/defines.h b/esphome/core/defines.h index ffd5cc6f1b..bf676107c7 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -29,6 +29,7 @@ #define USE_DATETIME_TIME #define USE_DEEP_SLEEP #define USE_DISPLAY +#define USE_ESP32_IMPROV_STATE_CALLBACK #define USE_EVENT #define USE_FAN #define USE_GRAPH @@ -45,10 +46,10 @@ #define USE_LVGL_BUTTONMATRIX #define USE_LVGL_FONT #define USE_LVGL_IMAGE -#define USE_LVGL_KEYBOARD #define USE_LVGL_KEY_LISTENER -#define USE_LVGL_TOUCHSCREEN +#define USE_LVGL_KEYBOARD #define USE_LVGL_ROTARY_ENCODER +#define USE_LVGL_TOUCHSCREEN #define USE_MDNS #define USE_MEDIA_PLAYER #define USE_MQTT From 2ff863deb3ed699d3ef24c8f0298dfe063fee42b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 23 Sep 2024 01:35:26 -0500 Subject: [PATCH 0393/1052] [micro_wake_word] Workaround for failing IDF 5+ tests (#7484) --- esphome/components/micro_wake_word/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/esphome/components/micro_wake_word/__init__.py b/esphome/components/micro_wake_word/__init__.py index a8aa590951..0862406e46 100644 --- a/esphome/components/micro_wake_word/__init__.py +++ b/esphome/components/micro_wake_word/__init__.py @@ -419,6 +419,13 @@ async def to_code(config): repo="https://github.com/espressif/esp-tflite-micro", ref="v1.3.1", ) + # add esp-nn dependency for tflite-micro to work around https://github.com/espressif/esp-nn/issues/17 + # ...remove after switching to IDF 5.1.4+ + esp32.add_idf_component( + name="esp-nn", + repo="https://github.com/espressif/esp-nn", + ref="v1.1.0", + ) cg.add_build_flag("-DTF_LITE_STATIC_MEMORY") cg.add_build_flag("-DTF_LITE_DISABLE_X86_NEON") From 4ece4a389e009ef2230b07e4ae9b1fdc18a0a148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2024 21:31:38 +0200 Subject: [PATCH 0394/1052] Bump peter-evans/create-pull-request from 7.0.3 to 7.0.5 (#7469) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index eeb8386e74..c066ae9fb4 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -36,7 +36,7 @@ jobs: python ./script/sync-device_class.py - name: Commit changes - uses: peter-evans/create-pull-request@v7.0.3 + uses: peter-evans/create-pull-request@v7.0.5 with: commit-message: "Synchronise Device Classes from Home Assistant" committer: esphomebot From cc53eb42b273631b14ddf3d0a0308372a58043e7 Mon Sep 17 00:00:00 2001 From: Nick Kinnan Date: Mon, 23 Sep 2024 20:53:13 -0700 Subject: [PATCH 0395/1052] Add CSE7766 reactive power (#7301) --- esphome/components/cse7766/cse7766.cpp | 11 +++++++++++ esphome/components/cse7766/cse7766.h | 4 ++++ esphome/components/cse7766/sensor.py | 12 ++++++++++++ 3 files changed, 27 insertions(+) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index f1420aa127..47058badce 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -147,6 +147,7 @@ void CSE7766Component::parse_data_() { float power = 0.0f; if (power_cycle_exceeds_range) { // Datasheet: power cycle exceeding range means active power is 0 + have_power = true; if (this->power_sensor_ != nullptr) { this->power_sensor_->publish_state(0.0f); } @@ -178,6 +179,15 @@ void CSE7766Component::parse_data_() { if (this->apparent_power_sensor_ != nullptr) { this->apparent_power_sensor_->publish_state(apparent_power); } + if (have_power && this->reactive_power_sensor_ != nullptr) { + const float reactive_power = apparent_power - power; + if (reactive_power < 0.0f) { + ESP_LOGD(TAG, "Impossible reactive power: %.4f is negative", reactive_power); + this->reactive_power_sensor_->publish_state(0.0f); + } else { + this->reactive_power_sensor_->publish_state(reactive_power); + } + } if (this->power_factor_sensor_ != nullptr && (have_power || power_cycle_exceeds_range)) { float pf = NAN; if (apparent_power > 0) { @@ -232,6 +242,7 @@ void CSE7766Component::dump_config() { LOG_SENSOR(" ", "Power", this->power_sensor_); LOG_SENSOR(" ", "Energy", this->energy_sensor_); LOG_SENSOR(" ", "Apparent Power", this->apparent_power_sensor_); + LOG_SENSOR(" ", "Reactive Power", this->reactive_power_sensor_); LOG_SENSOR(" ", "Power Factor", this->power_factor_sensor_); this->check_uart_settings(4800); } diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index 0b724d6bbb..5d89b3b75b 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -16,6 +16,9 @@ class CSE7766Component : public Component, public uart::UARTDevice { void set_apparent_power_sensor(sensor::Sensor *apparent_power_sensor) { apparent_power_sensor_ = apparent_power_sensor; } + void set_reactive_power_sensor(sensor::Sensor *reactive_power_sensor) { + reactive_power_sensor_ = reactive_power_sensor; + } void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; } void loop() override; @@ -35,6 +38,7 @@ class CSE7766Component : public Component, public uart::UARTDevice { sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *energy_sensor_{nullptr}; sensor::Sensor *apparent_power_sensor_{nullptr}; + sensor::Sensor *reactive_power_sensor_{nullptr}; sensor::Sensor *power_factor_sensor_{nullptr}; uint32_t cf_pulses_total_{0}; uint16_t cf_pulses_last_{0}; diff --git a/esphome/components/cse7766/sensor.py b/esphome/components/cse7766/sensor.py index b64dcf7de3..ecb59c4b5f 100644 --- a/esphome/components/cse7766/sensor.py +++ b/esphome/components/cse7766/sensor.py @@ -8,18 +8,21 @@ from esphome.const import ( CONF_ID, CONF_POWER, CONF_POWER_FACTOR, + CONF_REACTIVE_POWER, CONF_VOLTAGE, DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_POWER_FACTOR, + DEVICE_CLASS_REACTIVE_POWER, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, STATE_CLASS_TOTAL_INCREASING, UNIT_AMPERE, UNIT_VOLT, UNIT_VOLT_AMPS, + UNIT_VOLT_AMPS_REACTIVE, UNIT_WATT, UNIT_WATT_HOURS, ) @@ -62,6 +65,12 @@ CONFIG_SCHEMA = cv.Schema( device_class=DEVICE_CLASS_APPARENT_POWER, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema( + unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE, + accuracy_decimals=1, + device_class=DEVICE_CLASS_REACTIVE_POWER, + state_class=STATE_CLASS_MEASUREMENT, + ), cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema( accuracy_decimals=2, device_class=DEVICE_CLASS_POWER_FACTOR, @@ -94,6 +103,9 @@ async def to_code(config): if apparent_power_config := config.get(CONF_APPARENT_POWER): sens = await sensor.new_sensor(apparent_power_config) cg.add(var.set_apparent_power_sensor(sens)) + if reactive_power_config := config.get(CONF_REACTIVE_POWER): + sens = await sensor.new_sensor(reactive_power_config) + cg.add(var.set_reactive_power_sensor(sens)) if power_factor_config := config.get(CONF_POWER_FACTOR): sens = await sensor.new_sensor(power_factor_config) cg.add(var.set_power_factor_sensor(sens)) From 294fe8d9708bfb904824f38c7619ccd673905dc3 Mon Sep 17 00:00:00 2001 From: David Sichau Date: Wed, 25 Sep 2024 02:50:01 +0200 Subject: [PATCH 0396/1052] Support inkplate 5 and 5 V2 (#7448) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/inkplate6/display.py | 2 ++ esphome/components/inkplate6/inkplate.h | 32 +++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/esphome/components/inkplate6/display.py b/esphome/components/inkplate6/display.py index 58a146d2fd..8fe7f7d41d 100644 --- a/esphome/components/inkplate6/display.py +++ b/esphome/components/inkplate6/display.py @@ -53,6 +53,8 @@ MODELS = { "inkplate_10": InkplateModel.INKPLATE_10, "inkplate_6_plus": InkplateModel.INKPLATE_6_PLUS, "inkplate_6_v2": InkplateModel.INKPLATE_6_V2, + "inkplate_5": InkplateModel.INKPLATE_5, + "inkplate_5_v2": InkplateModel.INKPLATE_5_V2, } CONFIG_SCHEMA = cv.All( diff --git a/esphome/components/inkplate6/inkplate.h b/esphome/components/inkplate6/inkplate.h index 2946c89e1c..ca2ad46f1e 100644 --- a/esphome/components/inkplate6/inkplate.h +++ b/esphome/components/inkplate6/inkplate.h @@ -15,6 +15,8 @@ enum InkplateModel : uint8_t { INKPLATE_10 = 1, INKPLATE_6_PLUS = 2, INKPLATE_6_V2 = 3, + INKPLATE_5 = 4, + INKPLATE_5_V2 = 5, }; class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { @@ -29,7 +31,7 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { const uint8_t pixelMaskLUT[8] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80}; const uint8_t pixelMaskGLUT[2] = {0x0F, 0xF0}; - const uint8_t waveform3BitAll[4][8][9] = {// INKPLATE_6 + const uint8_t waveform3BitAll[6][8][9] = {// INKPLATE_6 {{0, 1, 1, 0, 0, 1, 1, 0, 0}, {0, 1, 2, 1, 1, 2, 1, 0, 0}, {1, 1, 1, 2, 2, 1, 0, 0, 0}, @@ -64,7 +66,25 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { {1, 1, 1, 1, 2, 2, 1, 0, 0}, {0, 1, 1, 1, 2, 2, 1, 0, 0}, {0, 0, 0, 0, 1, 1, 2, 0, 0}, - {0, 0, 0, 0, 0, 1, 2, 0, 0}}}; + {0, 0, 0, 0, 0, 1, 2, 0, 0}}, + // INKPLATE_5 + {{0, 0, 1, 1, 0, 1, 1, 1, 0}, + {0, 1, 1, 1, 1, 2, 0, 1, 0}, + {1, 2, 2, 0, 2, 1, 1, 1, 0}, + {1, 1, 1, 2, 0, 1, 1, 2, 0}, + {0, 1, 1, 1, 2, 0, 1, 2, 0}, + {0, 0, 0, 1, 1, 2, 1, 2, 0}, + {1, 1, 1, 2, 0, 2, 1, 2, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}}, + // INKPLATE_5_V2 + {{0, 0, 1, 1, 2, 1, 1, 1, 0}, + {1, 1, 2, 2, 1, 2, 1, 1, 0}, + {0, 1, 2, 2, 1, 1, 2, 1, 0}, + {0, 0, 1, 1, 1, 1, 1, 2, 0}, + {1, 2, 1, 2, 1, 1, 1, 2, 0}, + {0, 1, 1, 1, 2, 0, 1, 2, 0}, + {1, 1, 1, 2, 2, 2, 1, 2, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}}}; void set_greyscale(bool greyscale) { this->greyscale_ = greyscale; @@ -146,6 +166,10 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { return 800; } else if (this->model_ == INKPLATE_10) { return 1200; + } else if (this->model_ == INKPLATE_5) { + return 960; + } else if (this->model_ == INKPLATE_5_V2) { + return 1280; } else if (this->model_ == INKPLATE_6_PLUS) { return 1024; } @@ -155,6 +179,10 @@ class Inkplate6 : public display::DisplayBuffer, public i2c::I2CDevice { int get_height_internal() override { if (this->model_ == INKPLATE_6 || this->model_ == INKPLATE_6_V2) { return 600; + } else if (this->model_ == INKPLATE_5) { + return 540; + } else if (this->model_ == INKPLATE_5_V2) { + return 720; } else if (this->model_ == INKPLATE_10) { return 825; } else if (this->model_ == INKPLATE_6_PLUS) { From 8e54a622d3051e091b494896194f21205913d301 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 24 Sep 2024 17:50:44 -0700 Subject: [PATCH 0397/1052] fix bl0906 reset energy action (#7488) Co-authored-by: Samuel Sieb --- esphome/components/bl0906/sensor.py | 5 +++-- tests/components/bl0906/common.yaml | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/esphome/components/bl0906/sensor.py b/esphome/components/bl0906/sensor.py index bc370c9252..42c6f06092 100644 --- a/esphome/components/bl0906/sensor.py +++ b/esphome/components/bl0906/sensor.py @@ -145,8 +145,9 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( ), ) async def reset_energy_to_code(config, action_id, template_arg, args): - paren = await cg.get_variable(config[CONF_ID]) - return cg.new_Pvariable(action_id, template_arg, paren) + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var async def to_code(config): diff --git a/tests/components/bl0906/common.yaml b/tests/components/bl0906/common.yaml index 944791369c..29321a9471 100644 --- a/tests/components/bl0906/common.yaml +++ b/tests/components/bl0906/common.yaml @@ -8,6 +8,7 @@ uart: sensor: - platform: bl0906 + id: bl frequency: name: 'Frequency' temperature: @@ -60,3 +61,9 @@ sensor: name: 'Total_Energy' total_power: name: 'Total_Power' + +button: + - platform: template + id: reset + on_press: + - bl0906.reset_energy: bl From fcce70d416602da087617920fbfe02a428b5e51c Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:09:24 -0400 Subject: [PATCH 0398/1052] Add remote transmitter triggers (#7483) Co-authored-by: Jonathan Swoboda --- .../components/remote_transmitter/__init__.py | 20 +++++++++++++++++-- .../remote_transmitter/remote_transmitter.h | 6 ++++++ .../remote_transmitter_esp32.cpp | 2 ++ .../remote_transmitter_esp8266.cpp | 2 ++ .../remote_transmitter_libretiny.cpp | 2 ++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index d203ff3417..f979939739 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -1,10 +1,14 @@ +from esphome import automation, pins import esphome.codegen as cg +from esphome.components import esp32_rmt, remote_base import esphome.config_validation as cv -from esphome import pins -from esphome.components import remote_base, esp32_rmt from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL AUTO_LOAD = ["remote_base"] + +CONF_ON_TRANSMIT = "on_transmit" +CONF_ON_COMPLETE = "on_complete" + remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter") RemoteTransmitterComponent = remote_transmitter_ns.class_( "RemoteTransmitterComponent", remote_base.RemoteTransmitterBase, cg.Component @@ -19,6 +23,8 @@ CONFIG_SCHEMA = cv.Schema( cv.percentage_int, cv.Range(min=1, max=100) ), cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True), + cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True), + cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True), } ).extend(cv.COMPONENT_SCHEMA) @@ -32,3 +38,13 @@ async def to_code(config): await cg.register_component(var, config) cg.add(var.set_carrier_duty_percent(config[CONF_CARRIER_DUTY_PERCENT])) + + if on_transmit_config := config.get(CONF_ON_TRANSMIT): + await automation.build_automation( + var.get_transmit_trigger(), [], on_transmit_config + ) + + if on_complete_config := config.get(CONF_ON_COMPLETE): + await automation.build_automation( + var.get_complete_trigger(), [], on_complete_config + ) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index a5896796c0..4abe687d23 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -33,6 +33,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, void set_carrier_duty_percent(uint8_t carrier_duty_percent) { this->carrier_duty_percent_ = carrier_duty_percent; } + Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; }; + Trigger<> *get_complete_trigger() const { return this->complete_trigger_; }; + protected: void send_internal(uint32_t send_times, uint32_t send_wait) override; #if defined(USE_ESP8266) || defined(USE_LIBRETINY) @@ -57,6 +60,9 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, bool inverted_{false}; #endif uint8_t carrier_duty_percent_; + + Trigger<> *transmit_trigger_{new Trigger<>()}; + Trigger<> *complete_trigger_{new Trigger<>()}; }; } // namespace remote_transmitter diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index eea35019ff..bce2408723 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -124,6 +124,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen ESP_LOGE(TAG, "Empty data"); return; } + this->transmit_trigger_->trigger(); for (uint32_t i = 0; i < send_times; i++) { esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true); if (error != ESP_OK) { @@ -135,6 +136,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (i + 1 < send_times) delayMicroseconds(send_wait); } + this->complete_trigger_->trigger(); } } // namespace remote_transmitter diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index 1c0eb94e61..613f00b7f5 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -76,6 +76,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->target_time_ = 0; + this->transmit_trigger_->trigger(); for (uint32_t i = 0; i < send_times; i++) { for (int32_t item : this->temp_.get_data()) { if (item > 0) { @@ -93,6 +94,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (i + 1 < send_times) this->target_time_ += send_wait; } + this->complete_trigger_->trigger(); } } // namespace remote_transmitter diff --git a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp index 78bb280482..ad9265fb14 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp @@ -78,6 +78,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen uint32_t on_time, off_time; this->calculate_on_off_time_(this->temp_.get_carrier_frequency(), &on_time, &off_time); this->target_time_ = 0; + this->transmit_trigger_->trigger(); for (uint32_t i = 0; i < send_times; i++) { InterruptLock lock; for (int32_t item : this->temp_.get_data()) { @@ -96,6 +97,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (i + 1 < send_times) this->target_time_ += send_wait; } + this->complete_trigger_->trigger(); } } // namespace remote_transmitter From fa9df329795f746bc0fdec04e521d3db41c0612f Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Wed, 25 Sep 2024 04:27:14 +0200 Subject: [PATCH 0399/1052] tcs34725: fix color/clear channel percentage calculations on long exposures (#7493) --- esphome/components/tcs34725/tcs34725.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 88c59eb761..9bb7c9a3b2 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -211,7 +211,7 @@ void TCS34725Component::update() { if (raw_c == 0) { channel_c = channel_r = channel_g = channel_b = 0.0f; } else { - float max_count = this->integration_time_ * 1024.0f / 2.4; + float max_count = this->integration_time_ <= 153.6f ? this->integration_time_ * 1024.0f / 2.4f : 65535.0f; float sum = raw_c; channel_r = raw_r / sum * 100.0f; channel_g = raw_g / sum * 100.0f; From b61577b68b1e79595e751e158ee9873863b6d51c Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Wed, 25 Sep 2024 04:28:22 +0200 Subject: [PATCH 0400/1052] tcs34725: Add check for Division by Zero (#7485) --- esphome/components/tcs34725/tcs34725.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 9bb7c9a3b2..c9b2ae321a 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -1,6 +1,7 @@ #include "tcs34725.h" #include "esphome/core/log.h" #include "esphome/core/hal.h" +#include namespace esphome { namespace tcs34725 { @@ -254,7 +255,8 @@ void TCS34725Component::update() { // change integration time an gain to achieve maximum resolution an dynamic range // calculate optimal integration time to achieve 70% satuaration float integration_time_ideal; - integration_time_ideal = 60 / ((float) raw_c / 655.35) * this->integration_time_; + + integration_time_ideal = 60 / ((float) std::max((uint16_t) 1, raw_c) / 655.35f) * this->integration_time_; uint8_t gain_reg_val_new = this->gain_reg_; // increase gain if less than 20% of white channel used and high integration time From 21fbbc5fb9477704be8b1ec8293d2a729f7a0072 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:34:27 +1200 Subject: [PATCH 0401/1052] [config_validation] Fix bug with extras on schemas (#7497) --- esphome/voluptuous_schema.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/voluptuous_schema.py b/esphome/voluptuous_schema.py index 7f1573b443..15f9206f21 100644 --- a/esphome/voluptuous_schema.py +++ b/esphome/voluptuous_schema.py @@ -226,4 +226,6 @@ class _Schema(vol.Schema): if isinstance(schema, vol.Schema): schema = schema.schema ret = super().extend(schema, extra=extra) - return _Schema(ret.schema, extra=ret.extra, extra_schemas=self._extra_schemas) + return _Schema( + ret.schema, extra=ret.extra, extra_schemas=self._extra_schemas.copy() + ) From 3b1b1071f1f001755cffed5568c6578bf4a881a1 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Thu, 26 Sep 2024 17:25:20 -0400 Subject: [PATCH 0402/1052] [core] add ring buffer destructor (#7500) --- esphome/core/ring_buffer.cpp | 14 ++++++++++++-- esphome/core/ring_buffer.h | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index d8ca831de0..f97c686684 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -11,16 +11,26 @@ namespace esphome { static const char *const TAG = "ring_buffer"; +RingBuffer::~RingBuffer() { + if (this->handle_ != nullptr) { + vStreamBufferDelete(this->handle_); + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + allocator.deallocate(this->storage_, this->size_); + } +} + std::unique_ptr RingBuffer::create(size_t len) { std::unique_ptr rb = make_unique(); + rb->size_ = len + 1; + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - rb->storage_ = allocator.allocate(len + 1); + rb->storage_ = allocator.allocate(rb->size_); if (rb->storage_ == nullptr) { return nullptr; } - rb->handle_ = xStreamBufferCreateStatic(len + 1, 1, rb->storage_, &rb->structure_); + rb->handle_ = xStreamBufferCreateStatic(rb->size_, 1, rb->storage_, &rb->structure_); ESP_LOGD(TAG, "Created ring buffer with size %u", len); return rb; } diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h index 97ffefcefa..c0511fb52e 100644 --- a/esphome/core/ring_buffer.h +++ b/esphome/core/ring_buffer.h @@ -12,6 +12,8 @@ namespace esphome { class RingBuffer { public: + ~RingBuffer(); + /** * @brief Reads from the ring buffer, waiting up to a specified number of ticks if necessary. * @@ -83,6 +85,7 @@ class RingBuffer { StreamBufferHandle_t handle_; StaticStreamBuffer_t structure_; uint8_t *storage_; + size_t size_{0}; }; } // namespace esphome From c55b4f5e1b28259a3cf54539f5b709ab2ed7595e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 27 Sep 2024 07:51:08 +1000 Subject: [PATCH 0403/1052] [ch422g] Add support for pins 8-11; make input work. (#7467) --- CODEOWNERS | 2 +- esphome/components/ch422g/__init__.py | 43 ++++++---- esphome/components/ch422g/ch422g.cpp | 119 +++++++++++++++----------- esphome/components/ch422g/ch422g.h | 36 ++++---- tests/components/ch422g/common.yaml | 11 ++- 5 files changed, 121 insertions(+), 90 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index a2fe77dc84..1eb13a534b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -86,7 +86,7 @@ esphome/components/cap1188/* @mreditor97 esphome/components/captive_portal/* @OttoWinter esphome/components/ccs811/* @habbie esphome/components/cd74hc4067/* @asoehlke -esphome/components/ch422g/* @jesterret +esphome/components/ch422g/* @clydebarrow @jesterret esphome/components/climate/* @esphome/core esphome/components/climate_ir/* @glmnet esphome/components/color_temperature/* @jesserockz diff --git a/esphome/components/ch422g/__init__.py b/esphome/components/ch422g/__init__.py index cf8b5f65d3..6a7bace0a2 100644 --- a/esphome/components/ch422g/__init__.py +++ b/esphome/components/ch422g/__init__.py @@ -1,18 +1,20 @@ from esphome import pins import esphome.codegen as cg from esphome.components import i2c +from esphome.components.i2c import I2CBus import esphome.config_validation as cv from esphome.const import ( + CONF_I2C_ID, CONF_ID, CONF_INPUT, CONF_INVERTED, CONF_MODE, CONF_NUMBER, + CONF_OPEN_DRAIN, CONF_OUTPUT, - CONF_RESTORE_VALUE, ) -CODEOWNERS = ["@jesterret"] +CODEOWNERS = ["@jesterret", "@clydebarrow"] DEPENDENCIES = ["i2c"] MULTI_CONF = True ch422g_ns = cg.esphome_ns.namespace("ch422g") @@ -23,29 +25,36 @@ CH422GGPIOPin = ch422g_ns.class_( ) CONF_CH422G = "ch422g" -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.Required(CONF_ID): cv.declare_id(CH422GComponent), - cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean, - } - ) - .extend(cv.COMPONENT_SCHEMA) - .extend(i2c.i2c_device_schema(0x24)) -) + +# Note that no address is configurable - each register in the CH422G has a dedicated i2c address +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_ID): cv.declare_id(CH422GComponent), + cv.GenerateID(CONF_I2C_ID): cv.use_id(I2CBus), + } +).extend(cv.COMPONENT_SCHEMA) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) await cg.register_component(var, config) - await i2c.register_i2c_device(var, config) + # Can't use register_i2c_device because there is no CONF_ADDRESS + parent = await cg.get_variable(config[CONF_I2C_ID]) + cg.add(var.set_i2c_bus(parent)) + + +# This is used as a final validation step so that modes have been fully transformed. +def pin_mode_check(pin_config, _): + if pin_config[CONF_MODE][CONF_INPUT] and pin_config[CONF_NUMBER] >= 8: + raise cv.Invalid("CH422G only supports input on pins 0-7") + if pin_config[CONF_MODE][CONF_OPEN_DRAIN] and pin_config[CONF_NUMBER] < 8: + raise cv.Invalid("CH422G only supports open drain output on pins 8-11") CH422G_PIN_SCHEMA = pins.gpio_base_schema( CH422GGPIOPin, - cv.int_range(min=0, max=7), - modes=[CONF_INPUT, CONF_OUTPUT], + cv.int_range(min=0, max=11), + modes=[CONF_INPUT, CONF_OUTPUT, CONF_OPEN_DRAIN], ).extend( { cv.Required(CONF_CH422G): cv.use_id(CH422GComponent), @@ -53,7 +62,7 @@ CH422G_PIN_SCHEMA = pins.gpio_base_schema( ) -@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA) +@pins.PIN_SCHEMA_REGISTRY.register(CONF_CH422G, CH422G_PIN_SCHEMA, pin_mode_check) async def ch422g_pin_to_code(config): var = cg.new_Pvariable(config[CONF_ID]) parent = await cg.get_variable(config[CONF_CH422G]) diff --git a/esphome/components/ch422g/ch422g.cpp b/esphome/components/ch422g/ch422g.cpp index 25038991ed..0db179d99e 100644 --- a/esphome/components/ch422g/ch422g.cpp +++ b/esphome/components/ch422g/ch422g.cpp @@ -4,33 +4,33 @@ namespace esphome { namespace ch422g { -const uint8_t CH422G_REG_IN = 0x26; -const uint8_t CH422G_REG_OUT = 0x38; -const uint8_t OUT_REG_DEFAULT_VAL = 0xdf; +static const uint8_t CH422G_REG_MODE = 0x24; +static const uint8_t CH422G_MODE_OUTPUT = 0x01; // enables output mode on 0-7 +static const uint8_t CH422G_MODE_OPEN_DRAIN = 0x04; // enables open drain mode on 8-11 +static const uint8_t CH422G_REG_IN = 0x26; // read reg for input bits +static const uint8_t CH422G_REG_OUT = 0x38; // write reg for output bits 0-7 +static const uint8_t CH422G_REG_OUT_UPPER = 0x23; // write reg for output bits 8-11 static const char *const TAG = "ch422g"; void CH422GComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up CH422G..."); - // Test to see if device exists - if (!this->read_inputs_()) { + // set outputs before mode + this->write_outputs_(); + // Set mode and check for errors + if (!this->set_mode_(this->mode_value_) || !this->read_inputs_()) { ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_); this->mark_failed(); return; } - // restore defaults over whatever got saved on last boot - if (!this->restore_value_) { - this->write_output_(OUT_REG_DEFAULT_VAL); - } - - ESP_LOGD(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(), - this->status_has_error()); + ESP_LOGCONFIG(TAG, "Initialization complete. Warning: %d, Error: %d", this->status_has_warning(), + this->status_has_error()); } void CH422GComponent::loop() { // Clear all the previously read flags. - this->pin_read_cache_ = 0x00; + this->pin_read_flags_ = 0x00; } void CH422GComponent::dump_config() { @@ -41,82 +41,99 @@ void CH422GComponent::dump_config() { } } -// ch422g doesn't have any flag support (needs docs?) -void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) {} +void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) { + if (pin < 8) { + if (flags & gpio::FLAG_OUTPUT) { + this->mode_value_ |= CH422G_MODE_OUTPUT; + } + } else { + if (flags & gpio::FLAG_OPEN_DRAIN) { + this->mode_value_ |= CH422G_MODE_OPEN_DRAIN; + } + } +} bool CH422GComponent::digital_read(uint8_t pin) { - if (this->pin_read_cache_ == 0 || this->pin_read_cache_ & (1 << pin)) { + if (this->pin_read_flags_ == 0 || this->pin_read_flags_ & (1 << pin)) { // Read values on first access or in case it's being read again in the same loop this->read_inputs_(); } - this->pin_read_cache_ |= (1 << pin); - return this->state_mask_ & (1 << pin); + this->pin_read_flags_ |= (1 << pin); + return (this->input_bits_ & (1 << pin)) != 0; } void CH422GComponent::digital_write(uint8_t pin, bool value) { if (value) { - this->write_output_(this->state_mask_ | (1 << pin)); + this->output_bits_ |= (1 << pin); } else { - this->write_output_(this->state_mask_ & ~(1 << pin)); + this->output_bits_ &= ~(1 << pin); } + this->write_outputs_(); } bool CH422GComponent::read_inputs_() { if (this->is_failed()) { return false; } - - uint8_t temp = 0; - if ((this->last_error_ = this->read(&temp, 1)) != esphome::i2c::ERROR_OK) { - this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str()); - return false; + uint8_t result; + // reading inputs requires the chip to be in input mode, possibly temporarily. + if (this->mode_value_ & CH422G_MODE_OUTPUT) { + this->set_mode_(this->mode_value_ & ~CH422G_MODE_OUTPUT); + result = this->read_reg_(CH422G_REG_IN); + this->set_mode_(this->mode_value_); + } else { + result = this->read_reg_(CH422G_REG_IN); } - - uint8_t output = 0; - if ((this->last_error_ = this->bus_->read(CH422G_REG_IN, &output, 1)) != esphome::i2c::ERROR_OK) { - this->status_set_warning(str_sprintf("read_inputs_(): I2C I/O error: %d", (int) this->last_error_).c_str()); - return false; - } - - this->state_mask_ = output; + this->input_bits_ = result; this->status_clear_warning(); - return true; } -bool CH422GComponent::write_output_(uint8_t value) { - const uint8_t temp = 1; - if ((this->last_error_ = this->write(&temp, 1, false)) != esphome::i2c::ERROR_OK) { - this->status_set_warning(str_sprintf("write_output_(): I2C I/O error: %d", (int) this->last_error_).c_str()); +// Write a register. Can't use the standard write_byte() method because there is no single pre-configured i2c address. +bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) { + auto err = this->bus_->write(reg, &value, 1); + if (err != i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str()); return false; } - - uint8_t write_mask = value; - if ((this->last_error_ = this->bus_->write(CH422G_REG_OUT, &write_mask, 1)) != esphome::i2c::ERROR_OK) { - this->status_set_warning( - str_sprintf("write_output_(): I2C I/O error: %d for write_mask: %d", (int) this->last_error_, (int) write_mask) - .c_str()); - return false; - } - - this->state_mask_ = value; this->status_clear_warning(); return true; } +uint8_t CH422GComponent::read_reg_(uint8_t reg) { + uint8_t value; + auto err = this->bus_->read(reg, &value, 1); + if (err != i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str()); + return 0; + } + this->status_clear_warning(); + return value; +} + +bool CH422GComponent::set_mode_(uint8_t mode) { return this->write_reg_(CH422G_REG_MODE, mode); } + +bool CH422GComponent::write_outputs_() { + return this->write_reg_(CH422G_REG_OUT, static_cast(this->output_bits_)) && + this->write_reg_(CH422G_REG_OUT_UPPER, static_cast(this->output_bits_ >> 8)); +} + float CH422GComponent::get_setup_priority() const { return setup_priority::IO; } // Run our loop() method very early in the loop, so that we cache read values // before other components call our digital_read() method. float CH422GComponent::get_loop_priority() const { return 9.0f; } // Just after WIFI -void CH422GGPIOPin::setup() { pin_mode(flags_); } void CH422GGPIOPin::pin_mode(gpio::Flags flags) { this->parent_->pin_mode(this->pin_, flags); } -bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) != this->inverted_; } +bool CH422GGPIOPin::digital_read() { return this->parent_->digital_read(this->pin_) ^ this->inverted_; } -void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } +void CH422GGPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value ^ this->inverted_); } std::string CH422GGPIOPin::dump_summary() const { return str_sprintf("EXIO%u via CH422G", pin_); } +void CH422GGPIOPin::set_flags(gpio::Flags flags) { + flags_ = flags; + this->parent_->pin_mode(this->pin_, flags); +} } // namespace ch422g } // namespace esphome diff --git a/esphome/components/ch422g/ch422g.h b/esphome/components/ch422g/ch422g.h index 781df65437..30780e09ad 100644 --- a/esphome/components/ch422g/ch422g.h +++ b/esphome/components/ch422g/ch422g.h @@ -23,32 +23,30 @@ class CH422GComponent : public Component, public i2c::I2CDevice { void pin_mode(uint8_t pin, gpio::Flags flags); float get_setup_priority() const override; - float get_loop_priority() const override; - void dump_config() override; - void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } - protected: + bool write_reg_(uint8_t reg, uint8_t value); + uint8_t read_reg_(uint8_t reg); + bool set_mode_(uint8_t mode); bool read_inputs_(); - - bool write_output_(uint8_t value); + bool write_outputs_(); /// The mask to write as output state - 1 means HIGH, 0 means LOW - uint8_t state_mask_{0x00}; + uint16_t output_bits_{0x00}; /// Flags to check if read previously during this loop - uint8_t pin_read_cache_ = {0x00}; - /// Storage for last I2C error seen - esphome::i2c::ErrorCode last_error_; - /// Whether we want to override stored values on expander - bool restore_value_{false}; + uint8_t pin_read_flags_ = {0x00}; + /// Copy of last read values + uint8_t input_bits_ = {0x00}; + /// Copy of the mode value + uint8_t mode_value_{}; }; -/// Helper class to expose a CH422G pin as an internal input GPIO pin. +/// Helper class to expose a CH422G pin as a GPIO pin. class CH422GGPIOPin : public GPIOPin { public: - void setup() override; + void setup() override{}; void pin_mode(gpio::Flags flags) override; bool digital_read() override; void digital_write(bool value) override; @@ -57,13 +55,13 @@ class CH422GGPIOPin : public GPIOPin { void set_parent(CH422GComponent *parent) { parent_ = parent; } void set_pin(uint8_t pin) { pin_ = pin; } void set_inverted(bool inverted) { inverted_ = inverted; } - void set_flags(gpio::Flags flags) { flags_ = flags; } + void set_flags(gpio::Flags flags); protected: - CH422GComponent *parent_; - uint8_t pin_; - bool inverted_; - gpio::Flags flags_; + CH422GComponent *parent_{}; + uint8_t pin_{}; + bool inverted_{}; + gpio::Flags flags_{}; }; } // namespace ch422g diff --git a/tests/components/ch422g/common.yaml b/tests/components/ch422g/common.yaml index 02061bda59..d65956ecac 100644 --- a/tests/components/ch422g/common.yaml +++ b/tests/components/ch422g/common.yaml @@ -1,6 +1,5 @@ ch422g: - id: ch422g_hub - address: 0x24 binary_sensor: - platform: gpio @@ -11,10 +10,18 @@ binary_sensor: number: 1 mode: INPUT inverted: true +output: - platform: gpio - id: ch422g_output + id: ch422_out_0 pin: ch422g: ch422g_hub number: 0 mode: OUTPUT inverted: false + - platform: gpio + id: ch422_out_11 + pin: + ch422g: ch422g_hub + number: 11 + mode: OUTPUT_OPEN_DRAIN + inverted: true From 3df25a183a3a3d7018955e9335336791d704a4ed Mon Sep 17 00:00:00 2001 From: victorclaessen Date: Thu, 26 Sep 2024 23:57:51 +0200 Subject: [PATCH 0404/1052] Add clean_session as configurable option to the MQTT component (#7501) --- esphome/components/mqtt/__init__.py | 3 +++ esphome/components/mqtt/mqtt_client.cpp | 2 ++ esphome/components/mqtt/mqtt_client.h | 2 ++ esphome/const.py | 1 + tests/components/mqtt/common.yaml | 1 + 5 files changed, 9 insertions(+) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 240b407819..336d928f71 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -11,6 +11,7 @@ from esphome.const import ( CONF_BIRTH_MESSAGE, CONF_BROKER, CONF_CERTIFICATE_AUTHORITY, + CONF_CLEAN_SESSION, CONF_CLIENT_CERTIFICATE, CONF_CLIENT_CERTIFICATE_KEY, CONF_CLIENT_ID, @@ -209,6 +210,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_PORT, default=1883): cv.port, cv.Optional(CONF_USERNAME, default=""): cv.string, cv.Optional(CONF_PASSWORD, default=""): cv.string, + cv.Optional(CONF_CLEAN_SESSION, default=False): cv.boolean, cv.Optional(CONF_CLIENT_ID): cv.string, cv.SplitDefault(CONF_IDF_SEND_ASYNC, esp32_idf=False): cv.All( cv.boolean, cv.only_with_esp_idf @@ -325,6 +327,7 @@ async def to_code(config): cg.add(var.set_broker_port(config[CONF_PORT])) cg.add(var.set_username(config[CONF_USERNAME])) cg.add(var.set_password(config[CONF_PASSWORD])) + cg.add(var.set_clean_session(config[CONF_CLEAN_SESSION])) if CONF_CLIENT_ID in config: cg.add(var.set_client_id(config[CONF_CLIENT_ID])) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index c19b24c0cf..b5ac285026 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -147,6 +147,7 @@ void MQTTClientComponent::dump_config() { 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)); if (this->is_discovery_ip_enabled()) { ESP_LOGCONFIG(TAG, " Discovery IP enabled"); } @@ -246,6 +247,7 @@ void MQTTClientComponent::start_connect_() { this->mqtt_backend_.disconnect(); this->mqtt_backend_.set_client_id(this->credentials_.client_id.c_str()); + this->mqtt_backend_.set_clean_session(this->credentials_.clean_session); const char *username = nullptr; if (!this->credentials_.username.empty()) username = this->credentials_.username.c_str(); diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index b0d3bbe66d..887800f201 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -51,6 +51,7 @@ struct MQTTCredentials { std::string username; std::string password; std::string client_id; ///< The client ID. Will automatically be truncated to 23 characters. + bool clean_session; ///< Whether the session will be cleaned or remembered between connects. }; /// Simple data struct for Home Assistant component availability. @@ -254,6 +255,7 @@ class MQTTClientComponent : public Component { void set_username(const std::string &username) { this->credentials_.username = username; } void set_password(const std::string &password) { this->credentials_.password = password; } void set_client_id(const std::string &client_id) { this->credentials_.client_id = client_id; } + void set_clean_session(const bool &clean_session) { this->credentials_.clean_session = clean_session; } void set_on_connect(mqtt_on_connect_callback_t &&callback); void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback); diff --git a/esphome/const.py b/esphome/const.py index 6e7bbdec98..40b7a1c419 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -120,6 +120,7 @@ CONF_CHANNELS = "channels" CONF_CHARACTERISTIC_UUID = "characteristic_uuid" CONF_CHECK = "check" CONF_CHIPSET = "chipset" +CONF_CLEAN_SESSION = "clean_session" CONF_CLEAR_IMPEDANCE = "clear_impedance" CONF_CLIENT_CERTIFICATE = "client_certificate" CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key" diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index b7d1655ec9..f7a727ab2f 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -10,6 +10,7 @@ mqtt: port: 1883 username: debug password: debug + clean_session: True client_id: someclient use_abbreviations: false discovery: true From 529ff4bd526c258069e388dcdd16bfc1b5c43535 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 26 Sep 2024 17:24:18 -0500 Subject: [PATCH 0405/1052] [wifi] Use custom MAC address if programmed (#7498) --- .../wifi/wifi_component_esp_idf.cpp | 7 ++- esphome/core/helpers.cpp | 45 ++++++++++++++++++- esphome/core/helpers.h | 8 ++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 6008acb95d..c430d160f2 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -130,11 +130,16 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi } void WiFiComponent::wifi_pre_setup_() { -#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC uint8_t mac[6]; +#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC get_mac_address_raw(mac); set_mac_address(mac); ESP_LOGV(TAG, "Use EFuse MAC without checking CRC: %s", get_mac_address_pretty().c_str()); +#else + if (has_custom_mac_address()) { + get_mac_address_raw(mac); + set_mac_address(mac); + } #endif esp_err_t err = esp_netif_init(); if (err != ERR_OK) { diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index e75b06ccd3..2e99b0df70 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -671,9 +671,17 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame // match the CRC that goes along with it. For those devices, this // work-around reads and uses the MAC address as-is from EFuse, // without doing the CRC check. - esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, mac, 48); + if (has_custom_mac_address()) { + esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48); + } else { + esp_efuse_read_field_blob(ESP_EFUSE_MAC_FACTORY, mac, 48); + } #else - esp_efuse_mac_get_default(mac); + if (has_custom_mac_address()) { + esp_efuse_mac_get_custom(mac); + } else { + esp_efuse_mac_get_default(mac); + } #endif #elif defined(USE_ESP8266) wifi_get_macaddr(STATION_IF, mac); @@ -685,20 +693,53 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame // this should be an error, but that messes with CI checks. #error No mac address method defined #endif } + std::string get_mac_address() { uint8_t mac[6]; get_mac_address_raw(mac); return str_snprintf("%02x%02x%02x%02x%02x%02x", 12, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } + std::string get_mac_address_pretty() { uint8_t mac[6]; get_mac_address_raw(mac); return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } + #ifdef USE_ESP32 void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); } #endif +bool has_custom_mac_address() { +#ifdef USE_ESP32 + uint8_t mac[6]; +#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC) + return (esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac); +#else + return (esp_efuse_mac_get_custom(mac) == ESP_OK) && mac_address_is_valid(mac); +#endif +#else + return false; +#endif +} + +bool mac_address_is_valid(const uint8_t *mac) { + bool is_all_zeros = true; + bool is_all_ones = true; + + for (uint8_t i = 0; i < 6; i++) { + if (mac[i] != 0) { + is_all_zeros = false; + } + } + for (uint8_t i = 0; i < 6; i++) { + if (mac[i] != 0xFF) { + is_all_ones = false; + } + } + return !(is_all_zeros || is_all_ones); +} + void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability uint32_t start = micros(); diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 3e6fe9433e..7df4b84230 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -635,6 +635,14 @@ std::string get_mac_address_pretty(); void set_mac_address(uint8_t *mac); #endif +/// Check if a custom MAC address is set (ESP32 & variants) +/// @return True if a custom MAC address is set (ESP32 & variants), else false +bool has_custom_mac_address(); + +/// Check if the MAC address is not all zeros or all ones +/// @return True if MAC is valid, else false +bool mac_address_is_valid(const uint8_t *mac); + /// Delay for the given amount of microseconds, possibly yielding to other processes during the wait. void delay_microseconds_safe(uint32_t us); From 023cb4937e2ee3abac2c7c46cc54f9705c46a795 Mon Sep 17 00:00:00 2001 From: zry98 Date: Mon, 30 Sep 2024 03:22:27 +0200 Subject: [PATCH 0406/1052] Add support for Sharp GP2Y1010AU0F PM2.5 sensor (#6007) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/gp2y1010au0f/__init__.py | 0 .../components/gp2y1010au0f/gp2y1010au0f.cpp | 67 +++++++++++++++++++ .../components/gp2y1010au0f/gp2y1010au0f.h | 52 ++++++++++++++ esphome/components/gp2y1010au0f/sensor.py | 61 +++++++++++++++++ .../gp2y1010au0f/test.esp32-idf.yaml | 16 +++++ 6 files changed, 197 insertions(+) create mode 100644 esphome/components/gp2y1010au0f/__init__.py create mode 100644 esphome/components/gp2y1010au0f/gp2y1010au0f.cpp create mode 100644 esphome/components/gp2y1010au0f/gp2y1010au0f.h create mode 100644 esphome/components/gp2y1010au0f/sensor.py create mode 100644 tests/components/gp2y1010au0f/test.esp32-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 1eb13a534b..3f5ff46c02 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -152,6 +152,7 @@ esphome/components/ft63x6/* @gpambrozio esphome/components/gcja5/* @gcormier esphome/components/gdk101/* @Szewcson esphome/components/globals/* @esphome/core +esphome/components/gp2y1010au0f/* @zry98 esphome/components/gp8403/* @jesserockz esphome/components/gpio/* @esphome/core esphome/components/gpio/one_wire/* @ssieb diff --git a/esphome/components/gp2y1010au0f/__init__.py b/esphome/components/gp2y1010au0f/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp b/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp new file mode 100644 index 0000000000..95b7653e51 --- /dev/null +++ b/esphome/components/gp2y1010au0f/gp2y1010au0f.cpp @@ -0,0 +1,67 @@ +#include "gp2y1010au0f.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +#include + +namespace esphome { +namespace gp2y1010au0f { + +static const char *const TAG = "gp2y1010au0f"; +static const float MIN_VOLTAGE = 0.0f; +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_); + LOG_UPDATE_INTERVAL(this); +} + +void GP2Y1010AU0FSensor::update() { + is_sampling_ = true; + + this->set_timeout("read", this->sample_duration_, [this]() { + this->is_sampling_ = false; + if (this->num_samples_ == 0) + return; + + float mean = this->sample_sum_ / float(this->num_samples_); + ESP_LOGD(TAG, "ADC read voltage: %.3f V (mean from %" PRId32 " samples)", mean, this->num_samples_); + + // PM2.5 calculation + // ref: https://www.howmuchsnow.com/arduino/airquality/ + int16_t pm_2_5_value = 170 * mean; + this->publish_state(pm_2_5_value); + }); + + // reset readings + this->num_samples_ = 0; + this->sample_sum_ = 0.0f; +} + +void GP2Y1010AU0FSensor::loop() { + if (!this->is_sampling_) + return; + + // enable the internal IR LED + this->led_output_->turn_on(); + // wait for the sensor to stabilize + delayMicroseconds(this->sample_wait_before_); + // perform a single sample + float read_voltage = this->source_->sample(); + // disable the internal IR LED + this->led_output_->turn_off(); + + if (std::isnan(read_voltage)) + return; + read_voltage = read_voltage * this->voltage_multiplier_ - this->voltage_offset_; + if (read_voltage < MIN_VOLTAGE || read_voltage > MAX_VOLTAGE) + return; + + this->num_samples_++; + this->sample_sum_ += read_voltage; +} + +} // namespace gp2y1010au0f +} // namespace esphome diff --git a/esphome/components/gp2y1010au0f/gp2y1010au0f.h b/esphome/components/gp2y1010au0f/gp2y1010au0f.h new file mode 100644 index 0000000000..5ee58e68d2 --- /dev/null +++ b/esphome/components/gp2y1010au0f/gp2y1010au0f.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/voltage_sampler/voltage_sampler.h" +#include "esphome/components/output/binary_output.h" + +namespace esphome { +namespace gp2y1010au0f { + +class GP2Y1010AU0FSensor : public sensor::Sensor, public PollingComponent { + public: + void update() override; + void loop() override; + void dump_config() override; + float get_setup_priority() const override { + // after the base sensor has been initialized + return setup_priority::DATA - 1.0f; + } + + void set_adc_source(voltage_sampler::VoltageSampler *source) { source_ = source; } + void set_voltage_refs(float offset, float multiplier) { + this->voltage_offset_ = offset; + this->voltage_multiplier_ = multiplier; + } + void set_led_output(output::BinaryOutput *output) { led_output_ = output; } + + protected: + // duration in ms of the sampling phase + uint32_t sample_duration_ = 100; + // duration in us of the wait before sampling + // ref: https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y1010au_appl_e.pdf + uint32_t sample_wait_before_ = 280; + // duration in us of the wait after sampling + // it seems no need to delay on purpose since one ADC sampling takes longer than that (300-400 us on ESP8266) + // uint32_t sample_wait_after_ = 40; + // the sampling source to read voltage from + voltage_sampler::VoltageSampler *source_; + // ADC voltage reading offset + float voltage_offset_ = 0.0f; + // ADC voltage reading multiplier + float voltage_multiplier_ = 1.0f; + // the binary output to control the sampling LED + output::BinaryOutput *led_output_; + + float sample_sum_ = 0.0f; + uint32_t num_samples_ = 0; + bool is_sampling_ = false; +}; + +} // namespace gp2y1010au0f +} // namespace esphome diff --git a/esphome/components/gp2y1010au0f/sensor.py b/esphome/components/gp2y1010au0f/sensor.py new file mode 100644 index 0000000000..7e1bd277a6 --- /dev/null +++ b/esphome/components/gp2y1010au0f/sensor.py @@ -0,0 +1,61 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, voltage_sampler, output +from esphome.const import ( + CONF_SENSOR, + CONF_OUTPUT, + DEVICE_CLASS_PM25, + STATE_CLASS_MEASUREMENT, + UNIT_MICROGRAMS_PER_CUBIC_METER, + ICON_CHEMICAL_WEAPON, +) + +DEPENDENCIES = ["output"] +AUTO_LOAD = ["voltage_sampler"] +CODEOWNERS = ["@zry98"] + +CONF_ADC_VOLTAGE_OFFSET = "adc_voltage_offset" +CONF_ADC_VOLTAGE_MULTIPLIER = "adc_voltage_multiplier" + +gp2y1010au0f_ns = cg.esphome_ns.namespace("gp2y1010au0f") +GP2Y1010AU0FSensor = gp2y1010au0f_ns.class_( + "GP2Y1010AU0FSensor", sensor.Sensor, cg.PollingComponent +) + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + GP2Y1010AU0FSensor, + unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_PM25, + state_class=STATE_CLASS_MEASUREMENT, + icon=ICON_CHEMICAL_WEAPON, + ) + .extend( + { + cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler), + cv.Optional(CONF_ADC_VOLTAGE_OFFSET, default=0.0): cv.float_, + cv.Optional(CONF_ADC_VOLTAGE_MULTIPLIER, default=1.0): cv.float_, + cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), + } + ) + .extend(cv.polling_component_schema("60s")) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + + # the ADC sensor to read voltage from + adc_sensor = await cg.get_variable(config[CONF_SENSOR]) + cg.add(var.set_adc_source(adc_sensor)) + cg.add( + var.set_voltage_refs( + config[CONF_ADC_VOLTAGE_OFFSET], config[CONF_ADC_VOLTAGE_MULTIPLIER] + ) + ) + + # the binary output to control the module's internal IR LED + led_output = await cg.get_variable(config[CONF_OUTPUT]) + cg.add(var.set_led_output(led_output)) diff --git a/tests/components/gp2y1010au0f/test.esp32-idf.yaml b/tests/components/gp2y1010au0f/test.esp32-idf.yaml new file mode 100644 index 0000000000..eb5ad0ea67 --- /dev/null +++ b/tests/components/gp2y1010au0f/test.esp32-idf.yaml @@ -0,0 +1,16 @@ +sensor: + - platform: adc + pin: GPIO36 + id: adc_sensor + + - platform: gp2y1010au0f + sensor: adc_sensor + name: Dust Sensor + adc_voltage_offset: 0.2 + adc_voltage_multiplier: 3.3 + output: dust_sensor_led + +output: + - platform: gpio + id: dust_sensor_led + pin: GPIO32 From 49a3d385eb9f7b61457b97d87fe599ff4653f647 Mon Sep 17 00:00:00 2001 From: Nick Kinnan Date: Sun, 29 Sep 2024 18:59:12 -0700 Subject: [PATCH 0407/1052] Prevent rp2040 randomly breaking the build (#7507) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/wizard.py | 2 +- platformio.ini | 2 +- .../test_build_components/build_components_base.rp2040-ard.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/wizard.py b/esphome/wizard.py index 319fb31938..b1057189fd 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -92,7 +92,7 @@ rp2040: board: {board} framework: # Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git + platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c """ BK72XX_CONFIG = """ diff --git a/platformio.ini b/platformio.ini index e3593bf43f..fc38923f65 100644 --- a/platformio.ini +++ b/platformio.ini @@ -165,7 +165,7 @@ platform_packages = extends = common:arduino board_build.filesystem_size = 0.5m -platform = https://github.com/maxgerhardt/platform-raspberrypi.git +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c platform_packages = ; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.9.4/rp2040-3.9.4.zip diff --git a/tests/test_build_components/build_components_base.rp2040-ard.yaml b/tests/test_build_components/build_components_base.rp2040-ard.yaml index 6c6a27e0a7..f9815578c5 100644 --- a/tests/test_build_components/build_components_base.rp2040-ard.yaml +++ b/tests/test_build_components/build_components_base.rp2040-ard.yaml @@ -6,7 +6,7 @@ rp2040: board: rpipicow framework: # Waiting for https://github.com/platformio/platform-raspberrypi/pull/36 - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git + platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c logger: level: VERY_VERBOSE From 20cb2e147fd6bae669facaeeaf967c588f4b55d1 Mon Sep 17 00:00:00 2001 From: Nick Kinnan Date: Sun, 29 Sep 2024 20:27:22 -0700 Subject: [PATCH 0408/1052] Make time dependency optional (#7425) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/datetime/__init__.py | 28 ++++++++++++------- esphome/components/datetime/datetime_base.h | 9 +++++- .../components/datetime/datetime_entity.cpp | 2 ++ esphome/components/datetime/datetime_entity.h | 2 ++ esphome/components/datetime/time_entity.cpp | 2 ++ esphome/components/datetime/time_entity.h | 2 ++ 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 5429121d56..55066006d3 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -26,7 +26,6 @@ from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@rfdarter", "@jesserockz"] -DEPENDENCIES = ["time"] IS_PLATFORM_COMPONENT = True @@ -62,20 +61,28 @@ DATETIME_MODES = [ ] -_DATETIME_SCHEMA = ( - cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) - .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) - .extend( +def _validate_time_present(config): + config = config.copy() + if CONF_ON_TIME in config and CONF_TIME_ID not in config: + time_id = cv.use_id(time.RealTimeClock)(None) + config[CONF_TIME_ID] = time_id + return config + + +_DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( + web_server.WEBSERVER_SORTING_SCHEMA, + cv.MQTT_COMMAND_COMPONENT_SCHEMA, + cv.Schema( { cv.Optional(CONF_ON_VALUE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DateTimeStateTrigger), } ), - cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), + cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock), } - ) -) + ), +).add_extra(_validate_time_present) def date_schema(class_: MockObjClass) -> cv.Schema: @@ -138,8 +145,9 @@ async def setup_datetime_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf) - rtc = await cg.get_variable(config[CONF_TIME_ID]) - cg.add(var.set_rtc(rtc)) + if CONF_TIME_ID in config: + rtc = await cg.get_variable(config[CONF_TIME_ID]) + cg.add(var.set_rtc(rtc)) for conf in config.get(CONF_ON_TIME, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) diff --git a/esphome/components/datetime/datetime_base.h b/esphome/components/datetime/datetime_base.h index c8240390e3..dea34e6110 100644 --- a/esphome/components/datetime/datetime_base.h +++ b/esphome/components/datetime/datetime_base.h @@ -4,8 +4,9 @@ #include "esphome/core/component.h" #include "esphome/core/entity_base.h" #include "esphome/core/time.h" - +#ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" +#endif namespace esphome { namespace datetime { @@ -19,23 +20,29 @@ class DateTimeBase : public EntityBase { void add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); } +#ifdef USE_TIME void set_rtc(time::RealTimeClock *rtc) { this->rtc_ = rtc; } time::RealTimeClock *get_rtc() const { return this->rtc_; } +#endif protected: CallbackManager state_callback_; +#ifdef USE_TIME time::RealTimeClock *rtc_; +#endif bool has_state_{false}; }; +#ifdef USE_TIME class DateTimeStateTrigger : public Trigger { public: explicit DateTimeStateTrigger(DateTimeBase *parent) { parent->add_on_state_callback([this, parent]() { this->trigger(parent->state_as_esptime()); }); } }; +#endif } // namespace datetime } // namespace esphome diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index 9a61d341e4..f215b7acb5 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -192,6 +192,7 @@ void DateTimeEntityRestoreState::apply(DateTimeEntity *time) { time->publish_state(); } +#ifdef USE_TIME static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider // there has been a drastic time synchronization @@ -245,6 +246,7 @@ bool OnDateTimeTrigger::matches_(const ESPTime &time) const { time.day_of_month == this->parent_->day && time.hour == this->parent_->hour && time.minute == this->parent_->minute && time.second == this->parent_->second; } +#endif } // namespace datetime } // namespace esphome diff --git a/esphome/components/datetime/datetime_entity.h b/esphome/components/datetime/datetime_entity.h index d541fa96b1..27db84cf7e 100644 --- a/esphome/components/datetime/datetime_entity.h +++ b/esphome/components/datetime/datetime_entity.h @@ -134,6 +134,7 @@ template class DateTimeSetAction : public Action, public } }; +#ifdef USE_TIME class OnDateTimeTrigger : public Trigger<>, public Component, public Parented { public: void loop() override; @@ -143,6 +144,7 @@ class OnDateTimeTrigger : public Trigger<>, public Component, public Parented last_check_; }; +#endif } // namespace datetime } // namespace esphome diff --git a/esphome/components/datetime/time_entity.cpp b/esphome/components/datetime/time_entity.cpp index ea5e6684d0..db0094ae01 100644 --- a/esphome/components/datetime/time_entity.cpp +++ b/esphome/components/datetime/time_entity.cpp @@ -94,6 +94,7 @@ void TimeEntityRestoreState::apply(TimeEntity *time) { time->publish_state(); } +#ifdef USE_TIME static const int MAX_TIMESTAMP_DRIFT = 900; // how far can the clock drift before we consider // there has been a drastic time synchronization @@ -145,6 +146,7 @@ bool OnTimeTrigger::matches_(const ESPTime &time) const { return time.is_valid() && time.hour == this->parent_->hour && time.minute == this->parent_->minute && time.second == this->parent_->second; } +#endif } // namespace datetime } // namespace esphome diff --git a/esphome/components/datetime/time_entity.h b/esphome/components/datetime/time_entity.h index 62e593d28a..f7e0a7ddd9 100644 --- a/esphome/components/datetime/time_entity.h +++ b/esphome/components/datetime/time_entity.h @@ -113,6 +113,7 @@ template class TimeSetAction : public Action, public Pare } }; +#ifdef USE_TIME class OnTimeTrigger : public Trigger<>, public Component, public Parented { public: void loop() override; @@ -122,6 +123,7 @@ class OnTimeTrigger : public Trigger<>, public Component, public Parented last_check_; }; +#endif } // namespace datetime } // namespace esphome From 01f5ca26dcf30b42fa9dac32130667b990f8b375 Mon Sep 17 00:00:00 2001 From: Darren Griffin Date: Mon, 30 Sep 2024 17:49:13 +0100 Subject: [PATCH 0409/1052] Add OHF logo to README (#7509) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bb6fb37d3a..da1b2b3650 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,5 @@ For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues). For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues). + +[![ESPHome - A project from the Open Home Foundation](https://www.openhomefoundation.org/badges/esphome.png)](https://www.openhomefoundation.org/) From 507d27e84a742c2115ecd5d9bf4db1ec0db7d2b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:48:36 +0200 Subject: [PATCH 0410/1052] Bump pypa/gh-action-pypi-publish from 1.10.1 to 1.10.2 (#7487) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 522de63360..d63f221ef5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.10.1 + uses: pypa/gh-action-pypi-publish@v1.10.2 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From 01e03b76a7d42507c514173f84bbe741e7ab4c60 Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Tue, 1 Oct 2024 00:00:40 +0200 Subject: [PATCH 0411/1052] tcs34725: optimize fetch time with burst read for RGB and clear values (#7494) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/tcs34725/tcs34725.cpp | 34 +++++++++--------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index c9b2ae321a..9d682e094c 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #include "esphome/core/hal.h" #include +#include "esphome/core/helpers.h" namespace esphome { namespace tcs34725 { @@ -14,10 +15,7 @@ static const uint8_t TCS34725_REGISTER_ID = TCS34725_COMMAND_BIT | 0x12; static const uint8_t TCS34725_REGISTER_ATIME = TCS34725_COMMAND_BIT | 0x01; static const uint8_t TCS34725_REGISTER_CONTROL = TCS34725_COMMAND_BIT | 0x0F; static const uint8_t TCS34725_REGISTER_ENABLE = TCS34725_COMMAND_BIT | 0x00; -static const uint8_t TCS34725_REGISTER_CDATAL = TCS34725_COMMAND_BIT | 0x14; -static const uint8_t TCS34725_REGISTER_RDATAL = TCS34725_COMMAND_BIT | 0x16; -static const uint8_t TCS34725_REGISTER_GDATAL = TCS34725_COMMAND_BIT | 0x18; -static const uint8_t TCS34725_REGISTER_BDATAL = TCS34725_COMMAND_BIT | 0x1A; +static const uint8_t TCS34725_REGISTER_CRGBDATAL = TCS34725_COMMAND_BIT | 0x14; void TCS34725Component::setup() { ESP_LOGCONFIG(TAG, "Setting up TCS34725..."); @@ -181,27 +179,21 @@ void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, u } void TCS34725Component::update() { - uint16_t raw_c; - uint16_t raw_r; - uint16_t raw_g; - uint16_t raw_b; + uint8_t data[8]; // Buffer to hold the 8 bytes (2 bytes for each of the 4 channels) - if (this->read_data_register_(TCS34725_REGISTER_CDATAL, raw_c) != i2c::ERROR_OK) { - this->status_set_warning(); - return; - } - if (this->read_data_register_(TCS34725_REGISTER_RDATAL, raw_r) != i2c::ERROR_OK) { - this->status_set_warning(); - return; - } - if (this->read_data_register_(TCS34725_REGISTER_GDATAL, raw_g) != i2c::ERROR_OK) { - this->status_set_warning(); - return; - } - if (this->read_data_register_(TCS34725_REGISTER_BDATAL, raw_b) != i2c::ERROR_OK) { + // Perform burst + if (this->read_register(TCS34725_REGISTER_CRGBDATAL, data, 8) != i2c::ERROR_OK) { this->status_set_warning(); + ESP_LOGW(TAG, "Error reading TCS34725 sensor data"); return; } + + // Extract the data + uint16_t raw_c = encode_uint16(data[1], data[0]); // Clear channel + uint16_t raw_r = encode_uint16(data[3], data[2]); // Red channel + uint16_t raw_g = encode_uint16(data[5], data[4]); // Green channel + uint16_t raw_b = encode_uint16(data[7], data[6]); // Blue channel + ESP_LOGV(TAG, "Raw values clear=%d red=%d green=%d blue=%d", raw_c, raw_r, raw_g, raw_b); float channel_c; From c1a28ba5e249c83477f7d0a9bd000d65614db17e Mon Sep 17 00:00:00 2001 From: "@RubenKelevra" Date: Tue, 1 Oct 2024 00:03:42 +0200 Subject: [PATCH 0412/1052] tcs34725: Remove IR compensation and improve illuminance and color temperature handling in extreme conditions (#7492) --- esphome/components/tcs34725/tcs34725.cpp | 82 +++++++++++++----------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 9d682e094c..0830004b5a 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -73,20 +73,21 @@ float TCS34725Component::get_setup_priority() const { return setup_priority::DAT * @return Color temperature in degrees Kelvin */ void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, uint16_t b, uint16_t c) { - float r2, g2, b2; /* RGB values minus IR component */ - float sat; /* Digital saturation level */ - float ir; /* Inferred IR content */ + float sat; /* Digital saturation level */ - this->illuminance_ = 0; // Assign 0 value before calculation - this->color_temperature_ = 0; + this->illuminance_ = NAN; + this->color_temperature_ = NAN; - const float ga = this->glass_attenuation_; // Glass Attenuation Factor - static const float DF = 310.f; // Device Factor - static const float R_COEF = 0.136f; // - static const float G_COEF = 1.f; // used in lux computation - static const float B_COEF = -0.444f; // - static const float CT_COEF = 3810.f; // Color Temperature Coefficient - static const float CT_OFFSET = 1391.f; // Color Temperatuer Offset + const float ga = this->glass_attenuation_; // Glass Attenuation Factor + static const float DF = 310.f; // Device Factor + static const float R_COEF = 0.136f; // + static const float G_COEF = 1.f; // used in lux computation + static const float B_COEF = -0.444f; // + static const float CT_COEF = 3810.f; // Color Temperature Coefficient + static const float CT_OFFSET = 1391.f; // Color Temperatuer Offset + static const float MAX_ILLUMINANCE = 100000.0f; // Cap illuminance at 100,000 lux + static const float MAX_COLOR_TEMPERATURE = 15000.0f; // Maximum expected color temperature in Kelvin + static const float MIN_COLOR_TEMPERATURE = 1000.0f; // Maximum reasonable color temperature in Kelvin if (c == 0) { return; @@ -137,45 +138,48 @@ void TCS34725Component::calculate_temperature_and_lux_(uint16_t r, uint16_t g, u if (c >= sat) { if (this->integration_time_auto_) { ESP_LOGI(TAG, "Saturation too high, sample discarded, autogain ongoing"); + return; } else { - ESP_LOGW( - TAG, - "Saturation too high, sample with saturation %.1f and clear %d treat values carefully or use grey filter", - sat, c); - } - } - - /* AMS RGB sensors have no IR channel, so the IR content must be */ - /* calculated indirectly. */ - ir = ((r + g + b) > c) ? (r + g + b - c) / 2 : 0; - - /* Remove the IR component from the raw RGB values */ - r2 = r - ir; - g2 = g - ir; - b2 = b - ir; - - // discarding super low values? not recemmonded, and avoided by using auto gain. - if (r2 == 0) { - // legacy code - if (!this->integration_time_auto_) { ESP_LOGW(TAG, - "No light detected on red channel, switch to auto gain or adjust timing, values will be unreliable"); + "Saturation too high, sample with saturation %.1f and clear %d lux/color temperature cannot reliably " + "calculated, reduce integration/gain or use a grey filter.", + sat, c); return; } } // Lux Calculation (DN40 3.2) - float g1 = R_COEF * r2 + G_COEF * g2 + B_COEF * b2; + float g1 = R_COEF * (float) r + G_COEF * (float) g + B_COEF * (float) b; float cpl = (this->integration_time_ * this->gain_) / (ga * DF); - this->illuminance_ = g1 / cpl; + + this->illuminance_ = std::max(g1 / cpl, 0.0f); + + if (this->illuminance_ > MAX_ILLUMINANCE) { + ESP_LOGW(TAG, "Calculated illuminance greater than limit (%f), setting to NAN", this->illuminance_); + this->illuminance_ = NAN; + return; + } + + if (r == 0) { + ESP_LOGW(TAG, "Red channel is zero, cannot compute color temperature"); + return; + } // Color Temperature Calculation (DN40) /* A simple method of measuring color temp is to use the ratio of blue */ - /* to red light, taking IR cancellation into account. */ - this->color_temperature_ = (CT_COEF * b2) / /** Color temp coefficient. */ - r2 + - CT_OFFSET; /** Color temp offset. */ + /* to red light. */ + + this->color_temperature_ = (CT_COEF * (float) b) / (float) r + CT_OFFSET; + + // Ensure the color temperature stays within reasonable bounds + if (this->color_temperature_ < MIN_COLOR_TEMPERATURE) { + ESP_LOGW(TAG, "Calculated color temperature value too low (%f), setting to NAN", this->color_temperature_); + this->color_temperature_ = NAN; + } else if (this->color_temperature_ > MAX_COLOR_TEMPERATURE) { + ESP_LOGW(TAG, "Calculated color temperature value too high (%f), setting to NAN", this->color_temperature_); + this->color_temperature_ = NAN; + } } void TCS34725Component::update() { From d5fa17c316d763fc2bac14958f752b1a521aead3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:37:08 +1300 Subject: [PATCH 0413/1052] [rp2040] Always use maxgerhardt platform fork (#7514) --- esphome/components/rp2040/__init__.py | 27 +++++++++---------- esphome/wizard.py | 3 --- platformio.ini | 2 +- .../build_components_base.rp2040-ard.yaml | 3 --- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 8f1d26f780..925acb629d 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -71,6 +71,14 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: # return f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" +def _parse_platform_version(value): + value = cv.string(value) + if value.startswith("http"): + return value + + return f"https://github.com/maxgerhardt/platform-raspberrypi.git#{value}" + + # NOTE: Keep this in mind when updating the recommended version: # * The new version needs to be thoroughly validated before changing the # recommended version as otherwise a bunch of devices could be bricked @@ -82,10 +90,9 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: # - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 9, 4) -# The platformio/raspberrypi version to use for arduino frameworks -# - https://github.com/platformio/platform-raspberrypi/releases -# - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi -ARDUINO_PLATFORM_VERSION = cv.Version(1, 13, 0) +# The raspberrypi platform version to use for arduino frameworks +# - https://github.com/maxgerhardt/platform-raspberrypi/tags +RECOMMENDED_ARDUINO_PLATFORM_VERSION = "v1.2.0-gcc12" def _arduino_check_versions(value): @@ -111,7 +118,8 @@ def _arduino_check_versions(value): value[CONF_SOURCE] = source or _format_framework_arduino_version(version) value[CONF_PLATFORM_VERSION] = value.get( - CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION)) + CONF_PLATFORM_VERSION, + _parse_platform_version(RECOMMENDED_ARDUINO_PLATFORM_VERSION), ) if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: @@ -122,15 +130,6 @@ def _arduino_check_versions(value): return value -def _parse_platform_version(value): - try: - # if platform version is a valid version constraint, prefix the default package - cv.platformio_version_constraint(value) - return f"platformio/raspberrypi@{value}" - except cv.Invalid: - return value - - ARDUINO_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { diff --git a/esphome/wizard.py b/esphome/wizard.py index b1057189fd..eecbbdb172 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -90,9 +90,6 @@ esp32: RP2040_CONFIG = """ rp2040: board: {board} - framework: - # Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c """ BK72XX_CONFIG = """ diff --git a/platformio.ini b/platformio.ini index fc38923f65..bb122adc37 100644 --- a/platformio.ini +++ b/platformio.ini @@ -165,7 +165,7 @@ platform_packages = extends = common:arduino board_build.filesystem_size = 0.5m -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12 platform_packages = ; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.9.4/rp2040-3.9.4.zip diff --git a/tests/test_build_components/build_components_base.rp2040-ard.yaml b/tests/test_build_components/build_components_base.rp2040-ard.yaml index f9815578c5..4fb8d51333 100644 --- a/tests/test_build_components/build_components_base.rp2040-ard.yaml +++ b/tests/test_build_components/build_components_base.rp2040-ard.yaml @@ -4,9 +4,6 @@ esphome: rp2040: board: rpipicow - framework: - # Waiting for https://github.com/platformio/platform-raspberrypi/pull/36 - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c logger: level: VERY_VERBOSE From 4332301dbbe64e07964bb47d9e12c90776abe0c1 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 24 Sep 2024 17:50:44 -0700 Subject: [PATCH 0414/1052] fix bl0906 reset energy action (#7488) Co-authored-by: Samuel Sieb --- esphome/components/bl0906/sensor.py | 5 +++-- tests/components/bl0906/common.yaml | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/esphome/components/bl0906/sensor.py b/esphome/components/bl0906/sensor.py index bc370c9252..42c6f06092 100644 --- a/esphome/components/bl0906/sensor.py +++ b/esphome/components/bl0906/sensor.py @@ -145,8 +145,9 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( ), ) async def reset_energy_to_code(config, action_id, template_arg, args): - paren = await cg.get_variable(config[CONF_ID]) - return cg.new_Pvariable(action_id, template_arg, paren) + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var async def to_code(config): diff --git a/tests/components/bl0906/common.yaml b/tests/components/bl0906/common.yaml index 944791369c..29321a9471 100644 --- a/tests/components/bl0906/common.yaml +++ b/tests/components/bl0906/common.yaml @@ -8,6 +8,7 @@ uart: sensor: - platform: bl0906 + id: bl frequency: name: 'Frequency' temperature: @@ -60,3 +61,9 @@ sensor: name: 'Total_Energy' total_power: name: 'Total_Power' + +button: + - platform: template + id: reset + on_press: + - bl0906.reset_energy: bl From c2518cff89c60f3f9c37691e015132146b931a86 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:34:27 +1200 Subject: [PATCH 0415/1052] [config_validation] Fix bug with extras on schemas (#7497) --- esphome/voluptuous_schema.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/voluptuous_schema.py b/esphome/voluptuous_schema.py index 7f1573b443..15f9206f21 100644 --- a/esphome/voluptuous_schema.py +++ b/esphome/voluptuous_schema.py @@ -226,4 +226,6 @@ class _Schema(vol.Schema): if isinstance(schema, vol.Schema): schema = schema.schema ret = super().extend(schema, extra=extra) - return _Schema(ret.schema, extra=ret.extra, extra_schemas=self._extra_schemas) + return _Schema( + ret.schema, extra=ret.extra, extra_schemas=self._extra_schemas.copy() + ) From 050e2547ea714f4b72769f481f3e81a76fd719e0 Mon Sep 17 00:00:00 2001 From: Nick Kinnan Date: Sun, 29 Sep 2024 18:59:12 -0700 Subject: [PATCH 0416/1052] Prevent rp2040 randomly breaking the build (#7507) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/wizard.py | 2 +- platformio.ini | 2 +- .../test_build_components/build_components_base.rp2040-ard.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/wizard.py b/esphome/wizard.py index 319fb31938..b1057189fd 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -92,7 +92,7 @@ rp2040: board: {board} framework: # Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git + platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c """ BK72XX_CONFIG = """ diff --git a/platformio.ini b/platformio.ini index 7d912aaf54..583df7452c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -165,7 +165,7 @@ platform_packages = extends = common:arduino board_build.filesystem_size = 0.5m -platform = https://github.com/maxgerhardt/platform-raspberrypi.git +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c platform_packages = ; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.9.4/rp2040-3.9.4.zip diff --git a/tests/test_build_components/build_components_base.rp2040-ard.yaml b/tests/test_build_components/build_components_base.rp2040-ard.yaml index 6c6a27e0a7..f9815578c5 100644 --- a/tests/test_build_components/build_components_base.rp2040-ard.yaml +++ b/tests/test_build_components/build_components_base.rp2040-ard.yaml @@ -6,7 +6,7 @@ rp2040: board: rpipicow framework: # Waiting for https://github.com/platformio/platform-raspberrypi/pull/36 - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git + platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c logger: level: VERY_VERBOSE From 748bc85bfe4edfe7945bb74b8616f1123332a9ae Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:37:08 +1300 Subject: [PATCH 0417/1052] [rp2040] Always use maxgerhardt platform fork (#7514) --- esphome/components/rp2040/__init__.py | 27 +++++++++---------- esphome/wizard.py | 3 --- platformio.ini | 2 +- .../build_components_base.rp2040-ard.yaml | 3 --- 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 8f1d26f780..925acb629d 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -71,6 +71,14 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: # return f"~1.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" +def _parse_platform_version(value): + value = cv.string(value) + if value.startswith("http"): + return value + + return f"https://github.com/maxgerhardt/platform-raspberrypi.git#{value}" + + # NOTE: Keep this in mind when updating the recommended version: # * The new version needs to be thoroughly validated before changing the # recommended version as otherwise a bunch of devices could be bricked @@ -82,10 +90,9 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: # - https://api.registry.platformio.org/v3/packages/earlephilhower/tool/framework-arduinopico RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 9, 4) -# The platformio/raspberrypi version to use for arduino frameworks -# - https://github.com/platformio/platform-raspberrypi/releases -# - https://api.registry.platformio.org/v3/packages/platformio/platform/raspberrypi -ARDUINO_PLATFORM_VERSION = cv.Version(1, 13, 0) +# The raspberrypi platform version to use for arduino frameworks +# - https://github.com/maxgerhardt/platform-raspberrypi/tags +RECOMMENDED_ARDUINO_PLATFORM_VERSION = "v1.2.0-gcc12" def _arduino_check_versions(value): @@ -111,7 +118,8 @@ def _arduino_check_versions(value): value[CONF_SOURCE] = source or _format_framework_arduino_version(version) value[CONF_PLATFORM_VERSION] = value.get( - CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION)) + CONF_PLATFORM_VERSION, + _parse_platform_version(RECOMMENDED_ARDUINO_PLATFORM_VERSION), ) if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION: @@ -122,15 +130,6 @@ def _arduino_check_versions(value): return value -def _parse_platform_version(value): - try: - # if platform version is a valid version constraint, prefix the default package - cv.platformio_version_constraint(value) - return f"platformio/raspberrypi@{value}" - except cv.Invalid: - return value - - ARDUINO_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { diff --git a/esphome/wizard.py b/esphome/wizard.py index b1057189fd..eecbbdb172 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -90,9 +90,6 @@ esp32: RP2040_CONFIG = """ rp2040: board: {board} - framework: - # Required until https://github.com/platformio/platform-raspberrypi/pull/36 is merged - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c """ BK72XX_CONFIG = """ diff --git a/platformio.ini b/platformio.ini index 583df7452c..26617a933b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -165,7 +165,7 @@ platform_packages = extends = common:arduino board_build.filesystem_size = 0.5m -platform = https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c +platform = https://github.com/maxgerhardt/platform-raspberrypi.git#v1.2.0-gcc12 platform_packages = ; earlephilhower/framework-arduinopico@~1.20602.0 ; Cannot use the platformio package until old releases stop getting deleted earlephilhower/framework-arduinopico@https://github.com/earlephilhower/arduino-pico/releases/download/3.9.4/rp2040-3.9.4.zip diff --git a/tests/test_build_components/build_components_base.rp2040-ard.yaml b/tests/test_build_components/build_components_base.rp2040-ard.yaml index f9815578c5..4fb8d51333 100644 --- a/tests/test_build_components/build_components_base.rp2040-ard.yaml +++ b/tests/test_build_components/build_components_base.rp2040-ard.yaml @@ -4,9 +4,6 @@ esphome: rp2040: board: rpipicow - framework: - # Waiting for https://github.com/platformio/platform-raspberrypi/pull/36 - platform_version: https://github.com/maxgerhardt/platform-raspberrypi.git#5e87ae34ca025274df25b3303e9e9cb6c120123c logger: level: VERY_VERBOSE From f784e5c9f65401666ed7c676848c4ea66f506e91 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 1 Oct 2024 15:33:40 +1300 Subject: [PATCH 0418/1052] Bump version to 2024.9.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 29084c8955..533b547bcf 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.9.1" +__version__ = "2024.9.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 215f26fbe41d75e5742a6e0b55dc01a10a348a38 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 1 Oct 2024 19:08:12 -0500 Subject: [PATCH 0419/1052] [CI] Remove ``sorted`` from library include dirs (#7526) --- script/clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/clang-tidy b/script/clang-tidy index 5bb93846b2..a5da9fd3b0 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -98,7 +98,7 @@ def clang_options(idedata): cmd.extend(["-isystem", directory]) # add library include directories using -isystem to suppress their errors - for directory in sorted(set(idedata["includes"]["build"])): + for directory in set(idedata["includes"]["build"]): # skip our own directories, we add those later if ( not directory.startswith(f"{root_path}/") From d00e0eb2d6194be562219d1313d18d0236280307 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 1 Oct 2024 21:33:35 -0500 Subject: [PATCH 0420/1052] [wifi] Fix error message when no custom MAC is set (#7515) --- esphome/core/helpers.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 2e99b0df70..61fd23508e 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -44,9 +44,7 @@ #endif #ifdef USE_ESP32 #include "esp32/rom/crc.h" -#endif -#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC) #include "esp_efuse.h" #include "esp_efuse_table.h" #endif @@ -713,10 +711,11 @@ void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); } bool has_custom_mac_address() { #ifdef USE_ESP32 uint8_t mac[6]; -#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC) - return (esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac); + // do not use 'esp_efuse_mac_get_custom(mac)' because it drops an error in the logs whenever it fails +#ifndef USE_ESP32_VARIANT_ESP32 + return (esp_efuse_read_field_blob(ESP_EFUSE_USER_DATA_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac); #else - return (esp_efuse_mac_get_custom(mac) == ESP_OK) && mac_address_is_valid(mac); + return (esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48) == ESP_OK) && mac_address_is_valid(mac); #endif #else return false; From 0d80286bb3cbd8b0591e2789bf180cc2c05e8cab Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 2 Oct 2024 03:27:46 -0500 Subject: [PATCH 0421/1052] [esp32] Add ``ignore_efuse_custom_mac`` config var (#7527) --- esphome/components/esp32/__init__.py | 6 ++++++ esphome/const.py | 1 + esphome/core/helpers.cpp | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 9cb9ac257a..b7d3ef4a6d 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -13,6 +13,7 @@ from esphome.const import ( CONF_COMPONENTS, CONF_ESPHOME, CONF_FRAMEWORK, + CONF_IGNORE_EFUSE_CUSTOM_MAC, CONF_IGNORE_EFUSE_MAC_CRC, CONF_NAME, CONF_PATH, @@ -401,6 +402,9 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( }, cv.Optional(CONF_ADVANCED, default={}): cv.Schema( { + cv.Optional( + CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False + ): cv.boolean, cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean, } ), @@ -526,6 +530,8 @@ async def to_code(config): for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) + if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: + cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_MAC_CRC]: cg.add_define("USE_ESP32_IGNORE_EFUSE_MAC_CRC") if (framework_ver.major, framework_ver.minor) >= (4, 4): diff --git a/esphome/const.py b/esphome/const.py index 40b7a1c419..830f139077 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -376,6 +376,7 @@ CONF_IDLE_ACTION = "idle_action" CONF_IDLE_LEVEL = "idle_level" CONF_IDLE_TIME = "idle_time" CONF_IF = "if" +CONF_IGNORE_EFUSE_CUSTOM_MAC = "ignore_efuse_custom_mac" CONF_IGNORE_EFUSE_MAC_CRC = "ignore_efuse_mac_crc" CONF_IGNORE_OUT_OF_RANGE = "ignore_out_of_range" CONF_IGNORE_PIN_VALIDATION_ERROR = "ignore_pin_validation_error" diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 61fd23508e..492ab6dd1a 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -709,7 +709,7 @@ void set_mac_address(uint8_t *mac) { esp_base_mac_addr_set(mac); } #endif bool has_custom_mac_address() { -#ifdef USE_ESP32 +#if defined(USE_ESP32) && !defined(USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC) uint8_t mac[6]; // do not use 'esp_efuse_mac_get_custom(mac)' because it drops an error in the logs whenever it fails #ifndef USE_ESP32_VARIANT_ESP32 From 361b6ab961e12634fc040ec11e45ab5ae39359f6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 2 Oct 2024 22:27:32 +1300 Subject: [PATCH 0422/1052] [mics_4514] Move consts to consts.py (#7528) --- esphome/components/mics_4514/sensor.py | 37 ++++++++------------------ esphome/const.py | 6 +++++ 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/esphome/components/mics_4514/sensor.py b/esphome/components/mics_4514/sensor.py index 80c3524f66..59ccba235a 100644 --- a/esphome/components/mics_4514/sensor.py +++ b/esphome/components/mics_4514/sensor.py @@ -1,10 +1,14 @@ import esphome.codegen as cg +from esphome.components import i2c, sensor import esphome.config_validation as cv - -from esphome.components import sensor, i2c - from esphome.const import ( + CONF_AMMONIA, + CONF_CARBON_MONOXIDE, + CONF_ETHANOL, + CONF_HYDROGEN, CONF_ID, + CONF_METHANE, + CONF_NITROGEN_DIOXIDE, STATE_CLASS_MEASUREMENT, UNIT_PARTS_PER_MILLION, ) @@ -12,13 +16,6 @@ from esphome.const import ( CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["i2c"] -CONF_CARBON_MONOXIDE = "carbon_monoxide" -CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide" -CONF_METHANE = "methane" -CONF_ETHANOL = "ethanol" -CONF_HYDROGEN = "hydrogen" -CONF_AMMONIA = "ammonia" - mics_4514_ns = cg.esphome_ns.namespace("mics_4514") MICS4514Component = mics_4514_ns.class_( @@ -31,6 +28,7 @@ SENSORS = [ CONF_ETHANOL, CONF_HYDROGEN, CONF_AMMONIA, + CONF_NITROGEN_DIOXIDE, ] common_sensor_schema = sensor.sensor_schema( @@ -40,16 +38,7 @@ common_sensor_schema = sensor.sensor_schema( ) CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(MICS4514Component), - cv.Optional(CONF_NITROGEN_DIOXIDE): sensor.sensor_schema( - unit_of_measurement=UNIT_PARTS_PER_MILLION, - state_class=STATE_CLASS_MEASUREMENT, - accuracy_decimals=2, - ), - } - ) + cv.Schema({cv.GenerateID(): cv.declare_id(MICS4514Component)}) .extend({cv.Optional(sensor_type): common_sensor_schema for sensor_type in SENSORS}) .extend(i2c.i2c_device_schema(0x75)) .extend(cv.polling_component_schema("60s")) @@ -62,10 +51,6 @@ async def to_code(config): await i2c.register_i2c_device(var, config) for sensor_type in SENSORS: - if sensor_type in config: - sens = await sensor.new_sensor(config[sensor_type]) + if sensor_config := config.get(sensor_type): + sens = await sensor.new_sensor(sensor_config) cg.add(getattr(var, f"set_{sensor_type}_sensor")(sens)) - - if CONF_NITROGEN_DIOXIDE in config: - sens = await sensor.new_sensor(config[CONF_NITROGEN_DIOXIDE]) - cg.add(var.set_nitrogen_dioxide_sensor(sens)) diff --git a/esphome/const.py b/esphome/const.py index 830f139077..bfb0167282 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -53,6 +53,7 @@ CONF_ALLOW_OTHER_USES = "allow_other_uses" CONF_ALPHA = "alpha" CONF_ALTITUDE = "altitude" CONF_AMBIENT_LIGHT = "ambient_light" +CONF_AMMONIA = "ammonia" CONF_ANALOG = "analog" CONF_AND = "and" CONF_ANGLE = "angle" @@ -110,6 +111,7 @@ CONF_CALIBRATE_LINEAR = "calibrate_linear" CONF_CALIBRATION = "calibration" CONF_CAPACITANCE = "capacitance" CONF_CAPACITY = "capacity" +CONF_CARBON_MONOXIDE = "carbon_monoxide" CONF_CARRIER_DUTY_PERCENT = "carrier_duty_percent" CONF_CARRIER_FREQUENCY = "carrier_frequency" CONF_CERTIFICATE = "certificate" @@ -263,6 +265,7 @@ CONF_ENUM_DATAPOINT = "enum_datapoint" CONF_EQUATION = "equation" CONF_ESP8266_DISABLE_SSL_SUPPORT = "esp8266_disable_ssl_support" CONF_ESPHOME = "esphome" +CONF_ETHANOL = "ethanol" CONF_ETHERNET = "ethernet" CONF_EVENT = "event" CONF_EVENT_TYPE = "event_type" @@ -361,6 +364,7 @@ CONF_HOURS = "hours" CONF_HSYNC_PIN = "hsync_pin" CONF_HUMIDITY = "humidity" CONF_HUMIDITY_SENSOR = "humidity_sensor" +CONF_HYDROGEN = "hydrogen" CONF_HYSTERESIS = "hysteresis" CONF_I2C = "i2c" CONF_I2C_ID = "i2c_id" @@ -477,6 +481,7 @@ CONF_MEDIA_PLAYER = "media_player" CONF_MEDIUM = "medium" CONF_MEMORY_BLOCKS = "memory_blocks" CONF_MESSAGE = "message" +CONF_METHANE = "methane" CONF_METHOD = "method" CONF_MICROPHONE = "microphone" CONF_MIN_BRIGHTNESS = "min_brightness" @@ -523,6 +528,7 @@ CONF_NBITS = "nbits" CONF_NEC = "nec" CONF_NETWORKS = "networks" CONF_NEW_PASSWORD = "new_password" +CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide" CONF_NOISE_LEVEL = "noise_level" CONF_NUM_ATTEMPTS = "num_attempts" CONF_NUM_CHANNELS = "num_channels" From e57a1ff42d344d6da5fb0496145585ce25d14bb5 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 2 Oct 2024 19:54:12 +0100 Subject: [PATCH 0423/1052] =?UTF-8?q?Fix=20parsing=20of=20=C2=B5s=20time?= =?UTF-8?q?=20periods=20in=20config=20(#7495)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esphome/config_validation.py | 1 + .../components/esp32_rmt_led_strip/test.esp32-c3-idf.yaml | 8 ++++---- tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index e55879e37e..a7525a62dd 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -750,6 +750,7 @@ def time_period_str_unit(value): "ns": "nanoseconds", "nanoseconds": "nanoseconds", "us": "microseconds", + "µs": "microseconds", "microseconds": "microseconds", "ms": "milliseconds", "milliseconds": "milliseconds", 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 b226d1de06..8d04d3370b 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 @@ -12,7 +12,7 @@ light: num_leds: 60 rmt_channel: 1 rgb_order: RGB - bit0_high: 100us - bit0_low: 100us - bit1_high: 100us - bit1_low: 100us + bit0_high: 100µs + bit0_low: 100µs + bit1_high: 100µs + bit1_low: 100µs 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 d51a66451f..6e1763b339 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml @@ -12,7 +12,7 @@ light: num_leds: 60 rmt_channel: 2 rgb_order: RGB - bit0_high: 100us - bit0_low: 100us - bit1_high: 100us - bit1_low: 100us + bit0_high: 100µs + bit0_low: 100µs + bit1_high: 100µs + bit1_low: 100µs From 523eedbc51261ed1f1a330026139dd22e42c69d3 Mon Sep 17 00:00:00 2001 From: RFDarter Date: Thu, 3 Oct 2024 02:34:12 +0200 Subject: [PATCH 0424/1052] [web_server] Expose detail=all on all components (#7531) --- esphome/components/web_server/web_server.cpp | 156 +++++++++++++++---- 1 file changed, 127 insertions(+), 29 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 3bb7eee8f1..dc27db2f41 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -219,9 +219,16 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM for (sensor::Sensor *obj : App.get_sensors()) { if (obj->get_object_id() != match.id) continue; - std::string data = this->sensor_json(obj, obj->state, DETAIL_STATE); - request->send(200, "application/json", data.c_str()); - return; + if (request->method() == HTTP_GET && match.method.empty()) { + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->sensor_json(obj, obj->state, detail); + request->send(200, "application/json", data.c_str()); + return; + } } request->send(404); } @@ -257,9 +264,16 @@ void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const for (text_sensor::TextSensor *obj : App.get_text_sensors()) { if (obj->get_object_id() != match.id) continue; - std::string data = this->text_sensor_json(obj, obj->state, DETAIL_STATE); - request->send(200, "application/json", data.c_str()); - return; + if (request->method() == HTTP_GET && match.method.empty()) { + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->text_sensor_json(obj, obj->state, detail); + request->send(200, "application/json", data.c_str()); + return; + } } request->send(404); } @@ -288,7 +302,12 @@ void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlM continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->switch_json(obj, obj->state, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->switch_json(obj, obj->state, detail); request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { this->schedule_([obj]() { obj->toggle(); }); @@ -324,7 +343,15 @@ void WebServer::handle_button_request(AsyncWebServerRequest *request, const UrlM for (button::Button *obj : App.get_buttons()) { if (obj->get_object_id() != match.id) continue; - if (match.method == "press") { + if (request->method() == HTTP_GET && match.method.empty()) { + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->button_json(obj, detail); + request->send(200, "application/json", data.c_str()); + } else if (match.method == "press") { this->schedule_([obj]() { obj->press(); }); request->send(200); return; @@ -357,9 +384,16 @@ void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, con for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) { if (obj->get_object_id() != match.id) continue; - std::string data = this->binary_sensor_json(obj, obj->state, DETAIL_STATE); - request->send(200, "application/json", data.c_str()); - return; + if (request->method() == HTTP_GET && match.method.empty()) { + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->binary_sensor_json(obj, obj->state, detail); + request->send(200, "application/json", data.c_str()); + return; + } } request->send(404); } @@ -388,7 +422,12 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->fan_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->fan_json(obj, detail); request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { this->schedule_([obj]() { obj->toggle().perform(); }); @@ -466,7 +505,12 @@ void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMa continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->light_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->light_json(obj, detail); request->send(200, "application/json", data.c_str()); } else if (match.method == "toggle") { this->schedule_([obj]() { obj->toggle().perform(); }); @@ -577,9 +621,14 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->cover_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->cover_json(obj, detail); request->send(200, "application/json", data.c_str()); - continue; + return; } auto call = obj->make_call(); @@ -653,7 +702,12 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->number_json(obj, obj->state, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->number_json(obj, obj->state, detail); request->send(200, "application/json", data.c_str()); return; } @@ -717,8 +771,13 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat for (auto *obj : App.get_dates()) { if (obj->get_object_id() != match.id) continue; - if (request->method() == HTTP_GET) { - std::string data = this->date_json(obj, DETAIL_STATE); + if (request->method() == HTTP_GET && match.method.empty()) { + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->date_json(obj, detail); request->send(200, "application/json", data.c_str()); return; } @@ -772,7 +831,12 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat if (obj->get_object_id() != match.id) continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->time_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->time_json(obj, detail); request->send(200, "application/json", data.c_str()); return; } @@ -825,7 +889,12 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur if (obj->get_object_id() != match.id) continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->datetime_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->datetime_json(obj, detail); request->send(200, "application/json", data.c_str()); return; } @@ -880,8 +949,13 @@ void WebServer::handle_text_request(AsyncWebServerRequest *request, const UrlMat continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->text_json(obj, obj->state, DETAIL_STATE); - request->send(200, "text/json", data.c_str()); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->text_json(obj, obj->state, detail); + request->send(200, "application/json", data.c_str()); return; } if (match.method != "set") { @@ -995,11 +1069,15 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->climate_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->climate_json(obj, detail); request->send(200, "application/json", data.c_str()); return; } - if (match.method != "set") { request->send(404); return; @@ -1149,7 +1227,12 @@ void WebServer::handle_lock_request(AsyncWebServerRequest *request, const UrlMat continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->lock_json(obj, obj->state, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->lock_json(obj, obj->state, detail); request->send(200, "application/json", data.c_str()); } else if (match.method == "lock") { this->schedule_([obj]() { obj->lock(); }); @@ -1192,9 +1275,14 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->valve_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->valve_json(obj, detail); request->send(200, "application/json", data.c_str()); - continue; + return; } auto call = obj->make_call(); @@ -1257,7 +1345,12 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->alarm_control_panel_json(obj, obj->get_state(), DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->alarm_control_panel_json(obj, obj->get_state(), detail); request->send(200, "application/json", data.c_str()); return; } @@ -1314,7 +1407,12 @@ void WebServer::handle_update_request(AsyncWebServerRequest *request, const UrlM continue; if (request->method() == HTTP_GET && match.method.empty()) { - std::string data = this->update_json(obj, DETAIL_STATE); + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->update_json(obj, detail); request->send(200, "application/json", data.c_str()); return; } From 1cf4818640212ad7f980ef7117739e51c0a35fb0 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 4 Oct 2024 03:07:49 -0500 Subject: [PATCH 0425/1052] [CI] Use a list when reading idedata for includes (#7535) --- script/clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/clang-tidy b/script/clang-tidy index a5da9fd3b0..61199edce3 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -98,7 +98,7 @@ def clang_options(idedata): cmd.extend(["-isystem", directory]) # add library include directories using -isystem to suppress their errors - for directory in set(idedata["includes"]["build"]): + for directory in list(idedata["includes"]["build"]): # skip our own directories, we add those later if ( not directory.startswith(f"{root_path}/") From 0a62106b7bb52dd95d93cf2825edd8065be4821d Mon Sep 17 00:00:00 2001 From: guillempages Date: Sat, 5 Oct 2024 09:07:32 +0200 Subject: [PATCH 0426/1052] [image] Use "puremagic" instead of "magic" python module (#7536) --- esphome/components/image/__init__.py | 9 ++++----- requirements.txt | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index e80ba4498f..c72417bcda 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -6,7 +6,7 @@ import logging from pathlib import Path import re -from magic import Magic +import puremagic from esphome import core, external_files import esphome.codegen as cg @@ -237,8 +237,8 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA) def load_svg_image(file: bytes, resize: tuple[int, int]): - # Local import only to allow "validate_pillow_installed" to run *before* importing it - # This import is only needed in case of SVG images; adding it + # Local imports only to allow "validate_pillow_installed" to run *before* importing it + # cairosvg is only needed in case of SVG images; adding it # to the top would force configurations not using SVG to also have it # installed for no reason. from cairosvg import svg2png @@ -281,8 +281,7 @@ async def to_code(config): except Exception as e: raise core.EsphomeError(f"Could not load image file {path}: {e}") - mime = Magic(mime=True) - file_type = mime.from_buffer(file_contents) + file_type = puremagic.from_string(file_contents, mime=True) resize = config.get(CONF_RESIZE) if "svg" in file_type: diff --git a/requirements.txt b/requirements.txt index 3e658de8ad..3ffd364d87 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ click==8.1.7 esphome-dashboard==20240620.0 aioesphomeapi==24.6.2 zeroconf==0.132.2 -python-magic==0.4.27 +puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import # esp-idf requires this, but doesn't bundle it by default From b3cff566eb1ba79eb86a2b31df30e18691c9f241 Mon Sep 17 00:00:00 2001 From: guillempages Date: Sun, 6 Oct 2024 00:44:18 +0200 Subject: [PATCH 0427/1052] [lvgl] Remap image to img in "set_style_*" (#7546) --- esphome/components/lvgl/styles.py | 5 +++-- tests/components/lvgl/lvgl-package.yaml | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/lvgl/styles.py b/esphome/components/lvgl/styles.py index 26c2694a52..030db5fd22 100644 --- a/esphome/components/lvgl/styles.py +++ b/esphome/components/lvgl/styles.py @@ -12,7 +12,7 @@ from .defines import ( ) from .helpers import add_lv_use from .lvcode import LambdaContext, LocalVariable, lv, lv_assign, lv_variable -from .schemas import ALL_STYLES +from .schemas import ALL_STYLES, STYLE_REMAP from .types import lv_lambda_t, lv_obj_t, lv_obj_t_ptr from .widgets import Widget, add_widgets, set_obj_properties, theme_widget_map from .widgets.obj import obj_spec @@ -31,7 +31,8 @@ async def styles_to_code(config): value = await validator.process(value) if isinstance(value, list): value = "|".join(value) - lv.call(f"style_set_{prop}", svar, literal(value)) + remapped_prop = STYLE_REMAP.get(prop, prop) + lv.call(f"style_set_{remapped_prop}", svar, literal(value)) async def theme_to_code(config): diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index a3ed3047be..2b72f31770 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -67,6 +67,9 @@ lvgl: border_width: 2 pad_all: 4 align: center + - id: image_recolor + image_recolor: 0x10ca1e + image_recolor_opa: cover touchscreens: - touchscreen_id: tft_touch From 6a8e88b1cc5c47f23c394cf01328a96f907d671b Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 6 Oct 2024 12:49:52 -0700 Subject: [PATCH 0428/1052] CSE7766 needs even parity (#7549) --- esphome/components/cse7766/sensor.py | 2 +- tests/components/cse7766/test.esp32-ard.yaml | 1 + tests/components/cse7766/test.esp32-c3-ard.yaml | 1 + tests/components/cse7766/test.esp32-c3-idf.yaml | 1 + tests/components/cse7766/test.esp32-idf.yaml | 1 + tests/components/cse7766/test.esp8266-ard.yaml | 1 + tests/components/cse7766/test.rp2040-ard.yaml | 1 + 7 files changed, 7 insertions(+), 1 deletion(-) diff --git a/esphome/components/cse7766/sensor.py b/esphome/components/cse7766/sensor.py index ecb59c4b5f..b5b11a661e 100644 --- a/esphome/components/cse7766/sensor.py +++ b/esphome/components/cse7766/sensor.py @@ -79,7 +79,7 @@ CONFIG_SCHEMA = cv.Schema( } ).extend(uart.UART_DEVICE_SCHEMA) FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( - "cse7766", baud_rate=4800, require_rx=True + "cse7766", baud_rate=4800, parity="EVEN", require_rx=True ) diff --git a/tests/components/cse7766/test.esp32-ard.yaml b/tests/components/cse7766/test.esp32-ard.yaml index f94cd0f7d8..5542b52824 100644 --- a/tests/components/cse7766/test.esp32-ard.yaml +++ b/tests/components/cse7766/test.esp32-ard.yaml @@ -5,6 +5,7 @@ uart: rx_pin: number: 16 baud_rate: 4800 + parity: EVEN sensor: - platform: cse7766 diff --git a/tests/components/cse7766/test.esp32-c3-ard.yaml b/tests/components/cse7766/test.esp32-c3-ard.yaml index 432cc0a80e..d27c9d4463 100644 --- a/tests/components/cse7766/test.esp32-c3-ard.yaml +++ b/tests/components/cse7766/test.esp32-c3-ard.yaml @@ -5,6 +5,7 @@ uart: rx_pin: number: 5 baud_rate: 4800 + parity: EVEN sensor: - platform: cse7766 diff --git a/tests/components/cse7766/test.esp32-c3-idf.yaml b/tests/components/cse7766/test.esp32-c3-idf.yaml index 432cc0a80e..d27c9d4463 100644 --- a/tests/components/cse7766/test.esp32-c3-idf.yaml +++ b/tests/components/cse7766/test.esp32-c3-idf.yaml @@ -5,6 +5,7 @@ uart: rx_pin: number: 5 baud_rate: 4800 + parity: EVEN sensor: - platform: cse7766 diff --git a/tests/components/cse7766/test.esp32-idf.yaml b/tests/components/cse7766/test.esp32-idf.yaml index f94cd0f7d8..5542b52824 100644 --- a/tests/components/cse7766/test.esp32-idf.yaml +++ b/tests/components/cse7766/test.esp32-idf.yaml @@ -5,6 +5,7 @@ uart: rx_pin: number: 16 baud_rate: 4800 + parity: EVEN sensor: - platform: cse7766 diff --git a/tests/components/cse7766/test.esp8266-ard.yaml b/tests/components/cse7766/test.esp8266-ard.yaml index 432cc0a80e..d27c9d4463 100644 --- a/tests/components/cse7766/test.esp8266-ard.yaml +++ b/tests/components/cse7766/test.esp8266-ard.yaml @@ -5,6 +5,7 @@ uart: rx_pin: number: 5 baud_rate: 4800 + parity: EVEN sensor: - platform: cse7766 diff --git a/tests/components/cse7766/test.rp2040-ard.yaml b/tests/components/cse7766/test.rp2040-ard.yaml index 432cc0a80e..d27c9d4463 100644 --- a/tests/components/cse7766/test.rp2040-ard.yaml +++ b/tests/components/cse7766/test.rp2040-ard.yaml @@ -5,6 +5,7 @@ uart: rx_pin: number: 5 baud_rate: 4800 + parity: EVEN sensor: - platform: cse7766 From e31a96bfe2392e68411019cdc57f0d0b75d65783 Mon Sep 17 00:00:00 2001 From: Tobias Hoff Date: Sun, 6 Oct 2024 21:53:57 +0200 Subject: [PATCH 0429/1052] Allow use of all pulse count unit channels if needed. (#7550) --- .../components/pulse_counter/pulse_counter_sensor.cpp | 9 ++++++++- esphome/components/pulse_counter/pulse_counter_sensor.h | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 281a61a66a..bd3e4fcbef 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -53,12 +53,19 @@ pulse_counter_t BasicPulseCounterStorage::read_raw_value() { #ifdef HAS_PCNT bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { static pcnt_unit_t next_pcnt_unit = PCNT_UNIT_0; + static pcnt_channel_t next_pcnt_channel = PCNT_CHANNEL_0; this->pin = pin; this->pin->setup(); this->pcnt_unit = next_pcnt_unit; + this->pcnt_channel = next_pcnt_channel; next_pcnt_unit = pcnt_unit_t(int(next_pcnt_unit) + 1); + if (int(next_pcnt_unit) >= PCNT_UNIT_0 + PCNT_UNIT_MAX) { + next_pcnt_unit = PCNT_UNIT_0; + next_pcnt_channel = pcnt_channel_t(int(next_pcnt_channel) + 1); + } ESP_LOGCONFIG(TAG, " PCNT Unit Number: %u", this->pcnt_unit); + ESP_LOGCONFIG(TAG, " PCNT Channel Number: %u", this->pcnt_channel); pcnt_count_mode_t rising = PCNT_COUNT_DIS, falling = PCNT_COUNT_DIS; switch (this->rising_edge_mode) { @@ -94,7 +101,7 @@ bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { .counter_h_lim = 0, .counter_l_lim = 0, .unit = this->pcnt_unit, - .channel = PCNT_CHANNEL_0, + .channel = this->pcnt_channel, }; esp_err_t error = pcnt_unit_config(&pcnt_config); if (error != ESP_OK) { diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index ef9f73f95c..fc3d8711d1 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -55,6 +55,7 @@ struct HwPulseCounterStorage : public PulseCounterStorageBase { pulse_counter_t read_raw_value() override; pcnt_unit_t pcnt_unit; + pcnt_channel_t pcnt_channel; }; #endif From 949e61db8d6c7642784ee79325ed7f1c49b1596c Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 6 Oct 2024 15:00:09 -0500 Subject: [PATCH 0430/1052] [bang-bang] Remove ``assert()`` (#7533) --- esphome/components/bang_bang/bang_bang_climate.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index b34764907f..aed97b2890 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -157,8 +157,11 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) { default: trig = nullptr; } - assert(trig != nullptr); - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } else { + ESP_LOGW(TAG, "trig not set - unsupported action"); + } this->action = action; this->prev_trigger_ = trig; this->publish_state(); From 1c0ee5ae6b499d38c656dac2cc3725c3bc8e52e3 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 6 Oct 2024 15:01:11 -0500 Subject: [PATCH 0431/1052] [thermostat] Remove ``assert()``s (#7544) --- .../thermostat/thermostat_climate.cpp | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 26be6ba53a..f7b3410df9 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -502,8 +502,9 @@ void ThermostatClimate::switch_to_action_(climate::ClimateAction action, bool pu } this->action = action; this->prev_action_trigger_ = trig; - assert(trig != nullptr); - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } // if enabled, call the fan_only action with cooling/heating actions if (trig_fan != nullptr) { ESP_LOGVV(TAG, "Calling FAN_ONLY action with HEATING/COOLING action"); @@ -564,7 +565,6 @@ void ThermostatClimate::trigger_supplemental_action_() { } if (trig != nullptr) { - assert(trig != nullptr); trig->trigger(); } } @@ -634,8 +634,9 @@ void ThermostatClimate::switch_to_fan_mode_(climate::ClimateFanMode fan_mode, bo this->prev_fan_mode_trigger_ = nullptr; } this->start_timer_(thermostat::TIMER_FAN_MODE); - assert(trig != nullptr); - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } this->prev_fan_mode_ = fan_mode; this->prev_fan_mode_trigger_ = trig; } @@ -678,8 +679,9 @@ void ThermostatClimate::switch_to_mode_(climate::ClimateMode mode, bool publish_ mode = climate::CLIMATE_MODE_HEAT_COOL; // trig = this->auto_mode_trigger_; } - assert(trig != nullptr); - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } this->mode = mode; this->prev_mode_ = mode; this->prev_mode_trigger_ = trig; @@ -718,8 +720,9 @@ void ThermostatClimate::switch_to_swing_mode_(climate::ClimateSwingMode swing_mo swing_mode = climate::CLIMATE_SWING_OFF; // trig = this->swing_mode_off_trigger_; } - assert(trig != nullptr); - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } this->swing_mode = swing_mode; this->prev_swing_mode_ = swing_mode; this->prev_swing_mode_trigger_ = trig; @@ -867,8 +870,9 @@ void ThermostatClimate::check_temperature_change_trigger_() { } // trigger the action Trigger<> *trig = this->temperature_change_trigger_; - assert(trig != nullptr); - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } } bool ThermostatClimate::cooling_required_() { @@ -998,9 +1002,10 @@ void ThermostatClimate::change_preset_(climate::ClimatePreset preset) { this->preset.value() != preset) { // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; - assert(trig != nullptr); this->preset = preset; - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } this->refresh(); ESP_LOGI(TAG, "Preset %s applied", LOG_STR_ARG(climate::climate_preset_to_string(preset))); @@ -1023,9 +1028,10 @@ void ThermostatClimate::change_custom_preset_(const std::string &custom_preset) this->custom_preset.value() != custom_preset) { // Fire any preset changed trigger if defined Trigger<> *trig = this->preset_change_trigger_; - assert(trig != nullptr); this->custom_preset = custom_preset; - trig->trigger(); + if (trig != nullptr) { + trig->trigger(); + } this->refresh(); ESP_LOGI(TAG, "Custom preset %s applied", custom_preset.c_str()); From 56e305f986170bc4667c4d4a40fd41b8f262f6d0 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 6 Oct 2024 15:01:43 -0500 Subject: [PATCH 0432/1052] [bedjet_codec] Remove ``assert()`` (#7543) --- esphome/components/bedjet/bedjet_codec.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/bedjet/bedjet_codec.cpp b/esphome/components/bedjet/bedjet_codec.cpp index 7e90621235..9a312e226c 100644 --- a/esphome/components/bedjet/bedjet_codec.cpp +++ b/esphome/components/bedjet/bedjet_codec.cpp @@ -13,8 +13,10 @@ float bedjet_temp_to_f(const uint8_t temp) { /** Cleans up the packet before sending. */ BedjetPacket *BedjetCodec::clean_packet_() { - // So far no commands require more than 2 bytes of data. - assert(this->packet_.data_length <= 2); + // So far no commands require more than 2 bytes of data + if (this->packet_.data_length > 2) { + ESP_LOGW(TAG, "Packet may be malformed"); + } for (int i = this->packet_.data_length; i < 2; i++) { this->packet_.data[i] = '\0'; } From 9f85d99a2291e8b78aa0afe007e5bcdbbbaf50bd Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 6 Oct 2024 15:59:42 -0500 Subject: [PATCH 0433/1052] [audio_dac] [aic3204] Add new component + platform (#7505) --- CODEOWNERS | 2 + esphome/components/aic3204/__init__.py | 0 esphome/components/aic3204/aic3204.cpp | 173 ++++++++++++++++++ esphome/components/aic3204/aic3204.h | 88 +++++++++ esphome/components/aic3204/audio_dac.py | 52 ++++++ esphome/components/aic3204/automation.h | 23 +++ esphome/components/audio_dac/__init__.py | 57 ++++++ esphome/components/audio_dac/audio_dac.h | 23 +++ esphome/components/audio_dac/automation.h | 43 +++++ tests/components/aic3204/common.yaml | 15 ++ tests/components/aic3204/test.esp32-ard.yaml | 5 + .../components/aic3204/test.esp32-c3-ard.yaml | 5 + .../components/aic3204/test.esp32-c3-idf.yaml | 5 + tests/components/aic3204/test.esp32-idf.yaml | 5 + .../components/aic3204/test.esp8266-ard.yaml | 5 + 15 files changed, 501 insertions(+) create mode 100644 esphome/components/aic3204/__init__.py create mode 100644 esphome/components/aic3204/aic3204.cpp create mode 100644 esphome/components/aic3204/aic3204.h create mode 100644 esphome/components/aic3204/audio_dac.py create mode 100644 esphome/components/aic3204/automation.h create mode 100644 esphome/components/audio_dac/__init__.py create mode 100644 esphome/components/audio_dac/audio_dac.h create mode 100644 esphome/components/audio_dac/automation.h create mode 100644 tests/components/aic3204/common.yaml create mode 100644 tests/components/aic3204/test.esp32-ard.yaml create mode 100644 tests/components/aic3204/test.esp32-c3-ard.yaml create mode 100644 tests/components/aic3204/test.esp32-c3-idf.yaml create mode 100644 tests/components/aic3204/test.esp32-idf.yaml create mode 100644 tests/components/aic3204/test.esp8266-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 3f5ff46c02..db48e25743 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -24,6 +24,7 @@ esphome/components/ade7953_i2c/* @angelnu esphome/components/ade7953_spi/* @angelnu esphome/components/ads1118/* @solomondg1 esphome/components/ags10/* @mak-42 +esphome/components/aic3204/* @kbx81 esphome/components/airthings_ble/* @jeromelaban esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau esphome/components/airthings_wave_mini/* @ncareau @@ -47,6 +48,7 @@ esphome/components/at581x/* @X-Ryl669 esphome/components/atc_mithermometer/* @ahpohl esphome/components/atm90e26/* @danieltwagner esphome/components/atm90e32/* @circuitsetup @descipher +esphome/components/audio_dac/* @kbx81 esphome/components/b_parasite/* @rbaron esphome/components/ballu/* @bazuchan esphome/components/bang_bang/* @OttoWinter diff --git a/esphome/components/aic3204/__init__.py b/esphome/components/aic3204/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/aic3204/aic3204.cpp b/esphome/components/aic3204/aic3204.cpp new file mode 100644 index 0000000000..0560f2366b --- /dev/null +++ b/esphome/components/aic3204/aic3204.cpp @@ -0,0 +1,173 @@ +#include "aic3204.h" + +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace aic3204 { + +static const char *const TAG = "aic3204"; + +#define ERROR_CHECK(err, msg) \ + if (!(err)) { \ + ESP_LOGE(TAG, msg); \ + this->mark_failed(); \ + return; \ + } + +void AIC3204::setup() { + ESP_LOGCONFIG(TAG, "Setting up AIC3204..."); + + // Set register page to 0 + ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed"); + // Initiate SW reset (PLL is powered off as part of reset) + ERROR_CHECK(this->write_byte(AIC3204_SW_RST, 0x01), "Software reset failed"); + // *** Program clock settings *** + // Default is CODEC_CLKIN is from MCLK pin. Don't need to change this. + // MDAC*NDAC*FOSR*48Khz = mClk (24.576 MHz when the XMOS is expecting 48kHz audio) + // (See page 51 of https://www.ti.com/lit/ml/slaa557/slaa557.pdf) + // We do need MDAC*DOSR/32 >= the resource compute level for the processing block + // So here 2*128/32 = 8, which is equal to processing block 1 's resource compute + // See page 5 of https://www.ti.com/lit/an/slaa404c/slaa404c.pdf for the workflow + // for determining these settings. + + // Power up NDAC and set to 2 + ERROR_CHECK(this->write_byte(AIC3204_NDAC, 0x82), "Set NDAC failed"); + // Power up MDAC and set to 2 + ERROR_CHECK(this->write_byte(AIC3204_MDAC, 0x82), "Set MDAC failed"); + // Program DOSR = 128 + ERROR_CHECK(this->write_byte(AIC3204_DOSR, 0x80), "Set DOSR failed"); + // Set Audio Interface Config: I2S, 32 bits, DOUT always driving + ERROR_CHECK(this->write_byte(AIC3204_CODEC_IF, 0x30), "Set CODEC_IF failed"); + // For I2S Firmware only, set SCLK/MFP3 pin as Audio Data In + ERROR_CHECK(this->write_byte(AIC3204_SCLK_MFP3, 0x02), "Set SCLK/MFP3 failed"); + ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_4, 0x01), "Set AUDIO_IF_4 failed"); + ERROR_CHECK(this->write_byte(AIC3204_AUDIO_IF_5, 0x01), "Set AUDIO_IF_5 failed"); + // Program the DAC processing block to be used - PRB_P1 + ERROR_CHECK(this->write_byte(AIC3204_DAC_SIG_PROC, 0x01), "Set DAC_SIG_PROC failed"); + + // *** Select Page 1 *** + ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x01), "Set page 1 failed"); + // Enable the internal AVDD_LDO: + ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x09), "Set LDO_CTRL failed"); + // *** Program Analog Blocks *** + // Disable Internal Crude AVdd in presence of external AVdd supply or before powering up internal AVdd LDO + ERROR_CHECK(this->write_byte(AIC3204_PWR_CFG, 0x08), "Set PWR_CFG failed"); + // Enable Master Analog Power Control + ERROR_CHECK(this->write_byte(AIC3204_LDO_CTRL, 0x01), "Set LDO_CTRL failed"); + // Page 125: Common mode control register, set d6 to 1 to make the full chip common mode = 0.75 v + // We are using the internal AVdd regulator with a nominal output of 1.72 V (see LDO_CTRL_REGISTER on page 123) + // Page 86 says to only set the common mode voltage to 0.9 v if AVdd >= 1.8... but it isn't on our hardware + // We also adjust the HPL and HPR gains to -2dB gian later in this config flow compensate (see page 47) + // (All pages refer to the TLV320AIC3204 Application Reference Guide) + ERROR_CHECK(this->write_byte(AIC3204_CM_CTRL, 0x40), "Set CM_CTRL failed"); + // *** Set PowerTune Modes *** + // Set the Left & Right DAC PowerTune mode to PTM_P3/4. Use Class-AB driver. + ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG1, 0x00), "Set PLAY_CFG1 failed"); + ERROR_CHECK(this->write_byte(AIC3204_PLAY_CFG2, 0x00), "Set PLAY_CFG2 failed"); + // Set the REF charging time to 40ms + ERROR_CHECK(this->write_byte(AIC3204_REF_STARTUP, 0x01), "Set REF_STARTUP failed"); + // HP soft stepping settings for optimal pop performance at power up + // Rpop used is 6k with N = 6 and soft step = 20usec. This should work with 47uF coupling + // capacitor. Can try N=5,6 or 7 time constants as well. Trade-off delay vs “pop” sound. + ERROR_CHECK(this->write_byte(AIC3204_HP_START, 0x25), "Set HP_START failed"); + // Route Left DAC to HPL + ERROR_CHECK(this->write_byte(AIC3204_HPL_ROUTE, 0x08), "Set HPL_ROUTE failed"); + // Route Right DAC to HPR + ERROR_CHECK(this->write_byte(AIC3204_HPR_ROUTE, 0x08), "Set HPR_ROUTE failed"); + // Route Left DAC to LOL + ERROR_CHECK(this->write_byte(AIC3204_LOL_ROUTE, 0x08), "Set LOL_ROUTE failed"); + // Route Right DAC to LOR + ERROR_CHECK(this->write_byte(AIC3204_LOR_ROUTE, 0x08), "Set LOR_ROUTE failed"); + + // Unmute HPL and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register) + ERROR_CHECK(this->write_byte(AIC3204_HPL_GAIN, 0x3e), "Set HPL_GAIN failed"); + // Unmute HPR and set gain to -2dB (see comment before configuring the AIC3204_CM_CTRL register) + ERROR_CHECK(this->write_byte(AIC3204_HPR_GAIN, 0x3e), "Set HPR_GAIN failed"); + // Unmute LOL and set gain to 0dB + ERROR_CHECK(this->write_byte(AIC3204_LOL_DRV_GAIN, 0x00), "Set LOL_DRV_GAIN failed"); + // Unmute LOR and set gain to 0dB + ERROR_CHECK(this->write_byte(AIC3204_LOR_DRV_GAIN, 0x00), "Set LOR_DRV_GAIN failed"); + + // Power up HPL and HPR, LOL and LOR drivers + ERROR_CHECK(this->write_byte(AIC3204_OP_PWR_CTRL, 0x3C), "Set OP_PWR_CTRL failed"); + + // Wait for 2.5 sec for soft stepping to take effect before attempting power-up + this->set_timeout(2500, [this]() { + // *** Power Up DAC *** + // Select Page 0 + ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set PAGE_CTRL failed"); + // Power up the Left and Right DAC Channels. Route Left data to Left DAC and Right data to Right DAC. + // DAC Vol control soft step 1 step per DAC word clock. + ERROR_CHECK(this->write_byte(AIC3204_DAC_CH_SET1, 0xd4), "Set DAC_CH_SET1 failed"); + // Set left and right DAC digital volume control + ERROR_CHECK(this->write_volume_(), "Set volume failed"); + // Unmute left and right channels + ERROR_CHECK(this->write_mute_(), "Set mute failed"); + }); +} + +void AIC3204::dump_config() { + ESP_LOGCONFIG(TAG, "AIC3204:"); + LOG_I2C_DEVICE(this); + + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with AIC3204 failed"); + } +} + +bool AIC3204::set_mute_off() { + this->is_muted_ = false; + return this->write_mute_(); +} + +bool AIC3204::set_mute_on() { + this->is_muted_ = true; + return this->write_mute_(); +} + +bool AIC3204::set_auto_mute_mode(uint8_t auto_mute_mode) { + this->auto_mute_mode_ = auto_mute_mode & 0x07; + ESP_LOGVV(TAG, "Setting auto_mute_mode to 0x%.2x", this->auto_mute_mode_); + return this->write_mute_(); +} + +bool AIC3204::set_volume(float volume) { + this->volume_ = clamp(volume, 0.0, 1.0); + return this->write_volume_(); +} + +bool AIC3204::is_muted() { return this->is_muted_; } + +float AIC3204::volume() { return this->volume_; } + +bool AIC3204::write_mute_() { + uint8_t mute_mode_byte = this->auto_mute_mode_ << 4; // auto-mute control is bits 4-6 + mute_mode_byte |= this->is_muted_ ? 0x0c : 0x00; // mute bits are 2-3 + if (!this->write_byte(AIC3204_PAGE_CTRL, 0x00) || !this->write_byte(AIC3204_DAC_CH_SET2, mute_mode_byte)) { + ESP_LOGE(TAG, "Writing mute modes failed"); + return false; + } + return true; +} + +bool AIC3204::write_volume_() { + const int8_t dvc_min_byte = -127; + const int8_t dvc_max_byte = 48; + + int8_t volume_byte = dvc_min_byte + (this->volume_ * (dvc_max_byte - dvc_min_byte)); + volume_byte = clamp(volume_byte, dvc_min_byte, dvc_max_byte); + + ESP_LOGVV(TAG, "Setting volume to 0x%.2x", volume_byte & 0xFF); + + if ((!this->write_byte(AIC3204_PAGE_CTRL, 0x00)) || (!this->write_byte(AIC3204_DACL_VOL_D, volume_byte)) || + (!this->write_byte(AIC3204_DACR_VOL_D, volume_byte))) { + ESP_LOGE(TAG, "Writing volume failed"); + return false; + } + return true; +} + +} // namespace aic3204 +} // namespace esphome diff --git a/esphome/components/aic3204/aic3204.h b/esphome/components/aic3204/aic3204.h new file mode 100644 index 0000000000..783a58a2b9 --- /dev/null +++ b/esphome/components/aic3204/aic3204.h @@ -0,0 +1,88 @@ +#pragma once + +#include "esphome/components/audio_dac/audio_dac.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace aic3204 { + +// TLV320AIC3204 Register Addresses +// Page 0 +static const uint8_t AIC3204_PAGE_CTRL = 0x00; // Register 0 - Page Control +static const uint8_t AIC3204_SW_RST = 0x01; // Register 1 - Software Reset +static const uint8_t AIC3204_CLK_PLL1 = 0x04; // Register 4 - Clock Setting Register 1, Multiplexers +static const uint8_t AIC3204_CLK_PLL2 = 0x05; // Register 5 - Clock Setting Register 2, P and R values +static const uint8_t AIC3204_CLK_PLL3 = 0x06; // Register 6 - Clock Setting Register 3, J values +static const uint8_t AIC3204_NDAC = 0x0B; // Register 11 - NDAC Divider Value +static const uint8_t AIC3204_MDAC = 0x0C; // Register 12 - MDAC Divider Value +static const uint8_t AIC3204_DOSR = 0x0E; // Register 14 - DOSR Divider Value (LS Byte) +static const uint8_t AIC3204_NADC = 0x12; // Register 18 - NADC Divider Value +static const uint8_t AIC3204_MADC = 0x13; // Register 19 - MADC Divider Value +static const uint8_t AIC3204_AOSR = 0x14; // Register 20 - AOSR Divider Value +static const uint8_t AIC3204_CODEC_IF = 0x1B; // Register 27 - CODEC Interface Control +static const uint8_t AIC3204_AUDIO_IF_4 = 0x1F; // Register 31 - Audio Interface Setting Register 4 +static const uint8_t AIC3204_AUDIO_IF_5 = 0x20; // Register 32 - Audio Interface Setting Register 5 +static const uint8_t AIC3204_SCLK_MFP3 = 0x38; // Register 56 - SCLK/MFP3 Function Control +static const uint8_t AIC3204_DAC_SIG_PROC = 0x3C; // Register 60 - DAC Sig Processing Block Control +static const uint8_t AIC3204_ADC_SIG_PROC = 0x3D; // Register 61 - ADC Sig Processing Block Control +static const uint8_t AIC3204_DAC_CH_SET1 = 0x3F; // Register 63 - DAC Channel Setup 1 +static const uint8_t AIC3204_DAC_CH_SET2 = 0x40; // Register 64 - DAC Channel Setup 2 +static const uint8_t AIC3204_DACL_VOL_D = 0x41; // Register 65 - DAC Left Digital Vol Control +static const uint8_t AIC3204_DACR_VOL_D = 0x42; // Register 66 - DAC Right Digital Vol Control +static const uint8_t AIC3204_DRC_ENABLE = 0x44; +static const uint8_t AIC3204_ADC_CH_SET = 0x51; // Register 81 - ADC Channel Setup +static const uint8_t AIC3204_ADC_FGA_MUTE = 0x52; // Register 82 - ADC Fine Gain Adjust/Mute + +// Page 1 +static const uint8_t AIC3204_PWR_CFG = 0x01; // Register 1 - Power Config +static const uint8_t AIC3204_LDO_CTRL = 0x02; // Register 2 - LDO Control +static const uint8_t AIC3204_PLAY_CFG1 = 0x03; // Register 3 - Playback Config 1 +static const uint8_t AIC3204_PLAY_CFG2 = 0x04; // Register 4 - Playback Config 2 +static const uint8_t AIC3204_OP_PWR_CTRL = 0x09; // Register 9 - Output Driver Power Control +static const uint8_t AIC3204_CM_CTRL = 0x0A; // Register 10 - Common Mode Control +static const uint8_t AIC3204_HPL_ROUTE = 0x0C; // Register 12 - HPL Routing Select +static const uint8_t AIC3204_HPR_ROUTE = 0x0D; // Register 13 - HPR Routing Select +static const uint8_t AIC3204_LOL_ROUTE = 0x0E; // Register 14 - LOL Routing Selection +static const uint8_t AIC3204_LOR_ROUTE = 0x0F; // Register 15 - LOR Routing Selection +static const uint8_t AIC3204_HPL_GAIN = 0x10; // Register 16 - HPL Driver Gain +static const uint8_t AIC3204_HPR_GAIN = 0x11; // Register 17 - HPR Driver Gain +static const uint8_t AIC3204_LOL_DRV_GAIN = 0x12; // Register 18 - LOL Driver Gain Setting +static const uint8_t AIC3204_LOR_DRV_GAIN = 0x13; // Register 19 - LOR Driver Gain Setting +static const uint8_t AIC3204_HP_START = 0x14; // Register 20 - Headphone Driver Startup +static const uint8_t AIC3204_LPGA_P_ROUTE = 0x34; // Register 52 - Left PGA Positive Input Route +static const uint8_t AIC3204_LPGA_N_ROUTE = 0x36; // Register 54 - Left PGA Negative Input Route +static const uint8_t AIC3204_RPGA_P_ROUTE = 0x37; // Register 55 - Right PGA Positive Input Route +static const uint8_t AIC3204_RPGA_N_ROUTE = 0x39; // Register 57 - Right PGA Negative Input Route +static const uint8_t AIC3204_LPGA_VOL = 0x3B; // Register 59 - Left PGA Volume +static const uint8_t AIC3204_RPGA_VOL = 0x3C; // Register 60 - Right PGA Volume +static const uint8_t AIC3204_ADC_PTM = 0x3D; // Register 61 - ADC Power Tune Config +static const uint8_t AIC3204_AN_IN_CHRG = 0x47; // Register 71 - Analog Input Quick Charging Config +static const uint8_t AIC3204_REF_STARTUP = 0x7B; // Register 123 - Reference Power Up Config + +class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + + bool set_mute_off() override; + bool set_mute_on() override; + bool set_auto_mute_mode(uint8_t auto_mute_mode); + bool set_volume(float volume) override; + + bool is_muted() override; + float volume() override; + + protected: + bool write_mute_(); + bool write_volume_(); + + uint8_t auto_mute_mode_{0}; + float volume_{0}; +}; + +} // namespace aic3204 +} // namespace esphome diff --git a/esphome/components/aic3204/audio_dac.py b/esphome/components/aic3204/audio_dac.py new file mode 100644 index 0000000000..da7a54df54 --- /dev/null +++ b/esphome/components/aic3204/audio_dac.py @@ -0,0 +1,52 @@ +from esphome import automation +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, CONF_MODE + +CODEOWNERS = ["@kbx81"] +DEPENDENCIES = ["i2c"] + +aic3204_ns = cg.esphome_ns.namespace("aic3204") +AIC3204 = aic3204_ns.class_("AIC3204", AudioDac, cg.Component, i2c.I2CDevice) + +SetAutoMuteAction = aic3204_ns.class_("SetAutoMuteAction", automation.Action) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(AIC3204), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x18)) +) + + +SET_AUTO_MUTE_ACTION_SCHEMA = cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(AIC3204), + cv.Required(CONF_MODE): cv.templatable(cv.int_range(max=7, min=0)), + }, + key=CONF_MODE, +) + + +@automation.register_action( + "aic3204.set_auto_mute_mode", SetAutoMuteAction, SET_AUTO_MUTE_ACTION_SCHEMA +) +async def aic3204_set_volume_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config.get(CONF_MODE), args, int) + cg.add(var.set_auto_mute_mode(template_)) + + return var + + +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/aic3204/automation.h b/esphome/components/aic3204/automation.h new file mode 100644 index 0000000000..416a88fa12 --- /dev/null +++ b/esphome/components/aic3204/automation.h @@ -0,0 +1,23 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "aic3204.h" + +namespace esphome { +namespace aic3204 { + +template class SetAutoMuteAction : public Action { + public: + explicit SetAutoMuteAction(AIC3204 *aic3204) : aic3204_(aic3204) {} + + TEMPLATABLE_VALUE(uint8_t, auto_mute_mode) + + void play(Ts... x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); } + + protected: + AIC3204 *aic3204_; +}; + +} // namespace aic3204 +} // namespace esphome diff --git a/esphome/components/audio_dac/__init__.py b/esphome/components/audio_dac/__init__.py new file mode 100644 index 0000000000..978ed195bd --- /dev/null +++ b/esphome/components/audio_dac/__init__.py @@ -0,0 +1,57 @@ +from esphome import automation +from esphome.automation import maybe_simple_id +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_VOLUME +from esphome.core import coroutine_with_priority + +CODEOWNERS = ["@kbx81"] +IS_PLATFORM_COMPONENT = True + +audio_dac_ns = cg.esphome_ns.namespace("audio_dac") +AudioDac = audio_dac_ns.class_("AudioDac") + +MuteOffAction = audio_dac_ns.class_("MuteOffAction", automation.Action) +MuteOnAction = audio_dac_ns.class_("MuteOnAction", automation.Action) +SetVolumeAction = audio_dac_ns.class_("SetVolumeAction", automation.Action) + + +MUTE_ACTION_SCHEMA = maybe_simple_id( + { + cv.GenerateID(): cv.use_id(AudioDac), + } +) + +SET_VOLUME_ACTION_SCHEMA = cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(AudioDac), + cv.Required(CONF_VOLUME): cv.templatable(cv.percentage), + }, + key=CONF_VOLUME, +) + + +@automation.register_action("audio_dac.mute_off", MuteOffAction, MUTE_ACTION_SCHEMA) +@automation.register_action("audio_dac.mute_on", MuteOnAction, MUTE_ACTION_SCHEMA) +async def audio_dac_mute_action_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) + + +@automation.register_action( + "audio_dac.set_volume", SetVolumeAction, SET_VOLUME_ACTION_SCHEMA +) +async def audio_dac_set_volume_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config.get(CONF_VOLUME), args, float) + cg.add(var.set_volume(template_)) + + return var + + +@coroutine_with_priority(100.0) +async def to_code(config): + cg.add_define("USE_AUDIO_DAC") + cg.add_global(audio_dac_ns.using) diff --git a/esphome/components/audio_dac/audio_dac.h b/esphome/components/audio_dac/audio_dac.h new file mode 100644 index 0000000000..a62d17b849 --- /dev/null +++ b/esphome/components/audio_dac/audio_dac.h @@ -0,0 +1,23 @@ +#pragma once + +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace audio_dac { + +class AudioDac { + public: + virtual bool set_mute_off() = 0; + virtual bool set_mute_on() = 0; + virtual bool set_volume(float volume) = 0; + + virtual bool is_muted() = 0; + virtual float volume() = 0; + + protected: + bool is_muted_{false}; +}; + +} // namespace audio_dac +} // namespace esphome diff --git a/esphome/components/audio_dac/automation.h b/esphome/components/audio_dac/automation.h new file mode 100644 index 0000000000..b6cf2acaf4 --- /dev/null +++ b/esphome/components/audio_dac/automation.h @@ -0,0 +1,43 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "audio_dac.h" + +namespace esphome { +namespace audio_dac { + +template class MuteOffAction : public Action { + public: + explicit MuteOffAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {} + + void play(Ts... x) override { this->audio_dac_->set_mute_off(); } + + protected: + AudioDac *audio_dac_; +}; + +template class MuteOnAction : public Action { + public: + explicit MuteOnAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {} + + void play(Ts... x) override { this->audio_dac_->set_mute_on(); } + + protected: + AudioDac *audio_dac_; +}; + +template class SetVolumeAction : public Action { + public: + explicit SetVolumeAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {} + + TEMPLATABLE_VALUE(float, volume) + + void play(Ts... x) override { this->audio_dac_->set_volume(this->volume_.value(x...)); } + + protected: + AudioDac *audio_dac_; +}; + +} // namespace audio_dac +} // namespace esphome diff --git a/tests/components/aic3204/common.yaml b/tests/components/aic3204/common.yaml new file mode 100644 index 0000000000..6e939bd260 --- /dev/null +++ b/tests/components/aic3204/common.yaml @@ -0,0 +1,15 @@ +esphome: + on_boot: + then: + - audio_dac.mute_off: + - audio_dac.mute_on: + - audio_dac.set_volume: + volume: 50% + +i2c: + - id: i2c_aic3204 + scl: ${scl_pin} + sda: ${sda_pin} + +audio_dac: + - platform: aic3204 diff --git a/tests/components/aic3204/test.esp32-ard.yaml b/tests/components/aic3204/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/aic3204/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/aic3204/test.esp32-c3-ard.yaml b/tests/components/aic3204/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/aic3204/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/aic3204/test.esp32-c3-idf.yaml b/tests/components/aic3204/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/aic3204/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/aic3204/test.esp32-idf.yaml b/tests/components/aic3204/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/aic3204/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/aic3204/test.esp8266-ard.yaml b/tests/components/aic3204/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/aic3204/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml From e87169805cf9e38e55241529df479498bf61e3fb Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 6 Oct 2024 16:02:53 -0500 Subject: [PATCH 0434/1052] [wifi] Replace ``USE_ESP32_IGNORE_EFUSE_MAC_CRC`` with IDF's ``CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR`` (#7502) --- esphome/components/esp32/__init__.py | 16 +++++++++++++--- .../components/wifi/wifi_component_esp_idf.cpp | 6 ------ esphome/core/helpers.cpp | 8 ++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index b7d3ef4a6d..8a73f2020d 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -53,6 +53,7 @@ from .const import ( # noqa KEY_SDKCONFIG_OPTIONS, KEY_SUBMODULES, KEY_VARIANT, + VARIANT_ESP32, VARIANT_FRIENDLY, VARIANTS, ) @@ -376,6 +377,15 @@ def final_validate(config): f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only" ) + if ( + config[CONF_VARIANT] != VARIANT_ESP32 + and CONF_ADVANCED in (conf_fw := config[CONF_FRAMEWORK]) + and CONF_IGNORE_EFUSE_MAC_CRC in conf_fw[CONF_ADVANCED] + ): + raise cv.Invalid( + f"{CONF_IGNORE_EFUSE_MAC_CRC} is not supported on {config[CONF_VARIANT]}" + ) + return config @@ -405,7 +415,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( cv.Optional( CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False ): cv.boolean, - cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC, default=False): cv.boolean, + cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean, } ), cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list( @@ -532,8 +542,8 @@ async def to_code(config): if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") - if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_MAC_CRC]: - cg.add_define("USE_ESP32_IGNORE_EFUSE_MAC_CRC") + if conf[CONF_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( "CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index c430d160f2..0f2e181e31 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -131,16 +131,10 @@ void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, voi void WiFiComponent::wifi_pre_setup_() { uint8_t mac[6]; -#ifdef USE_ESP32_IGNORE_EFUSE_MAC_CRC - get_mac_address_raw(mac); - set_mac_address(mac); - ESP_LOGV(TAG, "Use EFuse MAC without checking CRC: %s", get_mac_address_pretty().c_str()); -#else if (has_custom_mac_address()) { get_mac_address_raw(mac); set_mac_address(mac); } -#endif esp_err_t err = esp_netif_init(); if (err != ERR_OK) { ESP_LOGE(TAG, "esp_netif_init failed: %s", esp_err_to_name(err)); diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 492ab6dd1a..dca35819ff 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -662,13 +662,9 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame static const uint8_t esphome_host_mac_address[6] = USE_ESPHOME_HOST_MAC_ADDRESS; memcpy(mac, esphome_host_mac_address, sizeof(esphome_host_mac_address)); #elif defined(USE_ESP32) -#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) || defined(USE_ESP32_IGNORE_EFUSE_MAC_CRC) +#if defined(CONFIG_SOC_IEEE802154_SUPPORTED) // When CONFIG_SOC_IEEE802154_SUPPORTED is defined, esp_efuse_mac_get_default - // returns the 802.15.4 EUI-64 address. Read directly from eFuse instead. - // On some devices, the MAC address that is burnt into EFuse does not - // match the CRC that goes along with it. For those devices, this - // work-around reads and uses the MAC address as-is from EFuse, - // without doing the CRC check. + // returns the 802.15.4 EUI-64 address, so we read directly from eFuse instead. if (has_custom_mac_address()) { esp_efuse_read_field_blob(ESP_EFUSE_MAC_CUSTOM, mac, 48); } else { From 239eadb89581083d349bd38dd7a37653afd65e29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:34:03 +1300 Subject: [PATCH 0435/1052] Bump docker/setup-buildx-action from 3.6.1 to 3.7.1 in the docker-actions group across 1 directory (#7542) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 891367d16a..f003e5d24c 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -46,7 +46,7 @@ jobs: with: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.6.1 + uses: docker/setup-buildx-action@v3.7.1 - name: Set up QEMU uses: docker/setup-qemu-action@v3.2.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d63f221ef5..210879b50e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,7 +90,7 @@ jobs: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.6.1 + uses: docker/setup-buildx-action@v3.7.1 - name: Set up QEMU if: matrix.platform != 'linux/amd64' uses: docker/setup-qemu-action@v3.2.0 @@ -184,7 +184,7 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.6.1 + uses: docker/setup-buildx-action@v3.7.1 - name: Log in to docker hub if: matrix.registry == 'dockerhub' From fbd600f43f5740cba0efd723236933ca5effdedd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:34:17 +1300 Subject: [PATCH 0436/1052] Bump pypa/gh-action-pypi-publish from 1.10.2 to 1.10.3 (#7541) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 210879b50e..8995c500ef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.10.2 + uses: pypa/gh-action-pypi-publish@v1.10.3 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From fc7628cdeaa8138244b98d1c0adbeb40d296c0f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:34:38 +1300 Subject: [PATCH 0437/1052] Bump docker/build-push-action from 6.7.0 to 6.9.0 in /.github/actions/build-image (#7511) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index d277ec06c7..5c686605c3 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.7.0 + uses: docker/build-push-action@v6.9.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false @@ -72,7 +72,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.7.0 + uses: docker/build-push-action@v6.9.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false From 390299894e2b15a8c5ab90919f33a7ec41595880 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 7 Oct 2024 00:53:49 +0200 Subject: [PATCH 0438/1052] [code-quality] fix clang-tidy md5 and hmac_md5 (#7325) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/hmac_md5/hmac_md5.cpp | 2 ++ esphome/components/hmac_md5/hmac_md5.h | 3 ++- esphome/components/md5/__init__.py | 6 ++++++ esphome/components/md5/md5.cpp | 2 ++ esphome/components/md5/md5.h | 2 ++ esphome/core/defines.h | 1 + 6 files changed, 15 insertions(+), 1 deletion(-) diff --git a/esphome/components/hmac_md5/hmac_md5.cpp b/esphome/components/hmac_md5/hmac_md5.cpp index 90bf91882f..d766a55fab 100644 --- a/esphome/components/hmac_md5/hmac_md5.cpp +++ b/esphome/components/hmac_md5/hmac_md5.cpp @@ -1,6 +1,7 @@ #include #include #include "hmac_md5.h" +#ifdef USE_MD5 #include "esphome/core/helpers.h" namespace esphome { @@ -54,3 +55,4 @@ bool HmacMD5::equals_hex(const char *expected) { return this->ohash_.equals_hex( } // namespace hmac_md5 } // namespace esphome +#endif diff --git a/esphome/components/hmac_md5/hmac_md5.h b/esphome/components/hmac_md5/hmac_md5.h index e6a97ad2e3..b83b9d5421 100644 --- a/esphome/components/hmac_md5/hmac_md5.h +++ b/esphome/components/hmac_md5/hmac_md5.h @@ -1,8 +1,8 @@ #pragma once #include "esphome/core/defines.h" +#ifdef USE_MD5 #include "esphome/components/md5/md5.h" - #include namespace esphome { @@ -46,3 +46,4 @@ class HmacMD5 { } // namespace hmac_md5 } // namespace esphome +#endif diff --git a/esphome/components/md5/__init__.py b/esphome/components/md5/__init__.py index f70ffa9520..1af9ee0b29 100644 --- a/esphome/components/md5/__init__.py +++ b/esphome/components/md5/__init__.py @@ -1 +1,7 @@ +import esphome.codegen as cg + CODEOWNERS = ["@esphome/core"] + + +async def to_code(config): + cg.add_define("USE_MD5") diff --git a/esphome/components/md5/md5.cpp b/esphome/components/md5/md5.cpp index 620b6749f3..31f52634be 100644 --- a/esphome/components/md5/md5.cpp +++ b/esphome/components/md5/md5.cpp @@ -1,6 +1,7 @@ #include #include #include "md5.h" +#ifdef USE_MD5 #include "esphome/core/helpers.h" namespace esphome { @@ -65,3 +66,4 @@ bool MD5Digest::equals_hex(const char *expected) { } // namespace md5 } // namespace esphome +#endif diff --git a/esphome/components/md5/md5.h b/esphome/components/md5/md5.h index 4ec8a8a12c..cb6accf46f 100644 --- a/esphome/components/md5/md5.h +++ b/esphome/components/md5/md5.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/defines.h" +#ifdef USE_MD5 #ifdef USE_ESP_IDF #include "esp_rom_md5.h" @@ -66,3 +67,4 @@ class MD5Digest { } // namespace md5 } // namespace esphome +#endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index bf676107c7..ca3db0ad56 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -50,6 +50,7 @@ #define USE_LVGL_KEYBOARD #define USE_LVGL_ROTARY_ENCODER #define USE_LVGL_TOUCHSCREEN +#define USE_MD5 #define USE_MDNS #define USE_MEDIA_PLAYER #define USE_MQTT From cbc03aae803118de3f09437ebb52e018c4467fdb Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 7 Oct 2024 00:55:11 +0200 Subject: [PATCH 0439/1052] [code-quality] fix clang-tidy api (#7279) --- esphome/components/api/api_connection.cpp | 2 ++ esphome/components/api/api_connection.h | 4 +++- esphome/components/api/api_frame_helper.cpp | 3 ++- esphome/components/api/api_frame_helper.h | 3 ++- esphome/components/api/api_server.cpp | 2 ++ esphome/components/api/api_server.h | 4 +++- esphome/components/api/custom_api_device.h | 5 +++-- esphome/components/api/homeassistant_service.h | 7 ++++--- esphome/components/api/list_entities.cpp | 2 ++ esphome/components/api/list_entities.h | 5 +++-- esphome/components/api/subscribe_state.cpp | 2 ++ esphome/components/api/subscribe_state.h | 5 +++-- 12 files changed, 31 insertions(+), 13 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index e28b244722..bb55a2ccf6 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1,4 +1,5 @@ #include "api_connection.h" +#ifdef USE_API #include #include #include @@ -1568,3 +1569,4 @@ void APIConnection::on_fatal_error() { } // namespace api } // namespace esphome +#endif diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index f176cf7c56..043aaee421 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -1,12 +1,13 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_API #include "api_frame_helper.h" #include "api_pb2.h" #include "api_pb2_service.h" #include "api_server.h" #include "esphome/core/application.h" #include "esphome/core/component.h" -#include "esphome/core/defines.h" #include @@ -268,3 +269,4 @@ class APIConnection : public APIServerConnection { } // namespace api } // namespace esphome +#endif diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index f4b18a1fd6..62f375508c 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -1,5 +1,5 @@ #include "api_frame_helper.h" - +#ifdef USE_API #include "esphome/core/log.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" @@ -1028,3 +1028,4 @@ APIError APIPlaintextFrameHelper::shutdown(int how) { } // namespace api } // namespace esphome +#endif diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index bf4872d2d6..56d8bf1973 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -5,7 +5,7 @@ #include #include "esphome/core/defines.h" - +#ifdef USE_API #ifdef USE_API_NOISE #include "noise/protocol.h" #endif @@ -190,3 +190,4 @@ class APIPlaintextFrameHelper : public APIFrameHelper { } // namespace api } // namespace esphome +#endif diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 0fde3e47af..f16b5a13cf 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -1,4 +1,5 @@ #include "api_server.h" +#ifdef USE_API #include #include "api_connection.h" #include "esphome/components/network/util.h" @@ -403,3 +404,4 @@ void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP } // namespace api } // namespace esphome +#endif diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 899eaede49..42e0b1048a 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -1,5 +1,7 @@ #pragma once +#include "esphome/core/defines.h" +#ifdef USE_API #include "api_noise_context.h" #include "api_pb2.h" #include "api_pb2_service.h" @@ -7,7 +9,6 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/controller.h" -#include "esphome/core/defines.h" #include "esphome/core/log.h" #include "list_entities.h" #include "subscribe_state.h" @@ -153,3 +154,4 @@ template class APIConnectedCondition : public Condition { } // namespace api } // namespace esphome +#endif diff --git a/esphome/components/api/custom_api_device.h b/esphome/components/api/custom_api_device.h index 845a35fc54..1a8e189f41 100644 --- a/esphome/components/api/custom_api_device.h +++ b/esphome/components/api/custom_api_device.h @@ -1,9 +1,9 @@ #pragma once #include -#include "user_services.h" #include "api_server.h" - +#ifdef USE_API +#include "user_services.h" namespace esphome { namespace api { @@ -216,3 +216,4 @@ class CustomAPIDevice { } // namespace api } // namespace esphome +#endif diff --git a/esphome/components/api/homeassistant_service.h b/esphome/components/api/homeassistant_service.h index f04181e5b2..e91756c3c9 100644 --- a/esphome/components/api/homeassistant_service.h +++ b/esphome/components/api/homeassistant_service.h @@ -1,10 +1,10 @@ #pragma once +#include "api_server.h" +#ifdef USE_API +#include "api_pb2.h" #include "esphome/core/helpers.h" #include "esphome/core/automation.h" -#include "api_pb2.h" -#include "api_server.h" - #include namespace esphome { @@ -81,3 +81,4 @@ template class HomeAssistantServiceCallAction : public Action Date: Mon, 7 Oct 2024 11:27:08 +1100 Subject: [PATCH 0440/1052] [lvgl] Bugfixes #3 (#7472) --- esphome/components/image/image.cpp | 44 ++++++++++++++++ esphome/components/image/image.h | 19 ++++++- esphome/components/lvgl/__init__.py | 4 +- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/lv_validation.py | 3 +- esphome/components/lvgl/lvgl_esphome.cpp | 43 --------------- esphome/components/lvgl/lvgl_esphome.h | 4 -- esphome/components/lvgl/widgets/__init__.py | 2 + esphome/components/lvgl/widgets/animimg.py | 7 ++- esphome/components/lvgl/widgets/meter.py | 27 ++++++---- tests/components/image/test.esp32-ard.yaml | 4 +- tests/components/image/test.esp32-c3-ard.yaml | 4 +- tests/components/image/test.esp32-c3-idf.yaml | 4 +- tests/components/image/test.esp32-idf.yaml | 4 +- tests/components/lvgl/lvgl-package.yaml | 52 +++++++++++++++++++ tests/components/lvgl/test.esp32-idf.yaml | 2 +- 16 files changed, 148 insertions(+), 76 deletions(-) diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index 0ddb8110cb..ded4c60d25 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -79,6 +79,50 @@ Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const { return color_off; } } +#ifdef USE_LVGL +lv_img_dsc_t *Image::get_lv_img_dsc() { + // lazily construct lvgl image_dsc. + if (this->dsc_.data != this->data_start_) { + this->dsc_.data = this->data_start_; + this->dsc_.header.always_zero = 0; + this->dsc_.header.reserved = 0; + this->dsc_.header.w = this->width_; + this->dsc_.header.h = this->height_; + this->dsc_.data_size = image_type_to_width_stride(this->dsc_.header.w * this->dsc_.header.h, this->get_type()); + switch (this->get_type()) { + case IMAGE_TYPE_BINARY: + this->dsc_.header.cf = LV_IMG_CF_ALPHA_1BIT; + break; + + case IMAGE_TYPE_GRAYSCALE: + this->dsc_.header.cf = LV_IMG_CF_ALPHA_8BIT; + break; + + case IMAGE_TYPE_RGB24: + this->dsc_.header.cf = LV_IMG_CF_RGB888; + break; + + case IMAGE_TYPE_RGB565: +#if LV_COLOR_DEPTH == 16 + this->dsc_.header.cf = this->has_transparency() ? LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED : LV_IMG_CF_TRUE_COLOR; +#else + this->dsc_.header.cf = LV_IMG_CF_RGB565; +#endif + break; + + case image::IMAGE_TYPE_RGBA: +#if LV_COLOR_DEPTH == 32 + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR; +#else + this->dsc_.header.cf = LV_IMG_CF_RGBA8888; +#endif + break; + } + } + return &this->dsc_; +} +#endif // USE_LVGL + bool Image::get_binary_pixel_(int x, int y) const { const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; const uint32_t pos = x + y * width_8; diff --git a/esphome/components/image/image.h b/esphome/components/image/image.h index 5f1f50a134..ae5a7a814d 100644 --- a/esphome/components/image/image.h +++ b/esphome/components/image/image.h @@ -1,6 +1,15 @@ #pragma once #include "esphome/core/color.h" -#include "esphome/components/display/display_buffer.h" +#include "esphome/components/display/display.h" + +#ifdef USE_LVGL +// required for clang-tidy +#ifndef LV_CONF_H +#define LV_CONF_SKIP 1 // NOLINT +#endif // LV_CONF_H + +#include +#endif // USE_LVGL namespace esphome { namespace image { @@ -37,7 +46,7 @@ class Image : public display::BaseImage { Color get_pixel(int x, int y, Color color_on = display::COLOR_ON, Color color_off = display::COLOR_OFF) const; int get_width() const override; int get_height() const override; - const uint8_t *get_data_start() { return this->data_start_; } + const uint8_t *get_data_start() const { return this->data_start_; } ImageType get_type() const; void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; @@ -45,6 +54,9 @@ class Image : public display::BaseImage { void set_transparency(bool transparent) { transparent_ = transparent; } bool has_transparency() const { return transparent_; } +#ifdef USE_LVGL + lv_img_dsc_t *get_lv_img_dsc(); +#endif protected: bool get_binary_pixel_(int x, int y) const; Color get_rgb24_pixel_(int x, int y) const; @@ -57,6 +69,9 @@ class Image : public display::BaseImage { ImageType type_; const uint8_t *data_start_; bool transparent_; +#ifdef USE_LVGL + lv_img_dsc_t dsc_{}; +#endif }; } // namespace image diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index a3a6f7ddaf..ce3843567b 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -53,7 +53,7 @@ from .types import ( lv_style_t, lvgl_ns, ) -from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties +from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties, styles_used from .widgets.animimg import animimg_spec from .widgets.arc import arc_spec from .widgets.button import button_spec @@ -280,6 +280,8 @@ async def to_code(config): for comp in helpers.lvgl_components_required: CORE.add_define(f"USE_LVGL_{comp.upper()}") + if "transform_angle" in styles_used: + add_define("LV_COLOR_SCREEN_TRANSP", "1") for use in helpers.lv_uses: add_define(f"LV_USE_{use.upper()}") lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 3db49d26a4..02f726e49c 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -452,6 +452,7 @@ CONF_OFFSET_Y = "offset_y" CONF_ONE_CHECKED = "one_checked" CONF_ONE_LINE = "one_line" CONF_ON_SELECT = "on_select" +CONF_OPA = "opa" CONF_NEXT = "next" CONF_PAD_ROW = "pad_row" CONF_PAD_COLUMN = "pad_column" diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 3dee0189fb..bd98319fd3 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -31,7 +31,6 @@ from .defines import ( literal, ) from .helpers import esphome_fonts_used, lv_fonts_used, requires_component -from .lvcode import lv_expr from .types import lv_font_t, lv_gradient_t, lv_img_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -330,7 +329,7 @@ def image_validator(value): lv_image = LValidator( image_validator, lv_img_t, - retmapper=lambda x: lv_expr.img_from(MockObj(x)), + retmapper=lambda x: MockObj(x, "->").get_lv_img_dsc(), requires="image", ) lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 89c9828740..b63fb0dab8 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -356,49 +356,6 @@ bool lv_is_pre_initialise() { return false; } -#ifdef USE_LVGL_IMAGE -lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc) { - if (img_dsc == nullptr) - img_dsc = new lv_img_dsc_t(); // NOLINT - img_dsc->header.always_zero = 0; - img_dsc->header.reserved = 0; - img_dsc->header.w = src->get_width(); - img_dsc->header.h = src->get_height(); - img_dsc->data = src->get_data_start(); - img_dsc->data_size = image_type_to_width_stride(img_dsc->header.w * img_dsc->header.h, src->get_type()); - switch (src->get_type()) { - case image::IMAGE_TYPE_BINARY: - img_dsc->header.cf = LV_IMG_CF_ALPHA_1BIT; - break; - - case image::IMAGE_TYPE_GRAYSCALE: - img_dsc->header.cf = LV_IMG_CF_ALPHA_8BIT; - break; - - case image::IMAGE_TYPE_RGB24: - img_dsc->header.cf = LV_IMG_CF_RGB888; - break; - - case image::IMAGE_TYPE_RGB565: -#if LV_COLOR_DEPTH == 16 - img_dsc->header.cf = src->has_transparency() ? LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED : LV_IMG_CF_TRUE_COLOR; -#else - img_dsc->header.cf = LV_IMG_CF_RGB565; -#endif - break; - - case image::IMAGE_TYPE_RGBA: -#if LV_COLOR_DEPTH == 32 - img_dsc->header.cf = LV_IMG_CF_TRUE_COLOR; -#else - img_dsc->header.cf = LV_IMG_CF_RGBA8888; -#endif - break; - } - return img_dsc; -} -#endif // USE_LVGL_IMAGE - #ifdef USE_LVGL_ANIMIMG void lv_animimg_stop(lv_obj_t *obj) { auto *animg = (lv_animimg_t *) obj; diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index d5cff51de2..0c3738bd1f 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -102,10 +102,6 @@ class FontEngine { lv_font_t lv_font_{}; }; #endif // USE_LVGL_FONT -#ifdef USE_LVGL_IMAGE -lv_img_dsc_t *lv_img_from(image::Image *src, lv_img_dsc_t *img_dsc = nullptr); -#endif // USE_LVGL_IMAGE - #ifdef USE_LVGL_ANIMIMG void lv_animimg_stop(lv_obj_t *obj); #endif // USE_LVGL_ANIMIMG diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index e093cebd16..533ffdea55 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -52,6 +52,7 @@ from ..types import LV_STATE, LvType, WidgetType, lv_coord_t, lv_obj_t, lv_obj_t EVENT_LAMB = "event_lamb__" theme_widget_map = {} +styles_used = set() class LvScrActType(WidgetType): @@ -158,6 +159,7 @@ class Widget: def set_style(self, prop, value, state): if value is None: return + styles_used.add(prop) lv.call(f"obj_set_style_{prop}", self.obj, value, state) def __type_base(self): diff --git a/esphome/components/lvgl/widgets/animimg.py b/esphome/components/lvgl/widgets/animimg.py index a973ca0702..3b20008c3d 100644 --- a/esphome/components/lvgl/widgets/animimg.py +++ b/esphome/components/lvgl/widgets/animimg.py @@ -2,13 +2,12 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_DURATION, CONF_ID -from esphome.cpp_generator import MockObj from ..automation import action_to_code from ..defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC from ..helpers import lvgl_components_required from ..lv_validation import lv_image, lv_milliseconds -from ..lvcode import lv, lv_expr +from ..lvcode import lv from ..types import LvType, ObjUpdateAction, void_ptr from . import Widget, WidgetType, get_widgets from .img import CONF_IMAGE @@ -63,7 +62,7 @@ class AnimimgType(WidgetType): if CONF_SRC in config: for x in config[CONF_SRC]: await cg.get_variable(x) - srcs = [lv_expr.img_from(MockObj(x)) for x in config[CONF_SRC]] + srcs = [await lv_image.process(x) for x in config[CONF_SRC]] src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs) count = len(config[CONF_SRC]) lv.animimg_set_src(w.obj, src_id, count) @@ -73,7 +72,7 @@ class AnimimgType(WidgetType): lv.animimg_start(w.obj) def get_uses(self): - return CONF_IMAGE, CONF_LABEL + return "img", CONF_IMAGE, CONF_LABEL animimg_spec = AnimimgType() diff --git a/esphome/components/lvgl/widgets/meter.py b/esphome/components/lvgl/widgets/meter.py index 36f6643022..bc455ccebc 100644 --- a/esphome/components/lvgl/widgets/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -20,6 +20,7 @@ from ..defines import ( CONF_END_VALUE, CONF_INDICATOR, CONF_MAIN, + CONF_OPA, CONF_PIVOT_X, CONF_PIVOT_Y, CONF_SRC, @@ -35,10 +36,11 @@ from ..lv_validation import ( lv_color, lv_float, lv_image, + opacity, requires_component, size, ) -from ..lvcode import LocalVariable, lv, lv_assign, lv_expr +from ..lvcode import LocalVariable, lv, lv_assign, lv_expr, lv_obj from ..types import LvType, ObjUpdateAction from . import Widget, WidgetType, get_widgets from .arc import CONF_ARC @@ -76,6 +78,7 @@ INDICATOR_LINE_SCHEMA = cv.Schema( cv.Optional(CONF_COLOR, default=0): lv_color, cv.Optional(CONF_R_MOD, default=0): size, cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_OPA): opacity, } ) INDICATOR_IMG_SCHEMA = cv.Schema( @@ -84,6 +87,7 @@ INDICATOR_IMG_SCHEMA = cv.Schema( cv.Required(CONF_PIVOT_X): pixels, cv.Required(CONF_PIVOT_Y): pixels, cv.Optional(CONF_VALUE): lv_float, + cv.Optional(CONF_OPA): opacity, } ) INDICATOR_ARC_SCHEMA = cv.Schema( @@ -94,6 +98,7 @@ INDICATOR_ARC_SCHEMA = cv.Schema( cv.Exclusive(CONF_VALUE, CONF_VALUE): lv_float, cv.Exclusive(CONF_START_VALUE, CONF_VALUE): lv_float, cv.Optional(CONF_END_VALUE): lv_float, + cv.Optional(CONF_OPA): opacity, } ) INDICATOR_TICKS_SCHEMA = cv.Schema( @@ -218,9 +223,7 @@ class MeterType(WidgetType): for indicator in scale_conf.get(CONF_INDICATORS, ()): (t, v) = next(iter(indicator.items())) iid = v[CONF_ID] - ivar = cg.new_variable( - iid, cg.nullptr, type_=lv_meter_indicator_t_ptr - ) + ivar = cg.Pvariable(iid, cg.nullptr, type_=lv_meter_indicator_t) # Enable getting the meter to which this belongs. wid = Widget.create(iid, var, obj_spec, v) wid.obj = ivar @@ -268,9 +271,7 @@ class MeterType(WidgetType): v[CONF_PIVOT_Y], ), ) - start_value = await get_start_value(v) - end_value = await get_end_value(v) - set_indicator_values(var, ivar, start_value, end_value) + await set_indicator_values(var, ivar, v) meter_spec = MeterType() @@ -285,21 +286,22 @@ meter_spec = MeterType() cv.Exclusive(CONF_VALUE, CONF_VALUE): lv_float, cv.Exclusive(CONF_START_VALUE, CONF_VALUE): lv_float, cv.Optional(CONF_END_VALUE): lv_float, + cv.Optional(CONF_OPA): opacity, } ), ) async def indicator_update_to_code(config, action_id, template_arg, args): widget = await get_widgets(config) - start_value = await get_start_value(config) - end_value = await get_end_value(config) async def set_value(w: Widget): - set_indicator_values(w.var, w.obj, start_value, end_value) + await set_indicator_values(w.var, w.obj, config) return await action_to_code(widget, set_value, action_id, template_arg, args) -def set_indicator_values(meter, indicator, start_value, end_value): +async def set_indicator_values(meter, indicator, config): + start_value = await get_start_value(config) + end_value = await get_end_value(config) if start_value is not None: if end_value is None: lv.meter_set_indicator_value(meter, indicator, start_value) @@ -307,3 +309,6 @@ def set_indicator_values(meter, indicator, start_value, end_value): lv.meter_set_indicator_start_value(meter, indicator, start_value) if end_value is not None: lv.meter_set_indicator_end_value(meter, indicator, end_value) + if opa := config.get(CONF_OPA): + lv_assign(indicator.opa, await opacity.process(opa)) + lv_obj.invalidate(meter) diff --git a/tests/components/image/test.esp32-ard.yaml b/tests/components/image/test.esp32-ard.yaml index 34c7914976..9dd44d177f 100644 --- a/tests/components/image/test.esp32-ard.yaml +++ b/tests/components/image/test.esp32-ard.yaml @@ -2,13 +2,13 @@ spi: - id: spi_main_lcd clk_pin: 16 mosi_pin: 17 - miso_pin: 15 + miso_pin: 32 display: - platform: ili9xxx id: main_lcd model: ili9342 - cs_pin: 12 + cs_pin: 14 dc_pin: 13 reset_pin: 21 invert_colors: true diff --git a/tests/components/image/test.esp32-c3-ard.yaml b/tests/components/image/test.esp32-c3-ard.yaml index 91ff0a0579..c0b2779773 100644 --- a/tests/components/image/test.esp32-c3-ard.yaml +++ b/tests/components/image/test.esp32-c3-ard.yaml @@ -8,8 +8,8 @@ display: - platform: ili9xxx id: main_lcd model: ili9342 - cs_pin: 8 - dc_pin: 9 + cs_pin: 3 + dc_pin: 11 reset_pin: 10 invert_colors: true diff --git a/tests/components/image/test.esp32-c3-idf.yaml b/tests/components/image/test.esp32-c3-idf.yaml index 91ff0a0579..c0b2779773 100644 --- a/tests/components/image/test.esp32-c3-idf.yaml +++ b/tests/components/image/test.esp32-c3-idf.yaml @@ -8,8 +8,8 @@ display: - platform: ili9xxx id: main_lcd model: ili9342 - cs_pin: 8 - dc_pin: 9 + cs_pin: 3 + dc_pin: 11 reset_pin: 10 invert_colors: true diff --git a/tests/components/image/test.esp32-idf.yaml b/tests/components/image/test.esp32-idf.yaml index 34c7914976..e903afea1f 100644 --- a/tests/components/image/test.esp32-idf.yaml +++ b/tests/components/image/test.esp32-idf.yaml @@ -2,13 +2,13 @@ spi: - id: spi_main_lcd clk_pin: 16 mosi_pin: 17 - miso_pin: 15 + miso_pin: 18 display: - platform: ili9xxx id: main_lcd model: ili9342 - cs_pin: 12 + cs_pin: 19 dc_pin: 13 reset_pin: 21 invert_colors: true diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 2b72f31770..6f79a1f810 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -608,6 +608,58 @@ lvgl: id: tabview_id index: 0 animated: true + - meter: + height: 200px + width: 200px + indicator: + bg_color: 0xFF + radius: 0 + bg_opa: TRANSP + text_color: 0xFFFFFF + scales: + - ticks: + width: 1 + count: 61 + length: 20 + color: 0xFFFFFF + range_from: 0 + range_to: 60 + angle_range: 360 + rotation: 270 + indicators: + - line: + opa: 50% + id: minute_hand + color: 0xFF0000 + r_mod: -1 + width: 3 + - + angle_range: 330 + rotation: 300 + range_from: 1 + range_to: 12 + ticks: + width: 1 + count: 12 + length: 1 + major: + stride: 1 + width: 4 + length: 8 + color: 0xC0C0C0 + label_gap: 6 + - angle_range: 360 + rotation: 270 + range_from: 0 + range_to: 720 + indicators: + - line: + id: hour_hand + value: 180 + width: 4 + color: 0xA0A0A0 + r_mod: -20 + font: - file: "gfonts://Roboto" id: space16 diff --git a/tests/components/lvgl/test.esp32-idf.yaml b/tests/components/lvgl/test.esp32-idf.yaml index 927d72d15c..05a1f243ed 100644 --- a/tests/components/lvgl/test.esp32-idf.yaml +++ b/tests/components/lvgl/test.esp32-idf.yaml @@ -10,7 +10,7 @@ sensor: - platform: rotary_encoder name: "Rotary Encoder" id: encoder - pin_a: 2 + pin_a: 3 pin_b: 1 internal: true From 03a95ee05f21fe0b4e71b9a8629610e875baefb9 Mon Sep 17 00:00:00 2001 From: YorkshireIoT <55233103+YorkshireIoT@users.noreply.github.com> Date: Mon, 7 Oct 2024 03:34:46 +0100 Subject: [PATCH 0441/1052] Feature/add seeed grove gmxxx multichannel gas support (#4304) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + .../components/grove_gas_mc_v2/__init__.py | 0 .../grove_gas_mc_v2/grove_gas_mc_v2.cpp | 88 +++++++++++++++++++ .../grove_gas_mc_v2/grove_gas_mc_v2.h | 39 ++++++++ esphome/components/grove_gas_mc_v2/sensor.py | 77 ++++++++++++++++ esphome/const.py | 3 + tests/components/grove_gas_mc_v2/common.yaml | 13 +++ .../grove_gas_mc_v2/test.esp32-ard.yaml | 6 ++ .../grove_gas_mc_v2/test.esp32-idf.yaml | 6 ++ .../grove_gas_mc_v2/test.esp8266-ard.yaml | 6 ++ .../grove_gas_mc_v2/test.rp2040-ard.yaml | 6 ++ 11 files changed, 245 insertions(+) create mode 100644 esphome/components/grove_gas_mc_v2/__init__.py create mode 100644 esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp create mode 100644 esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h create mode 100644 esphome/components/grove_gas_mc_v2/sensor.py create mode 100644 tests/components/grove_gas_mc_v2/common.yaml create mode 100644 tests/components/grove_gas_mc_v2/test.esp32-ard.yaml create mode 100644 tests/components/grove_gas_mc_v2/test.esp32-idf.yaml create mode 100644 tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml create mode 100644 tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index db48e25743..180024cd37 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -162,6 +162,7 @@ esphome/components/gps/* @coogle esphome/components/graph/* @synco esphome/components/graphical_display_menu/* @MrMDavidson esphome/components/gree/* @orestismers +esphome/components/grove_gas_mc_v2/* @YorkshireIoT esphome/components/grove_tb6612fng/* @max246 esphome/components/growatt_solar/* @leeuwte esphome/components/gt911/* @clydebarrow @jesserockz diff --git a/esphome/components/grove_gas_mc_v2/__init__.py b/esphome/components/grove_gas_mc_v2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..ed40ba42a5 --- /dev/null +++ b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp @@ -0,0 +1,88 @@ +#include "grove_gas_mc_v2.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace grove_gas_mc_v2 { + +static const char *const TAG = "grove_gas_mc_v2"; + +// I2C Commands for Grove Gas Multichannel V2 Sensor +// Taken from: +// https://github.com/Seeed-Studio/Seeed_Arduino_MultiGas/blob/master/src/Multichannel_Gas_GroveGasMultichannelV2.h +static const uint8_t GROVE_GAS_MC_V2_HEAT_ON = 0xFE; +static const uint8_t GROVE_GAS_MC_V2_HEAT_OFF = 0xFF; +static const uint8_t GROVE_GAS_MC_V2_READ_GM102B = 0x01; +static const uint8_t GROVE_GAS_MC_V2_READ_GM302B = 0x03; +static const uint8_t GROVE_GAS_MC_V2_READ_GM502B = 0x05; +static const uint8_t GROVE_GAS_MC_V2_READ_GM702B = 0x07; + +bool GroveGasMultichannelV2Component::read_sensor_(uint8_t address, sensor::Sensor *sensor) { + if (sensor == nullptr) { + return true; + } + uint32_t value = 0; + if (!this->read_bytes(address, (uint8_t *) &value, 4)) { + ESP_LOGW(TAG, "Reading Grove Gas Sensor data failed!"); + this->error_code_ = COMMUNICATION_FAILED; + this->status_set_warning(); + return false; + } + sensor->publish_state(value); + return true; +} + +void GroveGasMultichannelV2Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up Grove Multichannel Gas Sensor V2..."); + + // Before reading sensor values, must preheat sensor + if (!(this->write_bytes(GROVE_GAS_MC_V2_HEAT_ON, {}))) { + this->mark_failed(); + this->error_code_ = APP_START_FAILED; + } +} + +void GroveGasMultichannelV2Component::update() { + // Read from each of the gas sensors + if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM102B, this->nitrogen_dioxide_sensor_)) + return; + if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM302B, this->ethanol_sensor_)) + return; + if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM502B, this->tvoc_sensor_)) + return; + if (!this->read_sensor_(GROVE_GAS_MC_V2_READ_GM702B, this->carbon_monoxide_sensor_)) + return; + + this->status_clear_warning(); +} + +void GroveGasMultichannelV2Component::dump_config() { + ESP_LOGCONFIG(TAG, "Grove Multichannel Gas Sensor V2"); + LOG_I2C_DEVICE(this) + LOG_UPDATE_INTERVAL(this) + LOG_SENSOR(" ", "Nitrogen Dioxide", this->nitrogen_dioxide_sensor_) + LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_) + LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_) + LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_) + + if (this->is_failed()) { + switch (this->error_code_) { + case COMMUNICATION_FAILED: + ESP_LOGW(TAG, "Communication failed! Is the sensor connected?"); + break; + case APP_INVALID: + ESP_LOGW(TAG, "Sensor reported invalid APP installed."); + break; + case APP_START_FAILED: + ESP_LOGW(TAG, "Sensor reported APP start failed."); + break; + case UNKNOWN: + default: + ESP_LOGW(TAG, "Unknown setup error!"); + break; + } + } +} + +} // namespace grove_gas_mc_v2 +} // namespace esphome diff --git a/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h new file mode 100644 index 0000000000..1987d33f37 --- /dev/null +++ b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h @@ -0,0 +1,39 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace grove_gas_mc_v2 { + +class GroveGasMultichannelV2Component : public PollingComponent, public i2c::I2CDevice { + SUB_SENSOR(tvoc) + SUB_SENSOR(carbon_monoxide) + SUB_SENSOR(nitrogen_dioxide) + SUB_SENSOR(ethanol) + + public: + /// Setup the sensor and test for a connection. + void setup() override; + /// Schedule temperature+pressure readings. + void update() override; + + void dump_config() override; + + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + enum ErrorCode { + UNKNOWN, + COMMUNICATION_FAILED, + APP_INVALID, + APP_START_FAILED, + } error_code_{UNKNOWN}; + + bool read_sensor_(uint8_t address, sensor::Sensor *sensor); +}; + +} // namespace grove_gas_mc_v2 +} // namespace esphome diff --git a/esphome/components/grove_gas_mc_v2/sensor.py b/esphome/components/grove_gas_mc_v2/sensor.py new file mode 100644 index 0000000000..0c35047850 --- /dev/null +++ b/esphome/components/grove_gas_mc_v2/sensor.py @@ -0,0 +1,77 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_CARBON_MONOXIDE, + CONF_ETHANOL, + CONF_ID, + CONF_NITROGEN_DIOXIDE, + CONF_TVOC, + DEVICE_CLASS_CARBON_MONOXIDE, + DEVICE_CLASS_NITROGEN_DIOXIDE, + DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + ICON_AIR_FILTER, + ICON_FLASK_ROUND_BOTTOM, + ICON_GAS_CYLINDER, + ICON_MOLECULE_CO, + STATE_CLASS_MEASUREMENT, + UNIT_MICROGRAMS_PER_CUBIC_METER, + UNIT_PARTS_PER_MILLION, +) + +CODEOWNERS = ["@YorkshireIoT"] +DEPENDENCIES = ["i2c"] + +grove_gas_mc_v2_ns = cg.esphome_ns.namespace("grove_gas_mc_v2") + +GroveGasMultichannelV2Component = grove_gas_mc_v2_ns.class_( + "GroveGasMultichannelV2Component", cg.PollingComponent, i2c.I2CDevice +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(GroveGasMultichannelV2Component), + cv.Optional(CONF_TVOC): sensor.sensor_schema( + unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, + icon=ICON_AIR_FILTER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_CARBON_MONOXIDE): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + icon=ICON_MOLECULE_CO, + accuracy_decimals=0, + device_class=DEVICE_CLASS_CARBON_MONOXIDE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_NITROGEN_DIOXIDE): sensor.sensor_schema( + unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, + icon=ICON_GAS_CYLINDER, + accuracy_decimals=0, + device_class=DEVICE_CLASS_NITROGEN_DIOXIDE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_ETHANOL): sensor.sensor_schema( + unit_of_measurement=UNIT_PARTS_PER_MILLION, + icon=ICON_FLASK_ROUND_BOTTOM, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x08)) +) + + +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) + + for key in [CONF_TVOC, CONF_CARBON_MONOXIDE, CONF_NITROGEN_DIOXIDE, CONF_ETHANOL]: + if sensor_config := config.get(key): + sensor_ = await sensor.new_sensor(sensor_config) + cg.add(getattr(var, f"set_{key}_sensor")(sensor_)) diff --git a/esphome/const.py b/esphome/const.py index bfb0167282..506a30f5ed 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -962,6 +962,7 @@ ICON_ACCELERATION_Y = "mdi:axis-y-arrow" ICON_ACCELERATION_Z = "mdi:axis-z-arrow" ICON_ACCOUNT = "mdi:account" ICON_ACCOUNT_CHECK = "mdi:account-check" +ICON_AIR_FILTER = "mdi:air-filter" ICON_ARROW_EXPAND_VERTICAL = "mdi:arrow-expand-vertical" ICON_BATTERY = "mdi:battery" ICON_BLUETOOTH = "mdi:bluetooth" @@ -983,6 +984,7 @@ ICON_FINGERPRINT = "mdi:fingerprint" ICON_FLASH = "mdi:flash" ICON_FLASK = "mdi:flask" ICON_FLASK_OUTLINE = "mdi:flask-outline" +ICON_FLASK_ROUND_BOTTOM = "mdi:flask-round-bottom" ICON_FLOWER = "mdi:flower" ICON_GAS_CYLINDER = "mdi:gas-cylinder" ICON_GAUGE = "mdi:gauge" @@ -995,6 +997,7 @@ ICON_KEY_PLUS = "mdi:key-plus" ICON_LIGHTBULB = "mdi:lightbulb" ICON_MAGNET = "mdi:magnet" ICON_MEMORY = "mdi:memory" +ICON_MOLECULE_CO = "mdi:molecule-co" ICON_MOLECULE_CO2 = "mdi:molecule-co2" ICON_MOTION_SENSOR = "mdi:motion-sensor" ICON_NEW_BOX = "mdi:new-box" diff --git a/tests/components/grove_gas_mc_v2/common.yaml b/tests/components/grove_gas_mc_v2/common.yaml new file mode 100644 index 0000000000..0729e6b9c7 --- /dev/null +++ b/tests/components/grove_gas_mc_v2/common.yaml @@ -0,0 +1,13 @@ +sensor: + - platform: grove_gas_mc_v2 + i2c_id: i2c_bus + nitrogen_dioxide: + name: "Nitrogen Dioxide" + ethanol: + name: "Ethanol" + carbon_monoxide: + name: "Carbon Monoxide" + tvoc: + name: "Volatile Organic Compounds" + update_interval: 30s + address: 0xAD diff --git a/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml new file mode 100644 index 0000000000..00c7856c36 --- /dev/null +++ b/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml @@ -0,0 +1,6 @@ +i2c: + sda: 21 + scl: 22 + id: i2c_bus + +<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml b/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml new file mode 100644 index 0000000000..00c7856c36 --- /dev/null +++ b/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +i2c: + sda: 21 + scl: 22 + id: i2c_bus + +<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml new file mode 100644 index 0000000000..2de18bdf39 --- /dev/null +++ b/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml @@ -0,0 +1,6 @@ +i2c: + sda: 4 + scl: 5 + id: i2c_bus + +<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml b/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml new file mode 100644 index 0000000000..00c7856c36 --- /dev/null +++ b/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml @@ -0,0 +1,6 @@ +i2c: + sda: 21 + scl: 22 + id: i2c_bus + +<<: !include common.yaml From 6a2ed8241edf18867cdd61917cda0a406e179816 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:43:41 +1100 Subject: [PATCH 0442/1052] [lvgl] Fix: allow full range of styles on dropdown list. (#7552) --- esphome/components/lvgl/lv_validation.py | 2 + esphome/components/lvgl/widgets/dropdown.py | 6 ++- esphome/components/lvgl/widgets/meter.py | 2 +- tests/components/lvgl/lvgl-package.yaml | 50 +++++++++++++++++++++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index bd98319fd3..fd840cc417 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -242,6 +242,8 @@ def pixels_or_percent_validator(value): """A length in one axis - either a number (pixels) or a percentage""" if value == SCHEMA_EXTRACT: return ["pixels", "..%"] + if isinstance(value, str) and value.lower().endswith("px"): + value = cv.int_(value[:-2]) value = cv.Any(cv.int_, cv.percentage)(value) if isinstance(value, int): return value diff --git a/esphome/components/lvgl/widgets/dropdown.py b/esphome/components/lvgl/widgets/dropdown.py index dc0346b080..4fd7d8a7ee 100644 --- a/esphome/components/lvgl/widgets/dropdown.py +++ b/esphome/components/lvgl/widgets/dropdown.py @@ -6,6 +6,8 @@ from ..defines import ( CONF_DIR, CONF_INDICATOR, CONF_MAIN, + CONF_SCROLLBAR, + CONF_SELECTED, CONF_SELECTED_INDEX, CONF_SYMBOL, DIRECTIONS, @@ -23,7 +25,9 @@ CONF_DROPDOWN_LIST = "dropdown_list" lv_dropdown_t = LvSelect("lv_dropdown_t") lv_dropdown_list_t = LvType("lv_dropdown_list_t") -dropdown_list_spec = WidgetType(CONF_DROPDOWN_LIST, lv_dropdown_list_t, (CONF_MAIN,)) +dropdown_list_spec = WidgetType( + CONF_DROPDOWN_LIST, lv_dropdown_list_t, (CONF_MAIN, CONF_SELECTED, CONF_SCROLLBAR) +) DROPDOWN_BASE_SCHEMA = cv.Schema( { diff --git a/esphome/components/lvgl/widgets/meter.py b/esphome/components/lvgl/widgets/meter.py index bc455ccebc..cd61d1c775 100644 --- a/esphome/components/lvgl/widgets/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -309,6 +309,6 @@ async def set_indicator_values(meter, indicator, config): lv.meter_set_indicator_start_value(meter, indicator, start_value) if end_value is not None: lv.meter_set_indicator_end_value(meter, indicator, end_value) - if opa := config.get(CONF_OPA): + if (opa := config.get(CONF_OPA)) is not None: lv_assign(indicator.opa, await opacity.process(opa)) lv_obj.invalidate(meter) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 6f79a1f810..c968198e26 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -1,3 +1,16 @@ +substitutions: + light_recessed: "\U000F179B" + wall_sconce_round: "\U000F0748" + gas_burner: "\U000F1A1B" + home_icon: "\U000F02DC" + menu_left: "\U000F0A02" + menu_right: "\U000F035F" + close: "\U000F0156" + delete: "\U000F01B4" + backspace: "\U000F006E" + check: "\U000F012C" + arrow_down: "\U000F004B" + lvgl: log_level: TRACE bg_color: light_blue @@ -599,6 +612,42 @@ lvgl: - name: Cat id: tabview_tab_2 widgets: + - dropdown: + indicator: + text_font: helvetica20 + id: lv_dropdown + options: + - First + - Second + - Third + - 4th + - 5th + - 6th + - 7th + - 8th + - 9th + selected_index: 2 + dir: top + symbol: ${arrow_down} + dropdown_list: + max_height: 100px + bg_color: 0x000080 + text_color: 0xFF00 + selected: + bg_color: 0xFFFF00 + checked: + bg_color: 0x00 + text_color: 0xFF0000 + scrollbar: + bg_color: 0xFF + on_value: + logger.log: + format: "Dropdown changed = %d" + args: [x] + on_cancel: + logger.log: + format: "Dropdown closed = %d" + args: [x] - image: src: cat_image on_click: @@ -659,6 +708,7 @@ lvgl: width: 4 color: 0xA0A0A0 r_mod: -20 + opa: 0% font: - file: "gfonts://Roboto" From 86a34f4b17bf711570e2e18ae32a80fca0099321 Mon Sep 17 00:00:00 2001 From: RFDarter Date: Mon, 7 Oct 2024 04:52:26 +0200 Subject: [PATCH 0443/1052] [web_server] v3 entity grouping (#6833) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .../alarm_control_panel/__init__.py | 7 +- esphome/components/binary_sensor/__init__.py | 7 +- esphome/components/button/__init__.py | 7 +- esphome/components/climate/__init__.py | 7 +- esphome/components/cover/__init__.py | 9 +- esphome/components/datetime/__init__.py | 7 +- esphome/components/fan/__init__.py | 7 +- esphome/components/light/__init__.py | 7 +- esphome/components/lock/__init__.py | 7 +- esphome/components/number/__init__.py | 8 +- esphome/components/select/__init__.py | 7 +- esphome/components/sensor/__init__.py | 7 +- esphome/components/switch/__init__.py | 7 +- esphome/components/text/__init__.py | 7 +- esphome/components/text_sensor/__init__.py | 7 +- esphome/components/update/__init__.py | 7 +- esphome/components/valve/__init__.py | 7 +- esphome/components/web_server/__init__.py | 124 +++++++++++++----- esphome/components/web_server/web_server.cpp | 79 ++++++++++- esphome/components/web_server/web_server.h | 11 +- esphome/const.py | 1 - tests/components/web_server/common_v3.yaml | 37 ++++++ .../web_server/test_v3.esp32-ard.yaml | 1 + 23 files changed, 265 insertions(+), 110 deletions(-) create mode 100644 tests/components/web_server/common_v3.yaml create mode 100644 tests/components/web_server/test_v3.esp32-ard.yaml diff --git a/esphome/components/alarm_control_panel/__init__.py b/esphome/components/alarm_control_panel/__init__.py index 8987d708fd..379fbf32f9 100644 --- a/esphome/components/alarm_control_panel/__init__.py +++ b/esphome/components/alarm_control_panel/__init__.py @@ -9,7 +9,7 @@ from esphome.const import ( CONF_MQTT_ID, CONF_ON_STATE, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity @@ -195,9 +195,8 @@ async def setup_alarm_control_panel_core_(var, config): for conf in config.get(CONF_ON_READY, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) if mqtt_id := config.get(CONF_MQTT_ID): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 95fd17bcc0..d947c2aba6 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -25,7 +25,7 @@ from esphome.const import ( CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY_CHARGING, DEVICE_CLASS_CARBON_MONOXIDE, @@ -543,9 +543,8 @@ async def setup_binary_sensor_core_(var, config): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_binary_sensor(var, config): diff --git a/esphome/components/button/__init__.py b/esphome/components/button/__init__.py index 3010d3006a..366d0edf7d 100644 --- a/esphome/components/button/__init__.py +++ b/esphome/components/button/__init__.py @@ -11,7 +11,7 @@ from esphome.const import ( CONF_MQTT_ID, CONF_ON_PRESS, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_EMPTY, DEVICE_CLASS_IDENTIFY, DEVICE_CLASS_RESTART, @@ -97,9 +97,8 @@ async def setup_button_core_(var, config): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_button(var, config): diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index c7e4ce7745..b302e2ab4e 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -43,7 +43,7 @@ from esphome.const import ( CONF_TEMPERATURE_STEP, CONF_TRIGGER_ID, CONF_VISUAL, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity @@ -408,9 +408,8 @@ async def setup_climate_core_(var, config): trigger, [(ClimateCall.operator("ref"), "x")], conf ) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_climate(var, config): diff --git a/esphome/components/cover/__init__.py b/esphome/components/cover/__init__.py index d25dd91148..e7e3ac3bb0 100644 --- a/esphome/components/cover/__init__.py +++ b/esphome/components/cover/__init__.py @@ -17,7 +17,7 @@ from esphome.const import ( CONF_TILT_COMMAND_TOPIC, CONF_TILT_STATE_TOPIC, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_AWNING, DEVICE_CLASS_BLIND, DEVICE_CLASS_CURTAIN, @@ -137,10 +137,6 @@ async def setup_cover_core_(var, config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) - if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) @@ -156,6 +152,9 @@ async def setup_cover_core_(var, config): if (tilt_command_topic := config.get(CONF_TILT_COMMAND_TOPIC)) is not None: cg.add(mqtt_.set_custom_tilt_command_topic(tilt_command_topic)) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) + async def register_cover(var, config): if not CORE.has_id(config[CONF_ID]): diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 55066006d3..7edf527e01 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -18,7 +18,7 @@ from esphome.const import ( CONF_TIME_ID, CONF_TRIGGER_ID, CONF_TYPE, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, CONF_YEAR, ) from esphome.core import CORE, coroutine_with_priority @@ -138,9 +138,8 @@ async def setup_datetime_core_(var, config): if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) for conf in config.get(CONF_ON_VALUE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(cg.ESPTime, "x")], conf) diff --git a/esphome/components/fan/__init__.py b/esphome/components/fan/__init__.py index 62624ec6e3..4e0e52cd65 100644 --- a/esphome/components/fan/__init__.py +++ b/esphome/components/fan/__init__.py @@ -25,7 +25,7 @@ from esphome.const import ( CONF_SPEED_LEVEL_STATE_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity @@ -218,9 +218,8 @@ async def setup_fan_core_(var, config): if (speed_command_topic := config.get(CONF_SPEED_COMMAND_TOPIC)) is not None: cg.add(mqtt_.set_custom_speed_command_topic(speed_command_topic)) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) for conf in config.get(CONF_ON_STATE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index d9f139d2f4..7e16b7a648 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -18,7 +18,7 @@ from esphome.const import ( CONF_RESTORE_MODE, CONF_TRIGGER_ID, CONF_WARM_WHITE_COLOR_TEMPERATURE, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, ) from esphome.core import coroutine_with_priority from esphome.cpp_helpers import setup_entity @@ -181,9 +181,8 @@ async def setup_light_core_(light_var, output_var, config): mqtt_ = cg.new_Pvariable(mqtt_id, light_var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, light_var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(light_var, web_server_config) async def register_light(output_var, config): diff --git a/esphome/components/lock/__init__.py b/esphome/components/lock/__init__.py index 6b92bc264b..6925861b52 100644 --- a/esphome/components/lock/__init__.py +++ b/esphome/components/lock/__init__.py @@ -9,7 +9,7 @@ from esphome.const import ( CONF_ON_LOCK, CONF_ON_UNLOCK, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity @@ -66,9 +66,8 @@ async def setup_lock_core_(var, config): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_lock(var, config): diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index ece738af49..f45cfd54f2 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -18,7 +18,7 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_UNIT_OF_MEASUREMENT, CONF_VALUE, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, DEVICE_CLASS_ATMOSPHERIC_PRESSURE, @@ -254,10 +254,8 @@ async def setup_number_core_( if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_number( diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index 2bc68d43ec..5a3271fdfd 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -14,7 +14,7 @@ from esphome.const import ( CONF_OPERATION, CONF_OPTION, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_generator import MockObjClass @@ -104,9 +104,8 @@ async def setup_select_core_(var, config, *, options: list[str]): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_select(var, config, *, options: list[str]): diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 867cdc1f48..27338b8608 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -36,7 +36,7 @@ from esphome.const import ( CONF_TYPE, CONF_UNIT_OF_MEASUREMENT, CONF_VALUE, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, CONF_WINDOW_SIZE, DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_AQI, @@ -800,9 +800,8 @@ async def setup_sensor_core_(var, config): else: cg.add(mqtt_.set_expire_after(expire_after)) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_sensor(var, config): diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index fef4f7f007..0f159f69ec 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -14,7 +14,7 @@ from esphome.const import ( CONF_ON_TURN_ON, CONF_RESTORE_MODE, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_EMPTY, DEVICE_CLASS_OUTLET, DEVICE_CLASS_SWITCH, @@ -156,9 +156,8 @@ async def setup_switch_core_(var, config): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: cg.add(var.set_device_class(device_class)) diff --git a/esphome/components/text/__init__.py b/esphome/components/text/__init__.py index 386baaf756..20e5a645d1 100644 --- a/esphome/components/text/__init__.py +++ b/esphome/components/text/__init__.py @@ -11,7 +11,7 @@ from esphome.const import ( CONF_ON_VALUE, CONF_TRIGGER_ID, CONF_VALUE, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, ) from esphome.core import CORE, coroutine_with_priority from esphome.cpp_helpers import setup_entity @@ -82,9 +82,8 @@ async def setup_text_core_( mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_text( diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index ba8a2def41..12993d9ffc 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -15,7 +15,7 @@ from esphome.const import ( CONF_STATE, CONF_TO, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_DATE, DEVICE_CLASS_EMPTY, DEVICE_CLASS_TIMESTAMP, @@ -212,9 +212,8 @@ async def setup_text_sensor_core_(var, config): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_text_sensor(var, config): diff --git a/esphome/components/update/__init__.py b/esphome/components/update/__init__.py index ba3b2f20df..4729d954ee 100644 --- a/esphome/components/update/__init__.py +++ b/esphome/components/update/__init__.py @@ -8,7 +8,7 @@ from esphome.const import ( CONF_FORCE_UPDATE, CONF_ID, CONF_MQTT_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_EMPTY, DEVICE_CLASS_FIRMWARE, ENTITY_CATEGORY_CONFIG, @@ -73,9 +73,8 @@ async def setup_update_core_(var, config): mqtt_ = cg.new_Pvariable(mqtt_id_config, var) await mqtt.register_mqtt_component(mqtt_, config) - if web_server_id_config := config.get(CONF_WEB_SERVER_ID): - web_server_ = await cg.get_variable(web_server_id_config) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_update(var, config): diff --git a/esphome/components/valve/__init__.py b/esphome/components/valve/__init__.py index 3c03bab857..e55bb522de 100644 --- a/esphome/components/valve/__init__.py +++ b/esphome/components/valve/__init__.py @@ -14,7 +14,7 @@ from esphome.const import ( CONF_STATE, CONF_STOP, CONF_TRIGGER_ID, - CONF_WEB_SERVER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_EMPTY, DEVICE_CLASS_GAS, DEVICE_CLASS_WATER, @@ -124,9 +124,8 @@ async def setup_valve_core_(var, config): mqtt_.set_custom_position_command_topic(position_command_topic_config) ) - if (webserver_id := config.get(CONF_WEB_SERVER_ID)) is not None: - web_server_ = await cg.get_variable(webserver_id) - web_server.add_entity_to_sorting_list(web_server_, var, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) async def register_valve(var, config): diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index 02074dcf11..d846a3418b 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -17,13 +17,14 @@ from esphome.const import ( CONF_JS_URL, CONF_LOCAL, CONF_LOG, + CONF_NAME, CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_USERNAME, CONF_VERSION, + CONF_WEB_SERVER, CONF_WEB_SERVER_ID, - CONF_WEB_SERVER_SORTING_WEIGHT, PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, @@ -34,9 +35,15 @@ import esphome.final_validate as fv AUTO_LOAD = ["json", "web_server_base"] +CONF_SORTING_GROUP_ID = "sorting_group_id" +CONF_SORTING_GROUPS = "sorting_groups" +CONF_SORTING_WEIGHT = "sorting_weight" + web_server_ns = cg.esphome_ns.namespace("web_server") WebServer = web_server_ns.class_("WebServer", cg.Component, cg.Controller) +sorting_groups = {} + def default_url(config): config = config.copy() @@ -70,42 +77,74 @@ def validate_ota(config): return config -def _validate_no_sorting_weight( - webserver_version: int, config: dict, path: list[str] | None = None -) -> None: - if path is None: - path = [] - if CONF_WEB_SERVER_SORTING_WEIGHT in config: - raise cv.FinalExternalInvalid( - f"Sorting weight on entities is not supported in web_server version {webserver_version}", - path=path + [CONF_WEB_SERVER_SORTING_WEIGHT], +def validate_sorting_groups(config): + if CONF_SORTING_GROUPS in config and config[CONF_VERSION] != 3: + raise cv.Invalid( + f"'{CONF_SORTING_GROUPS}' is only supported in 'web_server' version 3" ) - for p, value in config.items(): - if isinstance(value, dict): - _validate_no_sorting_weight(webserver_version, value, path + [p]) - elif isinstance(value, list): - for i, item in enumerate(value): - if isinstance(item, dict): - _validate_no_sorting_weight(webserver_version, item, path + [p, i]) - - -def _final_validate_sorting_weight(config): - if (webserver_version := config.get(CONF_VERSION)) != 3: - _validate_no_sorting_weight(webserver_version, fv.full_config.get()) - return config -FINAL_VALIDATE_SCHEMA = _final_validate_sorting_weight +def _validate_no_sorting_component( + sorting_component: str, + webserver_version: int, + config: dict, + path: list[str] | None = None, +) -> None: + if path is None: + path = [] + if CONF_WEB_SERVER in config and sorting_component in config[CONF_WEB_SERVER]: + raise cv.FinalExternalInvalid( + f"{sorting_component} on entities is not supported in web_server version {webserver_version}", + path=path + [sorting_component], + ) + for p, value in config.items(): + if isinstance(value, dict): + _validate_no_sorting_component( + sorting_component, webserver_version, value, path + [p] + ) + elif isinstance(value, list): + for i, item in enumerate(value): + if isinstance(item, dict): + _validate_no_sorting_component( + sorting_component, webserver_version, item, path + [p, i] + ) +def _final_validate_sorting(config): + if (webserver_version := config.get(CONF_VERSION)) != 3: + _validate_no_sorting_component( + CONF_SORTING_WEIGHT, webserver_version, fv.full_config.get() + ) + _validate_no_sorting_component( + CONF_SORTING_GROUP_ID, webserver_version, fv.full_config.get() + ) + return config + + +FINAL_VALIDATE_SCHEMA = _final_validate_sorting + +sorting_group = { + cv.Required(CONF_ID): cv.declare_id(cg.int_), + cv.Required(CONF_NAME): cv.string, + cv.Optional(CONF_SORTING_WEIGHT): cv.float_, +} + WEBSERVER_SORTING_SCHEMA = cv.Schema( { - cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer), - cv.Optional(CONF_WEB_SERVER_SORTING_WEIGHT): cv.All( - cv.requires_component("web_server"), - cv.float_, - ), + cv.Optional(CONF_WEB_SERVER): cv.Schema( + { + cv.OnlyWith(CONF_WEB_SERVER_ID, "web_server"): cv.use_id(WebServer), + cv.Optional(CONF_SORTING_WEIGHT): cv.All( + cv.requires_component("web_server"), + cv.float_, + ), + cv.Optional(CONF_SORTING_GROUP_ID): cv.All( + cv.requires_component("web_server"), + cv.use_id(cg.int_), + ), + } + ) } ) @@ -145,24 +184,38 @@ CONFIG_SCHEMA = cv.All( ): cv.boolean, cv.Optional(CONF_LOG, default=True): cv.boolean, cv.Optional(CONF_LOCAL): cv.boolean, + cv.Optional(CONF_SORTING_GROUPS): cv.ensure_list(sorting_group), } ).extend(cv.COMPONENT_SCHEMA), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX, PLATFORM_RTL87XX]), default_url, validate_local, validate_ota, + validate_sorting_groups, ) -def add_entity_to_sorting_list(web_server, entity, config): - sorting_weight = 50 - if CONF_WEB_SERVER_SORTING_WEIGHT in config: - sorting_weight = config[CONF_WEB_SERVER_SORTING_WEIGHT] +def add_sorting_groups(web_server_var, config): + for group in config: + sorting_groups[group[CONF_ID]] = group[CONF_NAME] + group_sorting_weight = group.get(CONF_SORTING_WEIGHT, 50) + cg.add( + web_server_var.add_sorting_group( + hash(group[CONF_ID]), group[CONF_NAME], group_sorting_weight + ) + ) + + +async def add_entity_config(entity, config): + web_server = await cg.get_variable(config[CONF_WEB_SERVER_ID]) + sorting_weight = config.get(CONF_SORTING_WEIGHT, 50) + sorting_group_hash = hash(config.get(CONF_SORTING_GROUP_ID)) cg.add( - web_server.add_entity_to_sorting_list( + web_server.add_entity_config( entity, sorting_weight, + sorting_group_hash, ) ) @@ -241,3 +294,6 @@ async def to_code(config): cg.add(var.set_include_internal(config[CONF_INCLUDE_INTERNAL])) if CONF_LOCAL in config and config[CONF_LOCAL]: cg.add_define("USE_WEBSERVER_LOCAL") + + if (sorting_group_config := config.get(CONF_SORTING_GROUPS)) is not None: + add_sorting_groups(var, sorting_group_config) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index dc27db2f41..192feb78d5 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -105,6 +105,14 @@ void WebServer::setup() { // Configure reconnect timeout and send config client->send(this->get_config_json().c_str(), "ping", millis(), 30000); + for (auto &group : this->sorting_groups_) { + client->send(json::build_json([group](JsonObject root) { + root["name"] = group.second.name; + root["sorting_weight"] = group.second.weight; + }).c_str(), + "sorting_group"); + } + this->entities_iterator_.begin(this->include_internal_); }); @@ -246,6 +254,9 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } if (!obj->get_unit_of_measurement().empty()) root["uom"] = obj->get_unit_of_measurement(); @@ -284,6 +295,9 @@ std::string WebServer::text_sensor_json(text_sensor::TextSensor *obj, const std: if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -332,6 +346,9 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value, JsonDetail root["assumed_state"] = obj->assumed_state(); if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -368,6 +385,9 @@ std::string WebServer::button_json(button::Button *obj, JsonDetail start_config) if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -404,6 +424,9 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -487,6 +510,9 @@ std::string WebServer::fan_json(fan::Fan *obj, JsonDetail start_config) { if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -603,6 +629,9 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi } if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -684,6 +713,9 @@ std::string WebServer::cover_json(cover::Cover *obj, JsonDetail start_config) { if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -745,6 +777,9 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail root["uom"] = obj->traits.get_unit_of_measurement(); if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } if (std::isnan(value)) { @@ -814,6 +849,9 @@ std::string WebServer::date_json(datetime::DateEntity *obj, JsonDetail start_con if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -872,6 +910,9 @@ std::string WebServer::time_json(datetime::TimeEntity *obj, JsonDetail start_con if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -931,6 +972,9 @@ std::string WebServer::datetime_json(datetime::DateTimeEntity *obj, JsonDetail s if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -992,6 +1036,9 @@ std::string WebServer::text_json(text::Text *obj, const std::string &value, Json root["mode"] = (int) obj->traits.get_mode(); if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -1048,6 +1095,9 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value } if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -1164,6 +1214,9 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf } if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } @@ -1257,6 +1310,9 @@ std::string WebServer::lock_json(lock::Lock *obj, lock::LockState value, JsonDet if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -1326,8 +1382,13 @@ std::string WebServer::valve_json(valve::Valve *obj, JsonDetail start_config) { if (obj->get_traits().get_supports_position()) root["position"] = obj->position; - if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { - root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (start_config == DETAIL_ALL) { + if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { + root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } + } } }); } @@ -1367,6 +1428,9 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro if (start_config == DETAIL_ALL) { if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -1453,6 +1517,9 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c root["release_url"] = obj->update_info.release_url; if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } } } }); @@ -1751,8 +1818,12 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { bool WebServer::isRequestHandlerTrivial() { return false; } -void WebServer::add_entity_to_sorting_list(EntityBase *entity, float weight) { - this->sorting_entitys_[entity] = SortingComponents{weight}; +void WebServer::add_entity_config(EntityBase *entity, float weight, uint64_t group) { + this->sorting_entitys_[entity] = SortingComponents{weight, group}; +} + +void WebServer::add_sorting_group(uint64_t group_id, const std::string &group_name, float weight) { + this->sorting_groups_[group_id] = SortingGroup{group_name, weight}; } void WebServer::schedule_(std::function &&f) { diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index 3195fa7109..ea1f62fc43 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -44,6 +44,12 @@ struct UrlMatch { struct SortingComponents { float weight; + uint64_t group_id; +}; + +struct SortingGroup { + std::string name; + float weight; }; enum JsonDetail { DETAIL_ALL, DETAIL_STATE }; @@ -337,7 +343,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { /// This web handle is not trivial. bool isRequestHandlerTrivial() override; // NOLINT(readability-identifier-naming) - void add_entity_to_sorting_list(EntityBase *entity, float weight); + void add_entity_config(EntityBase *entity, float weight, uint64_t group); + void add_sorting_group(uint64_t group_id, const std::string &group_name, float weight); protected: void schedule_(std::function &&f); @@ -346,6 +353,8 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { AsyncEventSource events_{"/events"}; ListEntitiesIterator entities_iterator_; std::map sorting_entitys_; + std::map sorting_groups_; + #if USE_WEBSERVER_VERSION == 1 const char *css_url_{nullptr}; const char *js_url_{nullptr}; diff --git a/esphome/const.py b/esphome/const.py index 506a30f5ed..08fb34976b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -934,7 +934,6 @@ CONF_WARM_WHITE_COLOR_TEMPERATURE = "warm_white_color_temperature" CONF_WATCHDOG_THRESHOLD = "watchdog_threshold" CONF_WEB_SERVER = "web_server" CONF_WEB_SERVER_ID = "web_server_id" -CONF_WEB_SERVER_SORTING_WEIGHT = "web_server_sorting_weight" CONF_WEIGHT = "weight" CONF_WHILE = "while" CONF_WHITE = "white" diff --git a/tests/components/web_server/common_v3.yaml b/tests/components/web_server/common_v3.yaml new file mode 100644 index 0000000000..69f4b67f15 --- /dev/null +++ b/tests/components/web_server/common_v3.yaml @@ -0,0 +1,37 @@ +packages: + device_base: !include common.yaml + +web_server: + port: 8080 + version: 3 + sorting_groups: + - id: sorting_group_1 + name: "Group 1 Diplayed Last" + sorting_weight: 40 + - id: sorting_group_2 + name: "Group 2 Displayed Third" + sorting_weight: 30 + - id: sorting_group_3 + name: "Group 3 Displayed Second" + sorting_weight: 20 + - id: sorting_group_4 + name: "Group 4 Displayed First" + sorting_weight: 10 + +number: + - platform: template + name: "Template number" + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + web_server: + sorting_group_id: sorting_group_1 + sorting_weight: -1 +switch: + - platform: template + name: "Template Switch" + optimistic: true + web_server: + sorting_group_id: sorting_group_2 + sorting_weight: -10 diff --git a/tests/components/web_server/test_v3.esp32-ard.yaml b/tests/components/web_server/test_v3.esp32-ard.yaml new file mode 100644 index 0000000000..00d05521e4 --- /dev/null +++ b/tests/components/web_server/test_v3.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common_v3.yaml From 5ad5ef5a42b5000facf203171d6b4efa41ac074c Mon Sep 17 00:00:00 2001 From: Ken Baker Date: Sun, 6 Oct 2024 22:58:28 -0400 Subject: [PATCH 0444/1052] Add Initial TE-M3200 pressure sensor support (#6862) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/tem3200/__init__.py | 0 esphome/components/tem3200/sensor.py | 55 +++++++ esphome/components/tem3200/tem3200.cpp | 151 ++++++++++++++++++ esphome/components/tem3200/tem3200.h | 30 ++++ tests/components/tem3200/common.yaml | 16 ++ tests/components/tem3200/test.esp32-ard.yaml | 5 + tests/components/tem3200/test.esp32-idf.yaml | 5 + .../components/tem3200/test.esp32-s3-ard.yaml | 5 + .../components/tem3200/test.esp32-s3-idf.yaml | 5 + .../components/tem3200/test.esp8266-ard.yaml | 5 + 11 files changed, 278 insertions(+) create mode 100644 esphome/components/tem3200/__init__.py create mode 100644 esphome/components/tem3200/sensor.py create mode 100644 esphome/components/tem3200/tem3200.cpp create mode 100644 esphome/components/tem3200/tem3200.h create mode 100644 tests/components/tem3200/common.yaml create mode 100644 tests/components/tem3200/test.esp32-ard.yaml create mode 100644 tests/components/tem3200/test.esp32-idf.yaml create mode 100644 tests/components/tem3200/test.esp32-s3-ard.yaml create mode 100644 tests/components/tem3200/test.esp32-s3-idf.yaml create mode 100644 tests/components/tem3200/test.esp8266-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 180024cd37..f202936972 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -406,6 +406,7 @@ esphome/components/tca9555/* @mobrembski esphome/components/tcl112/* @glmnet esphome/components/tee501/* @Stock-M esphome/components/teleinfo/* @0hax +esphome/components/tem3200/* @bakerkj esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar esphome/components/template/datetime/* @rfdarter esphome/components/template/event/* @nohat diff --git a/esphome/components/tem3200/__init__.py b/esphome/components/tem3200/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/tem3200/sensor.py b/esphome/components/tem3200/sensor.py new file mode 100644 index 0000000000..5cd27433d0 --- /dev/null +++ b/esphome/components/tem3200/sensor.py @@ -0,0 +1,55 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c, sensor + +from esphome.const import ( + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, +) + +CODEOWNERS = ["@bakerkj"] +DEPENDENCIES = ["i2c"] + +tem3200_ns = cg.esphome_ns.namespace("tem3200") + +TEM3200Component = tem3200_ns.class_( + "TEM3200Component", cg.PollingComponent, i2c.I2CDevice +) + +CONF_RAW_PRESSURE = "raw_pressure" + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(TEM3200Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_RAW_PRESSURE): sensor.sensor_schema( + accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x28)) +) + + +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 temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature_sensor(sens)) + + if raw_pressure_config := config.get(CONF_RAW_PRESSURE): + sens = await sensor.new_sensor(raw_pressure_config) + cg.add(var.set_raw_pressure_sensor(sens)) diff --git a/esphome/components/tem3200/tem3200.cpp b/esphome/components/tem3200/tem3200.cpp new file mode 100644 index 0000000000..d8ce48af90 --- /dev/null +++ b/esphome/components/tem3200/tem3200.cpp @@ -0,0 +1,151 @@ +#include "tem3200.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace tem3200 { + +static const char *const TAG = "tem3200"; + +enum ErrorCode { + NONE = 0, + RESERVED = 1, + STALE = 2, + FAULT = 3, +}; + +void TEM3200Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up TEM3200..."); + + uint8_t status(NONE); + uint16_t raw_temperature(0); + uint16_t raw_pressure(0); + + i2c::ErrorCode err = this->read_(status, raw_temperature, raw_pressure); + if (err != i2c::ERROR_OK) { + ESP_LOGCONFIG(TAG, " I2C Communication Failed..."); + this->mark_failed(); + return; + } + + switch (status) { + case RESERVED: + ESP_LOGE(TAG, "Invalid RESERVED Device Status"); + this->mark_failed(); + return; + case FAULT: + ESP_LOGE(TAG, "FAULT condition in the SSC or sensing element"); + this->mark_failed(); + return; + case STALE: + ESP_LOGE(TAG, "STALE data. Data has not been updated since last fetch"); + this->status_set_warning(); + break; + } + ESP_LOGCONFIG(TAG, " Success..."); +} + +void TEM3200Component::dump_config() { + ESP_LOGCONFIG(TAG, "TEM3200:"); + LOG_I2C_DEVICE(this); + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Raw Pressure", this->raw_pressure_sensor_); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); +} + +float TEM3200Component::get_setup_priority() const { return setup_priority::DATA; } + +i2c::ErrorCode TEM3200Component::read_(uint8_t &status, uint16_t &raw_temperature, uint16_t &raw_pressure) { + uint8_t response[4] = {0x00, 0x00, 0x00, 0x00}; + + // initiate data read + i2c::ErrorCode err = this->read(response, 4); + if (err != i2c::ERROR_OK) { + return err; + } + + // extract top 2 bits of first byte for status + status = (ErrorCode) (response[0] & 0xc0) >> 6; + if (status == RESERVED || status == FAULT) { + return i2c::ERROR_OK; + } + + // if data is stale; reread + if (status == STALE) { + // wait for measurement 2ms + delay(2); + + err = this->read(response, 4); + if (err != i2c::ERROR_OK) { + return err; + } + } + + // extract top 2 bits of first byte for status + status = (ErrorCode) (response[0] & 0xc0) >> 6; + if (status == RESERVED || status == FAULT) { + return i2c::ERROR_OK; + } + + // extract top 6 bits of first byte and all bits of second byte for pressure + raw_pressure = (((response[0] & 0x3f)) << 8 | response[1]); + + // extract all bytes of 3rd byte and top 3 bits of fourth byte for temperature + raw_temperature = ((response[2] << 3) | (response[3] & 0xe0) >> 5); + + return i2c::ERROR_OK; +} + +inline float convert_temperature(uint16_t raw_temperature) { + const float temperature_bits_span = 2048; + const float temperature_max = 150; + const float temperature_min = -50; + const float temperature_span = temperature_max - temperature_min; + + float temperature = (raw_temperature * temperature_span / temperature_bits_span) + temperature_min; + + return temperature; +} + +void TEM3200Component::update() { + uint8_t status(NONE); + uint16_t raw_temperature(0); + uint16_t raw_pressure(0); + i2c::ErrorCode err = this->read_(status, raw_temperature, raw_pressure); + + if (err != i2c::ERROR_OK) { + ESP_LOGW(TAG, "I2C Communication Failed"); + this->status_set_warning(); + return; + } + + switch (status) { + case RESERVED: + ESP_LOGE(TAG, "Failed: Device return RESERVED status"); + this->status_set_warning(); + return; + case FAULT: + ESP_LOGE(TAG, "Failed: FAULT condition in the SSC or sensing element"); + this->mark_failed(); + return; + case STALE: + ESP_LOGE(TAG, "Warning: STALE data. Data has not been updated since last fetch"); + this->status_set_warning(); + return; + } + + float temperature = convert_temperature(raw_temperature); + + ESP_LOGD(TAG, "Got raw pressure=%d, temperature=%.1f°C", raw_pressure, temperature); + + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(temperature); + if (this->raw_pressure_sensor_ != nullptr) + this->raw_pressure_sensor_->publish_state(raw_pressure); + + this->status_clear_warning(); +} + +} // namespace tem3200 +} // namespace esphome diff --git a/esphome/components/tem3200/tem3200.h b/esphome/components/tem3200/tem3200.h new file mode 100644 index 0000000000..c84a2aba21 --- /dev/null +++ b/esphome/components/tem3200/tem3200.h @@ -0,0 +1,30 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace tem3200 { + +/// This class implements support for the tem3200 pressure and temperature i2c sensors. +class TEM3200Component : public PollingComponent, public i2c::I2CDevice { + public: + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } + void set_raw_pressure_sensor(sensor::Sensor *raw_pressure_sensor) { + this->raw_pressure_sensor_ = raw_pressure_sensor; + } + + float get_setup_priority() const override; + void setup() override; + void dump_config() override; + void update() override; + + protected: + i2c::ErrorCode read_(uint8_t &status, uint16_t &raw_temperature, uint16_t &raw_pressure); + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *raw_pressure_sensor_{nullptr}; +}; + +} // namespace tem3200 +} // namespace esphome diff --git a/tests/components/tem3200/common.yaml b/tests/components/tem3200/common.yaml new file mode 100644 index 0000000000..392c853bf4 --- /dev/null +++ b/tests/components/tem3200/common.yaml @@ -0,0 +1,16 @@ +i2c: + id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 200kHz + +sensor: + - platform: tem3200 + update_interval: 1s + i2c_id: i2c_bus + + temperature: + name: water temperature + + raw_pressure: + name: water pressure diff --git a/tests/components/tem3200/test.esp32-ard.yaml b/tests/components/tem3200/test.esp32-ard.yaml new file mode 100644 index 0000000000..3b761d3fc1 --- /dev/null +++ b/tests/components/tem3200/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +<<: !include common.yaml diff --git a/tests/components/tem3200/test.esp32-idf.yaml b/tests/components/tem3200/test.esp32-idf.yaml new file mode 100644 index 0000000000..3b761d3fc1 --- /dev/null +++ b/tests/components/tem3200/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +<<: !include common.yaml diff --git a/tests/components/tem3200/test.esp32-s3-ard.yaml b/tests/components/tem3200/test.esp32-s3-ard.yaml new file mode 100644 index 0000000000..4942e3c2b3 --- /dev/null +++ b/tests/components/tem3200/test.esp32-s3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO40 + sda_pin: GPIO41 + +<<: !include common.yaml diff --git a/tests/components/tem3200/test.esp32-s3-idf.yaml b/tests/components/tem3200/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..4942e3c2b3 --- /dev/null +++ b/tests/components/tem3200/test.esp32-s3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO40 + sda_pin: GPIO41 + +<<: !include common.yaml diff --git a/tests/components/tem3200/test.esp8266-ard.yaml b/tests/components/tem3200/test.esp8266-ard.yaml new file mode 100644 index 0000000000..3be5e53dcb --- /dev/null +++ b/tests/components/tem3200/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO05 + sda_pin: GPIO04 + +<<: !include common.yaml From b2bf2bc448a4e2b0bb92dc6d2d9358ab6c4ed775 Mon Sep 17 00:00:00 2001 From: Ken Baker Date: Sun, 6 Oct 2024 22:59:13 -0400 Subject: [PATCH 0445/1052] Add Initial NPI-19 pressure sensor support (#7181) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/npi19/__init__.py | 0 esphome/components/npi19/npi19.cpp | 111 ++++++++++++++++++ esphome/components/npi19/npi19.h | 30 +++++ esphome/components/npi19/sensor.py | 52 ++++++++ tests/components/npi19/common.yaml | 16 +++ tests/components/npi19/test.esp32-ard.yaml | 5 + tests/components/npi19/test.esp32-idf.yaml | 5 + tests/components/npi19/test.esp32-s3-ard.yaml | 5 + tests/components/npi19/test.esp32-s3-idf.yaml | 5 + tests/components/npi19/test.esp8266-ard.yaml | 5 + 11 files changed, 235 insertions(+) create mode 100644 esphome/components/npi19/__init__.py create mode 100644 esphome/components/npi19/npi19.cpp create mode 100644 esphome/components/npi19/npi19.h create mode 100644 esphome/components/npi19/sensor.py create mode 100644 tests/components/npi19/common.yaml create mode 100644 tests/components/npi19/test.esp32-ard.yaml create mode 100644 tests/components/npi19/test.esp32-idf.yaml create mode 100644 tests/components/npi19/test.esp32-s3-ard.yaml create mode 100644 tests/components/npi19/test.esp32-s3-idf.yaml create mode 100644 tests/components/npi19/test.esp8266-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index f202936972..c6ef528a8c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -291,6 +291,7 @@ esphome/components/nextion/switch/* @senexcrenshaw esphome/components/nextion/text_sensor/* @senexcrenshaw esphome/components/nfc/* @jesserockz @kbx81 esphome/components/noblex/* @AGalfra +esphome/components/npi19/* @bakerkj esphome/components/number/* @esphome/core esphome/components/one_wire/* @ssieb esphome/components/online_image/* @guillempages diff --git a/esphome/components/npi19/__init__.py b/esphome/components/npi19/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/npi19/npi19.cpp b/esphome/components/npi19/npi19.cpp new file mode 100644 index 0000000000..ca1fc39943 --- /dev/null +++ b/esphome/components/npi19/npi19.cpp @@ -0,0 +1,111 @@ +#include "npi19.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace npi19 { + +static const char *const TAG = "npi19"; + +static const uint8_t READ_COMMAND = 0xAC; + +void NPI19Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up NPI19..."); + + 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..."); + this->mark_failed(); + return; + } + + ESP_LOGCONFIG(TAG, " Success..."); +} + +void NPI19Component::dump_config() { + ESP_LOGCONFIG(TAG, "NPI19:"); + LOG_I2C_DEVICE(this); + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Raw Pressure", this->raw_pressure_sensor_); + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); +} + +float NPI19Component::get_setup_priority() const { return setup_priority::DATA; } + +i2c::ErrorCode NPI19Component::read_(uint16_t &raw_temperature, uint16_t &raw_pressure) { + // initiate data read from device + i2c::ErrorCode w_err = write(&READ_COMMAND, sizeof(READ_COMMAND), true); + if (w_err != i2c::ERROR_OK) { + return w_err; + } + + // read 4 bytes from senesor + uint8_t response[4] = {0x00, 0x00, 0x00, 0x00}; + i2c::ErrorCode r_err = this->read(response, 4); + + if (r_err != i2c::ERROR_OK) { + return r_err; + } + + // extract top 6 bits of first byte and all bits of second byte for pressure + raw_pressure = ((response[0] & 0x3F) << 8) | response[1]; + + // extract all bytes of 3rd byte and top 3 bits of fourth byte for temperature + raw_temperature = (response[2] << 3) | ((response[3] & 0xE0) >> 5); + + return i2c::ERROR_OK; +} + +inline float convert_temperature(uint16_t raw_temperature) { + /* + * Correspondance with Amphenol confirmed the appropriate equation for computing temperature is: + * T (°C) =(((((Th*8)+Tl)/2048)*200)-50), where Th is the high (third) byte and Tl is the low (fourth) byte. + * + * Tl is actually the upper 3 bits of the fourth data byte; the first 5 (LSBs) must be masked out. + * + * + * The NPI-19 I2C has a temperature output, however the manufacturer does + * not specify its accuracy on the published datasheet. They indicate + * that the sensor should not be used as a calibrated temperature + * reading; it’s only intended for curve fitting data during + * compensation. + */ + const float temperature_bits_span = 2048; + const float temperature_max = 150; + const float temperature_min = -50; + const float temperature_span = temperature_max - temperature_min; + + float temperature = (raw_temperature * temperature_span / temperature_bits_span) + temperature_min; + + return temperature; +} + +void NPI19Component::update() { + 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_LOGW(TAG, "I2C Communication Failed"); + this->status_set_warning(); + return; + } + + float temperature = convert_temperature(raw_temperature); + + ESP_LOGD(TAG, "Got raw pressure=%d, temperature=%.1f°C", raw_pressure, temperature); + + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(temperature); + if (this->raw_pressure_sensor_ != nullptr) + this->raw_pressure_sensor_->publish_state(raw_pressure); + + this->status_clear_warning(); +} + +} // namespace npi19 +} // namespace esphome diff --git a/esphome/components/npi19/npi19.h b/esphome/components/npi19/npi19.h new file mode 100644 index 0000000000..df289dffc1 --- /dev/null +++ b/esphome/components/npi19/npi19.h @@ -0,0 +1,30 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace npi19 { + +/// This class implements support for the npi19 pressure and temperature i2c sensors. +class NPI19Component : public PollingComponent, public i2c::I2CDevice { + public: + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } + void set_raw_pressure_sensor(sensor::Sensor *raw_pressure_sensor) { + this->raw_pressure_sensor_ = raw_pressure_sensor; + } + + float get_setup_priority() const override; + void setup() override; + void dump_config() override; + void update() override; + + protected: + i2c::ErrorCode read_(uint16_t &raw_temperature, uint16_t &raw_pressure); + sensor::Sensor *temperature_sensor_{nullptr}; + sensor::Sensor *raw_pressure_sensor_{nullptr}; +}; + +} // namespace npi19 +} // namespace esphome diff --git a/esphome/components/npi19/sensor.py b/esphome/components/npi19/sensor.py new file mode 100644 index 0000000000..d13e72f5f8 --- /dev/null +++ b/esphome/components/npi19/sensor.py @@ -0,0 +1,52 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, + CONF_TEMPERATURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, +) + +CODEOWNERS = ["@bakerkj"] +DEPENDENCIES = ["i2c"] + +npi19_ns = cg.esphome_ns.namespace("npi19") + +NPI19Component = npi19_ns.class_("NPI19Component", cg.PollingComponent, i2c.I2CDevice) + +CONF_RAW_PRESSURE = "raw_pressure" + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(NPI19Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_RAW_PRESSURE): sensor.sensor_schema( + accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x28)) +) + + +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 temperature_config := config.get(CONF_TEMPERATURE): + sens = await sensor.new_sensor(temperature_config) + cg.add(var.set_temperature_sensor(sens)) + + if raw_pressure_config := config.get(CONF_RAW_PRESSURE): + sens = await sensor.new_sensor(raw_pressure_config) + cg.add(var.set_raw_pressure_sensor(sens)) diff --git a/tests/components/npi19/common.yaml b/tests/components/npi19/common.yaml new file mode 100644 index 0000000000..012e05695e --- /dev/null +++ b/tests/components/npi19/common.yaml @@ -0,0 +1,16 @@ +i2c: + id: i2c_bus + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 200kHz + +sensor: + - platform: npi19 + update_interval: 1s + i2c_id: i2c_bus + + temperature: + name: water temperature + + raw_pressure: + name: water pressure diff --git a/tests/components/npi19/test.esp32-ard.yaml b/tests/components/npi19/test.esp32-ard.yaml new file mode 100644 index 0000000000..3b761d3fc1 --- /dev/null +++ b/tests/components/npi19/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +<<: !include common.yaml diff --git a/tests/components/npi19/test.esp32-idf.yaml b/tests/components/npi19/test.esp32-idf.yaml new file mode 100644 index 0000000000..3b761d3fc1 --- /dev/null +++ b/tests/components/npi19/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 + +<<: !include common.yaml diff --git a/tests/components/npi19/test.esp32-s3-ard.yaml b/tests/components/npi19/test.esp32-s3-ard.yaml new file mode 100644 index 0000000000..4942e3c2b3 --- /dev/null +++ b/tests/components/npi19/test.esp32-s3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO40 + sda_pin: GPIO41 + +<<: !include common.yaml diff --git a/tests/components/npi19/test.esp32-s3-idf.yaml b/tests/components/npi19/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..4942e3c2b3 --- /dev/null +++ b/tests/components/npi19/test.esp32-s3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO40 + sda_pin: GPIO41 + +<<: !include common.yaml diff --git a/tests/components/npi19/test.esp8266-ard.yaml b/tests/components/npi19/test.esp8266-ard.yaml new file mode 100644 index 0000000000..3be5e53dcb --- /dev/null +++ b/tests/components/npi19/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO05 + sda_pin: GPIO04 + +<<: !include common.yaml From ea23f49e90fb8848657f2ba1408fb30ce386b829 Mon Sep 17 00:00:00 2001 From: Curtis Malainey Date: Sun, 6 Oct 2024 20:08:56 -0700 Subject: [PATCH 0446/1052] nau7802: new component (#6291) --- CODEOWNERS | 1 + esphome/components/nau7802/__init__.py | 0 esphome/components/nau7802/nau7802.cpp | 323 ++++++++++++++++++ esphome/components/nau7802/nau7802.h | 121 +++++++ esphome/components/nau7802/sensor.py | 134 ++++++++ tests/components/nau7802/common.yaml | 13 + tests/components/nau7802/test.esp32-ard.yaml | 6 + .../components/nau7802/test.esp32-c3-ard.yaml | 6 + .../components/nau7802/test.esp32-c3-idf.yaml | 6 + tests/components/nau7802/test.esp32-idf.yaml | 6 + .../components/nau7802/test.esp8266-ard.yaml | 6 + tests/components/nau7802/test.rp2040-ard.yaml | 6 + 12 files changed, 628 insertions(+) create mode 100644 esphome/components/nau7802/__init__.py create mode 100644 esphome/components/nau7802/nau7802.cpp create mode 100644 esphome/components/nau7802/nau7802.h create mode 100644 esphome/components/nau7802/sensor.py create mode 100644 tests/components/nau7802/common.yaml create mode 100644 tests/components/nau7802/test.esp32-ard.yaml create mode 100644 tests/components/nau7802/test.esp32-c3-ard.yaml create mode 100644 tests/components/nau7802/test.esp32-c3-idf.yaml create mode 100644 tests/components/nau7802/test.esp32-idf.yaml create mode 100644 tests/components/nau7802/test.esp8266-ard.yaml create mode 100644 tests/components/nau7802/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index c6ef528a8c..ed9c13a975 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -283,6 +283,7 @@ esphome/components/mopeka_std_check/* @Fabian-Schmidt esphome/components/mpl3115a2/* @kbickar esphome/components/mpu6886/* @fabaff esphome/components/ms8607/* @e28eta +esphome/components/nau7802/* @cujomalainey esphome/components/network/* @esphome/core esphome/components/nextion/* @edwardtfn @senexcrenshaw esphome/components/nextion/binary_sensor/* @senexcrenshaw diff --git a/esphome/components/nau7802/__init__.py b/esphome/components/nau7802/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/nau7802/nau7802.cpp b/esphome/components/nau7802/nau7802.cpp new file mode 100644 index 0000000000..ea6c0258af --- /dev/null +++ b/esphome/components/nau7802/nau7802.cpp @@ -0,0 +1,323 @@ +#include "nau7802.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace nau7802 { + +static const char *const TAG = "nau7802"; + +// Only define what we need + +static const uint8_t READ_BIT = 0x01; + +static const uint8_t PU_CTRL_REG = 0x00; +static const uint8_t PU_CTRL_REGISTER_RESET = 0x01; +static const uint8_t PU_CTRL_POWERUP_DIGITAL = 0x02; +static const uint8_t PU_CTRL_POWERUP_ANALOG = 0x04; +static const uint8_t PU_CTRL_POWERUP_READY = 0x08; +static const uint8_t PU_CTRL_CYCLE_START = 0x10; +static const uint8_t PU_CTRL_CYCLE_READY = 0x20; +static const uint8_t PU_CTRL_AVDD_EXTERNAL = 0x80; + +static const uint8_t CTRL1_REG = 0x01; +static const uint8_t CTRL1_LDO_SHIFT = 3; +static const uint8_t CTRL1_LDO_MASK = (0x7 << CTRL1_LDO_SHIFT); +static const uint8_t CTRL1_GAIN_MASK = 0x7; + +static const uint8_t CTRL2_REG = 0x02; +static const uint8_t CTRL2_CRS_SHIFT = 4; +static const uint8_t CTRL2_CRS_MASK = (0x7 << CTRL2_CRS_SHIFT); +static const uint8_t CTRL2_CALS = 0x04; +static const uint8_t CTRL2_CAL_ERR = 0x08; +static const uint8_t CTRL2_GAIN_CALIBRATION = 0x03; +static const uint8_t CTRL2_CONFIG_MASK = 0xF0; + +static const uint8_t OCAL1_B2_REG = 0x03; +static const uint8_t GCAL1_B3_REG = 0x06; +static const uint8_t GCAL1_FRACTIONAL = 23; + +// only need the first data register for sequential read method +static const uint8_t ADCO_B2_REG = 0x12; + +static const uint8_t ADC_REG = 0x15; +static const uint8_t ADC_CHPS_DISABLE = 0x30; + +static const uint8_t PGA_REG = 0x1B; +static const uint8_t PGA_LDOMODE_ESR = 0x40; + +static const uint8_t POWER_REG = 0x1C; +static const uint8_t POWER_PGA_CAP_EN = 0x80; + +static const uint8_t DEVICE_REV = 0x1F; + +void NAU7802Sensor::setup() { + 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)) { + ESP_LOGE(TAG, "Failed I2C read during setup()"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "Setting up NAU7802 Rev %d", rev); + + // reset + pu_ctrl |= PU_CTRL_REGISTER_RESET; + delay(10); + pu_ctrl &= ~PU_CTRL_REGISTER_RESET; + + // power up digital hw + pu_ctrl |= PU_CTRL_POWERUP_DIGITAL; + + delay(1); + if (!(pu_ctrl.get() & PU_CTRL_POWERUP_READY)) { + ESP_LOGE(TAG, "Failed to reset sensor during setup()"); + this->mark_failed(); + return; + } + + uint32_t gcal = (uint32_t) (round(this->gain_calibration_ * (1 << GCAL1_FRACTIONAL))); + this->write_value_(OCAL1_B2_REG, 3, this->offset_calibration_); + this->write_value_(GCAL1_B3_REG, 4, gcal); + + // turn on AFE + pu_ctrl |= PU_CTRL_POWERUP_ANALOG; + auto f = std::bind(&NAU7802Sensor::complete_setup_, this); + this->set_timeout(600, f); +} + +void NAU7802Sensor::complete_setup_() { + i2c::I2CRegister pu_ctrl = this->reg(PU_CTRL_REG); + i2c::I2CRegister ctrl1 = this->reg(CTRL1_REG); + i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG); + pu_ctrl |= PU_CTRL_CYCLE_START; + + // set gain + ctrl1 &= ~CTRL1_GAIN_MASK; + ctrl1 |= this->gain_; + + // enable internal LDO + if (this->ldo_ != NAU7802_LDO_EXTERNAL) { + pu_ctrl |= PU_CTRL_AVDD_EXTERNAL; + ctrl1 &= ~CTRL1_LDO_MASK; + ctrl1 |= this->ldo_ << CTRL1_LDO_SHIFT; + } + + // set sps + ctrl2 &= ~CTRL2_CRS_MASK; + ctrl2 |= this->sps_ << CTRL2_CRS_SHIFT; + + // disable ADC chopper clock + i2c::I2CRegister adc_reg = this->reg(ADC_REG); + adc_reg |= ADC_CHPS_DISABLE; + + // use low ESR caps + i2c::I2CRegister pga_reg = this->reg(PGA_REG); + pga_reg &= ~PGA_LDOMODE_ESR; + + // PGA stabilizer cap on output + i2c::I2CRegister pwr_reg = this->reg(POWER_REG); + pwr_reg |= POWER_PGA_CAP_EN; + + this->setup_complete_ = true; +} + +void NAU7802Sensor::dump_config() { + LOG_SENSOR("", "NAU7802", this); + LOG_I2C_DEVICE(this); + + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with NAU7802 failed earlier, during setup"); + 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_); + + std::string voltage = "unknown"; + switch (this->ldo_) { + case NAU7802_LDO_2V4: + voltage = "2.4V"; + break; + case NAU7802_LDO_2V7: + voltage = "2.7V"; + break; + case NAU7802_LDO_3V0: + voltage = "3.0V"; + break; + case NAU7802_LDO_3V3: + voltage = "3.3V"; + break; + case NAU7802_LDO_3V6: + voltage = "3.6V"; + break; + case NAU7802_LDO_3V9: + voltage = "3.9V"; + break; + case NAU7802_LDO_4V2: + voltage = "4.2V"; + break; + case NAU7802_LDO_4V5: + voltage = "4.5V"; + break; + case NAU7802_LDO_EXTERNAL: + voltage = "External"; + break; + } + ESP_LOGCONFIG(TAG, " LDO Voltage: %s", voltage.c_str()); + int gain = 0; + switch (this->gain_) { + case NAU7802_GAIN_128: + gain = 128; + break; + case NAU7802_GAIN_64: + gain = 64; + break; + case NAU7802_GAIN_32: + gain = 32; + break; + case NAU7802_GAIN_16: + gain = 16; + break; + case NAU7802_GAIN_8: + gain = 8; + break; + case NAU7802_GAIN_4: + gain = 4; + break; + case NAU7802_GAIN_2: + gain = 2; + break; + case NAU7802_GAIN_1: + gain = 1; + break; + } + ESP_LOGCONFIG(TAG, " Gain: %dx", gain); + int sps = 0; + switch (this->sps_) { + case NAU7802_SPS_320: + sps = 320; + break; + case NAU7802_SPS_80: + sps = 80; + break; + case NAU7802_SPS_40: + sps = 40; + break; + case NAU7802_SPS_20: + sps = 20; + break; + case NAU7802_SPS_10: + sps = 10; + break; + } + ESP_LOGCONFIG(TAG, " Samples Per Second: %d", sps); + LOG_UPDATE_INTERVAL(this); +} + +void NAU7802Sensor::write_value_(uint8_t start_reg, size_t size, int32_t value) { + uint8_t data[4]; + for (int i = 0; i < size; i++) { + data[i] = 0xFF & (value >> (size - 1 - i) * 8); + } + this->write_register(start_reg, data, size); +} + +int32_t NAU7802Sensor::read_value_(uint8_t start_reg, size_t size) { + uint8_t data[4]; + this->read_register(start_reg, data, size); + int32_t result = 0; + for (int i = 0; i < size; i++) { + result |= data[i] << (size - 1 - i) * 8; + } + // extend sign bit + if (result & 0x800000 && size == 3) { + result |= 0xFF000000; + } + return result; +} + +bool NAU7802Sensor::calibrate_(enum NAU7802CalibrationModes mode) { + // check if already calbrating + if (this->state_ != CalibrationState::INACTIVE) { + ESP_LOGW(TAG, "Calibration already in progress"); + return false; + } + + this->state_ = mode == NAU7802_CALIBRATE_GAIN ? CalibrationState::GAIN : CalibrationState::OFFSET; + + i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG); + // clear calibraye registers + ctrl2 &= CTRL2_CONFIG_MASK; + // Calibrate + ctrl2 |= mode; + ctrl2 |= CTRL2_CALS; + return true; +} + +void NAU7802Sensor::set_calibration_failure_(bool failed) { + switch (this->state_) { + case CalibrationState::GAIN: + this->gain_calibration_failed_ = failed; + break; + case CalibrationState::OFFSET: + this->offset_calibration_failed_ = failed; + break; + case CalibrationState::INACTIVE: + // shouldn't happen + break; + } +} + +void NAU7802Sensor::loop() { + i2c::I2CRegister ctrl2 = this->reg(CTRL2_REG); + + if (this->state_ != CalibrationState::INACTIVE && !(ctrl2.get() & CTRL2_CALS)) { + if (ctrl2.get() & CTRL2_CAL_ERR) { + this->set_calibration_failure_(true); + this->state_ = CalibrationState::INACTIVE; + ESP_LOGE(TAG, "Failed to calibrate sensor"); + this->status_set_error("Calibration Failed"); + return; + } + + this->set_calibration_failure_(false); + this->state_ = CalibrationState::INACTIVE; + + if (!this->offset_calibration_failed_ && !this->gain_calibration_failed_) + this->status_clear_error(); + + int32_t ocal = this->read_value_(OCAL1_B2_REG, 3); + ESP_LOGI(TAG, "New Offset: %s", to_string(ocal).c_str()); + uint32_t gcal = this->read_value_(GCAL1_B3_REG, 4); + float gcal_f = ((float) gcal / (float) (1 << GCAL1_FRACTIONAL)); + ESP_LOGI(TAG, "New Gain: %f", gcal_f); + } +} + +float NAU7802Sensor::get_setup_priority() const { return setup_priority::DATA; } + +void NAU7802Sensor::update() { + if (!this->is_data_ready_()) { + ESP_LOGW(TAG, "No measurements ready!"); + this->status_set_warning(); + return; + } + + this->status_clear_warning(); + + // Get the most recent sample to publish + int32_t result = this->read_value_(ADCO_B2_REG, 3); + + ESP_LOGD(TAG, "'%s': Got value %" PRId32, this->name_.c_str(), result); + this->publish_state(result); +} + +bool NAU7802Sensor::is_data_ready_() { return this->reg(PU_CTRL_REG).get() & PU_CTRL_CYCLE_READY; } + +bool NAU7802Sensor::can_proceed() { return this->setup_complete_; } + +} // namespace nau7802 +} // namespace esphome diff --git a/esphome/components/nau7802/nau7802.h b/esphome/components/nau7802/nau7802.h new file mode 100644 index 0000000000..3b0372aa89 --- /dev/null +++ b/esphome/components/nau7802/nau7802.h @@ -0,0 +1,121 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +#include + +namespace esphome { +namespace nau7802 { + +enum NAU7802Gain { + NAU7802_GAIN_128 = 0b111, + NAU7802_GAIN_64 = 0b110, + NAU7802_GAIN_32 = 0b101, + NAU7802_GAIN_16 = 0b100, + NAU7802_GAIN_8 = 0b011, + NAU7802_GAIN_4 = 0b010, + NAU7802_GAIN_2 = 0b001, + NAU7802_GAIN_1 = 0b000, +}; + +enum NAU7802SPS { + NAU7802_SPS_320 = 0b111, + NAU7802_SPS_80 = 0b011, + NAU7802_SPS_40 = 0b010, + NAU7802_SPS_20 = 0b001, + NAU7802_SPS_10 = 0b000, +}; + +enum NAU7802LDO { + NAU7802_LDO_2V4 = 0b111, + NAU7802_LDO_2V7 = 0b110, + NAU7802_LDO_3V0 = 0b101, + NAU7802_LDO_3V3 = 0b100, + NAU7802_LDO_3V6 = 0b011, + NAU7802_LDO_3V9 = 0b010, + NAU7802_LDO_4V2 = 0b001, + NAU7802_LDO_4V5 = 0b000, + // Never write this to a register + NAU7802_LDO_EXTERNAL = 0b1000, +}; + +enum NAU7802CalibrationModes { + NAU7802_CALIBRATE_EXTERNAL_OFFSET = 0b10, + NAU7802_CALIBRATE_INTERNAL_OFFSET = 0b00, + NAU7802_CALIBRATE_GAIN = 0b11, +}; + +class NAU7802Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice { + public: + void set_samples_per_second(NAU7802SPS sps) { this->sps_ = sps; } + void set_ldo_voltage(NAU7802LDO ldo) { this->ldo_ = ldo; } + void set_gain(NAU7802Gain gain) { this->gain_ = gain; } + void set_gain_calibration(float gain_calibration) { this->gain_calibration_ = gain_calibration; } + void set_offset_calibration(int32_t offset_calibration) { this->offset_calibration_ = offset_calibration; } + bool calibrate_external_offset() { return this->calibrate_(NAU7802_CALIBRATE_EXTERNAL_OFFSET); } + bool calibrate_internal_offset() { return this->calibrate_(NAU7802_CALIBRATE_INTERNAL_OFFSET); } + bool calibrate_gain() { return this->calibrate_(NAU7802_CALIBRATE_GAIN); } + + void setup() override; + void loop() override; + bool can_proceed() override; + void dump_config() override; + float get_setup_priority() const override; + void update() override; + + protected: + // + // Internal state + // + enum class CalibrationState : uint8_t { + INACTIVE, + OFFSET, + GAIN, + } state_{CalibrationState::INACTIVE}; + + float gain_calibration_; + int32_t offset_calibration_; + bool offset_calibration_failed_ = false; + bool gain_calibration_failed_ = false; + bool setup_complete_ = false; + + // + // Config values + // + NAU7802LDO ldo_; + NAU7802SPS sps_; + NAU7802Gain gain_; + + // + // Internal Methods + // + bool calibrate_(enum NAU7802CalibrationModes mode); + void complete_setup_(); + void write_value_(uint8_t start_reg, size_t size, int32_t value); + int32_t read_value_(uint8_t start_reg, size_t size); + bool is_data_ready_(); + void set_calibration_failure_(bool failed); +}; + +template +class NAU7802CalbrateExternalOffsetAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->calibrate_external_offset(); } +}; + +template +class NAU7802CalbrateInternalOffsetAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->calibrate_internal_offset(); } +}; + +template class NAU7802CalbrateGainAction : public Action, public Parented { + public: + void play(Ts... x) override { this->parent_->calibrate_gain(); } +}; + +} // namespace nau7802 +} // namespace esphome diff --git a/esphome/components/nau7802/sensor.py b/esphome/components/nau7802/sensor.py new file mode 100644 index 0000000000..9192f48f53 --- /dev/null +++ b/esphome/components/nau7802/sensor.py @@ -0,0 +1,134 @@ +from esphome import automation +from esphome.automation import maybe_simple_id +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import CONF_GAIN, CONF_ID, ICON_SCALE, STATE_CLASS_MEASUREMENT + +CODEOWNERS = ["@cujomalainey"] +DEPENDENCIES = ["i2c"] + +CONF_GAIN_CALIBRATION = "gain_calibration" +CONF_OFFSET_CALIBRATION = "offset_calibration" +CONF_LDO_VOLTAGE = "ldo_voltage" +CONF_SAMPLES_PER_SECOND = "samples_per_second" + +nau7802_ns = cg.esphome_ns.namespace("nau7802") +NAU7802Sensor = nau7802_ns.class_( + "NAU7802Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice +) +NAU7802CalbrateExternalOffsetAction = nau7802_ns.class_( + "NAU7802CalbrateExternalOffsetAction", + automation.Action, + cg.Parented.template(NAU7802Sensor), +) +NAU7802CalbrateInternalOffsetAction = nau7802_ns.class_( + "NAU7802CalbrateInternalOffsetAction", + automation.Action, + cg.Parented.template(NAU7802Sensor), +) +NAU7802CalbrateGainAction = nau7802_ns.class_( + "NAU7802CalbrateGainAction", automation.Action, cg.Parented.template(NAU7802Sensor) +) + +NAU7802Gain = nau7802_ns.enum("NAU7802Gain") +GAINS = { + 128: NAU7802Gain.NAU7802_GAIN_128, + 64: NAU7802Gain.NAU7802_GAIN_64, + 32: NAU7802Gain.NAU7802_GAIN_32, + 16: NAU7802Gain.NAU7802_GAIN_16, + 8: NAU7802Gain.NAU7802_GAIN_8, + 4: NAU7802Gain.NAU7802_GAIN_4, + 2: NAU7802Gain.NAU7802_GAIN_2, + 1: NAU7802Gain.NAU7802_GAIN_1, +} + +NAU7802SPS = nau7802_ns.enum("NAU7802SPS") +SAMPLES_PER_SECOND = { + 320: NAU7802SPS.NAU7802_SPS_320, + 80: NAU7802SPS.NAU7802_SPS_80, + 40: NAU7802SPS.NAU7802_SPS_40, + 20: NAU7802SPS.NAU7802_SPS_20, + 10: NAU7802SPS.NAU7802_SPS_10, +} + +NAU7802LDO = nau7802_ns.enum("NAU7802LDO") +LDO = { + "2.4V": NAU7802LDO.NAU7802_LDO_2V4, + "2.7V": NAU7802LDO.NAU7802_LDO_2V7, + "3.0V": NAU7802LDO.NAU7802_LDO_3V0, + "3.3V": NAU7802LDO.NAU7802_LDO_3V3, + "3.6V": NAU7802LDO.NAU7802_LDO_3V6, + "3.9V": NAU7802LDO.NAU7802_LDO_3V9, + "4.2V": NAU7802LDO.NAU7802_LDO_4V2, + "4.5V": NAU7802LDO.NAU7802_LDO_4V5, + "EXTERNAL": NAU7802LDO.NAU7802_LDO_EXTERNAL, + "EXT": NAU7802LDO.NAU7802_LDO_EXTERNAL, +} + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + NAU7802Sensor, + icon=ICON_SCALE, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ) + .extend( + { + cv.Optional(CONF_LDO_VOLTAGE, default="3.0V"): cv.enum(LDO, upper=True), + cv.Optional(CONF_SAMPLES_PER_SECOND, default=10): cv.enum( + SAMPLES_PER_SECOND, int=True + ), + cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True), + cv.Optional(CONF_OFFSET_CALIBRATION, default=0): cv.int_range( + min=-8388608, max=8388607 + ), + cv.Optional(CONF_GAIN_CALIBRATION, default=1.0): cv.float_range( + min=0, max=511.9999998807907 + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x2A)) +) + + +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) + await sensor.register_sensor(var, config) + + cg.add(var.set_samples_per_second(config[CONF_SAMPLES_PER_SECOND])) + cg.add(var.set_ldo_voltage(config[CONF_LDO_VOLTAGE])) + cg.add(var.set_gain(config[CONF_GAIN])) + cg.add(var.set_gain_calibration(config[CONF_GAIN_CALIBRATION])) + cg.add(var.set_offset_calibration(config[CONF_OFFSET_CALIBRATION])) + + +NAU7802_CALIBRATE_SCHEMA = maybe_simple_id( + { + cv.GenerateID(CONF_ID): cv.use_id(NAU7802Sensor), + } +) + + +@automation.register_action( + "nau7802.calibrate_internal_offset", + NAU7802CalbrateInternalOffsetAction, + NAU7802_CALIBRATE_SCHEMA, +) +@automation.register_action( + "nau7802.calibrate_external_offset", + NAU7802CalbrateExternalOffsetAction, + NAU7802_CALIBRATE_SCHEMA, +) +@automation.register_action( + "nau7802.calibrate_gain", + NAU7802CalbrateGainAction, + NAU7802_CALIBRATE_SCHEMA, +) +async def nau7802_calibrate_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + return var diff --git a/tests/components/nau7802/common.yaml b/tests/components/nau7802/common.yaml new file mode 100644 index 0000000000..2501911a3f --- /dev/null +++ b/tests/components/nau7802/common.yaml @@ -0,0 +1,13 @@ +sensor: + - platform: nau7802 + id: test_id + name: weight + i2c_id: i2c_nau7802 + gain: 32 + ldo_voltage: "3.0v" + samples_per_second: 10 + on_value: + then: + - nau7802.calibrate_external_offset: test_id + - nau7802.calibrate_internal_offset: test_id + - nau7802.calibrate_gain: test_id diff --git a/tests/components/nau7802/test.esp32-ard.yaml b/tests/components/nau7802/test.esp32-ard.yaml new file mode 100644 index 0000000000..73a4aa4251 --- /dev/null +++ b/tests/components/nau7802/test.esp32-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_nau7802 + scl: 16 + sda: 17 + +<<: !include common.yaml diff --git a/tests/components/nau7802/test.esp32-c3-ard.yaml b/tests/components/nau7802/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..769468f9ec --- /dev/null +++ b/tests/components/nau7802/test.esp32-c3-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_nau7802 + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/nau7802/test.esp32-c3-idf.yaml b/tests/components/nau7802/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..769468f9ec --- /dev/null +++ b/tests/components/nau7802/test.esp32-c3-idf.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_nau7802 + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/nau7802/test.esp32-idf.yaml b/tests/components/nau7802/test.esp32-idf.yaml new file mode 100644 index 0000000000..73a4aa4251 --- /dev/null +++ b/tests/components/nau7802/test.esp32-idf.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_nau7802 + scl: 16 + sda: 17 + +<<: !include common.yaml diff --git a/tests/components/nau7802/test.esp8266-ard.yaml b/tests/components/nau7802/test.esp8266-ard.yaml new file mode 100644 index 0000000000..769468f9ec --- /dev/null +++ b/tests/components/nau7802/test.esp8266-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_nau7802 + scl: 5 + sda: 4 + +<<: !include common.yaml diff --git a/tests/components/nau7802/test.rp2040-ard.yaml b/tests/components/nau7802/test.rp2040-ard.yaml new file mode 100644 index 0000000000..769468f9ec --- /dev/null +++ b/tests/components/nau7802/test.rp2040-ard.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_nau7802 + scl: 5 + sda: 4 + +<<: !include common.yaml From 1d91601094c8696646f07f277c212cf71a91abc4 Mon Sep 17 00:00:00 2001 From: esphomebot Date: Mon, 7 Oct 2024 16:22:17 +1300 Subject: [PATCH 0447/1052] Update webserver local assets to 20241007-025551 (#7553) --- .../components/web_server/server_index_v3.h | 7968 +++++++++-------- 1 file changed, 3989 insertions(+), 3979 deletions(-) diff --git a/esphome/components/web_server/server_index_v3.h b/esphome/components/web_server/server_index_v3.h index 0f3f743a73..b5d7189137 100644 --- a/esphome/components/web_server/server_index_v3.h +++ b/esphome/components/web_server/server_index_v3.h @@ -12,7 +12,7 @@ namespace web_server { const uint8_t INDEX_GZ[] PROGMEM = { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xcc, 0xbd, 0xeb, 0x7a, 0x1b, 0xb7, 0xb2, 0x20, 0xfa, 0xfb, 0xcc, 0x53, 0x48, 0xbd, 0x1d, 0xa5, 0x21, 0x82, 0x2d, 0x92, 0xba, 0x58, 0x6e, 0x0a, 0xe2, 0xf8, 0x1a, 0x3b, - 0x71, 0x6c, 0xc7, 0x72, 0xec, 0xd8, 0x0c, 0xb7, 0x02, 0x36, 0x41, 0x12, 0x71, 0x13, 0x60, 0x1a, 0xa0, 0x25, 0x99, + 0x71, 0x6c, 0xc7, 0x72, 0xec, 0x38, 0x0c, 0xb7, 0x0c, 0x36, 0x41, 0x12, 0x71, 0x13, 0x60, 0x1a, 0xa0, 0x25, 0x99, 0xe4, 0xbb, 0x9f, 0xaf, 0x70, 0xe9, 0x46, 0x93, 0xb4, 0xd7, 0x5a, 0x73, 0x66, 0xce, 0x37, 0x3b, 0x7b, 0x59, 0x6c, 0xdc, 0x51, 0x28, 0x14, 0xaa, 0x0a, 0x55, 0x85, 0x8b, 0xfd, 0x91, 0xcc, 0xf4, 0xdd, 0x9c, 0xed, 0x4d, 0xf5, 0x2c, 0xbf, 0xbc, 0x70, 0xff, 0x32, 0x3a, 0xba, 0xbc, 0xc8, 0xb9, 0xf8, 0xbc, 0x57, 0xb0, 0x9c, 0xf0, 0x4c, 0x8a, 0xbd, @@ -34,3984 +34,3994 @@ const uint8_t INDEX_GZ[] PROGMEM = { 0x7b, 0xcf, 0xe4, 0xd2, 0x4c, 0xa3, 0x58, 0x64, 0x5a, 0x16, 0xb1, 0xc6, 0x0c, 0x0b, 0xb4, 0xe4, 0xe3, 0x58, 0x4f, 0xb9, 0x4a, 0xae, 0xef, 0x65, 0x4a, 0xbd, 0x65, 0x6a, 0x91, 0xeb, 0x7b, 0x64, 0xbf, 0x85, 0xc5, 0x3e, 0x21, 0x9f, 0x05, 0xd2, 0xd3, 0x42, 0xde, 0xec, 0x3d, 0x2d, 0x0a, 0x59, 0xc4, 0xd1, 0xe3, 0xab, 0x2b, 0x5b, 0x62, 0x8f, 0xab, - 0x3d, 0x21, 0xf5, 0x5e, 0xd9, 0x1e, 0x40, 0x3b, 0xd9, 0xfb, 0x5d, 0xb1, 0xbd, 0xbf, 0x16, 0x42, 0xd1, 0x31, 0x7b, - 0x7c, 0x75, 0xf5, 0xd7, 0x9e, 0x2c, 0xf6, 0xfe, 0xca, 0x94, 0xfa, 0x6b, 0x8f, 0x0b, 0xa5, 0x19, 0x1d, 0x25, 0x11, - 0xea, 0x9a, 0xce, 0x32, 0xa5, 0xde, 0xb1, 0x5b, 0x4d, 0x34, 0x36, 0x9f, 0x9a, 0xb0, 0xf5, 0x84, 0xe9, 0x3d, 0x55, - 0xce, 0x2b, 0x46, 0xcb, 0x9c, 0xe9, 0x3d, 0x4d, 0x4c, 0xbe, 0x74, 0xf0, 0x67, 0xf6, 0x53, 0x77, 0xf9, 0x38, 0xbe, - 0x11, 0x07, 0x07, 0xba, 0x04, 0x34, 0x5a, 0xba, 0x15, 0x22, 0x6c, 0xdf, 0xa7, 0x1d, 0x1c, 0xb0, 0x24, 0x67, 0x62, - 0xa2, 0xa7, 0x84, 0x90, 0x76, 0x57, 0x1c, 0x1c, 0xc4, 0x9a, 0x7c, 0x10, 0xc9, 0x84, 0xe9, 0x98, 0x21, 0x84, 0xab, - 0xda, 0x07, 0x07, 0xb1, 0x05, 0x82, 0x24, 0xda, 0x00, 0xae, 0x06, 0x63, 0x94, 0x38, 0xe8, 0x5f, 0xdd, 0x89, 0x2c, - 0x0e, 0xc7, 0x8f, 0xb0, 0x38, 0x38, 0xf8, 0x20, 0x12, 0x05, 0x2d, 0x62, 0x8d, 0xd0, 0xba, 0x60, 0x7a, 0x51, 0x88, - 0x3d, 0xbd, 0xd6, 0xf2, 0x4a, 0x17, 0x5c, 0x4c, 0x62, 0xb4, 0xf4, 0x69, 0x41, 0xc5, 0xf5, 0xda, 0x0e, 0xf7, 0xb7, - 0x82, 0x70, 0x72, 0x09, 0x3d, 0x3e, 0x93, 0xb1, 0xc3, 0x41, 0x4e, 0x48, 0xa4, 0x4c, 0xdd, 0xa8, 0xc7, 0x53, 0xde, - 0x88, 0x22, 0x6c, 0x47, 0x89, 0x3f, 0x0b, 0x84, 0xb9, 0x06, 0xd4, 0x4d, 0x92, 0x44, 0x23, 0x72, 0xb9, 0xf4, 0x60, - 0xe1, 0xc1, 0x44, 0x7b, 0xbc, 0xdf, 0x1a, 0xa4, 0x3a, 0x29, 0xd8, 0x68, 0x91, 0xb1, 0x38, 0x16, 0x58, 0x61, 0x89, - 0xc8, 0xa5, 0x68, 0xc4, 0x05, 0xb9, 0x84, 0xf5, 0x2e, 0xea, 0x8b, 0x4d, 0xc8, 0x7e, 0x0b, 0xb9, 0x41, 0x16, 0x7e, - 0x84, 0x00, 0x62, 0x37, 0xa0, 0x82, 0x90, 0x48, 0x2c, 0x66, 0x43, 0x56, 0x44, 0x65, 0xb1, 0x6e, 0x0d, 0x2f, 0x16, - 0x8a, 0xed, 0x65, 0x4a, 0xed, 0x8d, 0x17, 0x22, 0xd3, 0x5c, 0x8a, 0xbd, 0xa8, 0x51, 0x34, 0x22, 0x8b, 0x0f, 0x25, - 0x3a, 0x44, 0x68, 0x8d, 0x62, 0x85, 0x1a, 0xbc, 0x2f, 0x1b, 0xed, 0x01, 0x86, 0x51, 0xa2, 0xae, 0x6b, 0xcf, 0x41, - 0x80, 0x61, 0x0e, 0x93, 0x5c, 0xe3, 0x4f, 0x76, 0xe7, 0xc3, 0x14, 0x6f, 0x44, 0x8f, 0x27, 0xdb, 0x3b, 0x85, 0xe8, - 0x64, 0x46, 0xe7, 0x31, 0x23, 0x97, 0xcc, 0x60, 0x17, 0x15, 0x19, 0x8c, 0xb5, 0xb6, 0x70, 0x3d, 0x96, 0xb2, 0xa4, - 0xc2, 0x29, 0x94, 0xea, 0x64, 0x2c, 0x8b, 0xa7, 0x34, 0x9b, 0x42, 0xbd, 0x12, 0x63, 0x46, 0x7e, 0xc3, 0x65, 0x05, - 0xa3, 0x9a, 0x3d, 0xcd, 0x19, 0x7c, 0xc5, 0x91, 0xa9, 0x19, 0x21, 0xac, 0x60, 0xab, 0xe7, 0x5c, 0xbf, 0x92, 0x22, - 0x63, 0x5d, 0x15, 0xe0, 0x97, 0x59, 0xf9, 0x87, 0x5a, 0x17, 0x7c, 0xb8, 0xd0, 0x2c, 0x8e, 0x04, 0x94, 0x88, 0xb0, - 0x42, 0x58, 0x24, 0x9a, 0xdd, 0xea, 0xc7, 0x52, 0x68, 0x26, 0x34, 0x61, 0x1e, 0xaa, 0x98, 0x27, 0x74, 0x3e, 0x67, - 0x62, 0xf4, 0x78, 0xca, 0xf3, 0x51, 0x2c, 0xd0, 0x1a, 0xad, 0xf1, 0xef, 0x82, 0xc0, 0x24, 0xc9, 0x25, 0x4f, 0xe1, - 0x9f, 0x6f, 0x4f, 0x27, 0xd6, 0xe4, 0xd2, 0x6c, 0x0b, 0x46, 0xa2, 0xa8, 0x3b, 0x96, 0x45, 0xec, 0xa6, 0xb0, 0x07, - 0xa4, 0x0b, 0xfa, 0x78, 0xbb, 0xc8, 0x99, 0x42, 0xac, 0x41, 0x44, 0xb9, 0x8e, 0x0e, 0xc2, 0xbf, 0x15, 0x31, 0x83, - 0x05, 0xe0, 0x28, 0xe5, 0x86, 0x04, 0xbe, 0xe4, 0x6e, 0x53, 0x8d, 0x4a, 0xa2, 0xf6, 0x51, 0x90, 0x11, 0x4f, 0x74, - 0xb1, 0x50, 0x9a, 0x8d, 0xde, 0xdd, 0xcd, 0x99, 0xc2, 0x3f, 0x17, 0xe4, 0xa3, 0xe8, 0x7d, 0x14, 0x09, 0x9b, 0xcd, - 0xf5, 0xdd, 0x95, 0xa1, 0xe6, 0x69, 0x14, 0xe1, 0x7f, 0x4c, 0xd1, 0x82, 0xd1, 0x0c, 0x48, 0x9a, 0x03, 0xd9, 0x1b, - 0x99, 0xdf, 0x8d, 0x79, 0x9e, 0x5f, 0x2d, 0xe6, 0x73, 0x59, 0x68, 0xac, 0x05, 0x59, 0x6a, 0x59, 0xc1, 0x07, 0x56, - 0x74, 0xa9, 0x6e, 0xb8, 0xce, 0xa6, 0xb1, 0x46, 0xcb, 0x8c, 0x2a, 0xb6, 0xf7, 0x48, 0xca, 0x9c, 0x51, 0x91, 0x72, - 0xc2, 0x7b, 0x3f, 0x17, 0xa9, 0x58, 0xe4, 0x79, 0x77, 0x58, 0x30, 0xfa, 0xb9, 0x6b, 0xb2, 0xed, 0xe1, 0x90, 0x9a, - 0xdf, 0x0f, 0x8b, 0x82, 0xde, 0x41, 0x41, 0x42, 0xa0, 0x58, 0x8f, 0xa7, 0x3f, 0x5f, 0xbd, 0x7e, 0x95, 0xd8, 0xbd, - 0xc2, 0xc7, 0x77, 0x31, 0x2f, 0xf7, 0x1f, 0x5f, 0xe3, 0x71, 0x21, 0x67, 0x1b, 0x5d, 0x5b, 0xd0, 0xf1, 0xee, 0x37, - 0x86, 0xc0, 0x08, 0xdf, 0xb7, 0x4d, 0x87, 0x23, 0x78, 0x65, 0x30, 0x1f, 0x32, 0x89, 0xeb, 0x17, 0xfe, 0x49, 0x6d, - 0x72, 0xcc, 0xd1, 0xf7, 0x47, 0xab, 0x8b, 0xbb, 0x25, 0x23, 0x66, 0x9c, 0x73, 0x38, 0x18, 0x61, 0x8c, 0x19, 0xd5, - 0xd9, 0x74, 0xc9, 0x4c, 0x63, 0x6b, 0x3f, 0x62, 0xb6, 0x5e, 0xe3, 0xaf, 0xd2, 0x63, 0xbd, 0xde, 0x27, 0x84, 0x1b, - 0x7a, 0x45, 0xf4, 0x6a, 0xc5, 0x09, 0xe1, 0x08, 0xbf, 0xe5, 0x64, 0x49, 0xfd, 0x84, 0xe0, 0x64, 0x83, 0xed, 0x99, - 0x5a, 0x2a, 0x03, 0x27, 0xe0, 0x17, 0x56, 0x68, 0x56, 0xa4, 0x5a, 0xe0, 0x82, 0x8d, 0x73, 0x18, 0xc7, 0x7e, 0x1b, - 0x4f, 0xa9, 0x7a, 0x3c, 0xa5, 0x62, 0xc2, 0x46, 0xe9, 0x57, 0xb9, 0xc6, 0x4c, 0x90, 0x68, 0xcc, 0x05, 0xcd, 0xf9, - 0x57, 0x36, 0x8a, 0xdc, 0xb9, 0xf0, 0x48, 0xef, 0xb1, 0x5b, 0xcd, 0xc4, 0x48, 0xed, 0x3d, 0x7f, 0xf7, 0xeb, 0x4b, - 0xb7, 0x98, 0xb5, 0xb3, 0x02, 0x2d, 0xd5, 0x62, 0xce, 0x8a, 0x18, 0x61, 0x77, 0x56, 0x3c, 0xe5, 0x86, 0x4e, 0xfe, - 0x4a, 0xe7, 0x36, 0x85, 0xab, 0xdf, 0xe7, 0x23, 0xaa, 0xd9, 0x1b, 0x26, 0x46, 0x5c, 0x4c, 0xc8, 0x7e, 0xdb, 0xa6, - 0x4f, 0xa9, 0xcb, 0x18, 0x95, 0x49, 0xd7, 0xf7, 0x9e, 0xe6, 0x66, 0xee, 0xe5, 0xe7, 0x22, 0x46, 0x6b, 0xa5, 0xa9, - 0xe6, 0xd9, 0x1e, 0x1d, 0x8d, 0x5e, 0x08, 0xae, 0xb9, 0x19, 0x61, 0x01, 0x4b, 0x04, 0xb8, 0xca, 0xec, 0xa9, 0xe1, - 0x47, 0x1e, 0x23, 0x1c, 0xc7, 0xee, 0x2c, 0x98, 0x22, 0xb7, 0x66, 0x07, 0x07, 0x15, 0xe5, 0xef, 0xb1, 0xd4, 0x66, - 0x92, 0xfe, 0x00, 0x25, 0xf3, 0x85, 0x82, 0xc5, 0xf6, 0x5d, 0xc0, 0x41, 0x23, 0x87, 0x8a, 0x15, 0x5f, 0xd8, 0xa8, - 0x44, 0x10, 0x15, 0xa3, 0xe5, 0x46, 0x1f, 0x6e, 0x7b, 0x68, 0xd2, 0x1f, 0x74, 0x43, 0x12, 0xce, 0x1c, 0xb2, 0x5b, - 0x4e, 0x85, 0x33, 0x55, 0x12, 0x95, 0x18, 0x0e, 0xd4, 0x92, 0xb0, 0x28, 0xe2, 0xe7, 0x37, 0x8f, 0x05, 0xf0, 0x10, - 0x21, 0xe5, 0xf0, 0x67, 0xee, 0xd3, 0x2f, 0xe6, 0xf0, 0x50, 0x58, 0x20, 0xac, 0xed, 0x48, 0x15, 0x42, 0x6b, 0x84, - 0xb5, 0x1f, 0xae, 0x25, 0x4a, 0x9e, 0x2f, 0x82, 0x53, 0x9b, 0xbc, 0xe5, 0xe6, 0xd8, 0x06, 0xda, 0x46, 0x35, 0x3b, - 0x38, 0x88, 0x59, 0x52, 0x22, 0x06, 0xd9, 0x6f, 0xbb, 0x45, 0x0a, 0xa0, 0xf5, 0x8d, 0x71, 0x43, 0xcf, 0x86, 0xc1, - 0xd9, 0x67, 0x89, 0x90, 0x0f, 0xb3, 0x8c, 0x29, 0x25, 0x8b, 0x83, 0x83, 0x7d, 0x53, 0xbe, 0xe4, 0x2c, 0x60, 0x11, - 0x5f, 0xdf, 0x88, 0x6a, 0x08, 0xa8, 0x3a, 0x6d, 0x3d, 0xdf, 0x44, 0x2a, 0xbe, 0xc9, 0x33, 0x21, 0x69, 0x74, 0x7d, - 0x1d, 0x35, 0x34, 0x76, 0x70, 0x98, 0x30, 0xdf, 0xf5, 0xdd, 0x13, 0x66, 0xd9, 0x42, 0xc3, 0x84, 0x6c, 0x81, 0x66, - 0x27, 0x3f, 0x18, 0xd7, 0x87, 0x84, 0x35, 0x56, 0x68, 0x1d, 0xac, 0xe8, 0xce, 0xa6, 0x0d, 0x7f, 0x63, 0x97, 0x6e, - 0x39, 0x31, 0x3c, 0x45, 0xb0, 0x8e, 0x7d, 0x36, 0x58, 0x63, 0x03, 0x7b, 0x3f, 0x1b, 0x69, 0x06, 0xda, 0xd7, 0x83, - 0xae, 0xcb, 0x27, 0xca, 0x42, 0xae, 0x60, 0xff, 0x2c, 0x98, 0xd2, 0x16, 0x91, 0x63, 0x8d, 0x25, 0x86, 0x33, 0x6a, - 0x93, 0xe9, 0xac, 0xb1, 0xa4, 0xbb, 0xc6, 0xf6, 0x7a, 0x0e, 0x67, 0xa3, 0x02, 0xa4, 0xfe, 0x3e, 0x3e, 0xc1, 0x58, - 0x35, 0x5a, 0xad, 0xde, 0x72, 0xdf, 0x4a, 0xb5, 0x96, 0x25, 0xbf, 0xb6, 0xb1, 0x28, 0x4c, 0x20, 0x77, 0x38, 0xef, - 0xb7, 0xdd, 0xf8, 0xc5, 0x80, 0xec, 0xb7, 0x4a, 0x2c, 0x76, 0x60, 0xb5, 0xe3, 0xb1, 0x50, 0x7c, 0x6d, 0x9b, 0x42, - 0xe6, 0xac, 0xaf, 0xe1, 0x4b, 0x32, 0xdd, 0xc2, 0xd5, 0x29, 0xe9, 0x03, 0xd7, 0x91, 0x4c, 0x07, 0xdf, 0xc2, 0x27, - 0x4f, 0x11, 0x62, 0xbd, 0x9d, 0x57, 0x11, 0x8e, 0x2f, 0x75, 0xc2, 0xb1, 0x31, 0x8d, 0x68, 0x5e, 0x56, 0x89, 0x4a, - 0x34, 0x73, 0x5b, 0xbd, 0xca, 0xc2, 0xc2, 0x0c, 0xa6, 0x9a, 0x52, 0xd0, 0xc4, 0x2b, 0x3a, 0x63, 0x2a, 0x66, 0x08, - 0x7f, 0xab, 0x80, 0xc5, 0x4f, 0x28, 0x32, 0x08, 0xce, 0x50, 0x05, 0x67, 0x28, 0xb0, 0xbb, 0xc0, 0xa4, 0xd5, 0xb7, - 0x9c, 0xc2, 0xac, 0xaf, 0x06, 0x15, 0x6f, 0x17, 0x4c, 0xde, 0x1c, 0xce, 0x0e, 0xc1, 0x3d, 0xfc, 0x6c, 0x9a, 0x05, - 0x9a, 0x61, 0x21, 0x14, 0xc2, 0xfb, 0xad, 0xcd, 0x95, 0xf4, 0xa5, 0xaa, 0x39, 0xf6, 0x07, 0xb0, 0x0e, 0xe6, 0xd8, - 0x48, 0xb8, 0x32, 0x7f, 0x6b, 0x5b, 0x0d, 0xc0, 0x76, 0x05, 0x98, 0x91, 0x8c, 0x73, 0xaa, 0xe3, 0xf6, 0x51, 0x0b, - 0x18, 0xd3, 0x2f, 0x0c, 0x4e, 0x15, 0x84, 0xb6, 0xa7, 0xc2, 0x92, 0x85, 0x50, 0x53, 0x3e, 0xd6, 0xf1, 0xef, 0xc2, - 0x10, 0x15, 0x96, 0x2b, 0x06, 0x12, 0x4e, 0xc0, 0x1e, 0x1b, 0x82, 0xf3, 0xbb, 0x80, 0x7e, 0xba, 0xe5, 0x41, 0xe4, - 0x46, 0x6a, 0x08, 0x17, 0x90, 0x87, 0x8a, 0xb5, 0xae, 0xc8, 0x4c, 0xc9, 0xb8, 0x01, 0xf7, 0xd8, 0xee, 0xd9, 0x16, - 0x53, 0x47, 0x0d, 0x44, 0xc0, 0xc1, 0x8a, 0x34, 0x24, 0x11, 0x2e, 0x51, 0x27, 0x5a, 0xbe, 0x94, 0x37, 0xac, 0x78, - 0x4c, 0x61, 0xf0, 0xa9, 0xad, 0xbe, 0xb6, 0x47, 0x81, 0xa1, 0xf8, 0xba, 0xeb, 0xf1, 0xe5, 0xda, 0x4c, 0xfc, 0x4d, - 0x21, 0x67, 0x5c, 0x31, 0xe0, 0xdb, 0x2c, 0xfc, 0x05, 0x6c, 0x34, 0xb3, 0x23, 0xe1, 0xb8, 0x61, 0x25, 0x7e, 0x3d, - 0x7c, 0x59, 0xc7, 0xaf, 0xeb, 0x7b, 0x4f, 0x27, 0x9e, 0x02, 0xd6, 0xf7, 0x31, 0xc2, 0xb1, 0x13, 0x2f, 0x82, 0x93, - 0x2e, 0x99, 0x22, 0x77, 0xcc, 0xaf, 0x56, 0x3a, 0x10, 0xe3, 0x6a, 0x9c, 0x23, 0xb3, 0xdb, 0x06, 0xad, 0xe9, 0x68, - 0x04, 0x2c, 0x5e, 0x21, 0xf3, 0x3c, 0x38, 0xac, 0xb0, 0xe8, 0x96, 0xc7, 0xd3, 0xf5, 0xbd, 0xa7, 0x57, 0xdf, 0x3b, - 0xa1, 0x20, 0x3f, 0x3c, 0xa4, 0xfc, 0x40, 0xc5, 0x88, 0x15, 0x20, 0x57, 0x06, 0xab, 0xe5, 0xce, 0xd9, 0xc7, 0x52, - 0x08, 0x96, 0x69, 0x36, 0x02, 0xa1, 0x45, 0x10, 0x9d, 0x4c, 0xa5, 0xd2, 0x65, 0x62, 0x35, 0x7a, 0x11, 0x0a, 0xa1, - 0x49, 0x46, 0xf3, 0x3c, 0xb6, 0x02, 0xca, 0x4c, 0x7e, 0x61, 0x3b, 0x46, 0xdd, 0xad, 0x0d, 0xb9, 0x6c, 0x86, 0x05, - 0xcd, 0xb0, 0x44, 0xcd, 0x73, 0x9e, 0xb1, 0xf2, 0xf0, 0xba, 0x4a, 0xb8, 0x18, 0xb1, 0x5b, 0xa0, 0x23, 0xe8, 0xf2, - 0xf2, 0xb2, 0x85, 0xdb, 0x68, 0x6d, 0x01, 0xbe, 0xdc, 0x02, 0xec, 0x77, 0x8e, 0x4d, 0x2b, 0x88, 0x2f, 0x77, 0x92, - 0x35, 0x14, 0x9c, 0x95, 0xdc, 0x0b, 0x5a, 0x96, 0x3c, 0x23, 0x3c, 0x62, 0x39, 0xd3, 0xcc, 0x93, 0x73, 0x60, 0xa6, - 0xed, 0xd6, 0x7d, 0x5b, 0xc2, 0xaf, 0x44, 0x27, 0xbf, 0xcb, 0xfc, 0x9a, 0xab, 0x52, 0x74, 0xaf, 0x96, 0xa7, 0x82, - 0x76, 0x4f, 0xdb, 0xe5, 0xa1, 0x5a, 0xd3, 0x6c, 0x6a, 0x25, 0xf6, 0x78, 0x6b, 0x4a, 0x55, 0x1b, 0x8e, 0xb4, 0x97, - 0x9b, 0xe8, 0x53, 0xe1, 0x86, 0xb9, 0x0b, 0x04, 0x57, 0x8e, 0x28, 0x30, 0x10, 0x02, 0xed, 0xb2, 0x3d, 0xa6, 0x79, - 0x3e, 0xa4, 0xd9, 0xe7, 0x3a, 0xf6, 0x57, 0x68, 0x40, 0x36, 0xa9, 0x71, 0x90, 0x15, 0x90, 0xac, 0x70, 0xde, 0x9e, - 0x4a, 0xd7, 0x36, 0x4a, 0xbc, 0xdf, 0xaa, 0xd0, 0xbe, 0xbe, 0xd0, 0xdf, 0xc4, 0x76, 0x33, 0x22, 0xe1, 0x66, 0x16, - 0x03, 0x15, 0xf8, 0x97, 0x18, 0xe7, 0xe9, 0x81, 0xc3, 0x3b, 0x10, 0x3c, 0xd6, 0x1b, 0x03, 0xd1, 0x68, 0xb9, 0x1e, - 0x71, 0xf5, 0x6d, 0x08, 0xfc, 0x6f, 0x19, 0xe5, 0x93, 0xa0, 0x87, 0x7f, 0x77, 0xa0, 0x25, 0x8d, 0x73, 0x8c, 0x73, - 0x39, 0x32, 0xc7, 0x50, 0x78, 0x42, 0xf3, 0x0b, 0x30, 0x2f, 0x06, 0xdf, 0x5f, 0xdb, 0x2c, 0xc3, 0x97, 0xc1, 0x30, - 0x54, 0x37, 0x64, 0x28, 0x6a, 0x28, 0xe0, 0x88, 0xaa, 0x30, 0x67, 0xae, 0xac, 0x89, 0x92, 0x8e, 0x6b, 0xb7, 0xe2, - 0xb8, 0xa3, 0xb9, 0x05, 0x89, 0xe3, 0x58, 0x81, 0x34, 0xe7, 0xf9, 0xfb, 0x6a, 0x16, 0x6a, 0x6b, 0x16, 0x2a, 0x09, - 0xa4, 0x2d, 0x54, 0x21, 0x73, 0x50, 0x3d, 0xd5, 0x02, 0x85, 0xa5, 0x80, 0x65, 0x4d, 0x80, 0x42, 0xa3, 0x92, 0xe0, - 0xe6, 0x44, 0xe3, 0xc2, 0x89, 0x3a, 0x0e, 0xd7, 0x80, 0x64, 0x54, 0x55, 0x24, 0xb2, 0x9b, 0xa3, 0x26, 0xfb, 0x4a, - 0x5c, 0xa0, 0x0d, 0xfe, 0x7e, 0xbd, 0x76, 0x50, 0x62, 0xc8, 0xad, 0x4e, 0x8d, 0x31, 0x0e, 0xc0, 0x82, 0x25, 0x71, - 0xcc, 0xb0, 0x65, 0x7d, 0x36, 0x81, 0x53, 0xb6, 0xbb, 0x4f, 0x88, 0xac, 0x60, 0x53, 0x63, 0x2a, 0x3d, 0x77, 0x25, - 0x11, 0xa6, 0x9e, 0x2d, 0x2d, 0xaa, 0x89, 0x13, 0x12, 0x79, 0xed, 0x44, 0xd4, 0x5b, 0xd6, 0x84, 0xc3, 0x34, 0x28, - 0xb6, 0x4e, 0x81, 0xa8, 0x16, 0xbb, 0xe0, 0xbd, 0x0b, 0x6b, 0x6a, 0xed, 0x04, 0x10, 0x2f, 0x6a, 0x10, 0x0f, 0x40, - 0x2b, 0x2d, 0xf1, 0x92, 0x03, 0x42, 0xeb, 0x95, 0x63, 0x86, 0x0b, 0xbb, 0x10, 0x5b, 0x50, 0xdc, 0x64, 0x3f, 0x0d, - 0x16, 0x82, 0x2c, 0xab, 0x80, 0xbf, 0x0b, 0x8f, 0x88, 0x18, 0x06, 0x2f, 0x56, 0xab, 0x2d, 0xb4, 0xdb, 0xc9, 0x85, - 0xa2, 0xa4, 0x92, 0x0e, 0x57, 0xab, 0xaf, 0x12, 0xc5, 0x8e, 0xff, 0xc5, 0x0c, 0xf5, 0x3c, 0xd1, 0x7d, 0xf8, 0x12, - 0x4a, 0x19, 0x76, 0xb4, 0x4a, 0x29, 0x05, 0x87, 0x3a, 0xd6, 0xd6, 0x17, 0x4a, 0x07, 0x94, 0xfb, 0xf1, 0x16, 0x01, - 0x33, 0x89, 0xee, 0xa4, 0xae, 0xa6, 0xfc, 0xd8, 0x35, 0x2d, 0x10, 0x42, 0xa9, 0x32, 0xb2, 0xcc, 0xfe, 0x2e, 0xf9, - 0xf2, 0xe0, 0x40, 0x05, 0x0d, 0x5d, 0x97, 0x94, 0xe2, 0xef, 0x18, 0x4e, 0x65, 0x75, 0x27, 0x0c, 0xfb, 0xf2, 0xb7, - 0x3f, 0x87, 0xb6, 0xa4, 0xd3, 0x56, 0x17, 0x04, 0x73, 0x7a, 0x43, 0xb9, 0xde, 0x2b, 0x5b, 0xb1, 0x82, 0x79, 0xcc, - 0xd0, 0xd2, 0x71, 0x1b, 0x49, 0xc1, 0x80, 0x7f, 0x04, 0xb2, 0xe0, 0xb9, 0x68, 0x8b, 0xf8, 0xd9, 0x94, 0x81, 0x2a, - 0xdb, 0x33, 0x12, 0xa5, 0x78, 0xb8, 0xef, 0x0e, 0x12, 0xd7, 0xf0, 0xee, 0xb1, 0xaf, 0x37, 0xab, 0xd7, 0xa4, 0x81, - 0x39, 0x2b, 0xc6, 0xb2, 0x98, 0xf9, 0xbc, 0xf5, 0xc6, 0xb7, 0x23, 0x8e, 0x7c, 0x1c, 0xef, 0x6c, 0xdb, 0x89, 0x00, - 0xdd, 0x0d, 0xd9, 0xbb, 0x92, 0xda, 0x6b, 0xa7, 0x69, 0x79, 0x00, 0x5b, 0x05, 0xa1, 0xc7, 0x4c, 0x15, 0x4a, 0xf9, - 0x4e, 0xbd, 0xda, 0xb5, 0xba, 0x93, 0xfd, 0x76, 0xb7, 0x94, 0xfc, 0x3c, 0x36, 0x74, 0xad, 0x8e, 0xc3, 0x9d, 0xaa, - 0x72, 0x91, 0x8f, 0xdc, 0x60, 0x05, 0xc2, 0xcc, 0xe1, 0xd1, 0x0d, 0xcf, 0xf3, 0x2a, 0xf5, 0x3f, 0x21, 0xed, 0xca, - 0x91, 0x76, 0xe9, 0x49, 0x3b, 0x90, 0x0a, 0x20, 0xed, 0xb6, 0xb9, 0xaa, 0xba, 0xdc, 0xda, 0x9e, 0xd2, 0x12, 0x75, - 0x65, 0xc4, 0x69, 0xe8, 0x6f, 0xe1, 0x47, 0x80, 0x4a, 0xe6, 0xeb, 0x73, 0xec, 0xf4, 0x31, 0x20, 0x06, 0x5a, 0x9d, - 0x26, 0x0b, 0x35, 0x15, 0x9f, 0x63, 0x84, 0xd5, 0x9a, 0x95, 0x98, 0xfd, 0xf0, 0x29, 0x28, 0xed, 0x82, 0xe9, 0xc0, - 0x39, 0x66, 0x92, 0xff, 0x23, 0x3e, 0xca, 0xcf, 0x4e, 0xb8, 0xd9, 0x29, 0x3f, 0x3b, 0xa0, 0xf5, 0xd5, 0xec, 0x46, - 0xdf, 0xa7, 0xf6, 0x66, 0x7a, 0xa2, 0x9c, 0x5e, 0xb5, 0xde, 0xab, 0x55, 0xbc, 0x91, 0x02, 0x1a, 0x7d, 0x27, 0xa5, - 0x14, 0x65, 0xeb, 0x40, 0x03, 0x42, 0xc8, 0x40, 0xc2, 0xda, 0x4e, 0xba, 0x3c, 0xe5, 0x5e, 0xfe, 0x2b, 0x3d, 0x8f, - 0x51, 0xdc, 0xdb, 0xfa, 0x8f, 0xe5, 0x6c, 0x0e, 0x0c, 0xd9, 0x06, 0x4a, 0x4f, 0x98, 0xeb, 0xb0, 0xca, 0x5f, 0xef, - 0x48, 0xab, 0xd5, 0x31, 0xfb, 0xb1, 0x86, 0x4d, 0xa5, 0xd4, 0xbc, 0xdf, 0x5a, 0x2f, 0xca, 0xa4, 0x92, 0x70, 0xec, - 0xd2, 0xad, 0x3c, 0xde, 0xd4, 0xcc, 0xf8, 0x8c, 0xd7, 0xb1, 0xb0, 0x74, 0x58, 0x00, 0xad, 0x0b, 0xc8, 0x8f, 0x47, - 0xf7, 0x70, 0xfd, 0xd7, 0x15, 0x70, 0x96, 0xeb, 0x0d, 0xf0, 0x2d, 0xd7, 0xeb, 0x47, 0xda, 0x49, 0xda, 0xf8, 0xd1, - 0x0e, 0xb9, 0xb7, 0x84, 0x5e, 0x95, 0xe9, 0x64, 0xc6, 0xfe, 0x00, 0xd2, 0xb6, 0x58, 0x48, 0xb2, 0x9c, 0xc9, 0x11, - 0x4b, 0x23, 0x39, 0x67, 0x22, 0x5a, 0x83, 0x9e, 0xd5, 0x21, 0xc0, 0x3f, 0x22, 0x5e, 0xbe, 0xad, 0xeb, 0x5b, 0xd3, - 0x47, 0x7a, 0x0d, 0xaa, 0xb0, 0x97, 0x7c, 0x87, 0x32, 0xf6, 0x3d, 0x2b, 0x94, 0xe1, 0x49, 0x4b, 0xf6, 0xf6, 0x25, - 0xaf, 0x0e, 0xa8, 0x97, 0x3c, 0xfd, 0x76, 0x95, 0x4a, 0x20, 0x89, 0xda, 0xc9, 0x59, 0x72, 0x1c, 0x21, 0xa3, 0x31, - 0x7e, 0xe6, 0x35, 0xc6, 0x8b, 0x52, 0x63, 0xfc, 0x5c, 0x93, 0xc5, 0x86, 0xc6, 0xf8, 0x0f, 0x41, 0x9e, 0xeb, 0xde, - 0x73, 0xaf, 0x4d, 0x7f, 0x23, 0x73, 0x9e, 0xdd, 0xc5, 0x51, 0xce, 0x75, 0x13, 0x6e, 0x13, 0x23, 0xbc, 0xb4, 0x19, - 0xa0, 0x6a, 0x34, 0xfa, 0xee, 0xb5, 0x97, 0xff, 0xb0, 0x10, 0x24, 0xba, 0x97, 0x73, 0x7d, 0x2f, 0xc2, 0x53, 0x4d, - 0xfe, 0x82, 0x5f, 0xf7, 0x96, 0xf1, 0xaf, 0x54, 0x4f, 0x93, 0x82, 0x8a, 0x91, 0x9c, 0xc5, 0xa8, 0x11, 0x45, 0x28, - 0x51, 0x46, 0x08, 0x79, 0x80, 0xd6, 0xf7, 0xfe, 0xc2, 0xaf, 0x24, 0x89, 0x7a, 0x51, 0x63, 0xaa, 0xb1, 0xa6, 0xe4, - 0xaf, 0x8b, 0x7b, 0xcb, 0x57, 0x72, 0x7d, 0xf9, 0x17, 0x7e, 0xaa, 0x4b, 0xb5, 0x3e, 0xbe, 0x65, 0x24, 0x46, 0xe4, - 0xf2, 0xa9, 0x1f, 0xd2, 0x63, 0x39, 0xb3, 0x0a, 0xfe, 0x08, 0xe1, 0x2f, 0xa0, 0xd7, 0xbd, 0xe4, 0x15, 0x11, 0x72, - 0x77, 0x30, 0xfb, 0x24, 0x92, 0x46, 0x79, 0x10, 0x1d, 0x1c, 0x04, 0x69, 0x25, 0x0b, 0x81, 0xff, 0x96, 0xa4, 0x26, - 0xaa, 0x63, 0x46, 0xa1, 0xa5, 0xbf, 0x65, 0xcc, 0x91, 0x6f, 0x26, 0xf6, 0x9a, 0x6a, 0xb7, 0x63, 0x79, 0xdf, 0xea, - 0x1e, 0x12, 0xae, 0x59, 0x41, 0xb5, 0x2c, 0x06, 0x28, 0x64, 0x4b, 0xf0, 0x57, 0x4e, 0xfe, 0xea, 0xef, 0xfd, 0x3f, - 0xff, 0xe3, 0xcf, 0xf1, 0x9f, 0xc5, 0xe0, 0x2f, 0x2c, 0x18, 0x39, 0xba, 0x88, 0x7b, 0x69, 0xbc, 0xdf, 0x6c, 0xae, - 0xfe, 0x3c, 0xea, 0xff, 0x37, 0x6d, 0x7e, 0x7d, 0xd8, 0xfc, 0x34, 0x40, 0xab, 0xf8, 0xcf, 0xa3, 0x5e, 0xdf, 0x7d, - 0xf5, 0xff, 0xfb, 0xf2, 0x4f, 0x35, 0x38, 0xb4, 0x89, 0xf7, 0x10, 0x3a, 0x9a, 0xe0, 0x5f, 0x04, 0x39, 0x6a, 0x36, - 0x2f, 0x8f, 0x26, 0xf8, 0x27, 0x41, 0x8e, 0xe0, 0xef, 0x9d, 0x26, 0x6f, 0xd9, 0xe4, 0xe9, 0xed, 0x3c, 0xfe, 0xeb, - 0x72, 0x75, 0x6f, 0xf9, 0x95, 0xaf, 0xa1, 0xdd, 0xfe, 0x7f, 0xff, 0xf9, 0xa7, 0x8a, 0x7e, 0xbc, 0x24, 0x47, 0x83, - 0x06, 0x8a, 0x4d, 0xf2, 0x21, 0xb1, 0x7f, 0xe2, 0x5e, 0xda, 0xff, 0x6f, 0x37, 0x94, 0xe8, 0xc7, 0x3f, 0xff, 0xba, - 0xb8, 0x24, 0x83, 0x55, 0x1c, 0xad, 0x7e, 0x44, 0x2b, 0x84, 0x56, 0xf7, 0xd0, 0x5f, 0x38, 0x9a, 0x44, 0x08, 0xff, - 0x26, 0xc8, 0xd1, 0x8f, 0x47, 0x13, 0xfc, 0x49, 0x90, 0xa3, 0xe8, 0x68, 0x82, 0xdf, 0x4b, 0x72, 0xf4, 0xdf, 0x71, - 0x2f, 0xb5, 0x4a, 0xb8, 0x95, 0x51, 0x7f, 0xac, 0xe0, 0x26, 0x84, 0x16, 0x8c, 0xae, 0x34, 0xd7, 0x39, 0x43, 0xf7, - 0x8e, 0x38, 0x7e, 0x24, 0x01, 0x58, 0xb1, 0x06, 0x25, 0x8d, 0xb9, 0x84, 0x5d, 0x5e, 0xc3, 0xc2, 0x03, 0x06, 0xdd, - 0x4b, 0x39, 0xb6, 0x7a, 0x02, 0x95, 0x6a, 0x7b, 0x7b, 0xab, 0xe0, 0xfa, 0x16, 0x3f, 0x26, 0x8f, 0x64, 0xdc, 0x46, - 0x98, 0x53, 0xf8, 0xd1, 0x41, 0xf8, 0x83, 0x76, 0x17, 0x9e, 0xb0, 0xcd, 0x2d, 0x86, 0x09, 0x69, 0xf9, 0x99, 0x08, - 0xe1, 0x97, 0x3b, 0x32, 0xf5, 0x14, 0xd4, 0x0f, 0x08, 0xff, 0x5c, 0xbb, 0x1e, 0xc5, 0x8f, 0x35, 0x29, 0x91, 0xe3, - 0x5d, 0xc1, 0xd8, 0x07, 0x9a, 0x7f, 0x66, 0x45, 0xfc, 0x54, 0xe3, 0x76, 0xe7, 0x01, 0x36, 0xaa, 0xea, 0xfd, 0x36, - 0xea, 0x96, 0xb7, 0x5b, 0xcf, 0xa5, 0xbd, 0x4f, 0x80, 0x53, 0xb8, 0xae, 0xaf, 0x81, 0xb5, 0xdf, 0xe7, 0x5b, 0x4a, - 0xad, 0x82, 0xde, 0x44, 0xa8, 0x7e, 0x95, 0xca, 0xc5, 0x17, 0x9a, 0xf3, 0xd1, 0x9e, 0x66, 0xb3, 0x79, 0x4e, 0x35, - 0xdb, 0x73, 0x73, 0xde, 0xa3, 0xd0, 0x50, 0x54, 0xf2, 0x14, 0x7f, 0x88, 0x6a, 0xd3, 0xfe, 0x21, 0x92, 0x6a, 0xef, - 0xc4, 0x70, 0x9f, 0xe5, 0xf8, 0x12, 0x41, 0xcb, 0xeb, 0xb2, 0xcd, 0x1b, 0xc1, 0x66, 0x1b, 0x94, 0x65, 0x03, 0x73, - 0x7e, 0x2b, 0x0c, 0xf7, 0x9b, 0x84, 0x74, 0x7a, 0xd1, 0x85, 0xfa, 0x32, 0xb9, 0x8c, 0xe0, 0x26, 0xa7, 0x20, 0x82, - 0x19, 0xe5, 0x11, 0x94, 0xa0, 0xa4, 0xd5, 0xa5, 0x17, 0xac, 0x4b, 0x1b, 0x0d, 0xcf, 0x66, 0x67, 0x84, 0xf7, 0xa9, - 0xad, 0x9f, 0xe3, 0x29, 0x1e, 0x91, 0x66, 0x1b, 0x2f, 0x48, 0xcb, 0x54, 0xe9, 0x2e, 0x2e, 0x32, 0xd7, 0xcf, 0xc1, - 0x41, 0x5c, 0x24, 0x39, 0x55, 0xfa, 0x05, 0x68, 0x04, 0xc8, 0x02, 0x4f, 0x49, 0x91, 0xb0, 0x5b, 0x96, 0xc5, 0x19, - 0xc2, 0x53, 0x47, 0x83, 0x50, 0x17, 0x2d, 0x48, 0x50, 0x0c, 0xe4, 0x0c, 0x22, 0x58, 0x6f, 0xda, 0x6f, 0x0f, 0x08, - 0x21, 0xd1, 0x7e, 0xb3, 0x19, 0xf5, 0x0a, 0xf2, 0x8b, 0x48, 0x21, 0x25, 0x60, 0xa7, 0xc9, 0x4f, 0x90, 0xd4, 0x09, - 0x92, 0xe2, 0xf7, 0x32, 0xd1, 0x4c, 0xe9, 0x18, 0x92, 0x41, 0x49, 0xa0, 0x3c, 0x86, 0x47, 0x17, 0x47, 0x51, 0x03, - 0x52, 0x0d, 0x8a, 0x22, 0x5c, 0x90, 0x3b, 0x8d, 0xd2, 0x69, 0xff, 0x78, 0x10, 0x9e, 0x11, 0x36, 0x15, 0xfa, 0xbf, - 0xd3, 0xbd, 0x69, 0xbf, 0x65, 0xfa, 0xbf, 0x8c, 0x7a, 0x71, 0x41, 0x94, 0x65, 0xe3, 0x7a, 0x2a, 0x15, 0xcc, 0xcc, - 0x17, 0xa5, 0x6e, 0x80, 0xae, 0xef, 0x11, 0x69, 0x76, 0xd2, 0x78, 0x14, 0xce, 0xa4, 0x09, 0x1d, 0x3a, 0x50, 0xe0, - 0x9c, 0x40, 0x79, 0x5c, 0x10, 0xe8, 0xb4, 0xaa, 0x76, 0xa7, 0x53, 0x97, 0xf0, 0x63, 0xf4, 0x63, 0xef, 0x93, 0x48, - 0x7f, 0x13, 0x76, 0x04, 0x9f, 0xc4, 0x6a, 0x05, 0x7f, 0x7f, 0x13, 0x3d, 0x18, 0x96, 0x49, 0xfb, 0xc5, 0xa5, 0xfd, - 0x04, 0x69, 0x82, 0xa5, 0x66, 0xc0, 0x58, 0x95, 0xfc, 0x98, 0x5d, 0x9c, 0x31, 0xb1, 0x33, 0x38, 0x38, 0xe0, 0x7d, - 0xda, 0x68, 0x0f, 0xe0, 0x46, 0xa0, 0xd0, 0xea, 0x03, 0xd7, 0xd3, 0x38, 0x3a, 0xba, 0x8c, 0x50, 0x2f, 0xda, 0x83, - 0x55, 0xee, 0xca, 0x06, 0x71, 0xb0, 0xce, 0x1a, 0x9a, 0xa6, 0xa3, 0x4b, 0xd2, 0xea, 0xc5, 0xc2, 0x12, 0xf9, 0x1c, - 0xe1, 0xcc, 0xd1, 0xd4, 0x16, 0x1e, 0xa1, 0x86, 0x10, 0x0d, 0xff, 0x3d, 0x42, 0x8d, 0xa9, 0x6e, 0x8c, 0x51, 0x9a, - 0xc1, 0xdf, 0x78, 0x44, 0x08, 0x69, 0x76, 0xca, 0x8a, 0xfe, 0xb0, 0xa4, 0x28, 0x1d, 0x7b, 0xf5, 0x68, 0xdf, 0x6c, - 0x0e, 0xd9, 0x88, 0x79, 0x9f, 0x0d, 0x56, 0xab, 0xe8, 0xa2, 0x77, 0x19, 0xa1, 0x46, 0xec, 0xd1, 0xee, 0xc8, 0xe3, - 0x1d, 0x42, 0x58, 0x0c, 0xd6, 0xee, 0x06, 0xea, 0x86, 0xd5, 0x6e, 0x9b, 0x96, 0xd5, 0xfe, 0x0f, 0xc8, 0x02, 0x5b, - 0x97, 0x72, 0x8f, 0xe5, 0x6f, 0xe7, 0x30, 0x55, 0x8f, 0xdb, 0x92, 0xb4, 0x70, 0x41, 0xbc, 0xba, 0x9b, 0x12, 0x5d, - 0xe1, 0x7f, 0x46, 0xaa, 0xe2, 0xb8, 0x9f, 0xe3, 0xe9, 0x80, 0x08, 0x6a, 0xe4, 0x97, 0xae, 0x57, 0xa6, 0xb3, 0x9c, - 0xdc, 0xb0, 0x8d, 0xfb, 0xdf, 0x1c, 0xee, 0x64, 0x1e, 0xeb, 0x24, 0x5b, 0x14, 0x05, 0x13, 0xfa, 0x95, 0x1c, 0x39, - 0xc6, 0x8e, 0xe5, 0x20, 0x5b, 0xc1, 0xc5, 0x2e, 0x06, 0xae, 0xae, 0xe3, 0x77, 0xca, 0x68, 0x2b, 0x7b, 0x41, 0x46, - 0x96, 0xe1, 0x32, 0xd7, 0xbd, 0xdd, 0x85, 0x13, 0xa5, 0x63, 0x84, 0x47, 0xee, 0x1e, 0x38, 0x4e, 0x92, 0x64, 0x91, - 0x64, 0x90, 0x0d, 0x1d, 0x28, 0xb4, 0x36, 0xfb, 0x2a, 0x56, 0xe4, 0xb1, 0x4e, 0x04, 0xbb, 0x35, 0xdd, 0xc6, 0xa8, - 0x3a, 0xc4, 0xfd, 0x7e, 0xbb, 0xa0, 0x5d, 0x43, 0x80, 0x54, 0x22, 0xe4, 0x88, 0x01, 0x84, 0xe0, 0xee, 0xdf, 0x25, - 0x4d, 0xa9, 0x0a, 0x6f, 0xb6, 0xaa, 0x01, 0xf6, 0x43, 0x95, 0xf7, 0x02, 0xf4, 0xc4, 0x86, 0x3d, 0x2b, 0x0b, 0x5b, - 0xe5, 0x39, 0x42, 0x7c, 0x1c, 0x2f, 0x12, 0xb8, 0x11, 0x34, 0x98, 0x24, 0x04, 0x5a, 0xad, 0x16, 0x21, 0x6e, 0x4d, - 0x2b, 0xc5, 0xf4, 0x98, 0x4c, 0xfb, 0x45, 0xa3, 0x61, 0x94, 0xd7, 0x23, 0x8b, 0x17, 0x0b, 0x84, 0xc7, 0xe5, 0x5e, - 0xf3, 0xe5, 0xe6, 0xa4, 0xde, 0x55, 0x3c, 0xae, 0x2b, 0x81, 0x1b, 0x42, 0x20, 0xa3, 0x5f, 0xd4, 0xd0, 0x3a, 0x9e, - 0x90, 0xa3, 0xb8, 0x9f, 0xf4, 0xfe, 0xe7, 0x00, 0xf5, 0xe2, 0xe4, 0x10, 0x1d, 0x59, 0x5a, 0x32, 0x46, 0xdd, 0xcc, - 0xf6, 0xb1, 0x34, 0xb7, 0x9f, 0x6d, 0x6c, 0x14, 0x90, 0xa9, 0xc4, 0x82, 0xce, 0x58, 0x3a, 0x81, 0x5d, 0xef, 0x91, - 0x67, 0x8e, 0x01, 0x99, 0xd2, 0x89, 0xa3, 0x2d, 0x49, 0xd4, 0x93, 0xb4, 0xfc, 0xea, 0x45, 0x3d, 0x5a, 0x7d, 0xfd, - 0xcf, 0xa8, 0x97, 0xd1, 0xf4, 0x31, 0x5f, 0x3b, 0x25, 0x79, 0xad, 0x8f, 0x33, 0xdf, 0xc7, 0xda, 0x2e, 0x4e, 0x00, - 0xbc, 0x11, 0xda, 0xd6, 0x8e, 0x2c, 0xd0, 0x9a, 0x8f, 0x4b, 0xea, 0xa4, 0x12, 0x4d, 0x27, 0x00, 0xd5, 0x60, 0x11, - 0x54, 0x68, 0x1b, 0x10, 0x4c, 0x19, 0xb0, 0xc5, 0x23, 0x2d, 0x40, 0x73, 0x71, 0xd9, 0x42, 0xcb, 0x5a, 0x61, 0xc7, - 0x59, 0xd5, 0xef, 0xe2, 0x4b, 0xe2, 0x3d, 0x06, 0xaa, 0x7c, 0xb1, 0xe8, 0x8e, 0x1b, 0x0d, 0xa4, 0x3c, 0x7e, 0x8d, - 0xfa, 0xe3, 0x01, 0xbe, 0x05, 0x14, 0xc2, 0x35, 0x8c, 0xc2, 0xb5, 0x39, 0x76, 0xdc, 0x1c, 0x1b, 0x0d, 0xb9, 0x46, - 0xdd, 0xa0, 0xf2, 0xc2, 0x55, 0x5e, 0xaf, 0x2d, 0x64, 0x36, 0x31, 0xee, 0x1c, 0x99, 0x14, 0x30, 0x04, 0x23, 0x84, - 0xbc, 0x92, 0x68, 0x67, 0xb3, 0xd0, 0x28, 0x54, 0x37, 0xbb, 0x17, 0x28, 0xaa, 0x3d, 0x3d, 0x62, 0x80, 0x05, 0x54, - 0x2d, 0xd5, 0xc8, 0x53, 0x8d, 0x47, 0x8d, 0xb6, 0x41, 0xf7, 0x66, 0xbb, 0x5b, 0x6f, 0xec, 0x7e, 0xd5, 0x18, 0x1e, - 0x35, 0xc8, 0xb4, 0xda, 0xe1, 0x6b, 0xd9, 0x68, 0xac, 0xeb, 0xf7, 0xa5, 0x7e, 0x13, 0xd7, 0xee, 0x2f, 0x9e, 0x6e, - 0x99, 0x78, 0xf8, 0xd3, 0xb7, 0x3a, 0x6f, 0x45, 0xc2, 0x85, 0x60, 0x05, 0x9c, 0xb0, 0x44, 0x63, 0xb1, 0x5e, 0x97, - 0xa7, 0xfe, 0xef, 0xda, 0xda, 0x8c, 0x11, 0x0e, 0x74, 0xc8, 0x48, 0x6d, 0x58, 0xe2, 0x02, 0x53, 0x43, 0x45, 0x08, - 0x21, 0x1f, 0xb4, 0x37, 0x8f, 0xd1, 0x86, 0x24, 0x65, 0x24, 0x38, 0xbb, 0x63, 0x45, 0x58, 0x72, 0x7d, 0xef, 0xb1, - 0xfc, 0xae, 0x48, 0xd7, 0x17, 0x83, 0xd4, 0x14, 0xcb, 0x1d, 0x21, 0xcb, 0xc9, 0x17, 0x90, 0x73, 0xca, 0x0b, 0x96, - 0xc4, 0x10, 0xc4, 0x27, 0xbc, 0x60, 0x86, 0x71, 0xbf, 0xe7, 0xe5, 0xc6, 0xac, 0xce, 0x69, 0x66, 0xa1, 0xf6, 0x07, - 0xa0, 0x99, 0x83, 0x72, 0x48, 0x92, 0xad, 0x62, 0xd7, 0xf7, 0x1e, 0xbe, 0xde, 0x25, 0x43, 0xaf, 0x56, 0x4e, 0x7a, - 0xce, 0x80, 0xf5, 0xc1, 0x79, 0x35, 0xd4, 0xcc, 0xfd, 0x48, 0xe3, 0xcc, 0x30, 0x51, 0x79, 0xcc, 0x01, 0x99, 0xae, - 0xef, 0x3d, 0x7c, 0x17, 0x73, 0xa3, 0x9b, 0x42, 0x38, 0x9c, 0x77, 0x5c, 0x90, 0x98, 0x12, 0x86, 0xec, 0xe4, 0x4b, - 0x3a, 0x56, 0x04, 0xa7, 0x7b, 0x4a, 0x4d, 0x26, 0x88, 0x1d, 0x7d, 0x31, 0x20, 0x99, 0x03, 0x01, 0xc9, 0x10, 0xce, - 0x6a, 0x72, 0x1d, 0x31, 0x6b, 0x60, 0x3a, 0xbb, 0x82, 0xc5, 0x48, 0x2c, 0x7b, 0x88, 0x70, 0x66, 0xba, 0xd5, 0x6b, - 0x7b, 0x9c, 0x28, 0xba, 0x69, 0xe8, 0x56, 0xc9, 0xb3, 0xef, 0x41, 0xf0, 0xf2, 0x1f, 0xaf, 0x5c, 0xdb, 0x65, 0xc2, - 0x13, 0x6f, 0x91, 0x76, 0x7d, 0xef, 0xe1, 0xaf, 0xce, 0x28, 0x6d, 0x4e, 0x3d, 0xf9, 0xdf, 0x92, 0x51, 0x1f, 0xfe, - 0x9a, 0x54, 0xb9, 0xa6, 0xf0, 0xf5, 0xbd, 0x87, 0xbf, 0xef, 0x2a, 0x06, 0xe9, 0xeb, 0x45, 0xa5, 0x24, 0x30, 0xe3, - 0x5b, 0xb2, 0x3c, 0x5d, 0xba, 0xb3, 0x22, 0x15, 0x6b, 0x6c, 0x4e, 0xa8, 0x54, 0xad, 0x4b, 0xdd, 0xca, 0x13, 0x2c, - 0x89, 0xb9, 0x4a, 0xaa, 0x2f, 0x9b, 0x43, 0x63, 0x2e, 0xc5, 0x55, 0x26, 0xe7, 0xec, 0x1b, 0xf7, 0x4b, 0x4f, 0x35, - 0x4a, 0xf8, 0x0c, 0x0c, 0x71, 0xcc, 0xd8, 0x05, 0xde, 0x6f, 0xa1, 0xee, 0xc6, 0x79, 0x26, 0x0d, 0xa2, 0x16, 0xf5, - 0xc3, 0x06, 0x53, 0xd2, 0xc2, 0x19, 0x69, 0xe1, 0x9c, 0xa8, 0x7e, 0xcb, 0x9e, 0x18, 0xdd, 0xbc, 0x6c, 0xda, 0x9e, - 0x3b, 0xb0, 0xdd, 0x73, 0xbb, 0x6f, 0xed, 0xa1, 0x3c, 0xed, 0xe6, 0x46, 0x7f, 0x69, 0x0e, 0xfa, 0xa9, 0x41, 0x8d, - 0x27, 0x2c, 0x2e, 0x70, 0x61, 0x5a, 0xbe, 0xe2, 0xc3, 0x1c, 0xec, 0x54, 0x60, 0x66, 0x58, 0xa3, 0xb4, 0x2c, 0xdb, - 0x76, 0x65, 0xf3, 0xc4, 0xac, 0x55, 0x81, 0xf3, 0x04, 0x48, 0x39, 0xce, 0x9d, 0x5d, 0x8f, 0xda, 0xae, 0x72, 0x76, - 0x70, 0x10, 0xbb, 0x4a, 0x34, 0x2e, 0x7c, 0x7e, 0x75, 0x03, 0xf8, 0xde, 0x52, 0x8d, 0x29, 0x32, 0x13, 0x68, 0x34, - 0xb2, 0xc1, 0x9a, 0xee, 0x13, 0x12, 0xe7, 0x75, 0x28, 0xfa, 0xd1, 0x1b, 0x66, 0x70, 0x03, 0x00, 0x8d, 0x46, 0x79, - 0xdd, 0xbb, 0x01, 0xb1, 0xa7, 0x1a, 0xcb, 0xf5, 0x97, 0xb8, 0xb4, 0x26, 0x6a, 0x6d, 0xd9, 0x61, 0xf9, 0x51, 0x20, - 0x11, 0xe2, 0xae, 0xf0, 0xf3, 0x09, 0xb6, 0x86, 0x80, 0x72, 0x2f, 0x9c, 0x0d, 0x04, 0x36, 0x56, 0x5b, 0xae, 0x90, - 0x27, 0x6d, 0x1d, 0x94, 0xfa, 0x42, 0x70, 0xc1, 0x05, 0x85, 0x1a, 0x6b, 0x87, 0xe5, 0x4f, 0xd8, 0xb6, 0x39, 0x27, - 0x56, 0xc8, 0x69, 0xcb, 0xcc, 0x30, 0x0c, 0xc0, 0x3a, 0x25, 0x60, 0x9e, 0x93, 0x97, 0xdf, 0x46, 0xfd, 0x87, 0x01, - 0xea, 0x3f, 0x22, 0x2c, 0xd8, 0x06, 0x56, 0x57, 0x92, 0x48, 0xa7, 0xa0, 0x50, 0x3e, 0xeb, 0xf1, 0x9c, 0x80, 0x36, - 0xae, 0x0e, 0xd5, 0xda, 0x15, 0xe5, 0x37, 0x28, 0x4b, 0xb8, 0x53, 0x8c, 0x3e, 0x13, 0xfb, 0xfb, 0xe4, 0xb8, 0xba, - 0xa0, 0x83, 0xae, 0x77, 0x29, 0x07, 0x43, 0x52, 0xf8, 0xf0, 0xf7, 0xef, 0xdf, 0xad, 0x3e, 0x9e, 0x6f, 0xef, 0xe0, - 0xc0, 0xac, 0x14, 0x66, 0x1d, 0x6c, 0xe0, 0xba, 0x91, 0x29, 0xf4, 0x5f, 0xde, 0x89, 0xd7, 0xa9, 0xd0, 0xc6, 0x66, - 0xf4, 0xc7, 0x21, 0x8c, 0xb6, 0xdd, 0x36, 0x25, 0x58, 0xd0, 0x2c, 0xd0, 0x25, 0x6b, 0xdc, 0x4a, 0x8b, 0x6f, 0x90, - 0x91, 0x87, 0xa6, 0x00, 0x13, 0xa3, 0xdd, 0xd9, 0x8f, 0xd6, 0x0e, 0x4f, 0xec, 0xd0, 0xd0, 0xd2, 0x10, 0x42, 0x8b, - 0xf7, 0x80, 0x39, 0xf6, 0x88, 0x00, 0x10, 0xbd, 0x34, 0x90, 0xaa, 0x40, 0x16, 0x45, 0x95, 0x22, 0xff, 0xf9, 0x3e, - 0x21, 0x2f, 0x2b, 0x45, 0xe6, 0xdb, 0xca, 0x98, 0x0b, 0x10, 0x03, 0xa5, 0x70, 0x91, 0x50, 0x26, 0xd8, 0xcb, 0xd0, - 0x0f, 0xda, 0x97, 0x37, 0xd2, 0x66, 0x52, 0x71, 0xe3, 0xc1, 0x4d, 0xa9, 0x51, 0xf1, 0xd9, 0x7c, 0x0f, 0x89, 0x8d, - 0xdc, 0x7b, 0x90, 0xcb, 0xa8, 0x19, 0x24, 0x7c, 0xbf, 0x33, 0xa5, 0x7d, 0xbb, 0xeb, 0xcf, 0x9b, 0x16, 0x31, 0x1b, - 0xeb, 0x92, 0x70, 0xa1, 0x58, 0xa1, 0x1f, 0xb1, 0xb1, 0x2c, 0xe0, 0xfe, 0xa3, 0x04, 0x0b, 0x5a, 0xdf, 0x0b, 0x74, - 0x80, 0x66, 0x82, 0xc1, 0xa5, 0xc3, 0xc6, 0x0c, 0xcd, 0xaf, 0xcf, 0xe6, 0x0e, 0xfc, 0x7a, 0xb3, 0xd6, 0xcb, 0x83, - 0x83, 0x2f, 0xac, 0x02, 0x94, 0x1b, 0xa6, 0x19, 0x46, 0x40, 0xbc, 0x2c, 0x97, 0xe3, 0x6e, 0x86, 0xef, 0xc5, 0x95, - 0xca, 0xc0, 0x13, 0x8e, 0x90, 0x08, 0x3d, 0x27, 0x7a, 0x3d, 0xd9, 0xa4, 0xf7, 0x4e, 0x9b, 0x21, 0x42, 0xb1, 0x06, - 0xc8, 0x3d, 0xc8, 0xe5, 0x56, 0xc9, 0xa4, 0x2a, 0x5b, 0xdb, 0x72, 0x10, 0x8f, 0x01, 0x5c, 0xb1, 0x11, 0x52, 0x02, - 0x34, 0xdc, 0x2d, 0xb4, 0x3c, 0x97, 0xc0, 0xfe, 0x63, 0x95, 0x80, 0x48, 0x8b, 0x6a, 0x1b, 0x17, 0x21, 0x6c, 0x4d, - 0x7d, 0x02, 0xe3, 0x84, 0x87, 0xcf, 0x77, 0x69, 0xa8, 0x3d, 0x6a, 0x33, 0x73, 0x06, 0x41, 0x09, 0x89, 0xca, 0x0a, - 0xc9, 0x97, 0x58, 0x38, 0x6e, 0xce, 0xdf, 0xc3, 0x01, 0x29, 0x56, 0x34, 0xb6, 0x77, 0x5b, 0x70, 0x7c, 0x14, 0xc9, - 0x22, 0xae, 0x75, 0xdd, 0x2d, 0x4c, 0x35, 0xec, 0x40, 0x47, 0x43, 0x38, 0x15, 0xe6, 0x9e, 0xf0, 0x71, 0x45, 0x52, - 0x7f, 0xb6, 0x26, 0xda, 0xda, 0x13, 0xc3, 0xca, 0x34, 0x25, 0x98, 0xff, 0xcf, 0xd6, 0xea, 0xba, 0x2c, 0x84, 0x99, - 0x19, 0xc6, 0x8d, 0x5d, 0x05, 0xb6, 0x06, 0x1c, 0x5b, 0xfe, 0x2d, 0x83, 0x45, 0xf5, 0x4a, 0x71, 0xd3, 0x69, 0xc0, - 0x04, 0xbc, 0x05, 0xeb, 0x99, 0xcd, 0xad, 0xff, 0xdc, 0x1c, 0x8c, 0x02, 0xab, 0x1a, 0x81, 0x97, 0x86, 0xc0, 0x23, - 0x60, 0xdc, 0xbc, 0x69, 0x79, 0xcf, 0x19, 0xd1, 0x08, 0x7f, 0xe2, 0x39, 0x3c, 0xb3, 0x2c, 0xf7, 0xd6, 0xc7, 0xc6, - 0x8a, 0xa4, 0x82, 0x80, 0x6d, 0x11, 0x76, 0x44, 0x5e, 0x22, 0xac, 0x1a, 0x8d, 0xae, 0xba, 0x60, 0x95, 0x56, 0xa5, - 0x1a, 0xa6, 0x80, 0x5b, 0x62, 0xc0, 0xfb, 0xda, 0x89, 0x0a, 0x86, 0x04, 0xde, 0xfa, 0x5b, 0x81, 0xfa, 0xfe, 0xe1, - 0xdb, 0x38, 0xa4, 0x6f, 0x61, 0xd9, 0xf2, 0x22, 0x16, 0xa6, 0x14, 0x57, 0x77, 0x38, 0x6f, 0xbe, 0x6f, 0x36, 0x02, - 0xe3, 0xde, 0x6f, 0x63, 0xb0, 0x71, 0x43, 0x5d, 0x6d, 0x49, 0x43, 0xb9, 0x09, 0xbb, 0xa8, 0xb2, 0x77, 0x0c, 0x3b, - 0xeb, 0xea, 0x4a, 0xda, 0xd5, 0x44, 0xad, 0xd7, 0x8a, 0x55, 0x46, 0x03, 0x1b, 0x86, 0x9d, 0xe6, 0x98, 0xd9, 0x56, - 0xe0, 0x3f, 0x9e, 0x13, 0x8d, 0x03, 0x64, 0x7d, 0xf3, 0xad, 0xeb, 0x94, 0x6a, 0x98, 0xb0, 0xbd, 0xdd, 0xf9, 0xf8, - 0x98, 0xef, 0x3a, 0x1f, 0xb1, 0x74, 0x5b, 0xdf, 0x9c, 0x8d, 0xed, 0x7f, 0xe3, 0x6c, 0x74, 0x6a, 0x7b, 0x7f, 0x3c, - 0x02, 0x77, 0x52, 0x3b, 0x1e, 0xeb, 0x6b, 0x4a, 0x24, 0x16, 0x6e, 0x39, 0x2e, 0x3b, 0xab, 0x95, 0xe8, 0xb7, 0x40, - 0xed, 0x14, 0x45, 0xf0, 0xb3, 0x6d, 0x7f, 0x06, 0x24, 0xd9, 0xea, 0x90, 0x63, 0x51, 0x8a, 0x32, 0x28, 0x01, 0x03, - 0xea, 0xd8, 0xd8, 0x7a, 0x19, 0xc4, 0x76, 0x38, 0xe4, 0xb0, 0x9c, 0x88, 0xf2, 0xea, 0x0a, 0x46, 0x6c, 0x8e, 0x0d, - 0x27, 0x60, 0xc6, 0x3b, 0xad, 0x0a, 0xbd, 0xf8, 0xf9, 0xaf, 0x99, 0xd3, 0xda, 0x11, 0x63, 0x39, 0x89, 0x9a, 0x15, - 0x83, 0x1b, 0x81, 0x63, 0x18, 0xf7, 0x8d, 0x84, 0x5a, 0x9d, 0xea, 0xa8, 0x76, 0x24, 0xe1, 0x16, 0xa8, 0xdd, 0xf6, - 0xcd, 0xb9, 0xb4, 0x5a, 0xed, 0x3c, 0x58, 0x70, 0x11, 0xe0, 0xf6, 0x73, 0xa2, 0x6b, 0x24, 0x85, 0x12, 0x27, 0x41, - 0xe1, 0xdc, 0xa0, 0xaa, 0x26, 0xb2, 0xdf, 0x1a, 0x00, 0x4f, 0xda, 0xcd, 0x2e, 0x64, 0x25, 0x24, 0x67, 0x8d, 0x06, - 0xca, 0xcb, 0x8e, 0x69, 0x5f, 0x34, 0xb2, 0x01, 0x66, 0x38, 0xb3, 0x02, 0x0b, 0x9c, 0x5e, 0x71, 0x5e, 0x75, 0xdd, - 0xcf, 0x06, 0x08, 0x17, 0xab, 0x55, 0x6c, 0x87, 0x96, 0xa3, 0xd5, 0x2a, 0x0f, 0x87, 0x66, 0xf2, 0xa1, 0xe2, 0xcb, - 0x9e, 0x26, 0x2f, 0xcd, 0x79, 0xf8, 0x12, 0x06, 0xd9, 0x20, 0x71, 0xee, 0x54, 0x82, 0x39, 0x68, 0xae, 0x1a, 0xb2, - 0x9f, 0x35, 0xda, 0x83, 0x80, 0x86, 0xf5, 0xb3, 0x01, 0xc9, 0xd7, 0x60, 0x39, 0xab, 0xdc, 0x81, 0xf9, 0x37, 0x1c, - 0x6c, 0x7f, 0x9b, 0x73, 0xc6, 0x36, 0x18, 0xae, 0xc9, 0xa6, 0xca, 0xa0, 0xc4, 0x2b, 0xb7, 0xb8, 0xbe, 0x5c, 0xcd, - 0xc0, 0xa2, 0x2c, 0x84, 0xdd, 0x35, 0x73, 0x0f, 0x84, 0xff, 0x12, 0xdb, 0x25, 0x2d, 0x8d, 0xb8, 0x37, 0x10, 0xdf, - 0xdb, 0x6e, 0x27, 0x49, 0x42, 0x8b, 0x89, 0xb9, 0x12, 0xf1, 0x37, 0xbc, 0x66, 0x0f, 0x1c, 0xbb, 0x71, 0x06, 0x3d, - 0xf7, 0xcb, 0xce, 0x06, 0xc4, 0x8e, 0xdf, 0x33, 0x3b, 0xde, 0x71, 0xa5, 0xa0, 0xbb, 0x75, 0x11, 0x76, 0x30, 0xf4, - 0x7f, 0x79, 0x30, 0x27, 0x6e, 0x30, 0x16, 0x4d, 0x36, 0xe0, 0xf6, 0x0d, 0x78, 0x14, 0x74, 0x03, 0x6e, 0xdf, 0x86, - 0xaf, 0x87, 0x56, 0xf6, 0xcd, 0x01, 0x06, 0x64, 0xc2, 0x8e, 0xb4, 0x4a, 0x08, 0x86, 0x79, 0xba, 0xc9, 0x91, 0x59, - 0xb2, 0x0a, 0x87, 0xab, 0x26, 0xb1, 0xd8, 0xd8, 0x0b, 0x15, 0x93, 0x1a, 0x08, 0xc6, 0x22, 0x7d, 0x89, 0x42, 0xa5, - 0x41, 0xdd, 0x38, 0x06, 0xb0, 0xca, 0x69, 0xeb, 0x5f, 0x1e, 0x1c, 0x80, 0xd0, 0x00, 0xac, 0x5d, 0x92, 0xd1, 0xb9, - 0x5e, 0x14, 0xc0, 0x5f, 0x29, 0xff, 0x1b, 0x92, 0xc1, 0xed, 0xc4, 0xa4, 0xc1, 0x0f, 0x48, 0x98, 0x53, 0xa5, 0xf8, - 0x17, 0x9b, 0xe6, 0x7e, 0xe3, 0x82, 0x78, 0x8c, 0x56, 0x96, 0x53, 0x94, 0xa8, 0x2b, 0x1d, 0xba, 0xd6, 0x21, 0xf7, - 0xf4, 0x0b, 0x13, 0xfa, 0x25, 0x57, 0x9a, 0x09, 0x00, 0x40, 0x85, 0x78, 0x30, 0x25, 0x85, 0x60, 0xeb, 0xd6, 0x6a, - 0xd1, 0xd1, 0xe8, 0xbb, 0x55, 0x74, 0x9d, 0x2d, 0x9a, 0x52, 0x31, 0xca, 0x6d, 0x27, 0xa1, 0xcd, 0xa4, 0xb7, 0x13, - 0x2d, 0x4b, 0x86, 0x16, 0x3b, 0x15, 0xfb, 0x61, 0x68, 0x7d, 0x2c, 0x88, 0x3f, 0x17, 0xfc, 0x59, 0xfa, 0x5d, 0x3e, - 0x06, 0xae, 0xd4, 0xbf, 0xb1, 0x0a, 0xe1, 0x4c, 0xb0, 0x0e, 0xc8, 0x6b, 0x52, 0x1f, 0xa7, 0x47, 0x9d, 0x7c, 0x4b, - 0xb9, 0x50, 0x1a, 0x85, 0x6d, 0x9c, 0x14, 0x06, 0x53, 0xce, 0xbe, 0x2d, 0x71, 0xfd, 0xea, 0x8f, 0x11, 0x7f, 0x74, - 0x88, 0x7f, 0x97, 0x4a, 0xa3, 0x65, 0x89, 0x60, 0xc8, 0xef, 0x48, 0xad, 0xe0, 0x2a, 0x36, 0xe7, 0xfa, 0xb9, 0x9e, - 0xe5, 0x1b, 0x9e, 0x38, 0x5d, 0xad, 0x4a, 0xa9, 0x40, 0xc5, 0x37, 0x0c, 0x3f, 0x61, 0x70, 0x6f, 0xfc, 0x8c, 0x07, - 0x55, 0xb6, 0xef, 0x8b, 0x9f, 0x05, 0xf7, 0xc5, 0xcf, 0x78, 0xba, 0x5d, 0x34, 0xb8, 0x27, 0xee, 0x24, 0xe7, 0x49, - 0x2b, 0xf2, 0x7c, 0xd4, 0x94, 0x56, 0xfe, 0x95, 0x76, 0x6b, 0xe0, 0xca, 0x26, 0x0e, 0x8c, 0xf3, 0xea, 0x22, 0x14, - 0x73, 0xe6, 0x8c, 0x96, 0xc3, 0xff, 0xd6, 0x3a, 0xb9, 0x93, 0x47, 0x5a, 0x29, 0xe4, 0x0d, 0x2d, 0xf4, 0x3d, 0xd8, - 0x70, 0xc5, 0x96, 0x0f, 0x20, 0x25, 0xa0, 0x6c, 0xfb, 0xf7, 0xba, 0x08, 0xc4, 0x71, 0x65, 0x9d, 0x8f, 0xc2, 0xf6, - 0x49, 0x51, 0x72, 0x75, 0x75, 0x21, 0xe4, 0xd6, 0x68, 0x09, 0x10, 0xa6, 0xde, 0x35, 0x8f, 0x39, 0x9a, 0xcc, 0xd2, - 0xe5, 0xba, 0x54, 0x1d, 0x14, 0x96, 0xab, 0xe3, 0x08, 0x17, 0x6b, 0x73, 0x83, 0xfe, 0x8a, 0xe3, 0xbf, 0xb9, 0xa3, - 0x91, 0x9f, 0x4a, 0x0a, 0xf4, 0x68, 0xb7, 0xaf, 0xcd, 0x0e, 0x12, 0x69, 0xe7, 0x50, 0x5a, 0x0a, 0x00, 0x56, 0x1b, - 0x7c, 0x5d, 0x7b, 0x9c, 0x7a, 0x22, 0xdd, 0x6c, 0xbe, 0x69, 0x08, 0x8b, 0x59, 0x69, 0xc1, 0x63, 0xba, 0xd9, 0x61, - 0x39, 0xea, 0x65, 0x71, 0x5d, 0xee, 0xb1, 0x5a, 0xbf, 0xe8, 0x1b, 0xa0, 0xac, 0x0c, 0xd1, 0x56, 0xab, 0xb8, 0x0e, - 0x6f, 0x22, 0x82, 0x6b, 0x10, 0x84, 0x45, 0x60, 0xc0, 0x51, 0x63, 0xbc, 0x6d, 0x9d, 0x18, 0x6d, 0xda, 0x2f, 0x79, - 0xd6, 0xbd, 0x36, 0x8e, 0x50, 0xd1, 0x60, 0xab, 0x87, 0x9a, 0x07, 0x6c, 0x67, 0x57, 0x76, 0x14, 0x40, 0x68, 0x4a, - 0xbd, 0x71, 0x6e, 0x65, 0x45, 0xbb, 0x03, 0xbe, 0xe8, 0x3b, 0xe6, 0xb9, 0x0e, 0x74, 0xdb, 0xf9, 0x81, 0x6d, 0xd3, - 0x13, 0xf9, 0x2d, 0xdb, 0xa6, 0x1a, 0x27, 0xbc, 0xdf, 0x42, 0xdf, 0x37, 0x84, 0xb5, 0x7d, 0xed, 0x2e, 0xf2, 0xbf, - 0xd0, 0x5d, 0x1b, 0xd0, 0xd3, 0x82, 0xd9, 0xd3, 0x98, 0x0f, 0x7a, 0xbd, 0xfe, 0x54, 0xfa, 0x2f, 0x18, 0x5b, 0xa1, - 0x4f, 0x76, 0x17, 0x38, 0xb1, 0xd2, 0x38, 0x04, 0xc7, 0xaf, 0x38, 0x99, 0xe4, 0x72, 0x48, 0xf3, 0x77, 0xd0, 0x63, - 0x95, 0xfb, 0xfc, 0x6e, 0x54, 0x50, 0xcd, 0x1c, 0xad, 0xa9, 0x46, 0xf1, 0x8a, 0x07, 0xc3, 0x78, 0xc5, 0x2d, 0xe5, - 0xae, 0x5a, 0xc0, 0xcb, 0x97, 0x65, 0x13, 0xe9, 0xa7, 0x75, 0x29, 0x83, 0xa9, 0xdd, 0xbd, 0x6c, 0x92, 0x34, 0x56, - 0x92, 0x34, 0xa6, 0xe2, 0xcd, 0xa6, 0xe2, 0xf8, 0xef, 0x6f, 0x0c, 0x76, 0x9b, 0xcc, 0xfd, 0x1d, 0x90, 0xb9, 0xbf, - 0x79, 0xfa, 0xdd, 0x5a, 0x01, 0xc5, 0x3b, 0x4e, 0x8e, 0x8d, 0x65, 0x8c, 0x1d, 0xf5, 0x5b, 0x0d, 0x06, 0x0d, 0x9a, - 0x5c, 0x06, 0xde, 0x0e, 0xd5, 0xe9, 0xe5, 0xed, 0x8f, 0xe2, 0x6c, 0xa1, 0xb4, 0x9c, 0xb9, 0x46, 0x95, 0xf3, 0x71, - 0x32, 0x99, 0xa0, 0xc0, 0x36, 0x77, 0xf8, 0x69, 0xdd, 0x8d, 0x6c, 0xf9, 0x99, 0x8b, 0x51, 0xaa, 0xb0, 0x3b, 0x5b, - 0x54, 0x2a, 0xd7, 0xc4, 0x9b, 0x39, 0x6f, 0xe7, 0xe1, 0x31, 0x17, 0x5c, 0x4d, 0x59, 0x11, 0x17, 0x68, 0xf9, 0xad, - 0xce, 0x0a, 0xb8, 0xcd, 0xb1, 0x9d, 0xe1, 0x51, 0x69, 0x39, 0xa0, 0x13, 0x68, 0x0d, 0x74, 0x46, 0x33, 0xa6, 0xa7, - 0x72, 0x04, 0x86, 0x2f, 0xc9, 0xa8, 0x74, 0xa7, 0x3a, 0x38, 0xd8, 0x8f, 0x23, 0xa3, 0xbf, 0x00, 0x1f, 0xf4, 0x30, - 0x07, 0xf5, 0x96, 0xe0, 0x18, 0x54, 0x75, 0xcd, 0xd0, 0x92, 0x6d, 0xfa, 0xd0, 0xe8, 0xe4, 0x33, 0xbb, 0xc3, 0x1c, - 0xad, 0xd7, 0xa9, 0x1d, 0x75, 0x34, 0xe6, 0x2c, 0x1f, 0x45, 0xf8, 0x33, 0xbb, 0x4b, 0x4b, 0xb7, 0x75, 0xe3, 0x65, - 0x6d, 0x16, 0x31, 0x92, 0x37, 0x22, 0xc2, 0x55, 0x27, 0xe9, 0x72, 0x8d, 0x65, 0xc1, 0x27, 0x80, 0xa3, 0xbf, 0xb0, - 0xbb, 0xd4, 0xb5, 0x17, 0xb8, 0x0a, 0xa2, 0xa5, 0x07, 0x7d, 0x12, 0x24, 0x87, 0xcb, 0xe0, 0x04, 0x8e, 0xbe, 0xa9, - 0x3b, 0x20, 0xb5, 0x72, 0x95, 0x08, 0x89, 0xd0, 0xfa, 0xdf, 0x9d, 0x0a, 0x5e, 0x84, 0xe7, 0x9c, 0xae, 0x59, 0xdc, - 0x6e, 0x54, 0x62, 0x50, 0xa1, 0xb2, 0x20, 0xf9, 0x18, 0x73, 0xbf, 0xfb, 0x9c, 0xf7, 0x43, 0xa0, 0x33, 0x5b, 0x50, - 0xd7, 0x68, 0x3a, 0x32, 0xbf, 0x50, 0x75, 0x07, 0x35, 0xd3, 0x55, 0xc5, 0xbd, 0x8f, 0x31, 0x00, 0x1e, 0xac, 0x65, - 0xa8, 0x71, 0x08, 0x5d, 0x7b, 0x33, 0xd5, 0x31, 0x25, 0xf1, 0xd2, 0xcf, 0x21, 0xe5, 0x21, 0x18, 0xf5, 0x1a, 0xd0, - 0xd0, 0x21, 0x98, 0xb5, 0x3c, 0xe4, 0xe3, 0x58, 0x6c, 0x9d, 0xa1, 0xd2, 0x9c, 0xa1, 0x49, 0x00, 0xf2, 0x6f, 0x9c, - 0x99, 0xcc, 0x40, 0xc3, 0xf0, 0x96, 0xe6, 0x00, 0x74, 0xab, 0xeb, 0x70, 0x28, 0x5c, 0xd1, 0xd2, 0x79, 0xcf, 0x2e, - 0xba, 0xac, 0x0d, 0x2b, 0x36, 0xed, 0xa0, 0x75, 0x0a, 0x53, 0x62, 0xb6, 0xc0, 0xda, 0xeb, 0x7d, 0xb8, 0xb7, 0xab, - 0x8d, 0x8b, 0xc4, 0x4f, 0x8b, 0x78, 0x98, 0xc4, 0x14, 0x2d, 0x79, 0x4c, 0xb1, 0x04, 0x3b, 0xc8, 0x62, 0x5d, 0x8e, - 0x9f, 0x85, 0xcb, 0x51, 0xb3, 0x92, 0xde, 0xed, 0x60, 0x08, 0x5c, 0xbe, 0x06, 0xdb, 0x50, 0xcc, 0x3d, 0x61, 0xe1, - 0xb1, 0xf1, 0xf4, 0x0b, 0xd6, 0x6d, 0x6e, 0x17, 0xc4, 0xaf, 0xc0, 0x98, 0xc6, 0xcb, 0x60, 0x16, 0xa1, 0x53, 0xb9, - 0x73, 0x38, 0x74, 0xd7, 0x84, 0x95, 0xf1, 0x6a, 0xac, 0xc8, 0xc6, 0xd1, 0xf3, 0x7d, 0x1b, 0xcf, 0x7f, 0x16, 0xac, - 0xb8, 0xbb, 0x62, 0x60, 0x63, 0x2d, 0xc1, 0xdd, 0xb8, 0x5a, 0x86, 0xca, 0x40, 0xbe, 0x27, 0x0d, 0xeb, 0xb2, 0xc6, - 0xdf, 0x8d, 0x8a, 0xb1, 0x36, 0xf7, 0x94, 0x81, 0xb6, 0xc6, 0x6e, 0x17, 0xf6, 0x4d, 0xd7, 0x4d, 0xd6, 0x35, 0x8a, - 0xb8, 0x0a, 0xd2, 0xee, 0x6e, 0x01, 0x17, 0xa1, 0x3f, 0x6c, 0x5f, 0x0d, 0x36, 0x55, 0x37, 0x90, 0x04, 0xd7, 0x7e, - 0xf2, 0xdb, 0x53, 0xdd, 0x65, 0xad, 0xfb, 0xed, 0xa9, 0xd6, 0x2e, 0x0b, 0x8d, 0x21, 0x11, 0x76, 0xfd, 0x94, 0xfe, - 0xd3, 0x62, 0xbd, 0x46, 0x6b, 0x18, 0xde, 0x7b, 0xde, 0x8d, 0xe3, 0xf7, 0xde, 0x42, 0x31, 0x81, 0x8b, 0xdc, 0xab, - 0x5c, 0x7a, 0x42, 0x5e, 0x8d, 0xe0, 0x3d, 0xdf, 0x1a, 0xc2, 0x7b, 0x1e, 0x38, 0xbd, 0x82, 0xd4, 0x34, 0x11, 0x6c, - 0xe4, 0xe9, 0x27, 0xb2, 0x48, 0x68, 0xf8, 0xb8, 0xd7, 0x9c, 0x70, 0xfd, 0x57, 0x0a, 0xfc, 0x17, 0x1e, 0x2e, 0xb4, - 0x96, 0x02, 0x73, 0x31, 0x5f, 0x68, 0xac, 0xcc, 0xe8, 0x97, 0x63, 0x29, 0x74, 0x73, 0x4c, 0x67, 0x3c, 0xbf, 0x4b, - 0x17, 0xbc, 0x39, 0x93, 0x42, 0xaa, 0x39, 0xcd, 0x18, 0x56, 0x77, 0x4a, 0xb3, 0x59, 0x73, 0xc1, 0xf1, 0x73, 0x96, - 0x7f, 0x61, 0x9a, 0x67, 0x14, 0xbf, 0x95, 0x43, 0xa9, 0x25, 0x7e, 0x7d, 0x7b, 0x37, 0x61, 0x02, 0xff, 0x3e, 0x5c, - 0x08, 0xbd, 0xc0, 0x8a, 0x0a, 0xd5, 0x54, 0xac, 0xe0, 0xe3, 0x6e, 0xb3, 0x39, 0x2f, 0xf8, 0x8c, 0x16, 0x77, 0xcd, - 0x4c, 0xe6, 0xb2, 0x48, 0xff, 0xab, 0x75, 0x4c, 0x1f, 0x8c, 0x4f, 0xba, 0xba, 0xa0, 0x42, 0x71, 0x58, 0x98, 0x94, - 0xe6, 0xf9, 0xde, 0xf1, 0x69, 0x6b, 0xa6, 0xf6, 0xed, 0x85, 0x1f, 0x15, 0x7a, 0xfd, 0x17, 0xfe, 0x20, 0x61, 0x94, - 0xc9, 0x50, 0x0b, 0x37, 0xc8, 0x65, 0xb6, 0x28, 0x94, 0x2c, 0xd2, 0xb9, 0xe4, 0x42, 0xb3, 0xa2, 0x3b, 0x94, 0xc5, - 0x88, 0x15, 0xcd, 0x82, 0x8e, 0xf8, 0x42, 0xa5, 0x27, 0xf3, 0xdb, 0x6e, 0xbd, 0x07, 0x9b, 0x9f, 0x0a, 0x29, 0x58, - 0x17, 0xf8, 0x8d, 0x49, 0x21, 0x17, 0x62, 0xe4, 0x86, 0xb1, 0x10, 0x8a, 0xe9, 0xee, 0x9c, 0x8e, 0xc0, 0x0e, 0x38, - 0x3d, 0x9f, 0xdf, 0x76, 0xcd, 0xac, 0x6f, 0x18, 0x9f, 0x4c, 0x75, 0x7a, 0xda, 0x6a, 0xd9, 0x6f, 0xc5, 0xbf, 0xb2, - 0xb4, 0xdd, 0x49, 0x3a, 0xa7, 0xf3, 0x5b, 0xe0, 0xe0, 0x35, 0x2b, 0x9a, 0x00, 0x0b, 0xa8, 0xd4, 0x4e, 0x5a, 0x0f, - 0x8e, 0xef, 0x43, 0x06, 0xd8, 0x38, 0x34, 0xcd, 0x84, 0xc0, 0xd8, 0x3d, 0x5d, 0xcc, 0xe7, 0xac, 0x00, 0x2f, 0xfa, - 0xee, 0x8c, 0x16, 0x13, 0x2e, 0x9a, 0x85, 0x69, 0xb4, 0x79, 0x3e, 0xbf, 0x5d, 0xc3, 0x7c, 0x52, 0x6b, 0xb6, 0xea, - 0xa6, 0xe5, 0xbe, 0x96, 0xc1, 0x10, 0x4d, 0x4c, 0x9a, 0xb4, 0x98, 0x0c, 0x69, 0xdc, 0xee, 0xdc, 0xc7, 0xfe, 0x7f, - 0x49, 0x07, 0x05, 0x60, 0x6b, 0x8e, 0x16, 0x85, 0xb9, 0x45, 0x4d, 0xdb, 0xca, 0x36, 0x3b, 0x95, 0x5f, 0x58, 0xe1, - 0x5b, 0x35, 0x1f, 0xcb, 0xad, 0x79, 0xff, 0x27, 0x8d, 0xfe, 0x85, 0x27, 0x14, 0xd6, 0xc0, 0x20, 0x47, 0xdf, 0xc8, - 0x83, 0x30, 0xd3, 0xc1, 0xf2, 0x86, 0x8f, 0xf4, 0x34, 0x6d, 0xb7, 0x5a, 0x3f, 0x54, 0x2b, 0xd6, 0x9d, 0x5a, 0xd0, - 0xb5, 0x0b, 0x36, 0xab, 0xad, 0xe3, 0x8c, 0x96, 0xd8, 0xb6, 0x9c, 0x4b, 0xb7, 0xe4, 0x05, 0xcb, 0x4d, 0x34, 0x99, - 0xb5, 0x43, 0xb9, 0xad, 0x71, 0x72, 0x31, 0x65, 0x05, 0xd7, 0xdd, 0xfa, 0x57, 0xd5, 0xf1, 0xf6, 0xea, 0xaf, 0xad, - 0x1c, 0xba, 0xb4, 0x35, 0xdc, 0xa5, 0xe7, 0x63, 0xf8, 0xd8, 0x5e, 0xfd, 0x2f, 0xb4, 0x88, 0x37, 0x10, 0x13, 0x87, - 0x35, 0xd0, 0x3a, 0x98, 0x73, 0x01, 0x26, 0x99, 0x03, 0xfc, 0x0d, 0x28, 0x64, 0x34, 0xcf, 0x62, 0x18, 0xd1, 0x5e, - 0x73, 0xef, 0xb8, 0x60, 0x33, 0xe4, 0x01, 0x91, 0xdc, 0x3f, 0x2d, 0xd8, 0x6c, 0x9d, 0x98, 0xea, 0x4b, 0x83, 0x22, - 0x34, 0xe7, 0x13, 0x91, 0x66, 0x0c, 0xd0, 0x77, 0x9d, 0x30, 0xa1, 0xb9, 0xbe, 0x6b, 0x16, 0xf2, 0x66, 0x39, 0xe2, - 0x6a, 0x9e, 0xd3, 0xbb, 0x74, 0x9c, 0xb3, 0xdb, 0xae, 0x29, 0xd5, 0xe4, 0x9a, 0xcd, 0x94, 0x2b, 0xdb, 0x85, 0xf4, - 0xe6, 0xc8, 0x9a, 0x4d, 0x00, 0xf4, 0xe4, 0xcd, 0xe6, 0xfe, 0x49, 0x8e, 0xd5, 0x1e, 0xa3, 0x8a, 0x35, 0xe5, 0x42, - 0xef, 0xb5, 0x54, 0x77, 0xc6, 0x45, 0xd3, 0x0d, 0xe4, 0xa4, 0x35, 0xbf, 0xed, 0x6e, 0x43, 0x3e, 0xe8, 0x3f, 0x61, - 0xb7, 0x73, 0x2a, 0x46, 0x6c, 0xb4, 0x0c, 0xaa, 0x75, 0xa0, 0x5e, 0x58, 0x2a, 0x15, 0x7a, 0xda, 0x34, 0xb6, 0x5e, - 0x71, 0x47, 0xa0, 0x6f, 0xa0, 0xd6, 0x83, 0x16, 0xb6, 0xff, 0x9f, 0xb4, 0x51, 0x58, 0x79, 0x0f, 0xc2, 0x2e, 0xf1, - 0xf1, 0x5d, 0x13, 0xfe, 0x2e, 0xc1, 0xb7, 0x88, 0x67, 0x34, 0x77, 0x10, 0x99, 0xf1, 0xd1, 0x28, 0xaf, 0x8d, 0xe8, - 0x32, 0xe8, 0xac, 0x8d, 0x96, 0x30, 0xff, 0xb4, 0xb5, 0xd7, 0xda, 0x33, 0x73, 0x71, 0xdb, 0xfc, 0xe4, 0xe4, 0xfe, - 0xf1, 0x03, 0xd6, 0xcd, 0xb9, 0x60, 0xb5, 0xa9, 0x7e, 0x17, 0xd4, 0x61, 0xc3, 0x1d, 0xd7, 0x70, 0x7b, 0xaf, 0xbd, - 0x77, 0xd2, 0xfa, 0xc1, 0xef, 0xd6, 0x9c, 0x8d, 0x75, 0xda, 0x3e, 0x9b, 0xdf, 0xd6, 0xb7, 0xef, 0xb9, 0x6f, 0xfa, - 0xa6, 0xa0, 0xf3, 0x54, 0x48, 0xf8, 0xd3, 0x85, 0x4d, 0x36, 0xce, 0xe5, 0x4d, 0x3a, 0xe5, 0xa3, 0x11, 0x13, 0xb6, - 0x40, 0x99, 0xc8, 0xf2, 0x9c, 0xcf, 0x15, 0xb7, 0xab, 0xe1, 0x70, 0xf7, 0x74, 0x03, 0xaa, 0xe1, 0x80, 0x8e, 0x83, - 0x01, 0x9d, 0x56, 0x03, 0xaa, 0xfa, 0x0f, 0x47, 0xd8, 0xd9, 0x98, 0xab, 0x29, 0xd5, 0xad, 0x61, 0xd2, 0xdf, 0x0b, - 0xa5, 0x01, 0xe6, 0xde, 0x48, 0xc3, 0x50, 0xf1, 0xe6, 0x90, 0xe9, 0x1b, 0xc6, 0xc4, 0xb7, 0x07, 0x71, 0x99, 0x4a, - 0x91, 0xdf, 0xd9, 0xcf, 0x65, 0xd8, 0x25, 0x5d, 0x68, 0xb9, 0x4e, 0x86, 0x5c, 0xd0, 0xe2, 0xee, 0x5a, 0x31, 0xa1, - 0x64, 0x71, 0x2d, 0xc7, 0xe3, 0xe5, 0xb7, 0x48, 0xcb, 0x7d, 0xb4, 0x4e, 0x14, 0x17, 0x93, 0x9c, 0x59, 0xa2, 0x64, - 0x10, 0xc1, 0x11, 0x73, 0xdb, 0xae, 0x69, 0xb2, 0x36, 0xe8, 0x75, 0x92, 0xe5, 0x7c, 0x46, 0x35, 0x33, 0x70, 0x0e, - 0x48, 0x8d, 0x9b, 0x7c, 0xda, 0x6e, 0xcd, 0x6f, 0xf7, 0x5a, 0x7b, 0xf6, 0x4f, 0x55, 0x1a, 0xb6, 0x51, 0x50, 0xd8, - 0x37, 0xc9, 0x85, 0xc1, 0x0f, 0x03, 0x0e, 0xb3, 0x8b, 0xcc, 0xea, 0x99, 0xb5, 0x0b, 0x60, 0x07, 0xb3, 0xab, 0x35, - 0x75, 0xe9, 0xe8, 0x92, 0x6d, 0xf1, 0xb4, 0xf5, 0x43, 0x3d, 0x37, 0xa7, 0x43, 0x96, 0x2f, 0xed, 0x46, 0xf5, 0xc0, - 0x75, 0x5b, 0x35, 0x5c, 0xe6, 0x80, 0x64, 0x18, 0x10, 0x0d, 0xd2, 0xb4, 0x79, 0xc3, 0x86, 0x9f, 0xb9, 0xb6, 0x5b, - 0xa6, 0xa9, 0x6e, 0xc0, 0x79, 0xc7, 0x8c, 0x69, 0xce, 0x8a, 0xa5, 0x3f, 0x8e, 0x5a, 0x35, 0x02, 0x7a, 0x25, 0xcc, - 0x41, 0xa8, 0xe9, 0xb0, 0x09, 0xa1, 0xcc, 0x58, 0xb1, 0xdc, 0x35, 0xb9, 0xcd, 0x0d, 0x1f, 0x1e, 0x67, 0x27, 0xad, - 0x96, 0x3f, 0xea, 0x9a, 0xb6, 0x4e, 0xda, 0x4e, 0x4e, 0xd9, 0x6c, 0x17, 0xa9, 0xa9, 0xd3, 0xd5, 0x76, 0x67, 0x7e, - 0xbb, 0x67, 0xfe, 0x69, 0xed, 0xb5, 0xb6, 0xe9, 0xe8, 0xf6, 0x92, 0x1f, 0x23, 0x8f, 0xa4, 0x5a, 0xce, 0xd3, 0x36, - 0x9b, 0x75, 0x17, 0x0a, 0xce, 0x4c, 0x03, 0x4e, 0x73, 0x16, 0xaf, 0xcd, 0x4c, 0x00, 0x6a, 0x94, 0x0b, 0x38, 0xa2, - 0xec, 0x31, 0x0d, 0x7d, 0x28, 0x09, 0x36, 0xe5, 0x3b, 0x1b, 0xad, 0x0f, 0xab, 0xb5, 0x57, 0x0d, 0x0c, 0xfe, 0x59, - 0xff, 0x55, 0x31, 0xb9, 0x2f, 0x58, 0x20, 0x64, 0xf0, 0x46, 0x72, 0xba, 0x6a, 0x39, 0xc1, 0x62, 0xa4, 0x2b, 0x79, - 0xc7, 0xb8, 0x65, 0xcc, 0xe8, 0xad, 0xf5, 0xcf, 0x98, 0x71, 0x01, 0xd6, 0x5f, 0x08, 0xeb, 0xc0, 0x4e, 0x7e, 0x1a, - 0x36, 0x34, 0xd2, 0x31, 0x34, 0x7c, 0xd8, 0x49, 0x4e, 0x4f, 0x11, 0x6e, 0xe1, 0xce, 0xe9, 0x69, 0x20, 0xd8, 0x8c, - 0xf5, 0xae, 0xa2, 0xbb, 0x4a, 0xaa, 0x1d, 0x25, 0x8f, 0x4c, 0xa3, 0x47, 0xed, 0x56, 0x0b, 0x1b, 0x1f, 0xf4, 0xb2, - 0x30, 0x57, 0x3b, 0x9a, 0x6d, 0xb7, 0x5a, 0xd0, 0x2c, 0xfc, 0x71, 0xf3, 0xfa, 0x85, 0x2c, 0x5b, 0x69, 0x0b, 0xb7, - 0xd3, 0x36, 0xee, 0xa4, 0x1d, 0x7c, 0x9c, 0x1e, 0xe3, 0x93, 0xf4, 0x04, 0x9f, 0xa6, 0xa7, 0xf8, 0x2c, 0x3d, 0xc3, - 0xf7, 0xd3, 0xfb, 0xf8, 0x3c, 0x3d, 0xc7, 0x0f, 0xd2, 0x07, 0xf8, 0x61, 0xda, 0x6e, 0xe1, 0x47, 0x69, 0xbb, 0x8d, - 0x1f, 0xa7, 0xed, 0x0e, 0x7e, 0x92, 0xb6, 0x8f, 0xf1, 0xd3, 0xb4, 0x7d, 0x82, 0x9f, 0xa5, 0xed, 0x53, 0x4c, 0x21, - 0x77, 0x08, 0xb9, 0x19, 0xe4, 0x8e, 0x20, 0x97, 0x41, 0xee, 0x38, 0x6d, 0x9f, 0xae, 0xb1, 0xb2, 0x71, 0x2b, 0xa2, - 0x56, 0xbb, 0x73, 0x7c, 0x72, 0x7a, 0x76, 0xff, 0xfc, 0xc1, 0xc3, 0x47, 0x8f, 0x9f, 0x3c, 0x7d, 0x16, 0x0d, 0xf0, - 0xd0, 0xb8, 0x8f, 0x28, 0xd1, 0xe7, 0x07, 0xed, 0xd3, 0x01, 0xbe, 0xf6, 0x9f, 0x31, 0x3f, 0xe8, 0x9c, 0xb4, 0xd0, - 0xe5, 0xe5, 0xc9, 0xa0, 0x51, 0xe6, 0xbe, 0x37, 0x5e, 0x2b, 0x55, 0x16, 0x21, 0x24, 0x86, 0x1c, 0x84, 0xef, 0x4c, - 0xbd, 0xf7, 0x2c, 0xe6, 0x49, 0x81, 0x0e, 0x0e, 0xcc, 0x8f, 0x89, 0xff, 0x31, 0xf4, 0x3f, 0x68, 0xb0, 0x48, 0xb7, - 0x34, 0x76, 0x6e, 0xcb, 0xba, 0x74, 0x1a, 0x28, 0xed, 0x71, 0xf6, 0xb8, 0xb3, 0x8c, 0xff, 0xaf, 0xc8, 0x5a, 0xbe, - 0x90, 0x13, 0xab, 0x5d, 0x3a, 0xed, 0x31, 0xb2, 0x2c, 0xd2, 0xce, 0xe9, 0xe9, 0xc1, 0x2f, 0x7d, 0xde, 0x6f, 0x0f, - 0x06, 0x87, 0xed, 0xfb, 0x78, 0x52, 0x26, 0x74, 0x6c, 0xc2, 0xb0, 0x4c, 0x38, 0xb6, 0x09, 0x34, 0xb5, 0xb5, 0x21, - 0xe9, 0xc4, 0x24, 0x41, 0x89, 0x75, 0x6a, 0xda, 0xbe, 0x6f, 0xdb, 0x7e, 0x00, 0x26, 0x59, 0xa6, 0x79, 0xd7, 0xf4, - 0xc5, 0xc5, 0xc9, 0xca, 0x35, 0x8a, 0x27, 0xa9, 0x6b, 0xcd, 0x27, 0x9e, 0x0c, 0x06, 0x78, 0x68, 0x12, 0x4f, 0xab, - 0xc4, 0xb3, 0xc1, 0xc0, 0x75, 0xf5, 0xc0, 0x74, 0x75, 0xbf, 0xca, 0x3a, 0x1f, 0x0c, 0x4c, 0x97, 0xc8, 0x39, 0xe0, - 0x2b, 0xbd, 0xf7, 0xa5, 0x54, 0x82, 0xf0, 0x8b, 0xce, 0xe9, 0x69, 0x0f, 0x30, 0xcc, 0x18, 0xd6, 0x7a, 0x18, 0xdd, - 0x04, 0x30, 0xba, 0x83, 0xdf, 0xbd, 0x21, 0x4d, 0xaf, 0x69, 0x09, 0xa4, 0x5e, 0xf4, 0x5f, 0x51, 0x43, 0x1b, 0x98, - 0x9b, 0x3f, 0x13, 0xfb, 0x67, 0x88, 0x1a, 0x5f, 0x28, 0x80, 0x1b, 0xd4, 0x3a, 0x5e, 0x2f, 0x6b, 0x7a, 0xfc, 0x4c, - 0xc1, 0x4f, 0x66, 0xaa, 0x72, 0xda, 0x5b, 0x4d, 0x6f, 0x86, 0xab, 0xa9, 0xfa, 0x82, 0xfe, 0x8c, 0xff, 0x54, 0x87, - 0x71, 0xbf, 0xd9, 0x48, 0xd8, 0x9f, 0x23, 0x70, 0xc8, 0xe9, 0xa5, 0x23, 0x36, 0x41, 0xbd, 0xfe, 0x9f, 0x0a, 0x0f, - 0x1a, 0x41, 0xc6, 0x0f, 0xdb, 0x29, 0xe0, 0xae, 0xb3, 0x99, 0x18, 0xff, 0x80, 0x7a, 0xa8, 0xf7, 0xa7, 0x3a, 0xfc, - 0x13, 0xdd, 0x3b, 0xaa, 0xe6, 0xf2, 0xbb, 0x74, 0x5b, 0xb8, 0x8a, 0xe1, 0x73, 0x58, 0x6e, 0x61, 0x86, 0xdb, 0x4d, - 0x06, 0x11, 0xcf, 0xc0, 0x9f, 0x9b, 0xc4, 0xb2, 0xc1, 0x8f, 0x8e, 0x5b, 0xe8, 0x87, 0x76, 0x07, 0x34, 0x14, 0x4d, - 0x71, 0xb8, 0xbd, 0xe9, 0x8b, 0xe6, 0x31, 0x7e, 0xd0, 0x2c, 0x70, 0x1b, 0xe1, 0x66, 0xdb, 0xab, 0x8e, 0xfb, 0x2a, - 0x6e, 0x21, 0xac, 0xe2, 0x73, 0xf8, 0xe7, 0x04, 0x0d, 0xaa, 0x0d, 0x79, 0x45, 0x37, 0x7b, 0x07, 0xe7, 0x53, 0x12, - 0xab, 0x06, 0x3f, 0x3a, 0x6b, 0xa1, 0x1f, 0xce, 0x4c, 0x47, 0xec, 0x50, 0xef, 0xe8, 0x4a, 0xe2, 0x93, 0xa6, 0x84, - 0x8e, 0x5a, 0x65, 0x3f, 0x22, 0x3e, 0x45, 0x58, 0xc4, 0xc7, 0xf0, 0x4f, 0x3b, 0xec, 0xe7, 0xd7, 0xad, 0x7e, 0xcc, - 0xbc, 0xdb, 0x38, 0x39, 0xb5, 0xbe, 0xac, 0xca, 0x5e, 0x2c, 0x37, 0xd8, 0x65, 0xdb, 0xdc, 0x88, 0xb5, 0x8f, 0xe0, - 0x03, 0x61, 0x7d, 0x48, 0x14, 0x66, 0x87, 0xe0, 0x04, 0x0b, 0xb6, 0x1f, 0xea, 0xe2, 0xb8, 0xab, 0x1a, 0x0d, 0x24, - 0xfa, 0x6a, 0x70, 0x48, 0xda, 0x4d, 0xdd, 0x64, 0x18, 0x7e, 0x37, 0x48, 0x19, 0x59, 0x4d, 0x54, 0xbd, 0x3e, 0x76, - 0xbd, 0xda, 0xeb, 0x73, 0x8f, 0x1d, 0x84, 0x10, 0xd5, 0x8b, 0x75, 0x93, 0xa1, 0x23, 0xd1, 0x88, 0xf5, 0x05, 0xeb, - 0x9d, 0xa5, 0x2d, 0x64, 0xb0, 0x53, 0xf5, 0x62, 0xd6, 0xe4, 0x90, 0xde, 0x49, 0x63, 0xde, 0xd4, 0xf0, 0xeb, 0x24, - 0x98, 0x85, 0x00, 0xbc, 0xab, 0x5c, 0x7a, 0x8a, 0xa3, 0xce, 0xe9, 0x29, 0x16, 0x84, 0x27, 0x13, 0xf3, 0x4b, 0x11, - 0x9e, 0x0c, 0xcd, 0x2f, 0x49, 0x4a, 0x78, 0xd9, 0xde, 0x71, 0x41, 0x82, 0x55, 0x35, 0x29, 0x14, 0x16, 0xb4, 0x40, - 0x47, 0x1d, 0x7f, 0xb7, 0x8e, 0xa7, 0x7e, 0x0e, 0xa0, 0x4b, 0x28, 0x8c, 0x59, 0xa5, 0x6c, 0x16, 0x38, 0x27, 0xf4, - 0x32, 0x39, 0xed, 0x4d, 0x8f, 0xe2, 0x4e, 0x53, 0x36, 0x0b, 0x94, 0x4e, 0x8f, 0x4c, 0x4d, 0x9c, 0x91, 0xc7, 0xd4, - 0xb6, 0x86, 0xa7, 0x70, 0x21, 0x9a, 0x91, 0xec, 0xf0, 0xac, 0xd5, 0x48, 0x4e, 0x11, 0xee, 0x67, 0xab, 0x16, 0xce, - 0x57, 0xab, 0x16, 0xa6, 0xc1, 0x32, 0x3c, 0x16, 0x1e, 0x20, 0xa5, 0xba, 0x6b, 0x33, 0xc0, 0x4d, 0x8f, 0xc7, 0x1a, - 0x2e, 0xf7, 0x35, 0xb8, 0xcc, 0x68, 0x70, 0xe6, 0x49, 0xb9, 0xbb, 0x55, 0x43, 0x26, 0xc4, 0xdf, 0x38, 0x54, 0x80, - 0xbd, 0x16, 0x7e, 0x5d, 0xbd, 0x79, 0xa6, 0x88, 0x7f, 0x97, 0xd8, 0xa6, 0x05, 0xc5, 0xe8, 0x76, 0xb1, 0x5f, 0xe9, - 0x56, 0xb1, 0x37, 0x3b, 0x8a, 0x5d, 0x6d, 0x17, 0xfb, 0x28, 0x03, 0x75, 0x1d, 0xff, 0xe1, 0xf8, 0xac, 0xd5, 0x38, - 0x06, 0x64, 0x3d, 0x3e, 0x6b, 0x55, 0x85, 0xee, 0xd1, 0x6a, 0xad, 0x34, 0xf9, 0x4c, 0xad, 0xc3, 0x02, 0xf7, 0x9e, - 0xd3, 0x66, 0xe1, 0xac, 0xdf, 0x76, 0xe9, 0xa4, 0xdd, 0x3f, 0x05, 0x83, 0x10, 0x61, 0xa8, 0x9d, 0xee, 0x9f, 0x0d, - 0x7a, 0x53, 0x16, 0x37, 0x20, 0x15, 0xa5, 0x63, 0xed, 0x7e, 0xa1, 0xf2, 0x5e, 0xf8, 0xa3, 0x84, 0xa4, 0xce, 0x00, - 0x61, 0x49, 0x1a, 0xba, 0x7f, 0x3c, 0x30, 0xe7, 0x5d, 0x01, 0xbf, 0x4f, 0xcc, 0xef, 0x52, 0x2b, 0xe3, 0xbc, 0x1a, - 0xa6, 0x37, 0xc3, 0xa8, 0x27, 0xc8, 0x6b, 0x1a, 0x1b, 0x43, 0x75, 0x94, 0x96, 0x19, 0xea, 0x0b, 0x64, 0xbc, 0x29, - 0x33, 0x04, 0x79, 0x2d, 0xdc, 0x6f, 0xbc, 0x2c, 0x52, 0x30, 0x5a, 0xc1, 0x93, 0x14, 0x0c, 0x56, 0xf0, 0x30, 0x15, - 0xe0, 0x54, 0x41, 0x53, 0x16, 0x98, 0xc2, 0x3f, 0x74, 0x6a, 0x30, 0x73, 0x75, 0x4b, 0x0c, 0x96, 0x76, 0x19, 0x9c, - 0x14, 0x1f, 0x65, 0x0c, 0x7f, 0x1b, 0x1a, 0x61, 0x06, 0x6d, 0x32, 0x84, 0x79, 0x52, 0x10, 0x48, 0xc3, 0x3c, 0x99, - 0x10, 0x06, 0x4d, 0xf2, 0x64, 0x48, 0x58, 0xbf, 0x13, 0xa0, 0xc9, 0x53, 0x03, 0x3b, 0x00, 0x0e, 0xaf, 0xdf, 0x86, - 0x6b, 0xdb, 0x38, 0x5c, 0xb3, 0x43, 0x13, 0x82, 0x70, 0x15, 0xc3, 0x2c, 0x60, 0x73, 0x9a, 0x9f, 0x9d, 0x2a, 0x86, - 0x24, 0x4f, 0xa8, 0xa1, 0xde, 0x7f, 0x01, 0x59, 0x8d, 0xef, 0x2d, 0xd9, 0x1a, 0xef, 0xdd, 0x5b, 0x8a, 0xf5, 0x0f, - 0xf0, 0x47, 0xb9, 0x3f, 0xda, 0x9c, 0x7e, 0x6b, 0xf4, 0x57, 0x0a, 0xc5, 0x76, 0x94, 0x42, 0x7f, 0x79, 0x9f, 0x3a, - 0x45, 0x96, 0xb7, 0x69, 0x34, 0xa2, 0xc5, 0xe7, 0x08, 0x7f, 0x4a, 0xa3, 0x1c, 0x18, 0xc1, 0x08, 0x7f, 0x4c, 0xa3, - 0x82, 0x45, 0xf8, 0x8f, 0x34, 0x1a, 0xe6, 0x8b, 0x08, 0x7f, 0x48, 0xa3, 0x49, 0x11, 0xe1, 0xf7, 0xa0, 0xf1, 0x1c, - 0xf1, 0xc5, 0x2c, 0xc2, 0xbf, 0xa7, 0x91, 0x32, 0x2e, 0x05, 0xf8, 0x61, 0x1a, 0x31, 0x16, 0xe1, 0x77, 0x69, 0x24, - 0xf3, 0x08, 0x5f, 0xa5, 0x91, 0x2c, 0x22, 0xfc, 0x28, 0x8d, 0x0a, 0x1a, 0xe1, 0xc7, 0x69, 0x04, 0x85, 0x26, 0x11, - 0x7e, 0x92, 0x46, 0xd0, 0xb2, 0x8a, 0xf0, 0xdb, 0x34, 0xe2, 0x22, 0xc2, 0xbf, 0xa5, 0x91, 0x5e, 0x14, 0xff, 0x2c, - 0x24, 0x57, 0x11, 0x7e, 0x9a, 0x46, 0x53, 0x1e, 0xe1, 0x37, 0x69, 0x54, 0xc8, 0x08, 0xbf, 0x4e, 0x23, 0x9a, 0x47, - 0xf8, 0x55, 0x1a, 0xe5, 0x2c, 0xc2, 0xbf, 0xa6, 0xd1, 0x88, 0x45, 0xf8, 0x65, 0x1a, 0xdd, 0xb1, 0x3c, 0x97, 0x11, - 0x7e, 0x96, 0x46, 0x4c, 0x44, 0xf8, 0x97, 0x34, 0xca, 0xa6, 0x11, 0xfe, 0x29, 0x8d, 0x68, 0xf1, 0x59, 0x45, 0xf8, - 0x79, 0x1a, 0x31, 0x1a, 0xe1, 0x17, 0xb6, 0xa3, 0x49, 0x84, 0x7f, 0x4e, 0xa3, 0x9b, 0x69, 0xb4, 0xc6, 0x4a, 0x91, - 0xe5, 0x6b, 0x9e, 0xb1, 0x3f, 0x58, 0x1a, 0x8d, 0x5b, 0xe3, 0xf3, 0xf1, 0x38, 0xc2, 0x54, 0x68, 0xfe, 0xcf, 0x82, - 0xdd, 0x3c, 0xd5, 0x90, 0x48, 0xd9, 0x70, 0x74, 0x3f, 0xc2, 0xf4, 0x9f, 0x05, 0x4d, 0xa3, 0xf1, 0xd8, 0x14, 0xf8, - 0x67, 0x41, 0x67, 0xb4, 0x78, 0xcb, 0xd2, 0xe8, 0xfe, 0x78, 0x3c, 0x1e, 0x9d, 0x44, 0x98, 0x7e, 0x5d, 0x7c, 0x34, - 0x2d, 0x98, 0x02, 0x43, 0xc6, 0x27, 0x50, 0xf7, 0x74, 0x7c, 0x3a, 0xca, 0x22, 0x3c, 0xe4, 0xea, 0x9f, 0x05, 0x7c, - 0x8f, 0xd9, 0x49, 0x76, 0x12, 0xe1, 0x61, 0x4e, 0xb3, 0xcf, 0x69, 0xd4, 0x32, 0xbf, 0xc4, 0x2f, 0x6c, 0xf4, 0x7a, - 0x26, 0xcd, 0x7d, 0xc0, 0x98, 0x0d, 0xb3, 0x51, 0x84, 0xcd, 0x60, 0xc6, 0xf0, 0xf7, 0x0b, 0x7f, 0xc7, 0x74, 0x1a, - 0x9d, 0xd3, 0xce, 0x90, 0x75, 0x22, 0x3c, 0x7c, 0x73, 0x23, 0xd2, 0x88, 0x9e, 0x76, 0x68, 0x87, 0x46, 0x78, 0xb8, - 0x28, 0xf2, 0xbb, 0x1b, 0x29, 0x47, 0x00, 0x84, 0xe1, 0xf9, 0xf9, 0xfd, 0x08, 0x67, 0xf4, 0x57, 0x0d, 0xb5, 0x4f, - 0xc7, 0x0f, 0x18, 0x6d, 0x45, 0xf8, 0x17, 0x5a, 0xe8, 0x8f, 0x0b, 0xe5, 0x06, 0xda, 0x82, 0x14, 0x99, 0xbd, 0x03, - 0x5d, 0x79, 0x34, 0xea, 0x9c, 0x3d, 0x68, 0xb3, 0x08, 0x67, 0x57, 0xaf, 0xa1, 0xb7, 0xfb, 0xe3, 0xd3, 0x16, 0x7c, - 0x08, 0x10, 0x3a, 0x59, 0x01, 0x8d, 0x9c, 0x9d, 0x3c, 0x38, 0x65, 0x23, 0x93, 0xa8, 0x78, 0xfe, 0xd9, 0xcc, 0xfe, - 0x1c, 0xe6, 0x93, 0x15, 0x7c, 0xa6, 0xa4, 0x48, 0xa3, 0x51, 0xd6, 0x3e, 0x39, 0x86, 0x84, 0x3b, 0x2a, 0x3c, 0x70, - 0x6e, 0xa1, 0xea, 0xf9, 0x30, 0xc2, 0xb7, 0x36, 0xf5, 0x7c, 0x68, 0x3e, 0x26, 0xef, 0x7e, 0x15, 0x6f, 0x46, 0x69, - 0x34, 0x3c, 0x3f, 0x3f, 0x6b, 0x41, 0xc2, 0x07, 0x7a, 0x97, 0x46, 0xf4, 0x01, 0xfc, 0x07, 0xd9, 0x1f, 0x9f, 0x41, - 0x87, 0x30, 0xc2, 0xdb, 0xc9, 0xc7, 0x30, 0xe7, 0xf3, 0x94, 0x7e, 0xe6, 0x69, 0x34, 0x1c, 0x0d, 0xef, 0x9f, 0x41, - 0xbd, 0x19, 0x9d, 0x3c, 0xd3, 0x14, 0xda, 0x6d, 0xb5, 0x4c, 0xcb, 0xef, 0xf8, 0x17, 0x66, 0xaa, 0x9f, 0x9e, 0x9e, - 0x0d, 0x3b, 0x30, 0x82, 0x2b, 0xd0, 0x96, 0xc0, 0x78, 0xce, 0x33, 0xd3, 0xe0, 0x55, 0xf6, 0x74, 0x94, 0x46, 0x0f, - 0x1e, 0x1c, 0x77, 0xb2, 0x2c, 0xc2, 0xb7, 0x1f, 0x47, 0xb6, 0xb6, 0xc9, 0x53, 0x00, 0xfb, 0x34, 0x62, 0x0f, 0x1e, - 0x9c, 0xdd, 0xa7, 0xf0, 0xfd, 0xdc, 0xb4, 0x75, 0x3e, 0x1e, 0x66, 0xe7, 0xd0, 0xd6, 0xef, 0x30, 0x9d, 0x93, 0xf3, - 0xe3, 0x91, 0xe9, 0xeb, 0x77, 0x33, 0xea, 0xce, 0xf8, 0x64, 0x7c, 0x62, 0x32, 0xcd, 0x50, 0xcb, 0xcf, 0xdf, 0x58, - 0x1a, 0x65, 0x6c, 0xd4, 0x8e, 0xf0, 0xad, 0x5b, 0xb8, 0x07, 0x27, 0xad, 0xd6, 0xe8, 0x38, 0xc2, 0xa3, 0x87, 0xf3, - 0xf9, 0x5b, 0x03, 0xc1, 0xf6, 0xc9, 0x03, 0xfb, 0xad, 0x3e, 0xdf, 0x41, 0xd3, 0x43, 0x03, 0xb4, 0x11, 0x9f, 0x99, - 0x96, 0xcf, 0x1e, 0xc0, 0x7f, 0xe6, 0xdb, 0x34, 0x5d, 0x7e, 0xcb, 0xd1, 0xc4, 0x2e, 0x4a, 0x9b, 0x3d, 0x68, 0x41, - 0x8d, 0x31, 0xff, 0x38, 0x2c, 0x38, 0xa0, 0xd1, 0xb0, 0x03, 0xff, 0x17, 0xe1, 0x71, 0x7e, 0xf5, 0xda, 0xe1, 0xec, - 0x78, 0x4c, 0xc7, 0xad, 0x08, 0x8f, 0xe5, 0x47, 0xa5, 0x3f, 0x3c, 0x14, 0x69, 0xd4, 0xe9, 0x9c, 0x0f, 0x4d, 0x99, - 0xc5, 0x2f, 0x8a, 0x1b, 0x3c, 0x6e, 0x99, 0x56, 0x26, 0xf4, 0xad, 0x1a, 0x5e, 0x49, 0x58, 0x49, 0xf8, 0x2f, 0xc2, - 0x13, 0x50, 0xb1, 0xb9, 0x56, 0xce, 0xed, 0x76, 0x98, 0xbc, 0x33, 0xa8, 0x39, 0xba, 0x0f, 0xf0, 0xf2, 0xcb, 0x38, - 0xa2, 0xf4, 0xb4, 0xd3, 0x8a, 0xb0, 0x19, 0xf5, 0x79, 0x0b, 0xfe, 0x8b, 0xb0, 0x85, 0x9c, 0x81, 0xeb, 0xe4, 0xe3, - 0xb3, 0x97, 0x37, 0x69, 0x44, 0x47, 0xe3, 0x31, 0x2c, 0x89, 0x99, 0x8c, 0x2f, 0x36, 0x95, 0x82, 0xdd, 0xfd, 0x7a, - 0xe3, 0xb6, 0x8b, 0x49, 0xd0, 0x0e, 0x3a, 0x67, 0x0f, 0x86, 0x27, 0x11, 0x7e, 0x3b, 0xe2, 0x54, 0xc0, 0x2a, 0x65, - 0xa3, 0xd3, 0xec, 0x34, 0x33, 0x09, 0x13, 0x99, 0x46, 0x27, 0xb0, 0xe4, 0x9d, 0x08, 0xf3, 0x2f, 0x57, 0x77, 0x16, - 0xdd, 0xa0, 0xb6, 0x43, 0x90, 0x71, 0x8b, 0x9d, 0x9d, 0x67, 0x11, 0xce, 0xe9, 0x97, 0x67, 0xbf, 0x16, 0x69, 0xc4, - 0xce, 0xd8, 0xd9, 0x98, 0xfa, 0xef, 0x3f, 0xd4, 0xd4, 0xd4, 0x68, 0x8d, 0x4f, 0x21, 0xe9, 0x46, 0x98, 0xb1, 0xde, - 0xcf, 0xc6, 0x06, 0x43, 0x5e, 0xcd, 0xa4, 0xc8, 0x9e, 0x8e, 0xc7, 0xd2, 0x62, 0x31, 0x85, 0x4d, 0xf8, 0x09, 0xa0, - 0x4d, 0x47, 0xa3, 0x73, 0x76, 0x16, 0xe1, 0x4f, 0x76, 0x97, 0xb8, 0x09, 0x7c, 0xb2, 0x98, 0xcd, 0xdc, 0x6e, 0xff, - 0x64, 0x81, 0x02, 0xf3, 0x1d, 0xd3, 0x31, 0x1d, 0x75, 0x22, 0xfc, 0xc9, 0xc0, 0x65, 0x74, 0x0c, 0xff, 0x41, 0x01, - 0xe8, 0xec, 0x41, 0x8b, 0xb1, 0x07, 0x2d, 0xf3, 0x15, 0xe6, 0xb9, 0x99, 0x0f, 0xcf, 0xb2, 0x76, 0x84, 0x3f, 0x39, - 0x74, 0x1c, 0x8f, 0x69, 0x0b, 0xd0, 0xf1, 0x93, 0x43, 0xc7, 0x4e, 0x6b, 0xd8, 0xa1, 0xe6, 0xdb, 0x62, 0xcd, 0xf9, - 0xfd, 0x8c, 0xc1, 0xe4, 0x3e, 0x59, 0x84, 0xbc, 0x7f, 0xff, 0xfc, 0xfc, 0xc1, 0x03, 0xf8, 0x34, 0x6d, 0x97, 0x9f, - 0x4a, 0x3f, 0xcc, 0x0d, 0x92, 0xb5, 0xb2, 0x13, 0xa0, 0x93, 0x9f, 0xcc, 0x18, 0xc7, 0xe3, 0x31, 0x6b, 0x45, 0x38, - 0xe7, 0x33, 0x66, 0x31, 0xc1, 0xfe, 0x36, 0x1d, 0x1d, 0x77, 0xb2, 0xd1, 0x71, 0x27, 0xc2, 0xf9, 0xdb, 0x67, 0x66, - 0x36, 0x2d, 0x98, 0xbd, 0xdf, 0x72, 0x1e, 0x6b, 0x66, 0xf4, 0x0d, 0x0c, 0x12, 0x56, 0x1a, 0x2a, 0xbf, 0x0f, 0xe8, - 0xe1, 0xd9, 0x59, 0x36, 0x82, 0x81, 0xbe, 0x87, 0x6e, 0x01, 0x8c, 0xef, 0xed, 0xe6, 0x1b, 0xd2, 0xd3, 0x53, 0x98, - 0xee, 0xfb, 0xf9, 0xa2, 0x98, 0xbf, 0x4a, 0xa3, 0x07, 0xc7, 0xf7, 0x5b, 0xa3, 0x61, 0x84, 0xdf, 0xbb, 0x09, 0x1e, - 0x67, 0xc3, 0xe3, 0xfb, 0xed, 0x08, 0xbf, 0x37, 0xfb, 0xed, 0xfe, 0xf0, 0xec, 0x1c, 0xce, 0x8d, 0xf7, 0x6a, 0x5e, - 0xbc, 0x9d, 0x98, 0x02, 0x63, 0xfa, 0x00, 0x9a, 0xfd, 0xcd, 0xec, 0xc6, 0x51, 0x1b, 0x36, 0xf2, 0x7b, 0xb3, 0xc9, - 0x0c, 0x9e, 0xdc, 0x6f, 0x9f, 0x9e, 0x9f, 0x46, 0x78, 0xc6, 0x47, 0x02, 0x08, 0xbc, 0xd9, 0x28, 0x0f, 0xda, 0x0f, - 0xee, 0xb7, 0x22, 0x3c, 0x7b, 0xab, 0xb3, 0x8f, 0x74, 0x66, 0xa8, 0xf1, 0x18, 0x60, 0x36, 0xe3, 0x4a, 0xdf, 0xbd, - 0x51, 0x8e, 0x1e, 0xb3, 0x76, 0x84, 0x67, 0x32, 0xcb, 0xa8, 0x7a, 0x6b, 0x13, 0x86, 0xa7, 0x11, 0x16, 0xf4, 0x0b, - 0xfd, 0x5b, 0xfa, 0xcd, 0x34, 0x62, 0x74, 0x64, 0xd2, 0x0c, 0x0e, 0x47, 0xf8, 0xdd, 0x08, 0x6e, 0xf4, 0xd2, 0x68, - 0x3c, 0x1a, 0x9f, 0x02, 0x78, 0x80, 0x00, 0x59, 0xec, 0x06, 0x68, 0xc0, 0xd7, 0xe8, 0xd1, 0x30, 0x8d, 0xce, 0x86, - 0xe7, 0xac, 0x73, 0x1c, 0xe1, 0x92, 0x1a, 0xd1, 0x53, 0xc8, 0x37, 0x9f, 0x1f, 0xcd, 0x96, 0x3a, 0xb1, 0x09, 0x06, - 0x40, 0x23, 0x7a, 0xbf, 0x35, 0x3a, 0x8b, 0xf0, 0xfc, 0x35, 0xf3, 0x7b, 0x8c, 0x31, 0x76, 0x0e, 0xb0, 0x84, 0x24, - 0x83, 0x40, 0xe7, 0xe3, 0xe1, 0x83, 0x73, 0xf3, 0x0d, 0x60, 0xa0, 0x63, 0xc6, 0x00, 0x48, 0xf3, 0xd7, 0xac, 0x04, - 0xc4, 0x68, 0x78, 0xbf, 0x05, 0xf4, 0x65, 0x4e, 0xe7, 0xf4, 0x8e, 0xde, 0x3c, 0x9d, 0x9b, 0x39, 0x8d, 0x47, 0xa7, - 0x11, 0x9e, 0x3f, 0xff, 0x65, 0xbe, 0x18, 0x8f, 0xcd, 0x84, 0xe8, 0xf0, 0x41, 0x84, 0xe7, 0xac, 0x58, 0xc0, 0x1a, - 0x9d, 0x9f, 0x1e, 0x8f, 0x23, 0xec, 0xd0, 0x30, 0x6b, 0x65, 0x43, 0xb8, 0xb2, 0x5c, 0xcc, 0xd2, 0x68, 0x34, 0xa2, - 0xad, 0x11, 0x5c, 0x60, 0xca, 0x9b, 0x5f, 0x0b, 0x8b, 0x46, 0xcc, 0xe0, 0x83, 0x5b, 0x43, 0x98, 0x2f, 0xc0, 0xe3, - 0xe3, 0x90, 0x65, 0x19, 0x75, 0x89, 0x67, 0x67, 0xc7, 0xc7, 0x80, 0x7b, 0x76, 0x86, 0x16, 0x41, 0xde, 0xa8, 0xbb, - 0x61, 0x21, 0xe1, 0xe8, 0x02, 0xa2, 0x0a, 0x64, 0xf5, 0xcd, 0xdd, 0x6b, 0x43, 0x57, 0xdb, 0x67, 0x0f, 0x60, 0x01, - 0x14, 0x1d, 0x8d, 0x5e, 0xd9, 0xc3, 0xed, 0x7c, 0x78, 0x72, 0xda, 0x3e, 0x8e, 0xb0, 0xdf, 0x08, 0xf4, 0xbc, 0x75, - 0xbf, 0x03, 0x25, 0xc4, 0xe8, 0xce, 0x96, 0x18, 0x9f, 0xd0, 0x93, 0xb3, 0x56, 0x84, 0xfd, 0xd6, 0x60, 0xe7, 0xc3, - 0xd3, 0xfb, 0xf0, 0xa9, 0xa6, 0x2c, 0xcf, 0x0d, 0x7e, 0x9f, 0x02, 0x5c, 0x14, 0x7f, 0x26, 0x68, 0x1a, 0xd1, 0xd6, - 0x69, 0xa7, 0x33, 0x82, 0xcf, 0xfc, 0x0b, 0x2b, 0xd2, 0x28, 0x6b, 0xc1, 0x7f, 0x11, 0x0e, 0x76, 0x12, 0x1b, 0x46, - 0xd8, 0xe0, 0xdd, 0x19, 0x3d, 0x35, 0x7b, 0xdf, 0xed, 0xaa, 0xd6, 0x79, 0x0b, 0x36, 0xac, 0xdb, 0x54, 0xee, 0x4b, - 0x09, 0x79, 0xe3, 0x48, 0x2c, 0x8d, 0x70, 0x80, 0xa0, 0xe3, 0xfb, 0xe3, 0x08, 0xfb, 0x1d, 0x77, 0x72, 0x76, 0xde, - 0x01, 0x52, 0xa6, 0x81, 0x50, 0x8c, 0x3a, 0xc3, 0x13, 0x20, 0x4d, 0x9a, 0xbd, 0xb6, 0x78, 0x12, 0x61, 0xfd, 0x54, - 0xe9, 0x57, 0x69, 0x34, 0x3a, 0x1f, 0x8e, 0x47, 0xe7, 0x11, 0xd6, 0x72, 0x46, 0xb5, 0x34, 0x14, 0xf0, 0xf8, 0xe4, - 0x7e, 0x84, 0x0d, 0x9a, 0xb7, 0x58, 0x6b, 0xd4, 0x8a, 0xb0, 0x3b, 0x4a, 0x18, 0x3b, 0xef, 0xc0, 0xb4, 0x7e, 0x7e, - 0xae, 0x01, 0x97, 0x47, 0x6c, 0x78, 0x1c, 0xe1, 0x92, 0xde, 0x1b, 0x42, 0x04, 0x5f, 0x6a, 0x26, 0x3f, 0x3b, 0xd6, - 0x03, 0x48, 0x9d, 0xdf, 0xf0, 0xb0, 0x0c, 0x2f, 0x6f, 0x2c, 0x1a, 0x51, 0xb3, 0xc5, 0x83, 0x2b, 0xdd, 0x27, 0x34, - 0xf6, 0x6c, 0x3b, 0x27, 0xcb, 0x35, 0x2e, 0x23, 0xa5, 0x7e, 0x66, 0x77, 0x2a, 0x56, 0xca, 0x70, 0xb2, 0x41, 0x0a, - 0x78, 0x33, 0x38, 0xdf, 0x00, 0xe7, 0xfe, 0x09, 0x82, 0xa4, 0x20, 0xad, 0xae, 0xb8, 0xf0, 0x2e, 0xa9, 0x5d, 0x01, - 0xf1, 0x13, 0x20, 0xbd, 0x20, 0x94, 0x68, 0x08, 0x33, 0x63, 0x85, 0x49, 0x6f, 0xa9, 0x6f, 0x64, 0x4a, 0x69, 0x6d, - 0xff, 0x29, 0xa1, 0x3e, 0xc0, 0x3c, 0xdc, 0x37, 0x43, 0x08, 0x26, 0xd4, 0x95, 0xc4, 0x84, 0x8b, 0x7e, 0x21, 0x74, - 0xac, 0x54, 0xbf, 0x18, 0xe0, 0xf6, 0x19, 0xc2, 0x10, 0x88, 0x81, 0xf4, 0xe5, 0xe5, 0x65, 0xfb, 0xec, 0xc0, 0x08, - 0x7d, 0x97, 0x97, 0xe7, 0xf6, 0x07, 0xfc, 0x3b, 0xa8, 0x82, 0x5f, 0xc3, 0xf8, 0x1e, 0xb1, 0x40, 0xa3, 0x67, 0xf8, - 0xeb, 0x47, 0x6c, 0xb5, 0x8a, 0x1f, 0x31, 0x02, 0x33, 0xc6, 0x8f, 0x58, 0x62, 0x2e, 0x40, 0xac, 0x9b, 0x0d, 0xe9, - 0x83, 0xe6, 0xac, 0x85, 0x21, 0x24, 0xbb, 0xe7, 0xbc, 0x1f, 0xb1, 0x3e, 0xaf, 0xbb, 0x68, 0x57, 0x71, 0x90, 0x0f, - 0x0e, 0x96, 0x45, 0xaa, 0xad, 0x98, 0xa0, 0xad, 0x98, 0xa0, 0xad, 0x98, 0xa0, 0xab, 0x48, 0xf4, 0x27, 0x3d, 0x90, - 0x52, 0x8c, 0xb2, 0xc5, 0xf1, 0xd4, 0xef, 0x40, 0xed, 0x01, 0xda, 0xc9, 0x5e, 0xa5, 0xec, 0x28, 0x75, 0x15, 0x3b, - 0x15, 0x18, 0x3b, 0x13, 0x9d, 0xb6, 0xe3, 0xe8, 0xdf, 0x51, 0x77, 0xbc, 0xac, 0x89, 0x65, 0xef, 0x76, 0x8a, 0x65, - 0xb0, 0x92, 0x46, 0x34, 0xdb, 0xb7, 0x41, 0x3d, 0x74, 0xff, 0xbe, 0x11, 0xcc, 0xaa, 0x48, 0x73, 0x0d, 0x48, 0xea, - 0x82, 0x14, 0x72, 0x6e, 0xa4, 0xb4, 0x02, 0xa5, 0x23, 0x1d, 0x17, 0xa0, 0xa1, 0xf4, 0x0a, 0xca, 0x32, 0x20, 0x6a, - 0xc3, 0x00, 0x44, 0x59, 0x19, 0xcd, 0xca, 0x6a, 0xa7, 0x20, 0xba, 0x80, 0x26, 0xcc, 0x48, 0x2c, 0xd0, 0x80, 0x30, - 0x0d, 0x08, 0x57, 0x19, 0xc4, 0x19, 0x97, 0x7d, 0x62, 0xb2, 0x95, 0xc9, 0x56, 0x65, 0xb6, 0xf4, 0xd9, 0x56, 0x48, - 0x94, 0x26, 0x5b, 0x96, 0xd9, 0x20, 0xb3, 0xe1, 0x49, 0xaa, 0xf0, 0x30, 0x95, 0x56, 0x54, 0xab, 0x64, 0xab, 0xb7, - 0x34, 0xd4, 0xe6, 0x1e, 0x1c, 0xc4, 0xa5, 0x9c, 0x64, 0xd4, 0xc4, 0xf7, 0x96, 0x3c, 0x29, 0x8c, 0x0c, 0xc4, 0x93, - 0x89, 0xfb, 0x3b, 0x5c, 0x6f, 0xca, 0x4a, 0xc5, 0x64, 0xf8, 0x8d, 0x92, 0xe8, 0x2f, 0xaf, 0x44, 0x7d, 0xc4, 0x4d, - 0x28, 0x9d, 0x0b, 0x92, 0xb4, 0x5a, 0xc7, 0xed, 0xe3, 0xd6, 0x79, 0x8f, 0x1f, 0xb6, 0x3b, 0xc9, 0x83, 0x4e, 0x6a, - 0x14, 0x11, 0x73, 0x79, 0x03, 0x0a, 0x98, 0xa3, 0x4e, 0x72, 0x82, 0x0e, 0xdb, 0x49, 0xeb, 0xf4, 0xb4, 0x09, 0xff, - 0xe0, 0xf7, 0xba, 0xac, 0x76, 0xd2, 0x3a, 0x39, 0xed, 0xf1, 0xa3, 0x8d, 0x4a, 0x31, 0x6f, 0x40, 0x41, 0x74, 0x64, - 0x2a, 0x61, 0xa8, 0x5f, 0x2d, 0xef, 0xb3, 0x2d, 0x3d, 0xcf, 0x7b, 0x1d, 0x2b, 0xab, 0x8a, 0x03, 0xa8, 0xfa, 0xaf, - 0x89, 0x01, 0xa2, 0xff, 0x1a, 0x96, 0xe1, 0x6e, 0x97, 0x05, 0x88, 0xda, 0x8f, 0x78, 0x2c, 0x1a, 0xec, 0x30, 0xb6, - 0xf9, 0x1a, 0xea, 0x36, 0x21, 0x04, 0x1d, 0x9e, 0xb8, 0x5c, 0x15, 0xe6, 0x4e, 0x10, 0x6a, 0x2a, 0xc8, 0x1d, 0xba, - 0x5c, 0x19, 0xe6, 0x0e, 0x11, 0x6a, 0x4a, 0xc8, 0xa5, 0x29, 0x4f, 0x28, 0xe4, 0xe8, 0x84, 0x36, 0x0d, 0x24, 0xab, - 0x45, 0x79, 0xce, 0xfc, 0xb0, 0xf9, 0x18, 0x96, 0xc7, 0x10, 0x14, 0x27, 0x48, 0x0b, 0x78, 0xa6, 0xa4, 0xd4, 0xe6, - 0xb4, 0x70, 0xa9, 0xc6, 0x81, 0x8c, 0x06, 0xfc, 0x73, 0xc8, 0xcc, 0xdb, 0x15, 0xad, 0xde, 0xf1, 0x59, 0x2b, 0x6d, - 0x83, 0xbf, 0x35, 0xc8, 0xda, 0xc2, 0xca, 0xda, 0xc2, 0xcb, 0xda, 0xc2, 0xcb, 0xda, 0x20, 0xc0, 0x07, 0x7d, 0xff, - 0x23, 0x6b, 0x36, 0x2c, 0xbc, 0x34, 0x88, 0xb1, 0x16, 0x0f, 0xb1, 0x5e, 0xad, 0x96, 0x6b, 0x30, 0x57, 0x2a, 0x6b, - 0x48, 0x55, 0xa9, 0x3f, 0x97, 0x45, 0xda, 0xc2, 0x93, 0x14, 0xb4, 0xdc, 0x2d, 0x4c, 0xcd, 0xe6, 0xf6, 0x54, 0x61, - 0x33, 0x14, 0x4e, 0xcf, 0xab, 0x93, 0x2f, 0xc9, 0xb1, 0xd1, 0x1e, 0x2f, 0x8b, 0x94, 0x5b, 0x9a, 0xc1, 0x2d, 0xcd, - 0xe0, 0x96, 0x66, 0x40, 0x23, 0xb8, 0x2c, 0x6c, 0xca, 0x26, 0x94, 0xc0, 0x95, 0x40, 0xff, 0x78, 0x00, 0x91, 0x00, - 0x63, 0x4d, 0xcc, 0xa8, 0x37, 0x3a, 0x6f, 0x43, 0xe4, 0x33, 0x5b, 0x52, 0x27, 0xd4, 0x38, 0x80, 0x97, 0x63, 0xfe, - 0x5a, 0x43, 0xfb, 0x04, 0x9e, 0xa5, 0x79, 0xa8, 0xe3, 0x16, 0xd8, 0x7f, 0x44, 0x45, 0xd4, 0x33, 0x64, 0x21, 0x35, - 0x3a, 0x1b, 0x67, 0xd7, 0xfd, 0x79, 0xc3, 0x9d, 0xd6, 0x52, 0x82, 0xf0, 0x31, 0x86, 0xcf, 0xac, 0xf2, 0xef, 0x2f, - 0xcd, 0x56, 0x9d, 0xcd, 0x99, 0x3d, 0x12, 0xba, 0x60, 0x7b, 0xee, 0x03, 0x47, 0xf5, 0x04, 0x91, 0xca, 0x78, 0x3e, - 0x92, 0x2a, 0xf4, 0x31, 0x78, 0x02, 0x93, 0x5b, 0x6a, 0xfc, 0x62, 0x5e, 0xd8, 0x3f, 0x5f, 0x69, 0xe0, 0x38, 0x58, - 0x4c, 0x86, 0xde, 0xdf, 0xf6, 0xda, 0x04, 0x08, 0x22, 0xfb, 0xfb, 0xd6, 0x2c, 0xdc, 0x7c, 0x6d, 0xda, 0x85, 0x9b, - 0x44, 0x93, 0x0d, 0x3b, 0xd4, 0xaf, 0xd1, 0x3f, 0xde, 0xed, 0xad, 0x98, 0x0c, 0x51, 0x40, 0xb3, 0x0d, 0x58, 0x55, - 0x05, 0x2c, 0xe5, 0xea, 0x95, 0xde, 0x90, 0xd0, 0xbb, 0x19, 0xf3, 0xba, 0x98, 0x0c, 0x77, 0xbe, 0x5f, 0x62, 0x7b, - 0xec, 0xbd, 0xa5, 0x41, 0x0f, 0x5e, 0xb5, 0x3d, 0x65, 0xb7, 0xdf, 0xab, 0x73, 0xb3, 0xb3, 0x8e, 0xca, 0xbf, 0x57, - 0xe7, 0xe9, 0xae, 0x3a, 0x33, 0x7e, 0x1b, 0xfb, 0xbd, 0xa3, 0x03, 0x35, 0xb6, 0xb1, 0x35, 0x9a, 0x0c, 0x21, 0xe0, - 0x3c, 0xfc, 0xb5, 0x61, 0x61, 0xba, 0x9e, 0x84, 0xc3, 0x2a, 0xc8, 0x5e, 0x72, 0x9a, 0x32, 0x4c, 0x49, 0xe7, 0xb0, - 0x30, 0x81, 0x61, 0x44, 0x42, 0x9b, 0x2a, 0xa1, 0x38, 0x27, 0x71, 0x4c, 0x0f, 0x33, 0x08, 0x6f, 0xd3, 0xee, 0xd1, - 0x34, 0xa6, 0x8d, 0x0c, 0x1d, 0xc5, 0xed, 0x06, 0x3d, 0xcc, 0x10, 0x6a, 0xb4, 0x41, 0x67, 0x2a, 0x49, 0xbb, 0x99, - 0x43, 0xc0, 0x4b, 0x43, 0x8a, 0xf3, 0x43, 0x91, 0x14, 0x0d, 0x79, 0xa8, 0x92, 0xa2, 0x91, 0x9c, 0x62, 0x91, 0x4c, - 0xca, 0xe4, 0x89, 0x49, 0x9e, 0xd8, 0xe4, 0x61, 0x99, 0x3c, 0x34, 0xc9, 0x43, 0x9b, 0x4c, 0x49, 0x71, 0x28, 0x12, - 0xda, 0x88, 0xdb, 0xcd, 0x02, 0x1d, 0xc2, 0x08, 0xfc, 0xe8, 0x89, 0x08, 0xe3, 0x8c, 0xaf, 0x8d, 0xa1, 0xce, 0x5c, - 0xe6, 0x2e, 0xf2, 0x67, 0x05, 0xa4, 0xd2, 0x7b, 0x0a, 0xea, 0x3c, 0x0b, 0xc0, 0x84, 0xb5, 0xfd, 0xe3, 0xe3, 0xda, - 0xad, 0xb3, 0x5c, 0x8a, 0xc0, 0x3b, 0x0c, 0x0c, 0xda, 0x3f, 0x3b, 0x9f, 0x18, 0x80, 0xea, 0x9a, 0xe6, 0xf3, 0x29, - 0xdd, 0x72, 0xc1, 0x2d, 0x26, 0x43, 0xb7, 0xb3, 0xca, 0x66, 0x18, 0x2d, 0x6c, 0xbc, 0xe8, 0xba, 0xb3, 0x24, 0x80, - 0xda, 0x3b, 0x68, 0x26, 0xd4, 0x28, 0xc9, 0x6d, 0x8d, 0x49, 0xc1, 0xee, 0x54, 0x46, 0x73, 0x16, 0x57, 0x07, 0x70, - 0x35, 0x4c, 0x46, 0x5e, 0x80, 0x59, 0x7d, 0x71, 0x98, 0x1c, 0x37, 0x74, 0x32, 0x39, 0x4c, 0x4e, 0x1f, 0x34, 0x74, - 0x32, 0x3c, 0x4c, 0xda, 0xed, 0x0a, 0x67, 0x93, 0x82, 0xe8, 0x64, 0x42, 0x34, 0x68, 0x0c, 0x6d, 0xa3, 0x72, 0x4e, - 0xc1, 0x4e, 0xec, 0xdf, 0x18, 0x46, 0xc3, 0x0d, 0x43, 0xb0, 0x89, 0x0d, 0x9d, 0xb9, 0x35, 0x86, 0xb0, 0x9b, 0xce, - 0xe9, 0x69, 0x53, 0x27, 0x05, 0xd6, 0x76, 0x25, 0x9b, 0x3a, 0x99, 0x60, 0x6d, 0x97, 0xaf, 0xa9, 0x93, 0xa1, 0x6d, - 0xca, 0xe8, 0x00, 0x99, 0x08, 0x80, 0xf5, 0x9c, 0x05, 0x90, 0xef, 0x78, 0x4f, 0x97, 0x35, 0x68, 0x0d, 0xbf, 0x57, - 0xae, 0xe9, 0x0b, 0x2a, 0xaa, 0xc1, 0x5e, 0x88, 0x7d, 0xab, 0x68, 0xbb, 0x6a, 0x92, 0xfd, 0xeb, 0xb2, 0x65, 0xb3, - 0x85, 0xd4, 0xf5, 0x82, 0x0f, 0x6b, 0x18, 0xe2, 0x4a, 0xb9, 0x83, 0xfb, 0x15, 0x25, 0x31, 0x04, 0xc8, 0x33, 0xa7, - 0x10, 0x27, 0x5e, 0x8f, 0x0c, 0x49, 0xbc, 0xd1, 0x58, 0xa3, 0x38, 0x38, 0x6f, 0x9f, 0x86, 0x54, 0x75, 0x2b, 0x6a, - 0x1e, 0x21, 0xd1, 0x42, 0x58, 0xbb, 0xca, 0x51, 0x14, 0xb0, 0x20, 0x4e, 0xbb, 0x5b, 0x3b, 0x20, 0x0e, 0x0e, 0x36, - 0xcf, 0x0b, 0xff, 0x7e, 0xc1, 0xd6, 0x9b, 0x05, 0x95, 0x51, 0x9e, 0x7f, 0x55, 0xc9, 0x9a, 0xeb, 0xf2, 0x00, 0x51, - 0x7c, 0xfc, 0xaa, 0xfb, 0x86, 0xc2, 0xf7, 0xab, 0xe0, 0x7d, 0x2e, 0xa7, 0x79, 0x66, 0x32, 0x4c, 0x5f, 0x83, 0x60, - 0x6c, 0x6f, 0xc2, 0x09, 0x95, 0x06, 0x87, 0xff, 0xb2, 0xe3, 0xa0, 0x13, 0xf7, 0xea, 0x4b, 0xd8, 0xe8, 0xdf, 0xa1, - 0x79, 0x6f, 0x05, 0x1b, 0xe7, 0xd8, 0xbd, 0x5a, 0xd5, 0xde, 0xf8, 0xb1, 0x2f, 0xc9, 0xa0, 0x83, 0x03, 0xae, 0x9e, - 0x81, 0x45, 0x32, 0x8b, 0x1b, 0xe1, 0xe1, 0xfb, 0x4f, 0xed, 0xb4, 0xfe, 0xdb, 0x9c, 0xab, 0x69, 0x70, 0xd0, 0x3d, - 0xac, 0xe5, 0xef, 0x5c, 0x89, 0x9e, 0x4e, 0xb9, 0x5b, 0xeb, 0xbf, 0x2b, 0x7b, 0xef, 0xad, 0xd7, 0xa6, 0x0e, 0x0e, - 0x78, 0x15, 0xf3, 0x29, 0xfa, 0x21, 0x42, 0x3d, 0x23, 0x83, 0x3c, 0xcb, 0x25, 0x85, 0x1b, 0x51, 0xb8, 0x62, 0x48, - 0x1b, 0xfc, 0x48, 0xe3, 0x3f, 0xe4, 0xff, 0xa7, 0x46, 0x0e, 0x75, 0xda, 0xe0, 0x81, 0x00, 0x16, 0xb2, 0x42, 0x55, - 0xb4, 0x45, 0x03, 0xe9, 0xd0, 0x7c, 0x1b, 0x95, 0x87, 0x39, 0x9d, 0xcf, 0xf3, 0x3b, 0xf3, 0xe0, 0x56, 0xc0, 0x51, - 0x55, 0x17, 0x4d, 0x2e, 0xd4, 0x1d, 0x2e, 0x80, 0xa7, 0x07, 0xdc, 0x43, 0xc6, 0x55, 0xb5, 0xbc, 0xdc, 0x16, 0x08, - 0x24, 0x33, 0x45, 0x64, 0xb3, 0xdd, 0x55, 0x97, 0x20, 0x97, 0x35, 0x9b, 0x48, 0xbb, 0x08, 0xe0, 0x98, 0x83, 0x4c, - 0xa6, 0xac, 0x3b, 0xea, 0x9e, 0x2d, 0x08, 0x92, 0x9b, 0x34, 0x22, 0xdb, 0xee, 0x52, 0x7c, 0x1c, 0x03, 0x1a, 0x21, - 0x2b, 0xf0, 0x85, 0xc2, 0x22, 0x07, 0xae, 0xb3, 0xf0, 0x1d, 0x7f, 0xa3, 0xa5, 0xa2, 0xaf, 0x06, 0x03, 0x5c, 0x98, - 0x37, 0x26, 0xca, 0xf9, 0x14, 0x2a, 0x78, 0xb3, 0x28, 0x10, 0x51, 0xf8, 0x6a, 0xb5, 0x0f, 0x4f, 0x02, 0xb9, 0x36, - 0xc1, 0x7f, 0xd5, 0xfd, 0xac, 0x9e, 0xff, 0x80, 0x71, 0x30, 0xd2, 0x32, 0x17, 0x85, 0x4e, 0xde, 0x64, 0x17, 0xa2, - 0xdb, 0x68, 0x30, 0x13, 0xad, 0x89, 0x40, 0x68, 0x36, 0x70, 0x2e, 0x84, 0x3f, 0x36, 0x00, 0x93, 0x62, 0x36, 0x8c, - 0x1d, 0xc4, 0xd7, 0xae, 0x25, 0xac, 0x56, 0xca, 0x86, 0x49, 0x31, 0x39, 0x36, 0x60, 0x4a, 0xd9, 0x4f, 0x19, 0x8f, - 0xb5, 0x32, 0xe3, 0xe0, 0x6e, 0xab, 0xbf, 0xad, 0xf6, 0xf3, 0x1e, 0xb7, 0xd7, 0x78, 0xdc, 0x04, 0x1f, 0x30, 0x80, - 0x5a, 0x6e, 0x6c, 0x70, 0x6b, 0x2c, 0x1f, 0x5b, 0xcb, 0x5e, 0xb6, 0x09, 0x41, 0x51, 0x3a, 0xdb, 0xdb, 0x9b, 0x5b, - 0x1f, 0x7c, 0x50, 0x99, 0x39, 0x29, 0xa4, 0xfb, 0x20, 0x47, 0x0f, 0x08, 0x74, 0x6e, 0x7f, 0x56, 0x74, 0xa1, 0x92, - 0x89, 0xcb, 0x31, 0xfe, 0x12, 0xdc, 0xe6, 0xf5, 0xa3, 0xeb, 0x6b, 0xb3, 0xc9, 0xaf, 0xaf, 0x23, 0x1c, 0x5a, 0xa8, - 0x47, 0x01, 0x2f, 0x18, 0x0d, 0xca, 0xf8, 0x54, 0x66, 0xe3, 0x37, 0xdb, 0x55, 0x63, 0xef, 0x69, 0x85, 0x77, 0xb0, - 0x3c, 0xa6, 0xf1, 0x2d, 0x8f, 0xce, 0x3e, 0x07, 0x78, 0xb3, 0x3e, 0x1f, 0x74, 0xdf, 0xc4, 0x0a, 0x1d, 0x1c, 0xbc, - 0x89, 0x25, 0xea, 0x5d, 0x31, 0x73, 0xe7, 0x06, 0x2e, 0xdd, 0x7d, 0x6e, 0x86, 0x2f, 0x03, 0x04, 0xb8, 0x62, 0x9b, - 0x92, 0xcd, 0x5b, 0x13, 0x40, 0x23, 0x85, 0x00, 0xdd, 0x10, 0x26, 0xd8, 0x81, 0x04, 0x7a, 0x7d, 0x13, 0x42, 0xbb, - 0xcb, 0x08, 0x03, 0x16, 0xbe, 0x74, 0xb8, 0x63, 0xc9, 0x8c, 0x15, 0x13, 0x56, 0xac, 0x56, 0xef, 0xa9, 0x75, 0xa2, - 0xdb, 0x88, 0xf7, 0xa8, 0xba, 0x8d, 0x06, 0x35, 0xe3, 0x07, 0xf1, 0x81, 0x0e, 0xf0, 0xfe, 0x9b, 0xb8, 0x40, 0x08, - 0x2c, 0x8c, 0xb8, 0x58, 0x78, 0x87, 0xb1, 0xac, 0xb6, 0x2e, 0x05, 0x2a, 0x1b, 0xc9, 0x49, 0x0b, 0x4f, 0x49, 0x56, - 0xae, 0xd1, 0xc5, 0xb4, 0xdb, 0x68, 0xe4, 0x48, 0xc6, 0x59, 0x3f, 0x1f, 0x60, 0x8e, 0x0b, 0xb8, 0x4c, 0xdd, 0x5e, - 0x87, 0x39, 0xab, 0x51, 0x2e, 0x37, 0xdf, 0xa5, 0x1d, 0x6b, 0xfa, 0x88, 0xae, 0x03, 0x60, 0x3c, 0xa2, 0x01, 0x91, - 0xd8, 0x05, 0x64, 0x61, 0x81, 0xac, 0x3c, 0x90, 0x85, 0x01, 0xb2, 0x42, 0xbd, 0x39, 0x44, 0x3e, 0x52, 0x28, 0xdd, - 0xa2, 0xe8, 0xf5, 0x18, 0x9d, 0xce, 0xff, 0x03, 0x73, 0x13, 0x26, 0xc2, 0x2d, 0x07, 0xf8, 0x82, 0x38, 0x97, 0x42, - 0x45, 0x96, 0x51, 0x64, 0xc2, 0xd5, 0xe2, 0x5b, 0xf3, 0x27, 0xb9, 0xc5, 0x77, 0xf6, 0xc7, 0x5d, 0xa0, 0x4c, 0x7a, - 0x5e, 0xd3, 0x36, 0x70, 0x17, 0xdc, 0x2d, 0x4a, 0x22, 0x40, 0x6b, 0x17, 0xa9, 0x50, 0xd4, 0x1f, 0x6f, 0x53, 0x36, - 0xa6, 0x84, 0x68, 0x10, 0x85, 0x45, 0x40, 0x3a, 0xff, 0xfc, 0x33, 0x42, 0x3d, 0x01, 0x21, 0x81, 0xdc, 0xc9, 0xd6, - 0x6c, 0xa3, 0x46, 0x94, 0x44, 0x69, 0xec, 0x23, 0x4e, 0xc0, 0xce, 0x88, 0xa2, 0xe0, 0xe1, 0x96, 0x72, 0x18, 0x1f, - 0x6a, 0xc3, 0x30, 0x83, 0xaa, 0x62, 0x68, 0x5c, 0x2e, 0x37, 0x23, 0x16, 0x19, 0xa8, 0x0a, 0x13, 0x2e, 0x06, 0xd9, - 0xd7, 0xcc, 0x18, 0x61, 0x07, 0x07, 0xac, 0x2f, 0x06, 0xc1, 0xf3, 0x64, 0xd5, 0x75, 0xb8, 0x0e, 0x17, 0x2e, 0xa6, - 0x10, 0x32, 0x7e, 0xb5, 0xb2, 0x7f, 0xc9, 0x07, 0x23, 0xcd, 0xc0, 0x3b, 0x73, 0xc1, 0x19, 0x2b, 0x76, 0xcb, 0x62, - 0x89, 0x96, 0xbf, 0x83, 0xd9, 0x9e, 0x0b, 0x00, 0xc8, 0xdd, 0x54, 0xdb, 0x1e, 0xea, 0x73, 0xa3, 0x51, 0x08, 0xc2, - 0xef, 0x56, 0x47, 0x1a, 0x9e, 0xeb, 0x30, 0xaf, 0x16, 0x46, 0x37, 0x53, 0x65, 0x34, 0x54, 0x38, 0x52, 0x12, 0x30, - 0x41, 0x37, 0x74, 0x12, 0x7e, 0xd4, 0xa9, 0xa4, 0x63, 0x21, 0x01, 0x0a, 0x1c, 0x99, 0xcb, 0x79, 0x13, 0xed, 0x9e, - 0xa1, 0x1d, 0x44, 0x2e, 0x30, 0xa1, 0xa9, 0xcb, 0x96, 0x2e, 0x2c, 0x55, 0x34, 0x93, 0x0b, 0xc5, 0x16, 0x73, 0x38, - 0xdf, 0xcb, 0xb4, 0x2c, 0xe7, 0xd9, 0xe7, 0x7a, 0x0a, 0x58, 0x3b, 0xde, 0xea, 0x19, 0x13, 0x8b, 0xc8, 0xcd, 0xf3, - 0xab, 0x15, 0xf7, 0xdf, 0xbc, 0xc0, 0x8f, 0x48, 0xe7, 0xf0, 0x2b, 0xfe, 0x48, 0xc9, 0xa3, 0xc6, 0x57, 0x3c, 0xe1, - 0xc4, 0xf2, 0x06, 0xc9, 0x9b, 0xd7, 0x57, 0x2f, 0xde, 0xbd, 0x78, 0xff, 0xf4, 0xfa, 0xc5, 0xab, 0x67, 0x2f, 0x5e, - 0xbd, 0x78, 0xf7, 0x11, 0xff, 0x43, 0xc9, 0xd7, 0xa3, 0xf6, 0x79, 0x0b, 0x7f, 0x20, 0x5f, 0x8f, 0x3a, 0xf8, 0x56, - 0x93, 0xaf, 0x47, 0x27, 0x38, 0x57, 0xe4, 0xeb, 0x61, 0xe7, 0xe8, 0x18, 0x2f, 0xb4, 0x6d, 0x32, 0x97, 0x93, 0x76, - 0x0b, 0xff, 0xe3, 0xbe, 0x40, 0xbc, 0xaf, 0x66, 0x31, 0x61, 0x1b, 0xc6, 0x0f, 0xa6, 0x0c, 0x1d, 0x2a, 0x63, 0x88, - 0x72, 0x11, 0xa0, 0xd3, 0x54, 0x85, 0xe8, 0x64, 0xe3, 0x31, 0x83, 0x0d, 0x23, 0xa0, 0x15, 0x27, 0xae, 0x1d, 0x7e, - 0xd4, 0x66, 0xc7, 0x40, 0x9f, 0x78, 0x29, 0x1c, 0x97, 0x2a, 0x9c, 0xb6, 0xd3, 0x62, 0x8c, 0x73, 0x29, 0x8b, 0x78, - 0x01, 0x8c, 0x80, 0xd1, 0x5a, 0xf0, 0xa3, 0x32, 0xf0, 0x93, 0xb8, 0x20, 0xed, 0x5e, 0x3b, 0x15, 0x17, 0xa4, 0xd3, - 0xeb, 0xc0, 0x9f, 0xd3, 0xde, 0x69, 0xda, 0x6e, 0xa1, 0xc3, 0x60, 0x1c, 0x7f, 0xd4, 0xd0, 0xba, 0x3f, 0xc0, 0xae, - 0x0b, 0xf5, 0x4f, 0xa1, 0xbd, 0x4a, 0x4f, 0x38, 0x75, 0x6c, 0xbb, 0x2b, 0x2e, 0x98, 0xd1, 0xc3, 0xf2, 0x1f, 0x00, - 0xb5, 0x8d, 0x6f, 0x4a, 0xb9, 0x71, 0xdc, 0x2f, 0x7e, 0x24, 0x50, 0x2d, 0xba, 0x4c, 0xcc, 0x56, 0x2d, 0x04, 0x4c, - 0xa3, 0xc9, 0x06, 0x73, 0xa0, 0x44, 0xc9, 0x42, 0xfb, 0x08, 0xf9, 0xaa, 0x29, 0x51, 0x32, 0x97, 0xf3, 0xb8, 0xa6, - 0x6a, 0xf8, 0x35, 0x30, 0x73, 0xdc, 0xe7, 0xea, 0x15, 0x7d, 0x15, 0xd7, 0x78, 0x9e, 0x90, 0xb5, 0x0b, 0xb7, 0xc5, - 0x2f, 0xce, 0x8a, 0xa2, 0x06, 0xae, 0x12, 0xb0, 0x7e, 0x54, 0x4d, 0x7d, 0x01, 0x4f, 0x01, 0xb2, 0x86, 0xbe, 0x24, - 0x01, 0xf5, 0xfc, 0xa9, 0x34, 0xe3, 0x2a, 0x95, 0xd1, 0x5e, 0x11, 0x6d, 0xcc, 0x82, 0xbc, 0x22, 0xfa, 0x42, 0x19, - 0x20, 0x48, 0xc2, 0xfb, 0x62, 0x00, 0x07, 0xbe, 0x1d, 0xa0, 0x34, 0x74, 0x0e, 0xd4, 0x4a, 0x95, 0x99, 0x90, 0xf9, - 0x34, 0x71, 0x0e, 0x40, 0xf3, 0x54, 0xa9, 0xa0, 0xcc, 0x27, 0x96, 0x28, 0x18, 0xfa, 0x6f, 0xe1, 0x06, 0x38, 0x8c, - 0x0d, 0x2a, 0x06, 0xd9, 0xf7, 0x44, 0x3d, 0xbf, 0x7d, 0xde, 0x3a, 0xfa, 0x1a, 0xe4, 0x8f, 0x94, 0xb7, 0xf7, 0xf8, - 0x3b, 0xa0, 0xe4, 0x36, 0x32, 0x57, 0x1b, 0xfb, 0xa0, 0x6a, 0xdd, 0x10, 0x20, 0x87, 0x1a, 0x1d, 0x99, 0x57, 0x11, - 0xbb, 0x48, 0x1f, 0x92, 0x76, 0x0b, 0x22, 0xa1, 0xed, 0xa0, 0x7c, 0x3f, 0x6d, 0xc0, 0x54, 0x27, 0xb7, 0x4d, 0xa0, - 0xd5, 0xf0, 0x50, 0xd2, 0x5d, 0x93, 0x27, 0x77, 0x58, 0x05, 0x38, 0xc3, 0x0e, 0x59, 0x43, 0x1c, 0x0a, 0xe4, 0x22, - 0xc8, 0xda, 0x0d, 0xa0, 0xa9, 0xe8, 0xd8, 0x07, 0xfb, 0xbc, 0x71, 0xd4, 0x45, 0x33, 0x39, 0x3d, 0xfc, 0x7a, 0x70, - 0x10, 0xcb, 0x06, 0x79, 0x84, 0xf0, 0x92, 0x82, 0x41, 0x36, 0x38, 0xb0, 0x71, 0xcb, 0xc4, 0xa7, 0x2a, 0xa0, 0x8e, - 0x0b, 0x55, 0x3b, 0xd6, 0xaa, 0xce, 0xca, 0xdd, 0xe0, 0xc7, 0xd4, 0x41, 0x8d, 0x20, 0xcd, 0x8e, 0xae, 0x53, 0x83, - 0x72, 0xcd, 0xdb, 0x0c, 0xb6, 0x65, 0xe3, 0x23, 0x45, 0x3f, 0x3c, 0x6a, 0x7e, 0x0d, 0x26, 0x5c, 0x33, 0x4d, 0x7a, - 0xd4, 0x78, 0x84, 0x7e, 0x78, 0x14, 0xf8, 0x0b, 0xf2, 0x8a, 0x3d, 0xf1, 0xdc, 0xc8, 0x4f, 0x96, 0x2b, 0xfd, 0x09, - 0x24, 0xfb, 0x82, 0xfc, 0x04, 0x58, 0x4e, 0xc9, 0x4f, 0xb1, 0x6c, 0x42, 0x1c, 0x45, 0xf2, 0x53, 0x5c, 0xc0, 0x8f, - 0x9c, 0xfc, 0x14, 0x03, 0xb6, 0xe3, 0xa9, 0xf9, 0x51, 0x94, 0xc0, 0x00, 0x1f, 0x35, 0x69, 0x5d, 0xd5, 0x8a, 0xd5, - 0x4a, 0x1c, 0x1c, 0x48, 0xfb, 0x8b, 0x5e, 0x66, 0x07, 0x07, 0xf9, 0xc5, 0xb4, 0xea, 0x9b, 0xe9, 0x5d, 0xf4, 0xc5, - 0x20, 0x14, 0x0e, 0x4c, 0xd3, 0x78, 0x38, 0xe3, 0x4f, 0x21, 0x65, 0x35, 0x0d, 0x34, 0x8f, 0x3b, 0xf7, 0xcf, 0xce, - 0x31, 0xfc, 0x7b, 0x3f, 0x28, 0xf8, 0x73, 0xc9, 0x77, 0x91, 0x36, 0x6b, 0x9e, 0x55, 0xc8, 0x76, 0x19, 0xe0, 0x33, - 0x66, 0xa8, 0x29, 0x0e, 0x0e, 0xf8, 0x45, 0x80, 0xcb, 0x98, 0xa1, 0x46, 0x60, 0xb1, 0xf7, 0xb0, 0xb4, 0x27, 0x33, - 0x5c, 0x13, 0xbc, 0x90, 0xcb, 0xfb, 0xc5, 0xe0, 0x42, 0x3b, 0x6a, 0x12, 0xc6, 0xd1, 0x56, 0xa4, 0xe5, 0x36, 0x59, - 0x57, 0x34, 0xd5, 0x65, 0xbb, 0x8b, 0x24, 0x51, 0x0d, 0x71, 0x79, 0xd9, 0xc6, 0xa0, 0x92, 0xef, 0x29, 0x22, 0x53, - 0x41, 0xbc, 0xaf, 0xdf, 0x32, 0x97, 0xa9, 0xc2, 0x53, 0x9e, 0x0a, 0x2f, 0x67, 0xbf, 0xf6, 0xd6, 0xd3, 0xc6, 0xfb, - 0xd2, 0xf4, 0xcc, 0xb0, 0xe8, 0xa9, 0xd2, 0x6b, 0x10, 0x36, 0xa9, 0x1a, 0xc0, 0x03, 0x84, 0x25, 0xe6, 0x31, 0xeb, - 0x2a, 0xc7, 0x20, 0xc0, 0xb3, 0x6a, 0xb4, 0x21, 0x13, 0x3e, 0xd7, 0xa9, 0x82, 0x81, 0x9a, 0xc2, 0x17, 0x40, 0xa6, - 0xb2, 0xca, 0x30, 0xdb, 0x37, 0x0c, 0x05, 0x04, 0x14, 0xb8, 0x24, 0x2c, 0x90, 0xe0, 0xe1, 0xf6, 0x23, 0x20, 0x1c, - 0x75, 0x72, 0x61, 0x27, 0x77, 0xa1, 0xa0, 0x3b, 0x31, 0xb8, 0xd0, 0x5d, 0x24, 0x1a, 0x0d, 0xc7, 0x6d, 0x5f, 0x0a, - 0x33, 0x88, 0x66, 0x7b, 0x70, 0xc9, 0xba, 0x48, 0x35, 0x9b, 0xa5, 0x01, 0xe4, 0x65, 0x6b, 0xb5, 0x52, 0x17, 0xbe, - 0x91, 0x9e, 0x3f, 0xc7, 0x0d, 0xdf, 0xe5, 0x05, 0xcf, 0xdf, 0x24, 0xe9, 0x47, 0x40, 0x55, 0x81, 0xcf, 0x96, 0xf3, - 0x08, 0x47, 0xe6, 0x6d, 0x3a, 0xf8, 0x6b, 0xde, 0x14, 0x8b, 0x70, 0xe4, 0x9e, 0xab, 0x8b, 0x06, 0xd5, 0x60, 0x79, - 0x56, 0x46, 0x5a, 0xe7, 0xc9, 0x35, 0x30, 0x0e, 0xfa, 0x6f, 0x85, 0x96, 0xd5, 0xef, 0x24, 0x77, 0x31, 0x47, 0x94, - 0x7f, 0x41, 0xcd, 0x8d, 0x6a, 0xbd, 0xdb, 0xcb, 0x93, 0xe3, 0xc8, 0x57, 0x85, 0x97, 0x08, 0xbe, 0xf3, 0x84, 0x63, - 0xdb, 0xbd, 0x1c, 0xbe, 0x2c, 0x7b, 0x00, 0xce, 0x7b, 0xbd, 0x46, 0xf8, 0x37, 0xb9, 0xf3, 0x19, 0xe1, 0xe8, 0x5a, - 0x8a, 0x27, 0x54, 0xd3, 0xa8, 0xf1, 0xc6, 0x18, 0xbe, 0x59, 0x39, 0xab, 0xfb, 0xad, 0x71, 0xb0, 0x7f, 0xab, 0x7b, - 0x88, 0x02, 0x51, 0x7b, 0xf1, 0xc8, 0xca, 0xbe, 0x26, 0xf6, 0x87, 0x0c, 0x4c, 0xdf, 0x76, 0xc0, 0xc3, 0x8f, 0x91, - 0x82, 0x6f, 0xb2, 0xe5, 0x93, 0x28, 0x84, 0x57, 0xad, 0x79, 0x44, 0x43, 0x8a, 0xed, 0xc3, 0x78, 0xcf, 0xae, 0x51, - 0xc8, 0x75, 0x8f, 0x55, 0x9d, 0x98, 0x56, 0xdd, 0x18, 0xa9, 0x83, 0x6d, 0xb2, 0xe0, 0xac, 0xea, 0xdd, 0x48, 0x28, - 0xd5, 0xe3, 0x70, 0xe6, 0x81, 0xcf, 0x66, 0xdb, 0xbc, 0x98, 0x6c, 0x9f, 0x90, 0x53, 0x60, 0xc8, 0xbb, 0x5f, 0x46, - 0xbe, 0xba, 0x84, 0x63, 0x37, 0x0e, 0x20, 0x2b, 0xc9, 0xe5, 0xd2, 0x3d, 0xef, 0xc6, 0xfb, 0x72, 0xb0, 0x2e, 0x1f, - 0x7b, 0x0b, 0xf0, 0xa0, 0x1a, 0xa9, 0xc8, 0x42, 0xce, 0xc0, 0xbf, 0x94, 0x58, 0xd3, 0x0f, 0xf1, 0xaf, 0x70, 0xc0, - 0x57, 0x48, 0x9a, 0x5a, 0xf5, 0x13, 0x3c, 0xc2, 0x04, 0x0a, 0x6f, 0x5b, 0xf7, 0x93, 0x0c, 0xbd, 0x5d, 0xeb, 0x3a, - 0x15, 0xeb, 0x50, 0x5a, 0x57, 0xac, 0x94, 0x85, 0x83, 0xe3, 0x2e, 0x46, 0xeb, 0xd4, 0x39, 0x9f, 0xba, 0x97, 0x9b, - 0x1e, 0x0a, 0x70, 0x7c, 0xe1, 0x52, 0x3c, 0x2b, 0x20, 0x14, 0x57, 0xa8, 0x4f, 0xfb, 0x59, 0x86, 0x4f, 0x13, 0xf7, - 0xe1, 0x9e, 0xb0, 0xe4, 0x39, 0xcb, 0xe7, 0xb4, 0x61, 0x81, 0x14, 0x50, 0x28, 0x85, 0xc5, 0x6a, 0x15, 0x0b, 0x13, - 0xa0, 0xc1, 0xc5, 0xe7, 0x75, 0x0f, 0x71, 0x18, 0xfd, 0x1d, 0xd4, 0xc5, 0x5e, 0x3d, 0x62, 0x4c, 0x58, 0x51, 0x78, - 0xe9, 0xa4, 0xb2, 0xa0, 0xaf, 0x5d, 0x7d, 0x88, 0x6a, 0xca, 0xbd, 0xd8, 0xe8, 0x7b, 0xdf, 0xf1, 0x19, 0x93, 0x0b, - 0x78, 0x01, 0x09, 0x33, 0xa2, 0x98, 0xf6, 0xdf, 0x40, 0x41, 0xe0, 0x19, 0x1d, 0x1e, 0xe2, 0x23, 0xf0, 0x55, 0x9e, - 0xd6, 0xc9, 0xcc, 0xbf, 0xab, 0x11, 0x99, 0xb8, 0x97, 0x51, 0x2f, 0x02, 0x17, 0x21, 0x10, 0xa1, 0x08, 0x89, 0x98, - 0x18, 0x45, 0xbd, 0xc8, 0xf8, 0x5b, 0x45, 0x60, 0x35, 0x06, 0x4a, 0xee, 0x08, 0xcf, 0x55, 0x45, 0xc4, 0xc2, 0x9a, - 0x3a, 0xa8, 0xc4, 0x52, 0x63, 0xa6, 0x7d, 0xd4, 0xa9, 0x40, 0x58, 0x64, 0x9b, 0x82, 0xb2, 0xde, 0x50, 0x17, 0x60, - 0x49, 0x8c, 0xe9, 0x2d, 0x4f, 0xae, 0x81, 0x9b, 0x63, 0x23, 0x57, 0x74, 0xc9, 0xaf, 0x40, 0x3d, 0x9d, 0x16, 0xf8, - 0xda, 0x30, 0x6c, 0xa3, 0x94, 0xae, 0x09, 0xc7, 0x19, 0x29, 0x12, 0x7a, 0x0b, 0x01, 0x2a, 0x66, 0x5c, 0xa4, 0x39, - 0x9e, 0xd1, 0xdb, 0x74, 0x8a, 0x67, 0x5c, 0x3c, 0xb1, 0xcb, 0x9e, 0x8e, 0x20, 0xc9, 0x7f, 0x2c, 0xd6, 0xc4, 0xbc, - 0xaf, 0xf5, 0xbb, 0x62, 0xc5, 0x23, 0xe0, 0x55, 0x54, 0x8c, 0xba, 0x23, 0x63, 0x53, 0xce, 0x74, 0x65, 0xbc, 0xfe, - 0x5a, 0xc7, 0x14, 0x67, 0x38, 0x47, 0x49, 0x2e, 0x31, 0xeb, 0x89, 0xf4, 0x35, 0x04, 0xa7, 0xce, 0xb0, 0x7d, 0x9b, - 0x8b, 0xdf, 0xb2, 0xfc, 0x99, 0x2c, 0xde, 0x9b, 0x2d, 0x9f, 0x23, 0x28, 0x04, 0x2e, 0x2a, 0xa2, 0x09, 0xb7, 0x7b, - 0x8b, 0x9e, 0xac, 0x9a, 0xa2, 0xb7, 0xb6, 0x29, 0x37, 0xc4, 0x29, 0x44, 0xf5, 0x4d, 0xa6, 0xbc, 0xd1, 0xc6, 0xac, - 0xd7, 0xfa, 0x4e, 0xa3, 0x53, 0x54, 0x96, 0x44, 0x18, 0xd6, 0xaa, 0xa9, 0x52, 0x49, 0x44, 0x53, 0x39, 0x09, 0x6f, - 0x69, 0x80, 0x9d, 0x2a, 0x9c, 0xc9, 0x85, 0xd0, 0xa9, 0x0c, 0xf0, 0x86, 0x56, 0x9b, 0x6b, 0x79, 0x6b, 0x21, 0xa6, - 0xf1, 0x9d, 0xfd, 0xc1, 0xf0, 0xb5, 0x51, 0xf1, 0xbf, 0x05, 0xc3, 0x1e, 0x95, 0x0a, 0x80, 0x1f, 0x18, 0xce, 0x02, - 0xe4, 0x2c, 0x3f, 0x79, 0x0b, 0xe0, 0xb3, 0x2c, 0xe4, 0x1d, 0xa4, 0x32, 0x93, 0x7a, 0x07, 0xa9, 0x0c, 0x52, 0x8d, - 0x5b, 0xfa, 0xbe, 0xa8, 0x94, 0x45, 0x61, 0x83, 0x44, 0xe1, 0x52, 0x1d, 0x2c, 0x89, 0x48, 0xa0, 0x5d, 0x23, 0xca, - 0xcd, 0xb8, 0x80, 0xf8, 0x84, 0xd0, 0xb8, 0xfd, 0xa6, 0xb7, 0xf0, 0x7d, 0x67, 0xf3, 0x99, 0xcf, 0xbf, 0xb3, 0xf9, - 0xa6, 0x23, 0x8f, 0xf1, 0xf5, 0xdb, 0x4e, 0x63, 0x19, 0x2f, 0x1d, 0xd6, 0x7e, 0x28, 0x5f, 0x83, 0x69, 0x99, 0x57, - 0xb7, 0x49, 0x1b, 0x4f, 0x02, 0xa4, 0x6c, 0x56, 0x3c, 0x5c, 0x07, 0xb7, 0x5b, 0x87, 0x31, 0x6f, 0x92, 0x36, 0x42, - 0x87, 0x4e, 0xb8, 0x12, 0xb1, 0x91, 0x9c, 0x0e, 0x1f, 0x1d, 0xc1, 0xdd, 0xcb, 0x4c, 0x6d, 0xf8, 0x4a, 0xd9, 0x6a, - 0xcd, 0x76, 0xeb, 0x90, 0xef, 0xac, 0xd2, 0x68, 0xe3, 0x19, 0x23, 0x4b, 0x70, 0x2e, 0xa3, 0x85, 0x55, 0x35, 0x80, - 0x3f, 0xea, 0x0b, 0xf1, 0xdb, 0x82, 0x8e, 0xcc, 0xf7, 0xa1, 0x4d, 0x79, 0xbd, 0xd0, 0x3e, 0xa9, 0xc9, 0x61, 0x10, - 0x1d, 0xe4, 0x4a, 0x06, 0x39, 0x31, 0x3f, 0x22, 0xc9, 0x29, 0xba, 0x68, 0xf7, 0x92, 0xd3, 0x43, 0x7e, 0xc8, 0x53, - 0xe0, 0x61, 0xe3, 0xa6, 0xaf, 0xd0, 0x6c, 0xfb, 0x3a, 0x8f, 0x17, 0x43, 0x9e, 0xb9, 0xe6, 0xab, 0x0e, 0xca, 0x54, - 0x3b, 0x47, 0xc8, 0x02, 0x14, 0xf3, 0xbd, 0x04, 0xd9, 0xf5, 0x6e, 0x0e, 0x79, 0x0a, 0xfd, 0x40, 0xad, 0x8e, 0xad, - 0x55, 0x0e, 0xee, 0xb7, 0x05, 0x20, 0x98, 0xef, 0xa8, 0x36, 0x17, 0x9b, 0xde, 0x8c, 0xab, 0xce, 0x0e, 0x79, 0x35, - 0xc2, 0xb0, 0xcc, 0x76, 0x7f, 0x7e, 0x6a, 0x55, 0x97, 0x87, 0x01, 0x44, 0x7e, 0x5b, 0x70, 0x11, 0x76, 0x1a, 0x76, - 0xeb, 0x72, 0xc2, 0x4e, 0xeb, 0xb3, 0x0c, 0x8a, 0x6c, 0xf7, 0xba, 0x35, 0xd3, 0xfa, 0x6c, 0xaf, 0xc0, 0x47, 0x10, - 0x26, 0x65, 0x56, 0x3a, 0x83, 0x2b, 0xf4, 0xc3, 0x0f, 0xc8, 0xb5, 0xfe, 0x7a, 0xa1, 0x7d, 0x7e, 0x89, 0x08, 0x90, - 0x5d, 0x75, 0x5d, 0x56, 0x87, 0x3e, 0xca, 0x26, 0xbe, 0x1e, 0xf2, 0x60, 0xe5, 0x9e, 0xde, 0xce, 0x65, 0xea, 0xf1, - 0xb5, 0xd7, 0x4a, 0xb7, 0x90, 0x13, 0x88, 0x87, 0xeb, 0x2e, 0x2c, 0x0b, 0x72, 0x76, 0x73, 0x0b, 0x25, 0xc3, 0x89, - 0xfb, 0xd2, 0x1f, 0x98, 0xbd, 0x6e, 0xe0, 0x17, 0xc9, 0x29, 0x4c, 0x7d, 0xb3, 0x87, 0xc3, 0x0e, 0xf4, 0x61, 0xe0, - 0xb0, 0xd9, 0xa0, 0xcf, 0xac, 0x20, 0xf2, 0x98, 0x17, 0x16, 0xcf, 0x2e, 0x49, 0xbb, 0xc7, 0x53, 0xb7, 0x99, 0x8c, - 0x68, 0xd4, 0x6e, 0xf2, 0x60, 0x66, 0x80, 0x5f, 0xae, 0x6c, 0x58, 0xc4, 0xaf, 0x53, 0x00, 0x25, 0x5f, 0xac, 0x5a, - 0x9f, 0x0a, 0x5e, 0xf5, 0x86, 0xd3, 0xcd, 0x74, 0xbf, 0x6e, 0x70, 0xbb, 0xeb, 0xe1, 0x09, 0xaf, 0xb9, 0x58, 0xb4, - 0xf6, 0x13, 0x9f, 0x00, 0x07, 0x94, 0xb4, 0xee, 0x9f, 0x82, 0x0b, 0x65, 0x09, 0xcb, 0xed, 0x72, 0xb3, 0xad, 0x72, - 0x16, 0x8e, 0xb6, 0x64, 0xc0, 0x1d, 0x6c, 0x42, 0x14, 0x3a, 0x38, 0xec, 0xe0, 0xa4, 0xdd, 0xee, 0x9c, 0xe2, 0xe4, - 0xe4, 0x14, 0x06, 0xda, 0x48, 0x4e, 0x0f, 0x67, 0xca, 0x02, 0x30, 0xc8, 0x59, 0xbb, 0x76, 0x1f, 0x41, 0xe4, 0xa7, - 0x50, 0xbc, 0xe6, 0x87, 0x71, 0xdc, 0x4e, 0xee, 0xb7, 0xda, 0xa7, 0xe7, 0x0d, 0x00, 0x50, 0xd3, 0x7d, 0xb8, 0x1a, - 0xaf, 0x17, 0xba, 0x5e, 0xa5, 0x44, 0xf8, 0x7a, 0xb5, 0x86, 0xaf, 0xd6, 0x68, 0xaf, 0xab, 0x29, 0xf8, 0xaa, 0x4e, - 0x38, 0xb7, 0x45, 0xbc, 0xd2, 0x26, 0xdc, 0x16, 0xb1, 0x1d, 0x48, 0x0c, 0xd2, 0x79, 0x72, 0xda, 0x39, 0x45, 0x76, - 0x2c, 0xda, 0xe1, 0x47, 0xb9, 0x4f, 0xb6, 0x8a, 0x34, 0x34, 0x20, 0x49, 0x39, 0x3b, 0xb9, 0x00, 0x89, 0x9a, 0x93, - 0xcb, 0x76, 0x73, 0xc6, 0x12, 0x3f, 0x01, 0x93, 0x0a, 0xcb, 0x59, 0xae, 0x82, 0x4b, 0x0a, 0x00, 0x71, 0x01, 0xc6, - 0x45, 0xf7, 0x4f, 0x7b, 0xf7, 0x93, 0xd3, 0xb3, 0x8e, 0x25, 0x7a, 0xfc, 0xa2, 0x53, 0x4b, 0x33, 0x53, 0x4f, 0x4e, - 0x4d, 0x1a, 0x74, 0x9d, 0xdc, 0x3f, 0x85, 0x32, 0x2e, 0x25, 0x2c, 0x05, 0x11, 0x2b, 0xaa, 0x62, 0x10, 0xa6, 0x22, - 0xad, 0xe5, 0x9e, 0xd5, 0xb2, 0xcf, 0x4f, 0x8e, 0xef, 0x9f, 0x86, 0x50, 0x2b, 0x67, 0x61, 0x16, 0xda, 0x4d, 0xc4, - 0xcf, 0x0e, 0x96, 0x16, 0x1d, 0x26, 0xa7, 0xe9, 0xd6, 0x04, 0xed, 0xa6, 0x39, 0x34, 0x38, 0x10, 0x28, 0x1c, 0x9f, - 0x0a, 0xa7, 0x2f, 0x09, 0xee, 0xc7, 0x2a, 0x43, 0x93, 0x50, 0xe1, 0xec, 0xef, 0x29, 0x83, 0x47, 0x29, 0xc3, 0xab, - 0xca, 0xc7, 0x54, 0x7c, 0xa1, 0xea, 0x0d, 0x85, 0x30, 0x1c, 0x62, 0x10, 0xb9, 0x20, 0xe1, 0xf5, 0xdc, 0x9f, 0xc0, - 0x45, 0x98, 0x09, 0xb8, 0xd0, 0xf4, 0x4a, 0xd0, 0x8a, 0x17, 0x18, 0x86, 0x0e, 0xb5, 0x66, 0x58, 0x3d, 0x9e, 0x3a, - 0x93, 0x82, 0x50, 0xb7, 0xf5, 0x9c, 0x7f, 0xaf, 0x5c, 0x52, 0x5e, 0x65, 0x27, 0xa7, 0x28, 0x71, 0x97, 0xe5, 0x49, - 0x1b, 0x25, 0x81, 0x09, 0x89, 0x3b, 0x92, 0xb3, 0x8c, 0xf4, 0xa3, 0xdb, 0x08, 0x47, 0x77, 0x11, 0x8e, 0xac, 0x0f, - 0xf3, 0x07, 0x70, 0x10, 0x8f, 0x70, 0x64, 0x5d, 0x99, 0x23, 0x1c, 0x69, 0x26, 0x20, 0x3a, 0x57, 0x34, 0xc0, 0x39, - 0x94, 0x36, 0x9e, 0xd5, 0x65, 0xe9, 0xc7, 0xfe, 0xab, 0x74, 0xbd, 0xb6, 0x29, 0x81, 0x94, 0x39, 0x35, 0x3b, 0xd4, - 0xbe, 0x2e, 0x1d, 0x51, 0xcf, 0xac, 0x47, 0x18, 0x04, 0x10, 0x7a, 0xe7, 0x5f, 0xa7, 0xab, 0x02, 0x7b, 0xb0, 0x63, - 0x58, 0x69, 0xf0, 0x33, 0x8f, 0xc2, 0x33, 0x2c, 0xc2, 0x63, 0xe1, 0x0b, 0x83, 0x58, 0xe1, 0x7f, 0xe7, 0x52, 0xce, - 0xfd, 0x6f, 0x2d, 0xcb, 0x5f, 0xf0, 0xa6, 0x89, 0xb3, 0x68, 0x01, 0xcb, 0x2d, 0x1b, 0x47, 0x68, 0xc8, 0xea, 0x23, - 0xb8, 0x1e, 0xbb, 0x58, 0x6f, 0x20, 0x11, 0x5e, 0x1b, 0x81, 0xca, 0xcb, 0x87, 0xd7, 0x36, 0xee, 0x90, 0xf9, 0x84, - 0xc0, 0x63, 0x10, 0x5b, 0x58, 0xc2, 0x85, 0xc6, 0xa4, 0x60, 0x4a, 0x45, 0x36, 0x20, 0x5f, 0x24, 0x85, 0x7f, 0x61, - 0xd1, 0xa7, 0x8c, 0x45, 0x64, 0x3a, 0xac, 0xcf, 0xd6, 0x8a, 0xc3, 0xb9, 0x2c, 0x54, 0x6a, 0x9f, 0x5b, 0xf1, 0x60, - 0x9c, 0x97, 0x6f, 0x19, 0xa6, 0x79, 0xb6, 0xc6, 0xf6, 0x0e, 0xbb, 0x2c, 0xe4, 0xae, 0xb4, 0xc3, 0x52, 0x59, 0xb6, - 0xfe, 0xd6, 0x84, 0x54, 0x6d, 0x46, 0xc1, 0x44, 0xab, 0x01, 0x55, 0x51, 0x39, 0xa0, 0xb0, 0x8d, 0xec, 0x92, 0x2e, - 0xcb, 0x92, 0xe9, 0xb2, 0x5c, 0x86, 0x93, 0x56, 0x6b, 0xbd, 0xc6, 0x05, 0x33, 0x11, 0x66, 0x76, 0x96, 0x80, 0x7c, - 0x35, 0x95, 0x37, 0x41, 0xae, 0x4a, 0xcb, 0x59, 0x9a, 0x25, 0x8a, 0x02, 0x23, 0xd8, 0x68, 0x8d, 0xbf, 0x70, 0xc5, - 0x01, 0x9e, 0x6e, 0x76, 0x43, 0x29, 0x73, 0x46, 0x21, 0x10, 0x59, 0xd0, 0xe4, 0x1a, 0x4f, 0xf9, 0x88, 0xed, 0x6e, - 0x13, 0xcc, 0x98, 0xff, 0xbd, 0x16, 0x3d, 0x02, 0x59, 0x76, 0xcf, 0xa0, 0x0e, 0x2c, 0xe2, 0x0a, 0x3a, 0x08, 0x65, - 0xf0, 0x51, 0x88, 0x9b, 0x39, 0xbd, 0x93, 0x0b, 0x0d, 0x70, 0x59, 0x68, 0xf9, 0xc6, 0xc5, 0x3a, 0xd8, 0x6f, 0x61, - 0x1f, 0xf6, 0x60, 0x09, 0x21, 0x03, 0x5a, 0xd8, 0x86, 0xbb, 0x68, 0xe1, 0xa1, 0xd4, 0x5a, 0xce, 0xd2, 0x16, 0x36, - 0xb1, 0x27, 0x5a, 0xeb, 0x32, 0x40, 0xd8, 0x75, 0xf9, 0x2e, 0x65, 0xb5, 0x09, 0x16, 0x4e, 0x3a, 0xd4, 0x44, 0x07, - 0xb7, 0x87, 0x8c, 0xf0, 0xc6, 0xcf, 0x57, 0xaf, 0x5f, 0xb9, 0xf0, 0xcf, 0x7c, 0x0c, 0x2e, 0x9b, 0x4e, 0x35, 0x76, - 0x6d, 0x1e, 0x74, 0x8a, 0x2b, 0x45, 0xa9, 0x15, 0x4e, 0xa1, 0xe5, 0x17, 0x42, 0xe7, 0x89, 0xbd, 0xbc, 0x78, 0x26, - 0x8b, 0x19, 0xb5, 0x37, 0x46, 0xf8, 0x5a, 0xb9, 0x17, 0xdc, 0xcd, 0x23, 0x31, 0xd5, 0x24, 0xdf, 0x6d, 0x5e, 0x45, - 0x2c, 0x32, 0x23, 0xbf, 0x82, 0x36, 0xc0, 0x54, 0x2e, 0x1f, 0xe0, 0x2d, 0x88, 0x0b, 0xa2, 0x1f, 0x90, 0x97, 0xb7, - 0x96, 0xba, 0x44, 0x51, 0x83, 0x1b, 0xfc, 0x64, 0x05, 0xcf, 0x82, 0xeb, 0x42, 0xc3, 0x1e, 0x39, 0xf1, 0x22, 0x6a, - 0x45, 0xf5, 0x07, 0x6c, 0x8d, 0x2a, 0xc1, 0x07, 0x60, 0x4d, 0x72, 0x09, 0xa2, 0x47, 0xf9, 0x56, 0x1e, 0x07, 0xd1, - 0xc4, 0xdf, 0x3d, 0x5f, 0xb6, 0x3d, 0x9d, 0xcd, 0x2b, 0x75, 0x62, 0x79, 0x65, 0x02, 0x1e, 0x8e, 0xf6, 0x35, 0x1a, - 0x84, 0x83, 0x44, 0x56, 0x6a, 0x0f, 0x7d, 0x2e, 0xea, 0xc6, 0xf9, 0x45, 0x9b, 0x35, 0x4f, 0x56, 0xab, 0xfc, 0xb2, - 0xcd, 0xda, 0xa7, 0xf6, 0xed, 0xba, 0x48, 0x65, 0x40, 0x73, 0xf9, 0x98, 0x67, 0x11, 0x68, 0x67, 0xc7, 0x99, 0x09, - 0xa7, 0xe0, 0xa3, 0x2d, 0x93, 0x85, 0xae, 0xfa, 0x92, 0x60, 0x5c, 0x4a, 0xac, 0x1e, 0xbf, 0x40, 0xbd, 0x76, 0xba, - 0xed, 0x2a, 0xdd, 0x6c, 0x1f, 0x06, 0x17, 0x2e, 0x05, 0xc2, 0x1d, 0x08, 0x79, 0x00, 0xfa, 0xdd, 0xa5, 0x00, 0xd3, - 0x20, 0x40, 0x65, 0x05, 0x22, 0x2d, 0x9f, 0x2d, 0x66, 0xcf, 0x0a, 0x6a, 0x96, 0xe1, 0x09, 0x9f, 0x70, 0xad, 0x52, - 0x0a, 0xd2, 0xed, 0xae, 0xf4, 0xf5, 0x6e, 0x09, 0x2a, 0xab, 0x05, 0xb1, 0x4d, 0x34, 0xcf, 0x3e, 0x2b, 0xb7, 0x70, - 0x08, 0x9b, 0x95, 0x15, 0x38, 0x43, 0x6b, 0x9c, 0xcb, 0x09, 0x2d, 0xb8, 0x9e, 0xce, 0xfe, 0xad, 0xd5, 0x61, 0x7d, - 0x3d, 0x30, 0x17, 0x56, 0x00, 0x12, 0x2a, 0x46, 0xab, 0x15, 0x3f, 0xfa, 0xfe, 0x7d, 0x92, 0xf7, 0x09, 0x6f, 0xe3, - 0x0e, 0x3e, 0xc6, 0xa7, 0xb8, 0xdd, 0xc2, 0xed, 0x53, 0xb8, 0xba, 0xcf, 0xf2, 0xc5, 0x88, 0xa9, 0x18, 0x1e, 0x31, - 0xd3, 0x97, 0xc9, 0xf9, 0x61, 0x19, 0xba, 0x5f, 0x17, 0x89, 0x43, 0x97, 0x20, 0x82, 0xbc, 0x0b, 0xbd, 0x17, 0x45, - 0x61, 0xdc, 0xb7, 0x71, 0xa8, 0x3a, 0x29, 0xf5, 0x0b, 0x97, 0xc7, 0x3d, 0xb0, 0xe7, 0xb6, 0x2b, 0xdb, 0x04, 0xb3, - 0x6f, 0xfb, 0x33, 0xad, 0x7e, 0x36, 0x75, 0x89, 0x18, 0x1e, 0x7a, 0x15, 0x7a, 0xa0, 0x4b, 0xd2, 0x3e, 0x38, 0x00, - 0xab, 0xa3, 0x60, 0x36, 0xdc, 0x46, 0x3f, 0xe0, 0xcd, 0x5a, 0x1a, 0x04, 0x2b, 0x00, 0xe3, 0xce, 0x37, 0x9c, 0x2c, - 0x2d, 0x6c, 0x35, 0x50, 0x61, 0x5d, 0x84, 0xc1, 0xe9, 0x42, 0x52, 0x61, 0x84, 0x68, 0x38, 0xc2, 0x5c, 0xa4, 0x93, - 0xfd, 0x16, 0x96, 0xe3, 0xb1, 0x62, 0x1a, 0x8e, 0x8e, 0x82, 0x7d, 0x61, 0x85, 0x32, 0xa7, 0xc8, 0x90, 0x4d, 0xb8, - 0x78, 0xa8, 0x3f, 0xb1, 0x42, 0x9a, 0x4f, 0xa3, 0xc1, 0x48, 0x23, 0xb3, 0x8a, 0x11, 0xce, 0x72, 0x3e, 0x87, 0xaa, - 0x93, 0x02, 0x9c, 0x7e, 0xe0, 0x2f, 0x1f, 0xa5, 0x61, 0x9b, 0x40, 0xbe, 0x3e, 0xd8, 0x80, 0x2d, 0x78, 0x54, 0xd0, - 0x9b, 0xd7, 0xe2, 0x31, 0xec, 0xa8, 0x87, 0x05, 0xa3, 0x90, 0x0d, 0x49, 0xef, 0xa0, 0x29, 0xf8, 0x80, 0x36, 0x5f, - 0x1a, 0xc0, 0xa5, 0xe7, 0xe6, 0xc3, 0x56, 0xf4, 0x01, 0x10, 0x93, 0xb2, 0x2d, 0x93, 0x69, 0x4e, 0xe9, 0x2a, 0xd3, - 0x86, 0x98, 0x2a, 0xa7, 0xb0, 0xc6, 0x2e, 0xea, 0x49, 0x38, 0x98, 0x11, 0x55, 0xd3, 0xb4, 0x3f, 0x30, 0x7f, 0x5f, - 0xdb, 0x92, 0x2d, 0xec, 0xc2, 0xc9, 0xac, 0xb1, 0x79, 0x7d, 0x34, 0x28, 0xdf, 0xc6, 0x70, 0x0f, 0x0b, 0x4f, 0x60, - 0xd6, 0xc8, 0xe7, 0x89, 0x27, 0x9b, 0x27, 0xeb, 0xb5, 0x19, 0x88, 0x4a, 0x41, 0x0f, 0xf4, 0xd6, 0x6f, 0x9b, 0x16, - 0x6c, 0x8f, 0xf2, 0xeb, 0xb4, 0x85, 0x67, 0x1c, 0x5e, 0xf4, 0xf4, 0xed, 0x5d, 0xe9, 0x42, 0x7e, 0x76, 0x20, 0x69, - 0x05, 0x29, 0x76, 0x3a, 0x41, 0x67, 0xc7, 0x38, 0x18, 0x39, 0xd0, 0xf3, 0xab, 0xcf, 0x16, 0xd6, 0xfe, 0xf7, 0x9b, - 0xb2, 0xa0, 0x09, 0x96, 0x53, 0x4e, 0x28, 0xf3, 0xe7, 0xe7, 0x1b, 0x9e, 0x54, 0xa8, 0xe0, 0x9e, 0xc2, 0x82, 0x3d, - 0x6d, 0xa3, 0x65, 0xce, 0xe8, 0xdf, 0xf6, 0x87, 0x0d, 0xd0, 0x53, 0x6a, 0xd9, 0xb2, 0x42, 0x2a, 0xf5, 0xd0, 0xa6, - 0xd9, 0xa3, 0x07, 0x8e, 0xc8, 0x97, 0xd0, 0x05, 0xf0, 0xfa, 0xa3, 0x42, 0xce, 0x0d, 0x22, 0xb8, 0xdf, 0x6e, 0xdc, - 0xc6, 0x57, 0x00, 0xbc, 0x1d, 0xf6, 0xaa, 0x7f, 0x5a, 0xc0, 0xfe, 0x46, 0x65, 0x49, 0x3f, 0xde, 0x8e, 0x3d, 0xfe, - 0x0b, 0x09, 0xa1, 0xd7, 0x2d, 0x1e, 0x26, 0x0e, 0x9d, 0x4a, 0xd6, 0xac, 0xfc, 0xb9, 0x55, 0x12, 0x30, 0xac, 0x5e, - 0x30, 0x64, 0xe3, 0xb6, 0x8a, 0xdb, 0xcc, 0xff, 0xa0, 0x82, 0xc1, 0x82, 0x6f, 0x8d, 0xa4, 0x62, 0x59, 0xfc, 0xf6, - 0xa9, 0xf3, 0x5f, 0x75, 0x8e, 0x6b, 0x5f, 0xd7, 0x9e, 0xdb, 0x1c, 0x9a, 0x50, 0xc7, 0x11, 0x3a, 0x38, 0xd8, 0xc8, - 0xa0, 0x63, 0x00, 0x3c, 0x72, 0xec, 0x97, 0x5f, 0x3e, 0xcf, 0x8e, 0x19, 0xcd, 0x63, 0x11, 0x85, 0xcc, 0x9d, 0xe7, - 0xe6, 0xec, 0x44, 0x9e, 0x50, 0x35, 0xf5, 0x85, 0x01, 0x8e, 0x8f, 0xb6, 0x52, 0x01, 0xdf, 0xa3, 0xf5, 0x8e, 0x09, - 0x6c, 0xf0, 0x5b, 0x76, 0x52, 0xbb, 0x0a, 0xfa, 0x05, 0x5a, 0xee, 0x62, 0x2a, 0x37, 0x16, 0x38, 0xda, 0x9c, 0xc8, - 0xce, 0xa1, 0x6f, 0xd4, 0x29, 0x59, 0x8f, 0x27, 0xbb, 0x8d, 0xbe, 0xa4, 0xd8, 0x95, 0x5c, 0xd1, 0xb6, 0x21, 0xab, - 0x9e, 0xdc, 0xd5, 0x95, 0xa9, 0x53, 0x75, 0xcd, 0x5b, 0x59, 0xda, 0x94, 0x76, 0x49, 0xf6, 0x6e, 0x8b, 0x85, 0x57, - 0xe1, 0x8d, 0x46, 0x79, 0x11, 0x0a, 0xf6, 0x58, 0x62, 0xd0, 0xe5, 0x04, 0xae, 0x17, 0x56, 0xab, 0x18, 0xfe, 0xec, - 0x1a, 0xc3, 0x2e, 0xd3, 0xa5, 0x0f, 0x7c, 0x83, 0x5f, 0x09, 0xa2, 0xfe, 0x3a, 0x3b, 0x48, 0xb0, 0xee, 0x72, 0x83, - 0x86, 0xe3, 0xc4, 0x7f, 0xc1, 0x9b, 0xd3, 0xda, 0xbb, 0x1c, 0x4c, 0xb2, 0x6f, 0xbc, 0x53, 0x57, 0xb2, 0x96, 0xb5, - 0x90, 0xf1, 0x1b, 0x12, 0x0c, 0xb1, 0x9b, 0xd2, 0x39, 0x6e, 0x25, 0x6d, 0x14, 0xb9, 0x62, 0x15, 0xfa, 0x7f, 0xab, - 0x48, 0x66, 0x33, 0xff, 0xeb, 0xec, 0xec, 0xcc, 0xa5, 0x38, 0x9b, 0x3f, 0x65, 0x3c, 0xe0, 0x4c, 0x02, 0xfb, 0xc2, - 0x33, 0x66, 0x74, 0xc8, 0x6f, 0x61, 0x28, 0x44, 0x90, 0x4b, 0xe1, 0xd8, 0x25, 0x78, 0x32, 0x11, 0x28, 0x0f, 0xb0, - 0x7f, 0x4f, 0x36, 0xca, 0xf9, 0x37, 0x97, 0x7c, 0x4c, 0xe2, 0xb2, 0x41, 0xf6, 0xc5, 0x7c, 0xf6, 0xad, 0x99, 0x0c, - 0x3c, 0x33, 0x10, 0x61, 0xfb, 0xdb, 0xb0, 0xb4, 0xce, 0x52, 0x06, 0x47, 0x5a, 0x2e, 0xb2, 0xa9, 0xd5, 0xfc, 0xbb, - 0x0f, 0x53, 0xd6, 0xbd, 0xd7, 0x03, 0x41, 0xb9, 0xc8, 0xd2, 0x85, 0xd6, 0x8c, 0x7e, 0x2c, 0xa3, 0x68, 0xee, 0xbd, - 0x62, 0x0b, 0xf6, 0x23, 0xde, 0xab, 0x52, 0xe0, 0xe3, 0x61, 0xc1, 0x69, 0xfe, 0x23, 0xde, 0xab, 0xa2, 0x69, 0x82, - 0x2b, 0xa4, 0x09, 0x48, 0x89, 0xcd, 0xdb, 0xd4, 0x69, 0x24, 0x80, 0x82, 0xe6, 0x91, 0x39, 0xc8, 0x9e, 0xbb, 0x00, - 0x8c, 0x49, 0x07, 0xbb, 0xb8, 0x5f, 0x36, 0xac, 0xaa, 0x8d, 0x46, 0x0e, 0x41, 0xe9, 0xca, 0xd9, 0x98, 0xaf, 0x47, - 0x1b, 0x0b, 0x62, 0x94, 0xc9, 0xe4, 0xf2, 0x39, 0x8f, 0xb7, 0x16, 0x0b, 0x85, 0xd5, 0x82, 0x05, 0xaa, 0x55, 0xa9, - 0xd2, 0xc3, 0xe2, 0xdb, 0x05, 0xb3, 0xa0, 0x88, 0xd9, 0x7a, 0x0f, 0x6f, 0xb9, 0x22, 0x20, 0x25, 0xbb, 0x24, 0x78, - 0x5e, 0xdc, 0x60, 0xaa, 0x7f, 0x4d, 0x1e, 0x08, 0x3d, 0x53, 0x3a, 0xc2, 0x26, 0x4f, 0x41, 0x24, 0xb1, 0xfd, 0x16, - 0x76, 0xac, 0xd1, 0x0b, 0xe1, 0x85, 0x14, 0x38, 0x57, 0x4d, 0x13, 0x33, 0xca, 0x4d, 0x74, 0xb1, 0x87, 0x6a, 0xce, - 0x32, 0x6d, 0x11, 0x60, 0xdf, 0xa1, 0xa1, 0x14, 0xcf, 0x0d, 0x28, 0xcc, 0xbb, 0xd8, 0x2e, 0xe5, 0x31, 0x2c, 0x5e, - 0x90, 0x02, 0x44, 0x8d, 0x8b, 0x49, 0x59, 0x67, 0x9e, 0x2f, 0x26, 0x5c, 0x54, 0xc8, 0x50, 0x30, 0x35, 0x97, 0x02, - 0x9e, 0xa5, 0x28, 0x8b, 0x18, 0x3a, 0x54, 0xc3, 0x77, 0x4b, 0xc2, 0xca, 0x3a, 0xe6, 0x98, 0xe2, 0xa2, 0xaa, 0x01, - 0xcc, 0xc5, 0xc3, 0xf0, 0xfd, 0x7a, 0xf5, 0x5a, 0xbc, 0x93, 0xf3, 0x2a, 0xdf, 0xd3, 0x38, 0x1f, 0xfd, 0xdd, 0xd9, - 0x0d, 0xa3, 0xb5, 0x79, 0x39, 0x2a, 0xd8, 0xbe, 0x1f, 0x78, 0xf5, 0x9a, 0xda, 0xda, 0xbc, 0x3d, 0x55, 0x66, 0x0d, - 0x59, 0xf9, 0xd0, 0x42, 0xd5, 0x5e, 0xbd, 0xaa, 0x14, 0xb6, 0x22, 0x40, 0xa5, 0xe0, 0xa3, 0xad, 0xfc, 0x27, 0xda, - 0xe6, 0xdb, 0x73, 0xa8, 0x0c, 0x0f, 0xe4, 0xc9, 0x50, 0xd5, 0x03, 0x2e, 0xca, 0x0f, 0x01, 0x2c, 0x7e, 0x64, 0x82, - 0xf0, 0xee, 0xba, 0x40, 0xe6, 0x4c, 0xc5, 0x12, 0x2f, 0xfb, 0x74, 0x90, 0x5a, 0x79, 0x28, 0x95, 0x60, 0xdb, 0x73, - 0x53, 0x70, 0xed, 0xa3, 0xfd, 0xe2, 0x3e, 0x1b, 0xa4, 0xcb, 0x7a, 0x44, 0x60, 0x1b, 0x93, 0xd8, 0x9b, 0x73, 0x9a, - 0x10, 0xba, 0x74, 0x80, 0x73, 0x02, 0xb6, 0xc7, 0x9e, 0x3d, 0x7d, 0x13, 0x67, 0xa8, 0x57, 0xe7, 0xf0, 0x97, 0x6b, - 0x9c, 0xe3, 0x0c, 0xa5, 0x0f, 0x63, 0xb8, 0xc0, 0x5a, 0x63, 0x00, 0x5f, 0x66, 0x49, 0x15, 0x78, 0xa4, 0x66, 0x46, - 0x62, 0x75, 0x17, 0x81, 0x68, 0xa9, 0xc3, 0xdb, 0x71, 0xe6, 0x63, 0x6a, 0x1b, 0xee, 0xf5, 0x99, 0x11, 0x0e, 0x27, - 0x59, 0x5c, 0x3b, 0x67, 0x38, 0xb9, 0xdc, 0xe7, 0xb5, 0x13, 0x13, 0xac, 0xbd, 0xc3, 0x53, 0x05, 0xf4, 0x68, 0x70, - 0xaa, 0x58, 0x1a, 0x02, 0x31, 0x13, 0xc0, 0x9b, 0x39, 0x3c, 0xda, 0x02, 0x9c, 0x8f, 0xd6, 0x38, 0xf8, 0x4a, 0x6b, - 0x5d, 0x6d, 0x2a, 0x51, 0xd6, 0x6b, 0xdc, 0x9f, 0x66, 0x78, 0x94, 0xe1, 0x79, 0x36, 0x08, 0x8e, 0x9b, 0x59, 0x16, - 0x9a, 0x74, 0xad, 0x56, 0x4f, 0x9d, 0x19, 0x21, 0xb2, 0x3f, 0x2d, 0xfd, 0x41, 0x3d, 0x40, 0xf8, 0x14, 0xb2, 0x80, - 0x96, 0xf4, 0xdc, 0xdf, 0x86, 0x7d, 0x72, 0x1b, 0x35, 0x62, 0x9e, 0x58, 0x32, 0xd2, 0xf3, 0x3f, 0xca, 0x2c, 0xdb, - 0x5a, 0x23, 0x9a, 0xdf, 0xee, 0x45, 0x0d, 0xdf, 0x5e, 0xa0, 0x65, 0x2b, 0xcd, 0x76, 0x00, 0x51, 0xac, 0x71, 0x92, - 0x0e, 0xd6, 0x48, 0xae, 0x56, 0xb1, 0x4d, 0x21, 0x3c, 0x99, 0x31, 0xaa, 0x16, 0x85, 0x79, 0x85, 0x2e, 0x56, 0x28, - 0x31, 0xfc, 0x2e, 0x76, 0x36, 0xa2, 0xf0, 0xe8, 0x9b, 0x04, 0xc3, 0x8d, 0x58, 0x10, 0x59, 0x13, 0xb9, 0x87, 0x59, - 0x65, 0x19, 0x24, 0x88, 0x30, 0x22, 0xbf, 0xbd, 0x2e, 0x15, 0xf6, 0x9d, 0x3b, 0xfb, 0xc7, 0xf8, 0x02, 0xc2, 0xcd, - 0xdb, 0x84, 0x16, 0x43, 0x3a, 0x01, 0x36, 0x16, 0xe2, 0x10, 0x6e, 0x25, 0xac, 0x56, 0xfd, 0x41, 0x57, 0x18, 0xf2, - 0xec, 0x5e, 0xe1, 0x2b, 0x1b, 0xda, 0xdd, 0x00, 0x5c, 0x75, 0x5b, 0x6a, 0xae, 0x8d, 0xee, 0x87, 0x9a, 0x87, 0xc2, - 0xb8, 0x4b, 0x72, 0x6f, 0x7d, 0x54, 0xcf, 0x79, 0xd7, 0x2c, 0xc0, 0x4d, 0xe8, 0x2a, 0x3c, 0xc2, 0x0b, 0x6b, 0xc3, - 0x69, 0x5e, 0x85, 0xa2, 0xe6, 0x31, 0x28, 0x78, 0x83, 0x9a, 0xb0, 0x7e, 0x36, 0xc0, 0x23, 0x1f, 0x33, 0x7c, 0xff, - 0x6d, 0x3c, 0x42, 0xa8, 0x20, 0x06, 0xa6, 0xd6, 0x65, 0x7b, 0x54, 0xd9, 0xed, 0x9b, 0x4c, 0xc3, 0x30, 0x18, 0x23, - 0xe6, 0x51, 0x68, 0xc4, 0x9c, 0x37, 0x1a, 0x68, 0x41, 0x46, 0x60, 0xc4, 0xbc, 0x08, 0x5a, 0x5b, 0xd8, 0x17, 0x43, - 0x83, 0xf6, 0x16, 0x08, 0x75, 0x39, 0xd0, 0x34, 0x0d, 0x6f, 0x83, 0x54, 0x6f, 0xb3, 0xfb, 0x97, 0xaa, 0x8e, 0x3a, - 0xa0, 0x48, 0x18, 0x5f, 0xfa, 0x49, 0x58, 0xd7, 0x70, 0x3b, 0xee, 0xb1, 0x19, 0xb7, 0xb3, 0x6d, 0x50, 0x7d, 0xd9, - 0xcf, 0x06, 0x83, 0xae, 0xf4, 0x56, 0x12, 0x2d, 0x3c, 0xae, 0x5e, 0x13, 0xa9, 0x16, 0xef, 0x8b, 0xde, 0xbc, 0xf2, - 0xe6, 0xfe, 0x91, 0xd2, 0xcd, 0xf3, 0x18, 0x38, 0xa0, 0x7d, 0xb8, 0x1f, 0xaa, 0xe2, 0x83, 0x1d, 0x75, 0x20, 0x0a, - 0x5a, 0xda, 0xaa, 0x09, 0xa4, 0xd6, 0xcc, 0x2e, 0xd6, 0x4d, 0x85, 0x0e, 0x05, 0x84, 0x21, 0x53, 0x55, 0x77, 0x77, - 0x2a, 0x50, 0x0d, 0x71, 0x38, 0xf5, 0x1f, 0x5b, 0x23, 0xd6, 0x38, 0xea, 0x8c, 0x22, 0x63, 0x24, 0x69, 0x97, 0x0f, - 0x1e, 0x10, 0x02, 0x2b, 0x01, 0x1f, 0xc8, 0xd9, 0x24, 0x19, 0x43, 0x82, 0xb7, 0x2c, 0xd3, 0x86, 0x0f, 0xe1, 0x0e, - 0x41, 0x79, 0x62, 0x63, 0x6d, 0xba, 0x4a, 0x16, 0x72, 0x55, 0x97, 0xd7, 0x01, 0x7a, 0xde, 0x95, 0xbf, 0xb1, 0xe1, - 0xc8, 0x82, 0x81, 0x65, 0x5b, 0xfb, 0x04, 0x3c, 0xf2, 0x71, 0x85, 0x20, 0x7e, 0x29, 0x74, 0x62, 0x82, 0x5e, 0x5f, - 0xc1, 0x06, 0xc5, 0x73, 0x70, 0x10, 0x74, 0x12, 0x1c, 0x06, 0xef, 0x32, 0xab, 0x49, 0x36, 0xb8, 0x35, 0x23, 0xf1, - 0x7c, 0xb5, 0x6a, 0xa1, 0xc3, 0x7f, 0xcc, 0xbb, 0xce, 0xe3, 0x52, 0xe1, 0x3e, 0xae, 0x14, 0xee, 0x60, 0x09, 0x48, - 0xc6, 0x81, 0xae, 0x1d, 0xcb, 0x50, 0x8d, 0x0e, 0x21, 0xc7, 0x5f, 0x40, 0x00, 0x6a, 0x77, 0x2c, 0x81, 0x9e, 0x7d, - 0xab, 0x80, 0xd5, 0xb5, 0x97, 0x25, 0x90, 0x11, 0xdc, 0xfd, 0x26, 0x30, 0x2a, 0x44, 0xe3, 0xf3, 0x67, 0x9e, 0x86, - 0xe0, 0x89, 0xf3, 0xe7, 0x9a, 0x19, 0xd6, 0xbd, 0xa0, 0x37, 0xa6, 0xf9, 0x78, 0x8c, 0x9b, 0x63, 0x0b, 0xce, 0xa3, - 0x0e, 0xfc, 0xb4, 0x10, 0x3d, 0xea, 0x60, 0x97, 0x8a, 0xc7, 0x25, 0x90, 0x43, 0xf4, 0x74, 0x06, 0x52, 0xc0, 0x4a, - 0xc7, 0x56, 0x8b, 0x34, 0x41, 0xab, 0xd5, 0xe4, 0x82, 0xb4, 0x10, 0x5a, 0xaa, 0x1b, 0xae, 0xb3, 0x29, 0xf8, 0x48, - 0x83, 0x62, 0xe0, 0x0d, 0xd5, 0xd3, 0x18, 0xe1, 0x31, 0x5a, 0x8e, 0xd8, 0x98, 0x2e, 0x72, 0x9d, 0xaa, 0x1e, 0x4f, - 0x6c, 0x54, 0x5e, 0x66, 0x23, 0xc1, 0x1d, 0x75, 0xf0, 0xc4, 0xf0, 0x97, 0x8f, 0x8c, 0x39, 0x48, 0x91, 0x99, 0xe4, - 0x89, 0x49, 0xc0, 0x3c, 0xc9, 0x72, 0xa9, 0x98, 0x6d, 0xa6, 0x6b, 0x6d, 0xcb, 0x21, 0xae, 0x77, 0xa4, 0x0b, 0x6e, - 0xac, 0x28, 0xa3, 0x74, 0x4a, 0x54, 0x4f, 0x1d, 0x75, 0xd2, 0x09, 0xe6, 0x09, 0x70, 0x7a, 0xef, 0x64, 0xcc, 0x1a, - 0xe5, 0xad, 0xe8, 0x0c, 0x1d, 0x4e, 0xb1, 0xa8, 0x2e, 0x51, 0x67, 0xe8, 0x70, 0x82, 0xf0, 0xac, 0x41, 0x72, 0x05, - 0x1e, 0xc3, 0x5c, 0xfc, 0x1f, 0x29, 0xff, 0xcd, 0x61, 0x43, 0xfc, 0xe8, 0xb7, 0xb0, 0x53, 0xd8, 0x28, 0x4a, 0x73, - 0x02, 0x5e, 0x8b, 0xed, 0x33, 0x9c, 0x91, 0x49, 0x33, 0xf7, 0x01, 0xf7, 0x4c, 0x2b, 0x8d, 0x5b, 0x8d, 0x0e, 0x33, - 0x3c, 0xda, 0x4c, 0x8a, 0xcd, 0x5c, 0x9b, 0x79, 0x9a, 0xc1, 0xf9, 0x5e, 0x8d, 0xc2, 0x95, 0x5f, 0x6c, 0x26, 0x85, - 0xe5, 0x1d, 0x70, 0x9b, 0x23, 0x2c, 0x9a, 0x14, 0xe7, 0x78, 0xd6, 0xfc, 0x8a, 0x67, 0xcd, 0x0f, 0x65, 0x46, 0x63, - 0x81, 0x05, 0x04, 0xef, 0x83, 0x44, 0x3c, 0xab, 0x92, 0x47, 0x58, 0x34, 0x4c, 0x79, 0x3c, 0x6b, 0x54, 0xa5, 0x9b, - 0x0b, 0x2c, 0x1a, 0xa6, 0x74, 0xe3, 0x03, 0x9e, 0x35, 0xbe, 0xfe, 0x8b, 0x49, 0x47, 0x29, 0xa0, 0xcb, 0x1c, 0x2d, - 0x33, 0x3b, 0xc4, 0xab, 0xdf, 0xde, 0xbe, 0x6b, 0x5f, 0x77, 0x0e, 0x27, 0xd8, 0xaf, 0x5f, 0x66, 0x70, 0x2c, 0xd3, - 0x31, 0x6b, 0x02, 0x44, 0x33, 0xdc, 0x39, 0x9c, 0xe2, 0xce, 0x61, 0xe6, 0x9a, 0x5a, 0xcf, 0x1a, 0xe4, 0x56, 0x87, - 0x50, 0xd4, 0x51, 0x1a, 0xc2, 0xc7, 0x4f, 0x36, 0x9d, 0xa0, 0x1a, 0x28, 0xd1, 0xe1, 0xa4, 0x06, 0x2a, 0xf8, 0x5e, - 0xd4, 0xbe, 0xab, 0x7a, 0x15, 0x06, 0x59, 0x28, 0xa1, 0x70, 0xcd, 0x0d, 0x78, 0x6a, 0x29, 0x06, 0x32, 0x61, 0x8a, - 0x05, 0xca, 0x77, 0x40, 0x61, 0x94, 0x27, 0x66, 0xe8, 0xc1, 0x74, 0x4c, 0xe2, 0xff, 0xcf, 0x93, 0x29, 0x87, 0x5e, - 0x6e, 0x99, 0xad, 0xe9, 0xb9, 0xc9, 0x84, 0xc3, 0x07, 0x1e, 0xeb, 0xff, 0xda, 0x81, 0x62, 0x03, 0x52, 0xfc, 0x7f, - 0xe9, 0xe8, 0x42, 0x30, 0x42, 0x56, 0x94, 0x16, 0x0e, 0xf1, 0xbf, 0x3f, 0xac, 0xa0, 0xfb, 0x62, 0xab, 0xfb, 0xc2, - 0x74, 0x1f, 0x36, 0x6d, 0x54, 0x39, 0x69, 0x55, 0xc9, 0x92, 0xff, 0x3a, 0xdd, 0xda, 0x02, 0x8d, 0xa8, 0xd1, 0xb3, - 0x49, 0xd8, 0xe0, 0x7e, 0x3b, 0xdd, 0x81, 0xcc, 0x6b, 0x6e, 0x9f, 0x19, 0x85, 0xc3, 0x37, 0xb8, 0x53, 0xbd, 0x6c, - 0x81, 0xf7, 0xa6, 0x32, 0xfa, 0xca, 0x38, 0xb4, 0x1c, 0x2c, 0x36, 0x4d, 0xb9, 0x8d, 0xb1, 0x74, 0x72, 0x8a, 0x8d, - 0x2b, 0x22, 0x54, 0xba, 0xbd, 0x04, 0xa5, 0xf8, 0x58, 0x37, 0x99, 0xf9, 0xba, 0xd0, 0x89, 0xb9, 0x84, 0x6a, 0x98, - 0xcf, 0xbb, 0x4b, 0x9d, 0x68, 0x39, 0xb7, 0x79, 0x77, 0x17, 0xd0, 0x27, 0x68, 0x58, 0x1b, 0x81, 0xdd, 0x3e, 0x2b, - 0x9c, 0x7e, 0xa7, 0x3a, 0x04, 0xc3, 0x03, 0xc8, 0x91, 0x16, 0xdb, 0x07, 0x36, 0xad, 0x61, 0xd7, 0x45, 0xb3, 0x4c, - 0xb4, 0xad, 0x36, 0x4d, 0xae, 0xdd, 0xc3, 0x7c, 0x1e, 0xf2, 0x14, 0xbc, 0xb0, 0xfa, 0xf1, 0x1d, 0xec, 0xc6, 0x6d, - 0x8d, 0x91, 0xa8, 0x2b, 0x99, 0x4a, 0xe8, 0x27, 0xb7, 0x98, 0x25, 0x77, 0xc6, 0x8b, 0x51, 0x19, 0x7f, 0x1f, 0x13, - 0x74, 0x3f, 0xaa, 0x24, 0x39, 0xb0, 0xec, 0x6f, 0xb0, 0xe4, 0x16, 0xcc, 0x13, 0xcb, 0x6a, 0x12, 0xeb, 0xe4, 0x2e, - 0x58, 0x44, 0x69, 0x1a, 0x59, 0x1b, 0x06, 0xd4, 0x34, 0x63, 0xd5, 0x83, 0xfb, 0x10, 0xe8, 0xa1, 0x57, 0x96, 0xd2, - 0xae, 0xb3, 0xb4, 0xd6, 0xbd, 0x36, 0xdd, 0x6f, 0x0e, 0x28, 0xe0, 0x0b, 0x03, 0xae, 0xe9, 0x5f, 0x4d, 0x22, 0x19, - 0xb2, 0xaf, 0x9c, 0x15, 0x8f, 0x17, 0x85, 0xc1, 0x34, 0xd1, 0xd3, 0x49, 0x36, 0x6f, 0x83, 0xa9, 0x5e, 0x36, 0xef, - 0xdc, 0x62, 0xf7, 0x7d, 0x67, 0xbf, 0xef, 0xb0, 0xe8, 0x31, 0x93, 0x91, 0x32, 0x53, 0xcc, 0x7f, 0xdf, 0xd9, 0xef, - 0x3b, 0xbc, 0x3d, 0x98, 0x1b, 0x7f, 0xa1, 0x58, 0xb2, 0x33, 0x5c, 0x82, 0x09, 0x79, 0xc0, 0xdd, 0xd4, 0xb2, 0x4c, - 0x10, 0xd8, 0x5a, 0x02, 0xc4, 0xf9, 0x7c, 0x1a, 0x57, 0xbc, 0x1a, 0x02, 0xee, 0xd3, 0xbb, 0xb6, 0x57, 0xa9, 0xc0, - 0x63, 0x82, 0x46, 0xc4, 0xc4, 0xb6, 0x31, 0x4f, 0x84, 0x01, 0x97, 0x47, 0x74, 0xa9, 0x27, 0x49, 0x80, 0x57, 0x35, - 0x2a, 0x6f, 0x53, 0xa4, 0xfc, 0x22, 0x41, 0x8e, 0x2f, 0xf6, 0x88, 0x2a, 0x06, 0xb0, 0x2a, 0x4b, 0xfa, 0x04, 0x52, - 0xcf, 0x0f, 0x26, 0xfa, 0x79, 0x13, 0x79, 0xec, 0x63, 0xb9, 0x9f, 0x99, 0x9e, 0x16, 0x72, 0x31, 0x99, 0x82, 0x0f, - 0x2d, 0xb0, 0x0c, 0x85, 0xa9, 0x57, 0xd9, 0xfa, 0xd7, 0x24, 0x37, 0x01, 0x14, 0x4e, 0x37, 0x65, 0x42, 0x33, 0xbd, - 0xa0, 0xb9, 0xb1, 0x24, 0xe5, 0x62, 0xf2, 0x48, 0xde, 0xbe, 0x04, 0xec, 0xa6, 0x44, 0x37, 0x76, 0xe4, 0xbd, 0x85, - 0x1d, 0x80, 0x33, 0xc2, 0x76, 0x55, 0x7c, 0xa8, 0x40, 0xe7, 0x8f, 0x73, 0xc2, 0x76, 0x55, 0x7d, 0xc2, 0x6c, 0xf6, - 0x94, 0x6c, 0x0c, 0xb7, 0x17, 0x67, 0x8d, 0x1c, 0x1d, 0x75, 0xd2, 0xbc, 0xeb, 0x89, 0x81, 0x05, 0x68, 0x00, 0xdc, - 0xad, 0xed, 0x59, 0xde, 0xdd, 0x10, 0xd0, 0xbb, 0x64, 0xd2, 0x5e, 0x97, 0x9b, 0x94, 0xd5, 0xaa, 0x53, 0x51, 0xc1, - 0x02, 0x4f, 0x83, 0xbd, 0x40, 0xed, 0xd7, 0x0e, 0x8a, 0x73, 0x95, 0x6d, 0x9a, 0x9e, 0x97, 0x7d, 0x77, 0x77, 0x2c, - 0x32, 0xb6, 0x69, 0x6f, 0x77, 0x10, 0x09, 0xcb, 0x09, 0xeb, 0x80, 0x13, 0xae, 0x6a, 0x07, 0x04, 0xe8, 0x3a, 0x10, - 0xb9, 0xb1, 0x24, 0xcb, 0x75, 0x65, 0x74, 0x1f, 0xf8, 0xdd, 0x52, 0x22, 0xdd, 0x68, 0x4b, 0x82, 0xe9, 0x13, 0x8c, - 0x9a, 0xce, 0xbc, 0xef, 0x5c, 0x7b, 0xba, 0x78, 0x53, 0xb4, 0xf5, 0x0f, 0x29, 0x63, 0xb3, 0x3d, 0x4c, 0x0c, 0x65, - 0x10, 0x03, 0xbd, 0x8f, 0x78, 0xb7, 0xd1, 0xc8, 0x10, 0x28, 0x64, 0xb2, 0x01, 0x96, 0x89, 0xd7, 0xa2, 0x1f, 0x1c, - 0x18, 0x78, 0x54, 0x09, 0x08, 0x53, 0x10, 0x42, 0xc2, 0xae, 0x0d, 0xc2, 0x86, 0xcb, 0x55, 0xcb, 0x85, 0x8d, 0x54, - 0x1b, 0x3a, 0xf8, 0x7f, 0x85, 0xcb, 0x56, 0xcf, 0x2c, 0x17, 0xc5, 0xe0, 0x66, 0x6e, 0xc0, 0x22, 0x41, 0x7a, 0xb4, - 0xd9, 0x1e, 0x8a, 0xbb, 0x73, 0xb1, 0xd9, 0x10, 0x90, 0x98, 0xc3, 0x04, 0x45, 0xc3, 0xb9, 0x31, 0xc6, 0x2a, 0xa9, - 0xb4, 0xac, 0x35, 0x89, 0x39, 0xf0, 0xa5, 0x0b, 0xd7, 0x7d, 0x79, 0x9b, 0x32, 0x7c, 0x97, 0x0a, 0x7c, 0x03, 0x9e, - 0x34, 0xa9, 0xc4, 0xee, 0xf1, 0x82, 0x62, 0x4d, 0x74, 0xd7, 0xb3, 0xb7, 0x05, 0xac, 0xb3, 0xd9, 0x23, 0x22, 0xf8, - 0x5d, 0xfd, 0x6a, 0x83, 0xef, 0x16, 0xfe, 0x0a, 0xd6, 0xcf, 0xc1, 0x49, 0x8a, 0x45, 0x43, 0x36, 0x0b, 0x77, 0x64, - 0x40, 0xb9, 0x8a, 0x5f, 0x0e, 0x53, 0xb7, 0x8a, 0xe1, 0xda, 0xc7, 0x57, 0xfc, 0x61, 0xa3, 0xdd, 0x86, 0x2a, 0x8b, - 0xdb, 0xbd, 0x29, 0x1a, 0xb2, 0x6a, 0x7a, 0x47, 0xe6, 0x46, 0x4a, 0xfd, 0xeb, 0x03, 0x6e, 0x6d, 0xb5, 0xef, 0xa7, - 0xf9, 0xd6, 0xa3, 0x73, 0xd5, 0xb4, 0x4f, 0xad, 0x15, 0xc1, 0xc1, 0xcf, 0x16, 0x6e, 0x6e, 0x0d, 0x38, 0x80, 0x9f, - 0xbf, 0xa3, 0x79, 0x9c, 0x41, 0x74, 0x7a, 0xab, 0x19, 0x5f, 0xc5, 0x7f, 0x8e, 0x1a, 0x71, 0x2f, 0xfd, 0x33, 0xf9, - 0x73, 0xd4, 0x40, 0x3d, 0x14, 0xcf, 0x6f, 0x57, 0x6c, 0xb6, 0x82, 0x60, 0x6b, 0xf7, 0x8e, 0xf0, 0xeb, 0xb0, 0x24, - 0xd7, 0x34, 0xe7, 0xd9, 0xca, 0xbd, 0xaa, 0xb7, 0x72, 0x4f, 0x0e, 0xad, 0xcc, 0x43, 0x51, 0xab, 0x58, 0x0e, 0x73, - 0x08, 0x2c, 0x1c, 0xef, 0x35, 0x7b, 0xfd, 0x56, 0xf3, 0xc1, 0xc0, 0xfe, 0x6b, 0x22, 0xdc, 0xa3, 0x5a, 0xc4, 0xb6, - 0x37, 0x1b, 0x5b, 0x3f, 0x06, 0xc3, 0x0e, 0x08, 0x05, 0x0e, 0x72, 0xe9, 0xe3, 0x0c, 0x59, 0xdf, 0x93, 0xd5, 0x8a, - 0xb9, 0x68, 0xd6, 0x4e, 0x83, 0x5f, 0xc6, 0x66, 0x3a, 0x6c, 0x27, 0x9d, 0xae, 0x17, 0x63, 0x49, 0x03, 0x22, 0x4d, - 0x63, 0x06, 0x81, 0xa4, 0x96, 0x86, 0xc3, 0x9a, 0xdf, 0x46, 0x69, 0x75, 0x7f, 0x04, 0x29, 0x3f, 0x44, 0x29, 0x3f, - 0x22, 0x10, 0x40, 0xdb, 0x32, 0x47, 0x65, 0x43, 0xde, 0x77, 0xe9, 0x9e, 0x71, 0x66, 0x68, 0xf0, 0xd5, 0xaa, 0x55, - 0x0d, 0x53, 0x14, 0xf5, 0x61, 0x2e, 0xd7, 0x58, 0x90, 0x37, 0xa0, 0x6b, 0x56, 0x44, 0xf4, 0x42, 0x57, 0x79, 0x78, - 0x54, 0x18, 0x4b, 0x02, 0x4e, 0xfa, 0x3d, 0xd1, 0x2b, 0xc8, 0xe5, 0xc3, 0x18, 0x7c, 0xcc, 0x30, 0xef, 0xeb, 0x7e, - 0x31, 0x18, 0xa0, 0xd4, 0x39, 0x9d, 0xa5, 0x26, 0xe2, 0x4a, 0xe0, 0x97, 0x5c, 0x80, 0x5f, 0xb2, 0x42, 0xac, 0x5f, - 0x0c, 0xc8, 0xbd, 0x2c, 0x96, 0xe0, 0x94, 0xbf, 0xc3, 0xe7, 0xf1, 0x61, 0x68, 0x60, 0x6a, 0x86, 0x65, 0x2e, 0xb2, - 0xc1, 0x62, 0xce, 0x5a, 0x02, 0xc1, 0xcd, 0x80, 0xbb, 0xd4, 0x86, 0x44, 0x63, 0x0d, 0x14, 0xdd, 0x46, 0xa1, 0x99, - 0xd1, 0xd3, 0xad, 0x36, 0xfa, 0x91, 0xc3, 0x0b, 0x73, 0x0d, 0x63, 0x11, 0xc8, 0x5c, 0xae, 0x7a, 0xec, 0x2f, 0x3f, - 0x6c, 0x56, 0x18, 0xbc, 0xc2, 0x98, 0xec, 0x94, 0x56, 0x89, 0x66, 0x7c, 0x95, 0x27, 0x8e, 0x21, 0xc8, 0xc4, 0x52, - 0xe9, 0x86, 0x63, 0xe2, 0x4a, 0xfa, 0x4c, 0x0c, 0xd9, 0x6e, 0x78, 0x66, 0x2e, 0x74, 0xb3, 0xfd, 0xc3, 0xb9, 0x9d, - 0x73, 0xc2, 0x8d, 0x56, 0xd2, 0x68, 0xa3, 0x9e, 0x19, 0xaa, 0xea, 0x82, 0xf9, 0x3d, 0x74, 0x5a, 0x5a, 0xec, 0x5c, - 0xbd, 0xbb, 0xe1, 0x13, 0x77, 0x65, 0xfc, 0x2d, 0x56, 0x85, 0x56, 0x64, 0xb8, 0xdd, 0x42, 0xde, 0x9c, 0xe9, 0xa1, - 0x57, 0xe4, 0x42, 0x75, 0xf8, 0x8b, 0xba, 0xc2, 0xbc, 0x7a, 0x19, 0x35, 0x84, 0x47, 0xbf, 0xd7, 0x19, 0x28, 0xff, - 0x60, 0x62, 0x32, 0x67, 0xc9, 0x0d, 0x2d, 0x44, 0xfc, 0xe3, 0x0b, 0x61, 0x62, 0x55, 0xed, 0xc1, 0x40, 0xf6, 0x4c, - 0xc5, 0x3d, 0xb8, 0x35, 0xe1, 0x63, 0xce, 0x46, 0xe9, 0x5e, 0xf4, 0x63, 0x43, 0x34, 0x7e, 0x8c, 0x7e, 0x04, 0x77, - 0x67, 0xf7, 0xc4, 0x62, 0x19, 0x17, 0xc2, 0xdf, 0x63, 0x3d, 0x2c, 0x55, 0xca, 0x58, 0x7b, 0xdd, 0x72, 0x78, 0x21, - 0xf5, 0x26, 0x8b, 0x1f, 0x3a, 0x62, 0x6d, 0x53, 0xb0, 0x0e, 0x29, 0x29, 0x3c, 0xbb, 0x62, 0x6e, 0xb5, 0x98, 0xbb, - 0xd4, 0x12, 0xfe, 0xfa, 0xea, 0x61, 0xa9, 0x82, 0x86, 0x83, 0xd0, 0x95, 0xb6, 0x90, 0x00, 0x03, 0x97, 0xd2, 0xa7, - 0xd3, 0x9d, 0x49, 0x64, 0x96, 0xc5, 0xf0, 0xee, 0x41, 0x05, 0xf3, 0xdf, 0xd9, 0x46, 0x58, 0x15, 0xb8, 0x5c, 0xa9, - 0xa2, 0x5e, 0x4a, 0x02, 0x01, 0xe8, 0x4b, 0xef, 0x41, 0x79, 0x51, 0x74, 0x1b, 0x0d, 0x09, 0x5a, 0x58, 0x6a, 0xae, - 0x55, 0x31, 0xdd, 0x0f, 0x9f, 0x06, 0x0c, 0x3e, 0xbc, 0x43, 0xda, 0xc6, 0xfb, 0x9c, 0x94, 0x50, 0xbb, 0x83, 0xf6, - 0xc1, 0x2a, 0x3b, 0x28, 0xff, 0x36, 0xa6, 0xc8, 0xe6, 0xf7, 0xd9, 0x0f, 0xd4, 0x75, 0x38, 0x70, 0x05, 0xab, 0x5e, - 0xca, 0x28, 0x18, 0xb0, 0x72, 0x0a, 0xd4, 0xde, 0x49, 0x46, 0xb3, 0x29, 0x03, 0x75, 0xbf, 0x2d, 0x5a, 0xcd, 0xed, - 0x49, 0xdd, 0x6f, 0xc8, 0x38, 0xfb, 0x08, 0xe3, 0xec, 0xa3, 0xc0, 0x8b, 0x45, 0x92, 0x3f, 0x64, 0xac, 0x71, 0xac, - 0x9a, 0x02, 0x1d, 0x75, 0x80, 0x3b, 0x03, 0x07, 0x1e, 0xb0, 0x45, 0x39, 0x38, 0xa0, 0xce, 0xe2, 0x9e, 0x36, 0x32, - 0xef, 0xed, 0x09, 0xb5, 0x8b, 0x58, 0xe0, 0x66, 0xcd, 0x4c, 0x0b, 0x5a, 0x2b, 0x8c, 0xf3, 0x78, 0xc0, 0xdb, 0x3c, - 0xab, 0xc5, 0x4f, 0xd8, 0xb0, 0xa6, 0xaa, 0xdf, 0x40, 0x73, 0x54, 0x0b, 0x72, 0xf3, 0xc4, 0x78, 0xab, 0x92, 0x7e, - 0x14, 0x0d, 0x2c, 0xa7, 0x42, 0x0c, 0xc9, 0xe8, 0xb7, 0x06, 0xc1, 0xad, 0xf6, 0x6a, 0xc5, 0x3d, 0xe2, 0x8b, 0x9a, - 0xb7, 0x9a, 0xb9, 0x05, 0xa0, 0x45, 0x1c, 0x95, 0xf7, 0x26, 0x11, 0x78, 0xdf, 0x96, 0x11, 0xd2, 0x96, 0x7d, 0xfb, - 0xfe, 0x63, 0xa9, 0xd8, 0x7c, 0x47, 0x27, 0x83, 0x34, 0xb2, 0x23, 0x8a, 0xf0, 0x75, 0x09, 0x49, 0xb8, 0x4a, 0xba, - 0x56, 0x99, 0x9c, 0x33, 0x95, 0x72, 0x7c, 0x5d, 0x48, 0xa9, 0xaf, 0xec, 0x97, 0xc4, 0xd5, 0x9d, 0x8c, 0xc0, 0xd7, - 0x13, 0xa6, 0xdf, 0xd1, 0x62, 0xc2, 0xc0, 0xaf, 0xc8, 0xdf, 0x8e, 0xa5, 0x94, 0x5c, 0x3e, 0x11, 0x71, 0x9f, 0x62, - 0x78, 0xbc, 0x74, 0x80, 0xb5, 0x09, 0x81, 0x52, 0xe2, 0x22, 0x5c, 0x10, 0xbd, 0x29, 0xe4, 0xed, 0x5d, 0x5c, 0x60, - 0xe7, 0x00, 0x58, 0x3a, 0x4d, 0x02, 0xfc, 0xcb, 0xc7, 0x7c, 0xac, 0xc6, 0x9c, 0x1a, 0x5d, 0xbf, 0xfb, 0x9d, 0x5c, - 0x03, 0xbd, 0x2d, 0x1d, 0x05, 0xfb, 0xad, 0x01, 0xe4, 0xc2, 0x5d, 0x18, 0x5c, 0x7c, 0x85, 0xb5, 0x65, 0x61, 0xbc, - 0xb1, 0x00, 0x7a, 0x7f, 0x67, 0x60, 0xc1, 0x86, 0x39, 0xa6, 0xf0, 0xf2, 0xeb, 0x84, 0xe9, 0x20, 0x2a, 0xc8, 0x93, - 0xf2, 0x6d, 0xcf, 0x5a, 0xed, 0xb7, 0x6c, 0x0c, 0x77, 0x18, 0xc9, 0xb7, 0x0b, 0x27, 0x0e, 0x3c, 0x20, 0xd3, 0x64, - 0xb6, 0xd9, 0x37, 0x3e, 0xf2, 0xc8, 0xeb, 0x71, 0xbc, 0xab, 0xa5, 0x30, 0xdf, 0xac, 0xe8, 0x1a, 0x43, 0x28, 0x8a, - 0xb0, 0xdf, 0x2f, 0x2a, 0xa6, 0xa8, 0x32, 0x68, 0x83, 0x86, 0xe5, 0x8d, 0xf8, 0x05, 0xce, 0x18, 0x5a, 0x2f, 0x64, - 0xef, 0xe8, 0xac, 0xc3, 0x99, 0xc3, 0x8c, 0x29, 0x81, 0x51, 0x69, 0x59, 0xd0, 0x09, 0x38, 0x3a, 0x57, 0x1f, 0x44, - 0xc5, 0xd5, 0xb1, 0x02, 0xf0, 0x24, 0x53, 0xf8, 0x27, 0xdf, 0x04, 0xeb, 0x7e, 0xab, 0x66, 0x98, 0xfa, 0x8b, 0xde, - 0x76, 0x2d, 0x5f, 0x86, 0x38, 0xd2, 0xc6, 0x10, 0x5a, 0xe7, 0xf6, 0x0e, 0x50, 0xc4, 0x05, 0xbd, 0x48, 0x35, 0xbe, - 0x56, 0x8b, 0xa1, 0x59, 0x5f, 0xe3, 0x3a, 0xa6, 0x0d, 0xa2, 0x58, 0x77, 0x4d, 0x7c, 0x5d, 0x3d, 0xa5, 0xaa, 0x52, - 0x05, 0x67, 0x90, 0x40, 0x58, 0x95, 0x97, 0x0d, 0xa9, 0x24, 0x97, 0xa6, 0x53, 0x69, 0x3a, 0xad, 0x10, 0xca, 0xa5, - 0x27, 0xe5, 0xfd, 0x2b, 0x84, 0x30, 0x30, 0x65, 0x76, 0x60, 0x95, 0xda, 0xc2, 0x2a, 0x78, 0xf5, 0x62, 0x03, 0xab, - 0x24, 0x1c, 0xcf, 0x25, 0x1a, 0x15, 0x15, 0x0e, 0x19, 0xd2, 0x17, 0x62, 0x11, 0x24, 0x00, 0x16, 0xbd, 0xcb, 0x5c, - 0xde, 0xf7, 0x70, 0x28, 0xec, 0x49, 0x26, 0xe1, 0x74, 0x13, 0x9a, 0xc3, 0x1b, 0xbb, 0xaa, 0xe7, 0x11, 0x02, 0x96, - 0x9e, 0x63, 0x78, 0x5b, 0xf9, 0xfb, 0x6f, 0xba, 0x3a, 0x0b, 0xf2, 0xf4, 0x5f, 0xa2, 0x24, 0x34, 0xf6, 0x9f, 0xe3, - 0xa1, 0x43, 0xc2, 0x70, 0xe0, 0x9b, 0x23, 0xac, 0x70, 0x70, 0xab, 0x88, 0xcf, 0xe0, 0x0e, 0x1f, 0xeb, 0xd0, 0x03, - 0xc0, 0x12, 0x8a, 0x43, 0x90, 0x6f, 0xa0, 0x98, 0xc1, 0x01, 0x4d, 0x96, 0xe1, 0x05, 0x2e, 0x58, 0x2d, 0x94, 0xf7, - 0xb7, 0x2d, 0x2f, 0xa5, 0xd5, 0x2e, 0x79, 0x8d, 0x39, 0x50, 0xf9, 0x19, 0x5e, 0xf8, 0x0a, 0xf3, 0xe8, 0xb3, 0xfb, - 0xc2, 0xd7, 0x0e, 0xe8, 0x29, 0x04, 0x8c, 0x74, 0xbf, 0xd7, 0x84, 0x7b, 0x8a, 0x5e, 0xe6, 0xe2, 0xb0, 0xed, 0xa0, - 0x7b, 0x81, 0xb9, 0xba, 0xaa, 0xb2, 0xe6, 0x60, 0x0a, 0x0d, 0x0e, 0xaa, 0x70, 0x46, 0x60, 0xae, 0x5e, 0x94, 0x05, - 0xe7, 0x20, 0xde, 0xf7, 0x84, 0xc9, 0x29, 0xa3, 0x01, 0xbc, 0xc8, 0xca, 0x47, 0xa7, 0x7a, 0x1c, 0x5c, 0xc6, 0x0d, - 0x9b, 0xf8, 0x42, 0xf8, 0x54, 0x60, 0x25, 0xad, 0x71, 0x68, 0x44, 0x47, 0x74, 0x0e, 0x66, 0x1b, 0x40, 0xc1, 0xdd, - 0xf9, 0xb0, 0xb1, 0x50, 0xc1, 0xbb, 0xb6, 0xb5, 0x67, 0xa8, 0x09, 0x71, 0x26, 0x4d, 0xc1, 0xdd, 0xb6, 0x41, 0x06, - 0x6f, 0x7e, 0xfb, 0x6f, 0x85, 0x45, 0x82, 0x01, 0x95, 0x9a, 0x24, 0x08, 0x4f, 0x50, 0x1a, 0xe9, 0x56, 0x6e, 0x26, - 0x90, 0x4e, 0x44, 0xcd, 0xa8, 0x7b, 0xe3, 0x7c, 0x75, 0xd4, 0x40, 0x54, 0xd4, 0x40, 0x05, 0xd4, 0x40, 0xd6, 0xb7, - 0x7f, 0x01, 0x0b, 0x61, 0x23, 0x54, 0x89, 0x20, 0x20, 0xc2, 0x5c, 0x1b, 0x3e, 0xa0, 0x48, 0x42, 0xc8, 0x1b, 0x40, - 0xc5, 0x94, 0xbc, 0x04, 0xa3, 0x71, 0x78, 0xbd, 0x07, 0xdc, 0x2f, 0x2d, 0xc3, 0xe0, 0x39, 0x05, 0x93, 0xff, 0xd6, - 0xe7, 0x43, 0xf5, 0x72, 0x75, 0x10, 0xc2, 0x2f, 0x20, 0x56, 0x84, 0xe3, 0x2f, 0x7e, 0x01, 0xb2, 0xa9, 0xb0, 0x3c, - 0x38, 0x90, 0x20, 0xf0, 0x43, 0x14, 0xe1, 0x80, 0x67, 0x78, 0x99, 0x6d, 0x10, 0x3d, 0x3f, 0x2b, 0x55, 0xcd, 0x4a, - 0x06, 0xb3, 0x2a, 0x3c, 0x8d, 0xa3, 0x6b, 0xc2, 0x40, 0x70, 0xa1, 0x76, 0xdf, 0x20, 0x04, 0xca, 0x96, 0x1b, 0x43, - 0x97, 0x9e, 0x82, 0xf9, 0x68, 0x1c, 0xbd, 0x65, 0xf0, 0x3a, 0xaf, 0x31, 0xf9, 0x67, 0x9a, 0x65, 0xda, 0x30, 0x8f, - 0x8d, 0xc0, 0x49, 0x9d, 0xa2, 0xe4, 0x6f, 0xc9, 0x45, 0x1c, 0x35, 0x2f, 0x23, 0xd4, 0x80, 0x7f, 0x1b, 0x1c, 0x75, - 0x69, 0x42, 0x47, 0x23, 0x1f, 0xfc, 0x26, 0x23, 0x66, 0x93, 0xad, 0x56, 0xa2, 0x22, 0xe8, 0x89, 0xdd, 0x60, 0xc0, - 0x4a, 0xbc, 0x00, 0xf6, 0xc1, 0x72, 0xb0, 0xe4, 0x9d, 0x88, 0x95, 0x3f, 0xa5, 0x30, 0x58, 0x3d, 0x67, 0x08, 0xe1, - 0x2c, 0x88, 0xd9, 0xf8, 0x9f, 0xcf, 0x34, 0x5c, 0x3f, 0x3f, 0x5f, 0xc7, 0x88, 0x48, 0x1f, 0x44, 0xae, 0xc6, 0x8e, - 0x88, 0x20, 0x6c, 0x99, 0xee, 0xbb, 0x32, 0x3f, 0x78, 0xeb, 0xea, 0x81, 0x0d, 0x17, 0x07, 0x06, 0xd4, 0x28, 0x30, - 0x5a, 0xc1, 0x39, 0x29, 0x07, 0x0e, 0x4a, 0x08, 0xcd, 0x8a, 0x78, 0x4a, 0x2e, 0x21, 0x12, 0x5e, 0x86, 0xba, 0x60, - 0x58, 0x10, 0x48, 0x50, 0x53, 0x90, 0xa0, 0x32, 0x5f, 0x7b, 0x04, 0xb3, 0xce, 0xcd, 0x6c, 0xa7, 0xa8, 0xeb, 0x82, - 0xfc, 0xfc, 0xa2, 0xe3, 0x11, 0xb0, 0xb4, 0x07, 0x07, 0x05, 0x44, 0x10, 0x03, 0x0a, 0x5e, 0x4a, 0x80, 0x81, 0x06, - 0xbc, 0xd8, 0xd0, 0x80, 0xcf, 0xb5, 0xf1, 0x3a, 0x30, 0xb6, 0x3e, 0x65, 0x90, 0x8b, 0x67, 0xd5, 0x9e, 0x26, 0x84, - 0xec, 0xb7, 0x7a, 0x3a, 0xdd, 0x8e, 0x90, 0xd8, 0xfb, 0xa8, 0x4d, 0xa0, 0x31, 0x47, 0xba, 0xab, 0x8d, 0xf9, 0xb5, - 0xa6, 0x47, 0xac, 0x26, 0x21, 0x5d, 0x90, 0x2e, 0xcf, 0xa7, 0x3d, 0x83, 0x2b, 0x56, 0x69, 0xe4, 0xe0, 0x02, 0xf4, - 0xd9, 0x80, 0x00, 0x05, 0x2a, 0x4d, 0x25, 0x8a, 0x22, 0x2e, 0x92, 0x92, 0x0d, 0xc3, 0x0c, 0xc2, 0x14, 0x56, 0x2b, - 0x41, 0x37, 0xd6, 0x00, 0x78, 0x67, 0x66, 0xff, 0x94, 0x3e, 0xd8, 0x74, 0xed, 0xcd, 0x23, 0x80, 0x80, 0xec, 0xb7, - 0x4b, 0x76, 0x5d, 0x6c, 0x54, 0x66, 0x61, 0x2d, 0x63, 0x2b, 0xb7, 0xed, 0x31, 0xf6, 0x4e, 0x6c, 0xf3, 0x09, 0x10, - 0xa2, 0xb6, 0x64, 0x1a, 0x21, 0x42, 0x62, 0x11, 0xeb, 0xda, 0x90, 0x8d, 0x36, 0xb4, 0x6f, 0x5e, 0xb7, 0x87, 0xd8, - 0x07, 0xa0, 0x78, 0x73, 0x5c, 0x82, 0x43, 0x78, 0xe1, 0x11, 0xfe, 0x16, 0x58, 0xa4, 0x02, 0x33, 0x2c, 0x57, 0x2b, - 0xa8, 0xe7, 0xf1, 0x3e, 0xdb, 0x0c, 0x4e, 0x2a, 0x37, 0xc6, 0x2e, 0xed, 0xc4, 0xe3, 0xb2, 0x09, 0x89, 0x33, 0xe8, - 0xd7, 0x57, 0x44, 0xbd, 0xfd, 0x76, 0xfa, 0xc4, 0xbf, 0x57, 0xe6, 0x76, 0x20, 0x36, 0xac, 0x37, 0x58, 0x7d, 0x00, - 0x2d, 0x7f, 0x95, 0xf9, 0x87, 0xca, 0x82, 0x9b, 0x04, 0xb5, 0xb9, 0x88, 0x5d, 0xd6, 0x45, 0x8c, 0xd4, 0x16, 0x77, - 0x87, 0x10, 0xff, 0x6a, 0x2b, 0x8a, 0x01, 0x4f, 0x2a, 0xfe, 0x39, 0x46, 0x5d, 0x08, 0x45, 0x6d, 0x3d, 0x6c, 0x80, - 0xd2, 0x2e, 0xd7, 0x95, 0x18, 0x19, 0x12, 0xc8, 0xb7, 0x2e, 0xbc, 0xa0, 0x39, 0x89, 0x14, 0xc8, 0xc9, 0x41, 0x54, - 0xd2, 0x6c, 0x43, 0x98, 0xeb, 0x6e, 0xe1, 0x98, 0xb9, 0xda, 0xa0, 0x45, 0xfc, 0x02, 0xd8, 0x19, 0x6e, 0x24, 0x4b, - 0x07, 0x3e, 0x55, 0x03, 0x9f, 0x5f, 0x73, 0x43, 0x51, 0x14, 0xea, 0xbd, 0xb3, 0x8f, 0xcc, 0xc1, 0xef, 0x34, 0x10, - 0x1f, 0xa9, 0xd3, 0x91, 0x6c, 0x84, 0x5a, 0x73, 0x76, 0xbc, 0x6c, 0x33, 0xc2, 0xa0, 0xb0, 0xd1, 0xfb, 0x2a, 0x64, - 0x15, 0x3b, 0x3b, 0x15, 0xc1, 0x9c, 0xbe, 0xa8, 0xca, 0x39, 0x95, 0x5b, 0x46, 0xb5, 0xd4, 0x34, 0x40, 0x84, 0x2b, - 0x9f, 0x48, 0xde, 0x67, 0x26, 0xfc, 0x83, 0xc1, 0xb8, 0x7a, 0xa4, 0xf0, 0xf7, 0xbb, 0x62, 0x87, 0x6c, 0x47, 0x87, - 0xdb, 0x08, 0x9a, 0x17, 0x2a, 0x78, 0xc0, 0x51, 0xc9, 0x12, 0x22, 0x45, 0x2e, 0xf7, 0x55, 0xcd, 0x94, 0xed, 0x3a, - 0x42, 0x08, 0x69, 0x8f, 0xb3, 0x6e, 0x68, 0xf5, 0xd0, 0x23, 0x55, 0x94, 0xc3, 0x2d, 0x9a, 0xeb, 0x02, 0x54, 0x18, - 0x81, 0x74, 0xf9, 0x99, 0xdd, 0xa5, 0x12, 0xa2, 0x97, 0xaf, 0x5d, 0x08, 0x63, 0x67, 0x65, 0x89, 0x0b, 0x33, 0x6a, - 0x1b, 0x46, 0xd7, 0x6d, 0x0c, 0x67, 0x03, 0x63, 0xa6, 0x41, 0x49, 0x0b, 0x42, 0x5d, 0x77, 0xe9, 0x45, 0x66, 0x02, - 0x3d, 0xe6, 0x84, 0x36, 0x18, 0x9e, 0x12, 0x0d, 0x96, 0x4d, 0x05, 0x58, 0xf0, 0x2d, 0x8b, 0xd4, 0xda, 0x6c, 0xb2, - 0xf8, 0xa3, 0x8e, 0xcd, 0xd3, 0x7e, 0x79, 0xc5, 0x3c, 0x17, 0x8e, 0xba, 0x3d, 0xcf, 0x7c, 0x3c, 0xba, 0xa7, 0x6f, - 0xae, 0x5e, 0xbc, 0x7c, 0xfd, 0x6a, 0xb5, 0x6a, 0xb3, 0x66, 0xfb, 0x04, 0xff, 0xa4, 0xcb, 0x78, 0xb0, 0x65, 0x14, - 0xa0, 0x83, 0x83, 0x7d, 0x6e, 0x5c, 0x78, 0x3e, 0xf3, 0x39, 0xc4, 0x0d, 0xd2, 0x03, 0x9c, 0x15, 0x65, 0x4c, 0x90, - 0xdb, 0xa8, 0x17, 0xdd, 0x45, 0xa0, 0x84, 0xaa, 0xc8, 0xdf, 0x87, 0xcd, 0xd9, 0xef, 0x41, 0x60, 0x22, 0xa8, 0x0f, - 0x11, 0x40, 0x20, 0x5e, 0x29, 0x2e, 0x08, 0xf3, 0x09, 0x10, 0xc5, 0x7b, 0x01, 0x9c, 0xa9, 0x89, 0x5a, 0xb5, 0x50, - 0x71, 0x01, 0x24, 0xd1, 0x86, 0xa3, 0xa4, 0x47, 0x26, 0x80, 0x37, 0x04, 0xa5, 0xb4, 0xbf, 0xba, 0xb9, 0x73, 0x97, - 0xca, 0x51, 0xaf, 0x95, 0xe6, 0x78, 0xea, 0x3e, 0xa7, 0xf0, 0x39, 0xed, 0xfa, 0xd3, 0x41, 0x1c, 0xe6, 0x78, 0x41, - 0xc4, 0xa1, 0x7f, 0x16, 0x71, 0x39, 0x2f, 0xd8, 0x17, 0x2e, 0x17, 0x2a, 0x5d, 0xde, 0xa6, 0x32, 0xb9, 0x6d, 0x8e, - 0x0e, 0xe3, 0x22, 0xb9, 0x6d, 0xaa, 0xe4, 0x16, 0xe1, 0xbb, 0x54, 0x26, 0x77, 0x36, 0xe5, 0xae, 0xa9, 0xe0, 0xe6, - 0x0b, 0x0b, 0x38, 0x14, 0x6d, 0xd1, 0xc6, 0x62, 0xb3, 0xa8, 0x4d, 0x71, 0x45, 0x03, 0x0c, 0xfe, 0x7d, 0xc7, 0xc6, - 0x0f, 0xc3, 0x97, 0xe0, 0xd2, 0xa4, 0x89, 0xfc, 0x04, 0xd2, 0x4f, 0xab, 0x32, 0x70, 0x9f, 0x92, 0x56, 0x77, 0x7a, - 0x21, 0x9a, 0xed, 0x6e, 0xa3, 0x31, 0x85, 0xbd, 0x9b, 0x91, 0xdc, 0x17, 0x9b, 0x36, 0x4c, 0x7c, 0x9d, 0xfd, 0x6c, - 0xb5, 0xda, 0xcf, 0x91, 0xd9, 0x70, 0x13, 0x16, 0xeb, 0xfe, 0x74, 0x80, 0x5b, 0xf8, 0x79, 0x86, 0xd0, 0x92, 0xf5, - 0xa7, 0x03, 0xc2, 0xfa, 0xd3, 0x46, 0x7b, 0x60, 0x0d, 0xed, 0xcc, 0x56, 0x5c, 0x43, 0x08, 0xcd, 0xe9, 0xe0, 0xc8, - 0x94, 0x94, 0x2e, 0xdf, 0x7e, 0xd1, 0x2a, 0xa0, 0x9f, 0xaa, 0x05, 0x2f, 0x93, 0xb8, 0x03, 0x7d, 0xd1, 0x0b, 0xfb, - 0x74, 0x6b, 0x41, 0x8e, 0x8f, 0x2a, 0x57, 0x7b, 0x8a, 0xb0, 0xe9, 0x49, 0x1d, 0x16, 0x87, 0xa6, 0x19, 0xd7, 0xa5, - 0x74, 0xdf, 0xa1, 0x66, 0xe4, 0xa3, 0x83, 0x05, 0x20, 0x48, 0x05, 0x8f, 0xac, 0x70, 0xe1, 0x94, 0x42, 0xb8, 0x38, - 0xa8, 0x6c, 0xc1, 0x24, 0x27, 0xad, 0x6e, 0x6e, 0x2c, 0xfd, 0x73, 0x17, 0xd1, 0x94, 0x62, 0x4a, 0x32, 0x5f, 0x32, - 0x37, 0x60, 0xa1, 0x9b, 0x94, 0x67, 0x0a, 0x7a, 0xa5, 0x01, 0x1e, 0x11, 0x88, 0x87, 0xd4, 0x2d, 0x8c, 0x81, 0x57, - 0x3c, 0x6d, 0x16, 0x7d, 0x36, 0x40, 0x47, 0xc7, 0x98, 0xf6, 0xff, 0xca, 0xe6, 0x6d, 0x78, 0x2c, 0xf0, 0xaf, 0x01, - 0x99, 0x36, 0x65, 0x99, 0x20, 0x20, 0x61, 0xd4, 0x94, 0x87, 0xb0, 0x97, 0x10, 0xce, 0x6c, 0xc5, 0xac, 0xcf, 0x06, - 0xcd, 0x69, 0x59, 0xb1, 0xe3, 0x2b, 0x36, 0x64, 0x99, 0x60, 0x2b, 0x36, 0x5c, 0xc5, 0xf0, 0x75, 0x06, 0x03, 0x82, - 0x10, 0x00, 0x0c, 0x00, 0xa0, 0x51, 0x10, 0xcd, 0x17, 0x2b, 0xe2, 0x37, 0xbb, 0xbd, 0xc7, 0x6f, 0x81, 0x05, 0x5a, - 0x6d, 0xff, 0xef, 0x42, 0x19, 0xb0, 0xa7, 0x2c, 0x4c, 0xcc, 0xdc, 0xc2, 0xaa, 0xe8, 0x00, 0x2a, 0x25, 0xc2, 0x14, - 0x06, 0x32, 0xfb, 0x99, 0x81, 0x5a, 0xa0, 0x35, 0xc8, 0xfb, 0x7a, 0xd0, 0xcc, 0xe0, 0x88, 0x81, 0x77, 0x68, 0xc8, - 0xd4, 0x18, 0x13, 0xc6, 0x39, 0x4c, 0x31, 0x33, 0xe0, 0x99, 0xa6, 0xad, 0xb5, 0x34, 0xb2, 0x5c, 0x2f, 0xef, 0xfd, - 0xa3, 0x63, 0xd5, 0x2f, 0x9a, 0xed, 0x01, 0xda, 0x27, 0xc4, 0x7e, 0x0c, 0x60, 0x93, 0xb9, 0xd4, 0x86, 0xf9, 0x3e, - 0xea, 0xa4, 0xf6, 0x13, 0xfe, 0x0c, 0xd6, 0x66, 0x07, 0x80, 0x8e, 0x0c, 0x9b, 0xf5, 0x97, 0x35, 0x95, 0xd7, 0xc7, - 0x9d, 0x51, 0x2a, 0x77, 0xbd, 0x3b, 0x1d, 0x68, 0x8a, 0x43, 0x6f, 0x3d, 0x5c, 0x3e, 0xd4, 0x43, 0xc0, 0x8c, 0xc1, - 0xdc, 0x32, 0xa3, 0xef, 0x85, 0x48, 0x2e, 0x88, 0xc4, 0xd2, 0x60, 0x0d, 0x83, 0xbd, 0x75, 0x70, 0x60, 0xaa, 0xb1, - 0x06, 0x3c, 0x4f, 0x8a, 0x40, 0x30, 0xf0, 0x11, 0x94, 0x01, 0x4d, 0x94, 0xb9, 0x0d, 0x27, 0x1f, 0x99, 0xfb, 0x85, - 0xcb, 0xdb, 0xc7, 0xc2, 0x69, 0x5b, 0xcd, 0xf5, 0x78, 0x59, 0xe0, 0xae, 0xbc, 0x97, 0xb4, 0x0a, 0x6e, 0x64, 0x6f, - 0xf2, 0x94, 0xb9, 0x5b, 0xf7, 0xa5, 0x3a, 0xbb, 0x9b, 0xe9, 0x94, 0xcd, 0x74, 0xb6, 0x9b, 0x09, 0x35, 0x33, 0xdf, - 0xb2, 0x8a, 0x34, 0x27, 0x6b, 0xa2, 0xe6, 0x54, 0xfc, 0x44, 0xe7, 0xa0, 0x1d, 0xe5, 0xf6, 0x5e, 0x15, 0x4e, 0xae, - 0x9c, 0x5c, 0xee, 0xe7, 0x86, 0xb8, 0x22, 0x73, 0xa1, 0x0e, 0x01, 0x5e, 0x5e, 0x94, 0x8f, 0x0f, 0x70, 0x29, 0x7e, - 0x95, 0x23, 0x17, 0xe5, 0x54, 0x48, 0x2d, 0x05, 0x8b, 0x90, 0x41, 0x55, 0x17, 0x03, 0x7b, 0x69, 0xf7, 0x9e, 0xe8, - 0xf1, 0x7e, 0x15, 0x31, 0x6f, 0x60, 0x9e, 0xfb, 0xf8, 0x9e, 0xa6, 0xd8, 0xa9, 0x89, 0x33, 0xf2, 0x21, 0x8b, 0x73, - 0x90, 0xcd, 0xfa, 0xd5, 0x6b, 0xbf, 0x8d, 0x36, 0x2e, 0x9a, 0xb1, 0xe8, 0x99, 0x27, 0x4e, 0x7e, 0x28, 0x8c, 0x71, - 0x80, 0x75, 0xf4, 0x47, 0x98, 0x5a, 0xb0, 0x67, 0x89, 0xa7, 0xd0, 0xc9, 0xad, 0x4d, 0xbb, 0x0b, 0xd3, 0xee, 0x4c, - 0x5a, 0x07, 0xca, 0x01, 0x69, 0x76, 0x65, 0x3a, 0x77, 0xfe, 0xfb, 0x0e, 0x5e, 0xba, 0x5d, 0x43, 0x24, 0xee, 0xf9, - 0x23, 0x63, 0x0c, 0xf1, 0x06, 0x6c, 0x44, 0xd5, 0xc1, 0xc1, 0x1f, 0xce, 0xfb, 0xb6, 0x92, 0xfb, 0xbe, 0x15, 0x0e, - 0x6c, 0x83, 0xa9, 0x74, 0x79, 0x23, 0x99, 0x2d, 0xc0, 0xae, 0x73, 0xff, 0x1b, 0xf1, 0xf0, 0x45, 0xc8, 0xb4, 0x58, - 0x57, 0xf1, 0x57, 0x72, 0x54, 0x7a, 0x88, 0x6a, 0x88, 0x40, 0x5a, 0x59, 0x97, 0x86, 0xa6, 0xa3, 0x57, 0x53, 0x3a, - 0x92, 0x37, 0x6f, 0xa5, 0xd4, 0x03, 0xfb, 0x22, 0xb7, 0x4e, 0xe0, 0xd1, 0xc2, 0x1a, 0x43, 0x73, 0x57, 0x7a, 0x27, - 0xd9, 0x80, 0xa8, 0xf5, 0x71, 0x87, 0x92, 0x48, 0x2c, 0xaa, 0xbb, 0x10, 0x0e, 0x77, 0x21, 0x98, 0x97, 0x41, 0xdb, - 0x20, 0x76, 0xbb, 0x0b, 0xda, 0x06, 0x4e, 0xdd, 0x36, 0x70, 0x7b, 0x30, 0x58, 0xd8, 0xfb, 0xf0, 0x72, 0x2c, 0xc7, - 0xc2, 0x5f, 0x93, 0xd9, 0x07, 0x80, 0x40, 0xed, 0xc3, 0x8a, 0x27, 0x0e, 0x04, 0x89, 0x33, 0x1c, 0x7d, 0xcf, 0xd9, - 0x8d, 0xb5, 0x1c, 0x9e, 0xcd, 0x17, 0x9a, 0x8d, 0xcc, 0x1d, 0x35, 0xa8, 0xf8, 0xea, 0x7e, 0x5e, 0x3f, 0x65, 0x35, - 0xdd, 0xf8, 0x3d, 0x08, 0x23, 0xe1, 0x94, 0x1d, 0x46, 0x21, 0x61, 0x83, 0x59, 0x95, 0xf1, 0xda, 0x7e, 0x83, 0x78, - 0x0f, 0xda, 0x84, 0x13, 0x2c, 0x6a, 0x17, 0x54, 0x11, 0xb6, 0xf1, 0xc6, 0x82, 0x28, 0x0f, 0x6f, 0xb6, 0x8c, 0xa6, - 0x97, 0x6b, 0x08, 0x74, 0xdc, 0x8b, 0x9a, 0x51, 0x83, 0xa5, 0x2e, 0x28, 0xb3, 0x8f, 0x30, 0xae, 0x2e, 0x4e, 0x4c, - 0x9c, 0xf6, 0x52, 0xaf, 0xfe, 0x5b, 0x06, 0x06, 0xf8, 0x02, 0xbc, 0xc4, 0xc2, 0xe8, 0xae, 0x7d, 0xdd, 0x80, 0xfa, - 0xb2, 0xc1, 0x06, 0x68, 0xb5, 0x6a, 0x95, 0xcf, 0x40, 0xb9, 0x6b, 0x2e, 0x61, 0xaf, 0xb9, 0x84, 0xbb, 0xe6, 0x12, - 0xfe, 0x9a, 0x4b, 0x98, 0x6b, 0x2e, 0xe1, 0xaf, 0xb9, 0x3c, 0x08, 0x3f, 0x05, 0x71, 0x1c, 0x63, 0x0e, 0x71, 0x15, - 0xb5, 0x8d, 0x8c, 0x07, 0x17, 0x9e, 0xfb, 0x2c, 0x51, 0xe5, 0xf2, 0x87, 0x31, 0xe4, 0xb6, 0x6c, 0x25, 0x8c, 0xdb, - 0x14, 0x53, 0x10, 0x39, 0xfd, 0xe0, 0xa0, 0x74, 0x77, 0x06, 0x1f, 0xf5, 0x94, 0xe3, 0xa5, 0x75, 0xa2, 0xfd, 0x03, - 0x74, 0xf2, 0xe6, 0xd7, 0xc7, 0x54, 0xae, 0x89, 0x70, 0x26, 0xf7, 0xfb, 0x6d, 0x4f, 0x29, 0x3e, 0x65, 0x26, 0x3c, - 0x39, 0x4f, 0xb4, 0x11, 0x41, 0x10, 0xa2, 0x44, 0xe1, 0x8c, 0x48, 0xbb, 0xdf, 0xbd, 0x2b, 0xbc, 0x51, 0x45, 0x79, - 0xb3, 0x92, 0xc7, 0x39, 0x38, 0xb1, 0x1b, 0x2b, 0x0c, 0xd4, 0x05, 0x17, 0x82, 0xcc, 0x24, 0xfc, 0xd1, 0xcc, 0x2d, - 0x39, 0xcb, 0xca, 0xa4, 0x8f, 0xcd, 0xdc, 0x10, 0xb0, 0x82, 0xec, 0x7b, 0x98, 0x2d, 0x6f, 0x53, 0x8a, 0xef, 0xd2, - 0x0c, 0x0f, 0xe5, 0x6d, 0x5a, 0x84, 0xb6, 0x20, 0xfe, 0xe2, 0xef, 0xff, 0x65, 0xef, 0x5d, 0x9b, 0xdb, 0x36, 0xb2, - 0x76, 0xd1, 0xbf, 0x22, 0xb1, 0x6c, 0x06, 0x30, 0x9b, 0x14, 0xe5, 0xbd, 0x67, 0xaa, 0x0e, 0xa8, 0x16, 0xcb, 0xb1, - 0xe3, 0x89, 0x33, 0xf1, 0x65, 0x2c, 0x4f, 0x26, 0x19, 0x16, 0x0f, 0x03, 0x01, 0x4d, 0x01, 0x0e, 0x08, 0x30, 0x00, - 0x28, 0x91, 0x26, 0xf1, 0xdf, 0x77, 0xad, 0xb5, 0xfa, 0x0a, 0x82, 0xb2, 0xe7, 0x7d, 0xf7, 0xfb, 0xe9, 0x9c, 0x2f, - 0xb6, 0xd8, 0x68, 0x34, 0xfa, 0xde, 0xab, 0xd7, 0xe5, 0x79, 0x96, 0x5e, 0x2f, 0x0f, 0x21, 0xde, 0xa7, 0x97, 0xe6, - 0x67, 0x69, 0x2b, 0x0a, 0x70, 0x1f, 0xa1, 0x47, 0x75, 0x20, 0xd8, 0x09, 0x4f, 0x78, 0x00, 0x27, 0xab, 0x59, 0xc5, - 0x9f, 0xa4, 0x20, 0x4e, 0x14, 0x1c, 0x02, 0xae, 0xb6, 0x37, 0xe9, 0x17, 0x30, 0x7c, 0xe9, 0x60, 0xcb, 0xe1, 0x6d, - 0xb1, 0xed, 0xb1, 0x92, 0x7f, 0x00, 0xf6, 0xad, 0x9e, 0x8c, 0xd5, 0xed, 0x81, 0xb3, 0x2e, 0xa5, 0xe8, 0x78, 0x53, - 0x1c, 0xde, 0x9e, 0xcf, 0xf6, 0xdb, 0x20, 0x62, 0xbb, 0x20, 0xc3, 0x5a, 0x27, 0x0d, 0xff, 0x89, 0xb6, 0x0e, 0x16, - 0x23, 0xec, 0xff, 0xb2, 0x1e, 0x78, 0x09, 0xa9, 0xa1, 0xc0, 0xc5, 0x60, 0xc3, 0xd1, 0xda, 0x2e, 0xd3, 0xc0, 0x4d, - 0x0d, 0x7a, 0x7d, 0x4f, 0x21, 0xca, 0x4b, 0x46, 0x73, 0x23, 0x58, 0x37, 0x86, 0x5c, 0x1c, 0x8e, 0x9b, 0xe5, 0x90, - 0x97, 0x34, 0x9d, 0x06, 0xa1, 0x74, 0x67, 0x59, 0x43, 0x12, 0x65, 0x1f, 0x84, 0xda, 0xb5, 0x65, 0xbf, 0x0d, 0x6c, - 0x5f, 0xfe, 0x68, 0x18, 0xfb, 0x17, 0xcb, 0x67, 0x42, 0xba, 0x88, 0xe7, 0x20, 0x88, 0xda, 0xcf, 0xb3, 0xe1, 0xc6, - 0xbf, 0x58, 0x3f, 0x13, 0xca, 0x6f, 0x3c, 0xb7, 0xe5, 0x90, 0x3a, 0x6b, 0xe1, 0x0b, 0xe3, 0xe1, 0xc1, 0x95, 0xa1, - 0xed, 0x70, 0x10, 0xfa, 0x6f, 0xb3, 0x46, 0x70, 0x63, 0x43, 0xfb, 0x7c, 0xe1, 0xc3, 0xd6, 0x46, 0x63, 0x4d, 0x31, - 0xdd, 0x42, 0xff, 0x26, 0xb3, 0xa5, 0x3d, 0x8d, 0x4a, 0x5e, 0x9c, 0x9a, 0x46, 0x2c, 0x84, 0x01, 0x43, 0x3f, 0x99, - 0x0f, 0xa0, 0x9a, 0x3b, 0x1e, 0x81, 0x4c, 0x3e, 0xd0, 0x83, 0x35, 0xa9, 0x55, 0x7f, 0x0d, 0x33, 0xf9, 0x7f, 0xa4, - 0xc2, 0x62, 0x74, 0xb7, 0x0d, 0x33, 0xf5, 0x47, 0x24, 0xff, 0x60, 0x39, 0xdf, 0xa5, 0x5e, 0xa8, 0xfd, 0x58, 0x58, - 0x81, 0x41, 0x89, 0xaa, 0x01, 0x3d, 0x10, 0x41, 0x55, 0x06, 0x69, 0x86, 0xd5, 0x39, 0xe8, 0x77, 0x4f, 0xab, 0x8e, - 0xe4, 0x90, 0xd6, 0x6a, 0x48, 0x05, 0x53, 0xa5, 0x06, 0xf9, 0xe1, 0x70, 0x97, 0x32, 0x5d, 0x06, 0x5c, 0xd2, 0xef, - 0x52, 0xa5, 0x14, 0xfe, 0x13, 0x01, 0xe8, 0x1c, 0xdc, 0xe3, 0xcb, 0x31, 0x90, 0x66, 0x58, 0xf8, 0xad, 0xd9, 0xf1, - 0x35, 0x09, 0xb7, 0x49, 0x70, 0x31, 0xc0, 0x39, 0xba, 0x0a, 0xcb, 0xbb, 0x14, 0x22, 0xa8, 0x4a, 0xa8, 0x6f, 0x65, - 0x1a, 0x94, 0xb6, 0x1a, 0x84, 0x35, 0x09, 0x75, 0x26, 0xd9, 0xa8, 0xb4, 0xdd, 0x28, 0xcc, 0x16, 0x71, 0x3d, 0x23, - 0xac, 0x39, 0x9b, 0xa9, 0x06, 0x26, 0x0d, 0xc7, 0x4d, 0xa3, 0xb5, 0xa8, 0x50, 0x53, 0x98, 0xd7, 0xb8, 0xaa, 0x54, - 0x75, 0x37, 0xa7, 0x96, 0xd2, 0xb2, 0xbd, 0xea, 0x26, 0xd9, 0x90, 0xcb, 0x50, 0x86, 0xc1, 0x46, 0x8e, 0x60, 0x02, - 0x49, 0x72, 0xe6, 0x6f, 0xe4, 0x1f, 0x6a, 0xd3, 0xb5, 0x80, 0x39, 0xc6, 0x2c, 0x1b, 0x16, 0xf4, 0x0a, 0xdc, 0x03, - 0xad, 0xf4, 0x7c, 0x9a, 0x5d, 0xe4, 0x41, 0x32, 0x2c, 0xf4, 0xb2, 0xc9, 0xf8, 0x9f, 0xc2, 0x48, 0x93, 0x19, 0x2b, - 0x59, 0x64, 0xbb, 0x3a, 0x25, 0xce, 0xe3, 0x04, 0xb6, 0x47, 0xd3, 0x5b, 0xbe, 0xcf, 0x20, 0x2a, 0x08, 0x14, 0xcc, - 0x98, 0x2f, 0xbb, 0x78, 0xee, 0xfb, 0xcc, 0x32, 0x75, 0x1f, 0x0e, 0xc6, 0x8c, 0xed, 0xf7, 0xfb, 0x79, 0xbf, 0xaf, - 0xe6, 0x5b, 0xbf, 0x9f, 0x5c, 0x9b, 0xbf, 0x3d, 0x60, 0x50, 0x90, 0x13, 0xd1, 0x54, 0x88, 0xe0, 0x1f, 0x92, 0x67, - 0x48, 0x46, 0x77, 0xdc, 0xe7, 0x96, 0xb3, 0x65, 0x75, 0x04, 0x82, 0x79, 0x38, 0x5c, 0x2a, 0xb0, 0x6b, 0x89, 0x22, - 0x21, 0xcb, 0x7f, 0x06, 0xc6, 0x33, 0xf7, 0x01, 0x96, 0x0c, 0x40, 0xd8, 0x2a, 0x4f, 0xd7, 0x7b, 0xbe, 0x0a, 0xde, - 0xe9, 0x78, 0xd7, 0x58, 0x91, 0x81, 0xb8, 0x05, 0x36, 0x62, 0xad, 0x3d, 0x20, 0x67, 0x0a, 0x70, 0xbc, 0x38, 0x1c, - 0xce, 0xe5, 0x2f, 0xdd, 0x6c, 0x9d, 0x40, 0xa5, 0xc0, 0xed, 0xd1, 0xc9, 0xc1, 0x7f, 0x07, 0x9a, 0x41, 0x39, 0xcc, - 0xeb, 0xed, 0xef, 0xcc, 0xc9, 0x4f, 0x4f, 0xf1, 0x4f, 0x78, 0x88, 0x4e, 0xbf, 0xdd, 0x9b, 0x3f, 0x28, 0x2a, 0x0f, - 0x07, 0xb5, 0xf8, 0xcf, 0x39, 0xaf, 0xe0, 0x17, 0xbe, 0x09, 0xcc, 0x26, 0x53, 0xef, 0xe4, 0x9b, 0x3c, 0x67, 0xea, - 0x35, 0x5e, 0x31, 0xf9, 0x0e, 0x87, 0x73, 0x31, 0xaa, 0xb7, 0x23, 0x27, 0xda, 0x29, 0xc7, 0x38, 0x18, 0xfc, 0x17, - 0xd1, 0x36, 0x21, 0xc0, 0x90, 0xba, 0x25, 0xcd, 0x6c, 0x5c, 0x59, 0xe2, 0x59, 0x3a, 0xbf, 0x9c, 0xd4, 0xe5, 0x4e, - 0x2b, 0x9e, 0xf6, 0xc0, 0xe2, 0xb6, 0x06, 0x2f, 0x80, 0x7b, 0x8b, 0xad, 0x2b, 0x05, 0x87, 0x0b, 0x88, 0x53, 0x9c, - 0x80, 0x08, 0xda, 0xef, 0x4b, 0xbc, 0x57, 0xd0, 0x27, 0xfd, 0x00, 0xc1, 0x90, 0x3f, 0x4b, 0xc0, 0x5d, 0xaf, 0x57, - 0x63, 0x7c, 0x2f, 0x85, 0xe0, 0xfa, 0x4c, 0x03, 0xd0, 0x82, 0xdf, 0xe5, 0x63, 0x39, 0xfd, 0x26, 0x02, 0xcf, 0x96, - 0xbd, 0x89, 0x72, 0xb7, 0xe1, 0x69, 0xff, 0x68, 0x21, 0x00, 0x4b, 0xf1, 0x4c, 0x09, 0x16, 0xe4, 0x14, 0x73, 0xf1, - 0xff, 0x82, 0x8f, 0x98, 0xef, 0x49, 0x17, 0xb1, 0xf5, 0xf6, 0xc9, 0x85, 0x81, 0x04, 0x9a, 0x0e, 0xc0, 0x8f, 0x57, - 0x01, 0x5d, 0x19, 0x3f, 0x3f, 0xcb, 0x7a, 0xac, 0x8f, 0xff, 0x14, 0xdc, 0xa7, 0x9f, 0x29, 0x7c, 0x74, 0x38, 0xae, - 0xd2, 0xd1, 0x8e, 0x52, 0x10, 0x1d, 0xdd, 0x3e, 0x9f, 0xf2, 0xec, 0x9b, 0x0a, 0xc8, 0x2d, 0x47, 0xed, 0xa9, 0x00, - 0x2c, 0xb6, 0x74, 0x04, 0x3e, 0xcd, 0xf2, 0x09, 0xf9, 0x5e, 0x4f, 0xc5, 0xd5, 0xa5, 0x4e, 0x17, 0xd7, 0xe3, 0x29, - 0xfc, 0x0f, 0xc4, 0x1e, 0x96, 0x29, 0xb2, 0x63, 0xd7, 0xc5, 0x0f, 0xe2, 0x6d, 0x6d, 0x47, 0x7f, 0xec, 0x20, 0xd2, - 0x71, 0x4f, 0x2e, 0xd4, 0x97, 0x90, 0x4a, 0x2e, 0xd4, 0x0d, 0xc4, 0x2e, 0xd4, 0x78, 0xc7, 0x45, 0xac, 0xf5, 0xb7, - 0x35, 0x0a, 0x56, 0x02, 0xce, 0xb4, 0xb7, 0x60, 0xb0, 0x81, 0x75, 0xcb, 0x32, 0xf8, 0x1b, 0xae, 0x69, 0x02, 0x37, - 0x2c, 0xb2, 0xde, 0x1b, 0x6c, 0xa5, 0xb7, 0xe0, 0x68, 0x99, 0x38, 0x97, 0x92, 0xac, 0x6c, 0x91, 0x71, 0xf5, 0x28, - 0xa4, 0x6a, 0xba, 0xbf, 0x15, 0xf5, 0x83, 0x10, 0x79, 0xb0, 0x4a, 0x59, 0x54, 0xac, 0x40, 0x66, 0x0f, 0xfe, 0x11, - 0x32, 0x72, 0x94, 0x03, 0x47, 0xa1, 0xbf, 0x35, 0x81, 0xce, 0xf3, 0x53, 0xa8, 0xf3, 0x48, 0xb0, 0x95, 0x7a, 0x28, - 0xac, 0xbc, 0x80, 0xe8, 0x60, 0x0b, 0x63, 0x95, 0x27, 0xa1, 0x62, 0x53, 0x26, 0xf2, 0x38, 0xa8, 0x25, 0x60, 0xac, - 0x20, 0x98, 0xb3, 0x5c, 0xba, 0x20, 0x55, 0x8d, 0x1e, 0x16, 0x99, 0xfb, 0xa9, 0xa0, 0xfc, 0x4f, 0x55, 0x4e, 0xb8, - 0xbe, 0x0c, 0x01, 0x8e, 0xf6, 0x29, 0x88, 0x12, 0x63, 0xfd, 0xa2, 0xc5, 0x3b, 0x99, 0x39, 0x9b, 0xda, 0x5e, 0x82, - 0x8c, 0xed, 0xf0, 0x2b, 0x84, 0x56, 0x0b, 0x45, 0x16, 0x0d, 0x17, 0x4c, 0xb7, 0xa7, 0xb4, 0xea, 0x1e, 0x36, 0x3c, - 0x2b, 0x3d, 0x54, 0xea, 0xdb, 0x98, 0xc0, 0xb2, 0x4a, 0x19, 0xbe, 0x9d, 0x50, 0x75, 0x62, 0x50, 0xb1, 0x6e, 0xd8, - 0x12, 0x0e, 0xb1, 0x98, 0x34, 0xd6, 0xd9, 0x80, 0x47, 0x2c, 0x81, 0x7f, 0x36, 0x7c, 0xcc, 0x96, 0x3c, 0x9a, 0x6c, - 0xae, 0x96, 0xfd, 0x7e, 0xe9, 0x85, 0x5e, 0x3d, 0xcb, 0x9e, 0x46, 0xf3, 0x59, 0x3e, 0xf7, 0x51, 0x71, 0x31, 0x19, - 0x0c, 0x36, 0x7e, 0x36, 0x1c, 0xb2, 0x64, 0x38, 0x9c, 0x64, 0x4f, 0xe1, 0xb5, 0xa7, 0x3c, 0x52, 0x4b, 0x2a, 0xb9, - 0xca, 0x60, 0x7f, 0x1f, 0xf0, 0xc8, 0x67, 0x9d, 0x9f, 0x96, 0x4d, 0x97, 0xee, 0x67, 0x76, 0xdc, 0x85, 0xee, 0x00, - 0x1b, 0x6f, 0x1b, 0x74, 0xe4, 0x5f, 0xef, 0x90, 0x52, 0x37, 0x19, 0x80, 0xdd, 0x68, 0x80, 0x43, 0xa6, 0x7a, 0x29, - 0xb2, 0x7a, 0x29, 0x53, 0xbd, 0x24, 0x2b, 0x97, 0x60, 0x21, 0x31, 0x55, 0x6e, 0x23, 0x2b, 0xb7, 0x6c, 0xb8, 0x1e, - 0x0e, 0xb6, 0x56, 0x5c, 0x36, 0x77, 0x70, 0x5f, 0x58, 0x51, 0xe0, 0xff, 0x2d, 0x5b, 0xb0, 0x7b, 0x79, 0x0c, 0xbc, - 0x45, 0xc7, 0x24, 0xb8, 0x40, 0xdc, 0xb3, 0x5b, 0xb0, 0xc3, 0xc2, 0x5f, 0x70, 0x9d, 0x1c, 0xb3, 0x1d, 0x3e, 0x0a, - 0xbd, 0x82, 0xdd, 0xfa, 0x04, 0xb4, 0x0b, 0xb6, 0x06, 0xc8, 0xc6, 0xb6, 0xf8, 0xe8, 0xee, 0x70, 0x78, 0xeb, 0xf9, - 0xec, 0x01, 0x7f, 0x9c, 0xdf, 0x1d, 0x0e, 0x3b, 0xcf, 0xa8, 0xf7, 0x6e, 0x78, 0xc2, 0xde, 0xf3, 0x64, 0x72, 0x73, - 0xc5, 0xe3, 0xc9, 0x60, 0x70, 0xe3, 0x2f, 0x78, 0x3d, 0xbb, 0x01, 0xed, 0xc0, 0xf9, 0x42, 0xea, 0x9a, 0xbd, 0x5b, - 0x9e, 0x79, 0x0b, 0x1c, 0x9b, 0x5b, 0x38, 0x7a, 0xfb, 0x7d, 0xef, 0x8e, 0x47, 0xde, 0x2d, 0xa9, 0x98, 0x56, 0x5c, - 0x71, 0xbc, 0x6d, 0x71, 0x3f, 0x5d, 0xf1, 0x10, 0x1e, 0x61, 0x55, 0xa6, 0x37, 0xc1, 0x7b, 0x9f, 0xad, 0x34, 0x0b, - 0xdc, 0x03, 0xe6, 0x58, 0x93, 0x9d, 0xd0, 0x4c, 0xfc, 0x15, 0xf6, 0xcf, 0x8d, 0xea, 0x1f, 0x9a, 0xff, 0xa5, 0xee, - 0x27, 0x70, 0xfb, 0x22, 0x0b, 0x12, 0x7b, 0xcf, 0x6f, 0xd8, 0x3d, 0x37, 0x6c, 0xb3, 0x67, 0xa6, 0xec, 0x13, 0xa5, - 0xc6, 0x8f, 0x94, 0xba, 0xb6, 0x0c, 0x2b, 0x99, 0xbb, 0x2f, 0x23, 0x70, 0x38, 0x20, 0x3f, 0xdd, 0x21, 0x0e, 0x42, - 0xeb, 0x26, 0xab, 0xb9, 0xa2, 0x9c, 0x0b, 0x6d, 0x99, 0x79, 0x39, 0xb0, 0x98, 0xa5, 0x14, 0x1a, 0x0b, 0x00, 0x04, - 0x93, 0x42, 0x6b, 0xef, 0x65, 0x00, 0x39, 0x41, 0xc3, 0x1f, 0x9b, 0xab, 0xa2, 0xac, 0x65, 0x4b, 0x42, 0x94, 0xed, - 0x7a, 0x78, 0x89, 0x90, 0x69, 0xfd, 0xfe, 0x39, 0x91, 0xac, 0x4d, 0xaa, 0xab, 0x1a, 0x2d, 0x01, 0x15, 0x59, 0x02, - 0x26, 0x7e, 0xa5, 0xf9, 0x04, 0xe0, 0x49, 0xc7, 0x83, 0xea, 0x29, 0xaf, 0x99, 0x20, 0xb2, 0x8d, 0xca, 0x9f, 0x14, - 0xd7, 0x48, 0x46, 0x50, 0x3c, 0xad, 0x55, 0xc6, 0xc2, 0x30, 0x0f, 0x14, 0x90, 0x77, 0xef, 0x4e, 0x7d, 0x6b, 0x7f, - 0xec, 0xd8, 0xb3, 0xb5, 0x0a, 0xb5, 0x50, 0x53, 0xb8, 0xe4, 0x10, 0x5d, 0x41, 0x06, 0x0a, 0x19, 0x4f, 0x5e, 0x0f, - 0x2e, 0x27, 0xd1, 0x15, 0x17, 0xe8, 0x8c, 0xaf, 0x6f, 0xba, 0xe9, 0x2c, 0x7a, 0x5a, 0xcd, 0x27, 0xa4, 0x24, 0x3b, - 0x1c, 0xb2, 0x51, 0x55, 0x17, 0xeb, 0x69, 0x28, 0x7f, 0x7a, 0x08, 0xbe, 0x5e, 0x50, 0xaf, 0xc9, 0x2a, 0xd5, 0x4f, - 0xa9, 0x52, 0x5e, 0x34, 0xbc, 0xf4, 0x9f, 0x56, 0x72, 0xdf, 0x03, 0xd2, 0x5a, 0x5e, 0x72, 0xf9, 0x7e, 0x84, 0x18, - 0x23, 0x7e, 0xe0, 0x95, 0x3c, 0x62, 0xa1, 0x9a, 0xc2, 0x35, 0x8f, 0x10, 0xe4, 0x2d, 0xd3, 0xc1, 0xdf, 0x7a, 0xe2, - 0x74, 0x7f, 0xa2, 0xb4, 0x8b, 0x2f, 0x2c, 0xea, 0x9e, 0xac, 0xad, 0x1b, 0x90, 0x83, 0x0d, 0xd3, 0x45, 0x41, 0xb6, - 0x29, 0x8d, 0xa0, 0x8d, 0x96, 0x03, 0x1b, 0x4e, 0xa5, 0x36, 0x9c, 0xb9, 0x86, 0xe0, 0x3e, 0x3f, 0x4f, 0x47, 0x0b, - 0xf8, 0x90, 0xea, 0xf6, 0x12, 0x3f, 0x1f, 0x36, 0x3c, 0x02, 0x32, 0x3b, 0xe2, 0x33, 0x9b, 0x48, 0x3a, 0xa9, 0x73, - 0x05, 0xec, 0x76, 0xf6, 0x16, 0xe4, 0x88, 0x99, 0xfb, 0x0a, 0xd5, 0xb7, 0x68, 0xc0, 0x95, 0xb1, 0xf6, 0x35, 0xc9, - 0x58, 0x78, 0x55, 0x4e, 0xc3, 0x01, 0xc0, 0xd0, 0x65, 0xf4, 0xb5, 0xe5, 0x26, 0xcb, 0x7e, 0x2e, 0x20, 0x08, 0xa2, - 0x24, 0x1e, 0x1f, 0xf0, 0xbe, 0xac, 0x86, 0x1a, 0x25, 0x1f, 0xcb, 0x46, 0x2a, 0xbd, 0x12, 0xfd, 0xdd, 0x98, 0x4b, - 0x0c, 0xf8, 0xb6, 0x6a, 0x0b, 0x0a, 0xe7, 0xf9, 0xe1, 0x70, 0x9e, 0x8f, 0x8c, 0x67, 0x19, 0xa8, 0x56, 0xa6, 0x75, - 0x10, 0x9b, 0xf9, 0x62, 0xe1, 0x2f, 0x76, 0x4e, 0x22, 0xa2, 0x20, 0xb0, 0x23, 0xe1, 0x41, 0xa4, 0x7e, 0x59, 0x79, - 0xba, 0x53, 0x7d, 0xb6, 0x5f, 0xd8, 0x44, 0x7a, 0x41, 0xc9, 0xe4, 0x93, 0x60, 0xaf, 0xfa, 0x3b, 0x08, 0x1b, 0xc2, - 0x9b, 0x57, 0xbd, 0xce, 0x32, 0x35, 0x2b, 0x41, 0xc2, 0x8c, 0x39, 0x82, 0xc7, 0x61, 0xa7, 0xb1, 0x0d, 0x8f, 0x2d, - 0x38, 0x3a, 0x6f, 0xcd, 0xee, 0xd8, 0x8a, 0xdd, 0xaa, 0x3a, 0x2d, 0x78, 0x38, 0x1d, 0x5e, 0x06, 0xb8, 0xfa, 0xd6, - 0xe7, 0x9c, 0xdf, 0xd1, 0x09, 0xb6, 0x1e, 0xf0, 0x68, 0x22, 0x66, 0xeb, 0xa7, 0x91, 0x5a, 0x3c, 0xeb, 0x21, 0x5f, - 0xd0, 0xfa, 0x13, 0xb3, 0x3b, 0x93, 0x7c, 0x37, 0xe0, 0x8b, 0xc9, 0xfa, 0x69, 0x04, 0xaf, 0x3e, 0x05, 0x2b, 0x46, - 0xe6, 0xcc, 0xb2, 0xf5, 0xd3, 0x08, 0xc7, 0xec, 0xee, 0x69, 0x44, 0xa3, 0xb6, 0x92, 0xfb, 0xd2, 0x6d, 0x03, 0xc2, - 0xca, 0x2d, 0x8b, 0xe1, 0x35, 0x10, 0xcf, 0xb4, 0x91, 0x74, 0x2d, 0x0d, 0xbd, 0x31, 0x0f, 0xa7, 0x71, 0xb0, 0xa6, - 0x56, 0xc8, 0x33, 0x43, 0xcc, 0xe2, 0xa7, 0xd1, 0x9c, 0xad, 0xb0, 0x22, 0x1b, 0x1e, 0x0f, 0x2e, 0x27, 0x9b, 0x2b, - 0xbe, 0x06, 0xf2, 0xb3, 0xc9, 0xc6, 0x6c, 0x51, 0xb7, 0x5c, 0xcc, 0x36, 0x4f, 0xa3, 0xf9, 0x64, 0x05, 0x3d, 0x6b, - 0x0f, 0x98, 0xf7, 0x1a, 0x44, 0x28, 0x09, 0xa9, 0x29, 0x37, 0xbd, 0x1e, 0x5b, 0x8f, 0x83, 0x3b, 0xb6, 0xbe, 0x0c, - 0x6e, 0xd9, 0x7a, 0x0c, 0x44, 0x1c, 0xd4, 0xef, 0xde, 0x06, 0x16, 0x5f, 0xc4, 0xd6, 0x97, 0x26, 0x6d, 0xf3, 0x34, - 0x62, 0xee, 0xe0, 0x34, 0x70, 0xc1, 0xda, 0x64, 0xde, 0x8a, 0xc1, 0x25, 0x64, 0xe9, 0xc5, 0x6c, 0x33, 0xbc, 0x64, - 0xeb, 0x11, 0x4e, 0xf5, 0xc4, 0x67, 0x77, 0xfc, 0x96, 0x25, 0x7c, 0xd5, 0xc4, 0x57, 0x1b, 0xd0, 0x88, 0x1e, 0x65, - 0xd0, 0x57, 0x50, 0x33, 0x73, 0x5e, 0x5a, 0x18, 0x95, 0xfb, 0x16, 0x1c, 0x50, 0x90, 0xb6, 0x01, 0x82, 0x24, 0x9e, - 0xdd, 0xcb, 0x70, 0x7d, 0x23, 0x85, 0x01, 0x37, 0x81, 0x19, 0x30, 0x30, 0xfd, 0x0c, 0x7e, 0x58, 0xe9, 0x12, 0x21, - 0xce, 0x7e, 0x4a, 0x49, 0x32, 0xcf, 0x4f, 0x45, 0x9a, 0xbb, 0x85, 0xeb, 0x14, 0x66, 0x45, 0x81, 0xea, 0xa7, 0xa4, - 0x34, 0xb0, 0x50, 0x89, 0x4c, 0xa5, 0xe0, 0x97, 0xcd, 0x79, 0x94, 0x1d, 0xa3, 0x73, 0x9d, 0x5f, 0x4e, 0x9c, 0xd3, - 0x49, 0xdf, 0x7f, 0xe0, 0x18, 0xb6, 0x90, 0x81, 0x0b, 0x7f, 0xea, 0x09, 0xe3, 0xd4, 0x0a, 0xc4, 0x54, 0xf2, 0xec, - 0x29, 0x7c, 0x26, 0xb4, 0x3a, 0xba, 0xf0, 0xfd, 0xa0, 0xd0, 0x26, 0xe9, 0x16, 0x24, 0x29, 0x78, 0x8a, 0x9e, 0x73, - 0xde, 0x06, 0x2a, 0xc5, 0x88, 0x16, 0x44, 0xda, 0x5a, 0x66, 0x0e, 0xd2, 0x96, 0xe6, 0xbb, 0x26, 0x7e, 0x0e, 0x0b, - 0xb8, 0x88, 0x16, 0xb6, 0x86, 0x47, 0x55, 0xac, 0xdc, 0x9b, 0x3c, 0x47, 0x38, 0xa3, 0x4b, 0x99, 0x00, 0xb8, 0xde, - 0xaf, 0xc2, 0x5a, 0xe1, 0x15, 0x35, 0x8b, 0xbc, 0xa8, 0xe9, 0x93, 0x2d, 0x70, 0x1f, 0x8b, 0x12, 0x05, 0xce, 0x5a, - 0x30, 0x60, 0x2b, 0x2c, 0xd9, 0x49, 0x61, 0x53, 0xb4, 0x84, 0xde, 0x1e, 0x3f, 0x1d, 0xd4, 0x4c, 0x06, 0xd0, 0x04, - 0xd0, 0x78, 0xfc, 0x0b, 0x40, 0x4d, 0x6f, 0x6a, 0xb1, 0xae, 0x82, 0x52, 0x29, 0x37, 0xe1, 0x67, 0x60, 0x98, 0xe1, - 0x87, 0x42, 0x6e, 0x13, 0x25, 0x72, 0x7e, 0x2c, 0x4a, 0xb1, 0x2c, 0x45, 0x95, 0xb4, 0x1b, 0x0a, 0x1e, 0x11, 0x6e, - 0x83, 0xc6, 0xcc, 0xed, 0x89, 0x2e, 0x5a, 0x11, 0xca, 0xb1, 0x59, 0xc7, 0x48, 0xa3, 0xcc, 0x4e, 0x76, 0x9d, 0x2c, - 0xb4, 0xdf, 0x57, 0x39, 0x64, 0x1d, 0xb0, 0x46, 0xf2, 0xf5, 0x9a, 0x43, 0xb7, 0x8d, 0xf2, 0xe2, 0xc1, 0xf3, 0x15, - 0x9c, 0xe6, 0x78, 0x62, 0x77, 0xbd, 0xee, 0x14, 0x89, 0x78, 0x85, 0x93, 0x2a, 0x1f, 0xc9, 0xc2, 0x71, 0xe7, 0x4e, - 0x6b, 0xb1, 0xaa, 0x5c, 0xd6, 0x53, 0x8b, 0x23, 0x02, 0x9f, 0xca, 0xa3, 0xbd, 0xd0, 0xb6, 0x28, 0x16, 0xc2, 0xe8, - 0xd1, 0x09, 0x3f, 0x29, 0x81, 0xf5, 0x75, 0x38, 0x2c, 0xfd, 0x88, 0xa3, 0xdf, 0x69, 0x34, 0x5a, 0x10, 0xd2, 0xf0, - 0xd4, 0x8b, 0x46, 0x8b, 0xba, 0xa8, 0xc3, 0xec, 0x3a, 0xd7, 0x03, 0x85, 0x61, 0x04, 0xea, 0x07, 0x57, 0x19, 0x7c, - 0x16, 0x21, 0x6a, 0x1e, 0x98, 0x66, 0x43, 0x38, 0xea, 0x02, 0x0f, 0xad, 0xa0, 0xc5, 0xcc, 0x7c, 0x14, 0x62, 0xf8, - 0x90, 0x2e, 0xce, 0x9f, 0x90, 0x95, 0x0f, 0xb0, 0x3b, 0x74, 0x17, 0xca, 0x39, 0x53, 0x31, 0xc0, 0x8f, 0x02, 0xf2, - 0x51, 0x02, 0x6e, 0x06, 0xc8, 0x1e, 0x59, 0x02, 0x88, 0x15, 0xa3, 0xa3, 0xc9, 0xe7, 0xbe, 0x17, 0x29, 0x78, 0x67, - 0x9f, 0xe5, 0x6a, 0xc2, 0x50, 0xf8, 0xc4, 0x40, 0x37, 0xbf, 0xf1, 0xdb, 0xf3, 0x16, 0x8c, 0xec, 0x92, 0x14, 0xaf, - 0x35, 0xc3, 0xfd, 0x06, 0xdc, 0x8e, 0x80, 0xb2, 0xa6, 0x3a, 0x26, 0xd9, 0xa6, 0x21, 0x92, 0x01, 0x33, 0x62, 0x44, - 0x50, 0x59, 0x2e, 0xfc, 0xef, 0x5e, 0x16, 0x05, 0x0e, 0xe0, 0x6a, 0x26, 0x83, 0xd7, 0x2e, 0x8c, 0x0a, 0x80, 0x73, - 0x1a, 0x3a, 0xa5, 0xbd, 0xaa, 0x3a, 0x24, 0xab, 0xe6, 0x07, 0xb3, 0x79, 0xd3, 0x30, 0x31, 0x22, 0x88, 0x2e, 0xc2, - 0x09, 0xa6, 0x57, 0xa4, 0xaf, 0x95, 0x9c, 0x8e, 0x56, 0x1d, 0xad, 0x25, 0x26, 0xe6, 0x8a, 0xe2, 0xaf, 0x01, 0x8f, - 0x1b, 0xbc, 0x3a, 0x49, 0xd3, 0x89, 0xea, 0xd1, 0xe3, 0xd7, 0x69, 0x3a, 0x29, 0x71, 0x57, 0xf8, 0x0d, 0xb8, 0x68, - 0xb6, 0xf9, 0xd0, 0x8f, 0x5f, 0x50, 0xc4, 0x45, 0x0d, 0xae, 0xbc, 0x53, 0x7d, 0xa5, 0xfa, 0x08, 0x6a, 0xe1, 0x89, - 0x91, 0xb5, 0xf0, 0xe4, 0x92, 0xb5, 0x16, 0x04, 0x33, 0x9b, 0x03, 0x17, 0xf2, 0x2b, 0xa5, 0x88, 0x37, 0x91, 0x50, - 0x8b, 0x41, 0xeb, 0x31, 0x73, 0x56, 0x8d, 0x16, 0x2a, 0x33, 0x42, 0xfb, 0xb6, 0x16, 0x9d, 0xdf, 0xc8, 0x4f, 0x79, - 0x6a, 0x5f, 0xb6, 0xc7, 0xf9, 0x78, 0x8f, 0xee, 0xaa, 0xb3, 0xcc, 0xa4, 0x8c, 0x4f, 0x66, 0x09, 0x0a, 0x77, 0x09, - 0x36, 0x20, 0xc9, 0x7e, 0xad, 0x03, 0x64, 0xd4, 0x5e, 0xfb, 0x5d, 0x67, 0xf9, 0xea, 0x66, 0x6b, 0x28, 0x2a, 0xb5, - 0x92, 0x14, 0x07, 0x19, 0xae, 0xdb, 0xca, 0x87, 0x8b, 0x0b, 0xe8, 0x19, 0x23, 0x91, 0x79, 0xfe, 0x44, 0xbe, 0x04, - 0xe7, 0x8c, 0xb3, 0x42, 0x60, 0xc2, 0x58, 0xbd, 0x6b, 0x2d, 0x95, 0x86, 0x14, 0x63, 0x47, 0xa3, 0x2c, 0xab, 0x2c, - 0x5d, 0x66, 0x6b, 0x09, 0x5b, 0x96, 0x93, 0x5b, 0xd8, 0x32, 0x93, 0xd5, 0x7c, 0x5f, 0x71, 0x07, 0xe5, 0x9b, 0xad, - 0x33, 0xbe, 0x97, 0xc8, 0xde, 0x6d, 0xa0, 0x84, 0xeb, 0xd1, 0x5f, 0x90, 0x7e, 0x9b, 0x61, 0x9c, 0x72, 0x5b, 0x49, - 0x0b, 0x70, 0xfa, 0x87, 0xc3, 0xfb, 0x0a, 0x83, 0x06, 0x47, 0x18, 0x47, 0xd6, 0xef, 0xdf, 0x56, 0x5e, 0x8d, 0x89, - 0x3a, 0x3e, 0xab, 0xdf, 0xaf, 0xe8, 0xe1, 0xb4, 0x1a, 0xad, 0xd2, 0x2d, 0xb2, 0x13, 0xda, 0x58, 0xf9, 0x41, 0xad, - 0x80, 0xd9, 0x5b, 0x9f, 0x4f, 0x07, 0xa0, 0x63, 0x01, 0x12, 0xcd, 0x66, 0x22, 0x31, 0x27, 0xdd, 0x93, 0xf0, 0xf8, - 0xc0, 0x02, 0x07, 0x98, 0x8a, 0xff, 0x53, 0x78, 0x33, 0xb0, 0x41, 0xa3, 0x44, 0x5f, 0xa3, 0xab, 0xda, 0xdc, 0xe8, - 0x78, 0xe9, 0x29, 0x24, 0xb2, 0x82, 0x55, 0x73, 0x5f, 0x6e, 0xe0, 0xb4, 0x87, 0x9a, 0x43, 0x65, 0x09, 0xfe, 0xf6, - 0xcb, 0xfc, 0x70, 0x58, 0x67, 0x50, 0xd8, 0x6e, 0x2d, 0xb4, 0x37, 0x66, 0xa9, 0x86, 0x8a, 0x70, 0xd0, 0xf9, 0x4a, - 0xcc, 0xea, 0x11, 0xfd, 0x3d, 0x3f, 0x1c, 0x56, 0x04, 0x06, 0x1c, 0x96, 0x32, 0x13, 0x2d, 0x14, 0x4b, 0xeb, 0x6c, - 0x46, 0x75, 0xe0, 0x81, 0x89, 0x39, 0x0b, 0x77, 0x00, 0xda, 0xa4, 0x56, 0x81, 0x5e, 0x45, 0xf4, 0x13, 0xf7, 0x6b, - 0xfb, 0xf5, 0x7a, 0x64, 0x96, 0x8e, 0xdc, 0x18, 0x0b, 0x00, 0x0e, 0x3c, 0xaf, 0x49, 0x9e, 0x93, 0xaf, 0xa1, 0xdd, - 0x93, 0x0b, 0xf9, 0x13, 0x94, 0x2d, 0x3c, 0x57, 0x4d, 0x2b, 0x8b, 0x15, 0x57, 0xd5, 0xab, 0x0b, 0x5e, 0x99, 0x4c, - 0xab, 0xb4, 0x12, 0x95, 0x12, 0x0c, 0xa8, 0x4b, 0xbc, 0xd6, 0x34, 0xa3, 0xd4, 0x46, 0x9d, 0x89, 0x1a, 0xb0, 0xc1, - 0x7e, 0xaa, 0x36, 0x3a, 0x39, 0x97, 0xcf, 0x2f, 0x8d, 0xc3, 0xa7, 0x5d, 0xbd, 0x99, 0xa9, 0x1c, 0xf8, 0x6b, 0xe5, - 0x43, 0xab, 0xc7, 0x40, 0x07, 0xe4, 0xf4, 0xc7, 0xb0, 0x98, 0xd8, 0x1d, 0x9a, 0xb7, 0xbb, 0xcb, 0xea, 0x22, 0xbd, - 0xd3, 0x94, 0xcc, 0xea, 0x2d, 0x9f, 0x59, 0x3d, 0x3a, 0xe0, 0xc5, 0x63, 0xbd, 0x57, 0x98, 0x49, 0x04, 0x17, 0x43, - 0x35, 0x89, 0xec, 0x0e, 0xb4, 0xe6, 0x51, 0xc5, 0x04, 0xf8, 0x41, 0xa9, 0x35, 0xbd, 0xb7, 0xbb, 0x42, 0x9d, 0x52, - 0x78, 0xdc, 0x5a, 0xf2, 0x03, 0x73, 0xa7, 0x5d, 0xeb, 0x7c, 0x3c, 0xbf, 0xf4, 0xfd, 0x46, 0x9e, 0xd0, 0x66, 0x67, - 0x72, 0xfa, 0x27, 0x6f, 0xf5, 0x0f, 0x53, 0x7d, 0x0b, 0xdd, 0x09, 0xfa, 0x0c, 0x5d, 0x55, 0xdd, 0x95, 0xd8, 0xc2, - 0x50, 0x4f, 0x2c, 0xf2, 0x42, 0x9e, 0xb4, 0xc6, 0x8e, 0x83, 0xbd, 0x01, 0x4e, 0xfc, 0xf2, 0x70, 0x10, 0x57, 0xb9, - 0xcf, 0xce, 0xbb, 0x46, 0x56, 0x0e, 0x60, 0x05, 0x51, 0x30, 0x6e, 0xcd, 0xc7, 0x36, 0x48, 0x97, 0xb8, 0x1a, 0x1f, - 0xbf, 0xa1, 0x58, 0x26, 0x9b, 0x88, 0x8b, 0x8b, 0xfc, 0xe9, 0x73, 0x20, 0x2d, 0xeb, 0xf7, 0xa3, 0xeb, 0xcb, 0xe9, - 0xf3, 0x61, 0x14, 0x80, 0x63, 0x97, 0xbd, 0xbc, 0x8c, 0xf9, 0xea, 0x92, 0x59, 0xa6, 0xb0, 0xc8, 0x37, 0x03, 0xaa, - 0x4b, 0x56, 0x4b, 0xd7, 0x2b, 0xc0, 0xd2, 0xe5, 0x37, 0x0f, 0x61, 0x6a, 0x40, 0x23, 0x6b, 0xee, 0x4e, 0x73, 0x2d, - 0x50, 0xea, 0x79, 0x3f, 0x33, 0xe4, 0xeb, 0x32, 0xe8, 0x0a, 0xd2, 0x3d, 0x8f, 0x48, 0x2f, 0xf7, 0xd2, 0xe9, 0x7e, - 0x5f, 0x0a, 0xb0, 0xd4, 0x97, 0xe2, 0x33, 0x28, 0x2c, 0x1a, 0xdf, 0x08, 0xd0, 0xd6, 0x50, 0x4d, 0x7b, 0xa5, 0xa8, - 0x7a, 0x41, 0xaf, 0x14, 0x9f, 0x7b, 0x7a, 0xa8, 0xcc, 0x97, 0xa5, 0xa3, 0xff, 0x09, 0x35, 0x17, 0x9c, 0x10, 0x33, - 0x31, 0x07, 0x50, 0x09, 0xda, 0xf8, 0x56, 0x47, 0x1b, 0x9f, 0xea, 0x55, 0xdc, 0xf4, 0x79, 0x6d, 0x2d, 0x73, 0x42, - 0xd8, 0x74, 0x2f, 0x01, 0x2a, 0xf2, 0x4a, 0x78, 0x04, 0xcb, 0x2f, 0x7f, 0xc8, 0xd3, 0x15, 0xa2, 0x75, 0xdc, 0xb3, - 0xcc, 0xa5, 0xb1, 0x7f, 0x6d, 0x30, 0x7d, 0x7d, 0xbb, 0x2d, 0xf2, 0x53, 0x13, 0x13, 0xd6, 0x63, 0x45, 0xdf, 0xbc, - 0x0b, 0x57, 0x02, 0x05, 0x0e, 0x25, 0x12, 0xdb, 0x54, 0xa1, 0x88, 0x07, 0x49, 0x9f, 0x2e, 0x5a, 0x9f, 0x06, 0x98, - 0x5a, 0xcb, 0x81, 0x39, 0x84, 0xab, 0xb8, 0xf0, 0xd1, 0xd3, 0xb7, 0x98, 0x85, 0xf3, 0x89, 0xf7, 0xd1, 0x2b, 0x46, - 0xe6, 0xe3, 0x3e, 0x2a, 0x95, 0xf4, 0xcf, 0xc3, 0x61, 0x56, 0xcd, 0x7d, 0x87, 0x3e, 0xd2, 0x43, 0x95, 0x0b, 0xca, - 0xde, 0x18, 0x93, 0x08, 0x94, 0xc6, 0x78, 0x1f, 0x07, 0xc7, 0x79, 0x9f, 0x06, 0x90, 0xda, 0x27, 0xde, 0x93, 0x92, - 0xc3, 0x73, 0x8e, 0x39, 0xa1, 0xb4, 0x22, 0xac, 0xe2, 0x8b, 0x0c, 0xe5, 0xba, 0x53, 0x0a, 0x26, 0x39, 0x24, 0x18, - 0xfe, 0xaa, 0x79, 0x13, 0x2b, 0x10, 0x76, 0xcd, 0xbc, 0x1a, 0x3d, 0xa9, 0x92, 0xb0, 0x14, 0x70, 0x54, 0x66, 0x9e, - 0x61, 0x6f, 0x78, 0x62, 0x18, 0x39, 0x58, 0xee, 0x8f, 0xea, 0x44, 0xe4, 0x1e, 0x5d, 0x60, 0x54, 0x16, 0x9e, 0x37, - 0x74, 0xa5, 0x41, 0x25, 0xd9, 0xf1, 0x57, 0x5c, 0x03, 0x6a, 0x6b, 0x8c, 0x18, 0x0a, 0x18, 0x05, 0xaf, 0xed, 0x0f, - 0x21, 0x8b, 0xb2, 0xf5, 0x1b, 0x1c, 0xf3, 0x59, 0xc9, 0x5d, 0xef, 0x70, 0x16, 0x5a, 0x42, 0x9e, 0xdc, 0x31, 0x48, - 0xd3, 0x58, 0x1a, 0x01, 0x27, 0x22, 0xd9, 0xc6, 0x52, 0x38, 0x02, 0x08, 0x08, 0x74, 0x53, 0x66, 0x18, 0xd3, 0xc1, - 0xc8, 0xf3, 0xa4, 0x67, 0xbc, 0x57, 0xe1, 0x29, 0xa4, 0xc9, 0xf6, 0xf5, 0xfc, 0xbd, 0x11, 0x64, 0xe5, 0x96, 0x73, - 0x3c, 0x2c, 0xbe, 0x71, 0xf6, 0x55, 0x4e, 0x9e, 0x62, 0x96, 0x91, 0xde, 0x29, 0xe6, 0x05, 0xfc, 0xa9, 0x2c, 0xf5, - 0x39, 0x4a, 0x6f, 0x99, 0x4f, 0x56, 0x91, 0x74, 0xe9, 0x6d, 0xfa, 0xfd, 0x78, 0xa4, 0x0e, 0x35, 0x7f, 0x1f, 0x8f, - 0xe4, 0x19, 0xb6, 0x61, 0x09, 0x0b, 0xad, 0x82, 0x31, 0x80, 0x24, 0x36, 0x22, 0x1a, 0x8c, 0xf6, 0xe6, 0x70, 0x38, - 0xdf, 0x98, 0xb3, 0x64, 0x0f, 0xae, 0xaf, 0x3c, 0x31, 0xef, 0xc0, 0x97, 0x79, 0x4c, 0x10, 0xb1, 0x99, 0xb7, 0x61, - 0x35, 0x78, 0xb0, 0x83, 0xeb, 0x23, 0xb6, 0x28, 0xd6, 0x3a, 0x96, 0xca, 0x3a, 0x38, 0xad, 0x63, 0xd3, 0x8c, 0x94, - 0x22, 0xfb, 0x1c, 0xfb, 0x7b, 0x37, 0xb8, 0xba, 0x36, 0x06, 0xb5, 0xc6, 0x1d, 0xe6, 0xce, 0xa9, 0x80, 0x7a, 0x4c, - 0x57, 0x50, 0x3d, 0xcb, 0xc9, 0x97, 0xdf, 0xda, 0x39, 0x20, 0x68, 0x04, 0x02, 0x17, 0x0d, 0xb4, 0x6a, 0x97, 0x72, - 0xde, 0x05, 0x84, 0xf8, 0x26, 0x05, 0x7d, 0x3a, 0x83, 0x4d, 0x6c, 0x3e, 0x81, 0x58, 0x34, 0xdd, 0xe7, 0x5a, 0x33, - 0x5f, 0x8c, 0x68, 0x67, 0xd6, 0xdd, 0x22, 0xb7, 0x5a, 0x88, 0x64, 0xf4, 0x6c, 0x33, 0xe1, 0xa2, 0x43, 0x39, 0x23, - 0x01, 0x13, 0xb4, 0xb6, 0x52, 0xf2, 0xb9, 0xee, 0x75, 0x82, 0xf6, 0x40, 0xd2, 0xba, 0x7f, 0xb3, 0xe8, 0x8c, 0x92, - 0x93, 0xeb, 0x4d, 0xce, 0x20, 0x05, 0x0b, 0xb6, 0x97, 0x39, 0xe1, 0x06, 0xf8, 0xc4, 0x66, 0xc9, 0x69, 0x1a, 0xe4, - 0xb1, 0x30, 0x1e, 0x79, 0x6d, 0x7e, 0x59, 0x40, 0x87, 0x92, 0x45, 0x23, 0xc4, 0x03, 0xec, 0x1c, 0x92, 0xab, 0x02, - 0x75, 0xd3, 0x40, 0x57, 0xae, 0x9c, 0x29, 0xa6, 0xc0, 0x85, 0x50, 0x10, 0xb5, 0xa3, 0x93, 0xa8, 0x9c, 0xf7, 0x49, - 0x75, 0x99, 0x4f, 0x0b, 0x69, 0x1a, 0xc8, 0xa7, 0x95, 0x63, 0x1e, 0xd8, 0xd9, 0xc6, 0x35, 0x81, 0x81, 0x4e, 0xed, - 0x6b, 0x51, 0xce, 0xb1, 0x8a, 0xe8, 0x7d, 0xfe, 0xa1, 0xb2, 0xa7, 0x0f, 0x22, 0x6c, 0x54, 0xa0, 0xb1, 0x94, 0x18, - 0x1b, 0x39, 0xfe, 0x2d, 0x51, 0x36, 0x64, 0x08, 0x08, 0x21, 0x6d, 0xe4, 0xf4, 0xc3, 0xfa, 0xf2, 0x36, 0xd3, 0xfe, - 0x9f, 0x24, 0x7e, 0x1b, 0xec, 0xe5, 0xd4, 0x9f, 0x7a, 0xc4, 0xe3, 0xb5, 0x46, 0x8f, 0x29, 0xe9, 0x36, 0xc8, 0x53, - 0xe5, 0x29, 0x48, 0x26, 0x8c, 0x25, 0x04, 0x8b, 0x72, 0xc1, 0x73, 0x5e, 0x71, 0x09, 0xf7, 0x51, 0xcb, 0x8a, 0x08, - 0x55, 0x89, 0x9c, 0x3e, 0x5f, 0x01, 0xcf, 0x04, 0x04, 0x3a, 0xc6, 0x48, 0xa3, 0x0a, 0xbe, 0x04, 0xc6, 0x3a, 0x50, - 0x76, 0x9a, 0x91, 0xe0, 0xb2, 0x7b, 0x83, 0x44, 0xa9, 0xaf, 0x48, 0x49, 0xfa, 0x56, 0xd4, 0x78, 0x25, 0x56, 0x11, - 0x09, 0x64, 0xa8, 0x21, 0x62, 0x55, 0x3d, 0x75, 0xaf, 0x8a, 0xc9, 0x60, 0x50, 0xf9, 0x72, 0x7a, 0xe2, 0x0d, 0x0d, - 0x95, 0x77, 0x5d, 0xd1, 0x4e, 0x4f, 0xb4, 0x52, 0xde, 0x42, 0x5a, 0x82, 0xa6, 0x61, 0xa4, 0x39, 0x94, 0xba, 0x92, - 0xee, 0xc6, 0x20, 0xbe, 0x64, 0xa2, 0x67, 0x3b, 0xb5, 0xa3, 0xb4, 0x25, 0xed, 0x21, 0xa4, 0xe7, 0x2e, 0xf9, 0x98, - 0x85, 0x5c, 0xdd, 0x29, 0x27, 0xe5, 0x55, 0x88, 0x4e, 0xee, 0x7b, 0x0c, 0x89, 0x40, 0x9f, 0x73, 0x0c, 0xeb, 0xa2, - 0xa1, 0xce, 0x61, 0x85, 0x98, 0x2d, 0x94, 0x30, 0x5f, 0x32, 0x9e, 0x4a, 0x06, 0x0d, 0x80, 0x0c, 0xf8, 0xec, 0x65, - 0x60, 0xf9, 0x2b, 0x88, 0x1f, 0x6d, 0x7c, 0x38, 0xfc, 0x59, 0x53, 0x88, 0xed, 0x9f, 0xb0, 0x19, 0xc2, 0xa3, 0x7a, - 0xc0, 0x33, 0xdf, 0xc4, 0x09, 0x5a, 0x01, 0x49, 0x99, 0x1d, 0x4d, 0x64, 0xaf, 0x7a, 0x08, 0xa7, 0xb2, 0x02, 0x75, - 0x94, 0x75, 0x56, 0xc2, 0x8f, 0x30, 0xd5, 0xad, 0xc4, 0x5a, 0xa0, 0xcd, 0xd5, 0x8a, 0xb5, 0x00, 0x0e, 0xfc, 0x1c, - 0x82, 0x27, 0xf2, 0x39, 0xb8, 0x18, 0x14, 0xe0, 0x73, 0x00, 0xbc, 0xc8, 0x5d, 0x78, 0x30, 0x8f, 0x2c, 0xab, 0x11, - 0x86, 0xa3, 0x8a, 0x58, 0xbf, 0x66, 0x3b, 0xf2, 0x81, 0xdb, 0x31, 0x3e, 0xd7, 0x1e, 0x4b, 0x96, 0x83, 0x51, 0xe6, - 0x5e, 0x2d, 0xd1, 0xf3, 0x26, 0x8d, 0x9b, 0xd1, 0x93, 0x7d, 0x2d, 0xff, 0x17, 0xf4, 0x32, 0xe8, 0x6f, 0xe1, 0x96, - 0xd7, 0xfc, 0x6e, 0x41, 0xa4, 0x99, 0x5e, 0x41, 0xa4, 0x8c, 0x1a, 0x91, 0x31, 0x84, 0x4d, 0xaa, 0x9b, 0xdb, 0xa4, - 0xba, 0x10, 0xf0, 0x74, 0x44, 0xaa, 0x6b, 0x21, 0x6d, 0xe4, 0xd3, 0x3a, 0x90, 0xb1, 0x48, 0xef, 0x7e, 0xf8, 0xdb, - 0x8b, 0x4f, 0x6f, 0x7e, 0xf9, 0x61, 0xf1, 0xe6, 0xdd, 0xeb, 0x37, 0xef, 0xde, 0x7c, 0xfa, 0x8d, 0x20, 0x3c, 0xa6, - 0x42, 0x65, 0xf8, 0xf0, 0xfe, 0xe6, 0x8d, 0x93, 0xc1, 0xf6, 0x66, 0xc8, 0xda, 0x37, 0x72, 0x30, 0x04, 0x22, 0x1b, - 0x84, 0x0c, 0xb2, 0x53, 0xdb, 0xfe, 0x4c, 0xcc, 0x31, 0xf6, 0x4e, 0x60, 0xb2, 0x05, 0x9c, 0x63, 0x99, 0x97, 0x8c, - 0xc8, 0x55, 0xa1, 0xf5, 0x03, 0x5a, 0xf0, 0x16, 0x5c, 0x64, 0xd2, 0xfc, 0xee, 0x17, 0x82, 0xd8, 0xa7, 0x95, 0x94, - 0xfb, 0x6a, 0x5b, 0xf3, 0x7c, 0x7b, 0xbf, 0x97, 0x70, 0xfe, 0x73, 0x69, 0x44, 0x2d, 0xc0, 0x01, 0xf8, 0x1c, 0xfe, - 0xb8, 0xd2, 0x96, 0x34, 0x99, 0x45, 0xfb, 0x19, 0x43, 0xd0, 0xa5, 0xc1, 0x07, 0xb1, 0x47, 0x5e, 0xea, 0x93, 0x85, - 0x04, 0xee, 0x88, 0xe1, 0xd3, 0x8a, 0xa0, 0x57, 0x8c, 0x28, 0x2e, 0xb9, 0x42, 0xa5, 0x94, 0xfc, 0x1b, 0x65, 0x17, - 0x15, 0x72, 0x56, 0xb0, 0x7b, 0x45, 0x8e, 0x8c, 0x1f, 0x04, 0x13, 0x5f, 0x0e, 0xee, 0xbf, 0xc4, 0x3b, 0x9c, 0x29, - 0x8e, 0xe4, 0x84, 0x3f, 0x64, 0x18, 0xd8, 0x9f, 0x83, 0xcf, 0xab, 0xc3, 0xbc, 0xbc, 0xd1, 0xa7, 0xdc, 0x92, 0x8f, - 0x27, 0xcb, 0x2b, 0x30, 0xd8, 0x2f, 0x55, 0x73, 0xd7, 0xbc, 0x9e, 0x2d, 0xe7, 0x6c, 0x3f, 0x8b, 0xe6, 0xc1, 0x1d, - 0x9b, 0x65, 0xf3, 0x60, 0xd5, 0xf0, 0x35, 0xbb, 0xe5, 0x6b, 0xab, 0x6a, 0x6b, 0xbb, 0x6a, 0x93, 0x0d, 0xbf, 0x05, - 0x09, 0xe1, 0x26, 0xf3, 0x80, 0xf7, 0xf8, 0xce, 0x67, 0x1b, 0x90, 0x68, 0x57, 0x6c, 0x03, 0x17, 0xb1, 0x35, 0xff, - 0xa1, 0xf2, 0x36, 0xac, 0x64, 0xe7, 0x63, 0x96, 0xe3, 0xfc, 0xf3, 0xe1, 0x01, 0xed, 0x85, 0xfa, 0xd9, 0xa5, 0x7a, - 0x36, 0x51, 0x76, 0xb3, 0xcd, 0x68, 0x71, 0x9f, 0x56, 0x9b, 0x30, 0x43, 0xcf, 0x72, 0xf8, 0x68, 0x2b, 0x05, 0x3f, - 0xbd, 0xc0, 0x2f, 0xd9, 0x51, 0x5b, 0x69, 0xdb, 0xae, 0x4a, 0x6c, 0x05, 0x2d, 0x8a, 0xac, 0x56, 0x78, 0x60, 0xce, - 0xaf, 0x61, 0x01, 0x63, 0xcf, 0x71, 0xce, 0x6b, 0x7f, 0x84, 0x8c, 0xf7, 0x0e, 0x00, 0x5a, 0xe6, 0x38, 0xc0, 0x23, - 0x56, 0x8c, 0xa2, 0xc1, 0x3b, 0xbf, 0x54, 0x56, 0x2b, 0xcd, 0x49, 0x68, 0x1b, 0xb1, 0x6a, 0x39, 0x52, 0x35, 0x23, - 0xd2, 0x07, 0xe9, 0x79, 0xdf, 0x23, 0xaa, 0xc1, 0x9e, 0xcc, 0xeb, 0xc0, 0x3e, 0xbd, 0x6c, 0xad, 0xea, 0xce, 0xef, - 0xa9, 0xd2, 0x25, 0x47, 0xb6, 0xfc, 0x74, 0x19, 0x3e, 0xa8, 0x3f, 0x25, 0xd7, 0x87, 0x02, 0x47, 0x78, 0xac, 0x02, - 0xce, 0xd7, 0x2b, 0xd1, 0xee, 0x44, 0xd8, 0x95, 0x4b, 0x40, 0x88, 0x2f, 0x69, 0x9a, 0xe3, 0x71, 0x44, 0x13, 0x11, - 0x36, 0x31, 0xfa, 0x0b, 0xbb, 0x0f, 0x25, 0x96, 0xf3, 0x5c, 0x83, 0x92, 0x4b, 0x06, 0xef, 0x49, 0x7b, 0x0d, 0x9a, - 0xe5, 0x55, 0xa9, 0xc9, 0x44, 0x0e, 0xca, 0x87, 0x43, 0x01, 0x7b, 0xa9, 0xf1, 0xd3, 0x84, 0x9f, 0xb0, 0xbc, 0xb5, - 0xb7, 0xa6, 0x14, 0x95, 0x34, 0x40, 0x05, 0x3e, 0x66, 0xf0, 0xbf, 0x3b, 0x43, 0x2c, 0x98, 0xa2, 0xe3, 0x87, 0x33, - 0x31, 0xb7, 0x9e, 0x5b, 0x65, 0x1d, 0x65, 0x6b, 0x94, 0x13, 0xf0, 0x6f, 0xa9, 0x8e, 0x93, 0x44, 0x38, 0xf5, 0x1e, - 0x71, 0x51, 0xf7, 0x72, 0x88, 0xba, 0x61, 0x6f, 0x2a, 0x1d, 0x6c, 0x39, 0x4d, 0x83, 0x23, 0xf1, 0x2b, 0xf5, 0xd9, - 0xfb, 0xcc, 0xe2, 0x51, 0x47, 0x36, 0xa2, 0x24, 0x8d, 0x63, 0x91, 0xc3, 0xf6, 0xbe, 0x90, 0xfb, 0x7f, 0xbf, 0x0f, - 0xe1, 0xa4, 0x55, 0x90, 0x94, 0x9e, 0x40, 0x44, 0x38, 0x3a, 0xfc, 0x88, 0xf0, 0x44, 0xaa, 0x0a, 0x9f, 0xd4, 0x27, - 0x6e, 0xcc, 0xee, 0x85, 0x39, 0xaa, 0xb7, 0x00, 0xc3, 0x58, 0x6f, 0x2d, 0x42, 0x12, 0xad, 0x34, 0xa3, 0xad, 0x07, - 0xc4, 0x88, 0xf7, 0x6b, 0x8b, 0x0c, 0xc6, 0xda, 0x92, 0x48, 0x00, 0xbf, 0x23, 0x21, 0x43, 0xdb, 0x46, 0x60, 0xc6, - 0xf0, 0x76, 0x56, 0x5c, 0xba, 0x0e, 0xdb, 0x9c, 0xc3, 0x17, 0xb2, 0xd0, 0xac, 0x23, 0x4a, 0x13, 0x84, 0xfc, 0x03, - 0x4e, 0x16, 0x0a, 0xa3, 0x79, 0x75, 0x94, 0x4e, 0x12, 0xeb, 0xfb, 0xae, 0x52, 0xc1, 0x66, 0x73, 0x83, 0xfa, 0xb2, - 0xa3, 0xe4, 0x97, 0xe0, 0xa4, 0xe3, 0x24, 0x8b, 0x1c, 0x44, 0x2d, 0x2a, 0xe7, 0x26, 0x09, 0x4b, 0xbb, 0x3a, 0xd5, - 0x66, 0xbd, 0x2e, 0xca, 0xba, 0x7a, 0x25, 0x22, 0x45, 0xef, 0xa3, 0x1e, 0x3d, 0x91, 0x90, 0x0a, 0xad, 0x4a, 0xed, - 0xf2, 0x08, 0xdc, 0x36, 0xb5, 0x62, 0x5b, 0x2e, 0x61, 0x89, 0x1a, 0xff, 0x09, 0xfa, 0x28, 0x17, 0x0f, 0x32, 0x40, - 0xa3, 0xe3, 0xa9, 0x79, 0xeb, 0x91, 0x57, 0x8e, 0xf2, 0x4b, 0xab, 0x4d, 0xfa, 0x05, 0x90, 0x19, 0xed, 0x1f, 0x2d, - 0x25, 0x90, 0x19, 0x98, 0x49, 0x4b, 0x43, 0x22, 0x47, 0x31, 0x4b, 0xf3, 0x3f, 0x70, 0xc5, 0x56, 0x88, 0x34, 0xac, - 0xe6, 0x1e, 0x7f, 0x51, 0x79, 0xb5, 0x5c, 0xcb, 0x4c, 0x73, 0xb3, 0xc4, 0xb1, 0x62, 0x71, 0x51, 0xaf, 0x2b, 0x91, - 0x05, 0x42, 0x1c, 0x61, 0x1a, 0xeb, 0xa9, 0x37, 0x4a, 0xab, 0x0f, 0x48, 0x28, 0xf3, 0x03, 0xf6, 0x76, 0xec, 0xf5, - 0x20, 0x0b, 0x71, 0x6c, 0x39, 0xd8, 0x6c, 0xbd, 0x4f, 0x65, 0x2a, 0xe2, 0xb3, 0xba, 0x38, 0xdb, 0x54, 0xe2, 0xac, - 0x4e, 0xc4, 0xd9, 0x77, 0x90, 0xf3, 0xbb, 0x33, 0x2a, 0xfa, 0xec, 0x21, 0xad, 0x93, 0x62, 0x53, 0xd3, 0x93, 0xd7, - 0x58, 0xc6, 0x77, 0x67, 0xc4, 0x55, 0x73, 0x46, 0x23, 0x19, 0x8f, 0xce, 0x3e, 0x64, 0x40, 0xf2, 0x7a, 0x96, 0xae, - 0x60, 0xf0, 0xce, 0xc2, 0x3c, 0x3e, 0x2b, 0xc5, 0x1d, 0x58, 0x9c, 0xca, 0xce, 0xf7, 0x20, 0xc3, 0x2a, 0xfc, 0x43, - 0x9c, 0x01, 0xb4, 0xeb, 0x59, 0x5a, 0x9f, 0xa5, 0xd5, 0x59, 0x5e, 0xd4, 0x67, 0x4a, 0x0a, 0x87, 0x30, 0x7e, 0x78, - 0x4f, 0x5f, 0xd9, 0xe5, 0x6d, 0x16, 0x77, 0x59, 0xe4, 0x4f, 0xd1, 0xab, 0x88, 0x98, 0x34, 0x2a, 0xe1, 0xb5, 0xfb, - 0xdb, 0xe6, 0xfe, 0xe1, 0x75, 0x63, 0xf7, 0xb3, 0x3b, 0x46, 0x74, 0x41, 0x3d, 0x5e, 0x49, 0x4a, 0x05, 0x05, 0x04, - 0x4e, 0x34, 0x6b, 0x3c, 0xb8, 0xe3, 0x80, 0x57, 0x03, 0x5b, 0xb2, 0xb5, 0xcf, 0xaf, 0x63, 0x19, 0xa6, 0xbd, 0x09, - 0xf0, 0xaf, 0xb2, 0x37, 0x5d, 0x07, 0x4b, 0xbc, 0x6f, 0x21, 0xdb, 0xd0, 0x9b, 0x57, 0xfc, 0x85, 0x97, 0xab, 0xbf, - 0xd9, 0x3f, 0x01, 0x08, 0x03, 0x62, 0x56, 0x7d, 0x34, 0x71, 0xef, 0xac, 0x2c, 0x3b, 0x27, 0xcb, 0xae, 0x87, 0x7e, - 0x4d, 0x62, 0x54, 0x5a, 0x59, 0x4a, 0x27, 0x4b, 0x09, 0x59, 0xc0, 0x27, 0x46, 0x53, 0x1b, 0x01, 0x84, 0xed, 0x28, - 0x95, 0x2f, 0x54, 0x5e, 0x44, 0xe1, 0x9c, 0xe0, 0x79, 0x22, 0x46, 0xf7, 0x56, 0x32, 0x60, 0x38, 0x84, 0x60, 0x0e, - 0xda, 0x62, 0x6f, 0xe8, 0x26, 0xe2, 0xaf, 0xd7, 0x45, 0xf9, 0x26, 0x26, 0x9f, 0x82, 0xdd, 0xc9, 0xc7, 0x25, 0x3c, - 0x2e, 0x4f, 0x3e, 0x0e, 0xd1, 0x23, 0xe1, 0xe4, 0x63, 0xf0, 0x3d, 0x92, 0xf3, 0xba, 0xeb, 0x71, 0x82, 0xdc, 0x42, - 0xba, 0xbf, 0x1d, 0x93, 0x00, 0xcd, 0x6b, 0x58, 0x8e, 0x9a, 0x8a, 0x6b, 0x66, 0xc6, 0x78, 0xde, 0xe8, 0xfd, 0xb1, - 0xe3, 0x2d, 0x53, 0x28, 0x66, 0x31, 0xaf, 0xe1, 0xf7, 0xac, 0x0a, 0xd4, 0x5d, 0x6f, 0x93, 0xdc, 0x32, 0xab, 0xe7, - 0x68, 0xf7, 0x7d, 0x5f, 0x27, 0x82, 0xda, 0xdf, 0x61, 0xcf, 0x33, 0xeb, 0x5d, 0x15, 0x03, 0x97, 0x2a, 0xd9, 0x21, - 0x53, 0xd5, 0xf4, 0x40, 0xa5, 0x34, 0x78, 0x7a, 0x69, 0x5d, 0xbe, 0x54, 0xda, 0xc8, 0x33, 0xcd, 0x6f, 0x00, 0x2f, - 0xa6, 0x2e, 0x8b, 0xdd, 0x57, 0xf7, 0x15, 0xdc, 0xc6, 0xfb, 0xfd, 0x65, 0xe5, 0x99, 0x9f, 0xb8, 0x00, 0xec, 0x4d, - 0x85, 0xd6, 0x09, 0x94, 0x1a, 0xd6, 0xe1, 0xcb, 0x44, 0x44, 0x7f, 0xb4, 0xcb, 0x75, 0xe6, 0x3a, 0x60, 0x44, 0x11, - 0xbf, 0x8d, 0x47, 0x7f, 0x80, 0xe2, 0xda, 0xd8, 0x03, 0xc2, 0x3a, 0x24, 0xf4, 0x19, 0x01, 0x48, 0x3d, 0xe6, 0x28, - 0x01, 0xcd, 0x8a, 0xe6, 0x8e, 0xc9, 0xcf, 0xf5, 0x95, 0xd2, 0xdf, 0x2f, 0x2b, 0x8f, 0xcc, 0x29, 0x6d, 0x33, 0x8d, - 0xd5, 0x9a, 0x4a, 0x20, 0xbc, 0xa2, 0x92, 0x55, 0xf8, 0x6c, 0xde, 0x88, 0x7e, 0x5f, 0x1e, 0xe1, 0x69, 0xf5, 0xc3, - 0x16, 0xe3, 0x5b, 0x01, 0xd1, 0x48, 0x00, 0xf4, 0x13, 0xc0, 0xbc, 0xc8, 0x66, 0x76, 0x1f, 0x07, 0x54, 0x29, 0xd1, - 0x34, 0xce, 0xe6, 0xf9, 0x2d, 0xbd, 0x29, 0x3b, 0xe8, 0xd4, 0xa9, 0x02, 0x17, 0x5c, 0x95, 0x8c, 0x57, 0xd6, 0x13, - 0xf9, 0xfc, 0xe6, 0x76, 0x93, 0x66, 0xf1, 0xfb, 0xf2, 0x9f, 0x38, 0xb6, 0xba, 0x0e, 0x8f, 0x4c, 0x9d, 0xae, 0x9d, - 0x47, 0x5a, 0x7b, 0x21, 0x20, 0xa2, 0x5d, 0x43, 0xad, 0x17, 0x16, 0x7a, 0xa4, 0x27, 0xc2, 0x39, 0x49, 0xd4, 0xb4, - 0x03, 0x2d, 0x8d, 0xd0, 0xd7, 0xd7, 0x9c, 0xfe, 0xc2, 0x60, 0xed, 0xf3, 0x31, 0x03, 0xb2, 0x12, 0xfd, 0x58, 0x3d, - 0x34, 0x36, 0x73, 0xe8, 0x59, 0xab, 0xf2, 0xcc, 0xab, 0x0e, 0x07, 0xc4, 0x87, 0xd1, 0x5f, 0xf2, 0xfb, 0xfd, 0x57, - 0x34, 0xff, 0x98, 0x50, 0xe3, 0x67, 0x9b, 0x01, 0xba, 0xf6, 0x5d, 0x79, 0x20, 0xea, 0xb9, 0x56, 0x09, 0x42, 0xbc, - 0x41, 0x4c, 0x34, 0x23, 0xe6, 0xe0, 0xb4, 0x43, 0xcd, 0x3f, 0x49, 0x0d, 0x08, 0x51, 0xe2, 0x75, 0x4c, 0x59, 0x90, - 0xd3, 0x26, 0x8e, 0xf4, 0xa3, 0x70, 0x22, 0x3f, 0x8a, 0xaa, 0xc8, 0xee, 0xe1, 0x82, 0xc1, 0xd4, 0x7b, 0xda, 0x2f, - 0xd1, 0x6f, 0x09, 0x47, 0xce, 0xd1, 0xaa, 0x10, 0x44, 0x4e, 0x08, 0x6b, 0x0d, 0x61, 0x82, 0xd8, 0x20, 0x5e, 0xf6, - 0x5d, 0x92, 0xe1, 0x48, 0xc1, 0x65, 0x1d, 0x3b, 0xc6, 0x5c, 0x1d, 0x55, 0xaf, 0x01, 0x8c, 0x57, 0x8e, 0xa0, 0xd9, - 0x28, 0xb2, 0x4b, 0x88, 0x2a, 0x72, 0x3c, 0x01, 0xb5, 0x83, 0xd2, 0xd8, 0x4c, 0xcf, 0xc7, 0x41, 0x3e, 0x5a, 0x54, - 0xa8, 0x73, 0x62, 0x19, 0xaf, 0x01, 0x58, 0x3b, 0x57, 0xfd, 0x3c, 0xab, 0xc1, 0x93, 0x86, 0xf8, 0x7c, 0x8c, 0xb6, - 0x57, 0x36, 0x07, 0xd5, 0x76, 0x3a, 0x2b, 0xaf, 0x98, 0x2e, 0x07, 0xc6, 0x7d, 0xc3, 0x2b, 0x8a, 0x33, 0xfc, 0xe8, - 0xc1, 0x16, 0xe7, 0x4f, 0x37, 0xd4, 0x7e, 0xcc, 0x8d, 0x7a, 0x18, 0x68, 0x2d, 0x78, 0x53, 0x10, 0xeb, 0xef, 0x87, - 0x8e, 0x6c, 0xef, 0xb5, 0xc8, 0x68, 0xf2, 0xd9, 0xcf, 0x3f, 0x94, 0xe9, 0x2a, 0x85, 0xfb, 0x92, 0x93, 0x45, 0x33, - 0x0f, 0x81, 0xbd, 0x21, 0x86, 0xeb, 0xa3, 0xc2, 0x23, 0xca, 0xfa, 0x7d, 0xf8, 0x7d, 0x95, 0x81, 0x29, 0x06, 0xae, - 0x2b, 0x04, 0xe3, 0x21, 0x10, 0xc4, 0xc3, 0x34, 0x3a, 0x19, 0xd4, 0xa0, 0x0d, 0xdf, 0x00, 0x64, 0x06, 0x78, 0x64, - 0x2e, 0x3d, 0x02, 0xee, 0x02, 0xd7, 0x9e, 0x8c, 0xc7, 0xfe, 0xc4, 0x34, 0x34, 0x6a, 0x4a, 0x33, 0x3d, 0x37, 0x7e, - 0xd3, 0x51, 0x2d, 0xd7, 0xce, 0x7f, 0x7c, 0xc9, 0x6f, 0xd0, 0x0b, 0x5a, 0x5e, 0xee, 0x23, 0x75, 0xb9, 0xcf, 0x28, - 0x2e, 0x13, 0xc9, 0x61, 0x41, 0x2c, 0x4b, 0x38, 0xf0, 0x18, 0x95, 0x2c, 0xb6, 0xf4, 0x58, 0x15, 0x2d, 0x5f, 0x94, - 0x1b, 0xa4, 0x43, 0x27, 0x04, 0x4b, 0x54, 0x10, 0x2c, 0x81, 0x71, 0x11, 0x6b, 0xbe, 0x19, 0xe4, 0x2c, 0x9e, 0x6d, - 0xe6, 0x1c, 0x09, 0xeb, 0x92, 0xc3, 0xa1, 0x90, 0x60, 0x33, 0xd9, 0x6c, 0x3d, 0x67, 0x6b, 0x9f, 0x81, 0x12, 0xa0, - 0x94, 0x69, 0x82, 0xd2, 0xb4, 0x62, 0x2b, 0x6e, 0x5a, 0x83, 0xd5, 0x6a, 0xca, 0x56, 0x35, 0x65, 0xe7, 0x34, 0xe5, - 0xa8, 0x82, 0x92, 0x13, 0x4a, 0x51, 0x86, 0x01, 0x8c, 0xd8, 0x24, 0xba, 0xca, 0xd0, 0xc7, 0x3b, 0xe1, 0x11, 0x54, - 0x11, 0x91, 0x4f, 0x18, 0x42, 0x60, 0x22, 0x8a, 0x0b, 0x55, 0x28, 0x06, 0xc8, 0x88, 0x04, 0x82, 0x89, 0x4a, 0x9d, - 0x02, 0xf3, 0xd1, 0x54, 0x31, 0x6c, 0xda, 0x13, 0xe5, 0x5b, 0xea, 0xb8, 0x47, 0xd9, 0xe6, 0xef, 0x62, 0x17, 0x84, - 0xc8, 0xdd, 0xb8, 0x53, 0x3f, 0x23, 0xde, 0xdb, 0x1d, 0x61, 0xfc, 0x64, 0xc7, 0x2d, 0xc2, 0x15, 0xc1, 0x96, 0x6a, - 0x0e, 0xb1, 0x98, 0x57, 0x93, 0x04, 0xb5, 0x2c, 0x89, 0xbf, 0xe1, 0xc9, 0x20, 0x67, 0x4b, 0xf0, 0xa0, 0x9d, 0xb3, - 0x0c, 0xf0, 0x57, 0xac, 0x16, 0xfd, 0x56, 0x7b, 0x4b, 0x90, 0x9f, 0x36, 0x76, 0xa3, 0x30, 0x31, 0x82, 0x44, 0xdd, - 0xae, 0x0c, 0xe4, 0x87, 0x0f, 0x38, 0x1d, 0x8f, 0x3d, 0x65, 0xcc, 0xad, 0x4c, 0x2f, 0xd3, 0xb9, 0x92, 0x6f, 0xe4, - 0x5e, 0xfa, 0xd8, 0x4b, 0xb0, 0x73, 0xc0, 0x1b, 0x48, 0x1b, 0x78, 0x03, 0xdb, 0x85, 0xd7, 0x06, 0x09, 0x33, 0x02, - 0x6c, 0x71, 0x7c, 0x8c, 0x94, 0xc0, 0x10, 0x8e, 0xb3, 0x14, 0x80, 0x69, 0xf4, 0x65, 0xb6, 0xb2, 0x2f, 0xb3, 0x5a, - 0xb3, 0xa5, 0x72, 0xba, 0x77, 0x6e, 0xdd, 0xce, 0x27, 0x12, 0x00, 0x4c, 0xea, 0x1c, 0x88, 0x33, 0x13, 0xec, 0xd2, - 0x24, 0xb2, 0x7c, 0x0c, 0xf3, 0x3b, 0xf1, 0xba, 0x2c, 0x56, 0xaa, 0x2b, 0xda, 0x3e, 0x33, 0xf9, 0x8c, 0x74, 0x12, - 0x2a, 0xa0, 0xa0, 0x90, 0x6b, 0x7d, 0xfa, 0x2e, 0x7c, 0x17, 0x14, 0x1a, 0x98, 0xad, 0xc2, 0x3d, 0x4d, 0xd6, 0x48, - 0xbd, 0x51, 0xf5, 0xfb, 0xe4, 0x1a, 0x48, 0x75, 0xe6, 0xd0, 0xb2, 0x27, 0x15, 0x06, 0x88, 0x1d, 0xf5, 0x19, 0x09, - 0x75, 0x20, 0xf5, 0x80, 0x21, 0x44, 0xdb, 0xf4, 0xf1, 0x27, 0x43, 0xa2, 0x0b, 0xb0, 0x85, 0x68, 0x03, 0x3f, 0xfe, - 0x04, 0xfb, 0x2c, 0x08, 0x8f, 0x69, 0xfe, 0x16, 0x92, 0x8e, 0x0d, 0x9c, 0x56, 0x9f, 0x82, 0x0f, 0x92, 0x1c, 0x4c, - 0xd4, 0xc1, 0xcb, 0xfd, 0xa5, 0xdf, 0x87, 0x2d, 0x3b, 0x97, 0x52, 0x1d, 0x2b, 0xf5, 0xb6, 0xad, 0xfd, 0x20, 0xda, - 0x82, 0x23, 0x8b, 0xf8, 0xfb, 0x0c, 0x11, 0xc1, 0xcc, 0x20, 0xc2, 0xae, 0x85, 0xba, 0xdb, 0x53, 0x6a, 0x59, 0xd4, - 0xdb, 0x9e, 0x52, 0xea, 0x36, 0x0c, 0xdf, 0x4d, 0x30, 0x53, 0xdc, 0xf0, 0x3f, 0x32, 0x2f, 0xd4, 0x1b, 0x8f, 0x45, - 0x81, 0xee, 0xf9, 0xfb, 0x25, 0xaf, 0x66, 0x1b, 0x65, 0xc2, 0xbc, 0xe3, 0xcb, 0x59, 0x28, 0xbb, 0x5a, 0x1a, 0x77, - 0x3e, 0x7b, 0x4b, 0x35, 0x1f, 0xfc, 0xc3, 0x21, 0x81, 0x78, 0xa3, 0xf8, 0xea, 0xae, 0x91, 0x5b, 0xd7, 0x64, 0x73, - 0x55, 0x02, 0xea, 0xf7, 0xf9, 0x1a, 0xf7, 0x5b, 0xac, 0x7f, 0xf7, 0x34, 0xc8, 0x58, 0xcd, 0x70, 0xc5, 0x14, 0x3e, - 0x05, 0x80, 0xc1, 0xe1, 0x54, 0x90, 0x16, 0x78, 0xc3, 0xcb, 0xe1, 0xe5, 0x64, 0x43, 0x26, 0xdd, 0x8d, 0x8f, 0xdc, - 0x59, 0xa0, 0xea, 0xfd, 0x86, 0xe2, 0xa4, 0x41, 0xa2, 0xb1, 0xd7, 0xe0, 0x8b, 0x2c, 0xa3, 0x5c, 0x34, 0x71, 0x1f, - 0x93, 0xaf, 0xf4, 0x00, 0xe6, 0x2a, 0x94, 0x00, 0xd1, 0x6f, 0x2c, 0x8b, 0x8d, 0x68, 0x5b, 0x6c, 0x60, 0x29, 0x55, - 0x73, 0xbd, 0x9a, 0x3e, 0x7b, 0x25, 0x9a, 0xf7, 0xd1, 0x8c, 0x53, 0x1a, 0x0d, 0x38, 0x4e, 0xa3, 0x70, 0xfb, 0xfe, - 0x5e, 0x94, 0xcb, 0x0c, 0x2c, 0xd9, 0x2a, 0x9c, 0xe2, 0xb2, 0x51, 0x67, 0xc4, 0x8b, 0x3c, 0x56, 0x00, 0x1d, 0x8f, - 0x09, 0x80, 0xea, 0x82, 0x80, 0x8a, 0x68, 0x29, 0xbd, 0x15, 0x5a, 0x2c, 0xd4, 0x1b, 0x8e, 0x52, 0xf8, 0x23, 0xfd, - 0x79, 0x90, 0x4f, 0x01, 0x88, 0x5d, 0x1f, 0x47, 0xaf, 0x8b, 0x92, 0x3e, 0x55, 0xcc, 0x72, 0x39, 0x98, 0xc0, 0xae, - 0x4e, 0x64, 0xa8, 0x15, 0xe4, 0xad, 0xba, 0xf2, 0x56, 0x26, 0x6f, 0x63, 0x9c, 0x92, 0x1f, 0xb9, 0xe9, 0x58, 0x23, - 0x06, 0x5e, 0x79, 0x5a, 0xa7, 0x09, 0xd2, 0xe4, 0x02, 0x18, 0x86, 0xf8, 0x36, 0xf3, 0x5e, 0x78, 0x8e, 0x54, 0x05, - 0xc9, 0x6c, 0x97, 0x79, 0xea, 0x22, 0xaa, 0xaf, 0x9c, 0x5a, 0x3a, 0x73, 0xfa, 0x11, 0xc0, 0x7b, 0x4c, 0x4d, 0x1a, - 0xf2, 0x11, 0x6e, 0x4b, 0xf1, 0xf5, 0x56, 0x5d, 0xe3, 0xa5, 0xd1, 0xb9, 0x7b, 0xf9, 0xd2, 0x9d, 0x06, 0xfd, 0x14, - 0x04, 0xe5, 0x7c, 0x51, 0x0a, 0xd8, 0x53, 0x66, 0x73, 0xbd, 0x5a, 0xb5, 0x42, 0xeb, 0x70, 0x18, 0x6b, 0x47, 0x21, - 0xad, 0xce, 0x02, 0xb6, 0x1a, 0xe9, 0x94, 0x00, 0x21, 0x38, 0x4e, 0xc3, 0x4e, 0x30, 0xee, 0xd2, 0x69, 0x44, 0xd6, - 0x2b, 0x25, 0xe9, 0xc2, 0x0c, 0x92, 0x7f, 0x92, 0xd7, 0x33, 0xa0, 0x25, 0x80, 0x43, 0x11, 0x4b, 0x78, 0x38, 0x49, - 0xae, 0x00, 0x3a, 0x1d, 0x0e, 0x2a, 0x0d, 0xcd, 0x59, 0xcd, 0x92, 0xf9, 0x24, 0x96, 0xaa, 0xca, 0xc3, 0xc1, 0x53, - 0x6e, 0x06, 0xfd, 0x7e, 0x36, 0x2d, 0x95, 0x0b, 0x40, 0x10, 0xeb, 0xc2, 0x00, 0xf1, 0x48, 0x0b, 0x4f, 0x16, 0x7d, - 0x4a, 0xe2, 0x97, 0xb3, 0x64, 0x6e, 0xb2, 0xe1, 0x1d, 0x18, 0xc1, 0x66, 0x5c, 0x97, 0x94, 0x69, 0x8f, 0xca, 0xef, - 0x19, 0x3d, 0xb5, 0x7d, 0xad, 0xd5, 0x16, 0xb1, 0xae, 0x83, 0xab, 0x12, 0xf5, 0x14, 0x1f, 0x94, 0x24, 0x78, 0xbf, - 0x72, 0x6e, 0x46, 0xca, 0xd7, 0x22, 0xf7, 0x83, 0x76, 0xa6, 0x56, 0x0e, 0x1c, 0x81, 0x1c, 0xab, 0xa8, 0xe4, 0xf5, - 0xae, 0x43, 0xf0, 0xe8, 0xae, 0x54, 0xa0, 0x1c, 0x7c, 0x0d, 0x62, 0x74, 0x7d, 0xd5, 0x59, 0x43, 0xcd, 0x34, 0xaa, - 0x3c, 0x82, 0x4e, 0x1d, 0xc0, 0x93, 0x82, 0x97, 0x5a, 0xfd, 0x78, 0x38, 0x78, 0xe6, 0x07, 0x7f, 0x99, 0xe9, 0x5b, - 0x88, 0x89, 0x72, 0xaa, 0x11, 0x12, 0x57, 0x4a, 0x12, 0xf1, 0xf1, 0xa2, 0x65, 0xc5, 0xa8, 0x0c, 0x1f, 0x78, 0xa5, - 0xca, 0x57, 0xa7, 0x2a, 0x2f, 0x46, 0xda, 0x96, 0xc0, 0x6b, 0xf2, 0x0f, 0x91, 0x6b, 0xde, 0xfa, 0xba, 0xab, 0x0c, - 0x7d, 0x2b, 0x2b, 0xd0, 0x11, 0x6c, 0x65, 0x29, 0x39, 0xe0, 0x93, 0xea, 0xae, 0x5a, 0xb5, 0x3e, 0xa7, 0x6c, 0x23, - 0xdc, 0xe4, 0xd7, 0xb1, 0x83, 0x23, 0xe5, 0x37, 0x78, 0x2e, 0x80, 0xbd, 0x06, 0xec, 0xcd, 0x39, 0x2b, 0x9a, 0x47, - 0x87, 0xb4, 0x2d, 0xd0, 0xc8, 0xcc, 0xed, 0x5c, 0xdd, 0xb7, 0xe5, 0x51, 0x1a, 0x43, 0x64, 0xda, 0x23, 0xd3, 0xc1, - 0x66, 0x94, 0xff, 0x96, 0xf2, 0x5b, 0x85, 0x63, 0xe0, 0xdb, 0xa9, 0x77, 0x00, 0x55, 0x4f, 0x1b, 0x64, 0xac, 0x19, - 0x86, 0x56, 0x76, 0xb9, 0x14, 0x5a, 0x82, 0x96, 0xba, 0x09, 0x82, 0xf3, 0x23, 0xa2, 0x1c, 0x01, 0xe8, 0x22, 0x05, - 0x4c, 0xf0, 0x53, 0xda, 0xee, 0x7e, 0x7f, 0x9d, 0x7a, 0xe4, 0xde, 0x15, 0x6a, 0x94, 0x50, 0x82, 0xb1, 0x9f, 0x68, - 0xcc, 0xa0, 0xa3, 0x2b, 0x72, 0xc2, 0xb3, 0x56, 0x87, 0x75, 0xdd, 0x94, 0x41, 0x59, 0x1c, 0xf3, 0x6a, 0x3a, 0xfb, - 0xfd, 0xc9, 0xbe, 0x6e, 0x90, 0x85, 0xfc, 0x77, 0xd6, 0x43, 0x32, 0xe8, 0x1e, 0x84, 0x42, 0xf4, 0xe6, 0xc1, 0x0c, - 0xff, 0x63, 0x1b, 0x9e, 0x7d, 0xc3, 0x8d, 0x3a, 0x01, 0xcc, 0x11, 0xd7, 0x4b, 0x4f, 0xd1, 0xd6, 0xc3, 0x2d, 0x90, - 0xad, 0xf1, 0xf2, 0xd6, 0x5e, 0x03, 0x39, 0xc5, 0xf1, 0xdf, 0xf1, 0x4c, 0xad, 0x6c, 0xf0, 0xd3, 0x53, 0xb6, 0x03, - 0x0f, 0x2f, 0x42, 0x40, 0x31, 0x2c, 0x1b, 0x7f, 0x67, 0x39, 0xce, 0xe8, 0xbf, 0x79, 0xc4, 0x30, 0x58, 0x44, 0x7e, - 0x7c, 0x59, 0x0a, 0xf1, 0x45, 0x78, 0x6f, 0x2a, 0xef, 0x8e, 0x9c, 0x32, 0xef, 0xf4, 0x30, 0xba, 0x2e, 0x49, 0xdf, - 0x24, 0x1f, 0x5b, 0xc3, 0xf6, 0xbb, 0x76, 0xbf, 0x19, 0x22, 0x08, 0xa1, 0x1c, 0x3f, 0x67, 0x74, 0x42, 0xe3, 0xc3, - 0x6a, 0x76, 0x7a, 0xfd, 0xde, 0x39, 0x5e, 0xb0, 0x35, 0x1a, 0xe0, 0xf1, 0xd0, 0xc5, 0x3c, 0x51, 0x43, 0xa7, 0xeb, - 0xda, 0x39, 0x78, 0x60, 0x90, 0xe5, 0xc9, 0x37, 0x0c, 0x4b, 0xec, 0x4f, 0x22, 0x9e, 0xb4, 0x55, 0x1b, 0x9b, 0x23, - 0xd5, 0x46, 0xcd, 0xc0, 0x0f, 0x5e, 0x41, 0x81, 0xd1, 0x05, 0x69, 0x05, 0xc6, 0xe1, 0x08, 0x40, 0x56, 0x8c, 0xe3, - 0x91, 0xc1, 0x04, 0x86, 0x74, 0x43, 0x51, 0x00, 0x1e, 0x1e, 0xc7, 0x83, 0x90, 0x01, 0xa4, 0x0b, 0x1e, 0x1a, 0xb6, - 0x49, 0x48, 0xf9, 0x79, 0x9e, 0xd7, 0x6a, 0x08, 0x7d, 0x67, 0xa1, 0x3a, 0xf6, 0x23, 0xed, 0x15, 0xeb, 0x5a, 0x95, - 0x8e, 0x6c, 0x75, 0x80, 0xbe, 0x21, 0x03, 0xdf, 0x3a, 0xb6, 0x00, 0x88, 0x96, 0xf8, 0x2d, 0xf5, 0x6a, 0x5f, 0xc6, - 0xac, 0x50, 0xaf, 0x2f, 0x4c, 0xbb, 0x5e, 0x49, 0x8b, 0x02, 0x2a, 0x6e, 0x5b, 0xb5, 0x3d, 0x92, 0xf3, 0x1f, 0xdf, - 0x75, 0xb4, 0xe3, 0xb3, 0x53, 0x63, 0x4b, 0x28, 0x73, 0x8b, 0x27, 0xb2, 0x3a, 0xda, 0x52, 0x9d, 0xea, 0x03, 0x2e, - 0x35, 0xa9, 0xce, 0x0c, 0x0c, 0xaf, 0x11, 0xa0, 0xdc, 0x42, 0x24, 0x8d, 0xc3, 0xde, 0xf9, 0x64, 0x50, 0x30, 0xb7, - 0x48, 0x40, 0x02, 0xdb, 0xd8, 0xda, 0x45, 0x73, 0xfd, 0xfa, 0x2d, 0xf5, 0xaa, 0x36, 0x55, 0x3d, 0x78, 0xe3, 0x05, - 0xce, 0xde, 0x69, 0x2d, 0x20, 0x80, 0xc2, 0xd6, 0xb2, 0x1c, 0x9c, 0xbb, 0x5d, 0xd5, 0x52, 0x51, 0x46, 0xfd, 0xfe, - 0xf9, 0x6f, 0x29, 0x2a, 0x62, 0x4f, 0x15, 0xa7, 0xac, 0xdf, 0x6e, 0x99, 0x8b, 0xca, 0x92, 0x37, 0xa8, 0xa2, 0xb5, - 0x3a, 0x6a, 0x2a, 0xd7, 0xcd, 0x55, 0x4b, 0x26, 0x88, 0xd1, 0x7d, 0xba, 0xd6, 0xb9, 0x53, 0xef, 0xbd, 0x8a, 0x23, - 0x06, 0x82, 0x9b, 0xee, 0xf1, 0xc1, 0x41, 0x68, 0x54, 0x94, 0x0b, 0x6e, 0x94, 0x56, 0x95, 0x94, 0x42, 0xde, 0xaa, - 0x68, 0xce, 0xf4, 0x11, 0x00, 0x11, 0x60, 0x95, 0xa8, 0xff, 0xc3, 0x97, 0xc6, 0x78, 0xf0, 0xc0, 0xd7, 0xe4, 0x3a, - 0xb6, 0xde, 0x3f, 0xad, 0x91, 0x56, 0x1b, 0xc7, 0xa4, 0x56, 0xbd, 0x6c, 0x15, 0x2f, 0xbb, 0xd7, 0xa9, 0x18, 0x3c, - 0xff, 0x9f, 0xfb, 0x00, 0x35, 0xa2, 0xa5, 0x0c, 0x6e, 0x5d, 0x0d, 0xd0, 0xf8, 0x70, 0x2c, 0x7c, 0xe3, 0x87, 0x8c, - 0xf3, 0xc1, 0x0c, 0x1d, 0xd5, 0xe6, 0xe0, 0x80, 0xe0, 0xa8, 0xee, 0xd1, 0x98, 0x30, 0x0b, 0xe7, 0x1e, 0x04, 0xaa, - 0x4f, 0xdc, 0x67, 0x5c, 0x7b, 0x41, 0x9b, 0xc0, 0x27, 0xeb, 0xba, 0xa6, 0x08, 0x70, 0x11, 0x1b, 0x13, 0x31, 0xc4, - 0x65, 0x93, 0x48, 0x7d, 0x33, 0x06, 0x05, 0x40, 0x71, 0x5d, 0x91, 0x5c, 0xba, 0x48, 0xf3, 0x4a, 0x94, 0xb5, 0x6e, - 0x46, 0xc5, 0x8a, 0x21, 0x00, 0x3c, 0x04, 0xc5, 0x55, 0x65, 0x26, 0x34, 0x62, 0x03, 0xa9, 0x2c, 0x05, 0xab, 0x86, - 0x85, 0xdf, 0xb4, 0xdf, 0x24, 0x27, 0xbd, 0xf3, 0x71, 0xeb, 0xdc, 0xb1, 0xef, 0x1d, 0x85, 0x94, 0xf6, 0x50, 0x4c, - 0x10, 0x04, 0x3f, 0xad, 0xc3, 0xf9, 0x33, 0x7e, 0x4d, 0x60, 0x2a, 0xb2, 0x19, 0x03, 0x0e, 0x42, 0x44, 0x66, 0xfc, - 0x9e, 0xc3, 0x6b, 0x5e, 0x4e, 0xc2, 0xe1, 0xd0, 0x07, 0x7d, 0x28, 0xcf, 0x66, 0xe1, 0x50, 0xcc, 0xa5, 0xf7, 0x3a, - 0x58, 0xeb, 0x42, 0x5e, 0x4f, 0x42, 0x44, 0x0b, 0x0d, 0x7d, 0x70, 0x5e, 0x77, 0xcd, 0x11, 0x96, 0x00, 0x34, 0x71, - 0xf4, 0x65, 0xfd, 0x7e, 0xe4, 0x69, 0x43, 0x8b, 0x14, 0x17, 0x8d, 0x32, 0x9b, 0xe5, 0xb2, 0x13, 0x36, 0xae, 0xdd, - 0x02, 0xa1, 0x78, 0x98, 0xb6, 0x50, 0xb5, 0x9e, 0xea, 0xf5, 0xdc, 0xb4, 0xfb, 0xee, 0x51, 0xb5, 0xca, 0x91, 0xce, - 0xda, 0x74, 0xa5, 0x56, 0xb7, 0x8c, 0xaa, 0x75, 0x96, 0x46, 0x54, 0xb9, 0x49, 0xee, 0x1a, 0xb5, 0xe0, 0x93, 0x0d, - 0x5d, 0xa6, 0xec, 0x6c, 0x0d, 0x4e, 0x1c, 0x79, 0x2e, 0xb9, 0xe5, 0xbb, 0xf3, 0x8a, 0xee, 0x4e, 0xb5, 0x6f, 0x01, - 0xee, 0xcd, 0xb0, 0x21, 0x73, 0x5e, 0x63, 0xa7, 0x41, 0x98, 0x04, 0x7e, 0xc4, 0x3e, 0x66, 0xc8, 0x06, 0x03, 0x3a, - 0x0a, 0xe9, 0x7f, 0x6d, 0x99, 0x23, 0x01, 0x93, 0xbf, 0x9e, 0xfb, 0xcd, 0xa2, 0xc8, 0x61, 0x31, 0x7e, 0xd8, 0x60, - 0xa4, 0xb1, 0x5a, 0x83, 0x61, 0x79, 0x87, 0xc8, 0x9f, 0xda, 0x1d, 0xd3, 0x54, 0xc7, 0x9b, 0xf5, 0x5a, 0xf3, 0xab, - 0xa7, 0x4f, 0x75, 0x7d, 0xfe, 0xdb, 0xf7, 0x97, 0x61, 0xcd, 0xec, 0x0f, 0x41, 0x28, 0xed, 0xde, 0x2d, 0xce, 0x1d, - 0x89, 0xde, 0xb1, 0xd2, 0xcc, 0x2e, 0xed, 0x92, 0x5d, 0x9a, 0xd2, 0x6e, 0xc8, 0xf5, 0xea, 0x2b, 0xe5, 0x8d, 0x9d, - 0x57, 0x4c, 0xf7, 0xef, 0x85, 0xde, 0x51, 0x4e, 0xd5, 0x04, 0x22, 0x9a, 0xb4, 0x23, 0x71, 0xbb, 0x57, 0x86, 0xcf, - 0x27, 0x79, 0xbb, 0x84, 0xa3, 0xae, 0x61, 0xb9, 0xf9, 0xf6, 0x3f, 0xf3, 0xaa, 0xb3, 0xc2, 0xed, 0x97, 0xc6, 0xac, - 0xfd, 0x29, 0x88, 0xab, 0xfa, 0xc3, 0x7b, 0x52, 0x33, 0x25, 0xff, 0x57, 0x3d, 0x06, 0xae, 0x7e, 0x32, 0xed, 0xe8, - 0x9e, 0x42, 0xd8, 0x60, 0xf6, 0xf3, 0xe3, 0x87, 0x16, 0xac, 0xaa, 0x0b, 0x14, 0xc9, 0x01, 0x74, 0xee, 0x92, 0x11, - 0xde, 0xef, 0x18, 0xe7, 0xfe, 0xd5, 0xf7, 0x6a, 0x72, 0x84, 0x88, 0x76, 0x11, 0x0e, 0x00, 0xe2, 0x4e, 0x53, 0x59, - 0x87, 0x1a, 0xa0, 0x0f, 0x08, 0xac, 0x43, 0xdf, 0x66, 0x00, 0x07, 0x7d, 0xb4, 0x79, 0x16, 0x81, 0xbc, 0xee, 0xdd, - 0xb3, 0xb7, 0x6c, 0xe7, 0xf3, 0xeb, 0x55, 0xea, 0xdd, 0xa3, 0x43, 0xf0, 0xf9, 0xd8, 0x9f, 0x5e, 0x06, 0x06, 0x17, - 0x9a, 0xbd, 0x7d, 0x26, 0xd8, 0x8e, 0xed, 0x9e, 0x21, 0x52, 0x51, 0x77, 0xfe, 0xe1, 0xa5, 0x89, 0x9e, 0x77, 0x5e, - 0xb8, 0xe3, 0x4b, 0x00, 0x0f, 0x64, 0x31, 0xa0, 0xf8, 0x2c, 0xbd, 0x7f, 0xb1, 0x04, 0xd4, 0xe4, 0xb7, 0x7c, 0xed, - 0x7d, 0xa1, 0xd4, 0x05, 0xfc, 0x39, 0xa0, 0xf4, 0x49, 0xce, 0xbd, 0xbb, 0xe1, 0xad, 0x7f, 0xf1, 0x1c, 0x9c, 0x27, - 0x56, 0xc3, 0x05, 0xfc, 0x55, 0xf0, 0xa1, 0x77, 0x37, 0xc0, 0xc4, 0x92, 0x0f, 0xbd, 0xd5, 0x00, 0x52, 0x15, 0x2e, - 0x24, 0xc6, 0x3e, 0xfc, 0x1a, 0xe4, 0x0c, 0xff, 0xf8, 0x4d, 0x63, 0xb0, 0xfe, 0x1a, 0x14, 0x1a, 0x8d, 0xb5, 0x54, - 0x21, 0x4b, 0xb1, 0x38, 0x13, 0x60, 0x13, 0x8e, 0xbb, 0x7d, 0xb1, 0xaa, 0xcd, 0x5a, 0xd0, 0x9f, 0x8f, 0xf8, 0x1e, - 0x8d, 0xd5, 0x55, 0x39, 0x17, 0xe5, 0x47, 0xa4, 0x4f, 0x75, 0x7c, 0x8c, 0x8a, 0x4d, 0xdd, 0x9d, 0x4e, 0xb5, 0xea, - 0x48, 0xfb, 0x4d, 0xb9, 0x06, 0x3b, 0x5e, 0x27, 0x47, 0x96, 0xc2, 0xb3, 0x0e, 0x3b, 0x2f, 0x9d, 0x12, 0x1d, 0x86, - 0xf1, 0x6e, 0xab, 0x9e, 0x31, 0x94, 0xe7, 0x06, 0x63, 0xba, 0xe0, 0x11, 0xbf, 0x1e, 0xe4, 0x32, 0x34, 0xe6, 0x03, - 0xb2, 0x61, 0x28, 0x1f, 0x5a, 0x64, 0x48, 0x88, 0x78, 0x0f, 0x95, 0x80, 0x6d, 0x0b, 0xca, 0xa4, 0x80, 0xb3, 0x68, - 0xf0, 0x5b, 0xed, 0xe5, 0xc0, 0x7b, 0x10, 0xf9, 0x8d, 0x74, 0x29, 0x97, 0xd8, 0xe8, 0xc4, 0xb1, 0x2c, 0xb4, 0xf3, - 0xb8, 0xfe, 0x3a, 0x06, 0xf5, 0x7b, 0xa5, 0xdf, 0xa0, 0x9c, 0xfd, 0x51, 0xb2, 0x4e, 0x1b, 0x4f, 0x8c, 0x7f, 0xb8, - 0xca, 0x3f, 0x45, 0x4b, 0x3d, 0xfc, 0x7f, 0xc6, 0x14, 0x4a, 0xff, 0x32, 0x2d, 0xa3, 0xcd, 0x6a, 0x29, 0x4a, 0x91, - 0x47, 0xe2, 0xe4, 0x6b, 0x91, 0x9d, 0xcb, 0x77, 0x3e, 0x85, 0x7e, 0x01, 0x68, 0xd9, 0x27, 0xc8, 0xe8, 0xef, 0x99, - 0xe0, 0xc3, 0xef, 0xb5, 0x73, 0x6d, 0xce, 0xc7, 0x93, 0xfc, 0xca, 0xda, 0xbb, 0x1d, 0x2f, 0x12, 0xa3, 0x18, 0xcb, - 0x7d, 0xd5, 0xcd, 0xca, 0x89, 0x4a, 0x0e, 0x8c, 0x74, 0x4d, 0xf6, 0x72, 0x25, 0xeb, 0x76, 0xba, 0x95, 0x40, 0x44, - 0x15, 0x78, 0x8f, 0x71, 0x15, 0xfb, 0x08, 0xa6, 0xeb, 0x8e, 0xcb, 0x68, 0xc7, 0x7b, 0xc6, 0xab, 0x13, 0x65, 0x05, - 0xb7, 0x1b, 0xd1, 0x9e, 0xd0, 0xd1, 0x4f, 0x93, 0xda, 0xb2, 0x70, 0x00, 0x72, 0x97, 0x30, 0x96, 0x0d, 0xc1, 0x8a, - 0x41, 0xe9, 0xeb, 0x35, 0x25, 0xcb, 0x02, 0x2c, 0x3a, 0xbb, 0x8c, 0x40, 0x0c, 0xeb, 0xa6, 0x39, 0xa1, 0xe3, 0xa5, - 0x8b, 0xf3, 0x5e, 0xab, 0x48, 0xc1, 0x33, 0x5a, 0x74, 0xcc, 0x4d, 0x47, 0xba, 0x31, 0xda, 0xdb, 0xef, 0x0d, 0x42, - 0x8a, 0xe7, 0x0f, 0x6c, 0xb5, 0x2e, 0x2e, 0x12, 0xaf, 0x90, 0x89, 0x16, 0xc4, 0x52, 0x04, 0x66, 0xbc, 0xd0, 0x34, - 0xc2, 0x04, 0x65, 0x4a, 0xb0, 0x68, 0x8d, 0x0e, 0xed, 0x0f, 0x4b, 0xd8, 0x3d, 0xc6, 0x08, 0x10, 0xa8, 0x32, 0x7d, - 0x0e, 0x5b, 0x13, 0x66, 0x53, 0x17, 0x1b, 0xa0, 0xad, 0x62, 0x68, 0x10, 0xd6, 0x86, 0x98, 0x8f, 0x69, 0x7e, 0xf7, - 0x2f, 0x2c, 0xc6, 0xf6, 0x04, 0x62, 0x7b, 0xb7, 0x6b, 0x12, 0xa6, 0x7b, 0x2d, 0x6e, 0xac, 0x97, 0xdb, 0x53, 0x8e, - 0xa9, 0x1d, 0x6b, 0xa3, 0x76, 0xac, 0xa5, 0xde, 0xb1, 0xd6, 0x7a, 0xc7, 0xba, 0x6b, 0xf8, 0x87, 0xcc, 0x8b, 0x59, - 0x02, 0xfa, 0xdd, 0x15, 0x57, 0x0d, 0x82, 0x66, 0x6c, 0xd8, 0x2d, 0xfc, 0x96, 0x58, 0xbb, 0xa5, 0x7f, 0xb1, 0x64, - 0x0b, 0xd3, 0x07, 0xba, 0x75, 0x80, 0x65, 0x44, 0x4d, 0xbe, 0x47, 0xde, 0x4d, 0x67, 0x45, 0xe1, 0xf6, 0xc4, 0x16, - 0x3e, 0x7b, 0x6b, 0xde, 0xbc, 0x7f, 0x16, 0x41, 0xee, 0x1d, 0xf7, 0xee, 0x87, 0x6f, 0xfd, 0x0b, 0xdd, 0x02, 0x39, - 0x99, 0xe5, 0x0c, 0xa4, 0x8e, 0xf8, 0x04, 0xd1, 0xca, 0x9e, 0xf2, 0x9d, 0x90, 0x3b, 0xdb, 0xfa, 0xd9, 0xbd, 0xbb, - 0xad, 0xdd, 0x3d, 0xbb, 0x67, 0xd5, 0x88, 0x62, 0xc5, 0x69, 0x8a, 0x84, 0x59, 0xb4, 0x01, 0x9e, 0x7a, 0xf9, 0x7e, - 0xc7, 0x8e, 0x39, 0xdc, 0x3d, 0xeb, 0xe8, 0x78, 0x39, 0x07, 0xec, 0xee, 0x3f, 0xda, 0x84, 0x8d, 0x95, 0xae, 0x55, - 0xe8, 0x70, 0xf7, 0x2c, 0xd3, 0x78, 0x0e, 0x47, 0xf2, 0xe9, 0x58, 0x63, 0x83, 0xa0, 0xae, 0xcf, 0x19, 0xd4, 0x8e, - 0xdd, 0xd7, 0x84, 0x5d, 0x76, 0xcc, 0x6b, 0x5d, 0xf3, 0xf6, 0xca, 0x53, 0xb1, 0x21, 0xa0, 0xc3, 0xd7, 0xea, 0x06, - 0xf9, 0x97, 0xc0, 0x29, 0x02, 0x40, 0x0e, 0xc7, 0x4b, 0x1e, 0xfb, 0x3e, 0xcd, 0xd2, 0x7a, 0x87, 0x5a, 0x8b, 0xca, - 0xb2, 0x0c, 0x6b, 0xef, 0x07, 0xad, 0x18, 0x96, 0x9a, 0xfe, 0xe9, 0x38, 0x70, 0x3b, 0xdb, 0xad, 0x8c, 0x5d, 0xc6, - 0xb3, 0xe2, 0xe2, 0xfb, 0xd3, 0x42, 0xb9, 0x76, 0xf3, 0x36, 0x7e, 0xd3, 0x6a, 0xc9, 0xd2, 0x5a, 0x0f, 0x79, 0x69, - 0x59, 0x44, 0x20, 0x80, 0xe1, 0x48, 0xd9, 0xc5, 0x12, 0xee, 0x11, 0x56, 0xf7, 0x20, 0x94, 0xcc, 0x0b, 0x17, 0xcf, - 0x59, 0x0c, 0x89, 0x00, 0xdb, 0x1d, 0x2a, 0xb6, 0x85, 0x8b, 0xe7, 0x6c, 0xc3, 0x8b, 0x7e, 0x3f, 0x53, 0x9d, 0x42, - 0xd6, 0x9d, 0x25, 0xdf, 0xa8, 0xe6, 0x58, 0x43, 0xcd, 0xd6, 0x26, 0xd9, 0x1a, 0xe7, 0xb6, 0xe2, 0xe3, 0xae, 0xad, - 0xf8, 0x58, 0x59, 0xeb, 0xd2, 0xbd, 0xde, 0xa3, 0xba, 0x00, 0xb6, 0xfe, 0xdb, 0xe3, 0x95, 0xeb, 0xf9, 0x8c, 0x00, - 0xbe, 0x16, 0x7c, 0x3c, 0x59, 0xa0, 0x57, 0xc9, 0xc2, 0xbf, 0x1d, 0xa8, 0xf1, 0x77, 0x3a, 0x77, 0x01, 0xd0, 0x95, - 0x94, 0x57, 0x40, 0xde, 0x41, 0x8e, 0xb9, 0x65, 0x57, 0xde, 0x9f, 0x7c, 0x87, 0xbd, 0xe5, 0xf5, 0x6c, 0x31, 0x67, - 0x3b, 0x70, 0x2a, 0x48, 0x06, 0xf6, 0xb2, 0x62, 0xbb, 0x20, 0xb6, 0x13, 0x7e, 0x23, 0x60, 0xca, 0x17, 0x10, 0xc4, - 0x15, 0xdc, 0x42, 0x1c, 0x9e, 0xfc, 0x73, 0x70, 0xdf, 0xda, 0xac, 0xef, 0x99, 0xd5, 0x39, 0xc1, 0x9a, 0x59, 0x3d, - 0x18, 0x2c, 0x9b, 0xc9, 0xaa, 0xdf, 0xf7, 0x76, 0xda, 0xf1, 0xe9, 0x4e, 0xea, 0xc4, 0x4e, 0x6b, 0xb5, 0x16, 0xec, - 0xad, 0xd4, 0xba, 0x18, 0x43, 0x0f, 0x10, 0x3f, 0xdd, 0x0e, 0xf8, 0x7d, 0xc7, 0xda, 0xf2, 0xde, 0xb2, 0x05, 0xdb, - 0xc1, 0x25, 0xa8, 0x69, 0x2f, 0xfb, 0x93, 0xca, 0x05, 0xed, 0xd8, 0x25, 0xf1, 0x70, 0xc6, 0xac, 0x52, 0x66, 0xd6, - 0x49, 0x75, 0x25, 0x3a, 0x63, 0x3a, 0x6b, 0x3d, 0x9f, 0xab, 0xf9, 0xa4, 0xd0, 0xa0, 0x7e, 0xe7, 0xc4, 0x47, 0x54, - 0x74, 0x9e, 0xc0, 0xd6, 0xb2, 0x82, 0x58, 0xed, 0x73, 0xb0, 0xd6, 0x6a, 0x97, 0x7e, 0x2f, 0x1f, 0x70, 0x9b, 0x72, - 0x58, 0x07, 0x06, 0x35, 0x27, 0x56, 0xd4, 0x63, 0xb6, 0x63, 0xdc, 0xfc, 0xf4, 0xf2, 0x07, 0x27, 0x2c, 0x59, 0xb1, - 0xda, 0x9f, 0x7e, 0xff, 0xcc, 0xd3, 0xdf, 0xa9, 0xfd, 0x0b, 0xe1, 0x07, 0xe3, 0xff, 0xd4, 0xee, 0x6b, 0x2d, 0x46, - 0x65, 0xab, 0x1c, 0xa1, 0x71, 0xb7, 0x92, 0x26, 0xcb, 0x4f, 0xc2, 0x13, 0xd6, 0x82, 0x67, 0xb9, 0x5e, 0xa2, 0x59, - 0x01, 0x2b, 0xac, 0x65, 0x12, 0xae, 0x30, 0x56, 0x4b, 0x5b, 0x7d, 0x8b, 0xa6, 0x39, 0x3e, 0x9c, 0x6b, 0x83, 0x32, - 0xe5, 0xec, 0x8c, 0x58, 0x0d, 0x97, 0x61, 0x69, 0x42, 0x11, 0xb2, 0x7b, 0x3b, 0xb8, 0xb1, 0x53, 0x96, 0x52, 0x86, - 0x73, 0x0c, 0x26, 0x3c, 0x12, 0xa3, 0x2a, 0xdf, 0xdf, 0x97, 0x14, 0x39, 0x6d, 0xcb, 0x41, 0x15, 0xc2, 0x3e, 0x92, - 0x28, 0x81, 0x5b, 0x91, 0x16, 0x8a, 0x94, 0xc5, 0xdf, 0x0e, 0xd0, 0x05, 0x5e, 0x40, 0x5d, 0x8d, 0xba, 0xfd, 0xe1, - 0x88, 0x87, 0x8f, 0x4c, 0x7d, 0x60, 0xc4, 0x92, 0x40, 0x6d, 0x2f, 0xb2, 0xf4, 0x0e, 0x54, 0xf8, 0x3d, 0x5c, 0x4d, - 0xc4, 0x7e, 0x6e, 0x49, 0x51, 0x91, 0x8d, 0xf4, 0x86, 0xd6, 0xe0, 0x11, 0x5a, 0x53, 0xbe, 0x77, 0x52, 0x6d, 0xd2, - 0x79, 0x47, 0xc8, 0xb1, 0xfa, 0xd6, 0x12, 0x46, 0xbb, 0xa2, 0x17, 0xf7, 0x8e, 0xde, 0xf3, 0x74, 0xd5, 0x73, 0x7f, - 0xe2, 0x8a, 0x79, 0x72, 0x1b, 0x81, 0xba, 0x15, 0x54, 0xb7, 0xf7, 0x2a, 0xc1, 0x82, 0x25, 0xed, 0x3e, 0x7e, 0x3b, - 0x6b, 0x07, 0xa2, 0x32, 0x56, 0xe9, 0x6b, 0x92, 0xb0, 0x27, 0x06, 0x9d, 0x42, 0x55, 0x6e, 0x77, 0x47, 0x5b, 0xe0, - 0x3a, 0x66, 0x29, 0x7a, 0x61, 0x8b, 0xdc, 0x2d, 0xff, 0xee, 0xb9, 0x22, 0x67, 0xbf, 0x04, 0x04, 0xa7, 0xe6, 0x2b, - 0xe2, 0xcb, 0x11, 0x1e, 0x55, 0xb7, 0xc0, 0x71, 0xfa, 0x0e, 0xe0, 0x1f, 0x0e, 0x97, 0xa0, 0x09, 0x88, 0x05, 0xeb, - 0xa5, 0x71, 0x8f, 0xf5, 0xe2, 0x62, 0x73, 0x97, 0xe4, 0x1b, 0x70, 0x66, 0xa0, 0x54, 0x4b, 0x3f, 0x70, 0xac, 0x16, - 0x50, 0xe1, 0x60, 0x76, 0x52, 0x2f, 0x2c, 0xa3, 0x1e, 0xd3, 0xe7, 0x67, 0xb0, 0x77, 0x84, 0x04, 0xc0, 0xfd, 0xb2, - 0x0f, 0x48, 0xc0, 0x43, 0x67, 0x76, 0x40, 0x38, 0x61, 0x16, 0x55, 0x81, 0x44, 0x72, 0xa4, 0x9f, 0x3d, 0x66, 0x22, - 0xf9, 0x83, 0x59, 0xcf, 0x39, 0x25, 0x7a, 0xac, 0xa7, 0x8e, 0x90, 0x1e, 0xeb, 0x59, 0x47, 0x44, 0x8f, 0xf5, 0xac, - 0xe3, 0xa3, 0xc7, 0x7a, 0xe6, 0xd8, 0xe9, 0x41, 0x60, 0x02, 0x44, 0x1e, 0xb0, 0x1e, 0x4d, 0xa6, 0x9e, 0xe2, 0x1e, - 0x20, 0x1a, 0x04, 0xd6, 0x93, 0xc2, 0x79, 0x0f, 0x90, 0xc7, 0x48, 0xac, 0x0e, 0x7a, 0x7f, 0x19, 0x3f, 0xed, 0x19, - 0x19, 0x79, 0xdc, 0x3a, 0xac, 0xfe, 0xd7, 0x5f, 0x21, 0x00, 0x0e, 0xcf, 0xa6, 0xde, 0xe5, 0x18, 0xb2, 0xca, 0x32, - 0x02, 0xc9, 0x4f, 0x0c, 0xbe, 0x7c, 0x01, 0x50, 0xf5, 0x99, 0xae, 0xd5, 0xe4, 0xa8, 0x3d, 0xe6, 0xd0, 0x15, 0x03, - 0xc0, 0x36, 0x2c, 0x51, 0x55, 0x0b, 0x9b, 0xb0, 0xb8, 0xfd, 0x0c, 0xa3, 0xb9, 0x6c, 0x7a, 0x41, 0x03, 0xf5, 0x08, - 0xc1, 0x2f, 0xad, 0x87, 0xd6, 0x5a, 0xa6, 0x1c, 0xba, 0x36, 0x8a, 0x2a, 0x1b, 0xea, 0x12, 0x56, 0x6b, 0x11, 0xd5, - 0x44, 0x91, 0x72, 0xc9, 0x28, 0x8a, 0xa5, 0x0a, 0xf6, 0x99, 0xb8, 0x83, 0xa8, 0x79, 0xda, 0x6a, 0xab, 0x60, 0x7f, - 0x07, 0x08, 0x6b, 0x61, 0x2d, 0xa4, 0x33, 0xa8, 0xbd, 0xd3, 0x8f, 0x94, 0xbf, 0xbc, 0x90, 0xdb, 0xb9, 0x85, 0x22, - 0xdc, 0x9e, 0x83, 0xf2, 0xa6, 0xae, 0x4a, 0x45, 0x34, 0x5a, 0x02, 0xa5, 0xcc, 0x09, 0x22, 0x0b, 0x10, 0xc0, 0x71, - 0x03, 0x81, 0xcf, 0x6b, 0x7c, 0x02, 0x8d, 0x42, 0x20, 0x3f, 0xb0, 0x0a, 0xd7, 0x1e, 0xd2, 0x52, 0x6b, 0x44, 0x94, - 0x88, 0x1f, 0x5d, 0x3d, 0xc7, 0xf6, 0xd5, 0xd3, 0x58, 0x5b, 0x4a, 0x13, 0xc4, 0x4f, 0x2c, 0xb6, 0x10, 0x13, 0x44, - 0x75, 0x88, 0x8e, 0x60, 0x39, 0x21, 0x44, 0xe1, 0x0f, 0xa1, 0x9f, 0x1a, 0xf8, 0x4b, 0xb6, 0x2c, 0xf2, 0x9a, 0x60, - 0x31, 0x2b, 0x06, 0x68, 0x55, 0x04, 0x9e, 0xe9, 0x6c, 0xa9, 0xcc, 0x69, 0x1e, 0x1d, 0xd9, 0xc1, 0x79, 0xd7, 0xc1, - 0x5e, 0xfa, 0x32, 0x76, 0xb2, 0x6c, 0x1a, 0xb5, 0xb1, 0x21, 0x12, 0x5e, 0x91, 0xbf, 0xcc, 0x52, 0xe3, 0x1c, 0x99, - 0xcb, 0xf5, 0x5d, 0x17, 0x77, 0x77, 0xb4, 0x4d, 0x58, 0x85, 0x08, 0x75, 0xdb, 0x50, 0xb9, 0x14, 0x66, 0x63, 0xd3, - 0x34, 0xc0, 0x17, 0x8a, 0x4a, 0xa5, 0x2a, 0xb5, 0x95, 0x4a, 0x4e, 0x78, 0xd7, 0x57, 0xb5, 0x48, 0x5d, 0x11, 0x6c, - 0x63, 0x86, 0x7a, 0x28, 0x37, 0x6a, 0xec, 0xeb, 0x8e, 0x55, 0x7a, 0x87, 0x09, 0x72, 0x46, 0x5e, 0xe4, 0xe0, 0xa2, - 0xa4, 0x20, 0x73, 0x35, 0x84, 0xf9, 0xa3, 0x86, 0x4f, 0x0b, 0xcb, 0x3d, 0x94, 0x80, 0xd9, 0x51, 0xc3, 0xcb, 0x08, - 0x81, 0x88, 0x4b, 0x65, 0x5f, 0x31, 0xf1, 0x7b, 0x0a, 0x66, 0xc9, 0x84, 0xee, 0x45, 0x2c, 0x8c, 0xd0, 0xc6, 0x27, - 0x49, 0x32, 0xf5, 0x34, 0x05, 0x37, 0x72, 0x19, 0xe6, 0x68, 0x84, 0x96, 0x7c, 0xe4, 0x40, 0xfa, 0x5a, 0x4e, 0x25, - 0xf8, 0x88, 0x3a, 0x05, 0x1c, 0xcf, 0xcf, 0x0b, 0xeb, 0x27, 0xcb, 0x25, 0xe6, 0xb2, 0x36, 0xff, 0x65, 0x47, 0xc7, - 0x60, 0x97, 0xa7, 0x89, 0xe3, 0xea, 0x3f, 0xaa, 0x92, 0xe2, 0xe1, 0xe7, 0x34, 0x07, 0x14, 0xc1, 0xcc, 0x9e, 0x62, - 0x7c, 0xec, 0xb3, 0x4c, 0x01, 0x7f, 0xbb, 0xde, 0x5a, 0x32, 0xb1, 0x4b, 0xda, 0xcd, 0x95, 0xf1, 0x4b, 0x6d, 0xd8, - 0x71, 0x70, 0x6e, 0x00, 0x8a, 0xb3, 0x46, 0x87, 0xe5, 0xb5, 0x6e, 0x5b, 0x15, 0x2a, 0x50, 0xeb, 0xff, 0xec, 0x16, - 0xa6, 0xbc, 0xcd, 0x4b, 0xe5, 0x6d, 0x1e, 0x9a, 0x00, 0x81, 0xc8, 0x0c, 0x79, 0xd6, 0x74, 0x4c, 0x12, 0xf7, 0x8e, - 0x94, 0xb4, 0xef, 0x48, 0xf1, 0xa3, 0x77, 0x24, 0xe4, 0x5b, 0x42, 0x47, 0xf6, 0x25, 0x27, 0x27, 0x50, 0x66, 0xb0, - 0x97, 0xd7, 0x4c, 0xf6, 0x0f, 0x68, 0x2f, 0x9c, 0xcb, 0xf2, 0x8a, 0xbf, 0x15, 0xde, 0xda, 0x9f, 0xae, 0x4f, 0xbb, - 0xaa, 0xde, 0x7e, 0x65, 0x66, 0x1e, 0x0e, 0xc5, 0xe1, 0x50, 0x99, 0xa0, 0xdd, 0x05, 0x17, 0x83, 0x9c, 0xdd, 0xbb, - 0xf1, 0xf1, 0x6f, 0x39, 0x8a, 0xd8, 0x4a, 0x79, 0x24, 0x5d, 0xa8, 0xc4, 0xf0, 0xd2, 0xc0, 0xc3, 0xec, 0xf8, 0x78, - 0xb2, 0xbb, 0xba, 0x9f, 0x0c, 0x06, 0x3b, 0xd5, 0xb7, 0x5b, 0x5e, 0xcf, 0x76, 0x73, 0xf6, 0xc0, 0x6f, 0xa7, 0xdb, - 0x60, 0xdf, 0xc0, 0xb6, 0xbb, 0xbb, 0x12, 0x87, 0xc3, 0xee, 0x9a, 0x2f, 0xfc, 0xfd, 0x03, 0x02, 0x3a, 0xf3, 0xf3, - 0x71, 0x1b, 0xe3, 0xe7, 0xa6, 0xed, 0xaa, 0xb5, 0x03, 0x78, 0xfa, 0x1f, 0xbc, 0x9b, 0xd9, 0x72, 0xee, 0xb3, 0x27, - 0xfc, 0x01, 0xfc, 0xf3, 0x71, 0x93, 0x44, 0xea, 0x13, 0xed, 0x32, 0x79, 0x03, 0x0e, 0xe4, 0x3b, 0x9f, 0xbd, 0xe1, - 0x0f, 0xb3, 0xe5, 0x9c, 0x17, 0x87, 0xc3, 0xfb, 0x69, 0x88, 0x64, 0x4d, 0x61, 0x45, 0x2c, 0x29, 0x9e, 0x1f, 0x84, - 0xc7, 0xef, 0x45, 0x64, 0x88, 0xb4, 0xdc, 0xbb, 0x43, 0x76, 0xc3, 0x22, 0x3f, 0x80, 0x0f, 0xb2, 0x9d, 0x3f, 0x91, - 0x35, 0xa5, 0xfb, 0xc5, 0x13, 0xff, 0x70, 0xa0, 0xbf, 0xde, 0xf8, 0x87, 0xc3, 0x7b, 0xf6, 0x80, 0xe0, 0xe8, 0x7c, - 0x07, 0xfd, 0xa3, 0x6f, 0x1d, 0x50, 0x95, 0xe1, 0xdb, 0xd9, 0x66, 0xee, 0x5f, 0xaf, 0xd8, 0x1d, 0x70, 0xa1, 0x28, - 0x2f, 0xb4, 0x1b, 0xf6, 0x80, 0x5e, 0x67, 0xe4, 0x44, 0x34, 0xdb, 0xcd, 0x7d, 0x16, 0xe3, 0x73, 0x75, 0x5f, 0x4c, - 0xbe, 0x7a, 0x5f, 0xdc, 0xb1, 0x6d, 0xf7, 0x7d, 0x51, 0xbe, 0xe9, 0xae, 0x9f, 0x2d, 0xdb, 0xb1, 0x07, 0x98, 0x61, - 0x6f, 0xf9, 0x4d, 0x73, 0xec, 0x18, 0xfb, 0xd5, 0x1b, 0x23, 0x80, 0x32, 0x5b, 0xb0, 0x58, 0x70, 0x50, 0xaa, 0x55, - 0xdb, 0x92, 0xc8, 0x2b, 0x1d, 0xa8, 0x36, 0x23, 0xb8, 0xaf, 0x16, 0x72, 0xe6, 0x99, 0x81, 0xbe, 0xad, 0x10, 0x2d, - 0x1c, 0x36, 0xe0, 0xaf, 0xb4, 0x75, 0x8c, 0x61, 0x9a, 0xd5, 0x4c, 0xdb, 0xa2, 0x2e, 0xbf, 0xed, 0x3d, 0x93, 0xdf, - 0xc8, 0xc0, 0x16, 0x22, 0x29, 0x1c, 0xc7, 0x17, 0xcf, 0x4f, 0xf8, 0xaf, 0x5a, 0x1e, 0xb5, 0xda, 0x2f, 0x94, 0xfa, - 0xf4, 0x25, 0x1d, 0xd1, 0xc4, 0xbd, 0x68, 0xcb, 0xb0, 0x46, 0x59, 0x53, 0x4b, 0x87, 0x61, 0x5c, 0xc3, 0xbe, 0x3c, - 0x70, 0xe8, 0x3b, 0x20, 0xd0, 0x56, 0xa9, 0x14, 0x68, 0xe1, 0x18, 0x46, 0x61, 0x16, 0x52, 0x1e, 0x17, 0x66, 0x29, - 0xef, 0xb1, 0x40, 0x8b, 0x5b, 0x75, 0x8f, 0xa9, 0xed, 0x16, 0x44, 0x58, 0xbd, 0x65, 0x9c, 0x5f, 0x36, 0xaa, 0x70, - 0x5b, 0x80, 0xa2, 0x08, 0xca, 0x60, 0x4f, 0x72, 0xdb, 0x42, 0x49, 0xb3, 0x51, 0x58, 0x8b, 0xbb, 0xa2, 0xdc, 0xf5, - 0x1a, 0xb6, 0xc0, 0x0b, 0xaa, 0x7e, 0x42, 0xd8, 0x96, 0x3d, 0xeb, 0x50, 0x2e, 0xd2, 0xff, 0xc8, 0xd2, 0xf3, 0xed, - 0xd6, 0x9c, 0xff, 0xe9, 0x2b, 0xfa, 0xa8, 0xfc, 0xcf, 0x2f, 0xe9, 0x27, 0x83, 0x65, 0xe4, 0x94, 0xfa, 0x3e, 0x1a, - 0xdd, 0xa6, 0x39, 0x61, 0x6c, 0xf9, 0xfa, 0xe9, 0x37, 0xc8, 0x14, 0x24, 0x87, 0x52, 0xaa, 0x72, 0xb2, 0x87, 0xbe, - 0xf0, 0xba, 0x0f, 0x33, 0xc1, 0x00, 0x84, 0xd7, 0x68, 0x53, 0x4d, 0x98, 0xc4, 0xa3, 0x2b, 0xf8, 0xbf, 0x11, 0xc4, - 0xa0, 0x7d, 0xa2, 0xa8, 0x63, 0xdb, 0x48, 0xd7, 0x6d, 0xe7, 0x20, 0xb9, 0x53, 0x57, 0xfe, 0xa8, 0x9c, 0xfc, 0x27, - 0x1a, 0x22, 0xaf, 0xb8, 0x42, 0xac, 0x2c, 0xb8, 0xc4, 0x62, 0xa8, 0x48, 0x01, 0xae, 0x21, 0x88, 0x94, 0x45, 0x49, - 0xe1, 0x96, 0x83, 0xaa, 0x08, 0xc0, 0xb8, 0x5a, 0x1d, 0x75, 0x22, 0x7c, 0xdc, 0x5a, 0x8b, 0x10, 0xac, 0x68, 0xd4, - 0xca, 0x5a, 0x81, 0x2f, 0x48, 0x5f, 0x3a, 0x14, 0xc4, 0xf4, 0x28, 0xa4, 0xaa, 0x74, 0x28, 0x90, 0xe6, 0x50, 0xf1, - 0x8d, 0xc1, 0x46, 0x51, 0x91, 0x9e, 0xbf, 0x34, 0x29, 0xb9, 0x34, 0x66, 0x7c, 0x10, 0x65, 0x24, 0xf2, 0x3a, 0xbc, - 0x13, 0xd3, 0x02, 0xf9, 0x46, 0x8f, 0x1f, 0x04, 0x97, 0xf0, 0x6e, 0xc8, 0xbd, 0x02, 0x6c, 0x09, 0xd8, 0x01, 0xee, - 0x95, 0x19, 0xe5, 0x3a, 0xad, 0xeb, 0xb7, 0xd6, 0x43, 0x31, 0x0c, 0x9f, 0x59, 0x02, 0xdb, 0xd1, 0x3a, 0x3a, 0xd2, - 0xc3, 0x87, 0xff, 0x75, 0x55, 0x73, 0xd4, 0xa9, 0x5c, 0xce, 0x8e, 0x27, 0x2c, 0x45, 0xcc, 0xa0, 0xfb, 0xeb, 0xf6, - 0xa5, 0x00, 0xba, 0x5d, 0x16, 0xf3, 0x6c, 0xb4, 0x93, 0x7f, 0x4b, 0x37, 0x56, 0x94, 0x36, 0xf1, 0x2e, 0xeb, 0x8d, - 0xfd, 0xe1, 0xe8, 0x2f, 0xcf, 0xbe, 0x4c, 0x08, 0x55, 0x67, 0xc3, 0xd6, 0x3a, 0xce, 0xe5, 0x7f, 0xfd, 0x75, 0x4c, - 0x56, 0x10, 0x14, 0x84, 0x65, 0xa7, 0x98, 0xa8, 0x60, 0x14, 0x29, 0xd6, 0x7c, 0x3c, 0x59, 0xa3, 0x4e, 0x78, 0xed, - 0x2f, 0xb5, 0x4e, 0x98, 0x18, 0x59, 0xa9, 0xfc, 0x35, 0xab, 0xd8, 0x9d, 0xca, 0x2c, 0x20, 0xf3, 0x20, 0x9f, 0xac, - 0x8d, 0x06, 0x73, 0xc5, 0xeb, 0xd9, 0x7a, 0x2e, 0x95, 0xcf, 0x60, 0xca, 0x59, 0x0e, 0x4e, 0x96, 0xc2, 0xee, 0x49, - 0xa0, 0x68, 0xcd, 0xd0, 0xb5, 0x3f, 0xc5, 0x56, 0xbd, 0x4a, 0xab, 0x1a, 0xe0, 0x01, 0x21, 0x06, 0x86, 0xda, 0xab, - 0x85, 0x87, 0xd6, 0x02, 0x58, 0xfb, 0xa3, 0xd2, 0x0f, 0xc6, 0x93, 0x25, 0x5f, 0x20, 0xff, 0x72, 0xe4, 0xa8, 0xdd, - 0xfb, 0x7d, 0xef, 0x1e, 0xa4, 0xe0, 0xc8, 0xb5, 0x50, 0x20, 0x11, 0xd0, 0x82, 0x6f, 0x7c, 0xe5, 0x83, 0xf1, 0x16, - 0xb5, 0xd5, 0xa0, 0xa0, 0x76, 0x74, 0xcb, 0x63, 0x47, 0xef, 0x7c, 0x7f, 0x42, 0x5f, 0xbd, 0xd0, 0xc2, 0xf1, 0x57, - 0xce, 0xc8, 0x35, 0x5b, 0x75, 0xc8, 0x11, 0xcd, 0xa4, 0x43, 0x88, 0x58, 0xb1, 0x35, 0x7b, 0x4b, 0x2a, 0xe7, 0xce, - 0x21, 0x3b, 0x7d, 0x84, 0x2a, 0xbd, 0xd6, 0xe3, 0xdb, 0x89, 0xd2, 0xdd, 0x1e, 0xef, 0x26, 0xdf, 0xb2, 0x89, 0x88, - 0xc1, 0x80, 0x36, 0x08, 0x67, 0x64, 0x1d, 0x22, 0x95, 0x0e, 0x10, 0x02, 0xc7, 0x04, 0x34, 0xfd, 0xc7, 0xd7, 0x24, - 0x0a, 0x38, 0xd2, 0x46, 0xc8, 0x5a, 0x76, 0x38, 0xe4, 0xa0, 0x51, 0x6e, 0xfe, 0xf0, 0x0a, 0x75, 0x9a, 0x03, 0xf3, - 0x74, 0x09, 0x7b, 0x0e, 0x1e, 0xe9, 0xc5, 0xf1, 0x91, 0xfe, 0xdf, 0xd1, 0x44, 0x8d, 0xff, 0x73, 0x4d, 0x94, 0xd2, - 0x22, 0x39, 0xaa, 0xa5, 0x6f, 0x52, 0x47, 0xc1, 0x45, 0xde, 0x51, 0x0b, 0xd9, 0xb3, 0x6c, 0xdc, 0xa8, 0xe6, 0xfd, - 0xff, 0x5a, 0x99, 0xff, 0xaf, 0x69, 0x65, 0x98, 0x92, 0x1d, 0x4b, 0x35, 0xf3, 0x40, 0xab, 0x18, 0x66, 0x3f, 0x93, - 0x84, 0xc8, 0x70, 0x69, 0xc0, 0x8f, 0x2a, 0xd8, 0xc7, 0x69, 0xb5, 0xce, 0xc2, 0x1d, 0x2a, 0x51, 0x6f, 0xc5, 0x5d, - 0x9a, 0xbf, 0xa8, 0xff, 0x2d, 0xca, 0x02, 0xa6, 0xf6, 0x5d, 0x99, 0xc6, 0x01, 0x59, 0xf8, 0xb3, 0xb0, 0xc4, 0xc9, - 0x8d, 0x6d, 0xfc, 0x59, 0x8e, 0xa7, 0xfd, 0xaa, 0x33, 0xf3, 0x40, 0x02, 0x35, 0x10, 0x7f, 0xe4, 0x5c, 0x56, 0x16, - 0x0f, 0x08, 0xdd, 0xfc, 0x43, 0x59, 0x16, 0xa5, 0xd7, 0xfb, 0x94, 0xa4, 0xd5, 0xd9, 0x4a, 0xd4, 0x49, 0x11, 0x2b, - 0x28, 0x9b, 0x14, 0x60, 0xf4, 0x61, 0xe5, 0x89, 0x38, 0x38, 0x43, 0xa0, 0x86, 0xb3, 0x3a, 0x09, 0x01, 0x68, 0x58, - 0x21, 0xec, 0x9f, 0x41, 0x0b, 0xcf, 0xc2, 0x38, 0x5c, 0x03, 0x4c, 0x4e, 0x5a, 0x9d, 0xad, 0xcb, 0xe2, 0x3e, 0x8d, - 0x45, 0x3c, 0xea, 0x29, 0x4a, 0x96, 0xd7, 0xb9, 0x2b, 0xe7, 0xfa, 0xfb, 0x3f, 0x28, 0x80, 0xdd, 0x80, 0xd9, 0xb6, - 0xc0, 0x0e, 0x00, 0x12, 0x14, 0xc8, 0x16, 0xea, 0x34, 0x3a, 0x53, 0x4b, 0x05, 0xde, 0x73, 0x3d, 0xc0, 0x5f, 0xe7, - 0x80, 0x65, 0x5c, 0x17, 0x32, 0x60, 0x04, 0x01, 0x8c, 0xc0, 0x41, 0x09, 0x18, 0x3a, 0x43, 0xdc, 0x56, 0xe5, 0xac, - 0x85, 0xe6, 0x4a, 0xb7, 0x25, 0x37, 0x8d, 0x72, 0xb6, 0x12, 0x01, 0xf4, 0xd5, 0x4d, 0x89, 0xd3, 0xe5, 0xb2, 0x95, - 0x84, 0x7d, 0xfb, 0xbe, 0x9d, 0x2a, 0xf2, 0xf8, 0x28, 0x0d, 0x79, 0x05, 0x9e, 0x64, 0x1c, 0x49, 0xa2, 0x44, 0xf0, - 0x3a, 0x6f, 0xcc, 0x38, 0xbc, 0x68, 0x53, 0x4e, 0xed, 0xcd, 0x7a, 0x01, 0x38, 0x4f, 0xd0, 0x96, 0x01, 0xc6, 0x02, - 0x06, 0xe7, 0x42, 0x2c, 0x79, 0x8a, 0xe0, 0x97, 0x4e, 0xa4, 0x30, 0xee, 0x72, 0x18, 0xe6, 0x41, 0xd1, 0xbb, 0xa4, - 0xfe, 0xe8, 0xf7, 0x51, 0x9b, 0x0c, 0x86, 0xa0, 0x12, 0x40, 0x65, 0xdd, 0x20, 0x31, 0xb0, 0x2a, 0x2d, 0x24, 0x2e, - 0x21, 0x5e, 0xe6, 0xab, 0x69, 0x1d, 0x05, 0xef, 0xeb, 0x09, 0x21, 0x9c, 0x60, 0x7c, 0x88, 0x1b, 0x20, 0x60, 0xb0, - 0x8a, 0x0b, 0x0c, 0x92, 0xe7, 0x12, 0xdd, 0x1f, 0xcf, 0x77, 0x0c, 0x70, 0xe5, 0xbc, 0xa7, 0xda, 0xd5, 0x03, 0x7b, - 0xb9, 0x4a, 0x97, 0x8c, 0x10, 0x56, 0xfc, 0x5f, 0x44, 0xde, 0xb7, 0xc3, 0x04, 0xd4, 0x36, 0xf2, 0xc7, 0x20, 0x31, - 0x97, 0x89, 0x22, 0x88, 0x47, 0x59, 0xc1, 0x92, 0x34, 0xd8, 0x8c, 0x92, 0x14, 0x34, 0x9a, 0x18, 0x43, 0xa6, 0x42, - 0x3b, 0x24, 0x8d, 0x66, 0x63, 0xb2, 0x8f, 0x21, 0xaf, 0xe1, 0x62, 0xb1, 0xc0, 0xfb, 0x7e, 0x16, 0xaa, 0x83, 0x6d, - 0x69, 0x0e, 0x01, 0x27, 0x09, 0xf6, 0xd4, 0x15, 0x29, 0x09, 0xb3, 0xd1, 0xa7, 0x90, 0x73, 0x03, 0x3a, 0x4e, 0x1a, - 0x43, 0xf5, 0x81, 0x49, 0x78, 0x15, 0xa1, 0x93, 0xb2, 0x42, 0x58, 0xc0, 0x7d, 0x23, 0xa3, 0xd1, 0x4a, 0x1a, 0x04, - 0xde, 0x66, 0xd8, 0x0a, 0x6c, 0x42, 0xc3, 0x5f, 0x64, 0x1e, 0xa6, 0xd5, 0xac, 0x04, 0x73, 0xbe, 0x81, 0x4a, 0x8c, - 0x27, 0xcb, 0x2b, 0xbe, 0x71, 0xb1, 0x12, 0x93, 0xd9, 0x72, 0x3e, 0x59, 0x4b, 0xaa, 0xb9, 0xdc, 0x5b, 0xb3, 0x8c, - 0x2d, 0x61, 0xff, 0x30, 0x30, 0x94, 0x0e, 0xec, 0x68, 0xaa, 0x69, 0x93, 0x00, 0x93, 0xe9, 0x9c, 0xf3, 0xe1, 0x25, - 0xa2, 0xc9, 0xea, 0xd4, 0x9d, 0x4c, 0x55, 0x3b, 0xb8, 0x26, 0x67, 0x72, 0x7a, 0xa4, 0x9e, 0x6a, 0xdd, 0x4b, 0x3e, - 0xda, 0x0e, 0xab, 0xd1, 0xd6, 0x0f, 0xc0, 0xad, 0x53, 0xd8, 0xe9, 0xbb, 0x61, 0x35, 0xda, 0xf9, 0x1a, 0x76, 0x97, - 0x14, 0x02, 0xd5, 0x9f, 0x65, 0x4d, 0xe6, 0xe2, 0x75, 0xf1, 0xe0, 0x15, 0xec, 0xb9, 0x3f, 0xd0, 0xbf, 0x4a, 0xf6, - 0xdc, 0xb7, 0x99, 0x5c, 0xff, 0x4c, 0xbb, 0x46, 0x63, 0xa6, 0xe3, 0xb5, 0x2b, 0xb0, 0x42, 0x03, 0xe4, 0x17, 0xec, - 0x68, 0x6f, 0x72, 0x10, 0x08, 0xd0, 0xbd, 0x04, 0x47, 0x51, 0x40, 0xd4, 0xb4, 0xaa, 0x3c, 0x3a, 0xdd, 0xfb, 0x7b, - 0x7c, 0xa3, 0x04, 0x6c, 0xf2, 0xd4, 0xba, 0xb7, 0x8c, 0xfd, 0xc3, 0x01, 0x42, 0xe8, 0xe5, 0xf4, 0x1b, 0x6d, 0x59, - 0x3d, 0xda, 0xb1, 0xdc, 0x37, 0x8c, 0x7a, 0x0a, 0xc6, 0x30, 0x74, 0x61, 0x15, 0x23, 0x79, 0x06, 0x64, 0x8d, 0xdf, - 0x20, 0xba, 0x80, 0x45, 0xaf, 0xf7, 0xea, 0x88, 0x06, 0x11, 0x50, 0xe9, 0x35, 0x7f, 0x29, 0xf2, 0xb9, 0x2a, 0x44, - 0xef, 0xbd, 0xb5, 0xf3, 0x66, 0x46, 0xb2, 0x4c, 0x1a, 0xa9, 0x76, 0x2b, 0x8b, 0x75, 0xe5, 0xcd, 0x4e, 0x48, 0x17, - 0x73, 0x0c, 0x95, 0xc1, 0xe3, 0x00, 0x94, 0x9e, 0x7f, 0x0b, 0xbd, 0x92, 0x21, 0xd3, 0x2c, 0xd1, 0xcc, 0xee, 0x1a, - 0x7f, 0xb2, 0x4a, 0xbd, 0x18, 0x11, 0xb3, 0x81, 0x2d, 0xc4, 0x6d, 0x51, 0xe9, 0xb6, 0x28, 0x94, 0x2d, 0x8a, 0xf4, - 0xa1, 0x76, 0xa6, 0x3b, 0xb3, 0xf0, 0x59, 0x65, 0xda, 0xf7, 0x26, 0x33, 0x63, 0x03, 0xb4, 0x5d, 0x84, 0x6f, 0xa0, - 0x03, 0x15, 0x42, 0xfe, 0x03, 0x22, 0x22, 0x11, 0xb0, 0xcb, 0xa9, 0x3b, 0xb1, 0xe9, 0x90, 0xcc, 0x43, 0xcc, 0x0a, - 0x35, 0xca, 0x4b, 0x9e, 0x1c, 0x0d, 0x48, 0x45, 0xa8, 0xdb, 0xfd, 0xfe, 0xf9, 0xd2, 0x05, 0xb5, 0x5f, 0x53, 0xec, - 0x18, 0xdd, 0x14, 0x70, 0x2e, 0x78, 0x94, 0xf7, 0xdc, 0x3b, 0x07, 0x34, 0xc7, 0xf6, 0x14, 0x59, 0x03, 0x4e, 0x6f, - 0xbb, 0x10, 0x60, 0xfb, 0xac, 0xd9, 0xda, 0x9f, 0xac, 0xae, 0xa2, 0xa9, 0x57, 0xf2, 0x99, 0xee, 0xa2, 0xc4, 0xed, - 0xa2, 0x58, 0x76, 0xd1, 0xa6, 0x81, 0x60, 0xc7, 0x95, 0x1f, 0x00, 0x6f, 0x68, 0xd4, 0xef, 0x97, 0xad, 0x9e, 0x3d, - 0xf9, 0xda, 0x71, 0xcf, 0x66, 0x3e, 0x2b, 0x4d, 0xcf, 0xfe, 0x9a, 0xba, 0x3d, 0x2b, 0x27, 0x7b, 0xd1, 0x39, 0xd9, - 0xa7, 0xb3, 0x79, 0x20, 0xb8, 0xdc, 0xb9, 0xcf, 0xf3, 0xa9, 0x9e, 0x76, 0x95, 0x1f, 0xb4, 0x86, 0xc8, 0x7c, 0xe1, - 0x53, 0xd5, 0xbd, 0xae, 0x60, 0x01, 0x4b, 0x70, 0xb7, 0x5e, 0x9a, 0xff, 0x8a, 0xdd, 0xdf, 0x0b, 0x7a, 0x69, 0xfe, - 0x1b, 0xfd, 0x49, 0x01, 0x1c, 0x80, 0xc6, 0xd4, 0x6e, 0x81, 0x87, 0x18, 0x2a, 0x28, 0xdc, 0xcd, 0xca, 0xb9, 0x57, - 0x03, 0x1c, 0x26, 0xe9, 0x1b, 0x5a, 0xbd, 0xd2, 0x62, 0xd7, 0xcb, 0x64, 0xaf, 0x00, 0x0f, 0x55, 0xc8, 0xc3, 0xc3, - 0x21, 0xea, 0x18, 0x76, 0x50, 0x47, 0xc0, 0xb0, 0x87, 0xd0, 0xd8, 0x02, 0xcf, 0xc7, 0x4f, 0x19, 0xdf, 0x0b, 0x50, - 0x1b, 0x21, 0x3c, 0x5e, 0x2d, 0xca, 0x10, 0x5b, 0xf6, 0x06, 0xa9, 0xa4, 0x7e, 0x16, 0x88, 0x32, 0x5a, 0x05, 0xb4, - 0xd5, 0x1e, 0xb3, 0x34, 0xde, 0x40, 0xa8, 0x58, 0xea, 0x63, 0x08, 0x0d, 0x1c, 0x7e, 0x87, 0x03, 0x48, 0xf0, 0x25, - 0xd7, 0x64, 0x73, 0x6f, 0xf2, 0x7b, 0xda, 0xe7, 0x0f, 0x87, 0xf3, 0x4b, 0x04, 0xa5, 0x4b, 0xe1, 0x23, 0x95, 0x88, - 0xea, 0x29, 0x6e, 0x4a, 0xc8, 0x66, 0xc9, 0x4a, 0x3f, 0xf8, 0x55, 0xfd, 0x02, 0x00, 0x59, 0x08, 0xb4, 0x89, 0xcc, - 0xfe, 0x74, 0xa6, 0xa2, 0x0b, 0x80, 0x43, 0xfc, 0xf1, 0x13, 0x44, 0xdf, 0xd0, 0x32, 0x2d, 0x1f, 0x27, 0x3c, 0x04, - 0xad, 0x2d, 0xe9, 0x24, 0x62, 0xa5, 0xc0, 0x86, 0x48, 0xf8, 0x7e, 0xff, 0x3c, 0x96, 0x74, 0xa0, 0x51, 0xab, 0x7b, - 0xe3, 0x56, 0xf7, 0xca, 0xd7, 0x75, 0x27, 0x37, 0x3e, 0x28, 0xda, 0x67, 0xf3, 0x46, 0xe5, 0xfb, 0xb6, 0xce, 0xd9, - 0x9d, 0xee, 0x1d, 0x39, 0x27, 0xbe, 0xbd, 0x87, 0x50, 0xf4, 0xd0, 0x14, 0x59, 0x96, 0x84, 0x01, 0xad, 0xb5, 0x6b, - 0xcf, 0x32, 0x3a, 0x78, 0xed, 0x1b, 0x42, 0x44, 0x9e, 0xe2, 0x93, 0x90, 0x5b, 0x1c, 0x1f, 0x14, 0xe8, 0x9f, 0x19, - 0x7f, 0xe6, 0xc4, 0x0f, 0x5b, 0xfd, 0x02, 0x38, 0x37, 0xdd, 0x7b, 0x77, 0x62, 0xd6, 0x63, 0x28, 0x65, 0xe3, 0xff, - 0x7e, 0x9f, 0xc8, 0x02, 0x9d, 0x8e, 0x68, 0x18, 0x08, 0xee, 0xa2, 0xfa, 0xbf, 0x57, 0xbc, 0xee, 0x59, 0xab, 0xf3, - 0xe5, 0xa7, 0x4e, 0x4f, 0x7a, 0xf5, 0x32, 0xee, 0x01, 0x15, 0x3a, 0x40, 0x38, 0xaf, 0xfb, 0x0d, 0xdb, 0x7d, 0xf3, - 0xcb, 0xbb, 0xa3, 0x97, 0x81, 0x4d, 0x8a, 0xc4, 0xb6, 0x92, 0xcf, 0x7a, 0xa0, 0xf0, 0xeb, 0xb1, 0x5e, 0x5d, 0xac, - 0x7b, 0xac, 0x87, 0x5a, 0x40, 0xf4, 0xb0, 0x00, 0xf5, 0x5f, 0xcf, 0x3e, 0x0d, 0x85, 0x83, 0x6c, 0x9c, 0x2a, 0x50, - 0x64, 0xc1, 0xaf, 0xc5, 0x68, 0x5d, 0x10, 0x20, 0xb2, 0x25, 0xa4, 0x55, 0x27, 0xb3, 0xc7, 0xa5, 0x96, 0x64, 0xf0, - 0x4d, 0x40, 0x66, 0x07, 0x56, 0x4e, 0x50, 0x3a, 0x6e, 0x0d, 0xb8, 0xb2, 0xc5, 0xa3, 0xdd, 0xfe, 0x34, 0xc8, 0xce, - 0x9a, 0x93, 0x46, 0xfb, 0xb0, 0x4f, 0xf3, 0x00, 0x81, 0x48, 0xa6, 0x22, 0xc8, 0x35, 0xf7, 0x96, 0xf4, 0xd1, 0xe1, - 0x9c, 0x17, 0xf2, 0xcf, 0xa9, 0xd4, 0x21, 0x0e, 0x25, 0xd6, 0x40, 0xa0, 0xf2, 0x0c, 0x55, 0x0e, 0x1b, 0xe4, 0xf8, - 0x67, 0x47, 0x32, 0x93, 0x98, 0x2c, 0x72, 0xb7, 0x66, 0x2a, 0xfc, 0x40, 0xf0, 0x31, 0xcb, 0x39, 0x70, 0x81, 0xcd, - 0xe6, 0xbe, 0x9a, 0xe2, 0xe2, 0x0a, 0xfc, 0x31, 0x85, 0x5f, 0xf1, 0x14, 0x76, 0xda, 0xfd, 0xba, 0xa8, 0x52, 0xd4, - 0x6d, 0x14, 0x16, 0x95, 0x2c, 0x98, 0xd6, 0x90, 0x26, 0x3a, 0x8c, 0xfe, 0x20, 0x67, 0xa0, 0x20, 0xe4, 0x97, 0x4d, - 0x03, 0x8c, 0x54, 0x72, 0x79, 0x50, 0x25, 0x81, 0x17, 0x60, 0x1b, 0x54, 0x6c, 0x5d, 0x40, 0x90, 0x6d, 0x52, 0x94, - 0xe9, 0x97, 0x22, 0xaf, 0xc3, 0x2c, 0xa8, 0x46, 0x69, 0xf5, 0xa3, 0xfe, 0x09, 0xcc, 0xdb, 0x54, 0x8c, 0x6a, 0x15, - 0x93, 0xdf, 0xe8, 0xf7, 0x8b, 0x41, 0xeb, 0x43, 0x06, 0x1f, 0xbd, 0x36, 0x0d, 0xfe, 0xe8, 0x34, 0xd8, 0x61, 0xa2, - 0x11, 0x00, 0xc9, 0x9c, 0x5a, 0xf2, 0x50, 0xf4, 0x47, 0x90, 0x63, 0x8d, 0x2a, 0xa7, 0x60, 0xb0, 0xfe, 0xe3, 0xd1, - 0x0e, 0x4c, 0xbd, 0x38, 0xda, 0x92, 0x1d, 0xb4, 0xf2, 0x0d, 0x70, 0xbf, 0x46, 0xb6, 0x98, 0xe5, 0x00, 0xcd, 0x5e, - 0x23, 0x32, 0x3e, 0x79, 0x01, 0x8c, 0xd9, 0x3a, 0x0b, 0x23, 0x11, 0x07, 0x63, 0xd5, 0x98, 0x31, 0x03, 0x03, 0x17, - 0xe8, 0x5a, 0x26, 0x25, 0x69, 0x48, 0x07, 0x03, 0x56, 0xca, 0x16, 0x0e, 0x78, 0xd1, 0x1c, 0xb7, 0xe3, 0x75, 0x8b, - 0xc6, 0x03, 0xdb, 0xc5, 0xf6, 0xf7, 0xdf, 0x17, 0xdb, 0xb7, 0xe1, 0x96, 0xf4, 0x0a, 0x39, 0x4b, 0xe8, 0xe7, 0x8f, - 0xb2, 0xcf, 0x1a, 0x4e, 0x4e, 0x85, 0x66, 0x68, 0x29, 0x12, 0x4a, 0xf1, 0x4e, 0x4f, 0x0a, 0x8c, 0x65, 0x2c, 0xfc, - 0x3d, 0x70, 0x4e, 0x17, 0x8a, 0xc8, 0x1d, 0x38, 0x8e, 0x6f, 0xa0, 0x82, 0x51, 0xc3, 0xc1, 0xcb, 0x18, 0xb6, 0x45, - 0x31, 0x0b, 0x09, 0xa7, 0x10, 0x2e, 0x56, 0x59, 0xbf, 0x2f, 0x7f, 0x51, 0x17, 0x5d, 0x64, 0xb2, 0xee, 0x93, 0x70, - 0x64, 0xc6, 0x72, 0xea, 0x85, 0xe4, 0x79, 0xcf, 0x93, 0x69, 0xf2, 0x2c, 0x0f, 0x22, 0x80, 0x7c, 0x0e, 0xef, 0xc3, - 0x34, 0x03, 0xab, 0x34, 0x29, 0x3f, 0x42, 0xe9, 0x8b, 0xcf, 0x2b, 0x3f, 0xd0, 0xd9, 0x73, 0x93, 0x0c, 0x6f, 0x56, - 0xad, 0x37, 0xa9, 0x75, 0x5d, 0x3c, 0xe0, 0x5f, 0x9c, 0xc1, 0xc6, 0xb9, 0xce, 0x04, 0x07, 0x5e, 0x24, 0xb5, 0x5e, - 0x33, 0x7e, 0x9d, 0xe1, 0xba, 0x54, 0x6d, 0xf4, 0x51, 0x88, 0xce, 0x21, 0x53, 0x01, 0x0a, 0x45, 0xda, 0x3f, 0x28, - 0xb5, 0x32, 0xa9, 0xb4, 0x91, 0x00, 0xba, 0x87, 0x49, 0x83, 0x2d, 0x86, 0x32, 0x96, 0x26, 0x51, 0xee, 0x34, 0x88, - 0x2b, 0xfb, 0x73, 0x25, 0x71, 0x68, 0x59, 0x24, 0xff, 0xde, 0xf5, 0xf4, 0x15, 0x52, 0x77, 0xb2, 0x40, 0x66, 0x8c, - 0x17, 0x79, 0xfc, 0x09, 0x08, 0xb3, 0x41, 0x1b, 0x15, 0x85, 0x10, 0xb2, 0x41, 0x0c, 0x1a, 0x2f, 0xf2, 0xf8, 0x7b, - 0x45, 0xe3, 0x21, 0x1f, 0x45, 0xbe, 0xfa, 0xab, 0xd4, 0x7f, 0x85, 0x3e, 0x33, 0xc1, 0x23, 0x54, 0x13, 0xfd, 0xbb, - 0xe7, 0xb3, 0x7b, 0x50, 0x1b, 0x46, 0x61, 0x66, 0xca, 0xaf, 0x7c, 0x53, 0x9c, 0xbd, 0xfe, 0x8a, 0xae, 0xb2, 0xad, - 0xfb, 0xd1, 0xc7, 0x23, 0x02, 0x6b, 0x63, 0x74, 0xc5, 0x8d, 0x01, 0xe4, 0x30, 0x79, 0xbf, 0xa2, 0xb4, 0x1c, 0xd2, - 0x20, 0x74, 0xd0, 0x10, 0xf4, 0x4a, 0xa2, 0x0f, 0x24, 0x16, 0x31, 0x86, 0x17, 0xe2, 0x19, 0xa9, 0xc9, 0x44, 0x43, - 0xbc, 0x22, 0xf6, 0x43, 0xb4, 0xe4, 0xd4, 0x44, 0x37, 0xc2, 0x14, 0x03, 0x89, 0x9d, 0x41, 0x72, 0x92, 0xd4, 0xca, - 0x2f, 0x9e, 0x49, 0xc2, 0x12, 0x3b, 0x0f, 0x31, 0x98, 0xd4, 0xd2, 0x9d, 0xde, 0x54, 0xe9, 0xdd, 0x91, 0x96, 0x83, - 0xf6, 0x01, 0xd8, 0xa5, 0xa4, 0xf7, 0x4f, 0x0a, 0x45, 0x7c, 0x08, 0xe3, 0x18, 0xc2, 0xb7, 0x88, 0xea, 0x0a, 0x9c, - 0x6b, 0x05, 0x1a, 0xab, 0x81, 0x87, 0x66, 0x56, 0xcd, 0x87, 0x9c, 0x7e, 0x2a, 0x2d, 0x7f, 0x8c, 0x68, 0x6c, 0xb4, - 0x6e, 0x0e, 0x87, 0x3d, 0xad, 0x7a, 0xe9, 0x1c, 0x74, 0xd9, 0x4c, 0x62, 0xe2, 0x06, 0xd2, 0xf5, 0xa3, 0xdf, 0x4c, - 0xd8, 0x8b, 0xa8, 0x90, 0x4b, 0x21, 0x28, 0x68, 0x75, 0x20, 0x70, 0x28, 0xbc, 0x45, 0x99, 0x2f, 0x62, 0xda, 0x40, - 0x18, 0x7c, 0x7e, 0x20, 0x3f, 0xdf, 0x14, 0xa4, 0x62, 0xc7, 0xba, 0xf6, 0xfb, 0x9b, 0xd2, 0x03, 0x3c, 0x39, 0x93, - 0xe4, 0x69, 0x33, 0x84, 0x15, 0x01, 0x34, 0x66, 0x35, 0x59, 0x9c, 0x70, 0x65, 0x0e, 0x3f, 0x56, 0x5e, 0xc9, 0x52, - 0xa6, 0xce, 0x53, 0xbd, 0x00, 0xa2, 0x8e, 0x37, 0x68, 0x45, 0xea, 0x57, 0xe8, 0xec, 0x35, 0x2b, 0x21, 0xe3, 0xe1, - 0x39, 0xe7, 0xe9, 0xe8, 0x81, 0x25, 0x3c, 0xc2, 0xbf, 0x92, 0x89, 0x3e, 0xfc, 0x1e, 0x38, 0xdc, 0x8c, 0x13, 0x1e, - 0xb9, 0xcd, 0xde, 0x57, 0xe1, 0x0a, 0x6e, 0xa6, 0x05, 0x20, 0xb9, 0x05, 0x49, 0x13, 0x50, 0x42, 0x22, 0x13, 0x32, - 0x6b, 0x4a, 0x7e, 0x6e, 0x69, 0x1b, 0xac, 0x61, 0xd2, 0x79, 0xc0, 0x8b, 0x56, 0x1f, 0xad, 0x26, 0xda, 0x65, 0x96, - 0xcf, 0x87, 0x38, 0x43, 0x35, 0xc7, 0xdd, 0x19, 0xfc, 0x1c, 0xf0, 0x8a, 0x55, 0x4d, 0x3a, 0xda, 0x0d, 0xb8, 0xf0, - 0xe4, 0x3a, 0x4f, 0x47, 0x5b, 0xfc, 0x25, 0xf7, 0x07, 0x80, 0x0e, 0xa6, 0x2e, 0x81, 0x3f, 0x55, 0x5b, 0x4d, 0xa5, - 0x7e, 0x69, 0xed, 0xd7, 0x75, 0x67, 0xb5, 0x72, 0xcf, 0xba, 0x0c, 0xed, 0x91, 0x21, 0x67, 0xcc, 0x80, 0x3f, 0x67, - 0x2c, 0xf9, 0x73, 0xc6, 0x8a, 0x3f, 0x67, 0xdc, 0x18, 0x19, 0x40, 0x09, 0xee, 0x25, 0xbf, 0xde, 0x23, 0x66, 0x88, - 0xd5, 0xa0, 0x12, 0x58, 0x59, 0xca, 0xb9, 0x8f, 0x9c, 0x62, 0xca, 0x29, 0xc3, 0x4b, 0xa7, 0x33, 0x77, 0x20, 0xe7, - 0xc1, 0xcc, 0x1d, 0x26, 0x67, 0x7d, 0x8a, 0x63, 0x69, 0x4c, 0x8a, 0x0a, 0xd2, 0x39, 0x1d, 0x6e, 0x5e, 0x1d, 0xe7, - 0x09, 0xcb, 0xf8, 0xb8, 0x7d, 0xa6, 0x40, 0x88, 0x2d, 0x9e, 0x21, 0x91, 0x52, 0x35, 0xcb, 0x6d, 0xfe, 0x70, 0xa8, - 0x47, 0x0f, 0x7a, 0xa7, 0x87, 0x5f, 0x09, 0xfb, 0x25, 0xf3, 0xec, 0x13, 0x04, 0x30, 0x49, 0xe4, 0x99, 0x84, 0xa3, - 0x1f, 0xcb, 0xd1, 0xdf, 0x34, 0xfc, 0x5d, 0x86, 0xea, 0xee, 0x10, 0x98, 0xd8, 0xb2, 0x03, 0x87, 0xe0, 0x74, 0x55, - 0x89, 0x04, 0x1c, 0x6c, 0x36, 0x2c, 0xd2, 0x7b, 0x3c, 0xc4, 0xf9, 0xa0, 0xf0, 0x11, 0x1a, 0x66, 0xf4, 0x7e, 0x7f, - 0x23, 0xbc, 0x4a, 0xb6, 0xf2, 0x70, 0x48, 0xac, 0xbb, 0xb0, 0xa3, 0x8f, 0xa3, 0x3d, 0x4a, 0xa8, 0xfd, 0xa8, 0xd6, - 0x9b, 0x4a, 0x3d, 0xc8, 0xcd, 0x2e, 0x24, 0x06, 0x15, 0x4b, 0xf5, 0xe9, 0x95, 0xea, 0x43, 0xcd, 0x3a, 0xbf, 0xab, - 0xe3, 0x3e, 0x15, 0xa3, 0xb5, 0x9c, 0x10, 0xe0, 0x3a, 0x48, 0x34, 0x3a, 0x00, 0xc6, 0xd9, 0x66, 0xcb, 0x4b, 0x6d, - 0x9d, 0x28, 0x1d, 0xc7, 0xb9, 0x3e, 0x8e, 0x0f, 0x07, 0x29, 0x66, 0x5c, 0x1e, 0x89, 0x19, 0x97, 0x0d, 0xc0, 0x9b, - 0x75, 0x1e, 0xd4, 0x87, 0xc3, 0x25, 0x5d, 0x8a, 0x4c, 0x67, 0x1b, 0xe5, 0x67, 0x3d, 0x7a, 0x78, 0x96, 0xa0, 0xb9, - 0xb7, 0xc2, 0xde, 0x8b, 0x64, 0x7b, 0x26, 0xeb, 0xd4, 0xcb, 0xc8, 0xa7, 0x17, 0xee, 0xd9, 0x25, 0x57, 0x3f, 0xac, - 0xbe, 0x9e, 0xfe, 0x2a, 0xbc, 0x88, 0x55, 0xb4, 0x5b, 0x97, 0x4c, 0xd8, 0x5b, 0x4a, 0x25, 0xad, 0xf2, 0xf2, 0xe9, - 0xc6, 0x0f, 0x30, 0x33, 0xed, 0xe9, 0x83, 0x6c, 0x44, 0xf5, 0x67, 0x25, 0x6a, 0x65, 0x98, 0x2c, 0x9c, 0x97, 0x4c, - 0x3d, 0x19, 0xf0, 0x98, 0x95, 0x3c, 0x92, 0x9d, 0xde, 0x18, 0x04, 0x01, 0xac, 0x73, 0xd2, 0xaa, 0x33, 0x8e, 0x46, - 0xab, 0xca, 0xc5, 0xe9, 0x2a, 0x17, 0x18, 0x6e, 0xb7, 0x66, 0x1b, 0x55, 0x67, 0xb9, 0xa9, 0x55, 0xca, 0x77, 0x00, - 0x1f, 0xcb, 0x2a, 0x17, 0x74, 0x4c, 0x99, 0x3a, 0x6f, 0x20, 0x18, 0x5b, 0xd5, 0xb8, 0x70, 0x6a, 0x5c, 0xf0, 0x88, - 0xda, 0xdd, 0x34, 0xf5, 0x68, 0x0b, 0x2c, 0xa5, 0xa3, 0x1d, 0x2f, 0x51, 0xa5, 0xf0, 0x77, 0xc1, 0xf7, 0x61, 0x1c, - 0x7f, 0x5f, 0x6c, 0xd5, 0x81, 0x78, 0x5b, 0x6c, 0x91, 0xf6, 0x45, 0xfe, 0x85, 0x38, 0xe0, 0xb5, 0xae, 0x29, 0xaf, - 0xad, 0x39, 0x0d, 0x6c, 0x0d, 0x23, 0x25, 0x85, 0x73, 0xf3, 0xe7, 0xe1, 0x40, 0x2b, 0xbb, 0x56, 0x77, 0x85, 0x5a, - 0x8f, 0x39, 0x6c, 0xd8, 0x8b, 0x2c, 0xdc, 0x89, 0x12, 0x1c, 0xb9, 0xe4, 0x5f, 0x87, 0x83, 0x56, 0x59, 0xaa, 0x23, - 0x7d, 0xb6, 0xff, 0x12, 0x8c, 0x19, 0xba, 0x34, 0x01, 0xcb, 0xc6, 0x48, 0xfe, 0xd5, 0x34, 0xf3, 0x86, 0xc9, 0x9a, - 0x29, 0x1c, 0x87, 0x86, 0x11, 0xd2, 0x80, 0x6e, 0x83, 0xda, 0xf0, 0x64, 0xbe, 0xa9, 0xca, 0xaf, 0xee, 0x48, 0xb5, - 0x1f, 0x0c, 0x2f, 0x27, 0xe2, 0x9c, 0x2e, 0x49, 0xea, 0xa9, 0x84, 0x92, 0x10, 0xec, 0xd2, 0x07, 0x72, 0x62, 0x05, - 0x64, 0x2d, 0x63, 0xf9, 0xad, 0x1e, 0x10, 0xfa, 0x4f, 0xbb, 0xf5, 0x42, 0xff, 0x69, 0x9a, 0x2d, 0xd4, 0xf5, 0x87, - 0xc9, 0x7d, 0x47, 0xaf, 0x3f, 0x38, 0xbc, 0x53, 0x57, 0x15, 0x57, 0xf1, 0xb0, 0x36, 0x4c, 0x72, 0xa3, 0x2c, 0xdc, - 0x15, 0x9b, 0x5a, 0x2d, 0x4f, 0xc7, 0x61, 0x04, 0x66, 0x04, 0x05, 0xc8, 0xba, 0x6e, 0x23, 0x62, 0x58, 0xc9, 0x65, - 0x42, 0x3e, 0x21, 0x20, 0x8b, 0x52, 0xe3, 0x7c, 0xdc, 0x02, 0x95, 0x08, 0x06, 0xa7, 0xa1, 0xb5, 0xea, 0x26, 0x3f, - 0xaa, 0x6c, 0xec, 0x0e, 0xc8, 0x21, 0xc9, 0x64, 0x71, 0x37, 0xba, 0x15, 0xcb, 0xa2, 0x14, 0x3f, 0x63, 0x3d, 0x5c, - 0xb3, 0x85, 0xfb, 0x0c, 0x08, 0xed, 0x27, 0x4a, 0x7b, 0x13, 0x69, 0x82, 0xee, 0x3b, 0xb6, 0x02, 0x90, 0x01, 0x14, - 0x75, 0xb5, 0x5b, 0x9f, 0xf3, 0x73, 0x24, 0xcd, 0x70, 0x18, 0xdd, 0x3e, 0xbd, 0x0b, 0xee, 0x06, 0x97, 0xa8, 0x95, - 0xbe, 0x64, 0x71, 0x0b, 0x83, 0x6a, 0x6f, 0x96, 0x70, 0x50, 0x33, 0x6b, 0x6d, 0x04, 0x82, 0xc9, 0x1e, 0x0a, 0x2a, - 0xe6, 0x0a, 0xf6, 0x41, 0xc1, 0x5a, 0xf2, 0x3a, 0x38, 0xdc, 0xda, 0x97, 0x95, 0xe2, 0xe2, 0xf9, 0x45, 0xd2, 0xba, - 0xb0, 0x94, 0x17, 0xcf, 0x1b, 0x30, 0xb8, 0x1c, 0x61, 0x53, 0x55, 0xfe, 0x64, 0x03, 0xa0, 0x5b, 0x21, 0x45, 0xbc, - 0x28, 0x85, 0x6d, 0x2b, 0x9f, 0x39, 0x61, 0x83, 0x0d, 0x7b, 0x80, 0x7b, 0x65, 0x50, 0x32, 0xb8, 0x10, 0xe3, 0x76, - 0xb3, 0x0b, 0x70, 0x05, 0x43, 0x61, 0x6c, 0xcd, 0x5f, 0x67, 0x5e, 0xa4, 0x04, 0xdc, 0x0c, 0x51, 0xbe, 0x36, 0x70, - 0x32, 0xe9, 0xc9, 0xb5, 0x64, 0x31, 0x60, 0x41, 0x83, 0xef, 0xa8, 0xf5, 0x77, 0x26, 0xff, 0xc6, 0xd3, 0x43, 0x3f, - 0xf8, 0x9c, 0x79, 0x4b, 0x9f, 0xbd, 0xae, 0x64, 0xb4, 0x26, 0x89, 0xf2, 0xea, 0xe1, 0x12, 0xe4, 0x86, 0xe5, 0xe8, - 0x81, 0x2d, 0x41, 0x9c, 0x58, 0x8e, 0x12, 0xca, 0xe8, 0x0a, 0xf7, 0x2a, 0xb3, 0x65, 0x22, 0x90, 0xe2, 0xc0, 0x52, - 0xca, 0xbd, 0xc5, 0x3a, 0x58, 0xe2, 0xfe, 0x44, 0x72, 0x01, 0x25, 0x0f, 0xa0, 0x5c, 0x29, 0x20, 0xe0, 0xd3, 0x01, - 0x94, 0x2f, 0xe5, 0x45, 0xf8, 0x13, 0x27, 0x6a, 0xb0, 0x1c, 0x3d, 0x34, 0xec, 0x47, 0x2f, 0xb4, 0xec, 0x0f, 0x77, - 0x5a, 0xd3, 0xb0, 0xe2, 0x77, 0x30, 0x2d, 0x26, 0x6e, 0x5f, 0xae, 0xec, 0xaa, 0xf8, 0x6c, 0xa5, 0xce, 0x6e, 0x6a, - 0x48, 0xc2, 0xbe, 0x22, 0xab, 0x00, 0x07, 0xab, 0x22, 0xee, 0x59, 0x96, 0xfb, 0x30, 0xfa, 0x73, 0x93, 0x96, 0xc2, - 0x42, 0x95, 0xf4, 0xf7, 0x4d, 0x29, 0x90, 0xca, 0x44, 0x27, 0x5a, 0x08, 0xae, 0xc0, 0x20, 0x70, 0x2f, 0xf2, 0x1a, - 0x00, 0x63, 0xc0, 0xa5, 0x40, 0x59, 0xb6, 0x25, 0x84, 0x54, 0xf7, 0x33, 0x50, 0xdb, 0x89, 0xfb, 0x34, 0x22, 0x6b, - 0x21, 0xfa, 0x2a, 0x18, 0x33, 0xe7, 0xa5, 0x74, 0x8b, 0x4d, 0x57, 0x9b, 0xd5, 0x0d, 0x3a, 0x97, 0xb6, 0xdc, 0xfc, - 0x84, 0x2d, 0xd6, 0x0a, 0x94, 0x4d, 0x48, 0xda, 0xce, 0x79, 0x8e, 0xb2, 0x09, 0x2d, 0xed, 0x3d, 0xf5, 0xa8, 0x50, - 0x9d, 0x6c, 0xbd, 0x54, 0x4d, 0x2d, 0xc2, 0x6a, 0x71, 0x51, 0xf9, 0x01, 0xe8, 0xa6, 0xd2, 0xea, 0x45, 0x5d, 0xa3, - 0x29, 0xd4, 0x6a, 0xe1, 0xb8, 0xd1, 0xce, 0xa6, 0xcb, 0xf4, 0x0e, 0x71, 0x56, 0xa5, 0x1d, 0xfa, 0xfb, 0x4c, 0xbb, - 0x5e, 0x76, 0xf4, 0x9b, 0x71, 0x75, 0x81, 0x0b, 0xb1, 0x01, 0x9f, 0x73, 0x7f, 0x79, 0xbd, 0xe7, 0x71, 0xcf, 0x3f, - 0x1c, 0x90, 0x3d, 0xa9, 0xfd, 0xa1, 0xfa, 0xd8, 0x15, 0x0c, 0x59, 0x18, 0xa5, 0xfe, 0x22, 0xe5, 0xbd, 0x27, 0x38, - 0xee, 0x9f, 0xab, 0x1e, 0xfb, 0x31, 0xe3, 0xfb, 0xba, 0xd8, 0x44, 0x09, 0x45, 0x35, 0xf4, 0x56, 0xc5, 0xa6, 0x12, - 0x71, 0xf1, 0x90, 0xf7, 0x18, 0x26, 0xc3, 0x58, 0xc8, 0x54, 0xf8, 0x53, 0xa6, 0x82, 0x47, 0x08, 0x25, 0x6e, 0xd6, - 0x3d, 0xd2, 0x6e, 0x42, 0x9c, 0x52, 0x2d, 0x4a, 0x99, 0x8c, 0x7f, 0xeb, 0x27, 0x50, 0x9e, 0x53, 0xb4, 0x4c, 0x3f, - 0x2a, 0x5c, 0xa6, 0x6f, 0xd6, 0xc7, 0xa5, 0x67, 0x22, 0xd4, 0x99, 0x8b, 0x4d, 0xad, 0xd3, 0x31, 0x76, 0x4a, 0xa7, - 0x36, 0xec, 0x4b, 0xa5, 0xb8, 0xac, 0x28, 0xfc, 0x1b, 0x89, 0xac, 0x7a, 0x46, 0x1c, 0xff, 0x57, 0xd6, 0x3e, 0xc3, - 0x2a, 0xf0, 0xcb, 0x40, 0xde, 0x2f, 0x00, 0x3e, 0xae, 0xeb, 0x32, 0xbd, 0xdd, 0x00, 0x6d, 0x08, 0x0d, 0x7f, 0xcf, - 0x47, 0x06, 0x4c, 0xf7, 0x11, 0xce, 0x90, 0x1e, 0xea, 0x9c, 0xd3, 0x59, 0x99, 0xce, 0xb9, 0x0a, 0x6b, 0x09, 0xf6, - 0x72, 0xd2, 0xe4, 0x72, 0x5d, 0x82, 0x9a, 0x09, 0xdc, 0x3e, 0xb4, 0x47, 0x84, 0x50, 0x9b, 0xb2, 0x9a, 0x5e, 0x42, - 0xcd, 0x3b, 0x39, 0xed, 0x68, 0x52, 0x82, 0xab, 0x86, 0xce, 0xca, 0xf5, 0x5f, 0x87, 0x43, 0xef, 0x36, 0x2b, 0xa2, - 0x3f, 0x7a, 0xe8, 0xef, 0xb8, 0xbd, 0x49, 0xbf, 0x40, 0xb4, 0x8c, 0xf5, 0x37, 0x64, 0x40, 0xc7, 0x93, 0xe1, 0x6d, - 0xb1, 0xed, 0xb1, 0x2f, 0xa8, 0xc1, 0xd2, 0xd7, 0x8f, 0x3f, 0x40, 0x42, 0xd5, 0xb5, 0x2f, 0x2c, 0x9e, 0x30, 0x4f, - 0x89, 0xb6, 0x85, 0x0f, 0x61, 0xa1, 0x5f, 0x20, 0x32, 0x12, 0xc2, 0x4d, 0x65, 0xf7, 0x28, 0x69, 0x17, 0xfa, 0xd2, - 0xd7, 0xb2, 0xaf, 0x7c, 0xe7, 0x02, 0x60, 0x65, 0x9f, 0xdb, 0x70, 0x4f, 0xfa, 0x53, 0xaa, 0x0f, 0xdb, 0xdf, 0x92, - 0x05, 0x14, 0x5a, 0x58, 0x4f, 0xe5, 0xec, 0x5c, 0x97, 0x3c, 0xcd, 0xa6, 0xfb, 0x35, 0xec, 0x51, 0xf7, 0xe8, 0x35, - 0x15, 0x9c, 0x5f, 0x9a, 0xd1, 0xfb, 0xa7, 0xa1, 0x50, 0x1d, 0x75, 0xee, 0x20, 0xeb, 0xd2, 0xba, 0xe4, 0xfc, 0x66, - 0xe5, 0x8e, 0xc2, 0xfc, 0x3e, 0x04, 0xcf, 0xb0, 0xee, 0xdd, 0xc5, 0x79, 0xef, 0xcf, 0xd6, 0x1c, 0xf9, 0x31, 0x9b, - 0xa5, 0x88, 0x45, 0x32, 0x07, 0xab, 0x1f, 0xfa, 0x79, 0xec, 0xb7, 0x41, 0x0e, 0xc7, 0x4d, 0x03, 0x3a, 0x6c, 0xc8, - 0xac, 0x7d, 0x89, 0xc0, 0xa9, 0x46, 0x90, 0xa6, 0x26, 0xa8, 0x59, 0x1e, 0x22, 0xb1, 0x5d, 0xca, 0xb6, 0x41, 0xae, - 0xbb, 0x60, 0x9a, 0x23, 0xed, 0x19, 0xbc, 0x6f, 0xd2, 0x24, 0x15, 0x9a, 0x45, 0xda, 0x2a, 0x19, 0xff, 0x8e, 0xb4, - 0x99, 0x92, 0x3d, 0xb6, 0x06, 0xde, 0x4b, 0x50, 0x4e, 0x86, 0x29, 0x86, 0xef, 0xf8, 0x7a, 0xe7, 0x31, 0xf7, 0x9c, - 0x63, 0xb6, 0x49, 0xd9, 0x11, 0x4c, 0x92, 0x8d, 0x6f, 0x28, 0xde, 0xf0, 0xfd, 0x6d, 0x25, 0x4a, 0x00, 0xbd, 0x2c, - 0xf8, 0xb5, 0xb4, 0xb9, 0x42, 0xb7, 0xbb, 0x77, 0x94, 0xc2, 0x2f, 0x79, 0x79, 0x38, 0x6c, 0x53, 0x2f, 0x84, 0xce, - 0x17, 0xf1, 0x3b, 0x30, 0x87, 0x31, 0xc4, 0x66, 0x04, 0x08, 0x73, 0x7c, 0x40, 0x1d, 0xac, 0x1f, 0x01, 0x68, 0x9c, - 0x40, 0x01, 0x46, 0x5f, 0x6d, 0x0b, 0xfa, 0x96, 0x17, 0x17, 0x11, 0xa2, 0x46, 0x01, 0x26, 0x4a, 0x9a, 0xc5, 0x30, - 0x1c, 0xe8, 0xfc, 0xbe, 0xb9, 0xad, 0x4b, 0x81, 0x43, 0xef, 0x58, 0x86, 0xff, 0xfe, 0x3f, 0xd6, 0x96, 0x56, 0x95, - 0xed, 0xd6, 0x38, 0xcd, 0xfc, 0x6f, 0xb7, 0x85, 0xbe, 0xff, 0x52, 0x28, 0x9e, 0x77, 0xbc, 0x6e, 0xbf, 0x83, 0xe8, - 0x7d, 0xdd, 0xca, 0xbb, 0x52, 0xbb, 0x61, 0xa6, 0xfc, 0x21, 0xcd, 0xe3, 0xe2, 0x61, 0x14, 0xb7, 0x8e, 0xbc, 0x49, - 0x7a, 0xce, 0xf9, 0xbb, 0xaa, 0xdf, 0xf7, 0xde, 0x01, 0x19, 0xef, 0x4b, 0x61, 0x1c, 0x31, 0x89, 0x83, 0x6f, 0x2f, - 0x46, 0xd1, 0xa6, 0x84, 0x0d, 0xb9, 0x7d, 0x5a, 0x82, 0x66, 0xa6, 0xdf, 0x47, 0x89, 0xd2, 0x9a, 0xef, 0x7f, 0x93, - 0xf3, 0xfd, 0xa5, 0x90, 0x37, 0x2b, 0xf9, 0xe1, 0xa3, 0x15, 0x06, 0xbe, 0xc7, 0xe9, 0x17, 0xd1, 0x63, 0x77, 0xa5, - 0x0f, 0xdf, 0x95, 0x96, 0x3e, 0xab, 0xa8, 0x7f, 0xa0, 0xa2, 0xe6, 0xa5, 0x18, 0x11, 0xf1, 0x20, 0x68, 0x67, 0xdb, - 0xa5, 0x76, 0x2d, 0x41, 0xbb, 0x60, 0x53, 0xd8, 0xbf, 0x1f, 0x1d, 0xf2, 0x7e, 0xff, 0x63, 0xee, 0xb5, 0x78, 0xdd, - 0x75, 0x68, 0xca, 0x4f, 0x85, 0x87, 0x10, 0xc0, 0x5a, 0x06, 0xca, 0x38, 0xc2, 0xa4, 0x8b, 0xbc, 0x46, 0xd9, 0x74, - 0x22, 0xf0, 0x31, 0xcb, 0xae, 0x9c, 0x64, 0x1a, 0x60, 0x46, 0x35, 0x85, 0x99, 0x00, 0x23, 0xf5, 0x11, 0xeb, 0xa6, - 0xa7, 0x55, 0x68, 0xf9, 0x1a, 0x82, 0x75, 0x91, 0x65, 0x1c, 0xc5, 0x4c, 0x00, 0xb0, 0xf9, 0x08, 0xf2, 0x15, 0x5d, - 0x1d, 0x92, 0x56, 0xaa, 0xbc, 0x5f, 0x67, 0x44, 0x46, 0x93, 0x10, 0xcd, 0x6f, 0xe1, 0x81, 0x7d, 0xdb, 0xcc, 0xa8, - 0x52, 0xcf, 0xa8, 0xca, 0x67, 0x38, 0x2c, 0x85, 0x63, 0xc4, 0xff, 0x7b, 0xaa, 0x7a, 0x44, 0xa0, 0x57, 0x65, 0x5a, - 0x45, 0x45, 0x9e, 0x8b, 0x08, 0x11, 0xaa, 0xa5, 0x73, 0x38, 0xf4, 0x63, 0xbf, 0x8f, 0x03, 0x61, 0x5e, 0xfc, 0xe9, - 0xb1, 0xae, 0xfc, 0xa9, 0xc0, 0xb5, 0x92, 0x02, 0xa7, 0xa2, 0x46, 0x88, 0x10, 0xde, 0x9f, 0xc0, 0xb3, 0x9a, 0xfa, - 0x7e, 0x63, 0x99, 0xe8, 0xfe, 0x99, 0x01, 0xe5, 0x0f, 0xc8, 0xd7, 0x95, 0x14, 0x67, 0xea, 0xe4, 0x31, 0x71, 0xc6, - 0x01, 0x88, 0xf9, 0xba, 0x44, 0xa3, 0xb1, 0xff, 0x01, 0x09, 0x86, 0xea, 0x07, 0x3b, 0xdd, 0xd4, 0xfb, 0x57, 0x26, - 0x71, 0x14, 0x7d, 0xda, 0x26, 0x8f, 0x25, 0x4b, 0xa3, 0x85, 0xa3, 0xf7, 0x88, 0x61, 0x1c, 0x4e, 0xe7, 0x63, 0x92, - 0x6d, 0x4c, 0x56, 0x01, 0xa4, 0x93, 0x99, 0x3a, 0xa6, 0xd4, 0xd1, 0x38, 0xd7, 0x0b, 0xaa, 0xd0, 0x63, 0x5d, 0xf2, - 0x1c, 0xac, 0x27, 0x3f, 0x78, 0xa5, 0x3f, 0x15, 0x72, 0x0e, 0x1b, 0x89, 0xa0, 0xf0, 0x03, 0x5c, 0x0d, 0x56, 0x0a, - 0x18, 0x4c, 0x7d, 0x0b, 0x5f, 0x13, 0xcf, 0x51, 0xf0, 0x28, 0xec, 0x62, 0x6c, 0xad, 0x7c, 0xe7, 0x93, 0x82, 0x72, - 0xcf, 0x8a, 0x39, 0xaf, 0x80, 0x73, 0x19, 0x14, 0xc2, 0x74, 0x3c, 0xcb, 0xff, 0x99, 0xe4, 0xf5, 0xc4, 0x86, 0x00, - 0x19, 0xfc, 0x29, 0x71, 0x5a, 0xba, 0x43, 0x77, 0x1e, 0x7a, 0x16, 0x71, 0xd8, 0xe8, 0xc9, 0xba, 0x2c, 0xb6, 0x29, - 0xea, 0x25, 0xcc, 0x0f, 0xe4, 0xe7, 0x2d, 0xf9, 0x3e, 0x44, 0xf1, 0x36, 0xf8, 0x35, 0x63, 0xb1, 0xc0, 0xbf, 0xfe, - 0x9e, 0x31, 0x9a, 0x68, 0xc1, 0xbf, 0xb3, 0x06, 0x89, 0x8a, 0x7f, 0xca, 0x26, 0x00, 0xeb, 0xc8, 0xd5, 0x87, 0x4f, - 0x89, 0xf1, 0xd6, 0x6c, 0x78, 0xe4, 0x9b, 0x15, 0xe8, 0xd4, 0xe7, 0xee, 0xca, 0xf6, 0x54, 0x35, 0xfe, 0x9e, 0xea, - 0x6a, 0xa4, 0xaa, 0x1a, 0x7f, 0x4f, 0xa9, 0x1a, 0xbf, 0x65, 0x14, 0xbf, 0x53, 0xf9, 0x0c, 0x99, 0x93, 0x4d, 0x4c, - 0xd2, 0xe9, 0x7b, 0xc3, 0x89, 0x5d, 0xf6, 0xab, 0xb7, 0x89, 0xcc, 0x44, 0x0a, 0xb9, 0x37, 0x00, 0x6d, 0xbf, 0xcb, - 0x0d, 0xa7, 0xc4, 0xf9, 0xb9, 0x87, 0x2b, 0x36, 0xad, 0x5e, 0xd2, 0x82, 0x05, 0x36, 0x2f, 0xb3, 0x3c, 0x45, 0x02, - 0xdb, 0xa6, 0xcc, 0xfa, 0x73, 0xee, 0x01, 0x04, 0x33, 0xa9, 0x09, 0x00, 0x69, 0x21, 0x2a, 0x85, 0xc8, 0x5f, 0xe2, - 0xac, 0x3e, 0xe7, 0xbd, 0x4d, 0x1e, 0x13, 0x69, 0x75, 0xaf, 0xdf, 0x4f, 0xcf, 0xd2, 0x9c, 0x82, 0x1a, 0x8e, 0xb3, - 0x4e, 0xbf, 0xcf, 0x82, 0x3a, 0x91, 0xab, 0xf4, 0x1f, 0x6e, 0x90, 0x97, 0xf1, 0x7d, 0xdd, 0xf6, 0xfc, 0x89, 0xfa, - 0x7b, 0x67, 0xfd, 0x6d, 0x81, 0xe0, 0x4e, 0x8e, 0xfd, 0x64, 0x55, 0xca, 0x13, 0xe3, 0xd2, 0xde, 0xf3, 0x9b, 0xba, - 0x28, 0xb2, 0x3a, 0x5d, 0x7f, 0x90, 0x7a, 0x1a, 0xdd, 0x17, 0x7b, 0x30, 0x06, 0xef, 0x00, 0xf0, 0x4c, 0x87, 0x06, - 0x48, 0xdf, 0x33, 0xf2, 0x70, 0x9f, 0x5b, 0xf2, 0x93, 0xca, 0xda, 0x24, 0x61, 0x45, 0xb1, 0x19, 0xc6, 0x08, 0x25, - 0xe3, 0x34, 0xb6, 0x7e, 0xbf, 0xaf, 0xfe, 0xde, 0x61, 0x14, 0x15, 0x15, 0x77, 0x8c, 0x46, 0x65, 0x55, 0x8f, 0xb6, - 0x83, 0xc3, 0xe1, 0x3c, 0xb7, 0x71, 0xb4, 0xf5, 0x0a, 0xd8, 0x5b, 0xa1, 0x52, 0xf6, 0x4a, 0x84, 0xe5, 0x87, 0x2b, - 0xbf, 0xdf, 0x87, 0x7f, 0x65, 0xa4, 0x85, 0xe7, 0x4f, 0xf1, 0xd7, 0xa2, 0x2e, 0x30, 0x3c, 0x83, 0xd6, 0x68, 0x05, - 0xc1, 0x04, 0xff, 0xe8, 0x40, 0xbd, 0xb4, 0xd2, 0x3e, 0x82, 0x6e, 0x05, 0x7a, 0x50, 0x0f, 0x7d, 0x9a, 0xb4, 0x2f, - 0x24, 0xea, 0xf6, 0x56, 0xa7, 0xd1, 0x1f, 0x15, 0x5c, 0x4e, 0x61, 0x72, 0xb8, 0xa1, 0x4f, 0xab, 0x70, 0xfb, 0x09, - 0x9e, 0xfe, 0x0c, 0x94, 0x5b, 0x87, 0x43, 0x0e, 0x62, 0x0b, 0xb8, 0x79, 0xac, 0xc2, 0xcf, 0x45, 0x29, 0x23, 0xea, - 0xe3, 0x69, 0x01, 0xda, 0xbb, 0x00, 0x1d, 0xb0, 0x34, 0x88, 0x57, 0x48, 0x9e, 0xb3, 0x11, 0xc0, 0xb2, 0x03, 0xcb, - 0x59, 0xc6, 0x29, 0xcc, 0xb3, 0xbc, 0x56, 0x2b, 0xed, 0xac, 0x4c, 0xbc, 0x9a, 0x65, 0xe0, 0x2c, 0x70, 0x51, 0xf9, - 0x2c, 0xd3, 0xaa, 0xa7, 0x2a, 0x41, 0x9f, 0x57, 0x72, 0x82, 0x2b, 0xc1, 0xc9, 0x06, 0xe4, 0x17, 0x20, 0x49, 0x53, - 0xca, 0x9a, 0xf2, 0xfa, 0x92, 0x6e, 0xc8, 0xe8, 0x39, 0xef, 0x79, 0xd1, 0x30, 0xf4, 0x2f, 0xbc, 0x12, 0xc2, 0x37, - 0x71, 0xdb, 0x46, 0x29, 0xec, 0x6f, 0x02, 0x8b, 0x4f, 0xd8, 0x0f, 0xde, 0xd2, 0x9f, 0x8e, 0x83, 0x70, 0x88, 0xdc, - 0x50, 0x31, 0x07, 0xf6, 0x34, 0x60, 0xb1, 0x89, 0xaf, 0x36, 0x93, 0x78, 0x30, 0xf0, 0x75, 0xc6, 0x62, 0x16, 0x03, - 0x0d, 0x72, 0x3c, 0xb8, 0x9c, 0xeb, 0x13, 0x42, 0x3f, 0x8c, 0xa8, 0x1c, 0x15, 0xe8, 0x1c, 0x44, 0x83, 0x25, 0xe0, - 0xa9, 0xb7, 0xb2, 0x41, 0x92, 0x31, 0xc9, 0x24, 0xae, 0x35, 0x49, 0x75, 0x38, 0xa1, 0x75, 0xa0, 0xe3, 0xea, 0x02, - 0x3a, 0x1f, 0xd7, 0xbd, 0x8f, 0x57, 0xc3, 0x05, 0x95, 0x7e, 0x21, 0x06, 0x5e, 0x3d, 0x1d, 0x07, 0x97, 0x74, 0x2b, - 0x5c, 0xac, 0xc2, 0xed, 0xcf, 0xf2, 0x81, 0xe3, 0x8e, 0x4a, 0x1a, 0x02, 0x83, 0xb7, 0x87, 0xee, 0x66, 0x86, 0x86, - 0x3a, 0x69, 0x1f, 0xc6, 0xa1, 0x1c, 0x62, 0xd5, 0x8a, 0x0b, 0xe9, 0x8d, 0xe0, 0xdb, 0x85, 0x62, 0x2c, 0x1b, 0xbb, - 0x34, 0x14, 0x85, 0xbf, 0x02, 0xd8, 0xa1, 0xf6, 0x57, 0x2a, 0xf9, 0x18, 0x19, 0xd5, 0x34, 0xd0, 0x31, 0x00, 0x4b, - 0x96, 0x26, 0x92, 0x2a, 0xd2, 0x48, 0xfc, 0x91, 0x19, 0xeb, 0xa8, 0xe9, 0xfa, 0x82, 0xa9, 0x6a, 0x91, 0x74, 0x3b, - 0x93, 0x58, 0x4e, 0x24, 0xa9, 0xed, 0x3e, 0x22, 0x06, 0x03, 0x1f, 0x6c, 0xc4, 0x34, 0x13, 0xe1, 0x88, 0x47, 0x25, - 0xb2, 0xe8, 0xf2, 0xdb, 0x28, 0x93, 0xb6, 0x2f, 0x2b, 0xb2, 0x05, 0xc1, 0xf4, 0x24, 0xfa, 0x20, 0x49, 0x39, 0x15, - 0x89, 0x34, 0x23, 0x04, 0xf8, 0xf1, 0xa4, 0xbc, 0xd2, 0x9f, 0x83, 0xa6, 0x95, 0xe0, 0x25, 0x83, 0xe4, 0x91, 0xf8, - 0x99, 0x14, 0xcc, 0x62, 0xac, 0x1a, 0x0c, 0xb0, 0x9c, 0xea, 0x99, 0x63, 0x92, 0xfe, 0x5b, 0xa7, 0x13, 0xf6, 0x0b, - 0x2f, 0xb7, 0xb5, 0xbc, 0x69, 0xee, 0xbd, 0xf0, 0x2a, 0x96, 0x6a, 0x58, 0x06, 0xfd, 0xd7, 0x44, 0xbb, 0x60, 0x6b, - 0xcb, 0x98, 0xb0, 0xea, 0x07, 0x90, 0xf6, 0x48, 0x97, 0x57, 0x0d, 0x73, 0x26, 0x78, 0x74, 0x61, 0xcd, 0x83, 0xe8, - 0x42, 0xf8, 0xc8, 0x65, 0x37, 0x49, 0xae, 0xc6, 0x13, 0x3f, 0x1c, 0x0c, 0x14, 0x00, 0x2d, 0xad, 0x93, 0x62, 0x10, - 0x3e, 0x13, 0x72, 0x20, 0x8d, 0x8e, 0xaa, 0x00, 0x8b, 0x65, 0x76, 0x55, 0x4e, 0xb2, 0xc1, 0xc0, 0x07, 0xb1, 0x31, - 0xb1, 0x1b, 0x9a, 0xcd, 0x7d, 0x76, 0xa2, 0x20, 0xab, 0xcd, 0x61, 0x6b, 0xa6, 0x5b, 0x60, 0x00, 0x30, 0x88, 0x08, - 0x96, 0xfb, 0xdc, 0xc8, 0x47, 0xd4, 0xe9, 0x29, 0x8c, 0x80, 0xe0, 0x97, 0x13, 0x81, 0xc8, 0x45, 0x02, 0xf5, 0x00, - 0x33, 0x01, 0x66, 0x54, 0x31, 0xbc, 0x04, 0x76, 0xf1, 0xdc, 0xbc, 0x62, 0xd0, 0xbf, 0x68, 0x92, 0x25, 0x9a, 0x4a, - 0x1c, 0x8d, 0x91, 0x53, 0x69, 0x8c, 0x0c, 0x88, 0x5d, 0x1c, 0xff, 0x9e, 0xd2, 0xa3, 0x20, 0x65, 0x9f, 0x2b, 0x43, - 0x1c, 0x8e, 0xe2, 0x2b, 0x58, 0x35, 0x0e, 0x87, 0xda, 0xbc, 0x9e, 0xce, 0xea, 0xf9, 0x40, 0x04, 0xf0, 0xdf, 0x50, - 0xb0, 0x5f, 0x34, 0x15, 0xb9, 0x41, 0xea, 0x3c, 0x1c, 0x52, 0x90, 0x4f, 0x75, 0x93, 0xbf, 0xaf, 0xdc, 0xfd, 0x74, - 0x36, 0xb7, 0xe6, 0xe8, 0x45, 0x8d, 0xeb, 0xd6, 0xea, 0x86, 0x42, 0xa2, 0x35, 0x4d, 0x8a, 0xab, 0x6a, 0x52, 0x0c, - 0x78, 0xee, 0x0b, 0xd5, 0xc5, 0xd6, 0x08, 0x16, 0xfe, 0xdc, 0x02, 0x61, 0x32, 0xee, 0xc5, 0x47, 0x0b, 0x39, 0xa5, - 0x5d, 0x5b, 0xed, 0xb6, 0x95, 0x0d, 0x29, 0x9a, 0x0f, 0x2f, 0x61, 0x97, 0x4e, 0x11, 0x6d, 0xbb, 0x24, 0xf8, 0x02, - 0xb4, 0xac, 0x2e, 0x44, 0x1e, 0xd3, 0xaf, 0x90, 0x5f, 0x8a, 0xe1, 0x5f, 0xa5, 0x7b, 0x73, 0x6a, 0x83, 0x1c, 0xc0, - 0x76, 0xef, 0xe1, 0x76, 0x8c, 0x1e, 0xc8, 0xe0, 0x8d, 0x90, 0x73, 0xce, 0x2f, 0xa7, 0xd6, 0x8c, 0x89, 0x86, 0x05, - 0x2b, 0x87, 0x91, 0x1f, 0x20, 0xe3, 0xe5, 0x14, 0x58, 0xd9, 0x8f, 0x8a, 0xb8, 0xf4, 0x87, 0x91, 0x7f, 0xf1, 0x3c, - 0xc8, 0xb8, 0x17, 0x0d, 0x3b, 0xbe, 0x00, 0x7b, 0xf5, 0xc5, 0x73, 0x16, 0x0d, 0x78, 0x75, 0x55, 0x4f, 0xb3, 0x60, - 0x98, 0xb1, 0xe8, 0xaa, 0x18, 0x82, 0x0f, 0xed, 0x75, 0x39, 0x08, 0x7d, 0xdf, 0xec, 0x1c, 0xba, 0x1b, 0x12, 0x79, - 0x84, 0xfd, 0x08, 0x6e, 0xbb, 0x5a, 0x62, 0x06, 0x93, 0xcd, 0x5d, 0xc4, 0x0c, 0xb6, 0xfc, 0xc5, 0x73, 0xc3, 0x25, - 0x54, 0x5d, 0x4b, 0xcd, 0x46, 0x81, 0xe6, 0xe4, 0x0a, 0xcd, 0xc9, 0x4a, 0xa8, 0x25, 0x9f, 0x54, 0x38, 0x61, 0xe7, - 0x93, 0x5c, 0xd9, 0x8d, 0xc6, 0x18, 0xb8, 0x68, 0xcf, 0x6d, 0x61, 0x64, 0xa6, 0xb3, 0x14, 0x0d, 0x58, 0x78, 0x26, - 0x4e, 0x69, 0x0c, 0x68, 0x5f, 0x0e, 0x2c, 0x6d, 0xc8, 0x8f, 0x72, 0x66, 0xa0, 0x6d, 0x48, 0x69, 0xd4, 0x0c, 0xfc, - 0x99, 0x9a, 0x30, 0xbf, 0x82, 0x95, 0x08, 0xa2, 0xba, 0x00, 0x93, 0x24, 0x27, 0xa3, 0x91, 0xb2, 0x12, 0xc9, 0x39, - 0xe0, 0x7d, 0x04, 0x4f, 0x16, 0xb1, 0xad, 0xfd, 0x29, 0xfd, 0xaf, 0x0e, 0x9f, 0x4b, 0xff, 0x99, 0x00, 0x16, 0x72, - 0x69, 0x10, 0x19, 0x28, 0x1c, 0x52, 0x53, 0x89, 0x38, 0x71, 0x3c, 0x03, 0x5f, 0xc3, 0x05, 0x9a, 0x02, 0xfa, 0x83, - 0x9a, 0x51, 0x44, 0x16, 0xfe, 0xea, 0xd9, 0x4d, 0xdd, 0xe8, 0x79, 0xe6, 0xbc, 0x06, 0xcd, 0x0c, 0x84, 0xf4, 0x38, - 0x55, 0x6f, 0x43, 0xa2, 0xf3, 0xf2, 0x52, 0xbf, 0x4c, 0x88, 0x64, 0x45, 0xe4, 0xe9, 0xfb, 0x1c, 0xcc, 0x23, 0x8a, - 0xd0, 0xc1, 0x95, 0x79, 0x38, 0x9c, 0x0b, 0x0a, 0xdf, 0x51, 0x9e, 0x0f, 0x38, 0xcd, 0xa2, 0x04, 0xb4, 0x81, 0x2c, - 0x37, 0x65, 0xae, 0x93, 0x96, 0xa9, 0x7b, 0x0f, 0x56, 0x82, 0x0a, 0xdd, 0x9c, 0x82, 0x42, 0x19, 0x09, 0x4a, 0x69, - 0x35, 0x08, 0xa5, 0x3a, 0x2c, 0x82, 0xc8, 0x21, 0x0b, 0x01, 0x37, 0x53, 0xd1, 0x68, 0x49, 0xc3, 0x23, 0x9c, 0x1b, - 0x28, 0x04, 0x20, 0xb1, 0xa7, 0x8a, 0x32, 0x2e, 0x87, 0x80, 0x8f, 0x12, 0x0e, 0x71, 0xd6, 0xa4, 0x2d, 0xcf, 0x41, - 0x1c, 0xcb, 0x25, 0x5f, 0x57, 0x08, 0x06, 0x11, 0xfa, 0x0c, 0xf9, 0x93, 0xe5, 0xfc, 0xbb, 0x75, 0x98, 0x76, 0x84, - 0x0f, 0xbb, 0xda, 0x82, 0x8b, 0xd9, 0xed, 0x7c, 0x02, 0xf1, 0x2d, 0xb7, 0xf3, 0x63, 0x0c, 0x91, 0x85, 0x3f, 0xb8, - 0x1b, 0x4a, 0xae, 0x28, 0x74, 0x59, 0x8f, 0x48, 0x91, 0x3d, 0x5d, 0x73, 0x04, 0xc1, 0x81, 0x56, 0x0d, 0x32, 0x34, - 0x12, 0x5f, 0x3c, 0x87, 0xac, 0xc1, 0x9a, 0x7f, 0xae, 0xc8, 0x59, 0xdd, 0x9f, 0x6c, 0xa0, 0x9a, 0x64, 0xb2, 0x56, - 0x54, 0xce, 0x5f, 0xaf, 0xca, 0xf2, 0x64, 0x55, 0x86, 0xab, 0x41, 0x57, 0x55, 0x96, 0x1c, 0xa9, 0x0d, 0xd0, 0x9a, - 0xae, 0x10, 0x43, 0x21, 0x6b, 0xb0, 0xb4, 0xaa, 0xb2, 0xa6, 0x3e, 0x81, 0x40, 0x1f, 0x60, 0x19, 0x35, 0xfb, 0xe9, - 0xf0, 0x5f, 0xc1, 0xbf, 0x54, 0xc8, 0x52, 0x9d, 0xd6, 0x99, 0xf8, 0x35, 0x58, 0x32, 0xfc, 0xe3, 0xb7, 0x60, 0x0d, - 0x58, 0x02, 0x64, 0xb9, 0xdb, 0xd8, 0x68, 0xbd, 0xf2, 0x0a, 0xf1, 0xa5, 0xd6, 0x17, 0xfd, 0xd6, 0x6d, 0xa2, 0x56, - 0x80, 0x11, 0x0a, 0x2d, 0x02, 0x6c, 0xf5, 0xc0, 0x3d, 0x05, 0x3f, 0x10, 0xc3, 0xb9, 0x26, 0xad, 0xa9, 0x13, 0x5e, - 0x67, 0xe3, 0x48, 0x44, 0xf5, 0x16, 0x2e, 0xee, 0xf5, 0xd6, 0xe2, 0x6f, 0x54, 0x20, 0x00, 0xb2, 0x98, 0x62, 0xed, - 0xbc, 0x21, 0xbd, 0x32, 0xec, 0x24, 0xf4, 0xde, 0xb0, 0x13, 0xc8, 0x8b, 0xc3, 0x4e, 0xa1, 0x4b, 0xb4, 0x9d, 0x22, - 0x35, 0xd1, 0x76, 0xd2, 0x62, 0x15, 0x96, 0x10, 0xfc, 0xaa, 0xbd, 0x75, 0x94, 0xed, 0x8b, 0x2c, 0x61, 0xda, 0x02, - 0x46, 0xb9, 0x55, 0x9f, 0x39, 0x45, 0xac, 0x94, 0xbd, 0xd3, 0x49, 0x95, 0xbb, 0xc8, 0xa7, 0x56, 0x53, 0x64, 0xf2, - 0xf7, 0xc7, 0x2d, 0x92, 0x4f, 0x7e, 0x6e, 0x37, 0x4c, 0xa6, 0x7f, 0x3c, 0xfa, 0x02, 0xba, 0x22, 0x3b, 0x7d, 0x02, - 0x01, 0x99, 0x0a, 0xaa, 0xd5, 0xad, 0x62, 0x9a, 0xb7, 0xab, 0xec, 0xf6, 0x42, 0x89, 0xe1, 0x74, 0x76, 0x12, 0x1e, - 0x6d, 0x86, 0x0c, 0x1c, 0x82, 0x40, 0x21, 0x54, 0x14, 0xc3, 0x23, 0x50, 0x6b, 0x24, 0x1f, 0xe0, 0x47, 0xbb, 0x53, - 0x41, 0xa4, 0x76, 0x53, 0x71, 0xe3, 0xe4, 0xa6, 0xeb, 0xa5, 0x40, 0xad, 0x53, 0xb2, 0x02, 0x28, 0x21, 0xea, 0x4f, - 0x62, 0x5b, 0xbf, 0x84, 0x2b, 0x36, 0xdf, 0x37, 0x8a, 0x9e, 0x5c, 0x9f, 0xa2, 0x6e, 0xc5, 0xd5, 0x69, 0xda, 0x6a, - 0x8e, 0x1d, 0x67, 0xc8, 0xc1, 0xb3, 0x82, 0x60, 0x3b, 0x2a, 0x51, 0xbe, 0x6d, 0x37, 0x1d, 0x13, 0x5b, 0xfd, 0xb3, - 0xa8, 0x36, 0x77, 0x50, 0x11, 0x11, 0x1f, 0x65, 0x37, 0x4f, 0xda, 0xef, 0x60, 0x8f, 0xb5, 0x1a, 0x44, 0xf6, 0x19, - 0x5c, 0xe5, 0x3a, 0x2d, 0x72, 0x5b, 0x06, 0xe7, 0x1f, 0x5e, 0xed, 0x2a, 0x6c, 0x72, 0xac, 0xab, 0xab, 0x99, 0xea, - 0xa4, 0x62, 0x03, 0x63, 0x4d, 0x6b, 0xa9, 0xe6, 0x31, 0x24, 0xdd, 0x95, 0xc5, 0x59, 0x95, 0x74, 0xd3, 0x73, 0xe3, - 0x4c, 0x21, 0x06, 0xce, 0x56, 0xa3, 0xe5, 0x0c, 0x43, 0x74, 0x7d, 0x98, 0x25, 0x7e, 0xab, 0xa7, 0xdc, 0xe7, 0xe1, - 0xd6, 0xef, 0xea, 0x05, 0x27, 0x93, 0xfd, 0xe4, 0x38, 0x77, 0xbb, 0x48, 0xfb, 0x89, 0x6f, 0xc3, 0xfc, 0xeb, 0x1b, - 0xc4, 0x9d, 0xa8, 0xff, 0x59, 0x01, 0xd0, 0xe0, 0x26, 0x8f, 0x25, 0x4a, 0xfd, 0x5e, 0x55, 0x3f, 0xa8, 0x99, 0xaa, - 0x69, 0x20, 0x98, 0x53, 0x29, 0xe0, 0x0f, 0xb7, 0x0b, 0x57, 0x3c, 0xe2, 0x86, 0x85, 0xf1, 0x4f, 0xaf, 0x66, 0xa7, - 0x82, 0xca, 0xc0, 0xcd, 0xf8, 0x4f, 0x4f, 0xb0, 0x53, 0x58, 0x2b, 0x20, 0x2b, 0xfc, 0xe9, 0xe5, 0x8f, 0xbc, 0x5f, - 0xf1, 0x3f, 0xbd, 0xea, 0x91, 0xf7, 0x11, 0xe7, 0xe5, 0x4f, 0x24, 0x75, 0x42, 0x54, 0x97, 0x3f, 0x09, 0x53, 0x6c, - 0x95, 0xe6, 0xaf, 0x48, 0xe1, 0x13, 0x7c, 0x06, 0xbe, 0xc3, 0x55, 0xb8, 0x35, 0xbf, 0xc1, 0x63, 0xc7, 0x62, 0xdb, - 0xa5, 0xbe, 0x80, 0x72, 0x04, 0x16, 0x91, 0xdb, 0x6f, 0x57, 0xf6, 0xab, 0x85, 0x51, 0xc6, 0xd8, 0x7d, 0xc9, 0x4a, - 0x94, 0xce, 0xfa, 0xfd, 0x42, 0x0a, 0x46, 0x76, 0x61, 0x8d, 0xf6, 0x28, 0x55, 0xaf, 0xbe, 0x0d, 0xeb, 0x28, 0x49, - 0xf3, 0x3b, 0x19, 0x7d, 0x24, 0xc3, 0x8e, 0xf4, 0x95, 0x94, 0x68, 0xaf, 0x55, 0x58, 0x8e, 0x66, 0xbf, 0x2e, 0x39, - 0x50, 0x5e, 0xb7, 0x82, 0xf2, 0x55, 0x13, 0x40, 0xaf, 0x54, 0xfb, 0x0c, 0xb4, 0x82, 0xc2, 0x52, 0x79, 0xb0, 0x12, - 0xe7, 0xa2, 0xcf, 0x8a, 0xc3, 0x41, 0x5d, 0x0c, 0x09, 0x05, 0xaa, 0xc4, 0x49, 0x68, 0xc4, 0x73, 0xb8, 0x10, 0x8a, - 0xeb, 0x1c, 0x63, 0x2b, 0x72, 0xe0, 0x40, 0x86, 0x1f, 0x10, 0x78, 0x2f, 0xfb, 0x57, 0x30, 0x18, 0x26, 0xb8, 0x91, - 0x51, 0x27, 0xe7, 0xec, 0x4f, 0x0c, 0xcc, 0xa0, 0x9e, 0xd4, 0xee, 0xb3, 0x7b, 0x15, 0xd8, 0x0b, 0x67, 0x40, 0x7b, - 0x37, 0x46, 0x3f, 0xab, 0x62, 0xed, 0xa4, 0x7f, 0x2a, 0xd6, 0x90, 0x4c, 0x87, 0xc5, 0xd1, 0x36, 0x0d, 0x8f, 0xe4, - 0xc9, 0x71, 0xbc, 0xe9, 0x1f, 0x0e, 0x63, 0xfc, 0x38, 0xca, 0xaf, 0x2d, 0xe0, 0x55, 0xdc, 0x42, 0x1a, 0x8b, 0x14, - 0xbd, 0x03, 0x31, 0x87, 0xa2, 0x97, 0xec, 0xb7, 0x8c, 0x97, 0x13, 0x41, 0x29, 0x49, 0x6c, 0x78, 0x47, 0x7a, 0x9a, - 0xd6, 0xa3, 0xad, 0x0c, 0xd8, 0xaf, 0x47, 0x3b, 0xfa, 0x0b, 0x14, 0x8f, 0x16, 0xfe, 0x92, 0xfe, 0x2e, 0xee, 0xe6, - 0x9e, 0xf3, 0x4d, 0xe3, 0x3b, 0xe2, 0x02, 0xc5, 0x9a, 0xdd, 0x5f, 0xd3, 0xd2, 0x59, 0x07, 0x82, 0x03, 0xde, 0x62, - 0x17, 0xed, 0xfb, 0x8d, 0xeb, 0xf4, 0xb4, 0xff, 0xd6, 0xad, 0x51, 0xbe, 0xf7, 0x4f, 0x89, 0x72, 0xb0, 0x7f, 0xe5, - 0xa2, 0xf9, 0xdb, 0x4f, 0x19, 0x92, 0x0a, 0xcd, 0x0d, 0xb6, 0x93, 0x2d, 0xc2, 0xda, 0x18, 0x07, 0x15, 0xbb, 0x2b, - 0xc3, 0x08, 0x18, 0xd4, 0xb1, 0xff, 0xd1, 0x67, 0xd3, 0x86, 0xec, 0x03, 0x40, 0xe5, 0x2a, 0x04, 0xec, 0x01, 0x38, - 0xd1, 0x08, 0x37, 0xc0, 0xad, 0x46, 0x4b, 0x3a, 0xa8, 0xdb, 0x82, 0x81, 0x68, 0x09, 0x1b, 0x79, 0xdb, 0xd5, 0xe9, - 0x2b, 0xc2, 0x87, 0xda, 0x49, 0xe9, 0x50, 0xfe, 0xea, 0x39, 0xfb, 0x9f, 0x1d, 0xd6, 0xd4, 0x94, 0x1b, 0xc0, 0xcc, - 0x59, 0x89, 0xbc, 0x42, 0xe8, 0x14, 0xf9, 0xbd, 0xaa, 0x2b, 0x31, 0x5c, 0xd6, 0xa2, 0xec, 0xcc, 0x6e, 0x9d, 0xe8, - 0x9d, 0x53, 0x50, 0x4b, 0x65, 0x83, 0x9c, 0xa4, 0xda, 0x7c, 0x64, 0xad, 0xa0, 0x44, 0x5d, 0xa3, 0xc0, 0xf1, 0x29, - 0xd7, 0xee, 0xff, 0x9d, 0x33, 0x41, 0xcd, 0x36, 0xaa, 0xfb, 0x2b, 0xfd, 0x54, 0xd5, 0x24, 0x16, 0xe0, 0x72, 0x92, - 0xe6, 0x1d, 0x8f, 0xb0, 0xfa, 0xc7, 0xc9, 0x52, 0x04, 0x7a, 0x15, 0xd1, 0xae, 0x04, 0x24, 0x68, 0x27, 0x67, 0xa1, - 0x22, 0x50, 0xa0, 0xaf, 0x7f, 0xbf, 0x49, 0xb3, 0x58, 0xae, 0x66, 0x7b, 0x98, 0x28, 0x8b, 0xf5, 0x10, 0x41, 0xce, - 0x4c, 0x1d, 0xec, 0xf7, 0x34, 0xa3, 0x59, 0x78, 0x65, 0x4a, 0x70, 0x29, 0xae, 0xa2, 0x22, 0x07, 0x9f, 0x43, 0x7c, - 0xe1, 0x53, 0x21, 0x37, 0x88, 0x68, 0xfa, 0xbd, 0x44, 0xb5, 0x23, 0x05, 0x72, 0x28, 0xf9, 0x09, 0xf1, 0x97, 0xac, - 0x8d, 0x71, 0xbf, 0x74, 0xaa, 0xfd, 0x52, 0x21, 0xb8, 0xff, 0x6c, 0x8b, 0x8d, 0x2a, 0x4f, 0xf4, 0xe8, 0x53, 0xac, - 0xff, 0xc9, 0x02, 0x4a, 0x75, 0xdf, 0x06, 0xa7, 0xe2, 0x51, 0xb8, 0xa9, 0x8b, 0x1b, 0x84, 0x16, 0x28, 0x47, 0x55, - 0xb1, 0x29, 0x23, 0xe2, 0x84, 0xdd, 0xd4, 0x45, 0x4f, 0x73, 0xa0, 0x53, 0x87, 0xa5, 0x89, 0x3c, 0x11, 0xda, 0x2d, - 0xe8, 0x9e, 0xe6, 0x58, 0x89, 0x17, 0xb2, 0x74, 0x90, 0x75, 0x22, 0x4d, 0xa8, 0xdc, 0xd5, 0x55, 0x47, 0xa5, 0x52, - 0x37, 0xbc, 0x4e, 0x35, 0xe3, 0xef, 0xd2, 0xfc, 0x89, 0x65, 0xbf, 0x6e, 0xfd, 0x56, 0xab, 0xbd, 0xb1, 0x7a, 0x54, - 0xb2, 0xe6, 0x38, 0x9b, 0x90, 0x94, 0x3e, 0x61, 0xbb, 0x99, 0x74, 0xad, 0x03, 0x4f, 0x82, 0xcb, 0xa1, 0x27, 0xa0, - 0x62, 0xd0, 0xc4, 0xdb, 0x5d, 0xa0, 0x1e, 0x81, 0x67, 0xa0, 0x7c, 0xa2, 0xd6, 0x01, 0x3f, 0xaf, 0xb5, 0x3c, 0x65, - 0x84, 0x61, 0xb5, 0xb3, 0x68, 0x39, 0x38, 0xef, 0x14, 0x81, 0x6b, 0x57, 0x02, 0xcf, 0x87, 0xea, 0xbd, 0x10, 0x30, - 0xdc, 0x3f, 0x15, 0x2a, 0x9b, 0xdd, 0x0c, 0xe7, 0x51, 0xe3, 0xf4, 0x40, 0x7b, 0xdb, 0xb5, 0x1e, 0xea, 0x5d, 0xb7, - 0x73, 0x5b, 0xe9, 0xde, 0xaf, 0x9d, 0x4c, 0xba, 0x80, 0xd6, 0xe6, 0xb3, 0xef, 0xec, 0x4a, 0xeb, 0xa6, 0xe7, 0xec, - 0xc1, 0xd6, 0x2d, 0xd1, 0xb9, 0x20, 0x9a, 0xfc, 0x7e, 0xe0, 0x59, 0xdb, 0x8e, 0x7e, 0x9b, 0x76, 0x6c, 0x73, 0x0f, - 0x75, 0xaf, 0xa0, 0xd6, 0x1b, 0x9a, 0xf7, 0xcf, 0x5c, 0xdb, 0x8e, 0xaf, 0x7e, 0x5d, 0x77, 0xb8, 0xce, 0x9b, 0xe0, - 0xb8, 0xe9, 0xda, 0x56, 0x3b, 0xfb, 0xb9, 0xbb, 0xb7, 0x16, 0x51, 0x98, 0x65, 0x3f, 0x16, 0xc5, 0x1f, 0x95, 0xbe, - 0x23, 0xd0, 0xd1, 0x9d, 0x17, 0x75, 0xba, 0xdc, 0x7d, 0x20, 0x8c, 0x27, 0xaf, 0x3e, 0x22, 0xba, 0xf5, 0x7d, 0xe6, - 0x7e, 0x05, 0xb8, 0x11, 0xdc, 0x41, 0xb4, 0x77, 0x4b, 0x7d, 0x52, 0xab, 0xaf, 0xf5, 0xda, 0x79, 0x7a, 0x7e, 0xd3, - 0xb9, 0xfd, 0xee, 0x9b, 0xa3, 0xad, 0xf7, 0xb8, 0xb0, 0x56, 0x96, 0x9e, 0xaa, 0x82, 0xbd, 0x59, 0x9e, 0xaa, 0x82, - 0xc9, 0x03, 0xaf, 0xd9, 0x2f, 0x68, 0x70, 0xa5, 0xa3, 0x8d, 0xf7, 0x44, 0x0d, 0xdc, 0xa2, 0xb0, 0x74, 0xf8, 0x25, - 0x37, 0x93, 0x97, 0xb8, 0xbf, 0x54, 0xe4, 0x62, 0xdf, 0x39, 0xa3, 0x3b, 0x33, 0xeb, 0x5e, 0x55, 0xb8, 0x5a, 0x90, - 0xab, 0x03, 0x5b, 0xcb, 0x2e, 0x0e, 0x37, 0x2c, 0xa2, 0x00, 0x81, 0x98, 0x5e, 0xa9, 0xb5, 0x3f, 0xa2, 0x41, 0xc8, - 0x07, 0x03, 0xbf, 0xc0, 0x60, 0x55, 0xa0, 0xf0, 0x81, 0x22, 0xf9, 0x2b, 0x4f, 0xc0, 0x2e, 0x9e, 0x01, 0xba, 0x15, - 0x9b, 0x15, 0x23, 0x44, 0xc8, 0x64, 0x39, 0xab, 0xe9, 0x0c, 0xf2, 0xa9, 0x2f, 0xbe, 0xb1, 0x55, 0xa7, 0xf3, 0xb6, - 0xa6, 0xca, 0xa9, 0x43, 0xa1, 0xbb, 0x9b, 0xba, 0x73, 0xeb, 0x22, 0x4f, 0x1d, 0x42, 0xae, 0x54, 0xac, 0xc4, 0x34, - 0xd4, 0x3c, 0x49, 0x33, 0xea, 0x2f, 0xf6, 0x7e, 0xaf, 0x51, 0x38, 0xe5, 0x4f, 0xc7, 0xa0, 0x0a, 0x57, 0x35, 0xc4, - 0xb1, 0x54, 0xc5, 0x23, 0x1b, 0x04, 0x9a, 0x57, 0xb7, 0x2a, 0x69, 0x42, 0x26, 0x37, 0xc2, 0xa7, 0x26, 0xa5, 0x3c, - 0x4d, 0x9b, 0xb4, 0x52, 0xa4, 0x0e, 0x3e, 0xa8, 0x53, 0x8d, 0xe7, 0x66, 0x75, 0x0d, 0x60, 0xc6, 0xf9, 0x15, 0xbf, - 0x54, 0x5c, 0x46, 0x6d, 0x65, 0x26, 0xed, 0x4f, 0x8e, 0xc6, 0x46, 0x5d, 0x4e, 0x1b, 0x65, 0x84, 0x95, 0xd2, 0x9c, - 0x14, 0xcb, 0xf1, 0xfc, 0x03, 0x06, 0x6b, 0x9e, 0xc0, 0x0e, 0x26, 0x2a, 0xe5, 0x7d, 0x04, 0xc4, 0xd7, 0x49, 0x7a, - 0x97, 0x40, 0x8a, 0xf4, 0x2f, 0x5d, 0x72, 0x97, 0xb1, 0x81, 0x18, 0xb3, 0x62, 0x66, 0xf4, 0x3f, 0xb8, 0x4b, 0xfa, - 0x93, 0x10, 0x00, 0x37, 0xd1, 0x14, 0x3a, 0x75, 0x9e, 0x5c, 0xe4, 0xc1, 0xf2, 0xc2, 0x43, 0x2b, 0x46, 0x3c, 0xf8, - 0xeb, 0x75, 0x88, 0x20, 0xe6, 0x98, 0xe2, 0xe9, 0x17, 0x46, 0x7f, 0x09, 0x2e, 0x31, 0x82, 0xd0, 0xdd, 0x3b, 0x87, - 0x21, 0xdc, 0xec, 0x41, 0x06, 0xf5, 0x87, 0x3a, 0x24, 0x6a, 0xf8, 0x63, 0xe5, 0x41, 0xff, 0xd7, 0x99, 0xb0, 0xd4, - 0x7e, 0x7a, 0x3a, 0x80, 0x0a, 0xde, 0x57, 0xbc, 0x8d, 0x88, 0xef, 0x13, 0x3f, 0x8b, 0x07, 0x9b, 0x67, 0x1b, 0xb0, - 0xd6, 0x3d, 0xc9, 0x8d, 0x75, 0x95, 0xb0, 0x81, 0x80, 0xaf, 0x51, 0xd4, 0x9e, 0xd7, 0x6e, 0xf7, 0xe0, 0xaf, 0xfe, - 0x45, 0xc8, 0x80, 0x89, 0xd3, 0xf7, 0x99, 0x93, 0x35, 0xba, 0xc8, 0x64, 0xfa, 0xd0, 0x49, 0xdf, 0xe8, 0x74, 0xdf, - 0x09, 0xff, 0xa8, 0x98, 0xc5, 0x87, 0x5b, 0xfa, 0x4a, 0x93, 0xe2, 0x0e, 0x58, 0xd9, 0x3c, 0x2a, 0x08, 0x75, 0x2e, - 0xa2, 0xaf, 0x4c, 0xf9, 0x96, 0x50, 0xb3, 0x6f, 0x2c, 0x29, 0xa5, 0x7b, 0x0d, 0xbd, 0x4e, 0x6b, 0xfd, 0x36, 0x4a, - 0x30, 0x26, 0x3a, 0x9e, 0xbc, 0x8c, 0xc7, 0xca, 0xfb, 0x78, 0xdc, 0x48, 0x85, 0x3c, 0x00, 0x11, 0xa8, 0x18, 0x7f, - 0xba, 0xf2, 0xe4, 0xa4, 0x17, 0xc6, 0xab, 0x50, 0x0a, 0x0a, 0x03, 0xba, 0x02, 0x29, 0xe0, 0x51, 0x7b, 0xa2, 0xb3, - 0xb0, 0x4b, 0xb8, 0x47, 0x37, 0x01, 0x63, 0x7d, 0xfe, 0x11, 0xd0, 0xdc, 0x85, 0x3b, 0xbc, 0x18, 0xa0, 0x36, 0xf5, - 0xea, 0xee, 0xe3, 0x5a, 0x9d, 0xc3, 0x21, 0x38, 0x58, 0x0d, 0x22, 0x38, 0x9d, 0x4f, 0x1d, 0xcd, 0xb2, 0x00, 0x95, - 0x93, 0xe5, 0x46, 0xde, 0x3c, 0x5a, 0xf4, 0xea, 0xbe, 0xb7, 0x4c, 0xcb, 0xaa, 0x0e, 0x32, 0x96, 0x85, 0x15, 0xe0, - 0xea, 0xd0, 0xfa, 0x41, 0xb8, 0x2c, 0x9c, 0x3f, 0x10, 0x82, 0xd8, 0xbd, 0xda, 0x96, 0x3c, 0x57, 0x73, 0xf8, 0xd9, - 0x73, 0xb6, 0xe6, 0x12, 0x75, 0xd2, 0x99, 0x08, 0x40, 0xec, 0xa9, 0x59, 0x45, 0xd7, 0x40, 0x52, 0xa7, 0x59, 0x45, - 0xd7, 0xd4, 0x6c, 0x63, 0x1c, 0xc8, 0x47, 0xab, 0x14, 0xb0, 0xef, 0xa6, 0xe3, 0x60, 0xf5, 0x2c, 0x96, 0xd7, 0xa1, - 0xbb, 0x67, 0x1b, 0xe5, 0x33, 0xa8, 0x5b, 0x6d, 0x8c, 0x89, 0xed, 0xe6, 0xcb, 0xb9, 0x7e, 0x3b, 0x58, 0xfa, 0x76, - 0xd0, 0x9c, 0x53, 0xf6, 0x9d, 0x2e, 0x7b, 0x65, 0x97, 0x4d, 0x3d, 0x77, 0x54, 0xb4, 0x1a, 0x03, 0x7a, 0x03, 0x0b, - 0xd6, 0xe7, 0x22, 0xcd, 0x56, 0xa5, 0x2a, 0x01, 0x2f, 0x8c, 0x15, 0xbb, 0xf3, 0x1b, 0x99, 0x21, 0x09, 0xf3, 0x38, - 0x13, 0x6f, 0xe9, 0x5e, 0x0b, 0x93, 0xe3, 0x58, 0x24, 0x53, 0x42, 0xa7, 0x74, 0x67, 0x1b, 0x3a, 0x57, 0x61, 0x14, - 0xd1, 0x5a, 0x49, 0xa5, 0x91, 0xc0, 0xd4, 0x0c, 0x50, 0x32, 0x57, 0xe0, 0x94, 0x2e, 0xf7, 0xbf, 0x23, 0x31, 0xce, - 0x7c, 0x51, 0x32, 0x03, 0xba, 0xe5, 0xd7, 0xc5, 0xba, 0x95, 0x22, 0x23, 0xcc, 0x9b, 0xe3, 0xf6, 0xba, 0x3e, 0x04, - 0x72, 0xb5, 0xec, 0x51, 0x34, 0x0e, 0x0a, 0x1d, 0x2e, 0x55, 0x02, 0xec, 0x8b, 0xc4, 0xcf, 0x08, 0x5b, 0xda, 0x03, - 0xb9, 0x3d, 0x3a, 0x13, 0xe6, 0x9c, 0x93, 0xb2, 0xec, 0x5c, 0x9a, 0xc1, 0xe5, 0xc4, 0x95, 0xe0, 0x22, 0xbd, 0x6d, - 0x4f, 0x93, 0x96, 0xb6, 0x8f, 0x0d, 0xe7, 0x68, 0x68, 0x1b, 0x74, 0xc7, 0xfe, 0xd0, 0x5c, 0x2c, 0x62, 0xeb, 0x62, - 0x31, 0xec, 0xcc, 0x7e, 0xb4, 0x58, 0x80, 0x1c, 0x00, 0x8e, 0xba, 0x0d, 0x1f, 0xb3, 0x25, 0x70, 0x5a, 0x4d, 0xb3, - 0xa9, 0xb7, 0xe1, 0xd5, 0x33, 0xd5, 0xd3, 0x4b, 0x9e, 0x3f, 0x13, 0x66, 0x2c, 0x36, 0x3c, 0x7f, 0x66, 0x1d, 0x39, - 0xd5, 0x33, 0xa1, 0x44, 0xeb, 0x02, 0x9a, 0x81, 0xd7, 0x14, 0x30, 0x62, 0xc9, 0x64, 0x4a, 0x15, 0x79, 0xdc, 0x9b, - 0x6e, 0xd4, 0xe0, 0x05, 0x85, 0x43, 0x20, 0xa5, 0xd3, 0x2f, 0x9e, 0x33, 0xfd, 0xde, 0xc5, 0xf3, 0x0e, 0x59, 0xdb, - 0x30, 0x5d, 0x6e, 0x86, 0xc9, 0xa0, 0xf4, 0x9f, 0x99, 0x89, 0x71, 0x61, 0x4d, 0x12, 0x40, 0xfc, 0x1b, 0xfb, 0x1d, - 0x52, 0xb8, 0x79, 0x7f, 0x39, 0x8c, 0x1f, 0x79, 0x3f, 0x46, 0xf6, 0x24, 0xcd, 0x10, 0x6b, 0x26, 0x15, 0x72, 0xf7, - 0xd5, 0xfa, 0xc7, 0xc4, 0x6e, 0xb2, 0x07, 0x16, 0x80, 0xd8, 0x9a, 0xb6, 0xba, 0xe5, 0xfd, 0xbe, 0x67, 0x8a, 0x00, - 0x3f, 0x28, 0xff, 0xe8, 0xce, 0x90, 0x0c, 0xca, 0xae, 0x1b, 0x42, 0x3c, 0x28, 0x9b, 0xa6, 0xbd, 0xde, 0xf6, 0xce, - 0x3c, 0x56, 0xd7, 0x69, 0x67, 0x71, 0xb5, 0xc8, 0x20, 0xad, 0x3e, 0x64, 0xc7, 0x99, 0x7d, 0x76, 0xb4, 0x54, 0xba, - 0xdf, 0x87, 0x88, 0xb8, 0xa3, 0xac, 0xed, 0xb7, 0x5b, 0x70, 0x0d, 0x47, 0x83, 0xd0, 0x95, 0xbd, 0x5d, 0x46, 0x1b, - 0x17, 0xe2, 0xb8, 0x67, 0x3a, 0x5f, 0xf0, 0xe5, 0x51, 0xda, 0x79, 0x70, 0xaa, 0x27, 0xfa, 0xdc, 0x74, 0x57, 0x99, - 0x5c, 0xeb, 0xb0, 0x1a, 0x83, 0xda, 0x2c, 0x6c, 0xe1, 0x2e, 0x6c, 0xa3, 0x83, 0xd6, 0xbe, 0x2c, 0xf8, 0xa7, 0x0c, - 0xc0, 0x97, 0x9e, 0x2d, 0xdb, 0x5e, 0x93, 0x56, 0xaf, 0x65, 0x14, 0x62, 0x4b, 0xdb, 0xab, 0x4f, 0x47, 0xf9, 0xb8, - 0x39, 0xa1, 0xb8, 0x90, 0xa3, 0xfc, 0xe8, 0x35, 0x44, 0x5d, 0xeb, 0x3a, 0x2e, 0x16, 0x1d, 0x6e, 0x5c, 0x75, 0xdb, - 0x8d, 0xeb, 0x07, 0xc4, 0x5b, 0xa3, 0x4d, 0x0a, 0xb5, 0x32, 0x76, 0x04, 0x2f, 0xcb, 0x87, 0x43, 0x26, 0x86, 0x43, - 0x09, 0x99, 0xfa, 0xd8, 0xbd, 0xa1, 0x69, 0x9f, 0x9f, 0xb6, 0x7e, 0xc4, 0x52, 0xe3, 0x28, 0x36, 0xbc, 0xd3, 0x77, - 0x1e, 0x5b, 0xe3, 0x4a, 0xbe, 0x0c, 0x66, 0xbb, 0x82, 0x6a, 0x6b, 0xbc, 0x61, 0x2f, 0xe7, 0xdf, 0x57, 0x52, 0xc9, - 0xdf, 0xfe, 0x0c, 0xd7, 0xf0, 0xd6, 0x96, 0x0e, 0x9a, 0x6a, 0x96, 0xb3, 0x5c, 0xdf, 0x0b, 0x8e, 0x3f, 0xee, 0x5e, - 0x11, 0x0c, 0x7e, 0x4f, 0x47, 0x41, 0x2e, 0x96, 0x6a, 0x0d, 0x28, 0x48, 0x47, 0x76, 0x4c, 0x65, 0x81, 0x61, 0x00, - 0x6f, 0xc8, 0x00, 0x79, 0x4c, 0xe1, 0x6e, 0xa8, 0xf0, 0xc2, 0x5f, 0x2a, 0xb2, 0x4b, 0x60, 0x5b, 0x33, 0x3e, 0x66, - 0xb8, 0x83, 0x90, 0x7f, 0x04, 0xbb, 0x63, 0x2b, 0x76, 0xcb, 0x16, 0x0c, 0xc9, 0xc6, 0x71, 0x18, 0x63, 0x3e, 0x9e, - 0xc4, 0x57, 0x62, 0x12, 0x0f, 0x78, 0x84, 0x8e, 0x11, 0x6b, 0x5e, 0xcf, 0x62, 0x39, 0x80, 0xec, 0x8e, 0x2b, 0x1d, - 0x10, 0x42, 0x63, 0x43, 0x4b, 0x5e, 0x17, 0x06, 0x17, 0x3b, 0xf6, 0x19, 0x89, 0x64, 0x1c, 0x82, 0x45, 0xab, 0x1a, - 0x58, 0x98, 0xd8, 0x2d, 0x2f, 0x66, 0xab, 0x39, 0xfe, 0x73, 0x38, 0x20, 0x00, 0x76, 0xb0, 0x6f, 0xd8, 0x5d, 0x84, - 0x48, 0x6f, 0x0b, 0x7e, 0x67, 0x79, 0xba, 0xb0, 0x7b, 0xfe, 0x96, 0x8f, 0xd9, 0xf9, 0x0f, 0x1e, 0x44, 0xce, 0x9e, - 0x7f, 0x04, 0x34, 0xc4, 0x7b, 0x7e, 0x9b, 0x7a, 0x15, 0xbb, 0x25, 0x0a, 0xc2, 0x5b, 0x70, 0x06, 0xba, 0x87, 0x08, - 0xd8, 0xb7, 0x7c, 0x81, 0xb1, 0x62, 0x67, 0xe9, 0xd2, 0xc3, 0x8c, 0x50, 0x7b, 0x3a, 0x5f, 0xd6, 0x6a, 0x12, 0x6e, - 0xae, 0x96, 0x93, 0xc1, 0x60, 0xe3, 0xef, 0xf8, 0x1a, 0xf8, 0x60, 0xce, 0x7f, 0xf0, 0x76, 0x54, 0x2e, 0xfc, 0xe7, - 0x75, 0x96, 0xbc, 0xf3, 0xd9, 0xdb, 0x01, 0x5f, 0x00, 0xde, 0x12, 0x3a, 0x70, 0xdd, 0xfb, 0x4c, 0xe2, 0xb5, 0xbd, - 0xd5, 0xd7, 0x08, 0x24, 0xf2, 0x05, 0x60, 0xc4, 0xc4, 0xfc, 0x7e, 0x0b, 0x11, 0x18, 0x09, 0xf8, 0xb6, 0x6a, 0x8f, - 0xf8, 0x2d, 0x37, 0x80, 0x5f, 0x99, 0xcf, 0x1e, 0x78, 0xa8, 0x7f, 0x26, 0x3e, 0xbb, 0xe1, 0xef, 0xf9, 0xb5, 0x27, - 0x25, 0xe9, 0x72, 0xf6, 0x7e, 0x0e, 0xd7, 0x43, 0x29, 0x4f, 0x87, 0xf4, 0xb3, 0x31, 0x18, 0x40, 0x28, 0x64, 0xde, - 0x78, 0xc0, 0x9a, 0x14, 0xe2, 0x5f, 0xc0, 0xb7, 0xa3, 0x84, 0xcd, 0x1b, 0x6f, 0xeb, 0x6b, 0x79, 0xf3, 0xc6, 0x7b, - 0xf0, 0x29, 0x0a, 0xb0, 0x0a, 0x4a, 0x59, 0x60, 0x15, 0x84, 0x8d, 0x36, 0xc2, 0x18, 0xb8, 0x7a, 0xd7, 0x18, 0xea, - 0x7a, 0x8e, 0xd8, 0xb6, 0xd2, 0x77, 0xe1, 0x3b, 0xc8, 0x80, 0x0f, 0x5e, 0x17, 0x25, 0xd1, 0xe7, 0xd4, 0x14, 0x49, - 0xeb, 0x9e, 0xfb, 0xad, 0x75, 0x47, 0x6b, 0x4a, 0x7d, 0xe4, 0x6a, 0x7c, 0x38, 0xd4, 0xd7, 0x42, 0x8b, 0x04, 0x53, - 0xd0, 0xb8, 0x06, 0x6d, 0x01, 0x82, 0x3e, 0x0f, 0x90, 0xb5, 0xa4, 0x58, 0xf0, 0xed, 0xaf, 0x10, 0x83, 0x57, 0xa6, - 0x77, 0x2e, 0x57, 0x19, 0x09, 0xdb, 0x0b, 0xbf, 0x1c, 0xd6, 0xfe, 0xc4, 0xa9, 0x85, 0xa5, 0xd5, 0x1c, 0xd4, 0xcf, - 0x6c, 0x39, 0x4e, 0x55, 0xed, 0xdf, 0x92, 0xa4, 0xda, 0x55, 0x5a, 0x4e, 0xef, 0xed, 0x9b, 0x2e, 0x13, 0x6c, 0xec, - 0x07, 0x54, 0x1d, 0x59, 0x0d, 0xbb, 0x2f, 0xd4, 0x17, 0x3d, 0x25, 0x13, 0x9a, 0x8f, 0x2a, 0x9a, 0x67, 0xf7, 0x9b, - 0x1d, 0xf5, 0x9f, 0x5e, 0x0e, 0x45, 0x80, 0x64, 0x95, 0x16, 0x4b, 0x91, 0xb3, 0xb1, 0x1f, 0x0f, 0x93, 0x4c, 0x85, - 0x17, 0xa4, 0xa3, 0xbb, 0xdf, 0xb8, 0xbf, 0xe5, 0x06, 0xb2, 0x42, 0xab, 0x36, 0x18, 0x2b, 0x45, 0xcb, 0x60, 0x7d, - 0x35, 0xee, 0xf7, 0xc5, 0xd5, 0x78, 0x2a, 0x82, 0x1a, 0x88, 0x8b, 0xc4, 0xf5, 0x78, 0x5a, 0x13, 0x4b, 0x6a, 0x57, - 0x60, 0x8c, 0x1e, 0x57, 0x45, 0xed, 0x53, 0x5f, 0x43, 0x28, 0x52, 0xad, 0x99, 0x63, 0x8d, 0x1b, 0x23, 0xe2, 0x0e, - 0x2b, 0xd7, 0x4e, 0xed, 0x75, 0x00, 0x96, 0x57, 0xe3, 0x82, 0xb0, 0x49, 0x8e, 0x9d, 0x0b, 0x58, 0x8d, 0x86, 0x54, - 0xbb, 0xe1, 0xd6, 0xcb, 0xce, 0x6f, 0x1e, 0x27, 0xb6, 0x36, 0xc2, 0x2d, 0x05, 0x94, 0x51, 0x7e, 0x63, 0x39, 0x61, - 0x77, 0xaa, 0x77, 0xa4, 0x6a, 0x47, 0x9c, 0xb8, 0x80, 0xe5, 0x86, 0xa7, 0x56, 0xdf, 0xc4, 0xe0, 0x44, 0xa8, 0x5a, - 0xe9, 0x78, 0xed, 0x47, 0xdc, 0xaf, 0xee, 0xeb, 0x5e, 0x09, 0x7e, 0x12, 0xf2, 0xfa, 0x2d, 0xef, 0x00, 0xb0, 0xe2, - 0x43, 0x5e, 0x4c, 0x0b, 0x47, 0xeb, 0x32, 0x28, 0x03, 0x44, 0x68, 0x06, 0x40, 0x27, 0x57, 0x07, 0x51, 0x1a, 0xb8, - 0xe2, 0x0e, 0x11, 0x7e, 0x1a, 0x3d, 0xcb, 0xaf, 0xc3, 0x67, 0xd5, 0x34, 0xbc, 0xc8, 0x83, 0xe8, 0xa2, 0x0a, 0xa2, - 0x67, 0xd5, 0x55, 0xf8, 0x2c, 0x9f, 0x46, 0x17, 0x79, 0x10, 0x5e, 0x54, 0x8d, 0x7d, 0xd7, 0xee, 0xee, 0x09, 0x79, - 0xdb, 0xd5, 0x1f, 0x39, 0x57, 0xf6, 0x94, 0xe9, 0xf9, 0x79, 0xad, 0x57, 0x6a, 0xb7, 0xb9, 0x5e, 0xa3, 0x66, 0xea, - 0xa3, 0xec, 0x6f, 0xb6, 0xb1, 0xf0, 0x68, 0x0e, 0xa1, 0xcf, 0x48, 0x8b, 0xb9, 0xc7, 0xb9, 0xde, 0xec, 0x49, 0x61, - 0x60, 0xc4, 0xa4, 0x92, 0x91, 0xd3, 0x0b, 0x5c, 0x84, 0x2a, 0xc4, 0xb0, 0x96, 0xae, 0xf6, 0x59, 0x97, 0xde, 0x40, - 0x5d, 0x53, 0xec, 0x6b, 0xc8, 0xc0, 0x8b, 0xa6, 0x97, 0xc1, 0x18, 0x90, 0x23, 0xf0, 0x8e, 0xcf, 0x96, 0x70, 0x60, - 0xae, 0x01, 0xfa, 0xe6, 0x51, 0x5f, 0x97, 0x3b, 0xbe, 0x56, 0x7d, 0x33, 0x5d, 0x8f, 0x94, 0xf2, 0x63, 0xc5, 0xef, - 0x2e, 0x9e, 0xb3, 0x5b, 0xae, 0x51, 0x51, 0x7e, 0xd1, 0x8b, 0xf5, 0x1e, 0xb8, 0xea, 0x7e, 0x81, 0xdb, 0x2c, 0x1e, - 0xbb, 0xf2, 0x80, 0x65, 0x5b, 0xf6, 0xc0, 0x6e, 0xd8, 0x7b, 0xf6, 0x84, 0xbd, 0x61, 0xef, 0xd8, 0x4f, 0xa8, 0xda, - 0x50, 0x42, 0x9e, 0xbf, 0xe0, 0xb7, 0xd2, 0xf4, 0x28, 0x51, 0xc9, 0x1e, 0x6c, 0x33, 0xcd, 0x70, 0xc3, 0xde, 0xf3, - 0xc5, 0x70, 0xc5, 0xde, 0x40, 0x36, 0x94, 0x89, 0x07, 0x2b, 0xf6, 0x13, 0x57, 0x20, 0x66, 0xfa, 0x2c, 0x2c, 0x2d, - 0x51, 0xd1, 0x94, 0x89, 0x32, 0xf4, 0x1b, 0x8e, 0x2f, 0xb2, 0x9f, 0xb0, 0x08, 0xf9, 0x99, 0xe1, 0x8a, 0x3d, 0xf0, - 0xc5, 0x60, 0xc5, 0xde, 0x6b, 0x03, 0xd1, 0x60, 0xe3, 0x96, 0x46, 0x48, 0x56, 0xba, 0x2c, 0x29, 0x4d, 0x6f, 0xed, - 0x6b, 0xe0, 0x86, 0xdd, 0x60, 0xed, 0x9e, 0x60, 0xd1, 0x28, 0xf0, 0x0f, 0x56, 0xec, 0x1d, 0x97, 0x00, 0x6a, 0x6e, - 0x79, 0xd2, 0x2b, 0x54, 0x17, 0x48, 0xf7, 0x83, 0x27, 0x9c, 0x5e, 0x64, 0xef, 0xb0, 0x0c, 0xfa, 0xca, 0x70, 0xc5, - 0xb6, 0x58, 0xbb, 0x1b, 0x63, 0xd9, 0xb2, 0xaa, 0x27, 0x11, 0x81, 0x51, 0x50, 0x29, 0x2d, 0xff, 0x46, 0x2c, 0x9b, - 0xba, 0x69, 0x50, 0x1b, 0xfa, 0xf3, 0xc1, 0xe8, 0x2f, 0xbe, 0x7e, 0xf7, 0x83, 0x57, 0xea, 0x6b, 0xef, 0x2f, 0x8e, - 0x6b, 0x65, 0x89, 0xae, 0x95, 0xbf, 0xf2, 0x72, 0xf6, 0xcb, 0x7c, 0xa2, 0x6b, 0x49, 0x3b, 0x0c, 0xf9, 0x9a, 0xce, - 0x7e, 0xe9, 0x70, 0xb6, 0xfc, 0xd5, 0xf7, 0x1b, 0xd3, 0xc5, 0xea, 0xb3, 0xba, 0x77, 0x1f, 0x06, 0x9b, 0xc6, 0xa9, - 0xf7, 0xee, 0x74, 0xbd, 0xb1, 0x99, 0xb5, 0xf6, 0xcc, 0xfc, 0x1f, 0xae, 0xf4, 0x16, 0x87, 0xee, 0x86, 0x6f, 0x87, - 0x1b, 0x7b, 0x14, 0xe4, 0xf7, 0xa5, 0xd2, 0x38, 0xab, 0xf9, 0x0b, 0xaf, 0x53, 0x8a, 0x05, 0x44, 0xa3, 0x4f, 0x46, - 0x12, 0xba, 0x64, 0x26, 0x9e, 0x21, 0xbe, 0xc8, 0x00, 0x99, 0x0b, 0x44, 0xb3, 0x7b, 0x3e, 0x9e, 0xdc, 0x5f, 0xc5, - 0x93, 0xfb, 0x01, 0xff, 0x64, 0x5a, 0xd0, 0x5e, 0x6c, 0xf7, 0x3e, 0xfb, 0x95, 0x17, 0xf6, 0x72, 0xfc, 0xc5, 0x67, - 0x5f, 0x84, 0xbb, 0x42, 0x7f, 0xf1, 0xd9, 0x3b, 0xc1, 0x7f, 0x1d, 0x69, 0xa2, 0x0c, 0xf6, 0xae, 0xe6, 0xbf, 0x8e, - 0x90, 0xf1, 0x83, 0x7d, 0x16, 0xfc, 0x0b, 0xf8, 0x7e, 0x57, 0x09, 0x5a, 0xc5, 0x3f, 0xd7, 0xea, 0xe7, 0x7b, 0x19, - 0x97, 0x03, 0x6f, 0x42, 0x2b, 0xe8, 0xcd, 0xdb, 0x5a, 0xfe, 0x24, 0x1e, 0x8e, 0x54, 0x3d, 0x35, 0xfc, 0xb3, 0x58, - 0xcc, 0xa2, 0x3e, 0x4a, 0xa7, 0xf2, 0x26, 0x6f, 0x79, 0x26, 0xad, 0xcb, 0xf7, 0x10, 0x0a, 0xfc, 0xd6, 0x86, 0x28, - 0xd8, 0x71, 0xdc, 0x08, 0xde, 0xb2, 0x77, 0xc2, 0x67, 0xd9, 0x74, 0xcb, 0x6f, 0xf8, 0x13, 0xfe, 0x8e, 0xef, 0x82, - 0x07, 0xfe, 0x9e, 0xbf, 0xe1, 0x3f, 0xf1, 0x1d, 0x5b, 0x4a, 0xb4, 0xd3, 0x7a, 0x7b, 0x19, 0x6c, 0x59, 0xbd, 0xbb, - 0x0c, 0x1e, 0x58, 0xbd, 0x7d, 0x1e, 0xdc, 0xb0, 0x7a, 0xf7, 0x3c, 0x78, 0xcf, 0xb6, 0x97, 0xc1, 0x13, 0xb6, 0xbb, - 0x0c, 0xde, 0xb0, 0xed, 0xf3, 0xe0, 0x1d, 0xdb, 0x3d, 0x0f, 0x7e, 0x92, 0x18, 0x0f, 0xef, 0x84, 0xe4, 0x38, 0x79, - 0x57, 0x33, 0xc3, 0xa7, 0x1b, 0x7c, 0x16, 0xd6, 0x2f, 0xaa, 0x63, 0xf0, 0xb9, 0x66, 0xba, 0xc5, 0x81, 0x10, 0x4c, - 0xb7, 0x37, 0xb8, 0xa5, 0x27, 0xa6, 0x55, 0x41, 0x2a, 0x58, 0x57, 0x3b, 0x83, 0x45, 0xdd, 0xb4, 0xce, 0x64, 0xc7, - 0x2f, 0x31, 0xee, 0xf0, 0x4b, 0x5c, 0xb0, 0x65, 0xd3, 0xe9, 0xa4, 0x73, 0xfa, 0x24, 0xd0, 0x9b, 0xbf, 0xde, 0xf5, - 0x2b, 0xe9, 0x3b, 0x53, 0x34, 0x3c, 0x57, 0x5a, 0xe3, 0xd6, 0x4e, 0x1f, 0x5a, 0x3b, 0x3d, 0x93, 0x2a, 0xb4, 0x88, - 0x45, 0x65, 0x51, 0x55, 0xc8, 0x24, 0x1e, 0x64, 0x5a, 0x9f, 0x96, 0x30, 0x52, 0x64, 0x02, 0x1a, 0x7d, 0x41, 0xc7, - 0x40, 0x4e, 0x16, 0x05, 0xb6, 0xe4, 0x9b, 0x41, 0xc2, 0xd6, 0x3c, 0x9e, 0x0e, 0x93, 0x60, 0xc9, 0xee, 0xf8, 0xb0, - 0x5b, 0x20, 0x58, 0xa9, 0x00, 0x26, 0x7d, 0x71, 0x6a, 0xef, 0xeb, 0xbc, 0xb7, 0x4a, 0xe3, 0x38, 0x13, 0xa8, 0x6c, - 0xab, 0xf4, 0x06, 0xbf, 0x75, 0xf6, 0xf3, 0xb5, 0xda, 0xdf, 0x41, 0x52, 0xf8, 0x15, 0x18, 0x76, 0x88, 0xf0, 0x0e, - 0x2a, 0x8c, 0x3c, 0x4b, 0x66, 0xd1, 0x57, 0xf6, 0x96, 0xbe, 0x35, 0xdb, 0xf4, 0x7f, 0x5a, 0x04, 0xed, 0xe3, 0xb2, - 0xf3, 0x3f, 0x99, 0x57, 0x7f, 0xeb, 0x78, 0x75, 0xe3, 0x4f, 0x1e, 0xf8, 0x27, 0x0c, 0x4b, 0xc0, 0x44, 0xb6, 0xe3, - 0x9f, 0x46, 0xdb, 0xc6, 0x29, 0x4f, 0xee, 0xe3, 0xff, 0x57, 0x0a, 0xb4, 0x77, 0xf2, 0xca, 0xde, 0x11, 0xb7, 0xbc, - 0x63, 0x1f, 0x5f, 0x5a, 0x1b, 0xa2, 0x81, 0x26, 0xf9, 0xc4, 0xdd, 0x68, 0x68, 0xd8, 0x10, 0x7f, 0xe1, 0xd5, 0xec, - 0xd3, 0x7c, 0xb2, 0xe5, 0xc7, 0xdb, 0xe1, 0xa7, 0x8e, 0xed, 0xf0, 0x17, 0x7f, 0xb0, 0x6c, 0xbe, 0xd6, 0xab, 0x9d, - 0xdb, 0xb8, 0x53, 0xe9, 0x1d, 0x3f, 0xde, 0xc4, 0x87, 0xff, 0x71, 0xa5, 0x77, 0xdf, 0x5c, 0x69, 0xbb, 0xca, 0xdd, - 0x9d, 0x6f, 0x3a, 0xbe, 0x91, 0xb5, 0xc6, 0x38, 0x33, 0xa3, 0x59, 0xfc, 0x89, 0x66, 0x69, 0x10, 0x59, 0x0a, 0xc5, - 0x9f, 0xcc, 0xb4, 0x53, 0x77, 0xaa, 0xac, 0xee, 0x96, 0x6f, 0x71, 0x8f, 0xbf, 0xe5, 0x63, 0xb6, 0x30, 0x9e, 0x9a, - 0xb7, 0x57, 0x8b, 0xc9, 0x60, 0x70, 0xeb, 0xef, 0xef, 0x79, 0x38, 0xbb, 0x9d, 0xb3, 0xb7, 0xfc, 0x9e, 0x16, 0xd3, - 0x44, 0x35, 0xbd, 0x78, 0x4c, 0xf0, 0xba, 0xf5, 0xfd, 0x89, 0xc5, 0xff, 0x6a, 0x5f, 0x34, 0x6f, 0xfd, 0x81, 0xb4, - 0x46, 0xcb, 0x5d, 0xfd, 0xfd, 0xe3, 0x8a, 0x89, 0x5b, 0x10, 0x2f, 0xde, 0xdb, 0x9a, 0x86, 0x37, 0xfc, 0xa3, 0xf7, - 0xd6, 0x9f, 0xbe, 0xd5, 0x31, 0x37, 0x13, 0x75, 0x24, 0xbd, 0xb9, 0x78, 0xce, 0x7e, 0xe5, 0x9f, 0xe4, 0x71, 0xf2, - 0x45, 0xc8, 0x49, 0x7b, 0x83, 0xdc, 0x4d, 0x74, 0x4a, 0xbc, 0x73, 0x13, 0x09, 0x0b, 0x02, 0x61, 0x38, 0x6a, 0xfe, - 0x30, 0x29, 0xa7, 0xde, 0x0e, 0xb8, 0x5d, 0xb9, 0xad, 0x7f, 0xbe, 0xe5, 0x9c, 0x2f, 0x86, 0x97, 0xd3, 0x77, 0xdd, - 0x2e, 0x3d, 0x2a, 0x9a, 0x4d, 0x05, 0xba, 0xdd, 0x62, 0xec, 0xd5, 0xc9, 0xcc, 0x32, 0x97, 0x7c, 0xe9, 0x5d, 0x6d, - 0x66, 0x1e, 0xd3, 0xfb, 0xcd, 0x34, 0x43, 0x22, 0x5f, 0x20, 0x64, 0x3a, 0x1c, 0xee, 0xce, 0xb1, 0x3c, 0x3e, 0x7c, - 0xf3, 0xec, 0xc9, 0xe0, 0x09, 0x46, 0x6e, 0x59, 0xd1, 0x20, 0xef, 0xf8, 0x30, 0xab, 0x5b, 0xb7, 0x8d, 0x8b, 0xe7, - 0xc3, 0x5f, 0x20, 0x6f, 0xd0, 0xf5, 0xd0, 0x14, 0xd1, 0x2a, 0xbf, 0xa3, 0xe8, 0x13, 0x25, 0x07, 0x1d, 0x4f, 0xa0, - 0x76, 0x48, 0x81, 0xfb, 0xee, 0x19, 0x07, 0xfd, 0x06, 0x96, 0xda, 0xef, 0x9f, 0x7f, 0x22, 0x1e, 0x69, 0x18, 0xef, - 0xef, 0xc3, 0xe8, 0x8f, 0xb8, 0x2c, 0xd6, 0x70, 0xba, 0x0e, 0xe0, 0x73, 0xcf, 0xf4, 0xed, 0xeb, 0xce, 0xf7, 0xfd, - 0xc0, 0xdb, 0xf2, 0x1b, 0xf6, 0x8e, 0x7b, 0x97, 0xc3, 0x37, 0xfe, 0xb3, 0x27, 0x20, 0x3a, 0xc1, 0xb8, 0x7c, 0xc6, - 0x48, 0xd8, 0x8e, 0x62, 0xd4, 0x2a, 0xfc, 0x5c, 0x43, 0x88, 0xd6, 0x27, 0x64, 0xec, 0x82, 0xf4, 0x0f, 0x0a, 0xd0, - 0x4f, 0x08, 0xac, 0x26, 0xa9, 0x51, 0x60, 0x12, 0xdf, 0xd6, 0x90, 0x40, 0x0a, 0x16, 0x08, 0xbd, 0x81, 0xe2, 0x53, - 0xc1, 0xdf, 0x0d, 0x3f, 0x93, 0xe4, 0xb7, 0xa8, 0xf9, 0x18, 0xfe, 0x86, 0xa1, 0x99, 0x54, 0x0f, 0x69, 0x1d, 0x25, - 0xde, 0x4f, 0xfe, 0x3e, 0x0a, 0x2b, 0xa1, 0x8e, 0x85, 0x20, 0x15, 0x43, 0x2e, 0xc4, 0xc5, 0xf3, 0xc9, 0x6d, 0x29, - 0xc2, 0x3f, 0x26, 0xf8, 0x4c, 0x2e, 0x34, 0xf9, 0x8c, 0x9e, 0x34, 0xf2, 0xfd, 0x07, 0xf9, 0xbe, 0xec, 0xd4, 0x60, - 0x51, 0x0f, 0xf9, 0x6d, 0xed, 0xbe, 0x2f, 0xa7, 0x04, 0x3d, 0xb2, 0x1f, 0xd0, 0x14, 0x0c, 0xd4, 0x04, 0xa4, 0x0c, - 0xc1, 0x2d, 0x5c, 0xf5, 0x3d, 0x55, 0x90, 0x2f, 0xbf, 0xf7, 0x59, 0xc8, 0x70, 0x95, 0x05, 0x21, 0xc9, 0xa5, 0x42, - 0x0a, 0x1b, 0xb7, 0xf5, 0xe0, 0xb3, 0xc6, 0x24, 0x91, 0x90, 0x53, 0x02, 0x92, 0xa4, 0xbd, 0x81, 0x24, 0x11, 0xd3, - 0x7f, 0xb8, 0x4e, 0x9a, 0x66, 0x25, 0xa5, 0x1b, 0xe2, 0x54, 0x7d, 0x8b, 0x34, 0x67, 0xc1, 0x7b, 0x06, 0x4b, 0x47, - 0x8a, 0x15, 0xef, 0x8c, 0xc1, 0x58, 0x07, 0x0b, 0xdd, 0xc9, 0xe2, 0x7e, 0x95, 0x84, 0x69, 0x24, 0xaa, 0x7c, 0x11, - 0xf2, 0xe7, 0xbf, 0x94, 0xf8, 0xa3, 0xb7, 0x34, 0x10, 0x81, 0xe0, 0x07, 0x68, 0x3d, 0x60, 0x8d, 0x07, 0x3f, 0xb1, - 0xba, 0x0c, 0xf3, 0x2a, 0xa3, 0xf2, 0x66, 0x3b, 0xb6, 0x9d, 0x33, 0x55, 0xb5, 0xe0, 0xb3, 0x30, 0xb4, 0x68, 0x67, - 0xab, 0xe6, 0xe4, 0x36, 0x6f, 0xf0, 0x9d, 0x49, 0x12, 0xa9, 0xa5, 0x24, 0xd2, 0x56, 0xd7, 0xa7, 0x4b, 0xaf, 0x5b, - 0x54, 0xd0, 0x18, 0x01, 0x7a, 0x49, 0xba, 0xab, 0x7c, 0x42, 0xf1, 0xca, 0x6a, 0x58, 0x0d, 0x2f, 0x1d, 0x8a, 0x30, - 0xd6, 0xde, 0x5c, 0xc9, 0xb3, 0x3b, 0xb0, 0x1e, 0xa1, 0xb5, 0xab, 0x52, 0x87, 0xb0, 0xfd, 0x44, 0xef, 0x39, 0x95, - 0xfa, 0x1b, 0x50, 0x05, 0x4e, 0x1d, 0x0d, 0xf5, 0x51, 0x3b, 0x85, 0x6c, 0xe7, 0xde, 0x92, 0xa0, 0x72, 0x25, 0x37, - 0x55, 0x5a, 0x94, 0x52, 0xa6, 0x7c, 0x2d, 0xb3, 0x95, 0xdd, 0x27, 0x03, 0x88, 0x67, 0x83, 0x02, 0xc9, 0x45, 0x6d, - 0x35, 0x07, 0xe9, 0xa3, 0x59, 0xe2, 0x58, 0x3b, 0x28, 0xbc, 0xac, 0x02, 0x33, 0x97, 0xb9, 0x5c, 0x0e, 0x0a, 0x96, - 0xeb, 0xad, 0x66, 0x9a, 0xa9, 0xbe, 0xc8, 0xed, 0x6d, 0xc6, 0xcb, 0xf4, 0xdf, 0x2c, 0x19, 0xf0, 0xe8, 0xe2, 0xb9, - 0x1f, 0x40, 0x9a, 0xe4, 0x75, 0x80, 0x24, 0xd8, 0x1c, 0xec, 0x62, 0x87, 0x61, 0xab, 0x58, 0xd9, 0x93, 0xa7, 0xcb, - 0x1d, 0x9a, 0x72, 0x09, 0x23, 0x39, 0x31, 0x97, 0x52, 0xdf, 0x97, 0x54, 0x37, 0x14, 0x9c, 0x6c, 0x9a, 0x80, 0x52, - 0x40, 0xbb, 0x05, 0xff, 0x85, 0x4f, 0x0d, 0x9d, 0x16, 0x60, 0xa9, 0xed, 0x06, 0xfc, 0x17, 0xfa, 0xc5, 0xf6, 0x11, - 0xf5, 0x03, 0xf3, 0x60, 0x6f, 0xd6, 0x56, 0xc6, 0x80, 0x88, 0xc4, 0x15, 0xe4, 0x91, 0xe0, 0x07, 0xc5, 0x9e, 0x2e, - 0x13, 0x07, 0xce, 0x14, 0x17, 0x4b, 0xa9, 0xcd, 0xcc, 0x6b, 0xbf, 0xa5, 0x26, 0xde, 0x44, 0x49, 0x54, 0xd8, 0x0e, - 0x69, 0xf4, 0x92, 0x32, 0xa6, 0x0a, 0x36, 0x44, 0xf7, 0x75, 0x13, 0x4c, 0x81, 0x37, 0x55, 0x15, 0x10, 0xa1, 0xf6, - 0x22, 0xcb, 0xf3, 0x9b, 0x2e, 0xb0, 0xba, 0xe0, 0x63, 0x63, 0x9a, 0x5d, 0xb0, 0x92, 0xab, 0x99, 0xf4, 0x99, 0xb7, - 0x03, 0x2d, 0xe4, 0x5d, 0x5e, 0x16, 0xad, 0xd0, 0xf5, 0x20, 0x5a, 0xf8, 0x7b, 0xcd, 0xf1, 0xe8, 0xd9, 0xb6, 0x9a, - 0xda, 0xec, 0x6b, 0x2d, 0x16, 0xc8, 0x40, 0x34, 0xf4, 0x85, 0x9c, 0x51, 0xb8, 0xab, 0x34, 0x57, 0xab, 0x7d, 0x55, - 0x06, 0x09, 0x4c, 0x04, 0x59, 0xcb, 0xc2, 0x7b, 0x74, 0xaf, 0x1e, 0x69, 0x5e, 0x49, 0xf0, 0xcc, 0xc5, 0x5f, 0x00, - 0x08, 0xe5, 0x49, 0x42, 0x0e, 0xc8, 0x01, 0xfc, 0x2d, 0x45, 0xa9, 0x34, 0xc0, 0x3f, 0xab, 0xcb, 0xb1, 0xad, 0xef, - 0xef, 0xb4, 0x8a, 0xc1, 0xf5, 0xe7, 0xeb, 0xae, 0x67, 0xed, 0x10, 0xe7, 0xca, 0x56, 0xaf, 0x2d, 0xd3, 0x3c, 0x46, - 0xea, 0x1a, 0x80, 0x3b, 0x91, 0x1e, 0x81, 0x48, 0x66, 0xa2, 0x41, 0xce, 0xae, 0xf9, 0x78, 0x2a, 0x1e, 0x93, 0xf6, - 0x2a, 0xdf, 0x37, 0x17, 0xfa, 0x60, 0x8c, 0x7d, 0x0b, 0x1a, 0xc4, 0x47, 0xab, 0xad, 0x15, 0x88, 0xf5, 0x56, 0xa9, - 0x0f, 0xdd, 0x18, 0x05, 0x1d, 0x3c, 0xe2, 0x46, 0x2e, 0x38, 0xb6, 0xbb, 0xb6, 0x9e, 0xd2, 0x57, 0x00, 0xe6, 0x3a, - 0x50, 0xc9, 0x30, 0x48, 0x9d, 0x27, 0x0a, 0x93, 0xfc, 0x3c, 0x21, 0x09, 0x11, 0xd5, 0xd9, 0x72, 0x94, 0x72, 0xd3, - 0x02, 0x2e, 0x33, 0x32, 0xc0, 0x6c, 0xd2, 0xac, 0x9f, 0x5c, 0xbe, 0x04, 0xa9, 0x34, 0x44, 0x70, 0xc3, 0xf6, 0x92, - 0xd1, 0xad, 0xa3, 0x6e, 0x50, 0x25, 0x99, 0xeb, 0x37, 0xb7, 0xb3, 0x48, 0x99, 0x37, 0x1f, 0x61, 0xac, 0xc9, 0x87, - 0xb0, 0x4e, 0xf0, 0xdb, 0x00, 0x95, 0xf4, 0xa9, 0xf0, 0xa2, 0x11, 0x40, 0xa8, 0xef, 0x54, 0x19, 0x9f, 0x0a, 0x2f, - 0x1b, 0x6d, 0x59, 0x46, 0x29, 0x54, 0x17, 0xcc, 0x6e, 0x4d, 0x17, 0xa2, 0x5b, 0x55, 0x03, 0x6d, 0xe0, 0xda, 0x75, - 0xa0, 0x80, 0x86, 0x6a, 0x57, 0x6e, 0x58, 0x00, 0x56, 0x33, 0x11, 0x18, 0x2e, 0xff, 0x3e, 0x7f, 0xa9, 0x62, 0x78, - 0xfa, 0xfd, 0xd0, 0xdb, 0x6f, 0x83, 0x68, 0xb4, 0xbd, 0x64, 0xbb, 0x20, 0x1a, 0xed, 0x2e, 0x1b, 0x46, 0xbf, 0x9f, - 0xd3, 0xef, 0xe7, 0x0d, 0xe8, 0x48, 0x84, 0x09, 0xb3, 0xd7, 0x6f, 0xd4, 0xf2, 0x95, 0x5a, 0xbf, 0x53, 0xcb, 0x97, - 0x6a, 0x78, 0x6b, 0x4f, 0x12, 0x41, 0x64, 0xa9, 0x6a, 0x1e, 0x24, 0x45, 0xaa, 0xa5, 0xcb, 0x31, 0x5a, 0x8c, 0xa8, - 0xa5, 0xac, 0x39, 0xd6, 0x89, 0xb4, 0x73, 0x50, 0x32, 0xc0, 0xd1, 0xe2, 0xaa, 0xc6, 0x74, 0xb3, 0xa2, 0x25, 0x10, - 0x23, 0xac, 0x6c, 0xcb, 0xc5, 0x4d, 0xea, 0xa3, 0x73, 0xf2, 0x6d, 0xab, 0x94, 0x6f, 0x5b, 0xc1, 0xf3, 0xaf, 0x28, - 0x94, 0x4b, 0xae, 0x5d, 0xcb, 0xa6, 0x85, 0x52, 0x28, 0xe3, 0x1a, 0x6c, 0xed, 0x9b, 0xc0, 0x90, 0xf9, 0x48, 0x51, - 0x63, 0x7b, 0xd1, 0x28, 0x87, 0x20, 0x5b, 0x07, 0xa3, 0x4e, 0x59, 0xb0, 0xf8, 0x76, 0x87, 0x0c, 0x64, 0xa0, 0xa3, - 0xaa, 0x8d, 0x57, 0x3b, 0x2b, 0xfd, 0x61, 0x79, 0xf1, 0x9c, 0x25, 0x56, 0x3a, 0xf9, 0x4d, 0x85, 0xfe, 0x20, 0x44, - 0xdf, 0x94, 0x0d, 0x07, 0x2f, 0xba, 0xd8, 0xca, 0x80, 0x78, 0xc3, 0xf4, 0xde, 0xc6, 0x4a, 0x96, 0xbb, 0xa6, 0x7c, - 0x31, 0xe3, 0x09, 0xc7, 0xd1, 0x97, 0xab, 0x45, 0x58, 0xab, 0x45, 0x76, 0x02, 0x3c, 0xb4, 0x56, 0x4b, 0x21, 0x57, - 0x8b, 0x70, 0x66, 0xba, 0x50, 0x33, 0x3d, 0x03, 0xcd, 0xa3, 0x50, 0xb3, 0x3c, 0x01, 0x2c, 0x78, 0x61, 0x66, 0xb8, - 0x30, 0x33, 0x1c, 0x87, 0xd4, 0x38, 0x3d, 0xe8, 0xbd, 0xce, 0x3d, 0xb7, 0xdc, 0x8d, 0x4e, 0xc3, 0xbc, 0x1d, 0x6d, - 0x30, 0xc7, 0x07, 0xe1, 0x04, 0xe2, 0x03, 0x4b, 0x04, 0xe8, 0xd1, 0xb0, 0x3a, 0x6a, 0xa8, 0x1c, 0xc5, 0x97, 0x05, - 0x20, 0x59, 0x12, 0x80, 0xe4, 0x5e, 0x8d, 0x73, 0x69, 0xf9, 0x75, 0x95, 0x84, 0x1c, 0x91, 0xf1, 0x52, 0xda, 0xdd, - 0x13, 0x5e, 0x8e, 0x8c, 0xd0, 0x3c, 0x59, 0xa4, 0x5e, 0xce, 0x32, 0x36, 0x46, 0xe0, 0xa2, 0xd0, 0x6f, 0xaa, 0x7e, - 0x3f, 0x2d, 0xbd, 0x9c, 0xda, 0xf9, 0x09, 0xfc, 0x2d, 0x4f, 0x9d, 0x45, 0x8e, 0x90, 0x57, 0x23, 0x93, 0xb0, 0xbc, - 0x54, 0xea, 0xe9, 0x4b, 0x98, 0x41, 0xdd, 0xbd, 0x51, 0x00, 0xae, 0x45, 0x2e, 0x9d, 0x6a, 0x4b, 0xb8, 0x32, 0xe5, - 0x06, 0xfb, 0x3c, 0xe4, 0x39, 0x09, 0xa1, 0x12, 0x79, 0xa4, 0xb0, 0xee, 0xdb, 0x17, 0xcf, 0x27, 0xae, 0x0f, 0x8b, - 0x8d, 0x46, 0x70, 0x38, 0x00, 0xcc, 0xc1, 0xd4, 0x8b, 0x06, 0xbc, 0x54, 0x73, 0xe6, 0xa3, 0x97, 0x13, 0x36, 0x06, - 0xa8, 0x29, 0x06, 0x4e, 0x59, 0xcf, 0xe4, 0x23, 0xe3, 0x5b, 0xe6, 0xfb, 0x01, 0xbe, 0x5b, 0x17, 0x12, 0xf2, 0x41, - 0xa1, 0x12, 0x64, 0x0a, 0x95, 0x20, 0x31, 0xa8, 0x04, 0xb1, 0x41, 0x25, 0xd8, 0x34, 0x7c, 0x2d, 0x95, 0xb7, 0x11, - 0x70, 0x44, 0xf8, 0xd0, 0xb3, 0xb0, 0xb1, 0x42, 0xf1, 0x6c, 0xcc, 0xc6, 0xac, 0x50, 0x3b, 0x4f, 0x2e, 0xa7, 0x62, - 0x67, 0x31, 0xd6, 0x4d, 0x64, 0x99, 0x78, 0x21, 0x41, 0xc7, 0x39, 0x17, 0x12, 0x75, 0xf5, 0x73, 0xef, 0x25, 0x19, - 0x4b, 0xe6, 0x0d, 0x8d, 0x1a, 0xcc, 0xcb, 0xae, 0x03, 0x98, 0x96, 0x7c, 0x5b, 0xd0, 0x60, 0x3a, 0x55, 0x1e, 0x91, - 0x26, 0x41, 0xed, 0x5c, 0x26, 0x45, 0x4e, 0x08, 0x93, 0xa0, 0x57, 0x82, 0xdf, 0x48, 0x68, 0xff, 0xaf, 0x7a, 0xbe, - 0x03, 0x06, 0x13, 0xad, 0x92, 0x2f, 0x60, 0xb5, 0xcc, 0xf9, 0x0b, 0xe9, 0x89, 0x8d, 0xf8, 0x8b, 0x65, 0x1a, 0x8f, - 0xbe, 0xb0, 0x21, 0xe2, 0x59, 0xbd, 0x40, 0xd3, 0x12, 0xd4, 0x01, 0x1e, 0xd1, 0x5f, 0xa3, 0x2f, 0x86, 0x37, 0xa5, - 0xab, 0x91, 0xba, 0x66, 0xe7, 0x9c, 0x7f, 0xa9, 0x0d, 0x11, 0x32, 0xa6, 0x4d, 0x81, 0x64, 0x40, 0x20, 0xc9, 0x40, - 0x00, 0x60, 0x6a, 0x3a, 0xb3, 0x57, 0x00, 0xd1, 0x40, 0x00, 0x8f, 0xf3, 0x8e, 0xc7, 0x8f, 0xf4, 0x57, 0x71, 0xdc, - 0x3b, 0x4d, 0xc3, 0xf6, 0x5f, 0x80, 0xa6, 0x18, 0xca, 0xf1, 0x7c, 0xa7, 0x20, 0xd9, 0xa3, 0x94, 0xa5, 0xab, 0x26, - 0xb2, 0x43, 0xb1, 0x3e, 0xcd, 0x29, 0x0b, 0x69, 0x5b, 0x8e, 0xd1, 0x16, 0xeb, 0xc7, 0xc8, 0x7b, 0x73, 0xa3, 0x22, - 0x1f, 0xf4, 0xe0, 0xf6, 0xf6, 0xe6, 0x55, 0x8f, 0xd9, 0x24, 0x2b, 0x16, 0xb9, 0x8a, 0x38, 0x71, 0x5a, 0x87, 0x1c, - 0x30, 0x20, 0x27, 0x21, 0x30, 0x8d, 0x71, 0xa9, 0x40, 0x07, 0x25, 0xcb, 0x79, 0x0d, 0xd4, 0xb2, 0x88, 0xac, 0x01, - 0xa2, 0x9a, 0xe6, 0x5f, 0x35, 0xe4, 0x27, 0x55, 0x73, 0x4a, 0xa1, 0xf6, 0x15, 0x0f, 0xab, 0xd3, 0x27, 0x56, 0x6d, - 0x62, 0xac, 0x7f, 0xad, 0x3d, 0x41, 0x5b, 0x49, 0x03, 0xf1, 0x9d, 0xaf, 0xd2, 0x3b, 0x0a, 0xdd, 0x71, 0x66, 0xe2, - 0xa9, 0x0a, 0x8c, 0x7d, 0x6b, 0x47, 0x50, 0x38, 0x34, 0x5d, 0x07, 0x1c, 0xa6, 0xd1, 0x09, 0x8b, 0x7f, 0x4a, 0xc7, - 0xc9, 0x8b, 0x5a, 0x21, 0x92, 0xfc, 0x43, 0xb8, 0x30, 0x24, 0x16, 0xe4, 0x25, 0xa1, 0x8e, 0xc8, 0x88, 0xd5, 0xa8, - 0x58, 0x0b, 0x15, 0x15, 0xa7, 0x78, 0xbc, 0x55, 0x50, 0x5c, 0x8a, 0x52, 0xa5, 0x54, 0xe4, 0x46, 0xa5, 0x80, 0x58, - 0x36, 0xf0, 0x6e, 0x01, 0x07, 0x40, 0xd0, 0x59, 0xee, 0xd6, 0xb6, 0xbb, 0x8d, 0xcc, 0x67, 0xa6, 0x79, 0x5a, 0x7d, - 0x50, 0x7f, 0xbf, 0x5f, 0x62, 0x6c, 0x8d, 0xa7, 0xbf, 0x6f, 0xd3, 0x82, 0x9b, 0xbf, 0x61, 0x88, 0xee, 0x00, 0x11, - 0xb3, 0xb4, 0x87, 0x42, 0x16, 0x4c, 0x58, 0x86, 0xaa, 0x3c, 0xe5, 0xa8, 0x97, 0x4f, 0x6e, 0x01, 0x42, 0x0d, 0xfd, - 0xda, 0xe8, 0x54, 0x57, 0x25, 0x08, 0xdf, 0x77, 0x85, 0x7a, 0x6c, 0x0e, 0x78, 0x32, 0x00, 0xfe, 0x8a, 0xbc, 0xd6, - 0x63, 0xfb, 0x07, 0xbd, 0x51, 0x6f, 0x80, 0x20, 0x3a, 0xe7, 0x85, 0x7f, 0xc4, 0xb9, 0x4e, 0xfd, 0x19, 0x17, 0x82, - 0xf8, 0xd6, 0x93, 0xf0, 0x5e, 0x9c, 0xa5, 0x71, 0x70, 0xd6, 0x1b, 0x98, 0x8b, 0x40, 0x71, 0x96, 0xe6, 0x67, 0x20, - 0x96, 0x23, 0x26, 0x62, 0xcd, 0xee, 0x00, 0x26, 0xb0, 0xd4, 0x71, 0xc8, 0xaa, 0x63, 0xfb, 0xfd, 0xd7, 0x23, 0x43, - 0x96, 0x8e, 0x30, 0x30, 0xfa, 0x77, 0x05, 0x02, 0x14, 0x2c, 0x33, 0xdb, 0x83, 0x49, 0x57, 0x7b, 0x56, 0xcf, 0x9b, - 0x4d, 0xde, 0xd5, 0x3b, 0x56, 0xd3, 0x72, 0x6a, 0x5a, 0x65, 0x35, 0x6d, 0x92, 0x43, 0xcd, 0x44, 0xbf, 0xaf, 0x41, - 0x51, 0xf3, 0x39, 0x80, 0xb1, 0x61, 0xf2, 0xeb, 0x59, 0x35, 0xef, 0xf7, 0x3d, 0xf9, 0x08, 0x7e, 0x21, 0x5b, 0x99, - 0x5b, 0x63, 0xf9, 0xf4, 0x15, 0x91, 0x98, 0x19, 0x98, 0xa3, 0xbb, 0x23, 0x7c, 0xaf, 0x1b, 0xe1, 0x75, 0xcc, 0x15, - 0x36, 0x13, 0xd3, 0xd7, 0x30, 0x78, 0x9e, 0xf0, 0xc1, 0x45, 0x8e, 0xfe, 0x46, 0x0e, 0x33, 0x85, 0x05, 0x39, 0xf7, - 0x27, 0xaf, 0x11, 0x2f, 0x19, 0xe1, 0x1d, 0x74, 0x3a, 0xe1, 0x41, 0xf6, 0xfb, 0x2b, 0xe8, 0xcc, 0x56, 0x2a, 0x65, - 0xab, 0xa2, 0x32, 0x5d, 0xd7, 0x45, 0x59, 0x41, 0xc7, 0xd2, 0xcf, 0x5b, 0x21, 0x33, 0xeb, 0x67, 0x16, 0xdc, 0xd3, - 0x4a, 0x02, 0x4c, 0xd9, 0xb6, 0x89, 0xda, 0xc0, 0xcb, 0xba, 0xf8, 0x5c, 0xe0, 0xd1, 0x59, 0x7b, 0xbd, 0x11, 0x6a, - 0x9f, 0xf3, 0xd1, 0xba, 0x58, 0x7b, 0xe0, 0x07, 0x33, 0x4b, 0xe7, 0x8a, 0x38, 0x23, 0xf7, 0x47, 0x9f, 0x8b, 0x34, - 0xa7, 0x3c, 0xc0, 0x7d, 0x28, 0xe6, 0xf6, 0x5b, 0x20, 0xfd, 0xd0, 0x5b, 0x20, 0xfb, 0xe8, 0x9c, 0x93, 0xd7, 0x80, - 0x48, 0x87, 0x30, 0xb8, 0x15, 0x09, 0x3a, 0x56, 0x0d, 0x6f, 0x2d, 0xb0, 0xd3, 0x5e, 0x1a, 0xf7, 0xd2, 0xfc, 0x2c, - 0xed, 0xf7, 0x0d, 0x6a, 0x66, 0x8a, 0x70, 0xf0, 0x38, 0x23, 0x17, 0x49, 0x0b, 0xb6, 0x94, 0xf6, 0x5f, 0x0d, 0x1c, - 0x41, 0xc8, 0xdf, 0xff, 0x10, 0xde, 0x13, 0x80, 0xd8, 0xa4, 0x0d, 0xb8, 0xea, 0x31, 0x1d, 0x8d, 0x2d, 0x89, 0x5a, - 0x75, 0x36, 0x40, 0xe2, 0x54, 0x69, 0x3d, 0xe5, 0x66, 0x4d, 0x61, 0x90, 0x2a, 0x0b, 0xf5, 0x1b, 0xeb, 0xc9, 0x64, - 0x95, 0x8b, 0x8c, 0x38, 0x2a, 0xd3, 0x97, 0x9a, 0x11, 0x4c, 0x97, 0x7e, 0xbe, 0x80, 0x25, 0x1b, 0x7f, 0xc4, 0xc9, - 0x5b, 0x02, 0x8e, 0xed, 0xac, 0x5d, 0x55, 0xbb, 0x1c, 0xb7, 0x76, 0x73, 0x80, 0xef, 0xf5, 0x46, 0xa3, 0x91, 0x76, - 0x8e, 0x13, 0x30, 0x54, 0x3d, 0xb5, 0x14, 0x7a, 0xac, 0x56, 0x80, 0xba, 0x1d, 0xb9, 0xcc, 0x92, 0xc1, 0x7c, 0x61, - 0x1c, 0xbf, 0x34, 0x1f, 0x7d, 0xbc, 0x54, 0xd6, 0xae, 0x23, 0xbe, 0xfe, 0x83, 0xac, 0xd6, 0xb7, 0xbc, 0xab, 0x9a, - 0x80, 0x2f, 0xaa, 0x80, 0xd2, 0x6f, 0x78, 0x4f, 0xf6, 0x2e, 0xbe, 0x76, 0x83, 0x5d, 0xf2, 0x2d, 0x6f, 0x51, 0xe7, - 0xf9, 0xca, 0xc1, 0x8d, 0x2a, 0xdd, 0xde, 0x4b, 0x16, 0xb8, 0xf6, 0x8e, 0x9a, 0xc6, 0x7a, 0xe6, 0x47, 0x0f, 0x8b, - 0x90, 0xed, 0x7c, 0xec, 0x7d, 0xd5, 0x3c, 0x3d, 0x6b, 0xe8, 0x4d, 0x6a, 0xe8, 0x63, 0x2f, 0xca, 0xf6, 0xa9, 0x69, - 0x44, 0xaf, 0x61, 0x43, 0x1f, 0x7b, 0x4b, 0x4e, 0x0e, 0x89, 0x00, 0xa7, 0xc6, 0xfc, 0xf1, 0xe1, 0x74, 0x86, 0xbf, - 0x63, 0x40, 0x25, 0x10, 0xf3, 0xe9, 0x31, 0xed, 0x28, 0xc0, 0x8c, 0x2a, 0xbd, 0x7d, 0x7a, 0x60, 0x3b, 0x5e, 0xd6, - 0x43, 0x4b, 0xef, 0x9e, 0x1c, 0xdd, 0x8e, 0x57, 0xd5, 0xf8, 0x52, 0x0e, 0x79, 0x9e, 0xcf, 0x46, 0xa3, 0x91, 0x30, - 0x90, 0xdc, 0x95, 0xde, 0xc0, 0x0a, 0xa4, 0x6d, 0x51, 0x7d, 0x28, 0x97, 0xde, 0x4e, 0x1d, 0xda, 0x95, 0x3f, 0xc9, - 0x0f, 0x87, 0x62, 0x64, 0x8e, 0x71, 0x00, 0x37, 0x29, 0x94, 0x1c, 0x25, 0x6b, 0x09, 0xa2, 0x53, 0x1a, 0x4f, 0x65, - 0xbd, 0xb6, 0x22, 0xf2, 0x6a, 0xc4, 0x79, 0x08, 0x7e, 0xf4, 0x40, 0x2d, 0x7e, 0xad, 0x05, 0xb1, 0xc7, 0x3e, 0x55, - 0x4a, 0x2f, 0x78, 0x55, 0x40, 0x88, 0xd8, 0xdf, 0x0d, 0xb4, 0x83, 0x12, 0x1c, 0x4a, 0xb8, 0x0f, 0x08, 0x0b, 0xfd, - 0xca, 0xcb, 0x67, 0x32, 0x46, 0xb9, 0x37, 0xa8, 0xe6, 0x0c, 0x60, 0x2a, 0x7d, 0x06, 0x7e, 0x97, 0x00, 0x75, 0x8a, - 0x4f, 0xd1, 0xa9, 0xde, 0x3c, 0x6c, 0xba, 0x3e, 0x2d, 0x51, 0x14, 0xd1, 0x9d, 0x9f, 0x8f, 0x01, 0xb1, 0xb3, 0x6b, - 0x33, 0xd2, 0xae, 0xfd, 0x06, 0x0d, 0x56, 0x4a, 0x12, 0xed, 0x9c, 0x12, 0x76, 0x3b, 0x1f, 0xd9, 0xd2, 0x8f, 0x52, - 0x20, 0xe6, 0x8e, 0x13, 0x89, 0xec, 0xc1, 0x46, 0x4e, 0xe0, 0x16, 0xed, 0x1d, 0x1d, 0x80, 0xca, 0x8d, 0x82, 0xfc, - 0x6a, 0x8e, 0xe4, 0x8e, 0xef, 0x7a, 0xdf, 0x0d, 0xea, 0xc1, 0x77, 0xbd, 0xb3, 0x94, 0xe4, 0x8e, 0xf0, 0x4c, 0x4d, - 0x09, 0x11, 0x9f, 0x7d, 0x37, 0xc8, 0x07, 0x78, 0x96, 0x68, 0x91, 0x16, 0x09, 0xd5, 0xea, 0x1a, 0x37, 0xe1, 0x45, - 0x22, 0xb9, 0x87, 0x76, 0x9d, 0x47, 0xc4, 0x02, 0x90, 0xb1, 0xf8, 0x6c, 0xde, 0x50, 0xa8, 0xbb, 0x89, 0xd9, 0xa2, - 0xbb, 0x2c, 0xf6, 0xfb, 0x9b, 0x3c, 0xad, 0x7b, 0x3a, 0x3e, 0x06, 0x5f, 0x90, 0x6a, 0x02, 0x3c, 0xda, 0x5f, 0x99, - 0xe3, 0xd5, 0xab, 0xcd, 0x91, 0xb2, 0x50, 0x25, 0xea, 0xb7, 0x58, 0xcd, 0x7a, 0x08, 0xc3, 0x9d, 0x65, 0xc6, 0xda, - 0x5e, 0xf0, 0x4a, 0xce, 0xaa, 0xd8, 0x2e, 0xc7, 0x57, 0x2c, 0xb5, 0x95, 0x44, 0xe5, 0x68, 0x3d, 0xd6, 0xa6, 0x18, - 0xf9, 0x95, 0x42, 0xa2, 0x2c, 0x3a, 0xb6, 0x16, 0x0a, 0x88, 0x17, 0xa0, 0x2f, 0xd9, 0x99, 0x06, 0x58, 0x6f, 0xf4, - 0x2a, 0x22, 0xb4, 0x7c, 0xa4, 0xc2, 0x9b, 0xdc, 0x54, 0x99, 0x95, 0xcd, 0xa2, 0xdd, 0x4f, 0x15, 0xaf, 0x10, 0xac, - 0xde, 0xa8, 0x3d, 0x0a, 0x50, 0x7b, 0x68, 0xa1, 0x0c, 0x20, 0xa5, 0x69, 0x06, 0x80, 0x0c, 0x00, 0x32, 0x55, 0xc4, - 0x67, 0x02, 0x54, 0xda, 0xea, 0x46, 0x81, 0x13, 0xe9, 0x15, 0xd0, 0x2c, 0xb0, 0xd2, 0x47, 0x0a, 0x32, 0x58, 0x6c, - 0x11, 0x80, 0x95, 0x23, 0x67, 0x98, 0xc6, 0x90, 0x6d, 0x34, 0x71, 0x49, 0x9a, 0xdf, 0x87, 0x59, 0x2a, 0xf1, 0x24, - 0x7e, 0x90, 0x35, 0x46, 0x00, 0x20, 0x7d, 0x9f, 0x5e, 0x14, 0x59, 0x4c, 0x38, 0x70, 0xd6, 0x53, 0x07, 0x45, 0x4d, - 0xce, 0xb5, 0xa6, 0xd5, 0xb3, 0xda, 0xe4, 0x21, 0x0b, 0x74, 0xf6, 0x60, 0x4c, 0x6a, 0xf9, 0x9e, 0x47, 0xf6, 0x57, - 0x8e, 0x67, 0x84, 0xef, 0xba, 0x83, 0x53, 0xff, 0xdd, 0xd4, 0xc0, 0xc4, 0x94, 0x00, 0x6c, 0x0c, 0x8e, 0x26, 0xc4, - 0xef, 0x74, 0x4c, 0xa6, 0x36, 0x29, 0x02, 0x81, 0x87, 0xe0, 0x15, 0x3c, 0x37, 0x5c, 0x6e, 0xb9, 0xb1, 0xb3, 0xc8, - 0xd3, 0x04, 0xe0, 0xc4, 0x0b, 0xbe, 0x05, 0x38, 0x4e, 0xbd, 0x2a, 0x64, 0xcf, 0x9e, 0x8b, 0xe9, 0x6c, 0x1e, 0x3c, - 0x24, 0xb4, 0x7f, 0x31, 0xe1, 0x37, 0xdd, 0x55, 0x72, 0x65, 0x6a, 0xdd, 0x9b, 0xe8, 0x2a, 0x97, 0x3b, 0x7d, 0x5a, - 0x71, 0x0c, 0x73, 0x06, 0xab, 0x80, 0x9c, 0xb3, 0x21, 0xbf, 0x3e, 0x07, 0xc0, 0x96, 0x95, 0xf0, 0x22, 0x7e, 0x1d, - 0xca, 0x6a, 0x01, 0xdc, 0x23, 0xe7, 0x91, 0xf9, 0xe5, 0xab, 0xed, 0x50, 0xce, 0x29, 0x0a, 0x63, 0x39, 0x35, 0x2d, - 0x29, 0x4e, 0x87, 0x9e, 0x82, 0xc9, 0xd4, 0x96, 0xbf, 0xb7, 0x89, 0xcb, 0xec, 0xcd, 0x24, 0x9c, 0xaf, 0x23, 0xdb, - 0xd6, 0xaa, 0x7b, 0xe8, 0x86, 0x60, 0xd0, 0xc7, 0x08, 0x5a, 0x36, 0xd7, 0x77, 0xeb, 0xc1, 0x40, 0x61, 0xfb, 0xd6, - 0x74, 0xd3, 0xa2, 0x53, 0x1c, 0x70, 0x66, 0xad, 0x6b, 0x54, 0xaa, 0x8a, 0x43, 0x2f, 0x79, 0xb7, 0xac, 0xca, 0x2e, - 0x4b, 0x2f, 0x04, 0xa9, 0x51, 0x57, 0x11, 0x22, 0xa5, 0x62, 0x87, 0xf7, 0xe4, 0xd7, 0xc0, 0xc4, 0x33, 0x2b, 0x47, - 0x69, 0x3c, 0x07, 0x98, 0x20, 0x85, 0xbe, 0x29, 0xbf, 0x02, 0xdc, 0xd0, 0x45, 0x14, 0x66, 0x6f, 0xe2, 0x2a, 0xa8, - 0xad, 0xa6, 0xdf, 0x3b, 0x38, 0xb1, 0xe7, 0x75, 0xbf, 0x9f, 0x12, 0x8d, 0x1f, 0x86, 0x5e, 0xe0, 0xdf, 0xe3, 0xe9, - 0xbe, 0x09, 0x52, 0xf3, 0xca, 0x03, 0xbc, 0xa2, 0xcb, 0xad, 0x4d, 0xb9, 0xa2, 0x71, 0x31, 0xaf, 0x11, 0x11, 0x3e, - 0x75, 0x14, 0xdb, 0x6d, 0x7e, 0x9c, 0xda, 0x18, 0x0c, 0x42, 0xb8, 0x6f, 0x65, 0xfc, 0x3e, 0xf1, 0xf2, 0x59, 0x34, - 0x07, 0x45, 0x69, 0xa6, 0x49, 0x42, 0x0a, 0xe9, 0x25, 0x40, 0x1f, 0x0d, 0x42, 0xad, 0xae, 0xfc, 0x23, 0xf1, 0x52, - 0x35, 0xad, 0xcd, 0x53, 0xac, 0x51, 0x20, 0x66, 0xd1, 0xbc, 0x61, 0x19, 0x1d, 0x92, 0xea, 0x72, 0x69, 0x9a, 0xf1, - 0x87, 0xd5, 0x0c, 0xd5, 0x8a, 0xa3, 0x26, 0xa8, 0x51, 0xba, 0x81, 0x0b, 0xe0, 0xdf, 0xe9, 0x8e, 0xa3, 0x1a, 0x45, - 0x8a, 0x06, 0x7c, 0x82, 0xc0, 0xb0, 0x66, 0xf3, 0x84, 0xb5, 0xa6, 0xae, 0x19, 0xfd, 0xbe, 0x8c, 0x13, 0x32, 0x49, - 0x48, 0xce, 0x87, 0xcb, 0xf5, 0x23, 0xa9, 0x2e, 0x80, 0x54, 0xb9, 0x62, 0xb3, 0x5e, 0x6f, 0x0e, 0x18, 0xbd, 0xb0, - 0x7e, 0x61, 0xe3, 0x0a, 0xce, 0x2f, 0x09, 0x73, 0x57, 0xfd, 0x08, 0xb3, 0x0c, 0xaa, 0x80, 0x34, 0x3f, 0x16, 0xbc, - 0x79, 0xee, 0x02, 0x51, 0xbf, 0x1e, 0xa9, 0x0b, 0xca, 0x2c, 0x9d, 0x5b, 0x44, 0x20, 0xe0, 0x35, 0xac, 0x9e, 0x40, - 0xb2, 0x2f, 0x1f, 0xfb, 0x34, 0xa3, 0x40, 0x75, 0x04, 0xa0, 0x6c, 0xd6, 0x0f, 0x61, 0xff, 0x80, 0x70, 0x42, 0xfd, - 0xcd, 0x1b, 0x39, 0x6b, 0x48, 0x1e, 0x48, 0x35, 0xe1, 0x31, 0x9c, 0x1a, 0x0b, 0x7c, 0x69, 0xd1, 0x9b, 0x0a, 0x5e, - 0x13, 0x1c, 0xf7, 0x02, 0xad, 0x7d, 0x0b, 0x38, 0x42, 0x04, 0x97, 0xa1, 0x89, 0xd3, 0xde, 0xae, 0x17, 0x20, 0xa1, - 0xb9, 0x85, 0x73, 0xfd, 0xd6, 0x05, 0x2d, 0x4e, 0x91, 0x93, 0x45, 0x17, 0x18, 0xe8, 0x82, 0xcc, 0x1b, 0xff, 0xaa, - 0x60, 0xe5, 0x02, 0x64, 0x2f, 0x15, 0x2b, 0x89, 0xd8, 0x76, 0xea, 0x8f, 0x52, 0xd9, 0x6f, 0xcf, 0xac, 0x09, 0xfc, - 0x32, 0xb1, 0x5f, 0x22, 0x93, 0x6f, 0x7a, 0x6c, 0xf2, 0x95, 0xb1, 0xd0, 0xa9, 0x65, 0x70, 0x4e, 0x8f, 0x0c, 0xce, - 0xbd, 0x9d, 0x55, 0x9b, 0x10, 0x86, 0x82, 0x24, 0xd0, 0x74, 0xe9, 0x61, 0xdd, 0xf4, 0xe7, 0x27, 0x2d, 0x7e, 0xad, - 0xda, 0xb7, 0xee, 0xc7, 0x21, 0x76, 0xf1, 0xcb, 0xc4, 0x33, 0xec, 0xa3, 0x3e, 0x70, 0x80, 0xc9, 0x88, 0x89, 0xcb, - 0x7e, 0x1f, 0x0a, 0x9b, 0x8d, 0xe7, 0xa3, 0xba, 0xf8, 0xb9, 0x78, 0x00, 0x28, 0x87, 0x0a, 0xec, 0x72, 0x28, 0x43, - 0x19, 0xb1, 0xa9, 0x2d, 0xf7, 0xfc, 0xfe, 0x32, 0xcc, 0x41, 0xde, 0xd1, 0x98, 0x38, 0x67, 0x20, 0x86, 0xc1, 0xd7, - 0xbf, 0x7b, 0xb2, 0x4f, 0x9b, 0xef, 0xce, 0xe0, 0xbb, 0xa3, 0xb3, 0x0f, 0xc8, 0x71, 0x73, 0xb6, 0x2e, 0x8b, 0xfb, - 0x34, 0x16, 0x67, 0xdf, 0x41, 0xea, 0x77, 0x67, 0x45, 0x79, 0xf6, 0x9d, 0xaa, 0xcc, 0x77, 0x67, 0xb4, 0xe0, 0x46, - 0xbf, 0x5b, 0x13, 0xef, 0x9f, 0x95, 0xa6, 0x3d, 0x5b, 0x42, 0x38, 0x96, 0x56, 0x3f, 0x82, 0x12, 0x51, 0x91, 0xa2, - 0xca, 0x50, 0x56, 0x6b, 0xc7, 0x79, 0x9f, 0x68, 0x78, 0x6c, 0x9a, 0x90, 0xb8, 0x5a, 0xc2, 0x3a, 0xd4, 0xb3, 0xd3, - 0x26, 0xd9, 0x71, 0x1e, 0xa8, 0x03, 0x22, 0xe7, 0xd7, 0xf9, 0x68, 0x4b, 0x5f, 0x83, 0x6f, 0x1d, 0x0e, 0xf9, 0x68, - 0x67, 0x7e, 0xfa, 0x64, 0xad, 0x94, 0xc1, 0x46, 0x8a, 0x51, 0x08, 0x89, 0xe2, 0xb6, 0x3d, 0x06, 0xc0, 0xff, 0xfe, - 0xe1, 0x40, 0xbf, 0x77, 0xf2, 0xb7, 0xda, 0x2d, 0xad, 0x7a, 0x7e, 0x68, 0x11, 0x66, 0xbc, 0xaa, 0x0d, 0x3b, 0xdb, - 0x5e, 0x02, 0x4a, 0xef, 0x9b, 0x06, 0x35, 0x45, 0xf4, 0x13, 0x56, 0x13, 0xab, 0x38, 0x2c, 0x48, 0x89, 0x43, 0x0c, - 0xc7, 0x68, 0x87, 0x1e, 0xa7, 0x8b, 0x9a, 0x27, 0xf7, 0x1d, 0x32, 0x6e, 0x7d, 0x1f, 0x90, 0x5c, 0x0a, 0xe7, 0x1f, - 0xbc, 0xd0, 0x60, 0xa2, 0x17, 0x79, 0x55, 0x64, 0x62, 0x24, 0x68, 0x94, 0xdf, 0x90, 0x38, 0x73, 0x86, 0xb5, 0x38, - 0x53, 0x08, 0x61, 0x21, 0xa1, 0x72, 0x17, 0x25, 0xa5, 0x07, 0x67, 0x4f, 0xf6, 0x65, 0xf3, 0x3b, 0x61, 0x42, 0x8c, - 0x16, 0x40, 0x83, 0xb3, 0x6b, 0x97, 0xf7, 0x10, 0x96, 0xb9, 0xf7, 0xfb, 0x9b, 0xbb, 0xbc, 0x80, 0xb8, 0xcc, 0x33, - 0xa9, 0x58, 0x2d, 0xcf, 0x80, 0x26, 0x4f, 0xc4, 0x67, 0x61, 0x25, 0xa7, 0x41, 0xd5, 0x51, 0xac, 0xde, 0xc6, 0x73, - 0x0f, 0x78, 0xbd, 0xdf, 0x27, 0x40, 0xe0, 0xee, 0xb3, 0xd7, 0xca, 0x2d, 0x95, 0xf4, 0xc8, 0x73, 0x0c, 0x91, 0x4c, - 0x80, 0xd7, 0x19, 0x82, 0x23, 0x85, 0xd5, 0x73, 0x13, 0xe4, 0x1f, 0x5f, 0x9f, 0x50, 0x7c, 0xd1, 0x3c, 0x8a, 0x1a, - 0x16, 0xb2, 0x04, 0x8e, 0x87, 0x64, 0x96, 0xcd, 0x91, 0x9a, 0x3c, 0x6d, 0x4f, 0x91, 0x8e, 0x4e, 0x2c, 0xf1, 0xdb, - 0x9a, 0x54, 0x2f, 0x52, 0x61, 0x97, 0xb4, 0xb3, 0x95, 0xb9, 0x17, 0xc2, 0x50, 0x25, 0xdc, 0x7b, 0x55, 0xcf, 0x42, - 0xb9, 0x29, 0x5a, 0x15, 0xb3, 0x87, 0x29, 0x31, 0xc3, 0x14, 0xeb, 0x2f, 0x6c, 0xf8, 0x4d, 0xe2, 0xc5, 0x60, 0xb8, - 0x5e, 0xf2, 0x72, 0xb6, 0x31, 0x0b, 0xe1, 0x70, 0xd8, 0x4c, 0x8a, 0xd9, 0x12, 0x62, 0x5b, 0x97, 0xf3, 0xc3, 0xa1, - 0xab, 0x65, 0x6b, 0xe1, 0xc1, 0x43, 0xd5, 0xc2, 0x4d, 0xc3, 0x72, 0xf8, 0x99, 0xcc, 0x62, 0x6c, 0x5f, 0xe3, 0x33, - 0xfb, 0xf3, 0x45, 0xf7, 0x2c, 0x41, 0xc6, 0x8d, 0x35, 0x70, 0x8d, 0xcd, 0xda, 0x1d, 0xae, 0x46, 0x40, 0xf2, 0xb8, - 0x1b, 0xfd, 0x5d, 0xd9, 0x49, 0x4e, 0x82, 0x84, 0xd1, 0x0a, 0xe1, 0x77, 0xdf, 0xf8, 0x13, 0x2d, 0xf6, 0xa0, 0xdd, - 0xc6, 0x96, 0x10, 0xd5, 0xb4, 0xe7, 0x72, 0xa5, 0x58, 0x9a, 0xb7, 0xd2, 0x86, 0xcc, 0x87, 0xf5, 0xb9, 0x6f, 0xe4, - 0x40, 0xc1, 0x18, 0xf1, 0xd4, 0x3a, 0x88, 0x66, 0x73, 0xe0, 0xbe, 0x40, 0xf3, 0x08, 0x4f, 0x2d, 0x48, 0x50, 0x66, - 0x6d, 0xd8, 0x4f, 0x92, 0x93, 0xe5, 0x71, 0xf8, 0x16, 0xfe, 0xe5, 0x33, 0x6c, 0x12, 0x53, 0x14, 0x8f, 0xbf, 0x55, - 0x8a, 0xff, 0x8e, 0x2d, 0x88, 0x60, 0xed, 0x46, 0xd4, 0x86, 0xbf, 0xe1, 0xdf, 0xc2, 0x3e, 0xc2, 0x7e, 0x43, 0x13, - 0x84, 0x01, 0xac, 0x3f, 0x13, 0x88, 0x0b, 0x0b, 0x41, 0x82, 0xbf, 0x55, 0x92, 0x7f, 0x4e, 0xf8, 0x6c, 0x51, 0x02, - 0x59, 0x1d, 0x46, 0xf1, 0x09, 0xc5, 0x44, 0x21, 0x0c, 0xb7, 0x84, 0xde, 0xd1, 0x7f, 0x23, 0x4a, 0xb2, 0x49, 0x6e, - 0xc5, 0x7a, 0x20, 0x93, 0x24, 0x98, 0x60, 0xe5, 0x85, 0xf2, 0x85, 0x7b, 0xa1, 0xd4, 0x5a, 0x0b, 0x5a, 0xbf, 0xfc, - 0x49, 0xe2, 0x19, 0xd0, 0x3d, 0x90, 0x31, 0xe8, 0x36, 0xa2, 0x9a, 0xe4, 0x98, 0x3e, 0x4a, 0xe7, 0x19, 0xa8, 0x80, - 0xce, 0xd6, 0x59, 0x58, 0x2f, 0x8b, 0x72, 0xd5, 0x0a, 0x0f, 0x95, 0xa5, 0x8f, 0xd4, 0x63, 0xcc, 0x0b, 0xf3, 0xe4, - 0x44, 0x3e, 0x78, 0x04, 0x68, 0x78, 0x94, 0xa7, 0x55, 0x47, 0x69, 0xfd, 0xc0, 0x32, 0x60, 0x04, 0x4e, 0x94, 0x01, - 0x8f, 0xb0, 0x0c, 0xcc, 0xd3, 0x2e, 0x43, 0x0d, 0x62, 0x8d, 0xaa, 0x2b, 0xb5, 0xc1, 0x9c, 0x28, 0x4a, 0x3e, 0xc5, - 0xd2, 0x0a, 0x63, 0x68, 0xea, 0xca, 0x23, 0xeb, 0x25, 0x27, 0xec, 0xc9, 0x6e, 0x20, 0xdd, 0xc2, 0x46, 0x81, 0x0b, - 0xba, 0x96, 0x25, 0xca, 0x45, 0xb7, 0x8c, 0x28, 0x13, 0x21, 0xf5, 0xb3, 0x87, 0x33, 0xad, 0xf6, 0x1b, 0x3b, 0x69, - 0xdf, 0x1e, 0x29, 0x7a, 0xc1, 0x40, 0x7c, 0xda, 0x23, 0xa5, 0x9e, 0x35, 0x72, 0x19, 0xd8, 0xd2, 0xa5, 0xaa, 0xe7, - 0xbf, 0x41, 0xf9, 0x0e, 0x66, 0xc6, 0xd9, 0xec, 0x77, 0xbd, 0xb9, 0x3d, 0xd9, 0xd7, 0xcd, 0xef, 0xac, 0xd7, 0x83, - 0xad, 0x41, 0x26, 0xbe, 0x50, 0xd4, 0x53, 0x56, 0x21, 0x56, 0x64, 0xf6, 0xbf, 0x85, 0xf7, 0x3b, 0xbc, 0x35, 0x42, - 0xb3, 0x32, 0x1e, 0xe6, 0xa3, 0x27, 0x7b, 0xd1, 0xfc, 0xde, 0x59, 0xb6, 0x95, 0xab, 0x92, 0xd9, 0x7e, 0x3f, 0x4a, - 0x9a, 0xb3, 0xc7, 0x6b, 0x24, 0x75, 0x80, 0x8f, 0xd7, 0x67, 0xf8, 0x48, 0x25, 0x94, 0x5a, 0x50, 0xd5, 0xa0, 0xf5, - 0xb1, 0xdf, 0x5b, 0xcf, 0xe9, 0xe3, 0xc7, 0x72, 0xba, 0x25, 0x45, 0x18, 0x3f, 0x30, 0x98, 0xb2, 0x13, 0xa7, 0x2e, - 0x79, 0x33, 0xa4, 0x77, 0xdd, 0x2a, 0xa9, 0xcb, 0x1e, 0x25, 0x82, 0x50, 0x07, 0xeb, 0x17, 0xfb, 0x21, 0xcc, 0x6c, - 0xd1, 0x1f, 0x36, 0xab, 0x39, 0xa1, 0x20, 0x02, 0x44, 0xab, 0xbc, 0x0f, 0x1c, 0x93, 0x84, 0x59, 0x73, 0x43, 0xba, - 0xf5, 0xe6, 0x4a, 0x7b, 0x25, 0x05, 0xf4, 0x73, 0x90, 0xb9, 0x7d, 0x74, 0xcb, 0x55, 0xcb, 0x3c, 0x97, 0xb6, 0x1c, - 0xb0, 0x68, 0x21, 0x3a, 0xb3, 0x73, 0xe9, 0x70, 0xf0, 0x1f, 0xd4, 0x95, 0xa8, 0x22, 0x82, 0x8e, 0xa2, 0x05, 0xa3, - 0xd5, 0xaa, 0x5d, 0x4e, 0x36, 0x15, 0xb2, 0x25, 0x11, 0x4e, 0x94, 0xec, 0x95, 0x50, 0x1f, 0xe5, 0x6a, 0xcf, 0x34, - 0xc4, 0x9f, 0x09, 0xd8, 0xb4, 0xc1, 0xdf, 0x02, 0xf7, 0x32, 0x38, 0x33, 0xed, 0xd3, 0x30, 0x02, 0x22, 0x73, 0x08, - 0xf6, 0xf3, 0xbb, 0x1e, 0x54, 0xf0, 0xa0, 0x23, 0xfd, 0x55, 0x3d, 0x2b, 0xf0, 0xcc, 0x3d, 0xf1, 0xfc, 0xf5, 0x89, - 0xf4, 0x22, 0x87, 0x07, 0x9a, 0xfb, 0x30, 0xe3, 0x2f, 0xca, 0x32, 0xdc, 0x8d, 0x96, 0x65, 0xb1, 0xf2, 0x22, 0xbd, - 0x8f, 0x67, 0x52, 0x0c, 0x24, 0x3a, 0xcc, 0x8c, 0xae, 0x62, 0x1d, 0xe7, 0x30, 0xee, 0xed, 0x49, 0x58, 0xa1, 0xfd, - 0xb3, 0xc4, 0x5e, 0x17, 0x00, 0xe0, 0x90, 0x35, 0x68, 0x85, 0x77, 0xba, 0xbd, 0xdd, 0xe3, 0x92, 0x12, 0xc5, 0x8d, - 0x9a, 0x9f, 0xd5, 0xd0, 0x32, 0x41, 0x2d, 0xb3, 0xee, 0x64, 0x32, 0x45, 0x12, 0xf8, 0x36, 0xec, 0x35, 0x2b, 0xf2, - 0x79, 0x23, 0xb7, 0x87, 0x77, 0xe1, 0x4a, 0xc4, 0xda, 0x82, 0x4e, 0x3a, 0x32, 0x0e, 0xf7, 0x42, 0x73, 0x23, 0xdd, - 0x3f, 0xa9, 0x92, 0xb0, 0x14, 0x31, 0xdc, 0x02, 0xd9, 0x5e, 0x6d, 0x2b, 0x41, 0x09, 0x24, 0xb0, 0x1f, 0x4a, 0xb1, - 0x4c, 0xb7, 0x02, 0xc0, 0x1c, 0xf8, 0x9f, 0x12, 0x86, 0xd0, 0xdd, 0x79, 0x88, 0x57, 0x8d, 0xbc, 0x6f, 0x10, 0x82, - 0xfd, 0x15, 0xc8, 0x69, 0xc0, 0x20, 0x52, 0x8c, 0x64, 0xc1, 0x40, 0x02, 0x90, 0xf3, 0x35, 0x98, 0xe4, 0xa6, 0xb9, - 0xe7, 0x07, 0xb9, 0xee, 0x60, 0xda, 0x07, 0xdd, 0x8b, 0x6b, 0xcd, 0x72, 0xf0, 0x8a, 0x89, 0xf8, 0xdf, 0x6b, 0xaf, - 0x64, 0x39, 0xcb, 0xfc, 0xc6, 0x5c, 0x74, 0x32, 0xb8, 0x6a, 0x08, 0xbf, 0x98, 0x65, 0x73, 0x1e, 0xcd, 0x32, 0x1d, - 0xea, 0x5f, 0x34, 0x47, 0xa5, 0x00, 0x86, 0x3a, 0x5e, 0x80, 0x35, 0xde, 0x95, 0x6e, 0x5a, 0xf1, 0x48, 0x63, 0x8c, - 0x82, 0x0a, 0x1d, 0x84, 0xfe, 0x5e, 0x03, 0xbc, 0x06, 0x93, 0xdc, 0x08, 0x95, 0x0f, 0x2e, 0xe8, 0x86, 0x6e, 0xb9, - 0x72, 0x09, 0x6a, 0x52, 0xb5, 0xfc, 0x72, 0x84, 0x7a, 0x57, 0x4b, 0x2e, 0xd5, 0xe6, 0x53, 0xa3, 0xac, 0x11, 0x64, - 0x72, 0x94, 0x7e, 0x9f, 0x72, 0xe1, 0x56, 0xc6, 0x64, 0x7d, 0x38, 0x78, 0x05, 0x37, 0x35, 0x7e, 0x95, 0x13, 0x8b, - 0xa8, 0x3d, 0x24, 0xc2, 0xd6, 0x6e, 0x85, 0xee, 0x3d, 0x6e, 0x94, 0xe6, 0x51, 0xb6, 0x89, 0x45, 0xe5, 0xf5, 0x12, - 0xb0, 0x16, 0xf7, 0x80, 0x0c, 0x95, 0x96, 0x7e, 0xc5, 0x0a, 0x80, 0x0c, 0x90, 0xc2, 0xc6, 0x0f, 0x48, 0x7b, 0xf5, - 0xc1, 0x4b, 0xfd, 0x7e, 0xdf, 0x98, 0xf2, 0xdf, 0x3f, 0xe4, 0xc0, 0x4c, 0x28, 0xca, 0x7a, 0x07, 0x13, 0x08, 0xae, - 0x9d, 0xa4, 0x3d, 0xab, 0xf9, 0xf5, 0xba, 0xf6, 0x80, 0xd4, 0xca, 0xb7, 0x98, 0xab, 0x5e, 0xd9, 0x17, 0x9b, 0x7d, - 0x5a, 0xdd, 0x18, 0x8d, 0x83, 0x60, 0x69, 0xf5, 0x46, 0xab, 0x1c, 0xf2, 0x86, 0x57, 0x20, 0x52, 0x59, 0x57, 0xd7, - 0xca, 0xb9, 0xba, 0x16, 0x1c, 0x09, 0x64, 0x4b, 0x9e, 0xc3, 0x7f, 0x21, 0xf7, 0xca, 0xc3, 0xa1, 0xf0, 0xfb, 0xfd, - 0x74, 0x46, 0x5a, 0x59, 0xa0, 0x4c, 0x5b, 0xd7, 0x5e, 0xe8, 0x1f, 0x0e, 0x3f, 0x80, 0xd7, 0x88, 0x7f, 0x38, 0x94, - 0xfd, 0xfe, 0x47, 0x73, 0x93, 0x39, 0x1f, 0x2b, 0xa5, 0xec, 0x25, 0x2a, 0xdd, 0xdf, 0x24, 0xbc, 0xf7, 0xbf, 0x47, - 0xff, 0x7b, 0x74, 0xd9, 0x53, 0x01, 0x60, 0x09, 0x9f, 0xe1, 0x0d, 0x9d, 0xa9, 0xcb, 0x39, 0x93, 0xee, 0xee, 0xca, - 0x0f, 0xbd, 0xa7, 0xf1, 0xe1, 0x7b, 0x73, 0xd3, 0xc6, 0x5f, 0xab, 0x23, 0x4d, 0x42, 0xc7, 0x45, 0xff, 0x70, 0xf8, - 0x94, 0x68, 0x7d, 0x5a, 0xaa, 0xf4, 0x69, 0x0a, 0x3c, 0xc9, 0xb0, 0xe1, 0xba, 0x85, 0xe9, 0x68, 0x7e, 0xdc, 0x7c, - 0x95, 0xbc, 0x38, 0x4b, 0xe1, 0xda, 0x9b, 0xcf, 0xd2, 0xf9, 0x14, 0xac, 0x2b, 0xc3, 0x7c, 0x56, 0xcf, 0x03, 0x48, - 0x1d, 0x42, 0x9a, 0x35, 0x0d, 0xff, 0x56, 0xb9, 0x82, 0xb7, 0xf6, 0x78, 0x37, 0x18, 0x51, 0xea, 0x48, 0x9f, 0xb4, - 0x21, 0x74, 0x49, 0x25, 0xff, 0x51, 0xe4, 0x31, 0xc6, 0x6c, 0xbc, 0x22, 0xb2, 0xcf, 0x22, 0x7f, 0x59, 0x00, 0x60, - 0x11, 0x20, 0x20, 0xa7, 0x73, 0x47, 0x12, 0xff, 0x39, 0xf9, 0xf6, 0x8f, 0xe9, 0xd2, 0x3e, 0x94, 0xc5, 0x5d, 0x29, - 0xaa, 0xea, 0xa8, 0xb4, 0x9d, 0x2d, 0xd7, 0x03, 0x7d, 0x68, 0xbf, 0x2f, 0xe9, 0x43, 0x53, 0x0c, 0x45, 0x81, 0x5b, - 0x63, 0x6f, 0x9a, 0x72, 0x45, 0x53, 0x3d, 0x32, 0xd6, 0xcf, 0xef, 0x77, 0x6f, 0x62, 0x2f, 0xf5, 0x83, 0x14, 0x04, - 0x61, 0x8d, 0x9f, 0x94, 0x22, 0x09, 0x9c, 0xcf, 0x30, 0x95, 0xf8, 0x74, 0x29, 0x55, 0xfe, 0x30, 0xd2, 0x7c, 0x98, - 0x82, 0x5e, 0xf6, 0x1f, 0x15, 0xcc, 0x7f, 0xdd, 0x1e, 0xac, 0x4f, 0xeb, 0x32, 0x8d, 0x2a, 0xa2, 0xca, 0x0b, 0x53, - 0x6d, 0x02, 0x11, 0xfc, 0x5a, 0x58, 0x24, 0xbf, 0x3e, 0x39, 0x12, 0x34, 0x66, 0xb2, 0x7c, 0x3c, 0x72, 0xbf, 0xb0, - 0xaf, 0x5c, 0xc7, 0xf3, 0x3f, 0x37, 0xf3, 0x7f, 0x80, 0xce, 0x90, 0xc5, 0x35, 0xb7, 0x0c, 0x16, 0x38, 0xfb, 0xa5, - 0xab, 0x07, 0xfc, 0xcd, 0x3c, 0x71, 0x0d, 0x1c, 0xcc, 0xd7, 0xe8, 0xaa, 0x98, 0xce, 0x8a, 0x01, 0x10, 0xd8, 0xfa, - 0x8d, 0x35, 0x27, 0x5e, 0x5b, 0x3c, 0x57, 0x72, 0x41, 0xe8, 0xeb, 0x2a, 0xcc, 0xc6, 0x55, 0xb1, 0xa9, 0x44, 0xb1, - 0xa9, 0x7b, 0xa4, 0x96, 0xcd, 0xa7, 0xb5, 0xad, 0x90, 0xfd, 0x49, 0xb4, 0x68, 0xbb, 0x0c, 0xd5, 0x64, 0x94, 0xa5, - 0xeb, 0x29, 0x90, 0xea, 0x05, 0x70, 0x16, 0x99, 0x57, 0xbe, 0x38, 0x7b, 0xc0, 0x16, 0x8d, 0xa7, 0xc0, 0x88, 0x4a, - 0x7f, 0xe4, 0x8d, 0xd1, 0xe9, 0x89, 0x7e, 0x3f, 0x9f, 0x52, 0xc8, 0xd7, 0x4f, 0x80, 0xc9, 0x55, 0xcb, 0x05, 0xe8, - 0xcb, 0x50, 0x07, 0x95, 0x28, 0xb5, 0x62, 0x18, 0xb1, 0xf0, 0x93, 0x40, 0xf6, 0x66, 0x0a, 0x6a, 0x56, 0x51, 0x12, - 0x2a, 0x51, 0x29, 0xd9, 0x9a, 0xa0, 0x96, 0xde, 0x17, 0x45, 0xbd, 0xaf, 0xc0, 0x51, 0x32, 0xd2, 0x66, 0x39, 0x65, - 0xc6, 0x45, 0x99, 0x8b, 0x7e, 0xb0, 0x7f, 0x57, 0x9e, 0xdf, 0xc8, 0x7c, 0x96, 0xfb, 0x8e, 0xce, 0x69, 0x3b, 0x2e, - 0x50, 0xe6, 0x96, 0xd3, 0x56, 0x4b, 0x1e, 0x93, 0xf7, 0x2c, 0xd8, 0xf6, 0x5f, 0x24, 0xc8, 0xab, 0x08, 0xf3, 0x09, - 0x55, 0x36, 0xff, 0x80, 0x30, 0x5b, 0x1c, 0xd8, 0x63, 0x17, 0x26, 0x22, 0xbd, 0x05, 0x4b, 0x62, 0x98, 0x95, 0x22, - 0x8c, 0x77, 0xe0, 0xfd, 0xb3, 0xa9, 0xc4, 0xe8, 0x0c, 0x9d, 0xdc, 0xcf, 0x1e, 0xd2, 0x3a, 0x39, 0x7b, 0xf3, 0xea, - 0xec, 0xbb, 0xde, 0xa0, 0x18, 0xa5, 0xf1, 0xa0, 0xf7, 0xdd, 0xd9, 0x6a, 0x03, 0x10, 0x99, 0xe2, 0x2c, 0x26, 0x53, - 0x9a, 0x88, 0xcf, 0xc8, 0x30, 0x78, 0x56, 0x27, 0xe2, 0x8c, 0x26, 0xa6, 0xfb, 0x1a, 0xa5, 0xc9, 0xb7, 0xa3, 0x30, - 0x87, 0x97, 0x4b, 0xb1, 0xa9, 0x44, 0x0c, 0x76, 0x4a, 0x35, 0xcf, 0xf2, 0xf6, 0x59, 0x9c, 0x8f, 0x3a, 0x64, 0x95, - 0x0e, 0xd0, 0xed, 0x89, 0xb4, 0xab, 0xd2, 0x15, 0x10, 0x7a, 0x00, 0x9c, 0x74, 0xe5, 0xcf, 0xc3, 0x41, 0x24, 0x10, - 0x6a, 0xc1, 0x9c, 0x4c, 0x23, 0xba, 0x21, 0xbd, 0xc4, 0x3e, 0x03, 0xb3, 0x90, 0xd2, 0x3c, 0xb8, 0xb9, 0x5a, 0xb4, - 0xdc, 0x15, 0x2b, 0x47, 0x61, 0xb5, 0x16, 0x51, 0x8d, 0x54, 0xc7, 0xe0, 0xbc, 0x03, 0x11, 0x00, 0x8a, 0x11, 0x3c, - 0xe3, 0x51, 0xbf, 0x1f, 0xa9, 0xa0, 0x9c, 0x84, 0x7e, 0x51, 0xe8, 0x97, 0xc6, 0xa0, 0x8c, 0xf9, 0xbb, 0x50, 0x13, - 0x03, 0xd4, 0x5b, 0x1e, 0x2a, 0x8e, 0x00, 0x5c, 0xce, 0x11, 0x33, 0xce, 0x7b, 0xdc, 0x45, 0xe3, 0x54, 0xbc, 0x13, - 0xea, 0x3a, 0x58, 0x2a, 0xd4, 0x79, 0x53, 0x1f, 0xe9, 0x39, 0x69, 0x12, 0x34, 0x88, 0x1b, 0x78, 0xbc, 0x1a, 0x02, - 0xaa, 0x95, 0x90, 0x7a, 0x0b, 0x9d, 0x52, 0xd5, 0x21, 0xb0, 0x06, 0xb8, 0x44, 0x61, 0x5b, 0x61, 0x72, 0x44, 0x9b, - 0xb2, 0x14, 0xf9, 0x11, 0x1b, 0xb4, 0x4b, 0x46, 0xa6, 0x0e, 0xae, 0xff, 0xc3, 0xdb, 0xb7, 0x70, 0xb7, 0x6d, 0x63, - 0xeb, 0xfe, 0x15, 0x8b, 0x37, 0x55, 0x89, 0x08, 0x92, 0x25, 0x37, 0xe9, 0xb4, 0x94, 0x21, 0x1d, 0x37, 0x8f, 0x36, - 0x9d, 0xe6, 0xd1, 0x38, 0xed, 0x74, 0x46, 0x57, 0xc7, 0xa5, 0x49, 0xd8, 0x62, 0x43, 0x03, 0x2a, 0x49, 0xf9, 0x51, - 0x89, 0xff, 0xfd, 0xae, 0xbd, 0xf1, 0x24, 0x45, 0x3b, 0x99, 0xb9, 0xe7, 0xde, 0x95, 0xb5, 0x62, 0x11, 0x04, 0xf1, - 0xc6, 0xc6, 0xc6, 0x7e, 0x7c, 0xbb, 0x19, 0xcb, 0x49, 0x85, 0xfe, 0x75, 0x75, 0x01, 0x9c, 0x17, 0xc6, 0xb7, 0x6e, - 0x66, 0xcb, 0x75, 0xb4, 0xeb, 0xd2, 0xc5, 0x2c, 0x29, 0x78, 0xb9, 0x96, 0xa2, 0xcc, 0xae, 0xf9, 0x4f, 0xf6, 0x65, - 0x33, 0x80, 0x14, 0xda, 0x91, 0xbe, 0x6e, 0x77, 0x47, 0x8b, 0x71, 0x6c, 0x39, 0xbe, 0xa5, 0xd2, 0x9d, 0x1e, 0x55, - 0x2f, 0x6e, 0xb6, 0xce, 0xb5, 0xca, 0xd2, 0x94, 0x8b, 0x57, 0x22, 0xcd, 0x12, 0x2f, 0x39, 0xd6, 0x01, 0xaa, 0x5d, - 0xe4, 0x2b, 0x17, 0x1b, 0xf9, 0x79, 0x56, 0x62, 0xc0, 0xe0, 0x46, 0xa3, 0x5a, 0xa1, 0xa6, 0x4c, 0xe0, 0x0b, 0xf9, - 0x1e, 0x23, 0x6e, 0xb3, 0x32, 0x01, 0x86, 0x1f, 0x13, 0xf5, 0x25, 0x3d, 0x85, 0x28, 0x0f, 0x2a, 0x1e, 0xf7, 0x73, - 0x8e, 0x88, 0xd7, 0x56, 0x65, 0x0e, 0x4c, 0xb6, 0x56, 0x41, 0x22, 0xd8, 0x5d, 0xb6, 0xd0, 0x8b, 0x68, 0xa9, 0xee, - 0x42, 0xbd, 0x78, 0xb7, 0xeb, 0x25, 0x8a, 0x0e, 0x38, 0xf9, 0x69, 0xf0, 0x32, 0xce, 0x72, 0x9e, 0x1e, 0x54, 0xf2, - 0x40, 0x6d, 0xa8, 0x03, 0xe5, 0xcc, 0x01, 0x3b, 0xef, 0xcb, 0xea, 0x40, 0xaf, 0xe9, 0x03, 0xdd, 0xce, 0x03, 0xb8, - 0x60, 0xe0, 0xce, 0xbd, 0xcc, 0xae, 0xb9, 0x38, 0x00, 0x65, 0xa0, 0x35, 0x1e, 0xa8, 0x45, 0x35, 0x52, 0x13, 0xa3, - 0x03, 0x57, 0x27, 0xfa, 0x60, 0x0e, 0xe8, 0xf7, 0x10, 0x2b, 0xbc, 0xf5, 0x76, 0xad, 0x0f, 0xda, 0x80, 0xfe, 0xb4, - 0x32, 0x7d, 0xd0, 0xd1, 0xe2, 0x55, 0x48, 0xe0, 0xc6, 0x90, 0x6a, 0xa4, 0x56, 0x23, 0xab, 0x40, 0xf1, 0x86, 0xb7, - 0x78, 0xf7, 0xae, 0x25, 0x5b, 0xef, 0x25, 0x02, 0x7b, 0x65, 0xa2, 0x8a, 0x33, 0x71, 0xe2, 0xa5, 0xf2, 0x5a, 0x3b, - 0xc9, 0x08, 0xe3, 0x5b, 0x56, 0x52, 0x7f, 0x87, 0x98, 0x5b, 0xa4, 0x39, 0x0c, 0x5e, 0x84, 0x15, 0x99, 0xf3, 0x7e, - 0x5f, 0xce, 0x65, 0x54, 0xce, 0xc5, 0x61, 0x19, 0x29, 0x84, 0xb6, 0xfb, 0x44, 0x40, 0x0f, 0x4a, 0x80, 0x7c, 0x01, - 0x50, 0xf5, 0x90, 0xf0, 0xe7, 0x21, 0xa9, 0x4f, 0xa7, 0xd0, 0xa7, 0xd0, 0xd6, 0x2b, 0x5e, 0x41, 0x55, 0xdd, 0x18, - 0xd9, 0x46, 0x05, 0x2d, 0x1e, 0xcb, 0xb3, 0xda, 0x30, 0x36, 0xa7, 0xd6, 0xbb, 0xde, 0x6c, 0x30, 0x65, 0x73, 0xa1, - 0x56, 0x61, 0x48, 0xa2, 0x9b, 0xd2, 0x0b, 0x1f, 0x62, 0xb1, 0xb2, 0x5a, 0x9b, 0xdf, 0xc4, 0xfe, 0xc8, 0x44, 0x8a, - 0xfb, 0xd9, 0x12, 0xe7, 0x2e, 0x1e, 0xcf, 0xab, 0xbe, 0xd6, 0xd2, 0x22, 0xd3, 0xe6, 0x3b, 0x7d, 0x19, 0xd2, 0x54, - 0xd4, 0x90, 0x46, 0x9d, 0x19, 0x74, 0xdf, 0x2e, 0xaf, 0xa8, 0x46, 0x98, 0x00, 0xaf, 0x74, 0x06, 0xdd, 0x68, 0x3c, - 0x10, 0x45, 0x35, 0x2a, 0x36, 0x42, 0x20, 0xda, 0x30, 0xe4, 0x98, 0x5b, 0x42, 0x92, 0xfd, 0xc5, 0xbf, 0x53, 0xc1, - 0x15, 0x8a, 0xf8, 0xc6, 0xc0, 0x79, 0x57, 0xd6, 0xb3, 0xbb, 0x8e, 0xfc, 0x9c, 0x58, 0x58, 0xed, 0x3f, 0x34, 0x8f, - 0x5a, 0xe3, 0x2c, 0xa0, 0xad, 0x69, 0x75, 0xc3, 0xe1, 0x1e, 0xd5, 0xb1, 0x28, 0x0d, 0x20, 0xb1, 0x47, 0x96, 0x8b, - 0xd6, 0x31, 0x83, 0x06, 0xf4, 0xb7, 0xd9, 0xd5, 0xe6, 0x0a, 0x51, 0xdb, 0x4a, 0x64, 0x9d, 0xa4, 0xf2, 0x2f, 0x69, - 0x8f, 0xba, 0xb6, 0xa7, 0xf2, 0xbf, 0x6d, 0x53, 0xe5, 0xd0, 0x02, 0xc9, 0x63, 0x37, 0xe7, 0x81, 0xea, 0x48, 0x10, - 0x05, 0x6a, 0xeb, 0x05, 0x53, 0xef, 0x94, 0x29, 0x3a, 0x90, 0x9f, 0x0b, 0x73, 0x86, 0x7d, 0xc6, 0x11, 0x63, 0x96, - 0x4a, 0x0c, 0xa6, 0x3e, 0xc6, 0xa8, 0xa6, 0xb5, 0x02, 0x74, 0xfd, 0x74, 0x0b, 0x7f, 0xa2, 0xa2, 0x46, 0x43, 0xad, - 0x91, 0x14, 0x8a, 0x26, 0x2a, 0xe8, 0x58, 0x5a, 0xe8, 0x60, 0x0a, 0x9d, 0x44, 0xc2, 0x12, 0xd0, 0x30, 0x21, 0x3a, - 0xa9, 0xc0, 0x5b, 0x03, 0x38, 0xf3, 0x71, 0x51, 0x6e, 0x0a, 0x6d, 0x30, 0xf7, 0x43, 0x7c, 0xcd, 0x5f, 0x3d, 0x77, - 0x46, 0xf5, 0x2d, 0x6b, 0x7d, 0x4f, 0x0b, 0xf2, 0x43, 0xc8, 0x29, 0x3a, 0x30, 0xb1, 0xd9, 0x16, 0x8d, 0x31, 0xca, - 0x5a, 0x87, 0xba, 0x78, 0xab, 0xe3, 0xaf, 0x68, 0x13, 0xbc, 0x07, 0x3c, 0x45, 0xb4, 0xe1, 0xa1, 0x30, 0x56, 0xd5, - 0xf8, 0x54, 0xb2, 0x96, 0x1e, 0xac, 0xe0, 0xe9, 0x26, 0xe1, 0x21, 0xe8, 0x91, 0x08, 0x9b, 0x85, 0xc5, 0x22, 0x5e, - 0xc2, 0x71, 0x52, 0x10, 0x50, 0x3b, 0xe8, 0x2b, 0xf8, 0x62, 0x89, 0xee, 0xaf, 0x12, 0x3d, 0xc0, 0xd0, 0x82, 0xb8, - 0x19, 0xfa, 0x74, 0x74, 0x15, 0xaf, 0x1b, 0x2a, 0x12, 0xbe, 0x28, 0xc0, 0x76, 0x48, 0xa9, 0xa7, 0x40, 0x0b, 0x95, - 0x28, 0xfd, 0x30, 0xf0, 0x1d, 0x1a, 0xf8, 0x5a, 0xeb, 0x00, 0x0d, 0xfd, 0x8c, 0x69, 0x6a, 0x9d, 0xa1, 0xf2, 0xb9, - 0x77, 0xcf, 0x8c, 0x56, 0x73, 0x0b, 0xc6, 0xa0, 0x6f, 0xa3, 0x29, 0x8a, 0x73, 0xf2, 0x79, 0x50, 0xc4, 0x69, 0x16, - 0xe7, 0xe0, 0xb7, 0x19, 0x17, 0x98, 0x31, 0x89, 0x2b, 0x7e, 0x29, 0x0b, 0xd0, 0x76, 0xe7, 0x2a, 0xb5, 0xae, 0x41, - 0x40, 0xf6, 0x03, 0x58, 0xbd, 0x34, 0x74, 0x54, 0xce, 0xbb, 0x4b, 0x9b, 0x42, 0xc4, 0x22, 0x04, 0x9b, 0x66, 0xba, - 0x62, 0x27, 0xa1, 0xd2, 0xe6, 0x40, 0x7c, 0x23, 0x34, 0xee, 0x9f, 0x86, 0xb1, 0xd5, 0x14, 0x5b, 0xbb, 0xb7, 0xdd, - 0xee, 0xb7, 0xd2, 0x4b, 0xa7, 0x39, 0xe9, 0x31, 0xf6, 0x5b, 0x19, 0x16, 0x23, 0xdb, 0x11, 0x02, 0x4b, 0xce, 0xfb, - 0xd4, 0x7f, 0x45, 0xcb, 0x45, 0x02, 0xa6, 0x23, 0x3a, 0x42, 0x2e, 0x50, 0x76, 0x8c, 0xe2, 0x0e, 0x0c, 0xae, 0xe8, - 0xf7, 0xc1, 0x2a, 0xc3, 0x5c, 0x48, 0x56, 0x24, 0x65, 0xf0, 0x3c, 0xf5, 0x30, 0xe0, 0x37, 0x4c, 0x99, 0xbb, 0x28, - 0xeb, 0xd3, 0x15, 0x99, 0xa6, 0xc8, 0x40, 0x6c, 0xc2, 0x6d, 0x96, 0x46, 0x89, 0x12, 0x91, 0xad, 0xd0, 0x3f, 0xd2, - 0x50, 0x2c, 0x1d, 0xae, 0x17, 0xa9, 0x12, 0xa1, 0x62, 0x91, 0xe2, 0x49, 0x9d, 0xd6, 0xe9, 0x08, 0xe3, 0x4d, 0x82, - 0x52, 0xae, 0x86, 0x81, 0x2a, 0xa9, 0x5e, 0x0a, 0xdb, 0x62, 0xb7, 0xd3, 0x17, 0x2b, 0xb1, 0x88, 0x97, 0xf8, 0x52, - 0xe0, 0x28, 0xfe, 0x9d, 0x7b, 0xb1, 0xa6, 0xd4, 0xf6, 0xa0, 0x76, 0x44, 0x09, 0xfd, 0x3b, 0x87, 0x8b, 0xc4, 0x77, - 0x52, 0xc7, 0xfd, 0x43, 0x8b, 0x90, 0x33, 0x75, 0x90, 0x1a, 0x6e, 0x68, 0x4f, 0xf8, 0x6f, 0xb8, 0x3e, 0xe3, 0x8c, - 0xde, 0x54, 0x33, 0x6a, 0xfc, 0x5e, 0x0f, 0xcf, 0x18, 0xf5, 0xd9, 0xc0, 0x61, 0x85, 0x28, 0xb4, 0x61, 0xb3, 0x52, - 0x89, 0x16, 0x86, 0x52, 0xfd, 0x25, 0x54, 0xcc, 0xb8, 0x33, 0xa3, 0x2c, 0x19, 0x9f, 0x96, 0xc7, 0x62, 0x3a, 0x18, - 0x94, 0xa4, 0x32, 0x16, 0x7a, 0x70, 0x3d, 0xf0, 0xfc, 0x7b, 0xe0, 0x16, 0xe2, 0xc1, 0x21, 0x8b, 0x21, 0x37, 0xe0, - 0xf8, 0x2d, 0x4e, 0xae, 0x1a, 0x95, 0x2a, 0x78, 0x35, 0x51, 0x2d, 0xf8, 0x7b, 0x19, 0x06, 0xe8, 0x93, 0x14, 0x80, - 0xc9, 0x60, 0xca, 0x6f, 0x41, 0xa2, 0x74, 0xa6, 0x6e, 0x48, 0xbf, 0x88, 0x82, 0x5f, 0xf0, 0x82, 0x8b, 0xc4, 0x15, - 0x60, 0x79, 0x07, 0xdb, 0xeb, 0xa8, 0xa2, 0x0a, 0x88, 0xd7, 0xf4, 0x38, 0xe2, 0xc6, 0xfb, 0xcf, 0xf4, 0xd8, 0x02, - 0xb5, 0x5a, 0xc7, 0x06, 0x9f, 0x39, 0x06, 0x17, 0x74, 0x2d, 0xb1, 0x35, 0x54, 0xc3, 0x8a, 0xc0, 0xc0, 0x05, 0x1c, - 0x84, 0x25, 0x8a, 0x63, 0x2b, 0x79, 0x45, 0x1a, 0x52, 0xda, 0x07, 0x86, 0xa3, 0x4d, 0x72, 0x7c, 0x9b, 0x65, 0x37, - 0x81, 0x8b, 0x65, 0xe7, 0xa4, 0x99, 0x58, 0x36, 0x78, 0x9f, 0x37, 0xe7, 0xd7, 0xfd, 0x43, 0x42, 0x55, 0xb0, 0x1b, - 0xde, 0x0e, 0x76, 0xe3, 0x84, 0x5f, 0x0b, 0xb1, 0xd4, 0xf1, 0x59, 0xcc, 0x25, 0xcb, 0x6f, 0xad, 0x77, 0x4b, 0x92, - 0x5a, 0x01, 0xed, 0xb3, 0x2c, 0xa8, 0x89, 0x00, 0x74, 0x3f, 0xfc, 0x05, 0x42, 0x67, 0xf8, 0xdb, 0x63, 0x70, 0x45, - 0x0a, 0xef, 0x1d, 0x02, 0x61, 0x4d, 0x37, 0xf7, 0x6a, 0x03, 0xbe, 0x18, 0xf7, 0x67, 0x4c, 0x3d, 0xfd, 0x36, 0x93, - 0xfb, 0xba, 0x6e, 0x8f, 0x2c, 0xc3, 0x47, 0xb8, 0x52, 0x00, 0x2c, 0x13, 0xfe, 0x62, 0x6c, 0x49, 0xf5, 0x09, 0xc0, - 0xa9, 0xe9, 0x88, 0x3e, 0x41, 0x60, 0xe0, 0x94, 0x68, 0x31, 0xba, 0x56, 0x8e, 0x68, 0x06, 0x69, 0x4d, 0xb7, 0xc2, - 0x78, 0xeb, 0x41, 0x0b, 0x3d, 0xd3, 0x70, 0xe2, 0x3f, 0x68, 0xe6, 0x55, 0x01, 0x01, 0xb4, 0x32, 0x82, 0xb7, 0xd6, - 0x47, 0x73, 0x84, 0xf8, 0x84, 0x25, 0xd1, 0x84, 0xc5, 0x33, 0xc5, 0x8f, 0x09, 0xdd, 0x36, 0xb5, 0x4d, 0x1f, 0x90, - 0xfe, 0xe2, 0x9a, 0xf5, 0x53, 0x56, 0xb5, 0x6f, 0x0f, 0x15, 0x2f, 0xa7, 0xcd, 0xe0, 0x87, 0x89, 0x2a, 0xc6, 0xff, - 0xa2, 0xf2, 0xa5, 0x56, 0x00, 0xc3, 0xdc, 0x55, 0x4f, 0xbf, 0xdf, 0xcc, 0x96, 0x03, 0xa1, 0xf2, 0x3b, 0x83, 0xa4, - 0x4f, 0xc7, 0xf3, 0x03, 0x9b, 0x44, 0x6d, 0xa1, 0xe7, 0x8f, 0x4b, 0xdd, 0x84, 0xca, 0x6b, 0x53, 0x23, 0x5a, 0x21, - 0x43, 0x65, 0xeb, 0x80, 0xf5, 0xfd, 0x43, 0xb8, 0xbf, 0xa8, 0x69, 0xa8, 0x75, 0xcf, 0x5d, 0x8b, 0x82, 0x13, 0x7f, - 0x80, 0xb1, 0xb8, 0x90, 0xd4, 0x3a, 0x08, 0x93, 0x7e, 0xb4, 0x38, 0xc9, 0x8d, 0xba, 0x3a, 0x39, 0x53, 0xcc, 0x13, - 0xb8, 0xa8, 0x96, 0x6d, 0x7f, 0x45, 0xa5, 0x2e, 0xe5, 0xf6, 0x8a, 0xd2, 0xf4, 0x90, 0xb6, 0x57, 0x71, 0xde, 0x16, - 0x5c, 0xf0, 0xcf, 0x14, 0x5c, 0x58, 0x07, 0xeb, 0x8e, 0x3b, 0x65, 0x4f, 0x78, 0xa2, 0x4c, 0x6b, 0x83, 0xbb, 0x69, - 0x30, 0x26, 0xc6, 0x7e, 0x77, 0xc5, 0x93, 0x8f, 0xc8, 0x82, 0x7f, 0x97, 0x09, 0xf0, 0x4c, 0x76, 0xaf, 0x54, 0xfe, - 0x1f, 0xfc, 0xab, 0xad, 0x7d, 0x67, 0xcd, 0x3f, 0x3d, 0xeb, 0xe1, 0xce, 0x61, 0xf2, 0x03, 0x74, 0x06, 0x74, 0x7b, - 0x25, 0x53, 0x0e, 0xc8, 0x00, 0xd6, 0x22, 0x19, 0x0d, 0xf8, 0xd0, 0xca, 0xb2, 0xed, 0x3b, 0xad, 0x2e, 0x08, 0xf7, - 0x12, 0xb8, 0xe9, 0xfd, 0xb5, 0x99, 0x99, 0xd3, 0xb5, 0x12, 0x4d, 0x97, 0xc6, 0xd6, 0xb2, 0x54, 0x01, 0xbb, 0xdf, - 0x7b, 0x92, 0x4d, 0xf3, 0xe3, 0xd5, 0x34, 0xb7, 0xd4, 0x6d, 0xeb, 0x96, 0x0d, 0x00, 0x21, 0x76, 0xad, 0xad, 0x1c, - 0x40, 0x72, 0x7b, 0x10, 0xc2, 0xd7, 0x8a, 0xd0, 0x53, 0x25, 0x42, 0x9f, 0xa6, 0xcd, 0x3e, 0xd8, 0x55, 0xb5, 0x69, - 0xc4, 0x39, 0x1a, 0xa4, 0x9a, 0x91, 0x7f, 0x7b, 0xcd, 0x8b, 0x8b, 0x5c, 0xde, 0x00, 0x06, 0x32, 0xa9, 0x8d, 0xc2, - 0xf2, 0x0a, 0xdc, 0xf9, 0xd1, 0x71, 0x9c, 0x89, 0x51, 0x8e, 0xc1, 0x5a, 0x11, 0x1e, 0x59, 0x27, 0xce, 0x01, 0x04, - 0xd9, 0x9f, 0x34, 0x1d, 0xcf, 0xb5, 0xc0, 0x98, 0xbe, 0xc0, 0x5d, 0xe5, 0x6c, 0xb6, 0xcd, 0xed, 0xa2, 0x6f, 0xce, - 0xb0, 0xee, 0x48, 0x69, 0x6d, 0x2c, 0xba, 0xee, 0x60, 0xad, 0x19, 0xb4, 0x45, 0x28, 0xf9, 0x90, 0x3b, 0x69, 0xff, - 0x0a, 0x68, 0x70, 0x96, 0xa5, 0xb7, 0xd6, 0x2a, 0x7f, 0xab, 0x85, 0x38, 0x51, 0x4c, 0x9d, 0xf8, 0x26, 0x4a, 0xf4, - 0xf9, 0x99, 0x18, 0x37, 0x10, 0x48, 0xfd, 0x01, 0x83, 0x6a, 0x14, 0x61, 0x02, 0xd7, 0x81, 0x28, 0xb6, 0x27, 0x6a, - 0x63, 0x39, 0x82, 0x4e, 0x08, 0xf1, 0x0e, 0xca, 0x30, 0x56, 0x17, 0x07, 0xda, 0x60, 0xe9, 0xeb, 0xd6, 0x3a, 0x37, - 0x84, 0xc2, 0x38, 0x81, 0x29, 0x06, 0x49, 0x9d, 0x75, 0x96, 0x09, 0xaa, 0xec, 0x98, 0x74, 0xde, 0x07, 0xe8, 0xfe, - 0x5a, 0x34, 0xc5, 0xd7, 0x9d, 0x3b, 0xe8, 0x3e, 0xae, 0x5f, 0x6b, 0x91, 0x1b, 0xfc, 0x79, 0x4b, 0x84, 0x45, 0xe0, - 0xac, 0x35, 0xf9, 0xaa, 0x11, 0x0e, 0x4c, 0x49, 0xa6, 0x61, 0x2f, 0x51, 0x36, 0xdd, 0xbb, 0x5d, 0xaf, 0x77, 0xaf, - 0x88, 0xab, 0xc7, 0x58, 0xe5, 0xdd, 0xcc, 0xed, 0x9d, 0x6a, 0x23, 0xf6, 0x6f, 0xda, 0x7e, 0x8a, 0x1d, 0xb5, 0xd6, - 0x6e, 0x37, 0x9c, 0x50, 0x43, 0xbe, 0x15, 0x55, 0x5a, 0x9d, 0x6e, 0x0c, 0xda, 0x21, 0x9e, 0xb5, 0xc8, 0xe0, 0x46, - 0xf9, 0xdc, 0x09, 0x9d, 0x54, 0x9c, 0x55, 0xa7, 0x2e, 0xd8, 0x5e, 0xf1, 0x6a, 0x25, 0xd3, 0x48, 0x50, 0xb4, 0x39, - 0x8f, 0x4a, 0x9a, 0xc8, 0x8d, 0xa8, 0x22, 0x59, 0xa3, 0x5e, 0xd4, 0x6a, 0x0c, 0x10, 0x90, 0xe9, 0xac, 0xe9, 0x41, - 0x15, 0xcc, 0x87, 0x32, 0x92, 0xd3, 0xf7, 0x60, 0x69, 0x8f, 0x1c, 0x6b, 0x7d, 0x5f, 0x9d, 0x2d, 0xbe, 0xd5, 0x13, - 0x82, 0x29, 0xcc, 0x1e, 0x08, 0x03, 0xd7, 0x34, 0x86, 0x9c, 0x76, 0x89, 0xcb, 0x9a, 0x6e, 0x09, 0xf7, 0x70, 0xbb, - 0x92, 0xcd, 0xdc, 0x3c, 0x69, 0x6e, 0xae, 0x60, 0xb3, 0x62, 0x31, 0x06, 0xed, 0x97, 0x54, 0xd7, 0x2e, 0xcd, 0xad, - 0xc7, 0x83, 0x80, 0x06, 0x83, 0xc2, 0xf0, 0xaf, 0x13, 0xe3, 0xe1, 0x49, 0x03, 0x82, 0xa4, 0x5c, 0x84, 0x63, 0xdf, - 0x88, 0x7e, 0x32, 0x95, 0xc7, 0x1c, 0x2d, 0xde, 0xa1, 0xd5, 0x09, 0x44, 0xf1, 0x12, 0xa1, 0x24, 0x46, 0x55, 0x68, - 0x44, 0x50, 0x9e, 0x96, 0xbf, 0x54, 0xd5, 0x21, 0xa0, 0x90, 0xf6, 0x15, 0x85, 0xb2, 0x4d, 0x62, 0x68, 0x86, 0x5f, - 0x2e, 0x26, 0x4b, 0x3d, 0x03, 0x03, 0xb9, 0x38, 0x5a, 0xea, 0x59, 0x18, 0xc8, 0xc5, 0x57, 0xcb, 0xda, 0xad, 0x03, - 0x4d, 0x40, 0x3c, 0x17, 0x8e, 0x4e, 0x4a, 0xab, 0xb2, 0x05, 0x74, 0xfb, 0x10, 0x41, 0xff, 0xbb, 0x3d, 0x04, 0x9d, - 0x5c, 0x68, 0x4f, 0x6e, 0x40, 0xdb, 0x71, 0x08, 0xec, 0x15, 0x93, 0x0a, 0x13, 0x80, 0xe8, 0x98, 0x8d, 0xc1, 0x10, - 0x5b, 0x7d, 0x70, 0xcc, 0xc6, 0x53, 0x9f, 0x04, 0x01, 0xa3, 0xfb, 0x83, 0x81, 0x04, 0xbf, 0xc5, 0xab, 0xf4, 0x6c, - 0x2b, 0xd0, 0x4d, 0xdf, 0xdd, 0x0d, 0xbd, 0x8b, 0x2b, 0x38, 0x55, 0xbb, 0x7b, 0x12, 0xba, 0xc9, 0xb4, 0x03, 0xf4, - 0x1a, 0xe2, 0x86, 0xfc, 0xca, 0x68, 0x34, 0xb2, 0x29, 0x21, 0x21, 0x86, 0x73, 0x68, 0xe6, 0xb4, 0x5c, 0xbe, 0xba, - 0xf5, 0x6c, 0x41, 0x86, 0x99, 0xde, 0x32, 0x59, 0x3f, 0x40, 0x59, 0xf5, 0x18, 0xda, 0xa1, 0xf7, 0xc8, 0xf1, 0xc3, - 0x83, 0x6f, 0x32, 0x7e, 0xe2, 0x70, 0xed, 0xe1, 0x5c, 0xf8, 0x2e, 0x6b, 0x46, 0xe6, 0xd0, 0x79, 0xf6, 0x71, 0xbc, - 0x87, 0x71, 0xf2, 0x69, 0x16, 0xca, 0x1b, 0xaf, 0xe9, 0x7f, 0x54, 0x7a, 0xb3, 0xc3, 0x21, 0xa7, 0x6b, 0x58, 0x71, - 0xf3, 0x2a, 0x34, 0xfc, 0x2c, 0xf2, 0xc6, 0x11, 0xaf, 0x49, 0x54, 0x75, 0x9f, 0xf7, 0x36, 0x4c, 0x69, 0xc7, 0x38, - 0x00, 0x38, 0x51, 0xab, 0x86, 0x7d, 0x69, 0x5c, 0xab, 0x83, 0x18, 0x86, 0x12, 0xb6, 0x4a, 0x1c, 0x09, 0xe5, 0x6f, - 0x00, 0xc2, 0x62, 0x28, 0x8e, 0xb7, 0x86, 0xf5, 0x01, 0xf6, 0x43, 0x17, 0x68, 0x9a, 0x53, 0xaa, 0x19, 0x00, 0x24, - 0x01, 0x7f, 0xf4, 0x74, 0xd3, 0x50, 0xd9, 0xe6, 0x79, 0x68, 0x59, 0x5d, 0xc1, 0x03, 0x3d, 0x75, 0x25, 0x03, 0xe3, - 0xaa, 0x8e, 0xbd, 0xed, 0xfd, 0xed, 0xd1, 0x2a, 0xf2, 0xbd, 0x4d, 0x6a, 0x9b, 0x55, 0xa1, 0xb1, 0x8f, 0x27, 0xf4, - 0x74, 0x02, 0xb4, 0x5e, 0x5b, 0x2a, 0xda, 0xef, 0xa3, 0x18, 0x35, 0x2e, 0x14, 0x58, 0x85, 0x89, 0x04, 0x87, 0x08, - 0x23, 0x84, 0x7e, 0x5f, 0x86, 0x5b, 0x5f, 0x90, 0x41, 0x34, 0x5c, 0x8b, 0x8e, 0x3f, 0xe4, 0x78, 0xd1, 0xb6, 0x54, - 0xd5, 0x9c, 0x34, 0x6d, 0x09, 0xbc, 0x09, 0x07, 0xd8, 0xce, 0x3f, 0x6d, 0x88, 0x5c, 0x85, 0x8b, 0x12, 0xbe, 0x27, - 0xae, 0x05, 0xd1, 0x4d, 0x6d, 0xea, 0x6d, 0xd8, 0x21, 0x3a, 0x9a, 0xe2, 0xd1, 0x21, 0xf7, 0xdc, 0x3d, 0xb7, 0x45, - 0x7c, 0xf3, 0x09, 0x72, 0xd7, 0x74, 0xf6, 0x52, 0x84, 0x41, 0xdd, 0xb2, 0x81, 0x62, 0x1d, 0x3b, 0x41, 0x01, 0x46, - 0x6d, 0xf9, 0x0b, 0xe8, 0xd8, 0x60, 0x50, 0x11, 0x7c, 0x52, 0xd8, 0x36, 0x0d, 0xf2, 0x47, 0xbc, 0x1b, 0x3a, 0xbc, - 0xb6, 0xe4, 0x81, 0x78, 0x85, 0x7d, 0xa2, 0x84, 0xfb, 0x17, 0x14, 0x74, 0x47, 0x79, 0xb9, 0x2a, 0x5c, 0x95, 0x06, - 0xa0, 0xca, 0x9e, 0xe7, 0x5a, 0x53, 0xd2, 0x02, 0x56, 0x4a, 0xea, 0xce, 0x6f, 0x22, 0xe2, 0x96, 0x4c, 0xc5, 0x6c, - 0xd5, 0x8d, 0x2a, 0x8f, 0x25, 0x8a, 0x74, 0xec, 0xd9, 0xce, 0xc1, 0x1a, 0x00, 0x4f, 0x61, 0x7b, 0x71, 0x86, 0x05, - 0x65, 0x5c, 0xb6, 0xcc, 0x25, 0x50, 0xd4, 0x0f, 0xe3, 0xbc, 0xec, 0xf9, 0x72, 0x77, 0xb4, 0xbd, 0x87, 0xde, 0x88, - 0x8d, 0xf1, 0xfa, 0x3c, 0x6a, 0xfa, 0xd9, 0x33, 0x5c, 0x59, 0x0a, 0xf2, 0x40, 0x53, 0x3d, 0xc2, 0xe8, 0x10, 0x98, - 0xa6, 0x7c, 0xc6, 0xc6, 0xd3, 0xe1, 0xd0, 0x90, 0x41, 0xaf, 0x99, 0x18, 0xff, 0xeb, 0x33, 0x68, 0x9d, 0x99, 0xb8, - 0xc6, 0xa7, 0xed, 0x2b, 0x68, 0x75, 0x8b, 0x32, 0xb9, 0x33, 0x30, 0x7c, 0xa0, 0x25, 0x53, 0x30, 0x55, 0x78, 0x43, - 0xa4, 0x92, 0xfd, 0xb5, 0xb2, 0x0e, 0xfb, 0x76, 0xa1, 0xd0, 0x42, 0x13, 0xbf, 0xca, 0x10, 0x3f, 0x75, 0x9d, 0xf9, - 0xb7, 0x69, 0x9f, 0x1a, 0xc4, 0xc2, 0x92, 0x18, 0x85, 0xf8, 0xc5, 0xa9, 0xb2, 0x9d, 0x10, 0x2a, 0x20, 0x1e, 0xba, - 0xd6, 0x8d, 0x23, 0xa9, 0x62, 0x4f, 0x0a, 0x8d, 0xa7, 0x86, 0xfb, 0x5e, 0xe8, 0x98, 0x75, 0x98, 0xc5, 0x6d, 0xd6, - 0x48, 0x6a, 0x8c, 0x53, 0x61, 0x82, 0x53, 0xca, 0x75, 0x24, 0x30, 0x3a, 0x9e, 0x2d, 0x0c, 0xa2, 0x4a, 0x62, 0x92, - 0xb1, 0xb5, 0x10, 0x26, 0x76, 0x9d, 0x2b, 0x4c, 0x53, 0x17, 0xa9, 0xdf, 0x0c, 0x4c, 0x16, 0x34, 0xe4, 0xf7, 0x68, - 0xb4, 0xa6, 0x6a, 0x0a, 0x30, 0x8c, 0xa3, 0x54, 0xe3, 0xdf, 0x22, 0xd4, 0x66, 0x18, 0x00, 0xd8, 0xe6, 0x9d, 0xcc, - 0x44, 0xf5, 0x4a, 0x20, 0x04, 0x9a, 0xb3, 0x9f, 0x2a, 0xaa, 0xbd, 0x59, 0x30, 0x8a, 0x76, 0x7b, 0xe5, 0xf3, 0x81, - 0x13, 0xca, 0x13, 0x75, 0x81, 0x7a, 0x29, 0x8b, 0xd7, 0x32, 0xe5, 0xad, 0xb8, 0x98, 0x07, 0x92, 0x7d, 0xc8, 0x47, - 0x70, 0x5e, 0xa1, 0x53, 0xb9, 0xd9, 0x26, 0xca, 0x2c, 0x49, 0x32, 0x16, 0x18, 0x9b, 0x97, 0x60, 0x2e, 0x35, 0x33, - 0x86, 0x5f, 0x43, 0x70, 0xb1, 0xbd, 0x93, 0x70, 0x7b, 0x3f, 0x0f, 0x0c, 0xa1, 0xc9, 0x45, 0x4b, 0x34, 0x6c, 0xed, - 0x78, 0x3d, 0xb9, 0x26, 0xdc, 0x87, 0x8d, 0x58, 0x93, 0x31, 0xc6, 0xb5, 0xb9, 0x91, 0xf5, 0xa3, 0x05, 0x1e, 0x8c, - 0x29, 0xeb, 0x4f, 0x20, 0xd3, 0x4a, 0xca, 0xba, 0x58, 0x1a, 0x31, 0x93, 0x4a, 0xf4, 0x6e, 0xdf, 0xf8, 0xac, 0xee, - 0x22, 0xea, 0xb7, 0xf6, 0x7b, 0x52, 0x0f, 0x77, 0xfe, 0x83, 0xc2, 0x1a, 0x54, 0x46, 0x5c, 0x46, 0x94, 0x67, 0x0e, - 0x74, 0xd3, 0xa4, 0x88, 0xd3, 0xb3, 0x75, 0x5c, 0x94, 0x3c, 0x85, 0x4a, 0x35, 0x75, 0x8b, 0x7a, 0x13, 0xb0, 0x37, - 0x44, 0x92, 0x64, 0x2d, 0x8d, 0xad, 0xd8, 0xa5, 0x41, 0x7a, 0xee, 0x0d, 0xb3, 0xf4, 0xb2, 0x42, 0x43, 0x5a, 0xea, - 0x9d, 0x85, 0x4a, 0xe6, 0xaf, 0xf8, 0xcf, 0xa0, 0x56, 0xa0, 0xa3, 0x4d, 0x8a, 0xf1, 0x0c, 0x18, 0xf1, 0xfd, 0x08, - 0x56, 0x0f, 0x10, 0x17, 0x4d, 0x50, 0xea, 0x3d, 0xb1, 0xe3, 0xa7, 0x26, 0x0f, 0xef, 0x42, 0xce, 0x19, 0x7c, 0xfa, - 0x30, 0x4b, 0xd4, 0x5a, 0x47, 0x62, 0xa4, 0x66, 0x00, 0x4d, 0x07, 0x65, 0xce, 0x63, 0x11, 0xcc, 0x7b, 0x26, 0x31, - 0xea, 0x71, 0xfd, 0x0b, 0x34, 0xd4, 0x7e, 0xb3, 0xb2, 0x3c, 0xab, 0xee, 0x3e, 0x87, 0x03, 0x9b, 0xda, 0x0a, 0x7a, - 0xbc, 0xae, 0xe4, 0xe5, 0xa5, 0xea, 0xb6, 0x5f, 0x88, 0x91, 0xd3, 0x35, 0xae, 0xa5, 0x8b, 0x6a, 0xc9, 0x7a, 0xdd, - 0xe9, 0x66, 0x71, 0x37, 0xcb, 0x68, 0x20, 0xac, 0xed, 0x7d, 0xa2, 0xf9, 0xb3, 0x66, 0xdb, 0x7d, 0xbc, 0x05, 0x31, - 0x0f, 0x00, 0x20, 0x3d, 0x88, 0x82, 0x55, 0x96, 0xf2, 0x80, 0xca, 0xfb, 0x38, 0xca, 0x42, 0xe9, 0xe5, 0x2c, 0xe3, - 0xa7, 0x4d, 0x63, 0xad, 0xb3, 0x42, 0x19, 0x5a, 0x1b, 0xdd, 0xe9, 0x3a, 0x43, 0x6c, 0x3f, 0x89, 0xb3, 0x05, 0xb8, - 0x3f, 0x66, 0x28, 0x34, 0x74, 0x96, 0x91, 0x26, 0x1a, 0xbe, 0xeb, 0x9e, 0x41, 0x46, 0x71, 0xb2, 0xce, 0x2b, 0xe9, - 0x56, 0x9f, 0xb5, 0x91, 0x30, 0xf7, 0x10, 0xfd, 0x2a, 0x06, 0x8f, 0x72, 0x9f, 0xd7, 0x46, 0x27, 0xd3, 0x32, 0xd2, - 0xee, 0xfc, 0xa4, 0x5e, 0x65, 0xa9, 0xd6, 0x61, 0xfb, 0x0c, 0x7b, 0x6b, 0x4c, 0x7a, 0x13, 0x52, 0xc3, 0x48, 0x7c, - 0x3a, 0xa3, 0x46, 0x08, 0x68, 0xcb, 0xf1, 0xf7, 0xf8, 0x0c, 0x43, 0x53, 0x60, 0xa9, 0xe2, 0x16, 0x76, 0xc3, 0xd7, - 0x7c, 0xb2, 0x6a, 0x01, 0x88, 0x60, 0xe5, 0xeb, 0x5d, 0xbc, 0x12, 0xea, 0x33, 0x6d, 0x06, 0x80, 0x2c, 0x28, 0xe5, - 0x8e, 0x9f, 0x52, 0xe9, 0x60, 0x89, 0xa2, 0xed, 0xe5, 0xf4, 0x8d, 0x8e, 0x8d, 0x1f, 0xd2, 0x73, 0x01, 0xdb, 0x85, - 0xfc, 0xd6, 0xbd, 0x7a, 0x89, 0x8a, 0xd4, 0xb6, 0x59, 0x0f, 0xf0, 0xe5, 0x06, 0x4d, 0xc2, 0x08, 0xca, 0x94, 0x29, - 0x80, 0xc1, 0x4d, 0x35, 0x0a, 0x26, 0xad, 0x46, 0xc2, 0x96, 0x7a, 0x92, 0xe5, 0xa6, 0x0f, 0x4e, 0x75, 0x8f, 0xa0, - 0x47, 0x3b, 0x9c, 0xb4, 0xec, 0xd7, 0x0a, 0x8e, 0x4e, 0xae, 0x86, 0xa8, 0x99, 0xf7, 0xda, 0x8e, 0x0c, 0x29, 0x97, - 0x61, 0x20, 0x98, 0x72, 0xcc, 0xd3, 0x63, 0xeb, 0x19, 0x11, 0x3d, 0x70, 0xf6, 0x99, 0x6e, 0xd5, 0x95, 0x04, 0x44, - 0xc7, 0xaf, 0x9f, 0xbc, 0xba, 0x8a, 0x2f, 0x0d, 0x8a, 0x52, 0xc3, 0x22, 0x46, 0x99, 0xf6, 0x55, 0x12, 0x06, 0xef, - 0x97, 0xf7, 0x3f, 0xa9, 0x2c, 0xb5, 0xdf, 0x83, 0xad, 0x15, 0x55, 0xfd, 0x52, 0xf2, 0xa2, 0x29, 0xc0, 0xba, 0xcf, - 0x12, 0x05, 0x72, 0xbf, 0xb7, 0x69, 0xe6, 0x9b, 0xa8, 0x71, 0xb3, 0x61, 0xbd, 0x71, 0xdd, 0x2e, 0xb5, 0x25, 0x3b, - 0xb2, 0x12, 0x39, 0xb3, 0x18, 0xcc, 0xf8, 0x51, 0x61, 0x50, 0x1a, 0xb6, 0xa8, 0x4a, 0xc5, 0xef, 0x8d, 0x08, 0x4e, - 0x1d, 0xab, 0x0a, 0x63, 0x1a, 0x30, 0xdb, 0x8a, 0x5a, 0x83, 0x3a, 0x28, 0xa5, 0xad, 0x89, 0xc2, 0xf6, 0x1b, 0x2b, - 0xa8, 0xf9, 0xfd, 0x4f, 0x63, 0xc8, 0xd7, 0x94, 0x82, 0x4a, 0x02, 0x76, 0x06, 0x8d, 0x9e, 0x2a, 0x61, 0x20, 0x05, - 0xc1, 0x13, 0xa0, 0x7c, 0x11, 0x35, 0x56, 0xfb, 0x7d, 0x75, 0x6a, 0x8c, 0xb6, 0x80, 0xd0, 0x42, 0x7a, 0x74, 0xd9, - 0xc7, 0x6d, 0xad, 0x03, 0x89, 0x07, 0x27, 0xd8, 0xce, 0xd5, 0x35, 0x1a, 0x09, 0xcd, 0x1f, 0x1a, 0x0d, 0x78, 0x4d, - 0x2b, 0x50, 0xa8, 0xe7, 0x38, 0x1a, 0x3a, 0x3b, 0xa4, 0x20, 0x62, 0x83, 0x16, 0xf6, 0xdd, 0xf3, 0xa1, 0xd9, 0xd7, - 0x8b, 0x64, 0x49, 0x6a, 0x2a, 0xdd, 0xe7, 0x6e, 0x09, 0x59, 0xab, 0x0e, 0x65, 0xe5, 0x01, 0x8e, 0x17, 0x4a, 0xe6, - 0xef, 0x30, 0xa9, 0x51, 0x1a, 0x13, 0x1a, 0x23, 0x16, 0xb0, 0x24, 0x68, 0xaf, 0x07, 0xea, 0x97, 0x41, 0xa8, 0x70, - 0xa6, 0x27, 0x12, 0x9f, 0x52, 0xae, 0x3e, 0x2d, 0x48, 0x3d, 0x2d, 0x98, 0x03, 0xbd, 0xf4, 0xad, 0xfc, 0xca, 0xc6, - 0x47, 0xfb, 0x7b, 0xd7, 0x5c, 0x58, 0xc7, 0x10, 0x0c, 0x5b, 0xf8, 0xcd, 0xa9, 0x29, 0x00, 0x1b, 0x9e, 0xe8, 0xb2, - 0x7c, 0xa3, 0x26, 0x32, 0x8f, 0x43, 0x12, 0x81, 0x64, 0xbb, 0xb9, 0xb9, 0x8d, 0x60, 0xdb, 0x5b, 0xa8, 0x0d, 0xf5, - 0x97, 0xb7, 0xdd, 0xef, 0x19, 0x5e, 0xee, 0xc9, 0xbd, 0x9b, 0x36, 0x94, 0x3f, 0xdc, 0xbf, 0x4a, 0xfe, 0xaf, 0x2a, - 0xb9, 0xdf, 0x2a, 0xb3, 0x6e, 0x8b, 0xf7, 0xbb, 0x8e, 0x5b, 0x8e, 0xd1, 0x20, 0xb0, 0xa6, 0xc0, 0x40, 0x7a, 0xd2, - 0x98, 0x26, 0x3a, 0xa4, 0x32, 0x63, 0x06, 0x8f, 0x2e, 0x40, 0x73, 0x98, 0xce, 0xf3, 0x18, 0x80, 0x03, 0xfc, 0x23, - 0x8f, 0x50, 0xff, 0x74, 0x5e, 0x04, 0x67, 0xc1, 0xa0, 0x1c, 0x04, 0xfa, 0x13, 0xd7, 0x9c, 0x60, 0x09, 0x3a, 0xb7, - 0x98, 0x41, 0xb0, 0x49, 0x6b, 0xe6, 0x10, 0x1f, 0x27, 0xd3, 0xc1, 0x20, 0x26, 0x5b, 0x00, 0xe9, 0x8b, 0x97, 0xd6, - 0x39, 0xa8, 0xd0, 0x0b, 0xb2, 0x55, 0x77, 0xd1, 0xac, 0xd8, 0xab, 0x76, 0x9a, 0xf7, 0xfb, 0xf9, 0xa2, 0x1c, 0x04, - 0x8d, 0x0a, 0x0b, 0xe3, 0xfd, 0x47, 0x9b, 0x5f, 0x1a, 0x9d, 0x34, 0xc1, 0x30, 0xb5, 0x27, 0xa8, 0x5e, 0xf1, 0x34, - 0xa3, 0x8d, 0xdb, 0xb1, 0x52, 0xbe, 0x80, 0x28, 0x1e, 0x18, 0xb2, 0x56, 0xde, 0xbd, 0x83, 0xd7, 0xe5, 0xc6, 0x9b, - 0x23, 0x0a, 0xb0, 0x9b, 0xc2, 0x38, 0xa9, 0xb9, 0xe8, 0xa2, 0x26, 0x9e, 0xc1, 0x4e, 0x57, 0x6f, 0x25, 0x5a, 0x8d, - 0xf7, 0xe2, 0x7d, 0xb3, 0xf1, 0x37, 0xf2, 0x40, 0x97, 0x79, 0x70, 0x01, 0x88, 0xb3, 0x07, 0x71, 0x75, 0x80, 0xa5, - 0x1e, 0x04, 0x03, 0x8b, 0x1c, 0xd2, 0xae, 0x56, 0x0f, 0x45, 0xa4, 0xce, 0x63, 0x30, 0x60, 0x32, 0x0d, 0xa9, 0xc9, - 0xb4, 0x57, 0x28, 0x48, 0x1b, 0x6b, 0x2d, 0xa0, 0x0d, 0x87, 0xc5, 0x9e, 0xdd, 0xb0, 0x3b, 0xdd, 0x3a, 0x14, 0x4a, - 0x18, 0xbd, 0xba, 0x6e, 0x1e, 0x6a, 0x0d, 0x4f, 0x04, 0x3d, 0xa8, 0x46, 0xfb, 0xe9, 0xa1, 0x3c, 0x69, 0x8f, 0x05, - 0xb8, 0xe8, 0xe1, 0xcb, 0x17, 0x02, 0x2f, 0xda, 0x7b, 0xc8, 0x73, 0xe6, 0x53, 0xe5, 0x83, 0xd8, 0x70, 0xcb, 0xf0, - 0xa1, 0x7d, 0x7c, 0x2b, 0x90, 0x49, 0xdd, 0xd1, 0xd4, 0xd6, 0xee, 0x68, 0x1c, 0x13, 0xe8, 0x37, 0xe5, 0x28, 0x65, - 0x62, 0x6a, 0x59, 0xb1, 0x59, 0x2f, 0x57, 0xde, 0x50, 0x29, 0x9b, 0xad, 0xda, 0x9c, 0x5f, 0xda, 0x48, 0xe8, 0xf7, - 0xb5, 0x3b, 0x10, 0xbe, 0x51, 0xeb, 0x0d, 0x79, 0xd9, 0x10, 0xb1, 0x1c, 0x62, 0x06, 0x8e, 0x17, 0x52, 0xb9, 0x76, - 0x17, 0x4d, 0x55, 0xdd, 0xde, 0x56, 0x2e, 0x68, 0x89, 0xb7, 0x52, 0x60, 0x15, 0xa9, 0xd3, 0xeb, 0xa9, 0xc4, 0xfb, - 0x3e, 0x8a, 0xed, 0x47, 0xc0, 0x36, 0x36, 0x8e, 0xc6, 0xc6, 0x2d, 0x62, 0x8b, 0xaf, 0xa2, 0x8a, 0x16, 0x1c, 0x20, - 0xb8, 0xdb, 0x92, 0x5a, 0x9a, 0x39, 0xc4, 0x7d, 0xc5, 0x03, 0xb4, 0xef, 0xe2, 0x88, 0x53, 0x01, 0xb6, 0x75, 0xad, - 0x73, 0x56, 0xcb, 0x01, 0x9b, 0x89, 0x9e, 0x7f, 0x5a, 0x35, 0x12, 0x31, 0xac, 0xb2, 0x91, 0xb2, 0x42, 0x7b, 0x50, - 0xba, 0x84, 0x8b, 0x2f, 0xc0, 0xcb, 0xf6, 0xfd, 0xca, 0xee, 0xb3, 0x15, 0xf6, 0x0f, 0xf3, 0xaa, 0x09, 0x1e, 0x79, - 0x8d, 0xb7, 0xf7, 0x30, 0xf1, 0xb9, 0x52, 0x08, 0xaf, 0x52, 0x1a, 0x4a, 0x00, 0x06, 0x49, 0x50, 0xc3, 0x95, 0xb6, - 0xcd, 0x20, 0x95, 0x31, 0xec, 0x7e, 0xf5, 0x56, 0xff, 0xa7, 0x55, 0xb8, 0xa8, 0x64, 0x31, 0x26, 0x81, 0xce, 0xa9, - 0x96, 0x9b, 0x98, 0x82, 0x67, 0xfb, 0xe4, 0x08, 0x14, 0x76, 0x02, 0xb8, 0xa1, 0x84, 0xfd, 0x82, 0xb7, 0xa1, 0x9c, - 0xbd, 0xb4, 0x92, 0x27, 0xb7, 0x2f, 0xa9, 0xa0, 0x09, 0x99, 0x0a, 0xbb, 0x7f, 0x5b, 0x1b, 0xf6, 0x79, 0x28, 0x47, - 0x52, 0xe0, 0xe2, 0xa0, 0x0b, 0x00, 0xfb, 0x83, 0x5c, 0xc6, 0xe6, 0x33, 0xe9, 0xf7, 0xd5, 0xfb, 0x67, 0x79, 0x96, - 0x7c, 0xdc, 0x7b, 0x6f, 0x78, 0x9a, 0x15, 0x03, 0x2a, 0x11, 0x53, 0xeb, 0xaa, 0x18, 0xae, 0xb4, 0x8b, 0x71, 0x83, - 0x64, 0xc4, 0xf7, 0x52, 0x87, 0x18, 0x31, 0xbe, 0xc8, 0x1e, 0x49, 0xc9, 0xe9, 0xaa, 0xee, 0xec, 0xb9, 0x16, 0xcd, - 0xa0, 0x31, 0xdc, 0x9e, 0xf7, 0x92, 0x5e, 0x01, 0x2a, 0x2a, 0x74, 0xcf, 0x02, 0xd7, 0xf0, 0xe6, 0x92, 0x68, 0x6c, - 0xe9, 0x69, 0x4b, 0x34, 0x70, 0xaf, 0x4c, 0x48, 0xaa, 0x8d, 0x03, 0x2c, 0x62, 0x5d, 0x7f, 0x0c, 0x25, 0x00, 0xb5, - 0x1a, 0xa4, 0x57, 0xfa, 0x92, 0x50, 0x95, 0x84, 0x60, 0x74, 0x22, 0xe1, 0x65, 0x40, 0xe3, 0xcc, 0x24, 0x5a, 0xd8, - 0xe0, 0x80, 0x3e, 0xaf, 0x4c, 0xa2, 0xb1, 0x21, 0x0f, 0x68, 0x65, 0xd3, 0x00, 0x06, 0x1f, 0x24, 0x49, 0xf4, 0xd5, - 0xca, 0x24, 0x81, 0xa0, 0x04, 0xe5, 0x1b, 0xf4, 0xe7, 0xd2, 0xf3, 0xb1, 0xfc, 0x97, 0x77, 0x28, 0xfd, 0x10, 0x4a, - 0x90, 0x29, 0xea, 0x8a, 0x69, 0xc6, 0x66, 0x59, 0xb7, 0x31, 0x89, 0xe7, 0x69, 0x77, 0x5b, 0x28, 0x97, 0x2e, 0xf0, - 0x2b, 0xcb, 0x10, 0xc7, 0xfa, 0x59, 0xbc, 0x66, 0x27, 0x21, 0xd7, 0x78, 0xe9, 0xcf, 0xe2, 0x35, 0xce, 0x10, 0xad, - 0x5a, 0x09, 0x44, 0xf9, 0xaf, 0xda, 0xc0, 0x21, 0xee, 0x13, 0x0c, 0x72, 0x51, 0x79, 0x0f, 0x04, 0xf2, 0xb6, 0x82, - 0x88, 0x34, 0xb3, 0xeb, 0x30, 0x22, 0xd5, 0x5e, 0x92, 0xf9, 0xf2, 0x47, 0x99, 0x09, 0xef, 0x1b, 0x78, 0x6c, 0x36, - 0xcb, 0xa6, 0x98, 0x2f, 0x54, 0x30, 0x07, 0xf7, 0x89, 0x8a, 0x4b, 0x51, 0xf9, 0x4f, 0xd8, 0x05, 0x2f, 0xc6, 0x83, - 0xd7, 0x6b, 0x04, 0xd8, 0xaf, 0xfc, 0x27, 0x6f, 0xcc, 0xde, 0x58, 0x37, 0xbe, 0xcc, 0x44, 0x7c, 0xe0, 0xa3, 0x5b, - 0xca, 0x47, 0x77, 0x5e, 0xa6, 0x3f, 0x1a, 0x50, 0x22, 0xa3, 0xb2, 0xe2, 0xeb, 0x35, 0x4f, 0xe7, 0xb7, 0x49, 0x94, - 0x8d, 0x2a, 0x2e, 0x60, 0x7a, 0xc1, 0xf1, 0x2e, 0xd9, 0x9c, 0x67, 0xc9, 0x2b, 0x88, 0x3d, 0xb0, 0x96, 0x0a, 0x8b, - 0x1f, 0x96, 0x99, 0x5a, 0xcc, 0x42, 0x56, 0x52, 0xf0, 0x60, 0x7e, 0x9d, 0x44, 0x6f, 0x56, 0x1e, 0x92, 0x9a, 0x99, - 0xb2, 0x6d, 0xed, 0x08, 0xb5, 0xf1, 0x75, 0xa4, 0x5b, 0x6d, 0x01, 0x00, 0xf7, 0x6c, 0x91, 0x46, 0x92, 0x89, 0xe1, - 0xa4, 0x66, 0xdc, 0xa4, 0x17, 0x98, 0x1a, 0xd7, 0xac, 0xa2, 0x89, 0xb3, 0x90, 0x01, 0xbd, 0x3f, 0xe0, 0xe5, 0xe0, - 0x73, 0x06, 0xf7, 0x1f, 0xb4, 0x06, 0x2e, 0x8f, 0x8b, 0x7e, 0x5f, 0x1e, 0x17, 0xbb, 0x5d, 0x39, 0x8b, 0xfb, 0x7d, - 0x39, 0x8b, 0x0d, 0xff, 0xa0, 0x14, 0xdb, 0xc6, 0xdc, 0x20, 0xa1, 0xb9, 0x84, 0xa8, 0x45, 0x23, 0xf8, 0x43, 0xb3, - 0x9c, 0x8b, 0x28, 0x3f, 0x4e, 0xfa, 0xfd, 0xde, 0x6a, 0x2e, 0x06, 0xf9, 0x30, 0x89, 0xf2, 0x61, 0xe2, 0x39, 0x21, - 0xfe, 0xea, 0x39, 0x21, 0x2a, 0x1a, 0xb8, 0x86, 0x33, 0x03, 0x10, 0x05, 0x7c, 0xfa, 0x47, 0x75, 0x2d, 0x85, 0xae, - 0x25, 0x56, 0xb5, 0x24, 0xba, 0x82, 0x9a, 0x5d, 0x17, 0x61, 0x89, 0xa5, 0xd0, 0x15, 0xfb, 0x63, 0x05, 0x3c, 0x51, - 0xce, 0xab, 0x2d, 0x30, 0xb0, 0x11, 0xde, 0x39, 0x4c, 0x38, 0x89, 0x4d, 0x0d, 0x68, 0xa7, 0xdb, 0x9a, 0x5e, 0xd0, - 0x35, 0xbd, 0x44, 0x7e, 0xf6, 0x02, 0x0c, 0x96, 0x8e, 0x59, 0x3e, 0x1d, 0x0c, 0x2e, 0xc8, 0x9a, 0x95, 0x8b, 0x30, - 0x1e, 0x84, 0x9b, 0x79, 0x3e, 0xbc, 0x88, 0x2e, 0x08, 0xf9, 0xa2, 0x58, 0xd2, 0xde, 0x7a, 0x54, 0x7e, 0xcc, 0x20, - 0xb8, 0x5f, 0x3a, 0x0f, 0x33, 0x13, 0xe7, 0x63, 0x3d, 0xba, 0xa5, 0x6b, 0x88, 0x5f, 0x03, 0x37, 0x12, 0x12, 0x41, - 0x47, 0x2e, 0xe9, 0x9a, 0x6e, 0xa8, 0x34, 0x33, 0x8c, 0x21, 0xba, 0xed, 0x71, 0x92, 0x80, 0x63, 0xb2, 0x2b, 0x3e, - 0x1a, 0xab, 0xc2, 0xbb, 0xbe, 0x23, 0xb4, 0xd7, 0x4b, 0xdc, 0x20, 0x7d, 0xd7, 0x1e, 0x24, 0x60, 0x44, 0x46, 0x6a, - 0xa0, 0xcc, 0xc8, 0x48, 0x6a, 0x26, 0x15, 0x87, 0x24, 0xf6, 0x87, 0x44, 0x8d, 0x43, 0xe2, 0x8f, 0x43, 0xae, 0xc7, - 0x01, 0xb9, 0xfb, 0x15, 0x1b, 0xd3, 0x94, 0x8d, 0xe9, 0x46, 0x8d, 0x0a, 0xbd, 0xa2, 0xe7, 0x9a, 0x3a, 0x9e, 0xb1, - 0xd7, 0x70, 0x60, 0x0f, 0xc2, 0x7c, 0x1e, 0x0f, 0x5f, 0x47, 0xaf, 0x09, 0xf9, 0x42, 0xd2, 0x6b, 0x75, 0x29, 0x83, - 0x30, 0x88, 0x57, 0xe0, 0x5c, 0xea, 0x42, 0x9d, 0x5c, 0x99, 0x1d, 0x87, 0x4f, 0x97, 0x8d, 0xa7, 0x73, 0x88, 0xe8, - 0x83, 0x56, 0x2a, 0xfd, 0x7e, 0x78, 0xc1, 0xca, 0xc5, 0x59, 0x38, 0x26, 0x80, 0xc3, 0xa3, 0x87, 0xf3, 0x62, 0x74, - 0x4b, 0x2f, 0x46, 0x77, 0x04, 0x2c, 0xbc, 0xc6, 0xd3, 0xcd, 0x31, 0x8b, 0xa7, 0x83, 0xc1, 0x06, 0xa9, 0xba, 0xca, - 0xbd, 0x21, 0x4b, 0x7a, 0x81, 0x13, 0x41, 0x80, 0xa1, 0xcf, 0xc4, 0xc6, 0xd0, 0xf0, 0xd7, 0x0c, 0x3e, 0xbe, 0x63, - 0x17, 0xa3, 0x3b, 0x7a, 0xcb, 0x5e, 0xef, 0xc6, 0x53, 0x60, 0xa6, 0xd6, 0xf3, 0xf0, 0xee, 0xf8, 0x72, 0x7e, 0xc9, - 0xee, 0xa2, 0xbb, 0x19, 0x34, 0xf4, 0x8a, 0xdd, 0x21, 0xe0, 0x52, 0xfa, 0x78, 0x35, 0x78, 0x4d, 0x0e, 0x07, 0x83, - 0x94, 0x44, 0xe1, 0x75, 0xe8, 0xb5, 0xf2, 0x35, 0xbd, 0x23, 0x74, 0xcd, 0x6e, 0x71, 0x34, 0x2e, 0x19, 0x7e, 0x70, - 0xce, 0xee, 0xea, 0xeb, 0xd0, 0xdb, 0xcd, 0x89, 0xe8, 0x04, 0x31, 0x42, 0x5f, 0x03, 0x47, 0xb3, 0x5c, 0x98, 0x09, - 0x78, 0x32, 0x17, 0x19, 0x2d, 0x0a, 0xcd, 0x40, 0x9c, 0x95, 0x80, 0x58, 0x12, 0x75, 0xbf, 0xd9, 0xe8, 0x0c, 0x96, - 0x73, 0xbf, 0xdf, 0xab, 0x0c, 0x3d, 0x40, 0xe4, 0xcc, 0x4e, 0x7a, 0xd0, 0xf3, 0xe9, 0x01, 0x7e, 0xa2, 0x57, 0x0d, - 0xe2, 0x64, 0x7e, 0xb7, 0x8a, 0x7e, 0xf5, 0xe8, 0xc3, 0x0f, 0xdd, 0x94, 0x47, 0xe4, 0xff, 0x3e, 0xe5, 0x29, 0xf3, - 0xe8, 0x75, 0xe5, 0x81, 0xe0, 0x79, 0x6b, 0x52, 0x69, 0x24, 0xaa, 0xd1, 0xd9, 0x3a, 0x06, 0x6d, 0x24, 0x6a, 0x1b, - 0xf4, 0x13, 0x5a, 0x58, 0x41, 0x84, 0x9c, 0xa3, 0xe7, 0x60, 0x90, 0x0a, 0xa1, 0x72, 0xd4, 0xa2, 0x44, 0x43, 0x90, - 0x5c, 0x96, 0x5c, 0x85, 0xcf, 0x21, 0x54, 0x9d, 0x3e, 0xce, 0x44, 0xd8, 0xd0, 0xe3, 0xd0, 0x07, 0x80, 0xff, 0x65, - 0x8f, 0x5c, 0x94, 0xfc, 0x12, 0xcf, 0xe6, 0x36, 0xc1, 0x28, 0x58, 0x22, 0x9a, 0xa1, 0x6d, 0x10, 0xfb, 0xb1, 0x24, - 0x58, 0x8f, 0xa4, 0xf1, 0xa8, 0x34, 0x47, 0x84, 0x1f, 0xc5, 0x47, 0xd1, 0xd3, 0xd8, 0x90, 0x48, 0x8e, 0x24, 0x92, - 0x0f, 0x80, 0x70, 0x12, 0xf4, 0x17, 0x77, 0x4d, 0x76, 0x2d, 0x24, 0x06, 0xfd, 0x69, 0xc5, 0xb4, 0xec, 0x5e, 0xf5, - 0xd8, 0x57, 0x04, 0xb9, 0x63, 0xfa, 0x4f, 0xaf, 0x0f, 0xff, 0x5c, 0xe1, 0x0c, 0x5a, 0xcf, 0x17, 0xd5, 0x99, 0xb9, - 0x37, 0xb8, 0x91, 0xd7, 0x65, 0xed, 0xba, 0x7c, 0xc1, 0x0f, 0xf8, 0x6d, 0xc5, 0x45, 0x5a, 0x1e, 0xfc, 0x5c, 0xb5, - 0xf1, 0x9c, 0xca, 0xcd, 0xda, 0xc5, 0x59, 0x51, 0xc6, 0xa9, 0x9e, 0xd4, 0xc5, 0x58, 0xc3, 0x36, 0xfc, 0x1e, 0x51, - 0x57, 0xd2, 0x72, 0xf4, 0x94, 0x72, 0xdd, 0x4c, 0xb9, 0xd8, 0xe4, 0xf9, 0x4f, 0x7b, 0xa9, 0x38, 0xc5, 0xcd, 0x14, - 0xa4, 0x4a, 0x2d, 0x17, 0x50, 0x3d, 0x47, 0x2d, 0x77, 0x4b, 0xb3, 0x03, 0x9c, 0xdb, 0xa6, 0xfa, 0x58, 0x99, 0x5d, - 0x78, 0xc9, 0x8d, 0xfb, 0x93, 0x29, 0xc3, 0x82, 0x51, 0x68, 0xb3, 0xea, 0x4a, 0xdb, 0x17, 0x5a, 0xa7, 0x61, 0xb8, - 0xf2, 0xe3, 0x05, 0xa4, 0x0b, 0x18, 0xc7, 0x8b, 0x92, 0x89, 0x71, 0x7b, 0xf4, 0x56, 0x10, 0x9f, 0xb3, 0x15, 0x08, - 0x98, 0x6b, 0x78, 0xbb, 0xae, 0xa3, 0xed, 0x9e, 0x38, 0x65, 0x54, 0xae, 0x63, 0xf1, 0x7d, 0xbc, 0x36, 0x90, 0xc9, - 0xea, 0x78, 0x6c, 0x8c, 0xe9, 0xf4, 0xef, 0x49, 0xe8, 0x17, 0x42, 0xc1, 0x67, 0xbd, 0xb4, 0xf2, 0xe4, 0xf6, 0xb0, - 0x8c, 0x6b, 0xf4, 0x4a, 0x5c, 0xeb, 0xbe, 0x19, 0x29, 0xa4, 0x1e, 0xf9, 0xaa, 0x29, 0xa0, 0x37, 0x63, 0xdf, 0x4c, - 0x85, 0x79, 0xbb, 0x67, 0xcc, 0x15, 0x82, 0x95, 0x2a, 0xbb, 0x7d, 0xa7, 0xc6, 0x54, 0xcc, 0x60, 0x8a, 0x6d, 0x67, - 0x31, 0xe9, 0x56, 0xfe, 0x69, 0xe7, 0x7e, 0x95, 0x77, 0xb8, 0x2b, 0xea, 0xb7, 0xc0, 0x85, 0x66, 0x45, 0x59, 0xb5, - 0x65, 0xc3, 0xb6, 0xf1, 0x46, 0x16, 0x8a, 0x0d, 0xb0, 0xec, 0xb9, 0x6f, 0xe1, 0x01, 0xe2, 0x26, 0xdc, 0xb3, 0xcb, - 0x1a, 0x6e, 0x0c, 0x9f, 0x57, 0x92, 0xef, 0x4a, 0x63, 0x2e, 0x7d, 0xaa, 0x34, 0x31, 0x9c, 0x2c, 0x47, 0x5c, 0xa4, - 0xcb, 0x3a, 0xb3, 0x6b, 0xe1, 0x13, 0x5e, 0x86, 0x0b, 0xbe, 0x34, 0xba, 0x29, 0x5d, 0x7a, 0xc1, 0x62, 0xdd, 0xe9, - 0xed, 0x5a, 0x63, 0xa5, 0x44, 0xdc, 0x9a, 0x65, 0x02, 0x65, 0x29, 0x6b, 0x25, 0xbc, 0x29, 0x5a, 0xb6, 0x92, 0x46, - 0xde, 0xb3, 0x00, 0xf7, 0xb1, 0x1f, 0x10, 0x13, 0xd9, 0x04, 0x26, 0x45, 0x43, 0x07, 0xb4, 0xab, 0x2e, 0x7c, 0x33, - 0xea, 0xc1, 0x20, 0xb7, 0x24, 0x11, 0x2b, 0x48, 0xb1, 0x82, 0x4d, 0xcd, 0x8a, 0x45, 0xbe, 0xa4, 0x17, 0x4c, 0x2e, - 0xd2, 0x25, 0x5d, 0x33, 0xb9, 0xd8, 0xe0, 0x4d, 0xe8, 0x02, 0x4e, 0x48, 0xb2, 0x8d, 0x95, 0x02, 0xf6, 0x02, 0x2f, - 0x6f, 0x78, 0xa6, 0x6a, 0x5a, 0x76, 0xa9, 0x38, 0xc0, 0xf8, 0xbc, 0x0c, 0xc3, 0x72, 0x78, 0x01, 0xd6, 0x12, 0x87, - 0xe1, 0x7a, 0xc1, 0x97, 0xea, 0x37, 0x04, 0x9c, 0x4f, 0x42, 0xc5, 0x2e, 0xd8, 0xbd, 0x40, 0xa6, 0x57, 0x0b, 0xbe, - 0x54, 0x23, 0xa1, 0x0b, 0xbe, 0xb2, 0xc6, 0x26, 0xb1, 0x27, 0x68, 0x99, 0xc7, 0x8b, 0xf1, 0x32, 0x8a, 0x6b, 0x58, - 0x86, 0xa7, 0x6a, 0x66, 0x5a, 0xf2, 0x9f, 0x44, 0x6d, 0x68, 0xa2, 0x6f, 0xb0, 0x8a, 0xfc, 0xe1, 0xf1, 0xd1, 0x25, - 0x90, 0xb1, 0xb3, 0x2b, 0x99, 0xf9, 0xd0, 0xf7, 0x91, 0xc1, 0x3d, 0x37, 0xe5, 0x8c, 0xab, 0x20, 0x51, 0x06, 0xee, - 0x5e, 0xcd, 0x92, 0xb1, 0x16, 0xe1, 0xfb, 0x47, 0x45, 0xd1, 0x67, 0xd2, 0x34, 0xa0, 0xfb, 0x48, 0x30, 0x07, 0x7a, - 0xaf, 0xd0, 0xe1, 0xb2, 0xda, 0x66, 0x02, 0xfe, 0x22, 0x41, 0x7e, 0x2b, 0xf4, 0xaa, 0xc6, 0xa0, 0x8a, 0x76, 0x11, - 0x4b, 0xff, 0x3e, 0xe2, 0x47, 0xd9, 0xfc, 0xd3, 0xdc, 0xe3, 0x95, 0x84, 0xc1, 0x0f, 0xa9, 0xd9, 0x24, 0xf3, 0xf6, - 0x8a, 0x7d, 0x0f, 0x1d, 0xf5, 0xa8, 0x35, 0xde, 0x57, 0x2f, 0x38, 0x85, 0x18, 0x25, 0x14, 0x9d, 0x04, 0x03, 0xb8, - 0x5d, 0x42, 0x8a, 0xbb, 0xc1, 0x6e, 0x9b, 0xd7, 0xbc, 0x28, 0x38, 0xdf, 0x54, 0x55, 0xe0, 0x07, 0x34, 0x5c, 0x2c, - 0xf7, 0x43, 0x18, 0x8e, 0x69, 0xeb, 0x1a, 0x06, 0x61, 0xc6, 0x30, 0x12, 0x82, 0xd7, 0xbf, 0xe8, 0x2b, 0x9a, 0xc4, - 0xeb, 0xef, 0xf8, 0x5f, 0x19, 0x2f, 0x14, 0x91, 0x06, 0x11, 0x52, 0x37, 0xf1, 0x8d, 0x4c, 0x93, 0x02, 0x0a, 0x01, - 0x46, 0x01, 0x95, 0xd8, 0xd0, 0x54, 0xfc, 0xad, 0x16, 0x1f, 0xfc, 0xd4, 0x74, 0x3c, 0x1a, 0xd7, 0xad, 0xce, 0xa8, - 0xa0, 0x33, 0xd0, 0xa3, 0x56, 0xd4, 0xd3, 0xa0, 0x95, 0x60, 0x1a, 0x69, 0xde, 0xba, 0x87, 0xc0, 0x2b, 0xd3, 0xe2, - 0x9d, 0x07, 0x74, 0x7b, 0xe6, 0x83, 0x27, 0x8f, 0xe9, 0x99, 0x43, 0x4f, 0xae, 0xd8, 0xac, 0xea, 0xa1, 0xf6, 0xde, - 0x8c, 0x50, 0xd0, 0xef, 0x63, 0x0a, 0x74, 0x23, 0xa8, 0xbd, 0xab, 0xfb, 0x8f, 0xe5, 0x3e, 0x87, 0xef, 0x38, 0xcb, - 0x2d, 0x60, 0xa9, 0xc8, 0x5a, 0x81, 0x47, 0x01, 0xea, 0x52, 0x19, 0xc2, 0x16, 0x73, 0x38, 0x54, 0x76, 0xab, 0x56, - 0x43, 0x49, 0x8e, 0xcb, 0x11, 0x38, 0x84, 0x6e, 0xca, 0x41, 0x39, 0x5a, 0x65, 0xd5, 0x7b, 0xfc, 0xad, 0x59, 0x87, - 0x24, 0xbb, 0x8f, 0x75, 0xe0, 0x96, 0x75, 0x98, 0x7e, 0x34, 0x48, 0x01, 0x68, 0xb2, 0x11, 0xb8, 0x04, 0xe0, 0xbd, - 0xfd, 0x47, 0x84, 0x5a, 0x99, 0xde, 0xcb, 0x58, 0xa8, 0xef, 0x1b, 0x49, 0x50, 0x42, 0x33, 0xa1, 0x72, 0x2c, 0x05, - 0xef, 0x3c, 0xd2, 0x39, 0xa9, 0x33, 0xf1, 0x1e, 0xc4, 0x69, 0xe1, 0x03, 0x7b, 0x0b, 0x82, 0x73, 0x16, 0xf4, 0x0e, - 0x6f, 0xb3, 0x5a, 0x6a, 0xa3, 0x07, 0x0a, 0xe0, 0x77, 0x83, 0x3b, 0x04, 0xf9, 0x6a, 0x0c, 0xd7, 0x5a, 0xde, 0x84, - 0x7c, 0x58, 0xd0, 0x23, 0x32, 0xb0, 0xcf, 0x62, 0x18, 0xd3, 0x23, 0x72, 0x6c, 0x9f, 0xa5, 0x1b, 0xc0, 0x81, 0xd4, - 0xa3, 0x4a, 0x8f, 0xa0, 0x41, 0xbf, 0xd9, 0x16, 0x59, 0x92, 0xf5, 0x63, 0x69, 0x14, 0x31, 0x50, 0x25, 0x88, 0xa8, - 0xc5, 0x3f, 0x1f, 0xcc, 0x75, 0x87, 0xb9, 0x40, 0x98, 0x83, 0x01, 0x07, 0x71, 0x1b, 0x84, 0xe6, 0x80, 0xd9, 0xde, - 0x46, 0x82, 0xde, 0x59, 0xc3, 0xcc, 0x8e, 0xfe, 0x70, 0x2b, 0xc1, 0x37, 0x59, 0x6b, 0xd4, 0x79, 0x71, 0x08, 0x04, - 0xc1, 0x9b, 0x42, 0x55, 0x7b, 0xd5, 0x03, 0x1b, 0x6f, 0xd5, 0x8f, 0xdd, 0x6e, 0x3c, 0x15, 0xee, 0xda, 0x2f, 0x28, - 0x9c, 0x7c, 0x4a, 0xfe, 0xf5, 0xde, 0x64, 0x70, 0x60, 0x64, 0xf8, 0xd2, 0xdb, 0xbf, 0xf0, 0xb5, 0x96, 0xee, 0x89, - 0x41, 0x49, 0x1e, 0x1f, 0x29, 0xfa, 0x77, 0xaf, 0xac, 0x7c, 0x6a, 0xa7, 0x7f, 0xb7, 0x33, 0xeb, 0xf3, 0x78, 0x34, - 0xd9, 0xed, 0x7a, 0xda, 0xc0, 0x95, 0x6a, 0x15, 0x02, 0x76, 0xa1, 0x24, 0x87, 0x47, 0x10, 0x15, 0xa1, 0x19, 0x77, - 0xb3, 0x6c, 0x48, 0x64, 0xfc, 0x38, 0x9d, 0x65, 0x43, 0xb0, 0xc3, 0xbd, 0xa8, 0xc4, 0xe5, 0xa8, 0xb5, 0xc1, 0xe9, - 0x59, 0x12, 0x42, 0x28, 0x07, 0xac, 0xec, 0x56, 0xfd, 0xb9, 0x53, 0x66, 0x42, 0x6a, 0xb2, 0xba, 0x9d, 0xd2, 0x3d, - 0x4c, 0xf3, 0x03, 0x33, 0x82, 0x03, 0xee, 0xed, 0xaf, 0xfa, 0x63, 0x98, 0x64, 0x9a, 0x9c, 0x22, 0xf9, 0x45, 0x7a, - 0x0a, 0x49, 0x7b, 0xf4, 0x54, 0x11, 0xc0, 0x09, 0xb5, 0x1f, 0xc3, 0x6f, 0x18, 0xf7, 0xef, 0x9a, 0xaf, 0xdd, 0x54, - 0x44, 0x4f, 0x28, 0x96, 0xa9, 0xc9, 0x69, 0x92, 0x15, 0x09, 0x44, 0x6d, 0x54, 0xcd, 0x88, 0xbe, 0x72, 0x31, 0x1f, - 0x15, 0xe1, 0xf3, 0x6a, 0xfd, 0x9f, 0x21, 0x7c, 0x46, 0xe1, 0x06, 0x70, 0x79, 0xc5, 0xe5, 0x79, 0xf8, 0xf4, 0x09, - 0x3d, 0x98, 0x7c, 0x7d, 0x44, 0x0f, 0x8e, 0xbe, 0x7a, 0x4a, 0x00, 0x16, 0xed, 0xf2, 0x3c, 0x3c, 0x7a, 0xfa, 0x94, - 0x1e, 0x7c, 0xfb, 0x2d, 0x3d, 0x98, 0x7c, 0x75, 0xd4, 0x48, 0x9b, 0x3c, 0xfd, 0x96, 0x1e, 0x7c, 0xfd, 0xa4, 0x91, - 0x76, 0x34, 0x7e, 0x4a, 0x0f, 0xbe, 0xf9, 0xda, 0xa4, 0xfd, 0x0d, 0xb2, 0x7d, 0x7b, 0x84, 0xff, 0x99, 0xb4, 0xc9, - 0xd3, 0xaf, 0xe8, 0xc1, 0x64, 0x0c, 0x95, 0x3c, 0x75, 0x95, 0x8c, 0x27, 0xf0, 0xf1, 0x57, 0xf0, 0xdf, 0xdf, 0x48, - 0xb0, 0xa4, 0x95, 0x64, 0xb9, 0x40, 0xfd, 0x19, 0x8a, 0x38, 0x51, 0x35, 0x91, 0xf0, 0x10, 0x33, 0xab, 0x6f, 0xe2, - 0x30, 0x20, 0x2e, 0x1d, 0x0a, 0xa2, 0x07, 0xe3, 0xd1, 0x53, 0x12, 0xf8, 0xf0, 0x74, 0x37, 0x3e, 0xc8, 0x58, 0x2e, - 0x16, 0xd9, 0x17, 0xb9, 0x89, 0xad, 0xe0, 0x01, 0x58, 0x7d, 0xf4, 0x73, 0x55, 0x72, 0x91, 0x7d, 0x51, 0xc9, 0xfd, - 0x5c, 0xbf, 0xb5, 0x00, 0xe5, 0xfd, 0x55, 0xcb, 0x6e, 0x0a, 0x15, 0x3a, 0xad, 0x35, 0xfa, 0xec, 0x23, 0xa6, 0x0f, - 0x06, 0xde, 0x0d, 0xfb, 0xef, 0x7b, 0xe5, 0xb4, 0xbe, 0xd1, 0x28, 0xd4, 0xa8, 0x3c, 0x24, 0x6c, 0x06, 0x45, 0x0f, - 0x06, 0xc0, 0x13, 0x78, 0xb8, 0x6f, 0xff, 0x66, 0x19, 0x1f, 0x3b, 0xca, 0xf8, 0x19, 0x65, 0x08, 0x68, 0xd4, 0xc3, - 0xec, 0xa6, 0x87, 0x8d, 0x6e, 0xf5, 0x92, 0xa5, 0x3a, 0x99, 0x9a, 0x9e, 0xc1, 0xbe, 0xd6, 0xb5, 0x3c, 0x30, 0xa2, - 0x68, 0x79, 0x71, 0x90, 0xf2, 0x79, 0xc5, 0xfe, 0xbe, 0x42, 0xf5, 0x56, 0xd4, 0x78, 0x23, 0xb3, 0x79, 0xc5, 0xbe, - 0x37, 0x6f, 0x80, 0x9b, 0x61, 0xbf, 0xa9, 0x27, 0x3f, 0x70, 0x06, 0x97, 0xb6, 0x3d, 0xca, 0xc4, 0x08, 0xb0, 0x02, - 0x32, 0x70, 0xe0, 0x01, 0xd0, 0x41, 0x7f, 0xb4, 0x77, 0x3b, 0x95, 0xd2, 0xec, 0xb3, 0x85, 0x01, 0x34, 0xcc, 0xdb, - 0xc4, 0x95, 0xfd, 0xaf, 0x86, 0xbc, 0x04, 0x85, 0x5b, 0xcd, 0xf2, 0xf6, 0x0a, 0x43, 0x08, 0xc1, 0x1f, 0x57, 0x0c, - 0x00, 0x07, 0x02, 0x0c, 0xc6, 0x5a, 0x06, 0xd4, 0x6c, 0xf9, 0x68, 0xcb, 0x95, 0x7a, 0x12, 0x38, 0x83, 0x0b, 0x59, - 0x24, 0xfc, 0xad, 0x16, 0xfb, 0xa3, 0xf5, 0xa3, 0xef, 0xdb, 0xe3, 0xc1, 0xda, 0xf7, 0xf8, 0x48, 0x7f, 0xd6, 0xb8, - 0x0e, 0x6c, 0x5b, 0xbe, 0xf1, 0xa2, 0xb6, 0x12, 0x8f, 0x12, 0x78, 0x03, 0x13, 0x91, 0xc2, 0x20, 0xd5, 0x02, 0xc7, - 0xa0, 0xbc, 0xb1, 0x10, 0x4b, 0xd5, 0xd5, 0x0d, 0xb6, 0x20, 0x32, 0x04, 0x0f, 0xb7, 0x7f, 0xad, 0x54, 0xe0, 0xa8, - 0x7e, 0x9f, 0x4b, 0xdf, 0xed, 0xc9, 0xd8, 0x91, 0xe3, 0xd4, 0x4f, 0x85, 0x83, 0xff, 0x26, 0x75, 0x6d, 0x2c, 0x57, - 0x52, 0x66, 0x59, 0x16, 0x36, 0x0b, 0xb5, 0xdc, 0xa3, 0xf2, 0x20, 0xf9, 0x42, 0x0e, 0x91, 0x2c, 0x30, 0x0a, 0x05, - 0x19, 0x4e, 0xa8, 0x18, 0x6d, 0x44, 0xb9, 0xca, 0x2e, 0xaa, 0x70, 0xab, 0x14, 0xca, 0x9c, 0xa2, 0x6f, 0x37, 0x38, - 0x90, 0x90, 0x28, 0x2b, 0xdf, 0xc4, 0x6f, 0x42, 0x04, 0xab, 0xe3, 0xda, 0x16, 0x8a, 0x7b, 0xfb, 0x93, 0xa7, 0x5d, - 0xfc, 0x91, 0x71, 0x01, 0x75, 0xb1, 0x98, 0x86, 0x13, 0x1b, 0xfb, 0xc6, 0x7d, 0x61, 0x35, 0x3d, 0x00, 0xf5, 0x5d, - 0x2a, 0x31, 0x82, 0xfa, 0xca, 0xd8, 0xc7, 0xf6, 0x18, 0x93, 0x73, 0x88, 0x35, 0xac, 0x72, 0x66, 0xaa, 0x6f, 0x84, - 0xcd, 0x00, 0xb8, 0x11, 0x5a, 0xa3, 0x20, 0xf0, 0x78, 0x15, 0xe2, 0x79, 0xa9, 0xc2, 0xb7, 0x66, 0x84, 0x8e, 0xc1, - 0x9b, 0xca, 0x36, 0x32, 0x93, 0xbe, 0x60, 0xd0, 0x1c, 0xdb, 0x3a, 0x0a, 0xab, 0xad, 0x2c, 0x9b, 0x01, 0xdc, 0x40, - 0x76, 0x6c, 0x2e, 0x9e, 0xf3, 0x6a, 0x91, 0x2d, 0x23, 0x13, 0x14, 0x70, 0x25, 0x2c, 0x83, 0xf6, 0xd7, 0x3d, 0xb2, - 0x1d, 0x87, 0xd0, 0x0d, 0xf7, 0x11, 0x8c, 0xa7, 0xdd, 0x14, 0xac, 0x20, 0x1a, 0x21, 0x1e, 0x66, 0xcc, 0xe2, 0x7b, - 0xa5, 0x29, 0x4f, 0x55, 0x4b, 0x20, 0x70, 0x14, 0x42, 0x5d, 0xec, 0x1b, 0x25, 0xb8, 0x4c, 0x8d, 0x60, 0x06, 0x7b, - 0x76, 0xa4, 0xb6, 0x4b, 0xce, 0xe9, 0x50, 0x4d, 0x69, 0xa9, 0xa7, 0x54, 0xfb, 0x1a, 0x8a, 0x45, 0x89, 0x1e, 0x7a, - 0xe0, 0x7a, 0xa0, 0x1d, 0xf2, 0x4a, 0x3a, 0x31, 0x11, 0x74, 0x5a, 0x6d, 0xc2, 0xce, 0x8d, 0x74, 0xcb, 0x6a, 0xe4, - 0x1d, 0x43, 0xb3, 0x23, 0x5e, 0xf8, 0x81, 0xba, 0x00, 0x22, 0xe4, 0xde, 0x16, 0x99, 0x23, 0x9a, 0x65, 0xe5, 0x4b, - 0x28, 0x8b, 0x23, 0xb6, 0xae, 0x80, 0x6b, 0x29, 0x98, 0x5c, 0xf2, 0x88, 0xa7, 0x88, 0x08, 0x78, 0xa2, 0xb4, 0xeb, - 0x7b, 0x2d, 0x21, 0x34, 0x4b, 0x81, 0xb8, 0xb9, 0x28, 0xce, 0xb5, 0x0d, 0x64, 0x01, 0xf4, 0xed, 0xa7, 0xec, 0xca, - 0x0b, 0x07, 0xbb, 0xbd, 0xca, 0xc4, 0x73, 0x7e, 0x91, 0x09, 0x9e, 0x22, 0xd8, 0xd5, 0xad, 0x79, 0xe0, 0x8e, 0x6d, - 0x03, 0xcb, 0xb7, 0xef, 0x60, 0xc1, 0x94, 0xa1, 0x56, 0x4a, 0x64, 0x22, 0x12, 0x90, 0xd9, 0x67, 0xee, 0x5e, 0x67, - 0xe2, 0x75, 0x7c, 0x0b, 0xde, 0x14, 0x0d, 0x7e, 0x7a, 0x74, 0x8e, 0x5f, 0x22, 0x92, 0x28, 0xc4, 0xb0, 0xc5, 0x88, - 0x58, 0x88, 0x1c, 0x3b, 0x26, 0x94, 0x2b, 0x41, 0x6b, 0x6b, 0x08, 0xbc, 0xf8, 0xd3, 0xaa, 0x7b, 0x57, 0x99, 0x30, - 0xf6, 0x19, 0x57, 0xf1, 0x2d, 0x2b, 0x15, 0x98, 0x05, 0xc6, 0xb9, 0x6f, 0x4b, 0x49, 0xae, 0x32, 0x61, 0x04, 0x24, - 0x57, 0xf1, 0x2d, 0x6d, 0xca, 0x38, 0xb4, 0x15, 0x9d, 0x17, 0xe7, 0x77, 0x7f, 0xf8, 0x25, 0x86, 0x5a, 0x19, 0xf7, - 0xfb, 0x20, 0x31, 0x93, 0xb6, 0x29, 0x73, 0x19, 0x49, 0x8d, 0x16, 0x52, 0x51, 0x3e, 0x98, 0x90, 0xfd, 0x95, 0x6a, - 0x19, 0x51, 0xfb, 0x55, 0x28, 0xe6, 0xe3, 0x68, 0x42, 0xe8, 0xa4, 0x63, 0xbd, 0x9b, 0xd6, 0x42, 0xa6, 0xd1, 0xd3, - 0xc8, 0xf3, 0xe9, 0x2c, 0x58, 0x35, 0x2d, 0x8e, 0x19, 0x9f, 0x16, 0x83, 0x01, 0xd1, 0x2e, 0x85, 0x5b, 0xac, 0x07, - 0x4c, 0x69, 0x5c, 0xbc, 0x35, 0xd3, 0xea, 0x97, 0x52, 0x85, 0xa4, 0xf7, 0x0c, 0x48, 0x32, 0xe9, 0x82, 0xdd, 0x82, - 0x44, 0xd1, 0xf3, 0xbf, 0x53, 0x5b, 0x70, 0xdf, 0x83, 0xb1, 0x19, 0xdd, 0xd7, 0x33, 0xfe, 0x43, 0x6d, 0x0b, 0xa2, - 0x3e, 0x95, 0xac, 0xd7, 0x91, 0xa8, 0x42, 0x2e, 0xc2, 0xcf, 0x8e, 0x86, 0x18, 0xa2, 0xda, 0x63, 0x81, 0xd8, 0x5c, - 0x9d, 0xf3, 0x02, 0xa7, 0x9f, 0xb9, 0xcb, 0x15, 0x6c, 0x0b, 0x5a, 0x19, 0x1a, 0xf5, 0x26, 0x7e, 0x13, 0xd9, 0xcb, - 0x82, 0x2e, 0xf2, 0x39, 0x0a, 0x59, 0xf3, 0x30, 0xac, 0x86, 0xed, 0x41, 0x24, 0x87, 0xed, 0x49, 0x68, 0x34, 0x06, - 0x16, 0xc8, 0x1e, 0x8d, 0xc0, 0x45, 0x68, 0xe5, 0x6f, 0xc7, 0xe0, 0xc2, 0x65, 0x11, 0x59, 0x86, 0x3a, 0x7e, 0x53, - 0xbb, 0x09, 0xaa, 0x57, 0xe8, 0x34, 0x85, 0x55, 0x29, 0x93, 0x7c, 0xf8, 0xf5, 0x52, 0x16, 0x98, 0xc9, 0xeb, 0xb2, - 0x47, 0x5f, 0xdb, 0xed, 0x1d, 0x98, 0x82, 0x75, 0x9f, 0xbc, 0xaf, 0x1f, 0x77, 0xf6, 0x04, 0x8c, 0x62, 0x55, 0x8e, - 0xa6, 0x90, 0x52, 0xfb, 0xa0, 0xd4, 0x1f, 0xc3, 0x95, 0xd0, 0x1c, 0xbb, 0x05, 0x4c, 0x02, 0xf6, 0x19, 0x52, 0x3d, - 0xa6, 0x1d, 0xfb, 0x1c, 0x6d, 0x61, 0x49, 0xc0, 0xe1, 0x1f, 0x65, 0xb2, 0xf6, 0xaf, 0xee, 0x22, 0x6d, 0x86, 0x6c, - 0x59, 0x2c, 0x81, 0xcf, 0x87, 0x5d, 0x1b, 0x95, 0x28, 0x9b, 0x88, 0x24, 0x85, 0x2d, 0x8f, 0x41, 0xda, 0xa3, 0x98, - 0xae, 0x0b, 0x9e, 0x64, 0x28, 0xa5, 0x48, 0xb4, 0x4f, 0x70, 0x0e, 0x6f, 0x70, 0x3f, 0xaa, 0x80, 0xf0, 0x2a, 0xe4, - 0x74, 0x94, 0x52, 0x6d, 0x01, 0xa3, 0xa8, 0x07, 0x88, 0xf2, 0x32, 0x90, 0xe3, 0xed, 0x76, 0x13, 0xba, 0x66, 0xab, - 0xe1, 0x84, 0x22, 0x29, 0xb9, 0xc4, 0x72, 0xaf, 0x40, 0xe7, 0x71, 0xce, 0x7a, 0x2f, 0x00, 0x8b, 0xe0, 0x0c, 0xfe, - 0xc6, 0x84, 0x5e, 0xc3, 0xdf, 0x9c, 0xd0, 0xd7, 0x2c, 0xbc, 0x1a, 0x5e, 0x92, 0xc3, 0x30, 0x1d, 0x4c, 0x94, 0x60, - 0xec, 0x8e, 0xad, 0xca, 0x50, 0x25, 0xae, 0x0f, 0x2f, 0xc8, 0xe3, 0x0b, 0x7a, 0x4b, 0x6f, 0xe8, 0x29, 0x7d, 0x0b, - 0x84, 0xff, 0xee, 0x78, 0xc2, 0x87, 0x93, 0x27, 0xfd, 0x7e, 0xef, 0xbc, 0xdf, 0xef, 0x9d, 0x19, 0x03, 0x0a, 0xbd, - 0x8b, 0x2e, 0x6b, 0xaa, 0x7f, 0x5d, 0xd5, 0xcb, 0xe9, 0x5b, 0xb5, 0x71, 0x13, 0x9e, 0xe5, 0xe1, 0xd5, 0xe1, 0x1d, - 0x19, 0xe2, 0xe3, 0x45, 0x2e, 0x65, 0x11, 0x5e, 0x1e, 0xde, 0x11, 0xfa, 0x76, 0x06, 0x7a, 0x53, 0xac, 0xef, 0xed, - 0xe3, 0x3b, 0x5d, 0x1b, 0xa1, 0x2f, 0xc2, 0x04, 0xb6, 0xc9, 0x2d, 0xb3, 0x77, 0xed, 0xc9, 0x18, 0x62, 0x99, 0xdc, - 0x79, 0xe5, 0xdd, 0x3d, 0xbe, 0x25, 0x87, 0xb7, 0xe0, 0x29, 0x6a, 0xc9, 0xdf, 0x3c, 0xbc, 0x61, 0xad, 0x1a, 0x1e, - 0xdf, 0xd1, 0xd3, 0x56, 0x23, 0x1e, 0xdf, 0x91, 0x28, 0xbc, 0x61, 0x97, 0xf4, 0x94, 0x5d, 0x11, 0x7a, 0xde, 0xef, - 0x9f, 0xf5, 0xfb, 0xb2, 0xdf, 0xff, 0x7b, 0x1c, 0x86, 0xf1, 0xb0, 0x20, 0x87, 0x92, 0xde, 0x1d, 0x4e, 0xf8, 0x57, - 0x64, 0x1e, 0xea, 0xe6, 0xab, 0x05, 0x67, 0x55, 0xde, 0x2a, 0xd7, 0x1d, 0x05, 0x6b, 0x85, 0x3b, 0xa6, 0x9e, 0xde, - 0xd2, 0x1b, 0x56, 0xd0, 0x53, 0x16, 0x93, 0xe8, 0x1a, 0x5a, 0x71, 0x3e, 0x2f, 0xa2, 0x1b, 0x7a, 0xca, 0xce, 0xe6, - 0x71, 0x74, 0x4a, 0xdf, 0xb2, 0x7c, 0x38, 0x81, 0xbc, 0xa7, 0xc3, 0x1b, 0x72, 0xf8, 0x96, 0x44, 0xe1, 0x5b, 0xfd, - 0xfb, 0x8e, 0x5e, 0xf2, 0xf0, 0x2d, 0xf5, 0xaa, 0x79, 0x4b, 0x4c, 0xf5, 0x8d, 0xda, 0xdf, 0x92, 0xc8, 0x1f, 0xcc, - 0xb7, 0xd6, 0x9e, 0xe6, 0x91, 0xa3, 0x8d, 0x69, 0x19, 0x82, 0xbe, 0xb9, 0x0c, 0x6f, 0x08, 0x99, 0x36, 0xc7, 0x0e, - 0x06, 0x74, 0xfe, 0x28, 0x4a, 0x08, 0xbd, 0xf1, 0x4b, 0xbd, 0xc1, 0x31, 0x34, 0x23, 0xa4, 0xd2, 0x4e, 0x31, 0x0d, - 0xd7, 0xc1, 0x2b, 0x0d, 0xd6, 0x71, 0xde, 0xef, 0x87, 0x9b, 0x7e, 0x1f, 0x22, 0xdd, 0x17, 0x73, 0x13, 0xdb, 0xcd, - 0x91, 0x4d, 0x7a, 0x03, 0xda, 0xff, 0x57, 0x83, 0x01, 0x74, 0xc6, 0x2b, 0x29, 0xbc, 0x19, 0xbc, 0x7a, 0x7c, 0x47, - 0x54, 0x1d, 0x05, 0x15, 0x32, 0x2c, 0xe8, 0x6b, 0x9a, 0x01, 0xe0, 0xd7, 0xab, 0xc1, 0x80, 0x44, 0xe6, 0x33, 0x32, - 0x7d, 0x75, 0xfc, 0x76, 0x3a, 0x18, 0xbc, 0x32, 0xdb, 0xe4, 0x0d, 0xbb, 0xa7, 0x14, 0x58, 0x7f, 0x67, 0xfd, 0xfe, - 0x9b, 0x59, 0x4c, 0xce, 0x0b, 0x1e, 0x7f, 0x9c, 0x36, 0xdb, 0xf2, 0xc6, 0x45, 0x55, 0x3b, 0xeb, 0xf7, 0x37, 0xfd, - 0xfe, 0x29, 0x60, 0x17, 0xcd, 0x9d, 0xaf, 0x27, 0x48, 0x5b, 0x16, 0x8e, 0x22, 0x69, 0x92, 0x43, 0x63, 0x68, 0x5b, - 0xac, 0xda, 0x36, 0xef, 0xc8, 0xc0, 0xe2, 0xa8, 0x59, 0x51, 0x5c, 0x93, 0x28, 0xec, 0x9d, 0xed, 0x76, 0xa7, 0x8c, - 0xb1, 0x98, 0x80, 0xf4, 0xc3, 0x7f, 0x7d, 0x5a, 0x37, 0x62, 0x88, 0x09, 0x89, 0xcc, 0xe6, 0x76, 0x65, 0x0f, 0x81, - 0x88, 0xc3, 0xa6, 0x7f, 0x6f, 0xee, 0xe5, 0xa2, 0x76, 0x7c, 0xeb, 0xcf, 0x00, 0x42, 0x24, 0x59, 0xc8, 0xe7, 0x38, - 0x06, 0x65, 0x06, 0x40, 0xe6, 0x91, 0x9a, 0x79, 0x09, 0x20, 0xc0, 0x64, 0xb7, 0x1b, 0x8d, 0xc7, 0x13, 0x5a, 0xb0, - 0xd1, 0xdf, 0x9e, 0x3e, 0xae, 0x1e, 0x87, 0x41, 0x30, 0xc8, 0x48, 0x4b, 0x4f, 0x61, 0x17, 0x6b, 0x75, 0x08, 0x46, - 0xf0, 0x9a, 0x7d, 0xbc, 0xce, 0x3e, 0x9b, 0x7d, 0x44, 0xc2, 0xda, 0x60, 0x1c, 0xb9, 0x48, 0x5b, 0x7a, 0xbb, 0x7b, - 0x18, 0x4c, 0x2e, 0xd2, 0x4f, 0xb0, 0x9d, 0x3e, 0xff, 0xe6, 0xc1, 0x78, 0xc2, 0xc1, 0xe8, 0x2e, 0x0a, 0xfa, 0x4c, - 0xdb, 0xed, 0x2a, 0xff, 0x12, 0xf8, 0x06, 0x53, 0x41, 0xc7, 0x66, 0x59, 0xb8, 0x41, 0x45, 0xd4, 0xd1, 0x32, 0xa8, - 0x6a, 0x65, 0x3b, 0x07, 0xd4, 0x12, 0xab, 0x32, 0x71, 0x0b, 0x0c, 0x43, 0x86, 0xba, 0xdc, 0x93, 0xea, 0x5f, 0xbc, - 0x90, 0x06, 0x3e, 0xc3, 0x89, 0x08, 0x3d, 0x6e, 0x8d, 0xfb, 0xdc, 0x9a, 0xf8, 0x04, 0xb7, 0x56, 0x22, 0x89, 0x35, - 0xb0, 0xa4, 0xe6, 0x72, 0x94, 0xb0, 0x59, 0xc9, 0xf8, 0xbc, 0x8c, 0x12, 0x1a, 0xc3, 0x83, 0x64, 0x62, 0x2e, 0xa3, - 0x04, 0xed, 0x13, 0x5d, 0x84, 0xc1, 0x3f, 0x01, 0xb3, 0x9f, 0xe6, 0xf0, 0x57, 0x92, 0x69, 0x72, 0x0c, 0x01, 0x21, - 0x8e, 0xc7, 0xf3, 0x38, 0x1c, 0x93, 0x28, 0x99, 0xc1, 0x13, 0xfc, 0x57, 0x84, 0x63, 0x52, 0xeb, 0x3b, 0x8c, 0x54, - 0x97, 0xdb, 0x84, 0x01, 0x5c, 0xd9, 0x78, 0x3e, 0x89, 0xac, 0x74, 0x57, 0x3e, 0x1e, 0x8d, 0x9f, 0x92, 0x69, 0x1c, - 0xca, 0x41, 0x42, 0x28, 0x78, 0xf7, 0x86, 0xe5, 0x30, 0xd1, 0xf0, 0x6c, 0xc0, 0xe6, 0x95, 0x8e, 0xcd, 0x93, 0x70, - 0x02, 0xc2, 0x30, 0x21, 0xc7, 0x7a, 0x0f, 0x52, 0x8a, 0x3e, 0xcf, 0xb1, 0x9f, 0xfa, 0x08, 0xc2, 0xec, 0xa8, 0xa5, - 0xe2, 0x6b, 0x00, 0xba, 0xc4, 0xc1, 0xa1, 0xf6, 0xcc, 0x17, 0xf3, 0xb0, 0xf4, 0xa8, 0x94, 0xa9, 0xee, 0x50, 0x34, - 0x28, 0xbf, 0x69, 0xd0, 0xa1, 0x20, 0x83, 0x09, 0x2d, 0x67, 0x13, 0xfe, 0x15, 0x04, 0xf0, 0x68, 0x44, 0xfc, 0x52, - 0x38, 0x31, 0x10, 0x5e, 0x05, 0x19, 0xa8, 0xb4, 0x56, 0x8d, 0x19, 0xd9, 0x8a, 0x0f, 0x20, 0x4c, 0xca, 0xc1, 0x8d, - 0xdc, 0xe4, 0x29, 0x44, 0x05, 0xdb, 0xe4, 0xd5, 0xc1, 0x25, 0x58, 0xb2, 0xc7, 0x15, 0xc4, 0x09, 0xdb, 0xac, 0x01, - 0x3b, 0xf7, 0xd1, 0xb6, 0xac, 0x0f, 0xd4, 0x77, 0x07, 0xd8, 0x72, 0x78, 0x55, 0xc9, 0x83, 0xc9, 0x78, 0x3c, 0x1e, - 0xfd, 0x0e, 0x47, 0x07, 0x10, 0x5a, 0x12, 0x19, 0x3e, 0x19, 0xa0, 0x71, 0x37, 0x15, 0xf7, 0xc6, 0x85, 0xa2, 0xac, - 0x74, 0x32, 0x21, 0x20, 0x7e, 0x36, 0x7d, 0x83, 0x7d, 0xc5, 0x75, 0xfc, 0x93, 0xfd, 0x4f, 0xcc, 0x8a, 0x56, 0x2b, - 0x75, 0xf4, 0xee, 0xed, 0xe9, 0xab, 0x0f, 0xaf, 0x7e, 0x7d, 0x71, 0xf6, 0xea, 0xcd, 0xcb, 0x57, 0x6f, 0x5e, 0x7d, - 0xf8, 0xe7, 0x03, 0x0c, 0xb6, 0x6f, 0x2b, 0x62, 0xc7, 0xde, 0xbb, 0xc7, 0x78, 0xb5, 0xf8, 0xc2, 0xd9, 0x23, 0x77, - 0x8b, 0x05, 0xd8, 0x04, 0xc3, 0x2d, 0x08, 0xaa, 0x19, 0x8d, 0x4a, 0xdf, 0x13, 0x90, 0xd1, 0xa8, 0x90, 0x8d, 0x87, - 0x15, 0x5b, 0x21, 0x17, 0xef, 0x18, 0x0e, 0x3e, 0xb2, 0xbf, 0x15, 0x67, 0xc2, 0xed, 0x68, 0x6b, 0x56, 0x04, 0x7c, - 0xbe, 0x36, 0xa2, 0xf2, 0xb8, 0x10, 0xb5, 0xb7, 0xed, 0x73, 0x48, 0xa8, 0x47, 0xe4, 0x3a, 0x78, 0xdf, 0x06, 0xd9, - 0xe3, 0x23, 0xef, 0x49, 0x79, 0x86, 0xfa, 0x1c, 0x0d, 0x1f, 0x35, 0x9e, 0xd1, 0x89, 0xb9, 0x36, 0x3a, 0xd4, 0xb3, - 0x02, 0xf6, 0xb7, 0x12, 0x63, 0x43, 0xb4, 0x87, 0x14, 0xb1, 0x3e, 0x9c, 0xee, 0x77, 0xff, 0x66, 0xf4, 0x3d, 0x1c, - 0x3f, 0x4a, 0x35, 0x81, 0xb4, 0x28, 0x50, 0xba, 0x32, 0xe4, 0xb6, 0xe7, 0x61, 0x61, 0x7e, 0x86, 0x0d, 0x02, 0x68, - 0x2f, 0x3b, 0x96, 0x04, 0x9a, 0xc5, 0x6b, 0x5d, 0xff, 0xbc, 0x7c, 0x99, 0x68, 0xe7, 0x8b, 0x6f, 0x21, 0xc4, 0xb0, - 0x7f, 0x45, 0x68, 0x4c, 0xb8, 0x9b, 0x64, 0x77, 0x69, 0x31, 0xf7, 0xaa, 0xab, 0x18, 0x8f, 0xbb, 0x7b, 0xae, 0x14, - 0xcd, 0x5b, 0x17, 0xd8, 0x03, 0x35, 0xaf, 0xe3, 0x25, 0x0b, 0x01, 0x9b, 0xf1, 0xd0, 0x2e, 0x12, 0xe7, 0xf7, 0x4e, - 0x27, 0xe4, 0xf0, 0x68, 0xca, 0x87, 0xac, 0xa4, 0x62, 0xc0, 0xca, 0x7a, 0x8f, 0x9a, 0xf3, 0x36, 0x21, 0x17, 0xfb, - 0x34, 0x5c, 0x0c, 0xf9, 0x43, 0x97, 0xa4, 0x0f, 0xbc, 0xe1, 0x50, 0x6d, 0x9b, 0x8b, 0x21, 0x4d, 0x39, 0xdd, 0xa7, - 0x32, 0x20, 0x44, 0xba, 0x8a, 0x2b, 0x52, 0xeb, 0xa3, 0x2a, 0x75, 0x92, 0x8e, 0xeb, 0x6c, 0xfb, 0x89, 0x4b, 0xb6, - 0xba, 0x5d, 0xfb, 0xd7, 0xea, 0xf6, 0x85, 0x19, 0xc8, 0xdf, 0x1f, 0x88, 0x6a, 0x62, 0x20, 0xba, 0x80, 0x0a, 0xfe, - 0x01, 0x5e, 0x9e, 0x3c, 0xd2, 0x0a, 0xd0, 0xfb, 0xce, 0x8e, 0xae, 0x3d, 0xde, 0x98, 0xc5, 0xd6, 0x12, 0xe7, 0xac, - 0xf2, 0x9d, 0xe5, 0x55, 0xd9, 0x0a, 0x5d, 0x47, 0xb0, 0x9f, 0xc3, 0x8e, 0xbe, 0x7b, 0xdb, 0x00, 0x88, 0x52, 0x58, - 0xb9, 0xb3, 0x5f, 0x78, 0x67, 0xbf, 0xb0, 0x67, 0xbf, 0xdd, 0x04, 0xca, 0x87, 0x15, 0x5a, 0xf6, 0x52, 0x8a, 0xca, - 0x34, 0x79, 0xdc, 0xd4, 0x65, 0x21, 0x2d, 0xe6, 0x87, 0x96, 0x76, 0x3d, 0x19, 0x53, 0x89, 0xea, 0x91, 0x1f, 0xb0, - 0x55, 0x87, 0x25, 0x79, 0xf8, 0x9e, 0xf9, 0x3f, 0x7b, 0x83, 0xbc, 0xef, 0x6e, 0xf7, 0x7f, 0x73, 0xa1, 0x83, 0xdb, - 0x5a, 0x2a, 0x3c, 0x75, 0x75, 0x5c, 0xe0, 0x5d, 0x2d, 0x7d, 0xf8, 0xae, 0xf6, 0x2e, 0xd3, 0xcb, 0xae, 0x02, 0xd4, - 0x20, 0xb1, 0xb9, 0xe2, 0x45, 0x96, 0xd4, 0x56, 0xa1, 0xf1, 0x96, 0x43, 0x68, 0x0f, 0xef, 0xe0, 0x02, 0x39, 0x2c, - 0x21, 0xf4, 0x63, 0x65, 0x04, 0x80, 0x3e, 0x8b, 0xfd, 0x96, 0x87, 0x19, 0x19, 0xf8, 0x12, 0xbf, 0x52, 0xfa, 0xe2, - 0xe2, 0xc3, 0xbd, 0xcc, 0x04, 0xbd, 0x4a, 0x6c, 0x76, 0x29, 0xdb, 0x31, 0x3f, 0xfc, 0x2f, 0x30, 0x1a, 0x84, 0xd7, - 0x96, 0xec, 0x50, 0x74, 0xcc, 0x72, 0x05, 0x47, 0x6d, 0xe9, 0xca, 0x2c, 0x5b, 0xd7, 0xcf, 0x6a, 0x98, 0xe9, 0x33, - 0xe5, 0x2d, 0xc8, 0xbe, 0x90, 0xbb, 0x9f, 0xea, 0x8a, 0x05, 0x99, 0x4d, 0xc6, 0x53, 0x22, 0x06, 0x83, 0x56, 0xf2, - 0x31, 0x26, 0x0f, 0x87, 0x7b, 0xcc, 0xa5, 0xd0, 0xfd, 0xf0, 0xfa, 0x00, 0xf5, 0x35, 0xb6, 0x24, 0xd9, 0x56, 0xec, - 0x4f, 0x30, 0x8b, 0x05, 0xe2, 0xe8, 0xe0, 0x17, 0x17, 0x4b, 0x00, 0x59, 0x86, 0x65, 0xa6, 0x85, 0x45, 0x65, 0xaa, - 0x7c, 0x64, 0x0b, 0x26, 0x8f, 0xc7, 0x73, 0xbf, 0xe7, 0x8e, 0xc1, 0x21, 0x24, 0x9a, 0x58, 0xe3, 0x17, 0x3f, 0x0b, - 0xc6, 0x71, 0x28, 0x67, 0xb2, 0xf1, 0x5d, 0x49, 0xa2, 0xb1, 0x31, 0x55, 0xd6, 0x57, 0x89, 0x6a, 0x98, 0x90, 0xc7, - 0x05, 0x39, 0x2c, 0xe8, 0xca, 0x1f, 0x4b, 0x4c, 0x3f, 0x8c, 0x0f, 0x27, 0x63, 0xf2, 0x38, 0x7e, 0x3c, 0x31, 0x70, - 0xc3, 0x7e, 0x8e, 0x7c, 0xb8, 0x22, 0x87, 0xcd, 0x2a, 0xc1, 0x14, 0xd5, 0xf4, 0xcc, 0xaf, 0x24, 0x19, 0xac, 0x06, - 0xe9, 0xe3, 0x56, 0x5e, 0xac, 0x55, 0x8f, 0xf7, 0xe6, 0x98, 0x4f, 0x89, 0x68, 0xdc, 0x18, 0x36, 0xf4, 0x2a, 0xfe, - 0x43, 0x16, 0x51, 0x29, 0x01, 0x91, 0x10, 0xd4, 0xdb, 0xd9, 0x45, 0x96, 0xc4, 0x22, 0x8d, 0xd2, 0x9a, 0xd0, 0x74, - 0xc6, 0x26, 0xe3, 0x79, 0xca, 0xd2, 0xe3, 0xc9, 0xd3, 0xf9, 0xe4, 0x69, 0x74, 0x34, 0x8e, 0xd2, 0xc1, 0x00, 0x92, - 0x8f, 0xc6, 0xe0, 0x62, 0x07, 0xbf, 0xd9, 0x11, 0x0c, 0xdd, 0x0c, 0x59, 0xc2, 0x02, 0x9a, 0xf6, 0x79, 0x4d, 0xd2, - 0xc3, 0x79, 0xa1, 0x7a, 0x12, 0xdf, 0xd2, 0x8d, 0xe7, 0xe0, 0xe2, 0xb7, 0xf0, 0xc2, 0xb5, 0xf0, 0x62, 0xbf, 0x85, - 0x42, 0x93, 0xed, 0x42, 0xfe, 0xff, 0xb8, 0x61, 0xdc, 0x77, 0x97, 0x30, 0x8b, 0xeb, 0x3a, 0x1b, 0xad, 0x0b, 0x59, - 0x49, 0xb8, 0x4d, 0x28, 0x51, 0xd8, 0x28, 0x5e, 0xaf, 0x73, 0xed, 0x22, 0xb6, 0xa8, 0x28, 0x80, 0xbb, 0x40, 0x9c, - 0x62, 0x60, 0xa1, 0x8d, 0x81, 0xdc, 0x5f, 0xbc, 0x90, 0xcc, 0xaa, 0x7d, 0xcc, 0x3d, 0xf2, 0x8f, 0x10, 0x8c, 0x51, - 0xc5, 0x6c, 0x3c, 0x57, 0x58, 0x17, 0x9f, 0x92, 0xf7, 0xfe, 0x1b, 0x47, 0x91, 0x3d, 0x9a, 0x41, 0x4f, 0x10, 0x39, - 0x8f, 0x38, 0x7b, 0x32, 0x79, 0x19, 0xb8, 0x9f, 0xc1, 0x4a, 0x7f, 0xdd, 0x6d, 0xc6, 0xda, 0xf6, 0xe8, 0x5e, 0x18, - 0xa1, 0xe8, 0x5f, 0xf8, 0xce, 0xd4, 0x0b, 0xb8, 0x84, 0x6a, 0x60, 0x37, 0x97, 0x97, 0xbc, 0x04, 0x10, 0xa1, 0x4c, - 0xf4, 0xfb, 0xbd, 0x3f, 0x0c, 0x34, 0x69, 0xc9, 0x8b, 0xd7, 0x99, 0xb0, 0xce, 0x38, 0xd0, 0x54, 0xa0, 0xfe, 0x1f, - 0x2b, 0xfb, 0x4c, 0xc7, 0x64, 0xee, 0x3f, 0x0e, 0x27, 0x24, 0x6a, 0xbe, 0x26, 0x9f, 0x38, 0x4d, 0x3f, 0x71, 0x45, - 0xfb, 0x0f, 0x64, 0xe6, 0x86, 0x43, 0x86, 0xfa, 0x4b, 0xc7, 0x3c, 0x19, 0xbd, 0x4e, 0xcc, 0x66, 0x82, 0x55, 0x73, - 0x88, 0xc2, 0x5e, 0xc0, 0x83, 0xba, 0x96, 0xc5, 0x53, 0x98, 0x7d, 0x50, 0x23, 0x8a, 0x63, 0x36, 0x9e, 0x87, 0x32, - 0x9c, 0x80, 0x7d, 0xef, 0x64, 0x0c, 0xf7, 0x01, 0x19, 0x7e, 0xac, 0x42, 0xec, 0x1c, 0xa4, 0x7d, 0xac, 0x50, 0x31, - 0x01, 0x10, 0x81, 0x90, 0xb7, 0xdf, 0x97, 0x2a, 0x09, 0x5f, 0x97, 0x98, 0x52, 0xa8, 0x0f, 0xfe, 0x13, 0xa9, 0xba, - 0x63, 0xfa, 0xd5, 0xfa, 0xf1, 0x67, 0x42, 0xf1, 0xe9, 0x2e, 0x25, 0xbe, 0x85, 0xe0, 0xce, 0x12, 0x74, 0x10, 0x15, - 0x9a, 0xb1, 0x3d, 0xcc, 0xef, 0x8a, 0xfb, 0xf9, 0x5d, 0xf1, 0xff, 0x8e, 0xdf, 0x15, 0x0f, 0x31, 0x86, 0x95, 0x85, - 0x86, 0x9f, 0x07, 0xe3, 0x20, 0xfa, 0xcf, 0xf9, 0xc4, 0x7b, 0x79, 0xea, 0xab, 0x4c, 0x4c, 0xef, 0x61, 0x9a, 0x7d, - 0x82, 0x82, 0xb0, 0x8a, 0xfb, 0xf4, 0x64, 0x53, 0xd9, 0x5b, 0x2b, 0x19, 0x62, 0x9e, 0x07, 0x58, 0xa3, 0xb0, 0xf2, - 0x80, 0xee, 0x51, 0xb5, 0x41, 0x9c, 0x08, 0x1e, 0xc6, 0xcc, 0x4a, 0xdf, 0x77, 0x3b, 0xa3, 0xc2, 0x7c, 0x90, 0x8b, - 0x82, 0xec, 0xe6, 0xe3, 0xf9, 0x38, 0x0a, 0xb1, 0x01, 0xff, 0x31, 0x63, 0xd5, 0x90, 0xcd, 0x77, 0x32, 0x52, 0x7b, - 0x26, 0x4f, 0x93, 0x7d, 0xd2, 0x3b, 0xe0, 0x1d, 0xf2, 0xf3, 0xfa, 0x63, 0x58, 0x48, 0xc3, 0x6f, 0xc9, 0xcb, 0xb8, - 0xc8, 0xaa, 0xd5, 0x55, 0x96, 0x20, 0xd3, 0x05, 0x2f, 0x3e, 0x9b, 0xe9, 0xf2, 0x3e, 0xd6, 0x07, 0x8c, 0xa7, 0x14, - 0xaf, 0x1b, 0xa2, 0xf4, 0x4d, 0xcb, 0xb3, 0x42, 0x5d, 0x9e, 0x54, 0xcc, 0xf6, 0xac, 0x04, 0xa7, 0x53, 0x30, 0xc1, - 0xd7, 0x3f, 0x5d, 0xef, 0x63, 0xc0, 0x05, 0x85, 0x9a, 0xd3, 0x42, 0xae, 0x0d, 0x96, 0x93, 0x85, 0xee, 0x04, 0xcc, - 0x50, 0x29, 0xf0, 0x02, 0x05, 0x7f, 0xd1, 0xc0, 0x88, 0xbe, 0x74, 0xbf, 0xc9, 0xc0, 0x20, 0x5d, 0x9a, 0x13, 0x61, - 0xec, 0xb8, 0x9d, 0x22, 0x6d, 0x45, 0x39, 0xe3, 0xec, 0xbd, 0xba, 0x52, 0x80, 0x01, 0xde, 0xf6, 0x26, 0x3a, 0x4f, - 0xd0, 0x6b, 0x41, 0xe9, 0xbc, 0x81, 0xbb, 0x59, 0x45, 0x46, 0xb8, 0xf8, 0xb8, 0xf2, 0x58, 0x70, 0xcf, 0x7e, 0x21, - 0x96, 0x46, 0x33, 0x0d, 0xc6, 0x6c, 0x5e, 0xb0, 0x40, 0xa1, 0x02, 0x05, 0x96, 0x73, 0x6d, 0x69, 0x5a, 0x0d, 0xf9, - 0xe1, 0x11, 0x5a, 0x9b, 0x56, 0x03, 0x7e, 0x78, 0x54, 0x47, 0xd9, 0x31, 0x64, 0x99, 0xf9, 0x19, 0xd4, 0xeb, 0x3a, - 0x32, 0x29, 0x26, 0xbb, 0x5f, 0x5f, 0xea, 0x8f, 0xea, 0x16, 0x5c, 0x3f, 0x00, 0x01, 0x6c, 0x00, 0x0e, 0x81, 0x6a, - 0xb0, 0x34, 0x22, 0x58, 0x94, 0x29, 0xb4, 0xaf, 0xa1, 0xf7, 0x46, 0xc3, 0x7f, 0x81, 0xbb, 0x88, 0x5c, 0xfb, 0x9f, - 0x20, 0xf0, 0x57, 0x94, 0x69, 0x65, 0x8a, 0xff, 0x89, 0x56, 0xaf, 0x50, 0xce, 0x9a, 0xd6, 0x7c, 0x10, 0xad, 0x89, - 0x50, 0xcd, 0x18, 0x82, 0x7f, 0x2b, 0xcb, 0xb4, 0xa5, 0xaa, 0x52, 0x1f, 0x1a, 0xaf, 0xb5, 0xc2, 0x59, 0x3e, 0x8e, - 0xbc, 0xd7, 0x18, 0x3a, 0x36, 0x71, 0x96, 0x72, 0x2a, 0x75, 0xfe, 0xd7, 0xa1, 0x8c, 0x1c, 0xe0, 0x74, 0xc2, 0xc6, - 0xd3, 0xe4, 0x58, 0x4e, 0x13, 0x07, 0x99, 0x9f, 0x33, 0x8c, 0xac, 0x6a, 0x40, 0x58, 0x94, 0x0d, 0xa5, 0x2d, 0xc0, - 0x24, 0x27, 0x84, 0x4c, 0x31, 0x14, 0x45, 0x3e, 0xd2, 0xfd, 0xb0, 0xde, 0xac, 0xee, 0x8b, 0x77, 0x1a, 0xe0, 0x34, - 0x4c, 0x20, 0x10, 0x78, 0x11, 0xdf, 0x64, 0xe2, 0x12, 0x3c, 0x86, 0x07, 0xf0, 0x25, 0xb8, 0xc9, 0xa5, 0xec, 0xb7, - 0x2a, 0xcc, 0x71, 0x6d, 0x01, 0x83, 0x06, 0xab, 0x07, 0xd1, 0xe1, 0x52, 0xda, 0xec, 0x2a, 0x40, 0x6c, 0x4c, 0x21, - 0x96, 0x05, 0xdb, 0x58, 0xf6, 0xec, 0x7b, 0xd5, 0x34, 0xb4, 0x4e, 0x38, 0x11, 0x97, 0x39, 0x44, 0x51, 0x19, 0xc4, - 0xe0, 0x8e, 0xe4, 0xf1, 0x79, 0x8f, 0x44, 0x78, 0x41, 0xc0, 0xad, 0x2c, 0x96, 0xe1, 0x9a, 0xae, 0x46, 0xb7, 0x74, - 0x33, 0xba, 0xa1, 0x63, 0x3a, 0xf9, 0x66, 0x0c, 0x16, 0xd9, 0x3a, 0xf5, 0x8e, 0x6e, 0x46, 0x2b, 0xfa, 0xed, 0x98, - 0x1e, 0xfd, 0x0d, 0x4c, 0xf8, 0xf0, 0x30, 0xa1, 0x17, 0xe0, 0xd8, 0x45, 0x6a, 0xf4, 0xd4, 0xf4, 0x0d, 0x0e, 0xab, - 0x51, 0x3e, 0xe4, 0xa3, 0x9c, 0xf2, 0x51, 0x31, 0xac, 0x46, 0xe0, 0xe9, 0x58, 0x0d, 0xf9, 0xa8, 0xa2, 0x7c, 0x74, - 0x3e, 0xac, 0x46, 0xe7, 0xa4, 0xd9, 0xf4, 0x57, 0x15, 0xbf, 0x2a, 0x59, 0x0a, 0xdb, 0x02, 0x96, 0xaf, 0xe7, 0x15, - 0x95, 0xfa, 0xab, 0xda, 0x9c, 0xcc, 0x96, 0xb3, 0xb7, 0xd7, 0x5d, 0x4e, 0x2c, 0x1e, 0xb7, 0x4d, 0x87, 0xab, 0x2f, - 0x27, 0xea, 0xa4, 0x57, 0xc8, 0x0f, 0xe3, 0xa9, 0x50, 0xe7, 0x10, 0x98, 0x49, 0xcc, 0xc3, 0x98, 0x61, 0x33, 0x75, - 0x1a, 0x28, 0x70, 0xb2, 0x91, 0xe7, 0xa2, 0x98, 0x8d, 0x72, 0x0a, 0xef, 0x63, 0x42, 0x22, 0x01, 0x67, 0xd5, 0xac, - 0x1a, 0x15, 0x10, 0x73, 0x84, 0x85, 0xf8, 0x08, 0xfd, 0x52, 0x1f, 0x79, 0x48, 0xe0, 0x19, 0xf6, 0xb5, 0x18, 0xc4, - 0x70, 0xc4, 0xdb, 0xca, 0xaa, 0x79, 0x98, 0x40, 0x65, 0xd5, 0xb0, 0x34, 0x95, 0x15, 0x34, 0x1b, 0x55, 0x7e, 0x65, - 0x15, 0x8e, 0x51, 0x42, 0x48, 0x54, 0xea, 0xca, 0x40, 0x7d, 0x92, 0xb0, 0xb0, 0xd4, 0x95, 0x9d, 0xab, 0x8f, 0xce, - 0xfd, 0xca, 0xce, 0xc1, 0x85, 0x74, 0x90, 0xf8, 0x57, 0xa9, 0x3c, 0x6d, 0x5f, 0x07, 0x1b, 0xab, 0x8a, 0x6e, 0xf9, - 0x6d, 0x55, 0xc4, 0x51, 0x49, 0x5d, 0x0c, 0x68, 0x5c, 0x18, 0x91, 0xa4, 0x7a, 0x8d, 0x82, 0x3f, 0x24, 0x88, 0x4a, - 0x63, 0xf0, 0xea, 0x4c, 0xba, 0x56, 0x6a, 0x45, 0xc5, 0xa0, 0x1c, 0x14, 0x70, 0x7f, 0xca, 0x5b, 0x0b, 0xe9, 0x7b, - 0x88, 0xa8, 0x0c, 0xe5, 0x0d, 0xfe, 0x81, 0xc1, 0x93, 0xd9, 0x3a, 0x0d, 0x93, 0xd1, 0x1d, 0x8d, 0x47, 0x2b, 0x84, - 0x83, 0x61, 0x9b, 0x54, 0xe1, 0xad, 0x5f, 0x40, 0xfa, 0x2d, 0x8d, 0x47, 0x37, 0x34, 0xb5, 0x36, 0xa7, 0x06, 0xea, - 0xaa, 0x37, 0xa6, 0xb7, 0x11, 0xbc, 0xbe, 0x8b, 0x56, 0x14, 0xb6, 0xd2, 0x49, 0x9e, 0x5d, 0x8a, 0x28, 0xa5, 0x88, - 0x40, 0xb8, 0x41, 0xe4, 0xc0, 0x95, 0x46, 0x1b, 0xdc, 0x0c, 0xa0, 0x0c, 0x0d, 0x17, 0xb8, 0x1a, 0xc4, 0xa3, 0x95, - 0x47, 0xa6, 0x56, 0xfa, 0x22, 0x8b, 0xf0, 0xd1, 0xce, 0x46, 0x4b, 0xf1, 0x8c, 0x58, 0x18, 0x57, 0x30, 0x84, 0xba, - 0xb0, 0xd2, 0x14, 0x24, 0x5d, 0xe0, 0xc8, 0x5e, 0x58, 0x54, 0xe1, 0x16, 0x4c, 0x8b, 0xee, 0xc0, 0x3c, 0x0a, 0x14, - 0x0e, 0x2e, 0x41, 0xfa, 0x09, 0x65, 0x3b, 0x47, 0x69, 0x72, 0x78, 0x13, 0x94, 0xee, 0x4d, 0x10, 0xd2, 0xae, 0x6e, - 0xb2, 0x25, 0x7d, 0x83, 0xed, 0x3d, 0x3a, 0x15, 0x15, 0x54, 0x9f, 0x5b, 0x30, 0x59, 0xb2, 0x41, 0xd8, 0x12, 0xa6, - 0x67, 0x7a, 0x03, 0xd8, 0xd3, 0x87, 0x47, 0x7b, 0xf3, 0x5d, 0xcc, 0xff, 0x3a, 0x2c, 0xa3, 0xb1, 0xb2, 0xe0, 0xcd, - 0x2d, 0xb1, 0x5b, 0xb1, 0xf1, 0x74, 0x75, 0x5c, 0x4e, 0x57, 0x48, 0xec, 0x0c, 0xdd, 0x62, 0x7c, 0xb1, 0x5a, 0xd2, - 0x04, 0xcf, 0x36, 0x56, 0x2d, 0x56, 0x06, 0x2d, 0x25, 0x65, 0xb8, 0xde, 0x56, 0xe8, 0xff, 0xaf, 0x2e, 0x7e, 0x29, - 0xc0, 0x4b, 0x30, 0x16, 0x00, 0xc2, 0x3d, 0x98, 0x16, 0xa4, 0x36, 0xca, 0xc6, 0x2a, 0x0d, 0x53, 0x5c, 0x04, 0x26, - 0xa5, 0xdf, 0x0f, 0x73, 0x96, 0x12, 0x0f, 0x3a, 0xd4, 0x9d, 0xda, 0xa9, 0x2f, 0x04, 0x01, 0x1e, 0x49, 0x9d, 0x63, - 0x93, 0x6f, 0xc6, 0xf3, 0x40, 0x0d, 0x44, 0x10, 0x65, 0xc7, 0xf8, 0x88, 0x81, 0x8b, 0x22, 0x1d, 0xb7, 0xd3, 0x15, - 0x71, 0xb1, 0x7f, 0xcc, 0x42, 0x9c, 0x24, 0xcc, 0x35, 0xcf, 0x86, 0xac, 0x8a, 0x30, 0x41, 0x17, 0x06, 0x66, 0x79, - 0x43, 0x56, 0x1d, 0x1e, 0x41, 0xa4, 0x56, 0x5b, 0xc6, 0xba, 0xab, 0x8c, 0x6f, 0x01, 0xc8, 0x9a, 0x31, 0x76, 0xf4, - 0xb7, 0xf1, 0x5c, 0x7d, 0x13, 0x85, 0x7c, 0x76, 0xf4, 0x37, 0x48, 0x3e, 0xfe, 0x16, 0x99, 0x39, 0x48, 0x6e, 0x14, - 0x74, 0xd9, 0x9c, 0x75, 0x0d, 0xa5, 0x89, 0x6b, 0xaf, 0xd4, 0x6b, 0x4f, 0x9a, 0xb5, 0x57, 0xa0, 0x3b, 0xb5, 0xe1, - 0x3d, 0x94, 0xed, 0x2c, 0x98, 0xa0, 0xa3, 0xd9, 0x1d, 0xe8, 0xe0, 0x9d, 0x22, 0xe8, 0x45, 0x12, 0x1a, 0x8f, 0x50, - 0x65, 0xd4, 0x0b, 0x3b, 0xb2, 0x9b, 0x75, 0xc9, 0x3c, 0x03, 0xe6, 0xd8, 0x9e, 0x43, 0x62, 0x98, 0xab, 0x83, 0x3a, - 0x65, 0xe5, 0x30, 0xc7, 0x03, 0x78, 0xc3, 0xe4, 0x50, 0x0c, 0x72, 0x8d, 0xf2, 0x7d, 0xc1, 0x8a, 0x61, 0x39, 0xc8, - 0x35, 0x37, 0x33, 0x6d, 0xc6, 0xa6, 0x4d, 0x74, 0x78, 0xe6, 0x35, 0x9b, 0xad, 0x7b, 0xc0, 0xc7, 0x82, 0x27, 0xb3, - 0xef, 0xf9, 0xf8, 0x1a, 0x38, 0x99, 0xed, 0x6d, 0xb4, 0xa2, 0x77, 0x51, 0x4a, 0x6f, 0xa2, 0x0d, 0x5d, 0x45, 0x17, - 0xc6, 0xc4, 0x38, 0xa9, 0xe1, 0x1c, 0x80, 0x56, 0x01, 0x24, 0x9e, 0xfa, 0xf5, 0x9e, 0x27, 0x55, 0xb8, 0xa2, 0x29, - 0xb8, 0x0d, 0xfb, 0xf6, 0x99, 0x57, 0xbe, 0x44, 0x6a, 0x8b, 0x18, 0x6b, 0xd6, 0x50, 0x71, 0xeb, 0xad, 0xfb, 0x48, - 0xd4, 0xb0, 0x73, 0x5d, 0x6c, 0xa2, 0x6a, 0x38, 0x99, 0x96, 0x80, 0xd8, 0x5a, 0x0e, 0x87, 0xee, 0x08, 0xd9, 0x3f, - 0x7e, 0x74, 0xa0, 0xe7, 0x9e, 0xb4, 0xd8, 0xb6, 0x2d, 0x7f, 0x60, 0x08, 0x53, 0xfa, 0xe9, 0x23, 0x1f, 0x10, 0x2b, - 0x2e, 0xe1, 0x6c, 0x04, 0xea, 0x68, 0x85, 0x4e, 0xbf, 0x55, 0x61, 0xa1, 0x0f, 0xf0, 0xed, 0x6d, 0x94, 0xd0, 0xbb, - 0x28, 0xf7, 0xc8, 0xda, 0xaa, 0x66, 0x72, 0x7a, 0x96, 0x85, 0xbc, 0x7d, 0xa0, 0x97, 0x4b, 0x00, 0xd1, 0x1a, 0xc4, - 0xbe, 0xd4, 0xf5, 0x08, 0x9c, 0x86, 0xd0, 0x24, 0x34, 0x82, 0xab, 0x0a, 0xc2, 0x08, 0xb8, 0x92, 0xf0, 0x37, 0x98, - 0xa8, 0xc0, 0x17, 0xe0, 0x22, 0x93, 0xa6, 0x39, 0x0f, 0x6a, 0x7f, 0x24, 0x4f, 0x8b, 0xb6, 0xb7, 0x2b, 0x8c, 0x26, - 0x18, 0x7b, 0xa2, 0x7d, 0x1e, 0x29, 0x47, 0x71, 0x91, 0x84, 0xd9, 0xe8, 0x56, 0x9d, 0xe7, 0x34, 0x1b, 0xdd, 0xe9, - 0x5f, 0x15, 0x1d, 0xd3, 0xef, 0x74, 0x40, 0x1b, 0x25, 0x7d, 0xeb, 0x38, 0x1b, 0xd0, 0x7a, 0xb1, 0x34, 0xfe, 0xd7, - 0x72, 0x74, 0x4b, 0xe5, 0xe8, 0xce, 0xb7, 0xa4, 0x9a, 0x4c, 0x8b, 0x63, 0x81, 0x86, 0x54, 0x9d, 0xdf, 0x17, 0xc0, - 0xcf, 0x95, 0xc6, 0x77, 0xda, 0x7c, 0xef, 0xb5, 0xff, 0xbc, 0x93, 0x27, 0x50, 0x2c, 0x51, 0xc1, 0xaa, 0x11, 0xd8, - 0xb1, 0x6f, 0xf2, 0xb8, 0x30, 0xa3, 0x14, 0x53, 0x6b, 0xd2, 0x8f, 0x81, 0x2b, 0xa6, 0xbd, 0x02, 0x5c, 0x2d, 0x77, - 0x3b, 0x15, 0x43, 0x13, 0xf6, 0xec, 0x18, 0xa2, 0x9e, 0x1b, 0xc7, 0x28, 0xd9, 0x70, 0x0f, 0x88, 0xb5, 0xcc, 0x5b, - 0xb9, 0x04, 0x24, 0xf0, 0xd6, 0xc3, 0xa4, 0x00, 0x8c, 0xc1, 0x72, 0x45, 0x74, 0x1e, 0x0f, 0x7d, 0x42, 0xbd, 0xd0, - 0xa8, 0x13, 0xb2, 0xb1, 0x25, 0x70, 0xfc, 0x61, 0x7d, 0x08, 0x04, 0xaf, 0xf2, 0x5c, 0x7f, 0xa5, 0x75, 0xfd, 0xa5, - 0xd2, 0x73, 0xc7, 0x72, 0x5d, 0x3f, 0x6b, 0x53, 0xa3, 0x97, 0x60, 0xe1, 0xbb, 0x55, 0xe6, 0x91, 0xdc, 0x22, 0xa4, - 0x2a, 0xb0, 0x52, 0xb7, 0x90, 0x60, 0xfe, 0x95, 0x9c, 0xad, 0xca, 0x7c, 0xf5, 0xc8, 0x83, 0x72, 0x36, 0x3d, 0xfd, - 0x0d, 0x09, 0xda, 0x5d, 0x47, 0x9a, 0xc7, 0x5b, 0x74, 0xf8, 0xec, 0x5a, 0x4b, 0xcc, 0xbd, 0x44, 0xc5, 0xf3, 0x29, - 0x60, 0xab, 0xe7, 0xd9, 0x95, 0xf2, 0xb1, 0xda, 0xc7, 0xf1, 0x33, 0xe7, 0x4f, 0x5c, 0x85, 0x1b, 0xd1, 0x50, 0x82, - 0x80, 0x37, 0x87, 0xb1, 0x2b, 0x54, 0x01, 0x0d, 0xcd, 0x0d, 0x1c, 0xe7, 0x6a, 0x58, 0x69, 0x02, 0xa6, 0xa5, 0x3c, - 0x3a, 0xc0, 0xa1, 0xc9, 0xa3, 0x76, 0xd3, 0xb0, 0x32, 0x74, 0xad, 0xd1, 0xe7, 0xb6, 0xd2, 0x19, 0x6f, 0x36, 0xfc, - 0xf0, 0x68, 0x50, 0xe1, 0x4f, 0xd2, 0x1c, 0x8d, 0x76, 0x6e, 0xb8, 0xd3, 0x08, 0xcc, 0x5c, 0xc9, 0x35, 0xd9, 0x1f, - 0x25, 0x2f, 0xbf, 0xa7, 0x17, 0x16, 0xd0, 0x9f, 0xff, 0x5c, 0x4c, 0x38, 0x69, 0x89, 0x09, 0xd1, 0xd2, 0x41, 0x8b, - 0x0e, 0xf6, 0x94, 0x57, 0xf6, 0x25, 0x5e, 0x3a, 0xc7, 0xff, 0xbe, 0x1e, 0x6b, 0x5f, 0x81, 0xd0, 0xea, 0xe4, 0x61, - 0x7b, 0xb2, 0x40, 0xd4, 0x80, 0x6a, 0x76, 0x55, 0x8e, 0x32, 0xed, 0xac, 0xc8, 0xb6, 0x21, 0x73, 0xdd, 0xcf, 0xd2, - 0xb0, 0x99, 0xec, 0x58, 0x58, 0x66, 0x18, 0xac, 0x9d, 0x2a, 0xfa, 0x1c, 0xb4, 0xfc, 0x08, 0x9e, 0x37, 0x95, 0x67, - 0x3e, 0x9b, 0x65, 0xc4, 0x0b, 0x74, 0xc1, 0xa9, 0x58, 0x36, 0xa5, 0x63, 0xe5, 0x6e, 0x57, 0xa2, 0xb1, 0x44, 0x19, - 0x05, 0x41, 0x6d, 0x83, 0xb0, 0xeb, 0xd2, 0x3d, 0xe9, 0xd3, 0x3e, 0x3e, 0xad, 0x40, 0xdf, 0xe3, 0xfb, 0x0c, 0x24, - 0xa6, 0x9e, 0xe4, 0xa1, 0x6a, 0x34, 0x47, 0x27, 0xcf, 0xe3, 0x54, 0xe3, 0xf3, 0x2b, 0xd9, 0x59, 0xf3, 0x6e, 0x35, - 0xa6, 0xf8, 0x8f, 0xd4, 0xed, 0x3b, 0x97, 0xa1, 0x89, 0xfe, 0x5a, 0x1e, 0xb4, 0x14, 0x16, 0x1c, 0xb7, 0x8d, 0xbf, - 0x7e, 0x9b, 0x39, 0xc4, 0xb0, 0x74, 0x39, 0xbc, 0x09, 0x1d, 0xba, 0xbb, 0xca, 0xde, 0x5c, 0x1f, 0x51, 0xa7, 0x2e, - 0xd6, 0x6d, 0x40, 0xc9, 0x92, 0x77, 0xeb, 0xf4, 0xc4, 0x4a, 0xdf, 0x1d, 0x86, 0x7b, 0xf3, 0xa8, 0xd9, 0xdd, 0xdd, - 0x6e, 0x42, 0xda, 0xf6, 0xc1, 0x78, 0x5f, 0xc2, 0x42, 0x9c, 0x77, 0xd8, 0xc1, 0xf7, 0x61, 0xf5, 0x98, 0x0f, 0x7e, - 0xc6, 0x71, 0x86, 0xd1, 0xcf, 0x94, 0xa1, 0xcf, 0xcb, 0x42, 0x5e, 0xa9, 0x4e, 0xf9, 0x42, 0xb7, 0x96, 0xa9, 0xf7, - 0x9b, 0xf8, 0x4d, 0x2b, 0x40, 0x8c, 0xd7, 0x15, 0x2b, 0xc5, 0x1b, 0x5a, 0x61, 0x5c, 0x03, 0xb7, 0xc9, 0xa1, 0x96, - 0x6a, 0x81, 0xa8, 0xcb, 0x4f, 0x1e, 0xf3, 0xc8, 0xa8, 0x33, 0xe1, 0xbb, 0xc7, 0xdc, 0x97, 0xae, 0xed, 0x37, 0xf1, - 0x53, 0x4d, 0x3b, 0xdc, 0x1f, 0xe8, 0x8e, 0xd6, 0x3d, 0xdc, 0x3c, 0x9b, 0x9f, 0x47, 0xe6, 0x8b, 0x01, 0x36, 0x6b, - 0x9f, 0x71, 0xd9, 0x33, 0xdc, 0xf7, 0xa6, 0x07, 0x63, 0x01, 0x81, 0xc4, 0x0c, 0xbd, 0x0c, 0x5c, 0xe0, 0x02, 0x77, - 0x85, 0x01, 0x43, 0x5c, 0xd3, 0x92, 0x33, 0x6d, 0x65, 0xeb, 0x23, 0x6f, 0xa3, 0x42, 0xb0, 0xae, 0x3b, 0x6e, 0x92, - 0x1c, 0x82, 0x13, 0xb6, 0xdc, 0xfb, 0xda, 0x6b, 0x67, 0xf8, 0x8f, 0x81, 0x70, 0x6e, 0x89, 0x9e, 0x51, 0xdb, 0x63, - 0xad, 0xee, 0x35, 0xbc, 0xca, 0x5d, 0xe4, 0x59, 0xbf, 0x99, 0x97, 0x86, 0x7d, 0xc1, 0x6b, 0x29, 0x38, 0x34, 0xb6, - 0x5b, 0xe1, 0x16, 0x8b, 0x77, 0xb4, 0x5a, 0x59, 0x6b, 0xab, 0xbd, 0x56, 0x2a, 0x7a, 0xff, 0x9a, 0xe3, 0xc4, 0x59, - 0x0a, 0xdb, 0x0f, 0x1f, 0x2e, 0xd8, 0x35, 0x01, 0x0c, 0x5a, 0x4c, 0x16, 0x28, 0x41, 0x25, 0x6b, 0x55, 0xbb, 0x9d, - 0x12, 0xbf, 0xdc, 0xcf, 0xba, 0xcc, 0x76, 0x1e, 0xbf, 0x6e, 0xd2, 0x3e, 0xf1, 0x39, 0xfa, 0x61, 0x7e, 0x67, 0x9d, - 0x94, 0x9c, 0x61, 0x5c, 0xcb, 0xff, 0xaf, 0xa2, 0x97, 0x45, 0x96, 0x46, 0x5b, 0xc3, 0x83, 0xd9, 0x50, 0x9b, 0x3e, - 0x34, 0x46, 0xe5, 0x96, 0x8d, 0x22, 0xa2, 0xd5, 0x2d, 0x08, 0x66, 0x14, 0xf7, 0x25, 0xda, 0xbc, 0x52, 0x65, 0xe1, - 0x1d, 0x3e, 0xb1, 0xd1, 0x1b, 0xb6, 0x27, 0x84, 0xf2, 0xfd, 0xd3, 0xc2, 0xac, 0x5a, 0x2a, 0x1a, 0x6c, 0x97, 0xf0, - 0x2e, 0x46, 0x95, 0x7e, 0xc2, 0x64, 0xcb, 0x82, 0xa9, 0xfe, 0x7f, 0x5f, 0x64, 0x69, 0x9b, 0xa2, 0x03, 0xd3, 0xd9, - 0xf4, 0xe9, 0xa4, 0x5b, 0x5c, 0x67, 0xc0, 0x22, 0x82, 0x2d, 0x15, 0x8e, 0x47, 0xa9, 0xdd, 0x20, 0x61, 0x22, 0xb8, - 0x89, 0x7a, 0xd9, 0xd1, 0x32, 0x25, 0xab, 0x02, 0x9e, 0x5f, 0xb9, 0xca, 0x74, 0x1c, 0x0d, 0xfd, 0xfe, 0x55, 0x6a, - 0x42, 0xbf, 0x52, 0x2f, 0x55, 0x71, 0x1e, 0x46, 0xd5, 0xa1, 0xc2, 0x18, 0xad, 0x68, 0x0a, 0xc7, 0x60, 0x76, 0x11, - 0xa6, 0x78, 0x39, 0xdb, 0x26, 0xec, 0x33, 0x06, 0x72, 0xa5, 0x0d, 0xea, 0x35, 0x25, 0xda, 0xb0, 0xf6, 0x66, 0x4e, - 0x09, 0xbd, 0x60, 0xa5, 0x7f, 0x17, 0xda, 0x80, 0x40, 0x51, 0x36, 0x53, 0xa6, 0xe7, 0xba, 0x9d, 0x17, 0x34, 0xa1, - 0x05, 0x5d, 0x93, 0x1a, 0xf4, 0xbd, 0x4e, 0xce, 0x8e, 0x4e, 0x76, 0x66, 0xd6, 0x63, 0x56, 0x0c, 0x27, 0xd3, 0x18, - 0xae, 0x69, 0xb1, 0xbb, 0xa6, 0xad, 0x9a, 0x37, 0xae, 0xc6, 0xc6, 0x69, 0xd0, 0x2e, 0x90, 0xb6, 0x69, 0x6e, 0x3f, - 0xf5, 0xb8, 0xfd, 0x4d, 0xcd, 0x56, 0xd3, 0xde, 0x66, 0xb7, 0xeb, 0xa5, 0x60, 0x23, 0xea, 0xf1, 0xf1, 0x1b, 0x25, - 0x5d, 0xb7, 0x5c, 0x7e, 0x0a, 0xcf, 0x1e, 0x5f, 0xbf, 0xf2, 0xc1, 0xe5, 0x68, 0xd5, 0xe6, 0xee, 0x57, 0xfb, 0xc8, - 0x72, 0x9f, 0x35, 0xb4, 0x5c, 0xcf, 0x50, 0x93, 0x3c, 0x1b, 0xed, 0x1d, 0x6a, 0xc1, 0x72, 0xd6, 0x4d, 0x78, 0x62, - 0xb0, 0x63, 0xaf, 0x1a, 0x9b, 0xa3, 0x32, 0x97, 0xac, 0x06, 0x09, 0xf4, 0x49, 0x9e, 0x69, 0xfa, 0x07, 0x19, 0xe6, - 0xa3, 0x5b, 0x9a, 0x03, 0xae, 0x58, 0x65, 0x2f, 0x19, 0xa4, 0xae, 0xda, 0x4b, 0x5c, 0xf9, 0x0a, 0x87, 0x64, 0x8b, - 0x4f, 0x86, 0xa9, 0xfa, 0xe4, 0x92, 0x07, 0xff, 0x6f, 0xab, 0x56, 0xe9, 0xb9, 0x49, 0x6e, 0x38, 0xfe, 0x75, 0xd2, - 0xf6, 0x31, 0x31, 0x48, 0xc0, 0x53, 0xbb, 0x18, 0xaa, 0x51, 0x55, 0xc4, 0xa2, 0xcc, 0x4d, 0xcc, 0xb1, 0x7b, 0xbb, - 0x86, 0x0e, 0xca, 0xe0, 0xd7, 0x0d, 0x9f, 0x98, 0x3b, 0xb0, 0x15, 0xe8, 0xe8, 0x44, 0x73, 0x19, 0x66, 0xe6, 0x32, - 0x4c, 0xbb, 0xb6, 0x0a, 0x0c, 0xaf, 0xda, 0x2a, 0x89, 0x72, 0x35, 0xea, 0x71, 0x33, 0x4b, 0xcd, 0x5e, 0xe4, 0xdd, - 0x6b, 0xd2, 0x93, 0xf8, 0xd3, 0x95, 0x27, 0xaf, 0x87, 0x01, 0x91, 0x9f, 0xb3, 0x34, 0x5c, 0xa3, 0x20, 0x38, 0xb5, - 0xda, 0x81, 0x34, 0x1f, 0x01, 0x32, 0x3f, 0x4e, 0xc3, 0x77, 0x5a, 0x9c, 0x43, 0xb6, 0x4a, 0xe3, 0xc4, 0x56, 0x46, - 0x3d, 0x04, 0x77, 0xde, 0x2b, 0x1e, 0x43, 0xe0, 0xc3, 0x0f, 0xb8, 0x19, 0x54, 0x74, 0x5b, 0x62, 0xa2, 0xb4, 0x79, - 0xd4, 0x2d, 0x1f, 0x35, 0x84, 0x4a, 0x56, 0x86, 0x17, 0x43, 0x7b, 0xf7, 0x04, 0x46, 0x95, 0x13, 0xc8, 0x0c, 0x8b, - 0xc3, 0xa3, 0x61, 0xaa, 0x04, 0x45, 0x43, 0x39, 0x5c, 0xa1, 0x1c, 0x10, 0x93, 0x40, 0x60, 0x54, 0x0c, 0x52, 0x5d, - 0x99, 0x7a, 0x31, 0x48, 0xf5, 0xad, 0x8a, 0xd4, 0x67, 0x59, 0x58, 0x51, 0xdd, 0x22, 0x3a, 0xa6, 0x43, 0x49, 0x57, - 0x66, 0xa7, 0xe6, 0x5a, 0x7a, 0xa1, 0x96, 0xe3, 0x33, 0x9d, 0x06, 0xa3, 0x78, 0xea, 0x52, 0xf4, 0x5b, 0xb5, 0x9f, - 0xfd, 0xb7, 0x98, 0x52, 0x23, 0x36, 0xb5, 0xb7, 0x88, 0x61, 0xd5, 0x7e, 0xc8, 0xaa, 0x1c, 0xb4, 0xbb, 0xa0, 0x6c, - 0xac, 0x8c, 0xf3, 0x7c, 0x23, 0x98, 0x39, 0x68, 0x1b, 0xab, 0xa6, 0x0f, 0xbd, 0x11, 0xa3, 0xf6, 0xc6, 0x54, 0xe3, - 0x9e, 0xc0, 0x4f, 0x1b, 0x34, 0xdd, 0x8b, 0x3c, 0x47, 0x3d, 0xf2, 0xee, 0x7f, 0xe6, 0xc8, 0xce, 0xe4, 0x93, 0x58, - 0x26, 0x75, 0xfb, 0x98, 0x04, 0x0b, 0x55, 0xc7, 0xe8, 0xc2, 0x8d, 0x4c, 0x69, 0x3f, 0xf7, 0xa6, 0x1f, 0xf1, 0x4c, - 0x1e, 0xb6, 0x43, 0xa3, 0xbe, 0x34, 0xac, 0x25, 0x45, 0xd4, 0x17, 0xf4, 0xd6, 0x54, 0x47, 0x47, 0xd4, 0xeb, 0x08, - 0xac, 0xae, 0x68, 0x8b, 0x1a, 0x80, 0xc9, 0xb8, 0xb6, 0xb5, 0xf9, 0x1c, 0x4c, 0x6d, 0x55, 0x05, 0x4f, 0xe9, 0xbe, - 0x50, 0xba, 0x37, 0xa9, 0xeb, 0xd6, 0x10, 0x5b, 0xc0, 0x80, 0xc0, 0x8d, 0x9e, 0x9a, 0xfe, 0xa0, 0x89, 0x0a, 0x40, - 0x83, 0xc6, 0xed, 0x4c, 0xe7, 0x48, 0xf4, 0x3b, 0xb5, 0x69, 0x9b, 0xa9, 0x5e, 0x55, 0x3e, 0x80, 0x8a, 0x3f, 0x4b, - 0x67, 0x17, 0x66, 0xc4, 0x02, 0x18, 0xf7, 0xc0, 0x99, 0xea, 0x9d, 0x64, 0x60, 0x3d, 0x91, 0xe7, 0x59, 0xc9, 0x13, - 0x29, 0x60, 0x46, 0xe4, 0xd5, 0x95, 0x14, 0x30, 0x0c, 0x6a, 0x00, 0xd0, 0xa2, 0xb9, 0x8c, 0x26, 0xfc, 0xab, 0x9a, - 0xde, 0x97, 0x87, 0x7f, 0xa5, 0x73, 0x7d, 0x3d, 0xae, 0xc1, 0x50, 0x79, 0x53, 0xf1, 0xbd, 0x4c, 0x5f, 0xf3, 0x27, - 0x5e, 0xa6, 0x95, 0xdc, 0x14, 0x7b, 0x59, 0xbe, 0xfa, 0x9a, 0x3f, 0xd5, 0x79, 0x8e, 0x9e, 0xd4, 0x34, 0x8d, 0xef, - 0xf6, 0xb2, 0x7c, 0xf3, 0xf5, 0x13, 0x9b, 0xe7, 0xab, 0x71, 0x4d, 0x6f, 0x38, 0xff, 0xe8, 0x32, 0x4d, 0x74, 0x55, - 0xe3, 0x27, 0xdf, 0xd8, 0x5c, 0x4f, 0x6a, 0x7a, 0x25, 0x45, 0xb5, 0xda, 0x2b, 0xea, 0xe8, 0xeb, 0xa3, 0x6f, 0xf8, - 0xd7, 0xa6, 0x7b, 0x47, 0x35, 0xfd, 0x73, 0x13, 0x17, 0x15, 0x2f, 0xf6, 0x8a, 0xfb, 0xdb, 0x37, 0xdf, 0x3c, 0xb1, - 0x19, 0x9f, 0xd4, 0xf4, 0x8e, 0xc7, 0x1d, 0x6d, 0x9f, 0x3c, 0x7d, 0xc2, 0xff, 0x56, 0xd7, 0xf4, 0x17, 0xe6, 0x07, - 0x47, 0x3d, 0xc9, 0x3c, 0x3d, 0x7c, 0x22, 0x9b, 0xa8, 0x01, 0x43, 0x0f, 0x0d, 0x20, 0x97, 0x56, 0x4d, 0x73, 0x8f, - 0x57, 0x2e, 0xb8, 0x7d, 0x9f, 0xc5, 0x69, 0xbc, 0x86, 0x83, 0x60, 0x8b, 0xc6, 0x59, 0x05, 0x70, 0xaa, 0xc0, 0x7b, - 0x46, 0x25, 0xcd, 0x4a, 0xf9, 0x0f, 0xce, 0x3f, 0xc2, 0xa0, 0x21, 0xa4, 0x8d, 0x8a, 0x0c, 0xf4, 0x76, 0xad, 0x23, - 0x1b, 0xa1, 0xff, 0x66, 0x33, 0x0e, 0x8e, 0x0f, 0xa3, 0xd7, 0xef, 0x87, 0x05, 0x13, 0x61, 0x41, 0x08, 0xfd, 0x23, - 0x2c, 0xc0, 0xa1, 0xa4, 0x60, 0x5e, 0x3e, 0xe3, 0x7b, 0xae, 0x8d, 0xc2, 0x42, 0x10, 0xdd, 0x45, 0xf6, 0x01, 0x55, - 0x8f, 0xbe, 0x43, 0x37, 0xc4, 0xcb, 0x0a, 0x0b, 0x86, 0x56, 0x35, 0x30, 0x43, 0x50, 0xfc, 0x6b, 0x1e, 0x4a, 0xf0, - 0x89, 0x07, 0xf8, 0xe8, 0x31, 0x99, 0x73, 0x75, 0xad, 0x7d, 0x7b, 0x11, 0x16, 0x34, 0xd0, 0x6d, 0x87, 0xa0, 0x03, - 0x91, 0xff, 0x02, 0x3c, 0x05, 0x06, 0x3e, 0x2c, 0xec, 0x4a, 0xee, 0xfb, 0xab, 0xff, 0x62, 0x58, 0x47, 0x17, 0x7e, - 0xf4, 0x17, 0xeb, 0xc2, 0x9e, 0x91, 0xa9, 0x3c, 0x2e, 0x87, 0x93, 0xe9, 0x60, 0x20, 0x5d, 0x1c, 0xb7, 0x93, 0x6c, - 0xf1, 0xcb, 0x42, 0x2e, 0x97, 0xa8, 0xfb, 0xc6, 0x79, 0x9d, 0xeb, 0xbf, 0x91, 0x76, 0x3e, 0x78, 0x7d, 0xf2, 0xdb, - 0xd9, 0xe9, 0xc9, 0x4b, 0x70, 0x3e, 0xf8, 0xf0, 0xe2, 0xfb, 0x17, 0xef, 0x55, 0x70, 0x77, 0x35, 0xe7, 0xfd, 0xbe, - 0x93, 0xfa, 0x84, 0x7c, 0x58, 0x91, 0xc3, 0x30, 0x7e, 0x5c, 0x28, 0xa3, 0x07, 0x72, 0xcc, 0x2c, 0x14, 0x32, 0x54, - 0x51, 0xdb, 0xdf, 0xe5, 0x70, 0xe2, 0x81, 0x59, 0xdc, 0x35, 0x44, 0xb8, 0x7e, 0xcb, 0x6d, 0x90, 0x35, 0x39, 0xf3, - 0xfa, 0xc1, 0xc9, 0x54, 0x3a, 0xb6, 0xb0, 0x60, 0x50, 0x36, 0xb4, 0xe9, 0x24, 0x5b, 0x14, 0x4b, 0xdb, 0x2e, 0xb7, - 0x40, 0x46, 0x69, 0x76, 0x71, 0x11, 0x2a, 0xe8, 0xea, 0x19, 0x68, 0x00, 0x4c, 0xa3, 0x0a, 0xd7, 0x22, 0x3e, 0xf7, - 0xcb, 0x8f, 0xc6, 0x5e, 0xf3, 0x6e, 0x51, 0xf7, 0x64, 0x9a, 0x55, 0x35, 0x06, 0x74, 0x30, 0xa1, 0xdc, 0x0d, 0xba, - 0x09, 0x26, 0xa3, 0xda, 0xf2, 0xcb, 0xa2, 0x5a, 0x9a, 0xe6, 0xb8, 0x61, 0xa8, 0xbc, 0x92, 0x37, 0xb2, 0x81, 0xc8, - 0x40, 0x32, 0x0c, 0x7b, 0x34, 0x46, 0x91, 0xfa, 0xc1, 0xbe, 0x77, 0xfc, 0x36, 0x97, 0x10, 0x4d, 0x31, 0x03, 0xe9, - 0xfc, 0x89, 0x50, 0xce, 0xe5, 0x92, 0xf1, 0x85, 0x58, 0xce, 0xc0, 0xed, 0x7c, 0x21, 0x96, 0x11, 0x06, 0xe5, 0xcb, - 0x20, 0x56, 0x09, 0xd8, 0xbd, 0x38, 0x08, 0xdf, 0x4e, 0x68, 0x03, 0xbb, 0x81, 0x24, 0x1b, 0x94, 0x76, 0xa5, 0x21, - 0xca, 0x9d, 0xf2, 0x68, 0x83, 0xc8, 0x43, 0xac, 0x5a, 0x54, 0x6d, 0x4f, 0x36, 0x73, 0x31, 0xc1, 0x55, 0x16, 0x33, - 0x39, 0x8d, 0x8f, 0x59, 0x31, 0x8d, 0xa1, 0x94, 0x38, 0x4d, 0xc3, 0x98, 0x4e, 0xa8, 0x20, 0x24, 0x61, 0x7c, 0x11, - 0x2f, 0x69, 0x82, 0x52, 0x82, 0x10, 0x42, 0x7e, 0x8c, 0xd0, 0x36, 0x07, 0x96, 0xbc, 0xdd, 0x7e, 0x9e, 0x7e, 0x6e, - 0xcf, 0x70, 0x19, 0x15, 0xa1, 0x5b, 0x74, 0xd6, 0xf0, 0x6f, 0x44, 0x05, 0x8d, 0xb1, 0x62, 0x08, 0x02, 0x5e, 0x60, - 0x54, 0xc2, 0x82, 0xc4, 0xac, 0x82, 0x28, 0x02, 0xe5, 0x22, 0x5e, 0xb2, 0x82, 0x36, 0x6d, 0x4e, 0x63, 0x6d, 0x12, - 0xd4, 0x73, 0x58, 0x6a, 0x07, 0x52, 0xa9, 0x10, 0x7b, 0x7c, 0x2e, 0xa2, 0x6b, 0x6d, 0x68, 0x00, 0x28, 0x50, 0x4a, - 0x2e, 0x7e, 0xf3, 0xf9, 0x1e, 0x6e, 0x0a, 0xfa, 0x9f, 0x6d, 0x4d, 0xb4, 0xb3, 0x5c, 0x1d, 0x7a, 0x8b, 0x25, 0x8d, - 0xf3, 0x1c, 0x42, 0xb1, 0x19, 0x04, 0x72, 0x91, 0x55, 0x10, 0xd1, 0xe2, 0x2e, 0x30, 0x21, 0xe1, 0xa0, 0x4d, 0xbf, - 0x40, 0x6a, 0x43, 0x4c, 0xae, 0x3c, 0x31, 0xb0, 0xdb, 0x2a, 0x41, 0xc0, 0x91, 0x9e, 0x67, 0x7f, 0x35, 0x31, 0xd6, - 0x34, 0x35, 0x33, 0xf1, 0x36, 0x14, 0xa2, 0x41, 0x0b, 0xa2, 0x19, 0xbc, 0x7f, 0xae, 0x38, 0x5e, 0x75, 0xe0, 0x07, - 0xbc, 0x73, 0x71, 0xe6, 0xd5, 0xcc, 0x23, 0x72, 0xea, 0xa3, 0x1c, 0xd1, 0x2f, 0x79, 0x58, 0x8d, 0x74, 0x32, 0xc6, - 0x4a, 0xe2, 0xa0, 0xb7, 0xc1, 0x82, 0x39, 0xa1, 0x6b, 0x1e, 0x5a, 0x3e, 0xfe, 0x25, 0x32, 0x19, 0x25, 0x35, 0x56, - 0x74, 0xa5, 0xc5, 0x88, 0xf3, 0x1a, 0x66, 0x69, 0xb2, 0xa2, 0x8b, 0x85, 0x26, 0xcd, 0x42, 0x99, 0x06, 0xf8, 0x04, - 0x5a, 0x8c, 0xdc, 0x43, 0x4d, 0x1b, 0x08, 0x0d, 0xfb, 0x43, 0xc0, 0x47, 0xee, 0xa1, 0xc3, 0xff, 0xcf, 0xb3, 0x0b, - 0x44, 0xda, 0x9b, 0x9b, 0xc8, 0x78, 0xa4, 0x6e, 0xe0, 0xa0, 0x18, 0x1f, 0xfb, 0x66, 0xe2, 0x67, 0xce, 0xe8, 0x43, - 0x52, 0xf9, 0x0e, 0x1f, 0x2c, 0x7f, 0xbc, 0xa9, 0x99, 0x95, 0x11, 0xac, 0x87, 0xdd, 0x0e, 0x17, 0x44, 0xdb, 0x05, - 0x90, 0x7a, 0xc6, 0xab, 0x85, 0x6f, 0xbc, 0x1a, 0xdf, 0x63, 0xbc, 0xea, 0xce, 0xd4, 0x30, 0x27, 0x5b, 0xd4, 0x67, - 0x29, 0x79, 0x7e, 0x8e, 0x32, 0xc1, 0xa6, 0xcb, 0x59, 0x49, 0x55, 0x2a, 0xa1, 0xbd, 0xd8, 0xcf, 0x18, 0xdf, 0x12, - 0x8c, 0xb3, 0xe2, 0x30, 0x12, 0xa8, 0x4a, 0x25, 0x75, 0xd8, 0x2b, 0x40, 0x3d, 0x06, 0xef, 0x0d, 0x86, 0xa8, 0x91, - 0xb1, 0x9b, 0x36, 0x10, 0x1a, 0x1a, 0xeb, 0xd1, 0x9e, 0xb5, 0x1e, 0xdd, 0xed, 0x2a, 0xe3, 0x6f, 0x27, 0x37, 0x45, - 0x82, 0xa8, 0xc2, 0x6a, 0x34, 0x01, 0xde, 0x34, 0xb1, 0xb7, 0x25, 0xa7, 0xb4, 0xc0, 0xf0, 0xd9, 0x7f, 0x84, 0xa5, - 0x53, 0x49, 0x94, 0x64, 0x5e, 0x46, 0x03, 0x77, 0x0e, 0x3e, 0x8f, 0x2b, 0x58, 0x03, 0x10, 0xc9, 0x11, 0x3d, 0x5c, - 0xff, 0x08, 0xa5, 0xcb, 0x2c, 0xc9, 0x5c, 0x42, 0x66, 0x2e, 0xd2, 0x76, 0xd6, 0xc1, 0xc4, 0x99, 0xd4, 0x7a, 0x63, - 0x21, 0x87, 0x06, 0xf9, 0x01, 0x94, 0x21, 0x0e, 0x9f, 0x7c, 0x30, 0xa1, 0x52, 0x85, 0x52, 0x6d, 0x74, 0xb3, 0x1b, - 0x78, 0xe5, 0x43, 0x76, 0xc5, 0xcb, 0x2a, 0xbe, 0x5a, 0x1b, 0x4b, 0x62, 0xce, 0xee, 0x73, 0xdb, 0xa3, 0xc2, 0xbc, - 0x7a, 0xf3, 0xe2, 0xfb, 0x93, 0xc6, 0xab, 0x7d, 0xc4, 0xd1, 0x10, 0x6c, 0x2b, 0xc6, 0x18, 0xbd, 0xc5, 0xa7, 0xc1, - 0x44, 0xb9, 0x46, 0xa0, 0x77, 0x29, 0xe8, 0xb7, 0x3f, 0xd7, 0x13, 0xf0, 0x8a, 0xeb, 0xe5, 0x97, 0x7c, 0x04, 0x2c, - 0x51, 0xa1, 0x67, 0x85, 0xb9, 0x59, 0x99, 0xdf, 0xdb, 0xad, 0xc8, 0x4c, 0xbb, 0xd2, 0xc8, 0x40, 0xbc, 0xda, 0x0e, - 0x63, 0xe1, 0xd2, 0x35, 0xdd, 0x0e, 0x76, 0xb5, 0xf2, 0x2c, 0x91, 0x77, 0xbb, 0x12, 0x3a, 0x64, 0x07, 0xdc, 0x7b, - 0x19, 0xdf, 0xc2, 0xcb, 0xd2, 0xeb, 0x66, 0x33, 0x78, 0x02, 0x98, 0x09, 0x17, 0xce, 0xb2, 0x38, 0x66, 0x3c, 0x09, - 0x55, 0x6c, 0xae, 0x86, 0xc8, 0x5b, 0x11, 0x5a, 0xb3, 0xbf, 0x42, 0x31, 0x02, 0xbb, 0x93, 0xd3, 0x8f, 0xd9, 0x7a, - 0xbe, 0x02, 0xd4, 0xfc, 0xab, 0x4c, 0x00, 0xcd, 0xb5, 0x6b, 0xc1, 0x36, 0x85, 0x36, 0xd7, 0xf5, 0xb3, 0x78, 0x1d, - 0x27, 0xa0, 0xba, 0x01, 0x6f, 0x91, 0x3b, 0x2d, 0xba, 0x32, 0xe8, 0xa2, 0xf4, 0x81, 0x72, 0x2c, 0x29, 0x74, 0xf4, - 0xbd, 0x27, 0xd4, 0xb9, 0x67, 0x00, 0x97, 0x34, 0x6a, 0x9e, 0x6a, 0x29, 0x63, 0x01, 0xb0, 0xd0, 0xc1, 0x5c, 0x91, - 0xad, 0xe8, 0xd6, 0x60, 0x52, 0xc0, 0x5b, 0x03, 0xfc, 0x21, 0xb2, 0x4a, 0xdd, 0x15, 0xcb, 0xb0, 0xf4, 0xec, 0xaf, - 0xfb, 0xfd, 0xd8, 0xb3, 0xbf, 0xbe, 0xd0, 0xb4, 0x2e, 0x6e, 0x37, 0x80, 0xd4, 0x18, 0x40, 0xe4, 0x44, 0x0f, 0x84, - 0x89, 0x28, 0xd6, 0xf4, 0xfd, 0x3b, 0x31, 0x59, 0x14, 0x08, 0xfd, 0x5e, 0xbd, 0x9e, 0x94, 0x04, 0x74, 0x6a, 0x15, - 0x9b, 0x0d, 0xb4, 0xd9, 0x07, 0x04, 0x44, 0xf5, 0x33, 0xb2, 0xc5, 0x52, 0x39, 0x17, 0xab, 0xf0, 0xe1, 0x63, 0x0a, - 0x01, 0x85, 0x3b, 0x6a, 0x74, 0xde, 0x86, 0x48, 0xa0, 0xac, 0x50, 0xc4, 0x9a, 0x17, 0x6b, 0x49, 0xc8, 0x62, 0xbc, - 0x44, 0xc1, 0x95, 0x03, 0x76, 0xe5, 0x6c, 0x32, 0x2c, 0x23, 0xce, 0xc2, 0xfb, 0xbf, 0x99, 0x2c, 0x09, 0x6a, 0xae, - 0xfc, 0x40, 0x8e, 0x7b, 0x99, 0x1a, 0x7b, 0xaa, 0x51, 0x83, 0x60, 0x32, 0x82, 0xc0, 0x70, 0xc3, 0xcf, 0xf8, 0xf8, - 0x68, 0x49, 0x40, 0x45, 0x66, 0xcd, 0x42, 0xcc, 0x8b, 0xe3, 0xaf, 0x00, 0x35, 0x66, 0x74, 0xf4, 0x14, 0x40, 0x61, - 0x21, 0x20, 0xfa, 0x18, 0x64, 0xb4, 0x02, 0x7e, 0x0b, 0xf5, 0xbb, 0x75, 0xe2, 0xfb, 0xd0, 0xaf, 0x82, 0x5e, 0xc4, - 0xc0, 0x70, 0x44, 0x93, 0xc3, 0x90, 0x0f, 0x26, 0x03, 0xd0, 0x96, 0x78, 0xbb, 0xaf, 0xa5, 0x15, 0x37, 0xa7, 0x4b, - 0xa7, 0xfb, 0x27, 0x6d, 0x82, 0x24, 0x52, 0xc9, 0x4a, 0x45, 0x0c, 0x20, 0x94, 0xa5, 0xda, 0x26, 0x2b, 0xb0, 0xac, - 0x30, 0x4b, 0x9a, 0x1b, 0x94, 0xc4, 0xfd, 0xcd, 0xc0, 0x31, 0x6a, 0xd6, 0x49, 0x58, 0xb6, 0xdc, 0xa8, 0x01, 0x3e, - 0x27, 0x61, 0x85, 0xbd, 0xe1, 0xcc, 0xa5, 0x77, 0xa6, 0xc3, 0xd5, 0x31, 0x67, 0xaf, 0x39, 0x82, 0x71, 0x24, 0x78, - 0xe3, 0xa1, 0x2b, 0xa6, 0xa1, 0x22, 0x53, 0xc6, 0xc1, 0xb4, 0x07, 0xb8, 0xf7, 0x1c, 0x8c, 0xc3, 0xd8, 0xa0, 0xb2, - 0xa4, 0x3e, 0xf5, 0xee, 0x42, 0x20, 0x48, 0x6b, 0xbd, 0xcc, 0xe7, 0x78, 0x7a, 0x46, 0x28, 0xfb, 0x43, 0x0e, 0x5f, - 0x80, 0x1d, 0x05, 0x99, 0x4d, 0xf8, 0xd3, 0xc7, 0xfb, 0x81, 0xaa, 0xf8, 0x20, 0x38, 0x88, 0x45, 0x7a, 0x10, 0x0c, - 0x04, 0xfc, 0x2a, 0xf8, 0x41, 0x25, 0xe5, 0xc1, 0x45, 0x5c, 0x1c, 0xc4, 0xeb, 0xb8, 0xa8, 0x0e, 0x6e, 0xb2, 0x6a, - 0x75, 0x60, 0x3a, 0x04, 0xd0, 0xbc, 0xc1, 0x20, 0x1e, 0x04, 0x07, 0xc1, 0xa0, 0x30, 0x53, 0xbb, 0x66, 0x65, 0xe3, - 0x38, 0x33, 0x21, 0xca, 0x82, 0x66, 0x80, 0xb0, 0xc6, 0x69, 0x00, 0x7c, 0xea, 0x86, 0xa5, 0xf4, 0x02, 0xc3, 0x0d, - 0x88, 0xe9, 0x06, 0xfa, 0x00, 0x3c, 0xf2, 0x86, 0xc6, 0xb0, 0x04, 0x2e, 0x06, 0x03, 0xb2, 0x81, 0xc8, 0x05, 0x1b, - 0x6a, 0x83, 0x38, 0x84, 0x1b, 0x65, 0xa7, 0xbd, 0x0f, 0xcc, 0xb4, 0xdb, 0x01, 0xa2, 0xf2, 0x84, 0xf4, 0xfb, 0xf6, - 0x1b, 0xea, 0x5f, 0xb0, 0x57, 0x60, 0x7f, 0x55, 0x54, 0x61, 0x22, 0x95, 0xe6, 0xfb, 0x92, 0xcd, 0x06, 0x2a, 0xe2, - 0xf0, 0x9e, 0x23, 0x45, 0x1b, 0x95, 0xcb, 0xb2, 0x27, 0xab, 0x86, 0xaf, 0xc4, 0x15, 0x77, 0x7e, 0x5c, 0x95, 0x94, - 0x79, 0x95, 0xad, 0x15, 0xfb, 0x37, 0xe7, 0x9a, 0xfb, 0x03, 0xeb, 0xcf, 0xe6, 0x2b, 0xb8, 0xb6, 0x7a, 0xef, 0x9a, - 0x5c, 0x23, 0x72, 0x96, 0x50, 0x2e, 0xa9, 0x6d, 0x1e, 0xde, 0xd2, 0xf7, 0xf9, 0xd5, 0xb7, 0x99, 0x4e, 0xe3, 0xb3, - 0x0a, 0x0b, 0x17, 0xa2, 0x15, 0xc1, 0xa1, 0x21, 0x97, 0xcd, 0x23, 0xc0, 0x5c, 0xfb, 0x6c, 0x05, 0x05, 0xa9, 0xcf, - 0x2a, 0xf4, 0x6e, 0x85, 0x84, 0x97, 0x9a, 0x5d, 0x7a, 0x18, 0x48, 0x19, 0xb7, 0x87, 0x96, 0x30, 0x69, 0x79, 0x11, - 0xde, 0x7b, 0xcd, 0x4d, 0xee, 0x79, 0x88, 0xd1, 0x8b, 0x3c, 0x3b, 0x01, 0x63, 0xdd, 0x25, 0x3b, 0x1b, 0x9e, 0xf8, - 0x0d, 0xcf, 0x59, 0x8b, 0x46, 0xd3, 0x15, 0x4b, 0xfa, 0xfd, 0x18, 0x4c, 0xbc, 0x53, 0x96, 0xc3, 0xaf, 0x7c, 0x49, - 0x37, 0x0c, 0x30, 0xc5, 0xe8, 0x05, 0x24, 0xa4, 0x88, 0x44, 0xb2, 0x51, 0x27, 0xc9, 0x27, 0xba, 0x0b, 0xc0, 0xe8, - 0x17, 0xf3, 0x34, 0x5a, 0xdd, 0x6b, 0x66, 0x81, 0xe4, 0x19, 0xfa, 0xae, 0x83, 0xed, 0x8d, 0x7d, 0x90, 0x72, 0x7e, - 0x2c, 0xa6, 0x83, 0x01, 0x27, 0x1a, 0x6e, 0xbc, 0x54, 0xe2, 0x5a, 0xdd, 0xe2, 0x8e, 0x61, 0x2c, 0xf5, 0x6d, 0x11, - 0x83, 0x03, 0x76, 0xd1, 0xca, 0x6e, 0x1f, 0x60, 0x5f, 0x39, 0xde, 0xa5, 0xca, 0xee, 0xf4, 0x98, 0x69, 0x2e, 0x5b, - 0x4d, 0x3a, 0xa9, 0xb8, 0x9f, 0xc8, 0x37, 0xb9, 0x83, 0x2e, 0x97, 0x63, 0xcd, 0x5b, 0x0e, 0x40, 0x45, 0x3f, 0x52, - 0x54, 0xf7, 0x33, 0x1c, 0x61, 0x1e, 0xac, 0xdb, 0x7c, 0x72, 0x68, 0x0a, 0x1c, 0x22, 0x4f, 0xda, 0x68, 0x0a, 0xe8, - 0xde, 0xc5, 0xe3, 0xae, 0x7e, 0x5b, 0xba, 0x0b, 0x94, 0x68, 0xaf, 0xe2, 0x86, 0x1f, 0x13, 0x75, 0x3a, 0xd3, 0x86, - 0xd0, 0xbf, 0x32, 0xe2, 0xfe, 0xd2, 0xb8, 0x8a, 0x37, 0xbd, 0xcb, 0xe7, 0x1c, 0xea, 0xec, 0x86, 0x50, 0x00, 0xae, - 0xda, 0xd3, 0xa9, 0x1b, 0x43, 0x7a, 0xa5, 0x44, 0xb7, 0xc1, 0xc1, 0xee, 0xf5, 0x19, 0x47, 0xd1, 0x8f, 0x51, 0x23, - 0xdf, 0x44, 0xe2, 0xb1, 0x1c, 0xc4, 0x8f, 0x0b, 0xba, 0x8a, 0xc4, 0xe3, 0x62, 0x10, 0x3f, 0x96, 0x75, 0xbd, 0x7f, - 0xae, 0xdc, 0xdf, 0x47, 0xe4, 0x59, 0xf7, 0xf6, 0x52, 0x09, 0x1b, 0x03, 0xcf, 0xae, 0x25, 0x84, 0x53, 0xf0, 0x44, - 0xb6, 0x96, 0x3e, 0x74, 0x6e, 0xf7, 0xb1, 0x65, 0x92, 0x20, 0xe8, 0x79, 0x9b, 0x4d, 0xa2, 0xd8, 0xd9, 0xe6, 0xd1, - 0x87, 0x53, 0x20, 0xa1, 0xdb, 0x6d, 0xb3, 0xae, 0xd6, 0x80, 0x62, 0x1a, 0x8e, 0xf9, 0x61, 0x31, 0xba, 0xf1, 0xdd, - 0xf5, 0x0f, 0x8b, 0xd1, 0x8a, 0x0c, 0x27, 0x66, 0xf2, 0xe3, 0xd9, 0x78, 0x1e, 0x47, 0x93, 0xba, 0xe3, 0xb4, 0xd0, - 0xf8, 0xa7, 0xde, 0x2d, 0x14, 0x81, 0x53, 0x31, 0x82, 0x23, 0xa7, 0x42, 0x39, 0x29, 0x35, 0x30, 0xfc, 0x0f, 0xaa, - 0x3d, 0x6d, 0xda, 0xeb, 0xb8, 0x4a, 0x56, 0x99, 0xb8, 0xd4, 0xe1, 0xc3, 0x75, 0x74, 0x71, 0x1b, 0xd0, 0xce, 0xbb, - 0x4c, 0x3b, 0x7e, 0x9d, 0x34, 0xe8, 0x89, 0xab, 0x99, 0x01, 0xb7, 0xee, 0x47, 0x68, 0x86, 0xc0, 0x68, 0x79, 0xfe, - 0x0e, 0x31, 0xb7, 0x7f, 0x55, 0x36, 0xbf, 0x8a, 0xf6, 0x39, 0x32, 0x52, 0xb6, 0xc9, 0x48, 0x05, 0x46, 0x98, 0x52, - 0x24, 0x71, 0x15, 0x42, 0x20, 0xfb, 0xcf, 0x29, 0xae, 0xc5, 0xd2, 0x7b, 0x0d, 0xc2, 0x04, 0xdb, 0x05, 0xed, 0x57, - 0xb7, 0x77, 0x5b, 0x69, 0xb1, 0x47, 0xea, 0xfb, 0xdc, 0xd9, 0xae, 0x68, 0xf2, 0xf7, 0x79, 0x03, 0xda, 0x00, 0xa2, - 0xbc, 0xaf, 0x8f, 0x4a, 0xe0, 0x64, 0xc4, 0x0d, 0x25, 0x46, 0x2f, 0xe8, 0xea, 0x44, 0xee, 0xd9, 0xa9, 0x79, 0x53, - 0x31, 0x57, 0x71, 0xe5, 0x9b, 0x3d, 0xf3, 0x1f, 0x0c, 0x05, 0x15, 0x60, 0xe0, 0x6d, 0xce, 0x78, 0x74, 0xa0, 0xbb, - 0x31, 0x3a, 0x2d, 0xd8, 0x2c, 0xa8, 0xcb, 0xba, 0x69, 0xe3, 0x41, 0x23, 0x0e, 0x8a, 0x62, 0x55, 0xa8, 0x91, 0xf0, - 0x44, 0x20, 0x60, 0xca, 0xae, 0x78, 0x64, 0x04, 0x35, 0xbd, 0x09, 0x85, 0x0d, 0x05, 0x7f, 0x95, 0xa8, 0xa6, 0x37, - 0xa1, 0x4d, 0x26, 0x4e, 0x33, 0x88, 0x60, 0x46, 0x6c, 0xf7, 0x5b, 0x40, 0x9b, 0x5b, 0x33, 0xda, 0xd6, 0xb5, 0xd5, - 0x56, 0x21, 0x97, 0x14, 0x29, 0xcb, 0x7f, 0xa7, 0xa6, 0x82, 0x92, 0x5a, 0x2e, 0x7a, 0x93, 0xa6, 0x8b, 0x1e, 0xcf, - 0x8c, 0x24, 0x50, 0xb9, 0xe5, 0x8e, 0xd1, 0x1f, 0xc2, 0x02, 0x8f, 0x98, 0x38, 0xb1, 0x60, 0x6e, 0x35, 0x63, 0xd9, - 0x42, 0x2c, 0x47, 0x6b, 0x09, 0x61, 0x83, 0x8f, 0x59, 0xb6, 0x28, 0xf5, 0x43, 0xe8, 0x0b, 0x4b, 0xdf, 0x82, 0x5d, - 0x6c, 0xb0, 0x96, 0x65, 0x00, 0xbe, 0x17, 0x74, 0xbb, 0x96, 0x65, 0x24, 0x55, 0xf7, 0xe3, 0x1a, 0x4b, 0x50, 0x69, - 0x85, 0x4a, 0x4b, 0x6a, 0x2c, 0x08, 0x7c, 0x55, 0x75, 0xf9, 0x90, 0xec, 0x2a, 0x50, 0x4f, 0x1d, 0x35, 0xe0, 0x14, - 0xa8, 0x2a, 0xb0, 0x20, 0x09, 0x2a, 0x43, 0x57, 0x05, 0xa6, 0x15, 0x98, 0x66, 0xaa, 0x70, 0x51, 0x66, 0x87, 0xd2, - 0xac, 0x97, 0x7c, 0x1e, 0x0f, 0xc2, 0x64, 0x18, 0x93, 0xc7, 0x08, 0xb5, 0x7f, 0x98, 0x47, 0xb1, 0x96, 0x4b, 0xae, - 0x9d, 0x5f, 0xfc, 0xcd, 0x27, 0xec, 0x75, 0xcf, 0x30, 0x58, 0x80, 0xb3, 0xb4, 0xbd, 0xca, 0xc4, 0x3b, 0xd9, 0x0a, - 0x8e, 0x83, 0x59, 0x94, 0xc3, 0xaa, 0x27, 0x47, 0x34, 0x17, 0xb9, 0xf6, 0x2e, 0x42, 0xe4, 0x20, 0xb3, 0xc7, 0x00, - 0xbb, 0x11, 0xbe, 0x0e, 0xad, 0xcd, 0xad, 0xae, 0x10, 0x7f, 0xa3, 0x44, 0xe2, 0x27, 0x29, 0x3f, 0x6e, 0xd6, 0x2a, - 0x57, 0x65, 0xf0, 0x58, 0x75, 0x33, 0x78, 0xa6, 0x7d, 0x8f, 0xb5, 0x7f, 0x6b, 0xbb, 0x39, 0xde, 0x7b, 0xf0, 0xa0, - 0xf5, 0xbf, 0xf5, 0x24, 0x84, 0xf6, 0xca, 0x49, 0xea, 0x8e, 0x1a, 0x3d, 0x33, 0x59, 0x23, 0x2a, 0x61, 0x6a, 0x77, - 0x2a, 0xc7, 0x40, 0x4d, 0x07, 0x70, 0x2d, 0x51, 0x13, 0xf4, 0xa4, 0x60, 0x63, 0x38, 0xe2, 0x2c, 0x0e, 0xda, 0x71, - 0x8c, 0xe2, 0xe5, 0x5c, 0x89, 0x97, 0xf3, 0x19, 0xe3, 0x00, 0xad, 0x05, 0x48, 0xf5, 0x1a, 0xf6, 0x33, 0x57, 0xb0, - 0xc0, 0xe6, 0xce, 0x77, 0x64, 0x81, 0x0c, 0x71, 0xb2, 0x39, 0x4e, 0xf6, 0xb8, 0xd6, 0x73, 0x2f, 0xf0, 0x71, 0x52, - 0x2f, 0xbd, 0xba, 0xca, 0x76, 0x5d, 0x2b, 0x56, 0x2e, 0x8a, 0xc1, 0x04, 0x82, 0xb2, 0x94, 0x8b, 0x62, 0x38, 0x59, - 0xd2, 0x1c, 0x7e, 0x2c, 0x1b, 0xe8, 0x10, 0xab, 0x41, 0x02, 0x97, 0xce, 0x1e, 0x03, 0xde, 0x50, 0x6a, 0x71, 0x37, - 0xd6, 0x91, 0x63, 0x1d, 0xc5, 0x61, 0x18, 0x03, 0xae, 0xac, 0x13, 0x78, 0xdf, 0x7f, 0x7d, 0x6c, 0x02, 0xb2, 0x6a, - 0x57, 0x78, 0x35, 0xca, 0x5d, 0x57, 0x1a, 0x7d, 0x49, 0xe9, 0x09, 0x2f, 0x78, 0x2a, 0xd9, 0xed, 0x7a, 0x06, 0xce, - 0x96, 0x78, 0x48, 0xbc, 0x63, 0x44, 0x2f, 0xa6, 0x8d, 0xcc, 0x9c, 0xc0, 0x99, 0xed, 0x2e, 0xdb, 0x98, 0x1f, 0x3b, - 0xc0, 0xc1, 0x22, 0x08, 0x89, 0x1b, 0xc2, 0x30, 0xb1, 0x59, 0x39, 0xd4, 0x42, 0xb8, 0xae, 0x85, 0xd7, 0x71, 0x5a, - 0xc6, 0xe0, 0x22, 0xad, 0x6d, 0x13, 0xef, 0xa1, 0xeb, 0x9e, 0x1f, 0x73, 0xab, 0x63, 0xb4, 0x85, 0xf4, 0xdb, 0xd1, - 0xe9, 0x03, 0x87, 0x01, 0x68, 0x7a, 0x30, 0xaf, 0xda, 0x67, 0x12, 0x37, 0xa7, 0x9d, 0x20, 0x24, 0x02, 0x51, 0x94, - 0xce, 0x08, 0xd3, 0xbf, 0xd7, 0x5c, 0x56, 0xd1, 0xea, 0x41, 0x9e, 0x39, 0xe4, 0x59, 0xe8, 0x6d, 0x0f, 0x5a, 0x35, - 0x77, 0x83, 0x71, 0xe2, 0x76, 0x7b, 0xe7, 0xff, 0x2d, 0xeb, 0xda, 0x6a, 0x8d, 0x78, 0xdc, 0xae, 0x7e, 0xd0, 0xd8, - 0xab, 0x3d, 0x15, 0x03, 0x66, 0x2d, 0xbd, 0x33, 0xaa, 0xe4, 0x45, 0xc6, 0x4b, 0x3c, 0xa9, 0xd6, 0x0d, 0x1f, 0xef, - 0x9b, 0x6c, 0x64, 0x1e, 0xc8, 0x14, 0x10, 0xcf, 0x6f, 0x52, 0xa3, 0x3e, 0x4e, 0x51, 0x02, 0xfe, 0x4e, 0xc7, 0x37, - 0xa2, 0x1f, 0xed, 0x8b, 0x4b, 0x5e, 0xbd, 0xbd, 0x11, 0xe6, 0xc5, 0x73, 0xab, 0xf3, 0xa7, 0xaf, 0x0b, 0x1f, 0x3a, - 0x1c, 0xb5, 0x77, 0x50, 0x64, 0xc9, 0xc4, 0x6c, 0x62, 0x64, 0x6d, 0x62, 0xfe, 0x51, 0xc1, 0xc5, 0x44, 0x15, 0x7a, - 0xd6, 0xd9, 0x13, 0xa6, 0x00, 0x7d, 0xe3, 0x18, 0x95, 0x8c, 0x61, 0xc1, 0x40, 0x9d, 0xa6, 0x84, 0xe8, 0xa1, 0x98, - 0x63, 0xbc, 0x62, 0x00, 0x85, 0x29, 0x14, 0x88, 0xa2, 0xb3, 0x0f, 0x07, 0x9a, 0xd0, 0xef, 0xdf, 0xa4, 0x3a, 0x03, - 0x2d, 0xeb, 0xa9, 0x04, 0x51, 0x1d, 0x44, 0x5b, 0xe5, 0x45, 0xf8, 0xe3, 0x8a, 0x96, 0x19, 0x5d, 0x09, 0x9a, 0x0a, - 0x9a, 0x64, 0xf4, 0x82, 0x2b, 0x51, 0xf1, 0x85, 0x60, 0x8a, 0xb6, 0x1b, 0xc2, 0xfe, 0xaf, 0x06, 0x5d, 0x6f, 0xc5, - 0x5a, 0x43, 0xbb, 0x13, 0x64, 0x84, 0x16, 0x4b, 0x1d, 0x84, 0x0c, 0x95, 0x93, 0xd0, 0xb5, 0x4a, 0xe3, 0x15, 0xb8, - 0x64, 0x9a, 0x8d, 0x56, 0x71, 0x19, 0x06, 0xf6, 0xab, 0xc0, 0x62, 0x72, 0x60, 0xd2, 0xe9, 0xe6, 0xfc, 0x99, 0xbc, - 0x5a, 0x4b, 0xc1, 0x45, 0xa5, 0x20, 0xfa, 0x0d, 0xee, 0xbb, 0x89, 0xab, 0xce, 0x9a, 0xb5, 0xd2, 0x87, 0xbe, 0xf5, - 0x59, 0x1b, 0xf7, 0x85, 0xc1, 0x31, 0xd8, 0xfb, 0x88, 0x18, 0x48, 0x83, 0x4a, 0xb7, 0x38, 0x34, 0x01, 0xba, 0x74, - 0x48, 0x21, 0x4b, 0xa6, 0x32, 0x55, 0x82, 0x8a, 0x6f, 0xfc, 0x5e, 0xca, 0x6a, 0xf4, 0xe7, 0x86, 0x17, 0x77, 0xa7, - 0x3c, 0xe7, 0x38, 0x46, 0x41, 0x12, 0x8b, 0xeb, 0xb8, 0x0c, 0x88, 0x6f, 0x79, 0x15, 0x1c, 0xa5, 0x26, 0x6c, 0xcc, - 0x5e, 0xd5, 0xa8, 0xf5, 0x92, 0xe8, 0x2b, 0xa3, 0x7c, 0x63, 0x30, 0x34, 0x11, 0x55, 0xd0, 0xf7, 0x5a, 0xdd, 0xd3, - 0xea, 0x86, 0x05, 0xc4, 0x5f, 0x28, 0xbd, 0x50, 0xeb, 0x75, 0x33, 0xe6, 0x86, 0x89, 0x10, 0x34, 0xfa, 0xaa, 0x5e, - 0xd6, 0x9e, 0x5b, 0x9a, 0x8a, 0x8c, 0x1b, 0x6d, 0x73, 0x7e, 0x09, 0x32, 0x3e, 0x67, 0x2e, 0x34, 0xa9, 0x6b, 0xaa, - 0xa0, 0x0a, 0xa3, 0xed, 0x6d, 0x23, 0x9d, 0xde, 0x81, 0x3b, 0x9b, 0x31, 0x3b, 0xd2, 0x2e, 0x8d, 0x35, 0x2d, 0x78, - 0xb9, 0x96, 0xa2, 0x84, 0x30, 0xce, 0xbd, 0x31, 0xbd, 0x8a, 0x33, 0x51, 0xc5, 0x99, 0x38, 0x29, 0xd7, 0x3c, 0xa9, - 0xde, 0xc3, 0x2d, 0x4e, 0x59, 0xdd, 0xd4, 0x25, 0x5c, 0xe9, 0x92, 0x03, 0x0c, 0xa6, 0xa6, 0xe2, 0x1e, 0x3b, 0x83, - 0x8b, 0xea, 0xf7, 0x68, 0x25, 0x31, 0x16, 0xaa, 0x2e, 0x3e, 0x3e, 0x2f, 0x65, 0xbe, 0xa9, 0x40, 0xbb, 0x7b, 0x51, - 0x45, 0x47, 0x4f, 0xd6, 0xb7, 0x53, 0x75, 0x83, 0x89, 0x9e, 0x1c, 0xad, 0x6f, 0x7b, 0xd9, 0xd5, 0x5a, 0x16, 0x55, - 0x2c, 0xaa, 0xa9, 0x42, 0x24, 0x4b, 0xe2, 0x3c, 0x09, 0x27, 0xe3, 0xf1, 0x17, 0x07, 0xc3, 0x03, 0xc8, 0x40, 0xa6, - 0x7f, 0x0d, 0x95, 0xcb, 0xd1, 0x70, 0x32, 0x1e, 0x4f, 0xa5, 0xba, 0xdb, 0x45, 0xa3, 0x49, 0x8d, 0xf5, 0x0c, 0x13, - 0x3d, 0x33, 0x23, 0x7e, 0xbb, 0x8e, 0x45, 0x0a, 0xf1, 0xeb, 0x74, 0xf1, 0x47, 0x4f, 0xc6, 0x8d, 0xf2, 0xed, 0xa7, - 0x4f, 0xeb, 0xdf, 0x6b, 0x13, 0xd6, 0xda, 0xb4, 0xfb, 0xd9, 0xef, 0xc7, 0x6a, 0xbe, 0x67, 0xc7, 0x87, 0xfa, 0xc7, - 0xef, 0x75, 0x3d, 0x7d, 0x5d, 0x84, 0x8b, 0x7f, 0x86, 0x6a, 0x3e, 0x4f, 0x8a, 0x22, 0xbe, 0xab, 0x21, 0x92, 0xa7, - 0x70, 0xde, 0x24, 0xd4, 0xdb, 0x06, 0xf4, 0x88, 0x4c, 0x2f, 0x04, 0x83, 0x6f, 0xde, 0x57, 0x61, 0xc0, 0xcb, 0xf5, - 0x90, 0x8b, 0x2a, 0xab, 0xee, 0x86, 0x98, 0x27, 0xc0, 0x4f, 0x0d, 0x6f, 0xf6, 0xac, 0x30, 0xc4, 0xe6, 0xa2, 0xe0, - 0xfc, 0x2f, 0x1e, 0x2a, 0xe3, 0xe8, 0x31, 0x1a, 0x47, 0x8f, 0xa9, 0x1a, 0x8c, 0xc9, 0xd7, 0x54, 0x77, 0x66, 0xf2, - 0x35, 0x98, 0x20, 0x65, 0xed, 0x6f, 0x94, 0x71, 0x62, 0x34, 0xa6, 0xd7, 0x2f, 0xf3, 0x6c, 0x0d, 0x4c, 0xf0, 0x4a, - 0xff, 0xa8, 0x09, 0x7d, 0xcf, 0xdb, 0xd9, 0x47, 0xa3, 0xd1, 0xb3, 0x82, 0x8e, 0x46, 0xa3, 0x8f, 0x59, 0x4d, 0xe8, - 0x5a, 0x74, 0xbc, 0x7f, 0xcf, 0xe9, 0xb9, 0x4c, 0xef, 0xa2, 0x20, 0xa0, 0xab, 0x2c, 0x4d, 0xb9, 0x50, 0x65, 0x9d, - 0xa6, 0xed, 0xbc, 0xaa, 0x85, 0x08, 0xfc, 0xa3, 0xdb, 0x88, 0x10, 0x44, 0x84, 0xbe, 0xdd, 0xeb, 0xd9, 0x68, 0x34, - 0x3a, 0x4d, 0x4d, 0xb5, 0x8e, 0x21, 0x7f, 0x8d, 0xe6, 0x03, 0xce, 0x2e, 0x1f, 0xac, 0x6f, 0x4c, 0xb4, 0x93, 0xc3, - 0xff, 0x1e, 0xce, 0x17, 0xe3, 0xe1, 0xb7, 0xa3, 0xe5, 0xe3, 0x43, 0x1a, 0x04, 0x3e, 0x68, 0x75, 0xa8, 0xad, 0x39, - 0xa6, 0xe5, 0xf1, 0x78, 0x4a, 0xca, 0x01, 0x7b, 0x62, 0x7d, 0x69, 0xbe, 0x78, 0x02, 0x48, 0xa4, 0x28, 0x42, 0x0d, - 0x9c, 0xf4, 0x0f, 0xaf, 0x22, 0xaf, 0x04, 0xe0, 0xa3, 0xd9, 0x48, 0x06, 0x46, 0x0b, 0x38, 0x8e, 0xa0, 0xbc, 0xda, - 0x9a, 0x46, 0xf4, 0x18, 0xcb, 0x4c, 0x54, 0xd0, 0xf1, 0xb4, 0xbc, 0xc9, 0xaa, 0x64, 0x85, 0x81, 0x8d, 0xe2, 0x92, - 0x07, 0x5f, 0x04, 0x51, 0xc9, 0x8e, 0x9e, 0x4e, 0x15, 0xbc, 0x2f, 0x26, 0xa5, 0xfc, 0x12, 0x12, 0xbf, 0x1d, 0x23, - 0x04, 0x2a, 0xd1, 0x1e, 0x8b, 0x58, 0xe3, 0xcb, 0x5c, 0xc6, 0xe0, 0xc1, 0x59, 0x6a, 0x9e, 0xc5, 0x9e, 0x04, 0xd6, - 0xfe, 0xa2, 0xd5, 0x1c, 0x09, 0xcd, 0x09, 0x25, 0x93, 0x87, 0x25, 0x95, 0x5f, 0x4c, 0xd0, 0x2b, 0x08, 0xdc, 0xaa, - 0x23, 0x38, 0xee, 0xac, 0x65, 0x83, 0x5e, 0x3e, 0x29, 0x3b, 0x5c, 0xfc, 0xef, 0x92, 0x2e, 0x07, 0x87, 0x6e, 0x68, - 0xde, 0x6a, 0xf7, 0xd5, 0x0a, 0x19, 0xa5, 0x2a, 0x7c, 0x96, 0x12, 0x6b, 0x7c, 0xca, 0xd9, 0x6c, 0x6b, 0xba, 0x33, - 0xaa, 0x8a, 0xec, 0x2a, 0x24, 0xba, 0x57, 0x0e, 0x14, 0x33, 0x88, 0xb2, 0x11, 0xae, 0x1f, 0xb0, 0x16, 0xf1, 0x3a, - 0x79, 0xcd, 0x8b, 0x2a, 0x4b, 0xd4, 0xfb, 0xeb, 0xc6, 0x7b, 0xa0, 0x06, 0xaa, 0x41, 0xef, 0x0a, 0x06, 0xf3, 0xfc, - 0xb6, 0x00, 0xd0, 0xce, 0x92, 0x17, 0xd7, 0xdc, 0xa7, 0x1b, 0x41, 0x50, 0xbb, 0x66, 0x5e, 0x36, 0x82, 0x4d, 0xc0, - 0x57, 0xef, 0x0a, 0xc0, 0xdc, 0x08, 0x41, 0x6a, 0x0a, 0xa1, 0x70, 0xe0, 0x02, 0x5f, 0x55, 0x45, 0x76, 0xbe, 0xa9, - 0x38, 0x06, 0xfb, 0xf0, 0xe2, 0x26, 0x2a, 0x27, 0x3c, 0x1e, 0x06, 0xf8, 0x23, 0xa0, 0x2a, 0xe0, 0x86, 0xf1, 0xb0, - 0x83, 0x17, 0xea, 0x97, 0x7b, 0xa3, 0xf6, 0x08, 0x7b, 0x9d, 0x86, 0x10, 0x5c, 0x07, 0x1f, 0x02, 0x58, 0x52, 0x84, - 0xbe, 0xc5, 0x53, 0x35, 0x0c, 0x2e, 0xf2, 0x6c, 0xad, 0x93, 0xaa, 0x51, 0x47, 0xf3, 0xa1, 0xd4, 0x8e, 0xe4, 0x80, - 0x7a, 0xe9, 0x31, 0xa6, 0x17, 0x2a, 0x5d, 0x15, 0xe5, 0x8c, 0x50, 0xde, 0xe9, 0x89, 0x71, 0x61, 0xfa, 0x38, 0x44, - 0x7e, 0x79, 0x57, 0xa8, 0xd0, 0x2f, 0x7c, 0x09, 0xe0, 0x57, 0x70, 0xbb, 0xdf, 0x8f, 0xef, 0xa2, 0xb2, 0x9f, 0x71, - 0x76, 0xf8, 0xdf, 0x8b, 0x78, 0xf8, 0xd7, 0x78, 0xf8, 0xed, 0x72, 0x10, 0x0e, 0xed, 0x4f, 0xf2, 0xf8, 0xd1, 0x21, - 0x7d, 0xc9, 0x2d, 0x57, 0x02, 0x0b, 0xbf, 0x11, 0xdc, 0x46, 0xad, 0x84, 0x20, 0x0a, 0xf0, 0x46, 0xe1, 0x56, 0xe3, - 0x04, 0x00, 0xfe, 0x82, 0xff, 0x0a, 0xd0, 0x48, 0xc8, 0x5d, 0x34, 0x40, 0x3f, 0xa0, 0x7e, 0xcf, 0xbe, 0x6a, 0x18, - 0xc8, 0x81, 0x78, 0x42, 0xc5, 0x40, 0x21, 0xba, 0x8c, 0x89, 0x82, 0xfd, 0xb5, 0xd9, 0x77, 0xbb, 0x5e, 0x5b, 0xf2, - 0x83, 0x5f, 0xfa, 0x99, 0x26, 0x66, 0xde, 0xe1, 0x86, 0xb2, 0x96, 0xeb, 0x10, 0xb1, 0xf1, 0xf4, 0xaf, 0x9c, 0x41, - 0xac, 0xc9, 0xeb, 0x0c, 0x7c, 0x18, 0xec, 0x17, 0xe3, 0x39, 0xb0, 0x0d, 0x70, 0xc7, 0x29, 0xf8, 0x45, 0x06, 0x6e, - 0xcd, 0x22, 0xc6, 0x0b, 0xb6, 0x5d, 0x12, 0xfd, 0x7e, 0x2f, 0xcf, 0xc2, 0x5c, 0xe3, 0x2c, 0xe7, 0xb5, 0x11, 0xbb, - 0xa3, 0x4e, 0x18, 0xc4, 0xed, 0x7a, 0x08, 0x86, 0x6a, 0x08, 0x8a, 0x8e, 0xb6, 0xb8, 0x7a, 0x6d, 0x3d, 0x85, 0xe9, - 0xad, 0xaa, 0xaf, 0x18, 0xfd, 0x21, 0x33, 0x81, 0x85, 0xb4, 0x6b, 0x8e, 0x75, 0xcd, 0x31, 0xd2, 0x9e, 0x7e, 0x5f, - 0x34, 0xc8, 0x4f, 0x67, 0xe1, 0x41, 0xa0, 0x4a, 0x95, 0x7b, 0x65, 0x51, 0x6e, 0x4b, 0xf3, 0xc6, 0xb0, 0xa6, 0x79, - 0x66, 0xe3, 0xba, 0xcc, 0x7b, 0xbd, 0x30, 0x44, 0x87, 0x46, 0x2c, 0x15, 0x6b, 0x83, 0xf0, 0x3e, 0x26, 0x61, 0x74, - 0x05, 0xb2, 0xba, 0xf0, 0x8c, 0x13, 0xe4, 0xcb, 0xc0, 0x64, 0x4d, 0x55, 0xeb, 0xe5, 0x84, 0xc7, 0x46, 0xbe, 0x6c, - 0x04, 0x0d, 0xf2, 0x92, 0xa2, 0xde, 0xc4, 0xed, 0xd8, 0x47, 0x2d, 0xa4, 0xc6, 0x6d, 0x3d, 0xed, 0x69, 0x52, 0xd1, - 0x63, 0xbd, 0x4a, 0xfd, 0x02, 0xcb, 0x02, 0x4b, 0x3e, 0x08, 0xed, 0x69, 0x5a, 0x81, 0x19, 0xae, 0x6d, 0x06, 0x43, - 0x3f, 0x1c, 0xda, 0x22, 0x74, 0x46, 0x6d, 0x4b, 0x08, 0xdb, 0x36, 0x08, 0x2b, 0xef, 0x89, 0x7c, 0xf1, 0xc4, 0x63, - 0x84, 0x43, 0x6e, 0x36, 0xb3, 0x68, 0x60, 0x98, 0x5f, 0xc9, 0x66, 0xf3, 0x74, 0x73, 0xbd, 0xa8, 0x98, 0x02, 0xb6, - 0xdb, 0x5a, 0x10, 0xfc, 0xfb, 0x31, 0x9b, 0xe3, 0xdf, 0xac, 0xdf, 0xef, 0x85, 0xf8, 0x8b, 0x63, 0xf0, 0x9e, 0x85, - 0x58, 0xb2, 0x8f, 0x20, 0x53, 0x21, 0x11, 0xa6, 0x2a, 0xe3, 0x37, 0x56, 0x81, 0x05, 0x9c, 0xf9, 0x40, 0xe5, 0xc2, - 0x4c, 0xf6, 0xf2, 0xe2, 0x1a, 0x72, 0xd2, 0x1a, 0xa7, 0x6c, 0x94, 0x25, 0xca, 0x75, 0x21, 0x1b, 0xc5, 0x79, 0x16, - 0x97, 0xbc, 0xdc, 0xed, 0xf4, 0xe1, 0x98, 0x14, 0x1c, 0xd8, 0x53, 0x45, 0xa5, 0x4a, 0xd6, 0x91, 0xea, 0xc6, 0x5f, - 0x86, 0x05, 0xee, 0x53, 0xbe, 0x28, 0x0c, 0x8d, 0x38, 0x80, 0xcb, 0x3b, 0x53, 0xb7, 0xd2, 0x5e, 0x58, 0x40, 0xf3, - 0x4a, 0x42, 0xb6, 0x98, 0xea, 0x59, 0xb4, 0xc6, 0x4c, 0x2c, 0x8a, 0x25, 0x84, 0x91, 0x29, 0x96, 0x60, 0x33, 0xc5, - 0x05, 0x78, 0x91, 0xc4, 0x00, 0x13, 0x17, 0x93, 0x29, 0xc4, 0x33, 0x57, 0xe5, 0xc4, 0x4b, 0x73, 0xbf, 0x4c, 0x1c, - 0x52, 0x06, 0xbc, 0xaa, 0x0d, 0x9a, 0x98, 0x6d, 0x38, 0xea, 0x04, 0x39, 0x31, 0xf9, 0xfd, 0x54, 0x41, 0x88, 0x3b, - 0x71, 0x24, 0x5c, 0x56, 0x6c, 0x17, 0x5e, 0x74, 0x20, 0xc6, 0xa8, 0xc1, 0x29, 0x3f, 0x31, 0x38, 0x1a, 0x83, 0x73, - 0xeb, 0x9d, 0x20, 0x45, 0x18, 0x93, 0xad, 0x64, 0x57, 0x32, 0x14, 0x8b, 0x78, 0x09, 0xca, 0xba, 0x78, 0x09, 0x96, - 0x35, 0xc6, 0x00, 0x13, 0xe4, 0x55, 0xdc, 0x0b, 0xfd, 0x44, 0x71, 0x85, 0x48, 0xcf, 0xca, 0xf5, 0x51, 0xd1, 0x0e, - 0x7d, 0x81, 0xd7, 0x7b, 0x65, 0x8e, 0x9b, 0xf5, 0x58, 0x20, 0xb1, 0x21, 0x60, 0x6c, 0xa4, 0xd3, 0x54, 0x6b, 0xdd, - 0x1b, 0x33, 0x0f, 0x7c, 0x9a, 0x8d, 0x84, 0xac, 0xce, 0x2e, 0x40, 0x84, 0xe2, 0xa3, 0xc1, 0x23, 0xbf, 0x88, 0x3b, - 0xcb, 0xbc, 0xb5, 0x2d, 0x2a, 0xd9, 0x6c, 0x0b, 0x20, 0x7d, 0x3a, 0x5a, 0x94, 0x92, 0x53, 0x94, 0xa4, 0x76, 0x9b, - 0x02, 0x56, 0x92, 0xbf, 0x80, 0x21, 0xd8, 0xd8, 0x81, 0x70, 0x3a, 0x45, 0x88, 0x4f, 0x34, 0x45, 0x64, 0xc5, 0xb0, - 0xa4, 0x38, 0xb6, 0x25, 0xa2, 0x7e, 0xda, 0xb2, 0xec, 0x60, 0x98, 0xa8, 0xb8, 0xcf, 0x53, 0x8f, 0x12, 0x05, 0x01, - 0xd5, 0x43, 0x0e, 0x12, 0x5b, 0xdb, 0x40, 0x78, 0x40, 0x1e, 0xd1, 0x1b, 0xeb, 0xef, 0xb3, 0xce, 0xb3, 0x0b, 0xcd, - 0x51, 0xb9, 0xde, 0x15, 0x66, 0x8c, 0xf0, 0x24, 0x33, 0x30, 0xf9, 0xde, 0x79, 0x66, 0xd4, 0x14, 0x3d, 0x0f, 0x9f, - 0xec, 0x04, 0x23, 0xd2, 0xdd, 0x33, 0xe8, 0x26, 0x78, 0x55, 0x87, 0x8d, 0x76, 0xa5, 0x20, 0x24, 0x4c, 0x2d, 0x9a, - 0x98, 0xf5, 0xac, 0x01, 0xf5, 0x6e, 0xd7, 0xd3, 0x73, 0x75, 0xff, 0xdc, 0xed, 0x76, 0x3d, 0xec, 0xd6, 0xf3, 0xb4, - 0xdb, 0x0a, 0xbc, 0x52, 0x1f, 0xb4, 0xc7, 0x9f, 0xbb, 0xf1, 0xe7, 0x06, 0xc9, 0xa3, 0x74, 0x34, 0xd3, 0xd6, 0x07, - 0xe1, 0x70, 0xd3, 0xbb, 0x46, 0x93, 0xbe, 0xcf, 0x42, 0x49, 0xd7, 0xa2, 0x51, 0x5d, 0xed, 0x4c, 0x2a, 0x1f, 0x5c, - 0xff, 0x0f, 0xaf, 0x02, 0x3c, 0xe2, 0xd4, 0xce, 0xbe, 0xb7, 0x41, 0x45, 0xa3, 0x2d, 0x1c, 0x29, 0x42, 0x0f, 0x48, - 0xc2, 0x7d, 0x2d, 0x6b, 0x71, 0x9b, 0xa7, 0xd9, 0xc3, 0xf4, 0xe9, 0x55, 0xea, 0x5b, 0xdd, 0xbb, 0x65, 0x96, 0x99, - 0x03, 0xaf, 0xa2, 0x38, 0xa0, 0x51, 0x17, 0xed, 0xbb, 0xca, 0xca, 0x12, 0xbc, 0x3c, 0xe0, 0xfa, 0x7c, 0xca, 0x7d, - 0xb8, 0xb9, 0xcb, 0xaa, 0xb9, 0x49, 0x4f, 0xb3, 0x45, 0xb6, 0xdc, 0xed, 0x42, 0xfc, 0xdb, 0xd5, 0x22, 0x47, 0x93, - 0x17, 0xa0, 0xc3, 0xc3, 0xc8, 0x3d, 0x4c, 0x37, 0xce, 0xdb, 0xfc, 0x7f, 0x89, 0x86, 0x93, 0xc0, 0x09, 0xd0, 0x8b, - 0xf9, 0x23, 0x90, 0xc1, 0x18, 0xa7, 0x7e, 0x31, 0xd7, 0x6b, 0x06, 0xa2, 0x6f, 0x89, 0x08, 0x70, 0x74, 0xb1, 0x91, - 0x68, 0x64, 0xc1, 0x49, 0x4d, 0xc0, 0x62, 0xd3, 0x96, 0xf7, 0xc1, 0xd2, 0xb6, 0xaa, 0xb8, 0xf3, 0x96, 0x34, 0xc7, - 0x75, 0xe0, 0x6c, 0xfb, 0xcd, 0x10, 0x9b, 0xb2, 0xab, 0x25, 0x72, 0xbf, 0xbc, 0xa6, 0xbd, 0x71, 0x9d, 0xc0, 0xac, - 0x6d, 0x6b, 0xcb, 0xf8, 0xd9, 0xd2, 0x7f, 0xd2, 0x83, 0xab, 0x8c, 0x9f, 0x16, 0xc6, 0x2a, 0xc1, 0xee, 0x1b, 0xcf, - 0x77, 0x00, 0xc2, 0xb1, 0xf9, 0xf4, 0xf8, 0x34, 0xf3, 0xe8, 0x31, 0x10, 0x1d, 0xf3, 0x51, 0xe9, 0x3e, 0xb2, 0xbb, - 0xd7, 0x0f, 0x80, 0xb7, 0xa8, 0xda, 0x05, 0x2d, 0xca, 0x25, 0x04, 0x12, 0xf5, 0xca, 0x2b, 0x2c, 0x9f, 0x19, 0xb3, - 0x4b, 0x20, 0x43, 0x05, 0x01, 0x9b, 0xa4, 0xae, 0x73, 0x21, 0x56, 0x1d, 0x56, 0xe6, 0x23, 0x09, 0x9b, 0x85, 0x68, - 0xce, 0x19, 0xcc, 0x83, 0xff, 0x0a, 0x06, 0xe5, 0x20, 0x88, 0x82, 0x28, 0x08, 0xc8, 0xa0, 0x80, 0x5f, 0x88, 0x33, - 0x46, 0x30, 0x46, 0x09, 0x74, 0xf8, 0x1d, 0x67, 0x3e, 0x23, 0xf2, 0xa2, 0x11, 0xc6, 0xd2, 0x0d, 0xc0, 0xb9, 0x94, - 0x39, 0x8f, 0xd1, 0xc7, 0xe2, 0x1d, 0x67, 0x19, 0xa1, 0xef, 0xbc, 0x53, 0xf9, 0x11, 0x6f, 0x04, 0xb7, 0xdb, 0x1f, - 0xb6, 0x97, 0x3c, 0xcc, 0x68, 0x6f, 0x4c, 0xdf, 0x71, 0x12, 0x65, 0x0d, 0xe7, 0x61, 0x0e, 0x3d, 0xab, 0x2c, 0x6b, - 0x45, 0x0d, 0xb9, 0x41, 0xb1, 0x2e, 0xb2, 0x4c, 0x4e, 0x86, 0xab, 0xe6, 0x54, 0xe0, 0xba, 0xb3, 0xeb, 0x05, 0x24, - 0x65, 0x42, 0xb3, 0x74, 0x36, 0x7c, 0xb5, 0x6d, 0xd9, 0xf3, 0xd6, 0x29, 0xe4, 0x35, 0x44, 0x45, 0x3f, 0x74, 0x04, - 0xd4, 0xd0, 0x8a, 0xcb, 0x0a, 0x5c, 0x76, 0x4d, 0x7b, 0xb8, 0x69, 0x8f, 0x69, 0xc6, 0x07, 0x88, 0x11, 0xc7, 0xb1, - 0x65, 0x60, 0x37, 0xe1, 0xf0, 0x6c, 0x9c, 0xef, 0xcb, 0x3e, 0xbd, 0x75, 0xb5, 0x78, 0x84, 0xb5, 0xe7, 0xad, 0x90, - 0x10, 0x20, 0x2d, 0x4d, 0xa5, 0xbb, 0x5d, 0x10, 0xc0, 0x00, 0xf7, 0xfb, 0x3d, 0xe0, 0x5a, 0x0d, 0x3b, 0x69, 0x6e, - 0xcd, 0x96, 0xd8, 0x2b, 0x0a, 0x8f, 0x81, 0x28, 0x35, 0xff, 0x19, 0x04, 0x14, 0xcf, 0xdd, 0x10, 0xec, 0x2b, 0xd9, - 0x6c, 0x5b, 0xf4, 0xfb, 0xcf, 0x0b, 0x7c, 0x40, 0x39, 0x28, 0x88, 0x75, 0x75, 0xdc, 0x0a, 0xc3, 0x3e, 0xa9, 0x0f, - 0x71, 0x2c, 0xf2, 0x2c, 0x74, 0x84, 0xa5, 0x32, 0x84, 0x85, 0x2b, 0x46, 0x3a, 0x88, 0x83, 0x9a, 0x74, 0x0e, 0x56, - 0xe5, 0x82, 0x0d, 0xf7, 0x7a, 0x7f, 0x01, 0x2c, 0x78, 0xe6, 0x0d, 0xcb, 0x7b, 0x0f, 0x00, 0xac, 0xd7, 0xc3, 0x85, - 0xe2, 0x5e, 0xbe, 0x6c, 0xa0, 0x4f, 0xe2, 0x4b, 0xcb, 0xae, 0xcf, 0xb5, 0xac, 0x64, 0x34, 0x1a, 0x55, 0xb5, 0x92, - 0x7c, 0x38, 0xf2, 0xd2, 0x42, 0xad, 0x94, 0x71, 0xca, 0x53, 0xb0, 0xf4, 0x36, 0x94, 0x6e, 0xb1, 0xa4, 0x6b, 0x2e, - 0x52, 0xf5, 0xd3, 0x43, 0x9b, 0x6c, 0x10, 0xd7, 0xac, 0xa9, 0xb3, 0xb0, 0xc3, 0x0f, 0x01, 0x1f, 0xed, 0xc3, 0xdc, - 0xa5, 0x6b, 0x58, 0x5a, 0x10, 0x47, 0xc6, 0x05, 0x0f, 0x5d, 0x1e, 0xc0, 0xfa, 0x33, 0x87, 0x24, 0x7e, 0x0a, 0x3f, - 0xe7, 0x26, 0xad, 0xe3, 0x33, 0x9c, 0xcd, 0xa8, 0x54, 0x37, 0x82, 0xf6, 0x6b, 0x48, 0x24, 0x06, 0xd9, 0xb8, 0xc1, - 0x50, 0xb4, 0xee, 0x36, 0x70, 0xe5, 0xb7, 0xf4, 0xce, 0xa7, 0x41, 0x80, 0x6d, 0x8d, 0xc5, 0x00, 0x60, 0x28, 0xfe, - 0x40, 0x55, 0x8d, 0xb9, 0xa2, 0x98, 0x86, 0xa9, 0x44, 0x7b, 0xc7, 0x71, 0x1d, 0x35, 0xae, 0xb2, 0x82, 0x95, 0xd6, - 0x96, 0xd7, 0xbd, 0xa5, 0x85, 0x2d, 0x01, 0xd5, 0x60, 0xb8, 0x13, 0xc0, 0x67, 0x44, 0xaa, 0x03, 0x41, 0x76, 0x1f, - 0x1c, 0x34, 0x67, 0x09, 0x9e, 0x87, 0x21, 0xfc, 0x81, 0x85, 0x03, 0xcb, 0x52, 0xf5, 0x73, 0x35, 0x8d, 0xe1, 0xdc, - 0xcd, 0xd5, 0x0e, 0x9f, 0xaf, 0x40, 0x91, 0xa7, 0xe6, 0xd4, 0x5c, 0xbe, 0xf2, 0xc6, 0x7e, 0x8f, 0x09, 0xe6, 0x31, - 0xb3, 0x0d, 0xbf, 0xf5, 0x74, 0x5b, 0x5f, 0x58, 0x37, 0x70, 0xd2, 0x5e, 0x38, 0xed, 0xc5, 0x76, 0x65, 0x20, 0xee, - 0xea, 0x86, 0x10, 0xe1, 0x95, 0x26, 0x16, 0x59, 0x43, 0xa6, 0x63, 0xb1, 0x31, 0x54, 0x9b, 0x8a, 0x67, 0x5a, 0x21, - 0x5e, 0x4e, 0xd5, 0x85, 0xa9, 0x95, 0xca, 0x84, 0x41, 0x98, 0x29, 0x61, 0x51, 0x65, 0xe0, 0xb3, 0x5f, 0x21, 0xc5, - 0xb5, 0xf5, 0xbc, 0xc1, 0xe5, 0x9b, 0x99, 0x36, 0xdb, 0x4f, 0x5f, 0xe6, 0xf1, 0xe5, 0x6e, 0x17, 0x76, 0xbf, 0x00, - 0xf3, 0xcb, 0x52, 0x69, 0xd4, 0xc0, 0xe9, 0x21, 0x44, 0x3f, 0xe7, 0x7b, 0x72, 0x4e, 0x1c, 0x27, 0xd7, 0x6e, 0xde, - 0x7c, 0x2f, 0xc5, 0x08, 0x2c, 0xe0, 0xc4, 0x45, 0x3a, 0xd0, 0x52, 0xc1, 0x69, 0xcb, 0x78, 0x6f, 0xd3, 0x3b, 0x4a, - 0x85, 0x57, 0x0b, 0x4d, 0x42, 0x2a, 0x77, 0x2f, 0xb1, 0xa3, 0x06, 0x9c, 0x93, 0xba, 0x83, 0x80, 0x93, 0x9a, 0x6e, - 0xac, 0x55, 0x9c, 0x9a, 0x04, 0xef, 0x95, 0x1e, 0xba, 0x44, 0x3b, 0x71, 0xbb, 0x6d, 0x55, 0xb6, 0x50, 0x1f, 0x0f, - 0x72, 0x96, 0xa8, 0xe3, 0x01, 0x85, 0x2e, 0xea, 0x68, 0xc8, 0x97, 0xa4, 0xd0, 0x2b, 0x47, 0xab, 0x56, 0xf7, 0x25, - 0x03, 0xa5, 0x5a, 0x05, 0x79, 0x4d, 0xac, 0xbb, 0x56, 0xd6, 0x58, 0x5c, 0x39, 0x21, 0x85, 0x4d, 0xf8, 0xdc, 0x52, - 0x2c, 0xac, 0x60, 0x6f, 0x4c, 0x7d, 0xe1, 0x12, 0xa1, 0xed, 0x6e, 0x43, 0x4c, 0x32, 0x58, 0x37, 0xbb, 0xdd, 0xab, - 0x22, 0x5c, 0x64, 0x4b, 0x2a, 0x47, 0x59, 0x8a, 0x10, 0x62, 0xc6, 0x43, 0xd7, 0x76, 0xc1, 0x4c, 0x0c, 0x75, 0xed, - 0xf1, 0x92, 0x4c, 0xb1, 0x36, 0x49, 0x8e, 0xe2, 0x73, 0x59, 0xa8, 0xb5, 0x46, 0x08, 0x1e, 0xee, 0x7f, 0xa4, 0x10, - 0xc3, 0xcd, 0xac, 0xbb, 0x5f, 0xf7, 0x6e, 0x88, 0x7f, 0x40, 0x20, 0x81, 0x92, 0xbd, 0x2a, 0x46, 0xe7, 0x99, 0x48, - 0x71, 0xa7, 0xaa, 0xa8, 0xb8, 0x6a, 0x1d, 0x34, 0x5b, 0x6e, 0xef, 0xc5, 0x96, 0x28, 0x40, 0x5c, 0x63, 0xa1, 0x19, - 0xcf, 0xca, 0x59, 0x8a, 0x64, 0x14, 0x1b, 0x12, 0x95, 0x5e, 0x54, 0x74, 0x9f, 0xa7, 0x31, 0x3d, 0x74, 0x6b, 0x10, - 0x5c, 0x35, 0xf7, 0x36, 0xd2, 0x62, 0x49, 0x88, 0x9a, 0x00, 0x09, 0x1b, 0xd5, 0x9c, 0x5a, 0x97, 0xe2, 0x61, 0x56, - 0xf9, 0x4c, 0x1f, 0xc4, 0x97, 0x02, 0x78, 0x58, 0x6f, 0x7b, 0x5f, 0x09, 0x8f, 0xb5, 0xc1, 0xb7, 0xbb, 0xdd, 0xa5, - 0x58, 0x04, 0x81, 0xc7, 0x68, 0x7e, 0xa7, 0x24, 0xe6, 0xbd, 0x31, 0x85, 0x15, 0xef, 0xbb, 0xb4, 0x75, 0x93, 0x5a, - 0x6b, 0x81, 0xba, 0xc7, 0xf5, 0x01, 0xcf, 0x53, 0xe2, 0x68, 0x47, 0xe5, 0x54, 0x5a, 0x5d, 0x39, 0x76, 0x45, 0x60, - 0x60, 0xe8, 0x1f, 0x52, 0xb6, 0x05, 0x73, 0x3c, 0xb0, 0xb6, 0x41, 0x3f, 0x25, 0xa5, 0x85, 0x19, 0xa3, 0x31, 0x8b, - 0xdc, 0x54, 0xd1, 0x11, 0xd7, 0xd1, 0xdb, 0x79, 0xf4, 0xb7, 0xa7, 0x63, 0x5a, 0xc4, 0x22, 0x95, 0x57, 0xa0, 0x82, - 0x00, 0x65, 0x08, 0x1a, 0xfe, 0x6b, 0x6a, 0x00, 0x1a, 0x04, 0x37, 0x00, 0xff, 0xe8, 0x74, 0x1a, 0xb4, 0x35, 0xf9, - 0x98, 0xa4, 0xaa, 0xc8, 0x79, 0x1b, 0xca, 0x4c, 0x25, 0x87, 0xe4, 0x71, 0x09, 0x78, 0x8e, 0xd8, 0x2c, 0x65, 0x73, - 0xa1, 0x36, 0x9b, 0x7a, 0xad, 0xd8, 0x91, 0xdb, 0x46, 0xd1, 0x66, 0x2d, 0x6a, 0x3b, 0x89, 0xc5, 0x72, 0x7a, 0x6b, - 0x85, 0x81, 0x53, 0xd3, 0x9a, 0x9b, 0x3d, 0xe8, 0x34, 0x5b, 0x9f, 0xc9, 0x4d, 0x80, 0x38, 0xc0, 0x70, 0xdd, 0x2e, - 0x6e, 0x96, 0x84, 0xde, 0xb2, 0x5b, 0x2b, 0x56, 0xbd, 0xb1, 0x72, 0x11, 0x93, 0x76, 0x33, 0x98, 0xc0, 0x65, 0x9c, - 0x15, 0xf6, 0x85, 0x56, 0x37, 0x14, 0x1d, 0x6d, 0x93, 0xf6, 0xf3, 0x8e, 0x76, 0xc3, 0x05, 0xdf, 0x8a, 0x75, 0x9c, - 0x1b, 0xd2, 0x54, 0xa1, 0x47, 0x07, 0x7a, 0x3b, 0x04, 0x34, 0x67, 0x63, 0xba, 0xa2, 0x29, 0x5e, 0xa0, 0xe9, 0x06, - 0xcc, 0x52, 0x2e, 0xa0, 0xaf, 0xdd, 0x3e, 0xc9, 0x17, 0xaa, 0x27, 0xc2, 0x5b, 0xa2, 0xe0, 0xcb, 0x91, 0x82, 0x57, - 0x56, 0xce, 0x63, 0x33, 0x87, 0x80, 0xc7, 0xa2, 0x4a, 0xf4, 0x4e, 0x8a, 0x4b, 0x50, 0xa6, 0xc2, 0x11, 0x68, 0xaa, - 0x46, 0x2c, 0xe1, 0x00, 0xb7, 0x17, 0x4f, 0x03, 0x42, 0x41, 0xaa, 0xbb, 0xb1, 0x2b, 0xf2, 0x96, 0xcd, 0xb6, 0xb7, - 0x60, 0x16, 0x5b, 0x6d, 0xca, 0xd6, 0x57, 0x36, 0xd9, 0x7d, 0x5c, 0x13, 0x6c, 0xbb, 0xb7, 0x41, 0xc2, 0x5b, 0x7a, - 0x43, 0xb6, 0x37, 0xfd, 0x7e, 0x08, 0xfd, 0x21, 0x54, 0x77, 0xe8, 0xb6, 0xb3, 0x43, 0xb7, 0x3e, 0xf3, 0x6b, 0xf5, - 0x7c, 0xca, 0x1b, 0xe2, 0x03, 0x9a, 0x68, 0xd1, 0x75, 0x7c, 0x07, 0x9b, 0x3a, 0xaa, 0xa8, 0xaa, 0x3c, 0x4a, 0x28, - 0xa8, 0x80, 0x33, 0x5e, 0x9e, 0x72, 0x8c, 0x6d, 0xaa, 0x9f, 0xde, 0x69, 0x5e, 0x6d, 0x63, 0xd6, 0x66, 0xb9, 0x39, - 0x07, 0x8b, 0x80, 0x73, 0x1e, 0x5d, 0x69, 0x5a, 0x72, 0xe9, 0x31, 0xf5, 0x67, 0x38, 0x2a, 0xc1, 0x45, 0x9c, 0xe5, - 0x3c, 0x0d, 0xe8, 0x45, 0xb3, 0xff, 0xa1, 0xb6, 0x95, 0x5a, 0x35, 0xce, 0xdc, 0xeb, 0x90, 0x6c, 0xff, 0xc7, 0x06, - 0xea, 0x75, 0x88, 0x11, 0x51, 0xcd, 0x82, 0x7e, 0xcb, 0x20, 0x36, 0x66, 0x50, 0x6e, 0x92, 0x84, 0x97, 0x65, 0x60, - 0x94, 0x5a, 0x1b, 0xb6, 0x31, 0xe7, 0xd9, 0x23, 0x36, 0x7b, 0xd4, 0x63, 0xec, 0x96, 0xd0, 0x44, 0xeb, 0x84, 0x4c, - 0x8d, 0x91, 0xa7, 0x05, 0xd2, 0x1d, 0x8a, 0xb2, 0x8b, 0xf0, 0x2d, 0x0a, 0x59, 0xda, 0xfb, 0xdc, 0x9c, 0xc8, 0xea, - 0x1b, 0x6d, 0x74, 0x11, 0xa9, 0x44, 0x90, 0x8d, 0xdf, 0x20, 0x60, 0x2f, 0x34, 0x3b, 0x20, 0xdb, 0x15, 0x3b, 0xa5, - 0x67, 0xd6, 0x04, 0x06, 0x5e, 0xbf, 0x55, 0x89, 0x66, 0x94, 0x15, 0xd1, 0x55, 0x46, 0x2e, 0x77, 0x21, 0x89, 0xce, - 0x42, 0xe2, 0xe7, 0x86, 0xa5, 0x75, 0x1d, 0xa2, 0x98, 0xd9, 0x6c, 0x78, 0xd5, 0xdd, 0x47, 0x8d, 0x6d, 0x65, 0x7c, - 0xaa, 0x6f, 0x6d, 0x1a, 0x99, 0x42, 0x5f, 0x87, 0x93, 0x7e, 0x1f, 0xfe, 0x6a, 0xfa, 0x81, 0xb7, 0x14, 0xfc, 0xc5, - 0x1e, 0x91, 0x3a, 0x61, 0x01, 0xc0, 0x11, 0xe6, 0xbc, 0x6a, 0x4e, 0xe0, 0x23, 0x36, 0xdb, 0x3e, 0x0a, 0x4f, 0x1b, - 0x33, 0x77, 0x17, 0xe2, 0xa5, 0x2a, 0xe9, 0x79, 0xf3, 0x64, 0x06, 0x62, 0x1d, 0x9a, 0xfd, 0x7a, 0xcb, 0xac, 0x3e, - 0x01, 0x88, 0xd4, 0xad, 0x75, 0x28, 0xc5, 0x8f, 0x4d, 0x97, 0xc9, 0x36, 0x65, 0x6d, 0x26, 0x4a, 0xa9, 0x48, 0x9a, - 0x8b, 0x00, 0xfa, 0x0d, 0xc3, 0x51, 0x03, 0xbc, 0xb7, 0x1e, 0x7b, 0x33, 0x34, 0xde, 0x98, 0x1a, 0x7a, 0xb6, 0xd5, - 0xcb, 0xdb, 0x51, 0x08, 0x33, 0x16, 0xd1, 0xad, 0x3b, 0x16, 0xc3, 0x53, 0xfa, 0x16, 0x2a, 0x7c, 0x1d, 0x62, 0x34, - 0x5d, 0x52, 0xd7, 0xd3, 0x8d, 0xda, 0x4a, 0x37, 0x84, 0xe6, 0x18, 0xc5, 0xc7, 0x6b, 0xdb, 0x1d, 0x35, 0x42, 0x7b, - 0x42, 0x79, 0x78, 0x4b, 0x2b, 0x7a, 0x63, 0x59, 0x04, 0x27, 0x3f, 0xf6, 0xf2, 0x13, 0x7a, 0xee, 0x09, 0x4c, 0x8a, - 0xb6, 0x06, 0xf0, 0x07, 0xd4, 0x0f, 0x67, 0xf5, 0xd4, 0x4a, 0x39, 0x3c, 0x85, 0x2f, 0xd9, 0x82, 0x5c, 0x41, 0x2f, - 0xd6, 0x98, 0xcd, 0x62, 0xd0, 0x41, 0xed, 0xed, 0x0e, 0x6f, 0x52, 0xca, 0x10, 0xad, 0xef, 0x1c, 0xc4, 0xd3, 0x3f, - 0x40, 0xd3, 0x07, 0x69, 0x61, 0x4a, 0x37, 0x28, 0xe0, 0x01, 0x7d, 0x53, 0xbf, 0x9f, 0xe3, 0x73, 0xed, 0x59, 0x62, - 0x61, 0x8f, 0x57, 0x84, 0xae, 0xbc, 0xb8, 0x51, 0x20, 0x6d, 0x76, 0xac, 0x02, 0xb0, 0x22, 0x09, 0x34, 0x22, 0x01, - 0x4b, 0x1d, 0x4f, 0x5c, 0xb6, 0x45, 0x03, 0x92, 0xa8, 0xa4, 0x90, 0x25, 0x92, 0xc0, 0x0f, 0x23, 0x08, 0x51, 0x14, - 0x83, 0xb8, 0x57, 0x2f, 0xaf, 0xb8, 0xa6, 0x06, 0x9c, 0x28, 0x82, 0x09, 0xd6, 0xe9, 0x14, 0x88, 0xad, 0xd8, 0xac, - 0xc1, 0xf3, 0xd2, 0x71, 0xe2, 0xc8, 0x12, 0xa0, 0x41, 0x9a, 0x2f, 0x9d, 0x76, 0xcb, 0xdb, 0x13, 0x2d, 0x55, 0x6c, - 0xe1, 0xbd, 0x58, 0x5a, 0xee, 0xb1, 0xf2, 0xb7, 0x03, 0xed, 0x85, 0xd5, 0x9e, 0x88, 0x1a, 0xac, 0xec, 0xda, 0x76, - 0x6d, 0x28, 0x0d, 0xd5, 0xbd, 0x72, 0x4c, 0x40, 0x45, 0xd7, 0x71, 0xb5, 0x8a, 0xb2, 0x11, 0xfc, 0xd9, 0xed, 0x82, - 0xc3, 0x00, 0x2c, 0x20, 0x7f, 0x79, 0xff, 0x53, 0x84, 0xe1, 0x99, 0x7e, 0x79, 0xff, 0xd3, 0x6e, 0xf7, 0x74, 0x3c, - 0x36, 0x5c, 0x81, 0x53, 0xeb, 0x00, 0x7f, 0x60, 0xd8, 0x06, 0xbb, 0x64, 0x77, 0xbb, 0xa7, 0xc0, 0x41, 0x28, 0xb6, - 0xc1, 0xec, 0x62, 0xe5, 0xc8, 0xa5, 0x58, 0x0d, 0xbd, 0x23, 0x01, 0xab, 0x6e, 0x8f, 0xa5, 0xd8, 0xa7, 0x3e, 0x2a, - 0x04, 0xa3, 0x5e, 0xf4, 0x2f, 0x3a, 0x05, 0x96, 0x14, 0x4c, 0x57, 0x83, 0x55, 0x55, 0xad, 0xcb, 0xe8, 0xf0, 0x30, - 0x5e, 0x67, 0xa3, 0x32, 0x83, 0x6d, 0x5e, 0x5e, 0x5f, 0x02, 0xa0, 0x42, 0x40, 0x1b, 0xef, 0x36, 0x22, 0x33, 0x2f, - 0x96, 0x74, 0x95, 0xe1, 0x9a, 0x04, 0xb3, 0x83, 0x9c, 0x5b, 0xdd, 0xe4, 0x94, 0xd8, 0x07, 0xb0, 0x39, 0xdc, 0xed, - 0x1a, 0xfc, 0xc2, 0x6c, 0xf4, 0x74, 0xbe, 0xca, 0xb4, 0x41, 0x27, 0x37, 0xfb, 0x9f, 0x44, 0x5e, 0x1a, 0x2a, 0x3e, - 0xc9, 0xf4, 0x45, 0x06, 0x7c, 0x1e, 0x7b, 0x23, 0x42, 0x9f, 0xe5, 0x6a, 0xb4, 0x06, 0xd8, 0xd8, 0xec, 0xe2, 0x6e, - 0x94, 0x72, 0x88, 0x48, 0x11, 0x58, 0x75, 0xcd, 0x2a, 0x23, 0xbe, 0x4d, 0xc5, 0x5d, 0x4b, 0x15, 0xf6, 0x46, 0x78, - 0xce, 0x2a, 0xdc, 0x38, 0xca, 0xf4, 0x26, 0x51, 0xf8, 0x02, 0x85, 0xa8, 0x1c, 0x8d, 0xe9, 0x9c, 0x40, 0x2a, 0xf3, - 0x98, 0x50, 0xcc, 0xe1, 0xde, 0xfd, 0x92, 0x3a, 0x73, 0x19, 0x5f, 0xb8, 0xf7, 0xc2, 0x97, 0x99, 0xdc, 0x4a, 0x00, - 0x45, 0x52, 0xb5, 0xff, 0xfc, 0x09, 0xa9, 0xf1, 0x3f, 0x53, 0xad, 0x01, 0xe8, 0xfd, 0x0c, 0x35, 0x39, 0x82, 0x80, - 0xad, 0x98, 0xfa, 0x68, 0xfa, 0x56, 0x32, 0xff, 0x01, 0x75, 0x3b, 0x82, 0x6d, 0x55, 0xfc, 0x9c, 0xa8, 0xa2, 0x05, - 0x4f, 0x37, 0x22, 0x8d, 0x45, 0x72, 0x17, 0xf1, 0x7a, 0x8a, 0x25, 0x31, 0x1b, 0x21, 0xeb, 0xe7, 0x66, 0x17, 0x7e, - 0x2a, 0x1a, 0x26, 0xe0, 0xb4, 0xf4, 0xb7, 0x95, 0xb7, 0x99, 0x2c, 0xe3, 0x8c, 0x4c, 0xb9, 0x42, 0xec, 0xb6, 0xfa, - 0x1e, 0x73, 0x82, 0x3f, 0x39, 0x7a, 0x42, 0xe8, 0xad, 0x9c, 0x96, 0x08, 0x4a, 0x27, 0x52, 0xeb, 0xaa, 0x89, 0xfd, - 0x9a, 0x42, 0x14, 0x07, 0xc1, 0x20, 0x74, 0xa7, 0x69, 0x9f, 0xe2, 0xfb, 0x6c, 0xd9, 0x6f, 0x4c, 0xd9, 0x92, 0x6c, - 0x05, 0x74, 0x4c, 0x3a, 0x6f, 0x4f, 0x6f, 0xcf, 0xce, 0xbd, 0xdf, 0xa0, 0x09, 0x07, 0xd5, 0x0d, 0xb4, 0xab, 0x20, - 0xd3, 0x18, 0xc5, 0x66, 0x31, 0xd6, 0x6e, 0x4d, 0x44, 0x10, 0x74, 0xba, 0x9c, 0x87, 0xed, 0x76, 0x42, 0x7c, 0x09, - 0x24, 0x50, 0xe0, 0xca, 0x45, 0x39, 0x09, 0x89, 0xba, 0x90, 0xe9, 0xc9, 0xba, 0x96, 0x2c, 0xd0, 0x6b, 0xec, 0x28, - 0xa0, 0x27, 0xdc, 0x3e, 0x05, 0xf4, 0x7d, 0xc1, 0x4e, 0xf8, 0x20, 0x18, 0x62, 0x7c, 0xd5, 0x80, 0xde, 0x48, 0xf5, - 0x08, 0x1e, 0xc2, 0xc0, 0x72, 0xd1, 0x97, 0x05, 0x43, 0x58, 0xa1, 0x3f, 0x53, 0x36, 0xf9, 0xfa, 0x1b, 0x37, 0xbf, - 0xe7, 0x5a, 0xcc, 0x0e, 0x42, 0x71, 0x7b, 0x3d, 0x01, 0xe2, 0x57, 0xf1, 0x2b, 0xb0, 0xae, 0xd6, 0x12, 0x6f, 0x37, - 0x3d, 0x7f, 0x08, 0x5f, 0x8e, 0x6e, 0x3f, 0x29, 0xcd, 0x27, 0x10, 0xa4, 0xc6, 0x49, 0xca, 0xdd, 0x77, 0x1f, 0xa5, - 0xab, 0x08, 0x46, 0x0b, 0x10, 0xeb, 0xee, 0xad, 0xe4, 0xac, 0x29, 0xfc, 0xc7, 0x3a, 0xdf, 0x63, 0xec, 0x10, 0x79, - 0x8a, 0xd3, 0xdf, 0x00, 0xc3, 0xbe, 0xf3, 0x6f, 0x65, 0xd6, 0x90, 0xe8, 0x5c, 0x7d, 0x04, 0xf4, 0x7f, 0xac, 0xc7, - 0xef, 0x04, 0x25, 0x7d, 0x49, 0x9c, 0x23, 0x5c, 0x11, 0x2f, 0xd1, 0x54, 0xaf, 0x37, 0xae, 0xe9, 0x5f, 0x85, 0x79, - 0xa1, 0x15, 0x1c, 0xf6, 0xad, 0x51, 0x78, 0xe0, 0x99, 0xf7, 0xab, 0x68, 0x08, 0xba, 0x7f, 0xc3, 0xbd, 0xf1, 0xab, - 0x60, 0x19, 0xde, 0x94, 0xb3, 0xcc, 0xdc, 0xe1, 0x6e, 0x32, 0x91, 0xca, 0x1b, 0xc6, 0x82, 0x8d, 0x50, 0xe6, 0xab, - 0x69, 0x30, 0xdf, 0xd6, 0x91, 0x4a, 0x76, 0xdf, 0xbf, 0x69, 0x9c, 0xb0, 0xd9, 0x20, 0x38, 0xad, 0x64, 0x11, 0x5f, - 0xf2, 0x60, 0xaa, 0x55, 0x14, 0x59, 0xd6, 0xef, 0x67, 0x80, 0x0c, 0xe3, 0xb4, 0x77, 0xf0, 0x64, 0xa9, 0x99, 0x09, - 0x71, 0x6d, 0x75, 0x16, 0xf0, 0xd6, 0x8c, 0xe6, 0x49, 0x05, 0xbb, 0xcc, 0x57, 0x52, 0xfc, 0xd1, 0x92, 0x64, 0x63, - 0xfd, 0x0d, 0x19, 0xb6, 0x95, 0xcf, 0x9c, 0x03, 0xc6, 0xcc, 0x8d, 0x54, 0x41, 0xee, 0x7a, 0xc0, 0x08, 0x21, 0x11, - 0x10, 0xce, 0x62, 0xe2, 0x4e, 0x98, 0xf0, 0x8f, 0x2e, 0x30, 0x4e, 0x8c, 0x81, 0x71, 0x3e, 0xca, 0x90, 0xd3, 0x13, - 0x3e, 0x48, 0x1a, 0xb3, 0xf5, 0x87, 0x2a, 0x91, 0x5e, 0x4b, 0x42, 0xcf, 0xe0, 0xf7, 0xb8, 0xc5, 0x03, 0x35, 0x82, - 0x53, 0xba, 0x9b, 0xd3, 0xe1, 0xcb, 0x82, 0x0c, 0xff, 0x04, 0xef, 0xae, 0xd8, 0x5e, 0x96, 0x13, 0x58, 0xdc, 0xb1, - 0x57, 0x3c, 0xcd, 0x55, 0x8b, 0x13, 0xe2, 0x11, 0x8b, 0xdc, 0x27, 0x16, 0x30, 0xa2, 0x86, 0xd1, 0xf8, 0xf1, 0xf4, - 0xed, 0x1b, 0x8d, 0xd9, 0x94, 0xfb, 0x1f, 0xc0, 0x88, 0x6a, 0x69, 0xbb, 0x1d, 0xf0, 0xd5, 0x08, 0x0d, 0xb6, 0x53, - 0x37, 0xd8, 0xfd, 0xbe, 0x49, 0x9b, 0x95, 0x5e, 0x36, 0x27, 0x06, 0xdd, 0x53, 0xda, 0xac, 0x94, 0x41, 0x6d, 0x57, - 0xe1, 0x68, 0x3e, 0x6b, 0xc4, 0xaa, 0xde, 0x87, 0xe1, 0x8a, 0xc6, 0x56, 0x56, 0x6e, 0x77, 0x13, 0x8e, 0x6c, 0x02, - 0x5c, 0x9f, 0x82, 0xb2, 0x6a, 0xce, 0x41, 0x0b, 0x3a, 0x13, 0x38, 0xa2, 0xdd, 0x2e, 0x84, 0x08, 0x1c, 0xc5, 0x70, - 0x32, 0x0f, 0x8b, 0xe1, 0x50, 0x0d, 0x7c, 0x41, 0x48, 0xf4, 0x57, 0xb1, 0xc8, 0x96, 0x0a, 0xb1, 0xc7, 0xdf, 0x49, - 0xbf, 0x16, 0x8a, 0x53, 0xee, 0xfd, 0x2a, 0xc8, 0xf6, 0xb7, 0x14, 0x63, 0x0e, 0x3a, 0xcd, 0x66, 0x06, 0x12, 0xd6, - 0x93, 0x8a, 0xa8, 0x75, 0x64, 0x67, 0x03, 0x54, 0xb1, 0x68, 0x0a, 0x0b, 0xea, 0x16, 0x4f, 0xac, 0x67, 0xf4, 0x1e, - 0x54, 0x82, 0xa8, 0x16, 0xec, 0xc6, 0x70, 0xad, 0xfd, 0x25, 0x42, 0x49, 0x39, 0x69, 0x32, 0x33, 0x56, 0x34, 0x58, - 0x80, 0x90, 0x34, 0x2e, 0xab, 0xd7, 0x32, 0xcd, 0x2e, 0x32, 0x40, 0x4c, 0x70, 0xfe, 0x73, 0xb2, 0xf1, 0xe6, 0x99, - 0x9a, 0x97, 0xae, 0xc4, 0xb9, 0x85, 0xf9, 0xe8, 0x7a, 0x4b, 0x0b, 0x12, 0x15, 0x40, 0xa3, 0x7c, 0x2d, 0xcf, 0xdf, - 0xf7, 0xac, 0x42, 0xf6, 0x3f, 0x9c, 0x2a, 0xdb, 0x21, 0x3e, 0x63, 0x15, 0xf1, 0x4e, 0xeb, 0x4a, 0x89, 0x34, 0x3a, - 0xda, 0x06, 0xc4, 0xb0, 0x65, 0xdf, 0xa2, 0x86, 0x0f, 0xc2, 0x0c, 0x3a, 0xc9, 0x0f, 0x7a, 0x46, 0xc7, 0xd6, 0x40, - 0xd2, 0xd7, 0x22, 0xf8, 0x1a, 0x1d, 0xe9, 0x44, 0x99, 0x46, 0x62, 0x0a, 0x89, 0x7e, 0xbd, 0xd0, 0x1a, 0xcb, 0x28, - 0xfb, 0x8a, 0xfc, 0xef, 0x75, 0xf7, 0x7e, 0x15, 0xbb, 0x1d, 0x4c, 0xb2, 0xe7, 0x71, 0x05, 0x9b, 0x1a, 0xb5, 0x42, - 0x38, 0x3b, 0x27, 0x15, 0x6a, 0xc7, 0x7a, 0x61, 0x09, 0xe4, 0x01, 0x6c, 0x45, 0x1a, 0x94, 0x41, 0xb2, 0xbf, 0x8a, - 0x85, 0x58, 0x3a, 0x51, 0x8e, 0x54, 0x78, 0x5f, 0x72, 0x94, 0x72, 0xb8, 0x8a, 0x85, 0x05, 0x43, 0x7e, 0x75, 0x74, - 0x51, 0xc8, 0x2b, 0x90, 0x94, 0x18, 0x86, 0xca, 0xf2, 0xba, 0xb8, 0x6a, 0x4b, 0x42, 0x7b, 0x67, 0x00, 0xc2, 0x52, - 0x80, 0xe0, 0xa5, 0x51, 0x43, 0xcc, 0xb6, 0x6a, 0x77, 0x45, 0xf7, 0x92, 0x03, 0xea, 0x74, 0xd7, 0x6e, 0xbd, 0x29, - 0xdb, 0x6c, 0x2b, 0x2e, 0xfc, 0x03, 0x4a, 0x3f, 0xe1, 0x83, 0xc2, 0xa7, 0x12, 0xb8, 0xf1, 0xd5, 0x26, 0xcb, 0x2e, - 0xee, 0x70, 0xe9, 0x57, 0x8d, 0xf1, 0xeb, 0xf7, 0x7b, 0x6a, 0x21, 0x34, 0x52, 0x81, 0xf9, 0xf6, 0x99, 0xa9, 0xca, - 0x68, 0x4a, 0xed, 0x25, 0xb8, 0x72, 0xf6, 0x23, 0xa8, 0x88, 0xeb, 0x8a, 0x4c, 0xa6, 0x06, 0xe8, 0xc0, 0xcb, 0x0a, - 0xb7, 0xb2, 0x00, 0x8f, 0x9d, 0x80, 0xec, 0x76, 0x3c, 0x0c, 0xf4, 0xa1, 0x13, 0xf8, 0x5b, 0xf2, 0x14, 0x99, 0x35, - 0xfb, 0xf8, 0xb3, 0x16, 0xfc, 0x63, 0x0b, 0x7e, 0x42, 0x71, 0xa7, 0x95, 0xf9, 0xb7, 0xd2, 0xba, 0xc5, 0xfd, 0x7b, - 0x99, 0x26, 0x14, 0x95, 0x09, 0xb5, 0x5f, 0xe9, 0xbf, 0x4c, 0xb0, 0x24, 0x95, 0xfd, 0x83, 0x84, 0x0f, 0xe6, 0x8d, - 0x27, 0xd6, 0x78, 0x32, 0x9c, 0x6e, 0xa5, 0x61, 0x08, 0x50, 0xe8, 0xe7, 0x65, 0xae, 0xa8, 0x7e, 0xfe, 0x79, 0xc3, - 0x37, 0xbc, 0xd9, 0x62, 0x9b, 0xf4, 0x40, 0x83, 0xbd, 0x3c, 0x9a, 0x52, 0x38, 0x89, 0x3a, 0x37, 0x12, 0x75, 0x51, - 0xb3, 0x0c, 0xd5, 0x09, 0x5e, 0xcd, 0x53, 0x3d, 0xec, 0xcd, 0x44, 0xb4, 0x56, 0x52, 0x96, 0x18, 0xb0, 0xd6, 0x91, - 0x87, 0xe4, 0x6e, 0xad, 0xe3, 0x4e, 0x43, 0x5d, 0x9a, 0x42, 0x09, 0xb0, 0xc2, 0x05, 0x38, 0x82, 0x7e, 0x2a, 0x42, - 0x0e, 0xd7, 0x54, 0xa5, 0x5f, 0xd0, 0x94, 0x3c, 0xf1, 0x14, 0xb5, 0x5a, 0x91, 0x6e, 0x3f, 0xca, 0xb1, 0x1b, 0xbe, - 0x71, 0x42, 0x4e, 0x8c, 0xd0, 0xdf, 0x1d, 0x4b, 0x39, 0x43, 0x8b, 0x07, 0x75, 0x82, 0xf5, 0xf2, 0x96, 0x02, 0xc5, - 0x1c, 0x5d, 0x56, 0x5d, 0xf3, 0x0a, 0x6d, 0x5f, 0x56, 0xfd, 0x7e, 0x6e, 0xeb, 0x49, 0xd9, 0x6c, 0xbb, 0x32, 0xfb, - 0x10, 0x15, 0x53, 0xb8, 0xeb, 0x13, 0xcd, 0x5f, 0x85, 0xfa, 0xaa, 0x2d, 0x73, 0x3e, 0xe2, 0x88, 0x8b, 0x91, 0x93, - 0xfa, 0x67, 0x35, 0xf5, 0x4a, 0xdc, 0xaf, 0x2a, 0xf9, 0x4e, 0x18, 0x2b, 0x46, 0x07, 0xd4, 0x9f, 0x2a, 0x95, 0xf7, - 0x8b, 0x02, 0xe0, 0x9e, 0x04, 0xfb, 0x0b, 0xec, 0x2b, 0x34, 0xc2, 0x6f, 0x4b, 0xc0, 0xbf, 0x55, 0xdc, 0x80, 0x55, - 0x60, 0x80, 0xd1, 0x64, 0x7b, 0x4e, 0x13, 0x38, 0xe0, 0x84, 0x56, 0x51, 0x50, 0x61, 0x86, 0x86, 0xda, 0xc2, 0xe8, - 0x29, 0xca, 0xb8, 0x55, 0x66, 0xef, 0xc6, 0xd8, 0x69, 0x81, 0xd7, 0xf0, 0xe7, 0xf3, 0x42, 0x0f, 0x1b, 0x75, 0x90, - 0x1e, 0x9d, 0xc4, 0xf4, 0xc7, 0x2d, 0x9c, 0xdc, 0x2c, 0x9c, 0x55, 0xcd, 0x12, 0xe8, 0x0e, 0x5c, 0x10, 0xe3, 0x7e, - 0x3f, 0x87, 0x23, 0xd3, 0x8c, 0x7c, 0xc1, 0x72, 0x1a, 0xb3, 0x15, 0xd5, 0x9e, 0x76, 0x97, 0x55, 0x98, 0xd3, 0x95, - 0x95, 0xf1, 0xa6, 0x0c, 0x54, 0x46, 0xbb, 0x5d, 0x08, 0x7f, 0xba, 0xad, 0x5d, 0xd2, 0xc5, 0x0a, 0x32, 0xc0, 0x1f, - 0x90, 0x88, 0x22, 0xf6, 0xf5, 0xbf, 0xd5, 0x38, 0xa5, 0x27, 0x4a, 0x6b, 0x96, 0xd0, 0x0d, 0xd3, 0xf5, 0xd3, 0x0b, - 0xb6, 0x69, 0x2c, 0x85, 0xdd, 0x2e, 0x6c, 0x26, 0x30, 0xcd, 0xb9, 0x92, 0xe9, 0x05, 0xea, 0xa4, 0x80, 0x8a, 0x85, - 0x17, 0xb8, 0xfc, 0x52, 0x42, 0xa1, 0xb9, 0x8b, 0xd5, 0xd2, 0x28, 0x31, 0xa1, 0x55, 0xf2, 0xf3, 0x87, 0xca, 0x7c, - 0x6d, 0x3c, 0xe2, 0xfe, 0x95, 0x86, 0x89, 0x29, 0x12, 0x15, 0xa2, 0xf3, 0x5f, 0x41, 0x96, 0x23, 0x00, 0xc7, 0xf2, - 0x54, 0xd6, 0xf4, 0xc7, 0x14, 0xe2, 0xa0, 0x43, 0x83, 0xde, 0x15, 0xf2, 0x2a, 0x2b, 0x79, 0x88, 0xf7, 0x04, 0x4f, - 0x33, 0x7a, 0xbf, 0xc1, 0x87, 0xb6, 0xf6, 0xe8, 0x09, 0xb2, 0xf5, 0x94, 0xfb, 0xf5, 0x77, 0x22, 0x5c, 0x40, 0xb4, - 0xca, 0x25, 0xd5, 0xea, 0x6a, 0x07, 0x40, 0xe5, 0xd9, 0x5e, 0x3d, 0x82, 0xd3, 0x4d, 0x5f, 0xdf, 0xaa, 0xd0, 0x99, - 0x03, 0x48, 0x7b, 0x48, 0xd6, 0x35, 0xd7, 0x3b, 0xc0, 0x1d, 0x89, 0xd5, 0x06, 0x68, 0xac, 0xdb, 0x9a, 0x9d, 0xf6, - 0x28, 0x1e, 0x13, 0x99, 0x19, 0x8b, 0x14, 0x63, 0xee, 0xd6, 0x69, 0x51, 0xb4, 0x45, 0x33, 0x84, 0xfd, 0xbb, 0x0e, - 0xdf, 0xb4, 0x22, 0xac, 0xdf, 0x6f, 0xfb, 0x02, 0xa3, 0x61, 0xcc, 0xb5, 0x7b, 0x9e, 0xa1, 0x1b, 0x36, 0xd8, 0x46, - 0xce, 0x43, 0xe4, 0xc3, 0x4c, 0x1d, 0x88, 0xb2, 0xb6, 0x06, 0x6c, 0x8f, 0xb8, 0xde, 0xb4, 0x8a, 0x9f, 0x57, 0x31, - 0x67, 0x7b, 0xd6, 0x38, 0xa5, 0xf5, 0x35, 0xae, 0x39, 0xae, 0x0a, 0x11, 0xb5, 0xf5, 0x8c, 0x87, 0x61, 0xe7, 0x4b, - 0xdc, 0x99, 0x15, 0x06, 0x2f, 0xc2, 0x52, 0xc9, 0x5e, 0xe5, 0xfa, 0x73, 0xd8, 0xe2, 0x20, 0x95, 0x2f, 0xbd, 0xfe, - 0xfe, 0xcb, 0x17, 0x5f, 0xa0, 0x9b, 0x9a, 0xf3, 0x23, 0x08, 0x32, 0x81, 0x0e, 0x59, 0x4a, 0xf5, 0xf8, 0x5d, 0x01, - 0xd4, 0x1e, 0xe6, 0xe1, 0xbb, 0x82, 0x89, 0xf8, 0x3a, 0xbb, 0x8c, 0x2b, 0x59, 0x8c, 0xae, 0xb9, 0x48, 0x65, 0x61, - 0xa5, 0xc6, 0xc1, 0xc9, 0x7a, 0x9d, 0xf3, 0x00, 0x4c, 0xe5, 0x2d, 0xa3, 0x6c, 0x2b, 0xcb, 0xf4, 0xe0, 0x6a, 0x79, - 0x7a, 0xa5, 0x45, 0xe7, 0xe5, 0xf5, 0x65, 0x10, 0xe1, 0xaf, 0x73, 0xf3, 0xe3, 0x2a, 0x2e, 0x3f, 0x06, 0x91, 0xb5, - 0xa9, 0x33, 0x3f, 0x50, 0x2a, 0x0f, 0xfe, 0x53, 0x20, 0xd3, 0xfd, 0xae, 0x00, 0xcb, 0x6c, 0x5b, 0xf1, 0x71, 0x8c, - 0xb5, 0x0e, 0x27, 0x64, 0xae, 0x4a, 0xf4, 0xde, 0x25, 0x9b, 0x02, 0xac, 0xfd, 0x14, 0x96, 0xb1, 0xca, 0x35, 0xc7, - 0xca, 0x54, 0x45, 0x66, 0x56, 0x36, 0xec, 0x30, 0xb4, 0x4e, 0x34, 0x0b, 0xf4, 0x16, 0xd0, 0x0f, 0xe4, 0xf0, 0x92, - 0x96, 0x1b, 0xe6, 0xf9, 0xd8, 0x34, 0x5e, 0x3f, 0x3a, 0xbc, 0x74, 0x0b, 0xf6, 0xd6, 0xde, 0xc9, 0x51, 0x98, 0x08, - 0x9e, 0xb5, 0x66, 0x7c, 0x91, 0x67, 0x05, 0xac, 0x9c, 0xc9, 0x78, 0x4c, 0xbd, 0xa5, 0xd5, 0xba, 0x39, 0x3a, 0x24, - 0xd7, 0xec, 0x71, 0xf5, 0x98, 0x93, 0x43, 0xde, 0x32, 0xb5, 0x6d, 0x5b, 0xc7, 0x79, 0x9a, 0x7c, 0x65, 0xba, 0x2f, - 0x36, 0x36, 0x22, 0xba, 0x72, 0xee, 0x73, 0x5e, 0xc1, 0xad, 0x6f, 0x4a, 0x43, 0xaf, 0x25, 0x00, 0xd1, 0x69, 0x03, - 0xfe, 0x82, 0x95, 0x9b, 0x51, 0xc5, 0xcb, 0x0a, 0x24, 0x2c, 0x28, 0xc2, 0x9b, 0x62, 0x6f, 0x0a, 0x77, 0xe3, 0xf4, - 0x1c, 0x76, 0xe0, 0x62, 0x8a, 0xee, 0x38, 0x31, 0x99, 0x97, 0x46, 0x2b, 0x1a, 0xe9, 0x5f, 0xae, 0x2f, 0xb1, 0xee, - 0x8b, 0x56, 0xe6, 0xd9, 0x9c, 0x0a, 0x8b, 0xdd, 0x55, 0x2e, 0x9d, 0xa8, 0xdf, 0x32, 0xe1, 0xca, 0x95, 0x20, 0x20, - 0xd3, 0x82, 0xf5, 0x0a, 0xb3, 0x8b, 0xe4, 0x06, 0x08, 0x19, 0x18, 0xbe, 0x06, 0x1b, 0x51, 0x72, 0x63, 0x05, 0xeb, - 0xdd, 0xf3, 0x75, 0x82, 0x90, 0x82, 0x07, 0x6e, 0x82, 0x7e, 0x68, 0xdd, 0xbc, 0x1d, 0x25, 0xca, 0x20, 0x1e, 0xb7, - 0x76, 0xca, 0x41, 0x02, 0x01, 0xb8, 0xa7, 0x2a, 0x04, 0x87, 0x02, 0x59, 0x07, 0x57, 0x33, 0x8e, 0xe0, 0xea, 0xca, - 0x99, 0x8b, 0x1b, 0x80, 0x75, 0xe5, 0xcf, 0x65, 0x83, 0x0b, 0xeb, 0x11, 0x55, 0xe6, 0x8c, 0x53, 0x0c, 0x62, 0x64, - 0x09, 0xfa, 0xda, 0x52, 0xda, 0x4b, 0xd0, 0x34, 0x5e, 0xb1, 0xb5, 0xf2, 0x01, 0xa0, 0xe7, 0x6c, 0xad, 0x8c, 0xfd, - 0xf1, 0xeb, 0x33, 0xb6, 0xd6, 0xd2, 0xe0, 0xe9, 0xd5, 0xfc, 0x7c, 0x7e, 0x36, 0x60, 0x47, 0x51, 0xa8, 0x0d, 0x18, - 0x02, 0x87, 0xc4, 0x1f, 0x0c, 0x42, 0x8d, 0x77, 0x32, 0x50, 0x01, 0xb1, 0x88, 0xc7, 0x63, 0x23, 0x6e, 0x56, 0x38, - 0x1e, 0x62, 0xf0, 0xab, 0xe6, 0x0b, 0x12, 0x10, 0x6a, 0x4a, 0x43, 0x97, 0xc7, 0x70, 0x38, 0x39, 0x98, 0x40, 0x2a, - 0x66, 0x66, 0xaa, 0x30, 0x36, 0x26, 0x11, 0xc4, 0x3b, 0xed, 0xac, 0x17, 0xca, 0xed, 0xae, 0xd1, 0x40, 0xae, 0x0c, - 0x3e, 0xab, 0xe2, 0xc9, 0xc1, 0xb0, 0xab, 0x62, 0x1c, 0x85, 0x6b, 0xa3, 0x7c, 0x3b, 0x3b, 0x06, 0xf0, 0xda, 0xb3, - 0xa1, 0x2f, 0x97, 0x38, 0x3b, 0x7c, 0x42, 0x1e, 0x3f, 0x21, 0xf4, 0x8c, 0x9d, 0x7d, 0xf1, 0x84, 0x9e, 0x29, 0x72, - 0x72, 0x30, 0x89, 0xae, 0x99, 0xc5, 0x7c, 0x39, 0x52, 0x4d, 0xa0, 0x97, 0xa3, 0x8d, 0x50, 0x0b, 0x4c, 0x3b, 0x34, - 0x85, 0xdf, 0x8e, 0x0f, 0x82, 0xc1, 0x75, 0xbb, 0xe9, 0xd7, 0xed, 0xb6, 0x7a, 0x5e, 0x5d, 0x07, 0x47, 0xd1, 0x7e, - 0x31, 0x93, 0x6f, 0xc6, 0x07, 0x6e, 0x0e, 0xb0, 0xbe, 0x87, 0xc7, 0xc4, 0x34, 0x69, 0x6f, 0x54, 0xfc, 0x9a, 0xbe, - 0xc2, 0x3e, 0x34, 0x8b, 0xec, 0xe8, 0xc3, 0xf0, 0xdf, 0xea, 0x44, 0x7d, 0xf6, 0xc5, 0x11, 0x90, 0x23, 0x90, 0x81, - 0x62, 0x89, 0x60, 0x86, 0x03, 0x4d, 0x01, 0x05, 0x99, 0x1e, 0x77, 0xaa, 0x87, 0x5f, 0x8d, 0x9a, 0x9a, 0x91, 0x6b, - 0x98, 0x1a, 0x6c, 0x0b, 0x7e, 0xa0, 0xba, 0xa1, 0xbf, 0xd1, 0xe8, 0x46, 0xda, 0xc9, 0xcc, 0xbc, 0xa4, 0x36, 0xae, - 0xdb, 0x35, 0x04, 0x30, 0x76, 0xf0, 0x82, 0x92, 0x7d, 0x79, 0x7c, 0x79, 0x80, 0xab, 0x08, 0x50, 0xb2, 0x58, 0xf0, - 0xe5, 0xe0, 0x52, 0x6f, 0xee, 0x83, 0x80, 0x0c, 0xbe, 0x0c, 0x66, 0x5f, 0x0e, 0xe4, 0x20, 0x38, 0x3e, 0xbc, 0x9c, - 0x05, 0xce, 0xb8, 0x1f, 0x42, 0x3c, 0xaa, 0x8a, 0x62, 0x26, 0x4c, 0x15, 0x89, 0xad, 0x3d, 0xb7, 0xf5, 0x2a, 0xe3, - 0x33, 0x9a, 0x4e, 0x2d, 0xf2, 0x77, 0x98, 0xb2, 0xd8, 0xfc, 0x0e, 0x26, 0xfc, 0x2a, 0x88, 0x5c, 0x10, 0xd4, 0x79, - 0x1e, 0xc5, 0x74, 0xc5, 0x6e, 0x45, 0x98, 0xd2, 0xe4, 0x30, 0x27, 0x24, 0x0a, 0x57, 0x0a, 0x3c, 0x4f, 0xbd, 0x4e, - 0x20, 0x8e, 0xab, 0xfb, 0xfc, 0x56, 0x84, 0x2b, 0x9a, 0x1f, 0x26, 0xa4, 0x55, 0x84, 0x8b, 0xc8, 0xb2, 0xad, 0xe9, - 0x05, 0x0b, 0xd7, 0xf4, 0x12, 0x98, 0x29, 0xb9, 0x09, 0x2f, 0x81, 0xcb, 0xdb, 0x2c, 0xd6, 0x4b, 0x76, 0xd9, 0x90, - 0xbe, 0x19, 0xbe, 0xf8, 0xc2, 0xfa, 0xe4, 0x01, 0x0f, 0xe9, 0xfc, 0xf0, 0x52, 0xb0, 0x01, 0xb8, 0xce, 0xf8, 0xcd, - 0x77, 0xf2, 0x56, 0xcf, 0x4b, 0x7b, 0x8a, 0x71, 0x66, 0xda, 0x89, 0x49, 0x3b, 0x21, 0xf7, 0xef, 0xdb, 0xdb, 0xd8, - 0x9c, 0xec, 0x65, 0xb4, 0x51, 0x2e, 0xab, 0x96, 0x21, 0x29, 0x36, 0x0c, 0xf9, 0x7b, 0x94, 0x9c, 0x5a, 0x81, 0x27, - 0xbb, 0xe0, 0x55, 0xb2, 0xf2, 0x0f, 0x2a, 0x6b, 0x35, 0x60, 0x8f, 0x11, 0xcb, 0x42, 0xe1, 0xd8, 0xbf, 0xce, 0x58, - 0xb1, 0xf1, 0x05, 0x1a, 0x31, 0x72, 0x6f, 0xaf, 0x33, 0xe6, 0xc5, 0x5c, 0x4d, 0x36, 0x5e, 0xa8, 0x3a, 0x2f, 0x3d, - 0x6f, 0xf1, 0x5e, 0x4e, 0xa9, 0x61, 0x24, 0xa2, 0x07, 0x63, 0x65, 0x46, 0xa9, 0x12, 0xb5, 0x06, 0x8d, 0x08, 0x36, - 0x76, 0xc1, 0x2f, 0xc1, 0x09, 0x95, 0x7b, 0xea, 0x6c, 0xdf, 0x4e, 0xa9, 0xf4, 0x80, 0x65, 0xa9, 0x51, 0x95, 0xbb, - 0x65, 0x26, 0x59, 0x35, 0x08, 0x46, 0x7f, 0x94, 0x52, 0xcc, 0xf1, 0xce, 0xc8, 0x82, 0x29, 0x58, 0x09, 0xaa, 0x5a, - 0x86, 0xe5, 0x90, 0xa3, 0x16, 0xcf, 0xf8, 0xa4, 0x4a, 0xfd, 0xa3, 0x23, 0x48, 0xee, 0x6a, 0xd3, 0x0a, 0x92, 0xfb, - 0x64, 0xfc, 0x44, 0x0f, 0x74, 0xba, 0xd1, 0x8e, 0x87, 0x3e, 0xbf, 0x8d, 0xf8, 0xda, 0xba, 0xf7, 0x54, 0x6b, 0x15, - 0xca, 0x40, 0x8b, 0x15, 0x95, 0x2b, 0xb5, 0xa4, 0xf7, 0xbb, 0x08, 0x80, 0x45, 0x6c, 0xcc, 0xc6, 0xfb, 0xb6, 0x59, - 0x21, 0x68, 0x74, 0xd9, 0x6c, 0x1b, 0x0f, 0x58, 0xa2, 0x5b, 0x3b, 0x98, 0xd0, 0x78, 0xc6, 0xca, 0x7e, 0x3f, 0x9f, - 0x01, 0x3d, 0xd5, 0x46, 0x4c, 0x05, 0x1c, 0xf9, 0x9f, 0x5b, 0x91, 0x29, 0x0a, 0x6c, 0xd6, 0xd4, 0xdd, 0x1a, 0xcb, - 0x48, 0xf4, 0x65, 0x4a, 0x97, 0x27, 0x3c, 0x03, 0xa6, 0xcd, 0xa6, 0xe5, 0xb8, 0xb2, 0xaf, 0x38, 0xf2, 0x54, 0x58, - 0x56, 0x9c, 0x57, 0xe1, 0x78, 0xeb, 0xf1, 0x0d, 0x0e, 0x0d, 0x9b, 0x76, 0xe1, 0x0f, 0x21, 0x2c, 0x84, 0xd7, 0x19, - 0xdc, 0x46, 0xb4, 0x9d, 0x04, 0x2a, 0x6f, 0xcc, 0x75, 0x42, 0xd9, 0xdc, 0x6e, 0x36, 0x9e, 0x41, 0x3a, 0x31, 0x07, - 0x4a, 0x35, 0x82, 0xd6, 0x68, 0x16, 0x54, 0x8d, 0x78, 0xe4, 0x78, 0x78, 0x67, 0x10, 0xab, 0xe5, 0x4b, 0x9a, 0x4a, - 0xd1, 0x00, 0x8c, 0x0b, 0xe0, 0xf2, 0xf4, 0xcb, 0xfb, 0x9f, 0x4e, 0x79, 0x5c, 0x24, 0xab, 0x77, 0x71, 0x11, 0x5f, - 0x95, 0xe1, 0x56, 0x8d, 0x51, 0x5c, 0x93, 0xa9, 0x18, 0x30, 0x69, 0x56, 0x52, 0x73, 0x57, 0x6a, 0x42, 0x8c, 0x75, - 0x26, 0x9b, 0xb2, 0x92, 0x57, 0x8d, 0x4a, 0x37, 0x45, 0x86, 0x1f, 0xb7, 0x7c, 0x4e, 0x0f, 0x01, 0xc8, 0xd3, 0xb8, - 0x90, 0x46, 0x52, 0x17, 0x62, 0xcc, 0x45, 0xbc, 0xae, 0x8f, 0xc7, 0x8d, 0xae, 0x97, 0xec, 0xe9, 0xf8, 0xab, 0xe9, - 0xeb, 0x2c, 0xcc, 0x06, 0x82, 0x8c, 0xaa, 0x15, 0x17, 0x2d, 0x53, 0x4e, 0x65, 0x12, 0x80, 0x3e, 0x9e, 0x3d, 0xc6, - 0x8e, 0xc6, 0x63, 0xb2, 0x6d, 0x8b, 0x07, 0x78, 0xb8, 0xda, 0x84, 0x05, 0x99, 0xeb, 0x3a, 0xa2, 0x40, 0xf0, 0xdb, - 0x2a, 0x00, 0x24, 0x47, 0x5b, 0x95, 0xe1, 0xd2, 0xd8, 0xd3, 0xf1, 0x84, 0x4a, 0xec, 0x76, 0x48, 0x6a, 0xaf, 0x42, - 0x37, 0xf3, 0xd2, 0xf7, 0x28, 0x92, 0xc6, 0x65, 0x69, 0xaf, 0x52, 0xa9, 0xf6, 0xcc, 0xdc, 0x75, 0x0d, 0x62, 0x30, - 0x84, 0xba, 0xee, 0xd2, 0xab, 0x7b, 0xbf, 0xb9, 0xd6, 0x6c, 0x07, 0xbc, 0xd7, 0xa0, 0x19, 0x4a, 0xde, 0x62, 0xde, - 0xba, 0x22, 0x6a, 0xba, 0xde, 0x80, 0x59, 0x31, 0xca, 0x96, 0xa2, 0x74, 0x43, 0x41, 0x29, 0x18, 0x5d, 0x6c, 0xbc, - 0x85, 0xfb, 0x5a, 0x36, 0x2e, 0x2c, 0x99, 0x5e, 0x2d, 0x4a, 0x4a, 0xa8, 0x6e, 0x2a, 0x46, 0x4a, 0x18, 0x29, 0x0d, - 0x4f, 0xe5, 0x7b, 0x81, 0xc7, 0x79, 0x1e, 0x44, 0x2d, 0x2f, 0xb0, 0x93, 0x8a, 0x9c, 0x80, 0xa3, 0x97, 0xc9, 0x69, - 0x28, 0xf0, 0x8f, 0x99, 0x02, 0x31, 0x1d, 0xaa, 0xfb, 0x0d, 0x6e, 0xfe, 0x7f, 0x14, 0x2c, 0xf0, 0xf8, 0xd6, 0x4b, - 0xdc, 0x46, 0xff, 0x28, 0x7c, 0x5a, 0xfa, 0x4c, 0xfa, 0xae, 0x2e, 0x9e, 0xb4, 0x37, 0x1b, 0x25, 0xab, 0x2c, 0x4f, - 0xdf, 0xc8, 0x94, 0x83, 0xc8, 0x0c, 0xad, 0x41, 0xd9, 0x4c, 0x34, 0x6e, 0x78, 0x60, 0xc4, 0xd8, 0xb8, 0xf1, 0xfd, - 0x98, 0x81, 0x6c, 0x18, 0xac, 0xbe, 0x59, 0x2a, 0x93, 0xcd, 0x15, 0x60, 0x8a, 0x28, 0xf9, 0xc9, 0x8b, 0x9c, 0xc3, - 0x53, 0xa8, 0xaf, 0x5f, 0xe0, 0x36, 0x57, 0xfa, 0x3e, 0xe7, 0x3f, 0x66, 0xf4, 0x47, 0x04, 0x3a, 0x89, 0xd7, 0x20, - 0xf7, 0x78, 0x06, 0x75, 0x23, 0x4c, 0x2d, 0xc7, 0xe0, 0x40, 0x88, 0x06, 0x22, 0x2a, 0x16, 0x28, 0xa8, 0x0b, 0x03, - 0xac, 0xa1, 0x2e, 0x98, 0xc3, 0xf3, 0x5c, 0x26, 0x1f, 0xa7, 0xc6, 0x67, 0x7e, 0x18, 0x63, 0xcc, 0xe4, 0x60, 0x10, - 0x56, 0xf3, 0x60, 0x38, 0x1e, 0x4d, 0x8e, 0x9e, 0xc2, 0xb9, 0x1d, 0x8c, 0x03, 0x32, 0x08, 0xea, 0x72, 0x1d, 0x0b, - 0x5a, 0x5e, 0x5f, 0xda, 0x32, 0xf0, 0xe3, 0x3a, 0x18, 0xfc, 0xa3, 0xf0, 0x14, 0xef, 0xa0, 0x39, 0x39, 0x93, 0x21, - 0xd8, 0xd8, 0x6f, 0x08, 0x48, 0xca, 0x7a, 0x9a, 0x9f, 0xd4, 0x87, 0x1b, 0x53, 0xda, 0x3f, 0x73, 0x78, 0xc1, 0x61, - 0x87, 0x04, 0x0a, 0xa4, 0xf1, 0x34, 0x1b, 0xbd, 0x52, 0x8a, 0xdc, 0x77, 0x05, 0x87, 0x3b, 0x73, 0xcf, 0x99, 0x1e, - 0x39, 0x85, 0x44, 0x33, 0x0b, 0xb8, 0x91, 0xbf, 0x12, 0xd7, 0x71, 0x9e, 0xa5, 0x07, 0xcd, 0x37, 0x07, 0xe5, 0x9d, - 0xa8, 0xe2, 0xdb, 0x51, 0x60, 0xac, 0x09, 0xb9, 0xaf, 0x7a, 0x02, 0xf4, 0x04, 0xd8, 0x02, 0x60, 0x40, 0xbc, 0x67, - 0x66, 0x32, 0xe7, 0x11, 0x78, 0x04, 0x36, 0x7d, 0x20, 0x8b, 0x3b, 0xe7, 0x92, 0xe4, 0x6f, 0xa6, 0xd2, 0x5e, 0xf5, - 0xca, 0xbd, 0x82, 0xac, 0x57, 0x5b, 0xb9, 0xef, 0xd6, 0x67, 0xdf, 0x74, 0x78, 0x05, 0x9e, 0x4b, 0x70, 0x8b, 0xec, - 0xf7, 0x9b, 0x82, 0x4a, 0x61, 0x54, 0xc4, 0x7b, 0xc9, 0x35, 0xfa, 0xb7, 0x7b, 0x63, 0xa3, 0x48, 0x6e, 0xf9, 0xf0, - 0x00, 0xea, 0x4c, 0xde, 0x15, 0xb7, 0x73, 0x88, 0xda, 0xba, 0x1b, 0x0f, 0xac, 0x36, 0x68, 0x97, 0xb5, 0x40, 0x70, - 0xe1, 0xe5, 0x41, 0x06, 0x63, 0x81, 0xb3, 0x32, 0x52, 0x6a, 0x5c, 0x43, 0x6a, 0xc1, 0x27, 0x79, 0x7a, 0x0f, 0x59, - 0xea, 0x49, 0x50, 0xe4, 0x78, 0x16, 0x43, 0xa6, 0xf1, 0x36, 0xf0, 0xf8, 0x9d, 0x0c, 0x41, 0x9a, 0xb6, 0xdb, 0x35, - 0x47, 0xa0, 0xec, 0x1e, 0x98, 0x92, 0xd4, 0xb5, 0x31, 0x35, 0xd0, 0x50, 0x7b, 0xa8, 0x91, 0x8a, 0x38, 0x9b, 0xbd, - 0x06, 0x1d, 0x22, 0xf8, 0x7e, 0xa7, 0x59, 0xd9, 0xf1, 0x62, 0x42, 0xf0, 0xe4, 0x7d, 0x71, 0x9b, 0x95, 0x55, 0x19, - 0xbd, 0x4f, 0xd1, 0x10, 0x2a, 0x91, 0x22, 0x7a, 0x09, 0xf1, 0xf4, 0x4a, 0xfc, 0x5d, 0x46, 0x3f, 0xa5, 0x34, 0x4e, - 0x53, 0x4c, 0x7f, 0x5e, 0xc0, 0xcf, 0x67, 0x80, 0xea, 0x88, 0x3b, 0x21, 0x3a, 0x97, 0x60, 0xaf, 0x06, 0xd1, 0xac, - 0x2a, 0x0e, 0x18, 0x9a, 0xd1, 0xad, 0xa0, 0x88, 0xd1, 0x86, 0xd9, 0x7f, 0x28, 0x50, 0x28, 0xa4, 0x8a, 0xf9, 0x4e, - 0xd8, 0x87, 0xe8, 0x47, 0x2c, 0xf2, 0xe4, 0xdd, 0x2b, 0x33, 0xa4, 0xd1, 0x9d, 0xa4, 0x7a, 0x6b, 0xe3, 0xb1, 0x85, - 0x81, 0xcb, 0xa2, 0xcb, 0x0d, 0x3d, 0x8b, 0xd7, 0x59, 0xb4, 0x05, 0xfc, 0x89, 0x77, 0xaf, 0x9e, 0x29, 0x0b, 0x93, - 0xe7, 0x19, 0x28, 0x0e, 0x4e, 0xde, 0xbd, 0x7a, 0x2d, 0xd3, 0x4d, 0xce, 0xa3, 0x33, 0x89, 0xa4, 0xf5, 0xe4, 0xdd, - 0xab, 0x9f, 0xd1, 0xdc, 0xeb, 0xa7, 0x02, 0xde, 0xbf, 0x04, 0xde, 0x32, 0x8a, 0x37, 0xd0, 0x27, 0xf5, 0x3b, 0xd9, - 0x60, 0xa7, 0xbc, 0x5a, 0xcb, 0xe8, 0x97, 0xb4, 0xf6, 0xa4, 0x55, 0xff, 0x2c, 0x7c, 0x6a, 0xe7, 0x09, 0x78, 0x6e, - 0xf3, 0x4c, 0x7c, 0x8c, 0xac, 0x68, 0x27, 0x88, 0xbe, 0x3c, 0xb8, 0xbd, 0xca, 0x45, 0x19, 0xe1, 0x0b, 0x86, 0x76, - 0x41, 0xd1, 0xe1, 0xe1, 0xcd, 0xcd, 0xcd, 0xe8, 0xe6, 0xab, 0x91, 0x2c, 0x2e, 0x0f, 0x27, 0xdf, 0x7e, 0xfb, 0xed, - 0x21, 0xbe, 0x0d, 0xbe, 0x6c, 0xbb, 0xbd, 0x57, 0x84, 0x0f, 0x58, 0x80, 0x08, 0xd5, 0x5f, 0xc2, 0x15, 0x05, 0xb4, - 0x70, 0x83, 0x2f, 0x83, 0x2f, 0xf5, 0xa1, 0xf3, 0xe5, 0x71, 0x79, 0x7d, 0xa9, 0xca, 0xef, 0x2a, 0xf9, 0x68, 0x3c, - 0x1e, 0x1f, 0x82, 0x04, 0xea, 0xcb, 0x01, 0x1f, 0x04, 0xb3, 0x60, 0x90, 0xc1, 0x85, 0xa6, 0xbc, 0xbe, 0x9c, 0x05, - 0x9e, 0x69, 0x6e, 0x83, 0x45, 0x74, 0x20, 0x2e, 0xc1, 0xe1, 0x25, 0x0d, 0xbe, 0x0c, 0x88, 0x4b, 0xf9, 0x02, 0x52, - 0xbe, 0x38, 0x7a, 0xea, 0xa7, 0xfd, 0x2f, 0x95, 0xf6, 0x95, 0x9f, 0x76, 0x8c, 0x69, 0x5f, 0x3d, 0xf3, 0xd3, 0x66, - 0x2a, 0xed, 0x85, 0x9f, 0xf6, 0xbf, 0xcb, 0x01, 0xa4, 0x1e, 0xf8, 0xd6, 0x7f, 0xe7, 0x5e, 0x6b, 0xf0, 0x14, 0x8a, - 0xb2, 0xab, 0xf8, 0x92, 0x43, 0xa3, 0x07, 0xb7, 0x57, 0x39, 0x0d, 0x06, 0xd8, 0x5e, 0xcf, 0x24, 0xc4, 0xfb, 0xe0, - 0xcb, 0x4d, 0x91, 0x87, 0xc1, 0x97, 0x03, 0x2c, 0x64, 0xf0, 0x65, 0x40, 0xbe, 0x34, 0x06, 0x32, 0x82, 0x6d, 0x03, - 0x17, 0x8a, 0x74, 0x68, 0x03, 0x84, 0xf9, 0xd2, 0xb8, 0x9a, 0xfe, 0x59, 0x74, 0x67, 0xc3, 0x5b, 0xa2, 0x72, 0xd3, - 0x0d, 0x6a, 0xfa, 0x16, 0xbc, 0x13, 0xa0, 0x51, 0x51, 0x70, 0x1d, 0x17, 0xe1, 0x70, 0x58, 0x5e, 0x5f, 0x12, 0xb0, - 0xcb, 0x5c, 0xf3, 0xb8, 0x8a, 0x02, 0x21, 0x87, 0xea, 0x67, 0xa0, 0x22, 0x5f, 0x05, 0x08, 0x88, 0x04, 0xff, 0x05, - 0x35, 0x7d, 0x27, 0xd9, 0x36, 0x18, 0xde, 0xf0, 0xf3, 0x8f, 0x59, 0x35, 0x54, 0xa2, 0xc5, 0x6b, 0x41, 0xe1, 0x07, - 0xfc, 0x75, 0x55, 0x47, 0x7f, 0x82, 0x1b, 0x77, 0x53, 0xc3, 0xfe, 0x4e, 0x3a, 0x16, 0xf5, 0x9d, 0x5c, 0x64, 0xcb, - 0x69, 0xeb, 0x40, 0x7f, 0x2b, 0x49, 0xb5, 0xc8, 0x06, 0xc1, 0x30, 0x18, 0xf0, 0x25, 0x7b, 0x2b, 0x17, 0xdc, 0x33, - 0x9f, 0x7a, 0x24, 0xfd, 0x69, 0x9e, 0x67, 0x03, 0xf0, 0x4d, 0x41, 0x7e, 0xe4, 0xf0, 0xbf, 0x17, 0x43, 0x14, 0x1e, - 0x0e, 0x1e, 0x1d, 0x92, 0x79, 0xb0, 0xbe, 0x45, 0x8f, 0xce, 0x28, 0xc8, 0xc4, 0x8a, 0x17, 0x59, 0xe5, 0x2d, 0x95, - 0xbb, 0x4d, 0xdb, 0xcb, 0xe3, 0xde, 0xb3, 0x79, 0x1d, 0x8b, 0x40, 0x9d, 0x73, 0xa0, 0x78, 0x43, 0xd9, 0x53, 0xd9, - 0x94, 0x90, 0x6a, 0x43, 0xde, 0xb0, 0x1c, 0xb0, 0xe0, 0xb8, 0x37, 0x1c, 0x1e, 0x04, 0x03, 0xa7, 0xce, 0x1d, 0x04, - 0x07, 0xc3, 0xe1, 0x2c, 0x70, 0xf7, 0xa1, 0x6c, 0xe4, 0xee, 0x8c, 0xb4, 0x60, 0xff, 0x2c, 0xc2, 0x92, 0x82, 0x78, - 0x4c, 0x6a, 0xf1, 0x97, 0x06, 0x97, 0x19, 0x00, 0xf4, 0x91, 0x92, 0x80, 0x19, 0x58, 0x99, 0x01, 0x84, 0xe6, 0xa6, - 0x31, 0x3b, 0x03, 0xe6, 0x11, 0x38, 0xe6, 0x11, 0x32, 0x0e, 0x80, 0x58, 0x12, 0xe0, 0xdc, 0x05, 0x51, 0xac, 0x0b, - 0x79, 0x04, 0xa0, 0xf7, 0xf8, 0x93, 0x98, 0x52, 0x30, 0x49, 0xc7, 0x2a, 0x04, 0x41, 0x1c, 0x9f, 0x5f, 0x8b, 0xd6, - 0xe4, 0xac, 0xd0, 0xc1, 0x8c, 0x24, 0xc0, 0x86, 0x18, 0xd8, 0x39, 0xb8, 0x9f, 0x83, 0xd2, 0xc3, 0xea, 0x9d, 0x90, - 0x0b, 0xbe, 0xe3, 0x9e, 0x6c, 0x16, 0xae, 0x9e, 0x70, 0x10, 0xdc, 0x71, 0xcd, 0x02, 0x8c, 0xaa, 0x62, 0x53, 0x56, - 0x3c, 0xfd, 0x70, 0xb7, 0x86, 0xd8, 0x77, 0x38, 0xa0, 0xef, 0x64, 0x9e, 0x25, 0x77, 0xa1, 0xb3, 0xe7, 0xda, 0xaa, - 0xf4, 0x1f, 0x3e, 0xbc, 0xfe, 0x29, 0x02, 0x91, 0x63, 0x6d, 0x28, 0xfd, 0x1d, 0xc7, 0xb3, 0xc9, 0x8f, 0xf0, 0xe4, - 0x6f, 0xec, 0x3b, 0x6e, 0x4f, 0x8f, 0x7e, 0x1f, 0xea, 0xa6, 0x77, 0x7c, 0x7e, 0xc7, 0x47, 0xae, 0x38, 0x54, 0x57, - 0xb8, 0xaf, 0x6f, 0x36, 0xbe, 0x11, 0xd2, 0xc3, 0xf3, 0x4c, 0x79, 0x63, 0x7e, 0xb4, 0x83, 0x61, 0x10, 0x4c, 0xb5, - 0x50, 0x12, 0xa2, 0x6e, 0x30, 0x25, 0x60, 0x88, 0x0e, 0xf4, 0xb2, 0x9a, 0x22, 0xe7, 0xa6, 0x46, 0x16, 0xde, 0x0f, - 0x98, 0x16, 0x3a, 0x34, 0x72, 0x28, 0x3f, 0x38, 0x9c, 0x30, 0x66, 0xe1, 0xb7, 0x4a, 0x98, 0x7e, 0xb5, 0xa8, 0x9c, - 0x83, 0xe8, 0x01, 0x18, 0xe3, 0x0a, 0x5e, 0x40, 0x57, 0xd8, 0xf5, 0x46, 0x45, 0xc5, 0x40, 0xf0, 0x38, 0xe4, 0x00, - 0x3d, 0xec, 0x82, 0x96, 0x95, 0xa5, 0xba, 0x55, 0x39, 0x4b, 0x15, 0x75, 0x19, 0xca, 0xca, 0x58, 0x61, 0xbe, 0x97, - 0xec, 0x87, 0x02, 0x3d, 0xcb, 0xa7, 0xa2, 0x0b, 0x5e, 0x08, 0x25, 0x58, 0xae, 0xeb, 0x9d, 0x08, 0x44, 0x9d, 0x1f, - 0x7a, 0x57, 0x7d, 0x8d, 0x63, 0xc7, 0xd3, 0xd7, 0x32, 0xe5, 0xda, 0x84, 0x42, 0xf3, 0xf9, 0xd2, 0x57, 0x4c, 0x14, - 0xec, 0x06, 0xfa, 0xd5, 0xb6, 0xd1, 0x67, 0x77, 0x1b, 0xbd, 0x19, 0x94, 0xe8, 0x98, 0xd7, 0x28, 0xb8, 0x56, 0x0a, - 0x05, 0xa3, 0xbd, 0x8d, 0x3f, 0xc1, 0x91, 0x5b, 0xdd, 0x1e, 0x7a, 0xbf, 0x55, 0xf1, 0xe5, 0x1b, 0xf4, 0xed, 0xb4, - 0x3f, 0x47, 0x95, 0xfc, 0x65, 0xbd, 0x06, 0x1f, 0x2a, 0x88, 0x2c, 0x62, 0x71, 0x69, 0xa1, 0x9e, 0xd3, 0x77, 0x27, - 0x6f, 0xc0, 0x8f, 0x12, 0x7f, 0xff, 0xfa, 0x7d, 0x50, 0x93, 0x69, 0x3c, 0x2f, 0xcc, 0x87, 0x36, 0x07, 0x84, 0x26, - 0x71, 0x69, 0xf6, 0xfd, 0x3c, 0x6e, 0xb2, 0xef, 0x9a, 0xad, 0xa7, 0x45, 0x13, 0x49, 0xca, 0x70, 0xfb, 0x60, 0x40, - 0xa0, 0x0f, 0x10, 0xc5, 0xd9, 0x17, 0x34, 0x86, 0x34, 0x9f, 0xd9, 0xf7, 0x23, 0xe2, 0xbd, 0xd8, 0x0b, 0x21, 0xc6, - 0x15, 0x16, 0x8d, 0x1e, 0xf2, 0x39, 0x8f, 0x94, 0x61, 0xd1, 0x7b, 0x4c, 0x20, 0xce, 0x70, 0x5a, 0xbd, 0x47, 0xcc, - 0x63, 0xbc, 0x1b, 0x68, 0xd9, 0x43, 0x94, 0x51, 0x97, 0xbd, 0x61, 0xf1, 0xfd, 0x71, 0x13, 0x66, 0xd6, 0xf2, 0x72, - 0x08, 0x7f, 0x03, 0x6d, 0x00, 0x4e, 0x39, 0xb2, 0x7c, 0x95, 0xd9, 0xe8, 0x6a, 0x89, 0xe9, 0x4d, 0x04, 0xb1, 0x78, - 0x74, 0x3a, 0xac, 0x5d, 0x9d, 0xaa, 0x77, 0xb5, 0xf3, 0x99, 0xe8, 0x55, 0xa0, 0x95, 0x6b, 0xdb, 0xe3, 0x21, 0xdc, - 0xa5, 0x96, 0x56, 0xd8, 0x88, 0x72, 0x2e, 0x9e, 0xee, 0x02, 0x9b, 0x13, 0xd0, 0xe0, 0x4a, 0xa6, 0x00, 0x9c, 0xa5, - 0xd5, 0x68, 0xd4, 0x08, 0xfb, 0xac, 0x9c, 0xcf, 0x61, 0x6b, 0x21, 0x9e, 0x16, 0x80, 0xe1, 0x36, 0x31, 0x28, 0x79, - 0x37, 0x06, 0xe5, 0xf4, 0xa3, 0x82, 0xb7, 0x0e, 0xce, 0xca, 0x55, 0x9c, 0xca, 0x1b, 0xc0, 0x62, 0x0c, 0xfc, 0x54, - 0x2c, 0xd5, 0x4b, 0x48, 0x56, 0x3c, 0xf9, 0x88, 0x56, 0x1b, 0x69, 0x00, 0x5c, 0xe5, 0xd4, 0x58, 0xee, 0x29, 0x90, - 0x50, 0x57, 0x8a, 0x4a, 0x88, 0xab, 0x2a, 0x4e, 0x56, 0xa7, 0x98, 0x1a, 0x6e, 0xa1, 0x17, 0x51, 0x20, 0xd7, 0x5c, - 0x00, 0x49, 0xcf, 0xd9, 0xbf, 0x32, 0x8d, 0x35, 0xfe, 0x4c, 0xa2, 0x80, 0x49, 0xa3, 0x06, 0x63, 0xa5, 0xec, 0x85, - 0x34, 0xd1, 0xde, 0x82, 0xa0, 0x76, 0x2f, 0xff, 0x84, 0xba, 0x9f, 0x41, 0x2b, 0xc2, 0x06, 0x18, 0xa2, 0x3c, 0xc7, - 0x1d, 0x9a, 0xda, 0x25, 0xe7, 0x01, 0x23, 0x3a, 0xef, 0xb3, 0xda, 0x6e, 0xf5, 0x67, 0x2b, 0xc0, 0x36, 0x4d, 0x8d, - 0x4f, 0x61, 0x98, 0x10, 0x13, 0x1b, 0xd8, 0x2a, 0x2b, 0xed, 0x86, 0x32, 0xed, 0xa4, 0x2b, 0xe6, 0xb5, 0x70, 0x9a, - 0xf7, 0x18, 0x5b, 0x8d, 0x54, 0xee, 0x7e, 0x3f, 0x34, 0x3f, 0x59, 0x4e, 0x9f, 0xe9, 0x90, 0xcd, 0xde, 0x78, 0xd0, - 0x9c, 0x68, 0x75, 0x55, 0x47, 0x3f, 0xa0, 0x03, 0x30, 0xd3, 0x16, 0x20, 0xd3, 0x05, 0x9b, 0xf6, 0x95, 0xa8, 0xb8, - 0x24, 0x61, 0xa9, 0x24, 0xb0, 0xb3, 0x9b, 0x92, 0x9d, 0x6d, 0x40, 0x3c, 0xc3, 0x5d, 0x4f, 0x8b, 0x9d, 0x90, 0x26, - 0xbc, 0xc5, 0x41, 0x02, 0xa2, 0x0e, 0x55, 0x5d, 0x42, 0xb6, 0xc6, 0xd0, 0xc5, 0xbf, 0x28, 0x85, 0x09, 0x6b, 0x99, - 0x54, 0x25, 0x26, 0x08, 0x52, 0xb9, 0xdf, 0x22, 0xb0, 0x44, 0xc1, 0x0e, 0x60, 0xef, 0xdd, 0xa8, 0x9b, 0x51, 0x53, - 0xd5, 0xa9, 0x97, 0xe0, 0xe3, 0x34, 0xef, 0x2a, 0xc8, 0x2c, 0xec, 0xaa, 0xd8, 0xf0, 0x40, 0xc7, 0xa6, 0x52, 0xc6, - 0xc4, 0x5d, 0x5a, 0x64, 0x88, 0x07, 0x8c, 0xb1, 0x74, 0x21, 0x90, 0x6f, 0xb6, 0x3f, 0x6e, 0x7a, 0x82, 0xd0, 0x4f, - 0xd8, 0x50, 0x02, 0x37, 0x9d, 0xed, 0xa9, 0x69, 0xe6, 0x03, 0x22, 0x0e, 0x03, 0x0a, 0x24, 0x1b, 0x87, 0x34, 0x47, - 0xfa, 0x82, 0xa4, 0x09, 0x03, 0x43, 0x2b, 0x9e, 0x13, 0x64, 0x45, 0xa1, 0x67, 0xeb, 0xaa, 0x8d, 0x73, 0x65, 0x98, - 0xa3, 0x25, 0xa7, 0xc2, 0xe7, 0x04, 0x99, 0xd8, 0x3d, 0x6d, 0x33, 0x93, 0xe1, 0x28, 0x59, 0x60, 0x7e, 0x05, 0x51, - 0xe2, 0xce, 0x34, 0xab, 0x72, 0x30, 0x2e, 0x60, 0x81, 0x56, 0xbe, 0x07, 0x75, 0x63, 0x0d, 0x6d, 0x35, 0x0c, 0xb1, - 0xdb, 0x9f, 0x60, 0xbf, 0xd6, 0x4e, 0xeb, 0x32, 0xc5, 0xf2, 0x32, 0x85, 0x68, 0x2f, 0x64, 0x7e, 0xa3, 0x48, 0x74, - 0xaf, 0x08, 0x43, 0xc2, 0x3a, 0xca, 0x9e, 0xb4, 0xa9, 0x01, 0xf4, 0xd4, 0x0b, 0x78, 0xde, 0xb9, 0x96, 0x61, 0x17, - 0xe9, 0xfe, 0xaa, 0xe0, 0x53, 0xba, 0x41, 0x90, 0xa2, 0x37, 0x29, 0x98, 0xf3, 0x7a, 0x94, 0xd4, 0x9b, 0xd3, 0x96, - 0x19, 0x55, 0x47, 0x45, 0x48, 0x39, 0xc1, 0x7f, 0xf2, 0x52, 0x6a, 0x62, 0x13, 0x26, 0x78, 0xe0, 0xc3, 0x3c, 0xc3, - 0x06, 0xde, 0xed, 0xde, 0xa5, 0x61, 0xd2, 0x66, 0x1b, 0x52, 0x90, 0x56, 0x98, 0xb8, 0x18, 0x50, 0xd9, 0x2b, 0xdc, - 0x2f, 0xd8, 0x4e, 0x9a, 0x82, 0x07, 0x61, 0xa3, 0x81, 0x89, 0x5b, 0x5d, 0x7c, 0x13, 0x26, 0x34, 0x5c, 0x51, 0xed, - 0xec, 0xa4, 0x25, 0xcd, 0xed, 0x75, 0x79, 0x61, 0xfb, 0xa0, 0x63, 0x87, 0x75, 0x0d, 0x0f, 0x34, 0xaf, 0xd9, 0xc5, - 0x35, 0xd3, 0x34, 0xd1, 0x58, 0x0f, 0x29, 0x4b, 0x8e, 0x4d, 0x3d, 0x5d, 0xe3, 0x6a, 0x99, 0x6b, 0x60, 0x77, 0x89, - 0x17, 0x7a, 0xc0, 0xc3, 0x0e, 0xd7, 0x24, 0xba, 0xc0, 0x66, 0xb3, 0x75, 0x4d, 0xa6, 0xf9, 0x7d, 0xd9, 0x72, 0x13, - 0x10, 0xce, 0x52, 0xdf, 0xdc, 0x27, 0xc7, 0x9a, 0xb6, 0xf9, 0x49, 0x80, 0xe3, 0xed, 0x15, 0x90, 0x74, 0x2c, 0x41, - 0x17, 0xdf, 0xd2, 0x1f, 0x44, 0x6a, 0xa6, 0x82, 0xde, 0x3b, 0x5f, 0xa4, 0x6e, 0x7e, 0x01, 0xb6, 0x51, 0x5b, 0x63, - 0x9a, 0x95, 0x6d, 0xc2, 0x44, 0x59, 0x58, 0x23, 0x0b, 0xb9, 0x02, 0x1f, 0xcc, 0xfd, 0xa6, 0x4e, 0x4f, 0x3a, 0x88, - 0xb0, 0xdf, 0x45, 0x8f, 0x47, 0x18, 0x2b, 0xd6, 0x20, 0x31, 0xac, 0xc2, 0x86, 0x36, 0x97, 0x43, 0x94, 0x53, 0xb3, - 0x64, 0xa2, 0x15, 0xf5, 0x29, 0x45, 0x94, 0x82, 0xb9, 0xf1, 0xb4, 0x6c, 0x98, 0x12, 0x22, 0x64, 0x85, 0x74, 0x40, - 0xb5, 0x16, 0x5a, 0xaa, 0x09, 0x7a, 0x1d, 0x7a, 0x59, 0x68, 0x4c, 0x41, 0xf4, 0x11, 0x19, 0x6e, 0xc4, 0x91, 0xd1, - 0xfd, 0x31, 0x8a, 0x09, 0x84, 0xaa, 0xf6, 0xf2, 0xc2, 0xea, 0xd3, 0xb2, 0xad, 0x0e, 0xe2, 0x0a, 0x91, 0xef, 0xbb, - 0x09, 0x6a, 0x8c, 0x82, 0x36, 0xa7, 0x1b, 0xfd, 0xa5, 0x08, 0x7d, 0xbb, 0x70, 0xec, 0x46, 0x41, 0x24, 0x44, 0x60, - 0xf5, 0x9a, 0x8a, 0x01, 0x59, 0x17, 0xb1, 0x8b, 0xd0, 0xa4, 0xbb, 0x85, 0x28, 0x6f, 0x54, 0xd6, 0x1f, 0x37, 0x21, - 0xd9, 0xed, 0xb0, 0x2c, 0xf0, 0x65, 0x3f, 0xdd, 0xdc, 0x03, 0xf9, 0xfd, 0x7a, 0xf3, 0x49, 0xc8, 0xef, 0x57, 0xd9, - 0xe7, 0x40, 0x7e, 0xbf, 0xde, 0xfc, 0x4f, 0x43, 0x7e, 0x9f, 0x6e, 0x3c, 0xc8, 0x6f, 0x35, 0x18, 0xbf, 0x15, 0x2c, - 0x78, 0xfb, 0x26, 0xa0, 0xcf, 0x25, 0x0b, 0xde, 0xbe, 0x7c, 0xe9, 0x1b, 0x81, 0x08, 0x8d, 0x5c, 0x6f, 0x64, 0xc1, - 0x88, 0xdb, 0x02, 0xaf, 0x50, 0xeb, 0xe4, 0x03, 0x15, 0x65, 0x00, 0xbc, 0x5e, 0xfe, 0x23, 0xab, 0x56, 0x61, 0x70, - 0x18, 0x90, 0xb9, 0x83, 0x04, 0x1d, 0x4e, 0xe0, 0xf6, 0x86, 0x46, 0x96, 0xd5, 0x67, 0xc1, 0x87, 0x8f, 0x46, 0xa3, - 0xb8, 0xb8, 0xc4, 0x4b, 0x9d, 0xd9, 0x48, 0x08, 0x78, 0x9c, 0xf1, 0xd2, 0x86, 0x88, 0x58, 0xc5, 0xe5, 0x99, 0x8e, - 0xcd, 0x52, 0xda, 0xad, 0x08, 0x11, 0xe7, 0xcf, 0x00, 0xa7, 0xde, 0xee, 0xcd, 0x18, 0xfb, 0xa1, 0x38, 0x62, 0x1d, - 0x40, 0xf6, 0xd9, 0x46, 0xbf, 0x3b, 0x8f, 0x4b, 0xfe, 0x2e, 0xae, 0x56, 0x0c, 0x7a, 0x09, 0x77, 0x11, 0xc1, 0x93, - 0xca, 0x63, 0x9b, 0x14, 0x50, 0x79, 0xa6, 0x81, 0xca, 0x3b, 0xde, 0xd3, 0xd0, 0x0e, 0x8b, 0xf6, 0x01, 0x36, 0xd2, - 0xe5, 0x0c, 0x8c, 0x16, 0x5f, 0x5c, 0x73, 0x51, 0xfd, 0x04, 0x78, 0xea, 0x82, 0x17, 0x70, 0x4b, 0x40, 0x2e, 0xb6, - 0xe1, 0x84, 0x40, 0x85, 0xef, 0xd9, 0xa1, 0xa2, 0xc6, 0x18, 0xd1, 0x44, 0xa3, 0xdf, 0x78, 0x13, 0x42, 0xef, 0x4e, - 0xd0, 0x15, 0x61, 0x24, 0xbc, 0x3f, 0x37, 0xfc, 0x2c, 0x03, 0xf3, 0x79, 0x01, 0x50, 0x1a, 0x08, 0x87, 0xca, 0x94, - 0xdc, 0x02, 0x13, 0xb6, 0xc6, 0x5c, 0x29, 0x4b, 0x3d, 0xa4, 0x52, 0xaa, 0xe0, 0x74, 0x05, 0x4d, 0x25, 0xe0, 0x70, - 0x47, 0x12, 0xc0, 0x4c, 0x6d, 0x61, 0x10, 0xdd, 0x36, 0xa5, 0x59, 0x1a, 0x39, 0x45, 0x9a, 0xc3, 0x27, 0xa5, 0x0a, - 0x74, 0xfa, 0x2c, 0x89, 0x2b, 0x7e, 0x29, 0x0b, 0x08, 0x85, 0xdb, 0x4a, 0xa1, 0x48, 0xbf, 0xcf, 0xc4, 0xe6, 0x8a, - 0x17, 0x59, 0x72, 0xb6, 0xca, 0xca, 0x0a, 0xf2, 0x2d, 0xf4, 0xe9, 0xb7, 0xac, 0xa7, 0x05, 0xce, 0x9b, 0x9a, 0x14, - 0x66, 0xe6, 0xf1, 0x44, 0xed, 0x74, 0x50, 0xaf, 0x7a, 0xaf, 0x0d, 0xf6, 0x7b, 0x73, 0xa2, 0xc7, 0xad, 0xf5, 0x60, - 0x35, 0xa9, 0xcd, 0x54, 0x05, 0x71, 0x04, 0xd4, 0x81, 0xcd, 0x70, 0x16, 0x53, 0xba, 0xb1, 0x08, 0x2a, 0x60, 0xed, - 0x80, 0x39, 0x32, 0x71, 0x79, 0x76, 0xa3, 0xe4, 0x27, 0x3d, 0x45, 0x61, 0xd2, 0x28, 0x56, 0xc8, 0x3e, 0x2b, 0x16, - 0x6e, 0x58, 0x72, 0x4f, 0xae, 0x4d, 0x94, 0x34, 0x30, 0xba, 0xe2, 0xf6, 0x40, 0x1c, 0x27, 0xed, 0x94, 0xf9, 0x70, - 0x12, 0xed, 0x65, 0x03, 0xe6, 0xa0, 0x9d, 0x0f, 0xae, 0xaa, 0xab, 0xb9, 0x6a, 0xc5, 0xa8, 0x92, 0x3f, 0xc9, 0x1b, - 0x73, 0xb1, 0x3d, 0x4e, 0x3a, 0x12, 0xa1, 0xdc, 0x49, 0x94, 0x1f, 0xaf, 0xd4, 0x0f, 0x80, 0x5e, 0xd1, 0xe4, 0xf0, - 0xcf, 0x0d, 0x37, 0xe0, 0xf4, 0xa1, 0xf6, 0x10, 0x74, 0xa2, 0x7c, 0x3d, 0x23, 0x9e, 0x51, 0x9d, 0x5e, 0x2e, 0x0b, - 0x30, 0xe8, 0xf2, 0x87, 0x12, 0x22, 0xc8, 0x74, 0x4e, 0xeb, 0x72, 0xaa, 0xcd, 0x8b, 0x0d, 0x6f, 0x43, 0x3f, 0xef, - 0x3b, 0x0d, 0x9c, 0x9b, 0xf0, 0x70, 0xf8, 0x74, 0x4c, 0x6a, 0x6d, 0xeb, 0x8e, 0x0b, 0xcf, 0xfe, 0x56, 0x8b, 0xd3, - 0x3d, 0xdb, 0x05, 0x4a, 0x9b, 0x5e, 0x0a, 0xed, 0x1a, 0xa9, 0xb8, 0xa7, 0xfb, 0x35, 0xa9, 0xdd, 0x42, 0xb3, 0x92, - 0xa7, 0xdf, 0xd5, 0x69, 0x77, 0xf6, 0x68, 0x9b, 0xe9, 0x2a, 0xeb, 0xdf, 0x33, 0x1b, 0xa7, 0xae, 0x41, 0x39, 0x6a, - 0xbd, 0x04, 0x7d, 0x98, 0xb8, 0x8e, 0x6c, 0x7a, 0x3a, 0x59, 0xd6, 0x49, 0x7e, 0x46, 0xea, 0x91, 0xab, 0xb0, 0x5d, - 0xdd, 0x59, 0xf8, 0x2d, 0x4f, 0xc2, 0xae, 0x86, 0xa9, 0x9b, 0x81, 0xe9, 0x02, 0x22, 0x67, 0x82, 0x3e, 0x20, 0xfc, - 0xfd, 0xd1, 0xb6, 0x49, 0xce, 0xea, 0x43, 0xef, 0x33, 0xfc, 0x9d, 0xa5, 0xf0, 0xb7, 0xaa, 0x7f, 0xa7, 0xdb, 0x2b, - 0x5e, 0xad, 0x64, 0x1a, 0x05, 0xef, 0xde, 0x9e, 0x7e, 0x08, 0x34, 0x22, 0x3b, 0xde, 0x4b, 0x8c, 0x26, 0xda, 0x60, - 0x3f, 0x81, 0x66, 0x26, 0x97, 0x97, 0x08, 0x4a, 0xa8, 0x51, 0xed, 0x4f, 0x57, 0xf2, 0xe6, 0x24, 0xcf, 0x7d, 0xe6, - 0xd9, 0x10, 0x5c, 0xcd, 0x4f, 0x36, 0xa8, 0x55, 0x08, 0x32, 0xc0, 0x51, 0x56, 0x9e, 0x69, 0xad, 0x4d, 0x7a, 0x76, - 0x7e, 0x77, 0xa6, 0x25, 0x43, 0x16, 0x15, 0xf2, 0xd9, 0xef, 0xc7, 0x69, 0x76, 0x7d, 0x80, 0xa7, 0x02, 0x0b, 0xc0, - 0xa4, 0x3e, 0xe7, 0xe7, 0x9b, 0xaa, 0x92, 0x62, 0x58, 0xc8, 0x9b, 0x60, 0x76, 0xac, 0x1e, 0x4c, 0x86, 0x58, 0x3d, - 0x06, 0x07, 0xff, 0x95, 0xe4, 0x59, 0xf2, 0x91, 0x05, 0x8f, 0xb6, 0x19, 0x9b, 0xb5, 0x68, 0xff, 0xb8, 0x0e, 0x66, - 0xd0, 0xd6, 0x83, 0x93, 0x3c, 0x3f, 0x3e, 0x54, 0x5f, 0xcc, 0x8e, 0x0f, 0xd3, 0xec, 0x7a, 0xe6, 0x01, 0xf4, 0x3b, - 0x7b, 0x5b, 0x84, 0x42, 0x73, 0xf7, 0x69, 0x70, 0xac, 0x4d, 0x78, 0x68, 0x39, 0x10, 0x90, 0xe2, 0x98, 0xf0, 0x26, - 0x28, 0xf9, 0x09, 0x63, 0x38, 0x6f, 0x77, 0xbb, 0xd0, 0x1a, 0x03, 0x25, 0x1e, 0x52, 0x4e, 0x01, 0x1c, 0x0a, 0x66, - 0xa1, 0x09, 0xa1, 0x49, 0x4d, 0x42, 0x83, 0xe7, 0x13, 0x13, 0x5a, 0xd4, 0x14, 0x8e, 0xa0, 0xd7, 0xf1, 0xda, 0x08, - 0xbf, 0xb4, 0x30, 0xc1, 0xb4, 0x7e, 0xde, 0x18, 0xc7, 0xa8, 0x3d, 0xaa, 0x06, 0x61, 0xab, 0x57, 0xde, 0x37, 0xb0, - 0x20, 0xef, 0x0c, 0x2b, 0x1a, 0xb4, 0xe8, 0x0a, 0xc8, 0x2b, 0x7d, 0x31, 0x1b, 0xa7, 0xe1, 0xa2, 0xa4, 0x72, 0x49, - 0xd8, 0x2c, 0xdc, 0x22, 0xbb, 0x5d, 0x2a, 0xea, 0x1d, 0xc9, 0xda, 0xc1, 0x5d, 0xaa, 0xd9, 0x99, 0x3d, 0xda, 0x0a, - 0x44, 0x58, 0x2c, 0xd9, 0xac, 0x39, 0x5f, 0x55, 0x7c, 0x3e, 0x5c, 0x71, 0xf0, 0xcb, 0x09, 0x0e, 0xfe, 0x2b, 0x3d, - 0xcf, 0xed, 0xa4, 0xa8, 0x15, 0xb9, 0x8a, 0x45, 0x9a, 0xf3, 0x0f, 0xf1, 0xf9, 0x0f, 0x98, 0xe7, 0xf9, 0x79, 0xfe, - 0x0c, 0x32, 0xd4, 0xc1, 0xec, 0xd1, 0x36, 0xa9, 0x46, 0x2f, 0xde, 0x7c, 0x78, 0xf5, 0xe1, 0x9f, 0x67, 0xcf, 0x4e, - 0x3e, 0xbc, 0xf8, 0xfe, 0xed, 0xfb, 0x57, 0x2f, 0x4e, 0x17, 0xd6, 0x11, 0x56, 0xe1, 0xab, 0x91, 0xe5, 0x6e, 0xe7, - 0xf2, 0xfd, 0xf2, 0xe6, 0xf9, 0x8b, 0x97, 0xaf, 0xde, 0xbc, 0x78, 0x5e, 0xab, 0xb9, 0x6c, 0x37, 0x04, 0x76, 0x68, - 0x9c, 0x09, 0x5e, 0x40, 0xf1, 0x3a, 0xca, 0x23, 0x36, 0x5b, 0xc3, 0x02, 0x36, 0x9b, 0xae, 0x23, 0x28, 0xc0, 0x22, - 0x3b, 0xd0, 0x9b, 0x05, 0x1a, 0x2e, 0xcd, 0xc6, 0xf1, 0x97, 0x98, 0xdf, 0x9b, 0x17, 0xf8, 0xdd, 0x7b, 0x79, 0x63, - 0xba, 0xa2, 0x47, 0x48, 0x21, 0x7e, 0xcd, 0x9f, 0xfd, 0x7e, 0xec, 0x4b, 0xd9, 0x50, 0x14, 0xa1, 0xca, 0x85, 0x5f, - 0x75, 0x70, 0xa0, 0x2d, 0xfe, 0x02, 0x08, 0x58, 0x11, 0xcc, 0x8e, 0x0f, 0xfd, 0xdc, 0xb3, 0xdf, 0xa3, 0x9f, 0xbc, - 0xce, 0x61, 0xa9, 0x30, 0x0e, 0xcd, 0xb4, 0xbd, 0x53, 0x11, 0x42, 0x2a, 0xb9, 0x73, 0x53, 0xad, 0x20, 0x43, 0xae, - 0x24, 0x89, 0xec, 0x24, 0x2a, 0x4b, 0x16, 0x53, 0xda, 0xef, 0xfa, 0xaf, 0xeb, 0x33, 0x4a, 0x06, 0xb8, 0x28, 0x65, - 0x11, 0x40, 0x3f, 0xda, 0x71, 0x26, 0x0e, 0xbc, 0x78, 0x2e, 0xd8, 0xa3, 0x4e, 0xf2, 0x0e, 0x23, 0x72, 0xd8, 0xfe, - 0xd4, 0xeb, 0xd8, 0xef, 0xc4, 0xfd, 0xf8, 0x3f, 0xcd, 0x3d, 0xeb, 0x76, 0xdb, 0x36, 0xd2, 0xff, 0xfb, 0x14, 0x0c, - 0x93, 0x4d, 0xc5, 0x84, 0xa4, 0x49, 0xc9, 0xb2, 0x15, 0xc9, 0xb2, 0xdb, 0xe6, 0x72, 0x36, 0xfb, 0xb9, 0x4d, 0x4f, - 0xe2, 0xe6, 0xdb, 0xad, 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, 0x20, 0x78, 0x93, 0x94, 0x26, 0xed, 0xee, 0x69, 0x93, - 0x88, 0xb8, 0x63, 0x00, 0x0c, 0x06, 0x73, 0xd5, 0xf8, 0x64, 0x4a, 0xe8, 0x45, 0x0e, 0xb0, 0x39, 0x1e, 0xc8, 0xe5, - 0x1b, 0xdf, 0xfc, 0xdf, 0xc8, 0x9c, 0x7b, 0xe6, 0xd2, 0x33, 0xdf, 0x85, 0x57, 0x59, 0xed, 0xea, 0xc8, 0x58, 0x33, - 0x26, 0x1b, 0xb4, 0xc0, 0x63, 0x05, 0x7f, 0x47, 0x70, 0xea, 0xda, 0xb7, 0xb9, 0x8c, 0xed, 0xc2, 0x8b, 0xe7, 0x4c, - 0x84, 0x78, 0x11, 0xb9, 0x29, 0x87, 0x8a, 0xa1, 0x80, 0x05, 0xdc, 0xb9, 0x3c, 0xe0, 0xaa, 0x08, 0xbe, 0x3d, 0x49, - 0xe3, 0xe0, 0x7f, 0xd8, 0x3d, 0x50, 0x7b, 0x49, 0x1a, 0xad, 0x80, 0xc6, 0xf7, 0xe6, 0x9c, 0x67, 0x63, 0xb6, 0xd8, - 0x7e, 0xdd, 0x7d, 0xfc, 0xc8, 0x6c, 0xdc, 0x92, 0x40, 0x28, 0xda, 0x69, 0x34, 0x9f, 0x07, 0xac, 0xa5, 0x8b, 0xa0, - 0x23, 0xba, 0x29, 0xbb, 0x39, 0x7b, 0xe0, 0x08, 0x4f, 0x9f, 0x46, 0xd6, 0x74, 0xb4, 0xc4, 0x8c, 0x99, 0x74, 0x85, - 0x47, 0x14, 0x2f, 0xf2, 0x74, 0x6f, 0x50, 0x2c, 0xc2, 0xd7, 0x25, 0x3f, 0xba, 0xd6, 0x34, 0x5a, 0x8f, 0x03, 0x66, - 0xe1, 0x76, 0x87, 0x2e, 0x37, 0xe3, 0xf5, 0x78, 0x0c, 0xd1, 0x5d, 0x1e, 0x38, 0x26, 0xf8, 0xab, 0x89, 0x12, 0x7c, - 0x47, 0x66, 0xc6, 0x00, 0x26, 0x65, 0xa7, 0xe5, 0xe1, 0x83, 0x8e, 0x09, 0xb0, 0x88, 0xa8, 0x83, 0x14, 0xde, 0x8c, - 0x35, 0xa7, 0x76, 0xa8, 0xbf, 0x83, 0xdd, 0x97, 0xe8, 0x83, 0xba, 0xa3, 0x3f, 0xbc, 0xd4, 0xdf, 0x21, 0x8c, 0x31, - 0xea, 0xf1, 0x73, 0xda, 0xbd, 0xba, 0xa9, 0x93, 0xb0, 0x7c, 0x8d, 0xf1, 0x0f, 0x80, 0x59, 0xfc, 0xc2, 0xf7, 0xe6, - 0x61, 0x94, 0xa4, 0xfe, 0x44, 0xbf, 0x1a, 0xbc, 0xf6, 0x5b, 0x97, 0xcb, 0xb4, 0x65, 0x5c, 0x99, 0x93, 0x54, 0x0d, - 0x9d, 0x22, 0x10, 0x26, 0x46, 0x4e, 0x69, 0x2a, 0xa4, 0x9e, 0xa0, 0xad, 0x05, 0x05, 0x6a, 0xc6, 0x42, 0x93, 0x74, - 0x08, 0xe5, 0x4a, 0x71, 0x58, 0x30, 0xa0, 0x94, 0x8e, 0x35, 0x8d, 0x01, 0xbd, 0x70, 0x9e, 0xaf, 0x37, 0x78, 0x95, - 0xa7, 0xf9, 0x6d, 0x89, 0xbe, 0x83, 0x85, 0xc1, 0x0d, 0x7d, 0x3f, 0x50, 0x95, 0x45, 0x0b, 0xf7, 0xee, 0xe8, 0xdb, - 0x22, 0x5d, 0x00, 0xf7, 0x37, 0x68, 0x6a, 0x84, 0x51, 0xaa, 0x81, 0x43, 0x1c, 0xe8, 0x71, 0x54, 0x56, 0x2e, 0xe3, - 0xad, 0xb6, 0x8c, 0x8c, 0x23, 0x83, 0xef, 0xf0, 0xf2, 0x6b, 0x71, 0xb7, 0x68, 0x05, 0xcf, 0x17, 0xf4, 0xc8, 0x08, - 0x61, 0x01, 0x0b, 0x14, 0xa5, 0x82, 0xfb, 0x77, 0xde, 0xbd, 0x2d, 0x41, 0x5e, 0x8b, 0x68, 0x80, 0x2d, 0x1e, 0xd0, - 0x54, 0x10, 0x3a, 0xa5, 0x33, 0x85, 0x0a, 0x23, 0x82, 0x86, 0x49, 0x41, 0xbf, 0x0c, 0xef, 0x02, 0x40, 0x49, 0xfc, - 0x9a, 0x1e, 0x65, 0xd7, 0x22, 0xe4, 0xb2, 0x08, 0x78, 0xac, 0x5c, 0xce, 0x80, 0x5d, 0xc3, 0xd5, 0x3a, 0x45, 0x17, - 0xbd, 0x30, 0x00, 0x96, 0xe9, 0x1a, 0xba, 0xfc, 0x04, 0x2c, 0x9d, 0x93, 0x89, 0x99, 0xae, 0xf9, 0xd3, 0x6a, 0x1a, - 0x27, 0x7a, 0x01, 0x79, 0x21, 0x7e, 0x47, 0x06, 0x17, 0x7c, 0xc6, 0x7c, 0x1a, 0x13, 0x33, 0xf7, 0x6f, 0xdf, 0x9a, - 0xa0, 0x20, 0xa8, 0x06, 0x33, 0x4c, 0xa8, 0x9d, 0x41, 0x2b, 0xa8, 0x9d, 0x2c, 0xb8, 0xed, 0x2c, 0x4c, 0x73, 0xf4, - 0x68, 0x13, 0x66, 0x67, 0x8f, 0x36, 0x49, 0x36, 0x7c, 0xb4, 0xf1, 0xa4, 0x8e, 0x81, 0x7e, 0xa1, 0x93, 0x82, 0xc1, - 0x08, 0xc1, 0x30, 0xca, 0xae, 0x73, 0x8b, 0x9f, 0x7c, 0xbe, 0xb0, 0xcb, 0x28, 0x5d, 0x43, 0x91, 0xff, 0x90, 0x0b, - 0xf6, 0x57, 0xb1, 0xbf, 0xf4, 0xe2, 0x7b, 0xd2, 0x03, 0x30, 0x55, 0x65, 0x01, 0x43, 0xd7, 0x08, 0xd1, 0x13, 0x00, - 0x08, 0xe7, 0xeb, 0xda, 0x37, 0x32, 0x8d, 0xf1, 0xd9, 0x4a, 0x61, 0x28, 0xf4, 0x75, 0xad, 0x3f, 0x65, 0xf6, 0x94, - 0xa5, 0x9e, 0x1f, 0x50, 0x95, 0x81, 0x88, 0x72, 0x5f, 0x99, 0x5e, 0x52, 0x9c, 0x5e, 0x58, 0xdc, 0x3f, 0x38, 0x19, - 0xba, 0x02, 0x68, 0xdc, 0x38, 0x33, 0x8c, 0x7e, 0x55, 0xbf, 0xa2, 0x94, 0xf7, 0xa7, 0x2e, 0x07, 0x83, 0xe5, 0x08, - 0x61, 0x39, 0x58, 0x38, 0x89, 0xa6, 0xec, 0xa7, 0xb7, 0xaf, 0x65, 0xb8, 0x2d, 0xe0, 0x1c, 0x8d, 0xf8, 0xc6, 0x4c, - 0x90, 0x7e, 0x88, 0x91, 0x76, 0xa0, 0xc0, 0x58, 0x9a, 0xdc, 0x42, 0x71, 0xa6, 0x6b, 0x67, 0x34, 0x76, 0x36, 0xa5, - 0x51, 0x0f, 0x23, 0xac, 0x15, 0x67, 0x27, 0x07, 0x54, 0x9a, 0x6e, 0x3b, 0x2a, 0x04, 0x60, 0x88, 0x61, 0x86, 0x39, - 0x14, 0x20, 0x32, 0xe8, 0xd0, 0xcd, 0x1f, 0x14, 0xf6, 0x12, 0xf9, 0xf3, 0xee, 0x59, 0x91, 0x54, 0xc1, 0x5a, 0xfa, - 0xe9, 0x09, 0xc6, 0xfa, 0x82, 0xfb, 0x1a, 0xbc, 0x83, 0x9c, 0x1c, 0xd0, 0xa7, 0x56, 0x3a, 0x11, 0x79, 0x23, 0xe2, - 0x69, 0xd7, 0xe7, 0x0d, 0x7c, 0xd2, 0x51, 0x81, 0xd0, 0xf2, 0x90, 0xea, 0x65, 0xba, 0xb6, 0xe4, 0xa4, 0x11, 0x77, - 0x43, 0x04, 0x3e, 0x0a, 0x1c, 0x38, 0xbb, 0xba, 0xb6, 0xf4, 0xee, 0x70, 0xe6, 0x22, 0xc7, 0xbb, 0x6b, 0xb9, 0x3c, - 0x2b, 0x3f, 0x6b, 0x49, 0xf1, 0xac, 0x4d, 0xf8, 0xe2, 0x82, 0x01, 0x82, 0x7c, 0x91, 0x2f, 0x50, 0xb0, 0x5b, 0xb3, - 0xb8, 0x0b, 0xb1, 0xb8, 0xd3, 0x86, 0xc5, 0x9d, 0x6e, 0x59, 0xdc, 0x80, 0x2f, 0xa4, 0x26, 0x41, 0x17, 0xa3, 0x51, - 0x99, 0x04, 0x1e, 0x27, 0x34, 0xfa, 0xfc, 0x9c, 0x21, 0x9c, 0xac, 0x24, 0x00, 0xa5, 0xaa, 0x06, 0x58, 0xd5, 0xc1, - 0x45, 0x01, 0x44, 0x75, 0xe2, 0xf2, 0xd4, 0x89, 0x79, 0x43, 0xec, 0xce, 0x56, 0x50, 0x9e, 0x2f, 0xec, 0x52, 0x8a, - 0x4b, 0xde, 0x5a, 0x34, 0xcc, 0x74, 0xb1, 0x65, 0xa6, 0x93, 0xc2, 0xd1, 0xe5, 0xd3, 0xa6, 0x43, 0xa8, 0x4e, 0x0a, - 0xf6, 0x20, 0x28, 0x9a, 0xe2, 0x96, 0x29, 0xee, 0xc3, 0x66, 0x1c, 0xab, 0xec, 0xa8, 0x95, 0x97, 0x24, 0xb7, 0x51, - 0x0c, 0x92, 0x1a, 0x68, 0xe6, 0xd3, 0xb6, 0xd4, 0xd2, 0x0f, 0xb9, 0x13, 0x98, 0xc6, 0xcd, 0x94, 0xe7, 0xab, 0x5b, - 0xaa, 0xdd, 0xed, 0x52, 0x89, 0x95, 0x97, 0xa6, 0x2c, 0x46, 0xa0, 0x7b, 0xe0, 0x2d, 0xfc, 0xbf, 0x64, 0x9b, 0xd5, - 0xe0, 0x90, 0x40, 0xc1, 0xea, 0x88, 0xa1, 0x57, 0x40, 0x5b, 0xc5, 0xe2, 0x22, 0x56, 0x1c, 0xca, 0xc5, 0x12, 0xf0, - 0x3f, 0xe0, 0x71, 0x6d, 0xc5, 0x8a, 0xc9, 0x93, 0x7b, 0x64, 0xd8, 0x2b, 0x6f, 0xfa, 0x0e, 0x04, 0x82, 0xad, 0xb6, - 0x09, 0xca, 0xbd, 0xaa, 0xfb, 0xb8, 0x98, 0x88, 0xbd, 0x49, 0x8e, 0x24, 0x11, 0x4b, 0x72, 0xd5, 0x29, 0xb0, 0xba, - 0xf4, 0xac, 0xd9, 0xd5, 0xa6, 0x9d, 0x1d, 0xcc, 0x7d, 0xa3, 0x82, 0x35, 0x01, 0xb5, 0x05, 0xc3, 0x53, 0xf9, 0xe6, - 0x0a, 0x4c, 0xf7, 0xc8, 0x00, 0x8e, 0xf1, 0x25, 0xc4, 0x41, 0x75, 0xc4, 0x83, 0x76, 0x14, 0xc3, 0xad, 0x75, 0xe9, - 0x5c, 0x65, 0x8f, 0xe7, 0xf8, 0xcb, 0xbd, 0xca, 0x1e, 0x8f, 0xf1, 0x57, 0xfb, 0x0a, 0x23, 0xde, 0xd5, 0x3c, 0xe4, - 0x95, 0x39, 0xeb, 0xa7, 0x85, 0xfd, 0x44, 0x7a, 0x6b, 0x9f, 0xb0, 0x6d, 0xf8, 0x02, 0x3f, 0x7c, 0xb4, 0x49, 0xc0, - 0x52, 0x53, 0x9d, 0x43, 0x68, 0xc7, 0x46, 0x56, 0x9b, 0x3e, 0x6f, 0x48, 0x1f, 0x1b, 0x7f, 0xf2, 0xc5, 0x8f, 0xbb, - 0x24, 0xca, 0xef, 0x94, 0x22, 0x1b, 0xe2, 0x7a, 0xec, 0x87, 0x5e, 0x7c, 0x7f, 0x4d, 0xcf, 0x8b, 0x96, 0xa0, 0xdd, - 0x25, 0x7b, 0x85, 0xc8, 0xcb, 0xa2, 0x98, 0x2c, 0x55, 0x18, 0xc3, 0xf7, 0xfc, 0xa2, 0x1f, 0xfe, 0x3d, 0x56, 0xc8, - 0xb6, 0xc2, 0x03, 0x94, 0x2f, 0x48, 0xa1, 0xa3, 0xeb, 0x47, 0x9b, 0x16, 0xab, 0x36, 0x53, 0x9a, 0x6d, 0x89, 0x2e, - 0x84, 0xe5, 0xc1, 0xc7, 0xec, 0x72, 0xea, 0xf7, 0x51, 0x0e, 0x36, 0x8e, 0xee, 0xac, 0x47, 0x9b, 0xf4, 0x4c, 0x5f, - 0x7a, 0xf1, 0x07, 0x36, 0xb5, 0x26, 0x7e, 0x3c, 0x09, 0x98, 0xde, 0xd7, 0xc7, 0x81, 0x17, 0x7e, 0xe0, 0x9f, 0x56, - 0xb4, 0x4e, 0x51, 0xb2, 0xbd, 0xf3, 0xed, 0x2b, 0x60, 0x42, 0x2c, 0x3b, 0x24, 0x56, 0x6b, 0xa0, 0xa0, 0x3d, 0x97, - 0x0c, 0xaf, 0x9c, 0x50, 0xcc, 0x4b, 0x99, 0xa0, 0x98, 0x09, 0xc2, 0x76, 0xb0, 0x74, 0x35, 0x75, 0x5c, 0x2f, 0xdd, - 0x54, 0xa7, 0x4a, 0xcc, 0x4a, 0x19, 0xaa, 0xf1, 0x1a, 0x5b, 0xf8, 0xfd, 0xdd, 0x51, 0x10, 0xed, 0xfd, 0xbb, 0x93, - 0xad, 0x7c, 0xde, 0x0c, 0x21, 0xd5, 0x22, 0x0b, 0x8b, 0x4f, 0xe8, 0x9c, 0x13, 0x98, 0xcd, 0x5d, 0xab, 0x95, 0xbd, - 0x24, 0x59, 0x2f, 0xd9, 0x94, 0x24, 0x8a, 0x67, 0xf9, 0xa0, 0x8a, 0x2f, 0x0b, 0x75, 0x60, 0xbf, 0xac, 0xdb, 0xc7, - 0x87, 0xcf, 0x41, 0xd3, 0x01, 0x08, 0xca, 0x68, 0x36, 0xd3, 0xf3, 0x37, 0xfe, 0x8e, 0x6a, 0xee, 0xe1, 0x2f, 0xeb, - 0x57, 0x2f, 0x9d, 0x57, 0xb2, 0x72, 0x08, 0x84, 0xb1, 0x10, 0xdb, 0x72, 0xba, 0x58, 0x19, 0xaf, 0x98, 0xd1, 0xcc, - 0x0b, 0x9b, 0xa7, 0x73, 0x59, 0xd8, 0xe2, 0x2b, 0xc6, 0xa6, 0x40, 0x70, 0x9b, 0x95, 0xd4, 0xeb, 0x80, 0xdd, 0x30, - 0x29, 0x12, 0xae, 0x76, 0x56, 0x53, 0x03, 0x7d, 0xd6, 0x71, 0x51, 0x33, 0xa7, 0xea, 0x94, 0x29, 0x8d, 0x70, 0x0e, - 0x7c, 0xe6, 0xea, 0x11, 0x2b, 0x1d, 0xa9, 0x91, 0xa9, 0x2b, 0x03, 0x68, 0x1c, 0xd9, 0x59, 0x43, 0x7a, 0x1f, 0x03, - 0x56, 0xd7, 0x8f, 0xcd, 0x74, 0x8d, 0x3e, 0xf8, 0xf8, 0xe6, 0x70, 0x0a, 0xe0, 0xe4, 0xb5, 0x72, 0x76, 0x48, 0x13, - 0xc4, 0xea, 0x98, 0x64, 0x3a, 0x71, 0x5f, 0x84, 0x96, 0x24, 0xaa, 0x0b, 0x0b, 0x3e, 0x54, 0xed, 0xda, 0x68, 0xc5, - 0x99, 0x8f, 0x31, 0x10, 0x6c, 0xc8, 0x92, 0xa4, 0x11, 0x60, 0x72, 0xd1, 0x4d, 0x3d, 0x2f, 0x5d, 0x84, 0x47, 0x9e, - 0x6e, 0x3a, 0x26, 0x90, 0x04, 0x38, 0xc1, 0x72, 0x5f, 0x78, 0xbd, 0x5c, 0x2f, 0xb9, 0x9e, 0x4b, 0x3c, 0x1f, 0xeb, - 0x5c, 0x07, 0xa1, 0x29, 0xff, 0x56, 0xe7, 0x83, 0x2a, 0x5c, 0xd3, 0xb5, 0x43, 0x6b, 0x15, 0x50, 0x6f, 0x85, 0x5d, - 0x84, 0x0d, 0x88, 0x31, 0x95, 0xf0, 0x2b, 0x9b, 0xcd, 0xd8, 0x24, 0x4d, 0x0c, 0xc1, 0x3c, 0x92, 0x5e, 0x67, 0xc1, - 0xda, 0xe8, 0xc1, 0x50, 0xff, 0x01, 0x6c, 0xef, 0x85, 0x73, 0x26, 0x3e, 0x20, 0xf1, 0x66, 0xaa, 0x07, 0x13, 0xb5, - 0x58, 0x04, 0x11, 0xef, 0x05, 0x82, 0x8a, 0xd7, 0xa4, 0xe3, 0xd0, 0xf8, 0xfd, 0x93, 0xef, 0x8b, 0x48, 0x6a, 0xc3, - 0x6c, 0x47, 0x45, 0xdb, 0x8e, 0xef, 0xc6, 0x7d, 0xd5, 0x75, 0x9d, 0x4c, 0x37, 0xc1, 0xe6, 0xeb, 0xc3, 0xbe, 0x87, - 0x1e, 0x6b, 0x75, 0xa0, 0xd6, 0x3a, 0xfc, 0x94, 0x7a, 0x6d, 0xf7, 0x99, 0xab, 0x9b, 0xa4, 0x6a, 0xa7, 0xe0, 0xb6, - 0x49, 0x74, 0xc3, 0xe2, 0xcf, 0x9e, 0x4a, 0xb1, 0xf1, 0xfd, 0xc6, 0x73, 0xe4, 0x3a, 0x80, 0x84, 0xd3, 0x68, 0xf5, - 0x09, 0x53, 0xe8, 0xe8, 0xa6, 0x3e, 0x09, 0xa2, 0x84, 0xa9, 0x73, 0x20, 0x26, 0xc8, 0x67, 0x4e, 0xe2, 0xc7, 0xb7, - 0x2f, 0xdf, 0xbd, 0xd3, 0x4d, 0x8c, 0x20, 0x9a, 0xa8, 0xad, 0xf3, 0x0d, 0xb5, 0x03, 0xfb, 0xd7, 0xee, 0x3b, 0xba, - 0x61, 0xe8, 0x51, 0x5b, 0xdc, 0x73, 0x94, 0x56, 0xd9, 0x72, 0xfc, 0xe6, 0xe1, 0x3d, 0xd3, 0x4b, 0x74, 0xaf, 0x79, - 0xd5, 0xe0, 0x86, 0xed, 0xd7, 0x5b, 0x21, 0x65, 0xe9, 0x87, 0xd7, 0x35, 0xa9, 0xde, 0x5d, 0x4d, 0x2a, 0x3c, 0xe5, - 0x2a, 0xb8, 0x6a, 0x1d, 0x2d, 0x15, 0xd2, 0x00, 0x02, 0x40, 0xef, 0x02, 0x97, 0xf2, 0x9e, 0xfa, 0x8c, 0x41, 0x73, - 0x0f, 0xf0, 0xe5, 0x51, 0xd7, 0x24, 0xf3, 0x47, 0x90, 0x84, 0xed, 0x24, 0x00, 0x45, 0x41, 0xa6, 0x4a, 0xe5, 0x8a, - 0x64, 0x23, 0x57, 0xf3, 0x1d, 0x96, 0x28, 0x74, 0xaa, 0x46, 0x02, 0x10, 0x8e, 0xdf, 0x57, 0xde, 0x14, 0xb4, 0xef, - 0xac, 0x71, 0x94, 0xa6, 0xd1, 0xb2, 0xef, 0x3a, 0xab, 0x3b, 0x5d, 0x1b, 0x08, 0xc6, 0x03, 0x57, 0x0e, 0xec, 0xff, - 0xf6, 0xef, 0x12, 0xca, 0xa5, 0xf4, 0xeb, 0x94, 0x2d, 0x57, 0x2c, 0xf6, 0xd2, 0x75, 0xcc, 0x32, 0xed, 0xb7, 0xff, - 0x7b, 0x5e, 0x7a, 0x64, 0x0f, 0xd4, 0x3a, 0x44, 0x5e, 0xab, 0x55, 0xae, 0x83, 0xe8, 0xf6, 0x41, 0x6e, 0x06, 0xb0, - 0xa3, 0xf0, 0xc2, 0x9f, 0x2f, 0x64, 0xe9, 0xb3, 0x74, 0xcb, 0xdc, 0xc4, 0xe8, 0x89, 0xe9, 0xae, 0x9d, 0x47, 0xb7, - 0xfd, 0xdf, 0xfe, 0x2d, 0x99, 0x27, 0x3b, 0x77, 0x5d, 0xfd, 0x40, 0x8b, 0x2b, 0x5a, 0x5f, 0xa6, 0xb2, 0xc4, 0x90, - 0x5f, 0x59, 0xe0, 0x4a, 0x22, 0xed, 0xca, 0xaa, 0x84, 0x6b, 0xcb, 0x9c, 0xfe, 0xea, 0xcf, 0x17, 0x9f, 0x3b, 0x29, - 0x00, 0xe8, 0xce, 0x59, 0x41, 0xa1, 0x2f, 0x30, 0xad, 0x51, 0x7f, 0xff, 0x05, 0xfb, 0xcc, 0x79, 0xed, 0x9a, 0xd2, - 0x97, 0x98, 0x0d, 0xe7, 0xa2, 0x3e, 0x1f, 0x8d, 0x64, 0x04, 0x3d, 0xb5, 0x3e, 0x18, 0x32, 0x9c, 0x55, 0x52, 0xf8, - 0x55, 0xdf, 0x77, 0x0c, 0xf2, 0x30, 0xb0, 0x07, 0x40, 0x50, 0x25, 0xaf, 0x06, 0x1c, 0xcd, 0xf8, 0x9a, 0x34, 0xeb, - 0x2b, 0x7d, 0x57, 0x90, 0x35, 0xa4, 0x62, 0xf4, 0x35, 0x29, 0x9a, 0x33, 0xeb, 0x87, 0x73, 0x1b, 0x7b, 0x2b, 0x62, - 0xd8, 0x6b, 0x28, 0x8f, 0x00, 0x06, 0x88, 0x78, 0xd1, 0x62, 0x70, 0x97, 0x37, 0x4d, 0x0a, 0x71, 0x3f, 0xee, 0x56, - 0x88, 0xbb, 0xd8, 0x4b, 0x21, 0xee, 0xc7, 0x2f, 0xae, 0x10, 0xf7, 0x46, 0x55, 0x88, 0x83, 0xb5, 0x7c, 0xc9, 0xf6, - 0xd2, 0x52, 0x13, 0xaa, 0x26, 0xd1, 0x6d, 0x32, 0x74, 0x39, 0x1d, 0x9e, 0x4c, 0x16, 0x0c, 0x18, 0x1b, 0x1c, 0xea, - 0x41, 0x34, 0x07, 0x8d, 0xb5, 0x3f, 0x5e, 0xb7, 0x2c, 0x88, 0xe6, 0xaa, 0x66, 0x59, 0xc8, 0xdd, 0xdb, 0xe6, 0x2e, - 0xab, 0x48, 0x9b, 0xcb, 0x31, 0x85, 0x83, 0x2b, 0xeb, 0xd0, 0x50, 0x42, 0x78, 0x4b, 0x55, 0xbd, 0xb6, 0xd0, 0xf7, - 0xea, 0xa3, 0xaa, 0x98, 0xac, 0xd8, 0x7e, 0x2a, 0x1c, 0x79, 0xa8, 0x2d, 0x48, 0x95, 0x68, 0x72, 0x8a, 0xb1, 0xd1, - 0x7f, 0xb9, 0x73, 0xbf, 0xbb, 0x74, 0x07, 0x1d, 0x17, 0x2c, 0xd1, 0xe1, 0x59, 0x8c, 0x09, 0xce, 0xa0, 0xd3, 0x81, - 0x84, 0x5b, 0x25, 0xa1, 0x0d, 0x09, 0xbe, 0x92, 0xd0, 0x85, 0x84, 0x89, 0x92, 0x70, 0x04, 0x09, 0x53, 0x25, 0xe1, - 0x18, 0x12, 0x6e, 0xf4, 0xec, 0x32, 0x94, 0xc3, 0x3d, 0x36, 0xae, 0x4c, 0x7a, 0x09, 0x89, 0xb4, 0x63, 0xd3, 0x05, - 0x15, 0x31, 0x6f, 0xde, 0x8f, 0x4c, 0x62, 0x89, 0xf6, 0x63, 0xf3, 0x76, 0xc1, 0xc8, 0x2b, 0xf6, 0x0b, 0xbc, 0x28, - 0xed, 0x34, 0x02, 0x25, 0x71, 0xe1, 0x6d, 0x42, 0xc0, 0x41, 0xd3, 0x0d, 0xe0, 0x72, 0x0d, 0xe4, 0xca, 0x09, 0x8f, - 0x1d, 0xca, 0x5a, 0xe6, 0x79, 0xd4, 0x9d, 0x25, 0xb7, 0x40, 0xae, 0x26, 0xd3, 0x52, 0x59, 0xa9, 0x5f, 0x42, 0x59, - 0xe2, 0x05, 0x1b, 0xaf, 0xe7, 0xda, 0x79, 0x34, 0xdf, 0xa9, 0xf7, 0xa0, 0x66, 0xc1, 0x28, 0x75, 0x92, 0x19, 0x59, - 0x62, 0x5b, 0xf2, 0xbe, 0xe8, 0x33, 0x2b, 0x96, 0x4f, 0x61, 0x6c, 0x5a, 0x4a, 0x18, 0x07, 0xfa, 0x01, 0x18, 0x29, - 0x8a, 0x07, 0xe7, 0x00, 0x67, 0xe5, 0xfb, 0xc2, 0x53, 0xc6, 0x73, 0xfa, 0x3d, 0x4b, 0x12, 0x6f, 0x2e, 0xca, 0x57, - 0xc7, 0x09, 0x9a, 0x46, 0xf2, 0xd1, 0x88, 0x00, 0x04, 0xf6, 0xa3, 0x5f, 0x51, 0x28, 0x89, 0xa3, 0x5b, 0x0d, 0x54, - 0x96, 0x60, 0x43, 0xe5, 0xca, 0x15, 0xbe, 0x0d, 0x4b, 0x58, 0x54, 0x83, 0x80, 0xc3, 0x7f, 0xc3, 0x82, 0x72, 0x62, - 0xea, 0xcd, 0xcb, 0x49, 0xb4, 0x0f, 0x32, 0x75, 0x6c, 0x52, 0x0b, 0xa1, 0x90, 0xf8, 0x39, 0x62, 0xf5, 0x20, 0x9a, - 0xff, 0xa1, 0x32, 0xf5, 0x2d, 0xba, 0x10, 0xef, 0x42, 0xd3, 0x4f, 0x47, 0x36, 0xc2, 0x58, 0xb3, 0x01, 0x84, 0xfd, - 0x30, 0x5d, 0x58, 0x68, 0x47, 0xd7, 0x6a, 0x87, 0x86, 0x69, 0xe3, 0xda, 0x6e, 0xca, 0xd6, 0xc3, 0xfd, 0x78, 0x3e, - 0xf6, 0x5a, 0x6e, 0xfb, 0xd8, 0x14, 0x7f, 0x6c, 0xa7, 0x6b, 0x64, 0xd8, 0x82, 0x36, 0xf5, 0x6f, 0x36, 0xb3, 0x28, - 0x4c, 0xad, 0x99, 0xb7, 0xf4, 0x83, 0xfb, 0xfe, 0x32, 0x0a, 0xa3, 0x64, 0xe5, 0x4d, 0xd8, 0x20, 0xe7, 0x02, 0x0c, - 0xd0, 0x2f, 0x05, 0x37, 0x8d, 0x74, 0xed, 0x76, 0xcc, 0x96, 0x54, 0x5b, 0xba, 0x9d, 0x98, 0x05, 0xec, 0x2e, 0xe3, - 0xdd, 0x17, 0x0a, 0x53, 0x51, 0xdc, 0x72, 0x54, 0x03, 0xc8, 0x68, 0xee, 0xd3, 0x02, 0x3c, 0x39, 0x0d, 0x38, 0x2d, - 0xda, 0xb7, 0xdb, 0xdd, 0x98, 0x2d, 0x35, 0xbb, 0xdb, 0xd8, 0x78, 0x1c, 0xdd, 0x9e, 0xc2, 0x68, 0xb1, 0xb2, 0x95, - 0xb0, 0x60, 0x86, 0x39, 0x16, 0x9a, 0xd1, 0x88, 0x76, 0x2c, 0xe4, 0x1e, 0x40, 0x6b, 0x6c, 0x39, 0x80, 0xec, 0x7e, - 0x5b, 0x73, 0x06, 0x4b, 0x3f, 0xb4, 0x68, 0x3a, 0xc7, 0xce, 0x4a, 0x69, 0x4b, 0x85, 0x9f, 0xb1, 0xc1, 0xe2, 0xae, - 0xe6, 0x0c, 0xe0, 0x85, 0x39, 0x0b, 0xa2, 0xdb, 0xfe, 0xc2, 0x9f, 0x4e, 0x59, 0x38, 0xc0, 0x31, 0xcb, 0x44, 0x16, - 0x04, 0xfe, 0x2a, 0xf1, 0x93, 0xc1, 0xd2, 0xbb, 0xe3, 0xad, 0x1e, 0x36, 0xb5, 0xda, 0xe1, 0xad, 0x76, 0xf6, 0x6e, - 0x55, 0x69, 0x06, 0x4c, 0x76, 0xa8, 0x1d, 0x3e, 0xb4, 0xae, 0xe6, 0x94, 0xe6, 0xb9, 0x77, 0xab, 0xab, 0x98, 0x6d, - 0x96, 0x5e, 0x3c, 0xf7, 0xc3, 0xbe, 0x93, 0xd9, 0x37, 0x1b, 0xda, 0x18, 0x0f, 0x7b, 0xbd, 0x5e, 0x66, 0x4f, 0xc5, - 0x97, 0x33, 0x9d, 0x66, 0xf6, 0x44, 0x7c, 0xcd, 0x66, 0x8e, 0x33, 0x9b, 0x65, 0xb6, 0x2f, 0x12, 0x3a, 0xed, 0xc9, - 0xb4, 0xd3, 0xce, 0xec, 0x5b, 0xa5, 0x44, 0x66, 0x33, 0xfe, 0x15, 0xb3, 0xe9, 0x00, 0x37, 0x12, 0xe9, 0xd0, 0xf6, - 0x8f, 0x1d, 0x27, 0x43, 0x0c, 0x70, 0x59, 0xc0, 0x4d, 0xc8, 0xa0, 0xba, 0xda, 0xec, 0x5d, 0x52, 0xcb, 0xbb, 0x9b, - 0x4c, 0x6a, 0xcb, 0x4d, 0xbd, 0xf8, 0xc3, 0x95, 0xa6, 0xcc, 0xc2, 0xf3, 0xa8, 0xd8, 0x46, 0x80, 0xc1, 0xba, 0xeb, - 0x83, 0x7f, 0xb2, 0xc1, 0x38, 0x8a, 0xe1, 0xcc, 0xc6, 0xde, 0xd4, 0x5f, 0x27, 0x7d, 0xb7, 0xbd, 0xba, 0x13, 0x49, - 0x7c, 0xaf, 0xe7, 0x09, 0x78, 0xf6, 0xfa, 0x49, 0x14, 0xf8, 0x53, 0x91, 0xd4, 0x74, 0x96, 0xdc, 0xb6, 0x31, 0x40, - 0xeb, 0x7c, 0x1f, 0x7d, 0x4c, 0x78, 0x41, 0xa0, 0xd9, 0x9d, 0x44, 0x63, 0x5e, 0x82, 0x4c, 0x71, 0xcd, 0x49, 0x08, - 0x2e, 0x68, 0x89, 0xef, 0x1e, 0xae, 0xee, 0xe4, 0x9e, 0x77, 0x8f, 0x56, 0x77, 0xd9, 0x37, 0x4b, 0x36, 0xf5, 0x3d, - 0xad, 0x95, 0xef, 0x26, 0xd7, 0x01, 0xc6, 0xb9, 0xb1, 0x69, 0xd8, 0xa6, 0xe2, 0x58, 0x80, 0x1f, 0xc7, 0x07, 0xfe, - 0x72, 0x15, 0xc5, 0xa9, 0x17, 0xa6, 0x59, 0x36, 0xba, 0xca, 0xb2, 0xc1, 0x85, 0xdf, 0xba, 0xfc, 0x47, 0x8b, 0xee, - 0x69, 0x12, 0x34, 0x65, 0xc6, 0x95, 0xf9, 0x92, 0xa9, 0x8a, 0x2e, 0x70, 0x8d, 0xa1, 0x92, 0x8b, 0x5a, 0x98, 0x6e, - 0xc9, 0x6a, 0x61, 0x02, 0xb2, 0x2c, 0x4e, 0x8a, 0x33, 0xc5, 0x22, 0x78, 0x03, 0x41, 0x81, 0x97, 0x6c, 0x78, 0xa1, - 0x28, 0xcd, 0x00, 0xb1, 0x82, 0x85, 0xc9, 0x88, 0xe2, 0x51, 0x13, 0xcd, 0xf8, 0xed, 0x6e, 0x9a, 0xf1, 0xe7, 0x74, - 0x1f, 0x9a, 0xf1, 0xdb, 0x2f, 0x4e, 0x33, 0x3e, 0xaa, 0x1a, 0x51, 0xbc, 0x8e, 0x86, 0xba, 0x14, 0x8b, 0xc0, 0xd5, - 0x14, 0x93, 0x7b, 0xa2, 0xd7, 0xbf, 0xdb, 0xe6, 0x41, 0xb4, 0x46, 0x01, 0xf7, 0xe8, 0xe6, 0x06, 0x26, 0xf2, 0x9b, - 0x70, 0xf8, 0xf7, 0x58, 0xfd, 0x9e, 0xcd, 0x86, 0x2f, 0x22, 0x25, 0x41, 0x7e, 0x71, 0x8d, 0x91, 0x82, 0x2b, 0x09, - 0xca, 0x11, 0xaa, 0xa3, 0x18, 0x6c, 0x03, 0x2c, 0xd1, 0x49, 0x55, 0x7a, 0x2a, 0x55, 0xe6, 0x06, 0xc5, 0x21, 0xb4, - 0xa4, 0x9e, 0xaa, 0xb0, 0x37, 0xaa, 0xf0, 0x3f, 0xe7, 0x2c, 0xe5, 0x06, 0xc2, 0xdf, 0xdd, 0xbf, 0x9e, 0xb6, 0x5e, - 0x47, 0x46, 0xe6, 0x27, 0x6f, 0xca, 0xd6, 0x3e, 0x5c, 0x60, 0x35, 0x54, 0xa7, 0x93, 0x71, 0xb5, 0x37, 0x35, 0x9a, - 0x36, 0x64, 0x53, 0xf5, 0xb3, 0xc2, 0x4c, 0xfb, 0x6a, 0x45, 0x1e, 0xd5, 0xab, 0x72, 0x19, 0x73, 0x53, 0x8b, 0x0d, - 0xa7, 0x00, 0x31, 0x50, 0x19, 0x1a, 0x49, 0x4f, 0xa9, 0xba, 0x3f, 0xcd, 0x32, 0x63, 0x20, 0x00, 0xa1, 0x5c, 0xb4, - 0x6c, 0x17, 0x11, 0x97, 0xe4, 0xef, 0x31, 0x2e, 0xd6, 0x24, 0x99, 0xe5, 0x6b, 0xd0, 0x02, 0xe0, 0x12, 0x4e, 0x0e, - 0x33, 0x5d, 0x23, 0xf0, 0x91, 0x76, 0x88, 0x32, 0x21, 0x10, 0x5b, 0x4b, 0xf8, 0x8b, 0x2c, 0x91, 0x50, 0x55, 0x3c, - 0x25, 0xe0, 0xa0, 0x1a, 0x03, 0xb8, 0x34, 0x10, 0xcc, 0x1a, 0x42, 0x3b, 0xbc, 0x0c, 0x7e, 0x64, 0xba, 0xa4, 0xfd, - 0x70, 0xfb, 0x9d, 0x9e, 0x1c, 0x40, 0x85, 0xd3, 0x12, 0x23, 0x66, 0x87, 0x5a, 0x25, 0x90, 0x12, 0xc9, 0xad, 0x69, - 0x27, 0xb7, 0xda, 0x93, 0x8d, 0x70, 0x07, 0x92, 0x7a, 0x2b, 0x0b, 0x5e, 0xff, 0x88, 0x7b, 0x39, 0xc6, 0x53, 0x3c, - 0x8f, 0x0c, 0xd6, 0x09, 0xe0, 0x46, 0x7c, 0x88, 0x22, 0xfe, 0x19, 0x4c, 0xd6, 0x71, 0x12, 0xc5, 0xfd, 0x55, 0xe4, - 0x87, 0x29, 0x8b, 0x33, 0x04, 0xd5, 0x25, 0xc2, 0x47, 0x80, 0xe7, 0x6a, 0x13, 0xad, 0xbc, 0x89, 0x9f, 0xde, 0xf7, - 0x1d, 0x4e, 0x52, 0x38, 0x03, 0x4e, 0x1d, 0x38, 0xb5, 0xe5, 0xfb, 0x1c, 0x9a, 0x4f, 0x91, 0xf0, 0x8b, 0xab, 0xe4, - 0x8c, 0xba, 0xcd, 0x07, 0x4a, 0x2e, 0x39, 0x44, 0x01, 0xf2, 0xc3, 0x8b, 0xad, 0x39, 0x60, 0x79, 0x58, 0x6a, 0x67, - 0xca, 0xe6, 0x26, 0x62, 0x6d, 0x10, 0x26, 0x88, 0x3f, 0x76, 0xd7, 0xd0, 0x9c, 0xfa, 0x64, 0xa0, 0x78, 0x8c, 0x7d, - 0x46, 0xd6, 0xf7, 0x20, 0x7c, 0x98, 0xb9, 0x4f, 0xc9, 0x31, 0x9b, 0x45, 0x31, 0x23, 0xe7, 0xb9, 0x6e, 0x6f, 0x75, - 0xb7, 0x7f, 0xf3, 0xdb, 0xa7, 0x5f, 0xdf, 0x4e, 0x18, 0xa5, 0x2d, 0xd1, 0x98, 0xb1, 0xa3, 0xb5, 0xea, 0x7d, 0x06, - 0xa4, 0x21, 0x41, 0x7e, 0x42, 0x7e, 0xca, 0xfa, 0xba, 0x3e, 0xa8, 0xf5, 0x51, 0xb6, 0x8a, 0xf8, 0x9d, 0x17, 0xb3, - 0xc0, 0x4b, 0xfd, 0x1b, 0x41, 0x33, 0x76, 0x8e, 0x56, 0x77, 0x62, 0x8d, 0xf1, 0xc2, 0xfb, 0x84, 0x45, 0x2a, 0x0d, - 0x45, 0x2c, 0x52, 0x39, 0x19, 0x17, 0x69, 0x50, 0x99, 0x8d, 0x70, 0xdb, 0x51, 0xba, 0xe9, 0xbb, 0xab, 0x3b, 0xf5, - 0x8a, 0xce, 0xab, 0xc9, 0x9b, 0xba, 0xec, 0x6f, 0x6d, 0xe9, 0x4f, 0xa7, 0x01, 0xcb, 0x0a, 0x0b, 0x5d, 0x5c, 0x4b, - 0x05, 0x38, 0x12, 0x0e, 0xde, 0x38, 0x89, 0x82, 0x75, 0xca, 0xea, 0xc1, 0x45, 0xc0, 0x69, 0x3b, 0x39, 0x70, 0xf0, - 0x77, 0x71, 0xac, 0x5d, 0x20, 0xb7, 0x61, 0x9b, 0x38, 0x03, 0x70, 0xaf, 0x6c, 0x75, 0x8a, 0x43, 0x87, 0x2c, 0x39, - 0x68, 0xb3, 0x66, 0x22, 0x26, 0x5c, 0x4b, 0x84, 0xbd, 0x35, 0xdb, 0xe5, 0x69, 0xd2, 0xc5, 0xac, 0x4c, 0xca, 0x8a, - 0x93, 0xf9, 0x63, 0xce, 0xd8, 0xb3, 0xfa, 0x33, 0xf6, 0x4c, 0x9c, 0xb1, 0xed, 0x3b, 0xf3, 0xe1, 0xcc, 0x85, 0xff, - 0x06, 0xf9, 0x84, 0xfa, 0x8e, 0xd6, 0x59, 0xdd, 0x69, 0xee, 0xea, 0x4e, 0xb3, 0xda, 0xab, 0x3b, 0x0d, 0x9b, 0x46, - 0x25, 0x16, 0xd3, 0x6e, 0x1b, 0xa6, 0xa3, 0x41, 0x22, 0xfc, 0x71, 0x0a, 0x59, 0xee, 0x21, 0xe4, 0x41, 0xad, 0x6e, - 0x35, 0xaf, 0xbd, 0xfd, 0xa8, 0xd3, 0x59, 0x12, 0x48, 0xdb, 0xb0, 0x53, 0x6f, 0x3c, 0x66, 0xd3, 0xfe, 0x2c, 0x9a, - 0xac, 0x93, 0x7f, 0xf1, 0xf1, 0x73, 0x20, 0x6e, 0x45, 0x04, 0xa5, 0x76, 0x44, 0x55, 0x90, 0xee, 0xdc, 0x30, 0xd1, - 0xc2, 0x46, 0xae, 0x53, 0x9f, 0x7c, 0x41, 0xb7, 0xed, 0xc3, 0x9a, 0x4d, 0x5e, 0x0f, 0xe8, 0x3f, 0x6c, 0x95, 0x9a, - 0x51, 0xcc, 0x67, 0x80, 0x65, 0x2b, 0x38, 0x3e, 0x1d, 0x1a, 0x7c, 0x35, 0x9d, 0x5e, 0xfd, 0x70, 0x2f, 0x45, 0x4f, - 0x57, 0xe2, 0x52, 0xe1, 0xf7, 0x16, 0xb7, 0xa6, 0xd9, 0xde, 0x6a, 0xd3, 0x1e, 0xa9, 0xb4, 0xba, 0xe5, 0x42, 0xc8, - 0xcb, 0xee, 0x89, 0xe5, 0x1f, 0x3e, 0x3b, 0x84, 0xff, 0x88, 0xaa, 0xff, 0x39, 0xad, 0x23, 0xd4, 0x5f, 0x17, 0xd5, - 0xd7, 0x89, 0x54, 0x42, 0x42, 0x7c, 0xff, 0xf2, 0xb3, 0xd9, 0xa7, 0x55, 0xd8, 0xbb, 0x34, 0xe9, 0x7f, 0x95, 0x4b, - 0x7f, 0x17, 0x45, 0x10, 0xa7, 0xb4, 0x5a, 0x5c, 0x80, 0x87, 0x34, 0xf4, 0xd3, 0x21, 0x54, 0x12, 0xef, 0x08, 0x52, - 0x3d, 0xd0, 0xb1, 0x0e, 0x3d, 0x25, 0x5e, 0x36, 0x3d, 0x25, 0x5e, 0xec, 0x7e, 0x4a, 0xfc, 0x6d, 0xaf, 0xa7, 0xc4, - 0x8b, 0x2f, 0xfe, 0x94, 0x78, 0x59, 0x7d, 0x4a, 0x5c, 0x44, 0x42, 0xe9, 0xd7, 0x7c, 0xbd, 0xe6, 0x3f, 0xdf, 0x93, - 0x24, 0xf1, 0x3c, 0x1a, 0x76, 0x1d, 0xf2, 0xef, 0x7c, 0xf1, 0xbb, 0x1f, 0x16, 0xb8, 0x11, 0xdf, 0xa2, 0x0e, 0x5c, - 0xfe, 0xb4, 0xe0, 0x98, 0x1d, 0xfb, 0x51, 0x92, 0x83, 0x28, 0x9c, 0xff, 0x08, 0x92, 0x64, 0x60, 0x07, 0xc6, 0x4a, - 0x86, 0x9f, 0xfc, 0x18, 0xad, 0xd6, 0xab, 0xd7, 0xd0, 0xd6, 0x7b, 0x3f, 0xf1, 0xc7, 0x01, 0x93, 0x66, 0xd7, 0xa4, - 0xb3, 0xc7, 0x79, 0xe2, 0xa0, 0x26, 0x2b, 0x7e, 0x7a, 0x77, 0xe2, 0x27, 0x2a, 0xd2, 0xf2, 0xdf, 0xa4, 0x0c, 0xa8, - 0xd7, 0x3f, 0x44, 0xc0, 0x41, 0x51, 0x69, 0xd0, 0x9f, 0xfe, 0x18, 0xb9, 0x88, 0x8c, 0x9a, 0x59, 0x0a, 0x25, 0x8d, - 0xc6, 0x76, 0x58, 0xe5, 0x51, 0xb3, 0x36, 0x4c, 0xe9, 0x6f, 0xac, 0xca, 0x86, 0x5f, 0x46, 0xeb, 0x84, 0x4d, 0xa3, - 0xdb, 0x50, 0x37, 0x43, 0x69, 0x19, 0x01, 0x62, 0x59, 0x59, 0x07, 0x23, 0x65, 0xbe, 0x43, 0x42, 0x39, 0x8a, 0x5b, - 0x3a, 0x04, 0x6a, 0x5d, 0xaf, 0x2c, 0x92, 0x8f, 0x5b, 0x38, 0x45, 0x5d, 0x86, 0x74, 0x7a, 0xd0, 0x6a, 0x45, 0xc3, - 0x4f, 0xab, 0x29, 0xf4, 0x4b, 0x22, 0x9b, 0x73, 0x85, 0x93, 0x56, 0x28, 0x98, 0x8b, 0xc2, 0xe9, 0x47, 0xcd, 0xc2, - 0xf1, 0x1c, 0xb2, 0xb7, 0xcd, 0x73, 0xc1, 0x65, 0x4a, 0xb6, 0xe6, 0xeb, 0xc1, 0x5d, 0x60, 0xd0, 0xe7, 0x73, 0x05, - 0x8c, 0x6f, 0x6e, 0x58, 0x1c, 0x78, 0xf7, 0x2d, 0x23, 0x8b, 0xc2, 0xef, 0x01, 0x00, 0x2f, 0xa2, 0xdb, 0x50, 0x2d, - 0x80, 0x91, 0x69, 0x6a, 0xf6, 0x52, 0xad, 0xb3, 0x16, 0xb0, 0xb6, 0x51, 0x46, 0x00, 0x31, 0x81, 0xe7, 0xec, 0xef, - 0x26, 0xfd, 0xfb, 0x0f, 0x23, 0x33, 0xcf, 0x23, 0xd9, 0xd1, 0x4f, 0xab, 0x3d, 0xba, 0x79, 0xfc, 0xf8, 0x41, 0xf3, - 0xb4, 0x8b, 0xb1, 0xe8, 0x6b, 0x6a, 0x1b, 0x8d, 0xa7, 0x00, 0x46, 0x71, 0x11, 0xad, 0x27, 0x0b, 0xd4, 0xce, 0xfd, - 0x72, 0xf3, 0x4d, 0xa1, 0x4d, 0x0c, 0xc9, 0x2a, 0xa7, 0x5e, 0x4a, 0xca, 0xa1, 0x80, 0xfd, 0xbf, 0x04, 0x6f, 0xa3, - 0xff, 0x41, 0x30, 0x54, 0x77, 0x0d, 0x7f, 0xc5, 0xfb, 0x9f, 0xb6, 0x79, 0x07, 0x10, 0x39, 0x94, 0xfb, 0xf1, 0x10, - 0xc2, 0xb5, 0x7a, 0x24, 0x93, 0x95, 0x81, 0xa6, 0xfa, 0xcc, 0x6b, 0x72, 0x07, 0x28, 0x7a, 0x61, 0x36, 0x3d, 0xd3, - 0xb9, 0x75, 0x84, 0xc9, 0x38, 0xb6, 0x2a, 0x21, 0x19, 0xae, 0xa7, 0xc1, 0x10, 0x7d, 0x95, 0xf3, 0x96, 0x7e, 0x68, - 0xa2, 0xcb, 0xfb, 0x6a, 0x8e, 0x77, 0x07, 0x4e, 0x9f, 0x01, 0xb9, 0x95, 0xb3, 0x20, 0xd1, 0x54, 0x8d, 0xfd, 0x20, - 0xae, 0x95, 0x5e, 0x0b, 0x09, 0x21, 0xc5, 0x1b, 0x7d, 0xa5, 0x69, 0x9a, 0x26, 0x9f, 0x11, 0x9a, 0x7c, 0x47, 0x60, - 0x3a, 0x3e, 0x07, 0x40, 0x5a, 0x92, 0xad, 0xee, 0x28, 0x05, 0x5e, 0x06, 0x28, 0x99, 0x15, 0x09, 0xdc, 0xaf, 0x61, - 0xd7, 0x11, 0x09, 0xe2, 0x41, 0x0f, 0x3e, 0xe9, 0xbc, 0x18, 0xdc, 0x1f, 0xf7, 0x35, 0x7c, 0xb0, 0x63, 0x2e, 0xe7, - 0x04, 0x6b, 0x0e, 0x7d, 0x8e, 0x06, 0xac, 0xde, 0x01, 0x5e, 0xa8, 0x60, 0x41, 0x90, 0x3a, 0x94, 0xfc, 0x59, 0x9b, - 0xac, 0x06, 0x37, 0xe2, 0xbb, 0xe8, 0x2e, 0x5d, 0xb2, 0x70, 0xad, 0x63, 0x00, 0x2c, 0x74, 0x48, 0x08, 0x65, 0x5e, - 0x10, 0xb1, 0x05, 0xd8, 0xa6, 0xbe, 0xe6, 0x82, 0xee, 0xc2, 0x84, 0xa3, 0x54, 0xcf, 0x9c, 0x70, 0xc1, 0x66, 0xc2, - 0x71, 0x5b, 0xf9, 0x86, 0xe0, 0x4b, 0x1a, 0x95, 0xad, 0xcf, 0x48, 0x7d, 0x1b, 0xda, 0x20, 0x57, 0x20, 0x9c, 0x5d, - 0x24, 0xc0, 0xde, 0xf2, 0xca, 0x8b, 0x26, 0x25, 0x32, 0x5e, 0x89, 0x51, 0x14, 0x1b, 0xd5, 0x66, 0xf8, 0x38, 0xc1, - 0x0b, 0x53, 0x63, 0x3b, 0x93, 0x4a, 0x3b, 0x0d, 0x93, 0xfe, 0xc0, 0xee, 0xe9, 0x22, 0x21, 0x50, 0x7d, 0x60, 0xf7, - 0xa0, 0xb0, 0xf8, 0x12, 0xb8, 0x29, 0xfa, 0x16, 0x74, 0x6d, 0x42, 0x5c, 0x83, 0x09, 0x78, 0xe6, 0xda, 0x72, 0x80, - 0x9c, 0x6c, 0x0b, 0x16, 0x47, 0x10, 0x43, 0x08, 0x6b, 0x71, 0x88, 0xb9, 0x5d, 0x42, 0xab, 0x16, 0xc6, 0x56, 0xcd, - 0xd1, 0x30, 0x9e, 0xb8, 0x8e, 0x73, 0x50, 0x29, 0x0f, 0x8c, 0xec, 0xba, 0xd2, 0x86, 0x99, 0x0e, 0x5d, 0xc7, 0xf2, - 0x9f, 0xd8, 0xed, 0x41, 0xe5, 0x8e, 0x56, 0x1c, 0x67, 0x8e, 0x90, 0xfd, 0x75, 0xfa, 0x68, 0xd3, 0xaa, 0x1c, 0x48, - 0xa3, 0xac, 0xe7, 0x8f, 0x63, 0xcb, 0x38, 0xff, 0x6b, 0x54, 0xbd, 0xfa, 0xc9, 0x6d, 0x27, 0x05, 0x71, 0x19, 0x81, - 0xeb, 0xe7, 0x16, 0x1c, 0xa3, 0xbf, 0x68, 0x4f, 0xb5, 0x16, 0x1d, 0x1f, 0xc3, 0x18, 0xc9, 0xd8, 0xe0, 0xc2, 0x10, - 0x4e, 0x6d, 0xa0, 0xd4, 0x63, 0x52, 0xc6, 0x70, 0xdc, 0xc9, 0x2c, 0xcb, 0x25, 0x7a, 0x5b, 0xa9, 0x05, 0x6c, 0xbf, - 0xe1, 0xfa, 0xb4, 0xc7, 0xe0, 0x4c, 0x01, 0x4a, 0x80, 0xa3, 0xf8, 0x9d, 0x0d, 0xae, 0x57, 0xc5, 0xe6, 0x8a, 0x97, - 0xe4, 0xfe, 0x8d, 0xe1, 0xa5, 0x83, 0x32, 0x34, 0xd9, 0x5e, 0xfd, 0x75, 0xf7, 0x89, 0x4d, 0xb2, 0x70, 0x5a, 0x6c, - 0xb0, 0x74, 0x7f, 0xed, 0xdf, 0x5c, 0x01, 0xa3, 0x40, 0x04, 0x85, 0xa8, 0x06, 0xa3, 0x64, 0x51, 0x88, 0x9b, 0x9f, - 0x8e, 0x9b, 0xbf, 0x17, 0x15, 0x83, 0x15, 0xc8, 0xfd, 0x99, 0xac, 0xa6, 0x30, 0xc5, 0xc1, 0x4d, 0x37, 0xda, 0x32, - 0xb7, 0x04, 0x21, 0xda, 0xb8, 0x13, 0x53, 0xa1, 0x08, 0x99, 0xd7, 0xf1, 0xb9, 0xe3, 0xcd, 0x7d, 0xb9, 0xd6, 0xfe, - 0x6e, 0xae, 0x75, 0xba, 0x8b, 0x6b, 0x4d, 0x36, 0x60, 0xa4, 0xbd, 0x23, 0x6d, 0xe1, 0x04, 0x71, 0xae, 0x5a, 0x13, - 0x16, 0x58, 0xdd, 0x68, 0x32, 0x26, 0x6a, 0x55, 0x5a, 0x23, 0xd5, 0x46, 0x64, 0x7f, 0x2b, 0x0f, 0x14, 0x21, 0x50, - 0x57, 0x79, 0xe3, 0x17, 0x39, 0x6f, 0x9c, 0x5e, 0x35, 0xb9, 0xf5, 0x8f, 0xa0, 0xfe, 0x15, 0xcb, 0x3a, 0xf9, 0x3a, - 0xc8, 0x2d, 0xec, 0xf2, 0x91, 0x2a, 0x36, 0x63, 0xf9, 0x43, 0x43, 0xb1, 0x44, 0x14, 0xaf, 0x8c, 0xa2, 0x41, 0x62, - 0xb1, 0x68, 0x6e, 0x32, 0x96, 0xa7, 0x03, 0xd7, 0x1d, 0x87, 0x2c, 0x93, 0xd5, 0x6d, 0x53, 0xb4, 0x19, 0x52, 0xb3, - 0x95, 0x4d, 0x22, 0x8d, 0x7b, 0x08, 0xc0, 0x82, 0x4d, 0x5f, 0x92, 0x6b, 0x4b, 0x1d, 0x08, 0x1c, 0x64, 0x8d, 0x2d, - 0xe2, 0x6e, 0xee, 0x3c, 0x05, 0x87, 0xc8, 0x45, 0xd7, 0x2e, 0xde, 0xee, 0x24, 0x09, 0x56, 0xf9, 0x11, 0x08, 0xeb, - 0x2b, 0x85, 0x83, 0xd0, 0x77, 0x34, 0x67, 0x50, 0x43, 0x00, 0xe0, 0xfd, 0x5f, 0xfe, 0xe6, 0xa4, 0x00, 0x70, 0x22, - 0x35, 0x47, 0x95, 0xf9, 0xe3, 0x21, 0xb6, 0x48, 0xfd, 0x18, 0x8b, 0x56, 0xfb, 0x24, 0x7e, 0xcf, 0x86, 0x5b, 0xfe, - 0x14, 0xd9, 0xf9, 0xbc, 0x44, 0x5f, 0x8c, 0x83, 0xef, 0xb2, 0x78, 0x1d, 0xa2, 0xcf, 0x7f, 0x2b, 0x8d, 0xbd, 0xc9, - 0x87, 0x8d, 0xd2, 0x1f, 0x67, 0x89, 0x02, 0xbb, 0xb8, 0x28, 0x54, 0x18, 0x78, 0xe8, 0x22, 0x93, 0xf5, 0xed, 0x76, - 0xa2, 0x30, 0x6a, 0xfa, 0x0f, 0x9d, 0x8e, 0xf7, 0x6c, 0x76, 0x58, 0xe2, 0x9f, 0xb6, 0xbb, 0x45, 0xee, 0xba, 0x1c, - 0xc7, 0x32, 0xfa, 0x95, 0xdb, 0x48, 0xfe, 0xf9, 0x5d, 0x27, 0xbc, 0xcf, 0xd2, 0x1a, 0x7d, 0xce, 0x10, 0xa0, 0x7e, - 0x41, 0x30, 0xad, 0x8a, 0x69, 0x2a, 0x29, 0x4d, 0xc3, 0x9a, 0xf9, 0x41, 0x60, 0x05, 0x60, 0xa9, 0xb2, 0xf9, 0xac, - 0xe9, 0x61, 0x3b, 0x6b, 0x70, 0xce, 0xfc, 0x19, 0xed, 0x14, 0x77, 0x4a, 0xba, 0x58, 0x2f, 0xc7, 0x1b, 0x95, 0x51, - 0xae, 0xf0, 0xcf, 0xab, 0x3c, 0x73, 0xb5, 0xdb, 0xd9, 0x6c, 0x56, 0xe4, 0x1a, 0x3b, 0xda, 0x21, 0x72, 0x7e, 0x1f, - 0x3a, 0x8e, 0x53, 0x86, 0x6f, 0xd3, 0x41, 0xa1, 0x83, 0x61, 0x21, 0x13, 0xbe, 0xb7, 0x7b, 0x4f, 0xfd, 0x49, 0xa3, - 0xa5, 0xa6, 0x9a, 0xce, 0x23, 0x6d, 0xb5, 0xff, 0x8a, 0xa1, 0x20, 0x6a, 0xd8, 0x75, 0xfc, 0xab, 0x7b, 0x65, 0x4b, - 0x4b, 0xe5, 0x03, 0xfc, 0x69, 0x95, 0x77, 0xec, 0xf5, 0x3d, 0xaa, 0x36, 0x6d, 0xef, 0xcc, 0xce, 0xaf, 0xdd, 0x82, - 0xce, 0xd2, 0x80, 0x34, 0x95, 0xfc, 0x94, 0x2d, 0x93, 0xfe, 0x84, 0xa1, 0x80, 0xd4, 0x56, 0x6e, 0x5b, 0xd4, 0xea, - 0xb1, 0xe6, 0xa0, 0xc7, 0xe5, 0x0a, 0x3c, 0xec, 0x68, 0x28, 0xac, 0xaa, 0x48, 0xd6, 0x44, 0x27, 0x78, 0x8b, 0x6d, - 0xaa, 0x02, 0x27, 0xdc, 0xa6, 0x5d, 0xe7, 0x2f, 0x85, 0x72, 0x1a, 0x50, 0xa7, 0x1b, 0xa1, 0x6d, 0x42, 0xc2, 0x13, - 0xfc, 0x5b, 0x0a, 0xe7, 0x9e, 0xad, 0xee, 0x8a, 0xca, 0x5d, 0x3d, 0x10, 0x37, 0xe5, 0x57, 0x19, 0x8d, 0xba, 0x0e, - 0xf5, 0x49, 0x15, 0xa0, 0x99, 0xaa, 0xdd, 0x02, 0x1a, 0x34, 0x85, 0x50, 0x44, 0x35, 0xb2, 0x31, 0x7c, 0xce, 0xc2, - 0xce, 0xcb, 0xf9, 0xfb, 0x79, 0x20, 0x4d, 0x98, 0x83, 0xf9, 0xb4, 0x87, 0xc2, 0xbd, 0xc2, 0x56, 0x45, 0x55, 0x19, - 0xdc, 0x03, 0xe2, 0x45, 0xaa, 0xad, 0xe3, 0xc0, 0xa2, 0x38, 0x3d, 0x2d, 0x63, 0x53, 0x9d, 0x77, 0x73, 0xf3, 0x6e, - 0x17, 0xe4, 0x1a, 0x55, 0x50, 0xed, 0x25, 0xda, 0x2b, 0xcb, 0xb0, 0xc5, 0x38, 0x61, 0x05, 0xc0, 0x92, 0x42, 0x43, - 0xa5, 0x21, 0xad, 0x84, 0xfb, 0x68, 0xd2, 0x32, 0x57, 0x45, 0xd6, 0x62, 0x9e, 0xd8, 0x5c, 0x7d, 0x11, 0x6a, 0x5b, - 0x48, 0x06, 0x01, 0x76, 0x1c, 0x3b, 0xe1, 0xb7, 0x05, 0x3b, 0x46, 0x45, 0x57, 0x2e, 0xee, 0x20, 0x3c, 0xa5, 0x16, - 0xd2, 0xc9, 0x09, 0x9d, 0x52, 0x94, 0x25, 0xfc, 0xad, 0x96, 0x79, 0x7f, 0x51, 0xe0, 0xc6, 0x73, 0x73, 0x96, 0xb6, - 0xb1, 0x57, 0xe9, 0xa5, 0x1f, 0xee, 0x5f, 0xd6, 0xbb, 0xdb, 0xbb, 0x2c, 0x10, 0x87, 0x7b, 0x17, 0x06, 0xea, 0x92, - 0xb4, 0x94, 0xd2, 0xe1, 0xdf, 0x14, 0xe1, 0x81, 0xea, 0x16, 0x41, 0xc7, 0x5a, 0xf4, 0xa2, 0xbf, 0x58, 0x0f, 0x47, - 0x27, 0x67, 0x77, 0xcb, 0x40, 0xbb, 0x61, 0x31, 0xc4, 0x2c, 0x1b, 0xea, 0xae, 0xed, 0xe8, 0x1a, 0x1a, 0xf9, 0xfb, - 0xe1, 0x7c, 0xa8, 0xff, 0x74, 0xf1, 0xca, 0xea, 0xe9, 0x67, 0xa0, 0x8e, 0x71, 0x33, 0x47, 0x12, 0xf7, 0xdc, 0xbb, - 0x67, 0xf1, 0x75, 0x5b, 0xd7, 0x30, 0x34, 0x19, 0x11, 0xb7, 0x98, 0xa6, 0xb5, 0xf5, 0x3d, 0x22, 0xe0, 0x68, 0x22, - 0x88, 0xa5, 0x0e, 0x88, 0xd5, 0x6d, 0xf7, 0x34, 0xb7, 0x7d, 0x68, 0x1f, 0xf5, 0xf4, 0xd3, 0xaf, 0x34, 0xed, 0x64, - 0xca, 0x66, 0xc9, 0x29, 0xb2, 0x63, 0x4e, 0x90, 0x1e, 0xa4, 0xdf, 0x9a, 0x66, 0x4f, 0x82, 0xc4, 0x72, 0xb5, 0x0d, - 0xff, 0xd4, 0x34, 0x40, 0x46, 0x7d, 0xed, 0xe1, 0xac, 0x3d, 0x3b, 0x9c, 0x3d, 0x1b, 0xf0, 0xe4, 0xec, 0xab, 0x42, - 0x71, 0x93, 0xfe, 0x6d, 0x2b, 0xd5, 0x92, 0x34, 0x8e, 0x3e, 0x30, 0x4e, 0x4b, 0x6a, 0x92, 0x51, 0x54, 0xae, 0xda, - 0xae, 0xf6, 0xe4, 0xf6, 0xc6, 0x93, 0x59, 0x3b, 0x2f, 0x8e, 0x63, 0x3c, 0x90, 0x83, 0x3c, 0x39, 0x10, 0x43, 0x3f, - 0x51, 0xc1, 0xe4, 0x5a, 0x75, 0x80, 0x72, 0x75, 0x3e, 0xc7, 0xb9, 0x98, 0xdf, 0x09, 0x38, 0x98, 0xcd, 0x0d, 0x10, - 0x12, 0xac, 0x36, 0xd4, 0xbf, 0x77, 0xdb, 0x3d, 0xd3, 0x75, 0x8f, 0xec, 0xa3, 0xde, 0xc4, 0x31, 0x0f, 0xed, 0x43, - 0xab, 0x63, 0x1f, 0x99, 0x3d, 0xab, 0x67, 0xf6, 0xfe, 0xda, 0x9b, 0x58, 0x87, 0xf6, 0xa1, 0xe9, 0x58, 0x3d, 0x48, - 0xb4, 0x7a, 0x56, 0xef, 0xc6, 0x3a, 0xec, 0x4d, 0x1c, 0x4c, 0x6d, 0xdb, 0xdd, 0xae, 0xe5, 0x3a, 0x76, 0xb7, 0x6b, - 0x76, 0xed, 0xa3, 0x23, 0xcb, 0xed, 0xd8, 0x47, 0x47, 0xe7, 0xdd, 0x9e, 0xdd, 0x81, 0xbc, 0x4e, 0x67, 0xd2, 0xb1, - 0x5d, 0xd7, 0x82, 0xbf, 0xcc, 0x9e, 0xdd, 0xa6, 0x1f, 0xae, 0x6b, 0x77, 0x5c, 0xd3, 0x09, 0xba, 0x6d, 0xfb, 0xe8, - 0x99, 0x89, 0x7f, 0x63, 0x31, 0x13, 0xff, 0x82, 0x66, 0xcc, 0x67, 0x76, 0xfb, 0x88, 0x7e, 0x61, 0x83, 0x37, 0x87, - 0xbd, 0x9f, 0xf5, 0x83, 0xc6, 0x39, 0xb8, 0x34, 0x87, 0x5e, 0xd7, 0xee, 0x74, 0xcc, 0x43, 0xd7, 0xee, 0x75, 0x16, - 0xd6, 0x61, 0xdb, 0x3e, 0x3a, 0x9e, 0x58, 0xae, 0x7d, 0x7c, 0x6c, 0x3a, 0x56, 0xc7, 0x6e, 0x9b, 0xae, 0x7d, 0xd8, - 0xc1, 0x1f, 0x1d, 0xbb, 0x7d, 0x73, 0xfc, 0xcc, 0x3e, 0xea, 0x2e, 0x8e, 0xec, 0xc3, 0xf7, 0x87, 0x3d, 0xbb, 0xdd, - 0x59, 0x74, 0x8e, 0xec, 0xf6, 0xf1, 0xcd, 0x91, 0x7d, 0xb8, 0xb0, 0xda, 0x47, 0x5b, 0x6b, 0xba, 0x6d, 0x1b, 0x60, - 0x84, 0xd9, 0x90, 0x61, 0xf2, 0x0c, 0xf8, 0xb3, 0xc0, 0xba, 0x7f, 0x62, 0x33, 0x49, 0xb5, 0xea, 0x33, 0xbb, 0x77, - 0x3c, 0xa1, 0xe2, 0x90, 0x60, 0x89, 0x12, 0x50, 0xe5, 0xc6, 0xa2, 0x6e, 0xb1, 0x39, 0x4b, 0x34, 0x24, 0xfe, 0xf0, - 0xce, 0x6e, 0x2c, 0xe8, 0x98, 0xfa, 0xfd, 0x8f, 0xb6, 0x23, 0x97, 0x1c, 0x42, 0xf2, 0x7e, 0xc5, 0xff, 0xa1, 0x68, - 0x56, 0x23, 0xf3, 0xbc, 0x49, 0x28, 0xf9, 0x76, 0xb7, 0x50, 0xf2, 0xd5, 0x7a, 0x1f, 0xa1, 0xe4, 0xdb, 0x2f, 0x2e, - 0x94, 0x3c, 0x2f, 0xdb, 0xc4, 0xbc, 0x2d, 0x07, 0xdd, 0xf8, 0x79, 0x53, 0x66, 0x39, 0xf8, 0x5e, 0xeb, 0xf2, 0x62, - 0x7d, 0x05, 0xee, 0xdf, 0xde, 0x46, 0xc3, 0x57, 0xeb, 0x82, 0xc2, 0x67, 0x04, 0x38, 0xf6, 0x6d, 0x44, 0x38, 0xf6, - 0xd7, 0xf5, 0x10, 0xb4, 0xcc, 0x38, 0x99, 0xe3, 0x4f, 0xad, 0x85, 0x17, 0xcc, 0x24, 0x89, 0x04, 0x29, 0x03, 0x4c, - 0x06, 0xbb, 0x2b, 0xb8, 0x9e, 0xe1, 0x25, 0xb3, 0x5e, 0x86, 0x09, 0x68, 0x04, 0x83, 0x26, 0xc7, 0x2c, 0xce, 0x4a, - 0x95, 0x6d, 0xe1, 0x30, 0xef, 0x9a, 0x5b, 0x40, 0x35, 0xe6, 0xa3, 0x02, 0x70, 0x7d, 0xeb, 0x6e, 0xb5, 0x5d, 0x0d, - 0x34, 0xeb, 0x84, 0x82, 0x34, 0x50, 0xfb, 0x75, 0xf9, 0x45, 0x35, 0xdc, 0x92, 0xe2, 0x75, 0xf3, 0x48, 0x61, 0x24, - 0xe5, 0xfa, 0x6e, 0x51, 0x8d, 0x77, 0xd7, 0x34, 0x6b, 0xba, 0x2f, 0x54, 0xdf, 0xa2, 0x43, 0x2c, 0x1b, 0x2e, 0x83, - 0xaa, 0x14, 0x32, 0xb2, 0x16, 0x20, 0xf9, 0x03, 0x35, 0x57, 0x34, 0xce, 0x29, 0x55, 0x47, 0x43, 0x7a, 0xc7, 0x51, - 0xf2, 0x0a, 0x6d, 0xaa, 0xca, 0xc9, 0x4f, 0x36, 0xf8, 0xae, 0xf0, 0x7f, 0x05, 0x4a, 0x94, 0x53, 0x3c, 0xe3, 0x48, - 0x85, 0xf3, 0x46, 0x69, 0x97, 0xb8, 0x11, 0xd9, 0xc2, 0xdd, 0x54, 0x69, 0xd1, 0x46, 0xb3, 0x04, 0x97, 0x2d, 0x05, - 0x15, 0x84, 0xdd, 0x93, 0x11, 0x40, 0x46, 0x86, 0x1a, 0x68, 0xe7, 0xb0, 0xad, 0x31, 0x51, 0xee, 0x21, 0x6c, 0x62, - 0x93, 0x7f, 0xa8, 0x8e, 0x4b, 0x36, 0xb3, 0x20, 0xf2, 0xd2, 0x3e, 0x92, 0x69, 0x0a, 0xc9, 0xdb, 0x46, 0x8b, 0x85, - 0xc1, 0x16, 0x65, 0x3a, 0xb5, 0x61, 0xde, 0x08, 0x5a, 0x3e, 0x6c, 0xd3, 0xbf, 0x93, 0x86, 0x62, 0x9b, 0x82, 0x3a, - 0x8a, 0xdb, 0x3d, 0x36, 0xdd, 0x23, 0xd3, 0x3e, 0xee, 0x1a, 0x99, 0x38, 0x70, 0x6a, 0x93, 0x05, 0x80, 0x80, 0x01, - 0x84, 0x1c, 0xa6, 0x1f, 0xfa, 0xa9, 0xef, 0x05, 0x19, 0xd0, 0xc3, 0xc5, 0x47, 0xca, 0x3f, 0xd7, 0x49, 0x0a, 0x73, - 0x14, 0x44, 0x2f, 0x1a, 0x7f, 0x58, 0x63, 0x96, 0xde, 0x32, 0x16, 0x36, 0x28, 0xc6, 0x94, 0x6d, 0x49, 0xfe, 0x38, - 0xcd, 0xfa, 0x8c, 0xb4, 0xd6, 0xc6, 0x69, 0xc8, 0xf7, 0x87, 0x30, 0x7c, 0xc8, 0x46, 0xe6, 0x0f, 0x4d, 0x08, 0xf7, - 0x9f, 0xbb, 0x11, 0x6e, 0xca, 0xf6, 0x41, 0xb8, 0xff, 0xfc, 0xe2, 0x08, 0xf7, 0x07, 0x15, 0xe1, 0x16, 0xec, 0xfe, - 0x72, 0x09, 0xd3, 0x3b, 0xfc, 0x6e, 0x81, 0xb7, 0xfa, 0xa7, 0xfa, 0x01, 0x11, 0xf0, 0xba, 0x12, 0x45, 0xfc, 0x7d, - 0x21, 0x2c, 0x1a, 0x32, 0x40, 0xd1, 0x4b, 0x36, 0x85, 0x60, 0x82, 0x08, 0xdb, 0x8e, 0x0c, 0xc3, 0xc4, 0x6e, 0xb5, - 0xd7, 0x61, 0x1a, 0xd8, 0x6f, 0xf9, 0x3b, 0x12, 0x04, 0xba, 0xaf, 0xa2, 0x78, 0xe9, 0xa1, 0x87, 0x50, 0x1d, 0xc3, - 0xa9, 0xc2, 0x87, 0x03, 0xb6, 0xa4, 0x93, 0x28, 0x9c, 0x4a, 0xa9, 0x24, 0x1b, 0x5e, 0x12, 0xc5, 0xad, 0xdf, 0x33, - 0x2f, 0xd6, 0x4d, 0xca, 0x86, 0xc5, 0x7d, 0xd2, 0x71, 0x9e, 0xb4, 0x0f, 0x9f, 0x1c, 0x39, 0xf0, 0xbf, 0xcb, 0x3a, - 0x99, 0xc9, 0x0b, 0x2e, 0xa3, 0x10, 0x22, 0x3a, 0x89, 0x92, 0x4d, 0xc5, 0x6e, 0x19, 0xfb, 0x90, 0x97, 0x3a, 0xae, - 0x2f, 0x34, 0xf5, 0xee, 0xf3, 0x32, 0xb5, 0x25, 0x16, 0xd1, 0x5a, 0x19, 0x56, 0xcd, 0x68, 0xfc, 0x70, 0x0d, 0x7c, - 0x76, 0xa5, 0x84, 0x9a, 0xcd, 0xa7, 0x9b, 0xcf, 0x8b, 0x75, 0xb2, 0xab, 0x3c, 0x6c, 0x9c, 0x08, 0x5f, 0xb5, 0x13, - 0x82, 0x5c, 0x44, 0xe9, 0x60, 0xd0, 0x09, 0x0c, 0x9c, 0xa6, 0x41, 0xd0, 0x56, 0xb1, 0x40, 0x1e, 0x2d, 0x50, 0x1a, - 0xaf, 0xc3, 0x49, 0x0b, 0x7f, 0x7a, 0xe3, 0xa4, 0xe5, 0x1f, 0x80, 0xfb, 0x68, 0xec, 0xd8, 0xc0, 0x55, 0xf3, 0x4e, - 0x9d, 0x3c, 0xc6, 0x4e, 0x22, 0x56, 0xc5, 0x7b, 0x92, 0x9a, 0x31, 0x45, 0xe6, 0xc6, 0xa5, 0xb5, 0x86, 0xde, 0x13, - 0x59, 0xf1, 0x49, 0x6a, 0x42, 0x74, 0x6c, 0x58, 0xee, 0xc7, 0x8f, 0xa9, 0x14, 0xc4, 0xab, 0xa5, 0x69, 0x9d, 0x4d, - 0x72, 0xff, 0x93, 0x9a, 0x37, 0x8f, 0xc8, 0x05, 0x65, 0x7f, 0x62, 0x46, 0x4f, 0x9f, 0x9e, 0x0e, 0x5d, 0x83, 0x47, - 0x5b, 0x2e, 0x84, 0x06, 0x3c, 0xdf, 0x4f, 0xd1, 0xc8, 0xa8, 0x35, 0x81, 0x5d, 0xc1, 0x9b, 0xc9, 0x11, 0xe6, 0x08, - 0x1c, 0x7b, 0x41, 0xa8, 0x1e, 0x52, 0x28, 0xf0, 0x84, 0xc2, 0x8f, 0x28, 0x23, 0x5f, 0x5d, 0x1d, 0xdb, 0xb1, 0x1d, - 0x5d, 0x56, 0x9c, 0xf9, 0xf3, 0xe1, 0x26, 0x4a, 0x3d, 0x08, 0x7a, 0x16, 0x44, 0x73, 0xb0, 0xa3, 0x4b, 0xfd, 0x34, - 0x80, 0x08, 0x5a, 0x60, 0x50, 0xb7, 0xa4, 0x77, 0x79, 0xc6, 0xad, 0x1b, 0xbc, 0xf8, 0x03, 0x46, 0x51, 0x15, 0x26, - 0xb4, 0xe8, 0x12, 0xed, 0x7b, 0xb8, 0x0c, 0x5b, 0x7a, 0x0b, 0x78, 0x03, 0x2c, 0x4e, 0x2c, 0xd5, 0x5a, 0xa8, 0xaf, - 0x41, 0x1d, 0x43, 0xe7, 0x93, 0x98, 0xc5, 0xde, 0x12, 0x82, 0x4d, 0x6c, 0x32, 0x93, 0x63, 0x5a, 0x9d, 0xa3, 0x5a, - 0xcd, 0x7d, 0x76, 0x64, 0x6a, 0x6d, 0xd7, 0xd4, 0x1c, 0x40, 0xb7, 0x7a, 0x66, 0x6e, 0xb2, 0xab, 0xc1, 0x2e, 0x85, - 0x07, 0xc2, 0x2f, 0x0f, 0x69, 0x1e, 0xa4, 0xea, 0xc0, 0x45, 0x49, 0x29, 0x79, 0xb8, 0x6d, 0x29, 0x61, 0x20, 0x7c, - 0x12, 0x7a, 0x5e, 0xb0, 0xbb, 0xd4, 0xc0, 0x08, 0x53, 0xbc, 0x88, 0x6f, 0x6c, 0xd0, 0xd0, 0xd7, 0x0f, 0x35, 0xff, - 0xe3, 0xc7, 0x96, 0x0f, 0xc6, 0x4c, 0x43, 0x05, 0x3e, 0xf0, 0x6d, 0x14, 0x00, 0xe6, 0xe7, 0x62, 0x7a, 0x04, 0x16, - 0x58, 0x1a, 0xc2, 0xbf, 0x79, 0xb2, 0xf8, 0xc1, 0xd5, 0x24, 0xec, 0xc0, 0x0b, 0xe7, 0x80, 0xd2, 0xbc, 0x70, 0x5e, - 0x51, 0xc7, 0x22, 0x5b, 0xe5, 0x52, 0x6a, 0xde, 0x54, 0xae, 0x2a, 0x95, 0x7c, 0x77, 0x7f, 0x41, 0x11, 0xf4, 0x5a, - 0x3a, 0xdc, 0x72, 0x68, 0x58, 0x9b, 0x4b, 0x72, 0x9f, 0x0e, 0xbf, 0x3e, 0x59, 0xb2, 0xd4, 0x23, 0x31, 0x10, 0x3c, - 0x7e, 0x81, 0x1c, 0xd0, 0x26, 0x22, 0xfa, 0x35, 0x5e, 0x0d, 0xc3, 0x29, 0xbb, 0xf1, 0x27, 0xfc, 0x5d, 0x6a, 0x6a, - 0xfc, 0x9e, 0xb2, 0x50, 0xe3, 0x73, 0xe8, 0x9a, 0x64, 0x70, 0x30, 0xf1, 0xd0, 0x0f, 0xee, 0x30, 0x8c, 0xf4, 0xd3, - 0xaf, 0xa5, 0x6d, 0x66, 0xd3, 0x22, 0x40, 0x18, 0xdb, 0xcb, 0x98, 0x05, 0xff, 0x1a, 0x7e, 0x0d, 0x17, 0xf7, 0xd7, - 0x57, 0xba, 0x31, 0x48, 0xed, 0x45, 0xcc, 0x66, 0xc3, 0xaf, 0x6b, 0xc2, 0xb9, 0xe2, 0xf3, 0x9e, 0xc6, 0xa2, 0x77, - 0xda, 0xb9, 0xf7, 0xb2, 0xce, 0x5e, 0x8f, 0xfa, 0x53, 0xfe, 0x5a, 0x87, 0x17, 0xe0, 0xa6, 0xf0, 0xc6, 0x76, 0x07, - 0xf8, 0x7e, 0x1e, 0x07, 0xde, 0xe4, 0xc3, 0x80, 0x72, 0x0a, 0x1f, 0x16, 0xdc, 0xd6, 0x13, 0x6f, 0xd5, 0xc7, 0xeb, - 0x55, 0x4d, 0x04, 0xd3, 0x6c, 0x4a, 0x95, 0x94, 0x5d, 0xed, 0x5e, 0xc6, 0xad, 0xbc, 0xc1, 0x9e, 0xb1, 0xab, 0xdb, - 0x85, 0x9f, 0x32, 0xd1, 0x15, 0x7e, 0x64, 0x99, 0x78, 0xa8, 0xd3, 0x13, 0x15, 0x1f, 0xd6, 0x76, 0x47, 0x73, 0x7b, - 0x7f, 0xed, 0xde, 0xb8, 0xce, 0xa2, 0xed, 0xda, 0xbd, 0xf7, 0x6e, 0x6f, 0xd1, 0xb1, 0x8f, 0x03, 0xab, 0x63, 0x1f, - 0xc3, 0x9f, 0xf7, 0xc7, 0x76, 0x6f, 0x61, 0xb5, 0xed, 0xc3, 0xf7, 0x6e, 0x3b, 0xb0, 0x7a, 0xf6, 0x31, 0xfc, 0x39, - 0xa7, 0x5a, 0xf0, 0x00, 0xa2, 0xf7, 0xce, 0xd7, 0x05, 0x2c, 0xa0, 0xfc, 0x96, 0x32, 0x59, 0xb3, 0x70, 0xbd, 0xd5, - 0xc8, 0x75, 0x01, 0x65, 0xe8, 0xa6, 0xf0, 0x52, 0x1b, 0x0e, 0x5a, 0xe1, 0x90, 0x47, 0x46, 0x11, 0xea, 0x6d, 0xc2, - 0x06, 0x5d, 0xc4, 0x08, 0xa9, 0x3d, 0x46, 0xbc, 0x4e, 0x7d, 0x44, 0x08, 0x11, 0x72, 0x8f, 0x04, 0xc1, 0x3f, 0xad, - 0xd0, 0xcb, 0x9a, 0x88, 0x61, 0xa1, 0x60, 0xa5, 0x3c, 0xec, 0x6b, 0xb6, 0x7b, 0xe0, 0x68, 0x85, 0xcf, 0x64, 0xd8, - 0xb1, 0x2f, 0xda, 0x36, 0x17, 0x0e, 0xcb, 0xd6, 0x7f, 0x6f, 0x3b, 0x18, 0x6d, 0x9b, 0xda, 0x11, 0xee, 0xa6, 0xa7, - 0x7e, 0x2c, 0x87, 0xa7, 0xa0, 0x68, 0xb7, 0x3e, 0x94, 0x86, 0x01, 0xf1, 0x99, 0x5e, 0x03, 0x95, 0x7c, 0xe3, 0x05, - 0x8a, 0x22, 0x9b, 0x52, 0xf3, 0x81, 0xc4, 0xfc, 0x8f, 0x1f, 0xe7, 0x83, 0xb3, 0x4a, 0xe3, 0x3e, 0x71, 0xbb, 0x70, - 0xed, 0x76, 0x59, 0x67, 0xab, 0x4e, 0xe5, 0x6e, 0x7f, 0xe5, 0xb9, 0x3f, 0x63, 0xa1, 0x37, 0x25, 0x34, 0x36, 0x1a, - 0x15, 0x3b, 0x2b, 0xfa, 0x1a, 0xe0, 0xe9, 0xbd, 0xf4, 0xd4, 0xd1, 0x8d, 0x41, 0x28, 0xd4, 0x0f, 0xc2, 0x2d, 0x3e, - 0xda, 0xf9, 0x5b, 0x4c, 0x07, 0xd0, 0x6c, 0x99, 0xc7, 0x0e, 0x03, 0xf1, 0xff, 0xf4, 0x24, 0xd0, 0x58, 0x13, 0xf4, - 0x25, 0x4a, 0xa7, 0xb5, 0x60, 0xbc, 0x27, 0xef, 0x55, 0xba, 0x50, 0x59, 0x72, 0xa6, 0x43, 0x12, 0x04, 0xee, 0xc3, - 0x58, 0x9d, 0x52, 0x59, 0x54, 0xde, 0x16, 0x79, 0x82, 0xe9, 0x43, 0x90, 0x82, 0x96, 0x30, 0x1c, 0x35, 0x1e, 0x3f, - 0x6e, 0xbc, 0x84, 0x48, 0x39, 0x47, 0x0d, 0x59, 0xac, 0xab, 0xf8, 0x4d, 0x57, 0x51, 0x8c, 0x6c, 0x17, 0xb1, 0x86, - 0xd0, 0x71, 0xa5, 0xbd, 0x87, 0x3f, 0xc7, 0xcc, 0x4b, 0x6d, 0x2e, 0x2c, 0x6d, 0x29, 0x97, 0xbb, 0xe9, 0xb2, 0x0e, - 0x68, 0xb7, 0x72, 0x67, 0x8c, 0xdc, 0xd9, 0xe9, 0xa3, 0xcd, 0xfb, 0x35, 0xf7, 0xea, 0x00, 0x6d, 0x7c, 0x74, 0x72, - 0xff, 0x59, 0x6f, 0x52, 0x8f, 0x9c, 0x2c, 0xa9, 0x57, 0x6e, 0x94, 0x7a, 0x22, 0x40, 0x16, 0xd0, 0xe5, 0x83, 0x5a, - 0xf5, 0x0b, 0xc5, 0xf3, 0xc3, 0xe9, 0x9b, 0x8b, 0x6f, 0x35, 0xbe, 0xff, 0x49, 0x5b, 0x00, 0x1f, 0x32, 0x14, 0x96, - 0x65, 0x48, 0x61, 0x59, 0x34, 0x1e, 0x1f, 0x09, 0x82, 0x9b, 0x64, 0x07, 0x04, 0x41, 0x64, 0x40, 0x93, 0x0e, 0xc5, - 0x72, 0x1d, 0xa4, 0xfe, 0xca, 0x8b, 0xd3, 0x03, 0xa8, 0x6a, 0x01, 0x92, 0xd3, 0x9b, 0xfc, 0x41, 0x90, 0x1a, 0x86, - 0xf0, 0x01, 0x9a, 0x86, 0x42, 0x0f, 0x63, 0xe6, 0x07, 0x52, 0x0d, 0x43, 0x74, 0xe0, 0x4d, 0x26, 0x6c, 0x95, 0x0e, - 0x75, 0x6f, 0x05, 0xe1, 0x79, 0xd0, 0xe1, 0xfe, 0x41, 0x34, 0x49, 0x59, 0x6a, 0x25, 0x69, 0xcc, 0xbc, 0xa5, 0x2e, - 0x7d, 0x4d, 0x57, 0xdb, 0x4b, 0xd6, 0xe3, 0xa5, 0x9f, 0x4a, 0x67, 0xad, 0x34, 0x41, 0x50, 0x88, 0x80, 0x21, 0x82, - 0x73, 0x18, 0x02, 0xe1, 0x79, 0x34, 0x2f, 0xed, 0xa8, 0x9c, 0x72, 0x39, 0x43, 0x57, 0xe0, 0x3c, 0x24, 0xcb, 0x14, - 0xed, 0x1b, 0xaf, 0xb9, 0x0f, 0x0b, 0xe9, 0x53, 0x56, 0x3f, 0x3d, 0xe1, 0xcf, 0x5b, 0x0d, 0xdd, 0xae, 0xe8, 0x5d, - 0x07, 0x9c, 0x9d, 0x37, 0x79, 0xb7, 0x38, 0xe0, 0x85, 0xe1, 0x6a, 0xa2, 0x96, 0x31, 0x10, 0x05, 0x8d, 0xe5, 0x02, - 0x08, 0xa1, 0x82, 0xc2, 0xcc, 0xc2, 0x3d, 0x95, 0xe6, 0x94, 0x38, 0x2a, 0xa4, 0x95, 0x3e, 0x7e, 0x7c, 0x3e, 0xfa, - 0xed, 0xdf, 0x10, 0x2d, 0x63, 0xe1, 0x0a, 0x9f, 0x12, 0x97, 0x6a, 0x29, 0x4e, 0x7d, 0x9a, 0x23, 0x54, 0x96, 0x62, - 0x53, 0xe1, 0x98, 0x47, 0x6c, 0xad, 0x6c, 0x74, 0x25, 0xbc, 0xfd, 0x41, 0x44, 0x1c, 0x43, 0x78, 0xbe, 0x18, 0xc1, - 0xf2, 0x8e, 0x84, 0xc3, 0x15, 0xed, 0x97, 0xbb, 0xef, 0x8e, 0xb5, 0xdc, 0x05, 0x4f, 0x9d, 0x46, 0x0f, 0xed, 0xa1, - 0xd3, 0x13, 0x4f, 0x43, 0xa2, 0x05, 0xc9, 0x8f, 0xa4, 0x7f, 0x00, 0xd3, 0x5c, 0x44, 0x4b, 0x66, 0xfb, 0xd1, 0xc1, - 0x2d, 0x1b, 0x5b, 0xde, 0xca, 0x27, 0xbd, 0x1c, 0xe4, 0xbb, 0x69, 0x44, 0xf9, 0x49, 0x75, 0x17, 0xa2, 0xaf, 0xb3, - 0x1c, 0x94, 0x51, 0xd1, 0xbd, 0x63, 0xb7, 0x9d, 0xcb, 0x01, 0xc1, 0x7f, 0x81, 0x02, 0xc7, 0xe8, 0xf4, 0xe4, 0xc0, - 0x3b, 0x2d, 0xfa, 0x97, 0xb5, 0x45, 0x84, 0x93, 0xe2, 0x25, 0x70, 0x46, 0x6e, 0x62, 0x85, 0x47, 0xd8, 0xfc, 0xc3, - 0x8a, 0x66, 0x33, 0xd5, 0x27, 0xac, 0x5d, 0x1c, 0x9e, 0x04, 0x5a, 0xbe, 0xa5, 0xa3, 0x15, 0xf5, 0x54, 0xed, 0x42, - 0xfe, 0x84, 0xa8, 0xf0, 0xdc, 0x7d, 0x30, 0x1c, 0xf7, 0x8a, 0x6f, 0x59, 0x09, 0xb1, 0x87, 0x54, 0x88, 0xe3, 0x91, - 0x72, 0x24, 0x83, 0x06, 0xca, 0xe5, 0xc1, 0x70, 0x48, 0x68, 0xae, 0x8c, 0xed, 0x00, 0x88, 0x35, 0xd1, 0x5e, 0x60, - 0xb2, 0x29, 0x54, 0xb4, 0xc8, 0x5c, 0x16, 0x2b, 0x95, 0xa7, 0x53, 0x1d, 0xe3, 0x81, 0x27, 0xb6, 0x5f, 0x61, 0x83, - 0xc2, 0xc6, 0xe3, 0xeb, 0x0e, 0xf8, 0x5d, 0xb4, 0x53, 0x42, 0xf3, 0xda, 0x37, 0x84, 0xd1, 0xad, 0xc0, 0xbb, 0x8f, - 0x14, 0x35, 0x26, 0xee, 0xd1, 0xe4, 0x1c, 0x53, 0x2f, 0x84, 0x25, 0x71, 0xe5, 0xa0, 0xc9, 0x88, 0x19, 0xd5, 0xc3, - 0xa6, 0x86, 0xb8, 0xd8, 0x75, 0xd6, 0xd4, 0xb2, 0xc5, 0xc9, 0x20, 0xf2, 0xcc, 0xf2, 0x73, 0x58, 0xc8, 0x44, 0xb4, - 0x90, 0x9d, 0x1c, 0xc0, 0xfc, 0xc0, 0x0b, 0x4b, 0x81, 0x70, 0xf2, 0x0d, 0x70, 0xf5, 0xe2, 0x25, 0xa4, 0x8a, 0xf5, - 0x7a, 0x2a, 0x68, 0x3e, 0x7c, 0x58, 0x4a, 0xe7, 0xd5, 0x85, 0x22, 0x55, 0x5a, 0xc6, 0xa9, 0x27, 0x02, 0x77, 0x47, - 0x98, 0xf9, 0x75, 0x8d, 0xe1, 0x65, 0xb2, 0x41, 0xca, 0x84, 0x93, 0x83, 0xf3, 0xb4, 0xc1, 0x0f, 0x42, 0x53, 0x11, - 0xa2, 0x67, 0xb7, 0x14, 0xc8, 0xf7, 0xf1, 0xb6, 0x52, 0x39, 0xe5, 0x24, 0x8b, 0xad, 0xbc, 0x96, 0xfe, 0x10, 0x77, - 0x7c, 0xa5, 0x34, 0xa6, 0x42, 0xb9, 0xf3, 0x74, 0x08, 0x45, 0x05, 0x2f, 0xde, 0x5b, 0xad, 0xa8, 0xb0, 0x31, 0x38, - 0x39, 0xa0, 0x67, 0xe9, 0x29, 0xed, 0xb0, 0xd3, 0x13, 0x50, 0xe5, 0xa6, 0x45, 0xf7, 0x56, 0x2b, 0xbe, 0xa4, 0xf4, - 0x8b, 0x72, 0x0e, 0x16, 0xe9, 0x32, 0x38, 0xfd, 0x7f, 0x8a, 0xa5, 0x7c, 0x2e, 0xc4, 0x61, 0x03, 0x00}; + 0x3d, 0x21, 0xf5, 0x5e, 0xd9, 0x1e, 0x40, 0x3b, 0xd9, 0xfb, 0x5d, 0xb1, 0xbd, 0x4f, 0x0b, 0xa1, 0xe8, 0x98, 0x3d, + 0xbe, 0xba, 0xfa, 0xb4, 0x27, 0x8b, 0xbd, 0x4f, 0x99, 0x52, 0x9f, 0xf6, 0xb8, 0x50, 0x9a, 0xd1, 0x51, 0x12, 0xa1, + 0xae, 0xe9, 0x2c, 0x53, 0xea, 0x1d, 0xbb, 0xd5, 0x44, 0x63, 0xf3, 0xa9, 0x09, 0x5b, 0x4f, 0x98, 0xde, 0x53, 0xe5, + 0xbc, 0x62, 0xb4, 0xcc, 0x99, 0xde, 0xd3, 0xc4, 0xe4, 0x4b, 0x07, 0x7f, 0x66, 0x3f, 0x75, 0x97, 0x8f, 0xe3, 0x1b, + 0x71, 0x70, 0xa0, 0x4b, 0x40, 0xa3, 0xa5, 0x5b, 0x21, 0xc2, 0xf6, 0x7d, 0xda, 0xc1, 0x01, 0x4b, 0x72, 0x26, 0x26, + 0x7a, 0x4a, 0x08, 0x69, 0x77, 0xc5, 0xc1, 0x41, 0xac, 0xc9, 0x07, 0x91, 0x4c, 0x98, 0x8e, 0x19, 0x42, 0xb8, 0xaa, + 0x7d, 0x70, 0x10, 0x5b, 0x20, 0x48, 0xa2, 0x0d, 0xe0, 0x6a, 0x30, 0x46, 0x89, 0x83, 0xfe, 0xd5, 0x9d, 0xc8, 0xe2, + 0x70, 0xfc, 0x08, 0x8b, 0x83, 0x83, 0x0f, 0x22, 0x51, 0xd0, 0x22, 0xd6, 0x08, 0xad, 0x0b, 0xa6, 0x17, 0x85, 0xd8, + 0xd3, 0x6b, 0x2d, 0xaf, 0x74, 0xc1, 0xc5, 0x24, 0x46, 0x4b, 0x9f, 0x16, 0x54, 0x5c, 0xaf, 0xed, 0x70, 0x7f, 0x2b, + 0x08, 0x27, 0x97, 0xd0, 0xe3, 0x33, 0x19, 0x3b, 0x1c, 0xe4, 0x84, 0x44, 0xca, 0xd4, 0x8d, 0x7a, 0x3c, 0xe5, 0x8d, + 0x28, 0xc2, 0x76, 0x94, 0xf8, 0xb3, 0x40, 0x58, 0x68, 0x40, 0xdd, 0x24, 0x49, 0x34, 0x22, 0x97, 0x4b, 0x0f, 0x16, + 0x1e, 0x4c, 0xb4, 0xc7, 0xfb, 0xad, 0x41, 0xaa, 0x93, 0x82, 0x8d, 0x16, 0x19, 0x8b, 0x63, 0x81, 0x15, 0x96, 0x88, + 0x5c, 0x8a, 0x46, 0x5c, 0x90, 0x4b, 0x58, 0xef, 0xa2, 0xbe, 0xd8, 0x84, 0xec, 0xb7, 0x90, 0x1b, 0x64, 0xe1, 0x47, + 0x08, 0x20, 0x76, 0x03, 0x2a, 0x08, 0x89, 0xc4, 0x62, 0x36, 0x64, 0x45, 0x54, 0x16, 0xeb, 0xd6, 0xf0, 0x62, 0xa1, + 0xd8, 0x5e, 0xa6, 0xd4, 0xde, 0x78, 0x21, 0x32, 0xcd, 0xa5, 0xd8, 0x8b, 0x1a, 0x45, 0x23, 0xb2, 0xf8, 0x50, 0xa2, + 0x43, 0x84, 0xd6, 0x28, 0x56, 0xa8, 0xc1, 0xfb, 0xb2, 0xd1, 0x1e, 0x60, 0x18, 0x25, 0xea, 0xba, 0xf6, 0x1c, 0x04, + 0x18, 0xe6, 0x30, 0xc9, 0x35, 0xfe, 0xd3, 0xee, 0x7c, 0x98, 0xe2, 0x8d, 0xe8, 0xf1, 0x64, 0x7b, 0xa7, 0x10, 0x9d, + 0xcc, 0xe8, 0x3c, 0x66, 0xe4, 0x92, 0x19, 0xec, 0xa2, 0x22, 0x83, 0xb1, 0xd6, 0x16, 0xae, 0xc7, 0x52, 0x96, 0x54, + 0x38, 0x85, 0x52, 0x9d, 0x8c, 0x65, 0xf1, 0x94, 0x66, 0x53, 0xa8, 0x57, 0x62, 0xcc, 0xc8, 0x6f, 0xb8, 0xac, 0x60, + 0x54, 0xb3, 0xa7, 0x39, 0x83, 0xaf, 0x38, 0x32, 0x35, 0x23, 0x84, 0x15, 0x6c, 0xf5, 0x9c, 0xeb, 0x57, 0x52, 0x64, + 0xac, 0xab, 0x02, 0xfc, 0x32, 0x2b, 0xff, 0x50, 0xeb, 0x82, 0x0f, 0x17, 0x9a, 0xc5, 0x91, 0x80, 0x12, 0x11, 0x56, + 0x08, 0x8b, 0x44, 0xb3, 0x5b, 0xfd, 0x58, 0x0a, 0xcd, 0x84, 0x26, 0xcc, 0x43, 0x15, 0xf3, 0x84, 0xce, 0xe7, 0x4c, + 0x8c, 0x1e, 0x4f, 0x79, 0x3e, 0x8a, 0x05, 0x5a, 0xa3, 0x35, 0xfe, 0x5d, 0x10, 0x98, 0x24, 0xb9, 0xe4, 0x29, 0xfc, + 0xf3, 0xed, 0xe9, 0xc4, 0x9a, 0x5c, 0x9a, 0x6d, 0xc1, 0x48, 0x14, 0x75, 0xc7, 0xb2, 0x88, 0xdd, 0x14, 0xf6, 0x80, + 0x74, 0x41, 0x1f, 0x6f, 0x17, 0x39, 0x53, 0x88, 0x35, 0x88, 0x28, 0xd7, 0xd1, 0x41, 0xf8, 0xb7, 0x22, 0x66, 0xb0, + 0x00, 0x1c, 0xa5, 0xdc, 0x90, 0xc0, 0x97, 0xdc, 0x6d, 0xaa, 0x51, 0x49, 0xd4, 0x3e, 0x0a, 0x32, 0xe2, 0x89, 0x2e, + 0x16, 0x4a, 0xb3, 0xd1, 0xbb, 0xbb, 0x39, 0x53, 0xf8, 0xe7, 0x82, 0x7c, 0x14, 0xbd, 0x8f, 0x22, 0x61, 0xb3, 0xb9, + 0xbe, 0xbb, 0x32, 0xd4, 0x3c, 0x8d, 0x22, 0xfc, 0x8f, 0x29, 0x5a, 0x30, 0x9a, 0x01, 0x49, 0x73, 0x20, 0x7b, 0x23, + 0xf3, 0xbb, 0x31, 0xcf, 0xf3, 0xab, 0xc5, 0x7c, 0x2e, 0x0b, 0x8d, 0xb5, 0x20, 0x4b, 0x2d, 0x2b, 0xf8, 0xc0, 0x8a, + 0x2e, 0xd5, 0x0d, 0xd7, 0xd9, 0x34, 0xd6, 0x68, 0x99, 0x51, 0xc5, 0xf6, 0x1e, 0x49, 0x99, 0x33, 0x2a, 0x52, 0x4e, + 0x78, 0xef, 0xe7, 0x22, 0x15, 0x8b, 0x3c, 0xef, 0x0e, 0x0b, 0x46, 0x3f, 0x77, 0x4d, 0xb6, 0x3d, 0x1c, 0x52, 0xf3, + 0xfb, 0x61, 0x51, 0xd0, 0x3b, 0x28, 0x48, 0x08, 0x14, 0xeb, 0xf1, 0xf4, 0xe7, 0xab, 0xd7, 0xaf, 0x12, 0xbb, 0x57, + 0xf8, 0xf8, 0x2e, 0xe6, 0xe5, 0xfe, 0xe3, 0x6b, 0x3c, 0x2e, 0xe4, 0x6c, 0xa3, 0x6b, 0x0b, 0x3a, 0xde, 0xfd, 0xc6, + 0x10, 0x18, 0xe1, 0xfb, 0xb6, 0xe9, 0x70, 0x04, 0xaf, 0x0c, 0xe6, 0x43, 0x26, 0x71, 0xfd, 0xc2, 0x3f, 0xa9, 0x4d, + 0x8e, 0x39, 0xfa, 0xfe, 0x68, 0x75, 0x71, 0xb7, 0x64, 0xc4, 0x8c, 0x73, 0x0e, 0x07, 0x23, 0x8c, 0x31, 0xa3, 0x3a, + 0x9b, 0x2e, 0x99, 0x69, 0x6c, 0xed, 0x47, 0xcc, 0xd6, 0x6b, 0xfc, 0x55, 0x7a, 0xac, 0xd7, 0xfb, 0x84, 0x70, 0x43, + 0xaf, 0x88, 0x5e, 0xad, 0x38, 0x21, 0x1c, 0xe1, 0xb7, 0x9c, 0x2c, 0xa9, 0x9f, 0x10, 0x9c, 0x6c, 0xb0, 0x3d, 0x53, + 0x4b, 0x65, 0xe0, 0x04, 0xfc, 0xc2, 0x0a, 0xcd, 0x8a, 0x54, 0x0b, 0x5c, 0xb0, 0x71, 0x0e, 0xe3, 0xd8, 0x6f, 0xe3, + 0x29, 0x55, 0x8f, 0xa7, 0x54, 0x4c, 0xd8, 0x28, 0xfd, 0x2a, 0xd7, 0x98, 0x09, 0x12, 0x8d, 0xb9, 0xa0, 0x39, 0xff, + 0xca, 0x46, 0x91, 0x3b, 0x17, 0x1e, 0xe9, 0x3d, 0x76, 0xab, 0x99, 0x18, 0xa9, 0xbd, 0xe7, 0xef, 0x7e, 0x7d, 0xe9, + 0x16, 0xb3, 0x76, 0x56, 0xa0, 0xa5, 0x5a, 0xcc, 0x59, 0x11, 0x23, 0xec, 0xce, 0x8a, 0xa7, 0xdc, 0xd0, 0xc9, 0x5f, + 0xe9, 0xdc, 0xa6, 0x70, 0xf5, 0xfb, 0x7c, 0x44, 0x35, 0x7b, 0xc3, 0xc4, 0x88, 0x8b, 0x09, 0xd9, 0x6f, 0xdb, 0xf4, + 0x29, 0x75, 0x19, 0xa3, 0x32, 0xe9, 0xfa, 0xde, 0xd3, 0xdc, 0xcc, 0xbd, 0xfc, 0x5c, 0xc4, 0x68, 0xad, 0x34, 0xd5, + 0x3c, 0xdb, 0xa3, 0xa3, 0xd1, 0x0b, 0xc1, 0x35, 0x37, 0x23, 0x2c, 0x60, 0x89, 0x00, 0x57, 0x99, 0x3d, 0x35, 0xfc, + 0xc8, 0x63, 0x84, 0xe3, 0xd8, 0x9d, 0x05, 0x53, 0xe4, 0xd6, 0xec, 0xe0, 0xa0, 0xa2, 0xfc, 0x3d, 0x96, 0xda, 0x4c, + 0xd2, 0x1f, 0xa0, 0x64, 0xbe, 0x50, 0xb0, 0xd8, 0xbe, 0x0b, 0x38, 0x68, 0xe4, 0x50, 0xb1, 0xe2, 0x0b, 0x1b, 0x95, + 0x08, 0xa2, 0x62, 0xb4, 0xdc, 0xe8, 0xc3, 0x6d, 0x0f, 0x4d, 0xfa, 0x83, 0x6e, 0x48, 0xc2, 0x99, 0x43, 0x76, 0xcb, + 0xa9, 0x70, 0xa6, 0x4a, 0xa2, 0x12, 0xc3, 0x81, 0x5a, 0x12, 0x16, 0x45, 0xfc, 0xfc, 0xe6, 0xb1, 0x00, 0x1e, 0x22, + 0xa4, 0x1c, 0xfe, 0xcc, 0x7d, 0xfa, 0xc5, 0x1c, 0x1e, 0x0a, 0x0b, 0x84, 0xb5, 0x1d, 0xa9, 0x42, 0x68, 0x8d, 0xb0, + 0xf6, 0xc3, 0xb5, 0x44, 0xc9, 0xf3, 0x45, 0x70, 0x6a, 0x93, 0xb7, 0xdc, 0x1c, 0xdb, 0x40, 0xdb, 0xa8, 0x66, 0x07, + 0x07, 0x31, 0x4b, 0x4a, 0xc4, 0x20, 0xfb, 0x6d, 0xb7, 0x48, 0x01, 0xb4, 0xbe, 0x31, 0x6e, 0xe8, 0xd9, 0x30, 0x38, + 0xfb, 0x2c, 0x11, 0xf2, 0x61, 0x96, 0x31, 0xa5, 0x64, 0x71, 0x70, 0xb0, 0x6f, 0xca, 0x97, 0x9c, 0x05, 0x2c, 0xe2, + 0xeb, 0x1b, 0x51, 0x0d, 0x01, 0x55, 0xa7, 0xad, 0xe7, 0x9b, 0x48, 0xc5, 0x37, 0x79, 0x26, 0x24, 0x8d, 0xae, 0xaf, + 0xa3, 0x86, 0xc6, 0x0e, 0x0e, 0x13, 0xe6, 0xbb, 0xbe, 0x7b, 0xc2, 0x2c, 0x5b, 0x68, 0x98, 0x90, 0x2d, 0xd0, 0xec, + 0xe4, 0x07, 0xe3, 0xfa, 0x90, 0xb0, 0xc6, 0x0a, 0xad, 0x83, 0x15, 0xdd, 0xd9, 0xb4, 0xe1, 0x6f, 0xec, 0xd2, 0x2d, + 0x27, 0x86, 0xa7, 0x08, 0xd6, 0xb1, 0xcf, 0x06, 0x6b, 0x6c, 0x60, 0xef, 0x67, 0x23, 0xcd, 0x40, 0xfb, 0x7a, 0xd0, + 0x75, 0xf9, 0x44, 0x59, 0xc8, 0x15, 0xec, 0x9f, 0x05, 0x53, 0xda, 0x22, 0x72, 0xac, 0xb1, 0xc4, 0x70, 0x46, 0x6d, + 0x32, 0x9d, 0x35, 0x96, 0x74, 0xd7, 0xd8, 0x5e, 0xcf, 0xe1, 0x6c, 0x54, 0x80, 0xd4, 0xdf, 0xc7, 0x27, 0x18, 0xab, + 0x46, 0xab, 0xd5, 0x5b, 0xee, 0x5b, 0xa9, 0xd6, 0xb2, 0xe4, 0xd7, 0x36, 0x16, 0x85, 0x09, 0xe4, 0x0e, 0xe7, 0xfd, + 0xb6, 0x1b, 0xbf, 0x18, 0x90, 0xfd, 0x56, 0x89, 0xc5, 0x0e, 0xac, 0x76, 0x3c, 0x16, 0x8a, 0xaf, 0x6d, 0x53, 0xc8, + 0x9c, 0xf5, 0x35, 0x7c, 0x49, 0xa6, 0x5b, 0xb8, 0x3a, 0x25, 0x7d, 0xe0, 0x3a, 0x92, 0xe9, 0xe0, 0x5b, 0xf8, 0xe4, + 0x29, 0x42, 0xac, 0xb7, 0xf3, 0x2a, 0xc2, 0xf1, 0xa5, 0x4e, 0x38, 0x36, 0xa6, 0x11, 0xcd, 0xcb, 0x2a, 0x51, 0x89, + 0x66, 0x6e, 0xab, 0x57, 0x59, 0x58, 0x98, 0xc1, 0x54, 0x53, 0x0a, 0x9a, 0x78, 0x45, 0x67, 0x4c, 0xc5, 0x0c, 0xe1, + 0x6f, 0x15, 0xb0, 0xf8, 0x09, 0x45, 0x06, 0xc1, 0x19, 0xaa, 0xe0, 0x0c, 0x05, 0x76, 0x17, 0x98, 0xb4, 0xfa, 0x96, + 0x53, 0x98, 0xf5, 0xd5, 0xa0, 0xe2, 0xed, 0x82, 0xc9, 0x9b, 0xc3, 0xd9, 0x21, 0xb8, 0x87, 0x9f, 0x4d, 0xb3, 0x40, + 0x33, 0x2c, 0x84, 0x42, 0x78, 0xbf, 0xb5, 0xb9, 0x92, 0xbe, 0x54, 0x35, 0xc7, 0xfe, 0x00, 0xd6, 0xc1, 0x1c, 0x1b, + 0x09, 0x57, 0xe6, 0x6f, 0x6d, 0xab, 0x01, 0xd8, 0xae, 0x00, 0x33, 0x92, 0x71, 0x4e, 0x75, 0xdc, 0x3e, 0x6a, 0x01, + 0x63, 0xfa, 0x85, 0xc1, 0xa9, 0x82, 0xd0, 0xf6, 0x54, 0x58, 0xb2, 0x10, 0x6a, 0xca, 0xc7, 0x3a, 0xfe, 0x5d, 0x18, + 0xa2, 0xc2, 0x72, 0xc5, 0x40, 0xc2, 0x09, 0xd8, 0x63, 0x43, 0x70, 0x7e, 0x17, 0xd0, 0x4f, 0xb7, 0x3c, 0x88, 0xdc, + 0x48, 0x0d, 0xe1, 0x02, 0xf2, 0x50, 0xb1, 0xd6, 0x15, 0x99, 0x29, 0x19, 0x37, 0xe0, 0x1e, 0xdb, 0x3d, 0xdb, 0x62, + 0xea, 0xa8, 0x81, 0x08, 0x38, 0x58, 0x91, 0x86, 0x24, 0xc2, 0x25, 0xea, 0x44, 0xcb, 0x97, 0xf2, 0x86, 0x15, 0x8f, + 0x29, 0x0c, 0x3e, 0xb5, 0xd5, 0xd7, 0xf6, 0x28, 0x30, 0x14, 0x5f, 0x77, 0x3d, 0xbe, 0x5c, 0x9b, 0x89, 0xbf, 0x29, + 0xe4, 0x8c, 0x2b, 0x06, 0x7c, 0x9b, 0x85, 0xbf, 0x80, 0x8d, 0x66, 0x76, 0x24, 0x1c, 0x37, 0xac, 0xc4, 0xaf, 0x87, + 0x2f, 0xeb, 0xf8, 0x75, 0x7d, 0xef, 0xe9, 0xc4, 0x53, 0xc0, 0xfa, 0x3e, 0x46, 0x38, 0x76, 0xe2, 0x45, 0x70, 0xd2, + 0x25, 0x53, 0xe4, 0x8e, 0xf9, 0xd5, 0x4a, 0x07, 0x62, 0x5c, 0x8d, 0x73, 0x64, 0x76, 0xdb, 0xa0, 0x35, 0x1d, 0x8d, + 0x80, 0xc5, 0x2b, 0x64, 0x9e, 0x07, 0x87, 0x15, 0x16, 0xdd, 0xf2, 0x78, 0xba, 0xbe, 0xf7, 0xf4, 0xea, 0x7b, 0x27, + 0x14, 0xe4, 0x87, 0x87, 0x94, 0x1f, 0xa8, 0x18, 0xb1, 0x02, 0xe4, 0xca, 0x60, 0xb5, 0xdc, 0x39, 0xfb, 0x58, 0x0a, + 0xc1, 0x32, 0xcd, 0x46, 0x20, 0xb4, 0x08, 0xa2, 0x93, 0xa9, 0x54, 0xba, 0x4c, 0xac, 0x46, 0x2f, 0x42, 0x21, 0x34, + 0xc9, 0x68, 0x9e, 0xc7, 0x56, 0x40, 0x99, 0xc9, 0x2f, 0x6c, 0xc7, 0xa8, 0xbb, 0xb5, 0x21, 0x97, 0xcd, 0xb0, 0xa0, + 0x19, 0x96, 0xa8, 0x79, 0xce, 0x33, 0x56, 0x1e, 0x5e, 0x57, 0x09, 0x17, 0x23, 0x76, 0x0b, 0x74, 0x04, 0x5d, 0x5e, + 0x5e, 0xb6, 0x70, 0x1b, 0xad, 0x2d, 0xc0, 0x97, 0x5b, 0x80, 0xfd, 0xce, 0xb1, 0x69, 0x05, 0xf1, 0xe5, 0x4e, 0xb2, + 0x86, 0x82, 0xb3, 0x92, 0x7b, 0x41, 0xcb, 0x92, 0x67, 0x84, 0x47, 0x2c, 0x67, 0x9a, 0x79, 0x72, 0x0e, 0xcc, 0xb4, + 0xdd, 0xba, 0x6f, 0x4b, 0xf8, 0x95, 0xe8, 0xe4, 0x77, 0x99, 0x5f, 0x73, 0x55, 0x8a, 0xee, 0xd5, 0xf2, 0x54, 0xd0, + 0xee, 0x69, 0xbb, 0x3c, 0x54, 0x6b, 0x9a, 0x4d, 0xad, 0xc4, 0x1e, 0x6f, 0x4d, 0xa9, 0x6a, 0xc3, 0x91, 0xf6, 0x72, + 0x13, 0xfd, 0x59, 0xb8, 0x61, 0xee, 0x02, 0xc1, 0x95, 0x23, 0x0a, 0x0c, 0x84, 0x40, 0xbb, 0x6c, 0x8f, 0x69, 0x9e, + 0x0f, 0x69, 0xf6, 0xb9, 0x8e, 0xfd, 0x15, 0x1a, 0x90, 0x4d, 0x6a, 0x1c, 0x64, 0x05, 0x24, 0x2b, 0x9c, 0xb7, 0xa7, + 0xd2, 0xb5, 0x8d, 0x12, 0xef, 0xb7, 0x2a, 0xb4, 0xaf, 0x2f, 0xf4, 0x37, 0xb1, 0xdd, 0x8c, 0x48, 0xb8, 0x99, 0xc5, + 0x40, 0x05, 0xfe, 0x25, 0xc6, 0x79, 0x7a, 0xe0, 0xf0, 0x0e, 0x04, 0x8f, 0xf5, 0xc6, 0x40, 0x34, 0x5a, 0xae, 0x47, + 0x5c, 0x7d, 0x1b, 0x02, 0xff, 0x5b, 0x46, 0xf9, 0x24, 0xe8, 0xe1, 0xdf, 0x1d, 0x68, 0x49, 0xe3, 0x1c, 0xe3, 0x5c, + 0x8e, 0xcc, 0x31, 0x14, 0x9e, 0xd0, 0xfc, 0x02, 0xcc, 0x8b, 0xc1, 0xf7, 0xd7, 0x36, 0xcb, 0xf0, 0x65, 0x30, 0x0c, + 0xd5, 0x0d, 0x19, 0x8a, 0x1a, 0x0a, 0x38, 0xa2, 0x2a, 0xcc, 0x99, 0x2b, 0x6b, 0xa2, 0xa4, 0xe3, 0xda, 0xad, 0x38, + 0xee, 0x68, 0x6e, 0x41, 0xe2, 0x38, 0x56, 0x20, 0xcd, 0x79, 0xfe, 0xbe, 0x9a, 0x85, 0xda, 0x9a, 0x85, 0x4a, 0x02, + 0x69, 0x0b, 0x55, 0xc8, 0x1c, 0x54, 0x4f, 0xb5, 0x40, 0x61, 0x29, 0x60, 0x59, 0x13, 0xa0, 0xd0, 0xa8, 0x24, 0xb8, + 0x39, 0xd1, 0xb8, 0x70, 0xa2, 0x8e, 0xc3, 0x35, 0x20, 0x19, 0x55, 0x15, 0x89, 0xec, 0xe6, 0xa8, 0xc9, 0xbe, 0x12, + 0x17, 0x68, 0x83, 0xbf, 0x5f, 0xaf, 0x1d, 0x94, 0x18, 0x72, 0xab, 0x53, 0x63, 0x8c, 0x03, 0xb0, 0x60, 0x49, 0x1c, + 0x33, 0x6c, 0x59, 0x9f, 0x4d, 0xe0, 0x94, 0xed, 0xee, 0x13, 0x22, 0x2b, 0xd8, 0xd4, 0x98, 0x4a, 0xcf, 0x5d, 0x49, + 0x84, 0xa9, 0x67, 0x4b, 0x8b, 0x6a, 0xe2, 0x84, 0x44, 0x5e, 0x3b, 0x11, 0xf5, 0x96, 0x35, 0xe1, 0x30, 0x0d, 0x8a, + 0xad, 0x53, 0x20, 0xaa, 0xc5, 0x2e, 0x78, 0xef, 0xc2, 0x9a, 0x5a, 0x3b, 0x01, 0xc4, 0x8b, 0x1a, 0xc4, 0x03, 0xd0, + 0x4a, 0x4b, 0xbc, 0xe4, 0x80, 0xd0, 0x7a, 0xe5, 0x98, 0xe1, 0xc2, 0x2e, 0xc4, 0x16, 0x14, 0x37, 0xd9, 0x4f, 0x83, + 0x85, 0x20, 0xcb, 0x2a, 0xe0, 0xef, 0xc2, 0x23, 0x22, 0x86, 0xc1, 0x8b, 0xd5, 0x6a, 0x0b, 0xed, 0x76, 0x72, 0xa1, + 0x28, 0xa9, 0xa4, 0xc3, 0xd5, 0xea, 0xab, 0x44, 0xb1, 0xe3, 0x7f, 0x31, 0x43, 0x3d, 0x4f, 0x74, 0x1f, 0xbe, 0x84, + 0x52, 0x86, 0x1d, 0xad, 0x52, 0x4a, 0xc1, 0xa1, 0x8e, 0xb5, 0xf5, 0x85, 0xd2, 0x01, 0xe5, 0x7e, 0xbc, 0x45, 0xc0, + 0x4c, 0xa2, 0x3b, 0xa9, 0xab, 0x29, 0x3f, 0x76, 0x4d, 0x0b, 0x84, 0x50, 0xaa, 0x8c, 0x2c, 0xb3, 0xbf, 0x4b, 0xbe, + 0x3c, 0x38, 0x50, 0x41, 0x43, 0xd7, 0x25, 0xa5, 0xf8, 0x3b, 0x86, 0x53, 0x59, 0xdd, 0x09, 0xc3, 0xbe, 0xfc, 0xed, + 0xcf, 0xa1, 0x2d, 0xe9, 0xb4, 0xd5, 0x05, 0xc1, 0x9c, 0xde, 0x50, 0xae, 0xf7, 0xca, 0x56, 0xac, 0x60, 0x1e, 0x33, + 0xb4, 0x74, 0xdc, 0x46, 0x52, 0x30, 0xe0, 0x1f, 0x81, 0x2c, 0x78, 0x2e, 0xda, 0x22, 0x7e, 0x36, 0x65, 0xa0, 0xca, + 0xf6, 0x8c, 0x44, 0x29, 0x1e, 0xee, 0xbb, 0x83, 0xc4, 0x35, 0xbc, 0x7b, 0xec, 0xeb, 0xcd, 0xea, 0x35, 0x69, 0x60, + 0xce, 0x8a, 0xb1, 0x2c, 0x66, 0x3e, 0x6f, 0xbd, 0xf1, 0xed, 0x88, 0x23, 0x1f, 0xc7, 0x3b, 0xdb, 0x76, 0x22, 0x40, + 0x77, 0x43, 0xf6, 0xae, 0xa4, 0xf6, 0xda, 0x69, 0x5a, 0x1e, 0xc0, 0x56, 0x41, 0xe8, 0x31, 0x53, 0x85, 0x52, 0xbe, + 0x53, 0xaf, 0x76, 0xad, 0xee, 0x64, 0xbf, 0xdd, 0x2d, 0x25, 0x3f, 0x8f, 0x0d, 0x5d, 0xab, 0xe3, 0x70, 0xa7, 0xaa, + 0x5c, 0xe4, 0x23, 0x37, 0x58, 0x81, 0x30, 0x73, 0x78, 0x74, 0xc3, 0xf3, 0xbc, 0x4a, 0xfd, 0x4f, 0x48, 0xbb, 0x72, + 0xa4, 0x5d, 0x7a, 0xd2, 0x0e, 0xa4, 0x02, 0x48, 0xbb, 0x6d, 0xae, 0xaa, 0x2e, 0xb7, 0xb6, 0xa7, 0xb4, 0x44, 0x5d, + 0x19, 0x71, 0x1a, 0xfa, 0x5b, 0xf8, 0x11, 0xa0, 0x92, 0xf9, 0xfa, 0x1c, 0x3b, 0x7d, 0x0c, 0x88, 0x81, 0x56, 0xa7, + 0xc9, 0x42, 0x4d, 0xc5, 0xe7, 0x18, 0x61, 0xb5, 0x66, 0x25, 0x66, 0x3f, 0x7c, 0x0a, 0x4a, 0xbb, 0x60, 0x3a, 0x70, + 0x8e, 0x99, 0xe4, 0xff, 0x88, 0x8f, 0xf2, 0xb3, 0x13, 0x6e, 0x76, 0xca, 0xcf, 0x0e, 0x68, 0x7d, 0x35, 0xbb, 0xd1, + 0xf7, 0xa9, 0xbd, 0x99, 0x9e, 0x28, 0xa7, 0x57, 0xad, 0xf7, 0x6a, 0x15, 0x6f, 0xa4, 0x80, 0x46, 0xdf, 0x49, 0x29, + 0x45, 0xd9, 0x3a, 0xd0, 0x80, 0x10, 0x32, 0x90, 0xb0, 0xb6, 0x93, 0x2e, 0x4f, 0xb9, 0x97, 0xff, 0x4a, 0xcf, 0x63, + 0x14, 0xf7, 0xb6, 0xfe, 0x63, 0x39, 0x9b, 0x03, 0x43, 0xb6, 0x81, 0xd2, 0x13, 0xe6, 0x3a, 0xac, 0xf2, 0xd7, 0x3b, + 0xd2, 0x6a, 0x75, 0xcc, 0x7e, 0xac, 0x61, 0x53, 0x29, 0x35, 0xef, 0xb7, 0xd6, 0x8b, 0x32, 0xa9, 0x24, 0x1c, 0xbb, + 0x74, 0x2b, 0x8f, 0x37, 0x35, 0x33, 0x3e, 0xe3, 0x75, 0x2c, 0x2c, 0x1d, 0x16, 0x40, 0xeb, 0x02, 0xf2, 0xe3, 0xd1, + 0x3d, 0x5c, 0xff, 0x75, 0x05, 0x9c, 0xe5, 0x7a, 0x03, 0x7c, 0xcb, 0xf5, 0xfa, 0x91, 0x76, 0x92, 0x36, 0x7e, 0xb4, + 0x43, 0xee, 0x2d, 0xa1, 0x57, 0x65, 0x3a, 0x99, 0xb1, 0x3f, 0x80, 0xb4, 0x2d, 0x16, 0x92, 0x2c, 0x67, 0x72, 0xc4, + 0xd2, 0x48, 0xce, 0x99, 0x88, 0xd6, 0xa0, 0x67, 0x75, 0x08, 0xf0, 0x8f, 0x88, 0x97, 0x6f, 0xeb, 0xfa, 0xd6, 0xf4, + 0x91, 0x5e, 0x83, 0x2a, 0xec, 0x25, 0xdf, 0xa1, 0x8c, 0x7d, 0xcf, 0x0a, 0x65, 0x78, 0xd2, 0x92, 0xbd, 0x7d, 0xc9, + 0xab, 0x03, 0xea, 0x25, 0x4f, 0xbf, 0x5d, 0xa5, 0x12, 0x48, 0xa2, 0x76, 0x72, 0x96, 0x1c, 0x47, 0xc8, 0x68, 0x8c, + 0x9f, 0x79, 0x8d, 0xf1, 0xa2, 0xd4, 0x18, 0x3f, 0xd7, 0x64, 0xb1, 0xa1, 0x31, 0xfe, 0x43, 0x90, 0xe7, 0xba, 0xf7, + 0xdc, 0x6b, 0xd3, 0xdf, 0xc8, 0x9c, 0x67, 0x77, 0x71, 0x94, 0x73, 0xdd, 0x84, 0xdb, 0xc4, 0x08, 0x2f, 0x6d, 0x06, + 0xa8, 0x1a, 0x8d, 0xbe, 0x7b, 0xed, 0xe5, 0x3f, 0x2c, 0x04, 0x89, 0xee, 0xe5, 0x5c, 0xdf, 0x8b, 0xf0, 0x54, 0x93, + 0x4f, 0xf0, 0xeb, 0xde, 0x32, 0xfe, 0x95, 0xea, 0x69, 0x52, 0x50, 0x31, 0x92, 0xb3, 0x18, 0x35, 0xa2, 0x08, 0x25, + 0xca, 0x08, 0x21, 0x0f, 0xd0, 0xfa, 0xde, 0x27, 0xfc, 0x4a, 0x92, 0xa8, 0x17, 0x35, 0xa6, 0x1a, 0x6b, 0x4a, 0x3e, + 0x5d, 0xdc, 0x5b, 0xbe, 0x92, 0xeb, 0xcb, 0x4f, 0xf8, 0xa9, 0x2e, 0xd5, 0xfa, 0xf8, 0x96, 0x91, 0x18, 0x91, 0xcb, + 0xa7, 0x7e, 0x48, 0x8f, 0xe5, 0xcc, 0x2a, 0xf8, 0x23, 0x84, 0xbf, 0x80, 0x5e, 0xf7, 0x92, 0x57, 0x44, 0xc8, 0xdd, + 0xc1, 0xec, 0x93, 0x48, 0x1a, 0xe5, 0x41, 0x74, 0x70, 0x10, 0xa4, 0x95, 0x2c, 0x04, 0xfe, 0x5b, 0x92, 0x9a, 0xa8, + 0x8e, 0x19, 0x85, 0x96, 0xfe, 0x96, 0x31, 0x47, 0xbe, 0x99, 0xd8, 0x6b, 0xaa, 0xdd, 0x8e, 0xe5, 0x7d, 0xab, 0x7b, + 0x48, 0xb8, 0x66, 0x05, 0xd5, 0xb2, 0x18, 0xa0, 0x90, 0x2d, 0xc1, 0x5f, 0x39, 0xf9, 0xd4, 0xdf, 0xfb, 0x7f, 0xfe, + 0xc7, 0x5f, 0xe3, 0xbf, 0x8a, 0xc1, 0x27, 0x2c, 0x18, 0x39, 0xba, 0x88, 0x7b, 0x69, 0xbc, 0xdf, 0x6c, 0xae, 0xfe, + 0x3a, 0xea, 0xff, 0x37, 0x6d, 0x7e, 0x7d, 0xd8, 0xfc, 0x73, 0x80, 0x56, 0xf1, 0x5f, 0x47, 0xbd, 0xbe, 0xfb, 0xea, + 0xff, 0xf7, 0xe5, 0x5f, 0x6a, 0x70, 0x68, 0x13, 0xef, 0x21, 0x74, 0x34, 0xc1, 0xbf, 0x08, 0x72, 0xd4, 0x6c, 0x5e, + 0x1e, 0x4d, 0xf0, 0x4f, 0x82, 0x1c, 0xc1, 0xdf, 0x3b, 0x4d, 0xde, 0xb2, 0xc9, 0xd3, 0xdb, 0x79, 0xfc, 0xe9, 0x72, + 0x75, 0x6f, 0xf9, 0x95, 0xaf, 0xa1, 0xdd, 0xfe, 0x7f, 0xff, 0xf5, 0x97, 0x8a, 0x7e, 0xbc, 0x24, 0x47, 0x83, 0x06, + 0x8a, 0x4d, 0xf2, 0x21, 0xb1, 0x7f, 0xe2, 0x5e, 0xda, 0xff, 0x6f, 0x37, 0x94, 0xe8, 0xc7, 0xbf, 0x3e, 0x5d, 0x5c, + 0x92, 0xc1, 0x2a, 0x8e, 0x56, 0x3f, 0xa2, 0x15, 0x42, 0xab, 0x7b, 0xe8, 0x13, 0x8e, 0x26, 0x11, 0xc2, 0xbf, 0x09, + 0x72, 0xf4, 0xe3, 0xd1, 0x04, 0xff, 0x29, 0xc8, 0x51, 0x74, 0x34, 0xc1, 0xef, 0x25, 0x39, 0xfa, 0xef, 0xb8, 0x97, + 0x5a, 0x25, 0xdc, 0xca, 0xa8, 0x3f, 0x56, 0x70, 0x13, 0x42, 0x0b, 0x46, 0x57, 0x9a, 0xeb, 0x9c, 0xa1, 0x7b, 0x47, + 0x1c, 0x3f, 0x92, 0x00, 0xac, 0x58, 0x83, 0x92, 0xc6, 0x5c, 0xc2, 0x2e, 0xaf, 0x61, 0xe1, 0x01, 0x83, 0xee, 0xa5, + 0x1c, 0x5b, 0x3d, 0x81, 0x4a, 0xb5, 0xbd, 0xbd, 0x55, 0x70, 0x7d, 0x8b, 0x1f, 0x93, 0x47, 0x32, 0x6e, 0x23, 0xcc, + 0x29, 0xfc, 0xe8, 0x20, 0xfc, 0x41, 0xbb, 0x0b, 0x4f, 0xd8, 0xe6, 0x16, 0xc3, 0x84, 0xb4, 0xfc, 0x4c, 0x84, 0xf0, + 0xcb, 0x1d, 0x99, 0x7a, 0x0a, 0xea, 0x07, 0x84, 0x7f, 0xae, 0x5d, 0x8f, 0xe2, 0xc7, 0x9a, 0x94, 0xc8, 0xf1, 0xae, + 0x60, 0xec, 0x03, 0xcd, 0x3f, 0xb3, 0x22, 0x7e, 0xaa, 0x71, 0xbb, 0xf3, 0x00, 0x1b, 0x55, 0xf5, 0x7e, 0x1b, 0x75, + 0xcb, 0xdb, 0xad, 0xe7, 0xd2, 0xde, 0x27, 0xc0, 0x29, 0x5c, 0xd7, 0xd7, 0xc0, 0xda, 0xef, 0xf3, 0x2d, 0xa5, 0x56, + 0x41, 0x6f, 0x22, 0x54, 0xbf, 0x4a, 0xe5, 0xe2, 0x0b, 0xcd, 0xf9, 0x68, 0x4f, 0xb3, 0xd9, 0x3c, 0xa7, 0x9a, 0xed, + 0xb9, 0x39, 0xef, 0x51, 0x68, 0x28, 0x2a, 0x79, 0x8a, 0x3f, 0x44, 0xb5, 0x69, 0xff, 0x10, 0x49, 0xb5, 0x77, 0x62, + 0xb8, 0xcf, 0x72, 0x7c, 0x89, 0xa0, 0xe5, 0x75, 0xd9, 0xe6, 0x8d, 0x60, 0xb3, 0x0d, 0xca, 0xb2, 0x81, 0x39, 0xbf, + 0x15, 0x86, 0xfb, 0x4d, 0x42, 0x3a, 0xbd, 0xe8, 0x42, 0x7d, 0x99, 0x5c, 0x46, 0x70, 0x93, 0x53, 0x10, 0xc1, 0x8c, + 0xf2, 0x08, 0x4a, 0x50, 0xd2, 0xea, 0xd2, 0x0b, 0xd6, 0xa5, 0x8d, 0x86, 0x67, 0xb3, 0x33, 0xc2, 0xfb, 0xd4, 0xd6, + 0xcf, 0xf1, 0x14, 0x8f, 0x48, 0xb3, 0x8d, 0x17, 0xa4, 0x65, 0xaa, 0x74, 0x17, 0x17, 0x99, 0xeb, 0xe7, 0xe0, 0x20, + 0x2e, 0x92, 0x9c, 0x2a, 0xfd, 0x02, 0x34, 0x02, 0x64, 0x81, 0xa7, 0xa4, 0x48, 0xd8, 0x2d, 0xcb, 0xe2, 0x0c, 0xe1, + 0xa9, 0xa3, 0x41, 0xa8, 0x8b, 0x16, 0x24, 0x28, 0x06, 0x72, 0x06, 0x11, 0xac, 0x37, 0xed, 0xb7, 0x07, 0x84, 0x90, + 0x68, 0xbf, 0xd9, 0x8c, 0x7a, 0x05, 0xf9, 0x45, 0xa4, 0x90, 0x12, 0xb0, 0xd3, 0xe4, 0x27, 0x48, 0xea, 0x04, 0x49, + 0xf1, 0x7b, 0x99, 0x68, 0xa6, 0x74, 0x0c, 0xc9, 0xa0, 0x24, 0x50, 0x1e, 0xc3, 0xa3, 0x8b, 0xa3, 0xa8, 0x01, 0xa9, + 0x06, 0x45, 0x11, 0x2e, 0xc8, 0x9d, 0x46, 0xe9, 0xb4, 0x7f, 0x3c, 0x08, 0xcf, 0x08, 0x9b, 0x0a, 0xfd, 0xdf, 0xe9, + 0xde, 0xb4, 0xdf, 0x32, 0xfd, 0x5f, 0x46, 0xbd, 0xb8, 0x20, 0xca, 0xb2, 0x71, 0x3d, 0x95, 0x0a, 0x66, 0xe6, 0x8b, + 0x52, 0x37, 0x40, 0xd7, 0xf7, 0x88, 0x34, 0x3b, 0x69, 0x3c, 0x0a, 0x67, 0xd2, 0x84, 0x0e, 0x1d, 0x28, 0x70, 0x4e, + 0xa0, 0x3c, 0x2e, 0x08, 0x74, 0x5a, 0x55, 0xbb, 0xd3, 0xa9, 0x4b, 0xf8, 0x31, 0xfa, 0xb1, 0xf7, 0xa7, 0x48, 0x7f, + 0x13, 0x76, 0x04, 0x7f, 0x8a, 0xd5, 0x0a, 0xfe, 0xfe, 0x26, 0x7a, 0x30, 0x2c, 0x93, 0xf6, 0x8b, 0x4b, 0xfb, 0x09, + 0xd2, 0x04, 0x4b, 0xcd, 0x80, 0xb1, 0x2a, 0xf9, 0x31, 0xbb, 0x38, 0x63, 0x62, 0x67, 0x70, 0x70, 0xc0, 0xfb, 0xb4, + 0xd1, 0x1e, 0xc0, 0x8d, 0x40, 0xa1, 0xd5, 0x07, 0xae, 0xa7, 0x71, 0x74, 0x74, 0x19, 0xa1, 0x5e, 0xb4, 0x07, 0xab, + 0xdc, 0x95, 0x0d, 0xe2, 0x60, 0x9d, 0x35, 0x34, 0x4d, 0x47, 0x97, 0xa4, 0xd5, 0x8b, 0x85, 0x25, 0xf2, 0x39, 0xc2, + 0x99, 0xa3, 0xa9, 0x2d, 0x3c, 0x42, 0x0d, 0x21, 0x1a, 0xfe, 0x7b, 0x84, 0x1a, 0x53, 0xdd, 0x18, 0xa3, 0x34, 0x83, + 0xbf, 0xf1, 0x88, 0x10, 0xd2, 0xec, 0x94, 0x15, 0xfd, 0x61, 0x49, 0x51, 0x3a, 0xf6, 0xea, 0xd1, 0xbe, 0xd9, 0x1c, + 0xb2, 0x11, 0xf3, 0x3e, 0x1b, 0xac, 0x56, 0xd1, 0x45, 0xef, 0x32, 0x42, 0x8d, 0xd8, 0xa3, 0xdd, 0x91, 0xc7, 0x3b, + 0x84, 0xb0, 0x18, 0xac, 0xdd, 0x0d, 0xd4, 0x0d, 0xab, 0xdd, 0x36, 0x2d, 0xab, 0xfd, 0x1f, 0x90, 0x05, 0xb6, 0x2e, + 0xe5, 0x1e, 0xcb, 0xdf, 0xce, 0x61, 0xaa, 0x1e, 0xb7, 0x25, 0x69, 0xe1, 0x82, 0x78, 0x75, 0x37, 0x25, 0xba, 0xc2, + 0xff, 0x8c, 0x54, 0xc5, 0x71, 0x3f, 0xc7, 0xd3, 0x01, 0x11, 0xd4, 0xc8, 0x2f, 0x5d, 0xaf, 0x4c, 0x67, 0x39, 0xb9, + 0x61, 0x1b, 0xf7, 0xbf, 0x39, 0xdc, 0xc9, 0x3c, 0xd6, 0x49, 0xb6, 0x28, 0x0a, 0x26, 0xf4, 0x2b, 0x39, 0x72, 0x8c, + 0x1d, 0xcb, 0x41, 0xb6, 0x82, 0x8b, 0x5d, 0x0c, 0x5c, 0x5d, 0xc7, 0xef, 0x94, 0xd1, 0x56, 0xf6, 0x82, 0x8c, 0x2c, + 0xc3, 0x65, 0xae, 0x7b, 0xbb, 0x0b, 0x27, 0x4a, 0xc7, 0x08, 0x8f, 0xdc, 0x3d, 0x70, 0x9c, 0x24, 0xc9, 0x22, 0xc9, + 0x20, 0x1b, 0x3a, 0x50, 0x68, 0x6d, 0xf6, 0x55, 0xac, 0xc8, 0x63, 0x9d, 0x08, 0x76, 0x6b, 0xba, 0x8d, 0x51, 0x75, + 0x88, 0xfb, 0xfd, 0x76, 0x41, 0xbb, 0x86, 0x00, 0xa9, 0x44, 0xc8, 0x11, 0x03, 0x08, 0xc1, 0xdd, 0xbf, 0x4b, 0x9a, + 0x52, 0x15, 0xde, 0x6c, 0x55, 0x03, 0xec, 0x87, 0x2a, 0xef, 0x05, 0xe8, 0x89, 0x0d, 0x7b, 0x56, 0x16, 0xb6, 0xca, + 0x73, 0x84, 0xf8, 0x38, 0x5e, 0x24, 0x70, 0x23, 0x68, 0x30, 0x49, 0x08, 0xb4, 0x5a, 0x2d, 0x42, 0xdc, 0x9a, 0x56, + 0x8a, 0xe9, 0x31, 0x99, 0xf6, 0x8b, 0x46, 0xc3, 0x28, 0xaf, 0x47, 0x16, 0x2f, 0x16, 0x08, 0x8f, 0xcb, 0xbd, 0xe6, + 0xcb, 0xcd, 0x49, 0xbd, 0xab, 0x78, 0x5c, 0x57, 0x02, 0x37, 0x84, 0x40, 0x46, 0xbf, 0xa8, 0xa1, 0x75, 0x3c, 0x21, + 0x47, 0x71, 0x3f, 0xe9, 0xfd, 0xcf, 0x01, 0xea, 0xc5, 0xc9, 0x21, 0x3a, 0xb2, 0xb4, 0x64, 0x8c, 0xba, 0x99, 0xed, + 0x63, 0x69, 0x6e, 0x3f, 0xdb, 0xd8, 0x28, 0x20, 0x53, 0x89, 0x05, 0x9d, 0xb1, 0x74, 0x02, 0xbb, 0xde, 0x23, 0xcf, + 0x1c, 0x03, 0x32, 0xa5, 0x13, 0x47, 0x5b, 0x92, 0xa8, 0x27, 0x69, 0xf9, 0xd5, 0x8b, 0x7a, 0xb4, 0xfa, 0xfa, 0x9f, + 0x51, 0x2f, 0xa3, 0xe9, 0x63, 0xbe, 0x76, 0x4a, 0xf2, 0x5a, 0x1f, 0x67, 0xbe, 0x8f, 0xb5, 0x5d, 0x9c, 0x00, 0x78, + 0x23, 0xb4, 0xad, 0x1d, 0x59, 0xa0, 0x35, 0x1f, 0x97, 0xd4, 0x49, 0x25, 0x9a, 0x4e, 0x00, 0xaa, 0xc1, 0x22, 0xa8, + 0xd0, 0x36, 0x20, 0x98, 0x32, 0x60, 0x8b, 0x47, 0x5a, 0x80, 0xe6, 0xe2, 0xb2, 0x85, 0x96, 0xb5, 0xc2, 0x8e, 0xb3, + 0xaa, 0xdf, 0xc5, 0x97, 0xc4, 0x7b, 0x0c, 0x54, 0xf9, 0x62, 0xd1, 0x1d, 0x37, 0x1a, 0x48, 0x79, 0xfc, 0x1a, 0xf5, + 0xc7, 0x03, 0x7c, 0x0b, 0x28, 0x84, 0x6b, 0x18, 0x85, 0x6b, 0x73, 0xec, 0xb8, 0x39, 0x36, 0x1a, 0x72, 0x8d, 0xba, + 0x41, 0xe5, 0x85, 0xab, 0xbc, 0x5e, 0x5b, 0xc8, 0x6c, 0x62, 0xdc, 0x39, 0x32, 0x29, 0x60, 0x08, 0x46, 0x08, 0x79, + 0x25, 0xd1, 0xce, 0x66, 0xa1, 0x51, 0xa8, 0x6e, 0x76, 0x2f, 0x50, 0x54, 0x7b, 0x7a, 0xc4, 0x00, 0x0b, 0xa8, 0x5a, + 0xaa, 0x91, 0xa7, 0x1a, 0x8f, 0x1a, 0x6d, 0x83, 0xee, 0xcd, 0x76, 0xb7, 0xde, 0xd8, 0xfd, 0xaa, 0x31, 0x3c, 0x6a, + 0x90, 0x69, 0xb5, 0xc3, 0xd7, 0xb2, 0xd1, 0x58, 0xd7, 0xef, 0x4b, 0xfd, 0x26, 0xae, 0xdd, 0x5f, 0x3c, 0xdd, 0x32, + 0xf1, 0xf0, 0xa7, 0x6f, 0x75, 0xde, 0x8a, 0x84, 0x0b, 0xc1, 0x0a, 0x38, 0x61, 0x89, 0xc6, 0x62, 0xbd, 0x2e, 0x4f, + 0xfd, 0xdf, 0xb5, 0xb5, 0x19, 0x23, 0x1c, 0xe8, 0x90, 0x91, 0xda, 0xb0, 0xc4, 0x05, 0xa6, 0x86, 0x8a, 0x10, 0x42, + 0x3e, 0x68, 0x6f, 0x1e, 0xa3, 0x0d, 0x49, 0xca, 0x48, 0x70, 0x76, 0xc7, 0x8a, 0xb0, 0xe4, 0xfa, 0xde, 0x63, 0xf9, + 0x5d, 0x91, 0xae, 0x2f, 0x06, 0xa9, 0x29, 0x96, 0x3b, 0x42, 0x96, 0x93, 0x2f, 0x20, 0xe7, 0x94, 0x17, 0x2c, 0x89, + 0x21, 0x88, 0x4f, 0x78, 0xc1, 0x0c, 0xe3, 0x7e, 0xcf, 0xcb, 0x8d, 0x59, 0x9d, 0xd3, 0xcc, 0x42, 0xed, 0x0f, 0x40, + 0x33, 0x07, 0xe5, 0x90, 0x24, 0x5b, 0xc5, 0xae, 0xef, 0x3d, 0x7c, 0xbd, 0x4b, 0x86, 0x5e, 0xad, 0x9c, 0xf4, 0x9c, + 0x01, 0xeb, 0x83, 0xf3, 0x6a, 0xa8, 0x99, 0xfb, 0x91, 0xc6, 0x99, 0x61, 0xa2, 0xf2, 0x98, 0x03, 0x32, 0x5d, 0xdf, + 0x7b, 0xf8, 0x2e, 0xe6, 0x46, 0x37, 0x85, 0x70, 0x38, 0xef, 0xb8, 0x20, 0x31, 0x25, 0x0c, 0xd9, 0xc9, 0x97, 0x74, + 0xac, 0x08, 0x4e, 0xf7, 0x94, 0x9a, 0x4c, 0x10, 0x3b, 0xfa, 0x62, 0x40, 0x32, 0x07, 0x02, 0x92, 0x21, 0x9c, 0xd5, + 0xe4, 0x3a, 0x62, 0xd6, 0xc0, 0x74, 0x76, 0x05, 0x8b, 0x91, 0x58, 0xf6, 0x10, 0xe1, 0xcc, 0x74, 0xab, 0xd7, 0xf6, + 0x38, 0x51, 0x74, 0xd3, 0xd0, 0xad, 0x92, 0x67, 0xdf, 0x83, 0xe0, 0xe5, 0x3f, 0x5e, 0xb9, 0xb6, 0xcb, 0x84, 0x27, + 0xde, 0x22, 0xed, 0xfa, 0xde, 0xc3, 0x5f, 0x9d, 0x51, 0xda, 0x9c, 0x7a, 0xf2, 0xbf, 0x25, 0xa3, 0x3e, 0xfc, 0x35, + 0xa9, 0x72, 0x4d, 0xe1, 0xeb, 0x7b, 0x0f, 0x7f, 0xdf, 0x55, 0x0c, 0xd2, 0xd7, 0x8b, 0x4a, 0x49, 0x60, 0xc6, 0xb7, + 0x64, 0x79, 0xba, 0x74, 0x67, 0x45, 0x2a, 0xd6, 0xd8, 0x9c, 0x50, 0xa9, 0x5a, 0x97, 0xba, 0x95, 0x27, 0x58, 0x12, + 0x73, 0x95, 0x54, 0x5f, 0x36, 0x87, 0xc6, 0x5c, 0x8a, 0xab, 0x4c, 0xce, 0xd9, 0x37, 0xee, 0x97, 0x9e, 0x6a, 0x94, + 0xf0, 0x19, 0x18, 0xe2, 0x98, 0xb1, 0x0b, 0xbc, 0xdf, 0x42, 0xdd, 0x8d, 0xf3, 0x4c, 0x1a, 0x44, 0x2d, 0xea, 0x87, + 0x0d, 0xa6, 0xa4, 0x85, 0x33, 0xd2, 0xc2, 0x39, 0x51, 0xfd, 0x96, 0x3d, 0x31, 0xba, 0x79, 0xd9, 0xb4, 0x3d, 0x77, + 0x60, 0xbb, 0xe7, 0x76, 0xdf, 0xda, 0x43, 0x79, 0xda, 0xcd, 0x8d, 0xfe, 0xd2, 0x1c, 0xf4, 0x53, 0x83, 0x1a, 0x4f, + 0x58, 0x5c, 0xe0, 0xc2, 0xb4, 0x7c, 0xc5, 0x87, 0x39, 0xd8, 0xa9, 0xc0, 0xcc, 0xb0, 0x46, 0x69, 0x59, 0xb6, 0xed, + 0xca, 0xe6, 0x89, 0x59, 0xab, 0x02, 0xe7, 0x09, 0x90, 0x72, 0x9c, 0x3b, 0xbb, 0x1e, 0xb5, 0x5d, 0xe5, 0xec, 0xe0, + 0x20, 0x76, 0x95, 0x68, 0x5c, 0xf8, 0xfc, 0xea, 0x06, 0xf0, 0xbd, 0xa5, 0x1a, 0x53, 0x64, 0x26, 0xd0, 0x68, 0x64, + 0x83, 0x35, 0xdd, 0x27, 0x24, 0xce, 0xeb, 0x50, 0xf4, 0xa3, 0x37, 0xcc, 0xe0, 0x06, 0x00, 0x1a, 0x8d, 0xf2, 0xba, + 0x77, 0x03, 0x62, 0x4f, 0x35, 0x96, 0xeb, 0x2f, 0x71, 0x69, 0x4d, 0xd4, 0xda, 0xb2, 0xc3, 0xf2, 0xa3, 0x40, 0x22, + 0xc4, 0x5d, 0xe1, 0xe7, 0x13, 0x6c, 0x0d, 0x01, 0xe5, 0x5e, 0x38, 0x1b, 0x08, 0x6c, 0xac, 0xb6, 0x5c, 0x21, 0x4f, + 0xda, 0x3a, 0x28, 0xf5, 0x85, 0xe0, 0x82, 0x0b, 0x0a, 0x35, 0xd6, 0x0e, 0xcb, 0x9f, 0xb0, 0x6d, 0x73, 0x4e, 0xac, + 0x90, 0xd3, 0x96, 0x99, 0x61, 0x18, 0x80, 0x75, 0x4a, 0xc0, 0x3c, 0x27, 0x2f, 0xbf, 0x8d, 0xfa, 0x0f, 0x03, 0xd4, + 0x7f, 0x44, 0x58, 0xb0, 0x0d, 0xac, 0xae, 0x24, 0x91, 0x4e, 0x41, 0xa1, 0x7c, 0xd6, 0xe3, 0x39, 0x01, 0x6d, 0x5c, + 0x1d, 0xaa, 0xb5, 0x2b, 0xca, 0x6f, 0x50, 0x96, 0x70, 0xa7, 0x18, 0x7d, 0x26, 0xf6, 0xf7, 0xc9, 0x71, 0x75, 0x41, + 0x07, 0x5d, 0xef, 0x52, 0x0e, 0x86, 0xa4, 0xf0, 0xe1, 0xef, 0xdf, 0xbf, 0x5b, 0x7d, 0x3c, 0xdf, 0xde, 0xc1, 0x81, + 0x59, 0x29, 0xcc, 0x3a, 0xd8, 0xc0, 0x75, 0x23, 0x53, 0xe8, 0xbf, 0xbc, 0x13, 0xaf, 0x53, 0xa1, 0x8d, 0xcd, 0xe8, + 0x8f, 0x43, 0x18, 0x6d, 0xbb, 0x6d, 0x4a, 0xb0, 0xa0, 0x59, 0xa0, 0x4b, 0xd6, 0xb8, 0x95, 0x16, 0xdf, 0x20, 0x23, + 0x0f, 0x4d, 0x01, 0x26, 0x46, 0xbb, 0xb3, 0x1f, 0xad, 0x1d, 0x9e, 0xd8, 0xa1, 0xa1, 0xa5, 0x21, 0x84, 0x16, 0xef, + 0x01, 0x73, 0xec, 0x11, 0x01, 0x20, 0x7a, 0x69, 0x20, 0x55, 0x81, 0x2c, 0x8a, 0x2a, 0x45, 0xfe, 0xf3, 0x7d, 0x42, + 0x5e, 0x56, 0x8a, 0xcc, 0xb7, 0x95, 0x31, 0x17, 0x20, 0x06, 0x4a, 0xe1, 0x22, 0xa1, 0x4c, 0xb0, 0x97, 0xa1, 0x1f, + 0xb4, 0x2f, 0x6f, 0xa4, 0xcd, 0xa4, 0xe2, 0xc6, 0x83, 0x9b, 0x52, 0xa3, 0xe2, 0xb3, 0xf9, 0x1e, 0x12, 0x1b, 0xb9, + 0xf7, 0x20, 0x97, 0x51, 0x33, 0x48, 0xf8, 0x7e, 0x67, 0x4a, 0xfb, 0x76, 0xd7, 0x9f, 0x37, 0x2d, 0x62, 0x36, 0xd6, + 0x25, 0xe1, 0x42, 0xb1, 0x42, 0x3f, 0x62, 0x63, 0x59, 0xc0, 0xfd, 0x47, 0x09, 0x16, 0xb4, 0xbe, 0x17, 0xe8, 0x00, + 0xcd, 0x04, 0x83, 0x4b, 0x87, 0x8d, 0x19, 0x9a, 0x5f, 0x9f, 0xcd, 0x1d, 0xf8, 0xf5, 0x66, 0xad, 0x97, 0x07, 0x07, + 0x5f, 0x58, 0x05, 0x28, 0x37, 0x4c, 0x33, 0x8c, 0x80, 0x78, 0x59, 0x2e, 0xc7, 0xdd, 0x0c, 0xdf, 0x8b, 0x2b, 0x95, + 0x81, 0x27, 0x1c, 0x21, 0x11, 0x7a, 0x4e, 0xf4, 0x7a, 0xb2, 0x49, 0xef, 0x9d, 0x36, 0x43, 0x84, 0x62, 0x0d, 0x90, + 0x7b, 0x90, 0xcb, 0xad, 0x92, 0x49, 0x55, 0xb6, 0xb6, 0xe5, 0x20, 0x1e, 0x03, 0xb8, 0x62, 0x23, 0xa4, 0x04, 0x68, + 0xb8, 0x5b, 0x68, 0x79, 0x2e, 0x81, 0xfd, 0xc7, 0x2a, 0x01, 0x91, 0x16, 0xd5, 0x36, 0x2e, 0x42, 0xd8, 0x9a, 0xfa, + 0x04, 0xc6, 0x09, 0x0f, 0x9f, 0xef, 0xd2, 0x50, 0x7b, 0xd4, 0x66, 0xe6, 0x0c, 0x82, 0x12, 0x12, 0x95, 0x15, 0x92, + 0x2f, 0xb1, 0x70, 0xdc, 0x9c, 0xbf, 0x87, 0x03, 0x52, 0xac, 0x68, 0x6c, 0xef, 0xb6, 0xe0, 0xf8, 0x28, 0x92, 0x45, + 0x5c, 0xeb, 0xba, 0x5b, 0x98, 0x6a, 0xd8, 0x81, 0x8e, 0x86, 0x70, 0x2a, 0xcc, 0x3d, 0xe1, 0xe3, 0x8a, 0xa4, 0xfe, + 0x6c, 0x4d, 0xb4, 0xb5, 0x27, 0x86, 0x95, 0x69, 0x4a, 0x30, 0xff, 0x9f, 0xad, 0xd5, 0x75, 0x59, 0x08, 0x33, 0x33, + 0x8c, 0x1b, 0xbb, 0x0a, 0x6c, 0x0d, 0x38, 0xb6, 0xfc, 0x5b, 0x06, 0x8b, 0xea, 0x95, 0xe2, 0xa6, 0xd3, 0x80, 0x09, + 0x78, 0x0b, 0xd6, 0x33, 0x9b, 0x5b, 0xff, 0xb9, 0x39, 0x18, 0x05, 0x56, 0x35, 0x02, 0x2f, 0x0d, 0x81, 0x47, 0xc0, + 0xb8, 0x79, 0xd3, 0xf2, 0x9e, 0x33, 0xa2, 0x11, 0xfe, 0xc4, 0x73, 0x78, 0x66, 0x59, 0xee, 0xad, 0x8f, 0x8d, 0x15, + 0x49, 0x05, 0x01, 0xdb, 0x22, 0xec, 0x88, 0xbc, 0x44, 0x58, 0x35, 0x1a, 0x5d, 0x75, 0xc1, 0x2a, 0xad, 0x4a, 0x35, + 0x4c, 0x01, 0xb7, 0xc4, 0x80, 0xf7, 0xb5, 0x13, 0x15, 0x0c, 0x09, 0xbc, 0xf5, 0xb7, 0x02, 0xf5, 0xfd, 0xc3, 0xb7, + 0x71, 0x48, 0xdf, 0xc2, 0xb2, 0xe5, 0x45, 0x2c, 0x4c, 0x29, 0xae, 0xee, 0x70, 0xde, 0x7c, 0xdf, 0x6c, 0x04, 0xc6, + 0xbd, 0xdf, 0xc6, 0x60, 0xe3, 0x86, 0xba, 0xda, 0x92, 0x86, 0x72, 0x13, 0x76, 0x51, 0x65, 0xef, 0x18, 0x76, 0xd6, + 0xd5, 0x95, 0xb4, 0xab, 0x89, 0x5a, 0xaf, 0x15, 0xab, 0x8c, 0x06, 0x36, 0x0c, 0x3b, 0xcd, 0x31, 0xb3, 0xad, 0xc0, + 0x7f, 0x3c, 0x27, 0x1a, 0x07, 0xc8, 0xfa, 0xe6, 0x5b, 0xd7, 0x29, 0xd5, 0x30, 0x61, 0x7b, 0xbb, 0xf3, 0xf1, 0x31, + 0xdf, 0x75, 0x3e, 0x62, 0xe9, 0xb6, 0xbe, 0x39, 0x1b, 0xdb, 0xff, 0xc6, 0xd9, 0xe8, 0xd4, 0xf6, 0xfe, 0x78, 0x04, + 0xee, 0xa4, 0x76, 0x3c, 0xd6, 0xd7, 0x94, 0x48, 0x2c, 0xdc, 0x72, 0x5c, 0x76, 0x56, 0x2b, 0xd1, 0x6f, 0x81, 0xda, + 0x29, 0x8a, 0xe0, 0x67, 0xdb, 0xfe, 0x0c, 0x48, 0xb2, 0xd5, 0x21, 0xc7, 0xa2, 0x14, 0x65, 0x50, 0x02, 0x06, 0xd4, + 0xb1, 0xb1, 0xf5, 0x32, 0x88, 0xed, 0x70, 0xc8, 0x61, 0x39, 0x11, 0xe5, 0xd5, 0x15, 0x8c, 0xd8, 0x1c, 0x1b, 0x4e, + 0xc0, 0x8c, 0x77, 0x5a, 0x15, 0x7a, 0xf1, 0xf3, 0x5f, 0x33, 0xa7, 0xb5, 0x23, 0xc6, 0x72, 0x12, 0x35, 0x2b, 0x06, + 0x37, 0x02, 0xc7, 0x30, 0xee, 0x1b, 0x09, 0xb5, 0x3a, 0xd5, 0x51, 0xed, 0x48, 0xc2, 0x2d, 0x50, 0xbb, 0xed, 0x9b, + 0x73, 0x69, 0xb5, 0xda, 0x79, 0xb0, 0xe0, 0x22, 0xc0, 0xed, 0xe7, 0x44, 0xd7, 0x48, 0x0a, 0x25, 0x4e, 0x82, 0xc2, + 0xb9, 0x41, 0x55, 0x4d, 0x64, 0xbf, 0x35, 0x00, 0x9e, 0xb4, 0x9b, 0x5d, 0xc8, 0x4a, 0x48, 0xce, 0x1a, 0x0d, 0x94, + 0x97, 0x1d, 0xd3, 0xbe, 0x68, 0x64, 0x03, 0xcc, 0x70, 0x66, 0x05, 0x16, 0x38, 0xbd, 0xe2, 0xbc, 0xea, 0xba, 0x9f, + 0x0d, 0x10, 0x2e, 0x56, 0xab, 0xd8, 0x0e, 0x2d, 0x47, 0xab, 0x55, 0x1e, 0x0e, 0xcd, 0xe4, 0x43, 0xc5, 0x97, 0x3d, + 0x4d, 0x5e, 0x9a, 0xf3, 0xf0, 0x25, 0x0c, 0xb2, 0x41, 0xe2, 0xdc, 0xa9, 0x04, 0x73, 0xd0, 0x5c, 0x35, 0x64, 0x3f, + 0x6b, 0xb4, 0x07, 0x01, 0x0d, 0xeb, 0x67, 0x03, 0x92, 0xaf, 0xc1, 0x72, 0x56, 0xb9, 0x03, 0xf3, 0x6f, 0x38, 0xd8, + 0xfe, 0x36, 0xe7, 0x8c, 0x6d, 0x30, 0x5c, 0x93, 0x4d, 0x95, 0x41, 0x89, 0x57, 0x6e, 0x71, 0x7d, 0xb9, 0x9a, 0x81, + 0x45, 0x59, 0x08, 0xbb, 0x6b, 0xe6, 0x1e, 0x08, 0xff, 0x25, 0xb6, 0x4b, 0x5a, 0x1a, 0x71, 0x6f, 0x20, 0xbe, 0xb7, + 0xdd, 0x4e, 0x92, 0x84, 0x16, 0x13, 0x73, 0x25, 0xe2, 0x6f, 0x78, 0xcd, 0x1e, 0x38, 0x76, 0xe3, 0x0c, 0x7a, 0xee, + 0x97, 0x9d, 0x0d, 0x88, 0x1d, 0xbf, 0x67, 0x76, 0xbc, 0xe3, 0x4a, 0x41, 0x77, 0xeb, 0x22, 0xec, 0x60, 0xe8, 0xff, + 0xf2, 0x60, 0x4e, 0xdc, 0x60, 0x2c, 0x9a, 0x6c, 0xc0, 0xed, 0x1b, 0xf0, 0x28, 0xe8, 0x06, 0xdc, 0xbe, 0x0d, 0x5f, + 0x0f, 0xad, 0xec, 0x9b, 0x03, 0x0c, 0xc8, 0x84, 0x1d, 0x69, 0x95, 0x10, 0x0c, 0xf3, 0x74, 0x93, 0x23, 0xb3, 0x64, + 0x15, 0x0e, 0x57, 0x4d, 0x62, 0xb1, 0xb1, 0x17, 0x2a, 0x26, 0x35, 0x10, 0x8c, 0x45, 0xfa, 0x12, 0x85, 0x4a, 0x83, + 0xba, 0x71, 0x0c, 0x60, 0x95, 0xd3, 0xd6, 0xbf, 0x3c, 0x38, 0x00, 0xa1, 0x01, 0x58, 0xbb, 0x24, 0xa3, 0x73, 0xbd, + 0x28, 0x80, 0xbf, 0x52, 0xfe, 0x37, 0x24, 0x83, 0xdb, 0x89, 0x49, 0x83, 0x1f, 0x90, 0x30, 0xa7, 0x4a, 0xf1, 0x2f, + 0x36, 0xcd, 0xfd, 0xc6, 0x05, 0xf1, 0x18, 0xad, 0x2c, 0xa7, 0x28, 0x51, 0x57, 0x3a, 0x74, 0xad, 0x43, 0xee, 0xe9, + 0x17, 0x26, 0xf4, 0x4b, 0xae, 0x34, 0x13, 0x00, 0x80, 0x0a, 0xf1, 0x60, 0x4a, 0x0a, 0xc1, 0xd6, 0xad, 0xd5, 0xa2, + 0xa3, 0xd1, 0x77, 0xab, 0xe8, 0x3a, 0x5b, 0x34, 0xa5, 0x62, 0x94, 0xdb, 0x4e, 0x42, 0x9b, 0x49, 0x6f, 0x27, 0x5a, + 0x96, 0x0c, 0x2d, 0x76, 0x2a, 0xf6, 0xc3, 0xd0, 0xfa, 0x58, 0x10, 0x7f, 0x2e, 0xf8, 0xb3, 0xf4, 0xbb, 0x7c, 0x0c, + 0x5c, 0xa9, 0x7f, 0x63, 0x15, 0xc2, 0x99, 0x60, 0x1d, 0x90, 0xd7, 0xa4, 0x3e, 0x4e, 0x8f, 0x3a, 0xf9, 0x96, 0x72, + 0xa1, 0x34, 0x0a, 0xdb, 0x38, 0x29, 0x0c, 0xa6, 0x9c, 0x7d, 0x5b, 0xe2, 0xfa, 0xd5, 0x1f, 0x23, 0xfe, 0xe8, 0x10, + 0xff, 0x2e, 0x95, 0x46, 0xcb, 0x12, 0xc1, 0x90, 0xdf, 0x91, 0x5a, 0xc1, 0x55, 0x6c, 0xce, 0xf5, 0x73, 0x3d, 0xcb, + 0x37, 0x3c, 0x71, 0xba, 0x5a, 0x95, 0x52, 0x81, 0x8a, 0x6f, 0x18, 0x7e, 0xc2, 0xe0, 0xde, 0xf8, 0x19, 0x0f, 0xaa, + 0x6c, 0xdf, 0x17, 0x3f, 0x0b, 0xee, 0x8b, 0x9f, 0xf1, 0x74, 0xbb, 0x68, 0x70, 0x4f, 0xdc, 0x49, 0xce, 0x93, 0x56, + 0xe4, 0xf9, 0xa8, 0x29, 0xad, 0xfc, 0x2b, 0xed, 0xd6, 0xc0, 0x95, 0x4d, 0x1c, 0x18, 0xe7, 0xd5, 0x45, 0x28, 0xe6, + 0xcc, 0x19, 0x2d, 0x87, 0xff, 0xad, 0x75, 0x72, 0x27, 0x8f, 0xb4, 0x52, 0xc8, 0x1b, 0x5a, 0xe8, 0x7b, 0xb0, 0xe1, + 0x8a, 0x2d, 0x1f, 0x40, 0x4a, 0x40, 0xd9, 0xf6, 0xef, 0x75, 0x11, 0x88, 0xe3, 0xca, 0x3a, 0x1f, 0x85, 0xed, 0x93, + 0xa2, 0xe4, 0xea, 0xea, 0x42, 0xc8, 0xad, 0xd1, 0x12, 0x20, 0x4c, 0xbd, 0x6b, 0x1e, 0x73, 0x34, 0x99, 0xa5, 0xcb, + 0x75, 0xa9, 0x3a, 0x28, 0x2c, 0x57, 0xc7, 0x11, 0x2e, 0xd6, 0xe6, 0x06, 0xfd, 0x15, 0xc7, 0x7f, 0x73, 0x47, 0x23, + 0x7f, 0x2e, 0x29, 0xd0, 0xa3, 0xdd, 0xbe, 0x36, 0x3b, 0x48, 0xa4, 0x9d, 0x43, 0x69, 0x29, 0x00, 0x58, 0x6d, 0xf0, + 0x75, 0xed, 0x71, 0xea, 0x89, 0x74, 0xb3, 0xf9, 0xa6, 0x21, 0x2c, 0x66, 0xa5, 0x05, 0x8f, 0xe9, 0x66, 0x87, 0xe5, + 0xa8, 0x97, 0xc5, 0x75, 0xb9, 0xc7, 0x6a, 0xfd, 0xa2, 0x6f, 0x80, 0xb2, 0x32, 0x44, 0x5b, 0xad, 0xe2, 0x3a, 0xbc, + 0x89, 0x08, 0xae, 0x41, 0x10, 0x16, 0x81, 0x01, 0x47, 0x8d, 0xf1, 0xb6, 0x75, 0x62, 0xb4, 0x69, 0xbf, 0xe4, 0x59, + 0xf7, 0xda, 0x38, 0x42, 0x45, 0x83, 0xad, 0x1e, 0x6a, 0x1e, 0xb0, 0x9d, 0x5d, 0xd9, 0x51, 0x00, 0xa1, 0x29, 0xf5, + 0xc6, 0xb9, 0x95, 0x15, 0xed, 0x0e, 0xf8, 0xa2, 0xef, 0x98, 0xe7, 0x3a, 0xd0, 0x6d, 0xe7, 0x07, 0xb6, 0x4d, 0x4f, + 0xe4, 0xb7, 0x6c, 0x9b, 0x6a, 0x9c, 0xf0, 0x7e, 0x0b, 0x7d, 0xdf, 0x10, 0xd6, 0xf6, 0xb5, 0xbb, 0xc8, 0xff, 0x42, + 0x77, 0x6d, 0x40, 0x4f, 0x0b, 0x66, 0x4f, 0x63, 0x3e, 0xe8, 0xf5, 0xfa, 0xe7, 0xd2, 0x7f, 0xc1, 0xd8, 0x0a, 0xfd, + 0x6c, 0x77, 0x81, 0x13, 0x2b, 0x8d, 0x43, 0x70, 0xfc, 0x8a, 0x93, 0x49, 0x2e, 0x87, 0x34, 0x7f, 0x07, 0x3d, 0x56, + 0xb9, 0xcf, 0xef, 0x46, 0x05, 0xd5, 0xcc, 0xd1, 0x9a, 0x6a, 0x14, 0xaf, 0x78, 0x30, 0x8c, 0x57, 0xdc, 0x52, 0xee, + 0xaa, 0x05, 0xbc, 0x7c, 0x59, 0x36, 0x91, 0xfe, 0xbc, 0x2e, 0x65, 0x30, 0xb5, 0xbb, 0x97, 0x4d, 0x92, 0xc6, 0x4a, + 0x92, 0xc6, 0x54, 0xbc, 0xd9, 0x54, 0x1c, 0xff, 0xfd, 0x8d, 0xc1, 0x6e, 0x93, 0xb9, 0xbf, 0x03, 0x32, 0xf7, 0x37, + 0x4f, 0xbf, 0x5b, 0x2b, 0xa0, 0x78, 0xc7, 0xc9, 0xb1, 0xb1, 0x8c, 0xb1, 0xa3, 0x7e, 0xab, 0xc1, 0xa0, 0x41, 0x93, + 0xcb, 0xc0, 0xdb, 0xa1, 0x3a, 0xbd, 0xbc, 0xfd, 0x51, 0x9c, 0x2d, 0x94, 0x96, 0x33, 0xd7, 0xa8, 0x72, 0x3e, 0x4e, + 0x26, 0x13, 0x14, 0xd8, 0xe6, 0x0e, 0x3f, 0xad, 0xbb, 0x91, 0x2d, 0x3f, 0x73, 0x31, 0x4a, 0x15, 0x76, 0x67, 0x8b, + 0x4a, 0xe5, 0x9a, 0x78, 0x33, 0xe7, 0xed, 0x3c, 0x3c, 0xe6, 0x82, 0xab, 0x29, 0x2b, 0xe2, 0x02, 0x2d, 0xbf, 0xd5, + 0x59, 0x01, 0xb7, 0x39, 0xb6, 0x33, 0x3c, 0x2a, 0x2d, 0x07, 0x74, 0x02, 0xad, 0x81, 0xce, 0x68, 0xc6, 0xf4, 0x54, + 0x8e, 0xc0, 0xf0, 0x25, 0x19, 0x95, 0xee, 0x54, 0x07, 0x07, 0xfb, 0x71, 0x64, 0xf4, 0x17, 0xe0, 0x83, 0x1e, 0xe6, + 0xa0, 0xde, 0x12, 0x1c, 0x83, 0xaa, 0xae, 0x19, 0x5a, 0xb2, 0x4d, 0x1f, 0x1a, 0x9d, 0x7c, 0x66, 0x77, 0x98, 0xa3, + 0xf5, 0x3a, 0xb5, 0xa3, 0x8e, 0xc6, 0x9c, 0xe5, 0xa3, 0x08, 0x7f, 0x66, 0x77, 0x69, 0xe9, 0xb6, 0x6e, 0xbc, 0xac, + 0xcd, 0x22, 0x46, 0xf2, 0x46, 0x44, 0xb8, 0xea, 0x24, 0x5d, 0xae, 0xb1, 0x2c, 0xf8, 0x04, 0x70, 0xf4, 0x17, 0x76, + 0x97, 0xba, 0xf6, 0x02, 0x57, 0x41, 0xb4, 0xf4, 0xa0, 0x4f, 0x82, 0xe4, 0x70, 0x19, 0x9c, 0xc0, 0xd1, 0x37, 0x75, + 0x07, 0xa4, 0x56, 0xae, 0x12, 0x21, 0x11, 0x5a, 0xff, 0xbb, 0x53, 0xc1, 0x8b, 0xf0, 0x9c, 0xd3, 0x35, 0x8b, 0xdb, + 0x8d, 0x4a, 0x0c, 0x2a, 0x54, 0x16, 0x24, 0x1f, 0x63, 0xee, 0x77, 0x9f, 0xf3, 0x7e, 0x08, 0x74, 0x66, 0x0b, 0xea, + 0x1a, 0x4d, 0x47, 0xe6, 0x17, 0xaa, 0xee, 0xa0, 0x66, 0xba, 0xaa, 0xb8, 0xf7, 0x31, 0x06, 0xc0, 0x83, 0xb5, 0x0c, + 0x35, 0x0e, 0xa1, 0x6b, 0x6f, 0xa6, 0x3a, 0xa6, 0x24, 0x5e, 0xfa, 0x39, 0xa4, 0x3c, 0x04, 0xa3, 0x5e, 0x03, 0x1a, + 0x3a, 0x04, 0xb3, 0x96, 0x87, 0x7c, 0x1c, 0x8b, 0xad, 0x33, 0x54, 0x9a, 0x33, 0x34, 0x09, 0x40, 0xfe, 0x8d, 0x33, + 0x93, 0x19, 0x68, 0x18, 0xde, 0xd2, 0x1c, 0x80, 0x6e, 0x75, 0x1d, 0x0e, 0x85, 0x2b, 0x5a, 0x3a, 0xef, 0xd9, 0x45, + 0x97, 0xb5, 0x61, 0xc5, 0xa6, 0x1d, 0xb4, 0x4e, 0x61, 0x4a, 0xcc, 0x16, 0x58, 0x7b, 0xbd, 0x0f, 0xf7, 0x76, 0xb5, + 0x71, 0x91, 0xf8, 0x69, 0x11, 0x0f, 0x93, 0x98, 0xa2, 0x25, 0x8f, 0x29, 0x96, 0x60, 0x07, 0x59, 0xac, 0xcb, 0xf1, + 0xb3, 0x70, 0x39, 0x6a, 0x56, 0xd2, 0xbb, 0x1d, 0x0c, 0x81, 0xcb, 0xd7, 0x60, 0x1b, 0x8a, 0xb9, 0x27, 0x2c, 0x3c, + 0x36, 0x9e, 0x7e, 0xc1, 0xba, 0xcd, 0xed, 0x82, 0xf8, 0x15, 0x18, 0xd3, 0x78, 0x19, 0xcc, 0x22, 0x74, 0x2a, 0x77, + 0x0e, 0x87, 0xee, 0x9a, 0xb0, 0x32, 0x5e, 0x8d, 0x15, 0xd9, 0x38, 0x7a, 0xbe, 0x6f, 0xe3, 0xf9, 0xcf, 0x82, 0x15, + 0x77, 0x57, 0x0c, 0x6c, 0xac, 0x25, 0xb8, 0x1b, 0x57, 0xcb, 0x50, 0x19, 0xc8, 0xf7, 0xa4, 0x61, 0x5d, 0xd6, 0xf8, + 0xbb, 0x51, 0x31, 0xd6, 0xe6, 0x9e, 0x32, 0xd0, 0xd6, 0xd8, 0xed, 0xc2, 0xbe, 0xe9, 0xba, 0xc9, 0xba, 0x46, 0x11, + 0x57, 0x41, 0xda, 0xdd, 0x2d, 0xe0, 0x22, 0xf4, 0x87, 0xed, 0xab, 0xc1, 0xa6, 0xea, 0x06, 0x92, 0xe0, 0xda, 0x4f, + 0x7e, 0x7b, 0xaa, 0xbb, 0xac, 0x75, 0xbf, 0x3d, 0xd5, 0xda, 0x65, 0xa1, 0x31, 0x24, 0xc2, 0xae, 0x9f, 0xd2, 0x7f, + 0x5a, 0xac, 0xd7, 0x68, 0x0d, 0xc3, 0x7b, 0xcf, 0xbb, 0x71, 0xfc, 0xde, 0x5b, 0x28, 0x26, 0x70, 0x91, 0x7b, 0x95, + 0x4b, 0x4f, 0xc8, 0xab, 0x11, 0xbc, 0xe7, 0x5b, 0x43, 0x78, 0xcf, 0x03, 0xa7, 0x57, 0x90, 0x9a, 0x26, 0x82, 0x8d, + 0x3c, 0xfd, 0x44, 0x16, 0x09, 0x0d, 0x1f, 0xf7, 0x9a, 0x13, 0xa1, 0x3f, 0xa5, 0xc0, 0x7f, 0xe1, 0xe1, 0x42, 0x6b, + 0x29, 0x30, 0x17, 0xf3, 0x85, 0xc6, 0xca, 0x8c, 0x7e, 0x39, 0x96, 0x42, 0x37, 0xc7, 0x74, 0xc6, 0xf3, 0xbb, 0x74, + 0xc1, 0x9b, 0x33, 0x29, 0xa4, 0x9a, 0xd3, 0x8c, 0x61, 0x75, 0xa7, 0x34, 0x9b, 0x35, 0x17, 0x1c, 0x3f, 0x67, 0xf9, + 0x17, 0xa6, 0x79, 0x46, 0xf1, 0x5b, 0x39, 0x94, 0x5a, 0xe2, 0xd7, 0xb7, 0x77, 0x13, 0x26, 0xf0, 0xef, 0xc3, 0x85, + 0xd0, 0x0b, 0xac, 0xa8, 0x50, 0x4d, 0xc5, 0x0a, 0x3e, 0xee, 0x36, 0x9b, 0xf3, 0x82, 0xcf, 0x68, 0x71, 0xd7, 0xcc, + 0x64, 0x2e, 0x8b, 0xf4, 0xbf, 0x5a, 0xc7, 0xf4, 0xc1, 0xf8, 0xa4, 0xab, 0x0b, 0x2a, 0x14, 0x87, 0x85, 0x49, 0x69, + 0x9e, 0xef, 0x1d, 0x9f, 0xb6, 0x66, 0x6a, 0xdf, 0x5e, 0xf8, 0x51, 0xa1, 0xd7, 0x9f, 0xf0, 0x07, 0x09, 0xa3, 0x4c, + 0x86, 0x5a, 0xb8, 0x41, 0x2e, 0xb3, 0x45, 0xa1, 0x64, 0x91, 0xce, 0x25, 0x17, 0x9a, 0x15, 0xdd, 0xa1, 0x2c, 0x46, + 0xac, 0x68, 0x16, 0x74, 0xc4, 0x17, 0x2a, 0x3d, 0x99, 0xdf, 0x76, 0xeb, 0x3d, 0xd8, 0xfc, 0x54, 0x48, 0xc1, 0xba, + 0xc0, 0x6f, 0x4c, 0x0a, 0xb9, 0x10, 0x23, 0x37, 0x8c, 0x85, 0x50, 0x4c, 0x77, 0xe7, 0x74, 0x04, 0x76, 0xc0, 0xe9, + 0xf9, 0xfc, 0xb6, 0x6b, 0x66, 0x7d, 0xc3, 0xf8, 0x64, 0xaa, 0xd3, 0xd3, 0x56, 0xcb, 0x7e, 0x2b, 0xfe, 0x95, 0xa5, + 0xed, 0x4e, 0xd2, 0x39, 0x9d, 0xdf, 0x02, 0x07, 0xaf, 0x59, 0xd1, 0x04, 0x58, 0x40, 0xa5, 0x76, 0xd2, 0x7a, 0x70, + 0x7c, 0x1f, 0x32, 0xc0, 0xc6, 0xa1, 0x69, 0x26, 0x04, 0xc6, 0xee, 0xe9, 0x62, 0x3e, 0x67, 0x05, 0x78, 0xd1, 0x77, + 0x67, 0xb4, 0x98, 0x70, 0xd1, 0x2c, 0x4c, 0xa3, 0xcd, 0xf3, 0xf9, 0xed, 0x1a, 0xe6, 0x93, 0x5a, 0xb3, 0x55, 0x37, + 0x2d, 0xf7, 0xb5, 0x0c, 0x86, 0x68, 0x62, 0xd2, 0xa4, 0xc5, 0x64, 0x48, 0xe3, 0x76, 0xe7, 0x3e, 0xf6, 0xff, 0x4b, + 0x3a, 0x28, 0x00, 0x5b, 0x73, 0xb4, 0x28, 0xcc, 0x2d, 0x6a, 0xda, 0x56, 0xb6, 0xd9, 0xa9, 0xfc, 0xc2, 0x0a, 0xdf, + 0xaa, 0xf9, 0x58, 0x6e, 0xcd, 0xfb, 0x3f, 0x69, 0xf4, 0x13, 0x9e, 0x50, 0x58, 0x03, 0x83, 0x1c, 0x7d, 0x23, 0x0f, + 0xc2, 0x4c, 0x07, 0xcb, 0x1b, 0x3e, 0xd2, 0xd3, 0xb4, 0xdd, 0x6a, 0xfd, 0x50, 0xad, 0x58, 0x77, 0x6a, 0x41, 0xd7, + 0x2e, 0xd8, 0xac, 0xb6, 0x8e, 0x33, 0x5a, 0x62, 0xdb, 0x72, 0x2e, 0xdd, 0x92, 0x17, 0x2c, 0x37, 0xd1, 0x64, 0xd6, + 0x0e, 0xe5, 0xb6, 0xc6, 0xc9, 0xc5, 0x94, 0x15, 0x5c, 0x77, 0xeb, 0x5f, 0x55, 0xc7, 0xdb, 0xab, 0xbf, 0xb6, 0x72, + 0xe8, 0xd2, 0xd6, 0x70, 0x97, 0x9e, 0x8f, 0xe1, 0x63, 0x7b, 0xf5, 0xbf, 0xd0, 0x22, 0xde, 0x40, 0x4c, 0x1c, 0xd6, + 0x40, 0xeb, 0x60, 0xce, 0x05, 0x98, 0x64, 0x0e, 0xf0, 0x37, 0xa0, 0x90, 0xd1, 0x3c, 0x8b, 0x61, 0x44, 0x7b, 0xcd, + 0xbd, 0xe3, 0x82, 0xcd, 0x90, 0x07, 0x44, 0x72, 0xff, 0xb4, 0x60, 0xb3, 0x75, 0x62, 0xaa, 0x2f, 0x0d, 0x8a, 0xd0, + 0x9c, 0x4f, 0x44, 0x9a, 0x31, 0x40, 0xdf, 0x75, 0xc2, 0x84, 0xe6, 0xfa, 0xae, 0x59, 0xc8, 0x9b, 0xe5, 0x88, 0xab, + 0x79, 0x4e, 0xef, 0xd2, 0x71, 0xce, 0x6e, 0xbb, 0xa6, 0x54, 0x93, 0x6b, 0x36, 0x53, 0xae, 0x6c, 0x17, 0xd2, 0x9b, + 0x23, 0x6b, 0x36, 0x01, 0xd0, 0x93, 0x37, 0x9b, 0xfb, 0x27, 0x39, 0x56, 0x7b, 0x8c, 0x2a, 0xd6, 0x94, 0x0b, 0xbd, + 0xd7, 0x52, 0xdd, 0x19, 0x17, 0x4d, 0x37, 0x90, 0x93, 0xd6, 0xfc, 0xb6, 0xbb, 0x0d, 0xf9, 0xa0, 0xff, 0x84, 0xdd, + 0xce, 0xa9, 0x18, 0xb1, 0xd1, 0x32, 0xa8, 0xd6, 0x81, 0x7a, 0x61, 0xa9, 0x54, 0xe8, 0x69, 0xd3, 0xd8, 0x7a, 0xc5, + 0x1d, 0x81, 0xbe, 0x81, 0x5a, 0x0f, 0x5a, 0xd8, 0xfe, 0x7f, 0xd2, 0x46, 0x61, 0xe5, 0x3d, 0x08, 0xbb, 0xc4, 0xc7, + 0x77, 0x4d, 0xf8, 0xbb, 0x04, 0xdf, 0x22, 0x9e, 0xd1, 0xdc, 0x41, 0x64, 0xc6, 0x47, 0xa3, 0xbc, 0x36, 0xa2, 0xcb, + 0xa0, 0xb3, 0x36, 0x5a, 0xc2, 0xfc, 0xd3, 0xd6, 0x5e, 0x6b, 0xcf, 0xcc, 0xc5, 0x6d, 0xf3, 0x93, 0x93, 0xfb, 0xc7, + 0x0f, 0x58, 0x37, 0xe7, 0x82, 0xd5, 0xa6, 0xfa, 0x5d, 0x50, 0x87, 0x0d, 0x77, 0x5c, 0xc3, 0xed, 0xbd, 0xf6, 0xde, + 0x49, 0xeb, 0x07, 0xbf, 0x5b, 0x73, 0x36, 0xd6, 0x69, 0xfb, 0x6c, 0x7e, 0x5b, 0xdf, 0xbe, 0xe7, 0xbe, 0xe9, 0x9b, + 0x82, 0xce, 0x53, 0x21, 0xe1, 0x4f, 0x17, 0x36, 0xd9, 0x38, 0x97, 0x37, 0xe9, 0x94, 0x8f, 0x46, 0x4c, 0xd8, 0x02, + 0x65, 0x22, 0xcb, 0x73, 0x3e, 0x57, 0xdc, 0xae, 0x86, 0xc3, 0xdd, 0xd3, 0x0d, 0xa8, 0x86, 0x03, 0x3a, 0x0e, 0x06, + 0x74, 0x5a, 0x0d, 0xa8, 0xea, 0x3f, 0x1c, 0x61, 0x67, 0x63, 0xae, 0xa6, 0x54, 0xb7, 0x86, 0x49, 0x7f, 0x2f, 0x94, + 0x06, 0x98, 0x7b, 0x23, 0x0d, 0x43, 0xc5, 0x9b, 0x43, 0xa6, 0x6f, 0x18, 0x13, 0xdf, 0x1e, 0xc4, 0x65, 0x2a, 0x45, + 0x7e, 0x67, 0x3f, 0x97, 0x61, 0x97, 0x74, 0xa1, 0xe5, 0x3a, 0x19, 0x72, 0x41, 0x8b, 0xbb, 0x6b, 0xc5, 0x84, 0x92, + 0xc5, 0xb5, 0x1c, 0x8f, 0x97, 0xdf, 0x22, 0x2d, 0xf7, 0xd1, 0x3a, 0x51, 0x5c, 0x4c, 0x72, 0x66, 0x89, 0x92, 0x41, + 0x04, 0x47, 0xcc, 0x6d, 0xbb, 0xa6, 0xc9, 0xda, 0xa0, 0xd7, 0x49, 0x96, 0xf3, 0x19, 0xd5, 0xcc, 0xc0, 0x39, 0x20, + 0x35, 0x6e, 0xf2, 0x69, 0xbb, 0x35, 0xbf, 0xdd, 0x6b, 0xed, 0xd9, 0x3f, 0x55, 0x69, 0xd8, 0x46, 0x41, 0x61, 0xdf, + 0x24, 0x17, 0x06, 0x3f, 0x0c, 0x38, 0xcc, 0x2e, 0x32, 0xab, 0x67, 0xd6, 0x2e, 0x80, 0x1d, 0xcc, 0xae, 0xd6, 0xd4, + 0xa5, 0xa3, 0x4b, 0xb6, 0xc5, 0xd3, 0xd6, 0x0f, 0xf5, 0xdc, 0x9c, 0x0e, 0x59, 0xbe, 0xb4, 0x1b, 0xd5, 0x03, 0xd7, + 0x6d, 0xd5, 0x70, 0x99, 0x03, 0x92, 0x61, 0x40, 0x34, 0x48, 0xd3, 0xe6, 0x0d, 0x1b, 0x7e, 0xe6, 0xda, 0x6e, 0x99, + 0xa6, 0xba, 0x01, 0xe7, 0x1d, 0x33, 0xa6, 0x39, 0x2b, 0x96, 0xfe, 0x38, 0x6a, 0xd5, 0x08, 0xe8, 0x95, 0x30, 0x07, + 0xa1, 0xa6, 0xc3, 0x26, 0x84, 0x32, 0x63, 0xc5, 0x72, 0xd7, 0xe4, 0x66, 0xf4, 0xd6, 0xa1, 0xd8, 0x83, 0xd6, 0x0f, + 0xb5, 0xc3, 0xec, 0xa4, 0xd5, 0xf2, 0x07, 0x5d, 0xd3, 0xd6, 0x48, 0xdb, 0xc9, 0x29, 0x9b, 0x95, 0x89, 0x5a, 0xce, + 0xd3, 0x5a, 0xc2, 0x50, 0x6a, 0x2d, 0x67, 0x36, 0x6d, 0x07, 0x35, 0xaa, 0x93, 0xde, 0x76, 0x67, 0x7e, 0xbb, 0x67, + 0xfe, 0x69, 0xed, 0xb5, 0xb6, 0x49, 0xed, 0x36, 0x56, 0x1c, 0x23, 0x8f, 0xc7, 0xd0, 0x71, 0x9b, 0xcd, 0xba, 0x0b, + 0x05, 0xc7, 0xaa, 0x81, 0xb8, 0x39, 0xae, 0xd7, 0x66, 0xb2, 0x00, 0x58, 0xca, 0x05, 0x9c, 0x62, 0xf6, 0x24, 0x87, + 0x3e, 0x94, 0x04, 0xb3, 0xf3, 0x9d, 0x8d, 0xd6, 0x87, 0xd5, 0xda, 0xab, 0x06, 0x06, 0xff, 0xac, 0x3f, 0x55, 0x7c, + 0xf0, 0x0b, 0x16, 0xc8, 0x21, 0xbc, 0x91, 0x9c, 0xae, 0x5a, 0x4e, 0xf6, 0x18, 0xe9, 0x4a, 0x24, 0x32, 0x9e, 0x1b, + 0x33, 0x7a, 0x6b, 0x5d, 0x38, 0x66, 0x5c, 0x80, 0x81, 0x18, 0xc2, 0x3a, 0x30, 0xa5, 0x9f, 0x86, 0x0d, 0x8d, 0x74, + 0x0c, 0x0d, 0x1f, 0x76, 0x92, 0xd3, 0x53, 0x84, 0x5b, 0xb8, 0x73, 0x7a, 0x1a, 0xc8, 0x3e, 0x63, 0xbd, 0xab, 0xe8, + 0xae, 0x92, 0x72, 0x47, 0xc9, 0x23, 0xd3, 0xe8, 0x51, 0xbb, 0xd5, 0xc2, 0xc6, 0x4d, 0xbd, 0x2c, 0xcc, 0xd5, 0x8e, + 0x66, 0xdb, 0xad, 0x16, 0x34, 0x0b, 0x7f, 0xdc, 0xbc, 0x7e, 0x21, 0xcb, 0x56, 0xda, 0xc2, 0xed, 0xb4, 0x8d, 0x3b, + 0x69, 0x07, 0x1f, 0xa7, 0xc7, 0xf8, 0x24, 0x3d, 0xc1, 0xa7, 0xe9, 0x29, 0x3e, 0x4b, 0xcf, 0xf0, 0xfd, 0xf4, 0x3e, + 0x3e, 0x4f, 0xcf, 0xf1, 0x83, 0xf4, 0x01, 0x7e, 0x98, 0xb6, 0x5b, 0xf8, 0x51, 0xda, 0x6e, 0xe3, 0xc7, 0x69, 0xbb, + 0x83, 0x9f, 0xa4, 0xed, 0x63, 0xfc, 0x34, 0x6d, 0x9f, 0xe0, 0x67, 0x69, 0xfb, 0x14, 0x53, 0xc8, 0x1d, 0x42, 0x6e, + 0x06, 0xb9, 0x23, 0xc8, 0x65, 0x90, 0x3b, 0x4e, 0xdb, 0xa7, 0x6b, 0xac, 0x6c, 0x68, 0x8b, 0xa8, 0xd5, 0xee, 0x1c, + 0x9f, 0x9c, 0x9e, 0xdd, 0x3f, 0x7f, 0xf0, 0xf0, 0xd1, 0xe3, 0x27, 0x4f, 0x9f, 0x45, 0x03, 0x3c, 0x34, 0x1e, 0x26, + 0x4a, 0xf4, 0xf9, 0x41, 0xfb, 0x74, 0x80, 0xaf, 0xfd, 0x67, 0xcc, 0x0f, 0x3a, 0x27, 0x2d, 0x74, 0x79, 0x79, 0x32, + 0x68, 0x94, 0xb9, 0xef, 0x8d, 0x63, 0x4b, 0x95, 0x45, 0x08, 0x89, 0x21, 0x07, 0xe1, 0x3b, 0x53, 0xef, 0x3d, 0x8b, + 0x79, 0x52, 0xa0, 0x83, 0x03, 0xf3, 0x63, 0xe2, 0x7f, 0x0c, 0xfd, 0x0f, 0x1a, 0x2c, 0xd2, 0x2d, 0x8d, 0x9d, 0x67, + 0xb3, 0x2e, 0xfd, 0x0a, 0x4a, 0x93, 0x9d, 0x3d, 0xee, 0x8c, 0xe7, 0xff, 0x2b, 0xb2, 0xc6, 0x31, 0xe4, 0xc4, 0x2a, + 0xa0, 0x4e, 0x7b, 0x8c, 0x2c, 0x8b, 0xb4, 0x73, 0x7a, 0x7a, 0xf0, 0x4b, 0x9f, 0xf7, 0xdb, 0x83, 0xc1, 0x61, 0xfb, + 0x3e, 0x9e, 0x94, 0x09, 0x1d, 0x9b, 0x30, 0x2c, 0x13, 0x8e, 0x6d, 0x02, 0x4d, 0x6d, 0x6d, 0x48, 0x3a, 0x31, 0x49, + 0x50, 0x62, 0x9d, 0x9a, 0xb6, 0xef, 0xdb, 0xb6, 0x1f, 0x80, 0xd5, 0x96, 0x69, 0xde, 0x35, 0x7d, 0x71, 0x71, 0xb2, + 0x72, 0x8d, 0xe2, 0x49, 0xea, 0x5a, 0xf3, 0x89, 0x27, 0x83, 0x01, 0x1e, 0x9a, 0xc4, 0xd3, 0x2a, 0xf1, 0x6c, 0x30, + 0x70, 0x5d, 0x3d, 0x30, 0x5d, 0xdd, 0xaf, 0xb2, 0xce, 0x07, 0x03, 0xd3, 0x25, 0x72, 0x3e, 0xfa, 0x4a, 0xef, 0x7d, + 0x29, 0xf5, 0x24, 0xfc, 0xa2, 0x73, 0x7a, 0xda, 0x03, 0x0c, 0x33, 0xb6, 0xb7, 0x1e, 0x46, 0x37, 0x01, 0x8c, 0xee, + 0xe0, 0x77, 0x6f, 0x48, 0xd3, 0x6b, 0x5a, 0x02, 0xa9, 0x17, 0xfd, 0x57, 0xd4, 0xd0, 0x06, 0xe6, 0xe6, 0xcf, 0xc4, + 0xfe, 0x19, 0xa2, 0xc6, 0x17, 0x0a, 0xe0, 0x06, 0xcd, 0x8f, 0x57, 0xdd, 0x9a, 0x1e, 0x3f, 0x53, 0x70, 0xa5, 0x99, + 0xaa, 0x9c, 0xf6, 0x56, 0xd3, 0x9b, 0xe1, 0x6a, 0xaa, 0xbe, 0xa0, 0xbf, 0xe2, 0xbf, 0xd4, 0x61, 0xdc, 0x6f, 0x36, + 0x12, 0xf6, 0xd7, 0x08, 0x7c, 0x76, 0x7a, 0xe9, 0x88, 0x4d, 0x50, 0xaf, 0xff, 0x97, 0xc2, 0x83, 0x46, 0x90, 0xf1, + 0xc3, 0x76, 0x0a, 0x78, 0xf4, 0x6c, 0x26, 0xc6, 0x3f, 0xa0, 0x1e, 0xea, 0xfd, 0xa5, 0x0e, 0xff, 0x42, 0xf7, 0x8e, + 0xaa, 0xb9, 0xfc, 0x2e, 0xdd, 0x16, 0xae, 0xc2, 0xfc, 0x1c, 0x96, 0x5b, 0x98, 0xe1, 0x76, 0x93, 0x41, 0x50, 0x34, + 0x70, 0xf9, 0x26, 0xb1, 0x6c, 0xf0, 0xa3, 0xe3, 0x16, 0xfa, 0xa1, 0xdd, 0x01, 0x25, 0x46, 0x53, 0x1c, 0x6e, 0x6f, + 0xfa, 0xa2, 0x79, 0x8c, 0x1f, 0x34, 0x0b, 0xdc, 0x46, 0xb8, 0xd9, 0xf6, 0xda, 0xe5, 0xbe, 0x8a, 0x5b, 0x08, 0xab, + 0xf8, 0x1c, 0xfe, 0x39, 0x41, 0x83, 0x6a, 0x43, 0x5e, 0xd1, 0xcd, 0xde, 0xc1, 0x3f, 0x95, 0xc4, 0xaa, 0xc1, 0x8f, + 0xce, 0x5a, 0xe8, 0x87, 0x33, 0xd3, 0x11, 0x3b, 0xd4, 0x3b, 0xba, 0x92, 0xf8, 0xa4, 0x29, 0xa1, 0xa3, 0x56, 0xd9, + 0x8f, 0x88, 0x4f, 0x11, 0x16, 0xf1, 0x31, 0xfc, 0xd3, 0x0e, 0xfb, 0xf9, 0x75, 0xab, 0x1f, 0x33, 0xef, 0x36, 0x4e, + 0x4e, 0xad, 0xbb, 0xab, 0xb2, 0x77, 0xcf, 0x0d, 0x76, 0xd9, 0x36, 0x97, 0x66, 0xed, 0x23, 0xf8, 0x40, 0x58, 0x1f, + 0x12, 0x85, 0xd9, 0x21, 0xf8, 0xc9, 0x82, 0x79, 0x88, 0xba, 0x38, 0xee, 0xaa, 0x46, 0x03, 0x89, 0xbe, 0x1a, 0x1c, + 0x92, 0x76, 0x53, 0x37, 0x19, 0x86, 0xdf, 0x0d, 0x52, 0x06, 0x5f, 0x13, 0x55, 0xaf, 0x8f, 0x5d, 0xaf, 0xf6, 0x86, + 0xdd, 0x63, 0x07, 0x21, 0x44, 0xf5, 0x62, 0xdd, 0x64, 0xe8, 0x48, 0x34, 0x62, 0x7d, 0xc1, 0x7a, 0x67, 0x69, 0x0b, + 0x19, 0xec, 0x54, 0xbd, 0x98, 0x35, 0x39, 0xa4, 0x77, 0xd2, 0x98, 0x37, 0x35, 0xfc, 0x3a, 0x09, 0x66, 0x21, 0x00, + 0xef, 0x2a, 0xaf, 0x9f, 0xe2, 0xa8, 0x73, 0x7a, 0x8a, 0x05, 0xe1, 0xc9, 0xc4, 0xfc, 0x52, 0x84, 0x27, 0x43, 0xf3, + 0x4b, 0x92, 0x12, 0x5e, 0xb6, 0x77, 0x5c, 0x90, 0x60, 0x55, 0x4d, 0x0a, 0x85, 0x05, 0x2d, 0xd0, 0x51, 0xc7, 0x5f, + 0xbf, 0xe3, 0xa9, 0x9f, 0x03, 0xa8, 0x1b, 0x0a, 0x63, 0x79, 0x29, 0x9b, 0x05, 0xce, 0x09, 0xbd, 0x4c, 0x4e, 0x7b, + 0xd3, 0xa3, 0xb8, 0xd3, 0x94, 0xcd, 0x02, 0xa5, 0xd3, 0x23, 0x53, 0x13, 0x67, 0xe4, 0x31, 0xb5, 0xad, 0xe1, 0x29, + 0xdc, 0x99, 0x66, 0x24, 0x3b, 0x3c, 0x6b, 0x35, 0x92, 0x53, 0x84, 0xfb, 0xd9, 0xaa, 0x85, 0xf3, 0xd5, 0xaa, 0x85, + 0x69, 0xb0, 0x0c, 0x8f, 0x85, 0x07, 0x48, 0xa9, 0x11, 0xdb, 0x8c, 0x81, 0xd3, 0xe3, 0xb1, 0x86, 0xfb, 0x7f, 0x0d, + 0x5e, 0x35, 0x1a, 0xfc, 0x7d, 0x52, 0xee, 0x2e, 0xde, 0x90, 0x89, 0x02, 0x38, 0x0e, 0x75, 0x64, 0xaf, 0x85, 0x5f, + 0x57, 0x6f, 0xc1, 0x29, 0xe2, 0xdf, 0x25, 0xb6, 0x69, 0x41, 0x31, 0xba, 0x5d, 0xec, 0x57, 0xba, 0x55, 0xec, 0xcd, + 0x8e, 0x62, 0x57, 0xdb, 0xc5, 0x3e, 0xca, 0x40, 0xa3, 0xc7, 0x7f, 0x38, 0x3e, 0x6b, 0x35, 0x8e, 0x01, 0x59, 0x8f, + 0xcf, 0x5a, 0x55, 0xa1, 0x7b, 0xb4, 0x5a, 0x2b, 0x4d, 0x3e, 0x53, 0xeb, 0xd3, 0xc0, 0xbd, 0x73, 0xb5, 0x59, 0x38, + 0xeb, 0xda, 0x5d, 0xfa, 0x71, 0xf7, 0x4f, 0xc1, 0x66, 0x44, 0x18, 0x6a, 0xa7, 0xfb, 0x67, 0x83, 0xde, 0x94, 0xc5, + 0x0d, 0x48, 0x45, 0xe9, 0x58, 0xbb, 0x5f, 0xa8, 0xbc, 0x3a, 0xfe, 0x28, 0x21, 0xa9, 0x33, 0x40, 0x58, 0x92, 0x86, + 0xee, 0x1f, 0x0f, 0xcc, 0x79, 0x57, 0xc0, 0xef, 0x13, 0xf3, 0xbb, 0x54, 0xdc, 0x38, 0xc7, 0x87, 0xe9, 0xcd, 0x30, + 0xea, 0x09, 0xf2, 0x9a, 0xc6, 0xc6, 0x96, 0x1d, 0xa5, 0x65, 0x86, 0xfa, 0x02, 0x19, 0x6f, 0xca, 0x0c, 0x41, 0x5e, + 0x0b, 0xf7, 0x1b, 0x2f, 0x8b, 0x14, 0xec, 0x5a, 0xf0, 0x24, 0x05, 0x9b, 0x16, 0x3c, 0x4c, 0x05, 0xf8, 0x5d, 0xd0, + 0x94, 0x05, 0xd6, 0xf2, 0x0f, 0x9d, 0xa6, 0xcc, 0xdc, 0xee, 0x12, 0x83, 0xa5, 0x5d, 0x06, 0x27, 0xc5, 0x47, 0x19, + 0xc3, 0xdf, 0x86, 0x46, 0x98, 0x41, 0x9b, 0x0c, 0x61, 0x9e, 0x14, 0x04, 0xd2, 0x30, 0x4f, 0x26, 0x84, 0x41, 0x93, + 0x3c, 0x19, 0x12, 0xd6, 0xef, 0x04, 0x68, 0xf2, 0xd4, 0xc0, 0x0e, 0x80, 0xc3, 0xeb, 0x17, 0xe6, 0xda, 0x36, 0x0e, + 0x37, 0xf1, 0xd0, 0x84, 0x20, 0x5c, 0xc5, 0x30, 0x0b, 0xd8, 0x9c, 0xe6, 0x67, 0xa7, 0x0a, 0x33, 0xc9, 0x13, 0x6a, + 0xa8, 0xf7, 0x27, 0x20, 0xab, 0xf1, 0xbd, 0x25, 0x5b, 0xe3, 0xbd, 0x7b, 0x4b, 0xb1, 0xfe, 0x01, 0xfe, 0x28, 0xfb, + 0x07, 0x98, 0x87, 0x84, 0xa2, 0x35, 0xfa, 0x94, 0x42, 0xb1, 0x1d, 0xa5, 0xd0, 0x27, 0xef, 0x76, 0xa7, 0xc8, 0xf2, + 0x36, 0x8d, 0x46, 0xb4, 0xf8, 0x1c, 0xe1, 0x3f, 0xd3, 0x28, 0x07, 0x6e, 0x31, 0xc2, 0x1f, 0xd3, 0xa8, 0x60, 0x11, + 0xfe, 0x23, 0x8d, 0x86, 0xf9, 0x22, 0xc2, 0x1f, 0xd2, 0x68, 0x52, 0x44, 0xf8, 0x3d, 0x28, 0x45, 0x47, 0x7c, 0x31, + 0x8b, 0xf0, 0xef, 0x69, 0xa4, 0x8c, 0xd7, 0x01, 0x7e, 0x98, 0x46, 0x8c, 0x45, 0xf8, 0x5d, 0x1a, 0xc9, 0x3c, 0xc2, + 0x57, 0x69, 0x24, 0x8b, 0x08, 0x3f, 0x4a, 0xa3, 0x82, 0x46, 0xf8, 0x71, 0x1a, 0x41, 0xa1, 0x49, 0x84, 0x9f, 0xa4, + 0x11, 0xb4, 0xac, 0x22, 0xfc, 0x36, 0x8d, 0xb8, 0x88, 0xf0, 0x6f, 0x69, 0xa4, 0x17, 0xc5, 0x3f, 0x0b, 0xc9, 0x55, + 0x84, 0x9f, 0xa6, 0xd1, 0x94, 0x47, 0xf8, 0x4d, 0x1a, 0x15, 0x32, 0xc2, 0xaf, 0xd3, 0x88, 0xe6, 0x11, 0x7e, 0x95, + 0x46, 0x39, 0x8b, 0xf0, 0xaf, 0x69, 0x34, 0x62, 0x11, 0x7e, 0x99, 0x46, 0x77, 0x2c, 0xcf, 0x65, 0x84, 0x9f, 0xa5, + 0x11, 0x13, 0x11, 0xfe, 0x25, 0x8d, 0xb2, 0x69, 0x84, 0x7f, 0x4a, 0x23, 0x5a, 0x7c, 0x56, 0x11, 0x7e, 0x9e, 0x46, + 0x8c, 0x46, 0xf8, 0x85, 0xed, 0x68, 0x12, 0xe1, 0x9f, 0xd3, 0xe8, 0x66, 0x1a, 0xad, 0xb1, 0x52, 0x64, 0xf9, 0x9a, + 0x67, 0xec, 0x0f, 0x96, 0x46, 0xe3, 0xd6, 0xf8, 0x7c, 0x3c, 0x8e, 0x30, 0x15, 0x9a, 0xff, 0xb3, 0x60, 0x37, 0x4f, + 0x35, 0x24, 0x52, 0x36, 0x1c, 0xdd, 0x8f, 0x30, 0xfd, 0x67, 0x41, 0xd3, 0x68, 0x3c, 0x36, 0x05, 0xfe, 0x59, 0xd0, + 0x19, 0x2d, 0xde, 0xb2, 0x34, 0xba, 0x3f, 0x1e, 0x8f, 0x47, 0x27, 0x11, 0xa6, 0x5f, 0x17, 0x1f, 0x4d, 0x0b, 0xa6, + 0xc0, 0x90, 0xf1, 0x09, 0xd4, 0x3d, 0x1d, 0x9f, 0x8e, 0xb2, 0x08, 0x0f, 0xb9, 0xfa, 0x67, 0x01, 0xdf, 0x63, 0x76, + 0x92, 0x9d, 0x44, 0x78, 0x98, 0xd3, 0xec, 0x73, 0x1a, 0xb5, 0xcc, 0x2f, 0xf1, 0x0b, 0x1b, 0xbd, 0x9e, 0x49, 0x73, + 0x65, 0x30, 0x66, 0xc3, 0x6c, 0x14, 0x61, 0x33, 0x98, 0x31, 0xfc, 0xfd, 0xc2, 0xdf, 0x31, 0x9d, 0x46, 0xe7, 0xb4, + 0x33, 0x64, 0x9d, 0x08, 0x0f, 0xdf, 0xdc, 0x88, 0x34, 0xa2, 0xa7, 0x1d, 0xda, 0xa1, 0x11, 0x1e, 0x2e, 0x8a, 0xfc, + 0xee, 0x46, 0xca, 0x11, 0x00, 0x61, 0x78, 0x7e, 0x7e, 0x3f, 0xc2, 0x19, 0xfd, 0x55, 0x43, 0xed, 0xd3, 0xf1, 0x03, + 0x46, 0x5b, 0x11, 0xfe, 0x85, 0x16, 0xfa, 0xe3, 0x42, 0xb9, 0x81, 0xb6, 0x20, 0x45, 0x66, 0xef, 0x40, 0x9d, 0x1e, + 0x8d, 0x3a, 0x67, 0x0f, 0xda, 0x2c, 0xc2, 0xd9, 0xd5, 0x6b, 0xe8, 0xed, 0xfe, 0xf8, 0xb4, 0x05, 0x1f, 0x02, 0xe4, + 0x52, 0x56, 0x40, 0x23, 0x67, 0x27, 0x0f, 0x4e, 0xd9, 0xc8, 0x24, 0x2a, 0x9e, 0x7f, 0x36, 0xb3, 0x3f, 0x87, 0xf9, + 0x64, 0x05, 0x9f, 0x29, 0x29, 0xd2, 0x68, 0x94, 0xb5, 0x4f, 0x8e, 0x21, 0xe1, 0x8e, 0x0a, 0x0f, 0x9c, 0x5b, 0xa8, + 0x7a, 0x3e, 0x8c, 0xf0, 0xad, 0x4d, 0x3d, 0x1f, 0x9a, 0x8f, 0xc9, 0xbb, 0x5f, 0xc5, 0x9b, 0x51, 0x1a, 0x0d, 0xcf, + 0xcf, 0xcf, 0x5a, 0x90, 0xf0, 0x81, 0xde, 0xa5, 0x11, 0x7d, 0x00, 0xff, 0x41, 0xf6, 0xc7, 0x67, 0xd0, 0x21, 0x8c, + 0xf0, 0x76, 0xf2, 0x31, 0xcc, 0xf9, 0x3c, 0xa5, 0x9f, 0x79, 0x1a, 0x0d, 0x47, 0xc3, 0xfb, 0x67, 0x50, 0x6f, 0x46, + 0x27, 0xcf, 0x34, 0x85, 0x76, 0x5b, 0x2d, 0xd3, 0xf2, 0x3b, 0xfe, 0x85, 0x99, 0xea, 0xa7, 0xa7, 0x67, 0xc3, 0x0e, + 0x8c, 0xe0, 0x0a, 0x14, 0x2a, 0x30, 0x9e, 0xf3, 0xcc, 0x34, 0x78, 0x95, 0x3d, 0x1d, 0xa5, 0xd1, 0x83, 0x07, 0xc7, + 0x9d, 0x2c, 0x8b, 0xf0, 0xed, 0xc7, 0x91, 0xad, 0x6d, 0xf2, 0x14, 0xc0, 0x3e, 0x8d, 0xd8, 0x83, 0x07, 0x67, 0xf7, + 0x29, 0x7c, 0x3f, 0x37, 0x6d, 0x9d, 0x8f, 0x87, 0xd9, 0x39, 0xb4, 0xf5, 0x3b, 0x4c, 0xe7, 0xe4, 0xfc, 0x78, 0x64, + 0xfa, 0xfa, 0xdd, 0x8c, 0xba, 0x33, 0x3e, 0x19, 0x9f, 0x98, 0x4c, 0x33, 0xd4, 0xf2, 0xf3, 0x37, 0x96, 0x46, 0x19, + 0x1b, 0xb5, 0x23, 0x7c, 0xeb, 0x16, 0xee, 0xc1, 0x49, 0xab, 0x35, 0x3a, 0x8e, 0xf0, 0xe8, 0xe1, 0x7c, 0xfe, 0xd6, + 0x40, 0xb0, 0x7d, 0xf2, 0xc0, 0x7e, 0xab, 0xcf, 0x77, 0xd0, 0xf4, 0xd0, 0x00, 0x6d, 0xc4, 0x67, 0xa6, 0xe5, 0xb3, + 0x07, 0xf0, 0x9f, 0xf9, 0x36, 0x4d, 0x97, 0xdf, 0x72, 0x34, 0xb1, 0x8b, 0xd2, 0x66, 0x0f, 0x5a, 0x50, 0x63, 0xcc, + 0x3f, 0x0e, 0x0b, 0x0e, 0x68, 0x34, 0xec, 0xc0, 0xff, 0x45, 0x78, 0x9c, 0x5f, 0xbd, 0x76, 0x38, 0x3b, 0x1e, 0xd3, + 0x71, 0x2b, 0xc2, 0x63, 0xf9, 0x51, 0xe9, 0x0f, 0x0f, 0x45, 0x1a, 0x75, 0x3a, 0xe7, 0x43, 0x53, 0x66, 0xf1, 0x8b, + 0xe2, 0x06, 0x8f, 0x5b, 0xa6, 0x95, 0x09, 0x7d, 0xab, 0x86, 0x57, 0x12, 0x56, 0x12, 0xfe, 0x8b, 0xf0, 0x04, 0xb4, + 0x70, 0xae, 0x95, 0x73, 0xbb, 0x1d, 0x26, 0xef, 0x0c, 0x6a, 0x8e, 0xee, 0x03, 0xbc, 0xfc, 0x32, 0x8e, 0x28, 0x3d, + 0xed, 0xb4, 0x22, 0x6c, 0x46, 0x7d, 0xde, 0x82, 0xff, 0x22, 0x6c, 0x21, 0x67, 0xe0, 0x3a, 0xf9, 0xf8, 0xec, 0xe5, + 0x4d, 0x1a, 0xd1, 0xd1, 0x78, 0x0c, 0x4b, 0x62, 0x26, 0xe3, 0x8b, 0x4d, 0xa5, 0x60, 0x77, 0xbf, 0xde, 0xb8, 0xed, + 0x62, 0x12, 0xb4, 0x83, 0xce, 0xd9, 0x83, 0xe1, 0x49, 0x84, 0xdf, 0x8e, 0x38, 0x15, 0xb0, 0x4a, 0xd9, 0xe8, 0x34, + 0x3b, 0xcd, 0x4c, 0xc2, 0x44, 0xa6, 0xd1, 0x09, 0x2c, 0x79, 0x27, 0xc2, 0xfc, 0xcb, 0xd5, 0x9d, 0x45, 0x37, 0xa8, + 0xed, 0x10, 0x64, 0xdc, 0x62, 0x67, 0xe7, 0x59, 0x84, 0x73, 0xfa, 0xe5, 0xd9, 0xaf, 0x45, 0x1a, 0xb1, 0x33, 0x76, + 0x36, 0xa6, 0xfe, 0xfb, 0x0f, 0x35, 0x35, 0x35, 0x5a, 0xe3, 0x53, 0x48, 0xba, 0x11, 0x66, 0xac, 0xf7, 0xb3, 0xb1, + 0xc1, 0x90, 0x57, 0x33, 0x29, 0xb2, 0xa7, 0xe3, 0xb1, 0xb4, 0x58, 0x4c, 0x61, 0x13, 0xfe, 0x09, 0xd0, 0xa6, 0xa3, + 0xd1, 0x39, 0x3b, 0x8b, 0xf0, 0x9f, 0x76, 0x97, 0xb8, 0x09, 0xfc, 0x69, 0x31, 0x9b, 0xb9, 0xdd, 0xfe, 0xa7, 0x05, + 0x0a, 0xcc, 0x77, 0x4c, 0xc7, 0x74, 0xd4, 0x89, 0xf0, 0x9f, 0x06, 0x2e, 0xa3, 0x63, 0xf8, 0x0f, 0x0a, 0x40, 0x67, + 0x0f, 0x5a, 0x8c, 0x3d, 0x68, 0x99, 0xaf, 0x30, 0xcf, 0xcd, 0x7c, 0x78, 0x96, 0xb5, 0x23, 0xfc, 0xa7, 0x43, 0xc7, + 0xf1, 0x98, 0xb6, 0x00, 0x1d, 0xff, 0x74, 0xe8, 0xd8, 0x69, 0x0d, 0x3b, 0xd4, 0x7c, 0x5b, 0xac, 0x39, 0xbf, 0x9f, + 0x31, 0x98, 0xdc, 0x9f, 0x16, 0x21, 0xef, 0xdf, 0x3f, 0x3f, 0x7f, 0xf0, 0x00, 0x3e, 0x4d, 0xdb, 0xe5, 0xa7, 0xd2, + 0x0f, 0x73, 0x83, 0x64, 0xad, 0xec, 0x04, 0xe8, 0xe4, 0x9f, 0x66, 0x8c, 0xe3, 0xf1, 0x98, 0xb5, 0x22, 0x9c, 0xf3, + 0x19, 0xb3, 0x98, 0x60, 0x7f, 0x9b, 0x8e, 0x8e, 0x3b, 0xd9, 0xe8, 0xb8, 0x13, 0xe1, 0xfc, 0xed, 0x33, 0x33, 0x9b, + 0x16, 0xcc, 0xde, 0x6f, 0x39, 0x8f, 0x35, 0x33, 0xfa, 0x06, 0x06, 0x09, 0x2b, 0x0d, 0x95, 0xdf, 0x07, 0xf4, 0xf0, + 0xec, 0x2c, 0x1b, 0xc1, 0x40, 0xdf, 0x43, 0xb7, 0x00, 0xc6, 0xf7, 0x76, 0xf3, 0x0d, 0xe9, 0xe9, 0x29, 0x4c, 0xf7, + 0xfd, 0x7c, 0x51, 0xcc, 0x5f, 0xa5, 0xd1, 0x83, 0xe3, 0xfb, 0xad, 0xd1, 0x30, 0xc2, 0xef, 0xdd, 0x04, 0x8f, 0xb3, + 0xe1, 0xf1, 0xfd, 0x76, 0x84, 0xdf, 0x9b, 0xfd, 0x76, 0x7f, 0x78, 0x76, 0x0e, 0xe7, 0xc6, 0x7b, 0x35, 0x2f, 0xde, + 0x4e, 0x4c, 0x81, 0x31, 0x7d, 0x00, 0xcd, 0xfe, 0x66, 0x76, 0xe3, 0xa8, 0x0d, 0x1b, 0xf9, 0xbd, 0xd9, 0x64, 0x06, + 0x4f, 0xee, 0xb7, 0x4f, 0xcf, 0x4f, 0x23, 0x3c, 0xe3, 0x23, 0x01, 0x04, 0xde, 0x6c, 0x94, 0x07, 0xed, 0x07, 0xf7, + 0x5b, 0x11, 0x9e, 0xbd, 0xd5, 0xd9, 0x47, 0x3a, 0x33, 0xd4, 0x78, 0x0c, 0x30, 0x9b, 0x71, 0xa5, 0xef, 0xde, 0x28, + 0x47, 0x8f, 0x59, 0x3b, 0xc2, 0x33, 0x99, 0x65, 0x54, 0xbd, 0xb5, 0x09, 0xc3, 0xd3, 0x08, 0x0b, 0xfa, 0x85, 0xfe, + 0x2d, 0xfd, 0x66, 0x1a, 0x31, 0x3a, 0x32, 0x69, 0x06, 0x87, 0x23, 0xfc, 0x6e, 0x04, 0x97, 0x7e, 0x69, 0x34, 0x1e, + 0x8d, 0x4f, 0x01, 0x3c, 0x40, 0x80, 0x2c, 0x76, 0x03, 0x34, 0xe0, 0x6b, 0xf4, 0x68, 0x98, 0x46, 0x67, 0xc3, 0x73, + 0xd6, 0x39, 0x8e, 0x70, 0x49, 0x8d, 0xe8, 0x29, 0xe4, 0x9b, 0xcf, 0x8f, 0x66, 0x4b, 0x9d, 0xd8, 0x04, 0x03, 0xa0, + 0x11, 0xbd, 0xdf, 0x1a, 0x9d, 0x45, 0x78, 0xfe, 0x9a, 0xf9, 0x3d, 0xc6, 0x18, 0x3b, 0x07, 0x58, 0x42, 0x92, 0x41, + 0xa0, 0xf3, 0xf1, 0xf0, 0xc1, 0xb9, 0xf9, 0x06, 0x30, 0xd0, 0x31, 0x63, 0x00, 0xa4, 0xf9, 0x6b, 0x56, 0x02, 0x62, + 0x34, 0xbc, 0xdf, 0x02, 0xfa, 0x32, 0xa7, 0x73, 0x7a, 0x47, 0x6f, 0x9e, 0xce, 0xcd, 0x9c, 0xc6, 0xa3, 0xd3, 0x08, + 0xcf, 0x9f, 0xff, 0x32, 0x5f, 0x8c, 0xc7, 0x66, 0x42, 0x74, 0xf8, 0x20, 0xc2, 0x73, 0x56, 0x2c, 0x60, 0x8d, 0xce, + 0x4f, 0x8f, 0xc7, 0x11, 0x76, 0x68, 0x98, 0xb5, 0xb2, 0x21, 0xdc, 0x6a, 0x2e, 0x66, 0x69, 0x34, 0x1a, 0xd1, 0xd6, + 0x08, 0xee, 0x38, 0xe5, 0xcd, 0xaf, 0x85, 0x45, 0x23, 0x66, 0xf0, 0xc1, 0xad, 0x21, 0xcc, 0x17, 0xe0, 0xf1, 0x71, + 0xc8, 0xb2, 0x8c, 0xba, 0xc4, 0xb3, 0xb3, 0xe3, 0x63, 0xc0, 0x3d, 0x3b, 0x43, 0x8b, 0x20, 0x6f, 0xd4, 0xdd, 0xb0, + 0x90, 0x70, 0x74, 0x01, 0x51, 0x05, 0xb2, 0xfa, 0xe6, 0xee, 0xb5, 0xa1, 0xab, 0xed, 0xb3, 0x07, 0xb0, 0x00, 0x8a, + 0x8e, 0x46, 0xaf, 0xec, 0xe1, 0x76, 0x3e, 0x3c, 0x39, 0x6d, 0x1f, 0x47, 0xd8, 0x6f, 0x04, 0x7a, 0xde, 0xba, 0xdf, + 0x81, 0x12, 0x62, 0x74, 0x67, 0x4b, 0x8c, 0x4f, 0xe8, 0xc9, 0x59, 0x2b, 0xc2, 0x7e, 0x6b, 0xb0, 0xf3, 0xe1, 0xe9, + 0x7d, 0xf8, 0x54, 0x53, 0x96, 0xe7, 0x06, 0xbf, 0x4f, 0x01, 0x2e, 0x8a, 0x3f, 0x13, 0x34, 0x8d, 0x68, 0xeb, 0xb4, + 0xd3, 0x19, 0xc1, 0x67, 0xfe, 0x85, 0x15, 0x69, 0x94, 0xb5, 0xe0, 0xbf, 0x08, 0x07, 0x3b, 0x89, 0x0d, 0x23, 0x6c, + 0xf0, 0xee, 0x8c, 0x9e, 0x9a, 0xbd, 0xef, 0x76, 0x55, 0xeb, 0xbc, 0x05, 0x1b, 0xd6, 0x6d, 0x2a, 0xf7, 0xa5, 0x84, + 0xbc, 0x71, 0x24, 0x96, 0x46, 0x38, 0x40, 0xd0, 0xf1, 0xfd, 0x71, 0x84, 0xfd, 0x8e, 0x3b, 0x39, 0x3b, 0xef, 0x00, + 0x29, 0xd3, 0x40, 0x28, 0x46, 0x9d, 0xe1, 0x09, 0x90, 0x26, 0xcd, 0x5e, 0x5b, 0x3c, 0x89, 0xb0, 0x7e, 0xaa, 0xf4, + 0xab, 0x34, 0x1a, 0x9d, 0x0f, 0xc7, 0xa3, 0xf3, 0x08, 0x6b, 0x39, 0xa3, 0x5a, 0x1a, 0x0a, 0x78, 0x7c, 0x72, 0x3f, + 0xc2, 0x06, 0xcd, 0x5b, 0xac, 0x35, 0x6a, 0x45, 0xd8, 0x1d, 0x25, 0x8c, 0x9d, 0x77, 0x60, 0x5a, 0x3f, 0x3f, 0xd7, + 0x80, 0xcb, 0x23, 0x36, 0x3c, 0x8e, 0x70, 0x49, 0xef, 0x0d, 0x21, 0x82, 0x2f, 0x35, 0x93, 0x9f, 0x1d, 0xeb, 0x01, + 0xa4, 0xce, 0x6f, 0x78, 0x58, 0x86, 0x97, 0x37, 0x16, 0x8d, 0xa8, 0xd9, 0xe2, 0xc1, 0xad, 0xef, 0x13, 0x1a, 0x7b, + 0xb6, 0x9d, 0x93, 0xe5, 0x1a, 0x97, 0xc1, 0x54, 0x3f, 0xb3, 0x3b, 0x15, 0x2b, 0x65, 0x38, 0xd9, 0x20, 0x05, 0x1c, + 0x1e, 0x9c, 0xfb, 0x80, 0xf3, 0x10, 0x05, 0x41, 0x52, 0x90, 0x56, 0x57, 0x5c, 0x78, 0xaf, 0xd5, 0xae, 0x80, 0x10, + 0x0b, 0x90, 0x5e, 0x10, 0x4a, 0x34, 0x44, 0xa2, 0xb1, 0xc2, 0xa4, 0x37, 0xe6, 0x37, 0x32, 0xa5, 0xb4, 0xee, 0x01, + 0x94, 0x50, 0x1f, 0x83, 0x1e, 0xae, 0xa4, 0x21, 0x4a, 0x13, 0xea, 0x4a, 0x62, 0x22, 0x4a, 0xbf, 0x10, 0x3a, 0x56, + 0xaa, 0x5f, 0x0c, 0x70, 0xfb, 0x0c, 0x61, 0x88, 0xd5, 0x40, 0xfa, 0xf2, 0xf2, 0xb2, 0x7d, 0x76, 0x60, 0x84, 0xbe, + 0xcb, 0xcb, 0x73, 0xfb, 0x03, 0xfe, 0x1d, 0x54, 0xf1, 0xb1, 0x61, 0x7c, 0x8f, 0x58, 0xa0, 0xd1, 0x33, 0xfc, 0xf5, + 0x23, 0xb6, 0x5a, 0xc5, 0x8f, 0x18, 0x81, 0x19, 0xe3, 0x47, 0x2c, 0x31, 0x77, 0x24, 0xd6, 0x13, 0x87, 0xf4, 0x41, + 0x73, 0xd6, 0xc2, 0x10, 0xb5, 0xdd, 0x73, 0xde, 0x8f, 0x58, 0x9f, 0xd7, 0xbd, 0xb8, 0xab, 0x50, 0xc9, 0x07, 0x07, + 0xcb, 0x22, 0xd5, 0x56, 0x4c, 0xd0, 0x56, 0x4c, 0xd0, 0x56, 0x4c, 0xd0, 0x55, 0xb0, 0xfa, 0x93, 0x1e, 0x48, 0x29, + 0x46, 0xd9, 0xe2, 0x78, 0xea, 0x77, 0xa0, 0xf6, 0x00, 0xed, 0x64, 0xaf, 0x52, 0x76, 0x94, 0xba, 0x8a, 0x9d, 0x0a, + 0x8c, 0x9d, 0x89, 0x4e, 0xdb, 0x71, 0xf4, 0xef, 0xa8, 0x3b, 0x5e, 0xd6, 0xc4, 0xb2, 0x77, 0x3b, 0xc5, 0x32, 0x58, + 0x49, 0x23, 0x9a, 0xed, 0xdb, 0xb8, 0x1f, 0xba, 0x7f, 0xdf, 0x08, 0x66, 0x55, 0x30, 0xba, 0x06, 0x24, 0x75, 0x41, + 0x0a, 0x39, 0x37, 0x52, 0x5a, 0x81, 0xd2, 0x91, 0x8e, 0x0b, 0xd0, 0x50, 0x7a, 0x05, 0x65, 0x19, 0x33, 0xb5, 0x61, + 0x00, 0xa2, 0xac, 0x8c, 0x66, 0x65, 0xb5, 0x53, 0x10, 0x5d, 0x40, 0x13, 0x66, 0x24, 0x16, 0x68, 0x40, 0x98, 0x06, + 0x84, 0xab, 0x0c, 0xe2, 0x8c, 0xcb, 0x3e, 0x31, 0xd9, 0xca, 0x64, 0xab, 0x32, 0x5b, 0xfa, 0x6c, 0x2b, 0x24, 0x4a, + 0x93, 0x2d, 0xcb, 0x6c, 0x90, 0xd9, 0xf0, 0x24, 0x55, 0x78, 0x98, 0x4a, 0x2b, 0xaa, 0x55, 0xb2, 0xd5, 0x5b, 0x1a, + 0x6a, 0x73, 0x0f, 0x0e, 0xe2, 0x52, 0x4e, 0x32, 0x6a, 0xe2, 0x7b, 0x4b, 0x9e, 0x14, 0x46, 0x06, 0xe2, 0xc9, 0xc4, + 0xfd, 0x1d, 0xae, 0x37, 0x65, 0xa5, 0x62, 0x32, 0xfc, 0x46, 0x49, 0xf4, 0xc9, 0x2b, 0x51, 0x1f, 0x71, 0x13, 0x6d, + 0xe7, 0x82, 0x24, 0xad, 0xd6, 0x71, 0xfb, 0xb8, 0x75, 0xde, 0xe3, 0x87, 0xed, 0x4e, 0xf2, 0xa0, 0x93, 0x1a, 0x45, + 0xc4, 0x5c, 0xde, 0x80, 0x02, 0xe6, 0xa8, 0x93, 0x9c, 0xa0, 0xc3, 0x76, 0xd2, 0x3a, 0x3d, 0x6d, 0xc2, 0x3f, 0xf8, + 0xbd, 0x2e, 0xab, 0x9d, 0xb4, 0x4e, 0x4e, 0x7b, 0xfc, 0x68, 0xa3, 0x52, 0xcc, 0x1b, 0x50, 0x10, 0x1d, 0x99, 0x4a, + 0x18, 0xea, 0x57, 0xcb, 0xfb, 0x6c, 0x4b, 0xcf, 0xf3, 0x5e, 0xc7, 0xd2, 0xaa, 0xe2, 0x00, 0xaa, 0xfe, 0x6b, 0x62, + 0x80, 0xe8, 0xbf, 0x86, 0x65, 0x44, 0xdc, 0x65, 0x01, 0xa2, 0xf6, 0x23, 0x1e, 0x8b, 0x06, 0x3b, 0x8c, 0x6d, 0xbe, + 0x86, 0xba, 0x4d, 0x88, 0x52, 0x87, 0x27, 0x2e, 0x57, 0x85, 0xb9, 0x13, 0x84, 0x9a, 0x0a, 0x72, 0x87, 0x2e, 0x57, + 0x86, 0xb9, 0x43, 0x84, 0x9a, 0x12, 0x72, 0x69, 0xca, 0x13, 0x0a, 0x39, 0x3a, 0xa1, 0x4d, 0x03, 0xc9, 0x6a, 0x51, + 0x9e, 0x33, 0x3f, 0x6c, 0x3e, 0x86, 0xe5, 0x31, 0x04, 0xc5, 0x09, 0xd2, 0x02, 0x5e, 0x32, 0x29, 0xb5, 0x39, 0x2d, + 0x5c, 0xaa, 0x71, 0x20, 0xa3, 0x01, 0xff, 0x1c, 0x32, 0xf3, 0xbc, 0x45, 0xab, 0x77, 0x7c, 0xd6, 0x4a, 0xdb, 0xe0, + 0x92, 0x0d, 0xb2, 0xb6, 0xb0, 0xb2, 0xb6, 0xf0, 0xb2, 0xb6, 0xf0, 0xb2, 0x36, 0x08, 0xf0, 0x41, 0xdf, 0xff, 0xc8, + 0x9a, 0x99, 0x0b, 0x2f, 0x6d, 0x66, 0xac, 0x51, 0x44, 0xac, 0x57, 0xab, 0xe5, 0x1a, 0x2c, 0x9a, 0x2a, 0x95, 0xbb, + 0xaa, 0xd4, 0x9f, 0xcb, 0x22, 0x6d, 0xe1, 0x49, 0x0a, 0x5a, 0xee, 0x16, 0xa6, 0x66, 0x73, 0x7b, 0xaa, 0xb0, 0x19, + 0x2d, 0xa7, 0xe7, 0xd5, 0xc9, 0x97, 0xe4, 0xd8, 0x68, 0x8f, 0x97, 0x45, 0xca, 0x2d, 0xcd, 0xe0, 0x96, 0x66, 0x70, + 0x4b, 0x33, 0xa0, 0x11, 0x5c, 0x16, 0x36, 0x65, 0x13, 0x4a, 0xe0, 0x4a, 0xa0, 0x7f, 0x3c, 0x80, 0x60, 0x81, 0xb1, + 0x26, 0x66, 0xd4, 0x1b, 0x9d, 0xb7, 0x21, 0x38, 0x9a, 0x2d, 0xa9, 0x13, 0x6a, 0x7c, 0xc4, 0xcb, 0x31, 0x7f, 0xad, + 0xa1, 0x7d, 0x02, 0x2f, 0xd7, 0x3c, 0xd4, 0x71, 0x0b, 0x4c, 0x44, 0xa2, 0x22, 0xea, 0x19, 0xb2, 0x90, 0x1a, 0x9d, + 0x8d, 0x33, 0xfd, 0xfe, 0xbc, 0xe1, 0x71, 0x6b, 0x29, 0x41, 0xf8, 0x5e, 0xc3, 0x67, 0x56, 0x85, 0x00, 0x28, 0x2d, + 0x5b, 0x9d, 0x59, 0x9a, 0x3d, 0x12, 0xba, 0x60, 0x9e, 0xee, 0x63, 0x4b, 0xf5, 0x04, 0x91, 0xca, 0x38, 0x47, 0x92, + 0x2a, 0x3a, 0x32, 0x38, 0x0b, 0x93, 0x5b, 0x6a, 0x5c, 0x67, 0x5e, 0xd8, 0x3f, 0x5f, 0x69, 0xe0, 0x5b, 0x58, 0x4c, + 0x86, 0xde, 0x25, 0xf7, 0xda, 0xc4, 0x10, 0x22, 0xfb, 0xfb, 0xd6, 0x72, 0xdc, 0x7c, 0x6d, 0x9a, 0x8e, 0x9b, 0x44, + 0x93, 0x0d, 0x3b, 0xd4, 0xaf, 0xd1, 0x3f, 0xde, 0x33, 0xae, 0x98, 0x0c, 0x51, 0x40, 0xb3, 0x0d, 0x58, 0x65, 0x05, + 0x2c, 0xe5, 0xea, 0x95, 0x0e, 0x93, 0xd0, 0xbb, 0x19, 0xf3, 0xba, 0x98, 0x0c, 0x77, 0x3e, 0x71, 0x62, 0x7b, 0xec, + 0xbd, 0xa5, 0x41, 0x0f, 0x5e, 0xb5, 0x3d, 0x65, 0xb7, 0xdf, 0xab, 0x73, 0xb3, 0xb3, 0x8e, 0xca, 0xbf, 0x57, 0xe7, + 0xe9, 0xae, 0x3a, 0x33, 0x7e, 0x1b, 0xfb, 0xbd, 0xa3, 0x03, 0x35, 0xb6, 0x31, 0x47, 0x9a, 0x0c, 0x21, 0x26, 0x3d, + 0xfc, 0xb5, 0x91, 0x63, 0xba, 0x9e, 0x84, 0xc3, 0x2a, 0xc8, 0x5e, 0x72, 0x9a, 0x32, 0x4c, 0x49, 0xe7, 0xb0, 0x30, + 0xb1, 0x63, 0x44, 0x42, 0x9b, 0x2a, 0xa1, 0x38, 0x27, 0x71, 0x4c, 0x0f, 0x33, 0x88, 0x80, 0xd3, 0xee, 0xd1, 0x34, + 0xa6, 0x8d, 0x0c, 0x1d, 0xc5, 0xed, 0x06, 0x3d, 0xcc, 0x10, 0x6a, 0xb4, 0x41, 0x67, 0x2a, 0x49, 0xbb, 0x99, 0x43, + 0x4c, 0x4c, 0x43, 0x8a, 0xf3, 0x43, 0x91, 0x14, 0x0d, 0x79, 0xa8, 0x92, 0xa2, 0x91, 0x9c, 0x62, 0x91, 0x4c, 0xca, + 0xe4, 0x89, 0x49, 0x9e, 0xd8, 0xe4, 0x61, 0x99, 0x3c, 0x34, 0xc9, 0x43, 0x9b, 0x4c, 0x49, 0x71, 0x28, 0x12, 0xda, + 0x88, 0xdb, 0xcd, 0x02, 0x1d, 0xc2, 0x08, 0xfc, 0xe8, 0x89, 0x08, 0x43, 0x91, 0xaf, 0x8d, 0x2d, 0xcf, 0x5c, 0xe6, + 0x2e, 0x38, 0x68, 0x05, 0xa4, 0xd2, 0xc1, 0x0a, 0xea, 0x3c, 0x0b, 0xc0, 0x84, 0xb5, 0xfd, 0xe3, 0x43, 0xdf, 0xad, + 0xb3, 0x5c, 0x8a, 0xc0, 0x81, 0x0c, 0x6c, 0xde, 0x3f, 0x3b, 0xb7, 0x19, 0x80, 0xea, 0x9a, 0xe6, 0xf3, 0x29, 0xdd, + 0xf2, 0xd2, 0x2d, 0x26, 0x43, 0xb7, 0xb3, 0xca, 0x66, 0x18, 0x2d, 0x6c, 0x48, 0xe9, 0xba, 0x3f, 0x25, 0x80, 0xda, + 0xfb, 0x70, 0x26, 0xd4, 0x28, 0xc9, 0x6d, 0x8d, 0x49, 0xc1, 0xee, 0x54, 0x46, 0x73, 0x16, 0x57, 0x07, 0x70, 0x35, + 0x4c, 0x46, 0x5e, 0x80, 0xe5, 0x7d, 0x71, 0x98, 0x1c, 0x37, 0x74, 0x32, 0x39, 0x4c, 0x4e, 0x1f, 0x34, 0x74, 0x32, + 0x3c, 0x4c, 0xda, 0xed, 0x0a, 0x67, 0x93, 0x82, 0xe8, 0x64, 0x42, 0x34, 0x68, 0x0c, 0x6d, 0xa3, 0x72, 0x4e, 0xc1, + 0x94, 0xec, 0xdf, 0x18, 0x46, 0xc3, 0x0d, 0x43, 0xb0, 0x89, 0x8d, 0xae, 0xb9, 0x35, 0x86, 0xb0, 0x9b, 0xce, 0xe9, + 0x69, 0x53, 0x27, 0x05, 0xd6, 0x76, 0x25, 0x9b, 0x3a, 0x99, 0x60, 0x6d, 0x97, 0xaf, 0xa9, 0x93, 0xa1, 0x6d, 0xca, + 0xe8, 0x00, 0x99, 0x08, 0x80, 0xf5, 0x9c, 0x05, 0x90, 0xef, 0x78, 0x67, 0x98, 0x35, 0x68, 0x0d, 0xbf, 0x57, 0xae, + 0xe9, 0x0b, 0x2a, 0xaa, 0xc1, 0xa4, 0x88, 0x7d, 0xab, 0x68, 0xbb, 0x6a, 0x92, 0xfd, 0xeb, 0xb2, 0x65, 0xb3, 0x85, + 0xd4, 0xf5, 0x82, 0x0f, 0x6b, 0x18, 0xe2, 0x4a, 0xb9, 0x83, 0xfb, 0x15, 0x25, 0x31, 0xc4, 0xd0, 0x33, 0xa7, 0x10, + 0x27, 0x5e, 0x8f, 0x0c, 0x49, 0xbc, 0xd1, 0x58, 0xa3, 0x38, 0x38, 0x6f, 0x9f, 0x86, 0x54, 0x75, 0x2b, 0xb0, 0x1e, + 0x21, 0xd1, 0x42, 0x58, 0xd3, 0xcb, 0x51, 0x14, 0xb0, 0x20, 0x4e, 0xbb, 0x5b, 0x3b, 0x20, 0x0e, 0x0e, 0x36, 0xcf, + 0x0b, 0xff, 0xc4, 0xc1, 0xd6, 0xb3, 0x06, 0x95, 0xdd, 0x9e, 0x7f, 0x78, 0xc9, 0x5a, 0xf4, 0xf2, 0x00, 0x51, 0x7c, + 0x88, 0xab, 0xfb, 0x86, 0xc2, 0xf7, 0xab, 0xf8, 0x7e, 0x2e, 0xa7, 0x79, 0x66, 0x32, 0x4c, 0x5f, 0x83, 0x60, 0x6c, + 0x6f, 0xc2, 0x09, 0x95, 0x36, 0x89, 0xff, 0xb2, 0xe3, 0xa0, 0x13, 0xf7, 0x30, 0x4c, 0xd8, 0xe8, 0xdf, 0xa1, 0x05, + 0x70, 0x05, 0x1b, 0xe7, 0xfb, 0xbd, 0x5a, 0xd5, 0x9e, 0x01, 0xb2, 0x8f, 0xcd, 0xa0, 0x83, 0x03, 0xae, 0x9e, 0x81, + 0xd1, 0x32, 0x8b, 0x1b, 0xe1, 0xe1, 0xfb, 0x4f, 0xed, 0xb4, 0xfe, 0xdb, 0x9c, 0xab, 0x69, 0x70, 0xd0, 0x3d, 0xac, + 0xe5, 0xef, 0x5c, 0x89, 0x9e, 0x4e, 0xb9, 0x5b, 0xeb, 0xbf, 0x2b, 0x93, 0xf0, 0xad, 0x07, 0xa9, 0x0e, 0x0e, 0x78, + 0x15, 0x16, 0x2a, 0xfa, 0x21, 0x42, 0x3d, 0x23, 0x83, 0x3c, 0xcb, 0x25, 0x85, 0x1b, 0x51, 0xb8, 0x62, 0x48, 0x1b, + 0xfc, 0x48, 0xe3, 0x3f, 0xe4, 0xff, 0xa7, 0x46, 0x0e, 0x75, 0xda, 0xe0, 0x81, 0x00, 0x16, 0xb2, 0x42, 0x55, 0x40, + 0x46, 0x03, 0xe9, 0xd0, 0xc2, 0x1b, 0x95, 0x87, 0x39, 0x9d, 0xcf, 0xf3, 0x3b, 0xf3, 0x26, 0x57, 0xc0, 0x51, 0x55, + 0x17, 0x4d, 0x2e, 0x1a, 0x1e, 0x2e, 0x80, 0xa7, 0x07, 0xdc, 0x43, 0xc6, 0x9b, 0xb5, 0xbc, 0xdc, 0x16, 0x08, 0x24, + 0x33, 0x45, 0x64, 0xb3, 0xdd, 0x55, 0x97, 0x20, 0x97, 0x35, 0x9b, 0x48, 0xbb, 0x20, 0xe1, 0x98, 0x83, 0x4c, 0xa6, + 0xac, 0xc7, 0xea, 0x9e, 0x2d, 0x08, 0x92, 0x9b, 0x34, 0x22, 0xdb, 0xee, 0x52, 0x7c, 0x1c, 0x03, 0x1a, 0x21, 0x2b, + 0xf0, 0x85, 0xc2, 0x22, 0x07, 0xae, 0xb3, 0xf0, 0x1d, 0x7f, 0xa3, 0xa5, 0xa2, 0xaf, 0x06, 0x03, 0x5c, 0x98, 0x67, + 0x28, 0xca, 0xf9, 0x14, 0x2a, 0x78, 0xd6, 0x28, 0x10, 0x51, 0xf8, 0x6a, 0xb5, 0x0f, 0xaf, 0x06, 0xb9, 0x36, 0xc1, + 0xc5, 0xd5, 0xfd, 0xac, 0x5e, 0x08, 0x81, 0x71, 0x30, 0xd2, 0x32, 0x17, 0x85, 0x4e, 0xde, 0x64, 0x17, 0xa2, 0xdb, + 0x68, 0x30, 0x13, 0xd0, 0x89, 0x40, 0xf4, 0x36, 0xf0, 0x3f, 0x84, 0x3f, 0x36, 0x46, 0x93, 0x62, 0x36, 0xd2, 0x1d, + 0x84, 0xe0, 0xae, 0x25, 0xac, 0x56, 0xca, 0x46, 0x52, 0x31, 0x39, 0x36, 0xa6, 0x4a, 0xd9, 0x4f, 0x19, 0xb2, 0xb5, + 0x32, 0xe3, 0xe0, 0x6e, 0xab, 0xbf, 0xad, 0xf6, 0xf3, 0x1e, 0xb7, 0xd7, 0x78, 0xdc, 0xc4, 0x27, 0x30, 0x80, 0x5a, + 0x6e, 0x6c, 0x70, 0x6b, 0x4f, 0x1f, 0x5b, 0xe3, 0x5f, 0xb6, 0x09, 0x41, 0x51, 0xfa, 0xe3, 0xdb, 0x9b, 0x5b, 0x1f, + 0x9f, 0x50, 0x99, 0x39, 0x29, 0xa4, 0xfb, 0x20, 0x47, 0x0f, 0x08, 0x74, 0x6e, 0x7f, 0x56, 0x74, 0xa1, 0x92, 0x89, + 0xcb, 0x31, 0xfe, 0x12, 0xdc, 0xe6, 0xf5, 0xa3, 0xeb, 0x6b, 0xb3, 0xc9, 0xaf, 0xaf, 0x23, 0x1c, 0x1a, 0xb1, 0x47, + 0x01, 0x2f, 0x18, 0x0d, 0xca, 0x10, 0x56, 0x66, 0xe3, 0x37, 0xdb, 0x55, 0x63, 0xef, 0x69, 0x85, 0x77, 0xb0, 0x3c, + 0xa6, 0xf1, 0x2d, 0xa7, 0xcf, 0x3e, 0x07, 0x78, 0xb3, 0x3e, 0x1f, 0x74, 0xdf, 0xc4, 0x0a, 0x1d, 0x1c, 0xbc, 0x89, + 0x25, 0xea, 0x5d, 0x31, 0x73, 0xe7, 0x06, 0x5e, 0xdf, 0x7d, 0x6e, 0x86, 0x2f, 0x03, 0x04, 0xb8, 0x62, 0x9b, 0x92, + 0xcd, 0x5b, 0x13, 0x63, 0x23, 0x85, 0x18, 0xde, 0x10, 0x49, 0xd8, 0x81, 0x04, 0x7a, 0x7d, 0x13, 0x42, 0xbb, 0xcb, + 0x08, 0x03, 0x16, 0xbe, 0xf4, 0xc9, 0x63, 0xc9, 0x8c, 0x15, 0x13, 0x56, 0xac, 0x56, 0xef, 0xa9, 0xf5, 0xb3, 0xdb, + 0x08, 0x09, 0xa9, 0xba, 0x8d, 0x06, 0x35, 0xe3, 0x07, 0xf1, 0x81, 0x0e, 0xf0, 0xfe, 0x9b, 0xb8, 0x40, 0x08, 0x2c, + 0x8c, 0xb8, 0x58, 0x78, 0x9f, 0xb2, 0xac, 0xb6, 0x2e, 0x05, 0x2a, 0x1b, 0xc9, 0x49, 0x0b, 0x4f, 0x49, 0x56, 0xae, + 0xd1, 0xc5, 0xb4, 0xdb, 0x68, 0xe4, 0x48, 0xc6, 0x59, 0x3f, 0x1f, 0x60, 0x8e, 0x0b, 0xb8, 0x4c, 0xdd, 0x5e, 0x87, + 0x39, 0xab, 0x51, 0x2e, 0x37, 0xdf, 0xa5, 0x1d, 0x6b, 0xfa, 0x88, 0xae, 0x03, 0x60, 0x3c, 0xa2, 0x01, 0x91, 0xd8, + 0x05, 0x64, 0x61, 0x81, 0xac, 0x3c, 0x90, 0x85, 0x01, 0xb2, 0x42, 0xbd, 0x39, 0x04, 0x47, 0x52, 0x28, 0xdd, 0xa2, + 0xe8, 0xf5, 0x30, 0x9e, 0xce, 0x45, 0x04, 0x73, 0x13, 0x49, 0xc2, 0x2d, 0x07, 0xb8, 0x8b, 0x38, 0xaf, 0x43, 0x45, + 0x96, 0x51, 0x64, 0x22, 0xda, 0xe2, 0x5b, 0xf3, 0x27, 0xb9, 0xc5, 0x77, 0xf6, 0xc7, 0x5d, 0xa0, 0x4c, 0x7a, 0x5e, + 0xd3, 0x36, 0x70, 0x17, 0xff, 0x2d, 0x4a, 0x22, 0x40, 0x6b, 0x17, 0xcc, 0x50, 0xd4, 0xdf, 0x77, 0x53, 0x36, 0xec, + 0x84, 0x68, 0x10, 0x85, 0x45, 0x40, 0x3a, 0xff, 0xfa, 0x2b, 0x42, 0x3d, 0x01, 0x51, 0x83, 0xdc, 0xc9, 0xd6, 0x6c, + 0xa3, 0x46, 0x94, 0x44, 0x69, 0xec, 0x83, 0x52, 0xc0, 0xce, 0x88, 0xa2, 0xe0, 0x6d, 0x97, 0x72, 0x18, 0x1f, 0x6a, + 0xc3, 0x30, 0x83, 0xaa, 0xc2, 0x6c, 0x5c, 0x2e, 0x37, 0x83, 0x1a, 0x19, 0xa8, 0x0a, 0x13, 0x51, 0x06, 0xd9, 0x07, + 0xcf, 0x18, 0x61, 0x07, 0x07, 0xac, 0x2f, 0x06, 0xc1, 0x0b, 0x66, 0xd5, 0x75, 0xb8, 0x0e, 0x17, 0x2e, 0xa6, 0x10, + 0x55, 0x7e, 0xb5, 0xb2, 0x7f, 0xc9, 0x07, 0x23, 0xcd, 0xc0, 0x53, 0x74, 0xc1, 0x19, 0x2b, 0x76, 0xcb, 0x62, 0x89, + 0x96, 0xbf, 0x83, 0x65, 0x9f, 0x8b, 0x11, 0xc8, 0xdd, 0x54, 0xdb, 0x1e, 0xea, 0x73, 0xa3, 0x51, 0x08, 0x22, 0xf4, + 0x56, 0x47, 0x1a, 0x9e, 0xeb, 0x30, 0xaf, 0x16, 0x69, 0x37, 0x53, 0x65, 0xc0, 0x54, 0x38, 0x52, 0x12, 0xb0, 0x52, + 0x37, 0x74, 0x12, 0x7e, 0xd4, 0xa9, 0xa4, 0x63, 0x21, 0x01, 0x0a, 0x1c, 0x99, 0xcb, 0x79, 0x13, 0x10, 0x9f, 0xa1, + 0x1d, 0x44, 0x2e, 0x30, 0xa1, 0xa9, 0xcb, 0x96, 0x2e, 0x72, 0x55, 0x34, 0x93, 0x0b, 0xc5, 0x16, 0x73, 0x38, 0xdf, + 0xcb, 0xb4, 0x2c, 0xe7, 0xd9, 0xe7, 0x7a, 0x0a, 0x18, 0x44, 0xde, 0xea, 0x19, 0x13, 0x8b, 0xc8, 0xcd, 0xf3, 0xab, + 0x15, 0xf7, 0xdf, 0xbc, 0xc0, 0x8f, 0x48, 0xe7, 0xf0, 0x2b, 0xfe, 0x48, 0xc9, 0xa3, 0xc6, 0x57, 0x3c, 0xe1, 0xc4, + 0xf2, 0x06, 0xc9, 0x9b, 0xd7, 0x57, 0x2f, 0xde, 0xbd, 0x78, 0xff, 0xf4, 0xfa, 0xc5, 0xab, 0x67, 0x2f, 0x5e, 0xbd, + 0x78, 0xf7, 0x11, 0xff, 0x43, 0xc9, 0xd7, 0xa3, 0xf6, 0x79, 0x0b, 0x7f, 0x20, 0x5f, 0x8f, 0x3a, 0xf8, 0x56, 0x93, + 0xaf, 0x47, 0x27, 0x38, 0x57, 0xe4, 0xeb, 0x61, 0xe7, 0xe8, 0x18, 0x2f, 0xb4, 0x6d, 0x32, 0x97, 0x93, 0x76, 0x0b, + 0xff, 0xe3, 0xbe, 0x40, 0xbc, 0xaf, 0x66, 0x31, 0x61, 0x1b, 0xc6, 0x0f, 0xa6, 0x0c, 0x1d, 0x2a, 0x63, 0x88, 0x72, + 0x11, 0xa0, 0xd3, 0x54, 0x85, 0xe8, 0x64, 0x43, 0x36, 0x83, 0x0d, 0x23, 0xa0, 0x15, 0x27, 0xae, 0x1d, 0x7e, 0xd4, + 0x66, 0xc7, 0x40, 0x9f, 0x78, 0x29, 0x1c, 0x97, 0x2a, 0x9c, 0xb6, 0xd3, 0x62, 0x8c, 0x73, 0x29, 0x8b, 0x78, 0x01, + 0x8c, 0x80, 0xd1, 0x5a, 0xf0, 0xa3, 0x32, 0x36, 0x94, 0xb8, 0x20, 0xed, 0x5e, 0x3b, 0x15, 0x17, 0xa4, 0xd3, 0xeb, + 0xc0, 0x9f, 0xd3, 0xde, 0x69, 0xda, 0x6e, 0xa1, 0xc3, 0x60, 0x1c, 0x7f, 0xd4, 0xd0, 0xba, 0x3f, 0xc0, 0xae, 0x0b, + 0xf5, 0x4f, 0xa1, 0xbd, 0x4a, 0x4f, 0x38, 0x75, 0x6c, 0xbb, 0x2b, 0x2e, 0x98, 0xd1, 0xc3, 0xf2, 0x1f, 0x00, 0xb5, + 0x8d, 0xfb, 0x4a, 0xb9, 0x71, 0xdc, 0x2f, 0x7e, 0x24, 0x50, 0x2d, 0x00, 0x4d, 0xcc, 0x56, 0x2d, 0x04, 0x4c, 0xa3, + 0xc9, 0x06, 0x73, 0xa0, 0x44, 0xc9, 0x42, 0xfb, 0x20, 0xfa, 0xaa, 0x29, 0x51, 0x32, 0x97, 0xf3, 0xb8, 0xa6, 0x6a, + 0xf8, 0x35, 0x30, 0x73, 0xdc, 0xe7, 0xea, 0x15, 0x7d, 0x15, 0xd7, 0x78, 0x9e, 0x90, 0xb5, 0x0b, 0xb7, 0xc5, 0x2f, + 0xce, 0x8a, 0xa2, 0x06, 0xae, 0x12, 0xb0, 0x7e, 0x54, 0x4d, 0x7d, 0x01, 0xaf, 0x05, 0xb2, 0x86, 0xbe, 0x24, 0x01, + 0xf5, 0xfc, 0xa9, 0x34, 0xe3, 0x2a, 0x95, 0xd1, 0x5e, 0x11, 0x6d, 0xcc, 0x82, 0xbc, 0x22, 0xfa, 0x42, 0x19, 0x20, + 0x48, 0xc2, 0xfb, 0x62, 0x00, 0x07, 0xbe, 0x1d, 0xa0, 0x34, 0x74, 0x0e, 0xd4, 0x4a, 0x95, 0x99, 0x90, 0xf9, 0x34, + 0xa1, 0x10, 0x40, 0xf3, 0x54, 0xa9, 0xa0, 0xcc, 0x27, 0x96, 0x28, 0x18, 0xfa, 0x9f, 0xe1, 0x06, 0x38, 0x8c, 0x0d, + 0x2a, 0x06, 0xd9, 0xf7, 0x44, 0x3d, 0xbf, 0x7d, 0xde, 0x3a, 0xfa, 0x1a, 0xe4, 0x8f, 0x94, 0xb7, 0xf7, 0xf8, 0x3b, + 0xa0, 0xe4, 0x36, 0x78, 0x57, 0x1b, 0xfb, 0xb8, 0x6b, 0xdd, 0x10, 0x20, 0x87, 0x1a, 0x1d, 0x99, 0x87, 0x13, 0xbb, + 0x48, 0x1f, 0x92, 0x76, 0x0b, 0x82, 0xa5, 0xed, 0xa0, 0x7c, 0x3f, 0x6d, 0xc0, 0x54, 0x27, 0xb7, 0x4d, 0xa0, 0xd5, + 0xf0, 0x96, 0xd2, 0x5d, 0x93, 0x27, 0x77, 0x58, 0x05, 0x38, 0xc3, 0x0e, 0x59, 0x43, 0x1c, 0x0a, 0xe4, 0x82, 0xcc, + 0xda, 0x0d, 0xa0, 0xa9, 0xe8, 0xd8, 0x37, 0xfd, 0xbc, 0x71, 0xd4, 0x45, 0x33, 0x39, 0x3d, 0xfc, 0x7a, 0x70, 0x10, + 0xcb, 0x06, 0x79, 0x84, 0xf0, 0x92, 0x82, 0xcd, 0x36, 0xf8, 0xb8, 0x71, 0xcb, 0xc4, 0xa7, 0x2a, 0xa0, 0x8e, 0x0b, + 0x55, 0x3b, 0xd6, 0xaa, 0xce, 0xca, 0xdd, 0xe0, 0xc7, 0xd4, 0x41, 0x8d, 0x20, 0xcd, 0x8e, 0xae, 0x13, 0x42, 0xf9, + 0xb7, 0x9a, 0x43, 0x1a, 0x6c, 0xcb, 0xc6, 0x47, 0x8a, 0x7e, 0x78, 0xd4, 0xfc, 0x1a, 0x94, 0xa9, 0x99, 0x26, 0x3d, + 0x6a, 0x3c, 0x42, 0x3f, 0x3c, 0x0a, 0x5c, 0x0a, 0x79, 0xc5, 0x9e, 0x78, 0x6e, 0xe4, 0x37, 0xcb, 0x95, 0xfe, 0x06, + 0x92, 0x7d, 0x41, 0x7e, 0x03, 0x2c, 0xa7, 0xe4, 0xb7, 0x58, 0x36, 0x21, 0xd4, 0x22, 0xf9, 0x2d, 0x2e, 0xe0, 0x47, + 0x4e, 0x7e, 0x8b, 0x01, 0xdb, 0xf1, 0xd4, 0xfc, 0x28, 0x4a, 0x60, 0x80, 0x1b, 0x9b, 0xb4, 0xde, 0x6c, 0xc5, 0x6a, + 0x25, 0x0e, 0x0e, 0xa4, 0xfd, 0x45, 0x2f, 0xb3, 0x83, 0x83, 0xfc, 0x62, 0x1a, 0xd8, 0xde, 0xea, 0x5d, 0xf4, 0xc5, + 0x20, 0x14, 0x0e, 0x4c, 0xd3, 0x78, 0x0d, 0xaf, 0x6a, 0x94, 0x15, 0x1a, 0x68, 0x1e, 0x77, 0xee, 0x9f, 0x9d, 0x63, + 0xf8, 0xf7, 0x7e, 0x50, 0xf0, 0xe7, 0x92, 0xef, 0x22, 0x6d, 0xd6, 0x3c, 0xab, 0xea, 0x5c, 0x06, 0xf8, 0x8c, 0x19, + 0x6a, 0x8a, 0x83, 0x03, 0x7e, 0x11, 0xe0, 0x32, 0x66, 0xa8, 0x11, 0x58, 0xec, 0x3d, 0x2c, 0xed, 0xc9, 0x0c, 0xd7, + 0x04, 0x8f, 0xe8, 0xf2, 0x7e, 0x31, 0xb8, 0xd0, 0x8e, 0x9a, 0x84, 0xa1, 0xb6, 0x15, 0x69, 0xb9, 0x4d, 0xd6, 0x15, + 0x4d, 0x75, 0xd9, 0xee, 0x22, 0x49, 0x54, 0x43, 0x5c, 0x5e, 0xb6, 0x31, 0xa8, 0xe4, 0x7b, 0x8a, 0xc8, 0x54, 0x10, + 0xef, 0x0e, 0xb8, 0xcc, 0x65, 0xaa, 0xf0, 0x94, 0xa7, 0xc2, 0xcb, 0xd9, 0xaf, 0xbd, 0xf5, 0xb4, 0x71, 0xd0, 0x34, + 0x3d, 0x33, 0x2c, 0x7a, 0xaa, 0x74, 0x2c, 0x84, 0x4d, 0xaa, 0x06, 0xf0, 0x46, 0x61, 0x89, 0x79, 0xcc, 0x7a, 0xd3, + 0x31, 0x88, 0x01, 0xad, 0x1a, 0x6d, 0xc8, 0x84, 0xcf, 0x75, 0xaa, 0x60, 0xa0, 0xa6, 0xf0, 0x05, 0x90, 0xa9, 0xac, + 0x32, 0xcc, 0xf6, 0x0d, 0x43, 0x01, 0x01, 0x05, 0x2e, 0x09, 0x0b, 0x24, 0x78, 0xb8, 0xfd, 0x08, 0x08, 0x47, 0x9d, + 0x5c, 0xd8, 0xc9, 0x5d, 0x28, 0xe8, 0x4e, 0x0c, 0x2e, 0x74, 0x17, 0x89, 0x46, 0xc3, 0x71, 0xdb, 0x97, 0xc2, 0x0c, + 0xa2, 0xd9, 0x1e, 0x5c, 0xb2, 0x2e, 0x52, 0xcd, 0x66, 0x69, 0x00, 0x79, 0xd9, 0x5a, 0xad, 0xd4, 0x85, 0x6f, 0xa4, + 0xe7, 0xcf, 0x71, 0xc3, 0x77, 0x79, 0xc1, 0xf3, 0x37, 0x49, 0xfa, 0x11, 0x50, 0x55, 0xe0, 0xb3, 0xe5, 0x3c, 0xc2, + 0x91, 0x79, 0xbe, 0x0e, 0xfe, 0x9a, 0x67, 0xc7, 0x22, 0x1c, 0xb9, 0x17, 0xed, 0xa2, 0x41, 0x35, 0x58, 0x9e, 0x95, + 0xc1, 0xd8, 0x79, 0x72, 0x0d, 0x8c, 0x83, 0xfe, 0x5b, 0xa1, 0x65, 0xf5, 0x3b, 0xc9, 0x5d, 0x58, 0x12, 0xe5, 0x1f, + 0x59, 0x73, 0xa3, 0x5a, 0xef, 0x76, 0x04, 0xe5, 0x38, 0xf2, 0x55, 0xe1, 0xb1, 0x82, 0xef, 0xbc, 0xf2, 0xd8, 0x76, + 0x8f, 0x8b, 0x2f, 0xcb, 0x1e, 0x80, 0xf3, 0x5e, 0xaf, 0x11, 0xfe, 0x4d, 0xee, 0x7c, 0x69, 0x38, 0xba, 0x96, 0xe2, + 0x09, 0xd5, 0x34, 0x6a, 0xbc, 0x31, 0x86, 0x6f, 0x56, 0xce, 0xea, 0x7e, 0x6b, 0x1c, 0xec, 0xdf, 0xea, 0x1e, 0x02, + 0x45, 0xd4, 0x1e, 0x45, 0xb2, 0xb2, 0xaf, 0x09, 0x0f, 0x22, 0x03, 0xd3, 0xb7, 0x1d, 0xf0, 0xf0, 0x63, 0xa4, 0xe0, + 0xbe, 0x6c, 0xf9, 0x24, 0x0a, 0x11, 0x58, 0x6b, 0x4e, 0xd3, 0x90, 0x62, 0xfb, 0x30, 0x0e, 0xb6, 0x6b, 0x14, 0x72, + 0xdd, 0x63, 0x55, 0x27, 0xa6, 0x55, 0x37, 0x46, 0xea, 0x60, 0x9b, 0x2c, 0x38, 0xab, 0x7a, 0x37, 0x12, 0x4a, 0xf5, + 0x7e, 0x9c, 0x79, 0x03, 0xb4, 0xd9, 0x36, 0x8f, 0x2a, 0xdb, 0x57, 0xe6, 0x14, 0x18, 0xf2, 0xee, 0x97, 0xc1, 0xb1, + 0x2e, 0xe1, 0xd8, 0x8d, 0x03, 0xc8, 0x4a, 0x72, 0xb9, 0x74, 0x2f, 0xc0, 0xf1, 0xbe, 0x1c, 0xac, 0xcb, 0xf7, 0xe0, + 0x02, 0x3c, 0xa8, 0x46, 0x2a, 0xb2, 0x90, 0x33, 0xf0, 0x8f, 0x29, 0xd6, 0xf4, 0x43, 0xfc, 0x2b, 0x1c, 0xf0, 0x15, + 0x92, 0xa6, 0x56, 0xfd, 0x04, 0xef, 0x34, 0x81, 0xc2, 0xdb, 0xd6, 0xfd, 0x53, 0x86, 0x0e, 0xb1, 0x75, 0x9d, 0x8a, + 0xf5, 0x39, 0xad, 0x2b, 0x56, 0xca, 0xc2, 0x01, 0xd5, 0x8a, 0xd1, 0x3a, 0x75, 0xfe, 0xa9, 0xee, 0x71, 0xa7, 0x87, + 0x02, 0x7c, 0x63, 0xb8, 0x14, 0xcf, 0x0a, 0x88, 0xd6, 0x15, 0xea, 0xd3, 0x7e, 0x96, 0xe1, 0xeb, 0xc5, 0x7d, 0xb8, + 0x27, 0x2c, 0x79, 0xce, 0xf2, 0xc5, 0x6d, 0x58, 0x20, 0x05, 0x14, 0x4a, 0x61, 0xb1, 0x5a, 0xc5, 0x02, 0x02, 0x36, + 0xfc, 0xe9, 0x42, 0xf8, 0xba, 0xb7, 0x3a, 0x8c, 0xfe, 0x0e, 0xea, 0x62, 0xaf, 0x1e, 0x31, 0x26, 0xac, 0x28, 0xbc, + 0x74, 0x52, 0x59, 0xd0, 0xd7, 0xae, 0x3e, 0x44, 0x35, 0xe5, 0x5e, 0x6c, 0xf4, 0xbd, 0xef, 0xf8, 0x8c, 0xc9, 0x05, + 0x3c, 0x92, 0x84, 0x19, 0x51, 0x4c, 0xfb, 0x6f, 0xa0, 0x20, 0xf0, 0xd2, 0x0e, 0x0f, 0xf1, 0x11, 0xf8, 0x2a, 0x4f, + 0xeb, 0x64, 0xe6, 0x9f, 0xde, 0x88, 0x4c, 0x68, 0xcc, 0xa8, 0x17, 0x81, 0x17, 0x11, 0x88, 0x50, 0x84, 0x44, 0x4c, + 0x8c, 0xa2, 0x5e, 0x64, 0x5c, 0xb2, 0x22, 0xb0, 0x1a, 0x03, 0x25, 0x77, 0x84, 0xe7, 0xaa, 0x22, 0x62, 0x61, 0x4d, + 0x1d, 0x54, 0x62, 0xa9, 0x31, 0xd3, 0x3e, 0xea, 0x54, 0x20, 0x2c, 0xb2, 0x4d, 0x41, 0x59, 0x6f, 0xa8, 0x0b, 0xb0, + 0x24, 0xc6, 0xf4, 0x96, 0x27, 0xd7, 0xc0, 0xcd, 0xb1, 0x91, 0x2b, 0xba, 0xe4, 0x57, 0xa0, 0x9e, 0x4e, 0x0b, 0x7c, + 0x6d, 0x18, 0xb6, 0x51, 0x4a, 0xd7, 0x84, 0xe3, 0x8c, 0x14, 0x09, 0xbd, 0x85, 0x18, 0x16, 0x33, 0x2e, 0xd2, 0x1c, + 0xcf, 0xe8, 0x6d, 0x3a, 0xc5, 0x33, 0x2e, 0x9e, 0xd8, 0x65, 0x4f, 0x47, 0x90, 0xe4, 0x3f, 0x16, 0x6b, 0x62, 0x9e, + 0xe0, 0xfa, 0x5d, 0xb1, 0xe2, 0x11, 0xf0, 0x2a, 0x2a, 0x46, 0xdd, 0x91, 0xb1, 0x29, 0xe7, 0xba, 0x32, 0x5e, 0x7f, + 0xad, 0x63, 0x8a, 0x33, 0x9c, 0xa3, 0x24, 0x97, 0x98, 0xf5, 0x44, 0xfa, 0x1a, 0xe2, 0x57, 0x67, 0xd8, 0x3e, 0xdf, + 0xc5, 0x6f, 0x59, 0xfe, 0x4c, 0x16, 0xef, 0xcd, 0x96, 0xcf, 0x11, 0x14, 0x02, 0x17, 0x15, 0xd1, 0x84, 0xdb, 0xbd, + 0x45, 0x4f, 0x56, 0x4d, 0xd1, 0x5b, 0xdb, 0x94, 0x1b, 0xe2, 0x14, 0x02, 0xff, 0x26, 0x53, 0xde, 0x68, 0x63, 0xd6, + 0x6b, 0x7d, 0xa7, 0xd1, 0x29, 0x2a, 0x4b, 0x22, 0x0c, 0x6b, 0xd5, 0x54, 0xa9, 0x24, 0xa2, 0xa9, 0x9c, 0x84, 0xb7, + 0x34, 0xc0, 0x4e, 0x15, 0xce, 0xe4, 0x42, 0xe8, 0x54, 0x06, 0x78, 0x43, 0xab, 0xcd, 0xb5, 0xbc, 0xb5, 0x10, 0xd3, + 0xf8, 0xce, 0xfe, 0x60, 0xf8, 0xda, 0xa8, 0xf8, 0xdf, 0x82, 0x61, 0x8f, 0x4a, 0x05, 0xc0, 0x0f, 0x0c, 0x67, 0x01, + 0x72, 0x96, 0x9f, 0xbc, 0x05, 0xf0, 0x59, 0x16, 0xf2, 0x0e, 0x52, 0x99, 0x49, 0xbd, 0x83, 0x54, 0x06, 0xa9, 0xc6, + 0x73, 0x7d, 0x5f, 0x54, 0xca, 0xa2, 0xb0, 0x41, 0xa2, 0x70, 0xa9, 0x0e, 0x96, 0x44, 0x24, 0xd0, 0xae, 0x11, 0xe5, + 0x66, 0x5c, 0x40, 0x08, 0x43, 0x68, 0xdc, 0x7e, 0xd3, 0x5b, 0xf8, 0xbe, 0xb3, 0xf9, 0xcc, 0xe7, 0xdf, 0xd9, 0x7c, + 0xd3, 0x91, 0xc7, 0xf8, 0xfa, 0x6d, 0xa7, 0xb1, 0x8c, 0x97, 0x0e, 0x6b, 0x3f, 0x94, 0x0f, 0xc6, 0xb4, 0xcc, 0xc3, + 0xdc, 0xa4, 0x8d, 0x27, 0x01, 0x52, 0x36, 0x2b, 0x1e, 0xae, 0x83, 0xdb, 0xad, 0xc3, 0x98, 0x37, 0x49, 0x1b, 0xa1, + 0x43, 0x27, 0x5c, 0x89, 0xd8, 0x48, 0x4e, 0x87, 0x8f, 0x8e, 0xe0, 0xee, 0x65, 0xa6, 0x36, 0x7c, 0xa5, 0x6c, 0xb5, + 0x66, 0xbb, 0x75, 0xc8, 0x77, 0x56, 0x69, 0xb4, 0xf1, 0x8c, 0x91, 0x25, 0x78, 0xa0, 0xd1, 0xc2, 0xaa, 0x1a, 0xc0, + 0x65, 0xf5, 0x85, 0xf8, 0x6d, 0x41, 0x47, 0xe6, 0xfb, 0xd0, 0xa6, 0xbc, 0x5e, 0x68, 0x9f, 0xd4, 0xe4, 0x30, 0x88, + 0x0e, 0x72, 0x25, 0x83, 0x9c, 0x98, 0x1f, 0x91, 0xe4, 0x14, 0x5d, 0xb4, 0x7b, 0xc9, 0xe9, 0x21, 0x3f, 0xe4, 0x29, + 0xf0, 0xb0, 0x71, 0xd3, 0x57, 0x68, 0xb6, 0x7d, 0x9d, 0xc7, 0x8b, 0x21, 0xcf, 0x5c, 0xf3, 0x55, 0x07, 0x65, 0xaa, + 0x9d, 0x23, 0x64, 0x01, 0x8a, 0xf9, 0x5e, 0x82, 0xec, 0x7a, 0x37, 0x87, 0x3c, 0x85, 0x7e, 0xa0, 0x56, 0xc7, 0xd6, + 0x2a, 0x07, 0xf7, 0xdb, 0x02, 0x10, 0xcc, 0x77, 0x54, 0x9b, 0x8b, 0x4d, 0x6f, 0xc6, 0x55, 0x67, 0x87, 0xbc, 0x1a, + 0x61, 0x58, 0x66, 0xbb, 0x3f, 0x3f, 0xb5, 0xaa, 0xcb, 0xc3, 0x00, 0x22, 0xbf, 0x2d, 0xb8, 0x08, 0x3b, 0x0d, 0xbb, + 0x75, 0x39, 0x61, 0xa7, 0xf5, 0x59, 0x06, 0x45, 0xb6, 0x7b, 0xdd, 0x9a, 0x69, 0x7d, 0xb6, 0x57, 0xe0, 0x48, 0x08, + 0x93, 0x32, 0x2b, 0x9d, 0xc1, 0x15, 0xfa, 0xe1, 0x07, 0xe4, 0x5a, 0x7f, 0xbd, 0xd0, 0x3e, 0xbf, 0x44, 0x04, 0xc8, + 0xae, 0xba, 0x2e, 0xab, 0x43, 0x1f, 0x65, 0x13, 0x5f, 0x0f, 0x79, 0xb0, 0x72, 0x4f, 0x6f, 0xe7, 0x32, 0xf5, 0xf8, + 0xda, 0x6b, 0xa5, 0x5b, 0xc8, 0x09, 0xc4, 0xc3, 0x75, 0x17, 0x96, 0x05, 0x39, 0xbb, 0xb9, 0x85, 0x92, 0xe1, 0xc4, + 0x7d, 0xe9, 0x0f, 0xcc, 0x5e, 0x37, 0xf0, 0x8b, 0xe4, 0x14, 0xa6, 0xbe, 0xd9, 0xc3, 0x61, 0x07, 0xfa, 0x30, 0x70, + 0xd8, 0x6c, 0xd0, 0x67, 0x56, 0x10, 0x79, 0xcc, 0x0b, 0x8b, 0x67, 0x97, 0xa4, 0xdd, 0xe3, 0xa9, 0xdb, 0x4c, 0x46, + 0x34, 0x6a, 0x37, 0x79, 0x30, 0x33, 0xc0, 0x2f, 0x57, 0x36, 0x2c, 0xe2, 0xd7, 0x29, 0x80, 0x92, 0x2f, 0x56, 0xad, + 0x4f, 0x05, 0xaf, 0x7a, 0xc3, 0xe9, 0x66, 0xba, 0x5f, 0x37, 0xb8, 0xdd, 0xf5, 0xf0, 0x84, 0x07, 0x5f, 0x2c, 0x5a, + 0xfb, 0x89, 0x4f, 0x80, 0x03, 0x4a, 0x5a, 0xf7, 0x4f, 0xc1, 0x85, 0xb2, 0x84, 0xe5, 0x76, 0xb9, 0xd9, 0x56, 0x39, + 0x0b, 0x47, 0x5b, 0x32, 0xe0, 0x0e, 0x36, 0x21, 0x0a, 0x1d, 0x1c, 0x76, 0x70, 0xd2, 0x6e, 0x77, 0x4e, 0x71, 0x72, + 0x72, 0x0a, 0x03, 0x6d, 0x24, 0xa7, 0x87, 0x33, 0x65, 0x01, 0x18, 0xe4, 0xac, 0x5d, 0xbb, 0x8f, 0x20, 0x38, 0x54, + 0x28, 0x5e, 0xf3, 0xc3, 0x38, 0x6e, 0x27, 0xf7, 0x5b, 0xed, 0xd3, 0xf3, 0x06, 0x00, 0xa8, 0xe9, 0x3e, 0x5c, 0x8d, + 0xd7, 0x0b, 0x5d, 0xaf, 0x52, 0x22, 0x7c, 0xbd, 0x5a, 0xc3, 0x57, 0x6b, 0xb4, 0xd7, 0xd5, 0x14, 0x7c, 0x55, 0x27, + 0x9c, 0xdb, 0x22, 0x5e, 0x69, 0x13, 0x6e, 0x8b, 0xd8, 0x0e, 0x24, 0x06, 0xe9, 0x3c, 0x39, 0xed, 0x9c, 0x22, 0x3b, + 0x16, 0xed, 0xf0, 0xa3, 0xdc, 0x27, 0x5b, 0x45, 0x1a, 0x1a, 0x90, 0xa4, 0x9c, 0x9d, 0x5c, 0x80, 0x44, 0xcd, 0xc9, + 0x65, 0xbb, 0x39, 0x63, 0x89, 0x9f, 0x80, 0x49, 0x85, 0xe5, 0x2c, 0x57, 0xc1, 0x25, 0x05, 0x80, 0xb8, 0x00, 0xe3, + 0xa2, 0xfb, 0xa7, 0xbd, 0xfb, 0xc9, 0xe9, 0x59, 0xc7, 0x12, 0x3d, 0x7e, 0xd1, 0xa9, 0xa5, 0x99, 0xa9, 0x27, 0xa7, + 0x26, 0x0d, 0xba, 0x4e, 0xee, 0x9f, 0x42, 0x19, 0x97, 0x12, 0x96, 0x82, 0xa0, 0x16, 0x55, 0x31, 0x88, 0x64, 0x91, + 0xd6, 0x72, 0xcf, 0x6a, 0xd9, 0xe7, 0x27, 0xc7, 0xf7, 0x4f, 0x43, 0xa8, 0x95, 0xb3, 0x30, 0x0b, 0xed, 0x26, 0xe2, + 0x67, 0x07, 0x4b, 0x8b, 0x0e, 0x93, 0xd3, 0x74, 0x6b, 0x82, 0x76, 0xd3, 0x1c, 0x1a, 0x1c, 0x08, 0x14, 0x8e, 0x4f, + 0x85, 0xd3, 0x97, 0x04, 0xf7, 0x63, 0x95, 0xa1, 0x49, 0xa8, 0x70, 0xf6, 0xf7, 0x94, 0xc1, 0xbb, 0x95, 0xe1, 0x55, + 0xe5, 0x63, 0x2a, 0xbe, 0x50, 0xf5, 0x86, 0x42, 0xa4, 0x0e, 0x31, 0x88, 0x5c, 0x1c, 0xf1, 0x7a, 0xee, 0x4f, 0xe0, + 0x22, 0xcc, 0x04, 0x5c, 0x68, 0x7a, 0x25, 0x68, 0xc5, 0x0b, 0x0c, 0x43, 0x87, 0x5a, 0x33, 0xac, 0x1e, 0x4f, 0x9d, + 0x49, 0x41, 0xa8, 0xdb, 0x7a, 0xce, 0xbf, 0x57, 0x2e, 0x29, 0xaf, 0xb2, 0x93, 0x53, 0x94, 0xb8, 0xcb, 0xf2, 0xa4, + 0x8d, 0x92, 0xc0, 0x84, 0xc4, 0x1d, 0xc9, 0x59, 0x46, 0xfa, 0xd1, 0x6d, 0x84, 0xa3, 0xbb, 0x08, 0x47, 0xd6, 0x87, + 0xf9, 0x03, 0xf8, 0x71, 0x47, 0x38, 0xb2, 0xae, 0xcc, 0x11, 0x8e, 0x34, 0x13, 0x10, 0xc0, 0x2b, 0x1a, 0xe0, 0x1c, + 0x4a, 0x1b, 0xcf, 0xea, 0xb2, 0xf4, 0x63, 0xff, 0x55, 0xba, 0x5e, 0xdb, 0x94, 0x40, 0xca, 0x9c, 0x9a, 0x1d, 0x6a, + 0x1f, 0xa0, 0x8e, 0xa8, 0x67, 0xd6, 0x23, 0x0c, 0x02, 0x08, 0xbd, 0xf3, 0x0f, 0xd8, 0x55, 0xb1, 0x3f, 0xd8, 0x31, + 0xac, 0x34, 0xb8, 0xa2, 0x47, 0xe1, 0x19, 0x16, 0xe1, 0xb1, 0xf0, 0x85, 0x41, 0xac, 0xf0, 0xbf, 0x73, 0x29, 0xe7, + 0xfe, 0xb7, 0x96, 0xe5, 0x2f, 0x78, 0xf6, 0xc4, 0x59, 0xb4, 0x80, 0xe5, 0x96, 0x0d, 0x35, 0x34, 0x64, 0xf5, 0x11, + 0x5c, 0x8f, 0x5d, 0x38, 0x38, 0x90, 0x08, 0xaf, 0x8d, 0x40, 0xe5, 0xe5, 0xc3, 0x6b, 0x1b, 0x9a, 0xc8, 0x7c, 0x42, + 0x6c, 0x32, 0x08, 0x3f, 0x2c, 0xe1, 0x42, 0x63, 0x52, 0x30, 0xa5, 0x22, 0x1b, 0xb3, 0x2f, 0x92, 0xc2, 0x3f, 0xc2, + 0xe8, 0x53, 0xc6, 0x22, 0x32, 0x1d, 0xd6, 0x67, 0x6b, 0xc5, 0xe1, 0x5c, 0x16, 0x2a, 0xb5, 0x2f, 0xb2, 0x78, 0x30, + 0xce, 0xcb, 0xe7, 0x0e, 0xd3, 0x3c, 0x5b, 0x63, 0x7b, 0x87, 0x5d, 0x16, 0x72, 0x57, 0xda, 0x61, 0xa9, 0x2c, 0x5b, + 0x7f, 0x6b, 0x42, 0xaa, 0x36, 0xa3, 0x60, 0xa2, 0xd5, 0x80, 0xaa, 0xc0, 0x1d, 0x50, 0xd8, 0x06, 0x7f, 0x49, 0x97, + 0x65, 0xc9, 0x74, 0x59, 0x2e, 0xc3, 0x49, 0xab, 0xb5, 0x5e, 0xe3, 0x82, 0x99, 0x20, 0x34, 0x3b, 0x4b, 0x40, 0xbe, + 0x9a, 0xca, 0x9b, 0x20, 0x57, 0xa5, 0xe5, 0x2c, 0xcd, 0x12, 0x45, 0x81, 0x11, 0x6c, 0xb4, 0xc6, 0x5f, 0xb8, 0xe2, + 0x00, 0x4f, 0x37, 0xbb, 0xa1, 0x94, 0x39, 0xa3, 0x10, 0xab, 0x2c, 0x68, 0x72, 0x8d, 0xa7, 0x7c, 0xc4, 0x76, 0xb7, + 0x09, 0x66, 0xcc, 0xff, 0x5e, 0x8b, 0x1e, 0x81, 0x2c, 0xbb, 0x67, 0x50, 0x07, 0x16, 0x71, 0x05, 0x1d, 0x84, 0x32, + 0xf8, 0x28, 0xc4, 0xcd, 0x9c, 0xde, 0xc9, 0x85, 0x06, 0xb8, 0x2c, 0xb4, 0x7c, 0xe3, 0xc2, 0x21, 0xec, 0xb7, 0xb0, + 0x8f, 0x8c, 0xb0, 0x84, 0x90, 0x01, 0x2d, 0x6c, 0x23, 0x62, 0xb4, 0xb0, 0x0b, 0x54, 0xd0, 0xc2, 0x26, 0x3c, 0x45, + 0x6b, 0x5d, 0xc6, 0x10, 0xbb, 0x2e, 0x9f, 0xae, 0xac, 0x36, 0xc1, 0xc2, 0x49, 0x87, 0x9a, 0xe8, 0xe0, 0xf6, 0x90, + 0x11, 0xde, 0xf8, 0xf9, 0xea, 0xf5, 0x2b, 0x17, 0x21, 0x9a, 0x8f, 0xc1, 0x65, 0xd3, 0xa9, 0xc6, 0xae, 0xcd, 0x9b, + 0x4f, 0x71, 0xa5, 0x28, 0xb5, 0xc2, 0x29, 0xb4, 0xfc, 0x42, 0xe8, 0x3c, 0xb1, 0x97, 0x17, 0xcf, 0x64, 0x31, 0xa3, + 0xf6, 0xc6, 0x08, 0x5f, 0x2b, 0xf7, 0xc8, 0xbb, 0x79, 0x47, 0xa6, 0x9a, 0xe4, 0xbb, 0xcd, 0xab, 0x88, 0x45, 0x66, + 0xe4, 0x57, 0xd0, 0x06, 0x98, 0xca, 0xe5, 0x1b, 0xbd, 0x05, 0x71, 0x71, 0xf6, 0x03, 0xf2, 0xf2, 0xd6, 0x52, 0x97, + 0x28, 0x6a, 0x70, 0x83, 0x9f, 0xac, 0xe0, 0x59, 0x70, 0x5d, 0x68, 0xd8, 0x23, 0x27, 0x5e, 0x44, 0xad, 0xa8, 0xfe, + 0xc6, 0xad, 0x51, 0x25, 0xf8, 0x18, 0xad, 0x49, 0x2e, 0x41, 0xf4, 0x28, 0x9f, 0xd3, 0xe3, 0x20, 0x9a, 0xf8, 0xbb, + 0xe7, 0xcb, 0xb6, 0xa7, 0xb3, 0x79, 0xa5, 0x4e, 0x2c, 0xaf, 0x4c, 0xc0, 0xc3, 0xd1, 0x3e, 0x58, 0x83, 0x70, 0x90, + 0xc8, 0x4a, 0xed, 0xa1, 0xcf, 0x45, 0xdd, 0x38, 0xbf, 0x68, 0xb3, 0xe6, 0xc9, 0x6a, 0x95, 0x5f, 0xb6, 0x59, 0xfb, + 0xd4, 0x3e, 0x6f, 0x17, 0xa9, 0x0c, 0x68, 0x2e, 0x1f, 0xf3, 0x2c, 0x02, 0xed, 0xec, 0x38, 0x33, 0xe1, 0x14, 0x7c, + 0x40, 0x66, 0xb2, 0xd0, 0x55, 0x5f, 0x12, 0x8c, 0x4b, 0x89, 0xd5, 0xe3, 0x17, 0xa8, 0xd7, 0x4e, 0xb7, 0x5d, 0xa5, + 0x9b, 0xed, 0xc3, 0xe0, 0xc2, 0xa5, 0x40, 0xb8, 0x03, 0x21, 0x0f, 0x40, 0xbf, 0xbb, 0x14, 0x60, 0x1a, 0x04, 0xa8, + 0xac, 0x40, 0xa4, 0xe5, 0xb3, 0xc5, 0xec, 0x59, 0x41, 0xcd, 0x32, 0x3c, 0xe1, 0x13, 0xae, 0x55, 0x4a, 0x41, 0xba, + 0xdd, 0x95, 0xbe, 0xde, 0x2d, 0x41, 0x65, 0xb5, 0x38, 0xb7, 0x89, 0xe6, 0xd9, 0x67, 0xe5, 0x16, 0x0e, 0x61, 0xb3, + 0xb2, 0x02, 0x67, 0x68, 0x8d, 0x73, 0x39, 0xa1, 0x05, 0xd7, 0xd3, 0xd9, 0xbf, 0xb5, 0x3a, 0xac, 0xaf, 0x07, 0xe6, + 0xc2, 0x0a, 0x40, 0x42, 0xc5, 0x68, 0xb5, 0xe2, 0x47, 0xdf, 0xbf, 0x4f, 0xf2, 0x3e, 0xe1, 0x6d, 0xdc, 0xc1, 0xc7, + 0xf8, 0x14, 0xb7, 0x5b, 0xb8, 0x7d, 0x0a, 0x57, 0xf7, 0x59, 0xbe, 0x18, 0x31, 0x15, 0xc3, 0x3b, 0x67, 0xfa, 0x32, + 0x39, 0x3f, 0x2c, 0xa3, 0xfb, 0xeb, 0x22, 0x71, 0xe8, 0x12, 0x04, 0x99, 0x77, 0xd1, 0xf9, 0xa2, 0x28, 0x0c, 0x0d, + 0x37, 0x0e, 0x55, 0x27, 0xa5, 0x7e, 0xe1, 0xf2, 0xb8, 0x07, 0xf6, 0xdc, 0x76, 0x65, 0x9b, 0x60, 0xf6, 0x6d, 0x7f, + 0xa6, 0xd5, 0xcf, 0xa6, 0x2e, 0x11, 0xc3, 0x43, 0xaf, 0x42, 0x0f, 0x74, 0x49, 0xda, 0x07, 0x07, 0x60, 0x75, 0x14, + 0xcc, 0x86, 0xdb, 0xe8, 0x07, 0xbc, 0x59, 0x4b, 0x83, 0x60, 0x05, 0x60, 0xdc, 0xf9, 0x86, 0x93, 0xa5, 0x85, 0xad, + 0x06, 0x2a, 0xac, 0x8b, 0x30, 0x7e, 0x5d, 0x48, 0x2a, 0x8c, 0x10, 0x0d, 0x47, 0x98, 0x0b, 0x86, 0xb2, 0xdf, 0xc2, + 0x72, 0x3c, 0x56, 0x4c, 0xc3, 0xd1, 0x51, 0xb0, 0x2f, 0xac, 0x50, 0xe6, 0x14, 0x19, 0xb2, 0x09, 0x17, 0x0f, 0xf5, + 0x9f, 0xac, 0x90, 0xe6, 0xd3, 0x68, 0x30, 0xd2, 0xc8, 0xac, 0x62, 0x84, 0xb3, 0x9c, 0xcf, 0xa1, 0xea, 0xa4, 0x00, + 0xa7, 0x1f, 0xf8, 0xcb, 0x47, 0x69, 0xd8, 0x26, 0x90, 0xaf, 0x0f, 0x36, 0xa6, 0x0b, 0x1e, 0x15, 0xf4, 0xe6, 0xb5, + 0x78, 0x0c, 0x3b, 0xea, 0x61, 0xc1, 0x28, 0x64, 0x43, 0xd2, 0x3b, 0x68, 0x0a, 0x3e, 0xa0, 0xcd, 0x97, 0x06, 0x70, + 0xe9, 0xb9, 0xf9, 0xb0, 0x15, 0x7d, 0x8c, 0xc4, 0xa4, 0x6c, 0xcb, 0x64, 0x9a, 0x53, 0xba, 0xca, 0xb4, 0x51, 0xa8, + 0xca, 0x29, 0xac, 0xb1, 0x8b, 0x7a, 0x12, 0x0e, 0x66, 0x44, 0xd5, 0x34, 0xed, 0x0f, 0xcc, 0xdf, 0xd7, 0xb6, 0x64, + 0x0b, 0xbb, 0x88, 0x33, 0x6b, 0x6c, 0x1e, 0x28, 0x0d, 0xca, 0xb7, 0x31, 0xdc, 0xc3, 0xc2, 0x2b, 0x99, 0x35, 0xf2, + 0x79, 0xe2, 0xc9, 0xe6, 0xc9, 0x7a, 0x6d, 0x06, 0xa2, 0x52, 0xd0, 0x03, 0xbd, 0xf5, 0xdb, 0xa6, 0x05, 0xdb, 0xa3, + 0xfc, 0x3a, 0x6d, 0xe1, 0x19, 0x87, 0x47, 0x3f, 0x7d, 0x7b, 0x57, 0xba, 0x90, 0x9f, 0x1d, 0x48, 0x5a, 0x41, 0x8a, + 0x9d, 0x4e, 0xd0, 0xd9, 0x31, 0x0e, 0x46, 0x0e, 0xf4, 0xfc, 0xea, 0xb3, 0x85, 0xb5, 0xff, 0xfd, 0xa6, 0x2c, 0x68, + 0xe2, 0xe9, 0x94, 0x13, 0xca, 0xfc, 0xf9, 0xf9, 0x86, 0x27, 0x15, 0x2a, 0xb8, 0xd7, 0xb2, 0x60, 0x4f, 0xdb, 0x80, + 0x9a, 0x33, 0xfa, 0xb7, 0xfd, 0x61, 0x63, 0xf8, 0x94, 0x5a, 0xb6, 0xac, 0x90, 0x4a, 0x3d, 0xb4, 0x69, 0xf6, 0xe8, + 0x81, 0x23, 0xf2, 0x25, 0x74, 0x01, 0xbc, 0xfe, 0xa8, 0x90, 0x73, 0x83, 0x08, 0xee, 0xb7, 0x1b, 0xb7, 0xf1, 0x15, + 0x00, 0x6f, 0x87, 0xbd, 0xea, 0x9f, 0x16, 0xb0, 0xbf, 0x51, 0x59, 0xd2, 0x8f, 0xb7, 0x63, 0x8f, 0xff, 0x42, 0x42, + 0x74, 0x76, 0x8b, 0x87, 0x89, 0x43, 0xa7, 0x92, 0x35, 0x2b, 0x7f, 0x6e, 0x95, 0x04, 0x0c, 0xab, 0x17, 0x0c, 0xd9, + 0xb8, 0xad, 0xe2, 0x36, 0xf3, 0x3f, 0xa8, 0x60, 0xb0, 0xe0, 0x5b, 0x23, 0xa9, 0x58, 0x16, 0xbf, 0x7d, 0xea, 0xfc, + 0x57, 0x9d, 0xe3, 0xda, 0xd7, 0xb5, 0x17, 0x39, 0x87, 0x26, 0x1a, 0x72, 0x84, 0x0e, 0x0e, 0x36, 0x32, 0xe8, 0x18, + 0x00, 0x8f, 0x1c, 0xfb, 0xe5, 0x97, 0xcf, 0xb3, 0x63, 0x46, 0xf3, 0x58, 0x44, 0x21, 0x73, 0xe7, 0xb9, 0x39, 0x3b, + 0x91, 0x27, 0x54, 0x4d, 0x7d, 0x61, 0x80, 0xe3, 0xa3, 0xad, 0x54, 0xc0, 0xf7, 0x68, 0xbd, 0x63, 0x02, 0x1b, 0xfc, + 0x96, 0x9d, 0xd4, 0xae, 0x82, 0x7e, 0x81, 0x96, 0xbb, 0x98, 0xca, 0x8d, 0x05, 0x8e, 0x36, 0x27, 0xb2, 0x73, 0xe8, + 0x1b, 0x75, 0x4a, 0xd6, 0xe3, 0xc9, 0x6e, 0xa3, 0x2f, 0x29, 0x76, 0x25, 0x57, 0xb4, 0x6d, 0xc8, 0xaa, 0x57, 0x79, + 0x75, 0x65, 0xea, 0x54, 0x5d, 0xf3, 0x56, 0x96, 0x36, 0xa5, 0x5d, 0x92, 0xbd, 0xdb, 0x62, 0xe1, 0x55, 0x78, 0xa3, + 0x51, 0x5e, 0x84, 0x82, 0x3d, 0x96, 0x18, 0x74, 0x39, 0x81, 0xeb, 0x85, 0xd5, 0x2a, 0x86, 0x3f, 0xbb, 0xc6, 0xb0, + 0xcb, 0x74, 0xe9, 0x03, 0xdf, 0xe0, 0x57, 0x82, 0xc0, 0xc0, 0xce, 0x0e, 0x12, 0xac, 0xbb, 0xdc, 0xa0, 0xe1, 0x38, + 0xf1, 0x5f, 0xf0, 0x2c, 0xb5, 0xf6, 0x2e, 0x07, 0x93, 0xec, 0x1b, 0x4f, 0xd9, 0x95, 0xac, 0x65, 0x2d, 0xaa, 0xfc, + 0x86, 0x04, 0x43, 0xec, 0xa6, 0x74, 0x8e, 0x5b, 0x49, 0x1b, 0x45, 0xae, 0x58, 0x85, 0xfe, 0xdf, 0x2a, 0x92, 0xd9, + 0xcc, 0xff, 0x3a, 0x3b, 0x3b, 0x73, 0x29, 0xce, 0xe6, 0x4f, 0x19, 0x0f, 0x38, 0x93, 0xc0, 0xbe, 0xf0, 0x8c, 0x19, + 0x1d, 0xf2, 0x5b, 0x18, 0x0a, 0x11, 0xe4, 0x52, 0x38, 0x76, 0x09, 0x5e, 0x55, 0x04, 0xca, 0x03, 0xec, 0xdf, 0x93, + 0x8d, 0x72, 0xfe, 0x59, 0x26, 0x1f, 0xb6, 0xb8, 0x6c, 0x90, 0x7d, 0x31, 0x9f, 0x7d, 0x6b, 0x26, 0x03, 0x2f, 0x11, + 0x44, 0xd8, 0xfe, 0x36, 0x2c, 0xad, 0xb3, 0x94, 0xc1, 0x91, 0x96, 0x8b, 0x6c, 0x6a, 0x35, 0xff, 0xee, 0xc3, 0x94, + 0x75, 0x4f, 0xfa, 0x40, 0xe4, 0x2e, 0xb2, 0x74, 0xd1, 0x37, 0xa3, 0x1f, 0xcb, 0x40, 0x9b, 0x7b, 0xaf, 0xd8, 0x82, + 0xfd, 0x88, 0xf7, 0xaa, 0x14, 0xf8, 0x78, 0x58, 0x70, 0x9a, 0xff, 0x88, 0xf7, 0xaa, 0x80, 0x9b, 0xe0, 0x0a, 0x69, + 0x62, 0x56, 0x62, 0xf3, 0x7c, 0x75, 0x1a, 0x09, 0xa0, 0xa0, 0x79, 0x64, 0x0e, 0xb2, 0xe7, 0x2e, 0x46, 0x63, 0xd2, + 0xc1, 0x2e, 0x38, 0x98, 0x8d, 0xbc, 0x6a, 0x03, 0x96, 0x43, 0xdc, 0xba, 0x72, 0x36, 0xe6, 0xeb, 0xd1, 0xc6, 0x82, + 0x18, 0x65, 0x32, 0xb9, 0x7c, 0xce, 0xe3, 0xad, 0xc5, 0x42, 0x61, 0xb5, 0x60, 0x81, 0x6a, 0x55, 0xaa, 0xf4, 0xb0, + 0xf8, 0x76, 0xc1, 0x2c, 0x28, 0x62, 0xb6, 0xde, 0xc3, 0x5b, 0xae, 0x08, 0x48, 0xc9, 0x2e, 0x09, 0x5e, 0x20, 0x37, + 0x98, 0xea, 0x1f, 0x9c, 0x07, 0x42, 0xcf, 0x94, 0x8e, 0xb0, 0xc9, 0x53, 0x10, 0x49, 0x6c, 0xbf, 0x85, 0x1d, 0x6b, + 0xf4, 0x42, 0x78, 0x21, 0x05, 0xce, 0x55, 0xd3, 0xc4, 0x8c, 0x72, 0x13, 0x5d, 0xec, 0xa1, 0x9a, 0xb3, 0x4c, 0x5b, + 0x04, 0xd8, 0x77, 0x68, 0x28, 0xc5, 0x73, 0x03, 0x0a, 0xf3, 0x74, 0xb6, 0x4b, 0x79, 0x0c, 0x8b, 0x17, 0xa4, 0x00, + 0x51, 0xe3, 0x62, 0x52, 0xd6, 0x99, 0xe7, 0x8b, 0x09, 0x17, 0x15, 0x32, 0x14, 0x4c, 0xcd, 0xa5, 0x80, 0x97, 0x2b, + 0xca, 0x22, 0x86, 0x0e, 0xd5, 0xf0, 0xdd, 0x92, 0xb0, 0xb2, 0x8e, 0x39, 0xa6, 0xb8, 0xa8, 0x6a, 0x00, 0x73, 0xf1, + 0x30, 0x7c, 0xe2, 0x5e, 0xbd, 0x16, 0xef, 0xe4, 0xbc, 0xca, 0xf7, 0x34, 0xce, 0x07, 0x88, 0x77, 0x76, 0xc3, 0x68, + 0x6d, 0x1e, 0x97, 0x0a, 0xb6, 0xef, 0x07, 0x5e, 0x3d, 0xb8, 0xb6, 0x36, 0xcf, 0x53, 0x95, 0x59, 0x43, 0x56, 0xbe, + 0xc5, 0x50, 0xb5, 0x57, 0xaf, 0x2a, 0x85, 0xad, 0x08, 0x50, 0x29, 0xf8, 0x68, 0x2b, 0xff, 0x89, 0xb6, 0xf9, 0xf6, + 0x1c, 0x2a, 0xc3, 0x03, 0x79, 0x32, 0x54, 0xf5, 0x80, 0x8b, 0xf2, 0x43, 0x00, 0x8b, 0x1f, 0x99, 0x38, 0xbd, 0xbb, + 0x2e, 0x90, 0x39, 0x53, 0xb1, 0xc4, 0xcb, 0x3e, 0x1d, 0xa4, 0x56, 0x1e, 0x4a, 0x25, 0xd8, 0xf6, 0xdc, 0x14, 0x5c, + 0xfb, 0x80, 0xc0, 0xb8, 0xcf, 0x06, 0xe9, 0xb2, 0x1e, 0x34, 0xd8, 0x86, 0x2d, 0xf6, 0xe6, 0x9c, 0x26, 0xca, 0x2e, + 0x1d, 0xe0, 0x9c, 0x80, 0xed, 0xb1, 0x67, 0x4f, 0xdf, 0xc4, 0x19, 0xea, 0xd5, 0x39, 0xfc, 0xe5, 0x1a, 0xe7, 0x38, + 0x43, 0xe9, 0xc3, 0x18, 0x2e, 0xb0, 0xd6, 0x18, 0xc0, 0x97, 0x59, 0x52, 0x05, 0x1e, 0xa9, 0x99, 0x91, 0x58, 0xdd, + 0x45, 0x20, 0x5a, 0xea, 0xf0, 0x76, 0x9c, 0xf9, 0xb0, 0xdb, 0x86, 0x7b, 0x7d, 0x66, 0x84, 0xc3, 0x49, 0x16, 0xd7, + 0xce, 0x19, 0x4e, 0x2e, 0xf7, 0x79, 0xed, 0xc4, 0x04, 0x6b, 0xef, 0xf0, 0x54, 0x01, 0x3d, 0x1a, 0x9c, 0x2a, 0x96, + 0x86, 0x40, 0xcc, 0x04, 0xf0, 0x66, 0x0e, 0x8f, 0xb6, 0x00, 0xe7, 0xa3, 0x35, 0x0e, 0xbe, 0xd2, 0x5a, 0x57, 0x9b, + 0x4a, 0x94, 0xf5, 0x1a, 0xf7, 0xa7, 0x19, 0x1e, 0x65, 0x78, 0x9e, 0x0d, 0x82, 0xe3, 0x66, 0x96, 0x85, 0x26, 0x5d, + 0xab, 0xd5, 0x53, 0x67, 0x46, 0x88, 0xec, 0x4f, 0x4b, 0x7f, 0x50, 0x0f, 0x10, 0x3e, 0x85, 0x2c, 0xa0, 0x25, 0x3d, + 0xf7, 0xb7, 0x61, 0x5f, 0xe5, 0x46, 0x8d, 0x98, 0x27, 0x96, 0x8c, 0xf4, 0xfc, 0x8f, 0x32, 0xcb, 0xb6, 0xd6, 0x88, + 0xe6, 0xb7, 0x7b, 0x51, 0xc3, 0xb7, 0x17, 0x68, 0xd9, 0x4a, 0xb3, 0x1d, 0x40, 0x14, 0x6b, 0x9c, 0xa4, 0x83, 0x35, + 0x92, 0xab, 0x55, 0x6c, 0x53, 0x08, 0x4f, 0x66, 0x8c, 0xaa, 0x45, 0x61, 0x1e, 0xaa, 0x8b, 0x15, 0x4a, 0x0c, 0xbf, + 0x8b, 0x9d, 0x8d, 0x28, 0xbc, 0x0b, 0x27, 0xc1, 0x70, 0x23, 0x16, 0x44, 0xd6, 0x44, 0xee, 0x61, 0x56, 0x59, 0x06, + 0x09, 0x22, 0x8c, 0xc8, 0x6f, 0xaf, 0x4b, 0x85, 0x7d, 0x0a, 0xcf, 0xfe, 0x31, 0xbe, 0x80, 0x70, 0xf3, 0x36, 0xa1, + 0xc5, 0x90, 0x4e, 0x80, 0x8d, 0x85, 0x38, 0x84, 0x5b, 0x09, 0xab, 0x55, 0x7f, 0xd0, 0x15, 0x86, 0x3c, 0xbb, 0x87, + 0xfa, 0xca, 0x86, 0x76, 0x37, 0x00, 0x57, 0xdd, 0x96, 0x9a, 0x6b, 0xa3, 0xfb, 0xa1, 0xe6, 0x2d, 0x31, 0xee, 0x92, + 0xdc, 0x73, 0x20, 0xd5, 0x8b, 0xdf, 0x35, 0x0b, 0x70, 0x13, 0xba, 0x0a, 0x8f, 0xf0, 0xc2, 0xda, 0x70, 0x9a, 0x87, + 0xa3, 0xa8, 0x79, 0x2f, 0x0a, 0x9e, 0xa9, 0x26, 0xac, 0x9f, 0x0d, 0xf0, 0xc8, 0x87, 0x15, 0xdf, 0x7f, 0x1b, 0x8f, + 0x10, 0x2a, 0x88, 0x81, 0xa9, 0x75, 0xd9, 0x1e, 0x55, 0x76, 0xfb, 0x26, 0xd3, 0x30, 0x0c, 0xc6, 0x88, 0x79, 0x14, + 0x1a, 0x31, 0xe7, 0x8d, 0x06, 0x5a, 0x90, 0x11, 0x18, 0x31, 0x2f, 0x82, 0xd6, 0x16, 0xf6, 0x51, 0xd1, 0xa0, 0xbd, + 0x05, 0x42, 0x5d, 0x0e, 0x34, 0x4d, 0xc3, 0xf3, 0x21, 0xd5, 0xf3, 0xed, 0xfe, 0x31, 0xab, 0xa3, 0x0e, 0x28, 0x12, + 0xc6, 0x97, 0x7e, 0x12, 0xd6, 0x35, 0xdc, 0x8e, 0x7b, 0x6c, 0xc6, 0xed, 0x6c, 0x1b, 0x54, 0x5f, 0xf6, 0xb3, 0xc1, + 0xa0, 0x2b, 0xbd, 0x95, 0x44, 0x0b, 0x8f, 0xab, 0x07, 0x47, 0xaa, 0xc5, 0xfb, 0xa2, 0x37, 0xaf, 0xbc, 0xb9, 0x7f, + 0xc7, 0x74, 0xf3, 0x3c, 0x06, 0x0e, 0x68, 0x1f, 0xee, 0x87, 0xaa, 0xf8, 0x60, 0x47, 0x1d, 0x88, 0x82, 0x96, 0xb6, + 0x6a, 0x02, 0xa9, 0x35, 0xb3, 0x8b, 0x75, 0x53, 0xa1, 0x43, 0x01, 0x61, 0xc8, 0x54, 0xd5, 0xdd, 0x9d, 0x0a, 0x54, + 0x43, 0x1c, 0x4e, 0xfd, 0xc7, 0xd6, 0x88, 0x35, 0x8e, 0x3a, 0xa3, 0xc8, 0x18, 0x49, 0xda, 0xe5, 0x83, 0x37, 0x86, + 0xc0, 0x4a, 0xc0, 0xc7, 0x7a, 0x36, 0x49, 0xc6, 0x90, 0xe0, 0x2d, 0xcb, 0xb4, 0xe1, 0x43, 0xb8, 0x43, 0x50, 0x9e, + 0xd8, 0xa0, 0xb4, 0xae, 0x92, 0x85, 0x5c, 0xd5, 0xe5, 0x75, 0x80, 0x9e, 0x77, 0xe5, 0x6f, 0x6c, 0x38, 0xb2, 0x60, + 0x60, 0xd9, 0xd6, 0x3e, 0x01, 0x8f, 0x7c, 0x5c, 0x21, 0x88, 0x5f, 0x0a, 0x9d, 0x98, 0xb8, 0xd8, 0x57, 0xb0, 0x41, + 0xf1, 0x1c, 0x1c, 0x04, 0x9d, 0x04, 0x87, 0xc1, 0xbb, 0xcc, 0x6a, 0x92, 0x0d, 0x6e, 0xcd, 0x48, 0x3c, 0x5f, 0xad, + 0x5a, 0xe8, 0xf0, 0x1f, 0xf3, 0xf4, 0xf3, 0xb8, 0x54, 0xb8, 0x8f, 0x2b, 0x85, 0x3b, 0x58, 0x02, 0x92, 0x71, 0xa0, + 0x6b, 0xc7, 0x32, 0x54, 0xa3, 0x43, 0x54, 0xf2, 0x17, 0x10, 0xa3, 0xda, 0x1d, 0x4b, 0xa0, 0x67, 0xdf, 0x2a, 0x60, + 0x75, 0xed, 0x65, 0x09, 0x64, 0x04, 0x77, 0xbf, 0x09, 0x8c, 0x0a, 0xd1, 0xf8, 0xfc, 0x99, 0xd7, 0x23, 0x78, 0xe2, + 0xfc, 0xb9, 0x66, 0x86, 0x75, 0x2f, 0xe8, 0x8d, 0x69, 0x3e, 0x1e, 0xe3, 0xe6, 0xd8, 0x82, 0xf3, 0xa8, 0x03, 0x3f, + 0x2d, 0x44, 0x8f, 0x3a, 0xd8, 0xa5, 0xe2, 0x71, 0x09, 0xe4, 0x10, 0x3d, 0x9d, 0x81, 0x14, 0xb0, 0xd2, 0xb1, 0xd5, + 0x22, 0x4d, 0xd0, 0x6a, 0x35, 0xb9, 0x20, 0x2d, 0x84, 0x96, 0xea, 0x86, 0xeb, 0x6c, 0x0a, 0x3e, 0xd2, 0xa0, 0x18, + 0x78, 0x43, 0xf5, 0x34, 0x46, 0x78, 0x8c, 0x96, 0x23, 0x36, 0xa6, 0x8b, 0x5c, 0xa7, 0xaa, 0xc7, 0x13, 0x1b, 0xb8, + 0x97, 0xd9, 0x48, 0x70, 0x47, 0x1d, 0x3c, 0x31, 0xfc, 0xe5, 0x23, 0x63, 0x0e, 0x52, 0x64, 0x26, 0x79, 0x62, 0x12, + 0x30, 0x4f, 0xb2, 0x5c, 0x2a, 0x66, 0x9b, 0xe9, 0x5a, 0xdb, 0x72, 0x08, 0xfd, 0x1d, 0xe9, 0x82, 0x1b, 0x2b, 0xca, + 0x28, 0x9d, 0x12, 0xd5, 0x53, 0x47, 0x9d, 0x74, 0x82, 0x79, 0x02, 0x9c, 0xde, 0x3b, 0x19, 0xb3, 0x46, 0x79, 0x2b, + 0x3a, 0x43, 0x87, 0x53, 0x2c, 0xaa, 0x4b, 0xd4, 0x19, 0x3a, 0x9c, 0x20, 0x3c, 0x6b, 0x90, 0x5c, 0x81, 0xc7, 0x30, + 0x17, 0xff, 0x47, 0xca, 0x7f, 0x73, 0xd8, 0x10, 0x62, 0xfa, 0x2d, 0xec, 0x14, 0x36, 0x8a, 0xd2, 0x9c, 0x80, 0xd7, + 0x62, 0xfb, 0x0c, 0x67, 0x64, 0xd2, 0xcc, 0x7d, 0xc0, 0x3d, 0xd3, 0x4a, 0xe3, 0x56, 0xa3, 0xc3, 0x0c, 0x8f, 0x36, + 0x93, 0x62, 0x33, 0xd7, 0x66, 0x9e, 0x66, 0x70, 0xbe, 0x57, 0xa3, 0x70, 0xe5, 0x17, 0x9b, 0x49, 0x61, 0x79, 0x07, + 0xdc, 0xe6, 0x08, 0x8b, 0x26, 0xc5, 0x39, 0x9e, 0x35, 0xbf, 0xe2, 0x59, 0xf3, 0x43, 0x99, 0xd1, 0x58, 0x60, 0x01, + 0xc1, 0xfb, 0x20, 0x11, 0xcf, 0xaa, 0xe4, 0x11, 0x16, 0x0d, 0x53, 0x1e, 0xcf, 0x1a, 0x55, 0xe9, 0xe6, 0x02, 0x8b, + 0x86, 0x29, 0xdd, 0xf8, 0x80, 0x67, 0x8d, 0xaf, 0xff, 0x62, 0xd2, 0x51, 0x0a, 0xe8, 0x32, 0x47, 0xcb, 0xcc, 0x0e, + 0xf1, 0xea, 0xb7, 0xb7, 0xef, 0xda, 0xd7, 0x9d, 0xc3, 0x09, 0xf6, 0xeb, 0x97, 0x19, 0x1c, 0xcb, 0x74, 0xcc, 0x9a, + 0x00, 0xd1, 0x0c, 0x77, 0x0e, 0xa7, 0xb8, 0x73, 0x98, 0xb9, 0xa6, 0xd6, 0xb3, 0x06, 0xb9, 0xd5, 0x21, 0x14, 0x75, + 0x94, 0x86, 0xf0, 0xf1, 0x93, 0x4d, 0x27, 0xa8, 0x06, 0x4a, 0x74, 0x38, 0xa9, 0x81, 0x0a, 0xbe, 0x17, 0xb5, 0xef, + 0xaa, 0x5e, 0x85, 0x41, 0x16, 0x4a, 0x28, 0x5c, 0x73, 0x03, 0x9e, 0x5a, 0x8a, 0x81, 0x4c, 0x98, 0x62, 0x81, 0xf2, + 0x1d, 0x50, 0x18, 0xe5, 0x89, 0x19, 0x7a, 0x30, 0x1d, 0x93, 0xf8, 0xff, 0xf3, 0x64, 0xca, 0xa1, 0x97, 0x5b, 0x66, + 0x6b, 0x7a, 0x6e, 0x32, 0xe1, 0xf0, 0x81, 0xc7, 0xfa, 0xbf, 0x76, 0xa0, 0xd8, 0x80, 0x14, 0xff, 0x5f, 0x3a, 0xba, + 0x10, 0x8c, 0x90, 0x15, 0xa5, 0x85, 0x43, 0xfc, 0xef, 0x0f, 0x2b, 0xe8, 0xbe, 0xd8, 0xea, 0xbe, 0x30, 0xdd, 0x87, + 0x4d, 0x1b, 0x55, 0x4e, 0x5a, 0x55, 0xb2, 0xe4, 0xbf, 0x4e, 0xb7, 0xb6, 0x40, 0x23, 0x6a, 0xf4, 0x6c, 0x12, 0x36, + 0xb8, 0xdf, 0x4e, 0x77, 0x20, 0xf3, 0x9a, 0xdb, 0x97, 0x48, 0xe1, 0xf0, 0x0d, 0xee, 0x54, 0x2f, 0x5b, 0xe0, 0xbd, + 0xa9, 0x8c, 0xbe, 0x32, 0x0e, 0x2d, 0x07, 0xe9, 0xa6, 0x29, 0xb7, 0x31, 0x96, 0x4e, 0x4e, 0xb1, 0x71, 0x45, 0x84, + 0x4a, 0xb7, 0x97, 0xa0, 0x14, 0x1f, 0xeb, 0x26, 0x33, 0x5f, 0x17, 0x3a, 0x31, 0x97, 0x50, 0x0d, 0xf3, 0x79, 0x77, + 0xa9, 0x13, 0x2d, 0xe7, 0x36, 0xef, 0xee, 0x02, 0xfa, 0x04, 0x0d, 0x6b, 0x23, 0xb0, 0xdb, 0x67, 0x85, 0xd3, 0xef, + 0x54, 0x87, 0x60, 0x78, 0x00, 0x39, 0xd2, 0x62, 0xfb, 0xc0, 0xa6, 0x35, 0xec, 0xba, 0x68, 0x96, 0x89, 0xb6, 0xd5, + 0xa6, 0xc9, 0xb5, 0x7b, 0x98, 0xcf, 0x43, 0x9e, 0x82, 0x17, 0x56, 0x3f, 0xbe, 0x83, 0xdd, 0xb8, 0xad, 0x31, 0x12, + 0x75, 0x25, 0x53, 0x09, 0xfd, 0xe4, 0x16, 0xb3, 0xe4, 0xce, 0x78, 0x31, 0x2a, 0xe3, 0xef, 0x63, 0xe2, 0xf2, 0x47, + 0x95, 0x24, 0x07, 0x96, 0xfd, 0x0d, 0x96, 0xdc, 0x82, 0x79, 0x62, 0x59, 0x4d, 0x62, 0x9d, 0xdc, 0x05, 0x8b, 0x28, + 0x4d, 0x23, 0x6b, 0xc3, 0x80, 0x9a, 0x66, 0xac, 0x7a, 0x70, 0x1f, 0x02, 0x3d, 0xf4, 0xca, 0x52, 0xda, 0x75, 0x96, + 0xd6, 0xba, 0xd7, 0xa6, 0xfb, 0xcd, 0x01, 0x05, 0x7c, 0x61, 0xc0, 0x35, 0xfd, 0xab, 0x49, 0x24, 0x43, 0xf6, 0x95, + 0xb3, 0xe2, 0xf1, 0xa2, 0x30, 0x98, 0x26, 0x7a, 0x3a, 0xc9, 0xe6, 0x6d, 0x30, 0xd5, 0xcb, 0xe6, 0x9d, 0x5b, 0xec, + 0xbe, 0xef, 0xec, 0xf7, 0x1d, 0x16, 0x3d, 0x66, 0x32, 0x52, 0x66, 0x8a, 0xf9, 0xef, 0x3b, 0xfb, 0x7d, 0x87, 0xb7, + 0x07, 0x73, 0xe3, 0x2f, 0x14, 0x4b, 0x76, 0x86, 0x4b, 0x30, 0x21, 0x0f, 0xb8, 0x9b, 0x5a, 0x96, 0x09, 0x02, 0x5b, + 0x4b, 0x80, 0x38, 0x9f, 0x4f, 0xe3, 0x8a, 0x57, 0x43, 0xc0, 0x7d, 0x7a, 0xd7, 0xf6, 0x2a, 0x15, 0x78, 0x4c, 0xd0, + 0x88, 0x98, 0xd8, 0x36, 0xe6, 0x15, 0x31, 0xe0, 0xf2, 0x88, 0x2e, 0xf5, 0x24, 0x09, 0xf0, 0xaa, 0x46, 0xe5, 0x6d, + 0x8a, 0x94, 0x5f, 0x24, 0xc8, 0xf1, 0xc5, 0x1e, 0x51, 0xc5, 0x00, 0x56, 0x65, 0x49, 0x9f, 0x40, 0xea, 0xf9, 0xc1, + 0x44, 0x3f, 0x6f, 0x22, 0x8f, 0x7d, 0x4f, 0xf7, 0x33, 0xd3, 0xd3, 0x42, 0x2e, 0x26, 0x53, 0xf0, 0xa1, 0x05, 0x96, + 0xa1, 0x30, 0xf5, 0x2a, 0x5b, 0xff, 0x9a, 0xe4, 0x26, 0x80, 0xc2, 0xe9, 0xa6, 0x4c, 0x68, 0xa6, 0x17, 0x34, 0x37, + 0x96, 0xa4, 0x5c, 0x4c, 0x1e, 0xc9, 0xdb, 0x97, 0x80, 0xdd, 0x94, 0xe8, 0xc6, 0x8e, 0xbc, 0xb7, 0xb0, 0x03, 0x70, + 0x46, 0xd8, 0xae, 0x8a, 0x0f, 0x15, 0xe8, 0xfc, 0x71, 0x4e, 0xd8, 0xae, 0xaa, 0x4f, 0x98, 0xcd, 0x9e, 0x92, 0x8d, + 0xe1, 0xf6, 0xe2, 0xac, 0x91, 0xa3, 0xa3, 0x4e, 0x9a, 0x77, 0x3d, 0x31, 0xb0, 0x00, 0x0d, 0x80, 0xbb, 0xb5, 0x3d, + 0xcb, 0xbb, 0x1b, 0x02, 0x7a, 0x97, 0x4c, 0xda, 0xeb, 0x72, 0x93, 0xb2, 0x5a, 0x75, 0x2a, 0x2a, 0x58, 0xe0, 0x69, + 0xb0, 0x17, 0xa8, 0xfd, 0xda, 0x41, 0x71, 0xae, 0xb2, 0x4d, 0xd3, 0xf3, 0xb2, 0xef, 0xee, 0x8e, 0x45, 0xc6, 0x36, + 0xed, 0xed, 0x0e, 0x22, 0x61, 0x39, 0x61, 0x1d, 0x70, 0xc2, 0x55, 0xed, 0x80, 0x00, 0x5d, 0x07, 0x22, 0x37, 0x96, + 0x64, 0xb9, 0xae, 0x8c, 0xee, 0x03, 0xbf, 0x5b, 0x4a, 0xa4, 0x1b, 0x6d, 0x49, 0x30, 0x7d, 0x82, 0x51, 0xd3, 0x99, + 0x27, 0xa0, 0x6b, 0xaf, 0x1b, 0x6f, 0x8a, 0xb6, 0xfe, 0xad, 0x65, 0x6c, 0xb6, 0x87, 0x89, 0xa1, 0x0c, 0x62, 0xa0, + 0xf7, 0x11, 0xef, 0x36, 0x1a, 0x19, 0x02, 0x85, 0x4c, 0x36, 0xc0, 0x32, 0xf1, 0x5a, 0xf4, 0x83, 0x03, 0x03, 0x8f, + 0x2a, 0x01, 0x61, 0x0a, 0x42, 0x48, 0xd8, 0xb5, 0x41, 0xd8, 0x70, 0xb9, 0x6a, 0xb9, 0xb0, 0x91, 0x6a, 0x43, 0x07, + 0xff, 0xaf, 0x70, 0xd9, 0xea, 0x99, 0xe5, 0xa2, 0x18, 0xdc, 0xcc, 0x0d, 0x58, 0x24, 0x48, 0x8f, 0x36, 0xdb, 0x43, + 0x71, 0x77, 0x2e, 0x36, 0x1b, 0x02, 0x12, 0x73, 0x98, 0xa0, 0x68, 0x38, 0x37, 0xc6, 0x58, 0x25, 0x95, 0x96, 0xb5, + 0x26, 0x31, 0x07, 0xbe, 0x74, 0xe1, 0xba, 0x2f, 0x6f, 0x53, 0x86, 0xef, 0x52, 0x81, 0x6f, 0xc0, 0x93, 0x26, 0x95, + 0xd8, 0x3d, 0x5e, 0x50, 0xac, 0x89, 0xee, 0x7a, 0xf6, 0xb6, 0x80, 0x75, 0x36, 0x7b, 0x44, 0x04, 0xbf, 0xab, 0x5f, + 0x6d, 0xf0, 0xdd, 0xc2, 0x5f, 0xc1, 0xfa, 0x39, 0x38, 0x49, 0xb1, 0x68, 0xc8, 0x66, 0xe1, 0x8e, 0x0c, 0x28, 0x57, + 0xf1, 0xcb, 0x61, 0xea, 0x56, 0x31, 0x5c, 0xfb, 0xf8, 0x8a, 0x3f, 0x6c, 0xb4, 0xdb, 0x50, 0x65, 0x71, 0xbb, 0x37, + 0x45, 0x43, 0x56, 0x4d, 0xef, 0xc8, 0xdc, 0x48, 0xa9, 0x7f, 0x7d, 0xc0, 0xad, 0xad, 0xf6, 0xfd, 0x34, 0xdf, 0x7a, + 0x74, 0xae, 0x9a, 0xf6, 0xa9, 0xb5, 0x22, 0x38, 0xf8, 0xd9, 0xc2, 0xcd, 0xad, 0x01, 0x07, 0xf0, 0xf3, 0x77, 0x34, + 0x8f, 0x33, 0x88, 0x4e, 0x6f, 0x35, 0xe3, 0xab, 0xf8, 0xaf, 0x51, 0x23, 0xee, 0xa5, 0x7f, 0x25, 0x7f, 0x8d, 0x1a, + 0xa8, 0x87, 0xe2, 0xf9, 0xed, 0x8a, 0xcd, 0x56, 0x10, 0x6c, 0xed, 0xde, 0x11, 0x7e, 0x1d, 0x96, 0xe4, 0x9a, 0xe6, + 0x3c, 0x5b, 0xb9, 0x87, 0xf7, 0x56, 0xee, 0x55, 0xa2, 0x95, 0x79, 0x4b, 0x6a, 0x15, 0xcb, 0x61, 0x0e, 0x81, 0x85, + 0xe3, 0xbd, 0x66, 0xaf, 0xdf, 0x6a, 0x3e, 0x18, 0xd8, 0x7f, 0x4d, 0x84, 0x7b, 0x54, 0x8b, 0xd8, 0xf6, 0x66, 0x63, + 0xeb, 0xc7, 0x60, 0xd8, 0x01, 0xa1, 0xc0, 0x41, 0x2e, 0x7d, 0x9c, 0x21, 0xeb, 0x7b, 0xb2, 0x5a, 0x31, 0x17, 0xcd, + 0xda, 0x69, 0xf0, 0xcb, 0xd8, 0x4c, 0x87, 0xed, 0xa4, 0xd3, 0xf5, 0x62, 0x2c, 0x69, 0x40, 0xa4, 0x69, 0xcc, 0x20, + 0x90, 0xd4, 0xd2, 0x70, 0x58, 0xf3, 0xdb, 0x28, 0xad, 0xee, 0x8f, 0x20, 0xe5, 0x87, 0x28, 0xe5, 0x47, 0x04, 0x02, + 0x68, 0x5b, 0xe6, 0xa8, 0x6c, 0xc8, 0xfb, 0x2e, 0xdd, 0x33, 0xce, 0x0c, 0x0d, 0xbe, 0x5a, 0xb5, 0xaa, 0x61, 0x8a, + 0xa2, 0x3e, 0xcc, 0xe5, 0x1a, 0x0b, 0xf2, 0x06, 0x74, 0xcd, 0x8a, 0x88, 0x5e, 0xe8, 0x2a, 0x0f, 0xef, 0x0e, 0x63, + 0x49, 0xc0, 0x49, 0xbf, 0x27, 0x7a, 0x05, 0xb9, 0x7c, 0x18, 0x83, 0x8f, 0x19, 0xe6, 0x7d, 0xdd, 0x2f, 0x06, 0x03, + 0x94, 0x3a, 0xa7, 0xb3, 0xd4, 0x44, 0x5c, 0x09, 0xfc, 0x92, 0x0b, 0xf0, 0x4b, 0x56, 0x88, 0xf5, 0x8b, 0x01, 0xb9, + 0x97, 0xc5, 0x12, 0x9c, 0xf2, 0x77, 0xf8, 0x3c, 0x3e, 0x0c, 0x0d, 0x4c, 0xcd, 0xb0, 0xcc, 0x45, 0x36, 0x58, 0xcc, + 0x59, 0x4b, 0x20, 0xb8, 0x19, 0x70, 0x97, 0xda, 0x90, 0x68, 0xac, 0x81, 0xa2, 0xdb, 0x28, 0x34, 0x33, 0x7a, 0xba, + 0xd5, 0x46, 0x3f, 0x72, 0x78, 0x61, 0xae, 0x61, 0x2c, 0x02, 0x99, 0xcb, 0x55, 0x8f, 0xfd, 0xe5, 0x87, 0xcd, 0x0a, + 0x83, 0x57, 0x64, 0x3a, 0x74, 0xc7, 0x31, 0xe3, 0xab, 0x3c, 0x71, 0x0c, 0x41, 0x26, 0x96, 0x4a, 0x37, 0x1c, 0x13, + 0x57, 0xd2, 0x67, 0x62, 0xc8, 0x76, 0xc3, 0x33, 0x73, 0xa1, 0x9b, 0xed, 0x1f, 0xce, 0xed, 0x9c, 0x13, 0x6e, 0xb4, + 0x92, 0x46, 0x1b, 0xf5, 0xcc, 0x50, 0x55, 0x17, 0xcc, 0xef, 0xa1, 0xd3, 0xd2, 0x62, 0xe7, 0xea, 0xdd, 0x0d, 0x5f, + 0xc1, 0x2b, 0xe3, 0x6f, 0xb1, 0x2a, 0xb4, 0x22, 0xc3, 0xed, 0x16, 0xf2, 0xe6, 0x4c, 0x0f, 0xbd, 0x22, 0x17, 0xaa, + 0xc3, 0x5f, 0xd4, 0x15, 0xe6, 0x61, 0xcc, 0xa8, 0x21, 0x3c, 0xfa, 0xbd, 0xce, 0x40, 0xf9, 0x07, 0x13, 0x93, 0x39, + 0x4b, 0x6e, 0x68, 0x21, 0xe2, 0x1f, 0x5f, 0x08, 0x13, 0xab, 0x6a, 0x0f, 0x06, 0xb2, 0x67, 0x2a, 0xee, 0xc1, 0xad, + 0x09, 0x1f, 0x73, 0x36, 0x4a, 0xf7, 0xa2, 0x1f, 0x1b, 0xa2, 0xf1, 0x63, 0xf4, 0x23, 0xb8, 0x3b, 0xbb, 0x57, 0x18, + 0xcb, 0xb8, 0x10, 0xfe, 0x1e, 0xeb, 0x61, 0xa9, 0x52, 0xc6, 0xda, 0xeb, 0x96, 0xc3, 0x0b, 0xa9, 0x37, 0x59, 0xfc, + 0xd0, 0x11, 0x6b, 0x9b, 0x82, 0x75, 0x48, 0x49, 0xe1, 0xd9, 0x15, 0x73, 0xab, 0xc5, 0xdc, 0xa5, 0x96, 0xf0, 0xd7, + 0x57, 0x0f, 0x4b, 0x15, 0x34, 0x1c, 0x84, 0xae, 0xb4, 0x85, 0x04, 0x18, 0xb8, 0x94, 0x3e, 0x9d, 0xee, 0x4c, 0x22, + 0xb3, 0x2c, 0x86, 0x77, 0x0f, 0x2a, 0x98, 0xff, 0xce, 0x36, 0xc2, 0xaa, 0xc0, 0xe5, 0x4a, 0x15, 0xf5, 0x52, 0x12, + 0x08, 0x40, 0x5f, 0x7a, 0x0f, 0xca, 0x8b, 0xa2, 0xdb, 0x68, 0x48, 0xd0, 0xc2, 0x52, 0x73, 0xad, 0x8a, 0xe9, 0x7e, + 0xf8, 0x7a, 0x60, 0xf0, 0xe1, 0x1d, 0xd2, 0x36, 0x9e, 0xf0, 0xa4, 0x84, 0xda, 0x1d, 0xb4, 0x0f, 0x56, 0xd9, 0x41, + 0xf9, 0xb7, 0x31, 0x45, 0x36, 0xbf, 0xcf, 0x7e, 0xa0, 0xae, 0xc3, 0x81, 0x2b, 0x58, 0xf5, 0x52, 0x46, 0xc1, 0x80, + 0x95, 0x53, 0xa0, 0xf6, 0x4e, 0x32, 0x9a, 0x4d, 0x19, 0xa8, 0xfb, 0x6d, 0xd1, 0x6a, 0x6e, 0x4f, 0xea, 0x7e, 0x43, + 0xc6, 0xd9, 0x47, 0x18, 0x67, 0x1f, 0x05, 0x5e, 0x2c, 0x92, 0xfc, 0x21, 0x63, 0x8d, 0x63, 0xd5, 0x14, 0xe8, 0xa8, + 0x03, 0xdc, 0x19, 0x38, 0xf0, 0x80, 0x2d, 0xca, 0xc1, 0x01, 0x75, 0x16, 0xf7, 0xb4, 0x91, 0x79, 0x6f, 0x4f, 0xa8, + 0x5d, 0xc4, 0x02, 0x37, 0x6b, 0x66, 0x5a, 0xd0, 0x5a, 0x61, 0x9c, 0xc7, 0x03, 0xde, 0xe6, 0x59, 0x2d, 0x7e, 0xc2, + 0x86, 0x35, 0x55, 0xfd, 0x06, 0x9a, 0xa3, 0x5a, 0x90, 0x9b, 0x27, 0xc6, 0x5b, 0x95, 0xf4, 0xa3, 0x68, 0x60, 0x39, + 0x15, 0x62, 0x48, 0x46, 0xbf, 0x35, 0x08, 0x6e, 0xb5, 0x57, 0x2b, 0xee, 0x11, 0x5f, 0xd4, 0xbc, 0xd5, 0xcc, 0x2d, + 0x00, 0x2d, 0xe2, 0xa8, 0xbc, 0x37, 0x89, 0xc0, 0xfb, 0xb6, 0x8c, 0x90, 0xb6, 0xec, 0xdb, 0x27, 0x22, 0x4b, 0xc5, + 0xe6, 0x3b, 0x3a, 0x19, 0xa4, 0x91, 0x1d, 0x51, 0x84, 0xaf, 0x4b, 0x48, 0xc2, 0x55, 0xd2, 0xb5, 0xca, 0xe4, 0x9c, + 0xa9, 0x94, 0xe3, 0xeb, 0x42, 0x4a, 0x7d, 0x65, 0xbf, 0x24, 0xae, 0xee, 0x64, 0x04, 0xbe, 0x9e, 0x30, 0xfd, 0x8e, + 0x16, 0x13, 0x06, 0x7e, 0x45, 0xfe, 0x76, 0x2c, 0xa5, 0xe4, 0xf2, 0x89, 0x88, 0xfb, 0x14, 0xc3, 0xfb, 0xa6, 0x03, + 0xac, 0x4d, 0x08, 0x94, 0x12, 0x17, 0xe1, 0x82, 0xe8, 0x4d, 0x21, 0x6f, 0xef, 0xe2, 0x02, 0x3b, 0x07, 0xc0, 0xd2, + 0x69, 0x12, 0xe0, 0x5f, 0x3e, 0xe6, 0x63, 0x35, 0xe6, 0xd4, 0xe8, 0xfa, 0xdd, 0xef, 0xe4, 0x1a, 0xe8, 0x6d, 0xe9, + 0x28, 0xd8, 0x6f, 0x0d, 0x20, 0x17, 0xee, 0xc2, 0xe0, 0xe2, 0x2b, 0xac, 0x2d, 0x0b, 0xe3, 0x8d, 0x05, 0xd0, 0xfb, + 0x3b, 0x03, 0x0b, 0x36, 0xcc, 0x31, 0x85, 0xc7, 0x61, 0x27, 0x4c, 0x07, 0x51, 0x41, 0x9e, 0x94, 0xcf, 0x7f, 0xd6, + 0x6a, 0xbf, 0x65, 0x63, 0xb8, 0xc3, 0x48, 0xbe, 0x5d, 0x38, 0x71, 0xe0, 0x01, 0x99, 0x26, 0xb3, 0xcd, 0xbe, 0xf1, + 0x91, 0x47, 0x5e, 0x8f, 0xe3, 0x5d, 0x2d, 0x85, 0xf9, 0x66, 0x45, 0xd7, 0x18, 0x42, 0x51, 0x84, 0xfd, 0x7e, 0x51, + 0x31, 0x45, 0x95, 0x41, 0x1b, 0x34, 0x2c, 0x6f, 0xc4, 0x2f, 0x70, 0xc6, 0xd0, 0x7a, 0x21, 0x7b, 0x47, 0x67, 0x1d, + 0xce, 0x1c, 0x66, 0x4c, 0x09, 0x8c, 0x4a, 0xcb, 0x82, 0x4e, 0xc0, 0xd1, 0xb9, 0xfa, 0x20, 0x2a, 0xae, 0x8e, 0x15, + 0x80, 0x27, 0x99, 0xc2, 0x3f, 0xf9, 0x26, 0x58, 0xf7, 0x5b, 0x35, 0xc3, 0xd4, 0x5f, 0xf4, 0xb6, 0x6b, 0xf9, 0x32, + 0xc4, 0x91, 0x36, 0x86, 0xd0, 0x3a, 0xb7, 0x77, 0x80, 0x22, 0x2e, 0xe8, 0x45, 0xaa, 0xf1, 0xb5, 0x5a, 0x0c, 0xcd, + 0xfa, 0x1a, 0xd7, 0x31, 0x6d, 0x10, 0xc5, 0xba, 0x6b, 0xe2, 0xeb, 0xea, 0xb5, 0x55, 0x95, 0x2a, 0x38, 0x83, 0x04, + 0xc2, 0xaa, 0xbc, 0x6c, 0x48, 0x25, 0xb9, 0x34, 0x9d, 0x4a, 0xd3, 0x69, 0x85, 0x50, 0x2e, 0x3d, 0x29, 0xef, 0x5f, + 0x21, 0x84, 0x81, 0x29, 0xb3, 0x03, 0xab, 0xd4, 0x16, 0x56, 0xc1, 0xab, 0x17, 0x1b, 0x58, 0x25, 0xe1, 0x78, 0x2e, + 0xd1, 0xa8, 0xa8, 0x70, 0xc8, 0x90, 0xbe, 0x10, 0x8b, 0x20, 0x01, 0xb0, 0xe8, 0x5d, 0xe6, 0xf2, 0xbe, 0x87, 0x43, + 0x61, 0x4f, 0x32, 0x09, 0xa7, 0x9b, 0xd0, 0x1c, 0x9e, 0xe1, 0x55, 0x3d, 0x8f, 0x10, 0xb0, 0xf4, 0x1c, 0xc3, 0xf3, + 0xcb, 0xdf, 0x7f, 0xf6, 0xd5, 0x59, 0x90, 0xa7, 0xff, 0x12, 0x25, 0xa1, 0xb1, 0xff, 0x1c, 0x0f, 0x1d, 0x12, 0x86, + 0x03, 0xdf, 0x1c, 0x61, 0x85, 0x83, 0x5b, 0x45, 0x7c, 0x06, 0x77, 0xf8, 0x58, 0x87, 0x1e, 0x00, 0x96, 0x50, 0x1c, + 0x82, 0x7c, 0x03, 0xc5, 0x0c, 0x0e, 0x68, 0xb2, 0x0c, 0x2f, 0x70, 0xc1, 0x6a, 0xa1, 0xbc, 0xbf, 0x6d, 0x79, 0x29, + 0xad, 0x76, 0xc9, 0x6b, 0xcc, 0x81, 0xca, 0xcf, 0xf0, 0xc2, 0x57, 0x98, 0x77, 0xa1, 0xdd, 0x17, 0xbe, 0x76, 0x40, + 0x4f, 0x21, 0x60, 0xa4, 0xfb, 0xbd, 0x26, 0xdc, 0x53, 0xf4, 0x32, 0x17, 0x87, 0x6d, 0x07, 0xdd, 0x0b, 0xcc, 0xd5, + 0x55, 0x95, 0x35, 0x07, 0x53, 0x68, 0x70, 0x50, 0x85, 0x33, 0x02, 0x73, 0xf5, 0xa2, 0x2c, 0x38, 0x07, 0xf1, 0xbe, + 0x27, 0x4c, 0x4e, 0x19, 0x0d, 0xe0, 0x45, 0x56, 0x3e, 0x3a, 0xd5, 0xe3, 0xe0, 0x32, 0x6e, 0xd8, 0xc4, 0x17, 0xc2, + 0xa7, 0x02, 0x2b, 0x69, 0x8d, 0x43, 0x23, 0x3a, 0xa2, 0x73, 0x30, 0xdb, 0x00, 0x0a, 0xee, 0xce, 0x87, 0x8d, 0x85, + 0x0a, 0x9e, 0xbe, 0xad, 0xbd, 0x54, 0x4d, 0x88, 0x33, 0x69, 0x0a, 0xee, 0xb6, 0x0d, 0x32, 0x78, 0xf3, 0xdb, 0x7f, + 0x2b, 0x2c, 0x12, 0x0c, 0xa8, 0xd4, 0x24, 0x41, 0x78, 0x82, 0xd2, 0x48, 0xb7, 0x72, 0x33, 0x81, 0x74, 0x22, 0x6a, + 0x46, 0xdd, 0x1b, 0xe7, 0xab, 0xa3, 0x06, 0xa2, 0xa2, 0x06, 0x2a, 0xa0, 0x06, 0xb2, 0xbe, 0xfd, 0x0b, 0x58, 0x08, + 0x1b, 0xa1, 0x4a, 0x04, 0x01, 0x11, 0xe6, 0xda, 0xf0, 0x01, 0x45, 0x12, 0x42, 0xde, 0x00, 0x2a, 0xa6, 0xe4, 0x25, + 0x18, 0x8d, 0xc3, 0xeb, 0x3d, 0xe0, 0x7e, 0x69, 0x19, 0x06, 0xcf, 0x29, 0x98, 0xfc, 0xb7, 0x3e, 0x1f, 0xaa, 0x97, + 0xab, 0x83, 0x10, 0x7e, 0x01, 0xb1, 0x22, 0x1c, 0x7f, 0xf1, 0x0b, 0x90, 0x4d, 0x85, 0xe5, 0xc1, 0x81, 0x04, 0x81, + 0x1f, 0xa2, 0x08, 0x07, 0x3c, 0xc3, 0xcb, 0x6c, 0x83, 0xe8, 0xf9, 0x59, 0xa9, 0x6a, 0x56, 0x32, 0x98, 0x55, 0xe1, + 0x69, 0x1c, 0x5d, 0x13, 0x06, 0x82, 0x0b, 0xb5, 0xfb, 0x06, 0x21, 0x50, 0xb6, 0xdc, 0x18, 0xba, 0xf4, 0x14, 0xcc, + 0x47, 0xe3, 0xe8, 0x2d, 0x83, 0x07, 0x7c, 0x8d, 0xc9, 0x3f, 0xd3, 0x2c, 0xd3, 0x86, 0x79, 0x6c, 0x04, 0x4e, 0xea, + 0x14, 0x25, 0x7f, 0x4b, 0x2e, 0xe2, 0xa8, 0x79, 0x19, 0xa1, 0x06, 0xfc, 0xdb, 0xe0, 0xa8, 0x4b, 0x13, 0x3a, 0x1a, + 0xf9, 0xe0, 0x37, 0x19, 0x31, 0x9b, 0x6c, 0xb5, 0x12, 0x15, 0x41, 0x4f, 0xec, 0x06, 0x03, 0x56, 0xe2, 0x05, 0xb0, + 0x0f, 0x96, 0x83, 0x25, 0xef, 0x44, 0xac, 0xfc, 0x29, 0x85, 0xc1, 0xea, 0x39, 0x43, 0x08, 0x67, 0x41, 0xcc, 0xc6, + 0xff, 0x7c, 0xa6, 0xe1, 0xfa, 0xf9, 0xf9, 0x3a, 0x46, 0x44, 0xfa, 0x20, 0x72, 0x35, 0x76, 0x44, 0x04, 0x61, 0xcb, + 0x74, 0xdf, 0x95, 0xf9, 0xc1, 0x5b, 0x57, 0x0f, 0x6c, 0xb8, 0x38, 0x30, 0xa0, 0x46, 0x81, 0xd1, 0x0a, 0xce, 0x49, + 0x39, 0x70, 0x50, 0x42, 0x68, 0x56, 0xc4, 0x53, 0x72, 0x09, 0x91, 0xf0, 0x32, 0xd4, 0x05, 0xc3, 0x82, 0x40, 0x82, + 0x9a, 0x82, 0x04, 0x95, 0xf9, 0xda, 0x23, 0x98, 0x75, 0x6e, 0x66, 0x3b, 0x45, 0x5d, 0x17, 0xe4, 0xe7, 0x17, 0x1d, + 0x8f, 0x80, 0xa5, 0x3d, 0x38, 0x28, 0x20, 0x82, 0x18, 0x50, 0xf0, 0x52, 0x02, 0x0c, 0xc2, 0xf1, 0x15, 0x1b, 0x1a, + 0xf0, 0xb9, 0x36, 0x5e, 0x07, 0xc6, 0xd6, 0xa7, 0x0c, 0x72, 0xf1, 0xac, 0xda, 0xd3, 0x84, 0x90, 0xfd, 0x56, 0x4f, + 0xa7, 0xdb, 0x11, 0x12, 0x7b, 0x1f, 0xb5, 0x09, 0x34, 0xe6, 0x48, 0x77, 0xb5, 0x31, 0xbf, 0xd6, 0xf4, 0x88, 0xd5, + 0x24, 0xa4, 0x0b, 0xd2, 0xe5, 0xf9, 0xb4, 0x67, 0x70, 0xc5, 0x2a, 0x8d, 0x1c, 0x5c, 0x80, 0x3e, 0x1b, 0x10, 0xa0, + 0x40, 0xa5, 0xa9, 0x44, 0x51, 0xc4, 0x45, 0x52, 0xb2, 0x61, 0x98, 0x41, 0x98, 0xc2, 0x6a, 0x25, 0xe8, 0xc6, 0x1a, + 0x00, 0xef, 0xcc, 0xec, 0x9f, 0xd2, 0x07, 0x9b, 0xae, 0xbd, 0x79, 0x04, 0x10, 0x90, 0xfd, 0x76, 0xc9, 0xae, 0x8b, + 0x8d, 0xca, 0x2c, 0xac, 0x65, 0x6c, 0xe5, 0xb6, 0x3d, 0xc6, 0xde, 0x89, 0x6d, 0x3e, 0x01, 0x42, 0xd4, 0x96, 0x4c, + 0x23, 0x44, 0x48, 0x2c, 0x62, 0x5d, 0x1b, 0xb2, 0xd1, 0x86, 0xc2, 0x53, 0x89, 0x1c, 0xb8, 0x44, 0x13, 0x24, 0xdf, + 0x71, 0x09, 0x0e, 0xe1, 0x85, 0x47, 0xf8, 0x5b, 0x60, 0x91, 0x0a, 0xcc, 0xb0, 0x5c, 0xad, 0xa0, 0x9e, 0xc7, 0xfb, + 0x6c, 0x33, 0x38, 0xa9, 0xdc, 0x18, 0xbb, 0xb4, 0x13, 0x8f, 0xcb, 0x26, 0x24, 0xce, 0xa0, 0x5f, 0x5f, 0x11, 0xf5, + 0xf6, 0xdb, 0xe9, 0x13, 0xff, 0x5e, 0x99, 0xdb, 0x81, 0xd8, 0xb0, 0xde, 0x60, 0xf5, 0x01, 0xb4, 0xfc, 0x55, 0xe6, + 0x1f, 0x2a, 0x0b, 0x6e, 0x12, 0xd4, 0xe6, 0x22, 0x76, 0x59, 0x17, 0x31, 0x52, 0x5b, 0xdc, 0x1d, 0x42, 0xfc, 0xab, + 0xad, 0x28, 0x06, 0x3c, 0xa9, 0xf8, 0xe7, 0x18, 0x75, 0x21, 0x14, 0xb5, 0xf5, 0xb0, 0x01, 0x4a, 0xbb, 0x5c, 0x57, + 0x62, 0x64, 0x48, 0x20, 0xdf, 0xba, 0xf0, 0x82, 0xe6, 0x24, 0x52, 0x20, 0x27, 0x07, 0x51, 0x49, 0xb3, 0x0d, 0x61, + 0xae, 0xbb, 0x85, 0x63, 0xe6, 0x6a, 0x83, 0x16, 0xf1, 0x0b, 0x60, 0x67, 0xb8, 0x91, 0x2c, 0x1d, 0xf8, 0x54, 0x0d, + 0x7c, 0x7e, 0xcd, 0x0d, 0x45, 0x51, 0xa8, 0xf7, 0xce, 0x3e, 0x32, 0x07, 0xbf, 0xd3, 0x40, 0x7c, 0xa4, 0x4e, 0x47, + 0xb2, 0x11, 0x6a, 0xcd, 0xd9, 0xf1, 0xb2, 0xcd, 0x08, 0x83, 0xc2, 0x46, 0xef, 0xab, 0x90, 0x55, 0xec, 0xec, 0x54, + 0x04, 0x73, 0xfa, 0xa2, 0x2a, 0xe7, 0x54, 0x6e, 0x19, 0xd5, 0x52, 0xd3, 0x00, 0x11, 0xae, 0x7c, 0x22, 0x79, 0x9f, + 0x99, 0xf0, 0x0f, 0x06, 0xe3, 0xea, 0x91, 0xc2, 0xdf, 0xef, 0x8a, 0x1d, 0xb2, 0x1d, 0x1d, 0x6e, 0x23, 0x68, 0x5e, + 0xa8, 0xe0, 0x01, 0x47, 0x25, 0x4b, 0x88, 0x14, 0xb9, 0xdc, 0x57, 0x35, 0x53, 0xb6, 0xeb, 0x08, 0x21, 0xa4, 0x3d, + 0xce, 0xba, 0xa1, 0xd5, 0x43, 0x8f, 0x54, 0x51, 0x0e, 0xb7, 0x68, 0xae, 0x0b, 0x50, 0x61, 0x04, 0xd2, 0xe5, 0x67, + 0x76, 0x97, 0x4a, 0x88, 0x5e, 0xbe, 0x76, 0x21, 0x8c, 0x9d, 0x95, 0x25, 0x2e, 0xcc, 0xa8, 0x6d, 0x18, 0x5d, 0xb7, + 0x31, 0x9c, 0x0d, 0x8c, 0x99, 0x06, 0x25, 0x2d, 0x08, 0x75, 0xdd, 0xa5, 0x17, 0x99, 0x09, 0xf4, 0x98, 0x13, 0xda, + 0x60, 0x78, 0x4a, 0x34, 0x58, 0x36, 0x15, 0x60, 0xc1, 0xb7, 0x2c, 0x52, 0x6b, 0xb3, 0xc9, 0xe2, 0x8f, 0x3a, 0x36, + 0x4f, 0xfb, 0xe5, 0x15, 0xf3, 0x5c, 0x38, 0xea, 0xf6, 0x3c, 0xf3, 0xf1, 0xe8, 0x9e, 0xbe, 0xb9, 0x7a, 0xf1, 0xf2, + 0xf5, 0xab, 0xd5, 0xaa, 0xcd, 0x9a, 0xed, 0x13, 0xfc, 0x93, 0x2e, 0xe3, 0xc1, 0x96, 0x51, 0x80, 0x0e, 0x0e, 0xf6, + 0xb9, 0x71, 0xe1, 0xf9, 0xcc, 0xe7, 0x10, 0x37, 0x48, 0x0f, 0x70, 0x56, 0x94, 0x31, 0x41, 0x6e, 0xa3, 0x5e, 0x74, + 0x17, 0x81, 0x12, 0xaa, 0x22, 0x7f, 0x1f, 0x36, 0x67, 0xbf, 0x07, 0x81, 0x89, 0xa0, 0x3e, 0x44, 0x00, 0x81, 0x78, + 0xa5, 0xb8, 0x20, 0xcc, 0x27, 0x40, 0x14, 0xef, 0x05, 0x70, 0xa6, 0x26, 0x6a, 0xd5, 0x42, 0xc5, 0x05, 0x90, 0x44, + 0x1b, 0x8e, 0x92, 0x1e, 0x99, 0x00, 0xde, 0x10, 0x94, 0xd2, 0xfe, 0xea, 0xe6, 0xce, 0x5d, 0x2a, 0x47, 0xbd, 0x56, + 0x9a, 0xe3, 0xa9, 0xfb, 0x9c, 0xc2, 0xe7, 0xb4, 0xeb, 0x4f, 0x07, 0x71, 0x98, 0xe3, 0x05, 0x11, 0x87, 0xfe, 0x59, + 0xc4, 0xe5, 0xbc, 0x60, 0x5f, 0xb8, 0x5c, 0xa8, 0x74, 0x79, 0x9b, 0xca, 0xe4, 0xb6, 0x39, 0x3a, 0x8c, 0x8b, 0xe4, + 0xb6, 0xa9, 0x92, 0x5b, 0x84, 0xef, 0x52, 0x99, 0xdc, 0xd9, 0x94, 0xbb, 0xa6, 0x82, 0x9b, 0x2f, 0x2c, 0xe0, 0x50, + 0xb4, 0x45, 0x1b, 0x8b, 0xcd, 0xa2, 0x36, 0xc5, 0x15, 0x0d, 0x30, 0xf8, 0xf7, 0x1d, 0x1b, 0x3f, 0x0c, 0x5f, 0x82, + 0x4b, 0x93, 0x26, 0xf2, 0x13, 0x48, 0x3f, 0xad, 0xca, 0xc0, 0x7d, 0x4a, 0x5a, 0xdd, 0xe9, 0x85, 0x68, 0xb6, 0xbb, + 0x8d, 0xc6, 0x14, 0xf6, 0x6e, 0x46, 0x72, 0x5f, 0x6c, 0xda, 0x30, 0xf1, 0x75, 0xf6, 0xb3, 0xd5, 0x6a, 0x3f, 0x47, + 0x66, 0xc3, 0x4d, 0x58, 0xac, 0xfb, 0xd3, 0x01, 0x6e, 0xe1, 0xe7, 0x19, 0x42, 0x4b, 0xd6, 0x9f, 0x0e, 0x08, 0xeb, + 0x4f, 0x1b, 0xed, 0x81, 0x35, 0xb4, 0x33, 0x5b, 0x71, 0x0d, 0x21, 0x34, 0xa7, 0x83, 0x23, 0x53, 0x52, 0xba, 0x7c, + 0xfb, 0x45, 0xab, 0x80, 0x7e, 0xaa, 0x16, 0xbc, 0x4c, 0xe2, 0x0e, 0xf4, 0x45, 0x2f, 0xec, 0xd3, 0xad, 0x05, 0x39, + 0x3e, 0xaa, 0x5c, 0xed, 0x29, 0xc2, 0xa6, 0x27, 0x75, 0x58, 0x1c, 0x9a, 0x66, 0x5c, 0x97, 0xd2, 0x7d, 0x87, 0x9a, + 0x91, 0x8f, 0x0e, 0x16, 0x80, 0x20, 0x15, 0x3c, 0xb2, 0xc2, 0x85, 0x53, 0x0a, 0xe1, 0xe2, 0xa0, 0xb2, 0x05, 0x93, + 0x9c, 0xb4, 0xba, 0xb9, 0xb1, 0xf4, 0xcf, 0x5d, 0x44, 0x53, 0x8a, 0x29, 0xc9, 0x7c, 0xc9, 0xdc, 0x80, 0x85, 0x6e, + 0x52, 0x9e, 0x29, 0xe8, 0x95, 0x06, 0x78, 0x44, 0x20, 0x1e, 0x52, 0xb7, 0x30, 0x06, 0x5e, 0xf1, 0xb4, 0x59, 0xf4, + 0xd9, 0x00, 0x1d, 0x1d, 0x63, 0xda, 0xff, 0x94, 0xcd, 0xdb, 0xf0, 0x58, 0xe0, 0xa7, 0x01, 0x99, 0x36, 0x65, 0x99, + 0x20, 0x20, 0x61, 0xd4, 0x94, 0x87, 0xb0, 0x97, 0x10, 0xce, 0x6c, 0xc5, 0xac, 0xcf, 0x06, 0xcd, 0x69, 0x59, 0xb1, + 0xe3, 0x2b, 0x36, 0x64, 0x99, 0x60, 0x2b, 0x36, 0x5c, 0xc5, 0xf0, 0x75, 0x06, 0x03, 0x82, 0x10, 0x00, 0x0c, 0x00, + 0xa0, 0x51, 0x10, 0xcd, 0x17, 0x2b, 0xe2, 0x37, 0xbb, 0xbd, 0xc7, 0x6f, 0x81, 0x05, 0x5a, 0x6d, 0xff, 0xef, 0x42, + 0x19, 0xb0, 0xa7, 0x2c, 0x4c, 0xcc, 0xdc, 0xc2, 0xaa, 0xe8, 0x00, 0x2a, 0x25, 0xc2, 0x14, 0x06, 0x32, 0xfb, 0x99, + 0x81, 0x5a, 0xa0, 0x35, 0xc8, 0xfb, 0x7a, 0xd0, 0xcc, 0xe0, 0x88, 0x81, 0x77, 0x68, 0xc8, 0xd4, 0x18, 0x13, 0xc6, + 0x39, 0x4c, 0x31, 0x33, 0xe0, 0x99, 0xa6, 0xad, 0xb5, 0x34, 0xb2, 0x5c, 0x2f, 0xef, 0xfd, 0xa3, 0x63, 0xd5, 0x2f, + 0x9a, 0xed, 0x01, 0xda, 0x27, 0xc4, 0x7e, 0x0c, 0x60, 0x93, 0xb9, 0xd4, 0x86, 0xf9, 0x3e, 0xea, 0xa4, 0xf6, 0x13, + 0xfe, 0x0c, 0xd6, 0x66, 0x07, 0x80, 0x8e, 0x0c, 0x9b, 0xf5, 0x97, 0x35, 0x95, 0xd7, 0xc7, 0x9d, 0x51, 0x2a, 0x77, + 0xbd, 0x3b, 0x1d, 0x68, 0x8a, 0x43, 0x6f, 0x3d, 0x5c, 0x3e, 0xd4, 0x43, 0xc0, 0x8c, 0xc1, 0xdc, 0x32, 0xa3, 0xef, + 0x85, 0x48, 0x2e, 0x88, 0xc4, 0xd2, 0x60, 0x0d, 0x83, 0xbd, 0x75, 0x70, 0x60, 0xaa, 0xb1, 0x06, 0x3c, 0x4f, 0x8a, + 0x40, 0x30, 0xf0, 0x11, 0x94, 0x01, 0x4d, 0x94, 0xb9, 0x0d, 0x27, 0x1f, 0x99, 0xfb, 0x85, 0xcb, 0xdb, 0xc7, 0xc2, + 0x69, 0x5b, 0xcd, 0xf5, 0x78, 0x59, 0xe0, 0xae, 0xbc, 0x97, 0xb4, 0x0a, 0x6e, 0x64, 0x6f, 0xf2, 0x94, 0xb9, 0x5b, + 0xf7, 0xa5, 0x3a, 0xbb, 0x9b, 0xe9, 0x94, 0xcd, 0x74, 0xb6, 0x9b, 0x09, 0x35, 0x33, 0xdf, 0xb2, 0x8a, 0x34, 0x27, + 0x6b, 0xa2, 0xe6, 0x54, 0xfc, 0x44, 0xe7, 0xa0, 0x1d, 0xe5, 0xf6, 0x5e, 0x15, 0x4e, 0xae, 0x9c, 0x5c, 0xee, 0xe7, + 0x86, 0xb8, 0x22, 0x73, 0xa1, 0x0e, 0x01, 0x5e, 0x5e, 0x94, 0x8f, 0x0f, 0x70, 0x29, 0x7e, 0x95, 0x23, 0x17, 0xe5, + 0x54, 0x48, 0x2d, 0x05, 0x8b, 0x90, 0x41, 0x55, 0x17, 0x03, 0x7b, 0x69, 0xf7, 0x9e, 0xe8, 0xf1, 0x7e, 0x15, 0x31, + 0x6f, 0x60, 0x9e, 0xfb, 0xf8, 0x9e, 0xa6, 0xd8, 0xa9, 0x89, 0x33, 0xf2, 0x21, 0x8b, 0x73, 0x90, 0xcd, 0xfa, 0xd5, + 0x6b, 0xbf, 0x8d, 0x36, 0x2e, 0x9a, 0xb1, 0xe8, 0x99, 0x27, 0x4e, 0x7e, 0x28, 0x8c, 0x71, 0x80, 0x75, 0xf4, 0x47, + 0x98, 0x5a, 0xb0, 0x67, 0x89, 0xa7, 0xd0, 0xc9, 0xad, 0x4d, 0xbb, 0x0b, 0xd3, 0xee, 0x4c, 0x5a, 0x07, 0xca, 0x01, + 0x69, 0x76, 0x65, 0x3a, 0x77, 0xfe, 0xfb, 0x0e, 0x5e, 0xba, 0x5d, 0x43, 0x24, 0xee, 0xf9, 0x23, 0x63, 0x0c, 0xf1, + 0x06, 0x6c, 0x44, 0xd5, 0xc1, 0xc1, 0x1f, 0xce, 0xfb, 0xb6, 0x92, 0xfb, 0xbe, 0x15, 0x0e, 0x6c, 0x83, 0xa9, 0x74, + 0x79, 0x23, 0x99, 0x2d, 0xc0, 0xae, 0x73, 0xff, 0x1b, 0xf1, 0xf0, 0x45, 0xc8, 0xb4, 0x58, 0x57, 0xf1, 0x57, 0x72, + 0x54, 0x7a, 0x88, 0x6a, 0x88, 0x40, 0x5a, 0x59, 0x97, 0x86, 0xa6, 0xa3, 0x57, 0x53, 0x3a, 0x92, 0x37, 0x6f, 0xa5, + 0xd4, 0x03, 0xfb, 0x22, 0xb7, 0x4e, 0xe0, 0xd1, 0xc2, 0x1a, 0x43, 0x73, 0x57, 0x7a, 0x27, 0xd9, 0x80, 0xa8, 0xf5, + 0x71, 0x87, 0x92, 0x48, 0x2c, 0xaa, 0xbb, 0x10, 0x0e, 0x77, 0x21, 0x98, 0x97, 0x41, 0xdb, 0x20, 0x76, 0xbb, 0x0b, + 0xda, 0x06, 0x4e, 0xdd, 0x36, 0x70, 0x7b, 0x30, 0x58, 0xd8, 0xfb, 0xf0, 0x72, 0x2c, 0xc7, 0xc2, 0x5f, 0x93, 0xd9, + 0x07, 0x80, 0x40, 0xed, 0xc3, 0x8a, 0x27, 0x0e, 0x04, 0x89, 0x33, 0x1c, 0x7d, 0xcf, 0xd9, 0x8d, 0xb5, 0x1c, 0x9e, + 0xcd, 0x17, 0x9a, 0x8d, 0xcc, 0x1d, 0x35, 0xa8, 0xf8, 0xea, 0x7e, 0x5e, 0x3f, 0x65, 0x35, 0xdd, 0xf8, 0x3d, 0x08, + 0x23, 0xe1, 0x94, 0x1d, 0x46, 0x21, 0x61, 0x83, 0x59, 0x95, 0xf1, 0xda, 0x7e, 0x83, 0x78, 0x0f, 0xda, 0x84, 0x13, + 0x2c, 0x6a, 0x17, 0x54, 0x11, 0xb6, 0xf1, 0xc6, 0x82, 0x28, 0x0f, 0x6f, 0xb6, 0x8c, 0xa6, 0x97, 0x6b, 0x08, 0x74, + 0xdc, 0x8b, 0x9a, 0x51, 0x83, 0xa5, 0x2e, 0x28, 0xb3, 0x8f, 0x30, 0xae, 0x2e, 0x4e, 0x4c, 0x9c, 0xf6, 0x52, 0xaf, + 0xfe, 0x5b, 0x06, 0x06, 0xf8, 0x02, 0xbc, 0xc4, 0xc2, 0xe8, 0xae, 0x7d, 0xdd, 0x80, 0xfa, 0xb2, 0xc1, 0x06, 0x68, + 0xb5, 0x6a, 0x95, 0xcf, 0x40, 0xb9, 0x6b, 0x2e, 0x61, 0xaf, 0xb9, 0x84, 0xbb, 0xe6, 0x12, 0xfe, 0x9a, 0x4b, 0x98, + 0x6b, 0x2e, 0xe1, 0xaf, 0xb9, 0x3c, 0x08, 0xff, 0x0c, 0xe2, 0x38, 0xc6, 0x1c, 0xe2, 0x2a, 0x6a, 0x1b, 0x19, 0x0f, + 0x2e, 0x3c, 0xf7, 0x59, 0xa2, 0xca, 0xe5, 0x0f, 0x63, 0xc8, 0x6d, 0xd9, 0x4a, 0x18, 0xb7, 0x29, 0xa6, 0x20, 0x72, + 0xfa, 0xc1, 0x41, 0xe9, 0xee, 0x0c, 0x3e, 0xea, 0x29, 0xc7, 0x4b, 0xeb, 0x44, 0xfb, 0x07, 0xe8, 0xe4, 0xcd, 0xaf, + 0x8f, 0xa9, 0x5c, 0x13, 0xe1, 0x4c, 0xee, 0xf7, 0xdb, 0x9e, 0x52, 0xfc, 0x99, 0x99, 0xf0, 0xe4, 0x3c, 0xd1, 0x46, + 0x04, 0x41, 0x88, 0x12, 0x85, 0x33, 0x22, 0xed, 0x7e, 0xf7, 0xae, 0xf0, 0x46, 0x15, 0xe5, 0xcd, 0x4a, 0x1e, 0xe7, + 0xe0, 0xc4, 0x6e, 0xac, 0x30, 0x50, 0x17, 0x5c, 0x08, 0x32, 0x93, 0xf0, 0x47, 0x33, 0xb7, 0xe4, 0x2c, 0x2b, 0x93, + 0x3e, 0x36, 0x73, 0x43, 0xc0, 0x0a, 0xb2, 0xef, 0x61, 0xb6, 0xbc, 0x4d, 0xe9, 0xff, 0xcb, 0xde, 0xbb, 0x2e, 0xb7, + 0x8d, 0x64, 0xe9, 0xa2, 0xaf, 0x22, 0x31, 0x6c, 0x16, 0x60, 0x26, 0x29, 0xca, 0x7b, 0xcf, 0x44, 0x1c, 0x50, 0x29, + 0x86, 0x2f, 0xe5, 0x2e, 0x77, 0x97, 0x2f, 0x6d, 0xb9, 0xab, 0xab, 0x9a, 0xc1, 0xc3, 0x82, 0x80, 0xa4, 0x00, 0x17, + 0x08, 0xb0, 0x00, 0x50, 0x22, 0x4d, 0xe2, 0xdd, 0x77, 0xac, 0xb5, 0xf2, 0x0a, 0x82, 0xb2, 0x7b, 0x66, 0xcf, 0xaf, + 0x73, 0xfe, 0xd8, 0x62, 0x22, 0x91, 0xc8, 0x7b, 0xae, 0x5c, 0x97, 0xef, 0x63, 0xbb, 0x20, 0x62, 0xb7, 0xc5, 0x36, + 0x28, 0x6d, 0x5f, 0x10, 0x65, 0xf8, 0x5b, 0x7a, 0xbd, 0x3c, 0x84, 0x78, 0x9f, 0x5e, 0x9a, 0x9f, 0xa5, 0xad, 0x28, + 0xc0, 0x7d, 0x84, 0x1e, 0xd5, 0x81, 0x60, 0x27, 0x3c, 0xe1, 0x01, 0x9c, 0xac, 0x66, 0x15, 0x7f, 0x92, 0x82, 0x38, + 0x51, 0x70, 0x08, 0xb8, 0xda, 0xde, 0xa4, 0x5f, 0xc1, 0xf0, 0xa5, 0x83, 0x2d, 0x87, 0xb7, 0xc5, 0xb6, 0xc7, 0x4a, + 0xfe, 0x11, 0xd8, 0xb7, 0x7a, 0x32, 0x56, 0xb7, 0x07, 0xce, 0xba, 0x94, 0xa2, 0xe3, 0x4d, 0x71, 0x78, 0x7b, 0x3e, + 0xdb, 0x6f, 0x83, 0x88, 0xed, 0x82, 0x0c, 0x6b, 0x9d, 0x34, 0xfc, 0xaf, 0xb4, 0x75, 0xb0, 0x18, 0x61, 0xff, 0x97, + 0xf5, 0xc0, 0x4b, 0x48, 0x0d, 0x05, 0x2e, 0x06, 0x1b, 0x8e, 0xd6, 0x76, 0x99, 0x06, 0x6e, 0x6a, 0xd0, 0xeb, 0x7b, + 0x0a, 0x51, 0x5e, 0x32, 0x9a, 0x1b, 0xc1, 0xba, 0x31, 0xe4, 0xe2, 0x70, 0xdc, 0x2c, 0x87, 0xbc, 0xa4, 0xe9, 0x34, + 0x08, 0xa5, 0x3b, 0xcb, 0x1a, 0x92, 0x28, 0xfb, 0x20, 0xd4, 0xae, 0x2d, 0xfb, 0x6d, 0x60, 0xfb, 0xf2, 0x47, 0xc3, + 0xd8, 0xbf, 0x58, 0x3e, 0x13, 0xd2, 0x45, 0x3c, 0x07, 0x41, 0xd4, 0x7e, 0x9e, 0x0d, 0x37, 0xfe, 0xc5, 0xfa, 0x99, + 0x50, 0x7e, 0xe3, 0xb9, 0x2d, 0x87, 0xd4, 0x59, 0x0b, 0x5f, 0x18, 0x0f, 0x0f, 0xae, 0x0c, 0x6d, 0x87, 0x83, 0xd0, + 0x7f, 0x9b, 0x35, 0x82, 0x1b, 0x1b, 0xda, 0xe7, 0x0b, 0x1f, 0xb6, 0x36, 0x1a, 0x6b, 0x8a, 0xe9, 0x16, 0xfa, 0x37, + 0x99, 0x2d, 0xed, 0x69, 0x54, 0xf2, 0xe2, 0xd4, 0x34, 0x62, 0x21, 0x0c, 0x18, 0xfa, 0xc9, 0x7c, 0x04, 0xd5, 0xdc, + 0xf1, 0x08, 0x64, 0xf2, 0x81, 0x1e, 0xac, 0x49, 0xad, 0xfa, 0x6b, 0x98, 0xc9, 0xff, 0x23, 0x15, 0x16, 0xa3, 0xbb, + 0x6d, 0x98, 0xa9, 0x3f, 0x22, 0xf9, 0x07, 0xcb, 0xf9, 0x2e, 0xf5, 0x42, 0xed, 0xc7, 0xc2, 0x0a, 0x0c, 0x4a, 0x54, + 0x0d, 0xe8, 0x81, 0x08, 0xaa, 0x32, 0x48, 0x33, 0xac, 0xce, 0x41, 0xbf, 0x7b, 0x5a, 0x75, 0x24, 0x87, 0xb4, 0x56, + 0x43, 0x2a, 0x98, 0x2a, 0x35, 0xc8, 0x0f, 0x87, 0xbb, 0x94, 0xe9, 0x32, 0xe0, 0x92, 0x7e, 0x97, 0x2a, 0xa5, 0xf0, + 0x9f, 0x08, 0x40, 0xe7, 0xe0, 0x1e, 0x5f, 0x8e, 0x81, 0x34, 0xc3, 0xc2, 0x6f, 0xcd, 0x8e, 0xaf, 0x49, 0xb8, 0x4d, + 0x82, 0x8b, 0x01, 0xce, 0xd1, 0x55, 0x58, 0xde, 0xa5, 0x10, 0x41, 0x55, 0x42, 0x7d, 0x2b, 0xd3, 0xa0, 0xb4, 0xd5, + 0x20, 0xac, 0x49, 0xa8, 0x33, 0xc9, 0x46, 0xa5, 0xed, 0x46, 0x61, 0xb6, 0x88, 0xeb, 0x19, 0x61, 0xcd, 0xd9, 0x4c, + 0x35, 0x30, 0x69, 0x38, 0x6e, 0x1a, 0xad, 0x45, 0x85, 0x9a, 0xc2, 0xbc, 0xc6, 0x55, 0xa5, 0xaa, 0xbb, 0x39, 0xb5, + 0x94, 0x96, 0xed, 0x55, 0x37, 0xc9, 0x86, 0x5c, 0x86, 0x32, 0x0c, 0x36, 0x72, 0x04, 0x13, 0x48, 0x92, 0x33, 0x7f, + 0x23, 0xff, 0x50, 0x9b, 0xae, 0x05, 0xcc, 0x31, 0x66, 0xd9, 0xb0, 0xa0, 0x57, 0xe0, 0x1e, 0x68, 0xa5, 0xe7, 0xd3, + 0xec, 0x22, 0x0f, 0x92, 0x61, 0xa1, 0x97, 0x4d, 0xc6, 0xff, 0x14, 0x46, 0x9a, 0xcc, 0x58, 0xc9, 0x22, 0xdb, 0xd5, + 0x29, 0x71, 0x1e, 0x27, 0xb0, 0x3d, 0x9a, 0xde, 0xf2, 0x7d, 0x06, 0x51, 0x41, 0xa0, 0x60, 0xc6, 0x7c, 0xd9, 0xc5, + 0x73, 0xdf, 0x67, 0x96, 0xa9, 0xfb, 0x70, 0x30, 0x66, 0x6c, 0xbf, 0xdf, 0xcf, 0xfb, 0x7d, 0x35, 0xdf, 0xfa, 0xfd, + 0xe4, 0xda, 0xfc, 0xed, 0x01, 0x83, 0x82, 0x9c, 0x88, 0xa6, 0x42, 0x04, 0xff, 0x90, 0x3c, 0x43, 0x32, 0xba, 0xe3, + 0x3e, 0xb7, 0x9c, 0x2d, 0xab, 0x23, 0x10, 0xcc, 0xc3, 0xe1, 0x52, 0x81, 0x5d, 0x4b, 0x14, 0x09, 0x59, 0xfe, 0x33, + 0x30, 0x9e, 0xb9, 0x0f, 0xb0, 0x64, 0x00, 0xc2, 0x56, 0x79, 0xba, 0xde, 0xf3, 0x55, 0xf0, 0x4e, 0xc7, 0xbb, 0xc6, + 0x8a, 0x0c, 0xc4, 0x2d, 0xb0, 0x11, 0x6b, 0xed, 0x01, 0x39, 0x53, 0x80, 0xe3, 0xc5, 0xe1, 0x70, 0x2e, 0x7f, 0xe9, + 0x66, 0xeb, 0x04, 0x2a, 0x05, 0x6e, 0x8f, 0x4e, 0x0e, 0xfe, 0x3b, 0xd0, 0x0c, 0xca, 0x61, 0x5e, 0x6f, 0x7f, 0x67, + 0x4e, 0x7e, 0x7a, 0x8a, 0x7f, 0xc2, 0x43, 0x74, 0xfa, 0xed, 0xde, 0xfc, 0x41, 0x51, 0x79, 0x38, 0xa8, 0xc5, 0x7f, + 0xce, 0x79, 0x05, 0xbf, 0xf0, 0x4d, 0x60, 0x36, 0x99, 0x7a, 0x27, 0xdf, 0xe4, 0x39, 0x53, 0xaf, 0xf1, 0x8a, 0xc9, + 0x77, 0x38, 0x9c, 0x8b, 0x51, 0xbd, 0x1d, 0x39, 0xd1, 0x4e, 0x39, 0xc6, 0xc1, 0xe0, 0xbf, 0x88, 0xb6, 0x09, 0x01, + 0x86, 0xd4, 0x2d, 0x69, 0x66, 0xe3, 0xca, 0x12, 0xcf, 0xd2, 0xf9, 0xe5, 0xa4, 0x2e, 0x77, 0x5a, 0xf1, 0xb4, 0x07, + 0x16, 0xb7, 0x35, 0x78, 0x01, 0xdc, 0x5b, 0x6c, 0x5d, 0x29, 0x38, 0x5c, 0x40, 0x9c, 0xe2, 0x04, 0x44, 0xd0, 0x7e, + 0x5f, 0xe2, 0xbd, 0x82, 0x3e, 0xe9, 0x47, 0x08, 0x86, 0xfc, 0x59, 0x02, 0xee, 0x7a, 0xbd, 0x1a, 0xe3, 0x7b, 0x29, + 0x04, 0xd7, 0x67, 0x1a, 0x80, 0x16, 0xfc, 0x2e, 0x1f, 0xcb, 0xe9, 0x37, 0x11, 0x78, 0xb6, 0xec, 0x4d, 0x94, 0xbb, + 0x0d, 0x4f, 0xfb, 0x47, 0x0b, 0x01, 0x58, 0x8a, 0x67, 0x4a, 0xb0, 0x20, 0xa7, 0x98, 0x8b, 0xff, 0x17, 0x7c, 0xc4, + 0x7c, 0x4f, 0xba, 0x88, 0xad, 0xb7, 0x4f, 0x2e, 0x0c, 0x24, 0xd0, 0x74, 0x00, 0x7e, 0xbc, 0x0a, 0xe8, 0xca, 0xf8, + 0xf9, 0x59, 0xd6, 0x63, 0x7d, 0xfc, 0xa7, 0xe0, 0x3e, 0xfd, 0x4c, 0xe1, 0xa3, 0xc3, 0x71, 0x95, 0x8e, 0x76, 0x94, + 0x82, 0xe8, 0xe8, 0xf6, 0xf9, 0x94, 0x67, 0xdf, 0x55, 0x40, 0x6e, 0x39, 0x6a, 0x4f, 0x05, 0x60, 0xb1, 0xa5, 0x23, + 0xf0, 0x69, 0x96, 0x4f, 0xc8, 0xf7, 0x7a, 0x2a, 0xae, 0x2e, 0x75, 0xba, 0xb8, 0x1e, 0x4f, 0xe1, 0x7f, 0x20, 0xf6, + 0xb0, 0x4c, 0x91, 0x1d, 0xbb, 0x2e, 0x7e, 0x10, 0x6f, 0x6b, 0x3b, 0xfa, 0x63, 0x07, 0x91, 0x8e, 0x7b, 0x72, 0xa1, + 0xbe, 0x84, 0x54, 0x72, 0xa1, 0x6e, 0x20, 0x76, 0xa1, 0xc6, 0x3b, 0x2e, 0x62, 0xad, 0xbf, 0xab, 0x51, 0xb0, 0x12, + 0x70, 0xa6, 0xbd, 0x03, 0x83, 0x0d, 0xac, 0x5b, 0x96, 0xc1, 0xdf, 0x70, 0x4d, 0x13, 0xb8, 0x61, 0x91, 0xf5, 0xde, + 0x60, 0x2b, 0xbd, 0x03, 0x47, 0xcb, 0xc4, 0xb9, 0x94, 0x64, 0x65, 0x8b, 0x8c, 0xab, 0x47, 0x21, 0x55, 0xd3, 0xfd, + 0xad, 0xa8, 0x1f, 0x84, 0xc8, 0x83, 0x55, 0xca, 0xa2, 0x62, 0x05, 0x32, 0x7b, 0xf0, 0xf7, 0x90, 0x91, 0xa3, 0x1c, + 0x38, 0x0a, 0xfd, 0xbd, 0x09, 0x74, 0x9e, 0xbf, 0x86, 0x3a, 0x8f, 0x04, 0x5b, 0xa9, 0x87, 0xc2, 0xca, 0x0b, 0x88, + 0x0e, 0xb6, 0x30, 0x56, 0x79, 0x12, 0x2a, 0x36, 0x65, 0x22, 0x8f, 0x83, 0x5a, 0x02, 0xc6, 0x0a, 0x82, 0x39, 0xcb, + 0xa5, 0x0b, 0x52, 0xd5, 0xe8, 0x61, 0x91, 0xb9, 0x9f, 0x0a, 0xca, 0xff, 0x54, 0xe5, 0x84, 0xeb, 0xcb, 0x10, 0xe0, + 0x68, 0x9f, 0x82, 0x28, 0x31, 0xd6, 0x2f, 0x5a, 0xbc, 0x93, 0x99, 0xb3, 0xa9, 0xed, 0x25, 0xc8, 0xd8, 0x0e, 0xbf, + 0x42, 0x68, 0xb5, 0x50, 0x64, 0xd1, 0x70, 0xc1, 0x74, 0x7b, 0x4a, 0xab, 0xee, 0x61, 0xc3, 0xb3, 0xd2, 0x43, 0xa5, + 0xbe, 0x8d, 0x09, 0x2c, 0xab, 0x94, 0xe1, 0xdb, 0x09, 0x55, 0x27, 0x06, 0x15, 0xeb, 0x86, 0x2d, 0xe1, 0x10, 0x8b, + 0x49, 0x63, 0x9d, 0x0d, 0x78, 0xc4, 0x12, 0xf8, 0x67, 0xc3, 0xc7, 0x6c, 0xc9, 0xa3, 0xc9, 0xe6, 0x6a, 0xd9, 0xef, + 0x97, 0x5e, 0xe8, 0xd5, 0xb3, 0xec, 0x69, 0x34, 0x9f, 0xe5, 0x73, 0x1f, 0x15, 0x17, 0x93, 0xc1, 0x60, 0xe3, 0x67, + 0xc3, 0x21, 0x4b, 0x86, 0xc3, 0x49, 0xf6, 0x14, 0x5e, 0x7b, 0xca, 0x23, 0xb5, 0xa4, 0x92, 0xab, 0x0c, 0xf6, 0xf7, + 0x01, 0x8f, 0x7c, 0xd6, 0xf9, 0x69, 0xd9, 0x74, 0xe9, 0x7e, 0x66, 0x75, 0x40, 0xa9, 0x3b, 0xc0, 0xc6, 0xdb, 0x06, + 0x1d, 0xf9, 0xb7, 0x3b, 0xa4, 0xd4, 0x4d, 0x06, 0x60, 0x37, 0x1a, 0xe0, 0x90, 0xa9, 0x5e, 0x8a, 0xac, 0x5e, 0xca, + 0x54, 0x2f, 0xc9, 0xca, 0x25, 0x58, 0x48, 0x4c, 0x95, 0xdb, 0xc8, 0xca, 0x2d, 0x1b, 0xae, 0x87, 0x83, 0xad, 0x15, + 0x97, 0xcd, 0x1d, 0xdc, 0x17, 0x56, 0x14, 0xf8, 0x7f, 0xcb, 0x16, 0xec, 0x5e, 0x1e, 0x03, 0xef, 0xd0, 0x31, 0x09, + 0x2e, 0x10, 0xf7, 0xec, 0x16, 0xec, 0xb0, 0xf0, 0x17, 0x5c, 0x27, 0xc7, 0x6c, 0x87, 0x8f, 0x42, 0xaf, 0x60, 0xb7, + 0x3e, 0x01, 0xed, 0x82, 0xad, 0x01, 0xb2, 0xb1, 0x2d, 0x3e, 0xba, 0x3b, 0x1c, 0xde, 0x79, 0x3e, 0x7b, 0xc0, 0x1f, + 0xe7, 0x77, 0x87, 0xc3, 0xce, 0x33, 0xea, 0xbd, 0x1b, 0x9e, 0xb0, 0x0f, 0x3c, 0x99, 0xdc, 0x5c, 0xf1, 0x78, 0x32, + 0x18, 0xdc, 0xf8, 0x0b, 0x5e, 0xcf, 0x6e, 0x40, 0x3b, 0x70, 0xbe, 0x90, 0xba, 0x66, 0xef, 0x96, 0x67, 0xde, 0x02, + 0xc7, 0xe6, 0x16, 0x8e, 0xde, 0x7e, 0xdf, 0xbb, 0xe3, 0x91, 0x77, 0x4b, 0x2a, 0xa6, 0x15, 0x57, 0x1c, 0x6f, 0x5b, + 0xdc, 0x4f, 0x57, 0x3c, 0x84, 0x47, 0x58, 0x95, 0xe9, 0x4d, 0xf0, 0xc1, 0x67, 0x2b, 0xcd, 0x02, 0xf7, 0x80, 0x39, + 0xd6, 0x64, 0x27, 0x34, 0x13, 0x7f, 0x85, 0xfd, 0x73, 0xa3, 0xfa, 0x87, 0xe6, 0x7f, 0xa9, 0xfb, 0x09, 0xdc, 0xbe, + 0xc8, 0x82, 0xc4, 0x3e, 0xf0, 0x1b, 0x76, 0xcf, 0x0d, 0xdb, 0xec, 0x99, 0x29, 0xfb, 0x44, 0xa9, 0xf1, 0x23, 0xa5, + 0xae, 0x2d, 0xc3, 0x4a, 0xe6, 0xee, 0xcb, 0x08, 0x1c, 0x0e, 0xc8, 0x4f, 0x77, 0x88, 0x83, 0xd0, 0xba, 0xc9, 0x6a, + 0xae, 0x28, 0xe7, 0x42, 0x5b, 0x66, 0x5e, 0x0e, 0x2c, 0x66, 0x29, 0x85, 0xc6, 0x02, 0x00, 0xc1, 0xa4, 0xd0, 0xda, + 0x7b, 0x19, 0x40, 0x4e, 0xd0, 0xf0, 0xc7, 0xe6, 0xaa, 0x28, 0x6b, 0xd9, 0x92, 0x10, 0x65, 0xbb, 0x1e, 0x5e, 0x22, + 0x64, 0x5a, 0xbf, 0x7f, 0x4e, 0x24, 0x6b, 0x93, 0xea, 0xaa, 0x46, 0x4b, 0x40, 0x45, 0x96, 0x80, 0x89, 0x5f, 0x69, + 0x3e, 0x01, 0x78, 0xd2, 0xf1, 0xa0, 0x7a, 0xca, 0x6b, 0x26, 0x88, 0x6c, 0xa3, 0xf2, 0x27, 0xc5, 0x35, 0x92, 0x11, + 0x14, 0x4f, 0x6b, 0x95, 0xb1, 0x30, 0xcc, 0x03, 0x05, 0xe4, 0xdd, 0xbb, 0x53, 0xdf, 0xda, 0x1f, 0x3b, 0xf6, 0x6c, + 0xad, 0x42, 0x2d, 0xd4, 0x14, 0x2e, 0x39, 0x44, 0x57, 0x90, 0x81, 0x42, 0xc6, 0x93, 0xd7, 0x83, 0xcb, 0x49, 0x74, + 0xc5, 0x05, 0x3a, 0xe3, 0xeb, 0x9b, 0x6e, 0x3a, 0x8b, 0x9e, 0x56, 0xf3, 0x09, 0x29, 0xc9, 0x0e, 0x87, 0x6c, 0x54, + 0xd5, 0xc5, 0x7a, 0x1a, 0xca, 0x9f, 0x1e, 0x82, 0xaf, 0x17, 0xd4, 0x6b, 0xb2, 0x4a, 0xf5, 0x53, 0xaa, 0x94, 0x17, + 0x0d, 0x2f, 0xfd, 0xa7, 0x95, 0xdc, 0xf7, 0x80, 0xb4, 0x96, 0x97, 0x5c, 0xbe, 0x1f, 0x21, 0xc6, 0x88, 0x1f, 0x78, + 0x25, 0x8f, 0x58, 0xa8, 0xa6, 0x70, 0xcd, 0x23, 0x04, 0x79, 0xcb, 0x74, 0xf0, 0xb7, 0x9e, 0x38, 0xdd, 0x9f, 0x28, + 0xed, 0xe2, 0x0b, 0x8b, 0xba, 0x27, 0x6b, 0xeb, 0x06, 0xe4, 0x60, 0xc3, 0x74, 0x51, 0x90, 0x6d, 0x4a, 0x23, 0x68, + 0xa3, 0xe5, 0xc0, 0x86, 0x53, 0xa9, 0x0d, 0x67, 0xae, 0x21, 0xb8, 0xcf, 0xcf, 0xd3, 0xd1, 0x02, 0x3e, 0xa4, 0xba, + 0xbd, 0xc4, 0xcf, 0x87, 0x0d, 0x8f, 0x80, 0xcc, 0x8e, 0xf8, 0xcc, 0x26, 0x92, 0x4e, 0xea, 0x5c, 0x01, 0xbb, 0x9d, + 0xbd, 0x03, 0x39, 0x62, 0xe6, 0xbe, 0x42, 0xf5, 0x2d, 0x1a, 0x70, 0x65, 0xac, 0x7d, 0x4d, 0x32, 0x16, 0x5e, 0x95, + 0xd3, 0x70, 0x00, 0x30, 0x74, 0x19, 0x7d, 0x6d, 0xb9, 0xc9, 0xb2, 0x9f, 0x0b, 0x08, 0x82, 0x28, 0x89, 0xc7, 0x07, + 0xbc, 0x2f, 0xab, 0xa1, 0x46, 0xc9, 0xc7, 0xb2, 0x91, 0x4a, 0xaf, 0x44, 0x7f, 0x37, 0xe6, 0x12, 0x03, 0xbe, 0xab, + 0xda, 0x82, 0xc2, 0x79, 0x7e, 0x38, 0x9c, 0xe7, 0x23, 0xe3, 0x59, 0x06, 0xaa, 0x95, 0x69, 0x1d, 0xc4, 0x66, 0xbe, + 0x58, 0xf8, 0x8b, 0x9d, 0x93, 0x88, 0x28, 0x08, 0xec, 0x48, 0x78, 0x10, 0xa9, 0x5f, 0x55, 0x9e, 0xee, 0x54, 0x9f, + 0xed, 0x17, 0x36, 0x91, 0x5e, 0x50, 0x32, 0xf9, 0x24, 0xd8, 0xab, 0xfe, 0x0e, 0xc2, 0x86, 0xf0, 0xe6, 0x55, 0xaf, + 0xb3, 0x4c, 0xcd, 0x4a, 0x90, 0x30, 0x63, 0x8e, 0xe0, 0x71, 0xd8, 0x69, 0x6c, 0xc3, 0x63, 0x0b, 0x8e, 0xce, 0x5b, + 0xb3, 0x3b, 0xb6, 0x62, 0xb7, 0xaa, 0x4e, 0x0b, 0x1e, 0x4e, 0x87, 0x97, 0x01, 0xae, 0xbe, 0xf5, 0x39, 0xe7, 0x77, + 0x74, 0x82, 0xad, 0x07, 0x3c, 0x9a, 0x88, 0xd9, 0xfa, 0x69, 0xa4, 0x16, 0xcf, 0x7a, 0xc8, 0x17, 0xb4, 0xfe, 0xc4, + 0xec, 0xce, 0x24, 0xdf, 0x0d, 0xf8, 0x62, 0xb2, 0x7e, 0x1a, 0xc1, 0xab, 0x4f, 0xc1, 0x8a, 0x91, 0x39, 0xb3, 0x6c, + 0xfd, 0x34, 0xc2, 0x31, 0xbb, 0x7b, 0x1a, 0xd1, 0xa8, 0xad, 0xe4, 0xbe, 0x74, 0xdb, 0x80, 0xb0, 0x72, 0xcb, 0x62, + 0x78, 0x0d, 0xc4, 0x33, 0x6d, 0x24, 0x5d, 0x4b, 0x43, 0x6f, 0xcc, 0xc3, 0x69, 0x1c, 0xac, 0xa9, 0x15, 0xf2, 0xcc, + 0x10, 0xb3, 0xf8, 0x69, 0x34, 0x67, 0x2b, 0xac, 0xc8, 0x86, 0xc7, 0x83, 0xcb, 0xc9, 0xe6, 0x8a, 0xaf, 0x81, 0xfc, + 0x6c, 0xb2, 0x31, 0x5b, 0xd4, 0x2d, 0x17, 0xb3, 0xcd, 0xd3, 0x68, 0x3e, 0x59, 0x41, 0xcf, 0xda, 0x03, 0xe6, 0xbd, + 0x01, 0x11, 0x4a, 0x42, 0x6a, 0xca, 0x4d, 0xaf, 0xc7, 0xd6, 0xe3, 0xe0, 0x8e, 0xad, 0x2f, 0x83, 0x5b, 0xb6, 0x1e, + 0x03, 0x11, 0x07, 0xf5, 0xbb, 0xb7, 0x81, 0xc5, 0x17, 0xb1, 0xf5, 0xa5, 0x49, 0xdb, 0x3c, 0x8d, 0x98, 0x3b, 0x38, + 0x0d, 0x5c, 0xb0, 0x36, 0x99, 0xb7, 0x62, 0x70, 0x09, 0x59, 0x7a, 0x31, 0xdb, 0x0c, 0x2f, 0xd9, 0x7a, 0x84, 0x53, + 0x3d, 0xf1, 0xd9, 0x1d, 0xbf, 0x65, 0x09, 0x5f, 0x35, 0xf1, 0xd5, 0x06, 0x34, 0xa2, 0x47, 0x19, 0xf4, 0x15, 0xd4, + 0xcc, 0x9c, 0x57, 0x16, 0x46, 0xe5, 0xbe, 0x05, 0x07, 0x14, 0xa4, 0x6d, 0x80, 0x20, 0x89, 0x67, 0xf7, 0x2a, 0x5c, + 0xdf, 0x48, 0x61, 0xc0, 0x4d, 0x60, 0x06, 0x0c, 0x4c, 0x3f, 0x83, 0x1f, 0x56, 0xba, 0x44, 0x88, 0xb3, 0x9f, 0x52, + 0x92, 0xcc, 0xf3, 0xd7, 0x22, 0xcd, 0xdd, 0xc2, 0x75, 0x0a, 0xb3, 0xa2, 0x40, 0xf5, 0x53, 0x52, 0x1a, 0x58, 0xa8, + 0x44, 0xa6, 0x52, 0xf0, 0xcb, 0xe6, 0x3c, 0xca, 0x8e, 0xd1, 0xb9, 0xce, 0x2f, 0x27, 0xce, 0xe9, 0xa4, 0xef, 0x3f, + 0x70, 0x0c, 0x5b, 0xc8, 0xc0, 0x85, 0x3f, 0xf5, 0x84, 0x71, 0x6a, 0x05, 0x62, 0x2a, 0x79, 0xf6, 0x14, 0x3e, 0x13, + 0x5a, 0x1d, 0x5d, 0xf8, 0x7e, 0x50, 0x68, 0x93, 0x74, 0x0b, 0x92, 0x14, 0x3c, 0x45, 0xcf, 0x39, 0x6f, 0x03, 0x95, + 0x62, 0x44, 0x0b, 0x22, 0x6d, 0x2d, 0x33, 0x07, 0x69, 0x4b, 0xf3, 0x5d, 0x13, 0x3f, 0x87, 0x05, 0x5c, 0x44, 0x0b, + 0x5b, 0xc3, 0xa3, 0x2a, 0x56, 0xee, 0x4d, 0x9e, 0x23, 0x9c, 0xd1, 0xa5, 0x4c, 0x00, 0x5c, 0xef, 0xd7, 0x61, 0xad, + 0xf0, 0x8a, 0x9a, 0x45, 0x5e, 0xd4, 0xf4, 0xc9, 0x16, 0xb8, 0x8f, 0x45, 0x89, 0x02, 0x67, 0x2d, 0x18, 0xb0, 0x15, + 0x96, 0xec, 0xa4, 0xb0, 0x29, 0x5a, 0x42, 0x6f, 0x8f, 0x9f, 0x0e, 0x6a, 0x26, 0x03, 0x68, 0x02, 0x68, 0x3c, 0xfe, + 0x05, 0xa0, 0xa6, 0x37, 0xb5, 0x58, 0x57, 0x41, 0xa9, 0x94, 0x9b, 0xf0, 0x33, 0x30, 0xcc, 0xf0, 0x43, 0x21, 0xb7, + 0x89, 0x12, 0x39, 0x3f, 0x16, 0xa5, 0x58, 0x96, 0xa2, 0x4a, 0xda, 0x0d, 0x05, 0x8f, 0x08, 0xb7, 0x41, 0x63, 0xe6, + 0xf6, 0x44, 0x17, 0xad, 0x08, 0xe5, 0xd8, 0xac, 0x63, 0xa4, 0x51, 0x66, 0x27, 0xbb, 0x4e, 0x16, 0xda, 0xef, 0xab, + 0x1c, 0xb2, 0x0e, 0x58, 0x23, 0xf9, 0x7a, 0xcd, 0xa1, 0xdb, 0x46, 0x79, 0xf1, 0xe0, 0xf9, 0x0a, 0x4e, 0x73, 0x3c, + 0xb1, 0xbb, 0x5e, 0x77, 0x8a, 0x44, 0xbc, 0xc2, 0x49, 0x95, 0x8f, 0x64, 0xe1, 0xb8, 0x73, 0xa7, 0xb5, 0x58, 0x55, + 0x2e, 0xeb, 0xa9, 0xc5, 0x11, 0x81, 0x4f, 0xe5, 0xd1, 0x5e, 0x68, 0x5b, 0x14, 0x0b, 0x61, 0xf4, 0xe8, 0x84, 0x9f, + 0x94, 0xc0, 0xfa, 0x3a, 0x1c, 0x96, 0x7e, 0xc4, 0xd1, 0xef, 0x34, 0x1a, 0x2d, 0x08, 0x69, 0x78, 0xea, 0x45, 0xa3, + 0x45, 0x5d, 0xd4, 0x61, 0x76, 0x9d, 0xeb, 0x81, 0xc2, 0x30, 0x02, 0xf5, 0x83, 0xab, 0x0c, 0x3e, 0x8b, 0x10, 0x35, + 0x0f, 0x4c, 0xb3, 0x21, 0x1c, 0x75, 0x81, 0x87, 0x56, 0xd0, 0x62, 0x66, 0x3e, 0x0a, 0x31, 0x7c, 0x48, 0x17, 0xe7, + 0x4f, 0xc8, 0xca, 0x07, 0xd8, 0x1d, 0xba, 0x0b, 0xe5, 0x9c, 0xa9, 0x18, 0xe0, 0x47, 0x01, 0xf9, 0x28, 0x01, 0x37, + 0x03, 0x64, 0x8f, 0x2c, 0x01, 0xc4, 0x8a, 0xd1, 0xd1, 0xe4, 0x73, 0xdf, 0x8b, 0x14, 0xbc, 0xb3, 0xcf, 0x72, 0x35, + 0x61, 0x28, 0x7c, 0x62, 0xa0, 0x9b, 0xdf, 0xf8, 0xed, 0x79, 0x0b, 0x46, 0x76, 0x49, 0x8a, 0xd7, 0x9a, 0xe1, 0x7e, + 0x03, 0x6e, 0x47, 0x40, 0x59, 0x53, 0x1d, 0x93, 0x6c, 0xd3, 0x10, 0xc9, 0x80, 0x19, 0x31, 0x22, 0xa8, 0x2c, 0x17, + 0xfe, 0x77, 0x2f, 0x8b, 0x02, 0x07, 0x70, 0x35, 0x93, 0xc1, 0x6b, 0x17, 0x46, 0x05, 0xc0, 0x39, 0x0d, 0x9d, 0xd2, + 0x5e, 0x55, 0x1d, 0x92, 0x55, 0xf3, 0x83, 0xd9, 0xbc, 0x69, 0x98, 0x18, 0x11, 0x44, 0x17, 0xe1, 0x04, 0xd3, 0x2b, + 0xd2, 0xd7, 0x4a, 0x4e, 0x47, 0xab, 0x8e, 0xd6, 0x12, 0x13, 0x73, 0x45, 0xf1, 0xd7, 0x80, 0xc7, 0x0d, 0x5e, 0x9d, + 0xa4, 0xe9, 0x44, 0xf5, 0xe8, 0xf1, 0xeb, 0x34, 0x9d, 0x94, 0xb8, 0x2b, 0xfc, 0x06, 0x5c, 0x34, 0xdb, 0x7c, 0xe8, + 0xc7, 0x2f, 0x28, 0xe2, 0xa2, 0x06, 0x57, 0xde, 0xa9, 0xbe, 0x52, 0x7d, 0x04, 0xb5, 0xf0, 0xc4, 0xc8, 0x5a, 0x78, + 0x72, 0xc9, 0x5a, 0x0b, 0x82, 0x99, 0xcd, 0x81, 0x0b, 0xf9, 0x95, 0x52, 0xc4, 0x9b, 0x48, 0xa8, 0xc5, 0xa0, 0xf5, + 0x98, 0x39, 0xab, 0x46, 0x0b, 0x95, 0x19, 0xa1, 0x7d, 0x5b, 0x8b, 0xce, 0x6f, 0xe4, 0xa7, 0x3c, 0xb5, 0x2f, 0xdb, + 0xe3, 0x7c, 0xbc, 0x47, 0x77, 0xd5, 0x59, 0x66, 0x52, 0xc6, 0x27, 0xb3, 0x04, 0x85, 0xbb, 0x04, 0x1b, 0x90, 0x64, + 0xbf, 0xd5, 0x01, 0x32, 0x6a, 0xaf, 0xfd, 0xae, 0xb3, 0x7c, 0x75, 0xb3, 0x35, 0x14, 0x95, 0x5a, 0x49, 0x8a, 0x83, + 0x0c, 0xd7, 0x6d, 0xe5, 0xc3, 0xc5, 0x05, 0xf4, 0x8c, 0x91, 0xc8, 0x3c, 0x7f, 0x22, 0x5f, 0x82, 0x73, 0xc6, 0x59, + 0x21, 0x30, 0x61, 0xac, 0xde, 0xb5, 0x96, 0x4a, 0x43, 0x8a, 0xb1, 0xa3, 0x51, 0x96, 0x55, 0x96, 0x2e, 0xb3, 0xb5, + 0x84, 0x2d, 0xab, 0xc8, 0x2d, 0x6c, 0x99, 0xc9, 0x6a, 0x7e, 0xa8, 0xb8, 0x83, 0xf2, 0xcd, 0xd6, 0x19, 0xdf, 0x4b, + 0x64, 0xef, 0x36, 0x50, 0xc2, 0xf5, 0xe8, 0x3f, 0x90, 0x7e, 0x9b, 0x61, 0x9c, 0x72, 0x5b, 0x49, 0x0b, 0x70, 0xfa, + 0x87, 0xc3, 0x87, 0x0a, 0x83, 0x06, 0x47, 0x18, 0x47, 0xd6, 0xef, 0xdf, 0x56, 0x5e, 0x8d, 0x89, 0x3a, 0x3e, 0xab, + 0xdf, 0xaf, 0xe8, 0xe1, 0xb4, 0x1a, 0xad, 0xd2, 0x2d, 0xb2, 0x13, 0xda, 0x58, 0xf9, 0x41, 0xad, 0x80, 0xd9, 0x5b, + 0x9f, 0x4f, 0x07, 0xa0, 0x63, 0x01, 0x12, 0xcd, 0x66, 0x22, 0x31, 0x27, 0xdd, 0x93, 0xf0, 0xf8, 0xc0, 0x02, 0x07, + 0x98, 0x8a, 0xff, 0x43, 0x78, 0x33, 0xb0, 0x41, 0xa3, 0x44, 0x5f, 0xa3, 0xab, 0xda, 0xdc, 0xe8, 0x78, 0xe9, 0x29, + 0x24, 0xb2, 0x82, 0x55, 0x73, 0x5f, 0x6e, 0xe0, 0xb4, 0x87, 0x9a, 0x43, 0x65, 0x09, 0xfe, 0xf6, 0xcb, 0xfc, 0x70, + 0x58, 0x67, 0x50, 0xd8, 0x6e, 0x2d, 0xb4, 0x37, 0x66, 0xa9, 0x86, 0x8a, 0x70, 0xd0, 0xf9, 0x4a, 0xcc, 0xea, 0x11, + 0xfd, 0x3d, 0x3f, 0x1c, 0x56, 0x04, 0x06, 0x1c, 0x96, 0x32, 0x13, 0x2d, 0x14, 0x4b, 0xeb, 0x6c, 0x46, 0x75, 0xe0, + 0x81, 0x89, 0x39, 0x0b, 0x77, 0x00, 0xda, 0xa4, 0x56, 0x81, 0x5e, 0x45, 0xf4, 0x13, 0xf7, 0x6b, 0xfb, 0xf5, 0x7a, + 0x64, 0x96, 0x8e, 0xdc, 0x18, 0x0b, 0x00, 0x0e, 0x3c, 0xaf, 0x49, 0x9e, 0x93, 0xaf, 0xa1, 0xdd, 0x93, 0x0b, 0xf9, + 0x13, 0x94, 0x2d, 0x3c, 0x57, 0x4d, 0x2b, 0x8b, 0x15, 0x57, 0xd5, 0xab, 0x0b, 0x5e, 0x99, 0x4c, 0xab, 0xb4, 0x12, + 0x95, 0x12, 0x0c, 0xa8, 0x4b, 0xbc, 0xd6, 0x34, 0xa3, 0xd4, 0x46, 0x9d, 0x89, 0x1a, 0xb0, 0xc1, 0x7e, 0xaa, 0x36, + 0x3a, 0x39, 0x97, 0xcf, 0x2f, 0x8d, 0xc3, 0xa7, 0x5d, 0xbd, 0x99, 0xa9, 0x1c, 0xf8, 0x6b, 0xe5, 0x43, 0xab, 0xc7, + 0x40, 0x07, 0xe4, 0xf4, 0xc7, 0xb0, 0x98, 0xd8, 0x1d, 0x9a, 0xb7, 0xbb, 0xcb, 0xea, 0x22, 0xbd, 0xd3, 0x94, 0xcc, + 0xea, 0x2d, 0x9f, 0x59, 0x3d, 0x3a, 0xe0, 0xc5, 0x63, 0xbd, 0x57, 0x98, 0x49, 0x04, 0x17, 0x43, 0x35, 0x89, 0xec, + 0x0e, 0xb4, 0xe6, 0x51, 0xc5, 0x04, 0xf8, 0x41, 0xa9, 0x35, 0xbd, 0xb7, 0xbb, 0x42, 0x9d, 0x52, 0x78, 0xdc, 0x5a, + 0xf2, 0x03, 0x73, 0xa7, 0x5d, 0xeb, 0x7c, 0x3c, 0xbf, 0xf4, 0xfd, 0x46, 0x9e, 0xd0, 0x66, 0x67, 0x72, 0xfa, 0x27, + 0x6f, 0xf5, 0x0f, 0x53, 0x7d, 0x0b, 0xdd, 0x09, 0xfa, 0x0c, 0x5d, 0x55, 0xdd, 0x95, 0xd8, 0xc2, 0x50, 0x4f, 0x2c, + 0xf2, 0x42, 0x9e, 0xb4, 0xc6, 0x8e, 0x83, 0xbd, 0x01, 0x4e, 0xfc, 0xf2, 0x70, 0x10, 0x57, 0xb9, 0xcf, 0xce, 0xbb, + 0x46, 0x56, 0x0e, 0x60, 0x05, 0x51, 0x30, 0x6e, 0xcd, 0xc7, 0x36, 0x48, 0x97, 0xb8, 0x1a, 0x1f, 0xbf, 0xa1, 0x58, + 0x26, 0x9b, 0x88, 0x8b, 0x8b, 0xfc, 0xe9, 0x73, 0x20, 0x2d, 0xeb, 0xf7, 0xa3, 0xeb, 0xcb, 0xe9, 0xf3, 0x61, 0x14, + 0x80, 0x63, 0x97, 0xbd, 0xbc, 0x8c, 0xf9, 0xea, 0x92, 0x59, 0xa6, 0xb0, 0xc8, 0x37, 0x03, 0xaa, 0x4b, 0x56, 0x4b, + 0xd7, 0x2b, 0xc0, 0xd2, 0xe5, 0x37, 0x0f, 0x61, 0x6a, 0x40, 0x23, 0x6b, 0xee, 0x4e, 0x73, 0x2d, 0x50, 0xea, 0x79, + 0x3f, 0x33, 0xe4, 0xeb, 0x32, 0xe8, 0x0a, 0xd2, 0x3d, 0x8f, 0x48, 0x2f, 0xf7, 0xd2, 0xe9, 0x7e, 0x5f, 0x0a, 0xb0, + 0xd4, 0x97, 0xe2, 0x0b, 0x28, 0x2c, 0x1a, 0xdf, 0x08, 0xd0, 0xd6, 0x50, 0x4d, 0x7b, 0xa5, 0xa8, 0x7a, 0x41, 0xaf, + 0x14, 0x5f, 0x7a, 0x7a, 0xa8, 0xcc, 0x97, 0xa5, 0xa3, 0xff, 0x09, 0x35, 0x17, 0x9c, 0x10, 0x33, 0x31, 0x07, 0x50, + 0x09, 0xda, 0xf8, 0x56, 0x47, 0x1b, 0x9f, 0xea, 0x55, 0xdc, 0xf4, 0x79, 0x6d, 0x2d, 0x73, 0x42, 0xd8, 0x74, 0x2f, + 0x01, 0x2a, 0xf2, 0x4a, 0x78, 0x04, 0xcb, 0x2f, 0x7f, 0xc8, 0xd3, 0x15, 0xa2, 0x75, 0xdc, 0xb3, 0xcc, 0xa5, 0xb1, + 0x7f, 0x63, 0x30, 0x7d, 0x7d, 0xbb, 0x2d, 0xf2, 0x53, 0x13, 0x13, 0xd6, 0x63, 0x45, 0xdf, 0xbc, 0x0f, 0x57, 0x02, + 0x05, 0x0e, 0x25, 0x12, 0xdb, 0x54, 0xa1, 0x88, 0x07, 0x49, 0x9f, 0x2e, 0x5a, 0x9f, 0x06, 0x98, 0x5a, 0xcb, 0x81, + 0x39, 0x84, 0xab, 0xb8, 0xf0, 0xd1, 0xd3, 0xb7, 0x98, 0x85, 0xf3, 0x89, 0xf7, 0xc9, 0x2b, 0x46, 0xe6, 0xe3, 0x3e, + 0x2a, 0x95, 0xf4, 0xcf, 0xc3, 0x61, 0x56, 0xcd, 0x7d, 0x87, 0x3e, 0xd2, 0x43, 0x95, 0x0b, 0xca, 0xde, 0x18, 0x93, + 0x08, 0x94, 0xc6, 0x78, 0x1f, 0x07, 0xc7, 0x79, 0x9f, 0x06, 0x90, 0xda, 0x27, 0x3e, 0x90, 0x92, 0xc3, 0x73, 0x8e, + 0x39, 0xa1, 0xb4, 0x22, 0xac, 0xe2, 0x8b, 0x0c, 0xe5, 0xba, 0x53, 0x0a, 0x26, 0x39, 0x24, 0x18, 0xfe, 0xaa, 0x79, + 0x13, 0x2b, 0x10, 0x76, 0xcd, 0xbc, 0x1a, 0x3d, 0xa9, 0x92, 0xb0, 0x14, 0x70, 0x54, 0x66, 0x9e, 0x61, 0x6f, 0x78, + 0x62, 0x18, 0x39, 0x58, 0xee, 0x8f, 0xea, 0x44, 0xe4, 0x1e, 0x5d, 0x60, 0x54, 0x16, 0x9e, 0x37, 0x74, 0xa5, 0x41, + 0x25, 0xd9, 0xf1, 0x57, 0x5c, 0x03, 0x6a, 0x6b, 0x8c, 0x18, 0x0a, 0x18, 0x05, 0xaf, 0xed, 0x0f, 0x21, 0x8b, 0xb2, + 0xf5, 0x1b, 0x1c, 0xf3, 0x59, 0xc9, 0x5d, 0xef, 0x70, 0x16, 0x5a, 0x42, 0x9e, 0xdc, 0x31, 0x48, 0xd3, 0x58, 0x1a, + 0x01, 0x27, 0x22, 0xd9, 0xc6, 0x52, 0x38, 0x02, 0x08, 0x08, 0x74, 0x53, 0x66, 0x18, 0xd3, 0xc1, 0xc8, 0xf3, 0xa4, + 0x67, 0xbc, 0x57, 0xe1, 0x29, 0xa4, 0xc9, 0xf6, 0xf5, 0xfc, 0xbd, 0x11, 0x64, 0xe5, 0x96, 0x73, 0x3c, 0x2c, 0xbe, + 0x71, 0xf6, 0x55, 0x4e, 0x9e, 0x62, 0x96, 0x91, 0xde, 0x29, 0xe6, 0x05, 0xfc, 0xa9, 0x2c, 0xf5, 0x39, 0x4a, 0x6f, + 0x99, 0x4f, 0x56, 0x91, 0x74, 0xe9, 0x6d, 0xfa, 0xfd, 0x78, 0xa4, 0x0e, 0x35, 0x7f, 0x1f, 0x8f, 0xe4, 0x19, 0xb6, + 0x61, 0x09, 0x0b, 0xad, 0x82, 0x31, 0x80, 0x24, 0x36, 0x22, 0x1a, 0x8c, 0xf6, 0xe6, 0x70, 0x38, 0xdf, 0x98, 0xb3, + 0x64, 0x0f, 0xae, 0xaf, 0x3c, 0x31, 0xef, 0xc0, 0x97, 0x79, 0x4c, 0x10, 0xb1, 0x99, 0xb7, 0x61, 0x35, 0x78, 0xb0, + 0x83, 0xeb, 0x23, 0xb6, 0x28, 0xd6, 0x3a, 0x96, 0xca, 0x3a, 0x38, 0xad, 0x63, 0xd3, 0x8c, 0x94, 0x22, 0xfb, 0x1c, + 0xfb, 0x7b, 0x37, 0xb8, 0xba, 0x36, 0x06, 0xb5, 0xc6, 0x1d, 0xe6, 0xce, 0xa9, 0x80, 0x7a, 0x4c, 0x57, 0x50, 0x3d, + 0xab, 0xc8, 0x97, 0xdf, 0xda, 0x39, 0x20, 0x68, 0x04, 0x02, 0x17, 0x0d, 0xb4, 0x6a, 0x97, 0x72, 0xde, 0x05, 0x84, + 0xf8, 0x2e, 0x05, 0x7d, 0x3a, 0x83, 0x4d, 0x6c, 0x3e, 0x81, 0x58, 0x34, 0xdd, 0xe7, 0x5a, 0x33, 0x5f, 0x8c, 0x68, + 0x67, 0xd6, 0xdd, 0x22, 0xb7, 0x5a, 0x88, 0x64, 0xf4, 0x6c, 0x33, 0xe1, 0xa2, 0x43, 0x39, 0x23, 0x01, 0x13, 0xb4, + 0xb6, 0x52, 0xf2, 0xb9, 0xee, 0x75, 0x82, 0xf6, 0x40, 0xd2, 0xba, 0x7f, 0xb3, 0xe8, 0x8c, 0x92, 0x93, 0xeb, 0x4d, + 0xce, 0x20, 0x05, 0x0b, 0xb6, 0x97, 0x39, 0xe1, 0x06, 0xf8, 0xc4, 0x66, 0xc9, 0x69, 0x1a, 0xe4, 0xb1, 0x30, 0x1e, + 0x79, 0x6d, 0x7e, 0x59, 0x40, 0x87, 0x92, 0x45, 0x23, 0xc4, 0x03, 0xec, 0x1c, 0x92, 0xab, 0x02, 0x75, 0xd3, 0x40, + 0x57, 0xae, 0x9c, 0x29, 0xa6, 0xc0, 0x85, 0x50, 0x10, 0xb5, 0xa3, 0x93, 0xa8, 0x9c, 0xf7, 0x49, 0x75, 0x99, 0x4f, + 0x0b, 0x69, 0x1a, 0xc8, 0xa7, 0x95, 0x63, 0x1e, 0xd8, 0xd9, 0xc6, 0x35, 0x81, 0x81, 0x4e, 0xed, 0x6b, 0x51, 0xce, + 0xb1, 0x8a, 0xe8, 0x7d, 0xfe, 0xb1, 0xb2, 0xa7, 0x0f, 0x22, 0x6c, 0x54, 0xa0, 0xb1, 0x94, 0x18, 0x1b, 0x39, 0xfe, + 0x2d, 0x51, 0x36, 0x64, 0x08, 0x08, 0x21, 0x6d, 0xe4, 0xf4, 0xc3, 0xfa, 0xf2, 0x36, 0xd3, 0xfe, 0x9f, 0x24, 0x7e, + 0x1b, 0xec, 0xe5, 0xd4, 0x9f, 0x7a, 0xc4, 0xe3, 0xb5, 0x46, 0x8f, 0x29, 0xe9, 0x36, 0xc8, 0x53, 0xe5, 0x29, 0x48, + 0x26, 0x8c, 0x25, 0x04, 0x8b, 0x72, 0xc1, 0x73, 0x5e, 0x71, 0x09, 0xf7, 0x51, 0xcb, 0x8a, 0x08, 0x55, 0x89, 0x9c, + 0x3e, 0x5f, 0x01, 0xcf, 0x04, 0x04, 0x3a, 0xc6, 0x48, 0xa3, 0x0a, 0xbe, 0x04, 0xc6, 0x3a, 0x50, 0x76, 0x9a, 0x91, + 0xe0, 0xb2, 0x7b, 0x83, 0x44, 0xa9, 0xaf, 0x49, 0x49, 0xfa, 0x4e, 0xd4, 0x78, 0x25, 0x56, 0x11, 0x09, 0x64, 0xa8, + 0x21, 0x62, 0x55, 0x3d, 0x75, 0xaf, 0x8a, 0xc9, 0x60, 0x50, 0xf9, 0x72, 0x7a, 0xe2, 0x0d, 0x0d, 0x95, 0x77, 0x5d, + 0xd1, 0x4e, 0x4f, 0xb4, 0x52, 0xde, 0x42, 0x5a, 0x82, 0xa6, 0x61, 0xa4, 0x39, 0x94, 0xba, 0x92, 0xee, 0xc6, 0x20, + 0xbe, 0x64, 0xa2, 0x67, 0x3b, 0xb5, 0xa3, 0xb4, 0x25, 0xed, 0x21, 0xa4, 0xe7, 0x2e, 0xf9, 0x98, 0x85, 0x5c, 0xdd, + 0x29, 0x27, 0xe5, 0x55, 0x88, 0x4e, 0xee, 0x7b, 0x0c, 0x89, 0x40, 0x9f, 0x73, 0x0c, 0xeb, 0xa2, 0xa1, 0xce, 0x61, + 0x85, 0x98, 0x2d, 0x94, 0x30, 0x5f, 0x32, 0x9e, 0x4a, 0x06, 0x0d, 0x80, 0x0c, 0xf8, 0xe2, 0x65, 0x60, 0xf9, 0x2b, + 0x88, 0x1f, 0x6d, 0x7c, 0x38, 0xfc, 0x59, 0x53, 0x88, 0xed, 0x9f, 0xb0, 0x19, 0xc2, 0xa3, 0x7a, 0xc0, 0x33, 0xdf, + 0xc4, 0x09, 0x5a, 0x01, 0x49, 0x99, 0x1d, 0x4d, 0x64, 0xaf, 0x7a, 0x08, 0xa7, 0xb2, 0x02, 0x75, 0x94, 0x75, 0x56, + 0xc2, 0x8f, 0x30, 0xd5, 0xad, 0xc4, 0x5a, 0xa0, 0xcd, 0xd5, 0x8a, 0xb5, 0x00, 0x0e, 0xfc, 0x1c, 0x82, 0x27, 0xf2, + 0x39, 0xb8, 0x18, 0x14, 0xe0, 0x73, 0x00, 0xbc, 0xc8, 0x5d, 0x78, 0x30, 0x8f, 0x2c, 0xab, 0x11, 0x86, 0xa3, 0x8a, + 0x58, 0xbf, 0x66, 0x3b, 0xf2, 0x81, 0xdb, 0x31, 0x3e, 0xd7, 0x1e, 0x4b, 0x96, 0x83, 0x51, 0xe6, 0x5e, 0x2d, 0xd1, + 0xf3, 0x26, 0x8d, 0x9b, 0xd1, 0x93, 0x7d, 0x2d, 0xff, 0x17, 0xf4, 0x32, 0xe8, 0x6f, 0xe1, 0x96, 0xd7, 0xfc, 0x6e, + 0x41, 0xa4, 0x99, 0x5e, 0x41, 0xa4, 0x8c, 0x1a, 0x91, 0x31, 0x84, 0x4d, 0xaa, 0x9b, 0xdb, 0xa4, 0xba, 0x10, 0xf0, + 0x74, 0x44, 0xaa, 0x6b, 0x21, 0x6d, 0xe4, 0xd3, 0x3a, 0x90, 0xb1, 0x48, 0xef, 0x7f, 0xfc, 0xcb, 0x8b, 0xcf, 0x6f, + 0x7f, 0xf9, 0x71, 0xf1, 0xf6, 0xfd, 0x9b, 0xb7, 0xef, 0xdf, 0x7e, 0xfe, 0x8d, 0x20, 0x3c, 0xa6, 0x42, 0x65, 0xf8, + 0xf8, 0xe1, 0xe6, 0xad, 0x93, 0xc1, 0xf6, 0x66, 0xc8, 0xda, 0x37, 0x72, 0x30, 0x04, 0x22, 0x1b, 0x84, 0x0c, 0xb2, + 0x53, 0x32, 0xc7, 0x4c, 0xcc, 0x31, 0xf6, 0x4e, 0x60, 0xb2, 0x05, 0x9c, 0x63, 0x99, 0x97, 0x8c, 0xc8, 0x55, 0xa1, + 0xf5, 0x03, 0x5a, 0xf0, 0x0e, 0x5c, 0x64, 0xd2, 0xfc, 0xee, 0x17, 0x82, 0xd8, 0xa7, 0x95, 0x94, 0xfb, 0x6a, 0x5b, + 0xf3, 0x7c, 0x7b, 0xbf, 0x97, 0x70, 0xfe, 0x73, 0x69, 0x44, 0x2d, 0xc0, 0x01, 0xf8, 0x1c, 0xfe, 0xb8, 0xd2, 0x96, + 0x34, 0x99, 0x45, 0xfb, 0x19, 0x43, 0xd0, 0xa5, 0xc1, 0x07, 0xb1, 0x47, 0x5e, 0xea, 0x93, 0x85, 0x04, 0xee, 0x88, + 0xe1, 0xd3, 0x8a, 0xa0, 0x57, 0x8c, 0x28, 0x2e, 0xb9, 0x42, 0xa5, 0x94, 0xfc, 0x1b, 0x65, 0x17, 0x15, 0x72, 0x56, + 0xb0, 0x7b, 0x45, 0x8e, 0x8c, 0x1f, 0x04, 0x13, 0x5f, 0x0e, 0xee, 0xbf, 0xc4, 0x3b, 0x9c, 0x29, 0x8e, 0xe4, 0x84, + 0x3f, 0x64, 0x18, 0xd8, 0x9f, 0x83, 0xcf, 0xab, 0xc3, 0xbc, 0xbc, 0xd1, 0xa7, 0xdc, 0x92, 0x8f, 0x27, 0xcb, 0x2b, + 0x30, 0xd8, 0x2f, 0x55, 0x73, 0xd7, 0xbc, 0x9e, 0x2d, 0xe7, 0x6c, 0x3f, 0x8b, 0xe6, 0xc1, 0x1d, 0x9b, 0x65, 0xf3, + 0x60, 0xd5, 0xf0, 0x35, 0xbb, 0xe5, 0x6b, 0xab, 0x6a, 0x6b, 0xbb, 0x6a, 0x93, 0x0d, 0xbf, 0x05, 0x09, 0xe1, 0x26, + 0xf3, 0x80, 0xf7, 0xf8, 0xce, 0x67, 0x1b, 0x90, 0x68, 0x57, 0x6c, 0x03, 0x17, 0xb1, 0x35, 0xff, 0xb1, 0xf2, 0x36, + 0xac, 0x64, 0xe7, 0x63, 0x96, 0xe3, 0xfc, 0xf3, 0xe1, 0x01, 0xed, 0x85, 0xfa, 0xd9, 0xa5, 0x7a, 0x36, 0x51, 0x76, + 0xb3, 0xcd, 0x68, 0x71, 0x9f, 0x56, 0x9b, 0x30, 0x43, 0xcf, 0x72, 0xf8, 0x68, 0x2b, 0x05, 0x3f, 0xbd, 0xc0, 0x2f, + 0xd9, 0x51, 0x5b, 0x69, 0xdb, 0xae, 0x4a, 0x6c, 0x05, 0x2d, 0x8a, 0xac, 0x56, 0x78, 0x60, 0xce, 0xaf, 0x61, 0x01, + 0x63, 0xcf, 0x71, 0xce, 0x6b, 0x7f, 0x84, 0x8c, 0xf7, 0x0e, 0x00, 0x5a, 0xe6, 0x38, 0xc0, 0x23, 0x56, 0x8c, 0xa2, + 0xc1, 0x3b, 0xbf, 0x54, 0x56, 0x2b, 0xcd, 0x49, 0x68, 0x1b, 0xb1, 0x6a, 0x39, 0x52, 0x35, 0x23, 0xd2, 0x07, 0xe9, + 0x79, 0xdf, 0x23, 0xaa, 0xc1, 0x9e, 0xcc, 0xeb, 0xc0, 0x3e, 0xbd, 0x6a, 0xad, 0xea, 0xce, 0xef, 0xa9, 0xd2, 0x25, + 0x47, 0xb6, 0xfc, 0x74, 0x19, 0x3e, 0xa8, 0x3f, 0x25, 0xd7, 0x87, 0x02, 0x47, 0x78, 0xac, 0x02, 0xce, 0xd7, 0x2b, + 0xd1, 0xee, 0x44, 0xd8, 0x95, 0x4b, 0x40, 0x88, 0x2f, 0x69, 0x9a, 0xe3, 0x71, 0x44, 0x13, 0x11, 0x36, 0x31, 0xfa, + 0x0b, 0xbb, 0x0f, 0x25, 0x96, 0xf3, 0x5c, 0x83, 0x92, 0x4b, 0x06, 0xef, 0x49, 0x7b, 0x0d, 0x9a, 0xe5, 0x55, 0xa9, + 0xc9, 0x44, 0x0e, 0xca, 0x87, 0x43, 0x01, 0x7b, 0xa9, 0xf1, 0xd3, 0x84, 0x9f, 0xb0, 0xbc, 0xb5, 0xb7, 0xa6, 0x14, + 0x95, 0x34, 0x40, 0x05, 0x3e, 0x66, 0xf0, 0xbf, 0x3b, 0x43, 0x2c, 0x98, 0xa2, 0xe3, 0x87, 0x33, 0x31, 0xb7, 0x9e, + 0x5b, 0x65, 0x1d, 0x65, 0x6b, 0x94, 0x13, 0xf0, 0x6f, 0xa9, 0x8e, 0x93, 0x44, 0x38, 0xf5, 0x1e, 0x71, 0x51, 0xf7, + 0x72, 0x88, 0xba, 0x61, 0x6f, 0x2b, 0x1d, 0x6c, 0x39, 0x4d, 0x83, 0x23, 0xf1, 0x2b, 0xf5, 0xd9, 0x87, 0xcc, 0xe2, + 0x51, 0x47, 0x36, 0xa2, 0x24, 0x8d, 0x63, 0x91, 0xc3, 0xf6, 0xbe, 0x90, 0xfb, 0x7f, 0xbf, 0x0f, 0xe1, 0xa4, 0x55, + 0x90, 0x94, 0x9e, 0x40, 0x44, 0x38, 0x3a, 0xfc, 0x88, 0xf0, 0x44, 0xaa, 0x0a, 0x9f, 0xd4, 0x27, 0x6e, 0xcc, 0xee, + 0x85, 0x39, 0xaa, 0xb7, 0x00, 0xc3, 0x58, 0x6f, 0x2d, 0x42, 0x12, 0xad, 0x34, 0xa3, 0xad, 0x07, 0xc4, 0x88, 0x0f, + 0x6b, 0x8b, 0x0c, 0xc6, 0xda, 0x92, 0x48, 0x00, 0xbf, 0x23, 0x21, 0x43, 0xdb, 0x46, 0x60, 0xc6, 0xf0, 0x76, 0x56, + 0x5c, 0xba, 0x0e, 0xdb, 0x9c, 0xc3, 0x17, 0xb2, 0xd0, 0xac, 0x23, 0x4a, 0x13, 0x84, 0xfc, 0x03, 0x4e, 0x16, 0x0a, + 0xa3, 0x79, 0x7d, 0x94, 0x4e, 0x12, 0xeb, 0x87, 0xae, 0x52, 0xc1, 0x66, 0x73, 0x83, 0xfa, 0xb2, 0xa3, 0xe4, 0x57, + 0xe0, 0xa4, 0xe3, 0x24, 0x8b, 0x1c, 0x44, 0x2d, 0x2a, 0xe7, 0x26, 0x09, 0x4b, 0xbb, 0x3a, 0xd5, 0x66, 0xbd, 0x2e, + 0xca, 0xba, 0x7a, 0x2d, 0x22, 0x45, 0xef, 0xa3, 0x1e, 0x3d, 0x91, 0x90, 0x0a, 0xad, 0x4a, 0xed, 0xf2, 0x08, 0xdc, + 0x36, 0xb5, 0x62, 0x5b, 0x2e, 0x61, 0x89, 0x1a, 0xff, 0x19, 0xfa, 0x28, 0x17, 0x0f, 0x32, 0x40, 0xa3, 0xe3, 0xa9, + 0x79, 0xeb, 0x91, 0x57, 0x8e, 0xf2, 0x4b, 0xab, 0x4d, 0xfa, 0x15, 0x90, 0x19, 0xed, 0x1f, 0x2d, 0x25, 0x90, 0x19, + 0x98, 0x49, 0x4b, 0x43, 0x22, 0x47, 0x31, 0x4b, 0xf3, 0x3f, 0x70, 0xc5, 0x56, 0x88, 0x34, 0xac, 0xe6, 0x1e, 0x7f, + 0x51, 0x79, 0xb5, 0x5c, 0xcb, 0x4c, 0x73, 0xb3, 0xc4, 0xb1, 0x62, 0x71, 0x51, 0xaf, 0x2b, 0x91, 0x05, 0x42, 0x1c, + 0x61, 0x1a, 0xeb, 0xa9, 0x37, 0x4a, 0xab, 0x8f, 0x48, 0x28, 0xf3, 0x23, 0xf6, 0x76, 0xec, 0xf5, 0x20, 0x0b, 0x71, + 0x6c, 0x39, 0xd8, 0x6c, 0xbd, 0xcf, 0x65, 0x2a, 0xe2, 0xb3, 0xba, 0x38, 0xdb, 0x54, 0xe2, 0xac, 0x4e, 0xc4, 0xd9, + 0x0f, 0x90, 0xf3, 0x87, 0x33, 0x2a, 0xfa, 0xec, 0x21, 0xad, 0x93, 0x62, 0x53, 0xd3, 0x93, 0x37, 0x58, 0xc6, 0x0f, + 0x67, 0xc4, 0x55, 0x73, 0x46, 0x23, 0x19, 0x8f, 0xce, 0x3e, 0x66, 0x40, 0xf2, 0x7a, 0x96, 0xae, 0x60, 0xf0, 0xce, + 0xc2, 0x3c, 0x3e, 0x2b, 0xc5, 0x1d, 0x58, 0x9c, 0xca, 0xce, 0xf7, 0x20, 0xc3, 0x2a, 0xfc, 0x43, 0x9c, 0x01, 0xb4, + 0xeb, 0x59, 0x5a, 0x9f, 0xa5, 0xd5, 0x59, 0x5e, 0xd4, 0x67, 0x4a, 0x0a, 0x87, 0x30, 0x7e, 0x78, 0x4f, 0x5f, 0xd9, + 0xe5, 0x6d, 0x16, 0x77, 0x59, 0xe4, 0x4f, 0xd1, 0xab, 0x88, 0x98, 0x34, 0x2a, 0xe1, 0xb5, 0xfb, 0xdb, 0xe6, 0xfe, + 0xe1, 0x75, 0x63, 0xf7, 0xb3, 0x3b, 0x46, 0x74, 0x41, 0x3d, 0x5e, 0x49, 0x4a, 0x05, 0x05, 0x04, 0x4e, 0x34, 0x6b, + 0x3c, 0xb8, 0xe3, 0x80, 0x57, 0x03, 0x5b, 0xb2, 0xb5, 0xcf, 0xaf, 0x63, 0x19, 0xa6, 0xbd, 0x09, 0xf0, 0xaf, 0xb2, + 0x37, 0x5d, 0x07, 0x4b, 0xbc, 0x6f, 0x21, 0xdb, 0xd0, 0xdb, 0xd7, 0xfc, 0x85, 0x97, 0xab, 0xbf, 0xd9, 0x3f, 0x00, + 0x08, 0x03, 0x62, 0x56, 0x7d, 0x34, 0x71, 0xef, 0xac, 0x2c, 0x3b, 0x27, 0xcb, 0xae, 0x87, 0x7e, 0x4d, 0x62, 0x54, + 0x5a, 0x59, 0x4a, 0x27, 0x4b, 0x09, 0x59, 0xc0, 0x27, 0x46, 0x53, 0x1b, 0x01, 0x84, 0xed, 0x28, 0x95, 0x2f, 0x54, + 0x5e, 0x44, 0xe1, 0x9c, 0xe0, 0x79, 0x22, 0x46, 0xf7, 0x56, 0x32, 0x60, 0x38, 0x84, 0x60, 0x0e, 0xda, 0x62, 0x6f, + 0xe8, 0x26, 0xe2, 0xaf, 0x37, 0x45, 0xf9, 0x36, 0x26, 0x9f, 0x82, 0xdd, 0xc9, 0xc7, 0x25, 0x3c, 0x2e, 0x4f, 0x3e, + 0x0e, 0xd1, 0x23, 0xe1, 0xe4, 0x63, 0xf0, 0x3d, 0x92, 0xf3, 0xba, 0xeb, 0x71, 0x82, 0xdc, 0x42, 0xba, 0xbf, 0x1d, + 0x93, 0x00, 0xcd, 0x6b, 0x58, 0x8e, 0x9a, 0x8a, 0x6b, 0x66, 0xc6, 0x78, 0xde, 0xe8, 0xfd, 0xb1, 0xe3, 0x2d, 0x53, + 0x28, 0x66, 0x31, 0xaf, 0xe1, 0xf7, 0xac, 0x0a, 0xd4, 0x5d, 0x6f, 0x93, 0xdc, 0x32, 0xab, 0xe7, 0x68, 0xf7, 0xfd, + 0x50, 0x27, 0x82, 0xda, 0xdf, 0x61, 0xcf, 0x33, 0xeb, 0x5d, 0x15, 0x03, 0x97, 0x2a, 0xd9, 0x21, 0x53, 0xd5, 0xf4, + 0x40, 0xa5, 0x34, 0x78, 0x7a, 0x69, 0x5d, 0xbe, 0x54, 0xda, 0xc8, 0x33, 0xcd, 0x6f, 0x00, 0x2f, 0xa6, 0x2e, 0x8b, + 0xdd, 0x37, 0xf7, 0x15, 0xdc, 0xc6, 0xfb, 0xfd, 0x65, 0xe5, 0x99, 0x9f, 0xb8, 0x00, 0xec, 0x4d, 0x85, 0xd6, 0x09, + 0x94, 0x1a, 0xd6, 0xe1, 0xab, 0x44, 0x44, 0x7f, 0xb4, 0xcb, 0x75, 0xe6, 0x3a, 0x60, 0x44, 0x11, 0xbf, 0x8d, 0x47, + 0x7f, 0x80, 0xe2, 0xda, 0xd8, 0x03, 0xc2, 0x3a, 0x24, 0xf4, 0x19, 0x01, 0x48, 0x3d, 0xe6, 0x28, 0x01, 0xcd, 0x8a, + 0xe6, 0x8e, 0xc9, 0xcf, 0xf5, 0x95, 0xd2, 0xdf, 0x2f, 0x2b, 0x8f, 0xcc, 0x29, 0x6d, 0x33, 0x8d, 0xd5, 0x9a, 0x4a, + 0x20, 0xbc, 0xa2, 0x92, 0x55, 0xf8, 0x6c, 0xde, 0x88, 0x7e, 0x5f, 0x1e, 0xe1, 0x69, 0xf5, 0xe3, 0x16, 0xe3, 0x5b, + 0x01, 0xd1, 0x48, 0x00, 0xf4, 0x13, 0xc0, 0xbc, 0xc8, 0x66, 0x76, 0x1f, 0x07, 0x54, 0x29, 0xd1, 0x34, 0xce, 0xe6, + 0xf9, 0x3d, 0xbd, 0x29, 0x3b, 0xe8, 0xd4, 0xa9, 0x02, 0x17, 0x5c, 0x95, 0x8c, 0x57, 0xd6, 0x13, 0xf9, 0xfc, 0xe6, + 0x76, 0x93, 0x66, 0xf1, 0x87, 0xf2, 0x1f, 0x38, 0xb6, 0xba, 0x0e, 0x8f, 0x4c, 0x9d, 0xae, 0x9d, 0x47, 0x5a, 0x7b, + 0x21, 0x20, 0xa2, 0x5d, 0x43, 0xad, 0x17, 0x16, 0x7a, 0xa4, 0x27, 0xc2, 0x39, 0x49, 0xd4, 0xb4, 0x03, 0x2d, 0x8d, + 0xd0, 0xd7, 0xd7, 0x9c, 0xfe, 0xc2, 0x60, 0xed, 0xf3, 0x31, 0x03, 0xb2, 0x12, 0xfd, 0x58, 0x3d, 0x34, 0x36, 0x73, + 0xe8, 0x59, 0xab, 0xf2, 0xcc, 0xab, 0x0e, 0x07, 0xc4, 0x87, 0xd1, 0x5f, 0xf2, 0xfb, 0xfd, 0xd7, 0x34, 0xff, 0x98, + 0x50, 0xe3, 0x67, 0x9b, 0x01, 0xba, 0xf6, 0x5d, 0x79, 0x20, 0xea, 0xb9, 0x56, 0x09, 0x42, 0xbc, 0x41, 0x4c, 0x34, + 0x23, 0xe6, 0xe0, 0xb4, 0x43, 0xcd, 0x3f, 0x49, 0x0d, 0x08, 0x51, 0xe2, 0x75, 0x4c, 0x59, 0x90, 0xd3, 0x26, 0x8e, + 0xf4, 0xa3, 0x70, 0x22, 0x3f, 0x89, 0xaa, 0xc8, 0xee, 0xe1, 0x82, 0xc1, 0xd4, 0x7b, 0xda, 0x2f, 0xd1, 0x6f, 0x09, + 0x47, 0xce, 0xd1, 0xaa, 0x10, 0x44, 0x4e, 0x08, 0x6b, 0x0d, 0x61, 0x82, 0xd8, 0x20, 0x5e, 0xf6, 0x5d, 0x92, 0xe1, + 0x48, 0xc1, 0x65, 0x1d, 0x3b, 0xc6, 0x5c, 0x1d, 0x55, 0xaf, 0x01, 0x8c, 0x57, 0x8e, 0xa0, 0xd9, 0x28, 0xb2, 0x4b, + 0x88, 0x2a, 0x72, 0x3c, 0x01, 0xb5, 0x83, 0xd2, 0xd8, 0x4c, 0xcf, 0xc7, 0x41, 0x3e, 0x5a, 0x54, 0xa8, 0x73, 0x62, + 0x19, 0xaf, 0x01, 0x58, 0x3b, 0x57, 0xfd, 0x3c, 0xab, 0xc1, 0x93, 0x86, 0xf8, 0x7c, 0x8c, 0xb6, 0x57, 0x36, 0x07, + 0xd5, 0x76, 0x3a, 0x2b, 0xaf, 0x98, 0x2e, 0x07, 0xc6, 0x7d, 0xc3, 0x2b, 0x8a, 0x33, 0xfc, 0xe4, 0xc1, 0x16, 0xe7, + 0x4f, 0x37, 0xd4, 0x7e, 0xcc, 0x8d, 0x7a, 0x18, 0x68, 0x2d, 0x78, 0x53, 0x10, 0xeb, 0xef, 0xc7, 0x8e, 0x6c, 0x1f, + 0xb4, 0xc8, 0x68, 0xf2, 0xd9, 0xcf, 0x3f, 0x96, 0xe9, 0x2a, 0x85, 0xfb, 0x92, 0x93, 0x45, 0x33, 0x0f, 0x81, 0xbd, + 0x21, 0x86, 0xeb, 0xa3, 0xc2, 0x23, 0xca, 0xfa, 0x7d, 0xf8, 0x7d, 0x95, 0x81, 0x29, 0x06, 0xae, 0x2b, 0x04, 0xe3, + 0x21, 0x10, 0xc4, 0xc3, 0x34, 0x3a, 0x19, 0xd4, 0xa0, 0x0d, 0xdf, 0x00, 0x64, 0x06, 0x78, 0x64, 0x2e, 0x3d, 0x02, + 0xee, 0x02, 0xd7, 0x9e, 0x8c, 0xc7, 0xfe, 0xc4, 0x34, 0x34, 0x6a, 0x4a, 0x33, 0x3d, 0x37, 0x7e, 0xd3, 0x51, 0x2d, + 0xd7, 0xce, 0x7f, 0x7c, 0xc9, 0x6f, 0xd0, 0x0b, 0x5a, 0x5e, 0xee, 0x23, 0x75, 0xb9, 0xcf, 0x28, 0x2e, 0x13, 0xc9, + 0x61, 0x41, 0x2c, 0x4b, 0x38, 0xf0, 0x18, 0x95, 0x2c, 0xb6, 0xf4, 0x58, 0x15, 0x2d, 0x5f, 0x94, 0x1b, 0xa4, 0x43, + 0x27, 0x04, 0x4b, 0x54, 0x10, 0x2c, 0x81, 0x71, 0x11, 0x6b, 0xbe, 0x19, 0xe4, 0x2c, 0x9e, 0x6d, 0xe6, 0x1c, 0x09, + 0xeb, 0x92, 0xc3, 0xa1, 0x90, 0x60, 0x33, 0xd9, 0x6c, 0x3d, 0x67, 0x6b, 0x9f, 0x81, 0x12, 0xa0, 0x94, 0x69, 0x82, + 0xd2, 0xb4, 0x62, 0x2b, 0x6e, 0x5a, 0x83, 0xd5, 0x6a, 0xca, 0x56, 0x35, 0x65, 0xe7, 0x34, 0xe5, 0xa8, 0x82, 0x92, + 0x13, 0x4a, 0x51, 0x86, 0x01, 0x8c, 0xd8, 0x24, 0xba, 0xca, 0xd0, 0xc7, 0x3b, 0xe1, 0x11, 0x54, 0x11, 0x91, 0x4f, + 0x18, 0x42, 0x60, 0x22, 0x8a, 0x0b, 0x55, 0x28, 0x06, 0xc8, 0x88, 0x04, 0x82, 0x89, 0x4a, 0x9d, 0x02, 0xf3, 0xd1, + 0x54, 0x31, 0x6c, 0xda, 0x13, 0xe5, 0x7b, 0xea, 0xb8, 0x47, 0xd9, 0xe6, 0x6f, 0x62, 0x17, 0x84, 0xc8, 0xdd, 0xb8, + 0x53, 0x3f, 0x23, 0xde, 0xdb, 0x1d, 0x61, 0xfc, 0x64, 0xc7, 0x2d, 0xc2, 0x15, 0xc1, 0x96, 0x6a, 0x0e, 0xb1, 0x98, + 0x57, 0x93, 0x04, 0xb5, 0x2c, 0x89, 0xbf, 0xe1, 0xc9, 0x20, 0x67, 0x4b, 0xf0, 0xa0, 0x9d, 0xb3, 0x0c, 0xf0, 0x57, + 0xac, 0x16, 0xfd, 0x56, 0x7b, 0x4b, 0x90, 0x9f, 0x36, 0x76, 0xa3, 0x30, 0x31, 0x82, 0x44, 0xdd, 0xae, 0x0c, 0xe4, + 0x87, 0x8f, 0x38, 0x1d, 0x8f, 0x3d, 0x65, 0xcc, 0xad, 0x4c, 0x2f, 0xd3, 0xb9, 0x92, 0x6f, 0xe4, 0x5e, 0xfa, 0xd8, + 0x4b, 0xb0, 0x73, 0xc0, 0x1b, 0x48, 0x1b, 0x78, 0x03, 0xdb, 0x85, 0xd7, 0x06, 0x09, 0x33, 0x02, 0x6c, 0x71, 0x7c, + 0x8c, 0x94, 0xc0, 0x10, 0x8e, 0xb3, 0x14, 0x80, 0x69, 0xf4, 0x65, 0xb6, 0xb2, 0x2f, 0xb3, 0x5a, 0xb3, 0xa5, 0x72, + 0xba, 0x77, 0x6e, 0xdd, 0xce, 0x27, 0x12, 0x00, 0x4c, 0xea, 0x1c, 0x88, 0x33, 0x13, 0xec, 0xd2, 0x24, 0xb2, 0x7c, + 0x0a, 0xf3, 0x3b, 0xf1, 0xa6, 0x2c, 0x56, 0xaa, 0x2b, 0xda, 0x3e, 0x33, 0xf9, 0x8c, 0x74, 0x12, 0x2a, 0xa0, 0xa0, + 0x90, 0x6b, 0x7d, 0xfa, 0x3e, 0x7c, 0x1f, 0x14, 0x1a, 0x98, 0xad, 0xc2, 0x3d, 0x4d, 0xd6, 0x48, 0xbd, 0x51, 0xf5, + 0xfb, 0xe4, 0x1a, 0x48, 0x75, 0xe6, 0xd0, 0xb2, 0x27, 0x15, 0x06, 0x88, 0x1d, 0xf5, 0x19, 0x09, 0x75, 0x20, 0xf5, + 0x80, 0x21, 0x44, 0xdb, 0xf4, 0xf1, 0x27, 0x43, 0xa2, 0x0b, 0xb0, 0x85, 0x68, 0x03, 0x3f, 0xfe, 0x04, 0xfb, 0x2c, + 0x08, 0x8f, 0x69, 0xfe, 0x0e, 0x92, 0x8e, 0x0d, 0x9c, 0x56, 0x9f, 0x82, 0x0f, 0x92, 0x1c, 0x4c, 0xd4, 0xc1, 0xcb, + 0xfd, 0xa5, 0xdf, 0x87, 0x2d, 0x3b, 0x97, 0x52, 0x1d, 0x2b, 0xf5, 0xb6, 0xad, 0xfd, 0x20, 0xda, 0x82, 0x23, 0x8b, + 0xf8, 0x87, 0x0c, 0x11, 0xc1, 0xcc, 0x20, 0xc2, 0xae, 0x85, 0xba, 0xdb, 0x53, 0x6a, 0x59, 0xd4, 0xdb, 0x9e, 0x52, + 0xea, 0x36, 0x0c, 0xdf, 0x4d, 0x30, 0x53, 0xdc, 0xf0, 0x3f, 0x32, 0x2f, 0xd4, 0x1b, 0x8f, 0x45, 0x81, 0xee, 0xf9, + 0xfb, 0x25, 0xaf, 0x66, 0x1b, 0x65, 0xc2, 0xbc, 0xe3, 0xcb, 0x59, 0x28, 0xbb, 0x5a, 0x1a, 0x77, 0xbe, 0x78, 0x4b, + 0x35, 0x1f, 0xfc, 0xc3, 0x21, 0x81, 0x78, 0xa3, 0xf8, 0xea, 0xae, 0x91, 0x5b, 0xd7, 0x64, 0x73, 0x55, 0x02, 0xea, + 0xf7, 0xf9, 0x1a, 0xf7, 0x5b, 0xac, 0x7f, 0xf7, 0x34, 0xc8, 0x58, 0xcd, 0x70, 0xc5, 0x14, 0x3e, 0x05, 0x80, 0xc1, + 0xe1, 0x54, 0x90, 0x16, 0x78, 0xc3, 0xcb, 0xe1, 0xe5, 0x64, 0x43, 0x26, 0xdd, 0x8d, 0x8f, 0xdc, 0x59, 0xa0, 0xea, + 0xfd, 0x8e, 0xe2, 0xa4, 0x41, 0xa2, 0xb1, 0xd7, 0xe0, 0x8b, 0x2c, 0xa3, 0x5c, 0x34, 0x71, 0x1f, 0x93, 0xaf, 0xf4, + 0x00, 0xe6, 0x2a, 0x94, 0x00, 0xd1, 0x6f, 0x2c, 0x8b, 0x8d, 0x68, 0x5b, 0x6c, 0x60, 0x29, 0x55, 0x73, 0xbd, 0x9a, + 0xbe, 0x78, 0x25, 0x9a, 0xf7, 0xd1, 0x8c, 0x53, 0x1a, 0x0d, 0x38, 0x4e, 0xa3, 0x70, 0xfb, 0xe1, 0x5e, 0x94, 0xcb, + 0x0c, 0x2c, 0xd9, 0x2a, 0x9c, 0xe2, 0xb2, 0x51, 0x67, 0xc4, 0x8b, 0x3c, 0x56, 0x00, 0x1d, 0x8f, 0x09, 0x80, 0xea, + 0x82, 0x80, 0x8a, 0x68, 0x29, 0xbd, 0x15, 0x5a, 0x2c, 0xd4, 0x1b, 0x8e, 0x52, 0xf8, 0x23, 0xfd, 0x79, 0x90, 0x4f, + 0x01, 0x88, 0x5d, 0x1f, 0x47, 0x6f, 0x8a, 0x92, 0x3e, 0x55, 0xcc, 0x72, 0x39, 0x98, 0xc0, 0xae, 0x4e, 0x64, 0xa8, + 0x15, 0xe4, 0xad, 0xba, 0xf2, 0x56, 0x26, 0x6f, 0x63, 0x9c, 0x92, 0x1f, 0xb9, 0xe9, 0x58, 0x23, 0x06, 0x5e, 0x79, + 0x5a, 0xa7, 0x09, 0xd2, 0xe4, 0x02, 0x18, 0x86, 0xf8, 0x36, 0xf3, 0x5e, 0x78, 0x8e, 0x54, 0x05, 0xc9, 0x6c, 0x97, + 0x79, 0xea, 0x22, 0xaa, 0xaf, 0x9c, 0x5a, 0x3a, 0x73, 0xfa, 0x11, 0xc0, 0x7b, 0x4c, 0x4d, 0x1a, 0xf2, 0x11, 0x6e, + 0x4b, 0xf1, 0xf5, 0x56, 0x5d, 0xe3, 0xa5, 0xd1, 0xb9, 0x7b, 0xf9, 0xd2, 0x9d, 0x06, 0xfd, 0x14, 0x04, 0xe5, 0x7c, + 0x51, 0x0a, 0xd8, 0x53, 0x66, 0x73, 0xbd, 0x5a, 0xb5, 0x42, 0xeb, 0x70, 0x18, 0x6b, 0x47, 0x21, 0xad, 0xce, 0x02, + 0xb6, 0x1a, 0xe9, 0x94, 0x00, 0x21, 0x38, 0x4e, 0xc3, 0x4e, 0x30, 0xee, 0xd2, 0x69, 0x44, 0xd6, 0x2b, 0x25, 0xe9, + 0xc2, 0x0c, 0x92, 0x7f, 0x92, 0xd7, 0x33, 0xa0, 0x25, 0x80, 0x43, 0x11, 0x4b, 0x78, 0x38, 0x49, 0xae, 0x00, 0x3a, + 0x1d, 0x0e, 0x2a, 0x0d, 0xcd, 0x59, 0xcd, 0x92, 0xf9, 0x24, 0x96, 0xaa, 0xca, 0xc3, 0xc1, 0x53, 0x6e, 0x06, 0xfd, + 0x7e, 0x36, 0x2d, 0x95, 0x0b, 0x40, 0x10, 0xeb, 0xc2, 0x00, 0xf1, 0x48, 0x0b, 0x4f, 0x16, 0x7d, 0x4a, 0xe2, 0x97, + 0xb3, 0x64, 0x6e, 0xb2, 0xe1, 0x1d, 0x18, 0xc1, 0x66, 0x5c, 0x97, 0x94, 0x69, 0x8f, 0xca, 0xef, 0x19, 0x3d, 0xb5, + 0x7d, 0xad, 0xd5, 0x16, 0xb1, 0xae, 0x83, 0xab, 0x12, 0xf5, 0x14, 0x1f, 0x94, 0x24, 0x78, 0xbf, 0x76, 0x6e, 0x46, + 0xca, 0xd7, 0x22, 0xf7, 0x83, 0x76, 0xa6, 0x56, 0x0e, 0x1c, 0x81, 0x1c, 0xab, 0xa8, 0xe4, 0xf5, 0xae, 0x43, 0xf0, + 0xe8, 0xae, 0x54, 0xa0, 0x1c, 0x7c, 0x0d, 0x62, 0x74, 0x7d, 0xd5, 0x59, 0x43, 0xcd, 0x34, 0xaa, 0x3c, 0x82, 0x4e, + 0x1d, 0xc0, 0x93, 0x82, 0x97, 0x5a, 0xfd, 0x78, 0x38, 0x78, 0xe6, 0x07, 0x7f, 0x95, 0xe9, 0x5b, 0x88, 0x89, 0x72, + 0xaa, 0x11, 0x12, 0x57, 0x4a, 0x12, 0xf1, 0xf1, 0xa2, 0x65, 0xc5, 0xa8, 0x0c, 0x1f, 0x78, 0xa5, 0xca, 0x57, 0xa7, + 0x2a, 0x2f, 0x46, 0xda, 0x96, 0xc0, 0x6b, 0xf2, 0x0f, 0x91, 0x6b, 0xde, 0xfa, 0xba, 0xab, 0x0c, 0x7d, 0x27, 0x2b, + 0xd0, 0x11, 0x6c, 0x65, 0x29, 0x39, 0xe0, 0x93, 0xea, 0xae, 0x5a, 0xb5, 0x3e, 0xa7, 0x6c, 0x23, 0xdc, 0xe4, 0xd7, + 0xb1, 0x83, 0x23, 0xe5, 0x37, 0x78, 0x2e, 0x80, 0xbd, 0x06, 0xec, 0xcd, 0x39, 0x2b, 0x9a, 0x47, 0x87, 0xb4, 0x2d, + 0xd0, 0xc8, 0xcc, 0xed, 0x5c, 0xdd, 0xb7, 0xe5, 0x51, 0x1a, 0x43, 0x64, 0xda, 0x23, 0xd3, 0xc1, 0x66, 0x94, 0xff, + 0x9e, 0xf2, 0x5b, 0x85, 0x63, 0xe0, 0xdb, 0xa9, 0x77, 0x00, 0x55, 0x4f, 0x1b, 0x64, 0xac, 0x19, 0x86, 0x56, 0x76, + 0xb9, 0x14, 0x5a, 0x82, 0x96, 0xba, 0x09, 0x82, 0xf3, 0x23, 0xa2, 0x1c, 0x01, 0xe8, 0x22, 0x05, 0x4c, 0xf0, 0x53, + 0xda, 0xee, 0x7e, 0x7f, 0x9d, 0x7a, 0xe4, 0xde, 0x15, 0x6a, 0x94, 0x50, 0x82, 0xb1, 0x9f, 0x68, 0xcc, 0xa0, 0xa3, + 0x2b, 0x72, 0xc2, 0xb3, 0x56, 0x87, 0x75, 0xdd, 0x94, 0x41, 0x59, 0x1c, 0xf3, 0x6a, 0x3a, 0xfb, 0xfd, 0xc9, 0xbe, + 0x6e, 0x90, 0x85, 0xfc, 0x77, 0xd6, 0x43, 0x32, 0xe8, 0x1e, 0x84, 0x42, 0xf4, 0xe6, 0xc1, 0x0c, 0xff, 0x63, 0x1b, + 0x9e, 0x7d, 0xc7, 0x8d, 0x3a, 0x01, 0xcc, 0x11, 0xd7, 0x4b, 0x4f, 0xd1, 0xd6, 0xc3, 0x2d, 0x90, 0xad, 0xf1, 0xf2, + 0xd6, 0x5e, 0x03, 0x39, 0xc5, 0xf1, 0xdf, 0xf1, 0x4c, 0xad, 0x6c, 0xf0, 0xd3, 0x53, 0xb6, 0x03, 0x0f, 0x2f, 0x42, + 0x40, 0x31, 0x2c, 0x1b, 0x7f, 0x67, 0x39, 0xce, 0xe8, 0xbf, 0x79, 0xc4, 0x30, 0x58, 0x44, 0x7e, 0x7c, 0x59, 0x0a, + 0xf1, 0x55, 0x78, 0x6f, 0x2b, 0xef, 0x8e, 0x9c, 0x32, 0xef, 0xf4, 0x30, 0xba, 0x2e, 0x49, 0xdf, 0x25, 0x1f, 0x5b, + 0xc3, 0xf6, 0xbb, 0x76, 0xbf, 0x19, 0x22, 0x08, 0xa1, 0x1c, 0x3f, 0x67, 0x74, 0x42, 0xe3, 0xc3, 0x6a, 0x76, 0x7a, + 0xfd, 0xde, 0x39, 0x5e, 0xb0, 0x35, 0x1a, 0xe0, 0xf1, 0xd0, 0xc5, 0x3c, 0x51, 0x43, 0xa7, 0xeb, 0xda, 0x39, 0x78, + 0x60, 0x90, 0xe5, 0xc9, 0x77, 0x0c, 0x4b, 0xec, 0x4f, 0x22, 0x9e, 0xb4, 0x55, 0x1b, 0x9b, 0x23, 0xd5, 0x46, 0xcd, + 0xc0, 0x0f, 0x5e, 0x41, 0x81, 0xd1, 0x05, 0x69, 0x05, 0xc6, 0xe1, 0x08, 0x40, 0x56, 0x8c, 0xe3, 0x91, 0xc1, 0x04, + 0x86, 0x74, 0x43, 0x51, 0x00, 0x1e, 0x1e, 0xc7, 0x83, 0x90, 0x01, 0xa4, 0x0b, 0x1e, 0x1a, 0xb6, 0x49, 0x48, 0xf9, + 0x79, 0x9e, 0xd7, 0x6a, 0x08, 0x7d, 0x67, 0xa1, 0x3a, 0xf6, 0x23, 0xed, 0x15, 0xeb, 0x5a, 0x95, 0x8e, 0x6c, 0x75, + 0x80, 0xbe, 0x21, 0x03, 0xdf, 0x3a, 0xb6, 0x00, 0x88, 0x96, 0xf8, 0x2d, 0xf5, 0x6a, 0x5f, 0xc6, 0xac, 0x50, 0xaf, + 0x2f, 0x4c, 0xbb, 0x5e, 0x4b, 0x8b, 0x02, 0x2a, 0x6e, 0x5b, 0xb5, 0x3d, 0x92, 0xf3, 0x1f, 0xdf, 0x75, 0xb4, 0xe3, + 0xb3, 0x53, 0x63, 0x4b, 0x28, 0x73, 0x8b, 0x27, 0xb2, 0x3a, 0xda, 0x52, 0x9d, 0xea, 0x03, 0x2e, 0x35, 0xa9, 0xce, + 0x0c, 0x0c, 0xaf, 0x11, 0xa0, 0xdc, 0x42, 0x24, 0x8d, 0xc3, 0xde, 0xf9, 0x64, 0x50, 0x30, 0xb7, 0x48, 0x40, 0x02, + 0xdb, 0xd8, 0xda, 0x45, 0x73, 0xfd, 0xfa, 0x2d, 0xf5, 0xaa, 0x36, 0x55, 0x3d, 0x78, 0xe3, 0x05, 0xce, 0xde, 0x69, + 0x2d, 0x20, 0x80, 0xc2, 0xd6, 0xb2, 0x1c, 0x9c, 0xbb, 0x5d, 0xd5, 0x52, 0x51, 0x46, 0xfd, 0xfe, 0xf9, 0x6f, 0x29, + 0x2a, 0x62, 0x4f, 0x15, 0xa7, 0xac, 0xdf, 0x6e, 0x99, 0x8b, 0xca, 0x92, 0x37, 0xa8, 0xa2, 0xb5, 0x3a, 0x6a, 0x2a, + 0xd7, 0xcd, 0x55, 0x4b, 0x26, 0x88, 0xd1, 0x7d, 0xba, 0xd6, 0xb9, 0x53, 0xef, 0xbd, 0x8a, 0x23, 0x06, 0x82, 0x9b, + 0xee, 0xf1, 0xc1, 0x41, 0x68, 0x54, 0x94, 0x0b, 0x6e, 0x94, 0x56, 0x95, 0x94, 0x42, 0xde, 0xaa, 0x68, 0xce, 0xf4, + 0x11, 0x00, 0x11, 0x60, 0x95, 0xa8, 0xff, 0xcd, 0x97, 0xc6, 0x78, 0xf0, 0xc0, 0xd7, 0xe4, 0x3a, 0xb6, 0xde, 0x3f, + 0xad, 0x91, 0x56, 0x1b, 0xc7, 0xa4, 0x56, 0xbd, 0x6c, 0x15, 0x2f, 0xbb, 0xd7, 0xa9, 0x18, 0x3c, 0xff, 0x9f, 0xfb, + 0x00, 0x35, 0xa2, 0xa5, 0x0c, 0x6e, 0x5d, 0x0d, 0xd0, 0xf8, 0x70, 0x2c, 0x7c, 0xe3, 0x87, 0x8c, 0xf3, 0xc1, 0x0c, + 0x1d, 0xd5, 0xe6, 0xe0, 0x80, 0xe0, 0xa8, 0xee, 0xd1, 0x98, 0x30, 0x0b, 0xe7, 0x1e, 0x04, 0xaa, 0x4f, 0xdc, 0x67, + 0x5c, 0x7b, 0x41, 0x9b, 0xc0, 0x27, 0xeb, 0xba, 0xa6, 0x08, 0x70, 0x11, 0x1b, 0x13, 0x31, 0xc4, 0x65, 0x93, 0x48, + 0x7d, 0x33, 0x06, 0x05, 0x40, 0x71, 0x5d, 0x91, 0x5c, 0xba, 0x48, 0xf3, 0x4a, 0x94, 0xb5, 0x6e, 0x46, 0xc5, 0x8a, + 0x21, 0x00, 0x3c, 0x04, 0xc5, 0x55, 0x65, 0x26, 0x34, 0x62, 0x03, 0xa9, 0x2c, 0x05, 0xab, 0x86, 0x85, 0xdf, 0xb4, + 0xdf, 0x24, 0x27, 0xbd, 0xf3, 0x71, 0xeb, 0xdc, 0xb1, 0xef, 0x1d, 0x85, 0x94, 0xf6, 0x50, 0x4c, 0x10, 0x04, 0x3f, + 0xad, 0xc3, 0xf9, 0x33, 0x7e, 0x4d, 0x60, 0x2a, 0xb2, 0x19, 0x03, 0x0e, 0x42, 0x44, 0x66, 0xfc, 0x9e, 0xc3, 0x6b, + 0x5e, 0x4e, 0xc2, 0xe1, 0xd0, 0x07, 0x7d, 0x28, 0xcf, 0x66, 0xe1, 0x50, 0xcc, 0xa5, 0xf7, 0x3a, 0x58, 0xeb, 0x42, + 0x5e, 0x4f, 0x42, 0x44, 0x0b, 0x0d, 0x7d, 0x70, 0x5e, 0x77, 0xcd, 0x11, 0x96, 0x00, 0x34, 0x71, 0xf4, 0x65, 0xfd, + 0x7e, 0xe4, 0x69, 0x43, 0x8b, 0x14, 0x17, 0x8d, 0x32, 0x9b, 0xe5, 0xb2, 0x13, 0x36, 0xae, 0xdd, 0x02, 0xa1, 0x78, + 0x98, 0xb6, 0x50, 0xb5, 0x9e, 0xea, 0xf5, 0xdc, 0xb4, 0xfb, 0xee, 0x51, 0xb5, 0xca, 0x91, 0xce, 0xda, 0x74, 0xa5, + 0x56, 0xb7, 0x8c, 0xaa, 0x75, 0x96, 0x46, 0x54, 0xb9, 0x49, 0xee, 0x1a, 0xb5, 0xe0, 0x93, 0x0d, 0x5d, 0xa6, 0xec, + 0x6c, 0x0d, 0x4e, 0x1c, 0x79, 0x2e, 0xb9, 0xe5, 0xbb, 0xf3, 0x8a, 0xee, 0x4e, 0xb5, 0x6f, 0x01, 0xee, 0xcd, 0xb0, + 0x21, 0x73, 0x5e, 0x63, 0xa7, 0x41, 0x98, 0x04, 0x7e, 0xc4, 0x3e, 0x66, 0xc8, 0x06, 0x03, 0x3a, 0x0a, 0xe9, 0x7f, + 0x6d, 0x99, 0x23, 0x01, 0x93, 0xbf, 0x9e, 0xfb, 0xcd, 0xa2, 0xc8, 0x61, 0x31, 0x7e, 0xdc, 0x60, 0xa4, 0xb1, 0x5a, + 0x83, 0x61, 0x79, 0x87, 0xc8, 0x9f, 0xda, 0x1d, 0xd3, 0x54, 0xc7, 0x9b, 0xf5, 0x5a, 0xf3, 0xab, 0xa7, 0x4f, 0x75, + 0x7d, 0xfe, 0xdb, 0xf7, 0x97, 0x61, 0xcd, 0xec, 0x0f, 0x41, 0x28, 0xed, 0xde, 0x2d, 0xce, 0x1d, 0x89, 0xde, 0xb1, + 0xd2, 0xcc, 0x2e, 0xed, 0x92, 0x5d, 0x9a, 0xd2, 0x6e, 0xc8, 0xf5, 0xea, 0x1b, 0xe5, 0x8d, 0x9d, 0x57, 0x4c, 0xf7, + 0xef, 0x85, 0xde, 0x51, 0x4e, 0xd5, 0x04, 0x22, 0x9a, 0xb4, 0x23, 0x71, 0xbb, 0x57, 0x86, 0xcf, 0x27, 0x79, 0xbb, + 0x84, 0xa3, 0xae, 0x61, 0xb9, 0xf9, 0xf6, 0x3f, 0xf2, 0xaa, 0xb3, 0xc2, 0xed, 0x97, 0xc6, 0xac, 0xfd, 0x29, 0x88, + 0xab, 0xfa, 0xc3, 0x7b, 0x52, 0x33, 0x25, 0xff, 0x57, 0x3d, 0x06, 0xae, 0x7e, 0x32, 0xed, 0xe8, 0x9e, 0x42, 0xd8, + 0x60, 0xf6, 0xf3, 0xe3, 0x87, 0x16, 0xac, 0xaa, 0x0b, 0x14, 0xc9, 0x01, 0x74, 0xee, 0x92, 0x11, 0xde, 0xef, 0x18, + 0xe7, 0xfe, 0xd5, 0x4b, 0x35, 0x39, 0x42, 0x44, 0xbb, 0x08, 0x07, 0x00, 0x71, 0xa7, 0xa9, 0xac, 0x43, 0x0d, 0xd0, + 0x07, 0x04, 0xd6, 0xa1, 0x6f, 0x33, 0x80, 0x83, 0x3e, 0xda, 0x3c, 0x8b, 0x40, 0x5e, 0xf7, 0xee, 0xd9, 0x3b, 0xb6, + 0xf3, 0xf9, 0xf5, 0x2a, 0xf5, 0xee, 0xd1, 0x21, 0xf8, 0x7c, 0xec, 0x4f, 0x2f, 0x03, 0x83, 0x0b, 0xcd, 0xde, 0x3d, + 0x13, 0x6c, 0xc7, 0x76, 0xcf, 0x10, 0xa9, 0xa8, 0x3b, 0xff, 0xf0, 0xd2, 0x44, 0xcf, 0x3b, 0x2f, 0xdc, 0xf1, 0x25, + 0x80, 0x07, 0xb2, 0x18, 0x50, 0x7c, 0x96, 0xde, 0x3f, 0x59, 0x02, 0x6a, 0xf2, 0x5b, 0xbe, 0xf6, 0xbe, 0x52, 0xea, + 0x02, 0xfe, 0x1c, 0x50, 0xfa, 0x24, 0xe7, 0xde, 0xdd, 0xf0, 0xd6, 0xbf, 0x78, 0x0e, 0xce, 0x13, 0xab, 0xe1, 0x02, + 0xfe, 0x2a, 0xf8, 0xd0, 0xbb, 0x1b, 0x60, 0x62, 0xc9, 0x87, 0xde, 0x6a, 0x00, 0xa9, 0x0a, 0x17, 0x12, 0x63, 0x1f, + 0x7e, 0x0d, 0x72, 0x86, 0x7f, 0xfc, 0xa6, 0x31, 0x58, 0x7f, 0x0d, 0x0a, 0x8d, 0xc6, 0x5a, 0xaa, 0x90, 0xa5, 0x58, + 0x9c, 0x09, 0xb0, 0x09, 0xc7, 0xdd, 0xbe, 0x58, 0xd5, 0x66, 0x2d, 0xe8, 0xcf, 0x47, 0x7c, 0x8f, 0xc6, 0xea, 0xaa, + 0x9c, 0x8b, 0xf2, 0x13, 0xd2, 0xa7, 0x3a, 0x3e, 0x46, 0xc5, 0xa6, 0xee, 0x4e, 0xa7, 0x5a, 0x75, 0xa4, 0xfd, 0xa6, + 0x5c, 0x83, 0x1d, 0xaf, 0x93, 0x23, 0x4b, 0xe1, 0x59, 0x87, 0x9d, 0x97, 0x4e, 0x89, 0x0e, 0xc3, 0x78, 0xb7, 0x55, + 0xcf, 0x18, 0xca, 0x73, 0x83, 0x31, 0x5d, 0xf0, 0x88, 0x5f, 0x0f, 0x72, 0x19, 0x1a, 0xf3, 0x11, 0xd9, 0x30, 0x94, + 0x0f, 0x2d, 0x32, 0x24, 0x44, 0xbc, 0x87, 0x4a, 0xc0, 0xb6, 0x05, 0x65, 0x52, 0xc0, 0x59, 0x34, 0xf8, 0xad, 0xf6, + 0x72, 0xe0, 0x3d, 0x88, 0xfc, 0x46, 0xba, 0x94, 0x4b, 0x6c, 0x74, 0xe2, 0x58, 0x16, 0xda, 0x79, 0x5c, 0x7f, 0x1d, + 0x83, 0xfa, 0xbd, 0xd2, 0x6f, 0x50, 0xce, 0xfe, 0x24, 0x59, 0xa7, 0x8d, 0x27, 0xc6, 0xbf, 0x5c, 0xe5, 0x9f, 0xa2, + 0xa5, 0x1e, 0xfe, 0x3f, 0x63, 0x0a, 0xa5, 0x7f, 0x95, 0x96, 0xd1, 0x66, 0xb5, 0x14, 0xa5, 0xc8, 0x23, 0x71, 0xf2, + 0xb5, 0xc8, 0xce, 0xe5, 0x3b, 0x9f, 0x42, 0xbf, 0x00, 0xb4, 0xec, 0x13, 0x64, 0xf4, 0x4b, 0x26, 0xf8, 0xf0, 0xa5, + 0x76, 0xae, 0xcd, 0xf9, 0x78, 0x92, 0x5f, 0x59, 0x7b, 0xb7, 0xe3, 0x45, 0x62, 0x14, 0x63, 0xb9, 0xaf, 0xba, 0x59, + 0x39, 0x51, 0xc9, 0x81, 0x91, 0xae, 0xc9, 0x5e, 0xae, 0x64, 0xdd, 0x4e, 0xb7, 0x12, 0x88, 0xa8, 0x02, 0xef, 0x31, + 0xae, 0x62, 0x1f, 0xc1, 0x74, 0xdd, 0x71, 0x19, 0xed, 0x78, 0xcf, 0x78, 0x75, 0xa2, 0xac, 0xe0, 0x76, 0x23, 0xda, + 0x13, 0x3a, 0xfa, 0x69, 0x52, 0x5b, 0x16, 0x0e, 0x40, 0xee, 0x12, 0xc6, 0xb2, 0x21, 0x58, 0x31, 0x28, 0x7d, 0xbd, + 0xa6, 0x64, 0x59, 0x80, 0x45, 0x67, 0x97, 0x11, 0x88, 0x61, 0xdd, 0x34, 0x27, 0x74, 0xbc, 0x74, 0x71, 0xde, 0x6b, + 0x15, 0x29, 0x78, 0x46, 0x8b, 0x8e, 0xb9, 0xe9, 0x48, 0x37, 0x46, 0x7b, 0xfb, 0xd2, 0x20, 0xa4, 0x78, 0xfe, 0xc0, + 0x56, 0xeb, 0xe2, 0x22, 0xf1, 0x0a, 0x99, 0x68, 0x41, 0x2c, 0x45, 0x60, 0xc6, 0x0b, 0x4d, 0x23, 0x4c, 0x50, 0xa6, + 0x04, 0x8b, 0xd6, 0xe8, 0xd0, 0xfe, 0xb0, 0x84, 0xdd, 0x63, 0x8c, 0x00, 0x81, 0x2a, 0xd3, 0x97, 0xb0, 0x35, 0x61, + 0x36, 0x75, 0xb1, 0x01, 0xda, 0x2a, 0x86, 0x06, 0x61, 0x6d, 0x88, 0xf9, 0x94, 0xe6, 0x77, 0xff, 0xc4, 0x62, 0x6c, + 0x4f, 0x20, 0xb6, 0x77, 0xbb, 0x26, 0x61, 0xba, 0xd7, 0xe2, 0xc6, 0x7a, 0xb9, 0x3d, 0xe5, 0x98, 0xda, 0xb1, 0x36, + 0x6a, 0xc7, 0x5a, 0xea, 0x1d, 0x6b, 0xad, 0x77, 0xac, 0xbb, 0x86, 0x7f, 0xcc, 0xbc, 0x98, 0x25, 0xa0, 0xdf, 0x5d, + 0x71, 0xd5, 0x20, 0x68, 0xc6, 0x86, 0xdd, 0xc2, 0x6f, 0x89, 0xb5, 0x5b, 0xfa, 0x17, 0x4b, 0xb6, 0x30, 0x7d, 0xa0, + 0x5b, 0x07, 0x58, 0x46, 0xd4, 0xe4, 0x7b, 0xe4, 0xdd, 0x74, 0x56, 0x14, 0x6e, 0x4f, 0x6c, 0xe1, 0xb3, 0x77, 0xe6, + 0xcd, 0xfb, 0x67, 0x11, 0xe4, 0xde, 0x71, 0xef, 0x7e, 0xf8, 0xce, 0xbf, 0xd0, 0x2d, 0x90, 0x93, 0x59, 0xce, 0x40, + 0xea, 0x88, 0xcf, 0x10, 0xad, 0xec, 0x29, 0xdf, 0x09, 0xb9, 0xb3, 0xad, 0x9f, 0xdd, 0xbb, 0xdb, 0xda, 0xdd, 0xb3, + 0x7b, 0x56, 0x8d, 0x28, 0x56, 0x9c, 0xa6, 0x48, 0x98, 0x45, 0x1b, 0xe0, 0xa9, 0x97, 0xef, 0x77, 0xec, 0x98, 0xc3, + 0xdd, 0xb3, 0x8e, 0x8e, 0x97, 0x73, 0xc0, 0xee, 0xfe, 0xa3, 0x4d, 0xd8, 0x58, 0xe9, 0x5a, 0x85, 0x0e, 0x77, 0xcf, + 0x32, 0x8d, 0xe7, 0x70, 0x24, 0x9f, 0x8e, 0x35, 0x36, 0x08, 0xea, 0xfa, 0x9c, 0x41, 0xed, 0xd8, 0x7d, 0x4d, 0xd8, + 0x65, 0xc7, 0xbc, 0xd6, 0x35, 0x6f, 0xaf, 0x3c, 0x15, 0x1b, 0x02, 0x3a, 0x7c, 0xad, 0x6e, 0x90, 0x7f, 0x09, 0x9c, + 0x22, 0x00, 0xe4, 0x70, 0xbc, 0xe4, 0xb1, 0xef, 0xd3, 0x2c, 0xad, 0x77, 0xa8, 0xb5, 0xa8, 0x2c, 0xcb, 0xb0, 0xf6, + 0x7e, 0xd0, 0x8a, 0x61, 0xa9, 0xe9, 0x9f, 0x8e, 0x03, 0xb7, 0xb3, 0xdd, 0xca, 0xd8, 0x65, 0x3c, 0x2b, 0x2e, 0x5e, + 0x9e, 0x16, 0xca, 0xb5, 0x9b, 0xb7, 0xf1, 0x9b, 0x56, 0x4b, 0x96, 0xd6, 0x7a, 0xc8, 0x4b, 0xcb, 0x22, 0x02, 0x01, + 0x0c, 0x47, 0xca, 0x2e, 0x96, 0x70, 0x8f, 0xb0, 0xba, 0x07, 0xa1, 0x64, 0x5e, 0xb8, 0x78, 0xce, 0x62, 0x48, 0x04, + 0xd8, 0xee, 0x50, 0xb1, 0x2d, 0x5c, 0x3c, 0x67, 0x1b, 0x5e, 0xf4, 0xfb, 0x99, 0xea, 0x14, 0xb2, 0xee, 0x2c, 0xf9, + 0x46, 0x35, 0xc7, 0x1a, 0x6a, 0xb6, 0x36, 0xc9, 0xd6, 0x38, 0xb7, 0x15, 0x1f, 0x77, 0x6d, 0xc5, 0xc7, 0xca, 0x5a, + 0x97, 0xee, 0xf5, 0x1e, 0xd5, 0x05, 0xb0, 0xf5, 0xdf, 0x1e, 0xaf, 0x5c, 0xcf, 0x67, 0x04, 0xf0, 0xb5, 0xe0, 0xe3, + 0xc9, 0x02, 0xbd, 0x4a, 0x16, 0xfe, 0xed, 0x40, 0x8d, 0xbf, 0xd3, 0xb9, 0x0b, 0x80, 0xae, 0xa4, 0xbc, 0x02, 0xf2, + 0x0e, 0x72, 0xcc, 0x2d, 0xbb, 0xf2, 0xfe, 0xe4, 0x3b, 0xec, 0x1d, 0xaf, 0x67, 0x8b, 0x39, 0xdb, 0x81, 0x53, 0x41, + 0x32, 0xb0, 0x97, 0x15, 0xdb, 0x05, 0xb1, 0x9d, 0xf0, 0x1b, 0x01, 0x53, 0xbe, 0x80, 0x20, 0xae, 0xe0, 0x16, 0xe2, + 0xf0, 0xe4, 0x9f, 0x83, 0xfb, 0xd6, 0x66, 0x7d, 0xcf, 0xac, 0xce, 0x09, 0xd6, 0xcc, 0xea, 0xc1, 0x60, 0xd9, 0x4c, + 0x56, 0xfd, 0xbe, 0xb7, 0xd3, 0x8e, 0x4f, 0x77, 0x52, 0x27, 0x76, 0x5a, 0xab, 0xb5, 0x60, 0xef, 0xa4, 0xd6, 0xc5, + 0x18, 0x7a, 0x80, 0xf8, 0xe9, 0x76, 0xc0, 0xef, 0x3b, 0xd6, 0x96, 0xf7, 0x8e, 0x2d, 0xd8, 0x0e, 0x2e, 0x41, 0x4d, + 0x7b, 0xd9, 0x9f, 0x54, 0x2e, 0x68, 0xc7, 0x2e, 0x89, 0x87, 0x33, 0x66, 0x95, 0x32, 0xb3, 0x4e, 0xaa, 0x2b, 0xd1, + 0x19, 0xd3, 0x59, 0xeb, 0xf9, 0x5c, 0xcd, 0x27, 0x85, 0x06, 0xf5, 0x3b, 0x27, 0x3e, 0xa2, 0xa2, 0xf3, 0x04, 0xb6, + 0x96, 0x15, 0xc4, 0x6a, 0x9f, 0x83, 0xb5, 0x56, 0xbb, 0xf4, 0x7b, 0xf9, 0x80, 0xdb, 0x94, 0xc3, 0x3a, 0x30, 0xa8, + 0x39, 0xb1, 0xa2, 0x1e, 0xb3, 0x1d, 0xe3, 0xe6, 0xa7, 0x97, 0x3f, 0x38, 0x61, 0xc9, 0x8a, 0xd5, 0xfe, 0xf4, 0xe5, + 0x33, 0x4f, 0x7f, 0xa7, 0xf6, 0x2f, 0x84, 0x1f, 0x8c, 0xff, 0x5d, 0xbb, 0xaf, 0xb5, 0x18, 0x95, 0xad, 0x72, 0x84, + 0xc6, 0xdd, 0x4a, 0x9a, 0x2c, 0x3f, 0x0b, 0x4f, 0x58, 0x0b, 0x9e, 0xe5, 0x7a, 0x89, 0x66, 0x05, 0xac, 0xb0, 0x96, + 0x49, 0xb8, 0xc2, 0x58, 0x2d, 0x6d, 0xf5, 0x2d, 0x9a, 0xe6, 0xf8, 0x70, 0xae, 0x0d, 0xca, 0x94, 0xb3, 0x33, 0x62, + 0x35, 0x5c, 0x86, 0xa5, 0x09, 0x45, 0xc8, 0xee, 0xed, 0xe0, 0xc6, 0x4e, 0x59, 0x4a, 0x19, 0xce, 0x31, 0x98, 0xf0, + 0x48, 0x8c, 0xaa, 0x7c, 0x7f, 0x5f, 0x52, 0xe4, 0xb4, 0x2d, 0x07, 0x55, 0x08, 0xfb, 0x48, 0xa2, 0x04, 0x6e, 0x45, + 0x5a, 0x28, 0x52, 0x16, 0x7f, 0x3b, 0x40, 0x17, 0x78, 0x01, 0x75, 0x35, 0xea, 0xf6, 0x87, 0x23, 0x1e, 0x3e, 0x32, + 0xf5, 0x81, 0x11, 0x4b, 0x02, 0xb5, 0xbd, 0xc8, 0xd2, 0x3b, 0x50, 0xe1, 0xf7, 0x70, 0x35, 0x11, 0xfb, 0xb9, 0x25, + 0x45, 0x45, 0x36, 0xd2, 0x1b, 0x5a, 0x83, 0x47, 0x68, 0x4d, 0x79, 0xe9, 0xa4, 0xda, 0xa4, 0xf3, 0x8e, 0x90, 0x63, + 0xf5, 0xad, 0x25, 0x8c, 0x76, 0x45, 0x2f, 0xee, 0x1d, 0xbd, 0xe7, 0xe9, 0xaa, 0xe7, 0xfe, 0xc4, 0x15, 0xf3, 0xe4, + 0x36, 0x02, 0x75, 0x2b, 0xa8, 0x6e, 0x1f, 0x54, 0x82, 0x05, 0x4b, 0xda, 0x7d, 0xfc, 0x76, 0xd6, 0x0e, 0x44, 0x65, + 0xac, 0xd2, 0xb7, 0x24, 0x61, 0x4f, 0x0c, 0x3a, 0x85, 0xaa, 0xdc, 0xee, 0x8e, 0xb6, 0xc0, 0x75, 0xcc, 0x52, 0xf4, + 0xc2, 0x16, 0xb9, 0x5b, 0xfe, 0xdd, 0x73, 0x45, 0xce, 0x7e, 0x09, 0x08, 0x4e, 0xcd, 0x37, 0xc4, 0x97, 0x23, 0x3c, + 0xaa, 0x6e, 0x81, 0xe3, 0xf4, 0x1d, 0xc0, 0x3f, 0x1c, 0x2e, 0x41, 0x13, 0x10, 0x0b, 0xd6, 0x4b, 0xe3, 0x1e, 0xeb, + 0xc5, 0xc5, 0xe6, 0x2e, 0xc9, 0x37, 0xe0, 0xcc, 0x40, 0xa9, 0x96, 0x7e, 0xe0, 0x58, 0x2d, 0xa0, 0xc2, 0xc1, 0xec, + 0xa4, 0x5e, 0x58, 0x46, 0x3d, 0xa6, 0xcf, 0xcf, 0x60, 0xef, 0x08, 0x09, 0x80, 0xfb, 0x65, 0x1f, 0x90, 0x80, 0x87, + 0xce, 0xec, 0x80, 0x70, 0xc2, 0x2c, 0xaa, 0x02, 0x89, 0xe4, 0x48, 0x3f, 0x7b, 0xcc, 0x44, 0xf2, 0x07, 0xb3, 0x9e, + 0x73, 0x4a, 0xf4, 0x58, 0x4f, 0x1d, 0x21, 0x3d, 0xd6, 0xb3, 0x8e, 0x88, 0x1e, 0xeb, 0x59, 0xc7, 0x47, 0x8f, 0xf5, + 0xcc, 0xb1, 0xd3, 0x83, 0xc0, 0x04, 0x88, 0x3c, 0x60, 0x3d, 0x9a, 0x4c, 0x3d, 0xc5, 0x3d, 0x40, 0x34, 0x08, 0xac, + 0x27, 0x85, 0xf3, 0x1e, 0x20, 0x8f, 0x91, 0x58, 0x1d, 0xf4, 0xfe, 0x63, 0xfc, 0xb4, 0x67, 0x64, 0xe4, 0x71, 0xeb, + 0xb0, 0xfa, 0x5f, 0xff, 0x09, 0x01, 0x70, 0x78, 0x36, 0xf5, 0x2e, 0xc7, 0x90, 0x55, 0x96, 0x11, 0x48, 0x7e, 0x62, + 0xf0, 0xe5, 0x0b, 0x80, 0xaa, 0xcf, 0x74, 0xad, 0x26, 0x47, 0xed, 0x31, 0x87, 0xae, 0x18, 0x00, 0xb6, 0x61, 0x89, + 0xaa, 0x5a, 0xd8, 0x84, 0xc5, 0xed, 0x67, 0x18, 0xcd, 0x65, 0xd3, 0x0b, 0x1a, 0xa8, 0x47, 0x08, 0x7e, 0x69, 0x3d, + 0xb4, 0xd6, 0x32, 0xe5, 0xd0, 0xb5, 0x51, 0x54, 0xd9, 0x50, 0x97, 0xb0, 0x5a, 0x8b, 0xa8, 0x26, 0x8a, 0x94, 0x4b, + 0x46, 0x51, 0x2c, 0x55, 0xb0, 0xcf, 0xc4, 0x1d, 0x44, 0xcd, 0xd3, 0x56, 0x5b, 0x05, 0xfb, 0x3b, 0x40, 0x58, 0x0b, + 0x6b, 0x21, 0x9d, 0x41, 0xed, 0x9d, 0x7e, 0xa4, 0xfc, 0xe5, 0x85, 0xdc, 0xce, 0x2d, 0x14, 0xe1, 0xf6, 0x1c, 0x94, + 0x37, 0x75, 0x55, 0x2a, 0xa2, 0xd1, 0x12, 0x28, 0x65, 0x4e, 0x10, 0x59, 0x80, 0x00, 0x8e, 0x1b, 0x08, 0x7c, 0x5e, + 0xe3, 0x13, 0x68, 0x14, 0x02, 0xf9, 0x81, 0x55, 0xb8, 0xf6, 0x90, 0x96, 0x5a, 0x23, 0xa2, 0x44, 0xfc, 0xe8, 0xea, + 0x39, 0xb6, 0xaf, 0x9e, 0xc6, 0xda, 0x52, 0x9a, 0x20, 0x7e, 0x62, 0xb1, 0x85, 0x98, 0x20, 0xaa, 0x43, 0x74, 0x04, + 0xcb, 0x09, 0x21, 0x0a, 0x7f, 0x08, 0xfd, 0xd4, 0xc0, 0x5f, 0xb2, 0x65, 0x91, 0xd7, 0x04, 0x8b, 0x59, 0x31, 0x40, + 0xab, 0x22, 0xf0, 0x4c, 0x67, 0x4b, 0x65, 0x4e, 0xf3, 0xe8, 0xc8, 0x0e, 0xce, 0xbb, 0x0e, 0xf6, 0xd2, 0x97, 0xb1, + 0x93, 0x65, 0xd3, 0xa8, 0x8d, 0x0d, 0x91, 0xf0, 0x8a, 0xfc, 0x55, 0x96, 0x1a, 0xe7, 0xc8, 0x5c, 0xae, 0xef, 0xba, + 0xb8, 0xbb, 0xa3, 0x6d, 0xc2, 0x2a, 0x44, 0xa8, 0xdb, 0x86, 0xca, 0xa5, 0x30, 0x1b, 0x9b, 0xa6, 0x01, 0xbe, 0x50, + 0x54, 0x2a, 0x55, 0xa9, 0xad, 0x54, 0x72, 0xc2, 0xbb, 0xbe, 0xa9, 0x45, 0xea, 0x8a, 0x60, 0x1b, 0x33, 0xd4, 0x43, + 0xb9, 0x51, 0x63, 0xdf, 0x76, 0xac, 0xd2, 0x3b, 0x4c, 0x90, 0x33, 0xf2, 0x22, 0x07, 0x17, 0x25, 0x05, 0x99, 0xab, + 0x21, 0xcc, 0x1f, 0x35, 0x7c, 0x5a, 0x58, 0xee, 0xa1, 0x04, 0xcc, 0x8e, 0x1a, 0x5e, 0x46, 0x08, 0x44, 0x5c, 0x2a, + 0xfb, 0x8a, 0x89, 0xdf, 0x53, 0x30, 0x4b, 0x26, 0x74, 0x2f, 0x62, 0x61, 0x84, 0x36, 0x3e, 0x49, 0x92, 0xa9, 0xa7, + 0x29, 0xb8, 0x91, 0xcb, 0x30, 0x47, 0x23, 0xb4, 0xe4, 0x23, 0x07, 0xd2, 0xd7, 0x72, 0x2a, 0xc1, 0x47, 0xd4, 0x29, + 0xe0, 0x78, 0x7e, 0x5e, 0x58, 0x3f, 0x59, 0x2e, 0x31, 0x97, 0xb5, 0xf9, 0x2f, 0x3b, 0x3a, 0x06, 0xbb, 0x3c, 0x4d, + 0x1c, 0x57, 0xff, 0x51, 0x95, 0x14, 0x0f, 0x3f, 0xa7, 0x39, 0xa0, 0x08, 0x66, 0xf6, 0x14, 0xe3, 0x63, 0x9f, 0x65, + 0x0a, 0xf8, 0xdb, 0xf5, 0xd6, 0x92, 0x89, 0x5d, 0xd2, 0x6e, 0xae, 0x8c, 0x5f, 0x6a, 0xc3, 0x8e, 0x83, 0x73, 0x03, + 0x50, 0x9c, 0x35, 0x3a, 0x2c, 0xaf, 0x75, 0xdb, 0xaa, 0x50, 0x81, 0x5a, 0xff, 0x7b, 0xb7, 0x30, 0xe5, 0x6d, 0x5e, + 0x2a, 0x6f, 0xf3, 0xd0, 0x04, 0x08, 0x44, 0x66, 0xc8, 0xb3, 0xa6, 0x63, 0x92, 0xb8, 0x77, 0xa4, 0xa4, 0x7d, 0x47, + 0x8a, 0x1f, 0xbd, 0x23, 0x21, 0xdf, 0x12, 0x3a, 0xb2, 0x2f, 0x39, 0x39, 0x81, 0x32, 0x83, 0xbd, 0xbc, 0x66, 0xb2, + 0x7f, 0x40, 0x7b, 0xe1, 0x5c, 0x96, 0x57, 0xfc, 0x9d, 0xf0, 0xd6, 0xfe, 0x74, 0x7d, 0xda, 0x55, 0xf5, 0xf6, 0x1b, + 0x33, 0xf3, 0x70, 0x28, 0x0e, 0x87, 0xca, 0x04, 0xed, 0x2e, 0xb8, 0x18, 0xe4, 0xec, 0xde, 0x8d, 0x8f, 0x7f, 0xc7, + 0x51, 0xc4, 0x56, 0xca, 0x23, 0xe9, 0x42, 0x25, 0x86, 0x97, 0x06, 0x1e, 0x66, 0xc7, 0xc7, 0x93, 0xdd, 0xd5, 0xfd, + 0x64, 0x30, 0xd8, 0xa9, 0xbe, 0xdd, 0xf2, 0x7a, 0xb6, 0x9b, 0xb3, 0x07, 0x7e, 0x3b, 0xdd, 0x06, 0xfb, 0x06, 0xb6, + 0xdd, 0xdd, 0x95, 0x38, 0x1c, 0x76, 0xd7, 0x7c, 0xe1, 0xef, 0x1f, 0x10, 0xd0, 0x99, 0x9f, 0x8f, 0xdb, 0x18, 0x3f, + 0x37, 0x6d, 0x57, 0xad, 0x1d, 0xc0, 0xd3, 0xff, 0xe8, 0xdd, 0xcc, 0x96, 0x73, 0x9f, 0x3d, 0xe1, 0x0f, 0xe0, 0x9f, + 0x8f, 0x9b, 0x24, 0x52, 0x9f, 0x68, 0x97, 0xc9, 0x1b, 0x70, 0x20, 0xdf, 0xf9, 0xec, 0x2d, 0x7f, 0x98, 0x2d, 0xe7, + 0xbc, 0x38, 0x1c, 0x3e, 0x4c, 0x43, 0x24, 0x6b, 0x0a, 0x2b, 0x62, 0x49, 0xf1, 0xfc, 0x20, 0x3c, 0x7e, 0x2f, 0x22, + 0x43, 0xa4, 0xe5, 0xde, 0x1d, 0xb2, 0x1b, 0x16, 0xf9, 0x01, 0x7c, 0x90, 0xed, 0xfc, 0x89, 0xac, 0x29, 0xdd, 0x2f, + 0x9e, 0xf8, 0x87, 0x03, 0xfd, 0xf5, 0xd6, 0x3f, 0x1c, 0x3e, 0xb0, 0x07, 0x04, 0x47, 0xe7, 0x3b, 0xe8, 0x1f, 0x7d, + 0xeb, 0x80, 0xaa, 0x0c, 0xdf, 0xcd, 0x36, 0x73, 0xff, 0x7a, 0xc5, 0xee, 0x80, 0x0b, 0x45, 0x79, 0xa1, 0xdd, 0xb0, + 0x07, 0xf4, 0x3a, 0x23, 0x27, 0xa2, 0xd9, 0x6e, 0xee, 0xb3, 0x18, 0x9f, 0xab, 0xfb, 0x62, 0xf2, 0xcd, 0xfb, 0xe2, + 0x8e, 0x6d, 0xbb, 0xef, 0x8b, 0xf2, 0x4d, 0x77, 0xfd, 0x6c, 0xd9, 0x8e, 0x3d, 0xc0, 0x0c, 0x7b, 0xc7, 0x6f, 0x9a, + 0x63, 0xc7, 0xd8, 0x6f, 0xde, 0x18, 0x01, 0x94, 0xd9, 0x82, 0xc5, 0x82, 0x83, 0x52, 0xad, 0xda, 0x96, 0x44, 0x5e, + 0xe9, 0x40, 0xb5, 0x19, 0xc1, 0x7d, 0xb5, 0x90, 0x33, 0xcf, 0x0c, 0xf4, 0x6d, 0x85, 0x68, 0xe1, 0xb0, 0x01, 0x7f, + 0xa3, 0xad, 0x63, 0x0c, 0xd3, 0xac, 0x66, 0xda, 0x16, 0x75, 0xf9, 0x7d, 0xef, 0x99, 0xfc, 0x46, 0x06, 0xb6, 0x10, + 0x49, 0xe1, 0x38, 0xbe, 0x78, 0x7e, 0xc2, 0x7f, 0xd5, 0xf2, 0xa8, 0xd5, 0x7e, 0xa1, 0xd4, 0xa7, 0xaf, 0xe8, 0x88, + 0x26, 0xee, 0x45, 0x5b, 0x86, 0x35, 0xca, 0x9a, 0x5a, 0x3a, 0x0c, 0xe3, 0x1a, 0xf6, 0xe5, 0x81, 0x43, 0xdf, 0x01, + 0x81, 0xb6, 0x4a, 0xa5, 0x40, 0x0b, 0xc7, 0x30, 0x0a, 0xb3, 0x90, 0xf2, 0xb8, 0x30, 0x4b, 0x79, 0x8f, 0x05, 0x5a, + 0xdc, 0xaa, 0x7b, 0x4c, 0x6d, 0xb7, 0x20, 0xc2, 0xea, 0x2d, 0xe3, 0xfc, 0xb2, 0x51, 0x85, 0xdb, 0x02, 0x14, 0x45, + 0x50, 0x06, 0x7b, 0x92, 0xdb, 0x16, 0x4a, 0x9a, 0x8d, 0xc2, 0x5a, 0xdc, 0x15, 0xe5, 0xae, 0xd7, 0xb0, 0x05, 0x5e, + 0x50, 0xf5, 0x13, 0xc2, 0xb6, 0xec, 0x59, 0x87, 0x72, 0x91, 0xfe, 0x5b, 0x96, 0x9e, 0xef, 0xb7, 0xe6, 0xfc, 0x4f, + 0x5f, 0xd1, 0x47, 0xe5, 0xbf, 0x7f, 0x49, 0x3f, 0x19, 0x2c, 0x23, 0xa7, 0xd4, 0xcb, 0x68, 0x74, 0x9b, 0xe6, 0x84, + 0xb1, 0xe5, 0xeb, 0xa7, 0xdf, 0x21, 0x53, 0x90, 0x1c, 0x4a, 0xa9, 0xca, 0xc9, 0x1e, 0xfa, 0xc2, 0xeb, 0x3e, 0xcc, + 0x04, 0x03, 0x10, 0x5e, 0xa3, 0x4d, 0x35, 0x61, 0x12, 0x8f, 0xae, 0xe0, 0xff, 0x46, 0x10, 0x83, 0xf6, 0x89, 0xa2, + 0x8e, 0x6d, 0x23, 0x5d, 0xb7, 0x9d, 0x83, 0xe4, 0x4e, 0x5d, 0xf9, 0xa3, 0x72, 0xf2, 0xef, 0x68, 0x88, 0xbc, 0xe2, + 0x0a, 0xb1, 0xb2, 0xe0, 0x12, 0x8b, 0xa1, 0x22, 0x05, 0xb8, 0x86, 0x20, 0x52, 0x16, 0x25, 0x85, 0x5b, 0x0e, 0xaa, + 0x22, 0x00, 0xe3, 0x6a, 0x75, 0xd4, 0x89, 0xf0, 0x71, 0x6b, 0x2d, 0x42, 0xb0, 0xa2, 0x51, 0x2b, 0x6b, 0x05, 0xbe, + 0x20, 0x7d, 0xe9, 0x50, 0x10, 0xd3, 0xa3, 0x90, 0xaa, 0xd2, 0xa1, 0x40, 0x9a, 0x43, 0xc5, 0x37, 0x06, 0x1b, 0x45, + 0x45, 0x7a, 0xfe, 0xd2, 0xa4, 0xe4, 0xd2, 0x98, 0xf1, 0x51, 0x94, 0x91, 0xc8, 0xeb, 0xf0, 0x4e, 0x4c, 0x0b, 0xe4, + 0x1b, 0x3d, 0x7e, 0x10, 0x5c, 0xc2, 0xbb, 0x21, 0xf7, 0x0a, 0xb0, 0x25, 0x60, 0x07, 0xb8, 0x57, 0x66, 0x94, 0xeb, + 0xb4, 0xae, 0xdf, 0x5a, 0x0f, 0xc5, 0x30, 0x7c, 0x66, 0x09, 0x6c, 0x47, 0xeb, 0xe8, 0x48, 0x0f, 0x1f, 0xfe, 0xd7, + 0x55, 0xcd, 0x51, 0xa7, 0x72, 0x39, 0x3b, 0x9e, 0xb0, 0x14, 0x31, 0x83, 0xee, 0xaf, 0xdb, 0x57, 0x02, 0xe8, 0x76, + 0x59, 0xcc, 0xb3, 0xd1, 0x4e, 0xfe, 0x2d, 0xdd, 0x58, 0x51, 0xda, 0xc4, 0xbb, 0xac, 0x37, 0xf6, 0x87, 0xa3, 0xff, + 0x78, 0xf6, 0x75, 0x42, 0xa8, 0x3a, 0x1b, 0xb6, 0xd6, 0x71, 0x2e, 0xff, 0xeb, 0x3f, 0xc7, 0x64, 0x05, 0x41, 0x41, + 0x58, 0x76, 0x8a, 0x89, 0x0a, 0x46, 0x91, 0x62, 0xcd, 0xc7, 0x93, 0x35, 0xea, 0x84, 0xd7, 0xfe, 0x52, 0xeb, 0x84, + 0x89, 0x91, 0x95, 0xca, 0x5f, 0xb3, 0x8a, 0xdd, 0xa9, 0xcc, 0x02, 0x32, 0x0f, 0xf2, 0xc9, 0xda, 0x68, 0x30, 0x57, + 0xbc, 0x9e, 0xad, 0xe7, 0x52, 0xf9, 0x0c, 0xa6, 0x9c, 0xe5, 0xe0, 0x64, 0x29, 0xec, 0x9e, 0x04, 0x8a, 0xd6, 0x0c, + 0x5d, 0xfb, 0x53, 0x6c, 0xd5, 0xeb, 0xb4, 0xaa, 0x01, 0x1e, 0x10, 0x62, 0x60, 0xa8, 0xbd, 0x5a, 0x78, 0x68, 0x2d, + 0x80, 0xb5, 0x3f, 0x2a, 0xfd, 0x60, 0x3c, 0x59, 0xf2, 0x05, 0xf2, 0x2f, 0x47, 0x8e, 0xda, 0xbd, 0xdf, 0xf7, 0xee, + 0x41, 0x0a, 0x8e, 0x5c, 0x0b, 0x05, 0x12, 0x01, 0x2d, 0xf8, 0xc6, 0x57, 0x3e, 0x18, 0xef, 0x50, 0x5b, 0x0d, 0x0a, + 0x6a, 0x47, 0xb7, 0x3c, 0x76, 0xf4, 0xce, 0xf7, 0x27, 0xf4, 0xd5, 0x0b, 0x2d, 0x1c, 0x7f, 0xe3, 0x8c, 0x5c, 0xb3, + 0x55, 0x87, 0x1c, 0xd1, 0x4c, 0x3a, 0x84, 0x88, 0x15, 0x5b, 0xb3, 0x77, 0xa4, 0x72, 0xee, 0x1c, 0xb2, 0xd3, 0x47, + 0xa8, 0xd2, 0x6b, 0x3d, 0xbe, 0x9d, 0x28, 0xdd, 0xed, 0xf1, 0x6e, 0xf2, 0x3d, 0x9b, 0x88, 0x18, 0x0c, 0x68, 0x83, + 0x70, 0x46, 0xd6, 0x21, 0x52, 0xe9, 0x00, 0x21, 0x70, 0x4c, 0x40, 0xd3, 0x7f, 0x7d, 0x4b, 0xa2, 0x80, 0x23, 0x6d, + 0x84, 0xac, 0x65, 0x87, 0x43, 0x0e, 0x1a, 0xe5, 0xe6, 0x0f, 0xaf, 0x50, 0xa7, 0x39, 0x30, 0x4f, 0x97, 0xb0, 0xe7, + 0xe0, 0x91, 0x5e, 0x1c, 0x1f, 0xe9, 0xff, 0x1d, 0x4d, 0xd4, 0xf8, 0xdf, 0xd7, 0x44, 0x29, 0x2d, 0x92, 0xa3, 0x5a, + 0xfa, 0x2e, 0x75, 0x14, 0x5c, 0xe4, 0x1d, 0xb5, 0x90, 0x3d, 0xcb, 0xc6, 0x8d, 0x6a, 0xde, 0xff, 0xaf, 0x95, 0xf9, + 0xff, 0x9a, 0x56, 0x86, 0x29, 0xd9, 0xb1, 0x54, 0x33, 0x0f, 0xb4, 0x8a, 0x61, 0xf6, 0x33, 0x49, 0x88, 0x0c, 0x97, + 0x06, 0xfc, 0xa8, 0x82, 0x7d, 0x9c, 0x56, 0xeb, 0x2c, 0xdc, 0xa1, 0x12, 0xf5, 0x56, 0xdc, 0xa5, 0xf9, 0x8b, 0xfa, + 0x5f, 0xa2, 0x2c, 0x60, 0x6a, 0xdf, 0x95, 0x69, 0x1c, 0x90, 0x85, 0x3f, 0x0b, 0x4b, 0x9c, 0xdc, 0xd8, 0xc6, 0x9f, + 0xe5, 0x78, 0xda, 0xaf, 0x3a, 0x33, 0x0f, 0x24, 0x50, 0x03, 0xf1, 0x47, 0xce, 0x65, 0x65, 0xf1, 0x80, 0xd0, 0xcd, + 0x3f, 0x96, 0x65, 0x51, 0x7a, 0xbd, 0xcf, 0x49, 0x5a, 0x9d, 0xad, 0x44, 0x9d, 0x14, 0xb1, 0x82, 0xb2, 0x49, 0x01, + 0x46, 0x1f, 0x56, 0x9e, 0x88, 0x83, 0x33, 0x04, 0x6a, 0x38, 0xab, 0x93, 0x10, 0x80, 0x86, 0x15, 0xc2, 0xfe, 0x19, + 0xb4, 0xf0, 0x2c, 0x8c, 0xc3, 0x35, 0xc0, 0xe4, 0xa4, 0xd5, 0xd9, 0xba, 0x2c, 0xee, 0xd3, 0x58, 0xc4, 0xa3, 0x9e, + 0xa2, 0x64, 0x79, 0x93, 0xbb, 0x72, 0xae, 0xbf, 0xff, 0x83, 0x02, 0xd8, 0x0d, 0x98, 0x6d, 0x0b, 0xec, 0x00, 0x20, + 0x41, 0x81, 0x6c, 0xa1, 0x4e, 0xa3, 0x33, 0xb5, 0x54, 0xe0, 0x3d, 0xd7, 0x03, 0xfc, 0x4d, 0x0e, 0x58, 0xc6, 0x75, + 0x21, 0x03, 0x46, 0x10, 0xc0, 0x08, 0x1c, 0x94, 0x80, 0xa1, 0x33, 0xc4, 0x6d, 0x55, 0xce, 0x5a, 0x68, 0xae, 0x74, + 0x5b, 0x72, 0xd3, 0x28, 0x67, 0x2b, 0x11, 0x40, 0x5f, 0xdd, 0x94, 0x38, 0x5d, 0x2e, 0x5b, 0x49, 0xd8, 0xb7, 0x1f, + 0xda, 0xa9, 0x22, 0x8f, 0x8f, 0xd2, 0x90, 0x57, 0xe0, 0x49, 0xc6, 0x91, 0x24, 0x4a, 0x04, 0x6f, 0xf2, 0xc6, 0x8c, + 0xc3, 0x8b, 0x36, 0xe5, 0xd4, 0xde, 0xac, 0x17, 0x80, 0xf3, 0x04, 0x6d, 0x19, 0x60, 0x2c, 0x60, 0x70, 0x2e, 0xc4, + 0x92, 0xa7, 0x08, 0x7e, 0xe9, 0x44, 0x0a, 0xe3, 0x2e, 0x87, 0x61, 0x1e, 0x14, 0xbd, 0x4b, 0xea, 0x8f, 0x7e, 0x1f, + 0xb5, 0xc9, 0x60, 0x08, 0x2a, 0x01, 0x54, 0xd6, 0x0d, 0x12, 0x03, 0xab, 0xd2, 0x42, 0xe2, 0x12, 0xe2, 0x65, 0xbe, + 0x9a, 0xd6, 0x51, 0xf0, 0xa1, 0x9e, 0x10, 0xc2, 0x09, 0xc6, 0x87, 0xb8, 0x01, 0x02, 0x06, 0xab, 0xb8, 0xc0, 0x20, + 0x79, 0x2e, 0xd1, 0xfd, 0xf1, 0x7c, 0xc7, 0x00, 0x57, 0xce, 0x7b, 0xaa, 0x5d, 0x3d, 0xb0, 0x97, 0xab, 0x74, 0xc9, + 0x08, 0x61, 0xc5, 0xff, 0x45, 0xe4, 0x7d, 0x3b, 0x4c, 0x40, 0x6d, 0x23, 0x7f, 0x0c, 0x12, 0x73, 0x99, 0x28, 0x82, + 0x78, 0x94, 0x15, 0x2c, 0x49, 0x83, 0xcd, 0x28, 0x49, 0x41, 0xa3, 0x89, 0x31, 0x64, 0x2a, 0xb4, 0x43, 0xd2, 0x68, + 0x36, 0x26, 0xfb, 0x18, 0xf2, 0x1a, 0x2e, 0x16, 0x0b, 0xbc, 0xef, 0x67, 0xa1, 0x3a, 0xd8, 0x96, 0xe6, 0x10, 0x70, + 0x92, 0x60, 0x4f, 0x5d, 0x91, 0x92, 0x30, 0x1b, 0x7d, 0x0a, 0x39, 0x37, 0xa0, 0xe3, 0xa4, 0x31, 0x54, 0x1f, 0x98, + 0x84, 0x57, 0x11, 0x3a, 0x29, 0x2b, 0x84, 0x05, 0xdc, 0x37, 0x32, 0x1a, 0xad, 0xa4, 0x41, 0xe0, 0x6d, 0x86, 0xad, + 0xc0, 0x26, 0x34, 0xfc, 0x45, 0xe6, 0x61, 0x5a, 0xcd, 0x4a, 0x30, 0xe7, 0x1b, 0xa8, 0xc4, 0x78, 0xb2, 0xbc, 0xe2, + 0x1b, 0x17, 0x2b, 0x31, 0x99, 0x2d, 0xe7, 0x93, 0xb5, 0xa4, 0x9a, 0xcb, 0xbd, 0x35, 0xcb, 0xd8, 0x12, 0xf6, 0x0f, + 0x03, 0x43, 0xe9, 0xc0, 0x8e, 0xa6, 0x9a, 0x36, 0x09, 0x30, 0x99, 0xce, 0x39, 0x1f, 0x5e, 0x22, 0x9a, 0xac, 0x4e, + 0xdd, 0xc9, 0x54, 0xb5, 0x83, 0x6b, 0x72, 0x26, 0xa7, 0x47, 0xea, 0xa9, 0xd6, 0xbd, 0xe4, 0xa3, 0xed, 0xb0, 0x1a, + 0x6d, 0xfd, 0x00, 0xdc, 0x3a, 0x85, 0x9d, 0xbe, 0x1b, 0x56, 0xa3, 0x9d, 0xaf, 0x61, 0x77, 0x49, 0x21, 0x50, 0xfd, + 0x59, 0xd6, 0x64, 0x2e, 0x5e, 0x17, 0x0f, 0x5e, 0xc1, 0x9e, 0xfb, 0x03, 0xfd, 0xab, 0x64, 0xcf, 0x7d, 0x9b, 0xc9, + 0xf5, 0xcf, 0xb4, 0x6b, 0x34, 0x66, 0x3a, 0x5e, 0xbb, 0x02, 0x2b, 0x34, 0x40, 0x7e, 0xc1, 0x8e, 0xf6, 0x36, 0x07, + 0x81, 0x00, 0xdd, 0x4b, 0x70, 0x14, 0x05, 0x44, 0x4d, 0xab, 0xca, 0xa3, 0xd3, 0xbd, 0xbf, 0xc7, 0x37, 0x42, 0xc0, + 0x26, 0x4f, 0xad, 0x7b, 0xcb, 0xd8, 0x3f, 0x1c, 0x20, 0x84, 0x5e, 0x4e, 0xbf, 0xd1, 0x96, 0xd5, 0xa3, 0x1d, 0xcb, + 0x7d, 0xc3, 0xa8, 0xa7, 0x60, 0x0c, 0x43, 0x17, 0x56, 0x31, 0x92, 0x67, 0x40, 0xd6, 0xf8, 0x0d, 0xa2, 0x0b, 0x58, + 0xf4, 0x7a, 0xaf, 0x8f, 0x68, 0x10, 0x01, 0x95, 0x5e, 0xf3, 0x97, 0x22, 0x9f, 0xab, 0x42, 0xf4, 0xde, 0x5b, 0x3b, + 0x6f, 0x66, 0x24, 0xcb, 0xa4, 0x91, 0x6a, 0xb7, 0xb2, 0x58, 0x57, 0xde, 0xec, 0x84, 0x74, 0x31, 0xc7, 0x50, 0x19, + 0x3c, 0x0e, 0x40, 0xe9, 0xf9, 0x97, 0xd0, 0x2b, 0x19, 0x32, 0xcd, 0x12, 0xcd, 0xec, 0xae, 0xf1, 0x27, 0xab, 0xd4, + 0x8b, 0x11, 0x31, 0x1b, 0xd8, 0x42, 0xdc, 0x16, 0x95, 0x6e, 0x8b, 0x42, 0xd9, 0xa2, 0x48, 0x1f, 0x6a, 0x67, 0xba, + 0x33, 0x0b, 0x9f, 0x55, 0xa6, 0x7d, 0x6f, 0x33, 0x33, 0x36, 0x40, 0xdb, 0x45, 0xf8, 0x06, 0x3a, 0x50, 0x21, 0xe4, + 0x3f, 0x22, 0x22, 0x12, 0x01, 0xbb, 0x9c, 0xba, 0x13, 0x9b, 0x0e, 0xc9, 0x3c, 0xc4, 0xac, 0x50, 0xa3, 0xbc, 0xe4, + 0xc9, 0xd1, 0x80, 0x54, 0x84, 0xba, 0xdd, 0xef, 0x9f, 0x2f, 0x5d, 0x50, 0xfb, 0x35, 0xc5, 0x8e, 0xd1, 0x4d, 0x01, + 0xe7, 0x82, 0x47, 0x79, 0xcf, 0xbd, 0x73, 0x40, 0x73, 0x6c, 0x4f, 0x91, 0x35, 0xe0, 0xf4, 0xb6, 0x0b, 0x01, 0xb6, + 0xcf, 0x9a, 0xad, 0xfd, 0xc9, 0xea, 0x2a, 0x9a, 0x7a, 0x25, 0x9f, 0xe9, 0x2e, 0x4a, 0xdc, 0x2e, 0x8a, 0x65, 0x17, + 0x6d, 0x1a, 0x08, 0x76, 0x5c, 0xf9, 0x01, 0xf0, 0x86, 0x46, 0xfd, 0x7e, 0xd9, 0xea, 0xd9, 0x93, 0xaf, 0x1d, 0xf7, + 0x6c, 0xe6, 0xb3, 0xd2, 0xf4, 0xec, 0xaf, 0xa9, 0xdb, 0xb3, 0x72, 0xb2, 0x17, 0x9d, 0x93, 0x7d, 0x3a, 0x9b, 0x07, + 0x82, 0xcb, 0x9d, 0xfb, 0x3c, 0x9f, 0xea, 0x69, 0x57, 0xf9, 0x41, 0x6b, 0x88, 0xcc, 0x17, 0x3e, 0x57, 0xdd, 0xeb, + 0x0a, 0x16, 0xb0, 0x04, 0x77, 0xeb, 0xa5, 0xf9, 0xaf, 0xd8, 0xfd, 0xbd, 0xa0, 0x97, 0xe6, 0xbf, 0xd1, 0x9f, 0x14, + 0xc0, 0x01, 0x68, 0x4c, 0xed, 0x16, 0x78, 0x88, 0xa1, 0x82, 0xc2, 0xdd, 0xac, 0x9c, 0x7b, 0x35, 0xc0, 0x61, 0x92, + 0xbe, 0xa1, 0xd5, 0x2b, 0x2d, 0x76, 0xbd, 0x4c, 0xf6, 0x0a, 0xf0, 0x50, 0x85, 0x3c, 0x3c, 0x1c, 0xa2, 0x8e, 0x61, + 0x07, 0x75, 0x04, 0x0c, 0x7b, 0x08, 0x8d, 0x2d, 0xf0, 0x7c, 0xfc, 0x9c, 0xf1, 0xbd, 0x00, 0xb5, 0x11, 0xc2, 0xe3, + 0xd5, 0xa2, 0x0c, 0xb1, 0x65, 0x6f, 0x91, 0x4a, 0xea, 0x67, 0x81, 0x28, 0xa3, 0x55, 0x40, 0x5b, 0xed, 0x31, 0x4b, + 0xe3, 0x0d, 0x84, 0x8a, 0xa5, 0x3e, 0x86, 0xd0, 0xc0, 0xe1, 0x77, 0x38, 0x80, 0x04, 0x5f, 0x72, 0x4d, 0x36, 0xf7, + 0x36, 0xbf, 0xa7, 0x7d, 0xfe, 0x70, 0x38, 0xbf, 0x44, 0x50, 0xba, 0x14, 0x3e, 0x52, 0x89, 0xa8, 0x9e, 0xe2, 0xa6, + 0x84, 0x6c, 0x96, 0xac, 0xf4, 0x83, 0x5f, 0xd5, 0x2f, 0x00, 0x90, 0x85, 0x40, 0x9b, 0xc8, 0xec, 0x4f, 0x67, 0x2a, + 0xba, 0x00, 0x38, 0xc4, 0x1f, 0x3f, 0x41, 0xf4, 0x0d, 0x2d, 0xd3, 0xf2, 0x71, 0xc2, 0x43, 0xd0, 0xda, 0x92, 0x4e, + 0x22, 0x56, 0x0a, 0x6c, 0x88, 0x84, 0xef, 0xf7, 0xcf, 0x63, 0x49, 0x07, 0x1a, 0xb5, 0xba, 0x37, 0x6e, 0x75, 0xaf, + 0x7c, 0x5d, 0x77, 0x72, 0xe3, 0x83, 0xa2, 0x7d, 0x36, 0x6f, 0x54, 0xbe, 0xef, 0xeb, 0x9c, 0xdd, 0xe9, 0xde, 0x91, + 0x73, 0xe2, 0xfb, 0x7b, 0x08, 0x45, 0x0f, 0x4d, 0x91, 0x65, 0x49, 0x18, 0xd0, 0x5a, 0xbb, 0xf6, 0x2c, 0xa3, 0x83, + 0xd7, 0xbe, 0x21, 0x44, 0xe4, 0x29, 0x3e, 0x09, 0xb9, 0xc5, 0xf1, 0x41, 0x81, 0xfe, 0x99, 0xf1, 0x67, 0x4e, 0xfc, + 0xb0, 0xd5, 0x2f, 0x80, 0x73, 0xd3, 0xbd, 0x77, 0x27, 0x66, 0x3d, 0x86, 0x52, 0x36, 0xfe, 0xef, 0xf7, 0x89, 0x2c, + 0xd0, 0xe9, 0x88, 0x86, 0x81, 0xe0, 0x2e, 0xaa, 0xff, 0x7b, 0xc5, 0xeb, 0x9e, 0xb5, 0x3a, 0x5f, 0x7e, 0xea, 0xf4, + 0xa4, 0x57, 0x2f, 0xe3, 0x1e, 0x50, 0xa1, 0x03, 0x84, 0xf3, 0xba, 0xdf, 0xb0, 0xdd, 0x77, 0xbf, 0xbc, 0x3b, 0x7a, + 0x19, 0xd8, 0xa4, 0x48, 0x6c, 0x2b, 0xf9, 0xac, 0x07, 0x0a, 0xbf, 0x1e, 0xeb, 0xd5, 0xc5, 0xba, 0xc7, 0x7a, 0xa8, + 0x05, 0x44, 0x0f, 0x0b, 0x50, 0xff, 0xf5, 0xec, 0xd3, 0x50, 0x38, 0xc8, 0xc6, 0xa9, 0x02, 0x45, 0x16, 0xfc, 0x5a, + 0x8c, 0xd6, 0x05, 0x01, 0x22, 0x5b, 0x42, 0x5a, 0x75, 0x32, 0x7b, 0x5c, 0x6a, 0x49, 0x06, 0xdf, 0x04, 0x64, 0x76, + 0x60, 0xe5, 0x04, 0xa5, 0xe3, 0xd6, 0x80, 0x2b, 0x5b, 0x3c, 0xda, 0xed, 0x4f, 0x83, 0xec, 0xac, 0x39, 0x69, 0xb4, + 0x0f, 0xfb, 0x34, 0x0f, 0x10, 0x88, 0x64, 0x2a, 0x82, 0x5c, 0x73, 0x6f, 0x49, 0x1f, 0x1d, 0xce, 0x79, 0x21, 0xff, + 0x9c, 0x4a, 0x1d, 0xe2, 0x50, 0x62, 0x0d, 0x04, 0x2a, 0xcf, 0x50, 0xe5, 0xb0, 0x41, 0x8e, 0x7f, 0x76, 0x24, 0x33, + 0x89, 0xc9, 0x22, 0x77, 0x6b, 0xa6, 0xc2, 0x0f, 0x04, 0x1f, 0xb3, 0x9c, 0x03, 0x17, 0xd8, 0x6c, 0xee, 0xab, 0x29, + 0x2e, 0xae, 0xc0, 0x1f, 0x53, 0xf8, 0x15, 0x4f, 0x61, 0xa7, 0xdd, 0xaf, 0x8b, 0x2a, 0x45, 0xdd, 0x46, 0x61, 0x51, + 0xc9, 0x82, 0x69, 0x0d, 0x69, 0xa2, 0xc3, 0xe8, 0x0f, 0x72, 0x06, 0x0a, 0x42, 0x7e, 0xd9, 0x34, 0xc0, 0x48, 0x25, + 0x97, 0x07, 0x55, 0x12, 0x78, 0x01, 0xb6, 0x41, 0xc5, 0xd6, 0x05, 0x04, 0xd9, 0x26, 0x45, 0x99, 0x7e, 0x2d, 0xf2, + 0x3a, 0xcc, 0x82, 0x6a, 0x94, 0x56, 0x3f, 0xe9, 0x9f, 0xc0, 0xbc, 0x4d, 0xc5, 0xa8, 0x56, 0x31, 0xf9, 0x8d, 0x7e, + 0xbf, 0x18, 0xb4, 0x3e, 0x64, 0xf0, 0xd1, 0x6b, 0xd3, 0xe0, 0x4f, 0x4e, 0x83, 0x1d, 0x26, 0x1a, 0x01, 0x90, 0xcc, + 0xa9, 0x25, 0x0f, 0x45, 0x7f, 0x04, 0x39, 0xd6, 0xa8, 0x72, 0x0a, 0x06, 0xeb, 0x3f, 0x1e, 0xed, 0xc0, 0xd4, 0x8b, + 0xa3, 0x2d, 0xd9, 0x41, 0x2b, 0xdf, 0x00, 0xf7, 0x6b, 0x64, 0x8b, 0x59, 0x0e, 0xd0, 0xec, 0x35, 0x22, 0xe3, 0x93, + 0x17, 0xc0, 0x98, 0xad, 0xb3, 0x30, 0x12, 0x71, 0x30, 0x56, 0x8d, 0x19, 0x33, 0x30, 0x70, 0x81, 0xae, 0x65, 0x52, + 0x92, 0x86, 0x74, 0x30, 0x60, 0xa5, 0x6c, 0xe1, 0x80, 0x17, 0xcd, 0x71, 0x3b, 0xde, 0xb4, 0x68, 0x3c, 0xb0, 0x5d, + 0x6c, 0x7f, 0xff, 0xb2, 0xd8, 0xbe, 0x0b, 0xb7, 0xa4, 0x57, 0xc8, 0x59, 0x42, 0x3f, 0x7f, 0x92, 0x7d, 0xd6, 0x70, + 0x72, 0x2a, 0x34, 0x43, 0x4b, 0x91, 0x50, 0x8a, 0x77, 0x7a, 0x52, 0x60, 0x2c, 0x63, 0xe1, 0xef, 0x81, 0x73, 0xba, + 0x50, 0x44, 0xee, 0xc0, 0x71, 0x7c, 0x03, 0x15, 0x8c, 0x1a, 0x0e, 0x5e, 0xc6, 0xb0, 0x2d, 0x8a, 0x59, 0x48, 0x38, + 0x85, 0x70, 0xb1, 0xca, 0xfa, 0x7d, 0xf9, 0x8b, 0xba, 0xe8, 0x22, 0x93, 0x75, 0x9f, 0x84, 0x23, 0x33, 0x96, 0x53, + 0x2f, 0x24, 0xcf, 0x7b, 0x9e, 0x4c, 0x93, 0x67, 0x79, 0x10, 0x01, 0xe4, 0x73, 0x78, 0x1f, 0xa6, 0x19, 0x58, 0xa5, + 0x49, 0xf9, 0x11, 0x4a, 0x5f, 0x7c, 0x5e, 0xf9, 0x81, 0xce, 0x9e, 0x9b, 0x64, 0x78, 0xb3, 0x6a, 0xbd, 0x49, 0xad, + 0xeb, 0xe2, 0x01, 0xff, 0xea, 0x0c, 0x36, 0xce, 0x75, 0x26, 0x38, 0xf0, 0x22, 0xa9, 0xf5, 0x9a, 0xf1, 0xeb, 0x0c, + 0xd7, 0xa5, 0x6a, 0xa3, 0x8f, 0x42, 0x74, 0x0e, 0x99, 0x0a, 0x50, 0x28, 0xd2, 0xfe, 0x41, 0xa9, 0x95, 0x49, 0xa5, + 0x8d, 0x04, 0xd0, 0x3d, 0x4c, 0x1a, 0x6c, 0x31, 0x94, 0xb1, 0x34, 0x89, 0x72, 0xa7, 0x41, 0x5c, 0xd9, 0x9f, 0x2b, + 0x89, 0x43, 0xcb, 0x22, 0xf9, 0xf7, 0xae, 0xa7, 0xaf, 0x90, 0xba, 0x93, 0x05, 0x32, 0x63, 0xbc, 0xc8, 0xe3, 0xcf, + 0x40, 0x98, 0x0d, 0xda, 0xa8, 0x28, 0x84, 0x90, 0x0d, 0x62, 0xd0, 0x78, 0x91, 0xc7, 0x2f, 0x15, 0x8d, 0x87, 0x7c, + 0x14, 0xf9, 0xea, 0xaf, 0x52, 0xff, 0x15, 0xfa, 0xcc, 0x04, 0x8f, 0x50, 0x4d, 0xf4, 0xef, 0x9e, 0xcf, 0xee, 0x41, + 0x6d, 0x18, 0x85, 0x99, 0x29, 0xbf, 0xf2, 0x4d, 0x71, 0xf6, 0xfa, 0x2b, 0xba, 0xca, 0xb6, 0xee, 0x47, 0x9f, 0x8e, + 0x08, 0xac, 0x8d, 0xd1, 0x15, 0x37, 0x06, 0x90, 0xc3, 0xe4, 0xfd, 0x8a, 0xd2, 0x72, 0x48, 0x83, 0xd0, 0x41, 0x43, + 0xd0, 0x2b, 0x89, 0x3e, 0x90, 0x58, 0xc4, 0x18, 0x5e, 0x88, 0x67, 0xa4, 0x26, 0x13, 0x0d, 0xf1, 0x8a, 0xd8, 0x0f, + 0xd1, 0x92, 0x53, 0x13, 0xdd, 0x08, 0x53, 0x0c, 0x24, 0x76, 0x06, 0xc9, 0x49, 0x52, 0x2b, 0xbf, 0x78, 0x26, 0x09, + 0x4b, 0xec, 0x3c, 0xc4, 0x60, 0x52, 0x4b, 0x77, 0x7a, 0x53, 0xa5, 0xf7, 0x47, 0x5a, 0x0e, 0xda, 0x07, 0x60, 0x97, + 0x92, 0xde, 0x3f, 0x29, 0x14, 0xf1, 0x31, 0x8c, 0x63, 0x08, 0xdf, 0x22, 0xaa, 0x2b, 0x70, 0xae, 0x15, 0x68, 0xac, + 0x06, 0x1e, 0x9a, 0x59, 0x35, 0x1f, 0x72, 0xfa, 0xa9, 0xb4, 0xfc, 0x31, 0xa2, 0xb1, 0xd1, 0xba, 0x39, 0x1c, 0xf6, + 0xb4, 0xea, 0xa5, 0x73, 0xd0, 0x65, 0x33, 0x89, 0x89, 0x1b, 0x48, 0xd7, 0x8f, 0x7e, 0x33, 0x61, 0x2f, 0xa2, 0x42, + 0x2e, 0x85, 0xa0, 0xa0, 0xd5, 0x81, 0xc0, 0xa1, 0xf0, 0x16, 0x65, 0xbe, 0x88, 0x69, 0x03, 0x61, 0xf0, 0xf9, 0x81, + 0xfc, 0x7c, 0x53, 0x90, 0x8a, 0x1d, 0xeb, 0xda, 0xef, 0x6f, 0x4a, 0x0f, 0xf0, 0xe4, 0x4c, 0x92, 0xa7, 0xcd, 0x10, + 0x56, 0x04, 0xd0, 0x98, 0xd5, 0x64, 0x71, 0xc2, 0x95, 0x39, 0xfc, 0x54, 0x79, 0x25, 0x4b, 0x99, 0x3a, 0x4f, 0xf5, + 0x02, 0x88, 0x3a, 0xde, 0xa0, 0x15, 0xa9, 0x5f, 0xa1, 0xb3, 0xd7, 0xac, 0x84, 0x8c, 0x87, 0xe7, 0x9c, 0xa7, 0xa3, + 0x07, 0x96, 0xf0, 0x08, 0xff, 0x4a, 0x26, 0xfa, 0xf0, 0x7b, 0xe0, 0x70, 0x33, 0x4e, 0x78, 0xe4, 0x36, 0x7b, 0x5f, + 0x85, 0x2b, 0xb8, 0x99, 0x16, 0x80, 0xe4, 0x16, 0x24, 0x4d, 0x40, 0x09, 0x89, 0x4c, 0xc8, 0xac, 0x29, 0xf9, 0xa5, + 0xa5, 0x6d, 0xb0, 0x86, 0x49, 0xe7, 0x01, 0x2f, 0x5a, 0x7d, 0xb4, 0x9a, 0x68, 0x97, 0x59, 0x3e, 0x1f, 0xe2, 0x0c, + 0xd5, 0x1c, 0x77, 0x67, 0xf0, 0x73, 0xc0, 0x2b, 0x56, 0x35, 0xe9, 0x68, 0x37, 0xe0, 0xc2, 0x93, 0xeb, 0x3c, 0x1d, + 0x6d, 0xf1, 0x97, 0xdc, 0x1f, 0x00, 0x3a, 0x98, 0xba, 0x04, 0xfe, 0x54, 0x6d, 0x35, 0x95, 0xfa, 0xa5, 0xb5, 0x5f, + 0xd7, 0x9d, 0xd5, 0xca, 0x3d, 0xeb, 0x32, 0xb4, 0x47, 0x86, 0x9c, 0x31, 0x03, 0xfe, 0x9c, 0xb1, 0xe4, 0xcf, 0x19, + 0x2b, 0xfe, 0x9c, 0x71, 0x63, 0x64, 0x00, 0x25, 0xb8, 0x97, 0xfc, 0x7a, 0x8f, 0x98, 0x21, 0x56, 0x83, 0x4a, 0x60, + 0x65, 0x29, 0xe7, 0x3e, 0x72, 0x8a, 0x29, 0xa7, 0x0c, 0x2f, 0x9d, 0xce, 0xdc, 0x81, 0x9c, 0x07, 0x33, 0x77, 0x98, + 0x9c, 0xf5, 0x29, 0x8e, 0xa5, 0x31, 0x29, 0x2a, 0x48, 0xe7, 0x74, 0xb8, 0x79, 0x75, 0x9c, 0x27, 0x2c, 0xe3, 0xe3, + 0xf6, 0x99, 0x02, 0x21, 0xb6, 0x78, 0x86, 0x44, 0x4a, 0xd5, 0x2c, 0xb7, 0xf9, 0xc3, 0xa1, 0x1e, 0x3d, 0xe8, 0x9d, + 0x1e, 0x7e, 0x25, 0xec, 0x97, 0xcc, 0xb3, 0x4f, 0x10, 0xc0, 0x24, 0x91, 0x67, 0x12, 0x8e, 0x7e, 0x2c, 0x47, 0x7f, + 0xd3, 0xf0, 0xf7, 0x19, 0xaa, 0xbb, 0x43, 0x60, 0x62, 0xcb, 0x0e, 0x1c, 0x82, 0xd3, 0x55, 0x25, 0x12, 0x70, 0xb0, + 0xd9, 0xb0, 0x48, 0xef, 0xf1, 0x10, 0xe7, 0x83, 0xc2, 0x47, 0x68, 0x98, 0xd1, 0xfb, 0xfd, 0x8d, 0xf0, 0x2a, 0xd9, + 0xca, 0xc3, 0x21, 0xb1, 0xee, 0xc2, 0x8e, 0x3e, 0x8e, 0xf6, 0x28, 0xa1, 0xf6, 0xa3, 0x5a, 0x6f, 0x2a, 0xf5, 0x20, + 0x37, 0xbb, 0x90, 0x18, 0x54, 0x2c, 0xd5, 0xa7, 0x57, 0xaa, 0x0f, 0x35, 0xeb, 0xfc, 0xae, 0x8e, 0xfb, 0x54, 0x8c, + 0xd6, 0x72, 0x42, 0x80, 0xeb, 0x20, 0xd1, 0xe8, 0x00, 0x18, 0x67, 0x9b, 0x2d, 0x2f, 0xb5, 0x75, 0xa2, 0x74, 0x1c, + 0xe7, 0xfa, 0x38, 0x3e, 0x1c, 0xa4, 0x98, 0x71, 0x79, 0x24, 0x66, 0x5c, 0x36, 0x00, 0x6f, 0xd6, 0x79, 0x50, 0x1f, + 0x0e, 0x97, 0x74, 0x29, 0x32, 0x9d, 0x6d, 0x94, 0x9f, 0xf5, 0xe8, 0xe1, 0x59, 0x82, 0xe6, 0xde, 0x0a, 0x7b, 0x2f, + 0x92, 0xed, 0x99, 0xac, 0x53, 0x2f, 0x23, 0x9f, 0x5e, 0xb8, 0x67, 0x97, 0x5c, 0xfd, 0xb0, 0xfa, 0x7a, 0xfa, 0xab, + 0xf0, 0x22, 0x56, 0xd1, 0x6e, 0x5d, 0x32, 0x61, 0x6f, 0x29, 0x95, 0xb4, 0xca, 0xcb, 0xa7, 0x1b, 0x3f, 0xc0, 0xcc, + 0xb4, 0xa7, 0x0f, 0xb2, 0x11, 0xd5, 0x9f, 0x95, 0xa8, 0x95, 0x61, 0xb2, 0x70, 0x5e, 0x32, 0xf5, 0x64, 0xc0, 0x63, + 0x56, 0xf2, 0x48, 0x76, 0x7a, 0x63, 0x10, 0x04, 0xb0, 0xce, 0x49, 0xab, 0xce, 0x38, 0x1a, 0xad, 0x2a, 0x17, 0xa7, + 0xab, 0x5c, 0x60, 0xb8, 0xdd, 0x9a, 0x6d, 0x54, 0x9d, 0xe5, 0xa6, 0x56, 0x29, 0xdf, 0x01, 0x7c, 0x2c, 0xab, 0x5c, + 0xd0, 0x31, 0x65, 0xea, 0xbc, 0x81, 0x60, 0x6c, 0x55, 0xe3, 0xc2, 0xa9, 0x71, 0xc1, 0x23, 0x6a, 0x77, 0xd3, 0xd4, + 0xa3, 0x2d, 0xb0, 0x94, 0x8e, 0x76, 0xbc, 0x44, 0x95, 0xc2, 0xdf, 0x04, 0xdf, 0x87, 0x71, 0xfc, 0xb2, 0xd8, 0xaa, + 0x03, 0xf1, 0xb6, 0xd8, 0x22, 0xed, 0x8b, 0xfc, 0x0b, 0x71, 0xc0, 0x6b, 0x5d, 0x53, 0x5e, 0x5b, 0x73, 0x1a, 0xd8, + 0x1a, 0x46, 0x4a, 0x0a, 0xe7, 0xe6, 0xcf, 0xc3, 0x81, 0x56, 0x76, 0xad, 0xee, 0x0a, 0xb5, 0x1e, 0x73, 0xd8, 0xb0, + 0x17, 0x59, 0xb8, 0x13, 0x25, 0x38, 0x72, 0xc9, 0xbf, 0x0e, 0x07, 0xad, 0xb2, 0x54, 0x47, 0xfa, 0x6c, 0xff, 0x35, + 0x18, 0x33, 0x74, 0x69, 0x02, 0x96, 0x8d, 0x91, 0xfc, 0xab, 0x69, 0xe6, 0x0d, 0x93, 0x35, 0x53, 0x38, 0x0e, 0x0d, + 0x23, 0xa4, 0x01, 0xdd, 0x06, 0xb5, 0xe1, 0xc9, 0x7c, 0x53, 0x95, 0x5f, 0xdd, 0x91, 0x6a, 0x3f, 0x18, 0x5e, 0x4e, + 0xc4, 0x39, 0x5d, 0x92, 0xd4, 0x53, 0x09, 0x25, 0x21, 0xd8, 0xa5, 0x0f, 0xe4, 0xc4, 0x0a, 0xc8, 0x5a, 0xc6, 0xf2, + 0x5b, 0x3d, 0x20, 0xf4, 0x9f, 0x76, 0xeb, 0x85, 0xfe, 0xd3, 0x34, 0x5b, 0xa8, 0xeb, 0x0f, 0x93, 0xfb, 0x8e, 0x5e, + 0x7f, 0x70, 0x78, 0xa7, 0xae, 0x2a, 0xae, 0xe2, 0x51, 0x6d, 0x98, 0xe4, 0x46, 0x59, 0xb8, 0x2b, 0x36, 0xb5, 0x5a, + 0x9e, 0x8e, 0xc3, 0x08, 0xcc, 0x08, 0x0a, 0x90, 0x75, 0xdd, 0x46, 0xc4, 0xb0, 0x92, 0xcb, 0x84, 0x7c, 0x42, 0x40, + 0x16, 0xa5, 0xc6, 0xf9, 0xb8, 0x05, 0x2a, 0x11, 0x0c, 0x4e, 0x43, 0x6b, 0xd5, 0x4d, 0x7e, 0x52, 0xd9, 0xd8, 0x1d, + 0x90, 0x43, 0x92, 0xc9, 0xe2, 0x6e, 0x74, 0x2b, 0x96, 0x45, 0x29, 0x7e, 0xc6, 0x7a, 0xb8, 0x66, 0x0b, 0xf7, 0x19, + 0x10, 0xda, 0x4f, 0x94, 0xf6, 0x26, 0xd2, 0x04, 0xdd, 0x77, 0x6c, 0x05, 0x20, 0x03, 0x28, 0xea, 0x6a, 0xb7, 0x3e, + 0xe7, 0xe7, 0x48, 0x9a, 0xe1, 0x30, 0xba, 0x7d, 0x7a, 0x17, 0xdc, 0x0d, 0x2e, 0x51, 0x2b, 0x7d, 0xc9, 0xe2, 0x16, + 0x06, 0xd5, 0xde, 0x2c, 0xe1, 0xa0, 0x66, 0xd6, 0xda, 0x08, 0x04, 0x93, 0x3d, 0x14, 0x54, 0xcc, 0x15, 0xec, 0x83, + 0x82, 0xb5, 0xe4, 0x75, 0x70, 0xb8, 0xb5, 0x2f, 0x2b, 0xc5, 0xc5, 0xf3, 0x8b, 0xa4, 0x75, 0x61, 0x29, 0x2f, 0x9e, + 0x37, 0x60, 0x70, 0x39, 0xc2, 0xa6, 0xaa, 0xfc, 0xc9, 0x06, 0x40, 0xb7, 0x22, 0x8a, 0x78, 0x51, 0x0a, 0xdb, 0x56, + 0x3e, 0x73, 0xc2, 0x06, 0x1b, 0xf6, 0x00, 0xf7, 0xca, 0xa0, 0x64, 0x70, 0x21, 0xc6, 0xed, 0x66, 0x17, 0xe0, 0x0a, + 0x86, 0xc2, 0xd8, 0x9a, 0xbf, 0xc9, 0xbc, 0x48, 0x09, 0xb8, 0x19, 0xa2, 0x7c, 0x6d, 0xe0, 0x64, 0xd2, 0x93, 0x6b, + 0xc9, 0x62, 0xc0, 0x82, 0x06, 0xdf, 0x51, 0xeb, 0xef, 0x4c, 0xfe, 0x8d, 0xa7, 0x87, 0x7e, 0xf0, 0x25, 0xf3, 0x96, + 0x3e, 0x7b, 0x53, 0xc9, 0x68, 0x4d, 0x12, 0xe5, 0xd5, 0xc3, 0x25, 0xc8, 0x0d, 0xcb, 0xd1, 0x03, 0x5b, 0x82, 0x38, + 0xb1, 0x1c, 0x25, 0x94, 0xd1, 0x15, 0xee, 0x55, 0x66, 0xcb, 0x44, 0x20, 0xc5, 0x81, 0xa5, 0x94, 0x7b, 0x8b, 0x75, + 0xb0, 0xc4, 0xfd, 0x89, 0xe4, 0x02, 0x4a, 0x1e, 0x40, 0xb9, 0x52, 0x40, 0xc0, 0xa7, 0x03, 0x28, 0x5f, 0xca, 0x8b, + 0xf0, 0x27, 0x4e, 0xd4, 0x60, 0x39, 0x7a, 0x68, 0xd8, 0x4f, 0x5e, 0x68, 0xd9, 0x1f, 0xee, 0xb4, 0xa6, 0x61, 0xc5, + 0xef, 0x60, 0x5a, 0x4c, 0xdc, 0xbe, 0x5c, 0xd9, 0x55, 0xf1, 0xd9, 0x4a, 0x9d, 0xdd, 0xd4, 0x90, 0x84, 0x7d, 0x43, + 0x56, 0x01, 0x0e, 0x56, 0x45, 0xdc, 0xb3, 0x2c, 0xf7, 0x61, 0xf4, 0xe7, 0x26, 0x2d, 0x85, 0x85, 0x2a, 0xe9, 0xef, + 0x9b, 0x52, 0x20, 0x95, 0x89, 0x4e, 0xb4, 0x10, 0x5c, 0x81, 0x41, 0xe0, 0x5e, 0xe4, 0x35, 0x00, 0xc6, 0x80, 0x4b, + 0x81, 0xb2, 0x6c, 0x4b, 0x08, 0xa9, 0xee, 0x67, 0xa0, 0xb6, 0x13, 0xf7, 0x69, 0x44, 0xd6, 0x42, 0xf4, 0x55, 0x30, + 0x66, 0xce, 0x4b, 0xe9, 0x16, 0x9b, 0xae, 0x36, 0xab, 0x1b, 0x74, 0x2e, 0x6d, 0xb9, 0xf9, 0x09, 0x5b, 0xac, 0x15, + 0x28, 0x9b, 0x90, 0xb4, 0x9d, 0xf3, 0x1c, 0x65, 0x13, 0x5a, 0xda, 0x7b, 0xea, 0x51, 0xa1, 0x3a, 0xd9, 0x7a, 0xa9, + 0x9a, 0x5a, 0x84, 0xd5, 0xe2, 0xa2, 0xf2, 0x03, 0xd0, 0x4d, 0xa5, 0xd5, 0x8b, 0xba, 0x46, 0x53, 0xa8, 0xd5, 0xc2, + 0x71, 0xa3, 0x9d, 0x4d, 0x97, 0xe9, 0x1d, 0xe2, 0xac, 0x4a, 0x3b, 0xf4, 0xcb, 0x4c, 0xbb, 0x5e, 0x76, 0xf4, 0x9b, + 0x71, 0x75, 0x81, 0x0b, 0xb1, 0x01, 0x9f, 0x73, 0x7f, 0x79, 0xbd, 0xe7, 0x71, 0xcf, 0x3f, 0x1c, 0x90, 0x3d, 0xa9, + 0xfd, 0xa1, 0xfa, 0xd8, 0x15, 0x0c, 0x59, 0x18, 0xa5, 0xfe, 0x22, 0xe5, 0xbd, 0x27, 0x38, 0xee, 0x5f, 0xaa, 0x1e, + 0xfb, 0x29, 0xe3, 0xfb, 0xba, 0xd8, 0x44, 0x09, 0x45, 0x35, 0xf4, 0x56, 0xc5, 0xa6, 0x12, 0x71, 0xf1, 0x90, 0xf7, + 0x18, 0x26, 0xc3, 0x58, 0xc8, 0x54, 0xf8, 0x53, 0xa6, 0x82, 0x47, 0x08, 0x25, 0x6e, 0xd6, 0x3d, 0xd2, 0x6e, 0x42, + 0x9c, 0x52, 0x2d, 0x4a, 0x99, 0x8c, 0x7f, 0xeb, 0x27, 0x50, 0x9e, 0x53, 0xb4, 0x4c, 0x3f, 0x2a, 0x5c, 0xa6, 0x6f, + 0xd6, 0xc7, 0xa5, 0x67, 0x22, 0xd4, 0x99, 0x8b, 0x4d, 0xad, 0xd3, 0x31, 0x76, 0x4a, 0xa7, 0x36, 0xec, 0x6b, 0xa5, + 0xb8, 0xac, 0x28, 0xfc, 0x1b, 0x89, 0xac, 0x7a, 0x46, 0x1c, 0xff, 0x67, 0xd6, 0x3e, 0xc3, 0x2a, 0xf0, 0xcb, 0x40, + 0xde, 0x2f, 0x00, 0x3e, 0xae, 0xeb, 0x32, 0xbd, 0xdd, 0x00, 0x6d, 0x08, 0x0d, 0x7f, 0xcf, 0x47, 0x06, 0x4c, 0xf7, + 0x11, 0xce, 0x90, 0x1e, 0xea, 0x9c, 0xd3, 0x59, 0x99, 0xce, 0xb9, 0x0a, 0x6b, 0x09, 0xf6, 0x72, 0xd2, 0xe4, 0x72, + 0x5d, 0x82, 0x9a, 0x09, 0xdc, 0x3e, 0xb4, 0x47, 0x84, 0x50, 0x9b, 0xb2, 0x9a, 0x5e, 0x42, 0xcd, 0x3b, 0x39, 0xed, + 0x68, 0x52, 0x82, 0xab, 0x86, 0xce, 0xca, 0xf5, 0x5f, 0x87, 0x43, 0xef, 0x36, 0x2b, 0xa2, 0x3f, 0x7a, 0xe8, 0xef, + 0xb8, 0xbd, 0x49, 0xbf, 0x42, 0xb4, 0x8c, 0xf5, 0x37, 0x64, 0x40, 0xc7, 0x93, 0xe1, 0x6d, 0xb1, 0xed, 0xb1, 0xaf, + 0xa8, 0xc1, 0xd2, 0xd7, 0x8f, 0x3f, 0x40, 0x42, 0xd5, 0xb5, 0x2f, 0x2c, 0x9e, 0x30, 0x4f, 0x89, 0xb6, 0x85, 0x0f, + 0x61, 0xa1, 0x5f, 0x21, 0x32, 0x12, 0xc2, 0x4d, 0x65, 0xf7, 0x28, 0x69, 0x17, 0xfa, 0xd2, 0xd7, 0xb2, 0xaf, 0x7c, + 0xe7, 0x02, 0x60, 0x65, 0x9f, 0xdb, 0x70, 0x4f, 0xfa, 0x53, 0xaa, 0x0f, 0xdb, 0xdf, 0x92, 0x05, 0x14, 0x5a, 0x58, + 0x4f, 0xe5, 0xec, 0x5c, 0x97, 0x3c, 0xcd, 0xa6, 0xfb, 0x35, 0xec, 0x51, 0xf7, 0xe8, 0x35, 0x15, 0x9c, 0x5f, 0x9a, + 0xd1, 0xfb, 0x87, 0xa1, 0x50, 0x1d, 0x75, 0xee, 0x20, 0xeb, 0xd2, 0xba, 0xe4, 0xfc, 0x66, 0xe5, 0x8e, 0xc2, 0xfc, + 0x3e, 0x04, 0xcf, 0xb0, 0xee, 0xdd, 0xc5, 0x79, 0xef, 0xcf, 0xd6, 0x1c, 0xf9, 0x29, 0x9b, 0xa5, 0x88, 0x45, 0x32, + 0x07, 0xab, 0x1f, 0xfa, 0x79, 0xec, 0xb7, 0x41, 0x0e, 0xc7, 0x4d, 0x03, 0x3a, 0x6c, 0xc8, 0xac, 0x7d, 0x89, 0xc0, + 0xa9, 0x46, 0x90, 0xa6, 0x26, 0xa8, 0x59, 0x1e, 0x22, 0xb1, 0x5d, 0xca, 0xb6, 0x41, 0xae, 0xbb, 0x60, 0x9a, 0x23, + 0xed, 0x19, 0xbc, 0x6f, 0xd2, 0x24, 0x15, 0x9a, 0x45, 0xda, 0x2a, 0x19, 0xff, 0x8e, 0xb4, 0x99, 0x92, 0x3d, 0xb6, + 0x06, 0xde, 0x4b, 0x50, 0x4e, 0x86, 0x29, 0x86, 0xef, 0xf8, 0x7a, 0xe7, 0x31, 0xf7, 0x9c, 0x63, 0xb6, 0x49, 0xd9, + 0x11, 0x4c, 0x92, 0x8d, 0x6f, 0x28, 0xde, 0xf0, 0xc3, 0x6d, 0x25, 0x4a, 0x00, 0xbd, 0x2c, 0xf8, 0xb5, 0xb4, 0xb9, + 0x42, 0xb7, 0xbb, 0x77, 0x94, 0xc2, 0x2f, 0x79, 0x79, 0x38, 0x6c, 0x53, 0x2f, 0x84, 0xce, 0x17, 0xf1, 0x7b, 0x30, + 0x87, 0x31, 0xc4, 0x66, 0x04, 0x08, 0x73, 0x7c, 0x40, 0x1d, 0xac, 0x1f, 0x01, 0x68, 0x9c, 0x40, 0x01, 0x46, 0x5f, + 0x6d, 0x0b, 0xfa, 0x96, 0x17, 0x17, 0x11, 0xa2, 0x46, 0x01, 0x26, 0x4a, 0x9a, 0xc5, 0x30, 0x1c, 0xe8, 0xfc, 0xbe, + 0xb9, 0xad, 0x4b, 0x81, 0x43, 0xef, 0x58, 0x86, 0xff, 0xf6, 0x3f, 0xd6, 0x96, 0x56, 0x95, 0xed, 0xd6, 0x38, 0xcd, + 0xfc, 0x6f, 0xb7, 0x85, 0xbe, 0xff, 0x4a, 0x28, 0x9e, 0x77, 0xbc, 0x6e, 0xbf, 0x87, 0xe8, 0x7d, 0xdd, 0xca, 0xbb, + 0x52, 0xbb, 0x61, 0xa6, 0xfc, 0x21, 0xcd, 0xe3, 0xe2, 0x61, 0x14, 0xb7, 0x8e, 0xbc, 0x49, 0x7a, 0xce, 0xf9, 0xfb, + 0xaa, 0xdf, 0xf7, 0xde, 0x03, 0x19, 0xef, 0x2b, 0x61, 0x1c, 0x31, 0x89, 0x83, 0x6f, 0x2f, 0x46, 0xd1, 0xa6, 0x84, + 0x0d, 0xb9, 0x7d, 0x5a, 0x82, 0x66, 0xa6, 0xdf, 0x47, 0x89, 0xd2, 0x9a, 0xef, 0x7f, 0x91, 0xf3, 0xfd, 0x95, 0x90, + 0x37, 0x2b, 0xf9, 0xe1, 0xa3, 0x15, 0x06, 0xbe, 0xc7, 0xe9, 0x57, 0xd1, 0x63, 0x77, 0xa5, 0x0f, 0xdf, 0x95, 0x96, + 0x3e, 0xab, 0xa8, 0xbf, 0xa3, 0xa2, 0xe6, 0x95, 0x18, 0x11, 0xf1, 0x20, 0x68, 0x67, 0xdb, 0xa5, 0x76, 0x2d, 0x41, + 0xbb, 0x60, 0x53, 0xd8, 0xbf, 0x1e, 0x1d, 0xf2, 0x7e, 0xff, 0x53, 0xee, 0xb5, 0x78, 0xdd, 0x75, 0x68, 0xca, 0x5f, + 0x0b, 0x0f, 0x21, 0x80, 0xb5, 0x0c, 0x94, 0x71, 0x84, 0x49, 0x17, 0x79, 0x8d, 0xb2, 0xe9, 0x44, 0xe0, 0x63, 0x96, + 0x5d, 0x39, 0xc9, 0x34, 0xc0, 0x8c, 0x6a, 0x0a, 0x33, 0x01, 0x46, 0xea, 0x13, 0xd6, 0x4d, 0x4f, 0xab, 0xd0, 0xf2, + 0x35, 0x04, 0xeb, 0x22, 0xcb, 0x38, 0x8a, 0x99, 0x00, 0x60, 0xf3, 0x09, 0xe4, 0x2b, 0xba, 0x3a, 0x24, 0xad, 0x54, + 0x79, 0xbf, 0xce, 0x88, 0x8c, 0x26, 0x21, 0x9a, 0xdf, 0xc2, 0x03, 0xfb, 0xb6, 0x99, 0x51, 0xa5, 0x9e, 0x51, 0x95, + 0xcf, 0x70, 0x58, 0x0a, 0xc7, 0x88, 0xff, 0xb7, 0x54, 0xf5, 0x88, 0x40, 0xaf, 0xca, 0xb4, 0x8a, 0x8a, 0x3c, 0x17, + 0x11, 0x22, 0x54, 0x4b, 0xe7, 0x70, 0xe8, 0xc7, 0x7e, 0x1f, 0x07, 0xc2, 0xbc, 0xf8, 0xd7, 0xc7, 0xba, 0xf2, 0xaf, + 0x05, 0xae, 0x95, 0x14, 0x38, 0x15, 0x35, 0x42, 0x84, 0xf0, 0xfe, 0x04, 0x9e, 0xd5, 0xd4, 0xf7, 0x1b, 0xcb, 0x44, + 0xf7, 0x8f, 0x0c, 0x28, 0x7f, 0x40, 0xbe, 0xae, 0xa4, 0x38, 0x53, 0x27, 0x8f, 0x89, 0x33, 0x0e, 0x40, 0xcc, 0xb7, + 0x25, 0x1a, 0x8d, 0xfd, 0x0f, 0x48, 0x30, 0x54, 0x3f, 0xd8, 0xe9, 0xa6, 0xde, 0x3f, 0x33, 0x89, 0xa3, 0xe8, 0xd3, + 0x36, 0x79, 0x2c, 0x59, 0x1a, 0x2d, 0x1c, 0xbd, 0x47, 0x0c, 0xe3, 0x70, 0x3a, 0x1f, 0x93, 0x6c, 0x63, 0xb2, 0x0a, + 0x20, 0x9d, 0xcc, 0xd4, 0x31, 0xa5, 0x8e, 0xc6, 0xb9, 0x5e, 0x50, 0x85, 0x1e, 0xeb, 0x92, 0xe7, 0x60, 0x3d, 0xf9, + 0xd1, 0x2b, 0xfd, 0xa9, 0x90, 0x73, 0xd8, 0x48, 0x04, 0x85, 0x1f, 0xe0, 0x6a, 0xb0, 0x52, 0xc0, 0x60, 0xea, 0x5b, + 0xf8, 0x9a, 0x78, 0x8e, 0x82, 0x47, 0x61, 0x17, 0x63, 0x6b, 0xe5, 0x3b, 0x9f, 0x14, 0x94, 0x7b, 0x56, 0xcc, 0x79, + 0x05, 0x9c, 0xcb, 0xa0, 0x10, 0xa6, 0xe3, 0x59, 0xfe, 0xcf, 0x24, 0xaf, 0x27, 0x36, 0x04, 0xc8, 0xe0, 0x4f, 0x89, + 0xd3, 0xd2, 0x1d, 0xba, 0xf3, 0xd0, 0xb3, 0x88, 0xc3, 0x46, 0x4f, 0xd6, 0x65, 0xb1, 0x4d, 0x51, 0x2f, 0x61, 0x7e, + 0x20, 0x3f, 0x6f, 0xc9, 0xf7, 0x21, 0x8a, 0xb7, 0xc1, 0xaf, 0x19, 0x8b, 0x05, 0xfe, 0xf5, 0xb7, 0x8c, 0xd1, 0x44, + 0x0b, 0xfe, 0x95, 0x35, 0x48, 0x54, 0xfc, 0xd7, 0x6c, 0x02, 0xb0, 0x8e, 0x5c, 0x7d, 0xf8, 0x94, 0x18, 0x6f, 0xcd, + 0x86, 0x47, 0xbe, 0x59, 0x81, 0x4e, 0x7d, 0xee, 0xae, 0x6c, 0x4f, 0x55, 0xe3, 0x6f, 0xa9, 0xae, 0x46, 0xaa, 0xaa, + 0xf1, 0xb7, 0x94, 0xaa, 0xf1, 0x5b, 0x46, 0xf1, 0x3b, 0x95, 0xcf, 0x90, 0x39, 0xd9, 0xc4, 0x24, 0x9d, 0xbe, 0x37, + 0x9c, 0xd8, 0x65, 0xbf, 0x79, 0x9b, 0xc8, 0x4c, 0xa4, 0x90, 0x7b, 0x03, 0xd0, 0xf6, 0xbb, 0xdc, 0x70, 0x4a, 0x9c, + 0x9f, 0x7b, 0xb8, 0x62, 0xd3, 0xea, 0x15, 0x2d, 0x58, 0x60, 0xf3, 0x32, 0xcb, 0x53, 0x24, 0xb0, 0x6d, 0xca, 0xac, + 0x3f, 0xe7, 0x1e, 0x40, 0x30, 0x93, 0x9a, 0x00, 0x90, 0x16, 0xa2, 0x52, 0x88, 0xfc, 0x15, 0xce, 0xea, 0x73, 0xde, + 0xdb, 0xe4, 0x31, 0x91, 0x56, 0xf7, 0xfa, 0xfd, 0xf4, 0x2c, 0xcd, 0x29, 0xa8, 0xe1, 0x38, 0xeb, 0xf4, 0x65, 0x16, + 0xd4, 0x89, 0x5c, 0xa5, 0x7f, 0x77, 0x83, 0xbc, 0x8c, 0xef, 0xeb, 0xb6, 0xe7, 0x4f, 0xd4, 0xdf, 0x3b, 0xeb, 0x6f, + 0x0b, 0x04, 0x77, 0x72, 0xec, 0x27, 0xab, 0x52, 0x9e, 0x18, 0x97, 0xf6, 0x9e, 0xdf, 0xd4, 0x45, 0x91, 0xd5, 0xe9, + 0xfa, 0xa3, 0xd4, 0xd3, 0xe8, 0xbe, 0xd8, 0x83, 0x31, 0x78, 0x07, 0x80, 0x67, 0x3a, 0x34, 0x40, 0xfa, 0x9e, 0x91, + 0x87, 0xfb, 0xdc, 0x92, 0x9f, 0x54, 0xd6, 0x26, 0x09, 0x2b, 0x8a, 0xcd, 0x30, 0x46, 0x28, 0x19, 0xa7, 0xb1, 0xf5, + 0xfb, 0x7d, 0xf5, 0xf7, 0x0e, 0xa3, 0xa8, 0xa8, 0xb8, 0x63, 0x34, 0x2a, 0xab, 0x7a, 0xb4, 0x1d, 0x1c, 0x0e, 0xe7, + 0xb9, 0x8d, 0xa3, 0xad, 0x57, 0xc0, 0xde, 0x0a, 0x95, 0xb2, 0x57, 0x22, 0x2c, 0x3f, 0x5c, 0xf9, 0xfd, 0x3e, 0xfc, + 0x2b, 0x23, 0x2d, 0x3c, 0x7f, 0x8a, 0xbf, 0x16, 0x75, 0x81, 0xe1, 0x19, 0xb4, 0x46, 0x2b, 0x08, 0x26, 0xf8, 0x7b, + 0x07, 0xea, 0xa5, 0x95, 0xf6, 0x09, 0x74, 0x2b, 0xd0, 0x83, 0x7a, 0xe8, 0xd3, 0xa4, 0x7d, 0x21, 0x51, 0xb7, 0xb7, + 0x3a, 0x8d, 0xfe, 0xa8, 0xe0, 0x72, 0x0a, 0x93, 0xc3, 0x0d, 0x7d, 0x5a, 0x85, 0xdb, 0xcf, 0xf0, 0xf4, 0x67, 0xa0, + 0xdc, 0x3a, 0x1c, 0x72, 0x10, 0x5b, 0xc0, 0xcd, 0x63, 0x15, 0x7e, 0x29, 0x4a, 0x19, 0x51, 0x1f, 0x4f, 0x0b, 0xd0, + 0xde, 0x05, 0xe8, 0x80, 0xa5, 0x41, 0xbc, 0x42, 0xf2, 0x9c, 0x8d, 0x00, 0x96, 0x1d, 0x58, 0xce, 0x32, 0x4e, 0x61, + 0x9e, 0xe5, 0xb5, 0x5a, 0x69, 0x67, 0x65, 0xe2, 0xd5, 0x2c, 0x03, 0x67, 0x81, 0x8b, 0xca, 0x67, 0x99, 0x56, 0x3d, + 0x55, 0x09, 0xfa, 0xbc, 0x92, 0x13, 0x5c, 0x09, 0x4e, 0x36, 0x20, 0xbf, 0x00, 0x49, 0x9a, 0x52, 0xd6, 0x94, 0xd7, + 0x97, 0x74, 0x43, 0x46, 0xcf, 0x79, 0xcf, 0x8b, 0x86, 0xa1, 0x7f, 0xe1, 0x95, 0x10, 0xbe, 0x89, 0xdb, 0x36, 0x4a, + 0x61, 0x7f, 0x11, 0x58, 0x7c, 0xc2, 0x7e, 0xf4, 0x96, 0xfe, 0x74, 0x1c, 0x84, 0x43, 0xe4, 0x86, 0x8a, 0x39, 0xb0, + 0xa7, 0x01, 0x8b, 0x4d, 0x7c, 0xb5, 0x99, 0xc4, 0x83, 0x81, 0xaf, 0x33, 0x16, 0xb3, 0x18, 0x68, 0x90, 0xe3, 0xc1, + 0xe5, 0x5c, 0x9f, 0x10, 0xfa, 0x61, 0x44, 0xe5, 0xa8, 0x40, 0xe7, 0x20, 0x1a, 0x2c, 0x01, 0x4f, 0xbd, 0x95, 0x0d, + 0x92, 0x8c, 0x49, 0x26, 0x71, 0xad, 0x49, 0xaa, 0xc3, 0x09, 0xad, 0x03, 0x1d, 0x57, 0x17, 0xd0, 0xf9, 0xb8, 0xee, + 0x7d, 0xbc, 0x1a, 0x2e, 0xa8, 0xf4, 0x0b, 0x31, 0xf0, 0xea, 0xe9, 0x38, 0xb8, 0xa4, 0x5b, 0xe1, 0x62, 0x15, 0x6e, + 0x7f, 0x96, 0x0f, 0x1c, 0x77, 0x54, 0xd2, 0x10, 0x18, 0xbc, 0x3d, 0x74, 0x37, 0x33, 0x34, 0xd4, 0x49, 0xfb, 0x30, + 0x0e, 0xe5, 0x10, 0xab, 0x56, 0x5c, 0x48, 0x6f, 0x04, 0xdf, 0x2e, 0x14, 0x63, 0xd9, 0xd8, 0xa5, 0xa1, 0x28, 0xfc, + 0x15, 0xc0, 0x0e, 0xb5, 0xbf, 0x52, 0xc9, 0xc7, 0xc8, 0xa8, 0xa6, 0x81, 0x8e, 0x01, 0x58, 0xb2, 0x34, 0x91, 0x54, + 0x91, 0x46, 0xe2, 0x8f, 0xcc, 0x58, 0x47, 0x4d, 0xd7, 0x17, 0x4c, 0x55, 0x8b, 0xa4, 0xdb, 0x99, 0xc4, 0x72, 0x22, + 0x49, 0x6d, 0xf7, 0x11, 0x31, 0x18, 0xf8, 0x60, 0x23, 0xa6, 0x99, 0x08, 0x47, 0x3c, 0x2a, 0x91, 0x45, 0x97, 0xdf, + 0x46, 0x99, 0xb4, 0x7d, 0x59, 0x91, 0x2d, 0x08, 0xa6, 0x27, 0xd1, 0x07, 0x49, 0xca, 0xa9, 0x48, 0xa4, 0x19, 0x21, + 0xc0, 0x8f, 0x27, 0xe5, 0x95, 0xfe, 0x1c, 0x34, 0xad, 0x04, 0x2f, 0x19, 0x24, 0x8f, 0xc4, 0xcf, 0xa4, 0x60, 0x16, + 0x63, 0xd5, 0x60, 0x80, 0xe5, 0x54, 0xcf, 0x1c, 0x93, 0xf4, 0x5f, 0x3a, 0x9d, 0xb0, 0x5f, 0x78, 0xb9, 0xad, 0xe5, + 0x4d, 0x73, 0xef, 0x85, 0x57, 0xb1, 0x54, 0xc3, 0x32, 0xe8, 0xbf, 0x26, 0xda, 0x05, 0x5b, 0x5b, 0xc6, 0x84, 0x55, + 0x3f, 0x80, 0xb4, 0x47, 0xba, 0xbc, 0x6a, 0x98, 0x33, 0xc1, 0xa3, 0x0b, 0x6b, 0x1e, 0x44, 0x17, 0xc2, 0x47, 0x2e, + 0xbb, 0x49, 0x72, 0x35, 0x9e, 0xf8, 0xe1, 0x60, 0xa0, 0x00, 0x68, 0x69, 0x9d, 0x14, 0x83, 0xf0, 0x99, 0x90, 0x03, + 0x69, 0x74, 0x54, 0x05, 0x58, 0x2c, 0xb3, 0xab, 0x72, 0x92, 0x0d, 0x06, 0x3e, 0x88, 0x8d, 0x89, 0xdd, 0xd0, 0x6c, + 0xee, 0xb3, 0x13, 0x05, 0x59, 0x6d, 0x0e, 0x5b, 0x33, 0xdd, 0x02, 0x03, 0x80, 0x41, 0x44, 0xb0, 0xdc, 0xe7, 0x46, + 0x3e, 0xa2, 0x4e, 0x4f, 0x61, 0x04, 0x04, 0xbf, 0x9c, 0x08, 0x44, 0x2e, 0x12, 0xa8, 0x07, 0x98, 0x09, 0x30, 0xa3, + 0x8a, 0xe1, 0x25, 0xb0, 0x8b, 0xe7, 0xe6, 0x15, 0x83, 0xfe, 0x45, 0x93, 0x2c, 0xd1, 0x54, 0xe2, 0x68, 0x8c, 0x9c, + 0x4a, 0x63, 0x64, 0x40, 0xec, 0xe2, 0xf8, 0xf7, 0x94, 0x1e, 0x05, 0x29, 0xfb, 0x52, 0x19, 0xe2, 0x70, 0x14, 0x5f, + 0xc1, 0xaa, 0x71, 0x38, 0xd4, 0xe6, 0xf5, 0x74, 0x56, 0xcf, 0x07, 0x22, 0x80, 0xff, 0x86, 0x82, 0xfd, 0xa2, 0xa9, + 0xc8, 0x0d, 0x52, 0xe7, 0xe1, 0x90, 0x82, 0x7c, 0xaa, 0x9b, 0xfc, 0xb2, 0x72, 0xf7, 0xd3, 0xd9, 0xdc, 0x9a, 0xa3, + 0x17, 0x35, 0xae, 0x5b, 0xab, 0x1b, 0x0a, 0x89, 0xd6, 0x34, 0x29, 0xae, 0xaa, 0x49, 0x31, 0xe0, 0xb9, 0x2f, 0x54, + 0x17, 0x5b, 0x23, 0x58, 0xf8, 0x73, 0x0b, 0x84, 0xc9, 0xb8, 0x17, 0x1f, 0x2d, 0xe4, 0x94, 0x76, 0x6d, 0xb5, 0xdb, + 0x56, 0x36, 0xa4, 0x68, 0x3e, 0xbc, 0x84, 0x5d, 0x3a, 0x45, 0xb4, 0xed, 0x92, 0xe0, 0x0b, 0xd0, 0xb2, 0xba, 0x10, + 0x79, 0x4c, 0xbf, 0x42, 0x7e, 0x29, 0x86, 0xff, 0x29, 0xdd, 0x9b, 0x53, 0x1b, 0xe4, 0x00, 0xb6, 0x7b, 0x0f, 0xb7, + 0x63, 0xf4, 0x40, 0x06, 0x6f, 0x84, 0x9c, 0x73, 0x7e, 0x39, 0xb5, 0x66, 0x4c, 0x34, 0x2c, 0x58, 0x39, 0x8c, 0xfc, + 0x00, 0x19, 0x2f, 0xa7, 0xc0, 0xca, 0x7e, 0x54, 0xc4, 0xa5, 0x3f, 0x8c, 0xfc, 0x8b, 0xe7, 0x41, 0xc6, 0xbd, 0x68, + 0xd8, 0xf1, 0x05, 0xd8, 0xab, 0x2f, 0x9e, 0xb3, 0x68, 0xc0, 0xab, 0xab, 0x7a, 0x9a, 0x05, 0xc3, 0x8c, 0x45, 0x57, + 0xc5, 0x10, 0x7c, 0x68, 0xaf, 0xcb, 0x41, 0xe8, 0xfb, 0x66, 0xe7, 0xd0, 0xdd, 0x90, 0xc8, 0x23, 0xec, 0x27, 0x70, + 0xdb, 0xd5, 0x12, 0x33, 0x98, 0x6c, 0xee, 0x22, 0x66, 0xb0, 0xe5, 0x2f, 0x9e, 0x1b, 0x2e, 0xa1, 0xea, 0x5a, 0x6a, + 0x36, 0x0a, 0x34, 0x27, 0x57, 0x68, 0x4e, 0x56, 0x42, 0x2d, 0xf9, 0xa4, 0xc2, 0x09, 0x3b, 0x9f, 0xe4, 0xca, 0x6e, + 0x34, 0xc6, 0xc0, 0x45, 0x7b, 0x6e, 0x0b, 0x23, 0x33, 0x9d, 0xa5, 0x68, 0xc0, 0xc2, 0x33, 0x71, 0x4a, 0x63, 0x40, + 0xfb, 0x72, 0x60, 0x69, 0x43, 0x7e, 0x92, 0x33, 0x03, 0x6d, 0x43, 0x4a, 0xa3, 0x66, 0xe0, 0xcf, 0xd4, 0x84, 0xf9, + 0x15, 0xac, 0x44, 0x10, 0xd5, 0x05, 0x98, 0x24, 0x39, 0x19, 0x8d, 0x94, 0x95, 0x48, 0xce, 0x01, 0xef, 0x13, 0x78, + 0xb2, 0x88, 0x6d, 0xed, 0x4f, 0xe9, 0x7f, 0x75, 0xf8, 0x5c, 0xfa, 0xcf, 0x04, 0xb0, 0x90, 0x4b, 0x83, 0xc8, 0x40, + 0xe1, 0x90, 0x9a, 0x4a, 0xc4, 0x89, 0xe3, 0x19, 0xf8, 0x06, 0x2e, 0xd0, 0x14, 0xd0, 0x1f, 0xd4, 0x8c, 0x22, 0xb2, + 0xf0, 0x57, 0xcf, 0x6e, 0xea, 0x46, 0xcf, 0x33, 0xe7, 0x35, 0x68, 0x66, 0x20, 0xa4, 0xc7, 0xa9, 0x7a, 0x1b, 0x12, + 0x9d, 0x97, 0x97, 0xfa, 0x65, 0x42, 0x24, 0x2b, 0x22, 0x4f, 0xdf, 0xe7, 0x60, 0x1e, 0x51, 0x84, 0x0e, 0xae, 0xcc, + 0xc3, 0xe1, 0x5c, 0x50, 0xf8, 0x8e, 0xf2, 0x7c, 0xc0, 0x69, 0x16, 0x25, 0xa0, 0x0d, 0x64, 0xb9, 0x29, 0x73, 0x9d, + 0xb4, 0x4c, 0xdd, 0x7b, 0xb0, 0x12, 0x54, 0xe8, 0xe6, 0x14, 0x14, 0xca, 0x48, 0x50, 0x4a, 0xab, 0x41, 0x28, 0xd5, + 0x61, 0x11, 0x44, 0x0e, 0x59, 0x08, 0xb8, 0x99, 0x8a, 0x46, 0x4b, 0x1a, 0x1e, 0xe1, 0xdc, 0x40, 0x21, 0x00, 0x89, + 0x3d, 0x55, 0x94, 0x71, 0x39, 0x04, 0x7c, 0x94, 0x70, 0x88, 0xb3, 0x26, 0x6d, 0x79, 0x0e, 0xe2, 0x58, 0x2e, 0xf9, + 0xba, 0x42, 0x30, 0x88, 0xd0, 0x67, 0xc8, 0x9f, 0x2c, 0xe7, 0xdf, 0xad, 0xc3, 0xb4, 0x23, 0x7c, 0xd8, 0xd5, 0x16, + 0x5c, 0xcc, 0x6e, 0xe7, 0x13, 0x88, 0x6f, 0xb9, 0x9d, 0x1f, 0x63, 0x88, 0x2c, 0xfc, 0xc1, 0xdd, 0x50, 0x72, 0x45, + 0xa1, 0xcb, 0x7a, 0x44, 0x8a, 0xec, 0xe9, 0x9a, 0x23, 0x08, 0x0e, 0xb4, 0x6a, 0x90, 0xa1, 0x91, 0xf8, 0xe2, 0x39, + 0x64, 0x0d, 0xd6, 0xfc, 0x4b, 0x45, 0xce, 0xea, 0xfe, 0x64, 0x03, 0xd5, 0x24, 0x93, 0xb5, 0xa2, 0x72, 0xfe, 0x76, + 0x55, 0x96, 0x27, 0xab, 0x32, 0x5c, 0x0d, 0xba, 0xaa, 0xb2, 0xe4, 0x48, 0x6d, 0x80, 0xd6, 0x74, 0x85, 0x18, 0x0a, + 0x59, 0x83, 0xa5, 0x55, 0x95, 0x35, 0xf5, 0x09, 0x04, 0xfa, 0x00, 0xcb, 0xa8, 0xd9, 0x4f, 0x87, 0xff, 0x0c, 0xfe, + 0xa9, 0x42, 0x96, 0xea, 0xb4, 0xce, 0xc4, 0xaf, 0xc1, 0x92, 0xe1, 0x1f, 0xbf, 0x05, 0x6b, 0xc0, 0x12, 0x20, 0xcb, + 0xdd, 0xc6, 0x46, 0xeb, 0x95, 0x57, 0x88, 0xaf, 0xb5, 0xbe, 0xe8, 0xb7, 0x6e, 0x13, 0xb5, 0x02, 0x8c, 0x50, 0x68, + 0x11, 0x60, 0xab, 0x07, 0xee, 0x29, 0xf8, 0x81, 0x18, 0xce, 0x35, 0x69, 0x4d, 0x9d, 0xf0, 0x3a, 0x1b, 0x47, 0x22, + 0xaa, 0xb7, 0x70, 0x71, 0xaf, 0xb7, 0x16, 0x7f, 0xa3, 0x02, 0x01, 0x90, 0xc5, 0x14, 0x6b, 0xe7, 0x0d, 0xe9, 0x95, + 0x61, 0x27, 0xa1, 0xf7, 0x86, 0x9d, 0x40, 0x5e, 0x1c, 0x76, 0x0a, 0x5d, 0xa2, 0xed, 0x14, 0xa9, 0x89, 0xb6, 0x93, + 0x16, 0xab, 0xb0, 0x84, 0xe0, 0x57, 0xed, 0xad, 0xa3, 0x6c, 0x5f, 0x64, 0x09, 0xd3, 0x16, 0x30, 0xca, 0xad, 0xfa, + 0xcc, 0x29, 0x62, 0xa5, 0xec, 0x9d, 0x4e, 0xaa, 0xdc, 0x45, 0x3e, 0xb7, 0x9a, 0x22, 0x93, 0x5f, 0x1e, 0xb7, 0x48, + 0x3e, 0xf9, 0xb9, 0xdd, 0x30, 0x99, 0xfe, 0xe9, 0xe8, 0x0b, 0xe8, 0x8a, 0xec, 0xf4, 0x09, 0x04, 0x64, 0x2a, 0xa8, + 0x56, 0xb7, 0x8a, 0x69, 0xde, 0xae, 0xb2, 0xdb, 0x0b, 0x25, 0x86, 0xd3, 0xd9, 0x49, 0x78, 0xb4, 0x19, 0x32, 0x70, + 0x08, 0x02, 0x85, 0x50, 0x51, 0x0c, 0x8f, 0x40, 0xad, 0x91, 0x7c, 0x80, 0x1f, 0xed, 0x4e, 0x05, 0x91, 0xda, 0x4d, + 0xc5, 0x8d, 0x93, 0x9b, 0xae, 0x97, 0x02, 0xb5, 0x4e, 0xc9, 0x0a, 0xa0, 0x84, 0xa8, 0x3f, 0x8b, 0x6d, 0xfd, 0x0a, + 0xae, 0xd8, 0x7c, 0xdf, 0x28, 0x7a, 0x72, 0x7d, 0x8a, 0xba, 0x15, 0x57, 0xa7, 0x69, 0xab, 0x39, 0x76, 0x9c, 0x21, + 0x07, 0xcf, 0x0a, 0x82, 0xed, 0xa8, 0x44, 0xf9, 0xae, 0xdd, 0x74, 0x4c, 0x6c, 0xf5, 0xcf, 0xa2, 0xda, 0xdc, 0x41, + 0x45, 0x44, 0x7c, 0x94, 0xdd, 0x3c, 0x69, 0xbf, 0x83, 0x3d, 0xd6, 0x6a, 0x10, 0xd9, 0x67, 0x70, 0x95, 0xeb, 0xb4, + 0xc8, 0x6d, 0x19, 0x9c, 0x7f, 0x78, 0xb5, 0xab, 0xb0, 0xc9, 0xb1, 0xae, 0xae, 0x66, 0xaa, 0x93, 0x8a, 0x0d, 0x8c, + 0x35, 0xad, 0xa5, 0x9a, 0xc7, 0x90, 0x74, 0x57, 0x16, 0x67, 0x55, 0xd2, 0x4d, 0xcf, 0x8d, 0x33, 0x85, 0x18, 0x38, + 0x5b, 0x8d, 0x96, 0x33, 0x0c, 0xd1, 0xf5, 0x61, 0x96, 0xf8, 0xad, 0x9e, 0x72, 0x9f, 0x87, 0x5b, 0xbf, 0xab, 0x17, + 0x9c, 0x4c, 0xf6, 0x93, 0xe3, 0xdc, 0xed, 0x22, 0xed, 0x27, 0xbe, 0x0d, 0xf3, 0xaf, 0x6f, 0x10, 0x77, 0xa2, 0xfe, + 0x47, 0x05, 0x40, 0x83, 0x9b, 0x3c, 0x96, 0x28, 0xf5, 0x7b, 0x55, 0xfd, 0xa0, 0x66, 0xaa, 0xa6, 0x81, 0x60, 0x4e, + 0xa5, 0x80, 0x3f, 0xdc, 0x2e, 0x5c, 0xf1, 0x88, 0x1b, 0x16, 0xc6, 0x3f, 0xbd, 0x9a, 0x9d, 0x0a, 0x2a, 0x03, 0x37, + 0xe3, 0x3f, 0x3d, 0xc1, 0x4e, 0x61, 0xad, 0x80, 0xac, 0xf0, 0xa7, 0x97, 0x3f, 0xf2, 0x7e, 0xc5, 0xff, 0xf4, 0xaa, + 0x47, 0xde, 0x47, 0x9c, 0x97, 0x3f, 0x91, 0xd4, 0x09, 0x51, 0x5d, 0xfe, 0x24, 0x4c, 0xb1, 0x55, 0x9a, 0xbf, 0x26, + 0x85, 0x4f, 0xf0, 0x05, 0xf8, 0x0e, 0x57, 0xe1, 0xd6, 0xfc, 0x06, 0x8f, 0x1d, 0x8b, 0x6d, 0x97, 0xfa, 0x02, 0xca, + 0x11, 0x58, 0x44, 0x6e, 0xbf, 0x5d, 0xd9, 0xaf, 0x16, 0x46, 0x19, 0x63, 0xf7, 0x25, 0x2b, 0x51, 0x3a, 0xeb, 0xf7, + 0x0b, 0x29, 0x18, 0xd9, 0x85, 0x35, 0xda, 0xa3, 0x54, 0xbd, 0xfa, 0x2e, 0xac, 0xa3, 0x24, 0xcd, 0xef, 0x64, 0xf4, + 0x91, 0x0c, 0x3b, 0xd2, 0x57, 0x52, 0xa2, 0xbd, 0x56, 0x61, 0x39, 0x9a, 0xfd, 0xba, 0xe4, 0x40, 0x79, 0xdd, 0x0a, + 0xca, 0x57, 0x4d, 0x00, 0xbd, 0x52, 0xed, 0x33, 0xd0, 0x0a, 0x0a, 0x4b, 0xe5, 0xc1, 0x4a, 0x9c, 0x8b, 0x3e, 0x2b, + 0x0e, 0x07, 0x75, 0x31, 0x24, 0x14, 0xa8, 0x12, 0x27, 0xa1, 0x11, 0xcf, 0xe1, 0x42, 0x28, 0xae, 0x73, 0x8c, 0xad, + 0xc8, 0x81, 0x03, 0x19, 0x7e, 0x40, 0xe0, 0xbd, 0xec, 0x5f, 0xc1, 0x60, 0x98, 0xe0, 0x46, 0x46, 0x9d, 0x9c, 0xb3, + 0x3f, 0x31, 0x30, 0x83, 0x7a, 0x52, 0xbb, 0xcf, 0xee, 0x55, 0x60, 0x2f, 0x9c, 0x01, 0xed, 0xdd, 0x18, 0xfd, 0xac, + 0x8a, 0xb5, 0x93, 0xfe, 0xb9, 0x58, 0x43, 0x32, 0x1d, 0x16, 0x47, 0xdb, 0x34, 0x3c, 0x92, 0x27, 0xc7, 0xf1, 0xa6, + 0x7f, 0x38, 0x8c, 0xf1, 0xe3, 0x28, 0xbf, 0xb6, 0x80, 0x57, 0x71, 0x0b, 0x69, 0x2c, 0x52, 0xf4, 0x0e, 0xc4, 0x1c, + 0x8a, 0x5e, 0xb2, 0xdf, 0x32, 0x5e, 0x4e, 0x04, 0xa5, 0x24, 0xb1, 0xe1, 0x1d, 0xe9, 0x69, 0x5a, 0x8f, 0xb6, 0x32, + 0x60, 0xbf, 0x1e, 0xed, 0xe8, 0x2f, 0x50, 0x3c, 0x5a, 0xf8, 0x4b, 0xfa, 0xbb, 0xb8, 0x9b, 0x7b, 0xce, 0x37, 0x8d, + 0xef, 0x88, 0x0b, 0x14, 0x6b, 0x76, 0x7f, 0x4d, 0x4b, 0x67, 0x1d, 0x08, 0x0e, 0x78, 0x8b, 0x5d, 0xb4, 0xef, 0x37, + 0xae, 0xd3, 0xd3, 0xfe, 0x7b, 0xb7, 0x46, 0xf9, 0xde, 0x3f, 0x24, 0xca, 0xc1, 0xfe, 0xb5, 0x8b, 0xe6, 0x6f, 0x3f, + 0x65, 0x48, 0x2a, 0x34, 0x37, 0xd8, 0x4e, 0xb6, 0x08, 0x6b, 0x63, 0x1c, 0x54, 0xec, 0xae, 0x0c, 0x23, 0x60, 0x50, + 0xc7, 0xfe, 0x47, 0x9f, 0x4d, 0x1b, 0xb2, 0x0f, 0x00, 0x95, 0xab, 0x10, 0xb0, 0x07, 0xe0, 0x44, 0x23, 0xdc, 0x00, + 0xb7, 0x1a, 0x2d, 0xe9, 0xa0, 0x6e, 0x0b, 0x06, 0xa2, 0x25, 0x6c, 0xe4, 0x6d, 0x57, 0xa7, 0x6f, 0x08, 0x1f, 0x6a, + 0x27, 0xa5, 0x43, 0xf9, 0x9b, 0xe7, 0xec, 0xbf, 0x77, 0x58, 0x53, 0x53, 0x6e, 0x00, 0x33, 0x67, 0x25, 0xf2, 0x0a, + 0xa1, 0x53, 0xe4, 0xf7, 0xaa, 0xae, 0xc4, 0x70, 0x59, 0x8b, 0xb2, 0x33, 0xbb, 0x75, 0xa2, 0x77, 0x4e, 0x41, 0x2d, + 0x95, 0x0d, 0x72, 0x92, 0x6a, 0xf3, 0x91, 0xb5, 0x82, 0x12, 0x75, 0x8d, 0x02, 0xc7, 0xa7, 0x5c, 0xbb, 0xff, 0x77, + 0xce, 0x04, 0x35, 0xdb, 0xa8, 0xee, 0xaf, 0xf5, 0x53, 0x55, 0x93, 0x58, 0x80, 0xcb, 0x49, 0x9a, 0x77, 0x3c, 0xc2, + 0xea, 0x1f, 0x27, 0x4b, 0x11, 0xe8, 0x75, 0x44, 0xbb, 0x12, 0x90, 0xa0, 0x9d, 0x9c, 0x85, 0x8a, 0x40, 0x81, 0xbe, + 0xfe, 0x72, 0x93, 0x66, 0xb1, 0x5c, 0xcd, 0xf6, 0x30, 0x51, 0x16, 0xeb, 0x21, 0x82, 0x9c, 0x99, 0x3a, 0xd8, 0xef, + 0x69, 0x46, 0xb3, 0xf0, 0xca, 0x94, 0xe0, 0x52, 0x5c, 0x45, 0x45, 0x0e, 0x3e, 0x87, 0xf8, 0xc2, 0xe7, 0x42, 0x6e, + 0x10, 0xd1, 0xf4, 0xa5, 0x44, 0xb5, 0x23, 0x05, 0x72, 0x28, 0xf9, 0x09, 0xf1, 0x97, 0xac, 0x8d, 0x71, 0xbf, 0x74, + 0xaa, 0xfd, 0x4a, 0x21, 0xb8, 0xff, 0x6c, 0x8b, 0x8d, 0x2a, 0x4f, 0xf4, 0xe8, 0x53, 0xac, 0xff, 0xc9, 0x02, 0x4a, + 0x75, 0xdf, 0x06, 0xa7, 0xe2, 0x51, 0xb8, 0xa9, 0x8b, 0x1b, 0x84, 0x16, 0x28, 0x47, 0x55, 0xb1, 0x29, 0x23, 0xe2, + 0x84, 0xdd, 0xd4, 0x45, 0x4f, 0x73, 0xa0, 0x53, 0x87, 0xa5, 0x89, 0x3c, 0x11, 0xda, 0x2d, 0xe8, 0x9e, 0xe6, 0x58, + 0x89, 0x17, 0xb2, 0x74, 0x90, 0x75, 0x22, 0x4d, 0xa8, 0xdc, 0xd5, 0x55, 0x47, 0xa5, 0x52, 0x37, 0xbc, 0x49, 0x35, + 0xe3, 0xef, 0xd2, 0xfc, 0x89, 0x65, 0xbf, 0x69, 0xfd, 0x56, 0xab, 0xbd, 0xb1, 0x7a, 0x54, 0xb2, 0xe6, 0x38, 0x9b, + 0x90, 0x94, 0x3e, 0x61, 0xbb, 0x99, 0x74, 0xad, 0x03, 0x4f, 0x82, 0xcb, 0xa1, 0x27, 0xa0, 0x62, 0xd0, 0xc4, 0xdb, + 0x5d, 0xa0, 0x1e, 0x81, 0x67, 0xa0, 0x7c, 0xa2, 0xd6, 0x01, 0x3f, 0xaf, 0xb5, 0x3c, 0x65, 0x84, 0x61, 0xb5, 0xb3, + 0x68, 0x39, 0x38, 0xef, 0x14, 0x81, 0x6b, 0x57, 0x02, 0xcf, 0x87, 0xea, 0xbd, 0x10, 0x30, 0xdc, 0x3f, 0x17, 0x2a, + 0x9b, 0xdd, 0x0c, 0xe7, 0x51, 0xe3, 0xf4, 0x40, 0x7b, 0xdb, 0xb5, 0x1e, 0xea, 0x5d, 0xb7, 0x73, 0x5b, 0xe9, 0xde, + 0xaf, 0x9d, 0x4c, 0xba, 0x80, 0xd6, 0xe6, 0xb3, 0xef, 0xec, 0x4a, 0xeb, 0xa6, 0xe7, 0xec, 0xc1, 0xd6, 0x2d, 0xd1, + 0xb9, 0x20, 0x9a, 0xfc, 0x7e, 0xe0, 0x59, 0xdb, 0x8e, 0x7e, 0x9b, 0x76, 0x6c, 0x73, 0x0f, 0x75, 0xaf, 0xa0, 0xd6, + 0x1b, 0x9a, 0xf7, 0xcf, 0x5c, 0xdb, 0x8e, 0xaf, 0x7e, 0x5d, 0x77, 0xb8, 0xce, 0x9b, 0xe0, 0xb8, 0xe9, 0xda, 0x56, + 0x3b, 0xfb, 0xb9, 0xbb, 0xb7, 0x16, 0x51, 0x98, 0x65, 0x3f, 0x15, 0xc5, 0x1f, 0x95, 0xbe, 0x23, 0xd0, 0xd1, 0x9d, + 0x17, 0x75, 0xba, 0xdc, 0x7d, 0x24, 0x8c, 0x27, 0xaf, 0x3e, 0x22, 0xba, 0xf5, 0x7d, 0xe6, 0x7e, 0x05, 0xb8, 0x11, + 0xdc, 0x41, 0xb4, 0x77, 0x4b, 0x7d, 0x52, 0xab, 0xaf, 0xf5, 0xda, 0x79, 0x7a, 0x7e, 0xd3, 0xb9, 0xfd, 0xee, 0x9b, + 0xa3, 0xad, 0xf7, 0xb8, 0xb0, 0x56, 0x96, 0x9e, 0xaa, 0x82, 0xbd, 0x59, 0x9e, 0xaa, 0x82, 0xc9, 0x03, 0xaf, 0xd9, + 0x2f, 0x68, 0x70, 0xa5, 0xa3, 0x8d, 0xf7, 0x44, 0x0d, 0xdc, 0xa2, 0xb0, 0x74, 0xf8, 0x25, 0x37, 0x93, 0x57, 0xb8, + 0xbf, 0x54, 0xe4, 0x62, 0xdf, 0x39, 0xa3, 0x3b, 0x33, 0xeb, 0x5e, 0x55, 0xb8, 0x5a, 0x90, 0xab, 0x03, 0x5b, 0xcb, + 0x2e, 0x0e, 0x37, 0x2c, 0xa2, 0x00, 0x81, 0x98, 0x5e, 0xa9, 0xb5, 0x3f, 0xa2, 0x41, 0xc8, 0x07, 0x03, 0xbf, 0xc0, + 0x60, 0x55, 0xa0, 0xf0, 0x81, 0x22, 0xf9, 0x6b, 0x4f, 0xc0, 0x2e, 0x9e, 0x01, 0xba, 0x15, 0x9b, 0x15, 0x23, 0x44, + 0xc8, 0x64, 0x39, 0xab, 0xe9, 0x0c, 0xf2, 0xa9, 0x2f, 0xbe, 0xb3, 0x55, 0xa7, 0xf3, 0xb6, 0xa6, 0xca, 0xa9, 0x43, + 0xa1, 0xbb, 0x9b, 0xba, 0x73, 0xeb, 0x22, 0x4f, 0x1d, 0x42, 0xae, 0x54, 0xac, 0xc4, 0x34, 0xd4, 0x3c, 0x49, 0x33, + 0xea, 0x2f, 0xf6, 0x7e, 0xaf, 0x51, 0x38, 0xe5, 0x4f, 0xc7, 0xa0, 0x0a, 0x57, 0x35, 0xc4, 0xb1, 0x54, 0xc5, 0x23, + 0x1b, 0x04, 0x9a, 0x57, 0xb7, 0x2a, 0x69, 0x42, 0x26, 0x37, 0xc2, 0xa7, 0x26, 0xa5, 0x3c, 0x4d, 0x9b, 0xb4, 0x52, + 0xa4, 0x0e, 0x3e, 0xa8, 0x53, 0x8d, 0xe7, 0x66, 0x75, 0x0d, 0x60, 0xc6, 0xf9, 0x15, 0xbf, 0x54, 0x5c, 0x46, 0x6d, + 0x65, 0x26, 0xed, 0x4f, 0x8e, 0xc6, 0x46, 0x5d, 0x4e, 0x1b, 0x65, 0x84, 0x95, 0xd2, 0x9c, 0x14, 0xcb, 0xf1, 0xfc, + 0x03, 0x06, 0x6b, 0x9e, 0xc0, 0x0e, 0x26, 0x2a, 0xe5, 0x7d, 0x04, 0xc4, 0xd7, 0x49, 0x7a, 0x97, 0x40, 0x8a, 0xf4, + 0x2f, 0x5d, 0xf2, 0xd4, 0x61, 0x6c, 0x20, 0xc6, 0xac, 0x98, 0x19, 0xfd, 0x0f, 0xee, 0x92, 0xfe, 0x24, 0x04, 0xc0, + 0x4d, 0x34, 0x85, 0x4e, 0x9d, 0x27, 0x17, 0x79, 0xb0, 0xbc, 0xf0, 0xd0, 0x8a, 0x11, 0x0f, 0xfe, 0xf3, 0x3a, 0x44, + 0x10, 0x73, 0x4c, 0xf1, 0xf4, 0x0b, 0xa3, 0xff, 0x08, 0x2e, 0x31, 0x82, 0xd0, 0xdd, 0x3b, 0x87, 0x21, 0xdc, 0xec, + 0x41, 0x06, 0xf5, 0x87, 0x3a, 0x24, 0x6a, 0xf8, 0x53, 0xe5, 0x41, 0xff, 0xd7, 0x99, 0xb0, 0xd4, 0x7e, 0x7a, 0x3a, + 0x80, 0x0a, 0xde, 0x57, 0xbc, 0x8d, 0x88, 0xef, 0x13, 0x3f, 0x8b, 0x07, 0x9b, 0x67, 0x1b, 0xb0, 0xd6, 0x3d, 0xc9, + 0x8d, 0x75, 0x95, 0xb0, 0x81, 0x80, 0xaf, 0x31, 0xad, 0x3d, 0xaf, 0xdd, 0xee, 0xc1, 0x7f, 0xfa, 0x17, 0x21, 0x03, + 0x26, 0x4e, 0xdf, 0x67, 0x4e, 0xd6, 0xe8, 0x22, 0x93, 0xe9, 0x43, 0x27, 0x7d, 0xa3, 0xd3, 0x7d, 0x27, 0xfc, 0xa3, + 0x62, 0x16, 0x1f, 0x6e, 0xe9, 0x2b, 0x4d, 0x8a, 0x3b, 0x60, 0x65, 0xf3, 0xa8, 0x20, 0xd4, 0xb9, 0x88, 0xbe, 0x31, + 0xe5, 0x5b, 0x42, 0xcd, 0xbe, 0xb1, 0xa4, 0x94, 0xee, 0x35, 0xf4, 0x26, 0xad, 0xf5, 0xdb, 0x28, 0xc1, 0x98, 0xe8, + 0x78, 0xf2, 0x32, 0x1e, 0x2b, 0xef, 0xe3, 0x71, 0x23, 0x15, 0xf2, 0x00, 0x44, 0xa0, 0x62, 0xfc, 0xe9, 0xca, 0x93, + 0x93, 0x5e, 0x18, 0xaf, 0x42, 0x29, 0x28, 0x0c, 0xe8, 0x0a, 0xa4, 0x80, 0x47, 0xed, 0x89, 0xce, 0xc2, 0x2e, 0xe1, + 0x1e, 0xdd, 0x04, 0x8c, 0xf5, 0xf9, 0x27, 0x40, 0x73, 0x17, 0xee, 0xf0, 0x62, 0x80, 0xda, 0xd4, 0xab, 0xbb, 0x8f, + 0x6b, 0x75, 0x0e, 0x87, 0xe0, 0x60, 0x35, 0x88, 0xe0, 0x74, 0x3e, 0x75, 0x34, 0xcb, 0x02, 0x54, 0x4e, 0x96, 0x1b, + 0x79, 0xf3, 0x68, 0xd1, 0xab, 0xfb, 0xde, 0x32, 0x2d, 0xab, 0x3a, 0xc8, 0x58, 0x16, 0x56, 0x80, 0xab, 0x43, 0xeb, + 0x07, 0xe1, 0xb2, 0x70, 0xfe, 0x40, 0x08, 0x62, 0xf7, 0x6a, 0x5b, 0xf2, 0x5c, 0xcd, 0xe1, 0x67, 0xcf, 0xd9, 0x9a, + 0x4b, 0xd4, 0x49, 0x67, 0x22, 0x00, 0xb1, 0xa7, 0x66, 0x15, 0x5d, 0x03, 0x49, 0x9d, 0x66, 0x15, 0x5d, 0x53, 0xb3, + 0x8d, 0x71, 0x20, 0x1f, 0xad, 0x52, 0xc0, 0xbe, 0x9b, 0x8e, 0x83, 0xd5, 0xb3, 0x58, 0x5e, 0x87, 0xee, 0x9e, 0x6d, + 0x94, 0xcf, 0xa0, 0x6e, 0xb5, 0x31, 0x26, 0xb6, 0x9b, 0x2f, 0xe7, 0xfa, 0xed, 0x60, 0xe9, 0xdb, 0x41, 0x73, 0x4e, + 0xd9, 0x77, 0xba, 0xec, 0x95, 0x5d, 0x36, 0xf5, 0xdc, 0x51, 0xd1, 0x6a, 0x0c, 0xe8, 0x0d, 0x2c, 0x58, 0x9f, 0x8b, + 0x34, 0x5b, 0x95, 0xaa, 0x04, 0xbc, 0x30, 0x56, 0xec, 0xce, 0x6f, 0x64, 0x86, 0x24, 0xcc, 0xe3, 0x4c, 0xbc, 0xa3, + 0x7b, 0x2d, 0x4c, 0x8e, 0x63, 0x91, 0x4c, 0x09, 0x9d, 0xd2, 0x9d, 0x6d, 0xe8, 0x5c, 0x85, 0x51, 0x44, 0x6b, 0x25, + 0x95, 0x46, 0x02, 0x53, 0x33, 0x40, 0xc9, 0x5c, 0x81, 0x53, 0xba, 0xdc, 0xff, 0x8e, 0xc4, 0x38, 0xf3, 0x45, 0xc9, + 0x0c, 0xe8, 0x96, 0x5f, 0x17, 0xeb, 0x56, 0x8a, 0x8c, 0x30, 0x6f, 0x8e, 0xdb, 0xeb, 0xfa, 0x10, 0xc8, 0xd5, 0xb2, + 0x47, 0xd1, 0x38, 0x28, 0x74, 0xb8, 0x54, 0x09, 0xb0, 0x2f, 0x12, 0x3f, 0x23, 0x6c, 0x69, 0x0f, 0xe4, 0xf6, 0xe8, + 0x4c, 0x98, 0x73, 0x4e, 0xca, 0xb2, 0x73, 0x69, 0x06, 0x97, 0x13, 0x57, 0x82, 0x8b, 0xf4, 0xb6, 0x3d, 0x4d, 0x5a, + 0xda, 0x3e, 0x36, 0x9c, 0xa3, 0xa1, 0x6d, 0xd0, 0x1d, 0xfb, 0x43, 0x73, 0xb1, 0x88, 0xad, 0x8b, 0xc5, 0xb0, 0x33, + 0xfb, 0xd1, 0x62, 0x01, 0x72, 0x00, 0x38, 0xea, 0x36, 0x7c, 0xcc, 0x96, 0xc0, 0x69, 0x35, 0xcd, 0xa6, 0xde, 0x86, + 0x57, 0xcf, 0x54, 0x4f, 0x2f, 0x79, 0xfe, 0x4c, 0x98, 0xb1, 0xd8, 0xf0, 0xfc, 0x99, 0x75, 0xe4, 0x54, 0xcf, 0x84, + 0x12, 0xad, 0x0b, 0x68, 0x06, 0x5e, 0x53, 0xc0, 0x88, 0x25, 0x93, 0x29, 0x55, 0xe4, 0x71, 0x6f, 0xba, 0x51, 0x83, + 0x17, 0x14, 0x0e, 0x81, 0x94, 0x4e, 0xbf, 0x78, 0xce, 0xf4, 0x7b, 0x17, 0xcf, 0x3b, 0x64, 0x6d, 0xc3, 0x74, 0xb9, + 0x19, 0x26, 0x83, 0xd2, 0x7f, 0x66, 0x26, 0xc6, 0x85, 0x35, 0x49, 0x00, 0xf1, 0x6f, 0xec, 0x77, 0x48, 0xe1, 0xe6, + 0xfd, 0xe5, 0x30, 0x7e, 0xe4, 0xfd, 0x18, 0xd9, 0x93, 0x34, 0x43, 0xac, 0x99, 0x54, 0xc8, 0xdd, 0x57, 0xeb, 0x1f, + 0x13, 0xbb, 0xc9, 0x1e, 0x58, 0x00, 0x62, 0x6b, 0xda, 0xea, 0x96, 0xf7, 0xfb, 0x9e, 0x29, 0x02, 0xfc, 0xa0, 0xfc, + 0xa3, 0x3b, 0x43, 0x32, 0x28, 0xbb, 0x6e, 0x08, 0xf1, 0xa0, 0x6c, 0x9a, 0xf6, 0x7a, 0xdb, 0x3b, 0xf3, 0x58, 0x5d, + 0xa7, 0x9d, 0xc5, 0xd5, 0x22, 0x83, 0xb4, 0xfa, 0x90, 0x1d, 0x67, 0xf6, 0xd9, 0xd1, 0x52, 0xe9, 0x7e, 0x1f, 0x22, + 0xe2, 0x8e, 0xb2, 0xb6, 0xdf, 0x6e, 0xc1, 0x35, 0x1c, 0x0d, 0x42, 0x57, 0xf6, 0x76, 0x19, 0x6d, 0x5c, 0x88, 0xe3, + 0x9e, 0xe9, 0x7c, 0xc1, 0x97, 0x47, 0x69, 0xe7, 0xc1, 0xa9, 0x9e, 0xe8, 0x73, 0xd3, 0x5d, 0x65, 0x72, 0xad, 0xc3, + 0x6a, 0x0c, 0x6a, 0xb3, 0xb0, 0x85, 0xbb, 0xb0, 0x8d, 0x0e, 0x5a, 0xfb, 0xb2, 0xe0, 0x9f, 0x32, 0x00, 0x5f, 0x7a, + 0xb6, 0x6c, 0x7b, 0x4d, 0x5a, 0xbd, 0x91, 0x51, 0x88, 0x2d, 0x6d, 0xaf, 0x3e, 0x1d, 0xe5, 0xe3, 0xe6, 0x84, 0xe2, + 0x42, 0x8e, 0xf2, 0xa3, 0xd7, 0x10, 0x75, 0xad, 0xeb, 0xb8, 0x58, 0x74, 0xb8, 0x71, 0xd5, 0x6d, 0x37, 0xae, 0x1f, + 0x11, 0x6f, 0x8d, 0x36, 0x29, 0xd4, 0xca, 0xd8, 0x11, 0xbc, 0x2c, 0x1f, 0x0e, 0x99, 0x18, 0x0e, 0x25, 0x64, 0xea, + 0x63, 0xf7, 0x86, 0xa6, 0x7d, 0x7e, 0xda, 0xfa, 0x11, 0x4b, 0x8d, 0xa3, 0xd8, 0xf0, 0x4e, 0xdf, 0x79, 0x6c, 0x8d, + 0x2b, 0xf9, 0x32, 0x98, 0xed, 0x0a, 0xaa, 0xad, 0xf1, 0x86, 0xbd, 0x9c, 0xbf, 0xac, 0xa4, 0x92, 0xbf, 0xfd, 0x19, + 0xae, 0xe1, 0xad, 0x2d, 0x1d, 0x34, 0xd5, 0x2c, 0x67, 0xb9, 0xbe, 0x17, 0x1c, 0x7f, 0xdc, 0xbd, 0x22, 0x18, 0xfc, + 0x9e, 0x8e, 0x82, 0x5c, 0x2c, 0xd5, 0x1a, 0x50, 0x90, 0x8e, 0xec, 0x98, 0xca, 0x02, 0xc3, 0x00, 0xde, 0x90, 0x01, + 0xf2, 0x98, 0xc2, 0xdd, 0x50, 0xe1, 0x85, 0xbf, 0x54, 0x64, 0x97, 0xc0, 0xb6, 0x66, 0x7c, 0xcc, 0x70, 0x07, 0x21, + 0xff, 0x08, 0x76, 0xc7, 0x56, 0xec, 0x96, 0x2d, 0x18, 0x92, 0x8d, 0xe3, 0x30, 0xc6, 0x7c, 0x3c, 0x89, 0xaf, 0xc4, + 0x24, 0x1e, 0xf0, 0x08, 0x1d, 0x23, 0xd6, 0xbc, 0x9e, 0xc5, 0x72, 0x00, 0xd9, 0x1d, 0x57, 0x3a, 0x20, 0x84, 0xc6, + 0x86, 0x96, 0xbc, 0x29, 0x0c, 0x2e, 0x76, 0xec, 0x33, 0x12, 0xc9, 0x38, 0x04, 0x8b, 0x56, 0x35, 0xb0, 0x30, 0xb1, + 0x5b, 0x5e, 0xcc, 0x56, 0x73, 0xfc, 0xe7, 0x70, 0x40, 0x00, 0xec, 0x60, 0xdf, 0xb0, 0xbb, 0x08, 0x91, 0xde, 0x16, + 0xfc, 0xce, 0xf2, 0x74, 0x61, 0xf7, 0xfc, 0x1d, 0x1f, 0xb3, 0xf3, 0x1f, 0x3d, 0x88, 0x9c, 0x3d, 0xff, 0x04, 0x68, + 0x88, 0xf7, 0xfc, 0x36, 0xf5, 0x2a, 0x76, 0x4b, 0x14, 0x84, 0xb7, 0xe0, 0x0c, 0x74, 0x0f, 0x11, 0xb0, 0xef, 0xf8, + 0x02, 0x63, 0xc5, 0xce, 0xd2, 0xa5, 0x87, 0x19, 0xa1, 0xf6, 0x74, 0xbe, 0xac, 0xd5, 0x24, 0xdc, 0x5c, 0x2d, 0x27, + 0x83, 0xc1, 0xc6, 0xdf, 0xf1, 0x35, 0xf0, 0xc1, 0x9c, 0xff, 0xe8, 0xed, 0xa8, 0x5c, 0xf8, 0xcf, 0xeb, 0x2c, 0x79, + 0xe7, 0xb3, 0x77, 0x03, 0xbe, 0x00, 0xbc, 0x25, 0x74, 0xe0, 0xba, 0xf7, 0x99, 0xc4, 0x6b, 0x7b, 0xa7, 0xaf, 0x11, + 0x48, 0xe4, 0x0b, 0xc0, 0x88, 0x89, 0xf9, 0xfd, 0x0e, 0x22, 0x30, 0x12, 0xf0, 0x6d, 0xd5, 0x1e, 0xf1, 0x5b, 0x6e, + 0x00, 0xbf, 0x32, 0x9f, 0x3d, 0xf0, 0x50, 0xff, 0x4c, 0x7c, 0x76, 0xc3, 0x3f, 0xf0, 0x6b, 0x4f, 0x4a, 0xd2, 0xe5, + 0xec, 0xc3, 0x1c, 0xae, 0x87, 0x52, 0x9e, 0x0e, 0xe9, 0x67, 0x63, 0x30, 0x80, 0x50, 0xc8, 0xbc, 0xf1, 0x80, 0x35, + 0x29, 0xc4, 0xbf, 0x80, 0x6f, 0x47, 0x09, 0x9b, 0x37, 0xde, 0xd6, 0xd7, 0xf2, 0xe6, 0x8d, 0xf7, 0xe0, 0x53, 0x14, + 0x60, 0x15, 0x94, 0xb2, 0xc0, 0x2a, 0x08, 0x1b, 0x6d, 0x84, 0x31, 0x70, 0xf5, 0xae, 0x31, 0xd4, 0xf5, 0x1c, 0xb1, + 0x6d, 0xa5, 0xef, 0xc3, 0xf7, 0x90, 0x01, 0x1f, 0xbc, 0x29, 0x4a, 0xa2, 0xcf, 0xa9, 0x29, 0x92, 0xd6, 0x3d, 0xf7, + 0x5b, 0xeb, 0x8e, 0xd6, 0x94, 0xfa, 0xc8, 0xd5, 0xf8, 0x70, 0xa8, 0xaf, 0x85, 0x16, 0x09, 0xa6, 0xa0, 0x71, 0x0d, + 0xda, 0x02, 0x04, 0x7d, 0x1e, 0x20, 0x6b, 0x49, 0xb1, 0xe0, 0xdb, 0x5f, 0x21, 0x06, 0xaf, 0x4c, 0xef, 0x5c, 0xae, + 0x32, 0x12, 0xb6, 0x17, 0x7e, 0x39, 0xac, 0xfd, 0x89, 0x53, 0x0b, 0x4b, 0xab, 0x39, 0xa8, 0x9f, 0xd9, 0x72, 0x9c, + 0xaa, 0xda, 0xbf, 0x24, 0x49, 0xb5, 0xab, 0xb4, 0x9c, 0xde, 0xdb, 0x37, 0x5d, 0x26, 0xd8, 0xd8, 0x0f, 0xa8, 0x3a, + 0xb2, 0x1a, 0x76, 0x5f, 0xa8, 0x2f, 0x7a, 0x4a, 0x26, 0x34, 0x1f, 0x55, 0x34, 0xcf, 0xee, 0x37, 0x3b, 0xea, 0x3f, + 0xbd, 0x1c, 0x8a, 0x00, 0xc9, 0x2a, 0x2d, 0x96, 0x22, 0x67, 0x63, 0x3f, 0x1e, 0x26, 0x99, 0x0a, 0x2f, 0x48, 0x47, + 0x77, 0xbf, 0x71, 0x7f, 0xcb, 0x0d, 0x64, 0x85, 0x56, 0x6d, 0x30, 0x56, 0x8a, 0x96, 0xc1, 0xfa, 0x6a, 0xdc, 0xef, + 0x8b, 0xab, 0xf1, 0x54, 0x04, 0x35, 0x10, 0x17, 0x89, 0xeb, 0xf1, 0xb4, 0x26, 0x96, 0xd4, 0xae, 0xc0, 0x18, 0x3d, + 0xae, 0x8a, 0xda, 0xa7, 0xbe, 0x86, 0x50, 0xa4, 0x5a, 0x33, 0xc7, 0x1a, 0x37, 0x46, 0xc4, 0x1d, 0x56, 0xae, 0x9d, + 0xda, 0xeb, 0x00, 0x2c, 0xaf, 0xc6, 0x05, 0x61, 0x93, 0x1c, 0x3b, 0x17, 0xb0, 0x1a, 0x0d, 0xa9, 0x76, 0xc3, 0xad, + 0x97, 0x9d, 0xdf, 0x3c, 0x4e, 0x6c, 0x6d, 0x84, 0x5b, 0x0a, 0x28, 0xa3, 0xfc, 0xc6, 0x72, 0xc2, 0xee, 0x54, 0xef, + 0x48, 0xd5, 0x8e, 0x38, 0x71, 0x01, 0xcb, 0x0d, 0x4f, 0xad, 0xbe, 0x89, 0xc1, 0x89, 0x50, 0xb5, 0xd2, 0xe1, 0x4e, + 0x26, 0x10, 0xf7, 0xab, 0xfb, 0xba, 0x57, 0x82, 0x9f, 0x84, 0xbc, 0x7e, 0xcb, 0x3b, 0x00, 0xac, 0xf8, 0x90, 0x17, + 0xd3, 0xc2, 0xd1, 0xba, 0x0c, 0xca, 0x00, 0x11, 0x9a, 0x01, 0xd0, 0xc9, 0xd5, 0x41, 0x94, 0x06, 0xae, 0xb8, 0x43, + 0x84, 0x9f, 0x46, 0xcf, 0xf2, 0xeb, 0xf0, 0x59, 0x35, 0x0d, 0x2f, 0xf2, 0x20, 0xba, 0xa8, 0x82, 0xe8, 0x59, 0x75, + 0x15, 0x3e, 0xcb, 0xa7, 0xd1, 0x45, 0x1e, 0x84, 0x17, 0x55, 0x63, 0xdf, 0xb5, 0xbb, 0x7b, 0x42, 0xde, 0x76, 0xf5, + 0x47, 0xce, 0x95, 0x3d, 0x65, 0x7a, 0x7e, 0x5e, 0xeb, 0x95, 0xda, 0x6d, 0xae, 0xd7, 0xa8, 0x99, 0xfa, 0x28, 0xfb, + 0x8b, 0x6d, 0x2c, 0x3c, 0x9a, 0x43, 0xe8, 0x33, 0xd2, 0x62, 0xee, 0x71, 0xae, 0x37, 0x7b, 0x52, 0x18, 0x18, 0x31, + 0xa9, 0x64, 0xe4, 0xf4, 0x02, 0x17, 0xa1, 0x0a, 0x31, 0xac, 0xa5, 0xab, 0x7d, 0xd6, 0xa5, 0x37, 0x50, 0xd7, 0x14, + 0xfb, 0x1a, 0x32, 0xf0, 0xa2, 0xe9, 0x65, 0x30, 0x06, 0xe4, 0x08, 0xbc, 0xe3, 0xb3, 0x25, 0x1c, 0x98, 0x6b, 0x80, + 0xbe, 0x79, 0xd4, 0xd7, 0xe5, 0x8e, 0xaf, 0x55, 0xdf, 0x4c, 0xd7, 0x23, 0xa5, 0xfc, 0x58, 0xf1, 0xbb, 0x8b, 0xe7, + 0xec, 0x96, 0x6b, 0x54, 0x94, 0x5f, 0xf4, 0x62, 0xbd, 0x07, 0xae, 0xba, 0x5f, 0xe0, 0x36, 0x8b, 0xc7, 0xae, 0x3c, + 0x60, 0xd9, 0x96, 0x3d, 0xb0, 0x1b, 0xf6, 0x81, 0x3d, 0x61, 0x6f, 0xd9, 0x7b, 0x56, 0x23, 0x44, 0x79, 0xa9, 0xa4, + 0x3c, 0x7f, 0xc1, 0x6f, 0xa5, 0xed, 0x51, 0xc2, 0x92, 0x3d, 0xd8, 0x76, 0x9a, 0xe1, 0x86, 0x7d, 0xe0, 0x8b, 0xe1, + 0x8a, 0xbd, 0x85, 0x6c, 0x28, 0x14, 0x0f, 0x56, 0xac, 0x86, 0x2b, 0x2c, 0x65, 0xd0, 0xa7, 0x61, 0x69, 0x09, 0x8b, + 0xa6, 0x50, 0x94, 0xa2, 0xdf, 0xf2, 0x9a, 0xb0, 0xd3, 0x6a, 0x2c, 0x44, 0x7e, 0x68, 0xb8, 0x62, 0x0f, 0x7c, 0x31, + 0x58, 0xb1, 0x0f, 0xda, 0x46, 0x34, 0xd8, 0xb8, 0xc5, 0x11, 0x98, 0x95, 0x2e, 0x4c, 0x0a, 0xd4, 0x5b, 0xfb, 0x26, + 0xb8, 0x61, 0x37, 0x58, 0xbf, 0x27, 0x58, 0x34, 0xca, 0xfc, 0x83, 0x15, 0x7b, 0xcf, 0x25, 0x86, 0x9a, 0x5b, 0x9e, + 0x74, 0x0c, 0xd5, 0x05, 0xd2, 0x15, 0xe1, 0x09, 0xa7, 0x17, 0xd9, 0x7b, 0x2c, 0x83, 0xbe, 0x32, 0x5c, 0xb1, 0x2d, + 0xd6, 0xee, 0xc6, 0x18, 0xb7, 0xac, 0xea, 0x49, 0x50, 0x60, 0x94, 0x55, 0x4a, 0xcb, 0xc5, 0x11, 0xcb, 0xa6, 0x8e, + 0x1a, 0xd4, 0x86, 0x01, 0x7d, 0x30, 0xfa, 0x0f, 0x5f, 0xbf, 0xfb, 0xd1, 0x2b, 0xf5, 0xcd, 0xf7, 0x17, 0xc7, 0xbb, + 0xb2, 0x44, 0xef, 0xca, 0x5f, 0x79, 0x39, 0xfb, 0x65, 0x3e, 0xd1, 0xb5, 0xa4, 0x4d, 0x86, 0xdc, 0x4d, 0x67, 0xbf, + 0x74, 0xf8, 0x5b, 0xfe, 0xea, 0xfb, 0x8d, 0xd5, 0xc7, 0xea, 0xbb, 0xba, 0x7b, 0x1f, 0x06, 0x9b, 0xc6, 0xa9, 0xf8, + 0xee, 0x74, 0xc5, 0xb1, 0x9d, 0xb5, 0xf6, 0xce, 0xfc, 0x1f, 0xae, 0xf5, 0x16, 0xc7, 0xee, 0x86, 0x6f, 0x87, 0x1b, + 0x7b, 0x18, 0xe4, 0xf7, 0xa5, 0xe2, 0x38, 0xab, 0xf9, 0x0b, 0xaf, 0x53, 0x92, 0x05, 0x54, 0xa3, 0xcf, 0x46, 0x1a, + 0xba, 0x64, 0x26, 0xa6, 0x21, 0xbe, 0xc8, 0x00, 0x9d, 0x0b, 0xc4, 0xb3, 0x7b, 0x3e, 0x9e, 0xdc, 0x5f, 0xc5, 0x93, + 0xfb, 0x01, 0xff, 0x6c, 0x5a, 0xd0, 0x5e, 0x70, 0xf7, 0x3e, 0xfb, 0x95, 0x17, 0xf6, 0x92, 0xfc, 0xc5, 0x67, 0x5f, + 0x85, 0xbb, 0x4a, 0x7f, 0xf1, 0xd9, 0x7b, 0xc1, 0x7f, 0x1d, 0x69, 0xb2, 0x0c, 0xf6, 0xbe, 0xe6, 0xbf, 0x8e, 0x90, + 0xf5, 0x83, 0x7d, 0x11, 0xfc, 0x2b, 0xf8, 0x7f, 0x57, 0x09, 0x5a, 0xc6, 0xbf, 0xd4, 0xea, 0xe7, 0x07, 0x19, 0x9b, + 0x03, 0x6f, 0x42, 0x2b, 0xe8, 0xcd, 0xdb, 0x5a, 0xfe, 0x24, 0x2e, 0x8e, 0x54, 0x3d, 0x35, 0x1c, 0xb4, 0x58, 0xcc, + 0xa2, 0x3e, 0x4a, 0xa7, 0xf2, 0x26, 0xef, 0x78, 0x26, 0x2d, 0xcc, 0xf7, 0x10, 0x0e, 0xfc, 0xce, 0x86, 0x29, 0xd8, + 0x71, 0xdc, 0x0c, 0xde, 0xb1, 0xf7, 0xc2, 0x67, 0xd9, 0x74, 0xcb, 0x6f, 0xf8, 0x13, 0xfe, 0x9e, 0xef, 0x82, 0x07, + 0xfe, 0x81, 0xbf, 0xe5, 0x75, 0xcd, 0x77, 0x6c, 0x29, 0x21, 0x4f, 0xeb, 0xed, 0x65, 0xb0, 0x65, 0xf5, 0xee, 0x32, + 0x78, 0x60, 0xf5, 0xf6, 0x79, 0x70, 0xc3, 0xea, 0xdd, 0xf3, 0xe0, 0x03, 0xdb, 0x5e, 0x06, 0x4f, 0xd8, 0xee, 0x32, + 0x78, 0xcb, 0xb6, 0xcf, 0x83, 0xf7, 0x6c, 0xf7, 0x3c, 0xa8, 0x15, 0xd2, 0xc3, 0x7b, 0x21, 0x99, 0x4e, 0xde, 0xd7, + 0xcc, 0xb0, 0xea, 0x06, 0x5f, 0x84, 0xf5, 0x8b, 0x6a, 0x19, 0x7c, 0xa9, 0x99, 0x6e, 0x73, 0x20, 0x04, 0xd3, 0x2d, + 0x0e, 0x6e, 0xe9, 0x89, 0x69, 0x57, 0x90, 0x0a, 0xd6, 0xd5, 0xd2, 0x60, 0x51, 0x37, 0xad, 0x93, 0xd9, 0xf1, 0x4e, + 0x8c, 0x3b, 0xbc, 0x13, 0x17, 0x6c, 0xd9, 0x74, 0xba, 0xea, 0x9c, 0x3e, 0x0f, 0xf4, 0x11, 0xa0, 0xf7, 0xfe, 0x4a, + 0x7a, 0xd0, 0x14, 0x0d, 0xcf, 0x95, 0xee, 0xb8, 0xb5, 0xdf, 0x87, 0xd6, 0x7e, 0xcf, 0xa4, 0x22, 0x2d, 0x62, 0x51, + 0x59, 0x54, 0x15, 0xf2, 0x89, 0x07, 0x99, 0xd6, 0xaa, 0x25, 0x8c, 0xd4, 0x99, 0x80, 0x49, 0x5f, 0xd0, 0x61, 0x90, + 0x93, 0x5d, 0x81, 0x2d, 0xf9, 0x66, 0x90, 0xb0, 0x35, 0x8f, 0xa7, 0xc3, 0x24, 0x58, 0xb2, 0x3b, 0x3e, 0xec, 0x16, + 0x0b, 0x56, 0x2a, 0x8c, 0x49, 0x5f, 0x9f, 0x8e, 0x76, 0x77, 0xde, 0x5b, 0xa5, 0x71, 0x9c, 0x09, 0xd4, 0xb9, 0x55, + 0x7a, 0x9b, 0xdf, 0x3a, 0xbb, 0xfa, 0x5a, 0xed, 0xf2, 0x20, 0x30, 0xfc, 0x0a, 0x44, 0x3b, 0xc4, 0x7b, 0x07, 0x35, + 0x46, 0xba, 0x25, 0xb3, 0xee, 0x2b, 0x7b, 0x5f, 0xdf, 0x9a, 0xad, 0xfa, 0xdf, 0x2d, 0x82, 0xf6, 0x72, 0xd9, 0xfb, + 0x9f, 0xcd, 0xab, 0xbf, 0x75, 0xbc, 0xba, 0xf1, 0x27, 0x0f, 0xfc, 0x33, 0x46, 0x27, 0x60, 0x22, 0xdb, 0xf1, 0xcf, + 0xa3, 0x6d, 0xe3, 0x94, 0x27, 0xf7, 0xf2, 0xff, 0x2b, 0x05, 0xda, 0xbb, 0x79, 0x65, 0x6f, 0x8a, 0x5b, 0xde, 0xb1, + 0x97, 0x2f, 0xad, 0x3d, 0xd1, 0x20, 0x94, 0x7c, 0xe6, 0x6e, 0x50, 0x34, 0xec, 0x89, 0xbf, 0xf0, 0x6a, 0xf6, 0x79, + 0x3e, 0xd9, 0xf2, 0xe3, 0x1d, 0xf1, 0x73, 0xc7, 0x8e, 0xf8, 0x8b, 0x3f, 0x58, 0x36, 0xdf, 0xea, 0xd5, 0xce, 0x9d, + 0xdc, 0xa9, 0xf4, 0x8e, 0x1f, 0xef, 0xe3, 0xc3, 0x7f, 0xbb, 0xd2, 0xbb, 0xef, 0xae, 0xb4, 0x5d, 0xe5, 0xee, 0xce, + 0x37, 0x1d, 0xdf, 0xc8, 0x5a, 0x63, 0xb8, 0x99, 0x51, 0x30, 0xc2, 0xb4, 0x85, 0x69, 0x1a, 0x44, 0x96, 0x62, 0x11, + 0x12, 0x35, 0x4a, 0xe7, 0x44, 0x9f, 0x05, 0x9d, 0x82, 0x2e, 0x6e, 0xf4, 0xb7, 0x7c, 0xcc, 0x16, 0xc6, 0x65, 0xf3, + 0xf6, 0x6a, 0x31, 0x19, 0x0c, 0x6e, 0xfd, 0xfd, 0x3d, 0x0f, 0x67, 0xb7, 0x73, 0xf6, 0x8e, 0xdf, 0xd3, 0x7a, 0x9a, + 0xa8, 0xc6, 0x17, 0x8f, 0x49, 0x60, 0xb7, 0xbe, 0x3f, 0xb1, 0x88, 0x60, 0xed, 0x1b, 0xe7, 0xad, 0x3f, 0x90, 0x66, + 0x69, 0xb9, 0xb5, 0x7f, 0x78, 0x5c, 0x43, 0x71, 0x0b, 0x42, 0xc6, 0x07, 0x5b, 0xe5, 0xf0, 0x96, 0x7f, 0xf2, 0xde, + 0xf9, 0xd3, 0x77, 0x3a, 0xf8, 0x66, 0xa2, 0xce, 0xa5, 0xb7, 0x17, 0xcf, 0xd9, 0xaf, 0xfc, 0xb3, 0x3c, 0x53, 0xbe, + 0x0a, 0x39, 0x6d, 0x6f, 0x90, 0xc4, 0x89, 0x8e, 0x8a, 0xf7, 0x6e, 0x22, 0x81, 0x42, 0x20, 0x1e, 0x47, 0xcd, 0x1f, + 0x26, 0xe5, 0xd4, 0xdb, 0x01, 0xc9, 0x2b, 0xb7, 0x15, 0xd1, 0xb7, 0x9c, 0xf3, 0xc5, 0xf0, 0x72, 0xfa, 0xbe, 0xdb, + 0xb7, 0x47, 0x85, 0xb5, 0xa9, 0x88, 0xb7, 0x5b, 0x0c, 0xc2, 0x3a, 0x99, 0x59, 0xe6, 0x92, 0x2f, 0xbd, 0xaf, 0xcd, + 0xdc, 0x63, 0x7a, 0xc7, 0x99, 0x66, 0xc8, 0xe8, 0x0b, 0xcc, 0x4c, 0x87, 0xc3, 0xdd, 0x39, 0x96, 0xc7, 0x87, 0x6f, + 0x9f, 0x3d, 0x19, 0x3c, 0xc1, 0x10, 0x2e, 0x2b, 0x2c, 0xe4, 0x3d, 0x1f, 0x66, 0x75, 0xeb, 0xda, 0x71, 0xf1, 0x7c, + 0xf8, 0x0b, 0xe4, 0x0d, 0xba, 0x1e, 0x9a, 0x22, 0x5a, 0xe5, 0x77, 0x14, 0x7d, 0xa2, 0xe4, 0xa0, 0xe3, 0x09, 0xd4, + 0x0e, 0xb9, 0x70, 0xdf, 0x3f, 0xe3, 0xa0, 0xe8, 0xc0, 0x52, 0xfb, 0xfd, 0xf3, 0xcf, 0x44, 0x28, 0x0d, 0xe3, 0xfd, + 0x32, 0x8c, 0xfe, 0x88, 0xcb, 0x62, 0x0d, 0x47, 0xec, 0x00, 0x3e, 0xf7, 0x4c, 0x5f, 0xc3, 0xee, 0x7c, 0xdf, 0x0f, + 0xbc, 0x2d, 0xbf, 0x61, 0xef, 0xb9, 0x77, 0x39, 0x7c, 0xeb, 0x3f, 0x7b, 0x02, 0xf2, 0x13, 0x8c, 0xcb, 0x17, 0x0c, + 0x89, 0xed, 0x28, 0x46, 0xad, 0xc3, 0x2f, 0x35, 0xc4, 0x6a, 0x7d, 0x46, 0xea, 0x2e, 0x48, 0xff, 0xa8, 0x90, 0xfd, + 0x84, 0xc0, 0x6a, 0x92, 0x3e, 0x05, 0x26, 0xf1, 0x6d, 0x0d, 0x09, 0xa4, 0x69, 0x81, 0x18, 0x1c, 0x28, 0x3e, 0x15, + 0xfc, 0xfd, 0xf0, 0x0b, 0xc9, 0x7f, 0x8b, 0x9a, 0x8f, 0xe1, 0x6f, 0x18, 0x9a, 0x49, 0xf5, 0x90, 0xd6, 0x51, 0xe2, + 0xd5, 0x70, 0xea, 0x85, 0x95, 0x50, 0x27, 0x43, 0x90, 0x8a, 0x21, 0x17, 0xe2, 0xe2, 0xf9, 0xe4, 0xb6, 0x14, 0xe1, + 0x1f, 0x13, 0x7c, 0x26, 0x57, 0x9a, 0x7c, 0x46, 0x4f, 0x1a, 0x59, 0xc0, 0x83, 0x7c, 0x5f, 0xf6, 0x6a, 0xb0, 0xa8, + 0x87, 0xfc, 0xb6, 0x76, 0xdf, 0x97, 0x73, 0x82, 0x1e, 0xd9, 0x0f, 0x68, 0x0e, 0x06, 0x6a, 0x06, 0x52, 0x86, 0xe0, + 0x16, 0x2e, 0xfd, 0x9e, 0x2a, 0xc8, 0x97, 0xdf, 0xfb, 0x22, 0x64, 0xe0, 0xca, 0x82, 0x30, 0xe5, 0x52, 0x21, 0x05, + 0x8e, 0xdb, 0x7a, 0xf0, 0x45, 0xa3, 0x93, 0x48, 0xf0, 0x29, 0x01, 0x49, 0xd2, 0xf2, 0x40, 0xd2, 0x88, 0xe9, 0x40, + 0x5c, 0x28, 0x4d, 0xb3, 0x92, 0x22, 0x0e, 0xb1, 0xab, 0xbe, 0x43, 0xc2, 0xb3, 0xe0, 0x03, 0x83, 0xb5, 0x23, 0x45, + 0x8b, 0xf7, 0xc6, 0x74, 0xac, 0xc3, 0x86, 0xee, 0x64, 0x71, 0xbf, 0x4a, 0xea, 0x34, 0x12, 0x57, 0xbe, 0x0a, 0xf9, + 0xf3, 0x9f, 0x4a, 0x04, 0xd2, 0xbb, 0x1a, 0x88, 0x41, 0xf0, 0x03, 0xf4, 0x1f, 0xb0, 0xc8, 0x41, 0x50, 0xaa, 0xcb, + 0x30, 0xaf, 0x32, 0x2a, 0x70, 0xb6, 0x63, 0xdb, 0x39, 0x53, 0x75, 0x0b, 0xbe, 0x08, 0xc3, 0x90, 0x76, 0xb6, 0x6a, + 0x4e, 0x6e, 0xf5, 0x06, 0xea, 0x99, 0xc4, 0x91, 0x5a, 0x8a, 0x23, 0x6d, 0xcd, 0x7d, 0xba, 0xf4, 0xba, 0xe5, 0x05, + 0x0d, 0x17, 0xa0, 0x17, 0xa5, 0xbb, 0xce, 0x27, 0x14, 0xba, 0xac, 0xc6, 0xd5, 0x50, 0xd4, 0xa1, 0x1c, 0x63, 0xed, + 0xcf, 0x95, 0x3c, 0xbf, 0x03, 0xeb, 0x11, 0x1a, 0xbe, 0x2a, 0x75, 0x10, 0xdb, 0x4f, 0xf4, 0xae, 0x53, 0xa9, 0xbf, + 0x01, 0x60, 0xe0, 0xd4, 0xf1, 0x50, 0x1f, 0xb5, 0x53, 0xc8, 0x76, 0xee, 0x2d, 0x31, 0x2a, 0x57, 0xc2, 0x53, 0xa5, + 0xe5, 0x29, 0x65, 0xd5, 0xd7, 0x82, 0x5b, 0xd9, 0x7d, 0x36, 0x80, 0x8c, 0x36, 0x28, 0x90, 0x67, 0xd4, 0xd6, 0x78, + 0x90, 0x6a, 0x9a, 0x25, 0x8e, 0xe1, 0x83, 0x22, 0xcd, 0x2a, 0xb0, 0x78, 0x99, 0x4b, 0xe6, 0xa0, 0x60, 0xb9, 0xde, + 0x6c, 0xa6, 0x99, 0xea, 0x8b, 0xdc, 0xde, 0x68, 0xbc, 0x4c, 0xff, 0xcd, 0x92, 0x01, 0x8f, 0x2e, 0x9e, 0xfb, 0x01, + 0xa4, 0x49, 0x8a, 0x07, 0x48, 0x82, 0xed, 0xc1, 0x2e, 0x76, 0x18, 0xb6, 0x8a, 0x95, 0x3d, 0x79, 0xba, 0xdc, 0xa1, + 0x29, 0x97, 0xe0, 0x92, 0x13, 0x73, 0x39, 0xf5, 0x7d, 0xc9, 0x7a, 0x43, 0x71, 0xca, 0xa6, 0x09, 0x28, 0x09, 0xb4, + 0x5b, 0xf0, 0x5f, 0xf8, 0xd4, 0xd0, 0x69, 0x01, 0x96, 0xda, 0x6e, 0xc0, 0x7f, 0xa1, 0x5f, 0x6c, 0x77, 0x51, 0x3f, + 0x30, 0x0f, 0xf6, 0x66, 0x71, 0x65, 0x0c, 0x38, 0x49, 0x5c, 0x69, 0x1e, 0xb9, 0x7e, 0x50, 0xf4, 0xe9, 0xb2, 0x76, + 0xe0, 0x4c, 0x71, 0x61, 0x95, 0xda, 0x24, 0xbd, 0xf6, 0x5b, 0x6a, 0xe2, 0x4d, 0x94, 0x54, 0x85, 0xed, 0x90, 0xf6, + 0x2f, 0x29, 0x67, 0xaa, 0xb8, 0x43, 0xf4, 0x64, 0x37, 0x71, 0x15, 0x78, 0x61, 0x55, 0xb1, 0x11, 0x6a, 0x33, 0xb2, + 0x9c, 0xc0, 0xe9, 0x1e, 0xab, 0x0b, 0x3e, 0xb6, 0xab, 0xd9, 0x05, 0x2b, 0xd9, 0x9a, 0x49, 0xf7, 0x79, 0x3b, 0xe6, + 0x42, 0x5e, 0xe9, 0x65, 0xd1, 0x0a, 0x68, 0x0f, 0x02, 0x87, 0x5f, 0x6a, 0xba, 0x47, 0xcf, 0x36, 0xdb, 0xd4, 0x66, + 0x63, 0x6b, 0x11, 0x42, 0x06, 0xa2, 0xa1, 0x2f, 0xe4, 0x8c, 0x22, 0x5f, 0xa5, 0xe5, 0x5a, 0x6d, 0xac, 0x32, 0x5e, + 0x60, 0x22, 0xc8, 0x70, 0x16, 0xde, 0xa3, 0xa7, 0xf5, 0x48, 0x53, 0x4c, 0x82, 0x93, 0x2e, 0xfe, 0x02, 0x6c, 0x28, + 0x4f, 0x72, 0x73, 0x40, 0x0e, 0xa0, 0x72, 0x29, 0x4a, 0xa5, 0x0c, 0xfe, 0x59, 0xdd, 0x91, 0x6d, 0xd5, 0x7f, 0xa7, + 0x81, 0x0c, 0xee, 0x40, 0xdf, 0xf6, 0x42, 0x6b, 0x47, 0x3b, 0x57, 0xb6, 0xa6, 0x6d, 0x99, 0xe6, 0x31, 0xb2, 0xd8, + 0x00, 0xf2, 0x89, 0x74, 0x0e, 0x44, 0x5e, 0x13, 0x8d, 0x77, 0x76, 0xcd, 0xc7, 0x53, 0xf1, 0x98, 0xbc, 0x57, 0xf9, + 0xbe, 0xb9, 0xd7, 0x07, 0x63, 0xec, 0x5b, 0x50, 0x26, 0x3e, 0x5a, 0x6d, 0xad, 0x4b, 0xac, 0xb7, 0x4a, 0x93, 0xe8, + 0x86, 0x2b, 0xe8, 0x38, 0x12, 0x37, 0x88, 0xc1, 0x31, 0xe3, 0xb5, 0x55, 0x96, 0xbe, 0xc2, 0x32, 0xd7, 0x31, 0x4b, + 0x86, 0x4c, 0xea, 0x3c, 0x51, 0xf0, 0xe4, 0xe7, 0x09, 0xc9, 0x88, 0xa8, 0xd9, 0x96, 0xa3, 0x94, 0x9b, 0x16, 0x70, + 0x99, 0x91, 0x01, 0x7c, 0x93, 0x26, 0x00, 0xe5, 0xf2, 0x25, 0x48, 0xa5, 0x21, 0x82, 0x6b, 0xb6, 0x97, 0x8c, 0x6e, + 0x1d, 0xad, 0x83, 0x2a, 0xc9, 0xdc, 0xc1, 0xb9, 0x9d, 0x45, 0x4a, 0xbd, 0xf9, 0x08, 0xc3, 0x4e, 0x3e, 0x86, 0x75, + 0x82, 0xdf, 0x06, 0xd4, 0xa4, 0xcf, 0x85, 0x17, 0x8d, 0x00, 0x4d, 0x7d, 0xa7, 0xca, 0xf8, 0x5c, 0x78, 0xd9, 0x68, + 0xcb, 0x32, 0x4a, 0xa1, 0xba, 0x60, 0x76, 0x6b, 0xba, 0x10, 0xf3, 0xaa, 0x1a, 0x68, 0x83, 0xdc, 0xae, 0x63, 0x06, + 0x34, 0x6a, 0xbb, 0xf2, 0xc8, 0x02, 0xdc, 0x9a, 0x89, 0xc0, 0xc8, 0xf9, 0x0f, 0xf9, 0x2b, 0x15, 0xce, 0xd3, 0xef, + 0x87, 0xde, 0x7e, 0x1b, 0x44, 0xa3, 0xed, 0x25, 0xdb, 0x05, 0xd1, 0x68, 0x77, 0xd9, 0x30, 0xfa, 0xfd, 0x9c, 0x7e, + 0x3f, 0x6f, 0x40, 0x55, 0x22, 0x4c, 0xc4, 0xbd, 0x7e, 0xa3, 0x96, 0xaf, 0xd4, 0xfa, 0x9d, 0x5a, 0xbe, 0x54, 0xc3, + 0x5b, 0x7b, 0x12, 0x09, 0x22, 0x4b, 0x63, 0xf3, 0x20, 0xd9, 0x52, 0x2d, 0x95, 0x8e, 0x51, 0x65, 0x44, 0x2d, 0x9d, + 0xcd, 0xb1, 0x62, 0xa4, 0x9d, 0x83, 0x92, 0x01, 0x99, 0x16, 0x57, 0x35, 0xa6, 0x9b, 0x15, 0x2d, 0x31, 0x19, 0x61, + 0x65, 0x5b, 0xde, 0x6e, 0x52, 0x35, 0x9d, 0x93, 0x9b, 0x5b, 0xa5, 0xdc, 0xdc, 0x0a, 0x9e, 0x7f, 0x43, 0xb7, 0x5c, + 0x72, 0xed, 0x65, 0x36, 0x2d, 0x94, 0x6e, 0x19, 0xd7, 0x60, 0x6b, 0xdf, 0x04, 0xb2, 0xcc, 0x47, 0x8a, 0x1a, 0xdb, + 0x8b, 0x46, 0xf9, 0x06, 0xd9, 0x8a, 0x18, 0x75, 0xca, 0x82, 0xf1, 0xb7, 0x3b, 0x7a, 0x20, 0x03, 0x55, 0x55, 0x6d, + 0x1c, 0xdc, 0x59, 0xe9, 0x0f, 0xcb, 0x8b, 0xe7, 0x2c, 0xb1, 0xd2, 0xc9, 0x85, 0x2a, 0xf4, 0x07, 0x21, 0xba, 0xa9, + 0x6c, 0x38, 0x38, 0xd4, 0xc5, 0x56, 0x06, 0x84, 0x1e, 0xa6, 0xf7, 0x36, 0x56, 0xb2, 0xdc, 0x35, 0xe5, 0x8b, 0x19, + 0x4f, 0x38, 0x8e, 0xbe, 0x5c, 0x2d, 0xc2, 0x5a, 0x2d, 0xb2, 0x13, 0xe0, 0xa1, 0xb5, 0x5a, 0x0a, 0xb9, 0x5a, 0x84, + 0x33, 0xd3, 0x85, 0x9a, 0xe9, 0x19, 0x28, 0x20, 0x85, 0x9a, 0xe5, 0x09, 0xc0, 0xc2, 0x0b, 0x33, 0xc3, 0x85, 0x99, + 0xe1, 0x38, 0xa4, 0xc6, 0xff, 0x41, 0xef, 0x75, 0xee, 0xb9, 0xe5, 0x6e, 0x74, 0x1a, 0xf1, 0xed, 0x68, 0x83, 0x39, + 0x3e, 0x08, 0x27, 0x10, 0x2a, 0x58, 0x22, 0x56, 0x8f, 0x46, 0xd8, 0x51, 0x43, 0xe5, 0x68, 0xbf, 0x2c, 0x2c, 0xc9, + 0x92, 0xb0, 0x24, 0xf7, 0x6a, 0x9c, 0x4b, 0xcb, 0xc5, 0xab, 0x24, 0x10, 0x89, 0x8c, 0x97, 0xd2, 0x04, 0x9f, 0xf0, + 0x72, 0x64, 0xa4, 0xe6, 0xc9, 0x22, 0xf5, 0x72, 0x96, 0xb1, 0x31, 0x62, 0x18, 0x85, 0x7e, 0x53, 0xf5, 0xfb, 0x69, + 0xe9, 0xe5, 0xd4, 0xce, 0xcf, 0xe0, 0x7a, 0x79, 0xea, 0x2c, 0x72, 0x84, 0xbc, 0x1a, 0x49, 0x85, 0xe5, 0xb5, 0x52, + 0x4f, 0x5f, 0x82, 0x0f, 0xea, 0xee, 0x8d, 0x02, 0x20, 0x2e, 0x72, 0xe9, 0x5f, 0x5b, 0xc2, 0xa5, 0x29, 0x37, 0x30, + 0xe8, 0x21, 0xcf, 0x49, 0x08, 0x95, 0x20, 0x24, 0x85, 0x75, 0xe3, 0xbe, 0x78, 0x3e, 0x71, 0xdd, 0x59, 0x6c, 0x60, + 0x82, 0xc3, 0x01, 0x10, 0x0f, 0xa6, 0x5e, 0x34, 0xe0, 0xa5, 0x9a, 0x33, 0x9f, 0xbc, 0x9c, 0x60, 0x32, 0x40, 0x55, + 0x31, 0x70, 0xca, 0x7a, 0x26, 0x1f, 0x19, 0x37, 0x33, 0xdf, 0x0f, 0xf0, 0xdd, 0xba, 0x90, 0xe8, 0x0f, 0x0a, 0xa0, + 0x20, 0x53, 0x00, 0x05, 0x89, 0x01, 0x28, 0x88, 0x0d, 0x40, 0xc1, 0xa6, 0xe1, 0x6b, 0xa9, 0xc3, 0x8d, 0x80, 0x2e, + 0xc2, 0x87, 0x9e, 0x85, 0x8d, 0x15, 0x8a, 0x67, 0x63, 0x36, 0x66, 0x85, 0xda, 0x79, 0x72, 0x39, 0x15, 0x3b, 0x8b, + 0xb1, 0xae, 0x22, 0xcb, 0xc4, 0x0b, 0x09, 0x45, 0xce, 0xb9, 0x91, 0xa8, 0xbb, 0x9f, 0x7b, 0x2f, 0xc9, 0x58, 0x32, + 0x6f, 0x68, 0xd4, 0x60, 0x5e, 0x76, 0x1d, 0xc0, 0xb4, 0xe4, 0xdb, 0x82, 0x06, 0xd3, 0xa9, 0xf2, 0x88, 0x34, 0x09, + 0x6a, 0xe7, 0x32, 0x29, 0x72, 0x42, 0x98, 0x04, 0xbd, 0x12, 0xfc, 0x46, 0xa2, 0xfc, 0x7f, 0xd3, 0x09, 0x1e, 0xe0, + 0x98, 0x68, 0x95, 0x7c, 0x05, 0x03, 0x66, 0xce, 0x5f, 0x48, 0xa7, 0x6c, 0x84, 0x62, 0x2c, 0xd3, 0x78, 0xf4, 0x95, + 0x0d, 0x11, 0xda, 0xea, 0x05, 0x9a, 0x98, 0xa0, 0x0e, 0xf0, 0x88, 0xfe, 0x1a, 0x7d, 0x35, 0x14, 0x2a, 0x5d, 0x8d, + 0xd4, 0x35, 0x3b, 0xe7, 0xfc, 0x6b, 0x6d, 0x38, 0x91, 0x31, 0x6d, 0x0a, 0x7c, 0x03, 0x02, 0xf9, 0x06, 0x02, 0xc0, + 0x55, 0xd3, 0x99, 0xbd, 0x02, 0x38, 0x07, 0x02, 0x78, 0x9c, 0x77, 0x3c, 0x7e, 0xa4, 0xbf, 0x8a, 0xe3, 0xde, 0x69, + 0x1a, 0xb6, 0xff, 0x0a, 0x8c, 0xc5, 0x50, 0x8e, 0xe7, 0x3b, 0x05, 0xc9, 0x1e, 0xa5, 0x2c, 0x5d, 0x35, 0x91, 0x1d, + 0x8a, 0xf5, 0x69, 0x4e, 0x19, 0x4b, 0xdb, 0x72, 0x8c, 0x36, 0x5e, 0x3f, 0xc6, 0xe3, 0x9b, 0x1b, 0x3d, 0xf9, 0xa0, + 0x07, 0xb7, 0xb7, 0xb7, 0xaf, 0x7b, 0xcc, 0xe6, 0x5b, 0xb1, 0x78, 0x56, 0xc4, 0x89, 0xd3, 0x3a, 0xe4, 0x00, 0x07, + 0x39, 0x09, 0x81, 0x74, 0x8c, 0x4b, 0x2d, 0x3a, 0xa8, 0x59, 0xce, 0x6b, 0x60, 0x99, 0x45, 0x90, 0x0d, 0x10, 0xd5, + 0x34, 0x15, 0xab, 0xe1, 0x41, 0xa9, 0x9a, 0x53, 0x2a, 0xb5, 0x6f, 0x38, 0x5b, 0x9d, 0x3e, 0xb1, 0x6a, 0x13, 0x6e, + 0xfd, 0x6b, 0xed, 0x09, 0xda, 0x4a, 0x1a, 0x08, 0xf5, 0x7c, 0x9d, 0xde, 0x51, 0x14, 0x8f, 0x33, 0x13, 0x4f, 0x55, + 0x60, 0xec, 0x5b, 0x3b, 0x82, 0x82, 0xa4, 0xe9, 0x3a, 0xe0, 0x30, 0x8d, 0x4e, 0x58, 0xfc, 0x53, 0xfa, 0x50, 0x5e, + 0xd4, 0x0a, 0x9c, 0xe4, 0xef, 0xc2, 0x45, 0x24, 0xb1, 0xd0, 0x2f, 0x09, 0x80, 0x44, 0x06, 0xaf, 0x46, 0xc5, 0x5a, + 0xa8, 0x00, 0x39, 0x45, 0xe9, 0xad, 0xe2, 0xe3, 0x52, 0x94, 0x2a, 0xa5, 0x32, 0x37, 0x2a, 0x05, 0x84, 0xb5, 0x81, + 0xa3, 0x0b, 0xf8, 0x02, 0x82, 0xd6, 0x72, 0xb7, 0xb6, 0x3d, 0x6f, 0x64, 0x3e, 0x33, 0xcd, 0xd3, 0xea, 0xa3, 0xfa, + 0xfb, 0xc3, 0x12, 0xc3, 0x6c, 0x3c, 0xfd, 0x7d, 0x9b, 0x21, 0xdc, 0xfc, 0x0d, 0x43, 0x74, 0x07, 0xe0, 0x98, 0xa5, + 0x3d, 0x14, 0xb2, 0x60, 0x82, 0x35, 0x54, 0xe5, 0x29, 0x9f, 0xbd, 0x7c, 0x72, 0x0b, 0x68, 0x6a, 0xe8, 0xe2, 0x46, + 0xa7, 0xba, 0x2a, 0x41, 0xf8, 0xbe, 0x2b, 0xd4, 0x63, 0x73, 0xc0, 0xa9, 0x01, 0xa0, 0x58, 0xe4, 0xb5, 0x1e, 0xdb, + 0x3f, 0xe8, 0x8d, 0x7a, 0x03, 0xc4, 0xd3, 0x39, 0x2f, 0xfc, 0x23, 0xfa, 0x75, 0xea, 0xcf, 0xb8, 0x10, 0x44, 0xbd, + 0x9e, 0x84, 0xf7, 0xe2, 0x2c, 0x8d, 0x83, 0xb3, 0xde, 0xc0, 0x5c, 0x04, 0x8a, 0xb3, 0x34, 0x3f, 0x03, 0xb1, 0x1c, + 0xe1, 0x11, 0x6b, 0x76, 0x07, 0x88, 0x81, 0xa5, 0x0e, 0x49, 0x56, 0x1d, 0xdb, 0xef, 0xbf, 0x19, 0x19, 0xde, 0x74, + 0x44, 0x84, 0xd1, 0xbf, 0x2b, 0x10, 0xa0, 0x60, 0x99, 0xd9, 0xce, 0x4c, 0xba, 0xda, 0xb3, 0x7a, 0xde, 0x6c, 0xf2, + 0xae, 0xde, 0xb1, 0x9a, 0x96, 0x53, 0xd3, 0x2a, 0xab, 0x69, 0x93, 0x1c, 0x6a, 0x26, 0xfa, 0x7d, 0x8d, 0x8f, 0x9a, + 0xcf, 0x01, 0x97, 0x0d, 0x93, 0xdf, 0xcc, 0xaa, 0x79, 0xbf, 0xef, 0xc9, 0x47, 0xf0, 0x0b, 0x89, 0xcb, 0xdc, 0x1a, + 0xcb, 0xa7, 0xaf, 0x89, 0xcf, 0xcc, 0x20, 0x1e, 0xdd, 0x1d, 0x41, 0x7d, 0xdd, 0x08, 0xaf, 0x63, 0xae, 0xb0, 0x99, + 0x98, 0xbe, 0x81, 0xc1, 0xf3, 0x84, 0x0f, 0xde, 0x72, 0xf4, 0x37, 0xd2, 0x99, 0x29, 0x58, 0xc8, 0xb9, 0x3f, 0x79, + 0x83, 0xd0, 0xc9, 0x88, 0xf4, 0xa0, 0xd3, 0x09, 0x1a, 0xb2, 0xdf, 0x5f, 0x41, 0x67, 0xb6, 0x52, 0x29, 0x5b, 0x15, + 0x95, 0xe9, 0xba, 0x2e, 0xca, 0x0a, 0x3a, 0x96, 0x7e, 0xde, 0x0a, 0x99, 0x59, 0x3f, 0xb3, 0x90, 0x9f, 0x56, 0x12, + 0x6b, 0xca, 0xb6, 0x4f, 0xd4, 0x06, 0x69, 0xd6, 0x85, 0xea, 0x02, 0xe7, 0xce, 0xda, 0xeb, 0x8d, 0x50, 0xff, 0x9c, + 0x8f, 0xd6, 0xc5, 0xda, 0x03, 0x97, 0x98, 0x59, 0x3a, 0x57, 0x1c, 0x1a, 0xb9, 0x3f, 0xfa, 0x52, 0xa4, 0x39, 0xe5, + 0x01, 0x1a, 0x44, 0x31, 0xb7, 0xdf, 0x02, 0xe9, 0x87, 0xde, 0x02, 0xd9, 0x47, 0xe7, 0x9c, 0xbc, 0x01, 0x70, 0x3a, + 0x44, 0xc4, 0xad, 0x48, 0xd0, 0xb1, 0x6a, 0x78, 0x6b, 0xe1, 0x9e, 0xf6, 0xd2, 0xb8, 0x97, 0xe6, 0x67, 0x69, 0xbf, + 0x6f, 0x00, 0x34, 0x53, 0x44, 0x86, 0xc7, 0x19, 0xb9, 0x48, 0x5a, 0x08, 0xa6, 0xb4, 0xff, 0x6a, 0x0c, 0x09, 0x02, + 0x01, 0xff, 0xbb, 0xf0, 0x9e, 0x00, 0xda, 0x26, 0x6d, 0xc0, 0x55, 0x8f, 0xe9, 0xc0, 0x6c, 0xc9, 0xd9, 0xaa, 0xb3, + 0x01, 0x28, 0xa7, 0x4a, 0xeb, 0x29, 0x8f, 0x6b, 0x8a, 0x88, 0x54, 0x59, 0xa8, 0xdf, 0x58, 0x4f, 0x26, 0xab, 0x5c, + 0x64, 0xc8, 0x51, 0x99, 0xbe, 0xd6, 0x8c, 0x10, 0xbb, 0xf4, 0xf3, 0x05, 0x2c, 0xd9, 0xf8, 0x13, 0x4e, 0xde, 0x12, + 0x20, 0x6d, 0x67, 0xed, 0xaa, 0xda, 0xe5, 0xb8, 0xb5, 0x9b, 0x03, 0x92, 0xaf, 0x37, 0x1a, 0x8d, 0xb4, 0x9f, 0x9c, + 0x80, 0xa1, 0xea, 0xa9, 0xa5, 0xd0, 0x63, 0xb5, 0xc2, 0xd6, 0xed, 0xc8, 0x65, 0x96, 0x0c, 0xe6, 0x0b, 0xe3, 0xf8, + 0x95, 0xf9, 0xe8, 0xe3, 0xa5, 0xb2, 0x76, 0x1d, 0xf1, 0xf5, 0x1f, 0x65, 0xb5, 0xbe, 0xe7, 0x5d, 0xd5, 0x04, 0x7c, + 0x51, 0xc5, 0x96, 0x7e, 0xc7, 0x7b, 0xb2, 0x77, 0xf1, 0xb5, 0x1b, 0xec, 0x92, 0xef, 0x79, 0x8b, 0x3a, 0xcf, 0x57, + 0xbe, 0x6e, 0x54, 0xe9, 0xf6, 0x5e, 0xb2, 0xc0, 0xb5, 0x77, 0xd4, 0x34, 0xd6, 0x33, 0x3f, 0x7a, 0x58, 0x84, 0x6c, + 0xe7, 0x63, 0xef, 0xab, 0xe6, 0xe9, 0x59, 0x43, 0x6f, 0x52, 0x43, 0x1f, 0x7b, 0x51, 0xb6, 0x4f, 0x4d, 0x23, 0x7a, + 0x0d, 0x1b, 0xfa, 0xd8, 0x5b, 0x72, 0x72, 0x48, 0x30, 0x38, 0x35, 0xe6, 0x8f, 0x0f, 0xa7, 0x33, 0xfc, 0x1d, 0x03, + 0x2a, 0x31, 0x99, 0x4f, 0x8f, 0x69, 0x47, 0x01, 0x66, 0x54, 0xe9, 0xed, 0xd3, 0x03, 0xdb, 0xf1, 0xb2, 0x1e, 0x5a, + 0x7a, 0xf7, 0xe4, 0xe8, 0x76, 0xbc, 0xaa, 0xc6, 0x97, 0x72, 0xc8, 0xf3, 0x7c, 0x36, 0x1a, 0x8d, 0x84, 0x41, 0xe7, + 0xae, 0xf4, 0x06, 0x56, 0x20, 0x83, 0x8b, 0xea, 0x43, 0xb9, 0xf4, 0x76, 0xea, 0xd0, 0xae, 0xfc, 0x49, 0x7e, 0x38, + 0x14, 0x23, 0x73, 0x8c, 0x03, 0xce, 0x49, 0xa1, 0xe4, 0x28, 0x59, 0x4b, 0x10, 0x9d, 0xd2, 0x78, 0x2a, 0xeb, 0xb5, + 0x15, 0x91, 0x57, 0x23, 0xe4, 0x43, 0xf0, 0x93, 0x07, 0x6a, 0xf1, 0x6b, 0x2d, 0x88, 0x3d, 0xf6, 0xa9, 0x52, 0x3a, + 0xc4, 0xab, 0x02, 0x42, 0x84, 0x01, 0x6f, 0xa0, 0x1d, 0x94, 0xe0, 0xb0, 0xc3, 0x7d, 0x44, 0x84, 0xe8, 0xd7, 0x5e, + 0x3e, 0x93, 0xe1, 0xca, 0xbd, 0x41, 0x35, 0x67, 0x80, 0x58, 0xe9, 0x33, 0x70, 0xc1, 0x04, 0xd4, 0x53, 0x7c, 0x8a, + 0xfe, 0xf5, 0xe6, 0x61, 0xd3, 0xf5, 0x69, 0x09, 0xa8, 0x88, 0x9e, 0xfd, 0x7c, 0x0c, 0xe0, 0x9d, 0x5d, 0x9b, 0x91, + 0xf6, 0xf2, 0x37, 0xc0, 0xb0, 0x52, 0x92, 0x68, 0xe7, 0x94, 0x08, 0xdc, 0xf9, 0xc8, 0x96, 0x7e, 0x94, 0x02, 0x31, + 0x77, 0x3c, 0x49, 0x64, 0x0f, 0x36, 0x72, 0x02, 0xb7, 0x18, 0xf0, 0xe8, 0x00, 0x54, 0xae, 0x14, 0xe4, 0x5e, 0x73, + 0x24, 0x77, 0xfc, 0xd0, 0xfb, 0x61, 0x50, 0x0f, 0x7e, 0xe8, 0x9d, 0xa5, 0x24, 0x77, 0x84, 0x67, 0x6a, 0x4a, 0x88, + 0xf8, 0xec, 0x87, 0x41, 0x3e, 0xc0, 0xb3, 0x44, 0x8b, 0xb4, 0xc8, 0xad, 0x26, 0x6a, 0xdc, 0x84, 0x17, 0x89, 0xa4, + 0x21, 0xda, 0x75, 0x1e, 0x11, 0x0b, 0x00, 0xc9, 0xe2, 0xb3, 0x79, 0x43, 0x51, 0xef, 0x26, 0x7c, 0x8b, 0xee, 0xb2, + 0xd8, 0xef, 0x6f, 0xf3, 0xb4, 0xee, 0xe9, 0x50, 0x19, 0x7c, 0x41, 0xaa, 0x09, 0xf0, 0x68, 0x7f, 0x6d, 0x8e, 0x57, + 0xaf, 0x36, 0x47, 0xca, 0x42, 0x95, 0xa8, 0xdf, 0x62, 0x35, 0xeb, 0x21, 0x22, 0x77, 0x96, 0x19, 0x7b, 0x7b, 0xc1, + 0x2b, 0x39, 0xab, 0x62, 0xbb, 0x1c, 0x5f, 0x11, 0xd6, 0x56, 0x12, 0xa0, 0xa3, 0xf5, 0x58, 0x9b, 0x62, 0xe4, 0x57, + 0x0a, 0x09, 0xb8, 0xe8, 0xd8, 0x5a, 0x28, 0x36, 0x5e, 0x80, 0xbe, 0x64, 0x67, 0x1a, 0x60, 0xbd, 0xd1, 0xab, 0x88, + 0xdb, 0xf2, 0x91, 0x0a, 0x6f, 0x72, 0x53, 0x65, 0x56, 0x36, 0x8b, 0x76, 0x3f, 0x55, 0xbc, 0x42, 0xdc, 0x7a, 0xa3, + 0xf6, 0x28, 0x40, 0xed, 0xa1, 0x85, 0x32, 0x40, 0x97, 0xa6, 0x19, 0x00, 0x32, 0x00, 0xc8, 0x54, 0x11, 0x9f, 0x09, + 0x50, 0x69, 0xab, 0x1b, 0x05, 0x4e, 0xa4, 0xd7, 0xc0, 0xb8, 0xc0, 0x4a, 0x1f, 0xd9, 0xc8, 0x60, 0xb1, 0x45, 0x80, + 0x5b, 0x8e, 0xf4, 0x61, 0x1a, 0x4e, 0xb6, 0xd1, 0x1c, 0x26, 0x69, 0x7e, 0x1f, 0x66, 0xa9, 0x84, 0x96, 0xf8, 0x51, + 0xd6, 0x18, 0xb1, 0x80, 0xf4, 0x7d, 0x7a, 0x51, 0x64, 0x31, 0x41, 0xc2, 0x59, 0x4f, 0x1d, 0x40, 0x35, 0x39, 0xd7, + 0x9a, 0x56, 0xcf, 0x6a, 0x93, 0x87, 0x2c, 0xd0, 0xd9, 0x83, 0x31, 0xa9, 0xe5, 0x86, 0x1e, 0xd9, 0x5f, 0x39, 0x9e, + 0x11, 0xbe, 0xeb, 0x19, 0x4e, 0xfd, 0x77, 0x53, 0x03, 0x29, 0x53, 0x02, 0x08, 0x32, 0x38, 0x9a, 0x10, 0xca, 0xd3, + 0x31, 0x99, 0xda, 0xfc, 0x08, 0x84, 0x23, 0x82, 0x57, 0xf0, 0xdc, 0xd0, 0xba, 0xe5, 0xc6, 0xce, 0x22, 0x4f, 0x13, + 0x40, 0x16, 0x2f, 0xf8, 0x16, 0x90, 0x39, 0xf5, 0xaa, 0x90, 0x3d, 0x7b, 0x2e, 0xa6, 0xb3, 0x79, 0xf0, 0x90, 0xd0, + 0xfe, 0xc5, 0x84, 0xdf, 0x74, 0x57, 0xc9, 0x95, 0xa9, 0x75, 0x6f, 0xa2, 0xc7, 0x5c, 0xee, 0xf4, 0x69, 0xc5, 0x31, + 0xe2, 0x19, 0xac, 0x02, 0x72, 0xce, 0x86, 0xfc, 0xfa, 0x1c, 0xb0, 0x5b, 0x56, 0xc2, 0x8b, 0xf8, 0x75, 0x28, 0xab, + 0x05, 0xc8, 0x8f, 0x9c, 0x47, 0xe6, 0x97, 0xaf, 0xb6, 0x43, 0x39, 0xa7, 0x28, 0xa2, 0xe5, 0xd4, 0xb4, 0xa4, 0x90, + 0x1d, 0x7a, 0x0a, 0x26, 0x53, 0x5b, 0xfe, 0xde, 0x26, 0x2e, 0xc9, 0x37, 0x93, 0xc8, 0xbe, 0x0e, 0xb0, 0x66, 0xad, + 0xba, 0x87, 0x6e, 0x08, 0x06, 0x88, 0x8c, 0x50, 0x66, 0x73, 0x7d, 0xb7, 0x1e, 0x0c, 0x14, 0xcc, 0xaf, 0xa0, 0x9b, + 0x16, 0x9d, 0xe2, 0x00, 0x39, 0x6b, 0x5d, 0xa3, 0x52, 0x55, 0x1c, 0x3a, 0xcc, 0xbb, 0x65, 0x55, 0x76, 0x59, 0x7a, + 0x21, 0x48, 0x8d, 0xba, 0x0a, 0x16, 0x29, 0x15, 0x51, 0xbc, 0x27, 0xbf, 0x06, 0x26, 0x9e, 0x59, 0x39, 0x4a, 0xe3, + 0x39, 0x20, 0x06, 0x29, 0x20, 0x4e, 0xf9, 0x15, 0xa0, 0x89, 0x2e, 0xa2, 0x30, 0x7b, 0x1b, 0x57, 0x41, 0x6d, 0x35, + 0xfd, 0xde, 0x81, 0x8c, 0x3d, 0xaf, 0xfb, 0xfd, 0x94, 0x18, 0xfd, 0x30, 0x0a, 0x03, 0xff, 0x1e, 0x4f, 0xf7, 0x4d, + 0x90, 0x9a, 0x57, 0x1e, 0xe0, 0x15, 0x5d, 0x6e, 0x6d, 0xca, 0x15, 0x8d, 0x0b, 0x7f, 0x8d, 0xe0, 0xf0, 0xa9, 0xa3, + 0xd8, 0x6e, 0x53, 0xe5, 0xd4, 0xc6, 0x60, 0x10, 0xc2, 0x7d, 0x2b, 0xe3, 0xf7, 0x89, 0x97, 0xcf, 0xa2, 0x39, 0x28, + 0x4a, 0x33, 0xcd, 0x17, 0x52, 0x48, 0x37, 0x01, 0xfa, 0x68, 0x10, 0x6a, 0x75, 0xe5, 0x1f, 0x89, 0x97, 0xaa, 0x69, + 0x6d, 0x9e, 0x62, 0x8d, 0x02, 0x31, 0x8b, 0xe6, 0x0d, 0xcb, 0xe8, 0x90, 0x54, 0x97, 0x4b, 0xd3, 0x8c, 0x3f, 0xac, + 0x66, 0xa8, 0x56, 0x1c, 0x35, 0x41, 0x8d, 0xd2, 0x0d, 0x5c, 0x00, 0xff, 0x46, 0x77, 0x1c, 0xd5, 0x28, 0x52, 0x34, + 0xe0, 0x13, 0xc4, 0x88, 0x35, 0x9b, 0x27, 0xac, 0x35, 0x75, 0xcd, 0xe8, 0xf7, 0x65, 0xc8, 0x90, 0x49, 0x42, 0x9e, + 0x3e, 0x5c, 0xae, 0x9f, 0x48, 0x75, 0x01, 0xfc, 0xca, 0x15, 0x9b, 0xf5, 0x7a, 0x73, 0x80, 0xeb, 0x85, 0xf5, 0x0b, + 0x1b, 0x57, 0x70, 0x7e, 0x49, 0xf0, 0xbb, 0xea, 0x47, 0x98, 0x65, 0x50, 0x05, 0x64, 0xfc, 0xb1, 0x90, 0xce, 0x73, + 0x17, 0x93, 0xfa, 0xcd, 0x48, 0x5d, 0x50, 0x66, 0xe9, 0xdc, 0xe2, 0x04, 0x01, 0xe7, 0x61, 0xf5, 0x04, 0x92, 0x7d, + 0xf9, 0xd8, 0xa7, 0x19, 0x05, 0xaa, 0x23, 0xc0, 0x67, 0xb3, 0x7e, 0x08, 0xfb, 0x07, 0x44, 0x16, 0xea, 0x6f, 0xde, + 0xc8, 0x59, 0x43, 0xf2, 0x40, 0xaa, 0xb9, 0x8f, 0xe1, 0xd4, 0x58, 0xe0, 0x4b, 0x8b, 0xde, 0x54, 0xf0, 0x9a, 0x90, + 0xb9, 0x17, 0x68, 0xed, 0x5b, 0xc0, 0x11, 0x22, 0xb8, 0x8c, 0x52, 0x9c, 0xf6, 0x76, 0xbd, 0x00, 0xb9, 0xcd, 0x2d, + 0xc8, 0xeb, 0x77, 0x2e, 0x7e, 0x71, 0x8a, 0xf4, 0x2c, 0xba, 0xc0, 0x40, 0x17, 0x64, 0xde, 0xf8, 0x67, 0x05, 0x2b, + 0x17, 0xd0, 0x7b, 0xa9, 0x58, 0xc9, 0xc9, 0xb6, 0x53, 0x7f, 0x94, 0xca, 0x7e, 0x7b, 0x66, 0x4d, 0xe0, 0x57, 0x89, + 0xfd, 0x12, 0x99, 0x7c, 0xd3, 0x63, 0x93, 0xaf, 0x0c, 0x8b, 0x4e, 0x2d, 0x83, 0x73, 0x7a, 0x64, 0x70, 0xee, 0xed, + 0xac, 0xda, 0x84, 0x30, 0x14, 0x24, 0x81, 0xa6, 0x4b, 0x0f, 0xeb, 0xa6, 0x3f, 0x3f, 0x69, 0x51, 0x6d, 0xd5, 0xbe, + 0x75, 0x3f, 0x0e, 0xb1, 0x8b, 0x5f, 0x25, 0x9e, 0x21, 0x22, 0xf5, 0x81, 0x0e, 0x4c, 0x06, 0x4f, 0x5c, 0xf6, 0xfb, + 0x50, 0xd8, 0x6c, 0x3c, 0x1f, 0xd5, 0xc5, 0xcf, 0xc5, 0x03, 0xa0, 0x3a, 0x54, 0x60, 0x97, 0x43, 0x19, 0xca, 0x88, + 0x4d, 0x6d, 0xb9, 0xe7, 0xf7, 0x57, 0x61, 0x0e, 0xf2, 0x8e, 0x86, 0xc7, 0x39, 0x03, 0x31, 0x0c, 0xbe, 0xfe, 0xc3, + 0x93, 0x7d, 0xda, 0xfc, 0x70, 0x06, 0xdf, 0x1d, 0x9d, 0x7d, 0x44, 0xba, 0x9b, 0xb3, 0x75, 0x59, 0xdc, 0xa7, 0xb1, + 0x38, 0xfb, 0x01, 0x52, 0x7f, 0x38, 0x2b, 0xca, 0xb3, 0x1f, 0x54, 0x65, 0x7e, 0x38, 0xa3, 0x05, 0x37, 0xfa, 0xdd, + 0x9a, 0x78, 0xff, 0xa8, 0x34, 0x03, 0xda, 0x12, 0x22, 0xb3, 0xb4, 0xfa, 0x11, 0x94, 0x88, 0x8a, 0x1f, 0x55, 0x46, + 0xb5, 0x5a, 0x3b, 0xce, 0x87, 0x44, 0x23, 0x65, 0xd3, 0x84, 0xc4, 0xd5, 0x12, 0xd6, 0xa1, 0x9e, 0x9d, 0x36, 0xdf, + 0x8e, 0xf3, 0x40, 0x1d, 0x10, 0x39, 0xbf, 0xce, 0x47, 0x5b, 0xfa, 0x1a, 0x7c, 0xeb, 0x70, 0xc8, 0x47, 0x3b, 0xf3, + 0xd3, 0x27, 0x6b, 0xa5, 0x8c, 0x3b, 0x52, 0xe4, 0x42, 0xc8, 0x19, 0xb7, 0xed, 0x31, 0xe0, 0x00, 0xf0, 0x0f, 0x07, + 0xfa, 0xbd, 0x93, 0xbf, 0xd5, 0x6e, 0x69, 0xd5, 0xf3, 0x63, 0x8b, 0x3b, 0xe3, 0x75, 0x6d, 0x88, 0xda, 0xf6, 0x12, + 0x5b, 0x7a, 0xdf, 0x34, 0xa8, 0x29, 0xa2, 0x9f, 0xb0, 0x9a, 0x58, 0xc5, 0x61, 0x41, 0x4a, 0x48, 0x62, 0x38, 0x46, + 0x3b, 0xf4, 0x38, 0x5d, 0x2c, 0x3d, 0xb9, 0xef, 0xf0, 0x72, 0xeb, 0xfb, 0x80, 0xa4, 0x55, 0x38, 0xff, 0xe8, 0x85, + 0x06, 0x1e, 0xbd, 0xc8, 0xab, 0x22, 0x13, 0x23, 0x41, 0xa3, 0xfc, 0x96, 0xc4, 0x99, 0x33, 0xac, 0xc5, 0x99, 0x02, + 0x0b, 0x0b, 0x09, 0xa0, 0xbb, 0x28, 0x29, 0x3d, 0x38, 0x7b, 0xb2, 0x2f, 0x9b, 0xdf, 0x09, 0x1e, 0x62, 0xb4, 0x00, + 0x46, 0x9c, 0x5d, 0xbb, 0xbc, 0x87, 0xb0, 0xcc, 0xbd, 0xdf, 0xdf, 0xde, 0xe5, 0x05, 0x84, 0x68, 0x9e, 0x49, 0xc5, + 0x6a, 0x79, 0x06, 0x8c, 0x79, 0x22, 0x3e, 0x0b, 0x2b, 0x39, 0x0d, 0xaa, 0x8e, 0x62, 0xf5, 0x36, 0x9e, 0x7b, 0x40, + 0xf1, 0xfd, 0x21, 0x01, 0x2e, 0x77, 0x9f, 0xbd, 0x51, 0xae, 0xa9, 0xa4, 0x47, 0x9e, 0x63, 0xb4, 0x64, 0x02, 0x14, + 0xcf, 0x10, 0x27, 0x29, 0xac, 0x9e, 0x9b, 0x20, 0x15, 0xf9, 0xfa, 0x84, 0xe2, 0x8b, 0xe6, 0x51, 0xd4, 0xb0, 0x90, + 0x25, 0x70, 0x3c, 0x24, 0xb3, 0x6c, 0x8e, 0x2c, 0xe5, 0x69, 0x7b, 0x8a, 0x74, 0x74, 0x62, 0x89, 0xdf, 0xd6, 0xfc, + 0x7a, 0x91, 0x8a, 0xc0, 0xa4, 0x9d, 0xad, 0xcc, 0xbd, 0x10, 0x86, 0x2a, 0xe1, 0xde, 0xeb, 0x7a, 0x16, 0xca, 0x4d, + 0xd1, 0xaa, 0x98, 0x3d, 0x4c, 0x89, 0x19, 0xa6, 0x58, 0x7f, 0x61, 0xc3, 0x6f, 0x12, 0x2f, 0x06, 0xc3, 0xf5, 0x92, + 0x97, 0xb3, 0x8d, 0x59, 0x08, 0x87, 0xc3, 0x66, 0x52, 0xcc, 0x96, 0x10, 0xe6, 0xba, 0x9c, 0x1f, 0x0e, 0x5d, 0x2d, + 0x5b, 0x0b, 0x0f, 0x1e, 0xaa, 0x16, 0x6e, 0x1a, 0x96, 0xc3, 0xcf, 0x64, 0x16, 0x63, 0xfb, 0x1a, 0x9f, 0xd9, 0x9f, + 0x2f, 0xba, 0x67, 0x09, 0x92, 0x6f, 0xac, 0x81, 0x76, 0x6c, 0xd6, 0xee, 0x70, 0x35, 0x02, 0x92, 0xd2, 0xdd, 0xe8, + 0xef, 0xca, 0x4e, 0x9e, 0x12, 0xe4, 0x8e, 0x56, 0x60, 0xbf, 0xfb, 0xc6, 0x9f, 0x68, 0xb1, 0x07, 0xed, 0x36, 0xb6, + 0x84, 0xa8, 0xa6, 0x3d, 0x97, 0x2b, 0xc5, 0xd2, 0xbc, 0x95, 0x36, 0x7a, 0x3e, 0xac, 0xcf, 0x7d, 0x23, 0x07, 0x0a, + 0xc6, 0x88, 0xa7, 0xd6, 0x41, 0x34, 0x9b, 0x03, 0x0d, 0x06, 0x9a, 0x47, 0x78, 0x6a, 0xa1, 0x83, 0x32, 0x6b, 0xc3, + 0x7e, 0x92, 0x9c, 0x2c, 0x8f, 0xc3, 0xb7, 0xf0, 0x2f, 0x9f, 0x61, 0x93, 0x98, 0x62, 0x7b, 0xfc, 0xad, 0x52, 0x54, + 0x78, 0x6c, 0x41, 0x5c, 0x6b, 0x37, 0xa2, 0x36, 0x54, 0x0e, 0xff, 0x12, 0xf6, 0x11, 0xf6, 0x1b, 0x9a, 0x20, 0x0c, + 0x76, 0xfd, 0x99, 0x40, 0x88, 0x58, 0x88, 0x17, 0xfc, 0xad, 0x92, 0x54, 0x74, 0xc2, 0x67, 0x8b, 0x12, 0x78, 0xeb, + 0x30, 0xa0, 0x4f, 0x28, 0x52, 0x0a, 0x61, 0x68, 0x26, 0xf4, 0x8e, 0xfe, 0x1b, 0xb1, 0x93, 0x4d, 0x72, 0x2b, 0xe4, + 0x03, 0x49, 0x25, 0xc1, 0x04, 0x2b, 0x2f, 0x94, 0x2f, 0xdc, 0x0b, 0xa5, 0xd6, 0x5a, 0xd0, 0xfa, 0xe5, 0x4f, 0x12, + 0xcf, 0xe0, 0xef, 0x81, 0x8c, 0x41, 0xb7, 0x11, 0xd5, 0x24, 0xc7, 0xf4, 0x51, 0x3a, 0xcf, 0x40, 0x05, 0x74, 0xb6, + 0xce, 0xc2, 0x7a, 0x59, 0x94, 0xab, 0x56, 0xa4, 0xa8, 0x2c, 0x7d, 0xa4, 0x1e, 0x63, 0x5e, 0x98, 0x27, 0x27, 0xf2, + 0xc1, 0x23, 0x00, 0xc6, 0xa3, 0x3c, 0xad, 0x3a, 0x4a, 0xeb, 0x07, 0x96, 0x01, 0x23, 0x70, 0xa2, 0x0c, 0x78, 0x84, + 0x65, 0x60, 0x9e, 0x76, 0x19, 0x6a, 0x10, 0x6b, 0x54, 0x5d, 0xa9, 0x0d, 0xe6, 0x44, 0x51, 0xf2, 0x29, 0x96, 0x56, + 0x18, 0x43, 0x53, 0x57, 0x1e, 0x59, 0x2f, 0x39, 0x61, 0x4f, 0x76, 0x03, 0xe9, 0x16, 0x36, 0x0a, 0x67, 0xd0, 0xb5, + 0x2c, 0x51, 0x2e, 0xba, 0x65, 0x44, 0x99, 0x08, 0xa9, 0x9f, 0x3d, 0x9c, 0x69, 0xb5, 0xdf, 0xd8, 0x49, 0xfb, 0xf6, + 0x48, 0xd1, 0x0b, 0x06, 0xed, 0xd3, 0x1e, 0x29, 0xf5, 0xac, 0x91, 0xcb, 0xc0, 0x96, 0x2e, 0x55, 0x3d, 0xff, 0x05, + 0xca, 0x77, 0x30, 0x33, 0xce, 0x66, 0xbf, 0xeb, 0xcd, 0xed, 0xc9, 0xbe, 0x6e, 0x7e, 0x67, 0xbd, 0x1e, 0x6c, 0x0d, + 0x32, 0xf1, 0x85, 0x62, 0xa1, 0xb2, 0x0a, 0xb1, 0x82, 0xb4, 0xff, 0x25, 0xbc, 0xdf, 0xe1, 0xad, 0x11, 0x9a, 0x95, + 0xf1, 0x30, 0x1f, 0x3d, 0xd9, 0x8b, 0xe6, 0xf7, 0xce, 0xb2, 0xad, 0x5c, 0x95, 0xcc, 0xf6, 0xfb, 0x51, 0xd2, 0x9c, + 0x3d, 0x5e, 0x23, 0xa9, 0x03, 0x7c, 0xbc, 0x3e, 0xc3, 0x47, 0x2a, 0xa1, 0xd4, 0x82, 0xaa, 0x06, 0xad, 0x8f, 0xfd, + 0xde, 0x7a, 0x4e, 0x1f, 0x3f, 0x96, 0xd3, 0x2d, 0x29, 0xc2, 0xf8, 0x81, 0xc1, 0x94, 0x9d, 0x38, 0x75, 0xc9, 0x9b, + 0x21, 0xbd, 0xeb, 0x56, 0x49, 0x5d, 0xf6, 0x28, 0x11, 0x84, 0x3a, 0x58, 0xbf, 0xd8, 0x0f, 0x61, 0x66, 0x8b, 0xfe, + 0xb0, 0x59, 0xcd, 0x09, 0x10, 0x11, 0xd0, 0x5a, 0xe5, 0x7d, 0xe0, 0x98, 0x2f, 0xcc, 0x9a, 0x1b, 0xd2, 0xad, 0x37, + 0x57, 0xda, 0x2b, 0x29, 0xa0, 0x9f, 0x83, 0xcc, 0xed, 0xa3, 0x5b, 0xae, 0x5a, 0xe6, 0xb9, 0xb4, 0xe5, 0x80, 0x45, + 0x0b, 0x81, 0x9a, 0x9d, 0x4b, 0x87, 0x03, 0x05, 0xa1, 0xae, 0x44, 0x15, 0x71, 0x75, 0x14, 0x2d, 0x44, 0xad, 0x56, + 0xed, 0x72, 0xb2, 0xa9, 0x90, 0x2d, 0x89, 0x20, 0xa3, 0x64, 0xaf, 0x84, 0xfa, 0x28, 0x57, 0x7b, 0xa6, 0xe1, 0x00, + 0x4d, 0xc0, 0xa6, 0x0d, 0xfe, 0x16, 0xb8, 0x97, 0xc1, 0x99, 0x69, 0x9f, 0x86, 0x11, 0x70, 0x9a, 0x43, 0xcc, 0x9f, + 0xdf, 0xf5, 0xa0, 0x82, 0x07, 0x1d, 0xe9, 0xaf, 0xeb, 0x59, 0x81, 0x67, 0xee, 0x89, 0xe7, 0x6f, 0x4e, 0xa4, 0x17, + 0x39, 0x3c, 0xd0, 0x34, 0x88, 0x19, 0x7f, 0x51, 0x96, 0xe1, 0x6e, 0xb4, 0x2c, 0x8b, 0x95, 0x17, 0xe9, 0x7d, 0x3c, + 0x93, 0x62, 0x20, 0x31, 0x63, 0x66, 0x74, 0x15, 0xeb, 0x38, 0x87, 0x71, 0x6f, 0x4f, 0xc2, 0x0a, 0xed, 0x9f, 0x25, + 0xf6, 0xba, 0x00, 0x2c, 0x87, 0xac, 0x41, 0x2b, 0xbc, 0xd3, 0xed, 0xed, 0x1e, 0x97, 0xec, 0x28, 0x6e, 0x00, 0xfd, + 0xac, 0x86, 0x96, 0x09, 0x6a, 0x99, 0x75, 0x27, 0x93, 0x29, 0x92, 0xcb, 0xb7, 0x61, 0x6f, 0x58, 0x91, 0xcf, 0x1b, + 0xb9, 0x3d, 0xbc, 0x0f, 0x57, 0x22, 0xd6, 0x16, 0x74, 0xd2, 0x91, 0x71, 0xb8, 0x17, 0x9a, 0x1b, 0xe9, 0xfe, 0x49, + 0x95, 0x84, 0xa5, 0x88, 0xe1, 0x16, 0xc8, 0xf6, 0x6a, 0x5b, 0x09, 0x4a, 0xe0, 0x83, 0xfd, 0x58, 0x8a, 0x65, 0xba, + 0x15, 0x80, 0xeb, 0xc0, 0xff, 0x94, 0x88, 0x84, 0xee, 0xce, 0x43, 0x14, 0x6b, 0xe4, 0x7d, 0x83, 0x68, 0xec, 0xaf, + 0x41, 0x4e, 0x03, 0x32, 0x91, 0x62, 0x24, 0x0b, 0x06, 0x3e, 0x80, 0x9c, 0xaf, 0xc1, 0x24, 0x37, 0xcd, 0x3d, 0x3f, + 0xc8, 0x75, 0x07, 0xd3, 0x3e, 0xe8, 0x5e, 0x5c, 0x6b, 0x96, 0x83, 0x57, 0x4c, 0xc4, 0xff, 0x56, 0x7b, 0x25, 0xcb, + 0x59, 0xe6, 0x37, 0xe6, 0xa2, 0x93, 0xc1, 0x55, 0x43, 0xf8, 0xc5, 0x2c, 0x9b, 0xf3, 0x68, 0x96, 0xe9, 0xa8, 0xff, + 0xa2, 0x39, 0x2a, 0x05, 0xe0, 0xd4, 0xf1, 0x02, 0xac, 0xa1, 0xaf, 0x74, 0xd3, 0x8a, 0x47, 0x1a, 0x63, 0x14, 0x54, + 0xe8, 0x20, 0xf4, 0xb7, 0x1a, 0x90, 0x36, 0x98, 0xa4, 0x49, 0xa8, 0x7c, 0x70, 0x41, 0x37, 0xcc, 0xcb, 0x95, 0xcb, + 0x55, 0x93, 0xaa, 0xe5, 0x97, 0x23, 0xea, 0xbb, 0x5a, 0x72, 0xa9, 0x36, 0x9f, 0x1a, 0x65, 0x8d, 0x20, 0x93, 0xa3, + 0xf4, 0xfb, 0x94, 0x0b, 0xb7, 0x32, 0x26, 0xeb, 0xc3, 0xc1, 0x2b, 0xb8, 0xa9, 0xf1, 0xeb, 0x9c, 0x08, 0x45, 0xed, + 0x21, 0x11, 0xb6, 0x76, 0x2b, 0x74, 0xef, 0x71, 0xa3, 0x34, 0x8f, 0xb2, 0x4d, 0x2c, 0x2a, 0xaf, 0x97, 0x80, 0xb5, + 0xb8, 0x07, 0xbc, 0xa8, 0xb4, 0xf4, 0x2b, 0x56, 0x00, 0x7a, 0x80, 0x14, 0x36, 0x7e, 0x44, 0x06, 0xac, 0x8f, 0x5e, + 0xea, 0xf7, 0xfb, 0xc6, 0x94, 0xff, 0xe1, 0x21, 0x07, 0x92, 0x42, 0x51, 0xd6, 0x3b, 0x98, 0x40, 0x70, 0xed, 0x24, + 0xed, 0x59, 0xcd, 0xaf, 0xd7, 0xb5, 0x07, 0xfc, 0x56, 0xbe, 0x45, 0x62, 0xf5, 0xda, 0xbe, 0xd8, 0xec, 0xd3, 0xea, + 0xc6, 0x68, 0x1c, 0x04, 0x4b, 0xab, 0xb7, 0x5a, 0xe5, 0x90, 0x37, 0xbc, 0x02, 0x91, 0xca, 0xba, 0xba, 0x56, 0xce, + 0xd5, 0xb5, 0xe0, 0xc8, 0x25, 0x5b, 0xf2, 0x1c, 0xfe, 0x0b, 0xb9, 0x57, 0x1e, 0x0e, 0x85, 0xdf, 0xef, 0xa7, 0x33, + 0xd2, 0xca, 0x02, 0x7b, 0xda, 0xba, 0xf6, 0x42, 0xff, 0x70, 0xf8, 0x11, 0xbc, 0x46, 0xfc, 0xc3, 0xa1, 0xec, 0xf7, + 0x3f, 0x99, 0x9b, 0xcc, 0xf9, 0x58, 0x29, 0x65, 0x2f, 0x51, 0xe9, 0xfe, 0x36, 0xe1, 0xbd, 0xff, 0x3d, 0xfa, 0xdf, + 0xa3, 0xcb, 0x9e, 0x0a, 0x01, 0x4b, 0xf8, 0x0c, 0x6f, 0xe8, 0x4c, 0x5d, 0xce, 0x99, 0x74, 0x77, 0x57, 0x7e, 0xe8, + 0x3d, 0x0d, 0x15, 0xdf, 0x9b, 0x9b, 0x36, 0xfe, 0x5a, 0x1d, 0x69, 0x12, 0x3a, 0x2e, 0xfa, 0x87, 0xc3, 0xe7, 0x44, + 0xeb, 0xd3, 0x52, 0xa5, 0x4f, 0x53, 0x38, 0x4a, 0x86, 0x18, 0xd7, 0x2d, 0x4c, 0x07, 0xf6, 0xe3, 0xe6, 0xab, 0xe4, + 0xc5, 0x59, 0x0a, 0xd7, 0xde, 0x7c, 0x96, 0xce, 0xa7, 0x60, 0x5d, 0x19, 0xe6, 0xb3, 0x7a, 0x1e, 0x40, 0xea, 0x10, + 0xd2, 0xac, 0x69, 0xf8, 0x97, 0xca, 0x15, 0xbc, 0xb5, 0xc7, 0xbb, 0x81, 0x8b, 0x52, 0x47, 0xfa, 0xa4, 0x8d, 0xa6, + 0x4b, 0x2a, 0xf9, 0x4f, 0x22, 0x8f, 0x31, 0x66, 0xe3, 0x35, 0xf1, 0x7e, 0x16, 0xf9, 0xab, 0x02, 0xb0, 0x8b, 0x00, + 0x0c, 0x39, 0x9d, 0x3b, 0x92, 0xf8, 0xcf, 0xc9, 0xf7, 0x7f, 0x4c, 0x97, 0xf6, 0xb1, 0x2c, 0xee, 0x4a, 0x51, 0x55, + 0x47, 0xa5, 0xed, 0x6c, 0xb9, 0x1e, 0x98, 0x44, 0xfb, 0x7d, 0xc9, 0x24, 0x9a, 0x62, 0x28, 0x0a, 0xdc, 0x1a, 0x7b, + 0xd3, 0x94, 0x2b, 0xc6, 0xea, 0x91, 0xb1, 0x7e, 0xbe, 0xdc, 0xbd, 0x8d, 0xbd, 0xd4, 0x0f, 0x52, 0x10, 0x84, 0x35, + 0x94, 0x52, 0x8a, 0x7c, 0x70, 0x3e, 0xc3, 0x54, 0xa2, 0xd6, 0xa5, 0x54, 0xf9, 0xc3, 0x48, 0xf3, 0x61, 0x0a, 0x7a, + 0xd9, 0xbf, 0x57, 0x30, 0xff, 0x75, 0x7b, 0xb0, 0x3e, 0xad, 0xcb, 0x34, 0xaa, 0x88, 0x2a, 0x2f, 0x4c, 0xb5, 0x09, + 0x44, 0xf0, 0x6b, 0x61, 0xf1, 0xfd, 0xfa, 0xe4, 0x48, 0xd0, 0x98, 0xc9, 0xf2, 0xe9, 0xc8, 0xfd, 0xc2, 0xbe, 0x72, + 0x1d, 0xcf, 0xff, 0xdc, 0xcc, 0xff, 0x01, 0x3a, 0x43, 0x16, 0xd7, 0xdc, 0x32, 0x58, 0xe0, 0xec, 0x97, 0xae, 0x1e, + 0xf0, 0x37, 0xf3, 0xc4, 0x35, 0xd0, 0x31, 0x5f, 0xa3, 0xab, 0x62, 0x3a, 0x2b, 0x06, 0xc0, 0x65, 0xeb, 0x37, 0xd6, + 0x9c, 0x78, 0x63, 0x51, 0x5e, 0xc9, 0x05, 0xa1, 0xaf, 0xab, 0x30, 0x1b, 0x57, 0xc5, 0xa6, 0x12, 0xc5, 0xa6, 0xee, + 0x91, 0x5a, 0x36, 0x9f, 0xd6, 0xb6, 0x42, 0xf6, 0xaf, 0xa2, 0xc5, 0xe0, 0x65, 0x58, 0x27, 0xa3, 0x2c, 0x5d, 0x4f, + 0x81, 0x5f, 0x2f, 0x80, 0xb3, 0xc8, 0xbc, 0xf2, 0xd5, 0xd9, 0x03, 0xb6, 0x68, 0x3c, 0x05, 0x72, 0x54, 0xfa, 0x23, + 0x6f, 0x8c, 0x4e, 0x4f, 0xf4, 0xfb, 0xf9, 0x94, 0x62, 0xbe, 0xfe, 0x0a, 0xf0, 0x5c, 0xb5, 0x5c, 0x80, 0xbe, 0x0c, + 0x75, 0x50, 0x89, 0x52, 0x2b, 0x86, 0x11, 0x0b, 0x7f, 0x15, 0x48, 0xe4, 0x4c, 0x81, 0xcd, 0x2a, 0x4a, 0x42, 0x25, + 0x2a, 0x25, 0x5b, 0x13, 0xd4, 0xd2, 0xfb, 0xa2, 0xac, 0xf7, 0x15, 0x38, 0x4a, 0x46, 0xda, 0x2c, 0x27, 0xcd, 0xb8, + 0x02, 0x65, 0x2e, 0xfa, 0xc1, 0xfe, 0x55, 0x79, 0x7e, 0x23, 0xf3, 0x59, 0xee, 0x3b, 0x3a, 0xa7, 0xed, 0xb8, 0x40, + 0x99, 0x5b, 0x4e, 0x5b, 0x2d, 0x79, 0x4c, 0xde, 0xb3, 0x60, 0xdb, 0x7f, 0x91, 0x20, 0xc5, 0x22, 0xcc, 0x27, 0x54, + 0xd9, 0xfc, 0x1d, 0x42, 0x6d, 0x71, 0x60, 0x8f, 0x5d, 0x98, 0x88, 0xff, 0x16, 0x2c, 0x89, 0x61, 0x56, 0x8a, 0x30, + 0xde, 0x81, 0xf7, 0xcf, 0xa6, 0x12, 0xa3, 0x33, 0x74, 0x72, 0x3f, 0x7b, 0x48, 0xeb, 0xe4, 0xec, 0xed, 0xeb, 0xb3, + 0x1f, 0x7a, 0x83, 0x62, 0x94, 0xc6, 0x83, 0xde, 0x0f, 0x67, 0xab, 0x0d, 0xa0, 0x65, 0x8a, 0xb3, 0x98, 0x4c, 0x69, + 0x22, 0x3e, 0x23, 0xc3, 0xe0, 0x59, 0x9d, 0x88, 0x33, 0x9a, 0x98, 0xee, 0x6b, 0x94, 0x26, 0xdf, 0x8e, 0xc2, 0x1c, + 0x5e, 0x2e, 0xc5, 0xa6, 0x12, 0x31, 0xd8, 0x29, 0xd5, 0x3c, 0xcb, 0xdb, 0x67, 0x71, 0x3e, 0xea, 0x90, 0x55, 0x3a, + 0xf0, 0xb7, 0x27, 0xd2, 0xae, 0x4a, 0x57, 0x40, 0xe8, 0x01, 0x70, 0xd2, 0x95, 0x3f, 0x0f, 0x07, 0x91, 0x40, 0xa8, + 0x05, 0x73, 0x32, 0x8d, 0xe8, 0x86, 0xf4, 0x0a, 0xfb, 0x0c, 0xcc, 0x42, 0x4a, 0xf3, 0xe0, 0xe6, 0x6a, 0x31, 0x74, + 0x57, 0xac, 0x1c, 0x85, 0xd5, 0x5a, 0x44, 0x35, 0xb2, 0x1e, 0x83, 0xf3, 0x0e, 0x44, 0x00, 0x28, 0x72, 0xf0, 0x8c, + 0x47, 0xfd, 0x7e, 0xa4, 0x82, 0x72, 0x12, 0xfa, 0x45, 0xa1, 0x5f, 0x1a, 0x8e, 0x32, 0xe6, 0xef, 0x43, 0xcd, 0x11, + 0x50, 0x6f, 0x79, 0xa8, 0xe8, 0x02, 0x70, 0x39, 0x47, 0xcc, 0x38, 0xef, 0x71, 0x17, 0x98, 0x53, 0x51, 0x50, 0xa8, + 0xeb, 0x60, 0xa9, 0x00, 0xe8, 0x4d, 0x7d, 0xa4, 0xe7, 0xa4, 0x49, 0xd0, 0x78, 0x6e, 0xe0, 0xf1, 0x6a, 0xb8, 0xa8, + 0x56, 0x42, 0xea, 0x2d, 0x74, 0x4a, 0x55, 0x87, 0x40, 0x20, 0xf0, 0x7f, 0x78, 0xfb, 0x16, 0xee, 0xb6, 0x6d, 0x6c, + 0xdd, 0xbf, 0x62, 0xf1, 0xa6, 0x2a, 0x11, 0x41, 0xb2, 0xe4, 0x24, 0x9d, 0x29, 0x65, 0x58, 0xc7, 0xcd, 0xa3, 0x4d, + 0xa7, 0x89, 0xd3, 0x38, 0xed, 0x74, 0xaa, 0xab, 0xe3, 0xd2, 0x24, 0x6c, 0xb1, 0xa1, 0x01, 0x95, 0xa4, 0xfc, 0x88, + 0xc4, 0xff, 0x7e, 0xd7, 0xde, 0x78, 0x92, 0xa2, 0x9d, 0xcc, 0xdc, 0x73, 0xef, 0xca, 0x5a, 0xb1, 0x08, 0x82, 0x78, + 0x63, 0x63, 0x63, 0x3f, 0xbe, 0xdd, 0x8c, 0x19, 0x76, 0xcb, 0x5d, 0x8e, 0x64, 0x5d, 0x14, 0x5c, 0xec, 0x04, 0x86, + 0x6e, 0xc6, 0x25, 0x33, 0x07, 0x57, 0x33, 0xac, 0x93, 0x8a, 0x02, 0xec, 0xea, 0x02, 0x64, 0x2f, 0x0c, 0x75, 0xdd, + 0xcc, 0x96, 0xeb, 0xc0, 0xd7, 0xa5, 0x0b, 0x5f, 0x52, 0xf0, 0x72, 0x25, 0x45, 0x99, 0x5d, 0xf3, 0x9f, 0xec, 0xcb, + 0x66, 0x2c, 0x29, 0xb4, 0x23, 0x7d, 0xd3, 0xee, 0x8e, 0x16, 0xe3, 0xd8, 0x72, 0x7c, 0x4b, 0xa5, 0x3b, 0x3d, 0xaa, + 0x5e, 0x08, 0x6d, 0x9d, 0x6b, 0x99, 0xa5, 0x29, 0x17, 0xaf, 0x45, 0x9a, 0x25, 0x5e, 0x72, 0xac, 0x63, 0x55, 0xbb, + 0x20, 0x58, 0x2e, 0x4c, 0xf2, 0x8b, 0xac, 0xc4, 0xd8, 0xc1, 0x8d, 0x46, 0xb5, 0xa2, 0x4e, 0x99, 0x18, 0x18, 0xf2, + 0x3d, 0x06, 0xdf, 0x66, 0x65, 0x02, 0x0c, 0x3f, 0x26, 0xea, 0x4b, 0x7a, 0x0a, 0x01, 0x1f, 0x54, 0x68, 0xee, 0x17, + 0x1c, 0xc1, 0xaf, 0xad, 0xca, 0x1c, 0x98, 0x6c, 0xad, 0x82, 0x44, 0xdc, 0xbb, 0x6c, 0xae, 0x17, 0xd1, 0x42, 0xdd, + 0x85, 0x7a, 0xf1, 0x76, 0xdb, 0x4b, 0x14, 0x1d, 0x70, 0xf2, 0xd3, 0xe0, 0x55, 0x9c, 0xe5, 0x3c, 0xdd, 0xab, 0xe4, + 0x9e, 0xda, 0x50, 0x7b, 0xca, 0x99, 0x03, 0x76, 0xde, 0xd7, 0xd5, 0x9e, 0x5e, 0xd3, 0x7b, 0xba, 0x9d, 0x7b, 0x70, + 0xc1, 0xc0, 0x9d, 0x7b, 0x99, 0x5d, 0x73, 0xb1, 0x07, 0xca, 0x40, 0x6b, 0x3c, 0x50, 0x97, 0xd5, 0x48, 0x4d, 0x8c, + 0x8e, 0x61, 0x9d, 0xe8, 0x83, 0x39, 0xa0, 0xdf, 0x43, 0x58, 0xfb, 0xd6, 0xdb, 0x95, 0x3e, 0x68, 0x03, 0xfa, 0xd3, + 0xd2, 0xf4, 0x41, 0x07, 0x8e, 0x57, 0xd1, 0x81, 0x1b, 0x43, 0xaa, 0x41, 0x5b, 0x8d, 0xac, 0x02, 0xc5, 0x1b, 0xde, + 0xe2, 0xdd, 0xb9, 0x96, 0x6c, 0xbc, 0x97, 0x88, 0xf1, 0x95, 0x89, 0x2a, 0xce, 0xc4, 0xb1, 0x97, 0xca, 0x6b, 0xed, + 0x24, 0x23, 0x8c, 0x6f, 0x59, 0x49, 0xfd, 0x1d, 0x62, 0x6e, 0x91, 0xe6, 0x30, 0x78, 0x19, 0x56, 0x64, 0xc6, 0xfb, + 0x7d, 0x39, 0x93, 0x51, 0x39, 0x13, 0xfb, 0x65, 0xa4, 0xc0, 0xda, 0xee, 0x13, 0x01, 0x3d, 0x28, 0x01, 0xf2, 0x05, + 0x40, 0xd5, 0x43, 0xc2, 0x9f, 0x87, 0xa4, 0x3e, 0x9d, 0x42, 0x9f, 0x42, 0x5b, 0xaf, 0xb8, 0x82, 0x78, 0x55, 0x37, + 0x46, 0xb6, 0x51, 0x41, 0x8b, 0xc7, 0xf2, 0xac, 0x36, 0x8c, 0xcd, 0xa9, 0xf5, 0xaf, 0x37, 0x1b, 0x4c, 0xd9, 0x5c, + 0xa8, 0x55, 0x18, 0x92, 0xe8, 0xa6, 0xf4, 0x22, 0x89, 0x58, 0xd8, 0xac, 0xd6, 0xe6, 0x37, 0x61, 0x40, 0x32, 0x91, + 0xe2, 0x7e, 0xb6, 0xc4, 0xb9, 0x8b, 0xc7, 0xf3, 0xaa, 0xaf, 0xb5, 0xb4, 0xc8, 0xb4, 0xf9, 0x4e, 0x5f, 0x86, 0x34, + 0x15, 0x35, 0xa4, 0x51, 0x67, 0x06, 0xdd, 0xb7, 0xcb, 0x5b, 0x56, 0x23, 0x4c, 0x80, 0x57, 0x3a, 0x83, 0x6e, 0x34, + 0x1e, 0x88, 0x65, 0x35, 0x2a, 0xd6, 0x42, 0x20, 0xf0, 0x30, 0xe4, 0x98, 0x59, 0x42, 0x92, 0x7d, 0xe2, 0xdf, 0xa9, + 0x38, 0x0b, 0x45, 0x7c, 0x63, 0x90, 0xbd, 0x2b, 0xeb, 0xda, 0x5d, 0x47, 0x7e, 0x4e, 0x2c, 0xac, 0xf6, 0x1f, 0x9a, + 0x47, 0xad, 0x71, 0x16, 0xd0, 0xd6, 0xb4, 0xba, 0xe1, 0x70, 0x8f, 0xea, 0x58, 0x94, 0x06, 0x9b, 0xd8, 0x23, 0xcb, + 0x45, 0xeb, 0x98, 0x41, 0x03, 0xfa, 0xdb, 0xec, 0x6a, 0x7d, 0x85, 0x00, 0x6e, 0x25, 0xb2, 0x4e, 0x52, 0xf9, 0x97, + 0xb4, 0x47, 0x5d, 0xdb, 0x53, 0xf9, 0xdf, 0xb6, 0xa9, 0x72, 0x68, 0x31, 0xe5, 0xb1, 0x9b, 0xb3, 0x40, 0x75, 0x24, + 0x88, 0x02, 0xb5, 0xf5, 0x82, 0xa9, 0x77, 0xca, 0x14, 0x1d, 0x20, 0xd0, 0x85, 0x39, 0xc3, 0xbe, 0xe0, 0x88, 0x31, + 0x4b, 0x25, 0x06, 0x53, 0x1f, 0x63, 0x54, 0xd3, 0x5a, 0x01, 0xba, 0x7e, 0xba, 0x81, 0x3f, 0x51, 0x51, 0xa3, 0xa1, + 0xd6, 0x48, 0x0a, 0x45, 0x13, 0x15, 0x8a, 0x2c, 0x2d, 0x74, 0x5c, 0x85, 0x4e, 0x22, 0x61, 0x09, 0x68, 0x98, 0x10, + 0x9d, 0x54, 0xe0, 0xad, 0x01, 0x9c, 0xf9, 0xb8, 0x28, 0xd7, 0x85, 0x36, 0x98, 0xfb, 0x21, 0xbe, 0xe6, 0xaf, 0x5f, + 0x38, 0xa3, 0xfa, 0x96, 0xb5, 0xbe, 0xa7, 0x05, 0xf9, 0x21, 0xe4, 0x14, 0x1d, 0x98, 0xd8, 0xd1, 0x06, 0x8d, 0x31, + 0xca, 0x5a, 0x47, 0xbd, 0x38, 0xd1, 0xa1, 0x58, 0xb4, 0x09, 0xde, 0x03, 0x9e, 0x22, 0xda, 0xf0, 0x50, 0x18, 0xab, + 0x6a, 0x7c, 0x2a, 0x59, 0x4b, 0x0f, 0x56, 0xf0, 0x74, 0x9d, 0xf0, 0x10, 0xf4, 0x48, 0x84, 0x1d, 0x85, 0xc5, 0x3c, + 0x5e, 0xc0, 0x71, 0x52, 0x10, 0x50, 0x3b, 0xe8, 0x2b, 0xf8, 0x7c, 0x81, 0xee, 0xaf, 0x12, 0x3d, 0xc0, 0xd0, 0x82, + 0xb8, 0x19, 0x05, 0x75, 0x74, 0x15, 0xaf, 0x1a, 0x2a, 0x12, 0x3e, 0x2f, 0xc0, 0x76, 0x48, 0xa9, 0xa7, 0x40, 0x0b, + 0x95, 0x28, 0xfd, 0x30, 0xf0, 0x1d, 0x1a, 0x03, 0x5b, 0xeb, 0x00, 0x0d, 0xfd, 0x8c, 0x69, 0x6a, 0x9d, 0xa1, 0xf2, + 0x99, 0x77, 0xcf, 0x8c, 0x96, 0x33, 0x8b, 0xc6, 0xa0, 0x6f, 0xa3, 0x29, 0x8a, 0x73, 0xf2, 0x59, 0x50, 0xc4, 0x69, + 0x16, 0xe7, 0xe0, 0xb7, 0x19, 0x17, 0x98, 0x31, 0x89, 0x2b, 0x7e, 0x29, 0x0b, 0xd0, 0x76, 0xe7, 0x2a, 0xb5, 0xae, + 0x41, 0x40, 0xf6, 0x03, 0x58, 0xbd, 0x34, 0x74, 0x54, 0xce, 0xbb, 0x4b, 0x9b, 0x42, 0xc4, 0x22, 0x04, 0x9b, 0x66, + 0xba, 0x64, 0xc7, 0xa1, 0xd2, 0xe6, 0x40, 0xa8, 0x23, 0x34, 0xee, 0x9f, 0x86, 0xb1, 0xd5, 0x14, 0x5b, 0xbb, 0xb7, + 0xed, 0xf6, 0xb7, 0xd2, 0x4b, 0xa7, 0x39, 0xe9, 0x31, 0xf6, 0x5b, 0x19, 0x16, 0x23, 0xdb, 0x11, 0x02, 0x4b, 0xce, + 0xfb, 0xd4, 0x7f, 0x45, 0xcb, 0x79, 0x02, 0xa6, 0x23, 0x3a, 0x58, 0x2e, 0x50, 0x76, 0x0c, 0xe8, 0x0e, 0x0c, 0xae, + 0xe8, 0xf7, 0xc1, 0x2a, 0xc3, 0x5c, 0x48, 0x96, 0x24, 0x65, 0xf0, 0x3c, 0xf5, 0xe0, 0xe0, 0xd7, 0x4c, 0x99, 0xbb, + 0x28, 0xeb, 0xd3, 0x25, 0x99, 0xa6, 0xc8, 0x40, 0xac, 0xc3, 0x4d, 0x96, 0x46, 0x89, 0x12, 0x91, 0x2d, 0xd1, 0x3f, + 0xd2, 0x50, 0x2c, 0x1d, 0xb9, 0x17, 0xa9, 0x12, 0xa1, 0x62, 0x9e, 0xe2, 0x49, 0x9d, 0xd6, 0xe9, 0x08, 0x43, 0x4f, + 0x82, 0x52, 0xae, 0x86, 0x81, 0x2a, 0xa9, 0x5e, 0x0a, 0x9b, 0x62, 0xbb, 0xd5, 0x17, 0x2b, 0x31, 0x8f, 0x17, 0xf8, + 0x52, 0xe0, 0x28, 0xfe, 0x83, 0x7b, 0x61, 0xa7, 0xd4, 0xf6, 0xa0, 0x76, 0x44, 0x09, 0xfd, 0x07, 0x87, 0x8b, 0xc4, + 0x77, 0x52, 0x87, 0x00, 0x44, 0x8b, 0x90, 0x33, 0x75, 0x90, 0x1a, 0x6e, 0x68, 0x47, 0xf8, 0x6f, 0xb8, 0x3e, 0xe3, + 0x8c, 0xde, 0x54, 0x33, 0x6a, 0x28, 0x5f, 0x0f, 0xda, 0x18, 0xf5, 0xd9, 0xc0, 0x61, 0x85, 0x28, 0xb4, 0x61, 0x47, + 0xa5, 0x12, 0x2d, 0x0c, 0xa5, 0xfa, 0x4b, 0xa8, 0x38, 0xe2, 0xce, 0x8c, 0xb2, 0x64, 0x7c, 0x5a, 0x1e, 0x8a, 0xe9, + 0x60, 0x50, 0x92, 0xca, 0x58, 0xe8, 0xc1, 0xf5, 0xc0, 0xf3, 0xef, 0x81, 0x5b, 0x88, 0x87, 0x8c, 0x2c, 0x86, 0xdc, + 0xe0, 0xe4, 0xb7, 0x38, 0xb9, 0x6a, 0x54, 0xaa, 0x38, 0xd6, 0x44, 0xb5, 0xe0, 0x1f, 0x65, 0x18, 0xa0, 0x4f, 0x52, + 0x00, 0x26, 0x83, 0x29, 0xbf, 0x05, 0x89, 0xd2, 0x99, 0xba, 0x21, 0xfd, 0x22, 0x0a, 0x7e, 0xc1, 0x0b, 0x2e, 0x12, + 0x57, 0x80, 0xe5, 0x1d, 0x6c, 0xaf, 0xa3, 0x8a, 0x2a, 0x4c, 0x5e, 0xd3, 0xe3, 0x88, 0x1b, 0xef, 0x3f, 0xd3, 0x63, + 0x8b, 0xd9, 0x6a, 0x1d, 0x1b, 0x7c, 0xe6, 0x18, 0x5c, 0xd0, 0xb5, 0xc4, 0xd6, 0x50, 0x0d, 0x2b, 0x02, 0x03, 0x17, + 0x70, 0x10, 0x96, 0x28, 0x8e, 0xad, 0xe4, 0x15, 0x69, 0x48, 0x69, 0x1f, 0x18, 0x8e, 0x36, 0xc9, 0xf1, 0x6d, 0x96, + 0xdd, 0x04, 0xce, 0x17, 0x9d, 0x93, 0x66, 0xc2, 0xda, 0xe0, 0x7d, 0xde, 0x9c, 0x5f, 0xf7, 0x0f, 0x09, 0x55, 0x71, + 0x6f, 0x78, 0x3b, 0xee, 0x8d, 0x13, 0x7e, 0xcd, 0xc5, 0x42, 0x87, 0x6a, 0x31, 0x97, 0x2c, 0xbf, 0xb5, 0xde, 0x2d, + 0x49, 0x6a, 0x05, 0xb4, 0xcf, 0xb2, 0xa0, 0x26, 0x02, 0x40, 0xfe, 0xf0, 0x17, 0x08, 0x9d, 0xe1, 0x6f, 0x8f, 0xc1, + 0x15, 0x29, 0xbc, 0x77, 0x08, 0x84, 0x35, 0xdd, 0xdc, 0xa9, 0x0d, 0xf8, 0x62, 0xdc, 0x9f, 0x31, 0xf5, 0xf4, 0xdb, + 0x4c, 0xee, 0xea, 0xba, 0x3d, 0xb2, 0x0c, 0x1f, 0xe1, 0x4a, 0x01, 0xdc, 0x4c, 0xf8, 0x8b, 0x61, 0x26, 0xd5, 0x27, + 0x80, 0xa9, 0xa6, 0x83, 0xfb, 0x04, 0x81, 0x01, 0x54, 0xa2, 0xc5, 0xe8, 0x5a, 0x39, 0xa2, 0x19, 0xb8, 0x35, 0xdd, + 0x0a, 0xe3, 0xad, 0x07, 0x2d, 0xf4, 0x4c, 0xc3, 0x89, 0xff, 0xa0, 0x99, 0x57, 0x05, 0x04, 0xd0, 0xca, 0x08, 0xde, + 0x5a, 0x1f, 0xcd, 0x11, 0xe2, 0x13, 0x96, 0x44, 0x13, 0x16, 0xcf, 0x14, 0x3f, 0x26, 0x74, 0xd3, 0xd4, 0x36, 0x7d, + 0x40, 0xfa, 0x8b, 0x6b, 0xd6, 0x4f, 0x59, 0xd6, 0xbe, 0x3d, 0x54, 0xbc, 0x98, 0x36, 0xe3, 0x20, 0x26, 0xaa, 0x18, + 0xff, 0x0b, 0xee, 0x4b, 0xad, 0x00, 0x91, 0xb9, 0xab, 0x9e, 0x7e, 0xbf, 0x99, 0x2d, 0x07, 0x42, 0xe5, 0x77, 0x06, + 0x49, 0x9f, 0x0e, 0xed, 0x07, 0x36, 0x89, 0xda, 0x42, 0xcf, 0x1f, 0x97, 0xba, 0x89, 0x97, 0xd7, 0xa6, 0x46, 0xb4, + 0x42, 0x86, 0xca, 0xd6, 0x01, 0xeb, 0xfb, 0x87, 0x70, 0x77, 0x51, 0xd3, 0x50, 0xeb, 0x9e, 0xbb, 0x16, 0x05, 0x27, + 0xfe, 0x00, 0x63, 0x71, 0x21, 0xa9, 0x75, 0x3c, 0x26, 0xfd, 0x68, 0x21, 0x93, 0x1b, 0x75, 0x75, 0x72, 0xa6, 0x98, + 0x27, 0x70, 0x01, 0x2e, 0xdb, 0xfe, 0x8a, 0x4a, 0x5d, 0xca, 0xed, 0x15, 0xa5, 0xe9, 0x21, 0x6d, 0xaf, 0xe2, 0xbc, + 0x2d, 0xb8, 0xe0, 0x5f, 0x28, 0xb8, 0xb0, 0x0e, 0xd6, 0x1d, 0x77, 0xca, 0x9e, 0xf0, 0x44, 0x99, 0xd6, 0x06, 0x77, + 0xdd, 0x60, 0x4c, 0x8c, 0xfd, 0xee, 0x92, 0x27, 0x1f, 0x91, 0x05, 0xff, 0x2e, 0x13, 0xe0, 0x99, 0xec, 0x5e, 0xa9, + 0xfc, 0x3f, 0xf8, 0x57, 0x5b, 0xfb, 0xce, 0x9a, 0x7f, 0x7a, 0xd6, 0xc3, 0x9d, 0xc3, 0xe4, 0xc7, 0xea, 0x0c, 0xe8, + 0xe6, 0x4a, 0xa6, 0x1c, 0x90, 0x01, 0xac, 0x45, 0x32, 0x1a, 0xf0, 0xa1, 0x95, 0x65, 0xdb, 0x77, 0x5a, 0x5d, 0x10, + 0xee, 0x25, 0x70, 0xd3, 0xfb, 0x6b, 0x33, 0x33, 0xa7, 0x6b, 0x25, 0x9a, 0x2e, 0x8d, 0xad, 0x65, 0xa9, 0xc2, 0x78, + 0xbf, 0xf7, 0x24, 0x9b, 0xe6, 0x87, 0xcb, 0x69, 0x6e, 0xa9, 0xdb, 0xc6, 0x2d, 0x1b, 0x40, 0x43, 0xec, 0x5a, 0x5b, + 0x39, 0xe0, 0xe5, 0xf6, 0x20, 0x9a, 0xaf, 0x15, 0xa1, 0xa7, 0x4a, 0x84, 0x3e, 0x4d, 0x9b, 0x7d, 0xb0, 0xab, 0x6a, + 0xdd, 0x08, 0x79, 0x34, 0x48, 0x35, 0x23, 0x7f, 0x72, 0xcd, 0x8b, 0x8b, 0x5c, 0xde, 0x00, 0x1c, 0x32, 0xa9, 0x8d, + 0xc2, 0xf2, 0x0a, 0xdc, 0xf9, 0xd1, 0x71, 0x9c, 0x89, 0x51, 0x8e, 0x71, 0x5b, 0x11, 0x29, 0x59, 0x27, 0xce, 0x00, + 0x0f, 0xd9, 0x9f, 0x34, 0x1d, 0xda, 0xb5, 0xc0, 0xf0, 0xbe, 0xc0, 0x5d, 0xe5, 0xec, 0x68, 0x93, 0xdb, 0x45, 0xdf, + 0x9c, 0x61, 0xdd, 0x91, 0xd2, 0xda, 0x58, 0x74, 0xdd, 0xc1, 0x5a, 0x33, 0x68, 0x8b, 0x50, 0xf2, 0x21, 0x77, 0xd2, + 0x7e, 0x0a, 0x68, 0x70, 0x96, 0xa5, 0xb7, 0xd6, 0x2a, 0x7f, 0xa3, 0x85, 0x38, 0x51, 0x4c, 0x9d, 0xf8, 0x26, 0x4a, + 0xf4, 0xf9, 0x99, 0x18, 0x37, 0x10, 0x48, 0xfd, 0x01, 0xe3, 0x6b, 0x14, 0x61, 0x02, 0xd7, 0x81, 0x28, 0xb6, 0x27, + 0x6a, 0x63, 0x39, 0x82, 0x4e, 0x08, 0xf1, 0x0e, 0xca, 0x30, 0x56, 0x17, 0x07, 0xda, 0x60, 0xe9, 0xeb, 0xd6, 0x3a, + 0x37, 0x84, 0xc2, 0x38, 0x81, 0x29, 0x06, 0x49, 0x9d, 0x75, 0x96, 0x09, 0xaa, 0xec, 0x98, 0x74, 0xde, 0x07, 0xe8, + 0xee, 0x5a, 0x34, 0xc5, 0xd7, 0x9d, 0x3b, 0xe8, 0x3e, 0xae, 0x5f, 0x6b, 0x91, 0x1b, 0xfc, 0x79, 0x4b, 0x84, 0x45, + 0xe0, 0xac, 0x35, 0xf9, 0xaa, 0x11, 0x0e, 0x4c, 0x49, 0xa6, 0x61, 0x2f, 0x51, 0x36, 0xdd, 0xdb, 0x6d, 0xaf, 0x77, + 0xaf, 0x88, 0xab, 0xc7, 0x58, 0xe5, 0xdd, 0xcc, 0xed, 0x9d, 0x6a, 0x2d, 0x76, 0x6f, 0xda, 0x7e, 0x8a, 0x1d, 0xb5, + 0xd6, 0x6e, 0x37, 0x9c, 0x50, 0x43, 0xbe, 0x15, 0x55, 0x5a, 0x9d, 0x6e, 0x0c, 0xda, 0x21, 0xb4, 0xb5, 0xc8, 0xe0, + 0x46, 0xf9, 0xc2, 0x09, 0x9d, 0x54, 0xc8, 0x55, 0xa7, 0x2e, 0xd8, 0x5c, 0xf1, 0x6a, 0x29, 0xd3, 0x48, 0x50, 0xb4, + 0x39, 0x8f, 0x4a, 0x9a, 0xc8, 0xb5, 0xa8, 0x22, 0x59, 0xa3, 0x5e, 0xd4, 0x6a, 0x0c, 0x10, 0x90, 0xe9, 0xac, 0xe9, + 0x41, 0x15, 0xcc, 0x86, 0x32, 0x92, 0xd3, 0xf7, 0x60, 0x69, 0x8f, 0x1c, 0x6b, 0x7d, 0x5f, 0x9d, 0x2d, 0xbe, 0xd5, + 0x13, 0x82, 0x29, 0xcc, 0x1e, 0x88, 0x08, 0xd7, 0x34, 0x86, 0x9c, 0x76, 0x89, 0xcb, 0x9a, 0x6e, 0x09, 0xf7, 0x70, + 0xbb, 0x92, 0x1d, 0xb9, 0x79, 0xd2, 0xdc, 0x5c, 0xc1, 0x8e, 0x8a, 0xf9, 0x18, 0xb4, 0x5f, 0x52, 0x5d, 0xbb, 0x34, + 0xb7, 0x1e, 0x0f, 0x02, 0x1a, 0x0c, 0x0a, 0xc3, 0xbf, 0x4e, 0x8c, 0x87, 0x27, 0x0d, 0x08, 0x92, 0x72, 0x11, 0x8e, + 0x7d, 0x23, 0xfa, 0xc9, 0x54, 0x1e, 0x72, 0xb4, 0x78, 0x87, 0x56, 0x27, 0x10, 0xd0, 0x4b, 0x84, 0x92, 0x18, 0x55, + 0xa1, 0x11, 0x41, 0x79, 0x5a, 0xfe, 0x52, 0x55, 0x87, 0x80, 0x42, 0xda, 0x57, 0x14, 0xca, 0x36, 0x89, 0xa1, 0x19, + 0x7e, 0x39, 0x9f, 0x2c, 0xf4, 0x0c, 0x0c, 0xe4, 0xfc, 0x60, 0xa1, 0x67, 0x61, 0x20, 0xe7, 0x4f, 0x16, 0xb5, 0x5b, + 0x07, 0x9a, 0x80, 0x78, 0x2e, 0x1c, 0x9d, 0x94, 0x56, 0x65, 0x0b, 0xe8, 0xe6, 0x21, 0x82, 0xfe, 0x0f, 0x7b, 0x08, + 0x3a, 0xb9, 0xd0, 0x8e, 0xdc, 0x80, 0xb6, 0x43, 0x12, 0xd8, 0x2b, 0x26, 0x15, 0x26, 0x16, 0xd1, 0x21, 0x1b, 0x83, + 0x21, 0xb6, 0xfa, 0xe0, 0x90, 0x8d, 0xa7, 0x3e, 0x09, 0x02, 0x46, 0xf7, 0x07, 0x03, 0x0e, 0x7e, 0x8b, 0x57, 0xe9, + 0xa3, 0x8d, 0x40, 0x37, 0x7d, 0x77, 0x37, 0xf4, 0x2e, 0xae, 0xe0, 0x54, 0xed, 0xee, 0x49, 0xe8, 0x26, 0xd3, 0x8e, + 0xd5, 0x6b, 0x88, 0x1b, 0xf2, 0x2b, 0xa3, 0xd1, 0xc8, 0xa6, 0x84, 0x84, 0x18, 0xce, 0xa1, 0x99, 0xd3, 0x72, 0xf9, + 0xea, 0xd6, 0xb3, 0x01, 0x19, 0x66, 0x7a, 0xcb, 0x64, 0xfd, 0x00, 0x65, 0xd5, 0x63, 0x68, 0x87, 0xde, 0x23, 0xc7, + 0x0f, 0x0f, 0xbe, 0xc9, 0xf8, 0x99, 0xc3, 0xb5, 0x87, 0x73, 0xe1, 0xbb, 0xac, 0x19, 0x99, 0x43, 0xe7, 0xd9, 0xc7, + 0xf1, 0x1e, 0xc6, 0xc9, 0xe7, 0x59, 0x28, 0x6f, 0xbc, 0xa6, 0xff, 0x51, 0xe9, 0xcd, 0x0e, 0x87, 0x9c, 0xae, 0x60, + 0xc5, 0xcd, 0xaa, 0xd0, 0xf0, 0xb3, 0xc8, 0x1b, 0x47, 0xbc, 0x26, 0x51, 0xd5, 0x7d, 0xde, 0xdb, 0x88, 0xa5, 0x1d, + 0xe3, 0x00, 0xe0, 0x44, 0xad, 0x1a, 0x76, 0xa5, 0x71, 0xad, 0x0e, 0x62, 0x44, 0x4a, 0xd8, 0x2a, 0x71, 0x24, 0x94, + 0xbf, 0x01, 0x08, 0x8b, 0xa1, 0x38, 0xde, 0x1a, 0xd6, 0x07, 0xd8, 0x0f, 0x5d, 0xa0, 0x69, 0x4e, 0xa9, 0x66, 0x00, + 0x90, 0x04, 0xfc, 0xd1, 0xd3, 0x4d, 0x43, 0x65, 0x9b, 0xe7, 0xa1, 0x65, 0x75, 0x05, 0x0f, 0xf4, 0xd4, 0x95, 0x0c, + 0x8c, 0xab, 0x3a, 0xf6, 0x36, 0xf7, 0xb7, 0x47, 0xab, 0xc8, 0x77, 0x36, 0xa9, 0x69, 0x16, 0x40, 0x8a, 0xc6, 0xa5, + 0x2f, 0xf4, 0x74, 0x02, 0xb4, 0x5e, 0x5b, 0x2a, 0xda, 0xef, 0xa3, 0x18, 0x35, 0x2e, 0x14, 0x58, 0x85, 0x09, 0x0a, + 0x87, 0x08, 0x23, 0x84, 0x7e, 0x5f, 0x86, 0x1b, 0x5f, 0x90, 0x41, 0x34, 0x5c, 0x8b, 0x0e, 0x45, 0xe4, 0x78, 0xd1, + 0xb6, 0x54, 0xd5, 0x9c, 0x34, 0x6d, 0x09, 0xbc, 0x89, 0x0c, 0xd8, 0xce, 0x3f, 0x6d, 0x88, 0x5c, 0x85, 0x0b, 0x18, + 0xbe, 0x23, 0xae, 0x05, 0xd1, 0x4d, 0x6d, 0xea, 0x6d, 0xd8, 0x21, 0x3a, 0x9a, 0xe2, 0xd1, 0x21, 0xf7, 0xdc, 0x3d, + 0xb7, 0x45, 0x7c, 0xf3, 0x19, 0x72, 0xd7, 0x74, 0xf6, 0x52, 0x84, 0x41, 0xdd, 0xb2, 0x81, 0x62, 0x1d, 0x3a, 0x41, + 0x01, 0x06, 0x70, 0xf9, 0x04, 0x74, 0x6c, 0x30, 0xa8, 0x08, 0x3e, 0x29, 0x6c, 0x9b, 0x06, 0xf9, 0x23, 0xde, 0x0d, + 0x1d, 0x5e, 0x5b, 0xf2, 0x40, 0xbc, 0xc2, 0x3e, 0x53, 0xc2, 0xfd, 0x0b, 0x0a, 0xba, 0xa3, 0xbc, 0x5c, 0x15, 0xae, + 0x4a, 0x03, 0x50, 0x65, 0xc7, 0x73, 0xad, 0x29, 0x69, 0x01, 0x2b, 0x25, 0x75, 0xe7, 0x37, 0xc1, 0x71, 0x4b, 0xa6, + 0xc2, 0xb7, 0xea, 0x46, 0x95, 0x87, 0x12, 0x45, 0x3a, 0xf6, 0x6c, 0xe7, 0x60, 0x0d, 0x80, 0xa7, 0xb0, 0xbd, 0x38, + 0x13, 0xf0, 0xb9, 0xd3, 0x2e, 0x5b, 0xe6, 0x12, 0x28, 0xea, 0x87, 0x71, 0x5e, 0x76, 0x7c, 0xb9, 0x3b, 0xda, 0xde, + 0x43, 0x6f, 0xc4, 0xc6, 0x78, 0x7d, 0x19, 0x35, 0xfd, 0xe2, 0x19, 0xae, 0x2c, 0x05, 0x79, 0xa0, 0xa9, 0x1e, 0x61, + 0x74, 0x08, 0x4c, 0x53, 0x7e, 0xc4, 0xc6, 0xd3, 0xe1, 0xd0, 0x90, 0x41, 0xaf, 0x99, 0x18, 0x0a, 0xec, 0x0b, 0x68, + 0x9d, 0x99, 0xb8, 0xc6, 0xa7, 0xed, 0x2b, 0x68, 0x75, 0x8b, 0x32, 0xb9, 0x33, 0x30, 0x7c, 0xa0, 0x25, 0x53, 0x30, + 0x55, 0x78, 0x43, 0xa4, 0x92, 0x7d, 0x5a, 0x5a, 0x87, 0x7d, 0xbb, 0x50, 0x68, 0xa1, 0x89, 0x5f, 0x65, 0x88, 0x9f, + 0xba, 0xce, 0xfc, 0xdb, 0xb4, 0x4f, 0x0d, 0x62, 0xe1, 0x48, 0x0c, 0x22, 0x7e, 0x71, 0xaa, 0x6c, 0x27, 0x84, 0x8a, + 0x8d, 0x87, 0xae, 0x75, 0xe3, 0x48, 0xaa, 0x30, 0x94, 0x42, 0xe3, 0xa9, 0xe1, 0xbe, 0x17, 0x3a, 0x7c, 0x1d, 0x66, + 0x71, 0x9b, 0x35, 0x92, 0x1a, 0xe3, 0x54, 0x98, 0x38, 0x95, 0x72, 0x15, 0x09, 0x0c, 0x94, 0x67, 0x0b, 0x83, 0x00, + 0x93, 0x98, 0x64, 0x6c, 0x2d, 0x84, 0x09, 0x63, 0xe7, 0x0a, 0xd3, 0xd4, 0x45, 0xea, 0x37, 0x03, 0x93, 0x05, 0x0d, + 0xf9, 0x3d, 0x1a, 0xad, 0xa9, 0x9a, 0x02, 0x0c, 0xe3, 0x28, 0xd5, 0xf8, 0xb7, 0x08, 0xb5, 0x19, 0x06, 0x00, 0xb6, + 0x79, 0x27, 0x33, 0x51, 0xbd, 0x16, 0x08, 0x81, 0xe6, 0xec, 0xa7, 0xe2, 0x6a, 0x67, 0x16, 0x8c, 0xa2, 0xdd, 0x5e, + 0xf9, 0x7c, 0xe0, 0x84, 0xf2, 0x58, 0x5d, 0xa0, 0x5e, 0xc9, 0xe2, 0x8d, 0x4c, 0x79, 0x2b, 0x44, 0xe6, 0x9e, 0x64, + 0x1f, 0xf2, 0x11, 0x9c, 0x57, 0xe8, 0x54, 0x6e, 0xb6, 0x89, 0x32, 0x4b, 0x92, 0x8c, 0x05, 0xc6, 0xe6, 0x25, 0x98, + 0x49, 0xcd, 0x8c, 0xe1, 0xd7, 0x10, 0x67, 0x6c, 0xe7, 0x24, 0xdc, 0xdc, 0xcf, 0x03, 0x43, 0x94, 0x72, 0xd1, 0x12, + 0x0d, 0x5b, 0x3b, 0x5e, 0x4f, 0xae, 0x09, 0xf7, 0x61, 0x23, 0xd6, 0x64, 0x8c, 0x71, 0x6d, 0x6e, 0x64, 0xfd, 0x68, + 0x81, 0x07, 0x63, 0xca, 0xfa, 0x13, 0xc8, 0xb4, 0x92, 0xb2, 0xce, 0x17, 0x46, 0xcc, 0xa4, 0x12, 0xbd, 0xdb, 0x37, + 0x3e, 0xab, 0xbb, 0x88, 0xfa, 0xad, 0xfd, 0x9e, 0xd4, 0xc3, 0x9d, 0xff, 0xa0, 0xb0, 0x06, 0x95, 0x11, 0x97, 0x11, + 0xe5, 0x99, 0x03, 0xdd, 0x34, 0x29, 0xe2, 0xf4, 0x6c, 0x15, 0x17, 0x25, 0x4f, 0xa1, 0x52, 0x4d, 0xdd, 0xa2, 0xde, + 0x04, 0xec, 0x0d, 0x91, 0x24, 0x59, 0x4b, 0x63, 0x2b, 0x76, 0x69, 0x90, 0x9e, 0x7b, 0x23, 0x2e, 0xbd, 0xaa, 0xd0, + 0x90, 0x96, 0x7a, 0x67, 0xa1, 0x92, 0xf9, 0x2b, 0xfe, 0x33, 0xa8, 0x15, 0xe8, 0x68, 0x93, 0x62, 0x3c, 0x07, 0x46, + 0x7c, 0x37, 0x98, 0xd5, 0x03, 0xc4, 0x45, 0x13, 0x94, 0x7a, 0x47, 0xec, 0xf8, 0xb9, 0xc9, 0xc3, 0xbb, 0x90, 0x73, + 0x06, 0x9f, 0x3e, 0xcc, 0x12, 0xb5, 0xd6, 0x91, 0x18, 0xa9, 0x19, 0x40, 0xd3, 0x41, 0x99, 0xf3, 0x58, 0x04, 0xb3, + 0x9e, 0x49, 0x8c, 0x7a, 0x5c, 0xff, 0x02, 0x0d, 0xb5, 0xdf, 0xac, 0x2c, 0xcf, 0xaa, 0xbb, 0x2f, 0xe1, 0xc0, 0xa6, + 0xb6, 0x82, 0x1e, 0xaf, 0x2b, 0x79, 0x79, 0xa9, 0xba, 0xed, 0x17, 0x62, 0xe4, 0x74, 0x8d, 0x6b, 0xe9, 0xbc, 0x5a, + 0xb0, 0x5e, 0x77, 0xba, 0x59, 0xdc, 0xcd, 0x32, 0x1a, 0x08, 0x6b, 0x3b, 0x9f, 0x68, 0xfe, 0xac, 0xd9, 0x76, 0x1f, + 0x6f, 0x41, 0xcc, 0x02, 0x80, 0x48, 0x0f, 0xa2, 0x60, 0x99, 0xa5, 0x3c, 0xa0, 0xf2, 0x3e, 0x8e, 0xb2, 0x50, 0x7a, + 0x39, 0xcb, 0xf8, 0x69, 0xd3, 0x58, 0xeb, 0xac, 0x50, 0x86, 0xd6, 0x46, 0x77, 0xba, 0xca, 0x10, 0xdb, 0x4f, 0xe2, + 0x6c, 0x01, 0xee, 0x8f, 0x19, 0x0a, 0x0d, 0x9d, 0x65, 0xa4, 0x89, 0x86, 0xef, 0xba, 0x67, 0x90, 0x51, 0x9c, 0xac, + 0xf3, 0x4a, 0xba, 0xd1, 0x67, 0x6d, 0x24, 0xcc, 0x3d, 0x44, 0xbf, 0x8a, 0xc1, 0xa3, 0xdc, 0xe7, 0xb5, 0xd1, 0xc9, + 0xb4, 0x8c, 0xb4, 0x3b, 0x3f, 0xa9, 0x97, 0x59, 0xaa, 0x75, 0xd8, 0x3e, 0xc3, 0xde, 0x1a, 0x93, 0xde, 0x84, 0xd4, + 0x30, 0x12, 0x9f, 0xcf, 0xa8, 0x11, 0x02, 0xda, 0x72, 0xfc, 0x1d, 0x3e, 0xc3, 0xd0, 0x14, 0x58, 0xaa, 0xb8, 0x85, + 0xdd, 0xf0, 0x35, 0x9f, 0xac, 0x5a, 0x00, 0x82, 0x59, 0xf9, 0x7a, 0x17, 0xaf, 0x84, 0xfa, 0x4c, 0x9b, 0x01, 0x20, + 0x0b, 0x4a, 0xb9, 0xe3, 0xa7, 0x54, 0x3a, 0x58, 0xa2, 0x68, 0x7b, 0x39, 0x7d, 0xa3, 0x63, 0xe3, 0x87, 0xf4, 0x5c, + 0xc0, 0x76, 0x21, 0xbf, 0x75, 0xaf, 0x5e, 0xa2, 0x22, 0xb5, 0x6d, 0xd6, 0x03, 0x7c, 0xb9, 0x41, 0x93, 0x30, 0x82, + 0x32, 0x65, 0x0a, 0x60, 0x70, 0x53, 0x8d, 0x82, 0x49, 0xab, 0x91, 0xb0, 0xa5, 0x9e, 0x64, 0xb9, 0xe9, 0x83, 0x53, + 0xdd, 0x23, 0xe8, 0xd1, 0x0e, 0x27, 0x2d, 0xfb, 0xb5, 0x82, 0xa3, 0x93, 0xab, 0x21, 0x6a, 0xe6, 0xbd, 0xb6, 0x23, + 0x43, 0xca, 0x65, 0x18, 0x08, 0xa6, 0x1c, 0xf3, 0xf4, 0xd8, 0x7a, 0x46, 0x44, 0x0f, 0x9c, 0x7d, 0xa6, 0x5b, 0x75, + 0x25, 0x01, 0xd1, 0xf1, 0x9b, 0xa7, 0xaf, 0xaf, 0xe2, 0x4b, 0x83, 0xa2, 0xd4, 0xb0, 0x88, 0x51, 0xa6, 0x7d, 0x95, + 0x84, 0xc1, 0xfb, 0xe5, 0xfd, 0x4f, 0x2a, 0x4b, 0xed, 0xf7, 0x60, 0x63, 0x45, 0x55, 0xbf, 0x94, 0xbc, 0x68, 0x0a, + 0xb0, 0xee, 0xb3, 0x44, 0x81, 0xdc, 0xef, 0x6d, 0x9a, 0xf9, 0x26, 0x6a, 0xdc, 0x6c, 0x58, 0x6f, 0x5c, 0xb7, 0x4b, + 0x6d, 0xc9, 0x8e, 0xac, 0x44, 0xce, 0x2c, 0x06, 0x33, 0x7e, 0x54, 0x18, 0x94, 0x86, 0x0d, 0xaa, 0x52, 0xf1, 0x7b, + 0x23, 0x82, 0x53, 0xc7, 0xaa, 0xc2, 0x98, 0x06, 0xcc, 0xb6, 0xa2, 0xd6, 0xa0, 0x0e, 0x4a, 0x69, 0x6b, 0x02, 0xb2, + 0xfd, 0xc6, 0x0a, 0x6a, 0x7e, 0xff, 0xcb, 0x18, 0xf2, 0x35, 0xa5, 0xa0, 0x92, 0x80, 0x9d, 0x41, 0xa3, 0xa7, 0x4a, + 0x18, 0x48, 0x41, 0xf0, 0x04, 0x28, 0x5f, 0x44, 0x8d, 0xd5, 0x6e, 0x5f, 0x9d, 0x1a, 0xa3, 0x2d, 0x20, 0xb4, 0x90, + 0x1e, 0x5d, 0xf6, 0x71, 0x1b, 0xeb, 0x40, 0xe2, 0xc1, 0x09, 0xb6, 0x73, 0x75, 0x8d, 0x46, 0x42, 0xf3, 0x87, 0x46, + 0x03, 0x5e, 0xd3, 0x0a, 0x14, 0xea, 0x39, 0x8e, 0x86, 0xce, 0x0e, 0x29, 0x88, 0xd8, 0xa0, 0x85, 0x7d, 0xf7, 0x7c, + 0x68, 0xf6, 0xf5, 0x3c, 0x59, 0x90, 0x9a, 0x4a, 0xf7, 0xb9, 0x5b, 0x42, 0xd6, 0xaa, 0x43, 0x59, 0x79, 0x80, 0xe3, + 0x85, 0x92, 0xf9, 0x3b, 0x4c, 0x6a, 0x94, 0xc6, 0x84, 0xc6, 0x88, 0x05, 0x2c, 0x09, 0xda, 0xeb, 0x81, 0xfa, 0x65, + 0x10, 0x2a, 0x9c, 0xe9, 0x89, 0xc4, 0xa7, 0x94, 0xab, 0x4f, 0x0b, 0x52, 0x4f, 0x0b, 0xe6, 0x40, 0x2f, 0x7d, 0x2b, + 0xbf, 0xb2, 0xf1, 0xd1, 0xee, 0xde, 0x35, 0x17, 0xd6, 0x31, 0xc4, 0xc5, 0x16, 0x7e, 0x73, 0x6a, 0x0a, 0xc0, 0x86, + 0xc7, 0xba, 0x2c, 0xdf, 0xa8, 0x89, 0xcc, 0xe2, 0x90, 0x44, 0x20, 0xd9, 0x6e, 0x6e, 0x6e, 0x23, 0xd8, 0xf6, 0x16, + 0x6a, 0x43, 0xfd, 0xe5, 0x6d, 0xf7, 0x7b, 0x86, 0x97, 0x7b, 0x72, 0xef, 0xa6, 0x0d, 0xe5, 0x0f, 0xf7, 0xaf, 0x92, + 0xff, 0xab, 0x4a, 0xee, 0xb7, 0xca, 0xac, 0xdb, 0xe2, 0xfd, 0xae, 0xe3, 0x96, 0x63, 0x34, 0x08, 0xac, 0x29, 0x30, + 0x90, 0x9e, 0x34, 0xa6, 0x89, 0x8e, 0xae, 0xcc, 0x98, 0xc1, 0xa3, 0x0b, 0xd0, 0x1c, 0xa6, 0xf3, 0x3c, 0x06, 0xe0, + 0x00, 0xff, 0xc8, 0x23, 0xd4, 0x3f, 0x9d, 0xe7, 0xc1, 0x59, 0x30, 0x28, 0x07, 0x81, 0xfe, 0xc4, 0x35, 0x27, 0x58, + 0x80, 0xce, 0x2d, 0x66, 0x10, 0x77, 0xd2, 0x9a, 0x39, 0xc4, 0x87, 0xc9, 0x74, 0x30, 0x88, 0xc9, 0x06, 0x40, 0xfa, + 0xe2, 0x85, 0x75, 0x0e, 0x2a, 0xf4, 0x82, 0x6c, 0xd5, 0x5d, 0x34, 0x2b, 0xf6, 0xaa, 0x9d, 0xe6, 0xfd, 0x7e, 0x3e, + 0x2f, 0x07, 0x41, 0xa3, 0xc2, 0xc2, 0x78, 0xff, 0xd1, 0xe6, 0x97, 0x46, 0x27, 0x4d, 0x30, 0x62, 0xed, 0x31, 0xaa, + 0x57, 0x3c, 0xcd, 0x68, 0xe3, 0x76, 0xac, 0x94, 0x2f, 0x20, 0x8a, 0x07, 0x86, 0xac, 0x95, 0x77, 0xe7, 0xe0, 0x75, + 0xb9, 0xf1, 0xe6, 0x88, 0x02, 0xec, 0xa6, 0x30, 0x4e, 0x6a, 0x2e, 0xba, 0xa8, 0x89, 0x67, 0xb0, 0xd3, 0xd5, 0x5b, + 0x89, 0x56, 0xe3, 0xbd, 0x78, 0xd7, 0x6c, 0xfc, 0xad, 0xdc, 0xd3, 0x65, 0xee, 0x5d, 0x00, 0xe2, 0xec, 0x5e, 0x5c, + 0xed, 0x61, 0xa9, 0x7b, 0xc1, 0xc0, 0x22, 0x87, 0xb4, 0xab, 0xd5, 0x43, 0x11, 0xa9, 0xf3, 0x18, 0x0c, 0x98, 0x4c, + 0x43, 0x6a, 0x32, 0xed, 0x15, 0x0a, 0xd2, 0xc6, 0x5a, 0x0b, 0x68, 0xc3, 0x61, 0xb1, 0x63, 0x37, 0xec, 0x4e, 0xb7, + 0x0e, 0x85, 0x12, 0x06, 0xb2, 0xae, 0x9b, 0x87, 0x5a, 0xc3, 0x13, 0x41, 0x0f, 0xaa, 0xd1, 0x7e, 0x7a, 0x28, 0x4f, + 0xda, 0x63, 0x01, 0x2e, 0x7a, 0xf8, 0xf2, 0xa5, 0xc0, 0x8b, 0xf6, 0x0e, 0xf2, 0x9c, 0xf9, 0x54, 0xf9, 0x20, 0x36, + 0xdc, 0x32, 0x7c, 0x68, 0x1f, 0xdf, 0x0a, 0x64, 0x52, 0x77, 0x34, 0xb5, 0xb5, 0x3b, 0x1a, 0xc7, 0x04, 0xfa, 0x4d, + 0x39, 0x4a, 0x99, 0x98, 0x5a, 0x96, 0xec, 0xa8, 0x97, 0x2b, 0x6f, 0xa8, 0x94, 0x1d, 0x2d, 0xdb, 0x9c, 0x5f, 0xda, + 0x48, 0xe8, 0xf7, 0xb5, 0x3b, 0x10, 0xbe, 0x51, 0xeb, 0x0d, 0x79, 0xd9, 0x10, 0xb1, 0x1c, 0x62, 0x06, 0x8e, 0x17, + 0x52, 0xb9, 0x76, 0x17, 0x4d, 0x55, 0xdd, 0xce, 0x56, 0x2e, 0x68, 0x89, 0xb7, 0x52, 0x60, 0x15, 0xa9, 0xd3, 0xeb, + 0xa9, 0xc4, 0xfb, 0x3e, 0x8a, 0xed, 0x47, 0xc0, 0x36, 0x36, 0x8e, 0xc6, 0xc6, 0x2d, 0x62, 0x83, 0xaf, 0xa2, 0x8a, + 0x16, 0x1c, 0x20, 0xb8, 0xdb, 0x92, 0x5a, 0x9a, 0x39, 0xc4, 0x7d, 0xc5, 0x03, 0xb4, 0xef, 0xe2, 0x88, 0x53, 0x01, + 0xb6, 0x75, 0xad, 0x73, 0x56, 0xcb, 0x01, 0x9b, 0x89, 0x9e, 0x7f, 0x5a, 0x35, 0x12, 0x31, 0xac, 0xb2, 0x91, 0xb2, + 0x42, 0x7b, 0x50, 0xba, 0x84, 0x8b, 0x2f, 0xc0, 0xcb, 0xf6, 0xfd, 0xca, 0xee, 0xb3, 0x25, 0xf6, 0x0f, 0xf3, 0xaa, + 0x09, 0x1e, 0x79, 0x8d, 0xb7, 0xf7, 0x30, 0xf1, 0xa5, 0x52, 0x08, 0xaf, 0x52, 0x1a, 0x4a, 0x00, 0x06, 0x49, 0x50, + 0xc3, 0x95, 0xb6, 0xcd, 0x20, 0x95, 0x31, 0xec, 0x6e, 0xf5, 0x56, 0xff, 0xa7, 0x55, 0xb8, 0xa8, 0x64, 0x31, 0x26, + 0x81, 0xce, 0xa9, 0x96, 0x9b, 0xc0, 0x82, 0x67, 0xbb, 0xe4, 0x08, 0x14, 0x76, 0x02, 0xb8, 0xa1, 0x84, 0xfd, 0x82, + 0xb7, 0xa1, 0x9c, 0xbd, 0xb2, 0x92, 0x27, 0xb7, 0x2f, 0xa9, 0xa0, 0x09, 0x99, 0x0a, 0xbb, 0x7f, 0x5b, 0x1b, 0xf6, + 0x45, 0x28, 0x47, 0x52, 0xe0, 0xe2, 0xa0, 0x73, 0x00, 0xfb, 0x83, 0x5c, 0xc6, 0xe6, 0x33, 0xe9, 0xf7, 0xd5, 0xfb, + 0xe7, 0x79, 0x96, 0x7c, 0xdc, 0x79, 0x6f, 0x78, 0x9a, 0x25, 0x03, 0x2a, 0x11, 0x53, 0xeb, 0xaa, 0x18, 0x2e, 0xb5, + 0x8b, 0x71, 0x83, 0x64, 0xc4, 0xf7, 0x52, 0x87, 0x18, 0x31, 0xbe, 0xc8, 0x0e, 0x49, 0xc9, 0xe9, 0xb2, 0xee, 0xec, + 0xb9, 0x16, 0xcd, 0xa0, 0x31, 0xdc, 0x8e, 0xf7, 0x92, 0x5e, 0x01, 0x2a, 0x40, 0x74, 0xcf, 0x02, 0xd7, 0xf0, 0xe6, + 0x92, 0x68, 0x6c, 0xe9, 0x69, 0x4b, 0x34, 0x70, 0xaf, 0x4c, 0x48, 0xaa, 0x8d, 0x03, 0x2c, 0x62, 0x5d, 0x7f, 0x0c, + 0x0b, 0x00, 0x6a, 0x35, 0x48, 0xaf, 0xf4, 0x15, 0xa1, 0x2a, 0x09, 0xc1, 0xe8, 0x44, 0xc2, 0xcb, 0x80, 0xc6, 0x99, + 0x49, 0xb4, 0xb0, 0xc1, 0x01, 0x7d, 0x51, 0x99, 0x44, 0x63, 0x43, 0x1e, 0x50, 0x6e, 0xd3, 0x00, 0x06, 0x1f, 0x24, + 0x49, 0xf4, 0xf5, 0xd2, 0x24, 0x81, 0xa0, 0x04, 0xe5, 0x1b, 0xf4, 0xe7, 0xd2, 0xf3, 0xb1, 0xfc, 0xdd, 0x3b, 0x94, + 0x7e, 0x08, 0x0b, 0x90, 0x29, 0xea, 0x8a, 0x69, 0xc6, 0x8e, 0xb2, 0x6e, 0x63, 0x12, 0xcf, 0xd3, 0xee, 0xb6, 0x50, + 0x2e, 0x5d, 0xe0, 0x57, 0x96, 0x21, 0x8e, 0xf5, 0xf3, 0x78, 0xc5, 0x8e, 0x43, 0xae, 0xf1, 0xd2, 0x9f, 0xc7, 0x2b, + 0x9c, 0x21, 0x5a, 0xb5, 0x12, 0x88, 0xf2, 0x5f, 0xb5, 0x81, 0x43, 0xdc, 0x27, 0x18, 0xe4, 0xa2, 0xf2, 0x1e, 0x08, + 0xe4, 0x6d, 0x05, 0x11, 0x69, 0x66, 0xd7, 0x61, 0x44, 0xaa, 0x9d, 0x24, 0xf3, 0xe5, 0x8f, 0x32, 0x13, 0xde, 0x37, + 0xf0, 0xd8, 0x6c, 0x96, 0x4d, 0x31, 0x5f, 0xa8, 0x60, 0x0e, 0xee, 0x13, 0x15, 0x97, 0xa2, 0xf2, 0x9f, 0xb0, 0x0b, + 0x5e, 0x8c, 0x07, 0xaf, 0xd7, 0x08, 0xb0, 0x5f, 0xf9, 0x4f, 0xde, 0x98, 0xbd, 0xb5, 0x6e, 0x7c, 0x99, 0x89, 0xf8, + 0xc0, 0x47, 0xb7, 0x94, 0x8f, 0xee, 0xbc, 0x4c, 0x7f, 0x36, 0xa0, 0x44, 0x46, 0x65, 0xc5, 0x57, 0x2b, 0x9e, 0xce, + 0x6e, 0x93, 0x28, 0x1b, 0x55, 0x5c, 0xc0, 0xf4, 0x82, 0xe3, 0x5d, 0xb2, 0x3e, 0xcf, 0x92, 0xd7, 0x10, 0x7b, 0x60, + 0x25, 0x15, 0x16, 0x3f, 0x2c, 0x33, 0xb5, 0x98, 0x85, 0xac, 0xa4, 0xe0, 0xc1, 0xec, 0x3a, 0x89, 0xde, 0x2e, 0x3d, + 0x24, 0x35, 0x33, 0x65, 0x9b, 0xda, 0x11, 0x6a, 0xe3, 0xeb, 0x48, 0x37, 0xda, 0x02, 0x00, 0xee, 0xd9, 0x22, 0x8d, + 0x24, 0x13, 0xc3, 0x49, 0xcd, 0xb8, 0x49, 0x2f, 0x30, 0x35, 0xae, 0x59, 0x45, 0x13, 0x67, 0x21, 0x03, 0x7a, 0x7f, + 0xc0, 0xcb, 0xc1, 0xe7, 0x0c, 0xee, 0x3f, 0x68, 0x0d, 0x5c, 0x1e, 0x16, 0xfd, 0xbe, 0x3c, 0x2c, 0xb6, 0xdb, 0xf2, + 0x28, 0xee, 0xf7, 0xe5, 0x51, 0x6c, 0xf8, 0x07, 0xa5, 0xd8, 0x36, 0xe6, 0x06, 0x09, 0xcd, 0x25, 0x44, 0x2d, 0x1a, + 0xc1, 0x1f, 0x9a, 0xe5, 0x5c, 0x44, 0xf9, 0x61, 0xd2, 0xef, 0xf7, 0x96, 0x33, 0x31, 0xc8, 0x87, 0x49, 0x94, 0x0f, + 0x13, 0xcf, 0x09, 0xf1, 0x57, 0xcf, 0x09, 0x51, 0xd1, 0xc0, 0x15, 0x9c, 0x19, 0x80, 0x28, 0xe0, 0xd3, 0x3f, 0xaa, + 0x6b, 0x29, 0x74, 0x2d, 0xb1, 0xaa, 0x25, 0xd1, 0x15, 0xd4, 0xec, 0xba, 0x08, 0x4b, 0x2c, 0x85, 0x2e, 0xd9, 0x9f, + 0x4b, 0xe0, 0x89, 0x72, 0x5e, 0x6d, 0x80, 0x81, 0x8d, 0xf0, 0xce, 0x61, 0xc2, 0x49, 0xac, 0x6b, 0x40, 0x3b, 0xdd, + 0xd4, 0xf4, 0x82, 0xae, 0xe8, 0x25, 0xf2, 0xb3, 0x17, 0x60, 0xb0, 0x74, 0xc8, 0xf2, 0xe9, 0x60, 0x70, 0x41, 0x56, + 0xac, 0x9c, 0x87, 0xf1, 0x20, 0x5c, 0xcf, 0xf2, 0xe1, 0x45, 0x74, 0x41, 0xc8, 0x57, 0xc5, 0x82, 0xf6, 0x56, 0xa3, + 0xf2, 0x63, 0x06, 0xe1, 0xfd, 0xd2, 0x59, 0x98, 0x99, 0x38, 0x1f, 0xab, 0xd1, 0x2d, 0x5d, 0x41, 0xfc, 0x1a, 0xb8, + 0x91, 0x90, 0x08, 0x3a, 0x72, 0x49, 0x57, 0x74, 0x4d, 0xa5, 0x99, 0x61, 0x8c, 0xd6, 0x6d, 0x8f, 0x93, 0x04, 0x1c, + 0x93, 0x5d, 0xf1, 0xd1, 0x58, 0x15, 0xde, 0xf5, 0x1d, 0xa1, 0xbd, 0x5e, 0xe2, 0x06, 0xe9, 0xbb, 0xf6, 0x20, 0x01, + 0x23, 0x32, 0x52, 0x03, 0x65, 0x46, 0x46, 0x52, 0x33, 0xa9, 0x38, 0x24, 0xb1, 0x3f, 0x24, 0x6a, 0x1c, 0x12, 0x7f, + 0x1c, 0x72, 0x3d, 0x0e, 0xc8, 0xdd, 0x2f, 0xd9, 0x98, 0xa6, 0x6c, 0x4c, 0xd7, 0x6a, 0x54, 0xe8, 0x15, 0x3d, 0xd7, + 0xd4, 0xf1, 0x8c, 0xbd, 0x81, 0x03, 0x7b, 0x10, 0xe6, 0xb3, 0x78, 0xf8, 0x26, 0x7a, 0x43, 0xc8, 0x57, 0x92, 0x5e, + 0xab, 0x4b, 0x19, 0x04, 0x42, 0xbc, 0x02, 0xe7, 0x52, 0x17, 0xea, 0xe4, 0xca, 0xec, 0x38, 0x7c, 0xba, 0x6c, 0x3c, + 0x9d, 0x43, 0x44, 0x1f, 0xb4, 0x52, 0xe9, 0xf7, 0xc3, 0x0b, 0x56, 0xce, 0xcf, 0xc2, 0x31, 0x01, 0x1c, 0x1e, 0x3d, + 0x9c, 0x17, 0xa3, 0x5b, 0x7a, 0x31, 0xba, 0x23, 0x60, 0xe1, 0x35, 0x9e, 0xae, 0x0f, 0x59, 0x3c, 0x1d, 0x0c, 0xd6, + 0x48, 0xd5, 0x55, 0xee, 0x35, 0x59, 0xd0, 0x0b, 0x9c, 0x08, 0x02, 0x0c, 0x7d, 0x26, 0xd6, 0x86, 0x86, 0xbf, 0x61, + 0xf0, 0xf1, 0x1d, 0xbb, 0x18, 0xdd, 0xd1, 0x5b, 0xf6, 0x66, 0x3b, 0x9e, 0x02, 0x33, 0xb5, 0x9a, 0x85, 0x77, 0x87, + 0x97, 0xb3, 0x4b, 0x76, 0x17, 0xdd, 0x1d, 0x41, 0x43, 0xaf, 0xd8, 0x1d, 0x02, 0x2e, 0xa5, 0x8f, 0x97, 0x83, 0x37, + 0x64, 0x7f, 0x30, 0x48, 0x49, 0x14, 0x5e, 0x87, 0x5e, 0x2b, 0xdf, 0xd0, 0x3b, 0x42, 0x57, 0xec, 0x16, 0x47, 0xe3, + 0x92, 0xe1, 0x07, 0xe7, 0xec, 0xae, 0xbe, 0x0e, 0xbd, 0xdd, 0x9c, 0x88, 0x4e, 0x10, 0x23, 0xf4, 0x35, 0x70, 0x34, + 0xcb, 0x85, 0x99, 0x80, 0x27, 0x73, 0x91, 0xd1, 0xa2, 0xd0, 0x0c, 0xc4, 0x59, 0x09, 0x88, 0x25, 0x51, 0xf7, 0x9b, + 0x8d, 0xce, 0x60, 0x39, 0xf7, 0xfb, 0xbd, 0xca, 0xd0, 0x03, 0x44, 0xce, 0xec, 0xa4, 0x07, 0x3d, 0x9f, 0x1e, 0xe0, + 0x27, 0x7a, 0xd5, 0x20, 0x4e, 0xe6, 0x77, 0xcb, 0xe8, 0x57, 0x8f, 0x3e, 0xfc, 0xd0, 0x4d, 0x79, 0x44, 0xfe, 0xef, + 0x53, 0x9e, 0x32, 0x8f, 0xde, 0x54, 0x1e, 0x08, 0x9e, 0xb7, 0x26, 0x95, 0x46, 0xa2, 0x1a, 0x9d, 0xad, 0x62, 0xd0, + 0x46, 0xa2, 0xb6, 0x41, 0x3f, 0xa1, 0x85, 0x15, 0x44, 0xc8, 0x39, 0x78, 0x01, 0x06, 0xa9, 0x10, 0x2a, 0x47, 0x2d, + 0x4a, 0x34, 0x04, 0xc9, 0x65, 0xc9, 0x55, 0xf8, 0x1c, 0x42, 0xd5, 0xe9, 0xe3, 0x4c, 0x84, 0x0d, 0x3d, 0x0e, 0x7d, + 0x00, 0xf8, 0x5f, 0x76, 0xc8, 0x45, 0xc9, 0x2f, 0xf1, 0x6c, 0x6e, 0x13, 0x8c, 0x82, 0x25, 0xa2, 0x19, 0xda, 0x06, + 0xb1, 0x1f, 0x4b, 0x82, 0xf5, 0x48, 0x1a, 0x8f, 0x4a, 0x73, 0x44, 0xf8, 0x51, 0x7c, 0x14, 0x3d, 0x8d, 0x0d, 0x89, + 0xe4, 0x48, 0x22, 0xf9, 0x00, 0x08, 0x27, 0x41, 0x7f, 0x71, 0xd7, 0x64, 0xd7, 0x42, 0x62, 0xd0, 0x9f, 0x96, 0x4c, + 0xcb, 0xee, 0x55, 0x8f, 0x7d, 0x45, 0x90, 0x3b, 0xa6, 0xff, 0xf2, 0xfa, 0xf0, 0xaf, 0x25, 0xce, 0xa0, 0xf5, 0x7c, + 0x51, 0x9d, 0x99, 0x79, 0x83, 0x1b, 0x79, 0x5d, 0xd6, 0xae, 0xcb, 0x17, 0x7c, 0x8f, 0xdf, 0x56, 0x5c, 0xa4, 0xe5, + 0xde, 0xcf, 0x55, 0x1b, 0xcf, 0xa9, 0x5c, 0xaf, 0x5c, 0x9c, 0x15, 0x65, 0x9c, 0xea, 0x49, 0x5d, 0x8c, 0x35, 0x6c, + 0xc3, 0xef, 0x11, 0x75, 0x25, 0x2d, 0x47, 0x4f, 0x29, 0x57, 0xcd, 0x94, 0x8b, 0x75, 0x9e, 0xff, 0xb4, 0x93, 0x8a, + 0x53, 0xdc, 0x4c, 0x41, 0xaa, 0xd4, 0x72, 0x01, 0xd5, 0x73, 0xd4, 0x72, 0xb7, 0x34, 0x3b, 0xc0, 0xb9, 0x6d, 0xaa, + 0x8f, 0x95, 0xd9, 0x85, 0x97, 0xdc, 0xb8, 0x3f, 0x99, 0x32, 0x2c, 0x18, 0x85, 0x36, 0xab, 0xae, 0xb4, 0x7d, 0xa1, + 0x75, 0x1a, 0x86, 0x2b, 0x3f, 0x5e, 0x40, 0xba, 0x80, 0x71, 0xbc, 0x28, 0x99, 0x18, 0xb7, 0x47, 0x6f, 0x05, 0xf1, + 0x25, 0x5b, 0x81, 0xf4, 0xfb, 0x3d, 0xe1, 0xed, 0xba, 0x8e, 0xb6, 0x7b, 0xe2, 0x94, 0x51, 0xb9, 0x8a, 0xc5, 0xf7, + 0xf1, 0xca, 0x40, 0x26, 0xab, 0xe3, 0xb1, 0x31, 0xa6, 0xd3, 0x7f, 0x24, 0xa1, 0x5f, 0x08, 0x05, 0x9f, 0xf5, 0xd2, + 0xca, 0x93, 0xdb, 0xc3, 0x32, 0xae, 0xd1, 0x2b, 0x71, 0xa5, 0xfb, 0x66, 0xa4, 0x90, 0x7a, 0xe4, 0xab, 0xa6, 0x80, + 0xde, 0x8c, 0x7d, 0x33, 0x15, 0xe6, 0xed, 0x9e, 0x31, 0x57, 0x08, 0x56, 0xaa, 0xec, 0xf6, 0x9d, 0x1a, 0x53, 0x31, + 0x83, 0x29, 0xb6, 0x9d, 0xc5, 0xa4, 0x5b, 0xf9, 0xa7, 0x9d, 0xfb, 0x65, 0xde, 0xe1, 0xae, 0xa8, 0xdf, 0x02, 0x17, + 0x9a, 0x15, 0x65, 0xd5, 0x96, 0x0d, 0xdb, 0xc6, 0x1b, 0x59, 0x28, 0x36, 0xc0, 0xb2, 0xe7, 0xbe, 0x85, 0x07, 0x88, + 0x9b, 0x70, 0xcf, 0x2e, 0x6a, 0xb8, 0x31, 0x7c, 0x59, 0x49, 0xbe, 0x2b, 0x8d, 0xb9, 0xf4, 0xa9, 0xd2, 0xc4, 0x70, + 0xb2, 0x18, 0x71, 0x91, 0x2e, 0xea, 0xcc, 0xae, 0x85, 0xcf, 0x78, 0x19, 0xce, 0xf9, 0xc2, 0xe8, 0xa6, 0x74, 0xe9, + 0x05, 0x8b, 0x75, 0xa7, 0x37, 0x2b, 0x8d, 0x95, 0x12, 0x71, 0x6b, 0x96, 0x09, 0x94, 0xa5, 0xac, 0x95, 0xf0, 0xa6, + 0x68, 0xd9, 0x4a, 0x1a, 0x79, 0xcf, 0x1c, 0xdc, 0xc7, 0x7e, 0x40, 0x4c, 0x64, 0x13, 0x98, 0x14, 0x0d, 0x1d, 0xd0, + 0xae, 0xba, 0xf0, 0xcd, 0xa8, 0x07, 0x83, 0xdc, 0x92, 0x44, 0xac, 0x20, 0xc5, 0x0a, 0xd6, 0x35, 0x2b, 0xe6, 0xf9, + 0x82, 0x5e, 0x30, 0x39, 0x4f, 0x17, 0x74, 0xc5, 0xe4, 0x7c, 0x8d, 0x37, 0xa1, 0x0b, 0x38, 0x21, 0xc9, 0x26, 0x56, + 0x0a, 0xd8, 0x0b, 0xbc, 0xbc, 0xe1, 0x99, 0xaa, 0x69, 0xd9, 0xa5, 0xe2, 0x00, 0xe3, 0xf3, 0x32, 0x0c, 0xcb, 0xe1, + 0x05, 0x58, 0x4b, 0xec, 0x87, 0xab, 0x39, 0x5f, 0xa8, 0xdf, 0x10, 0x75, 0x3e, 0x09, 0x15, 0xbb, 0x60, 0xf7, 0x02, + 0x99, 0x5e, 0xcd, 0xf9, 0x42, 0x8d, 0x84, 0x2e, 0xf8, 0xca, 0x1a, 0x9b, 0xc4, 0x9e, 0xa0, 0x65, 0x16, 0xcf, 0xc7, + 0x8b, 0x28, 0xae, 0x61, 0x19, 0x9e, 0xaa, 0x99, 0x69, 0xc9, 0x7f, 0x12, 0xb5, 0xa1, 0x89, 0xbe, 0xc1, 0x2a, 0xf2, + 0x87, 0xc7, 0x47, 0x97, 0x40, 0xc6, 0xce, 0xae, 0x64, 0xe6, 0x43, 0xdf, 0x47, 0x06, 0xf7, 0xdc, 0x94, 0x33, 0xae, + 0x82, 0x44, 0x19, 0xb8, 0x7b, 0x35, 0x4b, 0xc6, 0x5a, 0x84, 0xef, 0x1e, 0x15, 0x45, 0x9f, 0x49, 0xd3, 0x80, 0xee, + 0x23, 0xc1, 0x1c, 0xe8, 0xbd, 0x42, 0x87, 0xcb, 0x6a, 0x9b, 0x09, 0xf8, 0x8b, 0x04, 0xf9, 0xad, 0xd0, 0xab, 0x1a, + 0x83, 0x2a, 0xda, 0x45, 0x2c, 0xfd, 0xfb, 0x88, 0x1f, 0x65, 0xf3, 0x2f, 0x73, 0x8f, 0x57, 0x12, 0x06, 0x3f, 0xa4, + 0x66, 0x93, 0xcc, 0xdb, 0x2b, 0xf6, 0x3d, 0x74, 0xd4, 0xa3, 0xd6, 0x78, 0x5f, 0xbd, 0xe0, 0x14, 0x62, 0x94, 0x50, + 0x74, 0x12, 0x0c, 0xe0, 0x76, 0x09, 0x29, 0xee, 0x06, 0xbb, 0x69, 0x5e, 0xf3, 0xa2, 0xe0, 0x7c, 0x5d, 0x55, 0x81, + 0x1f, 0xd0, 0x70, 0xbe, 0xd8, 0x0d, 0x61, 0x38, 0xa6, 0xad, 0x6b, 0x18, 0x84, 0x19, 0xc3, 0x48, 0x08, 0x5e, 0xff, + 0xa2, 0x27, 0x34, 0x89, 0x57, 0xdf, 0xf1, 0x4f, 0x19, 0x2f, 0x14, 0x91, 0x06, 0x11, 0x52, 0x37, 0xf1, 0x8d, 0x4c, + 0x93, 0x02, 0x0a, 0x01, 0x46, 0x01, 0x95, 0xd8, 0xd0, 0x54, 0xfc, 0xad, 0x16, 0x1f, 0xfc, 0xd4, 0x74, 0x3c, 0x1a, + 0xd7, 0xad, 0xce, 0xa8, 0xa0, 0x33, 0xd0, 0xa3, 0x56, 0xd4, 0xd3, 0xa0, 0x95, 0x60, 0x1a, 0x69, 0xde, 0xba, 0x87, + 0xc0, 0x2b, 0xd3, 0xe2, 0x9d, 0x07, 0x74, 0x73, 0xe6, 0x83, 0x27, 0x8f, 0xe9, 0x99, 0x43, 0x4f, 0xae, 0xd8, 0x51, + 0xd5, 0x43, 0xed, 0xbd, 0x19, 0xa1, 0xa0, 0xdf, 0xc7, 0x14, 0xe8, 0x46, 0x50, 0x7b, 0x57, 0xf7, 0x1f, 0xcb, 0x5d, + 0x0e, 0xdf, 0x71, 0x96, 0x1b, 0xc0, 0x52, 0x91, 0xb5, 0x02, 0x8f, 0x02, 0xd4, 0xa5, 0x32, 0x84, 0x2d, 0xe6, 0x70, + 0xa8, 0xec, 0x56, 0xad, 0x86, 0x92, 0x1c, 0x96, 0x23, 0x70, 0x08, 0x5d, 0x97, 0x83, 0x72, 0xb4, 0xcc, 0xaa, 0xf7, + 0xf8, 0x5b, 0xb3, 0x0e, 0x49, 0x76, 0x1f, 0xeb, 0xc0, 0x2d, 0xeb, 0x30, 0xfd, 0x68, 0x90, 0x02, 0xd0, 0x64, 0x23, + 0x70, 0x09, 0xc0, 0x7b, 0xfb, 0x8f, 0x08, 0xb5, 0x32, 0xbd, 0x97, 0xb1, 0x50, 0xdf, 0x37, 0x92, 0xa0, 0x84, 0x66, + 0x42, 0xe5, 0x58, 0x0a, 0xde, 0x79, 0xa4, 0x73, 0x52, 0x67, 0xe2, 0x3d, 0x88, 0xd3, 0xc2, 0x07, 0xf6, 0x16, 0x04, + 0xe7, 0x2c, 0xe8, 0x1d, 0xde, 0x66, 0xb5, 0xd4, 0x46, 0x0f, 0x14, 0xc0, 0xef, 0x06, 0x77, 0x08, 0xf2, 0xd5, 0x18, + 0xae, 0x95, 0xbc, 0x09, 0xf9, 0xb0, 0xa0, 0x07, 0x64, 0x60, 0x9f, 0xc5, 0x30, 0xa6, 0x07, 0xe4, 0xd0, 0x3e, 0x4b, + 0x37, 0x80, 0x03, 0xa9, 0x47, 0x95, 0x1e, 0x40, 0x83, 0x7e, 0xb3, 0x2d, 0xb2, 0x24, 0xeb, 0xc7, 0xd2, 0x28, 0x62, + 0xa0, 0x4a, 0x10, 0x51, 0x8b, 0x7f, 0x3d, 0x98, 0xeb, 0x0e, 0x73, 0x81, 0x30, 0x07, 0x03, 0x0e, 0xe2, 0x36, 0x08, + 0xcd, 0x01, 0xb3, 0xb9, 0x8d, 0x04, 0xbd, 0xb3, 0x86, 0x99, 0x1d, 0xfd, 0xe1, 0x56, 0x82, 0x6f, 0xb2, 0xd6, 0xa8, + 0xf3, 0xe2, 0x10, 0x08, 0x82, 0x37, 0x85, 0xaa, 0xf6, 0xaa, 0x07, 0x36, 0xde, 0xaa, 0x1f, 0xdb, 0xed, 0x78, 0x2a, + 0xdc, 0xb5, 0x5f, 0x50, 0x38, 0xf9, 0x94, 0xfc, 0xeb, 0xbd, 0xc9, 0xe0, 0xc0, 0xc8, 0xf0, 0xa5, 0xb7, 0x7f, 0xe1, + 0x6b, 0x2d, 0xdd, 0x13, 0x83, 0x92, 0x3c, 0x3e, 0x50, 0xf4, 0xef, 0x5e, 0x59, 0xf9, 0xd4, 0x4e, 0xff, 0x76, 0x6b, + 0xd6, 0xe7, 0xe1, 0x68, 0xb2, 0xdd, 0xf6, 0xe2, 0x4a, 0x7b, 0xac, 0xe9, 0x05, 0x81, 0xce, 0xf5, 0x64, 0xff, 0x00, + 0xa2, 0x22, 0x34, 0xe3, 0x6e, 0x96, 0x0d, 0x89, 0x8c, 0x1f, 0xa7, 0xb3, 0x6c, 0x08, 0x76, 0xb8, 0x17, 0x95, 0xb8, + 0x1c, 0xb5, 0x36, 0x38, 0x3d, 0x4b, 0x42, 0x08, 0xe5, 0x80, 0x95, 0xdd, 0xaa, 0x3f, 0x77, 0xca, 0x4c, 0x48, 0x4d, + 0x56, 0xb7, 0x53, 0xba, 0x87, 0x69, 0xbe, 0x67, 0x46, 0x70, 0xc0, 0xbd, 0xfd, 0x55, 0x7f, 0x0c, 0x93, 0x4c, 0x93, + 0x53, 0x24, 0xbf, 0x48, 0x4f, 0x21, 0x69, 0x87, 0x9e, 0x2a, 0x02, 0x38, 0xa1, 0xf6, 0x63, 0xf8, 0x0d, 0xe3, 0xfe, + 0x5d, 0xf3, 0xb5, 0x9b, 0x8a, 0xe8, 0x29, 0xc5, 0x32, 0x35, 0x39, 0x4d, 0xb2, 0x22, 0x81, 0xa8, 0x8d, 0xaa, 0x19, + 0xd1, 0x13, 0x17, 0xf3, 0x51, 0x11, 0x3e, 0xaf, 0xd6, 0xff, 0x19, 0xc2, 0x67, 0x14, 0x6e, 0x00, 0x97, 0x57, 0x5c, + 0x9e, 0x87, 0xcf, 0x9e, 0xd2, 0xbd, 0xc9, 0x37, 0x07, 0x74, 0xef, 0xe0, 0xc9, 0x33, 0x02, 0xb0, 0x68, 0x97, 0xe7, + 0xe1, 0xc1, 0xb3, 0x67, 0x74, 0xef, 0xdb, 0x6f, 0xe9, 0xde, 0xe4, 0xc9, 0x41, 0x23, 0x6d, 0xf2, 0xec, 0x5b, 0xba, + 0xf7, 0xcd, 0xd3, 0x46, 0xda, 0xc1, 0xf8, 0x19, 0xdd, 0xfb, 0xfb, 0x37, 0x26, 0xed, 0x6f, 0x90, 0xed, 0xdb, 0x03, + 0xfc, 0xcf, 0xa4, 0x4d, 0x9e, 0x3d, 0xa1, 0x7b, 0x93, 0x31, 0x54, 0xf2, 0xcc, 0x55, 0x32, 0x9e, 0xc0, 0xc7, 0x4f, + 0xe0, 0xbf, 0xbf, 0x91, 0x60, 0x41, 0x2b, 0xc9, 0x72, 0x81, 0xfa, 0x33, 0x14, 0x71, 0xa2, 0x6a, 0x22, 0xe1, 0x21, + 0x66, 0x56, 0xdf, 0xc4, 0x61, 0x40, 0x5c, 0x3a, 0x14, 0x44, 0xf7, 0xc6, 0xa3, 0x67, 0x24, 0xf0, 0xe1, 0xe9, 0x6e, + 0x7c, 0x90, 0xb1, 0x5c, 0xcc, 0xb3, 0xaf, 0x72, 0x13, 0x5b, 0xc1, 0x03, 0xb0, 0xfa, 0xe8, 0xe7, 0xaa, 0xe4, 0x3c, + 0xfb, 0xaa, 0x92, 0xbb, 0xb9, 0x7e, 0x6b, 0x01, 0xca, 0xfb, 0xab, 0x96, 0xdd, 0x14, 0x2a, 0x74, 0x5a, 0x6b, 0xf4, + 0xd9, 0x47, 0x4c, 0x1f, 0x0c, 0xbc, 0x1b, 0xf6, 0x3f, 0x76, 0xca, 0x69, 0x7d, 0xa3, 0x51, 0xa8, 0x51, 0x79, 0x48, + 0xd8, 0x11, 0x14, 0x3d, 0x18, 0x00, 0x4f, 0xe0, 0xe1, 0xbe, 0xfd, 0x9b, 0x65, 0x7c, 0xec, 0x28, 0xe3, 0x67, 0x94, + 0x21, 0xa0, 0x51, 0x0f, 0xb3, 0x9b, 0x1e, 0x36, 0xba, 0xd5, 0x4b, 0x96, 0xea, 0x64, 0x6a, 0x7a, 0x06, 0xfb, 0x5a, + 0xd7, 0x72, 0xcf, 0x88, 0xa2, 0xe5, 0xc5, 0x5e, 0xca, 0x67, 0x15, 0xfb, 0xc7, 0x12, 0xd5, 0x5b, 0x51, 0xe3, 0x8d, + 0xcc, 0x66, 0x15, 0xfb, 0xde, 0xbc, 0x01, 0x6e, 0x86, 0xfd, 0xa6, 0x9e, 0xfc, 0xc0, 0x19, 0x5c, 0xda, 0xf6, 0x28, + 0x13, 0x23, 0xc0, 0x0a, 0xc8, 0xc0, 0x81, 0x07, 0x40, 0x07, 0xfd, 0xd1, 0xde, 0x6e, 0x55, 0x4a, 0xb3, 0xcf, 0x16, + 0x06, 0xd0, 0x30, 0x6f, 0x13, 0x57, 0xf6, 0xef, 0x0d, 0x79, 0x09, 0x0a, 0xb7, 0x9a, 0xe5, 0xed, 0x14, 0x86, 0x10, + 0x82, 0x3f, 0x2e, 0x19, 0x00, 0x0e, 0x04, 0x18, 0x8c, 0xb5, 0x0c, 0xa8, 0xd9, 0xf2, 0xd1, 0x86, 0x2b, 0xf5, 0x24, + 0x70, 0x06, 0x17, 0xb2, 0x48, 0xf8, 0x89, 0x16, 0xfb, 0xa3, 0xf5, 0xa3, 0xef, 0xdb, 0xe3, 0xc1, 0xda, 0xf7, 0xf8, + 0x48, 0x7f, 0xd6, 0xb8, 0x0e, 0x6c, 0x5a, 0xbe, 0xf1, 0xa2, 0xb6, 0x12, 0x8f, 0x12, 0x78, 0x03, 0x13, 0x91, 0xc2, + 0x20, 0xd5, 0x02, 0xc7, 0xa0, 0xbc, 0xb1, 0x10, 0x4b, 0xd5, 0xd5, 0x0d, 0xb6, 0x20, 0x32, 0x04, 0x0f, 0xb7, 0xdf, + 0x97, 0x2a, 0x70, 0x54, 0xbf, 0xcf, 0xa5, 0xef, 0xf6, 0x64, 0xec, 0xc8, 0x71, 0xea, 0xa7, 0xc2, 0xc1, 0x7f, 0x93, + 0xba, 0x36, 0x96, 0x2b, 0x29, 0xb3, 0x2c, 0x0b, 0x3b, 0x0a, 0xb5, 0xdc, 0xa3, 0xf2, 0x20, 0xf9, 0x42, 0x0e, 0x91, + 0x2c, 0x30, 0x0a, 0x05, 0x19, 0x4e, 0xa8, 0x18, 0xad, 0x45, 0xb9, 0xcc, 0x2e, 0xaa, 0x70, 0xa3, 0x14, 0xca, 0x9c, + 0xa2, 0x6f, 0x37, 0x38, 0x90, 0x90, 0x28, 0x2b, 0xdf, 0xc6, 0x6f, 0x43, 0x04, 0xab, 0xe3, 0xda, 0x16, 0x8a, 0x7b, + 0xfb, 0x93, 0xa7, 0x5d, 0xfc, 0x91, 0x71, 0x01, 0x75, 0xb1, 0x98, 0x86, 0x13, 0x1b, 0xfb, 0xc6, 0x7d, 0x61, 0x35, + 0x3d, 0x00, 0xf5, 0x5d, 0x2a, 0x31, 0x82, 0xfa, 0xca, 0xd8, 0xc7, 0xf6, 0x18, 0x93, 0x33, 0x88, 0x35, 0xac, 0x72, + 0x66, 0xaa, 0x6f, 0x84, 0x1d, 0x01, 0x70, 0x23, 0xb4, 0x46, 0x47, 0x26, 0xa9, 0x42, 0x3c, 0x2f, 0x55, 0xf8, 0xd6, + 0x8c, 0xd0, 0x31, 0x78, 0x53, 0xd9, 0x46, 0x66, 0xd2, 0x17, 0x0c, 0x9a, 0x63, 0x5b, 0x47, 0x61, 0xb5, 0x95, 0x65, + 0x47, 0x00, 0x37, 0x90, 0x1d, 0x9a, 0x8b, 0xe7, 0xac, 0x9a, 0x67, 0x8b, 0xc8, 0x04, 0x05, 0x5c, 0x0a, 0xcb, 0xa0, + 0x7d, 0xba, 0x47, 0xb6, 0xe3, 0x10, 0xba, 0xe1, 0x3e, 0x82, 0xf1, 0xb4, 0x9b, 0x82, 0x15, 0x44, 0x23, 0xc4, 0xc3, + 0x8c, 0x59, 0x7c, 0xaf, 0x34, 0xe5, 0xa9, 0x6a, 0x09, 0x04, 0x8e, 0x42, 0xa8, 0x8b, 0x5d, 0xa3, 0x04, 0x97, 0xa9, + 0x11, 0xcc, 0x60, 0xc7, 0x8e, 0xd4, 0x76, 0xc9, 0x39, 0x1d, 0xaa, 0x29, 0x2d, 0xf5, 0x94, 0x6a, 0x5f, 0x43, 0x31, + 0x2f, 0xd1, 0x43, 0x0f, 0x5c, 0x0f, 0xb4, 0x43, 0x5e, 0x49, 0x27, 0x26, 0x82, 0x4e, 0xab, 0x4d, 0xd8, 0xb9, 0x91, + 0x6e, 0x59, 0x8d, 0xbc, 0x63, 0x68, 0x76, 0xc4, 0x4b, 0x3f, 0x50, 0x17, 0x40, 0x84, 0xdc, 0xdb, 0x22, 0x73, 0x44, + 0xb3, 0xac, 0x7c, 0x05, 0x65, 0x71, 0xc4, 0xd6, 0x15, 0x70, 0x2d, 0x05, 0x93, 0x4b, 0x1e, 0xf1, 0x14, 0x11, 0x01, + 0x8f, 0x95, 0x76, 0x7d, 0xa7, 0x25, 0x84, 0x66, 0x29, 0x10, 0x37, 0x17, 0xc5, 0xb9, 0xb6, 0x81, 0x2c, 0x80, 0xbe, + 0xfd, 0x94, 0x5d, 0x79, 0xe1, 0x60, 0x37, 0x57, 0x99, 0x78, 0xc1, 0x2f, 0x32, 0xc1, 0x53, 0x04, 0xbb, 0xba, 0x35, + 0x0f, 0xdc, 0xb1, 0x6d, 0x60, 0xf9, 0xf6, 0x1d, 0x2c, 0x98, 0x32, 0xd4, 0x4a, 0x89, 0x4c, 0x44, 0x02, 0x32, 0xfb, + 0xcc, 0xdd, 0x9b, 0x4c, 0xbc, 0x89, 0x6f, 0xc1, 0x9b, 0xa2, 0xc1, 0x4f, 0x8f, 0xce, 0xf1, 0x4b, 0x44, 0x12, 0x85, + 0x18, 0xb6, 0x18, 0x11, 0x0b, 0x91, 0x63, 0xc7, 0x84, 0x72, 0x25, 0x68, 0x6d, 0x0d, 0x81, 0x17, 0x7f, 0x5a, 0x75, + 0xef, 0x2a, 0x13, 0xc6, 0x3e, 0xe3, 0x2a, 0xbe, 0x65, 0xa5, 0x02, 0xb3, 0xc0, 0x38, 0xf7, 0x6d, 0x29, 0xc9, 0x55, + 0x26, 0x8c, 0x80, 0xe4, 0x2a, 0xbe, 0xa5, 0x4d, 0x19, 0x87, 0xb6, 0xa2, 0xf3, 0xe2, 0xfc, 0xee, 0x0e, 0xbf, 0xc4, + 0x50, 0x2b, 0xe3, 0x7e, 0x1f, 0x24, 0x66, 0xd2, 0x36, 0x65, 0x26, 0x23, 0xa9, 0xd1, 0x42, 0x2a, 0xca, 0x07, 0x13, + 0xb2, 0xbb, 0x52, 0x2d, 0x23, 0x6a, 0xbf, 0x0a, 0xc5, 0x6c, 0x1c, 0x4d, 0x08, 0x9d, 0x74, 0xac, 0x77, 0xd3, 0x5a, + 0xc8, 0x34, 0x7a, 0x16, 0x79, 0x3e, 0x9d, 0x05, 0xab, 0xa6, 0xc5, 0x21, 0xe3, 0xd3, 0x62, 0x30, 0x20, 0xda, 0xa5, + 0x70, 0x83, 0xf5, 0x80, 0x29, 0x8d, 0x8b, 0xb7, 0x66, 0x5a, 0xfd, 0x4a, 0xaa, 0x90, 0xf4, 0x9e, 0x01, 0x49, 0x26, + 0x5d, 0xb0, 0x5b, 0x90, 0x28, 0x7a, 0xfe, 0x77, 0x6a, 0x0b, 0xee, 0x7a, 0x30, 0x36, 0xa3, 0xfb, 0x7a, 0xc6, 0x7f, + 0xa8, 0x6d, 0x41, 0xd4, 0xa7, 0x92, 0xf5, 0x3a, 0x12, 0x55, 0xc8, 0x45, 0xf8, 0xd9, 0xd1, 0x10, 0x43, 0x54, 0x7b, + 0x2c, 0x10, 0xeb, 0xab, 0x73, 0x5e, 0xe0, 0xf4, 0x33, 0x77, 0xb9, 0x82, 0x6d, 0x41, 0x2b, 0x43, 0xa3, 0xde, 0xc6, + 0x6f, 0x23, 0x7b, 0x59, 0xd0, 0x45, 0xbe, 0x40, 0x21, 0x6b, 0x1e, 0x86, 0xd5, 0xb0, 0x3d, 0x88, 0x64, 0xbf, 0x3d, + 0x09, 0x8d, 0xc6, 0xc0, 0x02, 0xd9, 0xa1, 0x11, 0xb8, 0x08, 0xad, 0xfc, 0xed, 0x10, 0x5c, 0xb8, 0x2c, 0x22, 0xcb, + 0x50, 0xc7, 0x6f, 0x6a, 0x37, 0x41, 0xf5, 0x0a, 0x9d, 0xa6, 0xb0, 0x2a, 0x65, 0x92, 0x0f, 0xbf, 0x5e, 0xc9, 0x02, + 0x33, 0x79, 0x5d, 0xf6, 0xe8, 0x6b, 0xbb, 0xbd, 0x03, 0x53, 0xb0, 0xee, 0x93, 0xf7, 0xf5, 0xe3, 0xce, 0x9e, 0x80, + 0x51, 0xac, 0xca, 0xd1, 0x14, 0x52, 0x6a, 0x1f, 0x94, 0xfa, 0x63, 0xb8, 0x14, 0x9a, 0x63, 0xb7, 0x80, 0x49, 0xc0, + 0x3e, 0x43, 0xaa, 0xc7, 0xb4, 0x63, 0x9f, 0xa3, 0x0d, 0x2c, 0x09, 0x38, 0xfc, 0xa3, 0x4c, 0xd6, 0xfe, 0xd5, 0x5d, + 0xa4, 0xcd, 0x90, 0x2d, 0xf3, 0x05, 0xf0, 0xf9, 0xb0, 0x6b, 0xa3, 0x12, 0x65, 0x13, 0x91, 0xa4, 0xb0, 0xe5, 0x31, + 0x48, 0x7b, 0x14, 0xd3, 0x55, 0xc1, 0x93, 0x0c, 0xa5, 0x14, 0x89, 0xf6, 0x09, 0xce, 0xe1, 0x0d, 0xee, 0x47, 0x15, + 0x10, 0x5e, 0x85, 0x9c, 0x8e, 0x52, 0xaa, 0x2d, 0x60, 0x14, 0xf5, 0x00, 0x51, 0x5e, 0x06, 0x72, 0xbc, 0xed, 0x76, + 0x42, 0x57, 0x6c, 0x39, 0x9c, 0x50, 0x24, 0x25, 0x97, 0x58, 0xee, 0x15, 0xe8, 0x3c, 0xce, 0x59, 0xef, 0x25, 0x60, + 0x11, 0x9c, 0xc1, 0xdf, 0x98, 0xd0, 0x6b, 0xf8, 0x9b, 0x13, 0xfa, 0x86, 0x85, 0x57, 0xc3, 0x4b, 0xb2, 0x1f, 0xa6, + 0x83, 0x89, 0x12, 0x8c, 0xdd, 0xb1, 0x65, 0x19, 0xaa, 0xc4, 0xd5, 0xfe, 0x05, 0x79, 0x7c, 0x41, 0x6f, 0xe9, 0x0d, + 0x3d, 0xa5, 0x27, 0x40, 0xf8, 0xef, 0x0e, 0x27, 0x7c, 0x38, 0x79, 0xda, 0xef, 0xf7, 0xce, 0xfb, 0xfd, 0xde, 0x99, + 0x31, 0xa0, 0xd0, 0xbb, 0xe8, 0xb2, 0xa6, 0xfa, 0xd7, 0x55, 0xbd, 0x98, 0x9e, 0xa8, 0x8d, 0x9b, 0xf0, 0x2c, 0x0f, + 0xaf, 0xf6, 0xef, 0xc8, 0x10, 0x1f, 0x2f, 0x72, 0x29, 0x8b, 0xf0, 0x72, 0xff, 0x8e, 0xd0, 0x93, 0x23, 0xd0, 0x9b, + 0x62, 0x7d, 0x27, 0x8f, 0xef, 0x74, 0x6d, 0x84, 0xbe, 0x0c, 0x13, 0xd8, 0x26, 0xb7, 0xcc, 0xde, 0xb5, 0x27, 0x63, + 0x88, 0x65, 0x72, 0xe7, 0x95, 0x77, 0xf7, 0xf8, 0x96, 0xec, 0xdf, 0x82, 0xa7, 0xa8, 0x25, 0x7f, 0xb3, 0xf0, 0x86, + 0xb5, 0x6a, 0x78, 0x7c, 0x47, 0x4f, 0x5b, 0x8d, 0x78, 0x7c, 0x47, 0xa2, 0xf0, 0x86, 0x5d, 0xd2, 0x53, 0x76, 0x45, + 0xe8, 0x79, 0xbf, 0x7f, 0xd6, 0xef, 0xcb, 0x7e, 0xff, 0x1f, 0x71, 0x18, 0xc6, 0xc3, 0x82, 0xec, 0x4b, 0x7a, 0xb7, + 0x3f, 0xe1, 0x4f, 0xc8, 0x2c, 0xd4, 0xcd, 0x57, 0x0b, 0xce, 0xaa, 0xbc, 0x55, 0xae, 0x3b, 0x0a, 0xd6, 0x0a, 0x77, + 0x4c, 0x3d, 0x9d, 0xd0, 0x1b, 0x56, 0xd0, 0x53, 0x16, 0x93, 0xe8, 0x1a, 0x5a, 0x71, 0x3e, 0x2b, 0xa2, 0x1b, 0x7a, + 0xca, 0xce, 0x66, 0x71, 0x74, 0x4a, 0x4f, 0x58, 0x3e, 0x9c, 0x40, 0xde, 0xd3, 0xe1, 0x0d, 0xd9, 0x3f, 0x21, 0x51, + 0x78, 0xa2, 0x7f, 0xdf, 0xd1, 0x4b, 0x1e, 0x9e, 0x50, 0xaf, 0x9a, 0x13, 0x62, 0xaa, 0x6f, 0xd4, 0x7e, 0x42, 0x22, + 0x7f, 0x30, 0x4f, 0xac, 0x3d, 0xcd, 0x23, 0x47, 0x1b, 0xd3, 0x32, 0x04, 0x7d, 0x73, 0x19, 0xde, 0x10, 0x32, 0x6d, + 0x8e, 0x1d, 0x0c, 0xe8, 0xec, 0x51, 0x94, 0x10, 0x7a, 0xe3, 0x97, 0x7a, 0x83, 0x63, 0x68, 0x46, 0x48, 0xa5, 0x9d, + 0x62, 0x1a, 0xae, 0x83, 0xd7, 0x1a, 0xac, 0xe3, 0xbc, 0xdf, 0x0f, 0xd7, 0xfd, 0x3e, 0x44, 0xba, 0x2f, 0x66, 0x26, + 0xb6, 0x9b, 0x23, 0x9b, 0xf4, 0x06, 0xb4, 0xff, 0xaf, 0x07, 0x03, 0xe8, 0x8c, 0x57, 0x52, 0x78, 0x33, 0x78, 0xfd, + 0xf8, 0x8e, 0xa8, 0x3a, 0x0a, 0x2a, 0x64, 0x58, 0xd0, 0x37, 0x34, 0x03, 0xc0, 0xaf, 0xd7, 0x83, 0x01, 0x89, 0xcc, + 0x67, 0x64, 0xfa, 0xfa, 0xf0, 0x64, 0x3a, 0x18, 0xbc, 0x36, 0xdb, 0xe4, 0x2d, 0xbb, 0xa7, 0x14, 0x58, 0x7f, 0x67, + 0xfd, 0xfe, 0xdb, 0xa3, 0x98, 0x9c, 0x17, 0x3c, 0xfe, 0x38, 0x6d, 0xb6, 0xe5, 0xad, 0x8b, 0xaa, 0x76, 0xd6, 0xef, + 0xaf, 0xfb, 0xfd, 0x53, 0xc0, 0x2e, 0x9a, 0x39, 0x5f, 0x4f, 0x90, 0xb6, 0xcc, 0x1d, 0x45, 0xd2, 0x24, 0x87, 0xc6, + 0xd0, 0xb6, 0x58, 0xb5, 0x6d, 0xd6, 0x91, 0x81, 0xc5, 0x51, 0xb3, 0xa2, 0xb8, 0x26, 0x51, 0xd8, 0x3b, 0xdb, 0x6e, + 0x4f, 0x19, 0x63, 0x31, 0x01, 0xe9, 0x87, 0xff, 0xfa, 0xb4, 0x6e, 0xc4, 0x10, 0x13, 0x12, 0x99, 0xcd, 0xcd, 0xd2, + 0x1e, 0x02, 0x11, 0x87, 0x4d, 0xff, 0xde, 0xdc, 0xcb, 0x45, 0xed, 0xf8, 0xd6, 0xdf, 0x01, 0x84, 0x48, 0xb2, 0x90, + 0xcf, 0x70, 0x0c, 0xca, 0x0c, 0x80, 0xcc, 0x23, 0x35, 0xf3, 0x12, 0x40, 0x80, 0xc9, 0x76, 0x3b, 0x1a, 0x8f, 0x27, + 0xb4, 0x60, 0xa3, 0xbf, 0x3d, 0x7b, 0x5c, 0x3d, 0x0e, 0x83, 0x60, 0x90, 0x91, 0x96, 0x9e, 0xc2, 0x2e, 0xd6, 0x6a, + 0x1f, 0x8c, 0xe0, 0x35, 0xfb, 0x78, 0x9d, 0x7d, 0x31, 0xfb, 0x88, 0x84, 0xb5, 0xc1, 0x38, 0x72, 0x91, 0xb6, 0xf4, + 0x76, 0xf7, 0x30, 0x98, 0x5c, 0xa4, 0x9f, 0x61, 0x3b, 0x7d, 0xfe, 0xcd, 0x83, 0xf1, 0x84, 0x83, 0xd1, 0x5d, 0x14, + 0xf4, 0x99, 0xb6, 0xdd, 0x56, 0xfe, 0x25, 0xf0, 0x2d, 0xa6, 0x82, 0x8e, 0xcd, 0xb2, 0x70, 0x83, 0x8a, 0xa8, 0xa3, + 0x65, 0x50, 0xd5, 0xca, 0x76, 0x0e, 0xa8, 0x25, 0x56, 0x65, 0xe2, 0x16, 0x18, 0x86, 0x0c, 0x75, 0xb9, 0xc7, 0xd5, + 0xef, 0xbc, 0x90, 0x06, 0x3e, 0xc3, 0x89, 0x08, 0x3d, 0x6e, 0x8d, 0xfb, 0xdc, 0x9a, 0xf8, 0x0c, 0xb7, 0x56, 0x22, + 0x89, 0x35, 0xb0, 0xa4, 0xe6, 0x72, 0x94, 0xb0, 0xa3, 0x92, 0xf1, 0x59, 0x19, 0x25, 0x34, 0x86, 0x07, 0xc9, 0xc4, + 0x4c, 0x46, 0x09, 0xda, 0x27, 0xba, 0x08, 0x83, 0x7f, 0x01, 0x66, 0x3f, 0xcd, 0xe1, 0xaf, 0x24, 0xd3, 0xe4, 0x10, + 0x02, 0x42, 0x1c, 0x8e, 0x67, 0x71, 0x38, 0x26, 0x51, 0x72, 0x04, 0x4f, 0xf0, 0x5f, 0x11, 0x8e, 0x49, 0xad, 0xef, + 0x30, 0x52, 0x5d, 0x6e, 0x13, 0x06, 0x70, 0x65, 0xe3, 0xd9, 0x24, 0xb2, 0xd2, 0x5d, 0xf9, 0x78, 0x34, 0x7e, 0x46, + 0xa6, 0x71, 0x28, 0x07, 0x09, 0xa1, 0xe0, 0xdd, 0x1b, 0x96, 0xc3, 0x44, 0xc3, 0xb3, 0x01, 0x9b, 0x57, 0x3a, 0x36, + 0x4f, 0xc2, 0x09, 0x08, 0xc3, 0x84, 0x1c, 0xeb, 0x3d, 0x48, 0x29, 0xfa, 0x3c, 0xc7, 0x7e, 0xea, 0x23, 0x08, 0xb3, + 0xa3, 0x96, 0x8a, 0xaf, 0x00, 0xe8, 0x12, 0x07, 0x87, 0xda, 0x33, 0x5f, 0xcc, 0xc2, 0xd2, 0xa3, 0x52, 0xa6, 0xba, + 0x7d, 0xd1, 0xa0, 0xfc, 0xa6, 0x41, 0xfb, 0x82, 0x0c, 0x26, 0xb4, 0x3c, 0x9a, 0xf0, 0x27, 0x10, 0xc0, 0xa3, 0x11, + 0xf1, 0x4b, 0xe1, 0xc4, 0x40, 0x78, 0x15, 0x64, 0xa0, 0xd2, 0x5a, 0x35, 0x66, 0x64, 0x2b, 0xde, 0x83, 0x30, 0x29, + 0x7b, 0x37, 0x72, 0x9d, 0xa7, 0x10, 0x15, 0x6c, 0x9d, 0x57, 0x7b, 0x97, 0x60, 0xc9, 0x1e, 0x57, 0x10, 0x27, 0x6c, + 0xbd, 0x02, 0xec, 0xdc, 0x47, 0x9b, 0xb2, 0xde, 0x53, 0xdf, 0xed, 0x61, 0xcb, 0xe1, 0x55, 0x25, 0xf7, 0x26, 0xe3, + 0xf1, 0x78, 0xf4, 0x07, 0x1c, 0x1d, 0x40, 0x68, 0x49, 0x64, 0xf8, 0x64, 0x80, 0xc6, 0x5d, 0x57, 0xdc, 0x1b, 0x17, + 0x8a, 0xb2, 0xd2, 0xc9, 0x84, 0x80, 0xf8, 0xd9, 0xf4, 0x0d, 0xf6, 0x15, 0xd7, 0xf1, 0x4f, 0x76, 0x3f, 0x31, 0x2b, + 0x5a, 0xad, 0xd4, 0xd1, 0xbb, 0x93, 0xd3, 0xd7, 0x1f, 0x5e, 0xff, 0xfa, 0xf2, 0xec, 0xf5, 0xdb, 0x57, 0xaf, 0xdf, + 0xbe, 0xfe, 0xf0, 0xaf, 0x07, 0x18, 0x6c, 0xdf, 0x56, 0xc4, 0x8e, 0xbd, 0x77, 0x8f, 0xf1, 0x6a, 0xf1, 0x85, 0xb3, + 0x07, 0xee, 0x16, 0x0b, 0xb0, 0x09, 0x86, 0x5b, 0x10, 0x54, 0x33, 0x1a, 0x95, 0xbe, 0x27, 0x20, 0xa3, 0x51, 0x21, + 0x1b, 0x0f, 0x2b, 0xb6, 0x42, 0x2e, 0xde, 0x31, 0x1c, 0x7c, 0x64, 0x7f, 0x2b, 0xce, 0x84, 0xdb, 0xd1, 0xd6, 0xac, + 0x08, 0xf8, 0x7c, 0xad, 0x45, 0xe5, 0x71, 0x21, 0x6a, 0x6f, 0xdb, 0xe7, 0x90, 0x50, 0x8f, 0xc8, 0x75, 0xf0, 0xbe, + 0x0d, 0xb2, 0xc7, 0x47, 0xde, 0x93, 0xf2, 0x0c, 0xf5, 0x39, 0x1a, 0x3e, 0x6a, 0x3c, 0xa3, 0x13, 0x73, 0x6d, 0x74, + 0xa8, 0x67, 0x05, 0xec, 0x6f, 0x25, 0xc6, 0x86, 0x68, 0x0f, 0x29, 0x62, 0x7d, 0x38, 0xdd, 0xef, 0xee, 0xcd, 0xe8, + 0x7b, 0x38, 0x7e, 0x94, 0x6a, 0x02, 0x69, 0x51, 0xa0, 0x74, 0x65, 0xc8, 0x6d, 0xcf, 0xc2, 0xc2, 0xfc, 0x0c, 0x1b, + 0x04, 0xd0, 0x5e, 0x76, 0x2c, 0x09, 0x34, 0x8b, 0xd7, 0xba, 0xfe, 0x79, 0xf9, 0x32, 0xd1, 0xce, 0x17, 0xdf, 0x42, + 0x88, 0x61, 0xff, 0x8a, 0xd0, 0x98, 0x70, 0x37, 0xc9, 0xee, 0xd2, 0x62, 0xee, 0x55, 0x57, 0x31, 0x1e, 0x77, 0xf7, + 0x5c, 0x29, 0x9a, 0xb7, 0x2e, 0xb0, 0x07, 0x6a, 0x5e, 0xc7, 0x4b, 0x16, 0x02, 0x36, 0xe3, 0xbe, 0x5d, 0x24, 0xce, + 0xef, 0x9d, 0x4e, 0xc8, 0xfe, 0xc1, 0x94, 0x0f, 0x59, 0x49, 0xc5, 0x80, 0x95, 0xf5, 0x0e, 0x35, 0xe7, 0x6d, 0x42, + 0x2e, 0x76, 0x69, 0xb8, 0x18, 0xf2, 0x87, 0x2e, 0x49, 0x1f, 0x78, 0xc3, 0xa1, 0xda, 0x36, 0x17, 0x43, 0x9a, 0x72, + 0xba, 0x4b, 0x65, 0x40, 0x88, 0x74, 0x15, 0x57, 0xa4, 0xd6, 0x47, 0x55, 0xea, 0x24, 0x1d, 0xd7, 0xd9, 0xe6, 0x33, + 0x97, 0x6c, 0x75, 0xbb, 0xf6, 0xaf, 0xd5, 0xed, 0x0b, 0x33, 0x90, 0xbf, 0x3f, 0x11, 0xd5, 0xc4, 0x40, 0x74, 0x01, + 0x15, 0xfc, 0x13, 0xbc, 0x3c, 0x79, 0xa4, 0x15, 0xa0, 0xf7, 0x9d, 0x1d, 0x5d, 0x7b, 0xbc, 0x31, 0x8b, 0xad, 0x25, + 0xce, 0x59, 0xe5, 0x3b, 0xcb, 0xab, 0xb2, 0x15, 0xba, 0x8e, 0x60, 0xbf, 0x87, 0x1d, 0x7d, 0xf7, 0xb6, 0x01, 0x10, + 0xa5, 0xb0, 0x72, 0x67, 0xbf, 0xf0, 0xce, 0x7e, 0x61, 0xcf, 0x7e, 0xbb, 0x09, 0x94, 0x0f, 0x2b, 0xb4, 0xec, 0x95, + 0x14, 0x95, 0x69, 0xf2, 0xb8, 0xa9, 0xcb, 0x42, 0x5a, 0xcc, 0xf7, 0x2d, 0xed, 0x7a, 0x3a, 0xa6, 0x12, 0xd5, 0x23, + 0x3f, 0x60, 0xab, 0xf6, 0x4b, 0xf2, 0xf0, 0x3d, 0xf3, 0x7f, 0xf6, 0x06, 0x79, 0xdf, 0xdd, 0xee, 0xff, 0xe6, 0x42, + 0x07, 0xb7, 0xb5, 0x54, 0x78, 0xea, 0xea, 0xb8, 0xc0, 0xbb, 0x5a, 0xfa, 0xf0, 0x5d, 0xed, 0x5d, 0xa6, 0x97, 0x5d, + 0x05, 0xa8, 0x41, 0x62, 0x7d, 0xc5, 0x8b, 0x2c, 0xa9, 0xad, 0x42, 0xe3, 0x84, 0x43, 0x68, 0x0f, 0xef, 0xe0, 0x02, + 0x39, 0x2c, 0x21, 0xf4, 0x63, 0x65, 0x04, 0x80, 0x3e, 0x8b, 0x7d, 0xc2, 0xc3, 0x8c, 0x0c, 0x7c, 0x89, 0x5f, 0x29, + 0x7d, 0x71, 0xf1, 0xfe, 0x4e, 0x66, 0x82, 0x5e, 0x25, 0x36, 0xbb, 0x94, 0xed, 0x98, 0x1f, 0xfe, 0x17, 0x18, 0x0d, + 0xc2, 0x6b, 0x4b, 0xb6, 0x2f, 0x3a, 0x66, 0xb9, 0x82, 0xa3, 0xb6, 0x74, 0x65, 0x96, 0xad, 0xeb, 0x67, 0x35, 0xcc, + 0xf4, 0x99, 0x72, 0x02, 0xb2, 0x2f, 0xe4, 0xee, 0xa7, 0xba, 0x62, 0x41, 0x8e, 0x26, 0xe3, 0x29, 0x11, 0x83, 0x41, + 0x2b, 0xf9, 0x10, 0x93, 0x87, 0xc3, 0x1d, 0xe6, 0x52, 0xe8, 0x7e, 0x78, 0x7d, 0x80, 0xfa, 0x1a, 0x5b, 0x92, 0x6c, + 0x2a, 0xf6, 0x17, 0x98, 0xc5, 0x02, 0x71, 0x74, 0xf0, 0x8b, 0xf3, 0x05, 0x80, 0x2c, 0xc3, 0x32, 0xd3, 0xc2, 0xa2, + 0x32, 0x55, 0x3e, 0xb2, 0x05, 0x93, 0x87, 0xe3, 0x99, 0xdf, 0x73, 0xc7, 0xe0, 0x10, 0x12, 0x4d, 0xac, 0xf1, 0x8b, + 0x9f, 0x05, 0xe3, 0x38, 0x94, 0x47, 0xb2, 0xf1, 0x5d, 0x49, 0xa2, 0xb1, 0x31, 0x55, 0xd6, 0x57, 0x89, 0x6a, 0x98, + 0x90, 0xc7, 0x05, 0xd9, 0x2f, 0xe8, 0xd2, 0x1f, 0x4b, 0x4c, 0xdf, 0x8f, 0xf7, 0x27, 0x63, 0xf2, 0x38, 0x7e, 0x3c, + 0x31, 0x70, 0xc3, 0x7e, 0x8e, 0x7c, 0xb8, 0x24, 0xfb, 0xcd, 0x2a, 0xc1, 0x14, 0xd5, 0xf4, 0xcc, 0xaf, 0x24, 0x19, + 0x2c, 0x07, 0xe9, 0xe3, 0x56, 0x5e, 0xac, 0x55, 0x8f, 0xf7, 0xfa, 0x90, 0x4f, 0x89, 0x68, 0xdc, 0x18, 0xd6, 0xf4, + 0x2a, 0xfe, 0x53, 0x16, 0x51, 0x29, 0x01, 0x91, 0x10, 0xd4, 0xdb, 0xd9, 0x45, 0x96, 0xc4, 0x22, 0x8d, 0xd2, 0x9a, + 0xd0, 0xf4, 0x88, 0x4d, 0xc6, 0xb3, 0x94, 0xa5, 0x87, 0x93, 0x67, 0xb3, 0xc9, 0xb3, 0xe8, 0x60, 0x1c, 0xa5, 0x83, + 0x01, 0x24, 0x1f, 0x8c, 0xc1, 0xc5, 0x0e, 0x7e, 0xb3, 0x03, 0x18, 0xba, 0x23, 0x64, 0x09, 0x0b, 0x68, 0xda, 0x97, + 0x35, 0x49, 0x0f, 0xe7, 0x85, 0xea, 0x49, 0x7c, 0x4b, 0xd7, 0x9e, 0x83, 0x8b, 0xdf, 0xc2, 0x0b, 0xd7, 0xc2, 0x8b, + 0xdd, 0x16, 0x0a, 0x13, 0x37, 0x45, 0xfe, 0xff, 0xb8, 0x61, 0xdc, 0x77, 0x97, 0x30, 0x8b, 0xeb, 0x3a, 0x1b, 0xad, + 0x0a, 0x59, 0x49, 0xb8, 0x4d, 0x28, 0x51, 0xd8, 0x28, 0x5e, 0xad, 0x72, 0xed, 0x22, 0x36, 0xaf, 0x28, 0x80, 0xbb, + 0x40, 0x9c, 0x62, 0x60, 0xa1, 0x8d, 0x81, 0xdc, 0x27, 0x5e, 0x48, 0x66, 0xd5, 0x3e, 0xe6, 0x1e, 0xf9, 0x67, 0x08, + 0xc6, 0xa8, 0xe2, 0x68, 0x3c, 0x53, 0x58, 0x17, 0x9f, 0x93, 0xf7, 0xfe, 0x1b, 0x47, 0x91, 0x3d, 0x9a, 0x41, 0x4f, + 0x10, 0x39, 0x8f, 0x38, 0x7b, 0x32, 0x79, 0x19, 0xb8, 0x9f, 0xc1, 0x4a, 0x7f, 0xdd, 0x6d, 0xc6, 0xda, 0xf6, 0xe8, + 0x5e, 0x18, 0xa1, 0xe8, 0x27, 0x7c, 0x67, 0xea, 0x05, 0x5c, 0x42, 0x35, 0xb0, 0xeb, 0xcb, 0x4b, 0x5e, 0x02, 0x88, + 0x50, 0x26, 0xfa, 0xfd, 0xde, 0x9f, 0x06, 0x9a, 0xb4, 0xe4, 0xc5, 0x9b, 0x4c, 0x58, 0x67, 0x1c, 0x68, 0x2a, 0x50, + 0xff, 0x8f, 0x95, 0x7d, 0xa6, 0x63, 0x32, 0xf3, 0x1f, 0x87, 0x13, 0x12, 0x35, 0x5f, 0x93, 0xcf, 0x9c, 0xa6, 0x9f, + 0xb9, 0xa2, 0xfd, 0x07, 0x32, 0x73, 0xc3, 0x21, 0x43, 0xfd, 0xa5, 0x63, 0x9e, 0x8c, 0x5e, 0x27, 0x66, 0x47, 0x82, + 0x55, 0x33, 0x88, 0xc2, 0x5e, 0xc0, 0x83, 0xba, 0x96, 0xc5, 0x53, 0x98, 0x7d, 0x50, 0x23, 0x8a, 0x43, 0x36, 0x9e, + 0x85, 0x32, 0x9c, 0x80, 0x7d, 0xef, 0x64, 0x0c, 0xf7, 0x01, 0x19, 0x7e, 0xac, 0x42, 0xec, 0x1c, 0xa4, 0x7d, 0xac, + 0x50, 0x31, 0x01, 0x10, 0x81, 0x90, 0xb7, 0xdf, 0x97, 0x2a, 0x09, 0x5f, 0x97, 0x98, 0x52, 0xa8, 0x0f, 0xfe, 0x13, + 0xa9, 0xba, 0x63, 0xfa, 0xd5, 0xfa, 0xf1, 0x67, 0x42, 0xf1, 0xe9, 0x2e, 0x25, 0xbe, 0x85, 0xe0, 0xce, 0x12, 0x74, + 0x10, 0x15, 0x9a, 0xb1, 0x3d, 0xcc, 0xef, 0x8a, 0xfb, 0xf9, 0x5d, 0xf1, 0xff, 0x8e, 0xdf, 0x15, 0x0f, 0x31, 0x86, + 0x95, 0x85, 0x86, 0x9f, 0x05, 0xe3, 0x20, 0xfa, 0xcf, 0xf9, 0xc4, 0x7b, 0x79, 0xea, 0xab, 0x4c, 0x4c, 0xef, 0x61, + 0x9a, 0x7d, 0x82, 0x82, 0xb0, 0x8a, 0xbb, 0xf4, 0x64, 0x5d, 0xd9, 0x5b, 0x2b, 0x19, 0x62, 0x9e, 0x07, 0x58, 0xa3, + 0xb0, 0xf2, 0x80, 0xee, 0x51, 0xb5, 0x41, 0x9c, 0x08, 0x1e, 0xc6, 0xcc, 0x4a, 0xdf, 0xb7, 0x5b, 0xa3, 0xc2, 0x7c, + 0x90, 0x8b, 0x82, 0xec, 0xe6, 0xe3, 0xd9, 0x38, 0x0a, 0xb1, 0x01, 0xff, 0x31, 0x63, 0xd5, 0x90, 0xcd, 0x77, 0x32, + 0x52, 0x3b, 0x26, 0x4f, 0x93, 0x5d, 0xd2, 0x3b, 0xe0, 0x1d, 0xf2, 0x73, 0x70, 0x67, 0x93, 0x86, 0xdf, 0x92, 0x97, + 0x71, 0x91, 0x55, 0xcb, 0xab, 0x2c, 0x41, 0xa6, 0x0b, 0x5e, 0x7c, 0x31, 0xd3, 0xe5, 0x7d, 0xac, 0x0f, 0x18, 0x4f, + 0x29, 0x5e, 0x37, 0x44, 0xe9, 0xeb, 0x96, 0x67, 0x85, 0xba, 0x3c, 0xa9, 0x98, 0xed, 0x59, 0x09, 0x4e, 0xa7, 0x60, + 0x82, 0xaf, 0x7f, 0xba, 0xde, 0x27, 0x80, 0x0b, 0x0a, 0x35, 0xa7, 0x85, 0x5c, 0x19, 0x2c, 0x27, 0x0b, 0xdd, 0x09, + 0x98, 0xa1, 0x52, 0xe0, 0x05, 0x0a, 0xfe, 0xa2, 0x81, 0x11, 0x7d, 0xe5, 0x7e, 0x93, 0x81, 0x41, 0xba, 0x34, 0x27, + 0xc2, 0xd8, 0x71, 0x3b, 0x45, 0xda, 0x8a, 0x72, 0xc6, 0xd9, 0x7b, 0x75, 0xa5, 0x00, 0x03, 0xbc, 0xcd, 0x4d, 0x74, + 0x9e, 0xa0, 0xd7, 0x82, 0xd2, 0x79, 0x03, 0x77, 0xb3, 0x8c, 0x8c, 0x70, 0xf1, 0x71, 0xe5, 0xb1, 0xe0, 0x9e, 0xfd, + 0x42, 0x2c, 0x8d, 0x66, 0x1a, 0x8c, 0xd9, 0xbc, 0x60, 0x81, 0x42, 0x05, 0x0a, 0x2c, 0x67, 0xda, 0xd2, 0xb4, 0x1a, + 0xf2, 0xfd, 0x03, 0xb4, 0x36, 0xad, 0x06, 0x7c, 0xff, 0xa0, 0x8e, 0xb2, 0x43, 0xc8, 0x72, 0xe4, 0x67, 0x50, 0xaf, + 0xeb, 0xc8, 0xa4, 0x98, 0xec, 0x7e, 0x7d, 0xa9, 0x3f, 0xaa, 0x1b, 0x70, 0xfd, 0x00, 0x04, 0xb0, 0x01, 0x38, 0x04, + 0xaa, 0xc1, 0xd2, 0x88, 0x60, 0x51, 0xa6, 0xd0, 0xbe, 0x86, 0xde, 0x1b, 0x0d, 0xff, 0x05, 0xee, 0x22, 0x72, 0xe5, + 0x7f, 0x82, 0xc0, 0x5f, 0x51, 0xa6, 0x95, 0x29, 0xfe, 0x27, 0x5a, 0xbd, 0x42, 0x39, 0x6b, 0x5a, 0xf3, 0x41, 0xb4, + 0x26, 0x42, 0x35, 0x63, 0x08, 0xfe, 0xad, 0x2c, 0xd3, 0x96, 0xaa, 0x4a, 0x7d, 0x68, 0xbc, 0xd6, 0x0a, 0x67, 0xf9, + 0x38, 0xf2, 0x5e, 0x63, 0xe8, 0xd8, 0xc4, 0x59, 0xca, 0xa9, 0xd4, 0xd9, 0xa7, 0x7d, 0x19, 0x39, 0xc0, 0xe9, 0x84, + 0x8d, 0xa7, 0xc9, 0xa1, 0x9c, 0x26, 0x0e, 0x32, 0x3f, 0x67, 0x18, 0x59, 0xd5, 0x80, 0xb0, 0x28, 0x1b, 0x4a, 0x5b, + 0x80, 0x49, 0x4e, 0x08, 0x99, 0x62, 0x28, 0x8a, 0x7c, 0xa4, 0xfb, 0x61, 0xbd, 0x59, 0xdd, 0x17, 0xef, 0x34, 0xc0, + 0x69, 0x98, 0x40, 0x20, 0xf0, 0x22, 0xbe, 0xc9, 0xc4, 0x25, 0x78, 0x0c, 0x0f, 0xe0, 0x4b, 0x70, 0x93, 0x4b, 0xd9, + 0x6f, 0x55, 0x98, 0xe3, 0xda, 0x02, 0x06, 0x0d, 0x56, 0x0f, 0xa2, 0xc3, 0xa5, 0xb4, 0xd9, 0x55, 0x80, 0xd8, 0x98, + 0x42, 0x2c, 0x0b, 0xb6, 0xb6, 0xec, 0xd9, 0xcf, 0xaa, 0x69, 0x68, 0x9d, 0x70, 0x2c, 0x2e, 0x73, 0x88, 0xa2, 0x32, + 0x88, 0xc1, 0x1d, 0xc9, 0xe3, 0xf3, 0x1e, 0x89, 0xf0, 0x82, 0x80, 0x5b, 0x59, 0x2c, 0xc3, 0x15, 0x5d, 0x8e, 0x6e, + 0xe9, 0x7a, 0x74, 0x43, 0xc7, 0x74, 0xf2, 0xf7, 0x31, 0x58, 0x64, 0xeb, 0xd4, 0x3b, 0xba, 0x1e, 0x2d, 0xe9, 0xb7, + 0x63, 0x7a, 0xf0, 0x37, 0x30, 0xe1, 0xc3, 0xc3, 0x84, 0x5e, 0x80, 0x63, 0x17, 0xa9, 0xd1, 0x53, 0xd3, 0x37, 0x38, + 0xac, 0x46, 0xf9, 0x90, 0x8f, 0x72, 0xca, 0x47, 0xc5, 0xb0, 0x1a, 0x81, 0xa7, 0x63, 0x35, 0xe4, 0xa3, 0x8a, 0xf2, + 0xd1, 0xf9, 0xb0, 0x1a, 0x9d, 0x93, 0x66, 0xd3, 0x5f, 0x57, 0xfc, 0xaa, 0x64, 0x29, 0x6c, 0x0b, 0x58, 0xbe, 0x9e, + 0x57, 0x54, 0xea, 0xaf, 0x6a, 0x73, 0x32, 0x5b, 0xce, 0xde, 0x5e, 0x77, 0x39, 0xb1, 0x78, 0xdc, 0x36, 0x1d, 0xae, + 0xbe, 0x9c, 0xa8, 0x93, 0x5e, 0x21, 0x3f, 0x8c, 0xa7, 0x42, 0x9d, 0x43, 0x60, 0x26, 0x31, 0x0b, 0x63, 0x86, 0xcd, + 0xd4, 0x69, 0xa0, 0xc0, 0xc9, 0x46, 0x9e, 0x8b, 0x62, 0x36, 0xca, 0x29, 0xbc, 0x8f, 0x09, 0x89, 0x04, 0x9c, 0x55, + 0x47, 0xd5, 0xa8, 0x80, 0x98, 0x23, 0x2c, 0xc4, 0x47, 0xe8, 0x97, 0xfa, 0xc8, 0x43, 0x02, 0xcf, 0xb0, 0xaf, 0xc5, + 0x20, 0x86, 0x23, 0xde, 0x56, 0x56, 0xcd, 0xc2, 0x04, 0x2a, 0xab, 0x86, 0xa5, 0xa9, 0xac, 0xa0, 0xd9, 0xa8, 0xf2, + 0x2b, 0xab, 0x70, 0x8c, 0x12, 0x42, 0xa2, 0x52, 0x57, 0x06, 0xea, 0x93, 0x84, 0x85, 0xa5, 0xae, 0xec, 0x5c, 0x7d, + 0x74, 0xee, 0x57, 0x76, 0x0e, 0x2e, 0xa4, 0x83, 0xc4, 0xbf, 0x4a, 0xe5, 0x69, 0xfb, 0x3a, 0xd8, 0x58, 0x55, 0x74, + 0xc3, 0x6f, 0xab, 0x22, 0x8e, 0x4a, 0xea, 0x62, 0x40, 0xe3, 0xc2, 0x88, 0x24, 0xd5, 0x6b, 0x14, 0xfc, 0x21, 0x41, + 0x54, 0x1a, 0x83, 0x57, 0x67, 0xd2, 0xb5, 0x52, 0x2b, 0x2a, 0x06, 0xe5, 0xa0, 0x80, 0xfb, 0x53, 0xde, 0x5a, 0x48, + 0x3f, 0x43, 0x44, 0x65, 0x28, 0x6f, 0xf0, 0x4f, 0x0c, 0x9e, 0xcc, 0x56, 0x69, 0x98, 0x8c, 0xee, 0x68, 0x3c, 0x5a, + 0x22, 0x1c, 0x0c, 0x5b, 0xa7, 0x0a, 0x6f, 0xfd, 0x02, 0xd2, 0x6f, 0x69, 0x3c, 0xba, 0xa1, 0xa9, 0xb5, 0x39, 0x35, + 0x50, 0x57, 0xbd, 0x31, 0xbd, 0x8d, 0xe0, 0xf5, 0x5d, 0xb4, 0xa4, 0xb0, 0x95, 0x8e, 0xf3, 0xec, 0x52, 0x44, 0x29, + 0x45, 0x04, 0xc2, 0x35, 0x22, 0x07, 0x2e, 0x35, 0xda, 0xe0, 0x7a, 0x00, 0x65, 0x68, 0xb8, 0xc0, 0xe5, 0x20, 0x1e, + 0x2d, 0x3d, 0x32, 0xb5, 0xd4, 0x17, 0x59, 0x84, 0x8f, 0x76, 0x36, 0x5a, 0x8a, 0x67, 0xc4, 0xc2, 0xb8, 0x82, 0x21, + 0xd4, 0x85, 0x95, 0xa6, 0x20, 0xe9, 0x02, 0x47, 0xf6, 0xc2, 0xb8, 0x0a, 0x37, 0x60, 0x5a, 0x74, 0x07, 0xe6, 0x51, + 0xa0, 0x70, 0x70, 0x09, 0xd2, 0x4f, 0x28, 0xdb, 0x39, 0x4a, 0x93, 0xc3, 0x9b, 0xa0, 0x74, 0x67, 0x82, 0x90, 0x76, + 0x75, 0x93, 0x2d, 0xe9, 0x1b, 0x6c, 0xef, 0xd0, 0xa9, 0xa8, 0xa0, 0xfa, 0xdc, 0x82, 0xc9, 0x92, 0x0d, 0xc2, 0x96, + 0x30, 0x3d, 0xd3, 0x6b, 0xc0, 0x9e, 0xde, 0x3f, 0xd8, 0x99, 0xef, 0x62, 0xf6, 0x69, 0xbf, 0x8c, 0xc6, 0xca, 0x82, + 0x37, 0xb7, 0xc4, 0x6e, 0xc9, 0xc6, 0xd3, 0xe5, 0x61, 0x39, 0x5d, 0x22, 0xb1, 0x33, 0x74, 0x8b, 0xf1, 0xf9, 0x72, + 0x41, 0x13, 0x3c, 0xdb, 0x58, 0x35, 0x5f, 0x1a, 0xb4, 0x94, 0x94, 0xe1, 0x7a, 0x5b, 0xa2, 0xff, 0xbf, 0xba, 0xf8, + 0xa5, 0x00, 0x2f, 0xc1, 0x58, 0x00, 0x08, 0xf7, 0x60, 0x5a, 0x90, 0xda, 0x28, 0x1b, 0xcb, 0x34, 0x4c, 0x71, 0x11, + 0x98, 0x94, 0x7e, 0x3f, 0xcc, 0x59, 0x4a, 0x3c, 0xe8, 0x50, 0x77, 0x6a, 0xa7, 0xbe, 0x10, 0x04, 0x78, 0x24, 0x75, + 0x8e, 0x4d, 0xfe, 0x3e, 0x9e, 0x05, 0x6a, 0x20, 0x82, 0x28, 0x3b, 0xc4, 0x47, 0x0c, 0x5c, 0x14, 0xe9, 0xb8, 0x9d, + 0xae, 0x88, 0x8b, 0xdd, 0x63, 0x16, 0xe2, 0x24, 0x61, 0xae, 0x59, 0x36, 0x64, 0x55, 0x84, 0x09, 0xba, 0x30, 0x30, + 0xcb, 0x1b, 0xb2, 0x6a, 0xff, 0x00, 0x22, 0xb5, 0xda, 0x32, 0x56, 0x5d, 0x65, 0x7c, 0x0b, 0x40, 0xd6, 0x8c, 0xb1, + 0x83, 0xbf, 0x8d, 0x67, 0xea, 0x9b, 0x28, 0xe4, 0x47, 0x07, 0x7f, 0x83, 0xe4, 0xc3, 0x6f, 0x91, 0x99, 0x83, 0xe4, + 0x46, 0x41, 0x97, 0xcd, 0x59, 0xd7, 0x50, 0x9a, 0xb8, 0xf6, 0x4a, 0xbd, 0xf6, 0xa4, 0x59, 0x7b, 0x05, 0xba, 0x53, + 0x1b, 0xde, 0x43, 0xd9, 0xce, 0x82, 0x09, 0x3a, 0x9a, 0xdd, 0x81, 0x0e, 0xde, 0x29, 0x82, 0x5e, 0x26, 0xa1, 0xf1, + 0x08, 0x55, 0x46, 0xbd, 0x18, 0x0f, 0xaa, 0x93, 0x75, 0xc9, 0x3c, 0x03, 0xe6, 0xd8, 0x9e, 0x43, 0x62, 0x98, 0xab, + 0x83, 0x3a, 0x65, 0xe5, 0x30, 0xc7, 0x03, 0x78, 0xcd, 0xe4, 0x50, 0x0c, 0x72, 0x8d, 0xf2, 0x7d, 0xc1, 0x8a, 0x61, + 0x39, 0xc8, 0x35, 0x37, 0x33, 0x6d, 0xc6, 0xa6, 0x4d, 0x74, 0x78, 0xe6, 0x15, 0x3b, 0x5a, 0xf5, 0x80, 0x8f, 0x05, + 0x4f, 0x66, 0xdf, 0xf3, 0xf1, 0x0d, 0x70, 0x32, 0x9b, 0xdb, 0x68, 0x49, 0xef, 0xa2, 0x94, 0xde, 0x44, 0x6b, 0xba, + 0x8c, 0x2e, 0x8c, 0x89, 0x71, 0x52, 0xc3, 0x39, 0x00, 0xad, 0x02, 0x48, 0x3c, 0xf5, 0xeb, 0x3d, 0x4f, 0xaa, 0x70, + 0x49, 0x53, 0x70, 0x1b, 0xf6, 0xed, 0x33, 0xaf, 0x7c, 0x89, 0xd4, 0x06, 0x31, 0xd6, 0xac, 0xa1, 0xe2, 0xc6, 0x5b, + 0xf7, 0x91, 0xa8, 0x61, 0xe7, 0xba, 0xd8, 0x44, 0xd5, 0x70, 0x32, 0x2d, 0x01, 0xb1, 0xb5, 0x1c, 0x0e, 0xdd, 0x11, + 0xb2, 0x7b, 0xfc, 0xe8, 0x40, 0xcf, 0x3d, 0x69, 0xb1, 0x6d, 0x5b, 0xfe, 0xc0, 0x10, 0xa6, 0xf4, 0xf3, 0x47, 0x3e, + 0x20, 0x56, 0x5c, 0xc2, 0xd9, 0x08, 0xd4, 0xd1, 0x0a, 0x9d, 0x7e, 0xab, 0xc2, 0x42, 0x1f, 0xe0, 0x9b, 0xdb, 0x28, + 0xa1, 0x77, 0x51, 0xee, 0x91, 0xb5, 0x65, 0xcd, 0xe4, 0xf4, 0x2c, 0x0b, 0x79, 0xfb, 0x40, 0x2f, 0x17, 0x00, 0xa2, + 0x35, 0x88, 0x7d, 0xa9, 0xeb, 0x01, 0x38, 0x0d, 0xa1, 0x49, 0x68, 0x04, 0x57, 0x15, 0x84, 0x11, 0x70, 0x25, 0xe1, + 0x6f, 0x30, 0x51, 0x81, 0x2f, 0xc0, 0x45, 0x26, 0x4d, 0x73, 0x1e, 0xd4, 0xfe, 0x48, 0x9e, 0x16, 0x6d, 0x6f, 0x57, + 0x18, 0x4d, 0x30, 0xf6, 0x44, 0xfb, 0x3c, 0x52, 0x8e, 0xe2, 0x22, 0x09, 0xb3, 0xd1, 0xad, 0x3a, 0xcf, 0x69, 0x36, + 0xba, 0xd3, 0xbf, 0x2a, 0x3a, 0xa6, 0xdf, 0xe9, 0x80, 0x36, 0x4a, 0xfa, 0xd6, 0x71, 0x36, 0xa0, 0xf5, 0x62, 0x69, + 0xfc, 0xaf, 0xe5, 0xe8, 0x96, 0xca, 0xd1, 0x9d, 0x6f, 0x49, 0x35, 0x99, 0x16, 0x87, 0x02, 0x0d, 0xa9, 0x3a, 0xbf, + 0x2f, 0x80, 0x9f, 0x2b, 0x8d, 0xef, 0xb4, 0xf9, 0xde, 0x6b, 0xff, 0x79, 0x27, 0x4f, 0xa0, 0x58, 0xa2, 0x82, 0x55, + 0x23, 0xb0, 0x63, 0x5f, 0xe7, 0x71, 0x61, 0x46, 0x29, 0xa6, 0xd6, 0xa4, 0x1f, 0x03, 0x57, 0x4c, 0x7b, 0x05, 0xb8, + 0x5a, 0x82, 0x93, 0x00, 0xc4, 0xd0, 0x84, 0x3d, 0x3b, 0x86, 0xa8, 0xe7, 0xc6, 0x31, 0x4a, 0x36, 0xdc, 0x03, 0x62, + 0x2d, 0xf3, 0x56, 0x2e, 0x01, 0x09, 0xbc, 0xf5, 0x30, 0x29, 0x00, 0x63, 0xb0, 0x5c, 0x12, 0x9d, 0xc7, 0x43, 0x9f, + 0x50, 0x2f, 0x34, 0xea, 0x84, 0x6c, 0x6c, 0x09, 0x1c, 0x7f, 0x58, 0x1f, 0x02, 0xc1, 0xab, 0x3c, 0xd7, 0x5f, 0x69, + 0x5d, 0x7f, 0xa9, 0xf4, 0xdc, 0xb1, 0x5c, 0xd7, 0xcf, 0xda, 0xd4, 0xe8, 0x15, 0x58, 0xf8, 0x6e, 0x94, 0x79, 0x24, + 0xb7, 0x08, 0xa9, 0x0a, 0xac, 0xd4, 0x2d, 0x24, 0x98, 0x7f, 0x25, 0x67, 0xab, 0x32, 0x5f, 0x3d, 0xf2, 0xa0, 0x9c, + 0x4d, 0x4f, 0x7f, 0x43, 0x82, 0x76, 0xd7, 0x91, 0xe6, 0xf1, 0x16, 0x1d, 0x3e, 0xbb, 0xd6, 0x12, 0x73, 0x27, 0x51, + 0xf1, 0x7c, 0x0a, 0xd8, 0xea, 0x45, 0x76, 0xa5, 0x7c, 0xac, 0x76, 0x71, 0xfc, 0xcc, 0xf9, 0x93, 0x54, 0xe1, 0x5a, + 0x34, 0x94, 0x20, 0xe0, 0xcd, 0x61, 0xec, 0x0a, 0x55, 0x40, 0x43, 0x73, 0x03, 0xc7, 0xb9, 0x1a, 0x56, 0x9a, 0x80, + 0x69, 0x29, 0x8f, 0x0e, 0x70, 0x68, 0xf2, 0xa8, 0xdd, 0x34, 0xac, 0x0c, 0x5d, 0x6b, 0xf4, 0xb9, 0xad, 0x74, 0xc6, + 0x9b, 0x0d, 0xdf, 0x3f, 0x18, 0x54, 0xf8, 0x93, 0x34, 0x47, 0xa3, 0x9d, 0x1b, 0xee, 0x34, 0x02, 0x33, 0x57, 0x72, + 0x45, 0x76, 0x47, 0xc9, 0xcb, 0xef, 0xe9, 0x85, 0x05, 0xf4, 0xe7, 0x3f, 0x17, 0x13, 0x4e, 0x5a, 0x62, 0x42, 0xb4, + 0x74, 0xd0, 0xa2, 0x83, 0x1d, 0xe5, 0x95, 0x7d, 0x89, 0x97, 0xce, 0xf1, 0xbf, 0xaf, 0xc7, 0xda, 0x55, 0x20, 0xb4, + 0x3a, 0xb9, 0xdf, 0x9e, 0x2c, 0x10, 0x35, 0xa0, 0x9a, 0x5d, 0x95, 0xa3, 0x4c, 0x3b, 0x2b, 0xb2, 0x69, 0xc8, 0x5c, + 0x77, 0xb3, 0x34, 0x6c, 0x26, 0x3b, 0x16, 0x96, 0x19, 0x06, 0x6b, 0xa7, 0x8a, 0x3e, 0x07, 0x2d, 0x3f, 0x82, 0x17, + 0x4d, 0xe5, 0x99, 0xcf, 0x66, 0x19, 0xf1, 0x02, 0x9d, 0x73, 0x2a, 0x16, 0x4d, 0xe9, 0x58, 0xb9, 0xdd, 0x96, 0x68, + 0x2c, 0x51, 0x46, 0x41, 0x50, 0xdb, 0x20, 0xec, 0xba, 0x74, 0x4f, 0xfa, 0xb4, 0x8b, 0x4f, 0x2b, 0xd0, 0xf7, 0xf8, + 0x3e, 0x03, 0x89, 0xa9, 0x27, 0x79, 0xa8, 0x1a, 0xcd, 0xd1, 0xc9, 0xb3, 0x38, 0xd5, 0xf8, 0xfc, 0x4a, 0x76, 0xd6, + 0xbc, 0x5b, 0x8d, 0x29, 0xfe, 0x23, 0x75, 0xfb, 0xce, 0x65, 0x68, 0xa2, 0xbf, 0x96, 0x07, 0x2d, 0x85, 0x05, 0xc7, + 0x6d, 0xe3, 0xaf, 0xdf, 0x66, 0x0e, 0x31, 0x2c, 0x5d, 0x0e, 0x6f, 0x42, 0x87, 0xee, 0xae, 0xb2, 0x33, 0xd7, 0x07, + 0xd4, 0xa9, 0x8b, 0x75, 0x1b, 0x50, 0xb2, 0xe4, 0xdd, 0x3a, 0x3d, 0xb1, 0xd2, 0x77, 0xfb, 0xe1, 0xce, 0x3c, 0x6a, + 0x76, 0x77, 0xbb, 0x9d, 0x90, 0xb6, 0x7d, 0x30, 0xde, 0x97, 0xb0, 0x10, 0xe7, 0x1d, 0xb6, 0xf7, 0x73, 0x58, 0x3d, + 0xe6, 0x83, 0xdf, 0x71, 0x9c, 0x61, 0xf4, 0x33, 0x65, 0xe8, 0xf3, 0xaa, 0x90, 0x57, 0xaa, 0x53, 0xbe, 0xd0, 0xad, + 0x65, 0xea, 0xfd, 0x36, 0x7e, 0xdb, 0x0a, 0x10, 0xe3, 0x75, 0xc5, 0x4a, 0xf1, 0x86, 0x56, 0x18, 0xd7, 0xc0, 0x6d, + 0x72, 0xa8, 0xa5, 0x5a, 0x20, 0xea, 0xf2, 0x93, 0xc7, 0x3c, 0x32, 0xea, 0x4c, 0xf8, 0xee, 0x31, 0xf7, 0xa5, 0x6b, + 0xbb, 0x4d, 0xfc, 0x5c, 0xd3, 0xf6, 0x77, 0x07, 0xba, 0xa3, 0x75, 0x0f, 0x37, 0xcf, 0xe6, 0xe7, 0x91, 0xf9, 0x62, + 0x80, 0xcd, 0xda, 0x65, 0x5c, 0x76, 0x0c, 0xf7, 0xbd, 0xe9, 0xc1, 0x58, 0x40, 0x20, 0x31, 0x43, 0x2f, 0x03, 0x17, + 0xb8, 0xc0, 0x5d, 0x61, 0xc0, 0x10, 0xd7, 0xb4, 0xe4, 0x4c, 0x5b, 0xd9, 0xfa, 0xc8, 0xdb, 0xa8, 0x10, 0xac, 0xeb, + 0x8e, 0x9b, 0x24, 0x87, 0xe0, 0x84, 0x2d, 0xf7, 0xbe, 0xf6, 0xda, 0x19, 0xfe, 0x73, 0x20, 0x9c, 0x5b, 0xa2, 0x67, + 0xd4, 0xf6, 0x58, 0xab, 0x7b, 0x0d, 0xaf, 0x72, 0x17, 0x79, 0xd6, 0x6f, 0xe6, 0xa5, 0x61, 0x5f, 0xf0, 0x5a, 0x0a, + 0x0e, 0x8d, 0xed, 0x56, 0xb8, 0xc5, 0xe2, 0x1d, 0xad, 0x56, 0xd6, 0xda, 0x6a, 0xaf, 0x95, 0x8a, 0xde, 0xbf, 0xe6, + 0x38, 0x71, 0x96, 0xc2, 0xf6, 0xc3, 0x87, 0x0b, 0x76, 0x4d, 0x00, 0x83, 0x16, 0x93, 0x05, 0x4a, 0x50, 0xc9, 0x5a, + 0xd5, 0x6e, 0xa7, 0xc4, 0x2f, 0xf7, 0x8b, 0x2e, 0xb3, 0x9d, 0xc7, 0xaf, 0x9b, 0xb4, 0xcf, 0x7c, 0x8e, 0x7e, 0x98, + 0xdf, 0x59, 0x27, 0x25, 0x67, 0x18, 0xd7, 0xf2, 0xff, 0xab, 0xe8, 0x65, 0x91, 0xa5, 0xd1, 0xc6, 0xf0, 0x60, 0x36, + 0xd4, 0xa6, 0x0f, 0x8d, 0x51, 0xb9, 0x65, 0xa3, 0x88, 0x68, 0x75, 0x0b, 0x82, 0x19, 0xc5, 0x7d, 0x89, 0x36, 0xaf, + 0x54, 0x59, 0x78, 0x87, 0xcf, 0x6c, 0xf4, 0x86, 0xed, 0x09, 0xa1, 0x7c, 0xf7, 0xb4, 0x30, 0xab, 0x96, 0x8a, 0x06, + 0xdb, 0x25, 0xbc, 0x8b, 0x51, 0xa5, 0x9f, 0x30, 0xd9, 0xb2, 0x60, 0xaa, 0xff, 0xdf, 0x17, 0x59, 0xda, 0xa6, 0xe8, + 0xc0, 0x74, 0x36, 0x7d, 0x3a, 0xe9, 0x06, 0xd7, 0x19, 0xb0, 0x88, 0x60, 0x4b, 0x85, 0xe3, 0x51, 0x6a, 0x37, 0x48, + 0x98, 0x08, 0x6e, 0xa2, 0x5e, 0x76, 0xb4, 0x4c, 0xc9, 0xaa, 0x80, 0xe7, 0x57, 0xae, 0x32, 0x1d, 0x47, 0x43, 0xbf, + 0x7f, 0x95, 0x9a, 0xd0, 0xaf, 0xd4, 0x4b, 0x55, 0x9c, 0x87, 0x51, 0x75, 0xa8, 0x30, 0x46, 0x4b, 0x9a, 0xc2, 0x31, + 0x98, 0x5d, 0x84, 0x29, 0x5e, 0xce, 0x36, 0x09, 0xfb, 0x82, 0x81, 0x5c, 0x6a, 0x83, 0x7a, 0x4d, 0x89, 0xd6, 0xac, + 0xbd, 0x99, 0x53, 0x42, 0x2f, 0x58, 0xe9, 0xdf, 0x85, 0xd6, 0x20, 0x50, 0x94, 0xcd, 0x94, 0xe9, 0xb9, 0x6e, 0xe7, + 0x05, 0x4d, 0x68, 0x41, 0x57, 0xa4, 0x06, 0x7d, 0xaf, 0x93, 0xb3, 0xa3, 0x93, 0x9d, 0x99, 0xf5, 0x98, 0x15, 0xc3, + 0xc9, 0x34, 0x86, 0x6b, 0x5a, 0xec, 0xae, 0x69, 0xcb, 0xe6, 0x8d, 0xab, 0xb1, 0x71, 0x1a, 0xb4, 0x0b, 0xa4, 0x6d, + 0x9a, 0xdb, 0x4f, 0x3d, 0x6e, 0x7f, 0x5d, 0xb3, 0xe5, 0xb4, 0xb7, 0xde, 0x6e, 0x7b, 0x29, 0xd8, 0x88, 0x7a, 0x7c, + 0xfc, 0x5a, 0x49, 0xd7, 0x2d, 0x97, 0x9f, 0xc2, 0xb3, 0xc7, 0xd7, 0x2f, 0x7d, 0x70, 0x39, 0x5a, 0xb5, 0xb9, 0xfb, + 0xe5, 0x2e, 0xb2, 0xdc, 0x17, 0x0d, 0x2d, 0xd7, 0x33, 0xd4, 0x24, 0xcf, 0x46, 0x7b, 0x87, 0x5a, 0xb0, 0x9c, 0x75, + 0x13, 0x9e, 0x18, 0xec, 0xd8, 0xab, 0xc6, 0xe6, 0xa8, 0xcc, 0x25, 0xab, 0x41, 0x02, 0x7d, 0x92, 0x67, 0x9a, 0xfe, + 0x41, 0x86, 0xf9, 0xe8, 0x96, 0xe6, 0x80, 0x2b, 0x56, 0xd9, 0x4b, 0x06, 0xa9, 0xab, 0xf6, 0x12, 0x57, 0xbe, 0xc2, + 0x21, 0xd9, 0xe0, 0x93, 0x61, 0xaa, 0x3e, 0xbb, 0xe4, 0xc1, 0xff, 0xdb, 0xaa, 0x55, 0x7a, 0x6e, 0x92, 0x1b, 0x8e, + 0x7f, 0x9d, 0xb4, 0x7d, 0x4c, 0x0c, 0x12, 0xf0, 0xd4, 0x2e, 0x86, 0x6a, 0x54, 0x15, 0xb1, 0x28, 0x73, 0x13, 0x73, + 0xec, 0xde, 0xae, 0xa1, 0x83, 0x32, 0xf8, 0x75, 0xc3, 0x27, 0xe6, 0x0e, 0x6c, 0x05, 0x3a, 0x3a, 0xd1, 0x5c, 0x86, + 0x99, 0xb9, 0x0c, 0xd3, 0xae, 0xad, 0x02, 0xc3, 0xab, 0xb6, 0x4a, 0xa2, 0x5c, 0x8d, 0x7a, 0xdc, 0xcc, 0x52, 0xb3, + 0x17, 0x79, 0xf7, 0x9a, 0xf4, 0x24, 0xfe, 0x74, 0xe9, 0xc9, 0xeb, 0x61, 0x40, 0xe4, 0x97, 0x2c, 0x0d, 0xd7, 0x28, + 0x08, 0x4e, 0xad, 0x76, 0x20, 0xcd, 0x47, 0x80, 0xcc, 0x8f, 0xd3, 0xf0, 0x9d, 0x16, 0xe7, 0x90, 0x8d, 0xd2, 0x38, + 0xb1, 0xa5, 0x51, 0x0f, 0xc1, 0x9d, 0xf7, 0x8a, 0xc7, 0x10, 0xf8, 0xf0, 0x03, 0x6e, 0x06, 0x15, 0xdd, 0x96, 0x98, + 0x28, 0x6d, 0x1e, 0x75, 0xcb, 0x47, 0x0d, 0xa1, 0x92, 0x95, 0xe1, 0x25, 0xd0, 0xde, 0x1d, 0x81, 0x51, 0xe5, 0x04, + 0x32, 0xc3, 0x62, 0xff, 0x60, 0x98, 0x2a, 0x41, 0xd1, 0x50, 0x0e, 0x97, 0x28, 0x07, 0xc4, 0x24, 0x10, 0x18, 0x15, + 0x83, 0x54, 0x57, 0xa6, 0x5e, 0x0c, 0x52, 0x7d, 0xab, 0x22, 0xf5, 0x59, 0x16, 0x56, 0x54, 0xb7, 0x88, 0x8e, 0xe9, + 0x50, 0xd2, 0xa5, 0xd9, 0xa9, 0xb9, 0x96, 0x5e, 0xa8, 0xe5, 0xf8, 0x5c, 0xa7, 0xc1, 0x28, 0x9e, 0xba, 0x14, 0xfd, + 0x56, 0xed, 0x67, 0xff, 0x2d, 0xa6, 0xd4, 0x88, 0x4d, 0xed, 0x2d, 0x62, 0x58, 0xb5, 0x1f, 0xb2, 0x2a, 0x07, 0xed, + 0x2e, 0x28, 0x1b, 0x2b, 0xe3, 0x3c, 0xdf, 0x08, 0x66, 0x0e, 0xda, 0xc6, 0xaa, 0xe9, 0x43, 0x6f, 0xc4, 0xa8, 0xbd, + 0x31, 0xd5, 0xb8, 0x27, 0xf0, 0xd3, 0x06, 0x4d, 0xf7, 0x22, 0xcf, 0x51, 0x8f, 0xbc, 0xfb, 0x9f, 0x39, 0xb2, 0x33, + 0xf9, 0x2c, 0x96, 0x49, 0xdd, 0x3e, 0x26, 0xc1, 0x42, 0xd5, 0x31, 0xba, 0x70, 0x23, 0x53, 0xda, 0xcf, 0x9d, 0xe9, + 0x47, 0x3c, 0x93, 0x87, 0xed, 0xd0, 0xa8, 0x2f, 0x0d, 0x6b, 0x49, 0x11, 0xf5, 0x05, 0xbd, 0x35, 0xd5, 0xd1, 0x01, + 0xf5, 0x3a, 0x02, 0xab, 0x2b, 0xda, 0xa0, 0x06, 0x60, 0x32, 0xae, 0x6d, 0x6d, 0x3e, 0x07, 0x53, 0x5b, 0x55, 0xc1, + 0x33, 0xba, 0x2b, 0x94, 0xee, 0x4d, 0xea, 0xba, 0x35, 0xc4, 0x16, 0x30, 0x20, 0x70, 0xa3, 0xa7, 0xa6, 0x3f, 0x68, + 0xa2, 0x02, 0xd0, 0xa0, 0x71, 0x3b, 0xd3, 0x39, 0x12, 0xfd, 0x4e, 0x6d, 0xda, 0x66, 0xaa, 0x57, 0x95, 0x0f, 0xa0, + 0xe2, 0xcf, 0xd2, 0xd9, 0x85, 0x19, 0xb1, 0x00, 0xc6, 0x3d, 0x70, 0xa6, 0x7a, 0xc7, 0x19, 0x58, 0x4f, 0xe4, 0x79, + 0x56, 0xf2, 0x44, 0x0a, 0x98, 0x11, 0x79, 0x75, 0x25, 0x05, 0x0c, 0x83, 0x1a, 0x00, 0xb4, 0x68, 0x2e, 0xa3, 0x09, + 0x7f, 0x52, 0xd3, 0xfb, 0xf2, 0xf0, 0x27, 0x3a, 0xd7, 0x37, 0xe3, 0x1a, 0x0c, 0x95, 0xd7, 0x15, 0xdf, 0xc9, 0xf4, + 0x0d, 0x7f, 0xea, 0x65, 0x5a, 0xca, 0x75, 0xb1, 0x93, 0xe5, 0xc9, 0x37, 0xfc, 0x99, 0xce, 0x73, 0xf0, 0xb4, 0xa6, + 0x69, 0x7c, 0xb7, 0x93, 0xe5, 0xef, 0xdf, 0x3c, 0xb5, 0x79, 0x9e, 0x8c, 0x6b, 0x7a, 0xc3, 0xf9, 0x47, 0x97, 0x69, + 0xa2, 0xab, 0x1a, 0x3f, 0xfd, 0xbb, 0xcd, 0xf5, 0xb4, 0xa6, 0x57, 0x52, 0x54, 0xcb, 0x9d, 0xa2, 0x0e, 0xbe, 0x39, + 0xf8, 0x3b, 0xff, 0xc6, 0x74, 0xef, 0xa0, 0xa6, 0x7f, 0xad, 0xe3, 0xa2, 0xe2, 0xc5, 0x4e, 0x71, 0x7f, 0xfb, 0xfb, + 0xdf, 0x9f, 0xda, 0x8c, 0x4f, 0x6b, 0x7a, 0xc7, 0xe3, 0x8e, 0xb6, 0x4f, 0x9e, 0x3d, 0xe5, 0x7f, 0xab, 0x6b, 0xfa, + 0x0b, 0xf3, 0x83, 0xa3, 0x1e, 0x67, 0x9e, 0x1e, 0x3e, 0x91, 0x4d, 0xd4, 0x80, 0xa1, 0x87, 0x06, 0x90, 0x4b, 0xab, + 0xa6, 0xb9, 0xc7, 0x2b, 0x17, 0xdc, 0xbe, 0xcf, 0xe2, 0x34, 0x5e, 0xc1, 0x41, 0xb0, 0x41, 0xe3, 0xac, 0x02, 0x38, + 0x55, 0xe0, 0x3d, 0xa3, 0x92, 0x66, 0xa5, 0xfc, 0x27, 0xe7, 0x1f, 0x61, 0xd0, 0x10, 0xd2, 0x46, 0x45, 0x06, 0x3a, + 0x59, 0xe9, 0xc8, 0x46, 0xe8, 0xbf, 0xd9, 0x8c, 0x83, 0xe3, 0xc3, 0xe8, 0xf5, 0xfb, 0x61, 0xc1, 0x44, 0x58, 0x10, + 0x42, 0xff, 0x0c, 0x0b, 0x70, 0x28, 0x29, 0x98, 0x97, 0xcf, 0xf8, 0x9e, 0x6b, 0xa3, 0xb0, 0x10, 0x44, 0x77, 0x91, + 0x7d, 0x40, 0xd5, 0xa3, 0xef, 0xd0, 0x0d, 0xf1, 0xb2, 0xc2, 0x82, 0xa1, 0x55, 0x0d, 0xcc, 0x10, 0x14, 0xff, 0x86, + 0x87, 0x12, 0x7c, 0xe2, 0x01, 0x3e, 0x7a, 0x4c, 0x66, 0x5c, 0x5d, 0x6b, 0x4f, 0x2e, 0xc2, 0x82, 0x06, 0xba, 0xed, + 0x10, 0x74, 0x20, 0xf2, 0x5f, 0x80, 0xa7, 0xc0, 0xc0, 0x87, 0x85, 0x5d, 0xca, 0x5d, 0x7f, 0xf5, 0x5f, 0x0c, 0xeb, + 0xe8, 0xc2, 0x8f, 0xfe, 0x62, 0x5d, 0xd8, 0x33, 0x32, 0x95, 0x87, 0xe5, 0x70, 0x32, 0x1d, 0x0c, 0xa4, 0x8b, 0xe3, + 0x76, 0x9c, 0xcd, 0x7f, 0x99, 0xcb, 0xc5, 0x02, 0x75, 0xdf, 0x38, 0xaf, 0x33, 0xfd, 0x37, 0xd2, 0xce, 0x07, 0x6f, + 0x8e, 0x7f, 0x3b, 0x3b, 0x3d, 0x7e, 0x05, 0xce, 0x07, 0x1f, 0x5e, 0x7e, 0xff, 0xf2, 0xbd, 0x0a, 0xee, 0xae, 0xe6, + 0xbc, 0xdf, 0x77, 0x52, 0x9f, 0x90, 0x0f, 0x2b, 0xb2, 0x1f, 0xc6, 0x8f, 0x0b, 0x65, 0xf4, 0x40, 0x0e, 0x99, 0x85, + 0x42, 0x86, 0x2a, 0x6a, 0xfb, 0xbb, 0x1c, 0x4e, 0x3c, 0x30, 0x8b, 0xbb, 0x86, 0x08, 0xd7, 0x6f, 0xb9, 0x0d, 0xb2, + 0x26, 0x8f, 0xbc, 0x7e, 0x70, 0x32, 0x95, 0x8e, 0x2d, 0x2c, 0x18, 0x94, 0x0d, 0x6d, 0x3a, 0xce, 0xe6, 0xc5, 0xc2, + 0xb6, 0xcb, 0x2d, 0x90, 0x51, 0x9a, 0x5d, 0x5c, 0x84, 0x0a, 0xba, 0xfa, 0x08, 0x34, 0x00, 0xa6, 0x51, 0x85, 0x6b, + 0x11, 0x9f, 0xf9, 0xe5, 0x47, 0x63, 0xaf, 0x79, 0xb7, 0xa8, 0x7b, 0x32, 0xcd, 0xaa, 0x1a, 0x03, 0x3a, 0x98, 0x50, + 0xee, 0x06, 0xdd, 0x04, 0x93, 0x51, 0x6d, 0xf9, 0x65, 0x5e, 0x2d, 0x4c, 0x73, 0xdc, 0x30, 0x54, 0x5e, 0xc9, 0x6b, + 0xd9, 0x40, 0x64, 0x20, 0x19, 0x86, 0x3d, 0x1a, 0xa3, 0x48, 0x7d, 0x6f, 0xd7, 0x3b, 0x7e, 0x93, 0x4b, 0x88, 0xa6, + 0x98, 0x81, 0x74, 0xfe, 0x58, 0x28, 0xe7, 0x72, 0xc9, 0xf8, 0x5c, 0x2c, 0x8e, 0xc0, 0xed, 0x7c, 0x2e, 0x16, 0x11, + 0x06, 0xe5, 0xcb, 0x20, 0x56, 0x09, 0xd8, 0xbd, 0x38, 0x08, 0xdf, 0x4e, 0x68, 0x03, 0xbb, 0x81, 0x24, 0x1b, 0x94, + 0x76, 0xa5, 0x21, 0xca, 0x9d, 0xf2, 0x68, 0x83, 0xc8, 0x43, 0xac, 0x9a, 0x57, 0x6d, 0x4f, 0x36, 0x73, 0x31, 0xc1, + 0x55, 0x16, 0x33, 0x39, 0x8d, 0x0f, 0x59, 0x31, 0x8d, 0xa1, 0x94, 0x38, 0x4d, 0xc3, 0x98, 0x4e, 0xa8, 0x20, 0x24, + 0x61, 0x7c, 0x1e, 0x2f, 0x68, 0x82, 0x52, 0x82, 0x10, 0x42, 0x7e, 0x8c, 0xd0, 0x36, 0x07, 0x96, 0xbc, 0xdd, 0x7e, + 0x9e, 0x7e, 0x6e, 0xc7, 0x70, 0x19, 0x15, 0xa1, 0x1b, 0x74, 0xd6, 0xf0, 0x6f, 0x44, 0x05, 0x8d, 0xb1, 0x62, 0x08, + 0x02, 0x5e, 0x60, 0x54, 0xc2, 0x82, 0xc4, 0xac, 0x82, 0x28, 0x02, 0xe5, 0x3c, 0x5e, 0xb0, 0x82, 0x36, 0x6d, 0x4e, + 0x63, 0x6d, 0x12, 0xd4, 0x73, 0x58, 0x6a, 0x7b, 0x52, 0xa9, 0x10, 0x7b, 0x7c, 0x26, 0xa2, 0x6b, 0x6d, 0x68, 0x00, + 0x28, 0x50, 0x4a, 0x2e, 0x7e, 0xf3, 0xe5, 0x1e, 0x6e, 0x0a, 0xfa, 0x9f, 0x6d, 0x4c, 0xb4, 0xb3, 0x5c, 0x1d, 0x7a, + 0xf3, 0x05, 0x8d, 0xf3, 0x1c, 0x42, 0xb1, 0x19, 0x04, 0x72, 0x91, 0x55, 0x10, 0xd1, 0xe2, 0x2e, 0x30, 0x21, 0xe1, + 0xa0, 0x4d, 0xbf, 0x40, 0x6a, 0x43, 0x4c, 0xae, 0x3c, 0x31, 0xb0, 0xdb, 0x2a, 0x41, 0xc0, 0x91, 0x9e, 0x67, 0x9f, + 0x9a, 0x18, 0x6b, 0x9a, 0x9a, 0x99, 0x78, 0x1b, 0x0a, 0xd1, 0xa0, 0x05, 0xd1, 0x0c, 0xde, 0x3f, 0x57, 0x1c, 0xaf, + 0x3a, 0xf0, 0x03, 0xde, 0xb9, 0x38, 0xf3, 0x6a, 0xe6, 0x11, 0x39, 0xf5, 0x51, 0x8e, 0xe8, 0x97, 0x3c, 0xac, 0x46, + 0x3a, 0x19, 0x63, 0x25, 0x71, 0xd0, 0xdb, 0x60, 0xc1, 0x9c, 0xd0, 0x15, 0x0f, 0x2d, 0x1f, 0xff, 0x0a, 0x99, 0x8c, + 0x92, 0x1a, 0x2b, 0xba, 0xd2, 0x62, 0xc4, 0x79, 0x0d, 0xb3, 0x34, 0x59, 0xd1, 0xc5, 0x42, 0x93, 0x66, 0xa1, 0x4c, + 0x03, 0x7c, 0x02, 0x2d, 0x46, 0xee, 0xa1, 0xa6, 0x0d, 0x84, 0x86, 0xdd, 0x21, 0xe0, 0x23, 0xf7, 0xd0, 0xe1, 0xff, + 0xe7, 0xd9, 0x05, 0x22, 0xed, 0xcd, 0x4d, 0x64, 0x3c, 0x52, 0x37, 0x70, 0x50, 0x8c, 0x8f, 0x7d, 0x33, 0xf1, 0x0b, + 0x67, 0xf4, 0x21, 0xa9, 0x7c, 0x87, 0x0f, 0x96, 0x3f, 0xde, 0xd4, 0xcc, 0xca, 0x08, 0xd6, 0xc3, 0x76, 0x8b, 0x0b, + 0xa2, 0xed, 0x02, 0x48, 0x3d, 0xe3, 0xd5, 0xc2, 0x37, 0x5e, 0x8d, 0xef, 0x31, 0x5e, 0x75, 0x67, 0x6a, 0x98, 0x93, + 0x0d, 0xea, 0xb3, 0x94, 0x3c, 0x3f, 0x47, 0x99, 0x60, 0xd3, 0xe5, 0xac, 0xa4, 0x2a, 0x95, 0xd0, 0x5e, 0xec, 0x67, + 0x8c, 0x6f, 0x09, 0xc6, 0x59, 0x71, 0x18, 0x09, 0x54, 0xa5, 0x92, 0x3a, 0xec, 0x15, 0xa0, 0x1e, 0x83, 0xf7, 0x06, + 0x43, 0xd4, 0xc8, 0xd8, 0x4d, 0x1b, 0x08, 0x0d, 0x8d, 0xf5, 0x68, 0xcf, 0x5a, 0x8f, 0x6e, 0xb7, 0x95, 0xf1, 0xb7, + 0x93, 0xeb, 0x22, 0x41, 0x54, 0x61, 0x35, 0x9a, 0x00, 0x6f, 0x9a, 0xd8, 0xdb, 0x92, 0x53, 0x5a, 0x60, 0xf8, 0xec, + 0x3f, 0xc3, 0xd2, 0xa9, 0x24, 0x4a, 0x32, 0x2b, 0xa3, 0x81, 0x3b, 0x07, 0x5f, 0xc4, 0x15, 0xac, 0x01, 0x88, 0xe4, + 0x88, 0x1e, 0xae, 0x7f, 0x86, 0xd2, 0x65, 0x96, 0x64, 0x26, 0x21, 0x33, 0x17, 0x69, 0x3b, 0xeb, 0x60, 0xe2, 0x4c, + 0x6a, 0xbd, 0xb1, 0x90, 0x43, 0x83, 0xfc, 0x00, 0xca, 0x10, 0x87, 0x4f, 0x3e, 0x98, 0x50, 0xa9, 0x42, 0xa9, 0x36, + 0xba, 0xd9, 0x0d, 0xbc, 0xf2, 0x21, 0xbb, 0xe2, 0x65, 0x15, 0x5f, 0xad, 0x8c, 0x25, 0x31, 0x67, 0xf7, 0xb9, 0xed, + 0x51, 0x61, 0x5e, 0xbd, 0x7d, 0xf9, 0xfd, 0x71, 0xe3, 0xd5, 0x2e, 0xe2, 0x68, 0x08, 0xb6, 0x15, 0x63, 0x8c, 0xde, + 0xe2, 0xd3, 0x60, 0xa2, 0x5c, 0x23, 0xd0, 0xbb, 0x14, 0xf4, 0xdb, 0x5f, 0xea, 0x09, 0x78, 0xc5, 0xf5, 0xf2, 0x4b, + 0x3e, 0x02, 0x96, 0xa8, 0xd0, 0xb3, 0xc2, 0xdc, 0xac, 0xcc, 0xee, 0xed, 0x56, 0x64, 0xa6, 0x5d, 0x69, 0x64, 0x20, + 0x5e, 0x6d, 0x87, 0xb1, 0x70, 0xe9, 0x9a, 0x6e, 0x07, 0xbb, 0x5a, 0x7a, 0x96, 0xc8, 0xdb, 0x6d, 0x09, 0x1d, 0xb2, + 0x03, 0xee, 0xbd, 0x8c, 0x6f, 0xe1, 0x65, 0xe9, 0x75, 0xb3, 0x19, 0x3c, 0x01, 0xcc, 0x84, 0x0b, 0x67, 0x59, 0x1c, + 0x33, 0x9e, 0x84, 0x2a, 0x36, 0x57, 0x43, 0xe4, 0xad, 0x08, 0xad, 0xd9, 0x5f, 0xa1, 0x18, 0x81, 0xdd, 0xc9, 0xe9, + 0xc7, 0x6c, 0x35, 0x5b, 0x02, 0x6a, 0xfe, 0x55, 0x26, 0x80, 0xe6, 0xda, 0xb5, 0x60, 0x9b, 0x42, 0x9b, 0xeb, 0xfa, + 0x79, 0xbc, 0x8a, 0x13, 0x50, 0xdd, 0x80, 0xb7, 0xc8, 0x9d, 0x16, 0x5d, 0x19, 0x74, 0x51, 0xfa, 0x40, 0x39, 0x96, + 0x14, 0x3a, 0xfa, 0xde, 0x13, 0xea, 0xdc, 0x33, 0x80, 0x4b, 0x1a, 0x35, 0x4f, 0xb5, 0x94, 0xb1, 0x00, 0x58, 0xe8, + 0x60, 0xa6, 0xc8, 0x56, 0x74, 0x6b, 0x30, 0x29, 0xe0, 0xad, 0x01, 0xfe, 0x10, 0x59, 0xa5, 0xee, 0x8a, 0x65, 0x58, + 0x7a, 0xf6, 0xd7, 0xfd, 0x7e, 0xec, 0xd9, 0x5f, 0x5f, 0x68, 0x5a, 0x17, 0xb7, 0x1b, 0x40, 0x6a, 0x0c, 0x20, 0x72, + 0xac, 0x07, 0xc2, 0x44, 0x14, 0x6b, 0xfa, 0xfe, 0x1d, 0x9b, 0x2c, 0x0a, 0x84, 0x7e, 0xa7, 0x5e, 0x4f, 0x4a, 0x02, + 0x3a, 0xb5, 0x8a, 0x1d, 0x0d, 0xb4, 0xd9, 0x07, 0x04, 0x44, 0xf5, 0x33, 0xb2, 0xf9, 0x42, 0x39, 0x17, 0xab, 0xf0, + 0xe1, 0x63, 0x0a, 0x01, 0x85, 0x3b, 0x6a, 0x74, 0xde, 0x86, 0x48, 0xa0, 0xac, 0x50, 0xc4, 0x9a, 0x17, 0x6b, 0x49, + 0xc8, 0x7c, 0xbc, 0x40, 0xc1, 0x95, 0x03, 0x76, 0xe5, 0x6c, 0x32, 0x2c, 0x23, 0xce, 0xc2, 0xfb, 0xbf, 0x99, 0x2c, + 0x08, 0x6a, 0xae, 0xfc, 0x40, 0x8e, 0x3b, 0x99, 0x1a, 0x7b, 0xaa, 0x51, 0x83, 0x60, 0x32, 0x82, 0xc0, 0x70, 0xc3, + 0x2f, 0xf8, 0xf8, 0x60, 0x41, 0x40, 0x45, 0x66, 0xcd, 0x42, 0xcc, 0x8b, 0xc3, 0x27, 0x80, 0x1a, 0x33, 0x3a, 0x78, + 0x36, 0xe5, 0x0c, 0x0e, 0x51, 0x3a, 0x06, 0x19, 0xad, 0x80, 0xdf, 0x42, 0xfd, 0x6e, 0x9d, 0xf8, 0x3e, 0xf4, 0xab, + 0xa0, 0x17, 0x31, 0x30, 0x1c, 0xd1, 0x64, 0x3f, 0xe4, 0x83, 0xc9, 0x00, 0xb4, 0x25, 0xde, 0xee, 0x6b, 0x69, 0xc5, + 0xcd, 0xe9, 0xd2, 0xe9, 0xfe, 0x49, 0x9b, 0x20, 0x89, 0x54, 0xb2, 0x52, 0x11, 0x03, 0x08, 0x65, 0xa9, 0xb6, 0xc9, + 0x12, 0x2c, 0x2b, 0xcc, 0x92, 0xe6, 0x06, 0x25, 0x71, 0x77, 0x33, 0x70, 0x8c, 0x9a, 0x75, 0x1c, 0x96, 0x2d, 0x37, + 0x6a, 0x80, 0xcf, 0x49, 0x58, 0x61, 0x6f, 0x38, 0x33, 0xe9, 0x9d, 0xe9, 0x70, 0x75, 0xcc, 0xd9, 0x1b, 0x8e, 0x60, + 0x1c, 0x09, 0xde, 0x78, 0xe8, 0x92, 0x69, 0xa8, 0xc8, 0x94, 0x71, 0x30, 0xed, 0x01, 0xee, 0x3d, 0x07, 0xe3, 0x30, + 0x36, 0xa8, 0x2c, 0xa9, 0x4f, 0xbd, 0xbb, 0x10, 0x08, 0xd2, 0x5a, 0x2f, 0xf3, 0x19, 0x9e, 0x9e, 0x11, 0xca, 0xfe, + 0x90, 0xc3, 0x17, 0x60, 0x47, 0x41, 0x8e, 0x26, 0xfc, 0xd9, 0xe3, 0xdd, 0x40, 0x55, 0x7c, 0x10, 0xec, 0xc5, 0x22, + 0xdd, 0x0b, 0x06, 0x02, 0x7e, 0x15, 0x7c, 0xaf, 0x92, 0x72, 0xef, 0x22, 0x2e, 0xf6, 0xe2, 0x55, 0x5c, 0x54, 0x7b, + 0x37, 0x59, 0xb5, 0xdc, 0x33, 0x1d, 0x02, 0x68, 0xde, 0x60, 0x10, 0x0f, 0x82, 0xbd, 0x60, 0x50, 0x98, 0xa9, 0x5d, + 0xb1, 0xb2, 0x71, 0x9c, 0x99, 0x10, 0x65, 0x41, 0x33, 0x40, 0x58, 0xe3, 0x34, 0x00, 0x3e, 0x75, 0xcd, 0x52, 0x7a, + 0x81, 0xe1, 0x06, 0xc4, 0x74, 0x0d, 0x7d, 0x00, 0x1e, 0x79, 0x4d, 0x63, 0x58, 0x02, 0x17, 0x83, 0x01, 0x59, 0x43, + 0xe4, 0x82, 0x35, 0xb5, 0x41, 0x1c, 0xc2, 0xb5, 0xb2, 0xd3, 0xde, 0x05, 0x66, 0xda, 0x6e, 0x01, 0x51, 0x79, 0x42, + 0xfa, 0x7d, 0xfb, 0x0d, 0xf5, 0x2f, 0xd8, 0x4b, 0xb0, 0xbf, 0x2a, 0xaa, 0x30, 0x91, 0x4a, 0xf3, 0x7d, 0xc9, 0x8e, + 0x06, 0x2a, 0xe2, 0xf0, 0x8e, 0x23, 0x45, 0x1b, 0x95, 0xcb, 0xb2, 0x27, 0xcb, 0x86, 0xaf, 0xc4, 0x15, 0x77, 0x7e, + 0x5c, 0x95, 0x94, 0x79, 0x95, 0xad, 0x14, 0xfb, 0x37, 0xe3, 0x9a, 0xfb, 0x03, 0xeb, 0xcf, 0xe6, 0x2b, 0xb8, 0xb6, + 0x7a, 0xef, 0x9a, 0x5c, 0x23, 0x72, 0x96, 0x50, 0x2e, 0xa9, 0x6d, 0x1e, 0xde, 0xd2, 0xf7, 0xf9, 0xd5, 0xb7, 0x99, + 0x4e, 0xe3, 0xb3, 0x0a, 0x0b, 0x17, 0xa2, 0x15, 0xc1, 0xa1, 0x21, 0x17, 0xcd, 0x23, 0xc0, 0x5c, 0xfb, 0x6c, 0x05, + 0x05, 0xa9, 0xcf, 0x2a, 0xf4, 0x6e, 0x85, 0x84, 0x57, 0x9a, 0x5d, 0x7a, 0x18, 0x48, 0x19, 0xb7, 0x87, 0x96, 0x30, + 0x69, 0x79, 0x11, 0xde, 0x7b, 0xcd, 0x4d, 0xee, 0x45, 0x88, 0xd1, 0x8b, 0x3c, 0x3b, 0x01, 0x63, 0xdd, 0x25, 0x3b, + 0x1b, 0x9e, 0xf8, 0x0d, 0xcf, 0x59, 0x8b, 0x46, 0xd3, 0x25, 0x4b, 0xfa, 0xfd, 0x18, 0x4c, 0xbc, 0x53, 0x96, 0xc3, + 0xaf, 0x7c, 0x41, 0xd7, 0x0c, 0x30, 0xc5, 0xe8, 0x05, 0x24, 0xa4, 0x88, 0x44, 0xb2, 0x56, 0x27, 0xc9, 0x67, 0xba, + 0x0b, 0xc0, 0xe8, 0x17, 0xb3, 0x34, 0x5a, 0xde, 0x6b, 0x66, 0x81, 0xe4, 0x19, 0xfa, 0xae, 0x83, 0xed, 0x8d, 0x7d, + 0x90, 0x72, 0x7e, 0x28, 0xa6, 0x83, 0x01, 0x27, 0x1a, 0x6e, 0xbc, 0x54, 0xe2, 0x5a, 0xdd, 0xe2, 0x8e, 0x61, 0x2c, + 0xf5, 0x6d, 0x11, 0x83, 0x03, 0x76, 0xd1, 0xca, 0x6e, 0x1f, 0x60, 0x5f, 0x39, 0xde, 0xa5, 0xca, 0xee, 0xf4, 0x98, + 0x69, 0x2e, 0x5b, 0x4d, 0x3a, 0xa9, 0xb8, 0x9f, 0xc8, 0x37, 0xb9, 0x83, 0x2e, 0x97, 0x63, 0xcd, 0x5b, 0x0e, 0x40, + 0x45, 0x3f, 0x52, 0x54, 0xf7, 0x0b, 0x1c, 0x61, 0x1e, 0xac, 0xdb, 0x7c, 0xb2, 0x6f, 0x0a, 0x1c, 0x22, 0x4f, 0xda, + 0x68, 0x0a, 0xe8, 0xde, 0xc5, 0xe3, 0xae, 0x7e, 0x5b, 0xba, 0x0b, 0x94, 0x68, 0xa7, 0xe2, 0x86, 0x1f, 0x13, 0x75, + 0x3a, 0xd3, 0x86, 0xd0, 0xbf, 0x32, 0xe2, 0xfe, 0xd2, 0xb8, 0x8a, 0x37, 0xbd, 0xcb, 0x67, 0x1c, 0xea, 0xec, 0x86, + 0x50, 0x00, 0xae, 0xda, 0xd3, 0xa9, 0x1b, 0x43, 0x7a, 0xa5, 0x44, 0xb7, 0xc1, 0xc1, 0xee, 0xf5, 0x19, 0x47, 0xd1, + 0x8f, 0x51, 0x23, 0xdf, 0x44, 0xe2, 0xb1, 0x1c, 0xc4, 0x8f, 0x0b, 0xba, 0x8c, 0xc4, 0xe3, 0x62, 0x10, 0x3f, 0x96, + 0x75, 0xbd, 0x7b, 0xae, 0xdc, 0xdf, 0x47, 0xe4, 0x59, 0x77, 0xf6, 0x52, 0x09, 0x1b, 0x03, 0xcf, 0xae, 0x05, 0x84, + 0x53, 0xf0, 0x44, 0xb6, 0x96, 0x3e, 0x74, 0x6e, 0xf7, 0xb1, 0x65, 0x92, 0x20, 0xe8, 0x79, 0x9b, 0x4d, 0xa2, 0xd8, + 0xd9, 0xe6, 0xd1, 0x87, 0x53, 0x20, 0xa1, 0xdb, 0x6d, 0xb3, 0xae, 0xd6, 0x80, 0x62, 0x1a, 0x8e, 0xf9, 0x7e, 0x31, + 0xba, 0xf1, 0xdd, 0xf5, 0xf7, 0x8b, 0xd1, 0x92, 0x0c, 0x27, 0x66, 0xf2, 0xe3, 0xa3, 0xf1, 0x2c, 0x8e, 0x26, 0x75, + 0xc7, 0x69, 0xa1, 0xf1, 0x4f, 0xbd, 0x5b, 0x28, 0x02, 0xa7, 0x62, 0x04, 0x47, 0x4e, 0x85, 0x72, 0x52, 0x6a, 0x60, + 0xf8, 0xef, 0x55, 0x3b, 0xda, 0xb4, 0x37, 0x71, 0x95, 0x2c, 0x33, 0x71, 0xa9, 0xc3, 0x87, 0xeb, 0xe8, 0xe2, 0x36, + 0xa0, 0x9d, 0x77, 0x99, 0x76, 0xfc, 0x3a, 0x69, 0xd0, 0x13, 0x57, 0x33, 0x03, 0x6e, 0xdd, 0x8f, 0xd0, 0x0c, 0x81, + 0xd1, 0xf2, 0xfc, 0x1d, 0x62, 0x6e, 0xff, 0xaa, 0x6c, 0x7e, 0x15, 0xed, 0x73, 0x64, 0xa4, 0x6c, 0x93, 0x91, 0x0a, + 0x8c, 0x30, 0xa5, 0x48, 0xe2, 0x2a, 0x84, 0x40, 0xf6, 0x5f, 0x52, 0x5c, 0x8b, 0xa5, 0xf7, 0x1a, 0x84, 0x09, 0xb6, + 0x0b, 0xda, 0xaf, 0x6e, 0xe7, 0xb6, 0xd2, 0x62, 0x8f, 0xd4, 0xf7, 0xb9, 0xb3, 0x5d, 0xd1, 0xe4, 0xef, 0xcb, 0x06, + 0xb4, 0x01, 0x44, 0x79, 0x5f, 0x1f, 0x95, 0xc0, 0xc9, 0x88, 0x1b, 0x4a, 0x8c, 0x5e, 0xd0, 0xd5, 0x89, 0xdc, 0xb3, + 0x53, 0xf3, 0xa6, 0x62, 0xa6, 0xe2, 0xca, 0x37, 0x7b, 0xe6, 0x3f, 0x18, 0x0a, 0x2a, 0xc0, 0xc0, 0xdb, 0x9c, 0xf1, + 0xe8, 0x40, 0x77, 0x63, 0x74, 0x5a, 0xb0, 0x59, 0x50, 0x97, 0x75, 0xd3, 0xc6, 0x83, 0x46, 0x1c, 0x14, 0xc5, 0xaa, + 0x50, 0x23, 0xe1, 0x89, 0x40, 0xc0, 0x94, 0x5d, 0xf1, 0xc8, 0x08, 0x6a, 0x7a, 0x13, 0x0a, 0x1b, 0x0a, 0xfe, 0x2a, + 0x51, 0x4d, 0x6f, 0x42, 0x9b, 0x4c, 0x9c, 0x66, 0x10, 0xc1, 0x8c, 0xd8, 0xee, 0xb7, 0x80, 0x36, 0xb7, 0x66, 0xb4, + 0xa9, 0x6b, 0xab, 0xad, 0x42, 0x2e, 0x29, 0x52, 0x96, 0xff, 0x4e, 0x4d, 0x05, 0x25, 0xb5, 0x5c, 0xf4, 0x26, 0x4d, + 0x17, 0x3d, 0x9e, 0x19, 0x49, 0xa0, 0x72, 0xcb, 0x1d, 0xa3, 0x3f, 0x84, 0x05, 0x1e, 0x31, 0x71, 0x62, 0xc1, 0xdc, + 0xea, 0x88, 0x65, 0x73, 0xb1, 0x18, 0xad, 0x24, 0x84, 0x0d, 0x3e, 0x64, 0xd9, 0xbc, 0xd4, 0x0f, 0xa1, 0x2f, 0x2c, + 0x3d, 0x01, 0xbb, 0xd8, 0x60, 0x25, 0xcb, 0x00, 0x7c, 0x2f, 0xe8, 0x66, 0x25, 0xcb, 0x48, 0xaa, 0xee, 0xc7, 0x35, + 0x96, 0xa0, 0xd2, 0x0a, 0x95, 0x96, 0xd4, 0x58, 0x10, 0xf8, 0xaa, 0xea, 0xf2, 0x21, 0xd9, 0x55, 0xa0, 0x9e, 0x3a, + 0x6a, 0xc0, 0x29, 0x50, 0x55, 0x60, 0x41, 0x12, 0x54, 0x86, 0xae, 0x0a, 0x4c, 0x2b, 0x30, 0xcd, 0x54, 0xe1, 0xa2, + 0xcc, 0x0e, 0xa5, 0x59, 0x2f, 0xf9, 0x2c, 0x1e, 0x84, 0xc9, 0x30, 0x26, 0x8f, 0x11, 0x6a, 0x7f, 0x3f, 0x8f, 0x62, + 0x2d, 0x97, 0x5c, 0x39, 0xbf, 0xf8, 0x9b, 0xcf, 0xd8, 0xeb, 0x9e, 0x61, 0xb0, 0x00, 0x67, 0x69, 0x7b, 0x95, 0x89, + 0x77, 0xb2, 0x15, 0x1c, 0x07, 0xb3, 0x28, 0x87, 0x55, 0x4f, 0x8e, 0x68, 0x2e, 0x72, 0xed, 0x5d, 0x84, 0xc8, 0x41, + 0x66, 0x8f, 0x01, 0x76, 0x23, 0x7c, 0x1d, 0x5a, 0x9b, 0x5b, 0x5d, 0x21, 0xfe, 0x46, 0x89, 0xc4, 0x4f, 0x52, 0x7e, + 0x5c, 0xaf, 0x54, 0xae, 0xca, 0xe0, 0xb1, 0xea, 0x66, 0xf0, 0x4c, 0xfb, 0x1e, 0x6b, 0xff, 0xd6, 0x76, 0x73, 0xbc, + 0xf7, 0xe0, 0x41, 0xeb, 0x7f, 0xeb, 0x49, 0x08, 0xed, 0x95, 0x93, 0xd4, 0x1d, 0x35, 0x7a, 0x66, 0xb2, 0x46, 0x54, + 0xc2, 0xd4, 0xee, 0x54, 0x8e, 0x81, 0x9a, 0x0e, 0xe0, 0x5a, 0xa2, 0x26, 0xe8, 0x49, 0xc1, 0xc6, 0x70, 0xc4, 0x59, + 0x1c, 0xb4, 0xc3, 0x18, 0xc5, 0xcb, 0xb9, 0x12, 0x2f, 0xe7, 0x47, 0x8c, 0x03, 0xb4, 0x16, 0x20, 0xd5, 0x6b, 0xd8, + 0xcf, 0x5c, 0xc1, 0x02, 0x9b, 0x3b, 0xdf, 0x81, 0x05, 0x32, 0xc4, 0xc9, 0xe6, 0x38, 0xd9, 0xe3, 0x5a, 0xcf, 0xbd, + 0xc0, 0xc7, 0x49, 0xbd, 0xf0, 0xea, 0x2a, 0xdb, 0x75, 0x2d, 0x59, 0x39, 0x2f, 0x06, 0x13, 0x08, 0xca, 0x52, 0xce, + 0x8b, 0xe1, 0x64, 0x41, 0x73, 0xf8, 0xb1, 0x68, 0xa0, 0x43, 0x2c, 0x07, 0x09, 0x5c, 0x3a, 0x7b, 0x0c, 0x78, 0x43, + 0xa9, 0xc5, 0xdd, 0x58, 0x47, 0x8e, 0x75, 0x14, 0xfb, 0x61, 0x0c, 0xb8, 0xb2, 0x4e, 0xe0, 0x7d, 0xff, 0xf5, 0xb1, + 0x09, 0xc8, 0xaa, 0x5d, 0xe1, 0xd5, 0x28, 0x77, 0x5d, 0x69, 0xf4, 0x25, 0xa5, 0x27, 0xbc, 0xe0, 0xa9, 0x64, 0xbb, + 0xed, 0x19, 0x38, 0x5b, 0xe2, 0x21, 0xf1, 0x8e, 0x11, 0xbd, 0x98, 0x36, 0x32, 0x73, 0x02, 0x67, 0xb6, 0xbb, 0x6c, + 0x63, 0x7e, 0xec, 0x00, 0x07, 0x8b, 0x20, 0x24, 0x6e, 0x08, 0xc3, 0xc4, 0x8e, 0xca, 0xa1, 0x16, 0xc2, 0x75, 0x2d, + 0xbc, 0x8e, 0xd3, 0x32, 0x06, 0x17, 0x69, 0x6d, 0x9b, 0x78, 0x0f, 0x5d, 0xf7, 0xfc, 0x98, 0x5b, 0x1d, 0xa3, 0x2d, + 0xa4, 0xdf, 0x8e, 0x4e, 0xef, 0x39, 0x0c, 0x40, 0xd3, 0x83, 0x59, 0xd5, 0x3e, 0x93, 0xb8, 0x39, 0xed, 0x04, 0x21, + 0x11, 0x88, 0xa2, 0x74, 0x46, 0x98, 0xfe, 0x9d, 0xe6, 0xb2, 0x8a, 0x56, 0x0f, 0xf2, 0xcc, 0x21, 0xcf, 0x42, 0x6f, + 0x7b, 0xd0, 0xaa, 0xb9, 0x1b, 0x8c, 0x13, 0xb7, 0xdb, 0x3b, 0xff, 0x6f, 0x59, 0xd7, 0x56, 0x6b, 0xc4, 0xe3, 0x76, + 0xf5, 0x83, 0xc6, 0x5e, 0xed, 0xa9, 0x18, 0x30, 0x2b, 0xe9, 0x9d, 0x51, 0x25, 0x2f, 0x32, 0x5e, 0xe2, 0x49, 0xb5, + 0x6a, 0xf8, 0x78, 0xdf, 0x64, 0x23, 0xf3, 0x40, 0xa6, 0x80, 0x78, 0x7e, 0x93, 0x1a, 0xf5, 0x71, 0x8a, 0x12, 0xf0, + 0x77, 0x3a, 0xbe, 0x11, 0xfd, 0x68, 0x5f, 0x5c, 0xf2, 0xea, 0xe4, 0x46, 0x98, 0x17, 0x2f, 0xac, 0xce, 0x9f, 0xbe, + 0x29, 0x7c, 0xe8, 0x70, 0xd4, 0xde, 0x41, 0x91, 0x25, 0x13, 0x47, 0x13, 0x23, 0x6b, 0x13, 0xb3, 0x8f, 0x0a, 0x2e, + 0x26, 0xaa, 0xd0, 0xb3, 0xce, 0x9e, 0x30, 0x05, 0xe8, 0x1b, 0xc7, 0xa8, 0x64, 0x0c, 0x0b, 0x06, 0xea, 0x34, 0x25, + 0x44, 0x0f, 0xc5, 0x0c, 0xe3, 0x15, 0x03, 0x28, 0x4c, 0xa1, 0x40, 0x14, 0x9d, 0x7d, 0x38, 0xd0, 0x84, 0x7e, 0xff, + 0x26, 0xd5, 0x19, 0x68, 0x59, 0x4f, 0x0b, 0x10, 0xd5, 0x41, 0xb4, 0x55, 0x5e, 0x84, 0x3f, 0x2e, 0x69, 0x99, 0xd1, + 0xa5, 0xa0, 0xa9, 0xa0, 0x49, 0x46, 0x2f, 0xb8, 0x12, 0x15, 0x5f, 0x08, 0xa6, 0x68, 0xbb, 0x21, 0xec, 0x3f, 0x36, + 0xe8, 0x7a, 0x2b, 0xd6, 0x1a, 0xda, 0x9d, 0x20, 0x23, 0x34, 0x5f, 0xe8, 0x20, 0x64, 0xa8, 0x9c, 0x84, 0xae, 0x55, + 0x1a, 0xaf, 0xc0, 0x25, 0xd3, 0x6c, 0xb4, 0x8c, 0xcb, 0x30, 0xb0, 0x5f, 0x05, 0x16, 0x93, 0x03, 0x93, 0x4e, 0xd7, + 0xe7, 0xcf, 0xe5, 0xd5, 0x4a, 0x0a, 0x2e, 0x2a, 0x05, 0xd1, 0x6f, 0x70, 0xdf, 0x4d, 0x5c, 0x75, 0xd6, 0xac, 0x95, + 0x3e, 0xf4, 0xad, 0xcf, 0xda, 0xb8, 0x2f, 0x0c, 0x8e, 0xc1, 0xce, 0x47, 0xc4, 0x40, 0x1a, 0x54, 0xba, 0xc5, 0xa1, + 0x09, 0xd0, 0xa5, 0x43, 0x0a, 0x59, 0x32, 0x95, 0xa9, 0x12, 0x54, 0x7c, 0xe3, 0xf7, 0x52, 0x56, 0xa3, 0xbf, 0xd6, + 0xbc, 0xb8, 0x3b, 0xe5, 0x39, 0xc7, 0x31, 0x0a, 0x92, 0x58, 0x5c, 0xc7, 0x65, 0x40, 0x7c, 0xcb, 0xab, 0xe0, 0x20, + 0x35, 0x61, 0x63, 0x76, 0xaa, 0x46, 0xad, 0x57, 0x81, 0xbe, 0x32, 0xca, 0x37, 0x06, 0x43, 0x13, 0x51, 0x05, 0x7d, + 0xaf, 0xd5, 0x3d, 0xad, 0x6e, 0x58, 0x40, 0xfc, 0xb9, 0xd2, 0x0b, 0xb5, 0x5e, 0x37, 0x63, 0x6e, 0x98, 0x08, 0x41, + 0xa3, 0x27, 0xf5, 0xa2, 0xf6, 0xdc, 0xd2, 0x54, 0x64, 0xdc, 0x68, 0x93, 0xf3, 0x4b, 0x90, 0xf1, 0x39, 0x73, 0xa1, + 0x49, 0x5d, 0x53, 0x05, 0x55, 0x18, 0x6d, 0x6e, 0x1b, 0xe9, 0xf4, 0x0e, 0xdc, 0xd9, 0x8c, 0xd9, 0x91, 0x76, 0x69, + 0xac, 0x69, 0xc1, 0xcb, 0x95, 0x14, 0x25, 0x84, 0x71, 0xee, 0x8d, 0xe9, 0x55, 0x9c, 0x89, 0x2a, 0xce, 0xc4, 0x71, + 0xb9, 0xe2, 0x49, 0xf5, 0x1e, 0x6e, 0x71, 0xca, 0xea, 0xa6, 0x2e, 0xe1, 0x4a, 0x97, 0xec, 0x61, 0x30, 0x35, 0x15, + 0xf7, 0xd8, 0x71, 0x92, 0xd5, 0x1f, 0xd1, 0x52, 0x62, 0x2c, 0x54, 0x5d, 0x7c, 0x7c, 0x5e, 0xca, 0x7c, 0x5d, 0x81, + 0x76, 0xf7, 0xa2, 0x8a, 0x0e, 0x9e, 0xae, 0x6e, 0xa7, 0xea, 0x06, 0x13, 0x3d, 0x3d, 0x58, 0xdd, 0xf6, 0xb2, 0xab, + 0x95, 0x2c, 0xaa, 0x58, 0x54, 0x53, 0x85, 0x48, 0x96, 0xc4, 0x79, 0x12, 0x4e, 0xc6, 0xe3, 0xaf, 0xf6, 0x86, 0x7b, + 0x90, 0x81, 0x4c, 0x3f, 0x0d, 0x95, 0xcb, 0xd1, 0x70, 0x32, 0x1e, 0x4f, 0xa5, 0xba, 0xdb, 0x45, 0xa3, 0x49, 0x8d, + 0xf5, 0x0c, 0x13, 0x3d, 0x33, 0x23, 0x7e, 0xbb, 0x8a, 0x45, 0x0a, 0xf1, 0xeb, 0x74, 0xf1, 0x07, 0x4f, 0xc7, 0x8d, + 0xf2, 0xed, 0xa7, 0xcf, 0xea, 0x3f, 0x6a, 0x13, 0xd6, 0xda, 0xb4, 0xfb, 0xf9, 0x1f, 0x87, 0x6a, 0xbe, 0x8f, 0x0e, + 0xf7, 0xf5, 0x8f, 0x3f, 0xea, 0x7a, 0xfa, 0xa6, 0x08, 0xe7, 0xff, 0x0a, 0xd5, 0x7c, 0x1e, 0x17, 0x45, 0x7c, 0x57, + 0x43, 0x24, 0x4f, 0xe1, 0xbc, 0x49, 0xa8, 0xb7, 0x0d, 0xe8, 0x01, 0x99, 0x5e, 0x08, 0x06, 0xdf, 0xbc, 0xaf, 0xc2, + 0x80, 0x97, 0xab, 0x21, 0x17, 0x55, 0x56, 0xdd, 0x0d, 0x31, 0x4f, 0x80, 0x9f, 0x1a, 0xde, 0xec, 0x79, 0x61, 0x88, + 0xcd, 0x45, 0xc1, 0xf9, 0x27, 0x1e, 0x2a, 0xe3, 0xe8, 0x31, 0x1a, 0x47, 0x8f, 0xa9, 0x1a, 0x8c, 0xc9, 0x37, 0x54, + 0x77, 0x66, 0xf2, 0x0d, 0x98, 0x20, 0x65, 0xed, 0x6f, 0x94, 0x71, 0x62, 0x34, 0xa6, 0xd7, 0xaf, 0xf2, 0x6c, 0x05, + 0x4c, 0xf0, 0x52, 0xff, 0xa8, 0x09, 0x7d, 0xcf, 0xdb, 0xd9, 0x47, 0xa3, 0xd1, 0xf3, 0x82, 0x8e, 0x46, 0xa3, 0x8f, + 0x59, 0x4d, 0xe8, 0x4a, 0x74, 0xbc, 0x7f, 0xcf, 0xe9, 0xb9, 0x4c, 0xef, 0xa2, 0x20, 0xa0, 0xcb, 0x2c, 0x4d, 0xb9, + 0x50, 0x65, 0x9d, 0xa6, 0xed, 0xbc, 0xaa, 0x85, 0x08, 0xfc, 0xa3, 0xdb, 0x88, 0x10, 0x44, 0x84, 0x9e, 0xec, 0xf4, + 0x6c, 0x34, 0x1a, 0x9d, 0xa6, 0xa6, 0x5a, 0xc7, 0x90, 0xbf, 0x41, 0xf3, 0x01, 0x67, 0x97, 0x0f, 0xd6, 0x37, 0x26, + 0xda, 0xc9, 0xfe, 0x7f, 0x0f, 0x67, 0xf3, 0xf1, 0xf0, 0xdb, 0xd1, 0xe2, 0xf1, 0x3e, 0x0d, 0x02, 0x1f, 0xb4, 0x3a, + 0xd4, 0xd6, 0x1c, 0xd3, 0xf2, 0x70, 0x3c, 0x25, 0xe5, 0x80, 0x3d, 0xb5, 0xbe, 0x34, 0x5f, 0x3d, 0x05, 0x24, 0x52, + 0x14, 0xa1, 0x06, 0x4e, 0xfa, 0x87, 0x57, 0x91, 0xd7, 0x02, 0xf0, 0xd1, 0x6c, 0x24, 0x03, 0xa3, 0x05, 0x1c, 0x47, + 0x50, 0x5e, 0x6d, 0x4d, 0x23, 0x7a, 0x8c, 0x65, 0x26, 0x2a, 0xe8, 0x78, 0x5a, 0xde, 0x64, 0x55, 0xb2, 0xc4, 0xc0, + 0x46, 0x71, 0xc9, 0x83, 0xaf, 0x82, 0xa8, 0x64, 0x07, 0xcf, 0xa6, 0x0a, 0xde, 0x17, 0x93, 0x52, 0x7e, 0x09, 0x89, + 0xdf, 0x8e, 0x11, 0x02, 0x95, 0x68, 0x8f, 0x45, 0xac, 0xf1, 0x55, 0x2e, 0x63, 0xf0, 0xe0, 0x2c, 0x35, 0xcf, 0x62, + 0x4f, 0x02, 0x6b, 0x7f, 0xd1, 0x6a, 0x8e, 0x84, 0xe6, 0x84, 0x92, 0xc9, 0xfd, 0x92, 0xca, 0xaf, 0x26, 0xe8, 0x15, + 0x04, 0x6e, 0xd5, 0x11, 0x1c, 0x77, 0xd6, 0xb2, 0x41, 0x2f, 0x9f, 0x94, 0xed, 0xcf, 0xff, 0x77, 0x49, 0x17, 0x83, + 0x7d, 0x37, 0x34, 0x27, 0xda, 0x7d, 0xb5, 0x42, 0x46, 0xa9, 0x0a, 0x9f, 0xa7, 0xc4, 0x1a, 0x9f, 0x72, 0x76, 0xb4, + 0x31, 0xdd, 0x19, 0x55, 0x45, 0x76, 0x15, 0x12, 0xdd, 0x2b, 0x07, 0x8a, 0x19, 0x44, 0xd9, 0x08, 0xd7, 0x0f, 0x58, + 0x8b, 0x78, 0x9d, 0xbc, 0xe6, 0x45, 0x95, 0x25, 0xea, 0xfd, 0x75, 0xe3, 0x3d, 0x50, 0x03, 0xd5, 0xa0, 0x77, 0x05, + 0x83, 0x79, 0x3e, 0x29, 0x00, 0xb4, 0xb3, 0xe4, 0xc5, 0x35, 0xf7, 0xe9, 0x46, 0x10, 0xd4, 0xae, 0x99, 0x97, 0x8d, + 0x60, 0x13, 0xf0, 0xd5, 0xbb, 0x02, 0x30, 0x37, 0x42, 0x90, 0x9a, 0x42, 0x28, 0x1c, 0xb8, 0xc0, 0x57, 0x55, 0x91, + 0x9d, 0xaf, 0x2b, 0x8e, 0xc1, 0x3e, 0xbc, 0xb8, 0x89, 0xca, 0x09, 0x8f, 0x87, 0x01, 0xfe, 0x08, 0xa8, 0x0a, 0xb8, + 0x61, 0x3c, 0xec, 0xe0, 0x85, 0xfa, 0xe5, 0xde, 0xa8, 0x3d, 0xc2, 0xde, 0xa4, 0x21, 0x04, 0xd7, 0xc1, 0x87, 0x00, + 0x96, 0x14, 0xa1, 0x27, 0x78, 0xaa, 0x86, 0xc1, 0x45, 0x9e, 0xad, 0x74, 0x52, 0x35, 0xea, 0x68, 0x3e, 0x94, 0xda, + 0x91, 0x1c, 0x50, 0x2f, 0x3d, 0xc6, 0xf4, 0x42, 0xa5, 0xab, 0xa2, 0x9c, 0x11, 0xca, 0x3b, 0x3d, 0x31, 0x2e, 0x4c, + 0x1f, 0x87, 0xc8, 0x2f, 0xef, 0x0a, 0x15, 0xfa, 0x85, 0x2f, 0x00, 0xfc, 0x0a, 0x6e, 0xf7, 0xbb, 0xf1, 0x5d, 0x54, + 0xf6, 0x33, 0xce, 0xf6, 0xff, 0x7b, 0x1e, 0x0f, 0x3f, 0x8d, 0x87, 0xdf, 0x2e, 0x06, 0xe1, 0xd0, 0xfe, 0x24, 0x8f, + 0x1f, 0xed, 0xd3, 0x57, 0xdc, 0x72, 0x25, 0xb0, 0xf0, 0x1b, 0xc1, 0x6d, 0xd4, 0x4a, 0x08, 0xa2, 0x00, 0x6f, 0x14, + 0x6e, 0x35, 0x4e, 0x00, 0xe0, 0x2f, 0xf8, 0xaf, 0x00, 0x8d, 0x84, 0xdc, 0x45, 0x03, 0xf4, 0x03, 0xea, 0xf7, 0xd1, + 0x93, 0x86, 0x81, 0x1c, 0x88, 0x27, 0x54, 0x0c, 0x14, 0xa2, 0xcb, 0x98, 0x28, 0xd8, 0x5f, 0x9b, 0x7d, 0xbb, 0xed, + 0xb5, 0x25, 0x3f, 0xf8, 0xa5, 0x9f, 0x69, 0x62, 0xe6, 0x1d, 0x6e, 0x28, 0x2b, 0xb9, 0x0a, 0x11, 0x1b, 0x4f, 0xff, + 0xca, 0x19, 0xc4, 0x9a, 0xbc, 0xce, 0xc0, 0x87, 0xc1, 0x7e, 0x31, 0x9e, 0x01, 0xdb, 0x00, 0x77, 0x9c, 0x82, 0x5f, + 0x64, 0xe0, 0xd6, 0x2c, 0x62, 0xbc, 0x60, 0xdb, 0x25, 0xd1, 0xef, 0xf7, 0xf2, 0x2c, 0xcc, 0x35, 0xce, 0x72, 0x5e, + 0x1b, 0xb1, 0x3b, 0xea, 0x84, 0x41, 0xdc, 0xae, 0x87, 0x60, 0xa8, 0x86, 0xa0, 0xe8, 0x68, 0x8b, 0xab, 0xd7, 0xd6, + 0x53, 0x98, 0xde, 0xaa, 0xfa, 0x8a, 0xd1, 0x9f, 0x32, 0x13, 0x58, 0x48, 0xbb, 0xe6, 0x58, 0xd7, 0x1c, 0x23, 0xed, + 0xe9, 0xf7, 0x45, 0x83, 0xfc, 0x74, 0x16, 0x1e, 0x04, 0xaa, 0x54, 0xb9, 0x53, 0x16, 0xe5, 0xb6, 0x34, 0x6f, 0x0c, + 0x6b, 0x9a, 0x67, 0x36, 0xae, 0xcb, 0xac, 0xd7, 0x0b, 0x43, 0x74, 0x68, 0xc4, 0x52, 0xb1, 0x36, 0x08, 0xef, 0x63, + 0x12, 0x46, 0x57, 0x20, 0xab, 0x0b, 0xcf, 0x38, 0x41, 0xbe, 0x0c, 0x4c, 0xd6, 0x54, 0xb5, 0x5e, 0x4e, 0x78, 0x6c, + 0xe4, 0xcb, 0x46, 0xd0, 0x20, 0x2f, 0x29, 0xea, 0x4d, 0xdc, 0x8e, 0x7d, 0xd4, 0x42, 0x6a, 0xdc, 0xd4, 0xd3, 0x9e, + 0x26, 0x15, 0x3d, 0xd6, 0xab, 0xd4, 0x2f, 0xb0, 0x2c, 0xb0, 0xe4, 0x83, 0xd0, 0x9e, 0xa6, 0x15, 0x98, 0xe1, 0xda, + 0x66, 0x30, 0xf4, 0xc3, 0xa1, 0x2d, 0x42, 0x67, 0xd4, 0xb6, 0x84, 0xb0, 0x6d, 0x83, 0xb0, 0xf2, 0x9e, 0xc8, 0x57, + 0x4f, 0x3d, 0x46, 0x38, 0xe4, 0x66, 0x33, 0x8b, 0x06, 0x86, 0xf9, 0x95, 0x6c, 0x36, 0x4f, 0x37, 0xd7, 0x8b, 0x8a, + 0x29, 0x60, 0xbb, 0xad, 0x04, 0xc1, 0xbf, 0x1f, 0xb3, 0x19, 0xfe, 0xcd, 0xfa, 0xfd, 0x5e, 0x88, 0xbf, 0x38, 0x06, + 0xef, 0x99, 0x8b, 0x05, 0xfb, 0x08, 0x32, 0x15, 0x12, 0x61, 0xaa, 0x32, 0x7e, 0x63, 0x15, 0x58, 0xc0, 0x99, 0x0f, + 0x54, 0x2e, 0xcc, 0x64, 0x2f, 0x2f, 0xae, 0x21, 0xc7, 0xad, 0x71, 0xca, 0x46, 0x59, 0xa2, 0x5c, 0x17, 0xb2, 0x51, + 0x9c, 0x67, 0x71, 0xc9, 0xcb, 0xed, 0x56, 0x1f, 0x8e, 0x49, 0xc1, 0x81, 0x3d, 0x55, 0x54, 0xaa, 0x64, 0x1d, 0xa9, + 0x6e, 0xfc, 0x65, 0x58, 0xe0, 0x3e, 0xe5, 0xf3, 0xc2, 0xd0, 0x88, 0x3d, 0xb8, 0xbc, 0x33, 0x75, 0x2b, 0xed, 0x85, + 0x05, 0x34, 0xaf, 0x24, 0x64, 0x83, 0xa9, 0x9e, 0x45, 0x6b, 0xcc, 0xc4, 0xbc, 0x58, 0x40, 0x18, 0x99, 0x62, 0x01, + 0x36, 0x53, 0x5c, 0x80, 0x17, 0x49, 0x0c, 0x30, 0x71, 0x31, 0x99, 0x42, 0x3c, 0x73, 0x55, 0x4e, 0xbc, 0x30, 0xf7, + 0xcb, 0xc4, 0x21, 0x65, 0xc0, 0xab, 0xda, 0xa0, 0x89, 0xd9, 0x86, 0xa3, 0x4e, 0x90, 0x13, 0x93, 0xdf, 0x4f, 0x15, + 0x84, 0xb8, 0x13, 0x47, 0xc2, 0x65, 0xc5, 0x76, 0xe1, 0x65, 0x07, 0x62, 0x8c, 0x1a, 0x9c, 0xf2, 0x33, 0x83, 0xa3, + 0x31, 0x38, 0x37, 0xde, 0x09, 0x52, 0x84, 0x31, 0xd9, 0x48, 0x76, 0x25, 0x43, 0x31, 0x8f, 0x17, 0xa0, 0xac, 0x8b, + 0x17, 0x60, 0x59, 0x63, 0x0c, 0x30, 0x41, 0x5e, 0xc5, 0xbd, 0xd0, 0x4f, 0x14, 0x57, 0x88, 0xf4, 0xac, 0x5c, 0x1f, + 0x15, 0xed, 0xd0, 0x17, 0x78, 0xbd, 0x57, 0xe6, 0xb8, 0x59, 0x8f, 0x05, 0x12, 0x1b, 0x02, 0xc6, 0x46, 0x3a, 0x4d, + 0xb5, 0xd6, 0xbd, 0x31, 0xf3, 0xc0, 0xa7, 0xd9, 0x48, 0xc8, 0xea, 0xec, 0x02, 0x44, 0x28, 0x3e, 0x1a, 0x3c, 0xf2, + 0x8b, 0xb8, 0xb3, 0xcc, 0x5b, 0xdb, 0xa2, 0x92, 0x1d, 0x6d, 0x00, 0xa4, 0x4f, 0x47, 0x8b, 0x52, 0x72, 0x8a, 0x92, + 0xd4, 0x6e, 0x53, 0xc0, 0x4a, 0xf2, 0x17, 0x30, 0x04, 0x1b, 0xdb, 0x13, 0x4e, 0xa7, 0x08, 0xf1, 0x89, 0xa6, 0x88, + 0xac, 0x18, 0x96, 0x14, 0xc7, 0xb6, 0x44, 0xd4, 0x4f, 0x5b, 0x96, 0x1d, 0x0c, 0x13, 0x15, 0xf7, 0x45, 0xea, 0x51, + 0xa2, 0x20, 0xa0, 0x7a, 0xc8, 0x41, 0x62, 0x6b, 0x1b, 0x08, 0x0f, 0xc8, 0x23, 0x7a, 0x63, 0xfd, 0x7d, 0xd6, 0x79, + 0x76, 0xa1, 0x39, 0x2a, 0xd7, 0xbb, 0xc2, 0x8c, 0x11, 0x9e, 0x64, 0x06, 0x26, 0xdf, 0x3b, 0xcf, 0x8c, 0x9a, 0xa2, + 0xe7, 0xe1, 0x93, 0x1d, 0x63, 0x44, 0xba, 0x7b, 0x06, 0xdd, 0x04, 0xaf, 0xea, 0xb0, 0xd1, 0xae, 0x14, 0x84, 0x84, + 0xa9, 0x45, 0x13, 0xb3, 0x9e, 0x35, 0xa0, 0xde, 0x6e, 0x7b, 0x7a, 0xae, 0xee, 0x9f, 0xbb, 0xed, 0xb6, 0x87, 0xdd, + 0x7a, 0x91, 0x76, 0x5b, 0x81, 0x57, 0xea, 0x83, 0xf6, 0xf8, 0x73, 0x37, 0xfe, 0xdc, 0x20, 0x79, 0x94, 0x8e, 0x66, + 0xda, 0xfa, 0x20, 0x1c, 0x6e, 0x7a, 0xd7, 0x68, 0xd2, 0xf7, 0x59, 0x28, 0xe9, 0x4a, 0x34, 0xaa, 0xab, 0x9d, 0x49, + 0xe5, 0x83, 0xeb, 0xff, 0xe1, 0x55, 0x80, 0x47, 0x9c, 0xda, 0xd9, 0xf7, 0x36, 0xa8, 0x68, 0xb4, 0x85, 0x23, 0x45, + 0xe8, 0x01, 0x49, 0xb8, 0xaf, 0x65, 0x2d, 0x6e, 0xf3, 0x34, 0x7b, 0x98, 0x3e, 0xbd, 0x4e, 0x7d, 0xab, 0x7b, 0xb7, + 0xcc, 0x32, 0x73, 0xe0, 0x55, 0x14, 0x07, 0x34, 0xea, 0xa2, 0x7d, 0x57, 0x59, 0x59, 0x82, 0x97, 0x07, 0x5c, 0x9f, + 0x4f, 0xb9, 0x0f, 0x37, 0x77, 0x59, 0x35, 0x37, 0xe9, 0x69, 0x36, 0xcf, 0x16, 0xdb, 0x6d, 0x88, 0x7f, 0xbb, 0x5a, + 0xe4, 0x68, 0xf2, 0x1c, 0x74, 0x78, 0x18, 0xb9, 0x87, 0xe9, 0xc6, 0x79, 0x9b, 0xff, 0x93, 0x68, 0x38, 0x09, 0x1c, + 0x03, 0xbd, 0x98, 0x3d, 0x02, 0x19, 0x8c, 0x71, 0xea, 0x17, 0x33, 0xbd, 0x66, 0x20, 0xfa, 0x96, 0x88, 0x00, 0x47, + 0x17, 0x1b, 0x89, 0x46, 0x16, 0x9c, 0xd4, 0x04, 0x2c, 0x36, 0x6d, 0x79, 0x1f, 0x2c, 0x6d, 0xab, 0x8a, 0x3b, 0x6f, + 0x49, 0x73, 0x5c, 0x07, 0xce, 0xb6, 0xdf, 0x0c, 0xb1, 0x29, 0xbb, 0x5a, 0x20, 0xf7, 0xcb, 0x6b, 0xda, 0x1b, 0xd7, + 0x09, 0xcc, 0xda, 0xa6, 0xb6, 0x8c, 0x9f, 0x2d, 0xfd, 0x27, 0x3d, 0xb8, 0xca, 0xf8, 0x69, 0x6e, 0xac, 0x12, 0xec, + 0xbe, 0xf1, 0x7c, 0x07, 0x20, 0x1c, 0x9b, 0x4f, 0x8f, 0x4f, 0x33, 0x8f, 0x1e, 0x03, 0xd1, 0x31, 0x1f, 0x95, 0xee, + 0x23, 0xbb, 0x7b, 0xfd, 0x00, 0x78, 0xf3, 0xaa, 0x5d, 0xd0, 0xbc, 0x5c, 0x40, 0x20, 0x51, 0xaf, 0xbc, 0xc2, 0xf2, + 0x99, 0x31, 0xbb, 0x04, 0x32, 0x54, 0x10, 0x08, 0x54, 0xdd, 0x75, 0x2e, 0xc4, 0xaa, 0xc3, 0xca, 0x7c, 0x24, 0x61, + 0x47, 0x21, 0x9a, 0x73, 0x06, 0xb3, 0xe0, 0xbf, 0x82, 0x41, 0x39, 0x08, 0xa2, 0x20, 0x0a, 0x02, 0x32, 0x28, 0xe0, + 0x17, 0xe2, 0x8c, 0x11, 0x8c, 0x51, 0x02, 0x1d, 0x7e, 0xc7, 0x99, 0xcf, 0x88, 0xbc, 0x6c, 0x84, 0xb1, 0x74, 0x03, + 0x70, 0x2e, 0x65, 0xce, 0x63, 0xf4, 0xb1, 0x78, 0xc7, 0x59, 0x46, 0xe8, 0x3b, 0xef, 0x54, 0x7e, 0xc4, 0x1b, 0xc1, + 0xed, 0x76, 0x87, 0xed, 0x15, 0x0f, 0x33, 0xda, 0x1b, 0xd3, 0x77, 0x9c, 0x44, 0x59, 0xc3, 0x79, 0x98, 0x43, 0xcf, + 0x2a, 0xcb, 0x5a, 0x51, 0x43, 0x6e, 0x50, 0xac, 0x8b, 0x2c, 0x93, 0x93, 0xe1, 0xaa, 0x39, 0x15, 0xb8, 0xee, 0xec, + 0x7a, 0x01, 0x49, 0x99, 0xd0, 0x2c, 0x9d, 0x0d, 0x5f, 0x6d, 0x5b, 0xf6, 0xa2, 0x75, 0x0a, 0x79, 0x0d, 0x51, 0xd1, + 0x0f, 0x1d, 0x01, 0x35, 0xb4, 0xe2, 0xb2, 0x02, 0x97, 0x5d, 0xd3, 0x1e, 0x6e, 0xda, 0x63, 0x9a, 0xf1, 0x01, 0x62, + 0xc4, 0x71, 0x6c, 0x19, 0xd8, 0x4d, 0x38, 0x3c, 0x1b, 0xe7, 0xfb, 0xb2, 0x4b, 0x6f, 0x5d, 0x2d, 0x1e, 0x61, 0xed, + 0x79, 0x2b, 0x24, 0x04, 0x48, 0x4b, 0x53, 0xe9, 0x76, 0x1b, 0x04, 0x30, 0xc0, 0xfd, 0x7e, 0x0f, 0xb8, 0x56, 0xc3, + 0x4e, 0x9a, 0x5b, 0xb3, 0x25, 0xf6, 0x8a, 0xc2, 0x63, 0x20, 0x4a, 0xcd, 0x7f, 0x06, 0x01, 0xc5, 0x73, 0x37, 0x04, + 0xfb, 0x4a, 0x76, 0xb4, 0x29, 0xfa, 0xfd, 0x17, 0x05, 0x3e, 0xa0, 0x1c, 0x14, 0xc4, 0xba, 0x3a, 0x6e, 0x85, 0x61, + 0x9f, 0xd4, 0x87, 0x38, 0x16, 0x79, 0x16, 0x3a, 0xc2, 0x52, 0x19, 0xc2, 0xc2, 0x15, 0x23, 0x1d, 0xc4, 0x41, 0x4d, + 0x3a, 0x07, 0xab, 0x72, 0xc1, 0x86, 0x7b, 0xbd, 0x4f, 0x00, 0x0b, 0x9e, 0x79, 0xc3, 0xf2, 0xde, 0x03, 0x00, 0xeb, + 0xf5, 0x70, 0xa1, 0xb8, 0x97, 0xaf, 0x1a, 0xe8, 0x93, 0xf8, 0xd2, 0xb2, 0xeb, 0x33, 0x2d, 0x2b, 0x19, 0x8d, 0x46, + 0x55, 0xad, 0x24, 0x1f, 0x8e, 0xbc, 0xb4, 0x50, 0x2b, 0x65, 0x9c, 0xf2, 0x14, 0x2c, 0xbd, 0x0d, 0xa5, 0x9b, 0x2f, + 0xe8, 0x8a, 0x8b, 0x54, 0xfd, 0xf4, 0xd0, 0x26, 0x1b, 0xc4, 0x35, 0x6b, 0xea, 0x2c, 0xec, 0xf0, 0x43, 0xc0, 0x47, + 0xfb, 0x30, 0x73, 0xe9, 0x1a, 0x96, 0x16, 0xc4, 0x91, 0x71, 0xc1, 0x43, 0x97, 0x07, 0xb0, 0xfe, 0xcc, 0x21, 0x89, + 0x9f, 0xc2, 0xcf, 0x99, 0x49, 0xeb, 0xf8, 0x0c, 0x67, 0x33, 0x2a, 0xd5, 0x8d, 0xa0, 0xfd, 0x1a, 0x12, 0x89, 0x41, + 0x36, 0x6e, 0x30, 0x14, 0xad, 0xbb, 0x0d, 0x5c, 0xf9, 0x2d, 0xbd, 0xf3, 0x69, 0x10, 0x60, 0x5b, 0x63, 0x31, 0x00, + 0x18, 0x8a, 0x3f, 0x50, 0x55, 0x63, 0xae, 0x28, 0xa6, 0x61, 0x2a, 0xd1, 0xde, 0x71, 0x5c, 0x47, 0x8d, 0xab, 0xac, + 0x60, 0xa5, 0xb5, 0xe5, 0x75, 0x6f, 0x69, 0x61, 0x4b, 0x40, 0x35, 0x18, 0xee, 0x04, 0xf0, 0x19, 0x91, 0xea, 0x40, + 0x90, 0xdd, 0x07, 0x07, 0x00, 0x9a, 0xe1, 0x79, 0x18, 0xc2, 0x1f, 0x58, 0x38, 0xb0, 0x2c, 0x55, 0x3f, 0x97, 0xd3, + 0x18, 0xce, 0xdd, 0x5c, 0xed, 0xf0, 0xd9, 0x12, 0x14, 0x79, 0x6a, 0x4e, 0xcd, 0xe5, 0x2b, 0x6f, 0xec, 0xf7, 0x98, + 0x60, 0x1e, 0x33, 0xdb, 0xf0, 0x5b, 0x4f, 0xb7, 0xf5, 0x85, 0x75, 0x03, 0x27, 0xed, 0x85, 0xd3, 0x5e, 0x6c, 0x97, + 0x06, 0xe2, 0xae, 0x6e, 0x08, 0x11, 0x5e, 0x6b, 0x62, 0x91, 0x35, 0x64, 0x3a, 0x16, 0x1b, 0x43, 0xb5, 0xa9, 0x78, + 0xae, 0x15, 0xe2, 0xe5, 0x54, 0x5d, 0x98, 0x5a, 0xa9, 0x4c, 0x18, 0x84, 0x99, 0x12, 0x16, 0x55, 0x06, 0x3e, 0xfb, + 0x15, 0x52, 0x5c, 0x5b, 0xcf, 0x5b, 0x5c, 0xbe, 0x99, 0x69, 0xb3, 0xfd, 0xf4, 0x55, 0x1e, 0x5f, 0x6e, 0xb7, 0x61, + 0xf7, 0x0b, 0x30, 0xbf, 0x2c, 0x95, 0x46, 0x0d, 0x9c, 0x1e, 0x42, 0xf4, 0x73, 0xbe, 0x27, 0xe7, 0xc4, 0x71, 0x72, + 0xed, 0xe6, 0xcd, 0x76, 0x52, 0x8c, 0xc0, 0x02, 0x4e, 0x5c, 0xa4, 0x03, 0x2d, 0x15, 0x9c, 0xb6, 0x8c, 0xf7, 0x36, + 0xbd, 0xa3, 0x54, 0x78, 0xb5, 0xd0, 0x24, 0xa4, 0x72, 0xf7, 0x12, 0x3b, 0x6a, 0xc0, 0x39, 0xa9, 0x3b, 0x08, 0x38, + 0xa9, 0xe9, 0xc6, 0x5a, 0xc5, 0xa9, 0x49, 0xf0, 0x5e, 0xe9, 0xa1, 0x4b, 0xb4, 0x13, 0xb7, 0xdb, 0x56, 0x65, 0x0b, + 0xf5, 0x71, 0x2f, 0x67, 0x89, 0x3a, 0x1e, 0x50, 0xe8, 0xa2, 0x8e, 0x86, 0x7c, 0x41, 0x0a, 0xbd, 0x72, 0xb4, 0x6a, + 0x75, 0x57, 0x32, 0x50, 0xaa, 0x55, 0x90, 0xd7, 0xc4, 0xba, 0x6b, 0x65, 0x8d, 0xc5, 0x95, 0x13, 0x52, 0xd8, 0x84, + 0x2f, 0x2d, 0xc5, 0xc2, 0x0a, 0xf6, 0xc6, 0xd4, 0x17, 0x2e, 0x11, 0xda, 0xee, 0x36, 0xc4, 0x24, 0x83, 0x75, 0xb3, + 0xdd, 0xbe, 0x2e, 0xc2, 0x79, 0xb6, 0xa0, 0x72, 0x94, 0xa5, 0x08, 0x21, 0x66, 0x3c, 0x74, 0x6d, 0x17, 0xcc, 0xc4, + 0x50, 0xd7, 0x1e, 0x2f, 0xc9, 0x14, 0x6b, 0x93, 0xe4, 0x28, 0x3e, 0x97, 0x85, 0x5a, 0x6b, 0x84, 0xe0, 0xe1, 0xfe, + 0x67, 0x0a, 0x31, 0xdc, 0xcc, 0xba, 0xfb, 0x75, 0xe7, 0x86, 0xf8, 0x27, 0x04, 0x12, 0x28, 0xd9, 0xeb, 0x62, 0x74, + 0x9e, 0x89, 0x14, 0x77, 0xaa, 0x8a, 0x8a, 0xab, 0xd6, 0x41, 0xb3, 0xe5, 0xf6, 0x5e, 0x6c, 0x89, 0x02, 0xc4, 0x35, + 0x16, 0x9a, 0xf1, 0xac, 0x9c, 0xa5, 0x48, 0x46, 0xb1, 0x21, 0x51, 0xe9, 0x45, 0x45, 0xf7, 0x79, 0x1a, 0xd3, 0x43, + 0xb7, 0x06, 0xc1, 0x55, 0x73, 0x67, 0x23, 0xcd, 0x17, 0x84, 0xa8, 0x09, 0x90, 0xb0, 0x51, 0xcd, 0xa9, 0x75, 0x29, + 0x1e, 0x66, 0x95, 0xcf, 0xf4, 0x41, 0x7c, 0x29, 0x80, 0x87, 0xf5, 0xb6, 0xf7, 0x95, 0xf0, 0x58, 0x1b, 0x7c, 0xbb, + 0xdd, 0x5e, 0x8a, 0x79, 0x10, 0x78, 0x8c, 0xe6, 0x77, 0x4a, 0x62, 0xde, 0x1b, 0x53, 0x58, 0xf1, 0xbe, 0x4b, 0x5b, + 0x37, 0xa9, 0xb5, 0x16, 0xa8, 0x3b, 0x5c, 0x1f, 0xf0, 0x3c, 0x25, 0x8e, 0x76, 0x54, 0x4e, 0xa5, 0xd5, 0x95, 0x63, + 0x57, 0x04, 0x06, 0x86, 0xfe, 0x21, 0x65, 0x1b, 0x30, 0xc7, 0x03, 0x6b, 0x1b, 0xf4, 0x53, 0x52, 0x5a, 0x98, 0x31, + 0x1a, 0xb3, 0xc8, 0x75, 0x15, 0x1d, 0x70, 0x1d, 0xbd, 0x9d, 0x47, 0x7f, 0x7b, 0x36, 0xa6, 0x45, 0x2c, 0x52, 0x79, + 0x05, 0x2a, 0x08, 0x50, 0x86, 0xa0, 0xe1, 0xbf, 0xa6, 0x06, 0xa0, 0x41, 0x70, 0x03, 0xf0, 0xcf, 0x4e, 0xa7, 0x41, + 0x5b, 0x93, 0x8f, 0x49, 0xaa, 0x8a, 0x9c, 0xb5, 0xa1, 0xcc, 0x54, 0x72, 0x48, 0x1e, 0x97, 0x80, 0xe7, 0x88, 0xcd, + 0x52, 0x36, 0x17, 0x6a, 0xb3, 0xa9, 0xd7, 0x8a, 0x1d, 0xb9, 0x6d, 0x14, 0x6d, 0xd6, 0xa2, 0xb6, 0x93, 0x98, 0x2f, + 0xa6, 0xb7, 0x56, 0x18, 0x38, 0x35, 0xad, 0xb9, 0xd9, 0x81, 0x4e, 0xb3, 0xf5, 0x99, 0xdc, 0x04, 0x88, 0x03, 0x0c, + 0xd7, 0xed, 0xfc, 0x66, 0x41, 0xe8, 0x2d, 0xbb, 0xb5, 0x62, 0xd5, 0x1b, 0x2b, 0x17, 0x31, 0x69, 0x37, 0x83, 0x09, + 0x5c, 0xc6, 0x59, 0x61, 0x5f, 0x68, 0x75, 0x43, 0xd1, 0xd1, 0x36, 0x69, 0x3f, 0xef, 0x68, 0x37, 0x5c, 0xf0, 0xad, + 0x58, 0xc7, 0xb9, 0x21, 0x4d, 0x15, 0x7a, 0x74, 0xa0, 0xb7, 0x43, 0x40, 0x73, 0x36, 0xa6, 0x4b, 0x9a, 0xe2, 0x05, + 0x9a, 0xae, 0xc1, 0x2c, 0xe5, 0x02, 0xfa, 0xda, 0xed, 0x93, 0x7c, 0xa1, 0x7a, 0x22, 0xbc, 0x25, 0x0a, 0xbe, 0x1c, + 0x29, 0x78, 0x65, 0xe5, 0x3c, 0x36, 0x73, 0x08, 0x78, 0x2c, 0xaa, 0x44, 0xef, 0xa4, 0xb8, 0x04, 0x65, 0x2a, 0x1c, + 0x81, 0xa6, 0x6a, 0xc4, 0x12, 0x0e, 0x70, 0x7b, 0xf1, 0x34, 0x20, 0x14, 0xa4, 0xba, 0x6b, 0xbb, 0x22, 0x6f, 0xd9, + 0xd1, 0xe6, 0x16, 0xcc, 0x62, 0xab, 0x75, 0xd9, 0xfa, 0xca, 0x26, 0xbb, 0x8f, 0x6b, 0x82, 0x6d, 0xf7, 0x36, 0x48, + 0x78, 0x4b, 0x6f, 0xc8, 0xe6, 0xa6, 0xdf, 0x0f, 0xa1, 0x3f, 0x84, 0xea, 0x0e, 0xdd, 0x76, 0x76, 0xe8, 0xd6, 0x67, + 0x7e, 0xad, 0x9e, 0x4f, 0x79, 0x43, 0x7c, 0x40, 0x13, 0x2d, 0xba, 0x8a, 0xef, 0x60, 0x53, 0x47, 0x15, 0x55, 0x95, + 0x47, 0x09, 0x05, 0x15, 0x70, 0xc6, 0xcb, 0x53, 0x8e, 0xb1, 0x4d, 0xf5, 0xd3, 0x3b, 0xcd, 0xab, 0xad, 0xcd, 0xda, + 0x2c, 0xd7, 0xe7, 0x60, 0x11, 0x70, 0xce, 0xa3, 0x2b, 0x4d, 0x4b, 0x2e, 0x3d, 0xa6, 0xfe, 0x0c, 0x47, 0x25, 0xb8, + 0x88, 0xb3, 0x9c, 0xa7, 0x01, 0xbd, 0x68, 0xf6, 0x3f, 0xd4, 0xb6, 0x52, 0xcb, 0xc6, 0x99, 0x7b, 0x1d, 0x92, 0xcd, + 0xff, 0xd8, 0x40, 0xbd, 0x09, 0x31, 0x22, 0xaa, 0x59, 0xd0, 0x27, 0x0c, 0x62, 0x63, 0x06, 0xe5, 0x3a, 0x49, 0x78, + 0x59, 0x06, 0x46, 0xa9, 0xb5, 0x66, 0x6b, 0x73, 0x9e, 0x3d, 0x62, 0x47, 0x8f, 0x7a, 0x8c, 0xdd, 0x12, 0x9a, 0x68, + 0x9d, 0x90, 0xa9, 0x31, 0xf2, 0xb4, 0x40, 0xba, 0x43, 0x51, 0x76, 0x11, 0x9e, 0xa0, 0x90, 0xa5, 0xbd, 0xcf, 0xcd, + 0x89, 0xac, 0xbe, 0xd1, 0x46, 0x17, 0x91, 0x4a, 0x04, 0xd9, 0xf8, 0x0d, 0x02, 0xf6, 0x42, 0xb3, 0x03, 0xb2, 0x59, + 0xb2, 0x53, 0x7a, 0x66, 0x4d, 0x60, 0xe0, 0xf5, 0x89, 0x4a, 0x34, 0xa3, 0xac, 0x88, 0xae, 0x32, 0x72, 0xb9, 0x0b, + 0x49, 0x74, 0x16, 0x12, 0x3f, 0x37, 0x2c, 0xad, 0xeb, 0x10, 0xc5, 0xcc, 0x66, 0xc3, 0xab, 0xee, 0x3e, 0x6a, 0x6c, + 0x2b, 0xe3, 0x53, 0x7d, 0x6b, 0xd3, 0xc8, 0x14, 0xfa, 0x3a, 0x9c, 0xf4, 0xfb, 0xf0, 0x57, 0xd3, 0x0f, 0xbc, 0xa5, + 0xe0, 0x2f, 0xf6, 0x88, 0xd4, 0x09, 0x0b, 0x00, 0x8e, 0x30, 0xe7, 0x55, 0x73, 0x02, 0x1f, 0xb1, 0xa3, 0xcd, 0xa3, + 0xf0, 0xb4, 0x31, 0x73, 0x77, 0x21, 0x5e, 0xaa, 0x92, 0x9e, 0x37, 0x4f, 0x66, 0x20, 0x56, 0xa1, 0xd9, 0xaf, 0xb7, + 0xcc, 0xea, 0x13, 0x80, 0x48, 0xdd, 0x5a, 0x87, 0x52, 0xfc, 0xd8, 0x74, 0x99, 0x6c, 0x52, 0xd6, 0x66, 0xa2, 0x94, + 0x8a, 0xa4, 0xb9, 0x08, 0xa0, 0xdf, 0x30, 0x1c, 0x35, 0xc0, 0x7b, 0xeb, 0xb1, 0x37, 0x43, 0xe3, 0x8d, 0xa9, 0xa1, + 0x67, 0x1b, 0xbd, 0xbc, 0x1d, 0x85, 0x30, 0x63, 0x11, 0xdd, 0xba, 0x63, 0x31, 0x3c, 0xa5, 0x27, 0x50, 0xe1, 0x9b, + 0x10, 0xa3, 0xe9, 0x92, 0xba, 0x9e, 0xae, 0xd5, 0x56, 0xba, 0x21, 0x34, 0xc7, 0x28, 0x3e, 0x5e, 0xdb, 0xee, 0xa8, + 0x11, 0xda, 0x13, 0xca, 0xc3, 0x5b, 0x5a, 0xd1, 0x1b, 0xcb, 0x22, 0x38, 0xf9, 0xb1, 0x97, 0x9f, 0xd0, 0x73, 0x4f, + 0x60, 0x52, 0xb4, 0x35, 0x80, 0x3f, 0xa0, 0x7e, 0x38, 0xab, 0xa7, 0x56, 0xca, 0xe1, 0x29, 0x7c, 0xc9, 0x06, 0xe4, + 0x0a, 0x7a, 0xb1, 0xc6, 0xec, 0x28, 0x06, 0x1d, 0xd4, 0xce, 0xee, 0xf0, 0x26, 0xa5, 0x0c, 0xd1, 0xfa, 0xce, 0x41, + 0x3c, 0xfd, 0x13, 0x34, 0x7d, 0x90, 0x16, 0xa6, 0x74, 0x8d, 0x02, 0x1e, 0xd0, 0x37, 0xf5, 0xfb, 0x39, 0x3e, 0xd7, + 0x9e, 0x25, 0x16, 0xf6, 0x78, 0x49, 0xe8, 0xd2, 0x8b, 0x1b, 0x05, 0xd2, 0x66, 0xc7, 0x2a, 0x00, 0x2b, 0x92, 0x40, + 0x23, 0x12, 0xb0, 0xd4, 0xf1, 0xc4, 0x65, 0x1b, 0x34, 0x20, 0x89, 0x4a, 0x0a, 0x59, 0x22, 0x09, 0xfc, 0x30, 0x82, + 0x10, 0x45, 0x31, 0x88, 0x7b, 0xf5, 0xf2, 0x8a, 0x6b, 0x6a, 0xc0, 0x89, 0x22, 0x98, 0x60, 0x9d, 0x4e, 0x81, 0xd8, + 0x8a, 0xf5, 0x0a, 0x3c, 0x2f, 0x1d, 0x27, 0x8e, 0x2c, 0x01, 0x1a, 0xa4, 0xf9, 0xd2, 0x69, 0xb7, 0xbc, 0x3d, 0xd1, + 0x52, 0xc5, 0xe6, 0xde, 0x8b, 0x85, 0xe5, 0x1e, 0x2b, 0x7f, 0x3b, 0xd0, 0x5e, 0x58, 0xed, 0x88, 0xa8, 0xc1, 0xca, + 0xae, 0x6d, 0xd7, 0x86, 0xd2, 0x50, 0xdd, 0x2b, 0xc7, 0x04, 0x54, 0x74, 0x15, 0x57, 0xcb, 0x28, 0x1b, 0xc1, 0x9f, + 0xed, 0x36, 0xd8, 0x0f, 0xc0, 0x02, 0xf2, 0x97, 0xf7, 0x3f, 0x45, 0x18, 0x9e, 0xe9, 0x97, 0xf7, 0x3f, 0x6d, 0xb7, + 0xcf, 0xc6, 0x63, 0xc3, 0x15, 0x38, 0xb5, 0x0e, 0xf0, 0x07, 0x86, 0x6d, 0xb0, 0x4b, 0x76, 0xbb, 0x7d, 0x06, 0x1c, + 0x84, 0x62, 0x1b, 0xcc, 0x2e, 0x56, 0x8e, 0x5c, 0x8a, 0xd5, 0xd0, 0x3b, 0x12, 0xb0, 0xea, 0x76, 0x58, 0x8a, 0x5d, + 0xea, 0xa3, 0x42, 0x30, 0xea, 0x45, 0xff, 0xb2, 0x53, 0x60, 0x49, 0xc1, 0x74, 0x35, 0x58, 0x56, 0xd5, 0xaa, 0x8c, + 0xf6, 0xf7, 0xe3, 0x55, 0x36, 0x2a, 0x33, 0xd8, 0xe6, 0xe5, 0xf5, 0x25, 0x00, 0x2a, 0x04, 0xb4, 0xf1, 0x6e, 0x2d, + 0x32, 0xf3, 0x62, 0x41, 0x97, 0x19, 0xae, 0x49, 0x30, 0x3b, 0xc8, 0xb9, 0xd5, 0x4d, 0x4e, 0x89, 0x7d, 0x00, 0x9b, + 0xc3, 0xed, 0xb6, 0xc1, 0x2f, 0x1c, 0x8d, 0x9e, 0xcd, 0x96, 0x99, 0x36, 0xe8, 0xe4, 0x66, 0xff, 0x93, 0xc8, 0x4b, + 0x43, 0xc5, 0x27, 0x99, 0xbe, 0xcc, 0x80, 0xcf, 0x63, 0x6f, 0x45, 0xe8, 0xb3, 0x5c, 0x8d, 0xd6, 0x00, 0x1b, 0x9b, + 0x5d, 0xdc, 0x8d, 0x52, 0x0e, 0x11, 0x29, 0x02, 0xab, 0xae, 0x59, 0x66, 0xc4, 0xb7, 0xa9, 0xb8, 0x6b, 0xa9, 0xc2, + 0xde, 0x0a, 0xcf, 0x59, 0x85, 0x1b, 0x47, 0x99, 0xde, 0x24, 0x0a, 0x5f, 0xa2, 0x10, 0x95, 0xa3, 0x31, 0x9d, 0x13, + 0x48, 0x65, 0x1e, 0x13, 0x8a, 0x39, 0xdc, 0xbb, 0x5f, 0x52, 0x67, 0x2e, 0xe3, 0x0b, 0xf7, 0x5e, 0xfa, 0x32, 0x93, + 0x5b, 0x09, 0xa0, 0x48, 0xaa, 0xf6, 0x9f, 0x3f, 0x23, 0x35, 0xfe, 0x57, 0xaa, 0x35, 0x00, 0xbd, 0x9f, 0xa1, 0x26, + 0x47, 0x10, 0xb0, 0x15, 0x53, 0x1f, 0x4d, 0xdf, 0x4a, 0xe6, 0x3f, 0xa0, 0x6e, 0x47, 0xb0, 0x8d, 0x8a, 0x9f, 0x13, + 0x55, 0xb4, 0xe0, 0xe9, 0x5a, 0xa4, 0xb1, 0x48, 0xee, 0x22, 0x5e, 0x4f, 0xb1, 0x24, 0x66, 0x23, 0x64, 0xfd, 0xdc, + 0xec, 0xc2, 0x4f, 0x45, 0xc3, 0x04, 0x9c, 0x96, 0xfe, 0xb6, 0xf2, 0x36, 0x93, 0x65, 0x9c, 0x91, 0x29, 0x57, 0x88, + 0xdd, 0x56, 0xdf, 0x63, 0x4e, 0xf0, 0xa7, 0x07, 0x4f, 0x09, 0xbd, 0x95, 0xd3, 0x12, 0x41, 0xe9, 0x44, 0x6a, 0x5d, + 0x35, 0xb1, 0x5f, 0x53, 0x88, 0xe2, 0x20, 0x18, 0x84, 0xee, 0x34, 0xed, 0x53, 0x7c, 0x9f, 0x2d, 0xfb, 0xad, 0x29, + 0x5b, 0x92, 0x8d, 0x80, 0x8e, 0x49, 0xe7, 0xed, 0xe9, 0xed, 0xd9, 0x99, 0xf7, 0x1b, 0x34, 0xe1, 0xa0, 0xba, 0x81, + 0x76, 0x15, 0x64, 0x1a, 0xa3, 0xd8, 0x2c, 0xc6, 0xda, 0xad, 0x89, 0x08, 0x82, 0x4e, 0x97, 0xb3, 0xb0, 0xdd, 0x4e, + 0x88, 0x2f, 0x81, 0x04, 0x0a, 0x5c, 0xb9, 0x28, 0x27, 0x21, 0x51, 0x17, 0x32, 0x3d, 0x59, 0xd7, 0x92, 0x05, 0x7a, + 0x8d, 0x1d, 0x04, 0xf4, 0x98, 0xdb, 0xa7, 0x80, 0xbe, 0x2f, 0xd8, 0x31, 0x1f, 0x04, 0x43, 0x8c, 0xaf, 0x1a, 0xd0, + 0x1b, 0xa9, 0x1e, 0xc1, 0x43, 0x18, 0x58, 0x2e, 0xfa, 0xaa, 0x60, 0x08, 0x2b, 0xf4, 0x57, 0xca, 0x26, 0xdf, 0xfc, + 0xdd, 0xcd, 0xef, 0xb9, 0x16, 0xb3, 0x83, 0x50, 0xdc, 0x5e, 0x4f, 0x80, 0xf8, 0x55, 0xfc, 0x0a, 0xac, 0xab, 0xb5, + 0xc4, 0xdb, 0x4d, 0xcf, 0x9f, 0xc2, 0x97, 0xa3, 0xdb, 0x4f, 0x4a, 0xf3, 0x09, 0x04, 0xa9, 0x71, 0x92, 0x72, 0xf7, + 0xdd, 0x47, 0xe9, 0x2a, 0x82, 0xd1, 0x02, 0xc4, 0xba, 0x7b, 0x2b, 0x39, 0x6b, 0x0a, 0xff, 0xb1, 0xce, 0xf7, 0x18, + 0x3b, 0x44, 0x9e, 0xe2, 0xf4, 0x37, 0xc0, 0xb0, 0xef, 0xfc, 0x5b, 0x99, 0x35, 0x24, 0x3a, 0x57, 0x1f, 0x01, 0xfd, + 0x1f, 0xeb, 0xf1, 0x3b, 0x46, 0x49, 0x5f, 0x12, 0xe7, 0x08, 0x57, 0xc4, 0x4b, 0x34, 0xd5, 0xeb, 0x8d, 0x6b, 0xfa, + 0xa9, 0x30, 0x2f, 0xb4, 0x82, 0xc3, 0xbe, 0x35, 0x0a, 0x0f, 0x3c, 0xf3, 0x7e, 0x15, 0x0d, 0x41, 0xf7, 0x6f, 0xb8, + 0x37, 0x7e, 0x15, 0x2c, 0xc3, 0x9b, 0x72, 0x96, 0x99, 0x3b, 0xdc, 0x4d, 0x26, 0x52, 0x79, 0xc3, 0x58, 0xb0, 0x16, + 0xca, 0x7c, 0x35, 0x0d, 0x66, 0x9b, 0x3a, 0x52, 0xc9, 0xee, 0xfb, 0xb7, 0x8d, 0x13, 0x36, 0x1b, 0x04, 0xa7, 0x95, + 0x2c, 0xe2, 0x4b, 0x1e, 0x4c, 0xb5, 0x8a, 0x22, 0xcb, 0xfa, 0xfd, 0x0c, 0x90, 0x61, 0x9c, 0xf6, 0x0e, 0x9e, 0x2c, + 0x35, 0x33, 0x21, 0xae, 0xad, 0xce, 0x02, 0xde, 0x9a, 0xd1, 0x3c, 0xae, 0x60, 0x97, 0xf9, 0x4a, 0x8a, 0x3f, 0x5b, + 0x92, 0x6c, 0xac, 0xbf, 0x21, 0xc3, 0xb6, 0xf2, 0x99, 0x73, 0xc0, 0x98, 0xb9, 0x91, 0x2a, 0xc8, 0x5d, 0x0f, 0x18, + 0x21, 0x24, 0x02, 0xc2, 0x59, 0x4c, 0xdc, 0x09, 0x13, 0xfe, 0xd1, 0x05, 0xc6, 0x89, 0x31, 0x30, 0xce, 0x47, 0x19, + 0x72, 0x7a, 0xcc, 0x07, 0x49, 0x63, 0xb6, 0xfe, 0x54, 0x25, 0xd2, 0x6b, 0x49, 0xe8, 0x19, 0xfc, 0x1e, 0xb7, 0x78, + 0xa0, 0x46, 0x70, 0x4a, 0x77, 0x73, 0xda, 0x7f, 0x55, 0x90, 0xe1, 0x5f, 0xe0, 0xdd, 0x15, 0xdb, 0xcb, 0x72, 0x02, + 0x8b, 0x3b, 0xf6, 0x8a, 0xa7, 0xb9, 0x6a, 0x71, 0x42, 0x3c, 0x62, 0x91, 0xfb, 0xc4, 0x02, 0x46, 0xd4, 0x30, 0x1a, + 0x3f, 0x9e, 0x9e, 0xbc, 0xd5, 0x98, 0x4d, 0xb9, 0xff, 0x01, 0x8c, 0xa8, 0x96, 0xb6, 0xdb, 0x01, 0x5f, 0x8e, 0xd0, + 0x60, 0x3b, 0x75, 0x83, 0xdd, 0xef, 0x9b, 0xb4, 0xa3, 0xd2, 0xcb, 0xe6, 0xc4, 0xa0, 0x3b, 0x4a, 0x9b, 0xa5, 0x32, + 0xa8, 0xed, 0x2a, 0x1c, 0xcd, 0x67, 0x8d, 0x58, 0xd5, 0xfb, 0x30, 0x5c, 0xd2, 0xd8, 0xca, 0xca, 0xed, 0x6e, 0xc2, + 0x91, 0x4d, 0x80, 0xeb, 0x53, 0x50, 0x56, 0xcd, 0x39, 0x68, 0x41, 0x67, 0x02, 0x47, 0xb4, 0xdd, 0x86, 0x10, 0x81, + 0xa3, 0x18, 0x4e, 0x66, 0x61, 0x31, 0x1c, 0xaa, 0x81, 0x2f, 0x08, 0x89, 0x3e, 0x15, 0xf3, 0x6c, 0xa1, 0x10, 0x7b, + 0xfc, 0x9d, 0xf4, 0x6b, 0xa1, 0x38, 0xe5, 0xde, 0xaf, 0x82, 0x6c, 0x7e, 0x4b, 0x31, 0xe6, 0xa0, 0xd3, 0x6c, 0x66, + 0x20, 0x61, 0x3d, 0xae, 0x88, 0x5a, 0x47, 0x76, 0x36, 0x40, 0x15, 0x8b, 0xa6, 0xb0, 0xa0, 0x6e, 0xf1, 0xc4, 0x7a, + 0x46, 0xef, 0x41, 0x25, 0x88, 0x6a, 0xc1, 0x6e, 0x0c, 0xd7, 0xda, 0x27, 0x11, 0x4a, 0xca, 0x49, 0x93, 0x99, 0xb1, + 0xa2, 0xc1, 0x02, 0x84, 0xa4, 0x71, 0x59, 0xbd, 0x91, 0x69, 0x76, 0x91, 0x01, 0x62, 0x82, 0xf3, 0x9f, 0x93, 0x8d, + 0x37, 0xcf, 0xd5, 0xbc, 0x74, 0x25, 0xce, 0x2c, 0xcc, 0x47, 0xd7, 0x5b, 0x5a, 0x90, 0xa8, 0x00, 0x1a, 0xe5, 0x6b, + 0x79, 0xfe, 0xb1, 0x63, 0x15, 0xb2, 0xfb, 0xe1, 0x54, 0xd9, 0x0e, 0xf1, 0x23, 0x56, 0x11, 0xef, 0xb4, 0xae, 0x94, + 0x48, 0xa3, 0xa3, 0x6d, 0x40, 0x0c, 0x5b, 0xf6, 0x2d, 0x6a, 0xf8, 0x20, 0xcc, 0xa0, 0x93, 0xfc, 0xa0, 0x67, 0x74, + 0x6c, 0x0d, 0x24, 0x7d, 0x2d, 0x82, 0xaf, 0xd1, 0x91, 0x4e, 0x94, 0x69, 0x24, 0xa6, 0x90, 0xe8, 0xd7, 0x0b, 0xad, + 0xb1, 0x8c, 0xb2, 0xaf, 0xc8, 0xff, 0x5e, 0x77, 0xef, 0x57, 0xb1, 0xdd, 0xc2, 0x24, 0x7b, 0x1e, 0x57, 0xb0, 0xa9, + 0x51, 0x2b, 0x84, 0xb3, 0x73, 0x5c, 0xa1, 0x76, 0xac, 0x17, 0x96, 0x40, 0x1e, 0xc0, 0x56, 0xa4, 0x41, 0x19, 0x24, + 0xfb, 0x54, 0xcc, 0xc5, 0xc2, 0x89, 0x72, 0xa4, 0xc2, 0xfb, 0x92, 0xa3, 0x94, 0xc3, 0x55, 0x2c, 0x2c, 0x18, 0xf2, + 0xab, 0xa3, 0x8b, 0x42, 0x5e, 0x81, 0xa4, 0xc4, 0x30, 0x54, 0x96, 0xd7, 0xc5, 0x55, 0x5b, 0x12, 0xda, 0x3b, 0x03, + 0x10, 0x96, 0x02, 0x04, 0x2f, 0x8d, 0x1a, 0x62, 0xb6, 0x51, 0xbb, 0x2b, 0xba, 0x97, 0x1c, 0x50, 0xa7, 0xbb, 0x76, + 0xeb, 0x4d, 0xd9, 0x66, 0x5b, 0x71, 0xe1, 0x9f, 0x50, 0xfa, 0x31, 0x1f, 0x14, 0x3e, 0x95, 0xc0, 0x8d, 0xaf, 0x36, + 0x59, 0x76, 0x71, 0x87, 0x4b, 0xbf, 0x6a, 0x8c, 0x5f, 0xbf, 0xdf, 0x53, 0x0b, 0xa1, 0x91, 0x0a, 0xcc, 0xb7, 0xcf, + 0x4c, 0x55, 0x46, 0x53, 0x6a, 0x2f, 0xc1, 0x95, 0xb3, 0x1f, 0x41, 0x45, 0x5c, 0x57, 0x64, 0x32, 0x35, 0x40, 0x7b, + 0x5e, 0x56, 0xb8, 0x95, 0x05, 0x78, 0xec, 0x04, 0x64, 0xbb, 0xe5, 0x61, 0xa0, 0x0f, 0x9d, 0xc0, 0xdf, 0x92, 0xa7, + 0xc8, 0xac, 0xd9, 0xc7, 0x9f, 0xb5, 0xe0, 0x1f, 0x5b, 0xf0, 0x13, 0x8a, 0x3b, 0xad, 0xcc, 0xbf, 0x95, 0xd6, 0x2d, + 0xee, 0xdf, 0xc9, 0x34, 0xa1, 0xa8, 0x4c, 0xa8, 0xfd, 0x4a, 0x7f, 0x37, 0xc1, 0x92, 0x54, 0xf6, 0x0f, 0x12, 0x3e, + 0x98, 0x35, 0x9e, 0x58, 0xe3, 0xc9, 0x70, 0xba, 0x95, 0x86, 0x21, 0x40, 0xa1, 0x9f, 0x97, 0xb9, 0xa2, 0xfa, 0xf9, + 0xe7, 0x35, 0x5f, 0xf3, 0x66, 0x8b, 0x6d, 0xd2, 0x03, 0x0d, 0xf6, 0xf2, 0x68, 0x4a, 0xe1, 0x24, 0xea, 0xdc, 0x48, + 0xd4, 0x45, 0xcd, 0x32, 0x54, 0x27, 0x78, 0x35, 0x4f, 0xf5, 0xb0, 0x37, 0x13, 0xd1, 0x5a, 0x49, 0x59, 0x62, 0xc0, + 0x5a, 0x47, 0x1e, 0x92, 0xbb, 0xb5, 0x8e, 0x3b, 0x0d, 0x75, 0x69, 0x0a, 0x25, 0xc0, 0x0a, 0x17, 0xe0, 0x08, 0xfa, + 0xa9, 0x08, 0x39, 0x5c, 0x53, 0x95, 0x7e, 0x41, 0x53, 0xf2, 0xc4, 0x53, 0xd4, 0x6a, 0x45, 0xba, 0xfd, 0x28, 0xc7, + 0x6e, 0xf8, 0xc6, 0x09, 0x39, 0x31, 0x42, 0x7f, 0x77, 0x2c, 0xe5, 0x0c, 0x2d, 0x1e, 0xd4, 0x09, 0xd6, 0xcb, 0x5b, + 0x0a, 0x14, 0x73, 0x74, 0x59, 0x75, 0xcd, 0x6b, 0xb4, 0x7d, 0x59, 0xf6, 0xfb, 0xb9, 0xad, 0x27, 0x65, 0x47, 0x9b, + 0xa5, 0xd9, 0x87, 0xa8, 0x98, 0xc2, 0x5d, 0x9f, 0x68, 0xfe, 0x2a, 0xd4, 0x57, 0x6d, 0x99, 0xf3, 0x11, 0x47, 0x5c, + 0x8c, 0x9c, 0xd4, 0x3f, 0xab, 0xa9, 0x57, 0xe2, 0x7e, 0x55, 0xc9, 0x77, 0xc2, 0x58, 0x31, 0x3a, 0xa0, 0xfe, 0x54, + 0xa9, 0xbc, 0x5f, 0x16, 0x00, 0xf7, 0x24, 0xd8, 0x27, 0xb0, 0xaf, 0xd0, 0x08, 0xbf, 0x2d, 0x01, 0xff, 0x46, 0x71, + 0x03, 0x56, 0x81, 0x01, 0x46, 0x93, 0xed, 0x39, 0x4d, 0xe0, 0x80, 0x13, 0x5a, 0x45, 0x41, 0x85, 0x19, 0x1a, 0x6a, + 0x0b, 0xa3, 0xa7, 0x28, 0xe3, 0x56, 0x99, 0xbd, 0x1b, 0x63, 0xa7, 0x05, 0x5e, 0xc3, 0x9f, 0xcf, 0x0b, 0x3d, 0x6c, + 0xd4, 0x41, 0x7a, 0x74, 0x12, 0xd3, 0x1f, 0xb7, 0x70, 0x72, 0xb3, 0x70, 0x96, 0x35, 0x4b, 0xa0, 0x3b, 0x70, 0x41, + 0x8c, 0xfb, 0xfd, 0x1c, 0x8e, 0x4c, 0x33, 0xf2, 0x05, 0xcb, 0x69, 0xcc, 0x96, 0x54, 0x7b, 0xda, 0x5d, 0x56, 0x61, + 0x4e, 0x97, 0x56, 0xc6, 0x9b, 0x32, 0x50, 0x19, 0x6d, 0xb7, 0x21, 0xfc, 0xe9, 0xb6, 0x76, 0x49, 0xe7, 0x4b, 0xc8, + 0x00, 0x7f, 0x40, 0x22, 0x8a, 0xd8, 0xd7, 0xff, 0x56, 0xe3, 0x94, 0x9e, 0x28, 0xad, 0x59, 0x42, 0xd7, 0x4c, 0xd7, + 0x4f, 0x2f, 0xd8, 0xba, 0xb1, 0x14, 0xb6, 0xdb, 0xb0, 0x99, 0xc0, 0x34, 0xe7, 0x4a, 0xa6, 0x17, 0xa8, 0x93, 0x02, + 0x2a, 0x16, 0x5e, 0xe0, 0xf2, 0x4b, 0x09, 0x85, 0xe6, 0xce, 0x97, 0x0b, 0xa3, 0xc4, 0x84, 0x56, 0xc9, 0x2f, 0x1f, + 0x2a, 0xf3, 0xb5, 0xf1, 0x88, 0xfb, 0x3d, 0x0d, 0x13, 0x53, 0x24, 0x2a, 0x44, 0x67, 0xbf, 0x82, 0x2c, 0x47, 0x00, + 0x8e, 0xe5, 0xa9, 0xac, 0xe9, 0x8f, 0x29, 0xc4, 0x41, 0x87, 0x06, 0xbd, 0x2b, 0xe4, 0x55, 0x56, 0xf2, 0x10, 0xef, + 0x09, 0x9e, 0x66, 0xf4, 0x7e, 0x83, 0x0f, 0x6d, 0xed, 0xd1, 0x13, 0x64, 0xe3, 0x29, 0xf7, 0xeb, 0xef, 0x44, 0x38, + 0x87, 0x68, 0x95, 0x0b, 0xaa, 0xd5, 0xd5, 0x0e, 0x80, 0xca, 0xb3, 0xbd, 0x7a, 0x04, 0xa7, 0x9b, 0xbe, 0xbe, 0x55, + 0xa1, 0x33, 0x07, 0x90, 0xf6, 0x90, 0xac, 0x6b, 0xae, 0x77, 0x80, 0x3b, 0x12, 0xab, 0x35, 0xd0, 0x58, 0xb7, 0x35, + 0x3b, 0xed, 0x51, 0x3c, 0x26, 0x32, 0x33, 0x16, 0x29, 0xc6, 0xdc, 0xad, 0xd3, 0xa2, 0x68, 0x83, 0x66, 0x08, 0xbb, + 0x77, 0x1d, 0xbe, 0x6e, 0x45, 0x58, 0xbf, 0xdf, 0xf6, 0x05, 0x46, 0xc3, 0x98, 0x6b, 0xf7, 0x3c, 0x43, 0x37, 0x6c, + 0xb0, 0x8d, 0x9c, 0x87, 0xc8, 0x87, 0x99, 0x3a, 0x10, 0x65, 0x6d, 0x0d, 0xd8, 0x1e, 0x71, 0xbd, 0x69, 0x15, 0x3f, + 0xaf, 0x62, 0xce, 0xf6, 0xac, 0x71, 0x4a, 0xeb, 0x6b, 0x5c, 0x73, 0x5c, 0x15, 0x22, 0x6a, 0xeb, 0x19, 0x0f, 0xc3, + 0xce, 0x17, 0xb8, 0x33, 0x2b, 0x0c, 0x5e, 0x84, 0xa5, 0x92, 0x9d, 0xca, 0xf5, 0xe7, 0xb0, 0xc5, 0x41, 0x2a, 0x5f, + 0x7a, 0xfd, 0xfd, 0xdd, 0x17, 0x5f, 0xa0, 0x9b, 0x9a, 0xf3, 0x23, 0x08, 0x32, 0x81, 0x0e, 0x59, 0x4a, 0xf5, 0xf8, + 0x5d, 0x01, 0xd4, 0x1e, 0xe6, 0xe1, 0xbb, 0x82, 0x89, 0xf8, 0x3a, 0xbb, 0x8c, 0x2b, 0x59, 0x8c, 0xae, 0xb9, 0x48, + 0x65, 0x61, 0xa5, 0xc6, 0xc1, 0xf1, 0x6a, 0x95, 0xf3, 0x00, 0x4c, 0xe5, 0x2d, 0xa3, 0x6c, 0x2b, 0xcb, 0xf4, 0xe0, + 0x6a, 0x79, 0x7a, 0xa5, 0x45, 0xe7, 0xe5, 0xf5, 0x65, 0x10, 0xe1, 0xaf, 0x73, 0xf3, 0xe3, 0x2a, 0x2e, 0x3f, 0x06, + 0x91, 0xb5, 0xa9, 0x33, 0x3f, 0x50, 0x2a, 0x0f, 0xfe, 0x53, 0x20, 0xd3, 0xfd, 0xae, 0x00, 0xcb, 0x6c, 0x5b, 0xf1, + 0x61, 0x8c, 0xb5, 0x0e, 0x27, 0x64, 0xa6, 0x4a, 0xf4, 0xde, 0x25, 0xeb, 0x02, 0xac, 0xfd, 0x14, 0x96, 0xb1, 0xca, + 0x35, 0xc3, 0xca, 0x54, 0x45, 0x66, 0x56, 0xd6, 0x6c, 0x3f, 0xb4, 0x4e, 0x34, 0x73, 0xf4, 0x16, 0xd0, 0x0f, 0x64, + 0xff, 0x92, 0x96, 0x6b, 0xe6, 0xf9, 0xd8, 0x34, 0x5e, 0x3f, 0xda, 0xbf, 0x74, 0x0b, 0xf6, 0xd6, 0xde, 0xc9, 0x51, + 0x98, 0x08, 0x9e, 0xb5, 0x66, 0x7c, 0x91, 0x67, 0x05, 0xac, 0x9c, 0xc9, 0x78, 0x4c, 0xbd, 0xa5, 0xd5, 0xba, 0x39, + 0x3a, 0x24, 0xd7, 0xec, 0x71, 0xf5, 0x98, 0x93, 0x7d, 0xde, 0x32, 0xb5, 0x6d, 0x5b, 0xc7, 0x79, 0x9a, 0x7c, 0x65, + 0xba, 0x2f, 0xd6, 0x36, 0x22, 0xba, 0x72, 0xee, 0x73, 0x5e, 0xc1, 0xad, 0x6f, 0x4a, 0x43, 0xaf, 0x25, 0x00, 0xd1, + 0x69, 0x03, 0xfe, 0x82, 0x95, 0xeb, 0x51, 0xc5, 0xcb, 0x0a, 0x24, 0x2c, 0x28, 0xc2, 0x9b, 0x62, 0x6f, 0x0a, 0x77, + 0xe3, 0xf4, 0x1c, 0x76, 0xe0, 0x62, 0x8a, 0xee, 0x38, 0x31, 0x99, 0x95, 0x46, 0x2b, 0x1a, 0xe9, 0x5f, 0xae, 0x2f, + 0xb1, 0xee, 0x8b, 0x56, 0xe6, 0xd9, 0x9c, 0x0a, 0x8b, 0xdd, 0x55, 0x2e, 0x9d, 0xa8, 0xdf, 0x32, 0xe1, 0xca, 0x95, + 0x20, 0x20, 0xd3, 0x82, 0xf5, 0x0a, 0xb3, 0x8b, 0xe4, 0x1a, 0x08, 0x19, 0x18, 0xbe, 0x06, 0x6b, 0x51, 0x72, 0x63, + 0x05, 0xeb, 0xdd, 0xf3, 0x75, 0x82, 0x90, 0x82, 0x07, 0x6e, 0x82, 0x7e, 0x68, 0xdd, 0xbc, 0x1d, 0x25, 0xca, 0x20, + 0x1e, 0xb7, 0x76, 0xca, 0x41, 0x02, 0x01, 0xb8, 0xa7, 0x2a, 0x04, 0x87, 0x02, 0x59, 0x07, 0x57, 0x33, 0x8e, 0xe0, + 0xea, 0xca, 0x99, 0x8b, 0x1b, 0x80, 0x75, 0xe5, 0xcf, 0x65, 0x83, 0x0b, 0xeb, 0x11, 0x55, 0xe6, 0x8c, 0x53, 0x0c, + 0x62, 0x64, 0x09, 0xfa, 0xca, 0x52, 0xda, 0x4b, 0xd0, 0x34, 0x5e, 0xb1, 0x95, 0xf2, 0x01, 0xa0, 0xe7, 0x6c, 0xa5, + 0x8c, 0xfd, 0xf1, 0xeb, 0x33, 0xb6, 0xd2, 0xd2, 0xe0, 0xe9, 0xd5, 0xec, 0x7c, 0x76, 0x36, 0x60, 0x07, 0x51, 0xa8, + 0x0d, 0x18, 0x02, 0x87, 0xc4, 0x1f, 0x0c, 0x42, 0x8d, 0x77, 0x32, 0x50, 0x01, 0xb1, 0x88, 0xc7, 0x63, 0x23, 0x6e, + 0x56, 0x38, 0x1e, 0x62, 0xf0, 0xab, 0xe6, 0x0b, 0x12, 0x10, 0x6a, 0x4a, 0x43, 0x97, 0xc7, 0x70, 0x38, 0xd9, 0x9b, + 0x40, 0x2a, 0x66, 0x66, 0xaa, 0x30, 0x36, 0x26, 0x11, 0xc4, 0x3b, 0xed, 0xac, 0x17, 0xca, 0xed, 0xae, 0xd1, 0x40, + 0xae, 0x0c, 0xbe, 0xa8, 0xe2, 0xc9, 0xde, 0xb0, 0xab, 0x62, 0x1c, 0x85, 0x6b, 0xa3, 0x7c, 0x3b, 0x3b, 0x04, 0xf0, + 0xda, 0xb3, 0xa1, 0x2f, 0x97, 0x38, 0xdb, 0x7f, 0x4a, 0x1e, 0x3f, 0x25, 0xf4, 0x8c, 0x9d, 0x7d, 0xf5, 0x94, 0x9e, + 0x29, 0x72, 0xb2, 0x37, 0x89, 0xae, 0x99, 0xc5, 0x7c, 0x39, 0x50, 0x4d, 0xa0, 0x97, 0xa3, 0xb5, 0x50, 0x0b, 0x4c, + 0x3b, 0x34, 0x85, 0xdf, 0x8e, 0xf7, 0x82, 0xc1, 0x75, 0xbb, 0xe9, 0xd7, 0xed, 0xb6, 0x7a, 0x5e, 0x5d, 0x7b, 0x07, + 0xd1, 0x6e, 0x31, 0x93, 0xbf, 0x8f, 0xf7, 0xdc, 0x1c, 0x60, 0x7d, 0x0f, 0x8f, 0x89, 0x69, 0xd2, 0xce, 0xa8, 0xf8, + 0x35, 0x3d, 0xc1, 0x3e, 0x34, 0x8b, 0xec, 0xe8, 0xc3, 0xf0, 0xdf, 0xea, 0x44, 0x7d, 0xf6, 0xd5, 0x01, 0x90, 0x23, + 0x90, 0x81, 0x62, 0x89, 0x60, 0x86, 0x03, 0x4d, 0x01, 0x05, 0x99, 0x1e, 0x77, 0xaa, 0x87, 0x5f, 0x8d, 0x9a, 0x9a, + 0x91, 0x6b, 0x98, 0x1a, 0x6c, 0x0b, 0x7e, 0xa0, 0xba, 0xa1, 0xbf, 0xd1, 0xe8, 0x46, 0xda, 0xc9, 0xcc, 0xbc, 0xa4, + 0x36, 0xae, 0xdb, 0x35, 0x04, 0x30, 0x76, 0xf0, 0x82, 0x92, 0x7d, 0x7d, 0x78, 0xb9, 0x87, 0xab, 0x08, 0x50, 0xb2, + 0x58, 0xf0, 0xf5, 0xe0, 0x52, 0x6f, 0xee, 0xbd, 0x80, 0x0c, 0xbe, 0x0e, 0x8e, 0xbe, 0x1e, 0xc8, 0x41, 0x70, 0xb8, + 0x7f, 0x79, 0x14, 0x38, 0xe3, 0x7e, 0x08, 0xf1, 0xa8, 0x2a, 0x8a, 0x99, 0x30, 0x55, 0x24, 0xb6, 0xf6, 0xdc, 0xd6, + 0xab, 0x8c, 0xcf, 0x68, 0x3a, 0xb5, 0xc8, 0xdf, 0x61, 0xca, 0x62, 0xf3, 0x3b, 0x98, 0xf0, 0xab, 0x20, 0x72, 0x41, + 0x50, 0x67, 0x79, 0x14, 0xd3, 0x25, 0xbb, 0x15, 0x61, 0x4a, 0x93, 0xfd, 0x9c, 0x90, 0x28, 0x5c, 0x2a, 0xf0, 0x3c, + 0xf5, 0x3a, 0x81, 0x38, 0xae, 0xee, 0xf3, 0x5b, 0x11, 0x2e, 0x69, 0xbe, 0x9f, 0x90, 0x56, 0x11, 0x2e, 0x22, 0xcb, + 0xa6, 0xa6, 0x17, 0x2c, 0x5c, 0xd1, 0x4b, 0x60, 0xa6, 0xe4, 0x3a, 0xbc, 0x04, 0x2e, 0x6f, 0x3d, 0x5f, 0x2d, 0xd8, + 0x65, 0x43, 0xfa, 0x66, 0xf8, 0xe2, 0x0b, 0xeb, 0x93, 0x07, 0x3c, 0xa4, 0xf3, 0xc3, 0x4b, 0xc1, 0x06, 0xe0, 0x3a, + 0xe3, 0x37, 0xdf, 0xc9, 0x5b, 0x3d, 0x2f, 0xed, 0x29, 0xc6, 0x99, 0x69, 0x27, 0x26, 0xed, 0x84, 0xdc, 0xbf, 0x6f, + 0x6f, 0x62, 0x73, 0xb2, 0x97, 0xd1, 0x5a, 0xb9, 0xac, 0x5a, 0x86, 0xa4, 0x58, 0x33, 0xe4, 0xef, 0x51, 0x72, 0x6a, + 0x05, 0x9e, 0xec, 0x82, 0x57, 0xc9, 0xd2, 0x3f, 0xa8, 0xac, 0xd5, 0x80, 0x3d, 0x46, 0x2c, 0x0b, 0x85, 0x63, 0xff, + 0x26, 0x63, 0xc5, 0xda, 0x17, 0x68, 0xc4, 0xc8, 0xbd, 0xbd, 0xc9, 0x98, 0x17, 0x73, 0x35, 0x59, 0x7b, 0xa1, 0xea, + 0xbc, 0xf4, 0xbc, 0xc5, 0x7b, 0x39, 0xa5, 0x86, 0x91, 0x88, 0xee, 0x8d, 0x95, 0x19, 0xa5, 0x4a, 0xd4, 0x1a, 0x34, + 0x22, 0xd8, 0xd8, 0x05, 0xbf, 0x04, 0x27, 0x54, 0xee, 0xa9, 0xb3, 0x7d, 0x3b, 0xa5, 0xd2, 0x03, 0x96, 0xa5, 0x46, + 0x55, 0xee, 0x96, 0x99, 0x64, 0xd5, 0x20, 0x18, 0xfd, 0x59, 0x4a, 0x31, 0xc3, 0x3b, 0x23, 0x0b, 0xa6, 0x60, 0x25, + 0xa8, 0x6a, 0x19, 0x96, 0x43, 0x8e, 0x5a, 0x3c, 0xe3, 0x93, 0x2a, 0xf5, 0x8f, 0x8e, 0x20, 0xb9, 0xcb, 0x75, 0x2b, + 0x48, 0xee, 0xd3, 0xf1, 0x53, 0x3d, 0xd0, 0xe9, 0x5a, 0x3b, 0x1e, 0xfa, 0xfc, 0x36, 0xe2, 0x6b, 0xeb, 0xde, 0x53, + 0xad, 0x55, 0x28, 0x03, 0x2d, 0x56, 0x54, 0xae, 0xd4, 0x92, 0xde, 0xef, 0x22, 0x00, 0x16, 0xb1, 0x31, 0x1b, 0xef, + 0xda, 0x66, 0x85, 0xa0, 0xd1, 0x65, 0x47, 0x9b, 0x78, 0xc0, 0x12, 0xdd, 0xda, 0xc1, 0x84, 0xc6, 0x47, 0xac, 0xec, + 0xf7, 0xf3, 0x23, 0xa0, 0xa7, 0xda, 0x88, 0xa9, 0x80, 0x23, 0xff, 0x4b, 0x2b, 0x32, 0x45, 0x81, 0xcd, 0x9a, 0xba, + 0x5b, 0x63, 0x19, 0x89, 0xbe, 0x4c, 0xe9, 0xf2, 0x84, 0x67, 0xc0, 0xb4, 0x5e, 0xb7, 0x1c, 0x57, 0x76, 0x15, 0x47, + 0x9e, 0x0a, 0xcb, 0x8a, 0xf3, 0x2a, 0x1c, 0x6f, 0x3d, 0xbe, 0xc1, 0xbe, 0x61, 0xd3, 0x2e, 0xfc, 0x21, 0x84, 0x85, + 0xf0, 0x26, 0x83, 0xdb, 0x88, 0xb6, 0x93, 0x40, 0xe5, 0x8d, 0xb9, 0x4e, 0x28, 0x9b, 0xdb, 0xf5, 0xda, 0x33, 0x48, + 0x27, 0xe6, 0x40, 0xa9, 0x46, 0xd0, 0x1a, 0xcd, 0x82, 0xaa, 0x11, 0x8f, 0x1c, 0x0f, 0xef, 0x0c, 0x62, 0xb5, 0x7c, + 0x49, 0x53, 0x29, 0x1a, 0x80, 0x71, 0x01, 0x5c, 0x9e, 0x7e, 0x79, 0xff, 0xd3, 0x29, 0x8f, 0x8b, 0x64, 0xf9, 0x2e, + 0x2e, 0xe2, 0xab, 0x32, 0xdc, 0xa8, 0x31, 0x8a, 0x6b, 0x32, 0x15, 0x03, 0x26, 0xcd, 0x4a, 0x6a, 0xee, 0x4a, 0x4d, + 0x88, 0xb1, 0xce, 0x64, 0x5d, 0x56, 0xf2, 0xaa, 0x51, 0xe9, 0xba, 0xc8, 0xf0, 0xe3, 0x96, 0xcf, 0xe9, 0x3e, 0x00, + 0x79, 0x1a, 0x17, 0xd2, 0x48, 0xea, 0x42, 0x8c, 0xb9, 0x88, 0xd7, 0xf5, 0xf1, 0xb8, 0xd1, 0xf5, 0x92, 0x3d, 0x1b, + 0x3f, 0x99, 0xbe, 0xc9, 0xc2, 0x6c, 0x20, 0xc8, 0xa8, 0x5a, 0x72, 0xd1, 0x32, 0xe5, 0x54, 0x26, 0x01, 0xe8, 0xe3, + 0xd9, 0x63, 0xec, 0x60, 0x3c, 0x26, 0x9b, 0xb6, 0x78, 0x80, 0x87, 0xcb, 0x75, 0x58, 0x90, 0x99, 0xae, 0x23, 0x0a, + 0x04, 0xbf, 0xad, 0x02, 0x40, 0x72, 0xb4, 0x55, 0x19, 0x2e, 0x8d, 0x3d, 0x1b, 0x4f, 0xa8, 0xc4, 0x6e, 0x87, 0xa4, + 0xf6, 0x2a, 0x74, 0x33, 0x2f, 0x7d, 0x8f, 0x22, 0x69, 0x5c, 0x96, 0x76, 0x2a, 0x95, 0x6a, 0xcf, 0xcc, 0x5c, 0xd7, + 0x20, 0x06, 0x43, 0xa8, 0xeb, 0x2e, 0xbd, 0xba, 0x77, 0x9b, 0x6b, 0xcd, 0x76, 0xc0, 0x7b, 0x0d, 0x9a, 0xa1, 0xe4, + 0x2d, 0xe6, 0xad, 0x2b, 0xa2, 0xa6, 0xab, 0x35, 0x98, 0x15, 0xa3, 0x6c, 0x29, 0x4a, 0xd7, 0x14, 0x94, 0x82, 0xd1, + 0xc5, 0xda, 0x5b, 0xb8, 0x6f, 0x64, 0xe3, 0xc2, 0x92, 0xe9, 0xd5, 0xa2, 0xa4, 0x84, 0xea, 0xa6, 0x62, 0xa4, 0x84, + 0x91, 0xd2, 0xf0, 0x54, 0xbe, 0x17, 0x78, 0x9c, 0xe7, 0x41, 0xd4, 0xf2, 0x02, 0x3b, 0xae, 0xc8, 0x31, 0x38, 0x7a, + 0x99, 0x9c, 0x86, 0x02, 0xff, 0x98, 0x29, 0x10, 0xd3, 0xa1, 0xba, 0xdf, 0xe0, 0xe6, 0xff, 0x67, 0xc1, 0x02, 0x8f, + 0x6f, 0xbd, 0xc4, 0x6d, 0xf4, 0xcf, 0xc2, 0xa7, 0xa5, 0xcf, 0xa5, 0xef, 0xea, 0xe2, 0x49, 0x7b, 0xb3, 0x51, 0xb2, + 0xcc, 0xf2, 0xf4, 0xad, 0x4c, 0x39, 0x88, 0xcc, 0xd0, 0x1a, 0x94, 0x1d, 0x89, 0xc6, 0x0d, 0x0f, 0x8c, 0x18, 0x1b, + 0x37, 0xbe, 0x1f, 0x33, 0x90, 0x0d, 0x83, 0xd5, 0x37, 0x4b, 0x65, 0xb2, 0xbe, 0x02, 0x4c, 0x11, 0x25, 0x3f, 0x79, + 0x99, 0x73, 0x78, 0x0a, 0xf5, 0xf5, 0x0b, 0xdc, 0xe6, 0x4a, 0xdf, 0xe7, 0xfc, 0xc7, 0x8c, 0xfe, 0x88, 0x40, 0x27, + 0xf1, 0x0a, 0xe4, 0x1e, 0xcf, 0xa1, 0x6e, 0x84, 0xa9, 0xe5, 0x18, 0x1c, 0x08, 0xd1, 0x40, 0x44, 0xc5, 0x02, 0x05, + 0x75, 0x61, 0x80, 0x35, 0xd4, 0x05, 0x73, 0x78, 0x9e, 0xcb, 0xe4, 0xe3, 0xd4, 0xf8, 0xcc, 0x0f, 0x63, 0x8c, 0x99, + 0x1c, 0x0c, 0xc2, 0x6a, 0x16, 0x0c, 0xc7, 0xa3, 0xc9, 0xc1, 0x33, 0x38, 0xb7, 0x83, 0x71, 0x40, 0x06, 0x41, 0x5d, + 0xae, 0x62, 0x41, 0xcb, 0xeb, 0x4b, 0x5b, 0x06, 0x7e, 0x5c, 0x07, 0x83, 0x7f, 0x16, 0x9e, 0xe2, 0x1d, 0x34, 0x27, + 0x67, 0x32, 0x04, 0x1b, 0xfb, 0x35, 0x01, 0x49, 0x59, 0x4f, 0xf3, 0x93, 0xfa, 0x70, 0x63, 0x4a, 0xfb, 0x67, 0x0e, + 0x2f, 0x38, 0xec, 0x90, 0x40, 0x81, 0x34, 0x9e, 0x66, 0xa3, 0xd7, 0x4a, 0x91, 0xfb, 0xae, 0xe0, 0x70, 0x67, 0xee, + 0x39, 0xd3, 0x23, 0xa7, 0x90, 0x68, 0x66, 0x01, 0x37, 0xf2, 0xd7, 0xe2, 0x3a, 0xce, 0xb3, 0x74, 0xaf, 0xf9, 0x66, + 0xaf, 0xbc, 0x13, 0x55, 0x7c, 0x3b, 0x0a, 0x8c, 0x35, 0x21, 0xf7, 0x55, 0x4f, 0x80, 0x9e, 0x00, 0x5b, 0x00, 0x0c, + 0x88, 0x77, 0xcc, 0x4c, 0x66, 0x3c, 0x02, 0x8f, 0xc0, 0xa6, 0x0f, 0x64, 0x71, 0xe7, 0x5c, 0x92, 0xfc, 0xcd, 0x54, + 0xda, 0xab, 0x5e, 0xb9, 0x53, 0x90, 0xf5, 0x6a, 0x2b, 0x77, 0xdd, 0xfa, 0xec, 0x9b, 0x0e, 0xaf, 0xc0, 0x73, 0x09, + 0x6e, 0x91, 0xfd, 0x7e, 0x53, 0x50, 0x29, 0x8c, 0x8a, 0x78, 0x27, 0xb9, 0x46, 0xff, 0x76, 0x6f, 0x6c, 0x14, 0xc9, + 0x2d, 0x1f, 0x1e, 0x40, 0x9d, 0xc9, 0xbb, 0xe2, 0x76, 0x0e, 0x51, 0x5b, 0x77, 0xe3, 0x81, 0xd5, 0x06, 0xed, 0xb2, + 0xe6, 0x08, 0x2e, 0xbc, 0xd8, 0xcb, 0x60, 0x2c, 0x70, 0x56, 0x46, 0x4a, 0x8d, 0x6b, 0x48, 0x2d, 0xf8, 0x24, 0x4f, + 0xef, 0x21, 0x4b, 0x3d, 0x09, 0x8a, 0x1c, 0xcf, 0x62, 0xc8, 0x34, 0xde, 0x06, 0x1e, 0xbf, 0x93, 0x21, 0x48, 0xd3, + 0xb6, 0xdb, 0xe6, 0x08, 0x94, 0xdd, 0x03, 0x53, 0x92, 0xba, 0x36, 0xa6, 0x06, 0x1a, 0x6a, 0x0f, 0x35, 0x52, 0x11, + 0x67, 0x47, 0x6f, 0x40, 0x87, 0x08, 0xbe, 0xdf, 0x69, 0x56, 0x76, 0xbc, 0x98, 0x10, 0x3c, 0x79, 0x5f, 0xde, 0x66, + 0x65, 0x55, 0x46, 0xef, 0x53, 0x34, 0x84, 0x4a, 0xa4, 0x88, 0x5e, 0x41, 0x3c, 0xbd, 0x12, 0x7f, 0x97, 0xd1, 0x4f, + 0x29, 0x8d, 0xd3, 0x14, 0xd3, 0x5f, 0x14, 0xf0, 0xf3, 0x39, 0xa0, 0x3a, 0xe2, 0x4e, 0x88, 0xce, 0x25, 0xd8, 0xab, + 0x41, 0x34, 0xab, 0x8a, 0x03, 0x86, 0x66, 0x74, 0x2b, 0x28, 0x62, 0xb4, 0x61, 0xf6, 0x1f, 0x0a, 0x14, 0x0a, 0xa9, + 0x62, 0xbe, 0x13, 0xf6, 0x21, 0xfa, 0x11, 0x8b, 0x3c, 0x7e, 0xf7, 0xda, 0x0c, 0x69, 0x74, 0x27, 0xa9, 0xde, 0xda, + 0x78, 0x6c, 0x61, 0xe0, 0xb2, 0xe8, 0x72, 0x4d, 0xcf, 0xe2, 0x55, 0x16, 0x6d, 0x00, 0x7f, 0xe2, 0xdd, 0xeb, 0xe7, + 0xca, 0xc2, 0xe4, 0x45, 0x06, 0x8a, 0x83, 0xe3, 0x77, 0xaf, 0xdf, 0xc8, 0x74, 0x9d, 0xf3, 0xe8, 0x4c, 0x22, 0x69, + 0x3d, 0x7e, 0xf7, 0xfa, 0x67, 0x34, 0xf7, 0xfa, 0xa9, 0x80, 0xf7, 0xaf, 0x80, 0xb7, 0x8c, 0xe2, 0x35, 0xf4, 0x49, + 0xfd, 0x4e, 0xd6, 0xd8, 0x29, 0xaf, 0xd6, 0x32, 0xfa, 0x25, 0xad, 0x3d, 0x69, 0xd5, 0xbf, 0x0a, 0x9f, 0xda, 0x79, + 0x02, 0x9e, 0xdb, 0x3c, 0x13, 0x1f, 0x23, 0x2b, 0xda, 0x09, 0xa2, 0xaf, 0xf7, 0x6e, 0xaf, 0x72, 0x51, 0x46, 0xf8, + 0x82, 0xa1, 0x5d, 0x50, 0xb4, 0xbf, 0x7f, 0x73, 0x73, 0x33, 0xba, 0x79, 0x32, 0x92, 0xc5, 0xe5, 0xfe, 0xe4, 0xdb, + 0x6f, 0xbf, 0xdd, 0xc7, 0xb7, 0xc1, 0xd7, 0x6d, 0xb7, 0xf7, 0x8a, 0xf0, 0x01, 0x0b, 0x10, 0xa1, 0xfa, 0x6b, 0xb8, + 0xa2, 0x80, 0x16, 0x6e, 0xf0, 0x75, 0xf0, 0xb5, 0x3e, 0x74, 0xbe, 0x3e, 0x2c, 0xaf, 0x2f, 0x55, 0xf9, 0x5d, 0x25, + 0x1f, 0x8c, 0xc7, 0xe3, 0x7d, 0x90, 0x40, 0x7d, 0x3d, 0xe0, 0x83, 0xe0, 0x28, 0x18, 0x64, 0x70, 0xa1, 0x29, 0xaf, + 0x2f, 0x8f, 0x02, 0xcf, 0x34, 0xb7, 0xc1, 0x22, 0x3a, 0x10, 0x97, 0x60, 0xff, 0x92, 0x06, 0x5f, 0x07, 0xc4, 0xa5, + 0x7c, 0x05, 0x29, 0x5f, 0x1d, 0x3c, 0xf3, 0xd3, 0xfe, 0x97, 0x4a, 0x7b, 0xe2, 0xa7, 0x1d, 0x62, 0xda, 0x93, 0xe7, + 0x7e, 0xda, 0x91, 0x4a, 0x7b, 0xe9, 0xa7, 0xfd, 0xef, 0x72, 0x00, 0xa9, 0x7b, 0xbe, 0xf5, 0xdf, 0xb9, 0xd7, 0x1a, + 0x3c, 0x85, 0xa2, 0xec, 0x2a, 0xbe, 0xe4, 0xd0, 0xe8, 0xc1, 0xed, 0x55, 0x4e, 0x83, 0x01, 0xb6, 0xd7, 0x33, 0x09, + 0xf1, 0x3e, 0xf8, 0x7a, 0x5d, 0xe4, 0x61, 0xf0, 0xf5, 0x00, 0x0b, 0x19, 0x7c, 0x1d, 0x90, 0xaf, 0x8d, 0x81, 0x8c, + 0x60, 0x9b, 0xc0, 0x85, 0x22, 0x1d, 0xda, 0x00, 0x61, 0xbe, 0x34, 0xae, 0xa6, 0x7f, 0x15, 0xdd, 0xd9, 0xf0, 0x96, + 0xa8, 0xdc, 0x74, 0x83, 0x9a, 0x9e, 0x80, 0x77, 0x02, 0x34, 0x2a, 0x0a, 0xae, 0xe3, 0x22, 0x1c, 0x0e, 0xcb, 0xeb, + 0x4b, 0x02, 0x76, 0x99, 0x2b, 0x1e, 0x57, 0x51, 0x20, 0xe4, 0x50, 0xfd, 0x0c, 0x54, 0xe4, 0xab, 0x00, 0x01, 0x91, + 0xe0, 0xbf, 0xa0, 0xa6, 0xef, 0x24, 0xdb, 0x04, 0xc3, 0x1b, 0x7e, 0xfe, 0x31, 0xab, 0x86, 0x4a, 0xb4, 0x78, 0x2d, + 0x28, 0xfc, 0x80, 0xbf, 0xae, 0xea, 0xe8, 0x2f, 0x70, 0xe3, 0x6e, 0x6a, 0xd8, 0xdf, 0x49, 0xc7, 0xa2, 0xbe, 0x93, + 0xf3, 0x6c, 0x31, 0x6d, 0x1d, 0xe8, 0x27, 0x92, 0x54, 0xf3, 0x6c, 0x10, 0x0c, 0x83, 0x01, 0x5f, 0xb0, 0x13, 0x39, + 0xe7, 0x9e, 0xf9, 0xd4, 0x23, 0xe9, 0x4f, 0xf3, 0x2c, 0x1b, 0x80, 0x6f, 0x0a, 0xf2, 0x23, 0xfb, 0xff, 0x3d, 0x1f, + 0xa2, 0xf0, 0x70, 0xf0, 0x68, 0x9f, 0xcc, 0x82, 0xd5, 0x2d, 0x7a, 0x74, 0x46, 0x41, 0x26, 0x96, 0xbc, 0xc8, 0x2a, + 0x6f, 0xa9, 0xdc, 0xad, 0xdb, 0x5e, 0x1e, 0xf7, 0x9e, 0xcd, 0xab, 0x58, 0x04, 0xea, 0x9c, 0x03, 0xc5, 0x1b, 0xca, + 0x9e, 0xca, 0xa6, 0x84, 0x54, 0x1b, 0xf2, 0x86, 0xe5, 0x80, 0x05, 0x87, 0xbd, 0xe1, 0x70, 0x2f, 0x18, 0x38, 0x75, + 0xee, 0x20, 0xd8, 0x1b, 0x0e, 0x8f, 0x02, 0x77, 0x1f, 0xca, 0x46, 0xee, 0xce, 0x48, 0x0b, 0xf6, 0xaf, 0x22, 0x2c, + 0x29, 0x88, 0xc7, 0xa4, 0x16, 0x7f, 0x69, 0x70, 0x99, 0x01, 0x40, 0x1f, 0x29, 0x09, 0x98, 0x81, 0x95, 0x19, 0x40, + 0x68, 0x6e, 0x1a, 0xb3, 0x33, 0x60, 0x1e, 0x81, 0x63, 0x1e, 0x21, 0xe3, 0x00, 0x88, 0x25, 0x01, 0xce, 0x5d, 0x10, + 0xc5, 0xba, 0x90, 0x47, 0x00, 0x7a, 0x8f, 0x3f, 0x89, 0x29, 0x05, 0x93, 0x74, 0xac, 0x42, 0x10, 0xc4, 0xf1, 0xd9, + 0xb5, 0x68, 0x4d, 0xce, 0x12, 0x1d, 0xcc, 0x48, 0x02, 0x6c, 0x88, 0x81, 0x9d, 0x83, 0xfb, 0x39, 0x28, 0x3d, 0xac, + 0xde, 0x09, 0xb9, 0xe0, 0x3b, 0xee, 0xc9, 0x66, 0xe1, 0xea, 0x09, 0x07, 0xc1, 0x1d, 0xd7, 0x2c, 0xc0, 0xa8, 0x2a, + 0xd6, 0x65, 0xc5, 0xd3, 0x0f, 0x77, 0x2b, 0x88, 0x7d, 0x87, 0x03, 0xfa, 0x4e, 0xe6, 0x59, 0x72, 0x17, 0x3a, 0x7b, + 0xae, 0x8d, 0x4a, 0xff, 0xe1, 0xc3, 0x9b, 0x9f, 0x22, 0x10, 0x39, 0xd6, 0x86, 0xd2, 0xdf, 0x71, 0x3c, 0x9b, 0xfc, + 0x08, 0x4f, 0xfe, 0xc6, 0xbe, 0xe3, 0xf6, 0xf4, 0xe8, 0xf7, 0xa1, 0x6e, 0x7a, 0xc7, 0x67, 0x77, 0x7c, 0xe4, 0x8a, + 0x43, 0x75, 0x85, 0xfb, 0xfa, 0x66, 0xed, 0x1b, 0x21, 0x3d, 0x3c, 0xcf, 0x94, 0x37, 0xe6, 0x47, 0x3b, 0x18, 0x06, + 0xc1, 0x54, 0x0b, 0x25, 0x21, 0xea, 0x06, 0x53, 0x02, 0x86, 0x68, 0x4f, 0x2f, 0xab, 0x29, 0x72, 0x6e, 0x6a, 0x64, + 0xe1, 0xfd, 0x80, 0x69, 0xa1, 0x43, 0x23, 0x87, 0xf2, 0x83, 0xc3, 0x09, 0x63, 0x16, 0x7e, 0xab, 0x84, 0xe9, 0x57, + 0x8b, 0xca, 0x39, 0x88, 0xee, 0x81, 0x31, 0xae, 0xe0, 0x05, 0x74, 0x85, 0x5d, 0xaf, 0x55, 0x54, 0x0c, 0x04, 0x8f, + 0x43, 0x0e, 0xd0, 0xc3, 0x2e, 0x68, 0x59, 0x59, 0xaa, 0x5b, 0x95, 0xb3, 0x54, 0x51, 0x97, 0xa1, 0xac, 0x8c, 0x15, + 0xe6, 0x7b, 0xc9, 0x7e, 0x28, 0xd0, 0xb3, 0x7c, 0x2a, 0xba, 0xe0, 0x85, 0x50, 0x82, 0xe5, 0xba, 0xde, 0x89, 0x40, + 0xd4, 0xf9, 0xa1, 0x77, 0xd5, 0xd7, 0x38, 0x76, 0x3c, 0x7d, 0x23, 0x53, 0xae, 0x4d, 0x28, 0x34, 0x9f, 0x2f, 0x7d, + 0xc5, 0x44, 0xc1, 0x6e, 0xa0, 0x5f, 0x6d, 0x1b, 0x7d, 0x76, 0xb7, 0xd6, 0x9b, 0x41, 0x89, 0x8e, 0x79, 0x8d, 0x82, + 0x6b, 0xa5, 0x50, 0x30, 0xda, 0xdb, 0xf8, 0x33, 0x1c, 0xb9, 0xd5, 0xed, 0xa1, 0xf7, 0x5b, 0x15, 0x5f, 0xbe, 0x45, + 0xdf, 0x4e, 0xfb, 0x73, 0x54, 0xc9, 0x5f, 0x56, 0x2b, 0xf0, 0xa1, 0x82, 0xc8, 0x22, 0x16, 0x97, 0x16, 0xea, 0x39, + 0x7d, 0x77, 0xfc, 0x16, 0xfc, 0x28, 0xf1, 0xf7, 0xaf, 0xdf, 0x07, 0x35, 0x99, 0xc6, 0xb3, 0xc2, 0x7c, 0x68, 0x73, + 0x40, 0x68, 0x12, 0x97, 0x66, 0xdf, 0xcf, 0xe2, 0x26, 0xfb, 0xae, 0xd9, 0x7a, 0x5a, 0x34, 0x91, 0xa4, 0x0c, 0xb7, + 0x0f, 0x06, 0x04, 0xfa, 0x00, 0x51, 0x9c, 0x7d, 0x41, 0x63, 0x48, 0xf3, 0x99, 0x7d, 0x3f, 0x22, 0xde, 0xcb, 0x9d, + 0x10, 0x62, 0x5c, 0x61, 0xd1, 0xe8, 0x21, 0x9f, 0xf1, 0x48, 0x19, 0x16, 0xbd, 0xc7, 0x04, 0xe2, 0x0c, 0xa7, 0xd5, + 0x7b, 0xc4, 0x3c, 0xc6, 0xbb, 0x81, 0x96, 0x3d, 0x44, 0x19, 0x75, 0xd9, 0x1b, 0x16, 0xdf, 0x1f, 0xd7, 0x61, 0x66, + 0x2d, 0x2f, 0x87, 0xf0, 0x37, 0xd0, 0x06, 0xe0, 0x94, 0x23, 0xcb, 0x57, 0x99, 0x8d, 0xae, 0x96, 0x98, 0xde, 0x44, + 0x10, 0x8b, 0x47, 0xa7, 0xc3, 0xda, 0xd5, 0xa9, 0x7a, 0x57, 0x3b, 0x9f, 0x89, 0x5e, 0x05, 0x5a, 0xb9, 0xb6, 0x3d, + 0x1e, 0xc2, 0x5d, 0x6a, 0x69, 0x85, 0x8d, 0x28, 0xe7, 0xe2, 0xe9, 0xce, 0xb1, 0x39, 0x01, 0x0d, 0xae, 0x64, 0x0a, + 0xc0, 0x59, 0x5a, 0x8d, 0x46, 0x8d, 0xb0, 0xcf, 0xca, 0xf9, 0x1c, 0xb6, 0x16, 0xe2, 0x69, 0x01, 0x18, 0x6e, 0x13, + 0x83, 0x92, 0x77, 0x63, 0x50, 0x4e, 0x3f, 0x2a, 0x78, 0xeb, 0xe0, 0xac, 0x5c, 0xc6, 0xa9, 0xbc, 0x01, 0x2c, 0xc6, + 0xc0, 0x4f, 0xc5, 0x52, 0xbd, 0x84, 0x64, 0xc9, 0x93, 0x8f, 0x68, 0xb5, 0x91, 0x06, 0xc0, 0x55, 0x4e, 0x8d, 0xe5, + 0x9e, 0x02, 0x09, 0x75, 0xa5, 0xa8, 0x84, 0xb8, 0xaa, 0xe2, 0x64, 0x79, 0x8a, 0xa9, 0xe1, 0x06, 0x7a, 0x11, 0x05, + 0x72, 0xc5, 0x05, 0x90, 0xf4, 0x9c, 0xfd, 0x9e, 0x69, 0xac, 0xf1, 0xe7, 0x12, 0x05, 0x4c, 0x1a, 0x35, 0x18, 0x2b, + 0x65, 0x2f, 0xa5, 0x89, 0xf6, 0x16, 0x04, 0xb5, 0x7b, 0xf9, 0x17, 0xd4, 0xfd, 0x1c, 0x5a, 0x11, 0x36, 0xc0, 0x10, + 0xe5, 0x39, 0xee, 0xd0, 0xd4, 0x2e, 0x39, 0x0f, 0x18, 0xd1, 0x79, 0x9f, 0xd5, 0x76, 0xab, 0x3f, 0x5f, 0x02, 0xb6, + 0x69, 0x6a, 0x7c, 0x0a, 0xc3, 0x84, 0x98, 0xd8, 0xc0, 0x56, 0x59, 0x69, 0x37, 0x94, 0x69, 0x27, 0x5d, 0x32, 0xaf, + 0x85, 0xd3, 0xbc, 0xc7, 0xd8, 0x72, 0xa4, 0x72, 0xf7, 0xfb, 0xa1, 0xf9, 0xc9, 0x72, 0xfa, 0x5c, 0x87, 0x6c, 0xf6, + 0xc6, 0x83, 0xe6, 0x44, 0xab, 0xab, 0x3a, 0xfa, 0x01, 0x1d, 0x80, 0x99, 0xb6, 0x00, 0x99, 0x2e, 0xd8, 0xb4, 0xaf, + 0x44, 0xc5, 0x25, 0x09, 0x4b, 0x25, 0x81, 0x9d, 0xdd, 0x94, 0xec, 0x6c, 0x02, 0xe2, 0x19, 0xee, 0x7a, 0x5a, 0xec, + 0x84, 0x34, 0xe1, 0x2d, 0xf6, 0x12, 0x10, 0x75, 0xa8, 0xea, 0x12, 0xb2, 0x31, 0x86, 0x2e, 0xfe, 0x45, 0x29, 0x4c, + 0x58, 0xcb, 0xa4, 0x2a, 0x31, 0x41, 0x90, 0xca, 0xdd, 0x16, 0x81, 0x25, 0x0a, 0x76, 0x00, 0x7b, 0xef, 0x46, 0xdd, + 0x8c, 0x9a, 0xaa, 0x4e, 0xbd, 0x04, 0x1f, 0xa7, 0x59, 0x57, 0x41, 0x66, 0x61, 0x57, 0xc5, 0x9a, 0x07, 0x3a, 0x36, + 0x95, 0x32, 0x26, 0xee, 0xd2, 0x22, 0x43, 0x3c, 0x60, 0x8c, 0xa5, 0x0b, 0x81, 0x7c, 0xb3, 0xdd, 0x71, 0xd3, 0x13, + 0x84, 0x7e, 0xc2, 0x86, 0x12, 0xb8, 0xe9, 0x6c, 0x4f, 0x4d, 0x33, 0x1f, 0x10, 0x71, 0x18, 0x50, 0x20, 0xd9, 0x38, + 0xa4, 0x39, 0xd2, 0x17, 0x24, 0x4d, 0x18, 0x18, 0x5a, 0xf1, 0x9c, 0x20, 0x2b, 0x0a, 0x3d, 0x5b, 0x57, 0x6d, 0x9c, + 0x2b, 0xc3, 0x1c, 0x2d, 0x39, 0x15, 0x3e, 0x27, 0xc8, 0xc4, 0xee, 0x69, 0x9b, 0x99, 0x0c, 0x47, 0xc9, 0x02, 0xf3, + 0x2b, 0x88, 0x12, 0x77, 0xa6, 0x59, 0x95, 0x83, 0x71, 0x01, 0x0b, 0xb4, 0xf2, 0x3d, 0xa8, 0x1b, 0x6b, 0x68, 0xa3, + 0x61, 0x88, 0xdd, 0xfe, 0x04, 0xfb, 0xb5, 0x76, 0x5a, 0x97, 0x29, 0x96, 0x97, 0x29, 0x44, 0x7b, 0x21, 0xf3, 0x1b, + 0x45, 0xa2, 0x3b, 0x45, 0x18, 0x12, 0xd6, 0x51, 0xf6, 0xa4, 0x4d, 0x0d, 0xa0, 0xa7, 0x5e, 0xc0, 0xf3, 0xce, 0xb5, + 0x0c, 0xbb, 0x48, 0xf7, 0x57, 0x05, 0x9f, 0xd2, 0x0d, 0x82, 0x14, 0xbd, 0x49, 0xc1, 0x9c, 0xd7, 0xa3, 0xa4, 0xde, + 0x9c, 0xb6, 0xcc, 0xa8, 0x3a, 0x2a, 0x42, 0xca, 0x09, 0xfe, 0x93, 0x97, 0x52, 0x13, 0x9b, 0x30, 0xc1, 0x03, 0x1f, + 0xe6, 0x19, 0x36, 0xf0, 0x76, 0xfb, 0x2e, 0x0d, 0x93, 0x36, 0xdb, 0x90, 0x82, 0xb4, 0xc2, 0xc4, 0xc5, 0x80, 0xca, + 0x5e, 0xe3, 0x7e, 0xc1, 0x76, 0xd2, 0x14, 0x3c, 0x08, 0x1b, 0x0d, 0x4c, 0xdc, 0xea, 0xe2, 0xeb, 0x30, 0xa1, 0xe1, + 0x92, 0x6a, 0x67, 0x27, 0x2d, 0x69, 0x6e, 0xaf, 0xcb, 0x0b, 0xdb, 0x07, 0x1d, 0x3b, 0xac, 0x6b, 0x78, 0xa0, 0x79, + 0xcd, 0x2e, 0xae, 0x98, 0xa6, 0x89, 0xc6, 0x7a, 0x48, 0x59, 0x72, 0xac, 0xeb, 0xe9, 0x0a, 0x57, 0xcb, 0x4c, 0x03, + 0xbb, 0x4b, 0xbc, 0xd0, 0x03, 0x1e, 0x76, 0xb8, 0x22, 0xd1, 0x05, 0x36, 0x9b, 0xad, 0x6a, 0x32, 0xcd, 0xef, 0xcb, + 0x96, 0x9b, 0x80, 0x70, 0x96, 0xfa, 0xe6, 0x3e, 0x39, 0xd6, 0xb4, 0xcd, 0x4f, 0x02, 0x1c, 0x6f, 0xaf, 0x80, 0xa4, + 0x63, 0x09, 0xba, 0xf8, 0x96, 0xfe, 0x20, 0x52, 0x33, 0x15, 0xf4, 0xde, 0xf9, 0x22, 0x75, 0xf3, 0x0b, 0xb0, 0x8d, + 0xda, 0x18, 0xd3, 0xac, 0x6c, 0x1d, 0x26, 0xca, 0xc2, 0x1a, 0x59, 0xc8, 0x25, 0xf8, 0x60, 0xee, 0x36, 0x75, 0x7a, + 0xdc, 0x41, 0x84, 0xfd, 0x2e, 0x7a, 0x3c, 0xc2, 0x58, 0xb1, 0x06, 0x89, 0x61, 0x15, 0xd6, 0xb4, 0xb9, 0x1c, 0xa2, + 0x9c, 0x9a, 0x25, 0x13, 0x2d, 0xa9, 0x4f, 0x29, 0xa2, 0x14, 0xcc, 0x8d, 0xa7, 0x65, 0xc3, 0x94, 0x10, 0x21, 0x2b, + 0xa4, 0x03, 0xaa, 0xb5, 0xd0, 0x52, 0x4d, 0xd0, 0xeb, 0xd0, 0xcb, 0x42, 0x63, 0x0a, 0xa2, 0x8f, 0xc8, 0x70, 0x23, + 0x8e, 0x8c, 0xee, 0x8e, 0x51, 0x4c, 0x20, 0x54, 0xb5, 0x97, 0x17, 0x56, 0x9f, 0x96, 0x6d, 0x75, 0x10, 0x57, 0x88, + 0x7c, 0xdf, 0x4d, 0x50, 0x63, 0x14, 0xb4, 0x39, 0xdd, 0xe8, 0x2f, 0x45, 0xe8, 0xdb, 0x85, 0x63, 0x37, 0x0a, 0x22, + 0x21, 0x02, 0xab, 0xd7, 0x54, 0x0c, 0xc8, 0x3a, 0x8f, 0x5d, 0x84, 0x26, 0xdd, 0x2d, 0x44, 0x79, 0xa3, 0xb2, 0xfe, + 0xb8, 0x0e, 0xc9, 0x76, 0x8b, 0x65, 0x81, 0x2f, 0xfb, 0xe9, 0xfa, 0x1e, 0xc8, 0xef, 0x37, 0xeb, 0xcf, 0x42, 0x7e, + 0xbf, 0xce, 0xbe, 0x04, 0xf2, 0xfb, 0xcd, 0xfa, 0x7f, 0x1a, 0xf2, 0xfb, 0x74, 0xed, 0x41, 0x7e, 0xab, 0xc1, 0xf8, + 0xad, 0x60, 0xc1, 0xc9, 0xdb, 0x80, 0xbe, 0x90, 0x2c, 0x38, 0x79, 0xf5, 0xca, 0x37, 0x02, 0x11, 0x1a, 0xb9, 0xde, + 0xc8, 0x82, 0x11, 0xb7, 0x05, 0x5e, 0xa1, 0xd6, 0xc9, 0x07, 0x2a, 0xca, 0x00, 0x78, 0xbd, 0xfc, 0x67, 0x56, 0x2d, + 0xc3, 0x60, 0x3f, 0x20, 0x33, 0x07, 0x09, 0x3a, 0x9c, 0xc0, 0xed, 0x0d, 0x4a, 0xf9, 0xfe, 0x8b, 0xd0, 0xc3, 0x47, + 0xa3, 0x51, 0x5c, 0x5c, 0xe2, 0x9d, 0xce, 0xec, 0x23, 0xc4, 0x3b, 0xce, 0x78, 0x69, 0x23, 0x44, 0x2c, 0xe3, 0xf2, + 0x4c, 0x87, 0x66, 0x29, 0xed, 0x4e, 0x84, 0x80, 0xf3, 0x67, 0x00, 0x53, 0x6f, 0xb7, 0x66, 0x8c, 0xdd, 0x50, 0x0c, + 0xb1, 0x8e, 0x1f, 0xfb, 0x7c, 0xad, 0xdf, 0x9d, 0xc7, 0x25, 0x7f, 0x17, 0x57, 0x4b, 0x06, 0x9d, 0xd4, 0xdb, 0xb5, + 0x90, 0xeb, 0x15, 0x54, 0x02, 0x37, 0x13, 0xc1, 0x93, 0xca, 0x63, 0xa2, 0x14, 0x6c, 0x79, 0x46, 0x0d, 0x70, 0x79, + 0x47, 0x0e, 0x1a, 0xda, 0x61, 0xd2, 0x3e, 0xc1, 0x46, 0xda, 0x9c, 0x81, 0x11, 0xe3, 0xcb, 0x6b, 0x2e, 0xaa, 0x9f, + 0x00, 0x5f, 0x5d, 0xf0, 0x02, 0x6e, 0x0d, 0xc8, 0xd5, 0x02, 0xd8, 0x0a, 0x94, 0x5c, 0x58, 0xc6, 0x99, 0xd3, 0xd2, + 0xf7, 0xf7, 0x50, 0x67, 0xa5, 0x81, 0x2b, 0x6c, 0x0c, 0x07, 0xde, 0x8f, 0xd0, 0xe7, 0x13, 0xdd, 0x57, 0xc1, 0xb5, + 0xf0, 0xaf, 0x35, 0x3f, 0xcb, 0x52, 0x04, 0xb6, 0xc9, 0x52, 0x6d, 0x35, 0xa4, 0xa4, 0x19, 0x98, 0xb0, 0x51, 0x5e, + 0x17, 0xf0, 0xdb, 0xc3, 0x2f, 0xa5, 0x09, 0xda, 0xf3, 0x94, 0x34, 0x95, 0x80, 0xce, 0x1d, 0xc5, 0x00, 0x71, 0x6a, + 0x0b, 0x8b, 0x20, 0x37, 0xcd, 0xd2, 0x28, 0xb6, 0xea, 0x35, 0x87, 0x5a, 0x4a, 0x15, 0x14, 0xf5, 0x59, 0x12, 0x57, + 0xfc, 0x52, 0x82, 0x9b, 0xea, 0xa8, 0x95, 0x42, 0xc1, 0xba, 0x3a, 0x13, 0x97, 0x67, 0x38, 0xba, 0x11, 0x04, 0x17, + 0x1f, 0x35, 0x92, 0x48, 0x4f, 0x6d, 0xef, 0x22, 0xfa, 0x7e, 0xf4, 0xf2, 0xed, 0x87, 0xd7, 0x1f, 0xfe, 0x75, 0xf6, + 0xfc, 0xf8, 0xc3, 0xcb, 0xef, 0x4f, 0xde, 0xbf, 0x7e, 0x79, 0x3a, 0xb7, 0xce, 0x51, 0x3b, 0x05, 0x93, 0xc5, 0x76, + 0x6b, 0xbf, 0xf8, 0xe5, 0xed, 0x8b, 0x97, 0xaf, 0x5e, 0xbf, 0x7d, 0xf9, 0x82, 0xe2, 0x21, 0x72, 0x26, 0xd6, 0x57, + 0xbc, 0xc8, 0x92, 0xb3, 0x65, 0x56, 0x56, 0xd0, 0xac, 0xb9, 0x8e, 0xfb, 0xb5, 0xa8, 0xa7, 0x09, 0xae, 0x1e, 0xb5, + 0x34, 0x98, 0x59, 0x4d, 0xc7, 0x86, 0xdc, 0x50, 0xff, 0xb5, 0x01, 0xa0, 0x6f, 0x2e, 0xb7, 0x71, 0x6b, 0x55, 0x1a, + 0xd5, 0x6e, 0x2b, 0x55, 0xe1, 0x2c, 0x01, 0x79, 0xd7, 0x53, 0x7c, 0x41, 0x57, 0xd6, 0x06, 0x37, 0xbc, 0x60, 0xb9, + 0x1d, 0x86, 0x1b, 0x25, 0xc4, 0xd1, 0xe3, 0x70, 0x11, 0xe5, 0x0a, 0x5e, 0x68, 0xcd, 0xc2, 0x15, 0x5b, 0xde, 0x93, + 0x6b, 0x15, 0x2d, 0x1b, 0x40, 0x61, 0x79, 0x73, 0x50, 0x0f, 0x97, 0xcd, 0xe7, 0xd9, 0x70, 0x12, 0xb5, 0xb2, 0x30, + 0xc6, 0xda, 0x99, 0x60, 0xe1, 0xac, 0x67, 0xaa, 0xfa, 0x51, 0x25, 0x7f, 0x92, 0x37, 0xe6, 0x5a, 0x7d, 0xb8, 0xec, + 0x48, 0x84, 0x42, 0x27, 0x51, 0x7a, 0xb8, 0x56, 0x3f, 0x00, 0xf8, 0x45, 0x13, 0xe3, 0xbf, 0xd6, 0xdc, 0x40, 0xe3, + 0x87, 0xda, 0x3f, 0xd1, 0x29, 0x12, 0xf4, 0x54, 0x78, 0x26, 0x7d, 0x7a, 0x59, 0xce, 0xc1, 0x9c, 0xcc, 0x1f, 0xc3, + 0xb9, 0xd4, 0x31, 0xbc, 0xdb, 0xf3, 0xb9, 0x98, 0xc6, 0x1a, 0x91, 0x52, 0x73, 0x56, 0xf4, 0xcb, 0xbe, 0x03, 0xaf, + 0x46, 0x15, 0xec, 0x63, 0xf8, 0x6c, 0x4c, 0x6a, 0x6d, 0x69, 0x8f, 0x0b, 0xdc, 0xfe, 0x56, 0x9b, 0xc0, 0x3d, 0xdb, + 0x8d, 0x40, 0x9b, 0x3e, 0x12, 0xed, 0x1a, 0x69, 0x79, 0x4f, 0xf7, 0xc1, 0x2e, 0xbc, 0xba, 0x87, 0x32, 0x54, 0x5d, + 0x94, 0xc1, 0x9f, 0x13, 0x45, 0x21, 0x3e, 0x43, 0x1b, 0x4c, 0xbc, 0x2c, 0x45, 0xc0, 0x3c, 0xb2, 0x50, 0xb0, 0xa3, + 0xa2, 0x09, 0x8a, 0xa5, 0xcd, 0x3f, 0x37, 0xda, 0x6e, 0x02, 0xb6, 0x7d, 0x3d, 0xf5, 0x3f, 0x36, 0xb6, 0x09, 0x7e, + 0x9a, 0x5a, 0xca, 0x70, 0xda, 0x02, 0x99, 0x69, 0x2e, 0xc8, 0xc3, 0xa4, 0x95, 0x80, 0x8b, 0x01, 0x7b, 0xed, 0x13, + 0xd5, 0x8e, 0xbd, 0x8d, 0xb0, 0x7d, 0x1a, 0x1a, 0x31, 0xdc, 0x68, 0xfb, 0xdb, 0x66, 0x59, 0x91, 0xa8, 0x49, 0xb3, + 0x29, 0x0a, 0x9b, 0x08, 0x33, 0x77, 0x6c, 0xfe, 0xd6, 0xd7, 0xc3, 0x49, 0x4d, 0x6a, 0xb7, 0xbb, 0xad, 0xcc, 0xf1, + 0x0f, 0xc5, 0xe7, 0x9c, 0x3d, 0xda, 0x64, 0x7a, 0xba, 0xeb, 0x3f, 0x32, 0x1b, 0xa1, 0xb0, 0x71, 0x68, 0xd4, 0x7a, + 0xdf, 0xfb, 0x00, 0x81, 0x1d, 0xd9, 0x34, 0x71, 0x62, 0x59, 0xe7, 0xc9, 0x33, 0x52, 0x8f, 0x5c, 0x05, 0x6c, 0xeb, + 0xce, 0xc2, 0x6f, 0x79, 0x12, 0x76, 0x35, 0x4c, 0xdd, 0x09, 0x4d, 0x17, 0x10, 0x33, 0x15, 0x34, 0x41, 0xe1, 0x1f, + 0x8f, 0x36, 0xcd, 0x93, 0xac, 0xde, 0xf7, 0x3e, 0xc3, 0xdf, 0x59, 0x0a, 0x7f, 0xab, 0xfa, 0x0f, 0xba, 0xb9, 0xe2, + 0xd5, 0x52, 0xa6, 0x51, 0xf0, 0xee, 0xe4, 0xf4, 0x43, 0xa0, 0xb1, 0xf8, 0xf1, 0x46, 0x6a, 0x6c, 0x10, 0xcc, 0x32, + 0x03, 0x9d, 0x5c, 0x2e, 0x2f, 0x11, 0x8e, 0x52, 0xc7, 0x33, 0x38, 0x5d, 0xca, 0x9b, 0xe3, 0x3c, 0xf7, 0xaf, 0x4d, + 0xe6, 0xac, 0xd5, 0x37, 0x89, 0xc6, 0x89, 0x14, 0x82, 0xf4, 0x77, 0x94, 0x95, 0x67, 0x5a, 0x5f, 0x97, 0x9e, 0x9d, + 0xdf, 0x9d, 0x69, 0x99, 0xa0, 0xc5, 0x03, 0x7d, 0xfe, 0xc7, 0x61, 0x9a, 0x5d, 0xef, 0x21, 0x43, 0xc0, 0x02, 0x70, + 0xa6, 0xc8, 0xf9, 0xf9, 0xba, 0xaa, 0xa4, 0x18, 0x16, 0xf2, 0x26, 0x38, 0x3a, 0x54, 0x0f, 0x26, 0x43, 0xac, 0x1e, + 0x83, 0xbd, 0xff, 0x4a, 0xf2, 0x2c, 0xf9, 0xc8, 0x82, 0x47, 0x9b, 0x8c, 0x1d, 0xb5, 0x8e, 0xfd, 0x71, 0x1d, 0x1c, + 0x41, 0x5b, 0xf7, 0x8e, 0xf3, 0xfc, 0x70, 0x5f, 0x7d, 0x71, 0x74, 0xb8, 0x9f, 0x66, 0xd7, 0x47, 0x5e, 0x68, 0x06, + 0xad, 0xb7, 0x60, 0x1a, 0x02, 0xcf, 0x5a, 0x7a, 0x94, 0xe8, 0x53, 0x9d, 0xf0, 0xd0, 0x31, 0x9f, 0x80, 0xf5, 0xa0, + 0xda, 0x1b, 0x26, 0x28, 0xce, 0xca, 0x81, 0xd5, 0xda, 0x6e, 0x43, 0x6b, 0x07, 0xb6, 0xf4, 0x40, 0x92, 0x50, 0xcc, + 0x8e, 0x59, 0x28, 0x22, 0x3d, 0x90, 0xd0, 0x40, 0x39, 0xe5, 0x84, 0x26, 0x35, 0x05, 0xf6, 0xe3, 0x4d, 0xbc, 0x02, + 0x89, 0xbf, 0xfe, 0xe9, 0x71, 0xa4, 0x09, 0x44, 0x71, 0xf5, 0x16, 0x3a, 0xf1, 0x64, 0x9e, 0x8a, 0xce, 0x6b, 0x9c, + 0x2e, 0x10, 0x56, 0x62, 0x3d, 0x4a, 0x0e, 0x19, 0xe6, 0x04, 0xa2, 0x08, 0x59, 0x70, 0x8c, 0xb8, 0x36, 0x71, 0x7b, + 0xcc, 0xb8, 0xcc, 0x1a, 0x33, 0x14, 0xb5, 0xe7, 0xcb, 0xa0, 0xb6, 0xf5, 0xca, 0xfb, 0xa6, 0x0c, 0x64, 0xe8, 0x61, + 0x45, 0x5b, 0x74, 0x09, 0xbc, 0x6a, 0x3c, 0xc1, 0x2d, 0xa7, 0xe1, 0xbc, 0xa4, 0x72, 0xe1, 0xf6, 0x72, 0xa9, 0x8e, + 0xe2, 0x48, 0xd6, 0x0e, 0x40, 0x55, 0xcd, 0xfa, 0xd1, 0xa3, 0x8d, 0xc0, 0xcd, 0x5f, 0xb2, 0xa3, 0xe6, 0x3a, 0xa8, + 0xe2, 0xf3, 0xe1, 0x92, 0x83, 0xa7, 0x57, 0xb0, 0xf7, 0x5f, 0xe9, 0x79, 0x6e, 0x27, 0x5b, 0xad, 0xf4, 0x65, 0x2c, + 0xd2, 0x9c, 0x7f, 0x88, 0xcf, 0x7f, 0xf8, 0x3f, 0xcd, 0x3d, 0xed, 0x76, 0xdb, 0x36, 0xb2, 0xff, 0xfb, 0x14, 0x0c, + 0x93, 0x4d, 0xc9, 0x84, 0xa4, 0x49, 0xca, 0xb2, 0x15, 0xc9, 0xb2, 0xdb, 0xe6, 0x63, 0x37, 0xbd, 0x6e, 0xd3, 0x93, + 0xb8, 0xb9, 0xbb, 0xeb, 0xfa, 0x58, 0x94, 0x04, 0x49, 0xdc, 0x50, 0xa4, 0x0e, 0x49, 0xf9, 0xa3, 0x0a, 0xf7, 0x59, + 0xf6, 0x11, 0xee, 0x33, 0xf4, 0xc9, 0xee, 0x99, 0x19, 0x80, 0x04, 0xbf, 0x24, 0x65, 0x93, 0x76, 0xf7, 0xb4, 0x49, + 0x44, 0x10, 0x00, 0x31, 0x03, 0x60, 0x30, 0x33, 0x98, 0x0f, 0xac, 0xf3, 0x62, 0x1c, 0x3c, 0x87, 0x0a, 0x99, 0x7a, + 0xfa, 0x68, 0x43, 0xe4, 0xad, 0x89, 0x25, 0xc8, 0x68, 0x09, 0x54, 0xbf, 0x03, 0x1b, 0xdb, 0xf3, 0x43, 0x16, 0x53, + 0x6b, 0x1c, 0x2c, 0x91, 0x24, 0x8a, 0x23, 0x59, 0x1e, 0x19, 0x4f, 0xb9, 0x01, 0x6b, 0x53, 0xe1, 0x7b, 0x0c, 0xc6, + 0x15, 0x89, 0xfd, 0x26, 0xaf, 0x4c, 0x79, 0xb0, 0x2f, 0xb1, 0xdd, 0xdb, 0xe8, 0x56, 0x8c, 0x94, 0x23, 0x80, 0x42, + 0xc4, 0x9d, 0x3d, 0x1f, 0x9d, 0xc8, 0x6a, 0x59, 0xd4, 0x5d, 0x51, 0xbf, 0xf0, 0x2b, 0x53, 0x15, 0x6e, 0x22, 0xaa, + 0x42, 0x86, 0x13, 0xf5, 0xf4, 0xe4, 0x40, 0xae, 0x7d, 0x3a, 0xea, 0x9f, 0x4b, 0xc0, 0x61, 0xaf, 0x80, 0x84, 0x72, + 0x59, 0x8d, 0x83, 0x81, 0x1c, 0x5c, 0x05, 0x8f, 0x43, 0xcb, 0x43, 0x50, 0xb9, 0x48, 0xef, 0xe7, 0x73, 0x44, 0xa6, + 0x4f, 0xa2, 0xb7, 0x11, 0xff, 0xb7, 0x80, 0x19, 0x55, 0x49, 0x2c, 0x4c, 0xa2, 0x58, 0x05, 0x38, 0xaa, 0x89, 0x49, + 0x14, 0x29, 0x01, 0x10, 0x42, 0xd4, 0x78, 0x22, 0x03, 0x46, 0x0e, 0xaa, 0x4d, 0x25, 0xc0, 0x46, 0x7a, 0xf1, 0x43, + 0xe1, 0xc0, 0x54, 0xa8, 0x52, 0x3e, 0xc0, 0xf6, 0x04, 0x32, 0x97, 0x6f, 0x7c, 0xe3, 0x7f, 0x23, 0x63, 0xee, 0x19, + 0x4b, 0xcf, 0x78, 0x17, 0x5e, 0x65, 0x8d, 0xb3, 0x93, 0x27, 0x27, 0x32, 0xd8, 0x40, 0x83, 0x10, 0x27, 0x5c, 0xf0, + 0xe4, 0xe2, 0x98, 0x6f, 0xf1, 0x4b, 0xd9, 0x0b, 0x2f, 0x9e, 0x33, 0x91, 0x13, 0x48, 0xbc, 0x4d, 0x39, 0x56, 0x74, + 0x09, 0x2d, 0x10, 0xff, 0xe7, 0x01, 0xb7, 0x5d, 0xf1, 0xad, 0x49, 0x1a, 0x07, 0xff, 0xc3, 0xee, 0x41, 0x1c, 0x48, + 0xd2, 0x68, 0x05, 0x42, 0xa1, 0x37, 0xe7, 0x4a, 0x3e, 0x43, 0x63, 0xfb, 0x7d, 0xee, 0xe3, 0x47, 0x66, 0xe1, 0x92, + 0x04, 0x7e, 0xc1, 0x4a, 0xa3, 0xf9, 0x3c, 0x60, 0x9a, 0x2a, 0xb2, 0xd4, 0xa8, 0x46, 0xfe, 0x99, 0xb3, 0x07, 0xb6, + 0x08, 0x0d, 0xab, 0x67, 0x6d, 0x3b, 0x47, 0x40, 0xcc, 0xf2, 0xd8, 0x89, 0x24, 0x23, 0xe1, 0x25, 0xc0, 0x0d, 0xde, + 0xa3, 0xf1, 0x79, 0x29, 0x76, 0xa6, 0x39, 0x8d, 0xd6, 0xe3, 0x80, 0x99, 0xb8, 0xdc, 0xe1, 0x93, 0x9b, 0xf1, 0x7a, + 0x3c, 0x86, 0x74, 0x40, 0x0f, 0x6c, 0x03, 0x02, 0x1c, 0x45, 0x09, 0x2a, 0x1e, 0x32, 0x7d, 0x00, 0x40, 0x59, 0x69, + 0x75, 0xf8, 0x60, 0x94, 0x04, 0x3a, 0x45, 0xfa, 0x40, 0x0a, 0x4a, 0x86, 0xfa, 0xa6, 0x1d, 0xaa, 0xef, 0x60, 0xf1, + 0x25, 0xea, 0xa0, 0x81, 0x73, 0x18, 0x5e, 0xaa, 0xef, 0x10, 0xc3, 0x98, 0x24, 0xfb, 0x39, 0xad, 0x5d, 0xd5, 0x50, + 0xc9, 0xb6, 0x62, 0x8d, 0xe9, 0x32, 0xe0, 0x6e, 0xe1, 0x85, 0xef, 0xcd, 0xc3, 0x28, 0x49, 0xfd, 0x89, 0x7a, 0x35, + 0x78, 0xed, 0x6b, 0x97, 0xcb, 0x54, 0xd3, 0xaf, 0x8c, 0x3f, 0xcb, 0x89, 0x76, 0x04, 0x29, 0xc4, 0x3c, 0x3b, 0x2d, + 0x75, 0xe4, 0xdd, 0xb3, 0xad, 0x9e, 0x20, 0xb9, 0x58, 0xe7, 0xcf, 0x43, 0xa8, 0x55, 0x49, 0xd9, 0x83, 0xb9, 0xc7, + 0x20, 0x65, 0xcf, 0x9f, 0xf5, 0x01, 0x09, 0xc3, 0xcf, 0xd7, 0x1b, 0x3c, 0xfa, 0xd3, 0xe2, 0x74, 0xc5, 0x28, 0xd3, + 0xc2, 0x35, 0x8b, 0x9e, 0x1f, 0xc8, 0x66, 0xc5, 0xa5, 0x73, 0x7a, 0xf4, 0x6d, 0x99, 0x8f, 0x80, 0xf3, 0x1e, 0x6c, + 0x7a, 0xc2, 0x28, 0x55, 0x20, 0x74, 0x12, 0x7c, 0x70, 0x54, 0x35, 0x43, 0xe4, 0xbd, 0x6a, 0x7a, 0xc6, 0xa9, 0xc0, + 0x77, 0x78, 0x58, 0x6a, 0x3c, 0x80, 0x5e, 0x29, 0x46, 0x0a, 0xdd, 0x94, 0x87, 0x30, 0x73, 0x25, 0xee, 0x5f, 0xa2, + 0xe9, 0x3b, 0xcf, 0x6a, 0x4d, 0xc8, 0x40, 0x22, 0x6f, 0xa4, 0xc6, 0x45, 0x59, 0xc1, 0x18, 0x55, 0x36, 0x13, 0x9a, + 0x16, 0x09, 0x9e, 0x27, 0x05, 0x4b, 0x44, 0xa4, 0xf1, 0x40, 0x8b, 0xf8, 0xb1, 0x3e, 0xca, 0xae, 0x45, 0x72, 0x6e, + 0x91, 0x1a, 0xdb, 0x88, 0xe4, 0xbc, 0x4b, 0x7e, 0xb8, 0x5a, 0xa7, 0x18, 0xcc, 0x19, 0x06, 0xc0, 0x32, 0x55, 0x41, + 0x3e, 0x18, 0xc8, 0x73, 0xc1, 0xd2, 0x67, 0xaa, 0xe2, 0x4f, 0xeb, 0x65, 0x5c, 0x40, 0x01, 0xaa, 0x85, 0x84, 0x1d, + 0x55, 0xa1, 0xf0, 0x18, 0x73, 0x30, 0x26, 0x46, 0x91, 0x09, 0x41, 0x9b, 0xe0, 0x95, 0x61, 0x03, 0x49, 0x98, 0x50, + 0x3f, 0x03, 0x2d, 0x68, 0x04, 0x16, 0x02, 0xbc, 0x96, 0xc0, 0x1c, 0x3d, 0xda, 0x84, 0xd9, 0xd9, 0xa3, 0x4d, 0x92, + 0x0d, 0x1f, 0x6d, 0xbc, 0xdc, 0x1a, 0x45, 0xbd, 0x50, 0xc9, 0x14, 0x65, 0x84, 0x68, 0x18, 0x65, 0xd7, 0x85, 0x6f, + 0x58, 0x01, 0x2f, 0x2c, 0x32, 0x2a, 0x57, 0xd0, 0x38, 0x64, 0xc8, 0x4d, 0x40, 0x56, 0xb1, 0xbf, 0xf4, 0xe2, 0x7b, + 0xb2, 0x18, 0x31, 0x64, 0xb3, 0x12, 0x5d, 0x55, 0x88, 0xc2, 0x13, 0x02, 0x88, 0xd8, 0xab, 0xca, 0x37, 0x79, 0x99, + 0xd0, 0x4d, 0xe4, 0xd7, 0xe6, 0xf0, 0xad, 0x6b, 0xf5, 0x29, 0xb3, 0xa6, 0x2c, 0xf5, 0xfc, 0x80, 0x9a, 0x0c, 0xb4, + 0xa4, 0x05, 0xbc, 0xa4, 0x0c, 0x5e, 0x58, 0x5e, 0x3f, 0x08, 0x0c, 0xd1, 0x7e, 0x1a, 0x37, 0x42, 0x86, 0x79, 0xd2, + 0x9a, 0x67, 0x94, 0xde, 0xfd, 0xa1, 0xd3, 0xc1, 0x60, 0x3a, 0x42, 0x98, 0x0e, 0x16, 0x4e, 0xa2, 0x29, 0xfb, 0xf9, + 0xed, 0xeb, 0x3c, 0x31, 0x1b, 0xe8, 0x18, 0x47, 0x7c, 0x61, 0x26, 0xc8, 0x38, 0xc4, 0xc8, 0x34, 0x50, 0x0a, 0x35, + 0x25, 0x5f, 0x42, 0x71, 0xa6, 0x2a, 0x67, 0x34, 0x76, 0x36, 0xa5, 0x51, 0x0f, 0x23, 0x6c, 0x15, 0x67, 0x27, 0x07, + 0x54, 0x9b, 0x8e, 0x39, 0xaa, 0x04, 0x68, 0x88, 0x01, 0xc2, 0x02, 0x0b, 0x90, 0x43, 0x76, 0xe8, 0x14, 0x02, 0x88, + 0xb5, 0xc4, 0x9b, 0x1c, 0xe7, 0xac, 0xcc, 0xa3, 0x60, 0x2b, 0xf5, 0xf4, 0x04, 0xb3, 0xc2, 0xc1, 0x41, 0x0d, 0x71, + 0x64, 0x4e, 0x0e, 0xe8, 0x51, 0xa9, 0xec, 0x88, 0xa2, 0x13, 0x21, 0x86, 0xf7, 0x79, 0x07, 0x9f, 0xb4, 0x55, 0x92, + 0x94, 0xad, 0xa0, 0xd4, 0xcb, 0x54, 0x65, 0xc9, 0x79, 0x22, 0x1e, 0xb0, 0x0a, 0xa2, 0x59, 0xd8, 0xb0, 0x77, 0x55, + 0x65, 0xe9, 0xdd, 0x21, 0xe4, 0xe2, 0x8d, 0x77, 0xa7, 0x39, 0xfc, 0x55, 0xb1, 0xd7, 0x92, 0xf2, 0x5e, 0x9b, 0xf0, + 0xc9, 0x05, 0x57, 0x15, 0xc1, 0x0b, 0x6b, 0x0b, 0x34, 0x01, 0x68, 0x98, 0xdc, 0x85, 0x98, 0xdc, 0x69, 0xcb, 0xe4, + 0x4e, 0xb7, 0x4c, 0x6e, 0xc0, 0x27, 0x52, 0xc9, 0x51, 0x17, 0xa3, 0xfb, 0x61, 0x8e, 0x3c, 0xce, 0x61, 0xf4, 0xf9, + 0x3e, 0x43, 0x3c, 0x99, 0x49, 0x00, 0xe6, 0x77, 0x2d, 0xb8, 0x6a, 0xc2, 0x8b, 0x84, 0x88, 0x3a, 0xe0, 0xf9, 0xae, + 0x13, 0x70, 0x43, 0x96, 0x57, 0x2d, 0xa8, 0xc2, 0x0b, 0xab, 0x94, 0x32, 0xd8, 0x6b, 0x8b, 0x16, 0x48, 0x17, 0x5b, + 0x20, 0x9d, 0x94, 0xb6, 0x2e, 0x07, 0x9b, 0x36, 0xa1, 0x0c, 0x14, 0xac, 0x41, 0x30, 0x49, 0xc6, 0x25, 0x53, 0x5e, + 0x87, 0xed, 0x34, 0x56, 0x5a, 0x51, 0x2b, 0x2f, 0x49, 0x6e, 0xa3, 0x18, 0xee, 0xf4, 0xa0, 0x9b, 0x4f, 0x5b, 0x52, + 0x4b, 0x3f, 0xe4, 0xe1, 0x82, 0x5a, 0x17, 0x53, 0xf1, 0x5e, 0x5e, 0x52, 0x6e, 0xb7, 0x4b, 0x35, 0x56, 0x5e, 0x9a, + 0xb2, 0x18, 0x91, 0xee, 0x41, 0x5c, 0xf9, 0xff, 0x92, 0x65, 0xd6, 0x40, 0x43, 0x02, 0x89, 0xaa, 0x23, 0x85, 0x5e, + 0x01, 0x53, 0x15, 0x8b, 0x83, 0x58, 0x0a, 0x3d, 0x18, 0xe7, 0x88, 0xff, 0x11, 0xb7, 0xab, 0x16, 0x4b, 0xce, 0x71, + 0xce, 0x91, 0x6e, 0xad, 0xbc, 0xe9, 0x3b, 0xb8, 0x3a, 0xd6, 0x5c, 0x03, 0xcc, 0xc0, 0xe5, 0x40, 0x83, 0x31, 0x71, + 0x79, 0x93, 0x82, 0x48, 0x22, 0x95, 0xe4, 0x46, 0x76, 0xe0, 0x9f, 0xeb, 0x99, 0xb3, 0xab, 0x8d, 0x9b, 0x1d, 0xcc, + 0x7d, 0xbd, 0x46, 0x35, 0x81, 0xb4, 0x05, 0xc3, 0xd3, 0x5c, 0x13, 0x1b, 0x18, 0xce, 0x91, 0x0e, 0x77, 0x0b, 0x97, + 0x90, 0x31, 0xd7, 0x16, 0xf2, 0xef, 0x28, 0x86, 0x53, 0xeb, 0xd2, 0xbe, 0xca, 0x1e, 0xcf, 0xf1, 0x97, 0x73, 0x95, + 0x3d, 0x1e, 0xe3, 0x2f, 0xf7, 0x0a, 0x73, 0x23, 0x36, 0x08, 0xfe, 0x12, 0xcc, 0xea, 0x69, 0x69, 0x3d, 0x91, 0x85, + 0xe3, 0x27, 0x2c, 0x1b, 0x3e, 0xc1, 0x0f, 0x1f, 0x6d, 0x12, 0xf0, 0xe9, 0x95, 0x61, 0x08, 0xad, 0x58, 0xcf, 0x1a, + 0xcb, 0xe7, 0x2d, 0xe5, 0x63, 0xfd, 0x0f, 0x3e, 0xf8, 0x71, 0x95, 0x44, 0xc5, 0x99, 0x52, 0x56, 0x5b, 0x5c, 0x8f, + 0xfd, 0xd0, 0x8b, 0xef, 0xaf, 0x49, 0xae, 0xd0, 0x04, 0xd3, 0x9e, 0xab, 0x63, 0x88, 0xbb, 0x2c, 0x5f, 0xa8, 0xa6, + 0xd2, 0x65, 0xc1, 0x3d, 0x3f, 0xe8, 0x87, 0x7f, 0x8d, 0x25, 0xb6, 0xad, 0x24, 0x79, 0xf2, 0x09, 0x29, 0x7d, 0xe8, + 0xfa, 0xd1, 0x46, 0x63, 0xf5, 0x6e, 0x2a, 0xd0, 0x56, 0xf8, 0x42, 0x98, 0x1e, 0x94, 0x62, 0x97, 0x53, 0xbf, 0x8f, + 0x37, 0xa6, 0xe3, 0xe8, 0xce, 0x7c, 0xb4, 0x49, 0xcf, 0xd4, 0xa5, 0x17, 0x7f, 0x60, 0x53, 0x73, 0xe2, 0xc7, 0x93, + 0x80, 0xa9, 0x7d, 0x75, 0x1c, 0x78, 0xe1, 0x07, 0xfe, 0x68, 0x46, 0xeb, 0x14, 0x6d, 0x20, 0x76, 0x0a, 0xbd, 0x02, + 0x27, 0xa4, 0x5e, 0x45, 0x66, 0xb5, 0x01, 0x0b, 0xca, 0xf3, 0x5c, 0x41, 0x56, 0x30, 0x8a, 0x45, 0x2d, 0x03, 0x4c, + 0x78, 0xc1, 0x2c, 0x03, 0x7c, 0xa2, 0x0d, 0x15, 0xe7, 0x4b, 0x35, 0x64, 0x50, 0x49, 0xb1, 0x9c, 0x27, 0xf5, 0xbc, + 0xc6, 0x1e, 0xfe, 0xfd, 0xcf, 0x51, 0xba, 0xf5, 0xfd, 0x3f, 0x97, 0xf7, 0xf2, 0x79, 0x10, 0x42, 0xa9, 0x49, 0xbe, + 0x38, 0x9f, 0xf0, 0x71, 0xce, 0x60, 0xb6, 0x7f, 0x5a, 0x6e, 0xec, 0x25, 0xc9, 0x7a, 0xc9, 0xa6, 0x74, 0xf7, 0x7c, + 0x56, 0x0c, 0xaa, 0x2c, 0x59, 0xc8, 0x03, 0xfb, 0x65, 0xed, 0x1e, 0x1f, 0x3e, 0x07, 0x9b, 0x18, 0x60, 0x28, 0xa3, + 0xd9, 0x4c, 0x2d, 0x84, 0xfb, 0x1d, 0xcd, 0x9c, 0xc3, 0x5f, 0xd6, 0xaf, 0x5e, 0xda, 0xaf, 0xf2, 0xc6, 0x21, 0x30, + 0xc6, 0xe2, 0x82, 0x9f, 0xf3, 0xc5, 0xd2, 0x78, 0x05, 0x44, 0x33, 0x2f, 0x6c, 0x07, 0xe7, 0xb2, 0xb4, 0xc4, 0x57, + 0x8c, 0x4d, 0x81, 0xe1, 0x36, 0x6a, 0xa5, 0xd7, 0x01, 0xbb, 0x61, 0xb9, 0xf1, 0x40, 0xfd, 0x63, 0x0d, 0x2d, 0x30, + 0xba, 0x21, 0x37, 0x4a, 0xe0, 0x5c, 0x9d, 0x04, 0xd2, 0x08, 0x61, 0xe0, 0x90, 0xcb, 0x5b, 0xac, 0xb2, 0xa5, 0x46, + 0x86, 0x2a, 0x0d, 0xa0, 0x75, 0x64, 0x67, 0x2d, 0xe5, 0x7d, 0x4c, 0x6d, 0xde, 0x3c, 0x36, 0xc3, 0xd1, 0xfb, 0x10, + 0x0d, 0x9e, 0xe3, 0x29, 0x80, 0x9d, 0xa7, 0x15, 0x7a, 0x90, 0x36, 0x8c, 0x35, 0x69, 0xc7, 0x54, 0x52, 0xbb, 0x08, + 0x7b, 0x5a, 0x34, 0x2c, 0x17, 0x0a, 0xa8, 0xc6, 0xb9, 0x51, 0xca, 0x90, 0x8f, 0x31, 0x65, 0x70, 0xc8, 0x92, 0xa4, + 0x15, 0x61, 0xf9, 0xa4, 0x1b, 0x6a, 0x51, 0xbb, 0x8c, 0x8f, 0xa2, 0xdc, 0xb0, 0x0d, 0x60, 0x09, 0x10, 0xc0, 0xea, + 0xb7, 0xf0, 0x78, 0xb9, 0x5e, 0x72, 0x8b, 0xa8, 0x78, 0x3e, 0x56, 0xb9, 0xb5, 0x4a, 0xdb, 0xfb, 0x5b, 0x95, 0x0f, + 0xaa, 0x74, 0x4c, 0x37, 0x0e, 0x4d, 0x2b, 0x91, 0xde, 0x9a, 0x9e, 0x08, 0x3b, 0x10, 0x63, 0xaa, 0xd0, 0x57, 0x36, + 0x9b, 0xb1, 0x49, 0x9a, 0xe8, 0x42, 0x6b, 0x94, 0xc7, 0x27, 0x06, 0xbf, 0xb4, 0x07, 0x43, 0xf5, 0x47, 0x88, 0xd2, + 0x20, 0xc2, 0x78, 0xf1, 0x01, 0x09, 0x99, 0xa9, 0x19, 0x4d, 0xd4, 0x63, 0x19, 0x45, 0xfc, 0x2b, 0xa0, 0x38, 0x6e, + 0x28, 0xc7, 0xa1, 0xf1, 0xf3, 0xa7, 0x58, 0x17, 0x51, 0x6e, 0x37, 0xb5, 0x9d, 0x14, 0x6d, 0xdb, 0xbe, 0x1b, 0xe7, + 0x55, 0xd7, 0xb1, 0x33, 0xd5, 0x00, 0xef, 0xc0, 0x0f, 0xfb, 0x6e, 0x7a, 0x6c, 0xd5, 0x81, 0x56, 0xeb, 0xf0, 0x53, + 0xda, 0xb9, 0xce, 0x33, 0x47, 0x35, 0xc8, 0x28, 0x53, 0xa2, 0x6d, 0x93, 0xe8, 0x86, 0xc5, 0x9f, 0x0d, 0x4a, 0xb9, + 0xf3, 0xfd, 0xc6, 0x73, 0xe4, 0xd8, 0x40, 0x84, 0xd3, 0x68, 0xf5, 0x09, 0x20, 0x74, 0x54, 0x43, 0x9d, 0x04, 0x51, + 0xc2, 0x64, 0x18, 0x48, 0x09, 0xf2, 0x99, 0x40, 0xfc, 0xf4, 0xf6, 0xe5, 0xbb, 0x77, 0xaa, 0x81, 0xb9, 0x66, 0x13, + 0xb9, 0x77, 0xbe, 0xa0, 0x76, 0x50, 0xff, 0xc6, 0x75, 0x47, 0x27, 0x0c, 0x09, 0xb5, 0xe5, 0x35, 0x47, 0x65, 0xb5, + 0x25, 0xc7, 0x4f, 0x1e, 0xfe, 0x65, 0x92, 0x44, 0xf7, 0x82, 0xab, 0x81, 0x36, 0x6c, 0x3f, 0xde, 0x4a, 0x25, 0x4b, + 0x3f, 0xbc, 0x6e, 0x28, 0xf5, 0xee, 0x1a, 0x4a, 0x41, 0x94, 0xab, 0xd1, 0xaa, 0x75, 0xb4, 0x94, 0x58, 0x03, 0x48, + 0x15, 0xbe, 0x0b, 0x5d, 0x92, 0x3c, 0xf5, 0x19, 0x83, 0xe6, 0xb9, 0x02, 0xaa, 0xa3, 0x6e, 0x28, 0xe6, 0x42, 0x50, + 0x8e, 0xdb, 0x49, 0x00, 0x26, 0xa5, 0x4c, 0xbe, 0xc5, 0x2b, 0xb3, 0x8d, 0xdc, 0x20, 0x7c, 0x58, 0xe1, 0xd0, 0xa9, + 0x19, 0xdd, 0x7c, 0x70, 0xfa, 0xbe, 0xf2, 0xa6, 0x60, 0xa7, 0x69, 0x8e, 0xa3, 0x34, 0x8d, 0x96, 0x7d, 0xc7, 0x5e, + 0xdd, 0xa9, 0xca, 0x40, 0x28, 0x1e, 0xb8, 0x19, 0x69, 0xff, 0xb7, 0x7f, 0x55, 0x48, 0x2e, 0x95, 0x5f, 0xa7, 0x6c, + 0xb9, 0x62, 0xb1, 0x97, 0xae, 0x63, 0x96, 0x29, 0xbf, 0xfd, 0xdf, 0xf3, 0x8a, 0x90, 0x3d, 0x90, 0xdb, 0x10, 0x7b, + 0x2d, 0x37, 0xb9, 0x0e, 0xa2, 0xdb, 0x07, 0x85, 0xc3, 0xc8, 0x8e, 0xca, 0x0b, 0x7f, 0xbe, 0xc8, 0x6b, 0x9f, 0xa5, + 0x5b, 0x60, 0x13, 0xa3, 0x27, 0x6d, 0xbb, 0x72, 0x1e, 0xdd, 0xf6, 0x7f, 0xfb, 0x57, 0xae, 0x3c, 0xd9, 0xb9, 0xea, + 0x9a, 0x07, 0x5a, 0x9e, 0xd1, 0xe6, 0x3a, 0xb5, 0x29, 0x86, 0xf7, 0xb5, 0x09, 0xae, 0x15, 0xd2, 0xaa, 0xac, 0x5f, + 0x6d, 0x6d, 0x81, 0xe9, 0x2f, 0xfe, 0x7c, 0xf1, 0xb9, 0x40, 0x01, 0x42, 0x77, 0x42, 0x05, 0x95, 0xbe, 0x00, 0x58, + 0xa3, 0xfe, 0xfe, 0x13, 0xf6, 0x99, 0x70, 0xed, 0x02, 0xe9, 0x4b, 0x40, 0xc3, 0xb5, 0xa8, 0xcf, 0x47, 0xa3, 0x3c, + 0xd7, 0xa2, 0xdc, 0x1e, 0x5c, 0x5e, 0xce, 0x6a, 0x25, 0xfc, 0xa8, 0xef, 0xdb, 0x3a, 0xc5, 0xa2, 0xd8, 0x03, 0x21, + 0x68, 0xbc, 0xd9, 0x80, 0x8e, 0x76, 0x7a, 0x4d, 0x3e, 0x18, 0xb5, 0x6f, 0xd7, 0x88, 0x35, 0x94, 0x62, 0x9e, 0xbe, + 0xfc, 0x4e, 0xce, 0x68, 0x1e, 0xce, 0x6d, 0xec, 0xad, 0x48, 0x61, 0xaf, 0xe0, 0x7d, 0x04, 0x28, 0x40, 0x84, 0x44, + 0x8b, 0x69, 0x80, 0xde, 0xb4, 0x99, 0x4e, 0xfe, 0xb4, 0xdb, 0x74, 0xf2, 0x62, 0x2f, 0xd3, 0xc9, 0x9f, 0xbe, 0xb8, + 0xe9, 0xe4, 0x1b, 0xd9, 0x74, 0x12, 0xe6, 0xf2, 0x25, 0xdb, 0xcb, 0xa0, 0x51, 0x98, 0x05, 0x45, 0xb7, 0xc9, 0xd0, + 0xe1, 0x7c, 0x78, 0x32, 0x59, 0x30, 0x50, 0x6c, 0x70, 0xac, 0x07, 0xd1, 0x7c, 0xbb, 0xdd, 0xe1, 0x97, 0xb2, 0x3a, + 0x0c, 0xa2, 0xb9, 0x2a, 0x05, 0x42, 0x0e, 0x79, 0x20, 0xe4, 0x22, 0xb8, 0x19, 0x59, 0xf8, 0xd9, 0x86, 0x08, 0x85, + 0x66, 0x1e, 0xea, 0x52, 0xb2, 0xf7, 0xdc, 0xa8, 0xd3, 0x15, 0x36, 0x80, 0x7d, 0x15, 0xc2, 0xa2, 0xe4, 0x0d, 0xdd, + 0xa7, 0x22, 0xe4, 0x8b, 0xdc, 0x43, 0x6e, 0x3c, 0x4f, 0xe1, 0x53, 0x36, 0xea, 0x2f, 0x77, 0xce, 0x77, 0x97, 0xce, + 0xa0, 0xe3, 0x40, 0xcc, 0x02, 0x10, 0x8b, 0xb1, 0xc0, 0x1e, 0x74, 0x3a, 0x50, 0x70, 0x2b, 0x15, 0xb8, 0x50, 0xe0, + 0x4b, 0x05, 0x5d, 0x28, 0x98, 0x48, 0x05, 0x47, 0x50, 0x30, 0x95, 0x0a, 0x8e, 0xa1, 0xe0, 0x46, 0xcd, 0x2e, 0xc3, + 0x7c, 0xb8, 0xc7, 0xfa, 0x95, 0x41, 0x92, 0x90, 0x28, 0x3b, 0x36, 0x1c, 0xb0, 0xe3, 0xf3, 0xe6, 0xfd, 0xc8, 0x20, + 0x95, 0x68, 0x3f, 0x36, 0x6e, 0x17, 0x8c, 0xe2, 0xa7, 0xbf, 0xc0, 0x83, 0xd2, 0x4a, 0x23, 0x70, 0x27, 0x10, 0x71, + 0x49, 0x04, 0x1e, 0x14, 0x55, 0x07, 0x2d, 0xd7, 0x20, 0x9f, 0xb9, 0xb2, 0x01, 0x20, 0xce, 0x65, 0xf1, 0x8e, 0x3e, + 0x67, 0xe6, 0x4b, 0xa0, 0x30, 0xab, 0xd1, 0x64, 0x55, 0xea, 0x97, 0x30, 0x82, 0x78, 0xc1, 0xc6, 0xeb, 0xb9, 0x72, + 0x1e, 0xcd, 0x77, 0x1a, 0x3c, 0xc8, 0xaf, 0x60, 0x94, 0x2a, 0xdd, 0x19, 0x99, 0x62, 0x59, 0xf2, 0x6f, 0xd1, 0x63, + 0x56, 0xae, 0x9f, 0xc2, 0xd8, 0x94, 0x94, 0x28, 0x0e, 0x7c, 0x07, 0x70, 0x24, 0x59, 0x1c, 0x9c, 0x03, 0x9e, 0xa5, + 0xe7, 0x0b, 0x4f, 0x1a, 0xcf, 0xe9, 0x0f, 0x2c, 0x49, 0xbc, 0xb9, 0xa8, 0x5f, 0x1f, 0x27, 0x58, 0x26, 0xe5, 0x42, + 0x23, 0x22, 0x10, 0xd4, 0x8f, 0x7e, 0xcd, 0x50, 0x24, 0x8e, 0x6e, 0x15, 0x30, 0x71, 0x82, 0x05, 0x55, 0x58, 0x55, + 0xf8, 0x16, 0x4c, 0x61, 0xd9, 0xfe, 0x01, 0x36, 0xff, 0x0d, 0x0b, 0xaa, 0x85, 0xa9, 0x37, 0xaf, 0x16, 0xd1, 0x3a, + 0xc8, 0xe4, 0xb1, 0xe5, 0xe6, 0x07, 0xa5, 0xc2, 0xcf, 0xb9, 0x4f, 0x0f, 0xa2, 0xf9, 0xef, 0x7a, 0x99, 0xbe, 0xc5, + 0x08, 0xe2, 0x5d, 0x68, 0x84, 0xe9, 0xc8, 0x42, 0x1c, 0x2b, 0x16, 0xa0, 0xb0, 0x1f, 0xa6, 0x0b, 0x13, 0x3d, 0x2e, + 0x35, 0x37, 0xd4, 0x0d, 0x0b, 0xe7, 0x76, 0x53, 0xf5, 0x33, 0xef, 0xc7, 0xf3, 0xb1, 0xa7, 0x39, 0xee, 0xb1, 0x21, + 0xfe, 0x58, 0x76, 0x57, 0xcf, 0xb0, 0x07, 0x65, 0xea, 0xdf, 0x6c, 0x66, 0x51, 0x98, 0x9a, 0x33, 0x6f, 0xe9, 0x07, + 0xf7, 0xfd, 0x65, 0x14, 0x46, 0xc9, 0xca, 0x9b, 0xb0, 0x41, 0xa1, 0x05, 0x18, 0x60, 0x04, 0x13, 0xee, 0x44, 0xeb, + 0x58, 0x6e, 0xcc, 0x96, 0xd4, 0x3a, 0x0f, 0x50, 0x32, 0x0b, 0xd8, 0x5d, 0xc6, 0x3f, 0x5f, 0xaa, 0x4c, 0x55, 0x71, + 0xc9, 0x51, 0x0b, 0x60, 0xa3, 0x79, 0xf4, 0x13, 0x88, 0xf9, 0x35, 0xe0, 0xbc, 0x68, 0xdf, 0x72, 0xbb, 0x31, 0x5b, + 0x2a, 0x56, 0xb7, 0xb5, 0xf3, 0x38, 0xba, 0x3d, 0x85, 0xd1, 0x62, 0x63, 0x33, 0x61, 0xc1, 0x0c, 0xdf, 0x98, 0xe8, + 0x70, 0x25, 0xfa, 0x31, 0x51, 0x7b, 0x00, 0xbd, 0xb1, 0xe5, 0x00, 0x5e, 0xf7, 0x5d, 0xc5, 0x1e, 0x2c, 0xfd, 0xd0, + 0x24, 0x70, 0x8e, 0xed, 0x95, 0xd4, 0x97, 0x8c, 0x3f, 0x7d, 0x83, 0xd5, 0x1d, 0xc5, 0x1e, 0x80, 0x84, 0x39, 0x0b, + 0xa2, 0xdb, 0xfe, 0xc2, 0x9f, 0x4e, 0x59, 0x38, 0xc0, 0x31, 0xe7, 0x85, 0x2c, 0x08, 0xfc, 0x55, 0xe2, 0x27, 0x83, + 0xa5, 0x77, 0xc7, 0x7b, 0x3d, 0x6c, 0xeb, 0xb5, 0xc3, 0x7b, 0xed, 0xec, 0xdd, 0xab, 0xd4, 0x0d, 0x38, 0x77, 0x51, + 0x3f, 0x7c, 0x68, 0x5d, 0xc5, 0xae, 0xc0, 0xb9, 0x77, 0xaf, 0xab, 0x98, 0x6d, 0x96, 0x5e, 0x3c, 0xf7, 0xc3, 0xbe, + 0x9d, 0x59, 0x37, 0x1b, 0x5a, 0x18, 0x0f, 0x7b, 0xbd, 0x5e, 0x66, 0x4d, 0xc5, 0x93, 0x3d, 0x9d, 0x66, 0xd6, 0x44, + 0x3c, 0xcd, 0x66, 0xb6, 0x3d, 0x9b, 0x65, 0x96, 0x2f, 0x0a, 0x3a, 0xee, 0x64, 0xda, 0x71, 0x33, 0xeb, 0x56, 0xaa, + 0x91, 0x59, 0x8c, 0x3f, 0xc5, 0x6c, 0x3a, 0xc0, 0x85, 0xc4, 0x8d, 0x37, 0x8f, 0x6d, 0x3b, 0x43, 0x0a, 0x70, 0x59, + 0xa2, 0x4d, 0xa8, 0xa0, 0xba, 0xda, 0xec, 0x5d, 0x53, 0x29, 0x3e, 0x37, 0x99, 0x34, 0xd6, 0x9b, 0x7a, 0xf1, 0x87, + 0x2b, 0x45, 0x82, 0xc2, 0xf3, 0xa8, 0xda, 0x46, 0xa0, 0xc1, 0xbc, 0xeb, 0x43, 0x24, 0xbb, 0xc1, 0x38, 0x8a, 0x61, + 0xcf, 0xc6, 0xde, 0xd4, 0x5f, 0x27, 0x7d, 0xc7, 0x5d, 0xdd, 0x89, 0x22, 0xbe, 0xd6, 0x8b, 0x02, 0xdc, 0x7b, 0xfd, + 0x24, 0x0a, 0xfc, 0xa9, 0x28, 0x6a, 0xdb, 0x4b, 0x8e, 0xab, 0x0f, 0x30, 0x8e, 0x83, 0x8f, 0xd1, 0x48, 0xbc, 0x20, + 0x50, 0xac, 0x4e, 0xa2, 0x30, 0x2f, 0x41, 0xa5, 0xb8, 0x62, 0x27, 0x84, 0x17, 0x8c, 0xd9, 0xe0, 0x1c, 0xae, 0xee, + 0xf2, 0x35, 0xef, 0x1c, 0xad, 0xee, 0xb2, 0x6f, 0x96, 0x6c, 0xea, 0x7b, 0x8a, 0x56, 0xac, 0x26, 0xc7, 0x06, 0xc5, + 0xb9, 0xbe, 0x69, 0x59, 0xa6, 0x62, 0x5b, 0x40, 0xc4, 0xcf, 0x07, 0xfe, 0x72, 0x15, 0xc5, 0xa9, 0x17, 0xa6, 0x59, + 0x36, 0xba, 0xca, 0xb2, 0xc1, 0x85, 0xaf, 0x5d, 0xfe, 0x4d, 0xa3, 0x73, 0x9a, 0x2e, 0x9a, 0x32, 0xfd, 0xca, 0x78, + 0xc9, 0x64, 0x33, 0x17, 0x38, 0xc6, 0xd0, 0xc4, 0x45, 0xae, 0x4c, 0xa7, 0x64, 0xbd, 0x32, 0x21, 0x39, 0xaf, 0x4e, + 0x56, 0x33, 0xe5, 0x2a, 0x78, 0x02, 0x41, 0x85, 0x97, 0x6c, 0x78, 0x21, 0xd9, 0xcc, 0x00, 0xb3, 0x82, 0x95, 0xc9, + 0xdd, 0xe6, 0x51, 0x1b, 0xcf, 0xf8, 0xed, 0x6e, 0x9e, 0xf1, 0xef, 0xe9, 0x3e, 0x3c, 0xe3, 0xb7, 0x5f, 0x9c, 0x67, + 0x7c, 0x54, 0x77, 0xb7, 0x79, 0x1d, 0x0d, 0xd5, 0xfc, 0x5a, 0x04, 0x8e, 0xa6, 0x98, 0x02, 0x59, 0xbd, 0x4e, 0xff, + 0x5d, 0xf7, 0x18, 0xd1, 0x1b, 0xa5, 0x66, 0xa4, 0x93, 0x1b, 0x94, 0xc8, 0x6f, 0xc2, 0xe1, 0x5f, 0x63, 0xf9, 0x79, + 0x36, 0x1b, 0xbe, 0x88, 0xa4, 0x82, 0xfc, 0x89, 0x5b, 0x8c, 0x94, 0x82, 0x8e, 0xd0, 0x1b, 0x61, 0x10, 0x8a, 0x69, + 0x59, 0x20, 0x66, 0x01, 0x99, 0xb5, 0x4f, 0x73, 0x5b, 0xb9, 0x41, 0x79, 0x08, 0x5a, 0x6e, 0x7d, 0x2a, 0x3c, 0xd3, + 0x6a, 0xfa, 0xcf, 0x39, 0x4b, 0xb9, 0x2b, 0xf9, 0x77, 0xf7, 0xaf, 0xa7, 0xda, 0xeb, 0x48, 0xcf, 0xfc, 0xe4, 0x4d, + 0xd5, 0x2f, 0x8c, 0x5f, 0x58, 0x0d, 0x65, 0x70, 0x32, 0x6e, 0xef, 0x26, 0xe7, 0x5d, 0x87, 0xd7, 0xd4, 0xfc, 0xac, + 0x04, 0x69, 0x5f, 0x6e, 0xc8, 0xf3, 0xbf, 0xd5, 0x0e, 0x63, 0xee, 0x84, 0xb3, 0xe1, 0x1c, 0x20, 0xa6, 0xb4, 0x43, + 0x77, 0xfa, 0x29, 0x35, 0xf7, 0xa7, 0x59, 0xa6, 0x0f, 0x04, 0x22, 0xa4, 0x83, 0x96, 0xed, 0x62, 0xe2, 0x92, 0x42, + 0x1e, 0xe3, 0xd7, 0x9a, 0x74, 0x67, 0xf9, 0x1a, 0xac, 0x00, 0xf8, 0x0d, 0x27, 0xc7, 0x99, 0xaa, 0x10, 0xfa, 0xc8, + 0x3a, 0x44, 0x02, 0x08, 0xae, 0xad, 0x73, 0xfc, 0x8b, 0x57, 0xa2, 0xa0, 0x6e, 0x71, 0x4a, 0xc8, 0x41, 0x33, 0x06, + 0x08, 0x7e, 0x21, 0x94, 0x35, 0x44, 0x76, 0x78, 0x1d, 0x7c, 0xc8, 0xd4, 0x9c, 0xf7, 0xc3, 0xe5, 0x77, 0x7a, 0x72, + 0x00, 0x0d, 0x4e, 0x2b, 0x8a, 0x98, 0x1d, 0xf6, 0x94, 0xc0, 0x4a, 0x24, 0xb7, 0x86, 0x95, 0xdc, 0x2a, 0x4f, 0x36, + 0x22, 0x70, 0x4c, 0xea, 0xad, 0x4c, 0x90, 0xfe, 0x91, 0xf6, 0x72, 0x8a, 0x27, 0xc5, 0xa8, 0x19, 0xac, 0x13, 0xa0, + 0x8d, 0x28, 0x88, 0x22, 0xfd, 0x19, 0x4c, 0xd6, 0x71, 0x12, 0xc5, 0xfd, 0x55, 0xe4, 0x87, 0x29, 0x8b, 0x33, 0x44, + 0xd5, 0x25, 0xe2, 0x47, 0xa0, 0xe7, 0x6a, 0x13, 0xad, 0xbc, 0x89, 0x9f, 0xde, 0xf7, 0x6d, 0xce, 0x52, 0xd8, 0x03, + 0xce, 0x1d, 0xd8, 0x8d, 0xf5, 0xfb, 0x1c, 0x9b, 0x4f, 0x91, 0xf1, 0x8b, 0xeb, 0xec, 0x8c, 0xbc, 0xcc, 0x07, 0xd2, + 0x5b, 0x0a, 0x9d, 0x03, 0xec, 0x87, 0x17, 0x9b, 0x73, 0xa0, 0xf2, 0x30, 0xd5, 0xf6, 0x94, 0xcd, 0x0d, 0xa4, 0xda, + 0x70, 0x99, 0x20, 0xfe, 0x58, 0x5d, 0x5d, 0xb1, 0x9b, 0x8b, 0x81, 0xe3, 0xd1, 0xf7, 0x19, 0x59, 0xdf, 0x83, 0x44, + 0x73, 0xc6, 0x3e, 0x35, 0xc7, 0x6c, 0x16, 0xc5, 0x8c, 0xc2, 0x2c, 0x3b, 0xbd, 0xd5, 0xdd, 0xfe, 0xdd, 0x6f, 0x07, + 0xbf, 0xb9, 0x9f, 0x30, 0x4a, 0x35, 0xd1, 0x99, 0xbe, 0xa3, 0xb7, 0xfa, 0x79, 0x06, 0xac, 0x21, 0x61, 0x7e, 0x42, + 0x11, 0xed, 0xfa, 0xaa, 0x3a, 0x68, 0x8c, 0x66, 0xb7, 0x8a, 0xf8, 0x99, 0x17, 0xb3, 0xc0, 0x4b, 0xfd, 0x1b, 0xc1, + 0x33, 0x76, 0x8e, 0x56, 0x77, 0x62, 0x8e, 0xf1, 0xc0, 0xfb, 0x84, 0x49, 0xaa, 0x0c, 0x45, 0x4c, 0x52, 0xb5, 0x18, + 0x27, 0x69, 0x50, 0x83, 0x46, 0x04, 0x78, 0xa9, 0x9c, 0xf4, 0xdd, 0xd5, 0x9d, 0x7c, 0x44, 0x17, 0xcd, 0xf2, 0x93, + 0xba, 0x1a, 0x99, 0x6f, 0xe9, 0x4f, 0xa7, 0x01, 0xcb, 0x4a, 0x13, 0x5d, 0x9e, 0x4b, 0x09, 0x39, 0x39, 0x1e, 0xbc, + 0x71, 0x12, 0x05, 0xeb, 0x94, 0x35, 0xa3, 0x8b, 0x90, 0xe3, 0xda, 0x05, 0x72, 0xf0, 0x77, 0x79, 0xac, 0x5d, 0x60, + 0xb7, 0x61, 0x99, 0xd8, 0x03, 0x08, 0xc4, 0x6d, 0x76, 0xca, 0x43, 0x87, 0x57, 0xf9, 0xa0, 0x8d, 0x06, 0x40, 0x0c, + 0x38, 0x96, 0x88, 0x7a, 0x2b, 0x96, 0xc3, 0xcb, 0xf2, 0x60, 0xc4, 0x79, 0x51, 0x56, 0x06, 0xe6, 0xf7, 0xd9, 0x63, + 0xcf, 0x9a, 0xf7, 0xd8, 0x33, 0xb1, 0xc7, 0xb6, 0xaf, 0xcc, 0x87, 0x33, 0x07, 0xfe, 0x1b, 0x14, 0x00, 0xf5, 0x6d, + 0xa5, 0xb3, 0xba, 0x53, 0x9c, 0xd5, 0x9d, 0x62, 0xba, 0xab, 0x3b, 0x05, 0xbb, 0x46, 0x23, 0x16, 0xc3, 0x72, 0x75, + 0xc3, 0x56, 0xa0, 0x10, 0xfe, 0xd8, 0xa5, 0x57, 0xce, 0x21, 0xbc, 0x83, 0x56, 0xdd, 0xfa, 0x3b, 0x77, 0xfb, 0x56, + 0xa7, 0xbd, 0x24, 0x88, 0xb6, 0x6e, 0xa5, 0xde, 0x78, 0xcc, 0xa6, 0xfd, 0x59, 0x34, 0x59, 0x27, 0xff, 0xe4, 0xe3, + 0xe7, 0x48, 0xdc, 0x4a, 0x08, 0x2a, 0xfd, 0x88, 0xa6, 0x70, 0xbb, 0x73, 0xc3, 0x44, 0x0f, 0x9b, 0x7c, 0x9e, 0xfa, + 0x14, 0x35, 0xdc, 0xb5, 0x0e, 0x1b, 0x16, 0x79, 0x33, 0xa2, 0x7f, 0xb7, 0x59, 0x6a, 0x27, 0x31, 0x9f, 0x81, 0x96, + 0xad, 0xe8, 0xf8, 0x74, 0x6c, 0xf0, 0xd9, 0xb4, 0x7b, 0xcd, 0xc3, 0xbd, 0x14, 0x5f, 0xba, 0x12, 0x87, 0x0a, 0x3f, + 0xb7, 0xb8, 0x8f, 0xcc, 0xf6, 0x5e, 0xdb, 0xd6, 0x48, 0xad, 0xd7, 0x2d, 0x07, 0x42, 0x51, 0x77, 0x4f, 0x2a, 0xff, + 0xf0, 0xd9, 0x21, 0xfc, 0x47, 0x5c, 0xfd, 0xdf, 0xd3, 0x26, 0x46, 0xfd, 0x75, 0x5a, 0x62, 0xd4, 0x89, 0x55, 0x42, + 0x46, 0x7c, 0xff, 0xfa, 0xb3, 0xd9, 0xa7, 0x35, 0xd8, 0xbb, 0x36, 0xd9, 0x7f, 0x55, 0x6b, 0x7f, 0x17, 0x45, 0x90, + 0xd1, 0xb6, 0x5e, 0x5d, 0xa0, 0x87, 0xcc, 0xf3, 0xd3, 0x21, 0x34, 0x12, 0x72, 0x04, 0x99, 0x1e, 0xa8, 0xd8, 0x86, + 0x44, 0x89, 0x97, 0x6d, 0xa2, 0xc4, 0x8b, 0xdd, 0xa2, 0xc4, 0xf7, 0x7b, 0x89, 0x12, 0x2f, 0xbe, 0xb8, 0x28, 0xf1, + 0xb2, 0x2e, 0x4a, 0x5c, 0x44, 0xc2, 0xe8, 0xd7, 0x78, 0xbd, 0xe6, 0x3f, 0xdf, 0xd3, 0x4d, 0xe2, 0x79, 0x34, 0xec, + 0xda, 0x14, 0x09, 0xfc, 0xe2, 0xdf, 0x16, 0x2c, 0x70, 0x21, 0xbe, 0x45, 0x1b, 0xb8, 0x42, 0xb4, 0xe0, 0x94, 0x1d, + 0xbf, 0x23, 0x15, 0x07, 0x51, 0x38, 0xff, 0x09, 0x6e, 0x92, 0x41, 0x1d, 0x18, 0x4b, 0x2f, 0xfc, 0xe4, 0xa7, 0x68, + 0xb5, 0x5e, 0xbd, 0x86, 0xbe, 0xde, 0xfb, 0x89, 0x3f, 0x0e, 0x58, 0xee, 0xa1, 0x4f, 0x36, 0x7b, 0x5c, 0x27, 0x0e, + 0x66, 0xb2, 0xe2, 0xa7, 0x77, 0x27, 0x7e, 0xa2, 0x21, 0x2d, 0xff, 0x4d, 0xc6, 0x80, 0x6a, 0xb3, 0x20, 0x02, 0xa1, + 0xac, 0x2a, 0x83, 0xfe, 0x74, 0x61, 0xe4, 0x22, 0xd2, 0x1b, 0xa0, 0x14, 0x46, 0x1a, 0xad, 0xfd, 0xb0, 0x9a, 0x50, + 0xb3, 0xd6, 0x8d, 0x3c, 0x32, 0x5d, 0x5d, 0x0d, 0xbf, 0x8c, 0xd6, 0x09, 0x9b, 0x46, 0xb7, 0xa1, 0x6a, 0x84, 0xb9, + 0x67, 0x04, 0x5c, 0xcb, 0xe6, 0x6d, 0x30, 0xa7, 0xea, 0x3b, 0x64, 0x94, 0xa3, 0x58, 0x53, 0x21, 0xa5, 0xef, 0x7a, + 0x65, 0xd2, 0xfd, 0xb8, 0x89, 0x20, 0xaa, 0x79, 0xf2, 0xaf, 0x07, 0x9a, 0x16, 0x0d, 0x3f, 0xad, 0xa5, 0xb0, 0x2f, + 0x89, 0x2c, 0xae, 0x15, 0x4e, 0xb4, 0x50, 0x28, 0x17, 0x45, 0x78, 0x98, 0x86, 0x89, 0xe3, 0x6f, 0xc8, 0x37, 0xba, + 0x78, 0x0b, 0xc1, 0x75, 0xb2, 0x35, 0x9f, 0x0f, 0x1e, 0x2c, 0x85, 0x1e, 0x9f, 0x4b, 0x68, 0x7c, 0x73, 0xc3, 0xe2, + 0xc0, 0xbb, 0xd7, 0xf4, 0x2c, 0x0a, 0x7f, 0x00, 0x04, 0xbc, 0x88, 0x6e, 0x43, 0xb9, 0x02, 0xe6, 0x30, 0x6a, 0x58, + 0x4b, 0x8d, 0x61, 0x7d, 0xc0, 0xd7, 0x46, 0x1a, 0x01, 0x64, 0x8f, 0x9e, 0xb3, 0xbf, 0x1a, 0xf4, 0xef, 0xdf, 0xf4, + 0xcc, 0x38, 0x8f, 0xf2, 0x0f, 0xfd, 0xbc, 0xda, 0xe3, 0x33, 0x8f, 0x1f, 0x3f, 0x68, 0x07, 0x5b, 0x83, 0x44, 0xda, + 0x22, 0x27, 0xb4, 0xd6, 0xd0, 0x5a, 0x6f, 0xdd, 0x05, 0x30, 0x8a, 0x8b, 0x68, 0x3d, 0x59, 0xa0, 0x75, 0xee, 0x97, + 0x83, 0x37, 0x85, 0x3e, 0x31, 0x79, 0x6f, 0x0e, 0x7a, 0xa5, 0xa8, 0xc0, 0x02, 0x7e, 0xff, 0x25, 0xc4, 0xa5, 0xfd, + 0x0f, 0xa2, 0xa1, 0xbe, 0x6a, 0x72, 0x5f, 0xdc, 0x4f, 0x5a, 0xbc, 0x03, 0xc8, 0x31, 0xcb, 0x23, 0xbe, 0x88, 0xcb, + 0xb5, 0x66, 0x22, 0x93, 0x55, 0x91, 0x26, 0x47, 0x57, 0x6c, 0x0b, 0x1c, 0x29, 0xbe, 0xc2, 0x2c, 0x12, 0xd3, 0xb9, + 0x77, 0x84, 0xc1, 0x38, 0xb5, 0xaa, 0x10, 0x19, 0x6e, 0xa7, 0xc1, 0x90, 0x7c, 0x55, 0xdf, 0x2d, 0xfd, 0xd0, 0xc0, + 0xe4, 0x08, 0xf5, 0x37, 0xde, 0x1d, 0x84, 0x07, 0x07, 0xe2, 0x56, 0x7d, 0x05, 0x85, 0x86, 0xec, 0xe5, 0x07, 0x19, + 0xd0, 0xd4, 0x46, 0x4c, 0x88, 0x5b, 0xbc, 0xd1, 0x57, 0x8a, 0xa2, 0x28, 0xb9, 0x18, 0xa1, 0xe4, 0x72, 0x04, 0x96, + 0xa3, 0x38, 0x00, 0xb7, 0x25, 0xd9, 0xea, 0x8e, 0x4a, 0x40, 0x32, 0xc0, 0x9b, 0x59, 0x51, 0xc0, 0x23, 0x60, 0x76, + 0x6d, 0x51, 0x20, 0x04, 0x7a, 0x88, 0x5e, 0xe8, 0xc5, 0x10, 0x28, 0xbb, 0xaf, 0xa0, 0xc0, 0x8e, 0x6f, 0xb9, 0x26, + 0x58, 0xb1, 0xe9, 0x71, 0x34, 0x60, 0xcd, 0xa1, 0x12, 0x43, 0x89, 0x0a, 0xc2, 0xad, 0x43, 0x25, 0xf2, 0xb9, 0xc1, + 0x1a, 0x68, 0x23, 0xca, 0x45, 0x77, 0xe9, 0x92, 0x85, 0x6b, 0x15, 0x53, 0xa5, 0x61, 0xe8, 0x4a, 0xa8, 0xf3, 0x82, + 0x98, 0x2d, 0xa0, 0x36, 0xcd, 0x2d, 0x17, 0x74, 0x16, 0x26, 0x9c, 0xa4, 0x7a, 0xc6, 0x84, 0x5f, 0x6c, 0x26, 0x9c, + 0xb6, 0x55, 0x4f, 0x08, 0x3e, 0xa5, 0x51, 0xd5, 0xfb, 0x8c, 0xcc, 0xb7, 0x31, 0x2e, 0x0b, 0x2a, 0x8d, 0xb8, 0xba, + 0x48, 0xa0, 0x5d, 0xf3, 0xaa, 0x93, 0x96, 0xdf, 0xc8, 0x78, 0x15, 0x45, 0x51, 0xac, 0xd7, 0xbb, 0xe1, 0xe3, 0x84, + 0x78, 0x5d, 0xad, 0xfd, 0x4c, 0x6a, 0xfd, 0xb4, 0x00, 0xfd, 0x81, 0xdd, 0xd3, 0x41, 0x42, 0xa8, 0xfa, 0xc0, 0xee, + 0xc1, 0x60, 0xf1, 0x25, 0x68, 0x53, 0xd4, 0x2d, 0xe4, 0xda, 0x80, 0x0c, 0x18, 0x13, 0x88, 0xe1, 0xb6, 0x65, 0x03, + 0xd9, 0xd9, 0x16, 0x2a, 0x8e, 0x28, 0x86, 0x64, 0xe7, 0x62, 0x13, 0x73, 0xbf, 0x04, 0xad, 0x11, 0xc7, 0x66, 0xc3, + 0xd6, 0xd0, 0x9f, 0x38, 0xb6, 0x7d, 0x50, 0xab, 0x0f, 0x8a, 0xec, 0xa6, 0xda, 0xba, 0x91, 0x0e, 0x1d, 0xdb, 0xf4, + 0x9f, 0x58, 0xee, 0xa0, 0x76, 0x46, 0x4b, 0x21, 0x56, 0x47, 0xa8, 0xfe, 0x3a, 0x7d, 0xb4, 0xd1, 0x6a, 0x1b, 0x52, + 0xaf, 0xda, 0xf9, 0xe3, 0xd8, 0x32, 0xae, 0xff, 0x1a, 0xd5, 0x8f, 0x7e, 0x0a, 0xf0, 0x4a, 0xe9, 0x7e, 0x46, 0x10, + 0x24, 0x5c, 0x83, 0x6d, 0xf4, 0x27, 0xe5, 0xa9, 0xa2, 0xd1, 0xf6, 0xd1, 0xf5, 0x51, 0x9e, 0x45, 0x5e, 0x38, 0xc2, + 0xc9, 0x1d, 0x54, 0xbe, 0x98, 0x54, 0x29, 0x1c, 0x0f, 0x47, 0xcc, 0x8a, 0x1b, 0xbd, 0xad, 0xdc, 0x02, 0xf6, 0xdf, + 0x72, 0x7c, 0x5a, 0x63, 0x08, 0xbe, 0x00, 0x35, 0x20, 0xa5, 0xc0, 0xce, 0x0e, 0x21, 0xb6, 0x88, 0xdc, 0x5d, 0xf9, + 0x90, 0xdc, 0xbf, 0x33, 0x3c, 0x74, 0xf0, 0x0e, 0x2d, 0xef, 0xaf, 0xf9, 0xb8, 0xfb, 0xc4, 0x2e, 0x59, 0x38, 0x2d, + 0x77, 0x58, 0x39, 0xbf, 0xf6, 0xef, 0xae, 0x44, 0x51, 0x20, 0xd7, 0x46, 0xd4, 0x40, 0x51, 0xb2, 0x28, 0xc4, 0xc5, + 0x4f, 0xdb, 0xcd, 0xdf, 0x8b, 0x8b, 0xc1, 0x06, 0x14, 0x28, 0x2f, 0x6f, 0x26, 0x29, 0xc5, 0x21, 0xa0, 0x3b, 0x7a, + 0x31, 0x6b, 0x82, 0x11, 0x6d, 0x5d, 0x89, 0xa9, 0x30, 0x84, 0x2c, 0xda, 0xf8, 0x3c, 0x44, 0xeb, 0xbe, 0x5a, 0x6b, + 0x7f, 0xb7, 0xd6, 0x3a, 0xdd, 0xa5, 0xb5, 0x26, 0x1f, 0x30, 0xb2, 0xde, 0xc9, 0x7d, 0xe1, 0x04, 0x73, 0x2e, 0x7b, + 0x13, 0x96, 0x54, 0xdd, 0xe8, 0x32, 0x26, 0x5a, 0xd5, 0x7a, 0x23, 0xd3, 0x46, 0x54, 0x7f, 0x4b, 0x02, 0x8a, 0xb8, + 0x50, 0x97, 0x75, 0xe3, 0x17, 0x85, 0x6e, 0x9c, 0xa4, 0x9a, 0xc2, 0xfb, 0x47, 0x70, 0xff, 0x92, 0x67, 0x5d, 0x2e, + 0x1d, 0x14, 0x1e, 0x76, 0xc5, 0x48, 0x25, 0x9f, 0xb1, 0x42, 0xd0, 0x90, 0x3c, 0x11, 0x85, 0x94, 0x51, 0x76, 0x48, + 0x2c, 0x57, 0x2d, 0x5c, 0xc6, 0x8a, 0x72, 0xd0, 0xba, 0xe3, 0x90, 0xf3, 0x62, 0x79, 0xd9, 0x94, 0x7d, 0x86, 0xe4, + 0xd7, 0xd2, 0x22, 0xc9, 0x9d, 0x7b, 0x08, 0xc1, 0x42, 0x4d, 0x5f, 0xb9, 0xd7, 0xce, 0x6d, 0x20, 0x70, 0x90, 0x0d, + 0xbe, 0x88, 0xbb, 0xb5, 0xf3, 0x94, 0x46, 0xa4, 0xb8, 0xba, 0x76, 0xf0, 0x74, 0xa7, 0x9b, 0x60, 0x59, 0x1f, 0x81, + 0xb8, 0xbe, 0x92, 0x34, 0x08, 0x7d, 0x5b, 0xb1, 0x07, 0x0d, 0x0c, 0x00, 0x9e, 0xff, 0xd5, 0x67, 0xce, 0x0a, 0x80, + 0x26, 0x52, 0xb1, 0xe5, 0x3b, 0x7f, 0xdc, 0xc4, 0x26, 0x99, 0x1f, 0x63, 0xd5, 0xfa, 0x37, 0x49, 0xdf, 0xb3, 0xe1, + 0x9e, 0x3f, 0x65, 0x75, 0x3e, 0xaf, 0xd1, 0x17, 0xe3, 0xe0, 0xab, 0x2c, 0x5e, 0x87, 0x98, 0x1d, 0xc2, 0x4c, 0x63, + 0x6f, 0xf2, 0x61, 0x23, 0x7d, 0x8f, 0xab, 0x44, 0x41, 0x5d, 0x5c, 0xbe, 0x54, 0x18, 0x78, 0x18, 0x4c, 0x95, 0xf5, + 0x2d, 0x37, 0x91, 0x14, 0x35, 0xfd, 0x87, 0x76, 0xc7, 0x7b, 0x36, 0x3b, 0xac, 0xe8, 0x4f, 0xdd, 0x6e, 0x59, 0xbb, + 0x9e, 0x8f, 0x63, 0x19, 0xfd, 0xca, 0x7d, 0x24, 0xff, 0xf8, 0x4f, 0x27, 0xfc, 0x9b, 0x95, 0x39, 0xfa, 0x9c, 0x21, + 0x40, 0xfb, 0xd2, 0xc5, 0xb4, 0x7c, 0x4d, 0x53, 0x2b, 0x69, 0x1b, 0xd6, 0xcc, 0x0f, 0x02, 0x33, 0x00, 0x4f, 0x95, + 0xcd, 0x67, 0x81, 0x87, 0xfd, 0xac, 0x21, 0x8c, 0xf7, 0x67, 0xf4, 0x53, 0x5e, 0x29, 0xe9, 0x62, 0xbd, 0x1c, 0x6f, + 0x64, 0x45, 0xb9, 0xa4, 0x3f, 0xaf, 0xeb, 0xcc, 0xe5, 0xcf, 0xce, 0x66, 0xb3, 0xb2, 0xd6, 0xd8, 0x56, 0x0e, 0x51, + 0xf3, 0xfb, 0xd0, 0xb6, 0xed, 0x2a, 0x7e, 0xdb, 0x36, 0x0a, 0x6d, 0x0c, 0x13, 0x95, 0xf0, 0xbd, 0xdd, 0x6b, 0xea, + 0x0f, 0x1a, 0x2d, 0x75, 0xd5, 0xb6, 0x1f, 0x69, 0xa9, 0xfd, 0x57, 0x0c, 0x05, 0x49, 0xc3, 0xae, 0xed, 0x5f, 0x5f, + 0x2b, 0x5b, 0x7a, 0xaa, 0x6e, 0xe0, 0x4f, 0x6b, 0xbc, 0x63, 0xad, 0xef, 0xd1, 0xb4, 0x6d, 0x79, 0x67, 0x56, 0x71, + 0xec, 0x96, 0x6c, 0x96, 0x06, 0x64, 0xa9, 0xe4, 0xa7, 0x6c, 0x99, 0xf4, 0x27, 0x0c, 0x2f, 0x48, 0x2d, 0xe9, 0xb4, + 0x45, 0xab, 0x1e, 0x73, 0x0e, 0x76, 0x5c, 0x8e, 0xa0, 0xc3, 0xb6, 0x82, 0x97, 0x55, 0xb5, 0x9b, 0x35, 0xf1, 0x11, + 0x3c, 0xc5, 0x36, 0xf5, 0x0b, 0x27, 0x5c, 0xa6, 0x5d, 0xfb, 0x4f, 0xa5, 0x7a, 0x0a, 0x70, 0xa7, 0x1b, 0x61, 0x6d, + 0x42, 0x97, 0x27, 0xf8, 0x77, 0x7e, 0x39, 0xf7, 0x6c, 0x75, 0x57, 0x36, 0xee, 0xea, 0xc1, 0x75, 0x53, 0x71, 0x94, + 0xd1, 0xa8, 0x9b, 0x48, 0x5f, 0x6e, 0x02, 0x34, 0x93, 0xad, 0x5b, 0xc0, 0x82, 0xa6, 0x94, 0xb4, 0xaa, 0xe1, 0x6e, + 0x0c, 0xc5, 0x59, 0x58, 0x79, 0x85, 0x7e, 0xbf, 0x48, 0xb9, 0x0a, 0x30, 0x18, 0x4f, 0x7b, 0x78, 0xb9, 0x57, 0x5a, + 0xaa, 0x68, 0x2a, 0x83, 0x6b, 0x40, 0x48, 0xa4, 0xca, 0x3a, 0x0e, 0x4c, 0xca, 0xe8, 0xa4, 0xe9, 0x9b, 0x3a, 0xdc, + 0xed, 0xdd, 0x3b, 0x5d, 0xb8, 0xd7, 0xa8, 0xa3, 0x6a, 0xaf, 0xab, 0xbd, 0xea, 0x1d, 0xb6, 0x18, 0x27, 0xcc, 0x00, + 0x78, 0x52, 0x28, 0x68, 0x34, 0xa4, 0x54, 0x68, 0x1f, 0x01, 0x9d, 0xbf, 0x95, 0x89, 0xb5, 0x80, 0x13, 0xbb, 0x6b, + 0xae, 0x42, 0x7d, 0x8b, 0x9b, 0x41, 0xc0, 0x1d, 0xa7, 0x4e, 0xf8, 0x6c, 0xc2, 0x8a, 0x91, 0xc9, 0x95, 0x83, 0x2b, + 0x08, 0x77, 0xa9, 0x89, 0x7c, 0x72, 0x42, 0xbb, 0x14, 0xef, 0x12, 0xbe, 0x6f, 0x54, 0xde, 0x5f, 0x94, 0xb4, 0xf1, + 0xdc, 0x9d, 0xc5, 0xd5, 0xf7, 0xaa, 0xbd, 0xf4, 0xc3, 0xfd, 0xeb, 0x7a, 0x77, 0x7b, 0xd7, 0x05, 0xe6, 0x70, 0xef, + 0xca, 0xc0, 0x5d, 0x92, 0x95, 0x52, 0x3a, 0xfc, 0x5e, 0xba, 0x3c, 0x90, 0xc3, 0x22, 0xa8, 0xd8, 0x8a, 0x24, 0xfa, + 0x8b, 0xf5, 0x70, 0x74, 0x72, 0x76, 0xb7, 0x0c, 0x94, 0x1b, 0x16, 0x43, 0x76, 0xbb, 0xa1, 0xea, 0x58, 0xb6, 0xaa, + 0xa0, 0x93, 0xbf, 0x1f, 0xce, 0x87, 0xea, 0xcf, 0x17, 0xaf, 0xcc, 0x9e, 0x7a, 0x06, 0xe6, 0x18, 0x37, 0x73, 0x64, + 0x71, 0xcf, 0xbd, 0x7b, 0x16, 0x5f, 0xbb, 0xaa, 0x82, 0x49, 0xec, 0x88, 0xb9, 0xc5, 0x32, 0xc5, 0x55, 0xf7, 0xc8, + 0x95, 0xa4, 0x88, 0x74, 0xa7, 0x2a, 0x10, 0x56, 0xc7, 0xed, 0x29, 0x8e, 0x7b, 0x68, 0x1d, 0xf5, 0xd4, 0xd3, 0xaf, + 0x14, 0xe5, 0x64, 0xca, 0x66, 0xc9, 0x29, 0xaa, 0x63, 0x4e, 0x90, 0x1f, 0xa4, 0xdf, 0x8a, 0x62, 0x4d, 0x82, 0xc4, + 0x74, 0x94, 0x0d, 0x7f, 0x54, 0x14, 0x20, 0x46, 0x7d, 0xe5, 0xe1, 0xcc, 0x9d, 0x1d, 0xce, 0x9e, 0x0d, 0x78, 0x71, + 0xf6, 0x55, 0xa9, 0xba, 0x41, 0xff, 0xba, 0x52, 0xb3, 0x24, 0x8d, 0xa3, 0x0f, 0x8c, 0xf3, 0x92, 0x4a, 0xae, 0x28, + 0xaa, 0x36, 0x75, 0xeb, 0x5f, 0x72, 0x7a, 0xe3, 0xc9, 0xcc, 0x2d, 0xaa, 0xe3, 0x18, 0x0f, 0xf2, 0x41, 0x9e, 0x1c, + 0x88, 0xa1, 0x9f, 0xc8, 0x68, 0x72, 0xcc, 0x26, 0x44, 0x39, 0x2a, 0x87, 0x71, 0x2e, 0xe0, 0x3b, 0x81, 0x50, 0xc4, + 0x85, 0x03, 0x42, 0x82, 0xcd, 0x86, 0xea, 0x0f, 0x8e, 0xdb, 0x33, 0x1c, 0xe7, 0xc8, 0x3a, 0xea, 0x4d, 0x6c, 0xe3, + 0xd0, 0x3a, 0x34, 0x3b, 0xd6, 0x91, 0xd1, 0x33, 0x7b, 0x46, 0xef, 0x2f, 0xbd, 0x89, 0x79, 0x68, 0x1d, 0x1a, 0xb6, + 0xd9, 0x83, 0x42, 0xb3, 0x67, 0xf6, 0x6e, 0xcc, 0xc3, 0xde, 0xc4, 0xc6, 0x52, 0xd7, 0xea, 0x76, 0x4d, 0xc7, 0xb6, + 0xba, 0x5d, 0xa3, 0x6b, 0x1d, 0x1d, 0x99, 0x4e, 0xc7, 0x3a, 0x3a, 0x3a, 0xef, 0xf6, 0xac, 0x0e, 0xbc, 0xeb, 0x74, + 0x26, 0x1d, 0xcb, 0x71, 0x4c, 0xf8, 0xcb, 0xe8, 0x59, 0x2e, 0xfd, 0x70, 0x1c, 0xab, 0xe3, 0x18, 0x76, 0xd0, 0x75, + 0xad, 0xa3, 0x67, 0x06, 0xfe, 0x8d, 0xd5, 0x0c, 0xfc, 0x0b, 0xba, 0x31, 0x9e, 0x59, 0xee, 0x11, 0xfd, 0xc2, 0x0e, + 0x6f, 0x0e, 0x7b, 0x7f, 0x57, 0x0f, 0x5a, 0x61, 0x70, 0x08, 0x86, 0x5e, 0xd7, 0xea, 0x74, 0x8c, 0x43, 0xc7, 0xea, + 0x75, 0x16, 0xe6, 0xa1, 0x6b, 0x1d, 0x1d, 0x4f, 0x4c, 0xc7, 0x3a, 0x3e, 0x36, 0x6c, 0xb3, 0x63, 0xb9, 0x86, 0x63, + 0x1d, 0x76, 0xf0, 0x47, 0xc7, 0x72, 0x6f, 0x8e, 0x9f, 0x59, 0x47, 0xdd, 0xc5, 0x91, 0x75, 0xf8, 0xfe, 0xb0, 0x67, + 0xb9, 0x9d, 0x45, 0xe7, 0xc8, 0x72, 0x8f, 0x6f, 0x8e, 0xac, 0xc3, 0x85, 0xe9, 0x1e, 0x6d, 0x6d, 0xe9, 0xb8, 0x16, + 0xe0, 0x08, 0x5f, 0xc3, 0x0b, 0x83, 0xbf, 0x80, 0x3f, 0x0b, 0x6c, 0xfb, 0x07, 0x76, 0x93, 0xd4, 0x9b, 0x3e, 0xb3, + 0x7a, 0xc7, 0x13, 0xaa, 0x0e, 0x05, 0xa6, 0xa8, 0x01, 0x4d, 0x6e, 0x4c, 0xfa, 0x2c, 0x76, 0x67, 0x8a, 0x8e, 0xc4, + 0x1f, 0xfe, 0xb1, 0x1b, 0x13, 0x3e, 0x4c, 0xdf, 0xfd, 0x8f, 0xf6, 0x93, 0x4f, 0x39, 0x24, 0x6f, 0xfe, 0x8a, 0xff, + 0x43, 0x79, 0xcf, 0x46, 0xc6, 0x79, 0xdb, 0xa5, 0xe4, 0xdb, 0xdd, 0x97, 0x92, 0xaf, 0xd6, 0xfb, 0x5c, 0x4a, 0xbe, + 0xfd, 0xe2, 0x97, 0x92, 0xe7, 0x55, 0x9f, 0x98, 0xb7, 0xd5, 0xf4, 0x2c, 0xdf, 0x6f, 0xaa, 0x2a, 0x07, 0xdf, 0xd3, + 0x2e, 0x2f, 0xd6, 0x57, 0x10, 0xf7, 0xed, 0x6d, 0x34, 0x7c, 0xb5, 0x2e, 0x19, 0x7c, 0x46, 0x40, 0x63, 0xdf, 0x46, + 0x44, 0x63, 0x7f, 0x5d, 0x0f, 0xc1, 0xca, 0x8c, 0xb3, 0x39, 0xfe, 0xd4, 0x5c, 0x78, 0xc1, 0x2c, 0x67, 0x91, 0xa0, + 0x64, 0x80, 0xc5, 0xe0, 0x77, 0x05, 0xc7, 0x33, 0x48, 0x32, 0xeb, 0x65, 0x98, 0x80, 0x45, 0x30, 0x58, 0x72, 0xcc, + 0xe2, 0xac, 0xd2, 0xd8, 0x12, 0x91, 0xf2, 0xae, 0xb9, 0x07, 0x54, 0xeb, 0x7b, 0x34, 0x00, 0x6e, 0xee, 0xdd, 0xa9, + 0xf7, 0xab, 0x80, 0x65, 0x9d, 0x30, 0x90, 0x06, 0x6e, 0xbf, 0xe9, 0x7d, 0xd9, 0x0c, 0xb7, 0x62, 0x78, 0xdd, 0x3e, + 0x52, 0x18, 0x49, 0xb5, 0xbd, 0x53, 0x36, 0xe3, 0xdd, 0x05, 0x66, 0xc3, 0xe7, 0x4b, 0xcd, 0xb7, 0xd8, 0x10, 0xe7, + 0x1d, 0x57, 0x51, 0x55, 0x49, 0x2e, 0xda, 0x88, 0x90, 0x42, 0x40, 0x2d, 0x0c, 0x8d, 0x0b, 0x4e, 0xd5, 0x56, 0x90, + 0xdf, 0xb1, 0xa5, 0x77, 0xa5, 0x3e, 0x65, 0xe3, 0xe4, 0x27, 0x1b, 0x94, 0x2b, 0xfc, 0x5f, 0x81, 0x13, 0xe5, 0x1c, + 0xcf, 0x38, 0x92, 0xf1, 0xbc, 0x91, 0xfa, 0x25, 0x6d, 0x44, 0xb6, 0x70, 0x36, 0x75, 0x5e, 0xb4, 0xd5, 0x2d, 0xc1, + 0x61, 0x4b, 0xc1, 0x05, 0xe1, 0xe7, 0xc9, 0x09, 0x20, 0x23, 0x47, 0x0d, 0xf4, 0x73, 0xd8, 0xd6, 0x99, 0xa8, 0xf7, + 0x10, 0x16, 0xb1, 0xc1, 0x1f, 0xe4, 0xc0, 0x25, 0x9b, 0x59, 0x10, 0x79, 0x69, 0x1f, 0xd9, 0x34, 0x89, 0xe5, 0x75, + 0xd1, 0x63, 0x61, 0xb0, 0xc5, 0x98, 0x4e, 0xee, 0x98, 0x77, 0x82, 0x9e, 0x0f, 0xdb, 0xec, 0xef, 0x72, 0x47, 0xb1, + 0x4d, 0xc9, 0x1c, 0xc5, 0xe9, 0x1e, 0x1b, 0xce, 0x91, 0x61, 0x1d, 0x77, 0xf5, 0x4c, 0x6c, 0x38, 0xb9, 0xcb, 0x12, + 0x42, 0xc0, 0x01, 0x22, 0x1f, 0xa6, 0x1f, 0xfa, 0xa9, 0xef, 0x05, 0x19, 0xf0, 0xc3, 0x65, 0x21, 0xe5, 0x1f, 0xeb, + 0x24, 0x05, 0x18, 0x05, 0xd3, 0x8b, 0xce, 0x1f, 0xe6, 0x98, 0xa5, 0xb7, 0x8c, 0x85, 0x2d, 0x86, 0x31, 0x55, 0x5f, + 0x92, 0xdf, 0xcf, 0xb2, 0x3e, 0x23, 0xab, 0xb5, 0x71, 0x1a, 0xf2, 0xf5, 0x21, 0x1c, 0x1f, 0xb2, 0x91, 0xf1, 0x63, + 0x1b, 0xc1, 0xfd, 0xc7, 0x6e, 0x82, 0x9b, 0xb2, 0x7d, 0x08, 0xee, 0x3f, 0xbe, 0x38, 0xc1, 0xfd, 0x51, 0x26, 0xb8, + 0x25, 0xbf, 0xbf, 0xe2, 0x86, 0xe9, 0x1d, 0x3e, 0x6b, 0x90, 0xd8, 0xe0, 0xa9, 0x7a, 0x40, 0x0c, 0xbc, 0x2a, 0xe5, + 0x9b, 0x7f, 0x5f, 0x4a, 0xa0, 0x87, 0x0a, 0x50, 0x8c, 0x68, 0x4e, 0xc9, 0xba, 0x20, 0x17, 0xbb, 0x9d, 0x27, 0xec, + 0x62, 0xb7, 0xca, 0xeb, 0x30, 0x0d, 0xac, 0xb7, 0x5c, 0x8e, 0x84, 0x0b, 0xdd, 0x57, 0x51, 0xbc, 0xf4, 0x30, 0x34, + 0xa8, 0x8a, 0x89, 0x77, 0xe1, 0xc1, 0x06, 0x5f, 0xd2, 0x49, 0x14, 0x4e, 0xf3, 0x5b, 0x49, 0x36, 0xbc, 0x24, 0x8e, + 0x5b, 0xbd, 0x67, 0x5e, 0xac, 0x1a, 0xf4, 0x1a, 0x26, 0xf7, 0x49, 0xc7, 0x7e, 0xe2, 0x1e, 0x3e, 0x39, 0xb2, 0xe1, + 0x7f, 0x87, 0x75, 0x32, 0x83, 0x57, 0x5c, 0x46, 0x21, 0xe4, 0xfe, 0x12, 0x35, 0xdb, 0xaa, 0xdd, 0x32, 0xf6, 0xa1, + 0xa8, 0x75, 0xdc, 0x5c, 0x69, 0xea, 0xdd, 0x17, 0x75, 0x1a, 0x6b, 0x2c, 0xa2, 0xb5, 0x34, 0xac, 0x86, 0xd1, 0xf8, + 0xe1, 0x1a, 0xf4, 0xec, 0x52, 0x0d, 0xf9, 0x35, 0x07, 0xb7, 0x80, 0x8b, 0x75, 0xb2, 0xab, 0x22, 0xc1, 0xa0, 0x48, + 0x74, 0xb6, 0x13, 0x83, 0xfc, 0x8a, 0xd2, 0xc6, 0x60, 0xd0, 0x18, 0x96, 0x1d, 0x42, 0x41, 0xe7, 0x69, 0xe1, 0x3c, + 0x9a, 0xa0, 0x34, 0x5e, 0x87, 0x13, 0x0d, 0x7f, 0x7a, 0xe3, 0x44, 0xf3, 0x0f, 0x62, 0x8b, 0x7f, 0x58, 0xc7, 0x59, + 0xf3, 0x4e, 0xed, 0x22, 0x1b, 0x53, 0x22, 0x66, 0xc5, 0x7b, 0x92, 0x1a, 0x31, 0xe5, 0x70, 0xc7, 0xa9, 0x35, 0x87, + 0xde, 0x93, 0xbc, 0xe1, 0x93, 0xd4, 0x80, 0x3c, 0xea, 0x30, 0xdd, 0x8f, 0x1f, 0x53, 0x2d, 0xc8, 0x6c, 0x4c, 0x60, + 0x9d, 0x4d, 0x8a, 0xf8, 0x93, 0x8a, 0x37, 0x8f, 0x28, 0x04, 0x65, 0x7f, 0x62, 0x44, 0x4f, 0x9f, 0x9e, 0x0e, 0x1d, + 0x9d, 0xe7, 0xe5, 0x2e, 0x25, 0x91, 0x3c, 0xdf, 0xcf, 0xd0, 0x48, 0x6f, 0x74, 0x81, 0x5d, 0x81, 0xcc, 0x64, 0x0b, + 0x77, 0x04, 0x4e, 0xbd, 0x20, 0xa9, 0x13, 0x19, 0x14, 0x78, 0xc2, 0xe0, 0x47, 0xd4, 0xc9, 0xa5, 0xae, 0x8e, 0x65, + 0x5b, 0xb6, 0x9a, 0x37, 0x9c, 0xf9, 0xf3, 0xe1, 0x26, 0x4a, 0x3d, 0x48, 0x8f, 0x17, 0x44, 0x73, 0xf0, 0xa3, 0x4b, + 0xfd, 0x34, 0x80, 0x5c, 0x6b, 0xe0, 0x50, 0xb7, 0x24, 0xb9, 0x3c, 0xe3, 0xde, 0x0d, 0x5e, 0xfc, 0x01, 0xf3, 0xed, + 0x0a, 0x17, 0x5a, 0x0c, 0x89, 0xf6, 0x03, 0x1c, 0x86, 0x9a, 0xaa, 0x81, 0x6e, 0x80, 0xc5, 0x89, 0x29, 0x7b, 0x0b, + 0xf5, 0x15, 0x68, 0xa3, 0xab, 0x1c, 0x88, 0x59, 0xec, 0x2d, 0x21, 0x2f, 0xc9, 0x26, 0x33, 0x38, 0xa5, 0x55, 0x39, + 0xa9, 0x55, 0x9c, 0x67, 0x47, 0x86, 0xe2, 0x3a, 0x86, 0x62, 0x03, 0xb9, 0x55, 0x33, 0x63, 0x93, 0x5d, 0x0d, 0x76, + 0x19, 0x3c, 0x10, 0x7d, 0x79, 0x48, 0x70, 0x90, 0xa9, 0x03, 0xbf, 0x4a, 0x4a, 0x29, 0xb8, 0xad, 0x26, 0x85, 0xff, + 0xf7, 0xe9, 0xd2, 0xf3, 0x82, 0xdd, 0xa5, 0x3a, 0xe6, 0x22, 0xe3, 0x55, 0x7c, 0x7d, 0x83, 0x8e, 0xbe, 0x7e, 0xa8, + 0xf8, 0x1f, 0x3f, 0x6a, 0x3e, 0x38, 0x33, 0x0d, 0x25, 0xfc, 0xc0, 0xb3, 0x5e, 0x42, 0x98, 0x5f, 0x5c, 0xd3, 0x23, + 0xb2, 0xc0, 0xd3, 0x10, 0xfe, 0x2d, 0x8a, 0xc5, 0x0f, 0x6e, 0x26, 0x61, 0x05, 0x5e, 0x38, 0x07, 0x92, 0xe6, 0x85, + 0xf3, 0x9a, 0x39, 0x16, 0xf9, 0x2a, 0x57, 0x4a, 0x8b, 0xae, 0x0a, 0x53, 0xa9, 0xe4, 0xbb, 0xfb, 0x0b, 0xca, 0xb5, + 0xa8, 0xa9, 0x70, 0xca, 0xa1, 0x63, 0x6d, 0x71, 0x93, 0xfb, 0x74, 0xf8, 0xf5, 0xc9, 0x92, 0xa5, 0x1e, 0x5d, 0x03, + 0x81, 0xf0, 0x0b, 0xec, 0x80, 0x32, 0x11, 0x79, 0xd2, 0xf1, 0x68, 0x18, 0x4e, 0xd9, 0x8d, 0x3f, 0xe1, 0x72, 0xa9, + 0xa1, 0xf0, 0x73, 0xca, 0x44, 0x8b, 0xcf, 0xa1, 0x63, 0x90, 0xc3, 0xc1, 0xc4, 0xc3, 0x38, 0xb8, 0xc3, 0x30, 0x52, + 0x4f, 0xbf, 0xce, 0x7d, 0x33, 0xdb, 0x26, 0x01, 0x12, 0x1e, 0x5f, 0xc6, 0x2c, 0xf8, 0xe7, 0xf0, 0x6b, 0x38, 0xb8, + 0xbf, 0xbe, 0x52, 0xf5, 0x41, 0x6a, 0x2d, 0x62, 0x36, 0x1b, 0x7e, 0xdd, 0x90, 0xf8, 0x17, 0xc5, 0x7b, 0x1a, 0x8b, + 0xda, 0x71, 0x8b, 0xe8, 0x65, 0x9d, 0xbd, 0x84, 0xfa, 0x53, 0x2e, 0xad, 0x83, 0x04, 0xb8, 0x29, 0xc9, 0xd8, 0xce, + 0x00, 0xe5, 0xe7, 0x71, 0xe0, 0x4d, 0x3e, 0x0c, 0xe8, 0x4d, 0xe9, 0xc1, 0x84, 0xd3, 0x7a, 0xe2, 0xad, 0xfa, 0x78, + 0xbc, 0xca, 0x85, 0xe0, 0x9a, 0x4d, 0xa5, 0x39, 0x67, 0xd7, 0xb8, 0x96, 0x71, 0x29, 0x6f, 0xf0, 0xcb, 0xf8, 0xa9, + 0xdb, 0x85, 0x9f, 0x32, 0xf1, 0x29, 0x7c, 0xc8, 0x32, 0x21, 0xa8, 0x93, 0x88, 0x8a, 0x82, 0xb5, 0xd5, 0x51, 0x9c, + 0xde, 0x5f, 0xba, 0x37, 0x8e, 0xbd, 0x70, 0x1d, 0xab, 0xf7, 0xde, 0xe9, 0x2d, 0x3a, 0xd6, 0x71, 0x60, 0x76, 0xac, + 0x63, 0xf8, 0xf3, 0xfe, 0xd8, 0xea, 0x2d, 0x4c, 0xd7, 0x3a, 0x7c, 0xef, 0xb8, 0x81, 0xd9, 0xb3, 0x8e, 0xe1, 0xcf, + 0x39, 0xb5, 0x02, 0x01, 0x88, 0xe4, 0x9d, 0xaf, 0x4b, 0x54, 0x40, 0xfa, 0x9d, 0xdf, 0xc9, 0x1a, 0xa5, 0xe3, 0xad, + 0xe1, 0x5e, 0x17, 0x48, 0x46, 0x91, 0x41, 0x27, 0x1c, 0x68, 0xe1, 0x90, 0x51, 0x46, 0x0c, 0x61, 0xde, 0x26, 0x7c, + 0xd0, 0x45, 0x22, 0x97, 0xc6, 0x6d, 0xc4, 0xdb, 0x34, 0x67, 0xef, 0x10, 0xc9, 0x19, 0xe9, 0x22, 0xf8, 0xe7, 0x15, + 0x46, 0x59, 0x13, 0xf9, 0x46, 0x24, 0xaa, 0x54, 0x24, 0x08, 0xce, 0x76, 0x0f, 0x1c, 0xbd, 0xf0, 0x59, 0x9e, 0xa0, + 0xee, 0x8b, 0xf6, 0x2d, 0xe5, 0x15, 0xfa, 0xac, 0x7e, 0x30, 0x2f, 0x7b, 0x91, 0x7d, 0x04, 0xc2, 0x4d, 0x4f, 0xfd, + 0x38, 0x1f, 0x9e, 0x44, 0xa2, 0x9d, 0xe6, 0xb4, 0x27, 0x3a, 0x64, 0xf2, 0x7a, 0x0d, 0x5c, 0xf2, 0x8d, 0x17, 0x48, + 0x86, 0x6c, 0x52, 0xcb, 0x07, 0x39, 0xe5, 0x7f, 0xfc, 0xb8, 0x18, 0x9c, 0x59, 0x19, 0xf7, 0x89, 0xd3, 0x85, 0x63, + 0xb7, 0xcb, 0x3a, 0x5b, 0x6d, 0x2a, 0x77, 0x07, 0x2a, 0x2f, 0xe2, 0x19, 0x0b, 0xbb, 0x29, 0x61, 0xb1, 0xd1, 0x6a, + 0xd8, 0x59, 0xb3, 0xd7, 0x80, 0x10, 0xef, 0x15, 0x51, 0x47, 0xd5, 0x07, 0xa1, 0x30, 0x3f, 0x08, 0xb7, 0x04, 0x67, + 0xe7, 0xb2, 0x98, 0x0a, 0xa8, 0xd9, 0x02, 0xc7, 0x0e, 0x07, 0xf1, 0xff, 0x34, 0x10, 0xe8, 0xac, 0x09, 0xf6, 0x12, + 0x95, 0xdd, 0x5a, 0x72, 0xde, 0xcb, 0xcf, 0x55, 0x3a, 0x50, 0x59, 0x72, 0xa6, 0x42, 0x11, 0xa4, 0x78, 0xc4, 0xac, + 0xae, 0xb9, 0xb1, 0x68, 0x7e, 0x5a, 0x14, 0x05, 0x86, 0x8f, 0x99, 0x2e, 0x84, 0xe3, 0xa8, 0xfe, 0xf8, 0x71, 0xeb, + 0x21, 0x44, 0xc6, 0x39, 0x72, 0x72, 0x6b, 0x55, 0xa6, 0x6f, 0xaa, 0x4c, 0x62, 0xf2, 0x7e, 0x91, 0x6a, 0x08, 0x1b, + 0x57, 0x5a, 0x7b, 0xf8, 0x73, 0xcc, 0xbc, 0xd4, 0xe2, 0x97, 0xa5, 0x9a, 0x74, 0xb8, 0x1b, 0x0e, 0xeb, 0x80, 0x75, + 0x2b, 0x0f, 0xc6, 0xc8, 0x83, 0x9d, 0x3e, 0xda, 0xbc, 0x5f, 0xf3, 0xa8, 0x0e, 0xd0, 0xc7, 0x47, 0xbb, 0x88, 0x9f, + 0xf5, 0x26, 0xf5, 0x28, 0xc8, 0x92, 0x7c, 0xe4, 0x46, 0xa9, 0x27, 0x52, 0xa9, 0x01, 0x5f, 0x3e, 0x68, 0x34, 0xbf, + 0x90, 0x22, 0x3f, 0x9c, 0xbe, 0xb9, 0xf8, 0x56, 0xe1, 0xeb, 0x9f, 0xac, 0x05, 0x50, 0x90, 0xa1, 0x34, 0x2e, 0x43, + 0x4a, 0xe3, 0xa2, 0xf0, 0x24, 0x56, 0x90, 0x0c, 0x25, 0x3b, 0x20, 0x0c, 0xa2, 0x02, 0x9a, 0x6c, 0x28, 0x96, 0xeb, + 0x20, 0xf5, 0x57, 0x5e, 0x9c, 0x1e, 0x40, 0x53, 0x13, 0x88, 0x9c, 0xda, 0x16, 0x0f, 0x82, 0xcc, 0x30, 0x44, 0x0c, + 0xd0, 0x34, 0x14, 0x76, 0x18, 0x33, 0x3f, 0xc8, 0xcd, 0x30, 0xc4, 0x07, 0xbc, 0xc9, 0x84, 0xad, 0xd2, 0xa1, 0xea, + 0xad, 0x20, 0x95, 0x12, 0xc6, 0xda, 0x3f, 0x88, 0x26, 0x29, 0x4b, 0xcd, 0x24, 0x8d, 0x99, 0xb7, 0x54, 0xf3, 0x58, + 0xd3, 0xf5, 0xfe, 0x92, 0xf5, 0x78, 0xe9, 0xa7, 0x79, 0xb0, 0x56, 0x02, 0x10, 0x0c, 0x22, 0x60, 0x88, 0x10, 0x1c, + 0x86, 0x50, 0x78, 0x1e, 0xcd, 0x2b, 0x2b, 0xaa, 0xe0, 0x5c, 0xce, 0x30, 0x14, 0x38, 0x49, 0x32, 0xa0, 0x2d, 0x9e, + 0x44, 0xc1, 0x35, 0x8f, 0x61, 0x91, 0xc7, 0x94, 0x55, 0x4f, 0x4f, 0xb8, 0x78, 0xab, 0x60, 0xd8, 0x15, 0xb5, 0x6b, + 0x43, 0xb0, 0xf3, 0xb6, 0xe8, 0x16, 0x07, 0xbc, 0x32, 0x1c, 0x4d, 0xd4, 0x33, 0x66, 0xa0, 0xa0, 0xb1, 0x5c, 0x00, + 0x23, 0x54, 0x32, 0x98, 0x59, 0x38, 0xa7, 0xb9, 0x3b, 0x25, 0x8e, 0x0a, 0x79, 0xa5, 0x8f, 0x1f, 0x9f, 0x8f, 0x7e, + 0xfb, 0x17, 0xa4, 0xc9, 0x58, 0x38, 0x22, 0xa6, 0xc4, 0xa5, 0x5c, 0x8b, 0x73, 0x9f, 0xc6, 0x08, 0x8d, 0xa5, 0xd8, + 0x54, 0x04, 0xe6, 0x11, 0x4b, 0x2b, 0x1b, 0x5d, 0x89, 0x68, 0x7f, 0x90, 0x41, 0x47, 0x17, 0x91, 0x2f, 0x46, 0x30, + 0xbd, 0x23, 0x11, 0x70, 0x45, 0xf9, 0xe5, 0xee, 0xbb, 0x63, 0xa5, 0x08, 0xc1, 0xd3, 0x64, 0xd1, 0x43, 0x6b, 0xe8, + 0xf4, 0xc4, 0x53, 0x90, 0x69, 0x41, 0xf6, 0x23, 0xe9, 0x1f, 0x00, 0x98, 0x8b, 0x68, 0xc9, 0x2c, 0x3f, 0x3a, 0xb8, + 0x65, 0x63, 0xd3, 0x5b, 0xf9, 0x64, 0x97, 0x83, 0x7a, 0x37, 0x85, 0x38, 0xbf, 0xdc, 0xdc, 0x85, 0xf8, 0xeb, 0xac, + 0x40, 0x65, 0x54, 0x0e, 0xef, 0xd8, 0x75, 0x8b, 0x7b, 0x40, 0x88, 0x5f, 0x20, 0xe1, 0x31, 0x3a, 0x3d, 0x39, 0xf0, + 0x4e, 0xcb, 0xf1, 0x65, 0x2d, 0x91, 0xda, 0xa4, 0x7c, 0x08, 0x9c, 0x51, 0x98, 0x58, 0x11, 0x11, 0xb6, 0x78, 0x30, + 0xa3, 0xd9, 0x4c, 0x8e, 0x09, 0x6b, 0x95, 0x87, 0x97, 0x23, 0xad, 0x58, 0xd2, 0xd1, 0x8a, 0xbe, 0x54, 0xff, 0x44, + 0xfe, 0x53, 0xed, 0x63, 0x30, 0x68, 0x80, 0x19, 0xb6, 0x7b, 0x2d, 0xb6, 0x6c, 0x8e, 0xb1, 0x87, 0x54, 0x89, 0xd3, + 0x91, 0x6a, 0x26, 0x83, 0x16, 0xce, 0xe5, 0xc1, 0x70, 0x48, 0x64, 0xae, 0x4a, 0xed, 0x00, 0x89, 0x0d, 0x69, 0x5e, + 0x00, 0xd8, 0x14, 0x1a, 0x9a, 0xe4, 0x2e, 0x8b, 0x8d, 0xaa, 0xe0, 0xd4, 0xc7, 0x78, 0xe0, 0x89, 0xe5, 0x57, 0x5a, + 0xa0, 0xb0, 0xf0, 0xf8, 0xbc, 0x03, 0x7d, 0x17, 0xfd, 0x54, 0xc8, 0xbc, 0xf2, 0x0d, 0x51, 0x74, 0x33, 0xf0, 0xee, + 0x23, 0xc9, 0x8c, 0x89, 0x47, 0x34, 0x39, 0xc7, 0xd2, 0x0b, 0xe1, 0x49, 0x5c, 0xdb, 0x68, 0x79, 0xb6, 0x8c, 0xfa, + 0x66, 0x93, 0x33, 0x5c, 0xec, 0xda, 0x6b, 0x72, 0xdd, 0x32, 0x30, 0x48, 0x3c, 0xb3, 0x62, 0x1f, 0x96, 0x5e, 0x22, + 0x59, 0xc8, 0x4e, 0x0e, 0x00, 0x3e, 0x88, 0xc2, 0x52, 0x62, 0x9c, 0x7c, 0x1d, 0x42, 0xbd, 0x78, 0x09, 0x99, 0x62, + 0xbd, 0x9e, 0x0a, 0x9e, 0x0f, 0x05, 0xcb, 0x3c, 0x78, 0x75, 0xa9, 0x4a, 0x9d, 0x97, 0xb1, 0x9b, 0x99, 0xc0, 0xdd, + 0xa9, 0x65, 0x7e, 0x5d, 0x63, 0x5e, 0x99, 0x6c, 0x90, 0x32, 0x11, 0xe4, 0xe0, 0x3c, 0x6d, 0x89, 0x83, 0xd0, 0x56, + 0x85, 0xf8, 0xd9, 0x2d, 0x15, 0x8a, 0x75, 0xbc, 0xad, 0x56, 0xc1, 0x39, 0xe5, 0xd5, 0x56, 0x9e, 0xa6, 0x3e, 0xc4, + 0x15, 0x5f, 0xab, 0x8d, 0xa5, 0x50, 0xef, 0x3c, 0x1d, 0x42, 0x55, 0xa1, 0x8b, 0xf7, 0x56, 0x2b, 0xaa, 0xac, 0x0f, + 0x4e, 0x0e, 0x48, 0x2c, 0x3d, 0xa5, 0x15, 0x76, 0x7a, 0x02, 0xa6, 0xdc, 0x34, 0xe9, 0xde, 0x6a, 0xc5, 0xa7, 0x94, + 0x7e, 0xd1, 0x9b, 0x83, 0x45, 0xba, 0x0c, 0x4e, 0xff, 0x1f, 0xca, 0xa4, 0xb6, 0xee, 0x1c, 0x64, 0x03, 0x00}; } // namespace web_server } // namespace esphome From b8630363e0492b88393efe02c93b7f9553b405c5 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 8 Oct 2024 03:47:07 +1100 Subject: [PATCH 0448/1052] [online_image] Bugfix: Use std::string instead of const char * (#7556) --- esphome/components/online_image/__init__.py | 2 +- esphome/components/online_image/online_image.h | 3 +-- tests/components/online_image/common-esp32.yaml | 4 ++-- tests/components/online_image/common.yaml | 8 ++++++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index d9a7609543..dfb10137aa 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -125,7 +125,7 @@ async def online_image_action_to_code(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg, paren) if CONF_URL in config: - template_ = await cg.templatable(config[CONF_URL], args, cg.const_char_ptr) + template_ = await cg.templatable(config[CONF_URL], args, cg.std_string) cg.add(var.set_url(template_)) return var diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 775cc46e0b..51c11478cd 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -157,7 +157,7 @@ class OnlineImage : public PollingComponent, template class OnlineImageSetUrlAction : public Action { public: OnlineImageSetUrlAction(OnlineImage *parent) : parent_(parent) {} - TEMPLATABLE_VALUE(const char *, url) + TEMPLATABLE_VALUE(std::string, url) void play(Ts... x) override { this->parent_->set_url(this->url_.value(x...)); this->parent_->update(); @@ -170,7 +170,6 @@ template class OnlineImageSetUrlAction : public Action { template class OnlineImageReleaseAction : public Action { public: OnlineImageReleaseAction(OnlineImage *parent) : parent_(parent) {} - TEMPLATABLE_VALUE(const char *, url) void play(Ts... x) override { this->parent_->release(); } protected: diff --git a/tests/components/online_image/common-esp32.yaml b/tests/components/online_image/common-esp32.yaml index d3a304cdc0..787a1ad368 100644 --- a/tests/components/online_image/common-esp32.yaml +++ b/tests/components/online_image/common-esp32.yaml @@ -4,13 +4,13 @@ spi: - id: spi_main_lcd clk_pin: 16 mosi_pin: 17 - miso_pin: 15 + miso_pin: 18 display: - platform: ili9xxx id: main_lcd model: ili9342 - cs_pin: 12 + cs_pin: 20 dc_pin: 13 reset_pin: 21 invert_colors: true diff --git a/tests/components/online_image/common.yaml b/tests/components/online_image/common.yaml index 8f7ea6238b..5c6feb4c81 100644 --- a/tests/components/online_image/common.yaml +++ b/tests/components/online_image/common.yaml @@ -34,4 +34,12 @@ time: - online_image.set_url: id: online_rgba_image url: http://www.example.org/example.png + - online_image.set_url: + id: online_rgba_image + url: !lambda |- + return "http://www.example.org/example.png"; + - online_image.set_url: + id: online_rgba_image + url: !lambda |- + return str_sprintf("http://homeassistant.local:8123"); From 52e59d1dadb1daa3679078f5246b4811bcf1af03 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 8 Oct 2024 12:28:59 +1100 Subject: [PATCH 0449/1052] [ili9xxx] Put display into sleep mode on shutdown. (#7555) --- esphome/components/ili9xxx/ili9xxx_display.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index 5033f702de..c141739d2a 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -89,6 +89,7 @@ class ILI9XXXDisplay : public display::DisplayBuffer, void dump_config() override; void setup() override; + void on_shutdown() 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, From 659239e8cdd3d6890153e28197ae9d7f1cac9dc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:34:15 +1300 Subject: [PATCH 0450/1052] Bump actions/upload-artifact from 4.4.0 to 4.4.1 (#7559) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8995c500ef..99e201b1a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT - name: Upload digests - uses: actions/upload-artifact@v4.4.0 + uses: actions/upload-artifact@v4.4.1 with: name: digests-${{ steps.sanitize.outputs.name }} path: /tmp/digests From 3804b3b7595d32752568fb9e139171e933db21e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 14:34:26 +1300 Subject: [PATCH 0451/1052] Bump actions/cache from 4.0.2 to 4.1.0 in /.github/actions/restore-python (#7560) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index c618a5ca97..7f4de1e378 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -22,7 +22,7 @@ runs: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache/restore@v4.0.2 + uses: actions/cache/restore@v4.1.0 with: path: venv # yamllint disable-line rule:line-length From 6139b933c50302b5b0428ba2d13929ee6776271e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 02:00:10 +0000 Subject: [PATCH 0452/1052] Bump actions/cache from 4.0.2 to 4.1.0 (#7558) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c4fa65695..b0bae6cbec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.1.0 with: path: venv # yamllint disable-line rule:line-length @@ -302,14 +302,14 @@ jobs: - name: Cache platformio if: github.ref == 'refs/heads/dev' - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.1.0 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} - name: Cache platformio if: github.ref != 'refs/heads/dev' - uses: actions/cache/restore@v4.0.2 + uses: actions/cache/restore@v4.1.0 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} From 9211aad524ae52a16031cf90242715cf85b11a76 Mon Sep 17 00:00:00 2001 From: baldisos <76702976+baldisos@users.noreply.github.com> Date: Wed, 9 Oct 2024 03:33:50 +0200 Subject: [PATCH 0453/1052] Update radon_eye_listener.cpp for more possible variants (#7567) --- .../components/radon_eye_ble/radon_eye_listener.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/esphome/components/radon_eye_ble/radon_eye_listener.cpp b/esphome/components/radon_eye_ble/radon_eye_listener.cpp index 340322c188..a4c79db753 100644 --- a/esphome/components/radon_eye_ble/radon_eye_listener.cpp +++ b/esphome/components/radon_eye_ble/radon_eye_listener.cpp @@ -1,5 +1,7 @@ #include "radon_eye_listener.h" #include "esphome/core/log.h" +#include +#include #ifdef USE_ESP32 @@ -10,9 +12,14 @@ static const char *const TAG = "radon_eye_ble"; bool RadonEyeListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { if (not device.get_name().empty()) { - if (device.get_name().rfind("FR:R", 0) == 0) { - // This is an RD200, I think - ESP_LOGD(TAG, "Found Radon Eye RD200 device Name: %s (MAC: %s)", device.get_name().c_str(), + // Vector containing the prefixes to search for + std::vector prefixes = {"FR:R", "FR:I", "FR:H"}; + + // Check if the device name starts with any of the prefixes + if (std::any_of(prefixes.begin(), prefixes.end(), + [&](const std::string &prefix) { return device.get_name().rfind(prefix, 0) == 0; })) { + // Device found + ESP_LOGD(TAG, "Found Radon Eye device Name: %s (MAC: %s)", device.get_name().c_str(), device.address_str().c_str()); } } From 1a567b6986e4208c23677afef1b8e9eeaa9cd36a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:41:58 +1100 Subject: [PATCH 0454/1052] [cst816] Allow skipping i2c probe (#7557) --- .../components/cst816/touchscreen/__init__.py | 13 ++++--- .../cst816/touchscreen/cst816_touchscreen.cpp | 39 ++++++++++--------- .../cst816/touchscreen/cst816_touchscreen.h | 2 + tests/components/cst816/common.yaml | 5 ++- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/esphome/components/cst816/touchscreen/__init__.py b/esphome/components/cst816/touchscreen/__init__.py index a3603ef575..288ca17593 100644 --- a/esphome/components/cst816/touchscreen/__init__.py +++ b/esphome/components/cst816/touchscreen/__init__.py @@ -1,11 +1,10 @@ -import esphome.codegen as cg -import esphome.config_validation as cv - from esphome import pins +import esphome.codegen as cg from esphome.components import i2c, touchscreen -from esphome.const import CONF_INTERRUPT_PIN, CONF_ID, CONF_RESET_PIN -from .. import cst816_ns +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN +from .. import cst816_ns CST816Touchscreen = cst816_ns.class_( "CST816Touchscreen", @@ -14,11 +13,14 @@ CST816Touchscreen = cst816_ns.class_( ) CST816ButtonListener = cst816_ns.class_("CST816ButtonListener") + +CONF_SKIP_PROBE = "skip_probe" CONFIG_SCHEMA = touchscreen.TOUCHSCREEN_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(CST816Touchscreen), cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema, cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_SKIP_PROBE, default=False): cv.boolean, } ).extend(i2c.i2c_device_schema(0x15)) @@ -28,6 +30,7 @@ async def to_code(config): await touchscreen.register_touchscreen(var, config) await i2c.register_i2c_device(var, config) + cg.add(var.set_skip_probe(config[CONF_SKIP_PROBE])) if interrupt_pin := config.get(CONF_INTERRUPT_PIN): cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin))) if reset_pin := config.get(CONF_RESET_PIN): diff --git a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp index 9e59810c7e..7dcb130e20 100644 --- a/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +++ b/esphome/components/cst816/touchscreen/cst816_touchscreen.cpp @@ -8,32 +8,33 @@ void CST816Touchscreen::continue_setup_() { this->interrupt_pin_->setup(); this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); } - if (!this->read_byte(REG_CHIP_ID, &this->chip_id_)) { + if (this->read_byte(REG_CHIP_ID, &this->chip_id_)) { + switch (this->chip_id_) { + case CST820_CHIP_ID: + case CST826_CHIP_ID: + case CST716_CHIP_ID: + case CST816S_CHIP_ID: + case CST816D_CHIP_ID: + case CST816T_CHIP_ID: + break; + default: + this->mark_failed(); + this->status_set_error(str_sprintf("Unknown chip ID 0x%02X", this->chip_id_).c_str()); + return; + } + this->write_byte(REG_IRQ_CTL, IRQ_EN_MOTION); + } else if (!this->skip_probe_) { + this->status_set_error("Failed to read chip id"); this->mark_failed(); - esph_log_e(TAG, "Failed to read chip id"); return; } - switch (this->chip_id_) { - case CST820_CHIP_ID: - case CST826_CHIP_ID: - case CST716_CHIP_ID: - case CST816S_CHIP_ID: - case CST816D_CHIP_ID: - case CST816T_CHIP_ID: - break; - default: - this->mark_failed(); - esph_log_e(TAG, "Unknown chip ID 0x%02X", this->chip_id_); - return; - } - this->write_byte(REG_IRQ_CTL, IRQ_EN_MOTION); if (this->x_raw_max_ == this->x_raw_min_) { this->x_raw_max_ = this->display_->get_native_width(); } if (this->y_raw_max_ == this->y_raw_min_) { this->y_raw_max_ = this->display_->get_native_height(); } - esph_log_config(TAG, "CST816 Touchscreen setup complete"); + ESP_LOGCONFIG(TAG, "CST816 Touchscreen setup complete"); } void CST816Touchscreen::update_button_state_(bool state) { @@ -45,7 +46,7 @@ void CST816Touchscreen::update_button_state_(bool state) { } void CST816Touchscreen::setup() { - esph_log_config(TAG, "Setting up CST816 Touchscreen..."); + ESP_LOGCONFIG(TAG, "Setting up CST816 Touchscreen..."); if (this->reset_pin_ != nullptr) { this->reset_pin_->setup(); this->reset_pin_->digital_write(true); @@ -73,7 +74,7 @@ void CST816Touchscreen::update_touches() { uint16_t x = encode_uint16(data[REG_XPOS_HIGH] & 0xF, data[REG_XPOS_LOW]); uint16_t y = encode_uint16(data[REG_YPOS_HIGH] & 0xF, data[REG_YPOS_LOW]); - esph_log_v(TAG, "Read touch %d/%d", x, y); + ESP_LOGV(TAG, "Read touch %d/%d", x, y); if (x >= this->x_raw_max_) { this->update_button_state_(true); } else { diff --git a/esphome/components/cst816/touchscreen/cst816_touchscreen.h b/esphome/components/cst816/touchscreen/cst816_touchscreen.h index 24e664e7ee..dc00e675ba 100644 --- a/esphome/components/cst816/touchscreen/cst816_touchscreen.h +++ b/esphome/components/cst816/touchscreen/cst816_touchscreen.h @@ -45,6 +45,7 @@ class CST816Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; } + void set_skip_probe(bool skip_probe) { this->skip_probe_ = skip_probe; } protected: void continue_setup_(); @@ -53,6 +54,7 @@ class CST816Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice InternalGPIOPin *interrupt_pin_{}; GPIOPin *reset_pin_{}; uint8_t chip_id_{}; + bool skip_probe_{}; // if set, do not expect to be able to probe the controller on the i2c bus. std::vector button_listeners_; bool button_touched_{}; }; diff --git a/tests/components/cst816/common.yaml b/tests/components/cst816/common.yaml index 91abbfd4f6..765a353d1d 100644 --- a/tests/components/cst816/common.yaml +++ b/tests/components/cst816/common.yaml @@ -4,6 +4,7 @@ touchscreen: interrupt_pin: number: 21 reset_pin: GPIO16 + skip_probe: false transform: mirror_x: false mirror_y: false @@ -11,14 +12,14 @@ touchscreen: i2c: sda: 3 - scl: 2 + scl: 4 display: - id: my_display platform: ili9xxx dimensions: 480x320 model: ST7796 - cs_pin: 15 + cs_pin: 18 dc_pin: 20 reset_pin: 22 transform: From fc97a6d1e3f49d8a6914e450ac4f374c238ae9bc Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:43:28 +1100 Subject: [PATCH 0455/1052] [lvgl] Fix text component (#7563) --- esphome/components/lvgl/text/__init__.py | 6 +++--- tests/components/lvgl/common.yaml | 6 ++++++ tests/components/lvgl/lvgl-package.yaml | 4 ++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/text/__init__.py b/esphome/components/lvgl/text/__init__.py index 540591d24b..a59e703591 100644 --- a/esphome/components/lvgl/text/__init__.py +++ b/esphome/components/lvgl/text/__init__.py @@ -34,13 +34,13 @@ async def to_code(config): widget = widget[0] await wait_for_widgets() async with LambdaContext([(cg.std_string, "text_value")]) as control: - await widget.set_property("text", "text_value.c_str())") - lv.event_send(widget.obj, API_EVENT, None) + await widget.set_property("text", "text_value.c_str()") + lv.event_send(widget.obj, API_EVENT, cg.nullptr) control.add(textvar.publish_state(widget.get_value())) async with LambdaContext(EVENT_ARG) as lamb: lv_add(textvar.publish_state(widget.get_value())) async with LvContext(paren): - widget.var.set_control_lambda(await control.get_lambda()) + lv_add(textvar.set_control_lambda(await control.get_lambda())) lv_add( paren.add_event_cb( widget.obj, diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 7ef7772ac9..ad935ae563 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -135,3 +135,9 @@ wifi: time: platform: sntp id: time_id + +text: + - id: lvgl_text + platform: lvgl + widget: hello_label + mode: text diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index c968198e26..1770c1bfbc 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -151,6 +151,10 @@ lvgl: align: center text_font: montserrat_40 border_post: true + on_press: + lvgl.label.update: + id: hello_label + text: Goodbye on_click: then: - lvgl.animimg.stop: anim_img From 66f500e594ca29912e0f6d35d4965c23244a101f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:49:33 +1100 Subject: [PATCH 0456/1052] [template/binary_sensor] Implement `condition:` option as alternative to lambda. (#7561) --- .../template/binary_sensor/__init__.py | 25 ++++++++++++++----- tests/components/template/common.yaml | 7 ++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/esphome/components/template/binary_sensor/__init__.py b/esphome/components/template/binary_sensor/__init__.py index 4ce89503de..c93876380d 100644 --- a/esphome/components/template/binary_sensor/__init__.py +++ b/esphome/components/template/binary_sensor/__init__.py @@ -1,8 +1,10 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import binary_sensor -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_STATE +import esphome.config_validation as cv +from esphome.const import CONF_CONDITION, CONF_ID, CONF_LAMBDA, CONF_STATE +from esphome.cpp_generator import LambdaExpression + from .. import template_ns TemplateBinarySensor = template_ns.class_( @@ -13,7 +15,10 @@ CONFIG_SCHEMA = ( binary_sensor.binary_sensor_schema(TemplateBinarySensor) .extend( { - cv.Optional(CONF_LAMBDA): cv.returning_lambda, + cv.Exclusive(CONF_LAMBDA, CONF_CONDITION): cv.returning_lambda, + cv.Exclusive( + CONF_CONDITION, CONF_CONDITION + ): automation.validate_potentially_and_condition, } ) .extend(cv.COMPONENT_SCHEMA) @@ -24,9 +29,17 @@ async def to_code(config): var = await binary_sensor.new_binary_sensor(config) await cg.register_component(var, config) - if CONF_LAMBDA in config: + if lamb := config.get(CONF_LAMBDA): template_ = await cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.optional.template(bool) + lamb, [], return_type=cg.optional.template(bool) + ) + cg.add(var.set_template(template_)) + if condition := config.get(CONF_CONDITION): + condition = await automation.build_condition( + condition, cg.TemplateArguments(), [] + ) + template_ = LambdaExpression( + f"return {condition.check()};", [], return_type=cg.optional.template(bool) ) cg.add(var.set_template(template_)) diff --git a/tests/components/template/common.yaml b/tests/components/template/common.yaml index 9e89424d8a..3565926933 100644 --- a/tests/components/template/common.yaml +++ b/tests/components/template/common.yaml @@ -46,6 +46,13 @@ binary_sensor: // Garage Door is closed. return false; } + - platform: template + id: other_binary_sensor + name: "Garage Door Closed" + condition: + sensor.in_range: + id: template_sens + below: 30.0 output: - platform: template From 69467ea6ff9fd949ffe5913601e8809adbefee57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:51:23 +1300 Subject: [PATCH 0457/1052] Bump actions/upload-artifact from 4.4.1 to 4.4.2 (#7569) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99e201b1a7..8ffff895a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT - name: Upload digests - uses: actions/upload-artifact@v4.4.1 + uses: actions/upload-artifact@v4.4.2 with: name: digests-${{ steps.sanitize.outputs.name }} path: /tmp/digests From 94ad1237ce2252bb31c3f734be1be68ddb948482 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:51:31 +1300 Subject: [PATCH 0458/1052] Bump actions/cache from 4.1.0 to 4.1.1 (#7570) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0bae6cbec..38527c20c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.1.0 + uses: actions/cache@v4.1.1 with: path: venv # yamllint disable-line rule:line-length @@ -302,14 +302,14 @@ jobs: - name: Cache platformio if: github.ref == 'refs/heads/dev' - uses: actions/cache@v4.1.0 + uses: actions/cache@v4.1.1 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} - name: Cache platformio if: github.ref != 'refs/heads/dev' - uses: actions/cache/restore@v4.1.0 + uses: actions/cache/restore@v4.1.1 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} From 26694cb55e0740524b02a3b373349cc08db826f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:51:43 +1300 Subject: [PATCH 0459/1052] Bump actions/cache from 4.1.0 to 4.1.1 in /.github/actions/restore-python (#7571) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index 7f4de1e378..1f5812691e 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -22,7 +22,7 @@ runs: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache/restore@v4.1.0 + uses: actions/cache/restore@v4.1.1 with: path: venv # yamllint disable-line rule:line-length From 4a9d3a39275b53ff46e27b8a81da4e4384c0f713 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:01:49 +1300 Subject: [PATCH 0460/1052] Bump version to 2024.10.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 08fb34976b..0752e3b169 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.10.0-dev" +__version__ = "2024.10.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 1c05f5af03c3ad8b564d478d4c2b5319191143ee Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 9 Oct 2024 15:01:49 +1300 Subject: [PATCH 0461/1052] Bump version to 2024.11.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 08fb34976b..16f30c179d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.10.0-dev" +__version__ = "2024.11.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 4bac9707fe3e002bb140ac27ffee7a14fa693784 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 9 Oct 2024 03:44:19 -0700 Subject: [PATCH 0462/1052] fix uart settings check (#7573) --- esphome/components/cse7766/cse7766.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 47058badce..48240464b3 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -244,7 +244,7 @@ void CSE7766Component::dump_config() { LOG_SENSOR(" ", "Apparent Power", this->apparent_power_sensor_); LOG_SENSOR(" ", "Reactive Power", this->reactive_power_sensor_); LOG_SENSOR(" ", "Power Factor", this->power_factor_sensor_); - this->check_uart_settings(4800); + this->check_uart_settings(4800, 1, uart::UART_CONFIG_PARITY_EVEN); } } // namespace cse7766 From b08432bd0dbb5617dc95f61f09dce34724971519 Mon Sep 17 00:00:00 2001 From: Ilia Sotnikov Date: Thu, 10 Oct 2024 05:44:07 +0300 Subject: [PATCH 0463/1052] Update `pillow` to 10.4.0 (#7566) --- esphome/components/font/__init__.py | 8 ++++---- requirements_optional.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index b5ed02e89a..dacd0779b1 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -98,13 +98,13 @@ def validate_pillow_installed(value): except ImportError as err: raise cv.Invalid( "Please install the pillow python package to use this feature. " - '(pip install "pillow==10.2.0")' + '(pip install "pillow==10.4.0")' ) from err - if version.parse(PIL.__version__) != version.parse("10.2.0"): + if version.parse(PIL.__version__) != version.parse("10.4.0"): raise cv.Invalid( - "Please update your pillow installation to 10.2.0. " - '(pip install "pillow==10.2.0")' + "Please update your pillow installation to 10.4.0. " + '(pip install "pillow==10.4.0")' ) return value diff --git a/requirements_optional.txt b/requirements_optional.txt index c984d41332..2d57c5fd96 100644 --- a/requirements_optional.txt +++ b/requirements_optional.txt @@ -1,2 +1,2 @@ -pillow==10.2.0 +pillow==10.4.0 cairosvg==2.7.1 From c18bd3ac8159918679452431fbcfef100300e60c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:07:40 +1300 Subject: [PATCH 0464/1052] Bump actions/upload-artifact from 4.4.2 to 4.4.3 (#7575) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ffff895a0..26a213f170 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT - name: Upload digests - uses: actions/upload-artifact@v4.4.2 + uses: actions/upload-artifact@v4.4.3 with: name: digests-${{ steps.sanitize.outputs.name }} path: /tmp/digests From cedb671f075dc2ceb9c3e5f01c0e74268312a4e7 Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Thu, 10 Oct 2024 21:51:21 +0300 Subject: [PATCH 0465/1052] [fix] ESP32-C6 Reset Reasons (#7578) --- esphome/components/debug/debug_esp32.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index 34aea9e26b..cb4330f422 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -36,7 +36,8 @@ std::string DebugComponent::get_reset_reason_() { break; #if defined(USE_ESP32_VARIANT_ESP32) case SW_RESET: -#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ + defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) case RTC_SW_SYS_RESET: #endif reset_reason = "Software Reset Digital Core"; @@ -72,14 +73,16 @@ std::string DebugComponent::get_reset_reason_() { case TGWDT_CPU_RESET: reset_reason = "Timer Group Reset CPU"; break; -#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ + defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) case TG0WDT_CPU_RESET: reset_reason = "Timer Group 0 Reset CPU"; break; #endif #if defined(USE_ESP32_VARIANT_ESP32) case SW_CPU_RESET: -#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ + defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) case RTC_SW_CPU_RESET: #endif reset_reason = "Software Reset CPU"; @@ -98,27 +101,32 @@ std::string DebugComponent::get_reset_reason_() { case RTCWDT_RTC_RESET: reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; break; -#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \ + defined(USE_ESP32_VARIANT_ESP32C6) case TG1WDT_CPU_RESET: reset_reason = "Timer Group 1 Reset CPU"; break; case SUPER_WDT_RESET: reset_reason = "Super Watchdog Reset Digital Core And RTC Module"; break; - case GLITCH_RTC_RESET: - reset_reason = "Glitch Reset Digital Core And RTC Module"; - break; case EFUSE_RESET: reset_reason = "eFuse Reset Digital Core"; break; #endif -#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) + case GLITCH_RTC_RESET: + reset_reason = "Glitch Reset Digital Core And RTC Module"; + break; +#endif +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) case USB_UART_CHIP_RESET: reset_reason = "USB UART Reset Digital Core"; break; case USB_JTAG_CHIP_RESET: reset_reason = "USB JTAG Reset Digital Core"; break; +#endif +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) case POWER_GLITCH_RESET: reset_reason = "Power Glitch Reset Digital Core And RTC Module"; break; From efe4c5e3bc496d9f1f4d152c28a27e505b9f3024 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Sat, 12 Oct 2024 06:13:32 +1300 Subject: [PATCH 0466/1052] [light] Add ``initial_state`` configuration (#7577) --- esphome/components/light/__init__.py | 33 ++++- esphome/components/light/automation.py | 73 +++++----- esphome/components/light/effects.py | 80 +++++------ esphome/components/light/light_state.cpp | 22 +-- esphome/components/light/light_state.h | 34 +++++ esphome/components/light/types.py | 4 +- esphome/const.py | 1 + tests/components/light/common.yaml | 125 ++++++++++++++++++ tests/components/light/test.esp32-ard.yaml | 115 +--------------- tests/components/light/test.esp32-c3-ard.yaml | 115 +--------------- tests/components/light/test.esp32-c3-idf.yaml | 115 +--------------- tests/components/light/test.esp32-idf.yaml | 115 +--------------- tests/components/light/test.esp8266-ard.yaml | 115 +--------------- tests/components/light/test.rp2040-ard.yaml | 115 +--------------- 14 files changed, 287 insertions(+), 775 deletions(-) create mode 100644 tests/components/light/common.yaml diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 7e16b7a648..feac385b66 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -3,27 +3,39 @@ import esphome.codegen as cg from esphome.components import mqtt, power_supply, web_server import esphome.config_validation as cv from esphome.const import ( + CONF_BLUE, + CONF_BRIGHTNESS, + CONF_COLD_WHITE, CONF_COLD_WHITE_COLOR_TEMPERATURE, + CONF_COLOR_BRIGHTNESS, CONF_COLOR_CORRECT, + CONF_COLOR_MODE, + CONF_COLOR_TEMPERATURE, CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_FLASH_TRANSITION_LENGTH, CONF_GAMMA_CORRECT, + CONF_GREEN, CONF_ID, + CONF_INITIAL_STATE, CONF_MQTT_ID, CONF_ON_STATE, CONF_ON_TURN_OFF, CONF_ON_TURN_ON, CONF_POWER_SUPPLY, + CONF_RED, CONF_RESTORE_MODE, + CONF_STATE, CONF_TRIGGER_ID, + CONF_WARM_WHITE, CONF_WARM_WHITE_COLOR_TEMPERATURE, CONF_WEB_SERVER, + CONF_WHITE, ) from esphome.core import coroutine_with_priority from esphome.cpp_helpers import setup_entity -from .automation import light_control_to_code # noqa +from .automation import LIGHT_STATE_SCHEMA from .effects import ( ADDRESSABLE_EFFECTS, BINARY_EFFECTS, @@ -35,8 +47,10 @@ from .effects import ( from .types import ( # noqa AddressableLight, AddressableLightState, + ColorMode, LightOutput, LightState, + LightStateRTCState, LightStateTrigger, LightTurnOffTrigger, LightTurnOnTrigger, @@ -85,6 +99,7 @@ LIGHT_SCHEMA = ( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightStateTrigger), } ), + cv.Optional(CONF_INITIAL_STATE): LIGHT_STATE_SCHEMA, } ) ) @@ -145,6 +160,22 @@ async def setup_light_core_(light_var, output_var, config): cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE])) + if (initial_state_config := config.get(CONF_INITIAL_STATE)) is not None: + initial_state = LightStateRTCState( + initial_state_config.get(CONF_COLOR_MODE, ColorMode.UNKNOWN), + initial_state_config.get(CONF_STATE, False), + initial_state_config.get(CONF_BRIGHTNESS, 1.0), + initial_state_config.get(CONF_COLOR_BRIGHTNESS, 1.0), + initial_state_config.get(CONF_RED, 1.0), + initial_state_config.get(CONF_GREEN, 1.0), + initial_state_config.get(CONF_BLUE, 1.0), + initial_state_config.get(CONF_WHITE, 1.0), + initial_state_config.get(CONF_COLOR_TEMPERATURE, 1.0), + initial_state_config.get(CONF_COLD_WHITE, 1.0), + initial_state_config.get(CONF_WARM_WHITE, 1.0), + ) + cg.add(light_var.set_initial_state(initial_state)) + if ( default_transition_length := config.get(CONF_DEFAULT_TRANSITION_LENGTH) ) is not None: diff --git a/esphome/components/light/automation.py b/esphome/components/light/automation.py index ec0375f54a..e5aa8fa0e9 100644 --- a/esphome/components/light/automation.py +++ b/esphome/components/light/automation.py @@ -1,41 +1,42 @@ +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation from esphome.const import ( - CONF_ID, - CONF_COLOR_MODE, - CONF_TRANSITION_LENGTH, - CONF_STATE, - CONF_FLASH_LENGTH, - CONF_EFFECT, - CONF_BRIGHTNESS, - CONF_COLOR_BRIGHTNESS, - CONF_RED, - CONF_GREEN, CONF_BLUE, - CONF_WHITE, - CONF_COLOR_TEMPERATURE, + CONF_BRIGHTNESS, + CONF_BRIGHTNESS_LIMITS, CONF_COLD_WHITE, - CONF_WARM_WHITE, + CONF_COLOR_BRIGHTNESS, + CONF_COLOR_MODE, + CONF_COLOR_TEMPERATURE, + CONF_EFFECT, + CONF_FLASH_LENGTH, + CONF_GREEN, + CONF_ID, + CONF_LIMIT_MODE, + CONF_MAX_BRIGHTNESS, + CONF_MIN_BRIGHTNESS, CONF_RANGE_FROM, CONF_RANGE_TO, - CONF_BRIGHTNESS_LIMITS, - CONF_LIMIT_MODE, - CONF_MIN_BRIGHTNESS, - CONF_MAX_BRIGHTNESS, + CONF_RED, + CONF_STATE, + CONF_TRANSITION_LENGTH, + CONF_WARM_WHITE, + CONF_WHITE, ) + from .types import ( - ColorMode, COLOR_MODES, LIMIT_MODES, - DimRelativeAction, - ToggleAction, - LightState, - LightControlAction, AddressableLightState, AddressableSet, - LightIsOnCondition, + ColorMode, + DimRelativeAction, + LightControlAction, LightIsOffCondition, + LightIsOnCondition, + LightState, + ToggleAction, ) @@ -62,18 +63,10 @@ async def light_toggle_to_code(config, action_id, template_arg, args): return var -LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema( +LIGHT_STATE_SCHEMA = cv.Schema( { - cv.Required(CONF_ID): cv.use_id(LightState), cv.Optional(CONF_COLOR_MODE): cv.enum(COLOR_MODES, upper=True, space="_"), cv.Optional(CONF_STATE): cv.templatable(cv.boolean), - cv.Exclusive(CONF_TRANSITION_LENGTH, "transformer"): cv.templatable( - cv.positive_time_period_milliseconds - ), - cv.Exclusive(CONF_FLASH_LENGTH, "transformer"): cv.templatable( - cv.positive_time_period_milliseconds - ), - cv.Exclusive(CONF_EFFECT, "transformer"): cv.templatable(cv.string), cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage), cv.Optional(CONF_COLOR_BRIGHTNESS): cv.templatable(cv.percentage), cv.Optional(CONF_RED): cv.templatable(cv.percentage), @@ -85,6 +78,20 @@ LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema( cv.Optional(CONF_WARM_WHITE): cv.templatable(cv.percentage), } ) + +LIGHT_CONTROL_ACTION_SCHEMA = LIGHT_STATE_SCHEMA.extend( + { + cv.Required(CONF_ID): cv.use_id(LightState), + cv.Exclusive(CONF_TRANSITION_LENGTH, "transformer"): cv.templatable( + cv.positive_time_period_milliseconds + ), + cv.Exclusive(CONF_FLASH_LENGTH, "transformer"): cv.templatable( + cv.positive_time_period_milliseconds + ), + cv.Exclusive(CONF_EFFECT, "transformer"): cv.templatable(cv.string), + } +) + LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id( { cv.Required(CONF_ID): cv.use_id(LightState), diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index be50f63321..67c318eb8e 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -1,59 +1,59 @@ -from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation - from esphome.const import ( - CONF_NAME, - CONF_LAMBDA, - CONF_UPDATE_INTERVAL, - CONF_TRANSITION_LENGTH, - CONF_COLORS, - CONF_STATE, - CONF_DURATION, - CONF_BRIGHTNESS, - CONF_COLOR_MODE, - CONF_COLOR_BRIGHTNESS, - CONF_RED, - CONF_GREEN, - CONF_BLUE, - CONF_WHITE, - CONF_COLOR_TEMPERATURE, - CONF_COLD_WHITE, - CONF_WARM_WHITE, CONF_ALPHA, + CONF_BLUE, + CONF_BRIGHTNESS, + CONF_COLD_WHITE, + CONF_COLOR_BRIGHTNESS, + CONF_COLOR_MODE, + CONF_COLOR_TEMPERATURE, + CONF_COLORS, + CONF_DURATION, + CONF_GREEN, CONF_INTENSITY, - CONF_SPEED, - CONF_WIDTH, - CONF_NUM_LEDS, - CONF_RANDOM, - CONF_SEQUENCE, + CONF_LAMBDA, CONF_MAX_BRIGHTNESS, CONF_MIN_BRIGHTNESS, + CONF_NAME, + CONF_NUM_LEDS, + CONF_RANDOM, + CONF_RED, + CONF_SEQUENCE, + CONF_SPEED, + CONF_STATE, + CONF_TRANSITION_LENGTH, + CONF_UPDATE_INTERVAL, + CONF_WARM_WHITE, + CONF_WHITE, + CONF_WIDTH, ) +from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor from esphome.util import Registry + from .types import ( - ColorMode, COLOR_MODES, + AddressableColorWipeEffect, + AddressableColorWipeEffectColor, + AddressableFireworksEffect, + AddressableFlickerEffect, + AddressableLambdaLightEffect, + AddressableLightRef, + AddressableRainbowLightEffect, + AddressableRandomTwinkleEffect, + AddressableScanEffect, + AddressableTwinkleEffect, + AutomationLightEffect, + Color, + ColorMode, + FlickerLightEffect, LambdaLightEffect, + LightColorValues, PulseLightEffect, RandomLightEffect, StrobeLightEffect, StrobeLightEffectColor, - LightColorValues, - AddressableLightRef, - AddressableLambdaLightEffect, - FlickerLightEffect, - AddressableRainbowLightEffect, - AddressableColorWipeEffect, - AddressableColorWipeEffectColor, - AddressableScanEffect, - AddressableTwinkleEffect, - AddressableRandomTwinkleEffect, - AddressableFireworksEffect, - AddressableFlickerEffect, - AutomationLightEffect, - Color, ) CONF_ADD_LED_INTERVAL = "add_led_interval" diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index fe6538e65e..16b78a17bd 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -1,6 +1,7 @@ #include "esphome/core/log.h" -#include "light_state.h" + #include "light_output.h" +#include "light_state.h" #include "transformers.h" namespace esphome { @@ -16,21 +17,6 @@ LightCall LightState::turn_off() { return this->make_call().set_state(false); } LightCall LightState::toggle() { return this->make_call().set_state(!this->remote_values.is_on()); } LightCall LightState::make_call() { return LightCall(this); } -struct LightStateRTCState { - ColorMode color_mode{ColorMode::UNKNOWN}; - bool state{false}; - float brightness{1.0f}; - float color_brightness{1.0f}; - float red{1.0f}; - float green{1.0f}; - float blue{1.0f}; - float white{1.0f}; - float color_temp{1.0f}; - float cold_white{1.0f}; - float warm_white{1.0f}; - uint32_t effect{0}; -}; - void LightState::setup() { ESP_LOGCONFIG(TAG, "Setting up light '%s'...", this->get_name().c_str()); @@ -48,6 +34,9 @@ void LightState::setup() { auto call = this->make_call(); LightStateRTCState recovered{}; + if (this->initial_state_.has_value()) { + recovered = *this->initial_state_; + } switch (this->restore_mode_) { case LIGHT_RESTORE_DEFAULT_OFF: case LIGHT_RESTORE_DEFAULT_ON: @@ -175,6 +164,7 @@ void LightState::set_flash_transition_length(uint32_t flash_transition_length) { uint32_t LightState::get_flash_transition_length() const { return this->flash_transition_length_; } void LightState::set_gamma_correct(float gamma_correct) { this->gamma_correct_ = gamma_correct; } void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } +void LightState::set_initial_state(const LightStateRTCState &initial_state) { this->initial_state_ = initial_state; } bool LightState::supports_effects() { return !this->effects_.empty(); } const std::vector &LightState::get_effects() const { return this->effects_; } void LightState::add_effects(const std::vector &effects) { diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index b0aaa453b5..acba986f24 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -28,6 +28,35 @@ enum LightRestoreMode { LIGHT_RESTORE_AND_ON, }; +struct LightStateRTCState { + LightStateRTCState(ColorMode color_mode, bool state, float brightness, float color_brightness, float red, float green, + float blue, float white, float color_temp, float cold_white, float warm_white) + : color_mode(color_mode), + state(state), + brightness(brightness), + color_brightness(color_brightness), + red(red), + green(green), + blue(blue), + white(white), + color_temp(color_temp), + cold_white(cold_white), + warm_white(warm_white) {} + LightStateRTCState() = default; + ColorMode color_mode{ColorMode::UNKNOWN}; + bool state{false}; + float brightness{1.0f}; + float color_brightness{1.0f}; + float red{1.0f}; + float green{1.0f}; + float blue{1.0f}; + float white{1.0f}; + float color_temp{1.0f}; + float cold_white{1.0f}; + float warm_white{1.0f}; + uint32_t effect{0}; +}; + /** This class represents the communication layer between the front-end MQTT layer and the * hardware output layer. */ @@ -116,6 +145,9 @@ class LightState : public EntityBase, public Component { /// Set the restore mode of this light void set_restore_mode(LightRestoreMode restore_mode); + /// Set the initial state of this light + void set_initial_state(const LightStateRTCState &initial_state); + /// Return whether the light has any effects that meet the trait requirements. bool supports_effects(); @@ -212,6 +244,8 @@ class LightState : public EntityBase, public Component { float gamma_correct_{}; /// Restore mode of the light. LightRestoreMode restore_mode_; + /// Initial state of the light. + optional initial_state_{}; /// List of effects for this light. std::vector effects_; diff --git a/esphome/components/light/types.py b/esphome/components/light/types.py index 64483bcc9c..a586bcbd13 100644 --- a/esphome/components/light/types.py +++ b/esphome/components/light/types.py @@ -1,5 +1,5 @@ -import esphome.codegen as cg from esphome import automation +import esphome.codegen as cg # Base light_ns = cg.esphome_ns.namespace("light") @@ -12,6 +12,8 @@ AddressableLightRef = AddressableLight.operator("ref") Color = cg.esphome_ns.class_("Color") LightColorValues = light_ns.class_("LightColorValues") +LightStateRTCState = light_ns.struct("LightStateRTCState") + # Color modes ColorMode = light_ns.enum("ColorMode", is_class=True) COLOR_MODES = { diff --git a/esphome/const.py b/esphome/const.py index 16f30c179d..e02a1506cb 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -398,6 +398,7 @@ CONF_INDOOR = "indoor" CONF_INFRARED = "infrared" CONF_INITIAL_MODE = "initial_mode" CONF_INITIAL_OPTION = "initial_option" +CONF_INITIAL_STATE = "initial_state" CONF_INITIAL_VALUE = "initial_value" CONF_INPUT = "input" CONF_INTEGRATION_TIME = "integration_time" diff --git a/tests/components/light/common.yaml b/tests/components/light/common.yaml new file mode 100644 index 0000000000..a224dbe8bc --- /dev/null +++ b/tests/components/light/common.yaml @@ -0,0 +1,125 @@ +esphome: + on_boot: + then: + - light.toggle: test_binary_light + - light.turn_off: test_rgb_light + - light.turn_on: + id: test_rgb_light + brightness: 100% + red: 100% + green: 100% + blue: 1.0 + - light.control: + id: test_monochromatic_light + state: on + - light.dim_relative: + id: test_monochromatic_light + relative_brightness: 5% + brightness_limits: + max_brightness: 90% + +light: + - platform: binary + id: test_binary_light + name: Binary Light + output: test_binary + effects: + - strobe: + on_state: + - logger.log: Binary light state changed + - platform: monochromatic + id: test_monochromatic_light + name: Monochromatic Light + output: test_ledc_1 + gamma_correct: 2.8 + default_transition_length: 2s + effects: + - strobe: + - flicker: + - flicker: + name: My Flicker + alpha: 98% + intensity: 1.5% + - lambda: + name: My Custom Effect + update_interval: 1s + lambda: |- + static int state = 0; + state += 1; + if (state == 4) + state = 0; + - pulse: + transition_length: 10s + update_interval: 20s + min_brightness: 10% + max_brightness: 90% + - pulse: + name: pulse2 + transition_length: + on_length: 10s + off_length: 5s + update_interval: 15s + min_brightness: 10% + max_brightness: 90% + - platform: rgb + id: test_rgb_light + name: RGB Light + red: test_ledc_1 + green: test_ledc_2 + blue: test_ledc_3 + - platform: rgbw + id: test_rgbw_light + name: RGBW Light + red: test_ledc_1 + green: test_ledc_2 + blue: test_ledc_3 + white: test_ledc_4 + color_interlock: true + - platform: rgbww + id: test_rgbww_light + name: RGBWW Light + red: test_ledc_1 + green: test_ledc_2 + blue: test_ledc_3 + cold_white: test_ledc_4 + warm_white: test_ledc_5 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + color_interlock: true + - platform: rgbct + id: test_rgbct_light + name: RGBCT Light + red: test_ledc_1 + green: test_ledc_2 + blue: test_ledc_3 + color_temperature: test_ledc_4 + white_brightness: test_ledc_5 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + color_interlock: true + - platform: cwww + id: test_cwww_light + name: CWWW Light + cold_white: test_ledc_1 + warm_white: test_ledc_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true + - platform: color_temperature + id: test_color_temperature_light + name: CT Light + color_temperature: test_ledc_1 + brightness: test_ledc_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + - platform: rgb + id: test_rgb_light_initial_state + name: RGB Light + red: test_ledc_1 + green: test_ledc_2 + blue: test_ledc_3 + initial_state: + color_mode: rgb + red: 100% + green: 50% + blue: 50% diff --git a/tests/components/light/test.esp32-ard.yaml b/tests/components/light/test.esp32-ard.yaml index 1d0b4cd8f0..925197182c 100644 --- a/tests/components/light/test.esp32-ard.yaml +++ b/tests/components/light/test.esp32-ard.yaml @@ -1,23 +1,3 @@ -esphome: - on_boot: - then: - - light.toggle: test_binary_light - - light.turn_off: test_rgb_light - - light.turn_on: - id: test_rgb_light - brightness: 100% - red: 100% - green: 100% - blue: 1.0 - - light.control: - id: test_monochromatic_light - state: on - - light.dim_relative: - id: test_monochromatic_light - relative_brightness: 5% - brightness_limits: - max_brightness: 90% - output: - platform: gpio id: test_binary @@ -38,97 +18,4 @@ output: id: test_ledc_5 pin: 17 -light: - - platform: binary - id: test_binary_light - name: Binary Light - output: test_binary - effects: - - strobe: - on_state: - - logger.log: Binary light state changed - - platform: monochromatic - id: test_monochromatic_light - name: Monochromatic Light - output: test_ledc_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% - - platform: rgb - id: test_rgb_light - name: RGB Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - - platform: rgbw - id: test_rgbw_light - name: RGBW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - white: test_ledc_4 - color_interlock: true - - platform: rgbww - id: test_rgbww_light - name: RGBWW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - cold_white: test_ledc_4 - warm_white: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: rgbct - id: test_rgbct_light - name: RGBCT Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - color_temperature: test_ledc_4 - white_brightness: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: cwww - id: test_cwww_light - name: CWWW Light - cold_white: test_ledc_1 - warm_white: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true - - platform: color_temperature - id: test_color_temperature_light - name: CT Light - color_temperature: test_ledc_1 - brightness: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/light/test.esp32-c3-ard.yaml b/tests/components/light/test.esp32-c3-ard.yaml index 79171805a6..317d5748a3 100644 --- a/tests/components/light/test.esp32-c3-ard.yaml +++ b/tests/components/light/test.esp32-c3-ard.yaml @@ -1,23 +1,3 @@ -esphome: - on_boot: - then: - - light.toggle: test_binary_light - - light.turn_off: test_rgb_light - - light.turn_on: - id: test_rgb_light - brightness: 100% - red: 100% - green: 100% - blue: 1.0 - - light.control: - id: test_monochromatic_light - state: on - - light.dim_relative: - id: test_monochromatic_light - relative_brightness: 5% - brightness_limits: - max_brightness: 90% - output: - platform: gpio id: test_binary @@ -38,97 +18,4 @@ output: id: test_ledc_5 pin: 5 -light: - - platform: binary - id: test_binary_light - name: Binary Light - output: test_binary - effects: - - strobe: - on_state: - - logger.log: Binary light state changed - - platform: monochromatic - id: test_monochromatic_light - name: Monochromatic Light - output: test_ledc_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% - - platform: rgb - id: test_rgb_light - name: RGB Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - - platform: rgbw - id: test_rgbw_light - name: RGBW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - white: test_ledc_4 - color_interlock: true - - platform: rgbww - id: test_rgbww_light - name: RGBWW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - cold_white: test_ledc_4 - warm_white: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: rgbct - id: test_rgbct_light - name: RGBCT Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - color_temperature: test_ledc_4 - white_brightness: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: cwww - id: test_cwww_light - name: CWWW Light - cold_white: test_ledc_1 - warm_white: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true - - platform: color_temperature - id: test_color_temperature_light - name: CT Light - color_temperature: test_ledc_1 - brightness: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/light/test.esp32-c3-idf.yaml b/tests/components/light/test.esp32-c3-idf.yaml index 79171805a6..317d5748a3 100644 --- a/tests/components/light/test.esp32-c3-idf.yaml +++ b/tests/components/light/test.esp32-c3-idf.yaml @@ -1,23 +1,3 @@ -esphome: - on_boot: - then: - - light.toggle: test_binary_light - - light.turn_off: test_rgb_light - - light.turn_on: - id: test_rgb_light - brightness: 100% - red: 100% - green: 100% - blue: 1.0 - - light.control: - id: test_monochromatic_light - state: on - - light.dim_relative: - id: test_monochromatic_light - relative_brightness: 5% - brightness_limits: - max_brightness: 90% - output: - platform: gpio id: test_binary @@ -38,97 +18,4 @@ output: id: test_ledc_5 pin: 5 -light: - - platform: binary - id: test_binary_light - name: Binary Light - output: test_binary - effects: - - strobe: - on_state: - - logger.log: Binary light state changed - - platform: monochromatic - id: test_monochromatic_light - name: Monochromatic Light - output: test_ledc_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% - - platform: rgb - id: test_rgb_light - name: RGB Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - - platform: rgbw - id: test_rgbw_light - name: RGBW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - white: test_ledc_4 - color_interlock: true - - platform: rgbww - id: test_rgbww_light - name: RGBWW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - cold_white: test_ledc_4 - warm_white: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: rgbct - id: test_rgbct_light - name: RGBCT Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - color_temperature: test_ledc_4 - white_brightness: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: cwww - id: test_cwww_light - name: CWWW Light - cold_white: test_ledc_1 - warm_white: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true - - platform: color_temperature - id: test_color_temperature_light - name: CT Light - color_temperature: test_ledc_1 - brightness: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/light/test.esp32-idf.yaml b/tests/components/light/test.esp32-idf.yaml index 1d0b4cd8f0..925197182c 100644 --- a/tests/components/light/test.esp32-idf.yaml +++ b/tests/components/light/test.esp32-idf.yaml @@ -1,23 +1,3 @@ -esphome: - on_boot: - then: - - light.toggle: test_binary_light - - light.turn_off: test_rgb_light - - light.turn_on: - id: test_rgb_light - brightness: 100% - red: 100% - green: 100% - blue: 1.0 - - light.control: - id: test_monochromatic_light - state: on - - light.dim_relative: - id: test_monochromatic_light - relative_brightness: 5% - brightness_limits: - max_brightness: 90% - output: - platform: gpio id: test_binary @@ -38,97 +18,4 @@ output: id: test_ledc_5 pin: 17 -light: - - platform: binary - id: test_binary_light - name: Binary Light - output: test_binary - effects: - - strobe: - on_state: - - logger.log: Binary light state changed - - platform: monochromatic - id: test_monochromatic_light - name: Monochromatic Light - output: test_ledc_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% - - platform: rgb - id: test_rgb_light - name: RGB Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - - platform: rgbw - id: test_rgbw_light - name: RGBW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - white: test_ledc_4 - color_interlock: true - - platform: rgbww - id: test_rgbww_light - name: RGBWW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - cold_white: test_ledc_4 - warm_white: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: rgbct - id: test_rgbct_light - name: RGBCT Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - color_temperature: test_ledc_4 - white_brightness: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: cwww - id: test_cwww_light - name: CWWW Light - cold_white: test_ledc_1 - warm_white: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true - - platform: color_temperature - id: test_color_temperature_light - name: CT Light - color_temperature: test_ledc_1 - brightness: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/light/test.esp8266-ard.yaml b/tests/components/light/test.esp8266-ard.yaml index 555e1a1b67..518011e925 100644 --- a/tests/components/light/test.esp8266-ard.yaml +++ b/tests/components/light/test.esp8266-ard.yaml @@ -1,23 +1,3 @@ -esphome: - on_boot: - then: - - light.toggle: test_binary_light - - light.turn_off: test_rgb_light - - light.turn_on: - id: test_rgb_light - brightness: 100% - red: 100% - green: 100% - blue: 1.0 - - light.control: - id: test_monochromatic_light - state: on - - light.dim_relative: - id: test_monochromatic_light - relative_brightness: 5% - brightness_limits: - max_brightness: 90% - output: - platform: gpio id: test_binary @@ -38,97 +18,4 @@ output: id: test_ledc_5 pin: 16 -light: - - platform: binary - id: test_binary_light - name: Binary Light - output: test_binary - effects: - - strobe: - on_state: - - logger.log: Binary light state changed - - platform: monochromatic - id: test_monochromatic_light - name: Monochromatic Light - output: test_ledc_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% - - platform: rgb - id: test_rgb_light - name: RGB Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - - platform: rgbw - id: test_rgbw_light - name: RGBW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - white: test_ledc_4 - color_interlock: true - - platform: rgbww - id: test_rgbww_light - name: RGBWW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - cold_white: test_ledc_4 - warm_white: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: rgbct - id: test_rgbct_light - name: RGBCT Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - color_temperature: test_ledc_4 - white_brightness: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: cwww - id: test_cwww_light - name: CWWW Light - cold_white: test_ledc_1 - warm_white: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true - - platform: color_temperature - id: test_color_temperature_light - name: CT Light - color_temperature: test_ledc_1 - brightness: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/light/test.rp2040-ard.yaml b/tests/components/light/test.rp2040-ard.yaml index a509bc85c9..a5a37fd559 100644 --- a/tests/components/light/test.rp2040-ard.yaml +++ b/tests/components/light/test.rp2040-ard.yaml @@ -1,23 +1,3 @@ -esphome: - on_boot: - then: - - light.toggle: test_binary_light - - light.turn_off: test_rgb_light - - light.turn_on: - id: test_rgb_light - brightness: 100% - red: 100% - green: 100% - blue: 1.0 - - light.control: - id: test_monochromatic_light - state: on - - light.dim_relative: - id: test_monochromatic_light - relative_brightness: 5% - brightness_limits: - max_brightness: 90% - output: - platform: gpio id: test_binary @@ -38,97 +18,4 @@ output: id: test_ledc_5 pin: 5 -light: - - platform: binary - id: test_binary_light - name: Binary Light - output: test_binary - effects: - - strobe: - on_state: - - logger.log: Binary light state changed - - platform: monochromatic - id: test_monochromatic_light - name: Monochromatic Light - output: test_ledc_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% - - platform: rgb - id: test_rgb_light - name: RGB Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - - platform: rgbw - id: test_rgbw_light - name: RGBW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - white: test_ledc_4 - color_interlock: true - - platform: rgbww - id: test_rgbww_light - name: RGBWW Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - cold_white: test_ledc_4 - warm_white: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: rgbct - id: test_rgbct_light - name: RGBCT Light - red: test_ledc_1 - green: test_ledc_2 - blue: test_ledc_3 - color_temperature: test_ledc_4 - white_brightness: test_ledc_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true - - platform: cwww - id: test_cwww_light - name: CWWW Light - cold_white: test_ledc_1 - warm_white: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true - - platform: color_temperature - id: test_color_temperature_light - name: CT Light - color_temperature: test_ledc_1 - brightness: test_ledc_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml From f2249848585e98e532ae3d71d1df0a9f483a5a5c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sun, 13 Oct 2024 10:51:51 +1100 Subject: [PATCH 0467/1052] [CI] failures when installing using apt-get. (#7593) --- .github/workflows/ci.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38527c20c7..178b914a1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -315,7 +315,9 @@ jobs: key: platformio-${{ matrix.pio_cache_key }} - name: Install clang-tidy - run: sudo apt-get install clang-tidy-14 + run: | + sudo apt-get update + sudo apt-get install clang-tidy-14 - name: Register problem matchers run: | @@ -397,7 +399,9 @@ jobs: file: ${{ fromJson(needs.list-components.outputs.components) }} steps: - name: Install dependencies - run: sudo apt-get install libsdl2-dev + run: | + sudo apt-get update + sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 @@ -451,7 +455,9 @@ jobs: run: echo ${{ matrix.components }} - name: Install dependencies - run: sudo apt-get install libsdl2-dev + run: | + sudo apt-get update + sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 From 42f6095960718bc33b40e21ab3dbec50dc347a05 Mon Sep 17 00:00:00 2001 From: Pietro Date: Sun, 13 Oct 2024 11:24:17 +0200 Subject: [PATCH 0468/1052] [core][esp32_rmt_led_strip] Migrate ExternalRAMAllocator to RAMAllocator And add psram flag to esp32_rmt_led_strip Co-authored-by: guillempages Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- .../esp32_rmt_led_strip/led_strip.cpp | 4 +- .../esp32_rmt_led_strip/led_strip.h | 2 + .../components/esp32_rmt_led_strip/light.py | 4 +- esphome/core/helpers.h | 44 ++++++++++++------- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index 71ab099de5..c2209f7a6c 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 @@ void ESP32RMTLEDStripLightOutput::setup() { size_t buffer_size = this->get_buffer_size_(); - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); this->buf_ = allocator.allocate(buffer_size); if (this->buf_ == nullptr) { ESP_LOGE(TAG, "Cannot allocate LED buffer!"); @@ -37,7 +37,7 @@ void ESP32RMTLEDStripLightOutput::setup() { return; } - ExternalRAMAllocator rmt_allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1); // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index 43215cf12b..d21bd86e75 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -45,6 +45,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; } void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; } void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; } + void set_use_psram(bool use_psram) { this->use_psram_ = use_psram; } /// Set a maximum refresh rate in µs as some lights do not like being updated too often. void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; } @@ -75,6 +76,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint16_t num_leds_; bool is_rgbw_; bool is_wrgb_; + bool use_psram_; rmt_item32_t bit0_, bit1_, reset_; RGBOrder rgb_order_; diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 1e3c2d4f72..79f339e248 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -55,7 +55,7 @@ CHIPSETS = { "SM16703": LEDStripTimings(300, 900, 900, 300, 0, 0), } - +CONF_USE_PSRAM = "use_psram" CONF_IS_WRGB = "is_wrgb" CONF_BIT0_HIGH = "bit0_high" CONF_BIT0_LOW = "bit0_low" @@ -77,6 +77,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, cv.Optional(CONF_IS_WRGB, default=False): cv.boolean, + cv.Optional(CONF_USE_PSRAM, default=True): cv.boolean, cv.Inclusive( CONF_BIT0_HIGH, "custom", @@ -145,6 +146,7 @@ async def to_code(config): cg.add(var.set_rgb_order(config[CONF_RGB_ORDER])) cg.add(var.set_is_rgbw(config[CONF_IS_RGBW])) cg.add(var.set_is_wrgb(config[CONF_IS_WRGB])) + cg.add(var.set_use_psram(config[CONF_USE_PSRAM])) cg.add( var.set_rmt_channel( diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 7df4b84230..7f6fe9bfdc 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -651,35 +651,45 @@ void delay_microseconds_safe(uint32_t us); /// @name Memory management ///@{ -/** An STL allocator that uses SPI RAM. +/** An STL allocator that uses SPI or internal RAM. + * Returns `nullptr` in case no memory is available. * - * By setting flags, it can be configured to don't try main memory if SPI RAM is full or unavailable, and to return - * `nulllptr` instead of aborting when no memory is available. + * By setting flags, it can be configured to: + * - perform external allocation falling back to main memory if SPI RAM is full or unavailable + * - perform external allocation only + * - perform internal allocation only */ -template class ExternalRAMAllocator { +template class RAMAllocator { public: using value_type = T; enum Flags { - NONE = 0, - REFUSE_INTERNAL = 1 << 0, ///< Refuse falling back to internal memory when external RAM is full or unavailable. - ALLOW_FAILURE = 1 << 1, ///< Don't abort when memory allocation fails. + NONE = 0, // Perform external allocation and fall back to internal memory + ALLOC_EXTERNAL = 1 << 0, // Perform external allocation only. + ALLOC_INTERNAL = 1 << 1, // Perform internal allocation only. + ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility. }; - ExternalRAMAllocator() = default; - ExternalRAMAllocator(Flags flags) : flags_{flags} {} - template constexpr ExternalRAMAllocator(const ExternalRAMAllocator &other) : flags_{other.flags_} {} + RAMAllocator() = default; + RAMAllocator(uint8_t flags) : flags_{flags} {} + template constexpr RAMAllocator(const RAMAllocator &other) : flags_{other.flags_} {} T *allocate(size_t n) { size_t size = n * sizeof(T); T *ptr = nullptr; #ifdef USE_ESP32 - ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); -#endif - if (ptr == nullptr && (this->flags_ & Flags::REFUSE_INTERNAL) == 0) + // External allocation by default or if explicitely requested + if ((this->flags_ & Flags::ALLOC_EXTERNAL) || ((this->flags_ & Flags::ALLOC_INTERNAL) == 0)) { + ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); + } + // Fallback to internal allocation if explicitely requested or no flag is specified + if (ptr == nullptr && ((this->flags_ & Flags::ALLOC_INTERNAL) || (this->flags_ & Flags::ALLOC_EXTERNAL) == 0)) { ptr = static_cast(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) - if (ptr == nullptr && (this->flags_ & Flags::ALLOW_FAILURE) == 0) - abort(); + } +#else + // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported + ptr = static_cast(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) +#endif return ptr; } @@ -688,9 +698,11 @@ template class ExternalRAMAllocator { } private: - Flags flags_{Flags::ALLOW_FAILURE}; + uint8_t flags_{Flags::ALLOW_FAILURE}; }; +template using ExternalRAMAllocator = RAMAllocator; + /// @} /// @name Internal functions From cf14c02b8a3c1e71ed2dae186481822225e322cf Mon Sep 17 00:00:00 2001 From: RFDarter Date: Sun, 13 Oct 2024 20:50:13 +0200 Subject: [PATCH 0469/1052] [web_server] Event component grouping (#7586) --- esphome/components/event/__init__.py | 32 ++++++++++++-------- esphome/components/web_server/web_server.cpp | 8 ++++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/esphome/components/event/__init__.py b/esphome/components/event/__init__.py index 031a4c0de8..a7732dfcaf 100644 --- a/esphome/components/event/__init__.py +++ b/esphome/components/event/__init__.py @@ -1,6 +1,6 @@ from esphome import automation import esphome.codegen as cg -from esphome.components import mqtt +from esphome.components import mqtt, web_server import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, @@ -11,6 +11,7 @@ from esphome.const import ( CONF_MQTT_ID, CONF_ON_EVENT, CONF_TRIGGER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_BUTTON, DEVICE_CLASS_DOORBELL, DEVICE_CLASS_EMPTY, @@ -40,17 +41,21 @@ EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template()) validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") -EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( - { - cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent), - cv.GenerateID(): cv.declare_id(Event), - cv.Optional(CONF_DEVICE_CLASS): validate_device_class, - cv.Optional(CONF_ON_EVENT): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger), - } - ), - } +EVENT_SCHEMA = ( + cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) + .extend(cv.MQTT_COMPONENT_SCHEMA) + .extend( + { + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent), + cv.GenerateID(): cv.declare_id(Event), + cv.Optional(CONF_DEVICE_CLASS): validate_device_class, + cv.Optional(CONF_ON_EVENT): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger), + } + ), + } + ) ) _UNDEF = object() @@ -97,6 +102,9 @@ async def setup_event_core_(var, config, *, event_types: list[str]): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) + async def register_event(var, config, *, event_types: list[str]): if not CORE.has_id(config[CONF_ID]): diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 192feb78d5..c801efbe88 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1443,7 +1443,7 @@ void WebServer::on_event(event::Event *obj, const std::string &event_type) { } std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) { - return json::build_json([obj, event_type, start_config](JsonObject root) { + return json::build_json([this, obj, event_type, start_config](JsonObject root) { set_json_id(root, obj, "event-" + obj->get_object_id(), start_config); if (!event_type.empty()) { root["event_type"] = event_type; @@ -1454,6 +1454,12 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty event_types.add(event_type); } root["device_class"] = obj->get_device_class(); + if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { + root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } + } } }); } From 654cee6f83a84efd014ebbbe8fd097c1cd196f10 Mon Sep 17 00:00:00 2001 From: RFDarter Date: Sun, 13 Oct 2024 20:50:22 +0200 Subject: [PATCH 0470/1052] [web_server] expose event compoent to REST (#7587) --- esphome/components/web_server/web_server.cpp | 22 ++++++++++++++++++++ esphome/components/web_server/web_server.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index c801efbe88..0467023039 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1441,7 +1441,24 @@ std::string WebServer::alarm_control_panel_json(alarm_control_panel::AlarmContro void WebServer::on_event(event::Event *obj, const std::string &event_type) { this->events_.send(this->event_json(obj, event_type, DETAIL_STATE).c_str(), "state"); } +void WebServer::handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match) { + for (event::Event *obj : App.get_events()) { + if (obj->get_object_id() != match.id) + continue; + if (request->method() == HTTP_GET && match.method.empty()) { + auto detail = DETAIL_STATE; + auto *param = request->getParam("detail"); + if (param && param->value() == "all") { + detail = DETAIL_ALL; + } + std::string data = this->event_json(obj, "", detail); + request->send(200, "application/json", data.c_str()); + return; + } + } + request->send(404); +} std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) { return json::build_json([this, obj, event_type, start_config](JsonObject root) { set_json_id(root, obj, "event-" + obj->get_object_id(), start_config); @@ -1651,6 +1668,11 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { return true; #endif +#ifdef USE_EVENT + if (request->method() == HTTP_GET && match.domain == "event") + return true; +#endif + #ifdef USE_UPDATE if ((request->method() == HTTP_POST || request->method() == HTTP_GET) && match.domain == "update") return true; diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index ea1f62fc43..8edb678169 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -322,6 +322,9 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #ifdef USE_EVENT void on_event(event::Event *obj, const std::string &event_type) override; + /// Handle a event request under '/event'. + void handle_event_request(AsyncWebServerRequest *request, const UrlMatch &match); + /// Dump the event details with its value as a JSON string. std::string event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config); #endif From 77d0bfc4bb97edb209cd6481231aea9806125575 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 14 Oct 2024 07:10:48 +1100 Subject: [PATCH 0471/1052] [touchscreen] Fix coordinates when using rotation (#7591) --- esphome/components/touchscreen/touchscreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/touchscreen/touchscreen.cpp b/esphome/components/touchscreen/touchscreen.cpp index b9498de152..dfe723aedf 100644 --- a/esphome/components/touchscreen/touchscreen.cpp +++ b/esphome/components/touchscreen/touchscreen.cpp @@ -18,8 +18,8 @@ void Touchscreen::attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::Int void Touchscreen::call_setup() { if (this->display_ != nullptr) { - this->display_width_ = this->display_->get_native_width(); - this->display_height_ = this->display_->get_native_height(); + this->display_width_ = this->display_->get_width(); + this->display_height_ = this->display_->get_height(); } PollingComponent::call_setup(); } From 39e922580a7e288e80bd6a1f9deaf91bc8f5ff7b Mon Sep 17 00:00:00 2001 From: Niclas Larsson Date: Sun, 13 Oct 2024 22:17:37 +0200 Subject: [PATCH 0472/1052] Fix update sequence when update is set to false (#5225) (#7407) --- .../shelly_dimmer/shelly_dimmer.cpp | 68 +++++++++---------- .../components/shelly_dimmer/shelly_dimmer.h | 2 + 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index 144236bfe1..b415840bdc 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -64,46 +64,46 @@ uint16_t shelly_dimmer_checksum(const uint8_t *buf, int len) { return std::accumulate(buf, buf + len, 0); } +bool ShellyDimmer::is_running_configured_version() const { + return this->version_major_ == USE_SHD_FIRMWARE_MAJOR_VERSION && + this->version_minor_ == USE_SHD_FIRMWARE_MINOR_VERSION; +} + +void ShellyDimmer::handle_firmware() { + // Reset the STM32 and check the firmware version. + this->reset_normal_boot_(); + this->send_command_(SHELLY_DIMMER_PROTO_CMD_VERSION, nullptr, 0); + ESP_LOGI(TAG, "STM32 current firmware version: %d.%d, desired version: %d.%d", this->version_major_, + this->version_minor_, USE_SHD_FIRMWARE_MAJOR_VERSION, USE_SHD_FIRMWARE_MINOR_VERSION); + + if (!is_running_configured_version()) { +#ifdef USE_SHD_FIRMWARE_DATA + if (!this->upgrade_firmware_()) { + ESP_LOGW(TAG, "Failed to upgrade firmware"); + this->mark_failed(); + return; + } + + this->reset_normal_boot_(); + this->send_command_(SHELLY_DIMMER_PROTO_CMD_VERSION, nullptr, 0); + if (!is_running_configured_version()) { + ESP_LOGE(TAG, "STM32 firmware upgrade already performed, but version is still incorrect"); + this->mark_failed(); + return; + } +#else + ESP_LOGW(TAG, "Firmware version mismatch, put 'update: true' in the yaml to flash an update."); +#endif + } +} + void ShellyDimmer::setup() { this->pin_nrst_->setup(); this->pin_boot0_->setup(); ESP_LOGI(TAG, "Initializing Shelly Dimmer..."); - // Reset the STM32 and check the firmware version. - for (int i = 0; i < 2; i++) { - this->reset_normal_boot_(); - this->send_command_(SHELLY_DIMMER_PROTO_CMD_VERSION, nullptr, 0); - ESP_LOGI(TAG, "STM32 current firmware version: %d.%d, desired 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 || - this->version_minor_ != USE_SHD_FIRMWARE_MINOR_VERSION) { -#ifdef USE_SHD_FIRMWARE_DATA - // Update firmware if needed. - ESP_LOGW(TAG, "Unsupported STM32 firmware version, flashing"); - if (i > 0) { - // Upgrade was already performed but the reported version is still not right. - ESP_LOGE(TAG, "STM32 firmware upgrade already performed, but version is still incorrect"); - this->mark_failed(); - return; - } - - if (!this->upgrade_firmware_()) { - ESP_LOGW(TAG, "Failed to upgrade firmware"); - this->mark_failed(); - return; - } - - // Firmware upgrade completed, do the checks again. - continue; -#else - ESP_LOGW(TAG, "Firmware version mismatch, put 'update: true' in the yaml to flash an update."); - this->mark_failed(); - return; -#endif - } - break; - } + this->handle_firmware(); this->send_settings_(); // Do an immediate poll to refresh current state. diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.h b/esphome/components/shelly_dimmer/shelly_dimmer.h index 4701f3a32a..fd75caa797 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.h +++ b/esphome/components/shelly_dimmer/shelly_dimmer.h @@ -20,6 +20,8 @@ class ShellyDimmer : public PollingComponent, public light::LightOutput, public public: float get_setup_priority() const override { return setup_priority::LATE; } + bool is_running_configured_version() const; + void handle_firmware(); void setup() override; void update() override; void dump_config() override; From b617b92758004580ce64f4b9426b49c244675c01 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Wed, 9 Oct 2024 03:44:19 -0700 Subject: [PATCH 0473/1052] fix uart settings check (#7573) --- esphome/components/cse7766/cse7766.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 47058badce..48240464b3 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -244,7 +244,7 @@ void CSE7766Component::dump_config() { LOG_SENSOR(" ", "Apparent Power", this->apparent_power_sensor_); LOG_SENSOR(" ", "Reactive Power", this->reactive_power_sensor_); LOG_SENSOR(" ", "Power Factor", this->power_factor_sensor_); - this->check_uart_settings(4800); + this->check_uart_settings(4800, 1, uart::UART_CONFIG_PARITY_EVEN); } } // namespace cse7766 From bafb0ad6889b13d38ac4b6caaaa8798e58daf0dd Mon Sep 17 00:00:00 2001 From: RFDarter Date: Sun, 13 Oct 2024 20:50:13 +0200 Subject: [PATCH 0474/1052] [web_server] Event component grouping (#7586) --- esphome/components/event/__init__.py | 32 ++++++++++++-------- esphome/components/web_server/web_server.cpp | 8 ++++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/esphome/components/event/__init__.py b/esphome/components/event/__init__.py index 031a4c0de8..a7732dfcaf 100644 --- a/esphome/components/event/__init__.py +++ b/esphome/components/event/__init__.py @@ -1,6 +1,6 @@ from esphome import automation import esphome.codegen as cg -from esphome.components import mqtt +from esphome.components import mqtt, web_server import esphome.config_validation as cv from esphome.const import ( CONF_DEVICE_CLASS, @@ -11,6 +11,7 @@ from esphome.const import ( CONF_MQTT_ID, CONF_ON_EVENT, CONF_TRIGGER_ID, + CONF_WEB_SERVER, DEVICE_CLASS_BUTTON, DEVICE_CLASS_DOORBELL, DEVICE_CLASS_EMPTY, @@ -40,17 +41,21 @@ EventTrigger = event_ns.class_("EventTrigger", automation.Trigger.template()) validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_") -EVENT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend( - { - cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent), - cv.GenerateID(): cv.declare_id(Event), - cv.Optional(CONF_DEVICE_CLASS): validate_device_class, - cv.Optional(CONF_ON_EVENT): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger), - } - ), - } +EVENT_SCHEMA = ( + cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) + .extend(cv.MQTT_COMPONENT_SCHEMA) + .extend( + { + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTEventComponent), + cv.GenerateID(): cv.declare_id(Event), + cv.Optional(CONF_DEVICE_CLASS): validate_device_class, + cv.Optional(CONF_ON_EVENT): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(EventTrigger), + } + ), + } + ) ) _UNDEF = object() @@ -97,6 +102,9 @@ async def setup_event_core_(var, config, *, event_types: list[str]): mqtt_ = cg.new_Pvariable(mqtt_id, var) await mqtt.register_mqtt_component(mqtt_, config) + if web_server_config := config.get(CONF_WEB_SERVER): + await web_server.add_entity_config(var, web_server_config) + async def register_event(var, config, *, event_types: list[str]): if not CORE.has_id(config[CONF_ID]): diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 192feb78d5..c801efbe88 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1443,7 +1443,7 @@ void WebServer::on_event(event::Event *obj, const std::string &event_type) { } std::string WebServer::event_json(event::Event *obj, const std::string &event_type, JsonDetail start_config) { - return json::build_json([obj, event_type, start_config](JsonObject root) { + return json::build_json([this, obj, event_type, start_config](JsonObject root) { set_json_id(root, obj, "event-" + obj->get_object_id(), start_config); if (!event_type.empty()) { root["event_type"] = event_type; @@ -1454,6 +1454,12 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty event_types.add(event_type); } root["device_class"] = obj->get_device_class(); + if (this->sorting_entitys_.find(obj) != this->sorting_entitys_.end()) { + root["sorting_weight"] = this->sorting_entitys_[obj].weight; + if (this->sorting_groups_.find(this->sorting_entitys_[obj].group_id) != this->sorting_groups_.end()) { + root["sorting_group"] = this->sorting_groups_[this->sorting_entitys_[obj].group_id].name; + } + } } }); } From f52136338dae9dfbb48f05cea83b1a40ec9178de Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 14 Oct 2024 07:10:48 +1100 Subject: [PATCH 0475/1052] [touchscreen] Fix coordinates when using rotation (#7591) --- esphome/components/touchscreen/touchscreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/touchscreen/touchscreen.cpp b/esphome/components/touchscreen/touchscreen.cpp index b9498de152..dfe723aedf 100644 --- a/esphome/components/touchscreen/touchscreen.cpp +++ b/esphome/components/touchscreen/touchscreen.cpp @@ -18,8 +18,8 @@ void Touchscreen::attach_interrupt_(InternalGPIOPin *irq_pin, esphome::gpio::Int void Touchscreen::call_setup() { if (this->display_ != nullptr) { - this->display_width_ = this->display_->get_native_width(); - this->display_height_ = this->display_->get_native_height(); + this->display_width_ = this->display_->get_width(); + this->display_height_ = this->display_->get_height(); } PollingComponent::call_setup(); } From dda27d9de45e56006f4b207c4ad95c22ad16b330 Mon Sep 17 00:00:00 2001 From: Niclas Larsson Date: Sun, 13 Oct 2024 22:17:37 +0200 Subject: [PATCH 0476/1052] Fix update sequence when update is set to false (#5225) (#7407) --- .../shelly_dimmer/shelly_dimmer.cpp | 68 +++++++++---------- .../components/shelly_dimmer/shelly_dimmer.h | 2 + 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index 144236bfe1..b415840bdc 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -64,46 +64,46 @@ uint16_t shelly_dimmer_checksum(const uint8_t *buf, int len) { return std::accumulate(buf, buf + len, 0); } +bool ShellyDimmer::is_running_configured_version() const { + return this->version_major_ == USE_SHD_FIRMWARE_MAJOR_VERSION && + this->version_minor_ == USE_SHD_FIRMWARE_MINOR_VERSION; +} + +void ShellyDimmer::handle_firmware() { + // Reset the STM32 and check the firmware version. + this->reset_normal_boot_(); + this->send_command_(SHELLY_DIMMER_PROTO_CMD_VERSION, nullptr, 0); + ESP_LOGI(TAG, "STM32 current firmware version: %d.%d, desired version: %d.%d", this->version_major_, + this->version_minor_, USE_SHD_FIRMWARE_MAJOR_VERSION, USE_SHD_FIRMWARE_MINOR_VERSION); + + if (!is_running_configured_version()) { +#ifdef USE_SHD_FIRMWARE_DATA + if (!this->upgrade_firmware_()) { + ESP_LOGW(TAG, "Failed to upgrade firmware"); + this->mark_failed(); + return; + } + + this->reset_normal_boot_(); + this->send_command_(SHELLY_DIMMER_PROTO_CMD_VERSION, nullptr, 0); + if (!is_running_configured_version()) { + ESP_LOGE(TAG, "STM32 firmware upgrade already performed, but version is still incorrect"); + this->mark_failed(); + return; + } +#else + ESP_LOGW(TAG, "Firmware version mismatch, put 'update: true' in the yaml to flash an update."); +#endif + } +} + void ShellyDimmer::setup() { this->pin_nrst_->setup(); this->pin_boot0_->setup(); ESP_LOGI(TAG, "Initializing Shelly Dimmer..."); - // Reset the STM32 and check the firmware version. - for (int i = 0; i < 2; i++) { - this->reset_normal_boot_(); - this->send_command_(SHELLY_DIMMER_PROTO_CMD_VERSION, nullptr, 0); - ESP_LOGI(TAG, "STM32 current firmware version: %d.%d, desired 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 || - this->version_minor_ != USE_SHD_FIRMWARE_MINOR_VERSION) { -#ifdef USE_SHD_FIRMWARE_DATA - // Update firmware if needed. - ESP_LOGW(TAG, "Unsupported STM32 firmware version, flashing"); - if (i > 0) { - // Upgrade was already performed but the reported version is still not right. - ESP_LOGE(TAG, "STM32 firmware upgrade already performed, but version is still incorrect"); - this->mark_failed(); - return; - } - - if (!this->upgrade_firmware_()) { - ESP_LOGW(TAG, "Failed to upgrade firmware"); - this->mark_failed(); - return; - } - - // Firmware upgrade completed, do the checks again. - continue; -#else - ESP_LOGW(TAG, "Firmware version mismatch, put 'update: true' in the yaml to flash an update."); - this->mark_failed(); - return; -#endif - } - break; - } + this->handle_firmware(); this->send_settings_(); // Do an immediate poll to refresh current state. diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.h b/esphome/components/shelly_dimmer/shelly_dimmer.h index 4701f3a32a..fd75caa797 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.h +++ b/esphome/components/shelly_dimmer/shelly_dimmer.h @@ -20,6 +20,8 @@ class ShellyDimmer : public PollingComponent, public light::LightOutput, public public: float get_setup_priority() const override { return setup_priority::LATE; } + bool is_running_configured_version() const; + void handle_firmware(); void setup() override; void update() override; void dump_config() override; From d24ad2e0e7168e3e841ff09d3021256e38e43977 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:31:16 +1300 Subject: [PATCH 0477/1052] Bump version to 2024.10.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 0752e3b169..86f950dc79 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.10.0b1" +__version__ = "2024.10.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 27e1233fc0649f48d2f6eb5b89c5f65a59b0669c Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sun, 13 Oct 2024 10:51:51 +1100 Subject: [PATCH 0478/1052] [CI] failures when installing using apt-get. (#7593) --- .github/workflows/ci.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38527c20c7..178b914a1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -315,7 +315,9 @@ jobs: key: platformio-${{ matrix.pio_cache_key }} - name: Install clang-tidy - run: sudo apt-get install clang-tidy-14 + run: | + sudo apt-get update + sudo apt-get install clang-tidy-14 - name: Register problem matchers run: | @@ -397,7 +399,9 @@ jobs: file: ${{ fromJson(needs.list-components.outputs.components) }} steps: - name: Install dependencies - run: sudo apt-get install libsdl2-dev + run: | + sudo apt-get update + sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 @@ -451,7 +455,9 @@ jobs: run: echo ${{ matrix.components }} - name: Install dependencies - run: sudo apt-get install libsdl2-dev + run: | + sudo apt-get update + sudo apt-get install libsdl2-dev - name: Check out code from GitHub uses: actions/checkout@v4.1.7 From 312799babff8c80318b9d80fbaac3fe330e25d54 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 14 Oct 2024 03:31:37 +0200 Subject: [PATCH 0479/1052] Update test_build_components (#7597) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- script/test_build_components | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/script/test_build_components b/script/test_build_components index e885294b99..62fe0f1b55 100755 --- a/script/test_build_components +++ b/script/test_build_components @@ -2,6 +2,15 @@ set -e +help() { + echo "Usage: $0 [-e ] [-c ] [-t ]" 1>&2 + echo 1>&2 + echo " - e - Parameter for esphome command. Default compile. Common alternative is config." 1>&2 + echo " - c - Component folder name to test. Default *. E.g. '-c logger'." 1>&2 + echo " - t - Target name to test. Put '-t list' to display all possibilities. E.g. '-t esp32-s2-idf-51'." 1>&2 + exit 1 +} + # Parse parameter: # - `e` - Parameter for `esphome` command. Default `compile`. Common alternative is `config`. # - `c` - Component folder name to test. Default `*`. @@ -13,7 +22,7 @@ do e) esphome_command=${OPTARG};; c) target_component=${OPTARG};; t) requested_target_platform=${OPTARG};; - \?) echo "Usage: $0 [-e ] [-c ] [-t ]" 1>&2; exit 1;; + \?) help;; esac done @@ -24,8 +33,8 @@ if ! [ -d "./tests/test_build_components/build" ]; then fi start_esphome() { - if [ -n "$requested_target_platform" ] && [ "$requested_target_platform" != "$target_platform" ]; then - echo "Skiping $target_platform" + if [ -n "$requested_target_platform" ] && [ "$requested_target_platform" != "$target_platform_with_version" ]; then + echo "Skipping $target_platform_with_version" return fi # create dynamic yaml file in `build` folder. From 2cca26ada4f8c4525f1009b3e40f69e0f0675796 Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Mon, 14 Oct 2024 20:59:23 +0300 Subject: [PATCH 0480/1052] [fix] ESP32-C6: internal temperature reporting (#7579) --- .../internal_temperature/internal_temperature.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/internal_temperature/internal_temperature.cpp b/esphome/components/internal_temperature/internal_temperature.cpp index 47f516f568..9ef5cbecd5 100644 --- a/esphome/components/internal_temperature/internal_temperature.cpp +++ b/esphome/components/internal_temperature/internal_temperature.cpp @@ -7,7 +7,8 @@ extern "C" { uint8_t temprature_sens_read(); } -#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ + defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) #include "driver/temp_sensor.h" #endif // USE_ESP32_VARIANT #endif // USE_ESP32 @@ -34,7 +35,8 @@ void InternalTemperatureSensor::update() { ESP_LOGV(TAG, "Raw temperature value: %d", raw); temperature = (raw - 32) / 1.8f; success = (raw != 128); -#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) +#elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ + defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT(); temp_sensor_set_config(tsens); temp_sensor_start(); From 9b4b50a3a64e7268d96fdff35026152816e55d98 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:29:17 +1300 Subject: [PATCH 0481/1052] Bump version to 2024.10.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 86f950dc79..5061b1a439 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.10.0b2" +__version__ = "2024.10.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From b0a25872dadfd75584a0a90f05fb4b49577d1339 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 16 Oct 2024 05:22:45 +0200 Subject: [PATCH 0482/1052] [code-quality] statsd component (#7603) Co-authored-by: Tomasz Duda --- esphome/components/statsd/statsd.cpp | 2 ++ esphome/components/statsd/statsd.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/esphome/components/statsd/statsd.cpp b/esphome/components/statsd/statsd.cpp index 68b24908d2..b7fad19332 100644 --- a/esphome/components/statsd/statsd.cpp +++ b/esphome/components/statsd/statsd.cpp @@ -2,6 +2,7 @@ #include "statsd.h" +#ifdef USE_NETWORK namespace esphome { namespace statsd { @@ -154,3 +155,4 @@ void StatsdComponent::send_(std::string *out) { } // namespace statsd } // namespace esphome +#endif diff --git a/esphome/components/statsd/statsd.h b/esphome/components/statsd/statsd.h index ef42579587..34f84cbe00 100644 --- a/esphome/components/statsd/statsd.h +++ b/esphome/components/statsd/statsd.h @@ -3,6 +3,7 @@ #include #include "esphome/core/defines.h" +#ifdef USE_NETWORK #include "esphome/core/component.h" #include "esphome/components/socket/socket.h" #include "esphome/components/network/ip_address.h" @@ -84,3 +85,4 @@ class StatsdComponent : public PollingComponent { } // namespace statsd } // namespace esphome +#endif From de943908bd248c8c803fffb339e3a6e16d16b303 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:23:43 +1100 Subject: [PATCH 0483/1052] [automation] Implement all and any condition shortcuts (#7565) --- esphome/automation.py | 31 +++++++++++++++++++++++++++++-- esphome/const.py | 2 ++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/esphome/automation.py b/esphome/automation.py index 0bd6cf0af0..34159561c2 100644 --- a/esphome/automation.py +++ b/esphome/automation.py @@ -1,6 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( + CONF_ALL, + CONF_ANY, CONF_AUTOMATION_ID, CONF_CONDITION, CONF_COUNT, @@ -73,6 +75,13 @@ def validate_potentially_and_condition(value): return validate_condition(value) +def validate_potentially_or_condition(value): + if isinstance(value, list): + with cv.remove_prepend_path(["or"]): + return validate_condition({"or": value}) + return validate_condition(value) + + DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component) LambdaAction = cg.esphome_ns.class_("LambdaAction", Action) IfAction = cg.esphome_ns.class_("IfAction", Action) @@ -166,6 +175,18 @@ async def or_condition_to_code(config, condition_id, template_arg, args): return cg.new_Pvariable(condition_id, template_arg, conditions) +@register_condition("all", AndCondition, validate_condition_list) +async def all_condition_to_code(config, condition_id, template_arg, args): + conditions = await build_condition_list(config, template_arg, args) + return cg.new_Pvariable(condition_id, template_arg, conditions) + + +@register_condition("any", OrCondition, validate_condition_list) +async def any_condition_to_code(config, condition_id, template_arg, args): + conditions = await build_condition_list(config, template_arg, args) + return cg.new_Pvariable(condition_id, template_arg, conditions) + + @register_condition("not", NotCondition, validate_potentially_and_condition) async def not_condition_to_code(config, condition_id, template_arg, args): condition = await build_condition(config, template_arg, args) @@ -223,15 +244,21 @@ async def delay_action_to_code(config, action_id, template_arg, args): IfAction, cv.All( { - cv.Required(CONF_CONDITION): validate_potentially_and_condition, + cv.Exclusive( + CONF_CONDITION, CONF_CONDITION + ): validate_potentially_and_condition, + cv.Exclusive(CONF_ANY, CONF_CONDITION): validate_potentially_or_condition, + cv.Exclusive(CONF_ALL, CONF_CONDITION): validate_potentially_and_condition, cv.Optional(CONF_THEN): validate_action_list, cv.Optional(CONF_ELSE): validate_action_list, }, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE), + cv.has_at_least_one_key(CONF_CONDITION, CONF_ANY, CONF_ALL), ), ) async def if_action_to_code(config, action_id, template_arg, args): - conditions = await build_condition(config[CONF_CONDITION], template_arg, args) + cond_conf = next(el for el in config if el in (CONF_ANY, CONF_ALL, CONF_CONDITION)) + conditions = await build_condition(config[cond_conf], template_arg, args) var = cg.new_Pvariable(action_id, template_arg, conditions) if CONF_THEN in config: actions = await build_action_list(config[CONF_THEN], template_arg, args) diff --git a/esphome/const.py b/esphome/const.py index e02a1506cb..7665c77a32 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -49,6 +49,7 @@ CONF_ADDRESS = "address" CONF_ADDRESSABLE_LIGHT_ID = "addressable_light_id" CONF_ADVANCED = "advanced" CONF_AFTER = "after" +CONF_ALL = "all" CONF_ALLOW_OTHER_USES = "allow_other_uses" CONF_ALPHA = "alpha" CONF_ALTITUDE = "altitude" @@ -57,6 +58,7 @@ CONF_AMMONIA = "ammonia" CONF_ANALOG = "analog" CONF_AND = "and" CONF_ANGLE = "angle" +CONF_ANY = "any" CONF_AP = "ap" CONF_APPARENT_POWER = "apparent_power" CONF_ARDUINO_VERSION = "arduino_version" From fb002ac3b0a1e2c31456461daba895807d10eb83 Mon Sep 17 00:00:00 2001 From: Seth Girvan Date: Tue, 15 Oct 2024 20:24:37 -0700 Subject: [PATCH 0484/1052] Add TC74 temperature sensor (#7460) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/tc74/__init__.py | 1 + esphome/components/tc74/sensor.py | 32 +++++++++ esphome/components/tc74/tc74.cpp | 68 ++++++++++++++++++++ esphome/components/tc74/tc74.h | 28 ++++++++ tests/components/tc74/test.esp32-ard.yaml | 8 +++ tests/components/tc74/test.esp32-c3-ard.yaml | 8 +++ tests/components/tc74/test.esp32-c3-idf.yaml | 8 +++ tests/components/tc74/test.esp32-idf.yaml | 8 +++ tests/components/tc74/test.esp8266-ard.yaml | 8 +++ tests/components/tc74/test.rp2040-ard.yaml | 8 +++ 11 files changed, 178 insertions(+) create mode 100644 esphome/components/tc74/__init__.py create mode 100644 esphome/components/tc74/sensor.py create mode 100644 esphome/components/tc74/tc74.cpp create mode 100644 esphome/components/tc74/tc74.h create mode 100644 tests/components/tc74/test.esp32-ard.yaml create mode 100644 tests/components/tc74/test.esp32-c3-ard.yaml create mode 100644 tests/components/tc74/test.esp32-c3-idf.yaml create mode 100644 tests/components/tc74/test.esp32-idf.yaml create mode 100644 tests/components/tc74/test.esp8266-ard.yaml create mode 100644 tests/components/tc74/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index ed9c13a975..a19f3d75ed 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -403,6 +403,7 @@ esphome/components/sun/* @OttoWinter esphome/components/sun_gtil2/* @Mat931 esphome/components/switch/* @esphome/core esphome/components/t6615/* @tylermenezes +esphome/components/tc74/* @sethgirvan esphome/components/tca9548a/* @andreashergert1984 esphome/components/tca9555/* @mobrembski esphome/components/tcl112/* @glmnet diff --git a/esphome/components/tc74/__init__.py b/esphome/components/tc74/__init__.py new file mode 100644 index 0000000000..c1407c7cad --- /dev/null +++ b/esphome/components/tc74/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@sethgirvan"] diff --git a/esphome/components/tc74/sensor.py b/esphome/components/tc74/sensor.py new file mode 100644 index 0000000000..18fc2d9a42 --- /dev/null +++ b/esphome/components/tc74/sensor.py @@ -0,0 +1,32 @@ +import esphome.codegen as cg +from esphome.components import i2c, sensor +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + UNIT_CELSIUS, +) + +CODEOWNERS = ["@sethgirvan"] +DEPENDENCIES = ["i2c"] + +tc74_ns = cg.esphome_ns.namespace("tc74") +TC74Component = tc74_ns.class_("TC74Component", cg.PollingComponent, i2c.I2CDevice) + +CONFIG_SCHEMA = ( + sensor.sensor_schema( + TC74Component, + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x48)) +) + + +async def to_code(config): + var = await sensor.new_sensor(config) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) diff --git a/esphome/components/tc74/tc74.cpp b/esphome/components/tc74/tc74.cpp new file mode 100644 index 0000000000..2acb71f921 --- /dev/null +++ b/esphome/components/tc74/tc74.cpp @@ -0,0 +1,68 @@ +// Based on the TC74 datasheet https://ww1.microchip.com/downloads/en/DeviceDoc/21462D.pdf + +#include "tc74.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace tc74 { + +static const char *const TAG = "tc74"; + +static const uint8_t TC74_REGISTER_TEMPERATURE = 0x00; +static const uint8_t TC74_REGISTER_CONFIGURATION = 0x01; +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..."); + uint8_t config_reg; + if (this->read_register(TC74_REGISTER_CONFIGURATION, &config_reg, 1) != i2c::ERROR_OK) { + this->mark_failed(); + return; + } + this->data_ready_ = config_reg & TC74_DATA_READY_MASK; +} + +void TC74Component::update() { this->read_temperature_(); } + +void TC74Component::dump_config() { + LOG_SENSOR("", "TC74", this); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Connection with TC74 failed!"); + } + LOG_UPDATE_INTERVAL(this); +} + +void TC74Component::read_temperature_() { + if (!this->data_ready_) { + uint8_t config_reg; + if (this->read_register(TC74_REGISTER_CONFIGURATION, &config_reg, 1) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + + if (config_reg & TC74_DATA_READY_MASK) { + this->data_ready_ = true; + } else { + ESP_LOGD(TAG, "TC74 not ready"); + return; + } + } + + uint8_t temperature_reg; + if (this->read_register(TC74_REGISTER_TEMPERATURE, &temperature_reg, 1) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + + ESP_LOGD(TAG, "Got Temperature=%d °C", temperature_reg); + this->publish_state(temperature_reg); + this->status_clear_warning(); +} + +float TC74Component::get_setup_priority() const { return setup_priority::DATA; } + +} // namespace tc74 +} // namespace esphome diff --git a/esphome/components/tc74/tc74.h b/esphome/components/tc74/tc74.h new file mode 100644 index 0000000000..5d70c05420 --- /dev/null +++ b/esphome/components/tc74/tc74.h @@ -0,0 +1,28 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace tc74 { + +class TC74Component : public PollingComponent, public i2c::I2CDevice, public sensor::Sensor { + public: + /// Setup the sensor and check connection. + void setup() override; + void dump_config() override; + /// Update the sensor value (temperature). + void update() override; + + float get_setup_priority() const override; + + protected: + /// Internal method to read the temperature from the component after it has been scheduled. + void read_temperature_(); + + bool data_ready_ = false; +}; + +} // namespace tc74 +} // namespace esphome diff --git a/tests/components/tc74/test.esp32-ard.yaml b/tests/components/tc74/test.esp32-ard.yaml new file mode 100644 index 0000000000..ef9b40e184 --- /dev/null +++ b/tests/components/tc74/test.esp32-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tc74 + scl: 16 + sda: 17 + +sensor: + - platform: tc74 + name: TC74 Temperature diff --git a/tests/components/tc74/test.esp32-c3-ard.yaml b/tests/components/tc74/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..e1a373fbf4 --- /dev/null +++ b/tests/components/tc74/test.esp32-c3-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tc74 + scl: 5 + sda: 4 + +sensor: + - platform: tc74 + name: TC74 Temperature diff --git a/tests/components/tc74/test.esp32-c3-idf.yaml b/tests/components/tc74/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..e1a373fbf4 --- /dev/null +++ b/tests/components/tc74/test.esp32-c3-idf.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tc74 + scl: 5 + sda: 4 + +sensor: + - platform: tc74 + name: TC74 Temperature diff --git a/tests/components/tc74/test.esp32-idf.yaml b/tests/components/tc74/test.esp32-idf.yaml new file mode 100644 index 0000000000..ef9b40e184 --- /dev/null +++ b/tests/components/tc74/test.esp32-idf.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tc74 + scl: 16 + sda: 17 + +sensor: + - platform: tc74 + name: TC74 Temperature diff --git a/tests/components/tc74/test.esp8266-ard.yaml b/tests/components/tc74/test.esp8266-ard.yaml new file mode 100644 index 0000000000..e1a373fbf4 --- /dev/null +++ b/tests/components/tc74/test.esp8266-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tc74 + scl: 5 + sda: 4 + +sensor: + - platform: tc74 + name: TC74 Temperature diff --git a/tests/components/tc74/test.rp2040-ard.yaml b/tests/components/tc74/test.rp2040-ard.yaml new file mode 100644 index 0000000000..e1a373fbf4 --- /dev/null +++ b/tests/components/tc74/test.rp2040-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tc74 + scl: 5 + sda: 4 + +sensor: + - platform: tc74 + name: TC74 Temperature From 3ef31e55ca8b55252ad0fa7c342e94c6458ce767 Mon Sep 17 00:00:00 2001 From: Aleksandr Artemev <40710570+artemyevav@users.noreply.github.com> Date: Wed, 16 Oct 2024 05:25:05 +0200 Subject: [PATCH 0485/1052] [display] filled_ring and filled_gauge methods added (#7420) --- esphome/components/display/display.cpp | 142 +++++++++++++++++++++++++ esphome/components/display/display.h | 7 ++ tests/components/display/common.yaml | 4 + 3 files changed, 153 insertions(+) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 63c74e09ca..145a4f5278 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -156,6 +156,148 @@ void Display::filled_circle(int center_x, int center_y, int radius, Color color) } } while (dx <= 0); } +void Display::filled_ring(int center_x, int center_y, int radius1, int radius2, Color color) { + int rmax = radius1 > radius2 ? radius1 : radius2; + int rmin = radius1 < radius2 ? radius1 : radius2; + int dxmax = -int32_t(rmax), dxmin = -int32_t(rmin); + int dymax = 0, dymin = 0; + int errmax = 2 - 2 * rmax, errmin = 2 - 2 * rmin; + int e2max, e2min; + do { + // 8 dots for borders + this->draw_pixel_at(center_x - dxmax, center_y + dymax, color); + this->draw_pixel_at(center_x + dxmax, center_y + dymax, color); + this->draw_pixel_at(center_x - dxmin, center_y + dymin, color); + this->draw_pixel_at(center_x + dxmin, center_y + dymin, color); + this->draw_pixel_at(center_x + dxmax, center_y - dymax, color); + this->draw_pixel_at(center_x - dxmax, center_y - dymax, color); + this->draw_pixel_at(center_x + dxmin, center_y - dymin, color); + this->draw_pixel_at(center_x - dxmin, center_y - dymin, color); + if (dymin < rmin) { + // two parts - four lines + int hline_width = -(dxmax - dxmin) + 1; + this->horizontal_line(center_x + dxmax, center_y + dymax, hline_width, color); + this->horizontal_line(center_x - dxmin, center_y + dymax, hline_width, color); + this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color); + this->horizontal_line(center_x - dxmin, center_y - dymax, hline_width, color); + } else { + // one part - top and bottom + int hline_width = 2 * (-dxmax) + 1; + this->horizontal_line(center_x + dxmax, center_y + dymax, hline_width, color); + this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color); + } + e2max = errmax; + // tune external + if (e2max < dymax) { + errmax += ++dymax * 2 + 1; + if (-dxmax == dymax && e2max <= dxmax) { + e2max = 0; + } + } + if (e2max > dxmax) { + errmax += ++dxmax * 2 + 1; + } + // tune internal + while (dymin < dymax && dymin < rmin) { + e2min = errmin; + if (e2min < dymin) { + errmin += ++dymin * 2 + 1; + if (-dxmin == dymin && e2min <= dxmin) { + e2min = 0; + } + } + if (e2min > dxmin) { + errmin += ++dxmin * 2 + 1; + } + } + } while (dxmax <= 0); +} +void Display::filled_gauge(int center_x, int center_y, int radius1, int radius2, int progress, Color color) { + int rmax = radius1 > radius2 ? radius1 : radius2; + int rmin = radius1 < radius2 ? radius1 : radius2; + int dxmax = -int32_t(rmax), dxmin = -int32_t(rmin), upd_dxmax, upd_dxmin; + int dymax = 0, dymin = 0; + int errmax = 2 - 2 * rmax, errmin = 2 - 2 * rmin; + int e2max, e2min; + progress = std::max(0, std::min(progress, 100)); // 0..100 + int draw_progress = progress > 50 ? (100 - progress) : progress; + float tan_a = (progress == 50) ? 65535 : tan(float(draw_progress) * M_PI / 100); // slope + + do { + // outer dots + this->draw_pixel_at(center_x + dxmax, center_y - dymax, color); + this->draw_pixel_at(center_x - dxmax, center_y - dymax, color); + if (dymin < rmin) { // side parts + int lhline_width = -(dxmax - dxmin) + 1; + if (progress >= 50) { + if (float(dymax) < float(-dxmax) * tan_a) { + upd_dxmax = ceil(float(dymax) / tan_a); + } else { + upd_dxmax = -dxmax; + } + this->horizontal_line(center_x + dxmax, center_y - dymax, lhline_width, color); // left + if (!dymax) + this->horizontal_line(center_x - dxmin, center_y, lhline_width, color); // right horizontal border + if (upd_dxmax > -dxmin) { // right + int rhline_width = (upd_dxmax + dxmin) + 1; + this->horizontal_line(center_x - dxmin, center_y - dymax, + rhline_width > lhline_width ? lhline_width : rhline_width, color); + } + } else { + if (float(dymin) > float(-dxmin) * tan_a) { + upd_dxmin = ceil(float(dymin) / tan_a); + } else { + upd_dxmin = -dxmin; + } + lhline_width = -(dxmax + upd_dxmin) + 1; + if (!dymax) + this->horizontal_line(center_x - dxmin, center_y, lhline_width, color); // right horizontal border + if (lhline_width > 0) + this->horizontal_line(center_x + dxmax, center_y - dymax, lhline_width, color); + } + } else { // top part + int hline_width = 2 * (-dxmax) + 1; + if (progress >= 50) { + if (dymax < float(-dxmax) * tan_a) { + upd_dxmax = ceil(float(dymax) / tan_a); + hline_width = -dxmax + upd_dxmax + 1; + } + } else { + if (dymax < float(-dxmax) * tan_a) { + upd_dxmax = ceil(float(dymax) / tan_a); + hline_width = -dxmax - upd_dxmax + 1; + } else + hline_width = 0; + } + if (hline_width > 0) + this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color); + } + e2max = errmax; + if (e2max < dymax) { + errmax += ++dymax * 2 + 1; + if (-dxmax == dymax && e2max <= dxmax) { + e2max = 0; + } + } + if (e2max > dxmax) { + errmax += ++dxmax * 2 + 1; + } + while (dymin <= dymax && dymin <= rmin && dxmin <= 0) { + this->draw_pixel_at(center_x + dxmin, center_y - dymin, color); + this->draw_pixel_at(center_x - dxmin, center_y - dymin, color); + e2min = errmin; + if (e2min < dymin) { + errmin += ++dymin * 2 + 1; + if (-dxmin == dymin && e2min <= dxmin) { + e2min = 0; + } + } + if (e2min > dxmin) { + errmin += ++dxmin * 2 + 1; + } + } + } while (dxmax <= 0); +} void HOT Display::triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color) { this->line(x1, y1, x2, y2, color); this->line(x1, y1, x3, y3, color); diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 34feafea6e..54e897cdec 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -285,6 +285,13 @@ class Display : public PollingComponent { /// Fill a circle centered around [center_x,center_y] with the radius radius with the given color. void filled_circle(int center_x, int center_y, int radius, Color color = COLOR_ON); + /// Fill a ring centered around [center_x,center_y] between two circles with the radius1 and radius2 with the given + /// color. + void filled_ring(int center_x, int center_y, int radius1, int radius2, Color color = COLOR_ON); + /// Fill a half-ring "gauge" centered around [center_x,center_y] between two circles with the radius1 and radius2 + /// with he given color and filled up to 'progress' percent + void filled_gauge(int center_x, int center_y, int radius1, int radius2, int progress, Color color = COLOR_ON); + /// Draw the outline of a triangle contained between the points [x1,y1], [x2,y2] and [x3,y3] with the given color. void triangle(int x1, int y1, int x2, int y2, int x3, int y3, Color color = COLOR_ON); diff --git a/tests/components/display/common.yaml b/tests/components/display/common.yaml index 1df2665067..4fc4fafa25 100644 --- a/tests/components/display/common.yaml +++ b/tests/components/display/common.yaml @@ -34,3 +34,7 @@ display: it.line_at_angle(centerX, centerY, minuteAngle, radius - 5, radius); } + + // Nice ring around and some gauge + it.filled_ring(centerX, centerY, radius+5, radius+8); + it.filled_gauge(centerX, centerY, radius/2, radius/2-5, 66); From b274d6901a8542b10262361fb3e5e2ff27959a10 Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Wed, 16 Oct 2024 06:25:47 +0300 Subject: [PATCH 0486/1052] [fix] deprecated functions warnings for logger component with ESP IDF version 5.3.0+ (#7600) --- esphome/components/logger/logger_esp32.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/esphome/components/logger/logger_esp32.cpp b/esphome/components/logger/logger_esp32.cpp index b0f1051d34..c9de3d815a 100644 --- a/esphome/components/logger/logger_esp32.cpp +++ b/esphome/components/logger/logger_esp32.cpp @@ -10,8 +10,12 @@ #ifdef USE_LOGGER_USB_SERIAL_JTAG #include +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) #include #include +#else +#include +#endif #endif #include "freertos/FreeRTOS.h" @@ -36,10 +40,17 @@ static const char *const TAG = "logger"; static void init_usb_serial_jtag_() { setvbuf(stdin, NULL, _IONBF, 0); // Disable buffering on stdin +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) // Minicom, screen, idf_monitor send CR when ENTER key is pressed esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR); // Move the caret to the beginning of the next line on '\n' esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); +#else + // Minicom, screen, idf_monitor send CR when ENTER key is pressed + usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_CR); + // Move the caret to the beginning of the next line on '\n' + usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); +#endif // Enable non-blocking mode on stdin and stdout fcntl(fileno(stdout), F_SETFL, 0); @@ -57,7 +68,11 @@ static void init_usb_serial_jtag_() { } // Tell vfs to use usb-serial-jtag driver +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) esp_vfs_usb_serial_jtag_use_driver(); +#else + usb_serial_jtag_vfs_use_driver(); +#endif } #endif From 6a86d92781357a03c49dbdbb68953fcc2b04b933 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:26:06 +1100 Subject: [PATCH 0487/1052] [lvgl] Implement better software rotation (#7595) --- esphome/components/lvgl/__init__.py | 25 ++- esphome/components/lvgl/defines.py | 4 + esphome/components/lvgl/lvgl_esphome.cpp | 176 +++++++++++++++------- esphome/components/lvgl/lvgl_esphome.h | 47 +++++- esphome/components/lvgl/types.py | 1 + tests/components/lvgl/lvgl-package.yaml | 5 + tests/components/lvgl/test.esp32-ard.yaml | 1 + 7 files changed, 201 insertions(+), 58 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index ce3843567b..dea3b11a94 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -48,6 +48,7 @@ from .types import ( FontEngine, IdleTrigger, ObjUpdateAction, + PauseTrigger, lv_font_t, lv_group_t, lv_style_t, @@ -233,6 +234,8 @@ async def to_code(config): frac = 8 cg.add(lv_component.set_buffer_frac(int(frac))) cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH])) + cg.add(lv_component.set_draw_rounding(config[df.CONF_DRAW_ROUNDING])) + cg.add(lv_component.set_resume_on_input(config[df.CONF_RESUME_ON_INPUT])) for font in helpers.esphome_fonts_used: await cg.get_variable(font) @@ -272,11 +275,19 @@ async def to_code(config): async with LvContext(lv_component): await generate_triggers(lv_component) await generate_page_triggers(lv_component, config) + await initial_focus_to_code(config) for conf in config.get(CONF_ON_IDLE, ()): templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ) await build_automation(idle_trigger, [], conf) - await initial_focus_to_code(config) + for conf in config.get(df.CONF_ON_PAUSE, ()): + pause_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, True) + await build_automation(pause_trigger, [], conf) + for conf in config.get(df.CONF_ON_RESUME, ()): + resume_trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], lv_component, False + ) + await build_automation(resume_trigger, [], conf) for comp in helpers.lvgl_components_required: CORE.add_define(f"USE_LVGL_{comp.upper()}") @@ -314,6 +325,7 @@ CONFIG_SCHEMA = ( cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16), cv.Optional(df.CONF_DEFAULT_FONT, default="montserrat_14"): lvalid.lv_font, cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean, + cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int, cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage, cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of( *df.LOG_LEVELS, upper=True @@ -341,6 +353,16 @@ CONFIG_SCHEMA = ( ), } ), + cv.Optional(df.CONF_ON_PAUSE): validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), + } + ), + cv.Optional(df.CONF_ON_RESUME): validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), + } + ), cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list(WIDGET_SCHEMA), cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list( container_schema(page_spec) @@ -356,6 +378,7 @@ CONFIG_SCHEMA = ( cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t), + cv.Optional(df.CONF_RESUME_ON_INPUT, default=True): cv.boolean, } ) .extend(DISP_BG_SCHEMA) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 02f726e49c..7c42ed2f22 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -408,6 +408,7 @@ CONF_DEFAULT_FONT = "default_font" CONF_DEFAULT_GROUP = "default_group" CONF_DIR = "dir" CONF_DISPLAYS = "displays" +CONF_DRAW_ROUNDING = "draw_rounding" CONF_EDITING = "editing" CONF_ENCODERS = "encoders" CONF_END_ANGLE = "end_angle" @@ -451,6 +452,8 @@ CONF_OFFSET_X = "offset_x" CONF_OFFSET_Y = "offset_y" CONF_ONE_CHECKED = "one_checked" CONF_ONE_LINE = "one_line" +CONF_ON_PAUSE = "on_pause" +CONF_ON_RESUME = "on_resume" CONF_ON_SELECT = "on_select" CONF_OPA = "opa" CONF_NEXT = "next" @@ -466,6 +469,7 @@ CONF_POINTS = "points" CONF_PREVIOUS = "previous" CONF_REPEAT_COUNT = "repeat_count" CONF_RECOLOR = "recolor" +CONF_RESUME_ON_INPUT = "resume_on_input" CONF_RIGHT_BUTTON = "right_button" CONF_ROLLOVER = "rollover" CONF_ROOT_BACK_BTN = "root_back_btn" diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index b63fb0dab8..ddf41ae377 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -69,30 +69,38 @@ std::string lv_event_code_name_for(uint8_t event_code) { } return str_sprintf("%2d", event_code); } + static void rounder_cb(lv_disp_drv_t *disp_drv, lv_area_t *area) { - // make sure all coordinates are even - if (area->x1 & 1) - area->x1--; - if (!(area->x2 & 1)) - area->x2++; - if (area->y1 & 1) - area->y1--; - if (!(area->y2 & 1)) - area->y2++; + // cater for display driver chips with special requirements for bounds of partial + // draw areas. Extend the draw area to satisfy: + // * Coordinates must be a multiple of draw_rounding + auto *comp = static_cast(disp_drv->user_data); + auto draw_rounding = comp->draw_rounding; + // round down the start coordinates + area->x1 = area->x1 / draw_rounding * draw_rounding; + area->y1 = area->y1 / draw_rounding * draw_rounding; + // round up the end coordinates + area->x2 = (area->x2 + draw_rounding) / draw_rounding * draw_rounding - 1; + area->y2 = (area->y2 + draw_rounding) / draw_rounding * draw_rounding - 1; } lv_event_code_t lv_api_event; // NOLINT lv_event_code_t lv_update_event; // NOLINT -void LvglComponent::dump_config() { ESP_LOGCONFIG(TAG, "LVGL:"); } +void LvglComponent::dump_config() { + ESP_LOGCONFIG(TAG, "LVGL:"); + ESP_LOGCONFIG(TAG, " Rotation: %d", this->rotation); + ESP_LOGCONFIG(TAG, " Draw rounding: %d", (int) this->draw_rounding); +} void LvglComponent::set_paused(bool paused, bool show_snow) { this->paused_ = paused; this->show_snow_ = show_snow; - this->snow_line_ = 0; if (!paused && lv_scr_act() != nullptr) { lv_disp_trig_activity(this->disp_); // resets the inactivity time lv_obj_invalidate(lv_scr_act()); } + this->pause_callbacks_.call(paused); } + void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) { lv_obj_add_event_cb(obj, callback, event, this); } @@ -133,19 +141,64 @@ void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) { } while (this->pages_[this->current_page_]->skip); // skip empty pages() this->show_page(this->current_page_, anim, time); } -void LvglComponent::draw_buffer_(const lv_area_t *area, const uint8_t *ptr) { +void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) { + auto width = lv_area_get_width(area); + auto height = lv_area_get_height(area); + auto x1 = area->x1; + auto y1 = area->y1; + lv_color_t *dst = this->rotate_buf_; + switch (this->rotation) { + case display::DISPLAY_ROTATION_90_DEGREES: + for (lv_coord_t x = height - 1; x-- != 0;) { + for (lv_coord_t y = 0; y != width; y++) { + dst[y * height + x] = *ptr++; + } + } + y1 = x1; + x1 = this->disp_drv_.ver_res - area->y1 - height; + width = height; + height = lv_area_get_width(area); + break; + + case display::DISPLAY_ROTATION_180_DEGREES: + for (lv_coord_t y = height; y-- != 0;) { + for (lv_coord_t x = width; x-- != 0;) { + dst[y * width + x] = *ptr++; + } + } + x1 = this->disp_drv_.hor_res - x1 - width; + y1 = this->disp_drv_.ver_res - y1 - height; + break; + + case display::DISPLAY_ROTATION_270_DEGREES: + for (lv_coord_t x = 0; x != height; x++) { + for (lv_coord_t y = width; y-- != 0;) { + dst[y * height + x] = *ptr++; + } + } + x1 = y1; + y1 = this->disp_drv_.hor_res - area->x1 - width; + width = height; + height = lv_area_get_width(area); + break; + + default: + dst = ptr; + break; + } for (auto *display : this->displays_) { - display->draw_pixels_at(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), ptr, - display::COLOR_ORDER_RGB, LV_BITNESS, LV_COLOR_16_SWAP); + ESP_LOGV(TAG, "draw buffer x1=%d, y1=%d, width=%d, height=%d", x1, y1, width, height); + display->draw_pixels_at(x1, y1, width, height, (const uint8_t *) dst, display::COLOR_ORDER_RGB, LV_BITNESS, + LV_COLOR_16_SWAP); } } void LvglComponent::flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) { if (!this->paused_) { auto now = millis(); - this->draw_buffer_(area, (const uint8_t *) color_p); - ESP_LOGV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area), - lv_area_get_height(area), (int) (millis() - now)); + this->draw_buffer_(area, color_p); + ESP_LOGVV(TAG, "flush_cb, area=%d/%d, %d/%d took %dms", area->x1, area->y1, lv_area_get_width(area), + lv_area_get_height(area), (int) (millis() - now)); } lv_disp_flush_ready(disp_drv); } @@ -160,6 +213,13 @@ IdleTrigger::IdleTrigger(LvglComponent *parent, TemplatableValue timeo }); } +PauseTrigger::PauseTrigger(LvglComponent *parent, TemplatableValue paused) : paused_(std::move(paused)) { + parent->add_on_pause_callback([this](bool pausing) { + if (this->paused_.value() == pausing) + this->trigger(); + }); +} + #ifdef USE_LVGL_TOUCHSCREEN LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) { lv_indev_drv_init(&this->drv_); @@ -261,23 +321,31 @@ void LvKeyboardType::set_obj(lv_obj_t *lv_obj) { #endif // USE_LVGL_KEYBOARD void LvglComponent::write_random_() { - // length of 2 lines in 32 bit units - // we write 2 lines for the benefit of displays that won't write one line at a time. - size_t line_len = this->disp_drv_.hor_res * LV_COLOR_DEPTH / 8 / 4 * 2; - for (size_t i = 0; i != line_len; i++) { - ((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32(); + int iterations = 6 - lv_disp_get_inactive_time(this->disp_) / 60000; + if (iterations <= 0) + iterations = 1; + while (iterations-- != 0) { + auto col = random_uint32() % this->disp_drv_.hor_res; + col = col / this->draw_rounding * this->draw_rounding; + auto row = random_uint32() % this->disp_drv_.ver_res; + row = row / this->draw_rounding * this->draw_rounding; + auto size = (random_uint32() % 32) / this->draw_rounding * this->draw_rounding - 1; + lv_area_t area; + area.x1 = col; + area.y1 = row; + area.x2 = col + size; + area.y2 = row + size; + if (area.x2 >= this->disp_drv_.hor_res) + area.x2 = this->disp_drv_.hor_res - 1; + if (area.y2 >= this->disp_drv_.ver_res) + area.y2 = this->disp_drv_.ver_res - 1; + + size_t line_len = lv_area_get_width(&area) * lv_area_get_height(&area) / 2; + for (size_t i = 0; i != line_len; i++) { + ((uint32_t *) (this->draw_buf_.buf1))[i] = random_uint32(); + } + this->draw_buffer_(&area, (lv_color_t *) this->draw_buf_.buf1); } - lv_area_t area; - area.x1 = 0; - area.x2 = this->disp_drv_.hor_res - 1; - if (this->snow_line_ == this->disp_drv_.ver_res / 2) { - area.y1 = static_cast(random_uint32() % (this->disp_drv_.ver_res / 2) * 2); - } else { - area.y1 = this->snow_line_++ * 2; - } - // write 2 lines - area.y2 = area.y1 + 1; - this->draw_buffer_(&area, (const uint8_t *) this->draw_buf_.buf1); } void LvglComponent::setup() { @@ -291,7 +359,7 @@ void LvglComponent::setup() { auto *display = this->displays_[0]; size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_; auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; - auto *buf = lv_custom_mem_alloc(buf_bytes); + auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT if (buf == nullptr) { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes); @@ -307,26 +375,30 @@ void LvglComponent::setup() { this->disp_drv_.full_refresh = this->full_refresh_; this->disp_drv_.flush_cb = static_flush_cb; this->disp_drv_.rounder_cb = rounder_cb; - switch (display->get_rotation()) { - case display::DISPLAY_ROTATION_0_DEGREES: - break; - case display::DISPLAY_ROTATION_90_DEGREES: - this->disp_drv_.sw_rotate = true; - this->disp_drv_.rotated = LV_DISP_ROT_90; - break; - case display::DISPLAY_ROTATION_180_DEGREES: - this->disp_drv_.sw_rotate = true; - this->disp_drv_.rotated = LV_DISP_ROT_180; - break; - case display::DISPLAY_ROTATION_270_DEGREES: - this->disp_drv_.sw_rotate = true; - this->disp_drv_.rotated = LV_DISP_ROT_270; - break; + this->rotation = display->get_rotation(); + 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) { +#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR + ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes); +#endif + this->mark_failed(); + this->status_set_error("Memory allocation failure"); + return; + } } display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES); - this->disp_drv_.hor_res = (lv_coord_t) display->get_width(); - this->disp_drv_.ver_res = (lv_coord_t) display->get_height(); - ESP_LOGV(TAG, "sw_rotate = %d, rotated=%d", this->disp_drv_.sw_rotate, this->disp_drv_.rotated); + switch (this->rotation) { + default: + this->disp_drv_.hor_res = (lv_coord_t) display->get_width(); + this->disp_drv_.ver_res = (lv_coord_t) display->get_height(); + break; + case display::DISPLAY_ROTATION_90_DEGREES: + case display::DISPLAY_ROTATION_270_DEGREES: + this->disp_drv_.ver_res = (lv_coord_t) display->get_width(); + this->disp_drv_.hor_res = (lv_coord_t) display->get_height(); + break; + } this->disp_ = lv_disp_drv_register(&this->disp_drv_); for (const auto &v : this->init_lambdas_) v(this); diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 0c3738bd1f..b28a9bcbe1 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -119,6 +119,7 @@ class LvglComponent : public PollingComponent { void add_on_idle_callback(std::function &&callback) { this->idle_callbacks_.add(std::move(callback)); } + void add_on_pause_callback(std::function &&callback) { this->pause_callbacks_.add(std::move(callback)); } void add_display(display::Display *display) { this->displays_.push_back(display); } void add_init_lambda(const std::function &lamb) { this->init_lambdas_.push_back(lamb); } void dump_config() override; @@ -126,12 +127,22 @@ class LvglComponent : public PollingComponent { bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; } void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; } lv_disp_t *get_disp() { return this->disp_; } + // Pause or resume the display. + // @param paused If true, pause the display. If false, resume the display. + // @param show_snow If true, show the snow effect when paused. void set_paused(bool paused, bool show_snow); + bool is_paused() const { return this->paused_; } + // If the display is paused and we have resume_on_input_ set to true, resume the display. + void maybe_wakeup() { + if (this->paused_ && this->resume_on_input_) { + this->set_paused(false, false); + } + } + void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event); void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2); void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2, lv_event_code_t event3); - bool is_paused() const { return this->paused_; } void add_page(LvPageType *page); void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time); void show_next_page(lv_scr_load_anim_t anim, uint32_t time); @@ -144,10 +155,17 @@ class LvglComponent : public PollingComponent { lv_group_focus_obj(mark); } } + // rounding factor to align bounds of update area when drawing + size_t draw_rounding{2}; + void set_draw_rounding(size_t rounding) { this->draw_rounding = rounding; } + void set_resume_on_input(bool resume_on_input) { this->resume_on_input_ = resume_on_input; } + + // if set to true, the bounds of the update area will always start at 0,0 + display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES}; protected: void write_random_(); - void draw_buffer_(const lv_area_t *area, const uint8_t *ptr); + void draw_buffer_(const lv_area_t *area, lv_color_t *ptr); void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); std::vector displays_{}; lv_disp_draw_buf_t draw_buf_{}; @@ -157,14 +175,16 @@ class LvglComponent : public PollingComponent { std::vector pages_{}; size_t current_page_{0}; bool show_snow_{}; - lv_coord_t snow_line_{}; bool page_wrap_{true}; + bool resume_on_input_{}; std::map focus_marks_{}; std::vector> init_lambdas_; CallbackManager idle_callbacks_{}; + CallbackManager pause_callbacks_{}; size_t buffer_frac_{1}; bool full_refresh_{}; + lv_color_t *rotate_buf_{}; }; class IdleTrigger : public Trigger<> { @@ -176,6 +196,14 @@ class IdleTrigger : public Trigger<> { bool is_idle_{}; }; +class PauseTrigger : public Trigger<> { + public: + explicit PauseTrigger(LvglComponent *parent, TemplatableValue paused); + + protected: + TemplatableValue paused_; +}; + template class LvglAction : public Action, public Parented { public: explicit LvglAction(std::function &&lamb) : action_(std::move(lamb)) {} @@ -200,7 +228,10 @@ class LVTouchListener : public touchscreen::TouchListener, public Parentedparent_->maybe_wakeup(); + } lv_indev_drv_t *get_drv() { return &this->drv_; } protected: @@ -236,12 +267,18 @@ class LVEncoderListener : public Parented { if (!this->parent_->is_paused()) { this->pressed_ = pressed; this->key_ = key; + } else if (!pressed) { + // maybe wakeup on release if paused + this->parent_->maybe_wakeup(); } } void set_count(int32_t count) { - if (!this->parent_->is_paused()) + if (!this->parent_->is_paused()) { this->count_ = count; + } else { + this->parent_->maybe_wakeup(); + } } lv_indev_drv_t *get_drv() { return &this->drv_; } diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index b452ab5fb3..2d10b67c2d 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -40,6 +40,7 @@ lv_event_code_t = cg.global_ns.enum("lv_event_code_t") lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t") FontEngine = lvgl_ns.class_("FontEngine") IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template()) +PauseTrigger = lvgl_ns.class_("PauseTrigger", automation.Trigger.template()) ObjUpdateAction = lvgl_ns.class_("ObjUpdateAction", automation.Action) LvglCondition = lvgl_ns.class_("LvglCondition", automation.Condition) LvglAction = lvgl_ns.class_("LvglAction", automation.Action) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 1770c1bfbc..6aea606ac4 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -12,6 +12,11 @@ substitutions: arrow_down: "\U000F004B" lvgl: + resume_on_input: true + on_pause: + logger.log: LVGL is Paused + on_resume: + logger.log: LVGL has resumed log_level: TRACE bg_color: light_blue disp_bg_color: color_id diff --git a/tests/components/lvgl/test.esp32-ard.yaml b/tests/components/lvgl/test.esp32-ard.yaml index 51593e7967..80d5ce503f 100644 --- a/tests/components/lvgl/test.esp32-ard.yaml +++ b/tests/components/lvgl/test.esp32-ard.yaml @@ -44,6 +44,7 @@ binary_sensor: number: GPIO39 inverted: true lvgl: + draw_rounding: 8 encoders: group: switches initial_focus: button_button From 254522dd9341c9c6cbbb6903a42d787856d68e90 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:26:50 +1100 Subject: [PATCH 0488/1052] [qspi_dbi] Rename from qspi_amoled, add features (#7594) Co-authored-by: clydeps --- CODEOWNERS | 2 +- esphome/components/ili9xxx/display.py | 2 +- esphome/components/qspi_amoled/display.py | 142 +------------- .../{qspi_amoled => qspi_dbi}/__init__.py | 0 esphome/components/qspi_dbi/display.py | 185 ++++++++++++++++++ esphome/components/qspi_dbi/models.py | 64 ++++++ .../qspi_amoled.cpp => qspi_dbi/qspi_dbi.cpp} | 139 ++++++++----- .../qspi_amoled.h => qspi_dbi/qspi_dbi.h} | 41 ++-- esphome/components/st7701s/display.py | 2 +- esphome/const.py | 1 + .../{qspi_amoled => qspi_dbi}/common.yaml | 14 +- .../test.esp32-s3-idf.yaml | 0 12 files changed, 385 insertions(+), 207 deletions(-) rename esphome/components/{qspi_amoled => qspi_dbi}/__init__.py (100%) create mode 100644 esphome/components/qspi_dbi/display.py create mode 100644 esphome/components/qspi_dbi/models.py rename esphome/components/{qspi_amoled/qspi_amoled.cpp => qspi_dbi/qspi_dbi.cpp} (51%) rename esphome/components/{qspi_amoled/qspi_amoled.h => qspi_dbi/qspi_dbi.h} (80%) rename tests/components/{qspi_amoled => qspi_dbi}/common.yaml (73%) rename tests/components/{qspi_amoled => qspi_dbi}/test.esp32-s3-idf.yaml (100%) diff --git a/CODEOWNERS b/CODEOWNERS index a19f3d75ed..7dd18d2c51 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -324,7 +324,7 @@ esphome/components/pvvx_mithermometer/* @pasiz esphome/components/pylontech/* @functionpointer esphome/components/qmp6988/* @andrewpc esphome/components/qr_code/* @wjtje -esphome/components/qspi_amoled/* @clydebarrow +esphome/components/qspi_dbi/* @clydebarrow esphome/components/qwiic_pir/* @kahrendt esphome/components/radon_eye_ble/* @jeffeb3 esphome/components/radon_eye_rd200/* @jeffeb3 diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 2182ca9a6d..68e3aa953d 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -10,6 +10,7 @@ from esphome.const import ( CONF_DIMENSIONS, CONF_HEIGHT, CONF_ID, + CONF_INIT_SEQUENCE, CONF_INVERT_COLORS, CONF_LAMBDA, CONF_MIRROR_X, @@ -89,7 +90,6 @@ CONF_LED_PIN = "led_pin" CONF_COLOR_PALETTE_IMAGES = "color_palette_images" CONF_INVERT_DISPLAY = "invert_display" CONF_PIXEL_MODE = "pixel_mode" -CONF_INIT_SEQUENCE = "init_sequence" def cmd(c, *args): diff --git a/esphome/components/qspi_amoled/display.py b/esphome/components/qspi_amoled/display.py index 77d1e3d095..00773b516a 100644 --- a/esphome/components/qspi_amoled/display.py +++ b/esphome/components/qspi_amoled/display.py @@ -1,143 +1,3 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins -from esphome.components import ( - spi, - display, -) -from esphome.const import ( - CONF_RESET_PIN, - CONF_ID, - CONF_DIMENSIONS, - CONF_WIDTH, - CONF_HEIGHT, - CONF_LAMBDA, - CONF_BRIGHTNESS, - CONF_ENABLE_PIN, - CONF_MODEL, - CONF_OFFSET_HEIGHT, - CONF_OFFSET_WIDTH, - CONF_INVERT_COLORS, - CONF_MIRROR_X, - CONF_MIRROR_Y, - CONF_SWAP_XY, - CONF_COLOR_ORDER, - CONF_TRANSFORM, -) -DEPENDENCIES = ["spi"] - -qspi_amoled_ns = cg.esphome_ns.namespace("qspi_amoled") -QSPI_AMOLED = qspi_amoled_ns.class_( - "QspiAmoLed", display.Display, display.DisplayBuffer, cg.Component, spi.SPIDevice -) -ColorOrder = display.display_ns.enum("ColorMode") -Model = qspi_amoled_ns.enum("Model") - -MODELS = {"RM690B0": Model.RM690B0, "RM67162": Model.RM67162} - -COLOR_ORDERS = { - "RGB": ColorOrder.COLOR_ORDER_RGB, - "BGR": ColorOrder.COLOR_ORDER_BGR, -} -DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema - - -def validate_dimension(value): - value = cv.positive_int(value) - if value % 2 != 0: - raise cv.Invalid("Width/height/offset must be divisible by 2") - return value - - -CONFIG_SCHEMA = cv.All( - display.FULL_DISPLAY_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(QSPI_AMOLED), - cv.Required(CONF_MODEL): cv.enum(MODELS, upper=True), - cv.Required(CONF_DIMENSIONS): cv.Any( - cv.dimensions, - cv.Schema( - { - cv.Required(CONF_WIDTH): validate_dimension, - cv.Required(CONF_HEIGHT): validate_dimension, - cv.Optional( - CONF_OFFSET_HEIGHT, default=0 - ): validate_dimension, - cv.Optional( - CONF_OFFSET_WIDTH, default=0 - ): validate_dimension, - } - ), - ), - 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_SWAP_XY, default=False): cv.boolean, - } - ), - cv.Optional(CONF_COLOR_ORDER, default="RGB"): cv.enum( - COLOR_ORDERS, upper=True - ), - cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=0xD0): cv.int_range( - 0, 0xFF, min_included=True, max_included=True - ), - } - ).extend( - spi.spi_device_schema( - cs_pin_required=False, - default_mode="MODE0", - default_data_rate=10e6, - quad=True, - ) - ) - ), - cv.only_with_esp_idf, -) - - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await display.register_display(var, config) - await spi.register_spi_device(var, config) - - cg.add(var.set_color_mode(config[CONF_COLOR_ORDER])) - cg.add(var.set_invert_colors(config[CONF_INVERT_COLORS])) - cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) - cg.add(var.set_model(config[CONF_MODEL])) - if enable_pin := config.get(CONF_ENABLE_PIN): - enable = await cg.gpio_pin_expression(enable_pin) - cg.add(var.set_enable_pin(enable)) - - if reset_pin := config.get(CONF_RESET_PIN): - reset = await cg.gpio_pin_expression(reset_pin) - cg.add(var.set_reset_pin(reset)) - - 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_swap_xy(transform[CONF_SWAP_XY])) - - if CONF_DIMENSIONS in config: - dimensions = config[CONF_DIMENSIONS] - if isinstance(dimensions, dict): - cg.add(var.set_dimensions(dimensions[CONF_WIDTH], dimensions[CONF_HEIGHT])) - cg.add( - var.set_offsets( - dimensions[CONF_OFFSET_WIDTH], dimensions[CONF_OFFSET_HEIGHT] - ) - ) - else: - (width, height) = dimensions - cg.add(var.set_dimensions(width, height)) - - if lamb := config.get(CONF_LAMBDA): - lambda_ = await cg.process_lambda( - lamb, [(display.DisplayRef, "it")], return_type=cg.void - ) - cg.add(var.set_writer(lambda_)) +CONFIG_SCHEMA = cv.invalid("The qspi_amoled component has been renamed to qspi_dbi") diff --git a/esphome/components/qspi_amoled/__init__.py b/esphome/components/qspi_dbi/__init__.py similarity index 100% rename from esphome/components/qspi_amoled/__init__.py rename to esphome/components/qspi_dbi/__init__.py diff --git a/esphome/components/qspi_dbi/display.py b/esphome/components/qspi_dbi/display.py new file mode 100644 index 0000000000..71ae31f182 --- /dev/null +++ b/esphome/components/qspi_dbi/display.py @@ -0,0 +1,185 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components import display, spi +import esphome.config_validation as cv +from esphome.const import ( + CONF_BRIGHTNESS, + CONF_COLOR_ORDER, + CONF_DIMENSIONS, + CONF_ENABLE_PIN, + CONF_HEIGHT, + CONF_ID, + CONF_INIT_SEQUENCE, + CONF_INVERT_COLORS, + CONF_LAMBDA, + CONF_MIRROR_X, + CONF_MIRROR_Y, + CONF_MODEL, + CONF_OFFSET_HEIGHT, + CONF_OFFSET_WIDTH, + CONF_RESET_PIN, + CONF_SWAP_XY, + CONF_TRANSFORM, + CONF_WIDTH, +) +from esphome.core import TimePeriod + +from .models import DriverChip + +DEPENDENCIES = ["spi"] + +qspi_dbi_ns = cg.esphome_ns.namespace("qspi_dbi") +QSPI_DBI = qspi_dbi_ns.class_( + "QspiDbi", display.Display, display.DisplayBuffer, cg.Component, spi.SPIDevice +) +ColorOrder = display.display_ns.enum("ColorMode") +Model = qspi_dbi_ns.enum("Model") + +COLOR_ORDERS = { + "RGB": ColorOrder.COLOR_ORDER_RGB, + "BGR": ColorOrder.COLOR_ORDER_BGR, +} +DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema + +CONF_DRAW_FROM_ORIGIN = "draw_from_origin" +DELAY_FLAG = 0xFF + + +def validate_dimension(value): + value = cv.positive_int(value) + if value % 2 != 0: + raise cv.Invalid("Width/height/offset must be divisible by 2") + return value + + +def map_sequence(value): + """ + The format is a repeated sequence of [CMD, ] where is s a sequence of bytes. The length is inferred + from the length of the sequence and should not be explicit. + A delay can be inserted by specifying "- delay N" where N is in ms + """ + if isinstance(value, str) and value.lower().startswith("delay "): + value = value.lower()[6:] + delay = cv.All( + cv.positive_time_period_milliseconds, + cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)), + )(value) + return [delay, DELAY_FLAG] + value = cv.Length(min=1, max=254)(value) + params = value[1:] + return [value[0], len(params)] + list(params) + + +def _validate(config): + chip = DriverChip.chips[config[CONF_MODEL]] + if not chip.initsequence: + if CONF_INIT_SEQUENCE not in config: + raise cv.Invalid(f"{chip.name} model requires init_sequence") + return config + + +CONFIG_SCHEMA = cv.All( + display.FULL_DISPLAY_SCHEMA.extend( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(QSPI_DBI), + cv.Required(CONF_MODEL): cv.one_of( + *DriverChip.chips.keys(), upper=True + ), + cv.Optional(CONF_INIT_SEQUENCE): cv.ensure_list(map_sequence), + cv.Required(CONF_DIMENSIONS): cv.Any( + cv.dimensions, + cv.Schema( + { + cv.Required(CONF_WIDTH): validate_dimension, + cv.Required(CONF_HEIGHT): validate_dimension, + cv.Optional( + CONF_OFFSET_HEIGHT, default=0 + ): validate_dimension, + cv.Optional( + CONF_OFFSET_WIDTH, default=0 + ): validate_dimension, + } + ), + ), + 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_SWAP_XY, default=False): cv.boolean, + } + ), + cv.Optional(CONF_COLOR_ORDER, default="RGB"): cv.enum( + COLOR_ORDERS, upper=True + ), + cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=0xD0): cv.int_range( + 0, 0xFF, min_included=True, max_included=True + ), + cv.Optional(CONF_DRAW_FROM_ORIGIN, default=False): cv.boolean, + } + ).extend( + spi.spi_device_schema( + cs_pin_required=False, + default_mode="MODE0", + default_data_rate=10e6, + quad=True, + ) + ) + ), + cv.only_with_esp_idf, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await display.register_display(var, config) + await spi.register_spi_device(var, config) + + chip = DriverChip.chips[config[CONF_MODEL]] + if chip.initsequence: + cg.add(var.add_init_sequence(chip.initsequence)) + if init_sequences := config.get(CONF_INIT_SEQUENCE): + sequence = [] + for seq in init_sequences: + sequence.extend(seq) + cg.add(var.add_init_sequence(sequence)) + + cg.add(var.set_color_mode(config[CONF_COLOR_ORDER])) + cg.add(var.set_invert_colors(config[CONF_INVERT_COLORS])) + cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) + cg.add(var.set_model(config[CONF_MODEL])) + cg.add(var.set_draw_from_origin(config[CONF_DRAW_FROM_ORIGIN])) + if enable_pin := config.get(CONF_ENABLE_PIN): + enable = await cg.gpio_pin_expression(enable_pin) + cg.add(var.set_enable_pin(enable)) + + if reset_pin := config.get(CONF_RESET_PIN): + reset = await cg.gpio_pin_expression(reset_pin) + cg.add(var.set_reset_pin(reset)) + + 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_swap_xy(transform[CONF_SWAP_XY])) + + if CONF_DIMENSIONS in config: + dimensions = config[CONF_DIMENSIONS] + if isinstance(dimensions, dict): + cg.add(var.set_dimensions(dimensions[CONF_WIDTH], dimensions[CONF_HEIGHT])) + cg.add( + var.set_offsets( + dimensions[CONF_OFFSET_WIDTH], dimensions[CONF_OFFSET_HEIGHT] + ) + ) + else: + (width, height) = dimensions + cg.add(var.set_dimensions(width, height)) + + if lamb := config.get(CONF_LAMBDA): + lambda_ = await cg.process_lambda( + lamb, [(display.DisplayRef, "it")], return_type=cg.void + ) + cg.add(var.set_writer(lambda_)) diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py new file mode 100644 index 0000000000..071ea72d73 --- /dev/null +++ b/esphome/components/qspi_dbi/models.py @@ -0,0 +1,64 @@ +# Commands +SW_RESET_CMD = 0x01 +SLEEP_OUT = 0x11 +INVERT_OFF = 0x20 +INVERT_ON = 0x21 +ALL_ON = 0x23 +WRAM = 0x24 +MIPI = 0x26 +DISPLAY_OFF = 0x28 +DISPLAY_ON = 0x29 +RASET = 0x2B +CASET = 0x2A +WDATA = 0x2C +TEON = 0x35 +MADCTL_CMD = 0x36 +PIXFMT = 0x3A +BRIGHTNESS = 0x51 +SWIRE1 = 0x5A +SWIRE2 = 0x5B +PAGESEL = 0xFE + + +class DriverChip: + chips = {} + + def __init__(self, name: str): + name = name.upper() + self.name = name + self.chips[name] = self + self.initsequence = [] + + def cmd(self, c, *args): + """ + Add a command sequence to the init sequence + :param c: The command (8 bit) + :param args: zero or more arguments (8 bit values) + """ + self.initsequence.extend([c, len(args)] + list(args)) + + def delay(self, ms): + self.initsequence.extend([ms, 0xFF]) + + +chip = DriverChip("RM67162") +chip.cmd(PIXFMT, 0x55) +chip.cmd(BRIGHTNESS, 0) + +chip = DriverChip("RM690B0") +chip.cmd(PAGESEL, 0x20) +chip.cmd(MIPI, 0x0A) +chip.cmd(WRAM, 0x80) +chip.cmd(SWIRE1, 0x51) +chip.cmd(SWIRE2, 0x2E) +chip.cmd(PAGESEL, 0x00) +chip.cmd(0xC2, 0x00) +chip.delay(10) +chip.cmd(TEON, 0x00) + +chip = DriverChip("AXS15231") +chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) +chip.cmd(0xC1, 0x33) +chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +DriverChip("Custom") diff --git a/esphome/components/qspi_amoled/qspi_amoled.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp similarity index 51% rename from esphome/components/qspi_amoled/qspi_amoled.cpp rename to esphome/components/qspi_dbi/qspi_dbi.cpp index b1f651025a..a649a25ea6 100644 --- a/esphome/components/qspi_amoled/qspi_amoled.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -1,12 +1,12 @@ #ifdef USE_ESP_IDF -#include "qspi_amoled.h" +#include "qspi_dbi.h" #include "esphome/core/log.h" namespace esphome { -namespace qspi_amoled { +namespace qspi_dbi { -void QspiAmoLed::setup() { - esph_log_config(TAG, "Setting up QSPI_AMOLED"); +void QspiDbi::setup() { + ESP_LOGCONFIG(TAG, "Setting up QSPI_DBI"); this->spi_setup(); if (this->enable_pin_ != nullptr) { this->enable_pin_->setup(); @@ -22,13 +22,17 @@ void QspiAmoLed::setup() { } this->set_timeout(120, [this] { this->write_command_(SLEEP_OUT); }); this->set_timeout(240, [this] { this->write_init_sequence_(); }); + if (this->draw_from_origin_) + check_buffer_(); } -void QspiAmoLed::update() { +void QspiDbi::update() { if (!this->setup_complete_) { return; } this->do_update_(); + if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) + return; // Start addresses and widths/heights must be divisible by 2 (CASET/RASET restriction in datasheet) if (this->x_low_ % 2 == 1) { this->x_low_--; @@ -42,10 +46,15 @@ void QspiAmoLed::update() { if (this->y_high_ % 2 == 0) { this->y_high_++; } + if (this->draw_from_origin_) { + this->x_low_ = 0; + this->y_low_ = 0; + this->x_high_ = this->width_ - 1; + } int w = this->x_high_ - this->x_low_ + 1; int h = this->y_high_ - this->y_low_ + 1; - this->draw_pixels_at(this->x_low_, this->y_low_, w, h, this->buffer_, this->color_mode_, display::COLOR_BITNESS_565, - true, this->x_low_, this->y_low_, this->get_width_internal() - w - this->x_low_); + this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_, this->y_low_, + this->width_ - w - this->x_low_); // invalidate watermarks this->x_low_ = this->width_; this->y_low_ = this->height_; @@ -53,21 +62,19 @@ void QspiAmoLed::update() { this->y_high_ = 0; } -void QspiAmoLed::draw_absolute_pixel_internal(int x, int y, Color color) { +void QspiDbi::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { return; } - if (this->buffer_ == nullptr) - this->init_internal_(this->width_ * this->height_ * 2); if (this->is_failed()) return; + check_buffer_(); uint32_t pos = (y * this->width_) + x; - uint16_t new_color; bool updated = false; pos = pos * 2; - new_color = display::ColorUtil::color_to_565(color, display::ColorOrder::COLOR_ORDER_RGB); - if (this->buffer_[pos] != (uint8_t) (new_color >> 8)) { - this->buffer_[pos] = (uint8_t) (new_color >> 8); + uint16_t new_color = display::ColorUtil::color_to_565(color, display::ColorOrder::COLOR_ORDER_RGB); + if (this->buffer_[pos] != static_cast(new_color >> 8)) { + this->buffer_[pos] = static_cast(new_color >> 8); updated = true; } pos = pos + 1; @@ -90,7 +97,7 @@ void QspiAmoLed::draw_absolute_pixel_internal(int x, int y, Color color) { } } -void QspiAmoLed::reset_params_(bool ready) { +void QspiDbi::reset_params_(bool ready) { if (!ready && !this->is_ready()) return; this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF); @@ -102,55 +109,64 @@ void QspiAmoLed::reset_params_(bool ready) { mad |= MADCTL_MX; if (this->mirror_y_) mad |= MADCTL_MY; - this->write_command_(MADCTL_CMD, &mad, 1); - this->write_command_(BRIGHTNESS, &this->brightness_, 1); + this->write_command_(MADCTL_CMD, mad); + this->write_command_(BRIGHTNESS, this->brightness_); + this->write_command_(NORON); + this->write_command_(DISPLAY_ON); } -void QspiAmoLed::write_init_sequence_() { - if (this->model_ == RM690B0) { - this->write_command_(PAGESEL, 0x20); - this->write_command_(MIPI, 0x0A); - this->write_command_(WRAM, 0x80); - this->write_command_(SWIRE1, 0x51); - this->write_command_(SWIRE2, 0x2E); - this->write_command_(PAGESEL, 0x00); - this->write_command_(0xC2, 0x00); - delay(10); - this->write_command_(TEON, 0x00); +void QspiDbi::write_init_sequence_() { + for (const auto &seq : this->init_sequences_) { + this->write_sequence_(seq); } - this->write_command_(PIXFMT, 0x55); - this->write_command_(BRIGHTNESS, 0); - this->write_command_(DISPLAY_ON); this->reset_params_(true); this->setup_complete_ = true; - esph_log_config(TAG, "QSPI_AMOLED setup complete"); + ESP_LOGCONFIG(TAG, "QSPI_DBI setup complete"); } -void QspiAmoLed::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { +void QspiDbi::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { + ESP_LOGVV(TAG, "Set addr %d/%d, %d/%d", x1, y1, x2, y2); uint8_t buf[4]; x1 += this->offset_x_; x2 += this->offset_x_; y1 += this->offset_y_; y2 += this->offset_y_; - put16_be(buf, x1); - put16_be(buf + 2, x2); - this->write_command_(CASET, buf, sizeof buf); put16_be(buf, y1); put16_be(buf + 2, y2); this->write_command_(RASET, buf, sizeof buf); + put16_be(buf, x1); + put16_be(buf + 2, x2); + this->write_command_(CASET, buf, sizeof buf); } -void QspiAmoLed::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, - display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { +void QspiDbi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { if (!this->setup_complete_ || this->is_failed()) return; if (w <= 0 || h <= 0) return; if (bitness != display::COLOR_BITNESS_565 || order != this->color_mode_ || big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST)) { - return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, - x_pad); + return Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); + } else if (this->draw_from_origin_) { + auto stride = x_offset + w + x_pad; + for (int y = 0; y != h; y++) { + memcpy(this->buffer_ + ((y + y_start) * this->width_ + x_start) * 2, + ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2); + } + ptr = this->buffer_; + w = this->width_; + h += y_start; + x_start = 0; + y_start = 0; + x_offset = 0; + y_offset = 0; } + this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad); +} + +void QspiDbi::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, + int x_pad) { this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); this->enable(); // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. @@ -158,17 +174,50 @@ void QspiAmoLed::draw_pixels_at(int x_start, int y_start, int w, int h, const ui // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother this->write_cmd_addr_data(8, 0x32, 24, 0x2C00, ptr, w * h * 2, 4); } else { - this->write_cmd_addr_data(8, 0x32, 24, 0x2C00, nullptr, 0, 4); auto stride = x_offset + w + x_pad; + uint16_t cmd = 0x2C00; for (int y = 0; y != h; y++) { - this->write_cmd_addr_data(0, 0, 0, 0, ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2, 4); + this->write_cmd_addr_data(8, 0x32, 24, cmd, ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2, 4); + cmd = 0x3C00; } } this->disable(); } +void QspiDbi::write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) { + ESP_LOGV(TAG, "Command %02X, length %d, bytes %s", cmd, len, format_hex_pretty(bytes, len).c_str()); + this->enable(); + this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len); + this->disable(); +} -void QspiAmoLed::dump_config() { - ESP_LOGCONFIG("", "QSPI AMOLED"); +void QspiDbi::write_sequence_(const std::vector &vec) { + size_t index = 0; + while (index != vec.size()) { + if (vec.size() - index < 2) { + ESP_LOGE(TAG, "Malformed init sequence"); + return; + } + uint8_t cmd = vec[index++]; + uint8_t x = vec[index++]; + if (x == DELAY_FLAG) { + ESP_LOGV(TAG, "Delay %dms", cmd); + delay(cmd); + } else { + uint8_t num_args = x & 0x7F; + if (vec.size() - index < num_args) { + ESP_LOGE(TAG, "Malformed init sequence"); + return; + } + const auto *ptr = vec.data() + index; + this->write_command_(cmd, ptr, num_args); + index += num_args; + } + } +} + +void QspiDbi::dump_config() { + ESP_LOGCONFIG("", "QSPI_DBI Display"); + ESP_LOGCONFIG("", "Model: %s", this->model_); ESP_LOGCONFIG(TAG, " Height: %u", this->height_); ESP_LOGCONFIG(TAG, " Width: %u", this->width_); LOG_PIN(" CS Pin: ", this->cs_); @@ -176,6 +225,6 @@ void QspiAmoLed::dump_config() { ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000)); } -} // namespace qspi_amoled +} // namespace qspi_dbi } // namespace esphome #endif diff --git a/esphome/components/qspi_amoled/qspi_amoled.h b/esphome/components/qspi_dbi/qspi_dbi.h similarity index 80% rename from esphome/components/qspi_amoled/qspi_amoled.h rename to esphome/components/qspi_dbi/qspi_dbi.h index c766b4e685..ebb65a8a05 100644 --- a/esphome/components/qspi_amoled/qspi_amoled.h +++ b/esphome/components/qspi_dbi/qspi_dbi.h @@ -14,11 +14,12 @@ #include "esp_lcd_panel_rgb.h" namespace esphome { -namespace qspi_amoled { +namespace qspi_dbi { -constexpr static const char *const TAG = "display.qspi_amoled"; +constexpr static const char *const TAG = "display.qspi_dbi"; static const uint8_t SW_RESET_CMD = 0x01; static const uint8_t SLEEP_OUT = 0x11; +static const uint8_t NORON = 0x13; static const uint8_t INVERT_OFF = 0x20; static const uint8_t INVERT_ON = 0x21; static const uint8_t ALL_ON = 0x23; @@ -42,6 +43,7 @@ static const uint8_t MADCTL_MV = 0x20; ///< Bit 5 Reverse Mode static const uint8_t MADCTL_RGB = 0x00; ///< Bit 3 Red-Green-Blue pixel order static const uint8_t MADCTL_BGR = 0x08; ///< Bit 3 Blue-Green-Red pixel order +static const uint8_t DELAY_FLAG = 0xFF; // store a 16 bit value in a buffer, big endian. static inline void put16_be(uint8_t *buf, uint16_t value) { buf[0] = value >> 8; @@ -49,15 +51,16 @@ static inline void put16_be(uint8_t *buf, uint16_t value) { } enum Model { + CUSTOM, RM690B0, RM67162, }; -class QspiAmoLed : public display::DisplayBuffer, - public spi::SPIDevice { +class QspiDbi : public display::DisplayBuffer, + public spi::SPIDevice { public: - void set_model(Model model) { this->model_ = model; } + void set_model(const char *model) { this->model_ = model; } void update() override; void setup() override; display::ColorOrder get_color_mode() { return this->color_mode_; } @@ -93,17 +96,27 @@ class QspiAmoLed : public display::DisplayBuffer, this->offset_x_ = offset_x; this->offset_y_ = offset_y; } + + void set_draw_from_origin(bool draw_from_origin) { this->draw_from_origin_ = draw_from_origin; } display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } void dump_config() override; int get_width_internal() override { return this->width_; } int get_height_internal() override { return this->height_; } bool can_proceed() override { return this->setup_complete_; } + void add_init_sequence(const std::vector &sequence) { this->init_sequences_.push_back(sequence); } protected: + void check_buffer_() { + if (this->buffer_ == nullptr) + this->init_internal_(this->width_ * this->height_ * 2); + } + void write_sequence_(const std::vector &vec); void draw_absolute_pixel_internal(int x, int y, Color color) override; void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; + void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, + int x_pad); /** * the RM67162 in quad SPI mode seems to work like this (not in the datasheet, this is deduced from the * sample code.) @@ -122,11 +135,7 @@ class QspiAmoLed : public display::DisplayBuffer, * @param bytes * @param len */ - void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) { - this->enable(); - this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len); - this->disable(); - } + void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len); void write_command_(uint8_t cmd, uint8_t data) { this->write_command_(cmd, &data, 1); } void write_command_(uint8_t cmd) { this->write_command_(cmd, &cmd, 0); } @@ -136,8 +145,8 @@ class QspiAmoLed : public display::DisplayBuffer, GPIOPin *reset_pin_{nullptr}; GPIOPin *enable_pin_{nullptr}; - uint16_t x_low_{0}; - uint16_t y_low_{0}; + uint16_t x_low_{1}; + uint16_t y_low_{1}; uint16_t x_high_{0}; uint16_t y_high_{0}; bool setup_complete_{}; @@ -151,12 +160,14 @@ class QspiAmoLed : public display::DisplayBuffer, bool swap_xy_{}; bool mirror_x_{}; bool mirror_y_{}; + bool draw_from_origin_{false}; uint8_t brightness_{0xD0}; - Model model_{RM690B0}; + const char *model_{"Unknown"}; + std::vector> init_sequences_{}; esp_lcd_panel_handle_t handle_{}; }; -} // namespace qspi_amoled +} // namespace qspi_dbi } // namespace esphome #endif diff --git a/esphome/components/st7701s/display.py b/esphome/components/st7701s/display.py index 9310e9d760..e73c2467da 100644 --- a/esphome/components/st7701s/display.py +++ b/esphome/components/st7701s/display.py @@ -18,6 +18,7 @@ from esphome.const import ( CONF_HSYNC_PIN, CONF_ID, CONF_IGNORE_STRAPPING_WARNING, + CONF_INIT_SEQUENCE, CONF_INVERT_COLORS, CONF_LAMBDA, CONF_MIRROR_X, @@ -35,7 +36,6 @@ from esphome.core import TimePeriod from .init_sequences import ST7701S_INITS, cmd -CONF_INIT_SEQUENCE = "init_sequence" CONF_DE_PIN = "de_pin" CONF_PCLK_PIN = "pclk_pin" diff --git a/esphome/const.py b/esphome/const.py index 7665c77a32..a3a4318d69 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -398,6 +398,7 @@ CONF_INCLUDES = "includes" CONF_INDEX = "index" CONF_INDOOR = "indoor" CONF_INFRARED = "infrared" +CONF_INIT_SEQUENCE = "init_sequence" CONF_INITIAL_MODE = "initial_mode" CONF_INITIAL_OPTION = "initial_option" CONF_INITIAL_STATE = "initial_state" diff --git a/tests/components/qspi_amoled/common.yaml b/tests/components/qspi_dbi/common.yaml similarity index 73% rename from tests/components/qspi_amoled/common.yaml rename to tests/components/qspi_dbi/common.yaml index 01d1a63bcb..655af304af 100644 --- a/tests/components/qspi_amoled/common.yaml +++ b/tests/components/qspi_dbi/common.yaml @@ -5,7 +5,7 @@ spi: data_pins: [14, 10, 16, 12] display: - - platform: qspi_amoled + - platform: qspi_dbi model: RM690B0 data_rate: 80MHz spi_mode: mode0 @@ -20,9 +20,10 @@ display: reset_pin: 13 enable_pin: 9 - - platform: qspi_amoled - model: RM67162 + - platform: qspi_dbi + model: CUSTOM id: main_lcd + draw_from_origin: true dimensions: height: 240 width: 536 @@ -34,3 +35,10 @@ display: cs_pin: 6 reset_pin: 17 enable_pin: 38 + init_sequence: + - [0x3A, 0x66] + - [0x11] + - delay 120ms + - [0x29] + - delay 20ms + diff --git a/tests/components/qspi_amoled/test.esp32-s3-idf.yaml b/tests/components/qspi_dbi/test.esp32-s3-idf.yaml similarity index 100% rename from tests/components/qspi_amoled/test.esp32-s3-idf.yaml rename to tests/components/qspi_dbi/test.esp32-s3-idf.yaml From fa01149771be89caafe4a4103768bb83b6ca947a Mon Sep 17 00:00:00 2001 From: Paul Blacknell Date: Wed, 16 Oct 2024 04:28:24 +0100 Subject: [PATCH 0489/1052] Add support for Analog Devices MAX17043 battery fuel gauge (#7522) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Keith Burzinski --- CODEOWNERS | 1 + esphome/components/max17043/__init__.py | 1 + esphome/components/max17043/automation.h | 20 ++++ esphome/components/max17043/max17043.cpp | 98 +++++++++++++++++++ esphome/components/max17043/max17043.h | 29 ++++++ esphome/components/max17043/sensor.py | 77 +++++++++++++++ tests/components/max17043/common.yaml | 19 ++++ tests/components/max17043/test.esp32-ard.yaml | 6 ++ .../max17043/test.esp32-c3-ard.yaml | 5 + .../max17043/test.esp32-c3-idf.yaml | 5 + tests/components/max17043/test.esp32-idf.yaml | 5 + .../components/max17043/test.esp8266-ard.yaml | 5 + .../components/max17043/test.rp2040-ard.yaml | 5 + 13 files changed, 276 insertions(+) create mode 100644 esphome/components/max17043/__init__.py create mode 100644 esphome/components/max17043/automation.h create mode 100644 esphome/components/max17043/max17043.cpp create mode 100644 esphome/components/max17043/max17043.h create mode 100644 esphome/components/max17043/sensor.py create mode 100644 tests/components/max17043/common.yaml create mode 100644 tests/components/max17043/test.esp32-ard.yaml create mode 100644 tests/components/max17043/test.esp32-c3-ard.yaml create mode 100644 tests/components/max17043/test.esp32-c3-idf.yaml create mode 100644 tests/components/max17043/test.esp32-idf.yaml create mode 100644 tests/components/max17043/test.esp8266-ard.yaml create mode 100644 tests/components/max17043/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 7dd18d2c51..d6104c9345 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -237,6 +237,7 @@ esphome/components/ltr_als_ps/* @latonita esphome/components/lvgl/* @clydebarrow esphome/components/m5stack_8angle/* @rnauber esphome/components/matrix_keypad/* @ssieb +esphome/components/max17043/* @blacknell esphome/components/max31865/* @DAVe3283 esphome/components/max44009/* @berfenger esphome/components/max6956/* @looping40 diff --git a/esphome/components/max17043/__init__.py b/esphome/components/max17043/__init__.py new file mode 100644 index 0000000000..1db25ccdd6 --- /dev/null +++ b/esphome/components/max17043/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@blacknell"] diff --git a/esphome/components/max17043/automation.h b/esphome/components/max17043/automation.h new file mode 100644 index 0000000000..44729d119b --- /dev/null +++ b/esphome/components/max17043/automation.h @@ -0,0 +1,20 @@ + +#pragma once +#include "esphome/core/automation.h" +#include "max17043.h" + +namespace esphome { +namespace max17043 { + +template class SleepAction : public Action { + public: + explicit SleepAction(MAX17043Component *max17043) : max17043_(max17043) {} + + void play(Ts... x) override { this->max17043_->sleep_mode(); } + + protected: + MAX17043Component *max17043_; +}; + +} // namespace max17043 +} // namespace esphome diff --git a/esphome/components/max17043/max17043.cpp b/esphome/components/max17043/max17043.cpp new file mode 100644 index 0000000000..4a83320455 --- /dev/null +++ b/esphome/components/max17043/max17043.cpp @@ -0,0 +1,98 @@ +#include "max17043.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace max17043 { + +// MAX174043 is a 1-Cell Fuel Gauge with ModelGauge and Low-Battery Alert +// Consult the datasheet at https://www.analog.com/en/products/max17043.html + +static const char *const TAG = "max17043"; + +static const uint8_t MAX17043_VCELL = 0x02; +static const uint8_t MAX17043_SOC = 0x04; +static const uint8_t MAX17043_CONFIG = 0x0c; + +static const uint16_t MAX17043_CONFIG_POWER_UP_DEFAULT = 0x971C; +static const uint16_t MAX17043_CONFIG_SAFE_MASK = 0xFF1F; // mask out sleep bit (7), unused bit (6) and alert bit (4) +static const uint16_t MAX17043_CONFIG_SLEEP_MASK = 0x0080; + +void MAX17043Component::update() { + uint16_t raw_voltage, raw_percent; + + if (this->voltage_sensor_ != nullptr) { + if (!this->read_byte_16(MAX17043_VCELL, &raw_voltage)) { + this->status_set_warning("Unable to read MAX17043_VCELL"); + } else { + float voltage = (1.25 * (float) (raw_voltage >> 4)) / 1000.0; + this->voltage_sensor_->publish_state(voltage); + this->status_clear_warning(); + } + } + if (this->battery_remaining_sensor_ != nullptr) { + if (!this->read_byte_16(MAX17043_SOC, &raw_percent)) { + this->status_set_warning("Unable to read MAX17043_SOC"); + } else { + float percent = (float) ((raw_percent >> 8) + 0.003906f * (raw_percent & 0x00ff)); + this->battery_remaining_sensor_->publish_state(percent); + this->status_clear_warning(); + } + } +} + +void MAX17043Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up MAX17043..."); + + uint16_t config_reg; + if (this->write(&MAX17043_CONFIG, 1) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + + if (this->read(reinterpret_cast(&config_reg), 2) != i2c::ERROR_OK) { + this->status_set_warning(); + return; + } + + config_reg = i2c::i2ctohs(config_reg) & MAX17043_CONFIG_SAFE_MASK; + ESP_LOGV(TAG, "MAX17043 CONFIG register reads 0x%X", config_reg); + + if (config_reg != MAX17043_CONFIG_POWER_UP_DEFAULT) { + ESP_LOGE(TAG, "Device does not appear to be a MAX17043"); + this->status_set_error("unrecognised"); + this->mark_failed(); + return; + } + + // need to write back to config register to reset the sleep bit + if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT)) { + this->status_set_error("sleep reset failed"); + this->mark_failed(); + return; + } +} + +void MAX17043Component::dump_config() { + ESP_LOGCONFIG(TAG, "MAX17043:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with MAX17043 failed"); + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Battery Voltage", this->voltage_sensor_); + LOG_SENSOR(" ", "Battery Level", this->battery_remaining_sensor_); +} + +float MAX17043Component::get_setup_priority() const { return setup_priority::DATA; } + +void MAX17043Component::sleep_mode() { + if (!this->is_failed()) { + if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT | MAX17043_CONFIG_SLEEP_MASK)) { + ESP_LOGW(TAG, "Unable to write the sleep bit to config register"); + this->status_set_warning(); + } + } +} + +} // namespace max17043 +} // namespace esphome diff --git a/esphome/components/max17043/max17043.h b/esphome/components/max17043/max17043.h new file mode 100644 index 0000000000..540b977789 --- /dev/null +++ b/esphome/components/max17043/max17043.h @@ -0,0 +1,29 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace max17043 { + +class MAX17043Component : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void update() override; + void sleep_mode(); + + 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; + } + + protected: + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *battery_remaining_sensor_{nullptr}; +}; + +} // namespace max17043 +} // namespace esphome diff --git a/esphome/components/max17043/sensor.py b/esphome/components/max17043/sensor.py new file mode 100644 index 0000000000..3da0f953b0 --- /dev/null +++ b/esphome/components/max17043/sensor.py @@ -0,0 +1,77 @@ +from esphome import automation +from esphome.automation import maybe_simple_id +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, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_VOLTAGE, + ENTITY_CATEGORY_DIAGNOSTIC, + STATE_CLASS_MEASUREMENT, + UNIT_PERCENT, + UNIT_VOLT, +) + +DEPENDENCIES = ["i2c"] + +max17043_ns = cg.esphome_ns.namespace("max17043") +MAX17043Component = max17043_ns.class_( + "MAX17043Component", cg.PollingComponent, i2c.I2CDevice +) + +# Actions +SleepAction = max17043_ns.class_("SleepAction", automation.Action) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MAX17043Component), + 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, + ), + } + ) + .extend(cv.polling_component_schema("60s")) + .extend(i2c.i2c_device_schema(0x36)) +) + + +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 voltage_config := config.get(CONF_BATTERY_VOLTAGE): + sens = await sensor.new_sensor(voltage_config) + cg.add(var.set_voltage_sensor(sens)) + + if CONF_BATTERY_LEVEL in config: + sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL]) + cg.add(var.set_battery_remaining_sensor(sens)) + + +MAX17043_ACTION_SCHEMA = maybe_simple_id( + { + cv.Required(CONF_ID): cv.use_id(MAX17043Component), + } +) + + +@automation.register_action("max17043.sleep_mode", SleepAction, MAX17043_ACTION_SCHEMA) +async def max17043_sleep_mode_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) diff --git a/tests/components/max17043/common.yaml b/tests/components/max17043/common.yaml new file mode 100644 index 0000000000..c2f324212e --- /dev/null +++ b/tests/components/max17043/common.yaml @@ -0,0 +1,19 @@ +esphome: + on_boot: + then: + - max17043.sleep_mode: max17043_id + +i2c: + - id: i2c_id + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: max17043 + id: max17043_id + i2c_id: i2c_id + battery_voltage: + name: "Battery Voltage" + battery_level: + name: Battery + update_interval: 10s diff --git a/tests/components/max17043/test.esp32-ard.yaml b/tests/components/max17043/test.esp32-ard.yaml new file mode 100644 index 0000000000..c84e0a5c2e --- /dev/null +++ b/tests/components/max17043/test.esp32-ard.yaml @@ -0,0 +1,6 @@ +substitutions: + sda_pin: GPIO21 + scl_pin: GPIO22 + +<<: !include common.yaml + diff --git a/tests/components/max17043/test.esp32-c3-ard.yaml b/tests/components/max17043/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..9a1477d4b9 --- /dev/null +++ b/tests/components/max17043/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + sda_pin: GPIO8 + scl_pin: GPIO10 + +<<: !include common.yaml diff --git a/tests/components/max17043/test.esp32-c3-idf.yaml b/tests/components/max17043/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..9a1477d4b9 --- /dev/null +++ b/tests/components/max17043/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + sda_pin: GPIO8 + scl_pin: GPIO10 + +<<: !include common.yaml diff --git a/tests/components/max17043/test.esp32-idf.yaml b/tests/components/max17043/test.esp32-idf.yaml new file mode 100644 index 0000000000..c6615f51cd --- /dev/null +++ b/tests/components/max17043/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + sda_pin: GPIO21 + scl_pin: GPIO22 + +<<: !include common.yaml diff --git a/tests/components/max17043/test.esp8266-ard.yaml b/tests/components/max17043/test.esp8266-ard.yaml new file mode 100644 index 0000000000..a87353b78b --- /dev/null +++ b/tests/components/max17043/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + sda_pin: GPIO4 + scl_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/max17043/test.rp2040-ard.yaml b/tests/components/max17043/test.rp2040-ard.yaml new file mode 100644 index 0000000000..c6615f51cd --- /dev/null +++ b/tests/components/max17043/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + sda_pin: GPIO21 + scl_pin: GPIO22 + +<<: !include common.yaml From c38cc128dbfc711fb34cbcf59cf1bd89cb16fd65 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 16 Oct 2024 00:26:17 -0400 Subject: [PATCH 0490/1052] chore: bump pyyaml to 6.0.2 to support py3.13 build (#7610) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3ffd364d87..a89d3e281f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ async_timeout==4.0.3; python_version <= "3.10" cryptography==43.0.0 voluptuous==0.14.2 -PyYAML==6.0.1 +PyYAML==6.0.2 paho-mqtt==1.6.1 colorama==0.4.6 icmplib==3.0.4 From 22478ffb0f8fd67f4b8a0db9c2b84b5f12d27400 Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Wed, 16 Oct 2024 00:26:48 -0400 Subject: [PATCH 0491/1052] chore: bump platformio to 6.1.16 to support py3.13 build (#7590) --- docker/Dockerfile | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 85823687c2..52a4794f24 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -86,7 +86,7 @@ RUN \ pip3 install \ --break-system-packages --no-cache-dir \ # Keep platformio version in sync with requirements.txt - platformio==6.1.15 \ + platformio==6.1.16 \ # Change some platformio settings && platformio settings set enable_telemetry No \ && platformio settings set check_platformio_interval 1000000 \ diff --git a/requirements.txt b/requirements.txt index a89d3e281f..c03a9f181a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ tornado==6.4 tzlocal==5.2 # from time tzdata>=2021.1 # from time pyserial==3.5 -platformio==6.1.15 # When updating platformio, also update Dockerfile +platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20240620.0 From 1c845e0ff87676d93b46ada90f6973122b0fabf3 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 16 Oct 2024 18:47:11 -0400 Subject: [PATCH 0492/1052] [speaker, i2s_audio] I2S Speaker implementation using a ring buffer (#7605) --- CODEOWNERS | 1 + esphome/components/audio/__init__.py | 9 + esphome/components/audio/audio.h | 21 + .../components/i2s_audio/speaker/__init__.py | 3 +- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 651 +++++++++++------- .../i2s_audio/speaker/i2s_audio_speaker.h | 111 ++- esphome/components/speaker/__init__.py | 25 +- esphome/components/speaker/automation.h | 5 + esphome/components/speaker/speaker.h | 37 +- tests/components/speaker/test.esp32-ard.yaml | 1 + .../components/speaker/test.esp32-c3-ard.yaml | 1 + .../components/speaker/test.esp32-c3-idf.yaml | 1 + tests/components/speaker/test.esp32-idf.yaml | 1 + 13 files changed, 601 insertions(+), 266 deletions(-) create mode 100644 esphome/components/audio/__init__.py create mode 100644 esphome/components/audio/audio.h diff --git a/CODEOWNERS b/CODEOWNERS index d6104c9345..7ac6aa2f76 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -48,6 +48,7 @@ esphome/components/at581x/* @X-Ryl669 esphome/components/atc_mithermometer/* @ahpohl esphome/components/atm90e26/* @danieltwagner esphome/components/atm90e32/* @circuitsetup @descipher +esphome/components/audio/* @kahrendt esphome/components/audio_dac/* @kbx81 esphome/components/b_parasite/* @rbaron esphome/components/ballu/* @bazuchan diff --git a/esphome/components/audio/__init__.py b/esphome/components/audio/__init__.py new file mode 100644 index 0000000000..4ffdc401dc --- /dev/null +++ b/esphome/components/audio/__init__.py @@ -0,0 +1,9 @@ +import esphome.codegen as cg +import esphome.config_validation as cv + +CODEOWNERS = ["@kahrendt"] +audio_ns = cg.esphome_ns.namespace("audio") + +CONFIG_SCHEMA = cv.All( + cv.Schema({}), +) diff --git a/esphome/components/audio/audio.h b/esphome/components/audio/audio.h new file mode 100644 index 0000000000..b0968dc8da --- /dev/null +++ b/esphome/components/audio/audio.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace esphome { +namespace audio { + +struct AudioStreamInfo { + bool operator==(const AudioStreamInfo &rhs) const { + return (channels == rhs.channels) && (bits_per_sample == rhs.bits_per_sample) && (sample_rate == rhs.sample_rate); + } + bool operator!=(const AudioStreamInfo &rhs) const { return !operator==(rhs); } + size_t get_bytes_per_sample() const { return bits_per_sample / 8; } + uint8_t channels = 1; + uint8_t bits_per_sample = 16; + uint32_t sample_rate = 16000; +}; + +} // namespace audio +} // namespace esphome diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index bba886b39b..9fdaced64c 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -16,6 +16,7 @@ from .. import ( register_i2s_audio_component, ) +AUTO_LOAD = ["audio"] CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["i2s_audio"] @@ -72,7 +73,7 @@ BASE_SCHEMA = ( .extend( { cv.Optional( - CONF_TIMEOUT, default="100ms" + CONF_TIMEOUT, default="500ms" ): cv.positive_time_period_milliseconds, } ) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 97c1d86c36..4fc489d0a3 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -4,6 +4,8 @@ #include +#include "esphome/components/audio/audio.h" + #include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" @@ -11,186 +13,296 @@ namespace esphome { namespace i2s_audio { -static const size_t BUFFER_COUNT = 20; +static const size_t DMA_BUFFER_SIZE = 512; +static const size_t DMA_BUFFERS_COUNT = 4; +static const size_t FRAMES_IN_ALL_DMA_BUFFERS = DMA_BUFFER_SIZE * DMA_BUFFERS_COUNT; +static const size_t RING_BUFFER_SAMPLES = 8192; +static const size_t TASK_DELAY_MS = 10; +static const size_t TASK_STACK_SIZE = 4096; +static const ssize_t TASK_PRIORITY = 23; static const char *const TAG = "i2s_audio.speaker"; +enum SpeakerEventGroupBits : uint32_t { + COMMAND_START = (1 << 0), // Starts the main task purpose + COMMAND_STOP = (1 << 1), // stops the main task + COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the task once all data has been written + MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE = (1 << 5), // Locks the ring buffer when not set + STATE_STARTING = (1 << 10), + STATE_RUNNING = (1 << 11), + STATE_STOPPING = (1 << 12), + STATE_STOPPED = (1 << 13), + ERR_TASK_FAILED_TO_START = (1 << 15), + ERR_ESP_INVALID_STATE = (1 << 16), + ERR_ESP_INVALID_ARG = (1 << 17), + ERR_ESP_INVALID_SIZE = (1 << 18), + ERR_ESP_NO_MEM = (1 << 19), + ERR_ESP_FAIL = (1 << 20), + ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE | ERR_ESP_NO_MEM | ERR_ESP_FAIL, + ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits +}; + +// Translates a SpeakerEventGroupBits ERR_ESP bit to the coressponding esp_err_t +static esp_err_t err_bit_to_esp_err(uint32_t bit) { + switch (bit) { + case SpeakerEventGroupBits::ERR_ESP_INVALID_STATE: + return ESP_ERR_INVALID_STATE; + case SpeakerEventGroupBits::ERR_ESP_INVALID_ARG: + return ESP_ERR_INVALID_ARG; + case SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE: + return ESP_ERR_INVALID_SIZE; + case SpeakerEventGroupBits::ERR_ESP_NO_MEM: + return ESP_ERR_NO_MEM; + default: + return ESP_FAIL; + } +} + +/// @brief Multiplies the input array of Q15 numbers by a Q15 constant factor +/// +/// Based on `dsps_mulc_s16_ansi` from the esp-dsp library: +/// https://github.com/espressif/esp-dsp/blob/master/modules/math/mulc/fixed/dsps_mulc_s16_ansi.c +/// (accessed on 2024-09-30). +/// @param input Array of Q15 numbers +/// @param output Array of Q15 numbers +/// @param len Length of array +/// @param c Q15 constant factor +static void q15_multiplication(const int16_t *input, int16_t *output, size_t len, int16_t c) { + for (int i = 0; i < len; i++) { + int32_t acc = (int32_t) input[i] * (int32_t) c; + output[i] = (int16_t) (acc >> 15); + } +} + +// Lists the Q15 fixed point scaling factor for volume reduction. +// Has 100 values representing silence and a reduction [49, 48.5, ... 0.5, 0] dB. +// dB to PCM scaling factor formula: floating_point_scale_factor = 2^(-db/6.014) +// float to Q15 fixed point formula: q15_scale_factor = floating_point_scale_factor * 2^(15) +static const std::vector Q15_VOLUME_SCALING_FACTORS = { + 0, 116, 122, 130, 137, 146, 154, 163, 173, 183, 194, 206, 218, 231, 244, + 259, 274, 291, 308, 326, 345, 366, 388, 411, 435, 461, 488, 517, 548, 580, + 615, 651, 690, 731, 774, 820, 868, 920, 974, 1032, 1094, 1158, 1227, 1300, 1377, + 1459, 1545, 1637, 1734, 1837, 1946, 2061, 2184, 2313, 2450, 2596, 2750, 2913, 3085, 3269, + 3462, 3668, 3885, 4116, 4360, 4619, 4893, 5183, 5490, 5816, 6161, 6527, 6914, 7324, 7758, + 8218, 8706, 9222, 9770, 10349, 10963, 11613, 12302, 13032, 13805, 14624, 15491, 16410, 17384, 18415, + 19508, 20665, 21891, 23189, 24565, 26022, 27566, 29201, 30933, 32767}; + void I2SAudioSpeaker::setup() { ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker..."); - this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent)); - if (this->buffer_queue_ == nullptr) { - ESP_LOGE(TAG, "Failed to create buffer queue"); - this->mark_failed(); - return; + if (this->event_group_ == nullptr) { + this->event_group_ = xEventGroupCreate(); } - this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent)); - if (this->event_queue_ == nullptr) { - ESP_LOGE(TAG, "Failed to create event queue"); + if (this->event_group_ == nullptr) { + ESP_LOGE(TAG, "Failed to create event group"); this->mark_failed(); return; } } +void I2SAudioSpeaker::loop() { + uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); + + if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) { + this->status_set_error("Failed to start speaker task"); + } + + if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { + uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; + ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); + this->status_set_warning(); + } + + if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) { + ESP_LOGD(TAG, "Starting Speaker"); + this->state_ = speaker::STATE_STARTING; + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING); + } + if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) { + ESP_LOGD(TAG, "Started Speaker"); + this->state_ = speaker::STATE_RUNNING; + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING); + this->status_clear_warning(); + this->status_clear_error(); + } + if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) { + ESP_LOGD(TAG, "Stopping Speaker"); + this->state_ = speaker::STATE_STOPPING; + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING); + } + if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) { + if (!this->task_created_) { + ESP_LOGD(TAG, "Stopped Speaker"); + this->state_ = speaker::STATE_STOPPED; + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS); + this->speaker_task_handle_ = nullptr; + } + } +} + +void I2SAudioSpeaker::set_volume(float volume) { + this->volume_ = volume; + ssize_t decibel_index = remap(volume, 0.0f, 1.0f, 0, Q15_VOLUME_SCALING_FACTORS.size() - 1); + this->q15_volume_factor_ = Q15_VOLUME_SCALING_FACTORS[decibel_index]; +} + +size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) { + if (this->is_failed()) { + ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup"); + return 0; + } + if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) { + this->start(); + } + + // Wait for the ring buffer to be available + uint32_t event_bits = + xEventGroupWaitBits(this->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE, pdFALSE, + pdFALSE, pdMS_TO_TICKS(TASK_DELAY_MS)); + + if (event_bits & SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE) { + // Ring buffer is available to write + + // Lock the ring buffer, write to it, then unlock it + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE); + size_t bytes_written = this->audio_ring_buffer_->write_without_replacement((void *) data, length, ticks_to_wait); + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE); + + return bytes_written; + } + + return 0; +} + +bool I2SAudioSpeaker::has_buffered_data() const { + if (this->audio_ring_buffer_ != nullptr) { + return this->audio_ring_buffer_->available() > 0; + } + return false; +} + +void I2SAudioSpeaker::speaker_task(void *params) { + I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params; + uint32_t event_group_bits = + xEventGroupWaitBits(this_speaker->event_group_, + SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP | + SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY, // Bit message to read + pdTRUE, // Clear the bits on exit + pdFALSE, // Don't wait for all the bits, + portMAX_DELAY); // Block indefinitely until a bit is set + + if (event_group_bits & (SpeakerEventGroupBits::COMMAND_STOP | SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY)) { + // Received a stop signal before the task was requested to start + this_speaker->delete_task_(0); + } + + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING); + + audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_; + const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample(); + const uint8_t number_of_channels = audio_stream_info.channels; + + const size_t dma_buffers_size = FRAMES_IN_ALL_DMA_BUFFERS * bytes_per_sample * number_of_channels; + + if (this_speaker->send_esp_err_to_event_group_( + this_speaker->allocate_buffers_(dma_buffers_size, RING_BUFFER_SAMPLES * bytes_per_sample))) { + // Failed to allocate buffers + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); + this_speaker->delete_task_(dma_buffers_size); + } + + if (this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_())) { + // Failed to start I2S driver + this_speaker->delete_task_(dma_buffers_size); + } else { + // Ring buffer is allocated, so indicate its can be written to + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE); + } + + if (!this_speaker->send_esp_err_to_event_group_(this_speaker->reconfigure_i2s_stream_info_(audio_stream_info))) { + // Successfully set the I2S stream info, ready to write audio data to the I2S port + + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING); + + bool stop_gracefully = false; + uint32_t last_data_received_time = millis(); + + while ((millis() - last_data_received_time) <= this_speaker->timeout_) { + event_group_bits = xEventGroupGetBits(this_speaker->event_group_); + + if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) { + break; + } + if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY) { + stop_gracefully = true; + } + + size_t bytes_to_read = dma_buffers_size; + size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read, + pdMS_TO_TICKS(TASK_DELAY_MS)); + + if (bytes_read > 0) { + last_data_received_time = millis(); + size_t bytes_written = 0; + + if ((audio_stream_info.bits_per_sample == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) { + // Scale samples by the volume factor in place + q15_multiplication((int16_t *) this_speaker->data_buffer_, (int16_t *) this_speaker->data_buffer_, + bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_); + } + + if (audio_stream_info.bits_per_sample == (uint8_t) this_speaker->bits_per_sample_) { + i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read, &bytes_written, + portMAX_DELAY); + } else if (audio_stream_info.bits_per_sample < (uint8_t) this_speaker->bits_per_sample_) { + i2s_write_expand(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read, + audio_stream_info.bits_per_sample, this_speaker->bits_per_sample_, &bytes_written, + portMAX_DELAY); + } + + if (bytes_written != bytes_read) { + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); + } + + } else { + // No data received + + if (stop_gracefully) { + break; + } + + i2s_zero_dma_buffer(this_speaker->parent_->get_port()); + } + } + } + i2s_zero_dma_buffer(this_speaker->parent_->get_port()); + + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING); + + i2s_stop(this_speaker->parent_->get_port()); + i2s_driver_uninstall(this_speaker->parent_->get_port()); + + this_speaker->parent_->unlock(); + this_speaker->delete_task_(dma_buffers_size); +} + void I2SAudioSpeaker::start() { - if (this->is_failed()) { - ESP_LOGE(TAG, "Cannot start audio, speaker failed to setup"); + if (this->is_failed()) return; - } - if (this->task_created_) { - ESP_LOGW(TAG, "Called start while task has been already created."); + if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING)) return; - } - this->state_ = speaker::STATE_STARTING; -} -void I2SAudioSpeaker::start_() { - if (this->task_created_) { - return; - } - if (!this->parent_->try_lock()) { - return; // Waiting for another i2s component to return lock + + if (this->speaker_task_handle_ == nullptr) { + xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY, + &this->speaker_task_handle_); } - xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 1, &this->player_task_handle_); - this->task_created_ = true; -} - -template const uint8_t *convert_data_format(const a *from, b *to, size_t &bytes, bool repeat) { - if (sizeof(a) == sizeof(b) && !repeat) { - return reinterpret_cast(from); - } - const b *result = to; - for (size_t i = 0; i < bytes; i += sizeof(a)) { - b value = static_cast(*from++) << (sizeof(b) - sizeof(a)) * 8; - *to++ = value; - if (repeat) - *to++ = value; - } - bytes *= (sizeof(b) / sizeof(a)) * (repeat ? 2 : 1); // NOLINT - return reinterpret_cast(result); -} - -void I2SAudioSpeaker::player_task(void *params) { - I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params; - - TaskEvent event; - event.type = TaskEventType::STARTING; - xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); - - i2s_driver_config_t config = { - .mode = (i2s_mode_t) (this_speaker->i2s_mode_ | I2S_MODE_TX), - .sample_rate = this_speaker->sample_rate_, - .bits_per_sample = this_speaker->bits_per_sample_, - .channel_format = this_speaker->channel_, - .communication_format = this_speaker->i2s_comm_fmt_, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 8, - .dma_buf_len = 256, - .use_apll = this_speaker->use_apll_, - .tx_desc_auto_clear = true, - .fixed_mclk = 0, - .mclk_multiple = I2S_MCLK_MULTIPLE_256, - .bits_per_chan = this_speaker->bits_per_channel_, - }; -#if SOC_I2S_SUPPORTS_DAC - if (this_speaker->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) { - config.mode = (i2s_mode_t) (config.mode | I2S_MODE_DAC_BUILT_IN); - } -#endif - - esp_err_t err = i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr); - if (err != ESP_OK) { - event.type = TaskEventType::WARNING; - event.err = err; - xQueueSend(this_speaker->event_queue_, &event, 0); - event.type = TaskEventType::STOPPED; - xQueueSend(this_speaker->event_queue_, &event, 0); - while (true) { - delay(10); - } - } - -#if SOC_I2S_SUPPORTS_DAC - if (this_speaker->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) { -#endif - i2s_pin_config_t pin_config = this_speaker->parent_->get_pin_config(); - pin_config.data_out_num = this_speaker->dout_pin_; - - i2s_set_pin(this_speaker->parent_->get_port(), &pin_config); -#if SOC_I2S_SUPPORTS_DAC + if (this->speaker_task_handle_ != nullptr) { + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START); + this->task_created_ = true; } else { - i2s_set_dac_mode(this_speaker->internal_dac_mode_); - } -#endif - - DataEvent data_event; - - event.type = TaskEventType::STARTED; - xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY); - - int32_t buffer[BUFFER_SIZE]; - - while (true) { - if (xQueueReceive(this_speaker->buffer_queue_, &data_event, this_speaker->timeout_ / portTICK_PERIOD_MS) != - pdTRUE) { - break; // End of audio from main thread - } - if (data_event.stop) { - // Stop signal from main thread - xQueueReset(this_speaker->buffer_queue_); // Flush queue - break; - } - - const uint8_t *data = data_event.data; - size_t remaining = data_event.len; - switch (this_speaker->bits_per_sample_) { - case I2S_BITS_PER_SAMPLE_8BIT: - case I2S_BITS_PER_SAMPLE_16BIT: { - data = convert_data_format(reinterpret_cast(data), reinterpret_cast(buffer), - remaining, this_speaker->channel_ == I2S_CHANNEL_FMT_ALL_LEFT); - break; - } - case I2S_BITS_PER_SAMPLE_24BIT: - case I2S_BITS_PER_SAMPLE_32BIT: { - data = convert_data_format(reinterpret_cast(data), reinterpret_cast(buffer), - remaining, this_speaker->channel_ == I2S_CHANNEL_FMT_ALL_LEFT); - break; - } - } - - while (remaining != 0) { - size_t bytes_written; - esp_err_t err = - i2s_write(this_speaker->parent_->get_port(), data, remaining, &bytes_written, (32 / portTICK_PERIOD_MS)); - if (err != ESP_OK) { - event = {.type = TaskEventType::WARNING, .err = err}; - if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) { - ESP_LOGW(TAG, "Failed to send WARNING event"); - } - continue; - } - data += bytes_written; - remaining -= bytes_written; - } - } - - event.type = TaskEventType::STOPPING; - if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) { - ESP_LOGW(TAG, "Failed to send STOPPING event"); - } - - i2s_zero_dma_buffer(this_speaker->parent_->get_port()); - - i2s_driver_uninstall(this_speaker->parent_->get_port()); - - event.type = TaskEventType::STOPPED; - if (xQueueSend(this_speaker->event_queue_, &event, 10 / portTICK_PERIOD_MS) != pdTRUE) { - ESP_LOGW(TAG, "Failed to send STOPPED event"); - } - - while (true) { - delay(10); + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); } } @@ -203,92 +315,169 @@ void I2SAudioSpeaker::stop_(bool wait_on_empty) { return; if (this->state_ == speaker::STATE_STOPPED) return; - if (this->state_ == speaker::STATE_STARTING) { - this->state_ = speaker::STATE_STOPPED; - return; - } - this->state_ = speaker::STATE_STOPPING; - DataEvent data; - data.stop = true; + if (wait_on_empty) { - xQueueSend(this->buffer_queue_, &data, portMAX_DELAY); + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY); } else { - xQueueSendToFront(this->buffer_queue_, &data, portMAX_DELAY); + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP); } } -void I2SAudioSpeaker::watch_() { - TaskEvent event; - if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) { - switch (event.type) { - case TaskEventType::STARTING: - ESP_LOGD(TAG, "Starting I2S Audio Speaker"); - break; - case TaskEventType::STARTED: - ESP_LOGD(TAG, "Started I2S Audio Speaker"); - this->state_ = speaker::STATE_RUNNING; - this->status_clear_warning(); - break; - case TaskEventType::STOPPING: - ESP_LOGD(TAG, "Stopping I2S Audio Speaker"); - break; - case TaskEventType::STOPPED: - this->state_ = speaker::STATE_STOPPED; - vTaskDelete(this->player_task_handle_); - this->task_created_ = false; - this->player_task_handle_ = nullptr; - this->parent_->unlock(); - xQueueReset(this->buffer_queue_); - ESP_LOGD(TAG, "Stopped I2S Audio Speaker"); - break; - case TaskEventType::WARNING: - ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err)); - this->status_set_warning(); - break; - } +bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) { + switch (err) { + case ESP_OK: + return false; + case ESP_ERR_INVALID_STATE: + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_STATE); + return true; + case ESP_ERR_INVALID_ARG: + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_ARG); + return true; + case ESP_ERR_INVALID_SIZE: + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); + return true; + case ESP_ERR_NO_MEM: + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); + return true; + default: + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL); + return true; } } -void I2SAudioSpeaker::loop() { - switch (this->state_) { - case speaker::STATE_STARTING: - this->start_(); - [[fallthrough]]; - case speaker::STATE_RUNNING: - case speaker::STATE_STOPPING: - this->watch_(); - break; - case speaker::STATE_STOPPED: - break; +esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) { + if (this->data_buffer_ == nullptr) { + // Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->data_buffer_ = allocator.allocate(data_buffer_size); } + + if (this->data_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + if (this->audio_ring_buffer_ == nullptr) { + // Allocate ring buffer + this->audio_ring_buffer_ = RingBuffer::create(ring_buffer_size); + } + + if (this->audio_ring_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + return ESP_OK; } -size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length) { - if (this->is_failed()) { - ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup"); - return 0; +esp_err_t I2SAudioSpeaker::start_i2s_driver_() { + if (!this->parent_->try_lock()) { + return ESP_ERR_INVALID_STATE; } - if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) { - this->start(); + + i2s_driver_config_t config = { + .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX), + .sample_rate = this->sample_rate_, + .bits_per_sample = this->bits_per_sample_, + .channel_format = this->channel_, + .communication_format = this->i2s_comm_fmt_, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = DMA_BUFFERS_COUNT, + .dma_buf_len = DMA_BUFFER_SIZE, + .use_apll = this->use_apll_, + .tx_desc_auto_clear = true, + .fixed_mclk = I2S_PIN_NO_CHANGE, + .mclk_multiple = I2S_MCLK_MULTIPLE_256, + .bits_per_chan = this->bits_per_channel_, +#if SOC_I2S_SUPPORTS_TDM + .chan_mask = (i2s_channel_t) (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1), + .total_chan = 2, + .left_align = false, + .big_edin = false, + .bit_order_msb = false, + .skip_msk = false, +#endif + }; +#if SOC_I2S_SUPPORTS_DAC + if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) { + config.mode = (i2s_mode_t) (config.mode | I2S_MODE_DAC_BUILT_IN); } - size_t remaining = length; - size_t index = 0; - while (remaining > 0) { - DataEvent event; - event.stop = false; - size_t to_send_length = std::min(remaining, BUFFER_SIZE); - event.len = to_send_length; - memcpy(event.data, data + index, to_send_length); - if (xQueueSend(this->buffer_queue_, &event, 0) != pdTRUE) { - return index; - } - remaining -= to_send_length; - index += to_send_length; +#endif + + esp_err_t err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr); + if (err != ESP_OK) { + // Failed to install the driver, so unlock the I2S port + this->parent_->unlock(); + return err; } - return index; + +#if SOC_I2S_SUPPORTS_DAC + if (this->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) { +#endif + i2s_pin_config_t pin_config = this->parent_->get_pin_config(); + pin_config.data_out_num = this->dout_pin_; + + err = i2s_set_pin(this->parent_->get_port(), &pin_config); +#if SOC_I2S_SUPPORTS_DAC + } else { + i2s_set_dac_mode(this->internal_dac_mode_); + } +#endif + + if (err != ESP_OK) { + // Failed to set the data out pin, so uninstall the driver and unlock the I2S port + i2s_driver_uninstall(this->parent_->get_port()); + this->parent_->unlock(); + } + + return err; } -bool I2SAudioSpeaker::has_buffered_data() const { return uxQueueMessagesWaiting(this->buffer_queue_) > 0; } +esp_err_t I2SAudioSpeaker::reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info) { + if (this->i2s_mode_ & I2S_MODE_MASTER) { + // ESP controls for the the I2S bus, so adjust the sample rate and bits per sample to match the incoming audio + this->sample_rate_ = audio_stream_info.sample_rate; + this->bits_per_sample_ = (i2s_bits_per_sample_t) audio_stream_info.bits_per_sample; + } else if (this->sample_rate_ != audio_stream_info.sample_rate) { + // Can't reconfigure I2S bus, so the sample rate must match the configured value + return ESP_ERR_INVALID_ARG; + } + + if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) { + // Currently can't handle the case when the incoming audio has more bits per sample than the configured value + return ESP_ERR_INVALID_ARG; + } + + if (audio_stream_info.channels == 1) { + return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_MONO); + } else if (audio_stream_info.channels == 2) { + return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_STEREO); + } + + return ESP_ERR_INVALID_ARG; +} + +void I2SAudioSpeaker::delete_task_(size_t buffer_size) { + if (this->audio_ring_buffer_ != nullptr) { + xEventGroupWaitBits(this->event_group_, + MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE, // Bit message to read + pdFALSE, // Don't clear the bits on exit + pdTRUE, // Don't wait for all the bits, + portMAX_DELAY); // Block indefinitely until a command bit is set + + this->audio_ring_buffer_.reset(); // Deallocates the ring buffer stored in the unique_ptr + this->audio_ring_buffer_ = nullptr; + } + + if (this->data_buffer_ != nullptr) { + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + allocator.deallocate(this->data_buffer_, buffer_size); + this->data_buffer_ = nullptr; + } + + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED); + + this->task_created_ = false; + vTaskDelete(nullptr); +} } // namespace i2s_audio } // namespace esphome diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 9d1817c86f..245f97d1e7 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -5,38 +5,21 @@ #include "../i2s_audio.h" #include -#include -#include +#include +#include + +#include "esphome/components/audio/audio.h" #include "esphome/components/speaker/speaker.h" + #include "esphome/core/component.h" #include "esphome/core/gpio.h" #include "esphome/core/helpers.h" +#include "esphome/core/ring_buffer.h" namespace esphome { namespace i2s_audio { -static const size_t BUFFER_SIZE = 1024; - -enum class TaskEventType : uint8_t { - STARTING = 0, - STARTED, - STOPPING, - STOPPED, - WARNING = 255, -}; - -struct TaskEvent { - TaskEventType type; - esp_err_t err; -}; - -struct DataEvent { - bool stop; - size_t len; - uint8_t data[BUFFER_SIZE]; -}; - class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Component { public: float get_setup_priority() const override { return esphome::setup_priority::LATE; } @@ -55,25 +38,89 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp void stop() override; void finish() override; - size_t play(const uint8_t *data, size_t length) override; + /// @brief Plays the provided audio data. + /// Starts the speaker task, if necessary. Writes the audio data to the ring buffer. + /// @param data Audio data in the format set by the parent speaker classes ``set_audio_stream_info`` method. + /// @param length The length of the audio data in bytes. + /// @param ticks_to_wait The FreeRTOS ticks to wait before writing as much data as possible to the ring buffer. + /// @return The number of bytes that were actually written to the ring buffer. + size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override; + size_t play(const uint8_t *data, size_t length) override { return play(data, length, 0); } bool has_buffered_data() const override; + /// @brief Sets the volume of the speaker. It is implemented as a software volume control. + /// Overrides the default setter to convert the floating point volume to a Q15 fixed-point factor. + /// @param volume + void set_volume(float volume) override; + float get_volume() override { return this->volume_; } + protected: - void start_(); + /// @brief Function for the FreeRTOS task handling audio output. + /// After receiving the COMMAND_START signal, allocates space for the buffers, starts the I2S driver, and reads + /// audio from the ring buffer and writes audio to the I2S port. Stops immmiately after receiving the COMMAND_STOP + /// signal and stops only after the ring buffer is empty after receiving the COMMAND_STOP_GRACEFULLY signal. Stops if + /// the ring buffer hasn't read data for more than timeout_ milliseconds. When stopping, it deallocates the buffers, + /// stops the I2S driver, unlocks the I2S port, and deletes the task. It communicates the state and any errors via + /// event_group_. + /// @param params I2SAudioSpeaker component + static void speaker_task(void *params); + + /// @brief Sends a stop command to the speaker task via event_group_. + /// @param wait_on_empty If false, sends the COMMAND_STOP signal. If true, sends the COMMAND_STOP_GRACEFULLY signal. void stop_(bool wait_on_empty); - void watch_(); - static void player_task(void *params); + /// @brief Sets the corresponding ERR_ESP event group bits. + /// @param err esp_err_t error code. + /// @return True if an ERR_ESP bit is set and false if err == ESP_OK + bool send_esp_err_to_event_group_(esp_err_t err); - TaskHandle_t player_task_handle_{nullptr}; - QueueHandle_t buffer_queue_; - QueueHandle_t event_queue_; + /// @brief Allocates the data buffer and ring buffer + /// @param data_buffer_size Number of bytes to allocate for the data buffer. + /// @param ring_buffer_size Number of bytes to allocate for the ring buffer. + /// @return ESP_ERR_NO_MEM if either buffer fails to allocate + /// ESP_OK if successful + esp_err_t allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size); + + /// @brief Starts the ESP32 I2S driver. + /// Attempts to lock the I2S port, starts the I2S driver, and sets the data out pin. If it fails, it will unlock + /// the I2S port and uninstall the driver, if necessary. + /// @return ESP_ERR_INVALID_STATE if the I2S port is already locked. + /// ESP_ERR_INVALID_ARG if installing the driver or setting the data out pin fails due to a parameter error. + /// ESP_ERR_NO_MEM if the driver fails to install due to a memory allocation error. + /// ESP_FAIL if setting the data out pin fails due to an IO error + /// ESP_OK if successful + esp_err_t start_i2s_driver_(); + + /// @brief Adjusts the I2S driver configuration to match the incoming audio stream. + /// Modifies I2S driver's sample rate, bits per sample, and number of channel settings. If the I2S is in secondary + /// mode, it only modifies the number of channels. + /// @param audio_stream_info Describes the incoming audio stream + /// @return ESP_ERR_INVALID_ARG if there is a parameter error, if there is more than 2 channels in the stream, or if + /// the audio settings are incompatible with the configuration. + /// ESP_ERR_NO_MEM if the driver fails to reconfigure due to a memory allocation error. + /// ESP_OK if successful. + esp_err_t reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info); + + /// @brief Deletes the speaker's task. + /// Deallocates the data_buffer_ and audio_ring_buffer_, if necessary, and deletes the task. Should only be called by + /// the speaker_task itself. + /// @param buffer_size The allocated size of the data_buffer_. + void delete_task_(size_t buffer_size); + + TaskHandle_t speaker_task_handle_{nullptr}; + EventGroupHandle_t event_group_{nullptr}; + + uint8_t *data_buffer_; + std::unique_ptr audio_ring_buffer_; + + uint32_t timeout_; + uint8_t dout_pin_; - uint32_t timeout_{0}; - uint8_t dout_pin_{0}; bool task_created_{false}; + int16_t q15_volume_factor_{INT16_MAX}; + #if SOC_I2S_SUPPORTS_DAC i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE}; #endif diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index d28b726d1f..1bbc0b02ef 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -2,7 +2,7 @@ from esphome import automation from esphome.automation import maybe_simple_id import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_DATA, CONF_ID +from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME from esphome.core import CORE from esphome.coroutine import coroutine_with_priority @@ -23,6 +23,10 @@ StopAction = speaker_ns.class_( FinishAction = speaker_ns.class_( "FinishAction", automation.Action, cg.Parented.template(Speaker) ) +VolumeSetAction = speaker_ns.class_( + "VolumeSetAction", automation.Action, cg.Parented.template(Speaker) +) + IsPlayingCondition = speaker_ns.class_("IsPlayingCondition", automation.Condition) IsStoppedCondition = speaker_ns.class_("IsStoppedCondition", automation.Condition) @@ -90,6 +94,25 @@ automation.register_condition( )(speaker_action) +@automation.register_action( + "speaker.volume_set", + VolumeSetAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(Speaker), + cv.Required(CONF_VOLUME): cv.templatable(cv.percentage), + }, + key=CONF_VOLUME, + ), +) +async def speaker_volume_set_action(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + volume = await cg.templatable(config[CONF_VOLUME], args, float) + cg.add(var.set_volume(volume)) + return var + + @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(speaker_ns.using) diff --git a/esphome/components/speaker/automation.h b/esphome/components/speaker/automation.h index 2716fe6100..9efda011f2 100644 --- a/esphome/components/speaker/automation.h +++ b/esphome/components/speaker/automation.h @@ -34,6 +34,11 @@ template class PlayAction : public Action, public Parente std::vector data_static_{}; }; +template class VolumeSetAction : public Action, public Parented { + TEMPLATABLE_VALUE(float, volume) + void play(Ts... x) override { this->parent_->set_volume(this->volume_.value(x...)); } +}; + template class StopAction : public Action, public Parented { public: void play(Ts... x) override { this->parent_->stop(); } diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 375ccc4e8c..9390e4edb7 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -4,6 +4,12 @@ #include #include +#ifdef USE_ESP32 +#include +#endif + +#include "esphome/components/audio/audio.h" + namespace esphome { namespace speaker { @@ -16,14 +22,33 @@ enum State : uint8_t { class Speaker { public: +#ifdef USE_ESP32 + /// @brief Plays the provided audio data. + /// If the speaker component doesn't implement this method, it falls back to the play method without this parameter. + /// @param data Audio data in the format specified by ``set_audio_stream_info`` method. + /// @param length The length of the audio data in bytes. + /// @param ticks_to_wait The FreeRTOS ticks to wait before writing as much data as possible to the ring buffer. + /// @return The number of bytes that were actually written to the speaker's internal buffer. + virtual size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) { + return this->play(data, length); + }; +#endif + + /// @brief Plays the provided audio data. + /// If the audio stream is not the default defined in "esphome/core/audio.h" and the speaker component implements it, + /// then this should be called after calling ``set_audio_stream_info``. + /// @param data Audio data in the format specified by ``set_audio_stream_info`` method. + /// @param length The length of the audio data in bytes. + /// @return The number of bytes that were actually written to the speaker's internal buffer. virtual size_t play(const uint8_t *data, size_t length) = 0; + size_t play(const std::vector &data) { return this->play(data.data(), data.size()); } virtual void start() = 0; virtual void stop() = 0; // In compare between *STOP()* and *FINISH()*; *FINISH()* will stop after emptying the play buffer, // while *STOP()* will break directly. - // When finish() is not implemented on the plateform component it should just do a normal stop. + // When finish() is not implemented on the platform component it should just do a normal stop. virtual void finish() { this->stop(); } virtual bool has_buffered_data() const = 0; @@ -31,8 +56,18 @@ class Speaker { bool is_running() const { return this->state_ == STATE_RUNNING; } bool is_stopped() const { return this->state_ == STATE_STOPPED; } + // Volume control must be implemented by each speaker component, otherwise it will have no effect. + virtual void set_volume(float volume) { this->volume_ = volume; }; + virtual float get_volume() { return this->volume_; } + + void set_audio_stream_info(const audio::AudioStreamInfo &audio_stream_info) { + this->audio_stream_info_ = audio_stream_info; + } + protected: State state_{STATE_STOPPED}; + audio::AudioStreamInfo audio_stream_info_; + float volume_{1.0f}; }; } // namespace speaker diff --git a/tests/components/speaker/test.esp32-ard.yaml b/tests/components/speaker/test.esp32-ard.yaml index ab20f36eb6..9a24d00f68 100644 --- a/tests/components/speaker/test.esp32-ard.yaml +++ b/tests/components/speaker/test.esp32-ard.yaml @@ -5,6 +5,7 @@ esphome: condition: speaker.is_stopped then: - speaker.play: [0, 1, 2, 3] + - speaker.volume_set: 0.9 - if: condition: speaker.is_playing then: diff --git a/tests/components/speaker/test.esp32-c3-ard.yaml b/tests/components/speaker/test.esp32-c3-ard.yaml index c966f9daa7..f28014337c 100644 --- a/tests/components/speaker/test.esp32-c3-ard.yaml +++ b/tests/components/speaker/test.esp32-c3-ard.yaml @@ -5,6 +5,7 @@ esphome: condition: speaker.is_stopped then: - speaker.play: [0, 1, 2, 3] + - speaker.volume_set: 0.9 - if: condition: speaker.is_playing then: diff --git a/tests/components/speaker/test.esp32-c3-idf.yaml b/tests/components/speaker/test.esp32-c3-idf.yaml index c966f9daa7..f28014337c 100644 --- a/tests/components/speaker/test.esp32-c3-idf.yaml +++ b/tests/components/speaker/test.esp32-c3-idf.yaml @@ -5,6 +5,7 @@ esphome: condition: speaker.is_stopped then: - speaker.play: [0, 1, 2, 3] + - speaker.volume_set: 0.9 - if: condition: speaker.is_playing then: diff --git a/tests/components/speaker/test.esp32-idf.yaml b/tests/components/speaker/test.esp32-idf.yaml index ab20f36eb6..9a24d00f68 100644 --- a/tests/components/speaker/test.esp32-idf.yaml +++ b/tests/components/speaker/test.esp32-idf.yaml @@ -5,6 +5,7 @@ esphome: condition: speaker.is_stopped then: - speaker.play: [0, 1, 2, 3] + - speaker.volume_set: 0.9 - if: condition: speaker.is_playing then: From 0451b31f9eafc6fa78551c78e9b03671d1ba63d8 Mon Sep 17 00:00:00 2001 From: functionpointer Date: Thu, 17 Oct 2024 02:17:20 +0200 Subject: [PATCH 0493/1052] Bump arduino-mlx90393 to 1.0.2 (#7618) --- esphome/components/mlx90393/sensor.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/mlx90393/sensor.py b/esphome/components/mlx90393/sensor.py index 92ba30bea3..fe01d8ebfc 100644 --- a/esphome/components/mlx90393/sensor.py +++ b/esphome/components/mlx90393/sensor.py @@ -132,4 +132,4 @@ async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_DRDY_PIN]) cg.add(var.set_drdy_gpio(pin)) - cg.add_library("functionpointer/arduino-MLX90393", "1.0.0") + cg.add_library("functionpointer/arduino-MLX90393", "1.0.2") diff --git a/platformio.ini b/platformio.ini index bb122adc37..04afc059af 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,7 +38,7 @@ lib_deps = improv/Improv@1.2.4 ; improv_serial / esp32_improv bblanchon/ArduinoJson@6.18.5 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code - functionpointer/arduino-MLX90393@1.0.0 ; mlx90393 + functionpointer/arduino-MLX90393@1.0.2 ; mlx90393 pavlodn/HaierProtocol@0.9.31 ; haier kikuchan98/pngle@1.0.2 ; online_image ; This is using the repository until a new release is published to PlatformIO From c9e59197396c05bc7cc12740adf151a28997f527 Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Thu, 17 Oct 2024 03:31:02 +0300 Subject: [PATCH 0494/1052] [fix] ESP32-C6 BLE compile error (#7580) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32_ble/const_esp32c6.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/esphome/components/esp32_ble/const_esp32c6.h b/esphome/components/esp32_ble/const_esp32c6.h index 69f9adcf6b..89179d8dd9 100644 --- a/esphome/components/esp32_ble/const_esp32c6.h +++ b/esphome/components/esp32_ble/const_esp32c6.h @@ -40,6 +40,9 @@ static const esp_bt_controller_config_t BT_CONTROLLER_CONFIG = { .controller_run_cpu = 0, .enable_qa_test = RUN_QA_TEST, .enable_bqb_test = RUN_BQB_TEST, +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 1) + // The following fields have been removed since ESP IDF version 5.3.1, see commit: + // https://github.com/espressif/esp-idf/commit/e761c1de8f9c0777829d597b4d5a33bb070a30a8 .enable_uart_hci = HCI_UART_EN, .ble_hci_uart_port = DEFAULT_BT_LE_HCI_UART_PORT, .ble_hci_uart_baud = DEFAULT_BT_LE_HCI_UART_BAUD, @@ -47,6 +50,7 @@ static const esp_bt_controller_config_t BT_CONTROLLER_CONFIG = { .ble_hci_uart_stop_bits = DEFAULT_BT_LE_HCI_UART_STOP_BITS, .ble_hci_uart_flow_ctrl = DEFAULT_BT_LE_HCI_UART_FLOW_CTRL, .ble_hci_uart_uart_parity = DEFAULT_BT_LE_HCI_UART_PARITY, +#endif .enable_tx_cca = DEFAULT_BT_LE_TX_CCA_ENABLED, .cca_rssi_thresh = 256 - DEFAULT_BT_LE_CCA_RSSI_THRESH, .sleep_en = NIMBLE_SLEEP_ENABLE, @@ -58,6 +62,9 @@ static const esp_bt_controller_config_t BT_CONTROLLER_CONFIG = { .cpu_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, .ignore_wl_for_direct_adv = 0, .enable_pcl = DEFAULT_BT_LE_POWER_CONTROL_ENABLED, +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 3) + .csa2_select = DEFAULT_BT_LE_50_FEATURE_SUPPORT, +#endif .config_magic = CONFIG_MAGIC, }; From 56fa6fef855437dfe34027388a182c166b6823da Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:32:22 +1100 Subject: [PATCH 0495/1052] [config] Fix crash with empty substitutions block (#7612) --- esphome/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/config.py b/esphome/config.py index a2d0d15477..7d48569d2d 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -782,7 +782,7 @@ def validate_config( from esphome.components import substitutions result[CONF_SUBSTITUTIONS] = { - **config.get(CONF_SUBSTITUTIONS, {}), + **(config.get(CONF_SUBSTITUTIONS) or {}), **command_line_substitutions, } result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS) From 5ad68e926da373d2e24c2d8891a5d21926ba014f Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:44:20 +1100 Subject: [PATCH 0496/1052] [axs15231] Touchscreen driver (#7592) --- CODEOWNERS | 1 + esphome/components/axs15231/__init__.py | 6 ++ .../axs15231/touchscreen/__init__.py | 36 +++++++++++ .../touchscreen/axs15231_touchscreen.cpp | 64 +++++++++++++++++++ .../touchscreen/axs15231_touchscreen.h | 27 ++++++++ tests/components/axs15231/common.yaml | 20 ++++++ tests/components/axs15231/test.esp32-ard.yaml | 1 + .../axs15231/test.esp32-c3-ard.yaml | 1 + .../axs15231/test.esp32-c3-idf.yaml | 1 + tests/components/axs15231/test.esp32-idf.yaml | 1 + .../components/axs15231/test.esp8266-ard.yaml | 19 ++++++ .../components/axs15231/test.rp2040-ard.yaml | 1 + 12 files changed, 178 insertions(+) create mode 100644 esphome/components/axs15231/__init__.py create mode 100644 esphome/components/axs15231/touchscreen/__init__.py create mode 100644 esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp create mode 100644 esphome/components/axs15231/touchscreen/axs15231_touchscreen.h create mode 100644 tests/components/axs15231/common.yaml create mode 100644 tests/components/axs15231/test.esp32-ard.yaml create mode 100644 tests/components/axs15231/test.esp32-c3-ard.yaml create mode 100644 tests/components/axs15231/test.esp32-c3-idf.yaml create mode 100644 tests/components/axs15231/test.esp32-idf.yaml create mode 100644 tests/components/axs15231/test.esp8266-ard.yaml create mode 100644 tests/components/axs15231/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 7ac6aa2f76..53300d6740 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -50,6 +50,7 @@ esphome/components/atm90e26/* @danieltwagner esphome/components/atm90e32/* @circuitsetup @descipher esphome/components/audio/* @kahrendt esphome/components/audio_dac/* @kbx81 +esphome/components/axs15231/* @clydebarrow esphome/components/b_parasite/* @rbaron esphome/components/ballu/* @bazuchan esphome/components/bang_bang/* @OttoWinter diff --git a/esphome/components/axs15231/__init__.py b/esphome/components/axs15231/__init__.py new file mode 100644 index 0000000000..3246dbed24 --- /dev/null +++ b/esphome/components/axs15231/__init__.py @@ -0,0 +1,6 @@ +import esphome.codegen as cg + +CODEOWNERS = ["@clydebarrow"] +DEPENDENCIES = ["i2c"] + +axs15231_ns = cg.esphome_ns.namespace("axs15231") diff --git a/esphome/components/axs15231/touchscreen/__init__.py b/esphome/components/axs15231/touchscreen/__init__.py new file mode 100644 index 0000000000..8c18d8ca75 --- /dev/null +++ b/esphome/components/axs15231/touchscreen/__init__.py @@ -0,0 +1,36 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components import i2c, touchscreen +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_RESET_PIN + +from .. import axs15231_ns + +AXS15231Touchscreen = axs15231_ns.class_( + "AXS15231Touchscreen", + touchscreen.Touchscreen, + i2c.I2CDevice, +) + +CONFIG_SCHEMA = ( + touchscreen.touchscreen_schema("50ms") + .extend( + { + cv.GenerateID(): cv.declare_id(AXS15231Touchscreen), + cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + } + ) + .extend(i2c.i2c_device_schema(0x3B)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await touchscreen.register_touchscreen(var, config) + await i2c.register_i2c_device(var, config) + + if interrupt_pin := config.get(CONF_INTERRUPT_PIN): + cg.add(var.set_interrupt_pin(await cg.gpio_pin_expression(interrupt_pin))) + if reset_pin := config.get(CONF_RESET_PIN): + cg.add(var.set_reset_pin(await cg.gpio_pin_expression(reset_pin))) diff --git a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp new file mode 100644 index 0000000000..54b39a6bb9 --- /dev/null +++ b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp @@ -0,0 +1,64 @@ +#include "axs15231_touchscreen.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace axs15231 { + +static const char *const TAG = "ax15231.touchscreen"; + +constexpr static const uint8_t AXS_READ_TOUCHPAD[11] = {0xb5, 0xab, 0xa5, 0x5a, 0x0, 0x0, 0x0, 0x8}; + +#define ERROR_CHECK(err) \ + if ((err) != i2c::ERROR_OK) { \ + this->status_set_warning("Failed to communicate"); \ + return; \ + } + +void AXS15231Touchscreen::setup() { + ESP_LOGCONFIG(TAG, "Setting up AXS15231 Touchscreen..."); + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(false); + delay(5); + this->reset_pin_->digital_write(true); + delay(10); + } + if (this->interrupt_pin_ != nullptr) { + this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT); + this->interrupt_pin_->setup(); + this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); + } + this->x_raw_max_ = this->display_->get_native_width(); + this->y_raw_max_ = this->display_->get_native_height(); + ESP_LOGCONFIG(TAG, "AXS15231 Touchscreen setup complete"); +} + +void AXS15231Touchscreen::update_touches() { + i2c::ErrorCode err; + uint8_t data[8]{}; + + err = this->write(AXS_READ_TOUCHPAD, sizeof(AXS_READ_TOUCHPAD), false); + ERROR_CHECK(err); + err = this->read(data, sizeof(data)); + ERROR_CHECK(err); + this->status_clear_warning(); + if (data[0] != 0) // no touches + return; + uint16_t x = encode_uint16(data[2] & 0xF, data[3]); + uint16_t y = encode_uint16(data[4] & 0xF, data[5]); + this->add_raw_touch_position_(0, x, y); +} + +void AXS15231Touchscreen::dump_config() { + ESP_LOGCONFIG(TAG, "AXS15231 Touchscreen:"); + 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_); +} + +} // namespace axs15231 +} // namespace esphome diff --git a/esphome/components/axs15231/touchscreen/axs15231_touchscreen.h b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.h new file mode 100644 index 0000000000..a55c5c0d32 --- /dev/null +++ b/esphome/components/axs15231/touchscreen/axs15231_touchscreen.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/components/touchscreen/touchscreen.h" +#include "esphome/core/component.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace axs15231 { + +class AXS15231Touchscreen : public touchscreen::Touchscreen, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + + void set_interrupt_pin(InternalGPIOPin *pin) { this->interrupt_pin_ = pin; } + void set_reset_pin(GPIOPin *pin) { this->reset_pin_ = pin; } + + protected: + void update_touches() override; + + InternalGPIOPin *interrupt_pin_{}; + GPIOPin *reset_pin_{}; +}; + +} // namespace axs15231 +} // namespace esphome diff --git a/tests/components/axs15231/common.yaml b/tests/components/axs15231/common.yaml new file mode 100644 index 0000000000..1c0c79975f --- /dev/null +++ b/tests/components/axs15231/common.yaml @@ -0,0 +1,20 @@ +i2c: + - id: i2c_axs15231 + scl: 3 + sda: 21 + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: 19 + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: axs15231 + display: ssd1306_display + interrupt_pin: 20 + reset_pin: 18 diff --git a/tests/components/axs15231/test.esp32-ard.yaml b/tests/components/axs15231/test.esp32-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/axs15231/test.esp32-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/axs15231/test.esp32-c3-ard.yaml b/tests/components/axs15231/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/axs15231/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/axs15231/test.esp32-c3-idf.yaml b/tests/components/axs15231/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/axs15231/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/axs15231/test.esp32-idf.yaml b/tests/components/axs15231/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/axs15231/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/axs15231/test.esp8266-ard.yaml b/tests/components/axs15231/test.esp8266-ard.yaml new file mode 100644 index 0000000000..c09d139574 --- /dev/null +++ b/tests/components/axs15231/test.esp8266-ard.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_axs15231 + scl: 5 + sda: 4 + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: 13 + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: axs15231 + display: ssd1306_display + interrupt_pin: 12 diff --git a/tests/components/axs15231/test.rp2040-ard.yaml b/tests/components/axs15231/test.rp2040-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/axs15231/test.rp2040-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From fcfc76b01bed8cfe7d0dad846b74dca8da201e74 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:03:48 +1100 Subject: [PATCH 0497/1052] [lvgl] Roller and Dropdown enhancements; (#7608) --- esphome/components/lvgl/__init__.py | 3 +- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/lvgl_esphome.cpp | 35 ++++++++++++ esphome/components/lvgl/lvgl_esphome.h | 50 ++++++++++++++-- esphome/components/lvgl/schemas.py | 2 +- esphome/components/lvgl/select/__init__.py | 30 ++-------- esphome/components/lvgl/select/lvgl_select.h | 60 +++++++++----------- esphome/components/lvgl/trigger.py | 4 +- esphome/components/lvgl/types.py | 10 +++- esphome/components/lvgl/widgets/__init__.py | 18 ++++-- esphome/components/lvgl/widgets/dropdown.py | 32 +++++++---- esphome/components/lvgl/widgets/obj.py | 6 +- esphome/components/lvgl/widgets/roller.py | 35 +++++++----- esphome/components/lvgl/widgets/tileview.py | 3 +- esphome/core/defines.h | 2 + tests/components/lvgl/lvgl-package.yaml | 34 ++++++++--- 16 files changed, 218 insertions(+), 107 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index dea3b11a94..86fdc7d763 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -271,7 +271,8 @@ async def to_code(config): await disp_update(f"{lv_component}->get_disp()", config) # At this point only the setup code should be generated assert LvContext.added_lambda_count == 1 - Widget.set_completed() + # Set this directly since we are limited in how many methods can be added to the Widget class. + Widget.widgets_completed = True async with LvContext(lv_component): await generate_triggers(lv_component) await generate_page_triggers(lv_component, config) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 7c42ed2f22..c8ece02677 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -477,6 +477,7 @@ CONF_ROWS = "rows" CONF_SCALE_LINES = "scale_lines" CONF_SCROLLBAR_MODE = "scrollbar_mode" CONF_SELECTED_INDEX = "selected_index" +CONF_SELECTED_TEXT = "selected_text" CONF_SHOW_SNOW = "show_snow" CONF_SPIN_TIME = "spin_time" CONF_SRC = "src" diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index ddf41ae377..5a6c66c677 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -5,6 +5,8 @@ #include "lvgl_hal.h" #include "lvgl_esphome.h" +#include + namespace esphome { namespace lvgl { static const char *const TAG = "lvgl"; @@ -263,6 +265,39 @@ LVEncoderListener::LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_ } #endif // USE_LVGL_KEY_LISTENER +#if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER) +std::string LvSelectable::get_selected_text() { + auto selected = this->get_selected_index(); + if (selected >= this->options_.size()) + return ""; + return this->options_[selected]; +} + +static std::string join_string(std::vector options) { + return std::accumulate( + options.begin(), options.end(), std::string(), + [](const std::string &a, const std::string &b) -> std::string { return a + (a.length() > 0 ? "\n" : "") + b; }); +} + +void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t anim) { + auto index = std::find(this->options_.begin(), this->options_.end(), text); + if (index != this->options_.end()) { + this->set_selected_index(index - this->options_.begin(), anim); + lv_event_send(this->obj, lv_api_event, nullptr); + } +} + +void LvSelectable::set_options(std::vector options) { + auto index = this->get_selected_index(); + if (index >= options.size()) + index = options.size() - 1; + this->options_ = std::move(options); + this->set_option_string(join_string(this->options_).c_str()); + lv_event_send(this->obj, LV_EVENT_REFRESH, nullptr); + this->set_selected_index(index, LV_ANIM_OFF); +} +#endif // USE_LVGL_DROPDOWN || LV_USE_ROLLER + #ifdef USE_LVGL_BUTTONMATRIX void LvButtonMatrixType::set_obj(lv_obj_t *lv_obj) { LvCompound::set_obj(lv_obj); diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index b28a9bcbe1..2d326f4ae2 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -18,11 +18,9 @@ #include "esphome/core/component.h" #include "esphome/core/log.h" #include -#include #include -#ifdef USE_LVGL_IMAGE -#include "esphome/components/image/image.h" -#endif // USE_LVGL_IMAGE +#include +#include #ifdef USE_LVGL_FONT #include "esphome/components/font/font.h" @@ -246,6 +244,7 @@ class LVEncoderListener : public Parented { public: LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt); +#ifdef USE_BINARY_SENSOR void set_left_button(binary_sensor::BinarySensor *left_button) { left_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_LEFT, state); }); } @@ -256,6 +255,7 @@ class LVEncoderListener : public Parented { void set_enter_button(binary_sensor::BinarySensor *enter_button) { enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); }); } +#endif #ifdef USE_LVGL_ROTARY_ENCODER void set_sensor(rotary_encoder::RotaryEncoderSensor *sensor) { @@ -292,6 +292,48 @@ class LVEncoderListener : public Parented { }; #endif // USE_LVGL_KEY_LISTENER +#if defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER) +class LvSelectable : public LvCompound { + public: + virtual size_t get_selected_index() = 0; + virtual void set_selected_index(size_t index, lv_anim_enable_t anim) = 0; + void set_selected_text(const std::string &text, lv_anim_enable_t anim); + std::string get_selected_text(); + std::vector get_options() { return this->options_; } + void set_options(std::vector options); + + protected: + virtual void set_option_string(const char *options) = 0; + std::vector options_{}; +}; + +#ifdef USE_LVGL_DROPDOWN +class LvDropdownType : public LvSelectable { + public: + size_t get_selected_index() override { return lv_dropdown_get_selected(this->obj); } + void set_selected_index(size_t index, lv_anim_enable_t anim) override { lv_dropdown_set_selected(this->obj, index); } + + protected: + void set_option_string(const char *options) override { lv_dropdown_set_options(this->obj, options); } +}; +#endif // USE_LVGL_DROPDOWN + +#ifdef USE_LVGL_ROLLER +class LvRollerType : public LvSelectable { + public: + size_t get_selected_index() override { return lv_roller_get_selected(this->obj); } + void set_selected_index(size_t index, lv_anim_enable_t anim) override { + lv_roller_set_selected(this->obj, index, anim); + } + void set_mode(lv_roller_mode_t mode) { this->mode_ = mode; } + + protected: + void set_option_string(const char *options) override { lv_roller_set_options(this->obj, options, this->mode_); } + lv_roller_mode_t mode_{LV_ROLLER_MODE_NORMAL}; +}; +#endif +#endif // defined(USE_LVGL_DROPDOWN) || defined(LV_USE_ROLLER) + #ifdef USE_LVGL_BUTTONMATRIX class LvButtonMatrixType : public key_provider::KeyProvider, public LvCompound { public: diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 780057623a..7599d64416 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -216,7 +216,7 @@ def automation_schema(typ: LvType): events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,) else: events = df.LV_EVENT_TRIGGERS - args = [typ.get_arg_type()] if isinstance(typ, LvType) else [] + args = typ.get_arg_type() if isinstance(typ, LvType) else [] args.append(lv_event_t_ptr) return { cv.Optional(event): validate_automation( diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py index 73ac50aa55..5e50b6b385 100644 --- a/esphome/components/lvgl/select/__init__.py +++ b/esphome/components/lvgl/select/__init__.py @@ -3,18 +3,10 @@ from esphome.components import select import esphome.config_validation as cv from esphome.const import CONF_OPTIONS -from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET -from ..lvcode import ( - API_EVENT, - EVENT_ARG, - UPDATE_EVENT, - LambdaContext, - LvContext, - lv, - lv_add, -) +from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET, literal +from ..lvcode import LvContext from ..schemas import LVGL_SCHEMA -from ..types import LV_EVENT, LvSelect, lvgl_ns +from ..types import LvSelect, lvgl_ns from ..widgets import get_widgets, wait_for_widgets LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) @@ -38,20 +30,10 @@ async def to_code(config): selector = await select.new_select(config, options=options) paren = await cg.get_variable(config[CONF_LVGL_ID]) await wait_for_widgets() - async with LambdaContext(EVENT_ARG) as pub_ctx: - pub_ctx.add(selector.publish_index(widget.get_value())) - async with LambdaContext([(cg.uint16, "v")]) as control: - await widget.set_property("selected", "v", animated=config[CONF_ANIMATED]) - lv.event_send(widget.obj, API_EVENT, cg.nullptr) - control.add(selector.publish_index(widget.get_value())) async with LvContext(paren) as ctx: - lv_add(selector.set_control_lambda(await control.get_lambda())) ctx.add( - paren.add_event_cb( - widget.obj, - await pub_ctx.get_lambda(), - LV_EVENT.VALUE_CHANGED, - UPDATE_EVENT, + selector.set_widget( + widget.var, + literal("LV_ANIM_ON" if config[CONF_ANIMATED] else "LV_ANIM_OFF"), ) ) - lv_add(selector.publish_index(widget.get_value())) diff --git a/esphome/components/lvgl/select/lvgl_select.h b/esphome/components/lvgl/select/lvgl_select.h index 97cc8697eb..4538e339c3 100644 --- a/esphome/components/lvgl/select/lvgl_select.h +++ b/esphome/components/lvgl/select/lvgl_select.h @@ -6,58 +6,52 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "../lvgl.h" namespace esphome { namespace lvgl { -static std::vector split_string(const std::string &str) { - std::vector strings; - auto delimiter = std::string("\n"); - - std::string::size_type pos; - std::string::size_type prev = 0; - while ((pos = str.find(delimiter, prev)) != std::string::npos) { - strings.push_back(str.substr(prev, pos - prev)); - prev = pos + delimiter.size(); - } - - // To get the last substring (or only, if delimiter is not found) - strings.push_back(str.substr(prev)); - - return strings; -} - class LVGLSelect : public select::Select { public: - void set_control_lambda(std::function lambda) { - this->control_lambda_ = std::move(lambda); + void set_widget(LvSelectable *widget, lv_anim_enable_t anim = LV_ANIM_OFF) { + this->widget_ = widget; + this->anim_ = anim; + this->set_options_(); + lv_obj_add_event_cb( + this->widget_->obj, + [](lv_event_t *e) { + auto *it = static_cast(e->user_data); + it->set_options_(); + }, + LV_EVENT_REFRESH, this); if (this->initial_state_.has_value()) { this->control(this->initial_state_.value()); this->initial_state_.reset(); } + this->publish(); + auto lamb = [](lv_event_t *e) { + auto *self = static_cast(e->user_data); + self->publish(); + }; + lv_obj_add_event_cb(this->widget_->obj, lamb, LV_EVENT_VALUE_CHANGED, this); + lv_obj_add_event_cb(this->widget_->obj, lamb, lv_update_event, this); } - void publish_index(size_t index) { - auto value = this->at(index); - if (value) - this->publish_state(value.value()); - } - - void set_options(const char *str) { this->traits.set_options(split_string(str)); } + void publish() { this->publish_state(this->widget_->get_selected_text()); } protected: void control(const std::string &value) override { - if (this->control_lambda_ != nullptr) { - auto index = index_of(value); - if (index) - this->control_lambda_(index.value()); + if (this->widget_ != nullptr) { + this->widget_->set_selected_text(value, this->anim_); } else { - this->initial_state_ = value.c_str(); + this->initial_state_ = value; } } + void set_options_() { this->traits.set_options(this->widget_->get_options()); } - std::function control_lambda_{}; - optional initial_state_{}; + LvSelectable *widget_{}; + optional initial_state_{}; + lv_anim_enable_t anim_{LV_ANIM_OFF}; }; } // namespace lvgl diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index 5288745fab..eb6e483203 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -67,9 +67,9 @@ async def add_trigger(conf, lv_component, w, *events): tid = conf[CONF_TRIGGER_ID] trigger = cg.new_Pvariable(tid) args = w.get_args() + [(lv_event_t_ptr, "event")] - value = w.get_value() + value = w.get_values() await automation.build_automation(trigger, args, conf) async with LambdaContext(EVENT_ARG, where=tid) as context: with LvConditional(w.is_selected()): - lv_add(trigger.trigger(value, literal("event"))) + lv_add(trigger.trigger(*value, literal("event"))) lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events)) diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index 2d10b67c2d..b504f24674 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -18,7 +18,9 @@ class LvType(cg.MockObjClass): self.value_property = None def get_arg_type(self): - return self.args[0][0] if len(self.args) else None + if len(self.args) == 0: + return None + return [arg[0] for arg in self.args] class LvNumber(LvType): @@ -92,11 +94,13 @@ class LvBoolean(LvType): class LvSelect(LvType): def __init__(self, *args, **kwargs): + parens = kwargs.pop("parents", ()) + (LvCompound,) super().__init__( *args, - largs=[(cg.int_, "x")], - lvalue=lambda w: w.get_property("selected"), + largs=[(cg.int_, "x"), (cg.std_string, "text")], + lvalue=lambda w: [w.var.get_selected_index(), w.var.get_selected_text()], has_on_value=True, + parents=parens, **kwargs, ) diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 533ffdea55..35ee6c54e8 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -70,14 +70,11 @@ class LvScrActType(WidgetType): class Widget: """ Represents a Widget. + This class has a lot of methods. Adding any more runs foul of lint checks ("too many public methods"). """ widgets_completed = False - @staticmethod - def set_completed(): - Widget.widgets_completed = True - def __init__(self, var, wtype: WidgetType, config: dict = None): self.var = var self.type = wtype @@ -179,9 +176,20 @@ class Widget: def get_value(self): if isinstance(self.type.w_type, LvType): - return self.type.w_type.value(self) + result = self.type.w_type.value(self) + if isinstance(result, list): + return result[0] + return result return self.obj + def get_values(self): + if isinstance(self.type.w_type, LvType): + result = self.type.w_type.value(self) + if isinstance(result, list): + return result + return [result] + return [self.obj] + def get_number_value(self): value = self.type.mock_obj.get_value(self.obj) if self.scale == 1.0: diff --git a/esphome/components/lvgl/widgets/dropdown.py b/esphome/components/lvgl/widgets/dropdown.py index 4fd7d8a7ee..a6bfc6bb88 100644 --- a/esphome/components/lvgl/widgets/dropdown.py +++ b/esphome/components/lvgl/widgets/dropdown.py @@ -1,4 +1,3 @@ -import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_OPTIONS @@ -9,21 +8,24 @@ from ..defines import ( CONF_SCROLLBAR, CONF_SELECTED, CONF_SELECTED_INDEX, + CONF_SELECTED_TEXT, CONF_SYMBOL, DIRECTIONS, literal, ) +from ..helpers import lvgl_components_required from ..lv_validation import lv_int, lv_text, option_string -from ..lvcode import LocalVariable, lv, lv_expr +from ..lvcode import LocalVariable, lv, lv_add, lv_expr from ..schemas import part_schema -from ..types import LvSelect, LvType, lv_obj_t +from ..types import LvCompound, LvSelect, LvType, lv_obj_t from . import Widget, WidgetType, set_obj_properties from .label import CONF_LABEL CONF_DROPDOWN = "dropdown" CONF_DROPDOWN_LIST = "dropdown_list" -lv_dropdown_t = LvSelect("lv_dropdown_t") +lv_dropdown_t = LvSelect("LvDropdownType", parents=(LvCompound,)) + lv_dropdown_list_t = LvType("lv_dropdown_list_t") dropdown_list_spec = WidgetType( CONF_DROPDOWN_LIST, lv_dropdown_list_t, (CONF_MAIN, CONF_SELECTED, CONF_SCROLLBAR) @@ -32,7 +34,8 @@ dropdown_list_spec = WidgetType( DROPDOWN_BASE_SCHEMA = cv.Schema( { cv.Optional(CONF_SYMBOL): lv_text, - cv.Optional(CONF_SELECTED_INDEX): cv.templatable(cv.int_), + cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int, + cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text, cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of, cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec), } @@ -44,6 +47,12 @@ DROPDOWN_SCHEMA = DROPDOWN_BASE_SCHEMA.extend( } ) +DROPDOWN_UPDATE_SCHEMA = DROPDOWN_BASE_SCHEMA.extend( + { + cv.Optional(CONF_OPTIONS): cv.ensure_list(option_string), + } +) + class DropdownType(WidgetType): def __init__(self): @@ -52,18 +61,21 @@ class DropdownType(WidgetType): lv_dropdown_t, (CONF_MAIN, CONF_INDICATOR), DROPDOWN_SCHEMA, - DROPDOWN_BASE_SCHEMA, + modify_schema=DROPDOWN_UPDATE_SCHEMA, ) async def to_code(self, w: Widget, config): + lvgl_components_required.add(CONF_DROPDOWN) if options := config.get(CONF_OPTIONS): - text = cg.safe_exp("\n".join(options)) - lv.dropdown_set_options(w.obj, text) + lv_add(w.var.set_options(options)) if symbol := config.get(CONF_SYMBOL): - lv.dropdown_set_symbol(w.obj, await lv_text.process(symbol)) + lv.dropdown_set_symbol(w.var.obj, await lv_text.process(symbol)) if (selected := config.get(CONF_SELECTED_INDEX)) is not None: value = await lv_int.process(selected) - lv.dropdown_set_selected(w.obj, value) + lv_add(w.var.set_selected_index(value, literal("LV_ANIM_OFF"))) + if (selected := config.get(CONF_SELECTED_TEXT)) is not None: + value = await lv_text.process(selected) + lv_add(w.var.set_selected_text(value, literal("LV_ANIM_OFF"))) if dirn := config.get(CONF_DIR): lv.dropdown_set_dir(w.obj, literal(dirn)) if dlist := config.get(CONF_DROPDOWN_LIST): diff --git a/esphome/components/lvgl/widgets/obj.py b/esphome/components/lvgl/widgets/obj.py index 20a24c86f6..afb4c97f33 100644 --- a/esphome/components/lvgl/widgets/obj.py +++ b/esphome/components/lvgl/widgets/obj.py @@ -1,7 +1,7 @@ from esphome import automation from ..automation import update_to_code -from ..defines import CONF_MAIN, CONF_OBJ +from ..defines import CONF_MAIN, CONF_OBJ, CONF_SCROLLBAR from ..schemas import create_modify_schema from ..types import ObjUpdateAction, WidgetType, lv_obj_t @@ -12,7 +12,9 @@ class ObjType(WidgetType): """ def __init__(self): - super().__init__(CONF_OBJ, lv_obj_t, (CONF_MAIN,), schema={}, modify_schema={}) + super().__init__( + CONF_OBJ, lv_obj_t, (CONF_MAIN, CONF_SCROLLBAR), schema={}, modify_schema={} + ) async def to_code(self, w, config): return [] diff --git a/esphome/components/lvgl/widgets/roller.py b/esphome/components/lvgl/widgets/roller.py index 50fdf6113c..6f9fee47d4 100644 --- a/esphome/components/lvgl/widgets/roller.py +++ b/esphome/components/lvgl/widgets/roller.py @@ -1,4 +1,3 @@ -import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_MODE, CONF_OPTIONS @@ -7,36 +6,40 @@ from ..defines import ( CONF_MAIN, CONF_SELECTED, CONF_SELECTED_INDEX, + CONF_SELECTED_TEXT, CONF_VISIBLE_ROW_COUNT, ROLLER_MODES, literal, ) -from ..lv_validation import animated, lv_int, option_string -from ..lvcode import lv +from ..helpers import lvgl_components_required +from ..lv_validation import animated, lv_int, lv_text, option_string +from ..lvcode import lv_add from ..types import LvSelect from . import WidgetType from .label import CONF_LABEL CONF_ROLLER = "roller" -lv_roller_t = LvSelect("lv_roller_t") +lv_roller_t = LvSelect("LvRollerType") ROLLER_BASE_SCHEMA = cv.Schema( { - cv.Optional(CONF_SELECTED_INDEX): cv.templatable(cv.int_), + cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int, + cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text, cv.Optional(CONF_VISIBLE_ROW_COUNT): lv_int, + cv.Optional(CONF_MODE): ROLLER_MODES.one_of, } ) ROLLER_SCHEMA = ROLLER_BASE_SCHEMA.extend( { cv.Required(CONF_OPTIONS): cv.ensure_list(option_string), - cv.Optional(CONF_MODE, default="NORMAL"): ROLLER_MODES.one_of, } ) ROLLER_MODIFY_SCHEMA = ROLLER_BASE_SCHEMA.extend( { cv.Optional(CONF_ANIMATED, default=True): animated, + cv.Optional(CONF_OPTIONS): cv.ensure_list(option_string), } ) @@ -52,15 +55,19 @@ class RollerType(WidgetType): ) async def to_code(self, w, config): + lvgl_components_required.add(CONF_ROLLER) + if mode := config.get(CONF_MODE): + mode = await ROLLER_MODES.process(mode) + lv_add(w.var.set_mode(mode)) if options := config.get(CONF_OPTIONS): - mode = await ROLLER_MODES.process(config[CONF_MODE]) - text = cg.safe_exp("\n".join(options)) - lv.roller_set_options(w.obj, text, mode) - animopt = literal(config.get(CONF_ANIMATED) or "LV_ANIM_OFF") - if CONF_SELECTED_INDEX in config: - if selected := config[CONF_SELECTED_INDEX]: - value = await lv_int.process(selected) - lv.roller_set_selected(w.obj, value, animopt) + lv_add(w.var.set_options(options)) + animopt = literal(config.get(CONF_ANIMATED, "LV_ANIM_OFF")) + if (selected := config.get(CONF_SELECTED_INDEX)) is not None: + value = await lv_int.process(selected) + lv_add(w.var.set_selected_index(value, animopt)) + if (selected := config.get(CONF_SELECTED_TEXT)) is not None: + value = await lv_text.process(selected) + lv_add(w.var.set_selected_text(value, animopt)) await w.set_property( CONF_VISIBLE_ROW_COUNT, await lv_int.process(config.get(CONF_VISIBLE_ROW_COUNT)), diff --git a/esphome/components/lvgl/widgets/tileview.py b/esphome/components/lvgl/widgets/tileview.py index 05259fbd3c..3865d404e2 100644 --- a/esphome/components/lvgl/widgets/tileview.py +++ b/esphome/components/lvgl/widgets/tileview.py @@ -9,6 +9,7 @@ from ..defines import ( CONF_COLUMN, CONF_DIR, CONF_MAIN, + CONF_SCROLLBAR, CONF_TILE_ID, CONF_TILES, TILE_DIRECTIONS, @@ -56,7 +57,7 @@ class TileviewType(WidgetType): super().__init__( CONF_TILEVIEW, lv_tileview_t, - (CONF_MAIN,), + (CONF_MAIN, CONF_SCROLLBAR), schema=TILEVIEW_SCHEMA, modify_schema={}, ) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index ca3db0ad56..b5511b57eb 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -44,10 +44,12 @@ #define USE_LVGL_ANIMIMG #define USE_LVGL_BINARY_SENSOR #define USE_LVGL_BUTTONMATRIX +#define USE_LVGL_DROPDOWN #define USE_LVGL_FONT #define USE_LVGL_IMAGE #define USE_LVGL_KEY_LISTENER #define USE_LVGL_KEYBOARD +#define USE_LVGL_ROLLER #define USE_LVGL_ROTARY_ENCODER #define USE_LVGL_TOUCHSCREEN #define USE_MD5 diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 6aea606ac4..1f09bc22eb 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -138,6 +138,19 @@ lvgl: flex_align_cross: start flex_align_track: end widgets: + - roller: + id: lv_roller + visible_row_count: 2 + anim_time: 500ms + options: + - Nov + - Dec + selected_index: 1 + on_value: + then: + - logger.log: + format: "Roller changed = %d: %s" + args: [x, text.c_str()] - animimg: height: 60 id: anim_img @@ -245,9 +258,13 @@ lvgl: y: 120 - buttonmatrix: on_press: - logger.log: - format: "matrix button pressed: %d" - args: ["x"] + then: + - logger.log: + format: "matrix button pressed: %d" + args: ["x"] + - lvgl.widget.show: b_matrix + - lvgl.widget.redraw: + on_long_press: lvgl.matrix.button.update: id: [button_a, button_e, button_c] @@ -629,8 +646,6 @@ lvgl: - First - Second - Third - - 4th - - 5th - 6th - 7th - 8th @@ -651,8 +666,8 @@ lvgl: bg_color: 0xFF on_value: logger.log: - format: "Dropdown changed = %d" - args: [x] + format: "Dropdown changed = %d: %s" + args: [x, text.c_str()] on_cancel: logger.log: format: "Dropdown closed = %d" @@ -661,6 +676,11 @@ lvgl: src: cat_image on_click: then: + - lvgl.dropdown.update: + id: lv_dropdown + options: + ["First", "Second", "Third", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th"] + selected_index: 3 - logger.log: Cat image clicked - lvgl.tabview.select: id: tabview_id From f490585f66a653787f015384c27e2c7e50de214d Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 17 Oct 2024 03:38:02 +0200 Subject: [PATCH 0498/1052] [code-quality] udp component (#7602) Co-authored-by: Tomasz Duda Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/udp/udp_component.cpp | 9 ++++++--- esphome/components/udp/udp_component.h | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index 799ed813d3..a1c8889997 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -261,7 +261,8 @@ void UDPComponent::setup() { return; } } -#else +#endif +#ifdef USE_SOCKET_IMPL_LWIP_TCP // 8266 and RP2040 `Duino for (const auto &address : this->addresses_) { auto ipaddr = IPAddress(); @@ -370,7 +371,8 @@ void UDPComponent::loop() { for (;;) { #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) auto len = this->listen_socket_->read(buf, sizeof(buf)); -#else +#endif +#ifdef USE_SOCKET_IMPL_LWIP_TCP auto len = this->udp_client_.parsePacket(); if (len > 0) len = this->udp_client_.read(buf, sizeof(buf)); @@ -587,7 +589,8 @@ void UDPComponent::send_packet_(void *data, size_t len) { if (result < 0) ESP_LOGW(TAG, "sendto() error %d", errno); } -#else +#endif +#ifdef USE_SOCKET_IMPL_LWIP_TCP auto iface = IPAddress(0, 0, 0, 0); for (const auto &saddr : this->ipaddrs_) { if (this->udp_client_.beginPacketMulticast(saddr, this->port_, iface, 128) != 0) { diff --git a/esphome/components/udp/udp_component.h b/esphome/components/udp/udp_component.h index 69bf335a90..b4e11cf652 100644 --- a/esphome/components/udp/udp_component.h +++ b/esphome/components/udp/udp_component.h @@ -9,7 +9,8 @@ #endif #if defined(USE_SOCKET_IMPL_BSD_SOCKETS) || defined(USE_SOCKET_IMPL_LWIP_SOCKETS) #include "esphome/components/socket/socket.h" -#else +#endif +#ifdef USE_SOCKET_IMPL_LWIP_TCP #include #endif #include @@ -125,7 +126,8 @@ class UDPComponent : public PollingComponent { std::unique_ptr broadcast_socket_ = nullptr; std::unique_ptr listen_socket_ = nullptr; std::vector sockaddrs_{}; -#else +#endif +#ifdef USE_SOCKET_IMPL_LWIP_TCP std::vector ipaddrs_{}; WiFiUDP udp_client_{}; #endif From 8bbe4efded8803f0686eb4c71bf9b980a1a67456 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:20:19 +1100 Subject: [PATCH 0499/1052] [lvgl] Revise code generation to allow early widget creation (#7611) --- esphome/components/lvgl/__init__.py | 58 +++++++------ esphome/components/lvgl/automation.py | 32 ++++--- esphome/components/lvgl/defines.py | 16 ++-- esphome/components/lvgl/lvcode.py | 8 +- esphome/components/lvgl/lvgl_esphome.cpp | 96 ++++++++++++--------- esphome/components/lvgl/lvgl_esphome.h | 20 ++--- esphome/components/lvgl/styles.py | 7 +- esphome/components/lvgl/widgets/__init__.py | 35 ++++---- esphome/components/lvgl/widgets/msgbox.py | 11 +-- tests/components/lvgl/lvgl-package.yaml | 4 +- 10 files changed, 155 insertions(+), 132 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 86fdc7d763..beaf279a9a 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -22,7 +22,7 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, focused_widgets, update_to_code -from .defines import CONF_WIDGETS, add_define +from .defines import add_define from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code from .gradient import GRADIENT_SCHEMA, gradients_to_code from .hello_world import get_hello_world @@ -54,7 +54,7 @@ from .types import ( lv_style_t, lvgl_ns, ) -from .widgets import Widget, add_widgets, lv_scr_act, set_obj_properties, styles_used +from .widgets import Widget, add_widgets, get_scr_act, set_obj_properties, styles_used from .widgets.animimg import animimg_spec from .widgets.arc import arc_spec from .widgets.button import button_spec @@ -186,7 +186,7 @@ def final_validation(config): async def to_code(config): cg.add_library("lvgl/lvgl", "8.4.0") - CORE.add_define("USE_LVGL") + cg.add_define("USE_LVGL") # suppress default enabling of extra widgets add_define("_LV_KCONFIG_PRESENT") # Always enable - lots of things use it. @@ -200,7 +200,13 @@ async def to_code(config): add_define("LV_MEM_CUSTOM_REALLOC", "lv_custom_mem_realloc") add_define("LV_MEM_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"') - add_define("LV_LOG_LEVEL", f"LV_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}") + add_define( + "LV_LOG_LEVEL", f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config[df.CONF_LOG_LEVEL]]}" + ) + cg.add_define( + "LVGL_LOG_LEVEL", + cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}"), + ) add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH]) for font in helpers.lv_fonts_used: add_define(f"LV_FONT_{font.upper()}") @@ -214,15 +220,9 @@ async def to_code(config): "LV_COLOR_CHROMA_KEY", await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]), ) - CORE.add_build_flag("-Isrc") + cg.add_build_flag("-Isrc") cg.add_global(lvgl_ns.using) - lv_component = cg.new_Pvariable(config[CONF_ID]) - await cg.register_component(lv_component, config) - Widget.create(config[CONF_ID], lv_component, obj_spec, config) - for display in config[df.CONF_DISPLAYS]: - cg.add(lv_component.add_display(await cg.get_variable(display))) - frac = config[CONF_BUFFER_SIZE] if frac >= 0.75: frac = 1 @@ -232,10 +232,17 @@ async def to_code(config): frac = 4 else: frac = 8 - cg.add(lv_component.set_buffer_frac(int(frac))) - cg.add(lv_component.set_full_refresh(config[df.CONF_FULL_REFRESH])) - cg.add(lv_component.set_draw_rounding(config[df.CONF_DRAW_ROUNDING])) - cg.add(lv_component.set_resume_on_input(config[df.CONF_RESUME_ON_INPUT])) + displays = [await cg.get_variable(display) for display in config[df.CONF_DISPLAYS]] + lv_component = cg.new_Pvariable( + config[CONF_ID], + displays, + frac, + config[df.CONF_FULL_REFRESH], + config[df.CONF_DRAW_ROUNDING], + config[df.CONF_RESUME_ON_INPUT], + ) + await cg.register_component(lv_component, config) + Widget.create(config[CONF_ID], lv_component, obj_spec, config) for font in helpers.esphome_fonts_used: await cg.get_variable(font) @@ -257,6 +264,7 @@ async def to_code(config): else: add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font)) + lv_scr_act = get_scr_act(lv_component) async with LvContext(lv_component): await touchscreens_to_code(lv_component, config) await encoders_to_code(lv_component, config) @@ -266,11 +274,9 @@ async def to_code(config): await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) await add_pages(lv_component, config) - await add_top_layer(config) - await msgboxes_to_code(config) - await disp_update(f"{lv_component}->get_disp()", config) - # At this point only the setup code should be generated - assert LvContext.added_lambda_count == 1 + await add_top_layer(lv_component, config) + await msgboxes_to_code(lv_component, config) + await disp_update(lv_component.get_disp(), config) # Set this directly since we are limited in how many methods can be added to the Widget class. Widget.widgets_completed = True async with LvContext(lv_component): @@ -291,15 +297,15 @@ async def to_code(config): await build_automation(resume_trigger, [], conf) for comp in helpers.lvgl_components_required: - CORE.add_define(f"USE_LVGL_{comp.upper()}") + cg.add_define(f"USE_LVGL_{comp.upper()}") if "transform_angle" in styles_used: add_define("LV_COLOR_SCREEN_TRANSP", "1") for use in helpers.lv_uses: add_define(f"LV_USE_{use.upper()}") lv_conf_h_file = CORE.relative_src_path(LV_CONF_FILENAME) write_file_if_changed(lv_conf_h_file, generate_lv_conf_h()) - CORE.add_build_flag("-DLV_CONF_H=1") - CORE.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"') + cg.add_build_flag("-DLV_CONF_H=1") + cg.add_build_flag(f'-DLV_CONF_PATH="{LV_CONF_FILENAME}"') def display_schema(config): @@ -308,9 +314,9 @@ def display_schema(config): def add_hello_world(config): - if CONF_WIDGETS not in config and CONF_PAGES not in config: + if df.CONF_WIDGETS not in config and CONF_PAGES not in config: LOGGER.info("No pages or widgets configured, creating default hello_world page") - config[CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world()) + config[df.CONF_WIDGETS] = cv.ensure_list(WIDGET_SCHEMA)(get_hello_world()) return config @@ -329,7 +335,7 @@ CONFIG_SCHEMA = ( cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int, cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage, cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of( - *df.LOG_LEVELS, upper=True + *df.LV_LOG_LEVELS, upper=True ), cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of( "big_endian", "little_endian" diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index cdc7553e81..48472354f8 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -5,7 +5,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT -from esphome.cpp_generator import RawExpression, get_variable +from esphome.cpp_generator import get_variable from esphome.cpp_types import nullptr from .defines import ( @@ -23,7 +23,6 @@ from .lvcode import ( UPDATE_EVENT, LambdaContext, LocalVariable, - LvConditional, LvglComponent, ReturnStatement, add_line_marks, @@ -47,8 +46,8 @@ from .types import ( ) from .widgets import ( Widget, + get_scr_act, get_widgets, - lv_scr_act, set_obj_properties, wait_for_widgets, ) @@ -66,8 +65,6 @@ async def action_to_code( ): await wait_for_widgets() async with LambdaContext(parameters=args, where=action_id) as context: - with LvConditional(lv_expr.is_pre_initialise()): - context.add(RawExpression("return")) for widget in widgets: await action(widget) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) @@ -126,7 +123,7 @@ async def lvgl_is_idle(config, condition_id, template_arg, args): async def disp_update(disp, config: dict): if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config: return - with LocalVariable("lv_disp_tmp", lv_disp_t, literal(disp)) as disp_temp: + with LocalVariable("lv_disp_tmp", lv_disp_t, disp) as disp_temp: if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None: lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color)) if bg_image := config.get(CONF_DISP_BG_IMAGE): @@ -136,15 +133,24 @@ async def disp_update(disp, config: dict): @automation.register_action( "lvgl.widget.redraw", ObjUpdateAction, - cv.Schema( - { - cv.Optional(CONF_ID): cv.use_id(lv_obj_t), - cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), - } + cv.Any( + cv.maybe_simple_value( + { + cv.Required(CONF_ID): cv.use_id(lv_obj_t), + cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), + }, + key=CONF_ID, + ), + cv.Schema( + { + cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), + } + ), ), ) async def obj_invalidate_to_code(config, action_id, template_arg, args): - widgets = await get_widgets(config) or [lv_scr_act] + lv_comp = await cg.get_variable(config[CONF_LVGL_ID]) + widgets = await get_widgets(config) or [get_scr_act(lv_comp)] async def do_invalidate(widget: Widget): lv_obj.invalidate(widget.obj) @@ -164,7 +170,7 @@ async def obj_invalidate_to_code(config, action_id, template_arg, args): async def lvgl_update_to_code(config, action_id, template_arg, args): widgets = await get_widgets(config) w = widgets[0] - disp = f"{w.obj}->get_disp()" + disp = literal(f"{w.obj}->get_disp()") async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context: await disp_update(disp, config) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index c8ece02677..4d48028611 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -189,14 +189,14 @@ LV_ANIM = LvConstant( LV_GRAD_DIR = LvConstant("LV_GRAD_DIR_", "NONE", "HOR", "VER") LV_DITHER = LvConstant("LV_DITHER_", "NONE", "ORDERED", "ERR_DIFF") -LOG_LEVELS = ( - "TRACE", - "INFO", - "WARN", - "ERROR", - "USER", - "NONE", -) +LV_LOG_LEVELS = { + "VERBOSE": "TRACE", + "DEBUG": "TRACE", + "INFO": "INFO", + "WARN": "WARN", + "ERROR": "ERROR", + "NONE": "NONE", +} LV_LONG_MODES = LvConstant( "LV_LABEL_LONG_", diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 3a080d63e9..37d6670b84 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -183,17 +183,11 @@ class LvContext(LambdaContext): super().__init__(parameters=self.args) self.lv_component = lv_component - async def add_init_lambda(self): - if self.code_list: - cg.add(self.lv_component.add_init_lambda(await self.get_lambda())) - LvContext.added_lambda_count += 1 - async def __aexit__(self, exc_type, exc_val, exc_tb): await super().__aexit__(exc_type, exc_val, exc_tb) - await self.add_init_lambda() def add(self, expression: Union[Expression, Statement]): - self.code_list.append(self.indented_statement(expression)) + cg.add(expression) return expression def __call__(self, *args): diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 5a6c66c677..413b039af0 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -11,12 +11,6 @@ namespace esphome { namespace lvgl { static const char *const TAG = "lvgl"; -#if LV_USE_LOG -static void log_cb(const char *buf) { - esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf); -} -#endif // LV_USE_LOG - static const char *const EVENT_NAMES[] = { "NONE", "PRESSED", @@ -383,26 +377,48 @@ void LvglComponent::write_random_() { } } -void LvglComponent::setup() { - ESP_LOGCONFIG(TAG, "LVGL Setup starts"); -#if LV_USE_LOG - lv_log_register_print_cb(log_cb); -#endif +/** + * @class LvglComponent + * @brief Component for rendering LVGL. + * + * This component renders LVGL widgets on a display. Some initialisation must be done in the constructor + * since LVGL needs to be initialised before any widgets can be created. + * + * @param displays a list of displays to render onto. All displays must have the same + * resolution. + * @param buffer_frac the fraction of the display resolution to use for the LVGL + * draw buffer. A higher value will make animations smoother but + * also increase memory usage. + * @param full_refresh if true, the display will be fully refreshed on every frame. + * If false, only changed areas will be updated. + * @param draw_rounding the rounding to use when drawing. A value of 1 will draw + * without any rounding, a value of 2 will round to the nearest + * multiple of 2, and so on. + * @param resume_on_input if true, this component will resume rendering when the user + * presses a key or clicks on the screen. + */ +LvglComponent::LvglComponent(std::vector displays, float buffer_frac, bool full_refresh, + int draw_rounding, bool resume_on_input) + : draw_rounding(draw_rounding), + displays_(std::move(displays)), + buffer_frac_(buffer_frac), + full_refresh_(full_refresh), + resume_on_input_(resume_on_input) { lv_init(); lv_update_event = static_cast(lv_event_register_id()); lv_api_event = static_cast(lv_event_register_id()); auto *display = this->displays_[0]; size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_; auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; - auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT - if (buf == nullptr) { -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR - ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes); -#endif - this->mark_failed(); - this->status_set_error("Memory allocation failure"); - return; + this->rotation = display->get_rotation(); + 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) + return; } + auto *buf = lv_custom_mem_alloc(buf_bytes); // NOLINT + if (buf == nullptr) + return; lv_disp_draw_buf_init(&this->draw_buf_, buf, nullptr, buffer_pixels); lv_disp_drv_init(&this->disp_drv_); this->disp_drv_.draw_buf = &this->draw_buf_; @@ -410,18 +426,7 @@ void LvglComponent::setup() { this->disp_drv_.full_refresh = this->full_refresh_; this->disp_drv_.flush_cb = static_flush_cb; this->disp_drv_.rounder_cb = rounder_cb; - this->rotation = display->get_rotation(); - 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) { -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR - ESP_LOGE(TAG, "Malloc failed to allocate %zu bytes", buf_bytes); -#endif - this->mark_failed(); - this->status_set_error("Memory allocation failure"); - return; - } - } + // reset the display rotation since we will handle all rotations display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES); switch (this->rotation) { default: @@ -435,12 +440,30 @@ void LvglComponent::setup() { break; } this->disp_ = lv_disp_drv_register(&this->disp_drv_); - for (const auto &v : this->init_lambdas_) - v(this); +} + +void LvglComponent::setup() { + if (this->draw_buf_.buf1 == nullptr) { + this->mark_failed(); + this->status_set_error("Memory allocation failure"); + return; + } + ESP_LOGCONFIG(TAG, "LVGL Setup starts"); +#if LV_USE_LOG + lv_log_register_print_cb([](const char *buf) { + auto next = strchr(buf, ')'); + if (next != nullptr) + buf = next + 1; + while (isspace(*buf)) + buf++; + esp_log_printf_(LVGL_LOG_LEVEL, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf); + }); +#endif this->show_page(0, LV_SCR_LOAD_ANIM_NONE, 0); lv_disp_trig_activity(this->disp_); ESP_LOGCONFIG(TAG, "LVGL Setup complete"); } + void LvglComponent::update() { // update indicators if (this->paused_) { @@ -455,13 +478,6 @@ void LvglComponent::loop() { } lv_timer_handler_run_in_period(5); } -bool lv_is_pre_initialise() { - if (!lv_is_initialized()) { - ESP_LOGE(TAG, "LVGL call before component is initialised"); - return true; - } - return false; -} #ifdef USE_LVGL_ANIMIMG void lv_animimg_stop(lv_obj_t *obj) { diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 2d326f4ae2..b8c0f5738e 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -39,7 +39,6 @@ namespace lvgl { extern lv_event_code_t lv_api_event; // NOLINT extern lv_event_code_t lv_update_event; // NOLINT extern std::string lv_event_code_name_for(uint8_t event_code); -extern bool lv_is_pre_initialise(); #if LV_COLOR_DEPTH == 16 static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_565; #elif LV_COLOR_DEPTH == 32 @@ -108,6 +107,8 @@ class LvglComponent : public PollingComponent { constexpr static const char *const TAG = "lvgl"; public: + LvglComponent(std::vector displays, float buffer_frac, bool full_refresh, int draw_rounding, + bool resume_on_input); static void static_flush_cb(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); float get_setup_priority() const override { return setup_priority::PROCESSOR; } @@ -118,13 +119,10 @@ class LvglComponent : public PollingComponent { this->idle_callbacks_.add(std::move(callback)); } void add_on_pause_callback(std::function &&callback) { this->pause_callbacks_.add(std::move(callback)); } - void add_display(display::Display *display) { this->displays_.push_back(display); } - void add_init_lambda(const std::function &lamb) { this->init_lambdas_.push_back(lamb); } void dump_config() override; - void set_full_refresh(bool full_refresh) { this->full_refresh_ = full_refresh; } bool is_idle(uint32_t idle_ms) { return lv_disp_get_inactive_time(this->disp_) > idle_ms; } - void set_buffer_frac(size_t frac) { this->buffer_frac_ = frac; } lv_disp_t *get_disp() { return this->disp_; } + lv_obj_t *get_scr_act() { return lv_disp_get_scr_act(this->disp_); } // Pause or resume the display. // @param paused If true, pause the display. If false, resume the display. // @param show_snow If true, show the snow effect when paused. @@ -155,17 +153,19 @@ class LvglComponent : public PollingComponent { } // rounding factor to align bounds of update area when drawing size_t draw_rounding{2}; - void set_draw_rounding(size_t rounding) { this->draw_rounding = rounding; } - void set_resume_on_input(bool resume_on_input) { this->resume_on_input_ = resume_on_input; } - // if set to true, the bounds of the update area will always start at 0,0 display::DisplayRotation rotation{display::DISPLAY_ROTATION_0_DEGREES}; protected: void write_random_(); void draw_buffer_(const lv_area_t *area, lv_color_t *ptr); void flush_cb_(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p); + std::vector displays_{}; + size_t buffer_frac_{1}; + bool full_refresh_{}; + bool resume_on_input_{}; + lv_disp_draw_buf_t draw_buf_{}; lv_disp_drv_t disp_drv_{}; lv_disp_t *disp_{}; @@ -174,14 +174,10 @@ class LvglComponent : public PollingComponent { size_t current_page_{0}; bool show_snow_{}; bool page_wrap_{true}; - bool resume_on_input_{}; std::map focus_marks_{}; - std::vector> init_lambdas_; CallbackManager idle_callbacks_{}; CallbackManager pause_callbacks_{}; - size_t buffer_frac_{1}; - bool full_refresh_{}; lv_color_t *rotate_buf_{}; }; diff --git a/esphome/components/lvgl/styles.py b/esphome/components/lvgl/styles.py index 030db5fd22..6332e0976f 100644 --- a/esphome/components/lvgl/styles.py +++ b/esphome/components/lvgl/styles.py @@ -17,8 +17,6 @@ from .types import lv_lambda_t, lv_obj_t, lv_obj_t_ptr from .widgets import Widget, add_widgets, set_obj_properties, theme_widget_map from .widgets.obj import obj_spec -TOP_LAYER = literal("lv_disp_get_layer_top(lv_component->get_disp())") - async def styles_to_code(config): """Convert styles to C__ code.""" @@ -51,9 +49,10 @@ async def theme_to_code(config): lv_assign(apply, await context.get_lambda()) -async def add_top_layer(config): +async def add_top_layer(lv_component, config): + top_layer = lv.disp_get_layer_top(lv_component.get_disp()) if top_conf := config.get(CONF_TOP_LAYER): - with LocalVariable("top_layer", lv_obj_t, TOP_LAYER) as top_layer_obj: + with LocalVariable("top_layer", lv_obj_t, top_layer) as top_layer_obj: top_w = Widget(top_layer_obj, obj_spec, top_conf) await set_obj_properties(top_w, top_conf) await add_widgets(top_w, top_conf) diff --git a/esphome/components/lvgl/widgets/__init__.py b/esphome/components/lvgl/widgets/__init__.py index 35ee6c54e8..e946a96000 100644 --- a/esphome/components/lvgl/widgets/__init__.py +++ b/esphome/components/lvgl/widgets/__init__.py @@ -55,18 +55,6 @@ theme_widget_map = {} styles_used = set() -class LvScrActType(WidgetType): - """ - A "widget" representing the active screen. - """ - - def __init__(self): - super().__init__("lv_scr_act()", lv_obj_t, ()) - - async def to_code(self, w, config: dict): - return [] - - class Widget: """ Represents a Widget. @@ -221,6 +209,25 @@ class Widget: widget_map: dict[Any, Widget] = {} +class LvScrActType(WidgetType): + """ + A "widget" representing the active screen. + """ + + def __init__(self): + super().__init__("lv_scr_act()", lv_obj_t, ()) + + async def to_code(self, w, config: dict): + return [] + + +lv_scr_act_spec = LvScrActType() + + +def get_scr_act(lv_comp: MockObj) -> Widget: + return Widget.create(None, lv_comp.get_scr_act(), lv_scr_act_spec, {}) + + def get_widget_generator(wid): """ Used to wait for a widget during code generation. @@ -451,7 +458,3 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent): await set_obj_properties(w, w_cnfig) await add_widgets(w, w_cnfig) await spec.to_code(w, w_cnfig) - - -lv_scr_act_spec = LvScrActType() -lv_scr_act = Widget.create(None, literal("lv_scr_act()"), lv_scr_act_spec, {}) diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index 1af4ed6e05..be0f2100d7 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -20,6 +20,7 @@ from ..lvcode import ( EVENT_ARG, LambdaContext, LocalVariable, + lv, lv_add, lv_assign, lv_expr, @@ -27,7 +28,6 @@ from ..lvcode import ( lv_Pvariable, ) from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema -from ..styles import TOP_LAYER from ..types import LV_EVENT, char_ptr, lv_obj_t from . import Widget, set_obj_properties from .button import button_spec @@ -59,7 +59,7 @@ MSGBOX_SCHEMA = container_schema( ) -async def msgbox_to_code(conf): +async def msgbox_to_code(top_layer, conf): """ Construct a message box. This consists of a full-screen translucent background enclosing a centered container with an optional title, body, close button and a button matrix. And any other widgets the user cares to add @@ -101,7 +101,7 @@ async def msgbox_to_code(conf): text = await lv_text.process(conf[CONF_BODY].get(CONF_TEXT, "")) title = await lv_text.process(conf[CONF_TITLE].get(CONF_TEXT, "")) close_button = conf[CONF_CLOSE_BUTTON] - lv_assign(outer, lv_expr.obj_create(TOP_LAYER)) + lv_assign(outer, lv_expr.obj_create(top_layer)) lv_obj.set_width(outer, lv_pct(100)) lv_obj.set_height(outer, lv_pct(100)) lv_obj.set_style_bg_opa(outer, 128, 0) @@ -141,6 +141,7 @@ async def msgbox_to_code(conf): set_btn_data(buttonmatrix.obj, ctrl_list, width_list) -async def msgboxes_to_code(config): +async def msgboxes_to_code(lv_component, config): + top_layer = lv.disp_get_layer_top(lv_component.get_disp()) for conf in config.get(CONF_MSGBOXES, ()): - await msgbox_to_code(conf) + await msgbox_to_code(top_layer, conf) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 1f09bc22eb..8d515280c9 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -12,12 +12,12 @@ substitutions: arrow_down: "\U000F004B" lvgl: + log_level: debug resume_on_input: true on_pause: logger.log: LVGL is Paused on_resume: logger.log: LVGL has resumed - log_level: TRACE bg_color: light_blue disp_bg_color: color_id disp_bg_image: cat_image @@ -125,6 +125,8 @@ lvgl: on_unload: - logger.log: page unloaded - lvgl.widget.focus: mark + - lvgl.widget.redraw: hello_label + - lvgl.widget.redraw: on_all_events: logger.log: format: "Event %s" From ef6ccddc0d1bbe61d8c8331ed7eb933f284c8ef7 Mon Sep 17 00:00:00 2001 From: guillempages Date: Thu, 17 Oct 2024 22:23:37 +0200 Subject: [PATCH 0500/1052] [lvgl] Allow esphome::Image in lambda to update image source directly (#7624) --- esphome/components/lvgl/lvgl_esphome.h | 11 +++++++++++ tests/components/lvgl/common.yaml | 5 +++++ tests/components/lvgl/lvgl-package.yaml | 1 + 3 files changed, 17 insertions(+) diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index b8c0f5738e..f357c4950c 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -4,6 +4,9 @@ #ifdef USE_BINARY_SENSOR #include "esphome/components/binary_sensor/binary_sensor.h" #endif // USE_BINARY_SENSOR +#ifdef USE_LVGL_IMAGE +#include "esphome/components/image/image.h" +#endif // USE_LVGL_IMAGE #ifdef USE_LVGL_ROTARY_ENCODER #include "esphome/components/rotary_encoder/rotary_encoder.h" #endif // USE_LVGL_ROTARY_ENCODER @@ -47,6 +50,14 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BITNESS_332; #endif // LV_COLOR_DEPTH +#ifdef USE_LVGL_IMAGE +// Shortcut / overload, so that the source of an image can easily be updated +// from within a lambda. +inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) { + lv_img_set_src(obj, image->get_lv_img_dsc()); +} +#endif // USE_LVGL_IMAGE + // Parent class for things that wrap an LVGL object class LvCompound { public: diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index ad935ae563..5dcf30e0c1 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -127,6 +127,11 @@ binary_sensor: - platform: lvgl name: LVGL checkbox widget: checkbox_id + on_state: + then: + - lvgl.image.update: + id: lv_image + src: !lambda if (x) return id(cat_image); else return id(dog_image); wifi: ssid: SSID diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 8d515280c9..cef76396c2 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -419,6 +419,7 @@ lvgl: spin_time: 2s align: left_mid - image: + id: lv_image src: cat_image align: top_left y: 50 From c019ff34bccf08b3070cae3c1e71c2767cf70a6e Mon Sep 17 00:00:00 2001 From: Shivam Maurya <54358380+shvmm@users.noreply.github.com> Date: Fri, 18 Oct 2024 06:45:28 +0530 Subject: [PATCH 0501/1052] Bump bme68x_bsec2 version to 1.8.2610 (#7626) --- esphome/components/bme68x_bsec2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/bme68x_bsec2/__init__.py b/esphome/components/bme68x_bsec2/__init__.py index 1930c7c9e3..d6dbb52f18 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.7.2502" +BSEC2_LIBRARY_VERSION = "v1.8.2610" CONF_ALGORITHM_OUTPUT = "algorithm_output" CONF_BME68X_BSEC2_ID = "bme68x_bsec2_id" From 43a020641b89b86b7082f956e3c335de38bd97b0 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sun, 20 Oct 2024 21:16:08 +0200 Subject: [PATCH 0502/1052] Fix broken ibeacon_uuid config in ble_rssi (#7640) --- esphome/components/ble_rssi/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index e3ba1abfd7..c4e767aa21 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -45,7 +45,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t, cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t, - cv.Optional(CONF_IBEACON_UUID): cv.uuid, + cv.Optional(CONF_IBEACON_UUID): esp32_ble_tracker.bt_uuid, } ) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) @@ -79,7 +79,7 @@ async def to_code(config): cg.add(var.set_service_uuid128(uuid128)) if ibeacon_uuid := config.get(CONF_IBEACON_UUID): - ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid)) + ibeacon_uuid = esp32_ble_tracker.as_reversed_hex_array(ibeacon_uuid) cg.add(var.set_ibeacon_uuid(ibeacon_uuid)) if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None: From f7543a7b8dd1b5f8984c4dc84fc087df3f392784 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 21 Oct 2024 11:28:52 +1300 Subject: [PATCH 0503/1052] Update Pull request template (#7620) --- .github/PULL_REQUEST_TEMPLATE.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3bf9c4e1f6..5703d39be1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,11 +7,16 @@ - [ ] Bugfix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] Code quality improvements to existing code or addition of tests - [ ] Other -**Related issue or feature (if applicable):** fixes +**Related issue or feature (if applicable):** -**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs# +- fixes + +**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** + +- esphome/esphome-docs# ## Test Environment @@ -23,12 +28,6 @@ - [ ] RTL87xx ## Example entry for `config.yaml`: - ```yaml # Example config.yaml From 657527655d380554edfdd3274d7b1d5ac1b4a32a Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 20 Oct 2024 17:40:43 -0700 Subject: [PATCH 0504/1052] auto-load preferences (#7642) Co-authored-by: Samuel Sieb --- esphome/components/host/__init__.py | 2 +- esphome/components/libretiny/__init__.py | 2 +- esphome/components/rp2040/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index e83bf2dba8..eb8cfbd984 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -16,7 +16,7 @@ from .const import KEY_HOST from .gpio import host_pin_to_code # noqa CODEOWNERS = ["@esphome/core", "@clydebarrow"] -AUTO_LOAD = ["network"] +AUTO_LOAD = ["network", "preferences"] def set_core_data(config): diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index cc7fae7e70..b29d2e309c 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -46,7 +46,7 @@ from .const import ( _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@kuba2k2"] -AUTO_LOAD = [] +AUTO_LOAD = ["preferences"] def _detect_variant(value): diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 925acb629d..f59962477f 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -26,7 +26,7 @@ from .gpio import rp2040_pin_to_code # noqa _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@jesserockz"] -AUTO_LOAD = [] +AUTO_LOAD = ["preferences"] def set_core_data(config): From 5e8794175dceb777f928a2966c4c5e9a977c2acc Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 21 Oct 2024 17:46:41 -0500 Subject: [PATCH 0505/1052] [wifi] Support custom MAC on Arduino, too (#7644) --- esphome/components/esp32/__init__.py | 12 ++++++++++-- .../components/wifi/wifi_component_esp32_arduino.cpp | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 8a73f2020d..61fbb53e3a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -395,6 +395,13 @@ ARDUINO_FRAMEWORK_SCHEMA = cv.All( cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, cv.Optional(CONF_SOURCE): cv.string_strict, cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, + cv.Optional(CONF_ADVANCED, default={}): cv.Schema( + { + cv.Optional( + CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False + ): cv.boolean, + } + ), } ), _arduino_check_versions, @@ -494,6 +501,9 @@ 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]: + cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") + add_extra_script( "post", "post_build.py", @@ -540,8 +550,6 @@ async def to_code(config): for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) - if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: - cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") if conf[CONF_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): diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index b8724838c8..ef4308b28c 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -34,6 +34,11 @@ static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void WiFiComponent::wifi_pre_setup_() { + uint8_t mac[6]; + if (has_custom_mac_address()) { + get_mac_address_raw(mac); + set_mac_address(mac); + } auto f = std::bind(&WiFiComponent::wifi_event_callback_, this, std::placeholders::_1, std::placeholders::_2); WiFi.onEvent(f); WiFi.persistent(false); From c8d0cde329a83dd042412651c033dd133c9a3330 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:49:12 +1100 Subject: [PATCH 0506/1052] [config] Ensure user-supplied build flags don't get silently overwritten (#7622) --- esphome/core/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index f4253bee87..8c130eb6db 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -318,6 +318,8 @@ async def add_includes(includes): async def _add_platformio_options(pio_options): # Add includes at the very end, so that they override everything for key, val in pio_options.items(): + if key == "build_flags" and not isinstance(val, list): + val = [val] cg.add_platformio_option(key, val) From 612e2c164406fe0d4670b8af68478c3b3644c47e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:50:16 +1100 Subject: [PATCH 0507/1052] [lvgl] Defer display rotation reset until setup(). (Bugfix) (#7627) --- esphome/components/lvgl/lvgl_esphome.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 413b039af0..c8f7848a4f 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -84,6 +84,7 @@ 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); } @@ -426,19 +427,8 @@ LvglComponent::LvglComponent(std::vector displays, float buf this->disp_drv_.full_refresh = this->full_refresh_; this->disp_drv_.flush_cb = static_flush_cb; this->disp_drv_.rounder_cb = rounder_cb; - // reset the display rotation since we will handle all rotations - display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES); - switch (this->rotation) { - default: - this->disp_drv_.hor_res = (lv_coord_t) display->get_width(); - this->disp_drv_.ver_res = (lv_coord_t) display->get_height(); - break; - case display::DISPLAY_ROTATION_90_DEGREES: - case display::DISPLAY_ROTATION_270_DEGREES: - this->disp_drv_.ver_res = (lv_coord_t) display->get_width(); - this->disp_drv_.hor_res = (lv_coord_t) display->get_height(); - break; - } + this->disp_drv_.hor_res = (lv_coord_t) display->get_width(); + this->disp_drv_.ver_res = (lv_coord_t) display->get_height(); this->disp_ = lv_disp_drv_register(&this->disp_drv_); } @@ -459,6 +449,9 @@ void LvglComponent::setup() { esp_log_printf_(LVGL_LOG_LEVEL, TAG, 0, "%.*s", (int) strlen(buf) - 1, buf); }); #endif + // Rotation will be handled by our drawing function, so reset the display rotation. + for (auto *display : this->displays_) + display->set_rotation(display::DISPLAY_ROTATION_0_DEGREES); this->show_page(0, LV_SCR_LOAD_ANIM_NONE, 0); lv_disp_trig_activity(this->disp_); ESP_LOGCONFIG(TAG, "LVGL Setup complete"); From 40ad6befa83f23674d09bf70198eb8761fa69c81 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:51:40 +1100 Subject: [PATCH 0508/1052] [lvgl] Remove states from style definitions (Bugfix) (#7645) --- esphome/components/lvgl/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index beaf279a9a..215fdecdb5 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -33,7 +33,7 @@ from .schemas import ( FLEX_OBJ_SCHEMA, GRID_CELL_SCHEMA, LAYOUT_SCHEMAS, - STATE_SCHEMA, + STYLE_SCHEMA, WIDGET_TYPES, any_widget_schema, container_schema, @@ -342,7 +342,7 @@ CONFIG_SCHEMA = ( ), cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list( cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)}) - .extend(STATE_SCHEMA) + .extend(STYLE_SCHEMA) .extend( { cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, From dc42427c604ed14ab94270df8148fad22e101ee4 Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Mon, 21 Oct 2024 18:14:07 -0500 Subject: [PATCH 0509/1052] Move setting global voice assistant to constructor (#7630) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/voice_assistant/voice_assistant.cpp | 8 ++------ esphome/components/voice_assistant/voice_assistant.h | 3 ++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index a2210f188d..0b53e74ba3 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -23,6 +23,8 @@ static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t); static const size_t RECEIVE_SIZE = 1024; static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE; +VoiceAssistant::VoiceAssistant() { global_voice_assistant = this; } + float VoiceAssistant::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } bool VoiceAssistant::start_udp_socket_() { @@ -68,12 +70,6 @@ bool VoiceAssistant::start_udp_socket_() { return true; } -void VoiceAssistant::setup() { - ESP_LOGCONFIG(TAG, "Setting up Voice Assistant..."); - - global_voice_assistant = this; -} - bool VoiceAssistant::allocate_buffers_() { if (this->send_buffer_ != nullptr) { return true; // Already allocated diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 56ada0e75a..870e2acdaa 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -91,7 +91,8 @@ struct Configuration { class VoiceAssistant : public Component { public: - void setup() override; + VoiceAssistant(); + void loop() override; float get_setup_priority() const override; void start_streaming(); From 7004053538c16544740e5e866395e9f94da1a63a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:32:22 +1100 Subject: [PATCH 0510/1052] [config] Fix crash with empty substitutions block (#7612) --- esphome/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/config.py b/esphome/config.py index a2d0d15477..7d48569d2d 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -782,7 +782,7 @@ def validate_config( from esphome.components import substitutions result[CONF_SUBSTITUTIONS] = { - **config.get(CONF_SUBSTITUTIONS, {}), + **(config.get(CONF_SUBSTITUTIONS) or {}), **command_line_substitutions, } result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS) From 3dd34f66284ff8c5b0a69e70d57f6273d3395544 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sun, 20 Oct 2024 21:16:08 +0200 Subject: [PATCH 0511/1052] Fix broken ibeacon_uuid config in ble_rssi (#7640) --- esphome/components/ble_rssi/sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ble_rssi/sensor.py b/esphome/components/ble_rssi/sensor.py index e3ba1abfd7..c4e767aa21 100644 --- a/esphome/components/ble_rssi/sensor.py +++ b/esphome/components/ble_rssi/sensor.py @@ -45,7 +45,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.Optional(CONF_IBEACON_MAJOR): cv.uint16_t, cv.Optional(CONF_IBEACON_MINOR): cv.uint16_t, - cv.Optional(CONF_IBEACON_UUID): cv.uuid, + cv.Optional(CONF_IBEACON_UUID): esp32_ble_tracker.bt_uuid, } ) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) @@ -79,7 +79,7 @@ async def to_code(config): cg.add(var.set_service_uuid128(uuid128)) if ibeacon_uuid := config.get(CONF_IBEACON_UUID): - ibeacon_uuid = esp32_ble_tracker.as_hex_array(str(ibeacon_uuid)) + ibeacon_uuid = esp32_ble_tracker.as_reversed_hex_array(ibeacon_uuid) cg.add(var.set_ibeacon_uuid(ibeacon_uuid)) if (ibeacon_major := config.get(CONF_IBEACON_MAJOR)) is not None: From 10791db82e7784f2e8b22a69ca8ad01223f8f1d7 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 20 Oct 2024 17:40:43 -0700 Subject: [PATCH 0512/1052] auto-load preferences (#7642) Co-authored-by: Samuel Sieb --- esphome/components/host/__init__.py | 2 +- esphome/components/libretiny/__init__.py | 2 +- esphome/components/rp2040/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index e83bf2dba8..eb8cfbd984 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -16,7 +16,7 @@ from .const import KEY_HOST from .gpio import host_pin_to_code # noqa CODEOWNERS = ["@esphome/core", "@clydebarrow"] -AUTO_LOAD = ["network"] +AUTO_LOAD = ["network", "preferences"] def set_core_data(config): diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index cc7fae7e70..b29d2e309c 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -46,7 +46,7 @@ from .const import ( _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@kuba2k2"] -AUTO_LOAD = [] +AUTO_LOAD = ["preferences"] def _detect_variant(value): diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index 925acb629d..f59962477f 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -26,7 +26,7 @@ from .gpio import rp2040_pin_to_code # noqa _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@jesserockz"] -AUTO_LOAD = [] +AUTO_LOAD = ["preferences"] def set_core_data(config): From 748256b3eef8bcce7472983bd5d6f48dd55362cb Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 21 Oct 2024 17:46:41 -0500 Subject: [PATCH 0513/1052] [wifi] Support custom MAC on Arduino, too (#7644) --- esphome/components/esp32/__init__.py | 12 ++++++++++-- .../components/wifi/wifi_component_esp32_arduino.cpp | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 8a73f2020d..61fbb53e3a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -395,6 +395,13 @@ ARDUINO_FRAMEWORK_SCHEMA = cv.All( cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, cv.Optional(CONF_SOURCE): cv.string_strict, cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, + cv.Optional(CONF_ADVANCED, default={}): cv.Schema( + { + cv.Optional( + CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False + ): cv.boolean, + } + ), } ), _arduino_check_versions, @@ -494,6 +501,9 @@ 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]: + cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") + add_extra_script( "post", "post_build.py", @@ -540,8 +550,6 @@ async def to_code(config): for name, value in conf[CONF_SDKCONFIG_OPTIONS].items(): add_idf_sdkconfig_option(name, RawSdkconfigValue(value)) - if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]: - cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC") if conf[CONF_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): diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index b8724838c8..ef4308b28c 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -34,6 +34,11 @@ static esp_netif_t *s_ap_netif = nullptr; // NOLINT(cppcoreguidelines-avoid-non static bool s_sta_connecting = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void WiFiComponent::wifi_pre_setup_() { + uint8_t mac[6]; + if (has_custom_mac_address()) { + get_mac_address_raw(mac); + set_mac_address(mac); + } auto f = std::bind(&WiFiComponent::wifi_event_callback_, this, std::placeholders::_1, std::placeholders::_2); WiFi.onEvent(f); WiFi.persistent(false); From c26c96b8f469a3071c14ef91b5e7aa073a3aa9ca Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:49:12 +1100 Subject: [PATCH 0514/1052] [config] Ensure user-supplied build flags don't get silently overwritten (#7622) --- esphome/core/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index f4253bee87..8c130eb6db 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -318,6 +318,8 @@ async def add_includes(includes): async def _add_platformio_options(pio_options): # Add includes at the very end, so that they override everything for key, val in pio_options.items(): + if key == "build_flags" and not isinstance(val, list): + val = [val] cg.add_platformio_option(key, val) From 3ebdd62c672dba8fa7d4c4ebd021750394c76596 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:51:40 +1100 Subject: [PATCH 0515/1052] [lvgl] Remove states from style definitions (Bugfix) (#7645) --- esphome/components/lvgl/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index ce3843567b..1a587edb57 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -33,7 +33,7 @@ from .schemas import ( FLEX_OBJ_SCHEMA, GRID_CELL_SCHEMA, LAYOUT_SCHEMAS, - STATE_SCHEMA, + STYLE_SCHEMA, WIDGET_TYPES, any_widget_schema, container_schema, @@ -323,7 +323,7 @@ CONFIG_SCHEMA = ( ), cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list( cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)}) - .extend(STATE_SCHEMA) + .extend(STYLE_SCHEMA) .extend( { cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, From d95b370998527e279ac3a9c3376ede984929cd5d Mon Sep 17 00:00:00 2001 From: Michael Hansen Date: Mon, 21 Oct 2024 18:14:07 -0500 Subject: [PATCH 0516/1052] Move setting global voice assistant to constructor (#7630) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/voice_assistant/voice_assistant.cpp | 8 ++------ esphome/components/voice_assistant/voice_assistant.h | 3 ++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index a2210f188d..0b53e74ba3 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -23,6 +23,8 @@ static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t); static const size_t RECEIVE_SIZE = 1024; static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE; +VoiceAssistant::VoiceAssistant() { global_voice_assistant = this; } + float VoiceAssistant::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } bool VoiceAssistant::start_udp_socket_() { @@ -68,12 +70,6 @@ bool VoiceAssistant::start_udp_socket_() { return true; } -void VoiceAssistant::setup() { - ESP_LOGCONFIG(TAG, "Setting up Voice Assistant..."); - - global_voice_assistant = this; -} - bool VoiceAssistant::allocate_buffers_() { if (this->send_buffer_ != nullptr) { return true; // Already allocated diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 56ada0e75a..870e2acdaa 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -91,7 +91,8 @@ struct Configuration { class VoiceAssistant : public Component { public: - void setup() override; + VoiceAssistant(); + void loop() override; float get_setup_priority() const override; void start_streaming(); From 735c04cd6995e1cd220440af0fb79100a0b638f2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:57:17 +1300 Subject: [PATCH 0517/1052] Bump version to 2024.10.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5061b1a439..5fa457b25a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.10.0" +__version__ = "2024.10.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 8bb4316956a39a8c7141a9504f56ee0f5c32812a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:03:32 +1100 Subject: [PATCH 0518/1052] [lvgl] light schema should require `widget:` not `led:` (Bugfix) (#7649) --- esphome/components/lvgl/light/__init__.py | 8 ++++---- tests/components/lvgl/common.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/lvgl/light/__init__.py b/esphome/components/lvgl/light/__init__.py index a0eeded349..8031ae8221 100644 --- a/esphome/components/lvgl/light/__init__.py +++ b/esphome/components/lvgl/light/__init__.py @@ -2,9 +2,9 @@ import esphome.codegen as cg from esphome.components import light from esphome.components.light import LightOutput import esphome.config_validation as cv -from esphome.const import CONF_GAMMA_CORRECT, CONF_LED, CONF_OUTPUT_ID +from esphome.const import CONF_GAMMA_CORRECT, CONF_OUTPUT_ID -from ..defines import CONF_LVGL_ID +from ..defines import CONF_LVGL_ID, CONF_WIDGET from ..lvcode import LvContext from ..schemas import LVGL_SCHEMA from ..types import LvType, lvgl_ns @@ -15,7 +15,7 @@ LVLight = lvgl_ns.class_("LVLight", LightOutput) CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( { cv.Optional(CONF_GAMMA_CORRECT, default=0.0): cv.positive_float, - cv.Required(CONF_LED): cv.use_id(lv_led_t), + cv.Required(CONF_WIDGET): cv.use_id(lv_led_t), cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(LVLight), } ).extend(LVGL_SCHEMA) @@ -26,7 +26,7 @@ async def to_code(config): await light.register_light(var, config) paren = await cg.get_variable(config[CONF_LVGL_ID]) - widget = await get_widgets(config, CONF_LED) + widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] await wait_for_widgets() async with LvContext(paren) as ctx: diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index 5dcf30e0c1..cebc3caaa7 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -93,7 +93,7 @@ light: - platform: lvgl name: LVGL LED id: lv_light - led: lv_led + widget: lv_led binary_sensor: - platform: lvgl From ff48f53989f7886c167364e04df72f79039a9bac Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:05:39 +1100 Subject: [PATCH 0519/1052] [image] Fix compile time problem with host image not using lvgl (#7654) --- esphome/components/image/image.h | 7 +------ esphome/components/lvgl/lvgl_proxy.h | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 esphome/components/lvgl/lvgl_proxy.h diff --git a/esphome/components/image/image.h b/esphome/components/image/image.h index ae5a7a814d..a8a8aab2c2 100644 --- a/esphome/components/image/image.h +++ b/esphome/components/image/image.h @@ -3,12 +3,7 @@ #include "esphome/components/display/display.h" #ifdef USE_LVGL -// required for clang-tidy -#ifndef LV_CONF_H -#define LV_CONF_SKIP 1 // NOLINT -#endif // LV_CONF_H - -#include +#include "esphome/components/lvgl/lvgl_proxy.h" #endif // USE_LVGL namespace esphome { diff --git a/esphome/components/lvgl/lvgl_proxy.h b/esphome/components/lvgl/lvgl_proxy.h new file mode 100644 index 0000000000..0ccd80e541 --- /dev/null +++ b/esphome/components/lvgl/lvgl_proxy.h @@ -0,0 +1,17 @@ +#pragma once +/** +* This header is for use in components that might or might not use LVGL. There is a platformio bug where +the mere mention of a header file, even if ifdefed, causes the build to fail. This is a workaround, since if this +file is included in the build, LVGL is always included. +*/ +#ifdef USE_LVGL +// required for clang-tidy +#ifndef LV_CONF_H +#define LV_CONF_SKIP 1 // NOLINT +#endif // LV_CONF_H + +#include +namespace esphome { +namespace lvgl {} // namespace lvgl +} // namespace esphome +#endif // USE_LVGL From 3ac730fb2f5b7231e77d5d7c3822e7cc59fb4e59 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:06:58 +1100 Subject: [PATCH 0520/1052] [lvgl] Fix rotation code for 90deg (Bugfix) (#7653) --- esphome/components/lvgl/lvgl_esphome.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index c8f7848a4f..70cfb859de 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -146,7 +146,7 @@ void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) { lv_color_t *dst = this->rotate_buf_; switch (this->rotation) { case display::DISPLAY_ROTATION_90_DEGREES: - for (lv_coord_t x = height - 1; x-- != 0;) { + for (lv_coord_t x = height; x-- != 0;) { for (lv_coord_t y = 0; y != width; y++) { dst[y * height + x] = *ptr++; } From 6330177d24b82060afec7628d6152fea6b54f963 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:10:09 +1100 Subject: [PATCH 0521/1052] [lvgl] Allow strings to be interpreted as integers (Bugfix) (#7652) --- esphome/components/lvgl/lv_validation.py | 6 ++---- tests/components/lvgl/lvgl-package.yaml | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index fd840cc417..9af25a4e90 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -274,10 +274,8 @@ def size_validator(value): return ["SIZE_CONTENT", "number of pixels", "percentage"] if isinstance(value, str) and value.lower().endswith("px"): value = cv.int_(value[:-2]) - if isinstance(value, str) and not value.endswith("%"): - if value.upper() == "SIZE_CONTENT": - return "LV_SIZE_CONTENT" - raise cv.Invalid("must be 'size_content', a percentage or an integer (pixels)") + if isinstance(value, str) and value.upper() == "SIZE_CONTENT": + return "LV_SIZE_CONTENT" return pixels_or_percent_validator(value) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index cef76396c2..7fd824a87b 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -422,7 +422,7 @@ lvgl: id: lv_image src: cat_image align: top_left - y: 50 + y: "50" - tileview: id: tileview_id scrollbar_mode: active @@ -461,7 +461,7 @@ lvgl: bg_opa: transp knob: radius: 1 - width: 4 + width: "4" height: 10% bg_color: 0x000000 width: 100% From 2597975ae00dde096522b80824dbf9915a0d2fd7 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 22 Oct 2024 05:29:16 +0200 Subject: [PATCH 0522/1052] [rtttl] Add `get_gain()` (#7647) --- esphome/components/rtttl/rtttl.cpp | 5 ++++- esphome/components/rtttl/rtttl.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index 495b5c1c8a..db4cc731e4 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -26,7 +26,10 @@ inline double deg2rad(double degrees) { return degrees * PI_ON_180; } -void Rtttl::dump_config() { ESP_LOGCONFIG(TAG, "Rtttl"); } +void Rtttl::dump_config() { + ESP_LOGCONFIG(TAG, "Rtttl:"); + ESP_LOGCONFIG(TAG, " Gain: %f", gain_); +} void Rtttl::play(std::string rtttl) { if (this->state_ != State::STATE_STOPPED && this->state_ != State::STATE_STOPPING) { diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index 3cb6e3f5fb..10c290c5fb 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -39,6 +39,7 @@ class Rtttl : public Component { #ifdef USE_SPEAKER void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; } #endif + float get_gain() { return gain_; } void set_gain(float gain) { if (gain < 0.1f) gain = 0.1f; From a932ca2f64213e2af34c4d2e25f7f82766e98ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mart=C3=ADn?= Date: Tue, 22 Oct 2024 05:38:25 +0200 Subject: [PATCH 0523/1052] feat(MQTT): Add subscribe QoS to discovery (#7648) --- esphome/components/mqtt/__init__.py | 3 +++ esphome/components/mqtt/mqtt_component.cpp | 6 ++++++ esphome/components/mqtt/mqtt_component.h | 4 ++++ esphome/components/mqtt/mqtt_const.h | 2 ++ esphome/config_validation.py | 4 +++- esphome/const.py | 1 + tests/components/mqtt/common.yaml | 1 + 7 files changed, 20 insertions(+), 1 deletion(-) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 336d928f71..8851581ea0 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -41,6 +41,7 @@ from esphome.const import ( CONF_SHUTDOWN_MESSAGE, CONF_SSL_FINGERPRINTS, CONF_STATE_TOPIC, + CONF_SUBSCRIBE_QOS, CONF_TOPIC, CONF_TOPIC_PREFIX, CONF_TRIGGER_ID, @@ -518,6 +519,8 @@ async def register_mqtt_component(var, config): cg.add(var.set_qos(config[CONF_QOS])) if CONF_RETAIN in config: cg.add(var.set_retain(config[CONF_RETAIN])) + if CONF_SUBSCRIBE_QOS in config: + cg.add(var.set_subscribe_qos(config[CONF_SUBSCRIBE_QOS])) if not config.get(CONF_DISCOVERY, True): cg.add(var.disable_discovery()) if CONF_STATE_TOPIC in config: diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 295fbba5e5..3b9d367a7b 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -16,6 +16,8 @@ static const char *const TAG = "mqtt.component"; void MQTTComponent::set_qos(uint8_t qos) { this->qos_ = qos; } +void MQTTComponent::set_subscribe_qos(uint8_t qos) { this->subscribe_qos_ = qos; } + void MQTTComponent::set_retain(bool retain) { this->retain_ = retain; } std::string MQTTComponent::get_discovery_topic_(const MQTTDiscoveryInfo &discovery_info) const { @@ -76,6 +78,10 @@ bool MQTTComponent::send_discovery_() { config.command_topic = true; this->send_discovery(root, config); + // Set subscription QoS (default is 0) + if (this->subscribe_qos_ != 0) { + root[MQTT_QOS] = this->subscribe_qos_; + } // Fields from EntityBase if (this->get_entity()->has_own_name()) { diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 147840d11f..01ba98ad40 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -89,6 +89,9 @@ class MQTTComponent : public Component { void disable_discovery(); bool is_discovery_enabled() const; + /// Set the QOS for subscribe messages (used in discovery). + void set_subscribe_qos(uint8_t qos); + /// Override this method to return the component type (e.g. "light", "sensor", ...) virtual std::string component_type() const = 0; @@ -204,6 +207,7 @@ class MQTTComponent : public Component { bool command_retain_{false}; bool retain_{true}; uint8_t qos_{0}; + uint8_t subscribe_qos_{0}; bool discovery_enabled_{true}; bool resend_state_{false}; }; diff --git a/esphome/components/mqtt/mqtt_const.h b/esphome/components/mqtt/mqtt_const.h index 71f169fbe8..c1c40c4b6d 100644 --- a/esphome/components/mqtt/mqtt_const.h +++ b/esphome/components/mqtt/mqtt_const.h @@ -180,6 +180,7 @@ constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "pr_mode_cmd_t"; constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "pr_mode_stat_t"; constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "pr_mode_val_tpl"; constexpr const char *const MQTT_PRESET_MODES = "pr_modes"; +constexpr const char *const MQTT_QOS = "qos"; constexpr const char *const MQTT_RED_TEMPLATE = "r_tpl"; constexpr const char *const MQTT_RETAIN = "ret"; constexpr const char *const MQTT_RGB_COMMAND_TEMPLATE = "rgb_cmd_tpl"; @@ -441,6 +442,7 @@ constexpr const char *const MQTT_PRESET_MODE_COMMAND_TOPIC = "preset_mode_comman constexpr const char *const MQTT_PRESET_MODE_STATE_TOPIC = "preset_mode_state_topic"; constexpr const char *const MQTT_PRESET_MODE_VALUE_TEMPLATE = "preset_mode_value_template"; constexpr const char *const MQTT_PRESET_MODES = "preset_modes"; +constexpr const char *const MQTT_QOS = "qos"; constexpr const char *const MQTT_RED_TEMPLATE = "red_template"; constexpr const char *const MQTT_RETAIN = "retain"; constexpr const char *const MQTT_RGB_COMMAND_TEMPLATE = "rgb_command_template"; diff --git a/esphome/config_validation.py b/esphome/config_validation.py index a7525a62dd..98b81ec328 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -40,6 +40,7 @@ from esphome.const import ( CONF_SECOND, CONF_SETUP_PRIORITY, CONF_STATE_TOPIC, + CONF_SUBSCRIBE_QOS, CONF_TOPIC, CONF_TYPE, CONF_TYPE_ID, @@ -1893,9 +1894,10 @@ MQTT_COMPONENT_AVAILABILITY_SCHEMA = Schema( MQTT_COMPONENT_SCHEMA = Schema( { - Optional(CONF_QOS): All(requires_component("mqtt"), int_range(min=0, max=2)), + Optional(CONF_QOS): All(requires_component("mqtt"), mqtt_qos), Optional(CONF_RETAIN): All(requires_component("mqtt"), boolean), Optional(CONF_DISCOVERY): All(requires_component("mqtt"), boolean), + Optional(CONF_SUBSCRIBE_QOS): All(requires_component("mqtt"), mqtt_qos), Optional(CONF_STATE_TOPIC): All(requires_component("mqtt"), publish_topic), Optional(CONF_AVAILABILITY): All( requires_component("mqtt"), Any(None, MQTT_COMPONENT_AVAILABILITY_SCHEMA) diff --git a/esphome/const.py b/esphome/const.py index a3a4318d69..54ebb2815f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -819,6 +819,7 @@ CONF_STOP = "stop" CONF_STOP_ACTION = "stop_action" CONF_STORE_BASELINE = "store_baseline" CONF_SUBNET = "subnet" +CONF_SUBSCRIBE_QOS = "subscribe_qos" CONF_SUBSTITUTIONS = "substitutions" CONF_SUM = "sum" CONF_SUPPLEMENTAL_COOLING_ACTION = "supplemental_cooling_action" diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index f7a727ab2f..5ed6335d65 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -227,6 +227,7 @@ datetime: type: date state_topic: some/topic/date qos: 2 + subscribe_qos: 2 set_action: - logger.log: "set_value" on_value: From 7c0543862ad593916e1230d1dc2f2dec8e61c507 Mon Sep 17 00:00:00 2001 From: Kyle Cascade Date: Mon, 21 Oct 2024 21:11:23 -0700 Subject: [PATCH 0524/1052] Humanized the missing MQTT log topic error message (#7634) --- esphome/mqtt.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/mqtt.py b/esphome/mqtt.py index c1c45799cc..d55fb0202d 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -209,6 +209,12 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None): elif CONF_MQTT in config: conf = config[CONF_MQTT] if CONF_LOG_TOPIC in conf: + if config[CONF_MQTT][CONF_LOG_TOPIC] is None: + _LOGGER.error("MQTT log topic set to null, can't start MQTT logs") + return 1 + if CONF_TOPIC not in config[CONF_MQTT][CONF_LOG_TOPIC]: + _LOGGER.error("MQTT log topic not available, can't start MQTT logs") + return 1 topic = config[CONF_MQTT][CONF_LOG_TOPIC][CONF_TOPIC] elif CONF_TOPIC_PREFIX in config[CONF_MQTT]: topic = f"{config[CONF_MQTT][CONF_TOPIC_PREFIX]}/debug" From 68844c48698c6983a3d22498dbe70ee20c9835bf Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 23 Oct 2024 10:16:55 +1100 Subject: [PATCH 0525/1052] [lvgl] Some properties were not templatable (Bugfix) (#7655) --- esphome/components/lvgl/lv_validation.py | 4 ++++ esphome/components/lvgl/schemas.py | 14 +++++++------- tests/components/lvgl/lvgl-package.yaml | 7 +++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 9af25a4e90..b91b0905df 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -267,6 +267,9 @@ def angle(value): return int(cv.float_range(0.0, 360.0)(cv.angle(value)) * 10) +lv_angle = LValidator(angle, uint32) + + @schema_extractor("one_of") def size_validator(value): """A size in one axis - one of "size_content", a number (pixels) or a percentage""" @@ -401,6 +404,7 @@ class TextValidator(LValidator): lv_text = TextValidator() lv_float = LValidator(cv.float_, cg.float_) lv_int = LValidator(cv.int_, cg.int_) +lv_positive_int = LValidator(cv.positive_int, cg.int_) lv_brightness = LValidator(cv.percentage, cg.float_, retmapper=lambda x: int(x * 255)) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 7599d64416..bb14c11ddd 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -91,7 +91,7 @@ STYLE_PROPS = { "arc_opa": lvalid.opacity, "arc_color": lvalid.lv_color, "arc_rounded": lvalid.lv_bool, - "arc_width": cv.positive_int, + "arc_width": lvalid.lv_positive_int, "anim_time": lvalid.lv_milliseconds, "bg_color": lvalid.lv_color, "bg_grad": lv_gradient, @@ -111,7 +111,7 @@ STYLE_PROPS = { "border_side": df.LvConstant( "LV_BORDER_SIDE_", "NONE", "TOP", "BOTTOM", "LEFT", "RIGHT", "INTERNAL" ).several_of, - "border_width": cv.positive_int, + "border_width": lvalid.lv_positive_int, "clip_corner": lvalid.lv_bool, "color_filter_opa": lvalid.opacity, "height": lvalid.size, @@ -134,11 +134,11 @@ STYLE_PROPS = { "pad_right": lvalid.pixels, "pad_top": lvalid.pixels, "shadow_color": lvalid.lv_color, - "shadow_ofs_x": cv.int_, - "shadow_ofs_y": cv.int_, + "shadow_ofs_x": lvalid.lv_int, + "shadow_ofs_y": lvalid.lv_int, "shadow_opa": lvalid.opacity, - "shadow_spread": cv.int_, - "shadow_width": cv.positive_int, + "shadow_spread": lvalid.lv_int, + "shadow_width": lvalid.lv_positive_int, "text_align": df.LvConstant( "LV_TEXT_ALIGN_", "LEFT", "CENTER", "RIGHT", "AUTO" ).one_of, @@ -150,7 +150,7 @@ STYLE_PROPS = { "text_letter_space": cv.positive_int, "text_line_space": cv.positive_int, "text_opa": lvalid.opacity, - "transform_angle": lvalid.angle, + "transform_angle": lvalid.lv_angle, "transform_height": lvalid.pixels_or_percent, "transform_pivot_x": lvalid.pixels_or_percent, "transform_pivot_y": lvalid.pixels_or_percent, diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 7fd824a87b..4962a71596 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -323,6 +323,13 @@ lvgl: id: button_button width: 20% height: 10% + transform_angle: !lambda return 180*100; + arc_width: !lambda return 4; + border_width: !lambda return 6; + shadow_ofs_x: !lambda return 6; + shadow_ofs_y: !lambda return 6; + shadow_spread: !lambda return 6; + shadow_width: !lambda return 6; pressed: bg_color: light_blue checkable: true From dd8d25e43f6d30f93391aeee207a912dd52907d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Poczkodi?= Date: Wed, 23 Oct 2024 05:23:10 +0200 Subject: [PATCH 0526/1052] i2c_device (#7641) --- CODEOWNERS | 1 + esphome/components/i2c_device/__init__.py | 26 +++++++++++++++++++ esphome/components/i2c_device/i2c_device.cpp | 17 ++++++++++++ esphome/components/i2c_device/i2c_device.h | 18 +++++++++++++ .../components/i2c_device/test.esp32-ard.yaml | 8 ++++++ .../i2c_device/test.esp32-c3-ard.yaml | 8 ++++++ .../i2c_device/test.esp32-c3-idf.yaml | 8 ++++++ .../components/i2c_device/test.esp32-idf.yaml | 8 ++++++ .../i2c_device/test.esp8266-ard.yaml | 8 ++++++ .../i2c_device/test.rp2040-ard.yaml | 8 ++++++ 10 files changed, 110 insertions(+) create mode 100644 esphome/components/i2c_device/__init__.py create mode 100644 esphome/components/i2c_device/i2c_device.cpp create mode 100644 esphome/components/i2c_device/i2c_device.h create mode 100644 tests/components/i2c_device/test.esp32-ard.yaml create mode 100644 tests/components/i2c_device/test.esp32-c3-ard.yaml create mode 100644 tests/components/i2c_device/test.esp32-c3-idf.yaml create mode 100644 tests/components/i2c_device/test.esp32-idf.yaml create mode 100644 tests/components/i2c_device/test.esp8266-ard.yaml create mode 100644 tests/components/i2c_device/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 53300d6740..616b18293d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -198,6 +198,7 @@ esphome/components/htu31d/* @betterengineering esphome/components/hydreon_rgxx/* @functionpointer esphome/components/hyt271/* @Philippe12 esphome/components/i2c/* @esphome/core +esphome/components/i2c_device/* @gabest11 esphome/components/i2s_audio/* @jesserockz esphome/components/i2s_audio/media_player/* @jesserockz esphome/components/i2s_audio/microphone/* @jesserockz diff --git a/esphome/components/i2c_device/__init__.py b/esphome/components/i2c_device/__init__.py new file mode 100644 index 0000000000..e145ba56f8 --- /dev/null +++ b/esphome/components/i2c_device/__init__.py @@ -0,0 +1,26 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import i2c +from esphome.const import CONF_ID + +DEPENDENCIES = ["i2c"] +CODEOWNERS = ["@gabest11"] +MULTI_CONF = True + +i2c_device_ns = cg.esphome_ns.namespace("i2c_device") + +I2CDeviceComponent = i2c_device_ns.class_( + "I2CDeviceComponent", cg.Component, i2c.I2CDevice +) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_ID): cv.declare_id(I2CDeviceComponent), + } +).extend(i2c.i2c_device_schema(None)) + + +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/i2c_device/i2c_device.cpp b/esphome/components/i2c_device/i2c_device.cpp new file mode 100644 index 0000000000..455c68fbed --- /dev/null +++ b/esphome/components/i2c_device/i2c_device.cpp @@ -0,0 +1,17 @@ +#include "i2c_device.h" +#include "esphome/core/log.h" +#include "esphome/core/hal.h" +#include + +namespace esphome { +namespace i2c_device { + +static const char *const TAG = "i2c_device"; + +void I2CDeviceComponent::dump_config() { + ESP_LOGCONFIG(TAG, "I2CDevice"); + LOG_I2C_DEVICE(this); +} + +} // namespace i2c_device +} // namespace esphome diff --git a/esphome/components/i2c_device/i2c_device.h b/esphome/components/i2c_device/i2c_device.h new file mode 100644 index 0000000000..ab118e3e89 --- /dev/null +++ b/esphome/components/i2c_device/i2c_device.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace i2c_device { + +class I2CDeviceComponent : public Component, public i2c::I2CDevice { + public: + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: +}; + +} // namespace i2c_device +} // namespace esphome diff --git a/tests/components/i2c_device/test.esp32-ard.yaml b/tests/components/i2c_device/test.esp32-ard.yaml new file mode 100644 index 0000000000..6169d113f8 --- /dev/null +++ b/tests/components/i2c_device/test.esp32-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_i2c + scl: 16 + sda: 17 + +i2c_device: + id: i2cdev + address: 0x2C diff --git a/tests/components/i2c_device/test.esp32-c3-ard.yaml b/tests/components/i2c_device/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..5d53d12208 --- /dev/null +++ b/tests/components/i2c_device/test.esp32-c3-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_i2c + scl: 5 + sda: 4 + +i2c_device: + id: i2cdev + address: 0x2C diff --git a/tests/components/i2c_device/test.esp32-c3-idf.yaml b/tests/components/i2c_device/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5d53d12208 --- /dev/null +++ b/tests/components/i2c_device/test.esp32-c3-idf.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_i2c + scl: 5 + sda: 4 + +i2c_device: + id: i2cdev + address: 0x2C diff --git a/tests/components/i2c_device/test.esp32-idf.yaml b/tests/components/i2c_device/test.esp32-idf.yaml new file mode 100644 index 0000000000..6169d113f8 --- /dev/null +++ b/tests/components/i2c_device/test.esp32-idf.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_i2c + scl: 16 + sda: 17 + +i2c_device: + id: i2cdev + address: 0x2C diff --git a/tests/components/i2c_device/test.esp8266-ard.yaml b/tests/components/i2c_device/test.esp8266-ard.yaml new file mode 100644 index 0000000000..5d53d12208 --- /dev/null +++ b/tests/components/i2c_device/test.esp8266-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_i2c + scl: 5 + sda: 4 + +i2c_device: + id: i2cdev + address: 0x2C diff --git a/tests/components/i2c_device/test.rp2040-ard.yaml b/tests/components/i2c_device/test.rp2040-ard.yaml new file mode 100644 index 0000000000..5d53d12208 --- /dev/null +++ b/tests/components/i2c_device/test.rp2040-ard.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_i2c + scl: 5 + sda: 4 + +i2c_device: + id: i2cdev + address: 0x2C From fdebf041967549866f438431284c0690bec3d6da Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 23 Oct 2024 13:25:31 -0400 Subject: [PATCH 0527/1052] [voice_assistant] Bugfix: Fix crash on start (#7662) --- .../voice_assistant/voice_assistant.cpp | 54 +++++++++++-------- .../voice_assistant/voice_assistant.h | 6 +-- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 0b53e74ba3..6f164f69d3 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -433,16 +433,18 @@ void VoiceAssistant::loop() { #ifdef USE_SPEAKER void VoiceAssistant::write_speaker_() { - if (this->speaker_buffer_size_ > 0) { - size_t write_chunk = std::min(this->speaker_buffer_size_, 4 * 1024); - size_t written = this->speaker_->play(this->speaker_buffer_, write_chunk); - if (written > 0) { - memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); - this->speaker_buffer_size_ -= written; - this->speaker_buffer_index_ -= written; - this->set_timeout("speaker-timeout", 5000, [this]() { this->speaker_->stop(); }); - } else { - ESP_LOGV(TAG, "Speaker buffer full, trying again next loop"); + if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { + if (this->speaker_buffer_size_ > 0) { + size_t write_chunk = std::min(this->speaker_buffer_size_, 4 * 1024); + size_t written = this->speaker_->play(this->speaker_buffer_, write_chunk); + if (written > 0) { + memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); + this->speaker_buffer_size_ -= written; + this->speaker_buffer_index_ -= written; + this->set_timeout("speaker-timeout", 5000, [this]() { this->speaker_->stop(); }); + } else { + ESP_LOGV(TAG, "Speaker buffer full, trying again next loop"); + } } } } @@ -772,16 +774,20 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } case api::enums::VOICE_ASSISTANT_TTS_STREAM_START: { #ifdef USE_SPEAKER - this->wait_for_stream_end_ = true; - ESP_LOGD(TAG, "TTS stream start"); - this->defer([this] { this->tts_stream_start_trigger_->trigger(); }); + if (this->speaker_ != nullptr) { + this->wait_for_stream_end_ = true; + ESP_LOGD(TAG, "TTS stream start"); + this->defer([this] { this->tts_stream_start_trigger_->trigger(); }); + } #endif break; } case api::enums::VOICE_ASSISTANT_TTS_STREAM_END: { #ifdef USE_SPEAKER - this->stream_ended_ = true; - ESP_LOGD(TAG, "TTS stream end"); + if (this->speaker_ != nullptr) { + this->stream_ended_ = true; + ESP_LOGD(TAG, "TTS stream end"); + } #endif break; } @@ -802,14 +808,16 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) { #ifdef USE_SPEAKER // We should never get to this function if there is no speaker anyway - if (this->speaker_buffer_index_ + msg.data.length() < SPEAKER_BUFFER_SIZE) { - memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data.data(), msg.data.length()); - this->speaker_buffer_index_ += msg.data.length(); - this->speaker_buffer_size_ += msg.data.length(); - this->speaker_bytes_received_ += msg.data.length(); - ESP_LOGV(TAG, "Received audio: %u bytes from API", msg.data.length()); - } else { - ESP_LOGE(TAG, "Cannot receive audio, buffer is full"); + if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { + if (this->speaker_buffer_index_ + msg.data.length() < SPEAKER_BUFFER_SIZE) { + memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data.data(), msg.data.length()); + this->speaker_buffer_index_ += msg.data.length(); + this->speaker_buffer_size_ += msg.data.length(); + this->speaker_bytes_received_ += msg.data.length(); + ESP_LOGV(TAG, "Received audio: %u bytes from API", msg.data.length()); + } else { + ESP_LOGE(TAG, "Cannot receive audio, buffer is full"); + } } #endif } diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 870e2acdaa..0016d3157c 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -250,7 +250,7 @@ class VoiceAssistant : public Component { #ifdef USE_SPEAKER void write_speaker_(); speaker::Speaker *speaker_{nullptr}; - uint8_t *speaker_buffer_; + uint8_t *speaker_buffer_{nullptr}; size_t speaker_buffer_index_{0}; size_t speaker_buffer_size_{0}; size_t speaker_bytes_received_{0}; @@ -282,8 +282,8 @@ class VoiceAssistant : public Component { float volume_multiplier_; uint32_t conversation_timeout_; - uint8_t *send_buffer_; - int16_t *input_buffer_; + uint8_t *send_buffer_{nullptr}; + int16_t *input_buffer_{nullptr}; bool continuous_{false}; bool silence_detection_; From 833565feb985e91c9512774a363ea2e5ca79d267 Mon Sep 17 00:00:00 2001 From: Kyle Cascade Date: Mon, 21 Oct 2024 21:11:23 -0700 Subject: [PATCH 0528/1052] Humanized the missing MQTT log topic error message (#7634) --- esphome/mqtt.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/mqtt.py b/esphome/mqtt.py index c1c45799cc..d55fb0202d 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -209,6 +209,12 @@ def show_logs(config, topic=None, username=None, password=None, client_id=None): elif CONF_MQTT in config: conf = config[CONF_MQTT] if CONF_LOG_TOPIC in conf: + if config[CONF_MQTT][CONF_LOG_TOPIC] is None: + _LOGGER.error("MQTT log topic set to null, can't start MQTT logs") + return 1 + if CONF_TOPIC not in config[CONF_MQTT][CONF_LOG_TOPIC]: + _LOGGER.error("MQTT log topic not available, can't start MQTT logs") + return 1 topic = config[CONF_MQTT][CONF_LOG_TOPIC][CONF_TOPIC] elif CONF_TOPIC_PREFIX in config[CONF_MQTT]: topic = f"{config[CONF_MQTT][CONF_TOPIC_PREFIX]}/debug" From 8d90d256bf2518c46676a0a7dfe37ef3dd1868c8 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 23 Oct 2024 10:16:55 +1100 Subject: [PATCH 0529/1052] [lvgl] Some properties were not templatable (Bugfix) (#7655) --- esphome/components/lvgl/lv_validation.py | 4 ++++ esphome/components/lvgl/schemas.py | 14 +++++++------- tests/components/lvgl/lvgl-package.yaml | 7 +++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index fd840cc417..a58fbc33e0 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -267,6 +267,9 @@ def angle(value): return int(cv.float_range(0.0, 360.0)(cv.angle(value)) * 10) +lv_angle = LValidator(angle, uint32) + + @schema_extractor("one_of") def size_validator(value): """A size in one axis - one of "size_content", a number (pixels) or a percentage""" @@ -403,6 +406,7 @@ class TextValidator(LValidator): lv_text = TextValidator() lv_float = LValidator(cv.float_, cg.float_) lv_int = LValidator(cv.int_, cg.int_) +lv_positive_int = LValidator(cv.positive_int, cg.int_) lv_brightness = LValidator(cv.percentage, cg.float_, retmapper=lambda x: int(x * 255)) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 780057623a..0d5a609a2d 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -91,7 +91,7 @@ STYLE_PROPS = { "arc_opa": lvalid.opacity, "arc_color": lvalid.lv_color, "arc_rounded": lvalid.lv_bool, - "arc_width": cv.positive_int, + "arc_width": lvalid.lv_positive_int, "anim_time": lvalid.lv_milliseconds, "bg_color": lvalid.lv_color, "bg_grad": lv_gradient, @@ -111,7 +111,7 @@ STYLE_PROPS = { "border_side": df.LvConstant( "LV_BORDER_SIDE_", "NONE", "TOP", "BOTTOM", "LEFT", "RIGHT", "INTERNAL" ).several_of, - "border_width": cv.positive_int, + "border_width": lvalid.lv_positive_int, "clip_corner": lvalid.lv_bool, "color_filter_opa": lvalid.opacity, "height": lvalid.size, @@ -134,11 +134,11 @@ STYLE_PROPS = { "pad_right": lvalid.pixels, "pad_top": lvalid.pixels, "shadow_color": lvalid.lv_color, - "shadow_ofs_x": cv.int_, - "shadow_ofs_y": cv.int_, + "shadow_ofs_x": lvalid.lv_int, + "shadow_ofs_y": lvalid.lv_int, "shadow_opa": lvalid.opacity, - "shadow_spread": cv.int_, - "shadow_width": cv.positive_int, + "shadow_spread": lvalid.lv_int, + "shadow_width": lvalid.lv_positive_int, "text_align": df.LvConstant( "LV_TEXT_ALIGN_", "LEFT", "CENTER", "RIGHT", "AUTO" ).one_of, @@ -150,7 +150,7 @@ STYLE_PROPS = { "text_letter_space": cv.positive_int, "text_line_space": cv.positive_int, "text_opa": lvalid.opacity, - "transform_angle": lvalid.angle, + "transform_angle": lvalid.lv_angle, "transform_height": lvalid.pixels_or_percent, "transform_pivot_x": lvalid.pixels_or_percent, "transform_pivot_y": lvalid.pixels_or_percent, diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 1770c1bfbc..2e05cf10d3 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -299,6 +299,13 @@ lvgl: id: button_button width: 20% height: 10% + transform_angle: !lambda return 180*100; + arc_width: !lambda return 4; + border_width: !lambda return 6; + shadow_ofs_x: !lambda return 6; + shadow_ofs_y: !lambda return 6; + shadow_spread: !lambda return 6; + shadow_width: !lambda return 6; pressed: bg_color: light_blue checkable: true From 156ad773c91edca8b14a8518363287e027ef8147 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 23 Oct 2024 13:25:31 -0400 Subject: [PATCH 0530/1052] [voice_assistant] Bugfix: Fix crash on start (#7662) --- .../voice_assistant/voice_assistant.cpp | 54 +++++++++++-------- .../voice_assistant/voice_assistant.h | 6 +-- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 0b53e74ba3..6f164f69d3 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -433,16 +433,18 @@ void VoiceAssistant::loop() { #ifdef USE_SPEAKER void VoiceAssistant::write_speaker_() { - if (this->speaker_buffer_size_ > 0) { - size_t write_chunk = std::min(this->speaker_buffer_size_, 4 * 1024); - size_t written = this->speaker_->play(this->speaker_buffer_, write_chunk); - if (written > 0) { - memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); - this->speaker_buffer_size_ -= written; - this->speaker_buffer_index_ -= written; - this->set_timeout("speaker-timeout", 5000, [this]() { this->speaker_->stop(); }); - } else { - ESP_LOGV(TAG, "Speaker buffer full, trying again next loop"); + if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { + if (this->speaker_buffer_size_ > 0) { + size_t write_chunk = std::min(this->speaker_buffer_size_, 4 * 1024); + size_t written = this->speaker_->play(this->speaker_buffer_, write_chunk); + if (written > 0) { + memmove(this->speaker_buffer_, this->speaker_buffer_ + written, this->speaker_buffer_size_ - written); + this->speaker_buffer_size_ -= written; + this->speaker_buffer_index_ -= written; + this->set_timeout("speaker-timeout", 5000, [this]() { this->speaker_->stop(); }); + } else { + ESP_LOGV(TAG, "Speaker buffer full, trying again next loop"); + } } } } @@ -772,16 +774,20 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } case api::enums::VOICE_ASSISTANT_TTS_STREAM_START: { #ifdef USE_SPEAKER - this->wait_for_stream_end_ = true; - ESP_LOGD(TAG, "TTS stream start"); - this->defer([this] { this->tts_stream_start_trigger_->trigger(); }); + if (this->speaker_ != nullptr) { + this->wait_for_stream_end_ = true; + ESP_LOGD(TAG, "TTS stream start"); + this->defer([this] { this->tts_stream_start_trigger_->trigger(); }); + } #endif break; } case api::enums::VOICE_ASSISTANT_TTS_STREAM_END: { #ifdef USE_SPEAKER - this->stream_ended_ = true; - ESP_LOGD(TAG, "TTS stream end"); + if (this->speaker_ != nullptr) { + this->stream_ended_ = true; + ESP_LOGD(TAG, "TTS stream end"); + } #endif break; } @@ -802,14 +808,16 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) { #ifdef USE_SPEAKER // We should never get to this function if there is no speaker anyway - if (this->speaker_buffer_index_ + msg.data.length() < SPEAKER_BUFFER_SIZE) { - memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data.data(), msg.data.length()); - this->speaker_buffer_index_ += msg.data.length(); - this->speaker_buffer_size_ += msg.data.length(); - this->speaker_bytes_received_ += msg.data.length(); - ESP_LOGV(TAG, "Received audio: %u bytes from API", msg.data.length()); - } else { - ESP_LOGE(TAG, "Cannot receive audio, buffer is full"); + if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { + if (this->speaker_buffer_index_ + msg.data.length() < SPEAKER_BUFFER_SIZE) { + memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data.data(), msg.data.length()); + this->speaker_buffer_index_ += msg.data.length(); + this->speaker_buffer_size_ += msg.data.length(); + this->speaker_bytes_received_ += msg.data.length(); + ESP_LOGV(TAG, "Received audio: %u bytes from API", msg.data.length()); + } else { + ESP_LOGE(TAG, "Cannot receive audio, buffer is full"); + } } #endif } diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 870e2acdaa..0016d3157c 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -250,7 +250,7 @@ class VoiceAssistant : public Component { #ifdef USE_SPEAKER void write_speaker_(); speaker::Speaker *speaker_{nullptr}; - uint8_t *speaker_buffer_; + uint8_t *speaker_buffer_{nullptr}; size_t speaker_buffer_index_{0}; size_t speaker_buffer_size_{0}; size_t speaker_bytes_received_{0}; @@ -282,8 +282,8 @@ class VoiceAssistant : public Component { float volume_multiplier_; uint32_t conversation_timeout_; - uint8_t *send_buffer_; - int16_t *input_buffer_; + uint8_t *send_buffer_{nullptr}; + int16_t *input_buffer_{nullptr}; bool continuous_{false}; bool silence_detection_; From 127acfde6482ebb7e44894af0c17660263166fce Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:15:40 +1300 Subject: [PATCH 0531/1052] Bump version to 2024.10.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5fa457b25a..032a4c79a0 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.10.1" +__version__ = "2024.10.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 4289e00ad0ee3f466a60d9044c97157a8070a047 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 08:06:45 +1300 Subject: [PATCH 0532/1052] Bump actions/cache from 4.1.1 to 4.1.2 in /.github/actions/restore-python (#7659) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index 1f5812691e..c6978f68c5 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -22,7 +22,7 @@ runs: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache/restore@v4.1.1 + uses: actions/cache/restore@v4.1.2 with: path: venv # yamllint disable-line rule:line-length From 2feffddc550c0241aede210bb273b8e0001084f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Oct 2024 08:06:53 +1300 Subject: [PATCH 0533/1052] Bump actions/cache from 4.1.1 to 4.1.2 (#7660) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 178b914a1c..0d2f1c877d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.1.1 + uses: actions/cache@v4.1.2 with: path: venv # yamllint disable-line rule:line-length @@ -302,14 +302,14 @@ jobs: - name: Cache platformio if: github.ref == 'refs/heads/dev' - uses: actions/cache@v4.1.1 + uses: actions/cache@v4.1.2 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} - name: Cache platformio if: github.ref != 'refs/heads/dev' - uses: actions/cache/restore@v4.1.1 + uses: actions/cache/restore@v4.1.2 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} From bff0e81ed3863ee960d1612fd64bec78fe34c03b Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 23 Oct 2024 16:37:38 -0400 Subject: [PATCH 0534/1052] [speaker, i2s_audio] Support audio_dac component, mute actions, and improved logging (#7664) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 4 +- .../components/i2s_audio/speaker/__init__.py | 2 +- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 72 +++++++++++++++---- .../i2s_audio/speaker/i2s_audio_speaker.h | 14 ++-- esphome/components/speaker/__init__.py | 31 ++++++-- esphome/components/speaker/automation.h | 20 ++++++ esphome/components/speaker/speaker.h | 42 ++++++++++- tests/components/speaker/test.esp32-ard.yaml | 2 + .../components/speaker/test.esp32-c3-ard.yaml | 2 + .../components/speaker/test.esp32-c3-idf.yaml | 2 + tests/components/speaker/test.esp32-idf.yaml | 15 +++- 11 files changed, 177 insertions(+), 29 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 616b18293d..f96e43d5b7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -202,7 +202,7 @@ esphome/components/i2c_device/* @gabest11 esphome/components/i2s_audio/* @jesserockz esphome/components/i2s_audio/media_player/* @jesserockz esphome/components/i2s_audio/microphone/* @jesserockz -esphome/components/i2s_audio/speaker/* @jesserockz +esphome/components/i2s_audio/speaker/* @jesserockz @kahrendt esphome/components/iaqcore/* @yozik04 esphome/components/ili9xxx/* @clydebarrow @nielsnl68 esphome/components/improv_base/* @esphome/core @@ -377,7 +377,7 @@ esphome/components/smt100/* @piechade esphome/components/sn74hc165/* @jesserockz esphome/components/socket/* @esphome/core esphome/components/sonoff_d1/* @anatoly-savchenkov -esphome/components/speaker/* @jesserockz +esphome/components/speaker/* @jesserockz @kahrendt esphome/components/spi/* @clydebarrow @esphome/core esphome/components/spi_device/* @clydebarrow esphome/components/spi_led_strip/* @clydebarrow diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index 9fdaced64c..dd43d6cb39 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -17,7 +17,7 @@ from .. import ( ) AUTO_LOAD = ["audio"] -CODEOWNERS = ["@jesserockz"] +CODEOWNERS = ["@jesserockz", "@kahrendt"] DEPENDENCIES = ["i2s_audio"] I2SAudioSpeaker = i2s_audio_ns.class_( diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 4fc489d0a3..cf6c3bbbba 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -32,6 +32,7 @@ enum SpeakerEventGroupBits : uint32_t { STATE_RUNNING = (1 << 11), STATE_STOPPING = (1 << 12), STATE_STOPPED = (1 << 13), + ERR_INVALID_FORMAT = (1 << 14), ERR_TASK_FAILED_TO_START = (1 << 15), ERR_ESP_INVALID_STATE = (1 << 16), ERR_ESP_INVALID_ARG = (1 << 17), @@ -104,16 +105,6 @@ void I2SAudioSpeaker::setup() { void I2SAudioSpeaker::loop() { uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); - if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) { - this->status_set_error("Failed to start speaker task"); - } - - if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { - uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; - ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); - this->status_set_warning(); - } - if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) { ESP_LOGD(TAG, "Starting Speaker"); this->state_ = speaker::STATE_STARTING; @@ -139,12 +130,64 @@ void I2SAudioSpeaker::loop() { this->speaker_task_handle_ = nullptr; } } + + if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) { + this->status_set_error("Failed to start speaker task"); + xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); + } + + if (event_group_bits & SpeakerEventGroupBits::ERR_INVALID_FORMAT) { + this->status_set_error("Failed to adjust I2S bus to match the incoming audio"); + ESP_LOGE(TAG, + "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8, + this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels, + this->audio_stream_info_.bits_per_sample); + } + + if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { + uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; + ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); + this->status_set_warning(); + } } void I2SAudioSpeaker::set_volume(float volume) { this->volume_ = volume; - ssize_t decibel_index = remap(volume, 0.0f, 1.0f, 0, Q15_VOLUME_SCALING_FACTORS.size() - 1); - this->q15_volume_factor_ = Q15_VOLUME_SCALING_FACTORS[decibel_index]; +#ifdef USE_AUDIO_DAC + if (this->audio_dac_ != nullptr) { + if (volume > 0.0) { + this->audio_dac_->set_mute_off(); + } + this->audio_dac_->set_volume(volume); + } else +#endif + { + // Fallback to software volume control by using a Q15 fixed point scaling factor + ssize_t decibel_index = remap(volume, 0.0f, 1.0f, 0, Q15_VOLUME_SCALING_FACTORS.size() - 1); + this->q15_volume_factor_ = Q15_VOLUME_SCALING_FACTORS[decibel_index]; + } +} + +void I2SAudioSpeaker::set_mute_state(bool mute_state) { + this->mute_state_ = mute_state; +#ifdef USE_AUDIO_DAC + if (this->audio_dac_) { + if (mute_state) { + this->audio_dac_->set_mute_on(); + } else { + this->audio_dac_->set_mute_off(); + } + } else +#endif + { + if (mute_state) { + // Fallback to software volume control and scale by 0 + this->q15_volume_factor_ = 0; + } else { + // Revert to previous volume when unmuting + this->set_volume(this->volume_); + } + } } size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) { @@ -275,6 +318,9 @@ void I2SAudioSpeaker::speaker_task(void *params) { i2s_zero_dma_buffer(this_speaker->parent_->get_port()); } } + } else { + // Couldn't configure the I2S port to be compatible with the incoming audio + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_INVALID_FORMAT); } i2s_zero_dma_buffer(this_speaker->parent_->get_port()); @@ -288,7 +334,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { } void I2SAudioSpeaker::start() { - if (this->is_failed()) + if (this->is_failed() || this->status_has_error()) return; if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING)) return; diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 245f97d1e7..3c512d4d4d 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -49,11 +49,17 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp bool has_buffered_data() const override; - /// @brief Sets the volume of the speaker. It is implemented as a software volume control. - /// Overrides the default setter to convert the floating point volume to a Q15 fixed-point factor. - /// @param volume + /// @brief Sets the volume of the speaker. Uses the speaker's configured audio dac component. If unavailble, it is + /// implemented as a software volume control. Overrides the default setter to convert the floating point volume to a + /// Q15 fixed-point factor. + /// @param volume between 0.0 and 1.0 void set_volume(float volume) override; - float get_volume() override { return this->volume_; } + + /// @brief Mutes or unmute the speaker. Uses the speaker's configured audio dac component. If unavailble, it is + /// implemented as a software volume control. Overrides the default setter to convert the floating point volume to a + /// Q15 fixed-point factor. + /// @param mute_state true for muting, false for unmuting + void set_mute_state(bool mute_state) override; protected: /// @brief Function for the FreeRTOS task handling audio output. diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index 1bbc0b02ef..7a668dc2f3 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -1,15 +1,18 @@ from esphome import automation from esphome.automation import maybe_simple_id import esphome.codegen as cg +from esphome.components import audio_dac import esphome.config_validation as cv from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME from esphome.core import CORE from esphome.coroutine import coroutine_with_priority -CODEOWNERS = ["@jesserockz"] +CODEOWNERS = ["@jesserockz", "@kahrendt"] IS_PLATFORM_COMPONENT = True +CONF_AUDIO_DAC = "audio_dac" + speaker_ns = cg.esphome_ns.namespace("speaker") Speaker = speaker_ns.class_("Speaker") @@ -26,6 +29,12 @@ FinishAction = speaker_ns.class_( VolumeSetAction = speaker_ns.class_( "VolumeSetAction", automation.Action, cg.Parented.template(Speaker) ) +MuteOnAction = speaker_ns.class_( + "MuteOnAction", automation.Action, cg.Parented.template(Speaker) +) +MuteOffAction = speaker_ns.class_( + "MuteOffAction", automation.Action, cg.Parented.template(Speaker) +) IsPlayingCondition = speaker_ns.class_("IsPlayingCondition", automation.Condition) @@ -33,7 +42,9 @@ IsStoppedCondition = speaker_ns.class_("IsStoppedCondition", automation.Conditio async def setup_speaker_core_(var, config): - pass + if audio_dac_config := config.get(CONF_AUDIO_DAC): + aud_dac = await cg.get_variable(audio_dac_config) + cg.add(var.set_audio_dac(aud_dac)) async def register_speaker(var, config): @@ -42,8 +53,11 @@ async def register_speaker(var, config): await setup_speaker_core_(var, config) -SPEAKER_SCHEMA = cv.Schema({}) - +SPEAKER_SCHEMA = cv.Schema( + { + cv.Optional(CONF_AUDIO_DAC): cv.use_id(audio_dac.AudioDac), + } +) SPEAKER_AUTOMATION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(Speaker)}) @@ -113,6 +127,15 @@ async def speaker_volume_set_action(config, action_id, template_arg, args): return var +@automation.register_action( + "speaker.mute_off", MuteOffAction, SPEAKER_AUTOMATION_SCHEMA +) +@automation.register_action("speaker.mute_on", MuteOnAction, SPEAKER_AUTOMATION_SCHEMA) +async def speaker_mute_action_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) + + @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(speaker_ns.using) diff --git a/esphome/components/speaker/automation.h b/esphome/components/speaker/automation.h index 9efda011f2..c083796eea 100644 --- a/esphome/components/speaker/automation.h +++ b/esphome/components/speaker/automation.h @@ -39,6 +39,26 @@ template class VolumeSetAction : public Action, public Pa void play(Ts... x) override { this->parent_->set_volume(this->volume_.value(x...)); } }; +template class MuteOnAction : public Action { + public: + explicit MuteOnAction(Speaker *speaker) : speaker_(speaker) {} + + void play(Ts... x) override { this->speaker_->set_mute_state(true); } + + protected: + Speaker *speaker_; +}; + +template class MuteOffAction : public Action { + public: + explicit MuteOffAction(Speaker *speaker) : speaker_(speaker) {} + + void play(Ts... x) override { this->speaker_->set_mute_state(false); } + + protected: + Speaker *speaker_; +}; + template class StopAction : public Action, public Parented { public: void play(Ts... x) override { this->parent_->stop(); } diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 9390e4edb7..96843e2d5a 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -8,7 +8,12 @@ #include #endif +#include "esphome/core/defines.h" + #include "esphome/components/audio/audio.h" +#ifdef USE_AUDIO_DAC +#include "esphome/components/audio_dac/audio_dac.h" +#endif namespace esphome { namespace speaker { @@ -56,9 +61,35 @@ class Speaker { bool is_running() const { return this->state_ == STATE_RUNNING; } bool is_stopped() const { return this->state_ == STATE_STOPPED; } - // Volume control must be implemented by each speaker component, otherwise it will have no effect. - virtual void set_volume(float volume) { this->volume_ = volume; }; - virtual float get_volume() { return this->volume_; } + // Volume control is handled by a configured audio dac component. Individual speaker components can + // override and implement in software if an audio dac isn't available. + virtual void set_volume(float volume) { + this->volume_ = volume; +#ifdef USE_AUDIO_DAC + if (this->audio_dac_ != nullptr) { + this->audio_dac_->set_volume(volume); + } +#endif + }; + float get_volume() { return this->volume_; } + + virtual void set_mute_state(bool mute_state) { + this->mute_state_ = mute_state; +#ifdef USE_AUDIO_DAC + if (this->audio_dac_) { + if (mute_state) { + this->audio_dac_->set_mute_on(); + } else { + this->audio_dac_->set_mute_off(); + } + } +#endif + } + bool get_mute_state() { return this->mute_state_; } + +#ifdef USE_AUDIO_DAC + void set_audio_dac(audio_dac::AudioDac *audio_dac) { this->audio_dac_ = audio_dac; } +#endif void set_audio_stream_info(const audio::AudioStreamInfo &audio_stream_info) { this->audio_stream_info_ = audio_stream_info; @@ -68,6 +99,11 @@ class Speaker { State state_{STATE_STOPPED}; audio::AudioStreamInfo audio_stream_info_; float volume_{1.0f}; + bool mute_state_{false}; + +#ifdef USE_AUDIO_DAC + audio_dac::AudioDac *audio_dac_{nullptr}; +#endif }; } // namespace speaker diff --git a/tests/components/speaker/test.esp32-ard.yaml b/tests/components/speaker/test.esp32-ard.yaml index 9a24d00f68..396b4d95ea 100644 --- a/tests/components/speaker/test.esp32-ard.yaml +++ b/tests/components/speaker/test.esp32-ard.yaml @@ -1,6 +1,8 @@ esphome: on_boot: then: + - speaker.mute_on: + - speaker.mute_off: - if: condition: speaker.is_stopped then: diff --git a/tests/components/speaker/test.esp32-c3-ard.yaml b/tests/components/speaker/test.esp32-c3-ard.yaml index f28014337c..636aeba766 100644 --- a/tests/components/speaker/test.esp32-c3-ard.yaml +++ b/tests/components/speaker/test.esp32-c3-ard.yaml @@ -1,6 +1,8 @@ esphome: on_boot: then: + - speaker.mute_on: + - speaker.mute_off: - if: condition: speaker.is_stopped then: diff --git a/tests/components/speaker/test.esp32-c3-idf.yaml b/tests/components/speaker/test.esp32-c3-idf.yaml index f28014337c..636aeba766 100644 --- a/tests/components/speaker/test.esp32-c3-idf.yaml +++ b/tests/components/speaker/test.esp32-c3-idf.yaml @@ -1,6 +1,8 @@ esphome: on_boot: then: + - speaker.mute_on: + - speaker.mute_off: - if: condition: speaker.is_stopped then: diff --git a/tests/components/speaker/test.esp32-idf.yaml b/tests/components/speaker/test.esp32-idf.yaml index 9a24d00f68..b69440b133 100644 --- a/tests/components/speaker/test.esp32-idf.yaml +++ b/tests/components/speaker/test.esp32-idf.yaml @@ -1,6 +1,8 @@ esphome: on_boot: then: + - speaker.mute_on: + - speaker.mute_off: - if: condition: speaker.is_stopped then: @@ -17,8 +19,17 @@ i2s_audio: i2s_bclk_pin: 17 i2s_mclk_pin: 15 +i2c: + scl: 12 + sda: 10 + +audio_dac: + - platform: aic3204 + id: internal_dac + speaker: - platform: i2s_audio - id: speaker_id + id: speaker_with_audio_dac_id + audio_dac: internal_dac dac_type: external - i2s_dout_pin: 13 + i2s_dout_pin: 14 From 9acc21e81a393a409236f29aeaf195cedb1ea472 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Wed, 23 Oct 2024 23:04:59 +0200 Subject: [PATCH 0535/1052] unified way how all platforms handle copy_files (#7614) Co-authored-by: Tomasz Duda --- esphome/components/rp2040/__init__.py | 9 ++++++--- esphome/writer.py | 25 +++++++------------------ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index f59962477f..d612631a4c 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -17,7 +17,7 @@ from esphome.const import ( PLATFORM_RP2040, ) from esphome.core import CORE, EsphomeError, coroutine_with_priority -from esphome.helpers import copy_file_if_changed, mkdir_p, write_file +from esphome.helpers import copy_file_if_changed, mkdir_p, write_file, read_file from .const import KEY_BOARD, KEY_PIO_FILES, KEY_RP2040, rp2040_ns @@ -230,11 +230,14 @@ def generate_pio_files() -> bool: # Called by writer.py -def copy_files() -> bool: +def copy_files(): dir = os.path.dirname(__file__) post_build_file = os.path.join(dir, "post_build.py.script") copy_file_if_changed( post_build_file, CORE.relative_build_path("post_build.py"), ) - return generate_pio_files() + if generate_pio_files(): + path = CORE.relative_src_path("esphome.h") + content = read_file(path).rstrip("\n") + write_file(path, content + '\n#include "pio_includes.h"\n') diff --git a/esphome/writer.py b/esphome/writer.py index 79ee72996c..90446ae4b1 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -1,3 +1,4 @@ +import importlib import logging import os from pathlib import Path @@ -299,25 +300,13 @@ def copy_src_tree(): CORE.relative_src_path("esphome", "core", "version.h"), generate_version_h() ) - if CORE.is_esp32: - from esphome.components.esp32 import copy_files - + platform = "esphome.components." + CORE.target_platform + try: + module = importlib.import_module(platform) + copy_files = getattr(module, "copy_files") copy_files() - - elif CORE.is_esp8266: - from esphome.components.esp8266 import copy_files - - copy_files() - - elif CORE.is_rp2040: - from esphome.components.rp2040 import copy_files - - (pio) = copy_files() - if pio: - write_file_if_changed( - CORE.relative_src_path("esphome.h"), - ESPHOME_H_FORMAT.format(include_s + '\n#include "pio_includes.h"'), - ) + except AttributeError: + pass def generate_defines_h(): From 5b5c2fe71b8307aa692d3def1268837a1b26de51 Mon Sep 17 00:00:00 2001 From: Aaron Solochek Date: Wed, 23 Oct 2024 17:25:53 -0700 Subject: [PATCH 0536/1052] updating ESP32 board definitions (#7650) --- esphome/components/esp32/boards.py | 200 ++++++++++++++++++++++++++++- 1 file changed, 199 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index 60abcd447c..02744ecb6f 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -103,6 +103,173 @@ ESP32_BOARD_PINS = { "LED": 13, "LED_BUILTIN": 13, }, + "adafruit_feather_esp32s3": { + "BUTTON": 0, + "A0": 18, + "A1": 17, + "A2": 16, + "A3": 15, + "A4": 14, + "A5": 8, + "SCK": 36, + "MOSI": 35, + "MISO": 37, + "RX": 38, + "TX": 39, + "SCL": 4, + "SDA": 3, + "NEOPIXEL": 33, + "PIN_NEOPIXEL": 33, + "NEOPIXEL_POWER": 21, + "I2C_POWER": 7, + "LED": 13, + "LED_BUILTIN": 13, + }, + "adafruit_feather_esp32s3_nopsram": { + "BUTTON": 0, + "A0": 18, + "A1": 17, + "A2": 16, + "A3": 15, + "A4": 14, + "A5": 8, + "SCK": 36, + "MOSI": 35, + "MISO": 37, + "RX": 38, + "TX": 39, + "SCL": 4, + "SDA": 3, + "NEOPIXEL": 33, + "PIN_NEOPIXEL": 33, + "NEOPIXEL_POWER": 21, + "I2C_POWER": 7, + "LED": 13, + "LED_BUILTIN": 13, + }, + "adafruit_feather_esp32s3_tft": { + "BUTTON": 0, + "A0": 18, + "A1": 17, + "A2": 16, + "A3": 15, + "A4": 14, + "A5": 8, + "SCK": 36, + "MOSI": 35, + "MISO": 37, + "RX": 2, + "TX": 1, + "SCL": 41, + "SDA": 42, + "NEOPIXEL": 33, + "PIN_NEOPIXEL": 33, + "NEOPIXEL_POWER": 34, + "TFT_I2C_POWER": 21, + "TFT_CS": 7, + "TFT_DC": 39, + "TFT_RESET": 40, + "TFT_BACKLIGHT": 45, + "LED": 13, + "LED_BUILTIN": 13, + }, + "adafruit_funhouse_esp32s2": { + "BUTTON_UP": 5, + "BUTTON_DOWN": 3, + "BUTTON_SELECT": 4, + "DOTSTAR_DATA": 14, + "DOTSTAR_CLOCK": 15, + "PIR_SENSE": 16, + "A0": 17, + "A1": 2, + "A2": 1, + "CAP6": 6, + "CAP7": 7, + "CAP8": 8, + "CAP9": 9, + "CAP10": 10, + "CAP11": 11, + "CAP12": 12, + "CAP13": 13, + "SPEAKER": 42, + "LED": 37, + "LIGHT": 18, + "TFT_MOSI": 35, + "TFT_SCK": 36, + "TFT_CS": 40, + "TFT_DC": 39, + "TFT_RESET": 41, + "TFT_BACKLIGHT": 21, + "RED_LED": 31, + "BUTTON": 0, + }, + "adafruit_itsybitsy_esp32": { + "A0": 25, + "A1": 26, + "A2": 4, + "A3": 38, + "A4": 37, + "A5": 36, + "SCK": 19, + "MOSI": 21, + "MISO": 22, + "SCL": 27, + "SDA": 15, + "TX": 20, + "RX": 8, + "NEOPIXEL": 0, + "PIN_NEOPIXEL": 0, + "NEOPIXEL_POWER": 2, + "BUTTON": 35, + }, + "adafruit_magtag29_esp32s2": { + "A1": 18, + "BUTTON_A": 15, + "BUTTON_B": 14, + "BUTTON_C": 12, + "BUTTON_D": 11, + "SDA": 33, + "SCL": 34, + "SPEAKER": 17, + "SPEAKER_ENABLE": 16, + "VOLTAGE_MONITOR": 4, + "ACCELEROMETER_INT": 9, + "ACCELEROMETER_INTERRUPT": 9, + "LIGHT": 3, + "NEOPIXEL": 1, + "PIN_NEOPIXEL": 1, + "NEOPIXEL_POWER": 21, + "EPD_BUSY": 5, + "EPD_RESET": 6, + "EPD_DC": 7, + "EPD_CS": 8, + "EPD_MOSI": 35, + "EPD_SCK": 36, + "EPD_MISO": 37, + "BUTTON": 0, + "LED": 13, + "LED_BUILTIN": 13, + }, + "adafruit_metro_esp32s2": { + "A0": 17, + "A1": 18, + "A2": 1, + "A3": 2, + "A4": 3, + "A5": 4, + "RX": 38, + "TX": 37, + "SCL": 34, + "SDA": 33, + "MISO": 37, + "SCK": 36, + "MOSI": 35, + "NEOPIXEL": 45, + "PIN_NEOPIXEL": 45, + "LED": 42, + "LED_BUILTIN": 42, + "BUTTON": 0, + }, "adafruit_qtpy_esp32c3": { "A0": 4, "A1": 3, @@ -141,6 +308,26 @@ ESP32_BOARD_PINS = { "BUTTON": 0, "SWITCH": 0, }, + "adafruit_qtpy_esp32s3_nopsram": { + "A0": 18, + "A1": 17, + "A2": 9, + "A3": 8, + "SDA": 7, + "SCL": 6, + "MOSI": 35, + "MISO": 37, + "SCK": 36, + "RX": 16, + "TX": 5, + "SDA1": 41, + "SCL1": 40, + "NEOPIXEL": 39, + "PIN_NEOPIXEL": 39, + "NEOPIXEL_POWER": 38, + "BUTTON": 0, + "SWITCH": 0, + }, "adafruit_qtpy_esp32": { "A0": 26, "A1": 25, @@ -1068,7 +1255,18 @@ ESP32_BOARD_PINS = { "_VBAT": 35, }, "wemosbat": {"LED": 16}, - "wesp32": {"MISO": 32, "SCL": 4, "SDA": 15}, + "wesp32": { + "MISO": 32, + "MOSI": 23, + "SCK": 18, + "SCL": 4, + "SDA": 15, + "MISO1": 12, + "MOSI1": 13, + "SCK1": 14, + "SCL1": 5, + "SDA1": 33, + }, "widora-air": { "A1": 39, "A2": 35, From ca5c73d170c57a256688f51c09b38dac70b1fe6e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 25 Oct 2024 07:55:14 +1300 Subject: [PATCH 0537/1052] Support ignoring discovered devices from the dashboard (#7665) --- esphome/dashboard/core.py | 25 ++++++++++++++++++++ esphome/dashboard/web_server.py | 42 +++++++++++++++++++++++++++++++++ esphome/storage_json.py | 4 ++++ 3 files changed, 71 insertions(+) diff --git a/esphome/dashboard/core.py b/esphome/dashboard/core.py index eec0777da6..563ca1506d 100644 --- a/esphome/dashboard/core.py +++ b/esphome/dashboard/core.py @@ -5,10 +5,14 @@ from collections.abc import Coroutine import contextlib from dataclasses import dataclass from functools import partial +import json import logging +from pathlib import Path import threading from typing import TYPE_CHECKING, Any, Callable +from esphome.storage_json import ignored_devices_storage_path + from ..zeroconf import DiscoveredImport from .dns import DNSCache from .entries import DashboardEntries @@ -20,6 +24,8 @@ if TYPE_CHECKING: _LOGGER = logging.getLogger(__name__) +IGNORED_DEVICES_STORAGE_PATH = "ignored-devices.json" + @dataclass class Event: @@ -74,6 +80,7 @@ class ESPHomeDashboard: "settings", "dns_cache", "_background_tasks", + "ignored_devices", ) def __init__(self) -> None: @@ -89,12 +96,30 @@ class ESPHomeDashboard: self.settings = DashboardSettings() self.dns_cache = DNSCache() self._background_tasks: set[asyncio.Task] = set() + self.ignored_devices: set[str] = set() async def async_setup(self) -> None: """Setup the dashboard.""" self.loop = asyncio.get_running_loop() self.ping_request = asyncio.Event() self.entries = DashboardEntries(self) + self.load_ignored_devices() + + def load_ignored_devices(self) -> None: + storage_path = Path(ignored_devices_storage_path()) + try: + with storage_path.open("r", encoding="utf-8") as f_handle: + data = json.load(f_handle) + self.ignored_devices = set(data.get("ignored_devices", set())) + except FileNotFoundError: + pass + + def save_ignored_devices(self) -> None: + storage_path = Path(ignored_devices_storage_path()) + with storage_path.open("w", encoding="utf-8") as f_handle: + json.dump( + {"ignored_devices": sorted(self.ignored_devices)}, indent=2, fp=f_handle + ) async def async_run(self) -> None: """Run the dashboard.""" diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index e4b7b8d342..1a8b2ab83a 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -541,6 +541,46 @@ class ImportRequestHandler(BaseHandler): self.finish() +class IgnoreDeviceRequestHandler(BaseHandler): + @authenticated + def post(self) -> None: + dashboard = DASHBOARD + try: + args = json.loads(self.request.body.decode()) + device_name = args["name"] + ignore = args["ignore"] + except (json.JSONDecodeError, KeyError): + self.set_status(400) + self.set_header("content-type", "application/json") + self.write(json.dumps({"error": "Invalid payload"})) + return + + ignored_device = next( + ( + res + for res in dashboard.import_result.values() + if res.device_name == device_name + ), + None, + ) + + if ignored_device is None: + self.set_status(404) + self.set_header("content-type", "application/json") + self.write(json.dumps({"error": "Device not found"})) + return + + if ignore: + dashboard.ignored_devices.add(ignored_device.device_name) + else: + dashboard.ignored_devices.discard(ignored_device.device_name) + + dashboard.save_ignored_devices() + + self.set_status(204) + self.finish() + + class DownloadListRequestHandler(BaseHandler): @authenticated @bind_config @@ -688,6 +728,7 @@ class ListDevicesHandler(BaseHandler): "project_name": res.project_name, "project_version": res.project_version, "network": res.network, + "ignored": res.device_name in dashboard.ignored_devices, } for res in dashboard.import_result.values() if res.device_name not in configured @@ -1156,6 +1197,7 @@ def make_app(debug=get_bool_env(ENV_DEV)) -> tornado.web.Application: (f"{rel}prometheus-sd", PrometheusServiceDiscoveryHandler), (f"{rel}boards/([a-z0-9]+)", BoardsRequestHandler), (f"{rel}version", EsphomeVersionHandler), + (f"{rel}ignore-device", IgnoreDeviceRequestHandler), ], **app_settings, ) diff --git a/esphome/storage_json.py b/esphome/storage_json.py index 2d12ee01a0..97cf9ceadd 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -28,6 +28,10 @@ def esphome_storage_path() -> str: return os.path.join(CORE.data_dir, "esphome.json") +def ignored_devices_storage_path() -> str: + return os.path.join(CORE.data_dir, "ignored-devices.json") + + def trash_storage_path() -> str: return CORE.relative_config_path("trash") From 4fa3c6915ca7aaba27d001415aee5b613db33565 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 25 Oct 2024 08:10:30 +1300 Subject: [PATCH 0538/1052] Bump esphome-dashboard to 20241025.0 (#7669) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c03a9f181a..8cc26e4da0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20240620.0 +esphome-dashboard==20241025.0 aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 From c20e1975d1acd9c99fefd1a1da7e446a2e403bc7 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 24 Oct 2024 23:25:19 +0200 Subject: [PATCH 0539/1052] unified way how all platforms handle get_download_types (#7617) Co-authored-by: Tomasz Duda --- esphome/dashboard/web_server.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 1a8b2ab83a..9aeece9aab 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -7,6 +7,7 @@ import datetime import functools import gzip import hashlib +import importlib import json import logging import os @@ -595,26 +596,18 @@ class DownloadListRequestHandler(BaseHandler): downloads = [] platform: str = storage_json.target_platform.lower() - if platform == const.PLATFORM_RP2040: - from esphome.components.rp2040 import get_download_types as rp2040_types - downloads = rp2040_types(storage_json) - elif platform == const.PLATFORM_ESP8266: - from esphome.components.esp8266 import get_download_types as esp8266_types - - downloads = esp8266_types(storage_json) - elif platform.upper() in ESP32_VARIANTS: - from esphome.components.esp32 import get_download_types as esp32_types - - downloads = esp32_types(storage_json) + if platform.upper() in ESP32_VARIANTS: + platform = "esp32" elif platform in (const.PLATFORM_RTL87XX, const.PLATFORM_BK72XX): - from esphome.components.libretiny import ( - get_download_types as libretiny_types, - ) + platform = "libretiny" - downloads = libretiny_types(storage_json) - else: - raise ValueError(f"Unknown platform {platform}") + try: + module = importlib.import_module(f"esphome.components.{platform}") + get_download_types = getattr(module, "get_download_types") + except AttributeError as exc: + raise ValueError(f"Unknown platform {platform}") from exc + downloads = get_download_types(storage_json) self.set_status(200) self.set_header("content-type", "application/json") From 4101d5dad15a5f3162ada989a536bf2cb6da0307 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Thu, 24 Oct 2024 17:26:39 -0400 Subject: [PATCH 0540/1052] [media_player] Add new media player conditions (#7667) --- esphome/components/media_player/__init__.py | 11 +++++++++++ esphome/components/media_player/automation.h | 10 ++++++++++ esphome/components/media_player/media_player.cpp | 4 ++++ tests/components/media_player/common.yaml | 4 ++++ 4 files changed, 29 insertions(+) diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index 423cb065dc..a46b30db29 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -21,6 +21,7 @@ media_player_ns = cg.esphome_ns.namespace("media_player") MediaPlayer = media_player_ns.class_("MediaPlayer") + PlayAction = media_player_ns.class_( "PlayAction", automation.Action, cg.Parented.template(MediaPlayer) ) @@ -60,7 +61,11 @@ AnnoucementTrigger = media_player_ns.class_( "AnnouncementTrigger", automation.Trigger.template() ) IsIdleCondition = media_player_ns.class_("IsIdleCondition", automation.Condition) +IsPausedCondition = media_player_ns.class_("IsPausedCondition", automation.Condition) IsPlayingCondition = media_player_ns.class_("IsPlayingCondition", automation.Condition) +IsAnnouncingCondition = media_player_ns.class_( + "IsAnnouncingCondition", automation.Condition +) async def setup_media_player_core_(var, config): @@ -159,9 +164,15 @@ async def media_player_play_media_action(config, action_id, template_arg, args): @automation.register_condition( "media_player.is_idle", IsIdleCondition, MEDIA_PLAYER_ACTION_SCHEMA ) +@automation.register_condition( + "media_player.is_paused", IsPausedCondition, MEDIA_PLAYER_ACTION_SCHEMA +) @automation.register_condition( "media_player.is_playing", IsPlayingCondition, MEDIA_PLAYER_ACTION_SCHEMA ) +@automation.register_condition( + "media_player.is_announcing", IsAnnouncingCondition, MEDIA_PLAYER_ACTION_SCHEMA +) async def media_player_action(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) diff --git a/esphome/components/media_player/automation.h b/esphome/components/media_player/automation.h index f0e0a5dd31..7b9220c4a5 100644 --- a/esphome/components/media_player/automation.h +++ b/esphome/components/media_player/automation.h @@ -68,5 +68,15 @@ template class IsPlayingCondition : public Condition, pub bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PLAYING; } }; +template class IsPausedCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_PAUSED; } +}; + +template class IsAnnouncingCondition : public Condition, public Parented { + public: + bool check(Ts... x) override { return this->parent_->state == MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING; } +}; + } // namespace media_player } // namespace esphome diff --git a/esphome/components/media_player/media_player.cpp b/esphome/components/media_player/media_player.cpp index 586345ac9f..b5190d8573 100644 --- a/esphome/components/media_player/media_player.cpp +++ b/esphome/components/media_player/media_player.cpp @@ -37,6 +37,10 @@ const char *media_player_command_to_string(MediaPlayerCommand command) { return "UNMUTE"; case MEDIA_PLAYER_COMMAND_TOGGLE: return "TOGGLE"; + case MEDIA_PLAYER_COMMAND_VOLUME_UP: + return "VOLUME_UP"; + case MEDIA_PLAYER_COMMAND_VOLUME_DOWN: + return "VOLUME_DOWN"; default: return "UNKNOWN"; } diff --git a/tests/components/media_player/common.yaml b/tests/components/media_player/common.yaml index 24b85cd474..af0d5c7765 100644 --- a/tests/components/media_player/common.yaml +++ b/tests/components/media_player/common.yaml @@ -27,6 +27,10 @@ media_player: media_player.is_idle: - wait_until: media_player.is_playing: + - wait_until: + media_player.is_announcing: + - wait_until: + media_player.is_paused: - media_player.volume_up: - media_player.volume_down: - media_player.volume_set: 50% From 7dbda12008830ba576c2e6cddca87fab112999c6 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 24 Oct 2024 23:55:58 +0200 Subject: [PATCH 0541/1052] [code-quality] weikai.h (#7601) --- esphome/components/weikai/weikai.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/weikai/weikai.h b/esphome/components/weikai/weikai.h index 042c729162..175a067b27 100644 --- a/esphome/components/weikai/weikai.h +++ b/esphome/components/weikai/weikai.h @@ -209,7 +209,7 @@ class WeikaiComponent : public Component { /// @brief store the name for the component /// @param name the name as defined by the python code generator - void set_name(std::string name) { this->name_ = std::move(name); } + void set_name(std::string &&name) { this->name_ = std::move(name); } /// @brief Get the name of the component /// @return the name @@ -308,7 +308,7 @@ class WeikaiChannel : public uart::UARTComponent { /// @brief The name as generated by the Python code generator /// @param name of the channel - void set_channel_name(std::string name) { this->name_ = std::move(name); } + void set_channel_name(std::string &&name) { this->name_ = std::move(name); } /// @brief Get the channel name /// @return the name From 34a8eaddb227738ed1d22d43025a9af56ad5d4a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:56:48 +1300 Subject: [PATCH 0542/1052] Bump actions/setup-python from 5.2.0 to 5.3.0 in /.github/actions/restore-python (#7671) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index c6978f68c5..06c54578f5 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -17,7 +17,7 @@ runs: steps: - name: Set up Python ${{ inputs.python-version }} id: python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment From 09f9d915773c9ad2d15d60a60f6886f9da553da5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:57:09 +1300 Subject: [PATCH 0543/1052] Bump actions/setup-python from 5.2.0 to 5.3.0 (#7670) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .github/workflows/ci-api-proto.yml | 2 +- .github/workflows/ci-docker.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/sync-device-classes.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-api-proto.yml b/.github/workflows/ci-api-proto.yml index 8112c4e0ff..a6b2e2b2b3 100644 --- a/.github/workflows/ci-api-proto.yml +++ b/.github/workflows/ci-api-proto.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: "3.11" diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index f003e5d24c..435a58498e 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: "3.9" - name: Set up Docker Buildx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d2f1c877d..82a55d0e2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 26a213f170..5072bec222 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: "3.x" - name: Set up python environment @@ -85,7 +85,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: "3.9" diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index c066ae9fb4..7a46d596a1 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -22,7 +22,7 @@ jobs: path: lib/home-assistant - name: Setup Python - uses: actions/setup-python@v5.2.0 + uses: actions/setup-python@v5.3.0 with: python-version: 3.12 From 33fdbbe30c4d9643300483bf2dfd85d992e2cf86 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:05:25 +1100 Subject: [PATCH 0544/1052] [image][online_image][animation] Fix transparency in RGB565 (#7631) --- esphome/components/animation/__init__.py | 13 +++--- esphome/components/animation/animation.cpp | 2 +- esphome/components/image/__init__.py | 13 +++--- esphome/components/image/image.cpp | 28 ++++++------- esphome/components/image/image.h | 37 ++++++++--------- .../components/online_image/online_image.cpp | 10 +---- .../components/online_image/online_image.h | 8 +--- tests/components/image/common.yaml | 38 ++++++++++++++++++ tests/components/image/test.esp32-ard.yaml | 40 +------------------ tests/components/image/test.esp32-c3-ard.yaml | 39 +----------------- tests/components/image/test.esp32-c3-idf.yaml | 39 +----------------- tests/components/image/test.esp32-idf.yaml | 39 +----------------- tests/components/image/test.esp8266-ard.yaml | 39 +----------------- tests/components/image/test.host.yaml | 8 ++++ tests/components/image/test.rp2040-ard.yaml | 39 +----------------- 15 files changed, 101 insertions(+), 291 deletions(-) create mode 100644 tests/components/image/common.yaml create mode 100644 tests/components/image/test.host.yaml diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index eb3d09ac96..5a308855de 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -271,7 +271,8 @@ async def to_code(config): pos += 1 elif config[CONF_TYPE] in ["RGB565", "TRANSPARENT_IMAGE"]: - data = [0 for _ in range(height * width * 2 * frames)] + bytes_per_pixel = 3 if transparent else 2 + data = [0 for _ in range(height * width * bytes_per_pixel * frames)] pos = 0 for frameIndex in range(frames): image.seek(frameIndex) @@ -288,17 +289,13 @@ async def to_code(config): G = g >> 2 B = b >> 3 rgb = (R << 11) | (G << 5) | B - - if transparent: - if rgb == 0x0020: - rgb = 0 - if a < 0x80: - rgb = 0x0020 - data[pos] = rgb >> 8 pos += 1 data[pos] = rgb & 0xFF pos += 1 + if transparent: + data[pos] = a + pos += 1 elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]: width8 = ((width + 7) // 8) * 8 diff --git a/esphome/components/animation/animation.cpp b/esphome/components/animation/animation.cpp index 7e0efa97e0..1375dfe07e 100644 --- a/esphome/components/animation/animation.cpp +++ b/esphome/components/animation/animation.cpp @@ -62,7 +62,7 @@ void Animation::set_frame(int frame) { } void Animation::update_data_start_() { - const uint32_t image_size = image_type_to_width_stride(this->width_, this->type_) * this->height_; + const uint32_t image_size = this->get_width_stride() * this->height_; this->data_start_ = this->animation_data_start_ + image_size * this->current_frame_; } diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index c72417bcda..8742540067 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -361,24 +361,21 @@ async def to_code(config): elif config[CONF_TYPE] in ["RGB565"]: image = image.convert("RGBA") pixels = list(image.getdata()) - data = [0 for _ in range(height * width * 2)] + bytes_per_pixel = 3 if transparent else 2 + data = [0 for _ in range(height * width * bytes_per_pixel)] pos = 0 for r, g, b, a in pixels: R = r >> 3 G = g >> 2 B = b >> 3 rgb = (R << 11) | (G << 5) | B - - if transparent: - if rgb == 0x0020: - rgb = 0 - if a < 0x80: - rgb = 0x0020 - data[pos] = rgb >> 8 pos += 1 data[pos] = rgb & 0xFF pos += 1 + if transparent: + data[pos] = a + pos += 1 elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]: if transparent: diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index ded4c60d25..ca2f659fb0 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -88,7 +88,7 @@ lv_img_dsc_t *Image::get_lv_img_dsc() { this->dsc_.header.reserved = 0; this->dsc_.header.w = this->width_; this->dsc_.header.h = this->height_; - this->dsc_.data_size = image_type_to_width_stride(this->dsc_.header.w * this->dsc_.header.h, this->get_type()); + this->dsc_.data_size = this->get_width_stride() * this->get_height(); switch (this->get_type()) { case IMAGE_TYPE_BINARY: this->dsc_.header.cf = LV_IMG_CF_ALPHA_1BIT; @@ -104,17 +104,17 @@ lv_img_dsc_t *Image::get_lv_img_dsc() { case IMAGE_TYPE_RGB565: #if LV_COLOR_DEPTH == 16 - this->dsc_.header.cf = this->has_transparency() ? LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED : LV_IMG_CF_TRUE_COLOR; + this->dsc_.header.cf = this->has_transparency() ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; #else this->dsc_.header.cf = LV_IMG_CF_RGB565; #endif break; - case image::IMAGE_TYPE_RGBA: + case IMAGE_TYPE_RGBA: #if LV_COLOR_DEPTH == 32 this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR; #else - this->dsc_.header.cf = LV_IMG_CF_RGBA8888; + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; #endif break; } @@ -147,21 +147,21 @@ Color Image::get_rgb24_pixel_(int x, int y) const { return color; } Color Image::get_rgb565_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_) * 2; - uint16_t rgb565 = - progmem_read_byte(this->data_start_ + pos + 0) << 8 | progmem_read_byte(this->data_start_ + pos + 1); + const uint8_t *pos = this->data_start_; + if (this->transparent_) { + pos += (x + y * this->width_) * 3; + } else { + pos += (x + y * this->width_) * 2; + } + uint16_t rgb565 = encode_uint16(progmem_read_byte(pos), progmem_read_byte(pos + 1)); auto r = (rgb565 & 0xF800) >> 11; auto g = (rgb565 & 0x07E0) >> 5; auto b = rgb565 & 0x001F; - Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2)); - if (rgb565 == 0x0020 && transparent_) { - // darkest green has been defined as transparent color for transparent RGB565 images. - color.w = 0; - } else { - color.w = 0xFF; - } + auto a = this->transparent_ ? progmem_read_byte(pos + 2) : 0xFF; + Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2), a); return color; } + Color Image::get_grayscale_pixel_(int x, int y) const { const uint32_t pos = (x + y * this->width_); const uint8_t gray = progmem_read_byte(this->data_start_ + pos); diff --git a/esphome/components/image/image.h b/esphome/components/image/image.h index a8a8aab2c2..40370d18da 100644 --- a/esphome/components/image/image.h +++ b/esphome/components/image/image.h @@ -17,24 +17,6 @@ enum ImageType { IMAGE_TYPE_RGBA = 4, }; -inline int image_type_to_bpp(ImageType type) { - switch (type) { - case IMAGE_TYPE_BINARY: - return 1; - case IMAGE_TYPE_GRAYSCALE: - return 8; - case IMAGE_TYPE_RGB565: - return 16; - case IMAGE_TYPE_RGB24: - return 24; - case IMAGE_TYPE_RGBA: - return 32; - } - return 0; -} - -inline int image_type_to_width_stride(int width, ImageType type) { return (width * image_type_to_bpp(type) + 7u) / 8u; } - class Image : public display::BaseImage { public: Image(const uint8_t *data_start, int width, int height, ImageType type); @@ -44,6 +26,25 @@ class Image : public display::BaseImage { const uint8_t *get_data_start() const { return this->data_start_; } ImageType get_type() const; + int get_bpp() const { + switch (this->type_) { + case IMAGE_TYPE_BINARY: + return 1; + case IMAGE_TYPE_GRAYSCALE: + return 8; + case IMAGE_TYPE_RGB565: + return this->transparent_ ? 24 : 16; + case IMAGE_TYPE_RGB24: + return 24; + case IMAGE_TYPE_RGBA: + return 32; + } + return 0; + } + + /// Return the stride of the image in bytes, that is, the distance in bytes + /// between two consecutive rows of pixels. + uint32_t get_width_stride() const { return (this->width_ * this->get_bpp() + 7u) / 8u; } void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; void set_transparency(bool transparent) { transparent_ = transparent; } diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 480bad6aca..1786809dfa 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -215,16 +215,10 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) { } case ImageType::IMAGE_TYPE_RGB565: { uint16_t col565 = display::ColorUtil::color_to_565(color); - if (this->has_transparency()) { - if (col565 == 0x0020) { - col565 = 0; - } - if (color.w < 0x80) { - col565 = 0x0020; - } - } this->buffer_[pos + 0] = static_cast((col565 >> 8) & 0xFF); this->buffer_[pos + 1] = static_cast(col565 & 0xFF); + if (this->has_transparency()) + this->buffer_[pos + 2] = color.w; break; } case ImageType::IMAGE_TYPE_RGBA: { diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 51c11478cd..017402a088 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -86,13 +86,9 @@ class OnlineImage : public PollingComponent, Allocator allocator_{Allocator::Flags::ALLOW_FAILURE}; uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); } - int get_buffer_size_(int width, int height) const { - return std::ceil(image::image_type_to_bpp(this->type_) * width * height / 8.0); - } + int get_buffer_size_(int width, int height) const { return (this->get_bpp() * width + 7u) / 8u * height; } - int get_position_(int x, int y) const { - return ((x + y * this->buffer_width_) * image::image_type_to_bpp(this->type_)) / 8; - } + int get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; } ESPHOME_ALWAYS_INLINE bool auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } diff --git a/tests/components/image/common.yaml b/tests/components/image/common.yaml new file mode 100644 index 0000000000..313da6bc0b --- /dev/null +++ b/tests/components/image/common.yaml @@ -0,0 +1,38 @@ +image: + - id: binary_image + file: ../../pnglogo.png + type: BINARY + dither: FloydSteinberg + - id: transparent_transparent_image + file: ../../pnglogo.png + type: TRANSPARENT_BINARY + - id: rgba_image + file: ../../pnglogo.png + type: RGBA + resize: 50x50 + - id: rgb24_image + file: ../../pnglogo.png + type: RGB24 + use_transparency: yes + - id: rgb565_image + file: ../../pnglogo.png + type: RGB565 + use_transparency: no + - id: web_svg_image + file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg + resize: 256x48 + type: TRANSPARENT_BINARY + - id: web_tiff_image + file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff + type: RGB24 + resize: 48x48 + - id: web_redirect_image + file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 + type: RGB24 + resize: 48x48 + - id: mdi_alert + file: mdi:alert-circle-outline + resize: 50x50 + - id: another_alert_icon + file: mdi:alert-outline + type: BINARY diff --git a/tests/components/image/test.esp32-ard.yaml b/tests/components/image/test.esp32-ard.yaml index 9dd44d177f..818e720221 100644 --- a/tests/components/image/test.esp32-ard.yaml +++ b/tests/components/image/test.esp32-ard.yaml @@ -13,41 +13,5 @@ display: reset_pin: 21 invert_colors: true -image: - - id: binary_image - file: ../../pnglogo.png - type: BINARY - dither: FloydSteinberg - - id: transparent_transparent_image - file: ../../pnglogo.png - type: TRANSPARENT_BINARY - - id: rgba_image - file: ../../pnglogo.png - type: RGBA - resize: 50x50 - - id: rgb24_image - file: ../../pnglogo.png - type: RGB24 - use_transparency: yes - - id: rgb565_image - file: ../../pnglogo.png - type: RGB565 - use_transparency: no - - id: web_svg_image - file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg - resize: 256x48 - type: TRANSPARENT_BINARY - - id: web_tiff_image - file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff - type: RGB24 - resize: 48x48 - - id: web_redirect_image - file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 - type: RGB24 - resize: 48x48 - - id: mdi_alert - file: mdi:alert-circle-outline - resize: 50x50 - - id: another_alert_icon - file: mdi:alert-outline - type: BINARY +<<: !include common.yaml + diff --git a/tests/components/image/test.esp32-c3-ard.yaml b/tests/components/image/test.esp32-c3-ard.yaml index c0b2779773..4dae9cd5ec 100644 --- a/tests/components/image/test.esp32-c3-ard.yaml +++ b/tests/components/image/test.esp32-c3-ard.yaml @@ -13,41 +13,4 @@ display: reset_pin: 10 invert_colors: true -image: - - id: binary_image - file: ../../pnglogo.png - type: BINARY - dither: FloydSteinberg - - id: transparent_transparent_image - file: ../../pnglogo.png - type: TRANSPARENT_BINARY - - id: rgba_image - file: ../../pnglogo.png - type: RGBA - resize: 50x50 - - id: rgb24_image - file: ../../pnglogo.png - type: RGB24 - use_transparency: yes - - id: rgb565_image - file: ../../pnglogo.png - type: RGB565 - use_transparency: no - - id: web_svg_image - file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg - resize: 256x48 - type: TRANSPARENT_BINARY - - id: web_tiff_image - file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff - type: RGB24 - resize: 48x48 - - id: web_redirect_image - file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 - type: RGB24 - resize: 48x48 - - id: mdi_alert - file: mdi:alert-circle-outline - resize: 50x50 - - id: another_alert_icon - file: mdi:alert-outline - type: BINARY +<<: !include common.yaml diff --git a/tests/components/image/test.esp32-c3-idf.yaml b/tests/components/image/test.esp32-c3-idf.yaml index c0b2779773..4dae9cd5ec 100644 --- a/tests/components/image/test.esp32-c3-idf.yaml +++ b/tests/components/image/test.esp32-c3-idf.yaml @@ -13,41 +13,4 @@ display: reset_pin: 10 invert_colors: true -image: - - id: binary_image - file: ../../pnglogo.png - type: BINARY - dither: FloydSteinberg - - id: transparent_transparent_image - file: ../../pnglogo.png - type: TRANSPARENT_BINARY - - id: rgba_image - file: ../../pnglogo.png - type: RGBA - resize: 50x50 - - id: rgb24_image - file: ../../pnglogo.png - type: RGB24 - use_transparency: yes - - id: rgb565_image - file: ../../pnglogo.png - type: RGB565 - use_transparency: no - - id: web_svg_image - file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg - resize: 256x48 - type: TRANSPARENT_BINARY - - id: web_tiff_image - file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff - type: RGB24 - resize: 48x48 - - id: web_redirect_image - file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 - type: RGB24 - resize: 48x48 - - id: mdi_alert - file: mdi:alert-circle-outline - resize: 50x50 - - id: another_alert_icon - file: mdi:alert-outline - type: BINARY +<<: !include common.yaml diff --git a/tests/components/image/test.esp32-idf.yaml b/tests/components/image/test.esp32-idf.yaml index e903afea1f..814f16c36c 100644 --- a/tests/components/image/test.esp32-idf.yaml +++ b/tests/components/image/test.esp32-idf.yaml @@ -13,41 +13,4 @@ display: reset_pin: 21 invert_colors: true -image: - - id: binary_image - file: ../../pnglogo.png - type: BINARY - dither: FloydSteinberg - - id: transparent_transparent_image - file: ../../pnglogo.png - type: TRANSPARENT_BINARY - - id: rgba_image - file: ../../pnglogo.png - type: RGBA - resize: 50x50 - - id: rgb24_image - file: ../../pnglogo.png - type: RGB24 - use_transparency: yes - - id: rgb565_image - file: ../../pnglogo.png - type: RGB565 - use_transparency: no - - id: web_svg_image - file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg - resize: 256x48 - type: TRANSPARENT_BINARY - - id: web_tiff_image - file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff - type: RGB24 - resize: 48x48 - - id: web_redirect_image - file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 - type: RGB24 - resize: 48x48 - - id: mdi_alert - file: mdi:alert-circle-outline - resize: 50x50 - - id: another_alert_icon - file: mdi:alert-outline - type: BINARY +<<: !include common.yaml diff --git a/tests/components/image/test.esp8266-ard.yaml b/tests/components/image/test.esp8266-ard.yaml index 5a96ed9497..f963022ff4 100644 --- a/tests/components/image/test.esp8266-ard.yaml +++ b/tests/components/image/test.esp8266-ard.yaml @@ -13,41 +13,4 @@ display: reset_pin: 16 invert_colors: true -image: - - id: binary_image - file: ../../pnglogo.png - type: BINARY - dither: FloydSteinberg - - id: transparent_transparent_image - file: ../../pnglogo.png - type: TRANSPARENT_BINARY - - id: rgba_image - file: ../../pnglogo.png - type: RGBA - resize: 50x50 - - id: rgb24_image - file: ../../pnglogo.png - type: RGB24 - use_transparency: yes - - id: rgb565_image - file: ../../pnglogo.png - type: RGB565 - use_transparency: no - - id: web_svg_image - file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg - resize: 256x48 - type: TRANSPARENT_BINARY - - id: web_tiff_image - file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff - type: RGB24 - resize: 48x48 - - id: web_redirect_image - file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 - type: RGB24 - resize: 48x48 - - id: mdi_alert - file: mdi:alert-circle-outline - resize: 50x50 - - id: another_alert_icon - file: mdi:alert-outline - type: BINARY +<<: !include common.yaml diff --git a/tests/components/image/test.host.yaml b/tests/components/image/test.host.yaml new file mode 100644 index 0000000000..29509db66c --- /dev/null +++ b/tests/components/image/test.host.yaml @@ -0,0 +1,8 @@ +display: + - platform: sdl + auto_clear_enabled: false + dimensions: + width: 480 + height: 480 + +<<: !include common.yaml diff --git a/tests/components/image/test.rp2040-ard.yaml b/tests/components/image/test.rp2040-ard.yaml index 4c40ca464f..5167c99a7d 100644 --- a/tests/components/image/test.rp2040-ard.yaml +++ b/tests/components/image/test.rp2040-ard.yaml @@ -13,41 +13,4 @@ display: reset_pin: 22 invert_colors: true -image: - - id: binary_image - file: ../../pnglogo.png - type: BINARY - dither: FloydSteinberg - - id: transparent_transparent_image - file: ../../pnglogo.png - type: TRANSPARENT_BINARY - - id: rgba_image - file: ../../pnglogo.png - type: RGBA - resize: 50x50 - - id: rgb24_image - file: ../../pnglogo.png - type: RGB24 - use_transparency: yes - - id: rgb565_image - file: ../../pnglogo.png - type: RGB565 - use_transparency: no - - id: web_svg_image - file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg - resize: 256x48 - type: TRANSPARENT_BINARY - - id: web_tiff_image - file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff - type: RGB24 - resize: 48x48 - - id: web_redirect_image - file: https://avatars.githubusercontent.com/u/3060199?s=48&v=4 - type: RGB24 - resize: 48x48 - - id: mdi_alert - file: mdi:alert-circle-outline - resize: 50x50 - - id: another_alert_icon - file: mdi:alert-outline - type: BINARY +<<: !include common.yaml From 21cb941bbe7afb7096ee342fae2c88bdaa27d28b Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Fri, 25 Oct 2024 05:00:28 +0300 Subject: [PATCH 0545/1052] Add OpenTherm component (part 2.1: sensor platform) (#7529) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/opentherm/__init__.py | 20 +- esphome/components/opentherm/const.py | 5 + esphome/components/opentherm/generate.py | 140 ++++++ esphome/components/opentherm/hub.cpp | 108 ++++- esphome/components/opentherm/hub.h | 19 +- esphome/components/opentherm/opentherm.cpp | 3 + esphome/components/opentherm/opentherm.h | 3 +- .../components/opentherm/opentherm_macros.h | 91 ++++ esphome/components/opentherm/schema.py | 438 ++++++++++++++++++ .../components/opentherm/sensor/__init__.py | 35 ++ esphome/components/opentherm/validate.py | 31 ++ tests/components/opentherm/common.yaml | 77 ++- 12 files changed, 933 insertions(+), 37 deletions(-) create mode 100644 esphome/components/opentherm/const.py create mode 100644 esphome/components/opentherm/generate.py create mode 100644 esphome/components/opentherm/opentherm_macros.h create mode 100644 esphome/components/opentherm/schema.py create mode 100644 esphome/components/opentherm/sensor/__init__.py create mode 100644 esphome/components/opentherm/validate.py diff --git a/esphome/components/opentherm/__init__.py b/esphome/components/opentherm/__init__.py index 23443a4028..ee19818a29 100644 --- a/esphome/components/opentherm/__init__.py +++ b/esphome/components/opentherm/__init__.py @@ -1,9 +1,10 @@ from typing import Any -from esphome import pins import esphome.codegen as cg import esphome.config_validation as cv +from esphome import pins from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266 +from . import generate CODEOWNERS = ["@olegtarasov"] MULTI_CONF = True @@ -15,15 +16,14 @@ CONF_DHW_ENABLE = "dhw_enable" CONF_COOLING_ENABLE = "cooling_enable" CONF_OTC_ACTIVE = "otc_active" CONF_CH2_ACTIVE = "ch2_active" +CONF_SUMMER_MODE_ACTIVE = "summer_mode_active" +CONF_DHW_BLOCK = "dhw_block" CONF_SYNC_MODE = "sync_mode" -opentherm_ns = cg.esphome_ns.namespace("opentherm") -OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component) - CONFIG_SCHEMA = cv.All( cv.Schema( { - cv.GenerateID(): cv.declare_id(OpenthermHub), + cv.GenerateID(): cv.declare_id(generate.OpenthermHub), cv.Required(CONF_IN_PIN): pins.internal_gpio_input_pin_schema, cv.Required(CONF_OUT_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_CH_ENABLE, True): cv.boolean, @@ -31,6 +31,8 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_COOLING_ENABLE, False): cv.boolean, cv.Optional(CONF_OTC_ACTIVE, False): cv.boolean, cv.Optional(CONF_CH2_ACTIVE, False): cv.boolean, + cv.Optional(CONF_SUMMER_MODE_ACTIVE, False): cv.boolean, + cv.Optional(CONF_DHW_BLOCK, False): cv.boolean, cv.Optional(CONF_SYNC_MODE, False): cv.boolean, } ).extend(cv.COMPONENT_SCHEMA), @@ -39,8 +41,6 @@ CONFIG_SCHEMA = cv.All( async def to_code(config: dict[str, Any]) -> None: - # Create the hub, passing the two callbacks defined below - # Since the hub is used in the callbacks, we need to define it first var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) @@ -53,5 +53,7 @@ async def to_code(config: dict[str, Any]) -> None: non_sensors = {CONF_ID, CONF_IN_PIN, CONF_OUT_PIN} for key, value in config.items(): - if key not in non_sensors: - cg.add(getattr(var, f"set_{key}")(value)) + if key in non_sensors: + continue + + cg.add(getattr(var, f"set_{key}")(value)) diff --git a/esphome/components/opentherm/const.py b/esphome/components/opentherm/const.py new file mode 100644 index 0000000000..1f997c5d9c --- /dev/null +++ b/esphome/components/opentherm/const.py @@ -0,0 +1,5 @@ +OPENTHERM = "opentherm" + +CONF_OPENTHERM_ID = "opentherm_id" + +SENSOR = "sensor" diff --git a/esphome/components/opentherm/generate.py b/esphome/components/opentherm/generate.py new file mode 100644 index 0000000000..6a97835a57 --- /dev/null +++ b/esphome/components/opentherm/generate.py @@ -0,0 +1,140 @@ +from collections.abc import Awaitable +from typing import Any, Callable + +import esphome.codegen as cg +from esphome.const import CONF_ID +from . import const +from .schema import TSchema + +opentherm_ns = cg.esphome_ns.namespace("opentherm") +OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component) + + +def define_has_component(component_type: str, keys: list[str]) -> None: + cg.add_define( + f"OPENTHERM_{component_type.upper()}_LIST(F, sep)", + cg.RawExpression( + " sep ".join(map(lambda key: f"F({key}_{component_type.lower()})", keys)) + ), + ) + for key in keys: + cg.add_define(f"OPENTHERM_HAS_{component_type.upper()}_{key}") + + +def define_message_handler( + component_type: str, keys: list[str], schemas: dict[str, TSchema] +) -> None: + # The macros defined here should be able to generate things like this: + # // Parsing a message and publishing to sensors + # case MessageId::Message: + # // Can have multiple sensors here, for example for a Status message with multiple flags + # this->thing_binary_sensor->publish_state(parse_flag8_lb_0(response)); + # this->other_binary_sensor->publish_state(parse_flag8_lb_1(response)); + # break; + # // Building a message for a write request + # case MessageId::Message: { + # unsigned int data = 0; + # data = write_flag8_lb_0(some_input_switch->state, data); // Where input_sensor can also be a number/output/switch + # data = write_u8_hb(some_number->state, data); + # return opentherm_->build_request_(MessageType::WriteData, MessageId::Message, data); + # } + + messages: dict[str, list[tuple[str, str]]] = {} + for key in keys: + msg = schemas[key].message + if msg not in messages: + messages[msg] = [] + messages[msg].append((key, schemas[key].message_data)) + + cg.add_define( + f"OPENTHERM_{component_type.upper()}_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep)", + cg.RawExpression( + " msg_sep ".join( + [ + f"MESSAGE({msg}) " + + " entity_sep ".join( + [ + f"ENTITY({key}_{component_type.lower()}, {msg_data})" + for key, msg_data in keys + ] + ) + + " postscript" + for msg, keys in messages.items() + ] + ) + ), + ) + + +def define_readers(component_type: str, keys: list[str]) -> None: + for key in keys: + cg.add_define( + f"OPENTHERM_READ_{key}", + cg.RawExpression(f"this->{key}_{component_type.lower()}->state"), + ) + + +def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]): + messages: set[tuple[str, bool]] = set() + for key in keys: + messages.add((schemas[key].message, schemas[key].keep_updated)) + for msg, keep_updated in messages: + msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}") + if keep_updated: + cg.add(hub.add_repeating_message(msg_expr)) + else: + cg.add(hub.add_initial_message(msg_expr)) + + +def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None: + if config_key in config: + cg.add(getattr(var, f"set_{config_key}")(config[config_key])) + + +Create = Callable[[dict[str, Any], str, cg.MockObj], Awaitable[cg.Pvariable]] + + +def create_only_conf( + create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]] +) -> Create: + return lambda conf, _key, _hub: create(conf) + + +async def component_to_code( + component_type: str, + schemas: dict[str, TSchema], + type: cg.MockObjClass, + create: Create, + config: dict[str, Any], +) -> list[str]: + """Generate the code for each configured component in the schema of a component type. + + Parameters: + - component_type: The type of component, e.g. "sensor" or "binary_sensor" + - schema_: The schema for that component type, a list of available components + - type: The type of the component, e.g. sensor.Sensor or OpenthermOutput + - create: A constructor function for the component, which receives the config, + the key and the hub and should asynchronously return the new component + - config: The configuration for this component type + + Returns: The list of keys for the created components + """ + cg.add_define(f"OPENTHERM_USE_{component_type.upper()}") + + hub = await cg.get_variable(config[const.CONF_OPENTHERM_ID]) + + keys: list[str] = [] + for key, conf in config.items(): + if not isinstance(conf, dict): + continue + id = conf[CONF_ID] + if id and id.type == type: + entity = await create(conf, key, hub) + cg.add(getattr(hub, f"set_{key}_{component_type.lower()}")(entity)) + keys.append(key) + + define_has_component(component_type, keys) + define_message_handler(component_type, keys, schemas) + add_messages(hub, keys, schemas) + + return keys diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index c26fbced32..770bbd82b7 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -7,50 +7,114 @@ namespace esphome { namespace opentherm { static const char *const TAG = "opentherm"; +namespace message_data { +bool parse_flag8_lb_0(OpenthermData &data) { return read_bit(data.valueLB, 0); } +bool parse_flag8_lb_1(OpenthermData &data) { return read_bit(data.valueLB, 1); } +bool parse_flag8_lb_2(OpenthermData &data) { return read_bit(data.valueLB, 2); } +bool parse_flag8_lb_3(OpenthermData &data) { return read_bit(data.valueLB, 3); } +bool parse_flag8_lb_4(OpenthermData &data) { return read_bit(data.valueLB, 4); } +bool parse_flag8_lb_5(OpenthermData &data) { return read_bit(data.valueLB, 5); } +bool parse_flag8_lb_6(OpenthermData &data) { return read_bit(data.valueLB, 6); } +bool parse_flag8_lb_7(OpenthermData &data) { return read_bit(data.valueLB, 7); } +bool parse_flag8_hb_0(OpenthermData &data) { return read_bit(data.valueHB, 0); } +bool parse_flag8_hb_1(OpenthermData &data) { return read_bit(data.valueHB, 1); } +bool parse_flag8_hb_2(OpenthermData &data) { return read_bit(data.valueHB, 2); } +bool parse_flag8_hb_3(OpenthermData &data) { return read_bit(data.valueHB, 3); } +bool parse_flag8_hb_4(OpenthermData &data) { return read_bit(data.valueHB, 4); } +bool parse_flag8_hb_5(OpenthermData &data) { return read_bit(data.valueHB, 5); } +bool parse_flag8_hb_6(OpenthermData &data) { return read_bit(data.valueHB, 6); } +bool parse_flag8_hb_7(OpenthermData &data) { return read_bit(data.valueHB, 7); } +uint8_t parse_u8_lb(OpenthermData &data) { return data.valueLB; } +uint8_t parse_u8_hb(OpenthermData &data) { return data.valueHB; } +int8_t parse_s8_lb(OpenthermData &data) { return (int8_t) data.valueLB; } +int8_t parse_s8_hb(OpenthermData &data) { return (int8_t) data.valueHB; } +uint16_t parse_u16(OpenthermData &data) { return data.u16(); } +int16_t parse_s16(OpenthermData &data) { return data.s16(); } +float parse_f88(OpenthermData &data) { return data.f88(); } -OpenthermData OpenthermHub::build_request_(MessageId request_id) { +void write_flag8_lb_0(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 0, value); } +void write_flag8_lb_1(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 1, value); } +void write_flag8_lb_2(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 2, value); } +void write_flag8_lb_3(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 3, value); } +void write_flag8_lb_4(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 4, value); } +void write_flag8_lb_5(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 5, value); } +void write_flag8_lb_6(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 6, value); } +void write_flag8_lb_7(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 7, value); } +void write_flag8_hb_0(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 0, value); } +void write_flag8_hb_1(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 1, value); } +void write_flag8_hb_2(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 2, value); } +void write_flag8_hb_3(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 3, value); } +void write_flag8_hb_4(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 4, value); } +void write_flag8_hb_5(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 5, value); } +void write_flag8_hb_6(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 6, value); } +void write_flag8_hb_7(const bool value, OpenthermData &data) { data.valueHB = write_bit(data.valueHB, 7, value); } +void write_u8_lb(const uint8_t value, OpenthermData &data) { data.valueLB = value; } +void write_u8_hb(const uint8_t value, OpenthermData &data) { data.valueHB = value; } +void write_s8_lb(const int8_t value, OpenthermData &data) { data.valueLB = (uint8_t) value; } +void write_s8_hb(const int8_t value, OpenthermData &data) { data.valueHB = (uint8_t) value; } +void write_u16(const uint16_t value, OpenthermData &data) { data.u16(value); } +void write_s16(const int16_t value, OpenthermData &data) { data.s16(value); } +void write_f88(const float value, OpenthermData &data) { data.f88(value); } + +} // namespace message_data + +OpenthermData OpenthermHub::build_request_(MessageId request_id) const { OpenthermData data; data.type = 0; data.id = 0; data.valueHB = 0; data.valueLB = 0; - // First, handle the status request. This requires special logic, because we - // wouldn't want to inadvertently disable domestic hot water, for example. - // It is also included in the macro-generated code below, but that will - // never be executed, because we short-circuit it here. + // We need this special logic for STATUS message because we have two options for specifying boiler modes: + // with static config values in the hub, or with separate switches. if (request_id == MessageId::STATUS) { - bool const ch_enabled = this->ch_enable; - bool dhw_enabled = this->dhw_enable; - bool cooling_enabled = this->cooling_enable; - bool otc_enabled = this->otc_active; - bool ch2_enabled = this->ch2_active; + // NOLINTBEGIN + bool const ch_enabled = this->ch_enable && OPENTHERM_READ_ch_enable && OPENTHERM_READ_t_set > 0.0; + bool const dhw_enabled = this->dhw_enable && OPENTHERM_READ_dhw_enable; + bool const cooling_enabled = + this->cooling_enable && OPENTHERM_READ_cooling_enable && OPENTHERM_READ_cooling_control > 0.0; + bool const otc_enabled = this->otc_active && OPENTHERM_READ_otc_active; + bool const ch2_enabled = this->ch2_active && OPENTHERM_READ_ch2_active && OPENTHERM_READ_t_set_ch2 > 0.0; + bool const summer_mode_is_active = this->summer_mode_active && OPENTHERM_READ_summer_mode_active; + bool const dhw_blocked = this->dhw_block && OPENTHERM_READ_dhw_block; + // NOLINTEND data.type = MessageType::READ_DATA; data.id = MessageId::STATUS; - data.valueHB = ch_enabled | (dhw_enabled << 1) | (cooling_enabled << 2) | (otc_enabled << 3) | (ch2_enabled << 4); + data.valueHB = ch_enabled | (dhw_enabled << 1) | (cooling_enabled << 2) | (otc_enabled << 3) | (ch2_enabled << 4) | + (summer_mode_is_active << 5) | (dhw_blocked << 6); + + return data; + } // Disable incomplete switch statement warnings, because the cases in each // switch are generated based on the configured sensors and inputs. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch" - // TODO: This is a placeholder for an auto-generated switch statement which builds request structure based on - // which sensors are enabled in config. + switch (request_id) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) } #pragma GCC diagnostic pop - return data; - } - return OpenthermData(); + // And if we get here, a message was requested which somehow wasn't handled. + // This shouldn't happen due to the way the defines are configured, so we + // log an error and just return a 0 message. + ESP_LOGE(TAG, "Tried to create a request with unknown id %d. This should never happen, so please open an issue.", + request_id); + return {}; } -OpenthermHub::OpenthermHub() : Component() {} +OpenthermHub::OpenthermHub() : Component(), in_pin_{}, out_pin_{} {} void OpenthermHub::process_response(OpenthermData &data) { ESP_LOGD(TAG, "Received OpenTherm response with id %d (%s)", data.id, this->opentherm_->message_id_to_str((MessageId) data.id)); ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(data).c_str()); + + switch (data.id) { + OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, , + OPENTHERM_MESSAGE_RESPONSE_POSTSCRIPT, ) + } } void OpenthermHub::setup() { @@ -254,15 +318,17 @@ void OpenthermHub::handle_timeout_error_() { this->stop_opentherm_(); } -#define ID(x) x -#define SHOW2(x) #x -#define SHOW(x) SHOW2(x) - 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: %d", 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, " Initial requests:"); for (auto type : this->initial_messages_) { ESP_LOGCONFIG(TAG, " - %d", type); diff --git a/esphome/components/opentherm/hub.h b/esphome/components/opentherm/hub.h index ce9f09fe33..3b90cdf427 100644 --- a/esphome/components/opentherm/hub.h +++ b/esphome/components/opentherm/hub.h @@ -7,11 +7,17 @@ #include "opentherm.h" +#ifdef OPENTHERM_USE_SENSOR +#include "esphome/components/sensor/sensor.h" +#endif + #include #include #include #include +#include "opentherm_macros.h" + namespace esphome { namespace opentherm { @@ -23,6 +29,8 @@ class OpenthermHub : public Component { // The OpenTherm interface std::unique_ptr opentherm_; + OPENTHERM_SENSOR_LIST(OPENTHERM_DECLARE_SENSOR, ) + // The set of initial messages to send on starting communication with the boiler std::unordered_set initial_messages_; // and the repeating messages which are sent repeatedly to update various sensors @@ -44,7 +52,7 @@ class OpenthermHub : public Component { bool sync_mode_ = false; // Create OpenTherm messages based on the message id - OpenthermData build_request_(MessageId request_id); + OpenthermData build_request_(MessageId request_id) const; void handle_protocol_write_error_(); void handle_protocol_read_error_(); void handle_timeout_error_(); @@ -78,6 +86,8 @@ class OpenthermHub : public Component { void set_in_pin(InternalGPIOPin *in_pin) { this->in_pin_ = in_pin; } void set_out_pin(InternalGPIOPin *out_pin) { this->out_pin_ = out_pin; } + OPENTHERM_SENSOR_LIST(OPENTHERM_SET_SENSOR, ) + // Add a request to the set of initial requests void add_initial_message(MessageId message_id) { this->initial_messages_.insert(message_id); } // Add a request to the set of repeating requests. Note that a large number of repeating @@ -86,9 +96,10 @@ class OpenthermHub : public Component { // will be processed. void add_repeating_message(MessageId message_id) { this->repeating_messages_.insert(message_id); } - // There are five status variables, which can either be set as a simple variable, + // There are seven status variables, which can either be set as a simple variable, // or using a switch. ch_enable and dhw_enable default to true, the others to false. - bool ch_enable = true, dhw_enable = true, cooling_enable = false, otc_active = false, ch2_active = false; + bool ch_enable = true, dhw_enable = true, cooling_enable = false, otc_active = false, ch2_active = false, + summer_mode_active = false, dhw_block = false; // Setters for the status variables void set_ch_enable(bool value) { this->ch_enable = value; } @@ -96,6 +107,8 @@ class OpenthermHub : public Component { void set_cooling_enable(bool value) { this->cooling_enable = value; } void set_otc_active(bool value) { this->otc_active = value; } void set_ch2_active(bool value) { this->ch2_active = value; } + void set_summer_mode_active(bool value) { this->summer_mode_active = value; } + void set_dhw_block(bool value) { this->dhw_block = value; } void set_sync_mode(bool sync_mode) { this->sync_mode_ = sync_mode; } float get_setup_priority() const override { return setup_priority::HARDWARE; } diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index b830cc01d3..4a23bb94cf 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -283,6 +283,9 @@ bool OpenTherm::init_esp32_timer_() { .clk_src = TIMER_SRC_CLK_DEFAULT, #endif .divider = 80, +#if defined(SOC_TIMER_GROUP_SUPPORT_XTAL) && ESP_IDF_VERSION_MAJOR < 5 + .clk_src = TIMER_SRC_CLK_APB +#endif }; esp_err_t result; diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 609cfb6243..23f4b39a1a 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -20,7 +20,6 @@ namespace esphome { namespace opentherm { -// TODO: Account for immutable semantics change in hub.cpp when doing later installments of OpenTherm PR template constexpr T read_bit(T value, uint8_t bit) { return (value >> bit) & 0x01; } template constexpr T set_bit(T value, uint8_t bit) { return value |= (1UL << bit); } @@ -28,7 +27,7 @@ template constexpr T set_bit(T value, uint8_t bit) { return value |= (1 template constexpr T clear_bit(T value, uint8_t bit) { return value &= ~(1UL << bit); } template constexpr T write_bit(T value, uint8_t bit, uint8_t bit_value) { - return bit_value ? setBit(value, bit) : clearBit(value, bit); + return bit_value ? set_bit(value, bit) : clear_bit(value, bit); } enum OperationMode { diff --git a/esphome/components/opentherm/opentherm_macros.h b/esphome/components/opentherm/opentherm_macros.h new file mode 100644 index 0000000000..0389e975ff --- /dev/null +++ b/esphome/components/opentherm/opentherm_macros.h @@ -0,0 +1,91 @@ +#pragma once +namespace esphome { +namespace opentherm { + +// ===== hub.h macros ===== + +// *_LIST macros will be generated in defines.h if at least one sensor from each platform is used. +// These lists will look like this: +// #define OPENTHERM_BINARY_SENSOR_LIST(F, sep) F(sensor_1) sep F(sensor_2) +// These lists will be used in hub.h to define sensor fields (passing macros like OPENTHERM_DECLARE_SENSOR as F) +// and setters (passing macros like OPENTHERM_SET_SENSOR as F) (see below) +// In order for things not to break, we define empty lists here in case some platforms are not used in config. +#ifndef OPENTHERM_SENSOR_LIST +#define OPENTHERM_SENSOR_LIST(F, sep) +#endif + +// Use macros to create fields for every entity specified in the ESPHome configuration +#define OPENTHERM_DECLARE_SENSOR(entity) sensor::Sensor *entity; + +// Setter macros +#define OPENTHERM_SET_SENSOR(entity) \ + void set_##entity(sensor::Sensor *sensor) { this->entity = sensor; } + +// ===== hub.cpp macros ===== + +// *_MESSAGE_HANDLERS are generated in defines.h and look like this: +// OPENTHERM_NUMBER_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) MESSAGE(COOLING_CONTROL) +// ENTITY(cooling_control_number, f88) postscript msg_sep They contain placeholders for message part and entities parts, +// since one message can contain multiple entities. MESSAGE part is substituted with OPENTHERM_MESSAGE_WRITE_MESSAGE, +// OPENTHERM_MESSAGE_READ_MESSAGE or OPENTHERM_MESSAGE_RESPONSE_MESSAGE. ENTITY part is substituted with +// OPENTHERM_MESSAGE_WRITE_ENTITY or OPENTHERM_MESSAGE_RESPONSE_ENTITY. OPENTHERM_IGNORE is used for sensor read +// requests since no data needs to be sent or processed, just the data id. + +// In order for things not to break, we define empty lists here in case some platforms are not used in config. +#ifndef OPENTHERM_SENSOR_MESSAGE_HANDLERS +#define OPENTHERM_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) +#endif + +// Read data request builder +#define OPENTHERM_MESSAGE_READ_MESSAGE(msg) \ + case MessageId::msg: \ + data.type = MessageType::READ_DATA; \ + data.id = request_id; \ + return data; + +// Data processing builders +#define OPENTHERM_MESSAGE_RESPONSE_MESSAGE(msg) case MessageId::msg: +#define OPENTHERM_MESSAGE_RESPONSE_ENTITY(key, msg_data) this->key->publish_state(message_data::parse_##msg_data(data)); +#define OPENTHERM_MESSAGE_RESPONSE_POSTSCRIPT break; + +#define OPENTHERM_IGNORE(x, y) + +// Default macros for STATUS entities +#ifndef OPENTHERM_READ_ch_enable +#define OPENTHERM_READ_ch_enable true +#endif +#ifndef OPENTHERM_READ_dhw_enable +#define OPENTHERM_READ_dhw_enable true +#endif +#ifndef OPENTHERM_READ_t_set +#define OPENTHERM_READ_t_set 0.0 +#endif +#ifndef OPENTHERM_READ_cooling_enable +#define OPENTHERM_READ_cooling_enable false +#endif +#ifndef OPENTHERM_READ_cooling_control +#define OPENTHERM_READ_cooling_control 0.0 +#endif +#ifndef OPENTHERM_READ_otc_active +#define OPENTHERM_READ_otc_active false +#endif +#ifndef OPENTHERM_READ_ch2_active +#define OPENTHERM_READ_ch2_active false +#endif +#ifndef OPENTHERM_READ_t_set_ch2 +#define OPENTHERM_READ_t_set_ch2 0.0 +#endif +#ifndef OPENTHERM_READ_summer_mode_active +#define OPENTHERM_READ_summer_mode_active false +#endif +#ifndef OPENTHERM_READ_dhw_block +#define OPENTHERM_READ_dhw_block false +#endif + +// These macros utilize the structure of *_LIST macros in order +#define ID(x) x +#define SHOW_INNER(x) #x +#define SHOW(x) SHOW_INNER(x) + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/schema.py b/esphome/components/opentherm/schema.py new file mode 100644 index 0000000000..6ed0029437 --- /dev/null +++ b/esphome/components/opentherm/schema.py @@ -0,0 +1,438 @@ +# This file contains a schema for all supported sensors, binary sensors and +# inputs of the OpenTherm component. + +from dataclasses import dataclass +from typing import Optional, TypeVar + +from esphome.const import ( + UNIT_CELSIUS, + UNIT_EMPTY, + UNIT_KILOWATT, + UNIT_MICROAMP, + UNIT_PERCENT, + UNIT_REVOLUTIONS_PER_MINUTE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + STATE_CLASS_NONE, + STATE_CLASS_TOTAL_INCREASING, +) + + +@dataclass +class EntitySchema: + description: str + """Description of the item, based on the OpenTherm spec""" + + message: str + """OpenTherm message id used to read or write the value""" + + keep_updated: bool + """Whether the value should be read or write repeatedly (True) or only during + the initialization phase (False) + """ + + message_data: str + """Instructions on how to interpret the data in the message + - flag8_[hb|lb]_[0-7]: data is a byte of single bit flags, + this flag is set in the high (hb) or low byte (lb), + at position 0 to 7 + - u8_[hb|lb]: data is an unsigned 8-bit integer, + in the high (hb) or low byte (lb) + - s8_[hb|lb]: data is an signed 8-bit integer, + in the high (hb) or low byte (lb) + - f88: data is a signed fixed point value with + 1 sign bit, 7 integer bits, 8 fractional bits + - u16: data is an unsigned 16-bit integer + - s16: data is a signed 16-bit integer + """ + + +TSchema = TypeVar("TSchema", bound=EntitySchema) + + +@dataclass +class SensorSchema(EntitySchema): + accuracy_decimals: int + state_class: str + unit_of_measurement: Optional[str] = None + icon: Optional[str] = None + device_class: Optional[str] = None + disabled_by_default: bool = False + + +SENSORS: dict[str, SensorSchema] = { + "rel_mod_level": SensorSchema( + description="Relative modulation level", + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=2, + icon="mdi:percent", + state_class=STATE_CLASS_MEASUREMENT, + message="MODULATION_LEVEL", + keep_updated=True, + message_data="f88", + ), + "ch_pressure": SensorSchema( + description="Water pressure in CH circuit", + unit_of_measurement="bar", + accuracy_decimals=2, + device_class=DEVICE_CLASS_PRESSURE, + state_class=STATE_CLASS_MEASUREMENT, + message="CH_WATER_PRESSURE", + keep_updated=True, + message_data="f88", + ), + "dhw_flow_rate": SensorSchema( + description="Water flow rate in DHW circuit", + unit_of_measurement="l/min", + accuracy_decimals=2, + icon="mdi:waves-arrow-right", + state_class=STATE_CLASS_MEASUREMENT, + message="DHW_FLOW_RATE", + keep_updated=True, + message_data="f88", + ), + "t_boiler": SensorSchema( + description="Boiler water temperature", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="FEED_TEMP", + keep_updated=True, + message_data="f88", + ), + "t_dhw": SensorSchema( + description="DHW temperature", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="DHW_TEMP", + keep_updated=True, + message_data="f88", + ), + "t_outside": SensorSchema( + description="Outside temperature", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="OUTSIDE_TEMP", + keep_updated=True, + message_data="f88", + ), + "t_ret": SensorSchema( + description="Return water temperature", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="RETURN_WATER_TEMP", + keep_updated=True, + message_data="f88", + ), + "t_storage": SensorSchema( + description="Solar storage temperature", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="SOLAR_STORE_TEMP", + keep_updated=True, + message_data="f88", + ), + "t_collector": SensorSchema( + description="Solar collector temperature", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=0, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="SOLAR_COLLECT_TEMP", + keep_updated=True, + message_data="s16", + ), + "t_flow_ch2": SensorSchema( + description="Flow water temperature CH2 circuit", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="FEED_TEMP_CH2", + keep_updated=True, + message_data="f88", + ), + "t_dhw2": SensorSchema( + description="Domestic hot water temperature 2", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="DHW2_TEMP", + keep_updated=True, + message_data="f88", + ), + "t_exhaust": SensorSchema( + description="Boiler exhaust temperature", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=0, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="EXHAUST_TEMP", + keep_updated=True, + message_data="s16", + ), + "fan_speed": SensorSchema( + description="Boiler fan speed", + unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE, + accuracy_decimals=0, + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + message="FAN_SPEED", + keep_updated=True, + message_data="u16", + ), + "flame_current": SensorSchema( + description="Boiler flame current", + unit_of_measurement=UNIT_MICROAMP, + accuracy_decimals=0, + device_class=DEVICE_CLASS_CURRENT, + state_class=STATE_CLASS_MEASUREMENT, + message="FLAME_CURRENT", + keep_updated=True, + message_data="f88", + ), + "burner_starts": SensorSchema( + description="Number of starts burner", + accuracy_decimals=0, + icon="mdi:gas-burner", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="BURNER_STARTS", + keep_updated=True, + message_data="u16", + ), + "ch_pump_starts": SensorSchema( + description="Number of starts CH pump", + accuracy_decimals=0, + icon="mdi:pump", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="CH_PUMP_STARTS", + keep_updated=True, + message_data="u16", + ), + "dhw_pump_valve_starts": SensorSchema( + description="Number of starts DHW pump/valve", + accuracy_decimals=0, + icon="mdi:water-pump", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="DHW_PUMP_STARTS", + keep_updated=True, + message_data="u16", + ), + "dhw_burner_starts": SensorSchema( + description="Number of starts burner during DHW mode", + accuracy_decimals=0, + icon="mdi:gas-burner", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="DHW_BURNER_STARTS", + keep_updated=True, + message_data="u16", + ), + "burner_operation_hours": SensorSchema( + description="Number of hours that burner is in operation", + accuracy_decimals=0, + icon="mdi:clock-outline", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="BURNER_HOURS", + keep_updated=True, + message_data="u16", + ), + "ch_pump_operation_hours": SensorSchema( + description="Number of hours that CH pump has been running", + accuracy_decimals=0, + icon="mdi:clock-outline", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="CH_PUMP_HOURS", + keep_updated=True, + message_data="u16", + ), + "dhw_pump_valve_operation_hours": SensorSchema( + description="Number of hours that DHW pump has been running or DHW valve has been opened", + accuracy_decimals=0, + icon="mdi:clock-outline", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="DHW_PUMP_HOURS", + keep_updated=True, + message_data="u16", + ), + "dhw_burner_operation_hours": SensorSchema( + description="Number of hours that burner is in operation during DHW mode", + accuracy_decimals=0, + icon="mdi:clock-outline", + state_class=STATE_CLASS_TOTAL_INCREASING, + message="DHW_BURNER_HOURS", + keep_updated=True, + message_data="u16", + ), + "t_dhw_set_ub": SensorSchema( + description="Upper bound for adjustment of DHW setpoint", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=0, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="DHW_BOUNDS", + keep_updated=False, + message_data="s8_hb", + ), + "t_dhw_set_lb": SensorSchema( + description="Lower bound for adjustment of DHW setpoint", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=0, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="DHW_BOUNDS", + keep_updated=False, + message_data="s8_lb", + ), + "max_t_set_ub": SensorSchema( + description="Upper bound for adjustment of max CH setpoint", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=0, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="CH_BOUNDS", + keep_updated=False, + message_data="s8_hb", + ), + "max_t_set_lb": SensorSchema( + description="Lower bound for adjustment of max CH setpoint", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=0, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="CH_BOUNDS", + keep_updated=False, + message_data="s8_lb", + ), + "t_dhw_set": SensorSchema( + description="Domestic hot water temperature setpoint", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="DHW_SETPOINT", + keep_updated=True, + message_data="f88", + ), + "max_t_set": SensorSchema( + description="Maximum allowable CH water setpoint", + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=2, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + message="MAX_CH_SETPOINT", + keep_updated=True, + message_data="f88", + ), + "oem_fault_code": SensorSchema( + description="OEM fault code", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + message="FAULT_FLAGS", + keep_updated=True, + message_data="u8_lb", + ), + "oem_diagnostic_code": SensorSchema( + description="OEM diagnostic code", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + message="OEM_DIAGNOSTIC", + keep_updated=True, + message_data="u16", + ), + "max_capacity": SensorSchema( + description="Maximum boiler capacity (KW)", + unit_of_measurement=UNIT_KILOWATT, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + disabled_by_default=True, + message="MAX_BOILER_CAPACITY", + keep_updated=False, + message_data="u8_hb", + ), + "min_mod_level": SensorSchema( + description="Minimum modulation level", + unit_of_measurement=UNIT_PERCENT, + accuracy_decimals=0, + icon="mdi:percent", + disabled_by_default=True, + state_class=STATE_CLASS_MEASUREMENT, + message="MAX_BOILER_CAPACITY", + keep_updated=False, + message_data="u8_lb", + ), + "opentherm_version_device": SensorSchema( + description="Version of OpenTherm implemented by device", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + disabled_by_default=True, + message="OT_VERSION_DEVICE", + keep_updated=False, + message_data="f88", + ), + "device_type": SensorSchema( + description="Device product type", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + disabled_by_default=True, + message="VERSION_DEVICE", + keep_updated=False, + message_data="u8_hb", + ), + "device_version": SensorSchema( + description="Device product version", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + disabled_by_default=True, + message="VERSION_DEVICE", + keep_updated=False, + message_data="u8_lb", + ), + "device_id": SensorSchema( + description="Device ID code", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + disabled_by_default=True, + message="DEVICE_CONFIG", + keep_updated=False, + message_data="u8_lb", + ), + "otc_hc_ratio_ub": SensorSchema( + description="OTC heat curve ratio upper bound", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + disabled_by_default=True, + message="OTC_CURVE_BOUNDS", + keep_updated=False, + message_data="u8_hb", + ), + "otc_hc_ratio_lb": SensorSchema( + description="OTC heat curve ratio lower bound", + unit_of_measurement=UNIT_EMPTY, + accuracy_decimals=0, + state_class=STATE_CLASS_NONE, + disabled_by_default=True, + message="OTC_CURVE_BOUNDS", + keep_updated=False, + message_data="u8_lb", + ), +} diff --git a/esphome/components/opentherm/sensor/__init__.py b/esphome/components/opentherm/sensor/__init__.py new file mode 100644 index 0000000000..20224e0eda --- /dev/null +++ b/esphome/components/opentherm/sensor/__init__.py @@ -0,0 +1,35 @@ +from typing import Any + +import esphome.config_validation as cv +from esphome.components import sensor +from .. import const, schema, validate, generate + +DEPENDENCIES = [const.OPENTHERM] +COMPONENT_TYPE = const.SENSOR + + +def get_entity_validation_schema(entity: schema.SensorSchema) -> cv.Schema: + return sensor.sensor_schema( + unit_of_measurement=entity.unit_of_measurement + or sensor._UNDEF, # pylint: disable=protected-access + accuracy_decimals=entity.accuracy_decimals, + device_class=entity.device_class + or sensor._UNDEF, # pylint: disable=protected-access + icon=entity.icon or sensor._UNDEF, # pylint: disable=protected-access + state_class=entity.state_class, + ) + + +CONFIG_SCHEMA = validate.create_component_schema( + schema.SENSORS, get_entity_validation_schema +) + + +async def to_code(config: dict[str, Any]) -> None: + await generate.component_to_code( + COMPONENT_TYPE, + schema.SENSORS, + sensor.Sensor, + generate.create_only_conf(sensor.new_sensor), + config, + ) diff --git a/esphome/components/opentherm/validate.py b/esphome/components/opentherm/validate.py new file mode 100644 index 0000000000..d4507672a5 --- /dev/null +++ b/esphome/components/opentherm/validate.py @@ -0,0 +1,31 @@ +from typing import Callable + +from voluptuous import Schema + +import esphome.config_validation as cv + +from . import const, schema, generate +from .schema import TSchema + + +def create_entities_schema( + entities: dict[str, schema.EntitySchema], + get_entity_validation_schema: Callable[[TSchema], cv.Schema], +) -> Schema: + entity_schema = {} + for key, entity in entities.items(): + entity_schema[cv.Optional(key)] = get_entity_validation_schema(entity) + return cv.Schema(entity_schema) + + +def create_component_schema( + entities: dict[str, schema.EntitySchema], + get_entity_validation_schema: Callable[[TSchema], cv.Schema], +) -> Schema: + return ( + cv.Schema( + {cv.GenerateID(const.CONF_OPENTHERM_ID): cv.use_id(generate.OpenthermHub)} + ) + .extend(create_entities_schema(entities, get_entity_validation_schema)) + .extend(cv.COMPONENT_SCHEMA) + ) diff --git a/tests/components/opentherm/common.yaml b/tests/components/opentherm/common.yaml index 4148b280d0..27cbae280a 100644 --- a/tests/components/opentherm/common.yaml +++ b/tests/components/opentherm/common.yaml @@ -1,3 +1,76 @@ +api: +wifi: + ap: + ssid: "Thermostat" + password: "MySecretThemostat" + opentherm: - in_pin: 1 - out_pin: 2 + in_pin: 4 + out_pin: 5 + ch_enable: true + dhw_enable: false + cooling_enable: false + otc_active: false + ch2_active: true + summer_mode_active: true + dhw_block: true + sync_mode: true + +sensor: + - platform: opentherm + rel_mod_level: + name: "Boiler Relative modulation level" + ch_pressure: + name: "Boiler Water pressure in CH circuit" + dhw_flow_rate: + name: "Boiler Water flow rate in DHW circuit" + t_boiler: + name: "Boiler water temperature" + t_dhw: + name: "Boiler DHW temperature" + t_outside: + name: "Boiler Outside temperature" + t_ret: + name: "Boiler Return water temperature" + t_storage: + name: "Boiler Solar storage temperature" + t_collector: + name: "Boiler Solar collector temperature" + t_flow_ch2: + name: "Boiler Flow water temperature CH2 circuit" + t_dhw2: + name: "Boiler Domestic hot water temperature 2" + t_exhaust: + name: "Boiler Exhaust temperature" + burner_starts: + name: "Boiler Number of starts burner" + ch_pump_starts: + name: "Boiler Number of starts CH pump" + dhw_pump_valve_starts: + name: "Boiler Number of starts DHW pump/valve" + dhw_burner_starts: + name: "Boiler Number of starts burner during DHW mode" + burner_operation_hours: + name: "Boiler Number of hours that burner is in operation (i.e. flame on)" + ch_pump_operation_hours: + name: "Boiler Number of hours that CH pump has been running" + dhw_pump_valve_operation_hours: + name: "Boiler Number of hours that DHW pump has been running or DHW valve has been opened" + dhw_burner_operation_hours: + name: "Boiler Number of hours that burner is in operation during DHW mode" + t_dhw_set_ub: + name: "Boiler Upper bound for adjustement of DHW setpoint" + t_dhw_set_lb: + name: "Boiler Lower bound for adjustement of DHW setpoint" + max_t_set_ub: + name: "Boiler Upper bound for adjustement of max CH setpoint" + max_t_set_lb: + name: "Boiler Lower bound for adjustement of max CH setpoint" + t_dhw_set: + name: "Boiler Domestic hot water temperature setpoint" + max_t_set: + name: "Boiler Maximum allowable CH water setpoint" + otc_hc_ratio_ub: + name: "OTC heat curve ratio upper bound" + otc_hc_ratio_lb: + name: "OTC heat curve ratio lower bound" From 34de2bbe992b842ad8017daa94ba6500ffb26d1c Mon Sep 17 00:00:00 2001 From: SeByDocKy Date: Sat, 26 Oct 2024 23:54:57 +0200 Subject: [PATCH 0546/1052] gp8403 : Add the possibility to use substitution for channel selection (#7681) --- esphome/components/gp8403/output/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/gp8403/output/__init__.py b/esphome/components/gp8403/output/__init__.py index 1cf95ac6e5..7f17faa1b1 100644 --- a/esphome/components/gp8403/output/__init__.py +++ b/esphome/components/gp8403/output/__init__.py @@ -16,7 +16,7 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(GP8403Output), cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403), - cv.Required(CONF_CHANNEL): cv.one_of(0, 1), + cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=1), } ).extend(cv.COMPONENT_SCHEMA) From 1e2497748d3a18847df868ac8f4b893800426652 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:17:09 +1100 Subject: [PATCH 0547/1052] [rpi_dpi_rgb] Fix get_width and height (Bugfix) (#7675) Co-authored-by: clydeps --- .../components/rpi_dpi_rgb/rpi_dpi_rgb.cpp | 20 +++++++++++++++++++ esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index 655b469b91..ba09171649 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -84,6 +84,26 @@ void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uin ESP_LOGE(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err)); } +int RpiDpiRgb::get_width() { + switch (this->rotation_) { + case display::DISPLAY_ROTATION_90_DEGREES: + case display::DISPLAY_ROTATION_270_DEGREES: + return this->get_height_internal(); + default: + return this->get_width_internal(); + } +} + +int RpiDpiRgb::get_height() { + switch (this->rotation_) { + case display::DISPLAY_ROTATION_90_DEGREES: + case display::DISPLAY_ROTATION_270_DEGREES: + return this->get_width_internal(); + default: + return this->get_height_internal(); + } +} + void RpiDpiRgb::draw_pixel_at(int x, int y, Color color) { if (!this->get_clipping().inside(x, y)) return; // NOLINT diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h index 10f77a2624..7525040cd1 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h @@ -24,6 +24,7 @@ class RpiDpiRgb : public display::Display { void update() override { this->do_update_(); } void setup() override; void loop() override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; void draw_pixel_at(int x, int y, Color color) override; @@ -44,8 +45,8 @@ class RpiDpiRgb : public display::Display { this->width_ = width; this->height_ = height; } - int get_width() override { return this->width_; } - int get_height() override { return this->height_; } + int get_width() override; + int get_height() override; void set_hsync_back_porch(uint16_t hsync_back_porch) { this->hsync_back_porch_ = hsync_back_porch; } void set_hsync_front_porch(uint16_t hsync_front_porch) { this->hsync_front_porch_ = hsync_front_porch; } void set_hsync_pulse_width(uint16_t hsync_pulse_width) { this->hsync_pulse_width_ = hsync_pulse_width; } From 22f30d42a668e89b64318b1d8bf6c5cde38e2b21 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:05:51 +1100 Subject: [PATCH 0548/1052] [lvgl] Implement qrcode (#7623) --- esphome/components/lvgl/__init__.py | 2 + esphome/components/lvgl/widgets/qrcode.py | 54 +++++++++++++++++++++++ tests/components/lvgl/lvgl-package.yaml | 12 +++++ 3 files changed, 68 insertions(+) create mode 100644 esphome/components/lvgl/widgets/qrcode.py diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 215fdecdb5..4a1a26cc0b 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -71,6 +71,7 @@ from .widgets.meter import meter_spec from .widgets.msgbox import MSGBOX_SCHEMA, msgboxes_to_code from .widgets.obj import obj_spec from .widgets.page import add_pages, generate_page_triggers, page_spec +from .widgets.qrcode import qr_code_spec from .widgets.roller import roller_spec from .widgets.slider import slider_spec from .widgets.spinbox import spinbox_spec @@ -109,6 +110,7 @@ for w_type in ( spinbox_spec, keyboard_spec, tileview_spec, + qr_code_spec, ): WIDGET_TYPES[w_type.name] = w_type diff --git a/esphome/components/lvgl/widgets/qrcode.py b/esphome/components/lvgl/widgets/qrcode.py new file mode 100644 index 0000000000..742b538938 --- /dev/null +++ b/esphome/components/lvgl/widgets/qrcode.py @@ -0,0 +1,54 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_SIZE, CONF_TEXT +from esphome.cpp_generator import MockObjClass + +from ..defines import CONF_MAIN, literal +from ..lv_validation import color, color_retmapper, lv_text +from ..lvcode import LocalVariable, lv, lv_expr +from ..schemas import TEXT_SCHEMA +from ..types import WidgetType, lv_obj_t +from . import Widget + +CONF_QRCODE = "qrcode" +CONF_DARK_COLOR = "dark_color" +CONF_LIGHT_COLOR = "light_color" + +QRCODE_SCHEMA = TEXT_SCHEMA.extend( + { + cv.Optional(CONF_DARK_COLOR, default="black"): color, + cv.Optional(CONF_LIGHT_COLOR, default="white"): color, + cv.Required(CONF_SIZE): cv.int_, + } +) + + +class QrCodeType(WidgetType): + def __init__(self): + super().__init__( + CONF_QRCODE, + lv_obj_t, + (CONF_MAIN,), + QRCODE_SCHEMA, + modify_schema=TEXT_SCHEMA, + ) + + def get_uses(self): + return ("canvas", "img") + + def obj_creator(self, parent: MockObjClass, config: dict): + dark_color = color_retmapper(config[CONF_DARK_COLOR]) + light_color = color_retmapper(config[CONF_LIGHT_COLOR]) + size = config[CONF_SIZE] + return lv_expr.call("qrcode_create", parent, size, dark_color, light_color) + + async def to_code(self, w: Widget, config): + if (value := config.get(CONF_TEXT)) is not None: + value = await lv_text.process(value) + with LocalVariable( + "qr_text", cg.const_char_ptr, value, modifier="" + ) as str_obj: + lv.qrcode_update(w.obj, str_obj, literal(f"strlen({str_obj})")) + + +qr_code_spec = QrCodeType() diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 4962a71596..9bfbb5fc95 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -458,6 +458,18 @@ lvgl: - id: page2 widgets: + - qrcode: + id: lv_qr + align: left_mid + size: 100 + light_color: whitesmoke + dark_color: steelblue + text: esphome.io + on_click: + lvgl.qrcode.update: + id: lv_qr + text: homeassistant.io + - slider: min_value: 0 max_value: 255 From 858d97ccefff68f92af1c8f722738424ff2db791 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:08:29 +1100 Subject: [PATCH 0549/1052] [bytebuffer] Rework ByteBuffer using templates (#7638) --- CODEOWNERS | 1 + esphome/components/bytebuffer/__init__.py | 5 + esphome/components/bytebuffer/bytebuffer.h | 421 ++++++++++++++++++ esphome/core/bytebuffer.cpp | 167 ------- esphome/core/bytebuffer.h | 144 ------ tests/components/bytebuffer/common.yaml | 161 +++++++ .../components/bytebuffer/test.esp32-ard.yaml | 1 + .../bytebuffer/test.esp32-c3-ard.yaml | 1 + .../bytebuffer/test.esp32-c3-idf.yaml | 1 + .../components/bytebuffer/test.esp32-idf.yaml | 1 + .../bytebuffer/test.esp8266-ard.yaml | 1 + tests/components/bytebuffer/test.host.yaml | 1 + .../bytebuffer/test.rp2040-ard.yaml | 1 + 13 files changed, 595 insertions(+), 311 deletions(-) create mode 100644 esphome/components/bytebuffer/__init__.py create mode 100644 esphome/components/bytebuffer/bytebuffer.h delete mode 100644 esphome/core/bytebuffer.cpp delete mode 100644 esphome/core/bytebuffer.h create mode 100644 tests/components/bytebuffer/common.yaml create mode 100644 tests/components/bytebuffer/test.esp32-ard.yaml create mode 100644 tests/components/bytebuffer/test.esp32-c3-ard.yaml create mode 100644 tests/components/bytebuffer/test.esp32-c3-idf.yaml create mode 100644 tests/components/bytebuffer/test.esp32-idf.yaml create mode 100644 tests/components/bytebuffer/test.esp8266-ard.yaml create mode 100644 tests/components/bytebuffer/test.host.yaml create mode 100644 tests/components/bytebuffer/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index f96e43d5b7..5eb1f863f2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -85,6 +85,7 @@ esphome/components/bmp581/* @kahrendt esphome/components/bp1658cj/* @Cossid esphome/components/bp5758d/* @Cossid esphome/components/button/* @esphome/core +esphome/components/bytebuffer/* @clydebarrow esphome/components/canbus/* @danielschramm @mvturnho esphome/components/cap1188/* @mreditor97 esphome/components/captive_portal/* @OttoWinter diff --git a/esphome/components/bytebuffer/__init__.py b/esphome/components/bytebuffer/__init__.py new file mode 100644 index 0000000000..3c7c695118 --- /dev/null +++ b/esphome/components/bytebuffer/__init__.py @@ -0,0 +1,5 @@ +CODEOWNERS = ["@clydebarrow"] + +# Allows bytebuffer to be configured in yaml, to allow use of the C++ api. + +CONFIG_SCHEMA = {} diff --git a/esphome/components/bytebuffer/bytebuffer.h b/esphome/components/bytebuffer/bytebuffer.h new file mode 100644 index 0000000000..030484ce32 --- /dev/null +++ b/esphome/components/bytebuffer/bytebuffer.h @@ -0,0 +1,421 @@ +#pragma once + +#include +#include +#include +#include +#include "esphome/core/helpers.h" + +namespace esphome { +namespace bytebuffer { + +enum Endian { LITTLE, BIG }; + +/** + * A class modelled on the Java ByteBuffer class. It wraps a vector of bytes and permits putting and getting + * items of various sizes, with an automatically incremented position. + * + * There are three variables maintained pointing into the buffer: + * + * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed + * limit: the limit of the data currently available to get or put + * position: the current insert or extract position + * + * 0 <= position <= limit <= capacity + * + * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore + * the position to the mark. + * + * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. + * + * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading + * data from a buffer after it has been written. + * + * The code is defined here in the header file rather than in a .cpp file, so that it does not get compiled if not used. + * The templated functions ensure that only those typed functions actually used are compiled. The functions + * are implicitly inline-able which will aid performance. + */ +class ByteBuffer { + public: + // Default constructor (compatibility with TEMPLATABLE_VALUE) + // Creates a zero-length ByteBuffer which is little use to anybody. + ByteBuffer() : ByteBuffer(std::vector()) {} + + /** + * Create a new Bytebuffer with the given capacity + */ + ByteBuffer(size_t capacity, Endian endianness = LITTLE) + : data_(std::vector(capacity)), endianness_(endianness), limit_(capacity){}; + + // templated functions to implement putting and getting data of various types. There are two flavours of all + // functions - one that uses the position as the offset, and updates the position accordingly, and one that + // takes an explicit offset and does not update the position. + // Separate temnplates are provided for types that fit into 32 bits and those that are bigger. These delegate + // the actual put/get to common code based around those sizes. + // This reduces the code size and execution time for smaller types. A similar structure for e.g. 16 bits is unlikely + // to provide any further benefit given that all target platforms are native 32 bit. + + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + // integral types that fit into 32 bit + return static_cast(this->get_uint32_(sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + return static_cast(this->get_uint32_(offset, sizeof(T))); + } + + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(static_cast(value), sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(static_cast(value), offset, sizeof(T)); + } + + // integral types that do not fit into 32 bit (basically only 64 bit types) + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return static_cast(this->get_uint64_(sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return static_cast(this->get_uint64_(offset, sizeof(T))); + } + + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(value, sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(static_cast(value), offset, sizeof(T)); + } + + // floating point types. Caters for 32 and 64 bit floating point. + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint32_t)), T>::type * = 0) { + return bit_cast(this->get_uint32_(sizeof(T))); + } + + template + T get(typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return bit_cast(this->get_uint64_(sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint32_t)), T>::type * = 0) { + return bit_cast(this->get_uint32_(offset, sizeof(T))); + } + + template + T get(size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + return bit_cast(this->get_uint64_(offset, sizeof(T))); + } + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(bit_cast(value), sizeof(T)); + } + + template + void put(const T &value, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(bit_cast(value), sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) <= sizeof(uint32_t)), T>::type * = 0) { + this->put_uint32_(bit_cast(value), offset, sizeof(T)); + } + + template + void put(const T &value, size_t offset, typename std::enable_if::value, T>::type * = 0, + typename std::enable_if<(sizeof(T) == sizeof(uint64_t)), T>::type * = 0) { + this->put_uint64_(bit_cast(value), offset, sizeof(T)); + } + + template static ByteBuffer wrap(T value, Endian endianness = LITTLE) { + ByteBuffer buffer = ByteBuffer(sizeof(T), endianness); + buffer.put(value); + buffer.flip(); + return buffer; + } + + static ByteBuffer wrap(std::vector const &data, Endian endianness = LITTLE) { + ByteBuffer buffer = {data}; + buffer.endianness_ = endianness; + return buffer; + } + + static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE) { + return wrap(std::vector(ptr, ptr + len), endianness); + } + + // convenience functions with explicit types named.. + void put_float(float value) { this->put(value); } + void put_double(double value) { this->put(value); } + + uint8_t get_uint8() { return this->data_[this->position_++]; } + // Get a 16 bit unsigned value, increment by 2 + uint16_t get_uint16() { return this->get(); } + // Get a 24 bit unsigned value, increment by 3 + uint32_t get_uint24() { return this->get_uint32_(3); }; + // Get a 32 bit unsigned value, increment by 4 + uint32_t get_uint32() { return this->get(); }; + // Get a 64 bit unsigned value, increment by 8 + uint64_t get_uint64() { return this->get(); }; + // Signed versions of the get functions + uint8_t get_int8() { return static_cast(this->get_uint8()); }; + int16_t get_int16() { return this->get(); } + int32_t get_int32() { return this->get(); } + int64_t get_int64() { return this->get(); } + // Get a float value, increment by 4 + float get_float() { return this->get(); } + // Get a double value, increment by 8 + double get_double() { return this->get(); } + + // Get a bool value, increment by 1 + bool get_bool() { return static_cast(this->get_uint8()); } + + uint32_t get_int24(size_t offset) { + auto value = this->get_uint24(offset); + uint32_t mask = (~static_cast(0)) << 23; + if ((value & mask) != 0) + value |= mask; + return value; + } + + uint32_t get_int24() { + auto value = this->get_uint24(); + uint32_t mask = (~static_cast(0)) << 23; + if ((value & mask) != 0) + value |= mask; + return value; + } + std::vector get_vector(size_t length, size_t offset) { + auto start = this->data_.begin() + offset; + return {start, start + length}; + } + + std::vector get_vector(size_t length) { + auto result = this->get_vector(length, this->position_); + this->position_ += length; + return result; + } + + // Convenience named functions + void put_uint8(uint8_t value) { this->data_[this->position_++] = value; } + void put_uint16(uint16_t value) { this->put(value); } + void put_uint24(uint32_t value) { this->put_uint32_(value, 3); } + void put_uint32(uint32_t value) { this->put(value); } + void put_uint64(uint64_t value) { this->put(value); } + // Signed versions of the put functions + void put_int8(int8_t value) { this->put_uint8(static_cast(value)); } + void put_int16(int16_t value) { this->put(value); } + void put_int24(int32_t value) { this->put_uint32_(value, 3); } + void put_int32(int32_t value) { this->put(value); } + void put_int64(int64_t value) { this->put(value); } + // Extra put functions + void put_bool(bool value) { this->put_uint8(value); } + + // versions of the above with an offset, these do not update the position + + uint64_t get_uint64(size_t offset) { return this->get(offset); } + uint32_t get_uint24(size_t offset) { return this->get_uint32_(offset, 3); }; + double get_double(size_t offset) { return get(offset); } + + // Get one byte from the buffer, increment position by 1 + uint8_t get_uint8(size_t offset) { return this->data_[offset]; } + // Get a 16 bit unsigned value, increment by 2 + uint16_t get_uint16(size_t offset) { return get(offset); } + // Get a 24 bit unsigned value, increment by 3 + uint32_t get_uint32(size_t offset) { return this->get(offset); }; + // Get a 64 bit unsigned value, increment by 8 + uint8_t get_int8(size_t offset) { return get(offset); } + int16_t get_int16(size_t offset) { return get(offset); } + int32_t get_int32(size_t offset) { return get(offset); } + int64_t get_int64(size_t offset) { return get(offset); } + // Get a float value, increment by 4 + float get_float(size_t offset) { return get(offset); } + // Get a double value, increment by 8 + + // Get a bool value, increment by 1 + bool get_bool(size_t offset) { return this->get_uint8(offset); } + + void put_uint8(uint8_t value, size_t offset) { this->data_[offset] = value; } + void put_uint16(uint16_t value, size_t offset) { this->put(value, offset); } + void put_uint24(uint32_t value, size_t offset) { this->put(value, offset); } + void put_uint32(uint32_t value, size_t offset) { this->put(value, offset); } + void put_uint64(uint64_t value, size_t offset) { this->put(value, offset); } + // Signed versions of the put functions + void put_int8(int8_t value, size_t offset) { this->put_uint8(static_cast(value), offset); } + void put_int16(int16_t value, size_t offset) { this->put(value, offset); } + void put_int24(int32_t value, size_t offset) { this->put_uint32_(value, offset, 3); } + void put_int32(int32_t value, size_t offset) { this->put(value, offset); } + void put_int64(int64_t value, size_t offset) { this->put(value, offset); } + // Extra put functions + void put_float(float value, size_t offset) { this->put(value, offset); } + void put_double(double value, size_t offset) { this->put(value, offset); } + void put_bool(bool value, size_t offset) { this->put_uint8(value, offset); } + void put(const std::vector &value, size_t offset) { + std::copy(value.begin(), value.end(), this->data_.begin() + offset); + } + void put_vector(const std::vector &value, size_t offset) { this->put(value, offset); } + void put(const std::vector &value) { + this->put_vector(value, this->position_); + this->position_ += value.size(); + } + void put_vector(const std::vector &value) { this->put(value); } + + // Getters + + inline size_t get_capacity() const { return this->data_.size(); } + inline size_t get_position() const { return this->position_; } + inline size_t get_limit() const { return this->limit_; } + inline size_t get_remaining() const { return this->get_limit() - this->get_position(); } + inline Endian get_endianness() const { return this->endianness_; } + inline void mark() { this->mark_ = this->position_; } + inline void big_endian() { this->endianness_ = BIG; } + inline void little_endian() { this->endianness_ = LITTLE; } + // retrieve a pointer to the underlying data. + std::vector get_data() { return this->data_; }; + + void get_bytes(void *dest, size_t length) { + std::copy(this->data_.begin() + this->position_, this->data_.begin() + this->position_ + length, (uint8_t *) dest); + this->position_ += length; + } + + void get_bytes(void *dest, size_t length, size_t offset) { + std::copy(this->data_.begin() + offset, this->data_.begin() + offset + length, (uint8_t *) dest); + } + + void rewind() { this->position_ = 0; } + void reset() { this->position_ = this->mark_; } + + void set_limit(size_t limit) { this->limit_ = limit; } + void set_position(size_t position) { this->position_ = position; } + void clear() { + this->limit_ = this->get_capacity(); + this->position_ = 0; + } + void flip() { + this->limit_ = this->position_; + this->position_ = 0; + } + + protected: + uint64_t get_uint64_(size_t offset, size_t length) const { + uint64_t value = 0; + if (this->endianness_ == LITTLE) { + offset += length; + while (length-- != 0) { + value <<= 8; + value |= this->data_[--offset]; + } + } else { + while (length-- != 0) { + value <<= 8; + value |= this->data_[offset++]; + } + } + return value; + } + + uint64_t get_uint64_(size_t length) { + auto result = this->get_uint64_(this->position_, length); + this->position_ += length; + return result; + } + uint32_t get_uint32_(size_t offset, size_t length) const { + uint32_t value = 0; + if (this->endianness_ == LITTLE) { + offset += length; + while (length-- != 0) { + value <<= 8; + value |= this->data_[--offset]; + } + } else { + while (length-- != 0) { + value <<= 8; + value |= this->data_[offset++]; + } + } + return value; + } + + uint32_t get_uint32_(size_t length) { + auto result = this->get_uint32_(this->position_, length); + this->position_ += length; + return result; + } + + /// Putters + + void put_uint64_(uint64_t value, size_t length) { + this->put_uint64_(value, this->position_, length); + this->position_ += length; + } + void put_uint32_(uint32_t value, size_t length) { + this->put_uint32_(value, this->position_, length); + this->position_ += length; + } + + void put_uint64_(uint64_t value, size_t offset, size_t length) { + if (this->endianness_ == LITTLE) { + while (length-- != 0) { + this->data_[offset++] = static_cast(value); + value >>= 8; + } + } else { + offset += length; + while (length-- != 0) { + this->data_[--offset] = static_cast(value); + value >>= 8; + } + } + } + + void put_uint32_(uint32_t value, size_t offset, size_t length) { + if (this->endianness_ == LITTLE) { + while (length-- != 0) { + this->data_[offset++] = static_cast(value); + value >>= 8; + } + } else { + offset += length; + while (length-- != 0) { + this->data_[--offset] = static_cast(value); + value >>= 8; + } + } + } + ByteBuffer(std::vector const &data) : data_(data), limit_(data.size()) {} + + std::vector data_; + Endian endianness_{LITTLE}; + size_t position_{0}; + size_t mark_{0}; + size_t limit_{0}; +}; + +} // namespace bytebuffer +} // namespace esphome diff --git a/esphome/core/bytebuffer.cpp b/esphome/core/bytebuffer.cpp deleted file mode 100644 index 9dd32bf87a..0000000000 --- a/esphome/core/bytebuffer.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "bytebuffer.h" -#include -#include "esphome/core/helpers.h" - -#include -#include - -namespace esphome { - -ByteBuffer ByteBuffer::wrap(const uint8_t *ptr, size_t len, Endian endianness) { - // there is a double copy happening here, could be optimized but at cost of clarity. - std::vector data(ptr, ptr + len); - ByteBuffer buffer = {data}; - buffer.endianness_ = endianness; - return buffer; -} - -ByteBuffer ByteBuffer::wrap(std::vector const &data, Endian endianness) { - ByteBuffer buffer = {data}; - buffer.endianness_ = endianness; - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint8_t value) { - ByteBuffer buffer = ByteBuffer(1); - buffer.put_uint8(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint16_t value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(2, endianness); - buffer.put_uint16(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint32_t value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(4, endianness); - buffer.put_uint32(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(uint64_t value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(8, endianness); - buffer.put_uint64(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(float value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(sizeof(float), endianness); - buffer.put_float(value); - buffer.flip(); - return buffer; -} - -ByteBuffer ByteBuffer::wrap(double value, Endian endianness) { - ByteBuffer buffer = ByteBuffer(sizeof(double), endianness); - buffer.put_double(value); - buffer.flip(); - return buffer; -} - -void ByteBuffer::set_limit(size_t limit) { - assert(limit <= this->get_capacity()); - this->limit_ = limit; -} -void ByteBuffer::set_position(size_t position) { - assert(position <= this->get_limit()); - this->position_ = position; -} -void ByteBuffer::clear() { - this->limit_ = this->get_capacity(); - this->position_ = 0; -} -void ByteBuffer::flip() { - this->limit_ = this->position_; - this->position_ = 0; -} - -/// Getters -uint8_t ByteBuffer::get_uint8() { - assert(this->get_remaining() >= 1); - return this->data_[this->position_++]; -} -uint64_t ByteBuffer::get_uint(size_t length) { - assert(this->get_remaining() >= length); - uint64_t value = 0; - if (this->endianness_ == LITTLE) { - this->position_ += length; - auto index = this->position_; - while (length-- != 0) { - value <<= 8; - value |= this->data_[--index]; - } - } else { - while (length-- != 0) { - value <<= 8; - value |= this->data_[this->position_++]; - } - } - return value; -} - -uint32_t ByteBuffer::get_int24() { - auto value = this->get_uint24(); - uint32_t mask = (~static_cast(0)) << 23; - if ((value & mask) != 0) - value |= mask; - return value; -} -float ByteBuffer::get_float() { - assert(this->get_remaining() >= sizeof(float)); - return bit_cast(this->get_uint32()); -} -double ByteBuffer::get_double() { - assert(this->get_remaining() >= sizeof(double)); - return bit_cast(this->get_uint64()); -} - -std::vector ByteBuffer::get_vector(size_t length) { - assert(this->get_remaining() >= length); - auto start = this->data_.begin() + this->position_; - this->position_ += length; - return {start, start + length}; -} - -/// Putters -void ByteBuffer::put_uint8(uint8_t value) { - assert(this->get_remaining() >= 1); - this->data_[this->position_++] = value; -} - -void ByteBuffer::put_uint(uint64_t value, size_t length) { - assert(this->get_remaining() >= length); - if (this->endianness_ == LITTLE) { - while (length-- != 0) { - this->data_[this->position_++] = static_cast(value); - value >>= 8; - } - } else { - this->position_ += length; - auto index = this->position_; - while (length-- != 0) { - this->data_[--index] = static_cast(value); - value >>= 8; - } - } -} -void ByteBuffer::put_float(float value) { - static_assert(sizeof(float) == sizeof(uint32_t), "Float sizes other than 32 bit not supported"); - assert(this->get_remaining() >= sizeof(float)); - this->put_uint32(bit_cast(value)); -} -void ByteBuffer::put_double(double value) { - static_assert(sizeof(double) == sizeof(uint64_t), "Double sizes other than 64 bit not supported"); - assert(this->get_remaining() >= sizeof(double)); - this->put_uint64(bit_cast(value)); -} -void ByteBuffer::put_vector(const std::vector &value) { - assert(this->get_remaining() >= value.size()); - std::copy(value.begin(), value.end(), this->data_.begin() + this->position_); - this->position_ += value.size(); -} -} // namespace esphome diff --git a/esphome/core/bytebuffer.h b/esphome/core/bytebuffer.h deleted file mode 100644 index d44d01f275..0000000000 --- a/esphome/core/bytebuffer.h +++ /dev/null @@ -1,144 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace esphome { - -enum Endian { LITTLE, BIG }; - -/** - * A class modelled on the Java ByteBuffer class. It wraps a vector of bytes and permits putting and getting - * items of various sizes, with an automatically incremented position. - * - * There are three variables maintained pointing into the buffer: - * - * capacity: the maximum amount of data that can be stored - set on construction and cannot be changed - * limit: the limit of the data currently available to get or put - * position: the current insert or extract position - * - * 0 <= position <= limit <= capacity - * - * In addition a mark can be set to the current position with mark(). A subsequent call to reset() will restore - * the position to the mark. - * - * The buffer can be marked to be little-endian (default) or big-endian. All subsequent operations will use that order. - * - * The flip() operation will reset the position to 0 and limit to the current position. This is useful for reading - * data from a buffer after it has been written. - * - */ -class ByteBuffer { - public: - // Default constructor (compatibility with TEMPLATABLE_VALUE) - ByteBuffer() : ByteBuffer(std::vector()) {} - /** - * Create a new Bytebuffer with the given capacity - */ - ByteBuffer(size_t capacity, Endian endianness = LITTLE) - : data_(std::vector(capacity)), endianness_(endianness), limit_(capacity){}; - /** - * Wrap an existing vector in a ByteBufffer - */ - static ByteBuffer wrap(std::vector const &data, Endian endianness = LITTLE); - /** - * Wrap an existing array in a ByteBuffer. Note that this will create a copy of the data. - */ - static ByteBuffer wrap(const uint8_t *ptr, size_t len, Endian endianness = LITTLE); - // Convenience functions to create a ByteBuffer from a value - static ByteBuffer wrap(uint8_t value); - static ByteBuffer wrap(uint16_t value, Endian endianness = LITTLE); - static ByteBuffer wrap(uint32_t value, Endian endianness = LITTLE); - static ByteBuffer wrap(uint64_t value, Endian endianness = LITTLE); - static ByteBuffer wrap(int8_t value) { return wrap(static_cast(value)); } - static ByteBuffer wrap(int16_t value, Endian endianness = LITTLE) { - return wrap(static_cast(value), endianness); - } - static ByteBuffer wrap(int32_t value, Endian endianness = LITTLE) { - return wrap(static_cast(value), endianness); - } - static ByteBuffer wrap(int64_t value, Endian endianness = LITTLE) { - return wrap(static_cast(value), endianness); - } - static ByteBuffer wrap(float value, Endian endianness = LITTLE); - static ByteBuffer wrap(double value, Endian endianness = LITTLE); - static ByteBuffer wrap(bool value) { return wrap(static_cast(value)); } - - // Get an integral value from the buffer, increment position by length - uint64_t get_uint(size_t length); - // Get one byte from the buffer, increment position by 1 - uint8_t get_uint8(); - // Get a 16 bit unsigned value, increment by 2 - uint16_t get_uint16() { return static_cast(this->get_uint(sizeof(uint16_t))); }; - // Get a 24 bit unsigned value, increment by 3 - uint32_t get_uint24() { return static_cast(this->get_uint(3)); }; - // Get a 32 bit unsigned value, increment by 4 - uint32_t get_uint32() { return static_cast(this->get_uint(sizeof(uint32_t))); }; - // Get a 64 bit unsigned value, increment by 8 - uint64_t get_uint64() { return this->get_uint(sizeof(uint64_t)); }; - // Signed versions of the get functions - uint8_t get_int8() { return static_cast(this->get_uint8()); }; - int16_t get_int16() { return static_cast(this->get_uint(sizeof(int16_t))); } - uint32_t get_int24(); - int32_t get_int32() { return static_cast(this->get_uint(sizeof(int32_t))); } - int64_t get_int64() { return static_cast(this->get_uint(sizeof(int64_t))); } - // Get a float value, increment by 4 - float get_float(); - // Get a double value, increment by 8 - double get_double(); - // Get a bool value, increment by 1 - bool get_bool() { return this->get_uint8(); } - // Get vector of bytes, increment by length - std::vector get_vector(size_t length); - - // Put values into the buffer, increment the position accordingly - // put any integral value, length represents the number of bytes - void put_uint(uint64_t value, size_t length); - void put_uint8(uint8_t value); - void put_uint16(uint16_t value) { this->put_uint(value, sizeof(uint16_t)); } - void put_uint24(uint32_t value) { this->put_uint(value, 3); } - void put_uint32(uint32_t value) { this->put_uint(value, sizeof(uint32_t)); } - void put_uint64(uint64_t value) { this->put_uint(value, sizeof(uint64_t)); } - // Signed versions of the put functions - void put_int8(int8_t value) { this->put_uint8(static_cast(value)); } - void put_int16(int32_t value) { this->put_uint(static_cast(value), sizeof(uint16_t)); } - void put_int24(int32_t value) { this->put_uint(static_cast(value), 3); } - void put_int32(int32_t value) { this->put_uint(static_cast(value), sizeof(uint32_t)); } - void put_int64(int64_t value) { this->put_uint(static_cast(value), sizeof(uint64_t)); } - // Extra put functions - void put_float(float value); - void put_double(double value); - void put_bool(bool value) { this->put_uint8(value); } - void put_vector(const std::vector &value); - - inline size_t get_capacity() const { return this->data_.size(); } - inline size_t get_position() const { return this->position_; } - inline size_t get_limit() const { return this->limit_; } - inline size_t get_remaining() const { return this->get_limit() - this->get_position(); } - inline Endian get_endianness() const { return this->endianness_; } - inline void mark() { this->mark_ = this->position_; } - inline void big_endian() { this->endianness_ = BIG; } - inline void little_endian() { this->endianness_ = LITTLE; } - void set_limit(size_t limit); - void set_position(size_t position); - // set position to 0, limit to capacity. - void clear(); - // set limit to current position, postition to zero. Used when swapping from write to read operations. - void flip(); - // retrieve a pointer to the underlying data. - std::vector get_data() { return this->data_; }; - void rewind() { this->position_ = 0; } - void reset() { this->position_ = this->mark_; } - - protected: - ByteBuffer(std::vector const &data) : data_(data), limit_(data.size()) {} - std::vector data_; - Endian endianness_{LITTLE}; - size_t position_{0}; - size_t mark_{0}; - size_t limit_{0}; -}; - -} // namespace esphome diff --git a/tests/components/bytebuffer/common.yaml b/tests/components/bytebuffer/common.yaml new file mode 100644 index 0000000000..177f487e1e --- /dev/null +++ b/tests/components/bytebuffer/common.yaml @@ -0,0 +1,161 @@ +bytebuffer: + +esphome: + on_boot: + - lambda: |- + using namespace bytebuffer; + auto buf = ByteBuffer(16); + assert(buf.get_endianness() == LITTLE); + assert(buf.get_remaining() == 16); + buf.set_limit(10); + assert(buf.get_capacity() == 16); + buf.put_uint8(1); + assert(buf.get_remaining() == 9); + buf.put_uint16(0xABCD); + auto da = buf.get_data(); + assert(buf.get_uint8(0) == 1); + auto x = buf.get_uint16(1); + assert(buf.get_uint16(1) == 0xABCD); + assert(buf.get_remaining() == 7); + buf.put_uint32(0x12345678UL); + assert(buf.get_uint32(3) == 0x12345678UL); + assert(buf.get_remaining() == 3); + assert(buf.get_data()[1] == 0xCD); + assert(buf.get_data()[2] == 0xAB); + assert(buf.get_data()[3] == 0x78); + assert(buf.get_data()[4] == 0x56); + assert(buf.get_data()[5] == 0x34); + assert(buf.get_data()[6] == 0x12); + buf.flip(); + assert(buf.get_capacity() == 16); + assert(buf.get_uint32(3) == 0x12345678UL); + assert(buf.get_uint8(0) == 1); + assert(buf.get_uint16(1) == 0xABCD); + buf.put_uint16(0x1234, 1); + assert(buf.get_uint16(1) == 0x1234); + assert(buf.get_remaining() == 7); + assert(buf.get_uint8() == 1); + assert(buf.get_uint16() == 0x1234); + assert(buf.get_uint32() == 0x12345678ul); + assert(buf.get_remaining() == 0); + assert(buf.get_remaining() == 0); + buf.rewind(); + buf.big_endian(); + assert(buf.get_remaining() == 7); + assert(buf.get_uint8() == 1); + assert(buf.get_uint16() == 0x3412); + buf.mark(); + assert(buf.get_uint32() == 0x78563412ul); + assert(buf.get_remaining() == 0); + buf.reset(); + assert(buf.get_remaining() == 4); + assert(buf.get_uint32() == 0x78563412ul); + auto buf1 = ByteBuffer::wrap(buf.get_data().data(), buf.get_limit()); + buf.clear(); + assert(buf.get_position() == 0); + assert(buf.get_capacity() == 16); + assert(buf.get_limit() == 16); + assert(buf1.get_remaining() == 7); + assert(buf1.get_capacity() == 7); + buf1.set_position(3); + assert(buf1.get_uint32() == 0x12345678ul); + buf1.clear(); + assert(buf1.get_limit() == 7); + assert(buf1.get_capacity() == 7); + assert(buf1.get_position() == 0); + float f = 1.2345; + buf1.put_float(f); + buf1.flip(); + assert(buf1.get_remaining() == 4); + assert(buf1.get_float() == f); + buf1.clear(); + buf1.put_uint16(-32760); + buf1.put_uint24(-302760); + buf1.flip(); + assert(buf1.get_int16() == -32760); + assert(buf1.get_int24() == -302760); + uint8_t arr[4] = {0x10, 0x20, 0x30, 0x40}; + buf1 = ByteBuffer::wrap(arr, 4); + assert(buf1.get_capacity() == 4); + assert(buf1.get_limit() == 4); + assert(buf1.get_position() == 0); + assert(buf1.get_uint32() == 0x40302010UL); + assert(buf1.get_position() == 4); + assert(buf1.get_remaining() == 0); + std::vector vec{}; + vec.push_back(0x10); + vec.push_back(0x20); + vec.push_back(0x30); + vec.push_back(0x40); + buf1 = ByteBuffer::wrap(vec); + assert(buf1.get_capacity() == 4); + assert(buf1.get_limit() == 4); + assert(buf1.get_position() == 0); + buf1.mark(); + buf1.reset(); + assert(buf1.get_uint32() == 0x40302010UL); + buf = ByteBuffer::wrap(true); + assert(buf.get_bool() == true); + buf = ByteBuffer::wrap((uint8_t)0xFE); + assert(buf.get_uint8() == 0xFE); + buf = ByteBuffer::wrap((uint16_t)0xA5A6, BIG); + assert(buf.get_remaining() == 2); + assert(buf.get_position() == 0); + assert(buf.get_capacity() == 2); + assert(buf.get_endianness() == BIG); + assert(buf.get_data()[0] == 0xA5); + assert(buf.get_uint16() == 0xA5A6); + buf.flip(); + buf.little_endian(); + assert(buf.get_uint16() == 0xA6A5); + buf = ByteBuffer::wrap(f, BIG); + assert(buf.get_float() == f); + double d = 1.2345678E7; + buf = ByteBuffer::wrap(d, BIG); + assert(buf.get_double() == d); + buf = ByteBuffer::wrap({1, 2, 3, 4}, BIG); + assert(buf.get_endianness() == BIG); + assert(buf.get_remaining() == 4); + assert(buf.get_data()[2] == 3); + buf.little_endian(); + assert(buf.get_data()[2] == 3); + assert(buf.get_uint16() == 0x0201); + buf.big_endian(); + assert(buf.get_uint16() == 0x0304); + buf.rewind(); + vec = buf.get_vector(3); + assert(buf.get_remaining() == 1); + assert(vec[0] == 1); + assert(vec.size() == 3); + buf = ByteBuffer(10); + buf.put_vector(vec); + assert(buf.get_remaining() == 7); + buf.flip(); + assert(buf.get_remaining() == 3); + assert(buf.get_uint24() == 0x030201); + buf = ByteBuffer(64); + buf.put_uint8(1, 1); + buf.put_uint16(16, 2); + buf.put_uint32(1232, 4); + buf.put_uint64(123432ul, 8); + buf.put_float(1.2f, 16); + buf.put_int24(0x678, 20); + + assert(buf.get_uint8(1) == 1); + assert(buf.get(1) == 1); + assert(buf.get_uint16(2) == 16); + assert(buf.get(2) == 16); + assert(buf.get_uint32(4) == 1232); + assert(buf.get(4) == 1232); + assert(buf.get_uint64(8) == 123432ul); + assert(buf.get(8) == 123432ul); + assert(buf.get_float(16) == 1.2f); + assert(buf.get(16) == 1.2f); + assert(buf.get_int24(20) == 0x678); + buf.clear(); + buf.put(1.234, 10); + double dx = buf.get(10); + assert(dx == 1.234); + buf.put((uint16_t)1, 10); + assert(buf.get_uint16(10) == 1); + ESP_LOGD("bytebuffer", "******************** All tests succeeded"); diff --git a/tests/components/bytebuffer/test.esp32-ard.yaml b/tests/components/bytebuffer/test.esp32-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-ard.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-c3-ard.yaml b/tests/components/bytebuffer/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-c3-ard.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-c3-idf.yaml b/tests/components/bytebuffer/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-c3-idf.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp32-idf.yaml b/tests/components/bytebuffer/test.esp32-idf.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp32-idf.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.esp8266-ard.yaml b/tests/components/bytebuffer/test.esp8266-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.esp8266-ard.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.host.yaml b/tests/components/bytebuffer/test.host.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.host.yaml @@ -0,0 +1 @@ +!include common.yaml diff --git a/tests/components/bytebuffer/test.rp2040-ard.yaml b/tests/components/bytebuffer/test.rp2040-ard.yaml new file mode 100644 index 0000000000..380ca87628 --- /dev/null +++ b/tests/components/bytebuffer/test.rp2040-ard.yaml @@ -0,0 +1 @@ +!include common.yaml From 88627095fbc049ca342325be22d72e66e9b9c28a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:12:32 +1100 Subject: [PATCH 0550/1052] [http_request] Always return defined server response status (#7689) --- esphome/components/http_request/http_request_arduino.cpp | 3 +-- esphome/components/http_request/http_request_idf.cpp | 3 +-- esphome/components/http_request/ota/ota_http_request.cpp | 2 +- esphome/components/http_request/update/http_request_update.cpp | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index 2148d92ad2..f000082034 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -116,8 +116,7 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s if (container->status_code < 200 || container->status_code >= 300) { ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code); this->status_momentary_error("failed", 1000); - container->end(); - return nullptr; + // Still return the container, so it can be used to get the status code and error message } int content_length = container->client_.getSize(); diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 3819f5544e..e47e1d488e 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -172,8 +172,7 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code); this->status_momentary_error("failed", 1000); - esp_http_client_cleanup(client); - return nullptr; + return container; } int HttpContainerIDF::read(uint8_t *buf, size_t max_len) { diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index 1553de0bc1..f7c941da18 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -106,7 +106,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() { auto container = this->parent_->get(url_with_auth); - if (container == nullptr) { + if (container == nullptr || container->status_code != 200) { return OTA_CONNECTION_ERROR; } diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 059148e7e5..4d913f2158 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -31,7 +31,7 @@ void HttpRequestUpdate::setup() { void HttpRequestUpdate::update() { auto container = this->request_parent_->get(this->source_url_); - if (container == nullptr) { + if (container == nullptr || container->status_code != 200) { std::string msg = str_sprintf("Failed to fetch manifest from %s", this->source_url_.c_str()); this->status_set_error(msg.c_str()); return; From 63e4d4b493ec6c704ac6d41273b4190614726578 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 29 Oct 2024 13:56:32 +1100 Subject: [PATCH 0551/1052] [font] Fix failure with bitmap fonts (#7691) --- esphome/components/font/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index dacd0779b1..a3f11df50e 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -344,7 +344,7 @@ class TrueTypeFontWrapper: return offset_x, offset_y def getmask(self, glyph, **kwargs): - return self.font.getmask(glyph, **kwargs) + return self.font.getmask(str(glyph), **kwargs) def getmetrics(self, glyphs): return self.font.getmetrics() @@ -359,7 +359,7 @@ class BitmapFontWrapper: return 0, 0 def getmask(self, glyph, **kwargs): - return self.font.getmask(glyph, **kwargs) + return self.font.getmask(str(glyph), **kwargs) def getmetrics(self, glyphs): max_height = 0 From df750d0d11ce31e793de06d0e00663c8e23a3680 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:05:58 +1100 Subject: [PATCH 0552/1052] [http_request] Add enum for status codes (#7690) --- .../components/http_request/http_request.h | 57 +++++++++++++++++++ .../http_request/http_request_arduino.cpp | 2 +- .../http_request/http_request_idf.cpp | 17 ++---- .../http_request/ota/ota_http_request.cpp | 2 +- .../update/http_request_update.cpp | 2 +- 5 files changed, 65 insertions(+), 15 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index c01baf8644..d87d9b8a45 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -22,6 +22,63 @@ struct Header { const char *value; }; +// Some common HTTP status codes +enum HttpStatus { + HTTP_STATUS_OK = 200, + HTTP_STATUS_NO_CONTENT = 204, + HTTP_STATUS_PARTIAL_CONTENT = 206, + + /* 3xx - Redirection */ + HTTP_STATUS_MULTIPLE_CHOICES = 300, + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + HTTP_STATUS_NOT_MODIFIED = 304, + HTTP_STATUS_TEMPORARY_REDIRECT = 307, + HTTP_STATUS_PERMANENT_REDIRECT = 308, + + /* 4XX - CLIENT ERROR */ + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED = 401, + HTTP_STATUS_FORBIDDEN = 403, + HTTP_STATUS_NOT_FOUND = 404, + HTTP_STATUS_METHOD_NOT_ALLOWED = 405, + HTTP_STATUS_NOT_ACCEPTABLE = 406, + HTTP_STATUS_LENGTH_REQUIRED = 411, + + /* 5xx - Server Error */ + HTTP_STATUS_INTERNAL_ERROR = 500 +}; + +/** + * @brief Returns true if the HTTP status code is a redirect. + * + * @param status the HTTP status code to check + * @return true if the status code is a redirect, false otherwise + */ +inline bool is_redirect(int const status) { + switch (status) { + case HTTP_STATUS_MOVED_PERMANENTLY: + case HTTP_STATUS_FOUND: + case HTTP_STATUS_SEE_OTHER: + case HTTP_STATUS_TEMPORARY_REDIRECT: + case HTTP_STATUS_PERMANENT_REDIRECT: + return true; + default: + return false; + } +} + +/** + * @brief Checks if the given HTTP status code indicates a successful request. + * + * A successful request is one where the status code is in the range 200-299 + * + * @param status the HTTP status code to check + * @return true if the status code indicates a successful request, false otherwise + */ +inline bool is_success(int const status) { return status >= HTTP_STATUS_OK && status < HTTP_STATUS_MULTIPLE_CHOICES; } + class HttpRequestComponent; class HttpContainer : public Parented { diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index f000082034..af1eb6f459 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -113,7 +113,7 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s return nullptr; } - if (container->status_code < 200 || container->status_code >= 300) { + if (!is_success(container->status_code)) { ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code); this->status_momentary_error("failed", 1000); // Still return the container, so it can be used to get the status code and error message diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index e47e1d488e..c6c567b620 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -6,7 +6,6 @@ #include "esphome/components/watchdog/watchdog.h" #include "esphome/core/application.h" -#include "esphome/core/defines.h" #include "esphome/core/log.h" #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE @@ -118,20 +117,14 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } - auto is_ok = [](int code) { return code >= HttpStatus_Ok && code < HttpStatus_MultipleChoices; }; - container->content_length = esp_http_client_fetch_headers(client); container->status_code = esp_http_client_get_status_code(client); - if (is_ok(container->status_code)) { + if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; } if (this->follow_redirects_) { - auto is_redirect = [](int code) { - return code == HttpStatus_MovedPermanently || code == HttpStatus_Found || code == HttpStatus_SeeOther || - code == HttpStatus_TemporaryRedirect || code == HttpStatus_PermanentRedirect; - }; auto num_redirects = this->redirect_limit_; while (is_redirect(container->status_code) && num_redirects > 0) { err = esp_http_client_set_redirection(client); @@ -142,9 +135,9 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE - char url[256]{}; - if (esp_http_client_get_url(client, url, sizeof(url) - 1) == ESP_OK) { - ESP_LOGV(TAG, "redirecting to url: %s", url); + char redirect_url[256]{}; + if (esp_http_client_get_url(client, redirect_url, sizeof(redirect_url) - 1) == ESP_OK) { + ESP_LOGV(TAG, "redirecting to url: %s", redirect_url); } #endif err = esp_http_client_open(client, 0); @@ -157,7 +150,7 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin container->content_length = esp_http_client_fetch_headers(client); container->status_code = esp_http_client_get_status_code(client); - if (is_ok(container->status_code)) { + if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; } diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index f7c941da18..cec30d72ec 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -106,7 +106,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() { auto container = this->parent_->get(url_with_auth); - if (container == nullptr || container->status_code != 200) { + if (container == nullptr || container->status_code != HTTP_STATUS_OK) { return OTA_CONNECTION_ERROR; } diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 4d913f2158..0e0966c22b 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -31,7 +31,7 @@ void HttpRequestUpdate::setup() { void HttpRequestUpdate::update() { auto container = this->request_parent_->get(this->source_url_); - if (container == nullptr || container->status_code != 200) { + if (container == nullptr || container->status_code != HTTP_STATUS_OK) { std::string msg = str_sprintf("Failed to fetch manifest from %s", this->source_url_.c_str()); this->status_set_error(msg.c_str()); return; From 302ba2874e12960c76a624fa6066bf5df1ffac2e Mon Sep 17 00:00:00 2001 From: Satoshi YAMADA Date: Tue, 29 Oct 2024 12:08:08 +0900 Subject: [PATCH 0553/1052] Support W5500 SPI-Ethernet polling mode if framework is supported (#7503) --- esphome/components/ethernet/__init__.py | 56 ++++++++++++++++++- .../ethernet/ethernet_component.cpp | 15 ++++- .../components/ethernet/ethernet_component.h | 8 ++- esphome/const.py | 1 + 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 475d60df53..dca37b8dc2 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -1,3 +1,4 @@ +import logging from esphome import pins import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant @@ -23,6 +24,7 @@ from esphome.const import ( CONF_MISO_PIN, CONF_MOSI_PIN, CONF_PAGE_ID, + CONF_POLLING_INTERVAL, CONF_RESET_PIN, CONF_SPI, CONF_STATIC_IP, @@ -30,13 +32,16 @@ from esphome.const import ( CONF_TYPE, CONF_USE_ADDRESS, CONF_VALUE, + KEY_CORE, + KEY_FRAMEWORK_VERSION, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import CORE, TimePeriodMilliseconds, coroutine_with_priority import esphome.final_validate as fv CONFLICTS_WITH = ["wifi"] DEPENDENCIES = ["esp32"] AUTO_LOAD = ["network"] +LOGGER = logging.getLogger(__name__) ethernet_ns = cg.esphome_ns.namespace("ethernet") PHYRegister = ethernet_ns.struct("PHYRegister") @@ -63,6 +68,7 @@ ETHERNET_TYPES = { } SPI_ETHERNET_TYPES = ["W5500"] +SPI_ETHERNET_DEFAULT_POLLING_INTERVAL = TimePeriodMilliseconds(milliseconds=10) emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t") emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t") @@ -100,6 +106,24 @@ EthernetComponent = ethernet_ns.class_("EthernetComponent", cg.Component) ManualIP = ethernet_ns.struct("ManualIP") +def _is_framework_spi_polling_mode_supported(): + # SPI Ethernet without IRQ feature is added in + # esp-idf >= (5.3+ ,5.2.1+, 5.1.4) and arduino-esp32 >= 3.0.0 + framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + if CORE.using_esp_idf: + if framework_version >= cv.Version(5, 3, 0): + return True + if cv.Version(5, 3, 0) > framework_version >= cv.Version(5, 2, 1): + return True + if cv.Version(5, 2, 0) > framework_version >= cv.Version(5, 1, 4): + return True + return False + if CORE.using_arduino: + return framework_version >= cv.Version(3, 0, 0) + # fail safe: Unknown framework + return False + + def _validate(config): if CONF_USE_ADDRESS not in config: if CONF_MANUAL_IP in config: @@ -107,6 +131,27 @@ def _validate(config): else: use_address = CORE.name + config[CONF_DOMAIN] config[CONF_USE_ADDRESS] = use_address + if config[CONF_TYPE] in SPI_ETHERNET_TYPES: + if _is_framework_spi_polling_mode_supported(): + if CONF_POLLING_INTERVAL in config and CONF_INTERRUPT_PIN in config: + raise cv.Invalid( + f"Cannot specify more than one of {CONF_INTERRUPT_PIN}, {CONF_POLLING_INTERVAL}" + ) + if CONF_POLLING_INTERVAL not in config and CONF_INTERRUPT_PIN not in config: + config[CONF_POLLING_INTERVAL] = SPI_ETHERNET_DEFAULT_POLLING_INTERVAL + else: + if CONF_POLLING_INTERVAL in config: + raise cv.Invalid( + "In this version of the framework " + f"({CORE.target_framework} {CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}), " + f"'{CONF_POLLING_INTERVAL}' is not supported." + ) + if CONF_INTERRUPT_PIN not in config: + raise cv.Invalid( + "In this version of the framework " + f"({CORE.target_framework} {CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]}), " + f"'{CONF_INTERRUPT_PIN}' is a required option for [ethernet]." + ) return config @@ -157,6 +202,11 @@ SPI_SCHEMA = BASE_SCHEMA.extend( cv.Optional(CONF_CLOCK_SPEED, default="26.67MHz"): cv.All( cv.frequency, cv.int_range(int(8e6), int(80e6)) ), + # Set default value (SPI_ETHERNET_DEFAULT_POLLING_INTERVAL) at _validate() + cv.Optional(CONF_POLLING_INTERVAL): cv.All( + cv.positive_time_period_milliseconds, + cv.Range(min=TimePeriodMilliseconds(milliseconds=1)), + ), } ), ) @@ -234,6 +284,10 @@ async def to_code(config): cg.add(var.set_cs_pin(config[CONF_CS_PIN])) if CONF_INTERRUPT_PIN in config: cg.add(var.set_interrupt_pin(config[CONF_INTERRUPT_PIN])) + else: + cg.add(var.set_polling_interval(config[CONF_POLLING_INTERVAL])) + if _is_framework_spi_polling_mode_supported(): + cg.add_define("USE_ETHERNET_SPI_POLLING_SUPPORT") if CONF_RESET_PIN in config: cg.add(var.set_reset_pin(config[CONF_RESET_PIN])) cg.add(var.set_clock_speed(config[CONF_CLOCK_SPEED])) diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 00c7ae4ab8..08f5fa6642 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -116,6 +116,9 @@ void EthernetComponent::setup() { eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle); #endif w5500_config.int_gpio_num = this->interrupt_pin_; +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + w5500_config.poll_period_ms = this->polling_interval_; +#endif phy_config.phy_addr = this->phy_addr_spi_; phy_config.reset_gpio_num = this->reset_pin_; @@ -327,7 +330,14 @@ void EthernetComponent::dump_config() { 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, " IRQ Pin: %u", this->interrupt_pin_); +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + if (this->polling_interval_ != 0) { + ESP_LOGCONFIG(TAG, " Polling Interval: %lu ms", this->polling_interval_); + } else +#endif + { + 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); #else @@ -536,6 +546,9 @@ void EthernetComponent::set_cs_pin(uint8_t cs_pin) { this->cs_pin_ = cs_pin; } void EthernetComponent::set_interrupt_pin(uint8_t interrupt_pin) { this->interrupt_pin_ = interrupt_pin; } void EthernetComponent::set_reset_pin(uint8_t reset_pin) { this->reset_pin_ = reset_pin; } void EthernetComponent::set_clock_speed(int clock_speed) { this->clock_speed_ = clock_speed; } +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT +void EthernetComponent::set_polling_interval(uint32_t polling_interval) { this->polling_interval_ = polling_interval; } +#endif #else void EthernetComponent::set_phy_addr(uint8_t phy_addr) { this->phy_addr_ = phy_addr; } void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_pin; } diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 5ee430c046..fb178431d5 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -67,6 +67,9 @@ class EthernetComponent : public Component { void set_interrupt_pin(uint8_t interrupt_pin); void set_reset_pin(uint8_t reset_pin); void set_clock_speed(int clock_speed); +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + void set_polling_interval(uint32_t polling_interval); +#endif #else void set_phy_addr(uint8_t phy_addr); void set_power_pin(int power_pin); @@ -108,10 +111,13 @@ class EthernetComponent : public Component { uint8_t miso_pin_; uint8_t mosi_pin_; uint8_t cs_pin_; - uint8_t interrupt_pin_; + int interrupt_pin_{-1}; int reset_pin_{-1}; int phy_addr_spi_{-1}; int clock_speed_; +#ifdef USE_ETHERNET_SPI_POLLING_SUPPORT + uint32_t polling_interval_{0}; +#endif #else uint8_t phy_addr_{0}; int power_pin_{-1}; diff --git a/esphome/const.py b/esphome/const.py index 54ebb2815f..c39061631b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -666,6 +666,7 @@ CONF_PMC_1_0 = "pmc_1_0" CONF_PMC_10_0 = "pmc_10_0" CONF_PMC_2_5 = "pmc_2_5" CONF_PMC_4_0 = "pmc_4_0" +CONF_POLLING_INTERVAL = "polling_interval" CONF_PORT = "port" CONF_POSITION = "position" CONF_POSITION_ACTION = "position_action" From 444c0fc67f4284e42e00ad61466febe999122690 Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Mon, 28 Oct 2024 20:09:22 -0700 Subject: [PATCH 0554/1052] Add asdf to gitignore (and dockerignore) (#7686) --- .dockerignore | 3 +++ .gitignore | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.dockerignore b/.dockerignore index 9f14b98059..7998ff877f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -75,6 +75,9 @@ target/ # pyenv .python-version +# asdf +.tool-versions + # celery beat schedule file celerybeat-schedule diff --git a/.gitignore b/.gitignore index 79820249ac..ad38e26fdd 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,9 @@ cov.xml # pyenv .python-version +# asdf +.tool-versions + # Environments .env .venv From 90b076eccd67253fb9ce612251130a76039e549d Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Mon, 28 Oct 2024 20:43:02 -0700 Subject: [PATCH 0555/1052] Add more prometheus metrics (#7683) --- .../prometheus/prometheus_handler.cpp | 43 +++++++++++++++++++ .../prometheus/prometheus_handler.h | 7 +++ tests/components/prometheus/common.yaml | 14 ++++++ 3 files changed, 64 insertions(+) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 3e9cf81e6e..63b3fdf53f 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -50,6 +50,12 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) { this->lock_row_(stream, obj); #endif +#ifdef USE_TEXT_SENSOR + this->text_sensor_type_(stream); + for (auto *obj : App.get_text_sensors()) + this->text_sensor_row_(stream, obj); +#endif + req->send(stream); } @@ -349,6 +355,43 @@ void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) } #endif +// Type-specific implementation +#ifdef USE_TEXT_SENSOR +void PrometheusHandler::text_sensor_type_(AsyncResponseStream *stream) { + stream->print(F("#TYPE esphome_text_sensor_value gauge\n")); + stream->print(F("#TYPE esphome_text_sensor_failed gauge\n")); +} +void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_sensor::TextSensor *obj) { + if (obj->is_internal() && !this->include_internal_) + return; + if (obj->has_state()) { + // We have a valid value, output this value + stream->print(F("esphome_text_sensor_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 0\n")); + // Data itself + stream->print(F("esphome_text_sensor_value{id=\"")); + stream->print(relabel_id_(obj).c_str()); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\",value=\"")); + stream->print(obj->state.c_str()); + stream->print(F("\"} ")); + stream->print(F("1.0")); + stream->print(F("\n")); + } else { + // Invalid state + stream->print(F("esphome_text_sensor_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 1\n")); + } +} +#endif + } // namespace prometheus } // namespace esphome #endif diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index f5e49a1419..512e1bee4f 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -110,6 +110,13 @@ class PrometheusHandler : public AsyncWebHandler, public Component { void lock_row_(AsyncResponseStream *stream, lock::Lock *obj); #endif +#ifdef USE_TEXT_SENSOR + /// Return the type for prometheus + void text_sensor_type_(AsyncResponseStream *stream); + /// Return the lock Values state as prometheus data point + void text_sensor_row_(AsyncResponseStream *stream, text_sensor::TextSensor *obj); +#endif + web_server_base::WebServerBase *base_; bool include_internal_{false}; std::map relabel_map_id_; diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index c8ce17da88..7aa509f8b2 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -13,9 +13,23 @@ sensor: } update_interval: 60s +text_sensor: + - platform: template + id: template_text_sensor1 + lambda: |- + if (millis() > 10000) { + return {"Hello World"}; + } else { + return {"Goodbye (cruel) World"}; + } + update_interval: 60s + prometheus: include_internal: true relabel: template_sensor1: id: hellow_world name: Hello World + template_text_sensor1: + id: hello_text + name: Text Substitution From 0dab280440de853d57d750463b3be4e065fd480d Mon Sep 17 00:00:00 2001 From: Sean Brogan Date: Mon, 28 Oct 2024 20:49:06 -0700 Subject: [PATCH 0556/1052] Mopeka Pro Check improvement to allow user to configure the sensor reporting for lower quality readings (#7475) --- .../mopeka_pro_check/mopeka_pro_check.cpp | 63 ++++++++++++------- .../mopeka_pro_check/mopeka_pro_check.h | 11 +++- esphome/components/mopeka_pro_check/sensor.py | 41 ++++++++++++ tests/components/mopeka_pro_check/common.yaml | 17 +++++ 4 files changed, 108 insertions(+), 24 deletions(-) diff --git a/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp b/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp index f79e40bb4e..9527f09f59 100644 --- a/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp +++ b/esphome/components/mopeka_pro_check/mopeka_pro_check.cpp @@ -17,6 +17,8 @@ void MopekaProCheck::dump_config() { LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); LOG_SENSOR(" ", "Reading Distance", this->distance_); + LOG_SENSOR(" ", "Read Quality", this->read_quality_); + LOG_SENSOR(" ", "Ignored Reads", this->ignored_reads_); } /** @@ -66,34 +68,49 @@ bool MopekaProCheck::parse_device(const esp32_ble_tracker::ESPBTDevice &device) this->battery_level_->publish_state(level); } + // Get the quality value + SensorReadQuality quality_value = this->parse_read_quality_(manu_data.data); + if (this->read_quality_ != nullptr) { + this->read_quality_->publish_state(static_cast(quality_value)); + } + + // Determine if we have a good enough quality of read to report level and distance + // sensors. This sensor is reported regardless of distance or level sensors being enabled + if (quality_value < this->min_signal_quality_) { + ESP_LOGW(TAG, "Read Quality too low to report distance or level"); + this->ignored_read_count_++; + } else { + // reset to zero since read quality was sufficient + this->ignored_read_count_ = 0; + } + // Report number of contiguous ignored reads if sensor defined + if (this->ignored_reads_ != nullptr) { + this->ignored_reads_->publish_state(this->ignored_read_count_); + } + // Get distance and level if either are sensors if ((this->distance_ != nullptr) || (this->level_ != nullptr)) { uint32_t distance_value = this->parse_distance_(manu_data.data); - SensorReadQuality quality_value = this->parse_read_quality_(manu_data.data); ESP_LOGD(TAG, "Distance Sensor: Quality (0x%X) Distance (%" PRId32 "mm)", quality_value, distance_value); - if (quality_value < QUALITY_HIGH) { - ESP_LOGW(TAG, "Poor read quality."); - } - if (quality_value < QUALITY_MED) { - // if really bad reading set to 0 - ESP_LOGW(TAG, "Setting distance to 0"); - distance_value = 0; - } - // update distance sensor - if (this->distance_ != nullptr) { - this->distance_->publish_state(distance_value); - } - - // update level sensor - if (this->level_ != nullptr) { - uint8_t tank_level = 0; - if (distance_value >= this->full_mm_) { - tank_level = 100; // cap at 100% - } else if (distance_value > this->empty_mm_) { - tank_level = ((100.0f / (this->full_mm_ - this->empty_mm_)) * (distance_value - this->empty_mm_)); + // only update distance and level sensors if read quality was sufficient. This can be determined by + // if the ignored_read_count is zero. + if (this->ignored_read_count_ == 0) { + // update distance sensor + if (this->distance_ != nullptr) { + this->distance_->publish_state(distance_value); + } + + // update level sensor + if (this->level_ != nullptr) { + uint8_t tank_level = 0; + if (distance_value >= this->full_mm_) { + tank_level = 100; // cap at 100% + } else if (distance_value > this->empty_mm_) { + tank_level = ((100.0f / (this->full_mm_ - this->empty_mm_)) * (distance_value - this->empty_mm_)); + } + this->level_->publish_state(tank_level); } - this->level_->publish_state(tank_level); } } @@ -131,6 +148,8 @@ uint32_t MopekaProCheck::parse_distance_(const std::vector &message) { uint8_t MopekaProCheck::parse_temperature_(const std::vector &message) { return (message[2] & 0x7F) - 40; } SensorReadQuality MopekaProCheck::parse_read_quality_(const std::vector &message) { + // Since a 8 bit value is being shifted and truncated to 2 bits all possible values are defined as enumeration + // value and the static cast is safe. return static_cast(message[4] >> 6); } diff --git a/esphome/components/mopeka_pro_check/mopeka_pro_check.h b/esphome/components/mopeka_pro_check/mopeka_pro_check.h index 8b4d47e4c6..c58406ac18 100644 --- a/esphome/components/mopeka_pro_check/mopeka_pro_check.h +++ b/esphome/components/mopeka_pro_check/mopeka_pro_check.h @@ -24,9 +24,9 @@ enum SensorType { }; // Sensor read quality. If sensor is poorly placed or tank level -// gets too low the read quality will show and the distanace +// gets too low the read quality will show and the distance // measurement may be inaccurate. -enum SensorReadQuality { QUALITY_HIGH = 0x3, QUALITY_MED = 0x2, QUALITY_LOW = 0x1, QUALITY_NONE = 0x0 }; +enum SensorReadQuality { QUALITY_HIGH = 0x3, QUALITY_MED = 0x2, QUALITY_LOW = 0x1, QUALITY_ZERO = 0x0 }; class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceListener { public: @@ -35,11 +35,14 @@ class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceLi bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override; void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } + void set_min_signal_quality(SensorReadQuality min) { this->min_signal_quality_ = min; }; void set_level(sensor::Sensor *level) { level_ = level; }; void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }; void set_battery_level(sensor::Sensor *bat) { battery_level_ = bat; }; void set_distance(sensor::Sensor *distance) { distance_ = distance; }; + void set_signal_quality(sensor::Sensor *rq) { read_quality_ = rq; }; + void set_ignored_reads(sensor::Sensor *ir) { ignored_reads_ = ir; }; void set_tank_full(float full) { full_mm_ = full; }; void set_tank_empty(float empty) { empty_mm_ = empty; }; @@ -49,9 +52,13 @@ class MopekaProCheck : public Component, public esp32_ble_tracker::ESPBTDeviceLi sensor::Sensor *temperature_{nullptr}; sensor::Sensor *distance_{nullptr}; sensor::Sensor *battery_level_{nullptr}; + sensor::Sensor *read_quality_{nullptr}; + sensor::Sensor *ignored_reads_{nullptr}; uint32_t full_mm_; uint32_t empty_mm_; + uint32_t ignored_read_count_ = 0; + SensorReadQuality min_signal_quality_ = QUALITY_MED; uint8_t parse_battery_level_(const std::vector &message); uint32_t parse_distance_(const std::vector &message); diff --git a/esphome/components/mopeka_pro_check/sensor.py b/esphome/components/mopeka_pro_check/sensor.py index 0ba33e94de..95ade53013 100644 --- a/esphome/components/mopeka_pro_check/sensor.py +++ b/esphome/components/mopeka_pro_check/sensor.py @@ -5,9 +5,12 @@ from esphome.const import ( CONF_DISTANCE, CONF_MAC_ADDRESS, CONF_ID, + ICON_COUNTER, ICON_THERMOMETER, ICON_RULER, + ICON_SIGNAL, UNIT_PERCENT, + UNIT_EMPTY, CONF_LEVEL, CONF_TEMPERATURE, DEVICE_CLASS_TEMPERATURE, @@ -16,11 +19,15 @@ from esphome.const import ( STATE_CLASS_MEASUREMENT, CONF_BATTERY_LEVEL, DEVICE_CLASS_BATTERY, + ENTITY_CATEGORY_DIAGNOSTIC, ) CONF_TANK_TYPE = "tank_type" CONF_CUSTOM_DISTANCE_FULL = "custom_distance_full" CONF_CUSTOM_DISTANCE_EMPTY = "custom_distance_empty" +CONF_SIGNAL_QUALITY = "signal_quality" +CONF_MINIMUM_SIGNAL_QUALITY = "minimum_signal_quality" +CONF_IGNORED_READS = "ignored_reads" ICON_PROPANE_TANK = "mdi:propane-tank" @@ -56,6 +63,14 @@ MopekaProCheck = mopeka_pro_check_ns.class_( "MopekaProCheck", esp32_ble_tracker.ESPBTDeviceListener, cg.Component ) +SensorReadQuality = mopeka_pro_check_ns.enum("SensorReadQuality") +SIGNAL_QUALITIES = { + "ZERO": SensorReadQuality.QUALITY_ZERO, + "LOW": SensorReadQuality.QUALITY_LOW, + "MEDIUM": SensorReadQuality.QUALITY_MED, + "HIGH": SensorReadQuality.QUALITY_HIGH, +} + CONFIG_SCHEMA = ( cv.Schema( { @@ -89,6 +104,21 @@ CONFIG_SCHEMA = ( device_class=DEVICE_CLASS_BATTERY, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_SIGNAL_QUALITY): sensor.sensor_schema( + unit_of_measurement=UNIT_EMPTY, + icon=ICON_SIGNAL, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_IGNORED_READS): sensor.sensor_schema( + unit_of_measurement=UNIT_EMPTY, + icon=ICON_COUNTER, + accuracy_decimals=0, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), + cv.Optional(CONF_MINIMUM_SIGNAL_QUALITY, default="MEDIUM"): cv.enum( + SIGNAL_QUALITIES, upper=True + ), } ) .extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA) @@ -119,6 +149,11 @@ async def to_code(config): cg.add(var.set_tank_empty(CONF_SUPPORTED_TANKS_MAP[t][0])) cg.add(var.set_tank_full(CONF_SUPPORTED_TANKS_MAP[t][1])) + if ( + minimum_signal_quality := config.get(CONF_MINIMUM_SIGNAL_QUALITY, None) + ) is not None: + cg.add(var.set_min_signal_quality(minimum_signal_quality)) + if CONF_TEMPERATURE in config: sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) cg.add(var.set_temperature(sens)) @@ -131,3 +166,9 @@ async def to_code(config): if CONF_BATTERY_LEVEL in config: sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL]) cg.add(var.set_battery_level(sens)) + if CONF_SIGNAL_QUALITY in config: + sens = await sensor.new_sensor(config[CONF_SIGNAL_QUALITY]) + cg.add(var.set_signal_quality(sens)) + if CONF_IGNORED_READS in config: + sens = await sensor.new_sensor(config[CONF_IGNORED_READS]) + cg.add(var.set_ignored_reads(sens)) diff --git a/tests/components/mopeka_pro_check/common.yaml b/tests/components/mopeka_pro_check/common.yaml index 147cbcb9de..3533ecf631 100644 --- a/tests/components/mopeka_pro_check/common.yaml +++ b/tests/components/mopeka_pro_check/common.yaml @@ -14,3 +14,20 @@ sensor: name: Propane test distance battery_level: name: Propane test battery level + + - platform: mopeka_pro_check + mac_address: AA:BB:CC:DD:EE:FF + tank_type: 20LB_V + temperature: + name: "Propane test2 temp" + level: + name: "Propane test2 level" + distance: + name: "Propane test2 distance" + battery_level: + name: "Propane test2 battery level" + signal_quality: + name: "propane test2 read quality" + ignored_reads: + name: "propane test2 ignored reads" + minimum_signal_quality: "LOW" From aa0e155e22c422c789feeb0b1be9495f703fc8fa Mon Sep 17 00:00:00 2001 From: Bonne Eggleston Date: Mon, 28 Oct 2024 20:52:39 -0700 Subject: [PATCH 0557/1052] Fixes modbus timing error (#7674) --- esphome/components/modbus/modbus.cpp | 36 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index f8dd4c18b9..8544b50261 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -15,23 +15,33 @@ void Modbus::setup() { void Modbus::loop() { const uint32_t now = millis(); - if (now - this->last_modbus_byte_ > 50) { - this->rx_buffer_.clear(); - this->last_modbus_byte_ = now; - } - // stop blocking new send commands after send_wait_time_ ms regardless if a response has been received since then - if (now - this->last_send_ > send_wait_time_) { - waiting_for_response = 0; - } - while (this->available()) { uint8_t byte; this->read_byte(&byte); if (this->parse_modbus_byte_(byte)) { this->last_modbus_byte_ = now; } else { + size_t at = this->rx_buffer_.size(); + if (at > 0) { + ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at); + this->rx_buffer_.clear(); + } + } + } + + if (now - this->last_modbus_byte_ > 50) { + size_t at = this->rx_buffer_.size(); + if (at > 0) { + ESP_LOGV(TAG, "Clearing buffer of %d bytes - timeout", at); this->rx_buffer_.clear(); } + + // stop blocking new send commands after sent_wait_time_ ms after response received + if (now - this->last_send_ > send_wait_time_) { + if (waiting_for_response > 0) + ESP_LOGV(TAG, "Stop waiting for response from %d", waiting_for_response); + waiting_for_response = 0; + } } } @@ -39,7 +49,7 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { size_t at = this->rx_buffer_.size(); this->rx_buffer_.push_back(byte); const uint8_t *raw = &this->rx_buffer_[0]; - ESP_LOGV(TAG, "Modbus received Byte %d (0X%x)", byte, byte); + ESP_LOGVV(TAG, "Modbus received Byte %d (0X%x)", byte, byte); // Byte 0: modbus address (match all) if (at == 0) return true; @@ -144,8 +154,10 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { ESP_LOGW(TAG, "Got Modbus frame from unknown address 0x%02X! ", address); } - // return false to reset buffer - return false; + // reset buffer + ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse succeeded", at); + this->rx_buffer_.clear(); + return true; } void Modbus::dump_config() { From abbd7faa641220fc3cbc2e77f76f3bfcf41546ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mart=C3=ADn?= Date: Tue, 29 Oct 2024 04:56:50 +0100 Subject: [PATCH 0558/1052] fix(WiFi): Fix strncpy missing NULL terminator [-Werror=stringop-truncation] (#7668) --- esphome/components/wifi/wifi_component.cpp | 4 ++-- esphome/components/wifi/wifi_component_esp32_arduino.cpp | 8 ++++---- esphome/components/wifi/wifi_component_esp8266.cpp | 8 ++++---- esphome/components/wifi/wifi_component_esp_idf.cpp | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 583a27466a..8788711d5a 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -297,8 +297,8 @@ void WiFiComponent::set_sta(const WiFiAP &ap) { void WiFiComponent::clear_sta() { this->sta_.clear(); } void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &password) { SavedWifiSettings save{}; - strncpy(save.ssid, ssid.c_str(), sizeof(save.ssid)); - strncpy(save.password, password.c_str(), sizeof(save.password)); + snprintf(save.ssid, sizeof(save.ssid), "%s", ssid.c_str()); + snprintf(save.password, sizeof(save.password), "%s", password.c_str()); this->pref_.save(&save); // ensure it's written immediately global_preferences->sync(); diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index ef4308b28c..88648093c6 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -137,8 +137,8 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strncpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid)); - strncpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password)); + snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); + snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -746,7 +746,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strncpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); + snprintf(reinterpret_cast(conf.ap.ssid), sizeof(conf.ap.ssid), "%s", ap.get_ssid().c_str()); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -757,7 +757,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - strncpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password)); + snprintf(reinterpret_cast(conf.ap.password), sizeof(conf.ap.password), "%s", ap.get_password().c_str()); } // pairwise cipher of SoftAP, group cipher will be derived using this. diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 92f80c1e52..4568895950 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -236,8 +236,8 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { struct station_config conf {}; memset(&conf, 0, sizeof(conf)); - strncpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), sizeof(conf.ssid)); - strncpy(reinterpret_cast(conf.password), ap.get_password().c_str(), sizeof(conf.password)); + snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); + snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); if (ap.get_bssid().has_value()) { conf.bssid_set = 1; @@ -775,7 +775,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; struct softap_config conf {}; - strncpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), sizeof(conf.ssid)); + snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); conf.ssid_len = static_cast(ap.get_ssid().size()); conf.channel = ap.get_channel().value_or(1); conf.ssid_hidden = ap.get_hidden(); @@ -787,7 +787,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.password = 0; } else { conf.authmode = AUTH_WPA2_PSK; - strncpy(reinterpret_cast(conf.password), ap.get_password().c_str(), sizeof(conf.password)); + snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); } ETS_UART_INTR_DISABLE(); diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 0f2e181e31..13870136d4 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -289,8 +289,8 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strncpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), sizeof(conf.sta.ssid)); - strncpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), sizeof(conf.sta.password)); + snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); + snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { From 71e1e3b5f8575a3c075b90dbf573c95927a9bb0a Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 29 Oct 2024 04:58:36 +0100 Subject: [PATCH 0559/1052] let make new platform implementation in external components (#7615) Co-authored-by: Tomasz Duda --- esphome/core/helpers.cpp | 57 ++++++++++++++++++++++++---------------- esphome/core/helpers.h | 4 +++ 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index dca35819ff..8f94f624f1 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef USE_HOST #ifndef _WIN32 @@ -188,37 +189,39 @@ uint32_t fnv1_hash(const std::string &str) { return hash; } -uint32_t random_uint32() { #ifdef USE_ESP32 - return esp_random(); +uint32_t random_uint32() { return esp_random(); } #elif defined(USE_ESP8266) - return os_random(); +uint32_t random_uint32() { return os_random(); } #elif defined(USE_RP2040) +uint32_t random_uint32() { uint32_t result = 0; for (uint8_t i = 0; i < 32; i++) { result <<= 1; result |= rosc_hw->randombit; } return result; +} #elif defined(USE_LIBRETINY) - return rand(); +uint32_t random_uint32() { return rand(); } #elif defined(USE_HOST) +uint32_t random_uint32() { std::random_device dev; std::mt19937 rng(dev()); std::uniform_int_distribution dist(0, std::numeric_limits::max()); return dist(rng); -#else -#error "No random source available for this configuration." -#endif } +#endif float random_float() { return static_cast(random_uint32()) / static_cast(UINT32_MAX); } -bool random_bytes(uint8_t *data, size_t len) { #ifdef USE_ESP32 +bool random_bytes(uint8_t *data, size_t len) { esp_fill_random(data, len); return true; +} #elif defined(USE_ESP8266) - return os_get_random(data, len) == 0; +bool random_bytes(uint8_t *data, size_t len) { return os_get_random(data, len) == 0; } #elif defined(USE_RP2040) +bool random_bytes(uint8_t *data, size_t len) { while (len-- != 0) { uint8_t result = 0; for (uint8_t i = 0; i < 8; i++) { @@ -228,10 +231,14 @@ bool random_bytes(uint8_t *data, size_t len) { *data++ = result; } return true; +} #elif defined(USE_LIBRETINY) +bool random_bytes(uint8_t *data, size_t len) { lt_rand_bytes(data, len); return true; +} #elif defined(USE_HOST) +bool random_bytes(uint8_t *data, size_t len) { FILE *fp = fopen("/dev/urandom", "r"); if (fp == nullptr) { ESP_LOGW(TAG, "Could not open /dev/urandom, errno=%d", errno); @@ -244,10 +251,8 @@ bool random_bytes(uint8_t *data, size_t len) { } fclose(fp); return true; -#else -#error "No random source available for this configuration." -#endif } +#endif // Strings @@ -619,11 +624,13 @@ void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green #if defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_HOST) // ESP8266 doesn't have mutexes, but that shouldn't be an issue as it's single-core and non-preemptive OS. Mutex::Mutex() {} +Mutex::~Mutex() {} void Mutex::lock() {} bool Mutex::try_lock() { return true; } void Mutex::unlock() {} #elif defined(USE_ESP32) || defined(USE_LIBRETINY) Mutex::Mutex() { handle_ = xSemaphoreCreateMutex(); } +Mutex::~Mutex() {} void Mutex::lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); } bool Mutex::try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; } void Mutex::unlock() { xSemaphoreGive(this->handle_); } @@ -657,11 +664,13 @@ void HighFrequencyLoopRequester::stop() { } bool HighFrequencyLoopRequester::is_high_frequency() { return num_requests > 0; } -void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) #if defined(USE_HOST) +void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) static const uint8_t esphome_host_mac_address[6] = USE_ESPHOME_HOST_MAC_ADDRESS; memcpy(mac, esphome_host_mac_address, sizeof(esphome_host_mac_address)); +} #elif defined(USE_ESP32) +void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) #if defined(CONFIG_SOC_IEEE802154_SUPPORTED) // When CONFIG_SOC_IEEE802154_SUPPORTED is defined, esp_efuse_mac_get_default // returns the 802.15.4 EUI-64 address, so we read directly from eFuse instead. @@ -677,16 +686,20 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame esp_efuse_mac_get_default(mac); } #endif -#elif defined(USE_ESP8266) - wifi_get_macaddr(STATION_IF, mac); -#elif defined(USE_RP2040) && defined(USE_WIFI) - WiFi.macAddress(mac); -#elif defined(USE_LIBRETINY) - WiFi.macAddress(mac); -#else -// this should be an error, but that messes with CI checks. #error No mac address method defined -#endif } +#elif defined(USE_ESP8266) +void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) + wifi_get_macaddr(STATION_IF, mac); +} +#elif defined(USE_RP2040) && defined(USE_WIFI) +void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) + WiFi.macAddress(mac); +} +#elif defined(USE_LIBRETINY) +void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) + WiFi.macAddress(mac); +} +#endif std::string get_mac_address() { uint8_t mac[6]; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 7f6fe9bfdc..43001bafdd 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "esphome/core/optional.h" @@ -545,6 +546,7 @@ class Mutex { public: Mutex(); Mutex(const Mutex &) = delete; + ~Mutex(); void lock(); bool try_lock(); void unlock(); @@ -554,6 +556,8 @@ class Mutex { private: #if defined(USE_ESP32) || defined(USE_LIBRETINY) SemaphoreHandle_t handle_; +#else + void *handle_; // d-pointer to store private data on new platforms #endif }; From 38dd566e0cbb51b4eb6f14a8fcb03b36b7b962dc Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 28 Oct 2024 21:12:54 -0700 Subject: [PATCH 0560/1052] remove use of delay (#7680) Co-authored-by: Samuel Sieb Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/sgp4x/sgp4x.cpp | 114 ++++++++++------------------- esphome/components/sgp4x/sgp4x.h | 8 +- 2 files changed, 45 insertions(+), 77 deletions(-) diff --git a/esphome/components/sgp4x/sgp4x.cpp b/esphome/components/sgp4x/sgp4x.cpp index 7e474b9371..bf91c90832 100644 --- a/esphome/components/sgp4x/sgp4x.cpp +++ b/esphome/components/sgp4x/sgp4x.cpp @@ -111,7 +111,7 @@ void SGP4xComponent::setup() { number of records reported from being overwhelming. */ ESP_LOGD(TAG, "Component requires sampling of 1Hz, setting up background sampler"); - this->set_interval(1000, [this]() { this->update_gas_indices(); }); + this->set_interval(1000, [this]() { this->take_sample(); }); } void SGP4xComponent::self_test_() { @@ -146,31 +146,15 @@ void SGP4xComponent::self_test_() { }); } -/** - * @brief Combined the measured gasses, temperature, and humidity - * to calculate the VOC Index - * - * @param temperature The measured temperature in degrees C - * @param humidity The measured relative humidity in % rH - * @return int32_t The VOC Index - */ -bool SGP4xComponent::measure_gas_indices_(int32_t &voc, int32_t &nox) { - uint16_t voc_sraw; - uint16_t nox_sraw; - if (!measure_raw_(voc_sraw, nox_sraw)) - return false; - - this->status_clear_warning(); - - voc = voc_algorithm_.process(voc_sraw); - if (nox_sensor_) { - nox = nox_algorithm_.process(nox_sraw); - } - ESP_LOGV(TAG, "VOC = %" PRId32 ", NOx = %" PRId32, voc, nox); +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_); // 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) { - voc_algorithm_.get_states(this->voc_state0_, this->voc_state1_); + this->voc_algorithm_.get_states(this->voc_state0_, this->voc_state1_); if (std::abs(this->voc_baselines_storage_.state0 - this->voc_state0_) > MAXIMUM_STORAGE_DIFF || std::abs(this->voc_baselines_storage_.state1 - this->voc_state1_) > MAXIMUM_STORAGE_DIFF) { this->seconds_since_last_store_ = 0; @@ -179,29 +163,27 @@ bool SGP4xComponent::measure_gas_indices_(int32_t &voc, int32_t &nox) { if (this->pref_.save(&this->voc_baselines_storage_)) { ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 " ,state1: 0x%04" PRIX32, - this->voc_baselines_storage_.state0, voc_baselines_storage_.state1); + this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); } else { ESP_LOGW(TAG, "Could not store VOC baselines"); } } } - return true; + 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_); + } } -/** - * @brief Return the raw gas measurement - * - * @param temperature The measured temperature in degrees C - * @param humidity The measured relative humidity in % rH - * @return uint16_t The current raw gas measurement - */ -bool SGP4xComponent::measure_raw_(uint16_t &voc_raw, uint16_t &nox_raw) { + +void SGP4xComponent::measure_raw_() { float humidity = NAN; static uint32_t nox_conditioning_start = millis(); if (!this->self_test_complete_) { ESP_LOGD(TAG, "Self-test not yet complete"); - return false; + return; } if (this->humidity_sensor_ != nullptr) { humidity = this->humidity_sensor_->state; @@ -243,61 +225,45 @@ bool SGP4xComponent::measure_raw_(uint16_t &voc_raw, uint16_t &nox_raw) { data[1] = tempticks; if (!this->write_command(command, data, 2)) { - this->status_set_warning(); ESP_LOGD(TAG, "write error (%d)", this->last_error_); - return false; + this->status_set_warning("measurement request failed"); + return; } - delay(measure_time_); - uint16_t raw_data[2]; - raw_data[1] = 0; - if (!this->read_data(raw_data, response_words)) { - this->status_set_warning(); - ESP_LOGD(TAG, "read error (%d)", this->last_error_); - return false; - } - voc_raw = raw_data[0]; - nox_raw = raw_data[1]; // either 0 or the measured NOx ticks - return true; + + this->set_timeout(this->measure_time_, [this, response_words]() { + uint16_t raw_data[2]; + raw_data[1] = 0; + if (!this->read_data(raw_data, response_words)) { + ESP_LOGD(TAG, "read error (%d)", this->last_error_); + this->status_set_warning("measurement read failed"); + this->voc_index_ = this->nox_index_ = UINT16_MAX; + return; + } + this->voc_sraw_ = raw_data[0]; + this->nox_sraw_ = raw_data[1]; // either 0 or the measured NOx ticks + this->status_clear_warning(); + this->update_gas_indices_(); + }); } -void SGP4xComponent::update_gas_indices() { +void SGP4xComponent::take_sample() { if (!this->self_test_complete_) return; - this->seconds_since_last_store_ += 1; - if (!this->measure_gas_indices_(this->voc_index_, this->nox_index_)) { - // Set values to UINT16_MAX to indicate failure - this->voc_index_ = this->nox_index_ = UINT16_MAX; - ESP_LOGE(TAG, "measure gas indices failed"); - return; - } - 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_); - return; - } + this->measure_raw_(); } void SGP4xComponent::update() { if (this->samples_read_ < this->samples_to_stabilize_) { return; } - if (this->voc_sensor_) { - if (this->voc_index_ != UINT16_MAX) { - this->status_clear_warning(); + if (this->voc_sensor_ != nullptr) { + if (this->voc_index_ != UINT16_MAX) this->voc_sensor_->publish_state(this->voc_index_); - } else { - this->status_set_warning(); - } } - if (this->nox_sensor_) { - if (this->nox_index_ != UINT16_MAX) { - this->status_clear_warning(); + if (this->nox_sensor_ != nullptr) { + if (this->nox_index_ != UINT16_MAX) this->nox_sensor_->publish_state(this->nox_index_); - } else { - this->status_set_warning(); - } } } @@ -329,7 +295,7 @@ void SGP4xComponent::dump_config() { } LOG_UPDATE_INTERVAL(this); - if (this->humidity_sensor_ != nullptr && this->temperature_sensor_ != nullptr) { + 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_); diff --git a/esphome/components/sgp4x/sgp4x.h b/esphome/components/sgp4x/sgp4x.h index aa5ae4b9d2..959ff12c27 100644 --- a/esphome/components/sgp4x/sgp4x.h +++ b/esphome/components/sgp4x/sgp4x.h @@ -73,7 +73,7 @@ class SGP4xComponent : public PollingComponent, public sensor::Sensor, public se void setup() override; void update() override; - void update_gas_indices(); + void take_sample(); void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } void set_store_baseline(bool store_baseline) { store_baseline_ = store_baseline; } @@ -108,8 +108,10 @@ class SGP4xComponent : public PollingComponent, public sensor::Sensor, public se sensor::Sensor *temperature_sensor_{nullptr}; int16_t sensirion_init_sensors_(); - bool measure_gas_indices_(int32_t &voc, int32_t &nox); - bool measure_raw_(uint16_t &voc_raw, uint16_t &nox_raw); + void update_gas_indices_(); + void measure_raw_(); + uint16_t voc_sraw_; + uint16_t nox_sraw_; SgpType sgp_type_{SGP40}; uint64_t serial_number_; From 0982ab58ac1ff8c027bae542908e204b5d053728 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 29 Oct 2024 19:53:36 +0100 Subject: [PATCH 0561/1052] fix build error (#7694) Co-authored-by: Tomasz Duda --- esphome/core/helpers.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 8f94f624f1..dae60a4e1d 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -691,9 +691,11 @@ void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parame void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) wifi_get_macaddr(STATION_IF, mac); } -#elif defined(USE_RP2040) && defined(USE_WIFI) +#elif defined(USE_RP2040) void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) +#ifdef USE_WIFI WiFi.macAddress(mac); +#endif } #elif defined(USE_LIBRETINY) void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter) From bac6880a1e9df93a38cb6a7ba467c28a78209c2f Mon Sep 17 00:00:00 2001 From: Ilia Sotnikov Date: Wed, 30 Oct 2024 02:32:55 +0300 Subject: [PATCH 0562/1052] fix: [climate] Allow substitutions in `visual.temperature_step.{target_temperature,current_temperature}` (#7679) --- esphome/components/climate/__init__.py | 29 +++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index b302e2ab4e..ec68940726 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -119,10 +119,21 @@ visual_temperature = cv.float_with_unit( ) -def single_visual_temperature(value): - if isinstance(value, dict): - return value +VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Schema( + { + cv.Required(CONF_TARGET_TEMPERATURE): visual_temperature, + cv.Required(CONF_CURRENT_TEMPERATURE): visual_temperature, + } +) + +def visual_temperature_step(value): + + # Allow defining target/current temperature steps separately + if isinstance(value, dict): + return VISUAL_TEMPERATURE_STEP_SCHEMA(value) + + # Otherwise, use the single value for both properties value = visual_temperature(value) return VISUAL_TEMPERATURE_STEP_SCHEMA( { @@ -141,16 +152,6 @@ ControlTrigger = climate_ns.class_( "ControlTrigger", automation.Trigger.template(ClimateCall.operator("ref")) ) -VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Any( - single_visual_temperature, - cv.Schema( - { - cv.Required(CONF_TARGET_TEMPERATURE): visual_temperature, - cv.Required(CONF_CURRENT_TEMPERATURE): visual_temperature, - } - ), -) - CLIMATE_SCHEMA = ( cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA) .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) @@ -162,7 +163,7 @@ CLIMATE_SCHEMA = ( { cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature, cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature, - cv.Optional(CONF_TEMPERATURE_STEP): VISUAL_TEMPERATURE_STEP_SCHEMA, + cv.Optional(CONF_TEMPERATURE_STEP): visual_temperature_step, cv.Optional(CONF_MIN_HUMIDITY): cv.percentage_int, cv.Optional(CONF_MAX_HUMIDITY): cv.percentage_int, } From aae2ee2ecb34222ad61b52064f10006e86fc0fa3 Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Tue, 29 Oct 2024 18:03:10 -0700 Subject: [PATCH 0563/1052] Add in area and device to the prometheus labels (#7692) --- .../prometheus/prometheus_handler.cpp | 151 ++++++++++++++++-- .../prometheus/prometheus_handler.h | 27 +++- tests/components/prometheus/common.yaml | 54 +++++++ 3 files changed, 208 insertions(+), 24 deletions(-) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 63b3fdf53f..5d1861202a 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -7,53 +7,56 @@ namespace prometheus { void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) { AsyncResponseStream *stream = req->beginResponseStream("text/plain; version=0.0.4; charset=utf-8"); + std::string area = App.get_area(); + std::string node = App.get_name(); + std::string friendly_name = App.get_friendly_name(); #ifdef USE_SENSOR this->sensor_type_(stream); for (auto *obj : App.get_sensors()) - this->sensor_row_(stream, obj); + this->sensor_row_(stream, obj, area, node, friendly_name); #endif #ifdef USE_BINARY_SENSOR this->binary_sensor_type_(stream); for (auto *obj : App.get_binary_sensors()) - this->binary_sensor_row_(stream, obj); + this->binary_sensor_row_(stream, obj, area, node, friendly_name); #endif #ifdef USE_FAN this->fan_type_(stream); for (auto *obj : App.get_fans()) - this->fan_row_(stream, obj); + this->fan_row_(stream, obj, area, node, friendly_name); #endif #ifdef USE_LIGHT this->light_type_(stream); for (auto *obj : App.get_lights()) - this->light_row_(stream, obj); + this->light_row_(stream, obj, area, node, friendly_name); #endif #ifdef USE_COVER this->cover_type_(stream); for (auto *obj : App.get_covers()) - this->cover_row_(stream, obj); + this->cover_row_(stream, obj, area, node, friendly_name); #endif #ifdef USE_SWITCH this->switch_type_(stream); for (auto *obj : App.get_switches()) - this->switch_row_(stream, obj); + this->switch_row_(stream, obj, area, node, friendly_name); #endif #ifdef USE_LOCK this->lock_type_(stream); for (auto *obj : App.get_locks()) - this->lock_row_(stream, obj); + this->lock_row_(stream, obj, area, node, friendly_name); #endif #ifdef USE_TEXT_SENSOR this->text_sensor_type_(stream); for (auto *obj : App.get_text_sensors()) - this->text_sensor_row_(stream, obj); + this->text_sensor_row_(stream, obj, area, node, friendly_name); #endif req->send(stream); @@ -69,25 +72,53 @@ std::string PrometheusHandler::relabel_name_(EntityBase *obj) { return item == relabel_map_name_.end() ? obj->get_name() : item->second; } +void PrometheusHandler::add_area_label_(AsyncResponseStream *stream, std::string &area) { + if (!area.empty()) { + stream->print(F("\",area=\"")); + stream->print(area.c_str()); + } +} + +void PrometheusHandler::add_node_label_(AsyncResponseStream *stream, std::string &node) { + if (!node.empty()) { + stream->print(F("\",node=\"")); + stream->print(node.c_str()); + } +} + +void PrometheusHandler::add_friendly_name_label_(AsyncResponseStream *stream, std::string &friendly_name) { + if (!friendly_name.empty()) { + stream->print(F("\",friendly_name=\"")); + stream->print(friendly_name.c_str()); + } +} + // Type-specific implementation #ifdef USE_SENSOR void PrometheusHandler::sensor_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_sensor_value gauge\n")); stream->print(F("#TYPE esphome_sensor_failed gauge\n")); } -void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj) { +void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj, std::string &area, + std::string &node, std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; if (!std::isnan(obj->state)) { // We have a valid value, output this value stream->print(F("esphome_sensor_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_sensor_value{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",unit=\"")); @@ -99,6 +130,9 @@ void PrometheusHandler::sensor_row_(AsyncResponseStream *stream, sensor::Sensor // Invalid state stream->print(F("esphome_sensor_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 1\n")); @@ -112,19 +146,26 @@ void PrometheusHandler::binary_sensor_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_binary_sensor_value gauge\n")); stream->print(F("#TYPE esphome_binary_sensor_failed gauge\n")); } -void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj) { +void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj, + std::string &area, std::string &node, std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; if (obj->has_state()) { // We have a valid value, output this value stream->print(F("esphome_binary_sensor_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_binary_sensor_value{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -134,6 +175,9 @@ void PrometheusHandler::binary_sensor_row_(AsyncResponseStream *stream, binary_s // Invalid state stream->print(F("esphome_binary_sensor_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 1\n")); @@ -148,17 +192,24 @@ void PrometheusHandler::fan_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_fan_speed gauge\n")); stream->print(F("#TYPE esphome_fan_oscillation gauge\n")); } -void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { +void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj, std::string &area, std::string &node, + std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; stream->print(F("esphome_fan_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_fan_value{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -168,6 +219,9 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { if (obj->get_traits().supports_speed()) { stream->print(F("esphome_fan_speed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -178,6 +232,9 @@ void PrometheusHandler::fan_row_(AsyncResponseStream *stream, fan::Fan *obj) { if (obj->get_traits().supports_oscillation()) { stream->print(F("esphome_fan_oscillation{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -193,12 +250,16 @@ void PrometheusHandler::light_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_light_color gauge\n")); stream->print(F("#TYPE esphome_light_effect_active gauge\n")); } -void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightState *obj) { +void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightState *obj, std::string &area, + std::string &node, std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; // State stream->print(F("esphome_light_state{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -211,6 +272,9 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat color.as_rgbw(&r, &g, &b, &w); stream->print(F("esphome_light_color{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"brightness\"} ")); @@ -218,6 +282,9 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"r\"} ")); @@ -225,6 +292,9 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"g\"} ")); @@ -232,6 +302,9 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"b\"} ")); @@ -239,6 +312,9 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat stream->print(F("\n")); stream->print(F("esphome_light_color{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",channel=\"w\"} ")); @@ -249,12 +325,18 @@ void PrometheusHandler::light_row_(AsyncResponseStream *stream, light::LightStat if (effect == "None") { stream->print(F("esphome_light_effect_active{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",effect=\"None\"} 0\n")); } else { stream->print(F("esphome_light_effect_active{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",effect=\"")); @@ -269,19 +351,26 @@ void PrometheusHandler::cover_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_cover_value gauge\n")); stream->print(F("#TYPE esphome_cover_failed gauge\n")); } -void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *obj) { +void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *obj, std::string &area, std::string &node, + std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; if (!std::isnan(obj->position)) { // We have a valid value, output this value stream->print(F("esphome_cover_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_cover_value{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -290,6 +379,9 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob if (obj->get_traits().get_supports_tilt()) { stream->print(F("esphome_cover_tilt{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -300,6 +392,9 @@ void PrometheusHandler::cover_row_(AsyncResponseStream *stream, cover::Cover *ob // Invalid state stream->print(F("esphome_cover_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 1\n")); @@ -312,17 +407,24 @@ void PrometheusHandler::switch_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_switch_value gauge\n")); stream->print(F("#TYPE esphome_switch_failed gauge\n")); } -void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch *obj) { +void PrometheusHandler::switch_row_(AsyncResponseStream *stream, switch_::Switch *obj, std::string &area, + std::string &node, std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; stream->print(F("esphome_switch_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_switch_value{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -336,17 +438,24 @@ void PrometheusHandler::lock_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_lock_value gauge\n")); stream->print(F("#TYPE esphome_lock_failed gauge\n")); } -void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj) { +void PrometheusHandler::lock_row_(AsyncResponseStream *stream, lock::Lock *obj, std::string &area, std::string &node, + std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; stream->print(F("esphome_lock_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_lock_value{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} ")); @@ -361,19 +470,26 @@ void PrometheusHandler::text_sensor_type_(AsyncResponseStream *stream) { stream->print(F("#TYPE esphome_text_sensor_value gauge\n")); stream->print(F("#TYPE esphome_text_sensor_failed gauge\n")); } -void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_sensor::TextSensor *obj) { +void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_sensor::TextSensor *obj, std::string &area, + std::string &node, std::string &friendly_name) { if (obj->is_internal() && !this->include_internal_) return; if (obj->has_state()) { // We have a valid value, output this value stream->print(F("esphome_text_sensor_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 0\n")); // Data itself stream->print(F("esphome_text_sensor_value{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\",value=\"")); @@ -385,6 +501,9 @@ void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_senso // Invalid state stream->print(F("esphome_text_sensor_failed{id=\"")); stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); stream->print(F("\",name=\"")); stream->print(relabel_name_(obj).c_str()); stream->print(F("\"} 1\n")); diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 512e1bee4f..5d08aca63a 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -60,61 +60,72 @@ class PrometheusHandler : public AsyncWebHandler, public Component { protected: std::string relabel_id_(EntityBase *obj); std::string relabel_name_(EntityBase *obj); + void add_area_label_(AsyncResponseStream *stream, std::string &area); + void add_node_label_(AsyncResponseStream *stream, std::string &node); + void add_friendly_name_label_(AsyncResponseStream *stream, std::string &friendly_name); #ifdef USE_SENSOR /// Return the type for prometheus void sensor_type_(AsyncResponseStream *stream); /// Return the sensor state as prometheus data point - void sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj); + void sensor_row_(AsyncResponseStream *stream, sensor::Sensor *obj, std::string &area, std::string &node, + std::string &friendly_name); #endif #ifdef USE_BINARY_SENSOR /// Return the type for prometheus void binary_sensor_type_(AsyncResponseStream *stream); /// Return the sensor state as prometheus data point - void binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj); + void binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj, std::string &area, + std::string &node, std::string &friendly_name); #endif #ifdef USE_FAN /// Return the type for prometheus void fan_type_(AsyncResponseStream *stream); /// Return the sensor state as prometheus data point - void fan_row_(AsyncResponseStream *stream, fan::Fan *obj); + void fan_row_(AsyncResponseStream *stream, fan::Fan *obj, std::string &area, std::string &node, + std::string &friendly_name); #endif #ifdef USE_LIGHT /// Return the type for prometheus void light_type_(AsyncResponseStream *stream); /// Return the Light Values state as prometheus data point - void light_row_(AsyncResponseStream *stream, light::LightState *obj); + void light_row_(AsyncResponseStream *stream, light::LightState *obj, std::string &area, std::string &node, + std::string &friendly_name); #endif #ifdef USE_COVER /// Return the type for prometheus void cover_type_(AsyncResponseStream *stream); /// Return the switch Values state as prometheus data point - void cover_row_(AsyncResponseStream *stream, cover::Cover *obj); + void cover_row_(AsyncResponseStream *stream, cover::Cover *obj, std::string &area, std::string &node, + std::string &friendly_name); #endif #ifdef USE_SWITCH /// Return the type for prometheus void switch_type_(AsyncResponseStream *stream); /// Return the switch Values state as prometheus data point - void switch_row_(AsyncResponseStream *stream, switch_::Switch *obj); + void switch_row_(AsyncResponseStream *stream, switch_::Switch *obj, std::string &area, std::string &node, + std::string &friendly_name); #endif #ifdef USE_LOCK /// Return the type for prometheus void lock_type_(AsyncResponseStream *stream); /// Return the lock Values state as prometheus data point - void lock_row_(AsyncResponseStream *stream, lock::Lock *obj); + void lock_row_(AsyncResponseStream *stream, lock::Lock *obj, std::string &area, std::string &node, + std::string &friendly_name); #endif #ifdef USE_TEXT_SENSOR /// Return the type for prometheus void text_sensor_type_(AsyncResponseStream *stream); /// Return the lock Values state as prometheus data point - void text_sensor_row_(AsyncResponseStream *stream, text_sensor::TextSensor *obj); + void text_sensor_row_(AsyncResponseStream *stream, text_sensor::TextSensor *obj, std::string &area, std::string &node, + std::string &friendly_name); #endif web_server_base::WebServerBase *base_; diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index 7aa509f8b2..68ef2a2f58 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -1,3 +1,8 @@ +esphome: + name: livingroomdevice + friendly_name: Living Room Device + area: Living Room + wifi: ssid: MySSID password: password1 @@ -14,6 +19,9 @@ sensor: update_interval: 60s text_sensor: + - platform: version + name: "ESPHome Version" + hide_timestamp: true - platform: template id: template_text_sensor1 lambda: |- @@ -24,6 +32,52 @@ text_sensor: } update_interval: 60s +binary_sensor: + - platform: template + id: template_binary_sensor1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + +switch: + - platform: template + id: template_switch1 + lambda: |- + if (millis() > 10000) { + return true; + } else { + return false; + } + optimistic: true + +fan: + - platform: template + id: template_fan1 + +cover: + - platform: template + id: template_cover1 + lambda: |- + if (millis() > 10000) { + return COVER_OPEN; + } else { + return COVER_CLOSED; + } + +lock: + - platform: template + id: template_lock1 + lambda: |- + if (millis() > 10000) { + return LOCK_STATE_LOCKED; + } else { + return LOCK_STATE_UNLOCKED; + } + optimistic: true + prometheus: include_internal: true relabel: From ee3ee3a63b784767ea907f2f52935db34f0e1267 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:10:58 +1100 Subject: [PATCH 0564/1052] [http_request] Implement `on_error` trigger for requests (#7696) --- esphome/components/http_request/__init__.py | 12 ++++++++++++ esphome/components/http_request/http_request.h | 11 ++++++++--- tests/components/http_request/common.yaml | 2 ++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/esphome/components/http_request/__init__.py b/esphome/components/http_request/__init__.py index 0407bbd326..78064fb4b4 100644 --- a/esphome/components/http_request/__init__.py +++ b/esphome/components/http_request/__init__.py @@ -6,6 +6,7 @@ from esphome.const import ( CONF_ESP8266_DISABLE_SSL_SUPPORT, CONF_ID, CONF_METHOD, + CONF_ON_ERROR, CONF_TIMEOUT, CONF_TRIGGER_ID, CONF_URL, @@ -185,6 +186,13 @@ HTTP_REQUEST_ACTION_SCHEMA = cv.Schema( cv.Optional(CONF_ON_RESPONSE): automation.validate_automation( {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(HttpRequestResponseTrigger)} ), + cv.Optional(CONF_ON_ERROR): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + automation.Trigger.template() + ) + } + ), cv.Optional(CONF_MAX_RESPONSE_BUFFER_SIZE, default="1kB"): cv.validate_bytes, } ) @@ -272,5 +280,9 @@ async def http_request_action_to_code(config, action_id, template_arg, args): ], conf, ) + for conf in config.get(CONF_ON_ERROR, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) + cg.add(var.register_error_trigger(trigger)) + await automation.build_automation(trigger, [], conf) return var diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index d87d9b8a45..4ed2c834f8 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -135,8 +135,8 @@ class HttpRequestComponent : public Component { protected: const char *useragent_{nullptr}; - bool follow_redirects_; - uint16_t redirect_limit_; + bool follow_redirects_{}; + uint16_t redirect_limit_{}; uint16_t timeout_{4500}; uint32_t watchdog_timeout_{0}; }; @@ -157,6 +157,8 @@ template class HttpRequestSendAction : public Action { void register_response_trigger(HttpRequestResponseTrigger *trigger) { this->response_triggers_.push_back(trigger); } + void register_error_trigger(Trigger<> *trigger) { this->error_triggers_.push_back(trigger); } + void set_max_response_buffer_size(size_t max_response_buffer_size) { this->max_response_buffer_size_ = max_response_buffer_size; } @@ -186,6 +188,8 @@ template class HttpRequestSendAction : public Action { auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, headers); if (container == nullptr) { + for (auto *trigger : this->error_triggers_) + trigger->trigger(x...); return; } @@ -237,7 +241,8 @@ template class HttpRequestSendAction : public Action { std::map> headers_{}; std::map> json_{}; std::function json_func_{nullptr}; - std::vector response_triggers_; + std::vector response_triggers_{}; + std::vector *> error_triggers_{}; size_t max_response_buffer_size_{SIZE_MAX}; }; diff --git a/tests/components/http_request/common.yaml b/tests/components/http_request/common.yaml index 589b7fb4b4..593b85e435 100644 --- a/tests/components/http_request/common.yaml +++ b/tests/components/http_request/common.yaml @@ -12,6 +12,8 @@ esphome: url: https://esphome.io headers: Content-Type: application/json + on_error: + logger.log: "Request failed" on_response: then: - logger.log: From 6afd004ec54670ed27cd29202a7c290d258a8b64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 08:25:36 +1300 Subject: [PATCH 0565/1052] Bump pypa/gh-action-pypi-publish from 1.10.3 to 1.11.0 (#7700) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5072bec222..82d7ae5ee8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.10.3 + uses: pypa/gh-action-pypi-publish@v1.11.0 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From 765579dabbd607975901b34b350c4252f80ca3ab Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 30 Oct 2024 15:29:24 -0400 Subject: [PATCH 0566/1052] [es8311] Add es8311 dac component (#7693) --- CODEOWNERS | 1 + esphome/components/es8311/__init__.py | 0 esphome/components/es8311/audio_dac.py | 70 ++++++ esphome/components/es8311/es8311.cpp | 227 ++++++++++++++++++ esphome/components/es8311/es8311.h | 135 +++++++++++ esphome/components/es8311/es8311_const.h | 195 +++++++++++++++ esphome/components/i2s_audio/__init__.py | 4 +- esphome/const.py | 1 + tests/components/es8311/common.yaml | 15 ++ tests/components/es8311/test.esp32-ard.yaml | 5 + .../components/es8311/test.esp32-c3-ard.yaml | 5 + .../components/es8311/test.esp32-c3-idf.yaml | 5 + tests/components/es8311/test.esp32-idf.yaml | 5 + tests/components/es8311/test.esp8266-ard.yaml | 5 + 14 files changed, 670 insertions(+), 3 deletions(-) create mode 100644 esphome/components/es8311/__init__.py create mode 100644 esphome/components/es8311/audio_dac.py create mode 100644 esphome/components/es8311/es8311.cpp create mode 100644 esphome/components/es8311/es8311.h create mode 100644 esphome/components/es8311/es8311_const.h create mode 100644 tests/components/es8311/common.yaml create mode 100644 tests/components/es8311/test.esp32-ard.yaml create mode 100644 tests/components/es8311/test.esp32-c3-ard.yaml create mode 100644 tests/components/es8311/test.esp32-c3-idf.yaml create mode 100644 tests/components/es8311/test.esp32-idf.yaml create mode 100644 tests/components/es8311/test.esp8266-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 5eb1f863f2..8fbbacef59 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -131,6 +131,7 @@ esphome/components/ens160_base/* @latonita @vincentscode esphome/components/ens160_i2c/* @latonita esphome/components/ens160_spi/* @latonita esphome/components/ens210/* @itn3rd77 +esphome/components/es8311/* @kahrendt @kroimon esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @Rapsssito @jesserockz esphome/components/esp32_ble_client/* @jesserockz diff --git a/esphome/components/es8311/__init__.py b/esphome/components/es8311/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/es8311/audio_dac.py b/esphome/components/es8311/audio_dac.py new file mode 100644 index 0000000000..1b450c3c11 --- /dev/null +++ b/esphome/components/es8311/audio_dac.py @@ -0,0 +1,70 @@ +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_BITS_PER_SAMPLE, CONF_ID, CONF_SAMPLE_RATE + +CODEOWNERS = ["@kroimon", "@kahrendt"] +DEPENDENCIES = ["i2c"] + +es8311_ns = cg.esphome_ns.namespace("es8311") +ES8311 = es8311_ns.class_("ES8311", AudioDac, cg.Component, i2c.I2CDevice) + +CONF_MIC_GAIN = "mic_gain" +CONF_USE_MCLK = "use_mclk" +CONF_USE_MICROPHONE = "use_microphone" + +es8311_resolution = es8311_ns.enum("ES8311Resolution") +ES8311_BITS_PER_SAMPLE_ENUM = { + 16: es8311_resolution.ES8311_RESOLUTION_16, + 24: es8311_resolution.ES8311_RESOLUTION_24, + 32: es8311_resolution.ES8311_RESOLUTION_32, +} + +es8311_mic_gain = es8311_ns.enum("ES8311MicGain") +ES8311_MIC_GAIN_ENUM = { + "MIN": es8311_mic_gain.ES8311_MIC_GAIN_MIN, + "0DB": es8311_mic_gain.ES8311_MIC_GAIN_0DB, + "6DB": es8311_mic_gain.ES8311_MIC_GAIN_6DB, + "12DB": es8311_mic_gain.ES8311_MIC_GAIN_12DB, + "18DB": es8311_mic_gain.ES8311_MIC_GAIN_18DB, + "24DB": es8311_mic_gain.ES8311_MIC_GAIN_24DB, + "30DB": es8311_mic_gain.ES8311_MIC_GAIN_30DB, + "36DB": es8311_mic_gain.ES8311_MIC_GAIN_36DB, + "42DB": es8311_mic_gain.ES8311_MIC_GAIN_42DB, + "MAX": es8311_mic_gain.ES8311_MIC_GAIN_MAX, +} + + +_validate_bits = cv.float_with_unit("bits", "bit") + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ES8311), + cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All( + _validate_bits, cv.enum(ES8311_BITS_PER_SAMPLE_ENUM) + ), + cv.Optional(CONF_MIC_GAIN, default="42DB"): cv.enum( + ES8311_MIC_GAIN_ENUM, upper=True + ), + cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1), + cv.Optional(CONF_USE_MCLK, default=True): cv.boolean, + cv.Optional(CONF_USE_MICROPHONE, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x18)) +) + + +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_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) + cg.add(var.set_mic_gain(config[CONF_MIC_GAIN])) + cg.add(var.set_sample_frequency(config[CONF_SAMPLE_RATE])) + cg.add(var.set_use_mclk(config[CONF_USE_MCLK])) + cg.add(var.set_use_mic(config[CONF_USE_MICROPHONE])) diff --git a/esphome/components/es8311/es8311.cpp b/esphome/components/es8311/es8311.cpp new file mode 100644 index 0000000000..1cb1fbbe08 --- /dev/null +++ b/esphome/components/es8311/es8311.cpp @@ -0,0 +1,227 @@ +#include "es8311.h" +#include "es8311_const.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace es8311 { + +static const char *const TAG = "es8311"; + +// Mark the component as failed; use only in setup +#define ES8311_ERROR_FAILED(func) \ + if (!(func)) { \ + this->mark_failed(); \ + return; \ + } +// Return false; use outside of setup +#define ES8311_ERROR_CHECK(func) \ + if (!(func)) { \ + return false; \ + } + +void ES8311::setup() { + ESP_LOGCONFIG(TAG, "Setting up ES8311..."); + + // Reset + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x1F)); + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x00)); + + ES8311_ERROR_FAILED(this->configure_clock_()); + ES8311_ERROR_FAILED(this->configure_format_()); + ES8311_ERROR_FAILED(this->configure_mic_()); + + // Set initial volume + this->set_volume(0.75); // 0.75 = 0xBF = 0dB + + // Power up analog circuitry + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG0D_SYSTEM, 0x01)); + // Enable analog PGA, enable ADC modulator + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG0E_SYSTEM, 0x02)); + // Power up DAC + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG12_SYSTEM, 0x00)); + // Enable output to HP drive + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG13_SYSTEM, 0x10)); + // ADC Equalizer bypass, cancel DC offset in digital domain + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG1C_ADC, 0x6A)); + // Bypass DAC equalizer + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG37_DAC, 0x08)); + // Power On + ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x80)); +} + +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_); + + if (this->is_failed()) { + ESP_LOGCONFIG(TAG, " Failed to initialize!"); + return; + } +} + +bool ES8311::set_volume(float volume) { + volume = clamp(volume, 0.0f, 1.0f); + uint8_t reg32 = remap(volume, 0.0f, 1.0f, 0, 255); + return this->write_byte(ES8311_REG32_DAC, reg32); +} + +float ES8311::volume() { + uint8_t reg32; + this->read_byte(ES8311_REG32_DAC, ®32); + return remap(reg32, 0, 255, 0.0f, 1.0f); +} + +uint8_t ES8311::calculate_resolution_value(ES8311Resolution resolution) { + switch (resolution) { + case ES8311_RESOLUTION_16: + return (3 << 2); + case ES8311_RESOLUTION_18: + return (2 << 2); + case ES8311_RESOLUTION_20: + return (1 << 2); + case ES8311_RESOLUTION_24: + return (0 << 2); + case ES8311_RESOLUTION_32: + return (4 << 2); + default: + return 0; + } +} + +const ES8311Coefficient *ES8311::get_coefficient(uint32_t mclk, uint32_t rate) { + for (const auto &coefficient : ES8311_COEFFICIENTS) { + if (coefficient.mclk == mclk && coefficient.rate == rate) + return &coefficient; + } + return nullptr; +} + +bool ES8311::configure_clock_() { + // Register 0x01: select clock source for internal MCLK and determine its frequency + uint8_t reg01 = 0x3F; // Enable all clocks + + uint32_t mclk_frequency = this->sample_frequency_ * this->mclk_multiple_; + if (!this->use_mclk_) { + reg01 |= BIT(7); // Use SCLK + mclk_frequency = this->sample_frequency_ * (int) this->resolution_out_ * 2; + } + if (this->mclk_inverted_) { + reg01 |= BIT(6); // Invert MCLK pin + } + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG01_CLK_MANAGER, reg01)); + + // Get clock coefficients from coefficient table + auto *coefficient = get_coefficient(mclk_frequency, this->sample_frequency_); + if (coefficient == nullptr) { + ESP_LOGE(TAG, "Unable to configure sample rate %" PRIu32 "Hz with %" PRIu32 "Hz MCLK", this->sample_frequency_, + mclk_frequency); + return false; + } + + // Register 0x02 + uint8_t reg02; + ES8311_ERROR_CHECK(this->read_byte(ES8311_REG02_CLK_MANAGER, ®02)); + reg02 &= 0x07; + reg02 |= (coefficient->pre_div - 1) << 5; + reg02 |= coefficient->pre_mult << 3; + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG02_CLK_MANAGER, reg02)); + + // Register 0x03 + const uint8_t reg03 = (coefficient->fs_mode << 6) | coefficient->adc_osr; + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG03_CLK_MANAGER, reg03)); + + // Register 0x04 + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG04_CLK_MANAGER, coefficient->dac_osr)); + + // Register 0x05 + const uint8_t reg05 = ((coefficient->adc_div - 1) << 4) | (coefficient->dac_div - 1); + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG05_CLK_MANAGER, reg05)); + + // Register 0x06 + uint8_t reg06; + ES8311_ERROR_CHECK(this->read_byte(ES8311_REG06_CLK_MANAGER, ®06)); + if (this->sclk_inverted_) { + reg06 |= BIT(5); + } else { + reg06 &= ~BIT(5); + } + reg06 &= 0xE0; + if (coefficient->bclk_div < 19) { + reg06 |= (coefficient->bclk_div - 1) << 0; + } else { + reg06 |= (coefficient->bclk_div) << 0; + } + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG06_CLK_MANAGER, reg06)); + + // Register 0x07 + uint8_t reg07; + ES8311_ERROR_CHECK(this->read_byte(ES8311_REG07_CLK_MANAGER, ®07)); + reg07 &= 0xC0; + reg07 |= coefficient->lrck_h << 0; + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG07_CLK_MANAGER, reg07)); + + // Register 0x08 + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG08_CLK_MANAGER, coefficient->lrck_l)); + + // Successfully configured the clock + return true; +} + +bool ES8311::configure_format_() { + // Configure I2S mode and format + uint8_t reg00; + ES8311_ERROR_CHECK(this->read_byte(ES8311_REG00_RESET, ®00)); + reg00 &= 0xBF; + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG00_RESET, reg00)); + + // Configure SDP in resolution + uint8_t reg09 = calculate_resolution_value(this->resolution_in_); + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG09_SDPIN, reg09)); + + // Configure SDP out resolution + uint8_t reg0a = calculate_resolution_value(this->resolution_out_); + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG0A_SDPOUT, reg0a)); + + // Successfully configured the format + return true; +} + +bool ES8311::configure_mic_() { + uint8_t reg14 = 0x1A; // Enable analog MIC and max PGA gain + if (this->use_mic_) { + reg14 |= BIT(6); // Enable PDM digital microphone + } + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG14_SYSTEM, reg14)); + + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG16_ADC, this->mic_gain_)); // ADC gain scale up + ES8311_ERROR_CHECK(this->write_byte(ES8311_REG17_ADC, 0xC8)); // Set ADC gain + + // Successfully configured the microphones + return true; +} + +bool ES8311::set_mute_state_(bool mute_state) { + uint8_t reg31; + + this->is_muted_ = mute_state; + + if (!this->read_byte(ES8311_REG31_DAC, ®31)) { + return false; + } + + if (mute_state) { + reg31 |= BIT(6) | BIT(5); + } else { + reg31 &= ~(BIT(6) | BIT(5)); + } + + return this->write_byte(ES8311_REG31_DAC, reg31); +} + +} // namespace es8311 +} // namespace esphome diff --git a/esphome/components/es8311/es8311.h b/esphome/components/es8311/es8311.h new file mode 100644 index 0000000000..840a07204c --- /dev/null +++ b/esphome/components/es8311/es8311.h @@ -0,0 +1,135 @@ +#pragma once + +#include "esphome/components/audio_dac/audio_dac.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace es8311 { + +enum ES8311MicGain { + ES8311_MIC_GAIN_MIN = -1, + ES8311_MIC_GAIN_0DB, + ES8311_MIC_GAIN_6DB, + ES8311_MIC_GAIN_12DB, + ES8311_MIC_GAIN_18DB, + ES8311_MIC_GAIN_24DB, + ES8311_MIC_GAIN_30DB, + ES8311_MIC_GAIN_36DB, + ES8311_MIC_GAIN_42DB, + ES8311_MIC_GAIN_MAX +}; + +enum ES8311Resolution : uint8_t { + ES8311_RESOLUTION_16 = 16, + ES8311_RESOLUTION_18 = 18, + ES8311_RESOLUTION_20 = 20, + ES8311_RESOLUTION_24 = 24, + ES8311_RESOLUTION_32 = 32 +}; + +struct ES8311Coefficient { + uint32_t mclk; // mclk frequency + uint32_t rate; // sample rate + uint8_t pre_div; // the pre divider with range from 1 to 8 + uint8_t pre_mult; // the pre multiplier with x1, x2, x4 and x8 selection + uint8_t adc_div; // adcclk divider + uint8_t dac_div; // dacclk divider + uint8_t fs_mode; // single speed (0) or double speed (1) + uint8_t lrck_h; // adc lrck divider and dac lrck divider + uint8_t lrck_l; // + uint8_t bclk_div; // sclk divider + uint8_t adc_osr; // adc osr + uint8_t dac_osr; // dac osr +}; + +class ES8311 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice { + 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_; } + + ////////////////////////////////// + // ES8311 configuration setters // + ////////////////////////////////// + + void set_use_mclk(bool use_mclk) { this->use_mclk_ = use_mclk; } + void set_bits_per_sample(ES8311Resolution resolution) { + this->resolution_in_ = resolution; + this->resolution_out_ = resolution; + } + void set_sample_frequency(uint32_t sample_frequency) { this->sample_frequency_ = sample_frequency; } + void set_use_mic(bool use_mic) { this->use_mic_ = use_mic; } + void set_mic_gain(ES8311MicGain mic_gain) { this->mic_gain_ = mic_gain; } + + protected: + /// @brief Computes the register value for the configured resolution (bits per sample) + /// @param resolution bits per sample enum for both audio in and audio out + /// @return register value + static uint8_t calculate_resolution_value(ES8311Resolution resolution); + + /// @brief Retrieves the appropriate registers values for the configured mclk and rate + /// @param mclk mlck frequency in Hz + /// @param rate sample rate frequency in Hz + /// @return ES8311Coeffecient containing appropriate register values to configure the ES8311 or nullptr if impossible + static const ES8311Coefficient *get_coefficient(uint32_t mclk, uint32_t rate); + + /// @brief Configures the ES8311 registers for the chosen sample rate + /// @return True if successful and false otherwise + bool configure_clock_(); + + /// @brief Configures the ES8311 registers for the chosen bits per sample + /// @return True if successful and false otherwise + bool configure_format_(); + + /// @brief Configures the ES8311 microphone registers + /// @return True if successful and false otherwise + bool configure_mic_(); + + /// @brief Mutes or unmute the DAC audio out + /// @param mute_state True to mute, false to unmute + /// @return + bool set_mute_state_(bool mute_state); + + bool use_mic_; + ES8311MicGain mic_gain_; + + bool use_mclk_; // true = use dedicated MCLK pin, false = use SCLK + bool sclk_inverted_{false}; // SCLK is inverted + bool mclk_inverted_{false}; // MCLK is inverted (ignored if use_mclk_ == false) + uint32_t mclk_multiple_{256}; // MCLK frequency is sample rate * mclk_multiple_ (ignored if use_mclk_ == false) + + uint32_t sample_frequency_; // in Hz + ES8311Resolution resolution_in_; + ES8311Resolution resolution_out_; +}; + +} // namespace es8311 +} // namespace esphome diff --git a/esphome/components/es8311/es8311_const.h b/esphome/components/es8311/es8311_const.h new file mode 100644 index 0000000000..7463a92ef1 --- /dev/null +++ b/esphome/components/es8311/es8311_const.h @@ -0,0 +1,195 @@ +#pragma once + +#include "es8311.h" + +namespace esphome { +namespace es8311 { + +// ES8311 register addresses +static const uint8_t ES8311_REG00_RESET = 0x00; // Reset +static const uint8_t ES8311_REG01_CLK_MANAGER = 0x01; // Clock Manager: select clk src for mclk, enable clock for codec +static const uint8_t ES8311_REG02_CLK_MANAGER = 0x02; // Clock Manager: clk divider and clk multiplier +static const uint8_t ES8311_REG03_CLK_MANAGER = 0x03; // Clock Manager: adc fsmode and osr +static const uint8_t ES8311_REG04_CLK_MANAGER = 0x04; // Clock Manager: dac osr +static const uint8_t ES8311_REG05_CLK_MANAGER = 0x05; // Clock Manager: clk divider for adc and dac +static const uint8_t ES8311_REG06_CLK_MANAGER = 0x06; // Clock Manager: bclk inverter BIT(5) and divider +static const uint8_t ES8311_REG07_CLK_MANAGER = 0x07; // Clock Manager: tri-state, lrck divider +static const uint8_t ES8311_REG08_CLK_MANAGER = 0x08; // Clock Manager: lrck divider +static const uint8_t ES8311_REG09_SDPIN = 0x09; // Serial Digital Port: DAC +static const uint8_t ES8311_REG0A_SDPOUT = 0x0A; // Serial Digital Port: ADC +static const uint8_t ES8311_REG0B_SYSTEM = 0x0B; // System +static const uint8_t ES8311_REG0C_SYSTEM = 0x0C; // System +static const uint8_t ES8311_REG0D_SYSTEM = 0x0D; // System: power up/down +static const uint8_t ES8311_REG0E_SYSTEM = 0x0E; // System: power up/down +static const uint8_t ES8311_REG0F_SYSTEM = 0x0F; // System: low power +static const uint8_t ES8311_REG10_SYSTEM = 0x10; // System +static const uint8_t ES8311_REG11_SYSTEM = 0x11; // System +static const uint8_t ES8311_REG12_SYSTEM = 0x12; // System: Enable DAC +static const uint8_t ES8311_REG13_SYSTEM = 0x13; // System +static const uint8_t ES8311_REG14_SYSTEM = 0x14; // System: select DMIC, select analog pga gain +static const uint8_t ES8311_REG15_ADC = 0x15; // ADC: adc ramp rate, dmic sense +static const uint8_t ES8311_REG16_ADC = 0x16; // ADC +static const uint8_t ES8311_REG17_ADC = 0x17; // ADC: volume +static const uint8_t ES8311_REG18_ADC = 0x18; // ADC: alc enable and winsize +static const uint8_t ES8311_REG19_ADC = 0x19; // ADC: alc maxlevel +static const uint8_t ES8311_REG1A_ADC = 0x1A; // ADC: alc automute +static const uint8_t ES8311_REG1B_ADC = 0x1B; // ADC: alc automute, adc hpf s1 +static const uint8_t ES8311_REG1C_ADC = 0x1C; // ADC: equalizer, hpf s2 +static const uint8_t ES8311_REG1D_ADCEQ = 0x1D; // ADCEQ: equalizer B0 +static const uint8_t ES8311_REG1E_ADCEQ = 0x1E; // ADCEQ: equalizer B0 +static const uint8_t ES8311_REG1F_ADCEQ = 0x1F; // ADCEQ: equalizer B0 +static const uint8_t ES8311_REG20_ADCEQ = 0x20; // ADCEQ: equalizer B0 +static const uint8_t ES8311_REG21_ADCEQ = 0x21; // ADCEQ: equalizer A1 +static const uint8_t ES8311_REG22_ADCEQ = 0x22; // ADCEQ: equalizer A1 +static const uint8_t ES8311_REG23_ADCEQ = 0x23; // ADCEQ: equalizer A1 +static const uint8_t ES8311_REG24_ADCEQ = 0x24; // ADCEQ: equalizer A1 +static const uint8_t ES8311_REG25_ADCEQ = 0x25; // ADCEQ: equalizer A2 +static const uint8_t ES8311_REG26_ADCEQ = 0x26; // ADCEQ: equalizer A2 +static const uint8_t ES8311_REG27_ADCEQ = 0x27; // ADCEQ: equalizer A2 +static const uint8_t ES8311_REG28_ADCEQ = 0x28; // ADCEQ: equalizer A2 +static const uint8_t ES8311_REG29_ADCEQ = 0x29; // ADCEQ: equalizer B1 +static const uint8_t ES8311_REG2A_ADCEQ = 0x2A; // ADCEQ: equalizer B1 +static const uint8_t ES8311_REG2B_ADCEQ = 0x2B; // ADCEQ: equalizer B1 +static const uint8_t ES8311_REG2C_ADCEQ = 0x2C; // ADCEQ: equalizer B1 +static const uint8_t ES8311_REG2D_ADCEQ = 0x2D; // ADCEQ: equalizer B2 +static const uint8_t ES8311_REG2E_ADCEQ = 0x2E; // ADCEQ: equalizer B2 +static const uint8_t ES8311_REG2F_ADCEQ = 0x2F; // ADCEQ: equalizer B2 +static const uint8_t ES8311_REG30_ADCEQ = 0x30; // ADCEQ: equalizer B2 +static const uint8_t ES8311_REG31_DAC = 0x31; // DAC: mute +static const uint8_t ES8311_REG32_DAC = 0x32; // DAC: volume +static const uint8_t ES8311_REG33_DAC = 0x33; // DAC: offset +static const uint8_t ES8311_REG34_DAC = 0x34; // DAC: drc enable, drc winsize +static const uint8_t ES8311_REG35_DAC = 0x35; // DAC: drc maxlevel, minilevel +static const uint8_t ES8311_REG36_DAC = 0x36; // DAC +static const uint8_t ES8311_REG37_DAC = 0x37; // DAC: ramprate +static const uint8_t ES8311_REG38_DACEQ = 0x38; // DACEQ: equalizer B0 +static const uint8_t ES8311_REG39_DACEQ = 0x39; // DACEQ: equalizer B0 +static const uint8_t ES8311_REG3A_DACEQ = 0x3A; // DACEQ: equalizer B0 +static const uint8_t ES8311_REG3B_DACEQ = 0x3B; // DACEQ: equalizer B0 +static const uint8_t ES8311_REG3C_DACEQ = 0x3C; // DACEQ: equalizer B1 +static const uint8_t ES8311_REG3D_DACEQ = 0x3D; // DACEQ: equalizer B1 +static const uint8_t ES8311_REG3E_DACEQ = 0x3E; // DACEQ: equalizer B1 +static const uint8_t ES8311_REG3F_DACEQ = 0x3F; // DACEQ: equalizer B1 +static const uint8_t ES8311_REG40_DACEQ = 0x40; // DACEQ: equalizer A1 +static const uint8_t ES8311_REG41_DACEQ = 0x41; // DACEQ: equalizer A1 +static const uint8_t ES8311_REG42_DACEQ = 0x42; // DACEQ: equalizer A1 +static const uint8_t ES8311_REG43_DACEQ = 0x43; // DACEQ: equalizer A1 +static const uint8_t ES8311_REG44_GPIO = 0x44; // GPIO: dac2adc for test +static const uint8_t ES8311_REG45_GP = 0x45; // GPIO: GP control +static const uint8_t ES8311_REGFA_I2C = 0xFA; // I2C: reset registers +static const uint8_t ES8311_REGFC_FLAG = 0xFC; // Flag +static const uint8_t ES8311_REGFD_CHD1 = 0xFD; // Chip: ID1 +static const uint8_t ES8311_REGFE_CHD2 = 0xFE; // Chip: ID2 +static const uint8_t ES8311_REGFF_CHVER = 0xFF; // Chip: Version + +// ES8311 clock divider coefficients +static const ES8311Coefficient ES8311_COEFFICIENTS[] = { + // clang-format off + + // mclk, rate, pre_ pre_ adc_ dac_ fs_ lrck lrck bclk_ adc_ dac_ + // div, mult, div, div, mode, _h, _l, div, osr, osr + + // 8k + {12288000, 8000, 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + {18432000, 8000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x20}, + {16384000, 8000, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 8192000, 8000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 6144000, 8000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 4096000, 8000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 3072000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 2048000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 1536000, 8000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 1024000, 8000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + + // 11.025k + {11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 5644800, 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 2822400, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 1411200, 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + + // 12k + {12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 6144000, 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 3072000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 1536000, 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + + // 16k + {12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + {18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x20}, + {16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 8192000, 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 6144000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 4096000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 3072000, 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 2048000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 1536000, 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + { 1024000, 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20}, + + // 22.05k + {11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 5644800, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 2822400, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1411200, 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + // 24k + {12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 6144000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 3072000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1536000, 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + // 32k + {12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10}, + {16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 8192000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 6144000, 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 4096000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 3072000, 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 2048000, 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1536000, 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + { 1024000, 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + // 44.1k + {11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 5644800, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 2822400, 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1411200, 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + // 48k + {12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 6144000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 3072000, 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1536000, 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + // 64k + {12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + {16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 8192000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 6144000, 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + { 4096000, 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 3072000, 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + { 2048000, 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1536000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18}, + { 1024000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + + // 88.2k + {11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 5644800, 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 2822400, 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1411200, 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + + // 96k + {12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 6144000, 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 3072000, 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + { 1536000, 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + + // clang-format on +}; + +} // namespace es8311 +} // namespace esphome diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index d376907925..fa515a585f 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -8,7 +8,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S3, ) import esphome.config_validation as cv -from esphome.const import CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE +from esphome.const import CONF_BITS_PER_SAMPLE, CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE from esphome.cpp_generator import MockObjClass import esphome.final_validate as fv @@ -25,13 +25,11 @@ CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin" CONF_I2S_AUDIO = "i2s_audio" CONF_I2S_AUDIO_ID = "i2s_audio_id" -CONF_BITS_PER_SAMPLE = "bits_per_sample" CONF_I2S_MODE = "i2s_mode" CONF_PRIMARY = "primary" CONF_SECONDARY = "secondary" CONF_USE_APLL = "use_apll" -CONF_BITS_PER_SAMPLE = "bits_per_sample" CONF_BITS_PER_CHANNEL = "bits_per_channel" CONF_MONO = "mono" CONF_LEFT = "left" diff --git a/esphome/const.py b/esphome/const.py index c39061631b..5645c9eaab 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -92,6 +92,7 @@ CONF_BINARY_SENSORS = "binary_sensors" CONF_BINDKEY = "bindkey" CONF_BIRTH_MESSAGE = "birth_message" CONF_BIT_DEPTH = "bit_depth" +CONF_BITS_PER_SAMPLE = "bits_per_sample" CONF_BLOCK = "block" CONF_BLUE = "blue" CONF_BOARD = "board" diff --git a/tests/components/es8311/common.yaml b/tests/components/es8311/common.yaml new file mode 100644 index 0000000000..d833d1c043 --- /dev/null +++ b/tests/components/es8311/common.yaml @@ -0,0 +1,15 @@ +esphome: + on_boot: + then: + - audio_dac.mute_off: + - audio_dac.mute_on: + - audio_dac.set_volume: + volume: 50% + +i2c: + - id: i2c_aic3204 + scl: ${scl_pin} + sda: ${sda_pin} + +audio_dac: + - platform: es8311 diff --git a/tests/components/es8311/test.esp32-ard.yaml b/tests/components/es8311/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es8311/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es8311/test.esp32-c3-ard.yaml b/tests/components/es8311/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8311/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es8311/test.esp32-c3-idf.yaml b/tests/components/es8311/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8311/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es8311/test.esp32-idf.yaml b/tests/components/es8311/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es8311/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es8311/test.esp8266-ard.yaml b/tests/components/es8311/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8311/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml From d3563e4e9782299c7820ad4783a1813a361a9575 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 31 Oct 2024 06:30:46 +1100 Subject: [PATCH 0567/1052] [sdl] Allow window to be resized. (#7698) --- esphome/components/sdl/sdl_esphome.cpp | 27 +++++++++++++++++++++----- esphome/components/sdl/sdl_esphome.h | 1 + 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/esphome/components/sdl/sdl_esphome.cpp b/esphome/components/sdl/sdl_esphome.cpp index 5e17ca5650..8f0821a2fa 100644 --- a/esphome/components/sdl/sdl_esphome.cpp +++ b/esphome/components/sdl/sdl_esphome.cpp @@ -9,8 +9,9 @@ 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_, 0); + this->width_, this->height_, SDL_WINDOW_RESIZABLE); this->renderer_ = SDL_CreateRenderer(this->window_, -1, SDL_RENDERER_SOFTWARE); + SDL_RenderSetLogicalSize(this->renderer_, this->width_, this->height_); this->texture_ = SDL_CreateTexture(this->renderer_, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STATIC, this->width_, this->height_); SDL_SetTextureBlendMode(this->texture_, SDL_BLENDMODE_BLEND); @@ -25,6 +26,10 @@ void Sdl::update() { this->y_low_ = this->height_; this->x_high_ = 0; this->y_high_ = 0; + this->redraw_(rect); +} + +void Sdl::redraw_(SDL_Rect &rect) { SDL_RenderCopy(this->renderer_, this->texture_, &rect, &rect); SDL_RenderPresent(this->renderer_); } @@ -33,15 +38,13 @@ void Sdl::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t * display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { SDL_Rect rect{x_start, y_start, w, h}; if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || big_endian) { - display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, - x_pad); + Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); } else { auto stride = x_offset + w + x_pad; auto data = ptr + (stride * y_offset + x_offset) * 2; SDL_UpdateTexture(this->texture_, &rect, data, stride * 2); } - SDL_RenderCopy(this->renderer_, this->texture_, &rect, &rect); - SDL_RenderPresent(this->renderer_); + this->redraw_(rect); } void Sdl::draw_pixel_at(int x, int y, Color color) { @@ -84,6 +87,20 @@ void Sdl::loop() { } break; + case SDL_WINDOWEVENT: + switch (e.window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: + case SDL_WINDOWEVENT_EXPOSED: + case SDL_WINDOWEVENT_RESIZED: { + SDL_Rect rect{0, 0, this->width_, this->height_}; + this->redraw_(rect); + break; + } + default: + break; + } + break; + default: ESP_LOGV(TAG, "Event %d", e.type); break; diff --git a/esphome/components/sdl/sdl_esphome.h b/esphome/components/sdl/sdl_esphome.h index e4b2d9dd9f..4b0e59c9fe 100644 --- a/esphome/components/sdl/sdl_esphome.h +++ b/esphome/components/sdl/sdl_esphome.h @@ -38,6 +38,7 @@ class Sdl : public display::Display { protected: int get_width_internal() override { return this->width_; } int get_height_internal() override { return this->height_; } + void redraw_(SDL_Rect &rect); int width_{}; int height_{}; SDL_Renderer *renderer_{}; From e85157db4b246fd3c701c7b4195d4f52b735c554 Mon Sep 17 00:00:00 2001 From: Jason Nagin <33561705+JasonN3@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:34:33 -0400 Subject: [PATCH 0568/1052] Add config for current temperature precision (#7699) --- esphome/components/mqtt/mqtt_climate.cpp | 6 ++++-- esphome/components/mqtt/mqtt_const.h | 4 ++++ tests/components/mqtt/common.yaml | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 49a8f06734..773d863835 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -71,8 +71,10 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo root[MQTT_MIN_TEMP] = traits.get_visual_min_temperature(); // max_temp root[MQTT_MAX_TEMP] = traits.get_visual_max_temperature(); - // temp_step - root["temp_step"] = traits.get_visual_target_temperature_step(); + // target_temp_step + root[MQTT_TARGET_TEMPERATURE_STEP] = traits.get_visual_target_temperature_step(); + // current_temp_step + root[MQTT_CURRENT_TEMPERATURE_STEP] = traits.get_visual_current_temperature_step(); // temperature units are always coerced to Celsius internally root[MQTT_TEMPERATURE_UNIT] = "C"; diff --git a/esphome/components/mqtt/mqtt_const.h b/esphome/components/mqtt/mqtt_const.h index c1c40c4b6d..445457a27f 100644 --- a/esphome/components/mqtt/mqtt_const.h +++ b/esphome/components/mqtt/mqtt_const.h @@ -51,6 +51,7 @@ constexpr const char *const MQTT_COMMAND_TOPIC = "cmd_t"; constexpr const char *const MQTT_CONFIGURATION_URL = "cu"; constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "curr_hum_tpl"; constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "curr_hum_t"; +constexpr const char *const MQTT_CURRENT_TEMPERATURE_STEP = "precision"; constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "curr_temp_tpl"; constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "curr_temp_t"; constexpr const char *const MQTT_DEVICE = "dev"; @@ -232,6 +233,7 @@ constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "hum_cmd_tpl constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "hum_cmd_t"; constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "hum_state_tpl"; constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "hum_stat_t"; +constexpr const char *const MQTT_TARGET_TEMPERATURE_STEP = "temp_step"; constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temp_cmd_tpl"; constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temp_cmd_t"; constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temp_hi_cmd_tpl"; @@ -313,6 +315,7 @@ constexpr const char *const MQTT_COMMAND_TOPIC = "command_topic"; constexpr const char *const MQTT_CONFIGURATION_URL = "configuration_url"; constexpr const char *const MQTT_CURRENT_HUMIDITY_TEMPLATE = "current_humidity_template"; constexpr const char *const MQTT_CURRENT_HUMIDITY_TOPIC = "current_humidity_topic"; +constexpr const char *const MQTT_CURRENT_TEMPERATURE_STEP = "precision"; constexpr const char *const MQTT_CURRENT_TEMPERATURE_TEMPLATE = "current_temperature_template"; constexpr const char *const MQTT_CURRENT_TEMPERATURE_TOPIC = "current_temperature_topic"; constexpr const char *const MQTT_DEVICE = "device"; @@ -494,6 +497,7 @@ constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TEMPLATE = "target_humi constexpr const char *const MQTT_TARGET_HUMIDITY_COMMAND_TOPIC = "target_humidity_command_topic"; constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TEMPLATE = "target_humidity_state_template"; constexpr const char *const MQTT_TARGET_HUMIDITY_STATE_TOPIC = "target_humidity_state_topic"; +constexpr const char *const MQTT_TARGET_TEMPERATURE_STEP = "temp_step"; constexpr const char *const MQTT_TEMPERATURE_COMMAND_TEMPLATE = "temperature_command_template"; constexpr const char *const MQTT_TEMPERATURE_COMMAND_TOPIC = "temperature_command_topic"; constexpr const char *const MQTT_TEMPERATURE_HIGH_COMMAND_TEMPLATE = "temperature_high_command_template"; diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index 5ed6335d65..e154be8b5c 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -200,6 +200,10 @@ climate: fan_only_cooling: true fan_with_cooling: true fan_with_heating: true + visual: + temperature_step: + target_temperature: 0.1 + current_temperature: 0.1 cover: - platform: template From 5a2fed35693f7191f81fe31236e69c2132c23d99 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 31 Oct 2024 09:28:18 +1100 Subject: [PATCH 0569/1052] [spi] Add mosi pin checks for displays (#7702) --- esphome/components/ili9xxx/display.py | 4 ++++ esphome/components/pcd8544/display.py | 12 +++++++---- esphome/components/ssd1306_spi/display.py | 8 +++++-- esphome/components/ssd1322_spi/display.py | 8 +++++-- esphome/components/ssd1325_spi/display.py | 8 +++++-- esphome/components/ssd1327_spi/display.py | 8 +++++-- esphome/components/ssd1331_spi/display.py | 8 +++++-- esphome/components/ssd1351_spi/display.py | 8 +++++-- esphome/components/st7567_spi/display.py | 8 +++++-- esphome/components/st7701s/display.py | 4 ++++ esphome/components/st7735/display.py | 17 +++++++++------ esphome/components/st7789v/display.py | 21 ++++++++++++------- .../components/waveshare_epaper/display.py | 8 +++++-- 13 files changed, 88 insertions(+), 34 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 68e3aa953d..739ad07843 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -196,6 +196,10 @@ CONFIG_SCHEMA = cv.All( _validate, ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "ili9xxx", require_miso=False, require_mosi=True +) + async def to_code(config): rhs = MODELS[config[CONF_MODEL]].new() diff --git a/esphome/components/pcd8544/display.py b/esphome/components/pcd8544/display.py index d7e72d1c81..2c24b133da 100644 --- a/esphome/components/pcd8544/display.py +++ b/esphome/components/pcd8544/display.py @@ -1,15 +1,15 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import display, spi +import esphome.config_validation as cv from esphome.const import ( + CONF_CONTRAST, + CONF_CS_PIN, CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES, CONF_RESET_PIN, - CONF_CS_PIN, - CONF_CONTRAST, ) DEPENDENCIES = ["spi"] @@ -35,6 +35,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "pcd8544", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/ssd1306_spi/display.py b/esphome/components/ssd1306_spi/display.py index 0af1168bde..4af41073d4 100644 --- a/esphome/components/ssd1306_spi/display.py +++ b/esphome/components/ssd1306_spi/display.py @@ -1,8 +1,8 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import spi, ssd1306_base from esphome.components.ssd1306_base import _validate +import esphome.config_validation as cv from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES AUTO_LOAD = ["ssd1306_base"] @@ -24,6 +24,10 @@ CONFIG_SCHEMA = cv.All( _validate, ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "ssd1306_spi", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/ssd1322_spi/display.py b/esphome/components/ssd1322_spi/display.py index 88b3a53355..849e71abee 100644 --- a/esphome/components/ssd1322_spi/display.py +++ b/esphome/components/ssd1322_spi/display.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import spi, ssd1322_base +import esphome.config_validation as cv from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES CODEOWNERS = ["@kbx81"] @@ -24,6 +24,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "ssd1322_spi", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/ssd1325_spi/display.py b/esphome/components/ssd1325_spi/display.py index a86dc751d5..e18db33c68 100644 --- a/esphome/components/ssd1325_spi/display.py +++ b/esphome/components/ssd1325_spi/display.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import spi, ssd1325_base +import esphome.config_validation as cv from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES CODEOWNERS = ["@kbx81"] @@ -24,6 +24,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "ssd1325_spi", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/ssd1327_spi/display.py b/esphome/components/ssd1327_spi/display.py index 138e85eecd..b622c098ec 100644 --- a/esphome/components/ssd1327_spi/display.py +++ b/esphome/components/ssd1327_spi/display.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import spi, ssd1327_base +import esphome.config_validation as cv from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES CODEOWNERS = ["@kbx81"] @@ -24,6 +24,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "ssd1327_spi", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/ssd1331_spi/display.py b/esphome/components/ssd1331_spi/display.py index c32ac60578..50895b3175 100644 --- a/esphome/components/ssd1331_spi/display.py +++ b/esphome/components/ssd1331_spi/display.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import spi, ssd1331_base +import esphome.config_validation as cv from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES CODEOWNERS = ["@kbx81"] @@ -24,6 +24,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "ssd1331_spi", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/ssd1351_spi/display.py b/esphome/components/ssd1351_spi/display.py index 3f3409226c..bd7033c3d4 100644 --- a/esphome/components/ssd1351_spi/display.py +++ b/esphome/components/ssd1351_spi/display.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import spi, ssd1351_base +import esphome.config_validation as cv from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES CODEOWNERS = ["@kbx81"] @@ -24,6 +24,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "ssd1351_spi", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/st7567_spi/display.py b/esphome/components/st7567_spi/display.py index aabe02a2d8..305aa35024 100644 --- a/esphome/components/st7567_spi/display.py +++ b/esphome/components/st7567_spi/display.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import spi, st7567_base +import esphome.config_validation as cv from esphome.const import CONF_DC_PIN, CONF_ID, CONF_LAMBDA, CONF_PAGES CODEOWNERS = ["@latonita"] @@ -24,6 +24,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "st7567_spi", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/st7701s/display.py b/esphome/components/st7701s/display.py index e73c2467da..c6ad43c14c 100644 --- a/esphome/components/st7701s/display.py +++ b/esphome/components/st7701s/display.py @@ -167,6 +167,10 @@ CONFIG_SCHEMA = cv.All( cv.only_with_esp_idf, ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "st7701s", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/st7735/display.py b/esphome/components/st7735/display.py index d5bb2fa3d6..2761214315 100644 --- a/esphome/components/st7735/display.py +++ b/esphome/components/st7735/display.py @@ -1,17 +1,17 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins -from esphome.components import spi -from esphome.components import display +import esphome.codegen as cg +from esphome.components import display, spi +import esphome.config_validation as cv from esphome.const import ( CONF_DC_PIN, CONF_ID, + CONF_INVERT_COLORS, CONF_LAMBDA, CONF_MODEL, - CONF_RESET_PIN, CONF_PAGES, - CONF_INVERT_COLORS, + CONF_RESET_PIN, ) + from . import st7735_ns CODEOWNERS = ["@SenexCrenshaw"] @@ -68,6 +68,11 @@ CONFIG_SCHEMA = cv.All( ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "st7735", require_miso=False, require_mosi=True +) + + async def setup_st7735(var, config): await display.register_display(var, config) diff --git a/esphome/components/st7789v/display.py b/esphome/components/st7789v/display.py index 04dce2cf6c..8259eacf2d 100644 --- a/esphome/components/st7789v/display.py +++ b/esphome/components/st7789v/display.py @@ -1,22 +1,23 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins -from esphome.components import display, spi, power_supply +import esphome.codegen as cg +from esphome.components import display, power_supply, spi +import esphome.config_validation as cv from esphome.const import ( CONF_BACKLIGHT_PIN, + CONF_CS_PIN, CONF_DC_PIN, CONF_HEIGHT, CONF_ID, CONF_LAMBDA, CONF_MODEL, - CONF_RESET_PIN, - CONF_WIDTH, - CONF_POWER_SUPPLY, - CONF_ROTATION, - CONF_CS_PIN, CONF_OFFSET_HEIGHT, CONF_OFFSET_WIDTH, + CONF_POWER_SUPPLY, + CONF_RESET_PIN, + CONF_ROTATION, + CONF_WIDTH, ) + from . import st7789v_ns CONF_EIGHTBITCOLOR = "eightbitcolor" @@ -168,6 +169,10 @@ CONFIG_SCHEMA = cv.All( validate_st7789v, ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "st7789v", require_miso=False, require_mosi=True +) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 4d3965449f..8287788de5 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -1,7 +1,7 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import core, pins +import esphome.codegen as cg from esphome.components import display, spi +import esphome.config_validation as cv from esphome.const import ( CONF_BUSY_PIN, CONF_DC_PIN, @@ -187,6 +187,10 @@ CONFIG_SCHEMA = cv.All( cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), ) +FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( + "waveshare_epaper", require_miso=False, require_mosi=True +) + async def to_code(config): model_type, model = MODELS[config[CONF_MODEL]] From 74ea1b60e35fa351b5a5fc380dfdc1879d22f043 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:37:54 +1300 Subject: [PATCH 0570/1052] [CI] Fix webserver defines to be present based on platform, not just framework (#7703) --- esphome/core/defines.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index b5511b57eb..3798ddba6a 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -86,8 +86,6 @@ #ifdef USE_ARDUINO #define USE_CAPTIVE_PORTAL #define USE_PROMETHEUS -#define USE_WEBSERVER -#define USE_WEBSERVER_PORT 80 // NOLINT #define USE_WIFI_WPA2_EAP #endif @@ -111,6 +109,8 @@ #define USE_SPEAKER #define USE_SPI #define USE_VOICE_ASSISTANT +#define USE_WEBSERVER +#define USE_WEBSERVER_PORT 80 // NOLINT #define USE_WIFI_11KV_SUPPORT #ifdef USE_ARDUINO @@ -147,6 +147,8 @@ #define USE_SHD_FIRMWARE_DATA \ {} +#define USE_WEBSERVER +#define USE_WEBSERVER_PORT 80 // NOLINT #endif #ifdef USE_RP2040 @@ -158,6 +160,8 @@ #ifdef USE_LIBRETINY #define USE_SOCKET_IMPL_LWIP_SOCKETS +#define USE_WEBSERVER +#define USE_WEBSERVER_PORT 80 // NOLINT #endif #ifdef USE_HOST From 8b7e061f3ac5427dcc931feffdd0251100945a57 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:15:39 +1100 Subject: [PATCH 0571/1052] [touchscreen] Calibration fixes (#7704) --- esphome/components/touchscreen/__init__.py | 97 +++++++++---------- esphome/components/touchscreen/touchscreen.h | 12 +-- .../xpt2046/touchscreen/__init__.py | 39 ++------ tests/components/xpt2046/test.esp32-ard.yaml | 4 +- .../components/xpt2046/test.esp32-c3-ard.yaml | 2 +- .../components/xpt2046/test.esp32-c3-idf.yaml | 2 +- tests/components/xpt2046/test.esp32-idf.yaml | 2 +- .../components/xpt2046/test.esp8266-ard.yaml | 2 +- tests/components/xpt2046/test.rp2040-ard.yaml | 4 +- 9 files changed, 63 insertions(+), 101 deletions(-) diff --git a/esphome/components/touchscreen/__init__.py b/esphome/components/touchscreen/__init__.py index b2d3f60d2b..01a271a34e 100644 --- a/esphome/components/touchscreen/__init__.py +++ b/esphome/components/touchscreen/__init__.py @@ -1,21 +1,18 @@ -import esphome.config_validation as cv -import esphome.codegen as cg - -from esphome.components import display from esphome import automation - +import esphome.codegen as cg +from esphome.components import display +import esphome.config_validation as cv from esphome.const import ( + CONF_CALIBRATION, CONF_DISPLAY, - CONF_ON_TOUCH, - CONF_ON_RELEASE, - CONF_ON_UPDATE, - CONF_SWAP_XY, CONF_MIRROR_X, CONF_MIRROR_Y, + CONF_ON_RELEASE, + CONF_ON_TOUCH, + CONF_ON_UPDATE, + CONF_SWAP_XY, CONF_TRANSFORM, - CONF_CALIBRATION, ) - from esphome.core import coroutine_with_priority CODEOWNERS = ["@jesserockz", "@nielsnl68"] @@ -43,51 +40,45 @@ CONF_Y_MIN = "y_min" CONF_Y_MAX = "y_max" -def validate_calibration(config): - if CONF_CALIBRATION in config: - calibration_config = config[CONF_CALIBRATION] - if ( - cv.int_([CONF_X_MIN]) != 0 - and cv.int_(calibration_config[CONF_X_MAX]) != 0 - and abs( - cv.int_(calibration_config[CONF_X_MIN]) - - cv.int_(calibration_config[CONF_X_MAX]) - ) - < 10 - ): - raise cv.Invalid("Calibration X values difference must be more than 10") - - if ( - cv.int_(calibration_config[CONF_Y_MIN]) != 0 - and cv.int_(calibration_config[CONF_Y_MAX]) != 0 - and abs( - cv.int_(calibration_config[CONF_Y_MIN]) - - cv.int_(calibration_config[CONF_Y_MAX]) - ) - < 10 - ): - raise cv.Invalid("Calibration Y values difference must be more than 10") - - return config +def validate_calibration(calibration_config): + x_min = calibration_config[CONF_X_MIN] + x_max = calibration_config[CONF_X_MAX] + y_min = calibration_config[CONF_Y_MIN] + y_max = calibration_config[CONF_Y_MAX] + if x_max < x_min: + raise cv.Invalid( + "x_min must be smaller than x_max. To mirror the direction use the 'transform' options" + ) + if y_max < y_min: + raise cv.Invalid( + "y_min must be smaller than y_max. To mirror the direction use the 'transform' options" + ) + x_delta = x_max - x_min + y_delta = y_max - y_min + if x_delta < 10 or y_delta < 10: + raise cv.Invalid("Calibration value range must be greater than 10") + return calibration_config -def calibration_schema(default_max_values): - return cv.Schema( +CALIBRATION_SCHEMA = cv.All( + cv.Schema( { - cv.Optional(CONF_X_MIN, default=0): cv.int_range(min=0, max=4095), - cv.Optional(CONF_X_MAX, default=default_max_values): cv.int_range( - min=0, max=4095 - ), - cv.Optional(CONF_Y_MIN, default=0): cv.int_range(min=0, max=4095), - cv.Optional(CONF_Y_MAX, default=default_max_values): cv.int_range( - min=0, max=4095 - ), - }, - validate_calibration, + cv.Required(CONF_X_MIN): cv.int_range(min=0, max=4095), + cv.Required(CONF_X_MAX): cv.int_range(min=0, max=4095), + cv.Required(CONF_Y_MIN): cv.int_range(min=0, max=4095), + cv.Required(CONF_Y_MAX): cv.int_range(min=0, max=4095), + } + ), + validate_calibration, +) + + +def touchscreen_schema(default_touch_timeout=cv.UNDEFINED, calibration_required=False): + calibration = ( + cv.Required(CONF_CALIBRATION) + if calibration_required + else cv.Optional(CONF_CALIBRATION) ) - - -def touchscreen_schema(default_touch_timeout): return cv.Schema( { cv.GenerateID(CONF_DISPLAY): cv.use_id(display.Display), @@ -102,7 +93,7 @@ def touchscreen_schema(default_touch_timeout): cv.positive_time_period_milliseconds, cv.Range(max=cv.TimePeriod(milliseconds=65535)), ), - cv.Optional(CONF_CALIBRATION): calibration_schema(0), + calibration: CALIBRATION_SCHEMA, cv.Optional(CONF_ON_TOUCH): automation.validate_automation(single=True), cv.Optional(CONF_ON_UPDATE): automation.validate_automation(single=True), cv.Optional(CONF_ON_RELEASE): automation.validate_automation(single=True), diff --git a/esphome/components/touchscreen/touchscreen.h b/esphome/components/touchscreen/touchscreen.h index 21111f87b3..8016323d49 100644 --- a/esphome/components/touchscreen/touchscreen.h +++ b/esphome/components/touchscreen/touchscreen.h @@ -53,14 +53,10 @@ class Touchscreen : public PollingComponent { void set_swap_xy(bool swap) { this->swap_x_y_ = swap; } void set_calibration(int16_t x_min, int16_t x_max, int16_t y_min, int16_t y_max) { - this->x_raw_min_ = std::min(x_min, x_max); - this->x_raw_max_ = std::max(x_min, x_max); - this->y_raw_min_ = std::min(y_min, y_max); - this->y_raw_max_ = std::max(y_min, y_max); - if (x_min > x_max) - this->invert_x_ = true; - if (y_min > y_max) - this->invert_y_ = true; + this->x_raw_min_ = x_min; + this->x_raw_max_ = x_max; + this->y_raw_min_ = y_min; + this->y_raw_max_ = y_max; } Trigger *get_touch_trigger() { return &this->touch_trigger_; } diff --git a/esphome/components/xpt2046/touchscreen/__init__.py b/esphome/components/xpt2046/touchscreen/__init__.py index d45f309a3b..d91ae44789 100644 --- a/esphome/components/xpt2046/touchscreen/__init__.py +++ b/esphome/components/xpt2046/touchscreen/__init__.py @@ -1,9 +1,8 @@ -import esphome.codegen as cg -import esphome.config_validation as cv - from esphome import pins +import esphome.codegen as cg from esphome.components import spi, touchscreen -from esphome.const import CONF_ID, CONF_THRESHOLD, CONF_INTERRUPT_PIN +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_INTERRUPT_PIN, CONF_THRESHOLD CODEOWNERS = ["@numo68", "@nielsnl68"] DEPENDENCIES = ["spi"] @@ -15,13 +14,9 @@ XPT2046Component = XPT2046_ns.class_( spi.SPIDevice, ) -CONF_CALIBRATION_X_MIN = "calibration_x_min" -CONF_CALIBRATION_X_MAX = "calibration_x_max" -CONF_CALIBRATION_Y_MIN = "calibration_y_min" -CONF_CALIBRATION_Y_MAX = "calibration_y_max" - CONFIG_SCHEMA = cv.All( - touchscreen.TOUCHSCREEN_SCHEMA.extend( + touchscreen.touchscreen_schema(calibration_required=True) + .extend( cv.Schema( { cv.GenerateID(): cv.declare_id(XPT2046Component), @@ -29,30 +24,10 @@ CONFIG_SCHEMA = cv.All( pins.internal_gpio_input_pin_schema ), cv.Optional(CONF_THRESHOLD, default=400): cv.int_range(min=0, max=4095), - cv.Optional( - touchscreen.CONF_CALIBRATION - ): touchscreen.calibration_schema(4095), - cv.Optional(CONF_CALIBRATION_X_MIN): cv.invalid( - "Deprecated: use the new 'calibration' configuration variable" - ), - cv.Optional(CONF_CALIBRATION_X_MAX): cv.invalid( - "Deprecated: use the new 'calibration' configuration variable" - ), - cv.Optional(CONF_CALIBRATION_Y_MIN): cv.invalid( - "Deprecated: use the new 'calibration' configuration variable" - ), - cv.Optional(CONF_CALIBRATION_Y_MAX): cv.invalid( - "Deprecated: use the new 'calibration' configuration variable" - ), - cv.Optional(CONF_CALIBRATION_Y_MAX): cv.invalid( - "Deprecated: use the new 'calibration' configuration variable" - ), - cv.Optional("report_interval"): cv.invalid( - "Deprecated: use the 'update_interval' configuration variable" - ), }, ) - ).extend(spi.spi_device_schema()), + ) + .extend(spi.spi_device_schema()), ) diff --git a/tests/components/xpt2046/test.esp32-ard.yaml b/tests/components/xpt2046/test.esp32-ard.yaml index f15d1f9b41..9e305791e0 100644 --- a/tests/components/xpt2046/test.esp32-ard.yaml +++ b/tests/components/xpt2046/test.esp32-ard.yaml @@ -25,8 +25,8 @@ touchscreen: update_interval: 50ms threshold: 400 calibration: - x_min: 3860 - x_max: 280 + x_min: 280 + x_max: 3860 y_min: 340 y_max: 3860 on_touch: diff --git a/tests/components/xpt2046/test.esp32-c3-ard.yaml b/tests/components/xpt2046/test.esp32-c3-ard.yaml index ef4daa800d..c03fd6b345 100644 --- a/tests/components/xpt2046/test.esp32-c3-ard.yaml +++ b/tests/components/xpt2046/test.esp32-c3-ard.yaml @@ -25,7 +25,7 @@ touchscreen: update_interval: 50ms threshold: 400 calibration: - x_min: 3860 + x_min: 28 x_max: 280 y_min: 340 y_max: 3860 diff --git a/tests/components/xpt2046/test.esp32-c3-idf.yaml b/tests/components/xpt2046/test.esp32-c3-idf.yaml index ef4daa800d..787ca9b1ed 100644 --- a/tests/components/xpt2046/test.esp32-c3-idf.yaml +++ b/tests/components/xpt2046/test.esp32-c3-idf.yaml @@ -25,7 +25,7 @@ touchscreen: update_interval: 50ms threshold: 400 calibration: - x_min: 3860 + x_min: 50 x_max: 280 y_min: 340 y_max: 3860 diff --git a/tests/components/xpt2046/test.esp32-idf.yaml b/tests/components/xpt2046/test.esp32-idf.yaml index f15d1f9b41..e79997146b 100644 --- a/tests/components/xpt2046/test.esp32-idf.yaml +++ b/tests/components/xpt2046/test.esp32-idf.yaml @@ -25,7 +25,7 @@ touchscreen: update_interval: 50ms threshold: 400 calibration: - x_min: 3860 + x_min: 50 x_max: 280 y_min: 340 y_max: 3860 diff --git a/tests/components/xpt2046/test.esp8266-ard.yaml b/tests/components/xpt2046/test.esp8266-ard.yaml index 0daa25ad60..ab71f7b8bc 100644 --- a/tests/components/xpt2046/test.esp8266-ard.yaml +++ b/tests/components/xpt2046/test.esp8266-ard.yaml @@ -25,7 +25,7 @@ touchscreen: update_interval: 50ms threshold: 400 calibration: - x_min: 3860 + x_min: 50 x_max: 280 y_min: 340 y_max: 3860 diff --git a/tests/components/xpt2046/test.rp2040-ard.yaml b/tests/components/xpt2046/test.rp2040-ard.yaml index 8afc45d04d..622e69ac98 100644 --- a/tests/components/xpt2046/test.rp2040-ard.yaml +++ b/tests/components/xpt2046/test.rp2040-ard.yaml @@ -25,8 +25,8 @@ touchscreen: update_interval: 50ms threshold: 400 calibration: - x_min: 3860 - x_max: 280 + x_min: 280 + x_max: 3860 y_min: 340 y_max: 3860 on_touch: From a043022444609d11b2a8b040ae9515e54ce940f2 Mon Sep 17 00:00:00 2001 From: Faidon Liambotis Date: Thu, 31 Oct 2024 05:36:23 +0200 Subject: [PATCH 0572/1052] [font] Add support for "glyphsets" (#7429) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- docker/Dockerfile | 70 +- esphome/components/font/__init__.py | 303 +- requirements.txt | 3 + requirements_optional.txt | 1 - script/ci-custom.py | 2 +- tests/components/font/.gitattributes | 2 + tests/components/font/MatrixChunky8X.bdf | 7461 ++++++++++++++++++++++ tests/components/font/common.yaml | 24 + tests/components/font/test.host.yaml | 34 + tests/components/font/x11.pcf | Bin 0 -> 13368 bytes 10 files changed, 7771 insertions(+), 129 deletions(-) create mode 100644 tests/components/font/.gitattributes create mode 100644 tests/components/font/MatrixChunky8X.bdf create mode 100644 tests/components/font/x11.pcf diff --git a/docker/Dockerfile b/docker/Dockerfile index 52a4794f24..44ee879a12 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -40,25 +40,6 @@ RUN \ libcairo2=1.16.0-7 \ libmagic1=1:5.44-3 \ patch=2.7.6-7 \ - && ( \ - ( \ - [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] && \ - apt-get install -y --no-install-recommends \ - build-essential=12.9 \ - python3-dev=3.11.2-1+b1 \ - zlib1g-dev=1:1.2.13.dfsg-1 \ - libjpeg-dev=1:2.1.5-2 \ - libfreetype-dev=2.12.1+dfsg-5+deb12u3 \ - libssl-dev=3.0.14-1~deb12u2 \ - libffi-dev=3.4.4-1 \ - libopenjp2-7=2.5.0-2 \ - libtiff6=4.5.0-6+deb12u1 \ - cargo=0.66.0+ds1-1 \ - pkg-config=1.8.1-1 \ - gcc-arm-linux-gnueabihf=4:12.2.0-3 \ - ) \ - || [ "$TARGETARCH$TARGETVARIANT" != "armv7" ] \ - ) \ && rm -rf \ /tmp/* \ /var/{cache,log}/* \ @@ -97,15 +78,48 @@ RUN \ # tmpfs is for https://github.com/rust-lang/cargo/issues/8719 COPY requirements.txt requirements_optional.txt / -RUN --mount=type=tmpfs,target=/root/.cargo if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - curl -L https://www.piwheels.org/cp311/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl -o /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl \ - && pip3 install --break-system-packages --no-cache-dir /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl \ - && rm /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl \ - && export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - fi; \ - CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo \ - pip3 install \ - --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt +RUN --mount=type=tmpfs,target=/root/.cargo < len(y_): return 1 - raise cv.Invalid(f"Found duplicate glyph {x}") + return 0 -def validate_glyphs(value): - if isinstance(value, list): - value = cv.Schema([cv.string])(value) - value = cv.Schema([cv.string])(list(value)) +def flatten(lists) -> list: + """ + Given a list of lists, flatten it to a single list of all elements of all lists. + This wraps itertools.chain.from_iterable to make it more readable, and return a list + rather than a single use iterable. + """ + from itertools import chain - value.sort(key=functools.cmp_to_key(glyph_comparator)) - return value + return list(chain.from_iterable(lists)) -font_map = {} +def check_missing_glyphs(file, codepoints: Iterable, warning: bool = False): + """ + Check that the given font file actually contains the requested glyphs + :param file: A Truetype font file + :param codepoints: A list of codepoints to check + :param warning: If true, log a warning instead of raising an exception + """ - -def merge_glyphs(config): - glyphs = [] - glyphs.extend(config[CONF_GLYPHS]) - font_list = [(EFont(config[CONF_FILE], config[CONF_SIZE], config[CONF_GLYPHS]))] - if extras := config.get(CONF_EXTRAS): - extra_fonts = list( - map( - lambda x: EFont(x[CONF_FILE], config[CONF_SIZE], x[CONF_GLYPHS]), extras - ) + font = FONT_CACHE[file] + missing = [chr(x) for x in codepoints if font.get_char_index(x) == 0] + if missing: + # Only list up to 10 missing glyphs + missing.sort(key=functools.cmp_to_key(glyph_comparator)) + count = len(missing) + missing = missing[:10] + missing_str = "\n ".join( + f"{x} ({x.encode('unicode_escape')})" for x in missing ) - font_list.extend(extra_fonts) - for extra in extras: - glyphs.extend(extra[CONF_GLYPHS]) - validate_glyphs(glyphs) - font_map[config[CONF_ID]] = font_list + if count > 10: + missing_str += f"\n and {count - 10} more." + message = f"Font {Path(file).name} is missing {count} glyph{'s' if count != 1 else ''}:\n {missing_str}" + if warning: + _LOGGER.warning(message) + else: + raise cv.Invalid(message) + + +def validate_glyphs(config): + """ + Check for duplicate codepoints, then check that all requested codepoints actually + have glyphs defined in the appropriate font file. + """ + + # Collect all glyph codepoints and flatten to a list of chars + glyphspoints = flatten( + [x[CONF_GLYPHS] for x in config[CONF_EXTRAS]] + config[CONF_GLYPHS] + ) + # Convert a list of strings to a list of chars (one char strings) + glyphspoints = flatten([list(x) for x in glyphspoints]) + if len(set(glyphspoints)) != len(glyphspoints): + duplicates = {x for x in glyphspoints if glyphspoints.count(x) > 1} + dup_str = ", ".join(f"{x} ({x.encode('unicode_escape')})" for x in duplicates) + raise cv.Invalid( + f"Found duplicate glyph{'s' if len(duplicates) != 1 else ''}: {dup_str}" + ) + # convert to codepoints + glyphspoints = {ord(x) for x in glyphspoints} + fileconf = config[CONF_FILE] + setpoints = set( + flatten([glyphsets.unicodes_per_glyphset(x) for x in config[CONF_GLYPHSETS]]) + ) + # Make setpoints and glyphspoints disjoint + setpoints.difference_update(glyphspoints) + if fileconf[CONF_TYPE] == TYPE_LOCAL_BITMAP: + # Pillow only allows 256 glyphs per bitmap font. Not sure if that is a Pillow limitation + # or a file format limitation + if any(x >= 256 for x in setpoints.copy().union(glyphspoints)): + raise cv.Invalid("Codepoints in bitmap fonts must be in the range 0-255") + else: + # for TT fonts, check that glyphs are actually present + # Check extras against their own font, exclude from parent font codepoints + for extra in config[CONF_EXTRAS]: + points = {ord(x) for x in flatten(extra[CONF_GLYPHS])} + glyphspoints.difference_update(points) + setpoints.difference_update(points) + check_missing_glyphs(extra[CONF_FILE][CONF_PATH], points) + + # A named glyph that can't be provided is an error + check_missing_glyphs(fileconf[CONF_PATH], glyphspoints) + # A missing glyph from a set is a warning. + if not config[CONF_IGNORE_MISSING_GLYPHS]: + check_missing_glyphs(fileconf[CONF_PATH], setpoints, warning=True) + + # Populate the default after the above checks so that use of the default doesn't trigger errors + if not config[CONF_GLYPHS] and not config[CONF_GLYPHSETS]: + if fileconf[CONF_TYPE] == TYPE_LOCAL_BITMAP: + config[CONF_GLYPHS] = [DEFAULT_GLYPHS] + else: + # set a default glyphset, intersected with what the font actually offers + font = FONT_CACHE[fileconf[CONF_PATH]] + config[CONF_GLYPHS] = [ + chr(x) + for x in glyphsets.unicodes_per_glyphset(DEFAULT_GLYPHSET) + if font.get_char_index(x) != 0 + ] + return config @@ -120,7 +205,7 @@ def validate_truetype_file(value): ) if not any(map(value.lower().endswith, FONT_EXTENSIONS)): raise cv.Invalid(f"Only {FONT_EXTENSIONS} files are supported.") - return cv.file_(value) + return CORE.relative_config_path(cv.file_(value)) TYPE_LOCAL = "local" @@ -139,6 +224,10 @@ LOCAL_BITMAP_SCHEMA = cv.Schema( } ) +FULLPATH_SCHEMA = cv.maybe_simple_value( + {cv.Required(CONF_PATH): cv.string}, key=CONF_PATH +) + CONF_ITALIC = "italic" FONT_WEIGHTS = { "thin": 100, @@ -167,13 +256,13 @@ def _compute_local_font_path(value: dict) -> Path: return base_dir / key -def get_font_path(value, type) -> Path: - if type == TYPE_GFONTS: +def get_font_path(value, font_type) -> Path: + if font_type == TYPE_GFONTS: name = f"{value[CONF_FAMILY]}@{value[CONF_WEIGHT]}@{value[CONF_ITALIC]}@v1" return external_files.compute_local_file_dir(DOMAIN) / f"{name}.ttf" - if type == TYPE_WEB: + if font_type == TYPE_WEB: return _compute_local_font_path(value) / "font.ttf" - return None + assert False def download_gfont(value): @@ -203,7 +292,7 @@ def download_gfont(value): _LOGGER.debug("download_gfont: ttf_url=%s", ttf_url) external_files.download_content(ttf_url, path) - return value + return FULLPATH_SCHEMA(path) def download_web_font(value): @@ -212,7 +301,7 @@ def download_web_font(value): external_files.download_content(url, path) _LOGGER.debug("download_web_font: path=%s", path) - return value + return FULLPATH_SCHEMA(path) EXTERNAL_FONT_SCHEMA = cv.Schema( @@ -225,7 +314,6 @@ EXTERNAL_FONT_SCHEMA = cv.Schema( } ) - GFONTS_SCHEMA = cv.All( EXTERNAL_FONT_SCHEMA.extend( { @@ -259,10 +347,10 @@ def validate_file_shorthand(value): } if weight is not None: data[CONF_WEIGHT] = weight[1:] - return FILE_SCHEMA(data) + return font_file_schema(data) if value.startswith("http://") or value.startswith("https://"): - return FILE_SCHEMA( + return font_file_schema( { CONF_TYPE: TYPE_WEB, CONF_URL: value, @@ -270,14 +358,15 @@ def validate_file_shorthand(value): ) if value.endswith(".pcf") or value.endswith(".bdf"): - return FILE_SCHEMA( - { - CONF_TYPE: TYPE_LOCAL_BITMAP, - CONF_PATH: value, - } + value = convert_bitmap_to_pillow_font( + CORE.relative_config_path(cv.file_(value)) ) + return { + CONF_TYPE: TYPE_LOCAL_BITMAP, + CONF_PATH: value, + } - return FILE_SCHEMA( + return font_file_schema( { CONF_TYPE: TYPE_LOCAL, CONF_PATH: value, @@ -295,31 +384,35 @@ TYPED_FILE_SCHEMA = cv.typed_schema( ) -def _file_schema(value): +def font_file_schema(value): if isinstance(value, str): return validate_file_shorthand(value) return TYPED_FILE_SCHEMA(value) -FILE_SCHEMA = cv.All(_file_schema) +# Default if no glyphs or glyphsets are provided +DEFAULT_GLYPHSET = "GF_Latin_Kernel" +# default for bitmap fonts +DEFAULT_GLYPHS = ' !"%()+=,-.:/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz' -DEFAULT_GLYPHS = ( - ' !"%()+=,-.:/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' -) CONF_RAW_GLYPH_ID = "raw_glyph_id" FONT_SCHEMA = cv.Schema( { cv.Required(CONF_ID): cv.declare_id(Font), - cv.Required(CONF_FILE): FILE_SCHEMA, - cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs, + cv.Required(CONF_FILE): font_file_schema, + cv.Optional(CONF_GLYPHS, default=[]): cv.ensure_list(cv.string_strict), + cv.Optional(CONF_GLYPHSETS, default=[]): cv.ensure_list( + cv.one_of(*glyphsets.defined_glyphsets()) + ), + cv.Optional(CONF_IGNORE_MISSING_GLYPHS, default=False): cv.boolean, cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1), cv.Optional(CONF_BPP, default=1): cv.one_of(1, 2, 4, 8), - cv.Optional(CONF_EXTRAS): cv.ensure_list( + cv.Optional(CONF_EXTRAS, default=[]): cv.ensure_list( cv.Schema( { - cv.Required(CONF_FILE): FILE_SCHEMA, - cv.Required(CONF_GLYPHS): validate_glyphs, + cv.Required(CONF_FILE): font_file_schema, + cv.Required(CONF_GLYPHS): cv.ensure_list(cv.string_strict), } ) ), @@ -328,7 +421,7 @@ FONT_SCHEMA = cv.Schema( }, ) -CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA, merge_glyphs) +CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA, validate_glyphs) # PIL doesn't provide a consistent interface for both TrueType and bitmap @@ -367,28 +460,20 @@ class BitmapFontWrapper: mask = self.getmask(glyph, mode="1") _, height = mask.size max_height = max(max_height, height) - return (max_height, 0) + return max_height, 0 class EFont: - def __init__(self, file, size, glyphs): - self.glyphs = glyphs + def __init__(self, file, size, codepoints): + self.codepoints = codepoints + path = file[CONF_PATH] + self.name = Path(path).name ftype = file[CONF_TYPE] if ftype == TYPE_LOCAL_BITMAP: - font = load_bitmap_font(CORE.relative_config_path(file[CONF_PATH])) - elif ftype == TYPE_LOCAL: - path = CORE.relative_config_path(file[CONF_PATH]) - font = load_ttf_font(path, size) - elif ftype in (TYPE_GFONTS, TYPE_WEB): - path = get_font_path(file, ftype) - font = load_ttf_font(path, size) + self.font = load_bitmap_font(path) else: - raise cv.Invalid(f"Could not load font: unknown type: {ftype}") - self.font = font - self.ascent, self.descent = font.getmetrics(glyphs) - - def has_glyph(self, glyph): - return glyph in self.glyphs + self.font = load_ttf_font(path, size) + self.ascent, self.descent = self.font.getmetrics(codepoints) def convert_bitmap_to_pillow_font(filepath): @@ -400,6 +485,7 @@ def convert_bitmap_to_pillow_font(filepath): copy_file_if_changed(filepath, local_bitmap_font_file) + local_pil_font_file = local_bitmap_font_file.with_suffix(".pil") with open(local_bitmap_font_file, "rb") as fp: try: try: @@ -409,28 +495,22 @@ def convert_bitmap_to_pillow_font(filepath): p = BdfFontFile.BdfFontFile(fp) # Convert to pillow-formatted fonts, which have a .pil and .pbm extension. - p.save(local_bitmap_font_file) + p.save(local_pil_font_file) except (SyntaxError, OSError) as err: raise core.EsphomeError( f"Failed to parse as bitmap font: '{filepath}': {err}" ) - local_pil_font_file = os.path.splitext(local_bitmap_font_file)[0] + ".pil" - return cv.file_(local_pil_font_file) + return str(local_pil_font_file) def load_bitmap_font(filepath): from PIL import ImageFont - # Convert bpf and pcf files to pillow fonts, first. - pil_font_path = convert_bitmap_to_pillow_font(filepath) - try: - font = ImageFont.load(str(pil_font_path)) + font = ImageFont.load(str(filepath)) except Exception as e: - raise core.EsphomeError( - f"Failed to load bitmap font file: {pil_font_path} : {e}" - ) + raise core.EsphomeError(f"Failed to load bitmap font file: {filepath}: {e}") return BitmapFontWrapper(font) @@ -441,7 +521,7 @@ def load_ttf_font(path, size): try: font = ImageFont.truetype(str(path), size) except Exception as e: - raise core.EsphomeError(f"Could not load truetype file {path}: {e}") + raise core.EsphomeError(f"Could not load TrueType file {path}: {e}") return TrueTypeFontWrapper(font) @@ -456,14 +536,35 @@ class GlyphInfo: async def to_code(config): - glyph_to_font_map = {} - font_list = font_map[config[CONF_ID]] - glyphs = [] - for font in font_list: - glyphs.extend(font.glyphs) - for glyph in font.glyphs: - glyph_to_font_map[glyph] = font - glyphs.sort(key=functools.cmp_to_key(glyph_comparator)) + """ + Collect all glyph codepoints, construct a map from a codepoint to a font file. + Codepoints are either explicit (glyphs key in top level or extras) or part of a glyphset. + Codepoints listed in extras use the extra font and override codepoints from glyphsets. + Achieve this by processing the base codepoints first, then the extras + """ + + # get the codepoints from glyphsets and flatten to a set of chrs. + point_set: set[str] = { + chr(x) + for x in flatten( + [glyphsets.unicodes_per_glyphset(x) for x in config[CONF_GLYPHSETS]] + ) + } + # get the codepoints from the glyphs key, flatten to a list of chrs and combine with the points from glyphsets + point_set.update(flatten(config[CONF_GLYPHS])) + size = config[CONF_SIZE] + # Create the codepoint to font file map + base_font = EFont(config[CONF_FILE], size, point_set) + point_font_map: dict[str, EFont] = {c: base_font for c in point_set} + # process extras, updating the map and extending the codepoint list + for extra in config[CONF_EXTRAS]: + extra_points = flatten(extra[CONF_GLYPHS]) + point_set.update(extra_points) + extra_font = EFont(extra[CONF_FILE], size, extra_points) + point_font_map.update({c: extra_font for c in extra_points}) + + codepoints = list(point_set) + codepoints.sort(key=functools.cmp_to_key(glyph_comparator)) glyph_args = {} data = [] bpp = config[CONF_BPP] @@ -473,10 +574,11 @@ async def to_code(config): else: mode = "L" scale = 256 // (1 << bpp) - for glyph in glyphs: - font = glyph_to_font_map[glyph].font - mask = font.getmask(glyph, mode=mode) - offset_x, offset_y = font.getoffset(glyph) + # create the data array for all glyphs + for codepoint in codepoints: + font = point_font_map[codepoint] + mask = font.font.getmask(codepoint, mode=mode) + offset_x, offset_y = font.font.getoffset(codepoint) width, height = mask.size glyph_data = [0] * ((height * width * bpp + 7) // 8) pos = 0 @@ -487,31 +589,34 @@ async def to_code(config): if pixel & (1 << (bpp - bit_num - 1)): glyph_data[pos // 8] |= 0x80 >> (pos % 8) pos += 1 - glyph_args[glyph] = GlyphInfo(len(data), offset_x, offset_y, width, height) + glyph_args[codepoint] = GlyphInfo(len(data), offset_x, offset_y, width, height) data += glyph_data rhs = [HexInt(x) for x in data] prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + # Create the glyph table that points to data in the above array. glyph_initializer = [] - for glyph in glyphs: + for codepoint in codepoints: glyph_initializer.append( cg.StructInitializer( GlyphData, ( "a_char", - cg.RawExpression(f"(const uint8_t *){cpp_string_escape(glyph)}"), + cg.RawExpression( + f"(const uint8_t *){cpp_string_escape(codepoint)}" + ), ), ( "data", cg.RawExpression( - f"{str(prog_arr)} + {str(glyph_args[glyph].data_len)}" + f"{str(prog_arr)} + {str(glyph_args[codepoint].data_len)}" ), ), - ("offset_x", glyph_args[glyph].offset_x), - ("offset_y", glyph_args[glyph].offset_y), - ("width", glyph_args[glyph].width), - ("height", glyph_args[glyph].height), + ("offset_x", glyph_args[codepoint].offset_x), + ("offset_y", glyph_args[codepoint].offset_y), + ("width", glyph_args[codepoint].width), + ("height", glyph_args[codepoint].height), ) ) @@ -521,7 +626,7 @@ async def to_code(config): config[CONF_ID], glyphs, len(glyph_initializer), - font_list[0].ascent, - font_list[0].ascent + font_list[0].descent, + base_font.ascent, + base_font.ascent + base_font.descent, bpp, ) diff --git a/requirements.txt b/requirements.txt index 8cc26e4da0..e11e629743 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,9 @@ aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import +glyphsets==1.0.0 +pillow==10.4.0 +freetype-py==2.5.1 # esp-idf requires this, but doesn't bundle it by default # https://github.com/espressif/esp-idf/blob/220590d599e134d7a5e7f1e683cc4550349ffbf8/requirements.txt#L24 diff --git a/requirements_optional.txt b/requirements_optional.txt index 2d57c5fd96..7416753d55 100644 --- a/requirements_optional.txt +++ b/requirements_optional.txt @@ -1,2 +1 @@ -pillow==10.4.0 cairosvg==2.7.1 diff --git a/script/ci-custom.py b/script/ci-custom.py index 9a97d3e4a8..81e3da311a 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -58,7 +58,7 @@ file_types = ( ) cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc") py_include = ("*.py",) -ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf") +ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf", ".pcf") LINT_FILE_CHECKS = [] LINT_CONTENT_CHECKS = [] diff --git a/tests/components/font/.gitattributes b/tests/components/font/.gitattributes new file mode 100644 index 0000000000..18d9a389e8 --- /dev/null +++ b/tests/components/font/.gitattributes @@ -0,0 +1,2 @@ +*.pcf -text + diff --git a/tests/components/font/MatrixChunky8X.bdf b/tests/components/font/MatrixChunky8X.bdf new file mode 100644 index 0000000000..89b3683180 --- /dev/null +++ b/tests/components/font/MatrixChunky8X.bdf @@ -0,0 +1,7461 @@ +STARTFONT 2.1 +FONT -Trip5-MatrixChunky8X-Medium-R-Normal--8-80-75-75-P-40-ISO10646-1 +SIZE 8 75 75 +FONTBOUNDINGBOX 8 8 -1 0 +COMMENT "Generated by fontforge, http://fontforge.sourceforge.net" +COMMENT "Trip5" +COMMENT "Conventional Chaos" +COMMENT "CC-BY" +STARTPROPERTIES 25 +FOUNDRY "Conventional Chaos" +FAMILY_NAME "MatrixChunky8X" +FONT_NAME "MatrixChunky8X" +FACE_NAME "MatrixChunky8X" +COPYRIGHT "https://github.com/trip5/Matrix-Fonts" +FONT_VERSION "001.000" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 8 +POINT_SIZE 80 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "P" +AVERAGE_WIDTH 40 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +CHARSET_COLLECTIONS "ISO8859-2 ISO8859-9 ISO8859-4 ISO10646-1" +FONT_ASCENT 8 +FONT_DESCENT 0 +UNDERLINE_POSITION 0 +UNDERLINE_THICKNESS 1 +X_HEIGHT 6 +CAP_HEIGHT 8 +ENDPROPERTIES +CHARS 535 +STARTCHAR uni0000 +ENCODING 0 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 2 +BITMAP +A0 +00 +40 +00 +A0 +ENDCHAR +STARTCHAR space +ENCODING 32 +SWIDTH 250 0 +DWIDTH 2 0 +BBX 1 1 0 0 +BITMAP +00 +ENDCHAR +STARTCHAR exclam +ENCODING 33 +SWIDTH 250 0 +DWIDTH 2 0 +BBX 1 8 0 0 +BITMAP +80 +80 +80 +80 +80 +80 +00 +80 +ENDCHAR +STARTCHAR quotedbl +ENCODING 34 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 2 0 6 +BITMAP +A0 +A0 +ENDCHAR +STARTCHAR numbersign +ENCODING 35 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +50 +50 +F8 +50 +50 +F8 +50 +50 +ENDCHAR +STARTCHAR dollar +ENCODING 36 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +E0 +80 +E0 +20 +20 +E0 +40 +ENDCHAR +STARTCHAR percent +ENCODING 37 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +20 +40 +40 +80 +A0 +A0 +ENDCHAR +STARTCHAR ampersand +ENCODING 38 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +E0 +A0 +A0 +40 +B0 +A0 +B0 +D0 +ENDCHAR +STARTCHAR quotesingle +ENCODING 39 +SWIDTH 250 0 +DWIDTH 2 0 +BBX 1 3 0 5 +BITMAP +80 +80 +80 +ENDCHAR +STARTCHAR parenleft +ENCODING 40 +SWIDTH 1000 0 +DWIDTH 3 0 +BBX 2 8 0 0 +BITMAP +40 +40 +80 +80 +80 +80 +40 +40 +ENDCHAR +STARTCHAR parenright +ENCODING 41 +SWIDTH 1000 0 +DWIDTH 3 0 +BBX 2 8 0 0 +BITMAP +80 +80 +40 +40 +40 +40 +80 +80 +ENDCHAR +STARTCHAR asterisk +ENCODING 42 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 4 0 4 +BITMAP +90 +60 +60 +90 +ENDCHAR +STARTCHAR plus +ENCODING 43 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 3 0 3 +BITMAP +40 +E0 +40 +ENDCHAR +STARTCHAR comma +ENCODING 44 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 2 0 0 +BITMAP +80 +80 +ENDCHAR +STARTCHAR hyphen +ENCODING 45 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 1 0 4 +BITMAP +E0 +ENDCHAR +STARTCHAR period +ENCODING 46 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 1 0 0 +BITMAP +80 +ENDCHAR +STARTCHAR slash +ENCODING 47 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +20 +40 +40 +40 +80 +80 +80 +ENDCHAR +STARTCHAR zero +ENCODING 48 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR one +ENCODING 49 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +C0 +40 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR two +ENCODING 50 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +20 +20 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR three +ENCODING 51 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +20 +20 +E0 +20 +20 +20 +E0 +ENDCHAR +STARTCHAR four +ENCODING 52 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +E0 +20 +20 +20 +20 +ENDCHAR +STARTCHAR five +ENCODING 53 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +20 +20 +20 +E0 +ENDCHAR +STARTCHAR six +ENCODING 54 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR seven +ENCODING 55 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +20 +20 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR eight +ENCODING 56 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR nine +ENCODING 57 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +20 +20 +20 +E0 +ENDCHAR +STARTCHAR colon +ENCODING 58 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 3 0 3 +BITMAP +80 +00 +80 +ENDCHAR +STARTCHAR semicolon +ENCODING 59 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 4 0 2 +BITMAP +80 +00 +80 +80 +ENDCHAR +STARTCHAR less +ENCODING 60 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 2 +BITMAP +20 +40 +80 +40 +20 +ENDCHAR +STARTCHAR equal +ENCODING 61 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 3 0 3 +BITMAP +E0 +00 +E0 +ENDCHAR +STARTCHAR greater +ENCODING 62 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 2 +BITMAP +80 +40 +20 +40 +80 +ENDCHAR +STARTCHAR question +ENCODING 63 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +20 +60 +40 +40 +00 +40 +ENDCHAR +STARTCHAR at +ENCODING 64 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +B0 +B0 +80 +80 +F0 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +C0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +80 +80 +80 +80 +A0 +E0 +ENDCHAR +STARTCHAR D +ENCODING 68 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +C0 +A0 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR E +ENCODING 69 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR F +ENCODING 70 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR G +ENCODING 71 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +E0 +80 +80 +B0 +90 +90 +90 +F0 +ENDCHAR +STARTCHAR H +ENCODING 72 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +90 +90 +90 +F0 +90 +90 +90 +90 +ENDCHAR +STARTCHAR I +ENCODING 73 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR J +ENCODING 74 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +20 +20 +20 +20 +A0 +A0 +E0 +ENDCHAR +STARTCHAR K +ENCODING 75 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +C0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR L +ENCODING 76 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +80 +80 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR M +ENCODING 77 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +88 +D8 +A8 +A8 +88 +88 +88 +88 +ENDCHAR +STARTCHAR N +ENCODING 78 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +90 +90 +D0 +B0 +90 +90 +90 +90 +ENDCHAR +STARTCHAR O +ENCODING 79 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +90 +90 +90 +90 +F0 +ENDCHAR +STARTCHAR P +ENCODING 80 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR Q +ENCODING 81 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +90 +B0 +B0 +A0 +D0 +ENDCHAR +STARTCHAR R +ENCODING 82 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +C0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR S +ENCODING 83 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +80 +E0 +20 +20 +A0 +E0 +ENDCHAR +STARTCHAR T +ENCODING 84 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR U +ENCODING 85 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR V +ENCODING 86 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +A0 +A0 +A0 +E0 +40 +ENDCHAR +STARTCHAR W +ENCODING 87 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +A8 +A8 +A8 +A8 +A8 +A8 +F8 +50 +ENDCHAR +STARTCHAR X +ENCODING 88 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +40 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR Y +ENCODING 89 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +E0 +40 +40 +40 +40 +ENDCHAR +STARTCHAR Z +ENCODING 90 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +20 +20 +40 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR bracketleft +ENCODING 91 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +80 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR backslash +ENCODING 92 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +40 +40 +40 +20 +20 +20 +ENDCHAR +STARTCHAR bracketright +ENCODING 93 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +20 +20 +20 +20 +20 +20 +E0 +ENDCHAR +STARTCHAR asciicircum +ENCODING 94 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 3 0 5 +BITMAP +40 +E0 +A0 +ENDCHAR +STARTCHAR underscore +ENCODING 95 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 1 0 0 +BITMAP +E0 +ENDCHAR +STARTCHAR grave +ENCODING 96 +SWIDTH 1000 0 +DWIDTH 3 0 +BBX 2 2 0 6 +BITMAP +80 +40 +ENDCHAR +STARTCHAR a +ENCODING 97 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR b +ENCODING 98 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +80 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR c +ENCODING 99 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR d +ENCODING 100 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +20 +20 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR e +ENCODING 101 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR f +ENCODING 102 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +60 +40 +40 +E0 +40 +40 +40 +ENDCHAR +STARTCHAR g +ENCODING 103 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR h +ENCODING 104 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +80 +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR i +ENCODING 105 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR j +ENCODING 106 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +20 +00 +20 +20 +20 +A0 +E0 +ENDCHAR +STARTCHAR k +ENCODING 107 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +A0 +A0 +C0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR l +ENCODING 108 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +C0 +40 +40 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR m +ENCODING 109 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +F8 +A8 +A8 +A8 +A8 +ENDCHAR +STARTCHAR n +ENCODING 110 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR o +ENCODING 111 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR p +ENCODING 112 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +E0 +80 +80 +ENDCHAR +STARTCHAR q +ENCODING 113 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +E0 +20 +20 +ENDCHAR +STARTCHAR r +ENCODING 114 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR s +ENCODING 115 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +E0 +20 +E0 +ENDCHAR +STARTCHAR t +ENCODING 116 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +40 +40 +E0 +40 +40 +40 +60 +ENDCHAR +STARTCHAR u +ENCODING 117 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR v +ENCODING 118 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +A0 +E0 +40 +ENDCHAR +STARTCHAR w +ENCODING 119 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +A8 +A8 +A8 +F8 +50 +ENDCHAR +STARTCHAR x +ENCODING 120 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +40 +A0 +A0 +ENDCHAR +STARTCHAR y +ENCODING 121 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR z +ENCODING 122 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +20 +40 +80 +E0 +ENDCHAR +STARTCHAR braceleft +ENCODING 123 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +60 +40 +40 +C0 +40 +40 +40 +60 +ENDCHAR +STARTCHAR bar +ENCODING 124 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 8 0 0 +BITMAP +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR braceright +ENCODING 125 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +C0 +40 +40 +60 +40 +40 +40 +C0 +ENDCHAR +STARTCHAR asciitilde +ENCODING 126 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 2 0 3 +BITMAP +60 +C0 +ENDCHAR +STARTCHAR exclamdown +ENCODING 161 +SWIDTH 250 0 +DWIDTH 2 0 +BBX 1 8 0 0 +BITMAP +80 +80 +00 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR cent +ENCODING 162 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +E0 +80 +80 +80 +E0 +40 +ENDCHAR +STARTCHAR sterling +ENCODING 163 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +70 +50 +40 +E0 +40 +40 +40 +F0 +ENDCHAR +STARTCHAR currency +ENCODING 164 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 6 0 1 +BITMAP +F0 +60 +90 +90 +60 +F0 +ENDCHAR +STARTCHAR yen +ENCODING 165 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +E0 +40 +E0 +40 +40 +40 +ENDCHAR +STARTCHAR brokenbar +ENCODING 166 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 8 0 0 +BITMAP +80 +80 +80 +00 +80 +80 +80 +80 +ENDCHAR +STARTCHAR section +ENCODING 167 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +60 +80 +E0 +A0 +A0 +E0 +20 +C0 +ENDCHAR +STARTCHAR dieresis +ENCODING 168 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +E0 +A0 +C0 +A0 +A0 +E0 +40 +ENDCHAR +STARTCHAR copyright +ENCODING 169 +SWIDTH 1000 0 +DWIDTH 7 0 +BBX 6 7 0 1 +BITMAP +78 +CC +B4 +A4 +B4 +CC +78 +ENDCHAR +STARTCHAR ordfeminine +ENCODING 170 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 3 +BITMAP +60 +A0 +E0 +00 +E0 +ENDCHAR +STARTCHAR guillemotleft +ENCODING 171 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 3 0 3 +BITMAP +50 +F0 +50 +ENDCHAR +STARTCHAR logicalnot +ENCODING 172 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +A8 +20 +20 +20 +28 +28 +38 +ENDCHAR +STARTCHAR uni00AD +ENCODING 173 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 1 0 4 +BITMAP +E0 +ENDCHAR +STARTCHAR registered +ENCODING 174 +SWIDTH 1000 0 +DWIDTH 7 0 +BBX 6 7 0 1 +BITMAP +78 +CC +B4 +A4 +A4 +CC +78 +ENDCHAR +STARTCHAR macron +ENCODING 175 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +A0 +20 +38 +20 +20 +20 +20 +ENDCHAR +STARTCHAR degree +ENCODING 176 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 3 0 5 +BITMAP +E0 +A0 +E0 +ENDCHAR +STARTCHAR plusminus +ENCODING 177 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 2 +BITMAP +40 +E0 +40 +00 +E0 +ENDCHAR +STARTCHAR uni00B2 +ENCODING 178 +SWIDTH 1000 0 +DWIDTH 3 0 +BBX 2 5 0 3 +BITMAP +C0 +40 +C0 +80 +C0 +ENDCHAR +STARTCHAR uni00B3 +ENCODING 179 +SWIDTH 1000 0 +DWIDTH 3 0 +BBX 2 5 0 3 +BITMAP +C0 +40 +C0 +40 +C0 +ENDCHAR +STARTCHAR acute +ENCODING 180 +SWIDTH 1000 0 +DWIDTH 3 0 +BBX 2 2 0 6 +BITMAP +40 +C0 +ENDCHAR +STARTCHAR mu +ENCODING 181 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +A0 +A0 +A0 +A0 +E0 +80 +ENDCHAR +STARTCHAR paragraph +ENCODING 182 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +D0 +F0 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR periodcentered +ENCODING 183 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 1 0 4 +BITMAP +80 +ENDCHAR +STARTCHAR cedilla +ENCODING 184 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +00 +E0 +00 +00 +00 +E0 +ENDCHAR +STARTCHAR uni00B9 +ENCODING 185 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 5 0 3 +BITMAP +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR ordmasculine +ENCODING 186 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 5 0 3 +BITMAP +E0 +A0 +E0 +00 +E0 +ENDCHAR +STARTCHAR guillemotright +ENCODING 187 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 3 0 3 +BITMAP +A0 +F0 +A0 +ENDCHAR +STARTCHAR onequarter +ENCODING 188 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +30 +60 +40 +F0 +40 +40 +60 +30 +ENDCHAR +STARTCHAR onehalf +ENCODING 189 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +54 +54 +FE +54 +54 +54 +7C +28 +ENDCHAR +STARTCHAR threequarters +ENCODING 190 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +70 +50 +10 +F8 +40 +40 +50 +70 +ENDCHAR +STARTCHAR questiondown +ENCODING 191 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +40 +40 +C0 +80 +A0 +E0 +ENDCHAR +STARTCHAR Agrave +ENCODING 192 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +40 +00 +E0 +A0 +E0 +A0 +A0 +ENDCHAR +STARTCHAR Aacute +ENCODING 193 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +A0 +E0 +A0 +A0 +ENDCHAR +STARTCHAR Acircumflex +ENCODING 194 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +A0 +E0 +A0 +A0 +ENDCHAR +STARTCHAR Atilde +ENCODING 195 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +A0 +E0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR Adieresis +ENCODING 196 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +A0 +E0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR Aring +ENCODING 197 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +E0 +A0 +E0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR AE +ENCODING 198 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +F8 +A0 +A0 +F8 +A0 +A0 +A0 +B8 +ENDCHAR +STARTCHAR Ccedilla +ENCODING 199 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +80 +80 +80 +E0 +40 +ENDCHAR +STARTCHAR Egrave +ENCODING 200 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +40 +00 +E0 +80 +E0 +80 +E0 +ENDCHAR +STARTCHAR Eacute +ENCODING 201 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +80 +E0 +80 +E0 +ENDCHAR +STARTCHAR Ecircumflex +ENCODING 202 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +80 +E0 +80 +E0 +ENDCHAR +STARTCHAR Edieresis +ENCODING 203 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +80 +E0 +80 +80 +E0 +ENDCHAR +STARTCHAR Igrave +ENCODING 204 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +40 +00 +E0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Iacute +ENCODING 205 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Icircumflex +ENCODING 206 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Idieresis +ENCODING 207 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Eth +ENCODING 208 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +60 +50 +50 +F0 +50 +50 +50 +60 +ENDCHAR +STARTCHAR Ntilde +ENCODING 209 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +60 +00 +90 +D0 +B0 +90 +90 +90 +ENDCHAR +STARTCHAR Ograve +ENCODING 210 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +20 +00 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Oacute +ENCODING 211 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +80 +00 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Ocircumflex +ENCODING 212 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Otilde +ENCODING 213 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Odieresis +ENCODING 214 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR multiply +ENCODING 215 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 3 0 3 +BITMAP +A0 +40 +A0 +ENDCHAR +STARTCHAR Oslash +ENCODING 216 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +B0 +D0 +90 +90 +F0 +ENDCHAR +STARTCHAR Ugrave +ENCODING 217 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +40 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Uacute +ENCODING 218 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Ucircumflex +ENCODING 219 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Udieresis +ENCODING 220 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Yacute +ENCODING 221 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +A0 +A0 +E0 +40 +40 +ENDCHAR +STARTCHAR Thorn +ENCODING 222 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +E0 +A0 +A0 +E0 +80 +80 +ENDCHAR +STARTCHAR germandbls +ENCODING 223 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +C0 +A0 +A0 +E0 +80 +80 +ENDCHAR +STARTCHAR agrave +ENCODING 224 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +20 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR aacute +ENCODING 225 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +80 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR acircumflex +ENCODING 226 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR atilde +ENCODING 227 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR adieresis +ENCODING 228 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR aring +ENCODING 229 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR ae +ENCODING 230 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +F8 +28 +F8 +A0 +F8 +ENDCHAR +STARTCHAR ccedilla +ENCODING 231 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +80 +E0 +40 +ENDCHAR +STARTCHAR egrave +ENCODING 232 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +20 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR eacute +ENCODING 233 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +80 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR ecircumflex +ENCODING 234 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR edieresis +ENCODING 235 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR igrave +ENCODING 236 +SWIDTH 375 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +40 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR iacute +ENCODING 237 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR icircumflex +ENCODING 238 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR idieresis +ENCODING 239 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR eth +ENCODING 240 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +A0 +20 +E0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR ntilde +ENCODING 241 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR ograve +ENCODING 242 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +80 +40 +00 +E0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR oacute +ENCODING 243 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +20 +40 +00 +E0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR ocircumflex +ENCODING 244 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +A0 +00 +E0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR otilde +ENCODING 245 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +E0 +00 +E0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR odieresis +ENCODING 246 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +A0 +00 +E0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR divide +ENCODING 247 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 2 +BITMAP +40 +00 +E0 +00 +40 +ENDCHAR +STARTCHAR oslash +ENCODING 248 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +F0 +B0 +D0 +90 +F0 +ENDCHAR +STARTCHAR ugrave +ENCODING 249 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +80 +40 +00 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uacute +ENCODING 250 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +20 +40 +00 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR ucircumflex +ENCODING 251 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +A0 +00 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR udieresis +ENCODING 252 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +A0 +00 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR yacute +ENCODING 253 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +A0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR thorn +ENCODING 254 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +80 +E0 +A0 +A0 +E0 +80 +ENDCHAR +STARTCHAR ydieresis +ENCODING 255 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +A0 +A0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR Amacron +ENCODING 256 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +A0 +E0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR amacron +ENCODING 257 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR Abreve +ENCODING 258 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +E0 +A0 +A0 +ENDCHAR +STARTCHAR abreve +ENCODING 259 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR Aogonek +ENCODING 260 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 4 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +A0 +A0 +A0 +30 +ENDCHAR +STARTCHAR aogonek +ENCODING 261 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 4 6 0 0 +BITMAP +E0 +20 +E0 +A0 +E0 +30 +ENDCHAR +STARTCHAR Cacute +ENCODING 262 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +80 +00 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR cacute +ENCODING 263 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +80 +00 +E0 +80 +80 +E0 +ENDCHAR +STARTCHAR Ccircumflex +ENCODING 264 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR ccircumflex +ENCODING 265 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +A0 +00 +E0 +80 +80 +E0 +ENDCHAR +STARTCHAR Cdotaccent +ENCODING 266 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +E0 +80 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR cdotaccent +ENCODING 267 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR Ccaron +ENCODING 268 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR ccaron +ENCODING 269 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +40 +00 +E0 +80 +80 +E0 +ENDCHAR +STARTCHAR Dcaron +ENCODING 270 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +C0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR dcaron +ENCODING 271 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +30 +30 +20 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Dcroat +ENCODING 272 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +60 +50 +50 +F0 +50 +50 +50 +70 +ENDCHAR +STARTCHAR dcroat +ENCODING 273 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +20 +70 +20 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Emacron +ENCODING 274 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +80 +E0 +80 +80 +E0 +ENDCHAR +STARTCHAR emacron +ENCODING 275 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR Ebreve +ENCODING 276 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +80 +E0 +80 +E0 +ENDCHAR +STARTCHAR ebreve +ENCODING 277 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR Edotaccent +ENCODING 278 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +E0 +80 +E0 +80 +80 +E0 +ENDCHAR +STARTCHAR edotaccent +ENCODING 279 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR Eogonek +ENCODING 280 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 4 8 0 0 +BITMAP +E0 +80 +80 +E0 +80 +80 +E0 +30 +ENDCHAR +STARTCHAR eogonek +ENCODING 281 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +E0 +A0 +E0 +80 +E0 +40 +ENDCHAR +STARTCHAR Ecaron +ENCODING 282 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +80 +E0 +80 +E0 +ENDCHAR +STARTCHAR ecaron +ENCODING 283 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR Gcircumflex +ENCODING 284 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +40 +A0 +00 +E0 +80 +B0 +90 +F0 +ENDCHAR +STARTCHAR gcircumflex +ENCODING 285 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR Gbreve +ENCODING 286 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +A0 +40 +00 +E0 +80 +B0 +90 +F0 +ENDCHAR +STARTCHAR gbreve +ENCODING 287 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR Gdotaccent +ENCODING 288 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +40 +00 +E0 +80 +80 +B0 +90 +F0 +ENDCHAR +STARTCHAR gdotaccent +ENCODING 289 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +E0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR uni0122 +ENCODING 290 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +E0 +80 +80 +B0 +90 +90 +F0 +40 +ENDCHAR +STARTCHAR uni0123 +ENCODING 291 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +40 +00 +E0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR Hcircumflex +ENCODING 292 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +A0 +A0 +E0 +A0 +A0 +ENDCHAR +STARTCHAR hcircumflex +ENCODING 293 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +80 +80 +E0 +A0 +A0 +ENDCHAR +STARTCHAR Hbar +ENCODING 294 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +50 +F8 +50 +70 +50 +50 +50 +50 +ENDCHAR +STARTCHAR hbar +ENCODING 295 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +40 +E0 +40 +40 +70 +50 +50 +50 +ENDCHAR +STARTCHAR Itilde +ENCODING 296 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR itilde +ENCODING 297 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Imacron +ENCODING 298 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR imacron +ENCODING 299 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Ibreve +ENCODING 300 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR ibreve +ENCODING 301 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Iogonek +ENCODING 302 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +E0 +40 +ENDCHAR +STARTCHAR iogonek +ENCODING 303 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +C0 +40 +40 +E0 +20 +ENDCHAR +STARTCHAR Idotaccent +ENCODING 304 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +E0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR dotlessi +ENCODING 305 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR IJ +ENCODING 306 +SWIDTH 875 0 +DWIDTH 7 0 +BBX 6 8 0 0 +BITMAP +E4 +44 +44 +44 +44 +54 +54 +FC +ENDCHAR +STARTCHAR ij +ENCODING 307 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 5 7 -1 0 +BITMAP +48 +00 +C8 +48 +48 +68 +F8 +ENDCHAR +STARTCHAR Jcircumflex +ENCODING 308 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +20 +50 +00 +20 +20 +A0 +A0 +E0 +ENDCHAR +STARTCHAR jcircumflex +ENCODING 309 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 7 0 0 +BITMAP +20 +50 +00 +20 +20 +A0 +E0 +ENDCHAR +STARTCHAR uni0136 +ENCODING 310 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +C0 +A0 +A0 +A0 +40 +ENDCHAR +STARTCHAR uni0137 +ENCODING 311 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +A0 +A0 +C0 +A0 +A0 +40 +ENDCHAR +STARTCHAR kgreenlandic +ENCODING 312 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +A0 +A0 +C0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR Lacute +ENCODING 313 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +80 +00 +80 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR lacute +ENCODING 314 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +80 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR uni013B +ENCODING 315 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +80 +80 +80 +80 +E0 +40 +ENDCHAR +STARTCHAR uni013C +ENCODING 316 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +C0 +40 +40 +40 +40 +40 +E0 +40 +ENDCHAR +STARTCHAR Lcaron +ENCODING 317 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +80 +80 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR lcaron +ENCODING 318 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +D0 +50 +40 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Ldot +ENCODING 319 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +A0 +80 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR ldot +ENCODING 320 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +C0 +40 +60 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Lslash +ENCODING 321 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +40 +40 +60 +C0 +40 +40 +40 +70 +ENDCHAR +STARTCHAR lslash +ENCODING 322 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +C0 +40 +60 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Nacute +ENCODING 323 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +20 +40 +00 +90 +D0 +B0 +90 +90 +ENDCHAR +STARTCHAR nacute +ENCODING 324 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR uni0145 +ENCODING 325 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +90 +90 +D0 +B0 +90 +90 +90 +40 +ENDCHAR +STARTCHAR uni0146 +ENCODING 326 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +A0 +40 +ENDCHAR +STARTCHAR Ncaron +ENCODING 327 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +A0 +40 +00 +90 +D0 +B0 +90 +90 +ENDCHAR +STARTCHAR ncaron +ENCODING 328 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR napostrophe +ENCODING 329 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +80 +80 +00 +70 +50 +50 +50 +50 +ENDCHAR +STARTCHAR Eng +ENCODING 330 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +90 +90 +90 +90 +20 +ENDCHAR +STARTCHAR eng +ENCODING 331 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +E0 +A0 +A0 +A0 +A0 +20 +ENDCHAR +STARTCHAR Omacron +ENCODING 332 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR omacron +ENCODING 333 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Obreve +ENCODING 334 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR obreve +ENCODING 335 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Ohungarumlaut +ENCODING 336 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR ohungarumlaut +ENCODING 337 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR OE +ENCODING 338 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +F8 +A0 +A0 +B8 +A0 +A0 +A0 +F8 +ENDCHAR +STARTCHAR oe +ENCODING 339 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +F8 +A8 +B8 +A0 +F8 +ENDCHAR +STARTCHAR Racute +ENCODING 340 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +A0 +C0 +A0 +A0 +ENDCHAR +STARTCHAR racute +ENCODING 341 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR uni0156 +ENCODING 342 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +C0 +A0 +A0 +A0 +40 +ENDCHAR +STARTCHAR uni0157 +ENCODING 343 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +E0 +80 +80 +80 +80 +40 +ENDCHAR +STARTCHAR Rcaron +ENCODING 344 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +A0 +C0 +A0 +A0 +ENDCHAR +STARTCHAR rcaron +ENCODING 345 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR Sacute +ENCODING 346 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +80 +00 +E0 +80 +E0 +20 +E0 +ENDCHAR +STARTCHAR sacute +ENCODING 347 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +80 +E0 +20 +E0 +ENDCHAR +STARTCHAR Scircumflex +ENCODING 348 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +80 +E0 +20 +E0 +ENDCHAR +STARTCHAR scircumflex +ENCODING 349 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +E0 +80 +E0 +20 +E0 +ENDCHAR +STARTCHAR Scedilla +ENCODING 350 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +20 +20 +E0 +40 +ENDCHAR +STARTCHAR scedilla +ENCODING 351 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +E0 +80 +E0 +20 +E0 +40 +ENDCHAR +STARTCHAR Scaron +ENCODING 352 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +80 +E0 +20 +E0 +ENDCHAR +STARTCHAR scaron +ENCODING 353 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +80 +E0 +20 +E0 +ENDCHAR +STARTCHAR uni0162 +ENCODING 354 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +60 +20 +ENDCHAR +STARTCHAR uni0163 +ENCODING 355 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +40 +E0 +40 +40 +40 +60 +20 +ENDCHAR +STARTCHAR Tcaron +ENCODING 356 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR tcaron +ENCODING 357 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +50 +50 +40 +E0 +40 +40 +40 +60 +ENDCHAR +STARTCHAR Tbar +ENCODING 358 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +E0 +40 +40 +40 +40 +ENDCHAR +STARTCHAR tbar +ENCODING 359 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +40 +E0 +40 +E0 +40 +40 +60 +ENDCHAR +STARTCHAR Utilde +ENCODING 360 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR utilde +ENCODING 361 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Umacron +ENCODING 362 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR umacron +ENCODING 363 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +E0 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Ubreve +ENCODING 364 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR ubreve +ENCODING 365 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +40 +00 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Uring +ENCODING 366 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uring +ENCODING 367 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Uhungarumlaut +ENCODING 368 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +A0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uhungarumlaut +ENCODING 369 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Uogonek +ENCODING 370 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +A0 +A0 +A0 +E0 +20 +ENDCHAR +STARTCHAR uogonek +ENCODING 371 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +A0 +E0 +20 +ENDCHAR +STARTCHAR Wcircumflex +ENCODING 372 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +50 +00 +A8 +A8 +A8 +F8 +50 +ENDCHAR +STARTCHAR wcircumflex +ENCODING 373 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +20 +50 +00 +A8 +A8 +F8 +50 +ENDCHAR +STARTCHAR Ycircumflex +ENCODING 374 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +A0 +A0 +E0 +40 +40 +ENDCHAR +STARTCHAR ycircumflex +ENCODING 375 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +A0 +00 +A0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR Ydieresis +ENCODING 376 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +A0 +A0 +E0 +40 +40 +40 +ENDCHAR +STARTCHAR Zacute +ENCODING 377 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +20 +40 +80 +E0 +ENDCHAR +STARTCHAR zacute +ENCODING 378 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +20 +40 +80 +E0 +ENDCHAR +STARTCHAR Zdotaccent +ENCODING 379 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +E0 +20 +40 +80 +80 +E0 +ENDCHAR +STARTCHAR zdotaccent +ENCODING 380 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +E0 +20 +40 +80 +E0 +ENDCHAR +STARTCHAR Zcaron +ENCODING 381 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +20 +40 +80 +E0 +ENDCHAR +STARTCHAR zcaron +ENCODING 382 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +40 +00 +E0 +20 +40 +80 +E0 +ENDCHAR +STARTCHAR longs +ENCODING 383 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR Alphatonos +ENCODING 902 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +A8 +28 +38 +28 +28 +28 +28 +ENDCHAR +STARTCHAR Epsilontonos +ENCODING 904 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +A0 +20 +38 +20 +20 +20 +38 +ENDCHAR +STARTCHAR Etatonos +ENCODING 905 +SWIDTH 875 0 +DWIDTH 7 0 +BBX 6 8 0 0 +BITMAP +A4 +A4 +24 +3C +24 +24 +24 +24 +ENDCHAR +STARTCHAR Iotatonos +ENCODING 906 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +90 +10 +10 +10 +10 +10 +38 +ENDCHAR +STARTCHAR Omicrontonos +ENCODING 908 +SWIDTH 875 0 +DWIDTH 7 0 +BBX 6 8 0 0 +BITMAP +BC +A4 +24 +24 +24 +24 +24 +3C +ENDCHAR +STARTCHAR Upsilontonos +ENCODING 910 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +A8 +A8 +28 +38 +10 +10 +10 +10 +ENDCHAR +STARTCHAR Omegatonos +ENCODING 911 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +BE +A2 +22 +22 +22 +36 +14 +36 +ENDCHAR +STARTCHAR Alpha +ENCODING 913 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR Beta +ENCODING 914 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +C0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR Gamma +ENCODING 915 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR uni0394 +ENCODING 916 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +20 +50 +50 +88 +88 +88 +F8 +ENDCHAR +STARTCHAR Epsilon +ENCODING 917 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR Zeta +ENCODING 918 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +20 +20 +40 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR Eta +ENCODING 919 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +90 +90 +90 +F0 +90 +90 +90 +90 +ENDCHAR +STARTCHAR Theta +ENCODING 920 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +F0 +90 +90 +90 +F0 +ENDCHAR +STARTCHAR Iota +ENCODING 921 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Kappa +ENCODING 922 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +C0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR Lambda +ENCODING 923 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +20 +50 +50 +50 +88 +88 +88 +ENDCHAR +STARTCHAR Mu +ENCODING 924 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +88 +D8 +A8 +A8 +88 +88 +88 +88 +ENDCHAR +STARTCHAR Nu +ENCODING 925 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +90 +90 +D0 +B0 +90 +90 +90 +90 +ENDCHAR +STARTCHAR Xi +ENCODING 926 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +00 +E0 +00 +00 +00 +E0 +ENDCHAR +STARTCHAR Omicron +ENCODING 927 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +90 +90 +90 +90 +F0 +ENDCHAR +STARTCHAR Pi +ENCODING 928 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +90 +90 +90 +90 +90 +ENDCHAR +STARTCHAR Rho +ENCODING 929 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR Sigma +ENCODING 931 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +40 +20 +40 +80 +80 +E0 +ENDCHAR +STARTCHAR Tau +ENCODING 932 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR Upsilon +ENCODING 933 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +E0 +40 +40 +40 +40 +ENDCHAR +STARTCHAR Phi +ENCODING 934 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +F8 +A8 +A8 +A8 +A8 +F8 +20 +ENDCHAR +STARTCHAR Chi +ENCODING 935 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +40 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR Psi +ENCODING 936 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +A8 +A8 +A8 +F8 +20 +20 +20 +20 +ENDCHAR +STARTCHAR uni03A9 +ENCODING 937 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +F8 +88 +88 +88 +88 +D8 +50 +D8 +ENDCHAR +STARTCHAR Iotadieresis +ENCODING 938 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR Upsilondieresis +ENCODING 939 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +A0 +A0 +E0 +40 +40 +40 +ENDCHAR +STARTCHAR alphatonos +ENCODING 940 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +20 +40 +00 +D0 +A0 +A0 +A0 +D0 +ENDCHAR +STARTCHAR epsilontonos +ENCODING 941 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +80 +40 +80 +E0 +ENDCHAR +STARTCHAR etatonos +ENCODING 942 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +A0 +A0 +A0 +20 +ENDCHAR +STARTCHAR iotatonos +ENCODING 943 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +C0 +40 +40 +40 +60 +ENDCHAR +STARTCHAR alpha +ENCODING 945 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +D0 +A0 +A0 +A0 +D0 +ENDCHAR +STARTCHAR beta +ENCODING 946 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +60 +A0 +A0 +C0 +A0 +A0 +E0 +80 +ENDCHAR +STARTCHAR gamma +ENCODING 947 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +D0 +50 +70 +20 +20 +ENDCHAR +STARTCHAR delta +ENCODING 948 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +40 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR epsilon +ENCODING 949 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +40 +80 +E0 +ENDCHAR +STARTCHAR zeta +ENCODING 950 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +20 +C0 +80 +80 +80 +E0 +20 +ENDCHAR +STARTCHAR eta +ENCODING 951 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +A0 +20 +ENDCHAR +STARTCHAR theta +ENCODING 952 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR iota +ENCODING 953 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +C0 +40 +40 +40 +60 +ENDCHAR +STARTCHAR kappa +ENCODING 954 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +C0 +A0 +A0 +ENDCHAR +STARTCHAR lambda +ENCODING 955 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +60 +20 +20 +50 +50 +88 +88 +88 +ENDCHAR +STARTCHAR uni03BC +ENCODING 956 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +A0 +E0 +80 +ENDCHAR +STARTCHAR nu +ENCODING 957 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +A0 +E0 +40 +ENDCHAR +STARTCHAR xi +ENCODING 958 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +E0 +80 +60 +80 +80 +E0 +20 +ENDCHAR +STARTCHAR omicron +ENCODING 959 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR pi +ENCODING 960 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +F8 +50 +50 +50 +58 +ENDCHAR +STARTCHAR rho +ENCODING 961 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +E0 +80 +ENDCHAR +STARTCHAR sigma1 +ENCODING 962 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +80 +E0 +20 +ENDCHAR +STARTCHAR sigma +ENCODING 963 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +F0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR tau +ENCODING 964 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +40 +40 +40 +60 +ENDCHAR +STARTCHAR upsilon +ENCODING 965 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR phi +ENCODING 966 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +B8 +A8 +A8 +F8 +20 +ENDCHAR +STARTCHAR chi +ENCODING 967 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +40 +A0 +A0 +ENDCHAR +STARTCHAR psi +ENCODING 968 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +A8 +A8 +A8 +F8 +20 +ENDCHAR +STARTCHAR omega +ENCODING 969 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +88 +A8 +A8 +F8 +50 +ENDCHAR +STARTCHAR iotadieresis +ENCODING 970 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +C0 +40 +40 +40 +60 +ENDCHAR +STARTCHAR upsilondieresis +ENCODING 971 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR omicrontonos +ENCODING 972 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR upsilontonos +ENCODING 973 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +20 +40 +00 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR omegatonos +ENCODING 974 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +10 +20 +00 +88 +A8 +A8 +F8 +50 +ENDCHAR +STARTCHAR uni0401 +ENCODING 1025 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +80 +E0 +80 +80 +E0 +ENDCHAR +STARTCHAR uni0404 +ENCODING 1028 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +70 +80 +80 +E0 +80 +80 +80 +70 +ENDCHAR +STARTCHAR uni0406 +ENCODING 1030 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR uni0407 +ENCODING 1031 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +00 +E0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR uni040E +ENCODING 1038 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +00 +A0 +A0 +E0 +20 +20 +60 +ENDCHAR +STARTCHAR uni0410 +ENCODING 1040 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR uni0411 +ENCODING 1041 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uni0412 +ENCODING 1042 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +C0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uni0413 +ENCODING 1043 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR uni0414 +ENCODING 1044 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +30 +50 +50 +50 +50 +50 +F8 +88 +ENDCHAR +STARTCHAR uni0415 +ENCODING 1045 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +80 +80 +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR uni0416 +ENCODING 1046 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +A8 +A8 +A8 +F8 +A8 +A8 +A8 +A8 +ENDCHAR +STARTCHAR uni0417 +ENCODING 1047 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +20 +40 +20 +20 +A0 +E0 +ENDCHAR +STARTCHAR uni0418 +ENCODING 1048 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +88 +88 +98 +A8 +C8 +88 +88 +88 +ENDCHAR +STARTCHAR uni0419 +ENCODING 1049 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +A8 +88 +98 +A8 +C8 +88 +88 +88 +ENDCHAR +STARTCHAR uni041A +ENCODING 1050 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +C0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR uni041B +ENCODING 1051 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +30 +50 +50 +50 +50 +50 +50 +D0 +ENDCHAR +STARTCHAR uni041C +ENCODING 1052 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +88 +D8 +A8 +A8 +88 +88 +88 +88 +ENDCHAR +STARTCHAR uni041D +ENCODING 1053 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +90 +90 +90 +F0 +90 +90 +90 +90 +ENDCHAR +STARTCHAR uni041E +ENCODING 1054 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +90 +90 +90 +90 +F0 +ENDCHAR +STARTCHAR uni041F +ENCODING 1055 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +90 +90 +90 +90 +90 +90 +90 +ENDCHAR +STARTCHAR uni0420 +ENCODING 1056 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR uni0421 +ENCODING 1057 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +80 +80 +80 +80 +A0 +E0 +ENDCHAR +STARTCHAR uni0422 +ENCODING 1058 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR uni0423 +ENCODING 1059 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +90 +90 +90 +F0 +10 +10 +10 +70 +ENDCHAR +STARTCHAR uni0424 +ENCODING 1060 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +F8 +A8 +A8 +A8 +A8 +F8 +20 +ENDCHAR +STARTCHAR uni0425 +ENCODING 1061 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +40 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR uni0426 +ENCODING 1062 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +A0 +A0 +A0 +A0 +A0 +A0 +A0 +F0 +ENDCHAR +STARTCHAR uni0427 +ENCODING 1063 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +A0 +E0 +20 +20 +20 +20 +ENDCHAR +STARTCHAR uni0428 +ENCODING 1064 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +A8 +A8 +A8 +A8 +A8 +A8 +A8 +F8 +ENDCHAR +STARTCHAR uni0429 +ENCODING 1065 +SWIDTH 1000 0 +DWIDTH 7 0 +BBX 6 8 0 0 +BITMAP +A8 +A8 +A8 +A8 +A8 +A8 +A8 +FC +ENDCHAR +STARTCHAR uni042A +ENCODING 1066 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +C0 +40 +40 +70 +50 +50 +50 +70 +ENDCHAR +STARTCHAR uni042B +ENCODING 1067 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +88 +88 +88 +E8 +A8 +A8 +A8 +E8 +ENDCHAR +STARTCHAR uni042C +ENCODING 1068 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +80 +80 +80 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uni042D +ENCODING 1069 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +E0 +10 +10 +70 +10 +10 +10 +E0 +ENDCHAR +STARTCHAR uni042E +ENCODING 1070 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +A8 +A8 +E8 +A8 +A8 +A8 +B8 +ENDCHAR +STARTCHAR uni042F +ENCODING 1071 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +A0 +A0 +60 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR uni0430 +ENCODING 1072 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +20 +E0 +A0 +E0 +ENDCHAR +STARTCHAR uni0431 +ENCODING 1073 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +60 +80 +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uni0432 +ENCODING 1074 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +C0 +A0 +E0 +ENDCHAR +STARTCHAR uni0433 +ENCODING 1075 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR uni0434 +ENCODING 1076 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +30 +50 +50 +F8 +88 +ENDCHAR +STARTCHAR uni0435 +ENCODING 1077 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR uni0436 +ENCODING 1078 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +A8 +A8 +F8 +A8 +A8 +ENDCHAR +STARTCHAR uni0437 +ENCODING 1079 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +20 +40 +20 +E0 +ENDCHAR +STARTCHAR uni0438 +ENCODING 1080 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +90 +B0 +D0 +90 +90 +ENDCHAR +STARTCHAR uni0439 +ENCODING 1081 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 7 0 0 +BITMAP +60 +00 +90 +B0 +D0 +90 +90 +ENDCHAR +STARTCHAR uni043A +ENCODING 1082 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +C0 +A0 +A0 +ENDCHAR +STARTCHAR uni043B +ENCODING 1083 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +30 +50 +50 +50 +D0 +ENDCHAR +STARTCHAR uni043C +ENCODING 1084 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +88 +D8 +A8 +88 +88 +ENDCHAR +STARTCHAR uni043D +ENCODING 1085 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +E0 +A0 +A0 +ENDCHAR +STARTCHAR uni043E +ENCODING 1086 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uni043F +ENCODING 1087 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +A0 +A0 +A0 +ENDCHAR +STARTCHAR uni0440 +ENCODING 1088 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +E0 +80 +80 +ENDCHAR +STARTCHAR uni0441 +ENCODING 1089 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR uni0442 +ENCODING 1090 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +40 +40 +40 +40 +ENDCHAR +STARTCHAR uni0443 +ENCODING 1091 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR uni0444 +ENCODING 1092 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +20 +20 +F8 +A8 +A8 +F8 +20 +ENDCHAR +STARTCHAR uni0445 +ENCODING 1093 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +40 +A0 +A0 +ENDCHAR +STARTCHAR uni0446 +ENCODING 1094 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +A0 +A0 +A0 +A0 +F0 +ENDCHAR +STARTCHAR uni0447 +ENCODING 1095 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +A0 +A0 +E0 +20 +20 +ENDCHAR +STARTCHAR uni0448 +ENCODING 1096 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +A8 +A8 +A8 +A8 +F8 +ENDCHAR +STARTCHAR uni0449 +ENCODING 1097 +SWIDTH 1000 0 +DWIDTH 7 0 +BBX 6 5 0 0 +BITMAP +A8 +A8 +A8 +A8 +FC +ENDCHAR +STARTCHAR uni044A +ENCODING 1098 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +C0 +40 +70 +50 +70 +ENDCHAR +STARTCHAR uni044B +ENCODING 1099 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +88 +88 +E8 +A8 +E8 +ENDCHAR +STARTCHAR uni044C +ENCODING 1100 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +80 +80 +E0 +A0 +E0 +ENDCHAR +STARTCHAR uni044D +ENCODING 1101 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +C0 +20 +E0 +20 +C0 +ENDCHAR +STARTCHAR uni044E +ENCODING 1102 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 5 0 0 +BITMAP +B8 +A8 +E8 +A8 +B8 +ENDCHAR +STARTCHAR uni044F +ENCODING 1103 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +E0 +A0 +60 +A0 +A0 +ENDCHAR +STARTCHAR uni0451 +ENCODING 1105 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +E0 +A0 +E0 +80 +E0 +ENDCHAR +STARTCHAR uni0454 +ENCODING 1108 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 5 0 0 +BITMAP +60 +80 +E0 +80 +60 +ENDCHAR +STARTCHAR uni0456 +ENCODING 1110 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR uni0457 +ENCODING 1111 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +A0 +00 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR uni045E +ENCODING 1118 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +00 +A0 +A0 +E0 +20 +60 +ENDCHAR +STARTCHAR uni0490 +ENCODING 1168 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +10 +E0 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR uni0491 +ENCODING 1169 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +20 +C0 +80 +80 +80 +80 +ENDCHAR +STARTCHAR uni2002 +ENCODING 8194 +SWIDTH 375 0 +DWIDTH 3 0 +BBX 1 1 0 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2003 +ENCODING 8195 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 1 1 0 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2009 +ENCODING 8201 +SWIDTH 1000 0 +DWIDTH 1 0 +BBX 1 1 6 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2010 +ENCODING 8208 +SWIDTH 1000 0 +DWIDTH 1 0 +BBX 1 1 0 4 +BITMAP +80 +ENDCHAR +STARTCHAR endash +ENCODING 8211 +SWIDTH 1000 0 +DWIDTH 3 0 +BBX 2 1 0 4 +BITMAP +C0 +ENDCHAR +STARTCHAR emdash +ENCODING 8212 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 1 0 4 +BITMAP +F0 +ENDCHAR +STARTCHAR uni2015 +ENCODING 8213 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 4 1 0 4 +BITMAP +F0 +ENDCHAR +STARTCHAR bullet +ENCODING 8226 +SWIDTH 1000 0 +DWIDTH 2 0 +BBX 1 1 0 4 +BITMAP +80 +ENDCHAR +STARTCHAR colonmonetary +ENCODING 8353 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +10 +F0 +A0 +A0 +A0 +A0 +F0 +40 +ENDCHAR +STARTCHAR uni20A2 +ENCODING 8354 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +80 +80 +B0 +A0 +A0 +A0 +F0 +ENDCHAR +STARTCHAR uni20A6 +ENCODING 8358 +SWIDTH 875 0 +DWIDTH 7 0 +BBX 6 8 0 0 +BITMAP +48 +48 +EC +58 +CC +48 +48 +48 +ENDCHAR +STARTCHAR uni20A9 +ENCODING 8361 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +54 +54 +FE +54 +54 +54 +7C +28 +ENDCHAR +STARTCHAR uni20AA +ENCODING 8362 +SWIDTH 875 0 +DWIDTH 7 0 +BBX 6 6 0 0 +BITMAP +F4 +94 +B4 +B4 +A4 +BC +ENDCHAR +STARTCHAR dong +ENCODING 8363 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +20 +70 +20 +E0 +A0 +E0 +00 +E0 +ENDCHAR +STARTCHAR Euro +ENCODING 8364 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +30 +60 +40 +F0 +40 +40 +60 +30 +ENDCHAR +STARTCHAR uni20AD +ENCODING 8365 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +50 +50 +60 +F0 +60 +50 +50 +50 +ENDCHAR +STARTCHAR uni20AE +ENCODING 8366 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +40 +60 +40 +C0 +40 +40 +40 +ENDCHAR +STARTCHAR uni20B1 +ENCODING 8369 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +70 +D8 +50 +70 +40 +40 +40 +40 +ENDCHAR +STARTCHAR uni20B2 +ENCODING 8370 +SWIDTH 1000 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +40 +E0 +80 +B0 +90 +90 +F0 +40 +ENDCHAR +STARTCHAR uni20B4 +ENCODING 8372 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +70 +50 +10 +F8 +40 +50 +50 +70 +ENDCHAR +STARTCHAR uni20B5 +ENCODING 8373 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 7 0 0 +BITMAP +40 +E0 +80 +80 +80 +E0 +40 +ENDCHAR +STARTCHAR uni20B8 +ENCODING 8376 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +E0 +00 +E0 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR uni20B9 +ENCODING 8377 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +20 +F0 +80 +40 +20 +10 +10 +ENDCHAR +STARTCHAR uni20BA +ENCODING 8378 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +E0 +40 +E0 +40 +40 +50 +50 +70 +ENDCHAR +STARTCHAR uni20BC +ENCODING 8380 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 7 0 0 +BITMAP +20 +20 +F8 +A8 +A8 +A8 +A8 +ENDCHAR +STARTCHAR uni20BD +ENCODING 8381 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +70 +50 +70 +40 +E0 +40 +40 +40 +ENDCHAR +STARTCHAR uni20BE +ENCODING 8382 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +F8 +A8 +A8 +A0 +80 +40 +F8 +ENDCHAR +STARTCHAR uni20BF +ENCODING 8383 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +40 +E0 +A0 +C0 +A0 +A0 +E0 +40 +ENDCHAR +STARTCHAR uni20C0 +ENCODING 8384 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 3 6 0 0 +BITMAP +E0 +80 +80 +E0 +00 +E0 +ENDCHAR +STARTCHAR uni2103 +ENCODING 8451 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +A8 +20 +20 +20 +20 +28 +38 +ENDCHAR +STARTCHAR uni2109 +ENCODING 8457 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +B8 +A0 +20 +38 +20 +20 +20 +20 +ENDCHAR +STARTCHAR uni2460 +ENCODING 9312 +SWIDTH 125 0 +DWIDTH 1 0 +BBX 1 1 4 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2461 +ENCODING 9313 +SWIDTH 250 0 +DWIDTH 2 0 +BBX 1 1 1 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2462 +ENCODING 9314 +SWIDTH 375 0 +DWIDTH 3 0 +BBX 1 1 3 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2463 +ENCODING 9315 +SWIDTH 500 0 +DWIDTH 4 0 +BBX 1 1 4 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2464 +ENCODING 9316 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 1 1 4 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2465 +ENCODING 9317 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 1 1 1 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2466 +ENCODING 9318 +SWIDTH 875 0 +DWIDTH 7 0 +BBX 1 1 3 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2467 +ENCODING 9319 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 1 1 4 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2468 +ENCODING 9320 +SWIDTH 1125 0 +DWIDTH 9 0 +BBX 1 1 4 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni2469 +ENCODING 9321 +SWIDTH 1250 0 +DWIDTH 10 0 +BBX 1 1 4 0 +BITMAP +00 +ENDCHAR +STARTCHAR uni24EA +ENCODING 9450 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 1 1 3 0 +BITMAP +00 +ENDCHAR +STARTCHAR H22073 +ENCODING 9633 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 1 +BITMAP +E0 +A0 +A0 +A0 +A0 +E0 +ENDCHAR +STARTCHAR uni4E00 +ENCODING 19968 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 2 0 3 +BITMAP +04 +FE +ENDCHAR +STARTCHAR uni4E03 +ENCODING 19971 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +20 +20 +2E +F0 +20 +20 +22 +1E +ENDCHAR +STARTCHAR uni4E09 +ENCODING 19977 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +7C +00 +00 +38 +00 +00 +04 +FE +ENDCHAR +STARTCHAR uni4E0A +ENCODING 19978 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +40 +40 +70 +40 +40 +40 +40 +F0 +ENDCHAR +STARTCHAR uni4E0B +ENCODING 19979 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 4 8 0 0 +BITMAP +F0 +40 +60 +50 +40 +40 +40 +40 +ENDCHAR +STARTCHAR uni4E5D +ENCODING 20061 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +20 +F8 +28 +28 +48 +48 +4A +8E +ENDCHAR +STARTCHAR uni4E8C +ENCODING 20108 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 6 0 1 +BITMAP +7C +00 +00 +00 +04 +FE +ENDCHAR +STARTCHAR uni4E94 +ENCODING 20116 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +7C +10 +10 +7C +24 +24 +24 +FE +ENDCHAR +STARTCHAR uni516B +ENCODING 20843 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +28 +28 +28 +28 +44 +44 +44 +82 +ENDCHAR +STARTCHAR uni516D +ENCODING 20845 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +10 +FE +00 +28 +28 +44 +44 +82 +ENDCHAR +STARTCHAR uni5341 +ENCODING 21313 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +10 +10 +FE +10 +10 +10 +10 +10 +ENDCHAR +STARTCHAR uni5348 +ENCODING 21320 +SWIDTH 625 0 +DWIDTH 5 0 +BBX 5 8 0 0 +BITMAP +40 +70 +A0 +20 +F8 +20 +20 +20 +ENDCHAR +STARTCHAR uni56DB +ENCODING 22235 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +FE +AA +AA +AE +C2 +82 +FE +82 +ENDCHAR +STARTCHAR uni5929 +ENCODING 22825 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +7C +10 +10 +FE +10 +10 +28 +C6 +ENDCHAR +STARTCHAR uni661F +ENCODING 26143 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +FC +84 +FC +5E +90 +7C +10 +FE +ENDCHAR +STARTCHAR uni6708 +ENCODING 26376 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +3E +22 +3E +22 +3E +22 +42 +86 +ENDCHAR +STARTCHAR uni671F +ENCODING 26399 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +5E +FA +5E +7A +5E +EA +12 +A6 +ENDCHAR +STARTCHAR uniAE08 +ENCODING 44552 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +F8 +08 +00 +F8 +00 +F8 +88 +F8 +ENDCHAR +STARTCHAR uniBAA9 +ENCODING 47785 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +F8 +88 +F8 +20 +F8 +00 +F8 +08 +ENDCHAR +STARTCHAR uniC218 +ENCODING 49688 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +20 +50 +88 +00 +F8 +20 +20 +ENDCHAR +STARTCHAR uniC624 +ENCODING 50724 +SWIDTH 750 0 +DWIDTH 6 0 +BBX 5 7 0 1 +BITMAP +70 +88 +88 +70 +20 +20 +F8 +ENDCHAR +STARTCHAR uniC694 +ENCODING 50836 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 7 0 1 +BITMAP +70 +88 +88 +70 +50 +50 +F8 +ENDCHAR +STARTCHAR uniC6D4 +ENCODING 50900 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +48 +A8 +48 +E8 +58 +38 +C0 +F8 +ENDCHAR +STARTCHAR uniC77C +ENCODING 51068 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +48 +A8 +48 +00 +F8 +38 +C0 +F8 +ENDCHAR +STARTCHAR uniC804 +ENCODING 51204 +SWIDTH 875 0 +DWIDTH 7 0 +BBX 6 8 0 0 +BITMAP +F4 +24 +4C +A4 +94 +44 +40 +7C +ENDCHAR +STARTCHAR uniD1A0 +ENCODING 53664 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 7 0 1 +BITMAP +F8 +80 +F0 +80 +F8 +20 +F8 +ENDCHAR +STARTCHAR uniD654 +ENCODING 54868 +SWIDTH 1000 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +24 +FC +54 +56 +24 +24 +FC +04 +ENDCHAR +STARTCHAR uniD6C4 +ENCODING 54980 +SWIDTH 1000 0 +DWIDTH 6 0 +BBX 5 8 0 0 +BITMAP +20 +F8 +50 +20 +00 +F8 +20 +20 +ENDCHAR +STARTCHAR uniFFE5 +ENCODING 65509 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 8 0 0 +BITMAP +A0 +A0 +E0 +40 +E0 +40 +40 +40 +ENDCHAR +STARTCHAR uniFFFD +ENCODING 65533 +SWIDTH 1000 0 +DWIDTH 4 0 +BBX 3 6 0 1 +BITMAP +A0 +A0 +40 +A0 +A0 +A0 +ENDCHAR +ENDFONT diff --git a/tests/components/font/common.yaml b/tests/components/font/common.yaml index a81457a05d..5be9faf5be 100644 --- a/tests/components/font/common.yaml +++ b/tests/components/font/common.yaml @@ -1,4 +1,12 @@ font: + - file: + type: gfonts + family: "Roboto" + weight: bold + italic: true + size: 32 + id: roboto32 + - file: "gfonts://Roboto" id: roboto size: 20 @@ -9,6 +17,10 @@ font: - file: "gfonts://Roboto" id: roboto_web size: 20 + - file: "gfonts://Roboto" + id: roboto_greek + size: 20 + glyphs: ["\u0300", "\u00C5", "\U000000C7"] - file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" id: monocraft size: 20 @@ -20,6 +32,17 @@ font: - file: $component_dir/Monocraft.ttf id: monocraft3 size: 28 + - file: $component_dir/MatrixChunky8X.bdf + id: special_font + glyphs: + - '"' + - "'" + - '#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz°' + + - file: $component_dir/MatrixChunky8X.bdf + id: default_font + - file: $component_dir/x11.pcf + id: pcf_font i2c: scl: ${i2c_scl} @@ -36,3 +59,4 @@ display: it.print(0, 40, id(monocraft), "Hello, World!"); it.print(0, 60, id(monocraft2), "Hello, World!"); it.print(0, 80, id(monocraft3), "Hello, World!"); + it.print(0, 100, id(roboto_greek), "Hello κόσμε!"); diff --git a/tests/components/font/test.host.yaml b/tests/components/font/test.host.yaml index 017328ec83..c5399f2826 100644 --- a/tests/components/font/test.host.yaml +++ b/tests/components/font/test.host.yaml @@ -1,4 +1,12 @@ font: + - file: + type: gfonts + family: "Roboto" + weight: bold + italic: true + size: 32 + id: roboto32 + - file: "gfonts://Roboto" id: roboto size: 20 @@ -9,6 +17,10 @@ font: - file: "gfonts://Roboto" id: roboto_web size: 20 + - file: "gfonts://Roboto" + id: roboto_greek + size: 20 + glyphs: ["\u0300", "\u00C5", "\U000000C7"] - file: "https://github.com/IdreesInc/Monocraft/releases/download/v3.0/Monocraft.ttf" id: monocraft size: 20 @@ -20,4 +32,26 @@ font: - file: $component_dir/Monocraft.ttf id: monocraft3 size: 28 + - file: $component_dir/MatrixChunky8X.bdf + id: special_font + glyphs: + - '"' + - "'" + - '#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz°' + - file: $component_dir/MatrixChunky8X.bdf + id: default_font + +display: + - platform: sdl + id: sdl_display + dimensions: + width: 800 + height: 600 + lambda: |- + it.print(0, 0, id(roboto), "Hello, World!"); + it.print(0, 20, id(roboto_web), "Hello, World!"); + it.print(0, 40, id(roboto_greek), "Hello κόσμε!"); + it.print(0, 60, id(monocraft), "Hello, World!"); + it.print(0, 80, id(monocraft2), "Hello, World!"); + it.print(0, 100, id(monocraft3), "Hello, World!"); diff --git a/tests/components/font/x11.pcf b/tests/components/font/x11.pcf new file mode 100644 index 0000000000000000000000000000000000000000..19a38d4e3918b6bb4796fb23d34a24e2a8fa9656 GIT binary patch literal 13368 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 J-u(Cf{|En5p|}75 literal 0 HcmV?d00001 From 749b9421329f0ca8453e7df0df0702a3d29e8276 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:37:32 +1100 Subject: [PATCH 0573/1052] [lvlg] fix tests (#7708) --- tests/components/lvgl/common.yaml | 40 +------------------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/tests/components/lvgl/common.yaml b/tests/components/lvgl/common.yaml index cebc3caaa7..c7d635db1c 100644 --- a/tests/components/lvgl/common.yaml +++ b/tests/components/lvgl/common.yaml @@ -4,56 +4,18 @@ touchscreen: display: tft_display update_interval: 50ms threshold: 1 - calibration: - x_max: 240 - y_max: 320 font: - file: "$component_dir/roboto.ttf" id: roboto20 size: 20 - extras: - - file: '$component_dir/materialdesignicons-webfont.ttf' - glyphs: [ - "\U000F004B", - "\U0000f0ed", - "\U000F006E", - "\U000F012C", - "\U000F179B", - "\U000F0748", - "\U000F1A1B", - "\U000F02DC", - "\U000F0A02", - "\U000F035F", - "\U000F0156", - "\U000F0C5F", - "\U000f0084", - "\U000f0091", - ] + - file: "$component_dir/helvetica.ttf" id: helvetica20 - file: "$component_dir/roboto.ttf" id: roboto10 size: 10 bpp: 4 - extras: - - file: '$component_dir/materialdesignicons-webfont.ttf' - glyphs: [ - "\U000F004B", - "\U0000f0ed", - "\U000F006E", - "\U000F012C", - "\U000F179B", - "\U000F0748", - "\U000F1A1B", - "\U000F02DC", - "\U000F0A02", - "\U000F035F", - "\U000F0156", - "\U000F0C5F", - "\U000f0084", - "\U000f0091", - ] sensor: - platform: lvgl From cefbfb75bd4b5f8baaf5599c1c4221aab1919426 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:46:35 +1300 Subject: [PATCH 0574/1052] [esp32_ble] Add disconnect as a virtual function to ``ESPBTClient`` (#7705) --- esphome/components/esp32_ble_client/ble_client_base.h | 2 +- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index fd586e59d6..fca66c0b3c 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -35,7 +35,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; void connect() override; esp_err_t pair(); - void disconnect(); + void disconnect() override; void release_services(); bool connected() { return this->state_ == espbt::ClientState::ESTABLISHED; } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index d2bb6a6e6d..2fc5da829d 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -11,9 +11,9 @@ #ifdef USE_ESP32 +#include #include #include -#include #include #include @@ -172,6 +172,7 @@ class ESPBTClient : public ESPBTDeviceListener { esp_ble_gattc_cb_param_t *param) = 0; virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0; virtual void connect() = 0; + virtual void disconnect() = 0; virtual void set_state(ClientState st) { this->state_ = st; } ClientState state() const { return state_; } int app_id; From 77bb46ff3bf17afb7bfd3fe963a86af014a17d4b Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Fri, 1 Nov 2024 02:54:34 -0700 Subject: [PATCH 0575/1052] handle bad pin schemas (#7711) Co-authored-by: Samuel Sieb --- esphome/components/esp32/gpio.py | 4 +++- esphome/components/esp8266/gpio.py | 12 +++++++----- esphome/components/host/gpio.py | 10 ++++++---- esphome/components/libretiny/gpio.py | 6 ++++-- esphome/components/rp2040/gpio.py | 8 +++++--- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/esphome/components/esp32/gpio.py b/esphome/components/esp32/gpio.py index 558ff51af8..df01769a66 100644 --- a/esphome/components/esp32/gpio.py +++ b/esphome/components/esp32/gpio.py @@ -67,8 +67,10 @@ def _translate_pin(value): "This variable only supports pin numbers, not full pin schemas " "(with inverted and mode)." ) - if isinstance(value, int): + if isinstance(value, int) and not isinstance(value, bool): return value + if not isinstance(value, str): + raise cv.Invalid(f"Invalid pin number: {value}") try: return int(value) except ValueError: diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py index c42bc9204f..53016d2130 100644 --- a/esphome/components/esp8266/gpio.py +++ b/esphome/components/esp8266/gpio.py @@ -1,6 +1,9 @@ -import logging from dataclasses import dataclass +import logging +from esphome import pins +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( CONF_ANALOG, CONF_ID, @@ -14,10 +17,7 @@ from esphome.const import ( CONF_PULLUP, PLATFORM_ESP8266, ) -from esphome import pins from esphome.core import CORE, coroutine_with_priority -import esphome.config_validation as cv -import esphome.codegen as cg from . import boards from .const import KEY_BOARD, KEY_ESP8266, KEY_PIN_INITIAL_STATES, esp8266_ns @@ -48,8 +48,10 @@ def _translate_pin(value): "This variable only supports pin numbers, not full pin schemas " "(with inverted and mode)." ) - if isinstance(value, int): + if isinstance(value, int) and not isinstance(value, bool): return value + if not isinstance(value, str): + raise cv.Invalid(f"Invalid pin number: {value}") try: return int(value) except ValueError: diff --git a/esphome/components/host/gpio.py b/esphome/components/host/gpio.py index 180919de4f..0f22a790bd 100644 --- a/esphome/components/host/gpio.py +++ b/esphome/components/host/gpio.py @@ -1,5 +1,8 @@ import logging +from esphome import pins +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( CONF_ID, CONF_INPUT, @@ -11,9 +14,6 @@ from esphome.const import ( CONF_PULLDOWN, CONF_PULLUP, ) -from esphome import pins -import esphome.config_validation as cv -import esphome.codegen as cg from .const import host_ns @@ -28,8 +28,10 @@ def _translate_pin(value): "This variable only supports pin numbers, not full pin schemas " "(with inverted and mode)." ) - if isinstance(value, int): + if isinstance(value, int) and not isinstance(value, bool): return value + if not isinstance(value, str): + raise cv.Invalid(f"Invalid pin number: {value}") try: return int(value) except ValueError: diff --git a/esphome/components/libretiny/gpio.py b/esphome/components/libretiny/gpio.py index 1d7b37cc9b..07eb0ce133 100644 --- a/esphome/components/libretiny/gpio.py +++ b/esphome/components/libretiny/gpio.py @@ -1,8 +1,8 @@ import logging +from esphome import pins import esphome.codegen as cg import esphome.config_validation as cv -from esphome import pins from esphome.const import ( CONF_ANALOG, CONF_ID, @@ -103,8 +103,10 @@ def _translate_pin(value): "This variable only supports pin numbers, not full pin schemas " "(with inverted and mode)." ) - if isinstance(value, int): + if isinstance(value, int) and not isinstance(value, bool): return value + if not isinstance(value, str): + raise cv.Invalid(f"Invalid pin number: {value}") try: return int(value) except ValueError: diff --git a/esphome/components/rp2040/gpio.py b/esphome/components/rp2040/gpio.py index 6ba0975a2c..58514f7db5 100644 --- a/esphome/components/rp2040/gpio.py +++ b/esphome/components/rp2040/gpio.py @@ -1,6 +1,8 @@ +from esphome import pins import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( + CONF_ANALOG, CONF_ID, CONF_INPUT, CONF_INVERTED, @@ -10,10 +12,8 @@ from esphome.const import ( CONF_OUTPUT, CONF_PULLDOWN, CONF_PULLUP, - CONF_ANALOG, ) from esphome.core import CORE -from esphome import pins from . import boards from .const import KEY_BOARD, KEY_RP2040, rp2040_ns @@ -41,8 +41,10 @@ def _translate_pin(value): "This variable only supports pin numbers, not full pin schemas " "(with inverted and mode)." ) - if isinstance(value, int): + if isinstance(value, int) and not isinstance(value, bool): return value + if not isinstance(value, str): + raise cv.Invalid(f"Invalid pin number: {value}") try: return int(value) except ValueError: From 01497c891d08c4a9a245eaadb982e302d43f0b58 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 3 Nov 2024 22:22:16 +0100 Subject: [PATCH 0576/1052] datetime fix build_language_schema (#7710) Co-authored-by: Tomasz Duda --- esphome/components/datetime/__init__.py | 6 +++--- tests/components/mqtt/common.yaml | 1 + tests/components/web_server/common_v3.yaml | 8 ++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/esphome/components/datetime/__init__.py b/esphome/components/datetime/__init__.py index 7edf527e01..630bf6962c 100644 --- a/esphome/components/datetime/__init__.py +++ b/esphome/components/datetime/__init__.py @@ -70,8 +70,6 @@ def _validate_time_present(config): _DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( - web_server.WEBSERVER_SORTING_SCHEMA, - cv.MQTT_COMMAND_COMPONENT_SCHEMA, cv.Schema( { cv.Optional(CONF_ON_VALUE): automation.validate_automation( @@ -81,7 +79,9 @@ _DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( ), cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock), } - ), + ) + .extend(web_server.WEBSERVER_SORTING_SCHEMA) + .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) ).add_extra(_validate_time_present) diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index e154be8b5c..75c34bec56 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -230,6 +230,7 @@ datetime: id: test_date type: date state_topic: some/topic/date + command_topic: test_date/custom_command_topic qos: 2 subscribe_qos: 2 set_action: diff --git a/tests/components/web_server/common_v3.yaml b/tests/components/web_server/common_v3.yaml index 69f4b67f15..bdacaaddbe 100644 --- a/tests/components/web_server/common_v3.yaml +++ b/tests/components/web_server/common_v3.yaml @@ -35,3 +35,11 @@ switch: web_server: sorting_group_id: sorting_group_2 sorting_weight: -10 +datetime: + - platform: template + name: Pick a Date + type: datetime + optimistic: yes + web_server: + sorting_group_id: sorting_group_3 + sorting_weight: -5 From 2dca3d79e490abcf2a03e962cb7c88dc3bbfd83b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:32:18 +1100 Subject: [PATCH 0577/1052] [lvgl] Ensure images are configured before using them. (Bugfix) (#7721) --- esphome/components/lvgl/widgets/animimg.py | 7 ++++--- esphome/components/lvgl/widgets/img.py | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/widgets/animimg.py b/esphome/components/lvgl/widgets/animimg.py index 3b20008c3d..8adea72ad3 100644 --- a/esphome/components/lvgl/widgets/animimg.py +++ b/esphome/components/lvgl/widgets/animimg.py @@ -60,9 +60,10 @@ class AnimimgType(WidgetType): lvgl_components_required.add(CONF_IMAGE) lvgl_components_required.add(CONF_ANIMIMG) if CONF_SRC in config: - for x in config[CONF_SRC]: - await cg.get_variable(x) - srcs = [await lv_image.process(x) for x in config[CONF_SRC]] + srcs = [ + await lv_image.process(await cg.get_variable(x)) + for x in config[CONF_SRC] + ] src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs) count = len(config[CONF_SRC]) lv.animimg_set_src(w.obj, src_id, count) diff --git a/esphome/components/lvgl/widgets/img.py b/esphome/components/lvgl/widgets/img.py index 59b2c97c63..931d0c0b5b 100644 --- a/esphome/components/lvgl/widgets/img.py +++ b/esphome/components/lvgl/widgets/img.py @@ -1,3 +1,4 @@ +import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ANGLE, CONF_MODE @@ -64,6 +65,7 @@ class ImgType(WidgetType): async def to_code(self, w: Widget, config): if src := config.get(CONF_SRC): + src = await cg.get_variable(src) lv.img_set_src(w.obj, await lv_image.process(src)) if (cf_angle := config.get(CONF_ANGLE)) is not None: pivot_x = config[CONF_PIVOT_X] From dcc537d0d43c2b8fdff7ecac86c8154c7ed78172 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:45:40 +1300 Subject: [PATCH 0578/1052] [lvgl] Don't just throw key error if someone types a bad layout type (#7722) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/lvgl/schemas.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index bb14c11ddd..516627708e 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -391,7 +391,9 @@ def container_validator(schema, widget_type: WidgetType): add_lv_use(ltype) if value == SCHEMA_EXTRACT: return result - result = result.extend(LAYOUT_SCHEMAS[ltype.lower()]) + result = result.extend( + LAYOUT_SCHEMAS.get(ltype.lower(), LAYOUT_SCHEMAS[df.TYPE_NONE]) + ) return result(value) return validator From 5bb4d042e48873812684fcad189c233c64698629 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:54:47 +1100 Subject: [PATCH 0579/1052] [spi_device] rename mode to spi_mode (#7724) --- esphome/components/spi/__init__.py | 29 ++++++++++--------- esphome/components/spi_device/__init__.py | 19 +++--------- .../components/spi_device/test.esp32-ard.yaml | 2 +- .../spi_device/test.esp32-c3-ard.yaml | 2 +- .../spi_device/test.esp32-c3-idf.yaml | 2 +- .../components/spi_device/test.esp32-idf.yaml | 2 +- .../spi_device/test.esp8266-ard.yaml | 2 +- .../spi_device/test.rp2040-ard.yaml | 2 +- 8 files changed, 25 insertions(+), 35 deletions(-) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index fdf19bb56e..52afbf365e 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -1,40 +1,37 @@ import re +from esphome import pins import esphome.codegen as cg -import esphome.config_validation as cv -import esphome.final_validate as fv from esphome.components.esp32.const import ( KEY_ESP32, - VARIANT_ESP32S2, - VARIANT_ESP32S3, VARIANT_ESP32C2, VARIANT_ESP32C3, VARIANT_ESP32C6, VARIANT_ESP32H2, + VARIANT_ESP32S2, + VARIANT_ESP32S3, ) -from esphome import pins +import esphome.config_validation as cv from esphome.const import ( CONF_CLK_PIN, + CONF_CS_PIN, + CONF_DATA_PINS, + CONF_DATA_RATE, CONF_ID, + CONF_INVERTED, CONF_MISO_PIN, CONF_MOSI_PIN, - CONF_SPI_ID, - CONF_CS_PIN, CONF_NUMBER, - CONF_INVERTED, + CONF_SPI_ID, KEY_CORE, KEY_TARGET_PLATFORM, KEY_VARIANT, - CONF_DATA_RATE, PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, - CONF_DATA_PINS, -) -from esphome.core import ( - coroutine_with_priority, - CORE, ) +from esphome.core import CORE, coroutine_with_priority +import esphome.final_validate as fv CODEOWNERS = ["@esphome/core", "@clydebarrow"] spi_ns = cg.esphome_ns.namespace("spi") @@ -69,6 +66,10 @@ SPI_MODE_OPTIONS = { 1: SPIMode.MODE1, 2: SPIMode.MODE2, 3: SPIMode.MODE3, + "0": SPIMode.MODE0, + "1": SPIMode.MODE1, + "2": SPIMode.MODE2, + "3": SPIMode.MODE3, } CONF_SPI_MODE = "spi_mode" diff --git a/esphome/components/spi_device/__init__.py b/esphome/components/spi_device/__init__.py index 65e7ee6fc6..2f23d8a011 100644 --- a/esphome/components/spi_device/__init__.py +++ b/esphome/components/spi_device/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import spi +import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_MODE DEPENDENCIES = ["spi"] @@ -11,18 +11,6 @@ spi_device_ns = cg.esphome_ns.namespace("spi_device") spi_device = spi_device_ns.class_("SPIDeviceComponent", cg.Component, spi.SPIDevice) -Mode = spi.spi_ns.enum("SPIMode") -MODES = { - "0": Mode.MODE0, - "1": Mode.MODE1, - "2": Mode.MODE2, - "3": Mode.MODE3, - "MODE0": Mode.MODE0, - "MODE1": Mode.MODE1, - "MODE2": Mode.MODE2, - "MODE3": Mode.MODE3, -} - BitOrder = spi.spi_ns.enum("SPIBitOrder") ORDERS = { "msb_first": BitOrder.BIT_ORDER_MSB_FIRST, @@ -34,7 +22,9 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_ID): cv.declare_id(spi_device), cv.Optional(CONF_BIT_ORDER, default="msb_first"): cv.enum(ORDERS, lower=True), - cv.Optional(CONF_MODE, default="0"): cv.enum(MODES, upper=True), + cv.Optional(CONF_MODE): cv.invalid( + "The 'mode' option has been renamed to 'spi_mode'." + ), } ).extend(spi.spi_device_schema(False, "1MHz")) @@ -42,6 +32,5 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - cg.add(var.set_mode(config[CONF_MODE])) cg.add(var.set_bit_order(config[CONF_BIT_ORDER])) await spi.register_spi_device(var, config) diff --git a/tests/components/spi_device/test.esp32-ard.yaml b/tests/components/spi_device/test.esp32-ard.yaml index cad8ca49f8..b539cb3ec4 100644 --- a/tests/components/spi_device/test.esp32-ard.yaml +++ b/tests/components/spi_device/test.esp32-ard.yaml @@ -7,5 +7,5 @@ spi: spi_device: id: spi_device_test data_rate: 2MHz - mode: 3 + spi_mode: 3 bit_order: lsb_first diff --git a/tests/components/spi_device/test.esp32-c3-ard.yaml b/tests/components/spi_device/test.esp32-c3-ard.yaml index 49e2733676..99c0ac1ebb 100644 --- a/tests/components/spi_device/test.esp32-c3-ard.yaml +++ b/tests/components/spi_device/test.esp32-c3-ard.yaml @@ -7,5 +7,5 @@ spi: spi_device: id: spi_device_test data_rate: 2MHz - mode: 3 + spi_mode: 3 bit_order: lsb_first diff --git a/tests/components/spi_device/test.esp32-c3-idf.yaml b/tests/components/spi_device/test.esp32-c3-idf.yaml index 49e2733676..99c0ac1ebb 100644 --- a/tests/components/spi_device/test.esp32-c3-idf.yaml +++ b/tests/components/spi_device/test.esp32-c3-idf.yaml @@ -7,5 +7,5 @@ spi: spi_device: id: spi_device_test data_rate: 2MHz - mode: 3 + spi_mode: 3 bit_order: lsb_first diff --git a/tests/components/spi_device/test.esp32-idf.yaml b/tests/components/spi_device/test.esp32-idf.yaml index cad8ca49f8..b539cb3ec4 100644 --- a/tests/components/spi_device/test.esp32-idf.yaml +++ b/tests/components/spi_device/test.esp32-idf.yaml @@ -7,5 +7,5 @@ spi: spi_device: id: spi_device_test data_rate: 2MHz - mode: 3 + spi_mode: 3 bit_order: lsb_first diff --git a/tests/components/spi_device/test.esp8266-ard.yaml b/tests/components/spi_device/test.esp8266-ard.yaml index 1b191bdb6a..988825ce2d 100644 --- a/tests/components/spi_device/test.esp8266-ard.yaml +++ b/tests/components/spi_device/test.esp8266-ard.yaml @@ -7,5 +7,5 @@ spi: spi_device: id: spi_device_test data_rate: 2MHz - mode: 3 + spi_mode: 3 bit_order: lsb_first diff --git a/tests/components/spi_device/test.rp2040-ard.yaml b/tests/components/spi_device/test.rp2040-ard.yaml index c70493c70d..6020643f21 100644 --- a/tests/components/spi_device/test.rp2040-ard.yaml +++ b/tests/components/spi_device/test.rp2040-ard.yaml @@ -7,5 +7,5 @@ spi: spi_device: id: spi_device_test data_rate: 2MHz - mode: 3 + spi_mode: 3 bit_order: lsb_first From 80b4c264818df8ce79813d3f572e87b9c7d24bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mart=C3=ADn?= Date: Wed, 6 Nov 2024 01:56:48 +0100 Subject: [PATCH 0580/1052] feat(MQTT): Add `enable`, `disable` and `enable_on_boot` (#7716) --- esphome/components/mqtt/__init__.py | 33 +++++++++++++++++++++++++ esphome/components/mqtt/mqtt_client.cpp | 30 +++++++++++++++++++--- esphome/components/mqtt/mqtt_client.h | 29 ++++++++++++++++++++-- tests/components/mqtt/common.yaml | 3 +++ 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 8851581ea0..86d163e61d 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -22,6 +22,7 @@ from esphome.const import ( CONF_DISCOVERY_PREFIX, CONF_DISCOVERY_RETAIN, CONF_DISCOVERY_UNIQUE_ID_GENERATOR, + CONF_ENABLE_ON_BOOT, CONF_ID, CONF_KEEPALIVE, CONF_LEVEL, @@ -99,6 +100,8 @@ MQTTMessage = mqtt_ns.struct("MQTTMessage") MQTTClientComponent = mqtt_ns.class_("MQTTClientComponent", cg.Component) MQTTPublishAction = mqtt_ns.class_("MQTTPublishAction", automation.Action) MQTTPublishJsonAction = mqtt_ns.class_("MQTTPublishJsonAction", automation.Action) +MQTTEnableAction = mqtt_ns.class_("MQTTEnableAction", automation.Action) +MQTTDisableAction = mqtt_ns.class_("MQTTDisableAction", automation.Action) MQTTMessageTrigger = mqtt_ns.class_( "MQTTMessageTrigger", automation.Trigger.template(cg.std_string), cg.Component ) @@ -208,6 +211,7 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(MQTTClientComponent), cv.Required(CONF_BROKER): cv.string_strict, + cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean, cv.Optional(CONF_PORT, default=1883): cv.port, cv.Optional(CONF_USERNAME, default=""): cv.string, cv.Optional(CONF_PASSWORD, default=""): cv.string, @@ -325,6 +329,7 @@ async def to_code(config): cg.add_global(mqtt_ns.using) cg.add(var.set_broker_address(config[CONF_BROKER])) + cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) cg.add(var.set_broker_port(config[CONF_PORT])) cg.add(var.set_username(config[CONF_USERNAME])) cg.add(var.set_password(config[CONF_PASSWORD])) @@ -555,3 +560,31 @@ async def register_mqtt_component(var, config): async def mqtt_connected_to_code(config, condition_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) return cg.new_Pvariable(condition_id, template_arg, paren) + + +@automation.register_action( + "mqtt.enable", + MQTTEnableAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(MQTTClientComponent), + } + ), +) +async def mqtt_enable_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) + + +@automation.register_action( + "mqtt.disable", + MQTTDisableAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(MQTTClientComponent), + } + ), +) +async def mqtt_disable_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + return cg.new_Pvariable(action_id, template_arg, paren) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index b5ac285026..106192c0e3 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -50,6 +50,8 @@ void MQTTClientComponent::setup() { } }); this->mqtt_backend_.set_on_disconnect([this](MQTTClientDisconnectReason reason) { + if (this->state_ == MQTT_CLIENT_DISABLED) + return; this->state_ = MQTT_CLIENT_DISCONNECTED; this->disconnect_reason_ = reason; }); @@ -77,8 +79,9 @@ void MQTTClientComponent::setup() { topic, [this](const std::string &topic, const std::string &payload) { this->send_device_info_(); }, 2); } - this->last_connected_ = millis(); - this->start_dnslookup_(); + if (this->enable_on_boot_) { + this->enable(); + } } void MQTTClientComponent::send_device_info_() { @@ -163,7 +166,9 @@ void MQTTClientComponent::dump_config() { ESP_LOGCONFIG(TAG, " Availability: '%s'", this->availability_.topic.c_str()); } } -bool MQTTClientComponent::can_proceed() { return network::is_disabled() || this->is_connected(); } +bool MQTTClientComponent::can_proceed() { + return network::is_disabled() || this->state_ == MQTT_CLIENT_DISABLED || this->is_connected(); +} void MQTTClientComponent::start_dnslookup_() { for (auto &subscription : this->subscriptions_) { @@ -339,6 +344,8 @@ void MQTTClientComponent::loop() { const uint32_t now = millis(); switch (this->state_) { + case MQTT_CLIENT_DISABLED: + return; // Return to avoid a reboot when disabled case MQTT_CLIENT_DISCONNECTED: if (now - this->connect_begin_ > 5000) { this->start_dnslookup_(); @@ -501,6 +508,23 @@ bool MQTTClientComponent::publish_json(const std::string &topic, const json::jso return this->publish(topic, message, qos, retain); } +void MQTTClientComponent::enable() { + if (this->state_ != MQTT_CLIENT_DISABLED) + return; + ESP_LOGD(TAG, "Enabling MQTT..."); + this->state_ = MQTT_CLIENT_DISCONNECTED; + this->last_connected_ = millis(); + this->start_dnslookup_(); +} + +void MQTTClientComponent::disable() { + if (this->state_ == MQTT_CLIENT_DISABLED) + return; + ESP_LOGD(TAG, "Disabling MQTT..."); + this->state_ = MQTT_CLIENT_DISABLED; + this->on_shutdown(); +} + /** Check if the message topic matches the given subscription topic * * INFO: MQTT spec mandates that topics must not be empty and must be valid NULL-terminated UTF-8 strings. diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 887800f201..7ae3a6c5e8 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -87,7 +87,8 @@ struct MQTTDiscoveryInfo { }; enum MQTTClientState { - MQTT_CLIENT_DISCONNECTED = 0, + MQTT_CLIENT_DISABLED = 0, + MQTT_CLIENT_DISCONNECTED, MQTT_CLIENT_RESOLVING_ADDRESS, MQTT_CLIENT_CONNECTING, MQTT_CLIENT_CONNECTED, @@ -247,6 +248,9 @@ class MQTTClientComponent : public Component { void register_mqtt_component(MQTTComponent *component); bool is_connected(); + void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; } + void enable(); + void disable(); void on_shutdown() override; @@ -314,10 +318,11 @@ class MQTTClientComponent : public Component { MQTTBackendLibreTiny mqtt_backend_; #endif - MQTTClientState state_{MQTT_CLIENT_DISCONNECTED}; + MQTTClientState state_{MQTT_CLIENT_DISABLED}; network::IPAddress ip_; bool dns_resolved_{false}; bool dns_resolve_error_{false}; + bool enable_on_boot_{true}; std::vector children_; uint32_t reboot_timeout_{300000}; uint32_t connect_begin_; @@ -414,6 +419,26 @@ template class MQTTConnectedCondition : public Condition MQTTClientComponent *parent_; }; +template class MQTTEnableAction : public Action { + public: + MQTTEnableAction(MQTTClientComponent *parent) : parent_(parent) {} + + void play(Ts... x) override { this->parent_->enable(); } + + protected: + MQTTClientComponent *parent_; +}; + +template class MQTTDisableAction : public Action { + public: + MQTTDisableAction(MQTTClientComponent *parent) : parent_(parent) {} + + void play(Ts... x) override { this->parent_->disable(); } + + protected: + MQTTClientComponent *parent_; +}; + } // namespace mqtt } // namespace esphome diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index 75c34bec56..d22fe9579f 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -10,6 +10,7 @@ mqtt: port: 1883 username: debug password: debug + enable_on_boot: false clean_session: True client_id: someclient use_abbreviations: false @@ -87,6 +88,8 @@ button: state_topic: some/topic/button qos: 2 on_press: + - mqtt.disable + - mqtt.enable - mqtt.publish: topic: some/topic/button payload: Hello From 248b0bc378b93b8be87899a65f47b4d7a95b866d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 8 Nov 2024 07:05:23 +1100 Subject: [PATCH 0581/1052] [lvgl] Allow multiple LVGL instances (#7712) Co-authored-by: clydeps --- esphome/components/lvgl/__init__.py | 241 +++++++++++------- esphome/components/lvgl/automation.py | 14 +- .../components/lvgl/binary_sensor/__init__.py | 23 +- esphome/components/lvgl/light/__init__.py | 8 +- esphome/components/lvgl/lvcode.py | 4 +- esphome/components/lvgl/lvgl_esphome.cpp | 25 +- esphome/components/lvgl/lvgl_esphome.h | 14 +- esphome/components/lvgl/number/__init__.py | 25 +- esphome/components/lvgl/select/__init__.py | 21 +- esphome/components/lvgl/sensor/__init__.py | 22 +- esphome/components/lvgl/switch/__init__.py | 21 +- esphome/components/lvgl/text/__init__.py | 11 +- .../components/lvgl/text_sensor/__init__.py | 30 +-- esphome/components/lvgl/touchscreens.py | 5 +- esphome/components/lvgl/trigger.py | 12 +- esphome/components/lvgl/widgets/page.py | 5 +- tests/components/lvgl/test.host.yaml | 32 +++ 17 files changed, 287 insertions(+), 226 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 4a1a26cc0b..7476c0a09c 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -27,7 +27,7 @@ from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code from .gradient import GRADIENT_SCHEMA, gradients_to_code from .hello_world import get_hello_world from .lv_validation import lv_bool, lv_images_used -from .lvcode import LvContext, LvglComponent +from .lvcode import LvContext, LvglComponent, lvgl_static from .schemas import ( DISP_BG_SCHEMA, FLEX_OBJ_SCHEMA, @@ -152,41 +152,70 @@ def generate_lv_conf_h(): return LV_CONF_H_FORMAT.format("\n".join(definitions)) -def final_validation(config): - if pages := config.get(CONF_PAGES): - if all(p[df.CONF_SKIP] for p in pages): - raise cv.Invalid("At least one page must not be skipped") +def multi_conf_validate(configs: list[dict]): + displays = [config[df.CONF_DISPLAYS] for config in configs] + # flatten the display list + display_list = [disp for disps in displays for disp in disps] + if len(display_list) != len(set(display_list)): + raise cv.Invalid("A display ID may be used in only one LVGL instance") + base_config = configs[0] + for config in configs[1:]: + for item in ( + df.CONF_LOG_LEVEL, + df.CONF_COLOR_DEPTH, + df.CONF_BYTE_ORDER, + df.CONF_TRANSPARENCY_KEY, + ): + if base_config[item] != config[item]: + raise cv.Invalid( + f"Config item '{item}' must be the same for all LVGL instances" + ) + + +def final_validation(configs): + multi_conf_validate(configs) global_config = full_config.get() - for display_id in config[df.CONF_DISPLAYS]: - path = global_config.get_path_for_id(display_id)[:-1] - display = global_config.get_config_for_path(path) - if CONF_LAMBDA in display: - raise cv.Invalid("Using lambda: in display config not compatible with LVGL") - if display[CONF_AUTO_CLEAR_ENABLED]: - raise cv.Invalid( - "Using auto_clear_enabled: true in display config not compatible with LVGL" - ) - buffer_frac = config[CONF_BUFFER_SIZE] - if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config: - LOGGER.warning("buffer_size: may need to be reduced without PSRAM") - for image_id in lv_images_used: - path = global_config.get_path_for_id(image_id)[:-1] - image_conf = global_config.get_config_for_path(path) - if image_conf[CONF_TYPE] in ("RGBA", "RGB24"): - raise cv.Invalid( - "Using RGBA or RGB24 in image config not compatible with LVGL", path - ) - for w in focused_widgets: - path = global_config.get_path_for_id(w) - widget_conf = global_config.get_config_for_path(path[:-1]) - if df.CONF_ADJUSTABLE in widget_conf and not widget_conf[df.CONF_ADJUSTABLE]: - raise cv.Invalid( - "A non adjustable arc may not be focused", - path, - ) + for config in configs: + if pages := config.get(CONF_PAGES): + if all(p[df.CONF_SKIP] for p in pages): + raise cv.Invalid("At least one page must not be skipped") + for display_id in config[df.CONF_DISPLAYS]: + path = global_config.get_path_for_id(display_id)[:-1] + display = global_config.get_config_for_path(path) + if CONF_LAMBDA in display: + raise cv.Invalid( + "Using lambda: in display config not compatible with LVGL" + ) + if display[CONF_AUTO_CLEAR_ENABLED]: + raise cv.Invalid( + "Using auto_clear_enabled: true in display config not compatible with LVGL" + ) + buffer_frac = config[CONF_BUFFER_SIZE] + if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config: + LOGGER.warning("buffer_size: may need to be reduced without PSRAM") + for image_id in lv_images_used: + path = global_config.get_path_for_id(image_id)[:-1] + image_conf = global_config.get_config_for_path(path) + if image_conf[CONF_TYPE] in ("RGBA", "RGB24"): + raise cv.Invalid( + "Using RGBA or RGB24 in image config not compatible with LVGL", path + ) + for w in focused_widgets: + path = global_config.get_path_for_id(w) + widget_conf = global_config.get_config_for_path(path[:-1]) + if ( + df.CONF_ADJUSTABLE in widget_conf + and not widget_conf[df.CONF_ADJUSTABLE] + ): + raise cv.Invalid( + "A non adjustable arc may not be focused", + path, + ) -async def to_code(config): +async def to_code(configs): + config_0 = configs[0] + # Global configuration cg.add_library("lvgl/lvgl", "8.4.0") cg.add_define("USE_LVGL") # suppress default enabling of extra widgets @@ -203,53 +232,33 @@ async def to_code(config): add_define("LV_MEM_CUSTOM_INCLUDE", '"esphome/components/lvgl/lvgl_hal.h"') add_define( - "LV_LOG_LEVEL", f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config[df.CONF_LOG_LEVEL]]}" + "LV_LOG_LEVEL", + f"LV_LOG_LEVEL_{df.LV_LOG_LEVELS[config_0[df.CONF_LOG_LEVEL]]}", ) cg.add_define( "LVGL_LOG_LEVEL", - cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config[df.CONF_LOG_LEVEL]}"), + cg.RawExpression(f"ESPHOME_LOG_LEVEL_{config_0[df.CONF_LOG_LEVEL]}"), ) - add_define("LV_COLOR_DEPTH", config[df.CONF_COLOR_DEPTH]) + add_define("LV_COLOR_DEPTH", config_0[df.CONF_COLOR_DEPTH]) for font in helpers.lv_fonts_used: add_define(f"LV_FONT_{font.upper()}") - if config[df.CONF_COLOR_DEPTH] == 16: + if config_0[df.CONF_COLOR_DEPTH] == 16: add_define( "LV_COLOR_16_SWAP", - "1" if config[df.CONF_BYTE_ORDER] == "big_endian" else "0", + "1" if config_0[df.CONF_BYTE_ORDER] == "big_endian" else "0", ) add_define( "LV_COLOR_CHROMA_KEY", - await lvalid.lv_color.process(config[df.CONF_TRANSPARENCY_KEY]), + await lvalid.lv_color.process(config_0[df.CONF_TRANSPARENCY_KEY]), ) cg.add_build_flag("-Isrc") cg.add_global(lvgl_ns.using) - frac = config[CONF_BUFFER_SIZE] - if frac >= 0.75: - frac = 1 - elif frac >= 0.375: - frac = 2 - elif frac > 0.19: - frac = 4 - else: - frac = 8 - displays = [await cg.get_variable(display) for display in config[df.CONF_DISPLAYS]] - lv_component = cg.new_Pvariable( - config[CONF_ID], - displays, - frac, - config[df.CONF_FULL_REFRESH], - config[df.CONF_DRAW_ROUNDING], - config[df.CONF_RESUME_ON_INPUT], - ) - await cg.register_component(lv_component, config) - Widget.create(config[CONF_ID], lv_component, obj_spec, config) - for font in helpers.esphome_fonts_used: await cg.get_variable(font) cg.new_Pvariable(ID(f"{font}_engine", True, type=FontEngine), MockObj(font)) - default_font = config[df.CONF_DEFAULT_FONT] + default_font = config_0[df.CONF_DEFAULT_FONT] if not lvalid.is_lv_font(default_font): add_define( "LV_FONT_CUSTOM_DECLARE", f"LV_FONT_DECLARE(*{df.DEFAULT_ESPHOME_FONT})" @@ -265,39 +274,71 @@ async def to_code(config): add_define("LV_FONT_DEFAULT", df.DEFAULT_ESPHOME_FONT) else: add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font)) + cg.add(lvgl_static.esphome_lvgl_init()) - lv_scr_act = get_scr_act(lv_component) - async with LvContext(lv_component): - await touchscreens_to_code(lv_component, config) - await encoders_to_code(lv_component, config) - await theme_to_code(config) - await styles_to_code(config) - await gradients_to_code(config) - await set_obj_properties(lv_scr_act, config) - await add_widgets(lv_scr_act, config) - await add_pages(lv_component, config) - await add_top_layer(lv_component, config) - await msgboxes_to_code(lv_component, config) - await disp_update(lv_component.get_disp(), config) + for config in configs: + frac = config[CONF_BUFFER_SIZE] + if frac >= 0.75: + frac = 1 + elif frac >= 0.375: + frac = 2 + elif frac > 0.19: + frac = 4 + else: + frac = 8 + displays = [ + await cg.get_variable(display) for display in config[df.CONF_DISPLAYS] + ] + lv_component = cg.new_Pvariable( + config[CONF_ID], + displays, + frac, + config[df.CONF_FULL_REFRESH], + config[df.CONF_DRAW_ROUNDING], + config[df.CONF_RESUME_ON_INPUT], + ) + await cg.register_component(lv_component, config) + Widget.create(config[CONF_ID], lv_component, obj_spec, config) + + lv_scr_act = get_scr_act(lv_component) + async with LvContext(): + await touchscreens_to_code(lv_component, config) + await encoders_to_code(lv_component, config) + await theme_to_code(config) + await styles_to_code(config) + await gradients_to_code(config) + await set_obj_properties(lv_scr_act, config) + await add_widgets(lv_scr_act, config) + await add_pages(lv_component, config) + await add_top_layer(lv_component, config) + await msgboxes_to_code(lv_component, config) + await disp_update(lv_component.get_disp(), config) # Set this directly since we are limited in how many methods can be added to the Widget class. Widget.widgets_completed = True - async with LvContext(lv_component): - await generate_triggers(lv_component) - await generate_page_triggers(lv_component, config) - await initial_focus_to_code(config) - for conf in config.get(CONF_ON_IDLE, ()): - templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) - idle_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, templ) - await build_automation(idle_trigger, [], conf) - for conf in config.get(df.CONF_ON_PAUSE, ()): - pause_trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], lv_component, True) - await build_automation(pause_trigger, [], conf) - for conf in config.get(df.CONF_ON_RESUME, ()): - resume_trigger = cg.new_Pvariable( - conf[CONF_TRIGGER_ID], lv_component, False - ) - await build_automation(resume_trigger, [], conf) + async with LvContext(): + await generate_triggers() + for config in configs: + lv_component = await cg.get_variable(config[CONF_ID]) + await generate_page_triggers(config) + await initial_focus_to_code(config) + for conf in config.get(CONF_ON_IDLE, ()): + templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) + idle_trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], lv_component, templ + ) + await build_automation(idle_trigger, [], conf) + for conf in config.get(df.CONF_ON_PAUSE, ()): + pause_trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], lv_component, True + ) + await build_automation(pause_trigger, [], conf) + for conf in config.get(df.CONF_ON_RESUME, ()): + resume_trigger = cg.new_Pvariable( + conf[CONF_TRIGGER_ID], lv_component, False + ) + await build_automation(resume_trigger, [], conf) + # This must be done after all widgets are created for comp in helpers.lvgl_components_required: cg.add_define(f"USE_LVGL_{comp.upper()}") if "transform_angle" in styles_used: @@ -312,7 +353,10 @@ async def to_code(config): def display_schema(config): value = cv.ensure_list(cv.use_id(Display))(config) - return value or [cv.use_id(Display)(config)] + value = value or [cv.use_id(Display)(config)] + if len(set(value)) != len(value): + raise cv.Invalid("Display IDs must be unique") + return value def add_hello_world(config): @@ -324,7 +368,7 @@ def add_hello_world(config): FINAL_VALIDATE_SCHEMA = final_validation -CONFIG_SCHEMA = ( +LVGL_SCHEMA = ( cv.polling_component_schema("1s") .extend(obj_schema(obj_spec)) .extend( @@ -393,3 +437,16 @@ CONFIG_SCHEMA = ( .extend(DISP_BG_SCHEMA) .add_extra(add_hello_world) ) + + +def lvgl_config_schema(config): + """ + Can't use cv.ensure_list here because it converts an empty config to an empty list, + rather than a default config. + """ + if not config or isinstance(config, dict): + return [LVGL_SCHEMA(config)] + return cv.Schema([LVGL_SCHEMA])(config) + + +CONFIG_SCHEMA = lvgl_config_schema diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 48472354f8..58e3dd808b 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -137,20 +137,18 @@ async def disp_update(disp, config: dict): cv.maybe_simple_value( { cv.Required(CONF_ID): cv.use_id(lv_obj_t), - cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), }, key=CONF_ID, ), - cv.Schema( - { - cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), - } - ), + LVGL_SCHEMA, ), ) async def obj_invalidate_to_code(config, action_id, template_arg, args): - lv_comp = await cg.get_variable(config[CONF_LVGL_ID]) - widgets = await get_widgets(config) or [get_scr_act(lv_comp)] + if CONF_LVGL_ID in config: + lv_comp = await cg.get_variable(config[CONF_LVGL_ID]) + widgets = [get_scr_act(lv_comp)] + else: + widgets = await get_widgets(config) async def do_invalidate(widget: Widget): lv_obj.invalidate(widget.obj) diff --git a/esphome/components/lvgl/binary_sensor/__init__.py b/esphome/components/lvgl/binary_sensor/__init__.py index 56984405aa..ffbdc977b2 100644 --- a/esphome/components/lvgl/binary_sensor/__init__.py +++ b/esphome/components/lvgl/binary_sensor/__init__.py @@ -1,4 +1,3 @@ -import esphome.codegen as cg from esphome.components.binary_sensor import ( BinarySensor, binary_sensor_schema, @@ -6,36 +5,30 @@ from esphome.components.binary_sensor import ( ) import esphome.config_validation as cv -from ..defines import CONF_LVGL_ID, CONF_WIDGET -from ..lvcode import EVENT_ARG, LambdaContext, LvContext -from ..schemas import LVGL_SCHEMA +from ..defines import CONF_WIDGET +from ..lvcode import EVENT_ARG, LambdaContext, LvContext, lvgl_static from ..types import LV_EVENT, lv_pseudo_button_t from ..widgets import Widget, get_widgets, wait_for_widgets -CONFIG_SCHEMA = ( - binary_sensor_schema(BinarySensor) - .extend(LVGL_SCHEMA) - .extend( - { - cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t), - } - ) +CONFIG_SCHEMA = binary_sensor_schema(BinarySensor).extend( + { + cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t), + } ) async def to_code(config): sensor = await new_binary_sensor(config) - paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] assert isinstance(widget, Widget) await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pressed_ctx: pressed_ctx.add(sensor.publish_state(widget.is_pressed())) - async with LvContext(paren) as ctx: + async with LvContext() as ctx: ctx.add(sensor.publish_initial_state(widget.is_pressed())) ctx.add( - paren.add_event_cb( + lvgl_static.add_event_cb( widget.obj, await pressed_ctx.get_lambda(), LV_EVENT.PRESSING, diff --git a/esphome/components/lvgl/light/__init__.py b/esphome/components/lvgl/light/__init__.py index 8031ae8221..dcdf67a520 100644 --- a/esphome/components/lvgl/light/__init__.py +++ b/esphome/components/lvgl/light/__init__.py @@ -4,9 +4,8 @@ from esphome.components.light import LightOutput import esphome.config_validation as cv from esphome.const import CONF_GAMMA_CORRECT, CONF_OUTPUT_ID -from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..defines import CONF_WIDGET from ..lvcode import LvContext -from ..schemas import LVGL_SCHEMA from ..types import LvType, lvgl_ns from ..widgets import get_widgets, wait_for_widgets @@ -18,16 +17,15 @@ CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( cv.Required(CONF_WIDGET): cv.use_id(lv_led_t), cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(LVLight), } -).extend(LVGL_SCHEMA) +) async def to_code(config): var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) await light.register_light(var, config) - paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] await wait_for_widgets() - async with LvContext(paren) as ctx: + async with LvContext() as ctx: ctx.add(var.set_obj(widget.obj)) diff --git a/esphome/components/lvgl/lvcode.py b/esphome/components/lvgl/lvcode.py index 37d6670b84..6b98cc4251 100644 --- a/esphome/components/lvgl/lvcode.py +++ b/esphome/components/lvgl/lvcode.py @@ -178,10 +178,9 @@ class LvContext(LambdaContext): added_lambda_count = 0 - def __init__(self, lv_component, args=None): + def __init__(self, args=None): self.args = args or LVGL_COMP_ARG super().__init__(parameters=self.args) - self.lv_component = lv_component async def __aexit__(self, exc_type, exc_val, exc_tb): await super().__aexit__(exc_type, exc_val, exc_tb) @@ -298,6 +297,7 @@ lv_expr = LvExpr("lv_") lv_obj = MockLv("lv_obj_") # Operations on the LVGL component lvgl_comp = MockObj(LVGL_COMP, "->") +lvgl_static = MockObj("LvglComponent", "::") # equivalent to cg.add() for the current code context diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 70cfb859de..41346bc732 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -98,19 +98,24 @@ void LvglComponent::set_paused(bool paused, bool show_snow) { this->pause_callbacks_.call(paused); } +void LvglComponent::esphome_lvgl_init() { + lv_init(); + lv_update_event = static_cast(lv_event_register_id()); + lv_api_event = static_cast(lv_event_register_id()); +} void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event) { - lv_obj_add_event_cb(obj, callback, event, this); + lv_obj_add_event_cb(obj, callback, event, nullptr); } void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2) { - this->add_event_cb(obj, callback, event1); - this->add_event_cb(obj, callback, event2); + add_event_cb(obj, callback, event1); + add_event_cb(obj, callback, event2); } void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2, lv_event_code_t event3) { - this->add_event_cb(obj, callback, event1); - this->add_event_cb(obj, callback, event2); - this->add_event_cb(obj, callback, event3); + add_event_cb(obj, callback, event1); + add_event_cb(obj, callback, event2); + add_event_cb(obj, callback, event3); } void LvglComponent::add_page(LvPageType *page) { this->pages_.push_back(page); @@ -218,8 +223,10 @@ PauseTrigger::PauseTrigger(LvglComponent *parent, TemplatableValue paused) } #ifdef USE_LVGL_TOUCHSCREEN -LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time) { +LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent) { + this->set_parent(parent); lv_indev_drv_init(&this->drv_); + this->drv_.disp = parent->get_disp(); this->drv_.long_press_repeat_time = long_press_repeat_time; this->drv_.long_press_time = long_press_time; this->drv_.type = LV_INDEV_TYPE_POINTER; @@ -235,6 +242,7 @@ LVTouchListener::LVTouchListener(uint16_t long_press_time, uint16_t long_press_r } }; } + void LVTouchListener::update(const touchscreen::TouchPoints_t &tpoints) { this->touch_pressed_ = !this->parent_->is_paused() && !tpoints.empty(); if (this->touch_pressed_) @@ -405,9 +413,6 @@ LvglComponent::LvglComponent(std::vector displays, float buf buffer_frac_(buffer_frac), full_refresh_(full_refresh), resume_on_input_(resume_on_input) { - lv_init(); - lv_update_event = static_cast(lv_event_register_id()); - lv_api_event = static_cast(lv_event_register_id()); auto *display = this->displays_[0]; size_t buffer_pixels = display->get_width() * display->get_height() / this->buffer_frac_; auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8; diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index f357c4950c..dae07d5153 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -146,10 +146,14 @@ class LvglComponent : public PollingComponent { } } - void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event); - void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2); - void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2, - lv_event_code_t event3); + /** + * Initialize the LVGL library and register custom events. + */ + static void esphome_lvgl_init(); + static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event); + static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2); + static void add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_event_code_t event1, lv_event_code_t event2, + lv_event_code_t event3); void add_page(LvPageType *page); void show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time); void show_next_page(lv_scr_load_anim_t anim, uint32_t time); @@ -231,7 +235,7 @@ template class LvglCondition : public Condition, public P #ifdef USE_LVGL_TOUCHSCREEN class LVTouchListener : public touchscreen::TouchListener, public Parented { public: - LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time); + LVTouchListener(uint16_t long_press_time, uint16_t long_press_repeat_time, LvglComponent *parent); void update(const touchscreen::TouchPoints_t &tpoints) override; void release() override { touch_pressed_ = false; diff --git a/esphome/components/lvgl/number/__init__.py b/esphome/components/lvgl/number/__init__.py index 07f92635b5..b41a36bc0f 100644 --- a/esphome/components/lvgl/number/__init__.py +++ b/esphome/components/lvgl/number/__init__.py @@ -3,7 +3,7 @@ from esphome.components import number import esphome.config_validation as cv from esphome.cpp_generator import MockObj -from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_UPDATE_ON_RELEASE, CONF_WIDGET +from ..defines import CONF_ANIMATED, CONF_UPDATE_ON_RELEASE, CONF_WIDGET from ..lv_validation import animated from ..lvcode import ( API_EVENT, @@ -13,28 +13,23 @@ from ..lvcode import ( LvContext, lv, lv_add, + lvgl_static, ) -from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber, lvgl_ns from ..widgets import get_widgets, wait_for_widgets LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number) -CONFIG_SCHEMA = ( - number.number_schema(LVGLNumber) - .extend(LVGL_SCHEMA) - .extend( - { - cv.Required(CONF_WIDGET): cv.use_id(LvNumber), - cv.Optional(CONF_ANIMATED, default=True): animated, - cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean, - } - ) +CONFIG_SCHEMA = number.number_schema(LVGLNumber).extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvNumber), + cv.Optional(CONF_ANIMATED, default=True): animated, + cv.Optional(CONF_UPDATE_ON_RELEASE, default=False): cv.boolean, + } ) async def to_code(config): - paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] var = await number.new_number( @@ -58,10 +53,10 @@ async def to_code(config): if not config[CONF_UPDATE_ON_RELEASE] else LV_EVENT.RELEASED ) - async with LvContext(paren): + async with LvContext(): lv_add(var.set_control_lambda(await control.get_lambda())) lv_add( - paren.add_event_cb( + lvgl_static.add_event_cb( widget.obj, await event.get_lambda(), UPDATE_EVENT, event_code ) ) diff --git a/esphome/components/lvgl/select/__init__.py b/esphome/components/lvgl/select/__init__.py index 5e50b6b385..bd5ef8f237 100644 --- a/esphome/components/lvgl/select/__init__.py +++ b/esphome/components/lvgl/select/__init__.py @@ -1,25 +1,19 @@ -import esphome.codegen as cg from esphome.components import select import esphome.config_validation as cv from esphome.const import CONF_OPTIONS -from ..defines import CONF_ANIMATED, CONF_LVGL_ID, CONF_WIDGET, literal +from ..defines import CONF_ANIMATED, CONF_WIDGET, literal from ..lvcode import LvContext -from ..schemas import LVGL_SCHEMA from ..types import LvSelect, lvgl_ns from ..widgets import get_widgets, wait_for_widgets LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) -CONFIG_SCHEMA = ( - select.select_schema(LVGLSelect) - .extend(LVGL_SCHEMA) - .extend( - { - cv.Required(CONF_WIDGET): cv.use_id(LvSelect), - cv.Optional(CONF_ANIMATED, default=False): cv.boolean, - } - ) +CONFIG_SCHEMA = select.select_schema(LVGLSelect).extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvSelect), + cv.Optional(CONF_ANIMATED, default=False): cv.boolean, + } ) @@ -28,9 +22,8 @@ async def to_code(config): widget = widget[0] options = widget.config.get(CONF_OPTIONS, []) selector = await select.new_select(config, options=options) - paren = await cg.get_variable(config[CONF_LVGL_ID]) await wait_for_widgets() - async with LvContext(paren) as ctx: + async with LvContext() as ctx: ctx.add( selector.set_widget( widget.var, diff --git a/esphome/components/lvgl/sensor/__init__.py b/esphome/components/lvgl/sensor/__init__.py index a2a2298c27..03b2638ed0 100644 --- a/esphome/components/lvgl/sensor/__init__.py +++ b/esphome/components/lvgl/sensor/__init__.py @@ -1,8 +1,7 @@ -import esphome.codegen as cg from esphome.components.sensor import Sensor, new_sensor, sensor_schema import esphome.config_validation as cv -from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..defines import CONF_WIDGET from ..lvcode import ( API_EVENT, EVENT_ARG, @@ -11,34 +10,29 @@ from ..lvcode import ( LambdaContext, LvContext, lv_add, + lvgl_static, ) -from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvNumber from ..widgets import Widget, get_widgets, wait_for_widgets -CONFIG_SCHEMA = ( - sensor_schema(Sensor) - .extend(LVGL_SCHEMA) - .extend( - { - cv.Required(CONF_WIDGET): cv.use_id(LvNumber), - } - ) +CONFIG_SCHEMA = sensor_schema(Sensor).extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvNumber), + } ) async def to_code(config): sensor = await new_sensor(config) - paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] assert isinstance(widget, Widget) await wait_for_widgets() async with LambdaContext(EVENT_ARG) as lamb: lv_add(sensor.publish_state(widget.get_value())) - async with LvContext(paren, LVGL_COMP_ARG): + async with LvContext(LVGL_COMP_ARG): lv_add( - paren.add_event_cb( + lvgl_static.add_event_cb( widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED, diff --git a/esphome/components/lvgl/switch/__init__.py b/esphome/components/lvgl/switch/__init__.py index 8c090543f9..4e1e7f72e0 100644 --- a/esphome/components/lvgl/switch/__init__.py +++ b/esphome/components/lvgl/switch/__init__.py @@ -3,7 +3,7 @@ from esphome.components.switch import Switch, new_switch, switch_schema import esphome.config_validation as cv from esphome.cpp_generator import MockObj -from ..defines import CONF_LVGL_ID, CONF_WIDGET, literal +from ..defines import CONF_WIDGET, literal from ..lvcode import ( API_EVENT, EVENT_ARG, @@ -13,26 +13,21 @@ from ..lvcode import ( LvContext, lv, lv_add, + lvgl_static, ) -from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns from ..widgets import get_widgets, wait_for_widgets LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch) -CONFIG_SCHEMA = ( - switch_schema(LVGLSwitch) - .extend(LVGL_SCHEMA) - .extend( - { - cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t), - } - ) +CONFIG_SCHEMA = switch_schema(LVGLSwitch).extend( + { + cv.Required(CONF_WIDGET): cv.use_id(lv_pseudo_button_t), + } ) async def to_code(config): switch = await new_switch(config) - paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] await wait_for_widgets() @@ -45,10 +40,10 @@ async def to_code(config): widget.clear_state(LV_STATE.CHECKED) lv.event_send(widget.obj, API_EVENT, cg.nullptr) control.add(switch.publish_state(literal("v"))) - async with LvContext(paren) as ctx: + async with LvContext() as ctx: lv_add(switch.set_control_lambda(await control.get_lambda())) ctx.add( - paren.add_event_cb( + lvgl_static.add_event_cb( widget.obj, await checked_ctx.get_lambda(), LV_EVENT.VALUE_CHANGED, diff --git a/esphome/components/lvgl/text/__init__.py b/esphome/components/lvgl/text/__init__.py index a59e703591..89db139a6a 100644 --- a/esphome/components/lvgl/text/__init__.py +++ b/esphome/components/lvgl/text/__init__.py @@ -3,7 +3,7 @@ from esphome.components import text from esphome.components.text import new_text import esphome.config_validation as cv -from ..defines import CONF_LVGL_ID, CONF_WIDGET +from ..defines import CONF_WIDGET from ..lvcode import ( API_EVENT, EVENT_ARG, @@ -12,14 +12,14 @@ from ..lvcode import ( LvContext, lv, lv_add, + lvgl_static, ) -from ..schemas import LVGL_SCHEMA from ..types import LV_EVENT, LvText, lvgl_ns from ..widgets import get_widgets, wait_for_widgets LVGLText = lvgl_ns.class_("LVGLText", text.Text) -CONFIG_SCHEMA = text.TEXT_SCHEMA.extend(LVGL_SCHEMA).extend( +CONFIG_SCHEMA = text.TEXT_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(LVGLText), cv.Required(CONF_WIDGET): cv.use_id(LvText), @@ -29,7 +29,6 @@ CONFIG_SCHEMA = text.TEXT_SCHEMA.extend(LVGL_SCHEMA).extend( async def to_code(config): textvar = await new_text(config) - paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] await wait_for_widgets() @@ -39,10 +38,10 @@ async def to_code(config): control.add(textvar.publish_state(widget.get_value())) async with LambdaContext(EVENT_ARG) as lamb: lv_add(textvar.publish_state(widget.get_value())) - async with LvContext(paren): + async with LvContext(): lv_add(textvar.set_control_lambda(await control.get_lambda())) lv_add( - paren.add_event_cb( + lvgl_static.add_event_cb( widget.obj, await lamb.get_lambda(), LV_EVENT.VALUE_CHANGED, diff --git a/esphome/components/lvgl/text_sensor/__init__.py b/esphome/components/lvgl/text_sensor/__init__.py index ae39eec291..4728fd137a 100644 --- a/esphome/components/lvgl/text_sensor/__init__.py +++ b/esphome/components/lvgl/text_sensor/__init__.py @@ -1,4 +1,3 @@ -import esphome.codegen as cg from esphome.components.text_sensor import ( TextSensor, new_text_sensor, @@ -6,34 +5,35 @@ from esphome.components.text_sensor import ( ) import esphome.config_validation as cv -from ..defines import CONF_LVGL_ID, CONF_WIDGET -from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext -from ..schemas import LVGL_SCHEMA +from ..defines import CONF_WIDGET +from ..lvcode import ( + API_EVENT, + EVENT_ARG, + UPDATE_EVENT, + LambdaContext, + LvContext, + lvgl_static, +) from ..types import LV_EVENT, LvText from ..widgets import get_widgets, wait_for_widgets -CONFIG_SCHEMA = ( - text_sensor_schema(TextSensor) - .extend(LVGL_SCHEMA) - .extend( - { - cv.Required(CONF_WIDGET): cv.use_id(LvText), - } - ) +CONFIG_SCHEMA = text_sensor_schema(TextSensor).extend( + { + cv.Required(CONF_WIDGET): cv.use_id(LvText), + } ) async def to_code(config): sensor = await new_text_sensor(config) - paren = await cg.get_variable(config[CONF_LVGL_ID]) widget = await get_widgets(config, CONF_WIDGET) widget = widget[0] await wait_for_widgets() async with LambdaContext(EVENT_ARG) as pressed_ctx: pressed_ctx.add(sensor.publish_state(widget.get_value())) - async with LvContext(paren) as ctx: + async with LvContext() as ctx: ctx.add( - paren.add_event_cb( + lvgl_static.add_event_cb( widget.obj, await pressed_ctx.get_lambda(), LV_EVENT.VALUE_CHANGED, diff --git a/esphome/components/lvgl/touchscreens.py b/esphome/components/lvgl/touchscreens.py index 4d430a428e..f2dd013f6d 100644 --- a/esphome/components/lvgl/touchscreens.py +++ b/esphome/components/lvgl/touchscreens.py @@ -33,13 +33,12 @@ def touchscreen_schema(config): return [TOUCHSCREENS_CONFIG(config)] -async def touchscreens_to_code(var, config): +async def touchscreens_to_code(lv_component, config): for tconf in config[CONF_TOUCHSCREENS]: lvgl_components_required.add(CONF_TOUCHSCREEN) touchscreen = await cg.get_variable(tconf[CONF_TOUCHSCREEN_ID]) lpt = tconf[CONF_LONG_PRESS_TIME].total_milliseconds lprt = tconf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds - listener = cg.new_Pvariable(tconf[CONF_ID], lpt, lprt) - await cg.register_parented(listener, var) + listener = cg.new_Pvariable(tconf[CONF_ID], lpt, lprt, lv_component) lv.indev_drv_register(listener.get_drv()) cg.add(touchscreen.register_listener(listener)) diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index eb6e483203..fb856df04e 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -20,17 +20,16 @@ from .lvcode import ( lv, lv_add, lv_event_t_ptr, + lvgl_static, ) from .types import LV_EVENT from .widgets import widget_map -async def generate_triggers(lv_component): +async def generate_triggers(): """ Generate LVGL triggers for all defined widgets Must be done after all widgets completed - :param lv_component: The parent component - :return: """ for w in widget_map.values(): @@ -43,11 +42,10 @@ async def generate_triggers(lv_component): conf = conf[0] w.add_flag("LV_OBJ_FLAG_CLICKABLE") event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()]) - await add_trigger(conf, lv_component, w, event) + await add_trigger(conf, w, event) for conf in w.config.get(CONF_ON_VALUE, ()): await add_trigger( conf, - lv_component, w, LV_EVENT.VALUE_CHANGED, API_EVENT, @@ -63,7 +61,7 @@ async def generate_triggers(lv_component): lv.obj_align_to(w.obj, target, align, x, y) -async def add_trigger(conf, lv_component, w, *events): +async def add_trigger(conf, w, *events): tid = conf[CONF_TRIGGER_ID] trigger = cg.new_Pvariable(tid) args = w.get_args() + [(lv_event_t_ptr, "event")] @@ -72,4 +70,4 @@ async def add_trigger(conf, lv_component, w, *events): async with LambdaContext(EVENT_ARG, where=tid) as context: with LvConditional(w.is_selected()): lv_add(trigger.trigger(*value, literal("event"))) - lv_add(lv_component.add_event_cb(w.obj, await context.get_lambda(), *events)) + lv_add(lvgl_static.add_event_cb(w.obj, await context.get_lambda(), *events)) diff --git a/esphome/components/lvgl/widgets/page.py b/esphome/components/lvgl/widgets/page.py index 0e84ab6791..a754a9cb9a 100644 --- a/esphome/components/lvgl/widgets/page.py +++ b/esphome/components/lvgl/widgets/page.py @@ -20,6 +20,7 @@ from ..lvcode import ( add_line_marks, lv_add, lvgl_comp, + lvgl_static, ) from ..schemas import LVGL_SCHEMA from ..types import LvglAction, lv_page_t @@ -139,7 +140,7 @@ async def add_pages(lv_component, config): await add_widgets(page, pconf) -async def generate_page_triggers(lv_component, config): +async def generate_page_triggers(config): for pconf in config.get(CONF_PAGES, ()): page = (await get_widgets(pconf))[0] for ev in (CONF_ON_LOAD, CONF_ON_UNLOAD): @@ -149,7 +150,7 @@ async def generate_page_triggers(lv_component, config): async with LambdaContext(EVENT_ARG, where=id) as context: lv_add(trigger.trigger()) lv_add( - lv_component.add_event_cb( + lvgl_static.add_event_cb( page.obj, await context.get_lambda(), literal(f"LV_EVENT_SCREEN_{ev[3:].upper()}_START"), diff --git a/tests/components/lvgl/test.host.yaml b/tests/components/lvgl/test.host.yaml index 3a490bbe15..34918cb113 100644 --- a/tests/components/lvgl/test.host.yaml +++ b/tests/components/lvgl/test.host.yaml @@ -1,5 +1,12 @@ display: - platform: sdl + id: sdl0 + auto_clear_enabled: false + dimensions: + width: 480 + height: 320 + - platform: sdl + id: sdl1 auto_clear_enabled: false dimensions: width: 480 @@ -7,5 +14,30 @@ display: touchscreen: - platform: sdl + display: sdl0 + sdl_id: sdl0 lvgl: + - id: lvgl_0 + displays: sdl0 + - id: lvgl_1 + displays: sdl1 + on_idle: + timeout: 8s + then: + if: + condition: + lvgl.is_idle: + lvgl_id: lvgl_1 + timeout: 5s + then: + logger.log: Lvgl2 is idle + widgets: + - button: + align: center + widgets: + - label: + text: Click ME + on_click: + logger.log: Clicked + From c0658ffe2c31edcba1091dd05c922c36b3292c1c Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Fri, 8 Nov 2024 01:10:58 +0300 Subject: [PATCH 0582/1052] [fix] deprecated legacy driver tsens (#7658) Co-authored-by: luar123 <49960470+luar123@users.noreply.github.com> --- .../internal_temperature.cpp | 51 ++++++++++++++++++- .../internal_temperature.h | 1 + 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/esphome/components/internal_temperature/internal_temperature.cpp b/esphome/components/internal_temperature/internal_temperature.cpp index 9ef5cbecd5..afa5583e59 100644 --- a/esphome/components/internal_temperature/internal_temperature.cpp +++ b/esphome/components/internal_temperature/internal_temperature.cpp @@ -8,8 +8,13 @@ extern "C" { uint8_t temprature_sens_read(); } #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ - defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) + defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \ + defined(USE_ESP32_VARIANT_ESP32C2) +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #include "driver/temp_sensor.h" +#else +#include "driver/temperature_sensor.h" +#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #endif // USE_ESP32_VARIANT #endif // USE_ESP32 #ifdef USE_RP2040 @@ -25,6 +30,13 @@ namespace esphome { namespace internal_temperature { static const char *const TAG = "internal_temperature"; +#ifdef USE_ESP32 +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \ + (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)) +static temperature_sensor_handle_t tsensNew = NULL; +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && USE_ESP32_VARIANT +#endif // USE_ESP32 void InternalTemperatureSensor::update() { float temperature = NAN; @@ -36,7 +48,9 @@ void InternalTemperatureSensor::update() { temperature = (raw - 32) / 1.8f; success = (raw != 128); #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ - defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) + defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \ + defined(USE_ESP32_VARIANT_ESP32C2) +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT(); temp_sensor_set_config(tsens); temp_sensor_start(); @@ -47,6 +61,13 @@ void InternalTemperatureSensor::update() { esp_err_t result = temp_sensor_read_celsius(&temperature); temp_sensor_stop(); success = (result == ESP_OK); +#else + 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); + } +#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #endif // USE_ESP32_VARIANT #endif // USE_ESP32 #ifdef USE_RP2040 @@ -75,6 +96,32 @@ void InternalTemperatureSensor::update() { } } +void InternalTemperatureSensor::setup() { +#ifdef USE_ESP32 +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \ + (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)) + ESP_LOGCONFIG(TAG, "Setting up temperature sensor..."); + + 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); + this->mark_failed(); + return; + } + + result = temperature_sensor_enable(tsensNew); + if (result != ESP_OK) { + ESP_LOGE(TAG, "Failed to enable temperature sensor: %d", result); + this->mark_failed(); + return; + } +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && USE_ESP32_VARIANT +#endif // USE_ESP32 +} + void InternalTemperatureSensor::dump_config() { LOG_SENSOR("", "Internal Temperature Sensor", this); } } // namespace internal_temperature diff --git a/esphome/components/internal_temperature/internal_temperature.h b/esphome/components/internal_temperature/internal_temperature.h index 0e46a69769..78e3bcef7d 100644 --- a/esphome/components/internal_temperature/internal_temperature.h +++ b/esphome/components/internal_temperature/internal_temperature.h @@ -8,6 +8,7 @@ namespace internal_temperature { class InternalTemperatureSensor : public sensor::Sensor, public PollingComponent { public: + void setup() override; void dump_config() override; void update() override; From d189cc1fbe406dc971bfcbb33e302bedd806cc05 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:39:01 +1100 Subject: [PATCH 0583/1052] [lvgl] Fix id config for the lvgl component (Bugfix) (#7731) Co-authored-by: clydeps --- esphome/components/lvgl/automation.py | 52 +++++++++++++-------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 58e3dd808b..c26ae54892 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -1,5 +1,4 @@ -from collections.abc import Awaitable -from typing import Callable +from typing import Any, Callable from esphome import automation import esphome.codegen as cg @@ -23,7 +22,6 @@ from .lvcode import ( UPDATE_EVENT, LambdaContext, LocalVariable, - LvglComponent, ReturnStatement, add_line_marks, lv, @@ -58,7 +56,7 @@ focused_widgets = set() async def action_to_code( widgets: list[Widget], - action: Callable[[Widget], Awaitable[None]], + action: Callable[[Widget], Any], action_id, template_arg, args, @@ -159,14 +157,12 @@ async def obj_invalidate_to_code(config, action_id, template_arg, args): @automation.register_action( "lvgl.update", LvglAction, - DISP_BG_SCHEMA.extend( - { - cv.GenerateID(): cv.use_id(LvglComponent), - } - ).add_extra(cv.has_at_least_one_key(CONF_DISP_BG_COLOR, CONF_DISP_BG_IMAGE)), + DISP_BG_SCHEMA.extend(LVGL_SCHEMA).add_extra( + cv.has_at_least_one_key(CONF_DISP_BG_COLOR, CONF_DISP_BG_IMAGE) + ), ) async def lvgl_update_to_code(config, action_id, template_arg, args): - widgets = await get_widgets(config) + widgets = await get_widgets(config, CONF_LVGL_ID) w = widgets[0] disp = literal(f"{w.obj}->get_disp()") async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context: @@ -179,32 +175,33 @@ async def lvgl_update_to_code(config, action_id, template_arg, args): @automation.register_action( "lvgl.pause", LvglAction, - { - cv.GenerateID(): cv.use_id(LvglComponent), - cv.Optional(CONF_SHOW_SNOW, default=False): lv_bool, - }, + LVGL_SCHEMA.extend( + { + cv.Optional(CONF_SHOW_SNOW, default=False): lv_bool, + } + ), ) async def pause_action_to_code(config, action_id, template_arg, args): + lv_comp = await cg.get_variable(config[CONF_LVGL_ID]) async with LambdaContext(LVGL_COMP_ARG) as context: add_line_marks(where=action_id) lv_add(lvgl_comp.set_paused(True, config[CONF_SHOW_SNOW])) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) - await cg.register_parented(var, config[CONF_ID]) + await cg.register_parented(var, lv_comp) return var @automation.register_action( "lvgl.resume", LvglAction, - { - cv.GenerateID(): cv.use_id(LvglComponent), - }, + LVGL_SCHEMA, ) async def resume_action_to_code(config, action_id, template_arg, args): + lv_comp = await cg.get_variable(config[CONF_LVGL_ID]) async with LambdaContext(LVGL_COMP_ARG, where=action_id) as context: lv_add(lvgl_comp.set_paused(False, False)) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) - await cg.register_parented(var, config[CONF_ID]) + await cg.register_parented(var, lv_comp) return var @@ -263,14 +260,15 @@ def focused_id(value): ObjUpdateAction, cv.Any( cv.maybe_simple_value( - { - cv.Optional(CONF_GROUP): cv.use_id(lv_group_t), - cv.Required(CONF_ACTION): cv.one_of( - "MARK", "RESTORE", "NEXT", "PREVIOUS", upper=True - ), - cv.GenerateID(CONF_LVGL_ID): cv.use_id(LvglComponent), - cv.Optional(CONF_FREEZE, default=False): cv.boolean, - }, + LVGL_SCHEMA.extend( + { + cv.Optional(CONF_GROUP): cv.use_id(lv_group_t), + cv.Required(CONF_ACTION): cv.one_of( + "MARK", "RESTORE", "NEXT", "PREVIOUS", upper=True + ), + cv.Optional(CONF_FREEZE, default=False): cv.boolean, + } + ), key=CONF_ACTION, ), cv.maybe_simple_value( From 3f123d7542f850e7abe30d6196ec99356a9e343c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Nov 2024 12:42:36 +1300 Subject: [PATCH 0584/1052] Bump pypa/gh-action-pypi-publish from 1.11.0 to 1.12.2 (#7730) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 82d7ae5ee8..096b00f0f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.11.0 + uses: pypa/gh-action-pypi-publish@v1.12.2 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From 2f77d316905f688fef7d899dfedc004ba6a73a49 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 8 Nov 2024 03:38:13 +0000 Subject: [PATCH 0585/1052] OTA: Fix IPv6 and multiple address support (#7414) --- esphome/__main__.py | 4 +-- esphome/espota2.py | 69 +++++++++++++++++++------------------- esphome/helpers.py | 82 ++++++++++++++++++++++++++++++++++++--------- esphome/mqtt.py | 11 ++++-- esphome/zeroconf.py | 8 ++--- 5 files changed, 117 insertions(+), 57 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index cf2741dbdb..85ab3cc00c 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -38,7 +38,7 @@ from esphome.const import ( SECRETS_FILES, ) from esphome.core import CORE, EsphomeError, coroutine -from esphome.helpers import indent, is_ip_address, get_bool_env +from esphome.helpers import get_bool_env, indent, is_ip_address from esphome.log import Fore, color, setup_log from esphome.util import ( get_serial_ports, @@ -378,7 +378,7 @@ def show_logs(config, args, port): port = mqtt.get_esphome_device_ip( config, args.username, args.password, args.client_id - ) + )[0] from esphome.components.api.client import run_logs diff --git a/esphome/espota2.py b/esphome/espota2.py index 580536153a..94b845b246 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -10,7 +10,7 @@ import sys import time from esphome.core import EsphomeError -from esphome.helpers import is_ip_address, resolve_ip_address +from esphome.helpers import resolve_ip_address RESPONSE_OK = 0x00 RESPONSE_REQUEST_AUTH = 0x01 @@ -311,44 +311,45 @@ def perform_ota( def run_ota_impl_(remote_host, remote_port, password, filename): - if is_ip_address(remote_host): - _LOGGER.info("Connecting to %s", remote_host) - ip = remote_host - else: - _LOGGER.info("Resolving IP address of %s", remote_host) - try: - ip = resolve_ip_address(remote_host) - except EsphomeError as err: - _LOGGER.error( - "Error resolving IP address of %s. Is it connected to WiFi?", - remote_host, - ) - _LOGGER.error( - "(If this error persists, please set a static IP address: " - "https://esphome.io/components/wifi.html#manual-ips)" - ) - raise OTAError(err) from err - _LOGGER.info(" -> %s", ip) - - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(10.0) try: - sock.connect((ip, remote_port)) - except OSError as err: - sock.close() - _LOGGER.error("Connecting to %s:%s failed: %s", remote_host, remote_port, err) - return 1 + res = resolve_ip_address(remote_host, remote_port) + except EsphomeError as err: + _LOGGER.error( + "Error resolving IP address of %s. Is it connected to WiFi?", + remote_host, + ) + _LOGGER.error( + "(If this error persists, please set a static IP address: " + "https://esphome.io/components/wifi.html#manual-ips)" + ) + raise OTAError(err) from err - with open(filename, "rb") as file_handle: + for r in res: + af, socktype, _, _, sa = r + _LOGGER.info("Connecting to %s port %s...", sa[0], sa[1]) + sock = socket.socket(af, socktype) + sock.settimeout(10.0) try: - perform_ota(sock, password, file_handle, filename) - except OTAError as err: - _LOGGER.error(str(err)) - return 1 - finally: + sock.connect(sa) + except OSError as err: sock.close() + _LOGGER.error("Connecting to %s port %s failed: %s", sa[0], sa[1], err) + continue - return 0 + _LOGGER.info("Connected to %s", sa[0]) + with open(filename, "rb") as file_handle: + try: + perform_ota(sock, password, file_handle, filename) + except OTAError as err: + _LOGGER.error(str(err)) + return 1 + finally: + sock.close() + + return 0 + + _LOGGER.error("Connection failed.") + return 1 def run_ota(remote_host, remote_port, password, filename): diff --git a/esphome/helpers.py b/esphome/helpers.py index 2a7e5cd9b6..8aae43c2bb 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -1,5 +1,6 @@ import codecs from contextlib import suppress +import ipaddress import logging import os from pathlib import Path @@ -91,12 +92,8 @@ def mkdir_p(path): def is_ip_address(host): - parts = host.split(".") - if len(parts) != 4: - return False try: - for p in parts: - int(p) + ipaddress.ip_address(host) return True except ValueError: return False @@ -127,25 +124,80 @@ def _resolve_with_zeroconf(host): return info -def resolve_ip_address(host): +def addr_preference_(res): + # Trivial alternative to RFC6724 sorting. Put sane IPv6 first, then + # Legacy IP, then IPv6 link-local addresses without an actual link. + sa = res[4] + ip = ipaddress.ip_address(sa[0]) + if ip.version == 4: + return 2 + if ip.is_link_local and sa[3] == 0: + return 3 + return 1 + + +def resolve_ip_address(host, port): import socket from esphome.core import EsphomeError + # There are five cases here. The host argument could be one of: + # • a *list* of IP addresses discovered by MQTT, + # • a single IP address specified by the user, + # • a .local hostname to be resolved by mDNS, + # • a normal hostname to be resolved in DNS, or + # • A URL from which we should extract the hostname. + # + # In each of the first three cases, we end up with IP addresses in + # string form which need to be converted to a 5-tuple to be used + # for the socket connection attempt. The easiest way to construct + # those is to pass the IP address string to getaddrinfo(). Which, + # coincidentally, is how we do hostname lookups in the other cases + # too. So first build a list which contains either IP addresses or + # a single hostname, then call getaddrinfo() on each element of + # that list. + errs = [] + if isinstance(host, list): + addr_list = host + elif is_ip_address(host): + addr_list = [host] + else: + url = urlparse(host) + if url.scheme != "": + host = url.hostname - if host.endswith(".local"): + addr_list = [] + if host.endswith(".local"): + try: + _LOGGER.info("Resolving IP address of %s in mDNS", host) + addr_list = _resolve_with_zeroconf(host) + except EsphomeError as err: + errs.append(str(err)) + + # If not mDNS, or if mDNS failed, use normal DNS + if not addr_list: + addr_list = [host] + + # Now we have a list containing either IP addresses or a hostname + res = [] + for addr in addr_list: + if not is_ip_address(addr): + _LOGGER.info("Resolving IP address of %s", host) try: - return _resolve_with_zeroconf(host) - except EsphomeError as err: + r = socket.getaddrinfo(addr, port, proto=socket.IPPROTO_TCP) + except OSError as err: errs.append(str(err)) + raise EsphomeError( + f"Error resolving IP address: {', '.join(errs)}" + ) from err - try: - host_url = host if (urlparse(host).scheme != "") else "http://" + host - return socket.gethostbyname(urlparse(host_url).hostname) - except OSError as err: - errs.append(str(err)) - raise EsphomeError(f"Error resolving IP address: {', '.join(errs)}") from err + res = res + r + + # Zeroconf tends to give us link-local IPv6 addresses without specifying + # the link. Put those last in the list to be attempted. + res.sort(key=addr_preference_) + return res def get_bool_env(var, default=False): diff --git a/esphome/mqtt.py b/esphome/mqtt.py index d55fb0202d..2f90c49025 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -175,8 +175,15 @@ def get_esphome_device_ip( _LOGGER.Warn("Wrong device answer") return - if "ip" in data: - dev_ip = data["ip"] + dev_ip = [] + key = "ip" + n = 0 + while key in data: + dev_ip.append(data[key]) + n = n + 1 + key = "ip" + str(n) + + if dev_ip: client.disconnect() def on_connect(client, userdata, flags, return_code): diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index b3ee64e259..76049fa776 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -182,8 +182,8 @@ class EsphomeZeroconf(Zeroconf): if ( info.load_from_cache(self) or (timeout and info.request(self, timeout * 1000)) - ) and (addresses := info.ip_addresses_by_version(IPVersion.V4Only)): - return str(addresses[0]) + ) and (addresses := info.parsed_scoped_addresses(IPVersion.All)): + return addresses return None @@ -194,6 +194,6 @@ class AsyncEsphomeZeroconf(AsyncZeroconf): if ( info.load_from_cache(self.zeroconf) or (timeout and await info.async_request(self.zeroconf, timeout * 1000)) - ) and (addresses := info.ip_addresses_by_version(IPVersion.V4Only)): - return str(addresses[0]) + ) and (addresses := info.parsed_scoped_addresses(IPVersion.All)): + return addresses return None From 2ec17eed588f2e0ca2392337034981bdb83fcdcf Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Sun, 27 Oct 2024 13:17:09 +1100 Subject: [PATCH 0586/1052] [rpi_dpi_rgb] Fix get_width and height (Bugfix) (#7675) Co-authored-by: clydeps --- .../components/rpi_dpi_rgb/rpi_dpi_rgb.cpp | 20 +++++++++++++++++++ esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index 655b469b91..ba09171649 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -84,6 +84,26 @@ void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uin ESP_LOGE(TAG, "lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err)); } +int RpiDpiRgb::get_width() { + switch (this->rotation_) { + case display::DISPLAY_ROTATION_90_DEGREES: + case display::DISPLAY_ROTATION_270_DEGREES: + return this->get_height_internal(); + default: + return this->get_width_internal(); + } +} + +int RpiDpiRgb::get_height() { + switch (this->rotation_) { + case display::DISPLAY_ROTATION_90_DEGREES: + case display::DISPLAY_ROTATION_270_DEGREES: + return this->get_width_internal(); + default: + return this->get_height_internal(); + } +} + void RpiDpiRgb::draw_pixel_at(int x, int y, Color color) { if (!this->get_clipping().inside(x, y)) return; // NOLINT diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h index 10f77a2624..7525040cd1 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h @@ -24,6 +24,7 @@ class RpiDpiRgb : public display::Display { void update() override { this->do_update_(); } void setup() override; void loop() override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; void draw_pixel_at(int x, int y, Color color) override; @@ -44,8 +45,8 @@ class RpiDpiRgb : public display::Display { this->width_ = width; this->height_ = height; } - int get_width() override { return this->width_; } - int get_height() override { return this->height_; } + int get_width() override; + int get_height() override; void set_hsync_back_porch(uint16_t hsync_back_porch) { this->hsync_back_porch_ = hsync_back_porch; } void set_hsync_front_porch(uint16_t hsync_front_porch) { this->hsync_front_porch_ = hsync_front_porch; } void set_hsync_pulse_width(uint16_t hsync_pulse_width) { this->hsync_pulse_width_ = hsync_pulse_width; } From e85cbf26f881e91c4f02f4612d1b92d556ff56af Mon Sep 17 00:00:00 2001 From: Bonne Eggleston Date: Mon, 28 Oct 2024 20:52:39 -0700 Subject: [PATCH 0587/1052] Fixes modbus timing error (#7674) --- esphome/components/modbus/modbus.cpp | 36 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index f8dd4c18b9..8544b50261 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -15,23 +15,33 @@ void Modbus::setup() { void Modbus::loop() { const uint32_t now = millis(); - if (now - this->last_modbus_byte_ > 50) { - this->rx_buffer_.clear(); - this->last_modbus_byte_ = now; - } - // stop blocking new send commands after send_wait_time_ ms regardless if a response has been received since then - if (now - this->last_send_ > send_wait_time_) { - waiting_for_response = 0; - } - while (this->available()) { uint8_t byte; this->read_byte(&byte); if (this->parse_modbus_byte_(byte)) { this->last_modbus_byte_ = now; } else { + size_t at = this->rx_buffer_.size(); + if (at > 0) { + ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse failed", at); + this->rx_buffer_.clear(); + } + } + } + + if (now - this->last_modbus_byte_ > 50) { + size_t at = this->rx_buffer_.size(); + if (at > 0) { + ESP_LOGV(TAG, "Clearing buffer of %d bytes - timeout", at); this->rx_buffer_.clear(); } + + // stop blocking new send commands after sent_wait_time_ ms after response received + if (now - this->last_send_ > send_wait_time_) { + if (waiting_for_response > 0) + ESP_LOGV(TAG, "Stop waiting for response from %d", waiting_for_response); + waiting_for_response = 0; + } } } @@ -39,7 +49,7 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { size_t at = this->rx_buffer_.size(); this->rx_buffer_.push_back(byte); const uint8_t *raw = &this->rx_buffer_[0]; - ESP_LOGV(TAG, "Modbus received Byte %d (0X%x)", byte, byte); + ESP_LOGVV(TAG, "Modbus received Byte %d (0X%x)", byte, byte); // Byte 0: modbus address (match all) if (at == 0) return true; @@ -144,8 +154,10 @@ bool Modbus::parse_modbus_byte_(uint8_t byte) { ESP_LOGW(TAG, "Got Modbus frame from unknown address 0x%02X! ", address); } - // return false to reset buffer - return false; + // reset buffer + ESP_LOGV(TAG, "Clearing buffer of %d bytes - parse succeeded", at); + this->rx_buffer_.clear(); + return true; } void Modbus::dump_config() { From 3a25eaca3f90345dd45ffc7e03734f8a6c0c3c45 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:32:18 +1100 Subject: [PATCH 0588/1052] [lvgl] Ensure images are configured before using them. (Bugfix) (#7721) --- esphome/components/lvgl/widgets/animimg.py | 7 ++++--- esphome/components/lvgl/widgets/img.py | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/widgets/animimg.py b/esphome/components/lvgl/widgets/animimg.py index 3b20008c3d..8adea72ad3 100644 --- a/esphome/components/lvgl/widgets/animimg.py +++ b/esphome/components/lvgl/widgets/animimg.py @@ -60,9 +60,10 @@ class AnimimgType(WidgetType): lvgl_components_required.add(CONF_IMAGE) lvgl_components_required.add(CONF_ANIMIMG) if CONF_SRC in config: - for x in config[CONF_SRC]: - await cg.get_variable(x) - srcs = [await lv_image.process(x) for x in config[CONF_SRC]] + srcs = [ + await lv_image.process(await cg.get_variable(x)) + for x in config[CONF_SRC] + ] src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs) count = len(config[CONF_SRC]) lv.animimg_set_src(w.obj, src_id, count) diff --git a/esphome/components/lvgl/widgets/img.py b/esphome/components/lvgl/widgets/img.py index 59b2c97c63..931d0c0b5b 100644 --- a/esphome/components/lvgl/widgets/img.py +++ b/esphome/components/lvgl/widgets/img.py @@ -1,3 +1,4 @@ +import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ANGLE, CONF_MODE @@ -64,6 +65,7 @@ class ImgType(WidgetType): async def to_code(self, w: Widget, config): if src := config.get(CONF_SRC): + src = await cg.get_variable(src) lv.img_set_src(w.obj, await lv_image.process(src)) if (cf_angle := config.get(CONF_ANGLE)) is not None: pivot_x = config[CONF_PIVOT_X] From 551ea378824bc18df6523fa8ce810833f31b205e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:02:31 +1300 Subject: [PATCH 0589/1052] Bump version to 2024.10.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 032a4c79a0..c9decd4fd2 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.10.2" +__version__ = "2024.10.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 335faf858baabee77a8104379e5fcba8da5ac58b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 11 Nov 2024 08:55:19 +1300 Subject: [PATCH 0590/1052] Fix dashboard ip resolving (#7747) --- esphome/dashboard/status/mdns.py | 7 +++---- esphome/dashboard/web_server.py | 4 ++-- esphome/zeroconf.py | 6 ++++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/esphome/dashboard/status/mdns.py b/esphome/dashboard/status/mdns.py index bd212bc563..9f6399ca8b 100644 --- a/esphome/dashboard/status/mdns.py +++ b/esphome/dashboard/status/mdns.py @@ -26,7 +26,7 @@ class MDNSStatus: self.host_mdns_state: dict[str, bool | None] = {} self._loop = asyncio.get_running_loop() - async def async_resolve_host(self, host_name: str) -> str | None: + async def async_resolve_host(self, host_name: str) -> list[str] | None: """Resolve a host name to an address in a thread-safe manner.""" if aiozc := self.aiozc: return await aiozc.async_resolve_host(host_name) @@ -50,13 +50,12 @@ class MDNSStatus: poll_names.setdefault(entry.name, set()).add(entry) elif (online := host_mdns_state.get(entry.name, SENTINEL)) != SENTINEL: entries.async_set_state(entry, bool_to_entry_state(online)) - if poll_names and self.aiozc: results = await asyncio.gather( *(self.aiozc.async_resolve_host(name) for name in poll_names) ) - for name, address in zip(poll_names, results): - result = bool(address) + for name, address_list in zip(poll_names, results): + result = bool(address_list) host_mdns_state[name] = result for entry in poll_names[name]: entries.async_set_state(entry, bool_to_entry_state(result)) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 9aeece9aab..07f7f019f8 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -320,12 +320,12 @@ class EsphomePortCommandWebSocket(EsphomeCommandWebSocket): and "api" in entry.loaded_integrations ): if (mdns := dashboard.mdns_status) and ( - address := await mdns.async_resolve_host(entry.name) + address_list := await mdns.async_resolve_host(entry.name) ): # Use the IP address if available but only # if the API is loaded and the device is online # since MQTT logging will not work otherwise - port = address + port = address_list[0] elif ( entry.address and ( diff --git a/esphome/zeroconf.py b/esphome/zeroconf.py index 76049fa776..5a92a4ed7c 100644 --- a/esphome/zeroconf.py +++ b/esphome/zeroconf.py @@ -176,7 +176,7 @@ def _make_host_resolver(host: str) -> HostResolver: class EsphomeZeroconf(Zeroconf): - def resolve_host(self, host: str, timeout: float = 3.0) -> str | None: + def resolve_host(self, host: str, timeout: float = 3.0) -> list[str] | None: """Resolve a host name to an IP address.""" info = _make_host_resolver(host) if ( @@ -188,7 +188,9 @@ class EsphomeZeroconf(Zeroconf): class AsyncEsphomeZeroconf(AsyncZeroconf): - async def async_resolve_host(self, host: str, timeout: float = 3.0) -> str | None: + async def async_resolve_host( + self, host: str, timeout: float = 3.0 + ) -> list[str] | None: """Resolve a host name to an IP address.""" info = _make_host_resolver(host) if ( From 7c00c5db7020f09a6e8b3ffc3cb637fa2aa2fdc6 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 11 Nov 2024 09:44:02 +1300 Subject: [PATCH 0591/1052] [docker] Bump curl, iputils-ping and libssl-dev (#7748) --- docker/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 44ee879a12..ed6ce083a8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -32,9 +32,9 @@ RUN \ python3-setuptools=66.1.1-1 \ python3-venv=3.11.2-1+b1 \ python3-wheel=0.38.4-2 \ - iputils-ping=3:20221126-1 \ + iputils-ping=3:20221126-1+deb12u1 \ git=1:2.39.5-0+deb12u1 \ - curl=7.88.1-10+deb12u7 \ + curl=7.88.1-10+deb12u8 \ openssh-client=1:9.2p1-2+deb12u3 \ python3-cffi=1.15.1-5 \ libcairo2=1.16.0-7 \ @@ -97,7 +97,7 @@ BUILD_DEPS=" zlib1g-dev=1:1.2.13.dfsg-1 libjpeg-dev=1:2.1.5-2 libfreetype-dev=2.12.1+dfsg-5+deb12u3 - libssl-dev=3.0.14-1~deb12u2 + libssl-dev=3.0.15-1~deb12u1 libffi-dev=3.4.4-1 libopenjp2-7=2.5.0-2 libtiff6=4.5.0-6+deb12u1 From c35240ca3207f7efef3cb0dcd146c32c5e33c6c7 Mon Sep 17 00:00:00 2001 From: Kyle Cascade Date: Sun, 10 Nov 2024 17:13:43 -0800 Subject: [PATCH 0592/1052] Remove the choice for MQTT logging if it is disabled (#7723) --- esphome/__main__.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 85ab3cc00c..86d529e1bf 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -20,6 +20,8 @@ from esphome.const import ( CONF_DEASSERT_RTS_DTR, CONF_DISABLED, CONF_ESPHOME, + CONF_LEVEL, + CONF_LOG_TOPIC, CONF_LOGGER, CONF_MDNS, CONF_MQTT, @@ -30,6 +32,7 @@ from esphome.const import ( CONF_PLATFORMIO_OPTIONS, CONF_PORT, CONF_SUBSTITUTIONS, + CONF_TOPIC, PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, @@ -95,8 +98,12 @@ def choose_upload_log_host( options.append((f"Over The Air ({CORE.address})", CORE.address)) if default == "OTA": return CORE.address - if show_mqtt and CONF_MQTT in CORE.config: - options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT")) + if ( + show_mqtt + and (mqtt_config := CORE.config.get(CONF_MQTT)) + and mqtt_logging_enabled(mqtt_config) + ): + options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT")) if default == "OTA": return "MQTT" if default is not None: @@ -106,6 +113,17 @@ def choose_upload_log_host( return choose_prompt(options, purpose=purpose) +def mqtt_logging_enabled(mqtt_config): + log_topic = mqtt_config[CONF_LOG_TOPIC] + if log_topic is None: + return False + if CONF_TOPIC not in log_topic: + return False + if log_topic.get(CONF_LEVEL, None) == "NONE": + return False + return True + + def get_port_type(port): if port.startswith("/") or port.startswith("COM"): return "SERIAL" From d885d65c9bc7667c07afc1066f4bbc00efe09aff Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:18:05 +1100 Subject: [PATCH 0593/1052] [sensor] Make some values templatable (#7735) --- esphome/components/sensor/__init__.py | 26 +++++++++++++------ esphome/components/sensor/filter.cpp | 37 ++++++++++++++------------- esphome/components/sensor/filter.h | 19 +++++++------- tests/components/template/common.yaml | 19 ++++++++++++++ 4 files changed, 65 insertions(+), 36 deletions(-) diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 27338b8608..9dbad27102 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -335,19 +335,28 @@ def sensor_schema( return SENSOR_SCHEMA.extend(schema) -@FILTER_REGISTRY.register("offset", OffsetFilter, cv.float_) +@FILTER_REGISTRY.register("offset", OffsetFilter, cv.templatable(cv.float_)) async def offset_filter_to_code(config, filter_id): - return cg.new_Pvariable(filter_id, config) + template_ = await cg.templatable(config, [], float) + return cg.new_Pvariable(filter_id, template_) -@FILTER_REGISTRY.register("multiply", MultiplyFilter, cv.float_) +@FILTER_REGISTRY.register("multiply", MultiplyFilter, cv.templatable(cv.float_)) async def multiply_filter_to_code(config, filter_id): - return cg.new_Pvariable(filter_id, config) + template_ = await cg.templatable(config, [], float) + return cg.new_Pvariable(filter_id, template_) -@FILTER_REGISTRY.register("filter_out", FilterOutValueFilter, cv.float_) +@FILTER_REGISTRY.register( + "filter_out", + FilterOutValueFilter, + cv.Any(cv.templatable(cv.float_), [cv.templatable(cv.float_)]), +) async def filter_out_filter_to_code(config, filter_id): - return cg.new_Pvariable(filter_id, config) + if not isinstance(config, list): + config = [config] + template_ = [await cg.templatable(x, [], float) for x in config] + return cg.new_Pvariable(filter_id, template_) QUANTILE_SCHEMA = cv.All( @@ -573,7 +582,7 @@ async def heartbeat_filter_to_code(config, filter_id): TIMEOUT_SCHEMA = cv.maybe_simple_value( { cv.Required(CONF_TIMEOUT): cv.positive_time_period_milliseconds, - cv.Optional(CONF_VALUE, default="nan"): cv.float_, + cv.Optional(CONF_VALUE, default="nan"): cv.templatable(cv.float_), }, key=CONF_TIMEOUT, ) @@ -581,7 +590,8 @@ TIMEOUT_SCHEMA = cv.maybe_simple_value( @FILTER_REGISTRY.register("timeout", TimeoutFilter, TIMEOUT_SCHEMA) async def timeout_filter_to_code(config, filter_id): - var = cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], config[CONF_VALUE]) + template_ = await cg.templatable(config[CONF_VALUE], [], float) + var = cg.new_Pvariable(filter_id, config[CONF_TIMEOUT], template_) await cg.register_component(var, {}) return var diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index bcf1fc8269..0a8740dd5b 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -288,36 +288,36 @@ optional LambdaFilter::new_value(float value) { } // OffsetFilter -OffsetFilter::OffsetFilter(float offset) : offset_(offset) {} +OffsetFilter::OffsetFilter(TemplatableValue offset) : offset_(std::move(offset)) {} -optional OffsetFilter::new_value(float value) { return value + this->offset_; } +optional OffsetFilter::new_value(float value) { return value + this->offset_.value(); } // MultiplyFilter -MultiplyFilter::MultiplyFilter(float multiplier) : multiplier_(multiplier) {} +MultiplyFilter::MultiplyFilter(TemplatableValue multiplier) : multiplier_(std::move(multiplier)) {} -optional MultiplyFilter::new_value(float value) { return value * this->multiplier_; } +optional MultiplyFilter::new_value(float value) { return value * this->multiplier_.value(); } // FilterOutValueFilter -FilterOutValueFilter::FilterOutValueFilter(float value_to_filter_out) : value_to_filter_out_(value_to_filter_out) {} +FilterOutValueFilter::FilterOutValueFilter(std::vector> values_to_filter_out) + : values_to_filter_out_(std::move(values_to_filter_out)) {} optional FilterOutValueFilter::new_value(float value) { - if (std::isnan(this->value_to_filter_out_)) { - if (std::isnan(value)) { - return {}; - } else { - return value; + int8_t accuracy = this->parent_->get_accuracy_decimals(); + float accuracy_mult = powf(10.0f, accuracy); + for (auto filter_value : this->values_to_filter_out_) { + if (std::isnan(filter_value.value())) { + if (std::isnan(value)) { + return {}; + } + continue; } - } else { - int8_t accuracy = this->parent_->get_accuracy_decimals(); - float accuracy_mult = powf(10.0f, accuracy); - float rounded_filter_out = roundf(accuracy_mult * this->value_to_filter_out_); + float rounded_filter_out = roundf(accuracy_mult * filter_value.value()); float rounded_value = roundf(accuracy_mult * value); if (rounded_filter_out == rounded_value) { return {}; - } else { - return value; } } + return value; } // ThrottleFilter @@ -383,11 +383,12 @@ void OrFilter::initialize(Sensor *parent, Filter *next) { // TimeoutFilter optional TimeoutFilter::new_value(float value) { - this->set_timeout("timeout", this->time_period_, [this]() { this->output(this->value_); }); + this->set_timeout("timeout", this->time_period_, [this]() { this->output(this->value_.value()); }); return value; } -TimeoutFilter::TimeoutFilter(uint32_t time_period, float new_value) : time_period_(time_period), value_(new_value) {} +TimeoutFilter::TimeoutFilter(uint32_t time_period, TemplatableValue new_value) + : time_period_(time_period), value_(std::move(new_value)) {} float TimeoutFilter::get_setup_priority() const { return setup_priority::HARDWARE; } // DebounceFilter diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 92b1d8d240..86586b458d 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -5,6 +5,7 @@ #include #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include "esphome/core/automation.h" namespace esphome { namespace sensor { @@ -273,34 +274,33 @@ class LambdaFilter : public Filter { /// A simple filter that adds `offset` to each value it receives. class OffsetFilter : public Filter { public: - explicit OffsetFilter(float offset); + explicit OffsetFilter(TemplatableValue offset); optional new_value(float value) override; protected: - float offset_; + TemplatableValue offset_; }; /// A simple filter that multiplies to each value it receives by `multiplier`. class MultiplyFilter : public Filter { public: - explicit MultiplyFilter(float multiplier); - + explicit MultiplyFilter(TemplatableValue multiplier); optional new_value(float value) override; protected: - float multiplier_; + TemplatableValue multiplier_; }; /// A simple filter that only forwards the filter chain if it doesn't receive `value_to_filter_out`. class FilterOutValueFilter : public Filter { public: - explicit FilterOutValueFilter(float value_to_filter_out); + explicit FilterOutValueFilter(std::vector> values_to_filter_out); optional new_value(float value) override; protected: - float value_to_filter_out_; + std::vector> values_to_filter_out_; }; class ThrottleFilter : public Filter { @@ -316,8 +316,7 @@ class ThrottleFilter : public Filter { class TimeoutFilter : public Filter, public Component { public: - explicit TimeoutFilter(uint32_t time_period, float new_value); - void set_value(float new_value) { this->value_ = new_value; } + explicit TimeoutFilter(uint32_t time_period, TemplatableValue new_value); optional new_value(float value) override; @@ -325,7 +324,7 @@ class TimeoutFilter : public Filter, public Component { protected: uint32_t time_period_; - float value_; + TemplatableValue value_; }; class DebounceFilter : public Filter, public Component { diff --git a/tests/components/template/common.yaml b/tests/components/template/common.yaml index 3565926933..79201fbe07 100644 --- a/tests/components/template/common.yaml +++ b/tests/components/template/common.yaml @@ -9,6 +9,25 @@ sensor: return 0.0; } update_interval: 60s + filters: + - offset: 10 + - multiply: 1 + - offset: !lambda return 10; + - multiply: !lambda return 2; + - filter_out: + - 10 + - 20 + - !lambda return 10; + - filter_out: 10 + - filter_out: !lambda return NAN; + - timeout: + timeout: 10s + value: !lambda return 10; + - timeout: + timeout: 1h + value: 20.0 + - timeout: + timeout: 1d esphome: on_boot: From ffee2f0e8878edf6c5feafbd643a7ef6bcc3fc7a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:07:48 +1100 Subject: [PATCH 0594/1052] [lvgl] Implement keypads (#7719) --- esphome/components/lvgl/__init__.py | 24 +++++++- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/encoders.py | 19 +++--- esphome/components/lvgl/keypads.py | 77 +++++++++++++++++++++++++ esphome/components/lvgl/lvgl_esphome.h | 11 +--- esphome/components/lvgl/types.py | 1 + tests/components/lvgl/lvgl-package.yaml | 10 ++++ 7 files changed, 123 insertions(+), 20 deletions(-) create mode 100644 esphome/components/lvgl/keypads.py diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 7476c0a09c..d03adc9624 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -7,6 +7,7 @@ import esphome.config_validation as cv from esphome.const import ( CONF_AUTO_CLEAR_ENABLED, CONF_BUFFER_SIZE, + CONF_GROUP, CONF_ID, CONF_LAMBDA, CONF_ON_IDLE, @@ -23,9 +24,15 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, focused_widgets, update_to_code from .defines import add_define -from .encoders import ENCODERS_CONFIG, encoders_to_code, initial_focus_to_code +from .encoders import ( + ENCODERS_CONFIG, + encoders_to_code, + get_default_group, + initial_focus_to_code, +) from .gradient import GRADIENT_SCHEMA, gradients_to_code from .hello_world import get_hello_world +from .keypads import KEYPADS_CONFIG, keypads_to_code from .lv_validation import lv_bool, lv_images_used from .lvcode import LvContext, LvglComponent, lvgl_static from .schemas import ( @@ -158,6 +165,13 @@ def multi_conf_validate(configs: list[dict]): display_list = [disp for disps in displays for disp in disps] if len(display_list) != len(set(display_list)): raise cv.Invalid("A display ID may be used in only one LVGL instance") + for config in configs: + for item in (df.CONF_ENCODERS, df.CONF_KEYPADS): + for enc in config.get(item, ()): + if CONF_GROUP not in enc: + raise cv.Invalid( + f"'{item}' must have an explicit group set when using multiple LVGL instances" + ) base_config = configs[0] for config in configs[1:]: for item in ( @@ -173,7 +187,8 @@ def multi_conf_validate(configs: list[dict]): def final_validation(configs): - multi_conf_validate(configs) + if len(configs) != 1: + multi_conf_validate(configs) global_config = full_config.get() for config in configs: if pages := config.get(CONF_PAGES): @@ -275,6 +290,7 @@ async def to_code(configs): else: add_define("LV_FONT_DEFAULT", await lvalid.lv_font.process(default_font)) cg.add(lvgl_static.esphome_lvgl_init()) + default_group = get_default_group(config_0) for config in configs: frac = config[CONF_BUFFER_SIZE] @@ -303,7 +319,8 @@ async def to_code(configs): lv_scr_act = get_scr_act(lv_component) async with LvContext(): await touchscreens_to_code(lv_component, config) - await encoders_to_code(lv_component, config) + await encoders_to_code(lv_component, config, default_group) + await keypads_to_code(lv_component, config, default_group) await theme_to_code(config) await styles_to_code(config) await gradients_to_code(config) @@ -430,6 +447,7 @@ LVGL_SCHEMA = ( cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA, cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, + cv.Optional(df.CONF_KEYPADS, default=None): KEYPADS_CONFIG, cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t), cv.Optional(df.CONF_RESUME_ON_INPUT, default=True): cv.boolean, } diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 4d48028611..ea345fa55c 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -438,6 +438,7 @@ CONF_HEADER_MODE = "header_mode" CONF_HOME = "home" CONF_INITIAL_FOCUS = "initial_focus" CONF_KEY_CODE = "key_code" +CONF_KEYPADS = "keypads" CONF_LAYOUT = "layout" CONF_LEFT_BUTTON = "left_button" CONF_LINE_WIDTH = "line_width" diff --git a/esphome/components/lvgl/encoders.py b/esphome/components/lvgl/encoders.py index 81bcda95b4..952572df43 100644 --- a/esphome/components/lvgl/encoders.py +++ b/esphome/components/lvgl/encoders.py @@ -17,7 +17,7 @@ from .defines import ( from .helpers import lvgl_components_required, requires_component from .lvcode import lv, lv_add, lv_assign, lv_expr, lv_Pvariable from .schemas import ENCODER_SCHEMA -from .types import lv_group_t, lv_indev_type_t +from .types import lv_group_t, lv_indev_type_t, lv_key_t ENCODERS_CONFIG = cv.ensure_list( ENCODER_SCHEMA.extend( @@ -39,10 +39,13 @@ ENCODERS_CONFIG = cv.ensure_list( ) -async def encoders_to_code(var, config): - default_group = lv_Pvariable(lv_group_t, config[CONF_DEFAULT_GROUP]) - lv_assign(default_group, lv_expr.group_create()) - lv.group_set_default(default_group) +def get_default_group(config): + default_group = cg.Pvariable(config[CONF_DEFAULT_GROUP], lv_expr.group_create()) + cg.add(lv.group_set_default(default_group)) + return default_group + + +async def encoders_to_code(var, config, default_group): for enc_conf in config[CONF_ENCODERS]: lvgl_components_required.add("KEY_LISTENER") lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds @@ -54,14 +57,14 @@ async def encoders_to_code(var, config): if sensor_config := enc_conf.get(CONF_SENSOR): if isinstance(sensor_config, dict): b_sensor = await cg.get_variable(sensor_config[CONF_LEFT_BUTTON]) - cg.add(listener.set_left_button(b_sensor)) + cg.add(listener.add_button(b_sensor, lv_key_t.LV_KEY_LEFT)) b_sensor = await cg.get_variable(sensor_config[CONF_RIGHT_BUTTON]) - cg.add(listener.set_right_button(b_sensor)) + cg.add(listener.add_button(b_sensor, lv_key_t.LV_KEY_RIGHT)) else: sensor_config = await cg.get_variable(sensor_config) lv_add(listener.set_sensor(sensor_config)) b_sensor = await cg.get_variable(enc_conf[CONF_ENTER_BUTTON]) - cg.add(listener.set_enter_button(b_sensor)) + cg.add(listener.add_button(b_sensor, lv_key_t.LV_KEY_ENTER)) if group := enc_conf.get(CONF_GROUP): group = lv_Pvariable(lv_group_t, group) lv_assign(group, lv_expr.group_create()) diff --git a/esphome/components/lvgl/keypads.py b/esphome/components/lvgl/keypads.py new file mode 100644 index 0000000000..5e2953d57f --- /dev/null +++ b/esphome/components/lvgl/keypads.py @@ -0,0 +1,77 @@ +import esphome.codegen as cg +from esphome.components.binary_sensor import BinarySensor +import esphome.config_validation as cv +from esphome.const import CONF_GROUP, CONF_ID + +from .defines import ( + CONF_ENCODERS, + CONF_INITIAL_FOCUS, + CONF_KEYPADS, + CONF_LONG_PRESS_REPEAT_TIME, + CONF_LONG_PRESS_TIME, + literal, +) +from .helpers import lvgl_components_required +from .lvcode import lv, lv_assign, lv_expr, lv_Pvariable +from .schemas import ENCODER_SCHEMA +from .types import lv_group_t, lv_indev_type_t + +KEYPAD_KEYS = ( + "up", + "down", + "right", + "left", + "esc", + "del", + "backspace", + "enter", + "next", + "prev", + "home", + "end", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "#", + "*", +) + +KEYPADS_CONFIG = cv.ensure_list( + ENCODER_SCHEMA.extend( + {cv.Optional(key): cv.use_id(BinarySensor) for key in KEYPAD_KEYS} + ) +) + + +async def keypads_to_code(var, config, default_group): + for enc_conf in config[CONF_KEYPADS]: + lvgl_components_required.add("KEY_LISTENER") + lpt = enc_conf[CONF_LONG_PRESS_TIME].total_milliseconds + lprt = enc_conf[CONF_LONG_PRESS_REPEAT_TIME].total_milliseconds + listener = cg.new_Pvariable( + enc_conf[CONF_ID], lv_indev_type_t.LV_INDEV_TYPE_KEYPAD, lpt, lprt + ) + await cg.register_parented(listener, var) + for key in [x for x in enc_conf if x in KEYPAD_KEYS]: + b_sensor = await cg.get_variable(enc_conf[key]) + cg.add(listener.add_button(b_sensor, literal(f"LV_KEY_{key.upper()}"))) + if group := enc_conf.get(CONF_GROUP): + group = lv_Pvariable(lv_group_t, group) + lv_assign(group, lv_expr.group_create()) + else: + group = default_group + lv.indev_set_group(lv_expr.indev_drv_register(listener.get_drv()), group) + + +async def initial_focus_to_code(config): + for enc_conf in config[CONF_ENCODERS]: + if default_focus := enc_conf.get(CONF_INITIAL_FOCUS): + obj = await cg.get_variable(default_focus) + lv.group_focus_obj(obj) diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index dae07d5153..208cb1cbd5 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -256,15 +256,8 @@ class LVEncoderListener : public Parented { LVEncoderListener(lv_indev_type_t type, uint16_t lpt, uint16_t lprt); #ifdef USE_BINARY_SENSOR - void set_left_button(binary_sensor::BinarySensor *left_button) { - left_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_LEFT, state); }); - } - void set_right_button(binary_sensor::BinarySensor *right_button) { - right_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_RIGHT, state); }); - } - - void set_enter_button(binary_sensor::BinarySensor *enter_button) { - enter_button->add_on_state_callback([this](bool state) { this->event(LV_KEY_ENTER, state); }); + void add_button(binary_sensor::BinarySensor *button, lv_key_t key) { + button->add_on_state_callback([this, key](bool state) { this->event(key, state); }); } #endif diff --git a/esphome/components/lvgl/types.py b/esphome/components/lvgl/types.py index b504f24674..40e69119f0 100644 --- a/esphome/components/lvgl/types.py +++ b/esphome/components/lvgl/types.py @@ -40,6 +40,7 @@ void_ptr = cg.void.operator("ptr") lv_coord_t = cg.global_ns.namespace("lv_coord_t") lv_event_code_t = cg.global_ns.enum("lv_event_code_t") lv_indev_type_t = cg.global_ns.enum("lv_indev_type_t") +lv_key_t = cg.global_ns.enum("lv_key_t") FontEngine = lvgl_ns.class_("FontEngine") IdleTrigger = lvgl_ns.class_("IdleTrigger", automation.Trigger.template()) PauseTrigger = lvgl_ns.class_("PauseTrigger", automation.Trigger.template()) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 9bfbb5fc95..db0443b3bb 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -11,6 +11,12 @@ substitutions: check: "\U000F012C" arrow_down: "\U000F004B" +binary_sensor: + - id: enter_sensor + platform: template + - id: left_sensor + platform: template + lvgl: log_level: debug resume_on_input: true @@ -93,6 +99,10 @@ lvgl: - touchscreen_id: tft_touch long_press_repeat_time: 200ms long_press_time: 500ms + keypads: + - initial_focus: button_button + enter: enter_sensor + next: left_sensor msgboxes: - id: message_box From a2dccc4730566536b700985310016fe1299ae371 Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Mon, 11 Nov 2024 05:14:01 +0100 Subject: [PATCH 0595/1052] [midea] Add temperature validation in do_follow_me method (bugfix) (#7736) --- esphome/components/midea/air_conditioner.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index b5bf43b64f..a823680d03 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -3,6 +3,8 @@ #include "esphome/core/log.h" #include "air_conditioner.h" #include "ac_adapter.h" +#include +#include namespace esphome { namespace midea { @@ -121,7 +123,21 @@ void AirConditioner::dump_config() { void AirConditioner::do_follow_me(float temperature, bool beeper) { #ifdef USE_REMOTE_TRANSMITTER - IrFollowMeData data(static_cast(lroundf(temperature)), beeper); + // Check if temperature is finite (not NaN or infinite) + if (!std::isfinite(temperature)) { + ESP_LOGW(Constants::TAG, "Follow me action requires a finite temperature, got: %f", temperature); + return; + } + + // Round and convert temperature to long, then clamp and convert it to uint8_t + uint8_t temp_uint8 = + static_cast(std::max(0L, std::min(static_cast(UINT8_MAX), std::lroundf(temperature)))); + + ESP_LOGD(Constants::TAG, "Follow me action called with temperature: %f °C, rounded to: %u °C", temperature, + temp_uint8); + + // Create and transmit the data + IrFollowMeData data(temp_uint8, beeper); this->transmitter_.transmit(data); #else ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component"); From 58d028ac1375511362fac3bf524b86567ef584b0 Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Tue, 12 Nov 2024 06:19:42 +0300 Subject: [PATCH 0596/1052] Add OpenTherm component (part 3: rest of the sensors) (#7676) Co-authored-by: FreeBear Co-authored-by: FreeBear-nc <67865163+FreeBear-nc@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/opentherm/__init__.py | 30 +- .../opentherm/binary_sensor/__init__.py | 33 ++ esphome/components/opentherm/const.py | 6 + esphome/components/opentherm/generate.py | 2 + esphome/components/opentherm/hub.cpp | 42 +- esphome/components/opentherm/hub.h | 52 ++- esphome/components/opentherm/input.h | 18 + esphome/components/opentherm/input.py | 51 +++ .../components/opentherm/number/__init__.py | 74 ++++ .../components/opentherm/number/number.cpp | 40 ++ esphome/components/opentherm/number/number.h | 31 ++ esphome/components/opentherm/opentherm.h | 33 ++ .../components/opentherm/opentherm_macros.h | 60 +++ .../components/opentherm/output/__init__.py | 47 +++ .../components/opentherm/output/output.cpp | 18 + esphome/components/opentherm/output/output.h | 33 ++ esphome/components/opentherm/schema.py | 378 +++++++++++++++++- .../components/opentherm/sensor/__init__.py | 16 + .../components/opentherm/switch/__init__.py | 43 ++ .../components/opentherm/switch/switch.cpp | 28 ++ esphome/components/opentherm/switch/switch.h | 20 + tests/components/opentherm/common.yaml | 84 ++++ 22 files changed, 1128 insertions(+), 11 deletions(-) create mode 100644 esphome/components/opentherm/binary_sensor/__init__.py create mode 100644 esphome/components/opentherm/input.h create mode 100644 esphome/components/opentherm/input.py create mode 100644 esphome/components/opentherm/number/__init__.py create mode 100644 esphome/components/opentherm/number/number.cpp create mode 100644 esphome/components/opentherm/number/number.h create mode 100644 esphome/components/opentherm/output/__init__.py create mode 100644 esphome/components/opentherm/output/output.cpp create mode 100644 esphome/components/opentherm/output/output.h create mode 100644 esphome/components/opentherm/switch/__init__.py create mode 100644 esphome/components/opentherm/switch/switch.cpp create mode 100644 esphome/components/opentherm/switch/switch.h diff --git a/esphome/components/opentherm/__init__.py b/esphome/components/opentherm/__init__.py index ee19818a29..81cd78af08 100644 --- a/esphome/components/opentherm/__init__.py +++ b/esphome/components/opentherm/__init__.py @@ -3,8 +3,9 @@ from typing import Any import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins +from esphome.components import sensor from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266 -from . import generate +from . import const, schema, validate, generate CODEOWNERS = ["@olegtarasov"] MULTI_CONF = True @@ -19,6 +20,7 @@ CONF_CH2_ACTIVE = "ch2_active" CONF_SUMMER_MODE_ACTIVE = "summer_mode_active" CONF_DHW_BLOCK = "dhw_block" CONF_SYNC_MODE = "sync_mode" +CONF_OPENTHERM_VERSION = "opentherm_version" CONFIG_SCHEMA = cv.All( cv.Schema( @@ -34,8 +36,15 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SUMMER_MODE_ACTIVE, False): cv.boolean, cv.Optional(CONF_DHW_BLOCK, False): cv.boolean, cv.Optional(CONF_SYNC_MODE, False): cv.boolean, + cv.Optional(CONF_OPENTHERM_VERSION): cv.positive_float, } - ).extend(cv.COMPONENT_SCHEMA), + ) + .extend( + validate.create_entities_schema( + schema.INPUTS, (lambda _: cv.use_id(sensor.Sensor)) + ) + ) + .extend(cv.COMPONENT_SCHEMA), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]), ) @@ -52,8 +61,23 @@ async def to_code(config: dict[str, Any]) -> None: cg.add(var.set_out_pin(out_pin)) non_sensors = {CONF_ID, CONF_IN_PIN, CONF_OUT_PIN} + input_sensors = [] for key, value in config.items(): if key in non_sensors: continue + if key in schema.INPUTS: + input_sensor = await cg.get_variable(value) + cg.add( + getattr(var, f"set_{key}_{const.INPUT_SENSOR.lower()}")(input_sensor) + ) + input_sensors.append(key) + else: + cg.add(getattr(var, f"set_{key}")(value)) - cg.add(getattr(var, f"set_{key}")(value)) + if len(input_sensors) > 0: + generate.define_has_component(const.INPUT_SENSOR, input_sensors) + generate.define_message_handler( + const.INPUT_SENSOR, input_sensors, schema.INPUTS + ) + generate.define_readers(const.INPUT_SENSOR, input_sensors) + generate.add_messages(var, input_sensors, schema.INPUTS) diff --git a/esphome/components/opentherm/binary_sensor/__init__.py b/esphome/components/opentherm/binary_sensor/__init__.py new file mode 100644 index 0000000000..643734f90c --- /dev/null +++ b/esphome/components/opentherm/binary_sensor/__init__.py @@ -0,0 +1,33 @@ +from typing import Any + +import esphome.config_validation as cv +from esphome.components import binary_sensor +from .. import const, schema, validate, generate + +DEPENDENCIES = [const.OPENTHERM] +COMPONENT_TYPE = const.BINARY_SENSOR + + +def get_entity_validation_schema(entity: schema.BinarySensorSchema) -> cv.Schema: + return binary_sensor.binary_sensor_schema( + device_class=( + entity.device_class + or binary_sensor._UNDEF # pylint: disable=protected-access + ), + icon=(entity.icon or binary_sensor._UNDEF), # pylint: disable=protected-access + ) + + +CONFIG_SCHEMA = validate.create_component_schema( + schema.BINARY_SENSORS, get_entity_validation_schema +) + + +async def to_code(config: dict[str, Any]) -> None: + await generate.component_to_code( + COMPONENT_TYPE, + schema.BINARY_SENSORS, + binary_sensor.BinarySensor, + generate.create_only_conf(binary_sensor.new_binary_sensor), + config, + ) diff --git a/esphome/components/opentherm/const.py b/esphome/components/opentherm/const.py index 1f997c5d9c..a113331585 100644 --- a/esphome/components/opentherm/const.py +++ b/esphome/components/opentherm/const.py @@ -1,5 +1,11 @@ OPENTHERM = "opentherm" CONF_OPENTHERM_ID = "opentherm_id" +CONF_DATA_TYPE = "data_type" SENSOR = "sensor" +BINARY_SENSOR = "binary_sensor" +SWITCH = "switch" +NUMBER = "number" +OUTPUT = "output" +INPUT_SENSOR = "input_sensor" diff --git a/esphome/components/opentherm/generate.py b/esphome/components/opentherm/generate.py index 6a97835a57..9716cab093 100644 --- a/esphome/components/opentherm/generate.py +++ b/esphome/components/opentherm/generate.py @@ -130,6 +130,8 @@ async def component_to_code( id = conf[CONF_ID] if id and id.type == type: entity = await create(conf, key, hub) + if const.CONF_DATA_TYPE in conf: + schemas[key].message_data = conf[const.CONF_DATA_TYPE] cg.add(getattr(hub, f"set_{key}_{component_type.lower()}")(entity)) keys.append(key) diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index 770bbd82b7..432036d58d 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -29,6 +29,8 @@ uint8_t parse_u8_hb(OpenthermData &data) { return data.valueHB; } int8_t parse_s8_lb(OpenthermData &data) { return (int8_t) data.valueLB; } int8_t parse_s8_hb(OpenthermData &data) { return (int8_t) data.valueHB; } uint16_t parse_u16(OpenthermData &data) { return data.u16(); } +uint16_t parse_u8_lb_60(OpenthermData &data) { return data.valueLB * 60; } +uint16_t parse_u8_hb_60(OpenthermData &data) { return data.valueHB * 60; } int16_t parse_s16(OpenthermData &data) { return data.s16(); } float parse_f88(OpenthermData &data) { return data.f88(); } @@ -87,13 +89,40 @@ OpenthermData OpenthermHub::build_request_(MessageId request_id) const { return data; } + // Another special case is OpenTherm version number which is configured at hub level as a constant + if (request_id == MessageId::OT_VERSION_CONTROLLER) { + data.type = MessageType::WRITE_DATA; + data.id = MessageId::OT_VERSION_CONTROLLER; + data.f88(this->opentherm_version_); + + return data; + } + // Disable incomplete switch statement warnings, because the cases in each // switch are generated based on the configured sensors and inputs. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch" - switch (request_id) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) } + // Next, we start with the write requests from switches and other inputs, + // because we would want to write that data if it is available, rather than + // request a read for that type (in the case that both read and write are + // supported). + switch (request_id) { + OPENTHERM_SWITCH_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, , + OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, ) + OPENTHERM_NUMBER_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, , + OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, ) + OPENTHERM_OUTPUT_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, , + OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, ) + OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, , + OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, ) + } + // Finally, handle the simple read requests, which only change with the message id. + switch (request_id) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) } + switch (request_id) { + OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) + } #pragma GCC diagnostic pop // And if we get here, a message was requested which somehow wasn't handled. @@ -115,6 +144,10 @@ void OpenthermHub::process_response(OpenthermData &data) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, , OPENTHERM_MESSAGE_RESPONSE_POSTSCRIPT, ) } + switch (data.id) { + OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, , + OPENTHERM_MESSAGE_RESPONSE_POSTSCRIPT, ) + } } void OpenthermHub::setup() { @@ -131,6 +164,13 @@ void OpenthermHub::setup() { // good practice anyway. this->add_repeating_message(MessageId::STATUS); + // Also ensure that we start communication with the STATUS message + this->initial_messages_.insert(this->initial_messages_.begin(), MessageId::STATUS); + + if (this->opentherm_version_ > 0.0f) { + this->initial_messages_.insert(this->initial_messages_.begin(), MessageId::OT_VERSION_CONTROLLER); + } + this->current_message_iterator_ = this->initial_messages_.begin(); } diff --git a/esphome/components/opentherm/hub.h b/esphome/components/opentherm/hub.h index 3b90cdf427..1f536653e8 100644 --- a/esphome/components/opentherm/hub.h +++ b/esphome/components/opentherm/hub.h @@ -4,6 +4,7 @@ #include "esphome/core/hal.h" #include "esphome/core/component.h" #include "esphome/core/log.h" +#include #include "opentherm.h" @@ -11,6 +12,22 @@ #include "esphome/components/sensor/sensor.h" #endif +#ifdef OPENTHERM_USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif + +#ifdef OPENTHERM_USE_SWITCH +#include "esphome/components/opentherm/switch/switch.h" +#endif + +#ifdef OPENTHERM_USE_OUTPUT +#include "esphome/components/opentherm/output/output.h" +#endif + +#ifdef OPENTHERM_USE_NUMBER +#include "esphome/components/opentherm/number/number.h" +#endif + #include #include #include @@ -31,15 +48,25 @@ class OpenthermHub : public Component { OPENTHERM_SENSOR_LIST(OPENTHERM_DECLARE_SENSOR, ) + OPENTHERM_BINARY_SENSOR_LIST(OPENTHERM_DECLARE_BINARY_SENSOR, ) + + OPENTHERM_SWITCH_LIST(OPENTHERM_DECLARE_SWITCH, ) + + OPENTHERM_NUMBER_LIST(OPENTHERM_DECLARE_NUMBER, ) + + OPENTHERM_OUTPUT_LIST(OPENTHERM_DECLARE_OUTPUT, ) + + OPENTHERM_INPUT_SENSOR_LIST(OPENTHERM_DECLARE_INPUT_SENSOR, ) + // The set of initial messages to send on starting communication with the boiler - std::unordered_set initial_messages_; + std::vector initial_messages_; // and the repeating messages which are sent repeatedly to update various sensors // and boiler parameters (like the setpoint). - std::unordered_set repeating_messages_; + std::vector repeating_messages_; // Indicates if we are still working on the initial requests or not bool sending_initial_ = true; // Index for the current request in one of the _requests sets. - std::unordered_set::const_iterator current_message_iterator_; + std::vector::const_iterator current_message_iterator_; uint32_t last_conversation_start_ = 0; uint32_t last_conversation_end_ = 0; @@ -51,6 +78,8 @@ class OpenthermHub : public Component { // Very likely to happen while using Dallas temperature sensors. bool sync_mode_ = false; + float opentherm_version_ = 0.0f; + // Create OpenTherm messages based on the message id OpenthermData build_request_(MessageId request_id) const; void handle_protocol_write_error_(); @@ -88,13 +117,23 @@ class OpenthermHub : public Component { OPENTHERM_SENSOR_LIST(OPENTHERM_SET_SENSOR, ) - // Add a request to the set of initial requests - void add_initial_message(MessageId message_id) { this->initial_messages_.insert(message_id); } + OPENTHERM_BINARY_SENSOR_LIST(OPENTHERM_SET_BINARY_SENSOR, ) + + OPENTHERM_SWITCH_LIST(OPENTHERM_SET_SWITCH, ) + + OPENTHERM_NUMBER_LIST(OPENTHERM_SET_NUMBER, ) + + OPENTHERM_OUTPUT_LIST(OPENTHERM_SET_OUTPUT, ) + + OPENTHERM_INPUT_SENSOR_LIST(OPENTHERM_SET_INPUT_SENSOR, ) + + // Add a request to the vector of initial requests + void add_initial_message(MessageId message_id) { this->initial_messages_.push_back(message_id); } // Add a request to the set of repeating requests. Note that a large number of repeating // requests will slow down communication with the boiler. Each request may take up to 1 second, // so with all sensors enabled, it may take about half a minute before a change in setpoint // will be processed. - void add_repeating_message(MessageId message_id) { this->repeating_messages_.insert(message_id); } + void add_repeating_message(MessageId message_id) { this->repeating_messages_.push_back(message_id); } // There are seven status variables, which can either be set as a simple variable, // or using a switch. ch_enable and dhw_enable default to true, the others to false. @@ -110,6 +149,7 @@ class OpenthermHub : public Component { void set_summer_mode_active(bool value) { this->summer_mode_active = value; } void set_dhw_block(bool value) { this->dhw_block = value; } void set_sync_mode(bool sync_mode) { this->sync_mode_ = sync_mode; } + void set_opentherm_version(float value) { this->opentherm_version_ = value; } float get_setup_priority() const override { return setup_priority::HARDWARE; } diff --git a/esphome/components/opentherm/input.h b/esphome/components/opentherm/input.h new file mode 100644 index 0000000000..3567138792 --- /dev/null +++ b/esphome/components/opentherm/input.h @@ -0,0 +1,18 @@ +#pragma once + +namespace esphome { +namespace opentherm { + +class OpenthermInput { + public: + bool auto_min_value, auto_max_value; + + virtual void set_min_value(float min_value) = 0; + virtual void set_max_value(float max_value) = 0; + + virtual void set_auto_min_value(bool auto_min_value) { this->auto_min_value = auto_min_value; } + virtual void set_auto_max_value(bool auto_max_value) { this->auto_max_value = auto_max_value; } +}; + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/input.py b/esphome/components/opentherm/input.py new file mode 100644 index 0000000000..7897747be1 --- /dev/null +++ b/esphome/components/opentherm/input.py @@ -0,0 +1,51 @@ +from typing import Any + +import esphome.codegen as cg +import esphome.config_validation as cv +from . import schema, generate + +CONF_min_value = "min_value" +CONF_max_value = "max_value" +CONF_auto_min_value = "auto_min_value" +CONF_auto_max_value = "auto_max_value" +CONF_step = "step" + +OpenthermInput = generate.opentherm_ns.class_("OpenthermInput") + + +def validate_min_value_less_than_max_value(conf): + if ( + CONF_min_value in conf + and CONF_max_value in conf + and conf[CONF_min_value] > conf[CONF_max_value] + ): + raise cv.Invalid(f"{CONF_min_value} must be less than {CONF_max_value}") + return conf + + +def input_schema(entity: schema.InputSchema) -> cv.Schema: + result = cv.Schema( + { + cv.Optional(CONF_min_value, entity.range[0]): cv.float_range( + entity.range[0], entity.range[1] + ), + cv.Optional(CONF_max_value, entity.range[1]): cv.float_range( + entity.range[0], entity.range[1] + ), + } + ) + result = result.add_extra(validate_min_value_less_than_max_value) + result = result.extend({cv.Optional(CONF_step, False): cv.float_}) + if entity.auto_min_value is not None: + result = result.extend({cv.Optional(CONF_auto_min_value, False): cv.boolean}) + if entity.auto_max_value is not None: + result = result.extend({cv.Optional(CONF_auto_max_value, False): cv.boolean}) + + return result + + +def generate_setters(entity: cg.MockObj, conf: dict[str, Any]) -> None: + generate.add_property_set(entity, CONF_min_value, conf) + generate.add_property_set(entity, CONF_max_value, conf) + generate.add_property_set(entity, CONF_auto_min_value, conf) + generate.add_property_set(entity, CONF_auto_max_value, conf) diff --git a/esphome/components/opentherm/number/__init__.py b/esphome/components/opentherm/number/__init__.py new file mode 100644 index 0000000000..bbf3e87586 --- /dev/null +++ b/esphome/components/opentherm/number/__init__.py @@ -0,0 +1,74 @@ +from typing import Any + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import number +from esphome.const import ( + CONF_ID, + CONF_UNIT_OF_MEASUREMENT, + CONF_STEP, + CONF_INITIAL_VALUE, + CONF_RESTORE_VALUE, +) +from .. import const, schema, validate, input, generate + +DEPENDENCIES = [const.OPENTHERM] +COMPONENT_TYPE = const.NUMBER + +OpenthermNumber = generate.opentherm_ns.class_( + "OpenthermNumber", number.Number, cg.Component, input.OpenthermInput +) + + +async def new_openthermnumber(config: dict[str, Any]) -> cg.Pvariable: + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await number.register_number( + var, + config, + min_value=config[input.CONF_min_value], + max_value=config[input.CONF_max_value], + step=config[input.CONF_step], + ) + input.generate_setters(var, config) + + if CONF_INITIAL_VALUE in config: + cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE])) + if CONF_RESTORE_VALUE in config: + cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) + + return var + + +def get_entity_validation_schema(entity: schema.InputSchema) -> cv.Schema: + return ( + number.NUMBER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(OpenthermNumber), + cv.Optional( + CONF_UNIT_OF_MEASUREMENT, entity.unit_of_measurement + ): cv.string_strict, + cv.Optional(CONF_STEP, entity.step): cv.float_, + cv.Optional(CONF_INITIAL_VALUE): cv.float_, + cv.Optional(CONF_RESTORE_VALUE): cv.boolean, + } + ) + .extend(input.input_schema(entity)) + .extend(cv.COMPONENT_SCHEMA) + ) + + +CONFIG_SCHEMA = validate.create_component_schema( + schema.INPUTS, get_entity_validation_schema +) + + +async def to_code(config: dict[str, Any]) -> None: + keys = await generate.component_to_code( + COMPONENT_TYPE, + schema.INPUTS, + OpenthermNumber, + generate.create_only_conf(new_openthermnumber), + config, + ) + generate.define_readers(COMPONENT_TYPE, keys) diff --git a/esphome/components/opentherm/number/number.cpp b/esphome/components/opentherm/number/number.cpp new file mode 100644 index 0000000000..d02b99ee9c --- /dev/null +++ b/esphome/components/opentherm/number/number.cpp @@ -0,0 +1,40 @@ +#include "number.h" + +namespace esphome { +namespace opentherm { + +static const char *const TAG = "opentherm.number"; + +void OpenthermNumber::control(float value) { + this->publish_state(value); + + if (this->restore_value_) + this->pref_.save(&value); +} + +void OpenthermNumber::setup() { + float value; + if (!this->restore_value_) { + value = this->initial_value_; + } else { + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); + if (!this->pref_.load(&value)) { + if (!std::isnan(this->initial_value_)) { + value = this->initial_value_; + } else { + value = this->traits.get_min_value(); + } + } + } + this->publish_state(value); +} + +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); +} + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/number/number.h b/esphome/components/opentherm/number/number.h new file mode 100644 index 0000000000..6f86072754 --- /dev/null +++ b/esphome/components/opentherm/number/number.h @@ -0,0 +1,31 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "esphome/core/preferences.h" +#include "esphome/core/log.h" +#include "esphome/components/opentherm/input.h" + +namespace esphome { +namespace opentherm { + +// Just a simple number, which stores the number +class OpenthermNumber : public number::Number, public Component, public OpenthermInput { + protected: + void control(float value) override; + void setup() override; + void dump_config() override; + + float initial_value_{NAN}; + bool restore_value_{false}; + + ESPPreferenceObject pref_; + + public: + void set_min_value(float min_value) override { this->traits.set_min_value(min_value); } + void set_max_value(float max_value) override { this->traits.set_max_value(max_value); } + void set_initial_value(float initial_value) { initial_value_ = initial_value; } + void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } +}; + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 23f4b39a1a..85f4611125 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -99,6 +99,8 @@ enum MessageId { EXHAUST_TEMP = 33, FAN_SPEED = 35, FLAME_CURRENT = 36, + ROOM_TEMP_CH2 = 37, + REL_HUMIDITY = 38, DHW_BOUNDS = 48, CH_BOUNDS = 49, OTC_CURVE_BOUNDS = 50, @@ -110,15 +112,46 @@ enum MessageId { HVAC_STATUS = 70, REL_VENT_SETPOINT = 71, DEVICE_VENT = 74, + HVAC_VER_ID = 75, REL_VENTILATION = 77, REL_HUMID_EXHAUST = 78, + EXHAUST_CO2 = 79, SUPPLY_INLET_TEMP = 80, SUPPLY_OUTLET_TEMP = 81, EXHAUST_INLET_TEMP = 82, EXHAUST_OUTLET_TEMP = 83, + EXHAUST_FAN_SPEED = 84, + SUPPLY_FAN_SPEED = 85, + REMOTE_VENTILATION_PARAM = 86, NOM_REL_VENTILATION = 87, + HVAC_NUM_TSP = 88, + HVAC_IDX_TSP = 89, + HVAC_FHB_SIZE = 90, + HVAC_FHB_IDX = 91, + RF_SIGNAL = 98, + DHW_MODE = 99, OVERRIDE_FUNC = 100, + + // Solar Specific Message IDs + SOLAR_MODE_FLAGS = 101, // hb0-2 Controller storage mode + // lb0 Device fault + // lb1-3 Device mode status + // lb4-5 Device status + SOLAR_ASF = 102, + SOLAR_VERSION_ID = 103, + SOLAR_PRODUCT_ID = 104, + SOLAR_NUM_TSP = 105, + SOLAR_IDX_TSP = 106, + SOLAR_FHB_SIZE = 107, + SOLAR_FHB_IDX = 108, + SOLAR_STARTS = 109, + SOLAR_HOURS = 110, + SOLAR_ENERGY = 111, + SOLAR_TOTAL_ENERGY = 112, + + FAILED_BURNER_STARTS = 113, + BURNER_FLAME_LOW = 114, OEM_DIAGNOSTIC = 115, BURNER_STARTS = 116, CH_PUMP_STARTS = 117, diff --git a/esphome/components/opentherm/opentherm_macros.h b/esphome/components/opentherm/opentherm_macros.h index 0389e975ff..8aaec0b48a 100644 --- a/esphome/components/opentherm/opentherm_macros.h +++ b/esphome/components/opentherm/opentherm_macros.h @@ -13,14 +13,49 @@ namespace opentherm { #ifndef OPENTHERM_SENSOR_LIST #define OPENTHERM_SENSOR_LIST(F, sep) #endif +#ifndef OPENTHERM_BINARY_SENSOR_LIST +#define OPENTHERM_BINARY_SENSOR_LIST(F, sep) +#endif +#ifndef OPENTHERM_SWITCH_LIST +#define OPENTHERM_SWITCH_LIST(F, sep) +#endif +#ifndef OPENTHERM_NUMBER_LIST +#define OPENTHERM_NUMBER_LIST(F, sep) +#endif +#ifndef OPENTHERM_OUTPUT_LIST +#define OPENTHERM_OUTPUT_LIST(F, sep) +#endif +#ifndef OPENTHERM_INPUT_SENSOR_LIST +#define OPENTHERM_INPUT_SENSOR_LIST(F, sep) +#endif // Use macros to create fields for every entity specified in the ESPHome configuration #define OPENTHERM_DECLARE_SENSOR(entity) sensor::Sensor *entity; +#define OPENTHERM_DECLARE_BINARY_SENSOR(entity) binary_sensor::BinarySensor *entity; +#define OPENTHERM_DECLARE_SWITCH(entity) OpenthermSwitch *entity; +#define OPENTHERM_DECLARE_NUMBER(entity) OpenthermNumber *entity; +#define OPENTHERM_DECLARE_OUTPUT(entity) OpenthermOutput *entity; +#define OPENTHERM_DECLARE_INPUT_SENSOR(entity) sensor::Sensor *entity; // Setter macros #define OPENTHERM_SET_SENSOR(entity) \ void set_##entity(sensor::Sensor *sensor) { this->entity = sensor; } +#define OPENTHERM_SET_BINARY_SENSOR(entity) \ + void set_##entity(binary_sensor::BinarySensor *binary_sensor) { this->entity = binary_sensor; } + +#define OPENTHERM_SET_SWITCH(entity) \ + void set_##entity(OpenthermSwitch *sw) { this->entity = sw; } + +#define OPENTHERM_SET_NUMBER(entity) \ + void set_##entity(OpenthermNumber *number) { this->entity = number; } + +#define OPENTHERM_SET_OUTPUT(entity) \ + void set_##entity(OpenthermOutput *output) { this->entity = output; } + +#define OPENTHERM_SET_INPUT_SENSOR(entity) \ + void set_##entity(sensor::Sensor *sensor) { this->entity = sensor; } + // ===== hub.cpp macros ===== // *_MESSAGE_HANDLERS are generated in defines.h and look like this: @@ -35,6 +70,31 @@ namespace opentherm { #ifndef OPENTHERM_SENSOR_MESSAGE_HANDLERS #define OPENTHERM_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) #endif +#ifndef OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS +#define OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) +#endif +#ifndef OPENTHERM_SWITCH_MESSAGE_HANDLERS +#define OPENTHERM_SWITCH_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) +#endif +#ifndef OPENTHERM_NUMBER_MESSAGE_HANDLERS +#define OPENTHERM_NUMBER_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) +#endif +#ifndef OPENTHERM_OUTPUT_MESSAGE_HANDLERS +#define OPENTHERM_OUTPUT_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) +#endif +#ifndef OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS +#define OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) +#endif + +// Write data request builders +#define OPENTHERM_MESSAGE_WRITE_MESSAGE(msg) \ + case MessageId::msg: { \ + data.type = MessageType::WRITE_DATA; \ + data.id = request_id; +#define OPENTHERM_MESSAGE_WRITE_ENTITY(key, msg_data) message_data::write_##msg_data(this->key->state, data); +#define OPENTHERM_MESSAGE_WRITE_POSTSCRIPT \ + return data; \ + } // Read data request builder #define OPENTHERM_MESSAGE_READ_MESSAGE(msg) \ diff --git a/esphome/components/opentherm/output/__init__.py b/esphome/components/opentherm/output/__init__.py new file mode 100644 index 0000000000..3a53c9d4f4 --- /dev/null +++ b/esphome/components/opentherm/output/__init__.py @@ -0,0 +1,47 @@ +from typing import Any + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import output +from esphome.const import CONF_ID +from .. import const, schema, validate, input, generate + +DEPENDENCIES = [const.OPENTHERM] +COMPONENT_TYPE = const.OUTPUT + +OpenthermOutput = generate.opentherm_ns.class_( + "OpenthermOutput", output.FloatOutput, cg.Component, input.OpenthermInput +) + + +async def new_openthermoutput( + config: dict[str, Any], key: str, _hub: cg.MockObj +) -> cg.Pvariable: + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await output.register_output(var, config) + cg.add(getattr(var, "set_id")(cg.RawExpression(f'"{key}_{config[CONF_ID]}"'))) + input.generate_setters(var, config) + return var + + +def get_entity_validation_schema(entity: schema.InputSchema) -> cv.Schema: + return ( + output.FLOAT_OUTPUT_SCHEMA.extend( + {cv.GenerateID(): cv.declare_id(OpenthermOutput)} + ) + .extend(input.input_schema(entity)) + .extend(cv.COMPONENT_SCHEMA) + ) + + +CONFIG_SCHEMA = validate.create_component_schema( + schema.INPUTS, get_entity_validation_schema +) + + +async def to_code(config: dict[str, Any]) -> None: + keys = await generate.component_to_code( + COMPONENT_TYPE, schema.INPUTS, OpenthermOutput, new_openthermoutput, config + ) + generate.define_readers(COMPONENT_TYPE, keys) diff --git a/esphome/components/opentherm/output/output.cpp b/esphome/components/opentherm/output/output.cpp new file mode 100644 index 0000000000..f820dc76f1 --- /dev/null +++ b/esphome/components/opentherm/output/output.cpp @@ -0,0 +1,18 @@ +#include "esphome/core/helpers.h" // for clamp() and lerp() +#include "output.h" + +namespace esphome { +namespace opentherm { + +static const char *const TAG = "opentherm.output"; + +void opentherm::OpenthermOutput::write_state(float state) { + ESP_LOGD(TAG, "Received state: %.2f. Min value: %.2f, max value: %.2f", state, min_value_, max_value_); + this->state = state < 0.003 && this->zero_means_zero_ + ? 0.0 + : clamp(lerp(state, min_value_, max_value_), min_value_, max_value_); + this->has_state_ = true; + ESP_LOGD(TAG, "Output %s set to %.2f", this->id_, this->state); +} +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/output/output.h b/esphome/components/opentherm/output/output.h new file mode 100644 index 0000000000..8d6a0ee4ba --- /dev/null +++ b/esphome/components/opentherm/output/output.h @@ -0,0 +1,33 @@ +#pragma once + +#include "esphome/components/output/float_output.h" +#include "esphome/components/opentherm/input.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace opentherm { + +class OpenthermOutput : public output::FloatOutput, public Component, public OpenthermInput { + protected: + bool has_state_ = false; + const char *id_ = nullptr; + + float min_value_, max_value_; + + public: + float state; + + void set_id(const char *id) { this->id_ = id; } + + void write_state(float state) override; + + bool has_state() { return this->has_state_; }; + + void set_min_value(float min_value) override { this->min_value_ = min_value; } + void set_max_value(float max_value) override { this->max_value_ = max_value; } + float get_min_value() { return this->min_value_; } + float get_max_value() { return this->max_value_; } +}; + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/schema.py b/esphome/components/opentherm/schema.py index 6ed0029437..fe0f2a77a3 100644 --- a/esphome/components/opentherm/schema.py +++ b/esphome/components/opentherm/schema.py @@ -11,9 +11,12 @@ from esphome.const import ( UNIT_MICROAMP, UNIT_PERCENT, UNIT_REVOLUTIONS_PER_MINUTE, + DEVICE_CLASS_COLD, DEVICE_CLASS_CURRENT, DEVICE_CLASS_EMPTY, + DEVICE_CLASS_HEAT, DEVICE_CLASS_PRESSURE, + DEVICE_CLASS_PROBLEM, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT, STATE_CLASS_NONE, @@ -188,11 +191,23 @@ SENSORS: dict[str, SensorSchema] = { description="Boiler fan speed", unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE, accuracy_decimals=0, + icon="mdi:fan", device_class=DEVICE_CLASS_EMPTY, state_class=STATE_CLASS_MEASUREMENT, message="FAN_SPEED", keep_updated=True, - message_data="u16", + message_data="u8_lb_60", + ), + "fan_speed_setpoint": SensorSchema( + description="Boiler fan speed setpoint", + unit_of_measurement=UNIT_REVOLUTIONS_PER_MINUTE, + accuracy_decimals=0, + icon="mdi:fan", + device_class=DEVICE_CLASS_EMPTY, + state_class=STATE_CLASS_MEASUREMENT, + message="FAN_SPEED", + keep_updated=True, + message_data="u8_hb_60", ), "flame_current": SensorSchema( description="Boiler flame current", @@ -436,3 +451,364 @@ SENSORS: dict[str, SensorSchema] = { message_data="u8_lb", ), } + + +@dataclass +class BinarySensorSchema(EntitySchema): + icon: Optional[str] = None + device_class: Optional[str] = None + + +BINARY_SENSORS: dict[str, BinarySensorSchema] = { + "fault_indication": BinarySensorSchema( + description="Status: Fault indication", + device_class=DEVICE_CLASS_PROBLEM, + message="STATUS", + keep_updated=True, + message_data="flag8_lb_0", + ), + "ch_active": BinarySensorSchema( + description="Status: Central Heating active", + device_class=DEVICE_CLASS_HEAT, + icon="mdi:radiator", + message="STATUS", + keep_updated=True, + message_data="flag8_lb_1", + ), + "dhw_active": BinarySensorSchema( + description="Status: Domestic Hot Water active", + device_class=DEVICE_CLASS_HEAT, + icon="mdi:faucet", + message="STATUS", + keep_updated=True, + message_data="flag8_lb_2", + ), + "flame_on": BinarySensorSchema( + description="Status: Flame on", + device_class=DEVICE_CLASS_HEAT, + icon="mdi:fire", + message="STATUS", + keep_updated=True, + message_data="flag8_lb_3", + ), + "cooling_active": BinarySensorSchema( + description="Status: Cooling active", + device_class=DEVICE_CLASS_COLD, + message="STATUS", + keep_updated=True, + message_data="flag8_lb_4", + ), + "ch2_active": BinarySensorSchema( + description="Status: Central Heating 2 active", + device_class=DEVICE_CLASS_HEAT, + icon="mdi:radiator", + message="STATUS", + keep_updated=True, + message_data="flag8_lb_5", + ), + "diagnostic_indication": BinarySensorSchema( + description="Status: Diagnostic event", + device_class=DEVICE_CLASS_PROBLEM, + message="STATUS", + keep_updated=True, + message_data="flag8_lb_6", + ), + "electricity_production": BinarySensorSchema( + description="Status: Electricity production", + device_class=DEVICE_CLASS_PROBLEM, + message="STATUS", + keep_updated=True, + message_data="flag8_lb_7", + ), + "dhw_present": BinarySensorSchema( + description="Configuration: DHW present", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_0", + ), + "control_type_on_off": BinarySensorSchema( + description="Configuration: Control type is on/off", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_1", + ), + "cooling_supported": BinarySensorSchema( + description="Configuration: Cooling supported", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_2", + ), + "dhw_storage_tank": BinarySensorSchema( + description="Configuration: DHW storage tank", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_3", + ), + "controller_pump_control_allowed": BinarySensorSchema( + description="Configuration: Controller pump control allowed", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_4", + ), + "ch2_present": BinarySensorSchema( + description="Configuration: CH2 present", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_5", + ), + "water_filling": BinarySensorSchema( + description="Configuration: Remote water filling", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_6", + ), + "heat_mode": BinarySensorSchema( + description="Configuration: Heating or cooling", + message="DEVICE_CONFIG", + keep_updated=False, + message_data="flag8_hb_7", + ), + "dhw_setpoint_transfer_enabled": BinarySensorSchema( + description="Remote boiler parameters: DHW setpoint transfer enabled", + message="REMOTE", + keep_updated=False, + message_data="flag8_hb_0", + ), + "max_ch_setpoint_transfer_enabled": BinarySensorSchema( + description="Remote boiler parameters: CH maximum setpoint transfer enabled", + message="REMOTE", + keep_updated=False, + message_data="flag8_hb_1", + ), + "dhw_setpoint_rw": BinarySensorSchema( + description="Remote boiler parameters: DHW setpoint read/write", + message="REMOTE", + keep_updated=False, + message_data="flag8_lb_0", + ), + "max_ch_setpoint_rw": BinarySensorSchema( + description="Remote boiler parameters: CH maximum setpoint read/write", + message="REMOTE", + keep_updated=False, + message_data="flag8_lb_1", + ), + "service_request": BinarySensorSchema( + description="Service required", + device_class=DEVICE_CLASS_PROBLEM, + message="FAULT_FLAGS", + keep_updated=True, + message_data="flag8_hb_0", + ), + "lockout_reset": BinarySensorSchema( + description="Lockout Reset", + device_class=DEVICE_CLASS_PROBLEM, + message="FAULT_FLAGS", + keep_updated=True, + message_data="flag8_hb_1", + ), + "low_water_pressure": BinarySensorSchema( + description="Low water pressure fault", + device_class=DEVICE_CLASS_PROBLEM, + message="FAULT_FLAGS", + keep_updated=True, + message_data="flag8_hb_2", + ), + "flame_fault": BinarySensorSchema( + description="Flame fault", + device_class=DEVICE_CLASS_PROBLEM, + message="FAULT_FLAGS", + keep_updated=True, + message_data="flag8_hb_3", + ), + "air_pressure_fault": BinarySensorSchema( + description="Air pressure fault", + device_class=DEVICE_CLASS_PROBLEM, + message="FAULT_FLAGS", + keep_updated=True, + message_data="flag8_hb_4", + ), + "water_over_temp": BinarySensorSchema( + description="Water overtemperature", + device_class=DEVICE_CLASS_PROBLEM, + message="FAULT_FLAGS", + keep_updated=True, + message_data="flag8_hb_5", + ), +} + + +@dataclass +class SwitchSchema(EntitySchema): + default_mode: Optional[str] = None + + +SWITCHES: dict[str, SwitchSchema] = { + "ch_enable": SwitchSchema( + description="Central Heating enabled", + message="STATUS", + keep_updated=True, + message_data="flag8_hb_0", + default_mode="restore_default_off", + ), + "dhw_enable": SwitchSchema( + description="Domestic Hot Water enabled", + message="STATUS", + keep_updated=True, + message_data="flag8_hb_1", + default_mode="restore_default_off", + ), + "cooling_enable": SwitchSchema( + description="Cooling enabled", + message="STATUS", + keep_updated=True, + message_data="flag8_hb_2", + default_mode="restore_default_off", + ), + "otc_active": SwitchSchema( + description="Outside temperature compensation active", + message="STATUS", + keep_updated=True, + message_data="flag8_hb_3", + default_mode="restore_default_off", + ), + "ch2_active": SwitchSchema( + description="Central Heating 2 active", + message="STATUS", + keep_updated=True, + message_data="flag8_hb_4", + default_mode="restore_default_off", + ), + "summer_mode_active": SwitchSchema( + description="Summer mode active", + message="STATUS", + keep_updated=True, + message_data="flag8_hb_5", + default_mode="restore_default_off", + ), + "dhw_block": SwitchSchema( + description="DHW blocked", + message="STATUS", + keep_updated=True, + message_data="flag8_hb_6", + default_mode="restore_default_off", + ), +} + + +@dataclass +class AutoConfigure: + message: str + message_data: str + + +@dataclass +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 + + +INPUTS: dict[str, InputSchema] = { + "t_set": InputSchema( + description="Control setpoint: temperature setpoint for the boiler's supply water", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="CH_SETPOINT", + keep_updated=True, + message_data="f88", + range=(0, 100), + auto_max_value=AutoConfigure(message="MAX_CH_SETPOINT", message_data="f88"), + ), + "t_set_ch2": InputSchema( + description="Control setpoint 2: temperature setpoint for the boiler's supply water on the second heating circuit", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="CH2_SETPOINT", + keep_updated=True, + message_data="f88", + range=(0, 100), + auto_max_value=AutoConfigure(message="MAX_CH_SETPOINT", message_data="f88"), + ), + "cooling_control": InputSchema( + description="Cooling control signal", + unit_of_measurement=UNIT_PERCENT, + step=1.0, + message="COOLING_CONTROL", + keep_updated=True, + message_data="f88", + range=(0, 100), + ), + "t_dhw_set": InputSchema( + description="Domestic hot water temperature setpoint", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="DHW_SETPOINT", + keep_updated=True, + message_data="f88", + range=(0, 127), + auto_min_value=AutoConfigure(message="DHW_BOUNDS", message_data="s8_lb"), + auto_max_value=AutoConfigure(message="DHW_BOUNDS", message_data="s8_hb"), + ), + "max_t_set": InputSchema( + description="Maximum allowable CH water setpoint", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="MAX_CH_SETPOINT", + keep_updated=True, + message_data="f88", + range=(0, 127), + auto_min_value=AutoConfigure(message="CH_BOUNDS", message_data="s8_lb"), + auto_max_value=AutoConfigure(message="CH_BOUNDS", message_data="s8_hb"), + ), + "t_room_set": InputSchema( + description="Current room temperature setpoint (informational)", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="ROOM_SETPOINT", + keep_updated=True, + message_data="f88", + range=(-40, 127), + ), + "t_room_set_ch2": InputSchema( + description="Current room temperature setpoint on CH2 (informational)", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="ROOM_SETPOINT_CH2", + keep_updated=True, + message_data="f88", + range=(-40, 127), + ), + "t_room": InputSchema( + description="Current sensed room temperature (informational)", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="ROOM_TEMP", + keep_updated=True, + message_data="f88", + range=(-40, 127), + ), + "max_rel_mod_level": InputSchema( + description="Maximum relative modulation level", + unit_of_measurement=UNIT_PERCENT, + step=1, + icon="mdi:percent", + message="MAX_MODULATION_LEVEL", + keep_updated=True, + message_data="f88", + range=(0, 100), + ), + "otc_hc_ratio": InputSchema( + description="OTC heat curve ratio", + unit_of_measurement=UNIT_CELSIUS, + step=0.1, + message="OTC_CURVE_RATIO", + keep_updated=True, + message_data="f88", + range=(0, 127), + auto_min_value=AutoConfigure(message="OTC_CURVE_BOUNDS", message_data="u8_lb"), + auto_max_value=AutoConfigure(message="OTC_CURVE_BOUNDS", message_data="u8_hb"), + ), +} diff --git a/esphome/components/opentherm/sensor/__init__.py b/esphome/components/opentherm/sensor/__init__.py index 20224e0eda..546a79054b 100644 --- a/esphome/components/opentherm/sensor/__init__.py +++ b/esphome/components/opentherm/sensor/__init__.py @@ -7,6 +7,18 @@ from .. import const, schema, validate, generate DEPENDENCIES = [const.OPENTHERM] COMPONENT_TYPE = const.SENSOR +MSG_DATA_TYPES = { + "u8_lb", + "u8_hb", + "s8_lb", + "s8_hb", + "u8_lb_60", + "u8_hb_60", + "u16", + "s16", + "f88", +} + def get_entity_validation_schema(entity: schema.SensorSchema) -> cv.Schema: return sensor.sensor_schema( @@ -17,6 +29,10 @@ def get_entity_validation_schema(entity: schema.SensorSchema) -> cv.Schema: or sensor._UNDEF, # pylint: disable=protected-access icon=entity.icon or sensor._UNDEF, # pylint: disable=protected-access state_class=entity.state_class, + ).extend( + { + cv.Optional(const.CONF_DATA_TYPE): cv.one_of(*MSG_DATA_TYPES), + } ) diff --git a/esphome/components/opentherm/switch/__init__.py b/esphome/components/opentherm/switch/__init__.py new file mode 100644 index 0000000000..94ec25e36c --- /dev/null +++ b/esphome/components/opentherm/switch/__init__.py @@ -0,0 +1,43 @@ +from typing import Any + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import switch +from esphome.const import CONF_ID +from .. import const, schema, validate, generate + +DEPENDENCIES = [const.OPENTHERM] +COMPONENT_TYPE = const.SWITCH + +OpenthermSwitch = generate.opentherm_ns.class_( + "OpenthermSwitch", switch.Switch, cg.Component +) + + +async def new_openthermswitch(config: dict[str, Any]) -> cg.Pvariable: + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await switch.register_switch(var, config) + return var + + +def get_entity_validation_schema(entity: schema.SwitchSchema) -> cv.Schema: + return switch.SWITCH_SCHEMA.extend( + {cv.GenerateID(): cv.declare_id(OpenthermSwitch)} + ).extend(cv.COMPONENT_SCHEMA) + + +CONFIG_SCHEMA = validate.create_component_schema( + schema.SWITCHES, get_entity_validation_schema +) + + +async def to_code(config: dict[str, Any]) -> None: + keys = await generate.component_to_code( + COMPONENT_TYPE, + schema.SWITCHES, + OpenthermSwitch, + generate.create_only_conf(new_openthermswitch), + config, + ) + generate.define_readers(COMPONENT_TYPE, keys) diff --git a/esphome/components/opentherm/switch/switch.cpp b/esphome/components/opentherm/switch/switch.cpp new file mode 100644 index 0000000000..228d9ac8f3 --- /dev/null +++ b/esphome/components/opentherm/switch/switch.cpp @@ -0,0 +1,28 @@ +#include "switch.h" + +namespace esphome { +namespace opentherm { + +static const char *const TAG = "opentherm.switch"; + +void OpenthermSwitch::write_state(bool state) { this->publish_state(state); } + +void OpenthermSwitch::setup() { + auto restored = this->get_initial_state_with_restore_mode(); + bool state = false; + if (!restored.has_value()) { + ESP_LOGD(TAG, "Couldn't restore state for OpenTherm switch '%s'", this->get_name().c_str()); + } else { + ESP_LOGD(TAG, "Restored state for OpenTherm switch '%s': %d", this->get_name().c_str(), restored.value()); + state = restored.value(); + } + this->write_state(state); +} + +void OpenthermSwitch::dump_config() { + LOG_SWITCH("", "OpenTherm Switch", this); + ESP_LOGCONFIG(TAG, " Current state: %d", this->state); +} + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/switch/switch.h b/esphome/components/opentherm/switch/switch.h new file mode 100644 index 0000000000..0c20a0d9ed --- /dev/null +++ b/esphome/components/opentherm/switch/switch.h @@ -0,0 +1,20 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/switch/switch.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace opentherm { + +class OpenthermSwitch : public switch_::Switch, public Component { + protected: + void write_state(bool state) override; + + public: + void setup() override; + void dump_config() override; +}; + +} // namespace opentherm +} // namespace esphome diff --git a/tests/components/opentherm/common.yaml b/tests/components/opentherm/common.yaml index 27cbae280a..744580f18b 100644 --- a/tests/components/opentherm/common.yaml +++ b/tests/components/opentherm/common.yaml @@ -12,10 +12,41 @@ opentherm: cooling_enable: false otc_active: false ch2_active: true + t_room: boiler_sensor summer_mode_active: true dhw_block: true sync_mode: true +output: + - platform: opentherm + t_set: + id: t_set + min_value: 20 + auto_max_value: true + zero_means_zero: true + t_set_ch2: + id: t_set_ch2 + min_value: 20 + max_value: 40 + zero_means_zero: true + +number: + - platform: opentherm + cooling_control: + name: "Boiler Cooling control signal" + t_dhw_set: + name: "Boiler DHW Setpoint" + max_t_set: + name: "Boiler Max Setpoint" + t_room_set: + name: "Boiler Room Setpoint" + t_room_set_ch2: + name: "Boiler Room Setpoint CH2" + max_rel_mod_level: + name: "Maximum relative modulation level" + otc_hc_ratio: + name: "OTC heat curve ratio" + sensor: - platform: opentherm rel_mod_level: @@ -25,6 +56,7 @@ sensor: dhw_flow_rate: name: "Boiler Water flow rate in DHW circuit" t_boiler: + id: "boiler_sensor" name: "Boiler water temperature" t_dhw: name: "Boiler DHW temperature" @@ -74,3 +106,55 @@ sensor: name: "OTC heat curve ratio upper bound" otc_hc_ratio_lb: name: "OTC heat curve ratio lower bound" + +binary_sensor: + - platform: opentherm + fault_indication: + name: "Boiler Fault indication" + ch_active: + name: "Boiler Central Heating active" + dhw_active: + name: "Boiler Domestic Hot Water active" + flame_on: + name: "Boiler Flame on" + cooling_active: + name: "Boiler Cooling active" + ch2_active: + name: "Boiler Central Heating 2 active" + diagnostic_indication: + name: "Boiler Diagnostic event" + dhw_present: + name: "Boiler DHW present" + control_type_on_off: + name: "Boiler Control type is on/off" + cooling_supported: + name: "Boiler Cooling supported" + dhw_storage_tank: + name: "Boiler DHW storage tank" + controller_pump_control_allowed: + name: "Boiler Controller pump control allowed" + ch2_present: + name: "Boiler CH2 present" + dhw_setpoint_transfer_enabled: + name: "Boiler DHW setpoint transfer enabled" + max_ch_setpoint_transfer_enabled: + name: "Boiler CH maximum setpoint transfer enabled" + dhw_setpoint_rw: + name: "Boiler DHW setpoint read/write" + max_ch_setpoint_rw: + name: "Boiler CH maximum setpoint read/write" + +switch: + - platform: opentherm + ch_enable: + name: "Boiler Central Heating enabled" + restore_mode: RESTORE_DEFAULT_ON + dhw_enable: + name: "Boiler Domestic Hot Water enabled" + cooling_enable: + name: "Boiler Cooling enabled" + restore_mode: ALWAYS_OFF + otc_active: + name: "Boiler Outside temperature compensation active" + ch2_active: + name: "Boiler Central Heating 2 active" From 928b39f4950536ff2d6501da8c35c139ea650b8b Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 12 Nov 2024 13:20:12 -0500 Subject: [PATCH 0597/1052] [i2s_audio] I2S speaker improvements (#7749) --- .../components/i2s_audio/speaker/__init__.py | 13 ++- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 106 +++++++++--------- .../i2s_audio/speaker/i2s_audio_speaker.h | 12 +- 3 files changed, 72 insertions(+), 59 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index dd43d6cb39..0355c16321 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -24,9 +24,10 @@ I2SAudioSpeaker = i2s_audio_ns.class_( "I2SAudioSpeaker", cg.Component, speaker.Speaker, I2SAudioOut ) - +CONF_BUFFER_DURATION = "buffer_duration" CONF_DAC_TYPE = "dac_type" CONF_I2S_COMM_FMT = "i2s_comm_fmt" +CONF_NEVER = "never" i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t") INTERNAL_DAC_OPTIONS = { @@ -73,8 +74,12 @@ BASE_SCHEMA = ( .extend( { cv.Optional( - CONF_TIMEOUT, default="500ms" + CONF_BUFFER_DURATION, default="500ms" ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_TIMEOUT, default="500ms"): cv.Any( + cv.positive_time_period_milliseconds, + cv.one_of(CONF_NEVER, lower=True), + ), } ) .extend(cv.COMPONENT_SCHEMA) @@ -116,4 +121,6 @@ async def to_code(config): else: cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN])) cg.add(var.set_i2s_comm_fmt(config[CONF_I2S_COMM_FMT])) - cg.add(var.set_timeout(config[CONF_TIMEOUT])) + if config[CONF_TIMEOUT] != CONF_NEVER: + cg.add(var.set_timeout(config[CONF_TIMEOUT])) + cg.add(var.set_buffer_duration(config[CONF_BUFFER_DURATION])) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index cf6c3bbbba..c3f4566411 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -13,21 +13,22 @@ namespace esphome { namespace i2s_audio { -static const size_t DMA_BUFFER_SIZE = 512; +static const uint8_t DMA_BUFFER_DURATION_MS = 15; static const size_t DMA_BUFFERS_COUNT = 4; -static const size_t FRAMES_IN_ALL_DMA_BUFFERS = DMA_BUFFER_SIZE * DMA_BUFFERS_COUNT; -static const size_t RING_BUFFER_SAMPLES = 8192; -static const size_t TASK_DELAY_MS = 10; + +static const size_t TASK_DELAY_MS = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT / 2; + static const size_t TASK_STACK_SIZE = 4096; static const ssize_t TASK_PRIORITY = 23; +static const size_t I2S_EVENT_QUEUE_COUNT = DMA_BUFFERS_COUNT + 1; + static const char *const TAG = "i2s_audio.speaker"; enum SpeakerEventGroupBits : uint32_t { - COMMAND_START = (1 << 0), // Starts the main task purpose - COMMAND_STOP = (1 << 1), // stops the main task - COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the task once all data has been written - MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE = (1 << 5), // Locks the ring buffer when not set + COMMAND_START = (1 << 0), // starts the speaker task + COMMAND_STOP = (1 << 1), // stops the speaker task + COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the speaker task once all data has been written STATE_STARTING = (1 << 10), STATE_RUNNING = (1 << 11), STATE_STOPPING = (1 << 12), @@ -91,15 +92,21 @@ static const std::vector Q15_VOLUME_SCALING_FACTORS = { void I2SAudioSpeaker::setup() { ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker..."); - if (this->event_group_ == nullptr) { - this->event_group_ = xEventGroupCreate(); - } + this->event_group_ = xEventGroupCreate(); if (this->event_group_ == nullptr) { ESP_LOGE(TAG, "Failed to create event group"); this->mark_failed(); return; } + + this->i2s_event_queue_ = xQueueCreate(I2S_EVENT_QUEUE_COUNT, sizeof(i2s_event_t)); + + if (this->i2s_event_queue_ == nullptr) { + ESP_LOGE(TAG, "Failed to create I2S event queue"); + this->mark_failed(); + return; + } } void I2SAudioSpeaker::loop() { @@ -199,23 +206,17 @@ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t tick this->start(); } - // Wait for the ring buffer to be available - uint32_t event_bits = - xEventGroupWaitBits(this->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE, pdFALSE, - pdFALSE, pdMS_TO_TICKS(TASK_DELAY_MS)); + size_t bytes_written = 0; + if ((this->state_ == speaker::STATE_RUNNING) && (this->audio_ring_buffer_.use_count() == 1)) { + // Only one owner of the ring buffer (the speaker task), so the ring buffer is allocated and no other components are + // attempting to write to it. - if (event_bits & SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE) { - // Ring buffer is available to write - - // Lock the ring buffer, write to it, then unlock it - xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE); - size_t bytes_written = this->audio_ring_buffer_->write_without_replacement((void *) data, length, ticks_to_wait); - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE); - - return bytes_written; + // Temporarily share ownership of the ring buffer so it won't be deallocated while writing + std::shared_ptr temp_ring_buffer = this->audio_ring_buffer_; + bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait); } - return 0; + return bytes_written; } bool I2SAudioSpeaker::has_buffered_data() const { @@ -246,10 +247,12 @@ void I2SAudioSpeaker::speaker_task(void *params) { const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample(); const uint8_t number_of_channels = audio_stream_info.channels; - const size_t dma_buffers_size = FRAMES_IN_ALL_DMA_BUFFERS * bytes_per_sample * number_of_channels; + const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 * + bytes_per_sample * number_of_channels; + const size_t ring_buffer_size = + this_speaker->buffer_duration_ms_ * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels; - if (this_speaker->send_esp_err_to_event_group_( - this_speaker->allocate_buffers_(dma_buffers_size, RING_BUFFER_SAMPLES * bytes_per_sample))) { + if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) { // Failed to allocate buffers xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); this_speaker->delete_task_(dma_buffers_size); @@ -258,9 +261,6 @@ void I2SAudioSpeaker::speaker_task(void *params) { if (this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_())) { // Failed to start I2S driver this_speaker->delete_task_(dma_buffers_size); - } else { - // Ring buffer is allocated, so indicate its can be written to - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE); } if (!this_speaker->send_esp_err_to_event_group_(this_speaker->reconfigure_i2s_stream_info_(audio_stream_info))) { @@ -270,8 +270,10 @@ void I2SAudioSpeaker::speaker_task(void *params) { bool stop_gracefully = false; uint32_t last_data_received_time = millis(); + bool tx_dma_underflow = false; - while ((millis() - last_data_received_time) <= this_speaker->timeout_) { + while (!this_speaker->timeout_.has_value() || + (millis() - last_data_received_time) <= this_speaker->timeout_.value()) { event_group_bits = xEventGroupGetBits(this_speaker->event_group_); if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) { @@ -281,12 +283,18 @@ void I2SAudioSpeaker::speaker_task(void *params) { stop_gracefully = true; } + i2s_event_t i2s_event; + while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) { + if (i2s_event.type == I2S_EVENT_TX_Q_OVF) { + tx_dma_underflow = true; + } + } + size_t bytes_to_read = dma_buffers_size; size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read, pdMS_TO_TICKS(TASK_DELAY_MS)); if (bytes_read > 0) { - last_data_received_time = millis(); size_t bytes_written = 0; if ((audio_stream_info.bits_per_sample == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) { @@ -307,15 +315,13 @@ void I2SAudioSpeaker::speaker_task(void *params) { if (bytes_written != bytes_read) { xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); } - + tx_dma_underflow = false; + last_data_received_time = millis(); } else { // No data received - - if (stop_gracefully) { + if (stop_gracefully && tx_dma_underflow) { break; } - - i2s_zero_dma_buffer(this_speaker->parent_->get_port()); } } } else { @@ -326,7 +332,6 @@ void I2SAudioSpeaker::speaker_task(void *params) { xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING); - i2s_stop(this_speaker->parent_->get_port()); i2s_driver_uninstall(this_speaker->parent_->get_port()); this_speaker->parent_->unlock(); @@ -402,8 +407,8 @@ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t rin return ESP_ERR_NO_MEM; } - if (this->audio_ring_buffer_ == nullptr) { - // Allocate ring buffer + if (this->audio_ring_buffer_.use_count() == 0) { + // Allocate ring buffer. Uses a shared_ptr to ensure it isn't improperly deallocated. this->audio_ring_buffer_ = RingBuffer::create(ring_buffer_size); } @@ -419,6 +424,8 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_() { return ESP_ERR_INVALID_STATE; } + int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000; + i2s_driver_config_t config = { .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX), .sample_rate = this->sample_rate_, @@ -427,7 +434,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_() { .communication_format = this->i2s_comm_fmt_, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = DMA_BUFFERS_COUNT, - .dma_buf_len = DMA_BUFFER_SIZE, + .dma_buf_len = dma_buffer_length, .use_apll = this->use_apll_, .tx_desc_auto_clear = true, .fixed_mclk = I2S_PIN_NO_CHANGE, @@ -448,7 +455,8 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_() { } #endif - esp_err_t err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr); + esp_err_t err = + i2s_driver_install(this->parent_->get_port(), &config, I2S_EVENT_QUEUE_COUNT, &this->i2s_event_queue_); if (err != ESP_OK) { // Failed to install the driver, so unlock the I2S port this->parent_->unlock(); @@ -502,16 +510,7 @@ esp_err_t I2SAudioSpeaker::reconfigure_i2s_stream_info_(audio::AudioStreamInfo & } void I2SAudioSpeaker::delete_task_(size_t buffer_size) { - if (this->audio_ring_buffer_ != nullptr) { - xEventGroupWaitBits(this->event_group_, - MESSAGE_RING_BUFFER_AVAILABLE_TO_WRITE, // Bit message to read - pdFALSE, // Don't clear the bits on exit - pdTRUE, // Don't wait for all the bits, - portMAX_DELAY); // Block indefinitely until a command bit is set - - this->audio_ring_buffer_.reset(); // Deallocates the ring buffer stored in the unique_ptr - this->audio_ring_buffer_ = nullptr; - } + this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr if (this->data_buffer_ != nullptr) { ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); @@ -520,6 +519,7 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) { } xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED); + xQueueReset(this->i2s_event_queue_); this->task_created_ = false; vTaskDelete(nullptr); diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 3c512d4d4d..8b7386ba58 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "esphome/components/audio/audio.h" @@ -27,6 +28,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp void setup() override; void loop() override; + void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; } void set_timeout(uint32_t ms) { this->timeout_ = ms; } void set_dout_pin(uint8_t pin) { this->dout_pin_ = pin; } #if SOC_I2S_SUPPORTS_DAC @@ -117,10 +119,14 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp TaskHandle_t speaker_task_handle_{nullptr}; EventGroupHandle_t event_group_{nullptr}; - uint8_t *data_buffer_; - std::unique_ptr audio_ring_buffer_; + QueueHandle_t i2s_event_queue_; - uint32_t timeout_; + uint8_t *data_buffer_; + std::shared_ptr audio_ring_buffer_; + + uint32_t buffer_duration_ms_; + + optional timeout_; uint8_t dout_pin_; bool task_created_{false}; From 1e80c4807eaab36d92c732a303664d29576d9e06 Mon Sep 17 00:00:00 2001 From: FreeBear-nc <67865163+FreeBear-nc@users.noreply.github.com> Date: Tue, 12 Nov 2024 18:20:48 +0000 Subject: [PATCH 0598/1052] Message to string extend (#7755) --- esphome/components/opentherm/hub.cpp | 4 ++-- esphome/components/opentherm/opentherm.cpp | 27 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index 432036d58d..dfa8ea95c5 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -371,11 +371,11 @@ void OpenthermHub::dump_config() { ESP_LOGCONFIG(TAG, " Numbers: %s", SHOW(OPENTHERM_NUMBER_LIST(ID, ))); ESP_LOGCONFIG(TAG, " Initial requests:"); for (auto type : this->initial_messages_) { - ESP_LOGCONFIG(TAG, " - %d", type); + ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str((type))); } ESP_LOGCONFIG(TAG, " Repeating requests:"); for (auto type : this->repeating_messages_) { - ESP_LOGCONFIG(TAG, " - %d", type); + ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str((type))); } } diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 4a23bb94cf..26c707f9a0 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -483,6 +483,8 @@ const char *OpenTherm::message_id_to_str(MessageId id) { TO_STRING_MEMBER(EXHAUST_TEMP) TO_STRING_MEMBER(FAN_SPEED) TO_STRING_MEMBER(FLAME_CURRENT) + TO_STRING_MEMBER(ROOM_TEMP_CH2) + TO_STRING_MEMBER(REL_HUMIDITY) TO_STRING_MEMBER(DHW_BOUNDS) TO_STRING_MEMBER(CH_BOUNDS) TO_STRING_MEMBER(OTC_CURVE_BOUNDS) @@ -492,14 +494,39 @@ const char *OpenTherm::message_id_to_str(MessageId id) { TO_STRING_MEMBER(HVAC_STATUS) TO_STRING_MEMBER(REL_VENT_SETPOINT) TO_STRING_MEMBER(DEVICE_VENT) + TO_STRING_MEMBER(HVAC_VER_ID) TO_STRING_MEMBER(REL_VENTILATION) TO_STRING_MEMBER(REL_HUMID_EXHAUST) + TO_STRING_MEMBER(EXHAUST_CO2) TO_STRING_MEMBER(SUPPLY_INLET_TEMP) TO_STRING_MEMBER(SUPPLY_OUTLET_TEMP) TO_STRING_MEMBER(EXHAUST_INLET_TEMP) TO_STRING_MEMBER(EXHAUST_OUTLET_TEMP) + TO_STRING_MEMBER(EXHAUST_FAN_SPEED) + TO_STRING_MEMBER(SUPPLY_FAN_SPEED) + TO_STRING_MEMBER(REMOTE_VENTILATION_PARAM) TO_STRING_MEMBER(NOM_REL_VENTILATION) + TO_STRING_MEMBER(HVAC_NUM_TSP) + TO_STRING_MEMBER(HVAC_IDX_TSP) + TO_STRING_MEMBER(HVAC_FHB_SIZE) + TO_STRING_MEMBER(HVAC_FHB_IDX) + TO_STRING_MEMBER(RF_SIGNAL) + TO_STRING_MEMBER(DHW_MODE) TO_STRING_MEMBER(OVERRIDE_FUNC) + TO_STRING_MEMBER(SOLAR_MODE_FLAGS) + TO_STRING_MEMBER(SOLAR_ASF) + TO_STRING_MEMBER(SOLAR_VERSION_ID) + TO_STRING_MEMBER(SOLAR_PRODUCT_ID) + TO_STRING_MEMBER(SOLAR_NUM_TSP) + TO_STRING_MEMBER(SOLAR_IDX_TSP) + TO_STRING_MEMBER(SOLAR_FHB_SIZE) + TO_STRING_MEMBER(SOLAR_FHB_IDX) + TO_STRING_MEMBER(SOLAR_STARTS) + TO_STRING_MEMBER(SOLAR_HOURS) + TO_STRING_MEMBER(SOLAR_ENERGY) + TO_STRING_MEMBER(SOLAR_TOTAL_ENERGY) + TO_STRING_MEMBER(FAILED_BURNER_STARTS) + TO_STRING_MEMBER(BURNER_FLAME_LOW) TO_STRING_MEMBER(OEM_DIAGNOSTIC) TO_STRING_MEMBER(BURNER_STARTS) TO_STRING_MEMBER(CH_PUMP_STARTS) From e6a1254e65d69ae0f362891409e7085768b6a479 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 12 Nov 2024 19:23:00 +0100 Subject: [PATCH 0599/1052] [sun] Implements `is_above_horizon()` (#7754) --- esphome/components/sun/sun.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/sun/sun.h b/esphome/components/sun/sun.h index de4801a655..77d62d34c3 100644 --- a/esphome/components/sun/sun.h +++ b/esphome/components/sun/sun.h @@ -59,6 +59,9 @@ class Sun { void set_latitude(double latitude) { location_.latitude = latitude; } void set_longitude(double longitude) { location_.longitude = longitude; } + // Check if the sun is above the horizon, with a default elevation angle of -0.83333 (standard for sunrise/set). + bool is_above_horizon(double elevation = -0.83333) { return this->elevation() > elevation; } + optional sunrise(double elevation); optional sunset(double elevation); optional sunrise(ESPTime date, double elevation); From b367c01b4b27ac19c75d75774c7ce162894d6035 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 12 Nov 2024 13:48:03 -0500 Subject: [PATCH 0600/1052] [core] Ring buffer write functions use const pointer parameter (#7750) --- esphome/core/ring_buffer.cpp | 4 ++-- esphome/core/ring_buffer.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index f97c686684..6152ada314 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -46,7 +46,7 @@ size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { return bytes_read; } -size_t RingBuffer::write(void *data, size_t len) { +size_t RingBuffer::write(const void *data, size_t len) { size_t free = this->free(); if (free < len) { size_t needed = len - free; @@ -56,7 +56,7 @@ size_t RingBuffer::write(void *data, size_t len) { return xStreamBufferSend(this->handle_, data, len, 0); } -size_t RingBuffer::write_without_replacement(void *data, size_t len, TickType_t ticks_to_wait) { +size_t RingBuffer::write_without_replacement(const void *data, size_t len, TickType_t ticks_to_wait) { return xStreamBufferSend(this->handle_, data, len, ticks_to_wait); } diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h index c0511fb52e..aade1b5f49 100644 --- a/esphome/core/ring_buffer.h +++ b/esphome/core/ring_buffer.h @@ -37,7 +37,7 @@ class RingBuffer { * @param len Number of bytes to write * @return Number of bytes written */ - size_t write(void *data, size_t len); + size_t write(const void *data, size_t len); /** * @brief Writes to the ring buffer without overwriting oldest data. @@ -50,7 +50,7 @@ class RingBuffer { * @param ticks_to_wait Maximum number of FreeRTOS ticks to wait (default: 0) * @return Number of bytes written */ - size_t write_without_replacement(void *data, size_t len, TickType_t ticks_to_wait = 0); + size_t write_without_replacement(const void *data, size_t len, TickType_t ticks_to_wait = 0); /** * @brief Returns the number of available bytes in the ring buffer. From 7d75c9157bd9ddaa7c56389d6b3126a461b1e52b Mon Sep 17 00:00:00 2001 From: TFGF Date: Tue, 12 Nov 2024 17:48:40 -0300 Subject: [PATCH 0601/1052] [Modbus Controller] Added `on_online` and `on_offline` automation (#7417) --- .../components/modbus_controller/__init__.py | 32 ++++++++++++++++++- .../components/modbus_controller/automation.h | 16 ++++++++++ esphome/components/modbus_controller/const.py | 2 ++ .../modbus_controller/modbus_controller.cpp | 16 ++++++++-- .../modbus_controller/modbus_controller.h | 9 ++++++ .../modbus_controller/test.esp32-ard.yaml | 3 ++ .../modbus_controller/test.esp32-idf.yaml | 3 ++ 7 files changed, 78 insertions(+), 3 deletions(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 488baa245a..5c407d6fff 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -25,6 +25,8 @@ from .const import ( CONF_MODBUS_CONTROLLER_ID, CONF_OFFLINE_SKIP_UPDATES, CONF_ON_COMMAND_SENT, + CONF_ON_ONLINE, + CONF_ON_OFFLINE, CONF_REGISTER_COUNT, CONF_REGISTER_TYPE, CONF_RESPONSE_SIZE, @@ -114,6 +116,14 @@ ModbusCommandSentTrigger = modbus_controller_ns.class_( "ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_) ) +ModbusOnlineTrigger = modbus_controller_ns.class_( + "ModbusOnlineTrigger", automation.Trigger.template(cg.int_, cg.int_) +) + +ModbusOfflineTrigger = modbus_controller_ns.class_( + "ModbusOfflineTrigger", automation.Trigger.template(cg.int_, cg.int_) +) + _LOGGER = logging.getLogger(__name__) ModbusServerRegisterSchema = cv.Schema( @@ -146,6 +156,16 @@ CONFIG_SCHEMA = cv.All( ), } ), + cv.Optional(CONF_ON_ONLINE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOnlineTrigger), + } + ), + cv.Optional(CONF_ON_OFFLINE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOnlineTrigger), + } + ), } ) .extend(cv.polling_component_schema("60s")) @@ -284,7 +304,17 @@ async def to_code(config): for conf in config.get(CONF_ON_COMMAND_SENT, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation( - trigger, [(int, "function_code"), (int, "address")], conf + trigger, [(cg.int_, "function_code"), (cg.int_, "address")], conf + ) + for conf in config.get(CONF_ON_ONLINE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(cg.int_, "function_code"), (cg.int_, "address")], conf + ) + for conf in config.get(CONF_ON_OFFLINE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(cg.int_, "function_code"), (cg.int_, "address")], conf ) diff --git a/esphome/components/modbus_controller/automation.h b/esphome/components/modbus_controller/automation.h index ad8de4b05d..b3338192cc 100644 --- a/esphome/components/modbus_controller/automation.h +++ b/esphome/components/modbus_controller/automation.h @@ -15,5 +15,21 @@ class ModbusCommandSentTrigger : public Trigger { } }; +class ModbusOnlineTrigger : public Trigger { + public: + ModbusOnlineTrigger(ModbusController *a_modbuscontroller) { + a_modbuscontroller->add_on_online_callback( + [this](int function_code, int address) { this->trigger(function_code, address); }); + } +}; + +class ModbusOfflineTrigger : public Trigger { + public: + ModbusOfflineTrigger(ModbusController *a_modbuscontroller) { + a_modbuscontroller->add_on_offline_callback( + [this](int function_code, int address) { this->trigger(function_code, address); }); + } +}; + } // namespace modbus_controller } // namespace esphome diff --git a/esphome/components/modbus_controller/const.py b/esphome/components/modbus_controller/const.py index 5cf7d230f1..4d39e48dcd 100644 --- a/esphome/components/modbus_controller/const.py +++ b/esphome/components/modbus_controller/const.py @@ -9,6 +9,8 @@ CONF_MAX_CMD_RETRIES = "max_cmd_retries" CONF_MODBUS_CONTROLLER_ID = "modbus_controller_id" CONF_MODBUS_FUNCTIONCODE = "modbus_functioncode" CONF_ON_COMMAND_SENT = "on_command_sent" +CONF_ON_ONLINE = "on_online" +CONF_ON_OFFLINE = "on_offline" CONF_RAW_ENCODE = "raw_encode" CONF_REGISTER_COUNT = "register_count" CONF_REGISTER_TYPE = "register_type" diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 1dcb533629..e1102516ca 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -32,8 +32,10 @@ bool ModbusController::send_next_command_() { r.skip_updates_counter = this->offline_skip_updates_; } } + + this->module_offline_ = true; + this->offline_callback_.call((int) command->function_code, command->register_address); } - this->module_offline_ = true; ESP_LOGD(TAG, "Modbus command to device=%d register=0x%02X no response received - removed from send queue", this->address_, command->register_address); this->command_queue_.pop_front(); @@ -68,8 +70,10 @@ void ModbusController::on_modbus_data(const std::vector &data) { r.skip_updates_counter = 0; } } + // Restore module online state + this->module_offline_ = false; + this->online_callback_.call((int) current_command->function_code, current_command->register_address); } - this->module_offline_ = false; // Move the commandItem to the response queue current_command->payload = data; @@ -670,5 +674,13 @@ void ModbusController::add_on_command_sent_callback(std::functioncommand_sent_callback_.add(std::move(callback)); } +void ModbusController::add_on_online_callback(std::function &&callback) { + this->online_callback_.add(std::move(callback)); +} + +void ModbusController::add_on_offline_callback(std::function &&callback) { + this->offline_callback_.add(std::move(callback)); +} + } // namespace modbus_controller } // namespace esphome diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h index 1fa35e1535..2a0b936bf5 100644 --- a/esphome/components/modbus_controller/modbus_controller.h +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -468,6 +468,10 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { bool get_module_offline() { return module_offline_; } /// Set callback for commands void add_on_command_sent_callback(std::function &&callback); + /// Set callback for online changes + void add_on_online_callback(std::function &&callback); + /// Set callback for offline changes + void add_on_offline_callback(std::function &&callback); /// called by esphome generated code to set the max_cmd_retries. void set_max_cmd_retries(uint8_t max_cmd_retries) { this->max_cmd_retries_ = max_cmd_retries; } /// get how many times a command will be (re)sent if no response is received @@ -508,7 +512,12 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { uint16_t offline_skip_updates_; /// How many times we will retry a command if we get no response uint8_t max_cmd_retries_{4}; + /// Command sent callback CallbackManager command_sent_callback_{}; + /// Server online callback + CallbackManager online_callback_{}; + /// Server offline callback + CallbackManager offline_callback_{}; }; /** Convert vector response payload to float. diff --git a/tests/components/modbus_controller/test.esp32-ard.yaml b/tests/components/modbus_controller/test.esp32-ard.yaml index cd95d149cb..f5c5c10125 100644 --- a/tests/components/modbus_controller/test.esp32-ard.yaml +++ b/tests/components/modbus_controller/test.esp32-ard.yaml @@ -21,6 +21,9 @@ modbus_controller: address: 0x2 modbus_id: mod_bus1 allow_duplicate_commands: false + on_online: + then: + logger.log: "Module Online" - id: modbus_controller2 address: 0x2 modbus_id: mod_bus2 diff --git a/tests/components/modbus_controller/test.esp32-idf.yaml b/tests/components/modbus_controller/test.esp32-idf.yaml index ba28e94d73..0e1849dd88 100644 --- a/tests/components/modbus_controller/test.esp32-idf.yaml +++ b/tests/components/modbus_controller/test.esp32-idf.yaml @@ -13,4 +13,7 @@ modbus_controller: address: 0x2 modbus_id: mod_bus1 allow_duplicate_commands: true + on_offline: + then: + logger.log: "Module Offline" max_cmd_retries: 10 From 053465d3f627809fc890eb94271419df0368f369 Mon Sep 17 00:00:00 2001 From: Kyle Cascade Date: Tue, 12 Nov 2024 14:54:25 -0800 Subject: [PATCH 0602/1052] Updated dfplayer logging to be more user-friendly (#7740) --- esphome/components/dfplayer/dfplayer.cpp | 138 ++++++++++++++++++++++- esphome/components/dfplayer/dfplayer.h | 72 ++++-------- 2 files changed, 151 insertions(+), 59 deletions(-) diff --git a/esphome/components/dfplayer/dfplayer.cpp b/esphome/components/dfplayer/dfplayer.cpp index aa2dc260e0..98c3e91e46 100644 --- a/esphome/components/dfplayer/dfplayer.cpp +++ b/esphome/components/dfplayer/dfplayer.cpp @@ -6,7 +6,104 @@ namespace dfplayer { static const char *const TAG = "dfplayer"; +void DFPlayer::next() { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Playing next track"); + this->send_cmd_(0x01); +} + +void DFPlayer::previous() { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Playing previous track"); + this->send_cmd_(0x02); +} +void DFPlayer::play_mp3(uint16_t file) { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Playing file %d in mp3 folder", file); + this->send_cmd_(0x12, file); +} + +void DFPlayer::play_file(uint16_t file) { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Playing file %d", file); + this->send_cmd_(0x03, file); +} + +void DFPlayer::play_file_loop(uint16_t file) { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Playing file %d in loop", file); + this->send_cmd_(0x08, file); +} + +void DFPlayer::play_folder_loop(uint16_t folder) { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Playing folder %d in loop", folder); + this->send_cmd_(0x17, folder); +} + +void DFPlayer::volume_up() { + ESP_LOGD(TAG, "Increasing volume"); + this->send_cmd_(0x04); +} + +void DFPlayer::volume_down() { + ESP_LOGD(TAG, "Decreasing volume"); + this->send_cmd_(0x05); +} + +void DFPlayer::set_device(Device device) { + ESP_LOGD(TAG, "Setting device to %d", device); + this->send_cmd_(0x09, device); +} + +void DFPlayer::set_volume(uint8_t volume) { + ESP_LOGD(TAG, "Setting volume to %d", volume); + this->send_cmd_(0x06, volume); +} + +void DFPlayer::set_eq(EqPreset preset) { + ESP_LOGD(TAG, "Setting EQ to %d", preset); + this->send_cmd_(0x07, preset); +} + +void DFPlayer::sleep() { + this->ack_reset_is_playing_ = true; + ESP_LOGD(TAG, "Putting DFPlayer to sleep"); + this->send_cmd_(0x0A); +} + +void DFPlayer::reset() { + this->ack_reset_is_playing_ = true; + ESP_LOGD(TAG, "Resetting DFPlayer"); + this->send_cmd_(0x0C); +} + +void DFPlayer::start() { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Starting playback"); + this->send_cmd_(0x0D); +} + +void DFPlayer::pause() { + this->ack_reset_is_playing_ = true; + ESP_LOGD(TAG, "Pausing playback"); + this->send_cmd_(0x0E); +} + +void DFPlayer::stop() { + this->ack_reset_is_playing_ = true; + ESP_LOGD(TAG, "Stopping playback"); + this->send_cmd_(0x16); +} + +void DFPlayer::random() { + this->ack_set_is_playing_ = true; + ESP_LOGD(TAG, "Playing random file"); + this->send_cmd_(0x18); +} + void DFPlayer::play_folder(uint16_t folder, uint16_t file) { + ESP_LOGD(TAG, "Playing file %d in folder %d", file, folder); if (folder < 100 && file < 256) { this->ack_set_is_playing_ = true; this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file); @@ -29,7 +126,7 @@ void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) { this->sent_cmd_ = cmd; - ESP_LOGD(TAG, "Send Command %#02x arg %#04x", cmd, argument); + ESP_LOGV(TAG, "Send Command %#02x arg %#04x", cmd, argument); this->write_array(buffer, 10); } @@ -101,9 +198,37 @@ void DFPlayer::loop() { ESP_LOGV(TAG, "Nack"); this->ack_set_is_playing_ = false; this->ack_reset_is_playing_ = false; - if (argument == 6) { - ESP_LOGV(TAG, "File not found"); - this->is_playing_ = false; + switch (argument) { + case 0x01: + ESP_LOGE(TAG, "Module is busy or uninitialized"); + break; + case 0x02: + ESP_LOGE(TAG, "Module is in sleep mode"); + break; + case 0x03: + ESP_LOGE(TAG, "Serial receive error"); + break; + case 0x04: + ESP_LOGE(TAG, "Checksum incorrect"); + break; + case 0x05: + ESP_LOGE(TAG, "Specified track is out of current track scope"); + this->is_playing_ = false; + break; + case 0x06: + ESP_LOGE(TAG, "Specified track is not found"); + this->is_playing_ = false; + break; + case 0x07: + ESP_LOGE(TAG, "Insertion error (an inserting operation only can be done when a track is being played)"); + break; + case 0x08: + ESP_LOGE(TAG, "SD card reading failed (SD card pulled out or damaged)"); + break; + case 0x09: + ESP_LOGE(TAG, "Entered into sleep mode"); + this->is_playing_ = false; + break; } break; case 0x41: @@ -113,12 +238,13 @@ void DFPlayer::loop() { this->ack_set_is_playing_ = false; this->ack_reset_is_playing_ = false; break; - case 0x3D: // Playback finished + case 0x3D: + ESP_LOGV(TAG, "Playback finished"); this->is_playing_ = false; this->on_finished_playback_callback_.call(); break; default: - ESP_LOGD(TAG, "Command %#02x arg %#04x", cmd, argument); + ESP_LOGV(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument); } this->sent_cmd_ = 0; this->read_pos_ = 0; diff --git a/esphome/components/dfplayer/dfplayer.h b/esphome/components/dfplayer/dfplayer.h index 26e90fd410..d2ec0a2310 100644 --- a/esphome/components/dfplayer/dfplayer.h +++ b/esphome/components/dfplayer/dfplayer.h @@ -23,64 +23,30 @@ enum Device { TF_CARD = 2, }; +// See the datasheet here: +// https://github.com/DFRobot/DFRobotDFPlayerMini/blob/master/doc/FN-M16P%2BEmbedded%2BMP3%2BAudio%2BModule%2BDatasheet.pdf class DFPlayer : public uart::UARTDevice, public Component { public: void loop() override; - void next() { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x01); - } - void previous() { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x02); - } - void play_mp3(uint16_t file) { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x12, file); - } - void play_file(uint16_t file) { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x03, file); - } - void play_file_loop(uint16_t file) { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x08, file); - } + void next(); + void previous(); + void play_mp3(uint16_t file); + void play_file(uint16_t file); + void play_file_loop(uint16_t file); void play_folder(uint16_t folder, uint16_t file); - void play_folder_loop(uint16_t folder) { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x17, folder); - } - void volume_up() { this->send_cmd_(0x04); } - void volume_down() { this->send_cmd_(0x05); } - void set_device(Device device) { this->send_cmd_(0x09, device); } - void set_volume(uint8_t volume) { this->send_cmd_(0x06, volume); } - void set_eq(EqPreset preset) { this->send_cmd_(0x07, preset); } - void sleep() { - this->ack_reset_is_playing_ = true; - this->send_cmd_(0x0A); - } - void reset() { - this->ack_reset_is_playing_ = true; - this->send_cmd_(0x0C); - } - void start() { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x0D); - } - void pause() { - this->ack_reset_is_playing_ = true; - this->send_cmd_(0x0E); - } - void stop() { - this->ack_reset_is_playing_ = true; - this->send_cmd_(0x16); - } - void random() { - this->ack_set_is_playing_ = true; - this->send_cmd_(0x18); - } + void play_folder_loop(uint16_t folder); + void volume_up(); + void volume_down(); + void set_device(Device device); + void set_volume(uint8_t volume); + void set_eq(EqPreset preset); + void sleep(); + void reset(); + void start(); + void pause(); + void stop(); + void random(); bool is_playing() { return is_playing_; } void dump_config() override; From 80226694d5d0c5d44f0bb8c2c39b070802c4b073 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:16:13 +1300 Subject: [PATCH 0603/1052] Bump version to 2024.11.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5645c9eaab..c3c8712677 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.0-dev" +__version__ = "2024.11.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 1f7f03f563e3500beeda9a3c163ee48e309e7617 Mon Sep 17 00:00:00 2001 From: luar123 <49960470+luar123@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:18:10 +0100 Subject: [PATCH 0604/1052] Fix temperature and humidity for bme680 with bsec2 (#7728) --- .../components/bme68x_bsec2/bme68x_bsec2.cpp | 95 ++++++++++--------- .../components/bme68x_bsec2/bme68x_bsec2.h | 2 - 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp index 5425bbd5b7..f83f20f1a5 100644 --- a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp @@ -204,11 +204,11 @@ void BME68xBSEC2Component::update_subscription_() { } void BME68xBSEC2Component::run_() { + this->op_mode_ = this->bsec_settings_.op_mode; int64_t curr_time_ns = this->get_time_ns_(); - if (curr_time_ns < this->next_call_ns_) { + if (curr_time_ns < this->bsec_settings_.next_call) { return; } - this->op_mode_ = this->bsec_settings_.op_mode; uint8_t status; ESP_LOGV(TAG, "Performing sensor run"); @@ -219,57 +219,60 @@ void BME68xBSEC2Component::run_() { ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_); return; } - this->next_call_ns_ = this->bsec_settings_.next_call; - if (this->bsec_settings_.trigger_measurement) { - bme68x_get_conf(&bme68x_conf, &this->bme68x_); + switch (this->bsec_settings_.op_mode) { + case BME68X_FORCED_MODE: + bme68x_get_conf(&bme68x_conf, &this->bme68x_); - bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; - bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; - bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; - bme68x_set_conf(&bme68x_conf, &this->bme68x_); + bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; + bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; + bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; + bme68x_set_conf(&bme68x_conf, &this->bme68x_); + this->bme68x_heatr_conf_.enable = BME68X_ENABLE; + this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature; + this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration; + + // status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_); + status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); + status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_); + this->op_mode_ = BME68X_FORCED_MODE; + ESP_LOGV(TAG, "Using forced mode"); + + break; + case BME68X_PARALLEL_MODE: + if (this->op_mode_ != this->bsec_settings_.op_mode) { + bme68x_get_conf(&bme68x_conf, &this->bme68x_); + + bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; + bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; + bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; + bme68x_set_conf(&bme68x_conf, &this->bme68x_); - switch (this->bsec_settings_.op_mode) { - case BME68X_FORCED_MODE: this->bme68x_heatr_conf_.enable = BME68X_ENABLE; - this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature; - this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration; + this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile; + this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile; + this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len; + this->bme68x_heatr_conf_.shared_heatr_dur = + BSEC_TOTAL_HEAT_DUR - + (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000)); - status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_); - status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); - status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_); - this->op_mode_ = BME68X_FORCED_MODE; - this->sleep_mode_ = false; - ESP_LOGV(TAG, "Using forced mode"); + status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); - break; - case BME68X_PARALLEL_MODE: - if (this->op_mode_ != this->bsec_settings_.op_mode) { - this->bme68x_heatr_conf_.enable = BME68X_ENABLE; - this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile; - this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile; - this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len; - this->bme68x_heatr_conf_.shared_heatr_dur = - BSEC_TOTAL_HEAT_DUR - - (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000)); - - status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); - - status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_); - this->op_mode_ = BME68X_PARALLEL_MODE; - this->sleep_mode_ = false; - ESP_LOGV(TAG, "Using parallel mode"); - } - break; - case BME68X_SLEEP_MODE: - if (!this->sleep_mode_) { - bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_); - this->sleep_mode_ = true; - ESP_LOGV(TAG, "Using sleep mode"); - } - break; - } + status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_); + this->op_mode_ = BME68X_PARALLEL_MODE; + ESP_LOGV(TAG, "Using parallel mode"); + } + break; + case BME68X_SLEEP_MODE: + if (this->op_mode_ != this->bsec_settings_.op_mode) { + bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_); + this->op_mode_ = BME68X_SLEEP_MODE; + ESP_LOGV(TAG, "Using sleep mode"); + } + break; + } + if (this->bsec_settings_.trigger_measurement && this->bsec_settings_.op_mode != BME68X_SLEEP_MODE) { uint32_t meas_dur = 0; meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_); ESP_LOGV(TAG, "Queueing read in %uus", meas_dur); diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.h b/esphome/components/bme68x_bsec2/bme68x_bsec2.h index 7b9db2b7bf..86d3e5dfbf 100644 --- a/esphome/components/bme68x_bsec2/bme68x_bsec2.h +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.h @@ -113,13 +113,11 @@ class BME68xBSEC2Component : public Component { struct bme68x_heatr_conf bme68x_heatr_conf_; uint8_t op_mode_; // operating mode of sensor - bool sleep_mode_; bsec_library_return_t bsec_status_{BSEC_OK}; int8_t bme68x_status_{BME68X_OK}; int64_t last_time_ms_{0}; uint32_t millis_overflow_counter_{0}; - int64_t next_call_ns_{0}; std::queue> queue_; From a2cab960a9d2f138fe736ce8147b383f13146475 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:16:13 +1300 Subject: [PATCH 0605/1052] Bump version to 2024.12.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5645c9eaab..d42ee5ee72 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.0-dev" +__version__ = "2024.12.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From c7c8711c9c8380d9d90875973e2e8c0e70678667 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 13 Nov 2024 12:39:02 -0500 Subject: [PATCH 0606/1052] [i2s_audio] Bugfix: Adjust I2S speaker setup priority (#7759) --- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 11 +---------- .../components/i2s_audio/speaker/i2s_audio_speaker.h | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index c3f4566411..53b3cc8dc0 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -99,14 +99,6 @@ void I2SAudioSpeaker::setup() { this->mark_failed(); return; } - - this->i2s_event_queue_ = xQueueCreate(I2S_EVENT_QUEUE_COUNT, sizeof(i2s_event_t)); - - if (this->i2s_event_queue_ == nullptr) { - ESP_LOGE(TAG, "Failed to create I2S event queue"); - this->mark_failed(); - return; - } } void I2SAudioSpeaker::loop() { @@ -339,7 +331,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { } void I2SAudioSpeaker::start() { - if (this->is_failed() || this->status_has_error()) + if (!this->is_ready() || this->is_failed() || this->status_has_error()) return; if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING)) return; @@ -519,7 +511,6 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) { } xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED); - xQueueReset(this->i2s_event_queue_); this->task_created_ = false; vTaskDelete(nullptr); diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 8b7386ba58..2b90f39399 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -23,7 +23,7 @@ namespace i2s_audio { class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Component { public: - float get_setup_priority() const override { return esphome::setup_priority::LATE; } + float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; } void setup() override; void loop() override; From 39c889e6625a212b582d519012c6b69a45862bc6 Mon Sep 17 00:00:00 2001 From: Roving Ronin <108674933+Roving-Ronin@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:43:21 +1100 Subject: [PATCH 0607/1052] Update UNIT_VOLT_AMPS_REACTIVE = "var" (Currently 'VAR') (#7643) --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index d42ee5ee72..6a643e1e30 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1095,7 +1095,7 @@ UNIT_STEPS = "steps" UNIT_VOLT = "V" UNIT_VOLT_AMPS = "VA" UNIT_VOLT_AMPS_HOURS = "VAh" -UNIT_VOLT_AMPS_REACTIVE = "VAR" +UNIT_VOLT_AMPS_REACTIVE = "var" UNIT_VOLT_AMPS_REACTIVE_HOURS = "VARh" UNIT_WATT = "W" UNIT_WATT_HOURS = "Wh" From d01508885531c31a2ebb518c8b07f550d5768ccc Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Wed, 13 Nov 2024 21:44:18 -0300 Subject: [PATCH 0608/1052] Fix reactive power unit of measurement from VAR to var (#7757) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/sdm_meter/sdm_meter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sdm_meter/sdm_meter.cpp b/esphome/components/sdm_meter/sdm_meter.cpp index 9c35d306ad..18e06e2b04 100644 --- a/esphome/components/sdm_meter/sdm_meter.cpp +++ b/esphome/components/sdm_meter/sdm_meter.cpp @@ -38,7 +38,7 @@ void SDMMeter::on_modbus_data(const std::vector &data) { ESP_LOGD( TAG, - "SDMMeter Phase %c: V=%.3f V, I=%.3f A, Active P=%.3f W, Apparent P=%.3f VA, Reactive P=%.3f VAR, PF=%.3f, " + "SDMMeter Phase %c: V=%.3f V, I=%.3f A, Active P=%.3f W, Apparent P=%.3f VA, Reactive P=%.3f var, PF=%.3f, " "PA=%.3f °", i + 'A', voltage, current, active_power, apparent_power, reactive_power, power_factor, phase_angle); if (phase.voltage_sensor_ != nullptr) From 5e62c489b08a70295f3247c19c907de57507e263 Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Wed, 13 Nov 2024 16:57:09 -0800 Subject: [PATCH 0609/1052] Disable bluetooth proxy during update (#7695) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp | 3 +++ tests/components/esp32_ble_tracker/common.yaml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 74b4b9aa89..b86d32ee61 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -65,6 +65,9 @@ void ESP32BLETracker::setup() { [this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) { if (state == ota::OTA_STARTED) { this->stop_scan(); + for (auto *client : this->clients_) { + client->disconnect(); + } } }); #endif diff --git a/tests/components/esp32_ble_tracker/common.yaml b/tests/components/esp32_ble_tracker/common.yaml index ef23635c9e..018bbb42b3 100644 --- a/tests/components/esp32_ble_tracker/common.yaml +++ b/tests/components/esp32_ble_tracker/common.yaml @@ -39,3 +39,10 @@ esp32_ble_tracker: - then: - lambda: |- ESP_LOGD("ble_auto", "The scan has ended!"); + +wifi: + ssid: MySSID + password: password1 + +ota: + - platform: esphome From 0b51ec2c88793191f8d28fecfe9b514b9195312f Mon Sep 17 00:00:00 2001 From: Fabio Bonelli Date: Thu, 14 Nov 2024 01:57:51 +0100 Subject: [PATCH 0610/1052] ld2420: fix typo in log message (#7758) --- esphome/components/ld2420/ld2420.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index e57fdbc84e..9d628cc14f 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -180,7 +180,7 @@ void LD2420Component::apply_config_action() { } void LD2420Component::factory_reset_action() { - ESP_LOGCONFIG(TAG, "Setiing 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(); From 44545a18a0de2724d14e497357a4aea5a8f90d19 Mon Sep 17 00:00:00 2001 From: luar123 <49960470+luar123@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:18:10 +0100 Subject: [PATCH 0611/1052] Fix temperature and humidity for bme680 with bsec2 (#7728) --- .../components/bme68x_bsec2/bme68x_bsec2.cpp | 95 ++++++++++--------- .../components/bme68x_bsec2/bme68x_bsec2.h | 2 - 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp index 5425bbd5b7..f83f20f1a5 100644 --- a/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.cpp @@ -204,11 +204,11 @@ void BME68xBSEC2Component::update_subscription_() { } void BME68xBSEC2Component::run_() { + this->op_mode_ = this->bsec_settings_.op_mode; int64_t curr_time_ns = this->get_time_ns_(); - if (curr_time_ns < this->next_call_ns_) { + if (curr_time_ns < this->bsec_settings_.next_call) { return; } - this->op_mode_ = this->bsec_settings_.op_mode; uint8_t status; ESP_LOGV(TAG, "Performing sensor run"); @@ -219,57 +219,60 @@ void BME68xBSEC2Component::run_() { ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC2 error code %d)", this->bsec_status_); return; } - this->next_call_ns_ = this->bsec_settings_.next_call; - if (this->bsec_settings_.trigger_measurement) { - bme68x_get_conf(&bme68x_conf, &this->bme68x_); + switch (this->bsec_settings_.op_mode) { + case BME68X_FORCED_MODE: + bme68x_get_conf(&bme68x_conf, &this->bme68x_); - bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; - bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; - bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; - bme68x_set_conf(&bme68x_conf, &this->bme68x_); + bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; + bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; + bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; + bme68x_set_conf(&bme68x_conf, &this->bme68x_); + this->bme68x_heatr_conf_.enable = BME68X_ENABLE; + this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature; + this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration; + + // status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_); + status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); + status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_); + this->op_mode_ = BME68X_FORCED_MODE; + ESP_LOGV(TAG, "Using forced mode"); + + break; + case BME68X_PARALLEL_MODE: + if (this->op_mode_ != this->bsec_settings_.op_mode) { + bme68x_get_conf(&bme68x_conf, &this->bme68x_); + + bme68x_conf.os_hum = this->bsec_settings_.humidity_oversampling; + bme68x_conf.os_temp = this->bsec_settings_.temperature_oversampling; + bme68x_conf.os_pres = this->bsec_settings_.pressure_oversampling; + bme68x_set_conf(&bme68x_conf, &this->bme68x_); - switch (this->bsec_settings_.op_mode) { - case BME68X_FORCED_MODE: this->bme68x_heatr_conf_.enable = BME68X_ENABLE; - this->bme68x_heatr_conf_.heatr_temp = this->bsec_settings_.heater_temperature; - this->bme68x_heatr_conf_.heatr_dur = this->bsec_settings_.heater_duration; + this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile; + this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile; + this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len; + this->bme68x_heatr_conf_.shared_heatr_dur = + BSEC_TOTAL_HEAT_DUR - + (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000)); - status = bme68x_set_op_mode(this->bsec_settings_.op_mode, &this->bme68x_); - status = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); - status = bme68x_set_op_mode(BME68X_FORCED_MODE, &this->bme68x_); - this->op_mode_ = BME68X_FORCED_MODE; - this->sleep_mode_ = false; - ESP_LOGV(TAG, "Using forced mode"); + status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); - break; - case BME68X_PARALLEL_MODE: - if (this->op_mode_ != this->bsec_settings_.op_mode) { - this->bme68x_heatr_conf_.enable = BME68X_ENABLE; - this->bme68x_heatr_conf_.heatr_temp_prof = this->bsec_settings_.heater_temperature_profile; - this->bme68x_heatr_conf_.heatr_dur_prof = this->bsec_settings_.heater_duration_profile; - this->bme68x_heatr_conf_.profile_len = this->bsec_settings_.heater_profile_len; - this->bme68x_heatr_conf_.shared_heatr_dur = - BSEC_TOTAL_HEAT_DUR - - (bme68x_get_meas_dur(BME68X_PARALLEL_MODE, &bme68x_conf, &this->bme68x_) / INT64_C(1000)); - - status = bme68x_set_heatr_conf(BME68X_PARALLEL_MODE, &this->bme68x_heatr_conf_, &this->bme68x_); - - status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_); - this->op_mode_ = BME68X_PARALLEL_MODE; - this->sleep_mode_ = false; - ESP_LOGV(TAG, "Using parallel mode"); - } - break; - case BME68X_SLEEP_MODE: - if (!this->sleep_mode_) { - bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_); - this->sleep_mode_ = true; - ESP_LOGV(TAG, "Using sleep mode"); - } - break; - } + status = bme68x_set_op_mode(BME68X_PARALLEL_MODE, &this->bme68x_); + this->op_mode_ = BME68X_PARALLEL_MODE; + ESP_LOGV(TAG, "Using parallel mode"); + } + break; + case BME68X_SLEEP_MODE: + if (this->op_mode_ != this->bsec_settings_.op_mode) { + bme68x_set_op_mode(BME68X_SLEEP_MODE, &this->bme68x_); + this->op_mode_ = BME68X_SLEEP_MODE; + ESP_LOGV(TAG, "Using sleep mode"); + } + break; + } + if (this->bsec_settings_.trigger_measurement && this->bsec_settings_.op_mode != BME68X_SLEEP_MODE) { uint32_t meas_dur = 0; meas_dur = bme68x_get_meas_dur(this->op_mode_, &bme68x_conf, &this->bme68x_); ESP_LOGV(TAG, "Queueing read in %uus", meas_dur); diff --git a/esphome/components/bme68x_bsec2/bme68x_bsec2.h b/esphome/components/bme68x_bsec2/bme68x_bsec2.h index 7b9db2b7bf..86d3e5dfbf 100644 --- a/esphome/components/bme68x_bsec2/bme68x_bsec2.h +++ b/esphome/components/bme68x_bsec2/bme68x_bsec2.h @@ -113,13 +113,11 @@ class BME68xBSEC2Component : public Component { struct bme68x_heatr_conf bme68x_heatr_conf_; uint8_t op_mode_; // operating mode of sensor - bool sleep_mode_; bsec_library_return_t bsec_status_{BSEC_OK}; int8_t bme68x_status_{BME68X_OK}; int64_t last_time_ms_{0}; uint32_t millis_overflow_counter_{0}; - int64_t next_call_ns_{0}; std::queue> queue_; From a0159a274662b22a3c791961742cdf019894f041 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 13 Nov 2024 12:39:02 -0500 Subject: [PATCH 0612/1052] [i2s_audio] Bugfix: Adjust I2S speaker setup priority (#7759) --- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 11 +---------- .../components/i2s_audio/speaker/i2s_audio_speaker.h | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index c3f4566411..53b3cc8dc0 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -99,14 +99,6 @@ void I2SAudioSpeaker::setup() { this->mark_failed(); return; } - - this->i2s_event_queue_ = xQueueCreate(I2S_EVENT_QUEUE_COUNT, sizeof(i2s_event_t)); - - if (this->i2s_event_queue_ == nullptr) { - ESP_LOGE(TAG, "Failed to create I2S event queue"); - this->mark_failed(); - return; - } } void I2SAudioSpeaker::loop() { @@ -339,7 +331,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { } void I2SAudioSpeaker::start() { - if (this->is_failed() || this->status_has_error()) + if (!this->is_ready() || this->is_failed() || this->status_has_error()) return; if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING)) return; @@ -519,7 +511,6 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) { } xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED); - xQueueReset(this->i2s_event_queue_); this->task_created_ = false; vTaskDelete(nullptr); diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 8b7386ba58..2b90f39399 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -23,7 +23,7 @@ namespace i2s_audio { class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Component { public: - float get_setup_priority() const override { return esphome::setup_priority::LATE; } + float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; } void setup() override; void loop() override; From 15bfc4c91f5c567dc89fd3daced0db0339ee85ff Mon Sep 17 00:00:00 2001 From: Roving Ronin <108674933+Roving-Ronin@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:43:21 +1100 Subject: [PATCH 0613/1052] Update UNIT_VOLT_AMPS_REACTIVE = "var" (Currently 'VAR') (#7643) --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index c3c8712677..bb0100d348 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1095,7 +1095,7 @@ UNIT_STEPS = "steps" UNIT_VOLT = "V" UNIT_VOLT_AMPS = "VA" UNIT_VOLT_AMPS_HOURS = "VAh" -UNIT_VOLT_AMPS_REACTIVE = "VAR" +UNIT_VOLT_AMPS_REACTIVE = "var" UNIT_VOLT_AMPS_REACTIVE_HOURS = "VARh" UNIT_WATT = "W" UNIT_WATT_HOURS = "Wh" From 9bc7b74d0137aa351c906e0c38a913afa5835fee Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Wed, 13 Nov 2024 21:44:18 -0300 Subject: [PATCH 0614/1052] Fix reactive power unit of measurement from VAR to var (#7757) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/sdm_meter/sdm_meter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sdm_meter/sdm_meter.cpp b/esphome/components/sdm_meter/sdm_meter.cpp index 9c35d306ad..18e06e2b04 100644 --- a/esphome/components/sdm_meter/sdm_meter.cpp +++ b/esphome/components/sdm_meter/sdm_meter.cpp @@ -38,7 +38,7 @@ void SDMMeter::on_modbus_data(const std::vector &data) { ESP_LOGD( TAG, - "SDMMeter Phase %c: V=%.3f V, I=%.3f A, Active P=%.3f W, Apparent P=%.3f VA, Reactive P=%.3f VAR, PF=%.3f, " + "SDMMeter Phase %c: V=%.3f V, I=%.3f A, Active P=%.3f W, Apparent P=%.3f VA, Reactive P=%.3f var, PF=%.3f, " "PA=%.3f °", i + 'A', voltage, current, active_power, apparent_power, reactive_power, power_factor, phase_angle); if (phase.voltage_sensor_ != nullptr) From 67a4e56fcfd7bfd8017c179c8004e6f0c67920d3 Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Wed, 13 Nov 2024 16:57:09 -0800 Subject: [PATCH 0615/1052] Disable bluetooth proxy during update (#7695) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp | 3 +++ tests/components/esp32_ble_tracker/common.yaml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 74b4b9aa89..b86d32ee61 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -65,6 +65,9 @@ void ESP32BLETracker::setup() { [this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) { if (state == ota::OTA_STARTED) { this->stop_scan(); + for (auto *client : this->clients_) { + client->disconnect(); + } } }); #endif diff --git a/tests/components/esp32_ble_tracker/common.yaml b/tests/components/esp32_ble_tracker/common.yaml index ef23635c9e..018bbb42b3 100644 --- a/tests/components/esp32_ble_tracker/common.yaml +++ b/tests/components/esp32_ble_tracker/common.yaml @@ -39,3 +39,10 @@ esp32_ble_tracker: - then: - lambda: |- ESP_LOGD("ble_auto", "The scan has ended!"); + +wifi: + ssid: MySSID + password: password1 + +ota: + - platform: esphome From 754352b4d7e717fa04615ff9ee86d43955ceaaa8 Mon Sep 17 00:00:00 2001 From: Fabio Bonelli Date: Thu, 14 Nov 2024 01:57:51 +0100 Subject: [PATCH 0616/1052] ld2420: fix typo in log message (#7758) --- esphome/components/ld2420/ld2420.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ld2420/ld2420.cpp b/esphome/components/ld2420/ld2420.cpp index e57fdbc84e..9d628cc14f 100644 --- a/esphome/components/ld2420/ld2420.cpp +++ b/esphome/components/ld2420/ld2420.cpp @@ -180,7 +180,7 @@ void LD2420Component::apply_config_action() { } void LD2420Component::factory_reset_action() { - ESP_LOGCONFIG(TAG, "Setiing 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(); From f4dc11477fb62014bc4397acdfbce61bfa57a90a Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:21:43 +1300 Subject: [PATCH 0617/1052] Bump version to 2024.11.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index bb0100d348..51c8090e91 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.0b1" +__version__ = "2024.11.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From b29c1194089f4770b7a3e9ab99b12ac890c3e2cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:43:52 +0100 Subject: [PATCH 0618/1052] Bump codecov/codecov-action from 4 to 5 (#7771) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82a55d0e2a..f5af3ec9e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -219,7 +219,7 @@ jobs: . venv/bin/activate pytest -vv --cov-report=xml --tb=native tests - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} From e81191ebd2583d44b4237db8b7dad3ed18877d0d Mon Sep 17 00:00:00 2001 From: pethans <38168907+pethans@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:47:29 -0800 Subject: [PATCH 0619/1052] TuyaFan control should use oscillation_type (#7776) Co-authored-by: Peter Hanson --- esphome/components/tuya/fan/tuya_fan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index 8a613d0bae..9b132e0de6 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -86,7 +86,7 @@ void TuyaFan::control(const fan::FanCall &call) { if (this->oscillation_id_.has_value() && call.get_oscillating().has_value()) { if (this->oscillation_type_ == TuyaDatapointType::ENUM) { this->parent_->set_enum_datapoint_value(*this->oscillation_id_, *call.get_oscillating()); - } else if (this->speed_type_ == TuyaDatapointType::BOOLEAN) { + } else if (this->oscillation_type_ == TuyaDatapointType::BOOLEAN) { this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, *call.get_oscillating()); } } From 6e41c22e9d7f777a5c556725b1a54cf33a669e39 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:44:39 +1300 Subject: [PATCH 0620/1052] Bump esphome-dashboard to 20241118.0 (#7782) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e11e629743..4bea8cf4ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20241025.0 +esphome-dashboard==20241118.0 aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 From 50aeefc66299f675f62a9eb09a77ceded7c61950 Mon Sep 17 00:00:00 2001 From: pethans <38168907+pethans@users.noreply.github.com> Date: Sun, 17 Nov 2024 10:47:29 -0800 Subject: [PATCH 0621/1052] TuyaFan control should use oscillation_type (#7776) Co-authored-by: Peter Hanson --- esphome/components/tuya/fan/tuya_fan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index 8a613d0bae..9b132e0de6 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -86,7 +86,7 @@ void TuyaFan::control(const fan::FanCall &call) { if (this->oscillation_id_.has_value() && call.get_oscillating().has_value()) { if (this->oscillation_type_ == TuyaDatapointType::ENUM) { this->parent_->set_enum_datapoint_value(*this->oscillation_id_, *call.get_oscillating()); - } else if (this->speed_type_ == TuyaDatapointType::BOOLEAN) { + } else if (this->oscillation_type_ == TuyaDatapointType::BOOLEAN) { this->parent_->set_boolean_datapoint_value(*this->oscillation_id_, *call.get_oscillating()); } } From 585586780bf2fe6645eff64ed9528da8bd57e042 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:44:39 +1300 Subject: [PATCH 0622/1052] Bump esphome-dashboard to 20241118.0 (#7782) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e11e629743..4bea8cf4ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20241025.0 +esphome-dashboard==20241118.0 aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 From 1ed27b7cc0556679344f8fbe5101f1e338cc21a0 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 19 Nov 2024 09:04:30 +1300 Subject: [PATCH 0623/1052] Bump version to 2024.11.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 51c8090e91..e7edd8337e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.0b2" +__version__ = "2024.11.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 49e9c4333979bfeae77f25ee32c3065bc610b7a5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:54:19 +1300 Subject: [PATCH 0624/1052] [http_request] Feed watchdog timeout around http request functions (#7786) --- esphome/components/http_request/http_request_arduino.cpp | 2 ++ esphome/components/http_request/http_request_idf.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index af1eb6f459..85a1312aaa 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -104,7 +104,9 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s static const size_t HEADER_COUNT = sizeof(header_keys) / sizeof(header_keys[0]); container->client_.collectHeaders(header_keys, HEADER_COUNT); + App.feed_wdt(); container->status_code = container->client_.sendRequest(method.c_str(), body.c_str()); + App.feed_wdt(); if (container->status_code < 0) { ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s", url.c_str(), HTTPClient::errorToString(container->status_code).c_str()); diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index c6c567b620..b449f046ee 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -117,8 +117,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } + App.feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); + App.feed_wdt(); container->status_code = esp_http_client_get_status_code(client); + App.feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; @@ -148,8 +151,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } + App.feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); + App.feed_wdt(); container->status_code = esp_http_client_get_status_code(client); + App.feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; From cf63d627fee4224f3652a0d101fae4cad1039da4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Nov 2024 17:39:28 +1300 Subject: [PATCH 0625/1052] Bump esphome-dashboard to 20241120.0 (#7787) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4bea8cf4ef..7bc1c895df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20241118.0 +esphome-dashboard==20241120.0 aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 From eb8a2326ad2a9f32c52c94b52fbb3dfb4090c6c2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:54:19 +1300 Subject: [PATCH 0626/1052] [http_request] Feed watchdog timeout around http request functions (#7786) --- esphome/components/http_request/http_request_arduino.cpp | 2 ++ esphome/components/http_request/http_request_idf.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index af1eb6f459..85a1312aaa 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -104,7 +104,9 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s static const size_t HEADER_COUNT = sizeof(header_keys) / sizeof(header_keys[0]); container->client_.collectHeaders(header_keys, HEADER_COUNT); + App.feed_wdt(); container->status_code = container->client_.sendRequest(method.c_str(), body.c_str()); + App.feed_wdt(); if (container->status_code < 0) { ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s", url.c_str(), HTTPClient::errorToString(container->status_code).c_str()); diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index c6c567b620..b449f046ee 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -117,8 +117,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } + App.feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); + App.feed_wdt(); container->status_code = esp_http_client_get_status_code(client); + App.feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; @@ -148,8 +151,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } + App.feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); + App.feed_wdt(); container->status_code = esp_http_client_get_status_code(client); + App.feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; From 872b8ee753ef5799894289ac941392383145d1d7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Nov 2024 17:39:28 +1300 Subject: [PATCH 0627/1052] Bump esphome-dashboard to 20241120.0 (#7787) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4bea8cf4ef..7bc1c895df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20241118.0 +esphome-dashboard==20241120.0 aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 From ae46dcef7e51eb148dd0858834b02dc855979a1b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Nov 2024 17:50:30 +1300 Subject: [PATCH 0628/1052] Bump version to 2024.11.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e7edd8337e..659695465e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.0b3" +__version__ = "2024.11.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From ef78c404dd19bb35af412a9dab0bd8e294cd7469 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 20 Nov 2024 21:29:42 +1300 Subject: [PATCH 0629/1052] Bump version to 2024.11.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 659695465e..408dc52869 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.0b4" +__version__ = "2024.11.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 372d68a177196d6efb688b9f15943c5fd05dd202 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:27:23 -0500 Subject: [PATCH 0630/1052] [remote_base] Fix extra comma in dump raw (#7774) Co-authored-by: Jonathan Swoboda --- esphome/components/remote_base/raw_protocol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/remote_base/raw_protocol.cpp b/esphome/components/remote_base/raw_protocol.cpp index bdeb935dc4..ef0cb8454e 100644 --- a/esphome/components/remote_base/raw_protocol.cpp +++ b/esphome/components/remote_base/raw_protocol.cpp @@ -28,7 +28,7 @@ bool RawDumper::dump(RemoteReceiveData src) { ESP_LOGI(TAG, "%s", buffer); buffer_offset = 0; written = sprintf(buffer, " "); - if (i + 1 < src.size()) { + if (i + 1 < src.size() - 1) { written += sprintf(buffer + written, "%" PRId32 ", ", value); } else { written += sprintf(buffer + written, "%" PRId32, value); From 846b091aacbe72078996cd0c89419a40c32cb1f8 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Wed, 20 Nov 2024 19:28:21 +0100 Subject: [PATCH 0631/1052] [nextion] New trigger `on_buffer_overflow` (#7772) --- esphome/components/nextion/automation.h | 7 +++++++ esphome/components/nextion/base_component.py | 1 + esphome/components/nextion/display.py | 15 +++++++++++++++ esphome/components/nextion/nextion.cpp | 8 +++++++- esphome/components/nextion/nextion.h | 7 +++++++ tests/components/nextion/test.esp32-ard.yaml | 3 +++ tests/components/nextion/test.esp32-c3-ard.yaml | 3 +++ tests/components/nextion/test.esp32-c3-idf.yaml | 3 +++ tests/components/nextion/test.esp32-idf.yaml | 3 +++ tests/components/nextion/test.esp8266-ard.yaml | 3 +++ tests/components/nextion/test.rp2040-ard.yaml | 3 +++ 11 files changed, 55 insertions(+), 1 deletion(-) diff --git a/esphome/components/nextion/automation.h b/esphome/components/nextion/automation.h index f51fe6b4f8..5182e07229 100644 --- a/esphome/components/nextion/automation.h +++ b/esphome/components/nextion/automation.h @@ -42,5 +42,12 @@ class TouchTrigger : public Trigger { } }; +class BufferOverflowTrigger : public Trigger<> { + public: + explicit BufferOverflowTrigger(Nextion *nextion) { + nextion->add_buffer_overflow_event_callback([this]() { this->trigger(); }); + } +}; + } // namespace nextion } // namespace esphome diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py index 2924f66d3c..9708379861 100644 --- a/esphome/components/nextion/base_component.py +++ b/esphome/components/nextion/base_component.py @@ -18,6 +18,7 @@ CONF_ON_SLEEP = "on_sleep" CONF_ON_WAKE = "on_wake" CONF_ON_SETUP = "on_setup" CONF_ON_PAGE = "on_page" +CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow" CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout" CONF_WAKE_UP_PAGE = "wake_up_page" CONF_START_UP_PAGE = "start_up_page" diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index e403ba7ae8..6f284376af 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -13,6 +13,7 @@ from esphome.const import ( from esphome.core import CORE from . import Nextion, nextion_ns, nextion_ref from .base_component import ( + CONF_ON_BUFFER_OVERFLOW, CONF_ON_SLEEP, CONF_ON_WAKE, CONF_ON_SETUP, @@ -36,6 +37,9 @@ SleepTrigger = nextion_ns.class_("SleepTrigger", automation.Trigger.template()) WakeTrigger = nextion_ns.class_("WakeTrigger", automation.Trigger.template()) PageTrigger = nextion_ns.class_("PageTrigger", automation.Trigger.template()) TouchTrigger = nextion_ns.class_("TouchTrigger", automation.Trigger.template()) +BufferOverflowTrigger = nextion_ns.class_( + "BufferOverflowTrigger", automation.Trigger.template() +) CONFIG_SCHEMA = ( display.BASIC_DISPLAY_SCHEMA.extend( @@ -68,6 +72,13 @@ CONFIG_SCHEMA = ( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TouchTrigger), } ), + cv.Optional(CONF_ON_BUFFER_OVERFLOW): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + BufferOverflowTrigger + ), + } + ), 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, @@ -151,3 +162,7 @@ async def to_code(config): ], conf, ) + + for conf in config.get(CONF_ON_BUFFER_OVERFLOW, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index a80f6efc91..984db09c57 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -190,6 +190,10 @@ void Nextion::add_touch_event_callback(std::functiontouch_callback_.add(std::move(callback)); } +void Nextion::add_buffer_overflow_event_callback(std::function &&callback) { + this->buffer_overflow_callback_.add(std::move(callback)); +} + void Nextion::update_all_components() { if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) return; @@ -458,7 +462,9 @@ void Nextion::process_nextion_commands_() { this->remove_from_q_(); break; case 0x24: // Serial Buffer overflow occurs - ESP_LOGW(TAG, "Nextion reported Serial Buffer overflow!"); + // Buffer will continue to receive the current instruction, all previous instructions are lost. + ESP_LOGE(TAG, "Nextion reported Serial Buffer overflow!"); + this->buffer_overflow_callback_.call(); break; case 0x65: { // touch event return data if (to_process_length != 3) { diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 732ee9b455..f539c79718 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1134,6 +1134,12 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ void add_touch_event_callback(std::function &&callback); + /** Add a callback to be notified when the nextion reports a buffer overflow. + * + * @param callback The void() callback. + */ + void add_buffer_overflow_event_callback(std::function &&callback); + void update_all_components(); /** @@ -1323,6 +1329,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe CallbackManager wake_callback_{}; CallbackManager page_callback_{}; CallbackManager touch_callback_{}; + CallbackManager buffer_overflow_callback_{}; optional writer_; float brightness_{1.0}; diff --git a/tests/components/nextion/test.esp32-ard.yaml b/tests/components/nextion/test.esp32-ard.yaml index 27568ebc2a..ba76236fc6 100644 --- a/tests/components/nextion/test.esp32-ard.yaml +++ b/tests/components/nextion/test.esp32-ard.yaml @@ -58,3 +58,6 @@ display: on_page: then: lambda: 'ESP_LOGD("display","Display shows new page %u", x);' + on_buffer_overflow: + then: + logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp32-c3-ard.yaml b/tests/components/nextion/test.esp32-c3-ard.yaml index 5881d6e165..5d253268f8 100644 --- a/tests/components/nextion/test.esp32-c3-ard.yaml +++ b/tests/components/nextion/test.esp32-c3-ard.yaml @@ -58,3 +58,6 @@ display: on_page: then: lambda: 'ESP_LOGD("display","Display shows new page %u", x);' + on_buffer_overflow: + then: + logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp32-c3-idf.yaml b/tests/components/nextion/test.esp32-c3-idf.yaml index 5881d6e165..5d253268f8 100644 --- a/tests/components/nextion/test.esp32-c3-idf.yaml +++ b/tests/components/nextion/test.esp32-c3-idf.yaml @@ -58,3 +58,6 @@ display: on_page: then: lambda: 'ESP_LOGD("display","Display shows new page %u", x);' + on_buffer_overflow: + then: + logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp32-idf.yaml b/tests/components/nextion/test.esp32-idf.yaml index 27568ebc2a..ba76236fc6 100644 --- a/tests/components/nextion/test.esp32-idf.yaml +++ b/tests/components/nextion/test.esp32-idf.yaml @@ -58,3 +58,6 @@ display: on_page: then: lambda: 'ESP_LOGD("display","Display shows new page %u", x);' + on_buffer_overflow: + then: + logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp8266-ard.yaml b/tests/components/nextion/test.esp8266-ard.yaml index 5881d6e165..5d253268f8 100644 --- a/tests/components/nextion/test.esp8266-ard.yaml +++ b/tests/components/nextion/test.esp8266-ard.yaml @@ -58,3 +58,6 @@ display: on_page: then: lambda: 'ESP_LOGD("display","Display shows new page %u", x);' + on_buffer_overflow: + then: + logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.rp2040-ard.yaml b/tests/components/nextion/test.rp2040-ard.yaml index a1c5848ce6..9b04433095 100644 --- a/tests/components/nextion/test.rp2040-ard.yaml +++ b/tests/components/nextion/test.rp2040-ard.yaml @@ -53,3 +53,6 @@ display: on_page: then: lambda: 'ESP_LOGD("display","Display shows new page %u", x);' + on_buffer_overflow: + then: + logger.log: "Nextion reported a buffer overflow!" From 5e27a8df1f5fc3978939521d421d6330d1296128 Mon Sep 17 00:00:00 2001 From: Kjell Braden Date: Wed, 20 Nov 2024 19:29:48 +0100 Subject: [PATCH 0632/1052] enable rp2040 for online_image (#7769) --- esphome/components/online_image/__init__.py | 1 + .../online_image/common-rp2040.yaml | 19 +++++++++++++++++++ .../online_image/test.rp2040-ard.yaml | 4 ++++ 3 files changed, 24 insertions(+) create mode 100644 tests/components/online_image/common-rp2040.yaml create mode 100644 tests/components/online_image/test.rp2040-ard.yaml diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index dfb10137aa..be1bfb4a00 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -98,6 +98,7 @@ CONFIG_SCHEMA = cv.Schema( # esp8266_arduino=cv.Version(2, 7, 0), esp32_arduino=cv.Version(0, 0, 0), esp_idf=cv.Version(4, 0, 0), + rp2040_arduino=cv.Version(0, 0, 0), ), ) ) diff --git a/tests/components/online_image/common-rp2040.yaml b/tests/components/online_image/common-rp2040.yaml new file mode 100644 index 0000000000..16bb2b2c44 --- /dev/null +++ b/tests/components/online_image/common-rp2040.yaml @@ -0,0 +1,19 @@ +<<: !include common.yaml + +spi: + - id: spi_main_lcd + clk_pin: 18 + mosi_pin: 19 + miso_pin: 16 + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: 20 + dc_pin: 17 + reset_pin: 21 + invert_colors: true + lambda: |- + it.fill(Color(0, 0, 0)); + it.image(0, 0, id(online_rgba_image)); diff --git a/tests/components/online_image/test.rp2040-ard.yaml b/tests/components/online_image/test.rp2040-ard.yaml new file mode 100644 index 0000000000..d10f36b4e9 --- /dev/null +++ b/tests/components/online_image/test.rp2040-ard.yaml @@ -0,0 +1,4 @@ +<<: !include common-rp2040.yaml + +http_request: + verify_ssl: false From 6d4f787f67b6d4dc16fcdbdca3a1984db8d9df62 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:10:28 +1100 Subject: [PATCH 0633/1052] [http_request] Fix within context with parameters. (Bugfix) (#7790) --- esphome/components/http_request/http_request.h | 2 +- tests/components/http_request/common.yaml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 4ed2c834f8..b2ce718ec4 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -189,7 +189,7 @@ template class HttpRequestSendAction : public Action { if (container == nullptr) { for (auto *trigger : this->error_triggers_) - trigger->trigger(x...); + trigger->trigger(); return; } diff --git a/tests/components/http_request/common.yaml b/tests/components/http_request/common.yaml index 593b85e435..8408f27a05 100644 --- a/tests/components/http_request/common.yaml +++ b/tests/components/http_request/common.yaml @@ -39,6 +39,14 @@ http_request: timeout: 10s verify_ssl: ${verify_ssl} +script: + - id: does_not_compile + parameters: + api_url: string + then: + - http_request.get: + url: "http://google.com" + ota: - platform: http_request on_begin: From fbb9967117d248951906b0bb19ef200e7f546360 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:22:02 +1300 Subject: [PATCH 0634/1052] [rtttl] Clamp gain between 0 and 1 (#7793) --- esphome/components/rtttl/rtttl.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index 10c290c5fb..420948bfbf 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -40,13 +40,7 @@ class Rtttl : public Component { void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; } #endif float get_gain() { return gain_; } - void set_gain(float gain) { - if (gain < 0.1f) - gain = 0.1f; - if (gain > 1.0f) - gain = 1.0f; - this->gain_ = gain; - } + void set_gain(float gain) { this->gain_ = clamp(gain, 0.0f, 1.0f); } void play(std::string rtttl); void stop(); void dump_config() override; From 6bcbbcce02ca9441cffe6d7f559eceb92ab76811 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:10:20 +1300 Subject: [PATCH 0635/1052] [speaker] Add missing auto-load for ``audio`` (#7794) --- esphome/components/speaker/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index 7a668dc2f3..948fe4b534 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -7,6 +7,7 @@ from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME from esphome.core import CORE from esphome.coroutine import coroutine_with_priority +AUTO_LOAD = ["audio"] CODEOWNERS = ["@jesserockz", "@kahrendt"] IS_PLATFORM_COMPONENT = True From 03ae6b2c1b6e4ae2a509c109c2381214e0cdb131 Mon Sep 17 00:00:00 2001 From: Manuel Kasper Date: Thu, 21 Nov 2024 10:46:49 +0100 Subject: [PATCH 0636/1052] [qspi_dbi] Fix garbled graphics on RM690B0 (#7795) --- esphome/components/qspi_dbi/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py index 071ea72d73..cbd9c4663f 100644 --- a/esphome/components/qspi_dbi/models.py +++ b/esphome/components/qspi_dbi/models.py @@ -55,6 +55,7 @@ chip.cmd(PAGESEL, 0x00) chip.cmd(0xC2, 0x00) chip.delay(10) chip.cmd(TEON, 0x00) +chip.cmd(PIXFMT, 0x55) chip = DriverChip("AXS15231") chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) From ccf2854b612a1659c27cedeb22eaf49b871f157c Mon Sep 17 00:00:00 2001 From: Spencer Owen Date: Thu, 21 Nov 2024 12:24:10 -0700 Subject: [PATCH 0637/1052] Check for min_version earlier in validation (#7797) --- esphome/core/config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index 8c130eb6db..367e61c413 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -184,6 +184,9 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): cv.valid, cv.Optional(CONF_BOARD_FLASH_MODE): cv.valid, cv.Optional(CONF_ARDUINO_VERSION): cv.valid, + cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All( + cv.version_number, cv.validate_esphome_version + ), }, extra=cv.ALLOW_EXTRA, ) From 3232866dc337b0e3332f9b7283d834f025e2a642 Mon Sep 17 00:00:00 2001 From: Alain Turbide <7193213+Dilbert66@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:39:32 -0500 Subject: [PATCH 0638/1052] Fix for OTA mode not activating in safe_mode when OTA section has an on_xxxx action (#7796) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esphome/ota/__init__.py | 8 ++++---- esphome/components/ota/__init__.py | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index a852d8d001..86006e3e18 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -1,10 +1,9 @@ import logging import esphome.codegen as cg -import esphome.config_validation as cv -import esphome.final_validate as fv -from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code, OTAComponent +from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code from esphome.config_helpers import merge_config +import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, CONF_ID, @@ -18,6 +17,7 @@ from esphome.const import ( CONF_VERSION, ) from esphome.core import coroutine_with_priority +import esphome.final_validate as fv _LOGGER = logging.getLogger(__name__) @@ -124,7 +124,6 @@ FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate @coroutine_with_priority(52.0) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await ota_to_code(var, config) cg.add(var.set_port(config[CONF_PORT])) if CONF_PASSWORD in config: cg.add(var.set_auth_password(config[CONF_PASSWORD])) @@ -132,3 +131,4 @@ async def to_code(config): cg.add_define("USE_OTA_VERSION", config[CONF_VERSION]) await cg.register_component(var, config) + await ota_to_code(var, config) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index d9917a2aae..627c55e910 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -92,6 +92,7 @@ async def to_code(config): async def ota_to_code(var, config): + await cg.past_safe_mode() use_state_callback = False for conf in config.get(CONF_ON_STATE_CHANGE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) From 122ff731ef43206212976da35354494e928639f6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 Nov 2024 14:41:31 -0600 Subject: [PATCH 0639/1052] Ensure storage I/O for ignored devices runs in the executor (#7792) --- esphome/dashboard/core.py | 2 +- esphome/dashboard/web_server.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/dashboard/core.py b/esphome/dashboard/core.py index 563ca1506d..f53cb7ffb1 100644 --- a/esphome/dashboard/core.py +++ b/esphome/dashboard/core.py @@ -103,7 +103,7 @@ class ESPHomeDashboard: self.loop = asyncio.get_running_loop() self.ping_request = asyncio.Event() self.entries = DashboardEntries(self) - self.load_ignored_devices() + await self.loop.run_in_executor(None, self.load_ignored_devices) def load_ignored_devices(self) -> None: storage_path = Path(ignored_devices_storage_path()) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 07f7f019f8..0fed8e9c53 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -544,7 +544,7 @@ class ImportRequestHandler(BaseHandler): class IgnoreDeviceRequestHandler(BaseHandler): @authenticated - def post(self) -> None: + async def post(self) -> None: dashboard = DASHBOARD try: args = json.loads(self.request.body.decode()) @@ -576,7 +576,8 @@ class IgnoreDeviceRequestHandler(BaseHandler): else: dashboard.ignored_devices.discard(ignored_device.device_name) - dashboard.save_ignored_devices() + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, dashboard.save_ignored_devices) self.set_status(204) self.finish() From 888b2379647049712a94143074b95c79e1f8392b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:10:28 +1100 Subject: [PATCH 0640/1052] [http_request] Fix within context with parameters. (Bugfix) (#7790) --- esphome/components/http_request/http_request.h | 2 +- tests/components/http_request/common.yaml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 4ed2c834f8..b2ce718ec4 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -189,7 +189,7 @@ template class HttpRequestSendAction : public Action { if (container == nullptr) { for (auto *trigger : this->error_triggers_) - trigger->trigger(x...); + trigger->trigger(); return; } diff --git a/tests/components/http_request/common.yaml b/tests/components/http_request/common.yaml index 593b85e435..8408f27a05 100644 --- a/tests/components/http_request/common.yaml +++ b/tests/components/http_request/common.yaml @@ -39,6 +39,14 @@ http_request: timeout: 10s verify_ssl: ${verify_ssl} +script: + - id: does_not_compile + parameters: + api_url: string + then: + - http_request.get: + url: "http://google.com" + ota: - platform: http_request on_begin: From a0693060e401153409a2095b0a29248bd23dee0e Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:22:02 +1300 Subject: [PATCH 0641/1052] [rtttl] Clamp gain between 0 and 1 (#7793) --- esphome/components/rtttl/rtttl.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index 10c290c5fb..420948bfbf 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -40,13 +40,7 @@ class Rtttl : public Component { void set_speaker(speaker::Speaker *speaker) { this->speaker_ = speaker; } #endif float get_gain() { return gain_; } - void set_gain(float gain) { - if (gain < 0.1f) - gain = 0.1f; - if (gain > 1.0f) - gain = 1.0f; - this->gain_ = gain; - } + void set_gain(float gain) { this->gain_ = clamp(gain, 0.0f, 1.0f); } void play(std::string rtttl); void stop(); void dump_config() override; From f04e3de7b8d103a21cbe06f1b5c78e7a78be3429 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:10:20 +1300 Subject: [PATCH 0642/1052] [speaker] Add missing auto-load for ``audio`` (#7794) --- esphome/components/speaker/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index 7a668dc2f3..948fe4b534 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -7,6 +7,7 @@ from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME from esphome.core import CORE from esphome.coroutine import coroutine_with_priority +AUTO_LOAD = ["audio"] CODEOWNERS = ["@jesserockz", "@kahrendt"] IS_PLATFORM_COMPONENT = True From 489d0d20d2279257574038ab798755242441c18b Mon Sep 17 00:00:00 2001 From: Manuel Kasper Date: Thu, 21 Nov 2024 10:46:49 +0100 Subject: [PATCH 0643/1052] [qspi_dbi] Fix garbled graphics on RM690B0 (#7795) --- esphome/components/qspi_dbi/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py index 071ea72d73..cbd9c4663f 100644 --- a/esphome/components/qspi_dbi/models.py +++ b/esphome/components/qspi_dbi/models.py @@ -55,6 +55,7 @@ chip.cmd(PAGESEL, 0x00) chip.cmd(0xC2, 0x00) chip.delay(10) chip.cmd(TEON, 0x00) +chip.cmd(PIXFMT, 0x55) chip = DriverChip("AXS15231") chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) From ea424b069979e5c53438f95abdd43acdc521cd78 Mon Sep 17 00:00:00 2001 From: Spencer Owen Date: Thu, 21 Nov 2024 12:24:10 -0700 Subject: [PATCH 0644/1052] Check for min_version earlier in validation (#7797) --- esphome/core/config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index 8c130eb6db..367e61c413 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -184,6 +184,9 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): cv.valid, cv.Optional(CONF_BOARD_FLASH_MODE): cv.valid, cv.Optional(CONF_ARDUINO_VERSION): cv.valid, + cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All( + cv.version_number, cv.validate_esphome_version + ), }, extra=cv.ALLOW_EXTRA, ) From 1c1f3f7c55607c504226f7a1ed52794a0482b123 Mon Sep 17 00:00:00 2001 From: Alain Turbide <7193213+Dilbert66@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:39:32 -0500 Subject: [PATCH 0645/1052] Fix for OTA mode not activating in safe_mode when OTA section has an on_xxxx action (#7796) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esphome/ota/__init__.py | 8 ++++---- esphome/components/ota/__init__.py | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index a852d8d001..86006e3e18 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -1,10 +1,9 @@ import logging import esphome.codegen as cg -import esphome.config_validation as cv -import esphome.final_validate as fv -from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code, OTAComponent +from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code from esphome.config_helpers import merge_config +import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, CONF_ID, @@ -18,6 +17,7 @@ from esphome.const import ( CONF_VERSION, ) from esphome.core import coroutine_with_priority +import esphome.final_validate as fv _LOGGER = logging.getLogger(__name__) @@ -124,7 +124,6 @@ FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate @coroutine_with_priority(52.0) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) - await ota_to_code(var, config) cg.add(var.set_port(config[CONF_PORT])) if CONF_PASSWORD in config: cg.add(var.set_auth_password(config[CONF_PASSWORD])) @@ -132,3 +131,4 @@ async def to_code(config): cg.add_define("USE_OTA_VERSION", config[CONF_VERSION]) await cg.register_component(var, config) + await ota_to_code(var, config) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index d9917a2aae..627c55e910 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -92,6 +92,7 @@ async def to_code(config): async def ota_to_code(var, config): + await cg.past_safe_mode() use_state_callback = False for conf in config.get(CONF_ON_STATE_CHANGE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) From e51f3d9498d377ed45d15665e1c91f91d6a2aae0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 21 Nov 2024 14:41:31 -0600 Subject: [PATCH 0646/1052] Ensure storage I/O for ignored devices runs in the executor (#7792) --- esphome/dashboard/core.py | 2 +- esphome/dashboard/web_server.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/dashboard/core.py b/esphome/dashboard/core.py index 563ca1506d..f53cb7ffb1 100644 --- a/esphome/dashboard/core.py +++ b/esphome/dashboard/core.py @@ -103,7 +103,7 @@ class ESPHomeDashboard: self.loop = asyncio.get_running_loop() self.ping_request = asyncio.Event() self.entries = DashboardEntries(self) - self.load_ignored_devices() + await self.loop.run_in_executor(None, self.load_ignored_devices) def load_ignored_devices(self) -> None: storage_path = Path(ignored_devices_storage_path()) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 07f7f019f8..0fed8e9c53 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -544,7 +544,7 @@ class ImportRequestHandler(BaseHandler): class IgnoreDeviceRequestHandler(BaseHandler): @authenticated - def post(self) -> None: + async def post(self) -> None: dashboard = DASHBOARD try: args = json.loads(self.request.body.decode()) @@ -576,7 +576,8 @@ class IgnoreDeviceRequestHandler(BaseHandler): else: dashboard.ignored_devices.discard(ignored_device.device_name) - dashboard.save_ignored_devices() + loop = asyncio.get_running_loop() + await loop.run_in_executor(None, dashboard.save_ignored_devices) self.set_status(204) self.finish() From 2cc2a2153b2c219d1bd07a9fe427b16d38e2e6f7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:08:00 +1300 Subject: [PATCH 0647/1052] Bump version to 2024.11.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 408dc52869..d14cdecb23 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.0" +__version__ = "2024.11.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From dea297c8d7d73f0c4f366e58994cd6a9a3995e11 Mon Sep 17 00:00:00 2001 From: Petr Kejval Date: Sat, 23 Nov 2024 05:52:02 +0100 Subject: [PATCH 0648/1052] [nextion] Add publish actions (#7646) Co-authored-by: Keith Burzinski --- esphome/components/nextion/__init__.py | 2 + esphome/components/nextion/automation.h | 75 ++++- .../nextion/binary_sensor/__init__.py | 45 ++- esphome/components/nextion/sensor/__init__.py | 43 ++- esphome/components/nextion/switch/__init__.py | 40 ++- .../nextion/text_sensor/__init__.py | 39 ++- tests/components/nextion/common.yaml | 293 ++++++++++++++++++ tests/components/nextion/test.esp32-ard.yaml | 65 +--- .../components/nextion/test.esp32-c3-ard.yaml | 65 +--- .../components/nextion/test.esp32-c3-idf.yaml | 65 +--- tests/components/nextion/test.esp32-idf.yaml | 65 +--- .../components/nextion/test.esp8266-ard.yaml | 65 +--- tests/components/nextion/test.rp2040-ard.yaml | 61 +--- 13 files changed, 558 insertions(+), 365 deletions(-) create mode 100644 tests/components/nextion/common.yaml diff --git a/esphome/components/nextion/__init__.py b/esphome/components/nextion/__init__.py index 924d58198d..fb75daf4ba 100644 --- a/esphome/components/nextion/__init__.py +++ b/esphome/components/nextion/__init__.py @@ -6,3 +6,5 @@ Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice) nextion_ref = Nextion.operator("ref") CONF_NEXTION_ID = "nextion_id" +CONF_PUBLISH_STATE = "publish_state" +CONF_SEND_TO_NEXTION = "send_to_nextion" diff --git a/esphome/components/nextion/automation.h b/esphome/components/nextion/automation.h index 5182e07229..65f1fd0058 100644 --- a/esphome/components/nextion/automation.h +++ b/esphome/components/nextion/automation.h @@ -5,6 +5,13 @@ namespace esphome { namespace nextion { +class BufferOverflowTrigger : public Trigger<> { + public: + explicit BufferOverflowTrigger(Nextion *nextion) { + nextion->add_buffer_overflow_event_callback([this]() { this->trigger(); }); + } +}; + class SetupTrigger : public Trigger<> { public: explicit SetupTrigger(Nextion *nextion) { @@ -42,11 +49,73 @@ class TouchTrigger : public Trigger { } }; -class BufferOverflowTrigger : public Trigger<> { +template class NextionPublishFloatAction : public Action { public: - explicit BufferOverflowTrigger(Nextion *nextion) { - nextion->add_buffer_overflow_event_callback([this]() { this->trigger(); }); + explicit NextionPublishFloatAction(NextionComponent *component) : component_(component) {} + + TEMPLATABLE_VALUE(float, state) + TEMPLATABLE_VALUE(bool, publish_state) + TEMPLATABLE_VALUE(bool, send_to_nextion) + + void play(Ts... x) override { + this->component_->set_state(this->state_.value(x...), this->publish_state_.value(x...), + this->send_to_nextion_.value(x...)); } + + void set_state(std::function state) { this->state_ = state; } + void set_publish_state(std::function publish_state) { this->publish_state_ = publish_state; } + void set_send_to_nextion(std::function send_to_nextion) { + this->send_to_nextion_ = send_to_nextion; + } + + protected: + NextionComponent *component_; +}; + +template class NextionPublishTextAction : public Action { + public: + explicit NextionPublishTextAction(NextionComponent *component) : component_(component) {} + + TEMPLATABLE_VALUE(const char *, state) + TEMPLATABLE_VALUE(bool, publish_state) + TEMPLATABLE_VALUE(bool, send_to_nextion) + + void play(Ts... x) override { + this->component_->set_state(this->state_.value(x...), this->publish_state_.value(x...), + this->send_to_nextion_.value(x...)); + } + + void set_state(std::function state) { this->state_ = state; } + void set_publish_state(std::function publish_state) { this->publish_state_ = publish_state; } + void set_send_to_nextion(std::function send_to_nextion) { + this->send_to_nextion_ = send_to_nextion; + } + + protected: + NextionComponent *component_; +}; + +template class NextionPublishBoolAction : public Action { + public: + explicit NextionPublishBoolAction(NextionComponent *component) : component_(component) {} + + TEMPLATABLE_VALUE(bool, state) + TEMPLATABLE_VALUE(bool, publish_state) + TEMPLATABLE_VALUE(bool, send_to_nextion) + + void play(Ts... x) override { + this->component_->set_state(this->state_.value(x...), this->publish_state_.value(x...), + this->send_to_nextion_.value(x...)); + } + + void set_state(std::function state) { this->state_ = state; } + void set_publish_state(std::function publish_state) { this->publish_state_ = publish_state; } + void set_send_to_nextion(std::function send_to_nextion) { + this->send_to_nextion_ = send_to_nextion; + } + + protected: + NextionComponent *component_; }; } // namespace nextion diff --git a/esphome/components/nextion/binary_sensor/__init__.py b/esphome/components/nextion/binary_sensor/__init__.py index 8b4a45cc60..a257587e13 100644 --- a/esphome/components/nextion/binary_sensor/__init__.py +++ b/esphome/components/nextion/binary_sensor/__init__.py @@ -1,9 +1,16 @@ +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor -from esphome.const import CONF_COMPONENT_ID, CONF_PAGE_ID, CONF_ID -from .. import nextion_ns, CONF_NEXTION_ID +from esphome.const import ( + CONF_ID, + CONF_STATE, + CONF_COMPONENT_ID, + CONF_PAGE_ID, +) + +from .. import nextion_ns, CONF_NEXTION_ID, CONF_PUBLISH_STATE, CONF_SEND_TO_NEXTION from ..base_component import ( @@ -19,6 +26,10 @@ NextionBinarySensor = nextion_ns.class_( "NextionBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent ) +NextionPublishBoolAction = nextion_ns.class_( + "NextionPublishBoolAction", automation.Action +) + CONFIG_SCHEMA = cv.All( binary_sensor.binary_sensor_schema(NextionBinarySensor) .extend( @@ -52,3 +63,33 @@ async def to_code(config): if CONF_COMPONENT_NAME in config or CONF_VARIABLE_NAME in config: await setup_component_core_(var, config, ".val") cg.add(hub.register_binarysensor_component(var)) + + +@automation.register_action( + "binary_sensor.nextion.publish", + NextionPublishBoolAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(NextionBinarySensor), + cv.Required(CONF_STATE): cv.templatable(cv.boolean), + cv.Optional(CONF_PUBLISH_STATE, default="true"): cv.templatable(cv.boolean), + cv.Optional(CONF_SEND_TO_NEXTION, default="true"): cv.templatable( + cv.boolean + ), + } + ), +) +async def sensor_nextion_publish_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config[CONF_STATE], args, bool) + cg.add(var.set_state(template_)) + + template_ = await cg.templatable(config[CONF_PUBLISH_STATE], args, bool) + cg.add(var.set_publish_state(template_)) + + template_ = await cg.templatable(config[CONF_SEND_TO_NEXTION], args, bool) + cg.add(var.set_send_to_nextion(template_)) + + return var diff --git a/esphome/components/nextion/sensor/__init__.py b/esphome/components/nextion/sensor/__init__.py index eefbe34d58..1058c2a04b 100644 --- a/esphome/components/nextion/sensor/__init__.py +++ b/esphome/components/nextion/sensor/__init__.py @@ -1,12 +1,11 @@ +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor -from esphome.const import ( - CONF_ID, - CONF_COMPONENT_ID, -) -from .. import nextion_ns, CONF_NEXTION_ID +from esphome.const import CONF_ID, CONF_COMPONENT_ID, CONF_STATE + +from .. import nextion_ns, CONF_NEXTION_ID, CONF_PUBLISH_STATE, CONF_SEND_TO_NEXTION from ..base_component import ( setup_component_core_, @@ -25,6 +24,10 @@ CODEOWNERS = ["@senexcrenshaw"] NextionSensor = nextion_ns.class_("NextionSensor", sensor.Sensor, cg.PollingComponent) +NextionPublishFloatAction = nextion_ns.class_( + "NextionPublishFloatAction", automation.Action +) + def CheckWaveID(value): value = cv.int_(value) @@ -95,3 +98,33 @@ async def to_code(config): if CONF_WAVE_MAX_LENGTH in config: cg.add(var.set_wave_max_length(config[CONF_WAVE_MAX_LENGTH])) + + +@automation.register_action( + "sensor.nextion.publish", + NextionPublishFloatAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(NextionSensor), + cv.Required(CONF_STATE): cv.templatable(cv.float_), + cv.Optional(CONF_PUBLISH_STATE, default="true"): cv.templatable(cv.boolean), + cv.Optional(CONF_SEND_TO_NEXTION, default="true"): cv.templatable( + cv.boolean + ), + } + ), +) +async def sensor_nextion_publish_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config[CONF_STATE], args, float) + cg.add(var.set_state(template_)) + + template_ = await cg.templatable(config[CONF_PUBLISH_STATE], args, bool) + cg.add(var.set_publish_state(template_)) + + template_ = await cg.templatable(config[CONF_SEND_TO_NEXTION], args, bool) + cg.add(var.set_send_to_nextion(template_)) + + return var diff --git a/esphome/components/nextion/switch/__init__.py b/esphome/components/nextion/switch/__init__.py index 91ab0cc81f..de1a061478 100644 --- a/esphome/components/nextion/switch/__init__.py +++ b/esphome/components/nextion/switch/__init__.py @@ -1,9 +1,11 @@ +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch -from esphome.const import CONF_ID -from .. import nextion_ns, CONF_NEXTION_ID +from esphome.const import CONF_ID, CONF_STATE + +from .. import nextion_ns, CONF_NEXTION_ID, CONF_PUBLISH_STATE, CONF_SEND_TO_NEXTION from ..base_component import ( setup_component_core_, @@ -16,6 +18,10 @@ CODEOWNERS = ["@senexcrenshaw"] NextionSwitch = nextion_ns.class_("NextionSwitch", switch.Switch, cg.PollingComponent) +NextionPublishBoolAction = nextion_ns.class_( + "NextionPublishBoolAction", automation.Action +) + CONFIG_SCHEMA = cv.All( switch.switch_schema(NextionSwitch) .extend(CONFIG_SWITCH_COMPONENT_SCHEMA) @@ -33,3 +39,33 @@ async def to_code(config): cg.add(hub.register_switch_component(var)) await setup_component_core_(var, config, ".val") + + +@automation.register_action( + "switch.nextion.publish", + NextionPublishBoolAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(NextionSwitch), + cv.Required(CONF_STATE): cv.templatable(cv.boolean), + cv.Optional(CONF_PUBLISH_STATE, default="true"): cv.templatable(cv.boolean), + cv.Optional(CONF_SEND_TO_NEXTION, default="true"): cv.templatable( + cv.boolean + ), + } + ), +) +async def sensor_nextion_publish_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config[CONF_STATE], args, bool) + cg.add(var.set_state(template_)) + + template_ = await cg.templatable(config[CONF_PUBLISH_STATE], args, bool) + cg.add(var.set_publish_state(template_)) + + template_ = await cg.templatable(config[CONF_SEND_TO_NEXTION], args, bool) + cg.add(var.set_send_to_nextion(template_)) + + return var diff --git a/esphome/components/nextion/text_sensor/__init__.py b/esphome/components/nextion/text_sensor/__init__.py index 826ff2354e..793397b1f4 100644 --- a/esphome/components/nextion/text_sensor/__init__.py +++ b/esphome/components/nextion/text_sensor/__init__.py @@ -1,9 +1,10 @@ +from esphome import automation from esphome.components import text_sensor import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_ID +from esphome.const import CONF_ID, CONF_STATE -from .. import nextion_ns, CONF_NEXTION_ID +from .. import nextion_ns, CONF_NEXTION_ID, CONF_PUBLISH_STATE, CONF_SEND_TO_NEXTION from ..base_component import ( setup_component_core_, @@ -16,6 +17,10 @@ NextionTextSensor = nextion_ns.class_( "NextionTextSensor", text_sensor.TextSensor, cg.PollingComponent ) +NextionPublishTextAction = nextion_ns.class_( + "NextionPublishTextAction", automation.Action +) + CONFIG_SCHEMA = ( text_sensor.text_sensor_schema(NextionTextSensor) .extend(CONFIG_TEXT_COMPONENT_SCHEMA) @@ -32,3 +37,33 @@ async def to_code(config): cg.add(hub.register_textsensor_component(var)) await setup_component_core_(var, config, ".txt") + + +@automation.register_action( + "text_sensor.nextion.publish", + NextionPublishTextAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(NextionTextSensor), + cv.Required(CONF_STATE): cv.templatable(cv.string_strict), + cv.Optional(CONF_PUBLISH_STATE, default="true"): cv.templatable(cv.boolean), + cv.Optional(CONF_SEND_TO_NEXTION, default="true"): cv.templatable( + cv.boolean + ), + } + ), +) +async def sensor_nextion_publish_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config[CONF_STATE], args, cg.const_char_ptr) + cg.add(var.set_state(template_)) + + template_ = await cg.templatable(config[CONF_PUBLISH_STATE], args, cg.bool_) + cg.add(var.set_publish_state(template_)) + + template_ = await cg.templatable(config[CONF_SEND_TO_NEXTION], args, cg.bool_) + cg.add(var.set_send_to_nextion(template_)) + + return var diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml new file mode 100644 index 0000000000..e84cd08422 --- /dev/null +++ b/tests/components/nextion/common.yaml @@ -0,0 +1,293 @@ +esphome: + on_boot: + # Binary sensor publish action tests + - binary_sensor.nextion.publish: + id: r0_sensor + state: True + + - binary_sensor.nextion.publish: + id: r0_sensor + state: True + publish_state: True + send_to_nextion: True + + - binary_sensor.nextion.publish: + id: r0_sensor + state: True + publish_state: False + send_to_nextion: True + + - binary_sensor.nextion.publish: + id: r0_sensor + state: True + publish_state: True + send_to_nextion: False + + - binary_sensor.nextion.publish: + id: r0_sensor + state: True + publish_state: False + send_to_nextion: False + + # Templated + - binary_sensor.nextion.publish: + id: r0_sensor + state: !lambda 'return true;' + + - binary_sensor.nextion.publish: + id: r0_sensor + state: !lambda 'return true;' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return true;' + + - binary_sensor.nextion.publish: + id: r0_sensor + state: !lambda 'return true;' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return true;' + + - binary_sensor.nextion.publish: + id: r0_sensor + state: !lambda 'return true;' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return false;' + + - binary_sensor.nextion.publish: + id: r0_sensor + state: !lambda 'return true;' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return false;' + + # Sensor publish action tests + - sensor.nextion.publish: + id: testnumber + state: 42.0 + + - sensor.nextion.publish: + id: testnumber + state: 42.0 + publish_state: True + send_to_nextion: True + + - sensor.nextion.publish: + id: testnumber + state: 42.0 + publish_state: False + send_to_nextion: True + + - sensor.nextion.publish: + id: testnumber + state: 42.0 + publish_state: True + send_to_nextion: False + + - sensor.nextion.publish: + id: testnumber + state: 42.0 + publish_state: False + send_to_nextion: False + + # Templated + - sensor.nextion.publish: + id: testnumber + state: !lambda 'return 42.0;' + + - sensor.nextion.publish: + id: testnumber + state: !lambda 'return 42.0;' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return true;' + + - sensor.nextion.publish: + id: testnumber + state: !lambda 'return 42.0;' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return true;' + + - sensor.nextion.publish: + id: testnumber + state: !lambda 'return 42.0;' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return false;' + + - sensor.nextion.publish: + id: testnumber + state: !lambda 'return 42.0;' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return false;' + + # Switch publish action tests + - switch.nextion.publish: + id: r0 + state: True + + - switch.nextion.publish: + id: r0 + state: True + publish_state: true + send_to_nextion: true + + - switch.nextion.publish: + id: r0 + state: True + publish_state: false + send_to_nextion: true + + - switch.nextion.publish: + id: r0 + state: True + publish_state: true + send_to_nextion: false + + - switch.nextion.publish: + id: r0 + state: True + publish_state: false + send_to_nextion: false + + # Templated + - switch.nextion.publish: + id: r0 + state: !lambda 'return true;' + + - switch.nextion.publish: + id: r0 + state: !lambda 'return true;' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return true;' + + - switch.nextion.publish: + id: r0 + state: !lambda 'return true;' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return true;' + + - switch.nextion.publish: + id: r0 + state: !lambda 'return true;' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return false;' + + - switch.nextion.publish: + id: r0 + state: !lambda 'return true;' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return false;' + + # Test sensor publish action tests + - text_sensor.nextion.publish: + id: text0 + state: 'Test' + publish_state: true + send_to_nextion: true + + - text_sensor.nextion.publish: + id: text0 + state: 'Test' + publish_state: false + send_to_nextion: true + + - text_sensor.nextion.publish: + id: text0 + state: 'Test' + publish_state: true + send_to_nextion: false + + - text_sensor.nextion.publish: + id: text0 + state: 'Test' + publish_state: false + send_to_nextion: false + + # Templated + - text_sensor.nextion.publish: + id: text0 + state: !lambda 'return "Test";' + + - text_sensor.nextion.publish: + id: text0 + state: !lambda 'return "Test";' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return true;' + + - text_sensor.nextion.publish: + id: text0 + state: !lambda 'return "Test";' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return true;' + + - text_sensor.nextion.publish: + id: text0 + state: !lambda 'return "Test";' + publish_state: !lambda 'return true;' + send_to_nextion: !lambda 'return false;' + + - text_sensor.nextion.publish: + id: text0 + state: !lambda 'return "Test";' + publish_state: !lambda 'return false;' + send_to_nextion: !lambda 'return false;' + +wifi: + ssid: MySSID + password: password1 + +uart: + - id: uart_nextion + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +binary_sensor: + - platform: nextion + page_id: 0 + component_id: 2 + name: Nextion Touch Component + - platform: nextion + id: r0_sensor + name: R0 Sensor + component_name: page0.r0 + +sensor: + - platform: nextion + id: testnumber + name: testnumber + variable_name: testnumber + - platform: nextion + id: testwave + name: testwave + component_id: 2 + wave_channel_id: 1 + +switch: + - platform: nextion + id: r0 + name: R0 Switch + component_name: page0.r0 + +text_sensor: + - platform: nextion + name: text0 + id: text0 + update_interval: 4s + component_name: text0 + +display: + - platform: nextion + id: main_lcd + update_interval: 5s + on_sleep: + then: + lambda: 'ESP_LOGD("display","Display went to sleep");' + on_wake: + then: + lambda: 'ESP_LOGD("display","Display woke up");' + on_setup: + then: + lambda: 'ESP_LOGD("display","Display setup completed");' + on_page: + then: + lambda: 'ESP_LOGD("display","Display shows new page %u", x);' + on_buffer_overflow: + then: + logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp32-ard.yaml b/tests/components/nextion/test.esp32-ard.yaml index ba76236fc6..d5e02b8b85 100644 --- a/tests/components/nextion/test.esp32-ard.yaml +++ b/tests/components/nextion/test.esp32-ard.yaml @@ -1,63 +1,10 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_nextion - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 - -binary_sensor: - - platform: nextion - page_id: 0 - component_id: 2 - name: Nextion Touch Component - - platform: nextion - id: r0_sensor - name: R0 Sensor - component_name: page0.r0 - -sensor: - - platform: nextion - id: testnumber - name: testnumber - variable_name: testnumber - - platform: nextion - id: testwave - name: testwave - component_id: 2 - wave_channel_id: 1 - -switch: - - platform: nextion - id: r0 - name: R0 Switch - component_name: page0.r0 - -text_sensor: - - platform: nextion - name: text0 - id: text0 - update_interval: 4s - component_name: text0 +packages: + base: !include common.yaml display: - - platform: nextion + - id: !extend main_lcd tft_url: http://esphome.io/default35.tft - update_interval: 5s - on_sleep: - then: - lambda: 'ESP_LOGD("display","Display went to sleep");' - on_wake: - then: - lambda: 'ESP_LOGD("display","Display woke up");' - on_setup: - then: - lambda: 'ESP_LOGD("display","Display setup completed");' - on_page: - then: - lambda: 'ESP_LOGD("display","Display shows new page %u", x);' - on_buffer_overflow: - then: - logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp32-c3-ard.yaml b/tests/components/nextion/test.esp32-c3-ard.yaml index 5d253268f8..5135c7e4f4 100644 --- a/tests/components/nextion/test.esp32-c3-ard.yaml +++ b/tests/components/nextion/test.esp32-c3-ard.yaml @@ -1,63 +1,10 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_nextion - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -binary_sensor: - - platform: nextion - page_id: 0 - component_id: 2 - name: Nextion Touch Component - - platform: nextion - id: r0_sensor - name: R0 Sensor - component_name: page0.r0 - -sensor: - - platform: nextion - id: testnumber - name: testnumber - variable_name: testnumber - - platform: nextion - id: testwave - name: testwave - component_id: 2 - wave_channel_id: 1 - -switch: - - platform: nextion - id: r0 - name: R0 Switch - component_name: page0.r0 - -text_sensor: - - platform: nextion - name: text0 - id: text0 - update_interval: 4s - component_name: text0 +packages: + base: !include common.yaml display: - - platform: nextion + - id: !extend main_lcd tft_url: http://esphome.io/default35.tft - update_interval: 5s - on_sleep: - then: - lambda: 'ESP_LOGD("display","Display went to sleep");' - on_wake: - then: - lambda: 'ESP_LOGD("display","Display woke up");' - on_setup: - then: - lambda: 'ESP_LOGD("display","Display setup completed");' - on_page: - then: - lambda: 'ESP_LOGD("display","Display shows new page %u", x);' - on_buffer_overflow: - then: - logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp32-c3-idf.yaml b/tests/components/nextion/test.esp32-c3-idf.yaml index 5d253268f8..5135c7e4f4 100644 --- a/tests/components/nextion/test.esp32-c3-idf.yaml +++ b/tests/components/nextion/test.esp32-c3-idf.yaml @@ -1,63 +1,10 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_nextion - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -binary_sensor: - - platform: nextion - page_id: 0 - component_id: 2 - name: Nextion Touch Component - - platform: nextion - id: r0_sensor - name: R0 Sensor - component_name: page0.r0 - -sensor: - - platform: nextion - id: testnumber - name: testnumber - variable_name: testnumber - - platform: nextion - id: testwave - name: testwave - component_id: 2 - wave_channel_id: 1 - -switch: - - platform: nextion - id: r0 - name: R0 Switch - component_name: page0.r0 - -text_sensor: - - platform: nextion - name: text0 - id: text0 - update_interval: 4s - component_name: text0 +packages: + base: !include common.yaml display: - - platform: nextion + - id: !extend main_lcd tft_url: http://esphome.io/default35.tft - update_interval: 5s - on_sleep: - then: - lambda: 'ESP_LOGD("display","Display went to sleep");' - on_wake: - then: - lambda: 'ESP_LOGD("display","Display woke up");' - on_setup: - then: - lambda: 'ESP_LOGD("display","Display setup completed");' - on_page: - then: - lambda: 'ESP_LOGD("display","Display shows new page %u", x);' - on_buffer_overflow: - then: - logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp32-idf.yaml b/tests/components/nextion/test.esp32-idf.yaml index ba76236fc6..d5e02b8b85 100644 --- a/tests/components/nextion/test.esp32-idf.yaml +++ b/tests/components/nextion/test.esp32-idf.yaml @@ -1,63 +1,10 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_nextion - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 - -binary_sensor: - - platform: nextion - page_id: 0 - component_id: 2 - name: Nextion Touch Component - - platform: nextion - id: r0_sensor - name: R0 Sensor - component_name: page0.r0 - -sensor: - - platform: nextion - id: testnumber - name: testnumber - variable_name: testnumber - - platform: nextion - id: testwave - name: testwave - component_id: 2 - wave_channel_id: 1 - -switch: - - platform: nextion - id: r0 - name: R0 Switch - component_name: page0.r0 - -text_sensor: - - platform: nextion - name: text0 - id: text0 - update_interval: 4s - component_name: text0 +packages: + base: !include common.yaml display: - - platform: nextion + - id: !extend main_lcd tft_url: http://esphome.io/default35.tft - update_interval: 5s - on_sleep: - then: - lambda: 'ESP_LOGD("display","Display went to sleep");' - on_wake: - then: - lambda: 'ESP_LOGD("display","Display woke up");' - on_setup: - then: - lambda: 'ESP_LOGD("display","Display setup completed");' - on_page: - then: - lambda: 'ESP_LOGD("display","Display shows new page %u", x);' - on_buffer_overflow: - then: - logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.esp8266-ard.yaml b/tests/components/nextion/test.esp8266-ard.yaml index 5d253268f8..5135c7e4f4 100644 --- a/tests/components/nextion/test.esp8266-ard.yaml +++ b/tests/components/nextion/test.esp8266-ard.yaml @@ -1,63 +1,10 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_nextion - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -binary_sensor: - - platform: nextion - page_id: 0 - component_id: 2 - name: Nextion Touch Component - - platform: nextion - id: r0_sensor - name: R0 Sensor - component_name: page0.r0 - -sensor: - - platform: nextion - id: testnumber - name: testnumber - variable_name: testnumber - - platform: nextion - id: testwave - name: testwave - component_id: 2 - wave_channel_id: 1 - -switch: - - platform: nextion - id: r0 - name: R0 Switch - component_name: page0.r0 - -text_sensor: - - platform: nextion - name: text0 - id: text0 - update_interval: 4s - component_name: text0 +packages: + base: !include common.yaml display: - - platform: nextion + - id: !extend main_lcd tft_url: http://esphome.io/default35.tft - update_interval: 5s - on_sleep: - then: - lambda: 'ESP_LOGD("display","Display went to sleep");' - on_wake: - then: - lambda: 'ESP_LOGD("display","Display woke up");' - on_setup: - then: - lambda: 'ESP_LOGD("display","Display setup completed");' - on_page: - then: - lambda: 'ESP_LOGD("display","Display shows new page %u", x);' - on_buffer_overflow: - then: - logger.log: "Nextion reported a buffer overflow!" diff --git a/tests/components/nextion/test.rp2040-ard.yaml b/tests/components/nextion/test.rp2040-ard.yaml index 9b04433095..20347c6eff 100644 --- a/tests/components/nextion/test.rp2040-ard.yaml +++ b/tests/components/nextion/test.rp2040-ard.yaml @@ -1,58 +1,7 @@ -uart: - - id: uart_nextion - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -binary_sensor: - - platform: nextion - page_id: 0 - component_id: 2 - name: Nextion Touch Component - - platform: nextion - id: r0_sensor - name: R0 Sensor - component_name: page0.r0 +packages: + base: !include common.yaml -sensor: - - platform: nextion - id: testnumber - name: testnumber - variable_name: testnumber - - platform: nextion - id: testwave - name: testwave - component_id: 2 - wave_channel_id: 1 - -switch: - - platform: nextion - id: r0 - name: R0 Switch - component_name: page0.r0 - -text_sensor: - - platform: nextion - name: text0 - id: text0 - update_interval: 4s - component_name: text0 - -display: - - platform: nextion - update_interval: 5s - on_sleep: - then: - lambda: 'ESP_LOGD("display","Display went to sleep");' - on_wake: - then: - lambda: 'ESP_LOGD("display","Display woke up");' - on_setup: - then: - lambda: 'ESP_LOGD("display","Display setup completed");' - on_page: - then: - lambda: 'ESP_LOGD("display","Display shows new page %u", x);' - on_buffer_overflow: - then: - logger.log: "Nextion reported a buffer overflow!" From 2ecd5cff0701b807e5fa5558355c7beb7e6204ce Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Sun, 24 Nov 2024 21:16:51 +0100 Subject: [PATCH 0649/1052] [wifi] Make wifi_channel_() public (#7818) --- esphome/components/wifi/wifi_component.cpp | 4 ++-- esphome/components/wifi/wifi_component.h | 4 +++- esphome/components/wifi/wifi_component_esp32_arduino.cpp | 2 +- esphome/components/wifi/wifi_component_esp8266.cpp | 2 +- esphome/components/wifi/wifi_component_esp_idf.cpp | 2 +- esphome/components/wifi/wifi_component_libretiny.cpp | 2 +- esphome/components/wifi/wifi_component_pico_w.cpp | 2 +- 7 files changed, 10 insertions(+), 8 deletions(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 8788711d5a..eef962b8c4 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -444,7 +444,7 @@ void WiFiComponent::print_connect_params_() { 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, wifi_channel_()); + 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()); @@ -763,7 +763,7 @@ void WiFiComponent::load_fast_connect_settings_() { void WiFiComponent::save_fast_connect_settings_() { bssid_t bssid = wifi_bssid(); - uint8_t channel = wifi_channel_(); + uint8_t channel = get_wifi_channel(); if (bssid != this->selected_ap_.get_bssid() || channel != this->selected_ap_.get_channel()) { SavedWifiFastConnectSettings fast_connect_save{}; diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index dde0d1d5a5..5995f72e0b 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -317,6 +317,8 @@ class WiFiComponent : public Component { Trigger<> *get_connect_trigger() const { return this->connect_trigger_; }; Trigger<> *get_disconnect_trigger() const { return this->disconnect_trigger_; }; + int32_t get_wifi_channel(); + protected: static std::string format_mac_addr(const uint8_t mac[6]); @@ -344,7 +346,7 @@ class WiFiComponent : public Component { #endif // USE_WIFI_AP bool wifi_disconnect_(); - int32_t wifi_channel_(); + network::IPAddress wifi_subnet_mask_(); network::IPAddress wifi_gateway_ip_(); network::IPAddress wifi_dns_ip_(int num); diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 88648093c6..18c706cb01 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -799,7 +799,7 @@ bssid_t WiFiComponent::wifi_bssid() { } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } -int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddress WiFiComponent::wifi_subnet_mask_() { return network::IPAddress(WiFi.subnetMask()); } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return network::IPAddress(WiFi.gatewayIP()); } network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return network::IPAddress(WiFi.dnsIP(num)); } diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 4568895950..a18d078967 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -825,7 +825,7 @@ bssid_t WiFiComponent::wifi_bssid() { } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } -int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {(const ip_addr_t *) WiFi.subnetMask()}; } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {(const ip_addr_t *) WiFi.gatewayIP()}; } network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {(const ip_addr_t *) WiFi.dnsIP(num)}; } diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 13870136d4..1bf14ff40b 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -973,7 +973,7 @@ int8_t WiFiComponent::wifi_rssi() { } return info.rssi; } -int32_t WiFiComponent::wifi_channel_() { +int32_t WiFiComponent::get_wifi_channel() { uint8_t primary; wifi_second_chan_t second; esp_err_t err = esp_wifi_get_channel(&primary, &second); diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index afb30c3bcf..b02f8ef0ce 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -473,7 +473,7 @@ bssid_t WiFiComponent::wifi_bssid() { } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } -int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddress WiFiComponent::wifi_subnet_mask_() { return {WiFi.subnetMask()}; } network::IPAddress WiFiComponent::wifi_gateway_ip_() { return {WiFi.gatewayIP()}; } network::IPAddress WiFiComponent::wifi_dns_ip_(int num) { return {WiFi.dnsIP(num)}; } diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index bac986d899..23fd766abe 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -189,7 +189,7 @@ bssid_t WiFiComponent::wifi_bssid() { } std::string WiFiComponent::wifi_ssid() { return WiFi.SSID().c_str(); } int8_t WiFiComponent::wifi_rssi() { return WiFi.RSSI(); } -int32_t WiFiComponent::wifi_channel_() { return WiFi.channel(); } +int32_t WiFiComponent::get_wifi_channel() { return WiFi.channel(); } network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { network::IPAddresses addresses; From 4936ca17003404796d388363469badfab340d8b8 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:25:16 +1100 Subject: [PATCH 0650/1052] [lvgl] Bugfixes (#7803) --- esphome/components/lvgl/__init__.py | 2 +- esphome/components/lvgl/lv_validation.py | 3 ++- esphome/components/lvgl/schemas.py | 1 + esphome/components/lvgl/widgets/line.py | 5 ++++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index d03adc9624..8fdd03f647 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -322,8 +322,8 @@ async def to_code(configs): await encoders_to_code(lv_component, config, default_group) await keypads_to_code(lv_component, config, default_group) await theme_to_code(config) - await styles_to_code(config) await gradients_to_code(config) + await styles_to_code(config) await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) await add_pages(lv_component, config) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index b91b0905df..766c010244 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -30,7 +30,7 @@ from .defines import ( call_lambda, literal, ) -from .helpers import esphome_fonts_used, lv_fonts_used, requires_component +from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component from .types import lv_font_t, lv_gradient_t, lv_img_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -326,6 +326,7 @@ def image_validator(value): value = requires_component("image")(value) value = cv.use_id(Image_)(value) lv_images_used.add(value) + add_lv_use("img", "label") return value diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 516627708e..3f56b3345f 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -341,6 +341,7 @@ FLEX_OBJ_SCHEMA = { cv.Optional(df.CONF_FLEX_GROW): cv.int_, } + DISP_BG_SCHEMA = cv.Schema( { cv.Optional(df.CONF_DISP_BG_IMAGE): lv_image, diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 4c6439fde4..548dfa8452 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -39,7 +39,10 @@ LINE_SCHEMA = { class LineType(WidgetType): def __init__(self): super().__init__( - CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, modify_schema={} + CONF_LINE, + LvType("lv_line_t"), + (CONF_MAIN,), + LINE_SCHEMA, ) async def to_code(self, w: Widget, config): From 4001d82ca269472b6ef813fa4b894e1071d5571e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:25:51 +1100 Subject: [PATCH 0651/1052] [docker] Leave run-time required libraries installed. (#7804) --- docker/Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index ed6ce083a8..c2902a9dd1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -99,15 +99,17 @@ BUILD_DEPS=" libfreetype-dev=2.12.1+dfsg-5+deb12u3 libssl-dev=3.0.15-1~deb12u1 libffi-dev=3.4.4-1 - libopenjp2-7=2.5.0-2 - libtiff6=4.5.0-6+deb12u1 cargo=0.66.0+ds1-1 pkg-config=1.8.1-1 " +LIB_DEPS=" + libtiff6=4.5.0-6+deb12u1 + libopenjp2-7=2.5.0-2 +" if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ] || [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] then apt-get update - apt-get install -y --no-install-recommends $BUILD_DEPS + apt-get install -y --no-install-recommends $BUILD_DEPS $LIB_DEPS fi CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo From 13077095c2ece197c41c41324d578cb2a284edd8 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:27:09 +1100 Subject: [PATCH 0652/1052] [qspi_dbi] Fix init sequences (Bugfix) (#7805) --- esphome/components/qspi_dbi/models.py | 2 ++ esphome/components/qspi_dbi/qspi_dbi.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py index cbd9c4663f..c1fe434853 100644 --- a/esphome/components/qspi_dbi/models.py +++ b/esphome/components/qspi_dbi/models.py @@ -1,6 +1,7 @@ # Commands SW_RESET_CMD = 0x01 SLEEP_OUT = 0x11 +NORON = 0x13 INVERT_OFF = 0x20 INVERT_ON = 0x21 ALL_ON = 0x23 @@ -56,6 +57,7 @@ chip.cmd(0xC2, 0x00) chip.delay(10) chip.cmd(TEON, 0x00) chip.cmd(PIXFMT, 0x55) +chip.cmd(NORON) chip = DriverChip("AXS15231") chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index a649a25ea6..785885d4ec 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -111,7 +111,6 @@ void QspiDbi::reset_params_(bool ready) { mad |= MADCTL_MY; this->write_command_(MADCTL_CMD, mad); this->write_command_(BRIGHTNESS, this->brightness_); - this->write_command_(NORON); this->write_command_(DISPLAY_ON); } From e3e3d9234756b94a74f3788f64aa4d9b37d40f7a Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 24 Nov 2024 10:42:46 -1000 Subject: [PATCH 0653/1052] fix modbus crashing when bad data returned (#7810) Co-authored-by: Samuel Sieb --- esphome/components/modbus/modbus.cpp | 3 +- .../modbus_controller/modbus_controller.cpp | 72 ++++++++++++++----- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 8544b50261..47deea83e6 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -38,8 +38,9 @@ void Modbus::loop() { // stop blocking new send commands after sent_wait_time_ ms after response received if (now - this->last_send_ > send_wait_time_) { - if (waiting_for_response > 0) + if (waiting_for_response > 0) { ESP_LOGV(TAG, "Stop waiting for response from %d", waiting_for_response); + } waiting_for_response = 0; } } diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index e1102516ca..f8b72af817 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -622,51 +622,87 @@ int64_t payload_to_number(const std::vector &data, SensorValueType sens uint32_t bitmask) { int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits + size_t size = data.size() - offset; + bool error = false; switch (sensor_value_type) { case SensorValueType::U_WORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; + if (size >= 2) { + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; + } else { + error = true; + } break; case SensorValueType::U_DWORD: case SensorValueType::FP32: - value = get_data(data, offset); - value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + if (size >= 4) { + value = get_data(data, offset); + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + } else { + error = true; + } break; case SensorValueType::U_DWORD_R: case SensorValueType::FP32_R: - value = get_data(data, offset); - value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; - value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + if (size >= 4) { + value = get_data(data, offset); + value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + } else { + error = true; + } break; case SensorValueType::S_WORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), - bitmask); // default is 0xFFFF ; + if (size >= 2) { + value = mask_and_shift_by_rightbit(get_data(data, offset), + bitmask); // default is 0xFFFF ; + } else { + error = true; + } break; case SensorValueType::S_DWORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); + if (size >= 4) { + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); + } else { + error = true; + } break; case SensorValueType::S_DWORD_R: { - value = get_data(data, offset); - // Currently the high word is at the low position - // the sign bit is therefore at low before the switch - uint32_t sign_bit = (value & 0x8000) << 16; - value = mask_and_shift_by_rightbit( - static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); + if (size >= 4) { + value = get_data(data, offset); + // Currently the high word is at the low position + // the sign bit is therefore at low before the switch + uint32_t sign_bit = (value & 0x8000) << 16; + value = mask_and_shift_by_rightbit( + static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); + } else { + error = true; + } } break; case SensorValueType::U_QWORD: case SensorValueType::S_QWORD: // Ignore bitmask for QWORD - value = get_data(data, offset); + if (size >= 8) { + value = get_data(data, offset); + } else { + error = true; + } break; case SensorValueType::U_QWORD_R: case SensorValueType::S_QWORD_R: { // Ignore bitmask for QWORD - uint64_t tmp = get_data(data, offset); - value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); + if (size >= 8) { + uint64_t tmp = get_data(data, offset); + value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); + } else { + error = true; + } } break; case SensorValueType::RAW: default: break; } + if (error) + ESP_LOGE(TAG, "not enough data for value"); return value; } From 9fc1377b448fab25c91e3342834a0b38df0c4a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mart=C3=ADn?= Date: Sun, 24 Nov 2024 23:06:21 +0100 Subject: [PATCH 0654/1052] feat(WiFi): Add wifi.configure action (#7335) --- esphome/components/wifi/__init__.py | 42 +++++++++++++ esphome/components/wifi/wifi_component.h | 79 ++++++++++++++++++++++++ tests/components/wifi/common.yaml | 7 +++ 3 files changed, 128 insertions(+) diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index ea03cc16d1..ad1a4f5262 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -27,6 +27,7 @@ from esphome.const import ( CONF_NETWORKS, CONF_ON_CONNECT, CONF_ON_DISCONNECT, + CONF_ON_ERROR, CONF_PASSWORD, CONF_POWER_SAVE_MODE, CONF_PRIORITY, @@ -34,6 +35,7 @@ from esphome.const import ( CONF_SSID, CONF_STATIC_IP, CONF_SUBNET, + CONF_TIMEOUT, CONF_TTLS_PHASE_2, CONF_USE_ADDRESS, CONF_USERNAME, @@ -46,6 +48,7 @@ from . import wpa2_eap AUTO_LOAD = ["network"] NO_WIFI_VARIANTS = [const.VARIANT_ESP32H2] +CONF_SAVE = "save" wifi_ns = cg.esphome_ns.namespace("wifi") EAPAuth = wifi_ns.struct("EAPAuth") @@ -63,6 +66,9 @@ WiFiConnectedCondition = wifi_ns.class_("WiFiConnectedCondition", Condition) WiFiEnabledCondition = wifi_ns.class_("WiFiEnabledCondition", Condition) WiFiEnableAction = wifi_ns.class_("WiFiEnableAction", automation.Action) WiFiDisableAction = wifi_ns.class_("WiFiDisableAction", automation.Action) +WiFiConfigureAction = wifi_ns.class_( + "WiFiConfigureAction", automation.Action, cg.Component +) def validate_password(value): @@ -483,3 +489,39 @@ async def wifi_enable_to_code(config, action_id, template_arg, args): @automation.register_action("wifi.disable", WiFiDisableAction, cv.Schema({})) async def wifi_disable_to_code(config, action_id, template_arg, args): return cg.new_Pvariable(action_id, template_arg) + + +@automation.register_action( + "wifi.configure", + WiFiConfigureAction, + cv.Schema( + { + cv.Required(CONF_SSID): cv.templatable(cv.ssid), + cv.Required(CONF_PASSWORD): cv.templatable(validate_password), + cv.Optional(CONF_SAVE, default=True): cv.templatable(cv.boolean), + cv.Optional(CONF_TIMEOUT, default="30000ms"): cv.templatable( + cv.positive_time_period_milliseconds + ), + cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True), + cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True), + } + ), +) +async def wifi_set_sta_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + ssid = await cg.templatable(config[CONF_SSID], args, cg.std_string) + password = await cg.templatable(config[CONF_PASSWORD], args, cg.std_string) + save = await cg.templatable(config[CONF_SAVE], args, cg.bool_) + timeout = await cg.templatable(config.get(CONF_TIMEOUT), args, cg.uint32) + cg.add(var.set_ssid(ssid)) + cg.add(var.set_password(password)) + cg.add(var.set_save(save)) + cg.add(var.set_connection_timeout(timeout)) + if on_connect_config := config.get(CONF_ON_CONNECT): + await automation.build_automation( + var.get_connect_trigger(), [], on_connect_config + ) + if on_error_config := config.get(CONF_ON_ERROR): + await automation.build_automation(var.get_error_trigger(), [], on_error_config) + await cg.register_component(var, config) + return var diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 5995f72e0b..abedfab3a6 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -209,6 +209,7 @@ class WiFiComponent : public Component { WiFiComponent(); void set_sta(const WiFiAP &ap); + WiFiAP get_sta() { return this->selected_ap_; } void add_sta(const WiFiAP &ap); void clear_sta(); @@ -443,6 +444,84 @@ template class WiFiDisableAction : public Action { void play(Ts... x) override { global_wifi_component->disable(); } }; +template class WiFiConfigureAction : public Action, public Component { + public: + TEMPLATABLE_VALUE(std::string, ssid) + TEMPLATABLE_VALUE(std::string, password) + TEMPLATABLE_VALUE(bool, save) + TEMPLATABLE_VALUE(uint32_t, connection_timeout) + + void play(Ts... x) override { + auto ssid = this->ssid_.value(x...); + auto password = this->password_.value(x...); + // Avoid multiple calls + if (this->connecting_) + return; + // If already connected to the same AP, do nothing + if (global_wifi_component->wifi_ssid() == ssid) { + // Callback to notify the user that the connection was successful + this->connect_trigger_->trigger(); + return; + } + // Create a new WiFiAP object with the new SSID and password + this->new_sta_.set_ssid(ssid); + this->new_sta_.set_password(password); + // Save the current STA + this->old_sta_ = global_wifi_component->get_sta(); + // Disable WiFi + global_wifi_component->disable(); + // Set the state to connecting + this->connecting_ = true; + // Store the new STA so once the WiFi is enabled, it will connect to it + // This is necessary because the WiFiComponent will raise an error and fallback to the saved STA + // if trying to connect to a new STA while already connected to another one + if (this->save_.value(x...)) { + global_wifi_component->save_wifi_sta(new_sta_.get_ssid(), new_sta_.get_password()); + } else { + global_wifi_component->set_sta(new_sta_); + } + // 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; + // 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(); + }); + } + + Trigger<> *get_connect_trigger() const { return this->connect_trigger_; } + Trigger<> *get_error_trigger() const { return this->error_trigger_; } + + void loop() override { + if (!this->connecting_) + return; + 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->connecting_ = false; + if (global_wifi_component->wifi_ssid() == this->new_sta_.get_ssid()) { + // Callback to notify the user that the connection was successful + this->connect_trigger_->trigger(); + } else { + // Callback to notify the user that the connection failed + this->error_trigger_->trigger(); + } + } + } + + protected: + bool connecting_{false}; + WiFiAP new_sta_; + WiFiAP old_sta_; + Trigger<> *connect_trigger_{new Trigger<>()}; + Trigger<> *error_trigger_{new Trigger<>()}; +}; + } // namespace wifi } // namespace esphome #endif diff --git a/tests/components/wifi/common.yaml b/tests/components/wifi/common.yaml index 003f6347be..343d44b177 100644 --- a/tests/components/wifi/common.yaml +++ b/tests/components/wifi/common.yaml @@ -3,6 +3,13 @@ esphome: then: - wifi.disable - wifi.enable + - wifi.configure: + ssid: MySSID + password: password1 + on_connect: + - logger.log: "Connected to WiFi!" + on_error: + - logger.log: "Failed to connect to WiFi!" wifi: ssid: MySSID From d4d630823ccc3bdddb6e6b39176c188d353d0807 Mon Sep 17 00:00:00 2001 From: TFGF Date: Sun, 24 Nov 2024 19:15:10 -0300 Subject: [PATCH 0655/1052] [Modbus Controller] Fix issue #6477. Online automation triggering Offline (#7801) --- esphome/components/modbus_controller/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 5c407d6fff..2a08075831 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -163,7 +163,7 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_ON_OFFLINE): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOnlineTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOfflineTrigger), } ), } From e02f3cdac7b7e8a58711763b5d7f9dfac8ea6991 Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Mon, 25 Nov 2024 01:23:30 +0300 Subject: [PATCH 0656/1052] [fix] Status sensor does not check if required network component is missing (#7734) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/status/binary_sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/status/binary_sensor.py b/esphome/components/status/binary_sensor.py index 1f2b7c9d18..adc342ed4d 100644 --- a/esphome/components/status/binary_sensor.py +++ b/esphome/components/status/binary_sensor.py @@ -6,6 +6,8 @@ from esphome.const import ( ENTITY_CATEGORY_DIAGNOSTIC, ) +DEPENDENCIES = ["network"] + status_ns = cg.esphome_ns.namespace("status") StatusBinarySensor = status_ns.class_( "StatusBinarySensor", binary_sensor.BinarySensor, cg.Component From 59653ec7853a349e9e736710867daedc017a725e Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 24 Nov 2024 12:40:28 -1000 Subject: [PATCH 0657/1052] allow multiple graphical menus (#7809) Co-authored-by: Samuel Sieb --- esphome/components/display_menu_base/__init__.py | 2 -- esphome/components/graphical_display_menu/__init__.py | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/display_menu_base/__init__.py b/esphome/components/display_menu_base/__init__.py index 8ae9cbc2a4..f9c0424104 100644 --- a/esphome/components/display_menu_base/__init__.py +++ b/esphome/components/display_menu_base/__init__.py @@ -68,8 +68,6 @@ IsActiveCondition = display_menu_base_ns.class_( "IsActiveCondition", automation.Condition ) -MULTI_CONF = True - MenuItemType = display_menu_base_ns.enum("MenuItemType") MENU_ITEM_TYPES = { diff --git a/esphome/components/graphical_display_menu/__init__.py b/esphome/components/graphical_display_menu/__init__.py index f4d59b22b8..56b720e75c 100644 --- a/esphome/components/graphical_display_menu/__init__.py +++ b/esphome/components/graphical_display_menu/__init__.py @@ -36,6 +36,8 @@ CODEOWNERS = ["@MrMDavidson"] AUTO_LOAD = ["display_menu_base"] +MULTI_CONF = True + CONFIG_SCHEMA = DISPLAY_MENU_BASE_SCHEMA.extend( cv.Schema( { From b95b4a069417ea6fd31f96b5e9150ca16dd1c3f5 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 24 Nov 2024 12:40:51 -1000 Subject: [PATCH 0658/1052] keypad binary sensors should be initially off (#7808) Co-authored-by: Samuel Sieb --- esphome/components/binary_sensor/binary_sensor.h | 2 +- .../matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 301a472810..57cae9e2f5 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -58,7 +58,7 @@ class BinarySensor : public EntityBase, public EntityBase_DeviceClass { void publish_initial_state(bool state); /// The current reported state of the binary sensor. - bool state; + bool state{false}; void add_filter(Filter *filter); void add_filters(const std::vector &filters); diff --git a/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h b/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h index d8a217f55e..2c1ce96f0a 100644 --- a/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h +++ b/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h @@ -6,7 +6,7 @@ namespace esphome { namespace matrix_keypad { -class MatrixKeypadBinarySensor : public MatrixKeypadListener, public binary_sensor::BinarySensor { +class MatrixKeypadBinarySensor : public MatrixKeypadListener, public binary_sensor::BinarySensorInitiallyOff { public: MatrixKeypadBinarySensor(uint8_t key) : has_key_(true), key_(key){}; MatrixKeypadBinarySensor(const char *key) : has_key_(true), key_((uint8_t) key[0]){}; From 71496574e9e104433ded1f4da497454b1b266e3d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:26:36 +1300 Subject: [PATCH 0659/1052] Move ``CONF_NAME_ADD_MAC_SUFFIX`` to ``const.py`` (#7820) --- esphome/const.py | 1 + esphome/core/config.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/const.py b/esphome/const.py index 6a643e1e30..50528b7363 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -528,6 +528,7 @@ CONF_MULTIPLE = "multiple" CONF_MULTIPLEXER = "multiplexer" CONF_MULTIPLY = "multiply" CONF_NAME = "name" +CONF_NAME_ADD_MAC_SUFFIX = "name_add_mac_suffix" CONF_NAME_FONT = "name_font" CONF_NBITS = "nbits" CONF_NEC = "nec" diff --git a/esphome/core/config.py b/esphome/core/config.py index 367e61c413..eee8b73934 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -21,6 +21,7 @@ from esphome.const import ( CONF_LIBRARIES, CONF_MIN_VERSION, CONF_NAME, + CONF_NAME_ADD_MAC_SUFFIX, CONF_ON_BOOT, CONF_ON_LOOP, CONF_ON_SHUTDOWN, @@ -59,8 +60,6 @@ ProjectUpdateTrigger = cg.esphome_ns.class_( VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$") -CONF_NAME_ADD_MAC_SUFFIX = "name_add_mac_suffix" - VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"} From c49f7293fef3c052691f388b6c9af6f8709b7aca Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 24 Nov 2024 21:24:23 -1000 Subject: [PATCH 0660/1052] binary_sensor for switch state (#7819) --- CODEOWNERS | 1 + .../switch/binary_sensor/__init__.py | 31 +++++++++++++++++++ .../binary_sensor/switch_binary_sensor.cpp | 17 ++++++++++ .../binary_sensor/switch_binary_sensor.h | 22 +++++++++++++ tests/components/switch/common.yaml | 11 +++++++ tests/components/switch/test.bk72xx-ard.yaml | 2 ++ tests/components/switch/test.esp32-ard.yaml | 2 ++ .../components/switch/test.esp32-c3-ard.yaml | 2 ++ .../components/switch/test.esp32-c3-idf.yaml | 2 ++ tests/components/switch/test.esp32-idf.yaml | 2 ++ .../components/switch/test.esp32-s3-idf.yaml | 2 ++ tests/components/switch/test.esp8266-ard.yaml | 2 ++ tests/components/switch/test.rp2040-ard.yaml | 2 ++ 13 files changed, 98 insertions(+) create mode 100644 esphome/components/switch/binary_sensor/__init__.py create mode 100644 esphome/components/switch/binary_sensor/switch_binary_sensor.cpp create mode 100644 esphome/components/switch/binary_sensor/switch_binary_sensor.h create mode 100644 tests/components/switch/common.yaml create mode 100644 tests/components/switch/test.bk72xx-ard.yaml create mode 100644 tests/components/switch/test.esp32-ard.yaml create mode 100644 tests/components/switch/test.esp32-c3-ard.yaml create mode 100644 tests/components/switch/test.esp32-c3-idf.yaml create mode 100644 tests/components/switch/test.esp32-idf.yaml create mode 100644 tests/components/switch/test.esp32-s3-idf.yaml create mode 100644 tests/components/switch/test.esp8266-ard.yaml create mode 100644 tests/components/switch/test.rp2040-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 8fbbacef59..dd3926d283 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -408,6 +408,7 @@ esphome/components/substitutions/* @esphome/core esphome/components/sun/* @OttoWinter esphome/components/sun_gtil2/* @Mat931 esphome/components/switch/* @esphome/core +esphome/components/switch/binary_sensor/* @ssieb esphome/components/t6615/* @tylermenezes esphome/components/tc74/* @sethgirvan esphome/components/tca9548a/* @andreashergert1984 diff --git a/esphome/components/switch/binary_sensor/__init__.py b/esphome/components/switch/binary_sensor/__init__.py new file mode 100644 index 0000000000..61ca1a14a2 --- /dev/null +++ b/esphome/components/switch/binary_sensor/__init__.py @@ -0,0 +1,31 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +import esphome.config_validation as cv +from esphome.const import CONF_SOURCE_ID + +from .. import Switch, switch_ns + +CODEOWNERS = ["@ssieb"] + +SwitchBinarySensor = switch_ns.class_( + "SwitchBinarySensor", binary_sensor.BinarySensor, cg.Component +) + + +CONFIG_SCHEMA = ( + binary_sensor.binary_sensor_schema(SwitchBinarySensor) + .extend( + { + cv.Required(CONF_SOURCE_ID): cv.use_id(Switch), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = await binary_sensor.new_binary_sensor(config) + await cg.register_component(var, config) + + source = await cg.get_variable(config[CONF_SOURCE_ID]) + cg.add(var.set_source(source)) diff --git a/esphome/components/switch/binary_sensor/switch_binary_sensor.cpp b/esphome/components/switch/binary_sensor/switch_binary_sensor.cpp new file mode 100644 index 0000000000..ba57154446 --- /dev/null +++ b/esphome/components/switch/binary_sensor/switch_binary_sensor.cpp @@ -0,0 +1,17 @@ +#include "switch_binary_sensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace switch_ { + +static const char *const TAG = "switch.binary_sensor"; + +void SwitchBinarySensor::setup() { + source_->add_on_state_callback([this](bool value) { this->publish_state(value); }); + this->publish_state(source_->state); +} + +void SwitchBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Switch Binary Sensor", this); } + +} // namespace switch_ +} // namespace esphome diff --git a/esphome/components/switch/binary_sensor/switch_binary_sensor.h b/esphome/components/switch/binary_sensor/switch_binary_sensor.h new file mode 100644 index 0000000000..5a947c2fb4 --- /dev/null +++ b/esphome/components/switch/binary_sensor/switch_binary_sensor.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../switch.h" +#include "esphome/core/component.h" +#include "esphome/components/binary_sensor/binary_sensor.h" + +namespace esphome { +namespace switch_ { + +class SwitchBinarySensor : public binary_sensor::BinarySensor, public Component { + public: + void set_source(Switch *source) { source_ = source; } + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + + protected: + Switch *source_; +}; + +} // namespace switch_ +} // namespace esphome diff --git a/tests/components/switch/common.yaml b/tests/components/switch/common.yaml new file mode 100644 index 0000000000..8d6972f91b --- /dev/null +++ b/tests/components/switch/common.yaml @@ -0,0 +1,11 @@ +binary_sensor: + - platform: switch + id: some_binary_sensor + name: "Template Switch State" + source_id: the_switch + +switch: + - platform: template + name: "Template Switch" + id: the_switch + optimistic: true diff --git a/tests/components/switch/test.bk72xx-ard.yaml b/tests/components/switch/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.bk72xx-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/switch/test.esp32-ard.yaml b/tests/components/switch/test.esp32-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.esp32-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/switch/test.esp32-c3-ard.yaml b/tests/components/switch/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.esp32-c3-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/switch/test.esp32-c3-idf.yaml b/tests/components/switch/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.esp32-c3-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/switch/test.esp32-idf.yaml b/tests/components/switch/test.esp32-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.esp32-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/switch/test.esp32-s3-idf.yaml b/tests/components/switch/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.esp32-s3-idf.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/switch/test.esp8266-ard.yaml b/tests/components/switch/test.esp8266-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.esp8266-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml diff --git a/tests/components/switch/test.rp2040-ard.yaml b/tests/components/switch/test.rp2040-ard.yaml new file mode 100644 index 0000000000..25cb37a0b4 --- /dev/null +++ b/tests/components/switch/test.rp2040-ard.yaml @@ -0,0 +1,2 @@ +packages: + common: !include common.yaml From 7f75f2135d74c6daaaf5251c12a3a1f986ec6694 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 02:22:50 -0600 Subject: [PATCH 0661/1052] [nextion] Remove assignment within `if` (#7824) --- esphome/components/nextion/nextion_upload_idf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/nextion/nextion_upload_idf.cpp b/esphome/components/nextion/nextion_upload_idf.cpp index b5bb5478c1..7541a57d56 100644 --- a/esphome/components/nextion/nextion_upload_idf.cpp +++ b/esphome/components/nextion/nextion_upload_idf.cpp @@ -36,8 +36,8 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r ESP_LOGV(TAG, "Requesting range: %s", range_header); esp_http_client_set_header(http_client, "Range", range_header); ESP_LOGV(TAG, "Opening HTTP connetion"); - esp_err_t err; - if ((err = esp_http_client_open(http_client, 0)) != ESP_OK) { + 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)); return -1; } From 6c548a15966b0486226d0fd7c33a105b6776d3f4 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 02:23:00 -0600 Subject: [PATCH 0662/1052] [ota] `void` functions should return nothing (#7825) --- esphome/components/ota/automation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h index 4605193480..7e1a60f3ce 100644 --- a/esphome/components/ota/automation.h +++ b/esphome/components/ota/automation.h @@ -12,7 +12,7 @@ class OTAStateChangeTrigger : public Trigger { explicit OTAStateChangeTrigger(OTAComponent *parent) { parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { if (!parent->is_failed()) { - return trigger(state); + trigger(state); } }); } From 46a435f5f2ec8f8dfb306f953280b5b2be265195 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 02:24:35 -0600 Subject: [PATCH 0663/1052] [safe_mode] Remove unused capture (#7826) --- esphome/components/safe_mode/automation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/safe_mode/automation.h b/esphome/components/safe_mode/automation.h index d1388449ee..1ffa86a588 100644 --- a/esphome/components/safe_mode/automation.h +++ b/esphome/components/safe_mode/automation.h @@ -9,7 +9,7 @@ namespace safe_mode { class SafeModeTrigger : public Trigger<> { public: explicit SafeModeTrigger(SafeModeComponent *parent) { - parent->add_on_safe_mode_callback([this, parent]() { trigger(); }); + parent->add_on_safe_mode_callback([this]() { trigger(); }); } }; From ebf895990b494ec8c09b7dbab051b0d53cc2d029 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 02:25:04 -0600 Subject: [PATCH 0664/1052] [stepper] Remove unnecessary ``#include`` (#7827) --- esphome/components/stepper/stepper.h | 1 - 1 file changed, 1 deletion(-) diff --git a/esphome/components/stepper/stepper.h b/esphome/components/stepper/stepper.h index 560362e4d0..ba2b3182d7 100644 --- a/esphome/components/stepper/stepper.h +++ b/esphome/components/stepper/stepper.h @@ -2,7 +2,6 @@ #include "esphome/core/component.h" #include "esphome/core/automation.h" -#include "esphome/components/stepper/stepper.h" namespace esphome { namespace stepper { From aa6cea6f7e4ea4b656fa6d3a152581e623227bd3 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 02:27:36 -0600 Subject: [PATCH 0665/1052] [sx1509] Fix up includes (#7828) --- esphome/components/sx1509/sx1509_gpio_pin.cpp | 3 ++- esphome/components/sx1509/sx1509_gpio_pin.h | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/esphome/components/sx1509/sx1509_gpio_pin.cpp b/esphome/components/sx1509/sx1509_gpio_pin.cpp index 56b51ae311..a74c8b60b8 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.cpp +++ b/esphome/components/sx1509/sx1509_gpio_pin.cpp @@ -1,5 +1,6 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include "sx1509.h" #include "sx1509_gpio_pin.h" namespace esphome { @@ -13,7 +14,7 @@ bool SX1509GPIOPin::digital_read() { return this->parent_->digital_read(this->pi void SX1509GPIOPin::digital_write(bool value) { this->parent_->digital_write(this->pin_, value != this->inverted_); } std::string SX1509GPIOPin::dump_summary() const { char buffer[32]; - snprintf(buffer, sizeof(buffer), "%u via sx1509", pin_); + snprintf(buffer, sizeof(buffer), "%u via sx1509", this->pin_); return buffer; } diff --git a/esphome/components/sx1509/sx1509_gpio_pin.h b/esphome/components/sx1509/sx1509_gpio_pin.h index 4d8aa5ec83..1cfa341ee7 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.h +++ b/esphome/components/sx1509/sx1509_gpio_pin.h @@ -1,6 +1,6 @@ #pragma once -#include "sx1509.h" +#include "esphome/core/gpio.h" namespace esphome { namespace sx1509 { @@ -15,10 +15,10 @@ class SX1509GPIOPin : public GPIOPin { void digital_write(bool value) override; std::string dump_summary() const override; - void set_parent(SX1509Component *parent) { parent_ = parent; } - void set_pin(uint8_t pin) { pin_ = pin; } - void set_inverted(bool inverted) { inverted_ = inverted; } - void set_flags(gpio::Flags flags) { flags_ = flags; } + void set_parent(SX1509Component *parent) { this->parent_ = parent; } + void set_pin(uint8_t pin) { this->pin_ = pin; } + void set_inverted(bool inverted) { this->inverted_ = inverted; } + void set_flags(gpio::Flags flags) { this->flags_ = flags; } protected: SX1509Component *parent_; From 1bd2d41ffd1273eefa03aa331944d7f47d5cd7c9 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 02:39:22 -0600 Subject: [PATCH 0666/1052] [uart] `void` functions should return nothing (#7829) --- esphome/components/uart/uart.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index d41dbe26e6..dc6962fbae 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -40,7 +40,7 @@ class UARTDevice { int available() { return this->parent_->available(); } - void flush() { return this->parent_->flush(); } + void flush() { this->parent_->flush(); } // Compat APIs int read() { From 17a09cd22151d95d510c858253b78dadae8e53e8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 03:50:18 -0600 Subject: [PATCH 0667/1052] [audio] Header modernization (#7832) --- esphome/components/audio/audio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/audio/audio.h b/esphome/components/audio/audio.h index b0968dc8da..caf325cf54 100644 --- a/esphome/components/audio/audio.h +++ b/esphome/components/audio/audio.h @@ -1,7 +1,7 @@ #pragma once +#include #include -#include namespace esphome { namespace audio { From cf835d15806481c50361cd6d8d193cc7b0b0175e Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 03:50:24 -0600 Subject: [PATCH 0668/1052] [opentherm] Follow variable naming convention (#7833) --- esphome/components/opentherm/opentherm.cpp | 6 +++--- esphome/components/opentherm/opentherm.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 26c707f9a0..78ecb53428 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -29,7 +29,7 @@ using std::to_string; static const char *const TAG = "opentherm"; #ifdef ESP8266 -OpenTherm *OpenTherm::instance_ = nullptr; +OpenTherm *OpenTherm::instance = nullptr; #endif OpenTherm::OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t device_timeout) @@ -53,7 +53,7 @@ OpenTherm::OpenTherm(InternalGPIOPin *in_pin, InternalGPIOPin *out_pin, int32_t bool OpenTherm::initialize() { #ifdef ESP8266 - OpenTherm::instance_ = this; + OpenTherm::instance = this; #endif this->in_pin_->pin_mode(gpio::FLAG_INPUT); this->out_pin_->pin_mode(gpio::FLAG_OUTPUT); @@ -216,7 +216,7 @@ bool IRAM_ATTR OpenTherm::timer_isr(OpenTherm *arg) { } #ifdef ESP8266 -void IRAM_ATTR OpenTherm::esp8266_timer_isr() { OpenTherm::timer_isr(OpenTherm::instance_); } +void IRAM_ATTR OpenTherm::esp8266_timer_isr() { OpenTherm::timer_isr(OpenTherm::instance); } #endif void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) { diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 85f4611125..5088bb2aa3 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -371,7 +371,7 @@ class OpenTherm { #ifdef ESP8266 // ESP8266 timer can accept callback with no parameters, so we have this hack to save a static instance of OpenTherm - static OpenTherm *instance_; + static OpenTherm *instance; #endif }; From 89ecfc20049d2358ecdbe63dacaed9143c9d9ed5 Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Tue, 26 Nov 2024 00:47:01 +0300 Subject: [PATCH 0669/1052] [opentherm] Fix out of memory errors on ESP8266 (#7835) --- esphome/components/opentherm/hub.cpp | 13 ++++---- esphome/components/opentherm/opentherm.cpp | 36 ++++++---------------- esphome/components/opentherm/opentherm.h | 7 ++--- esphome/core/helpers.cpp | 12 ++++++++ esphome/core/helpers.h | 8 +++++ 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index dfa8ea95c5..aac2966ed1 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -138,7 +138,7 @@ OpenthermHub::OpenthermHub() : Component(), in_pin_{}, out_pin_{} {} void OpenthermHub::process_response(OpenthermData &data) { ESP_LOGD(TAG, "Received OpenTherm response with id %d (%s)", data.id, this->opentherm_->message_id_to_str((MessageId) data.id)); - ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(data).c_str()); + this->opentherm_->debug_data(data); switch (data.id) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, , @@ -315,7 +315,7 @@ void OpenthermHub::start_conversation_() { ESP_LOGD(TAG, "Sending request with id %d (%s)", request.id, this->opentherm_->message_id_to_str((MessageId) request.id)); - ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(request).c_str()); + this->opentherm_->debug_data(request); // Send the request this->last_conversation_start_ = millis(); this->opentherm_->send(request); @@ -340,19 +340,18 @@ void OpenthermHub::stop_opentherm_() { this->opentherm_->stop(); this->last_conversation_end_ = millis(); } - void OpenthermHub::handle_protocol_write_error_() { ESP_LOGW(TAG, "Error while sending request: %s", this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode())); - ESP_LOGW(TAG, "%s", this->opentherm_->debug_data(this->last_request_).c_str()); + this->opentherm_->debug_data(this->last_request_); } - void OpenthermHub::handle_protocol_read_error_() { OpenThermError error; this->opentherm_->get_protocol_error(error); - ESP_LOGW(TAG, "Protocol error occured while receiving response: %s", this->opentherm_->debug_error(error).c_str()); + ESP_LOGW(TAG, "Protocol error occured while receiving response: %s", + this->opentherm_->protocol_error_to_to_str(error.error_type)); + this->opentherm_->debug_error(error); } - void OpenthermHub::handle_timeout_error_() { ESP_LOGW(TAG, "Receive response timed out at a protocol level"); this->stop_opentherm_(); diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 78ecb53428..e40fc66b7d 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -15,15 +15,11 @@ #include "Arduino.h" #endif #include -#include -#include namespace esphome { namespace opentherm { using std::string; -using std::bitset; -using std::stringstream; using std::to_string; static const char *const TAG = "opentherm"; @@ -545,29 +541,17 @@ const char *OpenTherm::message_id_to_str(MessageId id) { } } -string OpenTherm::debug_data(OpenthermData &data) { - stringstream result; - result << bitset<8>(data.type) << " " << bitset<8>(data.id) << " " << bitset<8>(data.valueHB) << " " - << bitset<8>(data.valueLB) << "\n"; - result << "type: " << this->message_type_to_str((MessageType) data.type) << "; "; - result << "id: " << to_string(data.id) << "; "; - result << "HB: " << to_string(data.valueHB) << "; "; - result << "LB: " << to_string(data.valueLB) << "; "; - result << "uint_16: " << to_string(data.u16()) << "; "; - result << "float: " << to_string(data.f88()); - - return result.str(); +void OpenTherm::debug_data(OpenthermData &data) { + ESP_LOGD(TAG, "%s %s %s %s", format_bin(data.type).c_str(), format_bin(data.id).c_str(), + format_bin(data.valueHB).c_str(), format_bin(data.valueLB).c_str()); + ESP_LOGD(TAG, "type: %s; id: %s; HB: %s; LB: %s; uint_16: %s; float: %s", + this->message_type_to_str((MessageType) data.type), to_string(data.id).c_str(), + to_string(data.valueHB).c_str(), to_string(data.valueLB).c_str(), to_string(data.u16()).c_str(), + to_string(data.f88()).c_str()); } -std::string OpenTherm::debug_error(OpenThermError &error) { - stringstream result; - result << "type: " << this->protocol_error_to_to_str(error.error_type) << "; "; - result << "data: "; - result << format_hex(error.data); - result << "; clock: " << to_string(clock_); - result << "; capture: " << bitset<32>(error.capture); - result << "; bit_pos: " << to_string(error.bit_pos); - - return result.str(); +void OpenTherm::debug_error(OpenThermError &error) const { + ESP_LOGD(TAG, "data: %s; clock: %s; capture: %s; bit_pos: %s", format_hex(error.data).c_str(), + to_string(clock_).c_str(), format_bin(error.capture).c_str(), to_string(error.bit_pos).c_str()); } float OpenthermData::f88() { return ((float) this->s16()) / 256.0; } diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 5088bb2aa3..9532a77821 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -8,10 +8,9 @@ #pragma once #include -#include -#include #include "esphome/core/hal.h" #include "esphome/core/log.h" +#include "esphome/core/helpers.h" #if defined(ESP32) || defined(USE_ESP_IDF) #include "driver/timer.h" @@ -318,8 +317,8 @@ class OpenTherm { OperationMode get_mode() { return mode_; } - std::string debug_data(OpenthermData &data); - std::string debug_error(OpenThermError &error); + void debug_data(OpenthermData &data); + void debug_error(OpenThermError &error) const; const char *protocol_error_to_to_str(ProtocolErrorType error_type); const char *message_type_to_str(MessageType message_type); diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index dae60a4e1d..befc84516c 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -397,6 +397,18 @@ std::string format_hex_pretty(const uint16_t *data, size_t length) { } std::string format_hex_pretty(const std::vector &data) { return format_hex_pretty(data.data(), data.size()); } +std::string format_bin(const uint8_t *data, size_t length) { + std::string result; + result.resize(length * 8); + for (size_t byte_idx = 0; byte_idx < length; byte_idx++) { + for (size_t bit_idx = 0; bit_idx < 8; bit_idx++) { + result[byte_idx * 8 + bit_idx] = ((data[byte_idx] >> (7 - bit_idx)) & 1) + '0'; + } + } + + return result; +} + ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) { if (on == nullptr && strcasecmp(str, "on") == 0) return PARSE_ON; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 43001bafdd..305ec47f76 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -420,6 +420,14 @@ template::value, int> = 0> std::stri return format_hex_pretty(reinterpret_cast(&val), sizeof(T)); } +/// Format the byte array \p data of length \p len in binary. +std::string format_bin(const uint8_t *data, size_t length); +/// Format an unsigned integer in binary, starting with the most significant byte. +template::value, int> = 0> std::string format_bin(T val) { + val = convert_big_endian(val); + return format_bin(reinterpret_cast(&val), sizeof(T)); +} + /// Return values for parse_on_off(). enum ParseOnOffState { PARSE_NONE = 0, From b027b6a711be401153847ca6607a4f7b90bc6688 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 15:57:40 -0600 Subject: [PATCH 0670/1052] [opentherm] Add nolint for 8266 static global (#7837) --- esphome/components/opentherm/opentherm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 9532a77821..3be0191c63 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -370,7 +370,7 @@ class OpenTherm { #ifdef ESP8266 // ESP8266 timer can accept callback with no parameters, so we have this hack to save a static instance of OpenTherm - static OpenTherm *instance; + static OpenTherm *instance; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #endif }; From bdb91112eada52df7c31a0b86e2f6f3b16ab9a49 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 16:20:03 -0600 Subject: [PATCH 0671/1052] [helpers] Add NOLINT for Mutex private field ``handle_`` (#7838) --- esphome/core/helpers.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 305ec47f76..fcbd8d8683 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -565,7 +565,8 @@ class Mutex { #if defined(USE_ESP32) || defined(USE_LIBRETINY) SemaphoreHandle_t handle_; #else - void *handle_; // d-pointer to store private data on new platforms + // d-pointer to store private data on new platforms + void *handle_; // NOLINT(clang-diagnostic-unused-private-field) #endif }; From d6f4f0509081ea38cfe799806c8225903e6cd1be Mon Sep 17 00:00:00 2001 From: programmingbgloDE <47243850+programmingbgloDE@users.noreply.github.com> Date: Mon, 25 Nov 2024 23:26:48 +0100 Subject: [PATCH 0672/1052] Add waveshare 1 45 in v2 b support (#7052) --- .../components/waveshare_epaper/display.py | 4 + .../waveshare_epaper/waveshare_epaper.cpp | 84 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 18 ++++ 3 files changed, 106 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 8287788de5..fbb5e1353d 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -27,6 +27,9 @@ WaveshareEPaperBWR = waveshare_epaper_ns.class_( WaveshareEPaperTypeA = waveshare_epaper_ns.class_( "WaveshareEPaperTypeA", WaveshareEPaper ) +WaveshareEpaper1P54INBV2 = waveshare_epaper_ns.class_( + "WaveshareEPaper1P54InBV2", WaveshareEPaperBWR +) WaveshareEPaper2P7In = waveshare_epaper_ns.class_( "WaveshareEPaper2P7In", WaveshareEPaper ) @@ -105,6 +108,7 @@ WaveshareEPaperTypeBModel = waveshare_epaper_ns.enum("WaveshareEPaperTypeBModel" MODELS = { "1.54in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN), "1.54inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_1_54_IN_V2), + "1.54inv2-b": ("b", WaveshareEpaper1P54INBV2), "2.13in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN), "2.13inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_13_IN_V2), "2.13in-ttgo": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 7c1d436673..1e27d594b8 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -808,6 +808,90 @@ void WaveshareEPaper2P7InV2::dump_config() { LOG_UPDATE_INTERVAL(this); } +// ======================================================== +// 1.54inch_v2_e-paper_b +// ======================================================== +// Datasheet: +// - https://files.waveshare.com/upload/9/9e/1.54inch-e-paper-b-v2-specification.pdf +// - https://www.waveshare.com/wiki/1.54inch_e-Paper_Module_(B)_Manual + +void WaveshareEPaper1P54InBV2::initialize() { + this->reset_(); + + this->wait_until_idle_(); + + this->command(0x12); + this->wait_until_idle_(); + + this->command(0x01); + this->data(0xC7); + this->data(0x00); + this->data(0x01); + + this->command(0x11); // data entry mode + this->data(0x01); + + this->command(0x44); // set Ram-X address start/end position + this->data(0x00); + this->data(0x18); // 0x18-->(24+1)*8=200 + + this->command(0x45); // set Ram-Y address start/end position + this->data(0xC7); // 0xC7-->(199+1)=200 + this->data(0x00); + this->data(0x00); + this->data(0x00); + + this->command(0x3C); // BorderWavefrom + this->data(0x05); + + this->command(0x18); // Read built-in temperature sensor + this->data(0x80); + + this->command(0x4E); // set RAM x address count to 0; + this->data(0x00); + this->command(0x4F); // set RAM y address count to 0X199; + this->data(0xC7); + this->data(0x00); + + this->wait_until_idle_(); +} + +void HOT WaveshareEPaper1P54InBV2::display() { + uint32_t buf_len_half = this->get_buffer_length_() >> 1; + this->initialize(); + + // COMMAND DATA START TRANSMISSION 1 (BLACK) + this->command(0x24); + delay(2); + for (uint32_t i = 0; i < buf_len_half; i++) { + this->data(~this->buffer_[i]); + } + delay(2); + + // COMMAND DATA START TRANSMISSION 2 (RED) + this->command(0x26); + delay(2); + for (uint32_t i = buf_len_half; i < buf_len_half * 2u; i++) { + this->data(this->buffer_[i]); + } + this->command(0x22); + this->data(0xf7); + this->command(0x20); + this->wait_until_idle_(); + + this->deep_sleep(); +} +int WaveshareEPaper1P54InBV2::get_height_internal() { return 200; } +int WaveshareEPaper1P54InBV2::get_width_internal() { return 200; } +void WaveshareEPaper1P54InBV2::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 1.54in V2 B"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + // ======================================================== // 2.7inch_e-paper_b // ======================================================== diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 7572982a20..a319b078d0 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -166,6 +166,24 @@ enum WaveshareEPaperTypeBModel { WAVESHARE_EPAPER_13_3_IN_K, }; +class WaveshareEPaper1P54InBV2 : public WaveshareEPaperBWR { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + this->command(0x10); + this->data(0x01); + } + + protected: + int get_width_internal() override; + int get_height_internal() override; +}; + class WaveshareEPaper2P7In : public WaveshareEPaper { public: void initialize() override; From 140d77061b33895054b35c6e1245fe4217f81a1e Mon Sep 17 00:00:00 2001 From: JonasB2497 <45214989+JonasB2497@users.noreply.github.com> Date: Mon, 25 Nov 2024 23:29:58 +0100 Subject: [PATCH 0673/1052] added Waveshare BWR Mode for the 7.5in Display (#7687) --- .../components/waveshare_epaper/display.py | 4 + .../waveshare_epaper/waveshare_epaper.cpp | 107 ++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 38 +++++++ .../waveshare_epaper/test.esp32-ard.yaml | 17 +++ .../waveshare_epaper/test.esp32-c3-ard.yaml | 16 +++ .../waveshare_epaper/test.esp32-c3-idf.yaml | 16 +++ .../waveshare_epaper/test.esp32-idf.yaml | 16 +++ .../waveshare_epaper/test.esp8266-ard.yaml | 16 +++ .../waveshare_epaper/test.rp2040-ard.yaml | 16 +++ 9 files changed, 246 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index fbb5e1353d..d5240b2674 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -79,6 +79,9 @@ WaveshareEPaper7P5InBV2 = waveshare_epaper_ns.class_( WaveshareEPaper7P5InBV3 = waveshare_epaper_ns.class_( "WaveshareEPaper7P5InBV3", WaveshareEPaper ) +WaveshareEPaper7P5InBV3BWR = waveshare_epaper_ns.class_( + "WaveshareEPaper7P5InBV3BWR", WaveshareEPaperBWR +) WaveshareEPaper7P5InV2 = waveshare_epaper_ns.class_( "WaveshareEPaper7P5InV2", WaveshareEPaper ) @@ -133,6 +136,7 @@ MODELS = { "7.50in": ("b", WaveshareEPaper7P5In), "7.50in-bv2": ("b", WaveshareEPaper7P5InBV2), "7.50in-bv3": ("b", WaveshareEPaper7P5InBV3), + "7.50in-bv3-bwr": ("b", WaveshareEPaper7P5InBV3BWR), "7.50in-bc": ("b", WaveshareEPaper7P5InBC), "7.50inv2": ("b", WaveshareEPaper7P5InV2), "7.50inv2alt": ("b", WaveshareEPaper7P5InV2alt), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 1e27d594b8..cb3b19aa1a 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -2399,6 +2399,113 @@ void WaveshareEPaper7P5InBV3::dump_config() { LOG_UPDATE_INTERVAL(this); } +void WaveshareEPaper7P5InBV3BWR::initialize() { this->init_display_(); } +bool WaveshareEPaper7P5InBV3BWR::wait_until_idle_() { + if (this->busy_pin_ == nullptr) { + return true; + } + + const uint32_t start = millis(); + while (this->busy_pin_->digital_read()) { + this->command(0x71); + if (millis() - start > this->idle_timeout_()) { + ESP_LOGI(TAG, "Timeout while displaying image!"); + return false; + } + App.feed_wdt(); + delay(10); + } + delay(200); // NOLINT + return true; +}; +void WaveshareEPaper7P5InBV3BWR::init_display_() { + this->reset_(); + + // COMMAND POWER SETTING + this->command(0x01); + + // 1-0=11: internal power + this->data(0x07); + this->data(0x17); // VGH&VGL + this->data(0x3F); // VSH + this->data(0x26); // VSL + this->data(0x11); // VSHR + + // VCOM DC Setting + this->command(0x82); + this->data(0x24); // VCOM + + // Booster Setting + this->command(0x06); + this->data(0x27); + this->data(0x27); + this->data(0x2F); + this->data(0x17); + + // POWER ON + this->command(0x04); + + delay(100); // NOLINT + this->wait_until_idle_(); + // COMMAND PANEL SETTING + this->command(0x00); + this->data(0x0F); // KW-3f KWR-2F BWROTP 0f BWOTP 1f + + // COMMAND RESOLUTION SETTING + this->command(0x61); + this->data(0x03); // source 800 + this->data(0x20); + this->data(0x01); // gate 480 + this->data(0xE0); + // COMMAND ...? + this->command(0x15); + this->data(0x00); + // COMMAND VCOM AND DATA INTERVAL SETTING + this->command(0x50); + this->data(0x20); + this->data(0x00); + // COMMAND TCON SETTING + this->command(0x60); + this->data(0x22); + // Resolution setting + this->command(0x65); + this->data(0x00); + this->data(0x00); // 800*480 + this->data(0x00); + this->data(0x00); +}; +void HOT WaveshareEPaper7P5InBV3BWR::display() { + this->init_display_(); + const uint32_t buf_len = this->get_buffer_length_() / 2u; + + this->command(0x10); // Send BW data Transmission + delay(2); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(this->buffer_[i]); + } + + this->command(0x13); // Send red data Transmission + delay(2); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(this->buffer_[i + buf_len]); + } + + this->command(0x12); // Display Refresh + delay(100); // NOLINT + this->wait_until_idle_(); + this->deep_sleep(); +} +int WaveshareEPaper7P5InBV3BWR::get_width_internal() { return 800; } +int WaveshareEPaper7P5InBV3BWR::get_height_internal() { return 480; } +void WaveshareEPaper7P5InBV3BWR::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 7.5in-bv3 BWR-Mode"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + void WaveshareEPaper7P5In::initialize() { // COMMAND POWER SETTING this->command(0x01); diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index a319b078d0..4544f7df59 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -637,6 +637,44 @@ class WaveshareEPaper7P5InBV3 : public WaveshareEPaper { void init_display_(); }; +class WaveshareEPaper7P5InBV3BWR : public WaveshareEPaperBWR { + public: + bool wait_until_idle_(); + + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + this->command(0x02); // Power off + this->wait_until_idle_(); + this->command(0x07); // Deep sleep + this->data(0xA5); + } + + void clear_screen(); + + protected: + int get_width_internal() override; + + int get_height_internal() override; + + void reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(true); + delay(200); // NOLINT + this->reset_pin_->digital_write(false); + delay(5); + this->reset_pin_->digital_write(true); + delay(200); // NOLINT + } + }; + + void init_display_(); +}; + class WaveshareEPaper7P5InBC : public WaveshareEPaper { public: void initialize() override; diff --git a/tests/components/waveshare_epaper/test.esp32-ard.yaml b/tests/components/waveshare_epaper/test.esp32-ard.yaml index 2f06c5c51b..944f98a1e9 100644 --- a/tests/components/waveshare_epaper/test.esp32-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp32-ard.yaml @@ -188,3 +188,20 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 7.50in-bv3-bwr + spi_id: spi_id_1 + cs_pin: + allow_other_uses: true + number: GPIO25 + dc_pin: + allow_other_uses: true + number: GPIO26 + busy_pin: + allow_other_uses: true + number: GPIO27 + reset_pin: + allow_other_uses: true + number: GPIO32 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml b/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml index 1c4547b7b4..5d651bd180 100644 --- a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml @@ -105,3 +105,19 @@ display: model: 1.54in-m5coreink-m09 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: + allow_other_uses: true + number: 4 + dc_pin: + allow_other_uses: true + number: 4 + busy_pin: + allow_other_uses: true + number: 4 + reset_pin: + allow_other_uses: true + number: 4 + model: 7.50in-bv3-bwr + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml index 1c4547b7b4..5d651bd180 100644 --- a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml @@ -105,3 +105,19 @@ display: model: 1.54in-m5coreink-m09 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: + allow_other_uses: true + number: 4 + dc_pin: + allow_other_uses: true + number: 4 + busy_pin: + allow_other_uses: true + number: 4 + reset_pin: + allow_other_uses: true + number: 4 + model: 7.50in-bv3-bwr + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/waveshare_epaper/test.esp32-idf.yaml b/tests/components/waveshare_epaper/test.esp32-idf.yaml index b6082fcfbf..47f894d967 100644 --- a/tests/components/waveshare_epaper/test.esp32-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-idf.yaml @@ -105,3 +105,19 @@ display: model: 1.54in-m5coreink-m09 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: + allow_other_uses: true + number: 4 + dc_pin: + allow_other_uses: true + number: 4 + busy_pin: + allow_other_uses: true + number: 4 + reset_pin: + allow_other_uses: true + number: 4 + model: 7.50in-bv3-bwr + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/waveshare_epaper/test.esp8266-ard.yaml b/tests/components/waveshare_epaper/test.esp8266-ard.yaml index 1f076a67be..ceda328598 100644 --- a/tests/components/waveshare_epaper/test.esp8266-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp8266-ard.yaml @@ -105,3 +105,19 @@ display: model: 1.54in-m5coreink-m09 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: + allow_other_uses: true + number: 4 + dc_pin: + allow_other_uses: true + number: 4 + busy_pin: + allow_other_uses: true + number: 4 + reset_pin: + allow_other_uses: true + number: 4 + model: 7.50in-bv3-bwr + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/waveshare_epaper/test.rp2040-ard.yaml b/tests/components/waveshare_epaper/test.rp2040-ard.yaml index 6050062d7e..be7e780033 100644 --- a/tests/components/waveshare_epaper/test.rp2040-ard.yaml +++ b/tests/components/waveshare_epaper/test.rp2040-ard.yaml @@ -105,3 +105,19 @@ display: model: 1.54in-m5coreink-m09 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + cs_pin: + allow_other_uses: true + number: 5 + dc_pin: + allow_other_uses: true + number: 5 + busy_pin: + allow_other_uses: true + number: 5 + reset_pin: + allow_other_uses: true + number: 5 + model: 7.50in-bv3-bwr + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); From 6ee02c47c29712c2b8cffa2ef5cebe25b29719a1 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 16:42:12 -0600 Subject: [PATCH 0674/1052] [homeassistant.number] Return when value not set (#7839) --- .../components/homeassistant/number/homeassistant_number.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/homeassistant/number/homeassistant_number.cpp b/esphome/components/homeassistant/number/homeassistant_number.cpp index d3e285f4ac..a7f71c3244 100644 --- a/esphome/components/homeassistant/number/homeassistant_number.cpp +++ b/esphome/components/homeassistant/number/homeassistant_number.cpp @@ -27,6 +27,7 @@ void HomeassistantNumber::min_retrieved_(const std::string &min) { auto min_value = parse_number(min); if (!min_value.has_value()) { ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_.c_str(), min.c_str()); + return; } ESP_LOGD(TAG, "'%s': Min retrieved: %s", get_name().c_str(), min.c_str()); this->traits.set_min_value(min_value.value()); @@ -36,6 +37,7 @@ void HomeassistantNumber::max_retrieved_(const std::string &max) { auto max_value = parse_number(max); if (!max_value.has_value()) { ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_.c_str(), max.c_str()); + return; } ESP_LOGD(TAG, "'%s': Max retrieved: %s", get_name().c_str(), max.c_str()); this->traits.set_max_value(max_value.value()); @@ -45,6 +47,7 @@ void HomeassistantNumber::step_retrieved_(const std::string &step) { auto step_value = parse_number(step); if (!step_value.has_value()) { ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_.c_str(), step.c_str()); + return; } ESP_LOGD(TAG, "'%s': Step Retrieved %s", get_name().c_str(), step.c_str()); this->traits.set_step(step_value.value()); From 4fbf41472a318bf923632508a3ba9473f06f3171 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 17:41:27 -0600 Subject: [PATCH 0675/1052] [CI] Add/update some system include paths (#7831) --- .clang-tidy | 6 ++++-- script/clang-tidy | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 946f2950d8..994416b2f1 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -63,15 +63,16 @@ Checks: >- -misc-non-private-member-variables-in-classes, -misc-no-recursion, -misc-unused-parameters, - -modernize-avoid-c-arrays, -modernize-avoid-bind, + -modernize-avoid-c-arrays, -modernize-concat-nested-namespaces, -modernize-return-braced-init-list, -modernize-use-auto, -modernize-use-default-member-init, -modernize-use-equals-default, - -modernize-use-trailing-return-type, -modernize-use-nodiscard, + -modernize-use-nullptr, + -modernize-use-trailing-return-type, -mpi-*, -objc-*, -readability-container-data-pointer, @@ -82,6 +83,7 @@ Checks: >- -readability-isolate-declaration, -readability-magic-numbers, -readability-make-member-function-const, + -readability-named-parameter, -readability-redundant-string-init, -readability-uppercase-literal-suffix, -readability-use-anyofallof, diff --git a/script/clang-tidy b/script/clang-tidy index 61199edce3..319fab70a2 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -100,10 +100,13 @@ def clang_options(idedata): # add library include directories using -isystem to suppress their errors for directory in list(idedata["includes"]["build"]): # skip our own directories, we add those later - if ( - not directory.startswith(f"{root_path}/") - or directory.startswith(f"{root_path}/.pio/") - or directory.startswith(f"{root_path}/managed_components/") + if not directory.startswith(f"{root_path}") or directory.startswith( + ( + f"{root_path}/.pio", + f"{root_path}/.platformio", + f"{root_path}/.temp", + f"{root_path}/managed_components", + ) ): cmd.extend(["-isystem", directory]) From f4766ab74fda60c5e43eaea00c1f91f2a5e77ce2 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 25 Nov 2024 13:58:21 -1000 Subject: [PATCH 0676/1052] [wifi] fix 32 char SSIDs (#7834) Co-authored-by: Samuel Sieb --- .../wifi/wifi_component_esp32_arduino.cpp | 24 +++++++++++++++---- .../wifi/wifi_component_esp8266.cpp | 24 +++++++++++++++---- .../wifi/wifi_component_esp_idf.cpp | 24 +++++++++++++++---- 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 18c706cb01..bc10bbd1e5 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -137,8 +137,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.sta.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), ap.get_password().size()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -746,7 +754,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.ap.ssid), sizeof(conf.ap.ssid), "%s", ap.get_ssid().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -757,7 +769,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - snprintf(reinterpret_cast(conf.ap.password), sizeof(conf.ap.password), "%s", ap.get_password().c_str()); + if (ap.get_password().size() > sizeof(conf.ap.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); } // pairwise cipher of SoftAP, group cipher will be derived using this. diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index a18d078967..8e1c2e70d8 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -236,8 +236,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { struct station_config conf {}; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.password), ap.get_password().c_str(), ap.get_password().size()); if (ap.get_bssid().has_value()) { conf.bssid_set = 1; @@ -775,7 +783,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; struct softap_config conf {}; - snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ssid_len = static_cast(ap.get_ssid().size()); conf.channel = ap.get_channel().value_or(1); conf.ssid_hidden = ap.get_hidden(); @@ -787,7 +799,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.password = 0; } else { conf.authmode = AUTH_WPA2_PSK; - snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); + if (ap.get_password().size() > sizeof(conf.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.password), ap.get_password().c_str(), ap.get_password().size()); } ETS_UART_INTR_DISABLE(); diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 1bf14ff40b..1af271345f 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -289,8 +289,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.sta.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), ap.get_password().size()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -902,7 +910,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strncpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); + if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -913,7 +925,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - strncpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password)); + if (ap.get_password().size() > sizeof(conf.ap.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); } // pairwise cipher of SoftAP, group cipher will be derived using this. From a70cee1dc1162da1ee5f1f078991aa2c68d421a3 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 25 Nov 2024 14:15:01 -1000 Subject: [PATCH 0677/1052] fix local time timestamp calculation (#7807) Co-authored-by: Samuel Sieb --- .../components/datetime/datetime_entity.cpp | 4 +-- esphome/core/time.cpp | 34 +++++++++++-------- esphome/core/time.h | 4 +-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index f215b7acb5..3d92194efa 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -60,9 +60,7 @@ ESPTime DateTimeEntity::state_as_esptime() const { obj.hour = this->hour_; obj.minute = this->minute_; obj.second = this->second_; - obj.day_of_week = 1; // Required to be valid for recalc_timestamp_local but not used. - obj.day_of_year = 1; // Required to be valid for recalc_timestamp_local but not used. - obj.recalc_timestamp_local(false); + obj.recalc_timestamp_local(); return obj; } diff --git a/esphome/core/time.cpp b/esphome/core/time.cpp index f7aa4fdddb..31977d972b 100644 --- a/esphome/core/time.cpp +++ b/esphome/core/time.cpp @@ -5,20 +5,18 @@ namespace esphome { -bool is_leap_year(uint32_t year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); } - uint8_t days_in_month(uint8_t month, uint16_t year) { static const uint8_t DAYS_IN_MONTH[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - uint8_t days = DAYS_IN_MONTH[month]; - if (month == 2 && is_leap_year(year)) + if (month == 2 && (year % 4 == 0)) return 29; - return days; + return DAYS_IN_MONTH[month]; } size_t ESPTime::strftime(char *buffer, size_t buffer_len, const char *format) { struct tm c_tm = this->to_c_tm(); return ::strftime(buffer, buffer_len, format, &c_tm); } + ESPTime ESPTime::from_c_tm(struct tm *c_tm, time_t c_time) { ESPTime res{}; res.second = uint8_t(c_tm->tm_sec); @@ -33,6 +31,7 @@ ESPTime ESPTime::from_c_tm(struct tm *c_tm, time_t c_time) { res.timestamp = c_time; return res; } + struct tm ESPTime::to_c_tm() { struct tm c_tm {}; c_tm.tm_sec = this->second; @@ -46,6 +45,7 @@ struct tm ESPTime::to_c_tm() { c_tm.tm_isdst = this->is_dst; return c_tm; } + std::string ESPTime::strftime(const std::string &format) { std::string timestr; timestr.resize(format.size() * 4); @@ -142,6 +142,7 @@ void ESPTime::increment_second() { this->year++; } } + void ESPTime::increment_day() { this->timestamp += 86400; @@ -159,23 +160,22 @@ void ESPTime::increment_day() { this->year++; } } + void ESPTime::recalc_timestamp_utc(bool use_day_of_year) { time_t res = 0; - if (!this->fields_in_range()) { this->timestamp = -1; return; } for (int i = 1970; i < this->year; i++) - res += is_leap_year(i) ? 366 : 365; + res += (year % 4 == 0) ? 366 : 365; if (use_day_of_year) { res += this->day_of_year - 1; } else { for (int i = 1; i < this->month; i++) res += days_in_month(i, this->year); - res += this->day_of_month - 1; } @@ -188,13 +188,17 @@ void ESPTime::recalc_timestamp_utc(bool use_day_of_year) { this->timestamp = res; } -void ESPTime::recalc_timestamp_local(bool use_day_of_year) { - this->recalc_timestamp_utc(use_day_of_year); - this->timestamp -= ESPTime::timezone_offset(); - ESPTime temp = ESPTime::from_epoch_local(this->timestamp); - if (temp.is_dst) { - this->timestamp -= 3600; - } +void ESPTime::recalc_timestamp_local() { + struct tm tm; + + tm.tm_year = this->year - 1900; + tm.tm_mon = this->month - 1; + tm.tm_mday = this->day_of_month; + tm.tm_hour = this->hour; + tm.tm_min = this->minute; + tm.tm_sec = this->second; + + this->timestamp = mktime(&tm); } int32_t ESPTime::timezone_offset() { diff --git a/esphome/core/time.h b/esphome/core/time.h index bce1108d93..5cbd9369fb 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -9,8 +9,6 @@ namespace esphome { template bool increment_time_value(T ¤t, uint16_t begin, uint16_t end); -bool is_leap_year(uint32_t year); - uint8_t days_in_month(uint8_t month, uint16_t year); /// A more user-friendly version of struct tm from time.h @@ -100,7 +98,7 @@ struct ESPTime { void recalc_timestamp_utc(bool use_day_of_year = true); /// Recalculate the timestamp field from the other fields of this ESPTime instance assuming local fields. - void recalc_timestamp_local(bool use_day_of_year = true); + void recalc_timestamp_local(); /// Convert this ESPTime instance back to a tm struct. struct tm to_c_tm(); From d9d368d38eff2024bf50ab9fc034d19ef91d6799 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 25 Nov 2024 14:21:47 -1000 Subject: [PATCH 0678/1052] add on_key trigger to matrix_keypad (#7830) Co-authored-by: Samuel Sieb --- esphome/components/matrix_keypad/__init__.py | 18 +++++++++++++++--- .../components/matrix_keypad/matrix_keypad.cpp | 4 ++++ .../components/matrix_keypad/matrix_keypad.h | 5 +++++ esphome/components/wiegand/__init__.py | 7 +++---- esphome/const.py | 1 + tests/components/matrix_keypad/common.yaml | 8 ++++++++ .../matrix_keypad/test.esp32-ard.yaml | 12 ++++-------- .../matrix_keypad/test.esp32-c3-ard.yaml | 12 ++++-------- .../matrix_keypad/test.esp32-c3-idf.yaml | 12 ++++-------- .../matrix_keypad/test.esp32-idf.yaml | 12 ++++-------- .../matrix_keypad/test.esp32-s3-idf.yaml | 15 +++++++++++++++ .../matrix_keypad/test.esp8266-ard.yaml | 12 ++++-------- .../matrix_keypad/test.rp2040-ard.yaml | 12 ++++-------- 13 files changed, 75 insertions(+), 55 deletions(-) create mode 100644 tests/components/matrix_keypad/common.yaml create mode 100644 tests/components/matrix_keypad/test.esp32-s3-idf.yaml diff --git a/esphome/components/matrix_keypad/__init__.py b/esphome/components/matrix_keypad/__init__.py index 5250a45732..b2bcde98ec 100644 --- a/esphome/components/matrix_keypad/__init__.py +++ b/esphome/components/matrix_keypad/__init__.py @@ -1,8 +1,8 @@ +from esphome import automation, pins import esphome.codegen as cg -import esphome.config_validation as cv -from esphome import pins from esphome.components import key_provider -from esphome.const import CONF_ID, CONF_PIN +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_ON_KEY, CONF_PIN, CONF_TRIGGER_ID CODEOWNERS = ["@ssieb"] @@ -14,6 +14,9 @@ matrix_keypad_ns = cg.esphome_ns.namespace("matrix_keypad") MatrixKeypad = matrix_keypad_ns.class_( "MatrixKeypad", key_provider.KeyProvider, cg.Component ) +MatrixKeyTrigger = matrix_keypad_ns.class_( + "MatrixKeyTrigger", automation.Trigger.template(cg.uint8) +) CONF_KEYPAD_ID = "keypad_id" CONF_ROWS = "rows" @@ -47,6 +50,11 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_DEBOUNCE_TIME, default=1): cv.int_range(min=1, max=100), cv.Optional(CONF_HAS_DIODES): cv.boolean, cv.Optional(CONF_HAS_PULLDOWNS): cv.boolean, + cv.Optional(CONF_ON_KEY): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MatrixKeyTrigger), + } + ), } ), check_keys, @@ -73,3 +81,7 @@ async def to_code(config): cg.add(var.set_has_diodes(config[CONF_HAS_DIODES])) if CONF_HAS_PULLDOWNS in config: cg.add(var.set_has_pulldowns(config[CONF_HAS_PULLDOWNS])) + for conf in config.get(CONF_ON_KEY, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) + cg.add(var.register_key_trigger(trigger)) + await automation.build_automation(trigger, [(cg.uint8, "x")], conf) diff --git a/esphome/components/matrix_keypad/matrix_keypad.cpp b/esphome/components/matrix_keypad/matrix_keypad.cpp index f62c75c869..8537997935 100644 --- a/esphome/components/matrix_keypad/matrix_keypad.cpp +++ b/esphome/components/matrix_keypad/matrix_keypad.cpp @@ -86,6 +86,8 @@ void MatrixKeypad::loop() { if (!this->keys_.empty()) { uint8_t keycode = this->keys_[key]; ESP_LOGD(TAG, "key '%c' pressed", keycode); + for (auto &trigger : this->key_triggers_) + trigger->trigger(keycode); for (auto &listener : this->listeners_) listener->key_pressed(keycode); this->send_key_(keycode); @@ -107,5 +109,7 @@ void MatrixKeypad::dump_config() { void MatrixKeypad::register_listener(MatrixKeypadListener *listener) { this->listeners_.push_back(listener); } +void MatrixKeypad::register_key_trigger(MatrixKeyTrigger *trig) { this->key_triggers_.push_back(trig); } + } // namespace matrix_keypad } // namespace esphome diff --git a/esphome/components/matrix_keypad/matrix_keypad.h b/esphome/components/matrix_keypad/matrix_keypad.h index d506040b7c..8b309b42c2 100644 --- a/esphome/components/matrix_keypad/matrix_keypad.h +++ b/esphome/components/matrix_keypad/matrix_keypad.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/components/key_provider/key_provider.h" +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" @@ -18,6 +19,8 @@ class MatrixKeypadListener { virtual void key_released(uint8_t key){}; }; +class MatrixKeyTrigger : public Trigger {}; + class MatrixKeypad : public key_provider::KeyProvider, public Component { public: void setup() override; @@ -31,6 +34,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component { void set_has_pulldowns(int has_pulldowns) { has_pulldowns_ = has_pulldowns; }; void register_listener(MatrixKeypadListener *listener); + void register_key_trigger(MatrixKeyTrigger *trig); protected: std::vector rows_; @@ -42,6 +46,7 @@ class MatrixKeypad : public key_provider::KeyProvider, public Component { int pressed_key_ = -1; std::vector listeners_{}; + std::vector key_triggers_; }; } // namespace matrix_keypad diff --git a/esphome/components/wiegand/__init__.py b/esphome/components/wiegand/__init__.py index 7b05c43198..962ac4c373 100644 --- a/esphome/components/wiegand/__init__.py +++ b/esphome/components/wiegand/__init__.py @@ -1,8 +1,8 @@ +from esphome import automation, pins import esphome.codegen as cg -import esphome.config_validation as cv -from esphome import pins, automation from esphome.components import key_provider -from esphome.const import CONF_ID, CONF_ON_TAG, CONF_TRIGGER_ID +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_ON_KEY, CONF_ON_TAG, CONF_TRIGGER_ID CODEOWNERS = ["@ssieb"] @@ -25,7 +25,6 @@ WiegandKeyTrigger = wiegand_ns.class_( CONF_D0 = "d0" CONF_D1 = "d1" -CONF_ON_KEY = "on_key" CONF_ON_RAW = "on_raw" CONFIG_SCHEMA = cv.Schema( diff --git a/esphome/const.py b/esphome/const.py index 50528b7363..d2df83aa43 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -575,6 +575,7 @@ CONF_ON_FINGER_SCAN_UNMATCHED = "on_finger_scan_unmatched" CONF_ON_FINISHED_WRITE = "on_finished_write" CONF_ON_IDLE = "on_idle" CONF_ON_JSON_MESSAGE = "on_json_message" +CONF_ON_KEY = "on_key" CONF_ON_LOCK = "on_lock" CONF_ON_LOOP = "on_loop" CONF_ON_MESSAGE = "on_message" diff --git a/tests/components/matrix_keypad/common.yaml b/tests/components/matrix_keypad/common.yaml new file mode 100644 index 0000000000..32e334d890 --- /dev/null +++ b/tests/components/matrix_keypad/common.yaml @@ -0,0 +1,8 @@ +binary_sensor: + - platform: matrix_keypad + id: key4 + row: 1 + col: 1 + - platform: matrix_keypad + id: key1 + key: 1 diff --git a/tests/components/matrix_keypad/test.esp32-ard.yaml b/tests/components/matrix_keypad/test.esp32-ard.yaml index c8e9b54534..70bb70638d 100644 --- a/tests/components/matrix_keypad/test.esp32-ard.yaml +++ b/tests/components/matrix_keypad/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -binary_sensor: - - platform: matrix_keypad - id: key4 - row: 1 - col: 1 - - platform: matrix_keypad - id: key1 - key: 1 +packages: + common: !include common.yaml matrix_keypad: id: keypad @@ -17,3 +11,5 @@ matrix_keypad: - pin: 15 keys: "1234" has_pulldowns: true + on_key: + - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/matrix_keypad/test.esp32-c3-ard.yaml b/tests/components/matrix_keypad/test.esp32-c3-ard.yaml index d15e6af21a..75d9c0b263 100644 --- a/tests/components/matrix_keypad/test.esp32-c3-ard.yaml +++ b/tests/components/matrix_keypad/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -binary_sensor: - - platform: matrix_keypad - id: key4 - row: 1 - col: 1 - - platform: matrix_keypad - id: key1 - key: 1 +packages: + common: !include common.yaml matrix_keypad: id: keypad @@ -17,3 +11,5 @@ matrix_keypad: - pin: 4 keys: "1234" has_pulldowns: true + on_key: + - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/matrix_keypad/test.esp32-c3-idf.yaml b/tests/components/matrix_keypad/test.esp32-c3-idf.yaml index d15e6af21a..75d9c0b263 100644 --- a/tests/components/matrix_keypad/test.esp32-c3-idf.yaml +++ b/tests/components/matrix_keypad/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -binary_sensor: - - platform: matrix_keypad - id: key4 - row: 1 - col: 1 - - platform: matrix_keypad - id: key1 - key: 1 +packages: + common: !include common.yaml matrix_keypad: id: keypad @@ -17,3 +11,5 @@ matrix_keypad: - pin: 4 keys: "1234" has_pulldowns: true + on_key: + - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/matrix_keypad/test.esp32-idf.yaml b/tests/components/matrix_keypad/test.esp32-idf.yaml index c8e9b54534..70bb70638d 100644 --- a/tests/components/matrix_keypad/test.esp32-idf.yaml +++ b/tests/components/matrix_keypad/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -binary_sensor: - - platform: matrix_keypad - id: key4 - row: 1 - col: 1 - - platform: matrix_keypad - id: key1 - key: 1 +packages: + common: !include common.yaml matrix_keypad: id: keypad @@ -17,3 +11,5 @@ matrix_keypad: - pin: 15 keys: "1234" has_pulldowns: true + on_key: + - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/matrix_keypad/test.esp32-s3-idf.yaml b/tests/components/matrix_keypad/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..a491f2ed59 --- /dev/null +++ b/tests/components/matrix_keypad/test.esp32-s3-idf.yaml @@ -0,0 +1,15 @@ +packages: + common: !include common.yaml + +matrix_keypad: + id: keypad + rows: + - pin: 10 + - pin: 11 + columns: + - pin: 12 + - pin: 13 + keys: "1234" + has_pulldowns: true + on_key: + - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/matrix_keypad/test.esp8266-ard.yaml b/tests/components/matrix_keypad/test.esp8266-ard.yaml index c8e9b54534..70bb70638d 100644 --- a/tests/components/matrix_keypad/test.esp8266-ard.yaml +++ b/tests/components/matrix_keypad/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -binary_sensor: - - platform: matrix_keypad - id: key4 - row: 1 - col: 1 - - platform: matrix_keypad - id: key1 - key: 1 +packages: + common: !include common.yaml matrix_keypad: id: keypad @@ -17,3 +11,5 @@ matrix_keypad: - pin: 15 keys: "1234" has_pulldowns: true + on_key: + - lambda: ESP_LOGI("KEY", "key %d pressed", x); diff --git a/tests/components/matrix_keypad/test.rp2040-ard.yaml b/tests/components/matrix_keypad/test.rp2040-ard.yaml index d15e6af21a..75d9c0b263 100644 --- a/tests/components/matrix_keypad/test.rp2040-ard.yaml +++ b/tests/components/matrix_keypad/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -binary_sensor: - - platform: matrix_keypad - id: key4 - row: 1 - col: 1 - - platform: matrix_keypad - id: key1 - key: 1 +packages: + common: !include common.yaml matrix_keypad: id: keypad @@ -17,3 +11,5 @@ matrix_keypad: - pin: 4 keys: "1234" has_pulldowns: true + on_key: + - lambda: ESP_LOGI("KEY", "key %d pressed", x); From c0dcecc465fa8b0d3205880174ca820bc8bfd3d3 Mon Sep 17 00:00:00 2001 From: Citric Lee <37475446+limengdu@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:53:21 +0800 Subject: [PATCH 0679/1052] Add: Seeed Studio mr60fda2 mmwave sensor (#7576) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Spencer Yan --- CODEOWNERS | 1 + esphome/components/seeed_mr60fda2/__init__.py | 41 ++ .../seeed_mr60fda2/binary_sensor.py | 33 ++ .../seeed_mr60fda2/button/__init__.py | 45 +++ .../button/get_radar_parameters_button.cpp | 9 + .../button/get_radar_parameters_button.h | 18 + .../button/reset_radar_button.cpp | 9 + .../button/reset_radar_button.h | 18 + .../seeed_mr60fda2/seeed_mr60fda2.cpp | 368 ++++++++++++++++++ .../seeed_mr60fda2/seeed_mr60fda2.h | 101 +++++ .../seeed_mr60fda2/select/__init__.py | 59 +++ .../select/height_threshold_select.cpp | 15 + .../select/height_threshold_select.h | 18 + .../select/install_height_select.cpp | 15 + .../select/install_height_select.h | 18 + .../select/sensitivity_select.cpp | 15 + .../select/sensitivity_select.h | 18 + tests/components/seeed_mr60fda2/common.yaml | 34 ++ .../seeed_mr60fda2/test.esp32-c3-ard.yaml | 5 + .../seeed_mr60fda2/test.esp32-c3-idf.yaml | 5 + 20 files changed, 845 insertions(+) create mode 100644 esphome/components/seeed_mr60fda2/__init__.py create mode 100644 esphome/components/seeed_mr60fda2/binary_sensor.py create mode 100644 esphome/components/seeed_mr60fda2/button/__init__.py create mode 100644 esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.cpp create mode 100644 esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.h create mode 100644 esphome/components/seeed_mr60fda2/button/reset_radar_button.cpp create mode 100644 esphome/components/seeed_mr60fda2/button/reset_radar_button.h create mode 100644 esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp create mode 100644 esphome/components/seeed_mr60fda2/seeed_mr60fda2.h create mode 100644 esphome/components/seeed_mr60fda2/select/__init__.py create mode 100644 esphome/components/seeed_mr60fda2/select/height_threshold_select.cpp create mode 100644 esphome/components/seeed_mr60fda2/select/height_threshold_select.h create mode 100644 esphome/components/seeed_mr60fda2/select/install_height_select.cpp create mode 100644 esphome/components/seeed_mr60fda2/select/install_height_select.h create mode 100644 esphome/components/seeed_mr60fda2/select/sensitivity_select.cpp create mode 100644 esphome/components/seeed_mr60fda2/select/sensitivity_select.h create mode 100644 tests/components/seeed_mr60fda2/common.yaml create mode 100644 tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml create mode 100644 tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index dd3926d283..fb6d11d1fb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -354,6 +354,7 @@ esphome/components/sdl/* @clydebarrow esphome/components/sdm_meter/* @jesserockz @polyfaces esphome/components/sdp3x/* @Azimath esphome/components/seeed_mr24hpc1/* @limengdu +esphome/components/seeed_mr60fda2/* @limengdu esphome/components/selec_meter/* @sourabhjaiswal esphome/components/select/* @esphome/core esphome/components/sen0321/* @notjj diff --git a/esphome/components/seeed_mr60fda2/__init__.py b/esphome/components/seeed_mr60fda2/__init__.py new file mode 100644 index 0000000000..e79134deec --- /dev/null +++ b/esphome/components/seeed_mr60fda2/__init__.py @@ -0,0 +1,41 @@ +import esphome.codegen as cg +from esphome.components import uart +import esphome.config_validation as cv +from esphome.const import CONF_ID + +CODEOWNERS = ["@limengdu"] +DEPENDENCIES = ["uart"] +MULTI_CONF = True + +mr60fda2_ns = cg.esphome_ns.namespace("seeed_mr60fda2") + +MR60FDA2Component = mr60fda2_ns.class_( + "MR60FDA2Component", cg.Component, uart.UARTDevice +) + +CONF_MR60FDA2_ID = "mr60fda2_id" + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MR60FDA2Component), + } + ) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + +FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( + "seeed_mr60fda2", + require_tx=True, + require_rx=True, + baud_rate=115200, + parity="NONE", + stop_bits=1, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) diff --git a/esphome/components/seeed_mr60fda2/binary_sensor.py b/esphome/components/seeed_mr60fda2/binary_sensor.py new file mode 100644 index 0000000000..2860ac0100 --- /dev/null +++ b/esphome/components/seeed_mr60fda2/binary_sensor.py @@ -0,0 +1,33 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +import esphome.config_validation as cv +from esphome.const import DEVICE_CLASS_OCCUPANCY, DEVICE_CLASS_SAFETY + +from . import CONF_MR60FDA2_ID, MR60FDA2Component + +DEPENDENCIES = ["seeed_mr60fda2"] + +CONF_PEOPLE_EXIST = "people_exist" +CONF_FALL_DETECTED = "fall_detected" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_MR60FDA2_ID): cv.use_id(MR60FDA2Component), + cv.Optional(CONF_PEOPLE_EXIST): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_OCCUPANCY, icon="mdi:motion-sensor" + ), + cv.Optional(CONF_FALL_DETECTED): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_SAFETY, icon="mdi:emergency" + ), +} + + +async def to_code(config): + mr60fda2_component = await cg.get_variable(config[CONF_MR60FDA2_ID]) + + if people_exist_config := config.get(CONF_PEOPLE_EXIST): + sens = await binary_sensor.new_binary_sensor(people_exist_config) + cg.add(mr60fda2_component.set_people_exist_binary_sensor(sens)) + + if is_fall_config := config.get(CONF_FALL_DETECTED): + sens = await binary_sensor.new_binary_sensor(is_fall_config) + cg.add(mr60fda2_component.set_fall_detected_binary_sensor(sens)) diff --git a/esphome/components/seeed_mr60fda2/button/__init__.py b/esphome/components/seeed_mr60fda2/button/__init__.py new file mode 100644 index 0000000000..1415dc27ca --- /dev/null +++ b/esphome/components/seeed_mr60fda2/button/__init__.py @@ -0,0 +1,45 @@ +import esphome.codegen as cg +from esphome.components import button +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_RESTART, + DEVICE_CLASS_UPDATE, + ENTITY_CATEGORY_DIAGNOSTIC, + ENTITY_CATEGORY_NONE, + CONF_FACTORY_RESET, +) + +from .. import CONF_MR60FDA2_ID, MR60FDA2Component, mr60fda2_ns + +DEPENDENCIES = ["seeed_mr60fda2"] + +GetRadarParametersButton = mr60fda2_ns.class_("GetRadarParametersButton", button.Button) +ResetRadarButton = mr60fda2_ns.class_("ResetRadarButton", button.Button) + +CONF_GET_RADAR_PARAMETERS = "get_radar_parameters" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_MR60FDA2_ID): cv.use_id(MR60FDA2Component), + cv.Optional(CONF_GET_RADAR_PARAMETERS): button.button_schema( + GetRadarParametersButton, + device_class=DEVICE_CLASS_UPDATE, + entity_category=ENTITY_CATEGORY_NONE, + ), + cv.Optional(CONF_FACTORY_RESET): button.button_schema( + ResetRadarButton, + device_class=DEVICE_CLASS_RESTART, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + ), +} + + +async def to_code(config): + mr60fda2_component = await cg.get_variable(config[CONF_MR60FDA2_ID]) + if get_radar_parameters_config := config.get(CONF_GET_RADAR_PARAMETERS): + b = await button.new_button(get_radar_parameters_config) + await cg.register_parented(b, config[CONF_MR60FDA2_ID]) + cg.add(mr60fda2_component.set_get_radar_parameters_button(b)) + if factory_reset_config := config.get(CONF_FACTORY_RESET): + b = await button.new_button(factory_reset_config) + await cg.register_parented(b, config[CONF_MR60FDA2_ID]) + cg.add(mr60fda2_component.set_factory_reset_button(b)) diff --git a/esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.cpp b/esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.cpp new file mode 100644 index 0000000000..88be6dfe7c --- /dev/null +++ b/esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.cpp @@ -0,0 +1,9 @@ +#include "get_radar_parameters_button.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +void GetRadarParametersButton::press_action() { this->parent_->get_radar_parameters(); } + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.h b/esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.h new file mode 100644 index 0000000000..9d6d507383 --- /dev/null +++ b/esphome/components/seeed_mr60fda2/button/get_radar_parameters_button.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/button/button.h" +#include "../seeed_mr60fda2.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +class GetRadarParametersButton : public button::Button, public Parented { + public: + GetRadarParametersButton() = default; + + protected: + void press_action() override; +}; + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/button/reset_radar_button.cpp b/esphome/components/seeed_mr60fda2/button/reset_radar_button.cpp new file mode 100644 index 0000000000..0a5833a18c --- /dev/null +++ b/esphome/components/seeed_mr60fda2/button/reset_radar_button.cpp @@ -0,0 +1,9 @@ +#include "reset_radar_button.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +void ResetRadarButton::press_action() { this->parent_->factory_reset(); } + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/button/reset_radar_button.h b/esphome/components/seeed_mr60fda2/button/reset_radar_button.h new file mode 100644 index 0000000000..66780fb8af --- /dev/null +++ b/esphome/components/seeed_mr60fda2/button/reset_radar_button.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/button/button.h" +#include "../seeed_mr60fda2.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +class ResetRadarButton : public button::Button, public Parented { + public: + ResetRadarButton() = default; + + protected: + void press_action() override; +}; + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp new file mode 100644 index 0000000000..d183a1f77f --- /dev/null +++ b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp @@ -0,0 +1,368 @@ +#include "seeed_mr60fda2.h" +#include "esphome/core/log.h" + +#include +#include + +namespace esphome { +namespace seeed_mr60fda2 { + +static const char *const TAG = "seeed_mr60fda2"; + +// Prints the component's configuration data. dump_config() prints all of the component's configuration +// items in an easy-to-read format, including the configuration key-value pairs. +void MR60FDA2Component::dump_config() { + ESP_LOGCONFIG(TAG, "MR60FDA2:"); +#ifdef USE_BINARY_SENSOR + LOG_BINARY_SENSOR(" ", "People Exist Binary Sensor", this->people_exist_binary_sensor_); + LOG_BINARY_SENSOR(" ", "Is Fall Binary Sensor", this->fall_detected_binary_sensor_); +#endif +#ifdef USE_BUTTON + LOG_BUTTON(" ", "Get Radar Parameters Button", this->get_radar_parameters_button_); + LOG_BUTTON(" ", "Reset Radar Button", this->factory_reset_button_); +#endif +#ifdef USE_SELECT + LOG_SELECT(" ", "Install Height Select", this->install_height_select_); + LOG_SELECT(" ", "Height Threshold Select", this->height_threshold_select_); + LOG_SELECT(" ", "Sensitivity Select", this->sensitivity_select_); +#endif +} + +// Initialisation functions +void MR60FDA2Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up MR60FDA2..."); + this->check_uart_settings(115200); + + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + this->current_frame_id_ = 0; + this->current_frame_len_ = 0; + this->current_data_frame_len_ = 0; + this->current_frame_type_ = 0; + this->get_radar_parameters(); + + memset(this->current_frame_buf_, 0, FRAME_BUF_MAX_SIZE); + memset(this->current_data_buf_, 0, DATA_BUF_MAX_SIZE); + + ESP_LOGCONFIG(TAG, "Set up MR60FDA2 complete"); +} + +// main loop +void MR60FDA2Component::loop() { + uint8_t byte; + + // Is there data on the serial port + while (this->available()) { + this->read_byte(&byte); + this->split_frame_(byte); // split data frame + } +} + +/** + * @brief Calculate the checksum for a byte array. + * + * This function calculates the checksum for the provided byte array using an + * XOR-based checksum algorithm. + * + * @param data The byte array to calculate the checksum for. + * @param len The length of the byte array. + * @return The calculated checksum. + */ +static uint8_t calculate_checksum(const uint8_t *data, size_t len) { + uint8_t checksum = 0; + for (size_t i = 0; i < len; i++) { + checksum ^= data[i]; + } + checksum = ~checksum; + return checksum; +} + +/** + * @brief Validate the checksum of a byte array. + * + * This function validates the checksum of the provided byte array by comparing + * it to the expected checksum. + * + * @param data The byte array to validate. + * @param len The length of the byte array. + * @param expected_checksum The expected checksum. + * @return True if the checksum is valid, false otherwise. + */ +static bool validate_checksum(const uint8_t *data, size_t len, uint8_t expected_checksum) { + return calculate_checksum(data, len) == expected_checksum; +} + +static uint8_t find_nearest_index(float value, const float *arr, int size) { + int nearest_index = 0; + float min_diff = std::abs(value - arr[0]); + for (int i = 1; i < size; ++i) { + float diff = std::abs(value - arr[i]); + if (diff < min_diff) { + min_diff = diff; + nearest_index = i; + } + } + return nearest_index; +} + +/** + * @brief Convert a float value to a byte array. + * + * This function converts a float value to a byte array. + * + * @param value The float value to convert. + * @param bytes The byte array to store the converted value. + */ +static void float_to_bytes(float value, unsigned char *bytes) { + union { + float float_value; + unsigned char byte_array[4]; + } u; + + u.float_value = value; + memcpy(bytes, u.byte_array, 4); +} + +/** + * @brief Convert a 32-bit unsigned integer to a byte array. + * + * This function converts a 32-bit unsigned integer to a byte array. + * + * @param value The 32-bit unsigned integer to convert. + * @param bytes The byte array to store the converted value. + */ +static void int_to_bytes(uint32_t value, unsigned char *bytes) { + bytes[0] = value & 0xFF; + bytes[1] = (value >> 8) & 0xFF; + bytes[2] = (value >> 16) & 0xFF; + bytes[3] = (value >> 24) & 0xFF; +} + +void MR60FDA2Component::split_frame_(uint8_t buffer) { + switch (this->current_frame_locate_) { + case LOCATE_FRAME_HEADER: // starting buffer + if (buffer == FRAME_HEADER_BUFFER) { + this->current_frame_len_ = 1; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + } + break; + case LOCATE_ID_FRAME1: + this->current_frame_id_ = buffer << 8; + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + break; + case LOCATE_ID_FRAME2: + this->current_frame_id_ += buffer; + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + break; + case LOCATE_LENGTH_FRAME_H: + this->current_data_frame_len_ = buffer << 8; + if (this->current_data_frame_len_ == 0x00) { + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + } else { + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + } + break; + case LOCATE_LENGTH_FRAME_L: + this->current_data_frame_len_ += buffer; + if (this->current_data_frame_len_ > DATA_BUF_MAX_SIZE) { + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + } else { + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + } + break; + case LOCATE_TYPE_FRAME1: + this->current_frame_type_ = buffer << 8; + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + break; + case LOCATE_TYPE_FRAME2: + this->current_frame_type_ += buffer; + if ((this->current_frame_type_ == IS_FALL_TYPE_BUFFER) || + (this->current_frame_type_ == PEOPLE_EXIST_TYPE_BUFFER) || + (this->current_frame_type_ == RESULT_INSTALL_HEIGHT) || (this->current_frame_type_ == RESULT_PARAMETERS) || + (this->current_frame_type_ == RESULT_HEIGHT_THRESHOLD) || (this->current_frame_type_ == RESULT_SENSITIVITY)) { + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + } else { + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + } + break; + case LOCATE_HEAD_CKSUM_FRAME: + if (validate_checksum(this->current_frame_buf_, this->current_frame_len_, buffer)) { + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + } else { + ESP_LOGD(TAG, "HEAD_CKSUM_FRAME ERROR: 0x%02x", buffer); + ESP_LOGV(TAG, "CURRENT_FRAME: %s %s", + format_hex_pretty(this->current_frame_buf_, this->current_frame_len_).c_str(), + format_hex_pretty(&buffer, 1).c_str()); + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + } + break; + case LOCATE_DATA_FRAME: + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_data_buf_[this->current_frame_len_ - LEN_TO_DATA_FRAME] = buffer; + if (this->current_frame_len_ - LEN_TO_HEAD_CKSUM == this->current_data_frame_len_) { + this->current_frame_locate_++; + } + if (this->current_frame_len_ > FRAME_BUF_MAX_SIZE) { + ESP_LOGD(TAG, "PRACTICE_DATA_FRAME_LEN ERROR: %d", this->current_frame_len_ - LEN_TO_HEAD_CKSUM); + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + } + break; + case LOCATE_DATA_CKSUM_FRAME: + if (validate_checksum(this->current_data_buf_, this->current_data_frame_len_, buffer)) { + this->current_frame_len_++; + this->current_frame_buf_[this->current_frame_len_ - 1] = buffer; + this->current_frame_locate_++; + this->process_frame_(); + } else { + ESP_LOGD(TAG, "DATA_CKSUM_FRAME ERROR: 0x%02x", buffer); + ESP_LOGV(TAG, "GET CURRENT_FRAME: %s %s", + format_hex_pretty(this->current_frame_buf_, this->current_frame_len_).c_str(), + format_hex_pretty(&buffer, 1).c_str()); + + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + } + break; + default: + break; + } +} + +void MR60FDA2Component::process_frame_() { + switch (this->current_frame_type_) { + case IS_FALL_TYPE_BUFFER: + if (this->fall_detected_binary_sensor_ != nullptr) { + this->fall_detected_binary_sensor_->publish_state(this->current_frame_buf_[LEN_TO_HEAD_CKSUM]); + } + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + break; + + case PEOPLE_EXIST_TYPE_BUFFER: + if (this->people_exist_binary_sensor_ != nullptr) + this->people_exist_binary_sensor_->publish_state(this->current_frame_buf_[LEN_TO_HEAD_CKSUM]); + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + break; + + case RESULT_INSTALL_HEIGHT: + if (this->current_data_buf_[0]) { + ESP_LOGD(TAG, "Successfully set the mounting height"); + } else { + ESP_LOGD(TAG, "Failed to set the mounting height"); + } + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + break; + + case RESULT_HEIGHT_THRESHOLD: + if (this->current_data_buf_[0]) { + ESP_LOGD(TAG, "Successfully set the height threshold"); + } else { + ESP_LOGD(TAG, "Failed to set the height threshold"); + } + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + break; + + case RESULT_SENSITIVITY: + if (this->current_data_buf_[0]) { + ESP_LOGD(TAG, "Successfully set the sensitivity"); + } else { + ESP_LOGD(TAG, "Failed to set the sensitivity"); + } + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + break; + + case RESULT_PARAMETERS: { + float install_height_float = 0; + float height_threshold_float = 0; + uint32_t current_sensitivity = 0; + if (this->install_height_select_ != nullptr) { + uint32_t current_install_height_int = + encode_uint32(current_data_buf_[3], current_data_buf_[2], current_data_buf_[1], current_data_buf_[0]); + + install_height_float = bit_cast(current_install_height_int); + uint32_t select_index = find_nearest_index(install_height_float, INSTALL_HEIGHT, 7); + this->install_height_select_->publish_state(this->install_height_select_->at(select_index).value()); + } + + if (this->height_threshold_select_ != nullptr) { + uint32_t current_height_threshold_int = + encode_uint32(current_data_buf_[7], current_data_buf_[6], current_data_buf_[5], current_data_buf_[4]); + + height_threshold_float = bit_cast(current_height_threshold_int); + size_t select_index = find_nearest_index(height_threshold_float, HEIGHT_THRESHOLD, 7); + this->height_threshold_select_->publish_state(this->height_threshold_select_->at(select_index).value()); + } + + if (this->sensitivity_select_ != nullptr) { + current_sensitivity = + encode_uint32(current_data_buf_[11], current_data_buf_[10], current_data_buf_[9], current_data_buf_[8]); + + uint32_t select_index = find_nearest_index(current_sensitivity, SENSITIVITY, 3); + this->sensitivity_select_->publish_state(this->sensitivity_select_->at(select_index).value()); + } + + ESP_LOGD(TAG, "Mounting height: %.2f, Height threshold: %.2f, Sensitivity: %" PRIu32, install_height_float, + height_threshold_float, current_sensitivity); + this->current_frame_locate_ = LOCATE_FRAME_HEADER; + break; + } + default: + break; + } +} + +// Send Heartbeat Packet Command +void MR60FDA2Component::set_install_height(uint8_t index) { + uint8_t send_data[13] = {0x01, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x04, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00}; + float_to_bytes(INSTALL_HEIGHT[index], &send_data[8]); + send_data[12] = calculate_checksum(send_data + 8, 4); + this->write_array(send_data, 13); + ESP_LOGV(TAG, "SEND INSTALL HEIGHT FRAME: %s", format_hex_pretty(send_data, 13).c_str()); +} + +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]); + 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()); +} + +void MR60FDA2Component::set_sensitivity(uint8_t index) { + uint8_t send_data[13] = {0x01, 0x00, 0x00, 0x00, 0x04, 0x0E, 0x0A, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00}; + + int_to_bytes(SENSITIVITY[index], &send_data[8]); + + send_data[12] = calculate_checksum(send_data + 8, 4); + this->write_array(send_data, 13); + ESP_LOGV(TAG, "SEND SET SENSITIVITY: %s", format_hex_pretty(send_data, 13).c_str()); +} + +void MR60FDA2Component::get_radar_parameters() { + uint8_t send_data[8] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x06, 0xF6}; + this->write_array(send_data, 8); + ESP_LOGV(TAG, "SEND GET PARAMETERS: %s", format_hex_pretty(send_data, 8).c_str()); +} + +void MR60FDA2Component::factory_reset() { + uint8_t send_data[8] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x21, 0x10, 0xCF}; + this->write_array(send_data, 8); + ESP_LOGV(TAG, "SEND RESET: %s", format_hex_pretty(send_data, 8).c_str()); + this->get_radar_parameters(); +} + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/seeed_mr60fda2.h b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.h new file mode 100644 index 0000000000..e1ffa4f071 --- /dev/null +++ b/esphome/components/seeed_mr60fda2/seeed_mr60fda2.h @@ -0,0 +1,101 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif +#ifdef USE_BUTTON +#include "esphome/components/button/button.h" +#endif +#ifdef USE_SELECT +#include "esphome/components/select/select.h" +#endif +#ifdef USE_TEXT_SENSOR +#include "esphome/components/text_sensor/text_sensor.h" +#endif +#include "esphome/components/uart/uart.h" +#include "esphome/core/automation.h" +#include "esphome/core/helpers.h" + +#include + +namespace esphome { +namespace seeed_mr60fda2 { + +static const uint8_t DATA_BUF_MAX_SIZE = 28; +static const uint8_t FRAME_BUF_MAX_SIZE = 37; +static const uint8_t LEN_TO_HEAD_CKSUM = 8; +static const uint8_t LEN_TO_DATA_FRAME = 9; + +static const uint8_t FRAME_HEADER_BUFFER = 0x01; +static const uint16_t IS_FALL_TYPE_BUFFER = 0x0E02; +static const uint16_t PEOPLE_EXIST_TYPE_BUFFER = 0x0F09; +static const uint16_t RESULT_INSTALL_HEIGHT = 0x0E04; +static const uint16_t RESULT_PARAMETERS = 0x0E06; +static const uint16_t RESULT_HEIGHT_THRESHOLD = 0x0E08; +static const uint16_t RESULT_SENSITIVITY = 0x0E0A; + +enum FrameLocation { + LOCATE_FRAME_HEADER, + LOCATE_ID_FRAME1, + LOCATE_ID_FRAME2, + LOCATE_LENGTH_FRAME_H, + LOCATE_LENGTH_FRAME_L, + LOCATE_TYPE_FRAME1, + LOCATE_TYPE_FRAME2, + LOCATE_HEAD_CKSUM_FRAME, // Header checksum: [from the first byte to the previous byte of the HEAD_CKSUM bit] + LOCATE_DATA_FRAME, + LOCATE_DATA_CKSUM_FRAME, // Data checksum: [from the first to the previous byte of the DATA_CKSUM bit] + LOCATE_PROCESS_FRAME, +}; + +static const float INSTALL_HEIGHT[7] = {2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f}; +static const float HEIGHT_THRESHOLD[7] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f}; +static const float SENSITIVITY[3] = {3, 15, 30}; + +static const char *const INSTALL_HEIGHT_STR[7] = {"2.4m", "2.5m", "2.6", "2.7m", "2.8", "2.9m", "3.0m"}; +static const char *const HEIGHT_THRESHOLD_STR[7] = {"0.0m", "0.1m", "0.2m", "0.3m", "0.4m", "0.5m", "0.6m"}; +static const char *const SENSITIVITY_STR[3] = {"1", "2", "3"}; + +class MR60FDA2Component : public Component, + public uart::UARTDevice { // The class name must be the name defined by text_sensor.py +#ifdef USE_BINARY_SENSOR + SUB_BINARY_SENSOR(people_exist) + SUB_BINARY_SENSOR(fall_detected) +#endif +#ifdef USE_BUTTON + SUB_BUTTON(get_radar_parameters) + SUB_BUTTON(factory_reset) +#endif +#ifdef USE_SELECT + SUB_SELECT(install_height) + SUB_SELECT(height_threshold) + SUB_SELECT(sensitivity) +#endif + + protected: + uint8_t current_frame_locate_; + uint8_t current_frame_buf_[FRAME_BUF_MAX_SIZE]; + uint8_t current_data_buf_[DATA_BUF_MAX_SIZE]; + uint16_t current_frame_id_; + size_t current_frame_len_; + size_t current_data_frame_len_; + uint16_t current_frame_type_; + + void split_frame_(uint8_t buffer); + void process_frame_(); + + public: + float get_setup_priority() const override { return esphome::setup_priority::LATE; } + void setup() override; + void dump_config() override; + void loop() override; + void set_install_height(uint8_t index); + void set_height_threshold(uint8_t index); + void set_sensitivity(uint8_t index); + void get_radar_parameters(); + void factory_reset(); +}; + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/select/__init__.py b/esphome/components/seeed_mr60fda2/select/__init__.py new file mode 100644 index 0000000000..a6f9eeb920 --- /dev/null +++ b/esphome/components/seeed_mr60fda2/select/__init__.py @@ -0,0 +1,59 @@ +import esphome.codegen as cg +from esphome.components import select +import esphome.config_validation as cv +from esphome.const import CONF_SENSITIVITY, ENTITY_CATEGORY_CONFIG, ICON_ACCELERATION_Z + +from .. import CONF_MR60FDA2_ID, MR60FDA2Component, mr60fda2_ns + + +DEPENDENCIES = ["seeed_mr60fda2"] + +InstallHeightSelect = mr60fda2_ns.class_("InstallHeightSelect", select.Select) +HeightThresholdSelect = mr60fda2_ns.class_("HeightThresholdSelect", select.Select) +SensitivitySelect = mr60fda2_ns.class_("SensitivitySelect", select.Select) + +CONF_INSTALL_HEIGHT = "install_height" +CONF_HEIGHT_THRESHOLD = "height_threshold" + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_MR60FDA2_ID): cv.use_id(MR60FDA2Component), + cv.Optional(CONF_INSTALL_HEIGHT): select.select_schema( + InstallHeightSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_ACCELERATION_Z, + ), + cv.Optional(CONF_HEIGHT_THRESHOLD): select.select_schema( + HeightThresholdSelect, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_ACCELERATION_Z, + ), + cv.Optional(CONF_SENSITIVITY): select.select_schema( + SensitivitySelect, + entity_category=ENTITY_CATEGORY_CONFIG, + ), +} + + +async def to_code(config): + mr60fda2_component = await cg.get_variable(config[CONF_MR60FDA2_ID]) + if install_height_config := config.get(CONF_INSTALL_HEIGHT): + s = await select.new_select( + install_height_config, + options=["2.4m", "2.5m", "2.6m", "2.7m", "2.8m", "2.9m", "3.0m"], + ) + await cg.register_parented(s, config[CONF_MR60FDA2_ID]) + cg.add(mr60fda2_component.set_install_height_select(s)) + if height_threshold_config := config.get(CONF_HEIGHT_THRESHOLD): + s = await select.new_select( + height_threshold_config, + options=["0.0m", "0.1m", "0.2m", "0.3m", "0.4m", "0.5m", "0.6m"], + ) + await cg.register_parented(s, config[CONF_MR60FDA2_ID]) + cg.add(mr60fda2_component.set_height_threshold_select(s)) + if sensitivity_config := config.get(CONF_SENSITIVITY): + s = await select.new_select( + sensitivity_config, + options=["1", "2", "3"], + ) + await cg.register_parented(s, config[CONF_MR60FDA2_ID]) + cg.add(mr60fda2_component.set_sensitivity_select(s)) diff --git a/esphome/components/seeed_mr60fda2/select/height_threshold_select.cpp b/esphome/components/seeed_mr60fda2/select/height_threshold_select.cpp new file mode 100644 index 0000000000..037f8c6036 --- /dev/null +++ b/esphome/components/seeed_mr60fda2/select/height_threshold_select.cpp @@ -0,0 +1,15 @@ +#include "height_threshold_select.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +void HeightThresholdSelect::control(const std::string &value) { + this->publish_state(value); + auto index = this->index_of(value); + if (index.has_value()) { + this->parent_->set_height_threshold(index.value()); + } +} + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/select/height_threshold_select.h b/esphome/components/seeed_mr60fda2/select/height_threshold_select.h new file mode 100644 index 0000000000..b856dbc89a --- /dev/null +++ b/esphome/components/seeed_mr60fda2/select/height_threshold_select.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "../seeed_mr60fda2.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +class HeightThresholdSelect : public select::Select, public Parented { + public: + HeightThresholdSelect() = default; + + protected: + void control(const std::string &value) override; +}; + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/select/install_height_select.cpp b/esphome/components/seeed_mr60fda2/select/install_height_select.cpp new file mode 100644 index 0000000000..e791911613 --- /dev/null +++ b/esphome/components/seeed_mr60fda2/select/install_height_select.cpp @@ -0,0 +1,15 @@ +#include "install_height_select.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +void InstallHeightSelect::control(const std::string &value) { + this->publish_state(value); + auto index = this->index_of(value); + if (index.has_value()) { + this->parent_->set_install_height(index.value()); + } +} + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/select/install_height_select.h b/esphome/components/seeed_mr60fda2/select/install_height_select.h new file mode 100644 index 0000000000..7430da3493 --- /dev/null +++ b/esphome/components/seeed_mr60fda2/select/install_height_select.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "../seeed_mr60fda2.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +class InstallHeightSelect : public select::Select, public Parented { + public: + InstallHeightSelect() = default; + + protected: + void control(const std::string &value) override; +}; + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/select/sensitivity_select.cpp b/esphome/components/seeed_mr60fda2/select/sensitivity_select.cpp new file mode 100644 index 0000000000..e2507fb7cc --- /dev/null +++ b/esphome/components/seeed_mr60fda2/select/sensitivity_select.cpp @@ -0,0 +1,15 @@ +#include "sensitivity_select.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +void SensitivitySelect::control(const std::string &value) { + this->publish_state(value); + auto index = this->index_of(value); + if (index.has_value()) { + this->parent_->set_sensitivity(index.value()); + } +} + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60fda2/select/sensitivity_select.h b/esphome/components/seeed_mr60fda2/select/sensitivity_select.h new file mode 100644 index 0000000000..d1accc1b5b --- /dev/null +++ b/esphome/components/seeed_mr60fda2/select/sensitivity_select.h @@ -0,0 +1,18 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "../seeed_mr60fda2.h" + +namespace esphome { +namespace seeed_mr60fda2 { + +class SensitivitySelect : public select::Select, public Parented { + public: + SensitivitySelect() = default; + + protected: + void control(const std::string &value) override; +}; + +} // namespace seeed_mr60fda2 +} // namespace esphome diff --git a/tests/components/seeed_mr60fda2/common.yaml b/tests/components/seeed_mr60fda2/common.yaml new file mode 100644 index 0000000000..55a7cc1ab3 --- /dev/null +++ b/tests/components/seeed_mr60fda2/common.yaml @@ -0,0 +1,34 @@ +uart: + - id: seeed_mr60fda2_uart + tx_pin: ${uart_tx_pin} + rx_pin: ${uart_rx_pin} + baud_rate: 115200 + parity: NONE + stop_bits: 1 + +seeed_mr60fda2: + id: my_seeed_mr60fda2 + uart_id: seeed_mr60fda2_uart + +binary_sensor: + - platform: seeed_mr60fda2 + people_exist: + name: "Person Information" + fall_detected: + name: "Falling Information" + +button: + - platform: seeed_mr60fda2 + get_radar_parameters: + name: "Get Radar Parameters" + factory_reset: + name: "Reset" + +select: + - platform: seeed_mr60fda2 + install_height: + name: "Set Install Height" + height_threshold: + name: "Set Height Threshold" + sensitivity: + name: "Set Sensitivity" diff --git a/tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml b/tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..4fb884abf4 --- /dev/null +++ b/tests/components/seeed_mr60fda2/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + uart_tx_pin: GPIO5 + uart_rx_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml b/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..4fb884abf4 --- /dev/null +++ b/tests/components/seeed_mr60fda2/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + uart_tx_pin: GPIO5 + uart_rx_pin: GPIO4 + +<<: !include common.yaml From ae6736311a47ddc5c63dd36b0d2cb4af29f4dd14 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 25 Nov 2024 22:29:36 -0600 Subject: [PATCH 0680/1052] [lvgl] clang-tidy fixes for #7822 (#7843) --- esphome/components/lvgl/lvgl_esphome.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 41346bc732..61bdfe9755 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -279,7 +279,7 @@ std::string LvSelectable::get_selected_text() { static std::string join_string(std::vector options) { return std::accumulate( options.begin(), options.end(), std::string(), - [](const std::string &a, const std::string &b) -> std::string { return a + (a.length() > 0 ? "\n" : "") + b; }); + [](const std::string &a, const std::string &b) -> std::string { return a + (!a.empty() ? "\n" : "") + b; }); } void LvSelectable::set_selected_text(const std::string &text, lv_anim_enable_t anim) { From 72df3d1606aaaf650d49c342439bb2294d9ca76c Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:37:20 -0600 Subject: [PATCH 0681/1052] [xiaomi_ble] clang-tidy fixes for #7822 (#7860) --- esphome/components/xiaomi_ble/xiaomi_ble.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 85434341cc..04e0724ba7 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -249,7 +249,7 @@ optional parse_xiaomi_header(const esp32_ble_tracker::Service } bool decrypt_xiaomi_payload(std::vector &raw, const uint8_t *bindkey, const uint64_t &address) { - if (!((raw.size() == 19) || ((raw.size() >= 22) && (raw.size() <= 24)))) { + if ((raw.size() != 19) && ((raw.size() < 22) || (raw.size() > 24))) { ESP_LOGVV(TAG, "decrypt_xiaomi_payload(): data packet has wrong size (%d)!", raw.size()); ESP_LOGVV(TAG, " Packet : %s", format_hex_pretty(raw.data(), raw.size()).c_str()); return false; From 11076e4614075109ce8cc51d4aae70559fc2394b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:47:24 -0600 Subject: [PATCH 0682/1052] [wireguard] clang-tidy fixes for #7822 (#7859) --- esphome/components/wireguard/wireguard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/wireguard/wireguard.cpp b/esphome/components/wireguard/wireguard.cpp index 7b4011cb79..431bf52227 100644 --- a/esphome/components/wireguard/wireguard.cpp +++ b/esphome/components/wireguard/wireguard.cpp @@ -37,7 +37,7 @@ void Wireguard::setup() { this->wg_config_.netmask = this->netmask_.c_str(); this->wg_config_.persistent_keepalive = this->keepalive_; - if (this->preshared_key_.length() > 0) + if (!this->preshared_key_.empty()) this->wg_config_.preshared_key = this->preshared_key_.c_str(); this->publish_enabled_state(); @@ -137,7 +137,7 @@ void Wireguard::dump_config() { ESP_LOGCONFIG(TAG, " Peer Port: " LOG_SECRET("%d"), this->peer_port_); ESP_LOGCONFIG(TAG, " Peer Public Key: " LOG_SECRET("%s"), this->peer_public_key_.c_str()); ESP_LOGCONFIG(TAG, " Peer Pre-shared Key: " LOG_SECRET("%s"), - (this->preshared_key_.length() > 0 ? mask_key(this->preshared_key_).c_str() : "NOT IN USE")); + (!this->preshared_key_.empty() ? mask_key(this->preshared_key_).c_str() : "NOT IN USE")); ESP_LOGCONFIG(TAG, " Peer Allowed IPs:"); for (auto &allowed_ip : this->allowed_ips_) { ESP_LOGCONFIG(TAG, " - %s/%s", std::get<0>(allowed_ip).c_str(), std::get<1>(allowed_ip).c_str()); From 841d278224d7a82ff8112cb3ee80dbe31347d84b Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:47:57 -0600 Subject: [PATCH 0683/1052] [dsmr] clang-tidy fixes for #7822 (#7848) --- esphome/components/dsmr/dsmr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index 193ea1d4e5..c0a2883d79 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -296,7 +296,7 @@ void Dsmr::dump_config() { } void Dsmr::set_decryption_key(const std::string &decryption_key) { - if (decryption_key.length() == 0) { + if (decryption_key.empty()) { ESP_LOGI(TAG, "Disabling decryption"); this->decryption_key_.clear(); if (this->crypt_telegram_ != nullptr) { From 6e50e2aa6534c31dbc9e425781c76581178ea68c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 26 Nov 2024 22:50:16 +1300 Subject: [PATCH 0684/1052] Fix entity name validation to allow "Off" and "On" (#7821) --- esphome/config_validation.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 98b81ec328..ebfb2631c3 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1839,8 +1839,6 @@ def validate_registry_entry(name, registry): def none(value): if value in ("none", "None"): return None - if boolean(value) is False: - return None raise Invalid("Must be none") @@ -1912,17 +1910,23 @@ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend( } ) + +def _validate_entity_name(value): + value = string(value) + try: + value = none(value) # pylint: disable=assignment-from-none + except Invalid: + pass + else: + requires_friendly_name( + "Name cannot be None when esphome->friendly_name is not set!" + )(value) + return value + + ENTITY_BASE_SCHEMA = Schema( { - Optional(CONF_NAME): Any( - All( - none, - requires_friendly_name( - "Name cannot be None when esphome->friendly_name is not set!" - ), - ), - string, - ), + Optional(CONF_NAME): _validate_entity_name, Optional(CONF_INTERNAL): boolean, Optional(CONF_DISABLED_BY_DEFAULT, default=False): boolean, Optional(CONF_ICON): icon, From 2eac8b6c4643494640aa6889025ba5791c09dc36 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:50:33 -0600 Subject: [PATCH 0685/1052] [camera_web_server] Use header instead of mock struct (#7823) --- esphome/components/esp32_camera_web_server/camera_web_server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.h b/esphome/components/esp32_camera_web_server/camera_web_server.h index f65625554c..3ba8f31dd7 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.h +++ b/esphome/components/esp32_camera_web_server/camera_web_server.h @@ -11,7 +11,7 @@ #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" -struct httpd_req; +struct httpd_req; // NOLINT(readability-identifier-naming) namespace esphome { namespace esp32_camera_web_server { From 1c2d2bce5af36527abe758d88f68289b7bccd482 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:52:26 -0600 Subject: [PATCH 0686/1052] [display_menu_base] clang-tidy fixes for #7822 (#7847) --- esphome/components/display_menu_base/display_menu_base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/display_menu_base/display_menu_base.cpp b/esphome/components/display_menu_base/display_menu_base.cpp index 5502623607..2d8e6ae5fc 100644 --- a/esphome/components/display_menu_base/display_menu_base.cpp +++ b/esphome/components/display_menu_base/display_menu_base.cpp @@ -280,7 +280,7 @@ bool DisplayMenuComponent::cursor_down_() { bool DisplayMenuComponent::enter_menu_() { this->displayed_item_->on_leave(); this->displayed_item_ = static_cast(this->get_selected_item_()); - this->selection_stack_.push_front({this->top_index_, this->cursor_index_}); + this->selection_stack_.emplace_front(this->top_index_, this->cursor_index_); this->cursor_index_ = this->top_index_ = 0; this->displayed_item_->on_enter(); From 536bcab5def03c8177cdfc31c6deb3aa01d8ccf3 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:52:57 -0600 Subject: [PATCH 0687/1052] [nextion] clang-tidy fixes for #7822 (#7852) --- esphome/components/nextion/nextion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 984db09c57..7c41f8dfe2 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -343,7 +343,7 @@ void Nextion::process_serial_() { } // nextion.tech/instruction-set/ void Nextion::process_nextion_commands_() { - if (this->command_data_.length() == 0) { + if (this->command_data_.empty()) { return; } From 2d4688a2061e5734ad136f717ba77a7dc7a9fec7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:53:23 -0600 Subject: [PATCH 0688/1052] [shelly_dimmer] clang-tidy fixes for #7822 (#7844) --- esphome/components/shelly_dimmer/shelly_dimmer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/shelly_dimmer/shelly_dimmer.cpp b/esphome/components/shelly_dimmer/shelly_dimmer.cpp index b415840bdc..d4229b2384 100644 --- a/esphome/components/shelly_dimmer/shelly_dimmer.cpp +++ b/esphome/components/shelly_dimmer/shelly_dimmer.cpp @@ -466,7 +466,7 @@ bool ShellyDimmer::handle_frame_() { } case SHELLY_DIMMER_PROTO_CMD_SWITCH: case SHELLY_DIMMER_PROTO_CMD_SETTINGS: { - return !(payload_len < 1 || payload[0] != 0x01); + return payload_len >= 1 && payload[0] == 0x01; } default: { return false; From e6bd2238ce1fceef7db2cadbae3736a5759e16b2 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:54:16 -0600 Subject: [PATCH 0689/1052] [sim800l] clang-tidy fixes for #7822 (#7856) --- esphome/components/sim800l/sim800l.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index 4f7aa228e9..38775b0062 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -324,7 +324,7 @@ void Sim800LComponent::parse_cmd_(std::string message) { this->sms_received_callback_.call(this->message_, this->sender_); this->state_ = STATE_RECEIVED_SMS; } else { - if (this->message_.length() > 0) + if (!this->message_.empty()) this->message_ += "\n"; this->message_ += message; } From 6b59f55a5097f40c3db18ded853cc27888bd2885 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:58:18 -0600 Subject: [PATCH 0690/1052] [nfc, pn532, pn7150, pn7160] clang-tidy fixes for #7822 (#7853) --- esphome/components/nfc/ndef_record.cpp | 6 +++--- esphome/components/pn532/pn532_mifare_ultralight.cpp | 4 ++-- esphome/components/pn7150/pn7150_mifare_ultralight.cpp | 4 ++-- esphome/components/pn7160/pn7160_mifare_ultralight.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/esphome/components/nfc/ndef_record.cpp b/esphome/components/nfc/ndef_record.cpp index 8eb0c3b901..540ba62940 100644 --- a/esphome/components/nfc/ndef_record.cpp +++ b/esphome/components/nfc/ndef_record.cpp @@ -30,13 +30,13 @@ std::vector NdefRecord::encode(bool first, bool last) { data.push_back(payload_length & 0xFF); } - if (this->id_.length()) { + if (!this->id_.empty()) { data.push_back(this->id_.length()); } data.insert(data.end(), this->type_.begin(), this->type_.end()); - if (this->id_.length()) { + if (!this->id_.empty()) { data.insert(data.end(), this->id_.begin(), this->id_.end()); } @@ -55,7 +55,7 @@ uint8_t NdefRecord::create_flag_byte(bool first, bool last, size_t payload_size) if (payload_size <= 255) { value = value | 0x10; // Set SR bit } - if (this->id_.length()) { + if (!this->id_.empty()) { value = value | 0x08; // Set IL bit } return value; diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp index b08a7336c7..f823829a6c 100644 --- a/esphome/components/pn532/pn532_mifare_ultralight.cpp +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -80,8 +80,8 @@ bool PN532::is_mifare_ultralight_formatted_(const std::vector &page_3_t const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector return (page_3_to_6.size() > p4_offset + 3) && - !((page_3_to_6[p4_offset + 0] == 0xFF) && (page_3_to_6[p4_offset + 1] == 0xFF) && - (page_3_to_6[p4_offset + 2] == 0xFF) && (page_3_to_6[p4_offset + 3] == 0xFF)); + ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) || + (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF)); } uint16_t PN532::read_mifare_ultralight_capacity_() { diff --git a/esphome/components/pn7150/pn7150_mifare_ultralight.cpp b/esphome/components/pn7150/pn7150_mifare_ultralight.cpp index 791b0634d6..b107f6f79e 100644 --- a/esphome/components/pn7150/pn7150_mifare_ultralight.cpp +++ b/esphome/components/pn7150/pn7150_mifare_ultralight.cpp @@ -81,8 +81,8 @@ bool PN7150::is_mifare_ultralight_formatted_(const std::vector &page_3_ const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector return (page_3_to_6.size() > p4_offset + 3) && - !((page_3_to_6[p4_offset + 0] == 0xFF) && (page_3_to_6[p4_offset + 1] == 0xFF) && - (page_3_to_6[p4_offset + 2] == 0xFF) && (page_3_to_6[p4_offset + 3] == 0xFF)); + ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) || + (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF)); } uint16_t PN7150::read_mifare_ultralight_capacity_() { diff --git a/esphome/components/pn7160/pn7160_mifare_ultralight.cpp b/esphome/components/pn7160/pn7160_mifare_ultralight.cpp index a74f23d4f2..65daac494f 100644 --- a/esphome/components/pn7160/pn7160_mifare_ultralight.cpp +++ b/esphome/components/pn7160/pn7160_mifare_ultralight.cpp @@ -81,8 +81,8 @@ bool PN7160::is_mifare_ultralight_formatted_(const std::vector &page_3_ const uint8_t p4_offset = nfc::MIFARE_ULTRALIGHT_PAGE_SIZE; // page 4 will begin 4 bytes into the vector return (page_3_to_6.size() > p4_offset + 3) && - !((page_3_to_6[p4_offset + 0] == 0xFF) && (page_3_to_6[p4_offset + 1] == 0xFF) && - (page_3_to_6[p4_offset + 2] == 0xFF) && (page_3_to_6[p4_offset + 3] == 0xFF)); + ((page_3_to_6[p4_offset + 0] != 0xFF) || (page_3_to_6[p4_offset + 1] != 0xFF) || + (page_3_to_6[p4_offset + 2] != 0xFF) || (page_3_to_6[p4_offset + 3] != 0xFF)); } uint16_t PN7160::read_mifare_ultralight_capacity_() { From 31c13e4c16f18e3ae241b223a529ee5ed2a08fd8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 03:59:29 -0600 Subject: [PATCH 0691/1052] [output] clang-tidy fixes for #7822 (#7854) --- esphome/components/output/float_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/output/float_output.cpp b/esphome/components/output/float_output.cpp index f120f86f1f..e7dba1d81d 100644 --- a/esphome/components/output/float_output.cpp +++ b/esphome/components/output/float_output.cpp @@ -32,7 +32,7 @@ void FloatOutput::set_level(float state) { } #endif - if (!(state == 0.0f && this->zero_means_zero_)) // regardless of min_power_, 0.0 means off + if (state != 0.0f || !this->zero_means_zero_) // regardless of min_power_, 0.0 means off state = (state * (this->max_power_ - this->min_power_)) + this->min_power_; if (this->is_inverted()) From bdc6302ea1b17319d2529d9d5e277ba416e18687 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 04:00:03 -0600 Subject: [PATCH 0692/1052] [sun_gtil2] clang-tidy fixes for #7822 (#7858) --- esphome/components/sun_gtil2/sun_gtil2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/sun_gtil2/sun_gtil2.cpp b/esphome/components/sun_gtil2/sun_gtil2.cpp index 1653f937dd..46b4902654 100644 --- a/esphome/components/sun_gtil2/sun_gtil2.cpp +++ b/esphome/components/sun_gtil2/sun_gtil2.cpp @@ -83,7 +83,7 @@ void SunGTIL2::handle_char_(uint8_t c) { memcpy(&msg, this->rx_message_.data(), MESSAGE_SIZE); this->rx_message_.clear(); - if (!((msg.end[0] == 0) && (msg.end[38] == 0x08))) + if ((msg.end[0] != 0) || (msg.end[38] != 0x08)) return; ESP_LOGVV(TAG, "Frequency raw value: %02x", msg.frequency); From 4c383906c490192a692cc74da82fca5d81fe17fc Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 04:00:40 -0600 Subject: [PATCH 0693/1052] [pipsolar] clang-tidy fixes for #7822 (#7855) --- esphome/components/pipsolar/pipsolar.cpp | 4 ++-- esphome/components/pipsolar/switch/pipsolar_switch.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/pipsolar/pipsolar.cpp b/esphome/components/pipsolar/pipsolar.cpp index c4bc018b75..03c699e7d4 100644 --- a/esphome/components/pipsolar/pipsolar.cpp +++ b/esphome/components/pipsolar/pipsolar.cpp @@ -790,7 +790,7 @@ uint8_t Pipsolar::check_incoming_crc_() { // send next command used uint8_t Pipsolar::send_next_command_() { uint16_t crc16; - if (this->command_queue_[this->command_queue_position_].length() != 0) { + if (!this->command_queue_[this->command_queue_position_].empty()) { const char *command = this->command_queue_[this->command_queue_position_].c_str(); uint8_t byte_command[16]; uint8_t length = this->command_queue_[this->command_queue_position_].length(); @@ -846,7 +846,7 @@ void Pipsolar::queue_command_(const char *command, uint8_t length) { uint8_t next_position = command_queue_position_; for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) { uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH; - if (command_queue_[testposition].length() == 0) { + if (command_queue_[testposition].empty()) { command_queue_[testposition] = command; ESP_LOGD(TAG, "Command queued successfully: %s with length %u at position %d", command, command_queue_[testposition].length(), testposition); diff --git a/esphome/components/pipsolar/switch/pipsolar_switch.cpp b/esphome/components/pipsolar/switch/pipsolar_switch.cpp index 7eaeac1c2d..be7763226b 100644 --- a/esphome/components/pipsolar/switch/pipsolar_switch.cpp +++ b/esphome/components/pipsolar/switch/pipsolar_switch.cpp @@ -10,11 +10,11 @@ static const char *const TAG = "pipsolar.switch"; void PipsolarSwitch::dump_config() { LOG_SWITCH("", "Pipsolar Switch", this); } void PipsolarSwitch::write_state(bool state) { if (state) { - if (this->on_command_.length() > 0) { + if (!this->on_command_.empty()) { this->parent_->switch_command(this->on_command_); } } else { - if (this->off_command_.length() > 0) { + if (!this->off_command_.empty()) { this->parent_->switch_command(this->off_command_); } } From 2fa8d907b3506a0db2dc4ed28940bb7d7e30ca4f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 04:01:34 -0600 Subject: [PATCH 0694/1052] [ltr501] clang-tidy fixes for #7822 (#7850) --- esphome/components/ltr501/ltr501.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ltr501/ltr501.cpp b/esphome/components/ltr501/ltr501.cpp index 4f4e26f44f..b30e520f2b 100644 --- a/esphome/components/ltr501/ltr501.cpp +++ b/esphome/components/ltr501/ltr501.cpp @@ -23,7 +23,7 @@ bool operator==(const GainTimePair &lhs, const GainTimePair &rhs) { } bool operator!=(const GainTimePair &lhs, const GainTimePair &rhs) { - return !(lhs.gain == rhs.gain && lhs.time == rhs.time); + return lhs.gain != rhs.gain || lhs.time != rhs.time; } template T get_next(const T (&array)[size], const T val) { From cd1ee9660644177c99302d0698cefed6a9802361 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 04:04:50 -0600 Subject: [PATCH 0695/1052] [cse7766] clang-tidy fixes for #7822 (#7846) --- esphome/components/cse7766/cse7766.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 48240464b3..3907c195d0 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -43,7 +43,7 @@ bool CSE7766Component::check_byte_() { uint8_t index = this->raw_data_index_; uint8_t byte = this->raw_data_[index]; if (index == 0) { - return !((byte != 0x55) && ((byte & 0xF0) != 0xF0) && (byte != 0xAA)); + return (byte == 0x55) || ((byte & 0xF0) == 0xF0) || (byte == 0xAA); } if (index == 1) { From be7882727448093efd3043364607ad925058606b Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 26 Nov 2024 00:05:20 -1000 Subject: [PATCH 0696/1052] [honeywell] use warning instead of failing (#7862) Co-authored-by: Samuel Sieb --- .../components/honeywellabp2_i2c/honeywellabp2.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp index e2910032cc..d111723669 100644 --- a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +++ b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp @@ -15,7 +15,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!"); - this->mark_failed(); + this->status_set_warning("couldn't read sensor data"); return; } float press_counts = encode_uint24(raw_data_[1], raw_data_[2], raw_data_[3]); // calculate digital pressure counts @@ -25,12 +25,13 @@ void HONEYWELLABP2Sensor::read_sensor_data() { (this->max_pressure_ - this->min_pressure_)) + this->min_pressure_; this->last_temperature_ = (temp_counts * 200 / 16777215) - 50; + this->status_clear_warning(); } void HONEYWELLABP2Sensor::start_measurement() { if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with ABP2 failed!"); - this->mark_failed(); + this->status_set_warning("couldn't start measurement"); return; } this->measurement_running_ = true; @@ -39,7 +40,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!"); - this->mark_failed(); + this->status_set_warning("couldn't check measurement"); return false; } if ((raw_data_[0] & (0x1 << STATUS_BIT_BUSY)) > 0) { @@ -52,7 +53,7 @@ bool HONEYWELLABP2Sensor::is_measurement_ready() { void HONEYWELLABP2Sensor::measurement_timeout() { ESP_LOGE(TAG, "Timeout!"); this->measurement_running_ = false; - this->mark_failed(); + this->status_set_warning("measurement timed out"); } float HONEYWELLABP2Sensor::get_pressure() { return this->last_pressure_; } @@ -79,7 +80,7 @@ void HONEYWELLABP2Sensor::update() { ESP_LOGV(TAG, "Update Honeywell ABP2 Sensor"); this->start_measurement(); - this->set_timeout("meas_timeout", 50, [this] { this->measurement_timeout(); }); + this->set_timeout("meas_timeout", 100, [this] { this->measurement_timeout(); }); } void HONEYWELLABP2Sensor::dump_config() { From 2b9013699d841a7878a796f5621adfa3461f597d Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 04:05:39 -0600 Subject: [PATCH 0697/1052] [alarm_control_panel] clang-tidy fixes for #7822 (#7845) --- .../alarm_control_panel/alarm_control_panel_call.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/alarm_control_panel/alarm_control_panel_call.cpp b/esphome/components/alarm_control_panel/alarm_control_panel_call.cpp index b1d2b2a097..7bb9b9989c 100644 --- a/esphome/components/alarm_control_panel/alarm_control_panel_call.cpp +++ b/esphome/components/alarm_control_panel/alarm_control_panel_call.cpp @@ -72,10 +72,9 @@ void AlarmControlPanelCall::validate_() { this->state_.reset(); return; } - if (state == ACP_STATE_DISARMED && - !(this->parent_->is_state_armed(this->parent_->get_state()) || - this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_ARMING || - this->parent_->get_state() == ACP_STATE_TRIGGERED)) { + if (state == ACP_STATE_DISARMED && !this->parent_->is_state_armed(this->parent_->get_state()) && + this->parent_->get_state() != ACP_STATE_PENDING && this->parent_->get_state() != ACP_STATE_ARMING && + this->parent_->get_state() != ACP_STATE_TRIGGERED) { ESP_LOGW(TAG, "Cannot disarm when not armed"); this->state_.reset(); return; From 3730b0310b23c989c8c27d5701c3d423b6c749d6 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 12:07:36 -0600 Subject: [PATCH 0698/1052] [sprinkler] clang-tidy fixes for #7822 (#7857) --- esphome/components/sprinkler/sprinkler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 59565251c3..5384d29871 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -419,7 +419,7 @@ void Sprinkler::add_valve(SprinklerControllerSwitch *valve_sw, SprinklerControll SprinklerValve *new_valve = &this->valve_[new_valve_number]; new_valve->controller_switch = valve_sw; - new_valve->controller_switch->set_state_lambda([=]() -> optional { + new_valve->controller_switch->set_state_lambda([this, new_valve_number]() -> optional { if (this->valve_pump_switch(new_valve_number) != nullptr) { return this->valve_switch(new_valve_number)->state() && this->valve_pump_switch(new_valve_number)->state(); } @@ -445,7 +445,7 @@ void Sprinkler::add_controller(Sprinkler *other_controller) { this->other_contro void Sprinkler::set_controller_main_switch(SprinklerControllerSwitch *controller_switch) { this->controller_sw_ = controller_switch; - controller_switch->set_state_lambda([=]() -> optional { + controller_switch->set_state_lambda([this]() -> optional { for (size_t valve_number = 0; valve_number < this->number_of_valves(); valve_number++) { if (this->valve_[valve_number].controller_switch->state) { return true; From 53691d28a81644b84f82e4441da153b27cbfddd7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 12:07:42 -0600 Subject: [PATCH 0699/1052] [haier] clang-tidy fixes for #7822 (#7849) --- esphome/components/haier/hon_climate.cpp | 135 ++++++++++------------- 1 file changed, 60 insertions(+), 75 deletions(-) diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index e7be1fa418..85e9cf37b9 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -1069,19 +1069,17 @@ void HonClimate::fill_control_messages_queue_() { climate_control = this->current_hvac_settings_; // Beeper command { - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS, - this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::BEEPER_STATUS, + this->get_beeper_state() ? ZERO_BUF : ONE_BUF, 2); } // Health mode { - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::HEALTH_MODE, - this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::HEALTH_MODE, + this->get_health_mode() ? ONE_BUF : ZERO_BUF, 2); this->health_mode_ = (SwitchState) ((uint8_t) this->health_mode_ & 0b01); } // Climate mode @@ -1099,51 +1097,46 @@ void HonClimate::fill_control_messages_queue_() { case CLIMATE_MODE_HEAT_COOL: new_power = true; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::AUTO; - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::AC_MODE, - buffer, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::AC_MODE, + buffer, 2); fan_mode_buf[1] = this->other_modes_fan_speed_; break; case CLIMATE_MODE_HEAT: new_power = true; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::HEAT; - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::AC_MODE, - buffer, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::AC_MODE, + buffer, 2); fan_mode_buf[1] = this->other_modes_fan_speed_; break; case CLIMATE_MODE_DRY: new_power = true; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::DRY; - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::AC_MODE, - buffer, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::AC_MODE, + buffer, 2); fan_mode_buf[1] = this->other_modes_fan_speed_; break; case CLIMATE_MODE_FAN_ONLY: new_power = true; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::FAN; - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::AC_MODE, - buffer, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::AC_MODE, + buffer, 2); fan_mode_buf[1] = this->other_modes_fan_speed_; // Auto doesn't work in fan only mode break; case CLIMATE_MODE_COOL: new_power = true; buffer[1] = (uint8_t) hon_protocol::ConditioningMode::COOL; - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::AC_MODE, - buffer, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::AC_MODE, + buffer, 2); fan_mode_buf[1] = this->other_modes_fan_speed_; break; default: @@ -1153,11 +1146,10 @@ void HonClimate::fill_control_messages_queue_() { } // Climate power { - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::AC_POWER, - new_power ? ONE_BUF : ZERO_BUF, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::AC_POWER, + new_power ? ONE_BUF : ZERO_BUF, 2); } // CLimate preset { @@ -1199,36 +1191,32 @@ void HonClimate::fill_control_messages_queue_() { } auto presets = this->traits_.get_supported_presets(); if (quiet_mode_buf[1] != 0xFF) { - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::QUIET_MODE, - quiet_mode_buf, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::QUIET_MODE, + quiet_mode_buf, 2); } if ((fast_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_BOOST) != presets.end()))) { - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::FAST_MODE, - fast_mode_buf, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::FAST_MODE, + fast_mode_buf, 2); } if ((away_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_AWAY) != presets.end()))) { - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::TEN_DEGREE, - away_mode_buf, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::TEN_DEGREE, + away_mode_buf, 2); } } // Target temperature if (climate_control.target_temperature.has_value() && (this->mode != ClimateMode::CLIMATE_MODE_FAN_ONLY)) { uint8_t buffer[2] = {0x00, 0x00}; buffer[1] = ((uint8_t) climate_control.target_temperature.value()) - 16; - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::SET_POINT, - buffer, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::SET_POINT, + buffer, 2); } // Vertical swing mode if (climate_control.swing_mode.has_value()) { @@ -1248,16 +1236,14 @@ void HonClimate::fill_control_messages_queue_() { case CLIMATE_SWING_BOTH: break; } - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::HORIZONTAL_SWING_MODE, - horizontal_swing_buf, 2)); - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::VERTICAL_SWING_MODE, - vertical_swing_buf, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::HORIZONTAL_SWING_MODE, + horizontal_swing_buf, 2); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::VERTICAL_SWING_MODE, + vertical_swing_buf, 2); } // Fan mode if (climate_control.fan_mode.has_value()) { @@ -1280,11 +1266,10 @@ void HonClimate::fill_control_messages_queue_() { break; } if (fan_mode_buf[1] != 0xFF) { - this->control_messages_queue_.push( - haier_protocol::HaierMessage(haier_protocol::FrameType::CONTROL, - (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + - (uint8_t) hon_protocol::DataParameters::FAN_MODE, - fan_mode_buf, 2)); + this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL, + (uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER + + (uint8_t) hon_protocol::DataParameters::FAN_MODE, + fan_mode_buf, 2); } } } From 39f3f795e2b927af404e80286d5af8100fc7c80d Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 12:07:53 -0600 Subject: [PATCH 0700/1052] [mqtt] clang-tidy fixes for #7822 (#7851) --- esphome/components/mqtt/mqtt_backend_esp32.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index ed500c6d44..2cccb957eb 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -151,11 +151,11 @@ void MQTTBackendESP32::mqtt_event_handler_(const Event &event) { break; case MQTT_EVENT_DATA: { static std::string topic; - if (event.topic.length() > 0) { + if (!event.topic.empty()) { topic = event.topic; } ESP_LOGV(TAG, "MQTT_EVENT_DATA %s", topic.c_str()); - this->on_message_.call(event.topic.length() > 0 ? topic.c_str() : nullptr, event.data.data(), event.data.size(), + this->on_message_.call(!event.topic.empty() ? topic.c_str() : nullptr, event.data.data(), event.data.size(), event.current_data_offset, event.total_data_len); } break; case MQTT_EVENT_ERROR: @@ -184,7 +184,7 @@ void MQTTBackendESP32::mqtt_event_handler(void *handler_args, esp_event_base_t b // queue event to decouple processing if (instance) { auto event = *static_cast(event_data); - instance->mqtt_events_.push(Event(event)); + instance->mqtt_events_.emplace(event); } } From e3d673d16c766181c9cda046cca78bc17e0936a6 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 26 Nov 2024 12:08:02 -0600 Subject: [PATCH 0701/1052] [helpers, optional] clang-tidy fixes for #7822 (#7841) --- esphome/core/helpers.cpp | 2 +- esphome/core/optional.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index befc84516c..103173095b 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -293,7 +293,7 @@ std::string str_sanitize(const std::string &str) { std::replace_if( out.begin(), out.end(), [](const char &c) { - return !(c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); + return c != '-' && c != '_' && (c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z'); }, '_'); return out; diff --git a/esphome/core/optional.h b/esphome/core/optional.h index 1e28ef1354..591bc7aa68 100644 --- a/esphome/core/optional.h +++ b/esphome/core/optional.h @@ -59,7 +59,7 @@ template class optional { // NOLINT return *this; } - void swap(optional &rhs) { + void swap(optional &rhs) noexcept { using std::swap; if (has_value() && rhs.has_value()) { swap(**this, *rhs); @@ -206,7 +206,7 @@ template inline bool operator>=(U const &v, optional // Specialized algorithms -template void swap(optional &x, optional &y) { x.swap(y); } +template void swap(optional &x, optional &y) noexcept { x.swap(y); } // Convenience function to create an optional. From 921be1a17c5b8fdd638518e8c3c92f87ed0cca2d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 27 Nov 2024 07:09:16 +1300 Subject: [PATCH 0702/1052] Move ``USE_CAPTIVE_PORTAL`` into all define groups it can be used with (#7863) --- esphome/core/defines.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 3798ddba6a..eb3b20d007 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -84,7 +84,6 @@ // Arduino-specific feature flags #ifdef USE_ARDUINO -#define USE_CAPTIVE_PORTAL #define USE_PROMETHEUS #define USE_WIFI_WPA2_EAP #endif @@ -97,6 +96,7 @@ // ESP32-specific feature flags #ifdef USE_ESP32 #define USE_BLUETOOTH_PROXY +#define USE_CAPTIVE_PORTAL #define USE_ESP32_BLE #define USE_ESP32_BLE_CLIENT #define USE_ESP32_BLE_SERVER @@ -135,6 +135,7 @@ #ifdef USE_ESP8266 #define USE_ADC_SENSOR_VCC #define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 1, 2) +#define USE_CAPTIVE_PORTAL #define USE_ESP8266_PREFERENCES_FLASH #define USE_HTTP_REQUEST_ESP8266_HTTPS #define USE_SOCKET_IMPL_LWIP_TCP @@ -159,6 +160,7 @@ #endif #ifdef USE_LIBRETINY +#define USE_CAPTIVE_PORTAL #define USE_SOCKET_IMPL_LWIP_SOCKETS #define USE_WEBSERVER #define USE_WEBSERVER_PORT 80 // NOLINT From 3a8b41daa3297b77175a2303be3036fb93c16954 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:06:56 +0100 Subject: [PATCH 0703/1052] Bump docker/build-push-action from 6.9.0 to 6.10.0 in /.github/actions/build-image (#7866) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index 5c686605c3..cc9894a657 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false @@ -72,7 +72,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.9.0 + uses: docker/build-push-action@v6.10.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false From a3ef2ed7fd109fc6885de3dee7245d637d5ae2a9 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Tue, 26 Nov 2024 21:56:43 +0100 Subject: [PATCH 0704/1052] python lint for platform components (#7864) --- esphome/components/bk72xx/__init__.py | 2 +- esphome/components/esp8266/__init__.py | 12 +++++------- esphome/components/rp2040/__init__.py | 2 +- esphome/components/rtl87xx/__init__.py | 2 +- script/build_language_schema.py | 16 +++++++--------- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/esphome/components/bk72xx/__init__.py b/esphome/components/bk72xx/__init__.py index 6737631ac7..b5122de956 100644 --- a/esphome/components/bk72xx/__init__.py +++ b/esphome/components/bk72xx/__init__.py @@ -15,7 +15,7 @@ from esphome.components.libretiny.const import ( ) from esphome.core import CORE -from .boards import BK72XX_BOARDS, BK72XX_BOARD_PINS +from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS CODEOWNERS = ["@kuba2k2"] AUTO_LOAD = ["libretiny"] diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 64b127bda3..c73027fe1b 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -1,10 +1,13 @@ import logging import os +import esphome.codegen as cg +import esphome.config_validation as cv from esphome.const import ( CONF_BOARD, CONF_BOARD_FLASH_MODE, CONF_FRAMEWORK, + CONF_PLATFORM_VERSION, CONF_SOURCE, CONF_VERSION, KEY_CORE, @@ -12,27 +15,22 @@ from esphome.const import ( KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM, PLATFORM_ESP8266, - CONF_PLATFORM_VERSION, ) from esphome.core import CORE, coroutine_with_priority -import esphome.config_validation as cv -import esphome.codegen as cg from esphome.helpers import copy_file_if_changed +from .boards import BOARDS, ESP8266_LD_SCRIPTS from .const import ( - CONF_RESTORE_FROM_FLASH, CONF_EARLY_PIN_INIT, + CONF_RESTORE_FROM_FLASH, KEY_BOARD, KEY_ESP8266, KEY_FLASH_SIZE, KEY_PIN_INITIAL_STATES, esp8266_ns, ) -from .boards import BOARDS, ESP8266_LD_SCRIPTS - from .gpio import PinInitialState, add_pin_initial_states_array - CODEOWNERS = ["@esphome/core"] _LOGGER = logging.getLogger(__name__) AUTO_LOAD = ["preferences"] diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index d612631a4c..b04e539182 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -17,7 +17,7 @@ from esphome.const import ( PLATFORM_RP2040, ) from esphome.core import CORE, EsphomeError, coroutine_with_priority -from esphome.helpers import copy_file_if_changed, mkdir_p, write_file, read_file +from esphome.helpers import copy_file_if_changed, mkdir_p, read_file, write_file from .const import KEY_BOARD, KEY_PIO_FILES, KEY_RP2040, rp2040_ns diff --git a/esphome/components/rtl87xx/__init__.py b/esphome/components/rtl87xx/__init__.py index 9060a7c4a6..4c1956f0f4 100644 --- a/esphome/components/rtl87xx/__init__.py +++ b/esphome/components/rtl87xx/__init__.py @@ -15,7 +15,7 @@ from esphome.components.libretiny.const import ( ) from esphome.core import CORE -from .boards import RTL87XX_BOARDS, RTL87XX_BOARD_PINS +from .boards import RTL87XX_BOARD_PINS, RTL87XX_BOARDS CODEOWNERS = ["@kuba2k2"] AUTO_LOAD = ["libretiny"] diff --git a/script/build_language_schema.py b/script/build_language_schema.py index 8b2c28b06b..2023dc0402 100644 --- a/script/build_language_schema.py +++ b/script/build_language_schema.py @@ -394,9 +394,8 @@ def add_referenced_recursive(referenced_schemas, config_var, path, eat_schema=Fa for k in schema.get(S_EXTENDS, []): if k not in referenced_schemas: referenced_schemas[k] = [path] - else: - if path not in referenced_schemas[k]: - referenced_schemas[k].append(path) + elif path not in referenced_schemas[k]: + referenced_schemas[k].append(path) s1 = get_str_path_schema(k) p = k.split(".") @@ -868,13 +867,12 @@ def convert(schema, config_var, path): config_var[S_TYPE] = "use_id" else: print("TODO deferred?") + elif isinstance(data, str): + # TODO: Figure out why pipsolar does this + config_var["use_id_type"] = data else: - if isinstance(data, str): - # TODO: Figure out why pipsolar does this - config_var["use_id_type"] = data - else: - config_var["use_id_type"] = str(data.base) - config_var[S_TYPE] = "use_id" + config_var["use_id_type"] = str(data.base) + config_var[S_TYPE] = "use_id" else: raise TypeError("Unknown extracted schema type") elif config_var.get("key") == "GeneratedID": From 4a97064b2cd2412414b2d57ec1b7e151ffcf92c2 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:25:16 +1100 Subject: [PATCH 0705/1052] [lvgl] Bugfixes (#7803) --- esphome/components/lvgl/__init__.py | 2 +- esphome/components/lvgl/lv_validation.py | 3 ++- esphome/components/lvgl/schemas.py | 1 + esphome/components/lvgl/widgets/line.py | 5 ++++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index d03adc9624..8fdd03f647 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -322,8 +322,8 @@ async def to_code(configs): await encoders_to_code(lv_component, config, default_group) await keypads_to_code(lv_component, config, default_group) await theme_to_code(config) - await styles_to_code(config) await gradients_to_code(config) + await styles_to_code(config) await set_obj_properties(lv_scr_act, config) await add_widgets(lv_scr_act, config) await add_pages(lv_component, config) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index b91b0905df..766c010244 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -30,7 +30,7 @@ from .defines import ( call_lambda, literal, ) -from .helpers import esphome_fonts_used, lv_fonts_used, requires_component +from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component from .types import lv_font_t, lv_gradient_t, lv_img_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -326,6 +326,7 @@ def image_validator(value): value = requires_component("image")(value) value = cv.use_id(Image_)(value) lv_images_used.add(value) + add_lv_use("img", "label") return value diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 516627708e..3f56b3345f 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -341,6 +341,7 @@ FLEX_OBJ_SCHEMA = { cv.Optional(df.CONF_FLEX_GROW): cv.int_, } + DISP_BG_SCHEMA = cv.Schema( { cv.Optional(df.CONF_DISP_BG_IMAGE): lv_image, diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 4c6439fde4..548dfa8452 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -39,7 +39,10 @@ LINE_SCHEMA = { class LineType(WidgetType): def __init__(self): super().__init__( - CONF_LINE, LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, modify_schema={} + CONF_LINE, + LvType("lv_line_t"), + (CONF_MAIN,), + LINE_SCHEMA, ) async def to_code(self, w: Widget, config): From a4a71797d9790fe059210b79a35a95839ff2bef8 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:25:51 +1100 Subject: [PATCH 0706/1052] [docker] Leave run-time required libraries installed. (#7804) --- docker/Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index ed6ce083a8..c2902a9dd1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -99,15 +99,17 @@ BUILD_DEPS=" libfreetype-dev=2.12.1+dfsg-5+deb12u3 libssl-dev=3.0.15-1~deb12u1 libffi-dev=3.4.4-1 - libopenjp2-7=2.5.0-2 - libtiff6=4.5.0-6+deb12u1 cargo=0.66.0+ds1-1 pkg-config=1.8.1-1 " +LIB_DEPS=" + libtiff6=4.5.0-6+deb12u1 + libopenjp2-7=2.5.0-2 +" if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ] || [ "$TARGETARCH$TARGETVARIANT" = "armv7" ] then apt-get update - apt-get install -y --no-install-recommends $BUILD_DEPS + apt-get install -y --no-install-recommends $BUILD_DEPS $LIB_DEPS fi CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo From 80fedbc1a556e121ca166519aa3aae7e44c05a1b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 25 Nov 2024 07:27:09 +1100 Subject: [PATCH 0707/1052] [qspi_dbi] Fix init sequences (Bugfix) (#7805) --- esphome/components/qspi_dbi/models.py | 2 ++ esphome/components/qspi_dbi/qspi_dbi.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py index cbd9c4663f..c1fe434853 100644 --- a/esphome/components/qspi_dbi/models.py +++ b/esphome/components/qspi_dbi/models.py @@ -1,6 +1,7 @@ # Commands SW_RESET_CMD = 0x01 SLEEP_OUT = 0x11 +NORON = 0x13 INVERT_OFF = 0x20 INVERT_ON = 0x21 ALL_ON = 0x23 @@ -56,6 +57,7 @@ chip.cmd(0xC2, 0x00) chip.delay(10) chip.cmd(TEON, 0x00) chip.cmd(PIXFMT, 0x55) +chip.cmd(NORON) chip = DriverChip("AXS15231") chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index a649a25ea6..785885d4ec 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -111,7 +111,6 @@ void QspiDbi::reset_params_(bool ready) { mad |= MADCTL_MY; this->write_command_(MADCTL_CMD, mad); this->write_command_(BRIGHTNESS, this->brightness_); - this->write_command_(NORON); this->write_command_(DISPLAY_ON); } From e9851e7eb227ceebb4925b59e03c22c402b29d06 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 24 Nov 2024 10:42:46 -1000 Subject: [PATCH 0708/1052] fix modbus crashing when bad data returned (#7810) Co-authored-by: Samuel Sieb --- esphome/components/modbus/modbus.cpp | 3 +- .../modbus_controller/modbus_controller.cpp | 72 ++++++++++++++----- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 8544b50261..47deea83e6 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -38,8 +38,9 @@ void Modbus::loop() { // stop blocking new send commands after sent_wait_time_ ms after response received if (now - this->last_send_ > send_wait_time_) { - if (waiting_for_response > 0) + if (waiting_for_response > 0) { ESP_LOGV(TAG, "Stop waiting for response from %d", waiting_for_response); + } waiting_for_response = 0; } } diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index e1102516ca..f8b72af817 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -622,51 +622,87 @@ int64_t payload_to_number(const std::vector &data, SensorValueType sens uint32_t bitmask) { int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits + size_t size = data.size() - offset; + bool error = false; switch (sensor_value_type) { case SensorValueType::U_WORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; + if (size >= 2) { + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); // default is 0xFFFF ; + } else { + error = true; + } break; case SensorValueType::U_DWORD: case SensorValueType::FP32: - value = get_data(data, offset); - value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + if (size >= 4) { + value = get_data(data, offset); + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + } else { + error = true; + } break; case SensorValueType::U_DWORD_R: case SensorValueType::FP32_R: - value = get_data(data, offset); - value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; - value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + if (size >= 4) { + value = get_data(data, offset); + value = static_cast(value & 0xFFFF) << 16 | (value & 0xFFFF0000) >> 16; + value = mask_and_shift_by_rightbit((uint32_t) value, bitmask); + } else { + error = true; + } break; case SensorValueType::S_WORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), - bitmask); // default is 0xFFFF ; + if (size >= 2) { + value = mask_and_shift_by_rightbit(get_data(data, offset), + bitmask); // default is 0xFFFF ; + } else { + error = true; + } break; case SensorValueType::S_DWORD: - value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); + if (size >= 4) { + value = mask_and_shift_by_rightbit(get_data(data, offset), bitmask); + } else { + error = true; + } break; case SensorValueType::S_DWORD_R: { - value = get_data(data, offset); - // Currently the high word is at the low position - // the sign bit is therefore at low before the switch - uint32_t sign_bit = (value & 0x8000) << 16; - value = mask_and_shift_by_rightbit( - static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); + if (size >= 4) { + value = get_data(data, offset); + // Currently the high word is at the low position + // the sign bit is therefore at low before the switch + uint32_t sign_bit = (value & 0x8000) << 16; + value = mask_and_shift_by_rightbit( + static_cast(((value & 0x7FFF) << 16 | (value & 0xFFFF0000) >> 16) | sign_bit), bitmask); + } else { + error = true; + } } break; case SensorValueType::U_QWORD: case SensorValueType::S_QWORD: // Ignore bitmask for QWORD - value = get_data(data, offset); + if (size >= 8) { + value = get_data(data, offset); + } else { + error = true; + } break; case SensorValueType::U_QWORD_R: case SensorValueType::S_QWORD_R: { // Ignore bitmask for QWORD - uint64_t tmp = get_data(data, offset); - value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); + if (size >= 8) { + uint64_t tmp = get_data(data, offset); + value = (tmp << 48) | (tmp >> 48) | ((tmp & 0xFFFF0000) << 16) | ((tmp >> 16) & 0xFFFF0000); + } else { + error = true; + } } break; case SensorValueType::RAW: default: break; } + if (error) + ESP_LOGE(TAG, "not enough data for value"); return value; } From 1b91e0027b8cf40f3ed5e897ccbada8fab5d3448 Mon Sep 17 00:00:00 2001 From: TFGF Date: Sun, 24 Nov 2024 19:15:10 -0300 Subject: [PATCH 0709/1052] [Modbus Controller] Fix issue #6477. Online automation triggering Offline (#7801) --- esphome/components/modbus_controller/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 5c407d6fff..2a08075831 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -163,7 +163,7 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional(CONF_ON_OFFLINE): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOnlineTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ModbusOfflineTrigger), } ), } From 72bf0086e486a71d5f44e99f3bed925f996e7aa3 Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Mon, 25 Nov 2024 01:23:30 +0300 Subject: [PATCH 0710/1052] [fix] Status sensor does not check if required network component is missing (#7734) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/status/binary_sensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/status/binary_sensor.py b/esphome/components/status/binary_sensor.py index 1f2b7c9d18..adc342ed4d 100644 --- a/esphome/components/status/binary_sensor.py +++ b/esphome/components/status/binary_sensor.py @@ -6,6 +6,8 @@ from esphome.const import ( ENTITY_CATEGORY_DIAGNOSTIC, ) +DEPENDENCIES = ["network"] + status_ns = cg.esphome_ns.namespace("status") StatusBinarySensor = status_ns.class_( "StatusBinarySensor", binary_sensor.BinarySensor, cg.Component From 4c7552eca4af9c43f93e00e41e9ef7b1ed28db92 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sun, 24 Nov 2024 12:40:51 -1000 Subject: [PATCH 0711/1052] keypad binary sensors should be initially off (#7808) Co-authored-by: Samuel Sieb --- esphome/components/binary_sensor/binary_sensor.h | 2 +- .../matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index 301a472810..57cae9e2f5 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -58,7 +58,7 @@ class BinarySensor : public EntityBase, public EntityBase_DeviceClass { void publish_initial_state(bool state); /// The current reported state of the binary sensor. - bool state; + bool state{false}; void add_filter(Filter *filter); void add_filters(const std::vector &filters); diff --git a/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h b/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h index d8a217f55e..2c1ce96f0a 100644 --- a/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h +++ b/esphome/components/matrix_keypad/binary_sensor/matrix_keypad_binary_sensor.h @@ -6,7 +6,7 @@ namespace esphome { namespace matrix_keypad { -class MatrixKeypadBinarySensor : public MatrixKeypadListener, public binary_sensor::BinarySensor { +class MatrixKeypadBinarySensor : public MatrixKeypadListener, public binary_sensor::BinarySensorInitiallyOff { public: MatrixKeypadBinarySensor(uint8_t key) : has_key_(true), key_(key){}; MatrixKeypadBinarySensor(const char *key) : has_key_(true), key_((uint8_t) key[0]){}; From 5ddbe5cdba7faf510b15821e896b6d5b88d8af4d Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 25 Nov 2024 13:58:21 -1000 Subject: [PATCH 0712/1052] [wifi] fix 32 char SSIDs (#7834) Co-authored-by: Samuel Sieb --- .../wifi/wifi_component_esp32_arduino.cpp | 24 +++++++++++++++---- .../wifi/wifi_component_esp8266.cpp | 24 +++++++++++++++---- .../wifi/wifi_component_esp_idf.cpp | 24 +++++++++++++++---- 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 88648093c6..ee00e2ac6c 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -137,8 +137,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.sta.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), ap.get_password().size()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -746,7 +754,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.ap.ssid), sizeof(conf.ap.ssid), "%s", ap.get_ssid().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -757,7 +769,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - snprintf(reinterpret_cast(conf.ap.password), sizeof(conf.ap.password), "%s", ap.get_password().c_str()); + if (ap.get_password().size() > sizeof(conf.ap.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); } // pairwise cipher of SoftAP, group cipher will be derived using this. diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 4568895950..14506f569c 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -236,8 +236,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { struct station_config conf {}; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.password), ap.get_password().c_str(), ap.get_password().size()); if (ap.get_bssid().has_value()) { conf.bssid_set = 1; @@ -775,7 +783,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; struct softap_config conf {}; - snprintf(reinterpret_cast(conf.ssid), sizeof(conf.ssid), "%s", ap.get_ssid().c_str()); + if (ap.get_ssid().size() > sizeof(conf.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ssid_len = static_cast(ap.get_ssid().size()); conf.channel = ap.get_channel().value_or(1); conf.ssid_hidden = ap.get_hidden(); @@ -787,7 +799,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.password = 0; } else { conf.authmode = AUTH_WPA2_PSK; - snprintf(reinterpret_cast(conf.password), sizeof(conf.password), "%s", ap.get_password().c_str()); + if (ap.get_password().size() > sizeof(conf.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.password), ap.get_password().c_str(), ap.get_password().size()); } ETS_UART_INTR_DISABLE(); diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 13870136d4..3074ffbe1b 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -289,8 +289,16 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_wifi.html#_CPPv417wifi_sta_config_t wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - snprintf(reinterpret_cast(conf.sta.ssid), sizeof(conf.sta.ssid), "%s", ap.get_ssid().c_str()); - snprintf(reinterpret_cast(conf.sta.password), sizeof(conf.sta.password), "%s", ap.get_password().c_str()); + if (ap.get_ssid().size() > sizeof(conf.sta.ssid)) { + ESP_LOGE(TAG, "SSID is too long"); + return false; + } + if (ap.get_password().size() > sizeof(conf.sta.password)) { + ESP_LOGE(TAG, "password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.sta.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); + memcpy(reinterpret_cast(conf.sta.password), ap.get_password().c_str(), ap.get_password().size()); // The weakest authmode to accept in the fast scan mode if (ap.get_password().empty()) { @@ -902,7 +910,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { wifi_config_t conf; memset(&conf, 0, sizeof(conf)); - strncpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), sizeof(conf.ap.ssid)); + if (ap.get_ssid().size() > sizeof(conf.ap.ssid)) { + ESP_LOGE(TAG, "AP SSID is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.ssid), ap.get_ssid().c_str(), ap.get_ssid().size()); conf.ap.channel = ap.get_channel().value_or(1); conf.ap.ssid_hidden = ap.get_ssid().size(); conf.ap.max_connection = 5; @@ -913,7 +925,11 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.ap.password = 0; } else { conf.ap.authmode = WIFI_AUTH_WPA2_PSK; - strncpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), sizeof(conf.ap.password)); + if (ap.get_password().size() > sizeof(conf.ap.password)) { + ESP_LOGE(TAG, "AP password is too long"); + return false; + } + memcpy(reinterpret_cast(conf.ap.password), ap.get_password().c_str(), ap.get_password().size()); } // pairwise cipher of SoftAP, group cipher will be derived using this. From 2539cba61047781de159d174f613d7ca0478b9e8 Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Tue, 26 Nov 2024 00:05:20 -1000 Subject: [PATCH 0713/1052] [honeywell] use warning instead of failing (#7862) Co-authored-by: Samuel Sieb --- .../components/honeywellabp2_i2c/honeywellabp2.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp index e2910032cc..d111723669 100644 --- a/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +++ b/esphome/components/honeywellabp2_i2c/honeywellabp2.cpp @@ -15,7 +15,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!"); - this->mark_failed(); + this->status_set_warning("couldn't read sensor data"); return; } float press_counts = encode_uint24(raw_data_[1], raw_data_[2], raw_data_[3]); // calculate digital pressure counts @@ -25,12 +25,13 @@ void HONEYWELLABP2Sensor::read_sensor_data() { (this->max_pressure_ - this->min_pressure_)) + this->min_pressure_; this->last_temperature_ = (temp_counts * 200 / 16777215) - 50; + this->status_clear_warning(); } void HONEYWELLABP2Sensor::start_measurement() { if (this->write(i2c_cmd_, 3) != i2c::ERROR_OK) { ESP_LOGE(TAG, "Communication with ABP2 failed!"); - this->mark_failed(); + this->status_set_warning("couldn't start measurement"); return; } this->measurement_running_ = true; @@ -39,7 +40,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!"); - this->mark_failed(); + this->status_set_warning("couldn't check measurement"); return false; } if ((raw_data_[0] & (0x1 << STATUS_BIT_BUSY)) > 0) { @@ -52,7 +53,7 @@ bool HONEYWELLABP2Sensor::is_measurement_ready() { void HONEYWELLABP2Sensor::measurement_timeout() { ESP_LOGE(TAG, "Timeout!"); this->measurement_running_ = false; - this->mark_failed(); + this->status_set_warning("measurement timed out"); } float HONEYWELLABP2Sensor::get_pressure() { return this->last_pressure_; } @@ -79,7 +80,7 @@ void HONEYWELLABP2Sensor::update() { ESP_LOGV(TAG, "Update Honeywell ABP2 Sensor"); this->start_measurement(); - this->set_timeout("meas_timeout", 50, [this] { this->measurement_timeout(); }); + this->set_timeout("meas_timeout", 100, [this] { this->measurement_timeout(); }); } void HONEYWELLABP2Sensor::dump_config() { From c894645747d742937cfedc572dcc319f2a8caaa5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:06:21 +1300 Subject: [PATCH 0714/1052] Bump version to 2024.11.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index d14cdecb23..4b19e2865d 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.1" +__version__ = "2024.11.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From e6c730ab109ee2b896283399c3f7453c1591d1de Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:16:54 -0600 Subject: [PATCH 0715/1052] [max31865] clang-tidy fixes for #7822 (#7876) --- esphome/components/max31865/max31865.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/max31865/max31865.cpp b/esphome/components/max31865/max31865.cpp index b48aa2fdd3..4749874ac7 100644 --- a/esphome/components/max31865/max31865.cpp +++ b/esphome/components/max31865/max31865.cpp @@ -106,7 +106,8 @@ void MAX31865Sensor::read_data_() { // Check faults const uint8_t faults = this->read_register_(FAULT_STATUS_REG); - if ((has_fault_ = faults & 0b00111100)) { + has_fault_ = faults & 0b00111100; + if (has_fault_) { if (faults & (1 << 2)) { ESP_LOGE(TAG, "Overvoltage/undervoltage fault"); } @@ -125,7 +126,8 @@ void MAX31865Sensor::read_data_() { } else { this->status_clear_error(); } - if ((has_warn_ = faults & 0b11000000)) { + has_warn_ = faults & 0b11000000; + if (has_warn_) { if (faults & (1 << 6)) { ESP_LOGW(TAG, "RTD Low Threshold"); } From 8439232b11ad720c0896fa6bf66ab3594ef91a53 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:18:43 -0600 Subject: [PATCH 0716/1052] [esp32_ble] clang-tidy fixes for #7822 (#7883) --- esphome/components/esp32_ble/ble_uuid.cpp | 19 +++++++++---------- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 5 ++++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp index 07ac719434..aa1edd96b2 100644 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ b/esphome/components/esp32_ble/ble_uuid.cpp @@ -34,7 +34,7 @@ ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) { ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) { ESPBTUUID ret; ret.uuid_.len = ESP_UUID_LEN_128; - for (int i = 0; i < ESP_UUID_LEN_128; i++) + for (uint8_t i = 0; i < ESP_UUID_LEN_128; i++) ret.uuid_.uuid.uuid128[ESP_UUID_LEN_128 - 1 - i] = data[i]; return ret; } @@ -43,30 +43,30 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { if (data.length() == 4) { ret.uuid_.len = ESP_UUID_LEN_16; ret.uuid_.uuid.uuid16 = 0; - for (int i = 0; i < data.length();) { + for (uint i = 0; i < data.length(); i += 2) { uint8_t msb = data.c_str()[i]; uint8_t lsb = data.c_str()[i + 1]; + uint8_t lsb_shift = i <= 2 ? (2 - i) * 4 : 0; if (msb > '9') msb -= 7; if (lsb > '9') lsb -= 7; - ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (2 - i) * 4; - i += 2; + ret.uuid_.uuid.uuid16 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << lsb_shift; } } else if (data.length() == 8) { ret.uuid_.len = ESP_UUID_LEN_32; ret.uuid_.uuid.uuid32 = 0; - for (int i = 0; i < data.length();) { + for (uint i = 0; i < data.length(); i += 2) { uint8_t msb = data.c_str()[i]; uint8_t lsb = data.c_str()[i + 1]; + uint8_t lsb_shift = i <= 6 ? (6 - i) * 4 : 0; if (msb > '9') msb -= 7; if (lsb > '9') lsb -= 7; - ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << (6 - i) * 4; - i += 2; + ret.uuid_.uuid.uuid32 += (((msb & 0x0F) << 4) | (lsb & 0x0F)) << lsb_shift; } } else if (data.length() == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be // investigated (lack of time) @@ -77,7 +77,7 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { // UUID format. ret.uuid_.len = ESP_UUID_LEN_128; int n = 0; - for (int i = 0; i < data.length();) { + for (uint i = 0; i < data.length(); i += 2) { if (data.c_str()[i] == '-') i++; uint8_t msb = data.c_str()[i]; @@ -88,7 +88,6 @@ ESPBTUUID ESPBTUUID::from_raw(const std::string &data) { if (lsb > '9') lsb -= 7; ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F); - i += 2; } } else { ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data.c_str()); @@ -155,7 +154,7 @@ bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const { } break; case ESP_UUID_LEN_128: - for (int i = 0; i < ESP_UUID_LEN_128; i++) { + for (uint8_t i = 0; i < ESP_UUID_LEN_128; i++) { if (uuid.uuid_.uuid.uuid128[i] != this->uuid_.uuid.uuid128[i]) { return false; } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index b86d32ee61..6d051e3d4a 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -432,7 +432,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE ESP_LOGVV(TAG, "Parse Result:"); - const char *address_type = ""; + const char *address_type; switch (this->address_type_) { case BLE_ADDR_TYPE_PUBLIC: address_type = "PUBLIC"; @@ -446,6 +446,9 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e case BLE_ADDR_TYPE_RPA_RANDOM: address_type = "RPA_RANDOM"; break; + default: + address_type = "UNKNOWN"; + break; } ESP_LOGVV(TAG, " Address: %02X:%02X:%02X:%02X:%02X:%02X (%s)", this->address_[0], this->address_[1], this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type); From f2e8e655ba6476301842f260197e8e52f4ce71eb Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:19:41 -0600 Subject: [PATCH 0717/1052] [mqtt] clang-tidy fixes for #7822 (#7877) --- .../components/mqtt/mqtt_alarm_control_panel.cpp | 7 ++----- esphome/components/mqtt/mqtt_climate.cpp | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp index 660a030d11..4cc4773bd3 100644 --- a/esphome/components/mqtt/mqtt_alarm_control_panel.cpp +++ b/esphome/components/mqtt/mqtt_alarm_control_panel.cpp @@ -80,8 +80,7 @@ const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return th bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); } bool MQTTAlarmControlPanelComponent::publish_state() { - bool success = true; - const char *state_s = ""; + const char *state_s; switch (this->alarm_control_panel_->get_state()) { case ACP_STATE_DISARMED: state_s = "disarmed"; @@ -116,9 +115,7 @@ bool MQTTAlarmControlPanelComponent::publish_state() { default: state_s = "unknown"; } - if (!this->publish(this->get_state_topic_(), state_s)) - success = false; - return success; + return this->publish(this->get_state_topic_(), state_s); } } // namespace mqtt diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 773d863835..f06574fa26 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -257,7 +257,7 @@ const EntityBase *MQTTClimateComponent::get_entity() const { return this->device bool MQTTClimateComponent::publish_state_() { auto traits = this->device_->get_traits(); // mode - const char *mode_s = ""; + const char *mode_s; switch (this->device_->mode) { case CLIMATE_MODE_OFF: mode_s = "off"; @@ -280,6 +280,8 @@ bool MQTTClimateComponent::publish_state_() { case CLIMATE_MODE_HEAT_COOL: mode_s = "heat_cool"; break; + default: + mode_s = "unknown"; } bool success = true; if (!this->publish(this->get_mode_state_topic(), mode_s)) @@ -343,6 +345,8 @@ bool MQTTClimateComponent::publish_state_() { case CLIMATE_PRESET_ACTIVITY: payload = "activity"; break; + default: + payload = "unknown"; } } if (this->device_->custom_preset.has_value()) @@ -352,7 +356,7 @@ bool MQTTClimateComponent::publish_state_() { } if (traits.get_supports_action()) { - const char *payload = "unknown"; + const char *payload; switch (this->device_->action) { case CLIMATE_ACTION_OFF: payload = "off"; @@ -372,6 +376,8 @@ bool MQTTClimateComponent::publish_state_() { case CLIMATE_ACTION_FAN: payload = "fan"; break; + default: + payload = "unknown"; } if (!this->publish(this->get_action_state_topic(), payload)) success = false; @@ -411,6 +417,8 @@ bool MQTTClimateComponent::publish_state_() { case CLIMATE_FAN_QUIET: payload = "quiet"; break; + default: + payload = "unknown"; } } if (this->device_->custom_fan_mode.has_value()) @@ -420,7 +428,7 @@ bool MQTTClimateComponent::publish_state_() { } if (traits.get_supports_swing_modes()) { - const char *payload = ""; + const char *payload; switch (this->device_->swing_mode) { case CLIMATE_SWING_OFF: payload = "off"; @@ -434,6 +442,8 @@ bool MQTTClimateComponent::publish_state_() { case CLIMATE_SWING_HORIZONTAL: payload = "horizontal"; break; + default: + payload = "unknown"; } if (!this->publish(this->get_swing_mode_state_topic(), payload)) success = false; From 4da57c35d0c1b1a3ec413fe8352f71d7d7e12d0e Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:20:51 -0600 Subject: [PATCH 0718/1052] [uln2003] clang-tidy fixes for #7822 (#7881) --- esphome/components/uln2003/uln2003.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/esphome/components/uln2003/uln2003.cpp b/esphome/components/uln2003/uln2003.cpp index 1af9806906..991fe53487 100644 --- a/esphome/components/uln2003/uln2003.cpp +++ b/esphome/components/uln2003/uln2003.cpp @@ -40,7 +40,7 @@ void ULN2003::dump_config() { LOG_PIN(" Pin C: ", this->pin_c_); LOG_PIN(" Pin D: ", this->pin_d_); ESP_LOGCONFIG(TAG, " Sleep when done: %s", YESNO(this->sleep_when_done_)); - const char *step_mode_s = ""; + const char *step_mode_s; switch (this->step_mode_) { case ULN2003_STEP_MODE_FULL_STEP: step_mode_s = "FULL STEP"; @@ -51,6 +51,9 @@ void ULN2003::dump_config() { case ULN2003_STEP_MODE_WAVE_DRIVE: step_mode_s = "WAVE DRIVE"; break; + default: + step_mode_s = "UNKNOWN"; + break; } ESP_LOGCONFIG(TAG, " Step Mode: %s", step_mode_s); } From 567256bd62cb39ec9706af6b36ca405e584eae5d Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:21:10 -0600 Subject: [PATCH 0719/1052] [rotary_encoder] clang-tidy fixes for #7822 (#7880) --- esphome/components/rotary_encoder/rotary_encoder.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index a3631ffe27..e9a0eac3f5 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -162,7 +162,7 @@ void RotaryEncoderSensor::dump_config() { LOG_PIN(" Pin B: ", this->pin_b_); LOG_PIN(" Pin I: ", this->pin_i_); - const LogString *restore_mode = LOG_STR(""); + const LogString *restore_mode; switch (this->restore_mode_) { case ROTARY_ENCODER_RESTORE_DEFAULT_ZERO: restore_mode = LOG_STR("Restore (Defaults to zero)"); @@ -170,6 +170,8 @@ void RotaryEncoderSensor::dump_config() { case ROTARY_ENCODER_ALWAYS_ZERO: restore_mode = LOG_STR("Always zero"); break; + default: + restore_mode = LOG_STR(""); } ESP_LOGCONFIG(TAG, " Restore Mode: %s", LOG_STR_ARG(restore_mode)); From 65a5216d17b14fddb803906074faa3fb8ef1512f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:22:18 -0600 Subject: [PATCH 0720/1052] [pca6416a, pca9554] clang-tidy fixes for #7822 (#7879) --- esphome/components/pca6416a/pca6416a.cpp | 8 +++++--- esphome/components/pca9554/pca9554.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/esphome/components/pca6416a/pca6416a.cpp b/esphome/components/pca6416a/pca6416a.cpp index 1f4e315644..53c0dcaf76 100644 --- a/esphome/components/pca6416a/pca6416a.cpp +++ b/esphome/components/pca6416a/pca6416a.cpp @@ -34,7 +34,7 @@ void PCA6416AComponent::setup() { } // Test to see if the device supports pull-up resistors - if (this->read_register(PCAL6416A_PULL_EN0, &value, 1, true) == esphome::i2c::ERROR_OK) { + if (this->read_register(PCAL6416A_PULL_EN0, &value, 1, true) == i2c::ERROR_OK) { this->has_pullup_ = true; } @@ -106,7 +106,8 @@ bool PCA6416AComponent::read_register_(uint8_t reg, uint8_t *value) { return false; } - if ((this->last_error_ = this->read_register(reg, value, 1, true)) != esphome::i2c::ERROR_OK) { + this->last_error_ = this->read_register(reg, value, 1, true); + if (this->last_error_ != i2c::ERROR_OK) { this->status_set_warning(); ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); return false; @@ -122,7 +123,8 @@ bool PCA6416AComponent::write_register_(uint8_t reg, uint8_t value) { return false; } - if ((this->last_error_ = this->write_register(reg, &value, 1, true)) != esphome::i2c::ERROR_OK) { + this->last_error_ = this->write_register(reg, &value, 1, true); + if (this->last_error_ != i2c::ERROR_OK) { this->status_set_warning(); ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); return false; diff --git a/esphome/components/pca9554/pca9554.cpp b/esphome/components/pca9554/pca9554.cpp index c5a4bcfb09..78b877072a 100644 --- a/esphome/components/pca9554/pca9554.cpp +++ b/esphome/components/pca9554/pca9554.cpp @@ -95,8 +95,8 @@ bool PCA9554Component::read_inputs_() { return false; } - if ((this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true)) != - esphome::i2c::ERROR_OK) { + this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true); + if (this->last_error_ != i2c::ERROR_OK) { this->status_set_warning(); ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); return false; @@ -113,8 +113,8 @@ bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) { uint8_t outputs[2]; outputs[0] = (uint8_t) value; outputs[1] = (uint8_t) (value >> 8); - if ((this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true)) != - esphome::i2c::ERROR_OK) { + this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true); + if (this->last_error_ != i2c::ERROR_OK) { this->status_set_warning(); ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); return false; From a825ef59d47d0550983c733d5730d41ff52eaef7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:22:37 -0600 Subject: [PATCH 0721/1052] [nextion] clang-tidy fixes for #7822 (#7878) --- esphome/components/nextion/nextion.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 7c41f8dfe2..50a5834347 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -563,13 +563,10 @@ void Nextion::process_nextion_commands_() { break; } - int dataindex = 0; - int value = 0; for (int i = 0; i < 4; ++i) { value += to_process[i] << (8 * i); - ++dataindex; } NextionQueue *nb = this->nextion_queue_.front(); From 12cdeca48a78169069e462c3dc8785268d9ac90f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:23:20 -0600 Subject: [PATCH 0722/1052] [various] clang-tidy fixes for #7822 (#7874) --- .../hitachi_ac344/hitachi_ac344.cpp | 6 ++- .../hitachi_ac424/hitachi_ac424.cpp | 6 ++- .../components/ili9xxx/ili9xxx_display.cpp | 5 +- esphome/components/qspi_dbi/qspi_dbi.cpp | 3 +- esphome/components/st7735/st7735.cpp | 2 +- esphome/components/st7789v/st7789v.cpp | 2 +- esphome/components/udp/udp_component.cpp | 48 ++++++++++++------- esphome/core/component.cpp | 2 +- 8 files changed, 48 insertions(+), 26 deletions(-) diff --git a/esphome/components/hitachi_ac344/hitachi_ac344.cpp b/esphome/components/hitachi_ac344/hitachi_ac344.cpp index 2825e4f04c..2bcb205644 100644 --- a/esphome/components/hitachi_ac344/hitachi_ac344.cpp +++ b/esphome/components/hitachi_ac344/hitachi_ac344.cpp @@ -133,8 +133,10 @@ bool HitachiClimate::get_swing_v_() { } void HitachiClimate::set_swing_h_(uint8_t position) { - if (position > HITACHI_AC344_SWINGH_LEFT_MAX) - return set_swing_h_(HITACHI_AC344_SWINGH_MIDDLE); + if (position > HITACHI_AC344_SWINGH_LEFT_MAX) { + set_swing_h_(HITACHI_AC344_SWINGH_MIDDLE); + return; + } set_bits(&remote_state_[HITACHI_AC344_SWINGH_BYTE], HITACHI_AC344_SWINGH_OFFSET, HITACHI_AC344_SWINGH_SIZE, position); set_button_(HITACHI_AC344_BUTTON_SWINGH); } diff --git a/esphome/components/hitachi_ac424/hitachi_ac424.cpp b/esphome/components/hitachi_ac424/hitachi_ac424.cpp index 0bfc3ae564..64f23dfc17 100644 --- a/esphome/components/hitachi_ac424/hitachi_ac424.cpp +++ b/esphome/components/hitachi_ac424/hitachi_ac424.cpp @@ -133,8 +133,10 @@ bool HitachiClimate::get_swing_v_() { } void HitachiClimate::set_swing_h_(uint8_t position) { - if (position > HITACHI_AC424_SWINGH_LEFT_MAX) - return set_swing_h_(HITACHI_AC424_SWINGH_MIDDLE); + if (position > HITACHI_AC424_SWINGH_LEFT_MAX) { + set_swing_h_(HITACHI_AC424_SWINGH_MIDDLE); + return; + } set_bits(&remote_state_[HITACHI_AC424_SWINGH_BYTE], HITACHI_AC424_SWINGH_OFFSET, HITACHI_AC424_SWINGH_SIZE, position); set_button_(HITACHI_AC424_BUTTON_SWINGH); } diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index 81976dd2c9..b9664067a9 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -313,8 +313,9 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons // do color conversion pixel-by-pixel into the buffer and draw it later. If this is happening the user has not // configured the renderer well. if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || !big_endian) { - return display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, - x_pad); + display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, + x_pad); + return; } this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index 785885d4ec..f8fd5dd374 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -146,7 +146,8 @@ void QspiDbi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8 return; if (bitness != display::COLOR_BITNESS_565 || order != this->color_mode_ || big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST)) { - return Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); + Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); + return; } else if (this->draw_from_origin_) { auto stride = x_offset + w + x_pad; for (int y = 0; y != h; y++) { diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index a0c2d80d16..5985d8bfb3 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -483,7 +483,7 @@ void ST7735::spi_master_write_color_(uint16_t color, uint16_t size) { } this->dc_pin_->digital_write(true); - return write_array(byte, size * 2); + write_array(byte, size * 2); } } // namespace st7735 diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index 74c7a4e9e3..0d2f35aef5 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -252,7 +252,7 @@ void ST7789V::write_color_(uint16_t color, uint16_t size) { } this->dc_pin_->digital_write(true); - return write_array(byte, size * 2); + write_array(byte, size * 2); } size_t ST7789V::get_buffer_length_() { diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index a1c8889997..b8727ec423 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -434,7 +434,8 @@ static bool process_rolling_code(Provider &provider, uint8_t *&buf, const uint8_ void UDPComponent::process_(uint8_t *buf, const size_t len) { auto ping_key_seen = !this->ping_pong_enable_; if (len < 8) { - return ESP_LOGV(TAG, "Bad length %zu", len); + ESP_LOGV(TAG, "Bad length %zu", len); + return; } char namebuf[256]{}; uint8_t byte; @@ -442,31 +443,40 @@ void UDPComponent::process_(uint8_t *buf, const size_t len) { const uint8_t *end = buf + len; FuData rdata{}; auto magic = get_uint16(buf); - if (magic != MAGIC_NUMBER && magic != MAGIC_PING) - return ESP_LOGV(TAG, "Bad magic %X", magic); + if (magic != MAGIC_NUMBER && magic != MAGIC_PING) { + ESP_LOGV(TAG, "Bad magic %X", magic); + return; + } auto hlen = *buf++; if (hlen > len - 3) { - return ESP_LOGV(TAG, "Bad hostname length %u > %zu", hlen, len - 3); + ESP_LOGV(TAG, "Bad hostname length %u > %zu", hlen, len - 3); + return; } memcpy(namebuf, buf, hlen); if (strcmp(this->name_, namebuf) == 0) { - return ESP_LOGV(TAG, "Ignoring our own data"); + ESP_LOGV(TAG, "Ignoring our own data"); + return; } buf += hlen; - if (magic == MAGIC_PING) - return this->process_ping_request_(namebuf, buf, end - buf); + if (magic == MAGIC_PING) { + this->process_ping_request_(namebuf, buf, end - buf); + return; + } if (round4(len) != len) { - return ESP_LOGW(TAG, "Bad length %zu", len); + ESP_LOGW(TAG, "Bad length %zu", len); + return; } hlen = round4(hlen + 3); buf = start_ptr + hlen; if (buf == end) { - return ESP_LOGV(TAG, "No data after header"); + ESP_LOGV(TAG, "No data after header"); + return; } if (this->providers_.count(namebuf) == 0) { - return ESP_LOGVV(TAG, "Unknown hostname %s", namebuf); + ESP_LOGVV(TAG, "Unknown hostname %s", namebuf); + return; } auto &provider = this->providers_[namebuf]; // if encryption not used with this host, ping check is pointless since it would be easily spoofed. @@ -489,7 +499,8 @@ void UDPComponent::process_(uint8_t *buf, const size_t len) { if (!process_rolling_code(provider, buf, end)) return; } else if (byte != DATA_KEY) { - return ESP_LOGV(TAG, "Expected rolling_key or data_key, got %X", byte); + ESP_LOGV(TAG, "Expected rolling_key or data_key, got %X", byte); + return; } while (buf < end) { byte = *buf++; @@ -497,7 +508,8 @@ void UDPComponent::process_(uint8_t *buf, const size_t len) { continue; if (byte == PING_KEY) { if (end - buf < 4) { - return ESP_LOGV(TAG, "PING_KEY requires 4 more bytes"); + ESP_LOGV(TAG, "PING_KEY requires 4 more bytes"); + return; } auto key = get_uint32(buf); if (key == this->ping_key_) { @@ -515,21 +527,25 @@ void UDPComponent::process_(uint8_t *buf, const size_t len) { } if (byte == BINARY_SENSOR_KEY) { if (end - buf < 3) { - return ESP_LOGV(TAG, "Binary sensor key requires at least 3 more bytes"); + ESP_LOGV(TAG, "Binary sensor key requires at least 3 more bytes"); + return; } rdata.u32 = *buf++; } else if (byte == SENSOR_KEY) { if (end - buf < 6) { - return ESP_LOGV(TAG, "Sensor key requires at least 6 more bytes"); + ESP_LOGV(TAG, "Sensor key requires at least 6 more bytes"); + return; } rdata.u32 = get_uint32(buf); } else { - return ESP_LOGW(TAG, "Unknown key byte %X", byte); + ESP_LOGW(TAG, "Unknown key byte %X", byte); + return; } hlen = *buf++; if (end - buf < hlen) { - return ESP_LOGV(TAG, "Name length of %u not available", hlen); + ESP_LOGV(TAG, "Name length of %u not available", hlen); + return; } memset(namebuf, 0, sizeof namebuf); memcpy(namebuf, buf, hlen); diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index ae73a451d9..a6224a17c0 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -67,7 +67,7 @@ bool Component::cancel_retry(const std::string &name) { // NOLINT } void Component::set_timeout(const std::string &name, uint32_t timeout, std::function &&f) { // NOLINT - return App.scheduler.set_timeout(this, name, timeout, std::move(f)); + App.scheduler.set_timeout(this, name, timeout, std::move(f)); } bool Component::cancel_timeout(const std::string &name) { // NOLINT From e229ed0da3f7113757907b1b7db1934517390c11 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:23:40 -0600 Subject: [PATCH 0723/1052] [logger] clang-tidy fixes for #7822 (#7875) --- esphome/components/logger/logger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index dac08fbbce..36934c7459 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -47,7 +47,7 @@ void Logger::write_header_(int level, const char *tag, int line) { if (current_task == main_task_) { this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line); } else { - const char *thread_name = ""; + const char *thread_name = ""; // NOLINT(clang-analyzer-deadcode.DeadStores) #if defined(USE_ESP32) thread_name = pcTaskGetName(current_task); #elif defined(USE_LIBRETINY) From e124151e5c4c6ca925baf382e7dca19592a257c9 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:24:43 -0600 Subject: [PATCH 0724/1052] [ezo] clang-tidy fixes for #7822 (#7873) --- esphome/components/ezo/ezo.cpp | 40 ++++++++++++---------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 8e4486dbf2..10f3d530ce 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -111,11 +111,11 @@ void EZOSensor::loop() { if (buf[0] == 1) { std::string payload = reinterpret_cast(&buf[1]); if (!payload.empty()) { + auto start_location = payload.find(','); switch (to_run->command_type) { case EzoCommandType::EZO_READ: { // some sensors return multiple comma-separated values, terminate string after first one - int start_location = 0; - if ((start_location = payload.find(',')) != std::string::npos) { + if (start_location != std::string::npos) { payload.erase(start_location); } auto val = parse_number(payload); @@ -126,49 +126,37 @@ void EZOSensor::loop() { } break; } - case EzoCommandType::EZO_LED: { + case EzoCommandType::EZO_LED: this->led_callback_.call(payload.back() == '1'); break; - } - case EzoCommandType::EZO_DEVICE_INFORMATION: { - int start_location = 0; - if ((start_location = payload.find(',')) != std::string::npos) { + case EzoCommandType::EZO_DEVICE_INFORMATION: + if (start_location != std::string::npos) { this->device_infomation_callback_.call(payload.substr(start_location + 1)); } break; - } - case EzoCommandType::EZO_SLOPE: { - int start_location = 0; - if ((start_location = payload.find(',')) != std::string::npos) { + case EzoCommandType::EZO_SLOPE: + if (start_location != std::string::npos) { this->slope_callback_.call(payload.substr(start_location + 1)); } break; - } - case EzoCommandType::EZO_CALIBRATION: { - int start_location = 0; - if ((start_location = payload.find(',')) != std::string::npos) { + case EzoCommandType::EZO_CALIBRATION: + if (start_location != std::string::npos) { this->calibration_callback_.call(payload.substr(start_location + 1)); } break; - } - case EzoCommandType::EZO_T: { - int start_location = 0; - if ((start_location = payload.find(',')) != std::string::npos) { + case EzoCommandType::EZO_T: + if (start_location != std::string::npos) { this->t_callback_.call(payload.substr(start_location + 1)); } break; - } - case EzoCommandType::EZO_CUSTOM: { + case EzoCommandType::EZO_CUSTOM: this->custom_callback_.call(payload); break; - } - default: { + default: break; - } } } } - this->commands_.pop_front(); } @@ -178,7 +166,7 @@ void EZOSensor::add_command_(const std::string &command, EzoCommandType command_ ezo_command->command_type = command_type; ezo_command->delay_ms = delay_ms; this->commands_.push_back(std::move(ezo_command)); -}; +} void EZOSensor::set_calibration_point_(EzoCalibrationType type, float value) { std::string payload = str_sprintf("Cal,%s,%0.2f", EZO_CALIBRATION_TYPE_STRINGS[type], value); From 7aa3a1a1ccdb8e3cf2df837817e18a7307b90640 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:25:00 -0600 Subject: [PATCH 0725/1052] [apds9306] clang-tidy fixes for #7822 (#7872) --- esphome/components/apds9306/apds9306.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/apds9306/apds9306.cpp b/esphome/components/apds9306/apds9306.cpp index 7b79b0964c..bbb3ba1910 100644 --- a/esphome/components/apds9306/apds9306.cpp +++ b/esphome/components/apds9306/apds9306.cpp @@ -122,7 +122,8 @@ void APDS9306::update() { this->status_clear_warning(); - if (!(status &= 0b00001000)) { // No new data + status &= 0b00001000; + if (!status) { // No new data return; } From ff5004d7db6514536dfd0e28ec3d114bb14b120c Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:25:15 -0600 Subject: [PATCH 0726/1052] [dht] clang-tidy fixes for #7822 (#7871) --- esphome/components/dht/dht.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index db1c851d5f..3f9f9c57f4 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -135,7 +135,8 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r // Wait for falling edge while (this->pin_->digital_read()) { - if ((end_time = micros()) - start_time > 90) { + end_time = micros(); + if (end_time - start_time > 90) { if (i < 0) { error_code = 3; } else { From d30587028412d80a02ea1fc4ef7cd0085b1508d8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 27 Nov 2024 16:25:34 -0600 Subject: [PATCH 0727/1052] [network] clang-tidy fixes for #7822 (#7870) --- esphome/components/network/ip_address.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 941934cf0a..69d3788ca5 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -116,7 +116,7 @@ struct IPAddress { operator arduino_ns::IPAddress() const { return ip_addr_get_ip4_u32(&ip_addr_); } #endif - bool is_set() { return !ip_addr_isany(&ip_addr_); } + bool is_set() { return !ip_addr_isany(&ip_addr_); } // NOLINT(readability-simplify-boolean-expr) bool is_ip4() { return IP_IS_V4(&ip_addr_); } bool is_ip6() { return IP_IS_V6(&ip_addr_); } std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); } From c9b0490305fb402f13f7ee4ee449db9d207c978e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:48:48 +1100 Subject: [PATCH 0728/1052] [lvgl] Make image update via lambda work (#7886) --- esphome/components/lvgl/defines.py | 8 +++++- esphome/components/lvgl/lv_validation.py | 11 +++++--- esphome/components/lvgl/lvgl_esphome.h | 16 ++++++++++++ esphome/components/lvgl/widgets/animimg.py | 29 +++++++++------------- esphome/components/lvgl/widgets/img.py | 2 -- tests/components/lvgl/lvgl-package.yaml | 12 ++++++--- tests/components/lvgl/test.esp32-ard.yaml | 2 +- 7 files changed, 52 insertions(+), 28 deletions(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index ea345fa55c..81984637bd 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -8,7 +8,7 @@ import logging from esphome import codegen as cg, config_validation as cv from esphome.const import CONF_ITEMS -from esphome.core import Lambda +from esphome.core import ID, Lambda from esphome.cpp_generator import LambdaExpression, MockObj from esphome.cpp_types import uint32 from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -72,6 +72,12 @@ class LValidator: ) if self.retmapper is not None: return self.retmapper(value) + if isinstance(value, ID): + return await cg.get_variable(value) + if isinstance(value, list): + value = [ + await cg.get_variable(x) if isinstance(x, ID) else x for x in value + ] return cg.safe_exp(value) diff --git a/esphome/components/lvgl/lv_validation.py b/esphome/components/lvgl/lv_validation.py index 766c010244..f91ed893f2 100644 --- a/esphome/components/lvgl/lv_validation.py +++ b/esphome/components/lvgl/lv_validation.py @@ -1,6 +1,7 @@ from typing import Union import esphome.codegen as cg +from esphome.components import image from esphome.components.color import CONF_HEX, ColorStruct, from_rgbw from esphome.components.font import Font from esphome.components.image import Image_ @@ -31,7 +32,7 @@ from .defines import ( literal, ) from .helpers import add_lv_use, esphome_fonts_used, lv_fonts_used, requires_component -from .types import lv_font_t, lv_gradient_t, lv_img_t +from .types import lv_font_t, lv_gradient_t opacity_consts = LvConstant("LV_OPA_", "TRANSP", "COVER") @@ -332,8 +333,12 @@ def image_validator(value): lv_image = LValidator( image_validator, - lv_img_t, - retmapper=lambda x: MockObj(x, "->").get_lv_img_dsc(), + image.Image_.operator("ptr"), + requires="image", +) +lv_image_list = LValidator( + cv.ensure_list(image_validator), + cg.std_vector.template(image.Image_.operator("ptr")), requires="image", ) lv_bool = LValidator(cv.boolean, cg.bool_, retmapper=literal) diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 208cb1cbd5..921b7c109f 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -57,6 +57,22 @@ inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) { lv_img_set_src(obj, image->get_lv_img_dsc()); } #endif // USE_LVGL_IMAGE +#ifdef USE_LVGL_ANIMIMG +inline void lv_animimg_set_src(lv_obj_t *img, std::vector images) { + auto *dsc = static_cast *>(lv_obj_get_user_data(img)); + if (dsc == nullptr) { + // object will be lazily allocated but never freed. + dsc = new std::vector(images.size()); // NOLINT + lv_obj_set_user_data(img, dsc); + } + dsc->clear(); + for (auto &image : images) { + dsc->push_back(image->get_lv_img_dsc()); + } + lv_animimg_set_src(img, (const void **) dsc->data(), dsc->size()); +} + +#endif // USE_LVGL_ANIMIMG // Parent class for things that wrap an LVGL object class LvCompound { diff --git a/esphome/components/lvgl/widgets/animimg.py b/esphome/components/lvgl/widgets/animimg.py index 8adea72ad3..b824d28fb8 100644 --- a/esphome/components/lvgl/widgets/animimg.py +++ b/esphome/components/lvgl/widgets/animimg.py @@ -1,20 +1,18 @@ from esphome import automation -import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_DURATION, CONF_ID from ..automation import action_to_code from ..defines import CONF_AUTO_START, CONF_MAIN, CONF_REPEAT_COUNT, CONF_SRC from ..helpers import lvgl_components_required -from ..lv_validation import lv_image, lv_milliseconds +from ..lv_validation import lv_image_list, lv_milliseconds from ..lvcode import lv -from ..types import LvType, ObjUpdateAction, void_ptr +from ..types import LvType, ObjUpdateAction from . import Widget, WidgetType, get_widgets from .img import CONF_IMAGE from .label import CONF_LABEL CONF_ANIMIMG = "animimg" -CONF_SRC_LIST_ID = "src_list_id" def lv_repeat_count(value): @@ -32,14 +30,14 @@ ANIMIMG_BASE_SCHEMA = cv.Schema( ANIMIMG_SCHEMA = ANIMIMG_BASE_SCHEMA.extend( { cv.Required(CONF_DURATION): lv_milliseconds, - cv.Required(CONF_SRC): cv.ensure_list(lv_image), - cv.GenerateID(CONF_SRC_LIST_ID): cv.declare_id(void_ptr), + cv.Required(CONF_SRC): lv_image_list, } ) ANIMIMG_MODIFY_SCHEMA = ANIMIMG_BASE_SCHEMA.extend( { cv.Optional(CONF_DURATION): lv_milliseconds, + cv.Optional(CONF_SRC): lv_image_list, } ) @@ -59,17 +57,14 @@ class AnimimgType(WidgetType): async def to_code(self, w: Widget, config): lvgl_components_required.add(CONF_IMAGE) lvgl_components_required.add(CONF_ANIMIMG) - if CONF_SRC in config: - srcs = [ - await lv_image.process(await cg.get_variable(x)) - for x in config[CONF_SRC] - ] - src_id = cg.static_const_array(config[CONF_SRC_LIST_ID], srcs) - count = len(config[CONF_SRC]) - lv.animimg_set_src(w.obj, src_id, count) - lv.animimg_set_repeat_count(w.obj, config[CONF_REPEAT_COUNT]) - lv.animimg_set_duration(w.obj, config[CONF_DURATION]) - if config.get(CONF_AUTO_START): + if srcs := config.get(CONF_SRC): + srcs = await lv_image_list.process(srcs) + lv.animimg_set_src(w.obj, srcs) + if repeat_count := config.get(CONF_REPEAT_COUNT): + lv.animimg_set_repeat_count(w.obj, repeat_count) + if duration := config.get(CONF_DURATION): + lv.animimg_set_duration(w.obj, duration) + if config[CONF_AUTO_START]: lv.animimg_start(w.obj) def get_uses(self): diff --git a/esphome/components/lvgl/widgets/img.py b/esphome/components/lvgl/widgets/img.py index 931d0c0b5b..59b2c97c63 100644 --- a/esphome/components/lvgl/widgets/img.py +++ b/esphome/components/lvgl/widgets/img.py @@ -1,4 +1,3 @@ -import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ANGLE, CONF_MODE @@ -65,7 +64,6 @@ class ImgType(WidgetType): async def to_code(self, w: Widget, config): if src := config.get(CONF_SRC): - src = await cg.get_variable(src) lv.img_set_src(w.obj, await lv_image.process(src)) if (cf_angle := config.get(CONF_ANGLE)) is not None: pivot_x = config[CONF_PIVOT_X] diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index db0443b3bb..81b18c4ff8 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -171,9 +171,13 @@ lvgl: duration: 1s auto_start: true on_all_events: - logger.log: - format: "Event %s" - args: ['lv_event_code_name_for(event->code).c_str()'] + - logger.log: + format: "Event %s" + args: ['lv_event_code_name_for(event->code).c_str()'] + - lvgl.animimg.update: + id: anim_img + src: !lambda "return {dog_image, cat_image};" + duration: 2s - label: id: hello_label text: Hello world @@ -635,7 +639,7 @@ lvgl: - image: grid_cell_row_pos: 0 grid_cell_column_pos: 0 - src: dog_image + src: !lambda return dog_image; on_click: then: - lvgl.tabview.select: diff --git a/tests/components/lvgl/test.esp32-ard.yaml b/tests/components/lvgl/test.esp32-ard.yaml index 80d5ce503f..5b09147de7 100644 --- a/tests/components/lvgl/test.esp32-ard.yaml +++ b/tests/components/lvgl/test.esp32-ard.yaml @@ -55,5 +55,5 @@ lvgl: packages: lvgl: !include lvgl-package.yaml + xvgl: !include common.yaml -<<: !include common.yaml From 7cdf5b55ef637e76d13e31b596d7b7d76a5b4ab8 Mon Sep 17 00:00:00 2001 From: Max Slotov Date: Thu, 28 Nov 2024 05:51:07 +0200 Subject: [PATCH 0729/1052] [deep_sleep] fix deep_sleep not keeping awake when sleep_duration is defined (#7885) --- esphome/components/deep_sleep/deep_sleep_esp32.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/deep_sleep/deep_sleep_esp32.cpp b/esphome/components/deep_sleep/deep_sleep_esp32.cpp index d54046bc11..d647140865 100644 --- a/esphome/components/deep_sleep/deep_sleep_esp32.cpp +++ b/esphome/components/deep_sleep/deep_sleep_esp32.cpp @@ -52,11 +52,11 @@ void DeepSleepComponent::dump_config_platform_() { bool DeepSleepComponent::prepare_to_sleep_() { if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr && - !this->sleep_duration_.has_value() && this->wakeup_pin_->digital_read()) { + this->wakeup_pin_->digital_read()) { // Defer deep sleep until inactive if (!this->next_enter_deep_sleep_) { this->status_set_warning(); - ESP_LOGW(TAG, "Waiting for pin_ to switch state to enter deep sleep..."); + ESP_LOGW(TAG, "Waiting wakeup pin state change to enter deep sleep..."); } this->next_enter_deep_sleep_ = true; return false; From beb8ab50e27f5df45e15af1cf3691309a5eb2e92 Mon Sep 17 00:00:00 2001 From: guillempages Date: Thu, 28 Nov 2024 04:55:20 +0100 Subject: [PATCH 0730/1052] [online_image]Don't access decoder if not initialized (#7882) --- esphome/components/online_image/png_image.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/online_image/png_image.cpp b/esphome/components/online_image/png_image.cpp index c8e215a91d..4c4c22f9b7 100644 --- a/esphome/components/online_image/png_image.cpp +++ b/esphome/components/online_image/png_image.cpp @@ -49,6 +49,10 @@ void PngDecoder::prepare(uint32_t download_size) { } int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { + if (!this->pngle_) { + ESP_LOGE(TAG, "PNG decoder engine not initialized!"); + return -1; + } if (size < 256 && size < this->download_size_ - this->decoded_bytes_) { ESP_LOGD(TAG, "Waiting for data"); return 0; From 5486b40aab86f6505c62b9077b1d1737548ec554 Mon Sep 17 00:00:00 2001 From: FreeBear-nc <67865163+FreeBear-nc@users.noreply.github.com> Date: Thu, 28 Nov 2024 03:56:37 +0000 Subject: [PATCH 0731/1052] Add IRAM_ATTR to all functions used during interrupts on esp8266 chips. (#7840) --- esphome/components/opentherm/opentherm.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index e40fc66b7d..62ab1d3860 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -220,7 +220,7 @@ void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) { this->bit_pos_++; } -ProtocolErrorType OpenTherm::verify_stop_bit_(uint8_t value) { +ProtocolErrorType IRAM_ATTR OpenTherm::verify_stop_bit_(uint8_t value) { if (value) { // stop bit detected return check_parity_(this->data_) ? ProtocolErrorType::NO_ERROR : ProtocolErrorType::PARITY_ERROR; } else { // no stop bit detected, error @@ -365,7 +365,7 @@ void IRAM_ATTR OpenTherm::stop_timer_() { #ifdef ESP8266 // 5 kHz timer_ -void OpenTherm::start_read_timer_() { +void IRAM_ATTR OpenTherm::start_read_timer_() { InterruptLock const lock; timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) @@ -373,14 +373,14 @@ void OpenTherm::start_read_timer_() { } // 2 kHz timer_ -void OpenTherm::start_write_timer_() { +void IRAM_ATTR OpenTherm::start_write_timer_() { InterruptLock const lock; timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) timer1_write(2500); // 2kHz } -void OpenTherm::stop_timer_() { +void IRAM_ATTR OpenTherm::stop_timer_() { InterruptLock const lock; timer1_disable(); timer1_detachInterrupt(); @@ -389,7 +389,7 @@ void OpenTherm::stop_timer_() { #endif // END ESP8266 // https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd -bool OpenTherm::check_parity_(uint32_t val) { +bool IRAM_ATTR OpenTherm::check_parity_(uint32_t val) { val ^= val >> 16; val ^= val >> 8; val ^= val >> 4; From 217a80a1789d235705c47862748ef2c0a8af5a1c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:57:11 +1300 Subject: [PATCH 0732/1052] [st7920] Remove unnecessary warning when drawing outside display bounds (#7868) --- esphome/components/st7920/st7920.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/st7920/st7920.cpp b/esphome/components/st7920/st7920.cpp index f336d24e24..171e7095dd 100644 --- a/esphome/components/st7920/st7920.cpp +++ b/esphome/components/st7920/st7920.cpp @@ -1,7 +1,7 @@ #include "st7920.h" -#include "esphome/core/log.h" -#include "esphome/core/application.h" #include "esphome/components/display/display_buffer.h" +#include "esphome/core/application.h" +#include "esphome/core/log.h" namespace esphome { namespace st7920 { @@ -118,7 +118,6 @@ size_t ST7920::get_buffer_length_() { void HOT ST7920::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { - ESP_LOGW(TAG, "Position out of area: %dx%d", x, y); return; } int width = this->get_width_internal() / 8u; From 30477c764d9353381ef6e8bf186bae703cffbc7f Mon Sep 17 00:00:00 2001 From: Krzysztof Zdulski Date: Fri, 29 Nov 2024 22:05:00 +0100 Subject: [PATCH 0733/1052] Fix recalc_timestamp_utc (#7894) --- esphome/core/time.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/time.cpp b/esphome/core/time.cpp index 31977d972b..66a0e1c0a7 100644 --- a/esphome/core/time.cpp +++ b/esphome/core/time.cpp @@ -169,7 +169,7 @@ void ESPTime::recalc_timestamp_utc(bool use_day_of_year) { } for (int i = 1970; i < this->year; i++) - res += (year % 4 == 0) ? 366 : 365; + res += (i % 4 == 0) ? 366 : 365; if (use_day_of_year) { res += this->day_of_year - 1; From 8f69d070612d9ed7e862fc31c340568dffabf9f7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 1 Dec 2024 10:08:52 -0600 Subject: [PATCH 0734/1052] [hx711] clang-tidy fixes for #7822 (#7900) --- esphome/components/hx711/hx711.cpp | 2 +- esphome/components/hx711/hx711.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/hx711/hx711.cpp b/esphome/components/hx711/hx711.cpp index 1a7169eed7..9643d0c411 100644 --- a/esphome/components/hx711/hx711.cpp +++ b/esphome/components/hx711/hx711.cpp @@ -53,7 +53,7 @@ bool HX711Sensor::read_sensor_(uint32_t *result) { } // Cycle clock pin for gain setting - for (uint8_t i = 0; i < this->gain_; i++) { + for (uint8_t i = 0; i < static_cast(this->gain_); i++) { this->sck_pin_->digital_write(true); delayMicroseconds(1); this->sck_pin_->digital_write(false); diff --git a/esphome/components/hx711/hx711.h b/esphome/components/hx711/hx711.h index 0cb6868ab5..a92bb9945d 100644 --- a/esphome/components/hx711/hx711.h +++ b/esphome/components/hx711/hx711.h @@ -9,7 +9,7 @@ namespace esphome { namespace hx711 { -enum HX711Gain { +enum HX711Gain : uint8_t { HX711_GAIN_128 = 1, HX711_GAIN_32 = 2, HX711_GAIN_64 = 3, From 83d6834e277e9e9159f12653a2f39bc1113f9104 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 2 Dec 2024 05:10:18 +1300 Subject: [PATCH 0735/1052] Cast port to int for ota pushing (#7888) --- esphome/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 86d529e1bf..dce041e5ac 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -363,7 +363,7 @@ def upload_program(config, args, host): from esphome import espota2 - remote_port = ota_conf[CONF_PORT] + remote_port = int(ota_conf[CONF_PORT]) password = ota_conf.get(CONF_PASSWORD, "") if ( From edd847ea403c4a0fba05d652b8e30e09fd3e6a85 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 1 Dec 2024 18:27:32 -0600 Subject: [PATCH 0736/1052] [modbus_controller] Clang fixes (#7899) --- .../modbus_controller/modbus_controller.cpp | 40 ++++++++--------- .../modbus_controller/modbus_controller.h | 44 +++++++++---------- .../number/modbus_number.cpp | 16 +++---- .../modbus_controller/number/modbus_number.h | 8 ++-- .../output/modbus_output.cpp | 13 +++--- .../modbus_controller/output/modbus_output.h | 10 ++--- .../select/modbus_select.cpp | 7 +-- .../modbus_controller/select/modbus_select.h | 8 ++-- .../switch/modbus_switch.cpp | 8 ++-- .../modbus_controller/switch/modbus_switch.h | 4 +- 10 files changed, 80 insertions(+), 78 deletions(-) diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index f8b72af817..641ba68223 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -152,11 +152,11 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t } SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const { - auto reg_it = find_if(begin(register_ranges_), end(register_ranges_), [=](RegisterRange const &r) { + auto reg_it = find_if(begin(this->register_ranges_), end(this->register_ranges_), [=](RegisterRange const &r) { return (r.start_address == start_address && r.register_type == register_type); }); - if (reg_it == register_ranges_.end()) { + if (reg_it == this->register_ranges_.end()) { ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address); } else { return reg_it->sensors; @@ -240,18 +240,18 @@ void ModbusController::update() { // walk through the sensors and determine the register ranges to read size_t ModbusController::create_register_ranges_() { - register_ranges_.clear(); - if (this->parent_->role == modbus::ModbusRole::CLIENT && sensorset_.empty()) { + this->register_ranges_.clear(); + if (this->parent_->role == modbus::ModbusRole::CLIENT && this->sensorset_.empty()) { ESP_LOGW(TAG, "No sensors registered"); return 0; } // iterator is sorted see SensorItemsComparator for details - auto ix = sensorset_.begin(); + auto ix = this->sensorset_.begin(); RegisterRange r = {}; uint8_t buffer_offset = 0; SensorItem *prev = nullptr; - while (ix != sensorset_.end()) { + while (ix != this->sensorset_.end()) { SensorItem *curr = *ix; ESP_LOGV(TAG, "Register: 0x%X %d %d %d offset=%u skip=%u addr=%p", curr->start_address, curr->register_count, @@ -278,12 +278,12 @@ size_t ModbusController::create_register_ranges_() { // this register can re-use the data from the previous register // remove this sensore because start_address is changed (sort-order) - ix = sensorset_.erase(ix); + ix = this->sensorset_.erase(ix); curr->start_address = r.start_address; curr->offset += prev->offset; - sensorset_.insert(curr); + this->sensorset_.insert(curr); // move iterator backwards because it will be incremented later ix--; @@ -293,14 +293,14 @@ size_t ModbusController::create_register_ranges_() { // this register can extend the current range // remove this sensore because start_address is changed (sort-order) - ix = sensorset_.erase(ix); + ix = this->sensorset_.erase(ix); curr->start_address = r.start_address; curr->offset += buffer_offset; buffer_offset += curr->get_register_size(); r.register_count += curr->register_count; - sensorset_.insert(curr); + this->sensorset_.insert(curr); // move iterator backwards because it will be incremented later ix--; @@ -327,7 +327,7 @@ size_t ModbusController::create_register_ranges_() { ix++; } else { ESP_LOGV(TAG, "Add range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates); - register_ranges_.push_back(r); + this->register_ranges_.push_back(r); r = {}; buffer_offset = 0; // do not increment the iterator here because the current sensor has to be re-evaluated @@ -339,10 +339,10 @@ size_t ModbusController::create_register_ranges_() { if (r.register_count > 0) { // Add the last range ESP_LOGV(TAG, "Add last range 0x%X %d skip:%d", r.start_address, r.register_count, r.skip_updates); - register_ranges_.push_back(r); + this->register_ranges_.push_back(r); } - return register_ranges_.size(); + return this->register_ranges_.size(); } void ModbusController::dump_config() { @@ -352,18 +352,18 @@ void ModbusController::dump_config() { ESP_LOGCONFIG(TAG, " Offline Skip Updates: %d", this->offline_skip_updates_); #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE ESP_LOGCONFIG(TAG, "sensormap"); - for (auto &it : sensorset_) { + for (auto &it : this->sensorset_) { ESP_LOGCONFIG(TAG, " Sensor type=%zu start=0x%X offset=0x%X count=%d size=%d", static_cast(it->register_type), it->start_address, it->offset, it->register_count, it->get_register_size()); } ESP_LOGCONFIG(TAG, "ranges"); - for (auto &it : register_ranges_) { + for (auto &it : this->register_ranges_) { ESP_LOGCONFIG(TAG, " Range type=%zu start=0x%X count=%d skip_updates=%d", static_cast(it.register_type), it.start_address, it.register_count, it.skip_updates); } ESP_LOGCONFIG(TAG, "server registers"); - for (auto &r : server_registers_) { + for (auto &r : this->server_registers_) { ESP_LOGCONFIG(TAG, " Address=0x%02X value_type=%zu register_count=%u", r->address, static_cast(r->value_type), r->register_count); } @@ -372,11 +372,11 @@ void ModbusController::dump_config() { void ModbusController::loop() { // Incoming data to process? - if (!incoming_queue_.empty()) { - auto &message = incoming_queue_.front(); + if (!this->incoming_queue_.empty()) { + auto &message = this->incoming_queue_.front(); if (message != nullptr) process_modbus_data_(message.get()); - incoming_queue_.pop(); + this->incoming_queue_.pop(); } else { // all messages processed send pending commands @@ -391,7 +391,7 @@ void ModbusController::on_write_register_response(ModbusRegisterType register_ty void ModbusController::dump_sensors_() { ESP_LOGV(TAG, "sensors"); - for (auto &it : sensorset_) { + for (auto &it : this->sensorset_) { ESP_LOGV(TAG, " Sensor start=0x%X count=%d size=%d offset=%d", it->start_address, it->register_count, it->get_register_size(), it->offset); } diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h index 2a0b936bf5..dfd52e44bc 100644 --- a/esphome/components/modbus_controller/modbus_controller.h +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -240,14 +240,14 @@ class SensorItem { } // Override register size for modbus devices not using 1 register for one dword void set_register_size(uint8_t register_size) { response_bytes = register_size; } - ModbusRegisterType register_type; - SensorValueType sensor_value_type; - uint16_t start_address; - uint32_t bitmask; - uint8_t offset; - uint8_t register_count; + ModbusRegisterType register_type{ModbusRegisterType::CUSTOM}; + SensorValueType sensor_value_type{SensorValueType::RAW}; + uint16_t start_address{0}; + uint32_t bitmask{0}; + uint8_t offset{0}; + uint8_t register_count{0}; uint8_t response_bytes{0}; - uint16_t skip_updates; + uint16_t skip_updates{0}; std::vector custom_data{}; bool force_new_range{false}; }; @@ -261,9 +261,9 @@ class ServerRegister { this->register_count = register_count; this->read_lambda = std::move(read_lambda); } - uint16_t address; - SensorValueType value_type; - uint8_t register_count; + uint16_t address{0}; + SensorValueType value_type{SensorValueType::RAW}; + uint8_t register_count{0}; std::function read_lambda; }; @@ -312,11 +312,11 @@ struct RegisterRange { class ModbusCommandItem { public: static const size_t MAX_PAYLOAD_BYTES = 240; - ModbusController *modbusdevice; - uint16_t register_address; - uint16_t register_count; - ModbusFunctionCode function_code; - ModbusRegisterType register_type; + ModbusController *modbusdevice{nullptr}; + uint16_t register_address{0}; + uint16_t register_count{0}; + ModbusFunctionCode function_code{ModbusFunctionCode::CUSTOM}; + ModbusRegisterType register_type{ModbusRegisterType::CUSTOM}; std::function &data)> on_data_func; std::vector payload = {}; @@ -493,23 +493,23 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice { /// Collection of all sensors for this component SensorSet sensorset_; /// Collection of all server registers for this component - std::vector server_registers_; + std::vector server_registers_{}; /// Continuous range of modbus registers - std::vector register_ranges_; + std::vector register_ranges_{}; /// Hold the pending requests to be sent std::list> command_queue_; /// modbus response data waiting to get processed std::queue> incoming_queue_; /// if duplicate commands can be sent - bool allow_duplicate_commands_; + bool allow_duplicate_commands_{false}; /// when was the last send operation - uint32_t last_command_timestamp_; + uint32_t last_command_timestamp_{0}; /// min time in ms between sending modbus commands - uint16_t command_throttle_; + uint16_t command_throttle_{0}; /// if module didn't respond the last command - bool module_offline_; + bool module_offline_{false}; /// how many updates to skip if module is offline - uint16_t offline_skip_updates_; + uint16_t offline_skip_updates_{0}; /// How many times we will retry a command if we get no response uint8_t max_cmd_retries_{4}; /// Command sent callback diff --git a/esphome/components/modbus_controller/number/modbus_number.cpp b/esphome/components/modbus_controller/number/modbus_number.cpp index 001cfb5787..ea8467d5a3 100644 --- a/esphome/components/modbus_controller/number/modbus_number.cpp +++ b/esphome/components/modbus_controller/number/modbus_number.cpp @@ -8,7 +8,7 @@ namespace modbus_controller { static const char *const TAG = "modbus.number"; void ModbusNumber::parse_and_publish(const std::vector &data) { - float result = payload_to_float(data, *this) / multiply_by_; + float result = payload_to_float(data, *this) / this->multiply_by_; // Is there a lambda registered // call it with the pre converted value and the raw data array @@ -43,7 +43,7 @@ void ModbusNumber::control(float value) { return; } } else { - write_value = multiply_by_ * write_value; + write_value = this->multiply_by_ * write_value; } if (!data.empty()) { @@ -63,21 +63,21 @@ void ModbusNumber::control(float value) { // Create and send the write command if (this->register_count == 1 && !this->use_write_multiple_) { // since offset is in bytes and a register is 16 bits we get the start by adding offset/2 - write_cmd = - ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, data[0]); + write_cmd = ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset / 2, + data[0]); } else { - write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset / 2, - this->register_count, data); + write_cmd = ModbusCommandItem::create_write_multiple_command( + this->parent_, this->start_address + this->offset / 2, this->register_count, data); } // publish new value write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { // gets called when the write command is ack'd from the device - parent_->on_write_register_response(write_cmd.register_type, start_address, data); + this->parent_->on_write_register_response(write_cmd.register_type, start_address, data); this->publish_state(value); }; } - parent_->queue_command(write_cmd); + this->parent_->queue_command(write_cmd); this->publish_state(value); } void ModbusNumber::dump_config() { LOG_NUMBER(TAG, "Modbus Number", this); } diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h index 544d161cbc..8f77b2e014 100644 --- a/esphome/components/modbus_controller/number/modbus_number.h +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -29,7 +29,7 @@ class ModbusNumber : public number::Number, public Component, public SensorItem void parse_and_publish(const std::vector &data) override; float get_setup_priority() const override { return setup_priority::HARDWARE; } void set_parent(ModbusController *parent) { this->parent_ = parent; } - void set_write_multiply(float factor) { multiply_by_ = factor; } + void set_write_multiply(float factor) { this->multiply_by_ = factor; } using transform_func_t = std::function(ModbusNumber *, float, const std::vector &)>; using write_transform_func_t = std::function(ModbusNumber *, float, std::vector &)>; @@ -39,9 +39,9 @@ class ModbusNumber : public number::Number, public Component, public SensorItem protected: void control(float value) override; - optional transform_func_; - optional write_transform_func_; - ModbusController *parent_; + optional transform_func_{nullopt}; + optional write_transform_func_{nullopt}; + ModbusController *parent_{nullptr}; float multiply_by_{1.0}; bool use_write_multiple_{false}; }; diff --git a/esphome/components/modbus_controller/output/modbus_output.cpp b/esphome/components/modbus_controller/output/modbus_output.cpp index 79cd2d49c2..f0f6e64f10 100644 --- a/esphome/components/modbus_controller/output/modbus_output.cpp +++ b/esphome/components/modbus_controller/output/modbus_output.cpp @@ -27,7 +27,7 @@ void ModbusFloatOutput::write_state(float value) { return; } } else { - value = multiply_by_ * value; + value = this->multiply_by_ * value; } // lambda didn't set payload if (data.empty()) { @@ -40,12 +40,13 @@ void ModbusFloatOutput::write_state(float value) { // Create and send the write command ModbusCommandItem write_cmd; if (this->register_count == 1 && !this->use_write_multiple_) { - write_cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset, data[0]); + write_cmd = + ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset, data[0]); } else { - write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset, + write_cmd = ModbusCommandItem::create_write_multiple_command(this->parent_, this->start_address + this->offset, this->register_count, data); } - parent_->queue_command(write_cmd); + this->parent_->queue_command(write_cmd); } void ModbusFloatOutput::dump_config() { @@ -90,9 +91,9 @@ void ModbusBinaryOutput::write_state(bool state) { // offset for coil and discrete inputs is the coil/register number not bytes if (this->use_write_multiple_) { std::vector states{state}; - cmd = ModbusCommandItem::create_write_multiple_coils(parent_, this->start_address + this->offset, states); + cmd = ModbusCommandItem::create_write_multiple_coils(this->parent_, this->start_address + this->offset, states); } else { - cmd = ModbusCommandItem::create_write_single_coil(parent_, this->start_address + this->offset, state); + cmd = ModbusCommandItem::create_write_single_coil(this->parent_, this->start_address + this->offset, state); } } this->parent_->queue_command(cmd); diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h index f424671cd1..bceb97affb 100644 --- a/esphome/components/modbus_controller/output/modbus_output.h +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -25,7 +25,7 @@ class ModbusFloatOutput : public output::FloatOutput, public Component, public S void dump_config() override; void set_parent(ModbusController *parent) { this->parent_ = parent; } - void set_write_multiply(float factor) { multiply_by_ = factor; } + void set_write_multiply(float factor) { this->multiply_by_ = factor; } // Do nothing void parse_and_publish(const std::vector &data) override{}; @@ -37,9 +37,9 @@ class ModbusFloatOutput : public output::FloatOutput, public Component, public S void write_state(float value) override; optional write_transform_func_{nullopt}; - ModbusController *parent_; + ModbusController *parent_{nullptr}; float multiply_by_{1.0}; - bool use_write_multiple_; + bool use_write_multiple_{false}; }; class ModbusBinaryOutput : public output::BinaryOutput, public Component, public SensorItem { @@ -68,8 +68,8 @@ class ModbusBinaryOutput : public output::BinaryOutput, public Component, public void write_state(bool state) override; optional write_transform_func_{nullopt}; - ModbusController *parent_; - bool use_write_multiple_; + ModbusController *parent_{nullptr}; + bool use_write_multiple_{false}; }; } // namespace modbus_controller diff --git a/esphome/components/modbus_controller/select/modbus_select.cpp b/esphome/components/modbus_controller/select/modbus_select.cpp index 33cef39a18..56b8c783ed 100644 --- a/esphome/components/modbus_controller/select/modbus_select.cpp +++ b/esphome/components/modbus_controller/select/modbus_select.cpp @@ -74,12 +74,13 @@ void ModbusSelect::control(const std::string &value) { const uint16_t write_address = this->start_address + this->offset / 2; ModbusCommandItem write_cmd; if ((this->register_count == 1) && (!this->use_write_multiple_)) { - write_cmd = ModbusCommandItem::create_write_single_command(parent_, write_address, data[0]); + write_cmd = ModbusCommandItem::create_write_single_command(this->parent_, write_address, data[0]); } else { - write_cmd = ModbusCommandItem::create_write_multiple_command(parent_, write_address, this->register_count, data); + write_cmd = + ModbusCommandItem::create_write_multiple_command(this->parent_, write_address, this->register_count, data); } - parent_->queue_command(write_cmd); + this->parent_->queue_command(write_cmd); if (this->optimistic_) this->publish_state(value); diff --git a/esphome/components/modbus_controller/select/modbus_select.h b/esphome/components/modbus_controller/select/modbus_select.h index 1c046b11d0..55fb2107dd 100644 --- a/esphome/components/modbus_controller/select/modbus_select.h +++ b/esphome/components/modbus_controller/select/modbus_select.h @@ -42,12 +42,12 @@ class ModbusSelect : public Component, public select::Select, public SensorItem void control(const std::string &value) override; protected: - std::vector mapping_; - ModbusController *parent_; + std::vector mapping_{}; + ModbusController *parent_{nullptr}; bool use_write_multiple_{false}; bool optimistic_{false}; - optional transform_func_; - optional write_transform_func_; + optional transform_func_{nullopt}; + optional write_transform_func_{nullopt}; }; } // namespace modbus_controller diff --git a/esphome/components/modbus_controller/switch/modbus_switch.cpp b/esphome/components/modbus_controller/switch/modbus_switch.cpp index 3a679fbeb8..ec29eca7f8 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.cpp +++ b/esphome/components/modbus_controller/switch/modbus_switch.cpp @@ -80,18 +80,18 @@ void ModbusSwitch::write_state(bool state) { // offset for coil and discrete inputs is the coil/register number not bytes if (this->use_write_multiple_) { std::vector states{state}; - cmd = ModbusCommandItem::create_write_multiple_coils(parent_, this->start_address + this->offset, states); + cmd = ModbusCommandItem::create_write_multiple_coils(this->parent_, this->start_address + this->offset, states); } else { - cmd = ModbusCommandItem::create_write_single_coil(parent_, this->start_address + this->offset, state); + cmd = ModbusCommandItem::create_write_single_coil(this->parent_, this->start_address + this->offset, state); } } else { // since offset is in bytes and a register is 16 bits we get the start by adding offset/2 if (this->use_write_multiple_) { std::vector bool_states(1, state ? (0xFFFF & this->bitmask) : 0); - cmd = ModbusCommandItem::create_write_multiple_command(parent_, this->start_address + this->offset / 2, 1, + cmd = ModbusCommandItem::create_write_multiple_command(this->parent_, this->start_address + this->offset / 2, 1, bool_states); } else { - cmd = ModbusCommandItem::create_write_single_command(parent_, this->start_address + this->offset / 2, + cmd = ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset / 2, state ? 0xFFFF & this->bitmask : 0u); } } diff --git a/esphome/components/modbus_controller/switch/modbus_switch.h b/esphome/components/modbus_controller/switch/modbus_switch.h index bfe46f3ac8..fe4b7c1ad5 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.h +++ b/esphome/components/modbus_controller/switch/modbus_switch.h @@ -40,8 +40,8 @@ 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: - ModbusController *parent_; - bool use_write_multiple_; + ModbusController *parent_{nullptr}; + bool use_write_multiple_{false}; optional publish_transform_func_{nullopt}; optional write_transform_func_{nullopt}; }; From fb96e3588d1f771f7430beec570da2d09a83e2f2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 2 Dec 2024 08:16:58 +0000 Subject: [PATCH 0737/1052] Add H-Bridge switch component (#7421) Co-authored-by: Keith Burzinski --- CODEOWNERS | 1 + esphome/components/hbridge/switch/__init__.py | 44 +++++++++ .../hbridge/switch/hbridge_switch.cpp | 95 +++++++++++++++++++ .../hbridge/switch/hbridge_switch.h | 50 ++++++++++ tests/components/hbridge/common.yaml | 39 ++++++++ tests/components/hbridge/test.esp32-ard.yaml | 46 +++------ .../components/hbridge/test.esp32-c3-ard.yaml | 45 +++------ .../components/hbridge/test.esp32-c3-idf.yaml | 44 +++------ tests/components/hbridge/test.esp32-idf.yaml | 45 +++------ .../components/hbridge/test.esp8266-ard.yaml | 45 +++------ tests/components/hbridge/test.rp2040-ard.yaml | 45 +++------ 11 files changed, 313 insertions(+), 186 deletions(-) create mode 100644 esphome/components/hbridge/switch/__init__.py create mode 100644 esphome/components/hbridge/switch/hbridge_switch.cpp create mode 100644 esphome/components/hbridge/switch/hbridge_switch.h create mode 100644 tests/components/hbridge/common.yaml diff --git a/CODEOWNERS b/CODEOWNERS index fb6d11d1fb..74c205b302 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -179,6 +179,7 @@ esphome/components/haier/text_sensor/* @paveldn esphome/components/havells_solar/* @sourabhjaiswal esphome/components/hbridge/fan/* @WeekendWarrior esphome/components/hbridge/light/* @DotNetDann +esphome/components/hbridge/switch/* @dwmw2 esphome/components/he60r/* @clydebarrow esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal diff --git a/esphome/components/hbridge/switch/__init__.py b/esphome/components/hbridge/switch/__init__.py new file mode 100644 index 0000000000..e26bd6b1d8 --- /dev/null +++ b/esphome/components/hbridge/switch/__init__.py @@ -0,0 +1,44 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components import switch +import esphome.config_validation as cv +from esphome.const import CONF_OPTIMISTIC, CONF_PULSE_LENGTH, CONF_WAIT_TIME + +from .. import hbridge_ns + +HBridgeSwitch = hbridge_ns.class_("HBridgeSwitch", switch.Switch, cg.Component) + +CODEOWNERS = ["@dwmw2"] + +CONF_OFF_PIN = "off_pin" +CONF_ON_PIN = "on_pin" + +CONFIG_SCHEMA = ( + switch.switch_schema(HBridgeSwitch) + .extend( + { + cv.Required(CONF_ON_PIN): pins.gpio_output_pin_schema, + cv.Required(CONF_OFF_PIN): pins.gpio_output_pin_schema, + cv.Optional( + CONF_PULSE_LENGTH, default="100ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_WAIT_TIME): cv.positive_time_period_milliseconds, + cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = await switch.new_switch(config) + await cg.register_component(var, config) + + on_pin = await cg.gpio_pin_expression(config[CONF_ON_PIN]) + cg.add(var.set_on_pin(on_pin)) + off_pin = await cg.gpio_pin_expression(config[CONF_OFF_PIN]) + cg.add(var.set_off_pin(off_pin)) + cg.add(var.set_pulse_length(config[CONF_PULSE_LENGTH])) + cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) + if wait_time := config.get(CONF_WAIT_TIME): + cg.add(var.set_wait_time(wait_time)) diff --git a/esphome/components/hbridge/switch/hbridge_switch.cpp b/esphome/components/hbridge/switch/hbridge_switch.cpp new file mode 100644 index 0000000000..12d1c01bca --- /dev/null +++ b/esphome/components/hbridge/switch/hbridge_switch.cpp @@ -0,0 +1,95 @@ +#include "hbridge_switch.h" +#include "esphome/core/log.h" + +#include + +namespace esphome { +namespace hbridge { + +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()); + + optional initial_state = this->get_initial_state_with_restore_mode().value_or(false); + + // Like GPIOSwitch does, set the pin state both before and after pin setup() + this->on_pin_->digital_write(false); + this->on_pin_->setup(); + this->on_pin_->digital_write(false); + + this->off_pin_->digital_write(false); + this->off_pin_->setup(); + this->off_pin_->digital_write(false); + + if (initial_state.has_value()) + this->write_state(initial_state); +} + +void HBridgeSwitch::dump_config() { + LOG_SWITCH("", "H-Bridge Switch", this); + LOG_PIN(" On Pin: ", this->on_pin_); + LOG_PIN(" Off Pin: ", this->off_pin_); + ESP_LOGCONFIG(TAG, " Pulse length: %" PRId32 " ms", this->pulse_length_); + if (this->wait_time_) + ESP_LOGCONFIG(TAG, " Wait time %" PRId32 " ms", this->wait_time_); +} + +void HBridgeSwitch::write_state(bool state) { + this->desired_state_ = state; + if (!this->timer_running_) + this->timer_fn_(); +} + +void HBridgeSwitch::timer_fn_() { + uint32_t next_timeout = 0; + + while ((uint8_t) this->desired_state_ != this->relay_state_) { + switch (this->relay_state_) { + case RELAY_STATE_ON: + case RELAY_STATE_OFF: + case RELAY_STATE_UNKNOWN: + if (this->desired_state_) { + this->on_pin_->digital_write(true); + this->relay_state_ = RELAY_STATE_SWITCHING_ON; + } else { + this->off_pin_->digital_write(true); + this->relay_state_ = RELAY_STATE_SWITCHING_OFF; + } + next_timeout = this->pulse_length_; + if (!this->optimistic_) + this->publish_state(this->desired_state_); + break; + + case RELAY_STATE_SWITCHING_ON: + this->on_pin_->digital_write(false); + this->relay_state_ = RELAY_STATE_ON; + if (this->optimistic_) + this->publish_state(true); + next_timeout = this->wait_time_; + break; + + case RELAY_STATE_SWITCHING_OFF: + this->off_pin_->digital_write(false); + this->relay_state_ = RELAY_STATE_OFF; + if (this->optimistic_) + this->publish_state(false); + next_timeout = this->wait_time_; + break; + } + + if (next_timeout) { + this->timer_running_ = true; + this->set_timeout(next_timeout, [this]() { this->timer_fn_(); }); + return; + } + + // In the case where ON/OFF state has been reached but we need to + // immediately change back again to reach desired_state_, we loop. + } + this->timer_running_ = false; +} + +} // namespace hbridge +} // namespace esphome diff --git a/esphome/components/hbridge/switch/hbridge_switch.h b/esphome/components/hbridge/switch/hbridge_switch.h new file mode 100644 index 0000000000..ce00c6baa2 --- /dev/null +++ b/esphome/components/hbridge/switch/hbridge_switch.h @@ -0,0 +1,50 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/switch/switch.h" + +#include + +namespace esphome { +namespace hbridge { + +enum RelayState : uint8_t { + RELAY_STATE_OFF = 0, + RELAY_STATE_ON = 1, + RELAY_STATE_SWITCHING_ON = 2, + RELAY_STATE_SWITCHING_OFF = 3, + RELAY_STATE_UNKNOWN = 4, +}; + +class HBridgeSwitch : public switch_::Switch, public Component { + public: + void set_on_pin(GPIOPin *pin) { this->on_pin_ = pin; } + void set_off_pin(GPIOPin *pin) { this->off_pin_ = pin; } + void set_pulse_length(uint32_t pulse_length) { this->pulse_length_ = pulse_length; } + void set_wait_time(uint32_t wait_time) { this->wait_time_ = wait_time; } + void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } + + // ========== INTERNAL METHODS ========== + // (In most use cases you won't need these) + float get_setup_priority() const override; + + void setup() override; + void dump_config() override; + + protected: + void write_state(bool state) override; + void timer_fn_(); + + bool timer_running_{false}; + bool desired_state_{false}; + RelayState relay_state_{RELAY_STATE_UNKNOWN}; + GPIOPin *on_pin_{nullptr}; + GPIOPin *off_pin_{nullptr}; + uint32_t pulse_length_{0}; + uint32_t wait_time_{0}; + bool optimistic_{false}; +}; + +} // namespace hbridge +} // namespace esphome diff --git a/tests/components/hbridge/common.yaml b/tests/components/hbridge/common.yaml new file mode 100644 index 0000000000..0504cdea03 --- /dev/null +++ b/tests/components/hbridge/common.yaml @@ -0,0 +1,39 @@ +output: + - platform: ${pwm_platform} + pin: ${output1_pin} + id: gpio_output1 + - platform: ${pwm_platform} + pin: ${output2_pin} + id: gpio_output2 + - platform: ${pwm_platform} + pin: ${output3_pin} + id: gpio_output3 + - platform: ${pwm_platform} + pin: ${output4_pin} + id: gpio_output4 + +light: + - platform: hbridge + name: Icicle Lights + pin_a: gpio_output3 + pin_b: gpio_output4 + +fan: + - platform: hbridge + id: fan_hbridge + speed_count: 4 + name: H-bridge Fan with Presets + pin_a: gpio_output1 + pin_b: gpio_output2 + preset_modes: + - Preset 1 + - Preset 2 + on_preset_set: + then: + - logger.log: Preset mode was changed! + +switch: + - platform: hbridge + id: switch_hbridge + on_pin: ${hbridge_on_pin} + off_pin: ${hbridge_off_pin} diff --git a/tests/components/hbridge/test.esp32-ard.yaml b/tests/components/hbridge/test.esp32-ard.yaml index 6a80aaaf3b..e50d537749 100644 --- a/tests/components/hbridge/test.esp32-ard.yaml +++ b/tests/components/hbridge/test.esp32-ard.yaml @@ -1,33 +1,17 @@ -output: - - platform: ledc - pin: 14 - id: gpio_output1 - - platform: ledc - pin: 15 - id: gpio_output2 - - platform: ledc - pin: 12 - id: gpio_output3 - - platform: ledc - pin: 13 - id: gpio_output4 +substitutions: + pwm_platform: ledc + output1_pin: "14" + output2_pin: "15" + output3_pin: "12" + output4_pin: "13" + hbridge_on_pin: "4" + hbridge_off_pin: "5" -light: - - platform: hbridge - name: Icicle Lights - pin_a: gpio_output3 - pin_b: gpio_output4 +packages: + common: !include common.yaml -fan: - - platform: hbridge - id: fan_hbridge - speed_count: 4 - name: H-bridge Fan with Presets - pin_a: gpio_output1 - pin_b: gpio_output2 - preset_modes: - - Preset 1 - - Preset 2 - on_preset_set: - then: - - logger.log: Preset mode was changed! +switch: + - id: !extend switch_hbridge + pulse_length: 60ms + wait_time: 10ms + optimistic: false diff --git a/tests/components/hbridge/test.esp32-c3-ard.yaml b/tests/components/hbridge/test.esp32-c3-ard.yaml index 70cfd6ab6f..b9e8738442 100644 --- a/tests/components/hbridge/test.esp32-c3-ard.yaml +++ b/tests/components/hbridge/test.esp32-c3-ard.yaml @@ -1,33 +1,16 @@ -output: - - platform: ledc - pin: 4 - id: gpio_output1 - - platform: ledc - pin: 5 - id: gpio_output2 - - platform: ledc - pin: 6 - id: gpio_output3 - - platform: ledc - pin: 7 - id: gpio_output4 +substitutions: + pwm_platform: "ledc" + output1_pin: "4" + output2_pin: "5" + output3_pin: "6" + output4_pin: "7" + hbridge_on_pin: "2" + hbridge_off_pin: "3" -light: - - platform: hbridge - name: Icicle Lights - pin_a: gpio_output3 - pin_b: gpio_output4 +packages: + common: !include common.yaml -fan: - - platform: hbridge - id: fan_hbridge - speed_count: 4 - name: H-bridge Fan with Presets - pin_a: gpio_output1 - pin_b: gpio_output2 - preset_modes: - - Preset 1 - - Preset 2 - on_preset_set: - then: - - logger.log: Preset mode was changed! +switch: + - id: !extend switch_hbridge + wait_time: 10ms + optimistic: true diff --git a/tests/components/hbridge/test.esp32-c3-idf.yaml b/tests/components/hbridge/test.esp32-c3-idf.yaml index 70cfd6ab6f..c73f08b6de 100644 --- a/tests/components/hbridge/test.esp32-c3-idf.yaml +++ b/tests/components/hbridge/test.esp32-c3-idf.yaml @@ -1,33 +1,15 @@ -output: - - platform: ledc - pin: 4 - id: gpio_output1 - - platform: ledc - pin: 5 - id: gpio_output2 - - platform: ledc - pin: 6 - id: gpio_output3 - - platform: ledc - pin: 7 - id: gpio_output4 +substitutions: + pwm_platform: "ledc" + output1_pin: "4" + output2_pin: "5" + output3_pin: "6" + output4_pin: "7" + hbridge_on_pin: "2" + hbridge_off_pin: "3" -light: - - platform: hbridge - name: Icicle Lights - pin_a: gpio_output3 - pin_b: gpio_output4 +packages: + common: !include common.yaml -fan: - - platform: hbridge - id: fan_hbridge - speed_count: 4 - name: H-bridge Fan with Presets - pin_a: gpio_output1 - pin_b: gpio_output2 - preset_modes: - - Preset 1 - - Preset 2 - on_preset_set: - then: - - logger.log: Preset mode was changed! +switch: + - id: !extend switch_hbridge + pulse_length: 60ms diff --git a/tests/components/hbridge/test.esp32-idf.yaml b/tests/components/hbridge/test.esp32-idf.yaml index 6a80aaaf3b..dbbfa738c7 100644 --- a/tests/components/hbridge/test.esp32-idf.yaml +++ b/tests/components/hbridge/test.esp32-idf.yaml @@ -1,33 +1,16 @@ -output: - - platform: ledc - pin: 14 - id: gpio_output1 - - platform: ledc - pin: 15 - id: gpio_output2 - - platform: ledc - pin: 12 - id: gpio_output3 - - platform: ledc - pin: 13 - id: gpio_output4 +substitutions: + pwm_platform: "ledc" + output1_pin: "14" + output2_pin: "15" + output3_pin: "12" + output4_pin: "13" + hbridge_on_pin: "4" + hbridge_off_pin: "5" -light: - - platform: hbridge - name: Icicle Lights - pin_a: gpio_output3 - pin_b: gpio_output4 +packages: + common: !include common.yaml -fan: - - platform: hbridge - id: fan_hbridge - speed_count: 4 - name: H-bridge Fan with Presets - pin_a: gpio_output1 - pin_b: gpio_output2 - preset_modes: - - Preset 1 - - Preset 2 - on_preset_set: - then: - - logger.log: Preset mode was changed! +switch: + - id: !extend switch_hbridge + pulse_length: 60ms + wait_time: 10ms diff --git a/tests/components/hbridge/test.esp8266-ard.yaml b/tests/components/hbridge/test.esp8266-ard.yaml index 4f8915879d..f560da5d38 100644 --- a/tests/components/hbridge/test.esp8266-ard.yaml +++ b/tests/components/hbridge/test.esp8266-ard.yaml @@ -1,33 +1,16 @@ -output: - - platform: esp8266_pwm - pin: 4 - id: gpio_output1 - - platform: esp8266_pwm - pin: 5 - id: gpio_output2 - - platform: esp8266_pwm - pin: 12 - id: gpio_output3 - - platform: esp8266_pwm - pin: 13 - id: gpio_output4 +substitutions: + pwm_platform: "esp8266_pwm" + output1_pin: "4" + output2_pin: "5" + output3_pin: "12" + output4_pin: "13" + hbridge_on_pin: "14" + hbridge_off_pin: "15" -light: - - platform: hbridge - name: Icicle Lights - pin_a: gpio_output3 - pin_b: gpio_output4 +packages: + common: !include common.yaml -fan: - - platform: hbridge - id: fan_hbridge - speed_count: 4 - name: H-bridge Fan with Presets - pin_a: gpio_output1 - pin_b: gpio_output2 - preset_modes: - - Preset 1 - - Preset 2 - on_preset_set: - then: - - logger.log: Preset mode was changed! +switch: + - id: !extend switch_hbridge + pulse_length: 60ms + wait_time: 10ms diff --git a/tests/components/hbridge/test.rp2040-ard.yaml b/tests/components/hbridge/test.rp2040-ard.yaml index e21b55091d..aa6e290cab 100644 --- a/tests/components/hbridge/test.rp2040-ard.yaml +++ b/tests/components/hbridge/test.rp2040-ard.yaml @@ -1,33 +1,16 @@ -output: - - platform: rp2040_pwm - pin: 4 - id: gpio_output1 - - platform: rp2040_pwm - pin: 5 - id: gpio_output2 - - platform: rp2040_pwm - pin: 6 - id: gpio_output3 - - platform: rp2040_pwm - pin: 7 - id: gpio_output4 +substitutions: + pwm_platform: "rp2040_pwm" + output1_pin: "4" + output2_pin: "5" + output3_pin: "6" + output4_pin: "7" + hbridge_on_pin: "2" + hbridge_off_pin: "3" -light: - - platform: hbridge - name: Icicle Lights - pin_a: gpio_output3 - pin_b: gpio_output4 +packages: + common: !include common.yaml -fan: - - platform: hbridge - id: fan_hbridge - speed_count: 4 - name: H-bridge Fan with Presets - pin_a: gpio_output1 - pin_b: gpio_output2 - preset_modes: - - Preset 1 - - Preset 2 - on_preset_set: - then: - - logger.log: Preset mode was changed! +switch: + - id: !extend switch_hbridge + wait_time: 10ms + optimistic: true From b79a3d672782fee909f929da8cb0a516ee52132d Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 2 Dec 2024 11:42:44 -0600 Subject: [PATCH 0738/1052] [CI] Bump GHA runners to ``ubuntu-24.04`` (#7905) --- .github/workflows/ci.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5af3ec9e9..82e823adde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ concurrency: jobs: common: name: Create common environment - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 outputs: cache-key: ${{ steps.cache-key.outputs.key }} steps: @@ -62,7 +62,7 @@ jobs: black: name: Check black - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common steps: @@ -83,7 +83,7 @@ jobs: flake8: name: Check flake8 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common steps: @@ -104,7 +104,7 @@ jobs: pylint: name: Check pylint - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common steps: @@ -125,7 +125,7 @@ jobs: pyupgrade: name: Check pyupgrade - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common steps: @@ -146,7 +146,7 @@ jobs: ci-custom: name: Run script/ci-custom - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common steps: @@ -225,7 +225,7 @@ jobs: clang-format: name: Check clang-format - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common steps: @@ -251,7 +251,7 @@ jobs: clang-tidy: name: ${{ matrix.name }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common - black @@ -345,7 +345,7 @@ jobs: if: always() list-components: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common if: github.event_name == 'pull_request' @@ -387,7 +387,7 @@ jobs: test-build-components: name: Component test ${{ matrix.file }} - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common - list-components @@ -421,7 +421,7 @@ jobs: test-build-components-splitter: name: Split components for testing into 20 groups maximum - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common - list-components @@ -439,7 +439,7 @@ jobs: test-build-components-split: name: Test split components - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common - list-components @@ -483,7 +483,7 @@ jobs: ci-status: name: CI Status - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: - common - black From e08a9cc3a33ab431f6eeef869bdc49c46f17dcf1 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:27:51 +1100 Subject: [PATCH 0739/1052] [font et. al.] Remove explicit check for pillow installed. (#7891) --- esphome/components/animation/__init__.py | 3 +-- esphome/components/font/__init__.py | 24 ++---------------------- esphome/components/ili9xxx/display.py | 3 +-- esphome/components/image/__init__.py | 3 +-- 4 files changed, 5 insertions(+), 28 deletions(-) diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index 5a308855de..21a82649f0 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -2,7 +2,6 @@ import logging from esphome import automation, core import esphome.codegen as cg -from esphome.components import font import esphome.components.image as espImage from esphome.components.image import ( CONF_USE_TRANSPARENCY, @@ -131,7 +130,7 @@ ANIMATION_SCHEMA = cv.Schema( ) ) -CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA) +CONFIG_SCHEMA = ANIMATION_SCHEMA NEXT_FRAME_SCHEMA = automation.maybe_simple_id( { diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 6fd2d7c310..1b4fe4bff0 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -1,4 +1,3 @@ -from collections.abc import Iterable import functools import hashlib import logging @@ -8,7 +7,6 @@ import re import freetype import glyphsets -from packaging import version import requests from esphome import core, external_files @@ -88,7 +86,7 @@ def flatten(lists) -> list: return list(chain.from_iterable(lists)) -def check_missing_glyphs(file, codepoints: Iterable, warning: bool = False): +def check_missing_glyphs(file, codepoints, warning: bool = False): """ Check that the given font file actually contains the requested glyphs :param file: A Truetype font file @@ -177,24 +175,6 @@ def validate_glyphs(config): return config -def validate_pillow_installed(value): - try: - import PIL - except ImportError as err: - raise cv.Invalid( - "Please install the pillow python package to use this feature. " - '(pip install "pillow==10.4.0")' - ) from err - - if version.parse(PIL.__version__) != version.parse("10.4.0"): - raise cv.Invalid( - "Please update your pillow installation to 10.4.0. " - '(pip install "pillow==10.4.0")' - ) - - return value - - FONT_EXTENSIONS = (".ttf", ".woff", ".otf") @@ -421,7 +401,7 @@ FONT_SCHEMA = cv.Schema( }, ) -CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA, validate_glyphs) +CONFIG_SCHEMA = cv.All(FONT_SCHEMA, validate_glyphs) # PIL doesn't provide a consistent interface for both TrueType and bitmap diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 739ad07843..3c9dd2dab9 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -1,6 +1,6 @@ from esphome import core, pins import esphome.codegen as cg -from esphome.components import display, font, spi +from esphome.components import display, spi from esphome.components.display import validate_rotation import esphome.config_validation as cv from esphome.const import ( @@ -147,7 +147,6 @@ def _validate(config): CONFIG_SCHEMA = cv.All( - font.validate_pillow_installed, display.FULL_DISPLAY_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(ILI9XXXDisplay), diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index 8742540067..4669a3418a 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -10,7 +10,6 @@ import puremagic from esphome import core, external_files import esphome.codegen as cg -from esphome.components import font import esphome.config_validation as cv from esphome.const import ( CONF_DITHER, @@ -233,7 +232,7 @@ IMAGE_SCHEMA = cv.Schema( ) ) -CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA) +CONFIG_SCHEMA = IMAGE_SCHEMA def load_svg_image(file: bytes, resize: tuple[int, int]): From 9c8976be1328b4c5c1d019aa835f53f129262324 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 2 Dec 2024 16:29:45 -0600 Subject: [PATCH 0740/1052] [CI] Update clang-tidy to 18.1.3 (#7822) --- .clang-tidy | 29 +++++++++++++++++++++++++++-- .github/workflows/ci.yml | 5 ----- docker/Dockerfile | 8 ++++++-- requirements_dev.txt | 2 +- script/clang-tidy | 8 +++----- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 994416b2f1..8dba033e2d 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -7,28 +7,38 @@ Checks: >- -boost-*, -bugprone-easily-swappable-parameters, -bugprone-implicit-widening-of-multiplication-result, + -bugprone-multi-level-implicit-pointer-conversion, -bugprone-narrowing-conversions, -bugprone-signed-char-misuse, + -bugprone-switch-missing-default-case, -cert-dcl50-cpp, -cert-err33-c, -cert-err58-cpp, -cert-oop57-cpp, -cert-str34-c, + -clang-analyzer-optin.core.EnumCastOutOfRange, -clang-analyzer-optin.cplusplus.UninitializedObject, -clang-analyzer-osx.*, -clang-diagnostic-delete-abstract-non-virtual-dtor, -clang-diagnostic-delete-non-abstract-non-virtual-dtor, -clang-diagnostic-ignored-optimization-argument, + -clang-diagnostic-missing-field-initializers, -clang-diagnostic-shadow-field, -clang-diagnostic-unused-const-variable, -clang-diagnostic-unused-parameter, + -clang-diagnostic-vla-cxx-extension, -concurrency-*, -cppcoreguidelines-avoid-c-arrays, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-init-variables, + -cppcoreguidelines-macro-to-enum, -cppcoreguidelines-macro-usage, + -cppcoreguidelines-missing-std-forward, -cppcoreguidelines-narrowing-conversions, -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-owning-memory, -cppcoreguidelines-prefer-member-initializer, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, @@ -40,7 +50,9 @@ Checks: >- -cppcoreguidelines-pro-type-static-cast-downcast, -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-rvalue-reference-param-not-moved, -cppcoreguidelines-special-member-functions, + -cppcoreguidelines-use-default-member-init, -cppcoreguidelines-virtual-class-destructor, -fuchsia-multiple-inheritance, -fuchsia-overloaded-operator, @@ -60,21 +72,32 @@ Checks: >- -llvm-include-order, -llvm-qualified-auto, -llvmlibc-*, - -misc-non-private-member-variables-in-classes, + -misc-const-correctness, + -misc-include-cleaner, -misc-no-recursion, + -misc-non-private-member-variables-in-classes, -misc-unused-parameters, + -misc-use-anonymous-namespace, -modernize-avoid-bind, -modernize-avoid-c-arrays, -modernize-concat-nested-namespaces, + -modernize-macro-to-enum, -modernize-return-braced-init-list, + -modernize-type-traits, -modernize-use-auto, + -modernize-use-constraints, -modernize-use-default-member-init, -modernize-use-equals-default, -modernize-use-nodiscard, -modernize-use-nullptr, + -modernize-use-nodiscard, + -modernize-use-nullptr, -modernize-use-trailing-return-type, -mpi-*, -objc-*, + -performance-enum-size, + -readability-avoid-nested-conditional-operator, + -readability-container-contains, -readability-container-data-pointer, -readability-convert-member-functions-to-static, -readability-else-after-return, @@ -84,11 +107,13 @@ Checks: >- -readability-magic-numbers, -readability-make-member-function-const, -readability-named-parameter, + -readability-redundant-casting, + -readability-redundant-inline-specifier, + -readability-redundant-member-init, -readability-redundant-string-init, -readability-uppercase-literal-suffix, -readability-use-anyofallof, WarningsAsErrors: '*' -AnalyzeTemporaryDtors: false FormatStyle: google CheckOptions: - key: google-readability-function-size.StatementThreshold diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82e823adde..e4d3934c59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -314,11 +314,6 @@ jobs: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} - - name: Install clang-tidy - run: | - sudo apt-get update - sudo apt-get install clang-tidy-14 - - name: Register problem matchers run: | echo "::add-matcher::.github/workflows/matchers/gcc.json" diff --git a/docker/Dockerfile b/docker/Dockerfile index c2902a9dd1..c53856d0e8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -209,11 +209,12 @@ ENV \ PLATFORMIO_CORE_DIR=/esphome/.temp/platformio RUN \ - apt-get update \ + curl -L https://apt.llvm.org/llvm-snapshot.gpg.key -o /etc/apt/trusted.gpg.d/apt.llvm.org.asc \ + && echo "deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-18 main" > /etc/apt/sources.list.d/llvm.sources.list \ + && apt-get update \ # Use pinned versions so that we get updates with build caching && apt-get install -y --no-install-recommends \ clang-format-13=1:13.0.1-11+b2 \ - clang-tidy-14=1:14.0.6-12 \ patch=2.7.6-7 \ software-properties-common=0.99.30-4.1~deb12u1 \ nano=7.2-1+deb12u1 \ @@ -227,6 +228,9 @@ RUN \ COPY requirements_test.txt / RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ + else \ + # move this up into RUN above after armv7 is retired + apt-get install -y --no-install-recommends clang-tidy-18=1:18.1.8~++20240731024826+3b5b5c1ec4a3-1~exp1~20240731144843.145 ; \ fi; \ pip3 install \ --break-system-packages --no-cache-dir -r /requirements_test.txt diff --git a/requirements_dev.txt b/requirements_dev.txt index eb749a861d..2ef8330215 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,4 +1,4 @@ # Useful stuff when working in a development environment clang-format==13.0.1 # also change in .pre-commit-config.yaml and Dockerfile when updating -clang-tidy==14.0.6 # When updating clang-tidy, also update Dockerfile +clang-tidy==18.1.3 # When updating clang-tidy, also update Dockerfile yamllint==1.35.1 # also change in .pre-commit-config.yaml when updating diff --git a/script/clang-tidy b/script/clang-tidy index 319fab70a2..5c19f81043 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -63,8 +63,6 @@ def clang_options(idedata): "-Ddeprecated(x)=", # allow to condition code on the presence of clang-tidy "-DCLANG_TIDY", - # (esp-idf) Disable this header because they use asm with registers clang-tidy doesn't know - "-D__XTENSA_API_H__", # (esp-idf) Fix __once_callable in some libstdc++ headers "-D_GLIBCXX_HAVE_TLS", ] @@ -238,7 +236,7 @@ def main(): failed_files = [] try: - executable = get_binary("clang-tidy", 14) + executable = get_binary("clang-tidy", 18) task_queue = queue.Queue(args.jobs) lock = threading.Lock() for _ in range(args.jobs): @@ -283,12 +281,12 @@ def main(): print("Applying fixes ...") try: try: - subprocess.call(["clang-apply-replacements-14", tmpdir]) + subprocess.call(["clang-apply-replacements-18", tmpdir]) except FileNotFoundError: subprocess.call(["clang-apply-replacements", tmpdir]) except FileNotFoundError: print( - "Error please install clang-apply-replacements-14 or clang-apply-replacements.\n", + "Error please install clang-apply-replacements-18 or clang-apply-replacements.\n", file=sys.stderr, ) except: From 584dbf2668f888c774be7ad6ce4172d827965e9b Mon Sep 17 00:00:00 2001 From: kbullet Date: Tue, 3 Dec 2024 06:50:05 +0700 Subject: [PATCH 0741/1052] MQTT sensors handling of publishing NaN values (#7768) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mqtt/__init__.py | 4 ++++ esphome/components/mqtt/mqtt_client.cpp | 4 ++++ esphome/components/mqtt/mqtt_client.h | 6 ++++++ esphome/components/mqtt/mqtt_sensor.cpp | 2 ++ esphome/const.py | 1 + tests/components/mqtt/common.yaml | 1 + 6 files changed, 18 insertions(+) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 86d163e61d..2b0d941220 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -49,6 +49,7 @@ from esphome.const import ( CONF_USE_ABBREVIATIONS, CONF_USERNAME, CONF_WILL_MESSAGE, + CONF_PUBLISH_NAN_AS_NONE, PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, @@ -296,6 +297,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_QOS, default=0): cv.mqtt_qos, } ), + cv.Optional(CONF_PUBLISH_NAN_AS_NONE, default=False): cv.boolean, } ), validate_config, @@ -449,6 +451,8 @@ async def to_code(config): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) + cg.add(var.set_publish_nan_as_none(config[CONF_PUBLISH_NAN_AS_NONE])) + MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema( { diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 106192c0e3..c7ace505a8 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -608,6 +608,10 @@ void MQTTClientComponent::set_log_message_template(MQTTMessage &&message) { this const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; } void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix) { this->topic_prefix_ = topic_prefix; } const std::string &MQTTClientComponent::get_topic_prefix() const { return this->topic_prefix_; } +void MQTTClientComponent::set_publish_nan_as_none(bool publish_nan_as_none) { + this->publish_nan_as_none_ = publish_nan_as_none; +} +bool MQTTClientComponent::is_publish_nan_as_none() const { return this->publish_nan_as_none_; } void MQTTClientComponent::disable_birth_message() { this->birth_message_.topic = ""; this->recalculate_availability_(); diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 7ae3a6c5e8..34eac29464 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -263,6 +263,10 @@ class MQTTClientComponent : public Component { void set_on_connect(mqtt_on_connect_callback_t &&callback); void set_on_disconnect(mqtt_on_disconnect_callback_t &&callback); + // Publish None state instead of NaN for Home Assistant + void set_publish_nan_as_none(bool publish_nan_as_none); + bool is_publish_nan_as_none() const; + protected: void send_device_info_(); @@ -328,6 +332,8 @@ class MQTTClientComponent : public Component { uint32_t connect_begin_; uint32_t last_connected_{0}; optional disconnect_reason_{}; + + bool publish_nan_as_none_{false}; }; extern MQTTClientComponent *global_mqtt_client; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index fff75a3c00..2cbc291ccf 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -69,6 +69,8 @@ bool MQTTSensorComponent::send_initial_state() { } } bool MQTTSensorComponent::publish_state(float value) { + if (mqtt::global_mqtt_client->is_publish_nan_as_none() && std::isnan(value)) + return this->publish(this->get_state_topic_(), "None"); int8_t accuracy = this->sensor_->get_accuracy_decimals(); return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy)); } diff --git a/esphome/const.py b/esphome/const.py index d2df83aa43..3d3bfcc244 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -692,6 +692,7 @@ CONF_PRIORITY = "priority" CONF_PROJECT = "project" CONF_PROTOCOL = "protocol" CONF_PUBLISH_INITIAL_STATE = "publish_initial_state" +CONF_PUBLISH_NAN_AS_NONE = "publish_nan_as_none" CONF_PULL_MODE = "pull_mode" CONF_PULLDOWN = "pulldown" CONF_PULLUP = "pullup" diff --git a/tests/components/mqtt/common.yaml b/tests/components/mqtt/common.yaml index d22fe9579f..a4bdf58809 100644 --- a/tests/components/mqtt/common.yaml +++ b/tests/components/mqtt/common.yaml @@ -60,6 +60,7 @@ mqtt: - mqtt.publish: topic: some/topic payload: Good-bye + publish_nan_as_none: false binary_sensor: - platform: template From dc5942a59b521781279ff568057adf1dc01f11c8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:38:44 +1300 Subject: [PATCH 0742/1052] [ble] Allow setting shorter name for ble advertisements (#7867) Co-authored-by: Keith Burzinski --- esphome/components/esp32_ble/__init__.py | 24 +++++++++++++++++-- esphome/components/esp32_ble/ble.cpp | 18 ++++++++++---- esphome/components/esp32_ble/ble.h | 2 ++ .../components/esp32_ble/ble_advertising.cpp | 2 +- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 75cf9d707d..08e30c9247 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -2,8 +2,10 @@ from esphome import automation import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant import esphome.config_validation as cv -from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID +from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ESPHOME, CONF_ID, CONF_NAME from esphome.core import CORE +from esphome.core.config import CONF_NAME_ADD_MAC_SUFFIX +import esphome.final_validate as fv DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz", "@Rapsssito"] @@ -50,6 +52,7 @@ TX_POWER_LEVELS = { CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(ESP32BLE), + cv.Optional(CONF_NAME): cv.All(cv.string, cv.Length(max=20)), cv.Optional(CONF_IO_CAPABILITY, default="none"): cv.enum( IO_CAPABILITY, lower=True ), @@ -67,7 +70,22 @@ def validate_variant(_): raise cv.Invalid(f"{variant} does not support Bluetooth") -FINAL_VALIDATE_SCHEMA = validate_variant +def final_validation(config): + validate_variant(config) + if (name := config.get(CONF_NAME)) is not None: + full_config = fv.full_config.get() + max_length = 20 + if full_config[CONF_ESPHOME][CONF_NAME_ADD_MAC_SUFFIX]: + max_length -= 7 # "-AABBCC" is appended when add mac suffix option is used + if len(name) > max_length: + raise cv.Invalid( + f"Name '{name}' is too long, maximum length is {max_length} characters" + ) + + return config + + +FINAL_VALIDATE_SCHEMA = final_validation async def to_code(config): @@ -75,6 +93,8 @@ async def to_code(config): cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) cg.add(var.set_io_capability(config[CONF_IO_CAPABILITY])) cg.add(var.set_advertising_cycle_time(config[CONF_ADVERTISING_CYCLE_TIME])) + if (name := config.get(CONF_NAME)) is not None: + cg.add(var.set_name(name)) await cg.register_component(var, config) if CORE.using_esp_idf: diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index 5d08b6e973..d7dcf93f86 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -188,12 +188,20 @@ bool ESP32BLE::ble_setup_() { } } - std::string name = App.get_name(); - if (name.length() > 20) { + std::string name; + if (this->name_.has_value()) { + name = this->name_.value(); if (App.is_name_add_mac_suffix_enabled()) { - name.erase(name.begin() + 13, name.end() - 7); // Remove characters between 13 and the mac address - } else { - name = name.substr(0, 20); + name += "-" + get_mac_address().substr(6); + } + } else { + name = App.get_name(); + if (name.length() > 20) { + if (App.is_name_add_mac_suffix_enabled()) { + name.erase(name.begin() + 13, name.end() - 7); // Remove characters between 13 and the mac address + } else { + name = name.substr(0, 20); + } } } diff --git a/esphome/components/esp32_ble/ble.h b/esphome/components/esp32_ble/ble.h index 7c55583852..ed7575f128 100644 --- a/esphome/components/esp32_ble/ble.h +++ b/esphome/components/esp32_ble/ble.h @@ -90,6 +90,7 @@ class ESP32BLE : public Component { void loop() override; void dump_config() override; float get_setup_priority() const override; + void set_name(const std::string &name) { this->name_ = name; } void advertising_start(); void advertising_set_service_data(const std::vector &data); @@ -131,6 +132,7 @@ class ESP32BLE : public Component { esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; uint32_t advertising_cycle_time_; bool enable_on_boot_; + optional name_; }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index 1d340c76d9..92b7c60368 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -83,7 +83,7 @@ esp_err_t BLEAdvertising::services_advertisement_() { esp_err_t err; this->advertising_data_.set_scan_rsp = false; - this->advertising_data_.include_name = !this->scan_response_; + this->advertising_data_.include_name = true; this->advertising_data_.include_txpower = !this->scan_response_; err = esp_ble_gap_config_adv_data(&this->advertising_data_); if (err != ESP_OK) { From c95887a14a61c8a5b9cab909ee9f9493eeef8cc3 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:50:11 +1100 Subject: [PATCH 0743/1052] [lvgl] Bugfixes (#7896) --- esphome/components/lvgl/defines.py | 2 +- esphome/components/lvgl/lvgl_esphome.h | 3 +++ esphome/components/lvgl/widgets/line.py | 6 ++++++ tests/components/lvgl/lvgl-package.yaml | 4 ++-- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 81984637bd..5371f110a6 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -38,7 +38,7 @@ def literal(arg): def call_lambda(lamb: LambdaExpression): expr = lamb.content.strip() if expr.startswith("return") and expr.endswith(";"): - return expr[7:][:-1] + return expr[6:][:-1].strip() return f"{lamb}()" diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 921b7c109f..56413ad77e 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -56,6 +56,9 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) { lv_img_set_src(obj, image->get_lv_img_dsc()); } +inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) { + lv_disp_set_bg_image(disp, image->get_lv_img_dsc()); +} #endif // USE_LVGL_IMAGE #ifdef USE_LVGL_ANIMIMG inline void lv_animimg_set_src(lv_obj_t *img, std::vector images) { diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 548dfa8452..0156fb1780 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -35,6 +35,11 @@ LINE_SCHEMA = { cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t), } +LINE_MODIFY_SCHEMA = { + cv.Optional(CONF_POINTS): cv_point_list, + cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t), +} + class LineType(WidgetType): def __init__(self): @@ -43,6 +48,7 @@ class LineType(WidgetType): LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, + modify_schema=LINE_MODIFY_SCHEMA, ) async def to_code(self, w: Widget, config): diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 81b18c4ff8..6611209756 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -337,7 +337,7 @@ lvgl: id: button_button width: 20% height: 10% - transform_angle: !lambda return 180*100; + transform_angle: !lambda return(180*100); arc_width: !lambda return 4; border_width: !lambda return 6; shadow_ofs_x: !lambda return 6; @@ -581,7 +581,7 @@ lvgl: - 180, 60 - 240, 10 on_click: - - lvgl.widget.update: + - lvgl.line.update: id: lv_line_id line_color: 0xFFFF - lvgl.page.next: From 00ddb0a427b2cae4a0bda8dfc26066e6857d8e6d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:50:56 +1100 Subject: [PATCH 0744/1052] [font] Restore correct default glyphs for bitmap fonts (#7907) --- esphome/components/font/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 1b4fe4bff0..c397ba8306 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -373,7 +373,9 @@ def font_file_schema(value): # Default if no glyphs or glyphsets are provided DEFAULT_GLYPHSET = "GF_Latin_Kernel" # default for bitmap fonts -DEFAULT_GLYPHS = ' !"%()+=,-.:/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz' +DEFAULT_GLYPHS = ( + ' !"%()+=,-.:/?0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' +) CONF_RAW_GLYPH_ID = "raw_glyph_id" From a37ff2dbd97d17c5b9b522c2a4515fd8311b7003 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 4 Dec 2024 07:48:50 +1100 Subject: [PATCH 0745/1052] [lvgl] Fix msgbox content (#7912) --- esphome/components/lvgl/widgets/msgbox.py | 3 ++- tests/components/lvgl/lvgl-package.yaml | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index be0f2100d7..c3393940b6 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -29,7 +29,7 @@ from ..lvcode import ( ) from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema from ..types import LV_EVENT, char_ptr, lv_obj_t -from . import Widget, set_obj_properties +from . import Widget, add_widgets, set_obj_properties from .button import button_spec from .buttonmatrix import ( BUTTONMATRIX_BUTTON_SCHEMA, @@ -119,6 +119,7 @@ async def msgbox_to_code(top_layer, conf): button_style = {CONF_ITEMS: button_style} await set_obj_properties(buttonmatrix_widget, button_style) await set_obj_properties(msgbox_widget, conf) + await add_widgets(msgbox_widget, conf) async with LambdaContext(EVENT_ARG, where=messagebox_id) as close_action: outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN") if close_button: diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 6611209756..4b7e13db91 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -109,6 +109,10 @@ lvgl: close_button: true title: Messagebox bg_color: 0xffff + widgets: + - label: + text: Hello Msgbox + id: msgbox_label body: text: This is a sample messagebox bg_color: 0x808080 @@ -137,6 +141,9 @@ lvgl: - lvgl.widget.focus: mark - lvgl.widget.redraw: hello_label - lvgl.widget.redraw: + - lvgl.label.update: + id: msgbox_label + text: Unloaded on_all_events: logger.log: format: "Event %s" From d00ec7e544f26b3ec3844f531f4027b9947c8d14 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 3 Dec 2024 17:23:17 -0600 Subject: [PATCH 0746/1052] [helpers] clang-tidy fix for #7706 (#7909) --- esphome/core/helpers.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 103173095b..4e8caeae99 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -259,10 +259,15 @@ bool random_bytes(uint8_t *data, size_t len) { bool str_equals_case_insensitive(const std::string &a, const std::string &b) { return strcasecmp(a.c_str(), b.c_str()) == 0; } +#if ESP_IDF_VERSION_MAJOR >= 5 +bool str_startswith(const std::string &str, const std::string &start) { return str.starts_with(start); } +bool str_endswith(const std::string &str, const std::string &end) { return str.ends_with(end); } +#else bool str_startswith(const std::string &str, const std::string &start) { return str.rfind(start, 0) == 0; } bool str_endswith(const std::string &str, const std::string &end) { return str.rfind(end) == (str.size() - end.size()); } +#endif std::string str_truncate(const std::string &str, size_t length) { return str.length() > length ? str.substr(0, length) : str; } From dbed74b50d76cb7a6feb903908018aa3b96b9825 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 3 Dec 2024 17:26:27 -0600 Subject: [PATCH 0747/1052] [docker] Fix clang-tidy installation (#7910) --- docker/Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index c53856d0e8..cc05849271 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -220,7 +220,11 @@ RUN \ nano=7.2-1+deb12u1 \ build-essential=12.9 \ python3-dev=3.11.2-1+b1 \ - && rm -rf \ + && if [ "$TARGETARCH$TARGETVARIANT" != "armv7" ]; then \ + # move this up after armv7 is retired + apt-get install -y --no-install-recommends clang-tidy-18=1:18.1.8~++20240731024826+3b5b5c1ec4a3-1~exp1~20240731144843.145 ; \ + fi; \ + rm -rf \ /tmp/* \ /var/{cache,log}/* \ /var/lib/apt/lists/* @@ -228,9 +232,6 @@ RUN \ COPY requirements_test.txt / RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - else \ - # move this up into RUN above after armv7 is retired - apt-get install -y --no-install-recommends clang-tidy-18=1:18.1.8~++20240731024826+3b5b5c1ec4a3-1~exp1~20240731144843.145 ; \ fi; \ pip3 install \ --break-system-packages --no-cache-dir -r /requirements_test.txt From 79478cdb8a2731c23861da1c607e332117359179 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 4 Dec 2024 11:13:07 +1100 Subject: [PATCH 0748/1052] [sntp] Resolve warnings from ESP-IDF 5.x (#7913) --- esphome/components/sntp/sntp_component.cpp | 34 +++++++++------------- esphome/components/sntp/sntp_component.h | 15 ++++------ esphome/components/sntp/time.py | 11 +++---- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 4ded98d483..21add1451d 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -9,11 +9,6 @@ #include "lwip/apps/sntp.h" #endif -// Yes, the server names are leaked, but that's fine. -#ifdef CLANG_TIDY -#define strdup(x) (const_cast(x)) -#endif - namespace esphome { namespace sntp { @@ -26,30 +21,29 @@ void SNTPComponent::setup() { esp_sntp_stop(); } esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL); + size_t i = 0; + for (auto &server : this->servers_) { + esp_sntp_setservername(i++, server.c_str()); + } + esp_sntp_set_sync_interval(this->get_update_interval()); + esp_sntp_init(); #else sntp_stop(); sntp_setoperatingmode(SNTP_OPMODE_POLL); -#endif - sntp_setservername(0, strdup(this->server_1_.c_str())); - if (!this->server_2_.empty()) { - sntp_setservername(1, strdup(this->server_2_.c_str())); + size_t i = 0; + for (auto &server : this->servers_) { + sntp_setservername(i++, server.c_str()); } - if (!this->server_3_.empty()) { - sntp_setservername(2, strdup(this->server_3_.c_str())); - } -#ifdef USE_ESP_IDF - esp_sntp_set_sync_interval(this->get_update_interval()); -#endif - sntp_init(); +#endif } void SNTPComponent::dump_config() { ESP_LOGCONFIG(TAG, "SNTP Time:"); - ESP_LOGCONFIG(TAG, " Server 1: '%s'", this->server_1_.c_str()); - ESP_LOGCONFIG(TAG, " Server 2: '%s'", this->server_2_.c_str()); - ESP_LOGCONFIG(TAG, " Server 3: '%s'", this->server_3_.c_str()); - ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str()); + size_t i = 0; + for (auto &server : this->servers_) { + ESP_LOGCONFIG(TAG, " Server %zu: '%s'", i++, server.c_str()); + } } void SNTPComponent::update() { #if !defined(USE_ESP_IDF) diff --git a/esphome/components/sntp/sntp_component.h b/esphome/components/sntp/sntp_component.h index 987dd23a19..a4e8267383 100644 --- a/esphome/components/sntp/sntp_component.h +++ b/esphome/components/sntp/sntp_component.h @@ -14,23 +14,20 @@ namespace sntp { /// \see https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html class SNTPComponent : public time::RealTimeClock { public: + SNTPComponent(const std::vector &servers) : servers_(servers) {} + + // Note: set_servers() has been removed and replaced by a constructor - calling set_servers after setup would + // have had no effect anyway, and making the strings immutable avoids the need to strdup their contents. + void setup() override; void dump_config() override; - /// Change the servers used by SNTP for timekeeping - void set_servers(const std::string &server_1, const std::string &server_2, const std::string &server_3) { - this->server_1_ = server_1; - this->server_2_ = server_2; - this->server_3_ = server_3; - } float get_setup_priority() const override { return setup_priority::BEFORE_CONNECTION; } void update() override; void loop() override; protected: - std::string server_1_; - std::string server_2_; - std::string server_3_; + std::vector servers_; bool has_time_{false}; }; diff --git a/esphome/components/sntp/time.py b/esphome/components/sntp/time.py index 7cc82e3dff..6f883d5bed 100644 --- a/esphome/components/sntp/time.py +++ b/esphome/components/sntp/time.py @@ -1,16 +1,16 @@ +import esphome.codegen as cg from esphome.components import time as time_ import esphome.config_validation as cv -import esphome.codegen as cg -from esphome.core import CORE from esphome.const import ( CONF_ID, CONF_SERVERS, + PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, PLATFORM_RTL87XX, - PLATFORM_BK72XX, ) +from esphome.core import CORE DEPENDENCIES = ["network"] sntp_ns = cg.esphome_ns.namespace("sntp") @@ -40,11 +40,8 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - servers = config[CONF_SERVERS] - servers += [""] * (3 - len(servers)) - cg.add(var.set_servers(*servers)) + var = cg.new_Pvariable(config[CONF_ID], servers) await cg.register_component(var, config) await time_.register_time(var, config) From 016fac249649b475a918eb2c17f9daa0ad399485 Mon Sep 17 00:00:00 2001 From: mikosoft83 <63317931+mikosoft83@users.noreply.github.com> Date: Wed, 4 Dec 2024 01:18:00 +0100 Subject: [PATCH 0749/1052] Add strftime variant with background color (#7714) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/display/display.cpp | 14 +++++++++----- esphome/components/display/display.h | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 145a4f5278..1d996bd59b 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -662,20 +662,24 @@ void DisplayOnPageChangeTrigger::process(DisplayPage *from, DisplayPage *to) { if ((this->from_ == nullptr || this->from_ == from) && (this->to_ == nullptr || this->to_ == to)) this->trigger(from, to); } -void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) { +void Display::strftime(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format, + ESPTime time) { char buffer[64]; size_t ret = time.strftime(buffer, sizeof(buffer), format); if (ret > 0) - this->print(x, y, font, color, align, buffer); + this->print(x, y, font, color, align, buffer, background); +} +void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) { + this->strftime(x, y, font, color, COLOR_OFF, TextAlign::TOP_LEFT, format, time); } void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) { - this->strftime(x, y, font, color, TextAlign::TOP_LEFT, format, time); + this->strftime(x, y, font, color, COLOR_OFF, TextAlign::TOP_LEFT, format, time); } void Display::strftime(int x, int y, BaseFont *font, TextAlign align, const char *format, ESPTime time) { - this->strftime(x, y, font, COLOR_ON, align, format, time); + this->strftime(x, y, font, COLOR_ON, COLOR_OFF, align, format, time); } void Display::strftime(int x, int y, BaseFont *font, const char *format, ESPTime time) { - this->strftime(x, y, font, COLOR_ON, TextAlign::TOP_LEFT, format, time); + this->strftime(x, y, font, COLOR_ON, COLOR_OFF, TextAlign::TOP_LEFT, format, time); } void Display::start_clipping(Rect rect) { diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index 54e897cdec..43da08f4ac 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -437,6 +437,20 @@ class Display : public PollingComponent { */ void printf(int x, int y, BaseFont *font, const char *format, ...) __attribute__((format(printf, 5, 6))); + /** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`. + * + * @param x The x coordinate of the text alignment anchor point. + * @param y The y coordinate of the text alignment anchor point. + * @param font The font to draw the text with. + * @param color The color to draw the text with. + * @param background The background color to draw the text with. + * @param align The alignment of the text. + * @param format The format to use. + * @param ... The arguments to use for the text formatting. + */ + void strftime(int x, int y, BaseFont *font, Color color, Color background, TextAlign align, const char *format, + ESPTime time) __attribute__((format(strftime, 8, 0))); + /** Evaluate the strftime-format `format` and print the result with the anchor point at [x,y] with `font`. * * @param x The x coordinate of the text alignment anchor point. From 472402745d2f3821802bbdadf2ebc45d2ee6ccf5 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 4 Dec 2024 16:18:14 -0500 Subject: [PATCH 0750/1052] [i2s_audio] Bugfix: Follow configured bits per sample (#7916) --- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 123 +++++++++--------- .../i2s_audio/speaker/i2s_audio_speaker.h | 25 ++-- 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 53b3cc8dc0..194cc06a60 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -33,14 +33,15 @@ enum SpeakerEventGroupBits : uint32_t { STATE_RUNNING = (1 << 11), STATE_STOPPING = (1 << 12), STATE_STOPPED = (1 << 13), - ERR_INVALID_FORMAT = (1 << 14), - ERR_TASK_FAILED_TO_START = (1 << 15), - ERR_ESP_INVALID_STATE = (1 << 16), + ERR_TASK_FAILED_TO_START = (1 << 14), + ERR_ESP_INVALID_STATE = (1 << 15), + ERR_ESP_NOT_SUPPORTED = (1 << 16), ERR_ESP_INVALID_ARG = (1 << 17), ERR_ESP_INVALID_SIZE = (1 << 18), ERR_ESP_NO_MEM = (1 << 19), ERR_ESP_FAIL = (1 << 20), - ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE | ERR_ESP_NO_MEM | ERR_ESP_FAIL, + ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_NOT_SUPPORTED | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE | + ERR_ESP_NO_MEM | ERR_ESP_FAIL, ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits }; @@ -55,6 +56,8 @@ static esp_err_t err_bit_to_esp_err(uint32_t bit) { return ESP_ERR_INVALID_SIZE; case SpeakerEventGroupBits::ERR_ESP_NO_MEM: return ESP_ERR_NO_MEM; + case SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED: + return ESP_ERR_NOT_SUPPORTED; default: return ESP_FAIL; } @@ -135,19 +138,19 @@ void I2SAudioSpeaker::loop() { xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); } - if (event_group_bits & SpeakerEventGroupBits::ERR_INVALID_FORMAT) { + if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { + uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; + ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); + this->status_set_warning(); + } + + if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) { this->status_set_error("Failed to adjust I2S bus to match the incoming audio"); ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8, this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels, this->audio_stream_info_.bits_per_sample); } - - if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) { - uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS; - ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits))); - this->status_set_warning(); - } } void I2SAudioSpeaker::set_volume(float volume) { @@ -236,13 +239,14 @@ void I2SAudioSpeaker::speaker_task(void *params) { xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING); audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_; - const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample(); - const uint8_t number_of_channels = audio_stream_info.channels; - const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 * - bytes_per_sample * number_of_channels; - const size_t ring_buffer_size = - this_speaker->buffer_duration_ms_ * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels; + const uint32_t bytes_per_ms = + audio_stream_info.channels * audio_stream_info.get_bytes_per_sample() * audio_stream_info.sample_rate / 1000; + + const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * bytes_per_ms; + + // Ensure ring buffer is at least as large as the total size of the DMA buffers + const size_t ring_buffer_size = std::min(dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) { // Failed to allocate buffers @@ -250,14 +254,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { this_speaker->delete_task_(dma_buffers_size); } - if (this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_())) { - // Failed to start I2S driver - this_speaker->delete_task_(dma_buffers_size); - } - - if (!this_speaker->send_esp_err_to_event_group_(this_speaker->reconfigure_i2s_stream_info_(audio_stream_info))) { - // Successfully set the I2S stream info, ready to write audio data to the I2S port - + if (!this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_(audio_stream_info))) { xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING); bool stop_gracefully = false; @@ -275,6 +272,12 @@ void I2SAudioSpeaker::speaker_task(void *params) { stop_gracefully = true; } + if (this_speaker->audio_stream_info_ != audio_stream_info) { + // Audio stream info has changed, stop the speaker task so it will restart with the proper settings. + + break; + } + i2s_event_t i2s_event; while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) { if (i2s_event.type == I2S_EVENT_TX_Q_OVF) { @@ -316,17 +319,14 @@ void I2SAudioSpeaker::speaker_task(void *params) { } } } - } else { - // Couldn't configure the I2S port to be compatible with the incoming audio - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_INVALID_FORMAT); + + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING); + + i2s_driver_uninstall(this_speaker->parent_->get_port()); + + this_speaker->parent_->unlock(); } - i2s_zero_dma_buffer(this_speaker->parent_->get_port()); - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING); - - i2s_driver_uninstall(this_speaker->parent_->get_port()); - - this_speaker->parent_->unlock(); this_speaker->delete_task_(dma_buffers_size); } @@ -382,6 +382,9 @@ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) { case ESP_ERR_NO_MEM: xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); return true; + case ESP_ERR_NOT_SUPPORTED: + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED); + return true; default: xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL); return true; @@ -411,18 +414,40 @@ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t rin return ESP_OK; } -esp_err_t I2SAudioSpeaker::start_i2s_driver_() { +esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) { + if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.sample_rate)) { // NOLINT + // Can't reconfigure I2S bus, so the sample rate must match the configured value + return ESP_ERR_NOT_SUPPORTED; + } + + if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) { + // Currently can't handle the case when the incoming audio has more bits per sample than the configured value + return ESP_ERR_NOT_SUPPORTED; + } + if (!this->parent_->try_lock()) { return ESP_ERR_INVALID_STATE; } + i2s_channel_fmt_t channel = this->channel_; + + if (audio_stream_info.channels == 1) { + if (this->channel_ == I2S_CHANNEL_FMT_ONLY_LEFT) { + channel = I2S_CHANNEL_FMT_ONLY_LEFT; + } else { + channel = I2S_CHANNEL_FMT_ONLY_RIGHT; + } + } else if (audio_stream_info.channels == 2) { + channel = I2S_CHANNEL_FMT_RIGHT_LEFT; + } + int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000; i2s_driver_config_t config = { .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX), - .sample_rate = this->sample_rate_, + .sample_rate = audio_stream_info.sample_rate, .bits_per_sample = this->bits_per_sample_, - .channel_format = this->channel_, + .channel_format = channel, .communication_format = this->i2s_comm_fmt_, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = DMA_BUFFERS_COUNT, @@ -477,30 +502,6 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_() { return err; } -esp_err_t I2SAudioSpeaker::reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info) { - if (this->i2s_mode_ & I2S_MODE_MASTER) { - // ESP controls for the the I2S bus, so adjust the sample rate and bits per sample to match the incoming audio - this->sample_rate_ = audio_stream_info.sample_rate; - this->bits_per_sample_ = (i2s_bits_per_sample_t) audio_stream_info.bits_per_sample; - } else if (this->sample_rate_ != audio_stream_info.sample_rate) { - // Can't reconfigure I2S bus, so the sample rate must match the configured value - return ESP_ERR_INVALID_ARG; - } - - if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) { - // Currently can't handle the case when the incoming audio has more bits per sample than the configured value - return ESP_ERR_INVALID_ARG; - } - - if (audio_stream_info.channels == 1) { - return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_MONO); - } else if (audio_stream_info.channels == 2) { - return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_STEREO); - } - - return ESP_ERR_INVALID_ARG; -} - void I2SAudioSpeaker::delete_task_(size_t buffer_size) { this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 2b90f39399..d706deb0f4 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -91,24 +91,15 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp esp_err_t allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size); /// @brief Starts the ESP32 I2S driver. - /// Attempts to lock the I2S port, starts the I2S driver, and sets the data out pin. If it fails, it will unlock - /// the I2S port and uninstall the driver, if necessary. - /// @return ESP_ERR_INVALID_STATE if the I2S port is already locked. - /// ESP_ERR_INVALID_ARG if installing the driver or setting the data out pin fails due to a parameter error. + /// Attempts to lock the I2S port, starts the I2S driver using the passed in stream information, and sets the data out + /// pin. If it fails, it will unlock the I2S port and uninstall the driver, if necessary. + /// @param audio_stream_info Stream information for the I2S driver. + /// @return ESP_ERR_NOT_ALLOWED if the I2S port can't play the incoming audio stream. + /// ESP_ERR_INVALID_STATE if the I2S port is already locked. + /// ESP_ERR_INVALID_ARG if nstalling the driver or setting the data outpin fails due to a parameter error. /// ESP_ERR_NO_MEM if the driver fails to install due to a memory allocation error. - /// ESP_FAIL if setting the data out pin fails due to an IO error - /// ESP_OK if successful - esp_err_t start_i2s_driver_(); - - /// @brief Adjusts the I2S driver configuration to match the incoming audio stream. - /// Modifies I2S driver's sample rate, bits per sample, and number of channel settings. If the I2S is in secondary - /// mode, it only modifies the number of channels. - /// @param audio_stream_info Describes the incoming audio stream - /// @return ESP_ERR_INVALID_ARG if there is a parameter error, if there is more than 2 channels in the stream, or if - /// the audio settings are incompatible with the configuration. - /// ESP_ERR_NO_MEM if the driver fails to reconfigure due to a memory allocation error. - /// ESP_OK if successful. - esp_err_t reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info); + /// ESP_FAIL if setting the data out pin fails due to an IO error ESP_OK if successful + esp_err_t start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info); /// @brief Deletes the speaker's task. /// Deallocates the data_buffer_ and audio_ring_buffer_, if necessary, and deletes the task. Should only be called by From d429aa8bb8d9c8375996c0ab2335d66132cb3472 Mon Sep 17 00:00:00 2001 From: Pavlo Dudnytskyi Date: Wed, 4 Dec 2024 22:43:00 +0100 Subject: [PATCH 0751/1052] Haier AC quiet mode switch fix (#7902) --- esphome/components/haier/hon_climate.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index 85e9cf37b9..c95a87223d 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -35,7 +35,9 @@ void HonClimate::set_beeper_state(bool state) { if (state != this->settings_.beeper_state) { this->settings_.beeper_state = state; #ifdef USE_SWITCH - this->beeper_switch_->publish_state(state); + if (this->beeper_switch_ != nullptr) { + this->beeper_switch_->publish_state(state); + } #endif this->hon_rtc_.save(&this->settings_); } @@ -45,10 +47,17 @@ bool HonClimate::get_beeper_state() const { return this->settings_.beeper_state; void HonClimate::set_quiet_mode_state(bool state) { if (state != this->get_quiet_mode_state()) { - this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; + if ((this->mode != ClimateMode::CLIMATE_MODE_OFF) && (this->mode != ClimateMode::CLIMATE_MODE_FAN_ONLY)) { + this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; + this->force_send_control_ = true; + } else { + this->quiet_mode_state_ = state ? SwitchState::ON : SwitchState::OFF; + } this->settings_.quiet_mode_state = state; #ifdef USE_SWITCH - this->quiet_mode_switch_->publish_state(state); + if (this->quiet_mode_switch_ != nullptr) { + this->quiet_mode_switch_->publish_state(state); + } #endif this->hon_rtc_.save(&this->settings_); } @@ -509,7 +518,7 @@ void HonClimate::initialization() { } this->current_vertical_swing_ = this->settings_.last_vertiacal_swing; this->current_horizontal_swing_ = this->settings_.last_horizontal_swing; - this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; + this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::ON : SwitchState::OFF; } haier_protocol::HaierMessage HonClimate::get_control_message() { @@ -932,7 +941,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * if (this->mode == CLIMATE_MODE_OFF) { // AC just turned on from remote need to turn off display this->force_send_control_ = true; - } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) { + } else if ((((uint8_t) this->display_status_) & 0b10) == 0) { this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; } } @@ -1004,6 +1013,11 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * if (new_quiet_mode != this->get_quiet_mode_state()) { this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF; this->settings_.quiet_mode_state = new_quiet_mode; +#ifdef USE_SWITCH + if (this->quiet_mode_switch_ != nullptr) { + this->quiet_mode_switch_->publish_state(new_quiet_mode); + } +#endif // USE_SWITCH this->hon_rtc_.save(&this->settings_); } } From 4e839d42d050f3d6c83e045512fcf5bfab8d27ac Mon Sep 17 00:00:00 2001 From: Sebastian Muszynski Date: Wed, 4 Dec 2024 22:44:34 +0100 Subject: [PATCH 0752/1052] [CI] Update clang-tidy to 18.1.8 (#7915) --- requirements_dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_dev.txt b/requirements_dev.txt index 2ef8330215..1a98a15ab2 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,4 +1,4 @@ # Useful stuff when working in a development environment clang-format==13.0.1 # also change in .pre-commit-config.yaml and Dockerfile when updating -clang-tidy==18.1.3 # When updating clang-tidy, also update Dockerfile +clang-tidy==18.1.8 # When updating clang-tidy, also update Dockerfile yamllint==1.35.1 # also change in .pre-commit-config.yaml when updating From ece72c6b189e130bdc8a7dee7920abaa04cb61d9 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 4 Dec 2024 21:03:38 -0600 Subject: [PATCH 0753/1052] [i2s_audio] Speaker type fix (#7919) --- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 194cc06a60..d2a582c2cc 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -246,7 +246,8 @@ void I2SAudioSpeaker::speaker_task(void *params) { const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * bytes_per_ms; // Ensure ring buffer is at least as large as the total size of the DMA buffers - const size_t ring_buffer_size = std::min(dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); + const size_t ring_buffer_size = + std::min((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) { // Failed to allocate buffers From f3cc1e541a904d5ef44b431300e5457e4d782dd5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:44:59 +1300 Subject: [PATCH 0754/1052] [esp32_rmt_led_strip] Add ``COMPONENT_SCHEMA`` extending (#7918) --- esphome/components/esp32_rmt_led_strip/light.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 79f339e248..976f70e858 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -1,9 +1,9 @@ from dataclasses import dataclass -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins +import esphome.codegen as cg from esphome.components import esp32_rmt, light +import esphome.config_validation as cv from esphome.const import ( CONF_CHIPSET, CONF_IS_RGBW, @@ -103,7 +103,7 @@ CONFIG_SCHEMA = cv.All( default="0 us", ): cv.positive_time_period_nanoseconds, } - ), + ).extend(cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH), ) From acc8d24a325b34cd4af035bb2e26b26f1d6e4f83 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 5 Dec 2024 02:39:30 -0600 Subject: [PATCH 0755/1052] [esp32] Use pioarduino + IDF 5.1.5 as default for IDF builds (#7706) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- .clang-tidy | 1 + esphome/components/esp32/__init__.py | 106 ++++++++++++++++-- platformio.ini | 4 +- ...build_components_base.esp32-c3-idf-51.yaml | 19 ---- .../build_components_base.esp32-idf-51.yaml | 19 ---- ...build_components_base.esp32-s2-idf-51.yaml | 20 ---- ...build_components_base.esp32-s3-idf-51.yaml | 20 ---- 7 files changed, 97 insertions(+), 92 deletions(-) delete mode 100644 tests/test_build_components/build_components_base.esp32-c3-idf-51.yaml delete mode 100644 tests/test_build_components/build_components_base.esp32-idf-51.yaml delete mode 100644 tests/test_build_components/build_components_base.esp32-s2-idf-51.yaml delete mode 100644 tests/test_build_components/build_components_base.esp32-s3-idf-51.yaml diff --git a/.clang-tidy b/.clang-tidy index 8dba033e2d..5878028f48 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -21,6 +21,7 @@ Checks: >- -clang-analyzer-osx.*, -clang-diagnostic-delete-abstract-non-virtual-dtor, -clang-diagnostic-delete-non-abstract-non-virtual-dtor, + -clang-diagnostic-deprecated-declarations, -clang-diagnostic-ignored-optimization-argument, -clang-diagnostic-missing-field-initializers, -clang-diagnostic-shadow-field, diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 61fbb53e3a..580c3fc081 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -65,6 +65,8 @@ _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["preferences"] +CONF_RELEASE = "release" + def set_core_data(config): CORE.data[KEY_ESP32] = {} @@ -216,11 +218,17 @@ def _format_framework_arduino_version(ver: cv.Version) -> str: return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" -def _format_framework_espidf_version(ver: cv.Version) -> str: +def _format_framework_espidf_version( + ver: cv.Version, release: str, for_platformio: bool +) -> str: # format the given arduino (https://github.com/espressif/esp-idf/releases) version to # a PIO platformio/framework-espidf value # List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf - return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + if for_platformio: + return f"platformio/framework-espidf@~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0" + if release: + return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.zip" + return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.zip" # NOTE: Keep this in mind when updating the recommended version: @@ -241,11 +249,33 @@ 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(4, 4, 8) +RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 1, 5) # 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(5, 4, 0) +ESP_IDF_PLATFORM_VERSION = cv.Version(51, 3, 7) + +# List based on https://registry.platformio.org/tools/platformio/framework-espidf/versions +SUPPORTED_PLATFORMIO_ESP_IDF_5X = [ + cv.Version(5, 3, 1), + cv.Version(5, 3, 0), + cv.Version(5, 2, 2), + cv.Version(5, 2, 1), + cv.Version(5, 1, 2), + cv.Version(5, 1, 1), + cv.Version(5, 1, 0), + cv.Version(5, 0, 2), + cv.Version(5, 0, 1), + cv.Version(5, 0, 0), +] + +# pioarduino versions that don't require a release number +# List based on https://github.com/pioarduino/esp-idf/releases +SUPPORTED_PIOARDUINO_ESP_IDF_5X = [ + cv.Version(5, 3, 1), + cv.Version(5, 3, 0), + cv.Version(5, 1, 5), +] def _arduino_check_versions(value): @@ -286,8 +316,8 @@ def _arduino_check_versions(value): def _esp_idf_check_versions(value): value = value.copy() lookups = { - "dev": (cv.Version(5, 1, 2), "https://github.com/espressif/esp-idf.git"), - "latest": (cv.Version(5, 1, 2), None), + "dev": (cv.Version(5, 1, 5), "https://github.com/espressif/esp-idf.git"), + "latest": (cv.Version(5, 1, 5), None), "recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None), } @@ -305,13 +335,51 @@ def _esp_idf_check_versions(value): if version < cv.Version(4, 0, 0): raise cv.Invalid("Only ESP-IDF 4.0+ is supported.") - value[CONF_VERSION] = str(version) - value[CONF_SOURCE] = source or _format_framework_espidf_version(version) + # flag this for later *before* we set value[CONF_PLATFORM_VERSION] below + has_platform_ver = CONF_PLATFORM_VERSION in value value[CONF_PLATFORM_VERSION] = value.get( CONF_PLATFORM_VERSION, _parse_platform_version(str(ESP_IDF_PLATFORM_VERSION)) ) + if ( + (is_platformio := _platform_is_platformio(value[CONF_PLATFORM_VERSION])) + and version.major >= 5 + and version not in SUPPORTED_PLATFORMIO_ESP_IDF_5X + ): + raise cv.Invalid( + f"ESP-IDF {str(version)} not supported by platformio/espressif32" + ) + + if ( + version.major < 5 + or ( + version in SUPPORTED_PLATFORMIO_ESP_IDF_5X + and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X + ) + ) and not has_platform_ver: + raise cv.Invalid( + f"ESP-IDF {value[CONF_VERSION]} may be supported by platformio/espressif32; please specify '{CONF_PLATFORM_VERSION}'" + ) + + if ( + not is_platformio + and CONF_RELEASE not in value + and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X + ): + raise cv.Invalid( + f"ESP-IDF {value[CONF_VERSION]} is not available with pioarduino; you may need to specify '{CONF_RELEASE}'" + ) + + value[CONF_VERSION] = str(version) + value[CONF_SOURCE] = source or _format_framework_espidf_version( + version, value.get(CONF_RELEASE, None), is_platformio + ) + + if value[CONF_SOURCE].startswith("http"): + # prefix is necessary or platformio will complain with a cryptic error + value[CONF_SOURCE] = f"framework-espidf@{value[CONF_SOURCE]}" + if version != RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION: _LOGGER.warning( "The selected ESP-IDF framework version is not the recommended one. " @@ -323,6 +391,12 @@ def _esp_idf_check_versions(value): def _parse_platform_version(value): try: + ver = cv.Version.parse(cv.version_number(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}" # if platform version is a valid version constraint, prefix the default package cv.platformio_version_constraint(value) return f"platformio/espressif32@{value}" @@ -330,6 +404,14 @@ def _parse_platform_version(value): return value +def _platform_is_platformio(value): + try: + ver = cv.Version.parse(cv.version_number(value)) + return ver.major < 50 + except cv.Invalid: + return "platformio" in value + + def _detect_variant(value): board = value[CONF_BOARD] if board in BOARDS: @@ -412,6 +494,7 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All( cv.Schema( { cv.Optional(CONF_VERSION, default="recommended"): cv.string_strict, + cv.Optional(CONF_RELEASE): cv.string_strict, cv.Optional(CONF_SOURCE): cv.string_strict, cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, cv.Optional(CONF_SDKCONFIG_OPTIONS, default={}): { @@ -515,10 +598,9 @@ async def to_code(config): cg.add_build_flag("-DUSE_ESP_IDF") cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ESP_IDF") cg.add_build_flag("-Wno-nonnull-compare") - cg.add_platformio_option( - "platform_packages", - [f"platformio/framework-espidf@{conf[CONF_SOURCE]}"], - ) + + cg.add_platformio_option("platform_packages", [conf[CONF_SOURCE]]) + # platformio/toolchain-esp32ulp does not support linux_aarch64 yet and has not been updated for over 2 years # This is espressif's own published version which is more up to date. cg.add_platformio_option( diff --git a/platformio.ini b/platformio.ini index 04afc059af..b9b80e933f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -137,9 +137,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 = platformio/espressif32@5.4.0 +platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.06/platform-espressif32.zip platform_packages = - platformio/framework-espidf@~3.40408.0 + pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.1.5/esp-idf-v5.1.5.zip framework = espidf lib_deps = diff --git a/tests/test_build_components/build_components_base.esp32-c3-idf-51.yaml b/tests/test_build_components/build_components_base.esp32-c3-idf-51.yaml deleted file mode 100644 index eb5b23a4ec..0000000000 --- a/tests/test_build_components/build_components_base.esp32-c3-idf-51.yaml +++ /dev/null @@ -1,19 +0,0 @@ -esphome: - name: componenttestesp32c3idf51 - friendly_name: $component_name - -esp32: - board: lolin_c3_mini - framework: - type: esp-idf - version: 5.1.2 - platform_version: 6.5.0 - -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-idf-51.yaml b/tests/test_build_components/build_components_base.esp32-idf-51.yaml deleted file mode 100644 index b5e3dd6d83..0000000000 --- a/tests/test_build_components/build_components_base.esp32-idf-51.yaml +++ /dev/null @@ -1,19 +0,0 @@ -esphome: - name: componenttestesp32idf51 - friendly_name: $component_name - -esp32: - board: nodemcu-32s - framework: - type: esp-idf - version: 5.1.2 - platform_version: 6.5.0 - -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-s2-idf-51.yaml b/tests/test_build_components/build_components_base.esp32-s2-idf-51.yaml deleted file mode 100644 index 11b077509e..0000000000 --- a/tests/test_build_components/build_components_base.esp32-s2-idf-51.yaml +++ /dev/null @@ -1,20 +0,0 @@ -esphome: - name: componenttestesp32s2idf51 - friendly_name: $component_name - -esp32: - board: esp32-s2-saola-1 - variant: ESP32S2 - framework: - type: esp-idf - version: 5.1.2 - platform_version: 6.5.0 - -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-s3-idf-51.yaml b/tests/test_build_components/build_components_base.esp32-s3-idf-51.yaml deleted file mode 100644 index 4357b3581b..0000000000 --- a/tests/test_build_components/build_components_base.esp32-s3-idf-51.yaml +++ /dev/null @@ -1,20 +0,0 @@ -esphome: - name: componenttestesp32s3idf51 - friendly_name: $component_name - -esp32: - board: esp32s3box - variant: ESP32S3 - framework: - type: esp-idf - version: 5.1.2 - platform_version: 6.5.0 - -logger: - level: VERY_VERBOSE - -packages: - component_under_test: !include - file: $component_test_file - vars: - component_test_file: $component_test_file From 555bdac604147b1f918c6c11609589ac88f5c368 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:11:31 +1300 Subject: [PATCH 0756/1052] Bump actions/cache from 4.1.2 to 4.2.0 (#7926) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4d3934c59..6ce4159da0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.1.2 + uses: actions/cache@v4.2.0 with: path: venv # yamllint disable-line rule:line-length @@ -302,14 +302,14 @@ jobs: - name: Cache platformio if: github.ref == 'refs/heads/dev' - uses: actions/cache@v4.1.2 + uses: actions/cache@v4.2.0 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} - name: Cache platformio if: github.ref != 'refs/heads/dev' - uses: actions/cache/restore@v4.1.2 + uses: actions/cache/restore@v4.2.0 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} From d3a71a1d45fa3958fee3aac4e1afacb5408e726a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:11:46 +1300 Subject: [PATCH 0757/1052] Bump actions/cache from 4.1.2 to 4.2.0 in /.github/actions/restore-python (#7925) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index 06c54578f5..6b87cf0170 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -22,7 +22,7 @@ runs: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache/restore@v4.1.2 + uses: actions/cache/restore@v4.2.0 with: path: venv # yamllint disable-line rule:line-length From 4e3195b474e5ffeda909e9d8933f8611da758779 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:16:59 +1100 Subject: [PATCH 0758/1052] [esp32] Fix crash with empty `platformio_options:` value (#7920) --- esphome/components/esp32/__init__.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 580c3fc081..b0bde75451 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -437,24 +437,20 @@ def _detect_variant(value): def final_validate(config): - if CONF_PLATFORMIO_OPTIONS not in fv.full_config.get()[CONF_ESPHOME]: + if not ( + pio_options := fv.full_config.get()[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS) + ): + # Not specified or empty return config pio_flash_size_key = "board_upload.flash_size" pio_partitions_key = "board_build.partitions" - if ( - CONF_PARTITIONS in config - and pio_partitions_key - in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS] - ): + if CONF_PARTITIONS in config and pio_partitions_key in pio_options: raise cv.Invalid( f"Do not specify '{pio_partitions_key}' in '{CONF_PLATFORMIO_OPTIONS}' with '{CONF_PARTITIONS}' in esp32" ) - if ( - pio_flash_size_key - in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS] - ): + if pio_flash_size_key in pio_options: raise cv.Invalid( f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only" ) From bfd75d736c3693f7b055e890bf9a36d51543f646 Mon Sep 17 00:00:00 2001 From: alorente Date: Fri, 6 Dec 2024 01:21:14 +0100 Subject: [PATCH 0759/1052] Add OCI Image Labels (#7924) --- docker/Dockerfile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index cc05849271..1754d951e5 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -196,8 +196,16 @@ LABEL \ io.hass.name="ESPHome" \ io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ io.hass.type="addon" \ - io.hass.version="${BUILD_VERSION}" + io.hass.version="${BUILD_VERSION}" \ # io.hass.arch is inherited from addon-debian-base + org.opencontainers.image.authors="The ESPHome Authors" \ + org.opencontainers.image.title="ESPHome" \ + org.opencontainers.image.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + org.opencontainers.image.url="https://esphome.io/" \ + org.opencontainers.image.documentation="https://esphome.io/" \ + org.opencontainers.image.source="https://github.com/esphome/esphome" \ + org.opencontainers.image.licenses="ESPHome" \ + org.opencontainers.image.version=${BUILD_VERSION} From 58123845ff5f5a61139b9144b266fc1d32136926 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 6 Dec 2024 14:11:11 +1300 Subject: [PATCH 0760/1052] Move docker oci labels to correct image (#7927) --- docker/Dockerfile | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1754d951e5..947e410fe1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -163,6 +163,18 @@ ENTRYPOINT ["/entrypoint.sh"] CMD ["dashboard", "/config"] +ARG BUILD_VERSION=dev + +# Labels +LABEL \ + org.opencontainers.image.authors="The ESPHome Authors" \ + org.opencontainers.image.title="ESPHome" \ + org.opencontainers.image.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + org.opencontainers.image.url="https://esphome.io/" \ + org.opencontainers.image.documentation="https://esphome.io/" \ + org.opencontainers.image.source="https://github.com/esphome/esphome" \ + org.opencontainers.image.licenses="ESPHome" \ + org.opencontainers.image.version=${BUILD_VERSION} # ======================= hassio-type image ======================= @@ -196,16 +208,8 @@ LABEL \ io.hass.name="ESPHome" \ io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ io.hass.type="addon" \ - io.hass.version="${BUILD_VERSION}" \ + io.hass.version="${BUILD_VERSION}" # io.hass.arch is inherited from addon-debian-base - org.opencontainers.image.authors="The ESPHome Authors" \ - org.opencontainers.image.title="ESPHome" \ - org.opencontainers.image.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ - org.opencontainers.image.url="https://esphome.io/" \ - org.opencontainers.image.documentation="https://esphome.io/" \ - org.opencontainers.image.source="https://github.com/esphome/esphome" \ - org.opencontainers.image.licenses="ESPHome" \ - org.opencontainers.image.version=${BUILD_VERSION} From b0e3ac01e83258329244ca0efc557c2225c7d790 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:24:20 +1300 Subject: [PATCH 0761/1052] Update project description (#7928) --- docker/Dockerfile | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 947e410fe1..0bb558d35e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -169,7 +169,7 @@ ARG BUILD_VERSION=dev LABEL \ org.opencontainers.image.authors="The ESPHome Authors" \ org.opencontainers.image.title="ESPHome" \ - org.opencontainers.image.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + org.opencontainers.image.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \ org.opencontainers.image.url="https://esphome.io/" \ org.opencontainers.image.documentation="https://esphome.io/" \ org.opencontainers.image.source="https://github.com/esphome/esphome" \ @@ -206,7 +206,7 @@ RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ # Labels LABEL \ io.hass.name="ESPHome" \ - io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \ + io.hass.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \ io.hass.type="addon" \ io.hass.version="${BUILD_VERSION}" # io.hass.arch is inherited from addon-debian-base diff --git a/pyproject.toml b/pyproject.toml index cfc279845f..7789f6d645 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "esphome" license = {text = "MIT"} -description = "Make creating custom firmwares for ESP32/ESP8266 super easy." +description = "ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems." readme = "README.md" authors = [ {name = "The ESPHome Authors", email = "esphome@nabucasa.com"} From 749a5e3348e5da84b8a199ef49ea5122d993d6c1 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 5 Dec 2024 20:41:53 -0600 Subject: [PATCH 0762/1052] [modbus] More clean-up (#7921) --- .../components/modbus_controller/modbus_controller.cpp | 10 +++++----- .../modbus_controller/switch/modbus_switch.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 641ba68223..3f487abc94 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -152,9 +152,9 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t } SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const { - auto reg_it = find_if(begin(this->register_ranges_), end(this->register_ranges_), [=](RegisterRange const &r) { - return (r.start_address == start_address && r.register_type == register_type); - }); + auto reg_it = std::find_if( + std::begin(this->register_ranges_), std::end(this->register_ranges_), + [=](RegisterRange const &r) { return (r.start_address == start_address && r.register_type == register_type); }); if (reg_it == this->register_ranges_.end()) { ESP_LOGE(TAG, "No matching range for sensor found - start_address : 0x%X", start_address); @@ -375,12 +375,12 @@ void ModbusController::loop() { if (!this->incoming_queue_.empty()) { auto &message = this->incoming_queue_.front(); if (message != nullptr) - process_modbus_data_(message.get()); + this->process_modbus_data_(message.get()); this->incoming_queue_.pop(); } else { // all messages processed send pending commands - send_next_command_(); + this->send_next_command_(); } } diff --git a/esphome/components/modbus_controller/switch/modbus_switch.cpp b/esphome/components/modbus_controller/switch/modbus_switch.cpp index ec29eca7f8..b729e2659f 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.cpp +++ b/esphome/components/modbus_controller/switch/modbus_switch.cpp @@ -97,7 +97,7 @@ void ModbusSwitch::write_state(bool state) { } } this->parent_->queue_command(cmd); - publish_state(state); + this->publish_state(state); } // ModbusSwitch end } // namespace modbus_controller From 39cbc6b183c69fce10eb77c1c9393687335b7a30 Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Tue, 26 Nov 2024 00:47:01 +0300 Subject: [PATCH 0763/1052] [opentherm] Fix out of memory errors on ESP8266 (#7835) --- esphome/components/opentherm/hub.cpp | 13 ++++---- esphome/components/opentherm/opentherm.cpp | 36 ++++++---------------- esphome/components/opentherm/opentherm.h | 7 ++--- esphome/core/helpers.cpp | 12 ++++++++ esphome/core/helpers.h | 8 +++++ 5 files changed, 39 insertions(+), 37 deletions(-) diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index dfa8ea95c5..aac2966ed1 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -138,7 +138,7 @@ OpenthermHub::OpenthermHub() : Component(), in_pin_{}, out_pin_{} {} void OpenthermHub::process_response(OpenthermData &data) { ESP_LOGD(TAG, "Received OpenTherm response with id %d (%s)", data.id, this->opentherm_->message_id_to_str((MessageId) data.id)); - ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(data).c_str()); + this->opentherm_->debug_data(data); switch (data.id) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_RESPONSE_MESSAGE, OPENTHERM_MESSAGE_RESPONSE_ENTITY, , @@ -315,7 +315,7 @@ void OpenthermHub::start_conversation_() { ESP_LOGD(TAG, "Sending request with id %d (%s)", request.id, this->opentherm_->message_id_to_str((MessageId) request.id)); - ESP_LOGD(TAG, "%s", this->opentherm_->debug_data(request).c_str()); + this->opentherm_->debug_data(request); // Send the request this->last_conversation_start_ = millis(); this->opentherm_->send(request); @@ -340,19 +340,18 @@ void OpenthermHub::stop_opentherm_() { this->opentherm_->stop(); this->last_conversation_end_ = millis(); } - void OpenthermHub::handle_protocol_write_error_() { ESP_LOGW(TAG, "Error while sending request: %s", this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode())); - ESP_LOGW(TAG, "%s", this->opentherm_->debug_data(this->last_request_).c_str()); + this->opentherm_->debug_data(this->last_request_); } - void OpenthermHub::handle_protocol_read_error_() { OpenThermError error; this->opentherm_->get_protocol_error(error); - ESP_LOGW(TAG, "Protocol error occured while receiving response: %s", this->opentherm_->debug_error(error).c_str()); + ESP_LOGW(TAG, "Protocol error occured while receiving response: %s", + this->opentherm_->protocol_error_to_to_str(error.error_type)); + this->opentherm_->debug_error(error); } - void OpenthermHub::handle_timeout_error_() { ESP_LOGW(TAG, "Receive response timed out at a protocol level"); this->stop_opentherm_(); diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 26c707f9a0..62cfcdceea 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -15,15 +15,11 @@ #include "Arduino.h" #endif #include -#include -#include namespace esphome { namespace opentherm { using std::string; -using std::bitset; -using std::stringstream; using std::to_string; static const char *const TAG = "opentherm"; @@ -545,29 +541,17 @@ const char *OpenTherm::message_id_to_str(MessageId id) { } } -string OpenTherm::debug_data(OpenthermData &data) { - stringstream result; - result << bitset<8>(data.type) << " " << bitset<8>(data.id) << " " << bitset<8>(data.valueHB) << " " - << bitset<8>(data.valueLB) << "\n"; - result << "type: " << this->message_type_to_str((MessageType) data.type) << "; "; - result << "id: " << to_string(data.id) << "; "; - result << "HB: " << to_string(data.valueHB) << "; "; - result << "LB: " << to_string(data.valueLB) << "; "; - result << "uint_16: " << to_string(data.u16()) << "; "; - result << "float: " << to_string(data.f88()); - - return result.str(); +void OpenTherm::debug_data(OpenthermData &data) { + ESP_LOGD(TAG, "%s %s %s %s", format_bin(data.type).c_str(), format_bin(data.id).c_str(), + format_bin(data.valueHB).c_str(), format_bin(data.valueLB).c_str()); + ESP_LOGD(TAG, "type: %s; id: %s; HB: %s; LB: %s; uint_16: %s; float: %s", + this->message_type_to_str((MessageType) data.type), to_string(data.id).c_str(), + to_string(data.valueHB).c_str(), to_string(data.valueLB).c_str(), to_string(data.u16()).c_str(), + to_string(data.f88()).c_str()); } -std::string OpenTherm::debug_error(OpenThermError &error) { - stringstream result; - result << "type: " << this->protocol_error_to_to_str(error.error_type) << "; "; - result << "data: "; - result << format_hex(error.data); - result << "; clock: " << to_string(clock_); - result << "; capture: " << bitset<32>(error.capture); - result << "; bit_pos: " << to_string(error.bit_pos); - - return result.str(); +void OpenTherm::debug_error(OpenThermError &error) const { + ESP_LOGD(TAG, "data: %s; clock: %s; capture: %s; bit_pos: %s", format_hex(error.data).c_str(), + to_string(clock_).c_str(), format_bin(error.capture).c_str(), to_string(error.bit_pos).c_str()); } float OpenthermData::f88() { return ((float) this->s16()) / 256.0; } diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 85f4611125..76710af5f5 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -8,10 +8,9 @@ #pragma once #include -#include -#include #include "esphome/core/hal.h" #include "esphome/core/log.h" +#include "esphome/core/helpers.h" #if defined(ESP32) || defined(USE_ESP_IDF) #include "driver/timer.h" @@ -318,8 +317,8 @@ class OpenTherm { OperationMode get_mode() { return mode_; } - std::string debug_data(OpenthermData &data); - std::string debug_error(OpenThermError &error); + void debug_data(OpenthermData &data); + void debug_error(OpenThermError &error) const; const char *protocol_error_to_to_str(ProtocolErrorType error_type); const char *message_type_to_str(MessageType message_type); diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index dae60a4e1d..befc84516c 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -397,6 +397,18 @@ std::string format_hex_pretty(const uint16_t *data, size_t length) { } std::string format_hex_pretty(const std::vector &data) { return format_hex_pretty(data.data(), data.size()); } +std::string format_bin(const uint8_t *data, size_t length) { + std::string result; + result.resize(length * 8); + for (size_t byte_idx = 0; byte_idx < length; byte_idx++) { + for (size_t bit_idx = 0; bit_idx < 8; bit_idx++) { + result[byte_idx * 8 + bit_idx] = ((data[byte_idx] >> (7 - bit_idx)) & 1) + '0'; + } + } + + return result; +} + ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) { if (on == nullptr && strcasecmp(str, "on") == 0) return PARSE_ON; diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 43001bafdd..305ec47f76 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -420,6 +420,14 @@ template::value, int> = 0> std::stri return format_hex_pretty(reinterpret_cast(&val), sizeof(T)); } +/// Format the byte array \p data of length \p len in binary. +std::string format_bin(const uint8_t *data, size_t length); +/// Format an unsigned integer in binary, starting with the most significant byte. +template::value, int> = 0> std::string format_bin(T val) { + val = convert_big_endian(val); + return format_bin(reinterpret_cast(&val), sizeof(T)); +} + /// Return values for parse_on_off(). enum ParseOnOffState { PARSE_NONE = 0, From e623989878b3f73b5512c6380b4d4446183443da Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Mon, 25 Nov 2024 14:15:01 -1000 Subject: [PATCH 0764/1052] fix local time timestamp calculation (#7807) Co-authored-by: Samuel Sieb --- .../components/datetime/datetime_entity.cpp | 4 +-- esphome/core/time.cpp | 34 +++++++++++-------- esphome/core/time.h | 4 +-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/esphome/components/datetime/datetime_entity.cpp b/esphome/components/datetime/datetime_entity.cpp index f215b7acb5..3d92194efa 100644 --- a/esphome/components/datetime/datetime_entity.cpp +++ b/esphome/components/datetime/datetime_entity.cpp @@ -60,9 +60,7 @@ ESPTime DateTimeEntity::state_as_esptime() const { obj.hour = this->hour_; obj.minute = this->minute_; obj.second = this->second_; - obj.day_of_week = 1; // Required to be valid for recalc_timestamp_local but not used. - obj.day_of_year = 1; // Required to be valid for recalc_timestamp_local but not used. - obj.recalc_timestamp_local(false); + obj.recalc_timestamp_local(); return obj; } diff --git a/esphome/core/time.cpp b/esphome/core/time.cpp index f7aa4fdddb..31977d972b 100644 --- a/esphome/core/time.cpp +++ b/esphome/core/time.cpp @@ -5,20 +5,18 @@ namespace esphome { -bool is_leap_year(uint32_t year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); } - uint8_t days_in_month(uint8_t month, uint16_t year) { static const uint8_t DAYS_IN_MONTH[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - uint8_t days = DAYS_IN_MONTH[month]; - if (month == 2 && is_leap_year(year)) + if (month == 2 && (year % 4 == 0)) return 29; - return days; + return DAYS_IN_MONTH[month]; } size_t ESPTime::strftime(char *buffer, size_t buffer_len, const char *format) { struct tm c_tm = this->to_c_tm(); return ::strftime(buffer, buffer_len, format, &c_tm); } + ESPTime ESPTime::from_c_tm(struct tm *c_tm, time_t c_time) { ESPTime res{}; res.second = uint8_t(c_tm->tm_sec); @@ -33,6 +31,7 @@ ESPTime ESPTime::from_c_tm(struct tm *c_tm, time_t c_time) { res.timestamp = c_time; return res; } + struct tm ESPTime::to_c_tm() { struct tm c_tm {}; c_tm.tm_sec = this->second; @@ -46,6 +45,7 @@ struct tm ESPTime::to_c_tm() { c_tm.tm_isdst = this->is_dst; return c_tm; } + std::string ESPTime::strftime(const std::string &format) { std::string timestr; timestr.resize(format.size() * 4); @@ -142,6 +142,7 @@ void ESPTime::increment_second() { this->year++; } } + void ESPTime::increment_day() { this->timestamp += 86400; @@ -159,23 +160,22 @@ void ESPTime::increment_day() { this->year++; } } + void ESPTime::recalc_timestamp_utc(bool use_day_of_year) { time_t res = 0; - if (!this->fields_in_range()) { this->timestamp = -1; return; } for (int i = 1970; i < this->year; i++) - res += is_leap_year(i) ? 366 : 365; + res += (year % 4 == 0) ? 366 : 365; if (use_day_of_year) { res += this->day_of_year - 1; } else { for (int i = 1; i < this->month; i++) res += days_in_month(i, this->year); - res += this->day_of_month - 1; } @@ -188,13 +188,17 @@ void ESPTime::recalc_timestamp_utc(bool use_day_of_year) { this->timestamp = res; } -void ESPTime::recalc_timestamp_local(bool use_day_of_year) { - this->recalc_timestamp_utc(use_day_of_year); - this->timestamp -= ESPTime::timezone_offset(); - ESPTime temp = ESPTime::from_epoch_local(this->timestamp); - if (temp.is_dst) { - this->timestamp -= 3600; - } +void ESPTime::recalc_timestamp_local() { + struct tm tm; + + tm.tm_year = this->year - 1900; + tm.tm_mon = this->month - 1; + tm.tm_mday = this->day_of_month; + tm.tm_hour = this->hour; + tm.tm_min = this->minute; + tm.tm_sec = this->second; + + this->timestamp = mktime(&tm); } int32_t ESPTime::timezone_offset() { diff --git a/esphome/core/time.h b/esphome/core/time.h index bce1108d93..5cbd9369fb 100644 --- a/esphome/core/time.h +++ b/esphome/core/time.h @@ -9,8 +9,6 @@ namespace esphome { template bool increment_time_value(T ¤t, uint16_t begin, uint16_t end); -bool is_leap_year(uint32_t year); - uint8_t days_in_month(uint8_t month, uint16_t year); /// A more user-friendly version of struct tm from time.h @@ -100,7 +98,7 @@ struct ESPTime { void recalc_timestamp_utc(bool use_day_of_year = true); /// Recalculate the timestamp field from the other fields of this ESPTime instance assuming local fields. - void recalc_timestamp_local(bool use_day_of_year = true); + void recalc_timestamp_local(); /// Convert this ESPTime instance back to a tm struct. struct tm to_c_tm(); From 3bac45e737655af9f8fbf7734e3a17620fcdec47 Mon Sep 17 00:00:00 2001 From: guillempages Date: Thu, 28 Nov 2024 04:55:20 +0100 Subject: [PATCH 0765/1052] [online_image]Don't access decoder if not initialized (#7882) --- esphome/components/online_image/png_image.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/online_image/png_image.cpp b/esphome/components/online_image/png_image.cpp index c8e215a91d..4c4c22f9b7 100644 --- a/esphome/components/online_image/png_image.cpp +++ b/esphome/components/online_image/png_image.cpp @@ -49,6 +49,10 @@ void PngDecoder::prepare(uint32_t download_size) { } int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { + if (!this->pngle_) { + ESP_LOGE(TAG, "PNG decoder engine not initialized!"); + return -1; + } if (size < 256 && size < this->download_size_ - this->decoded_bytes_) { ESP_LOGD(TAG, "Waiting for data"); return 0; From 5717d557f5f421ae3402a5d13510dda81ac4bebd Mon Sep 17 00:00:00 2001 From: FreeBear-nc <67865163+FreeBear-nc@users.noreply.github.com> Date: Thu, 28 Nov 2024 03:56:37 +0000 Subject: [PATCH 0766/1052] Add IRAM_ATTR to all functions used during interrupts on esp8266 chips. (#7840) --- esphome/components/opentherm/opentherm.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 62cfcdceea..c56b49ccb8 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -220,7 +220,7 @@ void IRAM_ATTR OpenTherm::bit_read_(uint8_t value) { this->bit_pos_++; } -ProtocolErrorType OpenTherm::verify_stop_bit_(uint8_t value) { +ProtocolErrorType IRAM_ATTR OpenTherm::verify_stop_bit_(uint8_t value) { if (value) { // stop bit detected return check_parity_(this->data_) ? ProtocolErrorType::NO_ERROR : ProtocolErrorType::PARITY_ERROR; } else { // no stop bit detected, error @@ -365,7 +365,7 @@ void IRAM_ATTR OpenTherm::stop_timer_() { #ifdef ESP8266 // 5 kHz timer_ -void OpenTherm::start_read_timer_() { +void IRAM_ATTR OpenTherm::start_read_timer_() { InterruptLock const lock; timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) @@ -373,14 +373,14 @@ void OpenTherm::start_read_timer_() { } // 2 kHz timer_ -void OpenTherm::start_write_timer_() { +void IRAM_ATTR OpenTherm::start_write_timer_() { InterruptLock const lock; timer1_attachInterrupt(OpenTherm::esp8266_timer_isr); timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); // 5MHz (5 ticks/us - 1677721.4 us max) timer1_write(2500); // 2kHz } -void OpenTherm::stop_timer_() { +void IRAM_ATTR OpenTherm::stop_timer_() { InterruptLock const lock; timer1_disable(); timer1_detachInterrupt(); @@ -389,7 +389,7 @@ void OpenTherm::stop_timer_() { #endif // END ESP8266 // https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd -bool OpenTherm::check_parity_(uint32_t val) { +bool IRAM_ATTR OpenTherm::check_parity_(uint32_t val) { val ^= val >> 16; val ^= val >> 8; val ^= val >> 4; From 5fcd26bfe964f670759e08bc89386ee9c2b1c0d4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:57:11 +1300 Subject: [PATCH 0767/1052] [st7920] Remove unnecessary warning when drawing outside display bounds (#7868) --- esphome/components/st7920/st7920.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/st7920/st7920.cpp b/esphome/components/st7920/st7920.cpp index f336d24e24..171e7095dd 100644 --- a/esphome/components/st7920/st7920.cpp +++ b/esphome/components/st7920/st7920.cpp @@ -1,7 +1,7 @@ #include "st7920.h" -#include "esphome/core/log.h" -#include "esphome/core/application.h" #include "esphome/components/display/display_buffer.h" +#include "esphome/core/application.h" +#include "esphome/core/log.h" namespace esphome { namespace st7920 { @@ -118,7 +118,6 @@ size_t ST7920::get_buffer_length_() { void HOT ST7920::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { - ESP_LOGW(TAG, "Position out of area: %dx%d", x, y); return; } int width = this->get_width_internal() / 8u; From f042c6e643a0c1b67816cc1824611b98d6317ac4 Mon Sep 17 00:00:00 2001 From: Krzysztof Zdulski Date: Fri, 29 Nov 2024 22:05:00 +0100 Subject: [PATCH 0768/1052] Fix recalc_timestamp_utc (#7894) --- esphome/core/time.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/time.cpp b/esphome/core/time.cpp index 31977d972b..66a0e1c0a7 100644 --- a/esphome/core/time.cpp +++ b/esphome/core/time.cpp @@ -169,7 +169,7 @@ void ESPTime::recalc_timestamp_utc(bool use_day_of_year) { } for (int i = 1970; i < this->year; i++) - res += (year % 4 == 0) ? 366 : 365; + res += (i % 4 == 0) ? 366 : 365; if (use_day_of_year) { res += this->day_of_year - 1; From 982ce1db727b103250b69c91e3edaac918bd5f37 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 2 Dec 2024 05:10:18 +1300 Subject: [PATCH 0769/1052] Cast port to int for ota pushing (#7888) --- esphome/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index 86d529e1bf..dce041e5ac 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -363,7 +363,7 @@ def upload_program(config, args, host): from esphome import espota2 - remote_port = ota_conf[CONF_PORT] + remote_port = int(ota_conf[CONF_PORT]) password = ota_conf.get(CONF_PASSWORD, "") if ( From d0958f7cf28dd68e7149ae32d06b3489caf55c11 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:50:11 +1100 Subject: [PATCH 0770/1052] [lvgl] Bugfixes (#7896) --- esphome/components/lvgl/defines.py | 2 +- esphome/components/lvgl/lvgl_esphome.h | 3 +++ esphome/components/lvgl/widgets/line.py | 6 ++++++ tests/components/lvgl/lvgl-package.yaml | 4 ++-- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index ea345fa55c..bb7d25c2c7 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -38,7 +38,7 @@ def literal(arg): def call_lambda(lamb: LambdaExpression): expr = lamb.content.strip() if expr.startswith("return") and expr.endswith(";"): - return expr[7:][:-1] + return expr[6:][:-1].strip() return f"{lamb}()" diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 208cb1cbd5..7bc6b00cf5 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -56,6 +56,9 @@ static const display::ColorBitness LV_BITNESS = display::ColorBitness::COLOR_BIT inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) { lv_img_set_src(obj, image->get_lv_img_dsc()); } +inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) { + lv_disp_set_bg_image(disp, image->get_lv_img_dsc()); +} #endif // USE_LVGL_IMAGE // Parent class for things that wrap an LVGL object diff --git a/esphome/components/lvgl/widgets/line.py b/esphome/components/lvgl/widgets/line.py index 548dfa8452..0156fb1780 100644 --- a/esphome/components/lvgl/widgets/line.py +++ b/esphome/components/lvgl/widgets/line.py @@ -35,6 +35,11 @@ LINE_SCHEMA = { cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t), } +LINE_MODIFY_SCHEMA = { + cv.Optional(CONF_POINTS): cv_point_list, + cv.GenerateID(CONF_POINT_LIST_ID): cv.declare_id(lv_point_t), +} + class LineType(WidgetType): def __init__(self): @@ -43,6 +48,7 @@ class LineType(WidgetType): LvType("lv_line_t"), (CONF_MAIN,), LINE_SCHEMA, + modify_schema=LINE_MODIFY_SCHEMA, ) async def to_code(self, w: Widget, config): diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index db0443b3bb..b83ff2a3d5 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -333,7 +333,7 @@ lvgl: id: button_button width: 20% height: 10% - transform_angle: !lambda return 180*100; + transform_angle: !lambda return(180*100); arc_width: !lambda return 4; border_width: !lambda return 6; shadow_ofs_x: !lambda return 6; @@ -577,7 +577,7 @@ lvgl: - 180, 60 - 240, 10 on_click: - - lvgl.widget.update: + - lvgl.line.update: id: lv_line_id line_color: 0xFFFF - lvgl.page.next: From 86ae1c59315261fc36c207b4b76d32385e68a3ba Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 4 Dec 2024 07:48:50 +1100 Subject: [PATCH 0771/1052] [lvgl] Fix msgbox content (#7912) --- esphome/components/lvgl/widgets/msgbox.py | 3 ++- tests/components/lvgl/lvgl-package.yaml | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index be0f2100d7..c3393940b6 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -29,7 +29,7 @@ from ..lvcode import ( ) from ..schemas import STYLE_SCHEMA, STYLED_TEXT_SCHEMA, container_schema, part_schema from ..types import LV_EVENT, char_ptr, lv_obj_t -from . import Widget, set_obj_properties +from . import Widget, add_widgets, set_obj_properties from .button import button_spec from .buttonmatrix import ( BUTTONMATRIX_BUTTON_SCHEMA, @@ -119,6 +119,7 @@ async def msgbox_to_code(top_layer, conf): button_style = {CONF_ITEMS: button_style} await set_obj_properties(buttonmatrix_widget, button_style) await set_obj_properties(msgbox_widget, conf) + await add_widgets(msgbox_widget, conf) async with LambdaContext(EVENT_ARG, where=messagebox_id) as close_action: outer_widget.add_flag("LV_OBJ_FLAG_HIDDEN") if close_button: diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index b83ff2a3d5..e5df30f136 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -109,6 +109,10 @@ lvgl: close_button: true title: Messagebox bg_color: 0xffff + widgets: + - label: + text: Hello Msgbox + id: msgbox_label body: text: This is a sample messagebox bg_color: 0x808080 @@ -137,6 +141,9 @@ lvgl: - lvgl.widget.focus: mark - lvgl.widget.redraw: hello_label - lvgl.widget.redraw: + - lvgl.label.update: + id: msgbox_label + text: Unloaded on_all_events: logger.log: format: "Event %s" From c8ec0bb7eab3d77cb5b62a227d4041677f1225ad Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:16:59 +1100 Subject: [PATCH 0772/1052] [esp32] Fix crash with empty `platformio_options:` value (#7920) --- esphome/components/esp32/__init__.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 61fbb53e3a..aaef68fa27 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -355,24 +355,20 @@ def _detect_variant(value): def final_validate(config): - if CONF_PLATFORMIO_OPTIONS not in fv.full_config.get()[CONF_ESPHOME]: + if not ( + pio_options := fv.full_config.get()[CONF_ESPHOME].get(CONF_PLATFORMIO_OPTIONS) + ): + # Not specified or empty return config pio_flash_size_key = "board_upload.flash_size" pio_partitions_key = "board_build.partitions" - if ( - CONF_PARTITIONS in config - and pio_partitions_key - in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS] - ): + if CONF_PARTITIONS in config and pio_partitions_key in pio_options: raise cv.Invalid( f"Do not specify '{pio_partitions_key}' in '{CONF_PLATFORMIO_OPTIONS}' with '{CONF_PARTITIONS}' in esp32" ) - if ( - pio_flash_size_key - in fv.full_config.get()[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS] - ): + if pio_flash_size_key in pio_options: raise cv.Invalid( f"Please specify {CONF_FLASH_SIZE} within esp32 configuration only" ) From c80e035bd56fbfe89475fdb149b0ff26fb279e61 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:55:51 +1300 Subject: [PATCH 0773/1052] Bump version to 2024.11.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4b19e2865d..ae7feda6d8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.11.2" +__version__ = "2024.11.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 9d000e9abf3832497e8a6640e66c0eb4c8568060 Mon Sep 17 00:00:00 2001 From: Citric Lee <37475446+limengdu@users.noreply.github.com> Date: Mon, 9 Dec 2024 10:28:41 +0800 Subject: [PATCH 0774/1052] Add: Seeed Studio MR60BHA2 mmWave Sensor (#7589) Co-authored-by: Spencer Yan Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/seeed_mr60bha2/__init__.py | 41 +++++ .../seeed_mr60bha2/seeed_mr60bha2.cpp | 173 ++++++++++++++++++ .../seeed_mr60bha2/seeed_mr60bha2.h | 61 ++++++ esphome/components/seeed_mr60bha2/sensor.py | 57 ++++++ esphome/const.py | 2 + tests/components/seeed_mr60bha2/common.yaml | 19 ++ .../seeed_mr60bha2/test.esp32-c3-ard.yaml | 5 + .../seeed_mr60bha2/test.esp32-c3-idf.yaml | 5 + 9 files changed, 364 insertions(+) create mode 100644 esphome/components/seeed_mr60bha2/__init__.py create mode 100644 esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp create mode 100644 esphome/components/seeed_mr60bha2/seeed_mr60bha2.h create mode 100644 esphome/components/seeed_mr60bha2/sensor.py create mode 100644 tests/components/seeed_mr60bha2/common.yaml create mode 100644 tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml create mode 100644 tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 74c205b302..404ad35efc 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -355,6 +355,7 @@ esphome/components/sdl/* @clydebarrow esphome/components/sdm_meter/* @jesserockz @polyfaces esphome/components/sdp3x/* @Azimath esphome/components/seeed_mr24hpc1/* @limengdu +esphome/components/seeed_mr60bha2/* @limengdu esphome/components/seeed_mr60fda2/* @limengdu esphome/components/selec_meter/* @sourabhjaiswal esphome/components/select/* @esphome/core diff --git a/esphome/components/seeed_mr60bha2/__init__.py b/esphome/components/seeed_mr60bha2/__init__.py new file mode 100644 index 0000000000..87bdbbd003 --- /dev/null +++ b/esphome/components/seeed_mr60bha2/__init__.py @@ -0,0 +1,41 @@ +import esphome.codegen as cg +from esphome.components import uart +import esphome.config_validation as cv +from esphome.const import CONF_ID + +CODEOWNERS = ["@limengdu"] +DEPENDENCIES = ["uart"] +MULTI_CONF = True + +mr60bha2_ns = cg.esphome_ns.namespace("seeed_mr60bha2") + +MR60BHA2Component = mr60bha2_ns.class_( + "MR60BHA2Component", cg.Component, uart.UARTDevice +) + +CONF_MR60BHA2_ID = "mr60bha2_id" + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MR60BHA2Component), + } + ) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) + +FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema( + "seeed_mr60bha2", + require_tx=True, + require_rx=True, + baud_rate=115200, + parity="NONE", + stop_bits=1, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) diff --git a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp new file mode 100644 index 0000000000..50d709c3b0 --- /dev/null +++ b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp @@ -0,0 +1,173 @@ +#include "seeed_mr60bha2.h" +#include "esphome/core/log.h" + +#include + +namespace esphome { +namespace seeed_mr60bha2 { + +static const char *const TAG = "seeed_mr60bha2"; + +// Prints the component's configuration data. dump_config() prints all of the component's configuration +// items in an easy-to-read format, including the configuration key-value pairs. +void MR60BHA2Component::dump_config() { + ESP_LOGCONFIG(TAG, "MR60BHA2:"); +#ifdef USE_SENSOR + LOG_SENSOR(" ", "Breath Rate Sensor", this->breath_rate_sensor_); + LOG_SENSOR(" ", "Heart Rate Sensor", this->heart_rate_sensor_); + LOG_SENSOR(" ", "Distance Sensor", this->distance_sensor_); +#endif +} + +// main loop +void MR60BHA2Component::loop() { + uint8_t byte; + + // Is there data on the serial port + while (this->available()) { + this->read_byte(&byte); + this->rx_message_.push_back(byte); + if (!this->validate_message_()) { + this->rx_message_.clear(); + } + } +} + +/** + * @brief Calculate the checksum for a byte array. + * + * This function calculates the checksum for the provided byte array using an + * XOR-based checksum algorithm. + * + * @param data The byte array to calculate the checksum for. + * @param len The length of the byte array. + * @return The calculated checksum. + */ +static uint8_t calculate_checksum(const uint8_t *data, size_t len) { + uint8_t checksum = 0; + for (size_t i = 0; i < len; i++) { + checksum ^= data[i]; + } + checksum = ~checksum; + return checksum; +} + +/** + * @brief Validate the checksum of a byte array. + * + * This function validates the checksum of the provided byte array by comparing + * it to the expected checksum. + * + * @param data The byte array to validate. + * @param len The length of the byte array. + * @param expected_checksum The expected checksum. + * @return True if the checksum is valid, false otherwise. + */ +static bool validate_checksum(const uint8_t *data, size_t len, uint8_t expected_checksum) { + return calculate_checksum(data, len) == expected_checksum; +} + +bool MR60BHA2Component::validate_message_() { + size_t at = this->rx_message_.size() - 1; + auto *data = &this->rx_message_[0]; + uint8_t new_byte = data[at]; + + if (at == 0) { + return new_byte == FRAME_HEADER_BUFFER; + } + + if (at <= 2) { + return true; + } + uint16_t frame_id = encode_uint16(data[1], data[2]); + + if (at <= 4) { + return true; + } + + uint16_t length = encode_uint16(data[3], data[4]); + + if (at <= 6) { + return true; + } + + uint16_t frame_type = encode_uint16(data[5], data[6]); + + if (frame_type != BREATH_RATE_TYPE_BUFFER && frame_type != HEART_RATE_TYPE_BUFFER && + frame_type != DISTANCE_TYPE_BUFFER) { + return false; + } + + uint8_t header_checksum = new_byte; + + if (at == 7) { + if (!validate_checksum(data, 7, header_checksum)) { + ESP_LOGE(TAG, "HEAD_CKSUM_FRAME ERROR: 0x%02x", header_checksum); + ESP_LOGV(TAG, "GET FRAME: %s", format_hex_pretty(data, 8).c_str()); + return false; + } + return true; + } + + // Wait until all data is read + if (at - 8 < length) { + return true; + } + + uint8_t data_checksum = new_byte; + if (at == 8 + length) { + if (!validate_checksum(data + 8, length, data_checksum)) { + ESP_LOGE(TAG, "DATA_CKSUM_FRAME ERROR: 0x%02x", data_checksum); + ESP_LOGV(TAG, "GET FRAME: %s", format_hex_pretty(data, 8 + length).c_str()); + return false; + } + } + + const uint8_t *frame_data = data + 8; + ESP_LOGV(TAG, "Received Frame: ID: 0x%04x, Type: 0x%04x, Data: [%s] Raw Data: [%s]", frame_id, frame_type, + format_hex_pretty(frame_data, length).c_str(), format_hex_pretty(this->rx_message_).c_str()); + this->process_frame_(frame_id, frame_type, data + 8, length); + + // Return false to reset rx buffer + return false; +} + +void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, const uint8_t *data, size_t length) { + switch (frame_type) { + case BREATH_RATE_TYPE_BUFFER: + if (this->breath_rate_sensor_ != nullptr && length >= 4) { + uint32_t current_breath_rate_int = encode_uint32(data[3], data[2], data[1], data[0]); + if (current_breath_rate_int != 0) { + float breath_rate_float; + memcpy(&breath_rate_float, ¤t_breath_rate_int, sizeof(float)); + this->breath_rate_sensor_->publish_state(breath_rate_float); + } + } + break; + case HEART_RATE_TYPE_BUFFER: + if (this->heart_rate_sensor_ != nullptr && length >= 4) { + uint32_t current_heart_rate_int = encode_uint32(data[3], data[2], data[1], data[0]); + if (current_heart_rate_int != 0) { + float heart_rate_float; + memcpy(&heart_rate_float, ¤t_heart_rate_int, sizeof(float)); + this->heart_rate_sensor_->publish_state(heart_rate_float); + } + } + break; + case DISTANCE_TYPE_BUFFER: + if (!data[0]) { + if (this->distance_sensor_ != nullptr && length >= 8) { + uint32_t current_distance_int = encode_uint32(data[7], data[6], data[5], data[4]); + float distance_float; + memcpy(&distance_float, ¤t_distance_int, sizeof(float)); + this->distance_sensor_->publish_state(distance_float); + } + } + break; + default: + break; + } +} + +} // namespace seeed_mr60bha2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.h b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.h new file mode 100644 index 0000000000..0a4f21f1ad --- /dev/null +++ b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.h @@ -0,0 +1,61 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#ifdef USE_SENSOR +#include "esphome/components/sensor/sensor.h" +#endif +#include "esphome/components/uart/uart.h" +#include "esphome/core/automation.h" +#include "esphome/core/helpers.h" + +#include + +namespace esphome { +namespace seeed_mr60bha2 { + +static const uint8_t DATA_BUF_MAX_SIZE = 12; +static const uint8_t FRAME_BUF_MAX_SIZE = 21; +static const uint8_t LEN_TO_HEAD_CKSUM = 8; +static const uint8_t LEN_TO_DATA_FRAME = 9; + +static const uint8_t FRAME_HEADER_BUFFER = 0x01; +static const uint16_t BREATH_RATE_TYPE_BUFFER = 0x0A14; +static const uint16_t HEART_RATE_TYPE_BUFFER = 0x0A15; +static const uint16_t DISTANCE_TYPE_BUFFER = 0x0A16; + +enum FrameLocation { + LOCATE_FRAME_HEADER, + LOCATE_ID_FRAME1, + LOCATE_ID_FRAME2, + LOCATE_LENGTH_FRAME_H, + LOCATE_LENGTH_FRAME_L, + LOCATE_TYPE_FRAME1, + LOCATE_TYPE_FRAME2, + LOCATE_HEAD_CKSUM_FRAME, // Header checksum: [from the first byte to the previous byte of the HEAD_CKSUM bit] + LOCATE_DATA_FRAME, + LOCATE_DATA_CKSUM_FRAME, // Data checksum: [from the first to the previous byte of the DATA_CKSUM bit] + LOCATE_PROCESS_FRAME, +}; + +class MR60BHA2Component : public Component, + public uart::UARTDevice { // The class name must be the name defined by text_sensor.py +#ifdef USE_SENSOR + SUB_SENSOR(breath_rate); + SUB_SENSOR(heart_rate); + SUB_SENSOR(distance); +#endif + + public: + float get_setup_priority() const override { return esphome::setup_priority::LATE; } + void dump_config() override; + void loop() override; + + protected: + bool validate_message_(); + void process_frame_(uint16_t frame_id, uint16_t frame_type, const uint8_t *data, size_t length); + + std::vector rx_message_; +}; + +} // namespace seeed_mr60bha2 +} // namespace esphome diff --git a/esphome/components/seeed_mr60bha2/sensor.py b/esphome/components/seeed_mr60bha2/sensor.py new file mode 100644 index 0000000000..5f30b363bf --- /dev/null +++ b/esphome/components/seeed_mr60bha2/sensor.py @@ -0,0 +1,57 @@ +import esphome.codegen as cg +from esphome.components import sensor +import esphome.config_validation as cv +from esphome.const import ( + CONF_DISTANCE, + DEVICE_CLASS_DISTANCE, + ICON_HEART_PULSE, + ICON_PULSE, + ICON_SIGNAL, + STATE_CLASS_MEASUREMENT, + UNIT_BEATS_PER_MINUTE, + UNIT_CENTIMETER, +) + +from . import CONF_MR60BHA2_ID, MR60BHA2Component + +DEPENDENCIES = ["seeed_mr60bha2"] + +CONF_BREATH_RATE = "breath_rate" +CONF_HEART_RATE = "heart_rate" + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component), + cv.Optional(CONF_BREATH_RATE): sensor.sensor_schema( + accuracy_decimals=2, + state_class=STATE_CLASS_MEASUREMENT, + icon=ICON_PULSE, + ), + cv.Optional(CONF_HEART_RATE): sensor.sensor_schema( + accuracy_decimals=0, + icon=ICON_HEART_PULSE, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_BEATS_PER_MINUTE, + ), + cv.Optional(CONF_DISTANCE): sensor.sensor_schema( + device_class=DEVICE_CLASS_DISTANCE, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_CENTIMETER, + accuracy_decimals=2, + icon=ICON_SIGNAL, + ), + } +) + + +async def to_code(config): + mr60bha2_component = await cg.get_variable(config[CONF_MR60BHA2_ID]) + if breath_rate_config := config.get(CONF_BREATH_RATE): + sens = await sensor.new_sensor(breath_rate_config) + cg.add(mr60bha2_component.set_breath_rate_sensor(sens)) + if heart_rate_config := config.get(CONF_HEART_RATE): + sens = await sensor.new_sensor(heart_rate_config) + cg.add(mr60bha2_component.set_heart_rate_sensor(sens)) + if distance_config := config.get(CONF_DISTANCE): + sens = await sensor.new_sensor(distance_config) + cg.add(mr60bha2_component.set_distance_sensor(sens)) diff --git a/esphome/const.py b/esphome/const.py index 3d3bfcc244..b9397aa1bd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1001,6 +1001,7 @@ ICON_GRAIN = "mdi:grain" ICON_GYROSCOPE_X = "mdi:axis-x-rotate-clockwise" ICON_GYROSCOPE_Y = "mdi:axis-y-rotate-clockwise" ICON_GYROSCOPE_Z = "mdi:axis-z-rotate-clockwise" +ICON_HEART_PULSE = "mdi:heart-pulse" ICON_HEATING_COIL = "mdi:heating-coil" ICON_KEY_PLUS = "mdi:key-plus" ICON_LIGHTBULB = "mdi:lightbulb" @@ -1040,6 +1041,7 @@ ICON_WEATHER_WINDY = "mdi:weather-windy" ICON_WIFI = "mdi:wifi" UNIT_AMPERE = "A" +UNIT_BEATS_PER_MINUTE = "bpm" UNIT_BECQUEREL_PER_CUBIC_METER = "Bq/m³" UNIT_BYTES = "B" UNIT_CELSIUS = "°C" diff --git a/tests/components/seeed_mr60bha2/common.yaml b/tests/components/seeed_mr60bha2/common.yaml new file mode 100644 index 0000000000..e9d0c735af --- /dev/null +++ b/tests/components/seeed_mr60bha2/common.yaml @@ -0,0 +1,19 @@ +uart: + - id: seeed_mr60fda2_uart + tx_pin: ${uart_tx_pin} + rx_pin: ${uart_rx_pin} + baud_rate: 115200 + parity: NONE + stop_bits: 1 + +seeed_mr60bha2: + id: my_seeed_mr60bha2 + +sensor: + - platform: seeed_mr60bha2 + breath_rate: + name: "Real-time respiratory rate" + heart_rate: + name: "Real-time heart rate" + distance: + name: "Distance to detection object" diff --git a/tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml b/tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..4fb884abf4 --- /dev/null +++ b/tests/components/seeed_mr60bha2/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + uart_tx_pin: GPIO5 + uart_rx_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml b/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..4fb884abf4 --- /dev/null +++ b/tests/components/seeed_mr60bha2/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + uart_tx_pin: GPIO5 + uart_rx_pin: GPIO4 + +<<: !include common.yaml From f15e3cfb9b9cc26b402d1098549a79e87c00d9bc Mon Sep 17 00:00:00 2001 From: David Schneider Date: Sun, 8 Dec 2024 18:51:37 -0800 Subject: [PATCH 0775/1052] Optimize QMC5883L reads (#7889) --- esphome/components/qmc5883l/qmc5883l.cpp | 62 +++++++++++++++++------- esphome/components/qmc5883l/qmc5883l.h | 2 +- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/esphome/components/qmc5883l/qmc5883l.cpp b/esphome/components/qmc5883l/qmc5883l.cpp index 49a67d4e09..36286244fb 100644 --- a/esphome/components/qmc5883l/qmc5883l.cpp +++ b/esphome/components/qmc5883l/qmc5883l.cpp @@ -81,16 +81,39 @@ void QMC5883LComponent::dump_config() { } float QMC5883LComponent::get_setup_priority() const { return setup_priority::DATA; } void QMC5883LComponent::update() { + i2c::ErrorCode err; uint8_t status = false; - this->read_byte(QMC5883L_REGISTER_STATUS, &status); + // Status byte gets cleared when data is read, so we have to read this first. + // If status and two axes are desired, it's possible to save one byte of traffic by enabling + // ROL_PNT in setup and reading 7 bytes starting at the status register. + // If status and all three axes are desired, using ROL_PNT saves you 3 bytes. + // But simply not reading status saves you 4 bytes always and is much simpler. + if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG) { + err = this->read_register(QMC5883L_REGISTER_STATUS, &status, 1); + if (err != i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("status read failed (%d)", err).c_str()); + return; + } + } - // Always request X,Y,Z regardless if there are sensors for them - // to avoid https://github.com/esphome/issues/issues/5731 - uint16_t raw_x, raw_y, raw_z; - if (!this->read_byte_16_(QMC5883L_REGISTER_DATA_X_LSB, &raw_x) || - !this->read_byte_16_(QMC5883L_REGISTER_DATA_Y_LSB, &raw_y) || - !this->read_byte_16_(QMC5883L_REGISTER_DATA_Z_LSB, &raw_z)) { - this->status_set_warning(); + uint16_t raw[3] = {0}; + // Z must always be requested, otherwise the data registers will remain locked against updates. + // Skipping the Y axis if X and Z are needed actually requires an additional byte of comms. + // Starting partway through the axes does save you traffic. + uint8_t start, dest; + if (this->heading_sensor_ != nullptr || this->x_sensor_ != nullptr) { + start = QMC5883L_REGISTER_DATA_X_LSB; + dest = 0; + } else if (this->y_sensor_ != nullptr) { + start = QMC5883L_REGISTER_DATA_Y_LSB; + dest = 1; + } else { + start = QMC5883L_REGISTER_DATA_Z_LSB; + dest = 2; + } + err = this->read_bytes_16_le_(start, &raw[dest], 3 - dest); + if (err != i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("mag read failed (%d)", err).c_str()); return; } @@ -107,17 +130,18 @@ void QMC5883LComponent::update() { } // in µT - const float x = int16_t(raw_x) * mg_per_bit * 0.1f; - const float y = int16_t(raw_y) * mg_per_bit * 0.1f; - const float z = int16_t(raw_z) * mg_per_bit * 0.1f; + const float x = int16_t(raw[0]) * mg_per_bit * 0.1f; + const float y = int16_t(raw[1]) * mg_per_bit * 0.1f; + const float z = int16_t(raw[2]) * mg_per_bit * 0.1f; float heading = atan2f(0.0f - x, y) * 180.0f / M_PI; float temp = NAN; if (this->temperature_sensor_ != nullptr) { uint16_t raw_temp; - if (!this->read_byte_16_(QMC5883L_REGISTER_TEMPERATURE_LSB, &raw_temp)) { - this->status_set_warning(); + err = this->read_bytes_16_le_(QMC5883L_REGISTER_TEMPERATURE_LSB, &raw_temp); + if (err != i2c::ERROR_OK) { + this->status_set_warning(str_sprintf("temp read failed (%d)", err).c_str()); return; } temp = int16_t(raw_temp) * 0.01f; @@ -138,11 +162,13 @@ void QMC5883LComponent::update() { this->temperature_sensor_->publish_state(temp); } -bool QMC5883LComponent::read_byte_16_(uint8_t a_register, uint16_t *data) { - if (!this->read_byte_16(a_register, data)) - return false; - *data = (*data & 0x00FF) << 8 | (*data & 0xFF00) >> 8; // Flip Byte order, LSB first; - return true; +i2c::ErrorCode QMC5883LComponent::read_bytes_16_le_(uint8_t a_register, uint16_t *data, uint8_t len) { + i2c::ErrorCode err = this->read_register(a_register, reinterpret_cast(data), len * 2); + if (err != i2c::ERROR_OK) + return err; + for (size_t i = 0; i < len; i++) + data[i] = convert_little_endian(data[i]); + return err; } } // namespace qmc5883l diff --git a/esphome/components/qmc5883l/qmc5883l.h b/esphome/components/qmc5883l/qmc5883l.h index dd2008d453..3202e37780 100644 --- a/esphome/components/qmc5883l/qmc5883l.h +++ b/esphome/components/qmc5883l/qmc5883l.h @@ -55,7 +55,7 @@ class QMC5883LComponent : public PollingComponent, public i2c::I2CDevice { NONE = 0, COMMUNICATION_FAILED, } error_code_; - bool read_byte_16_(uint8_t a_register, uint16_t *data); + i2c::ErrorCode read_bytes_16_le_(uint8_t a_register, uint16_t *data, uint8_t len = 1); HighFrequencyLoopRequester high_freq_; }; From 440080a753ffeed63616f3c3601acd911f239e56 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:09:29 +1300 Subject: [PATCH 0776/1052] [display] Fix strftime overload ignoring alignment (#7937) --- esphome/components/display/display.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 1d996bd59b..f00c2936a8 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -1,6 +1,6 @@ #include "display.h" -#include "display_color_utils.h" #include +#include "display_color_utils.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" @@ -670,7 +670,7 @@ void Display::strftime(int x, int y, BaseFont *font, Color color, Color backgrou this->print(x, y, font, color, align, buffer, background); } void Display::strftime(int x, int y, BaseFont *font, Color color, TextAlign align, const char *format, ESPTime time) { - this->strftime(x, y, font, color, COLOR_OFF, TextAlign::TOP_LEFT, format, time); + this->strftime(x, y, font, color, COLOR_OFF, align, format, time); } void Display::strftime(int x, int y, BaseFont *font, Color color, const char *format, ESPTime time) { this->strftime(x, y, font, color, COLOR_OFF, TextAlign::TOP_LEFT, format, time); From 132a096ae75c39e6d882b616b79bffd0cc50ea69 Mon Sep 17 00:00:00 2001 From: Yoonji Park Date: Mon, 9 Dec 2024 20:13:21 +0900 Subject: [PATCH 0777/1052] Add font anti-aliasing for grayscale display (#7934) --- esphome/components/font/font.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/font/font.cpp b/esphome/components/font/font.cpp index aeca0f5cc0..8c4cba34b3 100644 --- a/esphome/components/font/font.cpp +++ b/esphome/components/font/font.cpp @@ -133,9 +133,11 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo auto diff_r = (float) color.r - (float) background.r; auto diff_g = (float) color.g - (float) background.g; auto diff_b = (float) color.b - (float) background.b; + auto diff_w = (float) color.w - (float) background.w; auto b_r = (float) background.r; auto b_g = (float) background.g; - auto b_b = (float) background.g; + auto b_b = (float) background.b; + auto b_w = (float) background.w; for (int glyph_y = y_start + scan_y1; glyph_y != max_y; glyph_y++) { for (int glyph_x = x_at + scan_x1; glyph_x != max_x; glyph_x++) { uint8_t pixel = 0; @@ -153,8 +155,8 @@ void Font::print(int x_start, int y_start, display::Display *display, Color colo display->draw_pixel_at(glyph_x, glyph_y, color); } else if (pixel != 0) { auto on = (float) pixel / (float) bpp_max; - auto blended = - Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g), (uint8_t) (diff_b * on + b_b)); + auto blended = Color((uint8_t) (diff_r * on + b_r), (uint8_t) (diff_g * on + b_g), + (uint8_t) (diff_b * on + b_b), (uint8_t) (diff_w * on + b_w)); display->draw_pixel_at(glyph_x, glyph_y, blended); } } From 14eac3dbce15acf6857c68db1e931130ae6bd462 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:44:39 +0100 Subject: [PATCH 0778/1052] Bump pypa/gh-action-pypi-publish from 1.12.2 to 1.12.3 (#7941) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 096b00f0f1..a4e4305207 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.12.2 + uses: pypa/gh-action-pypi-publish@v1.12.3 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From 437b236a4d5b7e2eb39a66b828b8dcfc1d51d162 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Tue, 10 Dec 2024 01:38:45 +0100 Subject: [PATCH 0779/1052] [adc] Split files by platform (#7940) --- esphome/components/adc/adc_sensor.h | 15 +- esphome/components/adc/adc_sensor_common.cpp | 24 +++ .../{adc_sensor.cpp => adc_sensor_esp32.cpp} | 189 +----------------- esphome/components/adc/adc_sensor_esp8266.cpp | 58 ++++++ esphome/components/adc/adc_sensor_rp2040.cpp | 93 +++++++++ 5 files changed, 192 insertions(+), 187 deletions(-) create mode 100644 esphome/components/adc/adc_sensor_common.cpp rename esphome/components/adc/{adc_sensor.cpp => adc_sensor_esp32.cpp} (53%) create mode 100644 esphome/components/adc/adc_sensor_esp8266.cpp create mode 100644 esphome/components/adc/adc_sensor_rp2040.cpp diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index b697d6dd7e..7a3e1c8da7 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -3,13 +3,12 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" #include "esphome/core/component.h" -#include "esphome/core/defines.h" #include "esphome/core/hal.h" #ifdef USE_ESP32 #include #include "driver/adc.h" -#endif +#endif // USE_ESP32 namespace esphome { namespace adc { @@ -43,7 +42,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage this->channel1_ = ADC1_CHANNEL_MAX; } void set_autorange(bool autorange) { this->autorange_ = autorange; } -#endif +#endif // USE_ESP32 /// Update ADC values void update() override; @@ -59,11 +58,11 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage #ifdef USE_ESP8266 std::string unique_id() override; -#endif +#endif // USE_ESP8266 #ifdef USE_RP2040 void set_is_temperature() { this->is_temperature_ = true; } -#endif +#endif // USE_RP2040 protected: InternalGPIOPin *pin_; @@ -72,7 +71,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage #ifdef USE_RP2040 bool is_temperature_{false}; -#endif +#endif // USE_RP2040 #ifdef USE_ESP32 adc_atten_t attenuation_{ADC_ATTEN_DB_0}; @@ -83,8 +82,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {}; #else esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {}; -#endif -#endif +#endif // ESP_IDF_VERSION_MAJOR +#endif // USE_ESP32 }; } // namespace adc diff --git a/esphome/components/adc/adc_sensor_common.cpp b/esphome/components/adc/adc_sensor_common.cpp new file mode 100644 index 0000000000..2dccd55fcd --- /dev/null +++ b/esphome/components/adc/adc_sensor_common.cpp @@ -0,0 +1,24 @@ +#include "adc_sensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace adc { + +static const char *const TAG = "adc.common"; + +void ADCSensor::update() { + float value_v = this->sample(); + ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v); + this->publish_state(value_v); +} + +void ADCSensor::set_sample_count(uint8_t sample_count) { + if (sample_count != 0) { + this->sample_count_ = sample_count; + } +} + +float ADCSensor::get_setup_priority() const { return setup_priority::DATA; } + +} // namespace adc +} // namespace esphome diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor_esp32.cpp similarity index 53% rename from esphome/components/adc/adc_sensor.cpp rename to esphome/components/adc/adc_sensor_esp32.cpp index 7257793016..24e3750091 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor_esp32.cpp @@ -1,30 +1,13 @@ +#ifdef USE_ESP32 + #include "adc_sensor.h" -#include "esphome/core/helpers.h" #include "esphome/core/log.h" -#ifdef USE_ESP8266 -#ifdef USE_ADC_SENSOR_VCC -#include -ADC_MODE(ADC_VCC) -#else -#include -#endif -#endif - -#ifdef USE_RP2040 -#ifdef CYW43_USES_VSYS_PIN -#include "pico/cyw43_arch.h" -#endif -#include -#endif - namespace esphome { namespace adc { -static const char *const TAG = "adc"; +static const char *const TAG = "adc.esp32"; -// 13-bit for S2, 12-bit for all other ESP32 variants -#ifdef USE_ESP32 static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast(ADC_WIDTH_MAX - 1); #ifndef SOC_ADC_RTC_MAX_BITWIDTH @@ -32,24 +15,15 @@ static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast> 1; // 2048 (12 bit) or 4096 (13 bit) -#endif +static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; +static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; -#ifdef USE_RP2040 -extern "C" -#endif - void - ADCSensor::setup() { +void ADCSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); -#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040) - this->pin_->setup(); -#endif -#ifdef USE_ESP32 if (this->channel1_ != ADC1_CHANNEL_MAX) { adc1_config_width(ADC_WIDTH_MAX_SOC_BITS); if (!this->autorange_) { @@ -61,7 +35,6 @@ extern "C" } } - // load characteristics for each attenuation for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) { auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2; auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS, @@ -79,31 +52,10 @@ extern "C" break; } } - -#endif // USE_ESP32 - -#ifdef USE_RP2040 - static bool initialized = false; - if (!initialized) { - adc_init(); - initialized = true; - } -#endif - - ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str()); } void ADCSensor::dump_config() { LOG_SENSOR("", "ADC Sensor", this); -#if defined(USE_ESP8266) || defined(USE_LIBRETINY) -#ifdef USE_ADC_SENSOR_VCC - ESP_LOGCONFIG(TAG, " Pin: VCC"); -#else - LOG_PIN(" Pin: ", this->pin_); -#endif -#endif // USE_ESP8266 || USE_LIBRETINY - -#ifdef USE_ESP32 LOG_PIN(" Pin: ", this->pin_); if (this->autorange_) { ESP_LOGCONFIG(TAG, " Attenuation: auto"); @@ -125,55 +77,10 @@ void ADCSensor::dump_config() { break; } } -#endif // USE_ESP32 - -#ifdef USE_RP2040 - if (this->is_temperature_) { - ESP_LOGCONFIG(TAG, " Pin: Temperature"); - } else { -#ifdef USE_ADC_SENSOR_VCC - ESP_LOGCONFIG(TAG, " Pin: VCC"); -#else - LOG_PIN(" Pin: ", this->pin_); -#endif // USE_ADC_SENSOR_VCC - } -#endif // USE_RP2040 ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); LOG_UPDATE_INTERVAL(this); } -float ADCSensor::get_setup_priority() const { return setup_priority::DATA; } -void ADCSensor::update() { - float value_v = this->sample(); - ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v); - this->publish_state(value_v); -} - -void ADCSensor::set_sample_count(uint8_t sample_count) { - if (sample_count != 0) { - this->sample_count_ = sample_count; - } -} - -#ifdef USE_ESP8266 -float ADCSensor::sample() { - uint32_t raw = 0; - for (uint8_t sample = 0; sample < this->sample_count_; sample++) { -#ifdef USE_ADC_SENSOR_VCC - raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance) -#else - raw += analogRead(this->pin_->get_pin()); // NOLINT -#endif - } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) - if (this->output_raw_) { - return raw; - } - return raw / 1024.0f; -} -#endif - -#ifdef USE_ESP32 float ADCSensor::sample() { if (!this->autorange_) { uint32_t sum = 0; @@ -240,93 +147,17 @@ float ADCSensor::sample() { uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]); uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]); - // Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC) uint32_t c12 = std::min(raw12, ADC_HALF); uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF); uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF); - // max theoretical csum value is 4096*4 = 16384 uint32_t csum = c12 + c6 + c2 + c0; - // each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32 uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0); return mv_scaled / (float) (csum * 1000U); } -#endif // USE_ESP32 - -#ifdef USE_RP2040 -float ADCSensor::sample() { - if (this->is_temperature_) { - adc_set_temp_sensor_enabled(true); - delay(1); - adc_select_input(4); - uint32_t raw = 0; - for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += adc_read(); - } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) - adc_set_temp_sensor_enabled(false); - if (this->output_raw_) { - return raw; - } - return raw * 3.3f / 4096.0f; - } else { - uint8_t pin = this->pin_->get_pin(); -#ifdef CYW43_USES_VSYS_PIN - if (pin == PICO_VSYS_PIN) { - // Measuring VSYS on Raspberry Pico W needs to be wrapped with - // `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in - // https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and - // VSYS ADC both share GPIO29 - cyw43_thread_enter(); - } -#endif // CYW43_USES_VSYS_PIN - - adc_gpio_init(pin); - adc_select_input(pin - 26); - - uint32_t raw = 0; - for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += adc_read(); - } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) - -#ifdef CYW43_USES_VSYS_PIN - if (pin == PICO_VSYS_PIN) { - cyw43_thread_exit(); - } -#endif // CYW43_USES_VSYS_PIN - - if (this->output_raw_) { - return raw; - } - float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0; - return raw * 3.3f / 4096.0f * coeff; - } -} -#endif - -#ifdef USE_LIBRETINY -float ADCSensor::sample() { - uint32_t raw = 0; - if (this->output_raw_) { - for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += analogRead(this->pin_->get_pin()); // NOLINT - } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) - return raw; - } - for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT - } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) - return raw / 1000.0f; -} -#endif // USE_LIBRETINY - -#ifdef USE_ESP8266 -std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } -#endif } // namespace adc } // namespace esphome + +#endif // USE_ESP32 diff --git a/esphome/components/adc/adc_sensor_esp8266.cpp b/esphome/components/adc/adc_sensor_esp8266.cpp new file mode 100644 index 0000000000..c9b6f8b652 --- /dev/null +++ b/esphome/components/adc/adc_sensor_esp8266.cpp @@ -0,0 +1,58 @@ +#ifdef USE_ESP8266 + +#include "adc_sensor.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#ifdef USE_ADC_SENSOR_VCC +#include +ADC_MODE(ADC_VCC) +#else +#include +#endif // USE_ADC_SENSOR_VCC + +namespace esphome { +namespace adc { + +static const char *const TAG = "adc.esp8266"; + +void ADCSensor::setup() { + ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); +#ifndef USE_ADC_SENSOR_VCC + this->pin_->setup(); +#endif +} + +void ADCSensor::dump_config() { + LOG_SENSOR("", "ADC Sensor", this); +#ifdef USE_ADC_SENSOR_VCC + ESP_LOGCONFIG(TAG, " Pin: VCC"); +#else + LOG_PIN(" Pin: ", this->pin_); +#endif // USE_ADC_SENSOR_VCC + ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); + LOG_UPDATE_INTERVAL(this); +} + +float ADCSensor::sample() { + uint32_t raw = 0; + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { +#ifdef USE_ADC_SENSOR_VCC + raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance) +#else + raw += analogRead(this->pin_->get_pin()); // NOLINT +#endif // USE_ADC_SENSOR_VCC + } + raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + if (this->output_raw_) { + return raw; + } + return raw / 1024.0f; +} + +std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } + +} // namespace adc +} // namespace esphome + +#endif // USE_ESP8266 diff --git a/esphome/components/adc/adc_sensor_rp2040.cpp b/esphome/components/adc/adc_sensor_rp2040.cpp new file mode 100644 index 0000000000..520ff3bacc --- /dev/null +++ b/esphome/components/adc/adc_sensor_rp2040.cpp @@ -0,0 +1,93 @@ +#ifdef USE_RP2040 + +#include "adc_sensor.h" +#include "esphome/core/log.h" + +#ifdef CYW43_USES_VSYS_PIN +#include "pico/cyw43_arch.h" +#endif // CYW43_USES_VSYS_PIN +#include + +namespace esphome { +namespace adc { + +static const char *const TAG = "adc.rp2040"; + +void ADCSensor::setup() { + ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); + static bool initialized = false; + if (!initialized) { + adc_init(); + initialized = true; + } +} + +void ADCSensor::dump_config() { + LOG_SENSOR("", "ADC Sensor", this); + if (this->is_temperature_) { + ESP_LOGCONFIG(TAG, " Pin: Temperature"); + } else { +#ifdef USE_ADC_SENSOR_VCC + ESP_LOGCONFIG(TAG, " Pin: VCC"); +#else + LOG_PIN(" Pin: ", this->pin_); +#endif // USE_ADC_SENSOR_VCC + } + ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); + LOG_UPDATE_INTERVAL(this); +} + +float ADCSensor::sample() { + if (this->is_temperature_) { + adc_set_temp_sensor_enabled(true); + delay(1); + adc_select_input(4); + uint32_t raw = 0; + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + raw += adc_read(); + } + raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + adc_set_temp_sensor_enabled(false); + if (this->output_raw_) { + return raw; + } + return raw * 3.3f / 4096.0f; + } + + uint8_t pin = this->pin_->get_pin(); +#ifdef CYW43_USES_VSYS_PIN + if (pin == PICO_VSYS_PIN) { + // Measuring VSYS on Raspberry Pico W needs to be wrapped with + // `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in + // https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and + // VSYS ADC both share GPIO29 + cyw43_thread_enter(); + } +#endif // CYW43_USES_VSYS_PIN + + adc_gpio_init(pin); + adc_select_input(pin - 26); + + uint32_t raw = 0; + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + raw += adc_read(); + } + raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + +#ifdef CYW43_USES_VSYS_PIN + if (pin == PICO_VSYS_PIN) { + cyw43_thread_exit(); + } +#endif // CYW43_USES_VSYS_PIN + + if (this->output_raw_) { + return raw; + } + float coeff = pin == PICO_VSYS_PIN ? 3.0f : 1.0f; + return raw * 3.3f / 4096.0f * coeff; +} + +} // namespace adc +} // namespace esphome + +#endif // USE_RP2040 From 5a92e2466238fa2627e9a16a1e29ec8d41375c92 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:22:30 +1300 Subject: [PATCH 0780/1052] [const] Move ``CONF_TEMPERATURE_COMPENSATION`` to common const.py (#7943) --- esphome/components/sen5x/sensor.py | 11 +++++------ esphome/components/ufire_ec/sensor.py | 8 ++++---- esphome/const.py | 1 + 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/esphome/components/sen5x/sensor.py b/esphome/components/sen5x/sensor.py index 67bd627f7f..a8a796853e 100644 --- a/esphome/components/sen5x/sensor.py +++ b/esphome/components/sen5x/sensor.py @@ -1,19 +1,19 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import i2c, sensor, sensirion_common from esphome import automation from esphome.automation import maybe_simple_id - +import esphome.codegen as cg +from esphome.components import i2c, sensirion_common, sensor +import esphome.config_validation as cv from esphome.const import ( CONF_HUMIDITY, CONF_ID, CONF_OFFSET, CONF_PM_1_0, - CONF_PM_10_0, CONF_PM_2_5, CONF_PM_4_0, + CONF_PM_10_0, CONF_STORE_BASELINE, CONF_TEMPERATURE, + CONF_TEMPERATURE_COMPENSATION, DEVICE_CLASS_AQI, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_PM1, @@ -51,7 +51,6 @@ CONF_LEARNING_TIME_OFFSET_HOURS = "learning_time_offset_hours" CONF_NORMALIZED_OFFSET_SLOPE = "normalized_offset_slope" CONF_NOX = "nox" CONF_STD_INITIAL = "std_initial" -CONF_TEMPERATURE_COMPENSATION = "temperature_compensation" CONF_TIME_CONSTANT = "time_constant" CONF_VOC = "voc" CONF_VOC_BASELINE = "voc_baseline" diff --git a/esphome/components/ufire_ec/sensor.py b/esphome/components/ufire_ec/sensor.py index 9602d0c2d0..944fdfdee9 100644 --- a/esphome/components/ufire_ec/sensor.py +++ b/esphome/components/ufire_ec/sensor.py @@ -1,11 +1,12 @@ -import esphome.codegen as cg from esphome import automation -import esphome.config_validation as cv +import esphome.codegen as cg from esphome.components import i2c, sensor +import esphome.config_validation as cv from esphome.const import ( - CONF_ID, CONF_EC, + CONF_ID, CONF_TEMPERATURE, + CONF_TEMPERATURE_COMPENSATION, DEVICE_CLASS_EMPTY, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, @@ -18,7 +19,6 @@ DEPENDENCIES = ["i2c"] CONF_SOLUTION = "solution" CONF_TEMPERATURE_SENSOR = "temperature_sensor" -CONF_TEMPERATURE_COMPENSATION = "temperature_compensation" CONF_TEMPERATURE_COEFFICIENT = "temperature_coefficient" ufire_ec_ns = cg.esphome_ns.namespace("ufire_ec") diff --git a/esphome/const.py b/esphome/const.py index b9397aa1bd..4d3818cb23 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -864,6 +864,7 @@ CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC = "target_temperature_low_command_topi CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC = "target_temperature_low_state_topic" CONF_TARGET_TEMPERATURE_STATE_TOPIC = "target_temperature_state_topic" CONF_TEMPERATURE = "temperature" +CONF_TEMPERATURE_COMPENSATION = "temperature_compensation" CONF_TEMPERATURE_OFFSET = "temperature_offset" CONF_TEMPERATURE_SOURCE = "temperature_source" CONF_TEMPERATURE_STEP = "temperature_step" From 517f659da86e111e5c203564aa91f54471e3ceca Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:23:30 +1100 Subject: [PATCH 0781/1052] [lvgl] Fix image `mode` property (Bugfix) (#7938) --- esphome/components/lvgl/widgets/img.py | 2 +- tests/components/lvgl/lvgl-package.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/lvgl/widgets/img.py b/esphome/components/lvgl/widgets/img.py index 59b2c97c63..46077190d0 100644 --- a/esphome/components/lvgl/widgets/img.py +++ b/esphome/components/lvgl/widgets/img.py @@ -79,7 +79,7 @@ class ImgType(WidgetType): if CONF_ANTIALIAS in config: lv.img_set_antialias(w.obj, config[CONF_ANTIALIAS]) if mode := config.get(CONF_MODE): - lv.img_set_mode(w.obj, mode) + await w.set_property("size_mode", mode) img_spec = ImgType() diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 4b7e13db91..556ce105ee 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -451,6 +451,7 @@ lvgl: src: cat_image align: top_left y: "50" + mode: real - tileview: id: tileview_id scrollbar_mode: active @@ -647,6 +648,7 @@ lvgl: grid_cell_row_pos: 0 grid_cell_column_pos: 0 src: !lambda return dog_image; + mode: virtual on_click: then: - lvgl.tabview.select: From bb27eaaf1e58316c6124f2d16a1fba6b0a033141 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:25:29 +1100 Subject: [PATCH 0782/1052] [lvgl] Add `on_change` event (#7939) --- esphome/components/lvgl/defines.py | 1 + tests/components/lvgl/lvgl-package.yaml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 5371f110a6..02323f9655 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -168,6 +168,7 @@ LV_EVENT_MAP = { "READY": "READY", "CANCEL": "CANCEL", "ALL_EVENTS": "ALL", + "CHANGE": "VALUE_CHANGED", } LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 556ce105ee..b1b89adfe0 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -165,6 +165,11 @@ lvgl: - Nov - Dec selected_index: 1 + on_change: + then: + - logger.log: + format: "Roller changed = %d: %s" + args: [x, text.c_str()] on_value: then: - logger.log: From 444e162c92c3148a14493f66d3de256679a6eedf Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:39:00 +1300 Subject: [PATCH 0783/1052] Synchronise esp32 boards with platform version 51.03.07 (#7945) --- esphome/components/esp32/boards.py | 236 +++++++++++++++++++++++++++-- 1 file changed, 222 insertions(+), 14 deletions(-) diff --git a/esphome/components/esp32/boards.py b/esphome/components/esp32/boards.py index 02744ecb6f..81400eb9c3 100644 --- a/esphome/components/esp32/boards.py +++ b/esphome/components/esp32/boards.py @@ -1,4 +1,12 @@ -from .const import VARIANT_ESP32, VARIANT_ESP32C3, VARIANT_ESP32S2, VARIANT_ESP32S3 +from .const import ( + VARIANT_ESP32, + VARIANT_ESP32C2, + VARIANT_ESP32C3, + VARIANT_ESP32C6, + VARIANT_ESP32H2, + VARIANT_ESP32S2, + VARIANT_ESP32S3, +) ESP32_BASE_PINS = { "TX": 1, @@ -1344,6 +1352,26 @@ done | sort """ BOARDS = { + "4d_systems_esp32s3_gen4_r8n16": { + "name": "4D Systems GEN4-ESP32 16MB (ESP32S3-R8N16)", + "variant": VARIANT_ESP32S3, + }, + "adafruit_camera_esp32s3": { + "name": "Adafruit pyCamera S3", + "variant": VARIANT_ESP32S3, + }, + "adafruit_feather_esp32c6": { + "name": "Adafruit Feather ESP32-C6", + "variant": VARIANT_ESP32C6, + }, + "adafruit_feather_esp32s2": { + "name": "Adafruit Feather ESP32-S2", + "variant": VARIANT_ESP32S2, + }, + "adafruit_feather_esp32s2_reversetft": { + "name": "Adafruit Feather ESP32-S2 Reverse TFT", + "variant": VARIANT_ESP32S2, + }, "adafruit_feather_esp32s2_tft": { "name": "Adafruit Feather ESP32-S2 TFT", "variant": VARIANT_ESP32S2, @@ -1356,6 +1384,10 @@ BOARDS = { "name": "Adafruit Feather ESP32-S3 No PSRAM", "variant": VARIANT_ESP32S3, }, + "adafruit_feather_esp32s3_reversetft": { + "name": "Adafruit Feather ESP32-S3 Reverse TFT", + "variant": VARIANT_ESP32S3, + }, "adafruit_feather_esp32s3_tft": { "name": "Adafruit Feather ESP32-S3 TFT", "variant": VARIANT_ESP32S3, @@ -1376,10 +1408,18 @@ BOARDS = { "name": "Adafruit MagTag 2.9", "variant": VARIANT_ESP32S2, }, + "adafruit_matrixportal_esp32s3": { + "name": "Adafruit MatrixPortal ESP32-S3", + "variant": VARIANT_ESP32S3, + }, "adafruit_metro_esp32s2": { "name": "Adafruit Metro ESP32-S2", "variant": VARIANT_ESP32S2, }, + "adafruit_metro_esp32s3": { + "name": "Adafruit Metro ESP32-S3", + "variant": VARIANT_ESP32S3, + }, "adafruit_qtpy_esp32c3": { "name": "Adafruit QT Py ESP32-C3", "variant": VARIANT_ESP32C3, @@ -1392,10 +1432,18 @@ BOARDS = { "name": "Adafruit QT Py ESP32-S2", "variant": VARIANT_ESP32S2, }, + "adafruit_qtpy_esp32s3_n4r2": { + "name": "Adafruit QT Py ESP32-S3 (4M Flash 2M PSRAM)", + "variant": VARIANT_ESP32S3, + }, "adafruit_qtpy_esp32s3_nopsram": { "name": "Adafruit QT Py ESP32-S3 No PSRAM", "variant": VARIANT_ESP32S3, }, + "adafruit_qualia_s3_rgb666": { + "name": "Adafruit Qualia ESP32-S3 RGB666", + "variant": VARIANT_ESP32S3, + }, "airm2m_core_esp32c3": { "name": "AirM2M CORE ESP32C3", "variant": VARIANT_ESP32C3, @@ -1404,14 +1452,30 @@ BOARDS = { "name": "ALKS ESP32", "variant": VARIANT_ESP32, }, + "arduino_nano_esp32": { + "name": "Arduino Nano ESP32", + "variant": VARIANT_ESP32S3, + }, + "atd147_s3": { + "name": "ArtronShop ATD1.47-S3", + "variant": VARIANT_ESP32S3, + }, "atmegazero_esp32s2": { "name": "EspinalLab ATMegaZero ESP32-S2", "variant": VARIANT_ESP32S2, }, + "aventen_s3_sync": { + "name": "Aventen S3 Sync", + "variant": VARIANT_ESP32S3, + }, "az-delivery-devkit-v4": { "name": "AZ-Delivery ESP-32 Dev Kit C V4", "variant": VARIANT_ESP32, }, + "bee_data_logger": { + "name": "Smart Bee Data Logger", + "variant": VARIANT_ESP32S3, + }, "bee_motion_mini": { "name": "Smart Bee Motion Mini", "variant": VARIANT_ESP32C3, @@ -1436,14 +1500,6 @@ BOARDS = { "name": "BPI-Leaf-S3", "variant": VARIANT_ESP32S3, }, - "briki_abc_esp32": { - "name": "Briki ABC (MBC-WB) - ESP32", - "variant": VARIANT_ESP32, - }, - "briki_mbc-wb_esp32": { - "name": "Briki MBC-WB - ESP32", - "variant": VARIANT_ESP32, - }, "cnrs_aw2eth": { "name": "CNRS AW2ETH", "variant": VARIANT_ESP32, @@ -1496,18 +1552,38 @@ BOARDS = { "name": "DFRobot Beetle ESP32-C3", "variant": VARIANT_ESP32C3, }, + "dfrobot_firebeetle2_esp32e": { + "name": "DFRobot Firebeetle 2 ESP32-E", + "variant": VARIANT_ESP32, + }, "dfrobot_firebeetle2_esp32s3": { "name": "DFRobot Firebeetle 2 ESP32-S3", "variant": VARIANT_ESP32S3, }, + "dfrobot_romeo_esp32s3": { + "name": "DFRobot Romeo ESP32-S3", + "variant": VARIANT_ESP32S3, + }, "dpu_esp32": { "name": "TAMC DPU ESP32", "variant": VARIANT_ESP32, }, + "edgebox-esp-100": { + "name": "Seeed Studio Edgebox-ESP-100", + "variant": VARIANT_ESP32S3, + }, "esp320": { "name": "Electronic SweetPeas ESP320", "variant": VARIANT_ESP32, }, + "esp32-c2-devkitm-1": { + "name": "Espressif ESP32-C2-DevKitM-1", + "variant": VARIANT_ESP32C2, + }, + "esp32-c3-devkitc-02": { + "name": "Espressif ESP32-C3-DevKitC-02", + "variant": VARIANT_ESP32C3, + }, "esp32-c3-devkitm-1": { "name": "Espressif ESP32-C3-DevKitM-1", "variant": VARIANT_ESP32C3, @@ -1516,6 +1592,14 @@ BOARDS = { "name": "Ai-Thinker ESP-C3-M1-I-Kit", "variant": VARIANT_ESP32C3, }, + "esp32-c6-devkitc-1": { + "name": "Espressif ESP32-C6-DevKitC-1", + "variant": VARIANT_ESP32C6, + }, + "esp32-c6-devkitm-1": { + "name": "Espressif ESP32-C6-DevKitM-1", + "variant": VARIANT_ESP32C6, + }, "esp32cam": { "name": "AI Thinker ESP32-CAM", "variant": VARIANT_ESP32, @@ -1544,6 +1628,14 @@ BOARDS = { "name": "OLIMEX ESP32-GATEWAY", "variant": VARIANT_ESP32, }, + "esp32-h2-devkitm-1": { + "name": "Espressif ESP32-H2-DevKit", + "variant": VARIANT_ESP32H2, + }, + "esp32-pico-devkitm-2": { + "name": "Espressif ESP32-PICO-DevKitM-2", + "variant": VARIANT_ESP32, + }, "esp32-poe-iso": { "name": "OLIMEX ESP32-PoE-ISO", "variant": VARIANT_ESP32, @@ -1580,10 +1672,22 @@ BOARDS = { "name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)", "variant": VARIANT_ESP32S3, }, - "esp32-s3-korvo-2": { - "name": "Espressif ESP32-S3-Korvo-2", + "esp32-s3-devkitm-1": { + "name": "Espressif ESP32-S3-DevKitM-1", "variant": VARIANT_ESP32S3, }, + "esp32s3_powerfeather": { + "name": "ESP32-S3 PowerFeather", + "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, @@ -1652,9 +1756,9 @@ BOARDS = { "name": "Heltec WiFi Kit 32", "variant": VARIANT_ESP32, }, - "heltec_wifi_kit_32_v2": { - "name": "Heltec WiFi Kit 32 (V2)", - "variant": VARIANT_ESP32, + "heltec_wifi_kit_32_V3": { + "name": "Heltec WiFi Kit 32 (V3)", + "variant": VARIANT_ESP32S3, }, "heltec_wifi_lora_32": { "name": "Heltec WiFi LoRa 32", @@ -1664,6 +1768,10 @@ BOARDS = { "name": "Heltec WiFi LoRa 32 (V2)", "variant": VARIANT_ESP32, }, + "heltec_wifi_lora_32_V3": { + "name": "Heltec WiFi LoRa 32 (V3)", + "variant": VARIANT_ESP32S3, + }, "heltec_wireless_stick_lite": { "name": "Heltec Wireless Stick Lite", "variant": VARIANT_ESP32, @@ -1708,6 +1816,14 @@ BOARDS = { "name": "oddWires IoT-Bus Proteus", "variant": VARIANT_ESP32, }, + "ioxesp32": { + "name": "ArtronShop IOXESP32", + "variant": VARIANT_ESP32, + }, + "ioxesp32ps": { + "name": "ArtronShop IOXESP32PS", + "variant": VARIANT_ESP32, + }, "kb32-ft": { "name": "MakerAsia KB32-FT", "variant": VARIANT_ESP32, @@ -1720,10 +1836,26 @@ BOARDS = { "name": "Labplus mPython", "variant": VARIANT_ESP32, }, + "lilka_v2": { + "name": "Lilka v2", + "variant": VARIANT_ESP32S3, + }, + "lilygo-t-display": { + "name": "LilyGo T-Display", + "variant": VARIANT_ESP32, + }, + "lilygo-t-display-s3": { + "name": "LilyGo T-Display-S3", + "variant": VARIANT_ESP32S3, + }, "lionbit": { "name": "Lion:Bit Dev Board", "variant": VARIANT_ESP32, }, + "lionbits3": { + "name": "Lion:Bit S3 STEM Dev Board", + "variant": VARIANT_ESP32S3, + }, "lolin32_lite": { "name": "WEMOS LOLIN32 Lite", "variant": VARIANT_ESP32, @@ -1752,10 +1884,18 @@ BOARDS = { "name": "WEMOS LOLIN S2 PICO", "variant": VARIANT_ESP32S2, }, + "lolin_s3_mini": { + "name": "WEMOS LOLIN S3 Mini", + "variant": VARIANT_ESP32S3, + }, "lolin_s3": { "name": "WEMOS LOLIN S3", "variant": VARIANT_ESP32S3, }, + "lolin_s3_pro": { + "name": "WEMOS LOLIN S3 PRO", + "variant": VARIANT_ESP32S3, + }, "lopy4": { "name": "Pycom LoPy4", "variant": VARIANT_ESP32, @@ -1768,10 +1908,18 @@ BOARDS = { "name": "M5Stack-ATOM", "variant": VARIANT_ESP32, }, + "m5stack-atoms3": { + "name": "M5Stack AtomS3", + "variant": VARIANT_ESP32S3, + }, "m5stack-core2": { "name": "M5Stack Core2", "variant": VARIANT_ESP32, }, + "m5stack-core-esp32-16M": { + "name": "M5Stack Core ESP32 16M", + "variant": VARIANT_ESP32, + }, "m5stack-core-esp32": { "name": "M5Stack Core ESP32", "variant": VARIANT_ESP32, @@ -1780,6 +1928,10 @@ BOARDS = { "name": "M5Stack-Core Ink", "variant": VARIANT_ESP32, }, + "m5stack-cores3": { + "name": "M5Stack CoreS3", + "variant": VARIANT_ESP32S3, + }, "m5stack-fire": { "name": "M5Stack FIRE", "variant": VARIANT_ESP32, @@ -1788,6 +1940,14 @@ BOARDS = { "name": "M5Stack GREY ESP32", "variant": VARIANT_ESP32, }, + "m5stack_paper": { + "name": "M5Stack Paper", + "variant": VARIANT_ESP32, + }, + "m5stack-stamps3": { + "name": "M5Stack StampS3", + "variant": VARIANT_ESP32S3, + }, "m5stack-station": { "name": "M5Stack Station", "variant": VARIANT_ESP32, @@ -1796,6 +1956,10 @@ BOARDS = { "name": "M5Stack Timer CAM", "variant": VARIANT_ESP32, }, + "m5stamp-pico": { + "name": "M5Stamp-Pico", + "variant": VARIANT_ESP32, + }, "m5stick-c": { "name": "M5Stick-C", "variant": VARIANT_ESP32, @@ -1832,10 +1996,26 @@ BOARDS = { "name": "Deparment of Alchemy MiniMain ESP32-S2", "variant": VARIANT_ESP32S2, }, + "motorgo_mini_1": { + "name": "MotorGo Mini 1 (ESP32-S3)", + "variant": VARIANT_ESP32S3, + }, + "namino_arancio": { + "name": "Namino Arancio", + "variant": VARIANT_ESP32S3, + }, + "namino_rosso": { + "name": "Namino Rosso", + "variant": VARIANT_ESP32S3, + }, "nano32": { "name": "MakerAsia Nano32", "variant": VARIANT_ESP32, }, + "nebulas3": { + "name": "Kinetic Dynamics Nebula S3", + "variant": VARIANT_ESP32S3, + }, "nina_w10": { "name": "u-blox NINA-W10 series", "variant": VARIANT_ESP32, @@ -1896,10 +2076,22 @@ BOARDS = { "name": "Munich Labs RedPill ESP32-S3", "variant": VARIANT_ESP32S3, }, + "roboheart_hercules": { + "name": "RoboHeart Hercules", + "variant": VARIANT_ESP32, + }, "seeed_xiao_esp32c3": { "name": "Seeed Studio XIAO ESP32C3", "variant": VARIANT_ESP32C3, }, + "seeed_xiao_esp32s3": { + "name": "Seeed Studio XIAO ESP32S3", + "variant": VARIANT_ESP32S3, + }, + "sensebox_mcu_esp32s2": { + "name": "senseBox MCU-S2 ESP32-S2", + "variant": VARIANT_ESP32S2, + }, "sensesiot_weizen": { "name": "LOGISENSES Senses Weizen", "variant": VARIANT_ESP32, @@ -1912,6 +2104,10 @@ BOARDS = { "name": "S.ODI Ultra v1", "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, @@ -2004,6 +2200,10 @@ BOARDS = { "name": "Unexpected Maker FeatherS3", "variant": VARIANT_ESP32S3, }, + "um_nanos3": { + "name": "Unexpected Maker NanoS3", + "variant": VARIANT_ESP32S3, + }, "um_pros3": { "name": "Unexpected Maker PROS3", "variant": VARIANT_ESP32S3, @@ -2040,6 +2240,14 @@ BOARDS = { "name": "uPesy ESP32 Wrover DevKit", "variant": VARIANT_ESP32, }, + "valtrack_v4_mfw_esp32_c3": { + "name": "Valetron Systems VALTRACK-V4MVF", + "variant": VARIANT_ESP32C3, + }, + "valtrack_v4_vts_esp32_c3": { + "name": "Valetron Systems VALTRACK-V4VTS", + "variant": VARIANT_ESP32C3, + }, "vintlabs-devkit-v1": { "name": "VintLabs ESP32 Devkit", "variant": VARIANT_ESP32, From 7aa54b6879ceb7bdec3d8b7d6ce8d143a394c1ff Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 10 Dec 2024 23:24:06 +1300 Subject: [PATCH 0784/1052] [i2c] Use correct macro to determine number of i2c peripherals for idf (#7947) --- esphome/components/i2c/i2c_bus_esp_idf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index 3a9c229778..ac3a754024 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -17,14 +17,14 @@ void IDFI2CBus::setup() { ESP_LOGCONFIG(TAG, "Setting up I2C bus..."); static i2c_port_t next_port = I2C_NUM_0; port_ = next_port; -#if I2C_NUM_MAX > 1 +#if SOC_I2C_NUM > 1 next_port = (next_port == I2C_NUM_0) ? I2C_NUM_1 : I2C_NUM_MAX; #else next_port = I2C_NUM_MAX; #endif if (port_ == I2C_NUM_MAX) { - ESP_LOGE(TAG, "Too many I2C buses configured"); + ESP_LOGE(TAG, "Too many I2C buses configured. Max %u supported.", SOC_I2C_NUM); this->mark_failed(); return; } From 5dcaf1241f12df31582546488fe70df78add11e7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:55:29 +1300 Subject: [PATCH 0785/1052] Bump version to 2024.12.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4d3818cb23..4d8d550fd1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.0-dev" +__version__ = "2024.12.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 1dfd15e607f76827f00ddb969bd83d6d65532f8d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 11 Dec 2024 15:55:29 +1300 Subject: [PATCH 0786/1052] Bump version to 2025.1.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4d3818cb23..08cc907fdf 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.0-dev" +__version__ = "2025.1.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From ba63d266d823170fbd19761f97ca6539418795ed Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:37:22 -0500 Subject: [PATCH 0787/1052] [const] Add RMT CONF variables to const.py (#7953) Co-authored-by: Jonathan Swoboda --- esphome/components/remote_receiver/__init__.py | 13 ++++++------- esphome/const.py | 4 ++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index e5085bb33c..d3f61977c6 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -1,24 +1,23 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins -from esphome.components import remote_base, esp32_rmt +import esphome.codegen as cg +from esphome.components import esp32_rmt, remote_base +import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, + CONF_CLOCK_DIVIDER, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, + CONF_MEMORY_BLOCKS, CONF_PIN, + CONF_RMT_CHANNEL, CONF_TOLERANCE, CONF_TYPE, - CONF_MEMORY_BLOCKS, - CONF_RMT_CHANNEL, CONF_VALUE, ) from esphome.core import CORE, TimePeriod -CONF_CLOCK_DIVIDER = "clock_divider" - AUTO_LOAD = ["remote_base"] remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver") remote_base_ns = cg.esphome_ns.namespace("remote_base") diff --git a/esphome/const.py b/esphome/const.py index 08cc907fdf..0f41dc1aec 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -131,7 +131,9 @@ CONF_CLIENT_CERTIFICATE = "client_certificate" CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key" CONF_CLIENT_ID = "client_id" CONF_CLK_PIN = "clk_pin" +CONF_CLOCK_DIVIDER = "clock_divider" CONF_CLOCK_PIN = "clock_pin" +CONF_CLOCK_RESOLUTION = "clock_resolution" CONF_CLOSE_ACTION = "close_action" CONF_CLOSE_DURATION = "close_duration" CONF_CLOSE_ENDSTOP = "close_endstop" @@ -739,6 +741,7 @@ CONF_RGB_ORDER = "rgb_order" CONF_RGBW = "rgbw" CONF_RISING_EDGE = "rising_edge" CONF_RMT_CHANNEL = "rmt_channel" +CONF_RMT_SYMBOLS = "rmt_symbols" CONF_ROTATION = "rotation" CONF_ROW = "row" CONF_RS_PIN = "rs_pin" @@ -918,6 +921,7 @@ CONF_UPDATE_ON_BOOT = "update_on_boot" CONF_URL = "url" CONF_USE_ABBREVIATIONS = "use_abbreviations" CONF_USE_ADDRESS = "use_address" +CONF_USE_DMA = "use_dma" CONF_USE_FAHRENHEIT = "use_fahrenheit" CONF_USERNAME = "username" CONF_UUID = "uuid" From 42bc960a36b32c5bdda326bf3eca80733ae38a6d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:37:51 +1300 Subject: [PATCH 0788/1052] [sgp30] Set default update interval to 60s (#7952) --- esphome/components/sgp30/sensor.py | 19 +++++++++---------- esphome/components/sgp30/sgp30.cpp | 8 ++------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index 13e859cc09..8c92f55ef7 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -1,23 +1,22 @@ import esphome.codegen as cg +from esphome.components import i2c, sensirion_common, sensor import esphome.config_validation as cv -from esphome.components import i2c, sensor, sensirion_common - from esphome.const import ( - CONF_COMPENSATION, - CONF_ID, CONF_BASELINE, + CONF_COMPENSATION, CONF_ECO2, + CONF_ID, CONF_STORE_BASELINE, CONF_TEMPERATURE_SOURCE, CONF_TVOC, - ICON_RADIATOR, DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, - STATE_CLASS_MEASUREMENT, - UNIT_PARTS_PER_MILLION, - UNIT_PARTS_PER_BILLION, - ICON_MOLECULE_CO2, ENTITY_CATEGORY_DIAGNOSTIC, + ICON_MOLECULE_CO2, + ICON_RADIATOR, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_BILLION, + UNIT_PARTS_PER_MILLION, ) DEPENDENCIES = ["i2c"] @@ -77,7 +76,7 @@ CONFIG_SCHEMA = ( ), } ) - .extend(cv.polling_component_schema("1s")) + .extend(cv.polling_component_schema("60s")) .extend(i2c.i2c_device_schema(0x58)) ) diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 261604b992..77e9ef9820 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -1,8 +1,8 @@ #include "sgp30.h" +#include +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" -#include namespace esphome { namespace sgp30 { @@ -295,10 +295,6 @@ void SGP30Component::update() { if (this->tvoc_sensor_ != nullptr) this->tvoc_sensor_->publish_state(tvoc); - if (this->get_update_interval() != 1000) { - ESP_LOGW(TAG, "Update interval for SGP30 sensor must be set to 1s for optimized readout"); - } - this->status_clear_warning(); this->send_env_data_(); this->read_iaq_baseline_(); From c187cb547cd2c7a1223d399ea4c99aec1d386c73 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:45:10 -0500 Subject: [PATCH 0789/1052] [core] Move delay_microseconds_safe to iram (#7957) Co-authored-by: Jonathan Swoboda --- esphome/core/helpers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 4e8caeae99..b11615204e 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -767,7 +767,8 @@ bool mac_address_is_valid(const uint8_t *mac) { return !(is_all_zeros || is_all_ones); } -void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability +void IRAM_ATTR HOT delay_microseconds_safe(uint32_t us) { + // avoids CPU locks that could trigger WDT or affect WiFi/BT stability uint32_t start = micros(); const uint32_t lag = 5000; // microseconds, specifies the maximum time for a CPU busy-loop. From 88742e03995145e572e0feed57c3c22a7ce196fc Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:16:11 +0100 Subject: [PATCH 0790/1052] [rotary_encoder] Fix volatile increment/decrement deprecation warnings (#7958) --- esphome/components/rotary_encoder/rotary_encoder.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index e9a0eac3f5..f8e5357a6e 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -93,13 +93,17 @@ void IRAM_ATTR HOT RotaryEncoderSensorStore::gpio_intr(RotaryEncoderSensorStore int8_t rotation_dir = 0; uint16_t new_state = STATE_LOOKUP_TABLE[input_state]; if ((new_state & arg->resolution & STATE_HAS_INCREMENTED) != 0) { - if (arg->counter < arg->max_value) - arg->counter++; + if (arg->counter < arg->max_value) { + auto x = arg->counter + 1; + arg->counter = x; + } rotation_dir = 1; } if ((new_state & arg->resolution & STATE_HAS_DECREMENTED) != 0) { - if (arg->counter > arg->min_value) - arg->counter--; + if (arg->counter > arg->min_value) { + auto x = arg->counter - 1; + arg->counter = x; + } rotation_dir = -1; } From ce7ff15c8ad9f3dd3ba4110428ffc42ae0f901dd Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:21:54 +0100 Subject: [PATCH 0791/1052] [pulse_counter] Fix volatile increment/decrement deprecation warnings (#7954) --- .../pulse_counter/pulse_counter_sensor.cpp | 23 +++++++++++-------- .../pulse_counter/pulse_counter_sensor.h | 8 +++---- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index bd3e4fcbef..2bc80c352c 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -13,9 +13,9 @@ PulseCounterStorageBase *get_storage(bool hw_pcnt) { return (hw_pcnt ? (PulseCounterStorageBase *) (new HwPulseCounterStorage) : (PulseCounterStorageBase *) (new BasicPulseCounterStorage)); } -#else +#else // HAS_PCNT PulseCounterStorageBase *get_storage(bool) { return new BasicPulseCounterStorage; } -#endif +#endif // HAS_PCNT void IRAM_ATTR BasicPulseCounterStorage::gpio_intr(BasicPulseCounterStorage *arg) { const uint32_t now = micros(); @@ -28,14 +28,17 @@ void IRAM_ATTR BasicPulseCounterStorage::gpio_intr(BasicPulseCounterStorage *arg switch (mode) { case PULSE_COUNTER_DISABLE: break; - case PULSE_COUNTER_INCREMENT: - arg->counter++; - break; - case PULSE_COUNTER_DECREMENT: - arg->counter--; - break; + case PULSE_COUNTER_INCREMENT: { + auto x = arg->counter + 1; + arg->counter = x; + } break; + case PULSE_COUNTER_DECREMENT: { + auto x = arg->counter - 1; + arg->counter = x; + } break; } } + bool BasicPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { this->pin = pin; this->pin->setup(); @@ -43,6 +46,7 @@ bool BasicPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { this->pin->attach_interrupt(BasicPulseCounterStorage::gpio_intr, this, gpio::INTERRUPT_ANY_EDGE); return true; } + pulse_counter_t BasicPulseCounterStorage::read_raw_value() { pulse_counter_t counter = this->counter; pulse_counter_t ret = counter - this->last_value; @@ -141,6 +145,7 @@ bool HwPulseCounterStorage::pulse_counter_setup(InternalGPIOPin *pin) { } return true; } + pulse_counter_t HwPulseCounterStorage::read_raw_value() { pulse_counter_t counter; pcnt_get_counter_value(this->pcnt_unit, &counter); @@ -148,7 +153,7 @@ pulse_counter_t HwPulseCounterStorage::read_raw_value() { this->last_value = counter; return ret; } -#endif +#endif // HAS_PCNT void PulseCounterSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up pulse counter '%s'...", this->name_.c_str()); diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.h b/esphome/components/pulse_counter/pulse_counter_sensor.h index fc3d8711d1..cea9fa7bf9 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.h +++ b/esphome/components/pulse_counter/pulse_counter_sensor.h @@ -9,7 +9,7 @@ #if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) #include #define HAS_PCNT -#endif +#endif // defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) namespace esphome { namespace pulse_counter { @@ -22,9 +22,9 @@ enum PulseCounterCountMode { #ifdef HAS_PCNT using pulse_counter_t = int16_t; -#else +#else // HAS_PCNT using pulse_counter_t = int32_t; -#endif +#endif // HAS_PCNT struct PulseCounterStorageBase { virtual bool pulse_counter_setup(InternalGPIOPin *pin) = 0; @@ -57,7 +57,7 @@ struct HwPulseCounterStorage : public PulseCounterStorageBase { pcnt_unit_t pcnt_unit; pcnt_channel_t pcnt_channel; }; -#endif +#endif // HAS_PCNT PulseCounterStorageBase *get_storage(bool hw_pcnt = false); From 7f2ca800c1312f8b864f17949ab35339a55ac4f6 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Sat, 14 Dec 2024 00:17:58 -0500 Subject: [PATCH 0792/1052] [i2s_audio] Bugfix: Correctly set ring buffer size (#7959) --- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index d2a582c2cc..46f1b00d05 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -247,7 +247,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { // Ensure ring buffer is at least as large as the total size of the DMA buffers const size_t ring_buffer_size = - std::min((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); + std::max((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) { // Failed to allocate buffers From 5877c57a3572138de2873262a0204759a2704149 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 14 Dec 2024 19:55:04 +0100 Subject: [PATCH 0793/1052] [adc] Restore missing LIBRETINY code in a separated file (#7955) --- .../components/adc/adc_sensor_libretiny.cpp | 48 +++++++++++++++++++ tests/components/adc/test.bk72xx-ard.yaml | 4 ++ 2 files changed, 52 insertions(+) create mode 100644 esphome/components/adc/adc_sensor_libretiny.cpp create mode 100644 tests/components/adc/test.bk72xx-ard.yaml diff --git a/esphome/components/adc/adc_sensor_libretiny.cpp b/esphome/components/adc/adc_sensor_libretiny.cpp new file mode 100644 index 0000000000..cd04477b3f --- /dev/null +++ b/esphome/components/adc/adc_sensor_libretiny.cpp @@ -0,0 +1,48 @@ +#ifdef USE_LIBRETINY + +#include "adc_sensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace adc { + +static const char *const TAG = "adc.libretiny"; + +void ADCSensor::setup() { + ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); +#ifndef USE_ADC_SENSOR_VCC + this->pin_->setup(); +#endif // !USE_ADC_SENSOR_VCC +} + +void ADCSensor::dump_config() { + LOG_SENSOR("", "ADC Sensor", this); +#ifdef USE_ADC_SENSOR_VCC + ESP_LOGCONFIG(TAG, " Pin: VCC"); +#else // USE_ADC_SENSOR_VCC + LOG_PIN(" Pin: ", this->pin_); +#endif // USE_ADC_SENSOR_VCC + ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); + LOG_UPDATE_INTERVAL(this); +} + +float ADCSensor::sample() { + uint32_t raw = 0; + if (this->output_raw_) { + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + raw += analogRead(this->pin_->get_pin()); // NOLINT + } + raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + return raw; + } + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT + } + raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + return raw / 1000.0f; +} + +} // namespace adc +} // namespace esphome + +#endif // USE_LIBRETINY diff --git a/tests/components/adc/test.bk72xx-ard.yaml b/tests/components/adc/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..491d0af3b9 --- /dev/null +++ b/tests/components/adc/test.bk72xx-ard.yaml @@ -0,0 +1,4 @@ +sensor: + - platform: adc + pin: P23 + name: Basic ADC Test From df4224e779a2173ca63e75ea7ed8f294e540a2f1 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 15 Dec 2024 19:30:47 +0100 Subject: [PATCH 0794/1052] [nextion] Publishes `is_connected()` (#7961) --- esphome/components/nextion/nextion.cpp | 2 +- esphome/components/nextion/nextion.h | 21 +++++++++++++++++++-- tests/components/nextion/common.yaml | 2 ++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 50a5834347..0b3ded07ae 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -40,7 +40,7 @@ bool Nextion::send_command_(const std::string &command) { } bool Nextion::check_connect_() { - if (this->get_is_connected_()) + if (this->is_connected_) return true; // Check if the handshake should be skipped for the Nextion connection diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index f539c79718..800bdaaa74 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1217,6 +1217,25 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ bool is_updating() override; + /** + * @brief Check if the Nextion display is successfully connected. + * + * This method returns whether a successful connection has been established with + * the Nextion display. A connection is considered established when: + * + * - The initial handshake with the display is completed successfully, or + * - The handshake is skipped via skip_connection_handshake_ flag + * + * The connection status is particularly useful when: + * - Troubleshooting communication issues + * - Ensuring the display is ready before sending commands + * - Implementing connection-dependent behaviors + * + * @return true if the Nextion display is connected and ready to receive commands + * @return false if the display is not yet connected or connection was lost + */ + bool is_connected() { return this->is_connected_; } + protected: std::deque nextion_queue_; std::deque waveform_queue_; @@ -1315,8 +1334,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe #endif // USE_NEXTION_TFT_UPLOAD - bool get_is_connected_() { return this->is_connected_; } - bool check_connect_(); std::vector touch_; diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml index e84cd08422..73fc8484c0 100644 --- a/tests/components/nextion/common.yaml +++ b/tests/components/nextion/common.yaml @@ -1,5 +1,7 @@ esphome: on_boot: + - lambda: 'ESP_LOGD("display","is_connected(): %s", YESNO(id(main_lcd).is_connected()));' + # Binary sensor publish action tests - binary_sensor.nextion.publish: id: r0_sensor From 4e1ff31342aae982efd473d117beaf1cced9d7ca Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:37:22 -0500 Subject: [PATCH 0795/1052] [const] Add RMT CONF variables to const.py (#7953) Co-authored-by: Jonathan Swoboda --- esphome/components/remote_receiver/__init__.py | 13 ++++++------- esphome/const.py | 4 ++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index e5085bb33c..d3f61977c6 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -1,24 +1,23 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins -from esphome.components import remote_base, esp32_rmt +import esphome.codegen as cg +from esphome.components import esp32_rmt, remote_base +import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, + CONF_CLOCK_DIVIDER, CONF_DUMP, CONF_FILTER, CONF_ID, CONF_IDLE, + CONF_MEMORY_BLOCKS, CONF_PIN, + CONF_RMT_CHANNEL, CONF_TOLERANCE, CONF_TYPE, - CONF_MEMORY_BLOCKS, - CONF_RMT_CHANNEL, CONF_VALUE, ) from esphome.core import CORE, TimePeriod -CONF_CLOCK_DIVIDER = "clock_divider" - AUTO_LOAD = ["remote_base"] remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver") remote_base_ns = cg.esphome_ns.namespace("remote_base") diff --git a/esphome/const.py b/esphome/const.py index 4d8d550fd1..6e44b5ff89 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -131,7 +131,9 @@ CONF_CLIENT_CERTIFICATE = "client_certificate" CONF_CLIENT_CERTIFICATE_KEY = "client_certificate_key" CONF_CLIENT_ID = "client_id" CONF_CLK_PIN = "clk_pin" +CONF_CLOCK_DIVIDER = "clock_divider" CONF_CLOCK_PIN = "clock_pin" +CONF_CLOCK_RESOLUTION = "clock_resolution" CONF_CLOSE_ACTION = "close_action" CONF_CLOSE_DURATION = "close_duration" CONF_CLOSE_ENDSTOP = "close_endstop" @@ -739,6 +741,7 @@ CONF_RGB_ORDER = "rgb_order" CONF_RGBW = "rgbw" CONF_RISING_EDGE = "rising_edge" CONF_RMT_CHANNEL = "rmt_channel" +CONF_RMT_SYMBOLS = "rmt_symbols" CONF_ROTATION = "rotation" CONF_ROW = "row" CONF_RS_PIN = "rs_pin" @@ -918,6 +921,7 @@ CONF_UPDATE_ON_BOOT = "update_on_boot" CONF_URL = "url" CONF_USE_ABBREVIATIONS = "use_abbreviations" CONF_USE_ADDRESS = "use_address" +CONF_USE_DMA = "use_dma" CONF_USE_FAHRENHEIT = "use_fahrenheit" CONF_USERNAME = "username" CONF_UUID = "uuid" From 0fbe6c0d8b6ef9bf25751cbdbf12b5be72d166d9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:37:51 +1300 Subject: [PATCH 0796/1052] [sgp30] Set default update interval to 60s (#7952) --- esphome/components/sgp30/sensor.py | 19 +++++++++---------- esphome/components/sgp30/sgp30.cpp | 8 ++------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index 13e859cc09..8c92f55ef7 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -1,23 +1,22 @@ import esphome.codegen as cg +from esphome.components import i2c, sensirion_common, sensor import esphome.config_validation as cv -from esphome.components import i2c, sensor, sensirion_common - from esphome.const import ( - CONF_COMPENSATION, - CONF_ID, CONF_BASELINE, + CONF_COMPENSATION, CONF_ECO2, + CONF_ID, CONF_STORE_BASELINE, CONF_TEMPERATURE_SOURCE, CONF_TVOC, - ICON_RADIATOR, DEVICE_CLASS_CARBON_DIOXIDE, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, - STATE_CLASS_MEASUREMENT, - UNIT_PARTS_PER_MILLION, - UNIT_PARTS_PER_BILLION, - ICON_MOLECULE_CO2, ENTITY_CATEGORY_DIAGNOSTIC, + ICON_MOLECULE_CO2, + ICON_RADIATOR, + STATE_CLASS_MEASUREMENT, + UNIT_PARTS_PER_BILLION, + UNIT_PARTS_PER_MILLION, ) DEPENDENCIES = ["i2c"] @@ -77,7 +76,7 @@ CONFIG_SCHEMA = ( ), } ) - .extend(cv.polling_component_schema("1s")) + .extend(cv.polling_component_schema("60s")) .extend(i2c.i2c_device_schema(0x58)) ) diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 261604b992..77e9ef9820 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -1,8 +1,8 @@ #include "sgp30.h" +#include +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" -#include namespace esphome { namespace sgp30 { @@ -295,10 +295,6 @@ void SGP30Component::update() { if (this->tvoc_sensor_ != nullptr) this->tvoc_sensor_->publish_state(tvoc); - if (this->get_update_interval() != 1000) { - ESP_LOGW(TAG, "Update interval for SGP30 sensor must be set to 1s for optimized readout"); - } - this->status_clear_warning(); this->send_env_data_(); this->read_iaq_baseline_(); From af23357dca9c5f4bcdb3566aec9813dfa3153dfc Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:45:10 -0500 Subject: [PATCH 0797/1052] [core] Move delay_microseconds_safe to iram (#7957) Co-authored-by: Jonathan Swoboda --- esphome/core/helpers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 4e8caeae99..b11615204e 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -767,7 +767,8 @@ bool mac_address_is_valid(const uint8_t *mac) { return !(is_all_zeros || is_all_ones); } -void delay_microseconds_safe(uint32_t us) { // avoids CPU locks that could trigger WDT or affect WiFi/BT stability +void IRAM_ATTR HOT delay_microseconds_safe(uint32_t us) { + // avoids CPU locks that could trigger WDT or affect WiFi/BT stability uint32_t start = micros(); const uint32_t lag = 5000; // microseconds, specifies the maximum time for a CPU busy-loop. From de1fbd390b166c31ef3a6c2af4359c94dee4ae48 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Sat, 14 Dec 2024 00:17:58 -0500 Subject: [PATCH 0798/1052] [i2s_audio] Bugfix: Correctly set ring buffer size (#7959) --- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index d2a582c2cc..46f1b00d05 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -247,7 +247,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { // Ensure ring buffer is at least as large as the total size of the DMA buffers const size_t ring_buffer_size = - std::min((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); + std::max((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) { // Failed to allocate buffers From 5382bd2a9788ffa1b2bf589675eee0f1e4a5ce47 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 14 Dec 2024 19:55:04 +0100 Subject: [PATCH 0799/1052] [adc] Restore missing LIBRETINY code in a separated file (#7955) --- .../components/adc/adc_sensor_libretiny.cpp | 48 +++++++++++++++++++ tests/components/adc/test.bk72xx-ard.yaml | 4 ++ 2 files changed, 52 insertions(+) create mode 100644 esphome/components/adc/adc_sensor_libretiny.cpp create mode 100644 tests/components/adc/test.bk72xx-ard.yaml diff --git a/esphome/components/adc/adc_sensor_libretiny.cpp b/esphome/components/adc/adc_sensor_libretiny.cpp new file mode 100644 index 0000000000..cd04477b3f --- /dev/null +++ b/esphome/components/adc/adc_sensor_libretiny.cpp @@ -0,0 +1,48 @@ +#ifdef USE_LIBRETINY + +#include "adc_sensor.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace adc { + +static const char *const TAG = "adc.libretiny"; + +void ADCSensor::setup() { + ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); +#ifndef USE_ADC_SENSOR_VCC + this->pin_->setup(); +#endif // !USE_ADC_SENSOR_VCC +} + +void ADCSensor::dump_config() { + LOG_SENSOR("", "ADC Sensor", this); +#ifdef USE_ADC_SENSOR_VCC + ESP_LOGCONFIG(TAG, " Pin: VCC"); +#else // USE_ADC_SENSOR_VCC + LOG_PIN(" Pin: ", this->pin_); +#endif // USE_ADC_SENSOR_VCC + ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); + LOG_UPDATE_INTERVAL(this); +} + +float ADCSensor::sample() { + uint32_t raw = 0; + if (this->output_raw_) { + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + raw += analogRead(this->pin_->get_pin()); // NOLINT + } + raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + return raw; + } + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT + } + raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + return raw / 1000.0f; +} + +} // namespace adc +} // namespace esphome + +#endif // USE_LIBRETINY diff --git a/tests/components/adc/test.bk72xx-ard.yaml b/tests/components/adc/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..491d0af3b9 --- /dev/null +++ b/tests/components/adc/test.bk72xx-ard.yaml @@ -0,0 +1,4 @@ +sensor: + - platform: adc + pin: P23 + name: Basic ADC Test From 63b0930ae812dd29fbcc3d9164425d640d5bcdf9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Dec 2024 07:57:06 +1300 Subject: [PATCH 0800/1052] Bump version to 2024.12.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 6e44b5ff89..64b3f83b83 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.0b1" +__version__ = "2024.12.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From ea06740b46564d4f87963f72de477defb2d62c1d Mon Sep 17 00:00:00 2001 From: luar123 <49960470+luar123@users.noreply.github.com> Date: Sun, 15 Dec 2024 22:59:54 +0100 Subject: [PATCH 0801/1052] Fix adc channel for ESP32-H2 (#7964) --- esphome/components/adc/__init__.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index 11b0ba2389..d8d21523b9 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -1,11 +1,6 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import pins -from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER - -from esphome.core import CORE +import esphome.codegen as cg from esphome.components.esp32 import get_esp32_variant -from esphome.const import PLATFORM_ESP8266 from esphome.components.esp32.const import ( VARIANT_ESP32, VARIANT_ESP32C2, @@ -15,6 +10,9 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S2, VARIANT_ESP32S3, ) +import esphome.config_validation as cv +from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266 +from esphome.core import CORE CODEOWNERS = ["@esphome/core"] @@ -102,11 +100,11 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = { 6: adc1_channel_t.ADC1_CHANNEL_6, }, VARIANT_ESP32H2: { - 0: adc1_channel_t.ADC1_CHANNEL_0, - 1: adc1_channel_t.ADC1_CHANNEL_1, - 2: adc1_channel_t.ADC1_CHANNEL_2, - 3: adc1_channel_t.ADC1_CHANNEL_3, - 4: adc1_channel_t.ADC1_CHANNEL_4, + 1: adc1_channel_t.ADC1_CHANNEL_0, + 2: adc1_channel_t.ADC1_CHANNEL_1, + 3: adc1_channel_t.ADC1_CHANNEL_2, + 4: adc1_channel_t.ADC1_CHANNEL_3, + 5: adc1_channel_t.ADC1_CHANNEL_4, }, } From 9816c27031e096ee63036ab3c402f615f6d4da68 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 15 Dec 2024 23:00:44 +0100 Subject: [PATCH 0802/1052] [nextion] Remove `_internal` from non-protected functions (#7656) --- esphome/components/nextion/display.py | 10 +- esphome/components/nextion/nextion.cpp | 8 - esphome/components/nextion/nextion.h | 148 ++++++++---------- .../components/nextion/nextion_commands.cpp | 20 +-- 4 files changed, 84 insertions(+), 102 deletions(-) diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 6f284376af..f6bd863d42 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -119,17 +119,17 @@ async def to_code(config): cg.add_library("ESP8266HTTPClient", None) if CONF_TOUCH_SLEEP_TIMEOUT in config: - cg.add(var.set_touch_sleep_timeout_internal(config[CONF_TOUCH_SLEEP_TIMEOUT])) + cg.add(var.set_touch_sleep_timeout(config[CONF_TOUCH_SLEEP_TIMEOUT])) if CONF_WAKE_UP_PAGE in config: - cg.add(var.set_wake_up_page_internal(config[CONF_WAKE_UP_PAGE])) + cg.add(var.set_wake_up_page(config[CONF_WAKE_UP_PAGE])) if CONF_START_UP_PAGE in config: - cg.add(var.set_start_up_page_internal(config[CONF_START_UP_PAGE])) + cg.add(var.set_start_up_page(config[CONF_START_UP_PAGE])) - cg.add(var.set_auto_wake_on_touch_internal(config[CONF_AUTO_WAKE_ON_TOUCH])) + cg.add(var.set_auto_wake_on_touch(config[CONF_AUTO_WAKE_ON_TOUCH])) - cg.add(var.set_exit_reparse_on_start_internal(config[CONF_EXIT_REPARSE_ON_START])) + cg.add(var.set_exit_reparse_on_start(config[CONF_EXIT_REPARSE_ON_START])) cg.add(var.set_skip_connection_handshake(config[CONF_SKIP_CONNECTION_HANDSHAKE])) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index 0b3ded07ae..e5df13c64e 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -280,14 +280,6 @@ void Nextion::loop() { this->goto_page(this->start_up_page_); } - // This could probably be removed from the loop area, as those are redundant. - this->set_auto_wake_on_touch(this->auto_wake_on_touch_); - this->set_exit_reparse_on_start(this->exit_reparse_on_start_); - - if (this->touch_sleep_timeout_ != 0) { - this->set_touch_sleep_timeout(this->touch_sleep_timeout_); - } - if (this->wake_up_page_ != -1) { this->set_wake_up_page(this->wake_up_page_); } diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 800bdaaa74..c293f80aee 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -856,76 +856,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe */ void set_backlight_brightness(float brightness); - /** - * Set the touch sleep timeout of the display. - * @param timeout Timeout in seconds. - * - * Example: - * ```cpp - * it.set_touch_sleep_timeout(30); - * ``` - * - * After 30 seconds the display will go to sleep. Note: the display will only wakeup by a restart or by setting up - * `thup`. - */ - void set_touch_sleep_timeout(uint16_t timeout); - - /** - * Sets which page Nextion loads when exiting sleep mode. Note this can be set even when Nextion is in sleep mode. - * @param page_id The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to - * wakes up to current page. - * - * Example: - * ```cpp - * it.set_wake_up_page(2); - * ``` - * - * The display will wake up to page 2. - */ - void set_wake_up_page(uint8_t page_id = 255); - - /** - * Sets which page Nextion loads when connecting to ESPHome. - * @param page_id The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to - * wakes up to current page. - * - * Example: - * ```cpp - * it.set_start_up_page(2); - * ``` - * - * The display will go to page 2 when it establishes a connection to ESPHome. - */ - void set_start_up_page(uint8_t page_id = 255); - - /** - * Sets if Nextion should auto-wake from sleep when touch press occurs. - * @param auto_wake True or false. When auto_wake is true and Nextion is in sleep mode, - * the first touch will only trigger the auto wake mode and not trigger a Touch Event. - * - * Example: - * ```cpp - * it.set_auto_wake_on_touch(true); - * ``` - * - * The display will wake up by touch. - */ - void set_auto_wake_on_touch(bool auto_wake); - - /** - * Sets if Nextion should exit the active reparse mode before the "connect" command is sent - * @param exit_reparse True or false. When exit_reparse is true, the exit reparse command - * will be sent before requesting the connection from Nextion. - * - * Example: - * ```cpp - * it.set_exit_reparse_on_start(true); - * ``` - * - * The display will be requested to leave active reparse mode before setup. - */ - void set_exit_reparse_on_start(bool exit_reparse); - /** * Sets whether the Nextion display should skip the connection handshake process. * @param skip_handshake True or false. When skip_connection_handshake is true, @@ -1172,15 +1102,75 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe void update_components_by_prefix(const std::string &prefix); - void set_touch_sleep_timeout_internal(uint32_t touch_sleep_timeout) { - this->touch_sleep_timeout_ = touch_sleep_timeout; - } - void set_wake_up_page_internal(uint8_t wake_up_page) { this->wake_up_page_ = wake_up_page; } - void set_start_up_page_internal(uint8_t start_up_page) { this->start_up_page_ = start_up_page; } - void set_auto_wake_on_touch_internal(bool auto_wake_on_touch) { this->auto_wake_on_touch_ = auto_wake_on_touch; } - void set_exit_reparse_on_start_internal(bool exit_reparse_on_start) { - this->exit_reparse_on_start_ = exit_reparse_on_start; - } + /** + * Set the touch sleep timeout of the display. + * @param timeout Timeout in seconds. + * + * Example: + * ```cpp + * it.set_touch_sleep_timeout(30); + * ``` + * + * After 30 seconds the display will go to sleep. Note: the display will only wakeup by a restart or by setting up + * `thup`. + */ + void set_touch_sleep_timeout(uint32_t touch_sleep_timeout); + + /** + * Sets which page Nextion loads when exiting sleep mode. Note this can be set even when Nextion is in sleep mode. + * @param wake_up_page The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to + * wakes up to current page. + * + * Example: + * ```cpp + * it.set_wake_up_page(2); + * ``` + * + * The display will wake up to page 2. + */ + void set_wake_up_page(uint8_t wake_up_page = 255); + + /** + * Sets which page Nextion loads when connecting to ESPHome. + * @param start_up_page The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to + * wakes up to current page. + * + * Example: + * ```cpp + * it.set_start_up_page(2); + * ``` + * + * The display will go to page 2 when it establishes a connection to ESPHome. + */ + void set_start_up_page(uint8_t start_up_page = 255) { this->start_up_page_ = start_up_page; } + + /** + * Sets if Nextion should auto-wake from sleep when touch press occurs. + * @param auto_wake_on_touch True or false. When auto_wake is true and Nextion is in sleep mode, + * the first touch will only trigger the auto wake mode and not trigger a Touch Event. + * + * Example: + * ```cpp + * it.set_auto_wake_on_touch(true); + * ``` + * + * The display will wake up by touch. + */ + void set_auto_wake_on_touch(bool auto_wake_on_touch); + + /** + * Sets if Nextion should exit the active reparse mode before the "connect" command is sent + * @param exit_reparse_on_start True or false. When exit_reparse_on_start is true, the exit reparse command + * will be sent before requesting the connection from Nextion. + * + * Example: + * ```cpp + * it.set_exit_reparse_on_start(true); + * ``` + * + * The display will be requested to leave active reparse mode before setup. + */ + void set_exit_reparse_on_start(bool exit_reparse_on_start) { this->exit_reparse_on_start_ = exit_reparse_on_start; } /** * @brief Retrieves the number of commands pending in the Nextion command queue. diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp index 398e9dd502..e3172c8c1b 100644 --- a/esphome/components/nextion/nextion_commands.cpp +++ b/esphome/components/nextion/nextion_commands.cpp @@ -10,19 +10,19 @@ static const char *const TAG = "nextion"; // Sleep safe commands void Nextion::soft_reset() { this->send_command_("rest"); } -void Nextion::set_wake_up_page(uint8_t page_id) { - this->add_no_result_to_queue_with_set_internal_("wake_up_page", "wup", page_id, true); +void Nextion::set_wake_up_page(uint8_t wake_up_page) { + this->wake_up_page_ = wake_up_page; + this->add_no_result_to_queue_with_set_internal_("wake_up_page", "wup", wake_up_page, true); } -void Nextion::set_start_up_page(uint8_t page_id) { this->start_up_page_ = page_id; } - -void Nextion::set_touch_sleep_timeout(uint16_t timeout) { - if (timeout < 3) { +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"); return; } - this->add_no_result_to_queue_with_set_internal_("touch_sleep_timeout", "thsp", timeout, true); + this->touch_sleep_timeout_ = touch_sleep_timeout; + this->add_no_result_to_queue_with_set_internal_("touch_sleep_timeout", "thsp", touch_sleep_timeout, true); } void Nextion::sleep(bool sleep) { @@ -54,7 +54,6 @@ bool Nextion::set_protocol_reparse_mode(bool active_mode) { this->ignore_is_setup_ = false; return all_commands_sent; } -void Nextion::set_exit_reparse_on_start(bool exit_reparse) { this->exit_reparse_on_start_ = exit_reparse; } // Set Colors - Background void Nextion::set_component_background_color(const char *component, uint16_t color) { @@ -191,8 +190,9 @@ void Nextion::set_backlight_brightness(float brightness) { this->add_no_result_to_queue_with_printf_("backlight_brightness", "dim=%d", static_cast(brightness * 100)); } -void Nextion::set_auto_wake_on_touch(bool auto_wake) { - this->add_no_result_to_queue_with_set("auto_wake_on_touch", "thup", auto_wake ? 1 : 0); +void Nextion::set_auto_wake_on_touch(bool auto_wake_on_touch) { + this->auto_wake_on_touch_ = auto_wake_on_touch; + this->add_no_result_to_queue_with_set("auto_wake_on_touch", "thup", auto_wake_on_touch ? 1 : 0); } // General Component From a6957b9d3b5edba89b357ff59850ce965afd9e57 Mon Sep 17 00:00:00 2001 From: Oleg Tarasov Date: Mon, 16 Dec 2024 02:04:26 +0300 Subject: [PATCH 0803/1052] [opentherm] Message ordering, on-the-fly message editing, code improvements (#7903) --- esphome/components/opentherm/__init__.py | 80 ++++++- esphome/components/opentherm/automation.h | 25 +++ esphome/components/opentherm/const.py | 1 + esphome/components/opentherm/generate.py | 47 +++- esphome/components/opentherm/hub.cpp | 202 +++++++++++------- esphome/components/opentherm/hub.h | 46 ++-- esphome/components/opentherm/opentherm.cpp | 74 ++++--- esphome/components/opentherm/opentherm.h | 33 ++- .../components/opentherm/opentherm_macros.h | 11 + esphome/components/opentherm/schema.py | 79 ++++++- esphome/components/opentherm/validate.py | 9 +- tests/components/opentherm/common.yaml | 13 ++ 12 files changed, 475 insertions(+), 145 deletions(-) create mode 100644 esphome/components/opentherm/automation.h diff --git a/esphome/components/opentherm/__init__.py b/esphome/components/opentherm/__init__.py index 81cd78af08..42b476eb87 100644 --- a/esphome/components/opentherm/__init__.py +++ b/esphome/components/opentherm/__init__.py @@ -1,10 +1,12 @@ from typing import Any +import logging +from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome import pins from esphome.components import sensor -from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266 +from esphome.const import CONF_ID, PLATFORM_ESP32, PLATFORM_ESP8266, CONF_TRIGGER_ID from . import const, schema, validate, generate CODEOWNERS = ["@olegtarasov"] @@ -20,7 +22,21 @@ CONF_CH2_ACTIVE = "ch2_active" CONF_SUMMER_MODE_ACTIVE = "summer_mode_active" CONF_DHW_BLOCK = "dhw_block" CONF_SYNC_MODE = "sync_mode" -CONF_OPENTHERM_VERSION = "opentherm_version" +CONF_OPENTHERM_VERSION = "opentherm_version" # Deprecated, will be removed +CONF_BEFORE_SEND = "before_send" +CONF_BEFORE_PROCESS_RESPONSE = "before_process_response" + +# Triggers +BeforeSendTrigger = generate.opentherm_ns.class_( + "BeforeSendTrigger", + automation.Trigger.template(generate.OpenthermData.operator("ref")), +) +BeforeProcessResponseTrigger = generate.opentherm_ns.class_( + "BeforeProcessResponseTrigger", + automation.Trigger.template(generate.OpenthermData.operator("ref")), +) + +_LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = cv.All( cv.Schema( @@ -36,7 +52,19 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_SUMMER_MODE_ACTIVE, False): cv.boolean, cv.Optional(CONF_DHW_BLOCK, False): cv.boolean, cv.Optional(CONF_SYNC_MODE, False): cv.boolean, - cv.Optional(CONF_OPENTHERM_VERSION): cv.positive_float, + cv.Optional(CONF_OPENTHERM_VERSION): cv.positive_float, # Deprecated + cv.Optional(CONF_BEFORE_SEND): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BeforeSendTrigger), + } + ), + cv.Optional(CONF_BEFORE_PROCESS_RESPONSE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + BeforeProcessResponseTrigger + ), + } + ), } ) .extend( @@ -44,6 +72,11 @@ CONFIG_SCHEMA = cv.All( schema.INPUTS, (lambda _: cv.use_id(sensor.Sensor)) ) ) + .extend( + validate.create_entities_schema( + schema.SETTINGS, (lambda s: s.validation_schema) + ) + ) .extend(cv.COMPONENT_SCHEMA), cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266]), ) @@ -60,18 +93,33 @@ async def to_code(config: dict[str, Any]) -> None: out_pin = await cg.gpio_pin_expression(config[CONF_OUT_PIN]) cg.add(var.set_out_pin(out_pin)) - non_sensors = {CONF_ID, CONF_IN_PIN, CONF_OUT_PIN} + non_sensors = { + CONF_ID, + CONF_IN_PIN, + CONF_OUT_PIN, + CONF_BEFORE_SEND, + CONF_BEFORE_PROCESS_RESPONSE, + } input_sensors = [] + settings = [] for key, value in config.items(): if key in non_sensors: continue if key in schema.INPUTS: input_sensor = await cg.get_variable(value) - cg.add( - getattr(var, f"set_{key}_{const.INPUT_SENSOR.lower()}")(input_sensor) - ) + cg.add(getattr(var, f"set_{key}_{const.INPUT_SENSOR}")(input_sensor)) input_sensors.append(key) + elif key in schema.SETTINGS: + if value == schema.SETTINGS[key].default_value: + continue + cg.add(getattr(var, f"set_{key}_{const.SETTING}")(value)) + settings.append(key) else: + if key == CONF_OPENTHERM_VERSION: + _LOGGER.warning( + "opentherm_version is deprecated and will be removed in esphome 2025.2.0\n" + "Please change to 'opentherm_version_controller'." + ) cg.add(getattr(var, f"set_{key}")(value)) if len(input_sensors) > 0: @@ -81,3 +129,21 @@ async def to_code(config: dict[str, Any]) -> None: ) generate.define_readers(const.INPUT_SENSOR, input_sensors) generate.add_messages(var, input_sensors, schema.INPUTS) + + if len(settings) > 0: + generate.define_has_settings(settings, schema.SETTINGS) + generate.define_message_handler(const.SETTING, settings, schema.SETTINGS) + generate.define_setting_readers(const.SETTING, settings) + generate.add_messages(var, settings, schema.SETTINGS) + + for conf in config.get(CONF_BEFORE_SEND, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(generate.OpenthermData.operator("ref"), "x")], conf + ) + + for conf in config.get(CONF_BEFORE_PROCESS_RESPONSE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation( + trigger, [(generate.OpenthermData.operator("ref"), "x")], conf + ) diff --git a/esphome/components/opentherm/automation.h b/esphome/components/opentherm/automation.h new file mode 100644 index 0000000000..acbe33ac8f --- /dev/null +++ b/esphome/components/opentherm/automation.h @@ -0,0 +1,25 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "hub.h" +#include "opentherm.h" + +namespace esphome { +namespace opentherm { + +class BeforeSendTrigger : public Trigger { + public: + BeforeSendTrigger(OpenthermHub *hub) { + hub->add_on_before_send_callback([this](OpenthermData &x) { this->trigger(x); }); + } +}; + +class BeforeProcessResponseTrigger : public Trigger { + public: + BeforeProcessResponseTrigger(OpenthermHub *hub) { + hub->add_on_before_process_response_callback([this](OpenthermData &x) { this->trigger(x); }); + } +}; + +} // namespace opentherm +} // namespace esphome diff --git a/esphome/components/opentherm/const.py b/esphome/components/opentherm/const.py index a113331585..51ad84ce46 100644 --- a/esphome/components/opentherm/const.py +++ b/esphome/components/opentherm/const.py @@ -9,3 +9,4 @@ SWITCH = "switch" NUMBER = "number" OUTPUT = "output" INPUT_SENSOR = "input_sensor" +SETTING = "setting" diff --git a/esphome/components/opentherm/generate.py b/esphome/components/opentherm/generate.py index 9716cab093..6b6a0255a8 100644 --- a/esphome/components/opentherm/generate.py +++ b/esphome/components/opentherm/generate.py @@ -1,13 +1,14 @@ from collections.abc import Awaitable -from typing import Any, Callable +from typing import Any, Callable, Optional import esphome.codegen as cg from esphome.const import CONF_ID from . import const -from .schema import TSchema +from .schema import TSchema, SettingSchema opentherm_ns = cg.esphome_ns.namespace("opentherm") OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component) +OpenthermData = opentherm_ns.class_("OpenthermData") def define_has_component(component_type: str, keys: list[str]) -> None: @@ -21,6 +22,24 @@ def define_has_component(component_type: str, keys: list[str]) -> None: cg.add_define(f"OPENTHERM_HAS_{component_type.upper()}_{key}") +# We need a separate set of macros for settings because there are different backing field types we need to take +# into account +def define_has_settings(keys: list[str], schemas: dict[str, SettingSchema]) -> None: + cg.add_define( + "OPENTHERM_SETTING_LIST(F, sep)", + cg.RawExpression( + " sep ".join( + map( + lambda key: f"F({schemas[key].backing_type}, {key}_setting, {schemas[key].default_value})", + keys, + ) + ) + ), + ) + for key in keys: + cg.add_define(f"OPENTHERM_HAS_SETTING_{key}") + + def define_message_handler( component_type: str, keys: list[str], schemas: dict[str, TSchema] ) -> None: @@ -74,16 +93,30 @@ def define_readers(component_type: str, keys: list[str]) -> None: ) -def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]): - messages: set[tuple[str, bool]] = set() +def define_setting_readers(component_type: str, keys: list[str]) -> None: for key in keys: - messages.add((schemas[key].message, schemas[key].keep_updated)) - for msg, keep_updated in messages: + cg.add_define( + f"OPENTHERM_READ_{key}", + cg.RawExpression(f"this->{key}_{component_type.lower()}"), + ) + + +def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]): + messages: dict[str, tuple[bool, Optional[int]]] = {} + for key in keys: + messages[schemas[key].message] = ( + schemas[key].keep_updated, + schemas[key].order if hasattr(schemas[key], "order") else None, + ) + for msg, (keep_updated, order) in messages.items(): msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}") if keep_updated: cg.add(hub.add_repeating_message(msg_expr)) else: - cg.add(hub.add_initial_message(msg_expr)) + if order is not None: + cg.add(hub.add_initial_message(msg_expr, order)) + else: + cg.add(hub.add_initial_message(msg_expr)) def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None: diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index aac2966ed1..97adf71752 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -63,7 +63,7 @@ void write_f88(const float value, OpenthermData &data) { data.f88(value); } OpenthermData OpenthermHub::build_request_(MessageId request_id) const { OpenthermData data; data.type = 0; - data.id = 0; + data.id = request_id; data.valueHB = 0; data.valueLB = 0; @@ -82,28 +82,13 @@ OpenthermData OpenthermHub::build_request_(MessageId request_id) const { // NOLINTEND data.type = MessageType::READ_DATA; - data.id = MessageId::STATUS; data.valueHB = ch_enabled | (dhw_enabled << 1) | (cooling_enabled << 2) | (otc_enabled << 3) | (ch2_enabled << 4) | (summer_mode_is_active << 5) | (dhw_blocked << 6); return data; } - // Another special case is OpenTherm version number which is configured at hub level as a constant - if (request_id == MessageId::OT_VERSION_CONTROLLER) { - data.type = MessageType::WRITE_DATA; - data.id = MessageId::OT_VERSION_CONTROLLER; - data.f88(this->opentherm_version_); - - return data; - } - -// Disable incomplete switch statement warnings, because the cases in each -// switch are generated based on the configured sensors and inputs. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch" - - // Next, we start with the write requests from switches and other inputs, + // Next, we start with write requests from switches and other inputs, // because we would want to write that data if it is available, rather than // request a read for that type (in the case that both read and write are // supported). @@ -116,14 +101,23 @@ OpenthermData OpenthermHub::build_request_(MessageId request_id) const { OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, ) OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_ENTITY, , OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, ) + OPENTHERM_SETTING_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_WRITE_MESSAGE, OPENTHERM_MESSAGE_WRITE_SETTING, , + OPENTHERM_MESSAGE_WRITE_POSTSCRIPT, ) + default: + break; } // Finally, handle the simple read requests, which only change with the message id. - switch (request_id) { OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) } + switch (request_id) { + OPENTHERM_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) + default: + break; + } switch (request_id) { OPENTHERM_BINARY_SENSOR_MESSAGE_HANDLERS(OPENTHERM_MESSAGE_READ_MESSAGE, OPENTHERM_IGNORE, , , ) + default: + break; } -#pragma GCC diagnostic pop // And if we get here, a message was requested which somehow wasn't handled. // This shouldn't happen due to the way the defines are configured, so we @@ -163,19 +157,37 @@ void OpenthermHub::setup() { // communicate at least once every second. Sending the status request is // good practice anyway. this->add_repeating_message(MessageId::STATUS); - - // Also ensure that we start communication with the STATUS message - this->initial_messages_.insert(this->initial_messages_.begin(), MessageId::STATUS); - - if (this->opentherm_version_ > 0.0f) { - this->initial_messages_.insert(this->initial_messages_.begin(), MessageId::OT_VERSION_CONTROLLER); - } - - this->current_message_iterator_ = this->initial_messages_.begin(); + this->write_initial_messages_(this->messages_); + this->message_iterator_ = this->messages_.begin(); } void OpenthermHub::on_shutdown() { this->opentherm_->stop(); } +// Disabling clang-tidy for this particular line since it keeps removing the trailing underscore (bug?) +void OpenthermHub::write_initial_messages_(std::vector &target) { // NOLINT + std::vector> sorted; + std::copy_if(this->configured_messages_.begin(), this->configured_messages_.end(), std::back_inserter(sorted), + [](const std::pair &pair) { return pair.second < REPEATING_MESSAGE_ORDER; }); + std::sort(sorted.begin(), sorted.end(), + [](const std::pair &a, const std::pair &b) { + return a.second < b.second; + }); + + target.clear(); + std::transform(sorted.begin(), sorted.end(), std::back_inserter(target), + [](const std::pair &pair) { return pair.first; }); +} + +// Disabling clang-tidy for this particular line since it keeps removing the trailing underscore (bug?) +void OpenthermHub::write_repeating_messages_(std::vector &target) { // NOLINT + target.clear(); + for (auto const &pair : this->configured_messages_) { + if (pair.second == REPEATING_MESSAGE_ORDER) { + target.push_back(pair.first); + } + } +} + void OpenthermHub::loop() { if (this->sync_mode_) { this->sync_loop_(); @@ -184,29 +196,18 @@ void OpenthermHub::loop() { auto cur_time = millis(); auto const cur_mode = this->opentherm_->get_mode(); + + if (this->handle_error_(cur_mode)) { + return; + } + switch (cur_mode) { case OperationMode::WRITE: case OperationMode::READ: case OperationMode::LISTEN: - if (!this->check_timings_(cur_time)) { - break; - } - this->last_mode_ = cur_mode; - break; - case OperationMode::ERROR_PROTOCOL: - if (this->last_mode_ == OperationMode::WRITE) { - this->handle_protocol_write_error_(); - } else if (this->last_mode_ == OperationMode::READ) { - this->handle_protocol_read_error_(); - } - - this->stop_opentherm_(); - break; - case OperationMode::ERROR_TIMEOUT: - this->handle_timeout_error_(); - this->stop_opentherm_(); break; case OperationMode::IDLE: + this->check_timings_(cur_time); if (this->should_skip_loop_(cur_time)) { break; } @@ -219,6 +220,28 @@ void OpenthermHub::loop() { case OperationMode::RECEIVED: this->read_response_(); break; + default: + break; + } + this->last_mode_ = cur_mode; +} + +bool OpenthermHub::handle_error_(OperationMode mode) { + switch (mode) { + case OperationMode::ERROR_PROTOCOL: + // Protocol error can happen only while reading boiler response. + this->handle_protocol_error_(); + return true; + case OperationMode::ERROR_TIMEOUT: + // Timeout error might happen while we wait for device to respond. + this->handle_timeout_error_(); + return true; + case OperationMode::ERROR_TIMER: + // Timer error can happen only on ESP32. + this->handle_timer_error_(); + return true; + default: + return false; } } @@ -237,16 +260,20 @@ void OpenthermHub::sync_loop_() { } this->start_conversation_(); + // There may be a timer error at this point + if (this->handle_error_(this->opentherm_->get_mode())) { + return; + } + // Spin while message is being sent to device if (!this->spin_wait_(1150, [&] { return this->opentherm_->is_active(); })) { ESP_LOGE(TAG, "Hub timeout triggered during send"); this->stop_opentherm_(); return; } - if (this->opentherm_->is_error()) { - this->handle_protocol_write_error_(); - this->stop_opentherm_(); + // Check for errors and ensure we are in the right state (message sent successfully) + if (this->handle_error_(this->opentherm_->get_mode())) { return; } else if (!this->opentherm_->is_sent()) { ESP_LOGW(TAG, "Unexpected state after sending request: %s", @@ -257,19 +284,20 @@ void OpenthermHub::sync_loop_() { // Listen for the response this->opentherm_->listen(); + // There may be a timer error at this point + if (this->handle_error_(this->opentherm_->get_mode())) { + return; + } + + // Spin while response is being received if (!this->spin_wait_(1150, [&] { return this->opentherm_->is_active(); })) { ESP_LOGE(TAG, "Hub timeout triggered during receive"); this->stop_opentherm_(); return; } - if (this->opentherm_->is_timeout()) { - this->handle_timeout_error_(); - this->stop_opentherm_(); - return; - } else if (this->opentherm_->is_protocol_error()) { - this->handle_protocol_read_error_(); - this->stop_opentherm_(); + // Check for errors and ensure we are in the right state (message received successfully) + if (this->handle_error_(this->opentherm_->get_mode())) { return; } else if (!this->opentherm_->has_message()) { ESP_LOGW(TAG, "Unexpected state after receiving response: %s", @@ -281,17 +309,13 @@ void OpenthermHub::sync_loop_() { this->read_response_(); } -bool OpenthermHub::check_timings_(uint32_t cur_time) { +void OpenthermHub::check_timings_(uint32_t cur_time) { if (this->last_conversation_start_ > 0 && (cur_time - this->last_conversation_start_) > 1150) { ESP_LOGW(TAG, "%d ms elapsed since the start of the last convo, but 1150 ms are allowed at maximum. Look at other " "components that might slow the loop down.", (int) (cur_time - this->last_conversation_start_)); - this->stop_opentherm_(); - return false; } - - return true; } bool OpenthermHub::should_skip_loop_(uint32_t cur_time) const { @@ -304,14 +328,17 @@ bool OpenthermHub::should_skip_loop_(uint32_t cur_time) const { } void OpenthermHub::start_conversation_() { - if (this->sending_initial_ && this->current_message_iterator_ == this->initial_messages_.end()) { - this->sending_initial_ = false; - this->current_message_iterator_ = this->repeating_messages_.begin(); - } else if (this->current_message_iterator_ == this->repeating_messages_.end()) { - this->current_message_iterator_ = this->repeating_messages_.begin(); + if (this->message_iterator_ == this->messages_.end()) { + if (this->sending_initial_) { + this->sending_initial_ = false; + this->write_repeating_messages_(this->messages_); + } + this->message_iterator_ = this->messages_.begin(); } - auto request = this->build_request_(*this->current_message_iterator_); + auto request = this->build_request_(*this->message_iterator_); + + this->before_send_callback_.call(request); ESP_LOGD(TAG, "Sending request with id %d (%s)", request.id, this->opentherm_->message_id_to_str((MessageId) request.id)); @@ -331,37 +358,48 @@ void OpenthermHub::read_response_() { this->stop_opentherm_(); + this->before_process_response_callback_.call(response); this->process_response(response); - this->current_message_iterator_++; + this->message_iterator_++; } void OpenthermHub::stop_opentherm_() { this->opentherm_->stop(); this->last_conversation_end_ = millis(); } -void OpenthermHub::handle_protocol_write_error_() { - ESP_LOGW(TAG, "Error while sending request: %s", - this->opentherm_->operation_mode_to_str(this->opentherm_->get_mode())); - this->opentherm_->debug_data(this->last_request_); -} -void OpenthermHub::handle_protocol_read_error_() { + +void OpenthermHub::handle_protocol_error_() { OpenThermError error; this->opentherm_->get_protocol_error(error); ESP_LOGW(TAG, "Protocol error occured while receiving response: %s", - this->opentherm_->protocol_error_to_to_str(error.error_type)); + this->opentherm_->protocol_error_to_str(error.error_type)); this->opentherm_->debug_error(error); -} -void OpenthermHub::handle_timeout_error_() { - ESP_LOGW(TAG, "Receive response timed out at a protocol level"); this->stop_opentherm_(); } +void OpenthermHub::handle_timeout_error_() { + ESP_LOGW(TAG, "Timeout while waiting for response from device"); + this->stop_opentherm_(); +} + +void OpenthermHub::handle_timer_error_() { + this->opentherm_->report_and_reset_timer_error(); + this->stop_opentherm_(); + // Timer error is critical, there is no point in retrying. + this->mark_failed(); +} + void OpenthermHub::dump_config() { + std::vector initial_messages; + std::vector repeating_messages; + this->write_initial_messages_(initial_messages); + this->write_repeating_messages_(repeating_messages); + ESP_LOGCONFIG(TAG, "OpenTherm:"); LOG_PIN(" In: ", this->in_pin_); LOG_PIN(" Out: ", this->out_pin_); - ESP_LOGCONFIG(TAG, " Sync mode: %d", this->sync_mode_); + 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, ))); @@ -369,12 +407,12 @@ void OpenthermHub::dump_config() { ESP_LOGCONFIG(TAG, " Outputs: %s", SHOW(OPENTHERM_OUTPUT_LIST(ID, ))); ESP_LOGCONFIG(TAG, " Numbers: %s", SHOW(OPENTHERM_NUMBER_LIST(ID, ))); ESP_LOGCONFIG(TAG, " Initial requests:"); - for (auto type : this->initial_messages_) { - ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str((type))); + for (auto type : initial_messages) { + ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str(type)); } ESP_LOGCONFIG(TAG, " Repeating requests:"); - for (auto type : this->repeating_messages_) { - ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str((type))); + for (auto type : repeating_messages) { + ESP_LOGCONFIG(TAG, " - %d (%s)", type, this->opentherm_->message_id_to_str(type)); } } diff --git a/esphome/components/opentherm/hub.h b/esphome/components/opentherm/hub.h index 1f536653e8..80fd268820 100644 --- a/esphome/components/opentherm/hub.h +++ b/esphome/components/opentherm/hub.h @@ -38,6 +38,9 @@ namespace esphome { namespace opentherm { +static const uint8_t REPEATING_MESSAGE_ORDER = 255; +static const uint8_t INITIAL_UNORDERED_MESSAGE_ORDER = 254; + // OpenTherm component for ESPHome class OpenthermHub : public Component { protected: @@ -58,15 +61,12 @@ class OpenthermHub : public Component { OPENTHERM_INPUT_SENSOR_LIST(OPENTHERM_DECLARE_INPUT_SENSOR, ) - // The set of initial messages to send on starting communication with the boiler - std::vector initial_messages_; - // and the repeating messages which are sent repeatedly to update various sensors - // and boiler parameters (like the setpoint). - std::vector repeating_messages_; - // Indicates if we are still working on the initial requests or not + OPENTHERM_SETTING_LIST(OPENTHERM_DECLARE_SETTING, ) + bool sending_initial_ = true; - // Index for the current request in one of the _requests sets. - std::vector::const_iterator current_message_iterator_; + std::unordered_map configured_messages_; + std::vector messages_; + std::vector::const_iterator message_iterator_; uint32_t last_conversation_start_ = 0; uint32_t last_conversation_end_ = 0; @@ -78,20 +78,25 @@ class OpenthermHub : public Component { // Very likely to happen while using Dallas temperature sensors. bool sync_mode_ = false; - float opentherm_version_ = 0.0f; + CallbackManager before_send_callback_; + CallbackManager before_process_response_callback_; // Create OpenTherm messages based on the message id OpenthermData build_request_(MessageId request_id) const; - void handle_protocol_write_error_(); - void handle_protocol_read_error_(); + bool handle_error_(OperationMode mode); + void handle_protocol_error_(); void handle_timeout_error_(); + void handle_timer_error_(); void stop_opentherm_(); void start_conversation_(); void read_response_(); - bool check_timings_(uint32_t cur_time); + void check_timings_(uint32_t cur_time); bool should_skip_loop_(uint32_t cur_time) const; void sync_loop_(); + void write_initial_messages_(std::vector &target); + void write_repeating_messages_(std::vector &target); + template bool spin_wait_(uint32_t timeout, F func) { auto start_time = millis(); while (func()) { @@ -127,13 +132,18 @@ class OpenthermHub : public Component { OPENTHERM_INPUT_SENSOR_LIST(OPENTHERM_SET_INPUT_SENSOR, ) + OPENTHERM_SETTING_LIST(OPENTHERM_SET_SETTING, ) + // Add a request to the vector of initial requests - void add_initial_message(MessageId message_id) { this->initial_messages_.push_back(message_id); } + void add_initial_message(MessageId message_id) { + this->configured_messages_[message_id] = INITIAL_UNORDERED_MESSAGE_ORDER; + } + void add_initial_message(MessageId message_id, uint8_t order) { this->configured_messages_[message_id] = order; } // Add a request to the set of repeating requests. Note that a large number of repeating // requests will slow down communication with the boiler. Each request may take up to 1 second, // so with all sensors enabled, it may take about half a minute before a change in setpoint // will be processed. - void add_repeating_message(MessageId message_id) { this->repeating_messages_.push_back(message_id); } + void add_repeating_message(MessageId message_id) { this->configured_messages_[message_id] = REPEATING_MESSAGE_ORDER; } // There are seven status variables, which can either be set as a simple variable, // or using a switch. ch_enable and dhw_enable default to true, the others to false. @@ -149,7 +159,13 @@ class OpenthermHub : public Component { void set_summer_mode_active(bool value) { this->summer_mode_active = value; } void set_dhw_block(bool value) { this->dhw_block = value; } void set_sync_mode(bool sync_mode) { this->sync_mode_ = sync_mode; } - void set_opentherm_version(float value) { this->opentherm_version_ = value; } + + void add_on_before_send_callback(std::function &&callback) { + this->before_send_callback_.add(std::move(callback)); + } + void add_on_before_process_response_callback(std::function &&callback) { + this->before_process_response_callback_.add(std::move(callback)); + } float get_setup_priority() const override { return setup_priority::HARDWARE; } diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 62ab1d3860..49482316ee 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -52,7 +52,9 @@ bool OpenTherm::initialize() { OpenTherm::instance = this; #endif this->in_pin_->pin_mode(gpio::FLAG_INPUT); + this->in_pin_->setup(); this->out_pin_->pin_mode(gpio::FLAG_OUTPUT); + this->out_pin_->setup(); this->out_pin_->digital_write(true); #if defined(ESP32) || defined(USE_ESP_IDF) @@ -182,7 +184,7 @@ bool IRAM_ATTR OpenTherm::timer_isr(OpenTherm *arg) { } arg->capture_ = 1; // reset counter } else if (arg->capture_ > 0xFF) { - // no change for too long, invalid mancheter encoding + // no change for too long, invalid manchester encoding arg->mode_ = OperationMode::ERROR_PROTOCOL; arg->error_type_ = ProtocolErrorType::NO_CHANGE_TOO_LONG; arg->stop_timer_(); @@ -312,21 +314,31 @@ bool OpenTherm::init_esp32_timer_() { } void IRAM_ATTR OpenTherm::start_esp32_timer_(uint64_t alarm_value) { - esp_err_t result; + // We will report timer errors outside of interrupt handler + this->timer_error_ = ESP_OK; + this->timer_error_type_ = TimerErrorType::NO_TIMER_ERROR; - result = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value); - if (result != ESP_OK) { - const auto *error = esp_err_to_name(result); - ESP_LOGE(TAG, "Failed to set alarm value. Error: %s", error); + this->timer_error_ = timer_set_alarm_value(this->timer_group_, this->timer_idx_, alarm_value); + if (this->timer_error_ != ESP_OK) { + this->timer_error_type_ = TimerErrorType::SET_ALARM_VALUE_ERROR; + return; + } + this->timer_error_ = timer_start(this->timer_group_, this->timer_idx_); + if (this->timer_error_ != ESP_OK) { + this->timer_error_type_ = TimerErrorType::TIMER_START_ERROR; + } +} + +void OpenTherm::report_and_reset_timer_error() { + if (this->timer_error_ == ESP_OK) { return; } - result = timer_start(this->timer_group_, this->timer_idx_); - if (result != ESP_OK) { - const auto *error = esp_err_to_name(result); - ESP_LOGE(TAG, "Failed to start the timer. Error: %s", error); - return; - } + ESP_LOGE(TAG, "Error occured while manipulating timer (%s): %s", this->timer_error_to_str(this->timer_error_type_), + esp_err_to_name(this->timer_error_)); + + this->timer_error_ = ESP_OK; + this->timer_error_type_ = NO_TIMER_ERROR; } // 5 kHz timer_ @@ -343,21 +355,18 @@ void IRAM_ATTR OpenTherm::start_write_timer_() { void IRAM_ATTR OpenTherm::stop_timer_() { InterruptLock const lock; + // We will report timer errors outside of interrupt handler + this->timer_error_ = ESP_OK; + this->timer_error_type_ = TimerErrorType::NO_TIMER_ERROR; - esp_err_t result; - - result = timer_pause(this->timer_group_, this->timer_idx_); - if (result != ESP_OK) { - const auto *error = esp_err_to_name(result); - ESP_LOGE(TAG, "Failed to pause the timer. Error: %s", error); + this->timer_error_ = timer_pause(this->timer_group_, this->timer_idx_); + if (this->timer_error_ != ESP_OK) { + this->timer_error_type_ = TimerErrorType::TIMER_PAUSE_ERROR; return; } - - result = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0); - if (result != ESP_OK) { - const auto *error = esp_err_to_name(result); - ESP_LOGE(TAG, "Failed to set timer counter to 0 after pausing. Error: %s", error); - return; + this->timer_error_ = timer_set_counter_value(this->timer_group_, this->timer_idx_, 0); + if (this->timer_error_ != ESP_OK) { + this->timer_error_type_ = TimerErrorType::SET_COUNTER_VALUE_ERROR; } } @@ -386,6 +395,9 @@ void IRAM_ATTR OpenTherm::stop_timer_() { timer1_detachInterrupt(); } +// There is nothing to report on ESP8266 +void OpenTherm::report_and_reset_timer_error() {} + #endif // END ESP8266 // https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd @@ -412,11 +424,12 @@ const char *OpenTherm::operation_mode_to_str(OperationMode mode) { TO_STRING_MEMBER(SENT) TO_STRING_MEMBER(ERROR_PROTOCOL) TO_STRING_MEMBER(ERROR_TIMEOUT) + TO_STRING_MEMBER(ERROR_TIMER) default: return ""; } } -const char *OpenTherm::protocol_error_to_to_str(ProtocolErrorType error_type) { +const char *OpenTherm::protocol_error_to_str(ProtocolErrorType error_type) { switch (error_type) { TO_STRING_MEMBER(NO_ERROR) TO_STRING_MEMBER(NO_TRANSITION) @@ -427,6 +440,17 @@ const char *OpenTherm::protocol_error_to_to_str(ProtocolErrorType error_type) { return ""; } } +const char *OpenTherm::timer_error_to_str(TimerErrorType error_type) { + switch (error_type) { + TO_STRING_MEMBER(NO_TIMER_ERROR) + TO_STRING_MEMBER(SET_ALARM_VALUE_ERROR) + TO_STRING_MEMBER(TIMER_START_ERROR) + TO_STRING_MEMBER(TIMER_PAUSE_ERROR) + TO_STRING_MEMBER(SET_COUNTER_VALUE_ERROR) + default: + return ""; + } +} const char *OpenTherm::message_type_to_str(MessageType message_type) { switch (message_type) { TO_STRING_MEMBER(READ_DATA) diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 3be0191c63..4280832d09 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -36,11 +36,12 @@ enum OperationMode { READ = 2, // reading 32-bit data frame RECEIVED = 3, // data frame received with valid start and stop bit - WRITE = 4, // writing data with timer_ + WRITE = 4, // writing data to output SENT = 5, // all data written to output - ERROR_PROTOCOL = 8, // manchester protocol data transfer error - ERROR_TIMEOUT = 9 // read timeout + ERROR_PROTOCOL = 8, // protocol error, can happed only during READ + ERROR_TIMEOUT = 9, // timeout while waiting for response from device, only during LISTEN + ERROR_TIMER = 10 // error operating the ESP32 timer }; enum ProtocolErrorType { @@ -51,6 +52,14 @@ enum ProtocolErrorType { NO_CHANGE_TOO_LONG = 4, // No level change for too much timer ticks }; +enum TimerErrorType { + NO_TIMER_ERROR = 0, // No error + SET_ALARM_VALUE_ERROR = 1, // No transition in the middle of the bit + TIMER_START_ERROR = 2, // Stop bit wasn't present when expected + TIMER_PAUSE_ERROR = 3, // Parity check didn't pass + SET_COUNTER_VALUE_ERROR = 4, // No level change for too much timer ticks +}; + enum MessageType { READ_DATA = 0, READ_ACK = 4, @@ -299,7 +308,9 @@ class OpenTherm { * * @return true if last listen() or send() operation ends up with an error. */ - bool is_error() { return mode_ == OperationMode::ERROR_TIMEOUT || mode_ == OperationMode::ERROR_PROTOCOL; } + bool is_error() { + return mode_ == OperationMode::ERROR_TIMEOUT || mode_ == OperationMode::ERROR_PROTOCOL || mode_ == ERROR_TIMER; + } /** * Indicates whether last listen() or send() operation ends up with a *timeout* error @@ -313,14 +324,22 @@ class OpenTherm { */ bool is_protocol_error() { return mode_ == OperationMode::ERROR_PROTOCOL; } + /** + * Indicates whether start_esp32_timer_() or stop_timer_() had an error. Only relevant when used on ESP32. + * @return true if there was an error. + */ + bool is_timer_error() { return mode_ == OperationMode::ERROR_TIMER; } + bool is_active() { return mode_ == LISTEN || mode_ == READ || mode_ == WRITE; } OperationMode get_mode() { return mode_; } void debug_data(OpenthermData &data); void debug_error(OpenThermError &error) const; + void report_and_reset_timer_error(); - const char *protocol_error_to_to_str(ProtocolErrorType error_type); + const char *protocol_error_to_str(ProtocolErrorType error_type); + const char *timer_error_to_str(TimerErrorType error_type); const char *message_type_to_str(MessageType message_type); const char *operation_mode_to_str(OperationMode mode); const char *message_id_to_str(MessageId id); @@ -349,10 +368,12 @@ class OpenTherm { uint32_t data_; uint8_t bit_pos_; int32_t timeout_counter_; // <0 no timeout - int32_t device_timeout_; #if defined(ESP32) || defined(USE_ESP_IDF) + esp_err_t timer_error_ = ESP_OK; + TimerErrorType timer_error_type_ = TimerErrorType::NO_TIMER_ERROR; + bool init_esp32_timer_(); void start_esp32_timer_(uint64_t alarm_value); #endif diff --git a/esphome/components/opentherm/opentherm_macros.h b/esphome/components/opentherm/opentherm_macros.h index 8aaec0b48a..398c64aa8f 100644 --- a/esphome/components/opentherm/opentherm_macros.h +++ b/esphome/components/opentherm/opentherm_macros.h @@ -28,6 +28,9 @@ namespace opentherm { #ifndef OPENTHERM_INPUT_SENSOR_LIST #define OPENTHERM_INPUT_SENSOR_LIST(F, sep) #endif +#ifndef OPENTHERM_SETTING_LIST +#define OPENTHERM_SETTING_LIST(F, sep) +#endif // Use macros to create fields for every entity specified in the ESPHome configuration #define OPENTHERM_DECLARE_SENSOR(entity) sensor::Sensor *entity; @@ -36,6 +39,7 @@ namespace opentherm { #define OPENTHERM_DECLARE_NUMBER(entity) OpenthermNumber *entity; #define OPENTHERM_DECLARE_OUTPUT(entity) OpenthermOutput *entity; #define OPENTHERM_DECLARE_INPUT_SENSOR(entity) sensor::Sensor *entity; +#define OPENTHERM_DECLARE_SETTING(type, entity, def) type entity = def; // Setter macros #define OPENTHERM_SET_SENSOR(entity) \ @@ -56,6 +60,9 @@ namespace opentherm { #define OPENTHERM_SET_INPUT_SENSOR(entity) \ void set_##entity(sensor::Sensor *sensor) { this->entity = sensor; } +#define OPENTHERM_SET_SETTING(type, entity, def) \ + void set_##entity(type value) { this->entity = value; } + // ===== hub.cpp macros ===== // *_MESSAGE_HANDLERS are generated in defines.h and look like this: @@ -85,6 +92,9 @@ namespace opentherm { #ifndef OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS #define OPENTHERM_INPUT_SENSOR_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) #endif +#ifndef OPENTHERM_SETTING_MESSAGE_HANDLERS +#define OPENTHERM_SETTING_MESSAGE_HANDLERS(MESSAGE, ENTITY, entity_sep, postscript, msg_sep) +#endif // Write data request builders #define OPENTHERM_MESSAGE_WRITE_MESSAGE(msg) \ @@ -92,6 +102,7 @@ namespace opentherm { data.type = MessageType::WRITE_DATA; \ data.id = request_id; #define OPENTHERM_MESSAGE_WRITE_ENTITY(key, msg_data) message_data::write_##msg_data(this->key->state, data); +#define OPENTHERM_MESSAGE_WRITE_SETTING(key, msg_data) message_data::write_##msg_data(this->key, data); #define OPENTHERM_MESSAGE_WRITE_POSTSCRIPT \ return data; \ } diff --git a/esphome/components/opentherm/schema.py b/esphome/components/opentherm/schema.py index fe0f2a77a3..a58de8e2da 100644 --- a/esphome/components/opentherm/schema.py +++ b/esphome/components/opentherm/schema.py @@ -2,8 +2,9 @@ # inputs of the OpenTherm component. from dataclasses import dataclass -from typing import Optional, TypeVar +from typing import Optional, TypeVar, Any +import esphome.config_validation as cv from esphome.const import ( UNIT_CELSIUS, UNIT_EMPTY, @@ -64,6 +65,7 @@ class SensorSchema(EntitySchema): icon: Optional[str] = None device_class: Optional[str] = None disabled_by_default: bool = False + order: Optional[int] = None SENSORS: dict[str, SensorSchema] = { @@ -399,6 +401,7 @@ SENSORS: dict[str, SensorSchema] = { message="OT_VERSION_DEVICE", keep_updated=False, message_data="f88", + order=2, ), "device_type": SensorSchema( description="Device product type", @@ -409,6 +412,7 @@ SENSORS: dict[str, SensorSchema] = { message="VERSION_DEVICE", keep_updated=False, message_data="u8_hb", + order=0, ), "device_version": SensorSchema( description="Device product version", @@ -419,6 +423,7 @@ SENSORS: dict[str, SensorSchema] = { message="VERSION_DEVICE", keep_updated=False, message_data="u8_lb", + order=0, ), "device_id": SensorSchema( description="Device ID code", @@ -429,6 +434,7 @@ SENSORS: dict[str, SensorSchema] = { message="DEVICE_CONFIG", keep_updated=False, message_data="u8_lb", + order=4, ), "otc_hc_ratio_ub": SensorSchema( description="OTC heat curve ratio upper bound", @@ -457,6 +463,7 @@ SENSORS: dict[str, SensorSchema] = { class BinarySensorSchema(EntitySchema): icon: Optional[str] = None device_class: Optional[str] = None + order: Optional[int] = None BINARY_SENSORS: dict[str, BinarySensorSchema] = { @@ -525,48 +532,56 @@ BINARY_SENSORS: dict[str, BinarySensorSchema] = { message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_0", + order=4, ), "control_type_on_off": BinarySensorSchema( description="Configuration: Control type is on/off", message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_1", + order=4, ), "cooling_supported": BinarySensorSchema( description="Configuration: Cooling supported", message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_2", + order=4, ), "dhw_storage_tank": BinarySensorSchema( description="Configuration: DHW storage tank", message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_3", + order=4, ), "controller_pump_control_allowed": BinarySensorSchema( description="Configuration: Controller pump control allowed", message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_4", + order=4, ), "ch2_present": BinarySensorSchema( description="Configuration: CH2 present", message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_5", + order=4, ), "water_filling": BinarySensorSchema( description="Configuration: Remote water filling", message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_6", + order=4, ), "heat_mode": BinarySensorSchema( description="Configuration: Heating or cooling", message="DEVICE_CONFIG", keep_updated=False, message_data="flag8_hb_7", + order=4, ), "dhw_setpoint_transfer_enabled": BinarySensorSchema( description="Remote boiler parameters: DHW setpoint transfer enabled", @@ -812,3 +827,65 @@ INPUTS: dict[str, InputSchema] = { auto_max_value=AutoConfigure(message="OTC_CURVE_BOUNDS", message_data="u8_hb"), ), } + + +@dataclass +class SettingSchema(EntitySchema): + backing_type: str + validation_schema: cv.Schema + default_value: Any + order: Optional[int] = None + + +SETTINGS: dict[str, SettingSchema] = { + "controller_product_type": SettingSchema( + description="Controller product type", + message="VERSION_CONTROLLER", + keep_updated=False, + message_data="u8_hb", + backing_type="uint8_t", + validation_schema=cv.int_range(min=0, max=255), + default_value=0, + order=1, + ), + "controller_product_version": SettingSchema( + description="Controller product version", + message="VERSION_CONTROLLER", + keep_updated=False, + message_data="u8_lb", + backing_type="uint8_t", + validation_schema=cv.int_range(min=0, max=255), + default_value=0, + order=1, + ), + "opentherm_version_controller": SettingSchema( + description="Version of OpenTherm implemented by controller", + message="OT_VERSION_CONTROLLER", + keep_updated=False, + message_data="f88", + backing_type="float", + validation_schema=cv.positive_float, + default_value=0, + order=3, + ), + "controller_configuration": SettingSchema( + description="Controller configuration", + message="CONTROLLER_CONFIG", + keep_updated=False, + message_data="u8_hb", + backing_type="uint8_t", + validation_schema=cv.int_range(min=0, max=255), + default_value=0, + order=5, + ), + "controller_id": SettingSchema( + description="Controller ID code", + message="CONTROLLER_CONFIG", + keep_updated=False, + message_data="u8_lb", + backing_type="uint8_t", + validation_schema=cv.int_range(min=0, max=255), + default_value=0, + order=5, + ), +} diff --git a/esphome/components/opentherm/validate.py b/esphome/components/opentherm/validate.py index d4507672a5..055cbfa827 100644 --- a/esphome/components/opentherm/validate.py +++ b/esphome/components/opentherm/validate.py @@ -9,12 +9,17 @@ from .schema import TSchema def create_entities_schema( - entities: dict[str, schema.EntitySchema], + entities: dict[str, TSchema], get_entity_validation_schema: Callable[[TSchema], cv.Schema], ) -> Schema: entity_schema = {} for key, entity in entities.items(): - entity_schema[cv.Optional(key)] = get_entity_validation_schema(entity) + schema_key = ( + cv.Optional(key, entity.default_value) + if hasattr(entity, "default_value") + else cv.Optional(key) + ) + entity_schema[schema_key] = get_entity_validation_schema(entity) return cv.Schema(entity_schema) diff --git a/tests/components/opentherm/common.yaml b/tests/components/opentherm/common.yaml index 744580f18b..5edacc6f17 100644 --- a/tests/components/opentherm/common.yaml +++ b/tests/components/opentherm/common.yaml @@ -16,6 +16,19 @@ opentherm: summer_mode_active: true dhw_block: true sync_mode: true + controller_product_type: 63 + controller_product_version: 1 + opentherm_version_controller: 2.2 + controller_id: 1 + controller_configuration: 1 + before_send: + then: + - lambda: |- + ESP_LOGW("OT", ">> Sending message %d", x.id); + before_process_response: + then: + - lambda: |- + ESP_LOGW("OT", "<< Processing response %d", x.id); output: - platform: opentherm From e04743e38196f59eb4e23b59756be6d71ed154c1 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 16 Dec 2024 00:12:45 +0100 Subject: [PATCH 0804/1052] [debug] Detailed reset reason (#7729) Co-authored-by: Ramil Valitov --- esphome/components/debug/debug_esp32.cpp | 201 ++++++++++++++--------- 1 file changed, 127 insertions(+), 74 deletions(-) diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index cb4330f422..5f7b9cdbb0 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -30,109 +30,162 @@ static const char *const TAG = "debug"; std::string DebugComponent::get_reset_reason_() { std::string reset_reason; - switch (rtc_get_reset_reason(0)) { - case POWERON_RESET: - reset_reason = "Power On Reset"; + switch (esp_reset_reason()) { + case ESP_RST_POWERON: + reset_reason = "Reset due to power-on event"; break; + case ESP_RST_EXT: + reset_reason = "Reset by external pin"; + break; + case ESP_RST_SW: + reset_reason = "Software reset via esp_restart"; + break; + case ESP_RST_PANIC: + reset_reason = "Software reset due to exception/panic"; + break; + case ESP_RST_INT_WDT: + reset_reason = "Reset (software or hardware) due to interrupt watchdog"; + break; + case ESP_RST_TASK_WDT: + reset_reason = "Reset due to task watchdog"; + break; + case ESP_RST_WDT: + reset_reason = "Reset due to other watchdogs"; + break; + case ESP_RST_DEEPSLEEP: + reset_reason = "Reset after exiting deep sleep mode"; + break; + case ESP_RST_BROWNOUT: + reset_reason = "Brownout reset (software or hardware)"; + break; + case ESP_RST_SDIO: + reset_reason = "Reset over SDIO"; + break; +#ifdef USE_ESP32_VARIANT_ESP32 +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4)) + case ESP_RST_USB: + reset_reason = "Reset by USB peripheral"; + break; + case ESP_RST_JTAG: + reset_reason = "Reset by JTAG"; + break; + case ESP_RST_EFUSE: + reset_reason = "Reset due to efuse error"; + break; + case ESP_RST_PWR_GLITCH: + reset_reason = "Reset due to power glitch detected"; + break; + case ESP_RST_CPU_LOCKUP: + reset_reason = "Reset due to CPU lock up (double exception)"; + break; +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 4) +#endif // USE_ESP32_VARIANT_ESP32 + default: // Includes ESP_RST_UNKNOWN + switch (rtc_get_reset_reason(0)) { + case POWERON_RESET: + reset_reason = "Power On Reset"; + break; #if defined(USE_ESP32_VARIANT_ESP32) - case SW_RESET: + case SW_RESET: #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) - case RTC_SW_SYS_RESET: + case RTC_SW_SYS_RESET: #endif - reset_reason = "Software Reset Digital Core"; - break; + reset_reason = "Software Reset Digital Core"; + break; #if defined(USE_ESP32_VARIANT_ESP32) - case OWDT_RESET: - reset_reason = "Watch Dog Reset Digital Core"; - break; + case OWDT_RESET: + reset_reason = "Watch Dog Reset Digital Core"; + break; #endif - case DEEPSLEEP_RESET: - reset_reason = "Deep Sleep Reset Digital Core"; - break; + case DEEPSLEEP_RESET: + reset_reason = "Deep Sleep Reset Digital Core"; + break; #if defined(USE_ESP32_VARIANT_ESP32) - case SDIO_RESET: - reset_reason = "SLC Module Reset Digital Core"; - break; + case SDIO_RESET: + reset_reason = "SLC Module Reset Digital Core"; + break; #endif - case TG0WDT_SYS_RESET: - reset_reason = "Timer Group 0 Watch Dog Reset Digital Core"; - break; - case TG1WDT_SYS_RESET: - reset_reason = "Timer Group 1 Watch Dog Reset Digital Core"; - break; - case RTCWDT_SYS_RESET: - reset_reason = "RTC Watch Dog Reset Digital Core"; - break; + case TG0WDT_SYS_RESET: + reset_reason = "Timer Group 0 Watch Dog Reset Digital Core"; + break; + case TG1WDT_SYS_RESET: + reset_reason = "Timer Group 1 Watch Dog Reset Digital Core"; + break; + case RTCWDT_SYS_RESET: + reset_reason = "RTC Watch Dog Reset Digital Core"; + break; #if !defined(USE_ESP32_VARIANT_ESP32C6) && !defined(USE_ESP32_VARIANT_ESP32H2) - case INTRUSION_RESET: - reset_reason = "Intrusion Reset CPU"; - break; + case INTRUSION_RESET: + reset_reason = "Intrusion Reset CPU"; + break; #endif #if defined(USE_ESP32_VARIANT_ESP32) - case TGWDT_CPU_RESET: - reset_reason = "Timer Group Reset CPU"; - break; + case TGWDT_CPU_RESET: + reset_reason = "Timer Group Reset CPU"; + break; #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) - case TG0WDT_CPU_RESET: - reset_reason = "Timer Group 0 Reset CPU"; - break; + case TG0WDT_CPU_RESET: + reset_reason = "Timer Group 0 Reset CPU"; + break; #endif #if defined(USE_ESP32_VARIANT_ESP32) - case SW_CPU_RESET: + case SW_CPU_RESET: #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || \ defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) - case RTC_SW_CPU_RESET: + case RTC_SW_CPU_RESET: #endif - reset_reason = "Software Reset CPU"; - break; - case RTCWDT_CPU_RESET: - reset_reason = "RTC Watch Dog Reset CPU"; - break; + reset_reason = "Software Reset CPU"; + break; + case RTCWDT_CPU_RESET: + reset_reason = "RTC Watch Dog Reset CPU"; + break; #if defined(USE_ESP32_VARIANT_ESP32) - case EXT_CPU_RESET: - reset_reason = "External CPU Reset"; - break; + case EXT_CPU_RESET: + reset_reason = "External CPU Reset"; + break; #endif - case RTCWDT_BROWN_OUT_RESET: - reset_reason = "Voltage Unstable Reset"; - break; - case RTCWDT_RTC_RESET: - reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; - break; + case RTCWDT_BROWN_OUT_RESET: + reset_reason = "Voltage Unstable Reset"; + break; + case RTCWDT_RTC_RESET: + reset_reason = "RTC Watch Dog Reset Digital Core And RTC Module"; + break; #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || \ defined(USE_ESP32_VARIANT_ESP32C6) - case TG1WDT_CPU_RESET: - reset_reason = "Timer Group 1 Reset CPU"; - break; - case SUPER_WDT_RESET: - reset_reason = "Super Watchdog Reset Digital Core And RTC Module"; - break; - case EFUSE_RESET: - reset_reason = "eFuse Reset Digital Core"; - break; + case TG1WDT_CPU_RESET: + reset_reason = "Timer Group 1 Reset CPU"; + break; + case SUPER_WDT_RESET: + reset_reason = "Super Watchdog Reset Digital Core And RTC Module"; + break; + case EFUSE_RESET: + reset_reason = "eFuse Reset Digital Core"; + break; #endif #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) - case GLITCH_RTC_RESET: - reset_reason = "Glitch Reset Digital Core And RTC Module"; - break; + case GLITCH_RTC_RESET: + reset_reason = "Glitch Reset Digital Core And RTC Module"; + break; #endif #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32C6) - case USB_UART_CHIP_RESET: - reset_reason = "USB UART Reset Digital Core"; - break; - case USB_JTAG_CHIP_RESET: - reset_reason = "USB JTAG Reset Digital Core"; - break; + case USB_UART_CHIP_RESET: + reset_reason = "USB UART Reset Digital Core"; + break; + case USB_JTAG_CHIP_RESET: + reset_reason = "USB JTAG Reset Digital Core"; + break; #endif #if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S3) - case POWER_GLITCH_RESET: - reset_reason = "Power Glitch Reset Digital Core And RTC Module"; - break; + case POWER_GLITCH_RESET: + reset_reason = "Power Glitch Reset Digital Core And RTC Module"; + break; #endif - default: - reset_reason = "Unknown Reset Reason"; + default: + reset_reason = "Unknown Reset Reason"; + } + break; } ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str()); return reset_reason; @@ -294,4 +347,4 @@ void DebugComponent::update_platform_() { } // namespace debug } // namespace esphome -#endif +#endif // USE_ESP32 From 1a89aa8fbfb5828f6c3bbf72343b72b8ce581187 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:52:34 +1300 Subject: [PATCH 0805/1052] [uart] Use ``SOC_UART_NUM`` as number of uarts instead of ``UART_NUM_MAX`` (#7967) --- esphome/components/uart/uart_component_esp32_arduino.cpp | 4 ++-- esphome/components/uart/uart_component_esp_idf.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/uart/uart_component_esp32_arduino.cpp b/esphome/components/uart/uart_component_esp32_arduino.cpp index 793c1d52f4..b241c03de4 100644 --- a/esphome/components/uart/uart_component_esp32_arduino.cpp +++ b/esphome/components/uart/uart_component_esp32_arduino.cpp @@ -1,9 +1,9 @@ #ifdef USE_ESP32_FRAMEWORK_ARDUINO +#include "uart_component_esp32_arduino.h" #include "esphome/core/application.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include "uart_component_esp32_arduino.h" #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -118,7 +118,7 @@ void ESP32ArduinoUARTComponent::setup() { } #endif // USE_LOGGER - if (next_uart_num >= UART_NUM_MAX) { + if (next_uart_num >= SOC_UART_NUM) { ESP_LOGW(TAG, "Maximum number of UART components created already."); this->mark_failed(); return; diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 6999dfb619..122d4105c8 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -1,11 +1,11 @@ #ifdef USE_ESP_IDF #include "uart_component_esp_idf.h" +#include #include "esphome/core/application.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#include #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" @@ -84,7 +84,7 @@ void IDFUARTComponent::setup() { } #endif // USE_LOGGER - if (next_uart_num >= UART_NUM_MAX) { + if (next_uart_num >= SOC_UART_NUM) { ESP_LOGW(TAG, "Maximum number of UART components created already."); this->mark_failed(); return; From 663e18310dbd46f96281fb554e2ad2aa495b22d8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:58:42 +1300 Subject: [PATCH 0806/1052] [ci] Dont run main ci suite on docker files (#7966) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ce4159da0..a344b177ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ on: - ".github/workflows/ci.yml" - "!.yamllint" - "!.github/dependabot.yml" + - "!docker/**" merge_group: permissions: From 9f6c64afa64155435f7191862d42896cb57839c9 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:07:43 +1100 Subject: [PATCH 0807/1052] [font] cleanly handle font file format exception (Bugfix) (#7970) --- esphome/components/font/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index c397ba8306..e2051298fe 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -51,8 +51,11 @@ CONF_IGNORE_MISSING_GLYPHS = "ignore_missing_glyphs" # Cache loaded freetype fonts class FontCache(dict): def __missing__(self, key): - res = self[key] = freetype.Face(key) - return res + try: + res = self[key] = freetype.Face(key) + return res + except freetype.FT_Exception as e: + raise cv.Invalid(f"Could not load Font file {key}: {e}") from e FONT_CACHE = FontCache() From 3d56397e5881baf38d6afa108659af50fbad4e18 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:09:09 +1300 Subject: [PATCH 0808/1052] Bump docker/setup-buildx-action from 3.7.1 to 3.8.0 in the docker-actions group (#7969) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 435a58498e..cf7b08f3d4 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -46,7 +46,7 @@ jobs: with: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.7.1 + uses: docker/setup-buildx-action@v3.8.0 - name: Set up QEMU uses: docker/setup-qemu-action@v3.2.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4e4305207..6cd86854b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,7 +90,7 @@ jobs: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.7.1 + uses: docker/setup-buildx-action@v3.8.0 - name: Set up QEMU if: matrix.platform != 'linux/amd64' uses: docker/setup-qemu-action@v3.2.0 @@ -184,7 +184,7 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.7.1 + uses: docker/setup-buildx-action@v3.8.0 - name: Log in to docker hub if: matrix.registry == 'dockerhub' From 759df7ae6c98e6ea656bcd0f56662204c0f6a963 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:26:16 +1300 Subject: [PATCH 0809/1052] [dashboard] Accept basic auth header (#7965) --- esphome/dashboard/web_server.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 0fed8e9c53..67712da9b6 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -108,6 +108,12 @@ def is_authenticated(handler: BaseHandler) -> bool: return True if settings.using_auth: + if auth_header := handler.request.headers.get("Authorization"): + assert isinstance(auth_header, str) + if auth_header.startswith("Basic "): + auth_decoded = base64.b64decode(auth_header[6:]).decode() + username, password = auth_decoded.split(":", 1) + return settings.check_password(username, password) return handler.get_secure_cookie(AUTH_COOKIE_NAME) == COOKIE_AUTHENTICATED_YES return True From 54fbf5184ed985a9b30d4a7688dcc3656c96fb8f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:32:52 +1300 Subject: [PATCH 0810/1052] Bump esphome-dashboard to 20241217.1 (#7971) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7bc1c895df..d96004c8ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20241120.0 +esphome-dashboard==20241217.1 aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 From ccc9fd4a3f30472a11071622963d1284aa3016fe Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Dec 2024 07:10:38 +1300 Subject: [PATCH 0811/1052] [esp32_ble] Use RAMAllocator to avoid panic abort from ``new`` (#7936) --- esphome/components/esp32_ble/ble.cpp | 33 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index d7dcf93f86..b10e454c21 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -27,6 +27,9 @@ namespace esp32_ble { static const char *const TAG = "esp32_ble"; +static RAMAllocator EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + RAMAllocator::ALLOW_FAILURE | RAMAllocator::ALLOC_INTERNAL); + void ESP32BLE::setup() { global_ble = this; ESP_LOGCONFIG(TAG, "Setting up BLE..."); @@ -322,7 +325,8 @@ void ESP32BLE::loop() { default: break; } - delete ble_event; // NOLINT(cppcoreguidelines-owning-memory) + ble_event->~BLEEvent(); + EVENT_ALLOCATOR.deallocate(ble_event, 1); ble_event = this->ble_events_.pop(); } if (this->advertising_ != nullptr) { @@ -331,9 +335,14 @@ void ESP32BLE::loop() { } void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, param); // NOLINT(cppcoreguidelines-owning-memory) + BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); + if (new_event == nullptr) { + // Memory too fragmented to allocate new event. Can only drop it until memory comes back + return; + } + new (new_event) BLEEvent(event, param); global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) +} // NOLINT(clang-analyzer-unix.Malloc) void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event); @@ -344,9 +353,14 @@ void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, gatts_if, param); // NOLINT(cppcoreguidelines-owning-memory) + BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); + if (new_event == nullptr) { + // Memory too fragmented to allocate new event. Can only drop it until memory comes back + return; + } + new (new_event) BLEEvent(event, gatts_if, param); global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) +} // NOLINT(clang-analyzer-unix.Malloc) void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { @@ -358,9 +372,14 @@ void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, gattc_if, param); // NOLINT(cppcoreguidelines-owning-memory) + BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); + if (new_event == nullptr) { + // Memory too fragmented to allocate new event. Can only drop it until memory comes back + return; + } + new (new_event) BLEEvent(event, gattc_if, param); global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) +} // NOLINT(clang-analyzer-unix.Malloc) void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { From e890486043f48b2995cc0c34c0455de12a10aebd Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:07:43 +1100 Subject: [PATCH 0812/1052] [font] cleanly handle font file format exception (Bugfix) (#7970) --- esphome/components/font/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index c397ba8306..e2051298fe 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -51,8 +51,11 @@ CONF_IGNORE_MISSING_GLYPHS = "ignore_missing_glyphs" # Cache loaded freetype fonts class FontCache(dict): def __missing__(self, key): - res = self[key] = freetype.Face(key) - return res + try: + res = self[key] = freetype.Face(key) + return res + except freetype.FT_Exception as e: + raise cv.Invalid(f"Could not load Font file {key}: {e}") from e FONT_CACHE = FontCache() From c38826824ff872229045623644ae211245b5b1ba Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:26:16 +1300 Subject: [PATCH 0813/1052] [dashboard] Accept basic auth header (#7965) --- esphome/dashboard/web_server.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 0fed8e9c53..67712da9b6 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -108,6 +108,12 @@ def is_authenticated(handler: BaseHandler) -> bool: return True if settings.using_auth: + if auth_header := handler.request.headers.get("Authorization"): + assert isinstance(auth_header, str) + if auth_header.startswith("Basic "): + auth_decoded = base64.b64decode(auth_header[6:]).decode() + username, password = auth_decoded.split(":", 1) + return settings.check_password(username, password) return handler.get_secure_cookie(AUTH_COOKIE_NAME) == COOKIE_AUTHENTICATED_YES return True From 766160904989c0d59296b6c70c918a77e546ca8d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:32:52 +1300 Subject: [PATCH 0814/1052] Bump esphome-dashboard to 20241217.1 (#7971) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7bc1c895df..d96004c8ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20241120.0 +esphome-dashboard==20241217.1 aioesphomeapi==24.6.2 zeroconf==0.132.2 puremagic==1.27 From c86ea99145472a587618773f3cf14f6a9a8ed828 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Dec 2024 07:10:38 +1300 Subject: [PATCH 0815/1052] [esp32_ble] Use RAMAllocator to avoid panic abort from ``new`` (#7936) --- esphome/components/esp32_ble/ble.cpp | 33 ++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index d7dcf93f86..b10e454c21 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -27,6 +27,9 @@ namespace esp32_ble { static const char *const TAG = "esp32_ble"; +static RAMAllocator EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + RAMAllocator::ALLOW_FAILURE | RAMAllocator::ALLOC_INTERNAL); + void ESP32BLE::setup() { global_ble = this; ESP_LOGCONFIG(TAG, "Setting up BLE..."); @@ -322,7 +325,8 @@ void ESP32BLE::loop() { default: break; } - delete ble_event; // NOLINT(cppcoreguidelines-owning-memory) + ble_event->~BLEEvent(); + EVENT_ALLOCATOR.deallocate(ble_event, 1); ble_event = this->ble_events_.pop(); } if (this->advertising_ != nullptr) { @@ -331,9 +335,14 @@ void ESP32BLE::loop() { } void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, param); // NOLINT(cppcoreguidelines-owning-memory) + BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); + if (new_event == nullptr) { + // Memory too fragmented to allocate new event. Can only drop it until memory comes back + return; + } + new (new_event) BLEEvent(event, param); global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) +} // NOLINT(clang-analyzer-unix.Malloc) void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event); @@ -344,9 +353,14 @@ void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, gatts_if, param); // NOLINT(cppcoreguidelines-owning-memory) + BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); + if (new_event == nullptr) { + // Memory too fragmented to allocate new event. Can only drop it until memory comes back + return; + } + new (new_event) BLEEvent(event, gatts_if, param); global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) +} // NOLINT(clang-analyzer-unix.Malloc) void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { @@ -358,9 +372,14 @@ void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { - BLEEvent *new_event = new BLEEvent(event, gattc_if, param); // NOLINT(cppcoreguidelines-owning-memory) + BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1); + if (new_event == nullptr) { + // Memory too fragmented to allocate new event. Can only drop it until memory comes back + return; + } + new (new_event) BLEEvent(event, gattc_if, param); global_ble->ble_events_.push(new_event); -} // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) +} // NOLINT(clang-analyzer-unix.Malloc) void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { From 1a692364736aeb242390774fa3cfcc2fafc56531 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Dec 2024 07:43:38 +1300 Subject: [PATCH 0816/1052] Bump version to 2024.12.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 64b3f83b83..ae1b6e46b8 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.0b2" +__version__ = "2024.12.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 7554e954fe4a50ba8514c301f1bffd73ae62223a Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:12:14 -0500 Subject: [PATCH 0817/1052] [core] Add c6 and h2 to split default (#7974) Co-authored-by: Jonathan Swoboda --- esphome/config_validation.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index ebfb2631c3..38fd677a2a 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1660,6 +1660,12 @@ class SplitDefault(Optional): esp32_c3=vol.UNDEFINED, esp32_c3_arduino=vol.UNDEFINED, esp32_c3_idf=vol.UNDEFINED, + esp32_c6=vol.UNDEFINED, + esp32_c6_arduino=vol.UNDEFINED, + esp32_c6_idf=vol.UNDEFINED, + esp32_h2=vol.UNDEFINED, + esp32_h2_arduino=vol.UNDEFINED, + esp32_h2_idf=vol.UNDEFINED, rp2040=vol.UNDEFINED, bk72xx=vol.UNDEFINED, rtl87xx=vol.UNDEFINED, @@ -1691,6 +1697,18 @@ class SplitDefault(Optional): self._esp32_c3_idf_default = vol.default_factory( _get_priority_default(esp32_c3_idf, esp32_c3, esp32_idf, esp32) ) + self._esp32_c6_arduino_default = vol.default_factory( + _get_priority_default(esp32_c6_arduino, esp32_c6, esp32_arduino, esp32) + ) + self._esp32_c6_idf_default = vol.default_factory( + _get_priority_default(esp32_c6_idf, esp32_c6, esp32_idf, esp32) + ) + self._esp32_h2_arduino_default = vol.default_factory( + _get_priority_default(esp32_h2_arduino, esp32_h2, esp32_arduino, esp32) + ) + self._esp32_h2_idf_default = vol.default_factory( + _get_priority_default(esp32_h2_idf, esp32_h2, esp32_idf, esp32) + ) self._rp2040_default = vol.default_factory(rp2040) self._bk72xx_default = vol.default_factory(bk72xx) self._rtl87xx_default = vol.default_factory(rtl87xx) @@ -1704,6 +1722,8 @@ class SplitDefault(Optional): from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32.const import ( VARIANT_ESP32C3, + VARIANT_ESP32C6, + VARIANT_ESP32H2, VARIANT_ESP32S2, VARIANT_ESP32S3, ) @@ -1724,6 +1744,16 @@ class SplitDefault(Optional): return self._esp32_c3_arduino_default if CORE.using_esp_idf: return self._esp32_c3_idf_default + elif variant == VARIANT_ESP32C6: + if CORE.using_arduino: + return self._esp32_c6_arduino_default + if CORE.using_esp_idf: + return self._esp32_c6_idf_default + elif variant == VARIANT_ESP32H2: + if CORE.using_arduino: + return self._esp32_h2_arduino_default + if CORE.using_esp_idf: + return self._esp32_h2_idf_default else: if CORE.using_arduino: return self._esp32_arduino_default From d330e73c1e5c4289fc557ef5c2fc54625a1db997 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 18 Dec 2024 11:35:43 +1300 Subject: [PATCH 0818/1052] Bump version to 2024.12.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index ae1b6e46b8..e9d7612e4a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.0b3" +__version__ = "2024.12.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From a9d883b65a94d609e4851ee23bb34760f648e808 Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Wed, 18 Dec 2024 01:47:43 +0100 Subject: [PATCH 0819/1052] [midea] Add Fahrenheit support to `midea_ac.follow_me` action (#7762) --- esphome/components/midea/ac_automations.h | 4 +- esphome/components/midea/air_conditioner.cpp | 12 +++--- esphome/components/midea/air_conditioner.h | 2 +- esphome/components/midea/climate.py | 6 ++- esphome/components/midea/ir_transmitter.h | 41 +++++++++++++++++--- 5 files changed, 51 insertions(+), 14 deletions(-) diff --git a/esphome/components/midea/ac_automations.h b/esphome/components/midea/ac_automations.h index 5084fd1eec..e6fffa2511 100644 --- a/esphome/components/midea/ac_automations.h +++ b/esphome/components/midea/ac_automations.h @@ -19,10 +19,12 @@ template class MideaActionBase : public Action { template class FollowMeAction : public MideaActionBase { TEMPLATABLE_VALUE(float, temperature) + TEMPLATABLE_VALUE(bool, use_fahrenheit) TEMPLATABLE_VALUE(bool, beeper) void play(Ts... x) override { - this->parent_->do_follow_me(this->temperature_.value(x...), this->beeper_.value(x...)); + this->parent_->do_follow_me(this->temperature_.value(x...), this->use_fahrenheit_.value(x...), + this->beeper_.value(x...)); } }; diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index a823680d03..247aea0488 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -1,6 +1,7 @@ #ifdef USE_ARDUINO #include "esphome/core/log.h" +#include "esphome/core/helpers.h" #include "air_conditioner.h" #include "ac_adapter.h" #include @@ -121,7 +122,7 @@ void AirConditioner::dump_config() { /* ACTIONS */ -void AirConditioner::do_follow_me(float temperature, bool beeper) { +void AirConditioner::do_follow_me(float temperature, bool use_fahrenheit, bool beeper) { #ifdef USE_REMOTE_TRANSMITTER // Check if temperature is finite (not NaN or infinite) if (!std::isfinite(temperature)) { @@ -131,13 +132,14 @@ void AirConditioner::do_follow_me(float temperature, bool beeper) { // Round and convert temperature to long, then clamp and convert it to uint8_t uint8_t temp_uint8 = - static_cast(std::max(0L, std::min(static_cast(UINT8_MAX), std::lroundf(temperature)))); + static_cast(esphome::clamp(std::lroundf(temperature), 0L, static_cast(UINT8_MAX))); - ESP_LOGD(Constants::TAG, "Follow me action called with temperature: %f °C, rounded to: %u °C", temperature, - temp_uint8); + char temp_symbol = use_fahrenheit ? 'F' : 'C'; + ESP_LOGD(Constants::TAG, "Follow me action called with temperature: %.5f °%c, rounded to: %u °%c", temperature, + temp_symbol, temp_uint8, temp_symbol); // Create and transmit the data - IrFollowMeData data(temp_uint8, beeper); + IrFollowMeData data(temp_uint8, use_fahrenheit, beeper); this->transmitter_.transmit(data); #else ESP_LOGW(Constants::TAG, "Action needs remote_transmitter component"); diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index d809aa78f6..e70bd34e71 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -32,7 +32,7 @@ class AirConditioner : public ApplianceBase, /* ### ACTIONS ### */ /* ############### */ - void do_follow_me(float temperature, bool beeper = false); + void do_follow_me(float temperature, bool use_fahrenheit, bool beeper = false); void do_display_toggle(); void do_swing_step(); void do_beeper_on() { this->set_beeper_feedback(true); } diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index e5612796a3..b7fef5e1ab 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -18,6 +18,7 @@ from esphome.const import ( CONF_SUPPORTED_SWING_MODES, CONF_TIMEOUT, CONF_TEMPERATURE, + CONF_USE_FAHRENHEIT, DEVICE_CLASS_POWER, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_HUMIDITY, @@ -172,11 +173,10 @@ MIDEA_ACTION_BASE_SCHEMA = cv.Schema( ) # FollowMe action -MIDEA_FOLLOW_ME_MIN = 0 -MIDEA_FOLLOW_ME_MAX = 37 MIDEA_FOLLOW_ME_SCHEMA = cv.Schema( { cv.Required(CONF_TEMPERATURE): cv.templatable(cv.temperature), + cv.Optional(CONF_USE_FAHRENHEIT, default=False): cv.templatable(cv.boolean), cv.Optional(CONF_BEEPER, default=False): cv.templatable(cv.boolean), } ) @@ -186,6 +186,8 @@ MIDEA_FOLLOW_ME_SCHEMA = cv.Schema( async def follow_me_to_code(var, config, args): template_ = await cg.templatable(config[CONF_BEEPER], args, cg.bool_) cg.add(var.set_beeper(template_)) + template_ = await cg.templatable(config[CONF_USE_FAHRENHEIT], args, cg.bool_) + cg.add(var.set_use_fahrenheit(template_)) template_ = await cg.templatable(config[CONF_TEMPERATURE], args, cg.float_) cg.add(var.set_temperature(template_)) diff --git a/esphome/components/midea/ir_transmitter.h b/esphome/components/midea/ir_transmitter.h index a8b89f9b7b..eba8fc87f7 100644 --- a/esphome/components/midea/ir_transmitter.h +++ b/esphome/components/midea/ir_transmitter.h @@ -16,22 +16,53 @@ class IrFollowMeData : public IrData { IrFollowMeData() : IrData({MIDEA_TYPE_FOLLOW_ME, 0x82, 0x48, 0x7F, 0x1F}) {} // Copy from Base IrFollowMeData(const IrData &data) : IrData(data) {} - // Direct from temperature and beeper values + // Direct from temperature in celsius and beeper values IrFollowMeData(uint8_t temp, bool beeper = false) : IrFollowMeData() { - this->set_temp(temp); + this->set_temp(temp, false); + this->set_beeper(beeper); + } + // Direct from temperature, fahrenheit and beeper values + IrFollowMeData(uint8_t temp, bool fahrenheit, bool beeper) : IrFollowMeData() { + this->set_temp(temp, fahrenheit); this->set_beeper(beeper); } /* TEMPERATURE */ - uint8_t temp() const { return this->get_value_(4) - 1; } - void set_temp(uint8_t val) { this->set_value_(4, std::min(MAX_TEMP, val) + 1); } + uint8_t temp() const { + if (this->fahrenheit()) { + return this->get_value_(4) + 31; + } + return this->get_value_(4) - 1; + } + void set_temp(uint8_t val, bool fahrenheit = false) { + this->set_fahrenheit(fahrenheit); + if (this->fahrenheit()) { + // see https://github.com/esphome/feature-requests/issues/1627#issuecomment-1365639966 + val = esphome::clamp(val, MIN_TEMP_F, MAX_TEMP_F) - 31; + } else { + val = esphome::clamp(val, MIN_TEMP_C, MAX_TEMP_C) + 1; + } + this->set_value_(4, val); + } /* BEEPER */ bool beeper() const { return this->get_value_(3, 128); } void set_beeper(bool val) { this->set_mask_(3, val, 128); } + /* FAHRENHEIT */ + bool fahrenheit() const { return this->get_value_(2, 32); } + void set_fahrenheit(bool val) { this->set_mask_(2, val, 32); } + protected: - static const uint8_t MAX_TEMP = 37; + static const uint8_t MIN_TEMP_C = 0; + static const uint8_t MAX_TEMP_C = 37; + + // see + // https://github.com/crankyoldgit/IRremoteESP8266/blob/9bdf8abcb465268c5409db99dc83a26df64c7445/src/ir_Midea.h#L116 + static const uint8_t MIN_TEMP_F = 32; + // see + // https://github.com/crankyoldgit/IRremoteESP8266/blob/9bdf8abcb465268c5409db99dc83a26df64c7445/src/ir_Midea.h#L117 + static const uint8_t MAX_TEMP_F = 99; }; class IrSpecialData : public IrData { From 61499dbdd829102f46937bf7a11d2bfa1dd43598 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 18 Dec 2024 21:07:07 -0500 Subject: [PATCH 0820/1052] [core] Bugfix: Implement ring buffer with xRingbuffer (#7973) --- esphome/core/ring_buffer.cpp | 90 +++++++++++++++++++++++++++++------- esphome/core/ring_buffer.h | 13 ++++-- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index 6152ada314..f779531263 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -13,8 +13,8 @@ static const char *const TAG = "ring_buffer"; RingBuffer::~RingBuffer() { if (this->handle_ != nullptr) { - vStreamBufferDelete(this->handle_); - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + vRingbufferDelete(this->handle_); + RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); allocator.deallocate(this->storage_, this->size_); } } @@ -22,26 +22,49 @@ RingBuffer::~RingBuffer() { std::unique_ptr RingBuffer::create(size_t len) { std::unique_ptr rb = make_unique(); - rb->size_ = len + 1; + rb->size_ = len; - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); rb->storage_ = allocator.allocate(rb->size_); if (rb->storage_ == nullptr) { return nullptr; } - rb->handle_ = xStreamBufferCreateStatic(rb->size_, 1, rb->storage_, &rb->structure_); + rb->handle_ = xRingbufferCreateStatic(rb->size_, RINGBUF_TYPE_BYTEBUF, rb->storage_, &rb->structure_); ESP_LOGD(TAG, "Created ring buffer with size %u", len); + return rb; } size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { - if (ticks_to_wait > 0) - xStreamBufferSetTriggerLevel(this->handle_, len); + size_t bytes_read = 0; - size_t bytes_read = xStreamBufferReceive(this->handle_, data, len, ticks_to_wait); + void *buffer_data = xRingbufferReceiveUpTo(this->handle_, &bytes_read, ticks_to_wait, len); - xStreamBufferSetTriggerLevel(this->handle_, 1); + if (buffer_data == nullptr) { + return 0; + } + + std::memcpy(data, buffer_data, bytes_read); + + vRingbufferReturnItem(this->handle_, buffer_data); + + if (bytes_read < len) { + // Data may have wrapped around, so read a second time to receive the remainder + size_t follow_up_bytes_read = 0; + size_t bytes_remaining = len - bytes_read; + + buffer_data = xRingbufferReceiveUpTo(this->handle_, &follow_up_bytes_read, 0, bytes_remaining); + + if (buffer_data == nullptr) { + return bytes_read; + } + + std::memcpy((void *) ((uint8_t *) (data) + bytes_read), buffer_data, follow_up_bytes_read); + + vRingbufferReturnItem(this->handle_, buffer_data); + bytes_read += follow_up_bytes_read; + } return bytes_read; } @@ -49,22 +72,55 @@ size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { size_t RingBuffer::write(const void *data, size_t len) { size_t free = this->free(); if (free < len) { - size_t needed = len - free; - uint8_t discard[needed]; - xStreamBufferReceive(this->handle_, discard, needed, 0); + // Free enough space in the ring buffer to fit the new data + this->discard_bytes_(len - free); } - return xStreamBufferSend(this->handle_, data, len, 0); + return this->write_without_replacement(data, len, 0); } size_t RingBuffer::write_without_replacement(const void *data, size_t len, TickType_t ticks_to_wait) { - return xStreamBufferSend(this->handle_, data, len, ticks_to_wait); + if (!xRingbufferSend(this->handle_, data, len, ticks_to_wait)) { + // Couldn't fit all the data, so only write what will fit + size_t free = std::min(this->free(), len); + if (xRingbufferSend(this->handle_, data, free, 0)) { + return free; + } + return 0; + } + return len; } -size_t RingBuffer::available() const { return xStreamBufferBytesAvailable(this->handle_); } +size_t RingBuffer::available() const { + UBaseType_t ux_items_waiting = 0; + vRingbufferGetInfo(this->handle_, nullptr, nullptr, nullptr, nullptr, &ux_items_waiting); + return ux_items_waiting; +} -size_t RingBuffer::free() const { return xStreamBufferSpacesAvailable(this->handle_); } +size_t RingBuffer::free() const { return xRingbufferGetCurFreeSize(this->handle_); } -BaseType_t RingBuffer::reset() { return xStreamBufferReset(this->handle_); } +BaseType_t RingBuffer::reset() { + // Discards all the available data + return this->discard_bytes_(this->available()); +} + +bool RingBuffer::discard_bytes_(size_t discard_bytes) { + size_t bytes_read = 0; + + void *buffer_data = xRingbufferReceiveUpTo(this->handle_, &bytes_read, 0, discard_bytes); + if (buffer_data != nullptr) + vRingbufferReturnItem(this->handle_, buffer_data); + + if (bytes_read < discard_bytes) { + size_t wrapped_bytes_read = 0; + buffer_data = xRingbufferReceiveUpTo(this->handle_, &wrapped_bytes_read, 0, discard_bytes - bytes_read); + if (buffer_data != nullptr) { + vRingbufferReturnItem(this->handle_, buffer_data); + bytes_read += wrapped_bytes_read; + } + } + + return (bytes_read == discard_bytes); +} } // namespace esphome diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h index aade1b5f49..bad96d3181 100644 --- a/esphome/core/ring_buffer.h +++ b/esphome/core/ring_buffer.h @@ -3,7 +3,7 @@ #ifdef USE_ESP32 #include -#include +#include #include #include @@ -82,9 +82,14 @@ class RingBuffer { static std::unique_ptr create(size_t len); protected: - StreamBufferHandle_t handle_; - StaticStreamBuffer_t structure_; - uint8_t *storage_; + /// @brief Discards data from the ring buffer. + /// @param discard_bytes amount of bytes to discard + /// @return True if all bytes were successfully discarded, false otherwise + bool discard_bytes_(size_t discard_bytes); + + RingbufHandle_t handle_{nullptr}; + StaticRingbuffer_t structure_; + uint8_t *storage_{nullptr}; size_t size_{0}; }; From 265b6ec44526392bc94df55d37b1e6b047518627 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 18 Dec 2024 21:31:22 -0500 Subject: [PATCH 0821/1052] [esp32_rmt] Updates for IDF 5+ (#7770) Co-authored-by: Jonathan Swoboda Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Keith Burzinski --- esphome/components/esp32_rmt/__init__.py | 31 +++- .../esp32_rmt_led_strip/led_strip.cpp | 78 ++++++++- .../esp32_rmt_led_strip/led_strip.h | 24 ++- .../components/esp32_rmt_led_strip/light.py | 35 +++- .../components/remote_base/remote_base.cpp | 2 +- esphome/components/remote_base/remote_base.h | 20 ++- .../components/remote_receiver/__init__.py | 70 ++++++-- .../remote_receiver/remote_receiver.h | 39 ++++- .../remote_receiver/remote_receiver_esp32.cpp | 145 +++++++++++++++- .../components/remote_transmitter/__init__.py | 56 ++++++- .../remote_transmitter/remote_transmitter.h | 22 ++- .../remote_transmitter_esp32.cpp | 110 +++++++++++- .../test.esp32-c3-idf.yaml | 2 - .../esp32_rmt_led_strip/test.esp32-idf.yaml | 2 - .../remote_receiver/common-actions.yaml | 144 ++++++++++++++++ .../remote_receiver/esp32-common-ard.yaml | 14 ++ .../remote_receiver/esp32-common-idf.yaml | 18 ++ .../remote_receiver/esp32-common.yaml | 157 ------------------ .../remote_receiver/test.esp32-ard.yaml | 2 +- .../remote_receiver/test.esp32-c3-ard.yaml | 2 +- .../remote_receiver/test.esp32-c3-idf.yaml | 8 +- .../remote_receiver/test.esp32-idf.yaml | 8 +- .../remote_receiver/test.esp32-s3-idf.yaml | 8 +- .../remote_receiver/test.esp8266-ard.yaml | 145 +--------------- .../remote_transmitter/esp32-common-ard.yaml | 8 + .../remote_transmitter/esp32-common-idf.yaml | 11 ++ .../remote_transmitter/esp32-common.yaml | 8 - .../remote_transmitter/test.esp32-ard.yaml | 2 +- .../remote_transmitter/test.esp32-c3-ard.yaml | 2 +- .../remote_transmitter/test.esp32-c3-idf.yaml | 7 +- .../remote_transmitter/test.esp32-idf.yaml | 7 +- .../remote_transmitter/test.esp32-s3-idf.yaml | 7 +- .../remote_transmitter/test.esp8266-ard.yaml | 2 +- 33 files changed, 817 insertions(+), 379 deletions(-) create mode 100644 tests/components/remote_receiver/common-actions.yaml create mode 100644 tests/components/remote_receiver/esp32-common-ard.yaml create mode 100644 tests/components/remote_receiver/esp32-common-idf.yaml delete mode 100644 tests/components/remote_receiver/esp32-common.yaml create mode 100644 tests/components/remote_transmitter/esp32-common-ard.yaml create mode 100644 tests/components/remote_transmitter/esp32-common-idf.yaml delete mode 100644 tests/components/remote_transmitter/esp32-common.yaml diff --git a/esphome/components/esp32_rmt/__init__.py b/esphome/components/esp32_rmt/__init__.py index bda240680b..171c335727 100644 --- a/esphome/components/esp32_rmt/__init__.py +++ b/esphome/components/esp32_rmt/__init__.py @@ -1,7 +1,8 @@ -import esphome.config_validation as cv import esphome.codegen as cg - from esphome.components import esp32 +import esphome.config_validation as cv +from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION +from esphome.core import CORE CODEOWNERS = ["@jesserockz"] @@ -36,8 +37,32 @@ RMT_CHANNEL_ENUMS = { } -def validate_rmt_channel(*, tx: bool): +def use_new_rmt_driver(): + framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0): + return True + return False + +def validate_clock_resolution(): + def _validator(value): + cv.only_on_esp32(value) + value = cv.int_(value) + variant = esp32.get_esp32_variant() + if variant == esp32.const.VARIANT_ESP32H2 and value > 32000000: + raise cv.Invalid( + f"ESP32 variant {variant} has a max clock_resolution of 32000000." + ) + if value > 80000000: + raise cv.Invalid( + f"ESP32 variant {variant} has a max clock_resolution of 80000000." + ) + return value + + return _validator + + +def validate_rmt_channel(*, tx: bool): rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS def _validator(value): diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index c2209f7a6c..8ee890ec10 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -1,5 +1,5 @@ -#include #include "led_strip.h" +#include #ifdef USE_ESP32 @@ -13,9 +13,13 @@ namespace esp32_rmt_led_strip { static const char *const TAG = "esp32_rmt_led_strip"; +#ifdef USE_ESP32_VARIANT_ESP32H2 +static const uint32_t RMT_CLK_FREQ = 32000000; +static const uint8_t RMT_CLK_DIV = 1; +#else static const uint32_t RMT_CLK_FREQ = 80000000; - static const uint8_t RMT_CLK_DIV = 2; +#endif void ESP32RMTLEDStripLightOutput::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP32 LED Strip..."); @@ -37,9 +41,48 @@ void ESP32RMTLEDStripLightOutput::setup() { return; } +#if ESP_IDF_VERSION_MAJOR >= 5 + RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); + + // 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset + this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1); + + rmt_tx_channel_config_t channel; + memset(&channel, 0, sizeof(channel)); + channel.clk_src = RMT_CLK_SRC_DEFAULT; + channel.resolution_hz = RMT_CLK_FREQ / RMT_CLK_DIV; + channel.gpio_num = gpio_num_t(this->pin_); + channel.mem_block_symbols = this->rmt_symbols_; + channel.trans_queue_depth = 1; + channel.flags.io_loop_back = 0; + channel.flags.io_od_mode = 0; + channel.flags.invert_out = 0; + channel.flags.with_dma = 0; + channel.intr_priority = 0; + if (rmt_new_tx_channel(&channel, &this->channel_) != ESP_OK) { + ESP_LOGE(TAG, "Channel creation failed"); + this->mark_failed(); + return; + } + + rmt_copy_encoder_config_t encoder; + memset(&encoder, 0, sizeof(encoder)); + if (rmt_new_copy_encoder(&encoder, &this->encoder_) != ESP_OK) { + ESP_LOGE(TAG, "Encoder creation failed"); + this->mark_failed(); + return; + } + + if (rmt_enable(this->channel_) != ESP_OK) { + ESP_LOGE(TAG, "Enabling channel failed"); + this->mark_failed(); + return; + } +#else RAMAllocator rmt_allocator(this->use_psram_ ? 0 : RAMAllocator::ALLOC_INTERNAL); - this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + - 1); // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset + + // 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset + this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1); rmt_config_t config; memset(&config, 0, sizeof(config)); @@ -64,6 +107,7 @@ void ESP32RMTLEDStripLightOutput::setup() { this->mark_failed(); return; } +#endif } void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high, @@ -100,7 +144,12 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { ESP_LOGVV(TAG, "Writing RGB values to bus..."); - if (rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000)) != ESP_OK) { +#if ESP_IDF_VERSION_MAJOR >= 5 + esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000); +#else + esp_err_t error = rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000)); +#endif + if (error != ESP_OK) { ESP_LOGE(TAG, "RMT TX timeout"); this->status_set_warning(); return; @@ -112,7 +161,11 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { size_t size = 0; size_t len = 0; uint8_t *psrc = this->buf_; +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_symbol_word_t *pdest = this->rmt_buf_; +#else rmt_item32_t *pdest = this->rmt_buf_; +#endif while (size < buffer_size) { uint8_t b = *psrc; for (int i = 0; i < 8; i++) { @@ -130,7 +183,16 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) { len++; } - if (rmt_write_items(this->channel_, this->rmt_buf_, len, false) != ESP_OK) { +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_transmit_config_t config; + memset(&config, 0, sizeof(config)); + config.loop_count = 0; + config.flags.eot_level = 0; + error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config); +#else + error = rmt_write_items(this->channel_, this->rmt_buf_, len, false); +#endif + if (error != ESP_OK) { ESP_LOGE(TAG, "RMT TX error"); this->status_set_warning(); return; @@ -186,7 +248,11 @@ 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_); +#if ESP_IDF_VERSION_MAJOR >= 5 + ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_); +#else ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_); +#endif const char *rgb_order; switch (this->rgb_order_) { case ORDER_RGB: diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.h b/esphome/components/esp32_rmt_led_strip/led_strip.h index d21bd86e75..fe49b9a2f3 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.h +++ b/esphome/components/esp32_rmt_led_strip/led_strip.h @@ -9,8 +9,14 @@ #include "esphome/core/helpers.h" #include -#include #include +#include + +#if ESP_IDF_VERSION_MAJOR >= 5 +#include +#else +#include +#endif namespace esphome { namespace esp32_rmt_led_strip { @@ -54,7 +60,11 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint32_t reset_time_high, uint32_t reset_time_low); void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; } +#if ESP_IDF_VERSION_MAJOR >= 5 + void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; } +#else void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; } +#endif void clear_effect_data() override { for (int i = 0; i < this->size(); i++) @@ -70,7 +80,17 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_channel_handle_t channel_{nullptr}; + rmt_encoder_handle_t encoder_{nullptr}; + rmt_symbol_word_t *rmt_buf_{nullptr}; + rmt_symbol_word_t bit0_, bit1_, reset_; + uint32_t rmt_symbols_; +#else rmt_item32_t *rmt_buf_{nullptr}; + rmt_item32_t bit0_, bit1_, reset_; + rmt_channel_t channel_{RMT_CHANNEL_0}; +#endif uint8_t pin_; uint16_t num_leds_; @@ -78,9 +98,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight { bool is_wrgb_; bool use_psram_; - rmt_item32_t bit0_, bit1_, reset_; RGBOrder rgb_order_; - rmt_channel_t channel_; uint32_t last_refresh_{0}; optional max_refresh_rate_{}; diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 976f70e858..67a0e31461 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -13,6 +13,7 @@ from esphome.const import ( CONF_PIN, CONF_RGB_ORDER, CONF_RMT_CHANNEL, + CONF_RMT_SYMBOLS, ) CODEOWNERS = ["@jesserockz"] @@ -23,8 +24,6 @@ ESP32RMTLEDStripLightOutput = esp32_rmt_led_strip_ns.class_( "ESP32RMTLEDStripLightOutput", light.AddressableLight ) -rmt_channel_t = cg.global_ns.enum("rmt_channel_t") - RGBOrder = esp32_rmt_led_strip_ns.enum("RGBOrder") RGB_ORDERS = { @@ -65,6 +64,13 @@ CONF_RESET_HIGH = "reset_high" CONF_RESET_LOW = "reset_low" +def final_validation(config): + if not esp32_rmt.use_new_rmt_driver() and CONF_RMT_CHANNEL not in config: + raise cv.Invalid("rmt_channel is a required option.") + + +FINAL_VALIDATE_SCHEMA = final_validation + CONFIG_SCHEMA = cv.All( light.ADDRESSABLE_LIGHT_SCHEMA.extend( { @@ -72,7 +78,18 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number, cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True), - cv.Required(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True), + cv.Optional(CONF_RMT_CHANNEL): cv.All( + cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=True) + ), + cv.SplitDefault( + CONF_RMT_SYMBOLS, + esp32_idf=64, + esp32_s2_idf=64, + esp32_s3_idf=48, + esp32_c3_idf=48, + esp32_c6_idf=48, + esp32_h2_idf=48, + ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, @@ -148,8 +165,12 @@ async def to_code(config): cg.add(var.set_is_wrgb(config[CONF_IS_WRGB])) cg.add(var.set_use_psram(config[CONF_USE_PSRAM])) - cg.add( - var.set_rmt_channel( - getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}") + if esp32_rmt.use_new_rmt_driver(): + cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + else: + rmt_channel_t = cg.global_ns.enum("rmt_channel_t") + cg.add( + var.set_rmt_channel( + getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}") + ) ) - ) diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index fdfd0b43cc..5dff2c6a38 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -8,7 +8,7 @@ namespace remote_base { static const char *const TAG = "remote_base"; -#ifdef USE_ESP32 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_block_num) { static rmt_channel_t next_rmt_channel = RMT_CHANNEL_0; this->channel_ = next_rmt_channel; diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index c31127735a..70691177ef 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -8,7 +8,7 @@ #include "esphome/core/component.h" #include "esphome/core/hal.h" -#ifdef USE_ESP32 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 #include #endif @@ -112,25 +112,43 @@ class RemoteComponentBase { #ifdef USE_ESP32 class RemoteRMTChannel { public: +#if ESP_IDF_VERSION_MAJOR >= 5 + void set_clock_resolution(uint32_t clock_resolution) { this->clock_resolution_ = clock_resolution; } + void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; } +#else explicit RemoteRMTChannel(uint8_t mem_block_num = 1); explicit RemoteRMTChannel(rmt_channel_t channel, uint8_t mem_block_num = 1); void config_rmt(rmt_config_t &rmt); void set_clock_divider(uint8_t clock_divider) { this->clock_divider_ = clock_divider; } +#endif protected: uint32_t from_microseconds_(uint32_t us) { +#if ESP_IDF_VERSION_MAJOR >= 5 + const uint32_t ticks_per_ten_us = this->clock_resolution_ / 100000u; +#else const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; +#endif return us * ticks_per_ten_us / 10; } uint32_t to_microseconds_(uint32_t ticks) { +#if ESP_IDF_VERSION_MAJOR >= 5 + const uint32_t ticks_per_ten_us = this->clock_resolution_ / 100000u; +#else const uint32_t ticks_per_ten_us = 80000000u / this->clock_divider_ / 100000u; +#endif return (ticks * 10) / ticks_per_ten_us; } RemoteComponentBase *remote_base_; +#if ESP_IDF_VERSION_MAJOR >= 5 + uint32_t clock_resolution_{1000000}; + uint32_t rmt_symbols_; +#else rmt_channel_t channel_{RMT_CHANNEL_0}; uint8_t mem_block_num_; uint8_t clock_divider_{80}; +#endif }; #endif diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index d3f61977c6..b01443a974 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -1,10 +1,11 @@ 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, CONF_CLOCK_DIVIDER, + CONF_CLOCK_RESOLUTION, CONF_DUMP, CONF_FILTER, CONF_ID, @@ -12,12 +13,17 @@ from esphome.const import ( CONF_MEMORY_BLOCKS, CONF_PIN, CONF_RMT_CHANNEL, + CONF_RMT_SYMBOLS, CONF_TOLERANCE, CONF_TYPE, + CONF_USE_DMA, CONF_VALUE, ) from esphome.core import CORE, TimePeriod +CONF_FILTER_SYMBOLS = "filter_symbols" +CONF_RECEIVE_SYMBOLS = "receive_symbols" + AUTO_LOAD = ["remote_base"] remote_receiver_ns = cg.esphome_ns.namespace("remote_receiver") remote_base_ns = cg.esphome_ns.namespace("remote_base") @@ -97,15 +103,43 @@ CONFIG_SCHEMA = remote_base.validate_triggers( cv.positive_time_period_microseconds, cv.Range(max=TimePeriod(microseconds=4294967295)), ), - cv.SplitDefault(CONF_CLOCK_DIVIDER, esp32=80): cv.All( - cv.only_on_esp32, cv.Range(min=1, max=255) + cv.SplitDefault(CONF_CLOCK_DIVIDER, esp32_arduino=80): cv.All( + cv.only_on_esp32, + cv.only_with_arduino, + cv.int_range(min=1, max=255), + ), + cv.Optional(CONF_CLOCK_RESOLUTION): cv.All( + cv.only_on_esp32, + cv.only_with_esp_idf, + esp32_rmt.validate_clock_resolution(), ), cv.Optional(CONF_IDLE, default="10ms"): cv.All( cv.positive_time_period_microseconds, cv.Range(max=TimePeriod(microseconds=4294967295)), ), - cv.Optional(CONF_MEMORY_BLOCKS, default=3): cv.Range(min=1, max=8), - cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=False), + cv.SplitDefault(CONF_MEMORY_BLOCKS, esp32_arduino=3): cv.All( + cv.only_with_arduino, cv.int_range(min=1, max=8) + ), + cv.Optional(CONF_RMT_CHANNEL): cv.All( + cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=False) + ), + cv.SplitDefault( + CONF_RMT_SYMBOLS, + esp32_idf=192, + esp32_s2_idf=192, + esp32_s3_idf=192, + esp32_c3_idf=96, + esp32_c6_idf=96, + esp32_h2_idf=96, + ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), + cv.Optional(CONF_FILTER_SYMBOLS): cv.All( + cv.only_with_esp_idf, cv.int_range(min=0) + ), + cv.SplitDefault( + 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), } ).extend(cv.COMPONENT_SCHEMA) ) @@ -114,13 +148,27 @@ CONFIG_SCHEMA = remote_base.validate_triggers( async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) if CORE.is_esp32: - if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: - var = cg.new_Pvariable( - config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS] - ) + if esp32_rmt.use_new_rmt_driver(): + var = cg.new_Pvariable(config[CONF_ID], pin) + cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + cg.add(var.set_receive_symbols(config[CONF_RECEIVE_SYMBOLS])) + if CONF_USE_DMA in config: + cg.add(var.set_with_dma(config[CONF_USE_DMA])) + if CONF_CLOCK_RESOLUTION in config: + cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) + if CONF_FILTER_SYMBOLS in config: + cg.add(var.set_filter_symbols(config[CONF_FILTER_SYMBOLS])) + if CORE.using_esp_idf: + esp32.add_idf_sdkconfig_option("CONFIG_RMT_RECV_FUNC_IN_IRAM", True) + esp32.add_idf_sdkconfig_option("CONFIG_RMT_ISR_IRAM_SAFE", True) else: - var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS]) - cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER])) + if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: + var = cg.new_Pvariable( + config[CONF_ID], pin, rmt_channel, config[CONF_MEMORY_BLOCKS] + ) + else: + var = cg.new_Pvariable(config[CONF_ID], pin, config[CONF_MEMORY_BLOCKS]) + cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER])) else: var = cg.new_Pvariable(config[CONF_ID], pin) diff --git a/esphome/components/remote_receiver/remote_receiver.h b/esphome/components/remote_receiver/remote_receiver.h index 773f8cf636..8d19d5490f 100644 --- a/esphome/components/remote_receiver/remote_receiver.h +++ b/esphome/components/remote_receiver/remote_receiver.h @@ -5,6 +5,10 @@ #include +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#include +#endif + namespace esphome { namespace remote_receiver { @@ -25,6 +29,21 @@ struct RemoteReceiverComponentStore { uint32_t filter_us{10}; ISRInternalGPIOPin pin; }; +#elif defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +struct RemoteReceiverComponentStore { + /// Stores RMT symbols and rx done event data + volatile uint8_t *buffer{nullptr}; + /// The position last written to + volatile uint32_t buffer_write{0}; + /// The position last read from + volatile uint32_t buffer_read{0}; + bool overflow{false}; + uint32_t buffer_size{1000}; + uint32_t receive_size{0}; + uint32_t filter_symbols{0}; + esp_err_t error{ESP_OK}; + rmt_receive_config_t config; +}; #endif class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, @@ -33,9 +52,10 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, , public remote_base::RemoteRMTChannel #endif + { public: -#ifdef USE_ESP32 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 RemoteReceiverComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) : RemoteReceiverBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} @@ -49,19 +69,32 @@ class RemoteReceiverComponent : public remote_base::RemoteReceiverBase, void loop() override; float get_setup_priority() const override { return setup_priority::DATA; } +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 + void set_filter_symbols(uint32_t filter_symbols) { this->filter_symbols_ = filter_symbols; } + void set_receive_symbols(uint32_t receive_symbols) { this->receive_symbols_ = receive_symbols; } + void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } +#endif void set_buffer_size(uint32_t buffer_size) { this->buffer_size_ = buffer_size; } void set_filter_us(uint32_t filter_us) { this->filter_us_ = filter_us; } void set_idle_us(uint32_t idle_us) { this->idle_us_ = idle_us; } protected: #ifdef USE_ESP32 - void decode_rmt_(rmt_item32_t *item, size_t len); +#if ESP_IDF_VERSION_MAJOR >= 5 + void decode_rmt_(rmt_symbol_word_t *item, size_t item_count); + rmt_channel_handle_t channel_{NULL}; + uint32_t filter_symbols_{0}; + uint32_t receive_symbols_{0}; + bool with_dma_{false}; +#else + void decode_rmt_(rmt_item32_t *item, size_t item_count); RingbufHandle_t ringbuf_; +#endif esp_err_t error_code_{ESP_OK}; std::string error_string_{""}; #endif -#if defined(USE_ESP8266) || defined(USE_LIBRETINY) +#if defined(USE_ESP8266) || defined(USE_LIBRETINY) || (defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5) RemoteReceiverComponentStore store_; HighFrequencyLoopRequester high_freq_; #endif diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index 91295871e2..48b84d61d4 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -2,15 +2,104 @@ #include "esphome/core/log.h" #ifdef USE_ESP32 -#include namespace esphome { namespace remote_receiver { static const char *const TAG = "remote_receiver.esp32"; +#ifdef USE_ESP32_VARIANT_ESP32H2 +static const uint32_t RMT_CLK_FREQ = 32000000; +#else +static const uint32_t RMT_CLK_FREQ = 80000000; +#endif + +#if ESP_IDF_VERSION_MAJOR >= 5 +static bool IRAM_ATTR HOT rmt_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *event, void *arg) { + RemoteReceiverComponentStore *store = (RemoteReceiverComponentStore *) arg; + rmt_rx_done_event_data_t *event_buffer = (rmt_rx_done_event_data_t *) (store->buffer + store->buffer_write); + uint32_t event_size = sizeof(rmt_rx_done_event_data_t); + uint32_t next_write = store->buffer_write + event_size + event->num_symbols * sizeof(rmt_symbol_word_t); + if (next_write + event_size + store->receive_size > store->buffer_size) { + next_write = 0; + } + if (store->buffer_read - next_write < event_size + store->receive_size) { + next_write = store->buffer_write; + store->overflow = true; + } + if (event->num_symbols <= store->filter_symbols) { + next_write = store->buffer_write; + } + store->error = + rmt_receive(channel, (uint8_t *) store->buffer + next_write + event_size, store->receive_size, &store->config); + event_buffer->num_symbols = event->num_symbols; + event_buffer->received_symbols = event->received_symbols; + store->buffer_write = next_write; + return false; +} +#endif void RemoteReceiverComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Remote Receiver..."); +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_rx_channel_config_t channel; + memset(&channel, 0, sizeof(channel)); + channel.clk_src = RMT_CLK_SRC_DEFAULT; + channel.resolution_hz = this->clock_resolution_; + channel.mem_block_symbols = rmt_symbols_; + channel.gpio_num = gpio_num_t(this->pin_->get_pin()); + channel.intr_priority = 0; + channel.flags.invert_in = 0; + channel.flags.with_dma = this->with_dma_; + channel.flags.io_loop_back = 0; + esp_err_t error = rmt_new_rx_channel(&channel, &this->channel_); + if (error != ESP_OK) { + this->error_code_ = error; + if (error == ESP_ERR_NOT_FOUND) { + this->error_string_ = "out of RMT symbol memory"; + } else { + this->error_string_ = "in rmt_new_rx_channel"; + } + this->mark_failed(); + return; + } + error = rmt_enable(this->channel_); + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_enable"; + this->mark_failed(); + return; + } + + rmt_rx_event_callbacks_t callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.on_recv_done = rmt_callback; + error = rmt_rx_register_event_callbacks(this->channel_, &callbacks, &this->store_); + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_rx_register_event_callbacks"; + this->mark_failed(); + return; + } + + uint32_t event_size = sizeof(rmt_rx_done_event_data_t); + uint32_t max_filter_ns = 255u * 1000 / (RMT_CLK_FREQ / 1000000); + uint32_t max_idle_ns = 65535u * 1000; + memset(&this->store_.config, 0, sizeof(this->store_.config)); + this->store_.config.signal_range_min_ns = std::min(this->filter_us_ * 1000, max_filter_ns); + this->store_.config.signal_range_max_ns = std::min(this->idle_us_ * 1000, max_idle_ns); + this->store_.filter_symbols = this->filter_symbols_; + this->store_.receive_size = this->receive_symbols_ * sizeof(rmt_symbol_word_t); + this->store_.buffer_size = std::max((event_size + this->store_.receive_size) * 2, this->buffer_size_); + this->store_.buffer = new uint8_t[this->buffer_size_]; + error = rmt_receive(this->channel_, (uint8_t *) this->store_.buffer + event_size, this->store_.receive_size, + &this->store_.config); + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_receive"; + this->mark_failed(); + return; + } +#else this->pin_->setup(); rmt_config_t rmt{}; this->config_rmt(rmt); @@ -59,7 +148,9 @@ void RemoteReceiverComponent::setup() { this->mark_failed(); return; } +#endif } + void RemoteReceiverComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Receiver:"); LOG_PIN(" Pin: ", this->pin_); @@ -67,9 +158,16 @@ 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!"); } +#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_); +#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_); +#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_); @@ -81,10 +179,38 @@ void RemoteReceiverComponent::dump_config() { } void RemoteReceiverComponent::loop() { +#if ESP_IDF_VERSION_MAJOR >= 5 + if (this->store_.error != ESP_OK) { + ESP_LOGE(TAG, "Receive error"); + this->error_code_ = this->store_.error; + this->error_string_ = "in rmt_callback"; + this->mark_failed(); + } + if (this->store_.overflow) { + ESP_LOGW(TAG, "Buffer overflow"); + this->store_.overflow = false; + } + uint32_t buffer_write = this->store_.buffer_write; + while (this->store_.buffer_read != buffer_write) { + rmt_rx_done_event_data_t *event = (rmt_rx_done_event_data_t *) (this->store_.buffer + this->store_.buffer_read); + uint32_t event_size = sizeof(rmt_rx_done_event_data_t); + uint32_t next_read = this->store_.buffer_read + event_size + event->num_symbols * sizeof(rmt_symbol_word_t); + if (next_read + event_size + this->store_.receive_size > this->store_.buffer_size) { + next_read = 0; + } + this->decode_rmt_(event->received_symbols, event->num_symbols); + this->store_.buffer_read = next_read; + + if (!this->temp_.empty()) { + this->temp_.push_back(-this->idle_us_); + this->call_listeners_dumpers_(); + } + } +#else size_t len = 0; auto *item = (rmt_item32_t *) xRingbufferReceive(this->ringbuf_, &len, 0); if (item != nullptr) { - this->decode_rmt_(item, len); + this->decode_rmt_(item, len / sizeof(rmt_item32_t)); vRingbufferReturnItem(this->ringbuf_, item); if (this->temp_.empty()) @@ -93,13 +219,18 @@ void RemoteReceiverComponent::loop() { this->temp_.push_back(-this->idle_us_); this->call_listeners_dumpers_(); } +#endif } -void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) { + +#if ESP_IDF_VERSION_MAJOR >= 5 +void RemoteReceiverComponent::decode_rmt_(rmt_symbol_word_t *item, size_t item_count) { +#else +void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t item_count) { +#endif bool prev_level = false; uint32_t prev_length = 0; this->temp_.clear(); int32_t multiplier = this->pin_->is_inverted() ? -1 : 1; - size_t item_count = len / sizeof(rmt_item32_t); uint32_t filter_ticks = this->from_microseconds_(this->filter_us_); ESP_LOGVV(TAG, "START:"); @@ -124,7 +255,8 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) { this->temp_.reserve(item_count * 2); // each RMT item has 2 pulses for (size_t i = 0; i < item_count; i++) { if (item[i].duration0 == 0u) { - // Do nothing + // EOF, sometimes garbage follows, break early + break; } else if ((bool(item[i].level0) == prev_level) || (item[i].duration0 < filter_ticks)) { prev_length += item[i].duration0; } else { @@ -140,7 +272,8 @@ void RemoteReceiverComponent::decode_rmt_(rmt_item32_t *item, size_t len) { } if (item[i].duration1 == 0u) { - // Do nothing + // EOF, sometimes garbage follows, break early + break; } else if ((bool(item[i].level1) == prev_level) || (item[i].duration1 < filter_ticks)) { prev_length += item[i].duration1; } else { diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index f979939739..ea29751671 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -2,12 +2,23 @@ from esphome import automation, pins import esphome.codegen as cg from esphome.components import esp32_rmt, remote_base import esphome.config_validation as cv -from esphome.const import CONF_CARRIER_DUTY_PERCENT, CONF_ID, CONF_PIN, CONF_RMT_CHANNEL +from esphome.const import ( + CONF_CARRIER_DUTY_PERCENT, + CONF_CLOCK_DIVIDER, + CONF_CLOCK_RESOLUTION, + CONF_ID, + CONF_PIN, + CONF_RMT_CHANNEL, + CONF_RMT_SYMBOLS, + CONF_USE_DMA, +) +from esphome.core import CORE AUTO_LOAD = ["remote_base"] CONF_ON_TRANSMIT = "on_transmit" CONF_ON_COMPLETE = "on_complete" +CONF_ONE_WIRE = "one_wire" remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter") RemoteTransmitterComponent = remote_transmitter_ns.class_( @@ -22,7 +33,28 @@ CONFIG_SCHEMA = cv.Schema( cv.Required(CONF_CARRIER_DUTY_PERCENT): cv.All( cv.percentage_int, cv.Range(min=1, max=100) ), - cv.Optional(CONF_RMT_CHANNEL): esp32_rmt.validate_rmt_channel(tx=True), + cv.Optional(CONF_CLOCK_RESOLUTION): cv.All( + cv.only_on_esp32, + cv.only_with_esp_idf, + esp32_rmt.validate_clock_resolution(), + ), + cv.Optional(CONF_CLOCK_DIVIDER): cv.All( + cv.only_on_esp32, cv.only_with_arduino, cv.int_range(min=1, max=255) + ), + cv.Optional(CONF_ONE_WIRE): cv.All(cv.only_with_esp_idf, cv.boolean), + cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), + cv.SplitDefault( + CONF_RMT_SYMBOLS, + esp32_idf=64, + esp32_s2_idf=64, + esp32_s3_idf=48, + esp32_c3_idf=48, + esp32_c6_idf=48, + esp32_h2_idf=48, + ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), + cv.Optional(CONF_RMT_CHANNEL): cv.All( + cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=True) + ), cv.Optional(CONF_ON_TRANSMIT): automation.validate_automation(single=True), cv.Optional(CONF_ON_COMPLETE): automation.validate_automation(single=True), } @@ -31,8 +63,24 @@ CONFIG_SCHEMA = cv.Schema( async def to_code(config): pin = await cg.gpio_pin_expression(config[CONF_PIN]) - if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: - var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) + if CORE.is_esp32: + if esp32_rmt.use_new_rmt_driver(): + var = cg.new_Pvariable(config[CONF_ID], pin) + cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS])) + if CONF_CLOCK_RESOLUTION in config: + cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) + if CONF_USE_DMA in config: + cg.add(var.set_with_dma(config[CONF_USE_DMA])) + if CONF_ONE_WIRE in config: + cg.add(var.set_one_wire(config[CONF_ONE_WIRE])) + else: + if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: + var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) + else: + var = cg.new_Pvariable(config[CONF_ID], pin) + if CONF_CLOCK_DIVIDER in config: + cg.add(var.set_clock_divider(config[CONF_CLOCK_DIVIDER])) + else: var = cg.new_Pvariable(config[CONF_ID], pin) await cg.register_component(var, config) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 4abe687d23..20b98d5488 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -5,6 +5,10 @@ #include +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 +#include +#endif + namespace esphome { namespace remote_transmitter { @@ -16,7 +20,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #endif { public: -#ifdef USE_ESP32 +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR < 5 RemoteTransmitterComponent(InternalGPIOPin *pin, uint8_t mem_block_num = 1) : remote_base::RemoteTransmitterBase(pin), remote_base::RemoteRMTChannel(mem_block_num) {} @@ -29,10 +33,16 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, void dump_config() override; - float get_setup_priority() const override { return setup_priority::DATA; } + // transmitter setup must run after receiver setup to allow the same GPIO to be used by both + float get_setup_priority() const override { return setup_priority::DATA - 1; } void set_carrier_duty_percent(uint8_t carrier_duty_percent) { this->carrier_duty_percent_ = carrier_duty_percent; } +#if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 + void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } + void set_one_wire(bool one_wire) { this->one_wire_ = one_wire; } +#endif + Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; }; Trigger<> *get_complete_trigger() const { return this->complete_trigger_; }; @@ -54,7 +64,15 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, uint32_t current_carrier_frequency_{38000}; bool initialized_{false}; +#if ESP_IDF_VERSION_MAJOR >= 5 + std::vector rmt_temp_; + bool with_dma_{false}; + bool one_wire_{false}; + rmt_channel_handle_t channel_{NULL}; + rmt_encoder_handle_t encoder_{NULL}; +#else std::vector rmt_temp_; +#endif esp_err_t error_code_{ESP_OK}; std::string error_string_{""}; bool inverted_{false}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index bce2408723..efb1735bfd 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -9,13 +9,22 @@ namespace remote_transmitter { static const char *const TAG = "remote_transmitter"; -void RemoteTransmitterComponent::setup() { this->configure_rmt_(); } +void RemoteTransmitterComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up Remote Transmitter..."); + this->configure_rmt_(); +} void RemoteTransmitterComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Remote Transmitter..."); + ESP_LOGCONFIG(TAG, "Remote Transmitter:"); +#if ESP_IDF_VERSION_MAJOR >= 5 + ESP_LOGCONFIG(TAG, " One wire: %s", this->one_wire_ ? "true" : "false"); + ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_); + ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, 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_); +#endif LOG_PIN(" Pin: ", this->pin_); if (this->current_carrier_frequency_ != 0 && this->carrier_duty_percent_ != 100) { @@ -29,6 +38,72 @@ void RemoteTransmitterComponent::dump_config() { } void RemoteTransmitterComponent::configure_rmt_() { +#if ESP_IDF_VERSION_MAJOR >= 5 + esp_err_t error; + + if (!this->initialized_) { + rmt_tx_channel_config_t channel; + memset(&channel, 0, sizeof(channel)); + channel.clk_src = RMT_CLK_SRC_DEFAULT; + channel.resolution_hz = this->clock_resolution_; + channel.gpio_num = gpio_num_t(this->pin_->get_pin()); + channel.mem_block_symbols = this->rmt_symbols_; + channel.trans_queue_depth = 1; + channel.flags.io_loop_back = this->one_wire_; + channel.flags.io_od_mode = this->one_wire_; + channel.flags.invert_out = 0; + channel.flags.with_dma = this->with_dma_; + channel.intr_priority = 0; + error = rmt_new_tx_channel(&channel, &this->channel_); + if (error != ESP_OK) { + this->error_code_ = error; + if (error == ESP_ERR_NOT_FOUND) { + this->error_string_ = "out of RMT symbol memory"; + } else { + this->error_string_ = "in rmt_new_tx_channel"; + } + this->mark_failed(); + return; + } + + rmt_copy_encoder_config_t encoder; + memset(&encoder, 0, sizeof(encoder)); + error = rmt_new_copy_encoder(&encoder, &this->encoder_); + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_new_copy_encoder"; + this->mark_failed(); + return; + } + + error = rmt_enable(this->channel_); + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_enable"; + this->mark_failed(); + return; + } + this->initialized_ = true; + } + + if (this->current_carrier_frequency_ == 0 || this->carrier_duty_percent_ == 100) { + error = rmt_apply_carrier(this->channel_, nullptr); + } else { + rmt_carrier_config_t carrier; + memset(&carrier, 0, sizeof(carrier)); + carrier.frequency_hz = this->current_carrier_frequency_; + carrier.duty_cycle = (float) this->carrier_duty_percent_ / 100.0f; + carrier.flags.polarity_active_low = this->inverted_; + carrier.flags.always_on = 1; + error = rmt_apply_carrier(this->channel_, &carrier); + } + if (error != ESP_OK) { + this->error_code_ = error; + this->error_string_ = "in rmt_apply_carrier"; + this->mark_failed(); + return; + } +#else rmt_config_t c{}; this->config_rmt(c); @@ -76,6 +151,7 @@ void RemoteTransmitterComponent::configure_rmt_() { } this->initialized_ = true; } +#endif } void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t send_wait) { @@ -90,7 +166,11 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen this->rmt_temp_.clear(); this->rmt_temp_.reserve((this->temp_.get_data().size() + 1) / 2); uint32_t rmt_i = 0; +#if ESP_IDF_VERSION_MAJOR >= 5 + rmt_symbol_word_t rmt_item; +#else rmt_item32_t rmt_item; +#endif for (int32_t val : this->temp_.get_data()) { bool level = val >= 0; @@ -125,6 +205,31 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen return; } this->transmit_trigger_->trigger(); +#if ESP_IDF_VERSION_MAJOR >= 5 + for (uint32_t i = 0; i < send_times; i++) { + rmt_transmit_config_t config; + memset(&config, 0, sizeof(config)); + config.loop_count = 0; + config.flags.eot_level = this->inverted_; + esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(), + this->rmt_temp_.size() * sizeof(rmt_symbol_word_t), &config); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } else { + this->status_clear_warning(); + } + error = rmt_tx_wait_all_done(this->channel_, -1); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } else { + this->status_clear_warning(); + } + if (i + 1 < send_times) + delayMicroseconds(send_wait); + } +#else for (uint32_t i = 0; i < send_times; i++) { esp_err_t error = rmt_write_items(this->channel_, this->rmt_temp_.data(), this->rmt_temp_.size(), true); if (error != ESP_OK) { @@ -136,6 +241,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (i + 1 < send_times) delayMicroseconds(send_wait); } +#endif this->complete_trigger_->trigger(); } 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 8d04d3370b..93318d0581 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 @@ -3,14 +3,12 @@ light: id: led_strip pin: 4 num_leds: 60 - rmt_channel: 0 rgb_order: GRB chipset: ws2812 - platform: esp32_rmt_led_strip id: led_strip2 pin: 5 num_leds: 60 - rmt_channel: 1 rgb_order: RGB bit0_high: 100µs bit0_low: 100µs 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 6e1763b339..228af17189 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml @@ -3,14 +3,12 @@ light: id: led_strip pin: 13 num_leds: 60 - rmt_channel: 6 rgb_order: GRB chipset: ws2812 - platform: esp32_rmt_led_strip id: led_strip2 pin: 14 num_leds: 60 - rmt_channel: 2 rgb_order: RGB bit0_high: 100µs bit0_low: 100µs diff --git a/tests/components/remote_receiver/common-actions.yaml b/tests/components/remote_receiver/common-actions.yaml new file mode 100644 index 0000000000..23589aed22 --- /dev/null +++ b/tests/components/remote_receiver/common-actions.yaml @@ -0,0 +1,144 @@ +on_abbwelcome: + then: + - logger.log: + format: "on_abbwelcome: %u" + args: ["x.data()[0]"] +on_aeha: + then: + - logger.log: + format: "on_aeha: %u %u" + args: ["x.address", "x.data.front()"] +on_byronsx: + then: + - logger.log: + format: "on_byronsx: %u %u" + args: ["x.address", "x.command"] +on_canalsat: + then: + - logger.log: + format: "on_canalsat: %u %u" + args: ["x.address", "x.command"] +# on_canalsatld: +# then: +# - logger.log: +# format: "on_canalsatld: %u %u" +# args: ["x.address", "x.command"] +on_coolix: + then: + - logger.log: + format: "on_coolix: %lu %lu" + args: ["long(x.first)", "long(x.second)"] +on_dish: + then: + - logger.log: + format: "on_dish: %u %u" + args: ["x.address", "x.command"] +on_dooya: + then: + - logger.log: + format: "on_dooya: %u %u %u" + args: ["x.channel", "x.button", "x.check"] +on_drayton: + then: + - logger.log: + format: "on_drayton: %u %u %u" + args: ["x.address", "x.channel", "x.command"] +on_jvc: + then: + - logger.log: + format: "on_jvc: %lu" + args: ["long(x.data)"] +on_keeloq: + then: + - logger.log: + format: "on_keeloq: %lu %lu %u" + args: ["long(x.encrypted)", "long(x.address)", "x.command"] +on_haier: + then: + - logger.log: + format: "on_haier: %u" + args: ["x.data.front()"] +on_lg: + then: + - logger.log: + format: "on_lg: %lu %u" + args: ["long(x.data)", "x.nbits"] +on_magiquest: + then: + - logger.log: + format: "on_magiquest: %u %lu" + args: ["x.magnitude", "long(x.wand_id)"] +on_midea: + then: + - logger.log: + format: "on_midea: %u %u" + args: ["x.size()", "x.data()[0]"] +on_nec: + then: + - logger.log: + format: "on_nec: %u %u" + args: ["x.address", "x.command"] +on_nexa: + then: + - logger.log: + format: "on_nexa: %lu %u %u %u %u" + args: ["long(x.device)", "x.group", "x.state", "x.channel", "x.level"] +on_panasonic: + then: + - logger.log: + format: "on_panasonic: %u %lu" + args: ["x.address", "long(x.command)"] +on_pioneer: + then: + - logger.log: + format: "on_pioneer: %u %u" + args: ["x.rc_code_1", "x.rc_code_2"] +on_pronto: + then: + - logger.log: + format: "on_pronto: %s" + args: ["x.data.c_str()"] +on_raw: + then: + - logger.log: + format: "on_raw: %lu" + args: ["long(x.front())"] +on_rc5: + then: + - logger.log: + format: "on_rc5: %u %u" + args: ["x.address", "x.command"] +on_rc6: + then: + - logger.log: + format: "on_rc6: %u %u" + args: ["x.address", "x.command"] +on_rc_switch: + then: + - logger.log: + format: "on_rc_switch: %llu %u" + args: ["x.code", "x.protocol"] +on_samsung: + then: + - logger.log: + format: "on_samsung: %llu %u" + args: ["x.data", "x.nbits"] +on_samsung36: + then: + - logger.log: + format: "on_samsung36: %u %lu" + args: ["x.address", "long(x.command)"] +on_sony: + then: + - logger.log: + format: "on_sony: %lu %u" + args: ["long(x.data)", "x.nbits"] +on_toshiba_ac: + then: + - logger.log: + format: "on_toshiba_ac: %llu %llu" + args: ["x.rc_code_1", "x.rc_code_2"] +on_mirage: + then: + - lambda: |- + ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); diff --git a/tests/components/remote_receiver/esp32-common-ard.yaml b/tests/components/remote_receiver/esp32-common-ard.yaml new file mode 100644 index 0000000000..e331a35307 --- /dev/null +++ b/tests/components/remote_receiver/esp32-common-ard.yaml @@ -0,0 +1,14 @@ +remote_receiver: + - id: rcvr + pin: ${pin} + rmt_channel: ${rmt_channel} + dump: all + tolerance: 25% + <<: !include common-actions.yaml + +binary_sensor: + - platform: remote_receiver + name: Panasonic Remote Input + panasonic: + address: 0x4004 + command: 0x100BCBD diff --git a/tests/components/remote_receiver/esp32-common-idf.yaml b/tests/components/remote_receiver/esp32-common-idf.yaml new file mode 100644 index 0000000000..b314880f8a --- /dev/null +++ b/tests/components/remote_receiver/esp32-common-idf.yaml @@ -0,0 +1,18 @@ +remote_receiver: + - id: rcvr + pin: ${pin} + dump: all + tolerance: 25% + clock_resolution: ${clock_resolution} + filter_symbols: ${filter_symbols} + receive_symbols: ${receive_symbols} + rmt_symbols: ${rmt_symbols} + use_dma: ${use_dma} + <<: !include common-actions.yaml + +binary_sensor: + - platform: remote_receiver + name: Panasonic Remote Input + panasonic: + address: 0x4004 + command: 0x100BCBD diff --git a/tests/components/remote_receiver/esp32-common.yaml b/tests/components/remote_receiver/esp32-common.yaml deleted file mode 100644 index 7e5d2cce32..0000000000 --- a/tests/components/remote_receiver/esp32-common.yaml +++ /dev/null @@ -1,157 +0,0 @@ -remote_receiver: - id: rcvr - pin: ${pin} - rmt_channel: ${rmt_channel} - dump: all - tolerance: 25% - on_abbwelcome: - then: - - logger.log: - format: "on_abbwelcome: %u" - args: ["x.data()[0]"] - on_aeha: - then: - - logger.log: - format: "on_aeha: %u %u" - args: ["x.address", "x.data.front()"] - on_byronsx: - then: - - logger.log: - format: "on_byronsx: %u %u" - args: ["x.address", "x.command"] - on_canalsat: - then: - - logger.log: - format: "on_canalsat: %u %u" - args: ["x.address", "x.command"] - # on_canalsatld: - # then: - # - logger.log: - # format: "on_canalsatld: %u %u" - # args: ["x.address", "x.command"] - on_coolix: - then: - - logger.log: - format: "on_coolix: %lu %lu" - args: ["long(x.first)", "long(x.second)"] - on_dish: - then: - - logger.log: - format: "on_dish: %u %u" - args: ["x.address", "x.command"] - on_dooya: - then: - - logger.log: - format: "on_dooya: %u %u %u" - args: ["x.channel", "x.button", "x.check"] - on_drayton: - then: - - logger.log: - format: "on_drayton: %u %u %u" - args: ["x.address", "x.channel", "x.command"] - on_jvc: - then: - - logger.log: - format: "on_jvc: %lu" - args: ["long(x.data)"] - on_keeloq: - then: - - logger.log: - format: "on_keeloq: %lu %lu %u" - args: ["long(x.encrypted)", "long(x.address)", "x.command"] - on_haier: - then: - - logger.log: - format: "on_haier: %u" - args: ["x.data.front()"] - on_lg: - then: - - logger.log: - format: "on_lg: %lu %u" - args: ["long(x.data)", "x.nbits"] - on_magiquest: - then: - - logger.log: - format: "on_magiquest: %u %lu" - args: ["x.magnitude", "long(x.wand_id)"] - on_midea: - then: - - logger.log: - format: "on_midea: %u %u" - args: ["x.size()", "x.data()[0]"] - on_nec: - then: - - logger.log: - format: "on_nec: %u %u" - args: ["x.address", "x.command"] - on_nexa: - then: - - logger.log: - format: "on_nexa: %lu %u %u %u %u" - args: ["long(x.device)", "x.group", "x.state", "x.channel", "x.level"] - on_panasonic: - then: - - logger.log: - format: "on_panasonic: %u %lu" - args: ["x.address", "long(x.command)"] - on_pioneer: - then: - - logger.log: - format: "on_pioneer: %u %u" - args: ["x.rc_code_1", "x.rc_code_2"] - on_pronto: - then: - - logger.log: - format: "on_pronto: %s" - args: ["x.data.c_str()"] - on_raw: - then: - - logger.log: - format: "on_raw: %lu" - args: ["long(x.front())"] - on_rc5: - then: - - logger.log: - format: "on_rc5: %u %u" - args: ["x.address", "x.command"] - on_rc6: - then: - - logger.log: - format: "on_rc6: %u %u" - args: ["x.address", "x.command"] - on_rc_switch: - then: - - logger.log: - format: "on_rc_switch: %llu %u" - args: ["x.code", "x.protocol"] - on_samsung: - then: - - logger.log: - format: "on_samsung: %llu %u" - args: ["x.data", "x.nbits"] - on_samsung36: - then: - - logger.log: - format: "on_samsung36: %u %lu" - args: ["x.address", "long(x.command)"] - on_sony: - then: - - logger.log: - format: "on_sony: %lu %u" - args: ["long(x.data)", "x.nbits"] - on_toshiba_ac: - then: - - logger.log: - format: "on_toshiba_ac: %llu %llu" - args: ["x.rc_code_1", "x.rc_code_2"] - on_mirage: - then: - - lambda: |- - ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); - -binary_sensor: - - platform: remote_receiver - name: Panasonic Remote Input - panasonic: - address: 0x4004 - command: 0x100BCBD diff --git a/tests/components/remote_receiver/test.esp32-ard.yaml b/tests/components/remote_receiver/test.esp32-ard.yaml index 16d276958a..5d29187206 100644 --- a/tests/components/remote_receiver/test.esp32-ard.yaml +++ b/tests/components/remote_receiver/test.esp32-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "2" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3-ard.yaml b/tests/components/remote_receiver/test.esp32-c3-ard.yaml index 16d276958a..5d29187206 100644 --- a/tests/components/remote_receiver/test.esp32-c3-ard.yaml +++ b/tests/components/remote_receiver/test.esp32-c3-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "2" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_receiver/test.esp32-c3-idf.yaml b/tests/components/remote_receiver/test.esp32-c3-idf.yaml index 16d276958a..495bb293c3 100644 --- a/tests/components/remote_receiver/test.esp32-c3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-c3-idf.yaml @@ -1,6 +1,10 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + 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 16d276958a..495bb293c3 100644 --- a/tests/components/remote_receiver/test.esp32-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-idf.yaml @@ -1,6 +1,10 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + 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 265ecda771..e678ba456d 100644 --- a/tests/components/remote_receiver/test.esp32-s3-idf.yaml +++ b/tests/components/remote_receiver/test.esp32-s3-idf.yaml @@ -1,6 +1,10 @@ substitutions: pin: GPIO38 - rmt_channel: "5" + clock_resolution: "2000000" + filter_symbols: "2" + receive_symbols: "4" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_receiver/test.esp8266-ard.yaml b/tests/components/remote_receiver/test.esp8266-ard.yaml index 27d36d4a16..c9784ae003 100644 --- a/tests/components/remote_receiver/test.esp8266-ard.yaml +++ b/tests/components/remote_receiver/test.esp8266-ard.yaml @@ -2,150 +2,7 @@ remote_receiver: id: rcvr pin: GPIO5 dump: all - on_abbwelcome: - then: - - logger.log: - format: "on_abbwelcome: %u" - args: ["x.data()[0]"] - on_aeha: - then: - - logger.log: - format: "on_aeha: %u %u" - args: ["x.address", "x.data.front()"] - on_byronsx: - then: - - logger.log: - format: "on_byronsx: %u %u" - args: ["x.address", "x.command"] - on_canalsat: - then: - - logger.log: - format: "on_canalsat: %u %u" - args: ["x.address", "x.command"] - # on_canalsatld: - # then: - # - logger.log: - # format: "on_canalsatld: %u %u" - # args: ["x.address", "x.command"] - on_coolix: - then: - - logger.log: - format: "on_coolix: %u %u" - args: ["x.first", "x.second"] - on_dish: - then: - - logger.log: - format: "on_dish: %u %u" - args: ["x.address", "x.command"] - on_dooya: - then: - - logger.log: - format: "on_dooya: %u %u %u" - args: ["x.channel", "x.button", "x.check"] - on_drayton: - then: - - logger.log: - format: "on_drayton: %u %u %u" - args: ["x.address", "x.channel", "x.command"] - on_jvc: - then: - - logger.log: - format: "on_jvc: %u" - args: ["x.data"] - on_keeloq: - then: - - logger.log: - format: "on_keeloq: %u %u %u" - args: ["x.encrypted", "x.address", "x.command"] - on_haier: - then: - - logger.log: - format: "on_haier: %u" - args: ["x.data.front()"] - on_lg: - then: - - logger.log: - format: "on_lg: %u %u" - args: ["x.data", "x.nbits"] - on_magiquest: - then: - - logger.log: - format: "on_magiquest: %u %u" - args: ["x.magnitude", "x.wand_id"] - on_midea: - then: - - logger.log: - format: "on_midea: %u %u" - args: ["x.size()", "x.data()[0]"] - on_nec: - then: - - logger.log: - format: "on_nec: %u %u" - args: ["x.address", "x.command"] - on_nexa: - then: - - logger.log: - format: "on_nexa: %u %u %u %u %u" - args: ["x.device", "x.group", "x.state", "x.channel", "x.level"] - on_panasonic: - then: - - logger.log: - format: "on_panasonic: %u %u" - args: ["x.address", "x.command"] - on_pioneer: - then: - - logger.log: - format: "on_pioneer: %u %u" - args: ["x.rc_code_1", "x.rc_code_2"] - on_pronto: - then: - - logger.log: - format: "on_pronto: %s" - args: ["x.data.c_str()"] - on_raw: - then: - - logger.log: - format: "on_raw: %u" - args: ["x.front()"] - on_rc5: - then: - - logger.log: - format: "on_rc5: %u %u" - args: ["x.address", "x.command"] - on_rc6: - then: - - logger.log: - format: "on_rc6: %u %u" - args: ["x.address", "x.command"] - on_rc_switch: - then: - - logger.log: - format: "on_rc_switch: %llu %u" - args: ["x.code", "x.protocol"] - on_samsung: - then: - - logger.log: - format: "on_samsung: %llu %u" - args: ["x.data", "x.nbits"] - on_samsung36: - then: - - logger.log: - format: "on_samsung36: %u %u" - args: ["x.address", "x.command"] - on_sony: - then: - - logger.log: - format: "on_sony: %u %u" - args: ["x.data", "x.nbits"] - on_toshiba_ac: - then: - - logger.log: - format: "on_toshiba_ac: %llu %llu" - args: ["x.rc_code_1", "x.rc_code_2"] - on_mirage: - then: - - lambda: |- - ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); + <<: !include common-actions.yaml binary_sensor: - platform: remote_receiver diff --git a/tests/components/remote_transmitter/esp32-common-ard.yaml b/tests/components/remote_transmitter/esp32-common-ard.yaml new file mode 100644 index 0000000000..420cea326d --- /dev/null +++ b/tests/components/remote_transmitter/esp32-common-ard.yaml @@ -0,0 +1,8 @@ +remote_transmitter: + - id: xmitr + pin: ${pin} + rmt_channel: ${rmt_channel} + carrier_duty_percent: 50% + +packages: + buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/esp32-common-idf.yaml b/tests/components/remote_transmitter/esp32-common-idf.yaml new file mode 100644 index 0000000000..3b8b5e2aef --- /dev/null +++ b/tests/components/remote_transmitter/esp32-common-idf.yaml @@ -0,0 +1,11 @@ +remote_transmitter: + - id: xmitr + pin: ${pin} + carrier_duty_percent: 50% + clock_resolution: ${clock_resolution} + one_wire: ${one_wire} + rmt_symbols: ${rmt_symbols} + use_dma: ${use_dma} + +packages: + buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/esp32-common.yaml b/tests/components/remote_transmitter/esp32-common.yaml deleted file mode 100644 index 3f3cd3f8c7..0000000000 --- a/tests/components/remote_transmitter/esp32-common.yaml +++ /dev/null @@ -1,8 +0,0 @@ -remote_transmitter: - id: rcvr - pin: ${pin} - rmt_channel: ${rmt_channel} - carrier_duty_percent: 50% - -packages: - buttons: !include common-buttons.yaml diff --git a/tests/components/remote_transmitter/test.esp32-ard.yaml b/tests/components/remote_transmitter/test.esp32-ard.yaml index 16d276958a..5d29187206 100644 --- a/tests/components/remote_transmitter/test.esp32-ard.yaml +++ b/tests/components/remote_transmitter/test.esp32-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "2" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml b/tests/components/remote_transmitter/test.esp32-c3-ard.yaml index 3e2dc88e5a..c755b11563 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-ard.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-ard.yaml @@ -3,4 +3,4 @@ substitutions: rmt_channel: "1" packages: - common: !include esp32-common.yaml + common: !include esp32-common-ard.yaml diff --git a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml index 3e2dc88e5a..1a27f29dac 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO2 - rmt_channel: "1" + clock_resolution: "2000000" + one_wire: "true" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + 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 16d276958a..1a27f29dac 100644 --- a/tests/components/remote_transmitter/test.esp32-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-idf.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO2 - rmt_channel: "2" + clock_resolution: "2000000" + one_wire: "true" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + 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 31851dc54c..25bdbd4772 100644 --- a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml @@ -1,6 +1,9 @@ substitutions: pin: GPIO38 - rmt_channel: "3" + clock_resolution: "2000000" + one_wire: "true" + rmt_symbols: "64" + use_dma: "true" packages: - common: !include esp32-common.yaml + common: !include esp32-common-idf.yaml diff --git a/tests/components/remote_transmitter/test.esp8266-ard.yaml b/tests/components/remote_transmitter/test.esp8266-ard.yaml index de494485f4..19759360f4 100644 --- a/tests/components/remote_transmitter/test.esp8266-ard.yaml +++ b/tests/components/remote_transmitter/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ remote_transmitter: - id: trns + id: xmitr pin: GPIO5 carrier_duty_percent: 50% From ac631711ab47019f31b31f9dbb5bf9062a899251 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:30:23 +1100 Subject: [PATCH 0822/1052] [qspi_dbi] Bugfix and new features (#7979) --- esphome/components/lvgl/__init__.py | 6 +- esphome/components/qspi_dbi/__init__.py | 3 + esphome/components/qspi_dbi/display.py | 123 ++++++----- esphome/components/qspi_dbi/models.py | 247 ++++++++++++++++++++++- esphome/components/qspi_dbi/qspi_dbi.cpp | 25 +-- esphome/components/qspi_dbi/qspi_dbi.h | 4 +- 6 files changed, 339 insertions(+), 69 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 8fdd03f647..b858e8df01 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -23,7 +23,7 @@ from esphome.helpers import write_file_if_changed from . import defines as df, helpers, lv_validation as lvalid from .automation import disp_update, focused_widgets, update_to_code -from .defines import add_define +from .defines import CONF_DRAW_ROUNDING, add_define from .encoders import ( ENCODERS_CONFIG, encoders_to_code, @@ -205,6 +205,10 @@ def final_validation(configs): raise cv.Invalid( "Using auto_clear_enabled: true in display config not compatible with LVGL" ) + if draw_rounding := display.get(CONF_DRAW_ROUNDING): + config[CONF_DRAW_ROUNDING] = max( + draw_rounding, config[CONF_DRAW_ROUNDING] + ) buffer_frac = config[CONF_BUFFER_SIZE] if CORE.is_esp32 and buffer_frac > 0.5 and "psram" not in global_config: LOGGER.warning("buffer_size: may need to be reduced without PSRAM") diff --git a/esphome/components/qspi_dbi/__init__.py b/esphome/components/qspi_dbi/__init__.py index c58ce8a01e..a4b833f6d7 100644 --- a/esphome/components/qspi_dbi/__init__.py +++ b/esphome/components/qspi_dbi/__init__.py @@ -1 +1,4 @@ CODEOWNERS = ["@clydebarrow"] + +CONF_DRAW_FROM_ORIGIN = "draw_from_origin" +CONF_DRAW_ROUNDING = "draw_rounding" diff --git a/esphome/components/qspi_dbi/display.py b/esphome/components/qspi_dbi/display.py index 71ae31f182..ab6dd66cf2 100644 --- a/esphome/components/qspi_dbi/display.py +++ b/esphome/components/qspi_dbi/display.py @@ -24,6 +24,7 @@ from esphome.const import ( ) from esphome.core import TimePeriod +from . import CONF_DRAW_FROM_ORIGIN, CONF_DRAW_ROUNDING from .models import DriverChip DEPENDENCIES = ["spi"] @@ -41,7 +42,6 @@ COLOR_ORDERS = { } DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema -CONF_DRAW_FROM_ORIGIN = "draw_from_origin" DELAY_FLAG = 0xFF @@ -78,56 +78,81 @@ def _validate(config): return config -CONFIG_SCHEMA = cv.All( - display.FULL_DISPLAY_SCHEMA.extend( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(QSPI_DBI), - cv.Required(CONF_MODEL): cv.one_of( - *DriverChip.chips.keys(), upper=True - ), - cv.Optional(CONF_INIT_SEQUENCE): cv.ensure_list(map_sequence), - cv.Required(CONF_DIMENSIONS): cv.Any( - cv.dimensions, - cv.Schema( - { - cv.Required(CONF_WIDTH): validate_dimension, - cv.Required(CONF_HEIGHT): validate_dimension, - cv.Optional( - CONF_OFFSET_HEIGHT, default=0 - ): validate_dimension, - cv.Optional( - CONF_OFFSET_WIDTH, default=0 - ): validate_dimension, - } - ), - ), - cv.Optional(CONF_TRANSFORM): cv.Schema( +def power_of_two(value): + value = cv.int_range(1, 128)(value) + if value & (value - 1) != 0: + raise cv.Invalid("value must be a power of two") + return value + + +BASE_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(QSPI_DBI), + cv.Optional(CONF_INIT_SEQUENCE): cv.ensure_list(map_sequence), + cv.Required(CONF_DIMENSIONS): cv.Any( + cv.dimensions, + cv.Schema( { - cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, - cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, - cv.Optional(CONF_SWAP_XY, default=False): cv.boolean, + cv.Required(CONF_WIDTH): validate_dimension, + cv.Required(CONF_HEIGHT): validate_dimension, + cv.Optional(CONF_OFFSET_HEIGHT, default=0): validate_dimension, + cv.Optional(CONF_OFFSET_WIDTH, default=0): validate_dimension, } ), - cv.Optional(CONF_COLOR_ORDER, default="RGB"): cv.enum( - COLOR_ORDERS, upper=True - ), - cv.Optional(CONF_INVERT_COLORS, default=False): cv.boolean, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_BRIGHTNESS, default=0xD0): cv.int_range( - 0, 0xFF, min_included=True, max_included=True - ), - cv.Optional(CONF_DRAW_FROM_ORIGIN, default=False): cv.boolean, - } - ).extend( - spi.spi_device_schema( - cs_pin_required=False, - default_mode="MODE0", - default_data_rate=10e6, - quad=True, - ) + ), + cv.Optional(CONF_DRAW_FROM_ORIGIN, default=False): cv.boolean, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_ENABLE_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_BRIGHTNESS, default=0xD0): cv.int_range( + 0, 0xFF, min_included=True, max_included=True + ), + } + ).extend( + spi.spi_device_schema( + cs_pin_required=False, + default_mode="MODE0", + default_data_rate=10e6, + quad=True, ) + ) +) + + +def model_property(name, defaults, fallback): + return cv.Optional(name, default=defaults.get(name, fallback)) + + +def model_schema(defaults): + transform = cv.Schema( + { + cv.Optional(CONF_MIRROR_X, default=False): cv.boolean, + cv.Optional(CONF_MIRROR_Y, default=False): cv.boolean, + } + ) + if defaults.get(CONF_SWAP_XY, True): + transform = transform.extend( + { + cv.Optional(CONF_SWAP_XY, default=False): cv.boolean, + } + ) + return BASE_SCHEMA.extend( + { + model_property(CONF_INVERT_COLORS, defaults, False): cv.boolean, + model_property(CONF_COLOR_ORDER, defaults, "RGB"): cv.enum( + COLOR_ORDERS, upper=True + ), + model_property(CONF_DRAW_ROUNDING, defaults, 2): power_of_two, + cv.Optional(CONF_TRANSFORM): transform, + } + ) + + +CONFIG_SCHEMA = cv.All( + cv.typed_schema( + {k.upper(): model_schema(v.defaults) for k, v in DriverChip.chips.items()}, + upper=True, + key=CONF_MODEL, ), cv.only_with_esp_idf, ) @@ -152,6 +177,7 @@ async def to_code(config): cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) cg.add(var.set_model(config[CONF_MODEL])) cg.add(var.set_draw_from_origin(config[CONF_DRAW_FROM_ORIGIN])) + cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING])) if enable_pin := config.get(CONF_ENABLE_PIN): enable = await cg.gpio_pin_expression(enable_pin) cg.add(var.set_enable_pin(enable)) @@ -163,7 +189,8 @@ async def to_code(config): 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_swap_xy(transform[CONF_SWAP_XY])) + # swap_xy is not implemented for some chips + cg.add(var.set_swap_xy(transform.get(CONF_SWAP_XY, False))) if CONF_DIMENSIONS in config: dimensions = config[CONF_DIMENSIONS] diff --git a/esphome/components/qspi_dbi/models.py b/esphome/components/qspi_dbi/models.py index c1fe434853..7ae1a10ec0 100644 --- a/esphome/components/qspi_dbi/models.py +++ b/esphome/components/qspi_dbi/models.py @@ -1,5 +1,10 @@ # Commands +from esphome.const import CONF_INVERT_COLORS, CONF_SWAP_XY + +from . import CONF_DRAW_ROUNDING + SW_RESET_CMD = 0x01 +SLEEP_IN = 0x10 SLEEP_OUT = 0x11 NORON = 0x13 INVERT_OFF = 0x20 @@ -24,11 +29,12 @@ PAGESEL = 0xFE class DriverChip: chips = {} - def __init__(self, name: str): + def __init__(self, name: str, defaults=None): name = name.upper() self.name = name self.chips[name] = self self.initsequence = [] + self.defaults = defaults or {} def cmd(self, c, *args): """ @@ -59,9 +65,246 @@ chip.cmd(TEON, 0x00) chip.cmd(PIXFMT, 0x55) chip.cmd(NORON) -chip = DriverChip("AXS15231") +chip = DriverChip("AXS15231", {CONF_DRAW_ROUNDING: 8, CONF_SWAP_XY: False}) chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) chip.cmd(0xC1, 0x33) chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) +chip = DriverChip( + "JC4832W535", + { + CONF_DRAW_ROUNDING: 8, + CONF_SWAP_XY: False, + }, +) +chip.cmd(DISPLAY_OFF) +chip.delay(20) +chip.cmd(SLEEP_IN) +chip.delay(80) +chip.cmd(SLEEP_OUT) +chip.cmd(INVERT_OFF) +# A magic sequence to enable the windowed drawing mode +chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5) +chip.cmd(0xC1, 0x33) +chip.cmd(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) + +chip = DriverChip("JC3636W518", {CONF_INVERT_COLORS: True}) +chip.cmd(0xF0, 0x08) +chip.cmd(0xF2, 0x08) +chip.cmd(0x9B, 0x51) +chip.cmd(0x86, 0x53) +chip.cmd(0xF2, 0x80) +chip.cmd(0xF0, 0x00) +chip.cmd(0xF0, 0x01) +chip.cmd(0xF1, 0x01) +chip.cmd(0xB0, 0x54) +chip.cmd(0xB1, 0x3F) +chip.cmd(0xB2, 0x2A) +chip.cmd(0xB4, 0x46) +chip.cmd(0xB5, 0x34) +chip.cmd(0xB6, 0xD5) +chip.cmd(0xB7, 0x30) +chip.cmd(0xBA, 0x00) +chip.cmd(0xBB, 0x08) +chip.cmd(0xBC, 0x08) +chip.cmd(0xBD, 0x00) +chip.cmd(0xC0, 0x80) +chip.cmd(0xC1, 0x10) +chip.cmd(0xC2, 0x37) +chip.cmd(0xC3, 0x80) +chip.cmd(0xC4, 0x10) +chip.cmd(0xC5, 0x37) +chip.cmd(0xC6, 0xA9) +chip.cmd(0xC7, 0x41) +chip.cmd(0xC8, 0x51) +chip.cmd(0xC9, 0xA9) +chip.cmd(0xCA, 0x41) +chip.cmd(0xCB, 0x51) +chip.cmd(0xD0, 0x91) +chip.cmd(0xD1, 0x68) +chip.cmd(0xD2, 0x69) +chip.cmd(0xF5, 0x00, 0xA5) +chip.cmd(0xDD, 0x3F) +chip.cmd(0xDE, 0x3F) +chip.cmd(0xF1, 0x10) +chip.cmd(0xF0, 0x00) +chip.cmd(0xF0, 0x02) +chip.cmd( + 0xE0, + 0x70, + 0x09, + 0x12, + 0x0C, + 0x0B, + 0x27, + 0x38, + 0x54, + 0x4E, + 0x19, + 0x15, + 0x15, + 0x2C, + 0x2F, +) +chip.cmd( + 0xE1, + 0x70, + 0x08, + 0x11, + 0x0C, + 0x0B, + 0x27, + 0x38, + 0x43, + 0x4C, + 0x18, + 0x14, + 0x14, + 0x2B, + 0x2D, +) +chip.cmd(0xF0, 0x10) +chip.cmd(0xF3, 0x10) +chip.cmd(0xE0, 0x08) +chip.cmd(0xE1, 0x00) +chip.cmd(0xE2, 0x00) +chip.cmd(0xE3, 0x00) +chip.cmd(0xE4, 0xE0) +chip.cmd(0xE5, 0x06) +chip.cmd(0xE6, 0x21) +chip.cmd(0xE7, 0x00) +chip.cmd(0xE8, 0x05) +chip.cmd(0xE9, 0x82) +chip.cmd(0xEA, 0xDF) +chip.cmd(0xEB, 0x89) +chip.cmd(0xEC, 0x20) +chip.cmd(0xED, 0x14) +chip.cmd(0xEE, 0xFF) +chip.cmd(0xEF, 0x00) +chip.cmd(0xF8, 0xFF) +chip.cmd(0xF9, 0x00) +chip.cmd(0xFA, 0x00) +chip.cmd(0xFB, 0x30) +chip.cmd(0xFC, 0x00) +chip.cmd(0xFD, 0x00) +chip.cmd(0xFE, 0x00) +chip.cmd(0xFF, 0x00) +chip.cmd(0x60, 0x42) +chip.cmd(0x61, 0xE0) +chip.cmd(0x62, 0x40) +chip.cmd(0x63, 0x40) +chip.cmd(0x64, 0x02) +chip.cmd(0x65, 0x00) +chip.cmd(0x66, 0x40) +chip.cmd(0x67, 0x03) +chip.cmd(0x68, 0x00) +chip.cmd(0x69, 0x00) +chip.cmd(0x6A, 0x00) +chip.cmd(0x6B, 0x00) +chip.cmd(0x70, 0x42) +chip.cmd(0x71, 0xE0) +chip.cmd(0x72, 0x40) +chip.cmd(0x73, 0x40) +chip.cmd(0x74, 0x02) +chip.cmd(0x75, 0x00) +chip.cmd(0x76, 0x40) +chip.cmd(0x77, 0x03) +chip.cmd(0x78, 0x00) +chip.cmd(0x79, 0x00) +chip.cmd(0x7A, 0x00) +chip.cmd(0x7B, 0x00) +chip.cmd(0x80, 0x48) +chip.cmd(0x81, 0x00) +chip.cmd(0x82, 0x05) +chip.cmd(0x83, 0x02) +chip.cmd(0x84, 0xDD) +chip.cmd(0x85, 0x00) +chip.cmd(0x86, 0x00) +chip.cmd(0x87, 0x00) +chip.cmd(0x88, 0x48) +chip.cmd(0x89, 0x00) +chip.cmd(0x8A, 0x07) +chip.cmd(0x8B, 0x02) +chip.cmd(0x8C, 0xDF) +chip.cmd(0x8D, 0x00) +chip.cmd(0x8E, 0x00) +chip.cmd(0x8F, 0x00) +chip.cmd(0x90, 0x48) +chip.cmd(0x91, 0x00) +chip.cmd(0x92, 0x09) +chip.cmd(0x93, 0x02) +chip.cmd(0x94, 0xE1) +chip.cmd(0x95, 0x00) +chip.cmd(0x96, 0x00) +chip.cmd(0x97, 0x00) +chip.cmd(0x98, 0x48) +chip.cmd(0x99, 0x00) +chip.cmd(0x9A, 0x0B) +chip.cmd(0x9B, 0x02) +chip.cmd(0x9C, 0xE3) +chip.cmd(0x9D, 0x00) +chip.cmd(0x9E, 0x00) +chip.cmd(0x9F, 0x00) +chip.cmd(0xA0, 0x48) +chip.cmd(0xA1, 0x00) +chip.cmd(0xA2, 0x04) +chip.cmd(0xA3, 0x02) +chip.cmd(0xA4, 0xDC) +chip.cmd(0xA5, 0x00) +chip.cmd(0xA6, 0x00) +chip.cmd(0xA7, 0x00) +chip.cmd(0xA8, 0x48) +chip.cmd(0xA9, 0x00) +chip.cmd(0xAA, 0x06) +chip.cmd(0xAB, 0x02) +chip.cmd(0xAC, 0xDE) +chip.cmd(0xAD, 0x00) +chip.cmd(0xAE, 0x00) +chip.cmd(0xAF, 0x00) +chip.cmd(0xB0, 0x48) +chip.cmd(0xB1, 0x00) +chip.cmd(0xB2, 0x08) +chip.cmd(0xB3, 0x02) +chip.cmd(0xB4, 0xE0) +chip.cmd(0xB5, 0x00) +chip.cmd(0xB6, 0x00) +chip.cmd(0xB7, 0x00) +chip.cmd(0xB8, 0x48) +chip.cmd(0xB9, 0x00) +chip.cmd(0xBA, 0x0A) +chip.cmd(0xBB, 0x02) +chip.cmd(0xBC, 0xE2) +chip.cmd(0xBD, 0x00) +chip.cmd(0xBE, 0x00) +chip.cmd(0xBF, 0x00) +chip.cmd(0xC0, 0x12) +chip.cmd(0xC1, 0xAA) +chip.cmd(0xC2, 0x65) +chip.cmd(0xC3, 0x74) +chip.cmd(0xC4, 0x47) +chip.cmd(0xC5, 0x56) +chip.cmd(0xC6, 0x00) +chip.cmd(0xC7, 0x88) +chip.cmd(0xC8, 0x99) +chip.cmd(0xC9, 0x33) +chip.cmd(0xD0, 0x21) +chip.cmd(0xD1, 0xAA) +chip.cmd(0xD2, 0x65) +chip.cmd(0xD3, 0x74) +chip.cmd(0xD4, 0x47) +chip.cmd(0xD5, 0x56) +chip.cmd(0xD6, 0x00) +chip.cmd(0xD7, 0x88) +chip.cmd(0xD8, 0x99) +chip.cmd(0xD9, 0x33) +chip.cmd(0xF3, 0x01) +chip.cmd(0xF0, 0x00) +chip.cmd(0xF0, 0x01) +chip.cmd(0xF1, 0x01) +chip.cmd(0xA0, 0x0B) +chip.cmd(0xA3, 0x2A) +chip.cmd(0xA5, 0xC3) +chip.cmd(PIXFMT, 0x55) + + DriverChip("Custom") diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index f8fd5dd374..380c93c400 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -33,19 +33,12 @@ void QspiDbi::update() { this->do_update_(); if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) return; - // Start addresses and widths/heights must be divisible by 2 (CASET/RASET restriction in datasheet) - if (this->x_low_ % 2 == 1) { - this->x_low_--; - } - if (this->x_high_ % 2 == 0) { - this->x_high_++; - } - if (this->y_low_ % 2 == 1) { - this->y_low_--; - } - if (this->y_high_ % 2 == 0) { - this->y_high_++; - } + // Some chips require that the drawing window be aligned on certain boundaries + auto dr = this->draw_rounding_; + this->x_low_ = this->x_low_ / dr * dr; + this->y_low_ = this->y_low_ / dr * dr; + this->x_high_ = (this->x_high_ + dr) / dr * dr - 1; + this->y_high_ = (this->y_high_ + dr) / dr * dr - 1; if (this->draw_from_origin_) { this->x_low_ = 0; this->y_low_ = 0; @@ -175,10 +168,9 @@ void QspiDbi::write_to_display_(int x_start, int y_start, int w, int h, const ui this->write_cmd_addr_data(8, 0x32, 24, 0x2C00, ptr, w * h * 2, 4); } else { auto stride = x_offset + w + x_pad; - uint16_t cmd = 0x2C00; + this->write_cmd_addr_data(8, 0x32, 24, 0x2C00, nullptr, 0, 4); for (int y = 0; y != h; y++) { - this->write_cmd_addr_data(8, 0x32, 24, cmd, ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2, 4); - cmd = 0x3C00; + this->write_cmd_addr_data(0, 0, 0, 0, ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2, 4); } } this->disable(); @@ -220,6 +212,7 @@ void QspiDbi::dump_config() { ESP_LOGCONFIG("", "Model: %s", this->model_); ESP_LOGCONFIG(TAG, " Height: %u", this->height_); ESP_LOGCONFIG(TAG, " Width: %u", this->width_); + ESP_LOGCONFIG(TAG, " Draw rounding: %u", this->draw_rounding_); LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" Reset Pin: ", this->reset_pin_); ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000)); diff --git a/esphome/components/qspi_dbi/qspi_dbi.h b/esphome/components/qspi_dbi/qspi_dbi.h index ebb65a8a05..2c555f115e 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.h +++ b/esphome/components/qspi_dbi/qspi_dbi.h @@ -4,12 +4,10 @@ #pragma once #ifdef USE_ESP_IDF -#include "esphome/core/component.h" #include "esphome/components/spi/spi.h" #include "esphome/components/display/display.h" #include "esphome/components/display/display_buffer.h" #include "esphome/components/display/display_color_utils.h" -#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_rgb.h" @@ -105,6 +103,7 @@ class QspiDbi : public display::DisplayBuffer, int get_height_internal() override { return this->height_; } bool can_proceed() override { return this->setup_complete_; } void add_init_sequence(const std::vector &sequence) { this->init_sequences_.push_back(sequence); } + void set_draw_rounding(unsigned rounding) { this->draw_rounding_ = rounding; } protected: void check_buffer_() { @@ -161,6 +160,7 @@ class QspiDbi : public display::DisplayBuffer, bool mirror_x_{}; bool mirror_y_{}; bool draw_from_origin_{false}; + unsigned draw_rounding_{2}; uint8_t brightness_{0xD0}; const char *model_{"Unknown"}; std::vector> init_sequences_{}; From b33b4481ea031c19280e3148148be40d99484f56 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 19 Dec 2024 14:40:08 +1100 Subject: [PATCH 0823/1052] [helpers] Provide calls to get free heap and largest available block. (#7978) --- esphome/components/json/json_util.cpp | 34 ++-------- .../components/online_image/online_image.cpp | 10 +-- esphome/core/helpers.h | 63 ++++++++++++++++--- 3 files changed, 61 insertions(+), 46 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 89ec13fe5b..87b1cc6d2d 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -1,38 +1,20 @@ #include "json_util.h" #include "esphome/core/log.h" -#ifdef USE_ESP8266 -#include -#endif -#ifdef USE_ESP32 -#include -#endif -#ifdef USE_RP2040 -#include -#endif - namespace esphome { namespace json { static const char *const TAG = "json"; static std::vector global_json_build_buffer; // NOLINT +static const auto ALLOCATOR = RAMAllocator(RAMAllocator::ALLOC_INTERNAL); std::string build_json(const json_build_t &f) { // Here we are allocating up to 5kb of memory, // with the heap size minus 2kb to be safe if less than 5kb // as we can not have a true dynamic sized document. // The excess memory is freed below with `shrinkToFit()` -#ifdef USE_ESP8266 - const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) -#elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); -#elif defined(USE_RP2040) - const size_t free_heap = rp2040.getFreeHeap(); -#elif defined(USE_LIBRETINY) - const size_t free_heap = lt_heap_get_free(); -#endif - + auto free_heap = ALLOCATOR.get_max_free_block_size(); size_t request_size = std::min(free_heap, (size_t) 512); while (true) { ESP_LOGV(TAG, "Attempting to allocate %u bytes for JSON serialization", request_size); @@ -67,20 +49,12 @@ bool parse_json(const std::string &data, const json_parse_t &f) { // with the heap size minus 2kb to be safe if less than that // as we can not have a true dynamic sized document. // The excess memory is freed below with `shrinkToFit()` -#ifdef USE_ESP8266 - const size_t free_heap = ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) -#elif defined(USE_ESP32) - const size_t free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); -#elif defined(USE_RP2040) - const size_t free_heap = rp2040.getFreeHeap(); -#elif defined(USE_LIBRETINY) - const size_t free_heap = lt_heap_get_free(); -#endif + auto free_heap = ALLOCATOR.get_max_free_block_size(); size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5)); while (true) { DynamicJsonDocument json_document(request_size); if (json_document.capacity() == 0) { - ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %u bytes, free heap: %u", request_size, + ESP_LOGE(TAG, "Could not allocate memory for JSON document! Requested %zu bytes, free heap: %zu", request_size, free_heap); return false; } diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 1786809dfa..8c4669cba5 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -80,15 +80,7 @@ bool OnlineImage::resize_(int width_in, int height_in) { this->width_ = width; ESP_LOGD(TAG, "New size: (%d, %d)", width, height); } else { -#if defined(USE_ESP8266) - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - int max_block = ESP.getMaxFreeBlockSize(); -#elif defined(USE_ESP32) - int max_block = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL); -#else - int max_block = -1; -#endif - ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %d Bytes", max_block); + ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %zu Bytes", this->allocator_.get_max_free_block_size()); this->end_connection_(); return false; } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index fcbd8d8683..c823439fb3 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -11,6 +11,14 @@ #include "esphome/core/optional.h" +#ifdef USE_ESP8266 +#include +#endif + +#ifdef USE_RP2040 +#include +#endif + #ifdef USE_ESP32 #include #endif @@ -684,20 +692,23 @@ template class RAMAllocator { }; RAMAllocator() = default; - RAMAllocator(uint8_t flags) : flags_{flags} {} + RAMAllocator(uint8_t flags) { + // default is both external and internal + flags &= ALLOC_INTERNAL | ALLOC_EXTERNAL; + if (flags != 0) + this->flags_ = flags; + } template constexpr RAMAllocator(const RAMAllocator &other) : flags_{other.flags_} {} T *allocate(size_t n) { size_t size = n * sizeof(T); T *ptr = nullptr; #ifdef USE_ESP32 - // External allocation by default or if explicitely requested - if ((this->flags_ & Flags::ALLOC_EXTERNAL) || ((this->flags_ & Flags::ALLOC_INTERNAL) == 0)) { + if (this->flags_ & Flags::ALLOC_EXTERNAL) { ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT)); } - // Fallback to internal allocation if explicitely requested or no flag is specified - if (ptr == nullptr && ((this->flags_ & Flags::ALLOC_INTERNAL) || (this->flags_ & Flags::ALLOC_EXTERNAL) == 0)) { - ptr = static_cast(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) + if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) { + ptr = static_cast(heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); } #else // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported @@ -710,8 +721,46 @@ template class RAMAllocator { free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) } + /** + * Return the total heap space available via this allocator + */ + size_t get_free_heap_size() const { +#ifdef USE_ESP8266 + return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP32) + auto max_internal = + this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0; + auto max_external = + this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0; + return max_internal + max_external; +#elif defined(USE_RP2040) + return ::rp2040.getFreeHeap(); +#elif defined(USE_LIBRETINY) + return lt_heap_get_free(); +#else + return 100000; +#endif + } + + /** + * Return the maximum size block this allocator could allocate. This may be an approximation on some platforms + */ + size_t get_max_free_block_size() const { +#ifdef USE_ESP8266 + return ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance) +#elif defined(USE_ESP32) + auto max_internal = + this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0; + auto max_external = + this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0; + return std::max(max_internal, max_external); +#else + return this->get_free_heap_size(); +#endif + } + private: - uint8_t flags_{Flags::ALLOW_FAILURE}; + uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL}; }; template using ExternalRAMAllocator = RAMAllocator; From 7da07303c934287837797a40b05526460a6734aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:42:29 +1300 Subject: [PATCH 0824/1052] Bump actions/upload-artifact from 4.4.3 to 4.5.0 (#7981) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6cd86854b2..0304cd2304 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT - name: Upload digests - uses: actions/upload-artifact@v4.4.3 + uses: actions/upload-artifact@v4.5.0 with: name: digests-${{ steps.sanitize.outputs.name }} path: /tmp/digests From 434879ea0464ed65f24f3446c80412c90cd37963 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Wed, 18 Dec 2024 21:07:07 -0500 Subject: [PATCH 0825/1052] [core] Bugfix: Implement ring buffer with xRingbuffer (#7973) --- esphome/core/ring_buffer.cpp | 90 +++++++++++++++++++++++++++++------- esphome/core/ring_buffer.h | 13 ++++-- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/esphome/core/ring_buffer.cpp b/esphome/core/ring_buffer.cpp index 6152ada314..f779531263 100644 --- a/esphome/core/ring_buffer.cpp +++ b/esphome/core/ring_buffer.cpp @@ -13,8 +13,8 @@ static const char *const TAG = "ring_buffer"; RingBuffer::~RingBuffer() { if (this->handle_ != nullptr) { - vStreamBufferDelete(this->handle_); - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + vRingbufferDelete(this->handle_); + RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); allocator.deallocate(this->storage_, this->size_); } } @@ -22,26 +22,49 @@ RingBuffer::~RingBuffer() { std::unique_ptr RingBuffer::create(size_t len) { std::unique_ptr rb = make_unique(); - rb->size_ = len + 1; + rb->size_ = len; - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + RAMAllocator allocator(RAMAllocator::ALLOW_FAILURE); rb->storage_ = allocator.allocate(rb->size_); if (rb->storage_ == nullptr) { return nullptr; } - rb->handle_ = xStreamBufferCreateStatic(rb->size_, 1, rb->storage_, &rb->structure_); + rb->handle_ = xRingbufferCreateStatic(rb->size_, RINGBUF_TYPE_BYTEBUF, rb->storage_, &rb->structure_); ESP_LOGD(TAG, "Created ring buffer with size %u", len); + return rb; } size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { - if (ticks_to_wait > 0) - xStreamBufferSetTriggerLevel(this->handle_, len); + size_t bytes_read = 0; - size_t bytes_read = xStreamBufferReceive(this->handle_, data, len, ticks_to_wait); + void *buffer_data = xRingbufferReceiveUpTo(this->handle_, &bytes_read, ticks_to_wait, len); - xStreamBufferSetTriggerLevel(this->handle_, 1); + if (buffer_data == nullptr) { + return 0; + } + + std::memcpy(data, buffer_data, bytes_read); + + vRingbufferReturnItem(this->handle_, buffer_data); + + if (bytes_read < len) { + // Data may have wrapped around, so read a second time to receive the remainder + size_t follow_up_bytes_read = 0; + size_t bytes_remaining = len - bytes_read; + + buffer_data = xRingbufferReceiveUpTo(this->handle_, &follow_up_bytes_read, 0, bytes_remaining); + + if (buffer_data == nullptr) { + return bytes_read; + } + + std::memcpy((void *) ((uint8_t *) (data) + bytes_read), buffer_data, follow_up_bytes_read); + + vRingbufferReturnItem(this->handle_, buffer_data); + bytes_read += follow_up_bytes_read; + } return bytes_read; } @@ -49,22 +72,55 @@ size_t RingBuffer::read(void *data, size_t len, TickType_t ticks_to_wait) { size_t RingBuffer::write(const void *data, size_t len) { size_t free = this->free(); if (free < len) { - size_t needed = len - free; - uint8_t discard[needed]; - xStreamBufferReceive(this->handle_, discard, needed, 0); + // Free enough space in the ring buffer to fit the new data + this->discard_bytes_(len - free); } - return xStreamBufferSend(this->handle_, data, len, 0); + return this->write_without_replacement(data, len, 0); } size_t RingBuffer::write_without_replacement(const void *data, size_t len, TickType_t ticks_to_wait) { - return xStreamBufferSend(this->handle_, data, len, ticks_to_wait); + if (!xRingbufferSend(this->handle_, data, len, ticks_to_wait)) { + // Couldn't fit all the data, so only write what will fit + size_t free = std::min(this->free(), len); + if (xRingbufferSend(this->handle_, data, free, 0)) { + return free; + } + return 0; + } + return len; } -size_t RingBuffer::available() const { return xStreamBufferBytesAvailable(this->handle_); } +size_t RingBuffer::available() const { + UBaseType_t ux_items_waiting = 0; + vRingbufferGetInfo(this->handle_, nullptr, nullptr, nullptr, nullptr, &ux_items_waiting); + return ux_items_waiting; +} -size_t RingBuffer::free() const { return xStreamBufferSpacesAvailable(this->handle_); } +size_t RingBuffer::free() const { return xRingbufferGetCurFreeSize(this->handle_); } -BaseType_t RingBuffer::reset() { return xStreamBufferReset(this->handle_); } +BaseType_t RingBuffer::reset() { + // Discards all the available data + return this->discard_bytes_(this->available()); +} + +bool RingBuffer::discard_bytes_(size_t discard_bytes) { + size_t bytes_read = 0; + + void *buffer_data = xRingbufferReceiveUpTo(this->handle_, &bytes_read, 0, discard_bytes); + if (buffer_data != nullptr) + vRingbufferReturnItem(this->handle_, buffer_data); + + if (bytes_read < discard_bytes) { + size_t wrapped_bytes_read = 0; + buffer_data = xRingbufferReceiveUpTo(this->handle_, &wrapped_bytes_read, 0, discard_bytes - bytes_read); + if (buffer_data != nullptr) { + vRingbufferReturnItem(this->handle_, buffer_data); + bytes_read += wrapped_bytes_read; + } + } + + return (bytes_read == discard_bytes); +} } // namespace esphome diff --git a/esphome/core/ring_buffer.h b/esphome/core/ring_buffer.h index aade1b5f49..bad96d3181 100644 --- a/esphome/core/ring_buffer.h +++ b/esphome/core/ring_buffer.h @@ -3,7 +3,7 @@ #ifdef USE_ESP32 #include -#include +#include #include #include @@ -82,9 +82,14 @@ class RingBuffer { static std::unique_ptr create(size_t len); protected: - StreamBufferHandle_t handle_; - StaticStreamBuffer_t structure_; - uint8_t *storage_; + /// @brief Discards data from the ring buffer. + /// @param discard_bytes amount of bytes to discard + /// @return True if all bytes were successfully discarded, false otherwise + bool discard_bytes_(size_t discard_bytes); + + RingbufHandle_t handle_{nullptr}; + StaticRingbuffer_t structure_; + uint8_t *storage_{nullptr}; size_t size_{0}; }; From d28cf011d116c0cfb5f93130d13da131c62f97fb Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:07:43 +1300 Subject: [PATCH 0826/1052] Bump version to 2024.12.1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index e9d7612e4a..5b87cad8e1 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.0" +__version__ = "2024.12.1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From f33b4a714ecd4e43d9f2f27f457d1dc8bad70a29 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Fri, 20 Dec 2024 01:45:40 +0100 Subject: [PATCH 0827/1052] [esp32_ble] do not skip events if queue is blocked (#7960) --- esphome/components/esp32_ble/queue.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/esp32_ble/queue.h b/esphome/components/esp32_ble/queue.h index 5b31b97ae2..c98477e121 100644 --- a/esphome/components/esp32_ble/queue.h +++ b/esphome/components/esp32_ble/queue.h @@ -26,10 +26,10 @@ template class Queue { void push(T *element) { if (element == nullptr) return; - if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) { - q_.push(element); - xSemaphoreGive(m_); - } + // It is not called from main loop. Thus it won't block main thread. + xSemaphoreTake(m_, portMAX_DELAY); + q_.push(element); + xSemaphoreGive(m_); } T *pop() { From ba2edbc18909953e0de22ee95e57726d476e8f05 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 20 Dec 2024 01:28:08 -0600 Subject: [PATCH 0828/1052] [esp32] Fix flash size warning when using IDF (#7983) --- esphome/components/esp32/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index b0bde75451..98db45831a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -602,6 +602,9 @@ async def to_code(config): cg.add_platformio_option( "platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"] ) + add_idf_sdkconfig_option( + f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True + ) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True) add_idf_sdkconfig_option( From f3cb179f54e7b86133e9707b6ad5dcd41eeda989 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 20 Dec 2024 14:16:18 -0600 Subject: [PATCH 0829/1052] [esp32_ble] Fix for Improv (#7984) --- esphome/components/esp32_ble/ble_advertising.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index 92b7c60368..1d340c76d9 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -83,7 +83,7 @@ esp_err_t BLEAdvertising::services_advertisement_() { esp_err_t err; this->advertising_data_.set_scan_rsp = false; - this->advertising_data_.include_name = true; + this->advertising_data_.include_name = !this->scan_response_; this->advertising_data_.include_txpower = !this->scan_response_; err = esp_ble_gap_config_adv_data(&this->advertising_data_); if (err != ESP_OK) { From 37fcccbb1c28507b89c70b015b3e119f60181f88 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 20 Dec 2024 01:28:08 -0600 Subject: [PATCH 0830/1052] [esp32] Fix flash size warning when using IDF (#7983) --- esphome/components/esp32/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index b0bde75451..98db45831a 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -602,6 +602,9 @@ async def to_code(config): cg.add_platformio_option( "platform_packages", ["espressif/toolchain-esp32ulp@2.35.0-20220830"] ) + add_idf_sdkconfig_option( + f"CONFIG_ESPTOOLPY_FLASHSIZE_{config[CONF_FLASH_SIZE]}", True + ) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_SINGLE_APP", False) add_idf_sdkconfig_option("CONFIG_PARTITION_TABLE_CUSTOM", True) add_idf_sdkconfig_option( From 69f1a81e1ddaa97ef4444cb551784787dd97fb17 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 20 Dec 2024 14:16:18 -0600 Subject: [PATCH 0831/1052] [esp32_ble] Fix for Improv (#7984) --- esphome/components/esp32_ble/ble_advertising.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_ble/ble_advertising.cpp b/esphome/components/esp32_ble/ble_advertising.cpp index 92b7c60368..1d340c76d9 100644 --- a/esphome/components/esp32_ble/ble_advertising.cpp +++ b/esphome/components/esp32_ble/ble_advertising.cpp @@ -83,7 +83,7 @@ esp_err_t BLEAdvertising::services_advertisement_() { esp_err_t err; this->advertising_data_.set_scan_rsp = false; - this->advertising_data_.include_name = true; + this->advertising_data_.include_name = !this->scan_response_; this->advertising_data_.include_txpower = !this->scan_response_; err = esp_ble_gap_config_adv_data(&this->advertising_data_); if (err != ESP_OK) { From 499953e3f4627b146e69fd1f3c8ab6dda2de7a5f Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Fri, 20 Dec 2024 14:34:11 -0600 Subject: [PATCH 0832/1052] Bump version to 2024.12.2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 5b87cad8e1..10ad9454fd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.1" +__version__ = "2024.12.2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 45beea68eb5c8c5b663b979515e37a0cffedc65c Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Mon, 23 Dec 2024 06:49:04 +0100 Subject: [PATCH 0833/1052] [ble_client, bluetooth_proxy, esp32_ble_client, esp32_ble_tracker] fix ble proxy stop working (#7901) Co-authored-by: J. Nick Koston --- esphome/components/ble_client/ble_client.cpp | 3 +- .../bluetooth_proxy/bluetooth_connection.cpp | 5 ++ .../bluetooth_proxy/bluetooth_connection.h | 1 + .../esp32_ble_client/ble_client_base.cpp | 46 +++++++++++++++++++ .../esp32_ble_client/ble_client_base.h | 2 + .../esp32_ble_tracker/esp32_ble_tracker.cpp | 43 +++++++++++++---- .../esp32_ble_tracker/esp32_ble_tracker.h | 12 +++-- 7 files changed, 96 insertions(+), 16 deletions(-) diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 19cf2bc1f3..5cf096c9d4 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -25,8 +25,7 @@ void BLEClient::loop() { void BLEClient::dump_config() { ESP_LOGCONFIG(TAG, "BLE Client:"); - ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str()); - ESP_LOGCONFIG(TAG, " Auto-Connect: %s", TRUEFALSE(this->auto_connect_)); + BLEClientBase::dump_config(); } bool BLEClient::parse_device(const espbt::ESPBTDevice &device) { diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index 543752853e..b63f7ccde9 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -13,6 +13,11 @@ namespace bluetooth_proxy { static const char *const TAG = "bluetooth_proxy.connection"; +void BluetoothConnection::dump_config() { + ESP_LOGCONFIG(TAG, "BLE Connection:"); + BLEClientBase::dump_config(); +} + bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { if (!BLEClientBase::gattc_event_handler(event, gattc_if, param)) diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.h b/esphome/components/bluetooth_proxy/bluetooth_connection.h index e6ab3cbccc..fd83f8dd00 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.h +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.h @@ -11,6 +11,7 @@ class BluetoothProxy; class BluetoothConnection : public esp32_ble_client::BLEClientBase { public: + void dump_config() override; bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 98e7792792..53c430350c 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -44,6 +44,50 @@ 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_)); + std::string state_name; + switch (this->state()) { + case espbt::ClientState::INIT: + state_name = "INIT"; + break; + case espbt::ClientState::DISCONNECTING: + state_name = "DISCONNECTING"; + break; + case espbt::ClientState::IDLE: + state_name = "IDLE"; + break; + case espbt::ClientState::SEARCHING: + state_name = "SEARCHING"; + break; + case espbt::ClientState::DISCOVERED: + state_name = "DISCOVERED"; + break; + case espbt::ClientState::READY_TO_CONNECT: + state_name = "READY_TO_CONNECT"; + break; + case espbt::ClientState::CONNECTING: + state_name = "CONNECTING"; + break; + case espbt::ClientState::CONNECTED: + state_name = "CONNECTED"; + break; + case espbt::ClientState::ESTABLISHED: + state_name = "ESTABLISHED"; + break; + default: + state_name = "UNKNOWN_STATE"; + break; + } + ESP_LOGCONFIG(TAG, " State: %s", state_name.c_str()); + if (this->status_ == ESP_GATT_NO_RESOURCES) { + ESP_LOGE(TAG, " Failed due to no resources. Try to reduce number of BLE clients in config."); + } else if (this->status_ != ESP_GATT_OK) { + ESP_LOGW(TAG, " Failed due to error code %d", this->status_); + } +} + bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) { if (!this->auto_connect_) return false; @@ -129,6 +173,8 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } else { ESP_LOGE(TAG, "[%d] [%s] gattc app registration failed id=%d code=%d", this->connection_index_, this->address_str_.c_str(), param->reg.app_id, param->reg.status); + this->status_ = param->reg.status; + this->mark_failed(); } break; } diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index fca66c0b3c..84c35c4633 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -26,6 +26,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { void setup() override; void loop() override; float get_setup_priority() const override; + void dump_config() override; void run_later(std::function &&f); // NOLINT bool parse_device(const espbt::ESPBTDevice &device) override; @@ -103,6 +104,7 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { bool paired_{false}; espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; std::vector services_; + esp_gatt_status_t status_{ESP_GATT_OK}; void log_event_(const char *name); }; diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 6d051e3d4a..5fff9dbcad 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -58,7 +58,6 @@ void ESP32BLETracker::setup() { global_esp32_ble_tracker = this; this->scan_result_lock_ = xSemaphoreCreateMutex(); this->scan_end_lock_ = xSemaphoreCreateMutex(); - this->scanner_idle_ = true; #ifdef USE_OTA ota::get_global_ota_callback()->add_on_state_callback( @@ -107,6 +106,15 @@ void ESP32BLETracker::loop() { break; } } + if (connecting != connecting_ || discovered != discovered_ || searching != searching_ || + disconnecting != disconnecting_) { + connecting_ = connecting; + discovered_ = discovered; + searching_ = searching; + disconnecting_ = disconnecting; + ESP_LOGD(TAG, "connecting: %d, discovered: %d, searching: %d, disconnecting: %d", connecting_, discovered_, + searching_, disconnecting_); + } bool promote_to_connecting = discovered && !searching && !connecting; if (!this->scanner_idle_) { @@ -183,8 +191,9 @@ void ESP32BLETracker::loop() { } if (this->scan_start_failed_ || this->scan_set_param_failed_) { - if (this->scan_start_fail_count_ == 255) { - ESP_LOGE(TAG, "ESP-IDF BLE scan could not restart after 255 attempts, rebooting to restore BLE stack..."); + 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...", + std::numeric_limits::max()); App.reboot(); } if (xSemaphoreTake(this->scan_end_lock_, 0L)) { @@ -282,6 +291,12 @@ void ESP32BLETracker::start_scan_(bool first) { this->scan_params_.scan_interval = this->scan_interval_; this->scan_params_.scan_window = this->scan_window_; + // 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..."); + App.reboot(); + }); + esp_err_t err = esp_ble_gap_set_scan_params(&this->scan_params_); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gap_set_scan_params failed: %d", err); @@ -293,11 +308,6 @@ void ESP32BLETracker::start_scan_(bool first) { return; } this->scanner_idle_ = false; - - this->set_timeout("scan", this->scan_duration_ * 2000, []() { - ESP_LOGE(TAG, "ESP-IDF BLE scan never terminated, rebooting to restore BLE stack..."); - App.reboot(); - }); } void ESP32BLETracker::end_of_scan_() { @@ -371,6 +381,7 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga } void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) { + ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status); if (param.status == ESP_BT_STATUS_DONE) { this->scan_set_param_failed_ = ESP_BT_STATUS_SUCCESS; } else { @@ -379,20 +390,25 @@ void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t: } void ESP32BLETracker::gap_scan_start_complete_(const esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param ¶m) { + ESP_LOGV(TAG, "gap_scan_start_complete - status %d", param.status); this->scan_start_failed_ = param.status; if (param.status == ESP_BT_STATUS_SUCCESS) { this->scan_start_fail_count_ = 0; } else { - this->scan_start_fail_count_++; + if (this->scan_start_fail_count_ != std::numeric_limits::max()) { + this->scan_start_fail_count_++; + } xSemaphoreGive(this->scan_end_lock_); } } void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m) { + ESP_LOGV(TAG, "gap_scan_stop_complete - status %d", param.status); xSemaphoreGive(this->scan_end_lock_); } void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) { + ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt); if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) { if (xSemaphoreTake(this->scan_result_lock_, 0L)) { if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) { @@ -663,7 +679,14 @@ void ESP32BLETracker::dump_config() { 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", this->scan_continuous_ ? "True" : "False"); + ESP_LOGCONFIG(TAG, " Continuous Scanning: %s", YESNO(this->scan_continuous_)); + ESP_LOGCONFIG(TAG, " Scanner Idle: %s", YESNO(this->scanner_idle_)); + ESP_LOGCONFIG(TAG, " Scan End: %s", YESNO(xSemaphoreGetMutexHolder(this->scan_end_lock_) == nullptr)); + ESP_LOGCONFIG(TAG, " Connecting: %d, discovered: %d, searching: %d, disconnecting: %d", connecting_, discovered_, + searching_, disconnecting_); + if (this->scan_start_fail_count_) { + ESP_LOGCONFIG(TAG, " Scan Start Fail Count: %d", this->scan_start_fail_count_); + } } void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 2fc5da829d..52b091619e 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -178,7 +178,7 @@ class ESPBTClient : public ESPBTDeviceListener { int app_id; protected: - ClientState state_; + ClientState state_{ClientState::INIT}; }; class ESP32BLETracker : public Component, @@ -229,7 +229,7 @@ class ESP32BLETracker : public Component, /// Called when a `ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT` event is received. void gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param ¶m); - int app_id_; + int app_id_{0}; /// Vector of addresses that have already been printed in print_bt_device_info std::vector already_discovered_; @@ -242,10 +242,10 @@ class ESP32BLETracker : public Component, uint32_t scan_duration_; uint32_t scan_interval_; uint32_t scan_window_; - uint8_t scan_start_fail_count_; + uint8_t scan_start_fail_count_{0}; bool scan_continuous_; bool scan_active_; - bool scanner_idle_; + bool scanner_idle_{true}; bool ble_was_disabled_{true}; bool raw_advertisements_{false}; bool parse_advertisements_{false}; @@ -260,6 +260,10 @@ class ESP32BLETracker : public Component, esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_; esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS}; esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS}; + int connecting_{0}; + int discovered_{0}; + int searching_{0}; + int disconnecting_{0}; }; // NOLINTNEXTLINE From 387bde665e3ae5d4b8d5a68299abd6032c4291b8 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 24 Dec 2024 04:15:40 -0500 Subject: [PATCH 0834/1052] [esp32_rmt] IDF 5+ update fixes (#8002) Co-authored-by: Jonathan Swoboda Co-authored-by: Keith Burzinski --- .../remote_receiver/remote_receiver_esp32.cpp | 8 ++--- .../components/remote_transmitter/__init__.py | 9 +++++ .../remote_transmitter/remote_transmitter.h | 3 ++ .../remote_transmitter_esp32.cpp | 34 ++++++++++++++++--- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index 48b84d61d4..8a36971e36 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -154,16 +154,16 @@ void RemoteReceiverComponent::setup() { void RemoteReceiverComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Receiver:"); LOG_PIN(" Pin: ", this->pin_); - 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!"); - } #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_); #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_); diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index ea29751671..e3462fb246 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -7,6 +7,7 @@ from esphome.const import ( CONF_CLOCK_DIVIDER, CONF_CLOCK_RESOLUTION, CONF_ID, + CONF_INVERTED, CONF_PIN, CONF_RMT_CHANNEL, CONF_RMT_SYMBOLS, @@ -16,6 +17,7 @@ from esphome.core import CORE AUTO_LOAD = ["remote_base"] +CONF_EOT_LEVEL = "eot_level" CONF_ON_TRANSMIT = "on_transmit" CONF_ON_COMPLETE = "on_complete" CONF_ONE_WIRE = "one_wire" @@ -41,6 +43,7 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_CLOCK_DIVIDER): cv.All( 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_ONE_WIRE): cv.All(cv.only_with_esp_idf, cv.boolean), cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), cv.SplitDefault( @@ -73,6 +76,12 @@ async def to_code(config): cg.add(var.set_with_dma(config[CONF_USE_DMA])) if CONF_ONE_WIRE in config: cg.add(var.set_one_wire(config[CONF_ONE_WIRE])) + if CONF_EOT_LEVEL in config: + cg.add(var.set_eot_level(config[CONF_EOT_LEVEL])) + elif CONF_ONE_WIRE in config and config[CONF_ONE_WIRE]: + cg.add(var.set_eot_level(True)) + elif CONF_INVERTED in config[CONF_PIN] and config[CONF_PIN][CONF_INVERTED]: + cg.add(var.set_eot_level(True)) else: if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index 20b98d5488..fd1d182063 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -41,6 +41,8 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } void set_one_wire(bool one_wire) { this->one_wire_ = one_wire; } + void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; } + void digital_write(bool value); #endif Trigger<> *get_transmit_trigger() const { return this->transmit_trigger_; }; @@ -68,6 +70,7 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, std::vector rmt_temp_; bool with_dma_{false}; bool one_wire_{false}; + bool eot_level_{false}; rmt_channel_handle_t channel_{NULL}; rmt_encoder_handle_t encoder_{NULL}; #else diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index efb1735bfd..cd7f366373 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -11,6 +11,7 @@ static const char *const TAG = "remote_transmitter"; void RemoteTransmitterComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Remote Transmitter..."); + this->inverted_ = this->pin_->is_inverted(); this->configure_rmt_(); } @@ -37,6 +38,31 @@ void RemoteTransmitterComponent::dump_config() { } } +#if ESP_IDF_VERSION_MAJOR >= 5 +void RemoteTransmitterComponent::digital_write(bool value) { + rmt_symbol_word_t symbol = { + .duration0 = 1, + .level0 = value, + .duration1 = 0, + .level1 = value, + }; + rmt_transmit_config_t config; + memset(&config, 0, sizeof(config)); + config.loop_count = 0; + config.flags.eot_level = value; + esp_err_t error = rmt_transmit(this->channel_, this->encoder_, &symbol, sizeof(symbol), &config); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_transmit failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } + error = rmt_tx_wait_all_done(this->channel_, -1); + if (error != ESP_OK) { + ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error)); + this->status_set_warning(); + } +} +#endif + void RemoteTransmitterComponent::configure_rmt_() { #if ESP_IDF_VERSION_MAJOR >= 5 esp_err_t error; @@ -83,6 +109,7 @@ void RemoteTransmitterComponent::configure_rmt_() { this->mark_failed(); return; } + this->digital_write(this->one_wire_ || this->inverted_); this->initialized_ = true; } @@ -120,13 +147,12 @@ void RemoteTransmitterComponent::configure_rmt_() { } c.tx_config.idle_output_en = true; - if (!this->pin_->is_inverted()) { + if (!this->inverted_) { c.tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH; c.tx_config.idle_level = RMT_IDLE_LEVEL_LOW; } else { c.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW; c.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH; - this->inverted_ = true; } esp_err_t error = rmt_config(&c); @@ -210,7 +236,7 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen rmt_transmit_config_t config; memset(&config, 0, sizeof(config)); config.loop_count = 0; - config.flags.eot_level = this->inverted_; + config.flags.eot_level = this->eot_level_; esp_err_t error = rmt_transmit(this->channel_, this->encoder_, this->rmt_temp_.data(), this->rmt_temp_.size() * sizeof(rmt_symbol_word_t), &config); if (error != ESP_OK) { @@ -223,8 +249,6 @@ void RemoteTransmitterComponent::send_internal(uint32_t send_times, uint32_t sen if (error != ESP_OK) { ESP_LOGW(TAG, "rmt_tx_wait_all_done failed: %s", esp_err_to_name(error)); this->status_set_warning(); - } else { - this->status_clear_warning(); } if (i + 1 < send_times) delayMicroseconds(send_wait); From dc5b4087486c8d707c7b89379c7bf8bdd367c86a Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Sun, 5 Jan 2025 17:50:35 -0800 Subject: [PATCH 0835/1052] Initialize esp32_rmt_led_strip buffer (#8036) --- esphome/components/esp32_rmt_led_strip/led_strip.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/esp32_rmt_led_strip/led_strip.cpp b/esphome/components/esp32_rmt_led_strip/led_strip.cpp index 8ee890ec10..4e8c862c23 100644 --- a/esphome/components/esp32_rmt_led_strip/led_strip.cpp +++ b/esphome/components/esp32_rmt_led_strip/led_strip.cpp @@ -33,6 +33,7 @@ void ESP32RMTLEDStripLightOutput::setup() { this->mark_failed(); return; } + memset(this->buf_, 0, buffer_size); this->effect_data_ = allocator.allocate(this->num_leds_); if (this->effect_data_ == nullptr) { From a0615a92f0124b4ee863e8d0f95f39d49c0d2a6e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 9 Jan 2025 07:25:10 +1100 Subject: [PATCH 0836/1052] [addressable_light] Remove rmt channel from idf tests (#7987) --- tests/components/addressable_light/test.esp32-c3-idf.yaml | 1 - tests/components/addressable_light/test.esp32-idf.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/components/addressable_light/test.esp32-c3-idf.yaml b/tests/components/addressable_light/test.esp32-c3-idf.yaml index f587113fac..7b3516345d 100644 --- a/tests/components/addressable_light/test.esp32-c3-idf.yaml +++ b/tests/components/addressable_light/test.esp32-c3-idf.yaml @@ -6,7 +6,6 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 display: - platform: addressable_light diff --git a/tests/components/addressable_light/test.esp32-idf.yaml b/tests/components/addressable_light/test.esp32-idf.yaml index f587113fac..7b3516345d 100644 --- a/tests/components/addressable_light/test.esp32-idf.yaml +++ b/tests/components/addressable_light/test.esp32-idf.yaml @@ -6,7 +6,6 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 display: - platform: addressable_light From 5e72b7196bee468a75a3b8b9cb6312de0eb2f04e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 9 Jan 2025 08:14:08 +1100 Subject: [PATCH 0837/1052] Remove rmt channel from idf tests (#8054) --- tests/components/e131/test.esp32-c3-idf.yaml | 1 - tests/components/e131/test.esp32-idf.yaml | 1 - tests/components/partition/test.esp32-c3-idf.yaml | 1 - tests/components/partition/test.esp32-idf.yaml | 1 - 4 files changed, 4 deletions(-) diff --git a/tests/components/e131/test.esp32-c3-idf.yaml b/tests/components/e131/test.esp32-c3-idf.yaml index 25304cd3b4..a27e62c1fb 100644 --- a/tests/components/e131/test.esp32-c3-idf.yaml +++ b/tests/components/e131/test.esp32-c3-idf.yaml @@ -12,7 +12,6 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 effects: - e131: universe: 1 diff --git a/tests/components/e131/test.esp32-idf.yaml b/tests/components/e131/test.esp32-idf.yaml index 25304cd3b4..a27e62c1fb 100644 --- a/tests/components/e131/test.esp32-idf.yaml +++ b/tests/components/e131/test.esp32-idf.yaml @@ -12,7 +12,6 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 effects: - e131: universe: 1 diff --git a/tests/components/partition/test.esp32-c3-idf.yaml b/tests/components/partition/test.esp32-c3-idf.yaml index 77cfc5ad44..397e1b0642 100644 --- a/tests/components/partition/test.esp32-c3-idf.yaml +++ b/tests/components/partition/test.esp32-c3-idf.yaml @@ -6,7 +6,6 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 - platform: partition name: Partition Light segments: diff --git a/tests/components/partition/test.esp32-idf.yaml b/tests/components/partition/test.esp32-idf.yaml index 77cfc5ad44..397e1b0642 100644 --- a/tests/components/partition/test.esp32-idf.yaml +++ b/tests/components/partition/test.esp32-idf.yaml @@ -6,7 +6,6 @@ light: rgb_order: GRB num_leds: 256 pin: 2 - rmt_channel: 0 - platform: partition name: Partition Light segments: From 78543e1e159c7043ce51336924747e6067125e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samu=20N=C3=A9meth?= Date: Wed, 8 Jan 2025 23:37:52 +0100 Subject: [PATCH 0838/1052] Fixed comment typo in light_color_values.h (#8050) Co-authored-by: Keith Burzinski --- esphome/components/light/light_color_values.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/light/light_color_values.h b/esphome/components/light/light_color_values.h index bad180ce6d..ca32b9c571 100644 --- a/esphome/components/light/light_color_values.h +++ b/esphome/components/light/light_color_values.h @@ -36,7 +36,7 @@ inline static uint8_t to_uint8_scale(float x) { return static_cast(roun * range as set in the traits, so the output needs to do this. * * For COLD_WARM_WHITE capability: - * - cold_white, warm_white: The brightness of the cald and warm white channels of the light. + * - cold_white, warm_white: The brightness of the light's cold and warm white channels. * * All values (except color temperature) are represented using floats in the range 0.0 (off) to 1.0 (on), and are * automatically clamped to this range. Properties not used in the current color mode can still have (invalid) values From a498fb5dcfa3d5999f87817d2b728dbb4972aad9 Mon Sep 17 00:00:00 2001 From: Peter Zich Date: Wed, 8 Jan 2025 22:47:30 -0800 Subject: [PATCH 0839/1052] Fix braceless else statements (#7799) --- esphome/components/climate_ir/climate_ir.cpp | 3 +- esphome/components/coolix/coolix.cpp | 3 +- .../dfrobot_sen0395/dfrobot_sen0395.cpp | 3 +- esphome/components/dht/dht.cpp | 3 +- esphome/components/display/display.cpp | 3 +- esphome/components/display/rect.cpp | 3 +- .../esp32_improv/esp32_improv_component.cpp | 2 +- esphome/components/gcja5/gcja5.cpp | 3 +- esphome/components/haier/haier_base.cpp | 3 +- esphome/components/haier/hon_climate.cpp | 3 +- esphome/components/heatpumpir/heatpumpir.cpp | 3 +- .../microphone/i2s_audio_microphone.cpp | 12 ++++---- .../micronova/switch/micronova_switch.cpp | 6 ++-- esphome/components/toshiba/toshiba.cpp | 3 +- esphome/components/tuya/light/tuya_light.cpp | 6 ++-- esphome/components/yashima/yashima.cpp | 3 +- esphome/core/helpers.cpp | 28 ++++++++++--------- 17 files changed, 55 insertions(+), 35 deletions(-) diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index 76adfb42bb..8175383627 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -37,8 +37,9 @@ void ClimateIR::setup() { this->publish_state(); }); this->current_temperature = this->sensor_->state; - } else + } else { this->current_temperature = NAN; + } // restore set points auto restore = this->restore_state_(); if (restore.has_value()) { diff --git a/esphome/components/coolix/coolix.cpp b/esphome/components/coolix/coolix.cpp index 22b3431c3e..5c6bfd7740 100644 --- a/esphome/components/coolix/coolix.cpp +++ b/esphome/components/coolix/coolix.cpp @@ -131,8 +131,9 @@ bool CoolixClimate::on_coolix(climate::Climate *parent, remote_base::RemoteRecei } else { parent->mode = climate::CLIMATE_MODE_FAN_ONLY; } - } else + } else { parent->mode = climate::CLIMATE_MODE_COOL; + } // Fan Speed if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || parent->mode == climate::CLIMATE_MODE_HEAT_COOL || diff --git a/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp b/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp index f8ef6c7138..f47025698b 100644 --- a/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +++ b/esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp @@ -118,8 +118,9 @@ std::unique_ptr CircularCommandQueue::dequeue() { if (front_ == rear_) { front_ = -1; rear_ = -1; - } else + } else { front_ = (front_ + 1) % COMMAND_QUEUE_SIZE; + } return dequeued_cmd; } diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 3f9f9c57f4..5a18f6f36e 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -157,8 +157,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r if (bit == 0) { bit = 7; byte++; - } else + } else { bit--; + } } } if (!report_errors && error_code != 0) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index f00c2936a8..202c64ef14 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -266,8 +266,9 @@ void Display::filled_gauge(int center_x, int center_y, int radius1, int radius2, if (dymax < float(-dxmax) * tan_a) { upd_dxmax = ceil(float(dymax) / tan_a); hline_width = -dxmax - upd_dxmax + 1; - } else + } else { hline_width = 0; + } } if (hline_width > 0) this->horizontal_line(center_x + dxmax, center_y - dymax, hline_width, color); diff --git a/esphome/components/display/rect.cpp b/esphome/components/display/rect.cpp index 34b611191f..49bb7d025f 100644 --- a/esphome/components/display/rect.cpp +++ b/esphome/components/display/rect.cpp @@ -90,8 +90,9 @@ void Rect::info(const std::string &prefix) { if (this->is_set()) { ESP_LOGI(TAG, "%s [%3d,%3d,%3d,%3d] (%3d,%3d)", prefix.c_str(), this->x, this->y, this->w, this->h, this->x2(), this->y2()); - } else + } else { ESP_LOGI(TAG, "%s ** IS NOT SET **", prefix.c_str()); + } } } // namespace display diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index d36b50feb0..c67431077c 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -112,7 +112,7 @@ void ESP32ImprovComponent::loop() { this->set_state_(improv::STATE_AUTHORIZED); } else #else - this->set_state_(improv::STATE_AUTHORIZED); + { this->set_state_(improv::STATE_AUTHORIZED); } #endif { if (!this->check_identify_()) diff --git a/esphome/components/gcja5/gcja5.cpp b/esphome/components/gcja5/gcja5.cpp index 7f980ca0ad..b1db58654b 100644 --- a/esphome/components/gcja5/gcja5.cpp +++ b/esphome/components/gcja5/gcja5.cpp @@ -97,8 +97,9 @@ void GCJA5Component::parse_data_() { if (this->rx_message_[0] != 0x02 || this->rx_message_[31] != 0x03 || !this->calculate_checksum_()) { ESP_LOGVV(TAG, "Discarding bad packet - failed checks."); return; - } else + } else { ESP_LOGVV(TAG, "Good packet found."); + } this->have_good_data_ = true; uint8_t status = this->rx_message_[29]; diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index ba80c1ca1b..f8c0a7587e 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -342,8 +342,9 @@ bool HaierClimateBase::prepare_pending_action() { this->action_request_.reset(); return false; } - } else + } else { return false; + } } ClimateTraits HaierClimateBase::traits() { return traits_; } diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index c95a87223d..9b59dd0c10 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -710,8 +710,9 @@ void HonClimate::process_alarm_message_(const uint8_t *packet, uint8_t size, boo alarm_code++; } active_alarms_[i] = packet[2 + i]; - } else + } else { alarm_code += 8; + } } } else { float alarm_count = 0.0f; diff --git a/esphome/components/heatpumpir/heatpumpir.cpp b/esphome/components/heatpumpir/heatpumpir.cpp index 144dcc9bfa..55f0599cba 100644 --- a/esphome/components/heatpumpir/heatpumpir.cpp +++ b/esphome/components/heatpumpir/heatpumpir.cpp @@ -87,8 +87,9 @@ void HeatpumpIRClimate::setup() { this->publish_state(); }); this->current_temperature = this->sensor_->state; - } else + } else { this->current_temperature = NAN; + } } void HeatpumpIRClimate::transmit_state() { diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 23689afb91..4dbc9dcdac 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -25,11 +25,13 @@ void I2SAudioMicrophone::setup() { } } else #endif - if (this->pdm_) { - if (this->parent_->get_port() != I2S_NUM_0) { - ESP_LOGE(TAG, "PDM only works on I2S0!"); - this->mark_failed(); - return; + { + if (this->pdm_) { + if (this->parent_->get_port() != I2S_NUM_0) { + ESP_LOGE(TAG, "PDM only works on I2S0!"); + this->mark_failed(); + return; + } } } } diff --git a/esphome/components/micronova/switch/micronova_switch.cpp b/esphome/components/micronova/switch/micronova_switch.cpp index dcc96102db..28674acd96 100644 --- a/esphome/components/micronova/switch/micronova_switch.cpp +++ b/esphome/components/micronova/switch/micronova_switch.cpp @@ -11,15 +11,17 @@ void MicroNovaSwitch::write_state(bool state) { if (this->micronova_->get_current_stove_state() == 0) { this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_on_); this->publish_state(true); - } else + } else { ESP_LOGW(TAG, "Unable to turn stove on, invalid state: %d", micronova_->get_current_stove_state()); + } } else { // don't send power-off when status is Off or Final cleaning if (this->micronova_->get_current_stove_state() != 0 && micronova_->get_current_stove_state() != 6) { this->micronova_->write_address(this->memory_location_, this->memory_address_, this->memory_data_off_); this->publish_state(false); - } else + } else { ESP_LOGW(TAG, "Unable to turn stove off, invalid state: %d", micronova_->get_current_stove_state()); + } } this->micronova_->update(); break; diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index 33d36d6a69..ff4241a81f 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -106,8 +106,9 @@ void ToshibaClimate::setup() { this->publish_state(); }); this->current_temperature = this->sensor_->state; - } else + } else { this->current_temperature = NAN; + } // restore set points auto restore = this->restore_state_(); if (restore.has_value()) { diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index 66931767b2..815a089d9f 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -120,8 +120,9 @@ light::LightTraits TuyaLight::get_traits() { traits.set_supported_color_modes( {light::ColorMode::RGB_COLOR_TEMPERATURE, light::ColorMode::COLOR_TEMPERATURE}); } - } else + } else { traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE}); + } traits.set_min_mireds(this->cold_white_temperature_); traits.set_max_mireds(this->warm_white_temperature_); } else if (this->color_id_.has_value()) { @@ -131,8 +132,9 @@ light::LightTraits TuyaLight::get_traits() { } else { traits.set_supported_color_modes({light::ColorMode::RGB_WHITE}); } - } else + } else { traits.set_supported_color_modes({light::ColorMode::RGB}); + } } else if (this->dimmer_id_.has_value()) { traits.set_supported_color_modes({light::ColorMode::BRIGHTNESS}); } else { diff --git a/esphome/components/yashima/yashima.cpp b/esphome/components/yashima/yashima.cpp index 493c689b42..a3cf53ff66 100644 --- a/esphome/components/yashima/yashima.cpp +++ b/esphome/components/yashima/yashima.cpp @@ -104,8 +104,9 @@ void YashimaClimate::setup() { this->publish_state(); }); this->current_temperature = this->sensor_->state; - } else + } else { this->current_temperature = NAN; + } // restore set points auto restore = this->restore_state_(); if (restore.has_value()) { diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index b11615204e..2d2c88b844 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -126,19 +126,21 @@ uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse } } else #endif - if (reverse_poly == 0xa001) { - while (len--) { - uint8_t combo = crc ^ (uint8_t) *data++; - crc = (crc >> 8) ^ CRC16_A001_LE_LUT_L[combo & 0x0F] ^ CRC16_A001_LE_LUT_H[combo >> 4]; - } - } else { - while (len--) { - crc ^= *data++; - for (uint8_t i = 0; i < 8; i++) { - if (crc & 0x0001) { - crc = (crc >> 1) ^ reverse_poly; - } else { - crc >>= 1; + { + if (reverse_poly == 0xa001) { + while (len--) { + uint8_t combo = crc ^ (uint8_t) *data++; + crc = (crc >> 8) ^ CRC16_A001_LE_LUT_L[combo & 0x0F] ^ CRC16_A001_LE_LUT_H[combo >> 4]; + } + } else { + while (len--) { + crc ^= *data++; + for (uint8_t i = 0; i < 8; i++) { + if (crc & 0x0001) { + crc = (crc >> 1) ^ reverse_poly; + } else { + crc >>= 1; + } } } } From de603c756500a5fe0dac805d498c3cb870e1b04b Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Fri, 10 Jan 2025 22:10:19 +0100 Subject: [PATCH 0840/1052] Enable udp to work (on ipv4) when ipv6 is enabled (#8060) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/udp/udp_component.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index b8727ec423..e29620fa9a 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -245,13 +245,9 @@ void UDPComponent::setup() { } struct sockaddr_in server {}; - socklen_t sl = socket::set_sockaddr_any((struct sockaddr *) &server, sizeof(server), this->port_); - if (sl == 0) { - ESP_LOGE(TAG, "Socket unable to set sockaddr: errno %d", errno); - this->mark_failed(); - this->status_set_error("Unable to set sockaddr"); - return; - } + server.sin_family = AF_INET; + server.sin_addr.s_addr = ESPHOME_INADDR_ANY; + server.sin_port = htons(this->port_); err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server)); if (err != 0) { From 4d7c6b28e1260be7217960ae61b74bc1f903d1ae Mon Sep 17 00:00:00 2001 From: Juan Jose Restrepo <40721479+jotaj91@users.noreply.github.com> Date: Fri, 10 Jan 2025 18:22:30 -0500 Subject: [PATCH 0841/1052] Update sprinkler.cpp (#7996) --- esphome/components/sprinkler/sprinkler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/esphome/components/sprinkler/sprinkler.cpp b/esphome/components/sprinkler/sprinkler.cpp index 5384d29871..3cfb5ccdee 100644 --- a/esphome/components/sprinkler/sprinkler.cpp +++ b/esphome/components/sprinkler/sprinkler.cpp @@ -184,11 +184,13 @@ void SprinklerValveOperator::set_controller(Sprinkler *controller) { void SprinklerValveOperator::set_valve(SprinklerValve *valve) { if (valve != nullptr) { + if (this->state_ != IDLE) { // Only kill if not already idle + this->kill_(); // ensure everything is off before we let go! + } this->state_ = IDLE; // reset state this->run_duration_ = 0; // reset to ensure the valve isn't started without updating it this->start_millis_ = 0; // reset because (new) valve has not been started yet this->stop_millis_ = 0; // reset because (new) valve has not been started yet - this->kill_(); // ensure everything is off before we let go! this->valve_ = valve; // finally, set the pointer to the new valve } } From 4530e4d60fedc3557e2ad52980a1968d0a47ec79 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:40:50 +1100 Subject: [PATCH 0842/1052] [lvgl] remove default state (#8038) --- esphome/components/lvgl/defines.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 02323f9655..7587c336bb 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -215,7 +215,7 @@ LV_LONG_MODES = LvConstant( ) STATES = ( - "default", + # default state not included here "checked", "focused", "focus_key", From 8a98b69a576bf11dd919e990b91b80127db9e486 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:42:03 +1100 Subject: [PATCH 0843/1052] [lvgl] fix bg_image_src (#8005) Co-authored-by: clydeps --- esphome/components/lvgl/lvgl_esphome.h | 10 ++++++++++ esphome/components/lvgl/widgets/meter.py | 3 ++- esphome/core/defines.h | 1 + tests/components/lvgl/lvgl-package.yaml | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 56413ad77e..8e89b02db9 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -59,6 +59,16 @@ inline void lv_img_set_src(lv_obj_t *obj, esphome::image::Image *image) { inline void lv_disp_set_bg_image(lv_disp_t *disp, esphome::image::Image *image) { lv_disp_set_bg_image(disp, image->get_lv_img_dsc()); } + +inline void lv_obj_set_style_bg_img_src(lv_obj_t *obj, esphome::image::Image *image, lv_style_selector_t selector) { + lv_obj_set_style_bg_img_src(obj, image->get_lv_img_dsc(), selector); +} +#ifdef USE_LVGL_METER +inline lv_meter_indicator_t *lv_meter_add_needle_img(lv_obj_t *obj, lv_meter_scale_t *scale, esphome::image::Image *src, + lv_coord_t pivot_x, lv_coord_t pivot_y) { + return lv_meter_add_needle_img(obj, scale, src->get_lv_img_dsc(), pivot_x, pivot_y); +} +#endif // USE_LVGL_METER #endif // USE_LVGL_IMAGE #ifdef USE_LVGL_ANIMIMG inline void lv_animimg_set_src(lv_obj_t *img, std::vector images) { diff --git a/esphome/components/lvgl/widgets/meter.py b/esphome/components/lvgl/widgets/meter.py index cd61d1c775..29a382f7cf 100644 --- a/esphome/components/lvgl/widgets/meter.py +++ b/esphome/components/lvgl/widgets/meter.py @@ -27,7 +27,7 @@ from ..defines import ( CONF_START_VALUE, CONF_TICKS, ) -from ..helpers import add_lv_use +from ..helpers import add_lv_use, lvgl_components_required from ..lv_validation import ( angle, get_end_value, @@ -182,6 +182,7 @@ class MeterType(WidgetType): async def to_code(self, w: Widget, config): """For a meter object, create and set parameters""" + lvgl_components_required.add(CONF_METER) var = w.obj for scale_conf in config.get(CONF_SCALES, ()): rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2 diff --git a/esphome/core/defines.h b/esphome/core/defines.h index eb3b20d007..c38a26c6a8 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -49,6 +49,7 @@ #define USE_LVGL_IMAGE #define USE_LVGL_KEY_LISTENER #define USE_LVGL_KEYBOARD +#define USE_LVGL_METER #define USE_LVGL_ROLLER #define USE_LVGL_ROTARY_ENCODER #define USE_LVGL_TOUCHSCREEN diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index b1b89adfe0..68f91884b2 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -132,6 +132,7 @@ lvgl: pages: - id: page1 + bg_image_src: cat_image on_load: - logger.log: page loaded - lvgl.widget.focus: From 0df6a913b3df34c9d6d7229dd802bc22a75db808 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:46:17 +1100 Subject: [PATCH 0844/1052] [lgvl] disp_bg_image and disp_bg_opa changes (#8025) --- esphome/components/lvgl/automation.py | 16 +++++++++++++--- esphome/components/lvgl/defines.py | 1 + esphome/components/lvgl/schemas.py | 7 +++++-- tests/components/lvgl/lvgl-package.yaml | 3 ++- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index c26ae54892..a987cf4097 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -10,13 +10,14 @@ from esphome.cpp_types import nullptr from .defines import ( CONF_DISP_BG_COLOR, CONF_DISP_BG_IMAGE, + CONF_DISP_BG_OPA, CONF_EDITING, CONF_FREEZE, CONF_LVGL_ID, CONF_SHOW_SNOW, literal, ) -from .lv_validation import lv_bool, lv_color, lv_image +from .lv_validation import lv_bool, lv_color, lv_image, opacity from .lvcode import ( LVGL_COMP_ARG, UPDATE_EVENT, @@ -119,13 +120,22 @@ async def lvgl_is_idle(config, condition_id, template_arg, args): async def disp_update(disp, config: dict): - if CONF_DISP_BG_COLOR not in config and CONF_DISP_BG_IMAGE not in config: + if ( + CONF_DISP_BG_COLOR not in config + and CONF_DISP_BG_IMAGE not in config + and CONF_DISP_BG_OPA not in config + ): return with LocalVariable("lv_disp_tmp", lv_disp_t, disp) as disp_temp: if (bg_color := config.get(CONF_DISP_BG_COLOR)) is not None: lv.disp_set_bg_color(disp_temp, await lv_color.process(bg_color)) if bg_image := config.get(CONF_DISP_BG_IMAGE): - lv.disp_set_bg_image(disp_temp, await lv_image.process(bg_image)) + if bg_image == "none": + lv.disp_set_bg_image(disp_temp, static_cast("void *", "nullptr")) + else: + lv.disp_set_bg_image(disp_temp, await lv_image.process(bg_image)) + if (bg_opa := config.get(CONF_DISP_BG_OPA)) is not None: + lv.disp_set_bg_opa(disp_temp, await opacity.process(bg_opa)) @automation.register_action( diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 7587c336bb..733a6bc180 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -403,6 +403,7 @@ CONF_COLUMN = "column" CONF_DIGITS = "digits" CONF_DISP_BG_COLOR = "disp_bg_color" CONF_DISP_BG_IMAGE = "disp_bg_image" +CONF_DISP_BG_OPA = "disp_bg_opa" CONF_BODY = "body" CONF_BUTTONS = "buttons" CONF_BYTE_ORDER = "byte_order" diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 3f56b3345f..271dbea19f 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -19,7 +19,7 @@ from esphome.schema_extractors import SCHEMA_EXTRACT from . import defines as df, lv_validation as lvalid 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 +from .lv_validation import lv_color, lv_font, lv_gradient, lv_image, opacity from .lvcode import LvglComponent, lv_event_t_ptr from .types import ( LVEncoderListener, @@ -344,8 +344,11 @@ FLEX_OBJ_SCHEMA = { DISP_BG_SCHEMA = cv.Schema( { - cv.Optional(df.CONF_DISP_BG_IMAGE): lv_image, + cv.Optional(df.CONF_DISP_BG_IMAGE): cv.Any( + cv.one_of("none", lower=True), lv_image + ), cv.Optional(df.CONF_DISP_BG_COLOR): lv_color, + cv.Optional(df.CONF_DISP_BG_OPA): opacity, } ) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 68f91884b2..512045d748 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -27,6 +27,7 @@ lvgl: bg_color: light_blue disp_bg_color: color_id disp_bg_image: cat_image + disp_bg_opa: cover theme: obj: border_width: 1 @@ -207,7 +208,7 @@ lvgl: - lvgl.animimg.stop: anim_img - lvgl.update: disp_bg_color: 0xffff00 - disp_bg_image: cat_image + disp_bg_image: none - lvgl.widget.show: message_box - label: text: "Hello shiny day" From f1712cffa8c15037d5f1b23c862c904fac6d7318 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:49:05 +1100 Subject: [PATCH 0845/1052] [spi_led_strip] Fix priority (#8021) --- esphome/components/spi_led_strip/light.py | 8 +-- .../spi_led_strip/spi_led_strip.cpp | 67 ++++++++++++++++++ .../components/spi_led_strip/spi_led_strip.h | 68 +++---------------- 3 files changed, 78 insertions(+), 65 deletions(-) create mode 100644 esphome/components/spi_led_strip/spi_led_strip.cpp diff --git a/esphome/components/spi_led_strip/light.py b/esphome/components/spi_led_strip/light.py index 78642935de..ca320265a9 100644 --- a/esphome/components/spi_led_strip/light.py +++ b/esphome/components/spi_led_strip/light.py @@ -1,8 +1,7 @@ import esphome.codegen as cg +from esphome.components import light, spi import esphome.config_validation as cv -from esphome.components import light -from esphome.components import spi -from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS +from esphome.const import CONF_NUM_LEDS, CONF_OUTPUT_ID spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip") SpiLedStrip = spi_led_strip_ns.class_( @@ -18,8 +17,7 @@ CONFIG_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( async def to_code(config): - var = cg.new_Pvariable(config[CONF_OUTPUT_ID]) - cg.add(var.set_num_leds(config[CONF_NUM_LEDS])) + var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_NUM_LEDS]) await light.register_light(var, config) await spi.register_spi_device(var, config) await cg.register_component(var, config) diff --git a/esphome/components/spi_led_strip/spi_led_strip.cpp b/esphome/components/spi_led_strip/spi_led_strip.cpp new file mode 100644 index 0000000000..46243c0686 --- /dev/null +++ b/esphome/components/spi_led_strip/spi_led_strip.cpp @@ -0,0 +1,67 @@ +#include "spi_led_strip.h" + +namespace esphome { +namespace spi_led_strip { + +SpiLedStrip::SpiLedStrip(uint16_t num_leds) { + this->num_leds_ = num_leds; + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->buffer_size_ = num_leds * 4 + 8; + this->buf_ = allocator.allocate(this->buffer_size_); + if (this->buf_ == nullptr) { + ESP_LOGE(TAG, "Failed to allocate buffer of size %u", this->buffer_size_); + return; + } + + this->effect_data_ = allocator.allocate(num_leds); + if (this->effect_data_ == nullptr) { + ESP_LOGE(TAG, "Failed to allocate effect data of size %u", num_leds); + return; + } + memset(this->buf_, 0xFF, this->buffer_size_); + memset(this->buf_, 0, 4); +} +void SpiLedStrip::setup() { + if (this->effect_data_ == nullptr || this->buf_ == nullptr) { + this->mark_failed(); + return; + } + this->spi_setup(); +} +light::LightTraits SpiLedStrip::get_traits() { + auto traits = light::LightTraits(); + traits.set_supported_color_modes({light::ColorMode::RGB}); + return traits; +} +void SpiLedStrip::dump_config() { + esph_log_config(TAG, "SPI LED Strip:"); + esph_log_config(TAG, " LEDs: %d", this->num_leds_); + if (this->data_rate_ >= spi::DATA_RATE_1MHZ) { + esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000)); + } else { + esph_log_config(TAG, " Data rate: %ukHz", (unsigned) (this->data_rate_ / 1000)); + } +} +void SpiLedStrip::write_state(light::LightState *state) { + if (this->is_failed()) + return; + if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE) { + char strbuf[49]; + size_t len = std::min(this->buffer_size_, (size_t) (sizeof(strbuf) - 1) / 3); + memset(strbuf, 0, sizeof(strbuf)); + for (size_t i = 0; i != len; i++) { + sprintf(strbuf + i * 3, "%02X ", this->buf_[i]); + } + esph_log_v(TAG, "write_state: buf = %s", strbuf); + } + this->enable(); + this->write_array(this->buf_, this->buffer_size_); + this->disable(); +} +light::ESPColorView SpiLedStrip::get_view_internal(int32_t index) const { + size_t pos = index * 4 + 5; + return {this->buf_ + pos + 2, this->buf_ + pos + 1, this->buf_ + pos + 0, nullptr, + this->effect_data_ + index, &this->correction_}; +} +} // namespace spi_led_strip +} // namespace esphome diff --git a/esphome/components/spi_led_strip/spi_led_strip.h b/esphome/components/spi_led_strip/spi_led_strip.h index 1b317cdd69..14c5627ac3 100644 --- a/esphome/components/spi_led_strip/spi_led_strip.h +++ b/esphome/components/spi_led_strip/spi_led_strip.h @@ -13,74 +13,22 @@ class SpiLedStrip : public light::AddressableLight, public spi::SPIDevice { public: - void setup() override { this->spi_setup(); } + SpiLedStrip(uint16_t num_leds); + void setup() override; + float get_setup_priority() const override { return setup_priority::IO; } int32_t size() const override { return this->num_leds_; } - light::LightTraits get_traits() override { - auto traits = light::LightTraits(); - traits.set_supported_color_modes({light::ColorMode::RGB}); - return traits; - } - void set_num_leds(uint16_t num_leds) { - this->num_leds_ = num_leds; - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - this->buffer_size_ = num_leds * 4 + 8; - this->buf_ = allocator.allocate(this->buffer_size_); - if (this->buf_ == nullptr) { - esph_log_e(TAG, "Failed to allocate buffer of size %u", this->buffer_size_); - this->mark_failed(); - return; - } + light::LightTraits get_traits() override; - this->effect_data_ = allocator.allocate(num_leds); - if (this->effect_data_ == nullptr) { - esph_log_e(TAG, "Failed to allocate effect data of size %u", num_leds); - this->mark_failed(); - return; - } - memset(this->buf_, 0xFF, this->buffer_size_); - memset(this->buf_, 0, 4); - } + void dump_config() override; - void dump_config() override { - esph_log_config(TAG, "SPI LED Strip:"); - esph_log_config(TAG, " LEDs: %d", this->num_leds_); - if (this->data_rate_ >= spi::DATA_RATE_1MHZ) { - esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000)); - } else { - esph_log_config(TAG, " Data rate: %ukHz", (unsigned) (this->data_rate_ / 1000)); - } - } + void write_state(light::LightState *state) override; - void write_state(light::LightState *state) override { - if (this->is_failed()) - return; - if (ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE) { - char strbuf[49]; - size_t len = std::min(this->buffer_size_, (size_t) (sizeof(strbuf) - 1) / 3); - memset(strbuf, 0, sizeof(strbuf)); - for (size_t i = 0; i != len; i++) { - sprintf(strbuf + i * 3, "%02X ", this->buf_[i]); - } - esph_log_v(TAG, "write_state: buf = %s", strbuf); - } - this->enable(); - this->write_array(this->buf_, this->buffer_size_); - this->disable(); - } - - void clear_effect_data() override { - for (int i = 0; i < this->size(); i++) - this->effect_data_[i] = 0; - } + void clear_effect_data() override { memset(this->effect_data_, 0, this->num_leds_ * sizeof(this->effect_data_[0])); } protected: - light::ESPColorView get_view_internal(int32_t index) const override { - size_t pos = index * 4 + 5; - return {this->buf_ + pos + 2, this->buf_ + pos + 1, this->buf_ + pos + 0, nullptr, - this->effect_data_ + index, &this->correction_}; - } + light::ESPColorView get_view_internal(int32_t index) const override; size_t buffer_size_{}; uint8_t *effect_data_{nullptr}; From bd17ee8e3304b311bfac699ce49e6f16bc91fa1e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:50:13 +1100 Subject: [PATCH 0846/1052] [config] Early check for required version (#8000) --- esphome/config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/config.py b/esphome/config.py index 7d48569d2d..65e9ac29bc 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -18,6 +18,7 @@ from esphome.const import ( CONF_ESPHOME, CONF_EXTERNAL_COMPONENTS, CONF_ID, + CONF_MIN_VERSION, CONF_PACKAGES, CONF_PLATFORM, CONF_SUBSTITUTIONS, @@ -839,6 +840,10 @@ def validate_config( # Remove temporary esphome config path again, it will be reloaded later result.remove_output_path([CONF_ESPHOME], CONF_ESPHOME) + # Check version number now to avoid loading components that are not supported + if min_version := config[CONF_ESPHOME].get(CONF_MIN_VERSION): + cv.All(cv.version_number, cv.validate_esphome_version)(min_version) + # First run platform validation steps for key in TARGET_PLATFORMS: if key in config: From 109d737d5d7fbe8d55c2fb3c74179c43e9f8f92a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:53:26 +1100 Subject: [PATCH 0847/1052] [lvgl] Implement `lvgl.page.is_showing:` condition (#8055) --- esphome/components/lvgl/automation.py | 15 +++++++-- esphome/components/lvgl/lvgl_esphome.cpp | 3 ++ esphome/components/lvgl/lvgl_esphome.h | 15 ++++++--- esphome/components/lvgl/widgets/page.py | 39 ++++++++++++++++++++++-- tests/components/lvgl/lvgl-package.yaml | 5 +++ 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index a987cf4097..7db6e1f045 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -4,7 +4,7 @@ from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import CONF_ACTION, CONF_GROUP, CONF_ID, CONF_TIMEOUT -from esphome.cpp_generator import get_variable +from esphome.cpp_generator import TemplateArguments, get_variable from esphome.cpp_types import nullptr from .defines import ( @@ -23,6 +23,7 @@ from .lvcode import ( UPDATE_EVENT, LambdaContext, LocalVariable, + LvglComponent, ReturnStatement, add_line_marks, lv, @@ -93,7 +94,11 @@ async def lvgl_is_paused(config, condition_id, template_arg, args): lvgl = config[CONF_LVGL_ID] async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context: lv_add(ReturnStatement(lvgl_comp.is_paused())) - var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda()) + var = cg.new_Pvariable( + condition_id, + TemplateArguments(LvglComponent, *template_arg), + await context.get_lambda(), + ) await cg.register_parented(var, lvgl) return var @@ -114,7 +119,11 @@ async def lvgl_is_idle(config, condition_id, template_arg, args): timeout = await cg.templatable(config[CONF_TIMEOUT], [], cg.uint32) async with LambdaContext(LVGL_COMP_ARG, return_type=cg.bool_) as context: lv_add(ReturnStatement(lvgl_comp.is_idle(timeout))) - var = cg.new_Pvariable(condition_id, template_arg, await context.get_lambda()) + var = cg.new_Pvariable( + condition_id, + TemplateArguments(LvglComponent, *template_arg), + await context.get_lambda(), + ) await cg.register_parented(var, lvgl) return var diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 61bdfe9755..5abeead9d8 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -119,6 +119,7 @@ void LvglComponent::add_event_cb(lv_obj_t *obj, event_callback_t callback, lv_ev } void LvglComponent::add_page(LvPageType *page) { this->pages_.push_back(page); + page->set_parent(this); page->setup(this->pages_.size() - 1); } void LvglComponent::show_page(size_t index, lv_scr_load_anim_t anim, uint32_t time) { @@ -143,6 +144,8 @@ void LvglComponent::show_prev_page(lv_scr_load_anim_t anim, uint32_t time) { } while (this->pages_[this->current_page_]->skip); // skip empty pages() this->show_page(this->current_page_, anim, time); } +size_t LvglComponent::get_current_page() const { return this->current_page_; } +bool LvPageType::is_showing() const { return this->parent_->get_current_page() == this->index; } void LvglComponent::draw_buffer_(const lv_area_t *area, lv_color_t *ptr) { auto width = lv_area_get_width(area); auto height = lv_area_get_height(area); diff --git a/esphome/components/lvgl/lvgl_esphome.h b/esphome/components/lvgl/lvgl_esphome.h index 8e89b02db9..69fa808d53 100644 --- a/esphome/components/lvgl/lvgl_esphome.h +++ b/esphome/components/lvgl/lvgl_esphome.h @@ -94,7 +94,9 @@ class LvCompound { lv_obj_t *obj{}; }; -class LvPageType { +class LvglComponent; + +class LvPageType : public Parented { public: LvPageType(bool skip) : skip(skip) {} @@ -102,6 +104,9 @@ class LvPageType { this->index = index; this->obj = lv_obj_create(nullptr); } + + bool is_showing() const; + lv_obj_t *obj{}; size_t index{}; bool skip; @@ -188,6 +193,7 @@ class LvglComponent : public PollingComponent { void show_next_page(lv_scr_load_anim_t anim, uint32_t time); void show_prev_page(lv_scr_load_anim_t anim, uint32_t time); void set_page_wrap(bool wrap) { this->page_wrap_ = wrap; } + size_t get_current_page() const; void set_focus_mark(lv_group_t *group) { this->focus_marks_[group] = lv_group_get_focused(group); } void restore_focus_mark(lv_group_t *group) { auto *mark = this->focus_marks_[group]; @@ -251,14 +257,13 @@ template class LvglAction : public Action, public Parente std::function action_{}; }; -template class LvglCondition : public Condition, public Parented { +template class LvglCondition : public Condition, public Parented { public: - LvglCondition(std::function &&condition_lambda) - : condition_lambda_(std::move(condition_lambda)) {} + LvglCondition(std::function &&condition_lambda) : condition_lambda_(std::move(condition_lambda)) {} bool check(Ts... x) override { return this->condition_lambda_(this->parent_); } protected: - std::function condition_lambda_{}; + std::function condition_lambda_{}; }; #ifdef USE_LVGL_TOUCHSCREEN diff --git a/esphome/components/lvgl/widgets/page.py b/esphome/components/lvgl/widgets/page.py index a754a9cb9a..23c162e010 100644 --- a/esphome/components/lvgl/widgets/page.py +++ b/esphome/components/lvgl/widgets/page.py @@ -2,6 +2,7 @@ from esphome import automation, codegen as cg from esphome.automation import Trigger import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_PAGES, CONF_TIME, CONF_TRIGGER_ID +from esphome.cpp_generator import MockObj, TemplateArguments from ..defines import ( CONF_ANIMATION, @@ -17,18 +18,28 @@ from ..lvcode import ( EVENT_ARG, LVGL_COMP_ARG, LambdaContext, + ReturnStatement, add_line_marks, lv_add, lvgl_comp, lvgl_static, ) from ..schemas import LVGL_SCHEMA -from ..types import LvglAction, lv_page_t -from . import Widget, WidgetType, add_widgets, get_widgets, set_obj_properties +from ..types import LvglAction, LvglCondition, lv_page_t +from . import ( + Widget, + WidgetType, + add_widgets, + get_widgets, + set_obj_properties, + wait_for_widgets, +) CONF_ON_LOAD = "on_load" CONF_ON_UNLOAD = "on_unload" +PAGE_ARG = "_page" + PAGE_SCHEMA = cv.Schema( { cv.Optional(CONF_SKIP, default=False): lv_bool, @@ -86,6 +97,30 @@ async def page_next_to_code(config, action_id, template_arg, args): return var +@automation.register_condition( + "lvgl.page.is_showing", + LvglCondition, + cv.maybe_simple_value( + cv.Schema({cv.Required(CONF_ID): cv.use_id(lv_page_t)}), + key=CONF_ID, + ), +) +async def page_is_showing_to_code(config, condition_id, template_arg, args): + await wait_for_widgets() + page = await cg.get_variable(config[CONF_ID]) + async with LambdaContext( + [(lv_page_t.operator("ptr"), PAGE_ARG)], return_type=cg.bool_ + ) as context: + lv_add(ReturnStatement(MockObj(PAGE_ARG, "->").is_showing())) + var = cg.new_Pvariable( + condition_id, + TemplateArguments(lv_page_t, *template_arg), + await context.get_lambda(), + ) + await cg.register_parented(var, page) + return var + + @automation.register_action( "lvgl.page.previous", LvglAction, diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 512045d748..234fd78678 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -138,6 +138,11 @@ lvgl: - logger.log: page loaded - lvgl.widget.focus: action: restore + - if: + condition: + lvgl.page.is_showing: page1 + then: + logger.log: "Yes, page1 showing" on_unload: - logger.log: page unloaded - lvgl.widget.focus: mark From fe80750743e2eeddd1a5631aed30b81c2f252d62 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:56:54 +1100 Subject: [PATCH 0848/1052] [display] auto_clear_enabled defaults (#7986) --- esphome/components/display/__init__.py | 23 ++++++++++++++++++----- esphome/components/lvgl/__init__.py | 6 +++--- tests/components/lvgl/test.host.yaml | 2 -- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index 32a8b3b090..99224df7b3 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -39,6 +39,7 @@ DisplayOnPageChangeTrigger = display_ns.class_( CONF_ON_PAGE_CHANGE = "on_page_change" CONF_SHOW_TEST_CARD = "show_test_card" +CONF_UNSPECIFIED = "unspecified" DISPLAY_ROTATIONS = { 0: display_ns.DISPLAY_ROTATION_0_DEGREES, @@ -55,16 +56,22 @@ def validate_rotation(value): return cv.enum(DISPLAY_ROTATIONS, int=True)(value) +def validate_auto_clear(value): + if value == CONF_UNSPECIFIED: + return value + return cv.boolean(value) + + BASIC_DISPLAY_SCHEMA = cv.Schema( { - cv.Optional(CONF_LAMBDA): cv.lambda_, + cv.Exclusive(CONF_LAMBDA, CONF_LAMBDA): cv.lambda_, } ).extend(cv.polling_component_schema("1s")) FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend( { cv.Optional(CONF_ROTATION): validate_rotation, - cv.Optional(CONF_PAGES): cv.All( + cv.Exclusive(CONF_PAGES, CONF_LAMBDA): cv.All( cv.ensure_list( { cv.GenerateID(): cv.declare_id(DisplayPage), @@ -82,7 +89,9 @@ FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend( cv.Optional(CONF_TO): cv.use_id(DisplayPage), } ), - cv.Optional(CONF_AUTO_CLEAR_ENABLED, default=True): cv.boolean, + cv.Optional( + CONF_AUTO_CLEAR_ENABLED, default=CONF_UNSPECIFIED + ): validate_auto_clear, cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean, } ) @@ -92,8 +101,12 @@ async def setup_display_core_(var, config): if CONF_ROTATION in config: cg.add(var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]])) - if CONF_AUTO_CLEAR_ENABLED in config: - cg.add(var.set_auto_clear(config[CONF_AUTO_CLEAR_ENABLED])) + if auto_clear := config.get(CONF_AUTO_CLEAR_ENABLED): + # Default to true if pages or lambda is specified. Ideally this would be done during validation, but + # the possible schemas are too complex to do this easily. + if auto_clear == CONF_UNSPECIFIED: + auto_clear = CONF_LAMBDA in config or CONF_PAGES in config + cg.add(var.set_auto_clear(auto_clear)) if CONF_PAGES in config: pages = [] diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index b858e8df01..c64ffcb5f2 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -197,11 +197,11 @@ def final_validation(configs): for display_id in config[df.CONF_DISPLAYS]: path = global_config.get_path_for_id(display_id)[:-1] display = global_config.get_config_for_path(path) - if CONF_LAMBDA in display: + if CONF_LAMBDA in display or CONF_PAGES in display: raise cv.Invalid( - "Using lambda: in display config not compatible with LVGL" + "Using lambda: or pages: in display config is not compatible with LVGL" ) - if display[CONF_AUTO_CLEAR_ENABLED]: + if display.get(CONF_AUTO_CLEAR_ENABLED) is True: raise cv.Invalid( "Using auto_clear_enabled: true in display config not compatible with LVGL" ) diff --git a/tests/components/lvgl/test.host.yaml b/tests/components/lvgl/test.host.yaml index 34918cb113..39d9a0ebf3 100644 --- a/tests/components/lvgl/test.host.yaml +++ b/tests/components/lvgl/test.host.yaml @@ -7,7 +7,6 @@ display: height: 320 - platform: sdl id: sdl1 - auto_clear_enabled: false dimensions: width: 480 height: 480 @@ -40,4 +39,3 @@ lvgl: text: Click ME on_click: logger.log: Clicked - From d69926485c6ed33b476aef8994b41ac50b31ece9 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Sun, 12 Jan 2025 20:12:38 +0100 Subject: [PATCH 0849/1052] Convert IPAddress to use Pythonmodule ipaddress (#8072) --- esphome/components/ethernet/__init__.py | 20 ++++++------- esphome/components/udp/__init__.py | 2 +- esphome/components/wifi/__init__.py | 12 ++++---- esphome/components/wireguard/__init__.py | 4 +-- esphome/config_validation.py | 33 +++++++++++----------- esphome/core/__init__.py | 10 ------- esphome/yaml_util.py | 4 +-- tests/unit_tests/test_config_validation.py | 30 +++++++++++++++++--- tests/unit_tests/test_core.py | 25 ++-------------- 9 files changed, 65 insertions(+), 75 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index dca37b8dc2..ab760a9b6c 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -94,11 +94,11 @@ CLK_MODES = { MANUAL_IP_SCHEMA = cv.Schema( { - cv.Required(CONF_STATIC_IP): cv.ipv4, - cv.Required(CONF_GATEWAY): cv.ipv4, - cv.Required(CONF_SUBNET): cv.ipv4, - cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, - cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4, + cv.Required(CONF_STATIC_IP): cv.ipv4address, + cv.Required(CONF_GATEWAY): cv.ipv4address, + cv.Required(CONF_SUBNET): cv.ipv4address, + cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4address, + cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4address, } ) @@ -255,11 +255,11 @@ FINAL_VALIDATE_SCHEMA = _final_validate def manual_ip(config): return cg.StructInitializer( ManualIP, - ("static_ip", IPAddress(*config[CONF_STATIC_IP].args)), - ("gateway", IPAddress(*config[CONF_GATEWAY].args)), - ("subnet", IPAddress(*config[CONF_SUBNET].args)), - ("dns1", IPAddress(*config[CONF_DNS1].args)), - ("dns2", IPAddress(*config[CONF_DNS2].args)), + ("static_ip", IPAddress(str(config[CONF_STATIC_IP]))), + ("gateway", IPAddress(str(config[CONF_GATEWAY]))), + ("subnet", IPAddress(str(config[CONF_SUBNET]))), + ("dns1", IPAddress(str(config[CONF_DNS1]))), + ("dns2", IPAddress(str(config[CONF_DNS2]))), ) diff --git a/esphome/components/udp/__init__.py b/esphome/components/udp/__init__.py index ca15be2a80..e189975ade 100644 --- a/esphome/components/udp/__init__.py +++ b/esphome/components/udp/__init__.py @@ -85,7 +85,7 @@ CONFIG_SCHEMA = cv.All( cv.GenerateID(): cv.declare_id(UDPComponent), cv.Optional(CONF_PORT, default=18511): cv.port, cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list( - cv.ipv4 + cv.ipv4address, ), cv.Optional(CONF_ROLLING_CODE_ENABLE, default=False): cv.boolean, cv.Optional(CONF_PING_PONG_ENABLE, default=False): cv.boolean, diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index ad1a4f5262..582b826de0 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -93,16 +93,16 @@ def validate_channel(value): AP_MANUAL_IP_SCHEMA = cv.Schema( { - cv.Required(CONF_STATIC_IP): cv.ipv4, - cv.Required(CONF_GATEWAY): cv.ipv4, - cv.Required(CONF_SUBNET): cv.ipv4, + cv.Required(CONF_STATIC_IP): cv.ipv4address, + cv.Required(CONF_GATEWAY): cv.ipv4address, + cv.Required(CONF_SUBNET): cv.ipv4address, } ) STA_MANUAL_IP_SCHEMA = AP_MANUAL_IP_SCHEMA.extend( { - cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, - cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4, + cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4address, + cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4address, } ) @@ -364,7 +364,7 @@ def eap_auth(config): def safe_ip(ip): if ip is None: return IPAddress(0, 0, 0, 0) - return IPAddress(*ip.args) + return IPAddress(str(ip)) def manual_ip(config): diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index 5e34a8a19b..fc0e4e0538 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -67,8 +67,8 @@ CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(Wireguard), cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), - cv.Required(CONF_ADDRESS): cv.ipv4, - cv.Optional(CONF_NETMASK, default="255.255.255.255"): cv.ipv4, + cv.Required(CONF_ADDRESS): cv.ipv4address, + cv.Optional(CONF_NETMASK, default="255.255.255.255"): cv.ipv4address, cv.Required(CONF_PRIVATE_KEY): _wireguard_key, cv.Required(CONF_PEER_ENDPOINT): cv.string, cv.Required(CONF_PEER_PUBLIC_KEY): _wireguard_key, diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 38fd677a2a..20a0774ccb 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -3,6 +3,7 @@ from contextlib import contextmanager from dataclasses import dataclass from datetime import datetime +from ipaddress import AddressValueError, IPv4Address, ip_address import logging import os import re @@ -67,7 +68,6 @@ from esphome.const import ( from esphome.core import ( CORE, HexInt, - IPAddress, Lambda, TimePeriod, TimePeriodMicroseconds, @@ -1130,7 +1130,7 @@ def domain(value): if re.match(vol.DOMAIN_REGEX, value) is not None: return value try: - return str(ipv4(value)) + return str(ipaddress(value)) except Invalid as err: raise Invalid(f"Invalid domain: {value}") from err @@ -1160,21 +1160,20 @@ def ssid(value): return value -def ipv4(value): - if isinstance(value, list): - parts = value - elif isinstance(value, str): - parts = value.split(".") - elif isinstance(value, IPAddress): - return value - else: - raise Invalid("IPv4 address must consist of either string or integer list") - if len(parts) != 4: - raise Invalid("IPv4 address must consist of four point-separated integers") - parts_ = list(map(int, parts)) - if not all(0 <= x < 256 for x in parts_): - raise Invalid("IPv4 address parts must be in range from 0 to 255") - return IPAddress(*parts_) +def ipv4address(value): + try: + address = IPv4Address(value) + except AddressValueError as exc: + raise Invalid(f"{value} is not a valid IPv4 address") from exc + return address + + +def ipaddress(value): + try: + address = ip_address(value) + except ValueError as exc: + raise Invalid(f"{value} is not a valid IP address") from exc + return address def _valid_topic(value): diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index a97c3b18c9..f26c3da483 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -54,16 +54,6 @@ class HexInt(int): return f"{sign}0x{value:X}" -class IPAddress: - def __init__(self, *args): - if len(args) != 4: - raise ValueError("IPAddress must consist of 4 items") - self.args = args - - def __str__(self): - return ".".join(str(x) for x in self.args) - - class MACAddress: def __init__(self, *parts): if len(parts) != 6: diff --git a/esphome/yaml_util.py b/esphome/yaml_util.py index d67511dfec..b27ce4c3e3 100644 --- a/esphome/yaml_util.py +++ b/esphome/yaml_util.py @@ -4,6 +4,7 @@ import fnmatch import functools import inspect from io import TextIOWrapper +from ipaddress import _BaseAddress import logging import math import os @@ -25,7 +26,6 @@ from esphome.core import ( CORE, DocumentRange, EsphomeError, - IPAddress, Lambda, MACAddress, TimePeriod, @@ -576,7 +576,7 @@ ESPHomeDumper.add_multi_representer(bool, ESPHomeDumper.represent_bool) ESPHomeDumper.add_multi_representer(str, ESPHomeDumper.represent_stringify) ESPHomeDumper.add_multi_representer(int, ESPHomeDumper.represent_int) ESPHomeDumper.add_multi_representer(float, ESPHomeDumper.represent_float) -ESPHomeDumper.add_multi_representer(IPAddress, ESPHomeDumper.represent_stringify) +ESPHomeDumper.add_multi_representer(_BaseAddress, ESPHomeDumper.represent_stringify) ESPHomeDumper.add_multi_representer(MACAddress, ESPHomeDumper.represent_stringify) ESPHomeDumper.add_multi_representer(TimePeriod, ESPHomeDumper.represent_stringify) ESPHomeDumper.add_multi_representer(Lambda, ESPHomeDumper.represent_lambda) diff --git a/tests/unit_tests/test_config_validation.py b/tests/unit_tests/test_config_validation.py index 34f70be2fb..93ae67754a 100644 --- a/tests/unit_tests/test_config_validation.py +++ b/tests/unit_tests/test_config_validation.py @@ -1,12 +1,12 @@ -import pytest import string -from hypothesis import given, example -from hypothesis.strategies import one_of, text, integers, builds +from hypothesis import example, given +from hypothesis.strategies import builds, integers, ip_addresses, one_of, text +import pytest from esphome import config_validation from esphome.config_validation import Invalid -from esphome.core import CORE, Lambda, HexInt +from esphome.core import CORE, HexInt, Lambda def test_check_not_templatable__invalid(): @@ -145,6 +145,28 @@ def test_boolean__invalid(value): config_validation.boolean(value) +@given(value=ip_addresses(v=4).map(str)) +def test_ipv4__valid(value): + config_validation.ipv4address(value) + + +@pytest.mark.parametrize("value", ("127.0.0", "localhost", "")) +def test_ipv4__invalid(value): + with pytest.raises(Invalid, match="is not a valid IPv4 address"): + config_validation.ipv4address(value) + + +@given(value=ip_addresses(v=6).map(str)) +def test_ipv6__valid(value): + config_validation.ipaddress(value) + + +@pytest.mark.parametrize("value", ("127.0.0", "localhost", "", "2001:db8::2::3")) +def test_ipv6__invalid(value): + with pytest.raises(Invalid, match="is not a valid IP address"): + config_validation.ipaddress(value) + + # TODO: ensure_list @given(integers()) def hex_int__valid(value): diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index 2860486efe..4f2a6453b4 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -1,10 +1,8 @@ -import pytest - from hypothesis import given -from hypothesis.strategies import ip_addresses +import pytest from strategies import mac_addr_strings -from esphome import core, const +from esphome import const, core class TestHexInt: @@ -26,25 +24,6 @@ class TestHexInt: assert actual == expected -class TestIPAddress: - @given(value=ip_addresses(v=4).map(str)) - def test_init__valid(self, value): - core.IPAddress(*value.split(".")) - - @pytest.mark.parametrize("value", ("127.0.0", "localhost", "")) - def test_init__invalid(self, value): - with pytest.raises(ValueError, match="IPAddress must consist of 4 items"): - core.IPAddress(*value.split(".")) - - @given(value=ip_addresses(v=4).map(str)) - def test_str(self, value): - target = core.IPAddress(*value.split(".")) - - actual = str(target) - - assert actual == value - - class TestMACAddress: @given(value=mac_addr_strings()) def test_init__valid(self, value): From 40bee2a854a0cd82872c83d5c81f812199df5495 Mon Sep 17 00:00:00 2001 From: Brian Whicheloe Date: Sun, 12 Jan 2025 11:15:22 -0800 Subject: [PATCH 0850/1052] Add log level env var (#7604) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/__main__.py | 19 ++++++++++++++++--- esphome/log.py | 12 +++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index dce041e5ac..2a0bd8f2b3 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -758,6 +758,14 @@ def parse_args(argv): options_parser.add_argument( "-q", "--quiet", help="Disable all ESPHome logs.", action="store_true" ) + options_parser.add_argument( + "-l", + "--log-level", + help="Set the log level.", + default=os.getenv("ESPHOME_LOG_LEVEL", "INFO"), + action="store", + choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], + ) options_parser.add_argument( "--dashboard", help=argparse.SUPPRESS, action="store_true" ) @@ -987,11 +995,16 @@ def run_esphome(argv): args = parse_args(argv) CORE.dashboard = args.dashboard + # Override log level if verbose is set + if args.verbose: + args.log_level = "DEBUG" + elif args.quiet: + args.log_level = "CRITICAL" + setup_log( - args.verbose, - args.quiet, + log_level=args.log_level, # Show timestamp for dashboard access logs - args.command == "dashboard", + include_timestamp=args.command == "dashboard", ) if args.command in PRE_CONFIG_ACTIONS: diff --git a/esphome/log.py b/esphome/log.py index 23dc453d32..835cd6b44d 100644 --- a/esphome/log.py +++ b/esphome/log.py @@ -67,20 +67,18 @@ class ESPHomeLogFormatter(logging.Formatter): def setup_log( - debug: bool = False, quiet: bool = False, include_timestamp: bool = False + log_level=logging.INFO, + include_timestamp: bool = False, ) -> None: import colorama colorama.init() - if debug: - log_level = logging.DEBUG + if log_level == logging.DEBUG: CORE.verbose = True - elif quiet: - log_level = logging.CRITICAL + elif log_level == logging.CRITICAL: CORE.quiet = True - else: - log_level = logging.INFO + logging.basicConfig(level=log_level) logging.getLogger("urllib3").setLevel(logging.WARNING) From 731fb1d172f83d15a3f03152477dbd5f482cd19f Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Sun, 12 Jan 2025 23:15:39 +0100 Subject: [PATCH 0851/1052] [spi] relay on KEY_TARGET_PLATFORM as the other platforms does (#8066) --- esphome/components/spi/__init__.py | 6 +----- script/build_codeowners.py | 4 ++-- script/build_language_schema.py | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/esphome/components/spi/__init__.py b/esphome/components/spi/__init__.py index 52afbf365e..3e6d680b89 100644 --- a/esphome/components/spi/__init__.py +++ b/esphome/components/spi/__init__.py @@ -97,11 +97,7 @@ RP_SPI_PINSETS = [ def get_target_platform(): - return ( - CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] - if KEY_TARGET_PLATFORM in CORE.data[KEY_CORE] - else "" - ) + return CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] def get_target_variant(): diff --git a/script/build_codeowners.py b/script/build_codeowners.py index db34ad7702..523fe8ac7f 100755 --- a/script/build_codeowners.py +++ b/script/build_codeowners.py @@ -5,7 +5,7 @@ from pathlib import Path import sys from esphome.config import get_component, get_platform -from esphome.const import KEY_CORE, KEY_TARGET_FRAMEWORK +from esphome.const import KEY_CORE, KEY_TARGET_FRAMEWORK, KEY_TARGET_PLATFORM from esphome.core import CORE from esphome.helpers import write_file_if_changed @@ -39,7 +39,7 @@ parts = [BASE] # Fake some directory so that get_component works CORE.config_path = str(root) -CORE.data[KEY_CORE] = {KEY_TARGET_FRAMEWORK: None} +CORE.data[KEY_CORE] = {KEY_TARGET_FRAMEWORK: None, KEY_TARGET_PLATFORM: None} codeowners = defaultdict(list) diff --git a/script/build_language_schema.py b/script/build_language_schema.py index 2023dc0402..07093e179a 100644 --- a/script/build_language_schema.py +++ b/script/build_language_schema.py @@ -85,12 +85,12 @@ def load_components(): # pylint: disable=wrong-import-position -from esphome.const import CONF_TYPE, KEY_CORE +from esphome.const import CONF_TYPE, KEY_CORE, KEY_TARGET_PLATFORM from esphome.core import CORE # pylint: enable=wrong-import-position -CORE.data[KEY_CORE] = {} +CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: None} load_components() # Import esphome after loading components (so schema is tracked) From 7c3942269200e43f81391cc0e63d636ab63b6761 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:54:44 +1300 Subject: [PATCH 0852/1052] Bump actions/upload-artifact from 4.5.0 to 4.6.0 (#8058) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0304cd2304..e791f666d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -141,7 +141,7 @@ jobs: echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT - name: Upload digests - uses: actions/upload-artifact@v4.5.0 + uses: actions/upload-artifact@v4.6.0 with: name: digests-${{ steps.sanitize.outputs.name }} path: /tmp/digests From 571935fb3b97cd031390bb5b7ac4b4da5f62575b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:55:00 +1300 Subject: [PATCH 0853/1052] Bump peter-evans/create-pull-request from 7.0.5 to 7.0.6 (#8024) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/sync-device-classes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 7a46d596a1..9160ab4a1b 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -36,7 +36,7 @@ jobs: python ./script/sync-device_class.py - name: Commit changes - uses: peter-evans/create-pull-request@v7.0.5 + uses: peter-evans/create-pull-request@v7.0.6 with: commit-message: "Synchronise Device Classes from Home Assistant" committer: esphomebot From f25f3334d11d5a508aad6cdf5056b4cc33213fe7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:55:37 +1300 Subject: [PATCH 0854/1052] Bump docker/setup-qemu-action from 3.2.0 to 3.3.0 in the docker-actions group (#8052) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index cf7b08f3d4..b994cfaf17 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -48,7 +48,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.8.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v3.2.0 + uses: docker/setup-qemu-action@v3.3.0 - name: Set TAG run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e791f666d4..962bc66e94 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,7 @@ jobs: uses: docker/setup-buildx-action@v3.8.0 - name: Set up QEMU if: matrix.platform != 'linux/amd64' - uses: docker/setup-qemu-action@v3.2.0 + uses: docker/setup-qemu-action@v3.3.0 - name: Log in to docker hub uses: docker/login-action@v3.3.0 From 739edce268f83657ce5ef7d823008260a7c424db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 12:55:53 +1300 Subject: [PATCH 0855/1052] Bump docker/build-push-action from 6.10.0 to 6.11.0 in /.github/actions/build-image (#8053) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index cc9894a657..e6b6da4177 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.10.0 + uses: docker/build-push-action@v6.11.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false @@ -72,7 +72,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.10.0 + uses: docker/build-push-action@v6.11.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false From 4409471cd1c20a43a45bcfddead959c9d58c3419 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:32:10 +1300 Subject: [PATCH 0856/1052] Bump python3-setuptools to 66.1.1-1+deb12u1 (#8074) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0bb558d35e..429f5c4a1f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,7 +29,7 @@ RUN \ # Use pinned versions so that we get updates with build caching && apt-get install -y --no-install-recommends \ python3-pip=23.0.1+dfsg-1 \ - python3-setuptools=66.1.1-1 \ + python3-setuptools=66.1.1-1+deb12u1 \ python3-venv=3.11.2-1+b1 \ python3-wheel=0.38.4-2 \ iputils-ping=3:20221126-1+deb12u1 \ From fb87a1c0bc565447785f90367f28bb0e74584f49 Mon Sep 17 00:00:00 2001 From: Mischa Siekmann <45062894+gnumpi@users.noreply.github.com> Date: Mon, 13 Jan 2025 02:42:03 +0100 Subject: [PATCH 0857/1052] Allow CONF_RMT_CHANNEL parameter for IDF 4.X (#8035) --- .../components/esp32_rmt_led_strip/light.py | 54 +++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 67a0e31461..64104bb6de 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +import logging from esphome import pins import esphome.codegen as cg @@ -15,6 +16,9 @@ from esphome.const import ( CONF_RMT_CHANNEL, CONF_RMT_SYMBOLS, ) +from esphome.core import CORE + +_LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@jesserockz"] DEPENDENCIES = ["esp32"] @@ -64,13 +68,53 @@ CONF_RESET_HIGH = "reset_high" CONF_RESET_LOW = "reset_low" +class OptionalForIDF5(cv.SplitDefault): + @property + def default(self): + if not esp32_rmt.use_new_rmt_driver(): + return cv.UNDEFINED + return super().default + + @default.setter + def default(self, value): + # Ignore default set from vol.Optional + pass + + +def only_with_new_rmt_driver(obj): + if not esp32_rmt.use_new_rmt_driver(): + raise cv.Invalid( + "This feature is only available for the IDF framework version 5." + ) + return obj + + +def not_with_new_rmt_driver(obj): + if esp32_rmt.use_new_rmt_driver(): + raise cv.Invalid( + "This feature is not available for the IDF framework version 5." + ) + return obj + + def final_validation(config): - if not esp32_rmt.use_new_rmt_driver() and CONF_RMT_CHANNEL not in config: - raise cv.Invalid("rmt_channel is a required option.") + if not esp32_rmt.use_new_rmt_driver(): + if CONF_RMT_CHANNEL not in config: + if CORE.using_esp_idf: + raise cv.Invalid( + "rmt_channel is a required option for IDF version < 5." + ) + raise cv.Invalid( + "rmt_channel is a required option for the Arduino framework." + ) + _LOGGER.warning( + "RMT_LED_STRIP support for IDF version < 5 is deprecated and will be removed soon." + ) FINAL_VALIDATE_SCHEMA = final_validation + CONFIG_SCHEMA = cv.All( light.ADDRESSABLE_LIGHT_SCHEMA.extend( { @@ -79,9 +123,9 @@ CONFIG_SCHEMA = cv.All( cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int, cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True), cv.Optional(CONF_RMT_CHANNEL): cv.All( - cv.only_with_arduino, esp32_rmt.validate_rmt_channel(tx=True) + not_with_new_rmt_driver, esp32_rmt.validate_rmt_channel(tx=True) ), - cv.SplitDefault( + OptionalForIDF5( CONF_RMT_SYMBOLS, esp32_idf=64, esp32_s2_idf=64, @@ -89,7 +133,7 @@ CONFIG_SCHEMA = cv.All( esp32_c3_idf=48, esp32_c6_idf=48, esp32_h2_idf=48, - ): cv.All(cv.only_with_esp_idf, cv.int_range(min=2)), + ): cv.All(only_with_new_rmt_driver, cv.int_range(min=2)), cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), cv.Optional(CONF_IS_RGBW, default=False): cv.boolean, From aac38419915486fff121651bf6a4d063f6f4b312 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:45:35 -0500 Subject: [PATCH 0858/1052] [esp32] Fix arch_get_cpu_freq_hz (#8047) Co-authored-by: Jonathan Swoboda --- esphome/components/esp32/core.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index 48c8b2b04d..ff8e663ec1 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -58,7 +58,11 @@ uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); } #else uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); } #endif -uint32_t arch_get_cpu_freq_hz() { return rtc_clk_apb_freq_get(); } +uint32_t arch_get_cpu_freq_hz() { + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_get_config(&config); + return config.freq_mhz * 1000000U; +} #ifdef USE_ESP_IDF TaskHandle_t loop_task_handle = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) From dd3ffc7f29d8436171af8ce070ab68f7e226d570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20=C5=BBbik?= Date: Mon, 13 Jan 2025 03:55:30 +0100 Subject: [PATCH 0859/1052] Fix Waveshare 7in5bv3bwr image quality in BWR mode (#8043) Co-authored-by: zbikmarc --- .../waveshare_epaper/waveshare_epaper.cpp | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index cb3b19aa1a..fb9e8ff6e5 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -2425,28 +2425,21 @@ void WaveshareEPaper7P5InBV3BWR::init_display_() { this->command(0x01); // 1-0=11: internal power - this->data(0x07); - this->data(0x17); // VGH&VGL - this->data(0x3F); // VSH - this->data(0x26); // VSL - this->data(0x11); // VSHR + this->data(0x07); // VRS_EN=1, VS_EN=1, VG_EN=1 + this->data(0x17); // VGH&VGL ??? VCOM_SLEW=1 but this is fixed, VG_LVL[2:0]=111 => VGH=20V VGL=-20V, it could be 0x07 + this->data(0x3F); // VSH=15V? + this->data(0x26); // VSL=-9.4V? + this->data(0x11); // VSHR=5.8V? // VCOM DC Setting this->command(0x82); - this->data(0x24); // VCOM - - // Booster Setting - this->command(0x06); - this->data(0x27); - this->data(0x27); - this->data(0x2F); - this->data(0x17); + this->data(0x24); // VCOM=-1.9V // POWER ON this->command(0x04); - delay(100); // NOLINT this->wait_until_idle_(); + // COMMAND PANEL SETTING this->command(0x00); this->data(0x0F); // KW-3f KWR-2F BWROTP 0f BWOTP 1f @@ -2457,16 +2450,16 @@ void WaveshareEPaper7P5InBV3BWR::init_display_() { this->data(0x20); this->data(0x01); // gate 480 this->data(0xE0); - // COMMAND ...? - this->command(0x15); - this->data(0x00); + // COMMAND VCOM AND DATA INTERVAL SETTING this->command(0x50); this->data(0x20); this->data(0x00); + // COMMAND TCON SETTING this->command(0x60); this->data(0x22); + // Resolution setting this->command(0x65); this->data(0x00); From 92a8ebe1f8c15db118dac1269eb309b83f0ed9ee Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 13:56:42 +1100 Subject: [PATCH 0860/1052] [json] use correct formatting (#8039) --- esphome/components/json/json_util.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 87b1cc6d2d..d50b2b483c 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -17,11 +17,11 @@ std::string build_json(const json_build_t &f) { auto free_heap = ALLOCATOR.get_max_free_block_size(); size_t request_size = std::min(free_heap, (size_t) 512); while (true) { - ESP_LOGV(TAG, "Attempting to allocate %u bytes for JSON serialization", request_size); + 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 %u bytes, largest free heap block: %u bytes", + "Could not allocate memory for JSON document! Requested %zu bytes, largest free heap block: %zu bytes", request_size, free_heap); return "{}"; } @@ -29,7 +29,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: %u bytes", + ESP_LOGE(TAG, "Could not allocate memory for JSON document! Overflowed largest free heap block: %zu bytes", free_heap); return "{}"; } @@ -37,7 +37,7 @@ std::string build_json(const json_build_t &f) { continue; } json_document.shrinkToFit(); - ESP_LOGV(TAG, "Size after shrink %u bytes", json_document.capacity()); + ESP_LOGV(TAG, "Size after shrink %zu bytes", json_document.capacity()); std::string output; serializeJson(json_document, output); return output; From aa87c607173d9ef65e047a5e6b73d7ab4fac649d Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 12 Jan 2025 21:12:54 -0600 Subject: [PATCH 0861/1052] [nextion] Brightness control tweaks (#8027) --- esphome/components/nextion/automation.h | 17 +++++++++ esphome/components/nextion/display.py | 51 ++++++++++++++++++------- esphome/components/nextion/nextion.cpp | 4 +- esphome/components/nextion/nextion.h | 2 +- tests/components/nextion/common.yaml | 2 + 5 files changed, 61 insertions(+), 15 deletions(-) diff --git a/esphome/components/nextion/automation.h b/esphome/components/nextion/automation.h index 65f1fd0058..c718355af8 100644 --- a/esphome/components/nextion/automation.h +++ b/esphome/components/nextion/automation.h @@ -49,6 +49,23 @@ class TouchTrigger : public Trigger { } }; +template class NextionSetBrightnessAction : public Action { + public: + explicit NextionSetBrightnessAction(Nextion *component) : component_(component) {} + + TEMPLATABLE_VALUE(float, brightness) + + void play(Ts... x) override { + this->component_->set_brightness(this->brightness_.value(x...)); + this->component_->set_backlight_brightness(this->brightness_.value(x...)); + } + + void set_brightness(std::function brightness) { this->brightness_ = brightness; } + + protected: + Nextion *component_; +}; + template class NextionPublishFloatAction : public Action { public: explicit NextionPublishFloatAction(NextionComponent *component) : component_(component) {} diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index f6bd863d42..60f26e5234 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -1,30 +1,30 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation -from esphome.components import display, uart -from esphome.components import esp32 +import esphome.codegen as cg +from esphome.components import display, esp32, uart +import esphome.config_validation as cv from esphome.const import ( + CONF_BRIGHTNESS, CONF_ID, CONF_LAMBDA, - CONF_BRIGHTNESS, - CONF_TRIGGER_ID, CONF_ON_TOUCH, + CONF_TRIGGER_ID, ) from esphome.core import CORE + from . import Nextion, nextion_ns, nextion_ref from .base_component import ( + CONF_AUTO_WAKE_ON_TOUCH, + CONF_EXIT_REPARSE_ON_START, CONF_ON_BUFFER_OVERFLOW, + CONF_ON_PAGE, + CONF_ON_SETUP, CONF_ON_SLEEP, CONF_ON_WAKE, - CONF_ON_SETUP, - CONF_ON_PAGE, + CONF_SKIP_CONNECTION_HANDSHAKE, + CONF_START_UP_PAGE, CONF_TFT_URL, CONF_TOUCH_SLEEP_TIMEOUT, CONF_WAKE_UP_PAGE, - CONF_START_UP_PAGE, - CONF_AUTO_WAKE_ON_TOUCH, - CONF_EXIT_REPARSE_ON_START, - CONF_SKIP_CONNECTION_HANDSHAKE, ) CODEOWNERS = ["@senexcrenshaw", "@edwardtfn"] @@ -32,6 +32,9 @@ CODEOWNERS = ["@senexcrenshaw", "@edwardtfn"] DEPENDENCIES = ["uart"] AUTO_LOAD = ["binary_sensor", "switch", "sensor", "text_sensor"] +NextionSetBrightnessAction = nextion_ns.class_( + "NextionSetBrightnessAction", automation.Action +) SetupTrigger = nextion_ns.class_("SetupTrigger", automation.Trigger.template()) SleepTrigger = nextion_ns.class_("SleepTrigger", automation.Trigger.template()) WakeTrigger = nextion_ns.class_("WakeTrigger", automation.Trigger.template()) @@ -46,7 +49,7 @@ CONFIG_SCHEMA = ( { cv.GenerateID(): cv.declare_id(Nextion), cv.Optional(CONF_TFT_URL): cv.url, - cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_BRIGHTNESS): cv.percentage, cv.Optional(CONF_ON_SETUP): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger), @@ -92,12 +95,34 @@ CONFIG_SCHEMA = ( ) +@automation.register_action( + "display.nextion.set_brightness", + NextionSetBrightnessAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(Nextion), + cv.Required(CONF_BRIGHTNESS): cv.templatable(cv.percentage), + }, + key=CONF_BRIGHTNESS, + ), +) +async def nextion_set_brightness_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, float) + cg.add(var.set_brightness(template_)) + + return var + + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await uart.register_uart_device(var, config) if CONF_BRIGHTNESS in config: cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) + if CONF_LAMBDA in config: lambda_ = await cg.process_lambda( config[CONF_LAMBDA], [(nextion_ref, "it")], return_type=cg.void diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index e5df13c64e..67f08f68f8 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -273,7 +273,9 @@ void Nextion::loop() { this->sent_setup_commands_ = true; this->send_command_("bkcmd=3"); // Always, returns 0x00 to 0x23 result of serial command. - this->set_backlight_brightness(this->brightness_); + if (this->brightness_.has_value()) { + this->set_backlight_brightness(this->brightness_.value()); + } // Check if a startup page has been set and send the command if (this->start_up_page_ != -1) { diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index c293f80aee..b2404e1f0d 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1339,7 +1339,7 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe CallbackManager buffer_overflow_callback_{}; optional writer_; - float brightness_{1.0}; + optional brightness_; std::string device_model_; std::string firmware_version_; diff --git a/tests/components/nextion/common.yaml b/tests/components/nextion/common.yaml index 73fc8484c0..589afcfefb 100644 --- a/tests/components/nextion/common.yaml +++ b/tests/components/nextion/common.yaml @@ -2,6 +2,8 @@ esphome: on_boot: - lambda: 'ESP_LOGD("display","is_connected(): %s", YESNO(id(main_lcd).is_connected()));' + - display.nextion.set_brightness: 80% + # Binary sensor publish action tests - binary_sensor.nextion.publish: id: r0_sensor From f1c0570e3b51062f12c8ebf0fcefeb6fc81e22b7 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:21:42 +1100 Subject: [PATCH 0862/1052] [image] Transparency changes; code refactor (#7908) --- CODEOWNERS | 2 +- esphome/components/animation/__init__.py | 284 +------ esphome/components/animation/animation.cpp | 4 +- esphome/components/animation/animation.h | 3 +- esphome/components/image/__init__.py | 691 +++++++++++------- esphome/components/image/image.cpp | 143 ++-- esphome/components/image/image.h | 39 +- esphome/components/online_image/__init__.py | 118 +-- .../components/online_image/image_decoder.h | 11 +- .../components/online_image/online_image.cpp | 92 +-- .../components/online_image/online_image.h | 3 +- esphome/components/online_image/png_image.h | 1 + script/ci-custom.py | 17 +- tests/components/animation/.gitattributes | 4 + tests/components/animation/anim.apng | Bin 0 -> 12626 bytes tests/components/animation/anim.gif | Bin 0 -> 9735 bytes tests/components/animation/anim.webp | Bin 0 -> 8244 bytes tests/components/animation/common.yaml | 23 + .../components/animation/test.esp32-ard.yaml | 10 +- .../animation/test.esp32-c3-ard.yaml | 11 +- .../animation/test.esp32-c3-idf.yaml | 11 +- .../components/animation/test.esp32-idf.yaml | 11 +- .../animation/test.esp8266-ard.yaml | 11 +- .../components/animation/test.rp2040-ard.yaml | 11 +- tests/components/image/common.yaml | 49 +- tests/components/image/test.host.yaml | 42 +- tests/components/online_image/common.yaml | 41 +- 27 files changed, 845 insertions(+), 787 deletions(-) create mode 100644 tests/components/animation/.gitattributes create mode 100644 tests/components/animation/anim.apng create mode 100644 tests/components/animation/anim.gif create mode 100644 tests/components/animation/anim.webp create mode 100644 tests/components/animation/common.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 404ad35efc..088e350f5d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -302,7 +302,7 @@ esphome/components/noblex/* @AGalfra esphome/components/npi19/* @bakerkj esphome/components/number/* @esphome/core esphome/components/one_wire/* @ssieb -esphome/components/online_image/* @guillempages +esphome/components/online_image/* @clydebarrow @guillempages esphome/components/opentherm/* @olegtarasov esphome/components/ota/* @esphome/core esphome/components/output/* @esphome/core diff --git a/esphome/components/animation/__init__.py b/esphome/components/animation/__init__.py index 21a82649f0..f73b8ef08f 100644 --- a/esphome/components/animation/__init__.py +++ b/esphome/components/animation/__init__.py @@ -1,28 +1,10 @@ import logging -from esphome import automation, core +from esphome import automation import esphome.codegen as cg import esphome.components.image as espImage -from esphome.components.image import ( - CONF_USE_TRANSPARENCY, - LOCAL_SCHEMA, - SOURCE_LOCAL, - SOURCE_WEB, - WEB_SCHEMA, -) import esphome.config_validation as cv -from esphome.const import ( - CONF_FILE, - CONF_ID, - CONF_PATH, - CONF_RAW_DATA_ID, - CONF_REPEAT, - CONF_RESIZE, - CONF_SOURCE, - CONF_TYPE, - CONF_URL, -) -from esphome.core import CORE, HexInt +from esphome.const import CONF_ID, CONF_REPEAT _LOGGER = logging.getLogger(__name__) @@ -30,6 +12,7 @@ AUTO_LOAD = ["image"] CODEOWNERS = ["@syndlex"] DEPENDENCIES = ["display"] MULTI_CONF = True +MULTI_CONF_NO_DEFAULT = True CONF_LOOP = "loop" CONF_START_FRAME = "start_frame" @@ -51,86 +34,19 @@ SetFrameAction = animation_ns.class_( "AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_) ) -TYPED_FILE_SCHEMA = cv.typed_schema( +CONFIG_SCHEMA = espImage.IMAGE_SCHEMA.extend( { - SOURCE_LOCAL: LOCAL_SCHEMA, - SOURCE_WEB: WEB_SCHEMA, - }, - key=CONF_SOURCE, -) - - -def _file_schema(value): - if isinstance(value, str): - return validate_file_shorthand(value) - return TYPED_FILE_SCHEMA(value) - - -FILE_SCHEMA = cv.Schema(_file_schema) - - -def validate_file_shorthand(value): - value = cv.string_strict(value) - if value.startswith("http://") or value.startswith("https://"): - return FILE_SCHEMA( + cv.Required(CONF_ID): cv.declare_id(Animation_), + cv.Optional(CONF_LOOP): cv.All( { - CONF_SOURCE: SOURCE_WEB, - CONF_URL: value, + cv.Optional(CONF_START_FRAME, default=0): cv.positive_int, + cv.Optional(CONF_END_FRAME): cv.positive_int, + cv.Optional(CONF_REPEAT): cv.positive_int, } - ) - return FILE_SCHEMA( - { - CONF_SOURCE: SOURCE_LOCAL, - CONF_PATH: value, - } - ) - - -def validate_cross_dependencies(config): - """ - Validate fields whose possible values depend on other fields. - For example, validate that explicitly transparent image types - have "use_transparency" set to True. - Also set the default value for those kind of dependent fields. - """ - image_type = config[CONF_TYPE] - is_transparent_type = image_type in ["TRANSPARENT_BINARY", "RGBA"] - # If the use_transparency option was not specified, set the default depending on the image type - if CONF_USE_TRANSPARENCY not in config: - config[CONF_USE_TRANSPARENCY] = is_transparent_type - - if is_transparent_type and not config[CONF_USE_TRANSPARENCY]: - raise cv.Invalid(f"Image type {image_type} must always be transparent.") - - return config - - -ANIMATION_SCHEMA = cv.Schema( - cv.All( - { - cv.Required(CONF_ID): cv.declare_id(Animation_), - cv.Required(CONF_FILE): FILE_SCHEMA, - cv.Optional(CONF_RESIZE): cv.dimensions, - cv.Optional(CONF_TYPE, default="BINARY"): cv.enum( - espImage.IMAGE_TYPE, upper=True - ), - # Not setting default here on purpose; the default depends on the image type, - # and thus will be set in the "validate_cross_dependencies" validator. - cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean, - cv.Optional(CONF_LOOP): cv.All( - { - cv.Optional(CONF_START_FRAME, default=0): cv.positive_int, - cv.Optional(CONF_END_FRAME): cv.positive_int, - cv.Optional(CONF_REPEAT): cv.positive_int, - } - ), - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), - }, - validate_cross_dependencies, - ) + ), + }, ) -CONFIG_SCHEMA = ANIMATION_SCHEMA NEXT_FRAME_SCHEMA = automation.maybe_simple_id( { @@ -164,180 +80,26 @@ async def animation_action_to_code(config, action_id, template_arg, args): async def to_code(config): - from PIL import Image + ( + prog_arr, + width, + height, + image_type, + trans_value, + frame_count, + ) = await espImage.write_image(config, all_frames=True) - conf_file = config[CONF_FILE] - if conf_file[CONF_SOURCE] == SOURCE_LOCAL: - path = CORE.relative_config_path(conf_file[CONF_PATH]) - elif conf_file[CONF_SOURCE] == SOURCE_WEB: - path = espImage.compute_local_image_path(conf_file).as_posix() - else: - raise core.EsphomeError(f"Unknown animation source: {conf_file[CONF_SOURCE]}") - - try: - image = Image.open(path) - except Exception as e: - raise core.EsphomeError(f"Could not load image file {path}: {e}") - - width, height = image.size - frames = image.n_frames - if CONF_RESIZE in config: - new_width_max, new_height_max = config[CONF_RESIZE] - ratio = min(new_width_max / width, new_height_max / height) - width, height = int(width * ratio), int(height * ratio) - elif width > 500 or height > 500: - _LOGGER.warning( - 'The image "%s" you requested is very big. Please consider' - " using the resize parameter.", - path, - ) - - transparent = config[CONF_USE_TRANSPARENCY] - - if config[CONF_TYPE] == "GRAYSCALE": - data = [0 for _ in range(height * width * frames)] - pos = 0 - for frameIndex in range(frames): - image.seek(frameIndex) - frame = image.convert("LA", dither=Image.Dither.NONE) - if CONF_RESIZE in config: - frame = frame.resize([width, height]) - pixels = list(frame.getdata()) - if len(pixels) != height * width: - raise core.EsphomeError( - f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})" - ) - for pix, a in pixels: - if transparent: - if pix == 1: - pix = 0 - if a < 0x80: - pix = 1 - - data[pos] = pix - pos += 1 - - elif config[CONF_TYPE] == "RGBA": - data = [0 for _ in range(height * width * 4 * frames)] - pos = 0 - for frameIndex in range(frames): - image.seek(frameIndex) - frame = image.convert("RGBA") - if CONF_RESIZE in config: - frame = frame.resize([width, height]) - pixels = list(frame.getdata()) - if len(pixels) != height * width: - raise core.EsphomeError( - f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})" - ) - for pix in pixels: - data[pos] = pix[0] - pos += 1 - data[pos] = pix[1] - pos += 1 - data[pos] = pix[2] - pos += 1 - data[pos] = pix[3] - pos += 1 - - elif config[CONF_TYPE] == "RGB24": - data = [0 for _ in range(height * width * 3 * frames)] - pos = 0 - for frameIndex in range(frames): - image.seek(frameIndex) - frame = image.convert("RGBA") - if CONF_RESIZE in config: - frame = frame.resize([width, height]) - pixels = list(frame.getdata()) - if len(pixels) != height * width: - raise core.EsphomeError( - f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})" - ) - for r, g, b, a in pixels: - if transparent: - if r == 0 and g == 0 and b == 1: - b = 0 - if a < 0x80: - r = 0 - g = 0 - b = 1 - - data[pos] = r - pos += 1 - data[pos] = g - pos += 1 - data[pos] = b - pos += 1 - - elif config[CONF_TYPE] in ["RGB565", "TRANSPARENT_IMAGE"]: - bytes_per_pixel = 3 if transparent else 2 - data = [0 for _ in range(height * width * bytes_per_pixel * frames)] - pos = 0 - for frameIndex in range(frames): - image.seek(frameIndex) - frame = image.convert("RGBA") - if CONF_RESIZE in config: - frame = frame.resize([width, height]) - pixels = list(frame.getdata()) - if len(pixels) != height * width: - raise core.EsphomeError( - f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})" - ) - for r, g, b, a in pixels: - R = r >> 3 - G = g >> 2 - B = b >> 3 - rgb = (R << 11) | (G << 5) | B - data[pos] = rgb >> 8 - pos += 1 - data[pos] = rgb & 0xFF - pos += 1 - if transparent: - data[pos] = a - pos += 1 - - elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]: - width8 = ((width + 7) // 8) * 8 - data = [0 for _ in range((height * width8 // 8) * frames)] - for frameIndex in range(frames): - image.seek(frameIndex) - if transparent: - alpha = image.split()[-1] - has_alpha = alpha.getextrema()[0] < 0xFF - else: - has_alpha = False - frame = image.convert("1", dither=Image.Dither.NONE) - if CONF_RESIZE in config: - frame = frame.resize([width, height]) - if transparent: - alpha = alpha.resize([width, height]) - for x, y in [(i, j) for i in range(width) for j in range(height)]: - if transparent and has_alpha: - if not alpha.getpixel((x, y)): - continue - elif frame.getpixel((x, y)): - continue - - pos = x + y * width8 + (height * width8 * frameIndex) - data[pos // 8] |= 0x80 >> (pos % 8) - else: - raise core.EsphomeError( - f"Animation f{config[CONF_ID]} has not supported type {config[CONF_TYPE]}." - ) - - rhs = [HexInt(x) for x in data] - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) var = cg.new_Pvariable( config[CONF_ID], prog_arr, width, height, - frames, - espImage.IMAGE_TYPE[config[CONF_TYPE]], + frame_count, + image_type, + trans_value, ) - cg.add(var.set_transparency(transparent)) if loop_config := config.get(CONF_LOOP): start = loop_config[CONF_START_FRAME] - end = loop_config.get(CONF_END_FRAME, frames) + end = loop_config.get(CONF_END_FRAME, frame_count) count = loop_config.get(CONF_REPEAT, -1) cg.add(var.set_loop(start, end, count)) diff --git a/esphome/components/animation/animation.cpp b/esphome/components/animation/animation.cpp index 1375dfe07e..6db6f1a7bd 100644 --- a/esphome/components/animation/animation.cpp +++ b/esphome/components/animation/animation.cpp @@ -6,8 +6,8 @@ namespace esphome { namespace animation { Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, - image::ImageType type) - : Image(data_start, width, height, type), + image::ImageType type, image::Transparency transparent) + : Image(data_start, width, height, type, transparent), animation_data_start_(data_start), current_frame_(0), animation_frame_count_(animation_frame_count), diff --git a/esphome/components/animation/animation.h b/esphome/components/animation/animation.h index 272c5153d1..c44e0060af 100644 --- a/esphome/components/animation/animation.h +++ b/esphome/components/animation/animation.h @@ -8,7 +8,8 @@ namespace animation { class Animation : public image::Image { public: - Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type); + Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type, + image::Transparency transparent); uint32_t get_animation_frame_count() const; int get_current_frame() const; diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index 4669a3418a..801b05e160 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -6,7 +6,7 @@ import logging from pathlib import Path import re -import puremagic +from PIL import Image, UnidentifiedImageError from esphome import core, external_files import esphome.codegen as cg @@ -29,21 +29,236 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = "image" DEPENDENCIES = ["display"] -MULTI_CONF = True -MULTI_CONF_NO_DEFAULT = True image_ns = cg.esphome_ns.namespace("image") ImageType = image_ns.enum("ImageType") + +CONF_OPAQUE = "opaque" +CONF_CHROMA_KEY = "chroma_key" +CONF_ALPHA_CHANNEL = "alpha_channel" +CONF_INVERT_ALPHA = "invert_alpha" + +TRANSPARENCY_TYPES = ( + CONF_OPAQUE, + CONF_CHROMA_KEY, + CONF_ALPHA_CHANNEL, +) + + +def get_image_type_enum(type): + return getattr(ImageType, f"IMAGE_TYPE_{type.upper()}") + + +def get_transparency_enum(transparency): + return getattr(TransparencyType, f"TRANSPARENCY_{transparency.upper()}") + + +class ImageEncoder: + """ + Superclass of image type encoders + """ + + # Control which transparency options are available for a given type + allow_config = {CONF_ALPHA_CHANNEL, CONF_CHROMA_KEY, CONF_OPAQUE} + + # All imageencoder types are valid + @staticmethod + def validate(value): + return value + + def __init__(self, width, height, transparency, dither, invert_alpha): + """ + :param width: The image width in pixels + :param height: The image height in pixels + :param transparency: Transparency type + :param dither: Dither method + :param invert_alpha: True if the alpha channel should be inverted; for monochrome formats inverts the colours. + """ + self.transparency = transparency + self.width = width + self.height = height + self.data = [0 for _ in range(width * height)] + self.dither = dither + self.index = 0 + self.invert_alpha = invert_alpha + + def convert(self, image): + """ + Convert the image format + :param image: Input image + :return: converted image + """ + return image + + def encode(self, pixel): + """ + Encode a single pixel + """ + + def end_row(self): + """ + Marks the end of a pixel row + :return: + """ + + +class ImageBinary(ImageEncoder): + allow_config = {CONF_OPAQUE, CONF_INVERT_ALPHA, CONF_CHROMA_KEY} + + def __init__(self, width, height, transparency, dither, invert_alpha): + self.width8 = (width + 7) // 8 + super().__init__(self.width8, height, transparency, dither, invert_alpha) + self.bitno = 0 + + def convert(self, image): + return image.convert("1", dither=self.dither) + + def encode(self, pixel): + if self.invert_alpha: + pixel = not pixel + if pixel: + self.data[self.index] |= 0x80 >> (self.bitno % 8) + self.bitno += 1 + if self.bitno == 8: + self.bitno = 0 + self.index += 1 + + def end_row(self): + """ + Pad rows to a byte boundary + """ + if self.bitno != 0: + self.bitno = 0 + self.index += 1 + + +class ImageGrayscale(ImageEncoder): + allow_config = {CONF_ALPHA_CHANNEL, CONF_CHROMA_KEY, CONF_INVERT_ALPHA, CONF_OPAQUE} + + def convert(self, image): + return image.convert("LA") + + def encode(self, pixel): + b, a = pixel + if self.transparency == CONF_CHROMA_KEY: + if b == 1: + b = 0 + if a != 0xFF: + b = 1 + if self.invert_alpha: + b ^= 0xFF + if self.transparency == CONF_ALPHA_CHANNEL: + if a != 0xFF: + b = a + self.data[self.index] = b + self.index += 1 + + +class ImageRGB565(ImageEncoder): + def __init__(self, width, height, transparency, dither, invert_alpha): + stride = 3 if transparency == CONF_ALPHA_CHANNEL else 2 + super().__init__( + width * stride, + height, + transparency, + dither, + invert_alpha, + ) + + def convert(self, image): + return image.convert("RGBA") + + def encode(self, pixel): + r, g, b, a = pixel + r = r >> 3 + g = g >> 2 + b = b >> 3 + if self.transparency == CONF_CHROMA_KEY: + if r == 0 and g == 1 and b == 0: + g = 0 + elif a < 128: + r = 0 + g = 1 + b = 0 + rgb = (r << 11) | (g << 5) | b + self.data[self.index] = rgb >> 8 + self.index += 1 + self.data[self.index] = rgb & 0xFF + self.index += 1 + if self.transparency == CONF_ALPHA_CHANNEL: + if self.invert_alpha: + a ^= 0xFF + self.data[self.index] = a + self.index += 1 + + +class ImageRGB(ImageEncoder): + def __init__(self, width, height, transparency, dither, invert_alpha): + stride = 4 if transparency == CONF_ALPHA_CHANNEL else 3 + super().__init__( + width * stride, + height, + transparency, + dither, + invert_alpha, + ) + + def convert(self, image): + return image.convert("RGBA") + + def encode(self, pixel): + r, g, b, a = pixel + if self.transparency == CONF_CHROMA_KEY: + if r == 0 and g == 1 and b == 0: + g = 0 + elif a < 128: + r = 0 + g = 1 + b = 0 + self.data[self.index] = r + self.index += 1 + self.data[self.index] = g + self.index += 1 + self.data[self.index] = b + self.index += 1 + if self.transparency == CONF_ALPHA_CHANNEL: + if self.invert_alpha: + a ^= 0xFF + self.data[self.index] = a + self.index += 1 + + +class ReplaceWith: + """ + Placeholder class to provide feedback on deprecated features + """ + + allow_config = {CONF_ALPHA_CHANNEL, CONF_CHROMA_KEY, CONF_OPAQUE} + + def __init__(self, replace_with): + self.replace_with = replace_with + + def validate(self, value): + raise cv.Invalid( + f"Image type {value} is removed; replace with {self.replace_with}" + ) + + IMAGE_TYPE = { - "BINARY": ImageType.IMAGE_TYPE_BINARY, - "TRANSPARENT_BINARY": ImageType.IMAGE_TYPE_BINARY, - "GRAYSCALE": ImageType.IMAGE_TYPE_GRAYSCALE, - "RGB565": ImageType.IMAGE_TYPE_RGB565, - "RGB24": ImageType.IMAGE_TYPE_RGB24, - "RGBA": ImageType.IMAGE_TYPE_RGBA, + "BINARY": ImageBinary, + "GRAYSCALE": ImageGrayscale, + "RGB565": ImageRGB565, + "RGB": ImageRGB, + "TRANSPARENT_BINARY": ReplaceWith( + "'type: BINARY' and 'use_transparency: chroma_key'" + ), + "RGB24": ReplaceWith("'type: RGB'"), + "RGBA": ReplaceWith("'type: RGB' and 'use_transparency: alpha_channel'"), } +TransparencyType = image_ns.enum("TransparencyType") + CONF_USE_TRANSPARENCY = "use_transparency" # If the MDI file cannot be downloaded within this time, abort. @@ -53,17 +268,11 @@ SOURCE_LOCAL = "local" SOURCE_MDI = "mdi" SOURCE_WEB = "web" - Image_ = image_ns.class_("Image") -def _compute_local_icon_path(value: dict) -> Path: - base_dir = external_files.compute_local_file_dir(DOMAIN) / "mdi" - return base_dir / f"{value[CONF_ICON]}.svg" - - -def compute_local_image_path(value: dict) -> Path: - url = value[CONF_URL] +def compute_local_image_path(value) -> Path: + url = value[CONF_URL] if isinstance(value, dict) else value h = hashlib.new("sha256") h.update(url.encode()) key = h.hexdigest()[:8] @@ -71,30 +280,38 @@ def compute_local_image_path(value: dict) -> Path: return base_dir / key -def download_mdi(value): - validate_cairosvg_installed(value) +def local_path(value): + value = value[CONF_PATH] if isinstance(value, dict) else value + return str(CORE.relative_config_path(value)) - mdi_id = value[CONF_ICON] - path = _compute_local_icon_path(value) + +def download_file(url, path): + external_files.download_content(url, path, IMAGE_DOWNLOAD_TIMEOUT) + return str(path) + + +def download_mdi(value): + mdi_id = value[CONF_ICON] if isinstance(value, dict) else value + base_dir = external_files.compute_local_file_dir(DOMAIN) / "mdi" + path = base_dir / f"{mdi_id}.svg" url = f"https://raw.githubusercontent.com/Templarian/MaterialDesign/master/svg/{mdi_id}.svg" - - external_files.download_content(url, path, IMAGE_DOWNLOAD_TIMEOUT) - - return value + return download_file(url, path) def download_image(value): - url = value[CONF_URL] - path = compute_local_image_path(value) - - external_files.download_content(url, path, IMAGE_DOWNLOAD_TIMEOUT) - - return value + value = value[CONF_URL] if isinstance(value, dict) else value + return download_file(value, compute_local_image_path(value)) -def validate_cairosvg_installed(value): - """Validate that cairosvg is installed""" +def is_svg_file(file): + if not file: + return False + with open(file, "rb") as f: + return " 500 or height > 500): + if not resize and (width > 500 or height > 500): _LOGGER.warning( 'The image "%s" you requested is very big. Please consider' " using the resize parameter.", path, ) - transparent = config[CONF_USE_TRANSPARENCY] - dither = ( Image.Dither.NONE if config[CONF_DITHER] == "NONE" else Image.Dither.FLOYDSTEINBERG ) - if config[CONF_TYPE] == "GRAYSCALE": - image = image.convert("LA", dither=dither) - pixels = list(image.getdata()) - data = [0 for _ in range(height * width)] - pos = 0 - for g, a in pixels: - if transparent: - if g == 1: - g = 0 - if a < 0x80: - g = 1 + type = config[CONF_TYPE] + transparency = config[CONF_USE_TRANSPARENCY] + invert_alpha = config[CONF_INVERT_ALPHA] + frame_count = 1 + if all_frames: + try: + frame_count = image.n_frames + except AttributeError: + pass + if frame_count <= 1: + _LOGGER.warning("Image file %s has no animation frames", path) - data[pos] = g - pos += 1 + total_rows = height * frame_count + encoder = IMAGE_TYPE[type](width, total_rows, transparency, dither, invert_alpha) + for frame_index in range(frame_count): + image.seek(frame_index) + pixels = encoder.convert(image.resize((width, height))).getdata() + for row in range(height): + for col in range(width): + encoder.encode(pixels[row * width + col]) + encoder.end_row() - elif config[CONF_TYPE] == "RGBA": - image = image.convert("RGBA") - pixels = list(image.getdata()) - data = [0 for _ in range(height * width * 4)] - pos = 0 - for r, g, b, a in pixels: - data[pos] = r - pos += 1 - data[pos] = g - pos += 1 - data[pos] = b - pos += 1 - data[pos] = a - pos += 1 - - elif config[CONF_TYPE] == "RGB24": - image = image.convert("RGBA") - pixels = list(image.getdata()) - data = [0 for _ in range(height * width * 3)] - pos = 0 - for r, g, b, a in pixels: - if transparent: - if r == 0 and g == 0 and b == 1: - b = 0 - if a < 0x80: - r = 0 - g = 0 - b = 1 - - data[pos] = r - pos += 1 - data[pos] = g - pos += 1 - data[pos] = b - pos += 1 - - elif config[CONF_TYPE] in ["RGB565"]: - image = image.convert("RGBA") - pixels = list(image.getdata()) - bytes_per_pixel = 3 if transparent else 2 - data = [0 for _ in range(height * width * bytes_per_pixel)] - pos = 0 - for r, g, b, a in pixels: - R = r >> 3 - G = g >> 2 - B = b >> 3 - rgb = (R << 11) | (G << 5) | B - data[pos] = rgb >> 8 - pos += 1 - data[pos] = rgb & 0xFF - pos += 1 - if transparent: - data[pos] = a - pos += 1 - - elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]: - if transparent: - alpha = image.split()[-1] - has_alpha = alpha.getextrema()[0] < 0xFF - _LOGGER.debug("%s Has alpha: %s", config[CONF_ID], has_alpha) - image = image.convert("1", dither=dither) - width8 = ((width + 7) // 8) * 8 - data = [0 for _ in range(height * width8 // 8)] - for y in range(height): - for x in range(width): - if transparent and has_alpha: - a = alpha.getpixel((x, y)) - if not a: - continue - elif image.getpixel((x, y)): - continue - pos = x + y * width8 - data[pos // 8] |= 0x80 >> (pos % 8) - else: - raise core.EsphomeError( - f"Image f{config[CONF_ID]} has an unsupported type: {config[CONF_TYPE]}." - ) - - rhs = [HexInt(x) for x in data] + rhs = [HexInt(x) for x in encoder.data] prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - var = cg.new_Pvariable( - config[CONF_ID], prog_arr, width, height, IMAGE_TYPE[config[CONF_TYPE]] - ) - cg.add(var.set_transparency(transparent)) + image_type = get_image_type_enum(type) + trans_value = get_transparency_enum(transparency) + + return prog_arr, width, height, image_type, trans_value, frame_count + + +async def to_code(config): + if isinstance(config, list): + for entry in config: + await to_code(entry) + elif CONF_ID not in config: + for entry in config.values(): + await to_code(entry) + else: + prog_arr, width, height, image_type, trans_value, _ = await write_image(config) + cg.new_Pvariable( + config[CONF_ID], prog_arr, width, height, image_type, trans_value + ) diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index ca2f659fb0..e380112050 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -12,7 +12,7 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color for (int img_y = 0; img_y < height_; img_y++) { if (this->get_binary_pixel_(img_x, img_y)) { display->draw_pixel_at(x + img_x, y + img_y, color_on); - } else if (!this->transparent_) { + } else if (!this->transparency_) { display->draw_pixel_at(x + img_x, y + img_y, color_off); } } @@ -39,20 +39,10 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color } } break; - case IMAGE_TYPE_RGB24: + case IMAGE_TYPE_RGB: for (int img_x = 0; img_x < width_; img_x++) { for (int img_y = 0; img_y < height_; img_y++) { - auto color = this->get_rgb24_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - case IMAGE_TYPE_RGBA: - for (int img_x = 0; img_x < width_; img_x++) { - for (int img_y = 0; img_y < height_; img_y++) { - auto color = this->get_rgba_pixel_(img_x, img_y); + auto color = this->get_rgb_pixel_(img_x, img_y); if (color.w >= 0x80) { display->draw_pixel_at(x + img_x, y + img_y, color); } @@ -61,20 +51,20 @@ void Image::draw(int x, int y, display::Display *display, Color color_on, Color break; } } -Color Image::get_pixel(int x, int y, Color color_on, Color color_off) const { +Color Image::get_pixel(int x, int y, const Color color_on, const Color color_off) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return color_off; switch (this->type_) { case IMAGE_TYPE_BINARY: - return this->get_binary_pixel_(x, y) ? color_on : color_off; + if (this->get_binary_pixel_(x, y)) + return color_on; + return color_off; case IMAGE_TYPE_GRAYSCALE: return this->get_grayscale_pixel_(x, y); case IMAGE_TYPE_RGB565: return this->get_rgb565_pixel_(x, y); - case IMAGE_TYPE_RGB24: - return this->get_rgb24_pixel_(x, y); - case IMAGE_TYPE_RGBA: - return this->get_rgba_pixel_(x, y); + case IMAGE_TYPE_RGB: + return this->get_rgb_pixel_(x, y); default: return color_off; } @@ -98,23 +88,40 @@ lv_img_dsc_t *Image::get_lv_img_dsc() { this->dsc_.header.cf = LV_IMG_CF_ALPHA_8BIT; break; - case IMAGE_TYPE_RGB24: - this->dsc_.header.cf = LV_IMG_CF_RGB888; + case IMAGE_TYPE_RGB: +#if LV_COLOR_DEPTH == 32 + switch (this->transparent_) { + case TRANSPARENCY_ALPHA_CHANNEL: + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + break; + case TRANSPARENCY_CHROMA_KEY: + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED; + break; + default: + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR; + break; + } +#else + this->dsc_.header.cf = + this->transparency_ == TRANSPARENCY_ALPHA_CHANNEL ? LV_IMG_CF_RGBA8888 : LV_IMG_CF_RGB888; +#endif break; case IMAGE_TYPE_RGB565: #if LV_COLOR_DEPTH == 16 - this->dsc_.header.cf = this->has_transparency() ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; + switch (this->transparency_) { + case TRANSPARENCY_ALPHA_CHANNEL: + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + break; + case TRANSPARENCY_CHROMA_KEY: + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED; + break; + default: + this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR; + break; + } #else - this->dsc_.header.cf = LV_IMG_CF_RGB565; -#endif - break; - - case IMAGE_TYPE_RGBA: -#if LV_COLOR_DEPTH == 32 - this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR; -#else - this->dsc_.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + this->dsc_.header.cf = this->transparent_ == TRANSPARENCY_ALPHA_CHANNEL ? LV_IMG_CF_RGB565A8 : LV_IMG_CF_RGB565; #endif break; } @@ -128,51 +135,73 @@ bool Image::get_binary_pixel_(int x, int y) const { const uint32_t pos = x + y * width_8; return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u)); } -Color Image::get_rgba_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_) * 4; - return Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), - progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 3)); -} -Color Image::get_rgb24_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_) * 3; +Color Image::get_rgb_pixel_(int x, int y) const { + const uint32_t pos = (x + y * this->width_) * this->bpp_ / 8; Color color = Color(progmem_read_byte(this->data_start_ + pos + 0), progmem_read_byte(this->data_start_ + pos + 1), - progmem_read_byte(this->data_start_ + pos + 2)); - if (color.b == 1 && color.r == 0 && color.g == 0 && transparent_) { - // (0, 0, 1) has been defined as transparent color for non-alpha images. - // putting blue == 1 as a first condition for performance reasons (least likely value to short-cut the if) - color.w = 0; - } else { - color.w = 0xFF; + progmem_read_byte(this->data_start_ + pos + 2), 0xFF); + + switch (this->transparency_) { + case TRANSPARENCY_CHROMA_KEY: + if (color.g == 1 && color.r == 0 && color.b == 0) { + // (0, 1, 0) has been defined as transparent color for non-alpha images. + color.w = 0; + } + break; + case TRANSPARENCY_ALPHA_CHANNEL: + color.w = progmem_read_byte(this->data_start_ + (pos + 3)); + break; + default: + break; } return color; } Color Image::get_rgb565_pixel_(int x, int y) const { - const uint8_t *pos = this->data_start_; - if (this->transparent_) { - pos += (x + y * this->width_) * 3; - } else { - pos += (x + y * this->width_) * 2; - } + const uint8_t *pos = this->data_start_ + (x + y * this->width_) * this->bpp_ / 8; uint16_t rgb565 = encode_uint16(progmem_read_byte(pos), progmem_read_byte(pos + 1)); auto r = (rgb565 & 0xF800) >> 11; auto g = (rgb565 & 0x07E0) >> 5; auto b = rgb565 & 0x001F; - auto a = this->transparent_ ? progmem_read_byte(pos + 2) : 0xFF; - Color color = Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2), a); - return color; + auto a = 0xFF; + switch (this->transparency_) { + case TRANSPARENCY_ALPHA_CHANNEL: + a = progmem_read_byte(pos + 2); + break; + case TRANSPARENCY_CHROMA_KEY: + if (rgb565 == 0x0020) + a = 0; + break; + default: + break; + } + return Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2), a); } Color Image::get_grayscale_pixel_(int x, int y) const { const uint32_t pos = (x + y * this->width_); const uint8_t gray = progmem_read_byte(this->data_start_ + pos); - uint8_t alpha = (gray == 1 && transparent_) ? 0 : 0xFF; + uint8_t alpha = (gray == 1 && this->transparency_ == TRANSPARENCY_CHROMA_KEY) ? 0 : 0xFF; return Color(gray, gray, gray, alpha); } int Image::get_width() const { return this->width_; } int Image::get_height() const { return this->height_; } ImageType Image::get_type() const { return this->type_; } -Image::Image(const uint8_t *data_start, int width, int height, ImageType type) - : width_(width), height_(height), type_(type), data_start_(data_start) {} +Image::Image(const uint8_t *data_start, int width, int height, ImageType type, Transparency transparency) + : width_(width), height_(height), type_(type), data_start_(data_start), transparency_(transparency) { + switch (this->type_) { + case IMAGE_TYPE_BINARY: + this->bpp_ = 1; + break; + case IMAGE_TYPE_GRAYSCALE: + this->bpp_ = 8; + break; + case IMAGE_TYPE_RGB565: + this->bpp_ = transparency == TRANSPARENCY_ALPHA_CHANNEL ? 24 : 16; + break; + case IMAGE_TYPE_RGB: + this->bpp_ = this->transparency_ == TRANSPARENCY_ALPHA_CHANNEL ? 32 : 24; + break; + } +} } // namespace image } // namespace esphome diff --git a/esphome/components/image/image.h b/esphome/components/image/image.h index 40370d18da..4024ab1357 100644 --- a/esphome/components/image/image.h +++ b/esphome/components/image/image.h @@ -12,51 +12,40 @@ namespace image { enum ImageType { IMAGE_TYPE_BINARY = 0, IMAGE_TYPE_GRAYSCALE = 1, - IMAGE_TYPE_RGB24 = 2, + IMAGE_TYPE_RGB = 2, IMAGE_TYPE_RGB565 = 3, - IMAGE_TYPE_RGBA = 4, +}; + +enum Transparency { + TRANSPARENCY_OPAQUE = 0, + TRANSPARENCY_CHROMA_KEY = 1, + TRANSPARENCY_ALPHA_CHANNEL = 2, }; class Image : public display::BaseImage { public: - Image(const uint8_t *data_start, int width, int height, ImageType type); + Image(const uint8_t *data_start, int width, int height, ImageType type, Transparency transparency); Color get_pixel(int x, int y, Color color_on = display::COLOR_ON, Color color_off = display::COLOR_OFF) const; int get_width() const override; int get_height() const override; const uint8_t *get_data_start() const { return this->data_start_; } ImageType get_type() const; - int get_bpp() const { - switch (this->type_) { - case IMAGE_TYPE_BINARY: - return 1; - case IMAGE_TYPE_GRAYSCALE: - return 8; - case IMAGE_TYPE_RGB565: - return this->transparent_ ? 24 : 16; - case IMAGE_TYPE_RGB24: - return 24; - case IMAGE_TYPE_RGBA: - return 32; - } - return 0; - } + int get_bpp() const { return this->bpp_; } /// Return the stride of the image in bytes, that is, the distance in bytes /// between two consecutive rows of pixels. - uint32_t get_width_stride() const { return (this->width_ * this->get_bpp() + 7u) / 8u; } + size_t get_width_stride() const { return (this->width_ * this->get_bpp() + 7u) / 8u; } void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; - void set_transparency(bool transparent) { transparent_ = transparent; } - bool has_transparency() const { return transparent_; } + bool has_transparency() const { return this->transparency_ != TRANSPARENCY_OPAQUE; } #ifdef USE_LVGL lv_img_dsc_t *get_lv_img_dsc(); #endif protected: bool get_binary_pixel_(int x, int y) const; - Color get_rgb24_pixel_(int x, int y) const; - Color get_rgba_pixel_(int x, int y) const; + Color get_rgb_pixel_(int x, int y) const; Color get_rgb565_pixel_(int x, int y) const; Color get_grayscale_pixel_(int x, int y) const; @@ -64,7 +53,9 @@ class Image : public display::BaseImage { int height_; ImageType type_; const uint8_t *data_start_; - bool transparent_; + Transparency transparency_; + size_t bpp_{}; + size_t stride_{}; #ifdef USE_LVGL lv_img_dsc_t dsc_{}; #endif diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index be1bfb4a00..d1915c7364 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -4,14 +4,18 @@ from esphome import automation import esphome.codegen as cg from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent from esphome.components.image import ( + CONF_INVERT_ALPHA, CONF_USE_TRANSPARENCY, - IMAGE_TYPE, + IMAGE_SCHEMA, Image_, - validate_cross_dependencies, + get_image_type_enum, + get_transparency_enum, ) import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, + CONF_DITHER, + CONF_FILE, CONF_FORMAT, CONF_ID, CONF_ON_ERROR, @@ -23,7 +27,7 @@ from esphome.const import ( AUTO_LOAD = ["image"] DEPENDENCIES = ["display", "http_request"] -CODEOWNERS = ["@guillempages"] +CODEOWNERS = ["@guillempages", "@clydebarrow"] MULTI_CONF = True CONF_ON_DOWNLOAD_FINISHED = "on_download_finished" @@ -35,9 +39,30 @@ online_image_ns = cg.esphome_ns.namespace("online_image") ImageFormat = online_image_ns.enum("ImageFormat") -FORMAT_PNG = "PNG" -IMAGE_FORMAT = {FORMAT_PNG: ImageFormat.PNG} # Add new supported formats here +class Format: + def __init__(self, image_type): + self.image_type = image_type + + @property + def enum(self): + return getattr(ImageFormat, self.image_type) + + def actions(self): + pass + + +class PNGFormat(Format): + def __init__(self): + super().__init__("PNG") + + def actions(self): + cg.add_define("USE_ONLINE_IMAGE_PNG_SUPPORT") + cg.add_library("pngle", "1.0.2") + + +# New formats can be added here. +IMAGE_FORMATS = {x.image_type: x for x in (PNGFormat(),)} OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_) @@ -57,48 +82,54 @@ DownloadErrorTrigger = online_image_ns.class_( "DownloadErrorTrigger", automation.Trigger.template() ) -ONLINE_IMAGE_SCHEMA = cv.Schema( - { - cv.Required(CONF_ID): cv.declare_id(OnlineImage), - cv.GenerateID(CONF_HTTP_REQUEST_ID): cv.use_id(HttpRequestComponent), - # - # Common image options - # - cv.Optional(CONF_RESIZE): cv.dimensions, - cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(IMAGE_TYPE, upper=True), - # Not setting default here on purpose; the default depends on the image type, - # and thus will be set in the "validate_cross_dependencies" validator. - cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean, - # - # Online Image specific options - # - cv.Required(CONF_URL): cv.url, - cv.Required(CONF_FORMAT): cv.enum(IMAGE_FORMAT, upper=True), - cv.Optional(CONF_PLACEHOLDER): cv.use_id(Image_), - cv.Optional(CONF_BUFFER_SIZE, default=2048): cv.int_range(256, 65536), - cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadFinishedTrigger), - } - ), - cv.Optional(CONF_ON_ERROR): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadErrorTrigger), - } - ), + +def remove_options(*options): + return { + cv.Optional(option): cv.invalid( + f"{option} is an invalid option for online_image" + ) + for option in options } -).extend(cv.polling_component_schema("never")) + + +ONLINE_IMAGE_SCHEMA = ( + IMAGE_SCHEMA.extend(remove_options(CONF_FILE, CONF_INVERT_ALPHA, CONF_DITHER)) + .extend( + { + cv.Required(CONF_ID): cv.declare_id(OnlineImage), + cv.GenerateID(CONF_HTTP_REQUEST_ID): cv.use_id(HttpRequestComponent), + # Online Image specific options + cv.Required(CONF_URL): cv.url, + 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=2048): cv.int_range(256, 65536), + cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + DownloadFinishedTrigger + ), + } + ), + cv.Optional(CONF_ON_ERROR): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DownloadErrorTrigger), + } + ), + } + ) + .extend(cv.polling_component_schema("never")) +) CONFIG_SCHEMA = cv.Schema( cv.All( ONLINE_IMAGE_SCHEMA, - validate_cross_dependencies, cv.require_framework_version( # esp8266 not supported yet; if enabled in the future, minimum version of 2.7.0 is needed # esp8266_arduino=cv.Version(2, 7, 0), esp32_arduino=cv.Version(0, 0, 0), esp_idf=cv.Version(4, 0, 0), rp2040_arduino=cv.Version(0, 0, 0), + host=cv.Version(0, 0, 0), ), ) ) @@ -132,29 +163,26 @@ async def online_image_action_to_code(config, action_id, template_arg, args): async def to_code(config): - format = config[CONF_FORMAT] - if format in [FORMAT_PNG]: - cg.add_define("USE_ONLINE_IMAGE_PNG_SUPPORT") - cg.add_library("pngle", "1.0.2") + image_format = IMAGE_FORMATS[config[CONF_FORMAT]] + image_format.actions() url = config[CONF_URL] width, height = config.get(CONF_RESIZE, (0, 0)) - transparent = config[CONF_USE_TRANSPARENCY] + transparent = get_transparency_enum(config[CONF_USE_TRANSPARENCY]) var = cg.new_Pvariable( config[CONF_ID], url, width, height, - format, - config[CONF_TYPE], + image_format.enum, + get_image_type_enum(config[CONF_TYPE]), + transparent, config[CONF_BUFFER_SIZE], ) await cg.register_component(var, config) await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID]) - cg.add(var.set_transparency(transparent)) - if placeholder_id := config.get(CONF_PLACEHOLDER): placeholder = await cg.get_variable(placeholder_id) cg.add(var.set_placeholder(placeholder)) diff --git a/esphome/components/online_image/image_decoder.h b/esphome/components/online_image/image_decoder.h index 908efab987..cde7f572e3 100644 --- a/esphome/components/online_image/image_decoder.h +++ b/esphome/components/online_image/image_decoder.h @@ -1,5 +1,4 @@ #pragma once -#include "esphome/core/defines.h" #include "esphome/core/color.h" namespace esphome { @@ -23,7 +22,7 @@ class ImageDecoder { /** * @brief Initialize the decoder. * - * @param download_size The total number of bytes that need to be download for the image. + * @param download_size The total number of bytes that need to be downloaded for the image. */ virtual void prepare(uint32_t download_size) { this->download_size_ = download_size; } @@ -38,7 +37,7 @@ class ImageDecoder { * @return int The amount of bytes read. It can be 0 if the buffer does not have enough content to meaningfully * decode anything, or negative in case of a decoding error. */ - virtual int decode(uint8_t *buffer, size_t size); + virtual int decode(uint8_t *buffer, size_t size) = 0; /** * @brief Request the image to be resized once the actual dimensions are known. @@ -50,7 +49,7 @@ class ImageDecoder { void set_size(int width, int height); /** - * @brief Draw a rectangle on the display_buffer using the defined color. + * @brief Fill a rectangle on the display_buffer using the defined color. * Will check the given coordinates for out-of-bounds, and clip the rectangle accordingly. * In case of binary displays, the color will be converted to binary as well. * Called by the callback functions, to be able to access the parent Image class. @@ -59,7 +58,7 @@ class ImageDecoder { * @param y The top-most coordinate of the rectangle. * @param w The width of the rectangle. * @param h The height of the rectangle. - * @param color The color to draw the rectangle with. + * @param color The fill color */ void draw(int x, int y, int w, int h, const Color &color); @@ -67,7 +66,7 @@ class ImageDecoder { protected: OnlineImage *image_; - // Initializing to 1, to ensure it is different than initial "decoded_bytes_". + // Initializing to 1, to ensure it is distinguishable from initial "decoded_bytes_". // Will be overwritten anyway once the download size is known. uint32_t download_size_ = 1; uint32_t decoded_bytes_ = 0; diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 8c4669cba5..93d070c6a9 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -25,8 +25,8 @@ inline bool is_color_on(const Color &color) { } OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type, - uint32_t download_buffer_size) - : Image(nullptr, 0, 0, type), + image::Transparency transparency, uint32_t download_buffer_size) + : Image(nullptr, 0, 0, type, transparency), buffer_(nullptr), download_buffer_(download_buffer_size), format_(format), @@ -45,7 +45,7 @@ void OnlineImage::draw(int x, int y, display::Display *display, Color color_on, void OnlineImage::release() { if (this->buffer_) { - ESP_LOGD(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; @@ -70,20 +70,19 @@ bool OnlineImage::resize_(int width_in, int height_in) { if (this->buffer_) { return false; } - auto new_size = this->get_buffer_size_(width, height); - ESP_LOGD(TAG, "Allocating new buffer of %d Bytes...", new_size); - delay_microseconds_safe(2000); + size_t new_size = this->get_buffer_size_(width, height); + ESP_LOGD(TAG, "Allocating new buffer of %zu bytes", new_size); this->buffer_ = this->allocator_.allocate(new_size); - if (this->buffer_) { - this->buffer_width_ = width; - this->buffer_height_ = height; - this->width_ = width; - ESP_LOGD(TAG, "New size: (%d, %d)", width, height); - } else { - ESP_LOGE(TAG, "allocation failed. Biggest block in heap: %zu Bytes", this->allocator_.get_max_free_block_size()); + if (this->buffer_ == nullptr) { + ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", new_size, + this->allocator_.get_max_free_block_size()); this->end_connection_(); return false; } + this->buffer_width_ = width; + this->buffer_height_ = height; + this->width_ = width; + ESP_LOGV(TAG, "New size: (%d, %d)", width, height); return true; } @@ -91,9 +90,8 @@ void OnlineImage::update() { if (this->decoder_) { ESP_LOGW(TAG, "Image already being updated."); return; - } else { - ESP_LOGI(TAG, "Updating image"); } + ESP_LOGI(TAG, "Updating image %s", this->url_.c_str()); this->downloader_ = this->parent_->get(this->url_); @@ -142,10 +140,11 @@ void OnlineImage::loop() { return; } if (!this->downloader_ || this->decoder_->is_finished()) { - ESP_LOGD(TAG, "Image fully downloaded"); this->data_start_ = buffer_; this->width_ = buffer_width_; this->height_ = buffer_height_; + ESP_LOGD(TAG, "Image fully downloaded, read %zu bytes, width/height = %d/%d", this->downloader_->get_bytes_read(), + this->width_, this->height_); this->end_connection_(); this->download_finished_callback_.call(); return; @@ -171,6 +170,19 @@ void OnlineImage::loop() { } } +void OnlineImage::map_chroma_key(Color &color) { + if (this->transparency_ == image::TRANSPARENCY_CHROMA_KEY) { + if (color.g == 1 && color.r == 0 && color.b == 0) { + color.g = 0; + } + if (color.w < 0x80) { + color.r = 0; + color.g = this->type_ == ImageType::IMAGE_TYPE_RGB565 ? 4 : 1; + color.b = 0; + } + } +} + void OnlineImage::draw_pixel_(int x, int y, Color color) { if (!this->buffer_) { ESP_LOGE(TAG, "Buffer not allocated!"); @@ -184,57 +196,53 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) { switch (this->type_) { case ImageType::IMAGE_TYPE_BINARY: { const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u; - const uint32_t pos = x + y * width_8; - if ((this->has_transparency() && color.w > 127) || is_color_on(color)) { - this->buffer_[pos / 8u] |= (0x80 >> (pos % 8u)); + pos = x + y * width_8; + auto bitno = 0x80 >> (pos % 8u); + pos /= 8u; + auto on = is_color_on(color); + if (this->has_transparency() && color.w < 0x80) + on = false; + if (on) { + this->buffer_[pos] |= bitno; } else { - this->buffer_[pos / 8u] &= ~(0x80 >> (pos % 8u)); + this->buffer_[pos] &= ~bitno; } break; } case ImageType::IMAGE_TYPE_GRAYSCALE: { uint8_t gray = static_cast(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b); - if (this->has_transparency()) { + if (this->transparency_ == image::TRANSPARENCY_CHROMA_KEY) { if (gray == 1) { gray = 0; } if (color.w < 0x80) { gray = 1; } + } else if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) { + if (color.w != 0xFF) + gray = color.w; } this->buffer_[pos] = gray; break; } case ImageType::IMAGE_TYPE_RGB565: { + this->map_chroma_key(color); uint16_t col565 = display::ColorUtil::color_to_565(color); this->buffer_[pos + 0] = static_cast((col565 >> 8) & 0xFF); this->buffer_[pos + 1] = static_cast(col565 & 0xFF); - if (this->has_transparency()) + if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) { this->buffer_[pos + 2] = color.w; - break; - } - case ImageType::IMAGE_TYPE_RGBA: { - this->buffer_[pos + 0] = color.r; - this->buffer_[pos + 1] = color.g; - this->buffer_[pos + 2] = color.b; - this->buffer_[pos + 3] = color.w; - break; - } - case ImageType::IMAGE_TYPE_RGB24: - default: { - if (this->has_transparency()) { - if (color.b == 1 && color.r == 0 && color.g == 0) { - color.b = 0; - } - if (color.w < 0x80) { - color.r = 0; - color.g = 0; - color.b = 1; - } } + break; + } + case ImageType::IMAGE_TYPE_RGB: { + this->map_chroma_key(color); this->buffer_[pos + 0] = color.r; this->buffer_[pos + 1] = color.g; this->buffer_[pos + 2] = color.b; + if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) { + this->buffer_[pos + 3] = color.w; + } break; } } diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 017402a088..e044b4f390 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -48,12 +48,13 @@ class OnlineImage : public PollingComponent, * @param buffer_size Size of the buffer used to download the image. */ OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type, - uint32_t buffer_size); + image::Transparency transparency, uint32_t buffer_size); void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override; void update() override; void loop() override; + void map_chroma_key(Color &color); /** Set the URL to download the image from. */ void set_url(const std::string &url) { diff --git a/esphome/components/online_image/png_image.h b/esphome/components/online_image/png_image.h index a928276dcc..d82ff93149 100644 --- a/esphome/components/online_image/png_image.h +++ b/esphome/components/online_image/png_image.h @@ -1,6 +1,7 @@ #pragma once #include "image_decoder.h" +#include "esphome/core/defines.h" #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT #include diff --git a/script/ci-custom.py b/script/ci-custom.py index 81e3da311a..d5d3ab88c8 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -58,7 +58,19 @@ file_types = ( ) cpp_include = ("*.h", "*.c", "*.cpp", "*.tcc") py_include = ("*.py",) -ignore_types = (".ico", ".png", ".woff", ".woff2", "", ".ttf", ".otf", ".pcf") +ignore_types = ( + ".ico", + ".png", + ".woff", + ".woff2", + "", + ".ttf", + ".otf", + ".pcf", + ".apng", + ".gif", + ".webp", +) LINT_FILE_CHECKS = [] LINT_CONTENT_CHECKS = [] @@ -669,8 +681,7 @@ def main(): ) args = parser.parse_args() - global EXECUTABLE_BIT - EXECUTABLE_BIT = git_ls_files() + EXECUTABLE_BIT.update(git_ls_files()) files = list(EXECUTABLE_BIT.keys()) # Match against re file_name_re = re.compile("|".join(args.files)) diff --git a/tests/components/animation/.gitattributes b/tests/components/animation/.gitattributes new file mode 100644 index 0000000000..ff9fc6f1f1 --- /dev/null +++ b/tests/components/animation/.gitattributes @@ -0,0 +1,4 @@ +*.apng -text +*.webp -text +*.gif -text + diff --git a/tests/components/animation/anim.apng b/tests/components/animation/anim.apng new file mode 100644 index 0000000000000000000000000000000000000000..927af5eb05a94ea8b1cdab493d2bfd8feffb7eac GIT binary patch literal 12626 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

m9s*zMUB&9i)Vj-aXg1Rr|O2;S{r^I>B3s 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 Date: Sun, 12 Jan 2025 19:23:35 -0800 Subject: [PATCH 0863/1052] Added VERY_VERBOSE dfplayer printing (#8026) --- esphome/components/dfplayer/dfplayer.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/esphome/components/dfplayer/dfplayer.cpp b/esphome/components/dfplayer/dfplayer.cpp index 98c3e91e46..70bd42e1a5 100644 --- a/esphome/components/dfplayer/dfplayer.cpp +++ b/esphome/components/dfplayer/dfplayer.cpp @@ -159,6 +159,15 @@ void DFPlayer::loop() { } break; case 9: // End byte +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE + char byte_sequence[100]; + byte_sequence[0] = '\0'; + for (size_t i = 0; i < this->read_pos_ + 1; ++i) { + snprintf(byte_sequence + strlen(byte_sequence), sizeof(byte_sequence) - strlen(byte_sequence), "%02X ", + this->read_buffer_[i]); + } + ESP_LOGVV(TAG, "Received byte sequence: %s", byte_sequence); +#endif if (byte != 0xEF) { ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte); this->read_pos_ = 0; @@ -238,13 +247,17 @@ void DFPlayer::loop() { this->ack_set_is_playing_ = false; this->ack_reset_is_playing_ = false; break; + case 0x3C: + ESP_LOGV(TAG, "Playback finished (USB drive)"); + this->is_playing_ = false; + this->on_finished_playback_callback_.call(); case 0x3D: - ESP_LOGV(TAG, "Playback finished"); + ESP_LOGV(TAG, "Playback finished (SD card)"); this->is_playing_ = false; this->on_finished_playback_callback_.call(); break; default: - ESP_LOGV(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument); + ESP_LOGE(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument); } this->sent_cmd_ = 0; this->read_pos_ = 0; From d8c943972b2a3e68ec126922cd8601fba290b6c0 Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:05:53 +0100 Subject: [PATCH 0864/1052] [core] fix comment for crc8 function in helpers.h (#8016) --- esphome/core/helpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index c823439fb3..82b0fe07f8 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -163,7 +163,7 @@ template T remap(U value, U min, U max, T min_out, T max return (value - min) * (max_out - min_out) / (max - min) + min_out; } -/// Calculate a CRC-8 checksum of \p data with size \p len. +/// Calculate a CRC-8 checksum of \p data with size \p len using the CRC-8-Dallas/Maxim polynomial. uint8_t crc8(const uint8_t *data, uint8_t len); /// Calculate a CRC-16 checksum of \p data with size \p len. From aa1879082c67dd893591d854ea8e057e2ad99fa6 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:06:44 +0100 Subject: [PATCH 0865/1052] [debug] Add framework type to debug info (#8013) --- esphome/components/debug/debug_esp32.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index 5f7b9cdbb0..b0631f2b61 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -276,6 +276,19 @@ void DebugComponent::get_device_info_(std::string &device_info) { device_info += " Cores:" + to_string(info.cores); device_info += " Revision:" + to_string(info.revision); + // Framework detection + device_info += "|Framework: "; +#ifdef USE_ARDUINO + ESP_LOGD(TAG, "Framework: Arduino"); + device_info += "Arduino"; +#elif defined(USE_ESP_IDF) + ESP_LOGD(TAG, "Framework: ESP-IDF"); + device_info += "ESP-IDF"; +#else + ESP_LOGW(TAG, "Framework: UNKNOWN"); + device_info += "UNKNOWN"; +#endif + ESP_LOGD(TAG, "ESP-IDF Version: %s", esp_get_idf_version()); device_info += "|ESP-IDF: "; device_info += esp_get_idf_version(); From fef50afef8bbf333de3d8f0fc3754d231f7bf165 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:08:20 +0100 Subject: [PATCH 0866/1052] [debug] Add ESP32 partition table logging to `dump_config` (#8012) --- esphome/components/debug/debug_component.cpp | 4 ++++ esphome/components/debug/debug_component.h | 14 ++++++++++++++ esphome/components/debug/debug_esp32.cpp | 14 ++++++++++++++ tests/components/debug/test.esp32-s2-ard.yaml | 1 + tests/components/debug/test.esp32-s2-idf.yaml | 1 + tests/components/debug/test.esp32-s3-ard.yaml | 1 + tests/components/debug/test.esp32-s3-idf.yaml | 1 + 7 files changed, 36 insertions(+) create mode 100644 tests/components/debug/test.esp32-s2-ard.yaml create mode 100644 tests/components/debug/test.esp32-s2-idf.yaml create mode 100644 tests/components/debug/test.esp32-s3-ard.yaml create mode 100644 tests/components/debug/test.esp32-s3-idf.yaml diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index cbd4249d92..7d25bf5472 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -50,6 +50,10 @@ void DebugComponent::dump_config() { this->reset_reason_->publish_state(get_reset_reason_()); } #endif // USE_TEXT_SENSOR + +#ifdef USE_ESP32 + this->log_partition_info_(); // Log partition information for ESP32 +#endif // USE_ESP32 } void DebugComponent::loop() { diff --git a/esphome/components/debug/debug_component.h b/esphome/components/debug/debug_component.h index 2b54406603..608addb4a3 100644 --- a/esphome/components/debug/debug_component.h +++ b/esphome/components/debug/debug_component.h @@ -55,6 +55,20 @@ class DebugComponent : public PollingComponent { #endif // USE_ESP32 #endif // USE_SENSOR +#ifdef USE_ESP32 + /** + * @brief Logs information about the device's partition table. + * + * This function iterates through the ESP32's partition table and logs details + * about each partition, including its name, type, subtype, starting address, + * and size. The information is useful for diagnosing issues related to flash + * memory or verifying the partition configuration dynamically at runtime. + * + * Only available when compiled for ESP32 platforms. + */ + void log_partition_info_(); +#endif // USE_ESP32 + #ifdef USE_TEXT_SENSOR text_sensor::TextSensor *device_info_{nullptr}; text_sensor::TextSensor *reset_reason_{nullptr}; diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index b0631f2b61..69ae7e3678 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #if defined(USE_ESP32_VARIANT_ESP32) #include @@ -28,6 +29,19 @@ namespace debug { static const char *const TAG = "debug"; +void DebugComponent::log_partition_info_() { + ESP_LOGCONFIG(TAG, "Partition table:"); + ESP_LOGCONFIG(TAG, " %-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); + ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08X 0x%08X", partition->label, partition->type, partition->subtype, + partition->address, partition->size); + it = esp_partition_next(it); + } + esp_partition_iterator_release(it); +} + std::string DebugComponent::get_reset_reason_() { std::string reset_reason; switch (esp_reset_reason()) { diff --git a/tests/components/debug/test.esp32-s2-ard.yaml b/tests/components/debug/test.esp32-s2-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/debug/test.esp32-s2-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/debug/test.esp32-s2-idf.yaml b/tests/components/debug/test.esp32-s2-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/debug/test.esp32-s2-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/debug/test.esp32-s3-ard.yaml b/tests/components/debug/test.esp32-s3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/debug/test.esp32-s3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/debug/test.esp32-s3-idf.yaml b/tests/components/debug/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/debug/test.esp32-s3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 528d3672b499650185427297cfb7399fedd8fcf4 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Mon, 13 Jan 2025 05:11:48 +0100 Subject: [PATCH 0867/1052] [psram] Improve total PSRAM display in logs by using rounded KB values (#8008) Co-authored-by: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> --- esphome/components/psram/psram.cpp | 9 ++++++++- tests/components/psram/test.esp32-s2-ard.yaml | 1 + tests/components/psram/test.esp32-s2-idf.yaml | 1 + tests/components/psram/test.esp32-s3-ard.yaml | 1 + tests/components/psram/test.esp32-s3-idf.yaml | 1 + 5 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/components/psram/test.esp32-s2-ard.yaml create mode 100644 tests/components/psram/test.esp32-s2-idf.yaml create mode 100644 tests/components/psram/test.esp32-s3-ard.yaml create mode 100644 tests/components/psram/test.esp32-s3-idf.yaml diff --git a/esphome/components/psram/psram.cpp b/esphome/components/psram/psram.cpp index 68d8dfd697..d9a5bd101f 100644 --- a/esphome/components/psram/psram.cpp +++ b/esphome/components/psram/psram.cpp @@ -21,7 +21,14 @@ void PsramComponent::dump_config() { ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available)); #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 1, 0) if (available) { - ESP_LOGCONFIG(TAG, " Size: %d KB", heap_caps_get_total_size(MALLOC_CAP_SPIRAM) / 1024); + const size_t psram_total_size_bytes = heap_caps_get_total_size(MALLOC_CAP_SPIRAM); + const float psram_total_size_kb = psram_total_size_bytes / 1024.0f; + + if (abs(std::round(psram_total_size_kb) - psram_total_size_kb) < 0.05f) { + ESP_LOGCONFIG(TAG, " Size: %.0f KB", psram_total_size_kb); + } else { + ESP_LOGCONFIG(TAG, " Size: %zu bytes", psram_total_size_bytes); + } } #endif } diff --git a/tests/components/psram/test.esp32-s2-ard.yaml b/tests/components/psram/test.esp32-s2-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/psram/test.esp32-s2-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-s2-idf.yaml b/tests/components/psram/test.esp32-s2-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/psram/test.esp32-s2-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-s3-ard.yaml b/tests/components/psram/test.esp32-s3-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/psram/test.esp32-s3-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/psram/test.esp32-s3-idf.yaml b/tests/components/psram/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/psram/test.esp32-s3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml From 8fbd512952e516b5b4a64d9592d81cbeb9349c38 Mon Sep 17 00:00:00 2001 From: Douglas <31328123+dougiteixeira@users.noreply.github.com> Date: Mon, 13 Jan 2025 01:16:43 -0300 Subject: [PATCH 0868/1052] Use ESPHome logo on readme page according to theme (light/dark) (#7992) --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da1b2b3650..8e3d8f71aa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # ESPHome [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/) -[![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/) + + + + ESPHome Logo + + **Documentation:** https://esphome.io/ From 3fa67fad32c9515b1bf876bb7aef02b6d311c1b3 Mon Sep 17 00:00:00 2001 From: Ryan Henderson Date: Sun, 12 Jan 2025 20:17:28 -0800 Subject: [PATCH 0869/1052] Fix compile errors with pioarduino/platform-espressif32: wifi_component_esp32_arduino.cpp (#7998) --- .../components/wifi/wifi_component_esp32_arduino.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index bc10bbd1e5..76d0b7d96c 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -11,6 +11,11 @@ #ifdef USE_WIFI_WPA2_EAP #include #endif + +#ifdef USE_WIFI_AP +#include "dhcpserver/dhcpserver.h" +#endif // USE_WIFI_AP + #include "lwip/apps/sntp.h" #include "lwip/dns.h" #include "lwip/err.h" @@ -638,7 +643,12 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ } WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() { - auto status = WiFiClass::status(); +#if USE_ARDUINO_VERSION_CODE < VERSION_CODE(3, 1, 0) + const auto status = WiFiClass::status(); +#else + const auto status = WiFi.status(); +#endif + if (status == WL_CONNECT_FAILED || status == WL_CONNECTION_LOST) { return WiFiSTAConnectStatus::ERROR_CONNECT_FAILED; } From df50e57409d80e8e43c9d0366ad4bbbdd42c07e1 Mon Sep 17 00:00:00 2001 From: Ryan Henderson Date: Sun, 12 Jan 2025 20:18:20 -0800 Subject: [PATCH 0870/1052] Include esp_mac.h and C++20 str_startswith/str_ends (#7999) --- esphome/core/helpers.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 2d2c88b844..439bb2ccb0 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -45,7 +45,9 @@ #endif #ifdef USE_ESP32 #include "esp32/rom/crc.h" - +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 2) +#include "esp_mac.h" +#endif #include "esp_efuse.h" #include "esp_efuse_table.h" #endif @@ -261,7 +263,7 @@ bool random_bytes(uint8_t *data, size_t len) { bool str_equals_case_insensitive(const std::string &a, const std::string &b) { return strcasecmp(a.c_str(), b.c_str()) == 0; } -#if ESP_IDF_VERSION_MAJOR >= 5 +#if __cplusplus >= 202002L bool str_startswith(const std::string &str, const std::string &start) { return str.starts_with(start); } bool str_endswith(const std::string &str, const std::string &end) { return str.ends_with(end); } #else From 13909b7994de2c25a62714bfc44c20b4fb845958 Mon Sep 17 00:00:00 2001 From: Ryan Henderson Date: Sun, 12 Jan 2025 20:26:23 -0800 Subject: [PATCH 0871/1052] [esp32_wifi] Enhance WiFi component with TCPIP core locking. (#7997) --- .../wifi/wifi_component_esp32_arduino.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/esphome/components/wifi/wifi_component_esp32_arduino.cpp b/esphome/components/wifi/wifi_component_esp32_arduino.cpp index 76d0b7d96c..b7a77fcdc9 100644 --- a/esphome/components/wifi/wifi_component_esp32_arduino.cpp +++ b/esphome/components/wifi/wifi_component_esp32_arduino.cpp @@ -20,6 +20,10 @@ #include "lwip/dns.h" #include "lwip/err.h" +#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING +#include "lwip/priv/tcpip_priv.h" +#endif + #include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/helpers.h" @@ -291,11 +295,26 @@ bool WiFiComponent::wifi_sta_ip_config_(optional manual_ip) { } if (!manual_ip.has_value()) { +// sntp_servermode_dhcp lwip/sntp.c (Required to lock TCPIP core functionality!) +// https://github.com/esphome/issues/issues/6591 +// https://github.com/espressif/arduino-esp32/issues/10526 +#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING + if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { + LOCK_TCPIP_CORE(); + } +#endif + // lwIP starts the SNTP client if it gets an SNTP server from DHCP. We don't need the time, and more importantly, // the built-in SNTP client has a memory leak in certain situations. Disable this feature. // https://github.com/esphome/issues/issues/2299 sntp_servermode_dhcp(false); +#ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING + if (sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { + UNLOCK_TCPIP_CORE(); + } +#endif + // No manual IP is set; use DHCP client if (dhcp_status != ESP_NETIF_DHCP_STARTED) { err = esp_netif_dhcpc_start(s_sta_netif); From 9874d17613147fd617841d7eae66505a5c024b56 Mon Sep 17 00:00:00 2001 From: NP v/d Spek Date: Mon, 13 Jan 2025 05:29:38 +0100 Subject: [PATCH 0872/1052] add missing include in base_automation.h (#8001) --- esphome/core/base_automation.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index dcf7da2f21..13179b90bb 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -2,6 +2,7 @@ #include "esphome/core/automation.h" #include "esphome/core/component.h" +#include "esphome/core/hal.h" #include "esphome/core/defines.h" #include "esphome/core/preferences.h" From 30bb806f26853e00c053f1fc69751a61f105b8ee Mon Sep 17 00:00:00 2001 From: Piotr Szulc Date: Mon, 13 Jan 2025 05:31:01 +0100 Subject: [PATCH 0873/1052] Fixed libretiny preference wrongly detecting change in the data to store (#7990) --- esphome/components/libretiny/preferences.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/libretiny/preferences.cpp b/esphome/components/libretiny/preferences.cpp index ceeb30baf5..a090f42aa7 100644 --- a/esphome/components/libretiny/preferences.cpp +++ b/esphome/components/libretiny/preferences.cpp @@ -147,7 +147,7 @@ class LibreTinyPreferences : public ESPPreferences { ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str()); return true; } - stored_data.data.reserve(kv.value_len); + stored_data.data.resize(kv.value_len); fdb_blob_make(&blob, stored_data.data.data(), kv.value_len); size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob); if (actual_len != kv.value_len) { From b4a2b50ee092abd0eabbbce6db0c8da1d825464e Mon Sep 17 00:00:00 2001 From: Dusan Cervenka Date: Mon, 13 Jan 2025 05:34:07 +0100 Subject: [PATCH 0874/1052] Fixed topic when mac is used (#7988) --- esphome/components/mqtt/__init__.py | 2 +- esphome/components/mqtt/mqtt_client.cpp | 8 +++++++- esphome/components/mqtt/mqtt_client.h | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 2b0d941220..e1002478a1 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -373,7 +373,7 @@ async def to_code(config): ) ) - cg.add(var.set_topic_prefix(config[CONF_TOPIC_PREFIX])) + cg.add(var.set_topic_prefix(config[CONF_TOPIC_PREFIX], CORE.name)) if config[CONF_USE_ABBREVIATIONS]: cg.add_define("USE_MQTT_ABBREVIATIONS") diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index c7ace505a8..9afa3a588d 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -606,7 +606,13 @@ void MQTTClientComponent::set_log_level(int level) { this->log_level_ = level; } void MQTTClientComponent::set_keep_alive(uint16_t keep_alive_s) { this->mqtt_backend_.set_keep_alive(keep_alive_s); } void MQTTClientComponent::set_log_message_template(MQTTMessage &&message) { this->log_message_ = std::move(message); } const MQTTDiscoveryInfo &MQTTClientComponent::get_discovery_info() const { return this->discovery_info_; } -void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix) { this->topic_prefix_ = topic_prefix; } +void MQTTClientComponent::set_topic_prefix(const std::string &topic_prefix, const std::string &check_topic_prefix) { + if (App.is_name_add_mac_suffix_enabled() && (topic_prefix == check_topic_prefix)) { + this->topic_prefix_ = str_sanitize(App.get_name()); + } else { + this->topic_prefix_ = topic_prefix; + } +} const std::string &MQTTClientComponent::get_topic_prefix() const { return this->topic_prefix_; } void MQTTClientComponent::set_publish_nan_as_none(bool publish_nan_as_none) { this->publish_nan_as_none_ = publish_nan_as_none; diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index 34eac29464..c68b3c62eb 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -165,7 +165,7 @@ class MQTTClientComponent : public Component { * * @param topic_prefix The topic prefix. The last "/" is appended automatically. */ - void set_topic_prefix(const std::string &topic_prefix); + void set_topic_prefix(const std::string &topic_prefix, const std::string &check_topic_prefix); /// Get the topic prefix of this device, using default if necessary const std::string &get_topic_prefix() const; From f319472066a45c11f4d71e96b39a201f07e7f0dd Mon Sep 17 00:00:00 2001 From: Nate Clark Date: Sun, 12 Jan 2025 23:35:29 -0500 Subject: [PATCH 0875/1052] web_server: Adds REST API POST endpoints to arm and disarm (#7985) --- esphome/components/web_server/web_server.cpp | 26 +++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 0467023039..ed0cb3db2c 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1415,6 +1415,30 @@ void WebServer::handle_alarm_control_panel_request(AsyncWebServerRequest *reques request->send(200, "application/json", data.c_str()); return; } + + auto call = obj->make_call(); + if (request->hasParam("code")) { + call.set_code(request->getParam("code")->value().c_str()); + } + + if (match.method == "disarm") { + call.disarm(); + } else if (match.method == "arm_away") { + call.arm_away(); + } else if (match.method == "arm_home") { + call.arm_home(); + } else if (match.method == "arm_night") { + call.arm_night(); + } else if (match.method == "arm_vacation") { + call.arm_vacation(); + } else { + request->send(404); + return; + } + + this->schedule_([call]() mutable { call.perform(); }); + request->send(200); + return; } request->send(404); } @@ -1664,7 +1688,7 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { #endif #ifdef USE_ALARM_CONTROL_PANEL - if (request->method() == HTTP_GET && match.domain == "alarm_control_panel") + if ((request->method() == HTTP_GET || request->method() == HTTP_POST) && match.domain == "alarm_control_panel") return true; #endif From 6262fb8fcf9538d161d94512a857755c839eb48d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 14 Jan 2025 08:32:54 +1100 Subject: [PATCH 0876/1052] [lvgl] fix tests (#8075) --- tests/components/lvgl/lvgl-package.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 234fd78678..7c59cfa171 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -795,6 +795,19 @@ lvgl: color: 0xA0A0A0 r_mod: -20 opa: 0% + - id: page3 + widgets: + - keyboard: + id: lv_keyboard + align: bottom_mid + on_value: + then: + - logger.log: + format: "keyboard value %s" + args: [text.c_str()] + - keyboard: + id: lv_keyboard1 + mode: special font: - file: "gfonts://Roboto" @@ -805,10 +818,13 @@ image: - id: cat_image resize: 256x48 file: $component_dir/logo-text.svg + type: RGB565 + use_transparency: alpha_channel - id: dog_image file: $component_dir/logo-text.svg resize: 256x48 - type: TRANSPARENT_BINARY + type: BINARY + use_transparency: chroma_key color: - id: light_blue From bdb1094b477baad40cfcf9d9bc86665c14069a55 Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Tue, 14 Jan 2025 04:20:52 +0100 Subject: [PATCH 0877/1052] Allow external libraries to use ESP_LOGx macros (#8078) --- esphome/components/lvgl/lvgl_esphome.cpp | 20 +++++--------------- esphome/core/log.h | 14 +++++++------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/esphome/components/lvgl/lvgl_esphome.cpp b/esphome/components/lvgl/lvgl_esphome.cpp index 5abeead9d8..a9fe56fb32 100644 --- a/esphome/components/lvgl/lvgl_esphome.cpp +++ b/esphome/components/lvgl/lvgl_esphome.cpp @@ -501,9 +501,7 @@ size_t lv_millis(void) { return esphome::millis(); } void *lv_custom_mem_alloc(size_t size) { auto *ptr = malloc(size); // NOLINT if (ptr == nullptr) { -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR - esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); -#endif + ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); } return ptr; } @@ -520,30 +518,22 @@ void *lv_custom_mem_alloc(size_t size) { ptr = heap_caps_malloc(size, cap_bits); } if (ptr == nullptr) { -#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR - esphome::ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); -#endif + ESP_LOGE(esphome::lvgl::TAG, "Failed to allocate %zu bytes", size); return nullptr; } -#ifdef ESPHOME_LOG_HAS_VERBOSE - esphome::ESP_LOGV(esphome::lvgl::TAG, "allocate %zu - > %p", size, ptr); -#endif + ESP_LOGV(esphome::lvgl::TAG, "allocate %zu - > %p", size, ptr); return ptr; } void lv_custom_mem_free(void *ptr) { -#ifdef ESPHOME_LOG_HAS_VERBOSE - esphome::ESP_LOGV(esphome::lvgl::TAG, "free %p", ptr); -#endif + ESP_LOGV(esphome::lvgl::TAG, "free %p", ptr); if (ptr == nullptr) return; heap_caps_free(ptr); } void *lv_custom_mem_realloc(void *ptr, size_t size) { -#ifdef ESPHOME_LOG_HAS_VERBOSE - esphome::ESP_LOGV(esphome::lvgl::TAG, "realloc %p: %zu", ptr, size); -#endif + ESP_LOGV(esphome::lvgl::TAG, "realloc %p: %zu", ptr, size); return heap_caps_realloc(ptr, size, cap_bits); } #endif diff --git a/esphome/core/log.h b/esphome/core/log.h index 86af534f98..99a68024c5 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -74,7 +74,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE #define esph_log_vv(tag, format, ...) \ - esp_log_printf_(ESPHOME_LOG_LEVEL_VERY_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) + ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_VERY_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) #define ESPHOME_LOG_HAS_VERY_VERBOSE #else @@ -83,7 +83,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE #define esph_log_v(tag, format, ...) \ - esp_log_printf_(ESPHOME_LOG_LEVEL_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) + ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_VERBOSE, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) #define ESPHOME_LOG_HAS_VERBOSE #else @@ -92,9 +92,9 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG #define esph_log_d(tag, format, ...) \ - esp_log_printf_(ESPHOME_LOG_LEVEL_DEBUG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) + ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_DEBUG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) #define esph_log_config(tag, format, ...) \ - esp_log_printf_(ESPHOME_LOG_LEVEL_CONFIG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) + ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_CONFIG, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) #define ESPHOME_LOG_HAS_DEBUG #define ESPHOME_LOG_HAS_CONFIG @@ -105,7 +105,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_INFO #define esph_log_i(tag, format, ...) \ - esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) + ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_INFO, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) #define ESPHOME_LOG_HAS_INFO #else @@ -114,7 +114,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_WARN #define esph_log_w(tag, format, ...) \ - esp_log_printf_(ESPHOME_LOG_LEVEL_WARN, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) + ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_WARN, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) #define ESPHOME_LOG_HAS_WARN #else @@ -123,7 +123,7 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR #define esph_log_e(tag, format, ...) \ - esp_log_printf_(ESPHOME_LOG_LEVEL_ERROR, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) + ::esphome::esp_log_printf_(ESPHOME_LOG_LEVEL_ERROR, tag, __LINE__, ESPHOME_LOG_FORMAT(format), ##__VA_ARGS__) #define ESPHOME_LOG_HAS_ERROR #else From fc2b15e307833359f079aec46ceffd30b9398abe Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:27:47 +1100 Subject: [PATCH 0878/1052] [uptime] Add text_sensor (#8028) --- .../uptime/{sensor.py => sensor/__init__.py} | 6 +-- .../{ => sensor}/uptime_seconds_sensor.cpp | 0 .../{ => sensor}/uptime_seconds_sensor.h | 0 .../{ => sensor}/uptime_timestamp_sensor.cpp | 0 .../{ => sensor}/uptime_timestamp_sensor.h | 0 .../components/uptime/text_sensor/__init__.py | 19 ++++++++ .../uptime/text_sensor/uptime_text_sensor.cpp | 46 +++++++++++++++++++ .../uptime/text_sensor/uptime_text_sensor.h | 25 ++++++++++ tests/components/uptime/common.yaml | 4 ++ 9 files changed, 97 insertions(+), 3 deletions(-) rename esphome/components/uptime/{sensor.py => sensor/__init__.py} (100%) rename esphome/components/uptime/{ => sensor}/uptime_seconds_sensor.cpp (100%) rename esphome/components/uptime/{ => sensor}/uptime_seconds_sensor.h (100%) rename esphome/components/uptime/{ => sensor}/uptime_timestamp_sensor.cpp (100%) rename esphome/components/uptime/{ => sensor}/uptime_timestamp_sensor.h (100%) create mode 100644 esphome/components/uptime/text_sensor/__init__.py create mode 100644 esphome/components/uptime/text_sensor/uptime_text_sensor.cpp create mode 100644 esphome/components/uptime/text_sensor/uptime_text_sensor.h diff --git a/esphome/components/uptime/sensor.py b/esphome/components/uptime/sensor/__init__.py similarity index 100% rename from esphome/components/uptime/sensor.py rename to esphome/components/uptime/sensor/__init__.py index 30220751b6..e2a7aee1a2 100644 --- a/esphome/components/uptime/sensor.py +++ b/esphome/components/uptime/sensor/__init__.py @@ -1,14 +1,14 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import sensor, time +import esphome.config_validation as cv from esphome.const import ( CONF_TIME_ID, + DEVICE_CLASS_DURATION, DEVICE_CLASS_TIMESTAMP, ENTITY_CATEGORY_DIAGNOSTIC, + ICON_TIMER, STATE_CLASS_TOTAL_INCREASING, UNIT_SECOND, - ICON_TIMER, - DEVICE_CLASS_DURATION, ) uptime_ns = cg.esphome_ns.namespace("uptime") diff --git a/esphome/components/uptime/uptime_seconds_sensor.cpp b/esphome/components/uptime/sensor/uptime_seconds_sensor.cpp similarity index 100% rename from esphome/components/uptime/uptime_seconds_sensor.cpp rename to esphome/components/uptime/sensor/uptime_seconds_sensor.cpp diff --git a/esphome/components/uptime/uptime_seconds_sensor.h b/esphome/components/uptime/sensor/uptime_seconds_sensor.h similarity index 100% rename from esphome/components/uptime/uptime_seconds_sensor.h rename to esphome/components/uptime/sensor/uptime_seconds_sensor.h diff --git a/esphome/components/uptime/uptime_timestamp_sensor.cpp b/esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp similarity index 100% rename from esphome/components/uptime/uptime_timestamp_sensor.cpp rename to esphome/components/uptime/sensor/uptime_timestamp_sensor.cpp diff --git a/esphome/components/uptime/uptime_timestamp_sensor.h b/esphome/components/uptime/sensor/uptime_timestamp_sensor.h similarity index 100% rename from esphome/components/uptime/uptime_timestamp_sensor.h rename to esphome/components/uptime/sensor/uptime_timestamp_sensor.h diff --git a/esphome/components/uptime/text_sensor/__init__.py b/esphome/components/uptime/text_sensor/__init__.py new file mode 100644 index 0000000000..996d983e71 --- /dev/null +++ b/esphome/components/uptime/text_sensor/__init__.py @@ -0,0 +1,19 @@ +import esphome.codegen as cg +from esphome.components import text_sensor +import esphome.config_validation as cv +from esphome.const import ENTITY_CATEGORY_DIAGNOSTIC, ICON_TIMER + +uptime_ns = cg.esphome_ns.namespace("uptime") +UptimeTextSensor = uptime_ns.class_( + "UptimeTextSensor", text_sensor.TextSensor, cg.PollingComponent +) +CONFIG_SCHEMA = text_sensor.text_sensor_schema( + UptimeTextSensor, + icon=ICON_TIMER, + entity_category=ENTITY_CATEGORY_DIAGNOSTIC, +).extend(cv.polling_component_schema("60s")) + + +async def to_code(config): + var = await text_sensor.new_text_sensor(config) + await cg.register_component(var, config) diff --git a/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp b/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp new file mode 100644 index 0000000000..0fa5e199f3 --- /dev/null +++ b/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp @@ -0,0 +1,46 @@ +#include "uptime_text_sensor.h" + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace uptime { + +static const char *const TAG = "uptime.sensor"; + +void UptimeTextSensor::setup() { this->last_ms_ = millis(); } + +void UptimeTextSensor::update() { + const auto now = millis(); + // get whole seconds since last update. Note that even if the millis count has overflowed between updates, + // the difference will still be correct due to the way twos-complement arithmetic works. + const uint32_t delta = (now - this->last_ms_) / 1000; + if (delta == 0) + return; + // set last_ms_ to the last second boundary + this->last_ms_ = now - (now % 1000); + this->uptime_ += delta; + auto uptime = this->uptime_; + unsigned days = uptime / (24 * 3600); + unsigned seconds = uptime % (24 * 3600); + unsigned hours = seconds / 3600; + seconds %= 3600; + unsigned minutes = seconds / 60; + seconds %= 60; + if (days != 0) { + this->publish_state(str_sprintf("%dd%dh%dm%ds", days, hours, minutes, seconds)); + } else if (hours != 0) { + this->publish_state(str_sprintf("%dh%dm%ds", hours, minutes, seconds)); + } else if (minutes != 0) { + this->publish_state(str_sprintf("%dm%ds", minutes, seconds)); + } else { + this->publish_state(str_sprintf("%ds", seconds)); + } +} + +float UptimeTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; } +void UptimeTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Uptime Text Sensor", this); } + +} // namespace uptime +} // namespace esphome diff --git a/esphome/components/uptime/text_sensor/uptime_text_sensor.h b/esphome/components/uptime/text_sensor/uptime_text_sensor.h new file mode 100644 index 0000000000..4baf1039b6 --- /dev/null +++ b/esphome/components/uptime/text_sensor/uptime_text_sensor.h @@ -0,0 +1,25 @@ +#pragma once + +#include "esphome/core/defines.h" + +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace uptime { + +class UptimeTextSensor : public text_sensor::TextSensor, public PollingComponent { + public: + void update() override; + void dump_config() override; + void setup() override; + + float get_setup_priority() const override; + + protected: + uint64_t uptime_{0}; + uint64_t last_ms_{0}; +}; + +} // namespace uptime +} // namespace esphome diff --git a/tests/components/uptime/common.yaml b/tests/components/uptime/common.yaml index f63f80b050..d78ef8eca9 100644 --- a/tests/components/uptime/common.yaml +++ b/tests/components/uptime/common.yaml @@ -13,3 +13,7 @@ sensor: - platform: uptime name: Uptime Sensor Timestamp type: timestamp + +text_sensor: + - platform: uptime + name: Uptime Text From c3412df169a0fc742b5bf854245d6a07adef836d Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:29:27 +1100 Subject: [PATCH 0879/1052] [image] Fix mdi images (#8082) --- esphome/components/image/__init__.py | 39 ++++++++++++++++++++++------ esphome/components/image/image.cpp | 35 +++++++++++++++++++++---- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index 801b05e160..a503e8f471 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -82,11 +82,13 @@ class ImageEncoder: self.dither = dither self.index = 0 self.invert_alpha = invert_alpha + self.path = "" - def convert(self, image): + def convert(self, image, path): """ Convert the image format :param image: Input image + :param path: Path to the image file :return: converted image """ return image @@ -103,6 +105,16 @@ class ImageEncoder: """ +def is_alpha_only(image: Image): + """ + Check if an image (assumed to be RGBA) is only alpha + """ + # Any alpha data? + if image.split()[-1].getextrema()[0] == 0xFF: + return False + return all(b.getextrema()[1] == 0 for b in image.split()[:-1]) + + class ImageBinary(ImageEncoder): allow_config = {CONF_OPAQUE, CONF_INVERT_ALPHA, CONF_CHROMA_KEY} @@ -111,7 +123,9 @@ class ImageBinary(ImageEncoder): super().__init__(self.width8, height, transparency, dither, invert_alpha) self.bitno = 0 - def convert(self, image): + def convert(self, image, path): + if is_alpha_only(image): + image = image.split()[-1] return image.convert("1", dither=self.dither) def encode(self, pixel): @@ -136,7 +150,16 @@ class ImageBinary(ImageEncoder): class ImageGrayscale(ImageEncoder): allow_config = {CONF_ALPHA_CHANNEL, CONF_CHROMA_KEY, CONF_INVERT_ALPHA, CONF_OPAQUE} - def convert(self, image): + def convert(self, image, path): + if is_alpha_only(image): + if self.transparency != CONF_ALPHA_CHANNEL: + _LOGGER.warning( + "Grayscale image %s is alpha only, but transparency is set to %s", + path, + self.transparency, + ) + self.transparency = CONF_ALPHA_CHANNEL + image = image.split()[-1] return image.convert("LA") def encode(self, pixel): @@ -166,7 +189,7 @@ class ImageRGB565(ImageEncoder): invert_alpha, ) - def convert(self, image): + def convert(self, image, path): return image.convert("RGBA") def encode(self, pixel): @@ -204,7 +227,7 @@ class ImageRGB(ImageEncoder): invert_alpha, ) - def convert(self, image): + def convert(self, image, path): return image.convert("RGBA") def encode(self, pixel): @@ -308,7 +331,7 @@ def is_svg_file(file): if not file: return False with open(file, "rb") as f: - return "get_grayscale_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); + const uint32_t pos = (img_x + img_y * this->width_); + const uint8_t gray = progmem_read_byte(this->data_start_ + pos); + Color color = Color(gray, gray, gray, 0xFF); + switch (this->transparency_) { + case TRANSPARENCY_CHROMA_KEY: + if (gray == 1) { + continue; // skip drawing + } + break; + case TRANSPARENCY_ALPHA_CHANNEL: { + auto on = (float) gray / 255.0f; + auto off = 1.0f - on; + // blend color_on and color_off + color = Color(color_on.r * on + color_off.r * off, color_on.g * on + color_off.g * off, + color_on.b * on + color_off.b * off, 0xFF); + break; + } + default: + break; } + display->draw_pixel_at(x + img_x, y + img_y, color); } } break; @@ -179,8 +196,16 @@ Color Image::get_rgb565_pixel_(int x, int y) const { Color Image::get_grayscale_pixel_(int x, int y) const { const uint32_t pos = (x + y * this->width_); const uint8_t gray = progmem_read_byte(this->data_start_ + pos); - uint8_t alpha = (gray == 1 && this->transparency_ == TRANSPARENCY_CHROMA_KEY) ? 0 : 0xFF; - return Color(gray, gray, gray, alpha); + switch (this->transparency_) { + case TRANSPARENCY_CHROMA_KEY: + if (gray == 1) + return Color(0, 0, 0, 0); + return Color(gray, gray, gray, 0xFF); + case TRANSPARENCY_ALPHA_CHANNEL: + return Color(0, 0, 0, gray); + default: + return Color(gray, gray, gray, 0xFF); + } } int Image::get_width() const { return this->width_; } int Image::get_height() const { return this->height_; } From e8d2ad4ce856ab2e2ae9b156c6f0cbe10f069ddf Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:53:44 +1100 Subject: [PATCH 0880/1052] [ili9xxx] psram and 8 bit changes (#8084) --- esphome/components/ili9xxx/display.py | 40 ++++++++++++++----- .../components/ili9xxx/ili9xxx_display.cpp | 7 +--- esphome/components/ili9xxx/ili9xxx_display.h | 3 +- tests/components/ili9xxx/test.esp32-ard.yaml | 1 + 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py index 3c9dd2dab9..e3abb7e98c 100644 --- a/esphome/components/ili9xxx/display.py +++ b/esphome/components/ili9xxx/display.py @@ -1,9 +1,12 @@ +import logging + from esphome import core, pins import esphome.codegen as cg from esphome.components import display, spi from esphome.components.display import validate_rotation import esphome.config_validation as cv from esphome.const import ( + CONF_AUTO_CLEAR_ENABLED, CONF_COLOR_ORDER, CONF_COLOR_PALETTE, CONF_DC_PIN, @@ -27,17 +30,12 @@ from esphome.const import ( CONF_WIDTH, ) from esphome.core import CORE, HexInt +from esphome.final_validate import full_config DEPENDENCIES = ["spi"] - -def AUTO_LOAD(): - if CORE.is_esp32: - return ["psram"] - return [] - - CODEOWNERS = ["@nielsnl68", "@clydebarrow"] +LOGGER = logging.getLogger(__name__) ili9xxx_ns = cg.esphome_ns.namespace("ili9xxx") ILI9XXXDisplay = ili9xxx_ns.class_( @@ -84,7 +82,7 @@ COLOR_ORDERS = { "BGR": ColorOrder.COLOR_ORDER_BGR, } -COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") +COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE", "8BIT", upper=True) CONF_LED_PIN = "led_pin" CONF_COLOR_PALETTE_IMAGES = "color_palette_images" @@ -195,9 +193,27 @@ CONFIG_SCHEMA = cv.All( _validate, ) -FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema( - "ili9xxx", require_miso=False, require_mosi=True -) + +def final_validate(config): + global_config = full_config.get() + # Ideally would calculate buffer size here, but that info is not available on the Python side + needs_buffer = ( + CONF_LAMBDA in config or CONF_PAGES in config or config[CONF_AUTO_CLEAR_ENABLED] + ) + if ( + CORE.is_esp32 + and config[CONF_COLOR_PALETTE] == "NONE" + and "psram" not in global_config + and needs_buffer + ): + LOGGER.info("Consider enabling PSRAM if available for the display buffer") + + return spi.final_validate_device_schema( + "ili9xxx", require_miso=False, require_mosi=True + ) + + +FINAL_VALIDATE_SCHEMA = final_validate async def to_code(config): @@ -283,6 +299,8 @@ async def to_code(config): palette = converted.getpalette() assert len(palette) == 256 * 3 rhs = palette + elif config[CONF_COLOR_PALETTE] == "8BIT": + cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8)) else: cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_16)) diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index b9664067a9..f056f0a128 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -66,12 +66,9 @@ void ILI9XXXDisplay::setup() { void ILI9XXXDisplay::alloc_buffer_() { if (this->buffer_color_mode_ == BITS_16) { this->init_internal_(this->get_buffer_length_() * 2); - if (this->buffer_ != nullptr) { - return; - } - this->buffer_color_mode_ = BITS_8; + } else { + this->init_internal_(this->get_buffer_length_()); } - this->init_internal_(this->get_buffer_length_()); if (this->buffer_ == nullptr) { this->mark_failed(); } diff --git a/esphome/components/ili9xxx/ili9xxx_display.h b/esphome/components/ili9xxx/ili9xxx_display.h index c141739d2a..87d7c86e5c 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -98,7 +98,8 @@ class ILI9XXXDisplay : public display::DisplayBuffer, protected: inline bool check_buffer_() { if (this->buffer_ == nullptr) { - this->alloc_buffer_(); + if (!this->is_failed()) + this->alloc_buffer_(); return !this->is_failed(); } return true; diff --git a/tests/components/ili9xxx/test.esp32-ard.yaml b/tests/components/ili9xxx/test.esp32-ard.yaml index 850273230a..c00c38ce3e 100644 --- a/tests/components/ili9xxx/test.esp32-ard.yaml +++ b/tests/components/ili9xxx/test.esp32-ard.yaml @@ -20,6 +20,7 @@ display: it.rectangle(0, 0, it.get_width(), it.get_height()); - platform: ili9xxx invert_colors: false + color_palette: 8bit dimensions: width: 320 height: 240 From dac9768f6ac897d46b093ecee81ee64101b29b59 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:56:52 +1100 Subject: [PATCH 0881/1052] [spi] Restore ``SPIDelegateDummy`` (#8019) --- esphome/components/spi/spi.cpp | 6 ++++++ esphome/components/spi/spi.h | 21 +++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index f9435b0424..b13826c443 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -7,6 +7,10 @@ namespace spi { const char *const TAG = "spi"; +SPIDelegate *const SPIDelegate::NULL_DELEGATE = // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + new SPIDelegateDummy(); +// https://bugs.llvm.org/show_bug.cgi?id=48040 + bool SPIDelegate::is_ready() { return true; } GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) @@ -75,6 +79,8 @@ void SPIComponent::dump_config() { } } +void SPIDelegateDummy::begin_transaction() { ESP_LOGE(TAG, "SPIDevice not initialised - did you call spi_setup()?"); } + uint8_t SPIDelegateBitBash::transfer(uint8_t data) { return this->transfer_(data, 8); } void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_(data, num_bits); } diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index 4cd8d3383c..f581dc3f56 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -163,6 +163,8 @@ class Utility { } }; +class SPIDelegateDummy; + // represents a device attached to an SPI bus, with a defined clock rate, mode and bit order. On Arduino this is // a thin wrapper over SPIClass. class SPIDelegate { @@ -248,6 +250,21 @@ class SPIDelegate { uint32_t data_rate_{1000000}; SPIMode mode_{MODE0}; GPIOPin *cs_pin_{NullPin::NULL_PIN}; + static SPIDelegate *const NULL_DELEGATE; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +}; + +/** + * A dummy SPIDelegate that complains if it's used. + */ + +class SPIDelegateDummy : public SPIDelegate { + public: + SPIDelegateDummy() = default; + + uint8_t transfer(uint8_t data) override { return 0; } + void end_transaction() override{}; + + void begin_transaction() override; }; /** @@ -365,7 +382,7 @@ class SPIClient { virtual void spi_teardown() { this->parent_->unregister_device(this); - this->delegate_ = nullptr; + this->delegate_ = SPIDelegate::NULL_DELEGATE; } bool spi_is_ready() { return this->delegate_->is_ready(); } @@ -376,7 +393,7 @@ class SPIClient { uint32_t data_rate_{1000000}; SPIComponent *parent_{nullptr}; GPIOPin *cs_{nullptr}; - SPIDelegate *delegate_{nullptr}; + SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE}; }; /** From 17b88f2e3e5d873b1961b40e4c1fb6acccfc1305 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 15 Jan 2025 12:29:51 +1100 Subject: [PATCH 0882/1052] [lvgl] fix lvgl.widget.update and friends (#8087) --- esphome/components/lvgl/automation.py | 15 +++++++++++- esphome/components/lvgl/schemas.py | 27 ++++++++++++++------- esphome/components/lvgl/widgets/dropdown.py | 2 +- esphome/components/lvgl/widgets/keyboard.py | 9 ++++++- esphome/components/lvgl/widgets/msgbox.py | 2 +- esphome/components/lvgl/widgets/obj.py | 13 +--------- esphome/components/lvgl/widgets/tabview.py | 2 +- tests/components/lvgl/lvgl-package.yaml | 15 ++++++++++++ 8 files changed, 59 insertions(+), 26 deletions(-) diff --git a/esphome/components/lvgl/automation.py b/esphome/components/lvgl/automation.py index 7db6e1f045..168fc03cb7 100644 --- a/esphome/components/lvgl/automation.py +++ b/esphome/components/lvgl/automation.py @@ -15,6 +15,7 @@ from .defines import ( CONF_FREEZE, CONF_LVGL_ID, CONF_SHOW_SNOW, + PARTS, literal, ) from .lv_validation import lv_bool, lv_color, lv_image, opacity @@ -33,7 +34,7 @@ from .lvcode import ( lvgl_comp, static_cast, ) -from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA +from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA, base_update_schema from .types import ( LV_STATE, LvglAction, @@ -41,6 +42,7 @@ from .types import ( ObjUpdateAction, lv_disp_t, lv_group_t, + lv_obj_base_t, lv_obj_t, lv_pseudo_button_t, ) @@ -336,3 +338,14 @@ async def widget_focus(config, action_id, template_arg, args): lv.group_focus_freeze(group, True) var = cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) return var + + +@automation.register_action( + "lvgl.widget.update", ObjUpdateAction, base_update_schema(lv_obj_base_t, PARTS) +) +async def obj_update_to_code(config, action_id, template_arg, args): + async def do_update(widget: Widget): + await set_obj_properties(widget, config) + + widgets = await get_widgets(config[CONF_ID]) + return await action_to_code(widgets, do_update, action_id, template_arg, args) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index 271dbea19f..f0318dd17a 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -199,13 +199,12 @@ FLAG_SCHEMA = cv.Schema({cv.Optional(flag): lvalid.lv_bool for flag in df.OBJ_FL FLAG_LIST = cv.ensure_list(df.LvConstant("LV_OBJ_FLAG_", *df.OBJ_FLAGS).one_of) -def part_schema(widget_type: WidgetType): +def part_schema(parts): """ Generate a schema for the various parts (e.g. main:, indicator:) of a widget type - :param widget_type: The type of widget to generate for - :return: + :param parts: The parts to include in the schema + :return: The schema """ - parts = widget_type.parts return cv.Schema({cv.Optional(part): STATE_SCHEMA for part in parts}).extend( STATE_SCHEMA ) @@ -228,9 +227,15 @@ def automation_schema(typ: LvType): } -def create_modify_schema(widget_type): +def base_update_schema(widget_type, parts): + """ + Create a schema for updating a widgets style properties, states and flags + :param widget_type: The type of the ID + :param parts: The allowable parts to specify + :return: + """ return ( - part_schema(widget_type) + part_schema(parts) .extend( { cv.Required(CONF_ID): cv.ensure_list( @@ -245,7 +250,12 @@ def create_modify_schema(widget_type): } ) .extend(FLAG_SCHEMA) - .extend(widget_type.modify_schema) + ) + + +def create_modify_schema(widget_type): + return base_update_schema(widget_type.w_type, widget_type.parts).extend( + widget_type.modify_schema ) @@ -256,7 +266,7 @@ def obj_schema(widget_type: WidgetType): :return: """ return ( - part_schema(widget_type) + part_schema(widget_type.parts) .extend(FLAG_SCHEMA) .extend(LAYOUT_SCHEMA) .extend(ALIGN_TO_SCHEMA) @@ -341,7 +351,6 @@ FLEX_OBJ_SCHEMA = { cv.Optional(df.CONF_FLEX_GROW): cv.int_, } - DISP_BG_SCHEMA = cv.Schema( { cv.Optional(df.CONF_DISP_BG_IMAGE): cv.Any( diff --git a/esphome/components/lvgl/widgets/dropdown.py b/esphome/components/lvgl/widgets/dropdown.py index a6bfc6bb88..b32b5a2b2e 100644 --- a/esphome/components/lvgl/widgets/dropdown.py +++ b/esphome/components/lvgl/widgets/dropdown.py @@ -37,7 +37,7 @@ DROPDOWN_BASE_SCHEMA = cv.Schema( cv.Exclusive(CONF_SELECTED_INDEX, CONF_SELECTED_TEXT): lv_int, cv.Exclusive(CONF_SELECTED_TEXT, CONF_SELECTED_TEXT): lv_text, cv.Optional(CONF_DIR, default="BOTTOM"): DIRECTIONS.one_of, - cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec), + cv.Optional(CONF_DROPDOWN_LIST): part_schema(dropdown_list_spec.parts), } ) diff --git a/esphome/components/lvgl/widgets/keyboard.py b/esphome/components/lvgl/widgets/keyboard.py index ba7edb302e..d4a71078d0 100644 --- a/esphome/components/lvgl/widgets/keyboard.py +++ b/esphome/components/lvgl/widgets/keyboard.py @@ -16,6 +16,11 @@ KEYBOARD_SCHEMA = { cv.Optional(CONF_TEXTAREA): cv.use_id(lv_textarea_t), } +KEYBOARD_MODIFY_SCHEMA = { + cv.Optional(CONF_MODE): KEYBOARD_MODES.one_of, + cv.Optional(CONF_TEXTAREA): cv.use_id(lv_textarea_t), +} + lv_keyboard_t = LvType( "LvKeyboardType", parents=(KeyProvider, LvCompound), @@ -32,6 +37,7 @@ class KeyboardType(WidgetType): lv_keyboard_t, (CONF_MAIN, CONF_ITEMS), KEYBOARD_SCHEMA, + modify_schema=KEYBOARD_MODIFY_SCHEMA, ) def get_uses(self): @@ -41,7 +47,8 @@ class KeyboardType(WidgetType): lvgl_components_required.add("KEY_LISTENER") lvgl_components_required.add(CONF_KEYBOARD) add_lv_use("btnmatrix") - await w.set_property(CONF_MODE, await KEYBOARD_MODES.process(config[CONF_MODE])) + if mode := config.get(CONF_MODE): + await w.set_property(CONF_MODE, await KEYBOARD_MODES.process(mode)) if ta := await get_widgets(config, CONF_TEXTAREA): await w.set_property(CONF_TEXTAREA, ta[0].obj) diff --git a/esphome/components/lvgl/widgets/msgbox.py b/esphome/components/lvgl/widgets/msgbox.py index c3393940b6..82b2442378 100644 --- a/esphome/components/lvgl/widgets/msgbox.py +++ b/esphome/components/lvgl/widgets/msgbox.py @@ -51,7 +51,7 @@ MSGBOX_SCHEMA = container_schema( cv.Required(CONF_TITLE): STYLED_TEXT_SCHEMA, cv.Optional(CONF_BODY, default=""): STYLED_TEXT_SCHEMA, cv.Optional(CONF_BUTTONS): cv.ensure_list(BUTTONMATRIX_BUTTON_SCHEMA), - cv.Optional(CONF_BUTTON_STYLE): part_schema(buttonmatrix_spec), + cv.Optional(CONF_BUTTON_STYLE): part_schema(buttonmatrix_spec.parts), cv.Optional(CONF_CLOSE_BUTTON, default=True): lv_bool, cv.GenerateID(CONF_BUTTON_TEXT_LIST_ID): cv.declare_id(char_ptr), } diff --git a/esphome/components/lvgl/widgets/obj.py b/esphome/components/lvgl/widgets/obj.py index afb4c97f33..ab22a5ce86 100644 --- a/esphome/components/lvgl/widgets/obj.py +++ b/esphome/components/lvgl/widgets/obj.py @@ -1,9 +1,5 @@ -from esphome import automation - -from ..automation import update_to_code from ..defines import CONF_MAIN, CONF_OBJ, CONF_SCROLLBAR -from ..schemas import create_modify_schema -from ..types import ObjUpdateAction, WidgetType, lv_obj_t +from ..types import WidgetType, lv_obj_t class ObjType(WidgetType): @@ -21,10 +17,3 @@ class ObjType(WidgetType): obj_spec = ObjType() - - -@automation.register_action( - "lvgl.widget.update", ObjUpdateAction, create_modify_schema(obj_spec) -) -async def obj_update_to_code(config, action_id, template_arg, args): - return await update_to_code(config, action_id, template_arg, args) diff --git a/esphome/components/lvgl/widgets/tabview.py b/esphome/components/lvgl/widgets/tabview.py index 226fc3f286..1d18ddd259 100644 --- a/esphome/components/lvgl/widgets/tabview.py +++ b/esphome/components/lvgl/widgets/tabview.py @@ -38,7 +38,7 @@ TABVIEW_SCHEMA = cv.Schema( }, ) ), - cv.Optional(CONF_TAB_STYLE): part_schema(buttonmatrix_spec), + cv.Optional(CONF_TAB_STYLE): part_schema(buttonmatrix_spec.parts), cv.Optional(CONF_POSITION, default="top"): DIRECTIONS.one_of, cv.Optional(CONF_SIZE, default="10%"): size, } diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index 7c59cfa171..b3227bb96e 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -805,9 +805,24 @@ lvgl: - logger.log: format: "keyboard value %s" args: [text.c_str()] + - lvgl.keyboard.update: + id: lv_keyboard + hidden: true + on_ready: + - lvgl.widget.update: + id: lv_keyboard + - lvgl.keyboard.update: + id: lv_keyboard + hidden: true + - keyboard: id: lv_keyboard1 mode: special + on_ready: + lvgl.keyboard.update: + id: lv_keyboard1 + hidden: true + mode: text_lower font: - file: "gfonts://Roboto" From c43d8460bdbc8d2e3c77986e55dbdc7deee99184 Mon Sep 17 00:00:00 2001 From: Saninn Salas Diaz <5490201+distante@users.noreply.github.com> Date: Wed, 15 Jan 2025 03:14:58 +0100 Subject: [PATCH 0883/1052] fix(web_server/fan): send speed update values even when fan is off (#8086) --- esphome/components/web_server/web_server.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index ed0cb3db2c..8c09d607a7 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -455,8 +455,9 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc } else if (match.method == "toggle") { this->schedule_([obj]() { obj->toggle().perform(); }); request->send(200); - } else if (match.method == "turn_on") { - auto call = obj->turn_on(); + } else if (match.method == "turn_on" || match.method == "turn_off") { + auto call = match.method == "turn_on" ? obj->turn_on() : obj->turn_off(); + if (request->hasParam("speed_level")) { auto speed_level = request->getParam("speed_level")->value(); auto val = parse_number(speed_level.c_str()); @@ -486,9 +487,6 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc } this->schedule_([call]() mutable { call.perform(); }); request->send(200); - } else if (match.method == "turn_off") { - this->schedule_([obj]() { obj->turn_off().perform(); }); - request->send(200); } else { request->send(404); } From 98817a5bbfacced643d1046135eea26099a0ac36 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 14 Jan 2025 21:47:22 -0600 Subject: [PATCH 0884/1052] [es7210] add support for es7210 ADC (#8007) --- CODEOWNERS | 1 + esphome/components/es7210/__init__.py | 67 ++++++ esphome/components/es7210/es7210.cpp | 201 ++++++++++++++++++ esphome/components/es7210/es7210.h | 69 ++++++ esphome/components/es7210/es7210_const.h | 126 +++++++++++ esphome/components/es8311/audio_dac.py | 3 +- esphome/const.py | 1 + tests/components/es7210/common.yaml | 6 + tests/components/es7210/test.esp32-ard.yaml | 5 + .../components/es7210/test.esp32-c3-ard.yaml | 5 + .../components/es7210/test.esp32-c3-idf.yaml | 5 + tests/components/es7210/test.esp32-idf.yaml | 5 + 12 files changed, 492 insertions(+), 2 deletions(-) create mode 100644 esphome/components/es7210/__init__.py create mode 100644 esphome/components/es7210/es7210.cpp create mode 100644 esphome/components/es7210/es7210.h create mode 100644 esphome/components/es7210/es7210_const.h create mode 100644 tests/components/es7210/common.yaml create mode 100644 tests/components/es7210/test.esp32-ard.yaml create mode 100644 tests/components/es7210/test.esp32-c3-ard.yaml create mode 100644 tests/components/es7210/test.esp32-c3-idf.yaml create mode 100644 tests/components/es7210/test.esp32-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 088e350f5d..ba7106e6a3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -131,6 +131,7 @@ esphome/components/ens160_base/* @latonita @vincentscode esphome/components/ens160_i2c/* @latonita esphome/components/ens160_spi/* @latonita esphome/components/ens210/* @itn3rd77 +esphome/components/es7210/* @kahrendt esphome/components/es8311/* @kahrendt @kroimon esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @Rapsssito @jesserockz diff --git a/esphome/components/es7210/__init__.py b/esphome/components/es7210/__init__.py new file mode 100644 index 0000000000..8e63d7f04f --- /dev/null +++ b/esphome/components/es7210/__init__.py @@ -0,0 +1,67 @@ +import esphome.codegen as cg +from esphome.components import i2c +import esphome.config_validation as cv +from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE + +CODEOWNERS = ["@kahrendt"] +DEPENDENCIES = ["i2c"] + +es7210_ns = cg.esphome_ns.namespace("es7210") +ES7210 = es7210_ns.class_("ES7210", cg.Component, i2c.I2CDevice) + + +es7210_bits_per_sample = es7210_ns.enum("ES7210BitsPerSample") +ES7210_BITS_PER_SAMPLE_ENUM = { + 16: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_16, + 24: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_24, + 32: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_32, +} + + +es7210_mic_gain = es7210_ns.enum("ES7210MicGain") +ES7210_MIC_GAIN_ENUM = { + "0DB": es7210_mic_gain.ES7210_MIC_GAIN_0DB, + "3DB": es7210_mic_gain.ES7210_MIC_GAIN_3DB, + "6DB": es7210_mic_gain.ES7210_MIC_GAIN_6DB, + "9DB": es7210_mic_gain.ES7210_MIC_GAIN_9DB, + "12DB": es7210_mic_gain.ES7210_MIC_GAIN_12DB, + "15DB": es7210_mic_gain.ES7210_MIC_GAIN_15DB, + "18DB": es7210_mic_gain.ES7210_MIC_GAIN_18DB, + "21DB": es7210_mic_gain.ES7210_MIC_GAIN_21DB, + "24DB": es7210_mic_gain.ES7210_MIC_GAIN_24DB, + "27DB": es7210_mic_gain.ES7210_MIC_GAIN_27DB, + "30DB": es7210_mic_gain.ES7210_MIC_GAIN_30DB, + "33DB": es7210_mic_gain.ES7210_MIC_GAIN_33DB, + "34.5DB": es7210_mic_gain.ES7210_MIC_GAIN_34_5DB, + "36DB": es7210_mic_gain.ES7210_MIC_GAIN_36DB, + "37.5DB": es7210_mic_gain.ES7210_MIC_GAIN_37_5DB, +} + +_validate_bits = cv.float_with_unit("bits", "bit") + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ES7210), + cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All( + _validate_bits, cv.enum(ES7210_BITS_PER_SAMPLE_ENUM) + ), + cv.Optional(CONF_MIC_GAIN, default="24DB"): cv.enum( + ES7210_MIC_GAIN_ENUM, upper=True + ), + cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x40)) +) + + +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_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) + cg.add(var.set_mic_gain(config[CONF_MIC_GAIN])) + cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) diff --git a/esphome/components/es7210/es7210.cpp b/esphome/components/es7210/es7210.cpp new file mode 100644 index 0000000000..d2f2c3c1ff --- /dev/null +++ b/esphome/components/es7210/es7210.cpp @@ -0,0 +1,201 @@ +#include "es7210.h" +#include "es7210_const.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace es7210 { + +static const char *const TAG = "es7210"; + +static const size_t MCLK_DIV_FRE = 256; + +// Mark the component as failed; use only in setup +#define ES7210_ERROR_FAILED(func) \ + if (!(func)) { \ + this->mark_failed(); \ + return; \ + } + +// Return false; use outside of setup +#define ES7210_ERROR_CHECK(func) \ + if (!(func)) { \ + return false; \ + } + +void ES7210::dump_config() { + ESP_LOGCONFIG(TAG, "ES7210 ADC:"); + ESP_LOGCONFIG(TAG, " Bits Per Sample: %" PRIu8, this->bits_per_sample_); + ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_rate_); + + if (this->is_failed()) { + ESP_LOGCONFIG(TAG, " Failed to initialize!"); + return; + } +} + +void ES7210::setup() { + ESP_LOGCONFIG(TAG, "Setting up ES7210..."); + + // Software reset + ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0xff)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x32)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_CLOCK_OFF_REG01, 0x3f)); + + // Set initialization time when device powers up + ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL0_REG09, 0x30)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_TIME_CONTROL1_REG0A, 0x30)); + + // Configure HFP for all ADC channels + ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF2_REG23, 0x2a)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC12_HPF1_REG22, 0x0a)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF2_REG20, 0x0a)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_ADC34_HPF1_REG21, 0x2a)); + + // Secondary I2S mode settings + ES7210_ERROR_FAILED(this->es7210_update_reg_bit_(ES7210_MODE_CONFIG_REG08, 0x01, 0x00)); + + // Configure analog power + ES7210_ERROR_FAILED(this->write_byte(ES7210_ANALOG_REG40, 0xC3)); + + // Set mic bias + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_BIAS_REG41, 0x70)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_BIAS_REG42, 0x70)); + + // Configure I2S settings, sample rate, and microphone gains + ES7210_ERROR_FAILED(this->configure_i2s_format_()); + ES7210_ERROR_FAILED(this->configure_sample_rate_()); + ES7210_ERROR_FAILED(this->configure_mic_gain_()); + + // Power on mics 1 through 4 + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC1_POWER_REG47, 0x08)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC2_POWER_REG48, 0x08)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC3_POWER_REG49, 0x08)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC4_POWER_REG4A, 0x08)); + + // Power down DLL + ES7210_ERROR_FAILED(this->write_byte(ES7210_POWER_DOWN_REG06, 0x04)); + + // Power on MIC1-4 bias & ADC1-4 & PGA1-4 Power + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x0F)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x0F)); + + // Enable device + ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x71)); + ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x41)); +} + +bool ES7210::configure_sample_rate_() { + int mclk_fre = this->sample_rate_ * MCLK_DIV_FRE; + int coeff = -1; + + for (int i = 0; i < (sizeof(ES7210_COEFFICIENTS) / sizeof(ES7210_COEFFICIENTS[0])); ++i) { + if (ES7210_COEFFICIENTS[i].lrclk == this->sample_rate_ && ES7210_COEFFICIENTS[i].mclk == mclk_fre) + coeff = i; + } + + if (coeff >= 0) { + // Set adc_div & doubler & dll + uint8_t regv; + ES7210_ERROR_CHECK(this->read_byte(ES7210_MAINCLK_REG02, ®v)); + regv = regv & 0x00; + regv |= ES7210_COEFFICIENTS[coeff].adc_div; + regv |= ES7210_COEFFICIENTS[coeff].doubler << 6; + regv |= ES7210_COEFFICIENTS[coeff].dll << 7; + + ES7210_ERROR_CHECK(this->write_byte(ES7210_MAINCLK_REG02, regv)); + + // Set osr + regv = ES7210_COEFFICIENTS[coeff].osr; + ES7210_ERROR_CHECK(this->write_byte(ES7210_OSR_REG07, regv)); + // Set lrck + regv = ES7210_COEFFICIENTS[coeff].lrck_h; + ES7210_ERROR_CHECK(this->write_byte(ES7210_LRCK_DIVH_REG04, regv)); + regv = ES7210_COEFFICIENTS[coeff].lrck_l; + ES7210_ERROR_CHECK(this->write_byte(ES7210_LRCK_DIVL_REG05, regv)); + } else { + // Invalid sample frequency + ESP_LOGE(TAG, "Invalid sample rate"); + return false; + } + + return true; +} +bool ES7210::configure_mic_gain_() { + for (int i = 0; i < 4; ++i) { + this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00); + } + ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0xff)); + ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0xff)); + + // Configure mic 1 + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); + ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x10, 0x10)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x0f, this->mic_gain_)); + + // Configure mic 2 + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); + ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x10, 0x10)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x0f, this->mic_gain_)); + + // Configure mic 3 + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); + ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x10, 0x10)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x0f, this->mic_gain_)); + + // Configure mic 4 + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); + ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x10, 0x10)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x0f, this->mic_gain_)); + + return true; +} + +bool ES7210::configure_i2s_format_() { + // Configure bits per sample + uint8_t reg_val = 0; + switch (this->bits_per_sample_) { + case ES7210_BITS_PER_SAMPLE_16: + reg_val = 0x60; + break; + case ES7210_BITS_PER_SAMPLE_18: + reg_val = 0x40; + break; + case ES7210_BITS_PER_SAMPLE_20: + reg_val = 0x20; + break; + case ES7210_BITS_PER_SAMPLE_24: + reg_val = 0x00; + break; + case ES7210_BITS_PER_SAMPLE_32: + reg_val = 0x80; + break; + default: + return false; + } + ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE1_REG11, reg_val)); + + if (this->enable_tdm_) { + ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE2_REG12, 0x02)); + } else { + // Microphones 1 and 2 output on SDOUT1, microphones 3 and 4 output on SDOUT2 + ES7210_ERROR_CHECK(this->write_byte(ES7210_SDP_INTERFACE2_REG12, 0x00)); + } + + return true; +} + +bool ES7210::es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data) { + uint8_t regv; + ES7210_ERROR_CHECK(this->read_byte(reg_addr, ®v)); + regv = (regv & (~update_bits)) | (update_bits & data); + return this->write_byte(reg_addr, regv); +} + +} // namespace es7210 +} // namespace esphome diff --git a/esphome/components/es7210/es7210.h b/esphome/components/es7210/es7210.h new file mode 100644 index 0000000000..a40dde5aa5 --- /dev/null +++ b/esphome/components/es7210/es7210.h @@ -0,0 +1,69 @@ +#pragma once + +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace es7210 { + +enum ES7210BitsPerSample : uint8_t { + ES7210_BITS_PER_SAMPLE_16 = 16, + ES7210_BITS_PER_SAMPLE_18 = 18, + ES7210_BITS_PER_SAMPLE_20 = 20, + ES7210_BITS_PER_SAMPLE_24 = 24, + ES7210_BITS_PER_SAMPLE_32 = 32, +}; + +enum ES7210MicGain : uint8_t { + ES7210_MIC_GAIN_0DB = 0, + ES7210_MIC_GAIN_3DB, + ES7210_MIC_GAIN_6DB, + ES7210_MIC_GAIN_9DB, + ES7210_MIC_GAIN_12DB, + ES7210_MIC_GAIN_15DB, + ES7210_MIC_GAIN_18DB, + ES7210_MIC_GAIN_21DB, + ES7210_MIC_GAIN_24DB, + ES7210_MIC_GAIN_27DB, + ES7210_MIC_GAIN_30DB, + ES7210_MIC_GAIN_33DB, + ES7210_MIC_GAIN_34_5DB, + ES7210_MIC_GAIN_36DB, + ES7210_MIC_GAIN_37_5DB, +}; + +class ES7210 : public Component, public i2c::I2CDevice { + /* Class for configuring an ES7210 ADC for microphone input. + * Based on code from: + * - https://github.com/espressif/esp-bsp/ (accessed 20241219) + * - https://github.com/espressif/esp-adf/ (accessed 20241219) + */ + public: + void setup() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void dump_config() override; + + void set_bits_per_sample(ES7210BitsPerSample bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } + void set_mic_gain(ES7210MicGain mic_gain) { this->mic_gain_ = mic_gain; } + void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } + + protected: + /// @brief Updates an I2C registry address by modifying the current state + /// @param reg_addr I2C register address + /// @param update_bits Mask of allowed bits to be modified + /// @param data Bit values to be written + /// @return True if successful, false otherwise + bool es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data); + + bool configure_i2s_format_(); + bool configure_mic_gain_(); + bool configure_sample_rate_(); + + bool enable_tdm_{false}; // TDM is unsupported in ESPHome as of version 2024.12 + ES7210MicGain mic_gain_; + ES7210BitsPerSample bits_per_sample_; + uint32_t sample_rate_; +}; + +} // namespace es7210 +} // namespace esphome diff --git a/esphome/components/es7210/es7210_const.h b/esphome/components/es7210/es7210_const.h new file mode 100644 index 0000000000..87fd6d86d2 --- /dev/null +++ b/esphome/components/es7210/es7210_const.h @@ -0,0 +1,126 @@ +#pragma once + +#include "es7210.h" + +namespace esphome { +namespace es7210 { + +// ES7210 register addresses +static const uint8_t ES7210_RESET_REG00 = 0x00; /* Reset control */ +static const uint8_t ES7210_CLOCK_OFF_REG01 = 0x01; /* Used to turn off the ADC clock */ +static const uint8_t ES7210_MAINCLK_REG02 = 0x02; /* Set ADC clock frequency division */ + +static const uint8_t ES7210_MASTER_CLK_REG03 = 0x03; /* MCLK source $ SCLK division */ +static const uint8_t ES7210_LRCK_DIVH_REG04 = 0x04; /* lrck_divh */ +static const uint8_t ES7210_LRCK_DIVL_REG05 = 0x05; /* lrck_divl */ +static const uint8_t ES7210_POWER_DOWN_REG06 = 0x06; /* power down */ +static const uint8_t ES7210_OSR_REG07 = 0x07; +static const uint8_t ES7210_MODE_CONFIG_REG08 = 0x08; /* Set primary/secondary & channels */ +static const uint8_t ES7210_TIME_CONTROL0_REG09 = 0x09; /* Set Chip intial state period*/ +static const uint8_t ES7210_TIME_CONTROL1_REG0A = 0x0A; /* Set Power up state period */ +static const uint8_t ES7210_SDP_INTERFACE1_REG11 = 0x11; /* Set sample & fmt */ +static const uint8_t ES7210_SDP_INTERFACE2_REG12 = 0x12; /* Pins state */ +static const uint8_t ES7210_ADC_AUTOMUTE_REG13 = 0x13; /* Set mute */ +static const uint8_t ES7210_ADC34_MUTERANGE_REG14 = 0x14; /* Set mute range */ +static const uint8_t ES7210_ADC12_MUTERANGE_REG15 = 0x15; /* Set mute range */ +static const uint8_t ES7210_ADC34_HPF2_REG20 = 0x20; /* HPF */ +static const uint8_t ES7210_ADC34_HPF1_REG21 = 0x21; /* HPF */ +static const uint8_t ES7210_ADC12_HPF1_REG22 = 0x22; /* HPF */ +static const uint8_t ES7210_ADC12_HPF2_REG23 = 0x23; /* HPF */ +static const uint8_t ES7210_ANALOG_REG40 = 0x40; /* ANALOG Power */ +static const uint8_t ES7210_MIC12_BIAS_REG41 = 0x41; +static const uint8_t ES7210_MIC34_BIAS_REG42 = 0x42; +static const uint8_t ES7210_MIC1_GAIN_REG43 = 0x43; +static const uint8_t ES7210_MIC2_GAIN_REG44 = 0x44; +static const uint8_t ES7210_MIC3_GAIN_REG45 = 0x45; +static const uint8_t ES7210_MIC4_GAIN_REG46 = 0x46; +static const uint8_t ES7210_MIC1_POWER_REG47 = 0x47; +static const uint8_t ES7210_MIC2_POWER_REG48 = 0x48; +static const uint8_t ES7210_MIC3_POWER_REG49 = 0x49; +static const uint8_t ES7210_MIC4_POWER_REG4A = 0x4A; +static const uint8_t ES7210_MIC12_POWER_REG4B = 0x4B; /* MICBias & ADC & PGA Power */ +static const uint8_t ES7210_MIC34_POWER_REG4C = 0x4C; + +/* + * Clock coefficient structer + */ +struct ES7210Coefficient { + uint32_t mclk; // mclk frequency + uint32_t lrclk; + uint8_t ss_ds; + uint8_t adc_div; + uint8_t dll; // dll_bypass + uint8_t doubler; // doubler_enable + uint8_t osr; // adc osr + uint8_t mclk_src; // sselect mclk source + uint8_t lrck_h; // High 4 bits of lrck + uint8_t lrck_l; // Low 8 bits of lrck +}; + +/* Codec hifi mclk clock divider coefficients + * MEMBER REG + * mclk: 0x03 + * lrck: standard + * ss_ds: -- + * adc_div: 0x02 + * dll: 0x06 + * doubler: 0x02 + * osr: 0x07 + * mclk_src: 0x03 + * lrckh: 0x04 + * lrckl: 0x05 + */ +static const ES7210Coefficient ES7210_COEFFICIENTS[] = { + // mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl + /* 8k */ + {12288000, 8000, 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00}, + {16384000, 8000, 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00}, + {19200000, 8000, 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60}, + {4096000, 8000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 11.025k */ + {11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + + /* 12k */ + {12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40}, + + /* 16k */ + {4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80}, + {16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00}, + + /* 22.05k */ + {11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 24k */ + {12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20}, + + /* 32k */ + {12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80}, + {16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58}, + + /* 44.1k */ + {11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + + /* 48k */ + {12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90}, + + /* 64k */ + {16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + {19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c}, + + /* 88.2k */ + {11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + + /* 96k */ + {12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + {19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8}, +}; + +} // namespace es7210 +} // namespace esphome diff --git a/esphome/components/es8311/audio_dac.py b/esphome/components/es8311/audio_dac.py index 1b450c3c11..7d80cfd5fb 100644 --- a/esphome/components/es8311/audio_dac.py +++ b/esphome/components/es8311/audio_dac.py @@ -2,7 +2,7 @@ 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_BITS_PER_SAMPLE, CONF_ID, CONF_SAMPLE_RATE +from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE CODEOWNERS = ["@kroimon", "@kahrendt"] DEPENDENCIES = ["i2c"] @@ -10,7 +10,6 @@ DEPENDENCIES = ["i2c"] es8311_ns = cg.esphome_ns.namespace("es8311") ES8311 = es8311_ns.class_("ES8311", AudioDac, cg.Component, i2c.I2CDevice) -CONF_MIC_GAIN = "mic_gain" CONF_USE_MCLK = "use_mclk" CONF_USE_MICROPHONE = "use_microphone" diff --git a/esphome/const.py b/esphome/const.py index 0f41dc1aec..4c0203c6a9 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -490,6 +490,7 @@ CONF_MEMORY_BLOCKS = "memory_blocks" CONF_MESSAGE = "message" CONF_METHANE = "methane" CONF_METHOD = "method" +CONF_MIC_GAIN = "mic_gain" CONF_MICROPHONE = "microphone" CONF_MIN_BRIGHTNESS = "min_brightness" CONF_MIN_COOLING_OFF_TIME = "min_cooling_off_time" diff --git a/tests/components/es7210/common.yaml b/tests/components/es7210/common.yaml new file mode 100644 index 0000000000..5c30f7e883 --- /dev/null +++ b/tests/components/es7210/common.yaml @@ -0,0 +1,6 @@ +i2c: + - id: i2c_aic3204 + scl: ${scl_pin} + sda: ${sda_pin} + +es7210: diff --git a/tests/components/es7210/test.esp32-ard.yaml b/tests/components/es7210/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es7210/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es7210/test.esp32-c3-ard.yaml b/tests/components/es7210/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es7210/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es7210/test.esp32-c3-idf.yaml b/tests/components/es7210/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es7210/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es7210/test.esp32-idf.yaml b/tests/components/es7210/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es7210/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml From c458fd18df52923b6b3fb35e2ea6efd4d6603f72 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:49:58 +1300 Subject: [PATCH 0885/1052] Bump version to 2025.2.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 4c0203c6a9..284f8d5f78 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.1.0-dev" +__version__ = "2025.2.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From e779a8bcb2e73eddb7eb3474814dd6c64bb98486 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:54:45 +1300 Subject: [PATCH 0886/1052] [event] Store ``last_event_type`` in class (#8088) --- esphome/components/event/event.cpp | 6 ++++-- esphome/components/event/event.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/esphome/components/event/event.cpp b/esphome/components/event/event.cpp index 061afcb026..d27b3b378e 100644 --- a/esphome/components/event/event.cpp +++ b/esphome/components/event/event.cpp @@ -8,11 +8,13 @@ namespace event { static const char *const TAG = "event"; void Event::trigger(const std::string &event_type) { - if (types_.find(event_type) == types_.end()) { + auto found = types_.find(event_type); + if (found == types_.end()) { ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str()); return; } - ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), event_type.c_str()); + last_event_type = &(*found); + ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str()); this->event_callback_.call(event_type); } diff --git a/esphome/components/event/event.h b/esphome/components/event/event.h index 067a867360..03c3c8d95a 100644 --- a/esphome/components/event/event.h +++ b/esphome/components/event/event.h @@ -23,6 +23,8 @@ namespace event { class Event : public EntityBase, public EntityBase_DeviceClass { public: + const std::string *last_event_type; + void trigger(const std::string &event_type); void set_event_types(const std::set &event_types) { this->types_ = event_types; } std::set get_event_types() const { return this->types_; } From df26ace0f172d1bad7f019fc4d095f3a7be188f9 Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Tue, 14 Jan 2025 19:56:22 -0800 Subject: [PATCH 0887/1052] [prometheus] Select, media_player, and number prometheus metrics (#7895) --- .../prometheus/prometheus_handler.cpp | 168 ++++++++++++++++++ .../prometheus/prometheus_handler.h | 24 +++ tests/components/prometheus/common.yaml | 20 +++ .../components/prometheus/test.esp32-ard.yaml | 33 ++++ 4 files changed, 245 insertions(+) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 5d1861202a..2d39d8ef3f 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -59,6 +59,24 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) { this->text_sensor_row_(stream, obj, area, node, friendly_name); #endif +#ifdef USE_NUMBER + this->number_type_(stream); + for (auto *obj : App.get_numbers()) + this->number_row_(stream, obj, area, node, friendly_name); +#endif + +#ifdef USE_SELECT + this->select_type_(stream); + for (auto *obj : App.get_selects()) + this->select_row_(stream, obj, area, node, friendly_name); +#endif + +#ifdef USE_MEDIA_PLAYER + this->media_player_type_(stream); + for (auto *obj : App.get_media_players()) + this->media_player_row_(stream, obj, area, node, friendly_name); +#endif + req->send(stream); } @@ -511,6 +529,156 @@ void PrometheusHandler::text_sensor_row_(AsyncResponseStream *stream, text_senso } #endif +// Type-specific implementation +#ifdef USE_NUMBER +void PrometheusHandler::number_type_(AsyncResponseStream *stream) { + stream->print(F("#TYPE esphome_number_value gauge\n")); + stream->print(F("#TYPE esphome_number_failed gauge\n")); +} +void PrometheusHandler::number_row_(AsyncResponseStream *stream, number::Number *obj, std::string &area, + std::string &node, std::string &friendly_name) { + if (obj->is_internal() && !this->include_internal_) + return; + if (!std::isnan(obj->state)) { + // We have a valid value, output this value + stream->print(F("esphome_number_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 0\n")); + // Data itself + stream->print(F("esphome_number_value{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} ")); + stream->print(obj->state); + stream->print(F("\n")); + } else { + // Invalid state + stream->print(F("esphome_number_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 1\n")); + } +} +#endif + +#ifdef USE_SELECT +void PrometheusHandler::select_type_(AsyncResponseStream *stream) { + stream->print(F("#TYPE esphome_select_value gauge\n")); + stream->print(F("#TYPE esphome_select_failed gauge\n")); +} +void PrometheusHandler::select_row_(AsyncResponseStream *stream, select::Select *obj, std::string &area, + std::string &node, std::string &friendly_name) { + if (obj->is_internal() && !this->include_internal_) + return; + if (obj->has_state()) { + // We have a valid value, output this value + stream->print(F("esphome_select_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 0\n")); + // Data itself + stream->print(F("esphome_select_value{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\",value=\"")); + stream->print(obj->state.c_str()); + stream->print(F("\"} ")); + stream->print(F("1.0")); + stream->print(F("\n")); + } else { + // Invalid state + stream->print(F("esphome_select_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 1\n")); + } +} +#endif + +#ifdef USE_MEDIA_PLAYER +void PrometheusHandler::media_player_type_(AsyncResponseStream *stream) { + stream->print(F("#TYPE esphome_media_player_state_value gauge\n")); + stream->print(F("#TYPE esphome_media_player_volume gauge\n")); + stream->print(F("#TYPE esphome_media_player_is_muted gauge\n")); + stream->print(F("#TYPE esphome_media_player_failed gauge\n")); +} +void PrometheusHandler::media_player_row_(AsyncResponseStream *stream, media_player::MediaPlayer *obj, + std::string &area, std::string &node, std::string &friendly_name) { + if (obj->is_internal() && !this->include_internal_) + return; + stream->print(F("esphome_media_player_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 0\n")); + // Data itself + stream->print(F("esphome_media_player_state_value{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\",value=\"")); + stream->print(media_player::media_player_state_to_string(obj->state)); + stream->print(F("\"} ")); + stream->print(F("1.0")); + stream->print(F("\n")); + stream->print(F("esphome_media_player_volume{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} ")); + stream->print(obj->volume); + stream->print(F("\n")); + stream->print(F("esphome_media_player_is_muted{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} ")); + if (obj->is_muted()) { + stream->print(F("1.0")); + } else { + stream->print(F("0.0")); + } + stream->print(F("\n")); +} +#endif + } // namespace prometheus } // namespace esphome #endif diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 5d08aca63a..41a06537ed 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -128,6 +128,30 @@ class PrometheusHandler : public AsyncWebHandler, public Component { std::string &friendly_name); #endif +#ifdef USE_NUMBER + /// Return the type for prometheus + void number_type_(AsyncResponseStream *stream); + /// Return the sensor state as prometheus data point + void number_row_(AsyncResponseStream *stream, number::Number *obj, std::string &area, std::string &node, + std::string &friendly_name); +#endif + +#ifdef USE_SELECT + /// Return the type for prometheus + void select_type_(AsyncResponseStream *stream); + /// Return the select state as prometheus data point + void select_row_(AsyncResponseStream *stream, select::Select *obj, std::string &area, std::string &node, + std::string &friendly_name); +#endif + +#ifdef USE_MEDIA_PLAYER + /// Return the type for prometheus + void media_player_type_(AsyncResponseStream *stream); + /// Return the select state as prometheus data point + void media_player_row_(AsyncResponseStream *stream, media_player::MediaPlayer *obj, std::string &area, + std::string &node, std::string &friendly_name); +#endif + web_server_base::WebServerBase *base_; bool include_internal_{false}; std::map relabel_map_id_; diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index 68ef2a2f58..1b87c1d6c1 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -78,6 +78,26 @@ lock: } optimistic: true +select: + - platform: template + id: template_select1 + name: "Template select" + optimistic: true + options: + - one + - two + - three + initial_option: two + +number: + - platform: template + id: template_number1 + name: "Template number" + optimistic: true + min_value: 0 + max_value: 100 + step: 1 + prometheus: include_internal: true relabel: diff --git a/tests/components/prometheus/test.esp32-ard.yaml b/tests/components/prometheus/test.esp32-ard.yaml index dade44d145..3045a6db13 100644 --- a/tests/components/prometheus/test.esp32-ard.yaml +++ b/tests/components/prometheus/test.esp32-ard.yaml @@ -1 +1,34 @@ <<: !include common.yaml + +i2s_audio: + i2s_lrclk_pin: 1 + i2s_bclk_pin: 2 + i2s_mclk_pin: 3 + +media_player: + - platform: i2s_audio + name: "Media Player" + dac_type: external + i2s_dout_pin: 18 + mute_pin: 19 + on_state: + - media_player.play: + - media_player.play_media: http://localhost/media.mp3 + - media_player.play_media: !lambda 'return "http://localhost/media.mp3";' + on_idle: + - media_player.pause: + on_play: + - media_player.stop: + on_pause: + - media_player.toggle: + - wait_until: + media_player.is_idle: + - wait_until: + media_player.is_playing: + - wait_until: + media_player.is_announcing: + - wait_until: + media_player.is_paused: + - media_player.volume_up: + - media_player.volume_down: + - media_player.volume_set: 50% From b4a804cc779bcb8a3b1480a44741bf9f29caa33c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:53:23 +1300 Subject: [PATCH 0888/1052] Bump docker/build-push-action from 6.11.0 to 6.12.0 in /.github/actions/build-image (#8090) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index e6b6da4177..b2a3394563 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.11.0 + uses: docker/build-push-action@v6.12.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false @@ -72,7 +72,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.11.0 + uses: docker/build-push-action@v6.12.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false From 49c01c26f1847debfa53dc7e31b6434debcfd40e Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:12:30 +1100 Subject: [PATCH 0889/1052] Revert "Add resistance_sampler interface for config validation" (#8093) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 - esphome/components/ntc/sensor.py | 6 ++---- esphome/components/resistance/resistance_sensor.h | 5 ++--- esphome/components/resistance/sensor.py | 11 ++--------- esphome/components/resistance_sampler/__init__.py | 6 ------ .../resistance_sampler/resistance_sampler.h | 10 ---------- 6 files changed, 6 insertions(+), 33 deletions(-) delete mode 100644 esphome/components/resistance_sampler/__init__.py delete mode 100644 esphome/components/resistance_sampler/resistance_sampler.h diff --git a/CODEOWNERS b/CODEOWNERS index ba7106e6a3..f0075549fd 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -339,7 +339,6 @@ esphome/components/radon_eye_rd200/* @jeffeb3 esphome/components/rc522/* @glmnet esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_spi/* @glmnet -esphome/components/resistance_sampler/* @jesserockz esphome/components/restart/* @esphome/core esphome/components/rf_bridge/* @jesserockz esphome/components/rgbct/* @jesserockz diff --git a/esphome/components/ntc/sensor.py b/esphome/components/ntc/sensor.py index 961511fe00..bd5f4a1841 100644 --- a/esphome/components/ntc/sensor.py +++ b/esphome/components/ntc/sensor.py @@ -2,7 +2,7 @@ from math import log import esphome.config_validation as cv import esphome.codegen as cg -from esphome.components import sensor, resistance_sampler +from esphome.components import sensor from esphome.const import ( CONF_CALIBRATION, CONF_REFERENCE_RESISTANCE, @@ -15,8 +15,6 @@ from esphome.const import ( UNIT_CELSIUS, ) -AUTO_LOAD = ["resistance_sampler"] - ntc_ns = cg.esphome_ns.namespace("ntc") NTC = ntc_ns.class_("NTC", cg.Component, sensor.Sensor) @@ -126,7 +124,7 @@ CONFIG_SCHEMA = ( ) .extend( { - cv.Required(CONF_SENSOR): cv.use_id(resistance_sampler.ResistanceSampler), + cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), cv.Required(CONF_CALIBRATION): process_calibration, } ) diff --git a/esphome/components/resistance/resistance_sensor.h b/esphome/components/resistance/resistance_sensor.h index 8fa1f8b570..b57f90b59c 100644 --- a/esphome/components/resistance/resistance_sensor.h +++ b/esphome/components/resistance/resistance_sensor.h @@ -1,8 +1,7 @@ #pragma once -#include "esphome/components/resistance_sampler/resistance_sampler.h" -#include "esphome/components/sensor/sensor.h" #include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" namespace esphome { namespace resistance { @@ -12,7 +11,7 @@ enum ResistanceConfiguration { DOWNSTREAM, }; -class ResistanceSensor : public Component, public sensor::Sensor, resistance_sampler::ResistanceSampler { +class ResistanceSensor : public Component, public sensor::Sensor { public: void set_sensor(Sensor *sensor) { sensor_ = sensor; } void set_configuration(ResistanceConfiguration configuration) { configuration_ = configuration; } diff --git a/esphome/components/resistance/sensor.py b/esphome/components/resistance/sensor.py index ce4459fc6d..3622799a07 100644 --- a/esphome/components/resistance/sensor.py +++ b/esphome/components/resistance/sensor.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor, resistance_sampler +from esphome.components import sensor from esphome.const import ( CONF_REFERENCE_VOLTAGE, CONF_SENSOR, @@ -9,15 +9,8 @@ from esphome.const import ( ICON_FLASH, ) -AUTO_LOAD = ["resistance_sampler"] - resistance_ns = cg.esphome_ns.namespace("resistance") -ResistanceSensor = resistance_ns.class_( - "ResistanceSensor", - cg.Component, - sensor.Sensor, - resistance_sampler.ResistanceSampler, -) +ResistanceSensor = resistance_ns.class_("ResistanceSensor", cg.Component, sensor.Sensor) CONF_CONFIGURATION = "configuration" CONF_RESISTOR = "resistor" diff --git a/esphome/components/resistance_sampler/__init__.py b/esphome/components/resistance_sampler/__init__.py deleted file mode 100644 index d2032848aa..0000000000 --- a/esphome/components/resistance_sampler/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import esphome.codegen as cg - -resistance_sampler_ns = cg.esphome_ns.namespace("resistance_sampler") -ResistanceSampler = resistance_sampler_ns.class_("ResistanceSampler") - -CODEOWNERS = ["@jesserockz"] diff --git a/esphome/components/resistance_sampler/resistance_sampler.h b/esphome/components/resistance_sampler/resistance_sampler.h deleted file mode 100644 index 9e300bebcc..0000000000 --- a/esphome/components/resistance_sampler/resistance_sampler.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -namespace esphome { -namespace resistance_sampler { - -/// Abstract interface to mark components that provide resistance values. -class ResistanceSampler {}; - -} // namespace resistance_sampler -} // namespace esphome From 16bf56b0f95b1c5a75cc2f4ae167e37485c8803b Mon Sep 17 00:00:00 2001 From: Katherine Whitlock Date: Thu, 16 Jan 2025 15:10:20 -0500 Subject: [PATCH 0890/1052] Fix running pre-commit on Windows (#8095) --- .pre-commit-config.yaml | 4 ++-- script/run-in-env | 53 +++++++++++++++++++++++++++++++++++++++++ script/run-in-env.sh | 13 ---------- 3 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 script/run-in-env delete mode 100755 script/run-in-env.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index aeb434167a..5308a2c047 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,6 +53,6 @@ repos: hooks: - id: pylint name: pylint - entry: script/run-in-env.sh pylint - language: script + entry: python script/run-in-env pylint + language: system types: [python] diff --git a/script/run-in-env b/script/run-in-env new file mode 100644 index 0000000000..57bfddccf7 --- /dev/null +++ b/script/run-in-env @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import os +from pathlib import Path +import subprocess +import sys + + +def find_and_activate_virtualenv(): + try: + # Get the top-level directory of the git repository + my_path = subprocess.check_output( + ["git", "rev-parse", "--show-toplevel"], text=True + ).strip() + except subprocess.CalledProcessError: + print( + "Error: Not a git repository or unable to determine the top-level directory.", + file=sys.stderr, + ) + sys.exit(1) + + # Check for virtual environments + for venv in ["venv", ".venv", "."]: + activate_path = ( + Path(my_path) + / venv + / ("Scripts" if os.name == "nt" else "bin") + / "activate" + ) + if activate_path.exists(): + # Activate the virtual environment by updating PATH + env = os.environ.copy() + venv_bin_dir = activate_path.parent + env["PATH"] = f"{venv_bin_dir}{os.pathsep}{env['PATH']}" + env["VIRTUAL_ENV"] = str(venv_bin_dir.parent) + print(f"Activated virtual environment: {venv_bin_dir.parent}") + + # Execute the remaining arguments in the new environment + if len(sys.argv) > 1: + subprocess.run(sys.argv[1:], env=env, check=False) + else: + print( + "No command provided to run in the virtual environment.", + file=sys.stderr, + ) + return + + print("No virtual environment found.", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + find_and_activate_virtualenv() diff --git a/script/run-in-env.sh b/script/run-in-env.sh deleted file mode 100755 index 2e05fe1d17..0000000000 --- a/script/run-in-env.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env sh -set -eu - -my_path=$(git rev-parse --show-toplevel) - -for venv in venv .venv .; do - if [ -f "${my_path}/${venv}/bin/activate" ]; then - . "${my_path}/${venv}/bin/activate" - break - fi -done - -exec "$@" From 8c6c45e6c197eb45400f6a2dd17568afe3977749 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Thu, 16 Jan 2025 15:43:41 -0600 Subject: [PATCH 0891/1052] [http_request] Bugfix: run update function in a task (#8018) --- .../http_request/http_request_idf.cpp | 24 ++- .../http_request/http_request_idf.h | 3 + .../update/http_request_update.cpp | 147 ++++++++++-------- .../http_request/update/http_request_update.h | 9 ++ 4 files changed, 115 insertions(+), 68 deletions(-) diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index b449f046ee..66f064c2ce 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -12,6 +12,8 @@ #include "esp_crt_bundle.h" #endif +#include "esp_task_wdt.h" + namespace esphome { namespace http_request { @@ -117,11 +119,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } - App.feed_wdt(); + container->feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); - App.feed_wdt(); + container->feed_wdt(); container->status_code = esp_http_client_get_status_code(client); - App.feed_wdt(); + container->feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; @@ -151,11 +153,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } - App.feed_wdt(); + container->feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); - App.feed_wdt(); + container->feed_wdt(); container->status_code = esp_http_client_get_status_code(client); - App.feed_wdt(); + container->feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; @@ -185,8 +187,9 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) { return 0; } - App.feed_wdt(); + this->feed_wdt(); int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize); + this->feed_wdt(); this->bytes_read_ += read_len; this->duration_ms += (millis() - start); @@ -201,6 +204,13 @@ void HttpContainerIDF::end() { esp_http_client_cleanup(this->client_); } +void HttpContainerIDF::feed_wdt() { + // Tests to see if the executing task has a watchdog timer attached + if (esp_task_wdt_status(nullptr) == ESP_OK) { + App.feed_wdt(); + } +} + } // namespace http_request } // namespace esphome diff --git a/esphome/components/http_request/http_request_idf.h b/esphome/components/http_request/http_request_idf.h index 431794924b..2ed50698b9 100644 --- a/esphome/components/http_request/http_request_idf.h +++ b/esphome/components/http_request/http_request_idf.h @@ -18,6 +18,9 @@ class HttpContainerIDF : public HttpContainer { int read(uint8_t *buf, size_t max_len) override; void end() override; + /// @brief Feeds the watchdog timer if the executing task has one attached + void feed_wdt(); + protected: esp_http_client_handle_t client_; }; diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 0e0966c22b..d683495ac6 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -9,6 +9,13 @@ namespace esphome { namespace http_request { +// The update function runs in a task only on ESP32s. +#ifdef USE_ESP32 +#define UPDATE_RETURN vTaskDelete(nullptr) // Delete the current update task +#else +#define UPDATE_RETURN return +#endif + static const char *const TAG = "http_request.update"; static const size_t MAX_READ_SIZE = 256; @@ -29,113 +36,131 @@ void HttpRequestUpdate::setup() { } void HttpRequestUpdate::update() { - auto container = this->request_parent_->get(this->source_url_); +#ifdef USE_ESP32 + xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_); +#else + this->update_task(this); +#endif +} + +void HttpRequestUpdate::update_task(void *params) { + HttpRequestUpdate *this_update = (HttpRequestUpdate *) params; + + auto container = this_update->request_parent_->get(this_update->source_url_); if (container == nullptr || container->status_code != HTTP_STATUS_OK) { - std::string msg = str_sprintf("Failed to fetch manifest from %s", this->source_url_.c_str()); - this->status_set_error(msg.c_str()); - return; + std::string msg = str_sprintf("Failed to fetch manifest from %s", this_update->source_url_.c_str()); + this_update->status_set_error(msg.c_str()); + UPDATE_RETURN; } ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); uint8_t *data = allocator.allocate(container->content_length); if (data == nullptr) { std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length); - this->status_set_error(msg.c_str()); + this_update->status_set_error(msg.c_str()); container->end(); - return; + UPDATE_RETURN; } size_t read_index = 0; while (container->get_bytes_read() < container->content_length) { int read_bytes = container->read(data + read_index, MAX_READ_SIZE); - App.feed_wdt(); yield(); read_index += read_bytes; } - std::string response((char *) data, read_index); - allocator.deallocate(data, container->content_length); + bool valid = false; + { // Ensures the response string falls out of scope and deallocates before the task ends + std::string response((char *) data, read_index); + allocator.deallocate(data, container->content_length); - container->end(); + container->end(); + container.reset(); // Release ownership of the container's shared_ptr - bool valid = json::parse_json(response, [this](JsonObject root) -> bool { - if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) { - ESP_LOGE(TAG, "Manifest does not contain required fields"); - return false; - } - this->update_info_.title = root["name"].as(); - this->update_info_.latest_version = root["version"].as(); - - for (auto build : root["builds"].as()) { - if (!build.containsKey("chipFamily")) { + valid = json::parse_json(response, [this_update](JsonObject root) -> bool { + if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } - if (build["chipFamily"] == ESPHOME_VARIANT) { - if (!build.containsKey("ota")) { + this_update->update_info_.title = root["name"].as(); + this_update->update_info_.latest_version = root["version"].as(); + + for (auto build : root["builds"].as()) { + if (!build.containsKey("chipFamily")) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } - auto ota = build["ota"]; - if (!ota.containsKey("path") || !ota.containsKey("md5")) { - ESP_LOGE(TAG, "Manifest does not contain required fields"); - return false; + if (build["chipFamily"] == ESPHOME_VARIANT) { + if (!build.containsKey("ota")) { + ESP_LOGE(TAG, "Manifest does not contain required fields"); + return false; + } + auto ota = build["ota"]; + if (!ota.containsKey("path") || !ota.containsKey("md5")) { + ESP_LOGE(TAG, "Manifest does not contain required fields"); + return false; + } + this_update->update_info_.firmware_url = ota["path"].as(); + this_update->update_info_.md5 = ota["md5"].as(); + + if (ota.containsKey("summary")) + this_update->update_info_.summary = ota["summary"].as(); + if (ota.containsKey("release_url")) + this_update->update_info_.release_url = ota["release_url"].as(); + + return true; } - this->update_info_.firmware_url = ota["path"].as(); - this->update_info_.md5 = ota["md5"].as(); - - if (ota.containsKey("summary")) - this->update_info_.summary = ota["summary"].as(); - if (ota.containsKey("release_url")) - this->update_info_.release_url = ota["release_url"].as(); - - return true; } - } - return false; - }); + return false; + }); + } if (!valid) { - std::string msg = str_sprintf("Failed to parse JSON from %s", this->source_url_.c_str()); - this->status_set_error(msg.c_str()); - return; + std::string msg = str_sprintf("Failed to parse JSON from %s", this_update->source_url_.c_str()); + this_update->status_set_error(msg.c_str()); + UPDATE_RETURN; } - // Merge source_url_ and this->update_info_.firmware_url - if (this->update_info_.firmware_url.find("http") == std::string::npos) { - std::string path = this->update_info_.firmware_url; + // Merge source_url_ and this_update->update_info_.firmware_url + if (this_update->update_info_.firmware_url.find("http") == std::string::npos) { + std::string path = this_update->update_info_.firmware_url; if (path[0] == '/') { - std::string domain = this->source_url_.substr(0, this->source_url_.find('/', 8)); - this->update_info_.firmware_url = domain + path; + std::string domain = this_update->source_url_.substr(0, this_update->source_url_.find('/', 8)); + this_update->update_info_.firmware_url = domain + path; } else { - std::string domain = this->source_url_.substr(0, this->source_url_.rfind('/') + 1); - this->update_info_.firmware_url = domain + path; + std::string domain = this_update->source_url_.substr(0, this_update->source_url_.rfind('/') + 1); + this_update->update_info_.firmware_url = domain + path; } } - std::string current_version; + { // Ensures the current version string falls out of scope and deallocates before the task ends + std::string current_version; #ifdef ESPHOME_PROJECT_VERSION - current_version = ESPHOME_PROJECT_VERSION; + current_version = ESPHOME_PROJECT_VERSION; #else - current_version = ESPHOME_VERSION; + current_version = ESPHOME_VERSION; #endif - this->update_info_.current_version = current_version; - - if (this->update_info_.latest_version.empty() || this->update_info_.latest_version == update_info_.current_version) { - this->state_ = update::UPDATE_STATE_NO_UPDATE; - } else { - this->state_ = update::UPDATE_STATE_AVAILABLE; + this_update->update_info_.current_version = current_version; } - this->update_info_.has_progress = false; - this->update_info_.progress = 0.0f; + if (this_update->update_info_.latest_version.empty() || + this_update->update_info_.latest_version == this_update->update_info_.current_version) { + this_update->state_ = update::UPDATE_STATE_NO_UPDATE; + } else { + this_update->state_ = update::UPDATE_STATE_AVAILABLE; + } - this->status_clear_error(); - this->publish_state(); + this_update->update_info_.has_progress = false; + this_update->update_info_.progress = 0.0f; + + this_update->status_clear_error(); + this_update->publish_state(); + + UPDATE_RETURN; } void HttpRequestUpdate::perform(bool force) { diff --git a/esphome/components/http_request/update/http_request_update.h b/esphome/components/http_request/update/http_request_update.h index 45c7e6a447..e05fdb0cc2 100644 --- a/esphome/components/http_request/update/http_request_update.h +++ b/esphome/components/http_request/update/http_request_update.h @@ -7,6 +7,10 @@ #include "esphome/components/http_request/ota/ota_http_request.h" #include "esphome/components/update/update_entity.h" +#ifdef USE_ESP32 +#include +#endif + namespace esphome { namespace http_request { @@ -29,6 +33,11 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent { HttpRequestComponent *request_parent_; OtaHttpRequestComponent *ota_parent_; std::string source_url_; + + static void update_task(void *params); +#ifdef USE_ESP32 + TaskHandle_t update_task_handle_{nullptr}; +#endif }; } // namespace http_request From 820e3488d03f3644a5661156d78995c2d964a092 Mon Sep 17 00:00:00 2001 From: Katherine Whitlock Date: Thu, 16 Jan 2025 16:44:26 -0500 Subject: [PATCH 0892/1052] Remove black-formatter from pre-commit hooks (#8097) --- .pre-commit-config.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5308a2c047..adf0ac6fc2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,14 +11,6 @@ repos: args: [--fix] # Run the formatter. - id: ruff-format - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 - hooks: - - id: black - args: - - --safe - - --quiet - files: ^((esphome|script|tests)/.+)?[^/]+\.py$ - repo: https://github.com/PyCQA/flake8 rev: 6.1.0 hooks: From 07be7ad7e2f874eaedcfc20ea68a9f8c33b72494 Mon Sep 17 00:00:00 2001 From: j-sepul <117353037+j-sepul@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:08:04 +0100 Subject: [PATCH 0893/1052] Increase Daly-BMS coltage cells from 16 to 18 cells (#8057) --- esphome/components/daly_bms/daly_bms.cpp | 6 ++++++ esphome/components/daly_bms/daly_bms.h | 2 ++ esphome/components/daly_bms/sensor.py | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/esphome/components/daly_bms/daly_bms.cpp b/esphome/components/daly_bms/daly_bms.cpp index 8f6fc0fb57..929f31e008 100644 --- a/esphome/components/daly_bms/daly_bms.cpp +++ b/esphome/components/daly_bms/daly_bms.cpp @@ -298,6 +298,12 @@ void DalyBmsComponent::decode_data_(std::vector data) { if (this->cell_16_voltage_sensor_) { this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000); } + if (this->cell_17_voltage_sensor_) { + this->cell_17_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000); + } + if (this->cell_18_voltage_sensor_) { + this->cell_18_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000); + } break; } break; diff --git a/esphome/components/daly_bms/daly_bms.h b/esphome/components/daly_bms/daly_bms.h index 52ea30ecde..e6d476bcdd 100644 --- a/esphome/components/daly_bms/daly_bms.h +++ b/esphome/components/daly_bms/daly_bms.h @@ -54,6 +54,8 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice { SUB_SENSOR(cell_14_voltage) SUB_SENSOR(cell_15_voltage) SUB_SENSOR(cell_16_voltage) + SUB_SENSOR(cell_17_voltage) + SUB_SENSOR(cell_18_voltage) #endif #ifdef USE_TEXT_SENSOR diff --git a/esphome/components/daly_bms/sensor.py b/esphome/components/daly_bms/sensor.py index c447fbd8a2..6d78946a02 100644 --- a/esphome/components/daly_bms/sensor.py +++ b/esphome/components/daly_bms/sensor.py @@ -52,6 +52,8 @@ CONF_CELL_13_VOLTAGE = "cell_13_voltage" CONF_CELL_14_VOLTAGE = "cell_14_voltage" CONF_CELL_15_VOLTAGE = "cell_15_voltage" CONF_CELL_16_VOLTAGE = "cell_16_voltage" +CONF_CELL_17_VOLTAGE = "cell_17_voltage" +CONF_CELL_18_VOLTAGE = "cell_18_voltage" ICON_CURRENT_DC = "mdi:current-dc" ICON_BATTERY_OUTLINE = "mdi:battery-outline" ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up" @@ -92,6 +94,8 @@ TYPES = [ CONF_CELL_14_VOLTAGE, CONF_CELL_15_VOLTAGE, CONF_CELL_16_VOLTAGE, + CONF_CELL_17_VOLTAGE, + CONF_CELL_18_VOLTAGE, ] CELL_VOLTAGE_SCHEMA = sensor.sensor_schema( @@ -212,6 +216,8 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_CELL_14_VOLTAGE): CELL_VOLTAGE_SCHEMA, cv.Optional(CONF_CELL_15_VOLTAGE): CELL_VOLTAGE_SCHEMA, cv.Optional(CONF_CELL_16_VOLTAGE): CELL_VOLTAGE_SCHEMA, + cv.Optional(CONF_CELL_17_VOLTAGE): CELL_VOLTAGE_SCHEMA, + cv.Optional(CONF_CELL_18_VOLTAGE): CELL_VOLTAGE_SCHEMA, } ).extend(cv.COMPONENT_SCHEMA) ) From abdd6b232f922b640ef93c335f71e9fc93f4cc2b Mon Sep 17 00:00:00 2001 From: Piotr Szulc Date: Mon, 13 Jan 2025 05:31:01 +0100 Subject: [PATCH 0894/1052] Fixed libretiny preference wrongly detecting change in the data to store (#7990) --- esphome/components/libretiny/preferences.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/libretiny/preferences.cpp b/esphome/components/libretiny/preferences.cpp index ceeb30baf5..a090f42aa7 100644 --- a/esphome/components/libretiny/preferences.cpp +++ b/esphome/components/libretiny/preferences.cpp @@ -147,7 +147,7 @@ class LibreTinyPreferences : public ESPPreferences { ESP_LOGV(TAG, "fdb_kv_get_obj('%s'): nullptr - the key might not be set yet", to_save.key.c_str()); return true; } - stored_data.data.reserve(kv.value_len); + stored_data.data.resize(kv.value_len); fdb_blob_make(&blob, stored_data.data.data(), kv.value_len); size_t actual_len = fdb_kv_get_blob(db, to_save.key.c_str(), &blob); if (actual_len != kv.value_len) { From 03c36920ffce80b4cf2505d2196ac8dca4c441eb Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Thu, 16 Jan 2025 15:43:41 -0600 Subject: [PATCH 0895/1052] [http_request] Bugfix: run update function in a task (#8018) --- .../http_request/http_request_idf.cpp | 24 ++- .../http_request/http_request_idf.h | 3 + .../update/http_request_update.cpp | 147 ++++++++++-------- .../http_request/update/http_request_update.h | 9 ++ 4 files changed, 115 insertions(+), 68 deletions(-) diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index b449f046ee..66f064c2ce 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -12,6 +12,8 @@ #include "esp_crt_bundle.h" #endif +#include "esp_task_wdt.h" + namespace esphome { namespace http_request { @@ -117,11 +119,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } - App.feed_wdt(); + container->feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); - App.feed_wdt(); + container->feed_wdt(); container->status_code = esp_http_client_get_status_code(client); - App.feed_wdt(); + container->feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; @@ -151,11 +153,11 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin return nullptr; } - App.feed_wdt(); + container->feed_wdt(); container->content_length = esp_http_client_fetch_headers(client); - App.feed_wdt(); + container->feed_wdt(); container->status_code = esp_http_client_get_status_code(client); - App.feed_wdt(); + container->feed_wdt(); if (is_success(container->status_code)) { container->duration_ms = millis() - start; return container; @@ -185,8 +187,9 @@ int HttpContainerIDF::read(uint8_t *buf, size_t max_len) { return 0; } - App.feed_wdt(); + this->feed_wdt(); int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize); + this->feed_wdt(); this->bytes_read_ += read_len; this->duration_ms += (millis() - start); @@ -201,6 +204,13 @@ void HttpContainerIDF::end() { esp_http_client_cleanup(this->client_); } +void HttpContainerIDF::feed_wdt() { + // Tests to see if the executing task has a watchdog timer attached + if (esp_task_wdt_status(nullptr) == ESP_OK) { + App.feed_wdt(); + } +} + } // namespace http_request } // namespace esphome diff --git a/esphome/components/http_request/http_request_idf.h b/esphome/components/http_request/http_request_idf.h index 431794924b..2ed50698b9 100644 --- a/esphome/components/http_request/http_request_idf.h +++ b/esphome/components/http_request/http_request_idf.h @@ -18,6 +18,9 @@ class HttpContainerIDF : public HttpContainer { int read(uint8_t *buf, size_t max_len) override; void end() override; + /// @brief Feeds the watchdog timer if the executing task has one attached + void feed_wdt(); + protected: esp_http_client_handle_t client_; }; diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 0e0966c22b..d683495ac6 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -9,6 +9,13 @@ namespace esphome { namespace http_request { +// The update function runs in a task only on ESP32s. +#ifdef USE_ESP32 +#define UPDATE_RETURN vTaskDelete(nullptr) // Delete the current update task +#else +#define UPDATE_RETURN return +#endif + static const char *const TAG = "http_request.update"; static const size_t MAX_READ_SIZE = 256; @@ -29,113 +36,131 @@ void HttpRequestUpdate::setup() { } void HttpRequestUpdate::update() { - auto container = this->request_parent_->get(this->source_url_); +#ifdef USE_ESP32 + xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_); +#else + this->update_task(this); +#endif +} + +void HttpRequestUpdate::update_task(void *params) { + HttpRequestUpdate *this_update = (HttpRequestUpdate *) params; + + auto container = this_update->request_parent_->get(this_update->source_url_); if (container == nullptr || container->status_code != HTTP_STATUS_OK) { - std::string msg = str_sprintf("Failed to fetch manifest from %s", this->source_url_.c_str()); - this->status_set_error(msg.c_str()); - return; + std::string msg = str_sprintf("Failed to fetch manifest from %s", this_update->source_url_.c_str()); + this_update->status_set_error(msg.c_str()); + UPDATE_RETURN; } ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); uint8_t *data = allocator.allocate(container->content_length); if (data == nullptr) { std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length); - this->status_set_error(msg.c_str()); + this_update->status_set_error(msg.c_str()); container->end(); - return; + UPDATE_RETURN; } size_t read_index = 0; while (container->get_bytes_read() < container->content_length) { int read_bytes = container->read(data + read_index, MAX_READ_SIZE); - App.feed_wdt(); yield(); read_index += read_bytes; } - std::string response((char *) data, read_index); - allocator.deallocate(data, container->content_length); + bool valid = false; + { // Ensures the response string falls out of scope and deallocates before the task ends + std::string response((char *) data, read_index); + allocator.deallocate(data, container->content_length); - container->end(); + container->end(); + container.reset(); // Release ownership of the container's shared_ptr - bool valid = json::parse_json(response, [this](JsonObject root) -> bool { - if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) { - ESP_LOGE(TAG, "Manifest does not contain required fields"); - return false; - } - this->update_info_.title = root["name"].as(); - this->update_info_.latest_version = root["version"].as(); - - for (auto build : root["builds"].as()) { - if (!build.containsKey("chipFamily")) { + valid = json::parse_json(response, [this_update](JsonObject root) -> bool { + if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } - if (build["chipFamily"] == ESPHOME_VARIANT) { - if (!build.containsKey("ota")) { + this_update->update_info_.title = root["name"].as(); + this_update->update_info_.latest_version = root["version"].as(); + + for (auto build : root["builds"].as()) { + if (!build.containsKey("chipFamily")) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } - auto ota = build["ota"]; - if (!ota.containsKey("path") || !ota.containsKey("md5")) { - ESP_LOGE(TAG, "Manifest does not contain required fields"); - return false; + if (build["chipFamily"] == ESPHOME_VARIANT) { + if (!build.containsKey("ota")) { + ESP_LOGE(TAG, "Manifest does not contain required fields"); + return false; + } + auto ota = build["ota"]; + if (!ota.containsKey("path") || !ota.containsKey("md5")) { + ESP_LOGE(TAG, "Manifest does not contain required fields"); + return false; + } + this_update->update_info_.firmware_url = ota["path"].as(); + this_update->update_info_.md5 = ota["md5"].as(); + + if (ota.containsKey("summary")) + this_update->update_info_.summary = ota["summary"].as(); + if (ota.containsKey("release_url")) + this_update->update_info_.release_url = ota["release_url"].as(); + + return true; } - this->update_info_.firmware_url = ota["path"].as(); - this->update_info_.md5 = ota["md5"].as(); - - if (ota.containsKey("summary")) - this->update_info_.summary = ota["summary"].as(); - if (ota.containsKey("release_url")) - this->update_info_.release_url = ota["release_url"].as(); - - return true; } - } - return false; - }); + return false; + }); + } if (!valid) { - std::string msg = str_sprintf("Failed to parse JSON from %s", this->source_url_.c_str()); - this->status_set_error(msg.c_str()); - return; + std::string msg = str_sprintf("Failed to parse JSON from %s", this_update->source_url_.c_str()); + this_update->status_set_error(msg.c_str()); + UPDATE_RETURN; } - // Merge source_url_ and this->update_info_.firmware_url - if (this->update_info_.firmware_url.find("http") == std::string::npos) { - std::string path = this->update_info_.firmware_url; + // Merge source_url_ and this_update->update_info_.firmware_url + if (this_update->update_info_.firmware_url.find("http") == std::string::npos) { + std::string path = this_update->update_info_.firmware_url; if (path[0] == '/') { - std::string domain = this->source_url_.substr(0, this->source_url_.find('/', 8)); - this->update_info_.firmware_url = domain + path; + std::string domain = this_update->source_url_.substr(0, this_update->source_url_.find('/', 8)); + this_update->update_info_.firmware_url = domain + path; } else { - std::string domain = this->source_url_.substr(0, this->source_url_.rfind('/') + 1); - this->update_info_.firmware_url = domain + path; + std::string domain = this_update->source_url_.substr(0, this_update->source_url_.rfind('/') + 1); + this_update->update_info_.firmware_url = domain + path; } } - std::string current_version; + { // Ensures the current version string falls out of scope and deallocates before the task ends + std::string current_version; #ifdef ESPHOME_PROJECT_VERSION - current_version = ESPHOME_PROJECT_VERSION; + current_version = ESPHOME_PROJECT_VERSION; #else - current_version = ESPHOME_VERSION; + current_version = ESPHOME_VERSION; #endif - this->update_info_.current_version = current_version; - - if (this->update_info_.latest_version.empty() || this->update_info_.latest_version == update_info_.current_version) { - this->state_ = update::UPDATE_STATE_NO_UPDATE; - } else { - this->state_ = update::UPDATE_STATE_AVAILABLE; + this_update->update_info_.current_version = current_version; } - this->update_info_.has_progress = false; - this->update_info_.progress = 0.0f; + if (this_update->update_info_.latest_version.empty() || + this_update->update_info_.latest_version == this_update->update_info_.current_version) { + this_update->state_ = update::UPDATE_STATE_NO_UPDATE; + } else { + this_update->state_ = update::UPDATE_STATE_AVAILABLE; + } - this->status_clear_error(); - this->publish_state(); + this_update->update_info_.has_progress = false; + this_update->update_info_.progress = 0.0f; + + this_update->status_clear_error(); + this_update->publish_state(); + + UPDATE_RETURN; } void HttpRequestUpdate::perform(bool force) { diff --git a/esphome/components/http_request/update/http_request_update.h b/esphome/components/http_request/update/http_request_update.h index 45c7e6a447..e05fdb0cc2 100644 --- a/esphome/components/http_request/update/http_request_update.h +++ b/esphome/components/http_request/update/http_request_update.h @@ -7,6 +7,10 @@ #include "esphome/components/http_request/ota/ota_http_request.h" #include "esphome/components/update/update_entity.h" +#ifdef USE_ESP32 +#include +#endif + namespace esphome { namespace http_request { @@ -29,6 +33,11 @@ class HttpRequestUpdate : public update::UpdateEntity, public PollingComponent { HttpRequestComponent *request_parent_; OtaHttpRequestComponent *ota_parent_; std::string source_url_; + + static void update_task(void *params); +#ifdef USE_ESP32 + TaskHandle_t update_task_handle_{nullptr}; +#endif }; } // namespace http_request From 7666581c547612984eaa7fb049ac0b5985c68066 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:24:22 +1300 Subject: [PATCH 0896/1052] Bump version to 2024.12.3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 10ad9454fd..a86e20c2e5 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.2" +__version__ = "2024.12.3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From c2423b18cbd279d4545ca672bebb665944e4b378 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:32:10 +1300 Subject: [PATCH 0897/1052] Bump python3-setuptools to 66.1.1-1+deb12u1 (#8074) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0bb558d35e..429f5c4a1f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,7 +29,7 @@ RUN \ # Use pinned versions so that we get updates with build caching && apt-get install -y --no-install-recommends \ python3-pip=23.0.1+dfsg-1 \ - python3-setuptools=66.1.1-1 \ + python3-setuptools=66.1.1-1+deb12u1 \ python3-venv=3.11.2-1+b1 \ python3-wheel=0.38.4-2 \ iputils-ping=3:20221126-1+deb12u1 \ From 5a01670803ff6e78a5e605613f4855c6c16e33e1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 17 Jan 2025 13:40:12 +1300 Subject: [PATCH 0898/1052] Bump version to 2024.12.4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index a86e20c2e5..b3d64997c4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2024.12.3" +__version__ = "2024.12.4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 47a0ec467abd812e85f6a65946f3922f31ac5981 Mon Sep 17 00:00:00 2001 From: guillempages Date: Sun, 19 Jan 2025 22:34:38 +0100 Subject: [PATCH 0899/1052] [image]Rename option "use_transparency" (#8113) --- esphome/components/image/__init__.py | 14 ++++++------- esphome/components/online_image/__init__.py | 4 ++-- tests/components/animation/common.yaml | 4 ++-- tests/components/image/common.yaml | 22 ++++++++++----------- tests/components/image/test.host.yaml | 14 ++++++------- tests/components/online_image/common.yaml | 6 +++--- 6 files changed, 31 insertions(+), 33 deletions(-) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index a503e8f471..7cdce757be 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -282,7 +282,7 @@ IMAGE_TYPE = { TransparencyType = image_ns.enum("TransparencyType") -CONF_USE_TRANSPARENCY = "use_transparency" +CONF_TRANSPARENCY = "transparency" # If the MDI file cannot be downloaded within this time, abort. IMAGE_DOWNLOAD_TIMEOUT = 30 # seconds @@ -417,7 +417,7 @@ def validate_type(image_types): def validate_settings(value): type = value[CONF_TYPE] - transparency = value[CONF_USE_TRANSPARENCY].lower() + transparency = value[CONF_TRANSPARENCY].lower() allow_config = IMAGE_TYPE[type].allow_config if transparency not in allow_config: raise cv.Invalid( @@ -458,9 +458,7 @@ BASE_SCHEMA = cv.Schema( IMAGE_SCHEMA = BASE_SCHEMA.extend( { cv.Required(CONF_TYPE): validate_type(IMAGE_TYPE), - cv.Optional( - CONF_USE_TRANSPARENCY, default=CONF_OPAQUE - ): validate_transparency(), + cv.Optional(CONF_TRANSPARENCY, default=CONF_OPAQUE): validate_transparency(), } ) @@ -476,7 +474,7 @@ def typed_image_schema(image_type): BASE_SCHEMA.extend( { cv.Optional( - CONF_USE_TRANSPARENCY, default=t + CONF_TRANSPARENCY, default=t ): validate_transparency((t,)), cv.Optional(CONF_TYPE, default=image_type): validate_type( (image_type,) @@ -494,7 +492,7 @@ def typed_image_schema(image_type): BASE_SCHEMA.extend( { cv.Optional( - CONF_USE_TRANSPARENCY, default=CONF_OPAQUE + CONF_TRANSPARENCY, default=CONF_OPAQUE ): validate_transparency(), cv.Optional(CONF_TYPE, default=image_type): validate_type( (image_type,) @@ -556,7 +554,7 @@ async def write_image(config, all_frames=False): else Image.Dither.FLOYDSTEINBERG ) type = config[CONF_TYPE] - transparency = config[CONF_USE_TRANSPARENCY] + transparency = config[CONF_TRANSPARENCY] invert_alpha = config[CONF_INVERT_ALPHA] frame_count = 1 if all_frames: diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index d1915c7364..ca4eefea6f 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -5,7 +5,7 @@ import esphome.codegen as cg from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent from esphome.components.image import ( CONF_INVERT_ALPHA, - CONF_USE_TRANSPARENCY, + CONF_TRANSPARENCY, IMAGE_SCHEMA, Image_, get_image_type_enum, @@ -168,7 +168,7 @@ async def to_code(config): url = config[CONF_URL] width, height = config.get(CONF_RESIZE, (0, 0)) - transparent = get_transparency_enum(config[CONF_USE_TRANSPARENCY]) + transparent = get_transparency_enum(config[CONF_TRANSPARENCY]) var = cg.new_Pvariable( config[CONF_ID], diff --git a/tests/components/animation/common.yaml b/tests/components/animation/common.yaml index c0f04fe768..8bb2a2f4d8 100644 --- a/tests/components/animation/common.yaml +++ b/tests/components/animation/common.yaml @@ -2,12 +2,12 @@ animation: - id: rgb565_animation file: $component_dir/anim.gif type: RGB565 - use_transparency: opaque + transparency: opaque resize: 50x50 - id: rgb_animation file: $component_dir/anim.apng type: RGB - use_transparency: chroma_key + transparency: chroma_key resize: 50x50 - id: grayscale_animation file: $component_dir/anim.apng diff --git a/tests/components/image/common.yaml b/tests/components/image/common.yaml index fdb0493d2a..4c9b9ed670 100644 --- a/tests/components/image/common.yaml +++ b/tests/components/image/common.yaml @@ -6,54 +6,54 @@ image: - id: transparent_transparent_image file: ../../pnglogo.png type: BINARY - use_transparency: chroma_key + transparency: chroma_key - id: rgba_image file: ../../pnglogo.png type: RGB - use_transparency: alpha_channel + transparency: alpha_channel resize: 50x50 - id: rgb24_image file: ../../pnglogo.png type: RGB - use_transparency: chroma_key + transparency: chroma_key - id: rgb_image file: ../../pnglogo.png type: RGB - use_transparency: opaque + transparency: opaque - id: rgb565_image file: ../../pnglogo.png type: RGB565 - use_transparency: opaque + transparency: opaque - id: rgb565_ck_image file: ../../pnglogo.png type: RGB565 - use_transparency: chroma_key + transparency: chroma_key - id: rgb565_alpha_image file: ../../pnglogo.png type: RGB565 - use_transparency: alpha_channel + transparency: alpha_channel - id: grayscale_alpha_image file: ../../pnglogo.png type: grayscale - use_transparency: alpha_channel + transparency: alpha_channel resize: 50x50 - id: grayscale_ck_image file: ../../pnglogo.png type: grayscale - use_transparency: chroma_key + transparency: chroma_key - id: grayscale_image file: ../../pnglogo.png type: grayscale - use_transparency: opaque + transparency: opaque - id: web_svg_image file: https://raw.githubusercontent.com/esphome/esphome-docs/a62d7ab193c1a464ed791670170c7d518189109b/images/logo.svg resize: 256x48 type: BINARY - use_transparency: chroma_key + transparency: chroma_key - id: web_tiff_image file: https://upload.wikimedia.org/wikipedia/commons/b/b6/SIPI_Jelly_Beans_4.1.07.tiff type: RGB diff --git a/tests/components/image/test.host.yaml b/tests/components/image/test.host.yaml index 61ecd5e374..0411195e2a 100644 --- a/tests/components/image/test.host.yaml +++ b/tests/components/image/test.host.yaml @@ -12,7 +12,7 @@ image: dither: FloydSteinberg - id: transparent_transparent_image file: ../../pnglogo.png - use_transparency: chroma_key + transparency: chroma_key rgb: alpha_channel: - id: rgba_image @@ -28,21 +28,21 @@ image: rgb565: - id: rgb565_image file: ../../pnglogo.png - use_transparency: opaque + transparency: opaque - id: rgb565_ck_image file: ../../pnglogo.png - use_transparency: chroma_key + transparency: chroma_key - id: rgb565_alpha_image file: ../../pnglogo.png - use_transparency: alpha_channel + transparency: alpha_channel grayscale: - id: grayscale_alpha_image file: ../../pnglogo.png - use_transparency: alpha_channel + transparency: alpha_channel resize: 50x50 - id: grayscale_ck_image file: ../../pnglogo.png - use_transparency: chroma_key + transparency: chroma_key - id: grayscale_image file: ../../pnglogo.png - use_transparency: opaque + transparency: opaque diff --git a/tests/components/online_image/common.yaml b/tests/components/online_image/common.yaml index 81f43e9fdc..6c161e4f20 100644 --- a/tests/components/online_image/common.yaml +++ b/tests/components/online_image/common.yaml @@ -14,18 +14,18 @@ online_image: - id: online_binary_transparent_image url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png type: BINARY - use_transparency: chroma_key + transparency: chroma_key format: png - id: online_rgba_image url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png format: PNG type: RGB - use_transparency: alpha_channel + transparency: alpha_channel - id: online_rgb24_image url: http://www.libpng.org/pub/png/img_png/pnglogo-blk-tiny.png format: PNG type: RGB - use_transparency: chroma_key + transparency: chroma_key # Check the set_url action esphome: From 75026be9519a17f3f502dcaea3f21ec5f0c2f917 Mon Sep 17 00:00:00 2001 From: guillempages Date: Sun, 19 Jan 2025 23:16:37 +0100 Subject: [PATCH 0900/1052] [online_image] Use RAMAllocator (#8114) --- esphome/components/online_image/image_decoder.h | 2 +- esphome/components/online_image/online_image.h | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/esphome/components/online_image/image_decoder.h b/esphome/components/online_image/image_decoder.h index cde7f572e3..7c5175d72d 100644 --- a/esphome/components/online_image/image_decoder.h +++ b/esphome/components/online_image/image_decoder.h @@ -100,7 +100,7 @@ class DownloadBuffer { void reset() { this->unread_ = 0; } protected: - ExternalRAMAllocator allocator_; + RAMAllocator allocator_{}; uint8_t *buffer_; size_t size_; /** Total number of downloaded bytes not yet read. */ diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index e044b4f390..bafd8ba67e 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -83,8 +83,7 @@ class OnlineImage : public PollingComponent, protected: bool validate_url_(const std::string &url); - using Allocator = ExternalRAMAllocator; - Allocator allocator_{Allocator::Flags::ALLOW_FAILURE}; + RAMAllocator allocator_{}; uint32_t get_buffer_size_() const { return get_buffer_size_(this->buffer_width_, this->buffer_height_); } int get_buffer_size_(int width, int height) const { return (this->get_bpp() * width + 7u) / 8u * height; } From 98b872abc71b555640a9465fd30e3ba184ab49be Mon Sep 17 00:00:00 2001 From: Mikkel Jeppesen <2756925+Duckle29@users.noreply.github.com> Date: Sun, 19 Jan 2025 23:36:07 +0100 Subject: [PATCH 0901/1052] Fixed incorrect display dimension (#8110) --- esphome/components/waveshare_epaper/waveshare_213v3.cpp | 6 ++++-- esphome/components/waveshare_epaper/waveshare_epaper.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/esphome/components/waveshare_epaper/waveshare_213v3.cpp b/esphome/components/waveshare_epaper/waveshare_213v3.cpp index 196aeed3f7..85d7033d4b 100644 --- a/esphome/components/waveshare_epaper/waveshare_213v3.cpp +++ b/esphome/components/waveshare_epaper/waveshare_213v3.cpp @@ -72,7 +72,8 @@ void WaveshareEPaper2P13InV3::write_buffer_(uint8_t cmd, int top, int bottom) { this->set_window_(top, bottom); this->command(cmd); this->start_data_(); - auto width_bytes = this->get_width_internal() / 8; + + auto width_bytes = this->get_width_controller() / 8; this->write_array(this->buffer_ + top * width_bytes, (bottom - top) * width_bytes); this->end_data_(); } @@ -162,7 +163,8 @@ void WaveshareEPaper2P13InV3::display() { } } -int WaveshareEPaper2P13InV3::get_width_internal() { return 128; } +int WaveshareEPaper2P13InV3::get_width_controller() { return 128; } +int WaveshareEPaper2P13InV3::get_width_internal() { return 122; } int WaveshareEPaper2P13InV3::get_height_internal() { return 250; } diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 4544f7df59..0fc1051268 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -811,6 +811,7 @@ class WaveshareEPaper2P13InV3 : public WaveshareEPaper { void initialize() override; protected: + int get_width_controller() override; int get_width_internal() override; int get_height_internal() override; uint32_t idle_timeout_() override; From c3d00b45f7703666fc06289d2ff84b72ce800ea5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:50:04 +1300 Subject: [PATCH 0902/1052] Update defines.h for esp-idf 5.1.5 (#8117) --- esphome/core/defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/defines.h b/esphome/core/defines.h index c38a26c6a8..96a05435ed 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -120,7 +120,7 @@ #endif #ifdef USE_ESP_IDF -#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(4, 4, 2) +#define USE_ESP_IDF_VERSION_CODE VERSION_CODE(5, 1, 5) #endif #if defined(USE_ESP32_VARIANT_ESP32S2) From 576dbd6f0c33ccddb2be4dcffd79e39749857b21 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 20 Jan 2025 20:35:40 -0600 Subject: [PATCH 0903/1052] [audio_adc] Add new ``audio_adc`` component (#8094) --- CODEOWNERS | 1 + esphome/components/audio_adc/__init__.py | 41 ++++++++++++++ esphome/components/audio_adc/audio_adc.h | 17 ++++++ esphome/components/audio_adc/automation.h | 23 ++++++++ esphome/components/es7210/__init__.py | 67 ----------------------- esphome/components/es7210/audio_adc.py | 51 +++++++++++++++++ esphome/components/es7210/es7210.cpp | 43 ++++++++++++--- esphome/components/es7210/es7210.h | 39 ++++++------- esphome/components/es7210/es7210_const.h | 7 ++- tests/components/es7210/common.yaml | 12 +++- 10 files changed, 200 insertions(+), 101 deletions(-) create mode 100644 esphome/components/audio_adc/__init__.py create mode 100644 esphome/components/audio_adc/audio_adc.h create mode 100644 esphome/components/audio_adc/automation.h create mode 100644 esphome/components/es7210/audio_adc.py diff --git a/CODEOWNERS b/CODEOWNERS index f0075549fd..56e20133ef 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -49,6 +49,7 @@ esphome/components/atc_mithermometer/* @ahpohl esphome/components/atm90e26/* @danieltwagner esphome/components/atm90e32/* @circuitsetup @descipher esphome/components/audio/* @kahrendt +esphome/components/audio_adc/* @kbx81 esphome/components/audio_dac/* @kbx81 esphome/components/axs15231/* @clydebarrow esphome/components/b_parasite/* @rbaron diff --git a/esphome/components/audio_adc/__init__.py b/esphome/components/audio_adc/__init__.py new file mode 100644 index 0000000000..dd3c958821 --- /dev/null +++ b/esphome/components/audio_adc/__init__.py @@ -0,0 +1,41 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_MIC_GAIN +from esphome.core import coroutine_with_priority + +CODEOWNERS = ["@kbx81"] +IS_PLATFORM_COMPONENT = True + +audio_adc_ns = cg.esphome_ns.namespace("audio_adc") +AudioAdc = audio_adc_ns.class_("AudioAdc") + +SetMicGainAction = audio_adc_ns.class_("SetMicGainAction", automation.Action) + + +SET_MIC_GAIN_ACTION_SCHEMA = cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(AudioAdc), + cv.Required(CONF_MIC_GAIN): cv.templatable(cv.decibel), + }, + key=CONF_MIC_GAIN, +) + + +@automation.register_action( + "audio_adc.set_mic_gain", SetMicGainAction, SET_MIC_GAIN_ACTION_SCHEMA +) +async def audio_adc_set_mic_gain_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + + template_ = await cg.templatable(config.get(CONF_MIC_GAIN), args, float) + cg.add(var.set_mic_gain(template_)) + + return var + + +@coroutine_with_priority(100.0) +async def to_code(config): + cg.add_define("USE_AUDIO_ADC") + cg.add_global(audio_adc_ns.using) diff --git a/esphome/components/audio_adc/audio_adc.h b/esphome/components/audio_adc/audio_adc.h new file mode 100644 index 0000000000..94bfb57db5 --- /dev/null +++ b/esphome/components/audio_adc/audio_adc.h @@ -0,0 +1,17 @@ +#pragma once + +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace audio_adc { + +class AudioAdc { + public: + virtual bool set_mic_gain(float mic_gain) = 0; + + virtual float mic_gain() = 0; +}; + +} // namespace audio_adc +} // namespace esphome diff --git a/esphome/components/audio_adc/automation.h b/esphome/components/audio_adc/automation.h new file mode 100644 index 0000000000..1b0bc2a6ad --- /dev/null +++ b/esphome/components/audio_adc/automation.h @@ -0,0 +1,23 @@ +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "audio_adc.h" + +namespace esphome { +namespace audio_adc { + +template class SetMicGainAction : public Action { + public: + explicit SetMicGainAction(AudioAdc *audio_adc) : audio_adc_(audio_adc) {} + + TEMPLATABLE_VALUE(float, mic_gain) + + void play(Ts... x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); } + + protected: + AudioAdc *audio_adc_; +}; + +} // namespace audio_adc +} // namespace esphome diff --git a/esphome/components/es7210/__init__.py b/esphome/components/es7210/__init__.py index 8e63d7f04f..e69de29bb2 100644 --- a/esphome/components/es7210/__init__.py +++ b/esphome/components/es7210/__init__.py @@ -1,67 +0,0 @@ -import esphome.codegen as cg -from esphome.components import i2c -import esphome.config_validation as cv -from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE - -CODEOWNERS = ["@kahrendt"] -DEPENDENCIES = ["i2c"] - -es7210_ns = cg.esphome_ns.namespace("es7210") -ES7210 = es7210_ns.class_("ES7210", cg.Component, i2c.I2CDevice) - - -es7210_bits_per_sample = es7210_ns.enum("ES7210BitsPerSample") -ES7210_BITS_PER_SAMPLE_ENUM = { - 16: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_16, - 24: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_24, - 32: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_32, -} - - -es7210_mic_gain = es7210_ns.enum("ES7210MicGain") -ES7210_MIC_GAIN_ENUM = { - "0DB": es7210_mic_gain.ES7210_MIC_GAIN_0DB, - "3DB": es7210_mic_gain.ES7210_MIC_GAIN_3DB, - "6DB": es7210_mic_gain.ES7210_MIC_GAIN_6DB, - "9DB": es7210_mic_gain.ES7210_MIC_GAIN_9DB, - "12DB": es7210_mic_gain.ES7210_MIC_GAIN_12DB, - "15DB": es7210_mic_gain.ES7210_MIC_GAIN_15DB, - "18DB": es7210_mic_gain.ES7210_MIC_GAIN_18DB, - "21DB": es7210_mic_gain.ES7210_MIC_GAIN_21DB, - "24DB": es7210_mic_gain.ES7210_MIC_GAIN_24DB, - "27DB": es7210_mic_gain.ES7210_MIC_GAIN_27DB, - "30DB": es7210_mic_gain.ES7210_MIC_GAIN_30DB, - "33DB": es7210_mic_gain.ES7210_MIC_GAIN_33DB, - "34.5DB": es7210_mic_gain.ES7210_MIC_GAIN_34_5DB, - "36DB": es7210_mic_gain.ES7210_MIC_GAIN_36DB, - "37.5DB": es7210_mic_gain.ES7210_MIC_GAIN_37_5DB, -} - -_validate_bits = cv.float_with_unit("bits", "bit") - -CONFIG_SCHEMA = ( - cv.Schema( - { - cv.GenerateID(): cv.declare_id(ES7210), - cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All( - _validate_bits, cv.enum(ES7210_BITS_PER_SAMPLE_ENUM) - ), - cv.Optional(CONF_MIC_GAIN, default="24DB"): cv.enum( - ES7210_MIC_GAIN_ENUM, upper=True - ), - cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1), - } - ) - .extend(cv.COMPONENT_SCHEMA) - .extend(i2c.i2c_device_schema(0x40)) -) - - -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_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) - cg.add(var.set_mic_gain(config[CONF_MIC_GAIN])) - cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) diff --git a/esphome/components/es7210/audio_adc.py b/esphome/components/es7210/audio_adc.py new file mode 100644 index 0000000000..f0bd8bc25a --- /dev/null +++ b/esphome/components/es7210/audio_adc.py @@ -0,0 +1,51 @@ +import esphome.codegen as cg +from esphome.components import i2c +from esphome.components.audio_adc import AudioAdc +import esphome.config_validation as cv +from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_MIC_GAIN, CONF_SAMPLE_RATE + +CODEOWNERS = ["@kahrendt"] +DEPENDENCIES = ["i2c"] + +es7210_ns = cg.esphome_ns.namespace("es7210") +ES7210 = es7210_ns.class_("ES7210", AudioAdc, cg.Component, i2c.I2CDevice) + + +es7210_bits_per_sample = es7210_ns.enum("ES7210BitsPerSample") +ES7210_BITS_PER_SAMPLE_ENUM = { + 16: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_16, + 24: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_24, + 32: es7210_bits_per_sample.ES7210_BITS_PER_SAMPLE_32, +} + + +ES7210_MIC_GAINS = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 34.5, 36, 37.5] + +_validate_bits = cv.float_with_unit("bits", "bit") + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ES7210), + cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All( + _validate_bits, cv.enum(ES7210_BITS_PER_SAMPLE_ENUM) + ), + cv.Optional(CONF_MIC_GAIN, default="24db"): cv.All( + cv.decibel, cv.one_of(*ES7210_MIC_GAINS) + ), + cv.Optional(CONF_SAMPLE_RATE, default=16000): cv.int_range(min=1), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x40)) +) + + +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_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) + cg.add(var.set_mic_gain(config[CONF_MIC_GAIN])) + cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE])) diff --git a/esphome/components/es7210/es7210.cpp b/esphome/components/es7210/es7210.cpp index d2f2c3c1ff..9a99e60995 100644 --- a/esphome/components/es7210/es7210.cpp +++ b/esphome/components/es7210/es7210.cpp @@ -25,12 +25,12 @@ static const size_t MCLK_DIV_FRE = 256; } void ES7210::dump_config() { - ESP_LOGCONFIG(TAG, "ES7210 ADC:"); + 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_); if (this->is_failed()) { - ESP_LOGCONFIG(TAG, " Failed to initialize!"); + ESP_LOGE(TAG, " Failed to initialize"); return; } } @@ -84,6 +84,16 @@ void ES7210::setup() { // Enable device ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x71)); ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0x41)); + + this->setup_complete_ = true; +} + +bool ES7210::set_mic_gain(float mic_gain) { + this->mic_gain_ = clamp(mic_gain, ES7210_MIC_GAIN_MIN, ES7210_MIC_GAIN_MAX); + if (this->setup_complete_) { + return this->configure_mic_gain_(); + } + return true; } bool ES7210::configure_sample_rate_() { @@ -122,9 +132,11 @@ bool ES7210::configure_sample_rate_() { return true; } + bool ES7210::configure_mic_gain_() { - for (int i = 0; i < 4; ++i) { - this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00); + auto regv = this->es7210_gain_reg_value_(this->mic_gain_); + for (uint8_t i = 0; i < 4; ++i) { + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00)); } ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0xff)); ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0xff)); @@ -133,29 +145,44 @@ bool ES7210::configure_mic_gain_() { ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00)); ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x10, 0x10)); - ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x0f, this->mic_gain_)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC1_GAIN_REG43, 0x0f, regv)); // Configure mic 2 ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC12_POWER_REG4B, 0x00)); ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x10, 0x10)); - ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x0f, this->mic_gain_)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC2_GAIN_REG44, 0x0f, regv)); // Configure mic 3 ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00)); ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x10, 0x10)); - ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x0f, this->mic_gain_)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC3_GAIN_REG45, 0x0f, regv)); // Configure mic 4 ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00)); ES7210_ERROR_CHECK(this->write_byte(ES7210_MIC34_POWER_REG4C, 0x00)); ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x10, 0x10)); - ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x0f, this->mic_gain_)); + ES7210_ERROR_CHECK(this->es7210_update_reg_bit_(ES7210_MIC4_GAIN_REG46, 0x0f, regv)); return true; } +uint8_t ES7210::es7210_gain_reg_value_(float mic_gain) { + // reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB + mic_gain += 0.5; + if (mic_gain <= 33.0) { + return (uint8_t) mic_gain / 3; + } + if (mic_gain < 36.0) { + return 12; + } + if (mic_gain < 37.0) { + return 13; + } + return 14; +} + bool ES7210::configure_i2s_format_() { // Configure bits per sample uint8_t reg_val = 0; diff --git a/esphome/components/es7210/es7210.h b/esphome/components/es7210/es7210.h index a40dde5aa5..8f6d9d8136 100644 --- a/esphome/components/es7210/es7210.h +++ b/esphome/components/es7210/es7210.h @@ -1,8 +1,11 @@ #pragma once +#include "esphome/components/audio_adc/audio_adc.h" #include "esphome/components/i2c/i2c.h" #include "esphome/core/component.h" +#include "es7210_const.h" + namespace esphome { namespace es7210 { @@ -14,25 +17,7 @@ enum ES7210BitsPerSample : uint8_t { ES7210_BITS_PER_SAMPLE_32 = 32, }; -enum ES7210MicGain : uint8_t { - ES7210_MIC_GAIN_0DB = 0, - ES7210_MIC_GAIN_3DB, - ES7210_MIC_GAIN_6DB, - ES7210_MIC_GAIN_9DB, - ES7210_MIC_GAIN_12DB, - ES7210_MIC_GAIN_15DB, - ES7210_MIC_GAIN_18DB, - ES7210_MIC_GAIN_21DB, - ES7210_MIC_GAIN_24DB, - ES7210_MIC_GAIN_27DB, - ES7210_MIC_GAIN_30DB, - ES7210_MIC_GAIN_33DB, - ES7210_MIC_GAIN_34_5DB, - ES7210_MIC_GAIN_36DB, - ES7210_MIC_GAIN_37_5DB, -}; - -class ES7210 : public Component, public i2c::I2CDevice { +class ES7210 : public audio_adc::AudioAdc, public Component, public i2c::I2CDevice { /* Class for configuring an ES7210 ADC for microphone input. * Based on code from: * - https://github.com/espressif/esp-bsp/ (accessed 20241219) @@ -44,9 +29,11 @@ class ES7210 : public Component, public i2c::I2CDevice { void dump_config() override; void set_bits_per_sample(ES7210BitsPerSample bits_per_sample) { this->bits_per_sample_ = bits_per_sample; } - void set_mic_gain(ES7210MicGain mic_gain) { this->mic_gain_ = mic_gain; } + bool set_mic_gain(float mic_gain) override; void set_sample_rate(uint32_t sample_rate) { this->sample_rate_ = sample_rate; } + float mic_gain() override { return this->mic_gain_; }; + protected: /// @brief Updates an I2C registry address by modifying the current state /// @param reg_addr I2C register address @@ -55,14 +42,20 @@ class ES7210 : public Component, public i2c::I2CDevice { /// @return True if successful, false otherwise bool es7210_update_reg_bit_(uint8_t reg_addr, uint8_t update_bits, uint8_t data); + /// @brief Convert floating point mic gain value to register value + /// @param mic_gain Gain value to convert + /// @return Corresponding register value for specified gain + uint8_t es7210_gain_reg_value_(float mic_gain); + bool configure_i2s_format_(); bool configure_mic_gain_(); bool configure_sample_rate_(); + bool setup_complete_{false}; bool enable_tdm_{false}; // TDM is unsupported in ESPHome as of version 2024.12 - ES7210MicGain mic_gain_; - ES7210BitsPerSample bits_per_sample_; - uint32_t sample_rate_; + float mic_gain_{0}; + ES7210BitsPerSample bits_per_sample_{ES7210_BITS_PER_SAMPLE_16}; + uint32_t sample_rate_{0}; }; } // namespace es7210 diff --git a/esphome/components/es7210/es7210_const.h b/esphome/components/es7210/es7210_const.h index 87fd6d86d2..e5ffea5743 100644 --- a/esphome/components/es7210/es7210_const.h +++ b/esphome/components/es7210/es7210_const.h @@ -1,6 +1,6 @@ #pragma once -#include "es7210.h" +#include namespace esphome { namespace es7210 { @@ -42,7 +42,7 @@ static const uint8_t ES7210_MIC12_POWER_REG4B = 0x4B; /* MICBias & ADC & PGA Pow static const uint8_t ES7210_MIC34_POWER_REG4C = 0x4C; /* - * Clock coefficient structer + * Clock coefficient structure */ struct ES7210Coefficient { uint32_t mclk; // mclk frequency @@ -122,5 +122,8 @@ static const ES7210Coefficient ES7210_COEFFICIENTS[] = { {19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8}, }; +static const float ES7210_MIC_GAIN_MIN = 0.0; +static const float ES7210_MIC_GAIN_MAX = 37.5; + } // namespace es7210 } // namespace esphome diff --git a/tests/components/es7210/common.yaml b/tests/components/es7210/common.yaml index 5c30f7e883..3fab177cb3 100644 --- a/tests/components/es7210/common.yaml +++ b/tests/components/es7210/common.yaml @@ -1,6 +1,16 @@ +esphome: + on_boot: + then: + - audio_adc.set_mic_gain: 0db + - audio_adc.set_mic_gain: !lambda 'return 4;' + i2c: - id: i2c_aic3204 scl: ${scl_pin} sda: ${sda_pin} -es7210: +audio_adc: + - platform: es7210 + id: es7210_adc + bits_per_sample: 16bit + sample_rate: 16000 From 0f4e274e522bb403b2e73fb535e0743c4129e2c4 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:43:50 +1100 Subject: [PATCH 0904/1052] [uptime] Cosmetic improvements for uptime text_sensor (#8101) --- .../components/uptime/text_sensor/__init__.py | 2 +- .../uptime/text_sensor/uptime_text_sensor.cpp | 59 ++++++++++++------- .../uptime/text_sensor/uptime_text_sensor.h | 4 +- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/esphome/components/uptime/text_sensor/__init__.py b/esphome/components/uptime/text_sensor/__init__.py index 996d983e71..e4a7ac6517 100644 --- a/esphome/components/uptime/text_sensor/__init__.py +++ b/esphome/components/uptime/text_sensor/__init__.py @@ -11,7 +11,7 @@ CONFIG_SCHEMA = text_sensor.text_sensor_schema( UptimeTextSensor, icon=ICON_TIMER, entity_category=ENTITY_CATEGORY_DIAGNOSTIC, -).extend(cv.polling_component_schema("60s")) +).extend(cv.polling_component_schema("30s")) async def to_code(config): diff --git a/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp b/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp index 0fa5e199f3..409af6e4ff 100644 --- a/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +++ b/esphome/components/uptime/text_sensor/uptime_text_sensor.cpp @@ -9,34 +9,51 @@ namespace uptime { static const char *const TAG = "uptime.sensor"; -void UptimeTextSensor::setup() { this->last_ms_ = millis(); } +void UptimeTextSensor::setup() { + this->last_ms_ = millis(); + if (this->last_ms_ < 60 * 1000) + this->last_ms_ = 0; + this->update(); +} void UptimeTextSensor::update() { - const auto now = millis(); + auto now = millis(); // get whole seconds since last update. Note that even if the millis count has overflowed between updates, // the difference will still be correct due to the way twos-complement arithmetic works. - const uint32_t delta = (now - this->last_ms_) / 1000; - if (delta == 0) - return; - // set last_ms_ to the last second boundary - this->last_ms_ = now - (now % 1000); + uint32_t delta = now - this->last_ms_; + this->last_ms_ = now - delta % 1000; // save remainder for next update + delta /= 1000; this->uptime_ += delta; auto uptime = this->uptime_; - unsigned days = uptime / (24 * 3600); - unsigned seconds = uptime % (24 * 3600); - unsigned hours = seconds / 3600; - seconds %= 3600; - unsigned minutes = seconds / 60; - seconds %= 60; - if (days != 0) { - this->publish_state(str_sprintf("%dd%dh%dm%ds", days, hours, minutes, seconds)); - } else if (hours != 0) { - this->publish_state(str_sprintf("%dh%dm%ds", hours, minutes, seconds)); - } else if (minutes != 0) { - this->publish_state(str_sprintf("%dm%ds", minutes, seconds)); - } else { - this->publish_state(str_sprintf("%ds", seconds)); + unsigned interval = this->get_update_interval() / 1000; + std::string buffer{}; + // display from the largest unit that corresponds to the update interval, drop larger units that are zero. + while (true) { // enable use of break for early exit + unsigned remainder = uptime % 60; + uptime /= 60; + if (interval < 30) { + buffer.insert(0, str_sprintf("%us", remainder)); + if (uptime == 0) + break; + } + remainder = uptime % 60; + uptime /= 60; + if (interval < 1800) { + buffer.insert(0, str_sprintf("%um", remainder)); + if (uptime == 0) + break; + } + remainder = uptime % 24; + uptime /= 24; + if (interval < 12 * 3600) { + buffer.insert(0, str_sprintf("%uh", remainder)); + if (uptime == 0) + break; + } + buffer.insert(0, str_sprintf("%ud", (unsigned) uptime)); + break; } + this->publish_state(buffer); } float UptimeTextSensor::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/uptime/text_sensor/uptime_text_sensor.h b/esphome/components/uptime/text_sensor/uptime_text_sensor.h index 4baf1039b6..5719ef38a2 100644 --- a/esphome/components/uptime/text_sensor/uptime_text_sensor.h +++ b/esphome/components/uptime/text_sensor/uptime_text_sensor.h @@ -17,8 +17,8 @@ class UptimeTextSensor : public text_sensor::TextSensor, public PollingComponent float get_setup_priority() const override; protected: - uint64_t uptime_{0}; - uint64_t last_ms_{0}; + uint32_t uptime_{0}; // uptime in seconds, will overflow after 136 years + uint32_t last_ms_{0}; }; } // namespace uptime From 716a8b87e1b033b454e3a1f50e3e37bb37177403 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Mon, 20 Jan 2025 21:15:18 -0600 Subject: [PATCH 0905/1052] [es8156] Add support for ES8156 audio DAC (#8085) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/es8156/__init__.py | 0 esphome/components/es8156/audio_dac.py | 27 ++++++ esphome/components/es8156/es8156.cpp | 87 +++++++++++++++++++ esphome/components/es8156/es8156.h | 51 +++++++++++ esphome/components/es8156/es8156_const.h | 68 +++++++++++++++ tests/components/es8156/common.yaml | 15 ++++ tests/components/es8156/test.esp32-ard.yaml | 5 ++ .../components/es8156/test.esp32-c3-ard.yaml | 5 ++ .../components/es8156/test.esp32-c3-idf.yaml | 5 ++ tests/components/es8156/test.esp32-idf.yaml | 5 ++ tests/components/es8156/test.esp8266-ard.yaml | 5 ++ 12 files changed, 274 insertions(+) create mode 100644 esphome/components/es8156/__init__.py create mode 100644 esphome/components/es8156/audio_dac.py create mode 100644 esphome/components/es8156/es8156.cpp create mode 100644 esphome/components/es8156/es8156.h create mode 100644 esphome/components/es8156/es8156_const.h create mode 100644 tests/components/es8156/common.yaml create mode 100644 tests/components/es8156/test.esp32-ard.yaml create mode 100644 tests/components/es8156/test.esp32-c3-ard.yaml create mode 100644 tests/components/es8156/test.esp32-c3-idf.yaml create mode 100644 tests/components/es8156/test.esp32-idf.yaml create mode 100644 tests/components/es8156/test.esp8266-ard.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 56e20133ef..e2c674cfd3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -133,6 +133,7 @@ esphome/components/ens160_i2c/* @latonita esphome/components/ens160_spi/* @latonita esphome/components/ens210/* @itn3rd77 esphome/components/es7210/* @kahrendt +esphome/components/es8156/* @kbx81 esphome/components/es8311/* @kahrendt @kroimon esphome/components/esp32/* @esphome/core esphome/components/esp32_ble/* @Rapsssito @jesserockz diff --git a/esphome/components/es8156/__init__.py b/esphome/components/es8156/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/es8156/audio_dac.py b/esphome/components/es8156/audio_dac.py new file mode 100644 index 0000000000..b9d8eae6b0 --- /dev/null +++ b/esphome/components/es8156/audio_dac.py @@ -0,0 +1,27 @@ +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 = ["@kbx81"] +DEPENDENCIES = ["i2c"] + +es8156_ns = cg.esphome_ns.namespace("es8156") +ES8156 = es8156_ns.class_("ES8156", AudioDac, cg.Component, i2c.I2CDevice) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ES8156), + } + ) + .extend(cv.COMPONENT_SCHEMA) + .extend(i2c.i2c_device_schema(0x08)) +) + + +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/es8156/es8156.cpp b/esphome/components/es8156/es8156.cpp new file mode 100644 index 0000000000..62d35aa2d2 --- /dev/null +++ b/esphome/components/es8156/es8156.cpp @@ -0,0 +1,87 @@ +#include "es8156.h" +#include "es8156_const.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace es8156 { + +static const char *const TAG = "es8156"; + +// Mark the component as failed; use only in setup +#define ES8156_ERROR_FAILED(func) \ + if (!(func)) { \ + this->mark_failed(); \ + return; \ + } + +void ES8156::setup() { + ESP_LOGCONFIG(TAG, "Setting up ES8156..."); + + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG02_SCLK_MODE, 0x04)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG20_ANALOG_SYS1, 0x2A)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG21_ANALOG_SYS2, 0x3C)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG22_ANALOG_SYS3, 0x00)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG24_ANALOG_LP, 0x07)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG23_ANALOG_SYS4, 0x00)); + + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0A_TIME_CONTROL1, 0x01)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0B_TIME_CONTROL2, 0x01)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG11_DAC_SDP, 0x00)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG19_EQ_CONTROL1, 0x20)); + + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG0D_P2S_CONTROL, 0x14)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG09_MISC_CONTROL2, 0x00)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG18_MISC_CONTROL3, 0x00)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG08_CLOCK_ON_OFF, 0x3F)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x02)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG00_RESET, 0x03)); + ES8156_ERROR_FAILED(this->write_byte(ES8156_REG25_ANALOG_SYS5, 0x20)); +} + +void ES8156::dump_config() { + ESP_LOGCONFIG(TAG, "ES8156 Audio Codec:"); + + if (this->is_failed()) { + ESP_LOGCONFIG(TAG, " Failed to initialize"); + return; + } +} + +bool ES8156::set_volume(float volume) { + volume = clamp(volume, 0.0f, 1.0f); + uint8_t reg = remap(volume, 0.0f, 1.0f, 0, 255); + ESP_LOGV(TAG, "Setting ES8156_REG14_VOLUME_CONTROL to %u (volume: %f)", reg, volume); + return this->write_byte(ES8156_REG14_VOLUME_CONTROL, reg); +} + +float ES8156::volume() { + uint8_t reg; + this->read_byte(ES8156_REG14_VOLUME_CONTROL, ®); + return remap(reg, 0, 255, 0.0f, 1.0f); +} + +bool ES8156::set_mute_state_(bool mute_state) { + uint8_t reg13; + + this->is_muted_ = mute_state; + + if (!this->read_byte(ES8156_REG13_DAC_MUTE, ®13)) { + return false; + } + + ESP_LOGV(TAG, "Read ES8156_REG13_DAC_MUTE: %u", reg13); + + if (mute_state) { + reg13 |= BIT(1) | BIT(2); + } else { + reg13 &= ~(BIT(1) | BIT(2)); + } + + ESP_LOGV(TAG, "Setting ES8156_REG13_DAC_MUTE to %u (muted: %s)", reg13, YESNO(mute_state)); + return this->write_byte(ES8156_REG13_DAC_MUTE, reg13); +} + +} // namespace es8156 +} // namespace esphome diff --git a/esphome/components/es8156/es8156.h b/esphome/components/es8156/es8156.h new file mode 100644 index 0000000000..e973599a7a --- /dev/null +++ b/esphome/components/es8156/es8156.h @@ -0,0 +1,51 @@ +#pragma once + +#include "esphome/components/audio_dac/audio_dac.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace es8156 { + +class ES8156 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice { + 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_; } + + 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 es8156 +} // namespace esphome diff --git a/esphome/components/es8156/es8156_const.h b/esphome/components/es8156/es8156_const.h new file mode 100644 index 0000000000..0bc8f89dd4 --- /dev/null +++ b/esphome/components/es8156/es8156_const.h @@ -0,0 +1,68 @@ +#pragma once + +#include "es8156.h" + +namespace esphome { +namespace es8156 { + +/* ES8156 register addresses */ +/* + * RESET Control + */ +static const uint8_t ES8156_REG00_RESET = 0x00; +/* + * Clock Managerment + */ +static const uint8_t ES8156_REG01_MAINCLOCK_CTL = 0x01; +static const uint8_t ES8156_REG02_SCLK_MODE = 0x02; +static const uint8_t ES8156_REG03_LRCLK_DIV_H = 0x03; +static const uint8_t ES8156_REG04_LRCLK_DIV_L = 0x04; +static const uint8_t ES8156_REG05_SCLK_DIV = 0x05; +static const uint8_t ES8156_REG06_NFS_CONFIG = 0x06; +static const uint8_t ES8156_REG07_MISC_CONTROL1 = 0x07; +static const uint8_t ES8156_REG08_CLOCK_ON_OFF = 0x08; +static const uint8_t ES8156_REG09_MISC_CONTROL2 = 0x09; +static const uint8_t ES8156_REG0A_TIME_CONTROL1 = 0x0a; +static const uint8_t ES8156_REG0B_TIME_CONTROL2 = 0x0b; +/* + * System Control + */ +static const uint8_t ES8156_REG0C_CHIP_STATUS = 0x0c; +static const uint8_t ES8156_REG0D_P2S_CONTROL = 0x0d; +static const uint8_t ES8156_REG10_DAC_OSR_COUNTER = 0x10; +/* + * SDP Control + */ +static const uint8_t ES8156_REG11_DAC_SDP = 0x11; +static const uint8_t ES8156_REG12_AUTOMUTE_SET = 0x12; +static const uint8_t ES8156_REG13_DAC_MUTE = 0x13; +static const uint8_t ES8156_REG14_VOLUME_CONTROL = 0x14; + +/* + * ALC Control + */ +static const uint8_t ES8156_REG15_ALC_CONFIG1 = 0x15; +static const uint8_t ES8156_REG16_ALC_CONFIG2 = 0x16; +static const uint8_t ES8156_REG17_ALC_CONFIG3 = 0x17; +static const uint8_t ES8156_REG18_MISC_CONTROL3 = 0x18; +static const uint8_t ES8156_REG19_EQ_CONTROL1 = 0x19; +static const uint8_t ES8156_REG1A_EQ_CONTROL2 = 0x1a; +/* + * Analog System Control + */ +static const uint8_t ES8156_REG20_ANALOG_SYS1 = 0x20; +static const uint8_t ES8156_REG21_ANALOG_SYS2 = 0x21; +static const uint8_t ES8156_REG22_ANALOG_SYS3 = 0x22; +static const uint8_t ES8156_REG23_ANALOG_SYS4 = 0x23; +static const uint8_t ES8156_REG24_ANALOG_LP = 0x24; +static const uint8_t ES8156_REG25_ANALOG_SYS5 = 0x25; +/* + * Chip Information + */ +static const uint8_t ES8156_REGFC_I2C_PAGESEL = 0xFC; +static const uint8_t ES8156_REGFD_CHIPID1 = 0xFD; +static const uint8_t ES8156_REGFE_CHIPID0 = 0xFE; +static const uint8_t ES8156_REGFF_CHIP_VERSION = 0xFF; + +} // namespace es8156 +} // namespace esphome diff --git a/tests/components/es8156/common.yaml b/tests/components/es8156/common.yaml new file mode 100644 index 0000000000..addaa0b70a --- /dev/null +++ b/tests/components/es8156/common.yaml @@ -0,0 +1,15 @@ +esphome: + on_boot: + then: + - audio_dac.mute_off: + - audio_dac.mute_on: + - audio_dac.set_volume: + volume: 50% + +i2c: + - id: i2c_es8156 + scl: ${scl_pin} + sda: ${sda_pin} + +audio_dac: + - platform: es8156 diff --git a/tests/components/es8156/test.esp32-ard.yaml b/tests/components/es8156/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es8156/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es8156/test.esp32-c3-ard.yaml b/tests/components/es8156/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8156/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es8156/test.esp32-c3-idf.yaml b/tests/components/es8156/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8156/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es8156/test.esp32-idf.yaml b/tests/components/es8156/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es8156/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es8156/test.esp8266-ard.yaml b/tests/components/es8156/test.esp8266-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es8156/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml From db644542eda3482a50f0d30c4ab36ea3c635ff6c Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:17:32 -0500 Subject: [PATCH 0906/1052] [esp32_touch] Fix deprecated warning (#8092) Co-authored-by: Jonathan Swoboda --- esphome/components/esp32_touch/esp32_touch.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index e43c3b844c..0ae414fa12 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -52,7 +52,12 @@ void ESP32TouchComponent::setup() { } #endif +#if ESP_IDF_VERSION_MAJOR >= 5 + touch_pad_set_measurement_clock_cycles(this->meas_cycle_); + touch_pad_set_measurement_interval(this->sleep_cycle_); +#else touch_pad_set_meas_time(this->sleep_cycle_, this->meas_cycle_); +#endif touch_pad_set_voltage(this->high_voltage_reference_, this->low_voltage_reference_, this->voltage_attenuation_); for (auto *child : this->children_) { From b454f63b3604d766abb038fe3c0f79dc20ab5cad Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 21 Jan 2025 19:32:47 +1300 Subject: [PATCH 0907/1052] [core] Remove old style platform configuration (#8118) --- esphome/core/config.py | 75 ++----------------- .../binary_sensor/test_binary_sensor.yaml | 3 +- tests/component_tests/button/test_button.yaml | 3 +- .../deep_sleep/test_deep_sleep1.yaml | 3 +- .../deep_sleep/test_deep_sleep2.yaml | 3 +- tests/component_tests/sensor/test_sensor.yaml | 3 +- .../text_sensor/test_text_sensor.yaml | 3 +- .../yaml_util/broken_includetest.yaml | 6 +- .../fixtures/yaml_util/includetest.yaml | 6 +- tests/unit_tests/test_yaml_util.py | 2 +- 10 files changed, 27 insertions(+), 80 deletions(-) diff --git a/esphome/core/config.py b/esphome/core/config.py index eee8b73934..c6a3b1b294 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -1,21 +1,16 @@ import logging import multiprocessing import os -import re from esphome import automation import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( - CONF_ARDUINO_VERSION, CONF_AREA, - CONF_BOARD, - CONF_BOARD_FLASH_MODE, CONF_BUILD_PATH, CONF_COMMENT, CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, - CONF_FRAMEWORK, CONF_FRIENDLY_NAME, CONF_INCLUDES, CONF_LIBRARIES, @@ -30,12 +25,9 @@ from esphome.const import ( CONF_PLATFORMIO_OPTIONS, CONF_PRIORITY, CONF_PROJECT, - CONF_SOURCE, CONF_TRIGGER_ID, - CONF_TYPE, CONF_VERSION, KEY_CORE, - PLATFORM_ESP8266, TARGET_PLATFORMS, __version__ as ESPHOME_VERSION, ) @@ -44,7 +36,6 @@ from esphome.helpers import copy_file_if_changed, get_str_env, walk_files _LOGGER = logging.getLogger(__name__) -BUILD_FLASH_MODES = ["qio", "qout", "dio", "dout"] StartupTrigger = cg.esphome_ns.class_( "StartupTrigger", cg.Component, automation.Trigger.template() ) @@ -58,8 +49,6 @@ ProjectUpdateTrigger = cg.esphome_ns.class_( "ProjectUpdateTrigger", cg.Component, automation.Trigger.template(cg.std_string) ) -VERSION_REGEX = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+(?:[ab]\d+)?$") - VALID_INCLUDE_EXTS = {".h", ".hpp", ".tcc", ".ino", ".cpp", ".c"} @@ -111,7 +100,6 @@ else: _compile_process_limit_default = cv.UNDEFINED -CONF_ESP8266_RESTORE_FROM_FLASH = "esp8266_restore_from_flash" CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -175,14 +163,9 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema( { cv.Required(CONF_NAME): cv.valid_name, cv.Optional(CONF_BUILD_PATH): cv.string, - # Compat options, these were moved to target-platform specific sections - # but we'll keep these around for a long time because every config would - # be impacted - cv.Optional(CONF_PLATFORM): cv.one_of(*TARGET_PLATFORMS, lower=True), - cv.Optional(CONF_BOARD): cv.string_strict, - cv.Optional(CONF_ESP8266_RESTORE_FROM_FLASH): cv.valid, - cv.Optional(CONF_BOARD_FLASH_MODE): cv.valid, - cv.Optional(CONF_ARDUINO_VERSION): cv.valid, + cv.Optional(CONF_PLATFORM): cv.invalid( + "Please remove the `platform` key from the [esphome] block and use the correct platform component. This style of configuration has now been removed." + ), cv.Optional(CONF_MIN_VERSION, default=ESPHOME_VERSION): cv.All( cv.version_number, cv.validate_esphome_version ), @@ -204,62 +187,20 @@ def preload_core_config(config, result): conf[CONF_BUILD_PATH] = os.path.join(build_path, CORE.name) CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH]) - has_oldstyle = CONF_PLATFORM in conf - newstyle_found = [key for key in TARGET_PLATFORMS if key in config] - oldstyle_opts = [ - CONF_ESP8266_RESTORE_FROM_FLASH, - CONF_BOARD_FLASH_MODE, - CONF_ARDUINO_VERSION, - CONF_BOARD, - ] + target_platforms = [key for key in TARGET_PLATFORMS if key in config] - if not has_oldstyle and not newstyle_found: + if not target_platforms: raise cv.Invalid( "Platform missing. You must include one of the available platform keys: " + ", ".join(TARGET_PLATFORMS), [CONF_ESPHOME], ) - if has_oldstyle and newstyle_found: + if len(target_platforms) > 1: raise cv.Invalid( - f"Please remove the `platform` key from the [esphome] block. You're already using the new style with the [{conf[CONF_PLATFORM]}] block", - [CONF_ESPHOME, CONF_PLATFORM], + f"Found multiple target platform blocks: {', '.join(target_platforms)}. Only one is allowed.", + [target_platforms[0]], ) - if len(newstyle_found) > 1: - raise cv.Invalid( - f"Found multiple target platform blocks: {', '.join(newstyle_found)}. Only one is allowed.", - [newstyle_found[0]], - ) - if newstyle_found: - # Convert to newstyle - for key in oldstyle_opts: - if key in conf: - raise cv.Invalid( - f"Please move {key} to the [{newstyle_found[0]}] block.", - [CONF_ESPHOME, key], - ) - if has_oldstyle: - plat = conf.pop(CONF_PLATFORM) - plat_conf = {} - if CONF_ESP8266_RESTORE_FROM_FLASH in conf: - plat_conf["restore_from_flash"] = conf.pop(CONF_ESP8266_RESTORE_FROM_FLASH) - if CONF_BOARD_FLASH_MODE in conf: - plat_conf[CONF_BOARD_FLASH_MODE] = conf.pop(CONF_BOARD_FLASH_MODE) - if CONF_ARDUINO_VERSION in conf: - plat_conf[CONF_FRAMEWORK] = {} - if plat != PLATFORM_ESP8266: - plat_conf[CONF_FRAMEWORK][CONF_TYPE] = "arduino" - - try: - if conf[CONF_ARDUINO_VERSION] not in ("recommended", "latest", "dev"): - cv.Version.parse(conf[CONF_ARDUINO_VERSION]) - plat_conf[CONF_FRAMEWORK][CONF_VERSION] = conf.pop(CONF_ARDUINO_VERSION) - except ValueError: - plat_conf[CONF_FRAMEWORK][CONF_SOURCE] = conf.pop(CONF_ARDUINO_VERSION) - if CONF_BOARD in conf: - plat_conf[CONF_BOARD] = conf.pop(CONF_BOARD) - # Insert generated target platform config to main config - config[plat] = plat_conf config[CONF_ESPHOME] = conf diff --git a/tests/component_tests/binary_sensor/test_binary_sensor.yaml b/tests/component_tests/binary_sensor/test_binary_sensor.yaml index f98ce693f7..8842dda837 100644 --- a/tests/component_tests/binary_sensor/test_binary_sensor.yaml +++ b/tests/component_tests/binary_sensor/test_binary_sensor.yaml @@ -1,7 +1,8 @@ --- esphome: name: test - platform: ESP8266 + +esp8266: board: d1_mini_lite binary_sensor: diff --git a/tests/component_tests/button/test_button.yaml b/tests/component_tests/button/test_button.yaml index 48e13f0353..916c85cb8b 100644 --- a/tests/component_tests/button/test_button.yaml +++ b/tests/component_tests/button/test_button.yaml @@ -1,7 +1,8 @@ --- esphome: name: test - platform: ESP8266 + +esp8266: board: d1_mini_lite wifi: diff --git a/tests/component_tests/deep_sleep/test_deep_sleep1.yaml b/tests/component_tests/deep_sleep/test_deep_sleep1.yaml index 96514a677f..03145290a9 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep1.yaml +++ b/tests/component_tests/deep_sleep/test_deep_sleep1.yaml @@ -1,7 +1,8 @@ --- esphome: name: test - platform: ESP32 + +esp32: board: nodemcu-32s deep_sleep: diff --git a/tests/component_tests/deep_sleep/test_deep_sleep2.yaml b/tests/component_tests/deep_sleep/test_deep_sleep2.yaml index 0e8e598402..88ebb31728 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep2.yaml +++ b/tests/component_tests/deep_sleep/test_deep_sleep2.yaml @@ -1,7 +1,8 @@ --- esphome: name: test - platform: ESP32 + +esp32: board: nodemcu-32s deep_sleep: diff --git a/tests/component_tests/sensor/test_sensor.yaml b/tests/component_tests/sensor/test_sensor.yaml index 8c0fd85b17..612b8e5e56 100644 --- a/tests/component_tests/sensor/test_sensor.yaml +++ b/tests/component_tests/sensor/test_sensor.yaml @@ -1,7 +1,8 @@ --- esphome: name: test - platform: ESP8266 + +esp8266: board: d1_mini_lite sensor: diff --git a/tests/component_tests/text_sensor/test_text_sensor.yaml b/tests/component_tests/text_sensor/test_text_sensor.yaml index b426cb102c..9cc75082ac 100644 --- a/tests/component_tests/text_sensor/test_text_sensor.yaml +++ b/tests/component_tests/text_sensor/test_text_sensor.yaml @@ -1,7 +1,8 @@ --- esphome: name: test - platform: ESP8266 + +esp8266: board: d1_mini_lite text_sensor: diff --git a/tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml b/tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml index aaca55b807..a49ae706a4 100644 --- a/tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml +++ b/tests/unit_tests/fixtures/yaml_util/broken_includetest.yaml @@ -12,7 +12,7 @@ esphome: # not overwritten by vars in the !include above name: ${name} name_add_mac_suffix: true - platform: esp8266 - board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}} - libraries: !include {file: includes/list.yaml, vars: {var1: Wire}} + +esp8266: + board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}} diff --git a/tests/unit_tests/fixtures/yaml_util/includetest.yaml b/tests/unit_tests/fixtures/yaml_util/includetest.yaml index af0a4e2030..7b01da6a17 100644 --- a/tests/unit_tests/fixtures/yaml_util/includetest.yaml +++ b/tests/unit_tests/fixtures/yaml_util/includetest.yaml @@ -12,7 +12,7 @@ esphome: # not overwritten by vars in the !include above name: ${name} name_add_mac_suffix: true - platform: esp8266 - board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}} - libraries: !include {file: includes/list.yaml, vars: {var1: Wire}} + +esp8266: + board: !include {file: includes/scalar.yaml, vars: {var1: nodemcu}} diff --git a/tests/unit_tests/test_yaml_util.py b/tests/unit_tests/test_yaml_util.py index 9178726247..828b2bf14b 100644 --- a/tests/unit_tests/test_yaml_util.py +++ b/tests/unit_tests/test_yaml_util.py @@ -10,7 +10,7 @@ def test_include_with_vars(fixture_path): substitutions.do_substitution_pass(actual, None) assert actual["esphome"]["name"] == "original" assert actual["esphome"]["libraries"][0] == "Wire" - assert actual["esphome"]["board"] == "nodemcu" + assert actual["esp8266"]["board"] == "nodemcu" assert actual["wifi"]["ssid"] == "my_custom_ssid" From 78ce8f014a8d8cf7b5f7e88101c6c9baa8a2da7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 08:15:56 +1300 Subject: [PATCH 0908/1052] Bump actions/stale from 9.0.0 to 9.1.0 (#8120) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 95f275e5a4..b79939fc8e 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -17,7 +17,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9.0.0 + - uses: actions/stale@v9.1.0 with: days-before-pr-stale: 90 days-before-pr-close: 7 @@ -37,7 +37,7 @@ jobs: close-issues: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9.0.0 + - uses: actions/stale@v9.1.0 with: days-before-pr-stale: -1 days-before-pr-close: -1 From 4843bbd38a313911e7cb7ae3d87fc5d8a00079b3 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 21 Jan 2025 17:56:51 -0600 Subject: [PATCH 0909/1052] [custom] Remove platforms (#8119) --- esphome/components/custom/__init__.py | 3 - .../custom/binary_sensor/__init__.py | 30 +--------- .../binary_sensor/custom_binary_sensor.cpp | 16 ----- .../binary_sensor/custom_binary_sensor.h | 26 -------- esphome/components/custom/climate/__init__.py | 29 +-------- .../custom/climate/custom_climate.h | 22 ------- esphome/components/custom/cover/__init__.py | 29 +-------- .../components/custom/cover/custom_cover.h | 21 ------- esphome/components/custom/light/__init__.py | 29 +-------- .../custom/light/custom_light_output.h | 24 -------- esphome/components/custom/output/__init__.py | 60 +------------------ .../components/custom/output/custom_output.h | 37 ------------ esphome/components/custom/sensor/__init__.py | 26 +------- .../custom/sensor/custom_sensor.cpp | 16 ----- .../components/custom/sensor/custom_sensor.h | 24 -------- esphome/components/custom/switch/__init__.py | 26 +------- .../custom/switch/custom_switch.cpp | 16 ----- .../components/custom/switch/custom_switch.h | 24 -------- .../components/custom/text_sensor/__init__.py | 31 +--------- .../custom/text_sensor/custom_text_sensor.cpp | 16 ----- .../custom/text_sensor/custom_text_sensor.h | 26 -------- .../components/custom_component/__init__.py | 30 +--------- .../custom_component/custom_component.h | 28 --------- 23 files changed, 19 insertions(+), 570 deletions(-) delete mode 100644 esphome/components/custom/binary_sensor/custom_binary_sensor.cpp delete mode 100644 esphome/components/custom/binary_sensor/custom_binary_sensor.h delete mode 100644 esphome/components/custom/climate/custom_climate.h delete mode 100644 esphome/components/custom/cover/custom_cover.h delete mode 100644 esphome/components/custom/light/custom_light_output.h delete mode 100644 esphome/components/custom/output/custom_output.h delete mode 100644 esphome/components/custom/sensor/custom_sensor.cpp delete mode 100644 esphome/components/custom/sensor/custom_sensor.h delete mode 100644 esphome/components/custom/switch/custom_switch.cpp delete mode 100644 esphome/components/custom/switch/custom_switch.h delete mode 100644 esphome/components/custom/text_sensor/custom_text_sensor.cpp delete mode 100644 esphome/components/custom/text_sensor/custom_text_sensor.h delete mode 100644 esphome/components/custom_component/custom_component.h diff --git a/esphome/components/custom/__init__.py b/esphome/components/custom/__init__.py index 74450300f3..e69de29bb2 100644 --- a/esphome/components/custom/__init__.py +++ b/esphome/components/custom/__init__.py @@ -1,3 +0,0 @@ -import esphome.codegen as cg - -custom_ns = cg.esphome_ns.namespace("custom") diff --git a/esphome/components/custom/binary_sensor/__init__.py b/esphome/components/custom/binary_sensor/__init__.py index 8d6d621b3a..ca9747ea3e 100644 --- a/esphome/components/custom/binary_sensor/__init__.py +++ b/esphome/components/custom/binary_sensor/__init__.py @@ -1,31 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import binary_sensor -from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA -from .. import custom_ns -CustomBinarySensorConstructor = custom_ns.class_("CustomBinarySensorConstructor") - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_BINARY_SENSORS): cv.ensure_list( - binary_sensor.binary_sensor_schema() - ), - } +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], - [], - return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr), - ) - - rhs = CustomBinarySensorConstructor(template_) - custom = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config[CONF_BINARY_SENSORS]): - rhs = custom.Pget_binary_sensor(i) - await binary_sensor.register_binary_sensor(rhs, conf) diff --git a/esphome/components/custom/binary_sensor/custom_binary_sensor.cpp b/esphome/components/custom/binary_sensor/custom_binary_sensor.cpp deleted file mode 100644 index ea83198568..0000000000 --- a/esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "custom_binary_sensor.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace custom { - -static const char *const TAG = "custom.binary_sensor"; - -void CustomBinarySensorConstructor::dump_config() { - for (auto *child : this->binary_sensors_) { - LOG_BINARY_SENSOR("", "Custom Binary Sensor", child); - } -} - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/binary_sensor/custom_binary_sensor.h b/esphome/components/custom/binary_sensor/custom_binary_sensor.h deleted file mode 100644 index b7d5458d9e..0000000000 --- a/esphome/components/custom/binary_sensor/custom_binary_sensor.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/binary_sensor/binary_sensor.h" - -#include - -namespace esphome { -namespace custom { - -class CustomBinarySensorConstructor : public Component { - public: - CustomBinarySensorConstructor(const std::function()> &init) { - this->binary_sensors_ = init(); - } - - binary_sensor::BinarySensor *get_binary_sensor(int i) { return this->binary_sensors_[i]; } - - void dump_config() override; - - protected: - std::vector binary_sensors_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/climate/__init__.py b/esphome/components/custom/climate/__init__.py index a95456133a..ca9747ea3e 100644 --- a/esphome/components/custom/climate/__init__.py +++ b/esphome/components/custom/climate/__init__.py @@ -1,30 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import climate -from esphome.const import CONF_ID, CONF_LAMBDA -from .. import custom_ns -CustomClimateConstructor = custom_ns.class_("CustomClimateConstructor") -CONF_CLIMATES = "climates" - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomClimateConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA), - } +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], - [], - return_type=cg.std_vector.template(climate.Climate.operator("ptr")), - ) - - rhs = CustomClimateConstructor(template_) - custom = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config[CONF_CLIMATES]): - rhs = custom.Pget_climate(i) - await climate.register_climate(rhs, conf) diff --git a/esphome/components/custom/climate/custom_climate.h b/esphome/components/custom/climate/custom_climate.h deleted file mode 100644 index 37876f7115..0000000000 --- a/esphome/components/custom/climate/custom_climate.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/climate/climate.h" - -#include - -namespace esphome { -namespace custom { - -class CustomClimateConstructor { - public: - CustomClimateConstructor(const std::function()> &init) { this->climates_ = init(); } - - climate::Climate *get_climate(int i) { return this->climates_[i]; } - - protected: - std::vector climates_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/cover/__init__.py b/esphome/components/custom/cover/__init__.py index 37fd4cdbbc..ca9747ea3e 100644 --- a/esphome/components/custom/cover/__init__.py +++ b/esphome/components/custom/cover/__init__.py @@ -1,30 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import cover -from esphome.const import CONF_ID, CONF_LAMBDA -from .. import custom_ns -CustomCoverConstructor = custom_ns.class_("CustomCoverConstructor") -CONF_COVERS = "covers" - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomCoverConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA), - } +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], - [], - return_type=cg.std_vector.template(cover.Cover.operator("ptr")), - ) - - rhs = CustomCoverConstructor(template_) - custom = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config[CONF_COVERS]): - rhs = custom.Pget_cover(i) - await cover.register_cover(rhs, conf) diff --git a/esphome/components/custom/cover/custom_cover.h b/esphome/components/custom/cover/custom_cover.h deleted file mode 100644 index 58330b9d54..0000000000 --- a/esphome/components/custom/cover/custom_cover.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "esphome/components/cover/cover.h" - -#include - -namespace esphome { -namespace custom { - -class CustomCoverConstructor { - public: - CustomCoverConstructor(const std::function()> &init) { this->covers_ = init(); } - - cover::Cover *get_cover(int i) { return this->covers_[i]; } - - protected: - std::vector covers_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/light/__init__.py b/esphome/components/custom/light/__init__.py index b6ebe13ab2..ca9747ea3e 100644 --- a/esphome/components/custom/light/__init__.py +++ b/esphome/components/custom/light/__init__.py @@ -1,30 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import light -from esphome.const import CONF_ID, CONF_LAMBDA -from .. import custom_ns -CustomLightOutputConstructor = custom_ns.class_("CustomLightOutputConstructor") -CONF_LIGHTS = "lights" - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA), - } +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], - [], - return_type=cg.std_vector.template(light.LightOutput.operator("ptr")), - ) - - rhs = CustomLightOutputConstructor(template_) - custom = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config[CONF_LIGHTS]): - rhs = custom.Pget_light(i) - await light.register_light(rhs, conf) diff --git a/esphome/components/custom/light/custom_light_output.h b/esphome/components/custom/light/custom_light_output.h deleted file mode 100644 index c2c83ebe97..0000000000 --- a/esphome/components/custom/light/custom_light_output.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/light/light_output.h" - -#include - -namespace esphome { -namespace custom { - -class CustomLightOutputConstructor { - public: - CustomLightOutputConstructor(const std::function()> &init) { - this->outputs_ = init(); - } - - light::LightOutput *get_light(int i) { return this->outputs_[i]; } - - protected: - std::vector outputs_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/output/__init__.py b/esphome/components/custom/output/__init__.py index 97ef070fc3..ca9747ea3e 100644 --- a/esphome/components/custom/output/__init__.py +++ b/esphome/components/custom/output/__init__.py @@ -1,61 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import output -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE, CONF_BINARY -from .. import custom_ns -CustomBinaryOutputConstructor = custom_ns.class_("CustomBinaryOutputConstructor") -CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor") - -CONF_FLOAT = "float" - -CONFIG_SCHEMA = cv.typed_schema( - { - CONF_BINARY: cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_OUTPUTS): cv.ensure_list( - output.BINARY_OUTPUT_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(output.BinaryOutput), - } - ) - ), - } - ), - CONF_FLOAT: cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_OUTPUTS): cv.ensure_list( - output.FLOAT_OUTPUT_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(output.FloatOutput), - } - ) - ), - } - ), - }, - lower=True, +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - type = config[CONF_TYPE] - if type == "binary": - ret_type = output.BinaryOutputPtr - klass = CustomBinaryOutputConstructor - else: - ret_type = output.FloatOutputPtr - klass = CustomFloatOutputConstructor - template_ = await cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(ret_type) - ) - - rhs = klass(template_) - custom = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config[CONF_OUTPUTS]): - out = cg.Pvariable(conf[CONF_ID], custom.get_output(i)) - await output.register_output(out, conf) diff --git a/esphome/components/custom/output/custom_output.h b/esphome/components/custom/output/custom_output.h deleted file mode 100644 index 4624642420..0000000000 --- a/esphome/components/custom/output/custom_output.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/output/binary_output.h" -#include "esphome/components/output/float_output.h" - -#include - -namespace esphome { -namespace custom { - -class CustomBinaryOutputConstructor { - public: - CustomBinaryOutputConstructor(const std::function()> &init) { - this->outputs_ = init(); - } - - output::BinaryOutput *get_output(int i) { return this->outputs_[i]; } - - protected: - std::vector outputs_; -}; - -class CustomFloatOutputConstructor { - public: - CustomFloatOutputConstructor(const std::function()> &init) { - this->outputs_ = init(); - } - - output::FloatOutput *get_output(int i) { return this->outputs_[i]; } - - protected: - std::vector outputs_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/sensor/__init__.py b/esphome/components/custom/sensor/__init__.py index be17d9a334..ca9747ea3e 100644 --- a/esphome/components/custom/sensor/__init__.py +++ b/esphome/components/custom/sensor/__init__.py @@ -1,27 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS -from .. import custom_ns -CustomSensorConstructor = custom_ns.class_("CustomSensorConstructor") - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomSensorConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_SENSORS): cv.ensure_list(sensor.sensor_schema()), - } +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(sensor.SensorPtr) - ) - - rhs = CustomSensorConstructor(template_) - var = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config[CONF_SENSORS]): - sens = cg.Pvariable(conf[CONF_ID], var.get_sensor(i)) - await sensor.register_sensor(sens, conf) diff --git a/esphome/components/custom/sensor/custom_sensor.cpp b/esphome/components/custom/sensor/custom_sensor.cpp deleted file mode 100644 index e670f09530..0000000000 --- a/esphome/components/custom/sensor/custom_sensor.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "custom_sensor.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace custom { - -static const char *const TAG = "custom.sensor"; - -void CustomSensorConstructor::dump_config() { - for (auto *child : this->sensors_) { - LOG_SENSOR("", "Custom Sensor", child); - } -} - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/sensor/custom_sensor.h b/esphome/components/custom/sensor/custom_sensor.h deleted file mode 100644 index d8f4fbc109..0000000000 --- a/esphome/components/custom/sensor/custom_sensor.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/sensor/sensor.h" - -#include - -namespace esphome { -namespace custom { - -class CustomSensorConstructor : public Component { - public: - CustomSensorConstructor(const std::function()> &init) { this->sensors_ = init(); } - - sensor::Sensor *get_sensor(int i) { return this->sensors_[i]; } - - void dump_config() override; - - protected: - std::vector sensors_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/switch/__init__.py b/esphome/components/custom/switch/__init__.py index 5538ae6aa0..ca9747ea3e 100644 --- a/esphome/components/custom/switch/__init__.py +++ b/esphome/components/custom/switch/__init__.py @@ -1,27 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import switch -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES -from .. import custom_ns -CustomSwitchConstructor = custom_ns.class_("CustomSwitchConstructor") - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomSwitchConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_SWITCHES): cv.ensure_list(switch.switch_schema(switch.Switch)), - } +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(switch.SwitchPtr) - ) - - rhs = CustomSwitchConstructor(template_) - var = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config[CONF_SWITCHES]): - switch_ = cg.Pvariable(conf[CONF_ID], var.get_switch(i)) - await switch.register_switch(switch_, conf) diff --git a/esphome/components/custom/switch/custom_switch.cpp b/esphome/components/custom/switch/custom_switch.cpp deleted file mode 100644 index 6d0a8fa621..0000000000 --- a/esphome/components/custom/switch/custom_switch.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "custom_switch.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace custom { - -static const char *const TAG = "custom.switch"; - -void CustomSwitchConstructor::dump_config() { - for (auto *child : this->switches_) { - LOG_SWITCH("", "Custom Switch", child); - } -} - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/switch/custom_switch.h b/esphome/components/custom/switch/custom_switch.h deleted file mode 100644 index 9657e4b44d..0000000000 --- a/esphome/components/custom/switch/custom_switch.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/switch/switch.h" - -#include - -namespace esphome { -namespace custom { - -class CustomSwitchConstructor : public Component { - public: - CustomSwitchConstructor(const std::function()> &init) { this->switches_ = init(); } - - switch_::Switch *get_switch(int i) { return this->switches_[i]; } - - void dump_config() override; - - protected: - std::vector switches_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/text_sensor/__init__.py b/esphome/components/custom/text_sensor/__init__.py index 70728af604..ca9747ea3e 100644 --- a/esphome/components/custom/text_sensor/__init__.py +++ b/esphome/components/custom/text_sensor/__init__.py @@ -1,32 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import text_sensor -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS -from .. import custom_ns -CustomTextSensorConstructor = custom_ns.class_("CustomTextSensorConstructor") - -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Required(CONF_TEXT_SENSORS): cv.ensure_list( - text_sensor.text_sensor_schema() - ), - } +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], - [], - return_type=cg.std_vector.template(text_sensor.TextSensorPtr), - ) - - rhs = CustomTextSensorConstructor(template_) - var = cg.variable(config[CONF_ID], rhs) - - for i, conf in enumerate(config[CONF_TEXT_SENSORS]): - text = cg.Pvariable(conf[CONF_ID], var.get_text_sensor(i)) - await text_sensor.register_text_sensor(text, conf) diff --git a/esphome/components/custom/text_sensor/custom_text_sensor.cpp b/esphome/components/custom/text_sensor/custom_text_sensor.cpp deleted file mode 100644 index 618ba832a5..0000000000 --- a/esphome/components/custom/text_sensor/custom_text_sensor.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "custom_text_sensor.h" -#include "esphome/core/log.h" - -namespace esphome { -namespace custom { - -static const char *const TAG = "custom.text_sensor"; - -void CustomTextSensorConstructor::dump_config() { - for (auto *child : this->text_sensors_) { - LOG_TEXT_SENSOR("", "Custom Text Sensor", child); - } -} - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom/text_sensor/custom_text_sensor.h b/esphome/components/custom/text_sensor/custom_text_sensor.h deleted file mode 100644 index 13732c00b6..0000000000 --- a/esphome/components/custom/text_sensor/custom_text_sensor.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/text_sensor/text_sensor.h" - -#include - -namespace esphome { -namespace custom { - -class CustomTextSensorConstructor : public Component { - public: - CustomTextSensorConstructor(const std::function()> &init) { - this->text_sensors_ = init(); - } - - text_sensor::TextSensor *get_text_sensor(int i) { return this->text_sensors_[i]; } - - void dump_config() override; - - protected: - std::vector text_sensors_; -}; - -} // namespace custom -} // namespace esphome diff --git a/esphome/components/custom_component/__init__.py b/esphome/components/custom_component/__init__.py index d41dd7ea59..982153414d 100644 --- a/esphome/components/custom_component/__init__.py +++ b/esphome/components/custom_component/__init__.py @@ -1,31 +1,7 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_LAMBDA - -custom_component_ns = cg.esphome_ns.namespace("custom_component") -CustomComponentConstructor = custom_component_ns.class_("CustomComponentConstructor") MULTI_CONF = True -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(CustomComponentConstructor), - cv.Required(CONF_LAMBDA): cv.returning_lambda, - cv.Optional(CONF_COMPONENTS): cv.ensure_list( - cv.Schema({cv.GenerateID(): cv.declare_id(cg.Component)}).extend( - cv.COMPONENT_SCHEMA - ) - ), - } + +CONFIG_SCHEMA = cv.invalid( + 'The "custom" component has been removed. Consider conversion to an external component.\nhttps://esphome.io/guides/contributing#a-note-about-custom-components' ) - - -async def to_code(config): - template_ = await cg.process_lambda( - config[CONF_LAMBDA], [], return_type=cg.std_vector.template(cg.ComponentPtr) - ) - - rhs = CustomComponentConstructor(template_) - var = cg.variable(config[CONF_ID], rhs) - for i, conf in enumerate(config.get(CONF_COMPONENTS, [])): - comp = cg.Pvariable(conf[CONF_ID], var.get_component(i)) - await cg.register_component(comp, conf) diff --git a/esphome/components/custom_component/custom_component.h b/esphome/components/custom_component/custom_component.h deleted file mode 100644 index 3b34019a7a..0000000000 --- a/esphome/components/custom_component/custom_component.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/core/application.h" - -#include - -namespace esphome { -namespace custom_component { - -class CustomComponentConstructor { - public: - CustomComponentConstructor(const std::function()> &init) { - this->components_ = init(); - - for (auto *comp : this->components_) { - App.register_component(comp); - } - } - - Component *get_component(int i) const { return this->components_[i]; } - - protected: - std::vector components_; -}; - -} // namespace custom_component -} // namespace esphome From c2e52f4b1177224a80e5585bbdc3151a61ec5a6d Mon Sep 17 00:00:00 2001 From: Citric Li <37475446+limengdu@users.noreply.github.com> Date: Wed, 22 Jan 2025 08:01:15 +0800 Subject: [PATCH 0910/1052] Add: Human Presence and Target Count to the Seeed Studio MR60BHA2 (#8010) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Spencer Yan --- .../seeed_mr60bha2/binary_sensor.py | 25 ++++++++++++++++ .../seeed_mr60bha2/seeed_mr60bha2.cpp | 28 ++++++++++++++++-- .../seeed_mr60bha2/seeed_mr60bha2.h | 29 ++++++------------- esphome/components/seeed_mr60bha2/sensor.py | 10 ++++++- tests/components/seeed_mr60bha2/common.yaml | 7 +++++ 5 files changed, 76 insertions(+), 23 deletions(-) create mode 100644 esphome/components/seeed_mr60bha2/binary_sensor.py diff --git a/esphome/components/seeed_mr60bha2/binary_sensor.py b/esphome/components/seeed_mr60bha2/binary_sensor.py new file mode 100644 index 0000000000..ae9e1c23e6 --- /dev/null +++ b/esphome/components/seeed_mr60bha2/binary_sensor.py @@ -0,0 +1,25 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +import esphome.config_validation as cv +from esphome.const import ( + DEVICE_CLASS_OCCUPANCY, + CONF_HAS_TARGET, +) +from . import CONF_MR60BHA2_ID, MR60BHA2Component + +DEPENDENCIES = ["seeed_mr60bha2"] + +CONFIG_SCHEMA = { + cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component), + cv.Optional(CONF_HAS_TARGET): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_OCCUPANCY, icon="mdi:motion-sensor" + ), +} + + +async def to_code(config): + mr60bha2_component = await cg.get_variable(config[CONF_MR60BHA2_ID]) + + if has_target_config := config.get(CONF_HAS_TARGET): + sens = await binary_sensor.new_binary_sensor(has_target_config) + cg.add(mr60bha2_component.set_has_target_binary_sensor(sens)) diff --git a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp index 50d709c3b0..75f3f092a6 100644 --- a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +++ b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp @@ -1,6 +1,7 @@ #include "seeed_mr60bha2.h" #include "esphome/core/log.h" +#include #include namespace esphome { @@ -12,10 +13,14 @@ static const char *const TAG = "seeed_mr60bha2"; // items in an easy-to-read format, including the configuration key-value pairs. void MR60BHA2Component::dump_config() { ESP_LOGCONFIG(TAG, "MR60BHA2:"); +#ifdef USE_BINARY_SENSOR + LOG_BINARY_SENSOR(" ", "People Exist Binary Sensor", this->has_target_binary_sensor_); +#endif #ifdef USE_SENSOR LOG_SENSOR(" ", "Breath Rate Sensor", this->breath_rate_sensor_); LOG_SENSOR(" ", "Heart Rate Sensor", this->heart_rate_sensor_); LOG_SENSOR(" ", "Distance Sensor", this->distance_sensor_); + LOG_SENSOR(" ", "Target Number Sensor", this->num_targets_sensor_); #endif } @@ -94,7 +99,8 @@ bool MR60BHA2Component::validate_message_() { uint16_t frame_type = encode_uint16(data[5], data[6]); if (frame_type != BREATH_RATE_TYPE_BUFFER && frame_type != HEART_RATE_TYPE_BUFFER && - frame_type != DISTANCE_TYPE_BUFFER) { + frame_type != DISTANCE_TYPE_BUFFER && frame_type != PEOPLE_EXIST_TYPE_BUFFER && + frame_type != PRINT_CLOUD_BUFFER) { return false; } @@ -144,6 +150,18 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c } } break; + case PEOPLE_EXIST_TYPE_BUFFER: + if (this->has_target_binary_sensor_ != nullptr && length >= 2) { + uint16_t has_target_int = encode_uint16(data[1], data[0]); + this->has_target_binary_sensor_->publish_state(has_target_int); + if (has_target_int == 0) { + this->breath_rate_sensor_->publish_state(0.0); + this->heart_rate_sensor_->publish_state(0.0); + this->distance_sensor_->publish_state(0.0); + this->num_targets_sensor_->publish_state(0); + } + } + break; case HEART_RATE_TYPE_BUFFER: if (this->heart_rate_sensor_ != nullptr && length >= 4) { uint32_t current_heart_rate_int = encode_uint32(data[3], data[2], data[1], data[0]); @@ -155,7 +173,7 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c } break; case DISTANCE_TYPE_BUFFER: - if (!data[0]) { + if (data[0] != 0) { if (this->distance_sensor_ != nullptr && length >= 8) { uint32_t current_distance_int = encode_uint32(data[7], data[6], data[5], data[4]); float distance_float; @@ -164,6 +182,12 @@ void MR60BHA2Component::process_frame_(uint16_t frame_id, uint16_t frame_type, c } } break; + case PRINT_CLOUD_BUFFER: + if (this->num_targets_sensor_ != nullptr && length >= 4) { + uint32_t current_num_targets_int = encode_uint32(data[3], data[2], data[1], data[0]); + this->num_targets_sensor_->publish_state(current_num_targets_int); + } + break; default: break; } diff --git a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.h b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.h index 0a4f21f1ad..d20c8e50cc 100644 --- a/esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +++ b/esphome/components/seeed_mr60bha2/seeed_mr60bha2.h @@ -1,6 +1,9 @@ #pragma once #include "esphome/core/component.h" #include "esphome/core/defines.h" +#ifdef USE_BINARY_SENSOR +#include "esphome/components/binary_sensor/binary_sensor.h" +#endif #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" #endif @@ -12,37 +15,23 @@ namespace esphome { namespace seeed_mr60bha2 { - -static const uint8_t DATA_BUF_MAX_SIZE = 12; -static const uint8_t FRAME_BUF_MAX_SIZE = 21; -static const uint8_t LEN_TO_HEAD_CKSUM = 8; -static const uint8_t LEN_TO_DATA_FRAME = 9; - static const uint8_t FRAME_HEADER_BUFFER = 0x01; static const uint16_t BREATH_RATE_TYPE_BUFFER = 0x0A14; +static const uint16_t PEOPLE_EXIST_TYPE_BUFFER = 0x0F09; static const uint16_t HEART_RATE_TYPE_BUFFER = 0x0A15; static const uint16_t DISTANCE_TYPE_BUFFER = 0x0A16; - -enum FrameLocation { - LOCATE_FRAME_HEADER, - LOCATE_ID_FRAME1, - LOCATE_ID_FRAME2, - LOCATE_LENGTH_FRAME_H, - LOCATE_LENGTH_FRAME_L, - LOCATE_TYPE_FRAME1, - LOCATE_TYPE_FRAME2, - LOCATE_HEAD_CKSUM_FRAME, // Header checksum: [from the first byte to the previous byte of the HEAD_CKSUM bit] - LOCATE_DATA_FRAME, - LOCATE_DATA_CKSUM_FRAME, // Data checksum: [from the first to the previous byte of the DATA_CKSUM bit] - LOCATE_PROCESS_FRAME, -}; +static const uint16_t PRINT_CLOUD_BUFFER = 0x0A04; class MR60BHA2Component : public Component, public uart::UARTDevice { // The class name must be the name defined by text_sensor.py +#ifdef USE_BINARY_SENSOR + SUB_BINARY_SENSOR(has_target); +#endif #ifdef USE_SENSOR SUB_SENSOR(breath_rate); SUB_SENSOR(heart_rate); SUB_SENSOR(distance); + SUB_SENSOR(num_targets); #endif public: diff --git a/esphome/components/seeed_mr60bha2/sensor.py b/esphome/components/seeed_mr60bha2/sensor.py index 5f30b363bf..916d4b4ba2 100644 --- a/esphome/components/seeed_mr60bha2/sensor.py +++ b/esphome/components/seeed_mr60bha2/sensor.py @@ -7,6 +7,7 @@ from esphome.const import ( ICON_HEART_PULSE, ICON_PULSE, ICON_SIGNAL, + ICON_COUNTER, STATE_CLASS_MEASUREMENT, UNIT_BEATS_PER_MINUTE, UNIT_CENTIMETER, @@ -18,12 +19,13 @@ DEPENDENCIES = ["seeed_mr60bha2"] CONF_BREATH_RATE = "breath_rate" CONF_HEART_RATE = "heart_rate" +CONF_NUM_TARGETS = "num_targets" CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(CONF_MR60BHA2_ID): cv.use_id(MR60BHA2Component), cv.Optional(CONF_BREATH_RATE): sensor.sensor_schema( - accuracy_decimals=2, + accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, icon=ICON_PULSE, ), @@ -40,6 +42,9 @@ CONFIG_SCHEMA = cv.Schema( accuracy_decimals=2, icon=ICON_SIGNAL, ), + cv.Optional(CONF_NUM_TARGETS): sensor.sensor_schema( + icon=ICON_COUNTER, + ), } ) @@ -55,3 +60,6 @@ async def to_code(config): if distance_config := config.get(CONF_DISTANCE): sens = await sensor.new_sensor(distance_config) cg.add(mr60bha2_component.set_distance_sensor(sens)) + if num_targets_config := config.get(CONF_NUM_TARGETS): + sens = await sensor.new_sensor(num_targets_config) + cg.add(mr60bha2_component.set_num_targets_sensor(sens)) diff --git a/tests/components/seeed_mr60bha2/common.yaml b/tests/components/seeed_mr60bha2/common.yaml index e9d0c735af..9eb0c8d527 100644 --- a/tests/components/seeed_mr60bha2/common.yaml +++ b/tests/components/seeed_mr60bha2/common.yaml @@ -9,6 +9,11 @@ uart: seeed_mr60bha2: id: my_seeed_mr60bha2 +binary_sensor: + - platform: seeed_mr60bha2 + has_target: + name: "Person Information" + sensor: - platform: seeed_mr60bha2 breath_rate: @@ -17,3 +22,5 @@ sensor: name: "Real-time heart rate" distance: name: "Distance to detection object" + num_targets: + name: "Target Number" From f2170c633a3ffe54960f84803e838f31944428b7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 22 Jan 2025 14:23:22 -0600 Subject: [PATCH 0911/1052] [es7243e] Add support for ES7243E audio ADC (#8098) --- CODEOWNERS | 1 + esphome/components/es7243e/__init__.py | 0 esphome/components/es7243e/audio_adc.py | 34 +++++ esphome/components/es7243e/es7243e.cpp | 125 ++++++++++++++++++ esphome/components/es7243e/es7243e.h | 37 ++++++ esphome/components/es7243e/es7243e_const.h | 54 ++++++++ tests/components/es7243e/common.yaml | 14 ++ tests/components/es7243e/test.esp32-ard.yaml | 5 + .../components/es7243e/test.esp32-c3-ard.yaml | 5 + .../components/es7243e/test.esp32-c3-idf.yaml | 5 + tests/components/es7243e/test.esp32-idf.yaml | 5 + 11 files changed, 285 insertions(+) create mode 100644 esphome/components/es7243e/__init__.py create mode 100644 esphome/components/es7243e/audio_adc.py create mode 100644 esphome/components/es7243e/es7243e.cpp create mode 100644 esphome/components/es7243e/es7243e.h create mode 100644 esphome/components/es7243e/es7243e_const.h create mode 100644 tests/components/es7243e/common.yaml create mode 100644 tests/components/es7243e/test.esp32-ard.yaml create mode 100644 tests/components/es7243e/test.esp32-c3-ard.yaml create mode 100644 tests/components/es7243e/test.esp32-c3-idf.yaml create mode 100644 tests/components/es7243e/test.esp32-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index e2c674cfd3..aa24b6cb82 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -133,6 +133,7 @@ esphome/components/ens160_i2c/* @latonita esphome/components/ens160_spi/* @latonita esphome/components/ens210/* @itn3rd77 esphome/components/es7210/* @kahrendt +esphome/components/es7243e/* @kbx81 esphome/components/es8156/* @kbx81 esphome/components/es8311/* @kahrendt @kroimon esphome/components/esp32/* @esphome/core diff --git a/esphome/components/es7243e/__init__.py b/esphome/components/es7243e/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/es7243e/audio_adc.py b/esphome/components/es7243e/audio_adc.py new file mode 100644 index 0000000000..c305d60172 --- /dev/null +++ b/esphome/components/es7243e/audio_adc.py @@ -0,0 +1,34 @@ +import esphome.codegen as cg +from esphome.components import i2c +from esphome.components.audio_adc import AudioAdc +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_MIC_GAIN + +CODEOWNERS = ["@kbx81"] +DEPENDENCIES = ["i2c"] + +es7243e_ns = cg.esphome_ns.namespace("es7243e") +ES7243E = es7243e_ns.class_("ES7243E", AudioAdc, cg.Component, i2c.I2CDevice) + +ES7243E_MIC_GAINS = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 34.5, 36, 37.5] + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(ES7243E), + cv.Optional(CONF_MIC_GAIN, default="24db"): cv.All( + cv.decibel, cv.one_of(*ES7243E_MIC_GAINS) + ), + } + ) + .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) + + cg.add(var.set_mic_gain(config[CONF_MIC_GAIN])) diff --git a/esphome/components/es7243e/es7243e.cpp b/esphome/components/es7243e/es7243e.cpp new file mode 100644 index 0000000000..ce65ce973e --- /dev/null +++ b/esphome/components/es7243e/es7243e.cpp @@ -0,0 +1,125 @@ +#include "es7243e.h" +#include "es7243e_const.h" + +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +#include + +namespace esphome { +namespace es7243e { + +static const char *const TAG = "es7243e"; + +// Mark the component as failed; use only in setup +#define ES7243E_ERROR_FAILED(func) \ + if (!(func)) { \ + this->mark_failed(); \ + return; \ + } + +// Return false; use outside of setup +#define ES7243E_ERROR_CHECK(func) \ + if (!(func)) { \ + return false; \ + } + +void ES7243E::dump_config() { + ESP_LOGCONFIG(TAG, "ES7243E audio ADC:"); + + if (this->is_failed()) { + ESP_LOGE(TAG, " Failed to initialize"); + return; + } +} + +void ES7243E::setup() { + ESP_LOGCONFIG(TAG, "Setting up ES7243E..."); + + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x02)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x01)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x1E)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x00)); + + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG02, 0x00)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG03, 0x20)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0D, 0x00)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG05, 0x00)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG06, 0x03)); // SCLK=MCLK/4 + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG07, 0x00)); // LRCK=MCLK/256 + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG08, 0xFF)); // LRCK=MCLK/256 + + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG09, 0xCA)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0A, 0x85)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_SDP_REG0B, 0x00)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0E, 0xBF)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG0F, 0x80)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG14, 0x0C)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ADC_CTRL_REG15, 0x0C)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x02)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG18, 0x26)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG19, 0x77)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1A, 0xF4)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1B, 0x66)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1C, 0x44)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1E, 0x00)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG1F, 0x0C)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG20, 0x1A)); // PGA gain +30dB + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG21, 0x1A)); + + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00)); + + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_TEST_MODE_REGF9, 0x00)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG04, 0x01)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG17, 0x01)); + ES7243E_ERROR_FAILED(this->configure_mic_gain_()); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x3F)); + ES7243E_ERROR_FAILED(this->write_byte(ES7243E_ANALOG_REG16, 0x00)); + + this->setup_complete_ = true; +} + +bool ES7243E::set_mic_gain(float mic_gain) { + this->mic_gain_ = clamp(mic_gain, 0, 37.5); + if (this->setup_complete_) { + return this->configure_mic_gain_(); + } + return true; +} + +bool ES7243E::configure_mic_gain_() { + auto regv = this->es7243e_gain_reg_value_(this->mic_gain_); + + ES7243E_ERROR_CHECK(this->write_byte(ES7243E_ANALOG_REG20, 0x10 | regv)); + ES7243E_ERROR_CHECK(this->write_byte(ES7243E_ANALOG_REG21, 0x10 | regv)); + + return true; +} + +uint8_t ES7243E::es7243e_gain_reg_value_(float mic_gain) { + // reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB + mic_gain += 0.5; + if (mic_gain <= 33.0) { + return (uint8_t) mic_gain / 3; + } + if (mic_gain < 36.0) { + return 12; + } + if (mic_gain < 37.0) { + return 13; + } + return 14; +} + +} // namespace es7243e +} // namespace esphome diff --git a/esphome/components/es7243e/es7243e.h b/esphome/components/es7243e/es7243e.h new file mode 100644 index 0000000000..41a8acac8d --- /dev/null +++ b/esphome/components/es7243e/es7243e.h @@ -0,0 +1,37 @@ +#pragma once + +#include "esphome/components/audio_adc/audio_adc.h" +#include "esphome/components/i2c/i2c.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace es7243e { + +class ES7243E : public audio_adc::AudioAdc, public Component, public i2c::I2CDevice { + /* Class for configuring an ES7243E ADC for microphone input. + * Based on code from: + * - https://github.com/espressif/esp-adf/ (accessed 20250116) + */ + public: + void setup() override; + float get_setup_priority() const override { return setup_priority::DATA; } + void dump_config() override; + + bool set_mic_gain(float mic_gain) override; + + float mic_gain() override { return this->mic_gain_; }; + + protected: + /// @brief Convert floating point mic gain value to register value + /// @param mic_gain Gain value to convert + /// @return Corresponding register value for specified gain + uint8_t es7243e_gain_reg_value_(float mic_gain); + + bool configure_mic_gain_(); + + bool setup_complete_{false}; + float mic_gain_{0}; +}; + +} // namespace es7243e +} // namespace esphome diff --git a/esphome/components/es7243e/es7243e_const.h b/esphome/components/es7243e/es7243e_const.h new file mode 100644 index 0000000000..daae53a108 --- /dev/null +++ b/esphome/components/es7243e/es7243e_const.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +namespace esphome { +namespace es7243e { + +// ES7243E register addresses +static const uint8_t ES7243E_RESET_REG00 = 0x00; // Reset control +static const uint8_t ES7243E_CLOCK_MGR_REG01 = 0x01; // MCLK/BCLK/ADCCLK/Analog clocks on/off +static const uint8_t ES7243E_CLOCK_MGR_REG02 = 0x02; // MCLK & BCLK configuration, source selection + +static const uint8_t ES7243E_CLOCK_MGR_REG03 = 0x03; // ADC Over-sample rate control +static const uint8_t ES7243E_CLOCK_MGR_REG04 = 0x04; // Pre-divide/Pre-multiplication +static const uint8_t ES7243E_CLOCK_MGR_REG05 = 0x05; // CF/DSP clock divider +static const uint8_t ES7243E_CLOCK_MGR_REG06 = 0x06; // BCLK divider at master mode +static const uint8_t ES7243E_CLOCK_MGR_REG07 = 0x07; // BCLK/LRCK/SDOUT tri-state control/LRCK divider bit 11->8 +static const uint8_t ES7243E_CLOCK_MGR_REG08 = 0x08; // Master LRCK divider bit 7 to bit 0 +static const uint8_t ES7243E_CLOCK_MGR_REG09 = 0x09; // SEL S1/Timer for S1 +static const uint8_t ES7243E_SDP_REG0A = 0x0A; // SEL S3/Timer for S3 +static const uint8_t ES7243E_SDP_REG0B = 0x0B; // SDP out mute control/I2S/left-justify case/word length/format +static const uint8_t ES7243E_SDP_REG0C = 0x0C; // NFS flag at slot0/LSB/TDM mode selection +static const uint8_t ES7243E_ADC_CTRL_REG0D = 0x0D; // data mux/pol. inv./ram clear on lrck/mclk active/gain scale up +static const uint8_t ES7243E_ADC_CTRL_REG0E = 0x0E; // volume control +static const uint8_t ES7243E_ADC_CTRL_REG0F = 0x0F; // offset freeze/auto level control/automute control/VC ramp rate +static const uint8_t ES7243E_ADC_CTRL_REG10 = 0x10; // automute noise gate/detection +static const uint8_t ES7243E_ADC_CTRL_REG11 = 0x11; // automute SDP control/out gain select +static const uint8_t ES7243E_ADC_CTRL_REG12 = 0x12; // controls for automute PDN_PGA/MOD/reset/digital circuit +static const uint8_t ES7243E_ADC_CTRL_REG13 = 0x13; // ALC rate selection/ALC target level +static const uint8_t ES7243E_ADC_CTRL_REG14 = 0x14; // ADCHPF stage1 coeff +static const uint8_t ES7243E_ADC_CTRL_REG15 = 0x15; // ADCHPF stage2 coeff +static const uint8_t ES7243E_ANALOG_REG16 = 0x16; // power-down/reset +static const uint8_t ES7243E_ANALOG_REG17 = 0x17; // VMIDSEL +static const uint8_t ES7243E_ANALOG_REG18 = 0x18; // ADC/ADCFL bias +static const uint8_t ES7243E_ANALOG_REG19 = 0x19; // PGA1/PGA2 bias +static const uint8_t ES7243E_ANALOG_REG1A = 0x1A; // ADCI1/ADCI23 bias +static const uint8_t ES7243E_ANALOG_REG1B = 0x1B; // ADCSM/ADCCM bias +static const uint8_t ES7243E_ANALOG_REG1C = 0x1C; // ADCVRP/ADCCPP bias +static const uint8_t ES7243E_ANALOG_REG1D = 0x1D; // low power bits +static const uint8_t ES7243E_ANALOG_REG1E = 0x1E; // low power bits +static const uint8_t ES7243E_ANALOG_REG1F = 0x1F; // ADC_DMIC_ON/REFSEL/VX2OFF/VX1SEL/VMIDLVL +static const uint8_t ES7243E_ANALOG_REG20 = 0x20; // select MIC1 as PGA1 input/PGA1 gain +static const uint8_t ES7243E_ANALOG_REG21 = 0x21; // select MIC2 as PGA1 input/PGA2 gain +static const uint8_t ES7243E_TEST_MODE_REGF7 = 0xF7; +static const uint8_t ES7243E_TEST_MODE_REGF8 = 0xF8; +static const uint8_t ES7243E_TEST_MODE_REGF9 = 0xF9; +static const uint8_t ES7243E_I2C_CONF_REGFA = 0xFA; // I2C signals retime/reset registers to default +static const uint8_t ES7243E_FLAG_REGFC = 0xFC; // CSM flag/ADC automute flag (RO) +static const uint8_t ES7243E_CHIP_ID1_REGFD = 0xFD; // chip ID 1, reads 0x7A (RO) +static const uint8_t ES7243E_CHIP_ID2_REGFE = 0xFE; // chip ID 2, reads 0x43 (RO) +static const uint8_t ES7243E_CHIP_VERSION_REGFF = 0xFF; // chip version, reads 0x00 (RO) + +} // namespace es7243e +} // namespace esphome diff --git a/tests/components/es7243e/common.yaml b/tests/components/es7243e/common.yaml new file mode 100644 index 0000000000..3de76909e9 --- /dev/null +++ b/tests/components/es7243e/common.yaml @@ -0,0 +1,14 @@ +esphome: + on_boot: + then: + - audio_adc.set_mic_gain: 0db + - audio_adc.set_mic_gain: !lambda 'return 4;' + +i2c: + - id: i2c_es7243e + scl: ${scl_pin} + sda: ${sda_pin} + +audio_adc: + - platform: es7243e + id: es7243e_adc diff --git a/tests/components/es7243e/test.esp32-ard.yaml b/tests/components/es7243e/test.esp32-ard.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es7243e/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/es7243e/test.esp32-c3-ard.yaml b/tests/components/es7243e/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es7243e/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es7243e/test.esp32-c3-idf.yaml b/tests/components/es7243e/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/es7243e/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/es7243e/test.esp32-idf.yaml b/tests/components/es7243e/test.esp32-idf.yaml new file mode 100644 index 0000000000..63c3bd6afd --- /dev/null +++ b/tests/components/es7243e/test.esp32-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml From 01ab6d3ddca81633d39c3279d5feb89eae3bec22 Mon Sep 17 00:00:00 2001 From: Frederik <5511687+fightforlife@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:37:32 +0100 Subject: [PATCH 0912/1052] [debug] fix debug_esp32 printf for partition size and address (#8122) Co-authored-by: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> --- esphome/components/debug/debug_esp32.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/debug/debug_esp32.cpp b/esphome/components/debug/debug_esp32.cpp index 69ae7e3678..caa9f8d743 100644 --- a/esphome/components/debug/debug_esp32.cpp +++ b/esphome/components/debug/debug_esp32.cpp @@ -35,8 +35,8 @@ void DebugComponent::log_partition_info_() { 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); - ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08X 0x%08X", partition->label, partition->type, partition->subtype, - partition->address, partition->size); + ESP_LOGCONFIG(TAG, " %-12s %-4d %-8d 0x%08" PRIX32 " 0x%08" PRIX32, partition->label, partition->type, + partition->subtype, partition->address, partition->size); it = esp_partition_next(it); } esp_partition_iterator_release(it); From 5a103543c43385d240c5f80cae749e47d55f4269 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 22 Jan 2025 17:00:40 -0600 Subject: [PATCH 0913/1052] [esp32] Set logger default interface for C6 (#8126) --- esphome/components/logger/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index f30bc23e38..6e92777058 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -186,6 +186,8 @@ CONFIG_SCHEMA = cv.All( esp32_s3_idf=USB_SERIAL_JTAG, esp32_c3_arduino=USB_CDC, esp32_c3_idf=USB_SERIAL_JTAG, + esp32_c6_arduino=USB_CDC, + esp32_c6_idf=USB_SERIAL_JTAG, rp2040=USB_CDC, bk72xx=DEFAULT, rtl87xx=DEFAULT, From 0c032bc431fb76ab5bc239e64733390c4bdfeef4 Mon Sep 17 00:00:00 2001 From: tomaszduda23 Date: Thu, 23 Jan 2025 00:06:07 +0100 Subject: [PATCH 0914/1052] [core] add support for custom platform (#7616) Co-authored-by: Tomasz Duda --- esphome/components/bk72xx/__init__.py | 1 + esphome/components/esp32/__init__.py | 1 + esphome/components/esp8266/__init__.py | 1 + esphome/components/host/__init__.py | 1 + esphome/components/libretiny/__init__.py | 1 + esphome/components/rp2040/__init__.py | 1 + esphome/components/rtl87xx/__init__.py | 2 ++ esphome/config.py | 9 +++--- esphome/const.py | 9 ------ esphome/core/config.py | 37 +++++++++++++++++++++--- esphome/loader.py | 16 +++++++--- 11 files changed, 57 insertions(+), 22 deletions(-) diff --git a/esphome/components/bk72xx/__init__.py b/esphome/components/bk72xx/__init__.py index b5122de956..5b14d0529d 100644 --- a/esphome/components/bk72xx/__init__.py +++ b/esphome/components/bk72xx/__init__.py @@ -19,6 +19,7 @@ from .boards import BK72XX_BOARD_PINS, BK72XX_BOARDS CODEOWNERS = ["@kuba2k2"] AUTO_LOAD = ["libretiny"] +IS_TARGET_PLATFORM = True COMPONENT_DATA = LibreTinyComponent( name=COMPONENT_BK72XX, diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 98db45831a..23b84f3d13 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -64,6 +64,7 @@ from .gpio import esp32_pin_to_code # noqa _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["preferences"] +IS_TARGET_PLATFORM = True CONF_RELEASE = "release" diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index c73027fe1b..c949e53aa6 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -34,6 +34,7 @@ from .gpio import PinInitialState, add_pin_initial_states_array CODEOWNERS = ["@esphome/core"] _LOGGER = logging.getLogger(__name__) AUTO_LOAD = ["preferences"] +IS_TARGET_PLATFORM = True def set_core_data(config): diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index eb8cfbd984..e275adafa9 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -17,6 +17,7 @@ from .gpio import host_pin_to_code # noqa CODEOWNERS = ["@esphome/core", "@clydebarrow"] AUTO_LOAD = ["network", "preferences"] +IS_TARGET_PLATFORM = True def set_core_data(config): diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index b29d2e309c..5bdfb15e19 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -47,6 +47,7 @@ from .const import ( _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@kuba2k2"] AUTO_LOAD = ["preferences"] +IS_TARGET_PLATFORM = True def _detect_variant(value): diff --git a/esphome/components/rp2040/__init__.py b/esphome/components/rp2040/__init__.py index b04e539182..3d73cad195 100644 --- a/esphome/components/rp2040/__init__.py +++ b/esphome/components/rp2040/__init__.py @@ -27,6 +27,7 @@ from .gpio import rp2040_pin_to_code # noqa _LOGGER = logging.getLogger(__name__) CODEOWNERS = ["@jesserockz"] AUTO_LOAD = ["preferences"] +IS_TARGET_PLATFORM = True def set_core_data(config): diff --git a/esphome/components/rtl87xx/__init__.py b/esphome/components/rtl87xx/__init__.py index 4c1956f0f4..109c986f75 100644 --- a/esphome/components/rtl87xx/__init__.py +++ b/esphome/components/rtl87xx/__init__.py @@ -19,6 +19,8 @@ from .boards import RTL87XX_BOARD_PINS, RTL87XX_BOARDS CODEOWNERS = ["@kuba2k2"] AUTO_LOAD = ["libretiny"] +IS_TARGET_PLATFORM = True + COMPONENT_DATA = LibreTinyComponent( name=COMPONENT_RTL87XX, diff --git a/esphome/config.py b/esphome/config.py index 65e9ac29bc..09ee2a8f9b 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -22,7 +22,6 @@ from esphome.const import ( CONF_PACKAGES, CONF_PLATFORM, CONF_SUBSTITUTIONS, - TARGET_PLATFORMS, ) from esphome.core import CORE, DocumentRange, EsphomeError import esphome.core.config as core_config @@ -833,7 +832,7 @@ def validate_config( result[CONF_ESPHOME] = config[CONF_ESPHOME] result.add_output_path([CONF_ESPHOME], CONF_ESPHOME) try: - core_config.preload_core_config(config, result) + target_platform = core_config.preload_core_config(config, result) except vol.Invalid as err: result.add_error(err) return result @@ -845,9 +844,9 @@ def validate_config( cv.All(cv.version_number, cv.validate_esphome_version)(min_version) # First run platform validation steps - for key in TARGET_PLATFORMS: - if key in config: - result.add_validation_step(LoadValidationStep(key, config[key])) + result.add_validation_step( + LoadValidationStep(target_platform, config[target_platform]) + ) result.run_validation_steps() if result.errors: diff --git a/esphome/const.py b/esphome/const.py index 284f8d5f78..95bf6afc02 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -15,15 +15,6 @@ PLATFORM_LIBRETINY_OLDSTYLE = "libretiny" PLATFORM_RP2040 = "rp2040" PLATFORM_RTL87XX = "rtl87xx" -TARGET_PLATFORMS = [ - PLATFORM_BK72XX, - PLATFORM_ESP32, - PLATFORM_ESP8266, - PLATFORM_HOST, - PLATFORM_LIBRETINY_OLDSTYLE, - PLATFORM_RP2040, - PLATFORM_RTL87XX, -] SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"} HEADER_FILE_EXTENSIONS = {".h", ".hpp", ".tcc"} diff --git a/esphome/core/config.py b/esphome/core/config.py index c6a3b1b294..06ae1d7747 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -1,6 +1,7 @@ import logging import multiprocessing import os +from pathlib import Path from esphome import automation import esphome.codegen as cg @@ -28,7 +29,6 @@ from esphome.const import ( CONF_TRIGGER_ID, CONF_VERSION, KEY_CORE, - TARGET_PLATFORMS, __version__ as ESPHOME_VERSION, ) from esphome.core import CORE, coroutine_with_priority @@ -174,7 +174,31 @@ PRELOAD_CONFIG_SCHEMA = cv.Schema( ) -def preload_core_config(config, result): +def _is_target_platform(name): + from esphome.loader import get_component + + try: + if get_component(name, True).is_target_platform: + return True + except KeyError: + pass + return False + + +def _list_target_platforms(): + target_platforms = [] + root = Path(__file__).parents[1] + for path in (root / "components").iterdir(): + if not path.is_dir(): + continue + if not (path / "__init__.py").is_file(): + continue + if _is_target_platform(path.name): + target_platforms += [path.name] + return target_platforms + + +def preload_core_config(config, result) -> str: with cv.prepend_path(CONF_ESPHOME): conf = PRELOAD_CONFIG_SCHEMA(config[CONF_ESPHOME]) @@ -187,12 +211,16 @@ def preload_core_config(config, result): conf[CONF_BUILD_PATH] = os.path.join(build_path, CORE.name) CORE.build_path = CORE.relative_internal_path(conf[CONF_BUILD_PATH]) - target_platforms = [key for key in TARGET_PLATFORMS if key in config] + target_platforms = [] + + for domain, _ in config.items(): + if _is_target_platform(domain): + target_platforms += [domain] if not target_platforms: raise cv.Invalid( "Platform missing. You must include one of the available platform keys: " - + ", ".join(TARGET_PLATFORMS), + + ", ".join(_list_target_platforms()), [CONF_ESPHOME], ) if len(target_platforms) > 1: @@ -202,6 +230,7 @@ def preload_core_config(config, result): ) config[CONF_ESPHOME] = conf + return target_platforms[0] def include_file(path, basename): diff --git a/esphome/loader.py b/esphome/loader.py index d808805119..0fb4187b04 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -52,6 +52,10 @@ class ComponentManifest: def is_platform_component(self) -> bool: return getattr(self.module, "IS_PLATFORM_COMPONENT", False) + @property + def is_target_platform(self) -> bool: + return getattr(self.module, "IS_TARGET_PLATFORM", False) + @property def config_schema(self) -> Optional[Any]: return getattr(self.module, "CONFIG_SCHEMA", None) @@ -169,13 +173,15 @@ def install_custom_components_meta_finder(): install_meta_finder(custom_components_dir) -def _lookup_module(domain): +def _lookup_module(domain, exception): if domain in _COMPONENT_CACHE: return _COMPONENT_CACHE[domain] try: module = importlib.import_module(f"esphome.components.{domain}") except ImportError as e: + if exception: + raise if "No module named" in str(e): _LOGGER.info( "Unable to import component %s: %s", domain, str(e), exc_info=False @@ -184,6 +190,8 @@ def _lookup_module(domain): _LOGGER.error("Unable to import component %s:", domain, exc_info=True) return None except Exception: # pylint: disable=broad-except + if exception: + raise _LOGGER.error("Unable to load component %s:", domain, exc_info=True) return None @@ -192,14 +200,14 @@ def _lookup_module(domain): return manif -def get_component(domain): +def get_component(domain, exception=False): assert "." not in domain - return _lookup_module(domain) + return _lookup_module(domain, exception) def get_platform(domain, platform): full = f"{platform}.{domain}" - return _lookup_module(full) + return _lookup_module(full, False) _COMPONENT_CACHE = {} From d4857a17271122d1304b298b8f71dd27b0760f1b Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Thu, 23 Jan 2025 00:07:26 +0100 Subject: [PATCH 0915/1052] Add verbose logging for pulse width calculation in pulse_meter (#8124) --- esphome/components/pulse_meter/pulse_meter_sensor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.cpp b/esphome/components/pulse_meter/pulse_meter_sensor.cpp index 530425563c..836a84b391 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.cpp +++ b/esphome/components/pulse_meter/pulse_meter_sensor.cpp @@ -75,6 +75,8 @@ void PulseMeterSensor::loop() { case MeterState::RUNNING: { uint32_t delta_us = this->get_->last_detected_edge_us_ - this->last_processed_edge_us_; float pulse_width_us = delta_us / float(this->get_->count_); + ESP_LOGV(TAG, "New pulse, delta: %" PRIu32 " µs, count: %" PRIu32 ", width: %.5f µs", delta_us, + this->get_->count_, pulse_width_us); this->publish_state((60.0f * 1000000.0f) / pulse_width_us); } break; } From 8aeb08f8689a5d746c6ef0d36f4d3f958edd2ac3 Mon Sep 17 00:00:00 2001 From: brambo123 <52667932+brambo123@users.noreply.github.com> Date: Thu, 23 Jan 2025 00:31:07 +0100 Subject: [PATCH 0916/1052] [ads1115] Add sample rate control (#8102) --- esphome/components/ads1115/ads1115.cpp | 63 ++++++++++++++++--- esphome/components/ads1115/ads1115.h | 14 ++++- esphome/components/ads1115/sensor/__init__.py | 16 +++++ .../ads1115/sensor/ads1115_sensor.cpp | 3 +- .../ads1115/sensor/ads1115_sensor.h | 2 + tests/components/ads1115/test.esp32-ard.yaml | 1 + .../components/ads1115/test.esp32-c3-ard.yaml | 1 + .../components/ads1115/test.esp32-c3-idf.yaml | 1 + tests/components/ads1115/test.esp32-idf.yaml | 1 + .../components/ads1115/test.esp8266-ard.yaml | 1 + tests/components/ads1115/test.rp2040-ard.yaml | 1 + 11 files changed, 95 insertions(+), 9 deletions(-) diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index 218edc4c81..c05064383c 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -9,8 +9,6 @@ static const char *const TAG = "ads1115"; static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00; static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; -static const uint8_t ADS1115_DATA_RATE_860_SPS = 0b111; // 3300_SPS for ADS1015 - void ADS1115Component::setup() { ESP_LOGCONFIG(TAG, "Setting up ADS1115..."); uint16_t value; @@ -43,9 +41,9 @@ void ADS1115Component::setup() { config |= 0b0000000100000000; } - // Set data rate - 860 samples per second (we're in singleshot mode) + // Set data rate - 860 samples per second // 0bxxxxxxxx100xxxxx - config |= ADS1115_DATA_RATE_860_SPS << 5; + config |= ADS1115_860SPS << 5; // Set comparator mode - hysteresis // 0bxxxxxxxxxxx0xxxx @@ -77,7 +75,7 @@ void ADS1115Component::dump_config() { } } float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, - ADS1115Resolution resolution) { + ADS1115Resolution resolution, ADS1115Samplerate samplerate) { uint16_t config = this->prev_config_; // Multiplexer // 0bxBBBxxxxxxxxxxxx @@ -89,6 +87,11 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1 config &= 0b1111000111111111; config |= (gain & 0b111) << 9; + // Sample rate + // 0bxxxxxxxxBBBxxxxx + config &= 0b1111111100011111; + config |= (samplerate & 0b111) << 5; + if (!this->continuous_mode_) { // Start conversion config |= 0b1000000000000000; @@ -101,8 +104,54 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1 } this->prev_config_ = config; - // about 1.2 ms with 860 samples per second - delay(2); + // Delay calculated as: ceil((1000/SPS)+.5) + if (resolution == ADS1015_12_BITS) { + switch (samplerate) { + case ADS1115_8SPS: + delay(9); + break; + case ADS1115_16SPS: + delay(5); + break; + case ADS1115_32SPS: + delay(3); + break; + case ADS1115_64SPS: + case ADS1115_128SPS: + delay(2); + break; + default: + delay(1); + break; + } + } else { + switch (samplerate) { + case ADS1115_8SPS: + delay(126); // NOLINT + break; + case ADS1115_16SPS: + delay(63); // NOLINT + break; + case ADS1115_32SPS: + delay(32); + break; + case ADS1115_64SPS: + delay(17); + break; + case ADS1115_128SPS: + delay(9); + break; + case ADS1115_250SPS: + delay(5); + break; + case ADS1115_475SPS: + delay(3); + break; + case ADS1115_860SPS: + delay(2); + break; + } + } // in continuous mode, conversion will always be running, rely on the delay // to ensure conversion is taking place with the correct settings diff --git a/esphome/components/ads1115/ads1115.h b/esphome/components/ads1115/ads1115.h index 509333d2c8..e65835a386 100644 --- a/esphome/components/ads1115/ads1115.h +++ b/esphome/components/ads1115/ads1115.h @@ -33,6 +33,17 @@ enum ADS1115Resolution { ADS1015_12_BITS = 12, }; +enum ADS1115Samplerate { + ADS1115_8SPS = 0b000, + ADS1115_16SPS = 0b001, + ADS1115_32SPS = 0b010, + ADS1115_64SPS = 0b011, + ADS1115_128SPS = 0b100, + ADS1115_250SPS = 0b101, + ADS1115_475SPS = 0b110, + ADS1115_860SPS = 0b111 +}; + class ADS1115Component : public Component, public i2c::I2CDevice { public: void setup() override; @@ -42,7 +53,8 @@ class ADS1115Component : public Component, public i2c::I2CDevice { void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; } /// Helper method to request a measurement from a sensor. - float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution); + float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, + ADS1115Samplerate samplerate); protected: uint16_t prev_config_{0}; diff --git a/esphome/components/ads1115/sensor/__init__.py b/esphome/components/ads1115/sensor/__init__.py index baec31d35c..f346a71198 100644 --- a/esphome/components/ads1115/sensor/__init__.py +++ b/esphome/components/ads1115/sensor/__init__.py @@ -5,6 +5,7 @@ from esphome.const import ( CONF_GAIN, CONF_MULTIPLEXER, CONF_RESOLUTION, + CONF_SAMPLE_RATE, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT, UNIT_VOLT, @@ -43,6 +44,17 @@ RESOLUTION = { "12_BITS": ADS1115Resolution.ADS1015_12_BITS, } +ADS1115Samplerate = ads1115_ns.enum("ADS1115Samplerate") +SAMPLERATE = { + "8": ADS1115Samplerate.ADS1115_8SPS, + "16": ADS1115Samplerate.ADS1115_16SPS, + "32": ADS1115Samplerate.ADS1115_32SPS, + "64": ADS1115Samplerate.ADS1115_64SPS, + "128": ADS1115Samplerate.ADS1115_128SPS, + "250": ADS1115Samplerate.ADS1115_250SPS, + "475": ADS1115Samplerate.ADS1115_475SPS, + "860": ADS1115Samplerate.ADS1115_860SPS, +} ADS1115Sensor = ads1115_ns.class_( "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler @@ -64,6 +76,9 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum( RESOLUTION, upper=True, space="_" ), + cv.Optional(CONF_SAMPLE_RATE, default="860"): cv.enum( + SAMPLERATE, string=True + ), } ) .extend(cv.polling_component_schema("60s")) @@ -79,3 +94,4 @@ async def to_code(config): cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER])) cg.add(var.set_gain(config[CONF_GAIN])) cg.add(var.set_resolution(config[CONF_RESOLUTION])) + cg.add(var.set_samplerate(config[CONF_SAMPLE_RATE])) diff --git a/esphome/components/ads1115/sensor/ads1115_sensor.cpp b/esphome/components/ads1115/sensor/ads1115_sensor.cpp index 335fca4845..6de95f1d12 100644 --- a/esphome/components/ads1115/sensor/ads1115_sensor.cpp +++ b/esphome/components/ads1115/sensor/ads1115_sensor.cpp @@ -8,7 +8,7 @@ namespace ads1115 { static const char *const TAG = "ads1115.sensor"; float ADS1115Sensor::sample() { - return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_); + return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_); } void ADS1115Sensor::update() { @@ -24,6 +24,7 @@ void ADS1115Sensor::dump_config() { ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_); ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_); ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_); + ESP_LOGCONFIG(TAG, " Sample rate: %u", this->samplerate_); } } // namespace ads1115 diff --git a/esphome/components/ads1115/sensor/ads1115_sensor.h b/esphome/components/ads1115/sensor/ads1115_sensor.h index 191afc3de6..5ca25c13ad 100644 --- a/esphome/components/ads1115/sensor/ads1115_sensor.h +++ b/esphome/components/ads1115/sensor/ads1115_sensor.h @@ -21,6 +21,7 @@ class ADS1115Sensor : public sensor::Sensor, void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } void set_gain(ADS1115Gain gain) { this->gain_ = gain; } void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; } + void set_samplerate(ADS1115Samplerate samplerate) { this->samplerate_ = samplerate; } float sample() override; void dump_config() override; @@ -29,6 +30,7 @@ class ADS1115Sensor : public sensor::Sensor, ADS1115Multiplexer multiplexer_; ADS1115Gain gain_; ADS1115Resolution resolution_; + ADS1115Samplerate samplerate_; }; } // namespace ads1115 diff --git a/tests/components/ads1115/test.esp32-ard.yaml b/tests/components/ads1115/test.esp32-ard.yaml index a869f2379b..0fdaeff275 100644 --- a/tests/components/ads1115/test.esp32-ard.yaml +++ b/tests/components/ads1115/test.esp32-ard.yaml @@ -10,4 +10,5 @@ sensor: - platform: ads1115 multiplexer: A0_A1 gain: 1.024 + sample_rate: 128 id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp32-c3-ard.yaml b/tests/components/ads1115/test.esp32-c3-ard.yaml index 7ac5a09f3f..265d2cad2c 100644 --- a/tests/components/ads1115/test.esp32-c3-ard.yaml +++ b/tests/components/ads1115/test.esp32-c3-ard.yaml @@ -10,4 +10,5 @@ sensor: - platform: ads1115 multiplexer: A0_A1 gain: 1.024 + sample_rate: 128 id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp32-c3-idf.yaml b/tests/components/ads1115/test.esp32-c3-idf.yaml index 7ac5a09f3f..265d2cad2c 100644 --- a/tests/components/ads1115/test.esp32-c3-idf.yaml +++ b/tests/components/ads1115/test.esp32-c3-idf.yaml @@ -10,4 +10,5 @@ sensor: - platform: ads1115 multiplexer: A0_A1 gain: 1.024 + sample_rate: 128 id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp32-idf.yaml b/tests/components/ads1115/test.esp32-idf.yaml index a869f2379b..0fdaeff275 100644 --- a/tests/components/ads1115/test.esp32-idf.yaml +++ b/tests/components/ads1115/test.esp32-idf.yaml @@ -10,4 +10,5 @@ sensor: - platform: ads1115 multiplexer: A0_A1 gain: 1.024 + sample_rate: 128 id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp8266-ard.yaml b/tests/components/ads1115/test.esp8266-ard.yaml index 7ac5a09f3f..265d2cad2c 100644 --- a/tests/components/ads1115/test.esp8266-ard.yaml +++ b/tests/components/ads1115/test.esp8266-ard.yaml @@ -10,4 +10,5 @@ sensor: - platform: ads1115 multiplexer: A0_A1 gain: 1.024 + sample_rate: 128 id: ads1115_sensor diff --git a/tests/components/ads1115/test.rp2040-ard.yaml b/tests/components/ads1115/test.rp2040-ard.yaml index 7ac5a09f3f..265d2cad2c 100644 --- a/tests/components/ads1115/test.rp2040-ard.yaml +++ b/tests/components/ads1115/test.rp2040-ard.yaml @@ -10,4 +10,5 @@ sensor: - platform: ads1115 multiplexer: A0_A1 gain: 1.024 + sample_rate: 128 id: ads1115_sensor From 65b2d48a6fea3a0ee249d6c2dcf4288ed1a63704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskari=20Lemmel=C3=A4?= <41640457+olemmela@users.noreply.github.com> Date: Thu, 23 Jan 2025 01:32:45 +0200 Subject: [PATCH 0917/1052] Fix mqtt climate step rounding (#8121) --- esphome/components/mqtt/mqtt_climate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index f06574fa26..a8768114a4 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -72,9 +72,9 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // max_temp root[MQTT_MAX_TEMP] = traits.get_visual_max_temperature(); // target_temp_step - root[MQTT_TARGET_TEMPERATURE_STEP] = traits.get_visual_target_temperature_step(); + root[MQTT_TARGET_TEMPERATURE_STEP] = roundf(traits.get_visual_target_temperature_step() * 10) * 0.1; // current_temp_step - root[MQTT_CURRENT_TEMPERATURE_STEP] = traits.get_visual_current_temperature_step(); + root[MQTT_CURRENT_TEMPERATURE_STEP] = roundf(traits.get_visual_current_temperature_step() * 10) * 0.1; // temperature units are always coerced to Celsius internally root[MQTT_TEMPERATURE_UNIT] = "C"; From dee1d849793ae789a6f4bab730f02268b3975e60 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Thu, 23 Jan 2025 00:41:55 +0100 Subject: [PATCH 0918/1052] [spi] Fix data type in bitbash transfer_() (#8125) --- esphome/components/spi/spi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index b13826c443..18f7852757 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -88,7 +88,7 @@ void SPIDelegateBitBash::write(uint16_t data, size_t num_bits) { this->transfer_ uint16_t SPIDelegateBitBash::transfer_(uint16_t data, size_t num_bits) { // Clock starts out at idle level this->clk_pin_->digital_write(clock_polarity_); - uint8_t out_data = 0; + uint16_t out_data = 0; for (uint8_t i = 0; i != num_bits; i++) { uint8_t shift; From 7fccc9ff86193a5f7d094c9253dcd4a5b924e6ad Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:10:19 +1300 Subject: [PATCH 0919/1052] [online_image] Add binary bmp support (#8116) Co-authored-by: guillempages --- esphome/components/online_image/__init__.py | 16 ++- esphome/components/online_image/bmp_image.cpp | 101 ++++++++++++++++++ esphome/components/online_image/bmp_image.h | 40 +++++++ .../components/online_image/image_decoder.cpp | 5 +- .../components/online_image/image_decoder.h | 9 +- .../components/online_image/online_image.cpp | 11 +- .../components/online_image/online_image.h | 8 +- esphome/core/defines.h | 1 + tests/components/online_image/common.yaml | 4 + 9 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 esphome/components/online_image/bmp_image.cpp create mode 100644 esphome/components/online_image/bmp_image.h diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index ca4eefea6f..fdb49fc493 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -61,8 +61,22 @@ class PNGFormat(Format): cg.add_library("pngle", "1.0.2") +class BMPFormat(Format): + def __init__(self): + super().__init__("BMP") + + def actions(self): + cg.add_define("USE_ONLINE_IMAGE_BMP_SUPPORT") + + # New formats can be added here. -IMAGE_FORMATS = {x.image_type: x for x in (PNGFormat(),)} +IMAGE_FORMATS = { + x.image_type: x + for x in ( + PNGFormat(), + BMPFormat(), + ) +} OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_) diff --git a/esphome/components/online_image/bmp_image.cpp b/esphome/components/online_image/bmp_image.cpp new file mode 100644 index 0000000000..af9019a4d2 --- /dev/null +++ b/esphome/components/online_image/bmp_image.cpp @@ -0,0 +1,101 @@ +#include "bmp_image.h" + +#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT + +#include "esphome/components/display/display.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace online_image { + +static const char *const TAG = "online_image.bmp"; + +int HOT BmpDecoder::decode(uint8_t *buffer, size_t size) { + size_t index = 0; + if (this->current_index_ == 0 && index == 0 && size > 14) { + /** + * BMP file format: + * 0-1: Signature (BM) + * 2-5: File size + * 6-9: Reserved + * 10-13: Pixel data offset + * + * Integer values are stored in little-endian format. + */ + + // Check if the file is a BMP image + if (buffer[0] != 'B' || buffer[1] != 'M') { + ESP_LOGE(TAG, "Not a BMP file"); + return DECODE_ERROR_INVALID_TYPE; + } + + this->download_size_ = encode_uint32(buffer[5], buffer[4], buffer[3], buffer[2]); + this->data_offset_ = encode_uint32(buffer[13], buffer[12], buffer[11], buffer[10]); + + this->current_index_ = 14; + index = 14; + } + if (this->current_index_ == 14 && index == 14 && size > this->data_offset_) { + /** + * BMP DIB header: + * 14-17: DIB header size + * 18-21: Image width + * 22-25: Image height + * 26-27: Number of color planes + * 28-29: Bits per pixel + * 30-33: Compression method + * 34-37: Image data size + * 38-41: Horizontal resolution + * 42-45: Vertical resolution + * 46-49: Number of colors in the color table + */ + + this->width_ = encode_uint32(buffer[21], buffer[20], buffer[19], buffer[18]); + this->height_ = encode_uint32(buffer[25], buffer[24], buffer[23], buffer[22]); + this->bits_per_pixel_ = encode_uint16(buffer[29], buffer[28]); + this->compression_method_ = encode_uint32(buffer[33], buffer[32], buffer[31], buffer[30]); + this->image_data_size_ = encode_uint32(buffer[37], buffer[36], buffer[35], buffer[34]); + this->color_table_entries_ = encode_uint32(buffer[49], buffer[48], buffer[47], buffer[46]); + + switch (this->bits_per_pixel_) { + case 1: + this->width_bytes_ = (this->width_ % 8 == 0) ? (this->width_ / 8) : (this->width_ / 8 + 1); + break; + default: + ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_); + return DECODE_ERROR_UNSUPPORTED_FORMAT; + } + + if (this->compression_method_ != 0) { + ESP_LOGE(TAG, "Unsupported compression method: %d", this->compression_method_); + return DECODE_ERROR_UNSUPPORTED_FORMAT; + } + + if (!this->set_size(this->width_, this->height_)) { + return DECODE_ERROR_OUT_OF_MEMORY; + } + this->current_index_ = this->data_offset_; + index = this->data_offset_; + } + while (index < size) { + size_t paint_index = this->current_index_ - this->data_offset_; + + uint8_t current_byte = buffer[index]; + for (uint8_t i = 0; i < 8; i++) { + size_t x = (paint_index * 8) % this->width_ + i; + size_t y = (this->height_ - 1) - (paint_index / this->width_bytes_); + Color c = (current_byte & (1 << (7 - i))) ? display::COLOR_ON : display::COLOR_OFF; + this->draw(x, y, 1, 1, c); + } + this->current_index_++; + index++; + } + this->decoded_bytes_ += size; + return size; +}; + +} // namespace online_image +} // namespace esphome + +#endif // USE_ONLINE_IMAGE_BMP_SUPPORT diff --git a/esphome/components/online_image/bmp_image.h b/esphome/components/online_image/bmp_image.h new file mode 100644 index 0000000000..61192f6a46 --- /dev/null +++ b/esphome/components/online_image/bmp_image.h @@ -0,0 +1,40 @@ +#pragma once + +#include "esphome/core/defines.h" +#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT + +#include "image_decoder.h" + +namespace esphome { +namespace online_image { + +/** + * @brief Image decoder specialization for PNG images. + */ +class BmpDecoder : public ImageDecoder { + public: + /** + * @brief Construct a new BMP Decoder object. + * + * @param display The image to decode the stream into. + */ + BmpDecoder(OnlineImage *image) : ImageDecoder(image) {} + + int HOT decode(uint8_t *buffer, size_t size) override; + + protected: + size_t current_index_{0}; + ssize_t width_{0}; + ssize_t height_{0}; + uint16_t bits_per_pixel_{0}; + uint32_t compression_method_{0}; + uint32_t image_data_size_{0}; + uint32_t color_table_entries_{0}; + size_t width_bytes_{0}; + size_t data_offset_{0}; +}; + +} // namespace online_image +} // namespace esphome + +#endif // USE_ONLINE_IMAGE_BMP_SUPPORT diff --git a/esphome/components/online_image/image_decoder.cpp b/esphome/components/online_image/image_decoder.cpp index 50ec39dfcc..d0d0495ba6 100644 --- a/esphome/components/online_image/image_decoder.cpp +++ b/esphome/components/online_image/image_decoder.cpp @@ -8,10 +8,11 @@ namespace online_image { static const char *const TAG = "online_image.decoder"; -void ImageDecoder::set_size(int width, int height) { - this->image_->resize_(width, height); +bool ImageDecoder::set_size(int width, int height) { + bool resized = this->image_->resize_(width, height); this->x_scale_ = static_cast(this->image_->buffer_width_) / width; this->y_scale_ = static_cast(this->image_->buffer_height_) / height; + return resized; } void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { diff --git a/esphome/components/online_image/image_decoder.h b/esphome/components/online_image/image_decoder.h index 7c5175d72d..3b04824bb9 100644 --- a/esphome/components/online_image/image_decoder.h +++ b/esphome/components/online_image/image_decoder.h @@ -4,6 +4,12 @@ namespace esphome { namespace online_image { +enum DecodeError : int { + DECODE_ERROR_INVALID_TYPE = -1, + DECODE_ERROR_UNSUPPORTED_FORMAT = -2, + DECODE_ERROR_OUT_OF_MEMORY = -3, +}; + class OnlineImage; /** @@ -45,8 +51,9 @@ class ImageDecoder { * * @param width The image's width. * @param height The image's height. + * @return true if the image was resized, false otherwise. */ - void set_size(int width, int height); + bool set_size(int width, int height); /** * @brief Fill a rectangle on the display_buffer using the defined color. diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 93d070c6a9..23fe61b534 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -10,6 +10,10 @@ static const char *const TAG = "online_image"; #include "png_image.h" #endif +#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT +#include "bmp_image.h" +#endif + namespace esphome { namespace online_image { @@ -120,9 +124,14 @@ void OnlineImage::update() { #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT if (this->format_ == ImageFormat::PNG) { - this->decoder_ = esphome::make_unique(this); + this->decoder_ = make_unique(this); } #endif // ONLINE_IMAGE_PNG_SUPPORT +#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT + if (this->format_ == ImageFormat::BMP) { + this->decoder_ = make_unique(this); + } +#endif // ONLINE_IMAGE_BMP_SUPPORT if (!this->decoder_) { ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported."); diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index bafd8ba67e..849f860ad5 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -1,10 +1,10 @@ #pragma once +#include "esphome/components/http_request/http_request.h" +#include "esphome/components/image/image.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" -#include "esphome/components/http_request/http_request.h" -#include "esphome/components/image/image.h" #include "image_decoder.h" @@ -27,6 +27,8 @@ enum ImageFormat { JPEG, /** PNG format. */ PNG, + /** BMP format. */ + BMP, }; /** @@ -146,7 +148,7 @@ class OnlineImage : public PollingComponent, */ int buffer_height_; - friend void ImageDecoder::set_size(int width, int height); + friend bool ImageDecoder::set_size(int width, int height); friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color); }; diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 96a05435ed..074b19809f 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -60,6 +60,7 @@ #define USE_NETWORK #define USE_NEXTION_TFT_UPLOAD #define USE_NUMBER +#define USE_ONLINE_IMAGE_BMP_SUPPORT #define USE_ONLINE_IMAGE_PNG_SUPPORT #define USE_OTA #define USE_OTA_PASSWORD diff --git a/tests/components/online_image/common.yaml b/tests/components/online_image/common.yaml index 6c161e4f20..d75900adf9 100644 --- a/tests/components/online_image/common.yaml +++ b/tests/components/online_image/common.yaml @@ -26,6 +26,10 @@ online_image: format: PNG type: RGB transparency: chroma_key + - id: online_binary_bmp + url: https://samples-files.com/samples/images/bmp/480-360-sample.bmp + format: BMP + type: BINARY # Check the set_url action esphome: From fc847c1de8927871e783db1a35e967efabda9f04 Mon Sep 17 00:00:00 2001 From: guillempages Date: Thu, 23 Jan 2025 21:32:03 +0100 Subject: [PATCH 0920/1052] [online_image] Code Improvements (#8130) --- esphome/components/online_image/__init__.py | 18 +++++++++--------- .../components/online_image/image_decoder.h | 6 +++--- .../components/online_image/online_image.cpp | 17 ++++++++--------- esphome/components/online_image/png_image.cpp | 4 ++-- esphome/components/online_image/png_image.h | 2 +- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index fdb49fc493..eb6debf8eb 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -52,6 +52,14 @@ class Format: pass +class BMPFormat(Format): + def __init__(self): + super().__init__("BMP") + + def actions(self): + cg.add_define("USE_ONLINE_IMAGE_BMP_SUPPORT") + + class PNGFormat(Format): def __init__(self): super().__init__("PNG") @@ -61,20 +69,12 @@ class PNGFormat(Format): cg.add_library("pngle", "1.0.2") -class BMPFormat(Format): - def __init__(self): - super().__init__("BMP") - - def actions(self): - cg.add_define("USE_ONLINE_IMAGE_BMP_SUPPORT") - - # New formats can be added here. IMAGE_FORMATS = { x.image_type: x for x in ( - PNGFormat(), BMPFormat(), + PNGFormat(), ) } diff --git a/esphome/components/online_image/image_decoder.h b/esphome/components/online_image/image_decoder.h index 3b04824bb9..4e5dd7b229 100644 --- a/esphome/components/online_image/image_decoder.h +++ b/esphome/components/online_image/image_decoder.h @@ -30,7 +30,7 @@ class ImageDecoder { * * @param download_size The total number of bytes that need to be downloaded for the image. */ - virtual void prepare(uint32_t download_size) { this->download_size_ = download_size; } + virtual void prepare(size_t download_size) { this->download_size_ = download_size; } /** * @brief Decode a part of the image. It will try reading from the buffer. @@ -75,8 +75,8 @@ class ImageDecoder { OnlineImage *image_; // Initializing to 1, to ensure it is distinguishable from initial "decoded_bytes_". // Will be overwritten anyway once the download size is known. - uint32_t download_size_ = 1; - uint32_t decoded_bytes_ = 0; + size_t download_size_ = 1; + size_t decoded_bytes_ = 0; double x_scale_ = 1.0; double y_scale_ = 1.0; }; diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 23fe61b534..c6499c24e4 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -6,13 +6,12 @@ static const char *const TAG = "online_image"; #include "image_decoder.h" -#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT -#include "png_image.h" -#endif - #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT #include "bmp_image.h" #endif +#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT +#include "png_image.h" +#endif namespace esphome { namespace online_image { @@ -122,16 +121,16 @@ void OnlineImage::update() { ESP_LOGD(TAG, "Starting download"); size_t total_size = this->downloader_->content_length; -#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT - if (this->format_ == ImageFormat::PNG) { - this->decoder_ = make_unique(this); - } -#endif // ONLINE_IMAGE_PNG_SUPPORT #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT if (this->format_ == ImageFormat::BMP) { this->decoder_ = make_unique(this); } #endif // ONLINE_IMAGE_BMP_SUPPORT +#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT + if (this->format_ == ImageFormat::PNG) { + this->decoder_ = make_unique(this); + } +#endif // ONLINE_IMAGE_PNG_SUPPORT if (!this->decoder_) { ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported."); diff --git a/esphome/components/online_image/png_image.cpp b/esphome/components/online_image/png_image.cpp index 4c4c22f9b7..59c1ce6c7d 100644 --- a/esphome/components/online_image/png_image.cpp +++ b/esphome/components/online_image/png_image.cpp @@ -41,7 +41,7 @@ static void draw_callback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, ui decoder->draw(x, y, w, h, color); } -void PngDecoder::prepare(uint32_t download_size) { +void PngDecoder::prepare(size_t download_size) { ImageDecoder::prepare(download_size); pngle_set_user_data(this->pngle_, this); pngle_set_init_callback(this->pngle_, init_callback); @@ -51,7 +51,7 @@ void PngDecoder::prepare(uint32_t download_size) { int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { if (!this->pngle_) { ESP_LOGE(TAG, "PNG decoder engine not initialized!"); - return -1; + return DECODE_ERROR_OUT_OF_MEMORY; } if (size < 256 && size < this->download_size_ - this->decoded_bytes_) { ESP_LOGD(TAG, "Waiting for data"); diff --git a/esphome/components/online_image/png_image.h b/esphome/components/online_image/png_image.h index d82ff93149..c137227907 100644 --- a/esphome/components/online_image/png_image.h +++ b/esphome/components/online_image/png_image.h @@ -21,7 +21,7 @@ class PngDecoder : public ImageDecoder { PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {} ~PngDecoder() override { pngle_destroy(this->pngle_); } - void prepare(uint32_t download_size) override; + void prepare(size_t download_size) override; int HOT decode(uint8_t *buffer, size_t size) override; protected: From dbf4c2c4dae43ee929471a26823db88f373576d6 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Mon, 27 Jan 2025 05:23:57 +0100 Subject: [PATCH 0921/1052] Update mdns for ESP-IDF (#8145) --- esphome/components/mdns/__init__.py | 2 +- esphome/idf_component.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index dd68fbb93c..1bc290b582 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -91,7 +91,7 @@ async def to_code(config): add_idf_component( name="mdns", repo="https://github.com/espressif/esp-protocols.git", - ref="mdns-v1.3.2", + ref="mdns-v1.5.1", path="components/mdns", ) diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index c79ba1b0ed..bd5bcda2fe 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -7,7 +7,7 @@ dependencies: version: v2.0.9 mdns: git: https://github.com/espressif/esp-protocols.git - version: mdns-v1.3.2 + version: mdns-v1.5.1 path: components/mdns rules: - if: "idf_version >=5.0" From dd18a219db7e4d7306c4cbd088baecfd146a811d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 27 Jan 2025 16:57:52 -1000 Subject: [PATCH 0922/1052] Include Bluetooth connection slot allocations in connections free message (#8148) --- esphome/components/api/api.proto | 1 + esphome/components/api/api_pb2.cpp | 14 ++++++++++++++ esphome/components/api/api_pb2.h | 1 + .../components/bluetooth_proxy/bluetooth_proxy.cpp | 5 +++++ 4 files changed, 21 insertions(+) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 684540ffa6..534098e5fd 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1381,6 +1381,7 @@ message BluetoothConnectionsFreeResponse { uint32 free = 1; uint32 limit = 2; + repeated uint64 allocated = 3; } message BluetoothGATTErrorResponse { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 8df152881c..41016e510f 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -6430,6 +6430,10 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar this->limit = value.as_uint32(); return true; } + case 3: { + this->allocated.push_back(value.as_uint64()); + return true; + } default: return false; } @@ -6437,6 +6441,9 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_uint32(1, this->free); buffer.encode_uint32(2, this->limit); + for (auto &it : this->allocated) { + buffer.encode_uint64(3, it, true); + } } #ifdef HAS_PROTO_MESSAGE_DUMP void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { @@ -6451,6 +6458,13 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const { sprintf(buffer, "%" PRIu32, this->limit); out.append(buffer); out.append("\n"); + + for (const auto &it : this->allocated) { + out.append(" allocated: "); + sprintf(buffer, "%llu", it); + out.append(buffer); + out.append("\n"); + } out.append("}"); } #endif diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 063c217bf7..a3fccbc641 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -1624,6 +1624,7 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage { public: uint32_t free{0}; uint32_t limit{0}; + std::vector allocated{}; void encode(ProtoWriteBuffer buffer) const override; #ifdef HAS_PROTO_MESSAGE_DUMP void dump_to(std::string &out) const override; diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index bd1c8b7ea4..a263aca456 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -475,6 +475,11 @@ void BluetoothProxy::send_connections_free() { api::BluetoothConnectionsFreeResponse call; call.free = this->get_bluetooth_connections_free(); call.limit = this->get_bluetooth_connections_limit(); + for (auto *connection : this->connections_) { + if (connection->address_ != 0) { + call.allocated.push_back(connection->address_); + } + } this->api_connection_->send_bluetooth_connections_free_response(call); } From f7f8bf4da44b83796a301bc486d77fdb46d8ed82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mart=C3=ADn?= Date: Tue, 28 Jan 2025 12:00:28 +0100 Subject: [PATCH 0923/1052] [esp32_ble_server] Create custom services, characteristics and descriptors (#7009) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- CODEOWNERS | 1 + .../components/ble_client/sensor/__init__.py | 2 +- .../ble_client/text_sensor/__init__.py | 2 +- esphome/components/esp32_ble/__init__.py | 39 ++ .../components/esp32_ble_server/__init__.py | 594 +++++++++++++++++- .../components/esp32_ble_server/ble_2901.cpp | 18 - .../components/esp32_ble_server/ble_2901.h | 19 - .../esp32_ble_server/ble_characteristic.cpp | 108 ++-- .../esp32_ble_server/ble_characteristic.h | 41 +- .../esp32_ble_server/ble_descriptor.cpp | 30 +- .../esp32_ble_server/ble_descriptor.h | 23 +- .../esp32_ble_server/ble_server.cpp | 129 ++-- .../components/esp32_ble_server/ble_server.h | 60 +- .../ble_server_automations.cpp | 77 +++ .../esp32_ble_server/ble_server_automations.h | 115 ++++ .../esp32_ble_server/ble_service.cpp | 32 +- .../components/esp32_ble_server/ble_service.h | 24 +- .../components/esp32_ble_tracker/__init__.py | 45 +- esphome/components/esp32_improv/__init__.py | 10 +- .../esp32_improv/esp32_improv_component.cpp | 39 +- .../esp32_improv/esp32_improv_component.h | 7 +- esphome/components/event_emitter/__init__.py | 5 + .../event_emitter/event_emitter.cpp | 14 + .../components/event_emitter/event_emitter.h | 63 ++ esphome/const.py | 1 + tests/components/esp32_ble_server/common.yaml | 65 +- 26 files changed, 1221 insertions(+), 342 deletions(-) delete mode 100644 esphome/components/esp32_ble_server/ble_2901.cpp delete mode 100644 esphome/components/esp32_ble_server/ble_2901.h create mode 100644 esphome/components/esp32_ble_server/ble_server_automations.cpp create mode 100644 esphome/components/esp32_ble_server/ble_server_automations.h create mode 100644 esphome/components/event_emitter/__init__.py create mode 100644 esphome/components/event_emitter/event_emitter.cpp create mode 100644 esphome/components/event_emitter/event_emitter.h diff --git a/CODEOWNERS b/CODEOWNERS index aa24b6cb82..cdf4ab7a99 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -148,6 +148,7 @@ esphome/components/esp32_rmt_led_strip/* @jesserockz esphome/components/esp8266/* @esphome/core esphome/components/ethernet_info/* @gtjadsonsantos esphome/components/event/* @nohat +esphome/components/event_emitter/* @Rapsssito esphome/components/exposure_notifications/* @OttoWinter esphome/components/ezo/* @ssieb esphome/components/ezo_pmp/* @carlos-sarmiento diff --git a/esphome/components/ble_client/sensor/__init__.py b/esphome/components/ble_client/sensor/__init__.py index 0c48902a90..960410a5cc 100644 --- a/esphome/components/ble_client/sensor/__init__.py +++ b/esphome/components/ble_client/sensor/__init__.py @@ -11,6 +11,7 @@ from esphome.const import ( DEVICE_CLASS_SIGNAL_STRENGTH, STATE_CLASS_MEASUREMENT, UNIT_DECIBEL_MILLIWATT, + CONF_NOTIFY, ) from .. import ble_client_ns @@ -19,7 +20,6 @@ DEPENDENCIES = ["ble_client"] CONF_DESCRIPTOR_UUID = "descriptor_uuid" -CONF_NOTIFY = "notify" CONF_ON_NOTIFY = "on_notify" TYPE_CHARACTERISTIC = "characteristic" TYPE_RSSI = "rssi" diff --git a/esphome/components/ble_client/text_sensor/__init__.py b/esphome/components/ble_client/text_sensor/__init__.py index 479af1a57e..a6672e68f5 100644 --- a/esphome/components/ble_client/text_sensor/__init__.py +++ b/esphome/components/ble_client/text_sensor/__init__.py @@ -6,6 +6,7 @@ from esphome.const import ( CONF_CHARACTERISTIC_UUID, CONF_ID, CONF_SERVICE_UUID, + CONF_NOTIFY, CONF_TRIGGER_ID, ) @@ -15,7 +16,6 @@ DEPENDENCIES = ["ble_client"] CONF_DESCRIPTOR_UUID = "descriptor_uuid" -CONF_NOTIFY = "notify" CONF_ON_NOTIFY = "on_notify" adv_data_t = cg.std_vector.template(cg.uint8) diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index 08e30c9247..37b4900a03 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -1,3 +1,5 @@ +import re + from esphome import automation import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant @@ -64,6 +66,43 @@ CONFIG_SCHEMA = cv.Schema( ).extend(cv.COMPONENT_SCHEMA) +bt_uuid16_format = "XXXX" +bt_uuid32_format = "XXXXXXXX" +bt_uuid128_format = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + + +def bt_uuid(value): + in_value = cv.string_strict(value) + value = in_value.upper() + + if len(value) == len(bt_uuid16_format): + pattern = re.compile("^[A-F|0-9]{4,}$") + if not pattern.match(value): + raise cv.Invalid( + f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'" + ) + return value + if len(value) == len(bt_uuid32_format): + pattern = re.compile("^[A-F|0-9]{8,}$") + if not pattern.match(value): + raise cv.Invalid( + f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'" + ) + return value + if len(value) == len(bt_uuid128_format): + pattern = re.compile( + "^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$" + ) + if not pattern.match(value): + raise cv.Invalid( + f"Invalid hexadecimal value for 128 UUID format: '{in_value}'" + ) + return value + raise cv.Invalid( + f"Bluetooth UUID must be in 16 bit '{bt_uuid16_format}', 32 bit '{bt_uuid32_format}', or 128 bit '{bt_uuid128_format}' format" + ) + + def validate_variant(_): variant = get_esp32_variant() if variant in NO_BLUETOOTH_VARIANTS: diff --git a/esphome/components/esp32_ble_server/__init__.py b/esphome/components/esp32_ble_server/__init__.py index 9da7d13999..ab8e27ec43 100644 --- a/esphome/components/esp32_ble_server/__init__.py +++ b/esphome/components/esp32_ble_server/__init__.py @@ -1,37 +1,526 @@ +import encodings + +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 import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_MODEL +from esphome.config_validation import UNDEFINED +from esphome.const import ( + CONF_DATA, + CONF_ESPHOME, + CONF_ID, + CONF_MAX_LENGTH, + CONF_MODEL, + CONF_NOTIFY, + CONF_ON_CONNECT, + CONF_ON_DISCONNECT, + CONF_PROJECT, + CONF_SERVICES, + CONF_TYPE, + CONF_UUID, + CONF_VALUE, + __version__ as ESPHOME_VERSION, +) from esphome.core import CORE +from esphome.schema_extractors import SCHEMA_EXTRACT -AUTO_LOAD = ["esp32_ble"] +AUTO_LOAD = ["esp32_ble", "bytebuffer", "event_emitter"] CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"] DEPENDENCIES = ["esp32"] +DOMAIN = "esp32_ble_server" +CONF_ADVERTISE = "advertise" +CONF_BROADCAST = "broadcast" +CONF_CHARACTERISTICS = "characteristics" +CONF_DESCRIPTION = "description" +CONF_DESCRIPTORS = "descriptors" +CONF_ENDIANNESS = "endianness" +CONF_FIRMWARE_VERSION = "firmware_version" +CONF_INDICATE = "indicate" CONF_MANUFACTURER = "manufacturer" CONF_MANUFACTURER_DATA = "manufacturer_data" +CONF_ON_WRITE = "on_write" +CONF_READ = "read" +CONF_STRING = "string" +CONF_STRING_ENCODING = "string_encoding" +CONF_WRITE = "write" +CONF_WRITE_NO_RESPONSE = "write_no_response" + +# Internal configuration keys +CONF_CHAR_VALUE_ACTION_ID_ = "char_value_action_id_" + +# BLE reserverd UUIDs +CCCD_DESCRIPTOR_UUID = 0x2902 +CUD_DESCRIPTOR_UUID = 0x2901 +DEVICE_INFORMATION_SERVICE_UUID = 0x180A +MANUFACTURER_NAME_CHARACTERISTIC_UUID = 0x2A29 +MODEL_CHARACTERISTIC_UUID = 0x2A24 +FIRMWARE_VERSION_CHARACTERISTIC_UUID = 0x2A26 + +# Core key to store the global configuration +KEY_NOTIFY_REQUIRED = "notify_required" +KEY_SET_VALUE = "set_value" esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server") +ESPBTUUID_ns = cg.esphome_ns.namespace("esp32_ble").namespace("ESPBTUUID") +BLECharacteristic_ns = esp32_ble_server_ns.namespace("BLECharacteristic") BLEServer = esp32_ble_server_ns.class_( "BLEServer", cg.Component, esp32_ble.GATTsEventHandler, cg.Parented.template(esp32_ble.ESP32BLE), ) -BLEServiceComponent = esp32_ble_server_ns.class_("BLEServiceComponent") +esp32_ble_server_automations_ns = esp32_ble_server_ns.namespace( + "esp32_ble_server_automations" +) +BLETriggers_ns = esp32_ble_server_automations_ns.namespace("BLETriggers") +BLEDescriptor = esp32_ble_server_ns.class_("BLEDescriptor") +BLECharacteristic = esp32_ble_server_ns.class_("BLECharacteristic") +BLEService = esp32_ble_server_ns.class_("BLEService") +BLECharacteristicSetValueAction = esp32_ble_server_automations_ns.class_( + "BLECharacteristicSetValueAction", automation.Action +) +BLEDescriptorSetValueAction = esp32_ble_server_automations_ns.class_( + "BLEDescriptorSetValueAction", automation.Action +) +BLECharacteristicNotifyAction = esp32_ble_server_automations_ns.class_( + "BLECharacteristicNotifyAction", automation.Action +) +bytebuffer_ns = cg.esphome_ns.namespace("bytebuffer") +Endianness_ns = bytebuffer_ns.namespace("Endian") +ByteBuffer_ns = bytebuffer_ns.namespace("ByteBuffer") +ByteBuffer = bytebuffer_ns.class_("ByteBuffer") +PROPERTY_MAP = { + CONF_READ: BLECharacteristic_ns.PROPERTY_READ, + CONF_WRITE: BLECharacteristic_ns.PROPERTY_WRITE, + CONF_NOTIFY: BLECharacteristic_ns.PROPERTY_NOTIFY, + CONF_BROADCAST: BLECharacteristic_ns.PROPERTY_BROADCAST, + CONF_INDICATE: BLECharacteristic_ns.PROPERTY_INDICATE, + CONF_WRITE_NO_RESPONSE: BLECharacteristic_ns.PROPERTY_WRITE_NR, +} + + +class ValueType: + def __init__(self, type_, validator, length): + self.type_ = type_ + self.validator = validator + self.length = length + + def validate(self, value, encoding): + value = self.validator(value) + if self.type_ == "string": + try: + value.encode(encoding) + except UnicodeEncodeError as e: + raise cv.Invalid(str(e)) from e + return value + + +VALUE_TYPES = { + type_name: ValueType(type_name, validator, length) + for type_name, validator, length in ( + ("uint8_t", cv.uint8_t, 1), + ("uint16_t", cv.uint16_t, 2), + ("uint32_t", cv.uint32_t, 4), + ("uint64_t", cv.uint64_t, 8), + ("int8_t", cv.int_range(-128, 127), 1), + ("int16_t", cv.int_range(-32768, 32767), 2), + ("int32_t", cv.int_range(-2147483648, 2147483647), 4), + ("int64_t", cv.int_range(-9223372036854775808, 9223372036854775807), 8), + ("float", cv.float_, 4), + ("double", cv.float_, 8), + ("string", cv.string_strict, None), # Length is variable + ) +} + + +def validate_char_on_write(char_config): + if CONF_ON_WRITE in char_config: + if not char_config[CONF_WRITE] and not char_config[CONF_WRITE_NO_RESPONSE]: + raise cv.Invalid( + f"{CONF_ON_WRITE} requires the {CONF_WRITE} or {CONF_WRITE_NO_RESPONSE} property to be set" + ) + return char_config + + +def validate_descriptor(desc_config): + if CONF_ON_WRITE in desc_config: + if not desc_config[CONF_WRITE]: + raise cv.Invalid( + f"{CONF_ON_WRITE} requires the {CONF_WRITE} property to be set" + ) + if CONF_MAX_LENGTH not in desc_config: + value = desc_config[CONF_VALUE][CONF_DATA] + if cg.is_template(value): + raise cv.Invalid( + f"Descriptor {desc_config[CONF_UUID]} has a templatable value and the {CONF_MAX_LENGTH} property is not set" + ) + if isinstance(value, list): + desc_config[CONF_MAX_LENGTH] = len(value) + elif isinstance(value, str): + desc_config[CONF_MAX_LENGTH] = len( + value.encode(desc_config[CONF_VALUE][CONF_STRING_ENCODING]) + ) + else: + desc_config[CONF_MAX_LENGTH] = VALUE_TYPES[ + desc_config[CONF_VALUE][CONF_TYPE] + ].length + return desc_config + + +def validate_notify_action(config): + # Store the characteristic ID in the global data for the final validation + data = CORE.data.setdefault(DOMAIN, {}).setdefault(KEY_NOTIFY_REQUIRED, set()) + data.add(config[CONF_ID]) + return config + + +def validate_set_value_action(config): + # Store the characteristic ID in the global data for the final validation + data = CORE.data.setdefault(DOMAIN, {}).setdefault(KEY_SET_VALUE, set()) + data.add(config[CONF_ID]) + return config + + +def create_description_cud(char_config): + if CONF_DESCRIPTION not in char_config: + return char_config + # If the config displays a description, there cannot be a descriptor with the CUD UUID + for desc in char_config[CONF_DESCRIPTORS]: + if desc[CONF_UUID] == CUD_DESCRIPTOR_UUID: + raise cv.Invalid( + f"Characteristic {char_config[CONF_UUID]} has a description, but a CUD descriptor is already present" + ) + # Manually add the CUD descriptor + char_config[CONF_DESCRIPTORS].append( + DESCRIPTOR_SCHEMA( + { + CONF_UUID: CUD_DESCRIPTOR_UUID, + CONF_READ: True, + CONF_WRITE: False, + CONF_VALUE: char_config[CONF_DESCRIPTION], + } + ) + ) + return char_config + + +def create_notify_cccd(char_config): + if not char_config[CONF_NOTIFY] and not char_config[CONF_INDICATE]: + return char_config + # If the CCCD descriptor is already present, return the config + for desc in char_config[CONF_DESCRIPTORS]: + if desc[CONF_UUID] == CCCD_DESCRIPTOR_UUID: + # Check if the WRITE property is set + if not desc[CONF_WRITE]: + raise cv.Invalid( + f"Characteristic {char_config[CONF_UUID]} has notify actions, but the CCCD descriptor does not have the {CONF_WRITE} property set" + ) + return char_config + # Manually add the CCCD descriptor + char_config[CONF_DESCRIPTORS].append( + DESCRIPTOR_SCHEMA( + { + CONF_UUID: CCCD_DESCRIPTOR_UUID, + CONF_READ: True, + CONF_WRITE: True, + CONF_MAX_LENGTH: 2, + CONF_VALUE: [0, 0], + } + ) + ) + return char_config + + +def create_device_information_service(config): + # If there is already a device information service, + # there cannot be CONF_MODEL, CONF_MANUFACTURER or CONF_FIRMWARE_VERSION properties + for service in config[CONF_SERVICES]: + if service[CONF_UUID] == DEVICE_INFORMATION_SERVICE_UUID: + if ( + CONF_MODEL in config + or CONF_MANUFACTURER in config + or CONF_FIRMWARE_VERSION in config + ): + raise cv.Invalid( + "Device information service already present, cannot add manufacturer, model or firmware version" + ) + return config + project = CORE.raw_config[CONF_ESPHOME].get(CONF_PROJECT, {}) + model = config.get(CONF_MODEL, project.get("name", CORE.data["esp32"]["board"])) + version = config.get( + CONF_FIRMWARE_VERSION, project.get("version", "ESPHome " + ESPHOME_VERSION) + ) + # Manually add the device information service + config[CONF_SERVICES].append( + SERVICE_SCHEMA( + { + CONF_UUID: DEVICE_INFORMATION_SERVICE_UUID, + CONF_CHARACTERISTICS: [ + { + CONF_UUID: MANUFACTURER_NAME_CHARACTERISTIC_UUID, + CONF_READ: True, + CONF_VALUE: config.get(CONF_MANUFACTURER, "ESPHome"), + }, + { + CONF_UUID: MODEL_CHARACTERISTIC_UUID, + CONF_READ: True, + CONF_VALUE: model, + }, + { + CONF_UUID: FIRMWARE_VERSION_CHARACTERISTIC_UUID, + CONF_READ: True, + CONF_VALUE: version, + }, + ], + } + ) + ) + return config + + +def final_validate_config(config): + # Check if all characteristics that require notifications have the notify property set + for char_id in CORE.data.get(DOMAIN, {}).get(KEY_NOTIFY_REQUIRED, set()): + # Look for the characteristic in the configuration + char_config = [ + char_conf + for service_conf in config[CONF_SERVICES] + for char_conf in service_conf[CONF_CHARACTERISTICS] + if char_conf[CONF_ID] == char_id + ][0] + if not char_config[CONF_NOTIFY]: + raise cv.Invalid( + f"Characteristic {char_config[CONF_UUID]} has notify actions and the {CONF_NOTIFY} property is not set" + ) + for char_id in CORE.data.get(DOMAIN, {}).get(KEY_SET_VALUE, set()): + # Look for the characteristic in the configuration + char_config = [ + char_conf + for service_conf in config[CONF_SERVICES] + for char_conf in service_conf[CONF_CHARACTERISTICS] + if char_conf[CONF_ID] == char_id + ][0] + if isinstance(char_config.get(CONF_VALUE, {}).get(CONF_DATA), cv.Lambda): + raise cv.Invalid( + f"Characteristic {char_config[CONF_UUID]} has both a set_value action and a templated value" + ) + return config + + +def validate_value_type(value_config): + # If the value is a not a templatable, the type must be set + value = value_config[CONF_DATA] + + if type_ := value_config.get(CONF_TYPE): + if cg.is_template(value): + raise cv.Invalid( + f'The "{CONF_TYPE}" property is not allowed for templatable values' + ) + value_config[CONF_DATA] = VALUE_TYPES[type_].validate( + value, value_config[CONF_STRING_ENCODING] + ) + elif isinstance(value, (float, int)): + raise cv.Invalid( + f'The "{CONF_TYPE}" property is required for the value "{value}"' + ) + return value_config + + +def validate_encoding(value): + if value == SCHEMA_EXTRACT: + return cv.one_of("utf-8", "latin-1", "ascii", "utf-16", "utf-32") + value = encodings.normalize_encoding(value) + if not value: + raise cv.Invalid("Invalid encoding") + return value + + +def value_schema(default_type=UNDEFINED, templatable=True): + data_validators = [ + cv.string_strict, + cv.int_, + cv.float_, + cv.All([cv.uint8_t], cv.Length(min=1)), + ] + if templatable: + data_validators.append(cv.returning_lambda) + + return cv.maybe_simple_value( + cv.All( + { + cv.Required(CONF_DATA): cv.Any(*data_validators), + cv.Optional(CONF_TYPE, default=default_type): cv.one_of( + *VALUE_TYPES, lower=True + ), + cv.Optional(CONF_STRING_ENCODING, default="utf_8"): validate_encoding, + cv.Optional(CONF_ENDIANNESS, default="LITTLE"): cv.enum( + { + "LITTLE": Endianness_ns.LITTLE, + "BIG": Endianness_ns.BIG, + } + ), + }, + validate_value_type, + ), + key=CONF_DATA, + ) + + +DESCRIPTOR_SCHEMA = cv.All( + { + cv.GenerateID(): cv.declare_id(BLEDescriptor), + cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t), + cv.Optional(CONF_READ, default=True): cv.boolean, + cv.Optional(CONF_WRITE, default=True): cv.boolean, + cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True), + cv.Required(CONF_VALUE): value_schema(templatable=False), + cv.Optional(CONF_MAX_LENGTH): cv.uint16_t, + }, + validate_descriptor, +) + +CHARACTERISTIC_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(BLECharacteristic), + cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t), + cv.Optional(CONF_VALUE): value_schema(templatable=True), + cv.GenerateID(CONF_CHAR_VALUE_ACTION_ID_): cv.declare_id( + BLECharacteristicSetValueAction + ), + cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list(DESCRIPTOR_SCHEMA), + cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True), + cv.Optional(CONF_DESCRIPTION): value_schema( + default_type="string", templatable=False + ), + }, + extra_schemas=[ + validate_char_on_write, + create_description_cud, + create_notify_cccd, + ], +).extend({cv.Optional(k, default=False): cv.boolean for k in PROPERTY_MAP}) + +SERVICE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(BLEService), + cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t), + cv.Optional(CONF_ADVERTISE, default=False): cv.boolean, + cv.Optional(CONF_CHARACTERISTICS, default=[]): cv.ensure_list( + CHARACTERISTIC_SCHEMA + ), + } +) + CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(BLEServer), cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE), - cv.Optional(CONF_MANUFACTURER, default="ESPHome"): cv.string, - cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.hex_uint8_t]), - cv.Optional(CONF_MODEL): cv.string, - } + cv.Optional(CONF_MANUFACTURER): value_schema("string", templatable=False), + cv.Optional(CONF_MODEL): value_schema("string", templatable=False), + cv.Optional(CONF_FIRMWARE_VERSION): value_schema("string", templatable=False), + cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.uint8_t]), + cv.Optional(CONF_SERVICES, default=[]): cv.ensure_list(SERVICE_SCHEMA), + cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True), + cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(single=True), + }, + extra_schemas=[create_device_information_service], ).extend(cv.COMPONENT_SCHEMA) +FINAL_VALIDATE_SCHEMA = final_validate_config + + +def parse_properties(char_conf): + return sum( + (PROPERTY_MAP[k] for k in char_conf if k in PROPERTY_MAP and char_conf[k]), + start=0, + ) + + +def parse_uuid(uuid): + # If the UUID is a int, use from_uint32 + if isinstance(uuid, int): + return ESPBTUUID_ns.from_uint32(uuid) + # Otherwise, use ESPBTUUID_ns.from_raw + return ESPBTUUID_ns.from_raw(uuid) + + +async def parse_value(value_config, args): + value = value_config[CONF_DATA] + if isinstance(value, cv.Lambda): + return await cg.templatable(value, args, cg.std_vector.template(cg.uint8)) + + if isinstance(value, str): + value = list(value.encode(value_config[CONF_STRING_ENCODING])) + if isinstance(value, list): + return cg.std_vector.template(cg.uint8)(value) + val = cg.RawExpression(f"{value_config[CONF_TYPE]}({cg.safe_exp(value)})") + return ByteBuffer_ns.wrap(val, value_config[CONF_ENDIANNESS]) + + +def calculate_num_handles(service_config): + total = 1 + len(service_config[CONF_CHARACTERISTICS]) * 2 + total += sum( + len(char_conf[CONF_DESCRIPTORS]) + for char_conf in service_config[CONF_CHARACTERISTICS] + ) + return total + + +async def to_code_descriptor(descriptor_conf, char_var): + value = await parse_value(descriptor_conf[CONF_VALUE], {}) + desc_var = cg.new_Pvariable( + descriptor_conf[CONF_ID], + parse_uuid(descriptor_conf[CONF_UUID]), + descriptor_conf[CONF_MAX_LENGTH], + descriptor_conf[CONF_READ], + descriptor_conf[CONF_WRITE], + ) + cg.add(char_var.add_descriptor(desc_var)) + cg.add(desc_var.set_value(value)) + if CONF_ON_WRITE in descriptor_conf: + on_write_conf = descriptor_conf[CONF_ON_WRITE] + await automation.build_automation( + BLETriggers_ns.create_descriptor_on_write_trigger(desc_var), + [(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")], + on_write_conf, + ) + + +async def to_code_characteristic(service_var, char_conf): + char_var = cg.Pvariable( + char_conf[CONF_ID], + service_var.create_characteristic( + parse_uuid(char_conf[CONF_UUID]), + parse_properties(char_conf), + ), + ) + if CONF_ON_WRITE in char_conf: + on_write_conf = char_conf[CONF_ON_WRITE] + await automation.build_automation( + BLETriggers_ns.create_characteristic_on_write_trigger(char_var), + [(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")], + on_write_conf, + ) + if CONF_VALUE in char_conf: + action_conf = { + CONF_ID: char_conf[CONF_ID], + CONF_VALUE: char_conf[CONF_VALUE], + } + value_action = await ble_server_characteristic_set_value( + action_conf, + char_conf[CONF_CHAR_VALUE_ACTION_ID_], + cg.TemplateArguments(), + {}, + ) + cg.add(value_action.play()) + for descriptor_conf in char_conf[CONF_DESCRIPTORS]: + await to_code_descriptor(descriptor_conf, char_var) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) @@ -42,13 +531,94 @@ async def to_code(config): cg.add(parent.register_gatts_event_handler(var)) cg.add(parent.register_ble_status_event_handler(var)) cg.add(var.set_parent(parent)) - - cg.add(var.set_manufacturer(config[CONF_MANUFACTURER])) if CONF_MANUFACTURER_DATA in config: cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA])) - if CONF_MODEL in config: - cg.add(var.set_model(config[CONF_MODEL])) + for service_config in config[CONF_SERVICES]: + # Calculate the optimal number of handles based on the number of characteristics and descriptors + num_handles = calculate_num_handles(service_config) + service_var = cg.Pvariable( + service_config[CONF_ID], + var.create_service( + parse_uuid(service_config[CONF_UUID]), + service_config[CONF_ADVERTISE], + num_handles, + ), + ) + for char_conf in service_config[CONF_CHARACTERISTICS]: + await to_code_characteristic(service_var, char_conf) + if service_config[CONF_UUID] == DEVICE_INFORMATION_SERVICE_UUID: + cg.add(var.set_device_information_service(service_var)) + else: + cg.add(var.enqueue_start_service(service_var)) + if CONF_ON_CONNECT in config: + await automation.build_automation( + BLETriggers_ns.create_server_on_connect_trigger(var), + [(cg.uint16, "id")], + config[CONF_ON_CONNECT], + ) + if CONF_ON_DISCONNECT in config: + await automation.build_automation( + BLETriggers_ns.create_server_on_disconnect_trigger(var), + [(cg.uint16, "id")], + config[CONF_ON_DISCONNECT], + ) cg.add_define("USE_ESP32_BLE_SERVER") - if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True) + + +@automation.register_action( + "ble_server.characteristic.set_value", + BLECharacteristicSetValueAction, + cv.All( + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(BLECharacteristic), + cv.Required(CONF_VALUE): value_schema(), + } + ), + validate_set_value_action, + ), +) +async def ble_server_characteristic_set_value(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + value = await parse_value(config[CONF_VALUE], args) + cg.add(var.set_buffer(value)) + return var + + +@automation.register_action( + "ble_server.descriptor.set_value", + BLEDescriptorSetValueAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(BLEDescriptor), + cv.Required(CONF_VALUE): value_schema(), + } + ), +) +async def ble_server_descriptor_set_value(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + value = await parse_value(config[CONF_VALUE], args) + cg.add(var.set_buffer(value)) + return var + + +@automation.register_action( + "ble_server.characteristic.notify", + BLECharacteristicNotifyAction, + cv.All( + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(BLECharacteristic), + } + ), + validate_notify_action, + ), +) +async def ble_server_characteristic_notify(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + return var diff --git a/esphome/components/esp32_ble_server/ble_2901.cpp b/esphome/components/esp32_ble_server/ble_2901.cpp deleted file mode 100644 index ee0808d2c4..0000000000 --- a/esphome/components/esp32_ble_server/ble_2901.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "ble_2901.h" -#include "esphome/components/esp32_ble/ble_uuid.h" - -#ifdef USE_ESP32 - -namespace esphome { -namespace esp32_ble_server { - -BLE2901::BLE2901(const std::string &value) : BLE2901((uint8_t *) value.data(), value.length()) {} -BLE2901::BLE2901(const uint8_t *data, size_t length) : BLEDescriptor(esp32_ble::ESPBTUUID::from_uint16(0x2901)) { - this->set_value(data, length); - this->permissions_ = ESP_GATT_PERM_READ; -} - -} // namespace esp32_ble_server -} // namespace esphome - -#endif diff --git a/esphome/components/esp32_ble_server/ble_2901.h b/esphome/components/esp32_ble_server/ble_2901.h deleted file mode 100644 index 60f53e55b2..0000000000 --- a/esphome/components/esp32_ble_server/ble_2901.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "ble_descriptor.h" - -#ifdef USE_ESP32 - -namespace esphome { -namespace esp32_ble_server { - -class BLE2901 : public BLEDescriptor { - public: - BLE2901(const std::string &value); - BLE2901(const uint8_t *data, size_t length); -}; - -} // namespace esp32_ble_server -} // namespace esphome - -#endif diff --git a/esphome/components/esp32_ble_server/ble_characteristic.cpp b/esphome/components/esp32_ble_server/ble_characteristic.cpp index 6ff7d615f9..15739d60bb 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.cpp +++ b/esphome/components/esp32_ble_server/ble_characteristic.cpp @@ -32,70 +32,36 @@ BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties) this->set_write_no_response_property((properties & PROPERTY_WRITE_NR) != 0); } -void BLECharacteristic::set_value(std::vector value) { +void BLECharacteristic::set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); } + +void BLECharacteristic::set_value(const std::vector &buffer) { xSemaphoreTake(this->set_value_lock_, 0L); - this->value_ = std::move(value); + this->value_ = buffer; xSemaphoreGive(this->set_value_lock_); } -void BLECharacteristic::set_value(const std::string &value) { - this->set_value(std::vector(value.begin(), value.end())); -} -void BLECharacteristic::set_value(const uint8_t *data, size_t length) { - this->set_value(std::vector(data, data + length)); -} -void BLECharacteristic::set_value(uint8_t &data) { - uint8_t temp[1]; - temp[0] = data; - this->set_value(temp, 1); -} -void BLECharacteristic::set_value(uint16_t &data) { - uint8_t temp[2]; - temp[0] = data; - temp[1] = data >> 8; - this->set_value(temp, 2); -} -void BLECharacteristic::set_value(uint32_t &data) { - uint8_t temp[4]; - temp[0] = data; - temp[1] = data >> 8; - temp[2] = data >> 16; - temp[3] = data >> 24; - this->set_value(temp, 4); -} -void BLECharacteristic::set_value(int &data) { - uint8_t temp[4]; - temp[0] = data; - temp[1] = data >> 8; - temp[2] = data >> 16; - temp[3] = data >> 24; - this->set_value(temp, 4); -} -void BLECharacteristic::set_value(float &data) { - float temp = data; - this->set_value((uint8_t *) &temp, 4); -} -void BLECharacteristic::set_value(double &data) { - double temp = data; - this->set_value((uint8_t *) &temp, 8); -} -void BLECharacteristic::set_value(bool &data) { - uint8_t temp[1]; - temp[0] = data; - this->set_value(temp, 1); +void BLECharacteristic::set_value(const std::string &buffer) { + this->set_value(std::vector(buffer.begin(), buffer.end())); } -void BLECharacteristic::notify(bool notification) { - if (!notification) { - ESP_LOGW(TAG, "notification=false is not yet supported"); - // TODO: Handle when notification=false - } - if (this->service_->get_server()->get_connected_client_count() == 0) +void BLECharacteristic::notify() { + if (this->service_ == nullptr || this->service_->get_server() == nullptr || + this->service_->get_server()->get_connected_client_count() == 0) return; for (auto &client : this->service_->get_server()->get_clients()) { size_t length = this->value_.size(); - esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client.first, - this->handle_, length, this->value_.data(), false); + // If the client is not in the list of clients to notify, skip it + if (this->clients_to_notify_.count(client) == 0) + continue; + // If the client is in the list of clients to notify, check if it requires an ack (i.e. INDICATE) + bool require_ack = this->clients_to_notify_[client]; + // TODO: Remove this block when INDICATE acknowledgment is supported + if (require_ack) { + ESP_LOGW(TAG, "INDICATE acknowledgment is not yet supported (i.e. it works as a NOTIFY)"); + require_ack = false; + } + esp_err_t err = esp_ble_gatts_send_indicate(this->service_->get_server()->get_gatts_if(), client, this->handle_, + length, this->value_.data(), require_ack); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_send_indicate failed %d", err); return; @@ -103,7 +69,24 @@ void BLECharacteristic::notify(bool notification) { } } -void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { this->descriptors_.push_back(descriptor); } +void BLECharacteristic::add_descriptor(BLEDescriptor *descriptor) { + // If the descriptor is the CCCD descriptor, listen to its write event to know if the client wants to be notified + if (descriptor->get_uuid() == ESPBTUUID::from_uint16(ESP_GATT_UUID_CHAR_CLIENT_CONFIG)) { + descriptor->on(BLEDescriptorEvt::VectorEvt::ON_WRITE, [this](const std::vector &value, uint16_t conn_id) { + if (value.size() != 2) + return; + uint16_t cccd = encode_uint16(value[1], value[0]); + bool notify = (cccd & 1) != 0; + bool indicate = (cccd & 2) != 0; + if (notify || indicate) { + this->clients_to_notify_[conn_id] = indicate; + } else { + this->clients_to_notify_.erase(conn_id); + } + }); + } + this->descriptors_.push_back(descriptor); +} void BLECharacteristic::remove_descriptor(BLEDescriptor *descriptor) { this->descriptors_.erase(std::remove(this->descriptors_.begin(), this->descriptors_.end(), descriptor), @@ -223,6 +206,9 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt if (!param->read.need_rsp) break; // For some reason you can request a read but not want a response + this->EventEmitter::emit_(BLECharacteristicEvt::EmptyEvt::ON_READ, + param->read.conn_id); + uint16_t max_offset = 22; esp_gatt_rsp_t response; @@ -262,13 +248,13 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt } case ESP_GATTS_WRITE_EVT: { if (this->handle_ != param->write.handle) - return; + break; if (param->write.is_prep) { this->value_.insert(this->value_.end(), param->write.value, param->write.value + param->write.len); this->write_event_ = true; } else { - this->set_value(param->write.value, param->write.len); + this->set_value(ByteBuffer::wrap(param->write.value, param->write.len)); } if (param->write.need_rsp) { @@ -289,7 +275,8 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt } if (!param->write.is_prep) { - this->on_write_(this->value_); + this->EventEmitter, uint16_t>::emit_( + BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->write.conn_id); } break; @@ -300,7 +287,8 @@ void BLECharacteristic::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt break; this->write_event_ = false; if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC) { - this->on_write_(this->value_); + this->EventEmitter, uint16_t>::emit_( + BLECharacteristicEvt::VectorEvt::ON_WRITE, this->value_, param->exec_write.conn_id); } esp_err_t err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, nullptr); diff --git a/esphome/components/esp32_ble_server/ble_characteristic.h b/esphome/components/esp32_ble_server/ble_characteristic.h index 8837c796a5..3698b8c4aa 100644 --- a/esphome/components/esp32_ble_server/ble_characteristic.h +++ b/esphome/components/esp32_ble_server/ble_characteristic.h @@ -2,8 +2,11 @@ #include "ble_descriptor.h" #include "esphome/components/esp32_ble/ble_uuid.h" +#include "esphome/components/event_emitter/event_emitter.h" +#include "esphome/components/bytebuffer/bytebuffer.h" #include +#include #ifdef USE_ESP32 @@ -19,24 +22,30 @@ namespace esphome { namespace esp32_ble_server { using namespace esp32_ble; +using namespace bytebuffer; +using namespace event_emitter; class BLEService; -class BLECharacteristic { +namespace BLECharacteristicEvt { +enum VectorEvt { + ON_WRITE, +}; + +enum EmptyEvt { + ON_READ, +}; +} // namespace BLECharacteristicEvt + +class BLECharacteristic : public EventEmitter, uint16_t>, + public EventEmitter { public: BLECharacteristic(ESPBTUUID uuid, uint32_t properties); ~BLECharacteristic(); - void set_value(const uint8_t *data, size_t length); - void set_value(std::vector value); - void set_value(const std::string &value); - void set_value(uint8_t &data); - void set_value(uint16_t &data); - void set_value(uint32_t &data); - void set_value(int &data); - void set_value(float &data); - void set_value(double &data); - void set_value(bool &data); + void set_value(ByteBuffer buffer); + void set_value(const std::vector &buffer); + void set_value(const std::string &buffer); void set_broadcast_property(bool value); void set_indicate_property(bool value); @@ -45,13 +54,12 @@ class BLECharacteristic { void set_write_property(bool value); void set_write_no_response_property(bool value); - void notify(bool notification = true); + void notify(); void do_create(BLEService *service); + void do_delete() { this->clients_to_notify_.clear(); } void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); - void on_write(const std::function &)> &&func) { this->on_write_ = func; } - void add_descriptor(BLEDescriptor *descriptor); void remove_descriptor(BLEDescriptor *descriptor); @@ -71,7 +79,7 @@ class BLECharacteristic { protected: bool write_event_{false}; - BLEService *service_; + BLEService *service_{}; ESPBTUUID uuid_; esp_gatt_char_prop_t properties_; uint16_t handle_{0xFFFF}; @@ -81,8 +89,7 @@ class BLECharacteristic { SemaphoreHandle_t set_value_lock_; std::vector descriptors_; - - std::function &)> on_write_; + std::unordered_map clients_to_notify_; esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; diff --git a/esphome/components/esp32_ble_server/ble_descriptor.cpp b/esphome/components/esp32_ble_server/ble_descriptor.cpp index bfb6224335..afbe579513 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.cpp +++ b/esphome/components/esp32_ble_server/ble_descriptor.cpp @@ -12,11 +12,19 @@ namespace esp32_ble_server { static const char *const TAG = "esp32_ble_server.descriptor"; -BLEDescriptor::BLEDescriptor(ESPBTUUID uuid, uint16_t max_len) { +static RAMAllocator descriptor_allocator{}; // NOLINT + +BLEDescriptor::BLEDescriptor(ESPBTUUID uuid, uint16_t max_len, bool read, bool write) { this->uuid_ = uuid; this->value_.attr_len = 0; this->value_.attr_max_len = max_len; - this->value_.attr_value = (uint8_t *) malloc(max_len); // NOLINT + this->value_.attr_value = descriptor_allocator.allocate(max_len); + if (read) { + this->permissions_ |= ESP_GATT_PERM_READ; + } + if (write) { + this->permissions_ |= ESP_GATT_PERM_WRITE; + } } BLEDescriptor::~BLEDescriptor() { free(this->value_.attr_value); } // NOLINT @@ -38,14 +46,15 @@ void BLEDescriptor::do_create(BLECharacteristic *characteristic) { this->state_ = CREATING; } -void BLEDescriptor::set_value(const std::string &value) { this->set_value((uint8_t *) value.data(), value.length()); } -void BLEDescriptor::set_value(const uint8_t *data, size_t length) { +void BLEDescriptor::set_value(std::vector buffer) { + size_t length = buffer.size(); + if (length > this->value_.attr_max_len) { ESP_LOGE(TAG, "Size %d too large, must be no bigger than %d", length, this->value_.attr_max_len); return; } this->value_.attr_len = length; - memcpy(this->value_.attr_value, data, length); + memcpy(this->value_.attr_value, buffer.data(), length); } void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, @@ -61,10 +70,13 @@ void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ break; } case ESP_GATTS_WRITE_EVT: { - if (this->handle_ == param->write.handle) { - this->value_.attr_len = param->write.len; - memcpy(this->value_.attr_value, param->write.value, param->write.len); - } + if (this->handle_ != param->write.handle) + break; + this->value_.attr_len = param->write.len; + memcpy(this->value_.attr_value, param->write.value, param->write.len); + this->emit_(BLEDescriptorEvt::VectorEvt::ON_WRITE, + std::vector(param->write.value, param->write.value + param->write.len), + param->write.conn_id); break; } default: diff --git a/esphome/components/esp32_ble_server/ble_descriptor.h b/esphome/components/esp32_ble_server/ble_descriptor.h index 4b8fb345c3..8d3c22c5a1 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.h +++ b/esphome/components/esp32_ble_server/ble_descriptor.h @@ -1,6 +1,8 @@ #pragma once #include "esphome/components/esp32_ble/ble_uuid.h" +#include "esphome/components/event_emitter/event_emitter.h" +#include "esphome/components/bytebuffer/bytebuffer.h" #ifdef USE_ESP32 @@ -11,17 +13,26 @@ namespace esphome { namespace esp32_ble_server { using namespace esp32_ble; +using namespace bytebuffer; +using namespace event_emitter; class BLECharacteristic; -class BLEDescriptor { +namespace BLEDescriptorEvt { +enum VectorEvt { + ON_WRITE, +}; +} // namespace BLEDescriptorEvt + +class BLEDescriptor : public EventEmitter, uint16_t> { public: - BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100); + BLEDescriptor(ESPBTUUID uuid, uint16_t max_len = 100, bool read = true, bool write = true); virtual ~BLEDescriptor(); void do_create(BLECharacteristic *characteristic); + ESPBTUUID get_uuid() const { return this->uuid_; } - void set_value(const std::string &value); - void set_value(const uint8_t *data, size_t length); + void set_value(std::vector buffer); + void set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); } void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); @@ -33,9 +44,9 @@ class BLEDescriptor { ESPBTUUID uuid_; uint16_t handle_{0xFFFF}; - esp_attr_value_t value_; + esp_attr_value_t value_{}; - esp_gatt_perm_t permissions_ = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + esp_gatt_perm_t permissions_{}; enum State : uint8_t { FAILED = 0x00, diff --git a/esphome/components/esp32_ble_server/ble_server.cpp b/esphome/components/esp32_ble_server/ble_server.cpp index 338413f64e..5339bf8aed 100644 --- a/esphome/components/esp32_ble_server/ble_server.cpp +++ b/esphome/components/esp32_ble_server/ble_server.cpp @@ -19,11 +19,6 @@ namespace esp32_ble_server { static const char *const TAG = "esp32_ble_server"; -static const uint16_t DEVICE_INFORMATION_SERVICE_UUID = 0x180A; -static const uint16_t MODEL_UUID = 0x2A24; -static const uint16_t VERSION_UUID = 0x2A26; -static const uint16_t MANUFACTURER_UUID = 0x2A29; - void BLEServer::setup() { if (this->parent_->is_failed()) { this->mark_failed(); @@ -38,9 +33,27 @@ void BLEServer::loop() { return; } switch (this->state_) { - case RUNNING: - return; - + case RUNNING: { + // Start all services that are pending to start + if (!this->services_to_start_.empty()) { + uint16_t index_to_remove = 0; + // Iterate over the services to start + for (unsigned i = 0; i < this->services_to_start_.size(); i++) { + BLEService *service = this->services_to_start_[i]; + if (service->is_created()) { + service->start(); // Needs to be called once per characteristic in the service + } else { + index_to_remove = i + 1; + } + } + // Remove the services that have been started + if (index_to_remove > 0) { + this->services_to_start_.erase(this->services_to_start_.begin(), + this->services_to_start_.begin() + index_to_remove - 1); + } + } + break; + } case INIT: { esp_err_t err = esp_ble_gatts_app_register(0); if (err != ESP_OK) { @@ -53,29 +66,26 @@ void BLEServer::loop() { } case REGISTERING: { if (this->registered_) { + // Create the device information service first so + // it is at the top of the GATT table + this->device_information_service_->do_create(this); // Create all services previously created for (auto &pair : this->services_) { + if (pair.second == this->device_information_service_) { + continue; + } pair.second->do_create(this); } - if (this->device_information_service_ == nullptr) { - this->create_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID)); - this->device_information_service_ = - this->get_service(ESPBTUUID::from_uint16(DEVICE_INFORMATION_SERVICE_UUID)); - this->create_device_characteristics_(); - } this->state_ = STARTING_SERVICE; } break; } case STARTING_SERVICE: { - if (!this->device_information_service_->is_created()) { - break; - } if (this->device_information_service_->is_running()) { this->state_ = RUNNING; this->restart_advertising_(); ESP_LOGD(TAG, "BLE server setup successfully"); - } else if (!this->device_information_service_->is_starting()) { + } else if (this->device_information_service_->is_created()) { this->device_information_service_->start(); } break; @@ -93,81 +103,66 @@ void BLEServer::restart_advertising_() { } } -bool BLEServer::create_device_characteristics_() { - if (this->model_.has_value()) { - BLECharacteristic *model = - this->device_information_service_->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ); - model->set_value(this->model_.value()); - } else { - BLECharacteristic *model = - this->device_information_service_->create_characteristic(MODEL_UUID, BLECharacteristic::PROPERTY_READ); - model->set_value(ESPHOME_BOARD); - } - - BLECharacteristic *version = - this->device_information_service_->create_characteristic(VERSION_UUID, BLECharacteristic::PROPERTY_READ); - version->set_value("ESPHome " ESPHOME_VERSION); - - BLECharacteristic *manufacturer = - this->device_information_service_->create_characteristic(MANUFACTURER_UUID, BLECharacteristic::PROPERTY_READ); - manufacturer->set_value(this->manufacturer_); - - return true; -} - -void BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles, uint8_t inst_id) { +BLEService *BLEServer::create_service(ESPBTUUID uuid, bool advertise, uint16_t num_handles) { ESP_LOGV(TAG, "Creating BLE service - %s", uuid.to_string().c_str()); - // If the service already exists, do nothing - BLEService *service = this->get_service(uuid); - if (service != nullptr) { - ESP_LOGW(TAG, "BLE service %s already exists", uuid.to_string().c_str()); - return; + // Calculate the inst_id for the service + uint8_t inst_id = 0; + for (; inst_id < 0xFF; inst_id++) { + if (this->get_service(uuid, inst_id) == nullptr) { + break; + } } - service = new BLEService(uuid, num_handles, inst_id, advertise); // NOLINT(cppcoreguidelines-owning-memory) - this->services_.emplace(uuid.to_string(), service); - service->do_create(this); + if (inst_id == 0xFF) { + ESP_LOGW(TAG, "Could not create BLE service %s, too many instances", uuid.to_string().c_str()); + return nullptr; + } + BLEService *service = // NOLINT(cppcoreguidelines-owning-memory) + new BLEService(uuid, num_handles, inst_id, advertise); + this->services_.emplace(BLEServer::get_service_key(uuid, inst_id), service); + if (this->parent_->is_active() && this->registered_) { + service->do_create(this); + } + return service; } -void BLEServer::remove_service(ESPBTUUID uuid) { - ESP_LOGV(TAG, "Removing BLE service - %s", uuid.to_string().c_str()); - BLEService *service = this->get_service(uuid); +void BLEServer::remove_service(ESPBTUUID uuid, uint8_t inst_id) { + ESP_LOGV(TAG, "Removing BLE service - %s %d", uuid.to_string().c_str(), inst_id); + BLEService *service = this->get_service(uuid, inst_id); if (service == nullptr) { - ESP_LOGW(TAG, "BLE service %s not found", uuid.to_string().c_str()); + ESP_LOGW(TAG, "BLE service %s %d does not exist", uuid.to_string().c_str(), inst_id); return; } service->do_delete(); delete service; // NOLINT(cppcoreguidelines-owning-memory) - this->services_.erase(uuid.to_string()); + this->services_.erase(BLEServer::get_service_key(uuid, inst_id)); } -BLEService *BLEServer::get_service(ESPBTUUID uuid) { +BLEService *BLEServer::get_service(ESPBTUUID uuid, uint8_t inst_id) { BLEService *service = nullptr; - if (this->services_.count(uuid.to_string()) > 0) { - service = this->services_.at(uuid.to_string()); + if (this->services_.count(BLEServer::get_service_key(uuid, inst_id)) > 0) { + service = this->services_.at(BLEServer::get_service_key(uuid, inst_id)); } return service; } +std::string BLEServer::get_service_key(ESPBTUUID uuid, uint8_t inst_id) { + return uuid.to_string() + std::to_string(inst_id); +} + void BLEServer::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) { switch (event) { case ESP_GATTS_CONNECT_EVT: { ESP_LOGD(TAG, "BLE Client connected"); - this->add_client_(param->connect.conn_id, (void *) this); - this->connected_clients_++; - for (auto *component : this->service_components_) { - component->on_client_connect(); - } + this->add_client_(param->connect.conn_id); + this->emit_(BLEServerEvt::EmptyEvt::ON_CONNECT, param->connect.conn_id); break; } case ESP_GATTS_DISCONNECT_EVT: { ESP_LOGD(TAG, "BLE Client disconnected"); - if (this->remove_client_(param->disconnect.conn_id)) - this->connected_clients_--; + this->remove_client_(param->disconnect.conn_id); this->parent_->advertising_start(); - for (auto *component : this->service_components_) { - component->on_client_disconnect(); - } + this->emit_(BLEServerEvt::EmptyEvt::ON_DISCONNECT, param->disconnect.conn_id); break; } case ESP_GATTS_REG_EVT: { diff --git a/esphome/components/esp32_ble_server/ble_server.h b/esphome/components/esp32_ble_server/ble_server.h index e379e67296..43599438f3 100644 --- a/esphome/components/esp32_ble_server/ble_server.h +++ b/esphome/components/esp32_ble_server/ble_server.h @@ -4,36 +4,38 @@ #include "ble_characteristic.h" #include "esphome/components/esp32_ble/ble.h" -#include "esphome/components/esp32_ble/ble_advertising.h" #include "esphome/components/esp32_ble/ble_uuid.h" -#include "esphome/components/esp32_ble/queue.h" +#include "esphome/components/bytebuffer/bytebuffer.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/preferences.h" #include #include #include +#include #ifdef USE_ESP32 -#include #include namespace esphome { namespace esp32_ble_server { using namespace esp32_ble; +using namespace bytebuffer; -class BLEServiceComponent { - public: - virtual void on_client_connect(){}; - virtual void on_client_disconnect(){}; - virtual void start(); - virtual void stop(); +namespace BLEServerEvt { +enum EmptyEvt { + ON_CONNECT, + ON_DISCONNECT, }; +} // namespace BLEServerEvt -class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEventHandler, public Parented { +class BLEServer : public Component, + public GATTsEventHandler, + public BLEStatusEventHandler, + public Parented, + public EventEmitter { public: void setup() override; void loop() override; @@ -44,47 +46,41 @@ class BLEServer : public Component, public GATTsEventHandler, public BLEStatusEv void teardown(); bool is_running(); - void set_manufacturer(const std::string &manufacturer) { this->manufacturer_ = manufacturer; } - void set_model(const std::string &model) { this->model_ = model; } void set_manufacturer_data(const std::vector &data) { this->manufacturer_data_ = data; this->restart_advertising_(); } - void create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15, uint8_t inst_id = 0); - void remove_service(ESPBTUUID uuid); - BLEService *get_service(ESPBTUUID uuid); + BLEService *create_service(ESPBTUUID uuid, bool advertise = false, uint16_t num_handles = 15); + void remove_service(ESPBTUUID uuid, uint8_t inst_id = 0); + BLEService *get_service(ESPBTUUID uuid, uint8_t inst_id = 0); + void enqueue_start_service(BLEService *service) { this->services_to_start_.push_back(service); } + void set_device_information_service(BLEService *service) { this->device_information_service_ = service; } esp_gatt_if_t get_gatts_if() { return this->gatts_if_; } - uint32_t get_connected_client_count() { return this->connected_clients_; } - const std::unordered_map &get_clients() { return this->clients_; } + uint32_t get_connected_client_count() { return this->clients_.size(); } + const std::unordered_set &get_clients() { return this->clients_; } void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) override; void ble_before_disabled_event_handler() override; - void register_service_component(BLEServiceComponent *component) { this->service_components_.push_back(component); } - protected: - bool create_device_characteristics_(); + static std::string get_service_key(ESPBTUUID uuid, uint8_t inst_id); void restart_advertising_(); - void add_client_(uint16_t conn_id, void *client) { this->clients_.emplace(conn_id, client); } - bool remove_client_(uint16_t conn_id) { return this->clients_.erase(conn_id) > 0; } + void add_client_(uint16_t conn_id) { this->clients_.insert(conn_id); } + void remove_client_(uint16_t conn_id) { this->clients_.erase(conn_id); } - std::string manufacturer_; - optional model_; - std::vector manufacturer_data_; + std::vector manufacturer_data_{}; esp_gatt_if_t gatts_if_{0}; bool registered_{false}; - uint32_t connected_clients_{0}; - std::unordered_map clients_; - std::unordered_map services_; - BLEService *device_information_service_; - - std::vector service_components_; + std::unordered_set clients_; + std::unordered_map services_{}; + std::vector services_to_start_{}; + BLEService *device_information_service_{}; enum State : uint8_t { INIT = 0x00, diff --git a/esphome/components/esp32_ble_server/ble_server_automations.cpp b/esphome/components/esp32_ble_server/ble_server_automations.cpp new file mode 100644 index 0000000000..41ef2b8bfe --- /dev/null +++ b/esphome/components/esp32_ble_server/ble_server_automations.cpp @@ -0,0 +1,77 @@ +#include "ble_server_automations.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace esp32_ble_server { +// Interface to interact with ESPHome automations and triggers +namespace esp32_ble_server_automations { + +using namespace esp32_ble; + +Trigger, uint16_t> *BLETriggers::create_characteristic_on_write_trigger( + BLECharacteristic *characteristic) { + Trigger, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory) + new Trigger, uint16_t>(); + characteristic->EventEmitter, uint16_t>::on( + BLECharacteristicEvt::VectorEvt::ON_WRITE, + [on_write_trigger](const std::vector &data, uint16_t id) { on_write_trigger->trigger(data, id); }); + return on_write_trigger; +} + +Trigger, uint16_t> *BLETriggers::create_descriptor_on_write_trigger(BLEDescriptor *descriptor) { + Trigger, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory) + new Trigger, uint16_t>(); + descriptor->on( + BLEDescriptorEvt::VectorEvt::ON_WRITE, + [on_write_trigger](const std::vector &data, uint16_t id) { on_write_trigger->trigger(data, id); }); + return on_write_trigger; +} + +Trigger *BLETriggers::create_server_on_connect_trigger(BLEServer *server) { + Trigger *on_connect_trigger = new Trigger(); // NOLINT(cppcoreguidelines-owning-memory) + server->on(BLEServerEvt::EmptyEvt::ON_CONNECT, + [on_connect_trigger](uint16_t conn_id) { on_connect_trigger->trigger(conn_id); }); + return on_connect_trigger; +} + +Trigger *BLETriggers::create_server_on_disconnect_trigger(BLEServer *server) { + Trigger *on_disconnect_trigger = new Trigger(); // NOLINT(cppcoreguidelines-owning-memory) + server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT, + [on_disconnect_trigger](uint16_t conn_id) { on_disconnect_trigger->trigger(conn_id); }); + return on_disconnect_trigger; +} + +void BLECharacteristicSetValueActionManager::set_listener(BLECharacteristic *characteristic, + EventEmitterListenerID listener_id, + const std::function &pre_notify_listener) { + // Check if there is already a listener for this characteristic + if (this->listeners_.count(characteristic) > 0) { + // Unpack the pair listener_id, pre_notify_listener_id + auto listener_pairs = this->listeners_[characteristic]; + EventEmitterListenerID old_listener_id = listener_pairs.first; + EventEmitterListenerID old_pre_notify_listener_id = listener_pairs.second; + // Remove the previous listener + characteristic->EventEmitter::off(BLECharacteristicEvt::EmptyEvt::ON_READ, + old_listener_id); + // Remove the pre-notify listener + this->off(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, old_pre_notify_listener_id); + } + // Create a new listener for the pre-notify event + EventEmitterListenerID pre_notify_listener_id = + this->on(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, + [pre_notify_listener, characteristic](const BLECharacteristic *evt_characteristic) { + // Only call the pre-notify listener if the characteristic is the one we are interested in + if (characteristic == evt_characteristic) { + pre_notify_listener(); + } + }); + // Save the pair listener_id, pre_notify_listener_id to the map + this->listeners_[characteristic] = std::make_pair(listener_id, pre_notify_listener_id); +} + +} // namespace esp32_ble_server_automations +} // namespace esp32_ble_server +} // namespace esphome + +#endif diff --git a/esphome/components/esp32_ble_server/ble_server_automations.h b/esphome/components/esp32_ble_server/ble_server_automations.h new file mode 100644 index 0000000000..eab6b05f05 --- /dev/null +++ b/esphome/components/esp32_ble_server/ble_server_automations.h @@ -0,0 +1,115 @@ +#pragma once + +#include "ble_server.h" +#include "ble_characteristic.h" +#include "ble_descriptor.h" + +#include "esphome/components/event_emitter/event_emitter.h" +#include "esphome/core/automation.h" + +#include +#include +#include + +#ifdef USE_ESP32 + +namespace esphome { +namespace esp32_ble_server { +// Interface to interact with ESPHome actions and triggers +namespace esp32_ble_server_automations { + +using namespace esp32_ble; +using namespace event_emitter; + +class BLETriggers { + public: + static Trigger, uint16_t> *create_characteristic_on_write_trigger( + BLECharacteristic *characteristic); + static Trigger, uint16_t> *create_descriptor_on_write_trigger(BLEDescriptor *descriptor); + static Trigger *create_server_on_connect_trigger(BLEServer *server); + static Trigger *create_server_on_disconnect_trigger(BLEServer *server); +}; + +enum BLECharacteristicSetValueActionEvt { + PRE_NOTIFY, +}; + +// Class to make sure only one BLECharacteristicSetValueAction is active at a time for each characteristic +class BLECharacteristicSetValueActionManager + : public EventEmitter { + public: + // Singleton pattern + static BLECharacteristicSetValueActionManager *get_instance() { + static BLECharacteristicSetValueActionManager instance; + return &instance; + } + void set_listener(BLECharacteristic *characteristic, EventEmitterListenerID listener_id, + const std::function &pre_notify_listener); + EventEmitterListenerID get_listener(BLECharacteristic *characteristic) { + return this->listeners_[characteristic].first; + } + void emit_pre_notify(BLECharacteristic *characteristic) { + this->emit_(BLECharacteristicSetValueActionEvt::PRE_NOTIFY, characteristic); + } + + private: + std::unordered_map> listeners_; +}; + +template class BLECharacteristicSetValueAction : public Action { + public: + BLECharacteristicSetValueAction(BLECharacteristic *characteristic) : parent_(characteristic) {} + TEMPLATABLE_VALUE(std::vector, buffer) + void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); } + void play(Ts... x) override { + // If the listener is already set, do nothing + if (BLECharacteristicSetValueActionManager::get_instance()->get_listener(this->parent_) == this->listener_id_) + return; + // Set initial value + this->parent_->set_value(this->buffer_.value(x...)); + // Set the listener for read events + this->listener_id_ = this->parent_->EventEmitter::on( + BLECharacteristicEvt::EmptyEvt::ON_READ, [this, x...](uint16_t id) { + // Set the value of the characteristic every time it is read + this->parent_->set_value(this->buffer_.value(x...)); + }); + // Set the listener in the global manager so only one BLECharacteristicSetValueAction is set for each characteristic + BLECharacteristicSetValueActionManager::get_instance()->set_listener( + this->parent_, this->listener_id_, [this, x...]() { this->parent_->set_value(this->buffer_.value(x...)); }); + } + + protected: + BLECharacteristic *parent_; + EventEmitterListenerID listener_id_; +}; + +template class BLECharacteristicNotifyAction : public Action { + public: + BLECharacteristicNotifyAction(BLECharacteristic *characteristic) : parent_(characteristic) {} + void play(Ts... x) override { + // Call the pre-notify event + BLECharacteristicSetValueActionManager::get_instance()->emit_pre_notify(this->parent_); + // Notify the characteristic + this->parent_->notify(); + } + + protected: + BLECharacteristic *parent_; +}; + +template class BLEDescriptorSetValueAction : public Action { + public: + BLEDescriptorSetValueAction(BLEDescriptor *descriptor) : parent_(descriptor) {} + TEMPLATABLE_VALUE(std::vector, buffer) + void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); } + void play(Ts... x) override { this->parent_->set_value(this->buffer_.value(x...)); } + + protected: + BLEDescriptor *parent_; +}; + +} // namespace esp32_ble_server_automations +} // namespace esp32_ble_server +} // namespace esphome + +#endif diff --git a/esphome/components/esp32_ble_server/ble_service.cpp b/esphome/components/esp32_ble_server/ble_service.cpp index 368f03fb52..96fedf2346 100644 --- a/esphome/components/esp32_ble_server/ble_service.cpp +++ b/esphome/components/esp32_ble_server/ble_service.cpp @@ -52,18 +52,21 @@ void BLEService::do_create(BLEServer *server) { esp_err_t err = esp_ble_gatts_create_service(server->get_gatts_if(), &srvc_id, this->num_handles_); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_create_service failed: %d", err); - this->init_state_ = FAILED; + this->state_ = FAILED; return; } - this->init_state_ = CREATING; + this->state_ = CREATING; } void BLEService::do_delete() { - if (this->init_state_ == DELETING || this->init_state_ == DELETED) + if (this->state_ == DELETING || this->state_ == DELETED) return; - this->init_state_ = DELETING; + this->state_ = DELETING; this->created_characteristic_count_ = 0; this->last_created_characteristic_ = nullptr; + // Call all characteristics to delete + for (auto *characteristic : this->characteristics_) + characteristic->do_delete(); this->stop_(); esp_err_t err = esp_ble_gatts_delete_service(this->handle_); if (err != ESP_OK) { @@ -91,6 +94,7 @@ void BLEService::start() { return; should_start_ = true; + this->state_ = STARTING; esp_err_t err = esp_ble_gatts_start_service(this->handle_); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_start_service failed: %d", err); @@ -98,7 +102,6 @@ void BLEService::start() { } if (this->advertise_) esp32_ble::global_ble->advertising_add_service_uuid(this->uuid_); - this->running_state_ = STARTING; } void BLEService::stop() { @@ -107,9 +110,9 @@ void BLEService::stop() { } void BLEService::stop_() { - if (this->running_state_ == STOPPING || this->running_state_ == STOPPED) + if (this->state_ == STOPPING || this->state_ == STOPPED) return; - this->running_state_ = STOPPING; + this->state_ = STOPPING; esp_err_t err = esp_ble_gatts_stop_service(this->handle_); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_ble_gatts_stop_service failed: %d", err); @@ -119,17 +122,16 @@ void BLEService::stop_() { esp32_ble::global_ble->advertising_remove_service_uuid(this->uuid_); } -bool BLEService::is_created() { return this->init_state_ == CREATED; } bool BLEService::is_failed() { - if (this->init_state_ == FAILED) + if (this->state_ == FAILED) return true; bool failed = false; for (auto *characteristic : this->characteristics_) failed |= characteristic->is_failed(); if (failed) - this->init_state_ = FAILED; - return this->init_state_ == FAILED; + this->state_ = FAILED; + return this->state_ == FAILED; } void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, @@ -139,7 +141,7 @@ void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t g if (this->uuid_ == ESPBTUUID::from_uuid(param->create.service_id.id.uuid) && this->inst_id_ == param->create.service_id.id.inst_id) { this->handle_ = param->create.service_handle; - this->init_state_ = CREATED; + this->state_ = CREATED; if (this->should_start_) this->start(); } @@ -147,18 +149,18 @@ void BLEService::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t g } case ESP_GATTS_DELETE_EVT: if (param->del.service_handle == this->handle_) { - this->init_state_ = DELETED; + this->state_ = DELETED; } break; case ESP_GATTS_START_EVT: { if (param->start.service_handle == this->handle_) { - this->running_state_ = RUNNING; + this->state_ = RUNNING; } break; } case ESP_GATTS_STOP_EVT: { if (param->start.service_handle == this->handle_) { - this->running_state_ = STOPPED; + this->state_ = STOPPED; } break; } diff --git a/esphome/components/esp32_ble_server/ble_service.h b/esphome/components/esp32_ble_server/ble_service.h index 5e5883b6bf..dcfad5f501 100644 --- a/esphome/components/esp32_ble_server/ble_service.h +++ b/esphome/components/esp32_ble_server/ble_service.h @@ -32,6 +32,7 @@ class BLEService { BLECharacteristic *create_characteristic(ESPBTUUID uuid, esp_gatt_char_prop_t properties); ESPBTUUID get_uuid() { return this->uuid_; } + uint8_t get_inst_id() { return this->inst_id_; } BLECharacteristic *get_last_created_characteristic() { return this->last_created_characteristic_; } uint16_t get_handle() { return this->handle_; } @@ -44,18 +45,17 @@ class BLEService { void start(); void stop(); - bool is_created(); bool is_failed(); - - bool is_running() { return this->running_state_ == RUNNING; } - bool is_starting() { return this->running_state_ == STARTING; } - bool is_deleted() { return this->init_state_ == DELETED; } + bool is_created() { return this->state_ == CREATED; } + bool is_running() { return this->state_ == RUNNING; } + bool is_starting() { return this->state_ == STARTING; } + bool is_deleted() { return this->state_ == DELETED; } protected: std::vector characteristics_; BLECharacteristic *last_created_characteristic_{nullptr}; uint32_t created_characteristic_count_{0}; - BLEServer *server_; + BLEServer *server_ = nullptr; ESPBTUUID uuid_; uint16_t num_handles_; uint16_t handle_{0xFFFF}; @@ -66,22 +66,18 @@ class BLEService { bool do_create_characteristics_(); void stop_(); - enum InitState : uint8_t { + enum State : uint8_t { FAILED = 0x00, INIT, CREATING, - CREATING_DEPENDENTS, CREATED, - DELETING, - DELETED, - } init_state_{INIT}; - - enum RunningState : uint8_t { STARTING, RUNNING, STOPPING, STOPPED, - } running_state_{STOPPED}; + DELETING, + DELETED, + } state_{INIT}; }; } // namespace esp32_ble_server diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 0aa8eadd0a..e762c89b94 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -1,9 +1,13 @@ -import re - 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, + bt_uuid16_format, + bt_uuid32_format, + bt_uuid128_format, +) import esphome.config_validation as cv from esphome.const import ( CONF_ACTIVE, @@ -86,43 +90,6 @@ def validate_scan_parameters(config): return config -bt_uuid16_format = "XXXX" -bt_uuid32_format = "XXXXXXXX" -bt_uuid128_format = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - - -def bt_uuid(value): - in_value = cv.string_strict(value) - value = in_value.upper() - - if len(value) == len(bt_uuid16_format): - pattern = re.compile("^[A-F|0-9]{4,}$") - if not pattern.match(value): - raise cv.Invalid( - f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'" - ) - return value - if len(value) == len(bt_uuid32_format): - pattern = re.compile("^[A-F|0-9]{8,}$") - if not pattern.match(value): - raise cv.Invalid( - f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'" - ) - return value - if len(value) == len(bt_uuid128_format): - pattern = re.compile( - "^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$" - ) - if not pattern.match(value): - raise cv.Invalid( - f"Invalid hexadecimal value for 128 UUID format: '{in_value}'" - ) - return value - raise cv.Invalid( - f"Service UUID must be in 16 bit '{bt_uuid16_format}', 32 bit '{bt_uuid32_format}', or 128 bit '{bt_uuid128_format}' format" - ) - - def as_hex(value): return cg.RawExpression(f"0x{value}ULL") diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index ecc07d4c91..ca39c1cd36 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -1,6 +1,6 @@ from esphome import automation import esphome.codegen as cg -from esphome.components import binary_sensor, esp32_ble_server, output +from esphome.components import binary_sensor, output import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID @@ -24,9 +24,7 @@ Error = improv_ns.enum("Error") State = improv_ns.enum("State") esp32_improv_ns = cg.esphome_ns.namespace("esp32_improv") -ESP32ImprovComponent = esp32_improv_ns.class_( - "ESP32ImprovComponent", cg.Component, esp32_ble_server.BLEServiceComponent -) +ESP32ImprovComponent = esp32_improv_ns.class_("ESP32ImprovComponent", cg.Component) ESP32ImprovProvisionedTrigger = esp32_improv_ns.class_( "ESP32ImprovProvisionedTrigger", automation.Trigger.template() ) @@ -47,7 +45,6 @@ ESP32ImprovStoppedTrigger = esp32_improv_ns.class_( CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(ESP32ImprovComponent), - cv.GenerateID(CONF_BLE_SERVER_ID): cv.use_id(esp32_ble_server.BLEServer), cv.Required(CONF_AUTHORIZER): cv.Any( cv.none, cv.use_id(binary_sensor.BinarySensor) ), @@ -100,9 +97,6 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) - ble_server = await cg.get_variable(config[CONF_BLE_SERVER_ID]) - cg.add(ble_server.register_service_component(var)) - cg.add_define("USE_IMPROV") cg.add_library("improv/Improv", "1.2.4") diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index c67431077c..b720425506 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -4,12 +4,15 @@ #include "esphome/components/esp32_ble_server/ble_2902.h" #include "esphome/core/application.h" #include "esphome/core/log.h" +#include "esphome/components/bytebuffer/bytebuffer.h" #ifdef USE_ESP32 namespace esphome { namespace esp32_improv { +using namespace bytebuffer; + static const char *const TAG = "esp32_improv.component"; static const char *const ESPHOME_MY_LINK = "https://my.home-assistant.io/redirect/config_flow_start?domain=esphome"; @@ -26,6 +29,8 @@ void ESP32ImprovComponent::setup() { }); } #endif + global_ble_server->on(BLEServerEvt::EmptyEvt::ON_DISCONNECT, + [this](uint16_t conn_id) { this->set_error_(improv::ERROR_NONE); }); } void ESP32ImprovComponent::setup_characteristics() { @@ -40,11 +45,12 @@ void ESP32ImprovComponent::setup_characteristics() { this->error_->add_descriptor(error_descriptor); this->rpc_ = this->service_->create_characteristic(improv::RPC_COMMAND_UUID, BLECharacteristic::PROPERTY_WRITE); - this->rpc_->on_write([this](const std::vector &data) { - if (!data.empty()) { - this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end()); - } - }); + this->rpc_->EventEmitter, uint16_t>::on( + BLECharacteristicEvt::VectorEvt::ON_WRITE, [this](const std::vector &data, uint16_t id) { + if (!data.empty()) { + this->incoming_data_.insert(this->incoming_data_.end(), data.begin(), data.end()); + } + }); BLEDescriptor *rpc_descriptor = new BLE2902(); this->rpc_->add_descriptor(rpc_descriptor); @@ -62,7 +68,7 @@ void ESP32ImprovComponent::setup_characteristics() { if (this->status_indicator_ != nullptr) capabilities |= improv::CAPABILITY_IDENTIFY; #endif - this->capabilities_->set_value(capabilities); + this->capabilities_->set_value(ByteBuffer::wrap(capabilities)); this->setup_complete_ = true; } @@ -80,8 +86,7 @@ void ESP32ImprovComponent::loop() { if (this->service_ == nullptr) { // Setup the service ESP_LOGD(TAG, "Creating Improv service"); - global_ble_server->create_service(ESPBTUUID::from_raw(improv::SERVICE_UUID), true); - this->service_ = global_ble_server->get_service(ESPBTUUID::from_raw(improv::SERVICE_UUID)); + this->service_ = global_ble_server->create_service(ESPBTUUID::from_raw(improv::SERVICE_UUID), true); this->setup_characteristics(); } @@ -93,15 +98,15 @@ void ESP32ImprovComponent::loop() { case improv::STATE_STOPPED: this->set_status_indicator_state_(false); - if (this->service_->is_created() && this->should_start_ && this->setup_complete_) { - if (this->service_->is_running()) { + if (this->should_start_ && this->setup_complete_) { + if (this->service_->is_created()) { + this->service_->start(); + } else if (this->service_->is_running()) { esp32_ble::global_ble->advertising_start(); this->set_state_(improv::STATE_AWAITING_AUTHORIZATION); this->set_error_(improv::ERROR_NONE); ESP_LOGD(TAG, "Service started!"); - } else { - this->service_->start(); } } break; @@ -199,8 +204,7 @@ void ESP32ImprovComponent::set_state_(improv::State state) { ESP_LOGV(TAG, "Setting state: %d", state); this->state_ = state; if (this->status_->get_value().empty() || this->status_->get_value()[0] != state) { - uint8_t data[1]{state}; - this->status_->set_value(data, 1); + this->status_->set_value(ByteBuffer::wrap(static_cast(state))); if (state != improv::STATE_STOPPED) this->status_->notify(); } @@ -232,15 +236,14 @@ void ESP32ImprovComponent::set_error_(improv::Error error) { ESP_LOGE(TAG, "Error: %d", error); } if (this->error_->get_value().empty() || this->error_->get_value()[0] != error) { - uint8_t data[1]{error}; - this->error_->set_value(data, 1); + this->error_->set_value(ByteBuffer::wrap(static_cast(error))); if (this->state_ != improv::STATE_STOPPED) this->error_->notify(); } } void ESP32ImprovComponent::send_response_(std::vector &response) { - this->rpc_response_->set_value(response); + this->rpc_response_->set_value(ByteBuffer::wrap(response)); if (this->state_ != improv::STATE_STOPPED) this->rpc_response_->notify(); } @@ -339,8 +342,6 @@ void ESP32ImprovComponent::on_wifi_connect_timeout_() { wifi::global_wifi_component->clear_sta(); } -void ESP32ImprovComponent::on_client_disconnect() { this->set_error_(improv::ERROR_NONE); }; - ESP32ImprovComponent *global_improv_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esp32_improv diff --git a/esphome/components/esp32_improv/esp32_improv_component.h b/esphome/components/esp32_improv/esp32_improv_component.h index 062b3f585b..87cec23876 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.h +++ b/esphome/components/esp32_improv/esp32_improv_component.h @@ -32,18 +32,17 @@ namespace esp32_improv { using namespace esp32_ble_server; -class ESP32ImprovComponent : public Component, public BLEServiceComponent { +class ESP32ImprovComponent : public Component { public: ESP32ImprovComponent(); void dump_config() override; void loop() override; void setup() override; void setup_characteristics(); - void on_client_disconnect() override; float get_setup_priority() const override; - void start() override; - void stop() override; + void start(); + void stop(); bool is_active() const { return this->state_ != improv::STATE_STOPPED; } #ifdef USE_ESP32_IMPROV_STATE_CALLBACK diff --git a/esphome/components/event_emitter/__init__.py b/esphome/components/event_emitter/__init__.py new file mode 100644 index 0000000000..fcbbf26f02 --- /dev/null +++ b/esphome/components/event_emitter/__init__.py @@ -0,0 +1,5 @@ +CODEOWNERS = ["@Rapsssito"] + +# Allows event_emitter to be configured in yaml, to allow use of the C++ api. + +CONFIG_SCHEMA = {} diff --git a/esphome/components/event_emitter/event_emitter.cpp b/esphome/components/event_emitter/event_emitter.cpp new file mode 100644 index 0000000000..8487e19c2f --- /dev/null +++ b/esphome/components/event_emitter/event_emitter.cpp @@ -0,0 +1,14 @@ +#include "event_emitter.h" + +namespace esphome { +namespace event_emitter { + +static const char *const TAG = "event_emitter"; + +void raise_event_emitter_full_error() { + ESP_LOGE(TAG, "EventEmitter has reached the maximum number of listeners for event"); + ESP_LOGW(TAG, "Removing listener to make space for new listener"); +} + +} // namespace event_emitter +} // namespace esphome diff --git a/esphome/components/event_emitter/event_emitter.h b/esphome/components/event_emitter/event_emitter.h new file mode 100644 index 0000000000..3876a2cc14 --- /dev/null +++ b/esphome/components/event_emitter/event_emitter.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include + +#include "esphome/core/log.h" + +namespace esphome { +namespace event_emitter { + +using EventEmitterListenerID = uint32_t; +void raise_event_emitter_full_error(); + +// EventEmitter class that can emit events with a specific name (it is highly recommended to use an enum class for this) +// and a list of arguments. Supports multiple listeners for each event. +template class EventEmitter { + public: + EventEmitterListenerID on(EvtType event, std::function listener) { + EventEmitterListenerID listener_id = get_next_id_(event); + listeners_[event][listener_id] = listener; + return listener_id; + } + + void off(EvtType event, EventEmitterListenerID id) { + if (listeners_.count(event) == 0) + return; + listeners_[event].erase(id); + } + + protected: + void emit_(EvtType event, Args... args) { + if (listeners_.count(event) == 0) + return; + for (const auto &listener : listeners_[event]) { + listener.second(args...); + } + } + + EventEmitterListenerID get_next_id_(EvtType event) { + // Check if the map is full + if (listeners_[event].size() == std::numeric_limits::max()) { + // Raise an error if the map is full + raise_event_emitter_full_error(); + off(event, 0); + return 0; + } + // Get the next ID for the given event. + EventEmitterListenerID next_id = (current_id_ + 1) % std::numeric_limits::max(); + while (listeners_[event].count(next_id) > 0) { + next_id = (next_id + 1) % std::numeric_limits::max(); + } + current_id_ = next_id; + return current_id_; + } + + private: + std::unordered_map>> listeners_; + EventEmitterListenerID current_id_ = 0; +}; + +} // namespace event_emitter +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 95bf6afc02..ab41d8cbc2 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -530,6 +530,7 @@ CONF_NETWORKS = "networks" CONF_NEW_PASSWORD = "new_password" CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide" CONF_NOISE_LEVEL = "noise_level" +CONF_NOTIFY = "notify" CONF_NUM_ATTEMPTS = "num_attempts" CONF_NUM_CHANNELS = "num_channels" CONF_NUM_CHIPS = "num_chips" diff --git a/tests/components/esp32_ble_server/common.yaml b/tests/components/esp32_ble_server/common.yaml index 29a5407f84..696f4ea8fe 100644 --- a/tests/components/esp32_ble_server/common.yaml +++ b/tests/components/esp32_ble_server/common.yaml @@ -1,3 +1,66 @@ esp32_ble_server: - id: ble + id: ble_server manufacturer_data: [0x72, 0x4, 0x00, 0x23] + manufacturer: ESPHome + model: Test + on_connect: + - lambda: |- + ESP_LOGD("BLE", "Connection from %d", id); + on_disconnect: + - lambda: |- + ESP_LOGD("BLE", "Disconnection from %d", id); + services: + - uuid: 2a24b789-7aab-4535-af3e-ee76a35cc12d + advertise: false + characteristics: + - id: test_notify_characteristic + description: "Notify characteristic" + uuid: cad48e28-7fbe-41cf-bae9-d77a6c233423 + read: true + notify: true + value: [1, 2, 3, 4] + descriptors: + - uuid: cad48e28-7fbe-41cf-bae9-d77a6c111111 + on_write: + logger.log: + format: "Descriptor write id %u, data %s" + args: [id, 'format_hex_pretty(x.data(), x.size()).c_str()'] + value: + data: "123.1" + type: float + endianness: BIG + - uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc42d + advertise: false + characteristics: + - id: test_change_characteristic + uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc11c + read: true + value: + data: "Initial" + string_encoding: utf-8 + description: Change characteristic + descriptors: + - uuid: 0x4414 + id: test_change_descriptor + value: "Initial descriptor value" + - uuid: 0x2312 + value: + data: 0x12 + type: uint16_t + on_write: + - lambda: |- + ESP_LOGD("BLE", "Descriptor received: %s from %d", std::string(x.begin(), x.end()).c_str(), id); + - uuid: 2a24b789-7a1b-4535-af3e-ee76a35cc99a + write: true + on_write: + then: + - lambda: |- + ESP_LOGD("BLE", "Characteristic received: %s from %d", std::string(x.begin(), x.end()).c_str(), id); + - ble_server.characteristic.set_value: + id: test_change_characteristic + value: !lambda 'return bytebuffer::ByteBuffer::wrap({0x00, 0x01, 0x02}).get_data();' + - ble_server.characteristic.notify: + id: test_notify_characteristic + - ble_server.descriptor.set_value: + id: test_change_descriptor + value: !lambda return bytebuffer::ByteBuffer::wrap({0x03, 0x04, 0x05}).get_data(); From 7dab1a6082a2b9938f0c7f28d6a9861df0ad718b Mon Sep 17 00:00:00 2001 From: guillempages Date: Wed, 29 Jan 2025 00:35:43 +0100 Subject: [PATCH 0924/1052] [online_image] Add JPEG support to online_image (#8127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jimmy Hedman Co-authored-by: J. Nick Koston Co-authored-by: Rodrigo Martín Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- esphome/components/online_image/__init__.py | 14 ++- .../components/online_image/image_decoder.cpp | 15 ++++ .../components/online_image/image_decoder.h | 2 + .../components/online_image/jpeg_image.cpp | 89 +++++++++++++++++++ esphome/components/online_image/jpeg_image.h | 34 +++++++ .../components/online_image/online_image.cpp | 22 ++++- .../components/online_image/online_image.h | 17 +++- esphome/components/online_image/png_image.cpp | 1 - esphome/core/defines.h | 1 + platformio.ini | 2 + tests/components/online_image/common.yaml | 8 ++ 11 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 esphome/components/online_image/jpeg_image.cpp create mode 100644 esphome/components/online_image/jpeg_image.h diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index eb6debf8eb..c476270571 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -60,6 +60,15 @@ class BMPFormat(Format): cg.add_define("USE_ONLINE_IMAGE_BMP_SUPPORT") +class JPEGFormat(Format): + def __init__(self): + super().__init__("JPEG") + + def actions(self): + cg.add_define("USE_ONLINE_IMAGE_JPEG_SUPPORT") + cg.add_library("JPEGDEC", "1.6.2", "https://github.com/bitbank2/JPEGDEC") + + class PNGFormat(Format): def __init__(self): super().__init__("PNG") @@ -69,14 +78,15 @@ class PNGFormat(Format): cg.add_library("pngle", "1.0.2") -# New formats can be added here. IMAGE_FORMATS = { x.image_type: x for x in ( BMPFormat(), + JPEGFormat(), PNGFormat(), ) } +IMAGE_FORMATS.update({"JPG": IMAGE_FORMATS["JPEG"]}) OnlineImage = online_image_ns.class_("OnlineImage", cg.PollingComponent, Image_) @@ -116,7 +126,7 @@ ONLINE_IMAGE_SCHEMA = ( cv.Required(CONF_URL): cv.url, 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=2048): cv.int_range(256, 65536), + cv.Optional(CONF_BUFFER_SIZE, default=65536): cv.int_range(256, 65536), cv.Optional(CONF_ON_DOWNLOAD_FINISHED): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( diff --git a/esphome/components/online_image/image_decoder.cpp b/esphome/components/online_image/image_decoder.cpp index d0d0495ba6..2958d8671d 100644 --- a/esphome/components/online_image/image_decoder.cpp +++ b/esphome/components/online_image/image_decoder.cpp @@ -41,5 +41,20 @@ size_t DownloadBuffer::read(size_t len) { return this->unread_; } +size_t DownloadBuffer::resize(size_t size) { + if (this->size_ == size) { + return size; + } + this->allocator_.deallocate(this->buffer_, this->size_); + this->size_ = size; + this->buffer_ = this->allocator_.allocate(size); + this->reset(); + if (this->buffer_) { + return size; + } else { + return 0; + } +} + } // namespace online_image } // namespace esphome diff --git a/esphome/components/online_image/image_decoder.h b/esphome/components/online_image/image_decoder.h index 4e5dd7b229..957af49ac9 100644 --- a/esphome/components/online_image/image_decoder.h +++ b/esphome/components/online_image/image_decoder.h @@ -106,6 +106,8 @@ class DownloadBuffer { void reset() { this->unread_ = 0; } + size_t resize(size_t size); + protected: RAMAllocator allocator_{}; uint8_t *buffer_; diff --git a/esphome/components/online_image/jpeg_image.cpp b/esphome/components/online_image/jpeg_image.cpp new file mode 100644 index 0000000000..773b85a2c4 --- /dev/null +++ b/esphome/components/online_image/jpeg_image.cpp @@ -0,0 +1,89 @@ +#include "jpeg_image.h" +#ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT + +#include "esphome/components/display/display_buffer.h" +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#include "online_image.h" +static const char *const TAG = "online_image.jpeg"; + +namespace esphome { +namespace online_image { + +/** + * @brief Callback method that will be called by the JPEGDEC engine when a chunk + * of the image is decoded. + * + * @param jpeg The JPEGDRAW object, including the context data. + */ +static int draw_callback(JPEGDRAW *jpeg) { + ImageDecoder *decoder = (ImageDecoder *) jpeg->pUser; + + // Some very big images take too long to decode, so feed the watchdog on each callback + // to avoid crashing. + App.feed_wdt(); + size_t position = 0; + for (size_t y = 0; y < jpeg->iHeight; y++) { + for (size_t x = 0; x < jpeg->iWidth; x++) { + auto rg = decode_value(jpeg->pPixels[position++]); + auto ba = decode_value(jpeg->pPixels[position++]); + Color color(rg[1], rg[0], ba[1], ba[0]); + + if (!decoder) { + ESP_LOGE(TAG, "Decoder pointer is null!"); + return 0; + } + decoder->draw(jpeg->x + x, jpeg->y + y, 1, 1, color); + } + } + return 1; +} + +void JpegDecoder::prepare(size_t download_size) { + ImageDecoder::prepare(download_size); + auto size = this->image_->resize_download_buffer(download_size); + if (size < download_size) { + ESP_LOGE(TAG, "Resize failed!"); + // TODO: return an error code; + } +} + +int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { + if (size < this->download_size_) { + ESP_LOGV(TAG, "Download not complete. Size: %d/%d", size, this->download_size_); + return 0; + } + + if (!this->jpeg_.openRAM(buffer, size, draw_callback)) { + ESP_LOGE(TAG, "Could not open image for decoding."); + return DECODE_ERROR_INVALID_TYPE; + } + auto jpeg_type = this->jpeg_.getJPEGType(); + if (jpeg_type == JPEG_MODE_INVALID) { + ESP_LOGE(TAG, "Unsupported JPEG image"); + return DECODE_ERROR_INVALID_TYPE; + } else if (jpeg_type == JPEG_MODE_PROGRESSIVE) { + ESP_LOGE(TAG, "Progressive JPEG images not supported"); + return DECODE_ERROR_INVALID_TYPE; + } + ESP_LOGD(TAG, "Image size: %d x %d, bpp: %d", this->jpeg_.getWidth(), this->jpeg_.getHeight(), this->jpeg_.getBpp()); + + this->jpeg_.setUserPointer(this); + this->jpeg_.setPixelType(RGB8888); + this->set_size(this->jpeg_.getWidth(), this->jpeg_.getHeight()); + if (!this->jpeg_.decode(0, 0, 0)) { + ESP_LOGE(TAG, "Error while decoding."); + this->jpeg_.close(); + return DECODE_ERROR_UNSUPPORTED_FORMAT; + } + this->decoded_bytes_ = size; + this->jpeg_.close(); + return size; +} + +} // namespace online_image +} // namespace esphome + +#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT diff --git a/esphome/components/online_image/jpeg_image.h b/esphome/components/online_image/jpeg_image.h new file mode 100644 index 0000000000..f04a35655a --- /dev/null +++ b/esphome/components/online_image/jpeg_image.h @@ -0,0 +1,34 @@ +#pragma once + +#include "image_decoder.h" +#include "esphome/core/defines.h" +#ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT +#include + +namespace esphome { +namespace online_image { + +/** + * @brief Image decoder specialization for JPEG images. + */ +class JpegDecoder : public ImageDecoder { + public: + /** + * @brief Construct a new JPEG Decoder object. + * + * @param display The image to decode the stream into. + */ + JpegDecoder(OnlineImage *image) : ImageDecoder(image) {} + ~JpegDecoder() override {} + + void prepare(size_t download_size) override; + int HOT decode(uint8_t *buffer, size_t size) override; + + protected: + JPEGDEC jpeg_{}; +}; + +} // namespace online_image +} // namespace esphome + +#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index c6499c24e4..b08c14b721 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -9,6 +9,9 @@ static const char *const TAG = "online_image"; #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT #include "bmp_image.h" #endif +#ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT +#include "jpeg_image.h" +#endif #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT #include "png_image.h" #endif @@ -32,6 +35,7 @@ OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFor : Image(nullptr, 0, 0, type, transparency), buffer_(nullptr), download_buffer_(download_buffer_size), + download_buffer_initial_size_(download_buffer_size), format_(format), fixed_width_(width), fixed_height_(height) { @@ -123,23 +127,32 @@ void OnlineImage::update() { #ifdef USE_ONLINE_IMAGE_BMP_SUPPORT if (this->format_ == ImageFormat::BMP) { + ESP_LOGD(TAG, "Allocating BMP decoder"); this->decoder_ = make_unique(this); } #endif // ONLINE_IMAGE_BMP_SUPPORT +#ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT + if (this->format_ == ImageFormat::JPEG) { + ESP_LOGD(TAG, "Allocating JPEG decoder"); + this->decoder_ = esphome::make_unique(this); + } +#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT if (this->format_ == ImageFormat::PNG) { + ESP_LOGD(TAG, "Allocating PNG decoder"); this->decoder_ = make_unique(this); } #endif // ONLINE_IMAGE_PNG_SUPPORT if (!this->decoder_) { - ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported."); + ESP_LOGE(TAG, "Could not instantiate decoder. Image format unsupported: %d", this->format_); this->end_connection_(); this->download_error_callback_.call(); return; } this->decoder_->prepare(total_size); - ESP_LOGI(TAG, "Downloading image"); + ESP_LOGI(TAG, "Downloading image (Size: %d)", total_size); + this->start_time_ = ::time(nullptr); } void OnlineImage::loop() { @@ -153,6 +166,7 @@ void OnlineImage::loop() { this->height_ = buffer_height_; 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->end_connection_(); this->download_finished_callback_.call(); return; @@ -163,6 +177,10 @@ void OnlineImage::loop() { } size_t available = this->download_buffer_.free_capacity(); if (available) { + // Some decoders need to fully download the image before downloading. + // In case of huge images, don't wait blocking until the whole image has been downloaded, + // use smaller chunks + available = std::min(available, this->download_buffer_initial_size_); auto len = this->downloader_->read(this->download_buffer_.append(), available); if (len > 0) { this->download_buffer_.write(len); diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 849f860ad5..4abc047083 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -23,7 +23,7 @@ using t_http_codes = enum { enum ImageFormat { /** Automatically detect from MIME type. Not supported yet. */ AUTO, - /** JPEG format. Not supported yet. */ + /** JPEG format. */ JPEG, /** PNG format. */ PNG, @@ -79,6 +79,13 @@ class OnlineImage : public PollingComponent, */ void release(); + /** + * Resize the download buffer + * + * @param size The new size for the download buffer. + */ + 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_error_callback(std::function &&callback); @@ -119,6 +126,12 @@ class OnlineImage : public PollingComponent, uint8_t *buffer_; DownloadBuffer download_buffer_; + /** + * This is the *initial* size of the download buffer, not the current size. + * The download buffer can be resized at runtime; the download_buffer_initial_size_ + * will *not* change even if the download buffer has been resized. + */ + size_t download_buffer_initial_size_; const ImageFormat format_; image::Image *placeholder_{nullptr}; @@ -148,6 +161,8 @@ class OnlineImage : public PollingComponent, */ int buffer_height_; + time_t start_time_; + friend bool ImageDecoder::set_size(int width, int height); friend void ImageDecoder::draw(int x, int y, int w, int h, const Color &color); }; diff --git a/esphome/components/online_image/png_image.cpp b/esphome/components/online_image/png_image.cpp index 59c1ce6c7d..6bc968c7ba 100644 --- a/esphome/components/online_image/png_image.cpp +++ b/esphome/components/online_image/png_image.cpp @@ -2,7 +2,6 @@ #ifdef USE_ONLINE_IMAGE_PNG_SUPPORT #include "esphome/components/display/display_buffer.h" -#include "esphome/core/application.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 074b19809f..211f3b8319 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -62,6 +62,7 @@ #define USE_NUMBER #define USE_ONLINE_IMAGE_BMP_SUPPORT #define USE_ONLINE_IMAGE_PNG_SUPPORT +#define USE_ONLINE_IMAGE_JPEG_SUPPORT #define USE_OTA #define USE_OTA_PASSWORD #define USE_OTA_STATE_CALLBACK diff --git a/platformio.ini b/platformio.ini index b9b80e933f..e91c06d86e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -41,6 +41,8 @@ lib_deps = functionpointer/arduino-MLX90393@1.0.2 ; mlx90393 pavlodn/HaierProtocol@0.9.31 ; haier kikuchan98/pngle@1.0.2 ; online_image + ; Using the repository directly, otherwise ESP-IDF can't use the library + https://github.com/bitbank2/JPEGDEC.git#1.6.2 ; online_image ; This is using the repository until a new release is published to PlatformIO https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library lvgl/lvgl@8.4.0 ; lvgl diff --git a/tests/components/online_image/common.yaml b/tests/components/online_image/common.yaml index d75900adf9..69daa915c5 100644 --- a/tests/components/online_image/common.yaml +++ b/tests/components/online_image/common.yaml @@ -30,6 +30,14 @@ online_image: url: https://samples-files.com/samples/images/bmp/480-360-sample.bmp format: BMP type: BINARY + - id: online_jpeg_image + url: http://www.faqs.org/images/library.jpg + format: JPEG + type: RGB + - id: online_jpg_image + url: http://www.faqs.org/images/library.jpg + format: JPG + type: RGB565 # Check the set_url action esphome: From 2489f951071ae45743a0dc1e28aa354bfc1250bd Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 29 Jan 2025 10:58:06 +1100 Subject: [PATCH 0925/1052] [logger] Ensure PRIu32 and friends are available (#8155) --- esphome/core/log.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/log.h b/esphome/core/log.h index 99a68024c5..9ab80afe78 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -2,6 +2,8 @@ #include #include +// for PRIu32 and friends +#include #include #ifdef USE_STORE_LOG_STR_IN_FLASH From a23ce416ea150c1d0fda829664097f7a26253223 Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Wed, 29 Jan 2025 04:54:10 +0100 Subject: [PATCH 0926/1052] Fix forgotten uses of use_transparency (#8115) --- esphome/components/image/__init__.py | 6 ++---- tests/components/lvgl/lvgl-package.yaml | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index 7cdce757be..20b041a321 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -273,11 +273,9 @@ IMAGE_TYPE = { "GRAYSCALE": ImageGrayscale, "RGB565": ImageRGB565, "RGB": ImageRGB, - "TRANSPARENT_BINARY": ReplaceWith( - "'type: BINARY' and 'use_transparency: chroma_key'" - ), + "TRANSPARENT_BINARY": ReplaceWith("'type: BINARY' and 'transparency: chroma_key'"), "RGB24": ReplaceWith("'type: RGB'"), - "RGBA": ReplaceWith("'type: RGB' and 'use_transparency: alpha_channel'"), + "RGBA": ReplaceWith("'type: RGB' and 'transparency: alpha_channel'"), } TransparencyType = image_ns.enum("TransparencyType") diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index b3227bb96e..c51a3d03e7 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -834,12 +834,12 @@ image: resize: 256x48 file: $component_dir/logo-text.svg type: RGB565 - use_transparency: alpha_channel + transparency: alpha_channel - id: dog_image file: $component_dir/logo-text.svg resize: 256x48 type: BINARY - use_transparency: chroma_key + transparency: chroma_key color: - id: light_blue From 9957840dfc7544eec0369fd5cec9a4a190dc8ea5 Mon Sep 17 00:00:00 2001 From: Jimmy Hedman Date: Wed, 29 Jan 2025 11:00:18 +0100 Subject: [PATCH 0927/1052] Add multicast support to udp component (#8051) --- esphome/components/network/ip_address.h | 2 ++ esphome/components/udp/__init__.py | 6 ++++++ esphome/components/udp/udp_component.cpp | 18 ++++++++++++++++++ esphome/components/udp/udp_component.h | 3 +++ esphome/config_validation.py | 9 +++++++++ tests/components/udp/common.yaml | 1 + 6 files changed, 39 insertions(+) diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index 69d3788ca5..1598daf6f9 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -49,6 +49,7 @@ struct IPAddress { } IPAddress(const std::string &in_address) { inet_aton(in_address.c_str(), &ip_addr_); } IPAddress(const ip_addr_t *other_ip) { ip_addr_ = *other_ip; } + std::string str() const { return str_lower_case(inet_ntoa(ip_addr_)); } #else IPAddress() { ip_addr_set_zero(&ip_addr_); } IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { @@ -119,6 +120,7 @@ struct IPAddress { bool is_set() { return !ip_addr_isany(&ip_addr_); } // NOLINT(readability-simplify-boolean-expr) bool is_ip4() { return IP_IS_V4(&ip_addr_); } bool is_ip6() { return IP_IS_V6(&ip_addr_); } + bool is_multicast() { return ip_addr_ismulticast(&ip_addr_); } std::string str() const { return str_lower_case(ipaddr_ntoa(&ip_addr_)); } bool operator==(const IPAddress &other) const { return ip_addr_cmp(&ip_addr_, &other.ip_addr_); } bool operator!=(const IPAddress &other) const { return !ip_addr_cmp(&ip_addr_, &other.ip_addr_); } diff --git a/esphome/components/udp/__init__.py b/esphome/components/udp/__init__.py index e189975ade..5485663f1c 100644 --- a/esphome/components/udp/__init__.py +++ b/esphome/components/udp/__init__.py @@ -27,6 +27,7 @@ UDPComponent = udp_ns.class_("UDPComponent", cg.PollingComponent) CONF_BROADCAST = "broadcast" CONF_BROADCAST_ID = "broadcast_id" CONF_ADDRESSES = "addresses" +CONF_LISTEN_ADDRESS = "listen_address" CONF_PROVIDER = "provider" CONF_PROVIDERS = "providers" CONF_REMOTE_ID = "remote_id" @@ -84,6 +85,9 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(UDPComponent), cv.Optional(CONF_PORT, default=18511): cv.port, + cv.Optional( + CONF_LISTEN_ADDRESS, default="255.255.255.255" + ): cv.ipv4address_multi_broadcast, cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list( cv.ipv4address, ), @@ -154,5 +158,7 @@ async def to_code(config): for provider in config.get(CONF_PROVIDERS, ()): name = provider[CONF_NAME] cg.add(var.add_provider(name)) + if (listen_address := str(config[CONF_LISTEN_ADDRESS])) != "255.255.255.255": + cg.add(var.set_listen_address(listen_address)) if encryption := provider.get(CONF_ENCRYPTION): cg.add(var.set_provider_encryption(name, hash_encryption_key(encryption))) diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index e29620fa9a..30f7356879 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -249,6 +249,21 @@ void UDPComponent::setup() { server.sin_addr.s_addr = ESPHOME_INADDR_ANY; server.sin_port = htons(this->port_); + if (this->listen_address_.has_value()) { + struct ip_mreq imreq = {}; + imreq.imr_interface.s_addr = ESPHOME_INADDR_ANY; + inet_aton(this->listen_address_.value().str().c_str(), &imreq.imr_multiaddr); + server.sin_addr.s_addr = imreq.imr_multiaddr.s_addr; + ESP_LOGV(TAG, "Join multicast %s", this->listen_address_.value().str().c_str()); + err = this->listen_socket_->setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(imreq)); + if (err < 0) { + ESP_LOGE(TAG, "Failed to set IP_ADD_MEMBERSHIP. Error %d", errno); + this->mark_failed(); + this->status_set_error("Failed to set IP_ADD_MEMBERSHIP"); + return; + } + } + err = this->listen_socket_->bind((struct sockaddr *) &server, sizeof(server)); if (err != 0) { ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); @@ -565,6 +580,9 @@ void UDPComponent::dump_config() { ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_)); 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()); + } #ifdef USE_SENSOR for (auto sensor : this->sensors_) ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id); diff --git a/esphome/components/udp/udp_component.h b/esphome/components/udp/udp_component.h index b4e11cf652..fb9b93e255 100644 --- a/esphome/components/udp/udp_component.h +++ b/esphome/components/udp/udp_component.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/component.h" +#include "esphome/components/network/ip_address.h" #ifdef USE_SENSOR #include "esphome/components/sensor/sensor.h" #endif @@ -69,6 +70,7 @@ class UDPComponent : public PollingComponent { } #endif void add_address(const char *addr) { this->addresses_.emplace_back(addr); } + void set_listen_address(const char *listen_addr) { this->listen_address_ = network::IPAddress(listen_addr); } void set_port(uint16_t port) { this->port_ = port; } float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } @@ -143,6 +145,7 @@ class UDPComponent : public PollingComponent { std::map> remote_binary_sensors_{}; #endif + optional listen_address_{}; std::map providers_{}; std::vector ping_header_{}; std::vector header_{}; diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 20a0774ccb..27d11e4ded 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1168,6 +1168,15 @@ def ipv4address(value): return address +def ipv4address_multi_broadcast(value): + address = ipv4address(value) + if not (address.is_multicast or (address == IPv4Address("255.255.255.255"))): + raise Invalid( + f"{value} is not a multicasst address nor local broadcast address" + ) + return address + + def ipaddress(value): try: address = ip_address(value) diff --git a/tests/components/udp/common.yaml b/tests/components/udp/common.yaml index 3bdc19ece5..e533cb965e 100644 --- a/tests/components/udp/common.yaml +++ b/tests/components/udp/common.yaml @@ -7,6 +7,7 @@ udp: encryption: "our key goes here" rolling_code_enable: true ping_pong_enable: true + listen_address: 239.0.60.53 binary_sensors: - binary_sensor_id1 - id: binary_sensor_id1 From 619ce93dece3bc066115ae1ec828b9f88925333a Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 29 Jan 2025 21:45:29 +1100 Subject: [PATCH 0928/1052] [display] Properly handle case of auto_clear_enabled: false (#8156) --- esphome/components/display/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/display/__init__.py b/esphome/components/display/__init__.py index 99224df7b3..12c63231e7 100644 --- a/esphome/components/display/__init__.py +++ b/esphome/components/display/__init__.py @@ -101,7 +101,7 @@ async def setup_display_core_(var, config): if CONF_ROTATION in config: cg.add(var.set_rotation(DISPLAY_ROTATIONS[config[CONF_ROTATION]])) - if auto_clear := config.get(CONF_AUTO_CLEAR_ENABLED): + if (auto_clear := config.get(CONF_AUTO_CLEAR_ENABLED)) is not None: # Default to true if pages or lambda is specified. Ideally this would be done during validation, but # the possible schemas are too complex to do this easily. if auto_clear == CONF_UNSPECIFIED: From 67ccd0eb7f864307891d71983338acc99547e808 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 29 Jan 2025 05:51:04 -0500 Subject: [PATCH 0929/1052] [esp32_rmt] Increase default symbols in led strip and remove IRAM config (#8133) --- esphome/components/esp32_rmt_led_strip/light.py | 12 ++++++------ esphome/components/remote_receiver/__init__.py | 5 +---- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/esphome/components/esp32_rmt_led_strip/light.py b/esphome/components/esp32_rmt_led_strip/light.py index 64104bb6de..e2c9f7e64a 100644 --- a/esphome/components/esp32_rmt_led_strip/light.py +++ b/esphome/components/esp32_rmt_led_strip/light.py @@ -127,12 +127,12 @@ CONFIG_SCHEMA = cv.All( ), OptionalForIDF5( CONF_RMT_SYMBOLS, - esp32_idf=64, - esp32_s2_idf=64, - esp32_s3_idf=48, - esp32_c3_idf=48, - esp32_c6_idf=48, - esp32_h2_idf=48, + esp32_idf=192, + esp32_s2_idf=192, + esp32_s3_idf=192, + esp32_c3_idf=96, + esp32_c6_idf=96, + esp32_h2_idf=96, ): cv.All(only_with_new_rmt_driver, cv.int_range(min=2)), cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds, cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index b01443a974..8c21cb210c 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, esp32_rmt, remote_base +from esphome.components import esp32_rmt, remote_base import esphome.config_validation as cv from esphome.const import ( CONF_BUFFER_SIZE, @@ -158,9 +158,6 @@ async def to_code(config): cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) if CONF_FILTER_SYMBOLS in config: cg.add(var.set_filter_symbols(config[CONF_FILTER_SYMBOLS])) - if CORE.using_esp_idf: - esp32.add_idf_sdkconfig_option("CONFIG_RMT_RECV_FUNC_IN_IRAM", True) - esp32.add_idf_sdkconfig_option("CONFIG_RMT_ISR_IRAM_SAFE", True) else: if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: var = cg.new_Pvariable( From ba3e5e8ecb77639b549114217c128e4e3cc58341 Mon Sep 17 00:00:00 2001 From: Olliver Schinagl Date: Wed, 29 Jan 2025 12:27:55 +0100 Subject: [PATCH 0930/1052] =?UTF-8?q?[climate]=20Accept=20=C2=B0K=20as=20i?= =?UTF-8?q?ntended=20(#8134)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- esphome/components/climate/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index ec68940726..aa705e7332 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -115,7 +115,7 @@ CONF_MAX_HUMIDITY = "max_humidity" CONF_TARGET_HUMIDITY = "target_humidity" visual_temperature = cv.float_with_unit( - "visual_temperature", "(°C|° C|°|C|° K|° K|K|°F|° F|F)?" + "visual_temperature", "(°C|° C|°|C|°K|° K|K|°F|° F|F)?" ) From f9856135d0fd6c919f732e18f374d3b7367ac209 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2025 13:40:25 +0100 Subject: [PATCH 0931/1052] Bump docker/build-push-action from 6.12.0 to 6.13.0 in /.github/actions/build-image (#8136) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/build-image/action.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/build-image/action.yaml b/.github/actions/build-image/action.yaml index b2a3394563..25ae21fbd0 100644 --- a/.github/actions/build-image/action.yaml +++ b/.github/actions/build-image/action.yaml @@ -46,7 +46,7 @@ runs: - name: Build and push to ghcr by digest id: build-ghcr - uses: docker/build-push-action@v6.12.0 + uses: docker/build-push-action@v6.13.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false @@ -72,7 +72,7 @@ runs: - name: Build and push to dockerhub by digest id: build-dockerhub - uses: docker/build-push-action@v6.12.0 + uses: docker/build-push-action@v6.13.0 env: DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_RECORD_UPLOAD: false From 334e952a340e57e84351fcb59ee29745ecb6d47e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2025 13:40:31 +0100 Subject: [PATCH 0932/1052] Bump pypa/gh-action-pypi-publish from 1.12.3 to 1.12.4 (#8137) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 962bc66e94..1d6813b1d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -65,7 +65,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.12.3 + uses: pypa/gh-action-pypi-publish@v1.12.4 deploy-docker: name: Build ESPHome ${{ matrix.platform }} From 7727879f01b4d52417b447446ab4575f301310d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:30:30 +0100 Subject: [PATCH 0933/1052] Bump actions/setup-python from 5.3.0 to 5.4.0 (#8154) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-api-proto.yml | 2 +- .github/workflows/ci-docker.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/sync-device-classes.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-api-proto.yml b/.github/workflows/ci-api-proto.yml index a6b2e2b2b3..d06d8a8eec 100644 --- a/.github/workflows/ci-api-proto.yml +++ b/.github/workflows/ci-api-proto.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: "3.11" diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index b994cfaf17..65f847bc66 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -42,7 +42,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: "3.9" - name: Set up Docker Buildx diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a344b177ae..ab77db5ca5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d6813b1d1..ca266c1f2c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: "3.x" - name: Set up python environment @@ -85,7 +85,7 @@ jobs: steps: - uses: actions/checkout@v4.1.7 - name: Set up Python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: "3.9" diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 9160ab4a1b..9abbb20e86 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -22,7 +22,7 @@ jobs: path: lib/home-assistant - name: Setup Python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: 3.12 From 12d6c1bbcae83cb79ed5f6018c18339a42a4dbf5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:31:49 +0100 Subject: [PATCH 0934/1052] Bump actions/setup-python from 5.3.0 to 5.4.0 in /.github/actions/restore-python (#8153) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index 6b87cf0170..e95eb6331f 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -17,7 +17,7 @@ runs: steps: - name: Set up Python ${{ inputs.python-version }} id: python - uses: actions/setup-python@v5.3.0 + uses: actions/setup-python@v5.4.0 with: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment From 714e2d3e56be5ecb1a0e1a1ea8a5ec1167f7e6c5 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Wed, 29 Jan 2025 08:34:10 -0500 Subject: [PATCH 0935/1052] [remote_transmitter] Fix issues with 32bit rollover on esp8266 and libretiny (#8056) Co-authored-by: Jonathan Swoboda --- .../remote_transmitter/remote_transmitter_esp8266.cpp | 6 +++--- .../remote_transmitter/remote_transmitter_libretiny.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index 613f00b7f5..09cc16e975 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -37,7 +37,7 @@ void RemoteTransmitterComponent::await_target_time_() { const uint32_t current_time = micros(); if (this->target_time_ == 0) { this->target_time_ = current_time; - } else if (this->target_time_ > current_time) { + } else if ((int32_t) (this->target_time_ - current_time) > 0) { delayMicroseconds(this->target_time_ - current_time); } } @@ -50,13 +50,13 @@ void RemoteTransmitterComponent::mark_(uint32_t on_time, uint32_t off_time, uint if (this->carrier_duty_percent_ < 100 && (on_time > 0 || off_time > 0)) { while (true) { // Modulate with carrier frequency this->target_time_ += on_time; - if (this->target_time_ >= target) + if ((int32_t) (this->target_time_ - target) >= 0) break; this->await_target_time_(); this->pin_->digital_write(false); this->target_time_ += off_time; - if (this->target_time_ >= target) + if ((int32_t) (this->target_time_ - target) >= 0) break; this->await_target_time_(); this->pin_->digital_write(true); diff --git a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp index ad9265fb14..20d8736c00 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp @@ -38,7 +38,7 @@ void RemoteTransmitterComponent::await_target_time_() { if (this->target_time_ == 0) { this->target_time_ = current_time; } else { - while (this->target_time_ > micros()) { + while ((int32_t) (this->target_time_ - micros()) > 0) { // busy loop that ensures micros is constantly called } } @@ -52,13 +52,13 @@ void RemoteTransmitterComponent::mark_(uint32_t on_time, uint32_t off_time, uint if (this->carrier_duty_percent_ < 100 && (on_time > 0 || off_time > 0)) { while (true) { // Modulate with carrier frequency this->target_time_ += on_time; - if (this->target_time_ >= target) + if ((int32_t) (this->target_time_ - target) >= 0) break; this->await_target_time_(); this->pin_->digital_write(false); this->target_time_ += off_time; - if (this->target_time_ >= target) + if ((int32_t) (this->target_time_ - target) >= 0) break; this->await_target_time_(); this->pin_->digital_write(true); From 73923976302825a2951bd8683cee3eda6a906b76 Mon Sep 17 00:00:00 2001 From: NicoIIT <69118344+NicoIIT@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:03:42 +0100 Subject: [PATCH 0936/1052] Use abspath for config path dir (#8044) --- esphome/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index f26c3da483..59cea6f09b 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -582,7 +582,7 @@ class EsphomeCore: @property def config_dir(self): - return os.path.dirname(self.config_path) + return os.path.dirname(os.path.abspath(self.config_path)) @property def data_dir(self): From 051fa3a49fe58155e48acfe168349c31c5efb344 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sat, 1 Feb 2025 05:13:38 -0500 Subject: [PATCH 0937/1052] [remote_base] Add default value for offset in is_valid (#8159) --- esphome/components/remote_base/remote_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 70691177ef..4131d080f5 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -54,7 +54,7 @@ class RemoteReceiveData { uint32_t get_index() const { return index_; } int32_t operator[](uint32_t index) const { return this->data_[index]; } int32_t size() const { return this->data_.size(); } - bool is_valid(uint32_t offset) const { return this->index_ + offset < this->data_.size(); } + bool is_valid(uint32_t offset = 0) const { return this->index_ + offset < this->data_.size(); } int32_t peek(uint32_t offset = 0) const { return this->data_[this->index_ + offset]; } bool peek_mark(uint32_t length, uint32_t offset = 0) const; bool peek_space(uint32_t length, uint32_t offset = 0) const; From 03e2701bd0bbed893a830bc37e12c9f91ce18057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Mart=C3=ADn?= Date: Sun, 2 Feb 2025 21:34:38 +0100 Subject: [PATCH 0938/1052] feat(core): Add support for <...> includes (#8132) --- esphome/core/__init__.py | 7 +++++-- esphome/core/config.py | 17 ++++++++++++++++- esphome/cpp_generator.py | 4 ++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 59cea6f09b..2d856d99c5 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -689,7 +689,7 @@ class EsphomeCore: _LOGGER.debug("Adding: %s", expression) return expression - def add_global(self, expression): + def add_global(self, expression, prepend=False): from esphome.cpp_generator import Expression, Statement, statement if isinstance(expression, Expression): @@ -698,7 +698,10 @@ class EsphomeCore: raise ValueError( f"Add '{expression}' must be expression or statement, not {type(expression)}" ) - self.global_statements.append(expression) + if prepend: + self.global_statements.insert(0, expression) + else: + self.global_statements.append(expression) _LOGGER.debug("Adding global: %s", expression) return expression diff --git a/esphome/core/config.py b/esphome/core/config.py index 06ae1d7747..7152414463 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -72,6 +72,9 @@ def validate_hostname(config): def valid_include(value): + # Look for "<...>" includes + if value.startswith("<") and value.endswith(">"): + return value try: return cv.directory(value) except cv.Invalid: @@ -360,7 +363,19 @@ async def to_code(config): CORE.add_job(add_arduino_global_workaround) if config[CONF_INCLUDES]: - CORE.add_job(add_includes, config[CONF_INCLUDES]) + # Get the <...> includes + system_includes = [] + other_includes = [] + for include in config[CONF_INCLUDES]: + if include.startswith("<") and include.endswith(">"): + system_includes.append(include) + else: + other_includes.append(include) + # <...> includes should be at the start + for include in system_includes: + cg.add_global(cg.RawStatement(f"#include {include}"), prepend=True) + # Other includes should be at the end + CORE.add_job(add_includes, other_includes) if project_conf := config.get(CONF_PROJECT): cg.add_define("ESPHOME_PROJECT_NAME", project_conf[CONF_NAME]) diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 7a82d5cba1..4e283868e1 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -588,9 +588,9 @@ def add(expression: Union[Expression, Statement]): CORE.add(expression) -def add_global(expression: Union[SafeExpType, Statement]): +def add_global(expression: Union[SafeExpType, Statement], prepend: bool = False): """Add an expression to the codegen global storage (above setup()).""" - CORE.add_global(expression) + CORE.add_global(expression, prepend) def add_library(name: str, version: Optional[str], repository: Optional[str] = None): From 72c6f04a974bf64c1534773cf626e2a23883d653 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 2 Feb 2025 14:35:52 -0600 Subject: [PATCH 0939/1052] Bump zeroconf to 0.143.0 (#8104) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d96004c8ef..0d93c3cc2d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20241217.1 aioesphomeapi==24.6.2 -zeroconf==0.132.2 +zeroconf==0.143.0 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import glyphsets==1.0.0 From 2b711e532b37746cd5e5d6b68e01012cb19b0d18 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Sun, 2 Feb 2025 14:38:10 -0600 Subject: [PATCH 0940/1052] [i2s_audio] Media Player Components PR1 (#8163) --- esphome/components/i2s_audio/speaker/__init__.py | 11 ++++++++--- esphome/const.py | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index 0355c16321..eb6bb329ad 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -2,7 +2,14 @@ from esphome import pins import esphome.codegen as cg from esphome.components import esp32, speaker import esphome.config_validation as cv -from esphome.const import CONF_CHANNEL, CONF_ID, CONF_MODE, CONF_TIMEOUT +from esphome.const import ( + CONF_BUFFER_DURATION, + CONF_CHANNEL, + CONF_ID, + CONF_MODE, + CONF_NEVER, + CONF_TIMEOUT, +) from .. import ( CONF_I2S_DOUT_PIN, @@ -24,10 +31,8 @@ I2SAudioSpeaker = i2s_audio_ns.class_( "I2SAudioSpeaker", cg.Component, speaker.Speaker, I2SAudioOut ) -CONF_BUFFER_DURATION = "buffer_duration" CONF_DAC_TYPE = "dac_type" CONF_I2S_COMM_FMT = "i2s_comm_fmt" -CONF_NEVER = "never" i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t") INTERNAL_DAC_OPTIONS = { diff --git a/esphome/const.py b/esphome/const.py index ab41d8cbc2..16bfda9478 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -94,6 +94,7 @@ CONF_BRIGHTNESS = "brightness" CONF_BRIGHTNESS_LIMITS = "brightness_limits" CONF_BROKER = "broker" CONF_BSSID = "bssid" +CONF_BUFFER_DURATION = "buffer_duration" CONF_BUFFER_SIZE = "buffer_size" CONF_BUILD_PATH = "build_path" CONF_BUS_VOLTAGE = "bus_voltage" @@ -527,6 +528,7 @@ CONF_NAME_FONT = "name_font" CONF_NBITS = "nbits" CONF_NEC = "nec" CONF_NETWORKS = "networks" +CONF_NEVER = "never" CONF_NEW_PASSWORD = "new_password" CONF_NITROGEN_DIOXIDE = "nitrogen_dioxide" CONF_NOISE_LEVEL = "noise_level" @@ -615,6 +617,7 @@ CONF_OTA = "ota" CONF_OUTDOOR_TEMPERATURE = "outdoor_temperature" CONF_OUTPUT = "output" CONF_OUTPUT_ID = "output_id" +CONF_OUTPUT_SPEAKER = "output_speaker" CONF_OUTPUTS = "outputs" CONF_OVERSAMPLING = "oversampling" CONF_PACKAGES = "packages" @@ -859,6 +862,7 @@ CONF_TARGET_TEMPERATURE_LOW = "target_temperature_low" CONF_TARGET_TEMPERATURE_LOW_COMMAND_TOPIC = "target_temperature_low_command_topic" CONF_TARGET_TEMPERATURE_LOW_STATE_TOPIC = "target_temperature_low_state_topic" CONF_TARGET_TEMPERATURE_STATE_TOPIC = "target_temperature_state_topic" +CONF_TASK_STACK_IN_PSRAM = "task_stack_in_psram" CONF_TEMPERATURE = "temperature" CONF_TEMPERATURE_COMPENSATION = "temperature_compensation" CONF_TEMPERATURE_OFFSET = "temperature_offset" From f6cf99384b8e34bc46162f6843953d22c412eff5 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Sun, 2 Feb 2025 20:25:41 -0600 Subject: [PATCH 0941/1052] [audio, i2s_audio, speaker] Media Player Components PR2 (#8164) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/audio/__init__.py | 112 ++++++++++++++ esphome/components/audio/audio.cpp | 67 +++++++++ esphome/components/audio/audio.h | 132 ++++++++++++++++- .../components/i2s_audio/speaker/__init__.py | 48 +++++- .../i2s_audio/speaker/i2s_audio_speaker.cpp | 138 ++++++++++++------ .../i2s_audio/speaker/i2s_audio_speaker.h | 8 + esphome/components/speaker/__init__.py | 9 +- esphome/components/speaker/speaker.h | 20 +++ platformio.ini | 4 +- 9 files changed, 477 insertions(+), 61 deletions(-) create mode 100644 esphome/components/audio/audio.cpp diff --git a/esphome/components/audio/__init__.py b/esphome/components/audio/__init__.py index 4ffdc401dc..31d3c39ffa 100644 --- a/esphome/components/audio/__init__.py +++ b/esphome/components/audio/__init__.py @@ -1,9 +1,121 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome.const import CONF_BITS_PER_SAMPLE, CONF_NUM_CHANNELS, CONF_SAMPLE_RATE +import esphome.final_validate as fv CODEOWNERS = ["@kahrendt"] audio_ns = cg.esphome_ns.namespace("audio") +AudioFile = audio_ns.struct("AudioFile") +AudioFileType = audio_ns.enum("AudioFileType", is_class=True) +AUDIO_FILE_TYPE_ENUM = { + "NONE": AudioFileType.NONE, + "WAV": AudioFileType.WAV, + "MP3": AudioFileType.MP3, + "FLAC": AudioFileType.FLAC, +} + + +CONF_MIN_BITS_PER_SAMPLE = "min_bits_per_sample" +CONF_MAX_BITS_PER_SAMPLE = "max_bits_per_sample" +CONF_MIN_CHANNELS = "min_channels" +CONF_MAX_CHANNELS = "max_channels" +CONF_MIN_SAMPLE_RATE = "min_sample_rate" +CONF_MAX_SAMPLE_RATE = "max_sample_rate" + + CONFIG_SCHEMA = cv.All( cv.Schema({}), ) + +AUDIO_COMPONENT_SCHEMA = cv.Schema( + { + cv.Optional(CONF_BITS_PER_SAMPLE): cv.int_range(8, 32), + cv.Optional(CONF_NUM_CHANNELS): cv.int_range(1, 2), + cv.Optional(CONF_SAMPLE_RATE): cv.int_range(8000, 48000), + } +) + + +_UNDEF = object() + + +def set_stream_limits( + min_bits_per_sample: int = _UNDEF, + max_bits_per_sample: int = _UNDEF, + min_channels: int = _UNDEF, + max_channels: int = _UNDEF, + min_sample_rate: int = _UNDEF, + max_sample_rate: int = _UNDEF, +): + def set_limits_in_config(config): + if min_bits_per_sample is not _UNDEF: + config[CONF_MIN_BITS_PER_SAMPLE] = min_bits_per_sample + if max_bits_per_sample is not _UNDEF: + config[CONF_MAX_BITS_PER_SAMPLE] = max_bits_per_sample + if min_channels is not _UNDEF: + config[CONF_MIN_CHANNELS] = min_channels + if max_channels is not _UNDEF: + config[CONF_MAX_CHANNELS] = max_channels + if min_sample_rate is not _UNDEF: + config[CONF_MIN_SAMPLE_RATE] = min_sample_rate + if max_sample_rate is not _UNDEF: + config[CONF_MAX_SAMPLE_RATE] = max_sample_rate + + return set_limits_in_config + + +def final_validate_audio_schema( + name: str, + *, + audio_device: str, + bits_per_sample: int, + channels: int, + sample_rate: int, +): + def validate_audio_compatiblity(audio_config): + audio_schema = {} + + try: + cv.int_range( + min=audio_config.get(CONF_MIN_BITS_PER_SAMPLE), + max=audio_config.get(CONF_MAX_BITS_PER_SAMPLE), + )(bits_per_sample) + except cv.Invalid as exc: + raise cv.Invalid( + f"Invalid configuration for the {name} component. The {CONF_BITS_PER_SAMPLE} {str(exc)}" + ) from exc + + try: + cv.int_range( + min=audio_config.get(CONF_MIN_CHANNELS), + max=audio_config.get(CONF_MAX_CHANNELS), + )(channels) + except cv.Invalid as exc: + raise cv.Invalid( + f"Invalid configuration for the {name} component. The {CONF_NUM_CHANNELS} {str(exc)}" + ) from exc + + try: + cv.int_range( + min=audio_config.get(CONF_MIN_SAMPLE_RATE), + max=audio_config.get(CONF_MAX_SAMPLE_RATE), + )(sample_rate) + return cv.Schema(audio_schema, extra=cv.ALLOW_EXTRA)(audio_config) + except cv.Invalid as exc: + raise cv.Invalid( + f"Invalid configuration for the {name} component. The {CONF_SAMPLE_RATE} {str(exc)}" + ) from exc + + return cv.Schema( + { + cv.Required(audio_device): fv.id_declaration_match_schema( + validate_audio_compatiblity + ) + }, + extra=cv.ALLOW_EXTRA, + ) + + +async def to_code(config): + cg.add_library("esphome/esp-audio-libs", "1.1.1") diff --git a/esphome/components/audio/audio.cpp b/esphome/components/audio/audio.cpp new file mode 100644 index 0000000000..2a58c38ac7 --- /dev/null +++ b/esphome/components/audio/audio.cpp @@ -0,0 +1,67 @@ +#include "audio.h" + +namespace esphome { +namespace audio { + +// Euclidean's algorithm for finding the greatest common divisor +static uint32_t gcd(uint32_t a, uint32_t b) { + while (b != 0) { + uint32_t t = b; + b = a % b; + a = t; + } + return a; +} + +AudioStreamInfo::AudioStreamInfo(uint8_t bits_per_sample, uint8_t channels, uint32_t sample_rate) + : bits_per_sample_(bits_per_sample), channels_(channels), sample_rate_(sample_rate) { + this->ms_sample_rate_gcd_ = gcd(1000, this->sample_rate_); + this->bytes_per_sample_ = (this->bits_per_sample_ + 7) / 8; +} + +uint32_t AudioStreamInfo::frames_to_microseconds(uint32_t frames) const { + return (frames * 1000000 + (this->sample_rate_ >> 1)) / this->sample_rate_; +} + +uint32_t AudioStreamInfo::frames_to_milliseconds_with_remainder(uint32_t *total_frames) const { + uint32_t unprocessable_frames = *total_frames % (this->sample_rate_ / this->ms_sample_rate_gcd_); + uint32_t frames_for_ms_calculation = *total_frames - unprocessable_frames; + + uint32_t playback_ms = (frames_for_ms_calculation * 1000) / this->sample_rate_; + *total_frames = unprocessable_frames; + return playback_ms; +} + +bool AudioStreamInfo::operator==(const AudioStreamInfo &rhs) const { + return (this->bits_per_sample_ == rhs.get_bits_per_sample()) && (this->channels_ == rhs.get_channels()) && + (this->sample_rate_ == rhs.get_sample_rate()); +} + +const char *audio_file_type_to_string(AudioFileType file_type) { + switch (file_type) { +#ifdef USE_AUDIO_FLAC_SUPPORT + case AudioFileType::FLAC: + return "FLAC"; +#endif +#ifdef USE_AUDIO_MP3_SUPPORT + case AudioFileType::MP3: + return "MP3"; +#endif + case AudioFileType::WAV: + return "WAV"; + default: + return "unknown"; + } +} + +void scale_audio_samples(const int16_t *audio_samples, int16_t *output_buffer, int16_t scale_factor, + size_t samples_to_scale) { + // Note the assembly dsps_mulc function has audio glitches if the input and output buffers are the same. + for (int i = 0; i < samples_to_scale; i++) { + int32_t acc = (int32_t) audio_samples[i] * (int32_t) scale_factor; + output_buffer[i] = (int16_t) (acc >> 15); + } +} + +} // namespace audio +} // namespace esphome diff --git a/esphome/components/audio/audio.h b/esphome/components/audio/audio.h index caf325cf54..6f0f1aaa46 100644 --- a/esphome/components/audio/audio.h +++ b/esphome/components/audio/audio.h @@ -1,21 +1,139 @@ #pragma once +#include "esphome/core/defines.h" + #include #include namespace esphome { namespace audio { -struct AudioStreamInfo { - bool operator==(const AudioStreamInfo &rhs) const { - return (channels == rhs.channels) && (bits_per_sample == rhs.bits_per_sample) && (sample_rate == rhs.sample_rate); +class AudioStreamInfo { + /* Class to respresent important parameters of the audio stream that also provides helper function to convert between + * various audio related units. + * + * - An audio sample represents a unit of audio for one channel. + * - A frame represents a unit of audio with a sample for every channel. + * + * In gneneral, converting between bytes, samples, and frames shouldn't result in rounding errors so long as frames + * are used as the main unit when transferring audio data. Durations may result in rounding for certain sample rates; + * e.g., 44.1 KHz. The ``frames_to_milliseconds_with_remainder`` function should be used for accuracy, as it takes + * into account the remainder rather than just ignoring any rounding. + */ + public: + AudioStreamInfo() + : AudioStreamInfo(16, 1, 16000){}; // Default values represent ESPHome's audio components historical values + AudioStreamInfo(uint8_t bits_per_sample, uint8_t channels, uint32_t sample_rate); + + uint8_t get_bits_per_sample() const { return this->bits_per_sample_; } + uint8_t get_channels() const { return this->channels_; } + uint32_t get_sample_rate() const { return this->sample_rate_; } + + /// @brief Convert bytes to duration in milliseconds. + /// @param bytes Number of bytes to convert + /// @return Duration in milliseconds that will store `bytes` bytes of audio. May round down for certain sample rates + /// or values of `bytes`. + uint32_t bytes_to_ms(size_t bytes) const { + return bytes * 1000 / (this->sample_rate_ * this->bytes_per_sample_ * this->channels_); } + + /// @brief Convert bytes to frames. + /// @param bytes Number of bytes to convert + /// @return Audio frames that will store `bytes` bytes. + uint32_t bytes_to_frames(size_t bytes) const { return (bytes / (this->bytes_per_sample_ * this->channels_)); } + + /// @brief Convert bytes to samples. + /// @param bytes Number of bytes to convert + /// @return Audio samples that will store `bytes` bytes. + uint32_t bytes_to_samples(size_t bytes) const { return (bytes / this->bytes_per_sample_); } + + /// @brief Converts frames to bytes. + /// @param frames Number of frames to convert. + /// @return Number of bytes that will store `frames` frames of audio. + size_t frames_to_bytes(uint32_t frames) const { return frames * this->bytes_per_sample_ * this->channels_; } + + /// @brief Converts samples to bytes. + /// @param samples Number of samples to convert. + /// @return Number of bytes that will store `samples` samples of audio. + size_t samples_to_bytes(uint32_t samples) const { return samples * this->bytes_per_sample_; } + + /// @brief Converts duration to frames. + /// @param ms Duration in milliseconds + /// @return Audio frames that will store `ms` milliseconds of audio. May round down for certain sample rates. + uint32_t ms_to_frames(uint32_t ms) const { return (ms * this->sample_rate_) / 1000; } + + /// @brief Converts duration to samples. + /// @param ms Duration in milliseconds + /// @return Audio samples that will store `ms` milliseconds of audio. May round down for certain sample rates. + uint32_t ms_to_samples(uint32_t ms) const { return (ms * this->channels_ * this->sample_rate_) / 1000; } + + /// @brief Converts duration to bytes. May round down for certain sample rates. + /// @param ms Duration in milliseconds + /// @return Bytes that will store `ms` milliseconds of audio. May round down for certain sample rates. + size_t ms_to_bytes(uint32_t ms) const { + return (ms * this->bytes_per_sample_ * this->channels_ * this->sample_rate_) / 1000; + } + + /// @brief Computes the duration, in microseconds, the given amount of frames represents. + /// @param frames Number of audio frames + /// @return Duration in microseconds `frames` respresents. May be slightly inaccurate due to integer divison rounding + /// for certain sample rates. + uint32_t frames_to_microseconds(uint32_t frames) const; + + /// @brief Computes the duration, in milliseconds, the given amount of frames represents. Avoids + /// accumulating rounding errors by updating `frames` with the remainder after converting. + /// @param frames Pointer to uint32_t with the number of audio frames. Replaced with the remainder. + /// @return Duration in milliseconds `frames` represents. Always less than or equal to the actual value due to + /// rounding. + uint32_t frames_to_milliseconds_with_remainder(uint32_t *frames) const; + + // Class comparison operators + bool operator==(const AudioStreamInfo &rhs) const; bool operator!=(const AudioStreamInfo &rhs) const { return !operator==(rhs); } - size_t get_bytes_per_sample() const { return bits_per_sample / 8; } - uint8_t channels = 1; - uint8_t bits_per_sample = 16; - uint32_t sample_rate = 16000; + + protected: + uint8_t bits_per_sample_; + uint8_t channels_; + uint32_t sample_rate_; + + // The greatest common divisor between 1000 ms = 1 second and the sample rate. Used to avoid accumulating error when + // converting from frames to duration. Computed at construction. + uint32_t ms_sample_rate_gcd_; + + // Conversion factor derived from the number of bits per sample. Assumes audio data is aligned to the byte. Computed + // at construction. + size_t bytes_per_sample_; }; +enum class AudioFileType : uint8_t { + NONE = 0, +#ifdef USE_AUDIO_FLAC_SUPPORT + FLAC, +#endif +#ifdef USE_AUDIO_MP3_SUPPORT + MP3, +#endif + WAV, +}; + +struct AudioFile { + const uint8_t *data; + size_t length; + AudioFileType file_type; +}; + +/// @brief Helper function to convert file type to a const char string +/// @param file_type +/// @return const char pointer to the readable file type +const char *audio_file_type_to_string(AudioFileType file_type); + +/// @brief Scales Q15 fixed point audio samples. Scales in place if audio_samples == output_buffer. +/// @param audio_samples PCM int16 audio samples +/// @param output_buffer Buffer to store the scaled samples +/// @param scale_factor Q15 fixed point scaling factor +/// @param samples_to_scale Number of samples to scale +void scale_audio_samples(const int16_t *audio_samples, int16_t *output_buffer, int16_t scale_factor, + size_t samples_to_scale); + } // namespace audio } // namespace esphome diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index eb6bb329ad..aa3b50d336 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -1,20 +1,25 @@ from esphome import pins import esphome.codegen as cg -from esphome.components import esp32, speaker +from esphome.components import audio, esp32, speaker import esphome.config_validation as cv from esphome.const import ( + CONF_BITS_PER_SAMPLE, CONF_BUFFER_DURATION, CONF_CHANNEL, CONF_ID, CONF_MODE, CONF_NEVER, + CONF_NUM_CHANNELS, + CONF_SAMPLE_RATE, CONF_TIMEOUT, ) from .. import ( CONF_I2S_DOUT_PIN, + CONF_I2S_MODE, CONF_LEFT, CONF_MONO, + CONF_PRIMARY, CONF_RIGHT, CONF_STEREO, I2SAudioOut, @@ -58,7 +63,41 @@ I2C_COMM_FMT_OPTIONS = { NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2] -def validate_esp32_variant(config): +def _set_num_channels_from_config(config): + if config[CONF_CHANNEL] in (CONF_MONO, CONF_LEFT, CONF_RIGHT): + config[CONF_NUM_CHANNELS] = 1 + else: + config[CONF_NUM_CHANNELS] = 2 + + return config + + +def _set_stream_limits(config): + if config[CONF_I2S_MODE] == CONF_PRIMARY: + # Primary mode has modifiable stream settings + audio.set_stream_limits( + min_bits_per_sample=8, + max_bits_per_sample=32, + min_channels=1, + max_channels=2, + min_sample_rate=16000, + max_sample_rate=48000, + )(config) + else: + # Secondary mode has unmodifiable max bits per sample and min/max sample rates + audio.set_stream_limits( + min_bits_per_sample=8, + max_bits_per_sample=config.get(CONF_BITS_PER_SAMPLE), + min_channels=1, + max_channels=2, + min_sample_rate=config.get(CONF_SAMPLE_RATE), + max_sample_rate=config.get(CONF_SAMPLE_RATE), + ) + + return config + + +def _validate_esp32_variant(config): if config[CONF_DAC_TYPE] != "internal": return config variant = esp32.get_esp32_variant() @@ -90,6 +129,7 @@ BASE_SCHEMA = ( .extend(cv.COMPONENT_SCHEMA) ) + CONFIG_SCHEMA = cv.All( cv.typed_schema( { @@ -111,7 +151,9 @@ CONFIG_SCHEMA = cv.All( }, key=CONF_DAC_TYPE, ), - validate_esp32_variant, + _validate_esp32_variant, + _set_num_channels_from_config, + _set_stream_limits, ) diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 46f1b00d05..2a3fa9f5f3 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -148,9 +148,11 @@ void I2SAudioSpeaker::loop() { this->status_set_error("Failed to adjust I2S bus to match the incoming audio"); ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8, - this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels, - this->audio_stream_info_.bits_per_sample); + this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(), + this->audio_stream_info_.get_bits_per_sample()); } + + xEventGroupClearBits(this->event_group_, ALL_ERR_ESP_BITS); } void I2SAudioSpeaker::set_volume(float volume) { @@ -201,6 +203,12 @@ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t tick this->start(); } + if ((this->state_ != speaker::STATE_RUNNING) || (this->audio_ring_buffer_.use_count() == 1)) { + // Unable to write data to a running speaker, so delay the max amount of time so it can get ready + vTaskDelay(ticks_to_wait); + ticks_to_wait = 0; + } + size_t bytes_written = 0; if ((this->state_ == speaker::STATE_RUNNING) && (this->audio_ring_buffer_.use_count() == 1)) { // Only one owner of the ring buffer (the speaker task), so the ring buffer is allocated and no other components are @@ -223,6 +231,8 @@ bool I2SAudioSpeaker::has_buffered_data() const { void I2SAudioSpeaker::speaker_task(void *params) { I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params; + this_speaker->task_created_ = true; + uint32_t event_group_bits = xEventGroupWaitBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP | @@ -240,19 +250,20 @@ void I2SAudioSpeaker::speaker_task(void *params) { audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_; - const uint32_t bytes_per_ms = - audio_stream_info.channels * audio_stream_info.get_bytes_per_sample() * audio_stream_info.sample_rate / 1000; + const uint32_t dma_buffers_duration_ms = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT; + // Ensure ring buffer duration is at least the duration of all DMA buffers + const uint32_t ring_buffer_duration = std::max(dma_buffers_duration_ms, this_speaker->buffer_duration_ms_); - const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * bytes_per_ms; + // The DMA buffers may have more bits per sample, so calculate buffer sizes based in the input audio stream info + const size_t data_buffer_size = audio_stream_info.ms_to_bytes(dma_buffers_duration_ms); + const size_t ring_buffer_size = audio_stream_info.ms_to_bytes(ring_buffer_duration); - // Ensure ring buffer is at least as large as the total size of the DMA buffers - const size_t ring_buffer_size = - std::max((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms); + const size_t single_dma_buffer_input_size = data_buffer_size / DMA_BUFFERS_COUNT; - if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) { + if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(data_buffer_size, ring_buffer_size))) { // Failed to allocate buffers xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM); - this_speaker->delete_task_(dma_buffers_size); + this_speaker->delete_task_(data_buffer_size); } if (!this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_(audio_stream_info))) { @@ -262,20 +273,25 @@ void I2SAudioSpeaker::speaker_task(void *params) { uint32_t last_data_received_time = millis(); bool tx_dma_underflow = false; - while (!this_speaker->timeout_.has_value() || + this_speaker->accumulated_frames_written_ = 0; + + // Keep looping if paused, there is no timeout configured, or data was received more recently than the configured + // timeout + while (this_speaker->pause_state_ || !this_speaker->timeout_.has_value() || (millis() - last_data_received_time) <= this_speaker->timeout_.value()) { event_group_bits = xEventGroupGetBits(this_speaker->event_group_); if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) { + xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP); break; } if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY) { + xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY); stop_gracefully = true; } if (this_speaker->audio_stream_info_ != audio_stream_info) { - // Audio stream info has changed, stop the speaker task so it will restart with the proper settings. - + // Audio stream info changed, stop the speaker task so it will restart with the proper settings. break; } @@ -286,33 +302,64 @@ void I2SAudioSpeaker::speaker_task(void *params) { } } - size_t bytes_to_read = dma_buffers_size; - size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read, + if (this_speaker->pause_state_) { + // Pause state is accessed atomically, so thread safe + // Delay so the task can yields, then skip transferring audio data + delay(TASK_DELAY_MS); + continue; + } + + size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, data_buffer_size, pdMS_TO_TICKS(TASK_DELAY_MS)); if (bytes_read > 0) { - size_t bytes_written = 0; - - if ((audio_stream_info.bits_per_sample == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) { + if ((audio_stream_info.get_bits_per_sample() == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) { // Scale samples by the volume factor in place q15_multiplication((int16_t *) this_speaker->data_buffer_, (int16_t *) this_speaker->data_buffer_, bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_); } - if (audio_stream_info.bits_per_sample == (uint8_t) this_speaker->bits_per_sample_) { - i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read, &bytes_written, - portMAX_DELAY); - } else if (audio_stream_info.bits_per_sample < (uint8_t) this_speaker->bits_per_sample_) { - i2s_write_expand(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read, - audio_stream_info.bits_per_sample, this_speaker->bits_per_sample_, &bytes_written, - portMAX_DELAY); - } + // Write the audio data to a single DMA buffer at a time to reduce latency for the audio duration played + // callback. + const uint32_t batches = (bytes_read + single_dma_buffer_input_size - 1) / single_dma_buffer_input_size; - if (bytes_written != bytes_read) { - xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); + for (uint32_t i = 0; i < batches; ++i) { + size_t bytes_written = 0; + size_t bytes_to_write = std::min(single_dma_buffer_input_size, bytes_read); + + if (audio_stream_info.get_bits_per_sample() == (uint8_t) this_speaker->bits_per_sample_) { + i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_ + i * single_dma_buffer_input_size, + bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5)); + } else if (audio_stream_info.get_bits_per_sample() < (uint8_t) this_speaker->bits_per_sample_) { + i2s_write_expand(this_speaker->parent_->get_port(), + this_speaker->data_buffer_ + i * single_dma_buffer_input_size, bytes_to_write, + audio_stream_info.get_bits_per_sample(), this_speaker->bits_per_sample_, &bytes_written, + pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5)); + } + + uint32_t write_timestamp = micros(); + + if (bytes_written != bytes_to_write) { + xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE); + } + + bytes_read -= bytes_written; + + this_speaker->accumulated_frames_written_ += audio_stream_info.bytes_to_frames(bytes_written); + const uint32_t new_playback_ms = + audio_stream_info.frames_to_milliseconds_with_remainder(&this_speaker->accumulated_frames_written_); + const uint32_t remainder_us = + audio_stream_info.frames_to_microseconds(this_speaker->accumulated_frames_written_); + + uint32_t pending_frames = + audio_stream_info.bytes_to_frames(bytes_read + this_speaker->audio_ring_buffer_->available()); + const uint32_t pending_ms = audio_stream_info.frames_to_milliseconds_with_remainder(&pending_frames); + + this_speaker->audio_output_callback_(new_playback_ms, remainder_us, pending_ms, write_timestamp); + + tx_dma_underflow = false; + last_data_received_time = millis(); } - tx_dma_underflow = false; - last_data_received_time = millis(); } else { // No data received if (stop_gracefully && tx_dma_underflow) { @@ -328,7 +375,7 @@ void I2SAudioSpeaker::speaker_task(void *params) { this_speaker->parent_->unlock(); } - this_speaker->delete_task_(dma_buffers_size); + this_speaker->delete_task_(data_buffer_size); } void I2SAudioSpeaker::start() { @@ -337,16 +384,15 @@ void I2SAudioSpeaker::start() { if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING)) return; - if (this->speaker_task_handle_ == nullptr) { + if (!this->task_created_ && (this->speaker_task_handle_ == nullptr)) { xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY, &this->speaker_task_handle_); - } - if (this->speaker_task_handle_ != nullptr) { - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START); - this->task_created_ = true; - } else { - xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); + if (this->speaker_task_handle_ != nullptr) { + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START); + } else { + xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START); + } } } @@ -416,12 +462,12 @@ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t rin } esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) { - if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.sample_rate)) { // NOLINT - // Can't reconfigure I2S bus, so the sample rate must match the configured value + if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT + // Can't reconfigure I2S bus, so the sample rate must match the configured value return ESP_ERR_NOT_SUPPORTED; } - if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) { + if ((i2s_bits_per_sample_t) audio_stream_info.get_bits_per_sample() > this->bits_per_sample_) { // Currently can't handle the case when the incoming audio has more bits per sample than the configured value return ESP_ERR_NOT_SUPPORTED; } @@ -432,21 +478,21 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea i2s_channel_fmt_t channel = this->channel_; - if (audio_stream_info.channels == 1) { + if (audio_stream_info.get_channels() == 1) { if (this->channel_ == I2S_CHANNEL_FMT_ONLY_LEFT) { channel = I2S_CHANNEL_FMT_ONLY_LEFT; } else { channel = I2S_CHANNEL_FMT_ONLY_RIGHT; } - } else if (audio_stream_info.channels == 2) { + } else if (audio_stream_info.get_channels() == 2) { channel = I2S_CHANNEL_FMT_RIGHT_LEFT; } - int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000; + int dma_buffer_length = audio_stream_info.ms_to_frames(DMA_BUFFER_DURATION_MS); i2s_driver_config_t config = { .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX), - .sample_rate = audio_stream_info.sample_rate, + .sample_rate = audio_stream_info.get_sample_rate(), .bits_per_sample = this->bits_per_sample_, .channel_format = channel, .communication_format = this->i2s_comm_fmt_, @@ -504,7 +550,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea } void I2SAudioSpeaker::delete_task_(size_t buffer_size) { - this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr + this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr if (this->data_buffer_ != nullptr) { ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index d706deb0f4..7b14a57aac 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -40,6 +40,9 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp void stop() override; void finish() override; + void set_pause_state(bool pause_state) override { this->pause_state_ = pause_state; } + bool get_pause_state() const override { return this->pause_state_; } + /// @brief Plays the provided audio data. /// Starts the speaker task, if necessary. Writes the audio data to the ring buffer. /// @param data Audio data in the format set by the parent speaker classes ``set_audio_stream_info`` method. @@ -121,13 +124,18 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp uint8_t dout_pin_; bool task_created_{false}; + bool pause_state_{false}; int16_t q15_volume_factor_{INT16_MAX}; + size_t bytes_written_{0}; + #if SOC_I2S_SUPPORTS_DAC i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE}; #endif i2s_comm_format_t i2s_comm_fmt_; + + uint32_t accumulated_frames_written_{0}; }; } // namespace i2s_audio diff --git a/esphome/components/speaker/__init__.py b/esphome/components/speaker/__init__.py index 948fe4b534..2ac1ca0cb9 100644 --- a/esphome/components/speaker/__init__.py +++ b/esphome/components/speaker/__init__.py @@ -1,7 +1,6 @@ from esphome import automation -from esphome.automation import maybe_simple_id import esphome.codegen as cg -from esphome.components import audio_dac +from esphome.components import audio, audio_dac import esphome.config_validation as cv from esphome.const import CONF_DATA, CONF_ID, CONF_VOLUME from esphome.core import CORE @@ -54,13 +53,15 @@ async def register_speaker(var, config): await setup_speaker_core_(var, config) -SPEAKER_SCHEMA = cv.Schema( +SPEAKER_SCHEMA = cv.Schema.extend(audio.AUDIO_COMPONENT_SCHEMA).extend( { cv.Optional(CONF_AUDIO_DAC): cv.use_id(audio_dac.AudioDac), } ) -SPEAKER_AUTOMATION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(Speaker)}) +SPEAKER_AUTOMATION_SCHEMA = automation.maybe_simple_id( + {cv.GenerateID(): cv.use_id(Speaker)} +) async def speaker_action(config, action_id, template_arg, args): diff --git a/esphome/components/speaker/speaker.h b/esphome/components/speaker/speaker.h index 96843e2d5a..74c4822eca 100644 --- a/esphome/components/speaker/speaker.h +++ b/esphome/components/speaker/speaker.h @@ -9,6 +9,7 @@ #endif #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" #include "esphome/components/audio/audio.h" #ifdef USE_AUDIO_DAC @@ -56,6 +57,10 @@ class Speaker { // When finish() is not implemented on the platform component it should just do a normal stop. virtual void finish() { this->stop(); } + // Pauses processing incoming audio. Needs to be implemented specifically per speaker component + virtual void set_pause_state(bool pause_state) {} + virtual bool get_pause_state() const { return false; } + virtual bool has_buffered_data() const = 0; bool is_running() const { return this->state_ == STATE_RUNNING; } @@ -95,6 +100,19 @@ class Speaker { this->audio_stream_info_ = audio_stream_info; } + audio::AudioStreamInfo &get_audio_stream_info() { return this->audio_stream_info_; } + + /// Callback function for sending the duration of the audio written to the speaker since the last callback. + /// Parameters: + /// - Duration in milliseconds. Never rounded and should always be less than or equal to the actual duration. + /// - Remainder duration in microseconds. Rounded duration after subtracting the previous parameter from the actual + /// duration. + /// - Duration of remaining, unwritten audio buffered in the speaker in milliseconds. + /// - System time in microseconds when the last write was completed. + void add_audio_output_callback(std::function &&callback) { + this->audio_output_callback_.add(std::move(callback)); + } + protected: State state_{STATE_STOPPED}; audio::AudioStreamInfo audio_stream_info_; @@ -104,6 +122,8 @@ class Speaker { #ifdef USE_AUDIO_DAC audio_dac::AudioDac *audio_dac_{nullptr}; #endif + + CallbackManager audio_output_callback_{}; }; } // namespace speaker diff --git a/platformio.ini b/platformio.ini index e91c06d86e..cf11139b73 100644 --- a/platformio.ini +++ b/platformio.ini @@ -127,7 +127,8 @@ lib_deps = ESPmDNS ; mdns (Arduino built-in) DNSServer ; captive_portal (Arduino built-in) esphome/ESP32-audioI2S@2.0.7 ; i2s_audio - droscy/esp_wireguard@0.4.2 ; wireguard + droscy/esp_wireguard@0.4.2 ; wireguard + esphome/esp-audio-libs@1.1.1 ; audio build_flags = ${common:arduino.build_flags} @@ -148,6 +149,7 @@ lib_deps = ${common:idf.lib_deps} droscy/esp_wireguard@0.4.2 ; wireguard kahrendt/ESPMicroSpeechFeatures@1.1.0 ; micro_wake_word + esphome/esp-audio-libs@1.1.1 ; audio build_flags = ${common:idf.build_flags} -Wno-nonnull-compare From 6e5e6810559a4e8f8e8306bd8843609ae1a06ad9 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Sun, 2 Feb 2025 20:54:55 -0600 Subject: [PATCH 0942/1052] [audio] Media Player Components PR3 (#8165) --- .../audio/audio_transfer_buffer.cpp | 165 ++++++++++++++++++ .../components/audio/audio_transfer_buffer.h | 139 +++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 esphome/components/audio/audio_transfer_buffer.cpp create mode 100644 esphome/components/audio/audio_transfer_buffer.h diff --git a/esphome/components/audio/audio_transfer_buffer.cpp b/esphome/components/audio/audio_transfer_buffer.cpp new file mode 100644 index 0000000000..9b6067aac4 --- /dev/null +++ b/esphome/components/audio/audio_transfer_buffer.cpp @@ -0,0 +1,165 @@ +#include "audio_transfer_buffer.h" + +#ifdef USE_ESP32 + +#include "esphome/core/helpers.h" + +namespace esphome { +namespace audio { + +AudioTransferBuffer::~AudioTransferBuffer() { this->deallocate_buffer_(); }; + +std::unique_ptr AudioSinkTransferBuffer::create(size_t buffer_size) { + std::unique_ptr sink_buffer = make_unique(); + + if (!sink_buffer->allocate_buffer_(buffer_size)) { + return nullptr; + } + + return sink_buffer; +} + +std::unique_ptr AudioSourceTransferBuffer::create(size_t buffer_size) { + std::unique_ptr source_buffer = make_unique(); + + if (!source_buffer->allocate_buffer_(buffer_size)) { + return nullptr; + } + + return source_buffer; +} + +size_t AudioTransferBuffer::free() const { + if (this->buffer_size_ == 0) { + return 0; + } + return this->buffer_size_ - (this->buffer_length_ - (this->data_start_ - this->buffer_)); +} + +void AudioTransferBuffer::decrease_buffer_length(size_t bytes) { + this->buffer_length_ -= bytes; + this->data_start_ += bytes; +} + +void AudioTransferBuffer::increase_buffer_length(size_t bytes) { this->buffer_length_ += bytes; } + +void AudioTransferBuffer::clear_buffered_data() { + this->buffer_length_ = 0; + if (this->ring_buffer_.use_count() > 0) { + this->ring_buffer_->reset(); + } +} + +void AudioSinkTransferBuffer::clear_buffered_data() { + this->buffer_length_ = 0; + if (this->ring_buffer_.use_count() > 0) { + this->ring_buffer_->reset(); + } +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + this->speaker_->stop(); + } +#endif +} + +bool AudioTransferBuffer::has_buffered_data() const { + if (this->ring_buffer_.use_count() > 0) { + return ((this->ring_buffer_->available() > 0) || (this->available() > 0)); + } + return (this->available() > 0); +} + +bool AudioTransferBuffer::reallocate(size_t new_buffer_size) { + if (this->buffer_length_ > 0) { + // Already has data in the buffer, fail + return false; + } + this->deallocate_buffer_(); + return this->allocate_buffer_(new_buffer_size); +} + +bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) { + this->buffer_size_ = buffer_size; + + RAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + + this->buffer_ = allocator.allocate(this->buffer_size_); + if (this->buffer_ == nullptr) { + return false; + } + + this->data_start_ = this->buffer_; + this->buffer_length_ = 0; + + return true; +} + +void AudioTransferBuffer::deallocate_buffer_() { + if (this->buffer_ != nullptr) { + RAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + allocator.deallocate(this->buffer_, this->buffer_size_); + this->buffer_ = nullptr; + this->data_start_ = nullptr; + } + + this->buffer_size_ = 0; + this->buffer_length_ = 0; +} + +size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_wait) { + // Shift data in buffer to start + if (this->buffer_length_ > 0) { + memmove(this->buffer_, this->data_start_, this->buffer_length_); + } + this->data_start_ = this->buffer_; + + size_t bytes_to_read = this->free(); + size_t bytes_read = 0; + if (bytes_to_read > 0) { + if (this->ring_buffer_.use_count() > 0) { + bytes_read = this->ring_buffer_->read((void *) this->get_buffer_end(), bytes_to_read, ticks_to_wait); + } + + this->increase_buffer_length(bytes_read); + } + return bytes_read; +} + +size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait) { + size_t bytes_written = 0; + if (this->available()) { +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + bytes_written = this->speaker_->play(this->data_start_, this->available(), ticks_to_wait); + } else +#endif + if (this->ring_buffer_.use_count() > 0) { + bytes_written = + this->ring_buffer_->write_without_replacement((void *) this->data_start_, this->available(), ticks_to_wait); + } + + this->decrease_buffer_length(bytes_written); + + // Shift unwritten data to the start of the buffer + memmove(this->buffer_, this->data_start_, this->buffer_length_); + this->data_start_ = this->buffer_; + } + return bytes_written; +} + +bool AudioSinkTransferBuffer::has_buffered_data() const { +#ifdef USE_SPEAKER + if (this->speaker_ != nullptr) { + return (this->speaker_->has_buffered_data() || (this->available() > 0)); + } +#endif + if (this->ring_buffer_.use_count() > 0) { + return ((this->ring_buffer_->available() > 0) || (this->available() > 0)); + } + return (this->available() > 0); +} + +} // namespace audio +} // namespace esphome + +#endif diff --git a/esphome/components/audio/audio_transfer_buffer.h b/esphome/components/audio/audio_transfer_buffer.h new file mode 100644 index 0000000000..4e461db56d --- /dev/null +++ b/esphome/components/audio/audio_transfer_buffer.h @@ -0,0 +1,139 @@ +#pragma once + +#ifdef USE_ESP32 +#include "esphome/core/defines.h" +#include "esphome/core/ring_buffer.h" + +#ifdef USE_SPEAKER +#include "esphome/components/speaker/speaker.h" +#endif + +#include "esp_err.h" + +#include + +namespace esphome { +namespace audio { + +class AudioTransferBuffer { + /* + * @brief Class that facilitates tranferring data between a buffer and an audio source or sink. + * The transfer buffer is a typical C array that temporarily holds data for processing in other audio components. + * Both sink and source transfer buffers can use a ring buffer as the sink/source. + * - The ring buffer is stored in a shared_ptr, so destroying the transfer buffer object will release ownership. + */ + public: + /// @brief Destructor that deallocates the transfer buffer + ~AudioTransferBuffer(); + + /// @brief Returns a pointer to the start of the transfer buffer where available() bytes of exisiting data can be read + uint8_t *get_buffer_start() const { return this->data_start_; } + + /// @brief Returns a pointer to the end of the transfer buffer where free() bytes of new data can be written + uint8_t *get_buffer_end() const { return this->data_start_ + this->buffer_length_; } + + /// @brief Updates the internal state of the transfer buffer. This should be called after reading data + /// @param bytes The number of bytes consumed/read + void decrease_buffer_length(size_t bytes); + + /// @brief Updates the internal state of the transfer buffer. This should be called after writing data + /// @param bytes The number of bytes written + void increase_buffer_length(size_t bytes); + + /// @brief Returns the transfer buffer's currently available bytes to read + size_t available() const { return this->buffer_length_; } + + /// @brief Returns the transfer buffers allocated bytes + size_t capacity() const { return this->buffer_size_; } + + /// @brief Returns the transfer buffer's currrently free bytes available to write + size_t free() const; + + /// @brief Clears data in the transfer buffer and, if possible, the source/sink. + virtual void clear_buffered_data(); + + /// @brief Tests if there is any data in the tranfer buffer or the source/sink. + /// @return True if there is data, false otherwise. + virtual bool has_buffered_data() const; + + bool reallocate(size_t new_buffer_size); + + protected: + /// @brief Allocates the transfer buffer in external memory, if available. + /// @return True is successful, false otherwise. + bool allocate_buffer_(size_t buffer_size); + + /// @brief Deallocates the buffer and resets the class variables. + void deallocate_buffer_(); + + // A possible source or sink for the transfer buffer + std::shared_ptr ring_buffer_; + + uint8_t *buffer_{nullptr}; + uint8_t *data_start_{nullptr}; + + size_t buffer_size_{0}; + size_t buffer_length_{0}; +}; + +class AudioSinkTransferBuffer : public AudioTransferBuffer { + /* + * @brief A class that implements a transfer buffer for audio sinks. + * Supports writing processed data in the transfer buffer to a ring buffer or a speaker component. + */ + public: + /// @brief Creates a new sink transfer buffer. + /// @param buffer_size Size of the transfer buffer in bytes. + /// @return unique_ptr if successfully allocated, nullptr otherwise + static std::unique_ptr create(size_t buffer_size); + + /// @brief Writes any available data in the transfer buffer to the sink. + /// @param ticks_to_wait FreeRTOS ticks to block while waiting for the sink to have enough space + /// @return Number of bytes written + size_t transfer_data_to_sink(TickType_t ticks_to_wait); + + /// @brief Adds a ring buffer as the transfer buffer's sink. + /// @param ring_buffer weak_ptr to the allocated ring buffer + void set_sink(const std::weak_ptr &ring_buffer) { this->ring_buffer_ = ring_buffer.lock(); } + +#ifdef USE_SPEAKER + /// @brief Adds a speaker as the transfer buffer's sink. + /// @param speaker Pointer to the speaker component + void set_sink(speaker::Speaker *speaker) { this->speaker_ = speaker; } +#endif + + void clear_buffered_data() override; + + bool has_buffered_data() const override; + + protected: +#ifdef USE_SPEAKER + speaker::Speaker *speaker_{nullptr}; +#endif +}; + +class AudioSourceTransferBuffer : public AudioTransferBuffer { + /* + * @brief A class that implements a transfer buffer for audio sources. + * Supports reading audio data from a ring buffer into the transfer buffer for processing. + */ + public: + /// @brief Creates a new source transfer buffer. + /// @param buffer_size Size of the transfer buffer in bytes. + /// @return unique_ptr if successfully allocated, nullptr otherwise + static std::unique_ptr create(size_t buffer_size); + + /// @brief Reads any available data from the sink into the transfer buffer. + /// @param ticks_to_wait FreeRTOS ticks to block while waiting for the source to have enough data + /// @return Number of bytes read + size_t transfer_data_from_source(TickType_t ticks_to_wait); + + /// @brief Adds a ring buffer as the transfer buffer's source. + /// @param ring_buffer weak_ptr to the allocated ring buffer + void set_source(const std::weak_ptr &ring_buffer) { this->ring_buffer_ = ring_buffer.lock(); }; +}; + +} // namespace audio +} // namespace esphome + +#endif From 8de5af4eecaddee52fe6416668c7bf27cf884f87 Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Mon, 3 Feb 2025 04:55:55 +0100 Subject: [PATCH 0943/1052] Add virtual get_flags() to GPIOPin and implementation in InternalGPIOPin derivatives (#8151) --- esphome/components/esp32/gpio.h | 2 ++ esphome/components/esp8266/gpio.h | 1 + esphome/components/host/gpio.h | 1 + esphome/components/libretiny/gpio_arduino.h | 1 + esphome/components/rp2040/gpio.h | 1 + esphome/core/gpio.h | 10 ++++++++++ 6 files changed, 16 insertions(+) diff --git a/esphome/components/esp32/gpio.h b/esphome/components/esp32/gpio.h index 23b723e0b4..d69ac1c493 100644 --- a/esphome/components/esp32/gpio.h +++ b/esphome/components/esp32/gpio.h @@ -13,6 +13,7 @@ class ESP32InternalGPIOPin : public InternalGPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_drive_strength(gpio_drive_cap_t drive_strength) { drive_strength_ = drive_strength; } void set_flags(gpio::Flags flags) { flags_ = flags; } + void setup() override; void pin_mode(gpio::Flags flags) override; bool digital_read() override; @@ -21,6 +22,7 @@ class ESP32InternalGPIOPin : public InternalGPIOPin { void detach_interrupt() const override; ISRInternalGPIOPin to_isr() const override; uint8_t get_pin() const override { return (uint8_t) pin_; } + gpio::Flags get_flags() const override { return flags_; } bool is_inverted() const override { return inverted_; } protected: diff --git a/esphome/components/esp8266/gpio.h b/esphome/components/esp8266/gpio.h index 0474d0baa6..dd6407885e 100644 --- a/esphome/components/esp8266/gpio.h +++ b/esphome/components/esp8266/gpio.h @@ -22,6 +22,7 @@ class ESP8266GPIOPin : public InternalGPIOPin { void detach_interrupt() const override; ISRInternalGPIOPin to_isr() const override; uint8_t get_pin() const override { return pin_; } + gpio::Flags get_flags() const override { return flags_; } bool is_inverted() const override { return inverted_; } protected: diff --git a/esphome/components/host/gpio.h b/esphome/components/host/gpio.h index c0920467d6..a60d535912 100644 --- a/esphome/components/host/gpio.h +++ b/esphome/components/host/gpio.h @@ -21,6 +21,7 @@ class HostGPIOPin : public InternalGPIOPin { void detach_interrupt() const override; ISRInternalGPIOPin to_isr() const override; uint8_t get_pin() const override { return pin_; } + gpio::Flags get_flags() const override { return flags_; } bool is_inverted() const override { return inverted_; } protected: diff --git a/esphome/components/libretiny/gpio_arduino.h b/esphome/components/libretiny/gpio_arduino.h index a43ed28c5e..9adc425a41 100644 --- a/esphome/components/libretiny/gpio_arduino.h +++ b/esphome/components/libretiny/gpio_arduino.h @@ -20,6 +20,7 @@ class ArduinoInternalGPIOPin : public InternalGPIOPin { void detach_interrupt() const override; ISRInternalGPIOPin to_isr() const override; uint8_t get_pin() const override { return pin_; } + gpio::Flags get_flags() const override { return flags_; } bool is_inverted() const override { return inverted_; } protected: diff --git a/esphome/components/rp2040/gpio.h b/esphome/components/rp2040/gpio.h index ef9500d5dd..9bc66d9e4b 100644 --- a/esphome/components/rp2040/gpio.h +++ b/esphome/components/rp2040/gpio.h @@ -22,6 +22,7 @@ class RP2040GPIOPin : public InternalGPIOPin { void detach_interrupt() const override; ISRInternalGPIOPin to_isr() const override; uint8_t get_pin() const override { return pin_; } + gpio::Flags get_flags() const override { return flags_; } bool is_inverted() const override { return inverted_; } protected: diff --git a/esphome/core/gpio.h b/esphome/core/gpio.h index 1b6f2ba1e6..19d57a0af8 100644 --- a/esphome/core/gpio.h +++ b/esphome/core/gpio.h @@ -53,6 +53,16 @@ class GPIOPin { virtual void pin_mode(gpio::Flags flags) = 0; + /** + * @brief Retrieve GPIO pin flags. + * + * @note This is currently optional to limit changes but will be mandatory in a future update. + * It is primarily applied to internal pins for now. + * + * @return The GPIO flags describing the pin mode and properties. Returns `gpio::Flags::FLAG_NONE` if not overridden. + */ + virtual gpio::Flags get_flags() const { return gpio::Flags::FLAG_NONE; } + virtual bool digital_read() = 0; virtual void digital_write(bool value) = 0; From 5108b9a8b7ca7bc3b541fd743d81821c47a6c000 Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Mon, 3 Feb 2025 18:14:55 +0100 Subject: [PATCH 0944/1052] Make get_flags() in GPIOPin mandatory (#8182) Co-authored-by: Keith Burzinski --- esphome/components/ch422g/ch422g.h | 2 ++ esphome/components/max6956/max6956.h | 2 ++ esphome/components/mcp23016/mcp23016.h | 2 ++ esphome/components/mcp23xxx_base/mcp23xxx_base.h | 2 ++ esphome/components/mpr121/mpr121.h | 2 ++ esphome/components/pca6416a/pca6416a.h | 2 ++ esphome/components/pca9554/pca9554.h | 2 ++ esphome/components/pcf8574/pcf8574.h | 2 ++ esphome/components/sn74hc165/sn74hc165.h | 3 +++ esphome/components/sn74hc595/sn74hc595.h | 3 +++ esphome/components/spi/spi.h | 2 ++ esphome/components/sx1509/sx1509_gpio_pin.h | 2 ++ esphome/components/tca9555/tca9555.h | 2 ++ esphome/components/weikai/weikai.h | 2 ++ esphome/components/xl9535/xl9535.h | 2 ++ esphome/core/gpio.h | 7 ++----- 16 files changed, 34 insertions(+), 5 deletions(-) diff --git a/esphome/components/ch422g/ch422g.h b/esphome/components/ch422g/ch422g.h index 30780e09ad..1193a3db27 100644 --- a/esphome/components/ch422g/ch422g.h +++ b/esphome/components/ch422g/ch422g.h @@ -57,6 +57,8 @@ class CH422GGPIOPin : public GPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_flags(gpio::Flags flags); + gpio::Flags get_flags() const override { return this->flags_; } + protected: CH422GComponent *parent_{}; uint8_t pin_{}; diff --git a/esphome/components/max6956/max6956.h b/esphome/components/max6956/max6956.h index 759fa45b07..0a1fd5e4b5 100644 --- a/esphome/components/max6956/max6956.h +++ b/esphome/components/max6956/max6956.h @@ -83,6 +83,8 @@ class MAX6956GPIOPin : public GPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_flags(gpio::Flags flags) { flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: MAX6956 *parent_; uint8_t pin_; diff --git a/esphome/components/mcp23016/mcp23016.h b/esphome/components/mcp23016/mcp23016.h index a4890b4120..e4ed47a3b2 100644 --- a/esphome/components/mcp23016/mcp23016.h +++ b/esphome/components/mcp23016/mcp23016.h @@ -61,6 +61,8 @@ class MCP23016GPIOPin : public GPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_flags(gpio::Flags flags) { flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: MCP23016 *parent_; uint8_t pin_; diff --git a/esphome/components/mcp23xxx_base/mcp23xxx_base.h b/esphome/components/mcp23xxx_base/mcp23xxx_base.h index a522ea28c5..9686c9fd33 100644 --- a/esphome/components/mcp23xxx_base/mcp23xxx_base.h +++ b/esphome/components/mcp23xxx_base/mcp23xxx_base.h @@ -43,6 +43,8 @@ class MCP23XXXGPIOPin : public GPIOPin { void set_flags(gpio::Flags flags) { flags_ = flags; } void set_interrupt_mode(MCP23XXXInterruptMode interrupt_mode) { interrupt_mode_ = interrupt_mode; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: MCP23XXXBase *parent_; uint8_t pin_; diff --git a/esphome/components/mpr121/mpr121.h b/esphome/components/mpr121/mpr121.h index f2dc2fe9c9..eb2e2edc57 100644 --- a/esphome/components/mpr121/mpr121.h +++ b/esphome/components/mpr121/mpr121.h @@ -117,6 +117,8 @@ class MPR121GPIOPin : public GPIOPin { void set_inverted(bool inverted) { this->inverted_ = inverted; } void set_flags(gpio::Flags flags) { this->flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: MPR121Component *parent_; uint8_t pin_; diff --git a/esphome/components/pca6416a/pca6416a.h b/esphome/components/pca6416a/pca6416a.h index 247f443e87..1e8015c40a 100644 --- a/esphome/components/pca6416a/pca6416a.h +++ b/esphome/components/pca6416a/pca6416a.h @@ -52,6 +52,8 @@ class PCA6416AGPIOPin : public GPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_flags(gpio::Flags flags) { flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: PCA6416AComponent *parent_; uint8_t pin_; diff --git a/esphome/components/pca9554/pca9554.h b/esphome/components/pca9554/pca9554.h index c548bec619..efeec4d306 100644 --- a/esphome/components/pca9554/pca9554.h +++ b/esphome/components/pca9554/pca9554.h @@ -65,6 +65,8 @@ class PCA9554GPIOPin : public GPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_flags(gpio::Flags flags) { flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: PCA9554Component *parent_; uint8_t pin_; diff --git a/esphome/components/pcf8574/pcf8574.h b/esphome/components/pcf8574/pcf8574.h index c201e0615f..6edc67fc96 100644 --- a/esphome/components/pcf8574/pcf8574.h +++ b/esphome/components/pcf8574/pcf8574.h @@ -54,6 +54,8 @@ class PCF8574GPIOPin : public GPIOPin { void set_inverted(bool inverted) { inverted_ = inverted; } void set_flags(gpio::Flags flags) { flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: PCF8574Component *parent_; uint8_t pin_; diff --git a/esphome/components/sn74hc165/sn74hc165.h b/esphome/components/sn74hc165/sn74hc165.h index c349d079ae..4684844687 100644 --- a/esphome/components/sn74hc165/sn74hc165.h +++ b/esphome/components/sn74hc165/sn74hc165.h @@ -52,6 +52,9 @@ class SN74HC165GPIOPin : public GPIOPin, public Parented { void set_pin(uint16_t pin) { pin_ = pin; } void set_inverted(bool inverted) { inverted_ = inverted; } + /// Always returns `gpio::Flags::FLAG_INPUT`. + gpio::Flags get_flags() const override { return gpio::Flags::FLAG_INPUT; } + protected: uint16_t pin_; bool inverted_; diff --git a/esphome/components/sn74hc595/sn74hc595.h b/esphome/components/sn74hc595/sn74hc595.h index cb9d7bf140..181015b1e6 100644 --- a/esphome/components/sn74hc595/sn74hc595.h +++ b/esphome/components/sn74hc595/sn74hc595.h @@ -59,6 +59,9 @@ class SN74HC595GPIOPin : public GPIOPin, public Parented { void set_pin(uint16_t pin) { pin_ = pin; } void set_inverted(bool inverted) { inverted_ = inverted; } + /// Always returns `gpio::Flags::FLAG_OUTPUT`. + gpio::Flags get_flags() const override { return gpio::Flags::FLAG_OUTPUT; } + protected: uint16_t pin_; bool inverted_; diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index f581dc3f56..64463747a2 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -114,6 +114,8 @@ class NullPin : public GPIOPin { void pin_mode(gpio::Flags flags) override {} + gpio::Flags get_flags() const override { return gpio::Flags::FLAG_NONE; } + bool digital_read() override { return false; } void digital_write(bool value) override {} diff --git a/esphome/components/sx1509/sx1509_gpio_pin.h b/esphome/components/sx1509/sx1509_gpio_pin.h index 1cfa341ee7..eb9207e882 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.h +++ b/esphome/components/sx1509/sx1509_gpio_pin.h @@ -20,6 +20,8 @@ class SX1509GPIOPin : public GPIOPin { void set_inverted(bool inverted) { this->inverted_ = inverted; } void set_flags(gpio::Flags flags) { this->flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: SX1509Component *parent_; uint8_t pin_; diff --git a/esphome/components/tca9555/tca9555.h b/esphome/components/tca9555/tca9555.h index ea464db043..0c236ae4e3 100644 --- a/esphome/components/tca9555/tca9555.h +++ b/esphome/components/tca9555/tca9555.h @@ -54,6 +54,8 @@ class TCA9555GPIOPin : public GPIOPin, public Parented { void set_inverted(bool inverted) { this->inverted_ = inverted; } void set_flags(gpio::Flags flags) { this->flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + protected: uint8_t pin_; bool inverted_; diff --git a/esphome/components/weikai/weikai.h b/esphome/components/weikai/weikai.h index 175a067b27..987278213a 100644 --- a/esphome/components/weikai/weikai.h +++ b/esphome/components/weikai/weikai.h @@ -275,6 +275,8 @@ class WeikaiGPIOPin : public GPIOPin { void set_inverted(bool inverted) { this->inverted_ = inverted; } void set_flags(gpio::Flags flags) { this->flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + void setup() override; std::string dump_summary() const override; void pin_mode(gpio::Flags flags) override { this->parent_->set_pin_direction_(this->pin_, flags); } diff --git a/esphome/components/xl9535/xl9535.h b/esphome/components/xl9535/xl9535.h index dd67990fa8..3b511fd9b3 100644 --- a/esphome/components/xl9535/xl9535.h +++ b/esphome/components/xl9535/xl9535.h @@ -36,6 +36,8 @@ class XL9535GPIOPin : public GPIOPin { void set_inverted(bool inverted) { this->inverted_ = inverted; } void set_flags(gpio::Flags flags) { this->flags_ = flags; } + gpio::Flags get_flags() const override { return this->flags_; } + void setup() override; std::string dump_summary() const override; void pin_mode(gpio::Flags flags) override; diff --git a/esphome/core/gpio.h b/esphome/core/gpio.h index 19d57a0af8..dd6f14fef9 100644 --- a/esphome/core/gpio.h +++ b/esphome/core/gpio.h @@ -56,12 +56,9 @@ class GPIOPin { /** * @brief Retrieve GPIO pin flags. * - * @note This is currently optional to limit changes but will be mandatory in a future update. - * It is primarily applied to internal pins for now. - * - * @return The GPIO flags describing the pin mode and properties. Returns `gpio::Flags::FLAG_NONE` if not overridden. + * @return The GPIO flags describing the pin mode and properties. */ - virtual gpio::Flags get_flags() const { return gpio::Flags::FLAG_NONE; } + virtual gpio::Flags get_flags() const = 0; virtual bool digital_read() = 0; From c8bbc2e84c2eb53c58ec43f0e69a2a4501f8afab Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Mon, 3 Feb 2025 16:34:20 -0600 Subject: [PATCH 0945/1052] [audio] Media Player Components PR4 (#8166) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/audio/audio_reader.cpp | 308 ++++++++++++++++++++++ esphome/components/audio/audio_reader.h | 85 ++++++ 2 files changed, 393 insertions(+) create mode 100644 esphome/components/audio/audio_reader.cpp create mode 100644 esphome/components/audio/audio_reader.h diff --git a/esphome/components/audio/audio_reader.cpp b/esphome/components/audio/audio_reader.cpp new file mode 100644 index 0000000000..b93e4e74ea --- /dev/null +++ b/esphome/components/audio/audio_reader.cpp @@ -0,0 +1,308 @@ +#include "audio_reader.h" + +#ifdef USE_ESP_IDF + +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" + +#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE +#include "esp_crt_bundle.h" +#endif + +namespace esphome { +namespace audio { + +static const uint32_t READ_WRITE_TIMEOUT_MS = 20; + +// The number of times the http read times out with no data before throwing an error +static const uint32_t ERROR_COUNT_NO_DATA_READ_TIMEOUT = 100; + +static const size_t HTTP_STREAM_BUFFER_SIZE = 2048; + +static const uint8_t MAX_REDIRECTION = 5; + +// Some common HTTP status codes - borrowed from http_request component accessed 20241224 +enum HttpStatus { + HTTP_STATUS_OK = 200, + HTTP_STATUS_NO_CONTENT = 204, + HTTP_STATUS_PARTIAL_CONTENT = 206, + + /* 3xx - Redirection */ + HTTP_STATUS_MULTIPLE_CHOICES = 300, + HTTP_STATUS_MOVED_PERMANENTLY = 301, + HTTP_STATUS_FOUND = 302, + HTTP_STATUS_SEE_OTHER = 303, + HTTP_STATUS_NOT_MODIFIED = 304, + HTTP_STATUS_TEMPORARY_REDIRECT = 307, + HTTP_STATUS_PERMANENT_REDIRECT = 308, + + /* 4XX - CLIENT ERROR */ + HTTP_STATUS_BAD_REQUEST = 400, + HTTP_STATUS_UNAUTHORIZED = 401, + HTTP_STATUS_FORBIDDEN = 403, + HTTP_STATUS_NOT_FOUND = 404, + HTTP_STATUS_METHOD_NOT_ALLOWED = 405, + HTTP_STATUS_NOT_ACCEPTABLE = 406, + HTTP_STATUS_LENGTH_REQUIRED = 411, + + /* 5xx - Server Error */ + HTTP_STATUS_INTERNAL_ERROR = 500 +}; + +AudioReader::~AudioReader() { this->cleanup_connection_(); } + +esp_err_t AudioReader::add_sink(const std::weak_ptr &output_ring_buffer) { + if (current_audio_file_ != nullptr) { + // A transfer buffer isn't ncessary for a local file + this->file_ring_buffer_ = output_ring_buffer.lock(); + return ESP_OK; + } + + if (this->output_transfer_buffer_ != nullptr) { + this->output_transfer_buffer_->set_sink(output_ring_buffer); + return ESP_OK; + } + + return ESP_ERR_INVALID_STATE; +} + +esp_err_t AudioReader::start(AudioFile *audio_file, AudioFileType &file_type) { + file_type = AudioFileType::NONE; + + this->current_audio_file_ = audio_file; + + this->file_current_ = audio_file->data; + file_type = audio_file->file_type; + + return ESP_OK; +} + +esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) { + file_type = AudioFileType::NONE; + + this->cleanup_connection_(); + + if (uri.empty()) { + return ESP_ERR_INVALID_ARG; + } + + esp_http_client_config_t client_config = {}; + + client_config.url = uri.c_str(); + client_config.cert_pem = nullptr; + client_config.disable_auto_redirect = false; + client_config.max_redirection_count = 10; + client_config.event_handler = http_event_handler; + client_config.user_data = this; + client_config.buffer_size = HTTP_STREAM_BUFFER_SIZE; + client_config.keep_alive_enable = true; + client_config.timeout_ms = 5000; // Shouldn't trigger watchdog resets if caller runs in a task + +#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE + if (uri.find("https:") != std::string::npos) { + client_config.crt_bundle_attach = esp_crt_bundle_attach; + } +#endif + + this->client_ = esp_http_client_init(&client_config); + + if (this->client_ == nullptr) { + return ESP_FAIL; + } + + esp_err_t err = esp_http_client_open(this->client_, 0); + + if (err != ESP_OK) { + this->cleanup_connection_(); + return err; + } + + int64_t header_length = esp_http_client_fetch_headers(this->client_); + if (header_length < 0) { + this->cleanup_connection_(); + return ESP_FAIL; + } + + int status_code = esp_http_client_get_status_code(this->client_); + + if ((status_code < HTTP_STATUS_OK) || (status_code > HTTP_STATUS_PERMANENT_REDIRECT)) { + this->cleanup_connection_(); + return ESP_FAIL; + } + + ssize_t redirect_count = 0; + + while ((esp_http_client_set_redirection(this->client_) == ESP_OK) && (redirect_count < MAX_REDIRECTION)) { + err = esp_http_client_open(this->client_, 0); + if (err != ESP_OK) { + this->cleanup_connection_(); + return ESP_FAIL; + } + + header_length = esp_http_client_fetch_headers(this->client_); + if (header_length < 0) { + this->cleanup_connection_(); + return ESP_FAIL; + } + + status_code = esp_http_client_get_status_code(this->client_); + + if ((status_code < HTTP_STATUS_OK) || (status_code > HTTP_STATUS_PERMANENT_REDIRECT)) { + this->cleanup_connection_(); + return ESP_FAIL; + } + + ++redirect_count; + } + + if (this->audio_file_type_ == AudioFileType::NONE) { + // Failed to determine the file type from the header, fallback to using the url + char url[500]; + err = esp_http_client_get_url(this->client_, url, 500); + if (err != ESP_OK) { + this->cleanup_connection_(); + return err; + } + + std::string url_string = str_lower_case(url); + + if (str_endswith(url_string, ".wav")) { + file_type = AudioFileType::WAV; + } +#ifdef USE_AUDIO_MP3_SUPPORT + else if (str_endswith(url_string, ".mp3")) { + file_type = AudioFileType::MP3; + } +#endif +#ifdef USE_AUDIO_FLAC_SUPPORT + else if (str_endswith(url_string, ".flac")) { + file_type = AudioFileType::FLAC; + } +#endif + else { + file_type = AudioFileType::NONE; + this->cleanup_connection_(); + return ESP_ERR_NOT_SUPPORTED; + } + } else { + file_type = this->audio_file_type_; + } + + this->no_data_read_count_ = 0; + + this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(this->buffer_size_); + if (this->output_transfer_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + return ESP_OK; +} + +AudioReaderState AudioReader::read() { + if (this->client_ != nullptr) { + return this->http_read_(); + } else if (this->current_audio_file_ != nullptr) { + return this->file_read_(); + } + + return AudioReaderState::FAILED; +} + +AudioFileType AudioReader::get_audio_type(const char *content_type) { +#ifdef USE_AUDIO_MP3_SUPPORT + if (strcasecmp(content_type, "mp3") == 0 || strcasecmp(content_type, "audio/mp3") == 0 || + strcasecmp(content_type, "audio/mpeg") == 0) { + return AudioFileType::MP3; + } +#endif + if (strcasecmp(content_type, "audio/wav") == 0) { + return AudioFileType::WAV; + } +#ifdef USE_AUDIO_FLAC_SUPPORT + if (strcasecmp(content_type, "audio/flac") == 0 || strcasecmp(content_type, "audio/x-flac") == 0) { + return AudioFileType::FLAC; + } +#endif + return AudioFileType::NONE; +} + +esp_err_t AudioReader::http_event_handler(esp_http_client_event_t *evt) { + // Based on https://github.com/maroc81/WeatherLily/tree/main/main/net accessed 20241224 + AudioReader *this_reader = (AudioReader *) evt->user_data; + + switch (evt->event_id) { + case HTTP_EVENT_ON_HEADER: + if (strcasecmp(evt->header_key, "Content-Type") == 0) { + this_reader->audio_file_type_ = get_audio_type(evt->header_value); + } + break; + default: + break; + } + return ESP_OK; +} + +AudioReaderState AudioReader::file_read_() { + size_t remaining_bytes = this->current_audio_file_->length - (this->file_current_ - this->current_audio_file_->data); + if (remaining_bytes > 0) { + size_t bytes_written = this->file_ring_buffer_->write_without_replacement(this->file_current_, remaining_bytes, + pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + this->file_current_ += bytes_written; + + return AudioReaderState::READING; + } + + return AudioReaderState::FINISHED; +} + +AudioReaderState AudioReader::http_read_() { + this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + + if (esp_http_client_is_complete_data_received(this->client_)) { + if (this->output_transfer_buffer_->available() == 0) { + this->cleanup_connection_(); + return AudioReaderState::FINISHED; + } + } else { + size_t bytes_to_read = this->output_transfer_buffer_->free(); + int received_len = + esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(), bytes_to_read); + + if (received_len > 0) { + this->output_transfer_buffer_->increase_buffer_length(received_len); + + this->no_data_read_count_ = 0; + } else if (received_len < 0) { + // HTTP read error + this->cleanup_connection_(); + return AudioReaderState::FAILED; + } else { + if (bytes_to_read > 0) { + // Read timed out + ++this->no_data_read_count_; + if (this->no_data_read_count_ >= ERROR_COUNT_NO_DATA_READ_TIMEOUT) { + // Timed out with no data read too many times, so the http read has failed + this->cleanup_connection_(); + return AudioReaderState::FAILED; + } + delay(READ_WRITE_TIMEOUT_MS); + } + } + } + + return AudioReaderState::READING; +} + +void AudioReader::cleanup_connection_() { + if (this->client_ != nullptr) { + esp_http_client_close(this->client_); + esp_http_client_cleanup(this->client_); + this->client_ = nullptr; + } +} + +} // namespace audio +} // namespace esphome + +#endif diff --git a/esphome/components/audio/audio_reader.h b/esphome/components/audio/audio_reader.h new file mode 100644 index 0000000000..90113e6dda --- /dev/null +++ b/esphome/components/audio/audio_reader.h @@ -0,0 +1,85 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include "audio.h" +#include "audio_transfer_buffer.h" + +#include "esphome/core/ring_buffer.h" + +#include "esp_err.h" + +#include + +namespace esphome { +namespace audio { + +enum class AudioReaderState : uint8_t { + READING = 0, // More data is available to read + FINISHED, // All data has been read and transferred + FAILED, // Encountered an error +}; + +class AudioReader { + /* + * @brief Class that facilitates reading a raw audio file. + * Files can be read from flash (stored in a AudioFile struct) or from an http source. + * The file data is sent to a ring buffer sink. + */ + public: + /// @brief Constructs an AudioReader object. + /// The transfer buffer isn't allocated here, but only if necessary (an http source) in the start function. + /// @param buffer_size Transfer buffer size in bytes. + AudioReader(size_t buffer_size) : buffer_size_(buffer_size) {} + ~AudioReader(); + + /// @brief Adds a sink ring buffer for audio data. Takes ownership of the ring buffer in a shared_ptr + /// @param output_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership + /// @return ESP_OK if successful, ESP_ERR_INVALID_STATE otherwise + esp_err_t add_sink(const std::weak_ptr &output_ring_buffer); + + /// @brief Starts reading an audio file from an http source. The transfer buffer is allocated here. + /// @param uri Web url to the http file. + /// @param file_type AudioFileType variable passed-by-reference indicating the type of file being read. + /// @return ESP_OK if successful, an ESP_ERR* code otherwise. + esp_err_t start(const std::string &uri, AudioFileType &file_type); + + /// @brief Starts reading an audio file from flash. No transfer buffer is allocated. + /// @param audio_file AudioFile struct containing the file. + /// @param file_type AudioFileType variable passed-by-reference indicating the type of file being read. + /// @return ESP_OK + esp_err_t start(AudioFile *audio_file, AudioFileType &file_type); + + /// @brief Reads new file data from the source and sends to the ring buffer sink. + /// @return AudioReaderState + AudioReaderState read(); + + protected: + /// @brief Monitors the http client events to attempt determining the file type from the Content-Type header + static esp_err_t http_event_handler(esp_http_client_event_t *evt); + + /// @brief Determines the audio file type from the http header's Content-Type key + /// @param content_type string with the Content-Type key + /// @return AudioFileType of the url, if it can be determined. If not, return AudioFileType::NONE. + static AudioFileType get_audio_type(const char *content_type); + + AudioReaderState file_read_(); + AudioReaderState http_read_(); + + std::shared_ptr file_ring_buffer_; + std::unique_ptr output_transfer_buffer_; + void cleanup_connection_(); + + size_t buffer_size_; + uint32_t no_data_read_count_; + + esp_http_client_handle_t client_{nullptr}; + + AudioFile *current_audio_file_{nullptr}; + AudioFileType audio_file_type_{AudioFileType::NONE}; + const uint8_t *file_current_{nullptr}; +}; +} // namespace audio +} // namespace esphome + +#endif From b8f9eaecd85fc2b9e6b3086f030b6fe76d94c656 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Mon, 3 Feb 2025 17:47:50 -0600 Subject: [PATCH 0946/1052] [audio] Media Player Components PR5 (#8167) --- esphome/components/audio/audio_decoder.cpp | 362 +++++++++++++++++++++ esphome/components/audio/audio_decoder.h | 135 ++++++++ 2 files changed, 497 insertions(+) create mode 100644 esphome/components/audio/audio_decoder.cpp create mode 100644 esphome/components/audio/audio_decoder.h diff --git a/esphome/components/audio/audio_decoder.cpp b/esphome/components/audio/audio_decoder.cpp new file mode 100644 index 0000000000..b249f1381d --- /dev/null +++ b/esphome/components/audio/audio_decoder.cpp @@ -0,0 +1,362 @@ +#include "audio_decoder.h" + +#ifdef USE_ESP32 + +#include "esphome/core/hal.h" + +namespace esphome { +namespace audio { + +static const uint32_t DECODING_TIMEOUT_MS = 50; // The decode function will yield after this duration +static const uint32_t READ_WRITE_TIMEOUT_MS = 20; // Timeout for transferring audio data + +static const uint32_t MAX_POTENTIALLY_FAILED_COUNT = 10; + +AudioDecoder::AudioDecoder(size_t input_buffer_size, size_t output_buffer_size) { + this->input_transfer_buffer_ = AudioSourceTransferBuffer::create(input_buffer_size); + this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(output_buffer_size); +} + +AudioDecoder::~AudioDecoder() { +#ifdef USE_AUDIO_MP3_SUPPORT + if (this->audio_file_type_ == AudioFileType::MP3) { + esp_audio_libs::helix_decoder::MP3FreeDecoder(this->mp3_decoder_); + } +#endif +} + +esp_err_t AudioDecoder::add_source(std::weak_ptr &input_ring_buffer) { + if (this->input_transfer_buffer_ != nullptr) { + this->input_transfer_buffer_->set_source(input_ring_buffer); + return ESP_OK; + } + return ESP_ERR_NO_MEM; +} + +esp_err_t AudioDecoder::add_sink(std::weak_ptr &output_ring_buffer) { + if (this->output_transfer_buffer_ != nullptr) { + this->output_transfer_buffer_->set_sink(output_ring_buffer); + return ESP_OK; + } + return ESP_ERR_NO_MEM; +} + +#ifdef USE_SPEAKER +esp_err_t AudioDecoder::add_sink(speaker::Speaker *speaker) { + if (this->output_transfer_buffer_ != nullptr) { + this->output_transfer_buffer_->set_sink(speaker); + return ESP_OK; + } + return ESP_ERR_NO_MEM; +} +#endif + +esp_err_t AudioDecoder::start(AudioFileType audio_file_type) { + if ((this->input_transfer_buffer_ == nullptr) || (this->output_transfer_buffer_ == nullptr)) { + return ESP_ERR_NO_MEM; + } + + this->audio_file_type_ = audio_file_type; + + this->potentially_failed_count_ = 0; + this->end_of_file_ = false; + + switch (this->audio_file_type_) { +#ifdef USE_AUDIO_FLAC_SUPPORT + case AudioFileType::FLAC: + this->flac_decoder_ = make_unique(); + this->free_buffer_required_ = + this->output_transfer_buffer_->capacity(); // We'll revise this after reading the header + break; +#endif +#ifdef USE_AUDIO_MP3_SUPPORT + case AudioFileType::MP3: + this->mp3_decoder_ = esp_audio_libs::helix_decoder::MP3InitDecoder(); + this->free_buffer_required_ = 1152 * sizeof(int16_t) * 2; // samples * size per sample * channels + break; +#endif + case AudioFileType::WAV: + this->wav_decoder_ = make_unique(); + this->wav_decoder_->reset(); + this->free_buffer_required_ = 1024; + break; + case AudioFileType::NONE: + default: + return ESP_ERR_NOT_SUPPORTED; + break; + } + + return ESP_OK; +} + +AudioDecoderState AudioDecoder::decode(bool stop_gracefully) { + if (stop_gracefully) { + if (this->output_transfer_buffer_->available() == 0) { + if (this->end_of_file_) { + // The file decoder indicates it reached the end of file + return AudioDecoderState::FINISHED; + } + + if (!this->input_transfer_buffer_->has_buffered_data()) { + // If all the internal buffers are empty, the decoding is done + return AudioDecoderState::FINISHED; + } + } + } + + if (this->potentially_failed_count_ > MAX_POTENTIALLY_FAILED_COUNT) { + if (stop_gracefully) { + // No more new data is going to come in, so decoding is done + return AudioDecoderState::FINISHED; + } + return AudioDecoderState::FAILED; + } + + FileDecoderState state = FileDecoderState::MORE_TO_PROCESS; + + uint32_t decoding_start = millis(); + + while (state == FileDecoderState::MORE_TO_PROCESS) { + // Transfer decoded out + if (!this->pause_output_) { + size_t bytes_written = this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + if (this->audio_stream_info_.has_value()) { + this->accumulated_frames_written_ += this->audio_stream_info_.value().bytes_to_frames(bytes_written); + this->playback_ms_ += + this->audio_stream_info_.value().frames_to_milliseconds_with_remainder(&this->accumulated_frames_written_); + } + } else { + // If paused, block to avoid wasting CPU resources + delay(READ_WRITE_TIMEOUT_MS); + } + + // Verify there is enough space to store more decoded audio and that the function hasn't been running too long + if ((this->output_transfer_buffer_->free() < this->free_buffer_required_) || + (millis() - decoding_start > DECODING_TIMEOUT_MS)) { + return AudioDecoderState::DECODING; + } + + // Decode more audio + + size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + + if ((this->potentially_failed_count_ > 0) && (bytes_read == 0)) { + // Failed to decode in last attempt and there is no new data + + if (this->input_transfer_buffer_->free() == 0) { + // The input buffer is full. Since it previously failed on the exact same data, we can never recover + state = FileDecoderState::FAILED; + } else { + // Attempt to get more data next time + state = FileDecoderState::IDLE; + } + } else if (this->input_transfer_buffer_->available() == 0) { + // No data to decode, attempt to get more data next time + state = FileDecoderState::IDLE; + } else { + switch (this->audio_file_type_) { +#ifdef USE_AUDIO_FLAC_SUPPORT + case AudioFileType::FLAC: + state = this->decode_flac_(); + break; +#endif +#ifdef USE_AUDIO_MP3_SUPPORT + case AudioFileType::MP3: + state = this->decode_mp3_(); + break; +#endif + case AudioFileType::WAV: + state = this->decode_wav_(); + break; + case AudioFileType::NONE: + default: + state = FileDecoderState::IDLE; + break; + } + } + + if (state == FileDecoderState::POTENTIALLY_FAILED) { + ++this->potentially_failed_count_; + } else if (state == FileDecoderState::END_OF_FILE) { + this->end_of_file_ = true; + } else if (state == FileDecoderState::FAILED) { + return AudioDecoderState::FAILED; + } else if (state == FileDecoderState::MORE_TO_PROCESS) { + this->potentially_failed_count_ = 0; + } + } + return AudioDecoderState::DECODING; +} + +#ifdef USE_AUDIO_FLAC_SUPPORT +FileDecoderState AudioDecoder::decode_flac_() { + if (!this->audio_stream_info_.has_value()) { + // Header hasn't been read + auto result = this->flac_decoder_->read_header(this->input_transfer_buffer_->get_buffer_start(), + this->input_transfer_buffer_->available()); + + if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) { + return FileDecoderState::POTENTIALLY_FAILED; + } + + if (result != esp_audio_libs::flac::FLAC_DECODER_SUCCESS) { + // Couldn't read FLAC header + return FileDecoderState::FAILED; + } + + size_t bytes_consumed = this->flac_decoder_->get_bytes_index(); + this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed); + + this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes(); + if (this->output_transfer_buffer_->capacity() < this->free_buffer_required_) { + // Output buffer is not big enough + if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) { + // Couldn't reallocate output buffer + return FileDecoderState::FAILED; + } + } + + this->audio_stream_info_ = + audio::AudioStreamInfo(this->flac_decoder_->get_sample_depth(), this->flac_decoder_->get_num_channels(), + this->flac_decoder_->get_sample_rate()); + + return FileDecoderState::MORE_TO_PROCESS; + } + + uint32_t output_samples = 0; + auto result = this->flac_decoder_->decode_frame( + this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available(), + reinterpret_cast(this->output_transfer_buffer_->get_buffer_end()), &output_samples); + + if (result == esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) { + // Not an issue, just needs more data that we'll get next time. + return FileDecoderState::POTENTIALLY_FAILED; + } + + size_t bytes_consumed = this->flac_decoder_->get_bytes_index(); + this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed); + + if (result > esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) { + // Corrupted frame, don't retry with current buffer content, wait for new sync + return FileDecoderState::POTENTIALLY_FAILED; + } + + // We have successfully decoded some input data and have new output data + this->output_transfer_buffer_->increase_buffer_length( + this->audio_stream_info_.value().samples_to_bytes(output_samples)); + + if (result == esp_audio_libs::flac::FLAC_DECODER_NO_MORE_FRAMES) { + return FileDecoderState::END_OF_FILE; + } + + return FileDecoderState::MORE_TO_PROCESS; +} +#endif + +#ifdef USE_AUDIO_MP3_SUPPORT +FileDecoderState AudioDecoder::decode_mp3_() { + // Look for the next sync word + int buffer_length = (int) this->input_transfer_buffer_->available(); + int32_t offset = + esp_audio_libs::helix_decoder::MP3FindSyncWord(this->input_transfer_buffer_->get_buffer_start(), buffer_length); + + if (offset < 0) { + // New data may have the sync word + this->input_transfer_buffer_->decrease_buffer_length(buffer_length); + return FileDecoderState::POTENTIALLY_FAILED; + } + + // Advance read pointer to match the offset for the syncword + this->input_transfer_buffer_->decrease_buffer_length(offset); + uint8_t *buffer_start = this->input_transfer_buffer_->get_buffer_start(); + + buffer_length = (int) this->input_transfer_buffer_->available(); + int err = esp_audio_libs::helix_decoder::MP3Decode(this->mp3_decoder_, &buffer_start, &buffer_length, + (int16_t *) this->output_transfer_buffer_->get_buffer_end(), 0); + + size_t consumed = this->input_transfer_buffer_->available() - buffer_length; + this->input_transfer_buffer_->decrease_buffer_length(consumed); + + if (err) { + switch (err) { + case esp_audio_libs::helix_decoder::ERR_MP3_OUT_OF_MEMORY: + return FileDecoderState::FAILED; + break; + case esp_audio_libs::helix_decoder::ERR_MP3_NULL_POINTER: + return FileDecoderState::FAILED; + break; + default: + // Most errors are recoverable by moving on to the next frame, so mark as potentailly failed + return FileDecoderState::POTENTIALLY_FAILED; + break; + } + } else { + esp_audio_libs::helix_decoder::MP3FrameInfo mp3_frame_info; + esp_audio_libs::helix_decoder::MP3GetLastFrameInfo(this->mp3_decoder_, &mp3_frame_info); + if (mp3_frame_info.outputSamps > 0) { + int bytes_per_sample = (mp3_frame_info.bitsPerSample / 8); + this->output_transfer_buffer_->increase_buffer_length(mp3_frame_info.outputSamps * bytes_per_sample); + + if (!this->audio_stream_info_.has_value()) { + this->audio_stream_info_ = + audio::AudioStreamInfo(mp3_frame_info.bitsPerSample, mp3_frame_info.nChans, mp3_frame_info.samprate); + } + } + } + + return FileDecoderState::MORE_TO_PROCESS; +} +#endif + +FileDecoderState AudioDecoder::decode_wav_() { + if (!this->audio_stream_info_.has_value()) { + // Header hasn't been processed + + esp_audio_libs::wav_decoder::WAVDecoderResult result = this->wav_decoder_->decode_header( + this->input_transfer_buffer_->get_buffer_start(), this->input_transfer_buffer_->available()); + + if (result == esp_audio_libs::wav_decoder::WAV_DECODER_SUCCESS_IN_DATA) { + this->input_transfer_buffer_->decrease_buffer_length(this->wav_decoder_->bytes_processed()); + + this->audio_stream_info_ = audio::AudioStreamInfo( + this->wav_decoder_->bits_per_sample(), this->wav_decoder_->num_channels(), this->wav_decoder_->sample_rate()); + + this->wav_bytes_left_ = this->wav_decoder_->chunk_bytes_left(); + this->wav_has_known_end_ = (this->wav_bytes_left_ > 0); + return FileDecoderState::MORE_TO_PROCESS; + } else if (result == esp_audio_libs::wav_decoder::WAV_DECODER_WARNING_INCOMPLETE_DATA) { + // Available data didn't have the full header + return FileDecoderState::POTENTIALLY_FAILED; + } else { + return FileDecoderState::FAILED; + } + } else { + if (!this->wav_has_known_end_ || (this->wav_bytes_left_ > 0)) { + size_t bytes_to_copy = this->input_transfer_buffer_->available(); + + if (this->wav_has_known_end_) { + bytes_to_copy = std::min(bytes_to_copy, this->wav_bytes_left_); + } + + bytes_to_copy = std::min(bytes_to_copy, this->output_transfer_buffer_->free()); + + if (bytes_to_copy > 0) { + std::memcpy(this->output_transfer_buffer_->get_buffer_end(), this->input_transfer_buffer_->get_buffer_start(), + bytes_to_copy); + this->input_transfer_buffer_->decrease_buffer_length(bytes_to_copy); + this->output_transfer_buffer_->increase_buffer_length(bytes_to_copy); + if (this->wav_has_known_end_) { + this->wav_bytes_left_ -= bytes_to_copy; + } + } + return FileDecoderState::IDLE; + } + } + + return FileDecoderState::END_OF_FILE; +} + +} // namespace audio +} // namespace esphome + +#endif diff --git a/esphome/components/audio/audio_decoder.h b/esphome/components/audio/audio_decoder.h new file mode 100644 index 0000000000..2ca1d623fe --- /dev/null +++ b/esphome/components/audio/audio_decoder.h @@ -0,0 +1,135 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "audio.h" +#include "audio_transfer_buffer.h" + +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/ring_buffer.h" + +#ifdef USE_SPEAKER +#include "esphome/components/speaker/speaker.h" +#endif + +#include "esp_err.h" + +// esp-audio-libs +#ifdef USE_AUDIO_FLAC_SUPPORT +#include +#endif +#ifdef USE_AUDIO_MP3_SUPPORT +#include +#endif +#include + +namespace esphome { +namespace audio { + +enum class AudioDecoderState : uint8_t { + DECODING = 0, // More data is available to decode + FINISHED, // All file data has been decoded and transferred + FAILED, // Encountered an error +}; + +// Only used within the AudioDecoder class; conveys the state of the particular file type decoder +enum class FileDecoderState : uint8_t { + MORE_TO_PROCESS, // Successsfully read a file chunk and more data is available to decode + IDLE, // Not enough data to decode, waiting for more to be transferred + POTENTIALLY_FAILED, // Decoder encountered a potentially recoverable error if more file data is available + FAILED, // Decoder encoutnered an uncrecoverable error + END_OF_FILE, // The specific file decoder knows its the end of the file +}; + +class AudioDecoder { + /* + * @brief Class that facilitates decoding an audio file. + * The audio file is read from a ring buffer source, decoded, and sent to an audio sink (ring buffer or speaker + * component). + * Supports wav, flac, and mp3 formats. + */ + public: + /// @brief Allocates the input and output transfer buffers + /// @param input_buffer_size Size of the input transfer buffer in bytes. + /// @param output_buffer_size Size of the output transfer buffer in bytes. + AudioDecoder(size_t input_buffer_size, size_t output_buffer_size); + + /// @brief Deallocates the MP3 decoder (the flac and wav decoders are deallocated automatically) + ~AudioDecoder(); + + /// @brief Adds a source ring buffer for raw file data. Takes ownership of the ring buffer in a shared_ptr. + /// @param input_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership + /// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated + esp_err_t add_source(std::weak_ptr &input_ring_buffer); + + /// @brief Adds a sink ring buffer for decoded audio. Takes ownership of the ring buffer in a shared_ptr. + /// @param output_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership + /// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated + esp_err_t add_sink(std::weak_ptr &output_ring_buffer); + +#ifdef USE_SPEAKER + /// @brief Adds a sink speaker for decoded audio. + /// @param speaker pointer to speaker component + /// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated + esp_err_t add_sink(speaker::Speaker *speaker); +#endif + + /// @brief Sets up decoding the file + /// @param audio_file_type AudioFileType of the file + /// @return ESP_OK if successful, ESP_ERR_NO_MEM if the transfer buffers fail to allocate, or ESP_ERR_NOT_SUPPORTED if + /// the format isn't supported. + esp_err_t start(AudioFileType audio_file_type); + + /// @brief Decodes audio from the ring buffer source and writes to the sink. + /// @param stop_gracefully If true, it indicates the file source is finished. The decoder will decode all the + /// reamining data and then finish. + /// @return AudioDecoderState + AudioDecoderState decode(bool stop_gracefully); + + /// @brief Gets the audio stream information, if it has been decoded from the files header + /// @return optional with the audio information. If not available yet, returns no value. + const optional &get_audio_stream_info() const { return this->audio_stream_info_; } + + /// @brief Returns the duration of audio (in milliseconds) decoded and sent to the sink + /// @return Duration of decoded audio in milliseconds + uint32_t get_playback_ms() const { return this->playback_ms_; } + + /// @brief Pauses sending resampled audio to the sink. If paused, it will continue to process internal buffers. + /// @param pause_state If true, audio data is not sent to the sink. + void set_pause_output_state(bool pause_state) { this->pause_output_ = pause_state; } + + protected: + std::unique_ptr wav_decoder_; +#ifdef USE_AUDIO_FLAC_SUPPORT + FileDecoderState decode_flac_(); + std::unique_ptr flac_decoder_; +#endif +#ifdef USE_AUDIO_MP3_SUPPORT + FileDecoderState decode_mp3_(); + esp_audio_libs::helix_decoder::HMP3Decoder mp3_decoder_; +#endif + FileDecoderState decode_wav_(); + + std::unique_ptr input_transfer_buffer_; + std::unique_ptr output_transfer_buffer_; + + AudioFileType audio_file_type_{AudioFileType::NONE}; + optional audio_stream_info_{}; + + size_t free_buffer_required_{0}; + size_t wav_bytes_left_{0}; + + uint32_t potentially_failed_count_{0}; + bool end_of_file_{false}; + bool wav_has_known_end_{false}; + + bool pause_output_{false}; + + uint32_t accumulated_frames_written_{0}; + uint32_t playback_ms_{0}; +}; +} // namespace audio +} // namespace esphome + +#endif From 6b55df36c7e21997d69c4688ee1627e545596087 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Mon, 3 Feb 2025 20:58:35 -0600 Subject: [PATCH 0947/1052] [audio] Media Player Components PR6 (#8168) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/audio/audio_resampler.cpp | 159 +++++++++++++++++++ esphome/components/audio/audio_resampler.h | 100 ++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 esphome/components/audio/audio_resampler.cpp create mode 100644 esphome/components/audio/audio_resampler.h diff --git a/esphome/components/audio/audio_resampler.cpp b/esphome/components/audio/audio_resampler.cpp new file mode 100644 index 0000000000..05e9ff6ca1 --- /dev/null +++ b/esphome/components/audio/audio_resampler.cpp @@ -0,0 +1,159 @@ +#include "audio_resampler.h" + +#ifdef USE_ESP32 + +#include "esphome/core/hal.h" + +namespace esphome { +namespace audio { + +static const uint32_t READ_WRITE_TIMEOUT_MS = 20; + +AudioResampler::AudioResampler(size_t input_buffer_size, size_t output_buffer_size) + : input_buffer_size_(input_buffer_size), output_buffer_size_(output_buffer_size) { + this->input_transfer_buffer_ = AudioSourceTransferBuffer::create(input_buffer_size); + this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(output_buffer_size); +} + +esp_err_t AudioResampler::add_source(std::weak_ptr &input_ring_buffer) { + if (this->input_transfer_buffer_ != nullptr) { + this->input_transfer_buffer_->set_source(input_ring_buffer); + return ESP_OK; + } + return ESP_ERR_NO_MEM; +} + +esp_err_t AudioResampler::add_sink(std::weak_ptr &output_ring_buffer) { + if (this->output_transfer_buffer_ != nullptr) { + this->output_transfer_buffer_->set_sink(output_ring_buffer); + return ESP_OK; + } + return ESP_ERR_NO_MEM; +} + +#ifdef USE_SPEAKER +esp_err_t AudioResampler::add_sink(speaker::Speaker *speaker) { + if (this->output_transfer_buffer_ != nullptr) { + this->output_transfer_buffer_->set_sink(speaker); + return ESP_OK; + } + return ESP_ERR_NO_MEM; +} +#endif + +esp_err_t AudioResampler::start(AudioStreamInfo &input_stream_info, AudioStreamInfo &output_stream_info, + uint16_t number_of_taps, uint16_t number_of_filters) { + this->input_stream_info_ = input_stream_info; + this->output_stream_info_ = output_stream_info; + + if ((this->input_transfer_buffer_ == nullptr) || (this->output_transfer_buffer_ == nullptr)) { + return ESP_ERR_NO_MEM; + } + + if ((input_stream_info.get_bits_per_sample() > 32) || (output_stream_info.get_bits_per_sample() > 32) || + (input_stream_info_.get_channels() != output_stream_info.get_channels())) { + return ESP_ERR_NOT_SUPPORTED; + } + + if ((input_stream_info.get_sample_rate() != output_stream_info.get_sample_rate()) || + (input_stream_info.get_bits_per_sample() != output_stream_info.get_bits_per_sample())) { + this->resampler_ = make_unique( + input_stream_info.bytes_to_samples(this->input_buffer_size_), + output_stream_info.bytes_to_samples(this->output_buffer_size_)); + + // Use cascaded biquad filters when downsampling to avoid aliasing + bool use_pre_filter = output_stream_info.get_sample_rate() < input_stream_info.get_sample_rate(); + + esp_audio_libs::resampler::ResamplerConfiguration resample_config = { + .source_sample_rate = static_cast(input_stream_info.get_sample_rate()), + .target_sample_rate = static_cast(output_stream_info.get_sample_rate()), + .source_bits_per_sample = input_stream_info.get_bits_per_sample(), + .target_bits_per_sample = output_stream_info.get_bits_per_sample(), + .channels = input_stream_info_.get_channels(), + .use_pre_or_post_filter = use_pre_filter, + .subsample_interpolate = false, // Doubles the CPU load. Using more filters is a better alternative + .number_of_taps = number_of_taps, + .number_of_filters = number_of_filters, + }; + + if (!this->resampler_->initialize(resample_config)) { + // Failed to allocate the resampler's internal buffers + return ESP_ERR_NO_MEM; + } + } + + return ESP_OK; +} + +AudioResamplerState AudioResampler::resample(bool stop_gracefully, int32_t *ms_differential) { + if (stop_gracefully) { + if (!this->input_transfer_buffer_->has_buffered_data() && (this->output_transfer_buffer_->available() == 0)) { + return AudioResamplerState::FINISHED; + } + } + + if (!this->pause_output_) { + // Move audio data to the sink + this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + } else { + // If paused, block to avoid wasting CPU resources + delay(READ_WRITE_TIMEOUT_MS); + } + + this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); + + if (this->input_transfer_buffer_->available() == 0) { + // No samples available to process + return AudioResamplerState::RESAMPLING; + } + + const size_t bytes_free = this->output_transfer_buffer_->free(); + const uint32_t frames_free = this->output_stream_info_.bytes_to_frames(bytes_free); + + const size_t bytes_available = this->input_transfer_buffer_->available(); + const uint32_t frames_available = this->input_stream_info_.bytes_to_frames(bytes_available); + + if ((this->input_stream_info_.get_sample_rate() != this->output_stream_info_.get_sample_rate()) || + (this->input_stream_info_.get_bits_per_sample() != this->output_stream_info_.get_bits_per_sample())) { + esp_audio_libs::resampler::ResamplerResults results = + this->resampler_->resample(this->input_transfer_buffer_->get_buffer_start(), + this->output_transfer_buffer_->get_buffer_end(), frames_available, frames_free, -3); + + this->input_transfer_buffer_->decrease_buffer_length(this->input_stream_info_.frames_to_bytes(results.frames_used)); + this->output_transfer_buffer_->increase_buffer_length( + this->output_stream_info_.frames_to_bytes(results.frames_generated)); + + // Resampling causes slight differences in the durations used versus generated. Computes the difference in + // millisconds. The callback function passing the played audio duration uses the difference to convert from output + // duration to input duration. + this->accumulated_frames_used_ += results.frames_used; + this->accumulated_frames_generated_ += results.frames_generated; + + const int32_t used_ms = + this->input_stream_info_.frames_to_milliseconds_with_remainder(&this->accumulated_frames_used_); + const int32_t generated_ms = + this->output_stream_info_.frames_to_milliseconds_with_remainder(&this->accumulated_frames_generated_); + + *ms_differential = used_ms - generated_ms; + + } else { + // No resampling required, copy samples directly to the output transfer buffer + *ms_differential = 0; + + const size_t bytes_to_transfer = std::min(this->output_stream_info_.frames_to_bytes(frames_free), + this->input_stream_info_.frames_to_bytes(frames_available)); + + std::memcpy((void *) this->output_transfer_buffer_->get_buffer_end(), + (void *) this->input_transfer_buffer_->get_buffer_start(), bytes_to_transfer); + + this->input_transfer_buffer_->decrease_buffer_length(bytes_to_transfer); + this->output_transfer_buffer_->increase_buffer_length(bytes_to_transfer); + } + + return AudioResamplerState::RESAMPLING; +} + +} // namespace audio +} // namespace esphome + +#endif diff --git a/esphome/components/audio/audio_resampler.h b/esphome/components/audio/audio_resampler.h new file mode 100644 index 0000000000..a348aaf783 --- /dev/null +++ b/esphome/components/audio/audio_resampler.h @@ -0,0 +1,100 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "audio.h" +#include "audio_transfer_buffer.h" + +#ifdef USE_SPEAKER +#include "esphome/components/speaker/speaker.h" +#endif + +#include "esphome/core/ring_buffer.h" + +#include "esp_err.h" + +#include // esp-audio-libs + +namespace esphome { +namespace audio { + +enum class AudioResamplerState : uint8_t { + RESAMPLING, // More data is available to resample + FINISHED, // All file data has been resampled and transferred + FAILED, // Unused state included for consistency among Audio classes +}; + +class AudioResampler { + /* + * @brief Class that facilitates resampling audio. + * The audio data is read from a ring buffer source, resampled, and sent to an audio sink (ring buffer or speaker + * component). Also supports converting bits per sample. + */ + public: + /// @brief Allocates the input and output transfer buffers + /// @param input_buffer_size Size of the input transfer buffer in bytes. + /// @param output_buffer_size Size of the output transfer buffer in bytes. + AudioResampler(size_t input_buffer_size, size_t output_buffer_size); + + /// @brief Adds a source ring buffer for audio data. Takes ownership of the ring buffer in a shared_ptr. + /// @param input_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership + /// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated + esp_err_t add_source(std::weak_ptr &input_ring_buffer); + + /// @brief Adds a sink ring buffer for resampled audio. Takes ownership of the ring buffer in a shared_ptr. + /// @param output_ring_buffer weak_ptr of a shared_ptr of the sink ring buffer to transfer ownership + /// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated + esp_err_t add_sink(std::weak_ptr &output_ring_buffer); + +#ifdef USE_SPEAKER + /// @brief Adds a sink speaker for decoded audio. + /// @param speaker pointer to speaker component + /// @return ESP_OK if successsful, ESP_ERR_NO_MEM if the transfer buffer wasn't allocated + esp_err_t add_sink(speaker::Speaker *speaker); +#endif + + /// @brief Sets up the class to resample. + /// @param input_stream_info The incoming sample rate, bits per sample, and number of channels + /// @param output_stream_info The desired outgoing sample rate, bits per sample, and number of channels + /// @param number_of_taps Number of taps per FIR filter + /// @param number_of_filters Number of FIR filters + /// @return ESP_OK if it is able to convert the incoming stream, + /// ESP_ERR_NO_MEM if the transfer buffers failed to allocate, + /// ESP_ERR_NOT_SUPPORTED if the stream can't be converted. + esp_err_t start(AudioStreamInfo &input_stream_info, AudioStreamInfo &output_stream_info, uint16_t number_of_taps, + uint16_t number_of_filters); + + /// @brief Resamples audio from the ring buffer source and writes to the sink. + /// @param stop_gracefully If true, it indicates the file decoder is finished. The resampler will resample all the + /// remaining audio and then finish. + /// @param ms_differential Pointer to a (int32_t) variable that will store the difference, in milliseconds, between + /// the duration of input audio used and the duration of output audio generated. + /// @return AudioResamplerState + AudioResamplerState resample(bool stop_gracefully, int32_t *ms_differential); + + /// @brief Pauses sending resampled audio to the sink. If paused, it will continue to process internal buffers. + /// @param pause_state If true, audio data is not sent to the sink. + void set_pause_output_state(bool pause_state) { this->pause_output_ = pause_state; } + + protected: + std::unique_ptr input_transfer_buffer_; + std::unique_ptr output_transfer_buffer_; + + size_t input_buffer_size_; + size_t output_buffer_size_; + + uint32_t accumulated_frames_used_{0}; + uint32_t accumulated_frames_generated_{0}; + + bool pause_output_{false}; + + AudioStreamInfo input_stream_info_; + AudioStreamInfo output_stream_info_; + + std::unique_ptr resampler_; +}; + +} // namespace audio +} // namespace esphome + +#endif From bd3469771566d04bd7a251081f26de22fbf9eb4b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 5 Feb 2025 07:56:38 +1300 Subject: [PATCH 0948/1052] Remove arm/v7 container image support (#8194) --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release.yml | 1 - docker/Dockerfile | 49 +++++---------------------------- docker/build.py | 12 +++----- 4 files changed, 12 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 65f847bc66..e156dbf1e2 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -37,7 +37,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [amd64, armv7, aarch64] + arch: [amd64, aarch64] build_type: ["ha-addon", "docker", "lint"] steps: - uses: actions/checkout@v4.1.7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca266c1f2c..d406ee0069 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,7 +80,6 @@ jobs: matrix: platform: - linux/amd64 - - linux/arm/v7 - linux/arm64 steps: - uses: actions/checkout@v4.1.7 diff --git a/docker/Dockerfile b/docker/Dockerfile index 429f5c4a1f..1db1ee7b51 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -51,19 +51,7 @@ ENV \ # Store globally installed pio libs in /piolibs PLATFORMIO_GLOBALLIB_DIR=/piolibs -# Support legacy binaries on Debian multiarch system. There is no "correct" way -# to do this, other than using properly built toolchains... -# See: https://unix.stackexchange.com/questions/553743/correct-way-to-add-lib-ld-linux-so-3-in-debian RUN \ - if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - ln -s /lib/arm-linux-gnueabihf/ld-linux-armhf.so.3 /lib/ld-linux.so.3; \ - fi - -RUN \ - # Ubuntu python3-pip is missing wheel - if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \ - export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \ - fi; \ pip3 install \ --break-system-packages --no-cache-dir \ # Keep platformio version in sync with requirements.txt @@ -82,14 +70,6 @@ RUN --mount=type=tmpfs,target=/root/.cargo < Date: Tue, 4 Feb 2025 14:18:11 -0600 Subject: [PATCH 0949/1052] [resampler] Media Player Components PR7 (#8169) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/audio/audio_decoder.cpp | 3 +- esphome/components/audio/audio_resampler.h | 5 +- esphome/components/resampler/__init__.py | 0 .../components/resampler/speaker/__init__.py | 103 ++++++ .../resampler/speaker/resampler_speaker.cpp | 318 ++++++++++++++++++ .../resampler/speaker/resampler_speaker.h | 107 ++++++ esphome/core/defines.h | 2 + tests/components/resampler/common.yaml | 13 + .../components/resampler/test.esp32-ard.yaml | 7 + .../resampler/test.esp32-c3-ard.yaml | 7 + .../resampler/test.esp32-c3-idf.yaml | 7 + .../components/resampler/test.esp32-idf.yaml | 7 + .../resampler/test.esp32-s3-ard.yaml | 7 + .../resampler/test.esp32-s3-idf.yaml | 7 + 15 files changed, 590 insertions(+), 4 deletions(-) create mode 100644 esphome/components/resampler/__init__.py create mode 100644 esphome/components/resampler/speaker/__init__.py create mode 100644 esphome/components/resampler/speaker/resampler_speaker.cpp create mode 100644 esphome/components/resampler/speaker/resampler_speaker.h create mode 100644 tests/components/resampler/common.yaml create mode 100644 tests/components/resampler/test.esp32-ard.yaml create mode 100644 tests/components/resampler/test.esp32-c3-ard.yaml create mode 100644 tests/components/resampler/test.esp32-c3-idf.yaml create mode 100644 tests/components/resampler/test.esp32-idf.yaml create mode 100644 tests/components/resampler/test.esp32-s3-ard.yaml create mode 100644 tests/components/resampler/test.esp32-s3-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index cdf4ab7a99..9fbf191be0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -343,6 +343,7 @@ esphome/components/radon_eye_rd200/* @jeffeb3 esphome/components/rc522/* @glmnet esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_spi/* @glmnet +esphome/components/resampler/speaker/* @kahrendt esphome/components/restart/* @esphome/core esphome/components/rf_bridge/* @jesserockz esphome/components/rgbct/* @jesserockz diff --git a/esphome/components/audio/audio_decoder.cpp b/esphome/components/audio/audio_decoder.cpp index b249f1381d..ab358ad805 100644 --- a/esphome/components/audio/audio_decoder.cpp +++ b/esphome/components/audio/audio_decoder.cpp @@ -280,8 +280,7 @@ FileDecoderState AudioDecoder::decode_mp3_() { if (err) { switch (err) { case esp_audio_libs::helix_decoder::ERR_MP3_OUT_OF_MEMORY: - return FileDecoderState::FAILED; - break; + // Intentional fallthrough case esp_audio_libs::helix_decoder::ERR_MP3_NULL_POINTER: return FileDecoderState::FAILED; break; diff --git a/esphome/components/audio/audio_resampler.h b/esphome/components/audio/audio_resampler.h index a348aaf783..7f4e987b4c 100644 --- a/esphome/components/audio/audio_resampler.h +++ b/esphome/components/audio/audio_resampler.h @@ -5,12 +5,13 @@ #include "audio.h" #include "audio_transfer_buffer.h" +#include "esphome/core/defines.h" +#include "esphome/core/ring_buffer.h" + #ifdef USE_SPEAKER #include "esphome/components/speaker/speaker.h" #endif -#include "esphome/core/ring_buffer.h" - #include "esp_err.h" #include // esp-audio-libs diff --git a/esphome/components/resampler/__init__.py b/esphome/components/resampler/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/resampler/speaker/__init__.py b/esphome/components/resampler/speaker/__init__.py new file mode 100644 index 0000000000..9e9b32476f --- /dev/null +++ b/esphome/components/resampler/speaker/__init__.py @@ -0,0 +1,103 @@ +import esphome.codegen as cg +from esphome.components import audio, esp32, speaker +import esphome.config_validation as cv +from esphome.const import ( + CONF_BITS_PER_SAMPLE, + CONF_BUFFER_DURATION, + CONF_FILTERS, + CONF_ID, + CONF_NUM_CHANNELS, + CONF_OUTPUT_SPEAKER, + CONF_SAMPLE_RATE, + CONF_TASK_STACK_IN_PSRAM, + PLATFORM_ESP32, +) +from esphome.core.entity_helpers import inherit_property_from + +AUTO_LOAD = ["audio"] +CODEOWNERS = ["@kahrendt"] + +resampler_ns = cg.esphome_ns.namespace("resampler") +ResamplerSpeaker = resampler_ns.class_( + "ResamplerSpeaker", cg.Component, speaker.Speaker +) + +CONF_TAPS = "taps" + + +def _set_stream_limits(config): + audio.set_stream_limits( + min_bits_per_sample=16, + max_bits_per_sample=32, + )(config) + + return config + + +def _validate_audio_compatability(config): + inherit_property_from(CONF_BITS_PER_SAMPLE, CONF_OUTPUT_SPEAKER)(config) + inherit_property_from(CONF_NUM_CHANNELS, CONF_OUTPUT_SPEAKER)(config) + inherit_property_from(CONF_SAMPLE_RATE, CONF_OUTPUT_SPEAKER)(config) + + audio.final_validate_audio_schema( + "source_speaker", + audio_device=CONF_OUTPUT_SPEAKER, + bits_per_sample=config.get(CONF_BITS_PER_SAMPLE), + channels=config.get(CONF_NUM_CHANNELS), + sample_rate=config.get(CONF_SAMPLE_RATE), + )(config) + + +def _validate_taps(taps): + value = cv.int_range(min=16, max=128)(taps) + if value % 4 != 0: + raise cv.Invalid("Number of taps must be divisible by 4") + return value + + +CONFIG_SCHEMA = cv.All( + speaker.SPEAKER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ResamplerSpeaker), + cv.Required(CONF_OUTPUT_SPEAKER): cv.use_id(speaker.Speaker), + cv.Optional( + CONF_BUFFER_DURATION, default="100ms" + ): cv.positive_time_period_milliseconds, + cv.SplitDefault(CONF_TASK_STACK_IN_PSRAM, esp32_idf=False): cv.All( + cv.boolean, cv.only_with_esp_idf + ), + cv.Optional(CONF_FILTERS, default=16): cv.int_range(min=2, max=1024), + cv.Optional(CONF_TAPS, default=16): _validate_taps, + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_on([PLATFORM_ESP32]), + _set_stream_limits, +) + + +FINAL_VALIDATE_SCHEMA = _validate_audio_compatability + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await speaker.register_speaker(var, config) + + output_spkr = await cg.get_variable(config[CONF_OUTPUT_SPEAKER]) + cg.add(var.set_output_speaker(output_spkr)) + + cg.add(var.set_buffer_duration(config[CONF_BUFFER_DURATION])) + + if task_stack_in_psram := config.get(CONF_TASK_STACK_IN_PSRAM): + cg.add(var.set_task_stack_in_psram(task_stack_in_psram)) + if task_stack_in_psram: + if config[CONF_TASK_STACK_IN_PSRAM]: + esp32.add_idf_sdkconfig_option( + "CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY", True + ) + + cg.add(var.set_target_bits_per_sample(config[CONF_BITS_PER_SAMPLE])) + cg.add(var.set_target_sample_rate(config[CONF_SAMPLE_RATE])) + + cg.add(var.set_filters(config[CONF_FILTERS])) + cg.add(var.set_taps(config[CONF_TAPS])) diff --git a/esphome/components/resampler/speaker/resampler_speaker.cpp b/esphome/components/resampler/speaker/resampler_speaker.cpp new file mode 100644 index 0000000000..9bb46ad78c --- /dev/null +++ b/esphome/components/resampler/speaker/resampler_speaker.cpp @@ -0,0 +1,318 @@ +#include "resampler_speaker.h" + +#ifdef USE_ESP32 + +#include "esphome/components/audio/audio_resampler.h" + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#include +#include + +namespace esphome { +namespace resampler { + +static const UBaseType_t RESAMPLER_TASK_PRIORITY = 1; + +static const uint32_t TRANSFER_BUFFER_DURATION_MS = 50; + +static const uint32_t TASK_DELAY_MS = 20; +static const uint32_t TASK_STACK_SIZE = 3072; + +static const char *const TAG = "resampler_speaker"; + +enum ResamplingEventGroupBits : uint32_t { + COMMAND_STOP = (1 << 0), // stops the resampler task + STATE_STARTING = (1 << 10), + STATE_RUNNING = (1 << 11), + STATE_STOPPING = (1 << 12), + STATE_STOPPED = (1 << 13), + ERR_ESP_NO_MEM = (1 << 19), + ERR_ESP_NOT_SUPPORTED = (1 << 20), + ERR_ESP_FAIL = (1 << 21), + ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits +}; + +void ResamplerSpeaker::setup() { + this->event_group_ = xEventGroupCreate(); + + if (this->event_group_ == nullptr) { + ESP_LOGE(TAG, "Failed to create event group"); + this->mark_failed(); + return; + } + + this->output_speaker_->add_audio_output_callback( + [this](uint32_t new_playback_ms, uint32_t remainder_us, uint32_t pending_ms, uint32_t write_timestamp) { + int32_t adjustment = this->playback_differential_ms_; + this->playback_differential_ms_ -= adjustment; + int32_t adjusted_playback_ms = static_cast(new_playback_ms) + adjustment; + this->audio_output_callback_(adjusted_playback_ms, remainder_us, pending_ms, write_timestamp); + }); +} + +void ResamplerSpeaker::loop() { + uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); + + if (event_group_bits & ResamplingEventGroupBits::STATE_STARTING) { + ESP_LOGD(TAG, "Starting resampler task"); + xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::STATE_STARTING); + } + + if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_NO_MEM) { + this->status_set_error("Resampler task failed to allocate the internal buffers"); + xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_NO_MEM); + this->state_ = speaker::STATE_STOPPING; + } + if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED) { + this->status_set_error("Cannot resample due to an unsupported audio stream"); + xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED); + this->state_ = speaker::STATE_STOPPING; + } + if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_FAIL) { + this->status_set_error("Resampler task failed"); + xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_FAIL); + this->state_ = speaker::STATE_STOPPING; + } + + if (event_group_bits & ResamplingEventGroupBits::STATE_RUNNING) { + ESP_LOGD(TAG, "Started resampler task"); + this->status_clear_error(); + xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::STATE_RUNNING); + } + if (event_group_bits & ResamplingEventGroupBits::STATE_STOPPING) { + ESP_LOGD(TAG, "Stopping resampler task"); + xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::STATE_STOPPING); + } + if (event_group_bits & ResamplingEventGroupBits::STATE_STOPPED) { + if (this->delete_task_() == ESP_OK) { + ESP_LOGD(TAG, "Stopped resampler task"); + xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ALL_BITS); + } + } + + switch (this->state_) { + case speaker::STATE_STARTING: { + esp_err_t err = this->start_(); + if (err == ESP_OK) { + this->status_clear_error(); + this->state_ = speaker::STATE_RUNNING; + } else { + switch (err) { + case ESP_ERR_INVALID_STATE: + this->status_set_error("Failed to start resampler: resampler task failed to start"); + break; + case ESP_ERR_NO_MEM: + this->status_set_error("Failed to start resampler: not enough memory for task stack"); + default: + this->status_set_error("Failed to start resampler"); + break; + } + + this->state_ = speaker::STATE_STOPPING; + } + break; + } + case speaker::STATE_RUNNING: + if (this->output_speaker_->is_stopped()) { + this->state_ = speaker::STATE_STOPPING; + } + + break; + case speaker::STATE_STOPPING: + this->stop_(); + this->state_ = speaker::STATE_STOPPED; + break; + case speaker::STATE_STOPPED: + break; + } +} + +size_t ResamplerSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) { + if (this->is_stopped()) { + this->start(); + } + + size_t bytes_written = 0; + if ((this->output_speaker_->is_running()) && (!this->requires_resampling_())) { + bytes_written = this->output_speaker_->play(data, length, ticks_to_wait); + } else { + if (this->ring_buffer_.use_count() == 1) { + std::shared_ptr temp_ring_buffer = this->ring_buffer_.lock(); + bytes_written = temp_ring_buffer->write_without_replacement(data, length, ticks_to_wait); + } + } + + return bytes_written; +} + +void ResamplerSpeaker::start() { this->state_ = speaker::STATE_STARTING; } + +esp_err_t ResamplerSpeaker::start_() { + this->target_stream_info_ = audio::AudioStreamInfo( + this->target_bits_per_sample_, this->audio_stream_info_.get_channels(), this->target_sample_rate_); + + this->output_speaker_->set_audio_stream_info(this->target_stream_info_); + this->output_speaker_->start(); + + if (this->requires_resampling_()) { + // Start the resampler task to handle converting sample rates + return this->start_task_(); + } + + return ESP_OK; +} + +esp_err_t ResamplerSpeaker::start_task_() { + if (this->task_stack_buffer_ == nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE); + } + } + + if (this->task_stack_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + if (this->task_handle_ == nullptr) { + this->task_handle_ = xTaskCreateStatic(resample_task, "sample", TASK_STACK_SIZE, (void *) this, + RESAMPLER_TASK_PRIORITY, this->task_stack_buffer_, &this->task_stack_); + } + + if (this->task_handle_ == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return ESP_OK; +} + +void ResamplerSpeaker::stop() { this->state_ = speaker::STATE_STOPPING; } + +void ResamplerSpeaker::stop_() { + if (this->task_handle_ != nullptr) { + xEventGroupSetBits(this->event_group_, ResamplingEventGroupBits::COMMAND_STOP); + } + this->output_speaker_->stop(); +} + +esp_err_t ResamplerSpeaker::delete_task_() { + if (!this->task_created_) { + this->task_handle_ = nullptr; + + if (this->task_stack_buffer_ != nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE); + } + + this->task_stack_buffer_ = nullptr; + } + + return ESP_OK; + } + + return ESP_ERR_INVALID_STATE; +} + +void ResamplerSpeaker::finish() { this->output_speaker_->finish(); } + +bool ResamplerSpeaker::has_buffered_data() const { + bool has_ring_buffer_data = false; + if (this->requires_resampling_() && (this->ring_buffer_.use_count() > 0)) { + has_ring_buffer_data = (this->ring_buffer_.lock()->available() > 0); + } + return (has_ring_buffer_data || this->output_speaker_->has_buffered_data()); +} + +void ResamplerSpeaker::set_mute_state(bool mute_state) { + this->mute_state_ = mute_state; + this->output_speaker_->set_mute_state(mute_state); +} + +void ResamplerSpeaker::set_volume(float volume) { + this->volume_ = volume; + this->output_speaker_->set_volume(volume); +} + +bool ResamplerSpeaker::requires_resampling_() const { + return (this->audio_stream_info_.get_sample_rate() != this->target_sample_rate_) || + (this->audio_stream_info_.get_bits_per_sample() != this->target_bits_per_sample_); +} + +void ResamplerSpeaker::resample_task(void *params) { + ResamplerSpeaker *this_resampler = (ResamplerSpeaker *) params; + + this_resampler->task_created_ = true; + xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_STARTING); + + std::unique_ptr resampler = + make_unique(this_resampler->audio_stream_info_.ms_to_bytes(TRANSFER_BUFFER_DURATION_MS), + this_resampler->target_stream_info_.ms_to_bytes(TRANSFER_BUFFER_DURATION_MS)); + + esp_err_t err = resampler->start(this_resampler->audio_stream_info_, this_resampler->target_stream_info_, + this_resampler->taps_, this_resampler->filters_); + + if (err == ESP_OK) { + std::shared_ptr temp_ring_buffer = + RingBuffer::create(this_resampler->audio_stream_info_.ms_to_bytes(this_resampler->buffer_duration_ms_)); + + if (temp_ring_buffer.use_count() == 0) { + err = ESP_ERR_NO_MEM; + } else { + this_resampler->ring_buffer_ = temp_ring_buffer; + resampler->add_source(this_resampler->ring_buffer_); + + this_resampler->output_speaker_->set_audio_stream_info(this_resampler->target_stream_info_); + resampler->add_sink(this_resampler->output_speaker_); + } + } + + if (err == ESP_OK) { + xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_RUNNING); + } else if (err == ESP_ERR_NO_MEM) { + xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::ERR_ESP_NO_MEM); + } else if (err == ESP_ERR_NOT_SUPPORTED) { + xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED); + } + + this_resampler->playback_differential_ms_ = 0; + while (err == ESP_OK) { + uint32_t event_bits = xEventGroupGetBits(this_resampler->event_group_); + + if (event_bits & ResamplingEventGroupBits::COMMAND_STOP) { + break; + } + + // Stop gracefully if the decoder is done + int32_t ms_differential = 0; + audio::AudioResamplerState resampler_state = resampler->resample(false, &ms_differential); + + this_resampler->playback_differential_ms_ += ms_differential; + + if (resampler_state == audio::AudioResamplerState::FINISHED) { + break; + } else if (resampler_state == audio::AudioResamplerState::FAILED) { + xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::ERR_ESP_FAIL); + break; + } + } + + xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_STOPPING); + resampler.reset(); + xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_STOPPED); + this_resampler->task_created_ = false; + vTaskDelete(nullptr); +} + +} // namespace resampler +} // namespace esphome + +#endif diff --git a/esphome/components/resampler/speaker/resampler_speaker.h b/esphome/components/resampler/speaker/resampler_speaker.h new file mode 100644 index 0000000000..c44f740fa2 --- /dev/null +++ b/esphome/components/resampler/speaker/resampler_speaker.h @@ -0,0 +1,107 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/audio/audio.h" +#include "esphome/components/audio/audio_transfer_buffer.h" +#include "esphome/components/speaker/speaker.h" + +#include "esphome/core/component.h" + +#include +#include + +namespace esphome { +namespace resampler { + +class ResamplerSpeaker : public Component, public speaker::Speaker { + public: + float get_setup_priority() const override { return esphome::setup_priority::DATA; } + void setup() override; + void loop() override; + + size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override; + size_t play(const uint8_t *data, size_t length) override { return this->play(data, length, 0); } + + void start() override; + void stop() override; + void finish() override; + + void set_pause_state(bool pause_state) override { this->output_speaker_->set_pause_state(pause_state); } + bool get_pause_state() const override { return this->output_speaker_->get_pause_state(); } + + bool has_buffered_data() const override; + + /// @brief Mute state changes are passed to the parent's output speaker + void set_mute_state(bool mute_state) override; + + /// @brief Volume state changes are passed to the parent's output speaker + void set_volume(float volume) override; + + void set_output_speaker(speaker::Speaker *speaker) { this->output_speaker_ = speaker; } + void set_task_stack_in_psram(bool task_stack_in_psram) { this->task_stack_in_psram_ = task_stack_in_psram; } + + void set_target_bits_per_sample(uint8_t target_bits_per_sample) { + this->target_bits_per_sample_ = target_bits_per_sample; + } + void set_target_sample_rate(uint32_t target_sample_rate) { this->target_sample_rate_ = target_sample_rate; } + + void set_filters(uint16_t filters) { this->filters_ = filters; } + void set_taps(uint16_t taps) { this->taps_ = taps; } + + void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; } + + protected: + /// @brief Starts the output speaker after setting the resampled stream info. If resampling is required, it starts the + /// task. + /// @return ESP_OK if resampling is required + /// return value of start_task_() if resampling is required + esp_err_t start_(); + + /// @brief Starts the resampler task after allocating the task stack + /// @return ESP_OK if successful, + /// ESP_ERR_NO_MEM if the task stack couldn't be allocated + /// ESP_ERR_INVALID_STATE if the task wasn't created + esp_err_t start_task_(); + + /// @brief Stops the output speaker. If the resampling task is running, it sends the stop command. + void stop_(); + + /// @brief Deallocates the task stack and resets the pointers. + /// @return ESP_OK if successful + /// ESP_ERR_INVALID_STATE if the task hasn't stopped itself + esp_err_t delete_task_(); + + inline bool requires_resampling_() const; + static void resample_task(void *params); + + EventGroupHandle_t event_group_{nullptr}; + + std::weak_ptr ring_buffer_; + + speaker::Speaker *output_speaker_{nullptr}; + + bool task_stack_in_psram_{false}; + bool task_created_{false}; + + TaskHandle_t task_handle_{nullptr}; + StaticTask_t task_stack_; + StackType_t *task_stack_buffer_{nullptr}; + + audio::AudioStreamInfo target_stream_info_; + + uint16_t taps_; + uint16_t filters_; + + uint8_t target_bits_per_sample_; + uint32_t target_sample_rate_; + + uint32_t buffer_duration_ms_; + + int32_t playback_differential_ms_{0}; +}; + +} // namespace resampler +} // namespace esphome + +#endif diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 211f3b8319..8407391bce 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -16,6 +16,8 @@ // Feature flags #define USE_ALARM_CONTROL_PANEL +#define USE_AUDIO_FLAC_SUPPORT +#define USE_AUDIO_MP3_SUPPORT #define USE_API #define USE_API_NOISE #define USE_API_PLAINTEXT diff --git a/tests/components/resampler/common.yaml b/tests/components/resampler/common.yaml new file mode 100644 index 0000000000..8ff09ed256 --- /dev/null +++ b/tests/components/resampler/common.yaml @@ -0,0 +1,13 @@ +i2s_audio: + i2s_lrclk_pin: ${lrclk_pin} + i2s_bclk_pin: ${bclk_pin} + i2s_mclk_pin: ${mclk_pin} + +speaker: + - platform: i2s_audio + id: speaker_id + dac_type: external + i2s_dout_pin: ${dout_pin} + - platform: resampler + id: resampler_speaker_id + output_speaker: speaker_id diff --git a/tests/components/resampler/test.esp32-ard.yaml b/tests/components/resampler/test.esp32-ard.yaml new file mode 100644 index 0000000000..96d2d37458 --- /dev/null +++ b/tests/components/resampler/test.esp32-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO16 + bclk_pin: GPIO17 + mclk_pin: GPIO15 + dout_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-c3-ard.yaml b/tests/components/resampler/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/resampler/test.esp32-c3-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-c3-idf.yaml b/tests/components/resampler/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/resampler/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-idf.yaml b/tests/components/resampler/test.esp32-idf.yaml new file mode 100644 index 0000000000..96d2d37458 --- /dev/null +++ b/tests/components/resampler/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO16 + bclk_pin: GPIO17 + mclk_pin: GPIO15 + dout_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-s3-ard.yaml b/tests/components/resampler/test.esp32-s3-ard.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/resampler/test.esp32-s3-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/resampler/test.esp32-s3-idf.yaml b/tests/components/resampler/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/resampler/test.esp32-s3-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml From 6f4e8f1fbfa77fa114f34296841af266c75324e6 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 4 Feb 2025 17:00:02 -0600 Subject: [PATCH 0950/1052] [mixer] Media Player Components PR8 (#8170) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/mixer/__init__.py | 0 esphome/components/mixer/speaker/__init__.py | 172 +++++ esphome/components/mixer/speaker/automation.h | 19 + .../mixer/speaker/mixer_speaker.cpp | 624 ++++++++++++++++++ .../components/mixer/speaker/mixer_speaker.h | 207 ++++++ tests/components/mixer/common.yaml | 23 + tests/components/mixer/test.esp32-ard.yaml | 7 + tests/components/mixer/test.esp32-c3-ard.yaml | 7 + tests/components/mixer/test.esp32-c3-idf.yaml | 7 + tests/components/mixer/test.esp32-idf.yaml | 7 + tests/components/mixer/test.esp32-s3-ard.yaml | 7 + tests/components/mixer/test.esp32-s3-idf.yaml | 7 + 13 files changed, 1088 insertions(+) create mode 100644 esphome/components/mixer/__init__.py create mode 100644 esphome/components/mixer/speaker/__init__.py create mode 100644 esphome/components/mixer/speaker/automation.h create mode 100644 esphome/components/mixer/speaker/mixer_speaker.cpp create mode 100644 esphome/components/mixer/speaker/mixer_speaker.h create mode 100644 tests/components/mixer/common.yaml create mode 100644 tests/components/mixer/test.esp32-ard.yaml create mode 100644 tests/components/mixer/test.esp32-c3-ard.yaml create mode 100644 tests/components/mixer/test.esp32-c3-idf.yaml create mode 100644 tests/components/mixer/test.esp32-idf.yaml create mode 100644 tests/components/mixer/test.esp32-s3-ard.yaml create mode 100644 tests/components/mixer/test.esp32-s3-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 9fbf191be0..d26e153c1a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -277,6 +277,7 @@ esphome/components/mics_4514/* @jesserockz esphome/components/midea/* @dudanov esphome/components/midea_ir/* @dudanov esphome/components/mitsubishi/* @RubyBailey +esphome/components/mixer/speaker/* @kahrendt esphome/components/mlx90393/* @functionpointer esphome/components/mlx90614/* @jesserockz esphome/components/mmc5603/* @benhoff diff --git a/esphome/components/mixer/__init__.py b/esphome/components/mixer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/mixer/speaker/__init__.py b/esphome/components/mixer/speaker/__init__.py new file mode 100644 index 0000000000..a451f2b7b4 --- /dev/null +++ b/esphome/components/mixer/speaker/__init__.py @@ -0,0 +1,172 @@ +from esphome import automation +import esphome.codegen as cg +from esphome.components import audio, esp32, speaker +import esphome.config_validation as cv +from esphome.const import ( + CONF_BITS_PER_SAMPLE, + CONF_BUFFER_DURATION, + CONF_DURATION, + CONF_ID, + CONF_NEVER, + CONF_NUM_CHANNELS, + CONF_OUTPUT_SPEAKER, + CONF_SAMPLE_RATE, + CONF_TASK_STACK_IN_PSRAM, + CONF_TIMEOUT, + PLATFORM_ESP32, +) +from esphome.core.entity_helpers import inherit_property_from +import esphome.final_validate as fv + +AUTO_LOAD = ["audio"] +CODEOWNERS = ["@kahrendt"] + +mixer_speaker_ns = cg.esphome_ns.namespace("mixer_speaker") +MixerSpeaker = mixer_speaker_ns.class_("MixerSpeaker", cg.Component) +SourceSpeaker = mixer_speaker_ns.class_("SourceSpeaker", cg.Component, speaker.Speaker) + +CONF_DECIBEL_REDUCTION = "decibel_reduction" +CONF_QUEUE_MODE = "queue_mode" +CONF_SOURCE_SPEAKERS = "source_speakers" + +DuckingApplyAction = mixer_speaker_ns.class_( + "DuckingApplyAction", automation.Action, cg.Parented.template(SourceSpeaker) +) + + +SOURCE_SPEAKER_SCHEMA = speaker.SPEAKER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SourceSpeaker), + cv.Optional( + CONF_BUFFER_DURATION, default="100ms" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_TIMEOUT, default="500ms"): cv.Any( + cv.positive_time_period_milliseconds, + cv.one_of(CONF_NEVER, lower=True), + ), + cv.Optional(CONF_BITS_PER_SAMPLE, default=16): cv.int_range(16, 16), + } +) + + +def _set_stream_limits(config): + audio.set_stream_limits( + min_bits_per_sample=16, + max_bits_per_sample=16, + )(config) + + return config + + +def _validate_source_speaker(config): + fconf = fv.full_config.get() + + # Get ID for the output speaker and add it to the source speakrs config to easily inherit properties + path = fconf.get_path_for_id(config[CONF_ID])[:-3] + path.append(CONF_OUTPUT_SPEAKER) + output_speaker_id = fconf.get_config_for_path(path) + config[CONF_OUTPUT_SPEAKER] = output_speaker_id + + inherit_property_from(CONF_NUM_CHANNELS, CONF_OUTPUT_SPEAKER)(config) + inherit_property_from(CONF_SAMPLE_RATE, CONF_OUTPUT_SPEAKER)(config) + + audio.final_validate_audio_schema( + "mixer", + audio_device=CONF_OUTPUT_SPEAKER, + bits_per_sample=config.get(CONF_BITS_PER_SAMPLE), + channels=config.get(CONF_NUM_CHANNELS), + sample_rate=config.get(CONF_SAMPLE_RATE), + )(config) + + return config + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MixerSpeaker), + cv.Required(CONF_OUTPUT_SPEAKER): cv.use_id(speaker.Speaker), + cv.Required(CONF_SOURCE_SPEAKERS): cv.All( + cv.ensure_list(SOURCE_SPEAKER_SCHEMA), + cv.Length(min=2, max=8), + [_set_stream_limits], + ), + cv.Optional(CONF_NUM_CHANNELS): cv.int_range(min=1, max=2), + cv.Optional(CONF_QUEUE_MODE, default=False): cv.boolean, + cv.SplitDefault(CONF_TASK_STACK_IN_PSRAM, esp32_idf=False): cv.All( + cv.boolean, cv.only_with_esp_idf + ), + } + ), + cv.only_on([PLATFORM_ESP32]), +) + +FINAL_VALIDATE_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_SOURCE_SPEAKERS): [_validate_source_speaker], + }, + extra=cv.ALLOW_EXTRA, + ), + inherit_property_from(CONF_NUM_CHANNELS, CONF_OUTPUT_SPEAKER), +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + spkr = await cg.get_variable(config[CONF_OUTPUT_SPEAKER]) + + cg.add(var.set_output_channels(config[CONF_NUM_CHANNELS])) + cg.add(var.set_output_speaker(spkr)) + cg.add(var.set_queue_mode(config[CONF_QUEUE_MODE])) + + if task_stack_in_psram := config.get(CONF_TASK_STACK_IN_PSRAM): + cg.add(var.set_task_stack_in_psram(task_stack_in_psram)) + if task_stack_in_psram: + if config[CONF_TASK_STACK_IN_PSRAM]: + esp32.add_idf_sdkconfig_option( + "CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY", True + ) + + for speaker_config in config[CONF_SOURCE_SPEAKERS]: + source_speaker = cg.new_Pvariable(speaker_config[CONF_ID]) + + cg.add(source_speaker.set_buffer_duration(speaker_config[CONF_BUFFER_DURATION])) + + if speaker_config[CONF_TIMEOUT] != CONF_NEVER: + cg.add(source_speaker.set_timeout(speaker_config[CONF_TIMEOUT])) + + await cg.register_component(source_speaker, speaker_config) + await cg.register_parented(source_speaker, config[CONF_ID]) + await speaker.register_speaker(source_speaker, speaker_config) + + cg.add(var.add_source_speaker(source_speaker)) + + +@automation.register_action( + "mixer_speaker.apply_ducking", + DuckingApplyAction, + cv.Schema( + { + cv.GenerateID(): cv.use_id(SourceSpeaker), + cv.Required(CONF_DECIBEL_REDUCTION): cv.templatable( + cv.int_range(min=0, max=51) + ), + cv.Optional(CONF_DURATION, default="0.0s"): cv.templatable( + cv.positive_time_period_milliseconds + ), + } + ), +) +async def ducking_set_to_code(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + decibel_reduction = await cg.templatable( + config[CONF_DECIBEL_REDUCTION], args, cg.uint8 + ) + cg.add(var.set_decibel_reduction(decibel_reduction)) + duration = await cg.templatable(config[CONF_DURATION], args, cg.uint32) + cg.add(var.set_duration(duration)) + return var diff --git a/esphome/components/mixer/speaker/automation.h b/esphome/components/mixer/speaker/automation.h new file mode 100644 index 0000000000..b688fa2c1e --- /dev/null +++ b/esphome/components/mixer/speaker/automation.h @@ -0,0 +1,19 @@ +#pragma once + +#include "mixer_speaker.h" + +#ifdef USE_ESP32 + +namespace esphome { +namespace mixer_speaker { +template class DuckingApplyAction : public Action, public Parented { + TEMPLATABLE_VALUE(uint8_t, decibel_reduction) + TEMPLATABLE_VALUE(uint32_t, duration) + void play(Ts... x) override { + this->parent_->apply_ducking(this->decibel_reduction_.value(x...), this->duration_.value(x...)); + } +}; +} // namespace mixer_speaker +} // namespace esphome + +#endif diff --git a/esphome/components/mixer/speaker/mixer_speaker.cpp b/esphome/components/mixer/speaker/mixer_speaker.cpp new file mode 100644 index 0000000000..60cff95eb2 --- /dev/null +++ b/esphome/components/mixer/speaker/mixer_speaker.cpp @@ -0,0 +1,624 @@ +#include "mixer_speaker.h" + +#ifdef USE_ESP32 + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#include +#include + +namespace esphome { +namespace mixer_speaker { + +static const UBaseType_t MIXER_TASK_PRIORITY = 10; + +static const uint32_t TRANSFER_BUFFER_DURATION_MS = 50; +static const uint32_t TASK_DELAY_MS = 25; + +static const size_t TASK_STACK_SIZE = 4096; + +static const int16_t MAX_AUDIO_SAMPLE_VALUE = INT16_MAX; +static const int16_t MIN_AUDIO_SAMPLE_VALUE = INT16_MIN; + +static const char *const TAG = "speaker_mixer"; + +// Gives the Q15 fixed point scaling factor to reduce by 0 dB, 1dB, ..., 50 dB +// dB to PCM scaling factor formula: floating_point_scale_factor = 2^(-db/6.014) +// float to Q15 fixed point formula: q15_scale_factor = floating_point_scale_factor * 2^(15) +static const std::vector DECIBEL_REDUCTION_TABLE = { + 32767, 29201, 26022, 23189, 20665, 18415, 16410, 14624, 13032, 11613, 10349, 9222, 8218, 7324, 6527, 5816, 5183, + 4619, 4116, 3668, 3269, 2913, 2596, 2313, 2061, 1837, 1637, 1459, 1300, 1158, 1032, 920, 820, 731, + 651, 580, 517, 461, 411, 366, 326, 291, 259, 231, 206, 183, 163, 146, 130, 116, 103}; + +enum MixerEventGroupBits : uint32_t { + COMMAND_STOP = (1 << 0), // stops the mixer task + STATE_STARTING = (1 << 10), + STATE_RUNNING = (1 << 11), + STATE_STOPPING = (1 << 12), + STATE_STOPPED = (1 << 13), + ERR_ESP_NO_MEM = (1 << 19), + ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits +}; + +void SourceSpeaker::dump_config() { + ESP_LOGCONFIG(TAG, "Mixer Source Speaker"); + ESP_LOGCONFIG(TAG, " 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 { + ESP_LOGCONFIG(TAG, " Timeout: never"); + } +} + +void SourceSpeaker::setup() { + this->parent_->get_output_speaker()->add_audio_output_callback( + [this](uint32_t new_playback_ms, uint32_t remainder_us, uint32_t pending_ms, uint32_t write_timestamp) { + uint32_t personal_playback_ms = std::min(new_playback_ms, this->pending_playback_ms_); + if (personal_playback_ms > 0) { + this->pending_playback_ms_ -= personal_playback_ms; + this->audio_output_callback_(personal_playback_ms, remainder_us, this->pending_playback_ms_, write_timestamp); + } + }); +} + +void SourceSpeaker::loop() { + switch (this->state_) { + case speaker::STATE_STARTING: { + esp_err_t err = this->start_(); + if (err == ESP_OK) { + this->state_ = speaker::STATE_RUNNING; + this->stop_gracefully_ = false; + this->last_seen_data_ms_ = millis(); + this->status_clear_error(); + } else { + switch (err) { + case ESP_ERR_NO_MEM: + this->status_set_error("Failed to start mixer: not enough memory"); + break; + case ESP_ERR_NOT_SUPPORTED: + this->status_set_error("Failed to start mixer: unsupported bits per sample"); + break; + case ESP_ERR_INVALID_ARG: + this->status_set_error("Failed to start mixer: audio stream isn't compatible with the other audio stream."); + break; + case ESP_ERR_INVALID_STATE: + this->status_set_error("Failed to start mixer: mixer task failed to start"); + break; + default: + this->status_set_error("Failed to start mixer"); + break; + } + + this->state_ = speaker::STATE_STOPPING; + } + break; + } + case speaker::STATE_RUNNING: + if (!this->transfer_buffer_->has_buffered_data()) { + if ((this->timeout_ms_.has_value() && ((millis() - this->last_seen_data_ms_) > this->timeout_ms_.value())) || + this->stop_gracefully_) { + this->state_ = speaker::STATE_STOPPING; + } + } + break; + case speaker::STATE_STOPPING: + this->stop_(); + this->stop_gracefully_ = false; + this->state_ = speaker::STATE_STOPPED; + break; + case speaker::STATE_STOPPED: + break; + } +} + +size_t SourceSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) { + if (this->is_stopped()) { + this->start(); + } + size_t bytes_written = 0; + if (this->ring_buffer_.use_count() == 1) { + std::shared_ptr temp_ring_buffer = this->ring_buffer_.lock(); + bytes_written = temp_ring_buffer->write_without_replacement(data, length, ticks_to_wait); + if (bytes_written > 0) { + this->last_seen_data_ms_ = millis(); + } + } + return bytes_written; +} + +void SourceSpeaker::start() { this->state_ = speaker::STATE_STARTING; } + +esp_err_t SourceSpeaker::start_() { + const size_t ring_buffer_size = this->audio_stream_info_.ms_to_bytes(this->buffer_duration_ms_); + if (this->transfer_buffer_.use_count() == 0) { + this->transfer_buffer_ = + audio::AudioSourceTransferBuffer::create(this->audio_stream_info_.ms_to_bytes(TRANSFER_BUFFER_DURATION_MS)); + + if (this->transfer_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + std::shared_ptr temp_ring_buffer; + + if (!this->ring_buffer_.use_count()) { + temp_ring_buffer = RingBuffer::create(ring_buffer_size); + this->ring_buffer_ = temp_ring_buffer; + } + + if (!this->ring_buffer_.use_count()) { + return ESP_ERR_NO_MEM; + } else { + this->transfer_buffer_->set_source(temp_ring_buffer); + } + } + + return this->parent_->start(this->audio_stream_info_); +} + +void SourceSpeaker::stop() { + if (this->state_ != speaker::STATE_STOPPED) { + this->state_ = speaker::STATE_STOPPING; + } +} + +void SourceSpeaker::stop_() { + this->transfer_buffer_.reset(); // deallocates the transfer buffer +} + +void SourceSpeaker::finish() { this->stop_gracefully_ = true; } + +bool SourceSpeaker::has_buffered_data() const { + return ((this->transfer_buffer_.use_count() > 0) && this->transfer_buffer_->has_buffered_data()); +} + +void SourceSpeaker::set_mute_state(bool mute_state) { + this->mute_state_ = mute_state; + this->parent_->get_output_speaker()->set_mute_state(mute_state); +} + +void SourceSpeaker::set_volume(float volume) { + this->volume_ = volume; + this->parent_->get_output_speaker()->set_volume(volume); +} + +size_t SourceSpeaker::process_data_from_source(TickType_t ticks_to_wait) { + if (!this->transfer_buffer_.use_count()) { + return 0; + } + + // Store current offset, as these samples are already ducked + const size_t current_length = this->transfer_buffer_->available(); + + size_t bytes_read = this->transfer_buffer_->transfer_data_from_source(ticks_to_wait); + + uint32_t samples_to_duck = this->audio_stream_info_.bytes_to_samples(bytes_read); + if (samples_to_duck > 0) { + int16_t *current_buffer = reinterpret_cast(this->transfer_buffer_->get_buffer_start() + current_length); + + duck_samples(current_buffer, samples_to_duck, &this->current_ducking_db_reduction_, + &this->ducking_transition_samples_remaining_, this->samples_per_ducking_step_, + this->db_change_per_ducking_step_); + } + + return bytes_read; +} + +void SourceSpeaker::apply_ducking(uint8_t decibel_reduction, uint32_t duration) { + if (this->target_ducking_db_reduction_ != decibel_reduction) { + this->current_ducking_db_reduction_ = this->target_ducking_db_reduction_; + + this->target_ducking_db_reduction_ = decibel_reduction; + + uint8_t total_ducking_steps = 0; + if (this->target_ducking_db_reduction_ > this->current_ducking_db_reduction_) { + // The dB reduction level is increasing (which results in quieter audio) + total_ducking_steps = this->target_ducking_db_reduction_ - this->current_ducking_db_reduction_ - 1; + this->db_change_per_ducking_step_ = 1; + } else { + // The dB reduction level is decreasing (which results in louder audio) + total_ducking_steps = this->current_ducking_db_reduction_ - this->target_ducking_db_reduction_ - 1; + this->db_change_per_ducking_step_ = -1; + } + if ((duration > 0) && (total_ducking_steps > 0)) { + this->ducking_transition_samples_remaining_ = this->audio_stream_info_.ms_to_samples(duration); + + this->samples_per_ducking_step_ = this->ducking_transition_samples_remaining_ / total_ducking_steps; + this->ducking_transition_samples_remaining_ = + this->samples_per_ducking_step_ * total_ducking_steps; // Adjust for integer division rounding + + this->current_ducking_db_reduction_ += this->db_change_per_ducking_step_; + } else { + this->ducking_transition_samples_remaining_ = 0; + this->current_ducking_db_reduction_ = this->target_ducking_db_reduction_; + } + } +} + +void SourceSpeaker::duck_samples(int16_t *input_buffer, uint32_t input_samples_to_duck, + int8_t *current_ducking_db_reduction, uint32_t *ducking_transition_samples_remaining, + uint32_t samples_per_ducking_step, int8_t db_change_per_ducking_step) { + if (*ducking_transition_samples_remaining > 0) { + // Ducking level is still transitioning + + // Takes the ceiling of input_samples_to_duck/samples_per_ducking_step + uint32_t ducking_steps_in_batch = + input_samples_to_duck / samples_per_ducking_step + (input_samples_to_duck % samples_per_ducking_step != 0); + + for (uint32_t i = 0; i < ducking_steps_in_batch; ++i) { + uint32_t samples_left_in_step = *ducking_transition_samples_remaining % samples_per_ducking_step; + + if (samples_left_in_step == 0) { + samples_left_in_step = samples_per_ducking_step; + } + + uint32_t samples_to_duck = std::min(input_samples_to_duck, samples_left_in_step); + samples_to_duck = std::min(samples_to_duck, *ducking_transition_samples_remaining); + + // Ensure we only point to valid index in the Q15 scaling factor table + uint8_t safe_db_reduction_index = + clamp(*current_ducking_db_reduction, 0, DECIBEL_REDUCTION_TABLE.size() - 1); + int16_t q15_scale_factor = DECIBEL_REDUCTION_TABLE[safe_db_reduction_index]; + + audio::scale_audio_samples(input_buffer, input_buffer, q15_scale_factor, samples_to_duck); + + if (samples_left_in_step - samples_to_duck == 0) { + // After scaling the current samples, we are ready to transition to the next step + *current_ducking_db_reduction += db_change_per_ducking_step; + } + + input_buffer += samples_to_duck; + *ducking_transition_samples_remaining -= samples_to_duck; + input_samples_to_duck -= samples_to_duck; + } + } + + if ((*current_ducking_db_reduction > 0) && (input_samples_to_duck > 0)) { + // Audio is ducked, but its not in the middle of a transition step + + uint8_t safe_db_reduction_index = + clamp(*current_ducking_db_reduction, 0, DECIBEL_REDUCTION_TABLE.size() - 1); + int16_t q15_scale_factor = DECIBEL_REDUCTION_TABLE[safe_db_reduction_index]; + + audio::scale_audio_samples(input_buffer, input_buffer, q15_scale_factor, input_samples_to_duck); + } +} + +void MixerSpeaker::dump_config() { + ESP_LOGCONFIG(TAG, "Speaker Mixer:"); + ESP_LOGCONFIG(TAG, " Number of output channels: %u", this->output_channels_); +} + +void MixerSpeaker::setup() { + this->event_group_ = xEventGroupCreate(); + + if (this->event_group_ == nullptr) { + ESP_LOGE(TAG, "Failed to create event group"); + this->mark_failed(); + return; + } +} + +void MixerSpeaker::loop() { + uint32_t event_group_bits = xEventGroupGetBits(this->event_group_); + + if (event_group_bits & MixerEventGroupBits::STATE_STARTING) { + ESP_LOGD(TAG, "Starting speaker mixer"); + xEventGroupClearBits(this->event_group_, MixerEventGroupBits::STATE_STARTING); + } + if (event_group_bits & MixerEventGroupBits::ERR_ESP_NO_MEM) { + this->status_set_error("Failed to allocate the mixer's internal buffer"); + xEventGroupClearBits(this->event_group_, MixerEventGroupBits::ERR_ESP_NO_MEM); + } + if (event_group_bits & MixerEventGroupBits::STATE_RUNNING) { + ESP_LOGD(TAG, "Started speaker mixer"); + this->status_clear_error(); + xEventGroupClearBits(this->event_group_, MixerEventGroupBits::STATE_RUNNING); + } + if (event_group_bits & MixerEventGroupBits::STATE_STOPPING) { + ESP_LOGD(TAG, "Stopping speaker mixer"); + xEventGroupClearBits(this->event_group_, MixerEventGroupBits::STATE_STOPPING); + } + if (event_group_bits & MixerEventGroupBits::STATE_STOPPED) { + if (this->delete_task_() == ESP_OK) { + xEventGroupClearBits(this->event_group_, MixerEventGroupBits::ALL_BITS); + } + } + + if (this->task_handle_ != nullptr) { + bool all_stopped = true; + + for (auto &speaker : this->source_speakers_) { + all_stopped &= speaker->is_stopped(); + } + + if (all_stopped) { + this->stop(); + } + } +} + +esp_err_t MixerSpeaker::start(audio::AudioStreamInfo &stream_info) { + if (!this->audio_stream_info_.has_value()) { + if (stream_info.get_bits_per_sample() != 16) { + // Audio streams that don't have 16 bits per sample are not supported + return ESP_ERR_NOT_SUPPORTED; + } + + this->audio_stream_info_ = audio::AudioStreamInfo(stream_info.get_bits_per_sample(), this->output_channels_, + stream_info.get_sample_rate()); + this->output_speaker_->set_audio_stream_info(this->audio_stream_info_.value()); + } else { + if (!this->queue_mode_ && (stream_info.get_sample_rate() != this->audio_stream_info_.value().get_sample_rate())) { + // The two audio streams must have the same sample rate to mix properly if not in queue mode + return ESP_ERR_INVALID_ARG; + } + } + + return this->start_task_(); +} + +esp_err_t MixerSpeaker::start_task_() { + if (this->task_stack_buffer_ == nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE); + } + } + + if (this->task_stack_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + if (this->task_handle_ == nullptr) { + this->task_handle_ = xTaskCreateStatic(audio_mixer_task, "mixer", TASK_STACK_SIZE, (void *) this, + MIXER_TASK_PRIORITY, this->task_stack_buffer_, &this->task_stack_); + } + + if (this->task_handle_ == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return ESP_OK; +} + +esp_err_t MixerSpeaker::delete_task_() { + if (!this->task_created_) { + this->task_handle_ = nullptr; + + if (this->task_stack_buffer_ != nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE); + } + + this->task_stack_buffer_ = nullptr; + } + + return ESP_OK; + } + + return ESP_ERR_INVALID_STATE; +} + +void MixerSpeaker::stop() { xEventGroupSetBits(this->event_group_, MixerEventGroupBits::COMMAND_STOP); } + +void MixerSpeaker::copy_frames(const int16_t *input_buffer, audio::AudioStreamInfo input_stream_info, + int16_t *output_buffer, audio::AudioStreamInfo output_stream_info, + uint32_t frames_to_transfer) { + uint8_t input_channels = input_stream_info.get_channels(); + uint8_t output_channels = output_stream_info.get_channels(); + const uint8_t max_input_channel_index = input_channels - 1; + + if (input_channels == output_channels) { + size_t bytes_to_copy = input_stream_info.frames_to_bytes(frames_to_transfer); + memcpy(output_buffer, input_buffer, bytes_to_copy); + + return; + } + + for (uint32_t frame_index = 0; frame_index < frames_to_transfer; ++frame_index) { + for (uint8_t output_channel_index = 0; output_channel_index < output_channels; ++output_channel_index) { + uint8_t input_channel_index = std::min(output_channel_index, max_input_channel_index); + output_buffer[output_channels * frame_index + output_channel_index] = + input_buffer[input_channels * frame_index + input_channel_index]; + } + } +} + +void MixerSpeaker::mix_audio_samples(const int16_t *primary_buffer, audio::AudioStreamInfo primary_stream_info, + const int16_t *secondary_buffer, audio::AudioStreamInfo secondary_stream_info, + int16_t *output_buffer, audio::AudioStreamInfo output_stream_info, + uint32_t frames_to_mix) { + const uint8_t primary_channels = primary_stream_info.get_channels(); + const uint8_t secondary_channels = secondary_stream_info.get_channels(); + const uint8_t output_channels = output_stream_info.get_channels(); + + const uint8_t max_primary_channel_index = primary_channels - 1; + const uint8_t max_secondary_channel_index = secondary_channels - 1; + + for (uint32_t frames_index = 0; frames_index < frames_to_mix; ++frames_index) { + for (uint8_t output_channel_index = 0; output_channel_index < output_channels; ++output_channel_index) { + const uint32_t secondary_channel_index = std::min(output_channel_index, max_secondary_channel_index); + const int32_t secondary_sample = secondary_buffer[frames_index * secondary_channels + secondary_channel_index]; + + const uint32_t primary_channel_index = std::min(output_channel_index, max_primary_channel_index); + const int32_t primary_sample = + static_cast(primary_buffer[frames_index * primary_channels + primary_channel_index]); + + const int32_t added_sample = secondary_sample + primary_sample; + + output_buffer[frames_index * output_channels + output_channel_index] = + static_cast(clamp(added_sample, MIN_AUDIO_SAMPLE_VALUE, MAX_AUDIO_SAMPLE_VALUE)); + } + } +} + +void MixerSpeaker::audio_mixer_task(void *params) { + MixerSpeaker *this_mixer = (MixerSpeaker *) params; + + xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_STARTING); + + this_mixer->task_created_ = true; + + std::unique_ptr output_transfer_buffer = audio::AudioSinkTransferBuffer::create( + this_mixer->audio_stream_info_.value().ms_to_bytes(TRANSFER_BUFFER_DURATION_MS)); + + if (output_transfer_buffer == nullptr) { + xEventGroupSetBits(this_mixer->event_group_, + MixerEventGroupBits::STATE_STOPPED | MixerEventGroupBits::ERR_ESP_NO_MEM); + + this_mixer->task_created_ = false; + vTaskDelete(nullptr); + } + + output_transfer_buffer->set_sink(this_mixer->output_speaker_); + + xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_RUNNING); + + bool sent_finished = false; + + while (true) { + uint32_t event_group_bits = xEventGroupGetBits(this_mixer->event_group_); + if (event_group_bits & MixerEventGroupBits::COMMAND_STOP) { + break; + } + + output_transfer_buffer->transfer_data_to_sink(pdMS_TO_TICKS(TASK_DELAY_MS)); + + const uint32_t output_frames_free = + this_mixer->audio_stream_info_.value().bytes_to_frames(output_transfer_buffer->free()); + + std::vector speakers_with_data; + std::vector> transfer_buffers_with_data; + + for (auto &speaker : this_mixer->source_speakers_) { + if (speaker->get_transfer_buffer().use_count() > 0) { + std::shared_ptr transfer_buffer = speaker->get_transfer_buffer().lock(); + speaker->process_data_from_source(0); // Transfers and ducks audio from source ring buffers + + if ((transfer_buffer->available() > 0) && !speaker->get_pause_state()) { + // Store the locked transfer buffers in their own vector to avoid releasing ownership until after the loop + transfer_buffers_with_data.push_back(transfer_buffer); + speakers_with_data.push_back(speaker); + } + } + } + + if (transfer_buffers_with_data.empty()) { + // No audio available for transferring, block task temporarily + delay(TASK_DELAY_MS); + continue; + } + + uint32_t frames_to_mix = output_frames_free; + + if ((transfer_buffers_with_data.size() == 1) || this_mixer->queue_mode_) { + // Only one speaker has audio data, just copy samples over + + audio::AudioStreamInfo active_stream_info = speakers_with_data[0]->get_audio_stream_info(); + + if (active_stream_info.get_sample_rate() == + this_mixer->output_speaker_->get_audio_stream_info().get_sample_rate()) { + // Speaker's sample rate matches the output speaker's, copy directly + + const uint32_t frames_available_in_buffer = + active_stream_info.bytes_to_frames(transfer_buffers_with_data[0]->available()); + frames_to_mix = std::min(frames_to_mix, frames_available_in_buffer); + copy_frames(reinterpret_cast(transfer_buffers_with_data[0]->get_buffer_start()), active_stream_info, + reinterpret_cast(output_transfer_buffer->get_buffer_end()), + this_mixer->audio_stream_info_.value(), frames_to_mix); + + // Update source speaker buffer length + transfer_buffers_with_data[0]->decrease_buffer_length(active_stream_info.frames_to_bytes(frames_to_mix)); + speakers_with_data[0]->accumulated_frames_read_ += frames_to_mix; + + // Add new audio duration to the source speaker pending playback + speakers_with_data[0]->pending_playback_ms_ += + active_stream_info.frames_to_milliseconds_with_remainder(&speakers_with_data[0]->accumulated_frames_read_); + + // Update output transfer buffer length + output_transfer_buffer->increase_buffer_length( + this_mixer->audio_stream_info_.value().frames_to_bytes(frames_to_mix)); + } else { + // Speaker's stream info doesn't match the output speaker's, so it's a new source speaker + if (!this_mixer->output_speaker_->is_stopped()) { + if (!sent_finished) { + this_mixer->output_speaker_->finish(); + sent_finished = true; // Avoid repeatedly sending the finish command + } + } else { + // Speaker has finished writing the current audio, update the stream information and restart the speaker + this_mixer->audio_stream_info_ = + audio::AudioStreamInfo(active_stream_info.get_bits_per_sample(), this_mixer->output_channels_, + active_stream_info.get_sample_rate()); + this_mixer->output_speaker_->set_audio_stream_info(this_mixer->audio_stream_info_.value()); + this_mixer->output_speaker_->start(); + sent_finished = false; + } + } + } else { + // Determine how many frames to mix + for (int i = 0; i < transfer_buffers_with_data.size(); ++i) { + const uint32_t frames_available_in_buffer = + speakers_with_data[i]->get_audio_stream_info().bytes_to_frames(transfer_buffers_with_data[i]->available()); + frames_to_mix = std::min(frames_to_mix, frames_available_in_buffer); + } + int16_t *primary_buffer = reinterpret_cast(transfer_buffers_with_data[0]->get_buffer_start()); + audio::AudioStreamInfo primary_stream_info = speakers_with_data[0]->get_audio_stream_info(); + + // Mix two streams together + for (int i = 1; i < transfer_buffers_with_data.size(); ++i) { + mix_audio_samples(primary_buffer, primary_stream_info, + reinterpret_cast(transfer_buffers_with_data[i]->get_buffer_start()), + speakers_with_data[i]->get_audio_stream_info(), + reinterpret_cast(output_transfer_buffer->get_buffer_end()), + this_mixer->audio_stream_info_.value(), frames_to_mix); + + speakers_with_data[i]->pending_playback_ms_ += + speakers_with_data[i]->get_audio_stream_info().frames_to_milliseconds_with_remainder( + &speakers_with_data[i]->accumulated_frames_read_); + + if (i != transfer_buffers_with_data.size() - 1) { + // Need to mix more streams together, point primary buffer and stream info to the already mixed output + primary_buffer = reinterpret_cast(output_transfer_buffer->get_buffer_end()); + primary_stream_info = this_mixer->audio_stream_info_.value(); + } + } + + // Update source transfer buffer lengths and add new audio durations to the source speaker pending playbacks + for (int i = 0; i < transfer_buffers_with_data.size(); ++i) { + transfer_buffers_with_data[i]->decrease_buffer_length( + speakers_with_data[i]->get_audio_stream_info().frames_to_bytes(frames_to_mix)); + speakers_with_data[i]->accumulated_frames_read_ += frames_to_mix; + + speakers_with_data[i]->pending_playback_ms_ += + speakers_with_data[i]->get_audio_stream_info().frames_to_milliseconds_with_remainder( + &speakers_with_data[i]->accumulated_frames_read_); + } + + // Update output transfer buffer length + output_transfer_buffer->increase_buffer_length( + this_mixer->audio_stream_info_.value().frames_to_bytes(frames_to_mix)); + } + } + + xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_STOPPING); + + output_transfer_buffer.reset(); + + xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_STOPPED); + this_mixer->task_created_ = false; + vTaskDelete(nullptr); +} + +} // namespace mixer_speaker +} // namespace esphome + +#endif diff --git a/esphome/components/mixer/speaker/mixer_speaker.h b/esphome/components/mixer/speaker/mixer_speaker.h new file mode 100644 index 0000000000..b2cb3e1e39 --- /dev/null +++ b/esphome/components/mixer/speaker/mixer_speaker.h @@ -0,0 +1,207 @@ +#pragma once + +#ifdef USE_ESP32 + +#include "esphome/components/audio/audio.h" +#include "esphome/components/audio/audio_transfer_buffer.h" +#include "esphome/components/speaker/speaker.h" + +#include "esphome/core/component.h" + +#include +#include + +namespace esphome { +namespace mixer_speaker { + +/* Classes for mixing several source speaker audio streams and writing it to another speaker component. + * - Volume controls are passed through to the output speaker + * - Directly handles pausing at the SourceSpeaker level; pause state is not passed through to the output speaker. + * - Audio sent to the SourceSpeaker's must have 16 bits per sample. + * - Audio sent to the SourceSpeaker can have any number of channels. They are duplicated or ignored as needed to match + * the number of channels required for the output speaker. + * - In queue mode, the audio sent to the SoureSpeakers can have different sample rates. + * - In non-queue mode, the audio sent to the SourceSpeakers must have the same sample rates. + * - SourceSpeaker has an internal ring buffer. It also allocates a shared_ptr for an AudioTranserBuffer object. + * - Audio Data Flow: + * - Audio data played on a SourceSpeaker first writes to its internal ring buffer. + * - MixerSpeaker task temporarily takes shared ownership of each SourceSpeaker's AudioTransferBuffer. + * - MixerSpeaker calls SourceSpeaker's `process_data_from_source`, which tranfers audio from the SourceSpeaker's + * ring buffer to its AudioTransferBuffer. Audio ducking is applied at this step. + * - In queue mode, MixerSpeaker prioritizes the earliest configured SourceSpeaker with audio data. Audio data is + * sent to the output speaker. + * - In non-queue mode, MixerSpeaker adds all the audio data in each SourceSpeaker into one stream that is written + * to the output speaker. + */ + +class MixerSpeaker; + +class SourceSpeaker : public speaker::Speaker, public Component { + public: + void dump_config() override; + void setup() override; + void loop() override; + + size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override; + size_t play(const uint8_t *data, size_t length) override { return this->play(data, length, 0); } + + void start() override; + void stop() override; + void finish() override; + + bool has_buffered_data() const override; + + /// @brief Mute state changes are passed to the parent's output speaker + void set_mute_state(bool mute_state) override; + + /// @brief Volume state changes are passed to the parent's output speaker + void set_volume(float volume) override; + + void set_pause_state(bool pause_state) override { this->pause_state_ = pause_state; } + bool get_pause_state() const override { return this->pause_state_; } + + /// @brief Transfers audio from the ring buffer into the transfer buffer. Ducks audio while transferring. + /// @param ticks_to_wait FreeRTOS ticks to wait while waiting to read from the ring buffer. + /// @return Number of bytes transferred from the ring buffer. + size_t process_data_from_source(TickType_t ticks_to_wait); + + /// @brief Sets the ducking level for the source speaker. + /// @param decibel_reduction (uint8_t) The dB reduction level. For example, 0 is no change, 10 is a reduction by 10 dB + /// @param duration (uint32_t) The number of milliseconds to transition from the current level to the new level + void apply_ducking(uint8_t decibel_reduction, uint32_t duration); + + void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; } + void set_parent(MixerSpeaker *parent) { this->parent_ = parent; } + void set_timeout(uint32_t ms) { this->timeout_ms_ = ms; } + + std::weak_ptr get_transfer_buffer() { return this->transfer_buffer_; } + + protected: + friend class MixerSpeaker; + esp_err_t start_(); + void stop_(); + + /// @brief Ducks audio samples by a specified amount. When changing the ducking amount, it can transition gradually + /// over a specified amount of samples. + /// @param input_buffer buffer with audio samples to be ducked in place + /// @param input_samples_to_duck number of samples to process in ``input_buffer`` + /// @param current_ducking_db_reduction pointer to the current dB reduction + /// @param ducking_transition_samples_remaining pointer to the total number of samples left before the the + /// transition is finished + /// @param samples_per_ducking_step total number of samples per ducking step for the transition + /// @param db_change_per_ducking_step the change in dB reduction per step + static void duck_samples(int16_t *input_buffer, uint32_t input_samples_to_duck, int8_t *current_ducking_db_reduction, + uint32_t *ducking_transition_samples_remaining, uint32_t samples_per_ducking_step, + int8_t db_change_per_ducking_step); + + MixerSpeaker *parent_; + + std::shared_ptr transfer_buffer_; + std::weak_ptr ring_buffer_; + + uint32_t buffer_duration_ms_; + uint32_t last_seen_data_ms_{0}; + optional timeout_ms_; + bool stop_gracefully_{false}; + + bool pause_state_{false}; + + int8_t target_ducking_db_reduction_{0}; + int8_t current_ducking_db_reduction_{0}; + int8_t db_change_per_ducking_step_{1}; + uint32_t ducking_transition_samples_remaining_{0}; + uint32_t samples_per_ducking_step_{0}; + + uint32_t accumulated_frames_read_{0}; + + uint32_t pending_playback_ms_{0}; +}; + +class MixerSpeaker : public Component { + public: + void dump_config() override; + void setup() override; + void loop() override; + + void add_source_speaker(SourceSpeaker *source_speaker) { this->source_speakers_.push_back(source_speaker); } + + /// @brief Starts the mixer task. Called by a source speaker giving the current audio stream information + /// @param stream_info The calling source speakers audio stream information + /// @return ESP_ERR_NOT_SUPPORTED if the incoming stream is incompatible due to unsupported bits per sample + /// ESP_ERR_INVALID_ARG if the incoming stream is incompatible to be mixed with the other input audio stream + /// ESP_ERR_NO_MEM if there isn't enough memory for the task's stack + /// ESP_ERR_INVALID_STATE if the task fails to start + /// ESP_OK if the incoming stream is compatible and the mixer task starts + esp_err_t start(audio::AudioStreamInfo &stream_info); + + void stop(); + + void set_output_channels(uint8_t output_channels) { this->output_channels_ = output_channels; } + void set_output_speaker(speaker::Speaker *speaker) { this->output_speaker_ = speaker; } + void set_queue_mode(bool queue_mode) { this->queue_mode_ = queue_mode; } + void set_task_stack_in_psram(bool task_stack_in_psram) { this->task_stack_in_psram_ = task_stack_in_psram; } + + speaker::Speaker *get_output_speaker() const { return this->output_speaker_; } + + protected: + /// @brief Copies audio frames from the input buffer to the output buffer taking into account the number of channels + /// in each stream. If the output stream has more channels, the input samples are duplicated. If the output stream has + /// less channels, the extra channel input samples are dropped. + /// @param input_buffer + /// @param input_stream_info + /// @param output_buffer + /// @param output_stream_info + /// @param frames_to_transfer number of frames (consisting of a sample for each channel) to copy from the input buffer + static void copy_frames(const int16_t *input_buffer, audio::AudioStreamInfo input_stream_info, int16_t *output_buffer, + audio::AudioStreamInfo output_stream_info, uint32_t frames_to_transfer); + + /// @brief Mixes the primary and secondary streams taking into account the number of channels in each stream. Primary + /// and secondary samples are duplicated or dropped as necessary to ensure the output stream has the configured number + /// of channels. Output samples are clamped to the corresponding int16 min or max values if the mixed sample + /// overflows. + /// @param primary_buffer (int16_t *) samples buffer for the primary stream + /// @param primary_stream_info stream info for the primary stream + /// @param secondary_buffer (int16_t *) samples buffer for secondary stream + /// @param secondary_stream_info stream info for the secondary stream + /// @param output_buffer (int16_t *) buffer for the mixed samples + /// @param output_stream_info stream info for the output buffer + /// @param frames_to_mix number of frames in the primary and secondary buffers to mix together + static void mix_audio_samples(const int16_t *primary_buffer, audio::AudioStreamInfo primary_stream_info, + const int16_t *secondary_buffer, audio::AudioStreamInfo secondary_stream_info, + int16_t *output_buffer, audio::AudioStreamInfo output_stream_info, + uint32_t frames_to_mix); + + static void audio_mixer_task(void *params); + + /// @brief Starts the mixer task after allocating memory for the task stack. + /// @return ESP_ERR_NO_MEM if there isn't enough memory for the task's stack + /// ESP_ERR_INVALID_STATE if the task didn't start + /// ESP_OK if successful + esp_err_t start_task_(); + + /// @brief If the task is stopped, it sets the task handle to the nullptr and deallocates its stack + /// @return ESP_OK if the task was stopped, ESP_ERR_INVALID_STATE otherwise. + esp_err_t delete_task_(); + + EventGroupHandle_t event_group_{nullptr}; + + std::vector source_speakers_; + speaker::Speaker *output_speaker_{nullptr}; + + uint8_t output_channels_; + bool queue_mode_; + bool task_stack_in_psram_{false}; + + bool task_created_{false}; + + TaskHandle_t task_handle_{nullptr}; + StaticTask_t task_stack_; + StackType_t *task_stack_buffer_{nullptr}; + + optional audio_stream_info_; +}; + +} // namespace mixer_speaker +} // namespace esphome + +#endif diff --git a/tests/components/mixer/common.yaml b/tests/components/mixer/common.yaml new file mode 100644 index 0000000000..e171b9499c --- /dev/null +++ b/tests/components/mixer/common.yaml @@ -0,0 +1,23 @@ +esphome: + on_boot: + then: + - mixer_speaker.apply_ducking: + id: source_speaker_1_id + decibel_reduction: 10 + duration: 1s + +i2s_audio: + i2s_lrclk_pin: ${lrclk_pin} + i2s_bclk_pin: ${bclk_pin} + i2s_mclk_pin: ${mclk_pin} + +speaker: + - platform: i2s_audio + id: speaker_id + dac_type: external + i2s_dout_pin: ${dout_pin} + - platform: mixer + output_speaker: speaker_id + source_speakers: + - id: source_speaker_1_id + - id: source_speaker_2_id diff --git a/tests/components/mixer/test.esp32-ard.yaml b/tests/components/mixer/test.esp32-ard.yaml new file mode 100644 index 0000000000..96d2d37458 --- /dev/null +++ b/tests/components/mixer/test.esp32-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO16 + bclk_pin: GPIO17 + mclk_pin: GPIO15 + dout_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-c3-ard.yaml b/tests/components/mixer/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/mixer/test.esp32-c3-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-c3-idf.yaml b/tests/components/mixer/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/mixer/test.esp32-c3-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-idf.yaml b/tests/components/mixer/test.esp32-idf.yaml new file mode 100644 index 0000000000..96d2d37458 --- /dev/null +++ b/tests/components/mixer/test.esp32-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO16 + bclk_pin: GPIO17 + mclk_pin: GPIO15 + dout_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-s3-ard.yaml b/tests/components/mixer/test.esp32-s3-ard.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/mixer/test.esp32-s3-ard.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/mixer/test.esp32-s3-idf.yaml b/tests/components/mixer/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..f1721f0862 --- /dev/null +++ b/tests/components/mixer/test.esp32-s3-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + lrclk_pin: GPIO4 + bclk_pin: GPIO5 + mclk_pin: GPIO6 + dout_pin: GPIO7 + +<<: !include common.yaml From d4ac2d3c7edfa7a4a30b50820e0eeb9bd198b036 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:04:53 -0600 Subject: [PATCH 0951/1052] [CI] Consolidate some tests (A) (#8184) --- tests/components/a02yyuw/common.yaml | 11 +++ tests/components/a02yyuw/test.esp32-ard.yaml | 16 +--- .../components/a02yyuw/test.esp32-c3-ard.yaml | 16 +--- .../components/a02yyuw/test.esp32-c3-idf.yaml | 16 +--- tests/components/a02yyuw/test.esp32-idf.yaml | 16 +--- .../components/a02yyuw/test.esp8266-ard.yaml | 16 +--- tests/components/a02yyuw/test.rp2040-ard.yaml | 16 +--- tests/components/a4988/common.yaml | 9 ++ tests/components/a4988/test.esp32-ard.yaml | 18 ++-- tests/components/a4988/test.esp32-c3-ard.yaml | 18 ++-- tests/components/a4988/test.esp32-c3-idf.yaml | 18 ++-- tests/components/a4988/test.esp32-idf.yaml | 18 ++-- tests/components/a4988/test.esp8266-ard.yaml | 18 ++-- tests/components/a4988/test.rp2040-ard.yaml | 18 ++-- tests/components/ac_dimmer/common.yaml | 5 + .../components/ac_dimmer/test.esp32-ard.yaml | 12 +-- .../ac_dimmer/test.esp32-c3-ard.yaml | 12 +-- .../ac_dimmer/test.esp8266-ard.yaml | 12 +-- .../components/ac_dimmer/test.rp2040-ard.yaml | 12 +-- tests/components/adc/test.esp32-c3-ard.yaml | 2 +- tests/components/adc/test.esp32-s2-ard.yaml | 2 +- tests/components/adc/test.esp32-s3-ard.yaml | 2 +- tests/components/adc128s102/common.yaml | 14 +++ .../components/adc128s102/test.esp32-ard.yaml | 19 ++-- .../adc128s102/test.esp32-c3-ard.yaml | 19 ++-- .../adc128s102/test.esp32-c3-idf.yaml | 19 ++-- .../components/adc128s102/test.esp32-idf.yaml | 19 ++-- .../adc128s102/test.esp8266-ard.yaml | 19 ++-- .../adc128s102/test.rp2040-ard.yaml | 19 ++-- ...ml => common-ard-esp32_rmt_led_strip.yaml} | 2 +- ...esp32-ard.yaml => common-ard-fastled.yaml} | 2 +- ...ml => common-idf-esp32_rmt_led_strip.yaml} | 2 +- .../esp32_rmt_led_strip.esp32-ard.yaml | 4 + .../esp32_rmt_led_strip.esp32-c3-ard.yaml | 4 + .../esp32_rmt_led_strip.esp32-c3-idf.yaml | 4 + .../esp32_rmt_led_strip.esp32-idf.yaml | 4 + .../fastled_clockless.esp32-ard.yaml | 4 + .../addressable_light/test.esp32-idf.yaml | 30 ------ tests/components/ade7953_i2c/common.yaml | 34 +++++++ .../ade7953_i2c/test.esp32-ard.yaml | 38 +------- .../ade7953_i2c/test.esp32-c3-ard.yaml | 38 +------- .../ade7953_i2c/test.esp32-c3-idf.yaml | 38 +------- .../ade7953_i2c/test.esp32-idf.yaml | 38 +------- .../ade7953_i2c/test.esp8266-ard.yaml | 38 +------- .../ade7953_i2c/test.rp2040-ard.yaml | 38 +------- tests/components/ade7953_spi/common.yaml | 36 +++++++ .../ade7953_spi/test.esp32-ard.yaml | 42 ++------ .../ade7953_spi/test.esp32-c3-ard.yaml | 42 ++------ .../ade7953_spi/test.esp32-c3-idf.yaml | 42 ++------ .../ade7953_spi/test.esp32-idf.yaml | 42 ++------ .../ade7953_spi/test.esp8266-ard.yaml | 42 ++------ .../ade7953_spi/test.rp2040-ard.yaml | 42 ++------ tests/components/ads1115/common.yaml | 14 +++ tests/components/ads1115/test.esp32-ard.yaml | 17 +--- .../components/ads1115/test.esp32-c3-ard.yaml | 17 +--- .../components/ads1115/test.esp32-c3-idf.yaml | 17 +--- tests/components/ads1115/test.esp32-idf.yaml | 17 +--- .../components/ads1115/test.esp8266-ard.yaml | 17 +--- tests/components/ads1115/test.rp2040-ard.yaml | 17 +--- tests/components/ags10/common.yaml | 12 +++ tests/components/ags10/test.esp32-ard.yaml | 15 +-- tests/components/ags10/test.esp32-c3-ard.yaml | 15 +-- tests/components/ags10/test.esp32-c3-idf.yaml | 15 +-- tests/components/ags10/test.esp32-idf.yaml | 15 +-- tests/components/ags10/test.esp8266-ard.yaml | 15 +-- tests/components/aht10/common.yaml | 11 +++ tests/components/aht10/test.esp32-ard.yaml | 14 +-- tests/components/aht10/test.esp32-c3-ard.yaml | 14 +-- tests/components/aht10/test.esp32-c3-idf.yaml | 14 +-- tests/components/aht10/test.esp32-idf.yaml | 14 +-- tests/components/aht10/test.esp8266-ard.yaml | 14 +-- tests/components/aht10/test.rp2040-ard.yaml | 14 +-- tests/components/am2315c/common.yaml | 11 +++ tests/components/am2315c/test.esp32-ard.yaml | 14 +-- .../components/am2315c/test.esp32-c3-ard.yaml | 14 +-- .../components/am2315c/test.esp32-c3-idf.yaml | 14 +-- tests/components/am2315c/test.esp32-idf.yaml | 14 +-- .../components/am2315c/test.esp8266-ard.yaml | 14 +-- tests/components/am2315c/test.rp2040-ard.yaml | 14 +-- tests/components/am2320/common.yaml | 11 +++ tests/components/am2320/test.esp32-ard.yaml | 14 +-- .../components/am2320/test.esp32-c3-ard.yaml | 14 +-- .../components/am2320/test.esp32-c3-idf.yaml | 14 +-- tests/components/am2320/test.esp32-idf.yaml | 14 +-- tests/components/am2320/test.esp8266-ard.yaml | 14 +-- tests/components/am2320/test.rp2040-ard.yaml | 14 +-- tests/components/apds9960/common.yaml | 48 ++++++++++ tests/components/apds9960/test.esp32-ard.yaml | 51 +--------- .../apds9960/test.esp32-c3-ard.yaml | 51 +--------- .../apds9960/test.esp32-c3-idf.yaml | 51 +--------- tests/components/apds9960/test.esp32-idf.yaml | 51 +--------- .../components/apds9960/test.esp8266-ard.yaml | 51 +--------- .../components/apds9960/test.rp2040-ard.yaml | 51 +--------- tests/components/as3935_i2c/common.yaml | 18 ++++ .../components/as3935_i2c/test.esp32-ard.yaml | 22 +---- .../as3935_i2c/test.esp32-c3-ard.yaml | 22 +---- .../as3935_i2c/test.esp32-c3-idf.yaml | 22 +---- .../components/as3935_i2c/test.esp32-idf.yaml | 22 +---- .../as3935_i2c/test.esp8266-ard.yaml | 22 +---- .../as3935_i2c/test.rp2040-ard.yaml | 22 +---- tests/components/as3935_spi/common.yaml | 20 ++++ .../components/as3935_spi/test.esp32-ard.yaml | 26 ++--- .../as3935_spi/test.esp32-c3-ard.yaml | 26 ++--- .../as3935_spi/test.esp32-c3-idf.yaml | 26 ++--- .../components/as3935_spi/test.esp32-idf.yaml | 26 ++--- .../as3935_spi/test.esp8266-ard.yaml | 26 ++--- .../as3935_spi/test.rp2040-ard.yaml | 26 ++--- tests/components/as5600/common.yaml | 27 ++++++ tests/components/as5600/test.esp32-ard.yaml | 31 +----- .../components/as5600/test.esp32-c3-ard.yaml | 31 +----- .../components/as5600/test.esp32-c3-idf.yaml | 31 +----- tests/components/as5600/test.esp32-idf.yaml | 31 +----- tests/components/as5600/test.esp8266-ard.yaml | 31 +----- tests/components/as5600/test.rp2040-ard.yaml | 31 +----- tests/components/as7341/common.yaml | 31 ++++++ tests/components/as7341/test.esp32-ard.yaml | 34 +------ .../components/as7341/test.esp32-c3-ard.yaml | 34 +------ .../components/as7341/test.esp32-c3-idf.yaml | 34 +------ tests/components/as7341/test.esp32-idf.yaml | 34 +------ tests/components/as7341/test.esp8266-ard.yaml | 34 +------ tests/components/as7341/test.rp2040-ard.yaml | 34 +------ tests/components/at581x/common.yaml | 28 ++++++ tests/components/at581x/test.esp32-ard.yaml | 41 +------- .../components/at581x/test.esp32-c3-ard.yaml | 41 +------- .../components/at581x/test.esp32-c3-idf.yaml | 41 +------- tests/components/at581x/test.esp32-idf.yaml | 41 +------- tests/components/at581x/test.esp8266-ard.yaml | 41 +------- tests/components/at581x/test.rp2040-ard.yaml | 41 +------- tests/components/atm90e26/common.yaml | 26 +++++ tests/components/atm90e26/test.esp32-ard.yaml | 31 ++---- .../atm90e26/test.esp32-c3-ard.yaml | 31 ++---- .../atm90e26/test.esp32-c3-idf.yaml | 31 ++---- tests/components/atm90e26/test.esp32-idf.yaml | 31 ++---- .../components/atm90e26/test.esp8266-ard.yaml | 31 ++---- .../components/atm90e26/test.rp2040-ard.yaml | 31 ++---- tests/components/atm90e32/common.yaml | 61 ++++++++++++ tests/components/atm90e32/test.esp32-ard.yaml | 65 ++----------- .../atm90e32/test.esp32-c3-ard.yaml | 65 ++----------- .../atm90e32/test.esp32-c3-idf.yaml | 65 ++----------- tests/components/atm90e32/test.esp32-idf.yaml | 65 ++----------- .../components/atm90e32/test.esp8266-ard.yaml | 96 ++----------------- .../components/atm90e32/test.rp2040-ard.yaml | 65 ++----------- 142 files changed, 1003 insertions(+), 2598 deletions(-) create mode 100644 tests/components/a02yyuw/common.yaml create mode 100644 tests/components/a4988/common.yaml create mode 100644 tests/components/ac_dimmer/common.yaml create mode 100644 tests/components/adc128s102/common.yaml rename tests/components/addressable_light/{test.esp32-c3-ard.yaml => common-ard-esp32_rmt_led_strip.yaml} (98%) rename tests/components/addressable_light/{test.esp32-ard.yaml => common-ard-fastled.yaml} (98%) rename tests/components/addressable_light/{test.esp32-c3-idf.yaml => common-idf-esp32_rmt_led_strip.yaml} (98%) create mode 100644 tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml create mode 100644 tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml create mode 100644 tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-idf.yaml create mode 100644 tests/components/addressable_light/esp32_rmt_led_strip.esp32-idf.yaml create mode 100644 tests/components/addressable_light/fastled_clockless.esp32-ard.yaml delete mode 100644 tests/components/addressable_light/test.esp32-idf.yaml create mode 100644 tests/components/ade7953_i2c/common.yaml create mode 100644 tests/components/ade7953_spi/common.yaml create mode 100644 tests/components/ads1115/common.yaml create mode 100644 tests/components/ags10/common.yaml create mode 100644 tests/components/aht10/common.yaml create mode 100644 tests/components/am2315c/common.yaml create mode 100644 tests/components/am2320/common.yaml create mode 100644 tests/components/apds9960/common.yaml create mode 100644 tests/components/as3935_i2c/common.yaml create mode 100644 tests/components/as3935_spi/common.yaml create mode 100644 tests/components/as5600/common.yaml create mode 100644 tests/components/as7341/common.yaml create mode 100644 tests/components/at581x/common.yaml create mode 100644 tests/components/atm90e26/common.yaml create mode 100644 tests/components/atm90e32/common.yaml diff --git a/tests/components/a02yyuw/common.yaml b/tests/components/a02yyuw/common.yaml new file mode 100644 index 0000000000..b2e5927ff4 --- /dev/null +++ b/tests/components/a02yyuw/common.yaml @@ -0,0 +1,11 @@ +uart: + - id: uart_a02yyuw + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: a02yyuw + id: a02yyuw_sensor + name: a02yyuw Distance + uart_id: uart_a02yyuw diff --git a/tests/components/a02yyuw/test.esp32-ard.yaml b/tests/components/a02yyuw/test.esp32-ard.yaml index 98d6a266b3..f486544afa 100644 --- a/tests/components/a02yyuw/test.esp32-ard.yaml +++ b/tests/components/a02yyuw/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_a02yyuw - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: a02yyuw - id: a02yyuw_sensor - name: a02yyuw Distance - uart_id: uart_a02yyuw +<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp32-c3-ard.yaml b/tests/components/a02yyuw/test.esp32-c3-ard.yaml index 76e1ad8ee1..b516342f3b 100644 --- a/tests/components/a02yyuw/test.esp32-c3-ard.yaml +++ b/tests/components/a02yyuw/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_a02yyuw - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: a02yyuw - id: a02yyuw_sensor - name: a02yyuw Distance - uart_id: uart_a02yyuw +<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp32-c3-idf.yaml b/tests/components/a02yyuw/test.esp32-c3-idf.yaml index 76e1ad8ee1..b516342f3b 100644 --- a/tests/components/a02yyuw/test.esp32-c3-idf.yaml +++ b/tests/components/a02yyuw/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_a02yyuw - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: a02yyuw - id: a02yyuw_sensor - name: a02yyuw Distance - uart_id: uart_a02yyuw +<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp32-idf.yaml b/tests/components/a02yyuw/test.esp32-idf.yaml index 98d6a266b3..f486544afa 100644 --- a/tests/components/a02yyuw/test.esp32-idf.yaml +++ b/tests/components/a02yyuw/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_a02yyuw - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: a02yyuw - id: a02yyuw_sensor - name: a02yyuw Distance - uart_id: uart_a02yyuw +<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.esp8266-ard.yaml b/tests/components/a02yyuw/test.esp8266-ard.yaml index 76e1ad8ee1..b516342f3b 100644 --- a/tests/components/a02yyuw/test.esp8266-ard.yaml +++ b/tests/components/a02yyuw/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_a02yyuw - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: a02yyuw - id: a02yyuw_sensor - name: a02yyuw Distance - uart_id: uart_a02yyuw +<<: !include common.yaml diff --git a/tests/components/a02yyuw/test.rp2040-ard.yaml b/tests/components/a02yyuw/test.rp2040-ard.yaml index 76e1ad8ee1..b516342f3b 100644 --- a/tests/components/a02yyuw/test.rp2040-ard.yaml +++ b/tests/components/a02yyuw/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_a02yyuw - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: a02yyuw - id: a02yyuw_sensor - name: a02yyuw Distance - uart_id: uart_a02yyuw +<<: !include common.yaml diff --git a/tests/components/a4988/common.yaml b/tests/components/a4988/common.yaml new file mode 100644 index 0000000000..b85f2f41c6 --- /dev/null +++ b/tests/components/a4988/common.yaml @@ -0,0 +1,9 @@ +stepper: + - platform: a4988 + id: a4988_stepper + step_pin: ${step_pin} + dir_pin: ${dir_pin} + sleep_pin: ${sleep_pin} + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/a4988/test.esp32-ard.yaml b/tests/components/a4988/test.esp32-ard.yaml index 0ca5e3f504..1ca8c0c084 100644 --- a/tests/components/a4988/test.esp32-ard.yaml +++ b/tests/components/a4988/test.esp32-ard.yaml @@ -1,12 +1,6 @@ -stepper: - - platform: a4988 - id: a4988_stepper - step_pin: - number: 22 - dir_pin: - number: 23 - sleep_pin: - number: 25 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +substitutions: + step_pin: GPIO22 + dir_pin: GPIO23 + sleep_pin: GPIO25 + +<<: !include common.yaml diff --git a/tests/components/a4988/test.esp32-c3-ard.yaml b/tests/components/a4988/test.esp32-c3-ard.yaml index af4e4fa32b..25caba75b5 100644 --- a/tests/components/a4988/test.esp32-c3-ard.yaml +++ b/tests/components/a4988/test.esp32-c3-ard.yaml @@ -1,12 +1,6 @@ -stepper: - - platform: a4988 - id: a4988_stepper - step_pin: - number: 2 - dir_pin: - number: 3 - sleep_pin: - number: 5 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +substitutions: + step_pin: GPIO2 + dir_pin: GPIO3 + sleep_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/a4988/test.esp32-c3-idf.yaml b/tests/components/a4988/test.esp32-c3-idf.yaml index af4e4fa32b..25caba75b5 100644 --- a/tests/components/a4988/test.esp32-c3-idf.yaml +++ b/tests/components/a4988/test.esp32-c3-idf.yaml @@ -1,12 +1,6 @@ -stepper: - - platform: a4988 - id: a4988_stepper - step_pin: - number: 2 - dir_pin: - number: 3 - sleep_pin: - number: 5 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +substitutions: + step_pin: GPIO2 + dir_pin: GPIO3 + sleep_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/a4988/test.esp32-idf.yaml b/tests/components/a4988/test.esp32-idf.yaml index 0ca5e3f504..1ca8c0c084 100644 --- a/tests/components/a4988/test.esp32-idf.yaml +++ b/tests/components/a4988/test.esp32-idf.yaml @@ -1,12 +1,6 @@ -stepper: - - platform: a4988 - id: a4988_stepper - step_pin: - number: 22 - dir_pin: - number: 23 - sleep_pin: - number: 25 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +substitutions: + step_pin: GPIO22 + dir_pin: GPIO23 + sleep_pin: GPIO25 + +<<: !include common.yaml diff --git a/tests/components/a4988/test.esp8266-ard.yaml b/tests/components/a4988/test.esp8266-ard.yaml index f4c1886fc5..22b5677d27 100644 --- a/tests/components/a4988/test.esp8266-ard.yaml +++ b/tests/components/a4988/test.esp8266-ard.yaml @@ -1,12 +1,6 @@ -stepper: - - platform: a4988 - id: a4988_stepper - step_pin: - number: 1 - dir_pin: - number: 2 - sleep_pin: - number: 5 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +substitutions: + step_pin: GPIO1 + dir_pin: GPIO2 + sleep_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/a4988/test.rp2040-ard.yaml b/tests/components/a4988/test.rp2040-ard.yaml index af4e4fa32b..25caba75b5 100644 --- a/tests/components/a4988/test.rp2040-ard.yaml +++ b/tests/components/a4988/test.rp2040-ard.yaml @@ -1,12 +1,6 @@ -stepper: - - platform: a4988 - id: a4988_stepper - step_pin: - number: 2 - dir_pin: - number: 3 - sleep_pin: - number: 5 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +substitutions: + step_pin: GPIO2 + dir_pin: GPIO3 + sleep_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/ac_dimmer/common.yaml b/tests/components/ac_dimmer/common.yaml new file mode 100644 index 0000000000..8f93066838 --- /dev/null +++ b/tests/components/ac_dimmer/common.yaml @@ -0,0 +1,5 @@ +output: + - platform: ac_dimmer + id: ac_dimmer_1 + gate_pin: ${gate_pin} + zero_cross_pin: ${zero_cross_pin} diff --git a/tests/components/ac_dimmer/test.esp32-ard.yaml b/tests/components/ac_dimmer/test.esp32-ard.yaml index cc17201666..3ec069f430 100644 --- a/tests/components/ac_dimmer/test.esp32-ard.yaml +++ b/tests/components/ac_dimmer/test.esp32-ard.yaml @@ -1,7 +1,5 @@ -output: - - platform: ac_dimmer - id: ac_dimmer_1 - gate_pin: - number: 12 - zero_cross_pin: - number: 13 +substitutions: + gate_pin: GPIO18 + zero_cross_pin: GPIO19 + +<<: !include common.yaml diff --git a/tests/components/ac_dimmer/test.esp32-c3-ard.yaml b/tests/components/ac_dimmer/test.esp32-c3-ard.yaml index f411d376be..5d2d42b713 100644 --- a/tests/components/ac_dimmer/test.esp32-c3-ard.yaml +++ b/tests/components/ac_dimmer/test.esp32-c3-ard.yaml @@ -1,7 +1,5 @@ -output: - - platform: ac_dimmer - id: ac_dimmer_1 - gate_pin: - number: 5 - zero_cross_pin: - number: 6 +substitutions: + gate_pin: GPIO5 + zero_cross_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ac_dimmer/test.esp8266-ard.yaml b/tests/components/ac_dimmer/test.esp8266-ard.yaml index af18d11c5f..5d2d42b713 100644 --- a/tests/components/ac_dimmer/test.esp8266-ard.yaml +++ b/tests/components/ac_dimmer/test.esp8266-ard.yaml @@ -1,7 +1,5 @@ -output: - - platform: ac_dimmer - id: ac_dimmer_1 - gate_pin: - number: 5 - zero_cross_pin: - number: 4 +substitutions: + gate_pin: GPIO5 + zero_cross_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ac_dimmer/test.rp2040-ard.yaml b/tests/components/ac_dimmer/test.rp2040-ard.yaml index f411d376be..5d2d42b713 100644 --- a/tests/components/ac_dimmer/test.rp2040-ard.yaml +++ b/tests/components/ac_dimmer/test.rp2040-ard.yaml @@ -1,7 +1,5 @@ -output: - - platform: ac_dimmer - id: ac_dimmer_1 - gate_pin: - number: 5 - zero_cross_pin: - number: 6 +substitutions: + gate_pin: GPIO5 + zero_cross_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/adc/test.esp32-c3-ard.yaml b/tests/components/adc/test.esp32-c3-ard.yaml index 18e5ab3561..e74477c582 100644 --- a/tests/components/adc/test.esp32-c3-ard.yaml +++ b/tests/components/adc/test.esp32-c3-ard.yaml @@ -2,4 +2,4 @@ sensor: - platform: adc id: my_sensor pin: 4 - attenuation: 11db + attenuation: 12db diff --git a/tests/components/adc/test.esp32-s2-ard.yaml b/tests/components/adc/test.esp32-s2-ard.yaml index 0119ad5e4d..e1a6bc22e5 100644 --- a/tests/components/adc/test.esp32-s2-ard.yaml +++ b/tests/components/adc/test.esp32-s2-ard.yaml @@ -2,4 +2,4 @@ sensor: - platform: adc id: my_sensor pin: 1 - attenuation: 11db + attenuation: 12db diff --git a/tests/components/adc/test.esp32-s3-ard.yaml b/tests/components/adc/test.esp32-s3-ard.yaml index 0119ad5e4d..e1a6bc22e5 100644 --- a/tests/components/adc/test.esp32-s3-ard.yaml +++ b/tests/components/adc/test.esp32-s3-ard.yaml @@ -2,4 +2,4 @@ sensor: - platform: adc id: my_sensor pin: 1 - attenuation: 11db + attenuation: 12db diff --git a/tests/components/adc128s102/common.yaml b/tests/components/adc128s102/common.yaml new file mode 100644 index 0000000000..5f1638a7e2 --- /dev/null +++ b/tests/components/adc128s102/common.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_adc128s102 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +adc128s102: + cs_pin: ${cs_pin} + id: adc128s102_adc + +sensor: + - platform: adc128s102 + id: adc128s102_channel_0 + channel: 0 diff --git a/tests/components/adc128s102/test.esp32-ard.yaml b/tests/components/adc128s102/test.esp32-ard.yaml index 005fbccc34..aba72f0614 100644 --- a/tests/components/adc128s102/test.esp32-ard.yaml +++ b/tests/components/adc128s102/test.esp32-ard.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_adc128s102 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO12 -adc128s102: - cs_pin: 12 - id: adc128s102_adc - -sensor: - - platform: adc128s102 - id: adc128s102_channel_0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp32-c3-ard.yaml b/tests/components/adc128s102/test.esp32-c3-ard.yaml index 8edf745e58..24da4b5452 100644 --- a/tests/components/adc128s102/test.esp32-c3-ard.yaml +++ b/tests/components/adc128s102/test.esp32-c3-ard.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_adc128s102 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO2 -adc128s102: - cs_pin: 8 - id: adc128s102_adc - -sensor: - - platform: adc128s102 - id: adc128s102_channel_0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp32-c3-idf.yaml b/tests/components/adc128s102/test.esp32-c3-idf.yaml index 8edf745e58..24da4b5452 100644 --- a/tests/components/adc128s102/test.esp32-c3-idf.yaml +++ b/tests/components/adc128s102/test.esp32-c3-idf.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_adc128s102 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO2 -adc128s102: - cs_pin: 8 - id: adc128s102_adc - -sensor: - - platform: adc128s102 - id: adc128s102_channel_0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp32-idf.yaml b/tests/components/adc128s102/test.esp32-idf.yaml index 005fbccc34..aba72f0614 100644 --- a/tests/components/adc128s102/test.esp32-idf.yaml +++ b/tests/components/adc128s102/test.esp32-idf.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_adc128s102 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO12 -adc128s102: - cs_pin: 12 - id: adc128s102_adc - -sensor: - - platform: adc128s102 - id: adc128s102_channel_0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/adc128s102/test.esp8266-ard.yaml b/tests/components/adc128s102/test.esp8266-ard.yaml index 09a51caec1..dbd158d030 100644 --- a/tests/components/adc128s102/test.esp8266-ard.yaml +++ b/tests/components/adc128s102/test.esp8266-ard.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_adc128s102 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -adc128s102: - cs_pin: 15 - id: adc128s102_adc - -sensor: - - platform: adc128s102 - id: adc128s102_channel_0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/adc128s102/test.rp2040-ard.yaml b/tests/components/adc128s102/test.rp2040-ard.yaml index a7d54cbfe6..f6c3f1eeca 100644 --- a/tests/components/adc128s102/test.rp2040-ard.yaml +++ b/tests/components/adc128s102/test.rp2040-ard.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_adc128s102 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -adc128s102: - cs_pin: 5 - id: adc128s102_adc - -sensor: - - platform: adc128s102 - id: adc128s102_channel_0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/addressable_light/test.esp32-c3-ard.yaml b/tests/components/addressable_light/common-ard-esp32_rmt_led_strip.yaml similarity index 98% rename from tests/components/addressable_light/test.esp32-c3-ard.yaml rename to tests/components/addressable_light/common-ard-esp32_rmt_led_strip.yaml index f587113fac..9c5e63cdc6 100644 --- a/tests/components/addressable_light/test.esp32-c3-ard.yaml +++ b/tests/components/addressable_light/common-ard-esp32_rmt_led_strip.yaml @@ -5,7 +5,7 @@ light: chipset: ws2812 rgb_order: GRB num_leds: 256 - pin: 2 + pin: ${pin} rmt_channel: 0 display: diff --git a/tests/components/addressable_light/test.esp32-ard.yaml b/tests/components/addressable_light/common-ard-fastled.yaml similarity index 98% rename from tests/components/addressable_light/test.esp32-ard.yaml rename to tests/components/addressable_light/common-ard-fastled.yaml index f7717be610..77a5c5fe12 100644 --- a/tests/components/addressable_light/test.esp32-ard.yaml +++ b/tests/components/addressable_light/common-ard-fastled.yaml @@ -3,7 +3,7 @@ light: id: led_matrix_32x8 name: led_matrix_32x8 chipset: WS2812B - pin: 2 + pin: ${pin} num_leds: 256 rgb_order: GRB default_transition_length: 0s diff --git a/tests/components/addressable_light/test.esp32-c3-idf.yaml b/tests/components/addressable_light/common-idf-esp32_rmt_led_strip.yaml similarity index 98% rename from tests/components/addressable_light/test.esp32-c3-idf.yaml rename to tests/components/addressable_light/common-idf-esp32_rmt_led_strip.yaml index 7b3516345d..a071f9df91 100644 --- a/tests/components/addressable_light/test.esp32-c3-idf.yaml +++ b/tests/components/addressable_light/common-idf-esp32_rmt_led_strip.yaml @@ -5,7 +5,7 @@ light: chipset: ws2812 rgb_order: GRB num_leds: 256 - pin: 2 + pin: ${pin} display: - platform: addressable_light diff --git a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml new file mode 100644 index 0000000000..d93c554dae --- /dev/null +++ b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO2 + +<<: !include common-ard-esp32_rmt_led_strip.yaml diff --git a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml new file mode 100644 index 0000000000..d93c554dae --- /dev/null +++ b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO2 + +<<: !include common-ard-esp32_rmt_led_strip.yaml diff --git a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-idf.yaml b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ca2c244d80 --- /dev/null +++ b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-c3-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO2 + +<<: !include common-idf-esp32_rmt_led_strip.yaml diff --git a/tests/components/addressable_light/esp32_rmt_led_strip.esp32-idf.yaml b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-idf.yaml new file mode 100644 index 0000000000..ca2c244d80 --- /dev/null +++ b/tests/components/addressable_light/esp32_rmt_led_strip.esp32-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO2 + +<<: !include common-idf-esp32_rmt_led_strip.yaml diff --git a/tests/components/addressable_light/fastled_clockless.esp32-ard.yaml b/tests/components/addressable_light/fastled_clockless.esp32-ard.yaml new file mode 100644 index 0000000000..78eb5d7fdb --- /dev/null +++ b/tests/components/addressable_light/fastled_clockless.esp32-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO2 + +<<: !include common-ard-fastled.yaml diff --git a/tests/components/addressable_light/test.esp32-idf.yaml b/tests/components/addressable_light/test.esp32-idf.yaml deleted file mode 100644 index 7b3516345d..0000000000 --- a/tests/components/addressable_light/test.esp32-idf.yaml +++ /dev/null @@ -1,30 +0,0 @@ -light: - - platform: esp32_rmt_led_strip - id: led_matrix_32x8 - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - -display: - - platform: addressable_light - id: led_matrix_32x8_display - addressable_light_id: led_matrix_32x8 - width: 32 - height: 8 - pixel_mapper: |- - if (x % 2 == 0) { - return (x * 8) + y; - } - return (x * 8) + (7 - y); - lambda: |- - Color red = Color(0xFF0000); - Color green = Color(0x00FF00); - Color blue = Color(0x0000FF); - it.rectangle(0, 0, it.get_width(), it.get_height(), red); - it.rectangle(1, 1, it.get_width()-2, it.get_height()-2, green); - it.rectangle(2, 2, it.get_width()-4, it.get_height()-4, blue); - it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); - rotation: 0° - update_interval: 16ms diff --git a/tests/components/ade7953_i2c/common.yaml b/tests/components/ade7953_i2c/common.yaml new file mode 100644 index 0000000000..a2d163567d --- /dev/null +++ b/tests/components/ade7953_i2c/common.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_ade7953 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ade7953_i2c + irq_pin: ${irq_pin} + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_i2c/test.esp32-ard.yaml b/tests/components/ade7953_i2c/test.esp32-ard.yaml index 71602f20a3..2c57d412f6 100644 --- a/tests/components/ade7953_i2c/test.esp32-ard.yaml +++ b/tests/components/ade7953_i2c/test.esp32-ard.yaml @@ -1,34 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO15 -sensor: - - platform: ade7953_i2c - irq_pin: 15 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp32-c3-ard.yaml b/tests/components/ade7953_i2c/test.esp32-c3-ard.yaml index d7b365a7e1..799acabd5a 100644 --- a/tests/components/ade7953_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/ade7953_i2c/test.esp32-c3-ard.yaml @@ -1,34 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -sensor: - - platform: ade7953_i2c - irq_pin: 6 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml b/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml index d7b365a7e1..799acabd5a 100644 --- a/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ade7953_i2c/test.esp32-c3-idf.yaml @@ -1,34 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -sensor: - - platform: ade7953_i2c - irq_pin: 6 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp32-idf.yaml b/tests/components/ade7953_i2c/test.esp32-idf.yaml index 71602f20a3..2c57d412f6 100644 --- a/tests/components/ade7953_i2c/test.esp32-idf.yaml +++ b/tests/components/ade7953_i2c/test.esp32-idf.yaml @@ -1,34 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO15 -sensor: - - platform: ade7953_i2c - irq_pin: 15 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.esp8266-ard.yaml b/tests/components/ade7953_i2c/test.esp8266-ard.yaml index 6903cd1953..c8e6a43f44 100644 --- a/tests/components/ade7953_i2c/test.esp8266-ard.yaml +++ b/tests/components/ade7953_i2c/test.esp8266-ard.yaml @@ -1,34 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO15 -sensor: - - platform: ade7953_i2c - irq_pin: 15 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_i2c/test.rp2040-ard.yaml b/tests/components/ade7953_i2c/test.rp2040-ard.yaml index d7b365a7e1..799acabd5a 100644 --- a/tests/components/ade7953_i2c/test.rp2040-ard.yaml +++ b/tests/components/ade7953_i2c/test.rp2040-ard.yaml @@ -1,34 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -sensor: - - platform: ade7953_i2c - irq_pin: 6 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_spi/common.yaml b/tests/components/ade7953_spi/common.yaml new file mode 100644 index 0000000000..706f31f22c --- /dev/null +++ b/tests/components/ade7953_spi/common.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ade7953 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: ade7953_spi + cs_pin: ${cs_pin} + irq_pin: ${irq_pin} + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s diff --git a/tests/components/ade7953_spi/test.esp32-ard.yaml b/tests/components/ade7953_spi/test.esp32-ard.yaml index e9ef7e4116..e00f522dd4 100644 --- a/tests/components/ade7953_spi/test.esp32-ard.yaml +++ b/tests/components/ade7953_spi/test.esp32-ard.yaml @@ -1,36 +1,8 @@ -spi: - - id: spi_ade7953 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + irq_pin: GPIO13 + cs_pin: GPIO5 -sensor: - - platform: ade7953_spi - cs_pin: 5 - irq_pin: 13 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp32-c3-ard.yaml b/tests/components/ade7953_spi/test.esp32-c3-ard.yaml index a967f28d9c..fcf35f528e 100644 --- a/tests/components/ade7953_spi/test.esp32-c3-ard.yaml +++ b/tests/components/ade7953_spi/test.esp32-c3-ard.yaml @@ -1,36 +1,8 @@ -spi: - - id: spi_ade7953 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + irq_pin: GPIO9 + cs_pin: GPIO8 -sensor: - - platform: ade7953_spi - cs_pin: 8 - irq_pin: 9 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp32-c3-idf.yaml b/tests/components/ade7953_spi/test.esp32-c3-idf.yaml index a967f28d9c..fcf35f528e 100644 --- a/tests/components/ade7953_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ade7953_spi/test.esp32-c3-idf.yaml @@ -1,36 +1,8 @@ -spi: - - id: spi_ade7953 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + irq_pin: GPIO9 + cs_pin: GPIO8 -sensor: - - platform: ade7953_spi - cs_pin: 8 - irq_pin: 9 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp32-idf.yaml b/tests/components/ade7953_spi/test.esp32-idf.yaml index e9ef7e4116..e00f522dd4 100644 --- a/tests/components/ade7953_spi/test.esp32-idf.yaml +++ b/tests/components/ade7953_spi/test.esp32-idf.yaml @@ -1,36 +1,8 @@ -spi: - - id: spi_ade7953 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + irq_pin: GPIO13 + cs_pin: GPIO5 -sensor: - - platform: ade7953_spi - cs_pin: 5 - irq_pin: 13 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.esp8266-ard.yaml b/tests/components/ade7953_spi/test.esp8266-ard.yaml index b36b4445ab..b90e661ec0 100644 --- a/tests/components/ade7953_spi/test.esp8266-ard.yaml +++ b/tests/components/ade7953_spi/test.esp8266-ard.yaml @@ -1,36 +1,8 @@ -spi: - - id: spi_ade7953 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + irq_pin: GPIO5 + cs_pin: GPIO15 -sensor: - - platform: ade7953_spi - cs_pin: 15 - irq_pin: 5 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ade7953_spi/test.rp2040-ard.yaml b/tests/components/ade7953_spi/test.rp2040-ard.yaml index 319abd4613..8f5941e1b2 100644 --- a/tests/components/ade7953_spi/test.rp2040-ard.yaml +++ b/tests/components/ade7953_spi/test.rp2040-ard.yaml @@ -1,36 +1,8 @@ -spi: - - id: spi_ade7953 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + irq_pin: GPIO5 + cs_pin: GPIO6 -sensor: - - platform: ade7953_spi - cs_pin: 5 - irq_pin: 6 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/ads1115/common.yaml b/tests/components/ads1115/common.yaml new file mode 100644 index 0000000000..297877d2d8 --- /dev/null +++ b/tests/components/ads1115/common.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_ads1115 + scl: ${scl_pin} + sda: ${sda_pin} + +ads1115: + address: 0x48 + +sensor: + - platform: ads1115 + multiplexer: A0_A1 + gain: 1.024 + sample_rate: 128 + id: ads1115_sensor diff --git a/tests/components/ads1115/test.esp32-ard.yaml b/tests/components/ads1115/test.esp32-ard.yaml index 0fdaeff275..63c3bd6afd 100644 --- a/tests/components/ads1115/test.esp32-ard.yaml +++ b/tests/components/ads1115/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ads1115 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -ads1115: - address: 0x48 - -sensor: - - platform: ads1115 - multiplexer: A0_A1 - gain: 1.024 - sample_rate: 128 - id: ads1115_sensor +<<: !include common.yaml diff --git a/tests/components/ads1115/test.esp32-c3-ard.yaml b/tests/components/ads1115/test.esp32-c3-ard.yaml index 265d2cad2c..ee2c29ca4e 100644 --- a/tests/components/ads1115/test.esp32-c3-ard.yaml +++ b/tests/components/ads1115/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ads1115 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ads1115: - address: 0x48 - -sensor: - - platform: ads1115 - multiplexer: A0_A1 - gain: 1.024 - sample_rate: 128 - id: ads1115_sensor +<<: !include common.yaml diff --git a/tests/components/ads1115/test.esp32-c3-idf.yaml b/tests/components/ads1115/test.esp32-c3-idf.yaml index 265d2cad2c..ee2c29ca4e 100644 --- a/tests/components/ads1115/test.esp32-c3-idf.yaml +++ b/tests/components/ads1115/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ads1115 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ads1115: - address: 0x48 - -sensor: - - platform: ads1115 - multiplexer: A0_A1 - gain: 1.024 - sample_rate: 128 - id: ads1115_sensor +<<: !include common.yaml diff --git a/tests/components/ads1115/test.esp32-idf.yaml b/tests/components/ads1115/test.esp32-idf.yaml index 0fdaeff275..63c3bd6afd 100644 --- a/tests/components/ads1115/test.esp32-idf.yaml +++ b/tests/components/ads1115/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ads1115 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -ads1115: - address: 0x48 - -sensor: - - platform: ads1115 - multiplexer: A0_A1 - gain: 1.024 - sample_rate: 128 - id: ads1115_sensor +<<: !include common.yaml diff --git a/tests/components/ads1115/test.esp8266-ard.yaml b/tests/components/ads1115/test.esp8266-ard.yaml index 265d2cad2c..ee2c29ca4e 100644 --- a/tests/components/ads1115/test.esp8266-ard.yaml +++ b/tests/components/ads1115/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ads1115 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ads1115: - address: 0x48 - -sensor: - - platform: ads1115 - multiplexer: A0_A1 - gain: 1.024 - sample_rate: 128 - id: ads1115_sensor +<<: !include common.yaml diff --git a/tests/components/ads1115/test.rp2040-ard.yaml b/tests/components/ads1115/test.rp2040-ard.yaml index 265d2cad2c..ee2c29ca4e 100644 --- a/tests/components/ads1115/test.rp2040-ard.yaml +++ b/tests/components/ads1115/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ads1115 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ads1115: - address: 0x48 - -sensor: - - platform: ads1115 - multiplexer: A0_A1 - gain: 1.024 - sample_rate: 128 - id: ads1115_sensor +<<: !include common.yaml diff --git a/tests/components/ags10/common.yaml b/tests/components/ags10/common.yaml new file mode 100644 index 0000000000..0c4c3513cf --- /dev/null +++ b/tests/components/ags10/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_ags10 + scl: ${scl_pin} + sda: ${sda_pin} + frequency: 10kHz + +sensor: + - platform: ags10 + id: ags10_1 + tvoc: + name: AGS10 TVOC + update_interval: 60s diff --git a/tests/components/ags10/test.esp32-ard.yaml b/tests/components/ags10/test.esp32-ard.yaml index b3b53c0d31..63c3bd6afd 100644 --- a/tests/components/ags10/test.esp32-ard.yaml +++ b/tests/components/ags10/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ags10 - scl: 16 - sda: 17 - frequency: 10kHz +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ags10 - id: ags10_1 - tvoc: - name: AGS10 TVOC - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ags10/test.esp32-c3-ard.yaml b/tests/components/ags10/test.esp32-c3-ard.yaml index e338fc78e0..ee2c29ca4e 100644 --- a/tests/components/ags10/test.esp32-c3-ard.yaml +++ b/tests/components/ags10/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ags10 - scl: 5 - sda: 4 - frequency: 10kHz +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ags10 - id: ags10_1 - tvoc: - name: AGS10 TVOC - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ags10/test.esp32-c3-idf.yaml b/tests/components/ags10/test.esp32-c3-idf.yaml index e338fc78e0..ee2c29ca4e 100644 --- a/tests/components/ags10/test.esp32-c3-idf.yaml +++ b/tests/components/ags10/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ags10 - scl: 5 - sda: 4 - frequency: 10kHz +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ags10 - id: ags10_1 - tvoc: - name: AGS10 TVOC - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ags10/test.esp32-idf.yaml b/tests/components/ags10/test.esp32-idf.yaml index b3b53c0d31..63c3bd6afd 100644 --- a/tests/components/ags10/test.esp32-idf.yaml +++ b/tests/components/ags10/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ags10 - scl: 16 - sda: 17 - frequency: 10kHz +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ags10 - id: ags10_1 - tvoc: - name: AGS10 TVOC - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ags10/test.esp8266-ard.yaml b/tests/components/ags10/test.esp8266-ard.yaml index e338fc78e0..ee2c29ca4e 100644 --- a/tests/components/ags10/test.esp8266-ard.yaml +++ b/tests/components/ags10/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ags10 - scl: 5 - sda: 4 - frequency: 10kHz +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ags10 - id: ags10_1 - tvoc: - name: AGS10 TVOC - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/aht10/common.yaml b/tests/components/aht10/common.yaml new file mode 100644 index 0000000000..721af09bb4 --- /dev/null +++ b/tests/components/aht10/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_aht10 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: aht10 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/aht10/test.esp32-ard.yaml b/tests/components/aht10/test.esp32-ard.yaml index 499e69e5d3..63c3bd6afd 100644 --- a/tests/components/aht10/test.esp32-ard.yaml +++ b/tests/components/aht10/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_aht10 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: aht10 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/aht10/test.esp32-c3-ard.yaml b/tests/components/aht10/test.esp32-c3-ard.yaml index 2e5f505476..ee2c29ca4e 100644 --- a/tests/components/aht10/test.esp32-c3-ard.yaml +++ b/tests/components/aht10/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_aht10 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: aht10 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/aht10/test.esp32-c3-idf.yaml b/tests/components/aht10/test.esp32-c3-idf.yaml index 2e5f505476..ee2c29ca4e 100644 --- a/tests/components/aht10/test.esp32-c3-idf.yaml +++ b/tests/components/aht10/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_aht10 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: aht10 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/aht10/test.esp32-idf.yaml b/tests/components/aht10/test.esp32-idf.yaml index 499e69e5d3..63c3bd6afd 100644 --- a/tests/components/aht10/test.esp32-idf.yaml +++ b/tests/components/aht10/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_aht10 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: aht10 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/aht10/test.esp8266-ard.yaml b/tests/components/aht10/test.esp8266-ard.yaml index 2e5f505476..ee2c29ca4e 100644 --- a/tests/components/aht10/test.esp8266-ard.yaml +++ b/tests/components/aht10/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_aht10 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: aht10 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/aht10/test.rp2040-ard.yaml b/tests/components/aht10/test.rp2040-ard.yaml index 2e5f505476..ee2c29ca4e 100644 --- a/tests/components/aht10/test.rp2040-ard.yaml +++ b/tests/components/aht10/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_aht10 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: aht10 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2315c/common.yaml b/tests/components/am2315c/common.yaml new file mode 100644 index 0000000000..ab4656c17d --- /dev/null +++ b/tests/components/am2315c/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_am2315c + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: am2315c + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am2315c/test.esp32-ard.yaml b/tests/components/am2315c/test.esp32-ard.yaml index ed6b65f787..63c3bd6afd 100644 --- a/tests/components/am2315c/test.esp32-ard.yaml +++ b/tests/components/am2315c/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_am2315c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: am2315c - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2315c/test.esp32-c3-ard.yaml b/tests/components/am2315c/test.esp32-c3-ard.yaml index d09bffb7a4..ee2c29ca4e 100644 --- a/tests/components/am2315c/test.esp32-c3-ard.yaml +++ b/tests/components/am2315c/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_am2315c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2315c - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2315c/test.esp32-c3-idf.yaml b/tests/components/am2315c/test.esp32-c3-idf.yaml index d09bffb7a4..ee2c29ca4e 100644 --- a/tests/components/am2315c/test.esp32-c3-idf.yaml +++ b/tests/components/am2315c/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_am2315c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2315c - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2315c/test.esp32-idf.yaml b/tests/components/am2315c/test.esp32-idf.yaml index ed6b65f787..63c3bd6afd 100644 --- a/tests/components/am2315c/test.esp32-idf.yaml +++ b/tests/components/am2315c/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_am2315c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: am2315c - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2315c/test.esp8266-ard.yaml b/tests/components/am2315c/test.esp8266-ard.yaml index d09bffb7a4..ee2c29ca4e 100644 --- a/tests/components/am2315c/test.esp8266-ard.yaml +++ b/tests/components/am2315c/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_am2315c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2315c - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2315c/test.rp2040-ard.yaml b/tests/components/am2315c/test.rp2040-ard.yaml index d09bffb7a4..ee2c29ca4e 100644 --- a/tests/components/am2315c/test.rp2040-ard.yaml +++ b/tests/components/am2315c/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_am2315c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2315c - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2320/common.yaml b/tests/components/am2320/common.yaml new file mode 100644 index 0000000000..c0982b8818 --- /dev/null +++ b/tests/components/am2320/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_am2320 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: am2320 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/am2320/test.esp32-ard.yaml b/tests/components/am2320/test.esp32-ard.yaml index 99f4173b85..63c3bd6afd 100644 --- a/tests/components/am2320/test.esp32-ard.yaml +++ b/tests/components/am2320/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: am2320 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2320/test.esp32-c3-ard.yaml b/tests/components/am2320/test.esp32-c3-ard.yaml index 6acfe8d4fd..ee2c29ca4e 100644 --- a/tests/components/am2320/test.esp32-c3-ard.yaml +++ b/tests/components/am2320/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2320 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2320/test.esp32-c3-idf.yaml b/tests/components/am2320/test.esp32-c3-idf.yaml index 6acfe8d4fd..ee2c29ca4e 100644 --- a/tests/components/am2320/test.esp32-c3-idf.yaml +++ b/tests/components/am2320/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2320 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2320/test.esp32-idf.yaml b/tests/components/am2320/test.esp32-idf.yaml index 99f4173b85..63c3bd6afd 100644 --- a/tests/components/am2320/test.esp32-idf.yaml +++ b/tests/components/am2320/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: am2320 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2320/test.esp8266-ard.yaml b/tests/components/am2320/test.esp8266-ard.yaml index 6acfe8d4fd..ee2c29ca4e 100644 --- a/tests/components/am2320/test.esp8266-ard.yaml +++ b/tests/components/am2320/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2320 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/am2320/test.rp2040-ard.yaml b/tests/components/am2320/test.rp2040-ard.yaml index 6acfe8d4fd..ee2c29ca4e 100644 --- a/tests/components/am2320/test.rp2040-ard.yaml +++ b/tests/components/am2320/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: am2320 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/apds9960/common.yaml b/tests/components/apds9960/common.yaml new file mode 100644 index 0000000000..de7706648a --- /dev/null +++ b/tests/components/apds9960/common.yaml @@ -0,0 +1,48 @@ +i2c: + - id: i2c_apds9960 + scl: ${scl_pin} + sda: ${sda_pin} + +apds9960: + address: 0x20 + update_interval: 60s + +binary_sensor: + - platform: apds9960 + id: apds9960_binary_sensor + direction: up + name: APDS9960 Up + device_class: motion + filters: + - invert + - delayed_on: 20ms + - delayed_off: 20ms + - lambda: "return false;" + on_state: + - logger.log: New state + - platform: apds9960 + direction: down + name: APDS9960 Down + - platform: apds9960 + direction: left + name: APDS9960 Left + - platform: apds9960 + direction: right + name: APDS9960 Right + +sensor: + - platform: apds9960 + type: proximity + name: APDS9960 Proximity + - platform: apds9960 + type: clear + name: APDS9960 Clear + - platform: apds9960 + type: red + name: APDS9960 Red + - platform: apds9960 + type: green + name: APDS9960 Green + - platform: apds9960 + type: blue + name: APDS9960 Blue diff --git a/tests/components/apds9960/test.esp32-ard.yaml b/tests/components/apds9960/test.esp32-ard.yaml index 7ff70a4d47..63c3bd6afd 100644 --- a/tests/components/apds9960/test.esp32-ard.yaml +++ b/tests/components/apds9960/test.esp32-ard.yaml @@ -1,48 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -apds9960: - address: 0x20 - update_interval: 60s - -binary_sensor: - - platform: apds9960 - id: apds9960_binary_sensor - direction: up - name: APDS9960 Up - device_class: motion - filters: - - invert - - delayed_on: 20ms - - delayed_off: 20ms - - lambda: "return false;" - on_state: - - logger.log: New state - - platform: apds9960 - direction: down - name: APDS9960 Down - - platform: apds9960 - direction: left - name: APDS9960 Left - - platform: apds9960 - direction: right - name: APDS9960 Right - -sensor: - - platform: apds9960 - type: proximity - name: APDS9960 Proximity - - platform: apds9960 - type: clear - name: APDS9960 Clear - - platform: apds9960 - type: red - name: APDS9960 Red - - platform: apds9960 - type: green - name: APDS9960 Green - - platform: apds9960 - type: blue - name: APDS9960 Blue +<<: !include common.yaml diff --git a/tests/components/apds9960/test.esp32-c3-ard.yaml b/tests/components/apds9960/test.esp32-c3-ard.yaml index f6b6f7bac0..ee2c29ca4e 100644 --- a/tests/components/apds9960/test.esp32-c3-ard.yaml +++ b/tests/components/apds9960/test.esp32-c3-ard.yaml @@ -1,48 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -apds9960: - address: 0x20 - update_interval: 60s - -binary_sensor: - - platform: apds9960 - id: apds9960_binary_sensor - direction: up - name: APDS9960 Up - device_class: motion - filters: - - invert - - delayed_on: 20ms - - delayed_off: 20ms - - lambda: "return false;" - on_state: - - logger.log: New state - - platform: apds9960 - direction: down - name: APDS9960 Down - - platform: apds9960 - direction: left - name: APDS9960 Left - - platform: apds9960 - direction: right - name: APDS9960 Right - -sensor: - - platform: apds9960 - type: proximity - name: APDS9960 Proximity - - platform: apds9960 - type: clear - name: APDS9960 Clear - - platform: apds9960 - type: red - name: APDS9960 Red - - platform: apds9960 - type: green - name: APDS9960 Green - - platform: apds9960 - type: blue - name: APDS9960 Blue +<<: !include common.yaml diff --git a/tests/components/apds9960/test.esp32-c3-idf.yaml b/tests/components/apds9960/test.esp32-c3-idf.yaml index f6b6f7bac0..ee2c29ca4e 100644 --- a/tests/components/apds9960/test.esp32-c3-idf.yaml +++ b/tests/components/apds9960/test.esp32-c3-idf.yaml @@ -1,48 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -apds9960: - address: 0x20 - update_interval: 60s - -binary_sensor: - - platform: apds9960 - id: apds9960_binary_sensor - direction: up - name: APDS9960 Up - device_class: motion - filters: - - invert - - delayed_on: 20ms - - delayed_off: 20ms - - lambda: "return false;" - on_state: - - logger.log: New state - - platform: apds9960 - direction: down - name: APDS9960 Down - - platform: apds9960 - direction: left - name: APDS9960 Left - - platform: apds9960 - direction: right - name: APDS9960 Right - -sensor: - - platform: apds9960 - type: proximity - name: APDS9960 Proximity - - platform: apds9960 - type: clear - name: APDS9960 Clear - - platform: apds9960 - type: red - name: APDS9960 Red - - platform: apds9960 - type: green - name: APDS9960 Green - - platform: apds9960 - type: blue - name: APDS9960 Blue +<<: !include common.yaml diff --git a/tests/components/apds9960/test.esp32-idf.yaml b/tests/components/apds9960/test.esp32-idf.yaml index 7ff70a4d47..63c3bd6afd 100644 --- a/tests/components/apds9960/test.esp32-idf.yaml +++ b/tests/components/apds9960/test.esp32-idf.yaml @@ -1,48 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -apds9960: - address: 0x20 - update_interval: 60s - -binary_sensor: - - platform: apds9960 - id: apds9960_binary_sensor - direction: up - name: APDS9960 Up - device_class: motion - filters: - - invert - - delayed_on: 20ms - - delayed_off: 20ms - - lambda: "return false;" - on_state: - - logger.log: New state - - platform: apds9960 - direction: down - name: APDS9960 Down - - platform: apds9960 - direction: left - name: APDS9960 Left - - platform: apds9960 - direction: right - name: APDS9960 Right - -sensor: - - platform: apds9960 - type: proximity - name: APDS9960 Proximity - - platform: apds9960 - type: clear - name: APDS9960 Clear - - platform: apds9960 - type: red - name: APDS9960 Red - - platform: apds9960 - type: green - name: APDS9960 Green - - platform: apds9960 - type: blue - name: APDS9960 Blue +<<: !include common.yaml diff --git a/tests/components/apds9960/test.esp8266-ard.yaml b/tests/components/apds9960/test.esp8266-ard.yaml index f6b6f7bac0..ee2c29ca4e 100644 --- a/tests/components/apds9960/test.esp8266-ard.yaml +++ b/tests/components/apds9960/test.esp8266-ard.yaml @@ -1,48 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -apds9960: - address: 0x20 - update_interval: 60s - -binary_sensor: - - platform: apds9960 - id: apds9960_binary_sensor - direction: up - name: APDS9960 Up - device_class: motion - filters: - - invert - - delayed_on: 20ms - - delayed_off: 20ms - - lambda: "return false;" - on_state: - - logger.log: New state - - platform: apds9960 - direction: down - name: APDS9960 Down - - platform: apds9960 - direction: left - name: APDS9960 Left - - platform: apds9960 - direction: right - name: APDS9960 Right - -sensor: - - platform: apds9960 - type: proximity - name: APDS9960 Proximity - - platform: apds9960 - type: clear - name: APDS9960 Clear - - platform: apds9960 - type: red - name: APDS9960 Red - - platform: apds9960 - type: green - name: APDS9960 Green - - platform: apds9960 - type: blue - name: APDS9960 Blue +<<: !include common.yaml diff --git a/tests/components/apds9960/test.rp2040-ard.yaml b/tests/components/apds9960/test.rp2040-ard.yaml index f6b6f7bac0..ee2c29ca4e 100644 --- a/tests/components/apds9960/test.rp2040-ard.yaml +++ b/tests/components/apds9960/test.rp2040-ard.yaml @@ -1,48 +1,5 @@ -i2c: - - id: i2c_bme280 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -apds9960: - address: 0x20 - update_interval: 60s - -binary_sensor: - - platform: apds9960 - id: apds9960_binary_sensor - direction: up - name: APDS9960 Up - device_class: motion - filters: - - invert - - delayed_on: 20ms - - delayed_off: 20ms - - lambda: "return false;" - on_state: - - logger.log: New state - - platform: apds9960 - direction: down - name: APDS9960 Down - - platform: apds9960 - direction: left - name: APDS9960 Left - - platform: apds9960 - direction: right - name: APDS9960 Right - -sensor: - - platform: apds9960 - type: proximity - name: APDS9960 Proximity - - platform: apds9960 - type: clear - name: APDS9960 Clear - - platform: apds9960 - type: red - name: APDS9960 Red - - platform: apds9960 - type: green - name: APDS9960 Green - - platform: apds9960 - type: blue - name: APDS9960 Blue +<<: !include common.yaml diff --git a/tests/components/as3935_i2c/common.yaml b/tests/components/as3935_i2c/common.yaml new file mode 100644 index 0000000000..d76cc37fc1 --- /dev/null +++ b/tests/components/as3935_i2c/common.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_as3935 + scl: ${scl_pin} + sda: ${sda_pin} + +as3935_i2c: + irq_pin: ${irq_pin} + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_i2c/test.esp32-ard.yaml b/tests/components/as3935_i2c/test.esp32-ard.yaml index fad703bee5..2c57d412f6 100644 --- a/tests/components/as3935_i2c/test.esp32-ard.yaml +++ b/tests/components/as3935_i2c/test.esp32-ard.yaml @@ -1,18 +1,6 @@ -i2c: - - id: i2c_as3935 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO15 -as3935_i2c: - irq_pin: 12 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.esp32-c3-ard.yaml b/tests/components/as3935_i2c/test.esp32-c3-ard.yaml index c72556dbac..799acabd5a 100644 --- a/tests/components/as3935_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/as3935_i2c/test.esp32-c3-ard.yaml @@ -1,18 +1,6 @@ -i2c: - - id: i2c_as3935 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -as3935_i2c: - irq_pin: 6 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.esp32-c3-idf.yaml b/tests/components/as3935_i2c/test.esp32-c3-idf.yaml index c72556dbac..799acabd5a 100644 --- a/tests/components/as3935_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/as3935_i2c/test.esp32-c3-idf.yaml @@ -1,18 +1,6 @@ -i2c: - - id: i2c_as3935 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -as3935_i2c: - irq_pin: 6 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.esp32-idf.yaml b/tests/components/as3935_i2c/test.esp32-idf.yaml index fad703bee5..2c57d412f6 100644 --- a/tests/components/as3935_i2c/test.esp32-idf.yaml +++ b/tests/components/as3935_i2c/test.esp32-idf.yaml @@ -1,18 +1,6 @@ -i2c: - - id: i2c_as3935 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO15 -as3935_i2c: - irq_pin: 12 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.esp8266-ard.yaml b/tests/components/as3935_i2c/test.esp8266-ard.yaml index adba9e440f..c8e6a43f44 100644 --- a/tests/components/as3935_i2c/test.esp8266-ard.yaml +++ b/tests/components/as3935_i2c/test.esp8266-ard.yaml @@ -1,18 +1,6 @@ -i2c: - - id: i2c_as3935 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO15 -as3935_i2c: - irq_pin: 15 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_i2c/test.rp2040-ard.yaml b/tests/components/as3935_i2c/test.rp2040-ard.yaml index c72556dbac..799acabd5a 100644 --- a/tests/components/as3935_i2c/test.rp2040-ard.yaml +++ b/tests/components/as3935_i2c/test.rp2040-ard.yaml @@ -1,18 +1,6 @@ -i2c: - - id: i2c_as3935 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -as3935_i2c: - irq_pin: 6 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_spi/common.yaml b/tests/components/as3935_spi/common.yaml new file mode 100644 index 0000000000..c3fb93dff1 --- /dev/null +++ b/tests/components/as3935_spi/common.yaml @@ -0,0 +1,20 @@ +spi: + - id: spi_as3935 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +as3935_spi: + cs_pin: ${cs_pin} + irq_pin: ${irq_pin} + +binary_sensor: + - platform: as3935 + name: Storm Alert + +sensor: + - platform: as3935 + lightning_energy: + name: Lightning Energy + distance: + name: Distance Storm diff --git a/tests/components/as3935_spi/test.esp32-ard.yaml b/tests/components/as3935_spi/test.esp32-ard.yaml index 813a39cb23..e00f522dd4 100644 --- a/tests/components/as3935_spi/test.esp32-ard.yaml +++ b/tests/components/as3935_spi/test.esp32-ard.yaml @@ -1,20 +1,8 @@ -spi: - - id: spi_as3935 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + irq_pin: GPIO13 + cs_pin: GPIO5 -as3935_spi: - cs_pin: 12 - irq_pin: 13 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp32-c3-ard.yaml b/tests/components/as3935_spi/test.esp32-c3-ard.yaml index 7a4a01aeea..fcf35f528e 100644 --- a/tests/components/as3935_spi/test.esp32-c3-ard.yaml +++ b/tests/components/as3935_spi/test.esp32-c3-ard.yaml @@ -1,20 +1,8 @@ -spi: - - id: spi_as3935 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + irq_pin: GPIO9 + cs_pin: GPIO8 -as3935_spi: - cs_pin: 2 - irq_pin: 3 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp32-c3-idf.yaml b/tests/components/as3935_spi/test.esp32-c3-idf.yaml index 7a4a01aeea..fcf35f528e 100644 --- a/tests/components/as3935_spi/test.esp32-c3-idf.yaml +++ b/tests/components/as3935_spi/test.esp32-c3-idf.yaml @@ -1,20 +1,8 @@ -spi: - - id: spi_as3935 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + irq_pin: GPIO9 + cs_pin: GPIO8 -as3935_spi: - cs_pin: 2 - irq_pin: 3 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp32-idf.yaml b/tests/components/as3935_spi/test.esp32-idf.yaml index 813a39cb23..e00f522dd4 100644 --- a/tests/components/as3935_spi/test.esp32-idf.yaml +++ b/tests/components/as3935_spi/test.esp32-idf.yaml @@ -1,20 +1,8 @@ -spi: - - id: spi_as3935 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + irq_pin: GPIO13 + cs_pin: GPIO5 -as3935_spi: - cs_pin: 12 - irq_pin: 13 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.esp8266-ard.yaml b/tests/components/as3935_spi/test.esp8266-ard.yaml index 38a40b0833..b90e661ec0 100644 --- a/tests/components/as3935_spi/test.esp8266-ard.yaml +++ b/tests/components/as3935_spi/test.esp8266-ard.yaml @@ -1,20 +1,8 @@ -spi: - - id: spi_as3935 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + irq_pin: GPIO5 + cs_pin: GPIO15 -as3935_spi: - cs_pin: 15 - irq_pin: 16 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as3935_spi/test.rp2040-ard.yaml b/tests/components/as3935_spi/test.rp2040-ard.yaml index 528759d97d..8f5941e1b2 100644 --- a/tests/components/as3935_spi/test.rp2040-ard.yaml +++ b/tests/components/as3935_spi/test.rp2040-ard.yaml @@ -1,20 +1,8 @@ -spi: - - id: spi_as3935 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + irq_pin: GPIO5 + cs_pin: GPIO6 -as3935_spi: - cs_pin: 6 - irq_pin: 7 - -binary_sensor: - - platform: as3935 - name: Storm Alert - -sensor: - - platform: as3935 - lightning_energy: - name: Lightning Energy - distance: - name: Distance Storm +<<: !include common.yaml diff --git a/tests/components/as5600/common.yaml b/tests/components/as5600/common.yaml new file mode 100644 index 0000000000..860f5bf803 --- /dev/null +++ b/tests/components/as5600/common.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_as5600 + scl: ${scl_pin} + sda: ${sda_pin} + +as5600: + dir_pin: ${dir_pin} + direction: clockwise + start_position: 90deg + range: 180deg + watchdog: true + power_mode: low1 + hysteresis: lsb1 + slow_filter: 8x + fast_filter: lsb6 + +sensor: + - platform: as5600 + name: AS5600 Position + raw_position: + name: AS5600 Raw Position + gain: + name: AS5600 Gain + magnitude: + name: AS5600 Magnitude + status: + name: AS5600 Status diff --git a/tests/components/as5600/test.esp32-ard.yaml b/tests/components/as5600/test.esp32-ard.yaml index 312ee9ad04..fa08763501 100644 --- a/tests/components/as5600/test.esp32-ard.yaml +++ b/tests/components/as5600/test.esp32-ard.yaml @@ -1,27 +1,6 @@ -i2c: - - id: i2c_as5600 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + dir_pin: GPIO15 -as5600: - dir_pin: 12 - direction: clockwise - start_position: 90deg - range: 180deg - watchdog: true - power_mode: low1 - hysteresis: lsb1 - slow_filter: 8x - fast_filter: lsb6 - -sensor: - - platform: as5600 - name: AS5600 Position - raw_position: - name: AS5600 Raw Position - gain: - name: AS5600 Gain - magnitude: - name: AS5600 Magnitude - status: - name: AS5600 Status +<<: !include common.yaml diff --git a/tests/components/as5600/test.esp32-c3-ard.yaml b/tests/components/as5600/test.esp32-c3-ard.yaml index e074fa5e0c..a0623c91e5 100644 --- a/tests/components/as5600/test.esp32-c3-ard.yaml +++ b/tests/components/as5600/test.esp32-c3-ard.yaml @@ -1,27 +1,6 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + dir_pin: GPIO6 -as5600: - dir_pin: 6 - direction: clockwise - start_position: 90deg - range: 180deg - watchdog: true - power_mode: low1 - hysteresis: lsb1 - slow_filter: 8x - fast_filter: lsb6 - -sensor: - - platform: as5600 - name: AS5600 Position - raw_position: - name: AS5600 Raw Position - gain: - name: AS5600 Gain - magnitude: - name: AS5600 Magnitude - status: - name: AS5600 Status +<<: !include common.yaml diff --git a/tests/components/as5600/test.esp32-c3-idf.yaml b/tests/components/as5600/test.esp32-c3-idf.yaml index e074fa5e0c..a0623c91e5 100644 --- a/tests/components/as5600/test.esp32-c3-idf.yaml +++ b/tests/components/as5600/test.esp32-c3-idf.yaml @@ -1,27 +1,6 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + dir_pin: GPIO6 -as5600: - dir_pin: 6 - direction: clockwise - start_position: 90deg - range: 180deg - watchdog: true - power_mode: low1 - hysteresis: lsb1 - slow_filter: 8x - fast_filter: lsb6 - -sensor: - - platform: as5600 - name: AS5600 Position - raw_position: - name: AS5600 Raw Position - gain: - name: AS5600 Gain - magnitude: - name: AS5600 Magnitude - status: - name: AS5600 Status +<<: !include common.yaml diff --git a/tests/components/as5600/test.esp32-idf.yaml b/tests/components/as5600/test.esp32-idf.yaml index 312ee9ad04..fa08763501 100644 --- a/tests/components/as5600/test.esp32-idf.yaml +++ b/tests/components/as5600/test.esp32-idf.yaml @@ -1,27 +1,6 @@ -i2c: - - id: i2c_as5600 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + dir_pin: GPIO15 -as5600: - dir_pin: 12 - direction: clockwise - start_position: 90deg - range: 180deg - watchdog: true - power_mode: low1 - hysteresis: lsb1 - slow_filter: 8x - fast_filter: lsb6 - -sensor: - - platform: as5600 - name: AS5600 Position - raw_position: - name: AS5600 Raw Position - gain: - name: AS5600 Gain - magnitude: - name: AS5600 Magnitude - status: - name: AS5600 Status +<<: !include common.yaml diff --git a/tests/components/as5600/test.esp8266-ard.yaml b/tests/components/as5600/test.esp8266-ard.yaml index a232d27305..5e27f8c134 100644 --- a/tests/components/as5600/test.esp8266-ard.yaml +++ b/tests/components/as5600/test.esp8266-ard.yaml @@ -1,27 +1,6 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + dir_pin: GPIO15 -as5600: - dir_pin: 15 - direction: clockwise - start_position: 90deg - range: 180deg - watchdog: true - power_mode: low1 - hysteresis: lsb1 - slow_filter: 8x - fast_filter: lsb6 - -sensor: - - platform: as5600 - name: AS5600 Position - raw_position: - name: AS5600 Raw Position - gain: - name: AS5600 Gain - magnitude: - name: AS5600 Magnitude - status: - name: AS5600 Status +<<: !include common.yaml diff --git a/tests/components/as5600/test.rp2040-ard.yaml b/tests/components/as5600/test.rp2040-ard.yaml index e074fa5e0c..a0623c91e5 100644 --- a/tests/components/as5600/test.rp2040-ard.yaml +++ b/tests/components/as5600/test.rp2040-ard.yaml @@ -1,27 +1,6 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + dir_pin: GPIO6 -as5600: - dir_pin: 6 - direction: clockwise - start_position: 90deg - range: 180deg - watchdog: true - power_mode: low1 - hysteresis: lsb1 - slow_filter: 8x - fast_filter: lsb6 - -sensor: - - platform: as5600 - name: AS5600 Position - raw_position: - name: AS5600 Raw Position - gain: - name: AS5600 Gain - magnitude: - name: AS5600 Magnitude - status: - name: AS5600 Status +<<: !include common.yaml diff --git a/tests/components/as7341/common.yaml b/tests/components/as7341/common.yaml new file mode 100644 index 0000000000..0351b344c6 --- /dev/null +++ b/tests/components/as7341/common.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_as7341 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: as7341 + update_interval: 15s + gain: X8 + atime: 120 + astep: 99 + f1: + name: F1 + f2: + name: F2 + f3: + name: F3 + f4: + name: F4 + f5: + name: F5 + f6: + name: F6 + f7: + name: F7 + f8: + name: F8 + clear: + name: Clear + nir: + name: NIR diff --git a/tests/components/as7341/test.esp32-ard.yaml b/tests/components/as7341/test.esp32-ard.yaml index d582a367ac..63c3bd6afd 100644 --- a/tests/components/as7341/test.esp32-ard.yaml +++ b/tests/components/as7341/test.esp32-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_as5600 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: as7341 - update_interval: 15s - gain: X8 - atime: 120 - astep: 99 - f1: - name: F1 - f2: - name: F2 - f3: - name: F3 - f4: - name: F4 - f5: - name: F5 - f6: - name: F6 - f7: - name: F7 - f8: - name: F8 - clear: - name: Clear - nir: - name: NIR +<<: !include common.yaml diff --git a/tests/components/as7341/test.esp32-c3-ard.yaml b/tests/components/as7341/test.esp32-c3-ard.yaml index 19965d1715..ee2c29ca4e 100644 --- a/tests/components/as7341/test.esp32-c3-ard.yaml +++ b/tests/components/as7341/test.esp32-c3-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: as7341 - update_interval: 15s - gain: X8 - atime: 120 - astep: 99 - f1: - name: F1 - f2: - name: F2 - f3: - name: F3 - f4: - name: F4 - f5: - name: F5 - f6: - name: F6 - f7: - name: F7 - f8: - name: F8 - clear: - name: Clear - nir: - name: NIR +<<: !include common.yaml diff --git a/tests/components/as7341/test.esp32-c3-idf.yaml b/tests/components/as7341/test.esp32-c3-idf.yaml index 19965d1715..ee2c29ca4e 100644 --- a/tests/components/as7341/test.esp32-c3-idf.yaml +++ b/tests/components/as7341/test.esp32-c3-idf.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: as7341 - update_interval: 15s - gain: X8 - atime: 120 - astep: 99 - f1: - name: F1 - f2: - name: F2 - f3: - name: F3 - f4: - name: F4 - f5: - name: F5 - f6: - name: F6 - f7: - name: F7 - f8: - name: F8 - clear: - name: Clear - nir: - name: NIR +<<: !include common.yaml diff --git a/tests/components/as7341/test.esp32-idf.yaml b/tests/components/as7341/test.esp32-idf.yaml index d582a367ac..63c3bd6afd 100644 --- a/tests/components/as7341/test.esp32-idf.yaml +++ b/tests/components/as7341/test.esp32-idf.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_as5600 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: as7341 - update_interval: 15s - gain: X8 - atime: 120 - astep: 99 - f1: - name: F1 - f2: - name: F2 - f3: - name: F3 - f4: - name: F4 - f5: - name: F5 - f6: - name: F6 - f7: - name: F7 - f8: - name: F8 - clear: - name: Clear - nir: - name: NIR +<<: !include common.yaml diff --git a/tests/components/as7341/test.esp8266-ard.yaml b/tests/components/as7341/test.esp8266-ard.yaml index 19965d1715..ee2c29ca4e 100644 --- a/tests/components/as7341/test.esp8266-ard.yaml +++ b/tests/components/as7341/test.esp8266-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: as7341 - update_interval: 15s - gain: X8 - atime: 120 - astep: 99 - f1: - name: F1 - f2: - name: F2 - f3: - name: F3 - f4: - name: F4 - f5: - name: F5 - f6: - name: F6 - f7: - name: F7 - f8: - name: F8 - clear: - name: Clear - nir: - name: NIR +<<: !include common.yaml diff --git a/tests/components/as7341/test.rp2040-ard.yaml b/tests/components/as7341/test.rp2040-ard.yaml index 19965d1715..ee2c29ca4e 100644 --- a/tests/components/as7341/test.rp2040-ard.yaml +++ b/tests/components/as7341/test.rp2040-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_as5600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: as7341 - update_interval: 15s - gain: X8 - atime: 120 - astep: 99 - f1: - name: F1 - f2: - name: F2 - f3: - name: F3 - f4: - name: F4 - f5: - name: F5 - f6: - name: F6 - f7: - name: F7 - f8: - name: F8 - clear: - name: Clear - nir: - name: NIR +<<: !include common.yaml diff --git a/tests/components/at581x/common.yaml b/tests/components/at581x/common.yaml new file mode 100644 index 0000000000..018a0fded1 --- /dev/null +++ b/tests/components/at581x/common.yaml @@ -0,0 +1,28 @@ +esphome: + on_boot: + then: + - at581x.settings: + id: waveradar + hw_frontend_reset: false + frequency: 5800MHz + sensing_distance: 200 + poweron_selfcheck_time: 2s + protect_time: 1s + trigger_base: 500ms + trigger_keep: 10s + stage_gain: 3 + power_consumption: 70uA + - at581x.reset: + id: waveradar + +at581x: + id: waveradar + +i2c: + - id: i2c_at581x + scl: ${scl_pin} + sda: ${sda_pin} + +switch: + - platform: at581x + name: Enable Radar diff --git a/tests/components/at581x/test.esp32-ard.yaml b/tests/components/at581x/test.esp32-ard.yaml index ff84e61e1e..63c3bd6afd 100644 --- a/tests/components/at581x/test.esp32-ard.yaml +++ b/tests/components/at581x/test.esp32-ard.yaml @@ -1,38 +1,5 @@ -esphome: - on_boot: - then: - - at581x.settings: - id: "Waveradar" - hw_frontend_reset: false - frequency: 5800MHz - sensing_distance: 200 - poweron_selfcheck_time: 2s - protect_time: 1s - trigger_base: 500ms - trigger_keep: 10s - stage_gain: 3 - power_consumption: 70uA - - at581x.reset: - id: "Waveradar" +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -at581x: - id: "Waveradar" - i2c_id: i2c_bus - -i2c: - sda: 14 - scl: 15 - scan: true - frequency: 100kHz - setup_priority: -100 - id: i2c_bus - -binary_sensor: - - platform: gpio - pin: GPIO21 - name: "Radar motion" - -switch: - - platform: at581x - at581x_id: "Waveradar" - name: "Enable Radar" +<<: !include common.yaml diff --git a/tests/components/at581x/test.esp32-c3-ard.yaml b/tests/components/at581x/test.esp32-c3-ard.yaml index b49a283eca..ee2c29ca4e 100644 --- a/tests/components/at581x/test.esp32-c3-ard.yaml +++ b/tests/components/at581x/test.esp32-c3-ard.yaml @@ -1,38 +1,5 @@ -esphome: - on_boot: - then: - - at581x.settings: - id: "Waveradar" - hw_frontend_reset: false - frequency: 5800MHz - sensing_distance: 200 - poweron_selfcheck_time: 2s - protect_time: 1s - trigger_base: 500ms - trigger_keep: 10s - stage_gain: 3 - power_consumption: 70uA - - at581x.reset: - id: "Waveradar" +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -at581x: - id: "Waveradar" - i2c_id: i2c_bus - -i2c: - sda: 8 - scl: 9 - scan: true - frequency: 100kHz - setup_priority: -100 - id: i2c_bus - -binary_sensor: - - platform: gpio - pin: GPIO21 - name: "Radar motion" - -switch: - - platform: at581x - at581x_id: "Waveradar" - name: "Enable Radar" +<<: !include common.yaml diff --git a/tests/components/at581x/test.esp32-c3-idf.yaml b/tests/components/at581x/test.esp32-c3-idf.yaml index b49a283eca..ee2c29ca4e 100644 --- a/tests/components/at581x/test.esp32-c3-idf.yaml +++ b/tests/components/at581x/test.esp32-c3-idf.yaml @@ -1,38 +1,5 @@ -esphome: - on_boot: - then: - - at581x.settings: - id: "Waveradar" - hw_frontend_reset: false - frequency: 5800MHz - sensing_distance: 200 - poweron_selfcheck_time: 2s - protect_time: 1s - trigger_base: 500ms - trigger_keep: 10s - stage_gain: 3 - power_consumption: 70uA - - at581x.reset: - id: "Waveradar" +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -at581x: - id: "Waveradar" - i2c_id: i2c_bus - -i2c: - sda: 8 - scl: 9 - scan: true - frequency: 100kHz - setup_priority: -100 - id: i2c_bus - -binary_sensor: - - platform: gpio - pin: GPIO21 - name: "Radar motion" - -switch: - - platform: at581x - at581x_id: "Waveradar" - name: "Enable Radar" +<<: !include common.yaml diff --git a/tests/components/at581x/test.esp32-idf.yaml b/tests/components/at581x/test.esp32-idf.yaml index ff84e61e1e..63c3bd6afd 100644 --- a/tests/components/at581x/test.esp32-idf.yaml +++ b/tests/components/at581x/test.esp32-idf.yaml @@ -1,38 +1,5 @@ -esphome: - on_boot: - then: - - at581x.settings: - id: "Waveradar" - hw_frontend_reset: false - frequency: 5800MHz - sensing_distance: 200 - poweron_selfcheck_time: 2s - protect_time: 1s - trigger_base: 500ms - trigger_keep: 10s - stage_gain: 3 - power_consumption: 70uA - - at581x.reset: - id: "Waveradar" +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -at581x: - id: "Waveradar" - i2c_id: i2c_bus - -i2c: - sda: 14 - scl: 15 - scan: true - frequency: 100kHz - setup_priority: -100 - id: i2c_bus - -binary_sensor: - - platform: gpio - pin: GPIO21 - name: "Radar motion" - -switch: - - platform: at581x - at581x_id: "Waveradar" - name: "Enable Radar" +<<: !include common.yaml diff --git a/tests/components/at581x/test.esp8266-ard.yaml b/tests/components/at581x/test.esp8266-ard.yaml index a7b0069045..ee2c29ca4e 100644 --- a/tests/components/at581x/test.esp8266-ard.yaml +++ b/tests/components/at581x/test.esp8266-ard.yaml @@ -1,38 +1,5 @@ -esphome: - on_boot: - then: - - at581x.settings: - id: "Waveradar" - hw_frontend_reset: false - frequency: 5800MHz - sensing_distance: 200 - poweron_selfcheck_time: 2s - protect_time: 1s - trigger_base: 500ms - trigger_keep: 10s - stage_gain: 3 - power_consumption: 70uA - - at581x.reset: - id: "Waveradar" +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -at581x: - id: "Waveradar" - i2c_id: i2c_bus - -i2c: - sda: 14 - scl: 15 - scan: true - frequency: 100kHz - setup_priority: -100 - id: i2c_bus - -binary_sensor: - - platform: gpio - pin: GPIO4 - name: "Radar motion" - -switch: - - platform: at581x - at581x_id: "Waveradar" - name: "Enable Radar" +<<: !include common.yaml diff --git a/tests/components/at581x/test.rp2040-ard.yaml b/tests/components/at581x/test.rp2040-ard.yaml index b49a283eca..ee2c29ca4e 100644 --- a/tests/components/at581x/test.rp2040-ard.yaml +++ b/tests/components/at581x/test.rp2040-ard.yaml @@ -1,38 +1,5 @@ -esphome: - on_boot: - then: - - at581x.settings: - id: "Waveradar" - hw_frontend_reset: false - frequency: 5800MHz - sensing_distance: 200 - poweron_selfcheck_time: 2s - protect_time: 1s - trigger_base: 500ms - trigger_keep: 10s - stage_gain: 3 - power_consumption: 70uA - - at581x.reset: - id: "Waveradar" +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -at581x: - id: "Waveradar" - i2c_id: i2c_bus - -i2c: - sda: 8 - scl: 9 - scan: true - frequency: 100kHz - setup_priority: -100 - id: i2c_bus - -binary_sensor: - - platform: gpio - pin: GPIO21 - name: "Radar motion" - -switch: - - platform: at581x - at581x_id: "Waveradar" - name: "Enable Radar" +<<: !include common.yaml diff --git a/tests/components/atm90e26/common.yaml b/tests/components/atm90e26/common.yaml new file mode 100644 index 0000000000..49c3a73ec8 --- /dev/null +++ b/tests/components/atm90e26/common.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_atm90e26 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: atm90e26 + cs_pin: ${cs_pin} + voltage: + name: Line Voltage + current: + name: CT Amps + power: + name: Active Watts + power_factor: + name: Power Factor + frequency: + name: Line Frequency + line_frequency: 50Hz + meter_constant: 1000 + pl_const: 1429876 + gain_pga: 1X + gain_metering: 7481 + gain_voltage: 26400 + gain_ct: 31251 diff --git a/tests/components/atm90e26/test.esp32-ard.yaml b/tests/components/atm90e26/test.esp32-ard.yaml index 72fb3e5b24..54e027a614 100644 --- a/tests/components/atm90e26/test.esp32-ard.yaml +++ b/tests/components/atm90e26/test.esp32-ard.yaml @@ -1,26 +1,7 @@ -spi: - - id: spi_atm90e26 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: atm90e26 - cs_pin: 13 - voltage: - name: Line Voltage - current: - name: CT Amps - power: - name: Active Watts - power_factor: - name: Power Factor - frequency: - name: Line Frequency - line_frequency: 50Hz - meter_constant: 1000 - pl_const: 1429876 - gain_pga: 1X - gain_metering: 7481 - gain_voltage: 26400 - gain_ct: 31251 +<<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp32-c3-ard.yaml b/tests/components/atm90e26/test.esp32-c3-ard.yaml index ce123bcf72..2415ba5dc6 100644 --- a/tests/components/atm90e26/test.esp32-c3-ard.yaml +++ b/tests/components/atm90e26/test.esp32-c3-ard.yaml @@ -1,26 +1,7 @@ -spi: - - id: spi_atm90e26 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: atm90e26 - cs_pin: 8 - voltage: - name: Line Voltage - current: - name: CT Amps - power: - name: Active Watts - power_factor: - name: Power Factor - frequency: - name: Line Frequency - line_frequency: 50Hz - meter_constant: 1000 - pl_const: 1429876 - gain_pga: 1X - gain_metering: 7481 - gain_voltage: 26400 - gain_ct: 31251 +<<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp32-c3-idf.yaml b/tests/components/atm90e26/test.esp32-c3-idf.yaml index ce123bcf72..2415ba5dc6 100644 --- a/tests/components/atm90e26/test.esp32-c3-idf.yaml +++ b/tests/components/atm90e26/test.esp32-c3-idf.yaml @@ -1,26 +1,7 @@ -spi: - - id: spi_atm90e26 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: atm90e26 - cs_pin: 8 - voltage: - name: Line Voltage - current: - name: CT Amps - power: - name: Active Watts - power_factor: - name: Power Factor - frequency: - name: Line Frequency - line_frequency: 50Hz - meter_constant: 1000 - pl_const: 1429876 - gain_pga: 1X - gain_metering: 7481 - gain_voltage: 26400 - gain_ct: 31251 +<<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp32-idf.yaml b/tests/components/atm90e26/test.esp32-idf.yaml index 72fb3e5b24..54e027a614 100644 --- a/tests/components/atm90e26/test.esp32-idf.yaml +++ b/tests/components/atm90e26/test.esp32-idf.yaml @@ -1,26 +1,7 @@ -spi: - - id: spi_atm90e26 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: atm90e26 - cs_pin: 13 - voltage: - name: Line Voltage - current: - name: CT Amps - power: - name: Active Watts - power_factor: - name: Power Factor - frequency: - name: Line Frequency - line_frequency: 50Hz - meter_constant: 1000 - pl_const: 1429876 - gain_pga: 1X - gain_metering: 7481 - gain_voltage: 26400 - gain_ct: 31251 +<<: !include common.yaml diff --git a/tests/components/atm90e26/test.esp8266-ard.yaml b/tests/components/atm90e26/test.esp8266-ard.yaml index 68d63cc278..dbd158d030 100644 --- a/tests/components/atm90e26/test.esp8266-ard.yaml +++ b/tests/components/atm90e26/test.esp8266-ard.yaml @@ -1,26 +1,7 @@ -spi: - - id: spi_atm90e26 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -sensor: - - platform: atm90e26 - cs_pin: 5 - voltage: - name: Line Voltage - current: - name: CT Amps - power: - name: Active Watts - power_factor: - name: Power Factor - frequency: - name: Line Frequency - line_frequency: 50Hz - meter_constant: 1000 - pl_const: 1429876 - gain_pga: 1X - gain_metering: 7481 - gain_voltage: 26400 - gain_ct: 31251 +<<: !include common.yaml diff --git a/tests/components/atm90e26/test.rp2040-ard.yaml b/tests/components/atm90e26/test.rp2040-ard.yaml index f43277dbb1..c8bfab0023 100644 --- a/tests/components/atm90e26/test.rp2040-ard.yaml +++ b/tests/components/atm90e26/test.rp2040-ard.yaml @@ -1,26 +1,7 @@ -spi: - - id: spi_atm90e26 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO6 -sensor: - - platform: atm90e26 - cs_pin: 5 - voltage: - name: Line Voltage - current: - name: CT Amps - power: - name: Active Watts - power_factor: - name: Power Factor - frequency: - name: Line Frequency - line_frequency: 50Hz - meter_constant: 1000 - pl_const: 1429876 - gain_pga: 1X - gain_metering: 7481 - gain_voltage: 26400 - gain_ct: 31251 +<<: !include common.yaml diff --git a/tests/components/atm90e32/common.yaml b/tests/components/atm90e32/common.yaml new file mode 100644 index 0000000000..156d00b4e0 --- /dev/null +++ b/tests/components/atm90e32/common.yaml @@ -0,0 +1,61 @@ +spi: + - id: spi_atm90e32 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: atm90e32 + cs_pin: ${cs_pin} + id: atm90e32_chip1 + phase_a: + voltage: + name: EMON Line Voltage A + current: + name: EMON CT1 Current + power: + name: EMON Active Power CT1 + reactive_power: + name: EMON Reactive Power CT1 + power_factor: + name: EMON Power Factor CT1 + gain_voltage: 7305 + gain_ct: 27961 + phase_b: + current: + name: EMON CT2 Current + power: + name: EMON Active Power CT2 + reactive_power: + name: EMON Reactive Power CT2 + power_factor: + name: EMON Power Factor CT2 + gain_voltage: 7305 + gain_ct: 27961 + phase_c: + current: + name: EMON CT3 Current + power: + name: EMON Active Power CT3 + reactive_power: + name: EMON Reactive Power CT3 + power_factor: + name: EMON Power Factor CT3 + gain_voltage: 7305 + gain_ct: 27961 + frequency: + name: EMON Line Frequency + chip_temperature: + name: EMON Chip Temp A + line_frequency: 60Hz + current_phases: 3 + gain_pga: 2X + enable_offset_calibration: True + +button: + - platform: atm90e32 + id: atm90e32_chip1 + run_offset_calibration: + name: Chip1 - Run Offset Calibration + clear_offset_calibration: + name: Chip1 - Clear Offset Calibration diff --git a/tests/components/atm90e32/test.esp32-ard.yaml b/tests/components/atm90e32/test.esp32-ard.yaml index 3bdc2bcec6..54e027a614 100644 --- a/tests/components/atm90e32/test.esp32-ard.yaml +++ b/tests/components/atm90e32/test.esp32-ard.yaml @@ -1,60 +1,7 @@ -spi: - - id: spi_atm90e32 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: atm90e32 - cs_pin: 13 - id: chip1 - phase_a: - voltage: - name: EMON Line Voltage A - current: - name: EMON CT1 Current - power: - name: EMON Active Power CT1 - reactive_power: - name: EMON Reactive Power CT1 - power_factor: - name: EMON Power Factor CT1 - gain_voltage: 7305 - gain_ct: 27961 - phase_b: - current: - name: EMON CT2 Current - power: - name: EMON Active Power CT2 - reactive_power: - name: EMON Reactive Power CT2 - power_factor: - name: EMON Power Factor CT2 - gain_voltage: 7305 - gain_ct: 27961 - phase_c: - current: - name: EMON CT3 Current - power: - name: EMON Active Power CT3 - reactive_power: - name: EMON Reactive Power CT3 - power_factor: - name: EMON Power Factor CT3 - gain_voltage: 7305 - gain_ct: 27961 - frequency: - name: EMON Line Frequency - chip_temperature: - name: EMON Chip Temp A - line_frequency: 60Hz - current_phases: 3 - gain_pga: 2X - enable_offset_calibration: True -button: - - platform: atm90e32 - id: chip1 - run_offset_calibration: - name: "Chip1 - Run Offset Calibration" - clear_offset_calibration: - name: "Chip1 - Clear Offset Calibration" +<<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp32-c3-ard.yaml b/tests/components/atm90e32/test.esp32-c3-ard.yaml index 9ec0037a61..2415ba5dc6 100644 --- a/tests/components/atm90e32/test.esp32-c3-ard.yaml +++ b/tests/components/atm90e32/test.esp32-c3-ard.yaml @@ -1,60 +1,7 @@ -spi: - - id: spi_atm90e32 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: atm90e32 - cs_pin: 8 - id: chip1 - phase_a: - voltage: - name: EMON Line Voltage A - current: - name: EMON CT1 Current - power: - name: EMON Active Power CT1 - reactive_power: - name: EMON Reactive Power CT1 - power_factor: - name: EMON Power Factor CT1 - gain_voltage: 7305 - gain_ct: 27961 - phase_b: - current: - name: EMON CT2 Current - power: - name: EMON Active Power CT2 - reactive_power: - name: EMON Reactive Power CT2 - power_factor: - name: EMON Power Factor CT2 - gain_voltage: 7305 - gain_ct: 27961 - phase_c: - current: - name: EMON CT3 Current - power: - name: EMON Active Power CT3 - reactive_power: - name: EMON Reactive Power CT3 - power_factor: - name: EMON Power Factor CT3 - gain_voltage: 7305 - gain_ct: 27961 - frequency: - name: EMON Line Frequency - chip_temperature: - name: EMON Chip Temp A - line_frequency: 60Hz - current_phases: 3 - gain_pga: 2X - enable_offset_calibration: True -button: - - platform: atm90e32 - id: chip1 - run_offset_calibration: - name: "Chip1 - Run Offset Calibration" - clear_offset_calibration: - name: "Chip1 - Clear Offset Calibration" +<<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp32-c3-idf.yaml b/tests/components/atm90e32/test.esp32-c3-idf.yaml index 9ec0037a61..2415ba5dc6 100644 --- a/tests/components/atm90e32/test.esp32-c3-idf.yaml +++ b/tests/components/atm90e32/test.esp32-c3-idf.yaml @@ -1,60 +1,7 @@ -spi: - - id: spi_atm90e32 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: atm90e32 - cs_pin: 8 - id: chip1 - phase_a: - voltage: - name: EMON Line Voltage A - current: - name: EMON CT1 Current - power: - name: EMON Active Power CT1 - reactive_power: - name: EMON Reactive Power CT1 - power_factor: - name: EMON Power Factor CT1 - gain_voltage: 7305 - gain_ct: 27961 - phase_b: - current: - name: EMON CT2 Current - power: - name: EMON Active Power CT2 - reactive_power: - name: EMON Reactive Power CT2 - power_factor: - name: EMON Power Factor CT2 - gain_voltage: 7305 - gain_ct: 27961 - phase_c: - current: - name: EMON CT3 Current - power: - name: EMON Active Power CT3 - reactive_power: - name: EMON Reactive Power CT3 - power_factor: - name: EMON Power Factor CT3 - gain_voltage: 7305 - gain_ct: 27961 - frequency: - name: EMON Line Frequency - chip_temperature: - name: EMON Chip Temp A - line_frequency: 60Hz - current_phases: 3 - gain_pga: 2X - enable_offset_calibration: True -button: - - platform: atm90e32 - id: chip1 - run_offset_calibration: - name: "Chip1 - Run Offset Calibration" - clear_offset_calibration: - name: "Chip1 - Clear Offset Calibration" +<<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp32-idf.yaml b/tests/components/atm90e32/test.esp32-idf.yaml index 3bdc2bcec6..54e027a614 100644 --- a/tests/components/atm90e32/test.esp32-idf.yaml +++ b/tests/components/atm90e32/test.esp32-idf.yaml @@ -1,60 +1,7 @@ -spi: - - id: spi_atm90e32 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: atm90e32 - cs_pin: 13 - id: chip1 - phase_a: - voltage: - name: EMON Line Voltage A - current: - name: EMON CT1 Current - power: - name: EMON Active Power CT1 - reactive_power: - name: EMON Reactive Power CT1 - power_factor: - name: EMON Power Factor CT1 - gain_voltage: 7305 - gain_ct: 27961 - phase_b: - current: - name: EMON CT2 Current - power: - name: EMON Active Power CT2 - reactive_power: - name: EMON Reactive Power CT2 - power_factor: - name: EMON Power Factor CT2 - gain_voltage: 7305 - gain_ct: 27961 - phase_c: - current: - name: EMON CT3 Current - power: - name: EMON Active Power CT3 - reactive_power: - name: EMON Reactive Power CT3 - power_factor: - name: EMON Power Factor CT3 - gain_voltage: 7305 - gain_ct: 27961 - frequency: - name: EMON Line Frequency - chip_temperature: - name: EMON Chip Temp A - line_frequency: 60Hz - current_phases: 3 - gain_pga: 2X - enable_offset_calibration: True -button: - - platform: atm90e32 - id: chip1 - run_offset_calibration: - name: "Chip1 - Run Offset Calibration" - clear_offset_calibration: - name: "Chip1 - Clear Offset Calibration" +<<: !include common.yaml diff --git a/tests/components/atm90e32/test.esp8266-ard.yaml b/tests/components/atm90e32/test.esp8266-ard.yaml index fbb3368efa..dbd158d030 100644 --- a/tests/components/atm90e32/test.esp8266-ard.yaml +++ b/tests/components/atm90e32/test.esp8266-ard.yaml @@ -1,91 +1,7 @@ -spi: - - id: spi_atm90e32 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -sensor: - - platform: atm90e32 - cs_pin: 5 - id: chip1 - phase_a: - voltage: - name: EMON Line Voltage A - current: - name: EMON CT1 Current - power: - name: EMON Active Power CT1 - reactive_power: - name: EMON Reactive Power CT1 - power_factor: - name: EMON Power Factor CT1 - gain_voltage: 7305 - gain_ct: 27961 - phase_b: - current: - name: EMON CT2 Current - power: - name: EMON Active Power CT2 - reactive_power: - name: EMON Reactive Power CT2 - power_factor: - name: EMON Power Factor CT2 - gain_voltage: 7305 - gain_ct: 27961 - phase_c: - current: - name: EMON CT3 Current - power: - name: EMON Active Power CT3 - reactive_power: - name: EMON Reactive Power CT3 - power_factor: - name: EMON Power Factor CT3 - gain_voltage: 7305 - gain_ct: 27961 - frequency: - name: EMON Line Frequency - chip_temperature: - name: EMON Chip Temp A - line_frequency: 60Hz - current_phases: 3 - gain_pga: 2X - enable_offset_calibration: True - - platform: atm90e32 - cs_pin: 4 - id: chip2 - phase_a: - voltage: - name: EMON Line Voltage A - current: - name: EMON CT1 Current - power: - name: EMON Active Power CT1 - reactive_power: - name: EMON Reactive Power CT1 - power_factor: - name: EMON Power Factor CT1 - gain_voltage: 7305 - gain_ct: 27961 - phase_c: - voltage: - name: EMON Line Voltage C - current: - name: EMON CT2 Current - power: - name: EMON Active Power CT2 - reactive_power: - name: EMON Reactive Power CT2 - power_factor: - name: EMON Power Factor CT2 - gain_voltage: 7305 - gain_ct: 27961 - line_frequency: 60Hz - current_phases: 2 -button: - - platform: atm90e32 - id: chip1 - run_offset_calibration: - name: "Chip1 - Run Offset Calibration" - clear_offset_calibration: - name: "Chip1 - Clear Offset Calibration" +<<: !include common.yaml diff --git a/tests/components/atm90e32/test.rp2040-ard.yaml b/tests/components/atm90e32/test.rp2040-ard.yaml index a6b7956da7..c8bfab0023 100644 --- a/tests/components/atm90e32/test.rp2040-ard.yaml +++ b/tests/components/atm90e32/test.rp2040-ard.yaml @@ -1,60 +1,7 @@ -spi: - - id: spi_atm90e32 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO6 -sensor: - - platform: atm90e32 - cs_pin: 5 - id: chip1 - phase_a: - voltage: - name: EMON Line Voltage A - current: - name: EMON CT1 Current - power: - name: EMON Active Power CT1 - reactive_power: - name: EMON Reactive Power CT1 - power_factor: - name: EMON Power Factor CT1 - gain_voltage: 7305 - gain_ct: 27961 - phase_b: - current: - name: EMON CT2 Current - power: - name: EMON Active Power CT2 - reactive_power: - name: EMON Reactive Power CT2 - power_factor: - name: EMON Power Factor CT2 - gain_voltage: 7305 - gain_ct: 27961 - phase_c: - current: - name: EMON CT3 Current - power: - name: EMON Active Power CT3 - reactive_power: - name: EMON Reactive Power CT3 - power_factor: - name: EMON Power Factor CT3 - gain_voltage: 7305 - gain_ct: 27961 - frequency: - name: EMON Line Frequency - chip_temperature: - name: EMON Chip Temp A - line_frequency: 60Hz - current_phases: 3 - gain_pga: 2X - enable_offset_calibration: True -button: - - platform: atm90e32 - id: chip1 - run_offset_calibration: - name: "Chip1 - Run Offset Calibration" - clear_offset_calibration: - name: "Chip1 - Clear Offset Calibration" +<<: !include common.yaml From 53c15f671691cf3ab8ac8bfc9961600afd910353 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:02 -0600 Subject: [PATCH 0952/1052] [CI] Consolidate some tests (B) (#8185) --- tests/components/ballu/common.yaml | 12 +++++++ tests/components/ballu/test.esp32-ard.yaml | 14 ++------ tests/components/ballu/test.esp8266-ard.yaml | 14 ++------ tests/components/bh1750/common.yaml | 10 ++++++ tests/components/bh1750/test.esp32-ard.yaml | 13 +++----- .../components/bh1750/test.esp32-c3-ard.yaml | 13 +++----- .../components/bh1750/test.esp32-c3-idf.yaml | 13 +++----- tests/components/bh1750/test.esp32-idf.yaml | 13 +++----- tests/components/bh1750/test.esp8266-ard.yaml | 13 +++----- tests/components/bh1750/test.rp2040-ard.yaml | 13 +++----- tests/components/bl0906/common.yaml | 6 ++-- tests/components/bl0939/common.yaml | 24 ++++++++++++++ tests/components/bl0939/test.esp32-ard.yaml | 29 +++-------------- .../components/bl0939/test.esp32-c3-ard.yaml | 29 +++-------------- .../components/bl0939/test.esp32-c3-idf.yaml | 29 +++-------------- tests/components/bl0939/test.esp32-idf.yaml | 29 +++-------------- tests/components/bl0939/test.esp8266-ard.yaml | 29 +++-------------- tests/components/bl0939/test.rp2040-ard.yaml | 29 +++-------------- tests/components/bl0940/common.yaml | 20 ++++++++++++ tests/components/bl0940/test.esp32-ard.yaml | 25 +++------------ .../components/bl0940/test.esp32-c3-ard.yaml | 25 +++------------ .../components/bl0940/test.esp32-c3-idf.yaml | 25 +++------------ tests/components/bl0940/test.esp32-idf.yaml | 25 +++------------ tests/components/bl0940/test.esp8266-ard.yaml | 25 +++------------ tests/components/bl0940/test.rp2040-ard.yaml | 25 +++------------ tests/components/bl0942/common.yaml | 19 +++++++++++ tests/components/bl0942/test.bk72xx-ard.yaml | 30 +++-------------- tests/components/bl0942/test.esp32-ard.yaml | 24 +++----------- .../components/bl0942/test.esp32-c3-ard.yaml | 23 +++---------- .../components/bl0942/test.esp32-c3-idf.yaml | 23 +++---------- tests/components/bl0942/test.esp32-idf.yaml | 23 +++---------- tests/components/bl0942/test.esp8266-ard.yaml | 23 +++---------- tests/components/bl0942/test.rp2040-ard.yaml | 25 +++------------ tests/components/bme680/common.yaml | 21 ++++++++++++ tests/components/bme680/test.esp32-ard.yaml | 24 +++----------- .../components/bme680/test.esp32-c3-ard.yaml | 24 +++----------- .../components/bme680/test.esp32-c3-idf.yaml | 24 +++----------- tests/components/bme680/test.esp32-idf.yaml | 24 +++----------- tests/components/bme680/test.esp8266-ard.yaml | 24 +++----------- tests/components/bme680/test.rp2040-ard.yaml | 24 +++----------- tests/components/bme680_bsec/common.yaml | 29 +++++++++++++++++ .../bme680_bsec/test.esp32-ard.yaml | 32 +++---------------- .../bme680_bsec/test.esp8266-ard.yaml | 32 +++---------------- tests/components/bmi160/common.yaml | 22 +++++++++++++ tests/components/bmi160/test.esp32-ard.yaml | 25 +++------------ .../components/bmi160/test.esp32-c3-ard.yaml | 25 +++------------ .../components/bmi160/test.esp32-c3-idf.yaml | 25 +++------------ tests/components/bmi160/test.esp32-idf.yaml | 25 +++------------ tests/components/bmi160/test.esp8266-ard.yaml | 25 +++------------ tests/components/bmi160/test.rp2040-ard.yaml | 25 +++------------ tests/components/bmp085/common.yaml | 15 +++++++++ tests/components/bmp085/test.esp32-ard.yaml | 18 +++-------- .../components/bmp085/test.esp32-c3-ard.yaml | 18 +++-------- .../components/bmp085/test.esp32-c3-idf.yaml | 18 +++-------- tests/components/bmp085/test.esp32-idf.yaml | 18 +++-------- tests/components/bmp085/test.esp8266-ard.yaml | 18 +++-------- tests/components/bmp085/test.rp2040-ard.yaml | 18 +++-------- tests/components/bmp581/common.yaml | 13 ++++++++ tests/components/bmp581/test.esp32-ard.yaml | 16 +++------- .../components/bmp581/test.esp32-c3-ard.yaml | 16 +++------- .../components/bmp581/test.esp32-c3-idf.yaml | 16 +++------- tests/components/bmp581/test.esp32-idf.yaml | 16 +++------- tests/components/bmp581/test.esp8266-ard.yaml | 16 +++------- tests/components/bmp581/test.rp2040-ard.yaml | 16 +++------- tests/components/bp1658cj/common.yaml | 22 +++++++++++++ tests/components/bp1658cj/test.esp32-ard.yaml | 25 +++------------ .../bp1658cj/test.esp32-c3-ard.yaml | 25 +++------------ .../bp1658cj/test.esp32-c3-idf.yaml | 25 +++------------ tests/components/bp1658cj/test.esp32-idf.yaml | 25 +++------------ .../components/bp1658cj/test.esp8266-ard.yaml | 25 +++------------ .../components/bp1658cj/test.rp2040-ard.yaml | 25 +++------------ tests/components/bp5758d/common.yaml | 25 +++++++++++++++ tests/components/bp5758d/test.esp32-ard.yaml | 28 +++------------- .../components/bp5758d/test.esp32-c3-ard.yaml | 28 +++------------- .../components/bp5758d/test.esp32-c3-idf.yaml | 28 +++------------- tests/components/bp5758d/test.esp32-idf.yaml | 28 +++------------- .../components/bp5758d/test.esp8266-ard.yaml | 28 +++------------- tests/components/bp5758d/test.rp2040-ard.yaml | 28 +++------------- 78 files changed, 492 insertions(+), 1227 deletions(-) create mode 100644 tests/components/ballu/common.yaml create mode 100644 tests/components/bh1750/common.yaml create mode 100644 tests/components/bl0939/common.yaml create mode 100644 tests/components/bl0940/common.yaml create mode 100644 tests/components/bl0942/common.yaml create mode 100644 tests/components/bme680/common.yaml create mode 100644 tests/components/bme680_bsec/common.yaml create mode 100644 tests/components/bmi160/common.yaml create mode 100644 tests/components/bmp085/common.yaml create mode 100644 tests/components/bmp581/common.yaml create mode 100644 tests/components/bp1658cj/common.yaml create mode 100644 tests/components/bp5758d/common.yaml diff --git a/tests/components/ballu/common.yaml b/tests/components/ballu/common.yaml new file mode 100644 index 0000000000..52f86aa26a --- /dev/null +++ b/tests/components/ballu/common.yaml @@ -0,0 +1,12 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: ballu + horizontal_default: middle + vertical_default: middle + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 diff --git a/tests/components/ballu/test.esp32-ard.yaml b/tests/components/ballu/test.esp32-ard.yaml index bb7b9b0435..7b012aa64c 100644 --- a/tests/components/ballu/test.esp32-ard.yaml +++ b/tests/components/ballu/test.esp32-ard.yaml @@ -1,12 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: heatpumpir - protocol: ballu - horizontal_default: middle - vertical_default: middle - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 +<<: !include common.yaml diff --git a/tests/components/ballu/test.esp8266-ard.yaml b/tests/components/ballu/test.esp8266-ard.yaml index 05aa446739..f5097fcf5f 100644 --- a/tests/components/ballu/test.esp8266-ard.yaml +++ b/tests/components/ballu/test.esp8266-ard.yaml @@ -1,12 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: heatpumpir - protocol: ballu - horizontal_default: middle - vertical_default: middle - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 +<<: !include common.yaml diff --git a/tests/components/bh1750/common.yaml b/tests/components/bh1750/common.yaml new file mode 100644 index 0000000000..c0e0bc1c59 --- /dev/null +++ b/tests/components/bh1750/common.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_bh1750 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: bh1750 + name: Living Room Brightness + address: 0x23 + update_interval: 30s diff --git a/tests/components/bh1750/test.esp32-ard.yaml b/tests/components/bh1750/test.esp32-ard.yaml index b10ec231ae..3b761d3fc1 100644 --- a/tests/components/bh1750/test.esp32-ard.yaml +++ b/tests/components/bh1750/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_bh1750 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bh1750 - name: Living Room Brightness - address: 0x23 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/bh1750/test.esp32-c3-ard.yaml b/tests/components/bh1750/test.esp32-c3-ard.yaml index e367de3845..ee2c29ca4e 100644 --- a/tests/components/bh1750/test.esp32-c3-ard.yaml +++ b/tests/components/bh1750/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_bh1750 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bh1750 - name: Living Room Brightness - address: 0x23 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/bh1750/test.esp32-c3-idf.yaml b/tests/components/bh1750/test.esp32-c3-idf.yaml index e367de3845..ee2c29ca4e 100644 --- a/tests/components/bh1750/test.esp32-c3-idf.yaml +++ b/tests/components/bh1750/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_bh1750 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bh1750 - name: Living Room Brightness - address: 0x23 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/bh1750/test.esp32-idf.yaml b/tests/components/bh1750/test.esp32-idf.yaml index b10ec231ae..3b761d3fc1 100644 --- a/tests/components/bh1750/test.esp32-idf.yaml +++ b/tests/components/bh1750/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_bh1750 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bh1750 - name: Living Room Brightness - address: 0x23 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/bh1750/test.esp8266-ard.yaml b/tests/components/bh1750/test.esp8266-ard.yaml index e367de3845..ee2c29ca4e 100644 --- a/tests/components/bh1750/test.esp8266-ard.yaml +++ b/tests/components/bh1750/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_bh1750 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bh1750 - name: Living Room Brightness - address: 0x23 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/bh1750/test.rp2040-ard.yaml b/tests/components/bh1750/test.rp2040-ard.yaml index e367de3845..ee2c29ca4e 100644 --- a/tests/components/bh1750/test.rp2040-ard.yaml +++ b/tests/components/bh1750/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_bh1750 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bh1750 - name: Living Room Brightness - address: 0x23 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/bl0906/common.yaml b/tests/components/bl0906/common.yaml index 29321a9471..29b82a5958 100644 --- a/tests/components/bl0906/common.yaml +++ b/tests/components/bl0906/common.yaml @@ -1,9 +1,7 @@ uart: - id: uart_bl0906 - tx_pin: - number: ${tx_pin} - rx_pin: - number: ${rx_pin} + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} baud_rate: 19200 sensor: diff --git a/tests/components/bl0939/common.yaml b/tests/components/bl0939/common.yaml new file mode 100644 index 0000000000..7a6b635b70 --- /dev/null +++ b/tests/components/bl0939/common.yaml @@ -0,0 +1,24 @@ +uart: + - id: uart_bl0939 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: bl0939 + voltage: + name: BL0939 Voltage + current_1: + name: BL0939 Current 1 + current_2: + name: BL0939 Current 2 + active_power_1: + name: BL0939 Active Power 1 + active_power_2: + name: BL0939 Active Power 2 + energy_1: + name: BL0939 Energy 1 + energy_2: + name: BL0939 Energy 2 + energy_total: + name: BL0939 Total energy diff --git a/tests/components/bl0939/test.esp32-ard.yaml b/tests/components/bl0939/test.esp32-ard.yaml index df0e683b2f..811f6b72a6 100644 --- a/tests/components/bl0939/test.esp32-ard.yaml +++ b/tests/components/bl0939/test.esp32-ard.yaml @@ -1,26 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: bl0939 - voltage: - name: BL0939 Voltage - current_1: - name: BL0939 Current 1 - current_2: - name: BL0939 Current 2 - active_power_1: - name: BL0939 Active Power 1 - active_power_2: - name: BL0939 Active Power 2 - energy_1: - name: BL0939 Energy 1 - energy_2: - name: BL0939 Energy 2 - energy_total: - name: BL0939 Total energy +<<: !include common.yaml diff --git a/tests/components/bl0939/test.esp32-c3-ard.yaml b/tests/components/bl0939/test.esp32-c3-ard.yaml index 4c92ccb7dd..c79d14c740 100644 --- a/tests/components/bl0939/test.esp32-c3-ard.yaml +++ b/tests/components/bl0939/test.esp32-c3-ard.yaml @@ -1,26 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: bl0939 - voltage: - name: BL0939 Voltage - current_1: - name: BL0939 Current 1 - current_2: - name: BL0939 Current 2 - active_power_1: - name: BL0939 Active Power 1 - active_power_2: - name: BL0939 Active Power 2 - energy_1: - name: BL0939 Energy 1 - energy_2: - name: BL0939 Energy 2 - energy_total: - name: BL0939 Total energy +<<: !include common.yaml diff --git a/tests/components/bl0939/test.esp32-c3-idf.yaml b/tests/components/bl0939/test.esp32-c3-idf.yaml index 4c92ccb7dd..c79d14c740 100644 --- a/tests/components/bl0939/test.esp32-c3-idf.yaml +++ b/tests/components/bl0939/test.esp32-c3-idf.yaml @@ -1,26 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: bl0939 - voltage: - name: BL0939 Voltage - current_1: - name: BL0939 Current 1 - current_2: - name: BL0939 Current 2 - active_power_1: - name: BL0939 Active Power 1 - active_power_2: - name: BL0939 Active Power 2 - energy_1: - name: BL0939 Energy 1 - energy_2: - name: BL0939 Energy 2 - energy_total: - name: BL0939 Total energy +<<: !include common.yaml diff --git a/tests/components/bl0939/test.esp32-idf.yaml b/tests/components/bl0939/test.esp32-idf.yaml index df0e683b2f..811f6b72a6 100644 --- a/tests/components/bl0939/test.esp32-idf.yaml +++ b/tests/components/bl0939/test.esp32-idf.yaml @@ -1,26 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: bl0939 - voltage: - name: BL0939 Voltage - current_1: - name: BL0939 Current 1 - current_2: - name: BL0939 Current 2 - active_power_1: - name: BL0939 Active Power 1 - active_power_2: - name: BL0939 Active Power 2 - energy_1: - name: BL0939 Energy 1 - energy_2: - name: BL0939 Energy 2 - energy_total: - name: BL0939 Total energy +<<: !include common.yaml diff --git a/tests/components/bl0939/test.esp8266-ard.yaml b/tests/components/bl0939/test.esp8266-ard.yaml index 4c92ccb7dd..3b44f9c9c3 100644 --- a/tests/components/bl0939/test.esp8266-ard.yaml +++ b/tests/components/bl0939/test.esp8266-ard.yaml @@ -1,26 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -sensor: - - platform: bl0939 - voltage: - name: BL0939 Voltage - current_1: - name: BL0939 Current 1 - current_2: - name: BL0939 Current 2 - active_power_1: - name: BL0939 Active Power 1 - active_power_2: - name: BL0939 Active Power 2 - energy_1: - name: BL0939 Energy 1 - energy_2: - name: BL0939 Energy 2 - energy_total: - name: BL0939 Total energy +<<: !include common.yaml diff --git a/tests/components/bl0939/test.rp2040-ard.yaml b/tests/components/bl0939/test.rp2040-ard.yaml index 4c92ccb7dd..b516342f3b 100644 --- a/tests/components/bl0939/test.rp2040-ard.yaml +++ b/tests/components/bl0939/test.rp2040-ard.yaml @@ -1,26 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: bl0939 - voltage: - name: BL0939 Voltage - current_1: - name: BL0939 Current 1 - current_2: - name: BL0939 Current 2 - active_power_1: - name: BL0939 Active Power 1 - active_power_2: - name: BL0939 Active Power 2 - energy_1: - name: BL0939 Energy 1 - energy_2: - name: BL0939 Energy 2 - energy_total: - name: BL0939 Total energy +<<: !include common.yaml diff --git a/tests/components/bl0940/common.yaml b/tests/components/bl0940/common.yaml new file mode 100644 index 0000000000..97a997d2b4 --- /dev/null +++ b/tests/components/bl0940/common.yaml @@ -0,0 +1,20 @@ +uart: + - id: uart_bl0939 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: bl0940 + voltage: + name: BL0940 Voltage + current: + name: BL0940 Current + power: + name: BL0940 Power + energy: + name: BL0940 Energy + internal_temperature: + name: BL0940 Internal temperature + external_temperature: + name: BL0940 External temperature diff --git a/tests/components/bl0940/test.esp32-ard.yaml b/tests/components/bl0940/test.esp32-ard.yaml index c7d97ca3b9..811f6b72a6 100644 --- a/tests/components/bl0940/test.esp32-ard.yaml +++ b/tests/components/bl0940/test.esp32-ard.yaml @@ -1,22 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: bl0940 - voltage: - name: BL0940 Voltage - current: - name: BL0940 Current - power: - name: BL0940 Power - energy: - name: BL0940 Energy - internal_temperature: - name: BL0940 Internal temperature - external_temperature: - name: BL0940 External temperature +<<: !include common.yaml diff --git a/tests/components/bl0940/test.esp32-c3-ard.yaml b/tests/components/bl0940/test.esp32-c3-ard.yaml index a20f785b02..c79d14c740 100644 --- a/tests/components/bl0940/test.esp32-c3-ard.yaml +++ b/tests/components/bl0940/test.esp32-c3-ard.yaml @@ -1,22 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: bl0940 - voltage: - name: BL0940 Voltage - current: - name: BL0940 Current - power: - name: BL0940 Power - energy: - name: BL0940 Energy - internal_temperature: - name: BL0940 Internal temperature - external_temperature: - name: BL0940 External temperature +<<: !include common.yaml diff --git a/tests/components/bl0940/test.esp32-c3-idf.yaml b/tests/components/bl0940/test.esp32-c3-idf.yaml index a20f785b02..c79d14c740 100644 --- a/tests/components/bl0940/test.esp32-c3-idf.yaml +++ b/tests/components/bl0940/test.esp32-c3-idf.yaml @@ -1,22 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: bl0940 - voltage: - name: BL0940 Voltage - current: - name: BL0940 Current - power: - name: BL0940 Power - energy: - name: BL0940 Energy - internal_temperature: - name: BL0940 Internal temperature - external_temperature: - name: BL0940 External temperature +<<: !include common.yaml diff --git a/tests/components/bl0940/test.esp32-idf.yaml b/tests/components/bl0940/test.esp32-idf.yaml index c7d97ca3b9..811f6b72a6 100644 --- a/tests/components/bl0940/test.esp32-idf.yaml +++ b/tests/components/bl0940/test.esp32-idf.yaml @@ -1,22 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: bl0940 - voltage: - name: BL0940 Voltage - current: - name: BL0940 Current - power: - name: BL0940 Power - energy: - name: BL0940 Energy - internal_temperature: - name: BL0940 Internal temperature - external_temperature: - name: BL0940 External temperature +<<: !include common.yaml diff --git a/tests/components/bl0940/test.esp8266-ard.yaml b/tests/components/bl0940/test.esp8266-ard.yaml index a20f785b02..3b44f9c9c3 100644 --- a/tests/components/bl0940/test.esp8266-ard.yaml +++ b/tests/components/bl0940/test.esp8266-ard.yaml @@ -1,22 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -sensor: - - platform: bl0940 - voltage: - name: BL0940 Voltage - current: - name: BL0940 Current - power: - name: BL0940 Power - energy: - name: BL0940 Energy - internal_temperature: - name: BL0940 Internal temperature - external_temperature: - name: BL0940 External temperature +<<: !include common.yaml diff --git a/tests/components/bl0940/test.rp2040-ard.yaml b/tests/components/bl0940/test.rp2040-ard.yaml index a20f785b02..b516342f3b 100644 --- a/tests/components/bl0940/test.rp2040-ard.yaml +++ b/tests/components/bl0940/test.rp2040-ard.yaml @@ -1,22 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: bl0940 - voltage: - name: BL0940 Voltage - current: - name: BL0940 Current - power: - name: BL0940 Power - energy: - name: BL0940 Energy - internal_temperature: - name: BL0940 Internal temperature - external_temperature: - name: BL0940 External temperature +<<: !include common.yaml diff --git a/tests/components/bl0942/common.yaml b/tests/components/bl0942/common.yaml new file mode 100644 index 0000000000..32da24885f --- /dev/null +++ b/tests/components/bl0942/common.yaml @@ -0,0 +1,19 @@ +uart: + - id: uart_bl0939 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: bl0942 + reset: true + voltage: + name: BL0942 Voltage + current: + name: BL0942 Current + power: + name: BL0942 Power + energy: + name: BL0942 Energy + frequency: + name: BL0942 Frequency diff --git a/tests/components/bl0942/test.bk72xx-ard.yaml b/tests/components/bl0942/test.bk72xx-ard.yaml index ea61734441..96e13c83a9 100644 --- a/tests/components/bl0942/test.bk72xx-ard.yaml +++ b/tests/components/bl0942/test.bk72xx-ard.yaml @@ -1,27 +1,5 @@ -uart: - - id: uart_bl0942 - tx_pin: - number: TX1 - rx_pin: - number: RX1 - baud_rate: 2400 +substitutions: + tx_pin: TX1 + rx_pin: RX1 -sensor: - - platform: bl0942 - address: 0 - line_frequency: 50Hz - reset: false - voltage: - name: BL0942 Voltage - current: - name: BL0942 Current - power: - name: BL0942 Power - energy: - name: BL0942 Energy - frequency: - name: BL0942 Frequency - voltage_reference: 15968 - current_reference: 124180 - power_reference: 309.1 - energy_reference: 2653 +<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-ard.yaml b/tests/components/bl0942/test.esp32-ard.yaml index 4138543967..811f6b72a6 100644 --- a/tests/components/bl0942/test.esp32-ard.yaml +++ b/tests/components/bl0942/test.esp32-ard.yaml @@ -1,21 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: bl0942 - reset: true - voltage: - name: BL0942 Voltage - current: - name: BL0942 Current - power: - name: BL0942 Power - energy: - name: BL0942 Energy - frequency: - name: BL0942 Frequency +<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-c3-ard.yaml b/tests/components/bl0942/test.esp32-c3-ard.yaml index 8d16efed4f..c79d14c740 100644 --- a/tests/components/bl0942/test.esp32-c3-ard.yaml +++ b/tests/components/bl0942/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: bl0942 - voltage: - name: BL0942 Voltage - current: - name: BL0942 Current - power: - name: BL0942 Power - energy: - name: BL0942 Energy - frequency: - name: BL0942 Frequency +<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-c3-idf.yaml b/tests/components/bl0942/test.esp32-c3-idf.yaml index 8d16efed4f..c79d14c740 100644 --- a/tests/components/bl0942/test.esp32-c3-idf.yaml +++ b/tests/components/bl0942/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: bl0942 - voltage: - name: BL0942 Voltage - current: - name: BL0942 Current - power: - name: BL0942 Power - energy: - name: BL0942 Energy - frequency: - name: BL0942 Frequency +<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp32-idf.yaml b/tests/components/bl0942/test.esp32-idf.yaml index 45ac85aa2a..811f6b72a6 100644 --- a/tests/components/bl0942/test.esp32-idf.yaml +++ b/tests/components/bl0942/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: bl0942 - voltage: - name: BL0942 Voltage - current: - name: BL0942 Current - power: - name: BL0942 Power - energy: - name: BL0942 Energy - frequency: - name: BL0942 Frequency +<<: !include common.yaml diff --git a/tests/components/bl0942/test.esp8266-ard.yaml b/tests/components/bl0942/test.esp8266-ard.yaml index 8d16efed4f..3b44f9c9c3 100644 --- a/tests/components/bl0942/test.esp8266-ard.yaml +++ b/tests/components/bl0942/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -sensor: - - platform: bl0942 - voltage: - name: BL0942 Voltage - current: - name: BL0942 Current - power: - name: BL0942 Power - energy: - name: BL0942 Energy - frequency: - name: BL0942 Frequency +<<: !include common.yaml diff --git a/tests/components/bl0942/test.rp2040-ard.yaml b/tests/components/bl0942/test.rp2040-ard.yaml index d07e0c4402..b516342f3b 100644 --- a/tests/components/bl0942/test.rp2040-ard.yaml +++ b/tests/components/bl0942/test.rp2040-ard.yaml @@ -1,22 +1,5 @@ -uart: - - id: uart_bl0939 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: bl0942 - voltage: - name: BL0942 Voltage - current: - name: BL0942 Current - power: - name: BL0942 Power - energy: - name: BL0942 Energy - frequency: - name: BL0942 Frequency - voltage_reference: 15968 - current_reference: 124180 +<<: !include common.yaml diff --git a/tests/components/bme680/common.yaml b/tests/components/bme680/common.yaml new file mode 100644 index 0000000000..13a42488f2 --- /dev/null +++ b/tests/components/bme680/common.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_bme680 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: bme680 + temperature: + name: BME680 Temperature + oversampling: 16x + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + address: 0x77 + heater: + temperature: 320 + duration: 150ms + update_interval: 15s diff --git a/tests/components/bme680/test.esp32-ard.yaml b/tests/components/bme680/test.esp32-ard.yaml index 04d0ed8fe4..3b761d3fc1 100644 --- a/tests/components/bme680/test.esp32-ard.yaml +++ b/tests/components/bme680/test.esp32-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bme680 - temperature: - name: BME680 Temperature - oversampling: 16x - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - address: 0x77 - heater: - temperature: 320 - duration: 150ms - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bme680/test.esp32-c3-ard.yaml b/tests/components/bme680/test.esp32-c3-ard.yaml index f12be09d20..ee2c29ca4e 100644 --- a/tests/components/bme680/test.esp32-c3-ard.yaml +++ b/tests/components/bme680/test.esp32-c3-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bme680 - temperature: - name: BME680 Temperature - oversampling: 16x - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - address: 0x77 - heater: - temperature: 320 - duration: 150ms - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bme680/test.esp32-c3-idf.yaml b/tests/components/bme680/test.esp32-c3-idf.yaml index f12be09d20..ee2c29ca4e 100644 --- a/tests/components/bme680/test.esp32-c3-idf.yaml +++ b/tests/components/bme680/test.esp32-c3-idf.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bme680 - temperature: - name: BME680 Temperature - oversampling: 16x - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - address: 0x77 - heater: - temperature: 320 - duration: 150ms - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bme680/test.esp32-idf.yaml b/tests/components/bme680/test.esp32-idf.yaml index 04d0ed8fe4..3b761d3fc1 100644 --- a/tests/components/bme680/test.esp32-idf.yaml +++ b/tests/components/bme680/test.esp32-idf.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bme680 - temperature: - name: BME680 Temperature - oversampling: 16x - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - address: 0x77 - heater: - temperature: 320 - duration: 150ms - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bme680/test.esp8266-ard.yaml b/tests/components/bme680/test.esp8266-ard.yaml index f12be09d20..ee2c29ca4e 100644 --- a/tests/components/bme680/test.esp8266-ard.yaml +++ b/tests/components/bme680/test.esp8266-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bme680 - temperature: - name: BME680 Temperature - oversampling: 16x - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - address: 0x77 - heater: - temperature: 320 - duration: 150ms - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bme680/test.rp2040-ard.yaml b/tests/components/bme680/test.rp2040-ard.yaml index f12be09d20..ee2c29ca4e 100644 --- a/tests/components/bme680/test.rp2040-ard.yaml +++ b/tests/components/bme680/test.rp2040-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bme680 - temperature: - name: BME680 Temperature - oversampling: 16x - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - address: 0x77 - heater: - temperature: 320 - duration: 150ms - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bme680_bsec/common.yaml b/tests/components/bme680_bsec/common.yaml new file mode 100644 index 0000000000..7d2e9e210b --- /dev/null +++ b/tests/components/bme680_bsec/common.yaml @@ -0,0 +1,29 @@ +i2c: + - id: i2c_bme680 + scl: ${scl_pin} + sda: ${sda_pin} + +bme680_bsec: + address: 0x77 + +sensor: + - platform: bme680_bsec + temperature: + name: BME680 Temperature + pressure: + name: BME680 Pressure + humidity: + name: BME680 Humidity + gas_resistance: + name: BME680 Gas Sensor + iaq: + name: BME680 IAQ + co2_equivalent: + name: BME680 eCO2 + breath_voc_equivalent: + name: BME680 Breath eVOC + +text_sensor: + - platform: bme680_bsec + iaq_accuracy: + name: BME680 Accuracy diff --git a/tests/components/bme680_bsec/test.esp32-ard.yaml b/tests/components/bme680_bsec/test.esp32-ard.yaml index 4f62f13abb..3b761d3fc1 100644 --- a/tests/components/bme680_bsec/test.esp32-ard.yaml +++ b/tests/components/bme680_bsec/test.esp32-ard.yaml @@ -1,29 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -bme680_bsec: - address: 0x77 - -sensor: - - platform: bme680_bsec - temperature: - name: BME680 Temperature - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - iaq: - name: BME680 IAQ - co2_equivalent: - name: BME680 eCO2 - breath_voc_equivalent: - name: BME680 Breath eVOC - -text_sensor: - - platform: bme680_bsec - iaq_accuracy: - name: BME680 Accuracy +<<: !include common.yaml diff --git a/tests/components/bme680_bsec/test.esp8266-ard.yaml b/tests/components/bme680_bsec/test.esp8266-ard.yaml index 84b32d3635..ee2c29ca4e 100644 --- a/tests/components/bme680_bsec/test.esp8266-ard.yaml +++ b/tests/components/bme680_bsec/test.esp8266-ard.yaml @@ -1,29 +1,5 @@ -i2c: - - id: i2c_bme680 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -bme680_bsec: - address: 0x77 - -sensor: - - platform: bme680_bsec - temperature: - name: BME680 Temperature - pressure: - name: BME680 Pressure - humidity: - name: BME680 Humidity - gas_resistance: - name: BME680 Gas Sensor - iaq: - name: BME680 IAQ - co2_equivalent: - name: BME680 eCO2 - breath_voc_equivalent: - name: BME680 Breath eVOC - -text_sensor: - - platform: bme680_bsec - iaq_accuracy: - name: BME680 Accuracy +<<: !include common.yaml diff --git a/tests/components/bmi160/common.yaml b/tests/components/bmi160/common.yaml new file mode 100644 index 0000000000..6aa9aa6ed0 --- /dev/null +++ b/tests/components/bmi160/common.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_bmi160 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: bmi160 + address: 0x68 + acceleration_x: + name: BMI160 Accel X + acceleration_y: + name: BMI160 Accel Y + acceleration_z: + name: BMI160 Accel z + gyroscope_x: + name: BMI160 Gyro X + gyroscope_y: + name: BMI160 Gyro Y + gyroscope_z: + name: BMI160 Gyro z + temperature: + name: BMI160 Temperature diff --git a/tests/components/bmi160/test.esp32-ard.yaml b/tests/components/bmi160/test.esp32-ard.yaml index a8a90c8c87..3b761d3fc1 100644 --- a/tests/components/bmi160/test.esp32-ard.yaml +++ b/tests/components/bmi160/test.esp32-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_bmi160 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bmi160 - address: 0x68 - acceleration_x: - name: BMI160 Accel X - acceleration_y: - name: BMI160 Accel Y - acceleration_z: - name: BMI160 Accel z - gyroscope_x: - name: BMI160 Gyro X - gyroscope_y: - name: BMI160 Gyro Y - gyroscope_z: - name: BMI160 Gyro z - temperature: - name: BMI160 Temperature +<<: !include common.yaml diff --git a/tests/components/bmi160/test.esp32-c3-ard.yaml b/tests/components/bmi160/test.esp32-c3-ard.yaml index 3fd6441980..ee2c29ca4e 100644 --- a/tests/components/bmi160/test.esp32-c3-ard.yaml +++ b/tests/components/bmi160/test.esp32-c3-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_bmi160 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmi160 - address: 0x68 - acceleration_x: - name: BMI160 Accel X - acceleration_y: - name: BMI160 Accel Y - acceleration_z: - name: BMI160 Accel z - gyroscope_x: - name: BMI160 Gyro X - gyroscope_y: - name: BMI160 Gyro Y - gyroscope_z: - name: BMI160 Gyro z - temperature: - name: BMI160 Temperature +<<: !include common.yaml diff --git a/tests/components/bmi160/test.esp32-c3-idf.yaml b/tests/components/bmi160/test.esp32-c3-idf.yaml index 3fd6441980..ee2c29ca4e 100644 --- a/tests/components/bmi160/test.esp32-c3-idf.yaml +++ b/tests/components/bmi160/test.esp32-c3-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_bmi160 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmi160 - address: 0x68 - acceleration_x: - name: BMI160 Accel X - acceleration_y: - name: BMI160 Accel Y - acceleration_z: - name: BMI160 Accel z - gyroscope_x: - name: BMI160 Gyro X - gyroscope_y: - name: BMI160 Gyro Y - gyroscope_z: - name: BMI160 Gyro z - temperature: - name: BMI160 Temperature +<<: !include common.yaml diff --git a/tests/components/bmi160/test.esp32-idf.yaml b/tests/components/bmi160/test.esp32-idf.yaml index a8a90c8c87..3b761d3fc1 100644 --- a/tests/components/bmi160/test.esp32-idf.yaml +++ b/tests/components/bmi160/test.esp32-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_bmi160 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bmi160 - address: 0x68 - acceleration_x: - name: BMI160 Accel X - acceleration_y: - name: BMI160 Accel Y - acceleration_z: - name: BMI160 Accel z - gyroscope_x: - name: BMI160 Gyro X - gyroscope_y: - name: BMI160 Gyro Y - gyroscope_z: - name: BMI160 Gyro z - temperature: - name: BMI160 Temperature +<<: !include common.yaml diff --git a/tests/components/bmi160/test.esp8266-ard.yaml b/tests/components/bmi160/test.esp8266-ard.yaml index 3fd6441980..ee2c29ca4e 100644 --- a/tests/components/bmi160/test.esp8266-ard.yaml +++ b/tests/components/bmi160/test.esp8266-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_bmi160 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmi160 - address: 0x68 - acceleration_x: - name: BMI160 Accel X - acceleration_y: - name: BMI160 Accel Y - acceleration_z: - name: BMI160 Accel z - gyroscope_x: - name: BMI160 Gyro X - gyroscope_y: - name: BMI160 Gyro Y - gyroscope_z: - name: BMI160 Gyro z - temperature: - name: BMI160 Temperature +<<: !include common.yaml diff --git a/tests/components/bmi160/test.rp2040-ard.yaml b/tests/components/bmi160/test.rp2040-ard.yaml index 3fd6441980..ee2c29ca4e 100644 --- a/tests/components/bmi160/test.rp2040-ard.yaml +++ b/tests/components/bmi160/test.rp2040-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_bmi160 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmi160 - address: 0x68 - acceleration_x: - name: BMI160 Accel X - acceleration_y: - name: BMI160 Accel Y - acceleration_z: - name: BMI160 Accel z - gyroscope_x: - name: BMI160 Gyro X - gyroscope_y: - name: BMI160 Gyro Y - gyroscope_z: - name: BMI160 Gyro z - temperature: - name: BMI160 Temperature +<<: !include common.yaml diff --git a/tests/components/bmp085/common.yaml b/tests/components/bmp085/common.yaml new file mode 100644 index 0000000000..219bc51fbb --- /dev/null +++ b/tests/components/bmp085/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_bmp085 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: bmp085 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + filters: + - lambda: >- + return x / powf(1.0 - (x / 44330.0), 5.255); + update_interval: 15s diff --git a/tests/components/bmp085/test.esp32-ard.yaml b/tests/components/bmp085/test.esp32-ard.yaml index 8a4f714ddd..3b761d3fc1 100644 --- a/tests/components/bmp085/test.esp32-ard.yaml +++ b/tests/components/bmp085/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_bmp085 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bmp085 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - filters: - - lambda: >- - return x / powf(1.0 - (x / 44330.0), 5.255); - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bmp085/test.esp32-c3-ard.yaml b/tests/components/bmp085/test.esp32-c3-ard.yaml index 76a9fd07ba..ee2c29ca4e 100644 --- a/tests/components/bmp085/test.esp32-c3-ard.yaml +++ b/tests/components/bmp085/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_bmp085 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp085 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - filters: - - lambda: >- - return x / powf(1.0 - (x / 44330.0), 5.255); - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bmp085/test.esp32-c3-idf.yaml b/tests/components/bmp085/test.esp32-c3-idf.yaml index 76a9fd07ba..ee2c29ca4e 100644 --- a/tests/components/bmp085/test.esp32-c3-idf.yaml +++ b/tests/components/bmp085/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_bmp085 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp085 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - filters: - - lambda: >- - return x / powf(1.0 - (x / 44330.0), 5.255); - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bmp085/test.esp32-idf.yaml b/tests/components/bmp085/test.esp32-idf.yaml index 8a4f714ddd..3b761d3fc1 100644 --- a/tests/components/bmp085/test.esp32-idf.yaml +++ b/tests/components/bmp085/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_bmp085 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bmp085 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - filters: - - lambda: >- - return x / powf(1.0 - (x / 44330.0), 5.255); - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bmp085/test.esp8266-ard.yaml b/tests/components/bmp085/test.esp8266-ard.yaml index 76a9fd07ba..ee2c29ca4e 100644 --- a/tests/components/bmp085/test.esp8266-ard.yaml +++ b/tests/components/bmp085/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_bmp085 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp085 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - filters: - - lambda: >- - return x / powf(1.0 - (x / 44330.0), 5.255); - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bmp085/test.rp2040-ard.yaml b/tests/components/bmp085/test.rp2040-ard.yaml index 76a9fd07ba..ee2c29ca4e 100644 --- a/tests/components/bmp085/test.rp2040-ard.yaml +++ b/tests/components/bmp085/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_bmp085 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp085 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - filters: - - lambda: >- - return x / powf(1.0 - (x / 44330.0), 5.255); - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/bmp581/common.yaml b/tests/components/bmp581/common.yaml new file mode 100644 index 0000000000..71ad4bfb1a --- /dev/null +++ b/tests/components/bmp581/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_bmp581 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: bmp581 + temperature: + name: BMP581 Temperature + iir_filter: 2x + pressure: + name: BMP581 Pressure + oversampling: 128x diff --git a/tests/components/bmp581/test.esp32-ard.yaml b/tests/components/bmp581/test.esp32-ard.yaml index a464b8ce6a..3b761d3fc1 100644 --- a/tests/components/bmp581/test.esp32-ard.yaml +++ b/tests/components/bmp581/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_bmp581 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bmp581 - temperature: - name: "BMP581 Temperature" - iir_filter: 2x - pressure: - name: "BMP581 Pressure" - oversampling: 128x +<<: !include common.yaml diff --git a/tests/components/bmp581/test.esp32-c3-ard.yaml b/tests/components/bmp581/test.esp32-c3-ard.yaml index 29d27afb90..ee2c29ca4e 100644 --- a/tests/components/bmp581/test.esp32-c3-ard.yaml +++ b/tests/components/bmp581/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_bmp581 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp581 - temperature: - name: "BMP581 Temperature" - iir_filter: 2x - pressure: - name: "BMP581 Pressure" - oversampling: 128x +<<: !include common.yaml diff --git a/tests/components/bmp581/test.esp32-c3-idf.yaml b/tests/components/bmp581/test.esp32-c3-idf.yaml index 29d27afb90..ee2c29ca4e 100644 --- a/tests/components/bmp581/test.esp32-c3-idf.yaml +++ b/tests/components/bmp581/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_bmp581 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp581 - temperature: - name: "BMP581 Temperature" - iir_filter: 2x - pressure: - name: "BMP581 Pressure" - oversampling: 128x +<<: !include common.yaml diff --git a/tests/components/bmp581/test.esp32-idf.yaml b/tests/components/bmp581/test.esp32-idf.yaml index a464b8ce6a..3b761d3fc1 100644 --- a/tests/components/bmp581/test.esp32-idf.yaml +++ b/tests/components/bmp581/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_bmp581 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO22 + sda_pin: GPIO21 -sensor: - - platform: bmp581 - temperature: - name: "BMP581 Temperature" - iir_filter: 2x - pressure: - name: "BMP581 Pressure" - oversampling: 128x +<<: !include common.yaml diff --git a/tests/components/bmp581/test.esp8266-ard.yaml b/tests/components/bmp581/test.esp8266-ard.yaml index 29d27afb90..ee2c29ca4e 100644 --- a/tests/components/bmp581/test.esp8266-ard.yaml +++ b/tests/components/bmp581/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_bmp581 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp581 - temperature: - name: "BMP581 Temperature" - iir_filter: 2x - pressure: - name: "BMP581 Pressure" - oversampling: 128x +<<: !include common.yaml diff --git a/tests/components/bmp581/test.rp2040-ard.yaml b/tests/components/bmp581/test.rp2040-ard.yaml index 29d27afb90..ee2c29ca4e 100644 --- a/tests/components/bmp581/test.rp2040-ard.yaml +++ b/tests/components/bmp581/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_bmp581 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: bmp581 - temperature: - name: "BMP581 Temperature" - iir_filter: 2x - pressure: - name: "BMP581 Pressure" - oversampling: 128x +<<: !include common.yaml diff --git a/tests/components/bp1658cj/common.yaml b/tests/components/bp1658cj/common.yaml new file mode 100644 index 0000000000..e1d71bd4f3 --- /dev/null +++ b/tests/components/bp1658cj/common.yaml @@ -0,0 +1,22 @@ +bp1658cj: + clock_pin: ${clock_pin} + data_pin: ${data_pin} + max_power_color_channels: 4 + max_power_white_channels: 6 + +output: + - platform: bp1658cj + id: bp1658cj_red + channel: 1 + - platform: bp1658cj + id: bp1658cj_green + channel: 2 + - platform: bp1658cj + id: bp1658cj_blue + channel: 0 + - platform: bp1658cj + id: bp1658cj_coldwhite + channel: 3 + - platform: bp1658cj + id: bp1658cj_warmwhite + channel: 4 diff --git a/tests/components/bp1658cj/test.esp32-ard.yaml b/tests/components/bp1658cj/test.esp32-ard.yaml index 5f9e25d3bd..d295973e3f 100644 --- a/tests/components/bp1658cj/test.esp32-ard.yaml +++ b/tests/components/bp1658cj/test.esp32-ard.yaml @@ -1,22 +1,5 @@ -bp1658cj: - clock_pin: 16 - data_pin: 17 - max_power_color_channels: 4 - max_power_white_channels: 6 +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 -output: - - platform: bp1658cj - id: bp1658cj_red - channel: 1 - - platform: bp1658cj - id: bp1658cj_green - channel: 2 - - platform: bp1658cj - id: bp1658cj_blue - channel: 0 - - platform: bp1658cj - id: bp1658cj_coldwhite - channel: 3 - - platform: bp1658cj - id: bp1658cj_warmwhite - channel: 4 +<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp32-c3-ard.yaml b/tests/components/bp1658cj/test.esp32-c3-ard.yaml index 74d3155371..7808481215 100644 --- a/tests/components/bp1658cj/test.esp32-c3-ard.yaml +++ b/tests/components/bp1658cj/test.esp32-c3-ard.yaml @@ -1,22 +1,5 @@ -bp1658cj: - clock_pin: 5 - data_pin: 4 - max_power_color_channels: 4 - max_power_white_channels: 6 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp1658cj - id: bp1658cj_red - channel: 1 - - platform: bp1658cj - id: bp1658cj_green - channel: 2 - - platform: bp1658cj - id: bp1658cj_blue - channel: 0 - - platform: bp1658cj - id: bp1658cj_coldwhite - channel: 3 - - platform: bp1658cj - id: bp1658cj_warmwhite - channel: 4 +<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp32-c3-idf.yaml b/tests/components/bp1658cj/test.esp32-c3-idf.yaml index 74d3155371..7808481215 100644 --- a/tests/components/bp1658cj/test.esp32-c3-idf.yaml +++ b/tests/components/bp1658cj/test.esp32-c3-idf.yaml @@ -1,22 +1,5 @@ -bp1658cj: - clock_pin: 5 - data_pin: 4 - max_power_color_channels: 4 - max_power_white_channels: 6 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp1658cj - id: bp1658cj_red - channel: 1 - - platform: bp1658cj - id: bp1658cj_green - channel: 2 - - platform: bp1658cj - id: bp1658cj_blue - channel: 0 - - platform: bp1658cj - id: bp1658cj_coldwhite - channel: 3 - - platform: bp1658cj - id: bp1658cj_warmwhite - channel: 4 +<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp32-idf.yaml b/tests/components/bp1658cj/test.esp32-idf.yaml index 5f9e25d3bd..d295973e3f 100644 --- a/tests/components/bp1658cj/test.esp32-idf.yaml +++ b/tests/components/bp1658cj/test.esp32-idf.yaml @@ -1,22 +1,5 @@ -bp1658cj: - clock_pin: 16 - data_pin: 17 - max_power_color_channels: 4 - max_power_white_channels: 6 +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 -output: - - platform: bp1658cj - id: bp1658cj_red - channel: 1 - - platform: bp1658cj - id: bp1658cj_green - channel: 2 - - platform: bp1658cj - id: bp1658cj_blue - channel: 0 - - platform: bp1658cj - id: bp1658cj_coldwhite - channel: 3 - - platform: bp1658cj - id: bp1658cj_warmwhite - channel: 4 +<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.esp8266-ard.yaml b/tests/components/bp1658cj/test.esp8266-ard.yaml index 74d3155371..7808481215 100644 --- a/tests/components/bp1658cj/test.esp8266-ard.yaml +++ b/tests/components/bp1658cj/test.esp8266-ard.yaml @@ -1,22 +1,5 @@ -bp1658cj: - clock_pin: 5 - data_pin: 4 - max_power_color_channels: 4 - max_power_white_channels: 6 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp1658cj - id: bp1658cj_red - channel: 1 - - platform: bp1658cj - id: bp1658cj_green - channel: 2 - - platform: bp1658cj - id: bp1658cj_blue - channel: 0 - - platform: bp1658cj - id: bp1658cj_coldwhite - channel: 3 - - platform: bp1658cj - id: bp1658cj_warmwhite - channel: 4 +<<: !include common.yaml diff --git a/tests/components/bp1658cj/test.rp2040-ard.yaml b/tests/components/bp1658cj/test.rp2040-ard.yaml index 74d3155371..7808481215 100644 --- a/tests/components/bp1658cj/test.rp2040-ard.yaml +++ b/tests/components/bp1658cj/test.rp2040-ard.yaml @@ -1,22 +1,5 @@ -bp1658cj: - clock_pin: 5 - data_pin: 4 - max_power_color_channels: 4 - max_power_white_channels: 6 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp1658cj - id: bp1658cj_red - channel: 1 - - platform: bp1658cj - id: bp1658cj_green - channel: 2 - - platform: bp1658cj - id: bp1658cj_blue - channel: 0 - - platform: bp1658cj - id: bp1658cj_coldwhite - channel: 3 - - platform: bp1658cj - id: bp1658cj_warmwhite - channel: 4 +<<: !include common.yaml diff --git a/tests/components/bp5758d/common.yaml b/tests/components/bp5758d/common.yaml new file mode 100644 index 0000000000..4a39a6f69f --- /dev/null +++ b/tests/components/bp5758d/common.yaml @@ -0,0 +1,25 @@ +bp5758d: + clock_pin: ${clock_pin} + data_pin: ${data_pin} + +output: + - platform: bp5758d + id: bp5758d_red + channel: 2 + current: 10 + - platform: bp5758d + id: bp5758d_green + channel: 3 + current: 10 + - platform: bp5758d + id: bp5758d_blue + channel: 1 + current: 10 + - platform: bp5758d + id: bp5758d_coldwhite + channel: 5 + current: 10 + - platform: bp5758d + id: bp5758d_warmwhite + channel: 4 + current: 10 diff --git a/tests/components/bp5758d/test.esp32-ard.yaml b/tests/components/bp5758d/test.esp32-ard.yaml index b7929a0518..d295973e3f 100644 --- a/tests/components/bp5758d/test.esp32-ard.yaml +++ b/tests/components/bp5758d/test.esp32-ard.yaml @@ -1,25 +1,5 @@ -bp5758d: - clock_pin: 16 - data_pin: 17 +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 -output: - - platform: bp5758d - id: bp5758d_red - channel: 2 - current: 10 - - platform: bp5758d - id: bp5758d_green - channel: 3 - current: 10 - - platform: bp5758d - id: bp5758d_blue - channel: 1 - current: 10 - - platform: bp5758d - id: bp5758d_coldwhite - channel: 5 - current: 10 - - platform: bp5758d - id: bp5758d_warmwhite - channel: 4 - current: 10 +<<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp32-c3-ard.yaml b/tests/components/bp5758d/test.esp32-c3-ard.yaml index ec74e935cd..7808481215 100644 --- a/tests/components/bp5758d/test.esp32-c3-ard.yaml +++ b/tests/components/bp5758d/test.esp32-c3-ard.yaml @@ -1,25 +1,5 @@ -bp5758d: - clock_pin: 5 - data_pin: 4 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp5758d - id: bp5758d_red - channel: 2 - current: 10 - - platform: bp5758d - id: bp5758d_green - channel: 3 - current: 10 - - platform: bp5758d - id: bp5758d_blue - channel: 1 - current: 10 - - platform: bp5758d - id: bp5758d_coldwhite - channel: 5 - current: 10 - - platform: bp5758d - id: bp5758d_warmwhite - channel: 4 - current: 10 +<<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp32-c3-idf.yaml b/tests/components/bp5758d/test.esp32-c3-idf.yaml index ec74e935cd..7808481215 100644 --- a/tests/components/bp5758d/test.esp32-c3-idf.yaml +++ b/tests/components/bp5758d/test.esp32-c3-idf.yaml @@ -1,25 +1,5 @@ -bp5758d: - clock_pin: 5 - data_pin: 4 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp5758d - id: bp5758d_red - channel: 2 - current: 10 - - platform: bp5758d - id: bp5758d_green - channel: 3 - current: 10 - - platform: bp5758d - id: bp5758d_blue - channel: 1 - current: 10 - - platform: bp5758d - id: bp5758d_coldwhite - channel: 5 - current: 10 - - platform: bp5758d - id: bp5758d_warmwhite - channel: 4 - current: 10 +<<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp32-idf.yaml b/tests/components/bp5758d/test.esp32-idf.yaml index b7929a0518..d295973e3f 100644 --- a/tests/components/bp5758d/test.esp32-idf.yaml +++ b/tests/components/bp5758d/test.esp32-idf.yaml @@ -1,25 +1,5 @@ -bp5758d: - clock_pin: 16 - data_pin: 17 +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 -output: - - platform: bp5758d - id: bp5758d_red - channel: 2 - current: 10 - - platform: bp5758d - id: bp5758d_green - channel: 3 - current: 10 - - platform: bp5758d - id: bp5758d_blue - channel: 1 - current: 10 - - platform: bp5758d - id: bp5758d_coldwhite - channel: 5 - current: 10 - - platform: bp5758d - id: bp5758d_warmwhite - channel: 4 - current: 10 +<<: !include common.yaml diff --git a/tests/components/bp5758d/test.esp8266-ard.yaml b/tests/components/bp5758d/test.esp8266-ard.yaml index ec74e935cd..7808481215 100644 --- a/tests/components/bp5758d/test.esp8266-ard.yaml +++ b/tests/components/bp5758d/test.esp8266-ard.yaml @@ -1,25 +1,5 @@ -bp5758d: - clock_pin: 5 - data_pin: 4 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp5758d - id: bp5758d_red - channel: 2 - current: 10 - - platform: bp5758d - id: bp5758d_green - channel: 3 - current: 10 - - platform: bp5758d - id: bp5758d_blue - channel: 1 - current: 10 - - platform: bp5758d - id: bp5758d_coldwhite - channel: 5 - current: 10 - - platform: bp5758d - id: bp5758d_warmwhite - channel: 4 - current: 10 +<<: !include common.yaml diff --git a/tests/components/bp5758d/test.rp2040-ard.yaml b/tests/components/bp5758d/test.rp2040-ard.yaml index ec74e935cd..7808481215 100644 --- a/tests/components/bp5758d/test.rp2040-ard.yaml +++ b/tests/components/bp5758d/test.rp2040-ard.yaml @@ -1,25 +1,5 @@ -bp5758d: - clock_pin: 5 - data_pin: 4 +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 -output: - - platform: bp5758d - id: bp5758d_red - channel: 2 - current: 10 - - platform: bp5758d - id: bp5758d_green - channel: 3 - current: 10 - - platform: bp5758d - id: bp5758d_blue - channel: 1 - current: 10 - - platform: bp5758d - id: bp5758d_coldwhite - channel: 5 - current: 10 - - platform: bp5758d - id: bp5758d_warmwhite - channel: 4 - current: 10 +<<: !include common.yaml From 61ad2510fc045f05034975c42516868acb9de5a8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:08 -0600 Subject: [PATCH 0953/1052] [CI] Consolidate some tests (C) (#8186) --- tests/components/cap1188/common.yaml | 11 +++ tests/components/cap1188/test.esp32-ard.yaml | 15 ++-- .../components/cap1188/test.esp32-c3-ard.yaml | 15 ++-- .../components/cap1188/test.esp32-c3-idf.yaml | 15 ++-- tests/components/cap1188/test.esp32-idf.yaml | 15 ++-- .../components/cap1188/test.esp8266-ard.yaml | 15 ++-- tests/components/cap1188/test.rp2040-ard.yaml | 15 ++-- tests/components/ccs811/common.yaml | 13 ++++ tests/components/ccs811/test.esp32-ard.yaml | 16 ++--- .../components/ccs811/test.esp32-c3-ard.yaml | 16 ++--- .../components/ccs811/test.esp32-c3-idf.yaml | 16 ++--- tests/components/ccs811/test.esp32-idf.yaml | 16 ++--- tests/components/ccs811/test.esp8266-ard.yaml | 16 ++--- tests/components/ccs811/test.rp2040-ard.yaml | 16 ++--- tests/components/cd74hc4067/common.yaml | 18 +++++ .../components/cd74hc4067/test.esp32-ard.yaml | 24 ++----- .../cd74hc4067/test.esp32-c3-ard.yaml | 24 ++----- .../cd74hc4067/test.esp32-c3-idf.yaml | 24 ++----- .../components/cd74hc4067/test.esp32-idf.yaml | 24 ++----- .../cd74hc4067/test.esp8266-ard.yaml | 24 ++----- .../cd74hc4067/test.rp2040-ard.yaml | 24 ++----- tests/components/climate_ir_lg/common.yaml | 7 ++ .../climate_ir_lg/test.esp32-ard.yaml | 9 +-- .../climate_ir_lg/test.esp32-c3-ard.yaml | 9 +-- .../climate_ir_lg/test.esp32-c3-idf.yaml | 9 +-- .../climate_ir_lg/test.esp32-idf.yaml | 9 +-- .../climate_ir_lg/test.esp8266-ard.yaml | 9 +-- .../components/color_temperature/common.yaml | 15 ++++ .../color_temperature/test.esp32-ard.yaml | 19 ++--- .../color_temperature/test.esp32-c3-ard.yaml | 19 ++--- .../color_temperature/test.esp32-c3-idf.yaml | 19 ++--- .../color_temperature/test.esp32-idf.yaml | 19 ++--- .../color_temperature/test.esp8266-ard.yaml | 19 ++--- .../color_temperature/test.rp2040-ard.yaml | 19 ++--- tests/components/coolix/common.yaml | 7 ++ tests/components/coolix/test.esp32-ard.yaml | 9 +-- .../components/coolix/test.esp32-c3-ard.yaml | 9 +-- .../components/coolix/test.esp32-c3-idf.yaml | 9 +-- tests/components/coolix/test.esp32-idf.yaml | 9 +-- tests/components/coolix/test.esp8266-ard.yaml | 9 +-- tests/components/copy/common.yaml | 23 ++++++ tests/components/copy/test.esp32-ard.yaml | 26 ++----- tests/components/copy/test.esp32-c3-ard.yaml | 26 ++----- tests/components/copy/test.esp32-c3-idf.yaml | 26 ++----- tests/components/copy/test.esp32-idf.yaml | 26 ++----- tests/components/copy/test.esp8266-ard.yaml | 26 ++----- tests/components/copy/test.rp2040-ard.yaml | 26 ++----- tests/components/cs5460a/common.yaml | 27 +++++++ tests/components/cs5460a/test.esp32-ard.yaml | 32 ++------- .../components/cs5460a/test.esp32-c3-ard.yaml | 32 ++------- .../components/cs5460a/test.esp32-c3-idf.yaml | 32 ++------- tests/components/cs5460a/test.esp32-idf.yaml | 32 ++------- .../components/cs5460a/test.esp8266-ard.yaml | 32 ++------- tests/components/cs5460a/test.rp2040-ard.yaml | 32 ++------- tests/components/cse7761/common.yaml | 18 +++++ tests/components/cse7761/test.esp32-ard.yaml | 23 ++---- .../components/cse7761/test.esp32-c3-ard.yaml | 23 ++---- .../components/cse7761/test.esp32-c3-idf.yaml | 23 ++---- tests/components/cse7761/test.esp32-idf.yaml | 23 ++---- .../components/cse7761/test.esp8266-ard.yaml | 23 ++---- tests/components/cse7761/test.rp2040-ard.yaml | 23 ++---- tests/components/cse7766/common.yaml | 15 ++++ tests/components/cse7766/test.esp32-ard.yaml | 20 ++---- .../components/cse7766/test.esp32-c3-ard.yaml | 20 ++---- .../components/cse7766/test.esp32-c3-idf.yaml | 20 ++---- tests/components/cse7766/test.esp32-idf.yaml | 20 ++---- .../components/cse7766/test.esp8266-ard.yaml | 20 ++---- tests/components/cse7766/test.rp2040-ard.yaml | 20 ++---- tests/components/cst226/common.yaml | 34 ++++----- tests/components/cst226/test.esp32-ard.yaml | 12 ++++ .../components/cst226/test.esp32-c3-ard.yaml | 11 +++ .../components/cst226/test.esp32-c3-idf.yaml | 12 ++++ tests/components/cst226/test.esp32-idf.yaml | 12 ++++ tests/components/cst816/common.yaml | 41 +++++------ tests/components/cst816/test.esp32-ard.yaml | 11 +++ .../components/cst816/test.esp32-c3-ard.yaml | 12 ++++ .../components/cst816/test.esp32-c3-idf.yaml | 12 ++++ tests/components/cst816/test.esp32-idf.yaml | 12 ++++ tests/components/ct_clamp/common.yaml | 9 +++ tests/components/ct_clamp/test.esp32-ard.yaml | 13 ++-- .../ct_clamp/test.esp32-c3-ard.yaml | 13 ++-- .../ct_clamp/test.esp32-c3-idf.yaml | 13 ++-- tests/components/ct_clamp/test.esp32-idf.yaml | 13 ++-- .../components/ct_clamp/test.esp8266-ard.yaml | 13 ++-- .../components/ct_clamp/test.rp2040-ard.yaml | 13 ++-- tests/components/current_based/common.yaml | 68 ++++++++++++++++++ .../current_based/test.esp32-ard.yaml | 72 ++----------------- .../current_based/test.esp32-c3-ard.yaml | 72 ++----------------- .../current_based/test.esp32-c3-idf.yaml | 72 ++----------------- .../current_based/test.esp32-idf.yaml | 72 ++----------------- .../current_based/test.esp8266-ard.yaml | 72 ++----------------- .../current_based/test.rp2040-ard.yaml | 72 ++----------------- tests/components/cwww/common.yaml | 16 +++++ tests/components/cwww/test.esp32-ard.yaml | 20 ++---- tests/components/cwww/test.esp32-c3-ard.yaml | 20 ++---- tests/components/cwww/test.esp32-c3-idf.yaml | 25 +++---- tests/components/cwww/test.esp32-idf.yaml | 25 +++---- tests/components/cwww/test.esp8266-ard.yaml | 20 ++---- tests/components/cwww/test.rp2040-ard.yaml | 20 ++---- 99 files changed, 737 insertions(+), 1459 deletions(-) create mode 100644 tests/components/cap1188/common.yaml create mode 100644 tests/components/ccs811/common.yaml create mode 100644 tests/components/cd74hc4067/common.yaml create mode 100644 tests/components/climate_ir_lg/common.yaml create mode 100644 tests/components/color_temperature/common.yaml create mode 100644 tests/components/coolix/common.yaml create mode 100644 tests/components/copy/common.yaml create mode 100644 tests/components/cs5460a/common.yaml create mode 100644 tests/components/cse7761/common.yaml create mode 100644 tests/components/cse7766/common.yaml create mode 100644 tests/components/cst226/test.esp32-ard.yaml create mode 100644 tests/components/cst226/test.esp32-c3-idf.yaml create mode 100644 tests/components/cst226/test.esp32-idf.yaml create mode 100644 tests/components/cst816/test.esp32-c3-ard.yaml create mode 100644 tests/components/cst816/test.esp32-c3-idf.yaml create mode 100644 tests/components/cst816/test.esp32-idf.yaml create mode 100644 tests/components/ct_clamp/common.yaml create mode 100644 tests/components/current_based/common.yaml create mode 100644 tests/components/cwww/common.yaml diff --git a/tests/components/cap1188/common.yaml b/tests/components/cap1188/common.yaml new file mode 100644 index 0000000000..e83bf5d5d2 --- /dev/null +++ b/tests/components/cap1188/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_cap1188 + scl: ${scl_pin} + sda: ${sda_pin} + +cap1188: + id: cap1188_component + address: 0x29 + reset_pin: ${reset_pin} + touch_threshold: 0x20 + allow_multiple_touches: true diff --git a/tests/components/cap1188/test.esp32-ard.yaml b/tests/components/cap1188/test.esp32-ard.yaml index efd1d60217..1ca773e06c 100644 --- a/tests/components/cap1188/test.esp32-ard.yaml +++ b/tests/components/cap1188/test.esp32-ard.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_cap1188 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -cap1188: - id: cap1188_component - address: 0x29 - reset_pin: 15 - touch_threshold: 0x20 - allow_multiple_touches: true +<<: !include common.yaml diff --git a/tests/components/cap1188/test.esp32-c3-ard.yaml b/tests/components/cap1188/test.esp32-c3-ard.yaml index c6d3c95942..1e6670c196 100644 --- a/tests/components/cap1188/test.esp32-c3-ard.yaml +++ b/tests/components/cap1188/test.esp32-c3-ard.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_cap1188 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -cap1188: - id: cap1188_component - address: 0x29 - reset_pin: 6 - touch_threshold: 0x20 - allow_multiple_touches: true +<<: !include common.yaml diff --git a/tests/components/cap1188/test.esp32-c3-idf.yaml b/tests/components/cap1188/test.esp32-c3-idf.yaml index c6d3c95942..1e6670c196 100644 --- a/tests/components/cap1188/test.esp32-c3-idf.yaml +++ b/tests/components/cap1188/test.esp32-c3-idf.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_cap1188 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -cap1188: - id: cap1188_component - address: 0x29 - reset_pin: 6 - touch_threshold: 0x20 - allow_multiple_touches: true +<<: !include common.yaml diff --git a/tests/components/cap1188/test.esp32-idf.yaml b/tests/components/cap1188/test.esp32-idf.yaml index efd1d60217..1ca773e06c 100644 --- a/tests/components/cap1188/test.esp32-idf.yaml +++ b/tests/components/cap1188/test.esp32-idf.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_cap1188 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -cap1188: - id: cap1188_component - address: 0x29 - reset_pin: 15 - touch_threshold: 0x20 - allow_multiple_touches: true +<<: !include common.yaml diff --git a/tests/components/cap1188/test.esp8266-ard.yaml b/tests/components/cap1188/test.esp8266-ard.yaml index 7573d45140..dfdc12a3d1 100644 --- a/tests/components/cap1188/test.esp8266-ard.yaml +++ b/tests/components/cap1188/test.esp8266-ard.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_cap1188 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO15 -cap1188: - id: cap1188_component - address: 0x29 - reset_pin: 15 - touch_threshold: 0x20 - allow_multiple_touches: true +<<: !include common.yaml diff --git a/tests/components/cap1188/test.rp2040-ard.yaml b/tests/components/cap1188/test.rp2040-ard.yaml index c6d3c95942..1e6670c196 100644 --- a/tests/components/cap1188/test.rp2040-ard.yaml +++ b/tests/components/cap1188/test.rp2040-ard.yaml @@ -1,11 +1,6 @@ -i2c: - - id: i2c_cap1188 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -cap1188: - id: cap1188_component - address: 0x29 - reset_pin: 6 - touch_threshold: 0x20 - allow_multiple_touches: true +<<: !include common.yaml diff --git a/tests/components/ccs811/common.yaml b/tests/components/ccs811/common.yaml new file mode 100644 index 0000000000..a781996c66 --- /dev/null +++ b/tests/components/ccs811/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ccs811 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ccs811 + eco2: + name: CCS811 eCO2 + tvoc: + name: CCS811 TVOC + baseline: 0x4242 + update_interval: 30s diff --git a/tests/components/ccs811/test.esp32-ard.yaml b/tests/components/ccs811/test.esp32-ard.yaml index 08b3a48cc7..63c3bd6afd 100644 --- a/tests/components/ccs811/test.esp32-ard.yaml +++ b/tests/components/ccs811/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ccs811 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ccs811 - eco2: - name: CCS811 eCO2 - tvoc: - name: CCS811 TVOC - baseline: 0x4242 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/ccs811/test.esp32-c3-ard.yaml b/tests/components/ccs811/test.esp32-c3-ard.yaml index 26ec7807e4..ee2c29ca4e 100644 --- a/tests/components/ccs811/test.esp32-c3-ard.yaml +++ b/tests/components/ccs811/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ccs811 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ccs811 - eco2: - name: CCS811 eCO2 - tvoc: - name: CCS811 TVOC - baseline: 0x4242 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/ccs811/test.esp32-c3-idf.yaml b/tests/components/ccs811/test.esp32-c3-idf.yaml index 26ec7807e4..ee2c29ca4e 100644 --- a/tests/components/ccs811/test.esp32-c3-idf.yaml +++ b/tests/components/ccs811/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ccs811 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ccs811 - eco2: - name: CCS811 eCO2 - tvoc: - name: CCS811 TVOC - baseline: 0x4242 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/ccs811/test.esp32-idf.yaml b/tests/components/ccs811/test.esp32-idf.yaml index 08b3a48cc7..63c3bd6afd 100644 --- a/tests/components/ccs811/test.esp32-idf.yaml +++ b/tests/components/ccs811/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ccs811 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ccs811 - eco2: - name: CCS811 eCO2 - tvoc: - name: CCS811 TVOC - baseline: 0x4242 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/ccs811/test.esp8266-ard.yaml b/tests/components/ccs811/test.esp8266-ard.yaml index 26ec7807e4..ee2c29ca4e 100644 --- a/tests/components/ccs811/test.esp8266-ard.yaml +++ b/tests/components/ccs811/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ccs811 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ccs811 - eco2: - name: CCS811 eCO2 - tvoc: - name: CCS811 TVOC - baseline: 0x4242 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/ccs811/test.rp2040-ard.yaml b/tests/components/ccs811/test.rp2040-ard.yaml index 26ec7807e4..ee2c29ca4e 100644 --- a/tests/components/ccs811/test.rp2040-ard.yaml +++ b/tests/components/ccs811/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ccs811 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ccs811 - eco2: - name: CCS811 eCO2 - tvoc: - name: CCS811 TVOC - baseline: 0x4242 - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/cd74hc4067/common.yaml b/tests/components/cd74hc4067/common.yaml new file mode 100644 index 0000000000..9afb39cd31 --- /dev/null +++ b/tests/components/cd74hc4067/common.yaml @@ -0,0 +1,18 @@ +cd74hc4067: + pin_s0: ${pin_s0} + pin_s1: ${pin_s1} + pin_s2: ${pin_s2} + pin_s3: ${pin_s3} + +sensor: + - platform: adc + id: esp_adc_sensor + pin: ${pin} + - platform: cd74hc4067 + id: cd74hc4067_adc_0 + number: 0 + sensor: esp_adc_sensor + - platform: cd74hc4067 + id: cd74hc4067_adc_1 + number: 1 + sensor: esp_adc_sensor diff --git a/tests/components/cd74hc4067/test.esp32-ard.yaml b/tests/components/cd74hc4067/test.esp32-ard.yaml index 71a1238ccc..c4dd280943 100644 --- a/tests/components/cd74hc4067/test.esp32-ard.yaml +++ b/tests/components/cd74hc4067/test.esp32-ard.yaml @@ -1,18 +1,8 @@ -cd74hc4067: - pin_s0: 12 - pin_s1: 13 - pin_s2: 14 - pin_s3: 15 +substitutions: + pin_s0: GPIO12 + pin_s1: GPIO13 + pin_s2: GPIO14 + pin_s3: GPIO15 + pin: GPIO39 -sensor: - - platform: adc - id: esp_adc_sensor - pin: 39 - - platform: cd74hc4067 - id: cd74hc4067_adc_0 - number: 0 - sensor: esp_adc_sensor - - platform: cd74hc4067 - id: cd74hc4067_adc_1 - number: 1 - sensor: esp_adc_sensor +<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.esp32-c3-ard.yaml b/tests/components/cd74hc4067/test.esp32-c3-ard.yaml index 5aa653d26c..5e8784c1fc 100644 --- a/tests/components/cd74hc4067/test.esp32-c3-ard.yaml +++ b/tests/components/cd74hc4067/test.esp32-c3-ard.yaml @@ -1,18 +1,8 @@ -cd74hc4067: - pin_s0: 2 - pin_s1: 3 - pin_s2: 4 - pin_s3: 5 +substitutions: + pin_s0: GPIO2 + pin_s1: GPIO3 + pin_s2: GPIO4 + pin_s3: GPIO5 + pin: GPIO0 -sensor: - - platform: adc - id: esp_adc_sensor - pin: 0 - - platform: cd74hc4067 - id: cd74hc4067_adc_0 - number: 0 - sensor: esp_adc_sensor - - platform: cd74hc4067 - id: cd74hc4067_adc_1 - number: 1 - sensor: esp_adc_sensor +<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.esp32-c3-idf.yaml b/tests/components/cd74hc4067/test.esp32-c3-idf.yaml index 5aa653d26c..5e8784c1fc 100644 --- a/tests/components/cd74hc4067/test.esp32-c3-idf.yaml +++ b/tests/components/cd74hc4067/test.esp32-c3-idf.yaml @@ -1,18 +1,8 @@ -cd74hc4067: - pin_s0: 2 - pin_s1: 3 - pin_s2: 4 - pin_s3: 5 +substitutions: + pin_s0: GPIO2 + pin_s1: GPIO3 + pin_s2: GPIO4 + pin_s3: GPIO5 + pin: GPIO0 -sensor: - - platform: adc - id: esp_adc_sensor - pin: 0 - - platform: cd74hc4067 - id: cd74hc4067_adc_0 - number: 0 - sensor: esp_adc_sensor - - platform: cd74hc4067 - id: cd74hc4067_adc_1 - number: 1 - sensor: esp_adc_sensor +<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.esp32-idf.yaml b/tests/components/cd74hc4067/test.esp32-idf.yaml index 71a1238ccc..c4dd280943 100644 --- a/tests/components/cd74hc4067/test.esp32-idf.yaml +++ b/tests/components/cd74hc4067/test.esp32-idf.yaml @@ -1,18 +1,8 @@ -cd74hc4067: - pin_s0: 12 - pin_s1: 13 - pin_s2: 14 - pin_s3: 15 +substitutions: + pin_s0: GPIO12 + pin_s1: GPIO13 + pin_s2: GPIO14 + pin_s3: GPIO15 + pin: GPIO39 -sensor: - - platform: adc - id: esp_adc_sensor - pin: 39 - - platform: cd74hc4067 - id: cd74hc4067_adc_0 - number: 0 - sensor: esp_adc_sensor - - platform: cd74hc4067 - id: cd74hc4067_adc_1 - number: 1 - sensor: esp_adc_sensor +<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.esp8266-ard.yaml b/tests/components/cd74hc4067/test.esp8266-ard.yaml index 8bcce5bc17..1162cec1f8 100644 --- a/tests/components/cd74hc4067/test.esp8266-ard.yaml +++ b/tests/components/cd74hc4067/test.esp8266-ard.yaml @@ -1,18 +1,8 @@ -cd74hc4067: - pin_s0: 12 - pin_s1: 13 - pin_s2: 14 - pin_s3: 15 +substitutions: + pin_s0: GPIO12 + pin_s1: GPIO13 + pin_s2: GPIO14 + pin_s3: GPIO15 + pin: A0 -sensor: - - platform: adc - id: esp_adc_sensor - pin: A0 - - platform: cd74hc4067 - id: cd74hc4067_adc_0 - number: 0 - sensor: esp_adc_sensor - - platform: cd74hc4067 - id: cd74hc4067_adc_1 - number: 1 - sensor: esp_adc_sensor +<<: !include common.yaml diff --git a/tests/components/cd74hc4067/test.rp2040-ard.yaml b/tests/components/cd74hc4067/test.rp2040-ard.yaml index 75adcce796..88c3273a44 100644 --- a/tests/components/cd74hc4067/test.rp2040-ard.yaml +++ b/tests/components/cd74hc4067/test.rp2040-ard.yaml @@ -1,18 +1,8 @@ -cd74hc4067: - pin_s0: 2 - pin_s1: 3 - pin_s2: 4 - pin_s3: 5 +substitutions: + pin_s0: GPIO2 + pin_s1: GPIO3 + pin_s2: GPIO4 + pin_s3: GPIO5 + pin: GPIO26 -sensor: - - platform: adc - id: esp_adc_sensor - pin: 26 - - platform: cd74hc4067 - id: cd74hc4067_adc_0 - number: 0 - sensor: esp_adc_sensor - - platform: cd74hc4067 - id: cd74hc4067_adc_1 - number: 1 - sensor: esp_adc_sensor +<<: !include common.yaml diff --git a/tests/components/climate_ir_lg/common.yaml b/tests/components/climate_ir_lg/common.yaml new file mode 100644 index 0000000000..c8f84411c0 --- /dev/null +++ b/tests/components/climate_ir_lg/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: climate_ir_lg + name: LG Climate diff --git a/tests/components/climate_ir_lg/test.esp32-ard.yaml b/tests/components/climate_ir_lg/test.esp32-ard.yaml index e714bf0686..7b012aa64c 100644 --- a/tests/components/climate_ir_lg/test.esp32-ard.yaml +++ b/tests/components/climate_ir_lg/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: climate_ir_lg - name: LG Climate +<<: !include common.yaml diff --git a/tests/components/climate_ir_lg/test.esp32-c3-ard.yaml b/tests/components/climate_ir_lg/test.esp32-c3-ard.yaml index e714bf0686..7b012aa64c 100644 --- a/tests/components/climate_ir_lg/test.esp32-c3-ard.yaml +++ b/tests/components/climate_ir_lg/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: climate_ir_lg - name: LG Climate +<<: !include common.yaml diff --git a/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml b/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml index e714bf0686..7b012aa64c 100644 --- a/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml +++ b/tests/components/climate_ir_lg/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: climate_ir_lg - name: LG Climate +<<: !include common.yaml diff --git a/tests/components/climate_ir_lg/test.esp32-idf.yaml b/tests/components/climate_ir_lg/test.esp32-idf.yaml index e714bf0686..7b012aa64c 100644 --- a/tests/components/climate_ir_lg/test.esp32-idf.yaml +++ b/tests/components/climate_ir_lg/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: climate_ir_lg - name: LG Climate +<<: !include common.yaml diff --git a/tests/components/climate_ir_lg/test.esp8266-ard.yaml b/tests/components/climate_ir_lg/test.esp8266-ard.yaml index 7482bf0580..f5097fcf5f 100644 --- a/tests/components/climate_ir_lg/test.esp8266-ard.yaml +++ b/tests/components/climate_ir_lg/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: climate_ir_lg - name: LG Climate +<<: !include common.yaml diff --git a/tests/components/color_temperature/common.yaml b/tests/components/color_temperature/common.yaml new file mode 100644 index 0000000000..fe0c5bf917 --- /dev/null +++ b/tests/components/color_temperature/common.yaml @@ -0,0 +1,15 @@ +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin_o1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin_o2} + +light: + - platform: color_temperature + name: Lights + color_temperature: light_output_1 + brightness: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds diff --git a/tests/components/color_temperature/test.esp32-ard.yaml b/tests/components/color_temperature/test.esp32-ard.yaml index 608907d2fc..1831adda6e 100644 --- a/tests/components/color_temperature/test.esp32-ard.yaml +++ b/tests/components/color_temperature/test.esp32-ard.yaml @@ -1,15 +1,6 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 +substitutions: + light_platform: ledc + pin_o1: GPIO16 + pin_o2: GPIO17 -light: - - platform: color_temperature - name: Lights - color_temperature: light_output_1 - brightness: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/color_temperature/test.esp32-c3-ard.yaml b/tests/components/color_temperature/test.esp32-c3-ard.yaml index 8d3faa54ee..016f315d9f 100644 --- a/tests/components/color_temperature/test.esp32-c3-ard.yaml +++ b/tests/components/color_temperature/test.esp32-c3-ard.yaml @@ -1,15 +1,6 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 +substitutions: + light_platform: ledc + pin_o1: GPIO6 + pin_o2: GPIO7 -light: - - platform: color_temperature - name: Lights - color_temperature: light_output_1 - brightness: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/color_temperature/test.esp32-c3-idf.yaml b/tests/components/color_temperature/test.esp32-c3-idf.yaml index 8d3faa54ee..016f315d9f 100644 --- a/tests/components/color_temperature/test.esp32-c3-idf.yaml +++ b/tests/components/color_temperature/test.esp32-c3-idf.yaml @@ -1,15 +1,6 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 +substitutions: + light_platform: ledc + pin_o1: GPIO6 + pin_o2: GPIO7 -light: - - platform: color_temperature - name: Lights - color_temperature: light_output_1 - brightness: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/color_temperature/test.esp32-idf.yaml b/tests/components/color_temperature/test.esp32-idf.yaml index 608907d2fc..1831adda6e 100644 --- a/tests/components/color_temperature/test.esp32-idf.yaml +++ b/tests/components/color_temperature/test.esp32-idf.yaml @@ -1,15 +1,6 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 +substitutions: + light_platform: ledc + pin_o1: GPIO16 + pin_o2: GPIO17 -light: - - platform: color_temperature - name: Lights - color_temperature: light_output_1 - brightness: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/color_temperature/test.esp8266-ard.yaml b/tests/components/color_temperature/test.esp8266-ard.yaml index ed0bfb6aa4..75a5b9d64d 100644 --- a/tests/components/color_temperature/test.esp8266-ard.yaml +++ b/tests/components/color_temperature/test.esp8266-ard.yaml @@ -1,15 +1,6 @@ -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 12 - - platform: esp8266_pwm - id: light_output_2 - pin: 13 +substitutions: + light_platform: esp8266_pwm + pin_o1: GPIO12 + pin_o2: GPIO13 -light: - - platform: color_temperature - name: Lights - color_temperature: light_output_1 - brightness: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/color_temperature/test.rp2040-ard.yaml b/tests/components/color_temperature/test.rp2040-ard.yaml index 887ad1c857..537177aca1 100644 --- a/tests/components/color_temperature/test.rp2040-ard.yaml +++ b/tests/components/color_temperature/test.rp2040-ard.yaml @@ -1,15 +1,6 @@ -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 12 - - platform: rp2040_pwm - id: light_output_2 - pin: 13 +substitutions: + light_platform: rp2040_pwm + pin_o1: GPIO12 + pin_o2: GPIO13 -light: - - platform: color_temperature - name: Lights - color_temperature: light_output_1 - brightness: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds +<<: !include common.yaml diff --git a/tests/components/coolix/common.yaml b/tests/components/coolix/common.yaml new file mode 100644 index 0000000000..abe609c3ea --- /dev/null +++ b/tests/components/coolix/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: coolix + name: Coolix Climate diff --git a/tests/components/coolix/test.esp32-ard.yaml b/tests/components/coolix/test.esp32-ard.yaml index 0f9518d2cf..7b012aa64c 100644 --- a/tests/components/coolix/test.esp32-ard.yaml +++ b/tests/components/coolix/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: coolix - name: Coolix Climate +<<: !include common.yaml diff --git a/tests/components/coolix/test.esp32-c3-ard.yaml b/tests/components/coolix/test.esp32-c3-ard.yaml index 0f9518d2cf..7b012aa64c 100644 --- a/tests/components/coolix/test.esp32-c3-ard.yaml +++ b/tests/components/coolix/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: coolix - name: Coolix Climate +<<: !include common.yaml diff --git a/tests/components/coolix/test.esp32-c3-idf.yaml b/tests/components/coolix/test.esp32-c3-idf.yaml index 0f9518d2cf..7b012aa64c 100644 --- a/tests/components/coolix/test.esp32-c3-idf.yaml +++ b/tests/components/coolix/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: coolix - name: Coolix Climate +<<: !include common.yaml diff --git a/tests/components/coolix/test.esp32-idf.yaml b/tests/components/coolix/test.esp32-idf.yaml index 0f9518d2cf..7b012aa64c 100644 --- a/tests/components/coolix/test.esp32-idf.yaml +++ b/tests/components/coolix/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: coolix - name: Coolix Climate +<<: !include common.yaml diff --git a/tests/components/coolix/test.esp8266-ard.yaml b/tests/components/coolix/test.esp8266-ard.yaml index 61de8c7558..f5097fcf5f 100644 --- a/tests/components/coolix/test.esp8266-ard.yaml +++ b/tests/components/coolix/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: coolix - name: Coolix Climate +<<: !include common.yaml diff --git a/tests/components/copy/common.yaml b/tests/components/copy/common.yaml new file mode 100644 index 0000000000..a73b3467e6 --- /dev/null +++ b/tests/components/copy/common.yaml @@ -0,0 +1,23 @@ +output: + - platform: ${pwm_platform} + id: fan_output_1 + pin: ${pin} + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 + - platform: copy + source_id: fan_speed + name: Fan Speed Copy + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + - platform: copy + source_id: test_select + name: Test Select Copy diff --git a/tests/components/copy/test.esp32-ard.yaml b/tests/components/copy/test.esp32-ard.yaml index 806dbfe9f3..e5337726dc 100644 --- a/tests/components/copy/test.esp32-ard.yaml +++ b/tests/components/copy/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 12 +substitutions: + pwm_platform: ledc + pin: GPIO12 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 - - platform: copy - source_id: fan_speed - name: Fan Speed Copy - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - - platform: copy - source_id: test_select - name: Test Select Copy +<<: !include common.yaml diff --git a/tests/components/copy/test.esp32-c3-ard.yaml b/tests/components/copy/test.esp32-c3-ard.yaml index 554638f462..76272beb77 100644 --- a/tests/components/copy/test.esp32-c3-ard.yaml +++ b/tests/components/copy/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 2 +substitutions: + pwm_platform: ledc + pin: GPIO2 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 - - platform: copy - source_id: fan_speed - name: Fan Speed Copy - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - - platform: copy - source_id: test_select - name: Test Select Copy +<<: !include common.yaml diff --git a/tests/components/copy/test.esp32-c3-idf.yaml b/tests/components/copy/test.esp32-c3-idf.yaml index 554638f462..76272beb77 100644 --- a/tests/components/copy/test.esp32-c3-idf.yaml +++ b/tests/components/copy/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 2 +substitutions: + pwm_platform: ledc + pin: GPIO2 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 - - platform: copy - source_id: fan_speed - name: Fan Speed Copy - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - - platform: copy - source_id: test_select - name: Test Select Copy +<<: !include common.yaml diff --git a/tests/components/copy/test.esp32-idf.yaml b/tests/components/copy/test.esp32-idf.yaml index 806dbfe9f3..e5337726dc 100644 --- a/tests/components/copy/test.esp32-idf.yaml +++ b/tests/components/copy/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 12 +substitutions: + pwm_platform: ledc + pin: GPIO12 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 - - platform: copy - source_id: fan_speed - name: Fan Speed Copy - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - - platform: copy - source_id: test_select - name: Test Select Copy +<<: !include common.yaml diff --git a/tests/components/copy/test.esp8266-ard.yaml b/tests/components/copy/test.esp8266-ard.yaml index 1521e5f279..497fac5161 100644 --- a/tests/components/copy/test.esp8266-ard.yaml +++ b/tests/components/copy/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -output: - - platform: esp8266_pwm - id: fan_output_1 - pin: 12 +substitutions: + pwm_platform: esp8266_pwm + pin: GPIO12 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 - - platform: copy - source_id: fan_speed - name: Fan Speed Copy - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - - platform: copy - source_id: test_select - name: Test Select Copy +<<: !include common.yaml diff --git a/tests/components/copy/test.rp2040-ard.yaml b/tests/components/copy/test.rp2040-ard.yaml index 42e5eb8000..61e461d247 100644 --- a/tests/components/copy/test.rp2040-ard.yaml +++ b/tests/components/copy/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -output: - - platform: rp2040_pwm - id: fan_output_1 - pin: 12 +substitutions: + pwm_platform: rp2040_pwm + pin: GPIO12 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 - - platform: copy - source_id: fan_speed - name: Fan Speed Copy - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - - platform: copy - source_id: test_select - name: Test Select Copy +<<: !include common.yaml diff --git a/tests/components/cs5460a/common.yaml b/tests/components/cs5460a/common.yaml new file mode 100644 index 0000000000..d97b01716b --- /dev/null +++ b/tests/components/cs5460a/common.yaml @@ -0,0 +1,27 @@ +spi: + - id: spi_cs5460a + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: cs5460a + id: cs5460a1 + cs_pin: ${cs_pin} + current: + name: Socket current + voltage: + name: Mains voltage + power: + name: Socket power + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: true + voltage_hpf: true + phase_offset: 20 + pulse_energy: 0.01 kWh diff --git a/tests/components/cs5460a/test.esp32-ard.yaml b/tests/components/cs5460a/test.esp32-ard.yaml index e7eb1cbd73..54e027a614 100644 --- a/tests/components/cs5460a/test.esp32-ard.yaml +++ b/tests/components/cs5460a/test.esp32-ard.yaml @@ -1,27 +1,7 @@ -spi: - - id: spi_cs5460a - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: cs5460a - id: cs5460a1 - cs_pin: 12 - current: - name: Socket current - voltage: - name: Mains voltage - power: - name: Socket power - on_value: - then: - cs5460a.restart: cs5460a1 - samples: 1600 - pga_gain: 10X - current_gain: 0.01 - voltage_gain: 0.000573 - current_hpf: true - voltage_hpf: true - phase_offset: 20 - pulse_energy: 0.01 kWh +<<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp32-c3-ard.yaml b/tests/components/cs5460a/test.esp32-c3-ard.yaml index 4ce21783a3..2415ba5dc6 100644 --- a/tests/components/cs5460a/test.esp32-c3-ard.yaml +++ b/tests/components/cs5460a/test.esp32-c3-ard.yaml @@ -1,27 +1,7 @@ -spi: - - id: spi_cs5460a - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: cs5460a - id: cs5460a1 - cs_pin: 8 - current: - name: Socket current - voltage: - name: Mains voltage - power: - name: Socket power - on_value: - then: - cs5460a.restart: cs5460a1 - samples: 1600 - pga_gain: 10X - current_gain: 0.01 - voltage_gain: 0.000573 - current_hpf: true - voltage_hpf: true - phase_offset: 20 - pulse_energy: 0.01 kWh +<<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp32-c3-idf.yaml b/tests/components/cs5460a/test.esp32-c3-idf.yaml index 4ce21783a3..2415ba5dc6 100644 --- a/tests/components/cs5460a/test.esp32-c3-idf.yaml +++ b/tests/components/cs5460a/test.esp32-c3-idf.yaml @@ -1,27 +1,7 @@ -spi: - - id: spi_cs5460a - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: cs5460a - id: cs5460a1 - cs_pin: 8 - current: - name: Socket current - voltage: - name: Mains voltage - power: - name: Socket power - on_value: - then: - cs5460a.restart: cs5460a1 - samples: 1600 - pga_gain: 10X - current_gain: 0.01 - voltage_gain: 0.000573 - current_hpf: true - voltage_hpf: true - phase_offset: 20 - pulse_energy: 0.01 kWh +<<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp32-idf.yaml b/tests/components/cs5460a/test.esp32-idf.yaml index e7eb1cbd73..54e027a614 100644 --- a/tests/components/cs5460a/test.esp32-idf.yaml +++ b/tests/components/cs5460a/test.esp32-idf.yaml @@ -1,27 +1,7 @@ -spi: - - id: spi_cs5460a - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: cs5460a - id: cs5460a1 - cs_pin: 12 - current: - name: Socket current - voltage: - name: Mains voltage - power: - name: Socket power - on_value: - then: - cs5460a.restart: cs5460a1 - samples: 1600 - pga_gain: 10X - current_gain: 0.01 - voltage_gain: 0.000573 - current_hpf: true - voltage_hpf: true - phase_offset: 20 - pulse_energy: 0.01 kWh +<<: !include common.yaml diff --git a/tests/components/cs5460a/test.esp8266-ard.yaml b/tests/components/cs5460a/test.esp8266-ard.yaml index c5a458d0ec..dbd158d030 100644 --- a/tests/components/cs5460a/test.esp8266-ard.yaml +++ b/tests/components/cs5460a/test.esp8266-ard.yaml @@ -1,27 +1,7 @@ -spi: - - id: spi_cs5460a - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -sensor: - - platform: cs5460a - id: cs5460a1 - cs_pin: 15 - current: - name: Socket current - voltage: - name: Mains voltage - power: - name: Socket power - on_value: - then: - cs5460a.restart: cs5460a1 - samples: 1600 - pga_gain: 10X - current_gain: 0.01 - voltage_gain: 0.000573 - current_hpf: true - voltage_hpf: true - phase_offset: 20 - pulse_energy: 0.01 kWh +<<: !include common.yaml diff --git a/tests/components/cs5460a/test.rp2040-ard.yaml b/tests/components/cs5460a/test.rp2040-ard.yaml index f3daf7d72d..f6c3f1eeca 100644 --- a/tests/components/cs5460a/test.rp2040-ard.yaml +++ b/tests/components/cs5460a/test.rp2040-ard.yaml @@ -1,27 +1,7 @@ -spi: - - id: spi_cs5460a - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -sensor: - - platform: cs5460a - id: cs5460a1 - cs_pin: 6 - current: - name: Socket current - voltage: - name: Mains voltage - power: - name: Socket power - on_value: - then: - cs5460a.restart: cs5460a1 - samples: 1600 - pga_gain: 10X - current_gain: 0.01 - voltage_gain: 0.000573 - current_hpf: true - voltage_hpf: true - phase_offset: 20 - pulse_energy: 0.01 kWh +<<: !include common.yaml diff --git a/tests/components/cse7761/common.yaml b/tests/components/cse7761/common.yaml new file mode 100644 index 0000000000..60cce3864a --- /dev/null +++ b/tests/components/cse7761/common.yaml @@ -0,0 +1,18 @@ +uart: + - id: uart_cse7761 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 38400 + +sensor: + - platform: cse7761 + voltage: + name: CSE7761 Voltage + current_1: + name: CSE7761 Current 1 + current_2: + name: CSE7761 Current 2 + active_power_1: + name: CSE7761 Active Power 1 + active_power_2: + name: CSE7761 Active Power 2 diff --git a/tests/components/cse7761/test.esp32-ard.yaml b/tests/components/cse7761/test.esp32-ard.yaml index 4174e9a92e..811f6b72a6 100644 --- a/tests/components/cse7761/test.esp32-ard.yaml +++ b/tests/components/cse7761/test.esp32-ard.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_cse7761 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 38400 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: cse7761 - voltage: - name: CSE7761 Voltage - current_1: - name: CSE7761 Current 1 - current_2: - name: CSE7761 Current 2 - active_power_1: - name: CSE7761 Active Power 1 - active_power_2: - name: CSE7761 Active Power 2 +<<: !include common.yaml diff --git a/tests/components/cse7761/test.esp32-c3-ard.yaml b/tests/components/cse7761/test.esp32-c3-ard.yaml index 581db24fd5..c79d14c740 100644 --- a/tests/components/cse7761/test.esp32-c3-ard.yaml +++ b/tests/components/cse7761/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_cse7761 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 38400 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: cse7761 - voltage: - name: CSE7761 Voltage - current_1: - name: CSE7761 Current 1 - current_2: - name: CSE7761 Current 2 - active_power_1: - name: CSE7761 Active Power 1 - active_power_2: - name: CSE7761 Active Power 2 +<<: !include common.yaml diff --git a/tests/components/cse7761/test.esp32-c3-idf.yaml b/tests/components/cse7761/test.esp32-c3-idf.yaml index 581db24fd5..c79d14c740 100644 --- a/tests/components/cse7761/test.esp32-c3-idf.yaml +++ b/tests/components/cse7761/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_cse7761 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 38400 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: cse7761 - voltage: - name: CSE7761 Voltage - current_1: - name: CSE7761 Current 1 - current_2: - name: CSE7761 Current 2 - active_power_1: - name: CSE7761 Active Power 1 - active_power_2: - name: CSE7761 Active Power 2 +<<: !include common.yaml diff --git a/tests/components/cse7761/test.esp32-idf.yaml b/tests/components/cse7761/test.esp32-idf.yaml index 4174e9a92e..811f6b72a6 100644 --- a/tests/components/cse7761/test.esp32-idf.yaml +++ b/tests/components/cse7761/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_cse7761 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 38400 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: cse7761 - voltage: - name: CSE7761 Voltage - current_1: - name: CSE7761 Current 1 - current_2: - name: CSE7761 Current 2 - active_power_1: - name: CSE7761 Active Power 1 - active_power_2: - name: CSE7761 Active Power 2 +<<: !include common.yaml diff --git a/tests/components/cse7761/test.esp8266-ard.yaml b/tests/components/cse7761/test.esp8266-ard.yaml index 581db24fd5..3b44f9c9c3 100644 --- a/tests/components/cse7761/test.esp8266-ard.yaml +++ b/tests/components/cse7761/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_cse7761 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 38400 +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -sensor: - - platform: cse7761 - voltage: - name: CSE7761 Voltage - current_1: - name: CSE7761 Current 1 - current_2: - name: CSE7761 Current 2 - active_power_1: - name: CSE7761 Active Power 1 - active_power_2: - name: CSE7761 Active Power 2 +<<: !include common.yaml diff --git a/tests/components/cse7761/test.rp2040-ard.yaml b/tests/components/cse7761/test.rp2040-ard.yaml index 581db24fd5..b516342f3b 100644 --- a/tests/components/cse7761/test.rp2040-ard.yaml +++ b/tests/components/cse7761/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -uart: - - id: uart_cse7761 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 38400 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: cse7761 - voltage: - name: CSE7761 Voltage - current_1: - name: CSE7761 Current 1 - current_2: - name: CSE7761 Current 2 - active_power_1: - name: CSE7761 Active Power 1 - active_power_2: - name: CSE7761 Active Power 2 +<<: !include common.yaml diff --git a/tests/components/cse7766/common.yaml b/tests/components/cse7766/common.yaml new file mode 100644 index 0000000000..f12b135a77 --- /dev/null +++ b/tests/components/cse7766/common.yaml @@ -0,0 +1,15 @@ +uart: + - id: uart_cse7766 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + parity: EVEN + +sensor: + - platform: cse7766 + voltage: + name: CSE7766 Voltage + current: + name: CSE7766 Current + power: + name: CSE776 Power diff --git a/tests/components/cse7766/test.esp32-ard.yaml b/tests/components/cse7766/test.esp32-ard.yaml index 5542b52824..811f6b72a6 100644 --- a/tests/components/cse7766/test.esp32-ard.yaml +++ b/tests/components/cse7766/test.esp32-ard.yaml @@ -1,17 +1,5 @@ -uart: - - id: uart_cse7766 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 4800 - parity: EVEN +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: cse7766 - voltage: - name: CSE7766 Voltage - current: - name: CSE7766 Current - power: - name: CSE776 Power +<<: !include common.yaml diff --git a/tests/components/cse7766/test.esp32-c3-ard.yaml b/tests/components/cse7766/test.esp32-c3-ard.yaml index d27c9d4463..c79d14c740 100644 --- a/tests/components/cse7766/test.esp32-c3-ard.yaml +++ b/tests/components/cse7766/test.esp32-c3-ard.yaml @@ -1,17 +1,5 @@ -uart: - - id: uart_cse7766 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 - parity: EVEN +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: cse7766 - voltage: - name: CSE7766 Voltage - current: - name: CSE7766 Current - power: - name: CSE776 Power +<<: !include common.yaml diff --git a/tests/components/cse7766/test.esp32-c3-idf.yaml b/tests/components/cse7766/test.esp32-c3-idf.yaml index d27c9d4463..c79d14c740 100644 --- a/tests/components/cse7766/test.esp32-c3-idf.yaml +++ b/tests/components/cse7766/test.esp32-c3-idf.yaml @@ -1,17 +1,5 @@ -uart: - - id: uart_cse7766 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 - parity: EVEN +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -sensor: - - platform: cse7766 - voltage: - name: CSE7766 Voltage - current: - name: CSE7766 Current - power: - name: CSE776 Power +<<: !include common.yaml diff --git a/tests/components/cse7766/test.esp32-idf.yaml b/tests/components/cse7766/test.esp32-idf.yaml index 5542b52824..811f6b72a6 100644 --- a/tests/components/cse7766/test.esp32-idf.yaml +++ b/tests/components/cse7766/test.esp32-idf.yaml @@ -1,17 +1,5 @@ -uart: - - id: uart_cse7766 - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 4800 - parity: EVEN +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: cse7766 - voltage: - name: CSE7766 Voltage - current: - name: CSE7766 Current - power: - name: CSE776 Power +<<: !include common.yaml diff --git a/tests/components/cse7766/test.esp8266-ard.yaml b/tests/components/cse7766/test.esp8266-ard.yaml index d27c9d4463..3b44f9c9c3 100644 --- a/tests/components/cse7766/test.esp8266-ard.yaml +++ b/tests/components/cse7766/test.esp8266-ard.yaml @@ -1,17 +1,5 @@ -uart: - - id: uart_cse7766 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 - parity: EVEN +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -sensor: - - platform: cse7766 - voltage: - name: CSE7766 Voltage - current: - name: CSE7766 Current - power: - name: CSE776 Power +<<: !include common.yaml diff --git a/tests/components/cse7766/test.rp2040-ard.yaml b/tests/components/cse7766/test.rp2040-ard.yaml index d27c9d4463..b516342f3b 100644 --- a/tests/components/cse7766/test.rp2040-ard.yaml +++ b/tests/components/cse7766/test.rp2040-ard.yaml @@ -1,17 +1,5 @@ -uart: - - id: uart_cse7766 - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: cse7766 - voltage: - name: CSE7766 Voltage - current: - name: CSE7766 Current - power: - name: CSE776 Power +<<: !include common.yaml diff --git a/tests/components/cst226/common.yaml b/tests/components/cst226/common.yaml index 7e1c5dde36..c12d8d872c 100644 --- a/tests/components/cst226/common.yaml +++ b/tests/components/cst226/common.yaml @@ -1,25 +1,25 @@ +i2c: + - id: i2c_cst226 + scl: ${scl_pin} + sda: ${sda_pin} + spi: - - id: spi_id_1 - clk_pin: GPIO7 - mosi_pin: GPIO6 - interface: any + - id: spi_ili9xxx + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} display: - - platform: ili9xxx - id: displ8 + - id: my_display + platform: ili9xxx model: ili9342 - cs_pin: GPIO5 - dc_pin: GPIO4 - reset_pin: - number: GPIO21 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${disp_reset_pin} invert_colors: false -i2c: - scl: GPIO18 - sda: GPIO8 - touchscreen: - - platform: cst226 - interrupt_pin: GPIO3 - reset_pin: GPIO20 + - id: ts_cst226 + platform: cst226 + interrupt_pin: ${interrupt_pin} + reset_pin: ${reset_pin} diff --git a/tests/components/cst226/test.esp32-ard.yaml b/tests/components/cst226/test.esp32-ard.yaml new file mode 100644 index 0000000000..11e2c4fd43 --- /dev/null +++ b/tests/components/cst226/test.esp32-ard.yaml @@ -0,0 +1,12 @@ +substitutions: + clk_pin: GPIO0 + mosi_pin: GPIO2 + cs_pin: GPIO4 + dc_pin: GPIO5 + disp_reset_pin: GPIO12 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 + +<<: !include common.yaml diff --git a/tests/components/cst226/test.esp32-c3-ard.yaml b/tests/components/cst226/test.esp32-c3-ard.yaml index dade44d145..2f9bd72882 100644 --- a/tests/components/cst226/test.esp32-c3-ard.yaml +++ b/tests/components/cst226/test.esp32-c3-ard.yaml @@ -1 +1,12 @@ +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + cs_pin: GPIO8 + dc_pin: GPIO9 + disp_reset_pin: GPIO10 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 + <<: !include common.yaml diff --git a/tests/components/cst226/test.esp32-c3-idf.yaml b/tests/components/cst226/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..2f9bd72882 --- /dev/null +++ b/tests/components/cst226/test.esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + cs_pin: GPIO8 + dc_pin: GPIO9 + disp_reset_pin: GPIO10 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/cst226/test.esp32-idf.yaml b/tests/components/cst226/test.esp32-idf.yaml new file mode 100644 index 0000000000..11e2c4fd43 --- /dev/null +++ b/tests/components/cst226/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +substitutions: + clk_pin: GPIO0 + mosi_pin: GPIO2 + cs_pin: GPIO4 + dc_pin: GPIO5 + disp_reset_pin: GPIO12 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 + +<<: !include common.yaml diff --git a/tests/components/cst816/common.yaml b/tests/components/cst816/common.yaml index 765a353d1d..a4ac4aafec 100644 --- a/tests/components/cst816/common.yaml +++ b/tests/components/cst816/common.yaml @@ -1,27 +1,21 @@ -touchscreen: - - platform: cst816 - id: my_touchscreen - interrupt_pin: - number: 21 - reset_pin: GPIO16 - skip_probe: false - transform: - mirror_x: false - mirror_y: false - swap_xy: false - i2c: - sda: 3 - scl: 4 + - id: i2c_cst816 + scl: ${scl_pin} + sda: ${sda_pin} + +spi: + - id: spi_ili9xxx + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} display: - id: my_display platform: ili9xxx dimensions: 480x320 model: ST7796 - cs_pin: 18 - dc_pin: 20 - reset_pin: 22 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${disp_reset_pin} transform: swap_xy: true mirror_x: true @@ -29,9 +23,16 @@ display: auto_clear_enabled: false invert_colors: false -spi: - clk_pin: 14 - mosi_pin: 13 +touchscreen: + - id: ts_cst816 + platform: cst816 + interrupt_pin: ${interrupt_pin} + reset_pin: ${reset_pin} + skip_probe: false + transform: + mirror_x: false + mirror_y: false + swap_xy: false binary_sensor: - platform: cst816 diff --git a/tests/components/cst816/test.esp32-ard.yaml b/tests/components/cst816/test.esp32-ard.yaml index dade44d145..11e2c4fd43 100644 --- a/tests/components/cst816/test.esp32-ard.yaml +++ b/tests/components/cst816/test.esp32-ard.yaml @@ -1 +1,12 @@ +substitutions: + clk_pin: GPIO0 + mosi_pin: GPIO2 + cs_pin: GPIO4 + dc_pin: GPIO5 + disp_reset_pin: GPIO12 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 + <<: !include common.yaml diff --git a/tests/components/cst816/test.esp32-c3-ard.yaml b/tests/components/cst816/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..2f9bd72882 --- /dev/null +++ b/tests/components/cst816/test.esp32-c3-ard.yaml @@ -0,0 +1,12 @@ +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + cs_pin: GPIO8 + dc_pin: GPIO9 + disp_reset_pin: GPIO10 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/cst816/test.esp32-c3-idf.yaml b/tests/components/cst816/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..2f9bd72882 --- /dev/null +++ b/tests/components/cst816/test.esp32-c3-idf.yaml @@ -0,0 +1,12 @@ +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + cs_pin: GPIO8 + dc_pin: GPIO9 + disp_reset_pin: GPIO10 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/cst816/test.esp32-idf.yaml b/tests/components/cst816/test.esp32-idf.yaml new file mode 100644 index 0000000000..11e2c4fd43 --- /dev/null +++ b/tests/components/cst816/test.esp32-idf.yaml @@ -0,0 +1,12 @@ +substitutions: + clk_pin: GPIO0 + mosi_pin: GPIO2 + cs_pin: GPIO4 + dc_pin: GPIO5 + disp_reset_pin: GPIO12 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 + +<<: !include common.yaml diff --git a/tests/components/ct_clamp/common.yaml b/tests/components/ct_clamp/common.yaml new file mode 100644 index 0000000000..3ed9678447 --- /dev/null +++ b/tests/components/ct_clamp/common.yaml @@ -0,0 +1,9 @@ +sensor: + - platform: adc + id: esp_adc_sensor + pin: ${pin} + - platform: ct_clamp + sensor: esp_adc_sensor + name: CT Clamp + sample_duration: 500ms + update_interval: 5s diff --git a/tests/components/ct_clamp/test.esp32-ard.yaml b/tests/components/ct_clamp/test.esp32-ard.yaml index 1ea964fa96..0a70e3f733 100644 --- a/tests/components/ct_clamp/test.esp32-ard.yaml +++ b/tests/components/ct_clamp/test.esp32-ard.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: esp_adc_sensor - pin: 39 - - platform: ct_clamp - sensor: esp_adc_sensor - name: CT Clamp - sample_duration: 500ms - update_interval: 5s +substitutions: + pin: GPIO39 + +<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.esp32-c3-ard.yaml b/tests/components/ct_clamp/test.esp32-c3-ard.yaml index e25acc95e1..a8f29c98ae 100644 --- a/tests/components/ct_clamp/test.esp32-c3-ard.yaml +++ b/tests/components/ct_clamp/test.esp32-c3-ard.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: esp_adc_sensor - pin: 0 - - platform: ct_clamp - sensor: esp_adc_sensor - name: CT Clamp - sample_duration: 500ms - update_interval: 5s +substitutions: + pin: GPIO0 + +<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.esp32-c3-idf.yaml b/tests/components/ct_clamp/test.esp32-c3-idf.yaml index e25acc95e1..a8f29c98ae 100644 --- a/tests/components/ct_clamp/test.esp32-c3-idf.yaml +++ b/tests/components/ct_clamp/test.esp32-c3-idf.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: esp_adc_sensor - pin: 0 - - platform: ct_clamp - sensor: esp_adc_sensor - name: CT Clamp - sample_duration: 500ms - update_interval: 5s +substitutions: + pin: GPIO0 + +<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.esp32-idf.yaml b/tests/components/ct_clamp/test.esp32-idf.yaml index 1ea964fa96..0a70e3f733 100644 --- a/tests/components/ct_clamp/test.esp32-idf.yaml +++ b/tests/components/ct_clamp/test.esp32-idf.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: esp_adc_sensor - pin: 39 - - platform: ct_clamp - sensor: esp_adc_sensor - name: CT Clamp - sample_duration: 500ms - update_interval: 5s +substitutions: + pin: GPIO39 + +<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.esp8266-ard.yaml b/tests/components/ct_clamp/test.esp8266-ard.yaml index 9c7126480d..4a6d36cbb4 100644 --- a/tests/components/ct_clamp/test.esp8266-ard.yaml +++ b/tests/components/ct_clamp/test.esp8266-ard.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: esp_adc_sensor - pin: A0 - - platform: ct_clamp - sensor: esp_adc_sensor - name: CT Clamp - sample_duration: 500ms - update_interval: 5s +substitutions: + pin: A0 + +<<: !include common.yaml diff --git a/tests/components/ct_clamp/test.rp2040-ard.yaml b/tests/components/ct_clamp/test.rp2040-ard.yaml index 47077308aa..9479437434 100644 --- a/tests/components/ct_clamp/test.rp2040-ard.yaml +++ b/tests/components/ct_clamp/test.rp2040-ard.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: esp_adc_sensor - pin: 26 - - platform: ct_clamp - sensor: esp_adc_sensor - name: CT Clamp - sample_duration: 500ms - update_interval: 5s +substitutions: + pin: GPIO26 + +<<: !include common.yaml diff --git a/tests/components/current_based/common.yaml b/tests/components/current_based/common.yaml new file mode 100644 index 0000000000..25dc9671b7 --- /dev/null +++ b/tests/components/current_based/common.yaml @@ -0,0 +1,68 @@ +i2c: + - id: i2c_ade7953 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ade7953_i2c + irq_pin: ${irq_pin} + voltage: + name: ADE7953 Voltage + id: ade7953_voltage + current_a: + name: ADE7953 Current A + id: ade7953_current_a + current_b: + name: ADE7953 Current B + id: ade7953_current_b + power_factor_a: + name: ADE7953 Power Factor A + power_factor_b: + name: ADE7953 Power Factor B + apparent_power_a: + name: ADE7953 Apparent Power A + apparent_power_b: + name: ADE7953 Apparent Power B + active_power_a: + name: ADE7953 Active Power A + active_power_b: + name: ADE7953 Active Power B + reactive_power_a: + name: ADE7953 Reactive Power A + reactive_power_b: + name: ADE7953 Reactive Power B + update_interval: 1s + +switch: + - platform: template + id: template_switch1 + optimistic: true + - platform: template + id: template_switch2 + optimistic: true + +cover: + - platform: current_based + name: Current Based Cover + id: current_based_cover + open_sensor: ade7953_current_a + open_moving_current_threshold: 0.5 + open_obstacle_current_threshold: 0.8 + open_duration: 12s + open_action: + - switch.turn_on: template_switch1 + close_sensor: ade7953_current_b + close_moving_current_threshold: 0.5 + close_obstacle_current_threshold: 0.8 + close_duration: 10s + close_action: + - switch.turn_on: template_switch2 + stop_action: + - switch.turn_off: template_switch1 + - switch.turn_off: template_switch2 + obstacle_rollback: 30% + start_sensing_delay: 0.8s + malfunction_detection: true + malfunction_action: + then: + - logger.log: Malfunction Detected diff --git a/tests/components/current_based/test.esp32-ard.yaml b/tests/components/current_based/test.esp32-ard.yaml index 90781120bc..2c57d412f6 100644 --- a/tests/components/current_based/test.esp32-ard.yaml +++ b/tests/components/current_based/test.esp32-ard.yaml @@ -1,68 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO15 -sensor: - - platform: ade7953_i2c - irq_pin: 15 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s - -switch: - - platform: template - id: template_switch1 - optimistic: true - - platform: template - id: template_switch2 - optimistic: true - -cover: - - platform: current_based - name: Current Based Cover - id: current_based_cover - open_sensor: ade7953_current_a - open_moving_current_threshold: 0.5 - open_obstacle_current_threshold: 0.8 - open_duration: 12s - open_action: - - switch.turn_on: template_switch1 - close_sensor: ade7953_current_b - close_moving_current_threshold: 0.5 - close_obstacle_current_threshold: 0.8 - close_duration: 10s - close_action: - - switch.turn_on: template_switch2 - stop_action: - - switch.turn_off: template_switch1 - - switch.turn_off: template_switch2 - obstacle_rollback: 30% - start_sensing_delay: 0.8s - malfunction_detection: true - malfunction_action: - then: - - logger.log: Malfunction Detected +<<: !include common.yaml diff --git a/tests/components/current_based/test.esp32-c3-ard.yaml b/tests/components/current_based/test.esp32-c3-ard.yaml index 69ab8830c3..799acabd5a 100644 --- a/tests/components/current_based/test.esp32-c3-ard.yaml +++ b/tests/components/current_based/test.esp32-c3-ard.yaml @@ -1,68 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -sensor: - - platform: ade7953_i2c - irq_pin: 6 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s - -switch: - - platform: template - id: template_switch1 - optimistic: true - - platform: template - id: template_switch2 - optimistic: true - -cover: - - platform: current_based - name: Current Based Cover - id: current_based_cover - open_sensor: ade7953_current_a - open_moving_current_threshold: 0.5 - open_obstacle_current_threshold: 0.8 - open_duration: 12s - open_action: - - switch.turn_on: template_switch1 - close_sensor: ade7953_current_b - close_moving_current_threshold: 0.5 - close_obstacle_current_threshold: 0.8 - close_duration: 10s - close_action: - - switch.turn_on: template_switch2 - stop_action: - - switch.turn_off: template_switch1 - - switch.turn_off: template_switch2 - obstacle_rollback: 30% - start_sensing_delay: 0.8s - malfunction_detection: true - malfunction_action: - then: - - logger.log: Malfunction Detected +<<: !include common.yaml diff --git a/tests/components/current_based/test.esp32-c3-idf.yaml b/tests/components/current_based/test.esp32-c3-idf.yaml index 69ab8830c3..799acabd5a 100644 --- a/tests/components/current_based/test.esp32-c3-idf.yaml +++ b/tests/components/current_based/test.esp32-c3-idf.yaml @@ -1,68 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -sensor: - - platform: ade7953_i2c - irq_pin: 6 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s - -switch: - - platform: template - id: template_switch1 - optimistic: true - - platform: template - id: template_switch2 - optimistic: true - -cover: - - platform: current_based - name: Current Based Cover - id: current_based_cover - open_sensor: ade7953_current_a - open_moving_current_threshold: 0.5 - open_obstacle_current_threshold: 0.8 - open_duration: 12s - open_action: - - switch.turn_on: template_switch1 - close_sensor: ade7953_current_b - close_moving_current_threshold: 0.5 - close_obstacle_current_threshold: 0.8 - close_duration: 10s - close_action: - - switch.turn_on: template_switch2 - stop_action: - - switch.turn_off: template_switch1 - - switch.turn_off: template_switch2 - obstacle_rollback: 30% - start_sensing_delay: 0.8s - malfunction_detection: true - malfunction_action: - then: - - logger.log: Malfunction Detected +<<: !include common.yaml diff --git a/tests/components/current_based/test.esp32-idf.yaml b/tests/components/current_based/test.esp32-idf.yaml index 90781120bc..2c57d412f6 100644 --- a/tests/components/current_based/test.esp32-idf.yaml +++ b/tests/components/current_based/test.esp32-idf.yaml @@ -1,68 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO15 -sensor: - - platform: ade7953_i2c - irq_pin: 15 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s - -switch: - - platform: template - id: template_switch1 - optimistic: true - - platform: template - id: template_switch2 - optimistic: true - -cover: - - platform: current_based - name: Current Based Cover - id: current_based_cover - open_sensor: ade7953_current_a - open_moving_current_threshold: 0.5 - open_obstacle_current_threshold: 0.8 - open_duration: 12s - open_action: - - switch.turn_on: template_switch1 - close_sensor: ade7953_current_b - close_moving_current_threshold: 0.5 - close_obstacle_current_threshold: 0.8 - close_duration: 10s - close_action: - - switch.turn_on: template_switch2 - stop_action: - - switch.turn_off: template_switch1 - - switch.turn_off: template_switch2 - obstacle_rollback: 30% - start_sensing_delay: 0.8s - malfunction_detection: true - malfunction_action: - then: - - logger.log: Malfunction Detected +<<: !include common.yaml diff --git a/tests/components/current_based/test.esp8266-ard.yaml b/tests/components/current_based/test.esp8266-ard.yaml index 42d6d4676b..c8e6a43f44 100644 --- a/tests/components/current_based/test.esp8266-ard.yaml +++ b/tests/components/current_based/test.esp8266-ard.yaml @@ -1,68 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO15 -sensor: - - platform: ade7953_i2c - irq_pin: 15 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s - -switch: - - platform: template - id: template_switch1 - optimistic: true - - platform: template - id: template_switch2 - optimistic: true - -cover: - - platform: current_based - name: Current Based Cover - id: current_based_cover - open_sensor: ade7953_current_a - open_moving_current_threshold: 0.5 - open_obstacle_current_threshold: 0.8 - open_duration: 12s - open_action: - - switch.turn_on: template_switch1 - close_sensor: ade7953_current_b - close_moving_current_threshold: 0.5 - close_obstacle_current_threshold: 0.8 - close_duration: 10s - close_action: - - switch.turn_on: template_switch2 - stop_action: - - switch.turn_off: template_switch1 - - switch.turn_off: template_switch2 - obstacle_rollback: 30% - start_sensing_delay: 0.8s - malfunction_detection: true - malfunction_action: - then: - - logger.log: Malfunction Detected +<<: !include common.yaml diff --git a/tests/components/current_based/test.rp2040-ard.yaml b/tests/components/current_based/test.rp2040-ard.yaml index 69ab8830c3..799acabd5a 100644 --- a/tests/components/current_based/test.rp2040-ard.yaml +++ b/tests/components/current_based/test.rp2040-ard.yaml @@ -1,68 +1,6 @@ -i2c: - - id: i2c_ade7953 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 -sensor: - - platform: ade7953_i2c - irq_pin: 6 - voltage: - name: ADE7953 Voltage - id: ade7953_voltage - current_a: - name: ADE7953 Current A - id: ade7953_current_a - current_b: - name: ADE7953 Current B - id: ade7953_current_b - power_factor_a: - name: ADE7953 Power Factor A - power_factor_b: - name: ADE7953 Power Factor B - apparent_power_a: - name: ADE7953 Apparent Power A - apparent_power_b: - name: ADE7953 Apparent Power B - active_power_a: - name: ADE7953 Active Power A - active_power_b: - name: ADE7953 Active Power B - reactive_power_a: - name: ADE7953 Reactive Power A - reactive_power_b: - name: ADE7953 Reactive Power B - update_interval: 1s - -switch: - - platform: template - id: template_switch1 - optimistic: true - - platform: template - id: template_switch2 - optimistic: true - -cover: - - platform: current_based - name: Current Based Cover - id: current_based_cover - open_sensor: ade7953_current_a - open_moving_current_threshold: 0.5 - open_obstacle_current_threshold: 0.8 - open_duration: 12s - open_action: - - switch.turn_on: template_switch1 - close_sensor: ade7953_current_b - close_moving_current_threshold: 0.5 - close_obstacle_current_threshold: 0.8 - close_duration: 10s - close_action: - - switch.turn_on: template_switch2 - stop_action: - - switch.turn_off: template_switch1 - - switch.turn_off: template_switch2 - obstacle_rollback: 30% - start_sensing_delay: 0.8s - malfunction_detection: true - malfunction_action: - then: - - logger.log: Malfunction Detected +<<: !include common.yaml diff --git a/tests/components/cwww/common.yaml b/tests/components/cwww/common.yaml new file mode 100644 index 0000000000..0ad5beeaae --- /dev/null +++ b/tests/components/cwww/common.yaml @@ -0,0 +1,16 @@ +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin_o1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin_o2} + +light: + - platform: cwww + name: CWWW Light + cold_white: light_output_1 + warm_white: light_output_2 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + constant_brightness: true diff --git a/tests/components/cwww/test.esp32-ard.yaml b/tests/components/cwww/test.esp32-ard.yaml index f108d96ad3..1831adda6e 100644 --- a/tests/components/cwww/test.esp32-ard.yaml +++ b/tests/components/cwww/test.esp32-ard.yaml @@ -1,16 +1,6 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 +substitutions: + light_platform: ledc + pin_o1: GPIO16 + pin_o2: GPIO17 -light: - - platform: cwww - name: CWWW Light - cold_white: light_output_1 - warm_white: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true +<<: !include common.yaml diff --git a/tests/components/cwww/test.esp32-c3-ard.yaml b/tests/components/cwww/test.esp32-c3-ard.yaml index c829ca2a2b..016f315d9f 100644 --- a/tests/components/cwww/test.esp32-c3-ard.yaml +++ b/tests/components/cwww/test.esp32-c3-ard.yaml @@ -1,16 +1,6 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 +substitutions: + light_platform: ledc + pin_o1: GPIO6 + pin_o2: GPIO7 -light: - - platform: cwww - name: CWWW Light - cold_white: light_output_1 - warm_white: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true +<<: !include common.yaml diff --git a/tests/components/cwww/test.esp32-c3-idf.yaml b/tests/components/cwww/test.esp32-c3-idf.yaml index 2760a167ee..982394ded6 100644 --- a/tests/components/cwww/test.esp32-c3-idf.yaml +++ b/tests/components/cwww/test.esp32-c3-idf.yaml @@ -1,19 +1,14 @@ +substitutions: + light_platform: ledc + pin_o1: GPIO6 + pin_o2: GPIO7 + +packages: + device_base: !include common.yaml + output: - - platform: ledc - id: light_output_1 - pin: 1 + - id: !extend light_output_1 channel: 0 - - platform: ledc - id: light_output_2 - pin: 2 + - id: !extend light_output_2 channel: 1 phase_angle: 180° - -light: - - platform: cwww - name: CWWW Light - cold_white: light_output_1 - warm_white: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true diff --git a/tests/components/cwww/test.esp32-idf.yaml b/tests/components/cwww/test.esp32-idf.yaml index 27fa160e56..d2a998e6b3 100644 --- a/tests/components/cwww/test.esp32-idf.yaml +++ b/tests/components/cwww/test.esp32-idf.yaml @@ -1,19 +1,14 @@ +substitutions: + light_platform: ledc + pin_o1: GPIO16 + pin_o2: GPIO17 + +packages: + device_base: !include common.yaml + output: - - platform: ledc - id: light_output_1 - pin: 12 + - id: !extend light_output_1 channel: 0 - - platform: ledc - id: light_output_2 - pin: 13 + - id: !extend light_output_2 channel: 1 phase_angle: 180° - -light: - - platform: cwww - name: CWWW Light - cold_white: light_output_1 - warm_white: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true diff --git a/tests/components/cwww/test.esp8266-ard.yaml b/tests/components/cwww/test.esp8266-ard.yaml index 50c311f616..75a5b9d64d 100644 --- a/tests/components/cwww/test.esp8266-ard.yaml +++ b/tests/components/cwww/test.esp8266-ard.yaml @@ -1,16 +1,6 @@ -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 12 - - platform: esp8266_pwm - id: light_output_2 - pin: 13 +substitutions: + light_platform: esp8266_pwm + pin_o1: GPIO12 + pin_o2: GPIO13 -light: - - platform: cwww - name: CWWW Light - cold_white: light_output_1 - warm_white: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true +<<: !include common.yaml diff --git a/tests/components/cwww/test.rp2040-ard.yaml b/tests/components/cwww/test.rp2040-ard.yaml index 505d67f862..537177aca1 100644 --- a/tests/components/cwww/test.rp2040-ard.yaml +++ b/tests/components/cwww/test.rp2040-ard.yaml @@ -1,16 +1,6 @@ -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 12 - - platform: rp2040_pwm - id: light_output_2 - pin: 13 +substitutions: + light_platform: rp2040_pwm + pin_o1: GPIO12 + pin_o2: GPIO13 -light: - - platform: cwww - name: CWWW Light - cold_white: light_output_1 - warm_white: light_output_2 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - constant_brightness: true +<<: !include common.yaml From 693d813c4b5afdc224ac57699944bd366f780f30 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:17 -0600 Subject: [PATCH 0954/1052] [CI] Consolidate some tests (D) (#8189) --- tests/components/dac7678/common.yaml | 43 ++++++++++++++ tests/components/dac7678/test.esp32-ard.yaml | 46 ++------------- .../components/dac7678/test.esp32-c3-ard.yaml | 46 ++------------- .../components/dac7678/test.esp32-c3-idf.yaml | 46 ++------------- tests/components/dac7678/test.esp32-idf.yaml | 46 ++------------- .../components/dac7678/test.esp8266-ard.yaml | 46 ++------------- tests/components/dac7678/test.rp2040-ard.yaml | 46 ++------------- tests/components/daikin/common.yaml | 12 ++++ tests/components/daikin/test.esp32-ard.yaml | 14 +---- tests/components/daikin/test.esp8266-ard.yaml | 14 +---- tests/components/daikin_arc/common.yaml | 19 ++++++ .../components/daikin_arc/test.esp32-ard.yaml | 22 ++----- .../daikin_arc/test.esp8266-ard.yaml | 22 ++----- tests/components/daikin_brc/common.yaml | 7 +++ .../components/daikin_brc/test.esp32-ard.yaml | 9 +-- .../daikin_brc/test.esp32-c3-ard.yaml | 9 +-- .../daikin_brc/test.esp32-c3-idf.yaml | 9 +-- .../components/daikin_brc/test.esp32-idf.yaml | 9 +-- .../daikin_brc/test.esp8266-ard.yaml | 9 +-- tests/components/daly_bms/common.yaml | 53 +++++++++++++++++ tests/components/daly_bms/test.esp32-ard.yaml | 58 ++----------------- .../daly_bms/test.esp32-c3-ard.yaml | 58 ++----------------- .../daly_bms/test.esp32-c3-idf.yaml | 58 ++----------------- tests/components/daly_bms/test.esp32-idf.yaml | 58 ++----------------- .../components/daly_bms/test.esp8266-ard.yaml | 58 ++----------------- .../components/daly_bms/test.rp2040-ard.yaml | 58 ++----------------- tests/components/deep_sleep/common-esp32.yaml | 7 +++ tests/components/deep_sleep/common.yaml | 6 ++ .../components/deep_sleep/test.esp32-ard.yaml | 17 ++---- .../deep_sleep/test.esp32-c3-ard.yaml | 17 ++---- .../deep_sleep/test.esp32-c3-idf.yaml | 17 ++---- .../components/deep_sleep/test.esp32-idf.yaml | 17 ++---- .../deep_sleep/test.esp8266-ard.yaml | 9 +-- tests/components/delonghi/common.yaml | 7 +++ tests/components/delonghi/test.esp32-ard.yaml | 9 +-- .../delonghi/test.esp32-c3-ard.yaml | 9 +-- .../delonghi/test.esp32-c3-idf.yaml | 9 +-- tests/components/delonghi/test.esp32-idf.yaml | 9 +-- .../components/delonghi/test.esp8266-ard.yaml | 9 +-- tests/components/dfplayer/common.yaml | 42 ++++++++++++++ tests/components/dfplayer/test.esp32-ard.yaml | 45 ++------------ .../dfplayer/test.esp32-c3-ard.yaml | 45 ++------------ .../dfplayer/test.esp32-c3-idf.yaml | 45 ++------------ tests/components/dfplayer/test.esp32-idf.yaml | 45 ++------------ .../components/dfplayer/test.esp8266-ard.yaml | 45 ++------------ .../components/dfplayer/test.rp2040-ard.yaml | 45 ++------------ tests/components/dfrobot_sen0395/common.yaml | 28 +++++++++ .../dfrobot_sen0395/test.esp32-ard.yaml | 31 ++-------- .../dfrobot_sen0395/test.esp32-c3-ard.yaml | 31 ++-------- .../dfrobot_sen0395/test.esp32-c3-idf.yaml | 31 ++-------- .../dfrobot_sen0395/test.esp32-idf.yaml | 31 ++-------- .../dfrobot_sen0395/test.esp8266-ard.yaml | 31 ++-------- .../dfrobot_sen0395/test.rp2040-ard.yaml | 31 ++-------- tests/components/dht12/common.yaml | 12 ++++ tests/components/dht12/test.esp32-ard.yaml | 15 ++--- tests/components/dht12/test.esp32-c3-ard.yaml | 15 ++--- tests/components/dht12/test.esp32-c3-idf.yaml | 15 ++--- tests/components/dht12/test.esp32-idf.yaml | 15 ++--- tests/components/dht12/test.esp8266-ard.yaml | 15 ++--- tests/components/dht12/test.rp2040-ard.yaml | 15 ++--- tests/components/dps310/common.yaml | 12 ++++ tests/components/dps310/test.esp32-ard.yaml | 15 ++--- .../components/dps310/test.esp32-c3-ard.yaml | 15 ++--- .../components/dps310/test.esp32-c3-idf.yaml | 15 ++--- tests/components/dps310/test.esp32-idf.yaml | 15 ++--- tests/components/dps310/test.esp8266-ard.yaml | 15 ++--- tests/components/dps310/test.rp2040-ard.yaml | 15 ++--- tests/components/ds1307/common.yaml | 9 +++ tests/components/ds1307/test.esp32-ard.yaml | 12 ++-- .../components/ds1307/test.esp32-c3-ard.yaml | 12 ++-- .../components/ds1307/test.esp32-c3-idf.yaml | 12 ++-- tests/components/ds1307/test.esp32-idf.yaml | 12 ++-- tests/components/ds1307/test.esp8266-ard.yaml | 12 ++-- tests/components/ds1307/test.rp2040-ard.yaml | 12 ++-- tests/components/dsmr/common.yaml | 12 ++++ tests/components/dsmr/test.esp32-ard.yaml | 16 ++--- tests/components/dsmr/test.esp32-c3-ard.yaml | 16 ++--- tests/components/dsmr/test.esp8266-ard.yaml | 16 ++--- tests/components/dsmr/test.rp2040-ard.yaml | 16 ++--- 79 files changed, 519 insertions(+), 1385 deletions(-) create mode 100644 tests/components/dac7678/common.yaml create mode 100644 tests/components/daikin/common.yaml create mode 100644 tests/components/daikin_arc/common.yaml create mode 100644 tests/components/daikin_brc/common.yaml create mode 100644 tests/components/daly_bms/common.yaml create mode 100644 tests/components/deep_sleep/common-esp32.yaml create mode 100644 tests/components/deep_sleep/common.yaml create mode 100644 tests/components/delonghi/common.yaml create mode 100644 tests/components/dfplayer/common.yaml create mode 100644 tests/components/dfrobot_sen0395/common.yaml create mode 100644 tests/components/dht12/common.yaml create mode 100644 tests/components/dps310/common.yaml create mode 100644 tests/components/ds1307/common.yaml create mode 100644 tests/components/dsmr/common.yaml diff --git a/tests/components/dac7678/common.yaml b/tests/components/dac7678/common.yaml new file mode 100644 index 0000000000..efad81a5ff --- /dev/null +++ b/tests/components/dac7678/common.yaml @@ -0,0 +1,43 @@ +i2c: + - id: i2c_dac7678 + scl: ${scl_pin} + sda: ${sda_pin} + +dac7678: + address: 0x4A + id: dac7678_hub + internal_reference: true + +output: + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 0 + id: dac7678_1_ch0 + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 1 + id: dac7678_1_ch1 + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 2 + id: dac7678_1_ch2 + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 3 + id: dac7678_1_ch3 + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 4 + id: dac7678_1_ch4 + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 5 + id: dac7678_1_ch5 + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 6 + id: dac7678_1_ch6 + - platform: dac7678 + dac7678_id: dac7678_hub + channel: 7 + id: dac7678_1_ch7 diff --git a/tests/components/dac7678/test.esp32-ard.yaml b/tests/components/dac7678/test.esp32-ard.yaml index 946a7ca58d..63c3bd6afd 100644 --- a/tests/components/dac7678/test.esp32-ard.yaml +++ b/tests/components/dac7678/test.esp32-ard.yaml @@ -1,43 +1,5 @@ -i2c: - - id: i2c_dac7678 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -dac7678: - address: 0x4A - id: dac7678_hub - internal_reference: true - -output: - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 0 - id: dac7678_1_ch0 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 1 - id: dac7678_1_ch1 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 2 - id: dac7678_1_ch2 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 3 - id: dac7678_1_ch3 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 4 - id: dac7678_1_ch4 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 5 - id: dac7678_1_ch5 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 6 - id: dac7678_1_ch6 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 7 - id: dac7678_1_ch7 +<<: !include common.yaml diff --git a/tests/components/dac7678/test.esp32-c3-ard.yaml b/tests/components/dac7678/test.esp32-c3-ard.yaml index 7e3455bb68..ee2c29ca4e 100644 --- a/tests/components/dac7678/test.esp32-c3-ard.yaml +++ b/tests/components/dac7678/test.esp32-c3-ard.yaml @@ -1,43 +1,5 @@ -i2c: - - id: i2c_dac7678 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -dac7678: - address: 0x4A - id: dac7678_hub - internal_reference: true - -output: - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 0 - id: dac7678_1_ch0 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 1 - id: dac7678_1_ch1 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 2 - id: dac7678_1_ch2 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 3 - id: dac7678_1_ch3 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 4 - id: dac7678_1_ch4 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 5 - id: dac7678_1_ch5 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 6 - id: dac7678_1_ch6 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 7 - id: dac7678_1_ch7 +<<: !include common.yaml diff --git a/tests/components/dac7678/test.esp32-c3-idf.yaml b/tests/components/dac7678/test.esp32-c3-idf.yaml index 7e3455bb68..ee2c29ca4e 100644 --- a/tests/components/dac7678/test.esp32-c3-idf.yaml +++ b/tests/components/dac7678/test.esp32-c3-idf.yaml @@ -1,43 +1,5 @@ -i2c: - - id: i2c_dac7678 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -dac7678: - address: 0x4A - id: dac7678_hub - internal_reference: true - -output: - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 0 - id: dac7678_1_ch0 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 1 - id: dac7678_1_ch1 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 2 - id: dac7678_1_ch2 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 3 - id: dac7678_1_ch3 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 4 - id: dac7678_1_ch4 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 5 - id: dac7678_1_ch5 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 6 - id: dac7678_1_ch6 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 7 - id: dac7678_1_ch7 +<<: !include common.yaml diff --git a/tests/components/dac7678/test.esp32-idf.yaml b/tests/components/dac7678/test.esp32-idf.yaml index 946a7ca58d..63c3bd6afd 100644 --- a/tests/components/dac7678/test.esp32-idf.yaml +++ b/tests/components/dac7678/test.esp32-idf.yaml @@ -1,43 +1,5 @@ -i2c: - - id: i2c_dac7678 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -dac7678: - address: 0x4A - id: dac7678_hub - internal_reference: true - -output: - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 0 - id: dac7678_1_ch0 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 1 - id: dac7678_1_ch1 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 2 - id: dac7678_1_ch2 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 3 - id: dac7678_1_ch3 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 4 - id: dac7678_1_ch4 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 5 - id: dac7678_1_ch5 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 6 - id: dac7678_1_ch6 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 7 - id: dac7678_1_ch7 +<<: !include common.yaml diff --git a/tests/components/dac7678/test.esp8266-ard.yaml b/tests/components/dac7678/test.esp8266-ard.yaml index 7e3455bb68..ee2c29ca4e 100644 --- a/tests/components/dac7678/test.esp8266-ard.yaml +++ b/tests/components/dac7678/test.esp8266-ard.yaml @@ -1,43 +1,5 @@ -i2c: - - id: i2c_dac7678 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -dac7678: - address: 0x4A - id: dac7678_hub - internal_reference: true - -output: - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 0 - id: dac7678_1_ch0 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 1 - id: dac7678_1_ch1 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 2 - id: dac7678_1_ch2 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 3 - id: dac7678_1_ch3 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 4 - id: dac7678_1_ch4 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 5 - id: dac7678_1_ch5 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 6 - id: dac7678_1_ch6 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 7 - id: dac7678_1_ch7 +<<: !include common.yaml diff --git a/tests/components/dac7678/test.rp2040-ard.yaml b/tests/components/dac7678/test.rp2040-ard.yaml index 7e3455bb68..ee2c29ca4e 100644 --- a/tests/components/dac7678/test.rp2040-ard.yaml +++ b/tests/components/dac7678/test.rp2040-ard.yaml @@ -1,43 +1,5 @@ -i2c: - - id: i2c_dac7678 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -dac7678: - address: 0x4A - id: dac7678_hub - internal_reference: true - -output: - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 0 - id: dac7678_1_ch0 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 1 - id: dac7678_1_ch1 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 2 - id: dac7678_1_ch2 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 3 - id: dac7678_1_ch3 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 4 - id: dac7678_1_ch4 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 5 - id: dac7678_1_ch5 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 6 - id: dac7678_1_ch6 - - platform: dac7678 - dac7678_id: dac7678_hub - channel: 7 - id: dac7678_1_ch7 +<<: !include common.yaml diff --git a/tests/components/daikin/common.yaml b/tests/components/daikin/common.yaml new file mode 100644 index 0000000000..27f381b422 --- /dev/null +++ b/tests/components/daikin/common.yaml @@ -0,0 +1,12 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: daikin + horizontal_default: middle + vertical_default: middle + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 diff --git a/tests/components/daikin/test.esp32-ard.yaml b/tests/components/daikin/test.esp32-ard.yaml index 6672fe3e45..7b012aa64c 100644 --- a/tests/components/daikin/test.esp32-ard.yaml +++ b/tests/components/daikin/test.esp32-ard.yaml @@ -1,12 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: heatpumpir - protocol: daikin - horizontal_default: middle - vertical_default: middle - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 +<<: !include common.yaml diff --git a/tests/components/daikin/test.esp8266-ard.yaml b/tests/components/daikin/test.esp8266-ard.yaml index 47f02bbad9..f5097fcf5f 100644 --- a/tests/components/daikin/test.esp8266-ard.yaml +++ b/tests/components/daikin/test.esp8266-ard.yaml @@ -1,12 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: heatpumpir - protocol: daikin - horizontal_default: middle - vertical_default: middle - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 +<<: !include common.yaml diff --git a/tests/components/daikin_arc/common.yaml b/tests/components/daikin_arc/common.yaml new file mode 100644 index 0000000000..5c0510f6df --- /dev/null +++ b/tests/components/daikin_arc/common.yaml @@ -0,0 +1,19 @@ +remote_transmitter: + pin: ${tx_pin} + carrier_duty_percent: 50% + id: tsvr + +remote_receiver: + id: rcvr + pin: + number: ${rx_pin} + inverted: true + mode: + input: true + pullup: true + tolerance: 40% + +climate: + - platform: daikin_arc + name: Daikin AC + receiver_id: rcvr diff --git a/tests/components/daikin_arc/test.esp32-ard.yaml b/tests/components/daikin_arc/test.esp32-ard.yaml index a8556e8576..cd59eb0832 100644 --- a/tests/components/daikin_arc/test.esp32-ard.yaml +++ b/tests/components/daikin_arc/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% - id: tsvr +substitutions: + tx_pin: GPIO2 + rx_pin: GPIO4 -remote_receiver: - id: rcvr - pin: - number: 27 - inverted: true - mode: - input: true - pullup: true - tolerance: 40% - -climate: - - platform: daikin_arc - name: "AC" - receiver_id: rcvr +<<: !include common.yaml diff --git a/tests/components/daikin_arc/test.esp8266-ard.yaml b/tests/components/daikin_arc/test.esp8266-ard.yaml index abf1b34a6e..8e08490d0c 100644 --- a/tests/components/daikin_arc/test.esp8266-ard.yaml +++ b/tests/components/daikin_arc/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% - id: tsvr +substitutions: + tx_pin: GPIO5 + rx_pin: GPIO4 -remote_receiver: - id: rcvr - pin: - number: 2 - inverted: true - mode: - input: true - pullup: true - tolerance: 40% - -climate: - - platform: daikin_arc - name: "AC" - receiver_id: rcvr +<<: !include common.yaml diff --git a/tests/components/daikin_brc/common.yaml b/tests/components/daikin_brc/common.yaml new file mode 100644 index 0000000000..c9d7baa989 --- /dev/null +++ b/tests/components/daikin_brc/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: daikin_brc + name: Daikin_brc Climate diff --git a/tests/components/daikin_brc/test.esp32-ard.yaml b/tests/components/daikin_brc/test.esp32-ard.yaml index 89a5b00f5d..7b012aa64c 100644 --- a/tests/components/daikin_brc/test.esp32-ard.yaml +++ b/tests/components/daikin_brc/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: daikin_brc - name: Daikin_brc Climate +<<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp32-c3-ard.yaml b/tests/components/daikin_brc/test.esp32-c3-ard.yaml index 89a5b00f5d..7b012aa64c 100644 --- a/tests/components/daikin_brc/test.esp32-c3-ard.yaml +++ b/tests/components/daikin_brc/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: daikin_brc - name: Daikin_brc Climate +<<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp32-c3-idf.yaml b/tests/components/daikin_brc/test.esp32-c3-idf.yaml index 89a5b00f5d..7b012aa64c 100644 --- a/tests/components/daikin_brc/test.esp32-c3-idf.yaml +++ b/tests/components/daikin_brc/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: daikin_brc - name: Daikin_brc Climate +<<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp32-idf.yaml b/tests/components/daikin_brc/test.esp32-idf.yaml index 89a5b00f5d..7b012aa64c 100644 --- a/tests/components/daikin_brc/test.esp32-idf.yaml +++ b/tests/components/daikin_brc/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: daikin_brc - name: Daikin_brc Climate +<<: !include common.yaml diff --git a/tests/components/daikin_brc/test.esp8266-ard.yaml b/tests/components/daikin_brc/test.esp8266-ard.yaml index b8c74803a2..f5097fcf5f 100644 --- a/tests/components/daikin_brc/test.esp8266-ard.yaml +++ b/tests/components/daikin_brc/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: daikin_brc - name: Daikin_brc Climate +<<: !include common.yaml diff --git a/tests/components/daly_bms/common.yaml b/tests/components/daly_bms/common.yaml new file mode 100644 index 0000000000..a4cb849f9f --- /dev/null +++ b/tests/components/daly_bms/common.yaml @@ -0,0 +1,53 @@ +uart: + - id: uart_daly_bms + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 4800 + +daly_bms: + update_interval: 20s + +binary_sensor: + - platform: daly_bms + charging_mos_enabled: + name: Charging MOS + discharging_mos_enabled: + name: Discharging MOS + +sensor: + - platform: daly_bms + voltage: + name: Battery Voltage + current: + name: Battery Current + battery_level: + name: Battery Level + max_cell_voltage: + name: Max Cell Voltage + max_cell_voltage_number: + name: Max Cell Voltage Number + min_cell_voltage: + name: Min Cell Voltage + min_cell_voltage_number: + name: Min Cell Voltage Number + max_temperature: + name: Max Temperature + max_temperature_probe_number: + name: Max Temperature Probe Number + min_temperature: + name: Min Temperature + min_temperature_probe_number: + name: Min Temperature Probe Number + remaining_capacity: + name: Remaining Capacity + cells_number: + name: Cells Number + temperature_1: + name: Temperature 1 + temperature_2: + name: Temperature 2 + +text_sensor: + - platform: daly_bms + status: + name: BMS Status diff --git a/tests/components/daly_bms/test.esp32-ard.yaml b/tests/components/daly_bms/test.esp32-ard.yaml index ec9607334d..811f6b72a6 100644 --- a/tests/components/daly_bms/test.esp32-ard.yaml +++ b/tests/components/daly_bms/test.esp32-ard.yaml @@ -1,55 +1,5 @@ -uart: - - id: uart_daly_bms - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 4800 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -daly_bms: - update_interval: 20s - -binary_sensor: - - platform: daly_bms - charging_mos_enabled: - name: Charging MOS - discharging_mos_enabled: - name: Discharging MOS - -sensor: - - platform: daly_bms - voltage: - name: Battery Voltage - current: - name: Battery Current - battery_level: - name: Battery Level - max_cell_voltage: - name: Max Cell Voltage - max_cell_voltage_number: - name: Max Cell Voltage Number - min_cell_voltage: - name: Min Cell Voltage - min_cell_voltage_number: - name: Min Cell Voltage Number - max_temperature: - name: Max Temperature - max_temperature_probe_number: - name: Max Temperature Probe Number - min_temperature: - name: Min Temperature - min_temperature_probe_number: - name: Min Temperature Probe Number - remaining_capacity: - name: Remaining Capacity - cells_number: - name: Cells Number - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - -text_sensor: - - platform: daly_bms - status: - name: BMS Status +<<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp32-c3-ard.yaml b/tests/components/daly_bms/test.esp32-c3-ard.yaml index 237a6570b5..c79d14c740 100644 --- a/tests/components/daly_bms/test.esp32-c3-ard.yaml +++ b/tests/components/daly_bms/test.esp32-c3-ard.yaml @@ -1,55 +1,5 @@ -uart: - - id: uart_daly_bms - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -daly_bms: - update_interval: 20s - -binary_sensor: - - platform: daly_bms - charging_mos_enabled: - name: Charging MOS - discharging_mos_enabled: - name: Discharging MOS - -sensor: - - platform: daly_bms - voltage: - name: Battery Voltage - current: - name: Battery Current - battery_level: - name: Battery Level - max_cell_voltage: - name: Max Cell Voltage - max_cell_voltage_number: - name: Max Cell Voltage Number - min_cell_voltage: - name: Min Cell Voltage - min_cell_voltage_number: - name: Min Cell Voltage Number - max_temperature: - name: Max Temperature - max_temperature_probe_number: - name: Max Temperature Probe Number - min_temperature: - name: Min Temperature - min_temperature_probe_number: - name: Min Temperature Probe Number - remaining_capacity: - name: Remaining Capacity - cells_number: - name: Cells Number - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - -text_sensor: - - platform: daly_bms - status: - name: BMS Status +<<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp32-c3-idf.yaml b/tests/components/daly_bms/test.esp32-c3-idf.yaml index 237a6570b5..c79d14c740 100644 --- a/tests/components/daly_bms/test.esp32-c3-idf.yaml +++ b/tests/components/daly_bms/test.esp32-c3-idf.yaml @@ -1,55 +1,5 @@ -uart: - - id: uart_daly_bms - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -daly_bms: - update_interval: 20s - -binary_sensor: - - platform: daly_bms - charging_mos_enabled: - name: Charging MOS - discharging_mos_enabled: - name: Discharging MOS - -sensor: - - platform: daly_bms - voltage: - name: Battery Voltage - current: - name: Battery Current - battery_level: - name: Battery Level - max_cell_voltage: - name: Max Cell Voltage - max_cell_voltage_number: - name: Max Cell Voltage Number - min_cell_voltage: - name: Min Cell Voltage - min_cell_voltage_number: - name: Min Cell Voltage Number - max_temperature: - name: Max Temperature - max_temperature_probe_number: - name: Max Temperature Probe Number - min_temperature: - name: Min Temperature - min_temperature_probe_number: - name: Min Temperature Probe Number - remaining_capacity: - name: Remaining Capacity - cells_number: - name: Cells Number - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - -text_sensor: - - platform: daly_bms - status: - name: BMS Status +<<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp32-idf.yaml b/tests/components/daly_bms/test.esp32-idf.yaml index ec9607334d..811f6b72a6 100644 --- a/tests/components/daly_bms/test.esp32-idf.yaml +++ b/tests/components/daly_bms/test.esp32-idf.yaml @@ -1,55 +1,5 @@ -uart: - - id: uart_daly_bms - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 4800 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -daly_bms: - update_interval: 20s - -binary_sensor: - - platform: daly_bms - charging_mos_enabled: - name: Charging MOS - discharging_mos_enabled: - name: Discharging MOS - -sensor: - - platform: daly_bms - voltage: - name: Battery Voltage - current: - name: Battery Current - battery_level: - name: Battery Level - max_cell_voltage: - name: Max Cell Voltage - max_cell_voltage_number: - name: Max Cell Voltage Number - min_cell_voltage: - name: Min Cell Voltage - min_cell_voltage_number: - name: Min Cell Voltage Number - max_temperature: - name: Max Temperature - max_temperature_probe_number: - name: Max Temperature Probe Number - min_temperature: - name: Min Temperature - min_temperature_probe_number: - name: Min Temperature Probe Number - remaining_capacity: - name: Remaining Capacity - cells_number: - name: Cells Number - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - -text_sensor: - - platform: daly_bms - status: - name: BMS Status +<<: !include common.yaml diff --git a/tests/components/daly_bms/test.esp8266-ard.yaml b/tests/components/daly_bms/test.esp8266-ard.yaml index 237a6570b5..3b44f9c9c3 100644 --- a/tests/components/daly_bms/test.esp8266-ard.yaml +++ b/tests/components/daly_bms/test.esp8266-ard.yaml @@ -1,55 +1,5 @@ -uart: - - id: uart_daly_bms - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -daly_bms: - update_interval: 20s - -binary_sensor: - - platform: daly_bms - charging_mos_enabled: - name: Charging MOS - discharging_mos_enabled: - name: Discharging MOS - -sensor: - - platform: daly_bms - voltage: - name: Battery Voltage - current: - name: Battery Current - battery_level: - name: Battery Level - max_cell_voltage: - name: Max Cell Voltage - max_cell_voltage_number: - name: Max Cell Voltage Number - min_cell_voltage: - name: Min Cell Voltage - min_cell_voltage_number: - name: Min Cell Voltage Number - max_temperature: - name: Max Temperature - max_temperature_probe_number: - name: Max Temperature Probe Number - min_temperature: - name: Min Temperature - min_temperature_probe_number: - name: Min Temperature Probe Number - remaining_capacity: - name: Remaining Capacity - cells_number: - name: Cells Number - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - -text_sensor: - - platform: daly_bms - status: - name: BMS Status +<<: !include common.yaml diff --git a/tests/components/daly_bms/test.rp2040-ard.yaml b/tests/components/daly_bms/test.rp2040-ard.yaml index 237a6570b5..b516342f3b 100644 --- a/tests/components/daly_bms/test.rp2040-ard.yaml +++ b/tests/components/daly_bms/test.rp2040-ard.yaml @@ -1,55 +1,5 @@ -uart: - - id: uart_daly_bms - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 4800 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -daly_bms: - update_interval: 20s - -binary_sensor: - - platform: daly_bms - charging_mos_enabled: - name: Charging MOS - discharging_mos_enabled: - name: Discharging MOS - -sensor: - - platform: daly_bms - voltage: - name: Battery Voltage - current: - name: Battery Current - battery_level: - name: Battery Level - max_cell_voltage: - name: Max Cell Voltage - max_cell_voltage_number: - name: Max Cell Voltage Number - min_cell_voltage: - name: Min Cell Voltage - min_cell_voltage_number: - name: Min Cell Voltage Number - max_temperature: - name: Max Temperature - max_temperature_probe_number: - name: Max Temperature Probe Number - min_temperature: - name: Min Temperature - min_temperature_probe_number: - name: Min Temperature Probe Number - remaining_capacity: - name: Remaining Capacity - cells_number: - name: Cells Number - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - -text_sensor: - - platform: daly_bms - status: - name: BMS Status +<<: !include common.yaml diff --git a/tests/components/deep_sleep/common-esp32.yaml b/tests/components/deep_sleep/common-esp32.yaml new file mode 100644 index 0000000000..c20e1a902e --- /dev/null +++ b/tests/components/deep_sleep/common-esp32.yaml @@ -0,0 +1,7 @@ +deep_sleep: + run_duration: + default: 10s + gpio_wakeup_reason: 30s + sleep_duration: 50s + wakeup_pin: ${wakeup_pin} + wakeup_pin_mode: INVERT_WAKEUP diff --git a/tests/components/deep_sleep/common.yaml b/tests/components/deep_sleep/common.yaml new file mode 100644 index 0000000000..c090cb83e2 --- /dev/null +++ b/tests/components/deep_sleep/common.yaml @@ -0,0 +1,6 @@ +esphome: + on_boot: + then: + - deep_sleep.prevent + - delay: 1s + - deep_sleep.allow diff --git a/tests/components/deep_sleep/test.esp32-ard.yaml b/tests/components/deep_sleep/test.esp32-ard.yaml index 94942fd5b0..10c17af0f5 100644 --- a/tests/components/deep_sleep/test.esp32-ard.yaml +++ b/tests/components/deep_sleep/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -esphome: - on_boot: - then: - - deep_sleep.prevent - - delay: 1s - - deep_sleep.allow +substitutions: + wakeup_pin: GPIO4 -deep_sleep: - run_duration: - default: 10s - gpio_wakeup_reason: 30s - sleep_duration: 50s - wakeup_pin: 4 - wakeup_pin_mode: INVERT_WAKEUP +<<: !include common.yaml +<<: !include common-esp32.yaml diff --git a/tests/components/deep_sleep/test.esp32-c3-ard.yaml b/tests/components/deep_sleep/test.esp32-c3-ard.yaml index 94942fd5b0..10c17af0f5 100644 --- a/tests/components/deep_sleep/test.esp32-c3-ard.yaml +++ b/tests/components/deep_sleep/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -esphome: - on_boot: - then: - - deep_sleep.prevent - - delay: 1s - - deep_sleep.allow +substitutions: + wakeup_pin: GPIO4 -deep_sleep: - run_duration: - default: 10s - gpio_wakeup_reason: 30s - sleep_duration: 50s - wakeup_pin: 4 - wakeup_pin_mode: INVERT_WAKEUP +<<: !include common.yaml +<<: !include common-esp32.yaml diff --git a/tests/components/deep_sleep/test.esp32-c3-idf.yaml b/tests/components/deep_sleep/test.esp32-c3-idf.yaml index 94942fd5b0..10c17af0f5 100644 --- a/tests/components/deep_sleep/test.esp32-c3-idf.yaml +++ b/tests/components/deep_sleep/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -esphome: - on_boot: - then: - - deep_sleep.prevent - - delay: 1s - - deep_sleep.allow +substitutions: + wakeup_pin: GPIO4 -deep_sleep: - run_duration: - default: 10s - gpio_wakeup_reason: 30s - sleep_duration: 50s - wakeup_pin: 4 - wakeup_pin_mode: INVERT_WAKEUP +<<: !include common.yaml +<<: !include common-esp32.yaml diff --git a/tests/components/deep_sleep/test.esp32-idf.yaml b/tests/components/deep_sleep/test.esp32-idf.yaml index 94942fd5b0..10c17af0f5 100644 --- a/tests/components/deep_sleep/test.esp32-idf.yaml +++ b/tests/components/deep_sleep/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -esphome: - on_boot: - then: - - deep_sleep.prevent - - delay: 1s - - deep_sleep.allow +substitutions: + wakeup_pin: GPIO4 -deep_sleep: - run_duration: - default: 10s - gpio_wakeup_reason: 30s - sleep_duration: 50s - wakeup_pin: 4 - wakeup_pin_mode: INVERT_WAKEUP +<<: !include common.yaml +<<: !include common-esp32.yaml diff --git a/tests/components/deep_sleep/test.esp8266-ard.yaml b/tests/components/deep_sleep/test.esp8266-ard.yaml index 0992fa9696..df08ec8a14 100644 --- a/tests/components/deep_sleep/test.esp8266-ard.yaml +++ b/tests/components/deep_sleep/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -esphome: - on_boot: - then: - - deep_sleep.prevent - - delay: 1s - - deep_sleep.allow - deep_sleep: run_duration: 10s sleep_duration: 50s + +<<: !include common.yaml diff --git a/tests/components/delonghi/common.yaml b/tests/components/delonghi/common.yaml new file mode 100644 index 0000000000..8e9a1293d7 --- /dev/null +++ b/tests/components/delonghi/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: delonghi + name: Delonghi Climate diff --git a/tests/components/delonghi/test.esp32-ard.yaml b/tests/components/delonghi/test.esp32-ard.yaml index cfe0f837f0..7b012aa64c 100644 --- a/tests/components/delonghi/test.esp32-ard.yaml +++ b/tests/components/delonghi/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: delonghi - name: Delonghi Climate +<<: !include common.yaml diff --git a/tests/components/delonghi/test.esp32-c3-ard.yaml b/tests/components/delonghi/test.esp32-c3-ard.yaml index cfe0f837f0..7b012aa64c 100644 --- a/tests/components/delonghi/test.esp32-c3-ard.yaml +++ b/tests/components/delonghi/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: delonghi - name: Delonghi Climate +<<: !include common.yaml diff --git a/tests/components/delonghi/test.esp32-c3-idf.yaml b/tests/components/delonghi/test.esp32-c3-idf.yaml index cfe0f837f0..7b012aa64c 100644 --- a/tests/components/delonghi/test.esp32-c3-idf.yaml +++ b/tests/components/delonghi/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: delonghi - name: Delonghi Climate +<<: !include common.yaml diff --git a/tests/components/delonghi/test.esp32-idf.yaml b/tests/components/delonghi/test.esp32-idf.yaml index cfe0f837f0..7b012aa64c 100644 --- a/tests/components/delonghi/test.esp32-idf.yaml +++ b/tests/components/delonghi/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: delonghi - name: Delonghi Climate +<<: !include common.yaml diff --git a/tests/components/delonghi/test.esp8266-ard.yaml b/tests/components/delonghi/test.esp8266-ard.yaml index adc478a6e6..f5097fcf5f 100644 --- a/tests/components/delonghi/test.esp8266-ard.yaml +++ b/tests/components/delonghi/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: delonghi - name: Delonghi Climate +<<: !include common.yaml diff --git a/tests/components/dfplayer/common.yaml b/tests/components/dfplayer/common.yaml new file mode 100644 index 0000000000..d7446141c3 --- /dev/null +++ b/tests/components/dfplayer/common.yaml @@ -0,0 +1,42 @@ +esphome: + on_boot: + then: + - dfplayer.play: 5 + - dfplayer.play: + file: 4 + loop: true + - dfplayer.play_folder: + folder: 1 + file: 3 + - dfplayer.play_folder: + folder: 1 + loop: true + - dfplayer.set_device: + device: TF_CARD + - dfplayer.set_volume: 5 + - dfplayer.set_eq: ROCK + - dfplayer.play_next + - dfplayer.play_previous + - dfplayer.reset + - dfplayer.start + - dfplayer.pause + - dfplayer.stop + - dfplayer.random + - dfplayer.volume_up + - dfplayer.volume_down + - dfplayer.sleep + +uart: + - id: uart_dfplayer + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +dfplayer: + on_finished_playback: + then: + if: + condition: + not: dfplayer.is_playing + then: + logger.log: Playback finished event diff --git a/tests/components/dfplayer/test.esp32-ard.yaml b/tests/components/dfplayer/test.esp32-ard.yaml index 03b44b8ca9..811f6b72a6 100644 --- a/tests/components/dfplayer/test.esp32-ard.yaml +++ b/tests/components/dfplayer/test.esp32-ard.yaml @@ -1,42 +1,5 @@ -esphome: - on_boot: - then: - - dfplayer.play: 5 - - dfplayer.play: - file: 4 - loop: true - - dfplayer.play_folder: - folder: 1 - file: 3 - - dfplayer.play_folder: - folder: 1 - loop: true - - dfplayer.set_device: - device: TF_CARD - - dfplayer.set_volume: 5 - - dfplayer.set_eq: ROCK - - dfplayer.play_next - - dfplayer.play_previous - - dfplayer.reset - - dfplayer.start - - dfplayer.pause - - dfplayer.stop - - dfplayer.random - - dfplayer.volume_up - - dfplayer.volume_down - - dfplayer.sleep +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -uart: - - id: uart_dfplayer - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -dfplayer: - on_finished_playback: - then: - if: - condition: - not: dfplayer.is_playing - then: - logger.log: Playback finished event +<<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp32-c3-ard.yaml b/tests/components/dfplayer/test.esp32-c3-ard.yaml index 94355915a7..c79d14c740 100644 --- a/tests/components/dfplayer/test.esp32-c3-ard.yaml +++ b/tests/components/dfplayer/test.esp32-c3-ard.yaml @@ -1,42 +1,5 @@ -esphome: - on_boot: - then: - - dfplayer.play: 5 - - dfplayer.play: - file: 4 - loop: true - - dfplayer.play_folder: - folder: 1 - file: 3 - - dfplayer.play_folder: - folder: 1 - loop: true - - dfplayer.set_device: - device: TF_CARD - - dfplayer.set_volume: 5 - - dfplayer.set_eq: ROCK - - dfplayer.play_next - - dfplayer.play_previous - - dfplayer.reset - - dfplayer.start - - dfplayer.pause - - dfplayer.stop - - dfplayer.random - - dfplayer.volume_up - - dfplayer.volume_down - - dfplayer.sleep +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -uart: - - id: uart_dfplayer - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfplayer: - on_finished_playback: - then: - if: - condition: - not: dfplayer.is_playing - then: - logger.log: Playback finished event +<<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp32-c3-idf.yaml b/tests/components/dfplayer/test.esp32-c3-idf.yaml index 94355915a7..c79d14c740 100644 --- a/tests/components/dfplayer/test.esp32-c3-idf.yaml +++ b/tests/components/dfplayer/test.esp32-c3-idf.yaml @@ -1,42 +1,5 @@ -esphome: - on_boot: - then: - - dfplayer.play: 5 - - dfplayer.play: - file: 4 - loop: true - - dfplayer.play_folder: - folder: 1 - file: 3 - - dfplayer.play_folder: - folder: 1 - loop: true - - dfplayer.set_device: - device: TF_CARD - - dfplayer.set_volume: 5 - - dfplayer.set_eq: ROCK - - dfplayer.play_next - - dfplayer.play_previous - - dfplayer.reset - - dfplayer.start - - dfplayer.pause - - dfplayer.stop - - dfplayer.random - - dfplayer.volume_up - - dfplayer.volume_down - - dfplayer.sleep +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -uart: - - id: uart_dfplayer - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfplayer: - on_finished_playback: - then: - if: - condition: - not: dfplayer.is_playing - then: - logger.log: Playback finished event +<<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp32-idf.yaml b/tests/components/dfplayer/test.esp32-idf.yaml index 03b44b8ca9..811f6b72a6 100644 --- a/tests/components/dfplayer/test.esp32-idf.yaml +++ b/tests/components/dfplayer/test.esp32-idf.yaml @@ -1,42 +1,5 @@ -esphome: - on_boot: - then: - - dfplayer.play: 5 - - dfplayer.play: - file: 4 - loop: true - - dfplayer.play_folder: - folder: 1 - file: 3 - - dfplayer.play_folder: - folder: 1 - loop: true - - dfplayer.set_device: - device: TF_CARD - - dfplayer.set_volume: 5 - - dfplayer.set_eq: ROCK - - dfplayer.play_next - - dfplayer.play_previous - - dfplayer.reset - - dfplayer.start - - dfplayer.pause - - dfplayer.stop - - dfplayer.random - - dfplayer.volume_up - - dfplayer.volume_down - - dfplayer.sleep +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -uart: - - id: uart_dfplayer - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -dfplayer: - on_finished_playback: - then: - if: - condition: - not: dfplayer.is_playing - then: - logger.log: Playback finished event +<<: !include common.yaml diff --git a/tests/components/dfplayer/test.esp8266-ard.yaml b/tests/components/dfplayer/test.esp8266-ard.yaml index 94355915a7..3b44f9c9c3 100644 --- a/tests/components/dfplayer/test.esp8266-ard.yaml +++ b/tests/components/dfplayer/test.esp8266-ard.yaml @@ -1,42 +1,5 @@ -esphome: - on_boot: - then: - - dfplayer.play: 5 - - dfplayer.play: - file: 4 - loop: true - - dfplayer.play_folder: - folder: 1 - file: 3 - - dfplayer.play_folder: - folder: 1 - loop: true - - dfplayer.set_device: - device: TF_CARD - - dfplayer.set_volume: 5 - - dfplayer.set_eq: ROCK - - dfplayer.play_next - - dfplayer.play_previous - - dfplayer.reset - - dfplayer.start - - dfplayer.pause - - dfplayer.stop - - dfplayer.random - - dfplayer.volume_up - - dfplayer.volume_down - - dfplayer.sleep +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -uart: - - id: uart_dfplayer - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfplayer: - on_finished_playback: - then: - if: - condition: - not: dfplayer.is_playing - then: - logger.log: Playback finished event +<<: !include common.yaml diff --git a/tests/components/dfplayer/test.rp2040-ard.yaml b/tests/components/dfplayer/test.rp2040-ard.yaml index 94355915a7..b516342f3b 100644 --- a/tests/components/dfplayer/test.rp2040-ard.yaml +++ b/tests/components/dfplayer/test.rp2040-ard.yaml @@ -1,42 +1,5 @@ -esphome: - on_boot: - then: - - dfplayer.play: 5 - - dfplayer.play: - file: 4 - loop: true - - dfplayer.play_folder: - folder: 1 - file: 3 - - dfplayer.play_folder: - folder: 1 - loop: true - - dfplayer.set_device: - device: TF_CARD - - dfplayer.set_volume: 5 - - dfplayer.set_eq: ROCK - - dfplayer.play_next - - dfplayer.play_previous - - dfplayer.reset - - dfplayer.start - - dfplayer.pause - - dfplayer.stop - - dfplayer.random - - dfplayer.volume_up - - dfplayer.volume_down - - dfplayer.sleep +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_dfplayer - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfplayer: - on_finished_playback: - then: - if: - condition: - not: dfplayer.is_playing - then: - logger.log: Playback finished event +<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/common.yaml b/tests/components/dfrobot_sen0395/common.yaml new file mode 100644 index 0000000000..69bcebf182 --- /dev/null +++ b/tests/components/dfrobot_sen0395/common.yaml @@ -0,0 +1,28 @@ +esphome: + on_boot: + then: + - dfrobot_sen0395.settings: + id: mmwave + factory_reset: true + detection_segments: + - [0cm, 5m] + - 600cm + - !lambda "return 7;" + output_latency: + delay_after_detect: 0s + delay_after_disappear: 0s + sensitivity: 6 + - dfrobot_sen0395.reset + +uart: + - id: uart_dfrobot_sen0395 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +dfrobot_sen0395: + - id: mmwave + +binary_sensor: + - platform: dfrobot_sen0395 + id: mmwave_detected diff --git a/tests/components/dfrobot_sen0395/test.esp32-ard.yaml b/tests/components/dfrobot_sen0395/test.esp32-ard.yaml index 5c06fc6660..811f6b72a6 100644 --- a/tests/components/dfrobot_sen0395/test.esp32-ard.yaml +++ b/tests/components/dfrobot_sen0395/test.esp32-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - dfrobot_sen0395.settings: - id: mmwave - factory_reset: true - detection_segments: - - [0cm, 5m] - - 600cm - - !lambda "return 7;" - output_latency: - delay_after_detect: 0s - delay_after_disappear: 0s - sensitivity: 6 - - dfrobot_sen0395.reset +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -uart: - - id: uart_dfrobot_sen0395 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -dfrobot_sen0395: - - id: mmwave - -binary_sensor: - - platform: dfrobot_sen0395 - id: mmwave_detected +<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml b/tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml index 71b17cecd5..c79d14c740 100644 --- a/tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml +++ b/tests/components/dfrobot_sen0395/test.esp32-c3-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - dfrobot_sen0395.settings: - id: mmwave - factory_reset: true - detection_segments: - - [0cm, 5m] - - 600cm - - !lambda "return 7;" - output_latency: - delay_after_detect: 0s - delay_after_disappear: 0s - sensitivity: 6 - - dfrobot_sen0395.reset +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -uart: - - id: uart_dfrobot_sen0395 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfrobot_sen0395: - - id: mmwave - -binary_sensor: - - platform: dfrobot_sen0395 - id: mmwave_detected +<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml b/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml index 71b17cecd5..c79d14c740 100644 --- a/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml +++ b/tests/components/dfrobot_sen0395/test.esp32-c3-idf.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - dfrobot_sen0395.settings: - id: mmwave - factory_reset: true - detection_segments: - - [0cm, 5m] - - 600cm - - !lambda "return 7;" - output_latency: - delay_after_detect: 0s - delay_after_disappear: 0s - sensitivity: 6 - - dfrobot_sen0395.reset +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -uart: - - id: uart_dfrobot_sen0395 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfrobot_sen0395: - - id: mmwave - -binary_sensor: - - platform: dfrobot_sen0395 - id: mmwave_detected +<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp32-idf.yaml b/tests/components/dfrobot_sen0395/test.esp32-idf.yaml index 5c06fc6660..811f6b72a6 100644 --- a/tests/components/dfrobot_sen0395/test.esp32-idf.yaml +++ b/tests/components/dfrobot_sen0395/test.esp32-idf.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - dfrobot_sen0395.settings: - id: mmwave - factory_reset: true - detection_segments: - - [0cm, 5m] - - 600cm - - !lambda "return 7;" - output_latency: - delay_after_detect: 0s - delay_after_disappear: 0s - sensitivity: 6 - - dfrobot_sen0395.reset +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -uart: - - id: uart_dfrobot_sen0395 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -dfrobot_sen0395: - - id: mmwave - -binary_sensor: - - platform: dfrobot_sen0395 - id: mmwave_detected +<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml b/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml index 71b17cecd5..3b44f9c9c3 100644 --- a/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml +++ b/tests/components/dfrobot_sen0395/test.esp8266-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - dfrobot_sen0395.settings: - id: mmwave - factory_reset: true - detection_segments: - - [0cm, 5m] - - 600cm - - !lambda "return 7;" - output_latency: - delay_after_detect: 0s - delay_after_disappear: 0s - sensitivity: 6 - - dfrobot_sen0395.reset +substitutions: + tx_pin: GPIO1 + rx_pin: GPIO3 -uart: - - id: uart_dfrobot_sen0395 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfrobot_sen0395: - - id: mmwave - -binary_sensor: - - platform: dfrobot_sen0395 - id: mmwave_detected +<<: !include common.yaml diff --git a/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml b/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml index 71b17cecd5..b516342f3b 100644 --- a/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml +++ b/tests/components/dfrobot_sen0395/test.rp2040-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - dfrobot_sen0395.settings: - id: mmwave - factory_reset: true - detection_segments: - - [0cm, 5m] - - 600cm - - !lambda "return 7;" - output_latency: - delay_after_detect: 0s - delay_after_disappear: 0s - sensitivity: 6 - - dfrobot_sen0395.reset +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_dfrobot_sen0395 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -dfrobot_sen0395: - - id: mmwave - -binary_sensor: - - platform: dfrobot_sen0395 - id: mmwave_detected +<<: !include common.yaml diff --git a/tests/components/dht12/common.yaml b/tests/components/dht12/common.yaml new file mode 100644 index 0000000000..91346e0e27 --- /dev/null +++ b/tests/components/dht12/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_dht12 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: dht12 + temperature: + name: DHT12 Temperature + humidity: + name: DHT12 Humidity + update_interval: 15s diff --git a/tests/components/dht12/test.esp32-ard.yaml b/tests/components/dht12/test.esp32-ard.yaml index 02a00f5df7..63c3bd6afd 100644 --- a/tests/components/dht12/test.esp32-ard.yaml +++ b/tests/components/dht12/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dht12 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: dht12 - temperature: - name: DHT12 Temperature - humidity: - name: DHT12 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/dht12/test.esp32-c3-ard.yaml b/tests/components/dht12/test.esp32-c3-ard.yaml index c06c20fd9f..ee2c29ca4e 100644 --- a/tests/components/dht12/test.esp32-c3-ard.yaml +++ b/tests/components/dht12/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dht12 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dht12 - temperature: - name: DHT12 Temperature - humidity: - name: DHT12 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/dht12/test.esp32-c3-idf.yaml b/tests/components/dht12/test.esp32-c3-idf.yaml index c06c20fd9f..ee2c29ca4e 100644 --- a/tests/components/dht12/test.esp32-c3-idf.yaml +++ b/tests/components/dht12/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dht12 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dht12 - temperature: - name: DHT12 Temperature - humidity: - name: DHT12 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/dht12/test.esp32-idf.yaml b/tests/components/dht12/test.esp32-idf.yaml index 02a00f5df7..63c3bd6afd 100644 --- a/tests/components/dht12/test.esp32-idf.yaml +++ b/tests/components/dht12/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dht12 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: dht12 - temperature: - name: DHT12 Temperature - humidity: - name: DHT12 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/dht12/test.esp8266-ard.yaml b/tests/components/dht12/test.esp8266-ard.yaml index c06c20fd9f..ee2c29ca4e 100644 --- a/tests/components/dht12/test.esp8266-ard.yaml +++ b/tests/components/dht12/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dht12 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dht12 - temperature: - name: DHT12 Temperature - humidity: - name: DHT12 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/dht12/test.rp2040-ard.yaml b/tests/components/dht12/test.rp2040-ard.yaml index c06c20fd9f..ee2c29ca4e 100644 --- a/tests/components/dht12/test.rp2040-ard.yaml +++ b/tests/components/dht12/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dht12 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dht12 - temperature: - name: DHT12 Temperature - humidity: - name: DHT12 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/dps310/common.yaml b/tests/components/dps310/common.yaml new file mode 100644 index 0000000000..e6c0c9fab8 --- /dev/null +++ b/tests/components/dps310/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_dps310 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: dps310 + temperature: + name: DPS310 Temperature + pressure: + name: DPS310 Pressure + address: 0x77 diff --git a/tests/components/dps310/test.esp32-ard.yaml b/tests/components/dps310/test.esp32-ard.yaml index 417cab5c40..63c3bd6afd 100644 --- a/tests/components/dps310/test.esp32-ard.yaml +++ b/tests/components/dps310/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dps310 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: dps310 - temperature: - name: DPS310 Temperature - pressure: - name: DPS310 Pressure - address: 0x77 +<<: !include common.yaml diff --git a/tests/components/dps310/test.esp32-c3-ard.yaml b/tests/components/dps310/test.esp32-c3-ard.yaml index 0e15e9ccc5..ee2c29ca4e 100644 --- a/tests/components/dps310/test.esp32-c3-ard.yaml +++ b/tests/components/dps310/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dps310 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dps310 - temperature: - name: DPS310 Temperature - pressure: - name: DPS310 Pressure - address: 0x77 +<<: !include common.yaml diff --git a/tests/components/dps310/test.esp32-c3-idf.yaml b/tests/components/dps310/test.esp32-c3-idf.yaml index 0e15e9ccc5..ee2c29ca4e 100644 --- a/tests/components/dps310/test.esp32-c3-idf.yaml +++ b/tests/components/dps310/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dps310 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dps310 - temperature: - name: DPS310 Temperature - pressure: - name: DPS310 Pressure - address: 0x77 +<<: !include common.yaml diff --git a/tests/components/dps310/test.esp32-idf.yaml b/tests/components/dps310/test.esp32-idf.yaml index 417cab5c40..63c3bd6afd 100644 --- a/tests/components/dps310/test.esp32-idf.yaml +++ b/tests/components/dps310/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dps310 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: dps310 - temperature: - name: DPS310 Temperature - pressure: - name: DPS310 Pressure - address: 0x77 +<<: !include common.yaml diff --git a/tests/components/dps310/test.esp8266-ard.yaml b/tests/components/dps310/test.esp8266-ard.yaml index 0e15e9ccc5..ee2c29ca4e 100644 --- a/tests/components/dps310/test.esp8266-ard.yaml +++ b/tests/components/dps310/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dps310 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dps310 - temperature: - name: DPS310 Temperature - pressure: - name: DPS310 Pressure - address: 0x77 +<<: !include common.yaml diff --git a/tests/components/dps310/test.rp2040-ard.yaml b/tests/components/dps310/test.rp2040-ard.yaml index 0e15e9ccc5..ee2c29ca4e 100644 --- a/tests/components/dps310/test.rp2040-ard.yaml +++ b/tests/components/dps310/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_dps310 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: dps310 - temperature: - name: DPS310 Temperature - pressure: - name: DPS310 Pressure - address: 0x77 +<<: !include common.yaml diff --git a/tests/components/ds1307/common.yaml b/tests/components/ds1307/common.yaml new file mode 100644 index 0000000000..3466a9eec8 --- /dev/null +++ b/tests/components/ds1307/common.yaml @@ -0,0 +1,9 @@ +i2c: + - id: i2c_ds1307 + scl: ${scl_pin} + sda: ${sda_pin} + +time: + - platform: ds1307 + id: ds1307_time + update_interval: never diff --git a/tests/components/ds1307/test.esp32-ard.yaml b/tests/components/ds1307/test.esp32-ard.yaml index 017c7aac92..63c3bd6afd 100644 --- a/tests/components/ds1307/test.esp32-ard.yaml +++ b/tests/components/ds1307/test.esp32-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ds1307 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -time: - - platform: ds1307 - id: ds1307_time - update_interval: never +<<: !include common.yaml diff --git a/tests/components/ds1307/test.esp32-c3-ard.yaml b/tests/components/ds1307/test.esp32-c3-ard.yaml index c309b9c212..ee2c29ca4e 100644 --- a/tests/components/ds1307/test.esp32-c3-ard.yaml +++ b/tests/components/ds1307/test.esp32-c3-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ds1307 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -time: - - platform: ds1307 - id: ds1307_time - update_interval: never +<<: !include common.yaml diff --git a/tests/components/ds1307/test.esp32-c3-idf.yaml b/tests/components/ds1307/test.esp32-c3-idf.yaml index c309b9c212..ee2c29ca4e 100644 --- a/tests/components/ds1307/test.esp32-c3-idf.yaml +++ b/tests/components/ds1307/test.esp32-c3-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ds1307 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -time: - - platform: ds1307 - id: ds1307_time - update_interval: never +<<: !include common.yaml diff --git a/tests/components/ds1307/test.esp32-idf.yaml b/tests/components/ds1307/test.esp32-idf.yaml index 017c7aac92..63c3bd6afd 100644 --- a/tests/components/ds1307/test.esp32-idf.yaml +++ b/tests/components/ds1307/test.esp32-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ds1307 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -time: - - platform: ds1307 - id: ds1307_time - update_interval: never +<<: !include common.yaml diff --git a/tests/components/ds1307/test.esp8266-ard.yaml b/tests/components/ds1307/test.esp8266-ard.yaml index c309b9c212..ee2c29ca4e 100644 --- a/tests/components/ds1307/test.esp8266-ard.yaml +++ b/tests/components/ds1307/test.esp8266-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ds1307 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -time: - - platform: ds1307 - id: ds1307_time - update_interval: never +<<: !include common.yaml diff --git a/tests/components/ds1307/test.rp2040-ard.yaml b/tests/components/ds1307/test.rp2040-ard.yaml index c309b9c212..ee2c29ca4e 100644 --- a/tests/components/ds1307/test.rp2040-ard.yaml +++ b/tests/components/ds1307/test.rp2040-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_ds1307 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -time: - - platform: ds1307 - id: ds1307_time - update_interval: never +<<: !include common.yaml diff --git a/tests/components/dsmr/common.yaml b/tests/components/dsmr/common.yaml new file mode 100644 index 0000000000..2901b811fe --- /dev/null +++ b/tests/components/dsmr/common.yaml @@ -0,0 +1,12 @@ +uart: + - id: uart_dsmr + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +dsmr: + decryption_key: 00112233445566778899aabbccddeeff + max_telegram_length: 1000 + request_pin: ${request_pin} + request_interval: 20s + receive_timeout: 100ms diff --git a/tests/components/dsmr/test.esp32-ard.yaml b/tests/components/dsmr/test.esp32-ard.yaml index 1fd0448ab3..7a65a48ec0 100644 --- a/tests/components/dsmr/test.esp32-ard.yaml +++ b/tests/components/dsmr/test.esp32-ard.yaml @@ -1,12 +1,6 @@ -uart: - - id: uart_dsmr - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + request_pin: GPIO15 -dsmr: - decryption_key: 00112233445566778899aabbccddeeff - max_telegram_length: 1000 - request_pin: 15 - request_interval: 20s - receive_timeout: 100ms +<<: !include common.yaml diff --git a/tests/components/dsmr/test.esp32-c3-ard.yaml b/tests/components/dsmr/test.esp32-c3-ard.yaml index e813556be8..72998506ac 100644 --- a/tests/components/dsmr/test.esp32-c3-ard.yaml +++ b/tests/components/dsmr/test.esp32-c3-ard.yaml @@ -1,12 +1,6 @@ -uart: - - id: uart_dsmr - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + request_pin: GPIO6 -dsmr: - decryption_key: 00112233445566778899aabbccddeeff - max_telegram_length: 1000 - request_pin: 6 - request_interval: 20s - receive_timeout: 100ms +<<: !include common.yaml diff --git a/tests/components/dsmr/test.esp8266-ard.yaml b/tests/components/dsmr/test.esp8266-ard.yaml index 8037fb4b1a..a47bd58806 100644 --- a/tests/components/dsmr/test.esp8266-ard.yaml +++ b/tests/components/dsmr/test.esp8266-ard.yaml @@ -1,12 +1,6 @@ -uart: - - id: uart_dsmr - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + request_pin: GPIO15 -dsmr: - decryption_key: 00112233445566778899aabbccddeeff - max_telegram_length: 1000 - request_pin: 15 - request_interval: 20s - receive_timeout: 100ms +<<: !include common.yaml diff --git a/tests/components/dsmr/test.rp2040-ard.yaml b/tests/components/dsmr/test.rp2040-ard.yaml index e813556be8..72998506ac 100644 --- a/tests/components/dsmr/test.rp2040-ard.yaml +++ b/tests/components/dsmr/test.rp2040-ard.yaml @@ -1,12 +1,6 @@ -uart: - - id: uart_dsmr - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + request_pin: GPIO6 -dsmr: - decryption_key: 00112233445566778899aabbccddeeff - max_telegram_length: 1000 - request_pin: 6 - request_interval: 20s - receive_timeout: 100ms +<<: !include common.yaml From 06f9764f5180cc75dca186491fe24710a53b4942 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:24 -0600 Subject: [PATCH 0955/1052] [CI] Consolidate some tests (E) (#8191) --- tests/components/e131/common-ard.yaml | 14 ++++ tests/components/e131/common-idf.yaml | 13 ++++ tests/components/e131/common.yaml | 5 ++ tests/components/e131/test.esp32-ard.yaml | 21 ++---- tests/components/e131/test.esp32-c3-ard.yaml | 21 ++---- tests/components/e131/test.esp32-c3-idf.yaml | 20 ++---- tests/components/e131/test.esp32-idf.yaml | 20 ++---- tests/components/e131/test.esp8266-ard.yaml | 6 +- tests/components/e131/test.rp2040-ard.yaml | 6 +- tests/components/ee895/common.yaml | 14 ++++ tests/components/ee895/test.esp32-ard.yaml | 17 ++--- tests/components/ee895/test.esp32-c3-ard.yaml | 17 ++--- tests/components/ee895/test.esp32-c3-idf.yaml | 17 ++--- tests/components/ee895/test.esp32-idf.yaml | 17 ++--- tests/components/ee895/test.esp8266-ard.yaml | 17 ++--- tests/components/ee895/test.rp2040-ard.yaml | 17 ++--- tests/components/ektf2232/common.yaml | 24 +++++++ tests/components/ektf2232/test.esp32-ard.yaml | 30 ++------- .../ektf2232/test.esp32-c3-ard.yaml | 30 ++------- .../ektf2232/test.esp32-c3-idf.yaml | 30 ++------- tests/components/ektf2232/test.esp32-idf.yaml | 30 ++------- .../components/ektf2232/test.esp8266-ard.yaml | 30 ++------- .../components/ektf2232/test.rp2040-ard.yaml | 30 ++------- tests/components/emc2101/common.yaml | 25 ++++++++ tests/components/emc2101/test.esp32-ard.yaml | 28 ++------ .../components/emc2101/test.esp32-c3-ard.yaml | 28 ++------ .../components/emc2101/test.esp32-c3-idf.yaml | 28 ++------ tests/components/emc2101/test.esp32-idf.yaml | 28 ++------ .../components/emc2101/test.esp8266-ard.yaml | 28 ++------ tests/components/emc2101/test.rp2040-ard.yaml | 28 ++------ tests/components/ens210/common.yaml | 12 ++++ tests/components/ens210/test.esp32-ard.yaml | 15 ++--- .../components/ens210/test.esp32-c3-ard.yaml | 15 ++--- .../components/ens210/test.esp32-c3-idf.yaml | 15 ++--- tests/components/ens210/test.esp32-idf.yaml | 15 ++--- tests/components/ens210/test.esp8266-ard.yaml | 15 ++--- tests/components/ens210/test.rp2040-ard.yaml | 15 ++--- tests/components/esp32_can/common.yaml | 45 +++++++++++++ .../components/esp32_can/test.esp32-ard.yaml | 48 ++------------ .../esp32_can/test.esp32-c3-ard.yaml | 48 ++------------ .../esp32_can/test.esp32-c3-idf.yaml | 48 ++------------ .../components/esp32_can/test.esp32-idf.yaml | 48 ++------------ .../esp32_rmt_led_strip/common-ard.yaml | 18 ++++++ .../esp32_rmt_led_strip/common-idf.yaml | 16 +++++ .../esp32_rmt_led_strip/test.esp32-ard.yaml | 23 ++----- .../test.esp32-c3-ard.yaml | 23 ++----- .../test.esp32-c3-idf.yaml | 21 ++---- .../esp32_rmt_led_strip/test.esp32-idf.yaml | 21 ++---- tests/components/ezo/common.yaml | 10 +++ tests/components/ezo/test.esp32-ard.yaml | 13 ++-- tests/components/ezo/test.esp32-c3-ard.yaml | 13 ++-- tests/components/ezo/test.esp32-c3-idf.yaml | 13 ++-- tests/components/ezo/test.esp32-idf.yaml | 13 ++-- tests/components/ezo/test.esp8266-ard.yaml | 13 ++-- tests/components/ezo/test.rp2040-ard.yaml | 13 ++-- tests/components/ezo_pmp/common.yaml | 61 ++++++++++++++++++ tests/components/ezo_pmp/test.esp32-ard.yaml | 64 ++----------------- .../components/ezo_pmp/test.esp32-c3-ard.yaml | 64 ++----------------- .../components/ezo_pmp/test.esp32-c3-idf.yaml | 64 ++----------------- tests/components/ezo_pmp/test.esp32-idf.yaml | 64 ++----------------- .../components/ezo_pmp/test.esp8266-ard.yaml | 64 ++----------------- tests/components/ezo_pmp/test.rp2040-ard.yaml | 64 ++----------------- 62 files changed, 473 insertions(+), 1160 deletions(-) create mode 100644 tests/components/e131/common-ard.yaml create mode 100644 tests/components/e131/common-idf.yaml create mode 100644 tests/components/e131/common.yaml create mode 100644 tests/components/ee895/common.yaml create mode 100644 tests/components/ektf2232/common.yaml create mode 100644 tests/components/emc2101/common.yaml create mode 100644 tests/components/ens210/common.yaml create mode 100644 tests/components/esp32_can/common.yaml create mode 100644 tests/components/esp32_rmt_led_strip/common-ard.yaml create mode 100644 tests/components/esp32_rmt_led_strip/common-idf.yaml create mode 100644 tests/components/ezo/common.yaml create mode 100644 tests/components/ezo_pmp/common.yaml diff --git a/tests/components/e131/common-ard.yaml b/tests/components/e131/common-ard.yaml new file mode 100644 index 0000000000..418453d6ef --- /dev/null +++ b/tests/components/e131/common-ard.yaml @@ -0,0 +1,14 @@ +<<: !include common.yaml + +light: + - platform: ${light_platform} + id: led_matrix_32x8 + default_transition_length: 500ms + chipset: ws2812 + rgb_order: GRB + num_leds: 256 + pin: ${pin} + rmt_channel: 0 + effects: + - e131: + universe: 1 diff --git a/tests/components/e131/common-idf.yaml b/tests/components/e131/common-idf.yaml new file mode 100644 index 0000000000..8300dbb01b --- /dev/null +++ b/tests/components/e131/common-idf.yaml @@ -0,0 +1,13 @@ +<<: !include common.yaml + +light: + - platform: ${light_platform} + id: led_matrix_32x8 + default_transition_length: 500ms + chipset: ws2812 + rgb_order: GRB + num_leds: 256 + pin: ${pin} + effects: + - e131: + universe: 1 diff --git a/tests/components/e131/common.yaml b/tests/components/e131/common.yaml new file mode 100644 index 0000000000..55e42dbc5a --- /dev/null +++ b/tests/components/e131/common.yaml @@ -0,0 +1,5 @@ +wifi: + ssid: MySSID + password: password1 + +e131: diff --git a/tests/components/e131/test.esp32-ard.yaml b/tests/components/e131/test.esp32-ard.yaml index 25304cd3b4..32cf2a64ba 100644 --- a/tests/components/e131/test.esp32-ard.yaml +++ b/tests/components/e131/test.esp32-ard.yaml @@ -1,18 +1,5 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO2 -e131: - -light: - - platform: esp32_rmt_led_strip - id: led_matrix_32x8 - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - rmt_channel: 0 - effects: - - e131: - universe: 1 +<<: !include common-ard.yaml diff --git a/tests/components/e131/test.esp32-c3-ard.yaml b/tests/components/e131/test.esp32-c3-ard.yaml index 25304cd3b4..32cf2a64ba 100644 --- a/tests/components/e131/test.esp32-c3-ard.yaml +++ b/tests/components/e131/test.esp32-c3-ard.yaml @@ -1,18 +1,5 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO2 -e131: - -light: - - platform: esp32_rmt_led_strip - id: led_matrix_32x8 - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - rmt_channel: 0 - effects: - - e131: - universe: 1 +<<: !include common-ard.yaml diff --git a/tests/components/e131/test.esp32-c3-idf.yaml b/tests/components/e131/test.esp32-c3-idf.yaml index a27e62c1fb..d9dc4f6804 100644 --- a/tests/components/e131/test.esp32-c3-idf.yaml +++ b/tests/components/e131/test.esp32-c3-idf.yaml @@ -1,17 +1,5 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO2 -e131: - -light: - - platform: esp32_rmt_led_strip - id: led_matrix_32x8 - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - effects: - - e131: - universe: 1 +<<: !include common-idf.yaml diff --git a/tests/components/e131/test.esp32-idf.yaml b/tests/components/e131/test.esp32-idf.yaml index a27e62c1fb..d9dc4f6804 100644 --- a/tests/components/e131/test.esp32-idf.yaml +++ b/tests/components/e131/test.esp32-idf.yaml @@ -1,17 +1,5 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO2 -e131: - -light: - - platform: esp32_rmt_led_strip - id: led_matrix_32x8 - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - effects: - - e131: - universe: 1 +<<: !include common-idf.yaml diff --git a/tests/components/e131/test.esp8266-ard.yaml b/tests/components/e131/test.esp8266-ard.yaml index 54245014a5..277928626e 100644 --- a/tests/components/e131/test.esp8266-ard.yaml +++ b/tests/components/e131/test.esp8266-ard.yaml @@ -1,8 +1,4 @@ -wifi: - ssid: MySSID - password: password1 - -e131: +<<: !include common.yaml light: - platform: neopixelbus diff --git a/tests/components/e131/test.rp2040-ard.yaml b/tests/components/e131/test.rp2040-ard.yaml index 0ae31f5403..25fe3b6796 100644 --- a/tests/components/e131/test.rp2040-ard.yaml +++ b/tests/components/e131/test.rp2040-ard.yaml @@ -1,8 +1,4 @@ -wifi: - ssid: MySSID - password: password1 - -e131: +<<: !include common.yaml light: - platform: rp2040_pio_led_strip diff --git a/tests/components/ee895/common.yaml b/tests/components/ee895/common.yaml new file mode 100644 index 0000000000..63d77abcaf --- /dev/null +++ b/tests/components/ee895/common.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_ee895 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ee895 + address: 0x5F + co2: + name: EE895 CO2 + temperature: + name: EE895 Temperature + pressure: + name: EE895 Pressure diff --git a/tests/components/ee895/test.esp32-ard.yaml b/tests/components/ee895/test.esp32-ard.yaml index 241bdb9574..63c3bd6afd 100644 --- a/tests/components/ee895/test.esp32-ard.yaml +++ b/tests/components/ee895/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ee895 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ee895 - address: 0x5F - co2: - name: EE895 CO2 - temperature: - name: EE895 Temperature - pressure: - name: EE895 Pressure +<<: !include common.yaml diff --git a/tests/components/ee895/test.esp32-c3-ard.yaml b/tests/components/ee895/test.esp32-c3-ard.yaml index 4a36117fab..ee2c29ca4e 100644 --- a/tests/components/ee895/test.esp32-c3-ard.yaml +++ b/tests/components/ee895/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ee895 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ee895 - address: 0x5F - co2: - name: EE895 CO2 - temperature: - name: EE895 Temperature - pressure: - name: EE895 Pressure +<<: !include common.yaml diff --git a/tests/components/ee895/test.esp32-c3-idf.yaml b/tests/components/ee895/test.esp32-c3-idf.yaml index 4a36117fab..ee2c29ca4e 100644 --- a/tests/components/ee895/test.esp32-c3-idf.yaml +++ b/tests/components/ee895/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ee895 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ee895 - address: 0x5F - co2: - name: EE895 CO2 - temperature: - name: EE895 Temperature - pressure: - name: EE895 Pressure +<<: !include common.yaml diff --git a/tests/components/ee895/test.esp32-idf.yaml b/tests/components/ee895/test.esp32-idf.yaml index 241bdb9574..63c3bd6afd 100644 --- a/tests/components/ee895/test.esp32-idf.yaml +++ b/tests/components/ee895/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ee895 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ee895 - address: 0x5F - co2: - name: EE895 CO2 - temperature: - name: EE895 Temperature - pressure: - name: EE895 Pressure +<<: !include common.yaml diff --git a/tests/components/ee895/test.esp8266-ard.yaml b/tests/components/ee895/test.esp8266-ard.yaml index 4a36117fab..ee2c29ca4e 100644 --- a/tests/components/ee895/test.esp8266-ard.yaml +++ b/tests/components/ee895/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ee895 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ee895 - address: 0x5F - co2: - name: EE895 CO2 - temperature: - name: EE895 Temperature - pressure: - name: EE895 Pressure +<<: !include common.yaml diff --git a/tests/components/ee895/test.rp2040-ard.yaml b/tests/components/ee895/test.rp2040-ard.yaml index 4a36117fab..ee2c29ca4e 100644 --- a/tests/components/ee895/test.rp2040-ard.yaml +++ b/tests/components/ee895/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_ee895 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ee895 - address: 0x5F - co2: - name: EE895 CO2 - temperature: - name: EE895 Temperature - pressure: - name: EE895 Pressure +<<: !include common.yaml diff --git a/tests/components/ektf2232/common.yaml b/tests/components/ektf2232/common.yaml new file mode 100644 index 0000000000..3271839fd4 --- /dev/null +++ b/tests/components/ektf2232/common.yaml @@ -0,0 +1,24 @@ +i2c: + - id: i2c_ektf2232 + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: ektf2232 + interrupt_pin: ${interrupt_pin} + rts_pin: ${rts_pin} + display: ssd1306_display + on_touch: + - logger.log: + format: Touch at (%d, %d) + args: [touch.x, touch.y] diff --git a/tests/components/ektf2232/test.esp32-ard.yaml b/tests/components/ektf2232/test.esp32-ard.yaml index 9c6eef8bb3..b8f491c0c3 100644 --- a/tests/components/ektf2232/test.esp32-ard.yaml +++ b/tests/components/ektf2232/test.esp32-ard.yaml @@ -1,24 +1,8 @@ -i2c: - - id: i2c_ektf2232 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO13 + interrupt_pin: GPIO14 + rts_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ektf2232 - interrupt_pin: 14 - rts_pin: 15 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp32-c3-ard.yaml b/tests/components/ektf2232/test.esp32-c3-ard.yaml index 371f2795a2..9f2149b9d7 100644 --- a/tests/components/ektf2232/test.esp32-c3-ard.yaml +++ b/tests/components/ektf2232/test.esp32-c3-ard.yaml @@ -1,24 +1,8 @@ -i2c: - - id: i2c_ektf2232 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 + interrupt_pin: GPIO6 + rts_pin: GPIO7 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ektf2232 - interrupt_pin: 6 - rts_pin: 7 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp32-c3-idf.yaml b/tests/components/ektf2232/test.esp32-c3-idf.yaml index 371f2795a2..9f2149b9d7 100644 --- a/tests/components/ektf2232/test.esp32-c3-idf.yaml +++ b/tests/components/ektf2232/test.esp32-c3-idf.yaml @@ -1,24 +1,8 @@ -i2c: - - id: i2c_ektf2232 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 + interrupt_pin: GPIO6 + rts_pin: GPIO7 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ektf2232 - interrupt_pin: 6 - rts_pin: 7 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp32-idf.yaml b/tests/components/ektf2232/test.esp32-idf.yaml index 9c6eef8bb3..b8f491c0c3 100644 --- a/tests/components/ektf2232/test.esp32-idf.yaml +++ b/tests/components/ektf2232/test.esp32-idf.yaml @@ -1,24 +1,8 @@ -i2c: - - id: i2c_ektf2232 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO13 + interrupt_pin: GPIO14 + rts_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ektf2232 - interrupt_pin: 14 - rts_pin: 15 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ektf2232/test.esp8266-ard.yaml b/tests/components/ektf2232/test.esp8266-ard.yaml index 03f18f7184..6d91a6533f 100644 --- a/tests/components/ektf2232/test.esp8266-ard.yaml +++ b/tests/components/ektf2232/test.esp8266-ard.yaml @@ -1,24 +1,8 @@ -i2c: - - id: i2c_ektf2232 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 + interrupt_pin: GPIO12 + rts_pin: GPIO13 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ektf2232 - interrupt_pin: 12 - rts_pin: 13 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ektf2232/test.rp2040-ard.yaml b/tests/components/ektf2232/test.rp2040-ard.yaml index 371f2795a2..9f2149b9d7 100644 --- a/tests/components/ektf2232/test.rp2040-ard.yaml +++ b/tests/components/ektf2232/test.rp2040-ard.yaml @@ -1,24 +1,8 @@ -i2c: - - id: i2c_ektf2232 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 + interrupt_pin: GPIO6 + rts_pin: GPIO7 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ektf2232 - interrupt_pin: 6 - rts_pin: 7 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/emc2101/common.yaml b/tests/components/emc2101/common.yaml new file mode 100644 index 0000000000..795b02c6d3 --- /dev/null +++ b/tests/components/emc2101/common.yaml @@ -0,0 +1,25 @@ +i2c: + - id: i2c_emc2101 + scl: ${scl_pin} + sda: ${sda_pin} + +emc2101: + pwm: + resolution: 8 + +output: + - platform: emc2101 + id: fan_duty_cycle + +sensor: + - platform: emc2101 + internal_temperature: + id: internal_temperature_sensor + name: Internal Temperature Sensor + speed: + id: speed_sensor + name: Speed Sensor + duty_cycle: + id: duty_cycle_sensor + name: Duty Cycle Sensor + update_interval: 5s diff --git a/tests/components/emc2101/test.esp32-ard.yaml b/tests/components/emc2101/test.esp32-ard.yaml index 34a7d22b71..63c3bd6afd 100644 --- a/tests/components/emc2101/test.esp32-ard.yaml +++ b/tests/components/emc2101/test.esp32-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_emc2101 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -emc2101: - pwm: - resolution: 8 - -output: - - platform: emc2101 - id: fan_duty_cycle - -sensor: - - platform: emc2101 - internal_temperature: - id: internal_temperature_sensor - name: Internal Temperature Sensor - speed: - id: speed_sensor - name: Speed Sensor - duty_cycle: - id: duty_cycle_sensor - name: Duty Cycle Sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/emc2101/test.esp32-c3-ard.yaml b/tests/components/emc2101/test.esp32-c3-ard.yaml index d95bc1b001..ee2c29ca4e 100644 --- a/tests/components/emc2101/test.esp32-c3-ard.yaml +++ b/tests/components/emc2101/test.esp32-c3-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_emc2101 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -emc2101: - pwm: - resolution: 8 - -output: - - platform: emc2101 - id: fan_duty_cycle - -sensor: - - platform: emc2101 - internal_temperature: - id: internal_temperature_sensor - name: Internal Temperature Sensor - speed: - id: speed_sensor - name: Speed Sensor - duty_cycle: - id: duty_cycle_sensor - name: Duty Cycle Sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/emc2101/test.esp32-c3-idf.yaml b/tests/components/emc2101/test.esp32-c3-idf.yaml index d95bc1b001..ee2c29ca4e 100644 --- a/tests/components/emc2101/test.esp32-c3-idf.yaml +++ b/tests/components/emc2101/test.esp32-c3-idf.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_emc2101 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -emc2101: - pwm: - resolution: 8 - -output: - - platform: emc2101 - id: fan_duty_cycle - -sensor: - - platform: emc2101 - internal_temperature: - id: internal_temperature_sensor - name: Internal Temperature Sensor - speed: - id: speed_sensor - name: Speed Sensor - duty_cycle: - id: duty_cycle_sensor - name: Duty Cycle Sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/emc2101/test.esp32-idf.yaml b/tests/components/emc2101/test.esp32-idf.yaml index 34a7d22b71..63c3bd6afd 100644 --- a/tests/components/emc2101/test.esp32-idf.yaml +++ b/tests/components/emc2101/test.esp32-idf.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_emc2101 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -emc2101: - pwm: - resolution: 8 - -output: - - platform: emc2101 - id: fan_duty_cycle - -sensor: - - platform: emc2101 - internal_temperature: - id: internal_temperature_sensor - name: Internal Temperature Sensor - speed: - id: speed_sensor - name: Speed Sensor - duty_cycle: - id: duty_cycle_sensor - name: Duty Cycle Sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/emc2101/test.esp8266-ard.yaml b/tests/components/emc2101/test.esp8266-ard.yaml index d95bc1b001..ee2c29ca4e 100644 --- a/tests/components/emc2101/test.esp8266-ard.yaml +++ b/tests/components/emc2101/test.esp8266-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_emc2101 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -emc2101: - pwm: - resolution: 8 - -output: - - platform: emc2101 - id: fan_duty_cycle - -sensor: - - platform: emc2101 - internal_temperature: - id: internal_temperature_sensor - name: Internal Temperature Sensor - speed: - id: speed_sensor - name: Speed Sensor - duty_cycle: - id: duty_cycle_sensor - name: Duty Cycle Sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/emc2101/test.rp2040-ard.yaml b/tests/components/emc2101/test.rp2040-ard.yaml index d95bc1b001..ee2c29ca4e 100644 --- a/tests/components/emc2101/test.rp2040-ard.yaml +++ b/tests/components/emc2101/test.rp2040-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_emc2101 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -emc2101: - pwm: - resolution: 8 - -output: - - platform: emc2101 - id: fan_duty_cycle - -sensor: - - platform: emc2101 - internal_temperature: - id: internal_temperature_sensor - name: Internal Temperature Sensor - speed: - id: speed_sensor - name: Speed Sensor - duty_cycle: - id: duty_cycle_sensor - name: Duty Cycle Sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/ens210/common.yaml b/tests/components/ens210/common.yaml new file mode 100644 index 0000000000..b276154449 --- /dev/null +++ b/tests/components/ens210/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_ens210 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ens210 + temperature: + name: ENS210 Temperature + humidity: + name: ENS210 Humidity + update_interval: 15s diff --git a/tests/components/ens210/test.esp32-ard.yaml b/tests/components/ens210/test.esp32-ard.yaml index 8b2d29cc25..63c3bd6afd 100644 --- a/tests/components/ens210/test.esp32-ard.yaml +++ b/tests/components/ens210/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ens210 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ens210 - temperature: - name: ENS210 Temperature - humidity: - name: ENS210 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ens210/test.esp32-c3-ard.yaml b/tests/components/ens210/test.esp32-c3-ard.yaml index bacb71f9f8..ee2c29ca4e 100644 --- a/tests/components/ens210/test.esp32-c3-ard.yaml +++ b/tests/components/ens210/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ens210 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ens210 - temperature: - name: ENS210 Temperature - humidity: - name: ENS210 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ens210/test.esp32-c3-idf.yaml b/tests/components/ens210/test.esp32-c3-idf.yaml index bacb71f9f8..ee2c29ca4e 100644 --- a/tests/components/ens210/test.esp32-c3-idf.yaml +++ b/tests/components/ens210/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ens210 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ens210 - temperature: - name: ENS210 Temperature - humidity: - name: ENS210 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ens210/test.esp32-idf.yaml b/tests/components/ens210/test.esp32-idf.yaml index 8b2d29cc25..63c3bd6afd 100644 --- a/tests/components/ens210/test.esp32-idf.yaml +++ b/tests/components/ens210/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ens210 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ens210 - temperature: - name: ENS210 Temperature - humidity: - name: ENS210 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ens210/test.esp8266-ard.yaml b/tests/components/ens210/test.esp8266-ard.yaml index bacb71f9f8..ee2c29ca4e 100644 --- a/tests/components/ens210/test.esp8266-ard.yaml +++ b/tests/components/ens210/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ens210 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ens210 - temperature: - name: ENS210 Temperature - humidity: - name: ENS210 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ens210/test.rp2040-ard.yaml b/tests/components/ens210/test.rp2040-ard.yaml index bacb71f9f8..ee2c29ca4e 100644 --- a/tests/components/ens210/test.rp2040-ard.yaml +++ b/tests/components/ens210/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_ens210 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ens210 - temperature: - name: ENS210 Temperature - humidity: - name: ENS210 Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/esp32_can/common.yaml b/tests/components/esp32_can/common.yaml new file mode 100644 index 0000000000..4349c470f3 --- /dev/null +++ b/tests/components/esp32_can/common.yaml @@ -0,0 +1,45 @@ +esphome: + on_boot: + then: + - canbus.send: + # Extended ID explicit + use_extended_id: true + can_id: 0x100 + data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] + - canbus.send: + # Standard ID by default + can_id: 0x100 + data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] + +canbus: + - platform: esp32_can + id: esp32_internal_can + rx_pin: ${rx_pin} + tx_pin: ${tx_pin} + can_id: 4 + bit_rate: 50kbps + on_frame: + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("canid 500", "%s", b.c_str() ); + - can_id: 0b00000000000000000000001000000 + can_id_mask: 0b11111000000000011111111000000 + use_extended_id: true + then: + - lambda: |- + auto pdo_id = can_id >> 14; + switch (pdo_id) + { + case 117: + ESP_LOGD("canbus", "exhaust_fan_duty"); + break; + case 118: + ESP_LOGD("canbus", "supply_fan_duty"); + break; + case 119: + ESP_LOGD("canbus", "supply_fan_flow"); + break; + // to be continued... + } diff --git a/tests/components/esp32_can/test.esp32-ard.yaml b/tests/components/esp32_can/test.esp32-ard.yaml index 159a695853..811f6b72a6 100644 --- a/tests/components/esp32_can/test.esp32-ard.yaml +++ b/tests/components/esp32_can/test.esp32-ard.yaml @@ -1,45 +1,5 @@ -esphome: - on_boot: - then: - - canbus.send: - # Extended ID explicit - use_extended_id: true - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] - - canbus.send: - # Standard ID by default - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -canbus: - - platform: esp32_can - id: esp32_internal_can - rx_pin: 13 - tx_pin: 14 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("canid 500", "%s", b.c_str() ); - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/esp32_can/test.esp32-c3-ard.yaml b/tests/components/esp32_can/test.esp32-c3-ard.yaml index b4fd34cf51..c79d14c740 100644 --- a/tests/components/esp32_can/test.esp32-c3-ard.yaml +++ b/tests/components/esp32_can/test.esp32-c3-ard.yaml @@ -1,45 +1,5 @@ -esphome: - on_boot: - then: - - canbus.send: - # Extended ID explicit - use_extended_id: true - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] - - canbus.send: - # Standard ID by default - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -canbus: - - platform: esp32_can - id: esp32_internal_can - rx_pin: 4 - tx_pin: 5 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("canid 500", "%s", b.c_str() ); - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/esp32_can/test.esp32-c3-idf.yaml b/tests/components/esp32_can/test.esp32-c3-idf.yaml index b4fd34cf51..c79d14c740 100644 --- a/tests/components/esp32_can/test.esp32-c3-idf.yaml +++ b/tests/components/esp32_can/test.esp32-c3-idf.yaml @@ -1,45 +1,5 @@ -esphome: - on_boot: - then: - - canbus.send: - # Extended ID explicit - use_extended_id: true - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] - - canbus.send: - # Standard ID by default - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] +substitutions: + tx_pin: GPIO7 + rx_pin: GPIO8 -canbus: - - platform: esp32_can - id: esp32_internal_can - rx_pin: 4 - tx_pin: 5 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("canid 500", "%s", b.c_str() ); - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/esp32_can/test.esp32-idf.yaml b/tests/components/esp32_can/test.esp32-idf.yaml index 159a695853..811f6b72a6 100644 --- a/tests/components/esp32_can/test.esp32-idf.yaml +++ b/tests/components/esp32_can/test.esp32-idf.yaml @@ -1,45 +1,5 @@ -esphome: - on_boot: - then: - - canbus.send: - # Extended ID explicit - use_extended_id: true - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] - - canbus.send: - # Standard ID by default - can_id: 0x100 - data: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08] +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -canbus: - - platform: esp32_can - id: esp32_internal_can - rx_pin: 13 - tx_pin: 14 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("canid 500", "%s", b.c_str() ); - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/esp32_rmt_led_strip/common-ard.yaml b/tests/components/esp32_rmt_led_strip/common-ard.yaml new file mode 100644 index 0000000000..287690e86e --- /dev/null +++ b/tests/components/esp32_rmt_led_strip/common-ard.yaml @@ -0,0 +1,18 @@ +light: + - platform: esp32_rmt_led_strip + id: led_strip1 + pin: ${pin1} + num_leds: 60 + rmt_channel: 0 + rgb_order: GRB + chipset: ws2812 + - platform: esp32_rmt_led_strip + id: led_strip2 + pin: ${pin2} + num_leds: 60 + rmt_channel: 1 + rgb_order: RGB + bit0_high: 100us + bit0_low: 100us + bit1_high: 100us + bit1_low: 100us diff --git a/tests/components/esp32_rmt_led_strip/common-idf.yaml b/tests/components/esp32_rmt_led_strip/common-idf.yaml new file mode 100644 index 0000000000..f3ee86bcce --- /dev/null +++ b/tests/components/esp32_rmt_led_strip/common-idf.yaml @@ -0,0 +1,16 @@ +light: + - platform: esp32_rmt_led_strip + id: led_strip1 + pin: ${pin1} + num_leds: 60 + rgb_order: GRB + chipset: ws2812 + - platform: esp32_rmt_led_strip + id: led_strip2 + pin: ${pin2} + num_leds: 60 + rgb_order: RGB + bit0_high: 100us + bit0_low: 100us + bit1_high: 100us + bit1_low: 100us 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 d51a66451f..c75ac73ace 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-ard.yaml @@ -1,18 +1,5 @@ -light: - - platform: esp32_rmt_led_strip - id: led_strip - pin: 13 - num_leds: 60 - rmt_channel: 6 - rgb_order: GRB - chipset: ws2812 - - platform: esp32_rmt_led_strip - id: led_strip2 - pin: 14 - num_leds: 60 - rmt_channel: 2 - rgb_order: RGB - bit0_high: 100us - bit0_low: 100us - bit1_high: 100us - bit1_low: 100us +substitutions: + pin1: GPIO13 + pin2: GPIO14 + +<<: !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 b226d1de06..7b4560a0d7 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 @@ -1,18 +1,5 @@ -light: - - platform: esp32_rmt_led_strip - id: led_strip - pin: 4 - num_leds: 60 - rmt_channel: 0 - rgb_order: GRB - chipset: ws2812 - - platform: esp32_rmt_led_strip - id: led_strip2 - pin: 5 - num_leds: 60 - rmt_channel: 1 - rgb_order: RGB - bit0_high: 100us - bit0_low: 100us - bit1_high: 100us - bit1_low: 100us +substitutions: + pin1: GPIO3 + pin2: GPIO4 + +<<: !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 93318d0581..b4110199f1 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 @@ -1,16 +1,5 @@ -light: - - platform: esp32_rmt_led_strip - id: led_strip - pin: 4 - num_leds: 60 - rgb_order: GRB - chipset: ws2812 - - platform: esp32_rmt_led_strip - id: led_strip2 - pin: 5 - num_leds: 60 - rgb_order: RGB - bit0_high: 100µs - bit0_low: 100µs - bit1_high: 100µs - bit1_low: 100µs +substitutions: + pin1: GPIO3 + pin2: GPIO4 + +<<: !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 228af17189..5adeba4f8c 100644 --- a/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml +++ b/tests/components/esp32_rmt_led_strip/test.esp32-idf.yaml @@ -1,16 +1,5 @@ -light: - - platform: esp32_rmt_led_strip - id: led_strip - pin: 13 - num_leds: 60 - rgb_order: GRB - chipset: ws2812 - - platform: esp32_rmt_led_strip - id: led_strip2 - pin: 14 - num_leds: 60 - rgb_order: RGB - bit0_high: 100µs - bit0_low: 100µs - bit1_high: 100µs - bit1_low: 100µs +substitutions: + pin1: GPIO13 + pin2: GPIO14 + +<<: !include common-idf.yaml diff --git a/tests/components/ezo/common.yaml b/tests/components/ezo/common.yaml new file mode 100644 index 0000000000..edeebd1a12 --- /dev/null +++ b/tests/components/ezo/common.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_ezo + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ezo + id: ph_ezo + address: 99 + unit_of_measurement: pH diff --git a/tests/components/ezo/test.esp32-ard.yaml b/tests/components/ezo/test.esp32-ard.yaml index 61a8d2b25f..63c3bd6afd 100644 --- a/tests/components/ezo/test.esp32-ard.yaml +++ b/tests/components/ezo/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_ezo - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ezo - id: ph_ezo - address: 99 - unit_of_measurement: pH +<<: !include common.yaml diff --git a/tests/components/ezo/test.esp32-c3-ard.yaml b/tests/components/ezo/test.esp32-c3-ard.yaml index 93ceb9efd3..ee2c29ca4e 100644 --- a/tests/components/ezo/test.esp32-c3-ard.yaml +++ b/tests/components/ezo/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_ezo - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ezo - id: ph_ezo - address: 99 - unit_of_measurement: pH +<<: !include common.yaml diff --git a/tests/components/ezo/test.esp32-c3-idf.yaml b/tests/components/ezo/test.esp32-c3-idf.yaml index 93ceb9efd3..ee2c29ca4e 100644 --- a/tests/components/ezo/test.esp32-c3-idf.yaml +++ b/tests/components/ezo/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_ezo - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ezo - id: ph_ezo - address: 99 - unit_of_measurement: pH +<<: !include common.yaml diff --git a/tests/components/ezo/test.esp32-idf.yaml b/tests/components/ezo/test.esp32-idf.yaml index 61a8d2b25f..63c3bd6afd 100644 --- a/tests/components/ezo/test.esp32-idf.yaml +++ b/tests/components/ezo/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_ezo - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ezo - id: ph_ezo - address: 99 - unit_of_measurement: pH +<<: !include common.yaml diff --git a/tests/components/ezo/test.esp8266-ard.yaml b/tests/components/ezo/test.esp8266-ard.yaml index 93ceb9efd3..ee2c29ca4e 100644 --- a/tests/components/ezo/test.esp8266-ard.yaml +++ b/tests/components/ezo/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_ezo - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ezo - id: ph_ezo - address: 99 - unit_of_measurement: pH +<<: !include common.yaml diff --git a/tests/components/ezo/test.rp2040-ard.yaml b/tests/components/ezo/test.rp2040-ard.yaml index 93ceb9efd3..ee2c29ca4e 100644 --- a/tests/components/ezo/test.rp2040-ard.yaml +++ b/tests/components/ezo/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_ezo - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ezo - id: ph_ezo - address: 99 - unit_of_measurement: pH +<<: !include common.yaml diff --git a/tests/components/ezo_pmp/common.yaml b/tests/components/ezo_pmp/common.yaml new file mode 100644 index 0000000000..00919797be --- /dev/null +++ b/tests/components/ezo_pmp/common.yaml @@ -0,0 +1,61 @@ +i2c: + - id: i2c_ezo_pmp + scl: ${scl_pin} + sda: ${sda_pin} + +ezo_pmp: + id: hcl_pump + update_interval: 1s + +binary_sensor: + - platform: ezo_pmp + pump_state: + name: Pump State + is_paused: + name: Is Paused + +sensor: + - platform: ezo_pmp + current_volume_dosed: + name: Current Volume Dosed + total_volume_dosed: + name: Total Volume Dosed + absolute_total_volume_dosed: + name: Absolute Total Volume Dosed + pump_voltage: + name: Pump Voltage + last_volume_requested: + name: Last Volume Requested + max_flow_rate: + name: Max Flow Rate + +text_sensor: + - platform: ezo_pmp + dosing_mode: + name: Dosing Mode + calibration_status: + name: Calibration Status + on_value: + - ezo_pmp.dose_volume: + id: hcl_pump + volume: 10 + - ezo_pmp.dose_volume_over_time: + id: hcl_pump + volume: 10 + duration: 2 + - ezo_pmp.dose_with_constant_flow_rate: + id: hcl_pump + volume_per_minute: 10 + duration: 2 + - ezo_pmp.set_calibration_volume: + id: hcl_pump + volume: 10 + - ezo_pmp.find: hcl_pump + - ezo_pmp.dose_continuously: hcl_pump + - ezo_pmp.clear_total_volume_dosed: hcl_pump + - ezo_pmp.clear_calibration: hcl_pump + - ezo_pmp.pause_dosing: hcl_pump + - ezo_pmp.stop_dosing: hcl_pump + - ezo_pmp.arbitrary_command: + id: hcl_pump + command: D,? diff --git a/tests/components/ezo_pmp/test.esp32-ard.yaml b/tests/components/ezo_pmp/test.esp32-ard.yaml index 9fc929b365..63c3bd6afd 100644 --- a/tests/components/ezo_pmp/test.esp32-ard.yaml +++ b/tests/components/ezo_pmp/test.esp32-ard.yaml @@ -1,61 +1,5 @@ -i2c: - - id: i2c_ezo_pmp - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -ezo_pmp: - id: hcl_pump - update_interval: 1s - -binary_sensor: - - platform: ezo_pmp - pump_state: - name: Pump State - is_paused: - name: Is Paused - -sensor: - - platform: ezo_pmp - current_volume_dosed: - name: Current Volume Dosed - total_volume_dosed: - name: Total Volume Dosed - absolute_total_volume_dosed: - name: Absolute Total Volume Dosed - pump_voltage: - name: Pump Voltage - last_volume_requested: - name: Last Volume Requested - max_flow_rate: - name: Max Flow Rate - -text_sensor: - - platform: ezo_pmp - dosing_mode: - name: Dosing Mode - calibration_status: - name: Calibration Status - on_value: - - ezo_pmp.dose_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.dose_volume_over_time: - id: hcl_pump - volume: 10 - duration: 2 - - ezo_pmp.dose_with_constant_flow_rate: - id: hcl_pump - volume_per_minute: 10 - duration: 2 - - ezo_pmp.set_calibration_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.find: hcl_pump - - ezo_pmp.dose_continuously: hcl_pump - - ezo_pmp.clear_total_volume_dosed: hcl_pump - - ezo_pmp.clear_calibration: hcl_pump - - ezo_pmp.pause_dosing: hcl_pump - - ezo_pmp.stop_dosing: hcl_pump - - ezo_pmp.arbitrary_command: - id: hcl_pump - command: D,? +<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp32-c3-ard.yaml b/tests/components/ezo_pmp/test.esp32-c3-ard.yaml index fa047de3de..ee2c29ca4e 100644 --- a/tests/components/ezo_pmp/test.esp32-c3-ard.yaml +++ b/tests/components/ezo_pmp/test.esp32-c3-ard.yaml @@ -1,61 +1,5 @@ -i2c: - - id: i2c_ezo_pmp - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ezo_pmp: - id: hcl_pump - update_interval: 1s - -binary_sensor: - - platform: ezo_pmp - pump_state: - name: Pump State - is_paused: - name: Is Paused - -sensor: - - platform: ezo_pmp - current_volume_dosed: - name: Current Volume Dosed - total_volume_dosed: - name: Total Volume Dosed - absolute_total_volume_dosed: - name: Absolute Total Volume Dosed - pump_voltage: - name: Pump Voltage - last_volume_requested: - name: Last Volume Requested - max_flow_rate: - name: Max Flow Rate - -text_sensor: - - platform: ezo_pmp - dosing_mode: - name: Dosing Mode - calibration_status: - name: Calibration Status - on_value: - - ezo_pmp.dose_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.dose_volume_over_time: - id: hcl_pump - volume: 10 - duration: 2 - - ezo_pmp.dose_with_constant_flow_rate: - id: hcl_pump - volume_per_minute: 10 - duration: 2 - - ezo_pmp.set_calibration_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.find: hcl_pump - - ezo_pmp.dose_continuously: hcl_pump - - ezo_pmp.clear_total_volume_dosed: hcl_pump - - ezo_pmp.clear_calibration: hcl_pump - - ezo_pmp.pause_dosing: hcl_pump - - ezo_pmp.stop_dosing: hcl_pump - - ezo_pmp.arbitrary_command: - id: hcl_pump - command: D,? +<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp32-c3-idf.yaml b/tests/components/ezo_pmp/test.esp32-c3-idf.yaml index fa047de3de..ee2c29ca4e 100644 --- a/tests/components/ezo_pmp/test.esp32-c3-idf.yaml +++ b/tests/components/ezo_pmp/test.esp32-c3-idf.yaml @@ -1,61 +1,5 @@ -i2c: - - id: i2c_ezo_pmp - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ezo_pmp: - id: hcl_pump - update_interval: 1s - -binary_sensor: - - platform: ezo_pmp - pump_state: - name: Pump State - is_paused: - name: Is Paused - -sensor: - - platform: ezo_pmp - current_volume_dosed: - name: Current Volume Dosed - total_volume_dosed: - name: Total Volume Dosed - absolute_total_volume_dosed: - name: Absolute Total Volume Dosed - pump_voltage: - name: Pump Voltage - last_volume_requested: - name: Last Volume Requested - max_flow_rate: - name: Max Flow Rate - -text_sensor: - - platform: ezo_pmp - dosing_mode: - name: Dosing Mode - calibration_status: - name: Calibration Status - on_value: - - ezo_pmp.dose_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.dose_volume_over_time: - id: hcl_pump - volume: 10 - duration: 2 - - ezo_pmp.dose_with_constant_flow_rate: - id: hcl_pump - volume_per_minute: 10 - duration: 2 - - ezo_pmp.set_calibration_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.find: hcl_pump - - ezo_pmp.dose_continuously: hcl_pump - - ezo_pmp.clear_total_volume_dosed: hcl_pump - - ezo_pmp.clear_calibration: hcl_pump - - ezo_pmp.pause_dosing: hcl_pump - - ezo_pmp.stop_dosing: hcl_pump - - ezo_pmp.arbitrary_command: - id: hcl_pump - command: D,? +<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp32-idf.yaml b/tests/components/ezo_pmp/test.esp32-idf.yaml index 9fc929b365..63c3bd6afd 100644 --- a/tests/components/ezo_pmp/test.esp32-idf.yaml +++ b/tests/components/ezo_pmp/test.esp32-idf.yaml @@ -1,61 +1,5 @@ -i2c: - - id: i2c_ezo_pmp - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -ezo_pmp: - id: hcl_pump - update_interval: 1s - -binary_sensor: - - platform: ezo_pmp - pump_state: - name: Pump State - is_paused: - name: Is Paused - -sensor: - - platform: ezo_pmp - current_volume_dosed: - name: Current Volume Dosed - total_volume_dosed: - name: Total Volume Dosed - absolute_total_volume_dosed: - name: Absolute Total Volume Dosed - pump_voltage: - name: Pump Voltage - last_volume_requested: - name: Last Volume Requested - max_flow_rate: - name: Max Flow Rate - -text_sensor: - - platform: ezo_pmp - dosing_mode: - name: Dosing Mode - calibration_status: - name: Calibration Status - on_value: - - ezo_pmp.dose_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.dose_volume_over_time: - id: hcl_pump - volume: 10 - duration: 2 - - ezo_pmp.dose_with_constant_flow_rate: - id: hcl_pump - volume_per_minute: 10 - duration: 2 - - ezo_pmp.set_calibration_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.find: hcl_pump - - ezo_pmp.dose_continuously: hcl_pump - - ezo_pmp.clear_total_volume_dosed: hcl_pump - - ezo_pmp.clear_calibration: hcl_pump - - ezo_pmp.pause_dosing: hcl_pump - - ezo_pmp.stop_dosing: hcl_pump - - ezo_pmp.arbitrary_command: - id: hcl_pump - command: D,? +<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.esp8266-ard.yaml b/tests/components/ezo_pmp/test.esp8266-ard.yaml index fa047de3de..ee2c29ca4e 100644 --- a/tests/components/ezo_pmp/test.esp8266-ard.yaml +++ b/tests/components/ezo_pmp/test.esp8266-ard.yaml @@ -1,61 +1,5 @@ -i2c: - - id: i2c_ezo_pmp - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ezo_pmp: - id: hcl_pump - update_interval: 1s - -binary_sensor: - - platform: ezo_pmp - pump_state: - name: Pump State - is_paused: - name: Is Paused - -sensor: - - platform: ezo_pmp - current_volume_dosed: - name: Current Volume Dosed - total_volume_dosed: - name: Total Volume Dosed - absolute_total_volume_dosed: - name: Absolute Total Volume Dosed - pump_voltage: - name: Pump Voltage - last_volume_requested: - name: Last Volume Requested - max_flow_rate: - name: Max Flow Rate - -text_sensor: - - platform: ezo_pmp - dosing_mode: - name: Dosing Mode - calibration_status: - name: Calibration Status - on_value: - - ezo_pmp.dose_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.dose_volume_over_time: - id: hcl_pump - volume: 10 - duration: 2 - - ezo_pmp.dose_with_constant_flow_rate: - id: hcl_pump - volume_per_minute: 10 - duration: 2 - - ezo_pmp.set_calibration_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.find: hcl_pump - - ezo_pmp.dose_continuously: hcl_pump - - ezo_pmp.clear_total_volume_dosed: hcl_pump - - ezo_pmp.clear_calibration: hcl_pump - - ezo_pmp.pause_dosing: hcl_pump - - ezo_pmp.stop_dosing: hcl_pump - - ezo_pmp.arbitrary_command: - id: hcl_pump - command: D,? +<<: !include common.yaml diff --git a/tests/components/ezo_pmp/test.rp2040-ard.yaml b/tests/components/ezo_pmp/test.rp2040-ard.yaml index fa047de3de..ee2c29ca4e 100644 --- a/tests/components/ezo_pmp/test.rp2040-ard.yaml +++ b/tests/components/ezo_pmp/test.rp2040-ard.yaml @@ -1,61 +1,5 @@ -i2c: - - id: i2c_ezo_pmp - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ezo_pmp: - id: hcl_pump - update_interval: 1s - -binary_sensor: - - platform: ezo_pmp - pump_state: - name: Pump State - is_paused: - name: Is Paused - -sensor: - - platform: ezo_pmp - current_volume_dosed: - name: Current Volume Dosed - total_volume_dosed: - name: Total Volume Dosed - absolute_total_volume_dosed: - name: Absolute Total Volume Dosed - pump_voltage: - name: Pump Voltage - last_volume_requested: - name: Last Volume Requested - max_flow_rate: - name: Max Flow Rate - -text_sensor: - - platform: ezo_pmp - dosing_mode: - name: Dosing Mode - calibration_status: - name: Calibration Status - on_value: - - ezo_pmp.dose_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.dose_volume_over_time: - id: hcl_pump - volume: 10 - duration: 2 - - ezo_pmp.dose_with_constant_flow_rate: - id: hcl_pump - volume_per_minute: 10 - duration: 2 - - ezo_pmp.set_calibration_volume: - id: hcl_pump - volume: 10 - - ezo_pmp.find: hcl_pump - - ezo_pmp.dose_continuously: hcl_pump - - ezo_pmp.clear_total_volume_dosed: hcl_pump - - ezo_pmp.clear_calibration: hcl_pump - - ezo_pmp.pause_dosing: hcl_pump - - ezo_pmp.stop_dosing: hcl_pump - - ezo_pmp.arbitrary_command: - id: hcl_pump - command: D,? +<<: !include common.yaml From 6e3527a88b295874111e8ef2abf6cb0ed33bb442 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:35 -0600 Subject: [PATCH 0956/1052] [CI] Consolidate some tests (F) (#8195) --- tests/components/fingerprint_grow/common.yaml | 56 +++++++++++++++++ .../fingerprint_grow/test.esp32-ard.yaml | 60 ++----------------- .../fingerprint_grow/test.esp32-c3-ard.yaml | 60 ++----------------- .../fingerprint_grow/test.esp32-c3-idf.yaml | 60 ++----------------- .../fingerprint_grow/test.esp32-idf.yaml | 60 ++----------------- .../fingerprint_grow/test.esp8266-ard.yaml | 60 ++----------------- .../fingerprint_grow/test.rp2040-ard.yaml | 60 ++----------------- tests/components/fs3000/common.yaml | 10 ++++ tests/components/fs3000/test.esp32-ard.yaml | 13 ++-- .../components/fs3000/test.esp32-c3-ard.yaml | 13 ++-- .../components/fs3000/test.esp32-c3-idf.yaml | 13 ++-- tests/components/fs3000/test.esp32-idf.yaml | 13 ++-- tests/components/fs3000/test.esp8266-ard.yaml | 13 ++-- tests/components/fs3000/test.rp2040-ard.yaml | 13 ++-- tests/components/ft5x06/common.yaml | 21 +++++++ tests/components/ft5x06/test.esp32-ard.yaml | 25 ++------ .../components/ft5x06/test.esp32-c3-ard.yaml | 25 ++------ .../components/ft5x06/test.esp32-c3-idf.yaml | 25 ++------ tests/components/ft5x06/test.esp32-idf.yaml | 25 ++------ tests/components/ft5x06/test.esp8266-ard.yaml | 25 ++------ tests/components/ft5x06/test.rp2040-ard.yaml | 25 ++------ tests/components/ft63x6/common.yaml | 36 +++++++++++ tests/components/ft63x6/test.esp32-ard.yaml | 46 +++----------- .../components/ft63x6/test.esp32-c3-ard.yaml | 28 +++------ .../components/ft63x6/test.esp32-c3-idf.yaml | 28 +++------ tests/components/ft63x6/test.esp32-idf.yaml | 28 +++------ tests/components/ft63x6/test.esp8266-ard.yaml | 28 +++------ tests/components/ft63x6/test.rp2040-ard.yaml | 28 +++------ tests/components/fujitsu_general/common.yaml | 7 +++ .../fujitsu_general/test.esp32-ard.yaml | 9 +-- .../fujitsu_general/test.esp32-c3-ard.yaml | 9 +-- .../fujitsu_general/test.esp32-c3-idf.yaml | 9 +-- .../fujitsu_general/test.esp32-idf.yaml | 9 +-- .../fujitsu_general/test.esp8266-ard.yaml | 9 +-- 34 files changed, 277 insertions(+), 672 deletions(-) create mode 100644 tests/components/fingerprint_grow/common.yaml create mode 100644 tests/components/fs3000/common.yaml create mode 100644 tests/components/ft5x06/common.yaml create mode 100644 tests/components/ft63x6/common.yaml create mode 100644 tests/components/fujitsu_general/common.yaml diff --git a/tests/components/fingerprint_grow/common.yaml b/tests/components/fingerprint_grow/common.yaml new file mode 100644 index 0000000000..e759ea5a02 --- /dev/null +++ b/tests/components/fingerprint_grow/common.yaml @@ -0,0 +1,56 @@ +esphome: + on_boot: + then: + - fingerprint_grow.enroll: + finger_id: 2 + num_scans: 2 + - fingerprint_grow.cancel_enroll: + - fingerprint_grow.delete: + finger_id: 2 + - fingerprint_grow.delete_all: + +uart: + - id: uart_fingerprint_grow + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 57600 + +fingerprint_grow: + sensing_pin: ${sensing_pin} + password: 0x12FE37DC + new_password: 0xA65B9840 + on_finger_scan_start: + - logger.log: test_fingerprint_grow_finger_scan_start + on_finger_scan_invalid: + - logger.log: test_fingerprint_grow_finger_scan_invalid + on_finger_scan_matched: + - logger.log: test_fingerprint_grow_finger_scan_matched + on_finger_scan_unmatched: + - logger.log: test_fingerprint_grow_finger_scan_unmatched + on_finger_scan_misplaced: + - logger.log: test_fingerprint_grow_finger_scan_misplaced + on_enrollment_scan: + - logger.log: test_fingerprint_grow_enrollment_scan + on_enrollment_done: + - logger.log: test_fingerprint_grow_node_enrollment_done + on_enrollment_failed: + - logger.log: test_fingerprint_grow_enrollment_failed + +binary_sensor: + - platform: fingerprint_grow + name: Fingerprint Enrolling + +sensor: + - platform: fingerprint_grow + fingerprint_count: + name: Fingerprint Count + status: + name: Fingerprint Status + capacity: + name: Fingerprint Capacity + security_level: + name: Fingerprint Security Level + last_finger_id: + name: Fingerprint Last Finger ID + last_confidence: + name: Fingerprint Last Confidence diff --git a/tests/components/fingerprint_grow/test.esp32-ard.yaml b/tests/components/fingerprint_grow/test.esp32-ard.yaml index 0950145a05..4aef3d8be6 100644 --- a/tests/components/fingerprint_grow/test.esp32-ard.yaml +++ b/tests/components/fingerprint_grow/test.esp32-ard.yaml @@ -1,56 +1,6 @@ -esphome: - on_boot: - then: - - fingerprint_grow.enroll: - finger_id: 2 - num_scans: 2 - - fingerprint_grow.cancel_enroll: - - fingerprint_grow.delete: - finger_id: 2 - - fingerprint_grow.delete_all: +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + sensing_pin: GPIO15 -uart: - - id: uart_fingerprint_grow - tx_pin: 17 - rx_pin: 16 - baud_rate: 57600 - -fingerprint_grow: - sensing_pin: 18 - password: 0x12FE37DC - new_password: 0xA65B9840 - on_finger_scan_start: - - logger.log: test_fingerprint_grow_finger_scan_start - on_finger_scan_invalid: - - logger.log: test_fingerprint_grow_finger_scan_invalid - on_finger_scan_matched: - - logger.log: test_fingerprint_grow_finger_scan_matched - on_finger_scan_unmatched: - - logger.log: test_fingerprint_grow_finger_scan_unmatched - on_finger_scan_misplaced: - - logger.log: test_fingerprint_grow_finger_scan_misplaced - on_enrollment_scan: - - logger.log: test_fingerprint_grow_enrollment_scan - on_enrollment_done: - - logger.log: test_fingerprint_grow_node_enrollment_done - on_enrollment_failed: - - logger.log: test_fingerprint_grow_enrollment_failed - -binary_sensor: - - platform: fingerprint_grow - name: Fingerprint Enrolling - -sensor: - - platform: fingerprint_grow - fingerprint_count: - name: Fingerprint Count - status: - name: Fingerprint Status - capacity: - name: Fingerprint Capacity - security_level: - name: Fingerprint Security Level - last_finger_id: - name: Fingerprint Last Finger ID - last_confidence: - name: Fingerprint Last Confidence +<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp32-c3-ard.yaml b/tests/components/fingerprint_grow/test.esp32-c3-ard.yaml index e7ac08eb28..faab50e152 100644 --- a/tests/components/fingerprint_grow/test.esp32-c3-ard.yaml +++ b/tests/components/fingerprint_grow/test.esp32-c3-ard.yaml @@ -1,56 +1,6 @@ -esphome: - on_boot: - then: - - fingerprint_grow.enroll: - finger_id: 2 - num_scans: 2 - - fingerprint_grow.cancel_enroll: - - fingerprint_grow.delete: - finger_id: 2 - - fingerprint_grow.delete_all: +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + sensing_pin: GPIO6 -uart: - - id: uart_fingerprint_grow - tx_pin: 4 - rx_pin: 5 - baud_rate: 57600 - -fingerprint_grow: - sensing_pin: 6 - password: 0x12FE37DC - new_password: 0xA65B9840 - on_finger_scan_start: - - logger.log: test_fingerprint_grow_finger_scan_start - on_finger_scan_invalid: - - logger.log: test_fingerprint_grow_finger_scan_invalid - on_finger_scan_matched: - - logger.log: test_fingerprint_grow_finger_scan_matched - on_finger_scan_unmatched: - - logger.log: test_fingerprint_grow_finger_scan_unmatched - on_finger_scan_misplaced: - - logger.log: test_fingerprint_grow_finger_scan_misplaced - on_enrollment_scan: - - logger.log: test_fingerprint_grow_enrollment_scan - on_enrollment_done: - - logger.log: test_fingerprint_grow_node_enrollment_done - on_enrollment_failed: - - logger.log: test_fingerprint_grow_enrollment_failed - -binary_sensor: - - platform: fingerprint_grow - name: Fingerprint Enrolling - -sensor: - - platform: fingerprint_grow - fingerprint_count: - name: Fingerprint Count - status: - name: Fingerprint Status - capacity: - name: Fingerprint Capacity - security_level: - name: Fingerprint Security Level - last_finger_id: - name: Fingerprint Last Finger ID - last_confidence: - name: Fingerprint Last Confidence +<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml b/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml index e7ac08eb28..faab50e152 100644 --- a/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml +++ b/tests/components/fingerprint_grow/test.esp32-c3-idf.yaml @@ -1,56 +1,6 @@ -esphome: - on_boot: - then: - - fingerprint_grow.enroll: - finger_id: 2 - num_scans: 2 - - fingerprint_grow.cancel_enroll: - - fingerprint_grow.delete: - finger_id: 2 - - fingerprint_grow.delete_all: +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + sensing_pin: GPIO6 -uart: - - id: uart_fingerprint_grow - tx_pin: 4 - rx_pin: 5 - baud_rate: 57600 - -fingerprint_grow: - sensing_pin: 6 - password: 0x12FE37DC - new_password: 0xA65B9840 - on_finger_scan_start: - - logger.log: test_fingerprint_grow_finger_scan_start - on_finger_scan_invalid: - - logger.log: test_fingerprint_grow_finger_scan_invalid - on_finger_scan_matched: - - logger.log: test_fingerprint_grow_finger_scan_matched - on_finger_scan_unmatched: - - logger.log: test_fingerprint_grow_finger_scan_unmatched - on_finger_scan_misplaced: - - logger.log: test_fingerprint_grow_finger_scan_misplaced - on_enrollment_scan: - - logger.log: test_fingerprint_grow_enrollment_scan - on_enrollment_done: - - logger.log: test_fingerprint_grow_node_enrollment_done - on_enrollment_failed: - - logger.log: test_fingerprint_grow_enrollment_failed - -binary_sensor: - - platform: fingerprint_grow - name: Fingerprint Enrolling - -sensor: - - platform: fingerprint_grow - fingerprint_count: - name: Fingerprint Count - status: - name: Fingerprint Status - capacity: - name: Fingerprint Capacity - security_level: - name: Fingerprint Security Level - last_finger_id: - name: Fingerprint Last Finger ID - last_confidence: - name: Fingerprint Last Confidence +<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp32-idf.yaml b/tests/components/fingerprint_grow/test.esp32-idf.yaml index 0950145a05..4aef3d8be6 100644 --- a/tests/components/fingerprint_grow/test.esp32-idf.yaml +++ b/tests/components/fingerprint_grow/test.esp32-idf.yaml @@ -1,56 +1,6 @@ -esphome: - on_boot: - then: - - fingerprint_grow.enroll: - finger_id: 2 - num_scans: 2 - - fingerprint_grow.cancel_enroll: - - fingerprint_grow.delete: - finger_id: 2 - - fingerprint_grow.delete_all: +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + sensing_pin: GPIO15 -uart: - - id: uart_fingerprint_grow - tx_pin: 17 - rx_pin: 16 - baud_rate: 57600 - -fingerprint_grow: - sensing_pin: 18 - password: 0x12FE37DC - new_password: 0xA65B9840 - on_finger_scan_start: - - logger.log: test_fingerprint_grow_finger_scan_start - on_finger_scan_invalid: - - logger.log: test_fingerprint_grow_finger_scan_invalid - on_finger_scan_matched: - - logger.log: test_fingerprint_grow_finger_scan_matched - on_finger_scan_unmatched: - - logger.log: test_fingerprint_grow_finger_scan_unmatched - on_finger_scan_misplaced: - - logger.log: test_fingerprint_grow_finger_scan_misplaced - on_enrollment_scan: - - logger.log: test_fingerprint_grow_enrollment_scan - on_enrollment_done: - - logger.log: test_fingerprint_grow_node_enrollment_done - on_enrollment_failed: - - logger.log: test_fingerprint_grow_enrollment_failed - -binary_sensor: - - platform: fingerprint_grow - name: Fingerprint Enrolling - -sensor: - - platform: fingerprint_grow - fingerprint_count: - name: Fingerprint Count - status: - name: Fingerprint Status - capacity: - name: Fingerprint Capacity - security_level: - name: Fingerprint Security Level - last_finger_id: - name: Fingerprint Last Finger ID - last_confidence: - name: Fingerprint Last Confidence +<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.esp8266-ard.yaml b/tests/components/fingerprint_grow/test.esp8266-ard.yaml index 1d00d977b9..f2a864596a 100644 --- a/tests/components/fingerprint_grow/test.esp8266-ard.yaml +++ b/tests/components/fingerprint_grow/test.esp8266-ard.yaml @@ -1,56 +1,6 @@ -esphome: - on_boot: - then: - - fingerprint_grow.enroll: - finger_id: 2 - num_scans: 2 - - fingerprint_grow.cancel_enroll: - - fingerprint_grow.delete: - finger_id: 2 - - fingerprint_grow.delete_all: +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + sensing_pin: GPIO15 -uart: - - id: uart_fingerprint_grow - tx_pin: 4 - rx_pin: 5 - baud_rate: 57600 - -fingerprint_grow: - sensing_pin: 16 - password: 0x12FE37DC - new_password: 0xA65B9840 - on_finger_scan_start: - - logger.log: test_fingerprint_grow_finger_scan_start - on_finger_scan_invalid: - - logger.log: test_fingerprint_grow_finger_scan_invalid - on_finger_scan_matched: - - logger.log: test_fingerprint_grow_finger_scan_matched - on_finger_scan_unmatched: - - logger.log: test_fingerprint_grow_finger_scan_unmatched - on_finger_scan_misplaced: - - logger.log: test_fingerprint_grow_finger_scan_misplaced - on_enrollment_scan: - - logger.log: test_fingerprint_grow_enrollment_scan - on_enrollment_done: - - logger.log: test_fingerprint_grow_node_enrollment_done - on_enrollment_failed: - - logger.log: test_fingerprint_grow_enrollment_failed - -binary_sensor: - - platform: fingerprint_grow - name: Fingerprint Enrolling - -sensor: - - platform: fingerprint_grow - fingerprint_count: - name: Fingerprint Count - status: - name: Fingerprint Status - capacity: - name: Fingerprint Capacity - security_level: - name: Fingerprint Security Level - last_finger_id: - name: Fingerprint Last Finger ID - last_confidence: - name: Fingerprint Last Confidence +<<: !include common.yaml diff --git a/tests/components/fingerprint_grow/test.rp2040-ard.yaml b/tests/components/fingerprint_grow/test.rp2040-ard.yaml index e7ac08eb28..faab50e152 100644 --- a/tests/components/fingerprint_grow/test.rp2040-ard.yaml +++ b/tests/components/fingerprint_grow/test.rp2040-ard.yaml @@ -1,56 +1,6 @@ -esphome: - on_boot: - then: - - fingerprint_grow.enroll: - finger_id: 2 - num_scans: 2 - - fingerprint_grow.cancel_enroll: - - fingerprint_grow.delete: - finger_id: 2 - - fingerprint_grow.delete_all: +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + sensing_pin: GPIO6 -uart: - - id: uart_fingerprint_grow - tx_pin: 4 - rx_pin: 5 - baud_rate: 57600 - -fingerprint_grow: - sensing_pin: 6 - password: 0x12FE37DC - new_password: 0xA65B9840 - on_finger_scan_start: - - logger.log: test_fingerprint_grow_finger_scan_start - on_finger_scan_invalid: - - logger.log: test_fingerprint_grow_finger_scan_invalid - on_finger_scan_matched: - - logger.log: test_fingerprint_grow_finger_scan_matched - on_finger_scan_unmatched: - - logger.log: test_fingerprint_grow_finger_scan_unmatched - on_finger_scan_misplaced: - - logger.log: test_fingerprint_grow_finger_scan_misplaced - on_enrollment_scan: - - logger.log: test_fingerprint_grow_enrollment_scan - on_enrollment_done: - - logger.log: test_fingerprint_grow_node_enrollment_done - on_enrollment_failed: - - logger.log: test_fingerprint_grow_enrollment_failed - -binary_sensor: - - platform: fingerprint_grow - name: Fingerprint Enrolling - -sensor: - - platform: fingerprint_grow - fingerprint_count: - name: Fingerprint Count - status: - name: Fingerprint Status - capacity: - name: Fingerprint Capacity - security_level: - name: Fingerprint Security Level - last_finger_id: - name: Fingerprint Last Finger ID - last_confidence: - name: Fingerprint Last Confidence +<<: !include common.yaml diff --git a/tests/components/fs3000/common.yaml b/tests/components/fs3000/common.yaml new file mode 100644 index 0000000000..e87ac28aa9 --- /dev/null +++ b/tests/components/fs3000/common.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_fs3000 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: fs3000 + name: Air Velocity + model: 1005 + update_interval: 60s diff --git a/tests/components/fs3000/test.esp32-ard.yaml b/tests/components/fs3000/test.esp32-ard.yaml index 53b49cc9a2..63c3bd6afd 100644 --- a/tests/components/fs3000/test.esp32-ard.yaml +++ b/tests/components/fs3000/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_fs3000 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: fs3000 - name: Air Velocity - model: 1005 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/fs3000/test.esp32-c3-ard.yaml b/tests/components/fs3000/test.esp32-c3-ard.yaml index 69de83b463..ee2c29ca4e 100644 --- a/tests/components/fs3000/test.esp32-c3-ard.yaml +++ b/tests/components/fs3000/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_fs3000 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: fs3000 - name: Air Velocity - model: 1005 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/fs3000/test.esp32-c3-idf.yaml b/tests/components/fs3000/test.esp32-c3-idf.yaml index 69de83b463..ee2c29ca4e 100644 --- a/tests/components/fs3000/test.esp32-c3-idf.yaml +++ b/tests/components/fs3000/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_fs3000 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: fs3000 - name: Air Velocity - model: 1005 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/fs3000/test.esp32-idf.yaml b/tests/components/fs3000/test.esp32-idf.yaml index 53b49cc9a2..63c3bd6afd 100644 --- a/tests/components/fs3000/test.esp32-idf.yaml +++ b/tests/components/fs3000/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_fs3000 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: fs3000 - name: Air Velocity - model: 1005 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/fs3000/test.esp8266-ard.yaml b/tests/components/fs3000/test.esp8266-ard.yaml index 69de83b463..ee2c29ca4e 100644 --- a/tests/components/fs3000/test.esp8266-ard.yaml +++ b/tests/components/fs3000/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_fs3000 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: fs3000 - name: Air Velocity - model: 1005 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/fs3000/test.rp2040-ard.yaml b/tests/components/fs3000/test.rp2040-ard.yaml index 69de83b463..ee2c29ca4e 100644 --- a/tests/components/fs3000/test.rp2040-ard.yaml +++ b/tests/components/fs3000/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_fs3000 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: fs3000 - name: Air Velocity - model: 1005 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ft5x06/common.yaml b/tests/components/ft5x06/common.yaml new file mode 100644 index 0000000000..322ee88b6d --- /dev/null +++ b/tests/components/ft5x06/common.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_ft5x06 + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: ft5x06 + on_touch: + - logger.log: + format: Touch at (%d, %d) + args: [touch.x, touch.y] diff --git a/tests/components/ft5x06/test.esp32-ard.yaml b/tests/components/ft5x06/test.esp32-ard.yaml index 648929896d..1ca773e06c 100644 --- a/tests/components/ft5x06/test.esp32-ard.yaml +++ b/tests/components/ft5x06/test.esp32-ard.yaml @@ -1,21 +1,6 @@ -i2c: - - id: i2c_ft5x06 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 18 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft5x06 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp32-c3-ard.yaml b/tests/components/ft5x06/test.esp32-c3-ard.yaml index 4fa9532f53..1e6670c196 100644 --- a/tests/components/ft5x06/test.esp32-c3-ard.yaml +++ b/tests/components/ft5x06/test.esp32-c3-ard.yaml @@ -1,21 +1,6 @@ -i2c: - - id: i2c_ft5x06 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft5x06 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp32-c3-idf.yaml b/tests/components/ft5x06/test.esp32-c3-idf.yaml index 4fa9532f53..1e6670c196 100644 --- a/tests/components/ft5x06/test.esp32-c3-idf.yaml +++ b/tests/components/ft5x06/test.esp32-c3-idf.yaml @@ -1,21 +1,6 @@ -i2c: - - id: i2c_ft5x06 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft5x06 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp32-idf.yaml b/tests/components/ft5x06/test.esp32-idf.yaml index 648929896d..1ca773e06c 100644 --- a/tests/components/ft5x06/test.esp32-idf.yaml +++ b/tests/components/ft5x06/test.esp32-idf.yaml @@ -1,21 +1,6 @@ -i2c: - - id: i2c_ft5x06 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 18 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft5x06 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft5x06/test.esp8266-ard.yaml b/tests/components/ft5x06/test.esp8266-ard.yaml index 4fa9532f53..dfdc12a3d1 100644 --- a/tests/components/ft5x06/test.esp8266-ard.yaml +++ b/tests/components/ft5x06/test.esp8266-ard.yaml @@ -1,21 +1,6 @@ -i2c: - - id: i2c_ft5x06 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft5x06 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft5x06/test.rp2040-ard.yaml b/tests/components/ft5x06/test.rp2040-ard.yaml index 4fa9532f53..1e6670c196 100644 --- a/tests/components/ft5x06/test.rp2040-ard.yaml +++ b/tests/components/ft5x06/test.rp2040-ard.yaml @@ -1,21 +1,6 @@ -i2c: - - id: i2c_ft5x06 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft5x06 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft63x6/common.yaml b/tests/components/ft63x6/common.yaml new file mode 100644 index 0000000000..e91266123e --- /dev/null +++ b/tests/components/ft63x6/common.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_ft63x6 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +i2c: + - id: i2c_ft63x6 + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: ft63x6 + interrupt_pin: ${interrupt_pin} + transform: + swap_xy: true + mirror_x: false + mirror_y: true + on_touch: + - logger.log: + format: tp touched + on_update: + - logger.log: + format: to updated + on_release: + - logger.log: + format: to released diff --git a/tests/components/ft63x6/test.esp32-ard.yaml b/tests/components/ft63x6/test.esp32-ard.yaml index 5c43cdff45..47b5796e8b 100644 --- a/tests/components/ft63x6/test.esp32-ard.yaml +++ b/tests/components/ft63x6/test.esp32-ard.yaml @@ -1,39 +1,9 @@ -spi: - clk_pin: 14 - mosi_pin: 13 +substitutions: + clk_pin: GPIO0 + mosi_pin: GPIO2 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 -i2c: - sda: GPIO18 - scl: GPIO19 - -display: - - id: my_display - platform: ili9xxx - dimensions: 480x320 - model: ST7796 - cs_pin: 15 - dc_pin: 21 - reset_pin: 22 - transform: - swap_xy: true - mirror_x: true - mirror_y: true - auto_clear_enabled: false - invert_colors: false - -touchscreen: - - platform: ft63x6 - interrupt_pin: GPIO39 - transform: - swap_xy: true - mirror_x: false - mirror_y: true - on_touch: - - logger.log: - format: tp touched - on_update: - - logger.log: - format: to updated - on_release: - - logger.log: - format: to released +<<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp32-c3-ard.yaml b/tests/components/ft63x6/test.esp32-c3-ard.yaml index 19ca4cfc19..397ac1e464 100644 --- a/tests/components/ft63x6/test.esp32-c3-ard.yaml +++ b/tests/components/ft63x6/test.esp32-c3-ard.yaml @@ -1,21 +1,9 @@ -i2c: - - id: i2c_ft63x6 - scl: 5 - sda: 4 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft63x6 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp32-c3-idf.yaml b/tests/components/ft63x6/test.esp32-c3-idf.yaml index 19ca4cfc19..397ac1e464 100644 --- a/tests/components/ft63x6/test.esp32-c3-idf.yaml +++ b/tests/components/ft63x6/test.esp32-c3-idf.yaml @@ -1,21 +1,9 @@ -i2c: - - id: i2c_ft63x6 - scl: 5 - sda: 4 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft63x6 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp32-idf.yaml b/tests/components/ft63x6/test.esp32-idf.yaml index 5ceb107e31..47b5796e8b 100644 --- a/tests/components/ft63x6/test.esp32-idf.yaml +++ b/tests/components/ft63x6/test.esp32-idf.yaml @@ -1,21 +1,9 @@ -i2c: - - id: i2c_ft63x6 - scl: 16 - sda: 17 +substitutions: + clk_pin: GPIO0 + mosi_pin: GPIO2 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 18 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft63x6 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft63x6/test.esp8266-ard.yaml b/tests/components/ft63x6/test.esp8266-ard.yaml index 19ca4cfc19..a4223733af 100644 --- a/tests/components/ft63x6/test.esp8266-ard.yaml +++ b/tests/components/ft63x6/test.esp8266-ard.yaml @@ -1,21 +1,9 @@ -i2c: - - id: i2c_ft63x6 - scl: 5 - sda: 4 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + scl_pin: GPIO5 + sda_pin: GPIO4 + interrupt_pin: GPIO12 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft63x6 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ft63x6/test.rp2040-ard.yaml b/tests/components/ft63x6/test.rp2040-ard.yaml index 19ca4cfc19..397ac1e464 100644 --- a/tests/components/ft63x6/test.rp2040-ard.yaml +++ b/tests/components/ft63x6/test.rp2040-ard.yaml @@ -1,21 +1,9 @@ -i2c: - - id: i2c_ft63x6 - scl: 5 - sda: 4 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: ft63x6 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/fujitsu_general/common.yaml b/tests/components/fujitsu_general/common.yaml new file mode 100644 index 0000000000..3359b89f2a --- /dev/null +++ b/tests/components/fujitsu_general/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: fujitsu_general + name: Fujitsu General Climate diff --git a/tests/components/fujitsu_general/test.esp32-ard.yaml b/tests/components/fujitsu_general/test.esp32-ard.yaml index b4146f2a18..7b012aa64c 100644 --- a/tests/components/fujitsu_general/test.esp32-ard.yaml +++ b/tests/components/fujitsu_general/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: fujitsu_general - name: Fujitsu General Climate +<<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp32-c3-ard.yaml b/tests/components/fujitsu_general/test.esp32-c3-ard.yaml index b4146f2a18..7b012aa64c 100644 --- a/tests/components/fujitsu_general/test.esp32-c3-ard.yaml +++ b/tests/components/fujitsu_general/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: fujitsu_general - name: Fujitsu General Climate +<<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp32-c3-idf.yaml b/tests/components/fujitsu_general/test.esp32-c3-idf.yaml index b4146f2a18..7b012aa64c 100644 --- a/tests/components/fujitsu_general/test.esp32-c3-idf.yaml +++ b/tests/components/fujitsu_general/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: fujitsu_general - name: Fujitsu General Climate +<<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp32-idf.yaml b/tests/components/fujitsu_general/test.esp32-idf.yaml index b4146f2a18..7b012aa64c 100644 --- a/tests/components/fujitsu_general/test.esp32-idf.yaml +++ b/tests/components/fujitsu_general/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: fujitsu_general - name: Fujitsu General Climate +<<: !include common.yaml diff --git a/tests/components/fujitsu_general/test.esp8266-ard.yaml b/tests/components/fujitsu_general/test.esp8266-ard.yaml index 2a05bdde6b..f5097fcf5f 100644 --- a/tests/components/fujitsu_general/test.esp8266-ard.yaml +++ b/tests/components/fujitsu_general/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: fujitsu_general - name: Fujitsu General Climate +<<: !include common.yaml From 211aee91e53db1923611c422b3f5ae795ba06aee Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:45 -0600 Subject: [PATCH 0957/1052] [CI] Consolidate some tests (G) (#8196) --- tests/components/gcja5/common.yaml | 25 ++++ tests/components/gcja5/test.esp32-ard.yaml | 28 +--- tests/components/gcja5/test.esp32-c3-ard.yaml | 28 +--- tests/components/gcja5/test.esp32-c3-idf.yaml | 28 +--- tests/components/gcja5/test.esp32-idf.yaml | 28 +--- tests/components/gcja5/test.esp8266-ard.yaml | 28 +--- tests/components/gcja5/test.rp2040-ard.yaml | 28 +--- tests/components/gdk101/common.yaml | 7 +- tests/components/gdk101/test.esp32-ard.yaml | 4 +- tests/components/gdk101/test.esp32-idf.yaml | 4 +- tests/components/gdk101/test.esp8266-ard.yaml | 4 +- tests/components/gdk101/test.rp2040-ard.yaml | 4 +- tests/components/gp2y1010au0f/common.yaml | 15 +++ .../gp2y1010au0f/test.esp32-ard.yaml | 5 + .../gp2y1010au0f/test.esp32-c3-ard.yaml | 5 + .../gp2y1010au0f/test.esp32-c3-idf.yaml | 5 + .../gp2y1010au0f/test.esp32-idf.yaml | 19 +-- .../gp2y1010au0f/test.esp8266-ard.yaml | 5 + .../gp2y1010au0f/test.rp2040-ard.yaml | 5 + tests/components/gp8403/common.yaml | 20 +++ tests/components/gp8403/test.esp32-ard.yaml | 23 +--- .../components/gp8403/test.esp32-c3-ard.yaml | 23 +--- .../components/gp8403/test.esp32-c3-idf.yaml | 23 +--- tests/components/gp8403/test.esp32-idf.yaml | 23 +--- tests/components/gp8403/test.esp8266-ard.yaml | 23 +--- tests/components/gp8403/test.rp2040-ard.yaml | 23 +--- tests/components/gpio/common.yaml | 14 ++ tests/components/gpio/test.esp32-ard.yaml | 18 +-- tests/components/gpio/test.esp32-c3-ard.yaml | 18 +-- tests/components/gpio/test.esp32-c3-idf.yaml | 18 +-- tests/components/gpio/test.esp32-idf.yaml | 18 +-- tests/components/gpio/test.esp8266-ard.yaml | 18 +-- tests/components/gpio/test.rp2040-ard.yaml | 18 +-- tests/components/gps/common.yaml | 14 ++ tests/components/gps/test.esp32-ard.yaml | 17 +-- tests/components/gps/test.esp32-c3-ard.yaml | 17 +-- tests/components/gps/test.esp8266-ard.yaml | 17 +-- tests/components/gps/test.rp2040-ard.yaml | 17 +-- tests/components/graph/common.yaml | 25 ++++ tests/components/graph/test.esp32-ard.yaml | 29 +--- tests/components/graph/test.esp32-c3-ard.yaml | 29 +--- tests/components/graph/test.esp32-c3-idf.yaml | 29 +--- tests/components/graph/test.esp32-idf.yaml | 29 +--- tests/components/graph/test.esp8266-ard.yaml | 29 +--- tests/components/graph/test.rp2040-ard.yaml | 29 +--- .../graphical_display_menu/common.yaml | 120 +++++++++++++++++ .../test.esp32-ard.yaml | 124 +----------------- .../test.esp32-c3-ard.yaml | 124 +----------------- .../test.esp32-c3-idf.yaml | 124 +----------------- .../test.esp32-idf.yaml | 124 +----------------- .../test.esp8266-ard.yaml | 124 +----------------- .../test.rp2040-ard.yaml | 124 +----------------- tests/components/gree/common.yaml | 8 ++ tests/components/gree/test.esp32-ard.yaml | 10 +- tests/components/gree/test.esp32-c3-ard.yaml | 10 +- tests/components/gree/test.esp32-c3-idf.yaml | 10 +- tests/components/gree/test.esp32-idf.yaml | 10 +- tests/components/gree/test.esp8266-ard.yaml | 10 +- tests/components/grove_gas_mc_v2/common.yaml | 6 +- .../grove_gas_mc_v2/test.esp32-ard.yaml | 7 +- .../grove_gas_mc_v2/test.esp32-c3-ard.yaml | 5 + .../grove_gas_mc_v2/test.esp32-c3-idf.yaml | 5 + .../grove_gas_mc_v2/test.esp32-idf.yaml | 7 +- .../grove_gas_mc_v2/test.esp8266-ard.yaml | 7 +- .../grove_gas_mc_v2/test.rp2040-ard.yaml | 7 +- tests/components/grove_tb6612fng/common.yaml | 23 ++++ .../grove_tb6612fng/test.esp32-ard.yaml | 26 +--- .../grove_tb6612fng/test.esp32-c3-ard.yaml | 26 +--- .../grove_tb6612fng/test.esp32-c3-idf.yaml | 26 +--- .../grove_tb6612fng/test.esp32-idf.yaml | 26 +--- .../grove_tb6612fng/test.esp8266-ard.yaml | 26 +--- .../grove_tb6612fng/test.rp2040-ard.yaml | 26 +--- tests/components/growatt_solar/common.yaml | 62 +++++++++ .../growatt_solar/test.esp32-ard.yaml | 66 +--------- .../growatt_solar/test.esp32-c3-ard.yaml | 66 +--------- .../growatt_solar/test.esp32-c3-idf.yaml | 66 +--------- .../growatt_solar/test.esp32-idf.yaml | 66 +--------- .../growatt_solar/test.esp8266-ard.yaml | 66 +--------- .../growatt_solar/test.rp2040-ard.yaml | 66 +--------- 79 files changed, 616 insertions(+), 1823 deletions(-) create mode 100644 tests/components/gcja5/common.yaml create mode 100644 tests/components/gp2y1010au0f/common.yaml create mode 100644 tests/components/gp2y1010au0f/test.esp32-ard.yaml create mode 100644 tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml create mode 100644 tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml create mode 100644 tests/components/gp2y1010au0f/test.esp8266-ard.yaml create mode 100644 tests/components/gp2y1010au0f/test.rp2040-ard.yaml create mode 100644 tests/components/gp8403/common.yaml create mode 100644 tests/components/gpio/common.yaml create mode 100644 tests/components/gps/common.yaml create mode 100644 tests/components/graph/common.yaml create mode 100644 tests/components/graphical_display_menu/common.yaml create mode 100644 tests/components/gree/common.yaml create mode 100644 tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml create mode 100644 tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml create mode 100644 tests/components/grove_tb6612fng/common.yaml create mode 100644 tests/components/growatt_solar/common.yaml diff --git a/tests/components/gcja5/common.yaml b/tests/components/gcja5/common.yaml new file mode 100644 index 0000000000..8f6250045d --- /dev/null +++ b/tests/components/gcja5/common.yaml @@ -0,0 +1,25 @@ +uart: + - id: uart_gcja5 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN + +sensor: + - platform: gcja5 + pm_1_0: + name: "Particulate Matter <1.0µm Concentration" + pm_2_5: + name: "Particulate Matter <2.5µm Concentration" + pm_10_0: + name: "Particulate Matter <10.0µm Concentration" + pmc_0_5: + name: "PMC 0.5" + pmc_1_0: + name: "PMC 1.0" + pmc_2_5: + name: "PMC 2.5" + pmc_5_0: + name: "PMC 5.0" + pmc_10_0: + name: "PMC 10.0" diff --git a/tests/components/gcja5/test.esp32-ard.yaml b/tests/components/gcja5/test.esp32-ard.yaml index bc0f89eb9e..811f6b72a6 100644 --- a/tests/components/gcja5/test.esp32-ard.yaml +++ b/tests/components/gcja5/test.esp32-ard.yaml @@ -1,25 +1,5 @@ -uart: - - id: uart_gcja5 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: gcja5 - pm_1_0: - name: "Particulate Matter <1.0µm Concentration" - pm_2_5: - name: "Particulate Matter <2.5µm Concentration" - pm_10_0: - name: "Particulate Matter <10.0µm Concentration" - pmc_0_5: - name: "PMC 0.5" - pmc_1_0: - name: "PMC 1.0" - pmc_2_5: - name: "PMC 2.5" - pmc_5_0: - name: "PMC 5.0" - pmc_10_0: - name: "PMC 10.0" +<<: !include common.yaml diff --git a/tests/components/gcja5/test.esp32-c3-ard.yaml b/tests/components/gcja5/test.esp32-c3-ard.yaml index ec8765be52..b516342f3b 100644 --- a/tests/components/gcja5/test.esp32-c3-ard.yaml +++ b/tests/components/gcja5/test.esp32-c3-ard.yaml @@ -1,25 +1,5 @@ -uart: - - id: uart_gcja5 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: gcja5 - pm_1_0: - name: "Particulate Matter <1.0µm Concentration" - pm_2_5: - name: "Particulate Matter <2.5µm Concentration" - pm_10_0: - name: "Particulate Matter <10.0µm Concentration" - pmc_0_5: - name: "PMC 0.5" - pmc_1_0: - name: "PMC 1.0" - pmc_2_5: - name: "PMC 2.5" - pmc_5_0: - name: "PMC 5.0" - pmc_10_0: - name: "PMC 10.0" +<<: !include common.yaml diff --git a/tests/components/gcja5/test.esp32-c3-idf.yaml b/tests/components/gcja5/test.esp32-c3-idf.yaml index ec8765be52..b516342f3b 100644 --- a/tests/components/gcja5/test.esp32-c3-idf.yaml +++ b/tests/components/gcja5/test.esp32-c3-idf.yaml @@ -1,25 +1,5 @@ -uart: - - id: uart_gcja5 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: gcja5 - pm_1_0: - name: "Particulate Matter <1.0µm Concentration" - pm_2_5: - name: "Particulate Matter <2.5µm Concentration" - pm_10_0: - name: "Particulate Matter <10.0µm Concentration" - pmc_0_5: - name: "PMC 0.5" - pmc_1_0: - name: "PMC 1.0" - pmc_2_5: - name: "PMC 2.5" - pmc_5_0: - name: "PMC 5.0" - pmc_10_0: - name: "PMC 10.0" +<<: !include common.yaml diff --git a/tests/components/gcja5/test.esp32-idf.yaml b/tests/components/gcja5/test.esp32-idf.yaml index bc0f89eb9e..811f6b72a6 100644 --- a/tests/components/gcja5/test.esp32-idf.yaml +++ b/tests/components/gcja5/test.esp32-idf.yaml @@ -1,25 +1,5 @@ -uart: - - id: uart_gcja5 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: gcja5 - pm_1_0: - name: "Particulate Matter <1.0µm Concentration" - pm_2_5: - name: "Particulate Matter <2.5µm Concentration" - pm_10_0: - name: "Particulate Matter <10.0µm Concentration" - pmc_0_5: - name: "PMC 0.5" - pmc_1_0: - name: "PMC 1.0" - pmc_2_5: - name: "PMC 2.5" - pmc_5_0: - name: "PMC 5.0" - pmc_10_0: - name: "PMC 10.0" +<<: !include common.yaml diff --git a/tests/components/gcja5/test.esp8266-ard.yaml b/tests/components/gcja5/test.esp8266-ard.yaml index ec8765be52..b516342f3b 100644 --- a/tests/components/gcja5/test.esp8266-ard.yaml +++ b/tests/components/gcja5/test.esp8266-ard.yaml @@ -1,25 +1,5 @@ -uart: - - id: uart_gcja5 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: gcja5 - pm_1_0: - name: "Particulate Matter <1.0µm Concentration" - pm_2_5: - name: "Particulate Matter <2.5µm Concentration" - pm_10_0: - name: "Particulate Matter <10.0µm Concentration" - pmc_0_5: - name: "PMC 0.5" - pmc_1_0: - name: "PMC 1.0" - pmc_2_5: - name: "PMC 2.5" - pmc_5_0: - name: "PMC 5.0" - pmc_10_0: - name: "PMC 10.0" +<<: !include common.yaml diff --git a/tests/components/gcja5/test.rp2040-ard.yaml b/tests/components/gcja5/test.rp2040-ard.yaml index ec8765be52..b516342f3b 100644 --- a/tests/components/gcja5/test.rp2040-ard.yaml +++ b/tests/components/gcja5/test.rp2040-ard.yaml @@ -1,25 +1,5 @@ -uart: - - id: uart_gcja5 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: gcja5 - pm_1_0: - name: "Particulate Matter <1.0µm Concentration" - pm_2_5: - name: "Particulate Matter <2.5µm Concentration" - pm_10_0: - name: "Particulate Matter <10.0µm Concentration" - pmc_0_5: - name: "PMC 0.5" - pmc_1_0: - name: "PMC 1.0" - pmc_2_5: - name: "PMC 2.5" - pmc_5_0: - name: "PMC 5.0" - pmc_10_0: - name: "PMC 10.0" +<<: !include common.yaml diff --git a/tests/components/gdk101/common.yaml b/tests/components/gdk101/common.yaml index f886fc415b..7805ad43f0 100644 --- a/tests/components/gdk101/common.yaml +++ b/tests/components/gdk101/common.yaml @@ -1,11 +1,10 @@ i2c: - id: i2c_bus - sda: ${i2c_sda} - scl: ${i2c_scl} + - id: i2c_gdk101 + scl: ${scl_pin} + sda: ${sda_pin} gdk101: id: my_gdk101 - i2c_id: i2c_bus sensor: - platform: gdk101 diff --git a/tests/components/gdk101/test.esp32-ard.yaml b/tests/components/gdk101/test.esp32-ard.yaml index 1037d5d35b..63c3bd6afd 100644 --- a/tests/components/gdk101/test.esp32-ard.yaml +++ b/tests/components/gdk101/test.esp32-ard.yaml @@ -1,5 +1,5 @@ substitutions: - i2c_scl: GPIO16 - i2c_sda: GPIO17 + scl_pin: GPIO16 + sda_pin: GPIO17 <<: !include common.yaml diff --git a/tests/components/gdk101/test.esp32-idf.yaml b/tests/components/gdk101/test.esp32-idf.yaml index 1037d5d35b..63c3bd6afd 100644 --- a/tests/components/gdk101/test.esp32-idf.yaml +++ b/tests/components/gdk101/test.esp32-idf.yaml @@ -1,5 +1,5 @@ substitutions: - i2c_scl: GPIO16 - i2c_sda: GPIO17 + scl_pin: GPIO16 + sda_pin: GPIO17 <<: !include common.yaml diff --git a/tests/components/gdk101/test.esp8266-ard.yaml b/tests/components/gdk101/test.esp8266-ard.yaml index d7ae0d5161..ee2c29ca4e 100644 --- a/tests/components/gdk101/test.esp8266-ard.yaml +++ b/tests/components/gdk101/test.esp8266-ard.yaml @@ -1,5 +1,5 @@ substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/gdk101/test.rp2040-ard.yaml b/tests/components/gdk101/test.rp2040-ard.yaml index d7ae0d5161..ee2c29ca4e 100644 --- a/tests/components/gdk101/test.rp2040-ard.yaml +++ b/tests/components/gdk101/test.rp2040-ard.yaml @@ -1,5 +1,5 @@ substitutions: - i2c_scl: GPIO5 - i2c_sda: GPIO4 + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/common.yaml b/tests/components/gp2y1010au0f/common.yaml new file mode 100644 index 0000000000..95112fa712 --- /dev/null +++ b/tests/components/gp2y1010au0f/common.yaml @@ -0,0 +1,15 @@ +sensor: + - platform: adc + id: adc_sensor + pin: ${adc_pin} + - platform: gp2y1010au0f + sensor: adc_sensor + name: Dust Sensor + adc_voltage_offset: 0.2 + adc_voltage_multiplier: 3.3 + output: dust_sensor_led + +output: + - platform: gpio + id: dust_sensor_led + pin: ${output_pin} diff --git a/tests/components/gp2y1010au0f/test.esp32-ard.yaml b/tests/components/gp2y1010au0f/test.esp32-ard.yaml new file mode 100644 index 0000000000..d9494a95b7 --- /dev/null +++ b/tests/components/gp2y1010au0f/test.esp32-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + adc_pin: A0 + output_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml b/tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..0e331c893c --- /dev/null +++ b/tests/components/gp2y1010au0f/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + adc_pin: GPIO0 + output_pin: GPIO1 + +<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml b/tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..0e331c893c --- /dev/null +++ b/tests/components/gp2y1010au0f/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + adc_pin: GPIO0 + output_pin: GPIO1 + +<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp32-idf.yaml b/tests/components/gp2y1010au0f/test.esp32-idf.yaml index eb5ad0ea67..d9494a95b7 100644 --- a/tests/components/gp2y1010au0f/test.esp32-idf.yaml +++ b/tests/components/gp2y1010au0f/test.esp32-idf.yaml @@ -1,16 +1,5 @@ -sensor: - - platform: adc - pin: GPIO36 - id: adc_sensor +substitutions: + adc_pin: A0 + output_pin: GPIO14 - - platform: gp2y1010au0f - sensor: adc_sensor - name: Dust Sensor - adc_voltage_offset: 0.2 - adc_voltage_multiplier: 3.3 - output: dust_sensor_led - -output: - - platform: gpio - id: dust_sensor_led - pin: GPIO32 +<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.esp8266-ard.yaml b/tests/components/gp2y1010au0f/test.esp8266-ard.yaml new file mode 100644 index 0000000000..a61053426a --- /dev/null +++ b/tests/components/gp2y1010au0f/test.esp8266-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + adc_pin: A0 + output_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/gp2y1010au0f/test.rp2040-ard.yaml b/tests/components/gp2y1010au0f/test.rp2040-ard.yaml new file mode 100644 index 0000000000..e00f19941c --- /dev/null +++ b/tests/components/gp2y1010au0f/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + adc_pin: GPIO26 + output_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/gp8403/common.yaml b/tests/components/gp8403/common.yaml new file mode 100644 index 0000000000..7074185273 --- /dev/null +++ b/tests/components/gp8403/common.yaml @@ -0,0 +1,20 @@ +i2c: + - id: i2c_gp8403 + scl: ${scl_pin} + sda: ${sda_pin} + +gp8403: + - id: gp8403_5v + voltage: 5V + - id: gp8403_10v + voltage: 10V + +output: + - platform: gp8403 + id: gp8403_output_0 + gp8403_id: gp8403_5v + channel: 0 + - platform: gp8403 + gp8403_id: gp8403_10v + id: gp8403_output_1 + channel: 1 diff --git a/tests/components/gp8403/test.esp32-ard.yaml b/tests/components/gp8403/test.esp32-ard.yaml index 8470a303e1..63c3bd6afd 100644 --- a/tests/components/gp8403/test.esp32-ard.yaml +++ b/tests/components/gp8403/test.esp32-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_gp8403 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -gp8403: - - id: gp8403_5v - voltage: 5V - - id: gp8403_10v - voltage: 10V - -output: - - platform: gp8403 - id: gp8403_output_0 - gp8403_id: gp8403_5v - channel: 0 - - platform: gp8403 - gp8403_id: gp8403_10v - id: gp8403_output_1 - channel: 1 +<<: !include common.yaml diff --git a/tests/components/gp8403/test.esp32-c3-ard.yaml b/tests/components/gp8403/test.esp32-c3-ard.yaml index fbc40b948b..ee2c29ca4e 100644 --- a/tests/components/gp8403/test.esp32-c3-ard.yaml +++ b/tests/components/gp8403/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_gp8403 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -gp8403: - - id: gp8403_5v - voltage: 5V - - id: gp8403_10v - voltage: 10V - -output: - - platform: gp8403 - id: gp8403_output_0 - gp8403_id: gp8403_5v - channel: 0 - - platform: gp8403 - gp8403_id: gp8403_10v - id: gp8403_output_1 - channel: 1 +<<: !include common.yaml diff --git a/tests/components/gp8403/test.esp32-c3-idf.yaml b/tests/components/gp8403/test.esp32-c3-idf.yaml index fbc40b948b..ee2c29ca4e 100644 --- a/tests/components/gp8403/test.esp32-c3-idf.yaml +++ b/tests/components/gp8403/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_gp8403 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -gp8403: - - id: gp8403_5v - voltage: 5V - - id: gp8403_10v - voltage: 10V - -output: - - platform: gp8403 - id: gp8403_output_0 - gp8403_id: gp8403_5v - channel: 0 - - platform: gp8403 - gp8403_id: gp8403_10v - id: gp8403_output_1 - channel: 1 +<<: !include common.yaml diff --git a/tests/components/gp8403/test.esp32-idf.yaml b/tests/components/gp8403/test.esp32-idf.yaml index 8470a303e1..63c3bd6afd 100644 --- a/tests/components/gp8403/test.esp32-idf.yaml +++ b/tests/components/gp8403/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_gp8403 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -gp8403: - - id: gp8403_5v - voltage: 5V - - id: gp8403_10v - voltage: 10V - -output: - - platform: gp8403 - id: gp8403_output_0 - gp8403_id: gp8403_5v - channel: 0 - - platform: gp8403 - gp8403_id: gp8403_10v - id: gp8403_output_1 - channel: 1 +<<: !include common.yaml diff --git a/tests/components/gp8403/test.esp8266-ard.yaml b/tests/components/gp8403/test.esp8266-ard.yaml index fbc40b948b..ee2c29ca4e 100644 --- a/tests/components/gp8403/test.esp8266-ard.yaml +++ b/tests/components/gp8403/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_gp8403 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -gp8403: - - id: gp8403_5v - voltage: 5V - - id: gp8403_10v - voltage: 10V - -output: - - platform: gp8403 - id: gp8403_output_0 - gp8403_id: gp8403_5v - channel: 0 - - platform: gp8403 - gp8403_id: gp8403_10v - id: gp8403_output_1 - channel: 1 +<<: !include common.yaml diff --git a/tests/components/gp8403/test.rp2040-ard.yaml b/tests/components/gp8403/test.rp2040-ard.yaml index fbc40b948b..ee2c29ca4e 100644 --- a/tests/components/gp8403/test.rp2040-ard.yaml +++ b/tests/components/gp8403/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_gp8403 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -gp8403: - - id: gp8403_5v - voltage: 5V - - id: gp8403_10v - voltage: 10V - -output: - - platform: gp8403 - id: gp8403_output_0 - gp8403_id: gp8403_5v - channel: 0 - - platform: gp8403 - gp8403_id: gp8403_10v - id: gp8403_output_1 - channel: 1 +<<: !include common.yaml diff --git a/tests/components/gpio/common.yaml b/tests/components/gpio/common.yaml new file mode 100644 index 0000000000..4e237349d9 --- /dev/null +++ b/tests/components/gpio/common.yaml @@ -0,0 +1,14 @@ +binary_sensor: + - platform: gpio + pin: ${binary_sensor_pin} + id: gpio_binary_sensor + +output: + - platform: gpio + pin: ${output_pin} + id: gpio_output + +switch: + - platform: gpio + pin: ${switch_pin} + id: gpio_switch diff --git a/tests/components/gpio/test.esp32-ard.yaml b/tests/components/gpio/test.esp32-ard.yaml index 30dfa94b68..09f41abb79 100644 --- a/tests/components/gpio/test.esp32-ard.yaml +++ b/tests/components/gpio/test.esp32-ard.yaml @@ -1,14 +1,6 @@ -binary_sensor: - - platform: gpio - pin: 12 - id: gpio_binary_sensor +substitutions: + binary_sensor_pin: GPIO12 + output_pin: GPIO13 + switch_pin: GPIO14 -output: - - platform: gpio - pin: 13 - id: gpio_output - -switch: - - platform: gpio - pin: 14 - id: gpio_switch +<<: !include common.yaml diff --git a/tests/components/gpio/test.esp32-c3-ard.yaml b/tests/components/gpio/test.esp32-c3-ard.yaml index 3ca285117d..fc7c9942d0 100644 --- a/tests/components/gpio/test.esp32-c3-ard.yaml +++ b/tests/components/gpio/test.esp32-c3-ard.yaml @@ -1,14 +1,6 @@ -binary_sensor: - - platform: gpio - pin: 2 - id: gpio_binary_sensor +substitutions: + binary_sensor_pin: GPIO2 + output_pin: GPIO3 + switch_pin: GPIO4 -output: - - platform: gpio - pin: 3 - id: gpio_output - -switch: - - platform: gpio - pin: 4 - id: gpio_switch +<<: !include common.yaml diff --git a/tests/components/gpio/test.esp32-c3-idf.yaml b/tests/components/gpio/test.esp32-c3-idf.yaml index 3ca285117d..fc7c9942d0 100644 --- a/tests/components/gpio/test.esp32-c3-idf.yaml +++ b/tests/components/gpio/test.esp32-c3-idf.yaml @@ -1,14 +1,6 @@ -binary_sensor: - - platform: gpio - pin: 2 - id: gpio_binary_sensor +substitutions: + binary_sensor_pin: GPIO2 + output_pin: GPIO3 + switch_pin: GPIO4 -output: - - platform: gpio - pin: 3 - id: gpio_output - -switch: - - platform: gpio - pin: 4 - id: gpio_switch +<<: !include common.yaml diff --git a/tests/components/gpio/test.esp32-idf.yaml b/tests/components/gpio/test.esp32-idf.yaml index 30dfa94b68..09f41abb79 100644 --- a/tests/components/gpio/test.esp32-idf.yaml +++ b/tests/components/gpio/test.esp32-idf.yaml @@ -1,14 +1,6 @@ -binary_sensor: - - platform: gpio - pin: 12 - id: gpio_binary_sensor +substitutions: + binary_sensor_pin: GPIO12 + output_pin: GPIO13 + switch_pin: GPIO14 -output: - - platform: gpio - pin: 13 - id: gpio_output - -switch: - - platform: gpio - pin: 14 - id: gpio_switch +<<: !include common.yaml diff --git a/tests/components/gpio/test.esp8266-ard.yaml b/tests/components/gpio/test.esp8266-ard.yaml index 30dfa94b68..09f41abb79 100644 --- a/tests/components/gpio/test.esp8266-ard.yaml +++ b/tests/components/gpio/test.esp8266-ard.yaml @@ -1,14 +1,6 @@ -binary_sensor: - - platform: gpio - pin: 12 - id: gpio_binary_sensor +substitutions: + binary_sensor_pin: GPIO12 + output_pin: GPIO13 + switch_pin: GPIO14 -output: - - platform: gpio - pin: 13 - id: gpio_output - -switch: - - platform: gpio - pin: 14 - id: gpio_switch +<<: !include common.yaml diff --git a/tests/components/gpio/test.rp2040-ard.yaml b/tests/components/gpio/test.rp2040-ard.yaml index 3ca285117d..fc7c9942d0 100644 --- a/tests/components/gpio/test.rp2040-ard.yaml +++ b/tests/components/gpio/test.rp2040-ard.yaml @@ -1,14 +1,6 @@ -binary_sensor: - - platform: gpio - pin: 2 - id: gpio_binary_sensor +substitutions: + binary_sensor_pin: GPIO2 + output_pin: GPIO3 + switch_pin: GPIO4 -output: - - platform: gpio - pin: 3 - id: gpio_output - -switch: - - platform: gpio - pin: 4 - id: gpio_switch +<<: !include common.yaml diff --git a/tests/components/gps/common.yaml b/tests/components/gps/common.yaml new file mode 100644 index 0000000000..fc8228c909 --- /dev/null +++ b/tests/components/gps/common.yaml @@ -0,0 +1,14 @@ +uart: + - id: uart_gps + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + parity: EVEN + +gps: + +time: + - platform: gps + on_time_sync: + then: + logger.log: "It's time!" diff --git a/tests/components/gps/test.esp32-ard.yaml b/tests/components/gps/test.esp32-ard.yaml index c4e4cf9f6f..811f6b72a6 100644 --- a/tests/components/gps/test.esp32-ard.yaml +++ b/tests/components/gps/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_gps - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -gps: - -time: - - platform: gps - on_time_sync: - then: - logger.log: "It's time!" +<<: !include common.yaml diff --git a/tests/components/gps/test.esp32-c3-ard.yaml b/tests/components/gps/test.esp32-c3-ard.yaml index 031f45b873..b516342f3b 100644 --- a/tests/components/gps/test.esp32-c3-ard.yaml +++ b/tests/components/gps/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_gps - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -gps: - -time: - - platform: gps - on_time_sync: - then: - logger.log: "It's time!" +<<: !include common.yaml diff --git a/tests/components/gps/test.esp8266-ard.yaml b/tests/components/gps/test.esp8266-ard.yaml index 031f45b873..b516342f3b 100644 --- a/tests/components/gps/test.esp8266-ard.yaml +++ b/tests/components/gps/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_gps - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -gps: - -time: - - platform: gps - on_time_sync: - then: - logger.log: "It's time!" +<<: !include common.yaml diff --git a/tests/components/gps/test.rp2040-ard.yaml b/tests/components/gps/test.rp2040-ard.yaml index 031f45b873..b516342f3b 100644 --- a/tests/components/gps/test.rp2040-ard.yaml +++ b/tests/components/gps/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_gps - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -gps: - -time: - - platform: gps - on_time_sync: - then: - logger.log: "It's time!" +<<: !include common.yaml diff --git a/tests/components/graph/common.yaml b/tests/components/graph/common.yaml new file mode 100644 index 0000000000..d2a6a9422d --- /dev/null +++ b/tests/components/graph/common.yaml @@ -0,0 +1,25 @@ +i2c: + - id: i2c_graph + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: template + id: some_sensor + +graph: + - id: some_graph + sensor: some_sensor + duration: 1h + width: 100 + height: 100 + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/graph/test.esp32-ard.yaml b/tests/components/graph/test.esp32-ard.yaml index 8c0c0d4c9e..1ca773e06c 100644 --- a/tests/components/graph/test.esp32-ard.yaml +++ b/tests/components/graph/test.esp32-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_graph - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -sensor: - - platform: template - id: some_sensor - -graph: - - id: some_graph - sensor: some_sensor - duration: 1h - width: 100 - height: 100 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/graph/test.esp32-c3-ard.yaml b/tests/components/graph/test.esp32-c3-ard.yaml index 8ce40e84ac..1e6670c196 100644 --- a/tests/components/graph/test.esp32-c3-ard.yaml +++ b/tests/components/graph/test.esp32-c3-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_graph - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -sensor: - - platform: template - id: some_sensor - -graph: - - id: some_graph - sensor: some_sensor - duration: 1h - width: 100 - height: 100 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/graph/test.esp32-c3-idf.yaml b/tests/components/graph/test.esp32-c3-idf.yaml index 8ce40e84ac..1e6670c196 100644 --- a/tests/components/graph/test.esp32-c3-idf.yaml +++ b/tests/components/graph/test.esp32-c3-idf.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_graph - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -sensor: - - platform: template - id: some_sensor - -graph: - - id: some_graph - sensor: some_sensor - duration: 1h - width: 100 - height: 100 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/graph/test.esp32-idf.yaml b/tests/components/graph/test.esp32-idf.yaml index 8c0c0d4c9e..1ca773e06c 100644 --- a/tests/components/graph/test.esp32-idf.yaml +++ b/tests/components/graph/test.esp32-idf.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_graph - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -sensor: - - platform: template - id: some_sensor - -graph: - - id: some_graph - sensor: some_sensor - duration: 1h - width: 100 - height: 100 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/graph/test.esp8266-ard.yaml b/tests/components/graph/test.esp8266-ard.yaml index 33318355d5..dfdc12a3d1 100644 --- a/tests/components/graph/test.esp8266-ard.yaml +++ b/tests/components/graph/test.esp8266-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_graph - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO15 -sensor: - - platform: template - id: some_sensor - -graph: - - id: some_graph - sensor: some_sensor - duration: 1h - width: 100 - height: 100 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/graph/test.rp2040-ard.yaml b/tests/components/graph/test.rp2040-ard.yaml index 8ce40e84ac..1e6670c196 100644 --- a/tests/components/graph/test.rp2040-ard.yaml +++ b/tests/components/graph/test.rp2040-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_graph - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -sensor: - - platform: template - id: some_sensor - -graph: - - id: some_graph - sensor: some_sensor - duration: 1h - width: 100 - height: 100 - -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/common.yaml b/tests/components/graphical_display_menu/common.yaml new file mode 100644 index 0000000000..d979a6a30e --- /dev/null +++ b/tests/components/graphical_display_menu/common.yaml @@ -0,0 +1,120 @@ +i2c: + - id: i2c_graphical_display_menu + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +font: + - file: "gfonts://Roboto" + id: roboto + size: 20 + +number: + - platform: template + id: test_number + min_value: 0 + step: 1 + max_value: 10 + optimistic: true + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + +switch: + - platform: template + id: test_switch + optimistic: true + +graphical_display_menu: + id: test_graphical_display_menu + display: ssd1306_display + font: roboto + active: false + mode: rotary + on_enter: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "root enter");' + on_leave: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' + items: + - type: back + text: "Back" + - type: label + - type: menu + text: "Submenu 1" + items: + - type: back + text: "Back" + - type: menu + text: "Submenu 21" + items: + - type: back + text: "Back" + - type: command + text: "Show Main" + on_value: + then: + - display_menu.show_main: test_graphical_display_menu + - type: select + text: "Enum Item" + immediate_edit: true + select: test_select + on_enter: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_leave: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_value: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + - type: number + text: "Number" + number: test_number + on_enter: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_leave: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_value: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + - type: command + text: "Hide" + on_value: + then: + - display_menu.hide: test_graphical_display_menu + - type: switch + text: "Switch" + switch: test_switch + on_text: "Bright" + off_text: "Dark" + immediate_edit: false + on_value: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "switch value: %s", it->get_value_text().c_str());' + - type: custom + text: !lambda 'return "Custom";' + value_lambda: 'return "Val";' + on_next: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "custom next: %s", it->get_text().c_str());' + on_prev: + then: + lambda: 'ESP_LOGI("graphical_display_menu", "custom prev: %s", it->get_text().c_str());' diff --git a/tests/components/graphical_display_menu/test.esp32-ard.yaml b/tests/components/graphical_display_menu/test.esp32-ard.yaml index a0897536d7..1ca773e06c 100644 --- a/tests/components/graphical_display_menu/test.esp32-ard.yaml +++ b/tests/components/graphical_display_menu/test.esp32-ard.yaml @@ -1,120 +1,6 @@ -i2c: - - id: i2c_graphical_display_menu - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -font: - - file: "gfonts://Roboto" - id: roboto - size: 20 - -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - id: test_switch - optimistic: true - -graphical_display_menu: - id: test_graphical_display_menu - display: ssd1306_display - font: roboto - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' - items: - - type: back - text: "Back" - - type: label - - type: menu - text: "Submenu 1" - items: - - type: back - text: "Back" - - type: menu - text: "Submenu 21" - items: - - type: back - text: "Back" - - type: command - text: "Show Main" - on_value: - then: - - display_menu.show_main: test_graphical_display_menu - - type: select - text: "Enum Item" - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: "Number" - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: "Hide" - on_value: - then: - - display_menu.hide: test_graphical_display_menu - - type: switch - text: "Switch" - switch: test_switch - on_text: "Bright" - off_text: "Dark" - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp32-c3-ard.yaml b/tests/components/graphical_display_menu/test.esp32-c3-ard.yaml index 23acd4e4d9..1e6670c196 100644 --- a/tests/components/graphical_display_menu/test.esp32-c3-ard.yaml +++ b/tests/components/graphical_display_menu/test.esp32-c3-ard.yaml @@ -1,120 +1,6 @@ -i2c: - - id: i2c_graphical_display_menu - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -font: - - file: "gfonts://Roboto" - id: roboto - size: 20 - -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - id: test_switch - optimistic: true - -graphical_display_menu: - id: test_graphical_display_menu - display: ssd1306_display - font: roboto - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' - items: - - type: back - text: "Back" - - type: label - - type: menu - text: "Submenu 1" - items: - - type: back - text: "Back" - - type: menu - text: "Submenu 21" - items: - - type: back - text: "Back" - - type: command - text: "Show Main" - on_value: - then: - - display_menu.show_main: test_graphical_display_menu - - type: select - text: "Enum Item" - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: "Number" - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: "Hide" - on_value: - then: - - display_menu.hide: test_graphical_display_menu - - type: switch - text: "Switch" - switch: test_switch - on_text: "Bright" - off_text: "Dark" - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml b/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml index 23acd4e4d9..1e6670c196 100644 --- a/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml +++ b/tests/components/graphical_display_menu/test.esp32-c3-idf.yaml @@ -1,120 +1,6 @@ -i2c: - - id: i2c_graphical_display_menu - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -font: - - file: "gfonts://Roboto" - id: roboto - size: 20 - -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - id: test_switch - optimistic: true - -graphical_display_menu: - id: test_graphical_display_menu - display: ssd1306_display - font: roboto - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' - items: - - type: back - text: "Back" - - type: label - - type: menu - text: "Submenu 1" - items: - - type: back - text: "Back" - - type: menu - text: "Submenu 21" - items: - - type: back - text: "Back" - - type: command - text: "Show Main" - on_value: - then: - - display_menu.show_main: test_graphical_display_menu - - type: select - text: "Enum Item" - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: "Number" - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: "Hide" - on_value: - then: - - display_menu.hide: test_graphical_display_menu - - type: switch - text: "Switch" - switch: test_switch - on_text: "Bright" - off_text: "Dark" - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp32-idf.yaml b/tests/components/graphical_display_menu/test.esp32-idf.yaml index a0897536d7..1ca773e06c 100644 --- a/tests/components/graphical_display_menu/test.esp32-idf.yaml +++ b/tests/components/graphical_display_menu/test.esp32-idf.yaml @@ -1,120 +1,6 @@ -i2c: - - id: i2c_graphical_display_menu - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -font: - - file: "gfonts://Roboto" - id: roboto - size: 20 - -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - id: test_switch - optimistic: true - -graphical_display_menu: - id: test_graphical_display_menu - display: ssd1306_display - font: roboto - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' - items: - - type: back - text: "Back" - - type: label - - type: menu - text: "Submenu 1" - items: - - type: back - text: "Back" - - type: menu - text: "Submenu 21" - items: - - type: back - text: "Back" - - type: command - text: "Show Main" - on_value: - then: - - display_menu.show_main: test_graphical_display_menu - - type: select - text: "Enum Item" - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: "Number" - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: "Hide" - on_value: - then: - - display_menu.hide: test_graphical_display_menu - - type: switch - text: "Switch" - switch: test_switch - on_text: "Bright" - off_text: "Dark" - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.esp8266-ard.yaml b/tests/components/graphical_display_menu/test.esp8266-ard.yaml index 28c1a7298d..dfdc12a3d1 100644 --- a/tests/components/graphical_display_menu/test.esp8266-ard.yaml +++ b/tests/components/graphical_display_menu/test.esp8266-ard.yaml @@ -1,120 +1,6 @@ -i2c: - - id: i2c_graphical_display_menu - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -font: - - file: "gfonts://Roboto" - id: roboto - size: 20 - -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - id: test_switch - optimistic: true - -graphical_display_menu: - id: test_graphical_display_menu - display: ssd1306_display - font: roboto - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' - items: - - type: back - text: "Back" - - type: label - - type: menu - text: "Submenu 1" - items: - - type: back - text: "Back" - - type: menu - text: "Submenu 21" - items: - - type: back - text: "Back" - - type: command - text: "Show Main" - on_value: - then: - - display_menu.show_main: test_graphical_display_menu - - type: select - text: "Enum Item" - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: "Number" - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: "Hide" - on_value: - then: - - display_menu.hide: test_graphical_display_menu - - type: switch - text: "Switch" - switch: test_switch - on_text: "Bright" - off_text: "Dark" - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/graphical_display_menu/test.rp2040-ard.yaml b/tests/components/graphical_display_menu/test.rp2040-ard.yaml index 23acd4e4d9..1e6670c196 100644 --- a/tests/components/graphical_display_menu/test.rp2040-ard.yaml +++ b/tests/components/graphical_display_menu/test.rp2040-ard.yaml @@ -1,120 +1,6 @@ -i2c: - - id: i2c_graphical_display_menu - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO6 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -font: - - file: "gfonts://Roboto" - id: roboto - size: 20 - -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true - -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - id: test_switch - optimistic: true - -graphical_display_menu: - id: test_graphical_display_menu - display: ssd1306_display - font: roboto - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "root leave");' - items: - - type: back - text: "Back" - - type: label - - type: menu - text: "Submenu 1" - items: - - type: back - text: "Back" - - type: menu - text: "Submenu 21" - items: - - type: back - text: "Back" - - type: command - text: "Show Main" - on_value: - then: - - display_menu.show_main: test_graphical_display_menu - - type: select - text: "Enum Item" - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: "Number" - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: "Hide" - on_value: - then: - - display_menu.hide: test_graphical_display_menu - - type: switch - text: "Switch" - switch: test_switch - on_text: "Bright" - off_text: "Dark" - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("graphical_display_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/gree/common.yaml b/tests/components/gree/common.yaml new file mode 100644 index 0000000000..c221184bbf --- /dev/null +++ b/tests/components/gree/common.yaml @@ -0,0 +1,8 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: gree + name: GREE + model: generic diff --git a/tests/components/gree/test.esp32-ard.yaml b/tests/components/gree/test.esp32-ard.yaml index 91491d7e16..7b012aa64c 100644 --- a/tests/components/gree/test.esp32-ard.yaml +++ b/tests/components/gree/test.esp32-ard.yaml @@ -1,8 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: gree - name: GREE - model: generic +<<: !include common.yaml diff --git a/tests/components/gree/test.esp32-c3-ard.yaml b/tests/components/gree/test.esp32-c3-ard.yaml index 91491d7e16..7b012aa64c 100644 --- a/tests/components/gree/test.esp32-c3-ard.yaml +++ b/tests/components/gree/test.esp32-c3-ard.yaml @@ -1,8 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: gree - name: GREE - model: generic +<<: !include common.yaml diff --git a/tests/components/gree/test.esp32-c3-idf.yaml b/tests/components/gree/test.esp32-c3-idf.yaml index 91491d7e16..7b012aa64c 100644 --- a/tests/components/gree/test.esp32-c3-idf.yaml +++ b/tests/components/gree/test.esp32-c3-idf.yaml @@ -1,8 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: gree - name: GREE - model: generic +<<: !include common.yaml diff --git a/tests/components/gree/test.esp32-idf.yaml b/tests/components/gree/test.esp32-idf.yaml index 91491d7e16..7b012aa64c 100644 --- a/tests/components/gree/test.esp32-idf.yaml +++ b/tests/components/gree/test.esp32-idf.yaml @@ -1,8 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: gree - name: GREE - model: generic +<<: !include common.yaml diff --git a/tests/components/gree/test.esp8266-ard.yaml b/tests/components/gree/test.esp8266-ard.yaml index d0542973ce..f5097fcf5f 100644 --- a/tests/components/gree/test.esp8266-ard.yaml +++ b/tests/components/gree/test.esp8266-ard.yaml @@ -1,8 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: gree - name: GREE - model: generic +<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/common.yaml b/tests/components/grove_gas_mc_v2/common.yaml index 0729e6b9c7..dbdb950033 100644 --- a/tests/components/grove_gas_mc_v2/common.yaml +++ b/tests/components/grove_gas_mc_v2/common.yaml @@ -1,6 +1,10 @@ +i2c: + - id: i2c_grove_gas_mc_v2 + scl: ${scl_pin} + sda: ${sda_pin} + sensor: - platform: grove_gas_mc_v2 - i2c_id: i2c_bus nitrogen_dioxide: name: "Nitrogen Dioxide" ethanol: diff --git a/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml index 00c7856c36..63c3bd6afd 100644 --- a/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml +++ b/tests/components/grove_gas_mc_v2/test.esp32-ard.yaml @@ -1,6 +1,5 @@ -i2c: - sda: 21 - scl: 22 - id: i2c_bus +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 <<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/grove_gas_mc_v2/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml b/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/grove_gas_mc_v2/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml b/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml index 00c7856c36..63c3bd6afd 100644 --- a/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml +++ b/tests/components/grove_gas_mc_v2/test.esp32-idf.yaml @@ -1,6 +1,5 @@ -i2c: - sda: 21 - scl: 22 - id: i2c_bus +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 <<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml b/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml index 2de18bdf39..ee2c29ca4e 100644 --- a/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml +++ b/tests/components/grove_gas_mc_v2/test.esp8266-ard.yaml @@ -1,6 +1,5 @@ -i2c: - sda: 4 - scl: 5 - id: i2c_bus +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml b/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml index 00c7856c36..ee2c29ca4e 100644 --- a/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml +++ b/tests/components/grove_gas_mc_v2/test.rp2040-ard.yaml @@ -1,6 +1,5 @@ -i2c: - sda: 21 - scl: 22 - id: i2c_bus +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/common.yaml b/tests/components/grove_tb6612fng/common.yaml new file mode 100644 index 0000000000..0db6c00bf7 --- /dev/null +++ b/tests/components/grove_tb6612fng/common.yaml @@ -0,0 +1,23 @@ +esphome: + on_boot: + then: + - grove_tb6612fng.run: + channel: 1 + speed: 255 + direction: BACKWARD + id: test_motor + - grove_tb6612fng.stop: + channel: 1 + id: test_motor + - grove_tb6612fng.break: + channel: 1 + id: test_motor + +i2c: + - id: i2c_grove_tb6612fng + scl: ${scl_pin} + sda: ${sda_pin} + +grove_tb6612fng: + id: test_motor + address: 0x14 diff --git a/tests/components/grove_tb6612fng/test.esp32-ard.yaml b/tests/components/grove_tb6612fng/test.esp32-ard.yaml index 3271fb754f..63c3bd6afd 100644 --- a/tests/components/grove_tb6612fng/test.esp32-ard.yaml +++ b/tests/components/grove_tb6612fng/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - grove_tb6612fng.run: - channel: 1 - speed: 255 - direction: BACKWARD - id: test_motor - - grove_tb6612fng.stop: - channel: 1 - id: test_motor - - grove_tb6612fng.break: - channel: 1 - id: test_motor +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_grove_tb6612fng - scl: 16 - sda: 17 - -grove_tb6612fng: - id: test_motor - address: 0x14 +<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml b/tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml index ef6dff6539..ee2c29ca4e 100644 --- a/tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml +++ b/tests/components/grove_tb6612fng/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - grove_tb6612fng.run: - channel: 1 - speed: 255 - direction: BACKWARD - id: test_motor - - grove_tb6612fng.stop: - channel: 1 - id: test_motor - - grove_tb6612fng.break: - channel: 1 - id: test_motor +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_grove_tb6612fng - scl: 5 - sda: 4 - -grove_tb6612fng: - id: test_motor - address: 0x14 +<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml b/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml index ef6dff6539..ee2c29ca4e 100644 --- a/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml +++ b/tests/components/grove_tb6612fng/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - grove_tb6612fng.run: - channel: 1 - speed: 255 - direction: BACKWARD - id: test_motor - - grove_tb6612fng.stop: - channel: 1 - id: test_motor - - grove_tb6612fng.break: - channel: 1 - id: test_motor +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_grove_tb6612fng - scl: 5 - sda: 4 - -grove_tb6612fng: - id: test_motor - address: 0x14 +<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp32-idf.yaml b/tests/components/grove_tb6612fng/test.esp32-idf.yaml index 3271fb754f..63c3bd6afd 100644 --- a/tests/components/grove_tb6612fng/test.esp32-idf.yaml +++ b/tests/components/grove_tb6612fng/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - grove_tb6612fng.run: - channel: 1 - speed: 255 - direction: BACKWARD - id: test_motor - - grove_tb6612fng.stop: - channel: 1 - id: test_motor - - grove_tb6612fng.break: - channel: 1 - id: test_motor +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_grove_tb6612fng - scl: 16 - sda: 17 - -grove_tb6612fng: - id: test_motor - address: 0x14 +<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.esp8266-ard.yaml b/tests/components/grove_tb6612fng/test.esp8266-ard.yaml index ef6dff6539..ee2c29ca4e 100644 --- a/tests/components/grove_tb6612fng/test.esp8266-ard.yaml +++ b/tests/components/grove_tb6612fng/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - grove_tb6612fng.run: - channel: 1 - speed: 255 - direction: BACKWARD - id: test_motor - - grove_tb6612fng.stop: - channel: 1 - id: test_motor - - grove_tb6612fng.break: - channel: 1 - id: test_motor +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_grove_tb6612fng - scl: 5 - sda: 4 - -grove_tb6612fng: - id: test_motor - address: 0x14 +<<: !include common.yaml diff --git a/tests/components/grove_tb6612fng/test.rp2040-ard.yaml b/tests/components/grove_tb6612fng/test.rp2040-ard.yaml index ef6dff6539..ee2c29ca4e 100644 --- a/tests/components/grove_tb6612fng/test.rp2040-ard.yaml +++ b/tests/components/grove_tb6612fng/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - grove_tb6612fng.run: - channel: 1 - speed: 255 - direction: BACKWARD - id: test_motor - - grove_tb6612fng.stop: - channel: 1 - id: test_motor - - grove_tb6612fng.break: - channel: 1 - id: test_motor +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_grove_tb6612fng - scl: 5 - sda: 4 - -grove_tb6612fng: - id: test_motor - address: 0x14 +<<: !include common.yaml diff --git a/tests/components/growatt_solar/common.yaml b/tests/components/growatt_solar/common.yaml new file mode 100644 index 0000000000..7cc6b2a139 --- /dev/null +++ b/tests/components/growatt_solar/common.yaml @@ -0,0 +1,62 @@ +uart: + - id: uart_growatt_solar + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +modbus: + flow_control_pin: ${flow_control_pin} + +sensor: + - platform: growatt_solar + update_interval: 10s + protocol_version: RTU + inverter_status: + name: Growatt Status Code + phase_a: + voltage: + name: Growatt Voltage Phase A + current: + name: Growatt Current Phase A + active_power: + name: Growatt Power Phase A + phase_b: + voltage: + name: Growatt Voltage Phase B + current: + name: Growatt Current Phase B + active_power: + name: Growatt Power Phase B + phase_c: + voltage: + name: Growatt Voltage Phase C + current: + name: Growatt Current Phase C + active_power: + name: Growatt Power Phase C + pv1: + voltage: + name: Growatt PV1 Voltage + current: + name: Growatt PV1 Current + active_power: + name: Growatt PV1 Active Power + pv2: + voltage: + name: Growatt PV2 Voltage + current: + name: Growatt PV2 Current + active_power: + name: Growatt PV2 Active Power + active_power: + name: Growatt Grid Active Power + pv_active_power: + name: Growatt PV Active Power + frequency: + name: Growatt Frequency + energy_production_day: + name: Growatt Today's Generation + total_energy_production: + name: Growatt Total Energy Production + inverter_module_temp: + name: Growatt Inverter Module Temp diff --git a/tests/components/growatt_solar/test.esp32-ard.yaml b/tests/components/growatt_solar/test.esp32-ard.yaml index 654f2ccedf..bd767a8ece 100644 --- a/tests/components/growatt_solar/test.esp32-ard.yaml +++ b/tests/components/growatt_solar/test.esp32-ard.yaml @@ -1,62 +1,6 @@ -uart: - - id: uart_growatt_solar - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 13 - -sensor: - - platform: growatt_solar - update_interval: 10s - protocol_version: RTU - inverter_status: - name: Growatt Status Code - phase_a: - voltage: - name: Growatt Voltage Phase A - current: - name: Growatt Current Phase A - active_power: - name: Growatt Power Phase A - phase_b: - voltage: - name: Growatt Voltage Phase B - current: - name: Growatt Current Phase B - active_power: - name: Growatt Power Phase B - phase_c: - voltage: - name: Growatt Voltage Phase C - current: - name: Growatt Current Phase C - active_power: - name: Growatt Power Phase C - pv1: - voltage: - name: Growatt PV1 Voltage - current: - name: Growatt PV1 Current - active_power: - name: Growatt PV1 Active Power - pv2: - voltage: - name: Growatt PV2 Voltage - current: - name: Growatt PV2 Current - active_power: - name: Growatt PV2 Active Power - active_power: - name: Growatt Grid Active Power - pv_active_power: - name: Growatt PV Active Power - frequency: - name: Growatt Frequency - energy_production_day: - name: Growatt Today's Generation - total_energy_production: - name: Growatt Total Energy Production - inverter_module_temp: - name: Growatt Inverter Module Temp +<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp32-c3-ard.yaml b/tests/components/growatt_solar/test.esp32-c3-ard.yaml index 7e73897856..452031a5aa 100644 --- a/tests/components/growatt_solar/test.esp32-c3-ard.yaml +++ b/tests/components/growatt_solar/test.esp32-c3-ard.yaml @@ -1,62 +1,6 @@ -uart: - - id: uart_growatt_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: growatt_solar - update_interval: 10s - protocol_version: RTU - inverter_status: - name: Growatt Status Code - phase_a: - voltage: - name: Growatt Voltage Phase A - current: - name: Growatt Current Phase A - active_power: - name: Growatt Power Phase A - phase_b: - voltage: - name: Growatt Voltage Phase B - current: - name: Growatt Current Phase B - active_power: - name: Growatt Power Phase B - phase_c: - voltage: - name: Growatt Voltage Phase C - current: - name: Growatt Current Phase C - active_power: - name: Growatt Power Phase C - pv1: - voltage: - name: Growatt PV1 Voltage - current: - name: Growatt PV1 Current - active_power: - name: Growatt PV1 Active Power - pv2: - voltage: - name: Growatt PV2 Voltage - current: - name: Growatt PV2 Current - active_power: - name: Growatt PV2 Active Power - active_power: - name: Growatt Grid Active Power - pv_active_power: - name: Growatt PV Active Power - frequency: - name: Growatt Frequency - energy_production_day: - name: Growatt Today's Generation - total_energy_production: - name: Growatt Total Energy Production - inverter_module_temp: - name: Growatt Inverter Module Temp +<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp32-c3-idf.yaml b/tests/components/growatt_solar/test.esp32-c3-idf.yaml index 7e73897856..452031a5aa 100644 --- a/tests/components/growatt_solar/test.esp32-c3-idf.yaml +++ b/tests/components/growatt_solar/test.esp32-c3-idf.yaml @@ -1,62 +1,6 @@ -uart: - - id: uart_growatt_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: growatt_solar - update_interval: 10s - protocol_version: RTU - inverter_status: - name: Growatt Status Code - phase_a: - voltage: - name: Growatt Voltage Phase A - current: - name: Growatt Current Phase A - active_power: - name: Growatt Power Phase A - phase_b: - voltage: - name: Growatt Voltage Phase B - current: - name: Growatt Current Phase B - active_power: - name: Growatt Power Phase B - phase_c: - voltage: - name: Growatt Voltage Phase C - current: - name: Growatt Current Phase C - active_power: - name: Growatt Power Phase C - pv1: - voltage: - name: Growatt PV1 Voltage - current: - name: Growatt PV1 Current - active_power: - name: Growatt PV1 Active Power - pv2: - voltage: - name: Growatt PV2 Voltage - current: - name: Growatt PV2 Current - active_power: - name: Growatt PV2 Active Power - active_power: - name: Growatt Grid Active Power - pv_active_power: - name: Growatt PV Active Power - frequency: - name: Growatt Frequency - energy_production_day: - name: Growatt Today's Generation - total_energy_production: - name: Growatt Total Energy Production - inverter_module_temp: - name: Growatt Inverter Module Temp +<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp32-idf.yaml b/tests/components/growatt_solar/test.esp32-idf.yaml index 654f2ccedf..bd767a8ece 100644 --- a/tests/components/growatt_solar/test.esp32-idf.yaml +++ b/tests/components/growatt_solar/test.esp32-idf.yaml @@ -1,62 +1,6 @@ -uart: - - id: uart_growatt_solar - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 13 - -sensor: - - platform: growatt_solar - update_interval: 10s - protocol_version: RTU - inverter_status: - name: Growatt Status Code - phase_a: - voltage: - name: Growatt Voltage Phase A - current: - name: Growatt Current Phase A - active_power: - name: Growatt Power Phase A - phase_b: - voltage: - name: Growatt Voltage Phase B - current: - name: Growatt Current Phase B - active_power: - name: Growatt Power Phase B - phase_c: - voltage: - name: Growatt Voltage Phase C - current: - name: Growatt Current Phase C - active_power: - name: Growatt Power Phase C - pv1: - voltage: - name: Growatt PV1 Voltage - current: - name: Growatt PV1 Current - active_power: - name: Growatt PV1 Active Power - pv2: - voltage: - name: Growatt PV2 Voltage - current: - name: Growatt PV2 Current - active_power: - name: Growatt PV2 Active Power - active_power: - name: Growatt Grid Active Power - pv_active_power: - name: Growatt PV Active Power - frequency: - name: Growatt Frequency - energy_production_day: - name: Growatt Today's Generation - total_energy_production: - name: Growatt Total Energy Production - inverter_module_temp: - name: Growatt Inverter Module Temp +<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.esp8266-ard.yaml b/tests/components/growatt_solar/test.esp8266-ard.yaml index a1cf8267ae..29c98d0957 100644 --- a/tests/components/growatt_solar/test.esp8266-ard.yaml +++ b/tests/components/growatt_solar/test.esp8266-ard.yaml @@ -1,62 +1,6 @@ -uart: - - id: uart_growatt_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 13 - -sensor: - - platform: growatt_solar - update_interval: 10s - protocol_version: RTU - inverter_status: - name: Growatt Status Code - phase_a: - voltage: - name: Growatt Voltage Phase A - current: - name: Growatt Current Phase A - active_power: - name: Growatt Power Phase A - phase_b: - voltage: - name: Growatt Voltage Phase B - current: - name: Growatt Current Phase B - active_power: - name: Growatt Power Phase B - phase_c: - voltage: - name: Growatt Voltage Phase C - current: - name: Growatt Current Phase C - active_power: - name: Growatt Power Phase C - pv1: - voltage: - name: Growatt PV1 Voltage - current: - name: Growatt PV1 Current - active_power: - name: Growatt PV1 Active Power - pv2: - voltage: - name: Growatt PV2 Voltage - current: - name: Growatt PV2 Current - active_power: - name: Growatt PV2 Active Power - active_power: - name: Growatt Grid Active Power - pv_active_power: - name: Growatt PV Active Power - frequency: - name: Growatt Frequency - energy_production_day: - name: Growatt Today's Generation - total_energy_production: - name: Growatt Total Energy Production - inverter_module_temp: - name: Growatt Inverter Module Temp +<<: !include common.yaml diff --git a/tests/components/growatt_solar/test.rp2040-ard.yaml b/tests/components/growatt_solar/test.rp2040-ard.yaml index 7e73897856..452031a5aa 100644 --- a/tests/components/growatt_solar/test.rp2040-ard.yaml +++ b/tests/components/growatt_solar/test.rp2040-ard.yaml @@ -1,62 +1,6 @@ -uart: - - id: uart_growatt_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: growatt_solar - update_interval: 10s - protocol_version: RTU - inverter_status: - name: Growatt Status Code - phase_a: - voltage: - name: Growatt Voltage Phase A - current: - name: Growatt Current Phase A - active_power: - name: Growatt Power Phase A - phase_b: - voltage: - name: Growatt Voltage Phase B - current: - name: Growatt Current Phase B - active_power: - name: Growatt Power Phase B - phase_c: - voltage: - name: Growatt Voltage Phase C - current: - name: Growatt Current Phase C - active_power: - name: Growatt Power Phase C - pv1: - voltage: - name: Growatt PV1 Voltage - current: - name: Growatt PV1 Current - active_power: - name: Growatt PV1 Active Power - pv2: - voltage: - name: Growatt PV2 Voltage - current: - name: Growatt PV2 Current - active_power: - name: Growatt PV2 Active Power - active_power: - name: Growatt Grid Active Power - pv_active_power: - name: Growatt PV Active Power - frequency: - name: Growatt Frequency - energy_production_day: - name: Growatt Today's Generation - total_energy_production: - name: Growatt Total Energy Production - inverter_module_temp: - name: Growatt Inverter Module Temp +<<: !include common.yaml From f8fae676b1464162d0623fc5369e7ae710c5e60d Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:50 -0600 Subject: [PATCH 0958/1052] [CI] Consolidate some tests (H) (#8198) --- tests/components/havells_solar/common.yaml | 79 ++++++++++++++++++ .../havells_solar/test.esp32-ard.yaml | 83 ++----------------- .../havells_solar/test.esp32-c3-ard.yaml | 83 ++----------------- .../havells_solar/test.esp32-c3-idf.yaml | 83 ++----------------- .../havells_solar/test.esp32-idf.yaml | 83 ++----------------- .../havells_solar/test.esp8266-ard.yaml | 83 ++----------------- .../havells_solar/test.rp2040-ard.yaml | 83 ++----------------- tests/components/hdc1080/common.yaml | 12 +++ tests/components/hdc1080/test.esp32-ard.yaml | 15 +--- .../components/hdc1080/test.esp32-c3-ard.yaml | 15 +--- .../components/hdc1080/test.esp32-c3-idf.yaml | 15 +--- tests/components/hdc1080/test.esp32-idf.yaml | 15 +--- .../components/hdc1080/test.esp8266-ard.yaml | 15 +--- tests/components/hdc1080/test.rp2040-ard.yaml | 15 +--- tests/components/he60r/common.yaml | 13 +++ tests/components/he60r/test.esp32-ard.yaml | 16 +--- tests/components/he60r/test.esp32-c3-ard.yaml | 16 +--- tests/components/he60r/test.esp32-c3-idf.yaml | 16 +--- tests/components/he60r/test.esp32-idf.yaml | 16 +--- tests/components/he60r/test.esp8266-ard.yaml | 16 +--- tests/components/he60r/test.rp2040-ard.yaml | 16 +--- tests/components/heatpumpir/common.yaml | 26 ++++++ .../heatpumpir/test.bk72xx-ard.yaml | 28 +------ .../components/heatpumpir/test.esp32-ard.yaml | 21 +---- .../heatpumpir/test.esp32-c3-ard.yaml | 4 + .../heatpumpir/test.esp8266-ard.yaml | 21 +---- tests/components/hitachi_ac344/common.yaml | 7 ++ .../hitachi_ac344/test.bk72xx-ard.yaml | 4 + .../hitachi_ac344/test.esp32-ard.yaml | 9 +- .../hitachi_ac344/test.esp32-c3-ard.yaml | 9 +- .../hitachi_ac344/test.esp32-c3-idf.yaml | 9 +- .../hitachi_ac344/test.esp32-idf.yaml | 9 +- .../hitachi_ac344/test.esp8266-ard.yaml | 9 +- tests/components/hitachi_ac424/common.yaml | 7 ++ .../hitachi_ac424/test.bk72xx-ard.yaml | 4 + .../hitachi_ac424/test.esp32-ard.yaml | 9 +- .../hitachi_ac424/test.esp32-c3-ard.yaml | 9 +- .../hitachi_ac424/test.esp32-c3-idf.yaml | 9 +- .../hitachi_ac424/test.esp32-idf.yaml | 9 +- .../hitachi_ac424/test.esp8266-ard.yaml | 9 +- tests/components/hlw8012/common.yaml | 21 +++++ tests/components/hlw8012/test.esp32-ard.yaml | 27 ++---- .../components/hlw8012/test.esp32-c3-ard.yaml | 27 ++---- .../components/hlw8012/test.esp32-c3-idf.yaml | 27 ++---- tests/components/hlw8012/test.esp32-idf.yaml | 27 ++---- .../components/hlw8012/test.esp8266-ard.yaml | 27 ++---- tests/components/hlw8012/test.rp2040-ard.yaml | 27 ++---- tests/components/hm3301/common.yaml | 16 ++++ tests/components/hm3301/test.esp32-ard.yaml | 19 +---- .../components/hm3301/test.esp32-c3-ard.yaml | 19 +---- .../components/hm3301/test.esp32-c3-idf.yaml | 19 +---- tests/components/hm3301/test.esp32-idf.yaml | 19 +---- tests/components/hm3301/test.esp8266-ard.yaml | 19 +---- tests/components/hm3301/test.rp2040-ard.yaml | 19 +---- tests/components/hmc5883l/common.yaml | 19 +++++ tests/components/hmc5883l/test.esp32-ard.yaml | 22 +---- .../hmc5883l/test.esp32-c3-ard.yaml | 22 +---- .../hmc5883l/test.esp32-c3-idf.yaml | 22 +---- tests/components/hmc5883l/test.esp32-idf.yaml | 22 +---- .../components/hmc5883l/test.esp8266-ard.yaml | 22 +---- .../components/hmc5883l/test.rp2040-ard.yaml | 22 +---- .../components/honeywell_hih_i2c/common.yaml | 12 +++ .../honeywell_hih_i2c/test.esp32-ard.yaml | 15 +--- .../honeywell_hih_i2c/test.esp32-c3-ard.yaml | 15 +--- .../honeywell_hih_i2c/test.esp32-c3-idf.yaml | 15 +--- .../honeywell_hih_i2c/test.esp32-idf.yaml | 15 +--- .../honeywell_hih_i2c/test.esp8266-ard.yaml | 15 +--- .../honeywell_hih_i2c/test.rp2040-ard.yaml | 15 +--- tests/components/honeywellabp/common.yaml | 15 ++++ .../honeywellabp/test.esp32-ard.yaml | 20 ++--- .../honeywellabp/test.esp32-c3-ard.yaml | 20 ++--- .../honeywellabp/test.esp32-c3-idf.yaml | 20 ++--- .../honeywellabp/test.esp32-idf.yaml | 20 ++--- .../honeywellabp/test.esp8266-ard.yaml | 20 ++--- .../honeywellabp/test.rp2040-ard.yaml | 20 ++--- .../components/honeywellabp2_i2c/common.yaml | 15 ++++ .../honeywellabp2_i2c/test.esp32-ard.yaml | 18 +--- .../honeywellabp2_i2c/test.esp32-c3-ard.yaml | 18 +--- .../honeywellabp2_i2c/test.esp32-c3-idf.yaml | 18 +--- .../honeywellabp2_i2c/test.esp32-idf.yaml | 18 +--- .../honeywellabp2_i2c/test.esp8266-ard.yaml | 18 +--- .../honeywellabp2_i2c/test.rp2040-ard.yaml | 18 +--- tests/components/hrxl_maxsonar_wr/common.yaml | 10 +++ .../hrxl_maxsonar_wr/test.esp32-ard.yaml | 13 +-- .../hrxl_maxsonar_wr/test.esp32-c3-ard.yaml | 13 +-- .../hrxl_maxsonar_wr/test.esp32-c3-idf.yaml | 13 +-- .../hrxl_maxsonar_wr/test.esp32-idf.yaml | 13 +-- .../hrxl_maxsonar_wr/test.esp8266-ard.yaml | 13 +-- .../hrxl_maxsonar_wr/test.rp2040-ard.yaml | 13 +-- tests/components/hte501/common.yaml | 12 +++ tests/components/hte501/test.esp32-ard.yaml | 15 +--- .../components/hte501/test.esp32-c3-ard.yaml | 15 +--- .../components/hte501/test.esp32-c3-idf.yaml | 15 +--- tests/components/hte501/test.esp32-idf.yaml | 15 +--- tests/components/hte501/test.esp8266-ard.yaml | 15 +--- tests/components/hte501/test.rp2040-ard.yaml | 15 +--- tests/components/htu21d/common.yaml | 15 ++++ tests/components/htu21d/test.esp32-ard.yaml | 18 +--- .../components/htu21d/test.esp32-c3-ard.yaml | 18 +--- .../components/htu21d/test.esp32-c3-idf.yaml | 18 +--- tests/components/htu21d/test.esp32-idf.yaml | 18 +--- tests/components/htu21d/test.esp8266-ard.yaml | 18 +--- tests/components/htu21d/test.rp2040-ard.yaml | 18 +--- tests/components/htu31d/common.yaml | 7 +- tests/components/htu31d/test.esp32-ard.yaml | 4 + .../components/htu31d/test.esp32-c3-ard.yaml | 5 ++ .../components/htu31d/test.esp32-c3-idf.yaml | 5 ++ tests/components/htu31d/test.esp32-idf.yaml | 4 + tests/components/htu31d/test.esp8266-ard.yaml | 4 + tests/components/htu31d/test.rp2040-ard.yaml | 5 ++ tests/components/hx711/common.yaml | 7 ++ tests/components/hx711/test.esp32-ard.yaml | 12 ++- tests/components/hx711/test.esp32-c3-ard.yaml | 12 ++- tests/components/hx711/test.esp32-c3-idf.yaml | 12 ++- tests/components/hx711/test.esp32-idf.yaml | 12 ++- tests/components/hx711/test.esp8266-ard.yaml | 12 ++- tests/components/hx711/test.rp2040-ard.yaml | 12 ++- tests/components/hydreon_rgxx/common.yaml | 37 +++++++++ .../hydreon_rgxx/test.esp32-ard.yaml | 40 +-------- .../hydreon_rgxx/test.esp32-c3-ard.yaml | 40 +-------- .../hydreon_rgxx/test.esp32-c3-idf.yaml | 40 +-------- .../hydreon_rgxx/test.esp32-idf.yaml | 40 +-------- .../hydreon_rgxx/test.esp8266-ard.yaml | 40 +-------- .../hydreon_rgxx/test.rp2040-ard.yaml | 40 +-------- tests/components/hyt271/common.yaml | 11 +++ tests/components/hyt271/test.esp32-ard.yaml | 14 +--- .../components/hyt271/test.esp32-c3-ard.yaml | 14 +--- .../components/hyt271/test.esp32-c3-idf.yaml | 14 +--- tests/components/hyt271/test.esp32-idf.yaml | 14 +--- tests/components/hyt271/test.esp8266-ard.yaml | 14 +--- tests/components/hyt271/test.rp2040-ard.yaml | 14 +--- 131 files changed, 813 insertions(+), 1809 deletions(-) create mode 100644 tests/components/havells_solar/common.yaml create mode 100644 tests/components/hdc1080/common.yaml create mode 100644 tests/components/he60r/common.yaml create mode 100644 tests/components/heatpumpir/common.yaml create mode 100644 tests/components/heatpumpir/test.esp32-c3-ard.yaml create mode 100644 tests/components/hitachi_ac344/common.yaml create mode 100644 tests/components/hitachi_ac344/test.bk72xx-ard.yaml create mode 100644 tests/components/hitachi_ac424/common.yaml create mode 100644 tests/components/hitachi_ac424/test.bk72xx-ard.yaml create mode 100644 tests/components/hlw8012/common.yaml create mode 100644 tests/components/hm3301/common.yaml create mode 100644 tests/components/hmc5883l/common.yaml create mode 100644 tests/components/honeywell_hih_i2c/common.yaml create mode 100644 tests/components/honeywellabp/common.yaml create mode 100644 tests/components/honeywellabp2_i2c/common.yaml create mode 100644 tests/components/hrxl_maxsonar_wr/common.yaml create mode 100644 tests/components/hte501/common.yaml create mode 100644 tests/components/htu21d/common.yaml create mode 100644 tests/components/htu31d/test.esp32-c3-ard.yaml create mode 100644 tests/components/htu31d/test.esp32-c3-idf.yaml create mode 100644 tests/components/htu31d/test.rp2040-ard.yaml create mode 100644 tests/components/hx711/common.yaml create mode 100644 tests/components/hydreon_rgxx/common.yaml create mode 100644 tests/components/hyt271/common.yaml diff --git a/tests/components/havells_solar/common.yaml b/tests/components/havells_solar/common.yaml new file mode 100644 index 0000000000..370b0d357d --- /dev/null +++ b/tests/components/havells_solar/common.yaml @@ -0,0 +1,79 @@ +uart: + - id: uart_havells_solar + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +modbus: + flow_control_pin: ${flow_control_pin} + +sensor: + - platform: havells_solar + update_interval: 60s + phase_a: + voltage: + name: Phase A Voltage + current: + name: Phase A Current + phase_b: + voltage: + name: Voltage Phase B + current: + name: Current Phase B + phase_c: + voltage: + name: Voltage Phase C + current: + name: Current Phase C + pv1: + voltage: + name: PV1 Voltage + current: + name: PV1 Current + active_power: + name: PV1 Active Power + voltage_sampled_by_secondary_cpu: + name: PV1 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV1 Insulation Of +VE To Ground + pv2: + voltage: + name: PV2 Voltage + current: + name: PV2 Current + active_power: + name: PV2 Active Power + voltage_sampled_by_secondary_cpu: + name: PV2 Voltage Sampled By Secondary CPU + insulation_of_p_to_ground: + name: PV2 Insulation Of +VE To Ground + active_power: + name: Active Power + reactive_power: + name: Reactive Power + frequency: + name: Frequency + energy_production_day: + name: Today's Generation + total_energy_production: + name: Total Energy Production + total_generation_time: + name: Total Generation Time + today_generation_time: + name: Today Generation Time + inverter_module_temp: + name: Inverter Module Temp + inverter_inner_temp: + name: Inverter Inner Temp + inverter_bus_voltage: + name: Inverter BUS Voltage + insulation_of_pv_n_to_ground: + name: Insulation Of PV- To Ground + gfci_value: + name: GFCI Value + dci_of_r: + name: DCI Of R + dci_of_s: + name: DCI Of S + dci_of_t: + name: DCI Of T diff --git a/tests/components/havells_solar/test.esp32-ard.yaml b/tests/components/havells_solar/test.esp32-ard.yaml index 2cda8e37be..bd767a8ece 100644 --- a/tests/components/havells_solar/test.esp32-ard.yaml +++ b/tests/components/havells_solar/test.esp32-ard.yaml @@ -1,79 +1,6 @@ -uart: - - id: uart_havells_solar - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 3 - -sensor: - - platform: havells_solar - update_interval: 60s - phase_a: - voltage: - name: Phase A Voltage - current: - name: Phase A Current - phase_b: - voltage: - name: Voltage Phase B - current: - name: Current Phase B - phase_c: - voltage: - name: Voltage Phase C - current: - name: Current Phase C - pv1: - voltage: - name: PV1 Voltage - current: - name: PV1 Current - active_power: - name: PV1 Active Power - voltage_sampled_by_secondary_cpu: - name: PV1 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV1 Insulation Of +VE To Ground - pv2: - voltage: - name: PV2 Voltage - current: - name: PV2 Current - active_power: - name: PV2 Active Power - voltage_sampled_by_secondary_cpu: - name: PV2 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV2 Insulation Of +VE To Ground - active_power: - name: Active Power - reactive_power: - name: Reactive Power - frequency: - name: Frequency - energy_production_day: - name: Today's Generation - total_energy_production: - name: Total Energy Production - total_generation_time: - name: Total Generation Time - today_generation_time: - name: Today Generation Time - inverter_module_temp: - name: Inverter Module Temp - inverter_inner_temp: - name: Inverter Inner Temp - inverter_bus_voltage: - name: Inverter BUS Voltage - insulation_of_pv_n_to_ground: - name: Insulation Of PV- To Ground - gfci_value: - name: GFCI Value - dci_of_r: - name: DCI Of R - dci_of_s: - name: DCI Of S - dci_of_t: - name: DCI Of T +<<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp32-c3-ard.yaml b/tests/components/havells_solar/test.esp32-c3-ard.yaml index 5cb911cf71..452031a5aa 100644 --- a/tests/components/havells_solar/test.esp32-c3-ard.yaml +++ b/tests/components/havells_solar/test.esp32-c3-ard.yaml @@ -1,79 +1,6 @@ -uart: - - id: uart_havells_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: havells_solar - update_interval: 60s - phase_a: - voltage: - name: Phase A Voltage - current: - name: Phase A Current - phase_b: - voltage: - name: Voltage Phase B - current: - name: Current Phase B - phase_c: - voltage: - name: Voltage Phase C - current: - name: Current Phase C - pv1: - voltage: - name: PV1 Voltage - current: - name: PV1 Current - active_power: - name: PV1 Active Power - voltage_sampled_by_secondary_cpu: - name: PV1 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV1 Insulation Of +VE To Ground - pv2: - voltage: - name: PV2 Voltage - current: - name: PV2 Current - active_power: - name: PV2 Active Power - voltage_sampled_by_secondary_cpu: - name: PV2 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV2 Insulation Of +VE To Ground - active_power: - name: Active Power - reactive_power: - name: Reactive Power - frequency: - name: Frequency - energy_production_day: - name: Today's Generation - total_energy_production: - name: Total Energy Production - total_generation_time: - name: Total Generation Time - today_generation_time: - name: Today Generation Time - inverter_module_temp: - name: Inverter Module Temp - inverter_inner_temp: - name: Inverter Inner Temp - inverter_bus_voltage: - name: Inverter BUS Voltage - insulation_of_pv_n_to_ground: - name: Insulation Of PV- To Ground - gfci_value: - name: GFCI Value - dci_of_r: - name: DCI Of R - dci_of_s: - name: DCI Of S - dci_of_t: - name: DCI Of T +<<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp32-c3-idf.yaml b/tests/components/havells_solar/test.esp32-c3-idf.yaml index 5cb911cf71..452031a5aa 100644 --- a/tests/components/havells_solar/test.esp32-c3-idf.yaml +++ b/tests/components/havells_solar/test.esp32-c3-idf.yaml @@ -1,79 +1,6 @@ -uart: - - id: uart_havells_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: havells_solar - update_interval: 60s - phase_a: - voltage: - name: Phase A Voltage - current: - name: Phase A Current - phase_b: - voltage: - name: Voltage Phase B - current: - name: Current Phase B - phase_c: - voltage: - name: Voltage Phase C - current: - name: Current Phase C - pv1: - voltage: - name: PV1 Voltage - current: - name: PV1 Current - active_power: - name: PV1 Active Power - voltage_sampled_by_secondary_cpu: - name: PV1 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV1 Insulation Of +VE To Ground - pv2: - voltage: - name: PV2 Voltage - current: - name: PV2 Current - active_power: - name: PV2 Active Power - voltage_sampled_by_secondary_cpu: - name: PV2 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV2 Insulation Of +VE To Ground - active_power: - name: Active Power - reactive_power: - name: Reactive Power - frequency: - name: Frequency - energy_production_day: - name: Today's Generation - total_energy_production: - name: Total Energy Production - total_generation_time: - name: Total Generation Time - today_generation_time: - name: Today Generation Time - inverter_module_temp: - name: Inverter Module Temp - inverter_inner_temp: - name: Inverter Inner Temp - inverter_bus_voltage: - name: Inverter BUS Voltage - insulation_of_pv_n_to_ground: - name: Insulation Of PV- To Ground - gfci_value: - name: GFCI Value - dci_of_r: - name: DCI Of R - dci_of_s: - name: DCI Of S - dci_of_t: - name: DCI Of T +<<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp32-idf.yaml b/tests/components/havells_solar/test.esp32-idf.yaml index 2cda8e37be..bd767a8ece 100644 --- a/tests/components/havells_solar/test.esp32-idf.yaml +++ b/tests/components/havells_solar/test.esp32-idf.yaml @@ -1,79 +1,6 @@ -uart: - - id: uart_havells_solar - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 3 - -sensor: - - platform: havells_solar - update_interval: 60s - phase_a: - voltage: - name: Phase A Voltage - current: - name: Phase A Current - phase_b: - voltage: - name: Voltage Phase B - current: - name: Current Phase B - phase_c: - voltage: - name: Voltage Phase C - current: - name: Current Phase C - pv1: - voltage: - name: PV1 Voltage - current: - name: PV1 Current - active_power: - name: PV1 Active Power - voltage_sampled_by_secondary_cpu: - name: PV1 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV1 Insulation Of +VE To Ground - pv2: - voltage: - name: PV2 Voltage - current: - name: PV2 Current - active_power: - name: PV2 Active Power - voltage_sampled_by_secondary_cpu: - name: PV2 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV2 Insulation Of +VE To Ground - active_power: - name: Active Power - reactive_power: - name: Reactive Power - frequency: - name: Frequency - energy_production_day: - name: Today's Generation - total_energy_production: - name: Total Energy Production - total_generation_time: - name: Total Generation Time - today_generation_time: - name: Today Generation Time - inverter_module_temp: - name: Inverter Module Temp - inverter_inner_temp: - name: Inverter Inner Temp - inverter_bus_voltage: - name: Inverter BUS Voltage - insulation_of_pv_n_to_ground: - name: Insulation Of PV- To Ground - gfci_value: - name: GFCI Value - dci_of_r: - name: DCI Of R - dci_of_s: - name: DCI Of S - dci_of_t: - name: DCI Of T +<<: !include common.yaml diff --git a/tests/components/havells_solar/test.esp8266-ard.yaml b/tests/components/havells_solar/test.esp8266-ard.yaml index 5cb911cf71..29c98d0957 100644 --- a/tests/components/havells_solar/test.esp8266-ard.yaml +++ b/tests/components/havells_solar/test.esp8266-ard.yaml @@ -1,79 +1,6 @@ -uart: - - id: uart_havells_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 3 - -sensor: - - platform: havells_solar - update_interval: 60s - phase_a: - voltage: - name: Phase A Voltage - current: - name: Phase A Current - phase_b: - voltage: - name: Voltage Phase B - current: - name: Current Phase B - phase_c: - voltage: - name: Voltage Phase C - current: - name: Current Phase C - pv1: - voltage: - name: PV1 Voltage - current: - name: PV1 Current - active_power: - name: PV1 Active Power - voltage_sampled_by_secondary_cpu: - name: PV1 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV1 Insulation Of +VE To Ground - pv2: - voltage: - name: PV2 Voltage - current: - name: PV2 Current - active_power: - name: PV2 Active Power - voltage_sampled_by_secondary_cpu: - name: PV2 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV2 Insulation Of +VE To Ground - active_power: - name: Active Power - reactive_power: - name: Reactive Power - frequency: - name: Frequency - energy_production_day: - name: Today's Generation - total_energy_production: - name: Total Energy Production - total_generation_time: - name: Total Generation Time - today_generation_time: - name: Today Generation Time - inverter_module_temp: - name: Inverter Module Temp - inverter_inner_temp: - name: Inverter Inner Temp - inverter_bus_voltage: - name: Inverter BUS Voltage - insulation_of_pv_n_to_ground: - name: Insulation Of PV- To Ground - gfci_value: - name: GFCI Value - dci_of_r: - name: DCI Of R - dci_of_s: - name: DCI Of S - dci_of_t: - name: DCI Of T +<<: !include common.yaml diff --git a/tests/components/havells_solar/test.rp2040-ard.yaml b/tests/components/havells_solar/test.rp2040-ard.yaml index 5cb911cf71..452031a5aa 100644 --- a/tests/components/havells_solar/test.rp2040-ard.yaml +++ b/tests/components/havells_solar/test.rp2040-ard.yaml @@ -1,79 +1,6 @@ -uart: - - id: uart_havells_solar - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: havells_solar - update_interval: 60s - phase_a: - voltage: - name: Phase A Voltage - current: - name: Phase A Current - phase_b: - voltage: - name: Voltage Phase B - current: - name: Current Phase B - phase_c: - voltage: - name: Voltage Phase C - current: - name: Current Phase C - pv1: - voltage: - name: PV1 Voltage - current: - name: PV1 Current - active_power: - name: PV1 Active Power - voltage_sampled_by_secondary_cpu: - name: PV1 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV1 Insulation Of +VE To Ground - pv2: - voltage: - name: PV2 Voltage - current: - name: PV2 Current - active_power: - name: PV2 Active Power - voltage_sampled_by_secondary_cpu: - name: PV2 Voltage Sampled By Secondary CPU - insulation_of_p_to_ground: - name: PV2 Insulation Of +VE To Ground - active_power: - name: Active Power - reactive_power: - name: Reactive Power - frequency: - name: Frequency - energy_production_day: - name: Today's Generation - total_energy_production: - name: Total Energy Production - total_generation_time: - name: Total Generation Time - today_generation_time: - name: Today Generation Time - inverter_module_temp: - name: Inverter Module Temp - inverter_inner_temp: - name: Inverter Inner Temp - inverter_bus_voltage: - name: Inverter BUS Voltage - insulation_of_pv_n_to_ground: - name: Insulation Of PV- To Ground - gfci_value: - name: GFCI Value - dci_of_r: - name: DCI Of R - dci_of_s: - name: DCI Of S - dci_of_t: - name: DCI Of T +<<: !include common.yaml diff --git a/tests/components/hdc1080/common.yaml b/tests/components/hdc1080/common.yaml new file mode 100644 index 0000000000..d260d4737c --- /dev/null +++ b/tests/components/hdc1080/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hdc1080 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: hdc1080 + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/hdc1080/test.esp32-ard.yaml b/tests/components/hdc1080/test.esp32-ard.yaml index 8e313dfa40..63c3bd6afd 100644 --- a/tests/components/hdc1080/test.esp32-ard.yaml +++ b/tests/components/hdc1080/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hdc1080 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hdc1080 - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hdc1080/test.esp32-c3-ard.yaml b/tests/components/hdc1080/test.esp32-c3-ard.yaml index 7bf7af6fa7..ee2c29ca4e 100644 --- a/tests/components/hdc1080/test.esp32-c3-ard.yaml +++ b/tests/components/hdc1080/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hdc1080 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hdc1080 - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hdc1080/test.esp32-c3-idf.yaml b/tests/components/hdc1080/test.esp32-c3-idf.yaml index 7bf7af6fa7..ee2c29ca4e 100644 --- a/tests/components/hdc1080/test.esp32-c3-idf.yaml +++ b/tests/components/hdc1080/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hdc1080 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hdc1080 - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hdc1080/test.esp32-idf.yaml b/tests/components/hdc1080/test.esp32-idf.yaml index 8e313dfa40..63c3bd6afd 100644 --- a/tests/components/hdc1080/test.esp32-idf.yaml +++ b/tests/components/hdc1080/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hdc1080 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hdc1080 - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hdc1080/test.esp8266-ard.yaml b/tests/components/hdc1080/test.esp8266-ard.yaml index 7bf7af6fa7..ee2c29ca4e 100644 --- a/tests/components/hdc1080/test.esp8266-ard.yaml +++ b/tests/components/hdc1080/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hdc1080 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hdc1080 - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hdc1080/test.rp2040-ard.yaml b/tests/components/hdc1080/test.rp2040-ard.yaml index 7bf7af6fa7..ee2c29ca4e 100644 --- a/tests/components/hdc1080/test.rp2040-ard.yaml +++ b/tests/components/hdc1080/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hdc1080 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hdc1080 - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/he60r/common.yaml b/tests/components/he60r/common.yaml new file mode 100644 index 0000000000..e10e745f57 --- /dev/null +++ b/tests/components/he60r/common.yaml @@ -0,0 +1,13 @@ +uart: + - id: uart_he60r + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN + +cover: + - platform: he60r + id: garage_door + name: Garage Door + open_duration: 14s + close_duration: 14s diff --git a/tests/components/he60r/test.esp32-ard.yaml b/tests/components/he60r/test.esp32-ard.yaml index 840387ae36..f486544afa 100644 --- a/tests/components/he60r/test.esp32-ard.yaml +++ b/tests/components/he60r/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_he60r - tx_pin: 17 - rx_pin: 16 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -cover: - - platform: he60r - id: garage_door - name: Garage Door - open_duration: 14s - close_duration: 14s +<<: !include common.yaml diff --git a/tests/components/he60r/test.esp32-c3-ard.yaml b/tests/components/he60r/test.esp32-c3-ard.yaml index bcbb544442..b516342f3b 100644 --- a/tests/components/he60r/test.esp32-c3-ard.yaml +++ b/tests/components/he60r/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_he60r - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -cover: - - platform: he60r - id: garage_door - name: Garage Door - open_duration: 14s - close_duration: 14s +<<: !include common.yaml diff --git a/tests/components/he60r/test.esp32-c3-idf.yaml b/tests/components/he60r/test.esp32-c3-idf.yaml index bcbb544442..b516342f3b 100644 --- a/tests/components/he60r/test.esp32-c3-idf.yaml +++ b/tests/components/he60r/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_he60r - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -cover: - - platform: he60r - id: garage_door - name: Garage Door - open_duration: 14s - close_duration: 14s +<<: !include common.yaml diff --git a/tests/components/he60r/test.esp32-idf.yaml b/tests/components/he60r/test.esp32-idf.yaml index 840387ae36..f486544afa 100644 --- a/tests/components/he60r/test.esp32-idf.yaml +++ b/tests/components/he60r/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_he60r - tx_pin: 17 - rx_pin: 16 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -cover: - - platform: he60r - id: garage_door - name: Garage Door - open_duration: 14s - close_duration: 14s +<<: !include common.yaml diff --git a/tests/components/he60r/test.esp8266-ard.yaml b/tests/components/he60r/test.esp8266-ard.yaml index bcbb544442..b516342f3b 100644 --- a/tests/components/he60r/test.esp8266-ard.yaml +++ b/tests/components/he60r/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_he60r - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -cover: - - platform: he60r - id: garage_door - name: Garage Door - open_duration: 14s - close_duration: 14s +<<: !include common.yaml diff --git a/tests/components/he60r/test.rp2040-ard.yaml b/tests/components/he60r/test.rp2040-ard.yaml index bcbb544442..b516342f3b 100644 --- a/tests/components/he60r/test.rp2040-ard.yaml +++ b/tests/components/he60r/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -uart: - - id: uart_he60r - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -cover: - - platform: he60r - id: garage_door - name: Garage Door - open_duration: 14s - close_duration: 14s +<<: !include common.yaml diff --git a/tests/components/heatpumpir/common.yaml b/tests/components/heatpumpir/common.yaml new file mode 100644 index 0000000000..2df195c5de --- /dev/null +++ b/tests/components/heatpumpir/common.yaml @@ -0,0 +1,26 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: heatpumpir + protocol: mitsubishi_heavy_zm + horizontal_default: left + vertical_default: up + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 + - platform: heatpumpir + protocol: daikin + horizontal_default: mleft + vertical_default: mup + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 + - platform: heatpumpir + protocol: panasonic_altdke + horizontal_default: mright + vertical_default: mdown + name: HeatpumpIR Climate + min_temperature: 18 + max_temperature: 30 diff --git a/tests/components/heatpumpir/test.bk72xx-ard.yaml b/tests/components/heatpumpir/test.bk72xx-ard.yaml index b616f9157c..06e1aea364 100644 --- a/tests/components/heatpumpir/test.bk72xx-ard.yaml +++ b/tests/components/heatpumpir/test.bk72xx-ard.yaml @@ -1,26 +1,4 @@ -remote_transmitter: - pin: 6 - carrier_duty_percent: 50% +substitutions: + pin: GPIO6 -climate: - - platform: heatpumpir - protocol: mitsubishi_heavy_zm - horizontal_default: left - vertical_default: up - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 - - platform: heatpumpir - protocol: daikin - horizontal_default: mleft - vertical_default: mup - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 - - platform: heatpumpir - protocol: panasonic_altdke - horizontal_default: mright - vertical_default: mdown - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 +<<: !include common.yaml diff --git a/tests/components/heatpumpir/test.esp32-ard.yaml b/tests/components/heatpumpir/test.esp32-ard.yaml index db3f81f6a0..7b012aa64c 100644 --- a/tests/components/heatpumpir/test.esp32-ard.yaml +++ b/tests/components/heatpumpir/test.esp32-ard.yaml @@ -1,19 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: heatpumpir - protocol: mitsubishi_heavy_zm - horizontal_default: left - vertical_default: up - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 - - platform: heatpumpir - protocol: greeyt - horizontal_default: left - vertical_default: up - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 +<<: !include common.yaml diff --git a/tests/components/heatpumpir/test.esp32-c3-ard.yaml b/tests/components/heatpumpir/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..7b012aa64c --- /dev/null +++ b/tests/components/heatpumpir/test.esp32-c3-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO2 + +<<: !include common.yaml diff --git a/tests/components/heatpumpir/test.esp8266-ard.yaml b/tests/components/heatpumpir/test.esp8266-ard.yaml index 26a01cb198..f5097fcf5f 100644 --- a/tests/components/heatpumpir/test.esp8266-ard.yaml +++ b/tests/components/heatpumpir/test.esp8266-ard.yaml @@ -1,19 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: heatpumpir - protocol: mitsubishi_heavy_zm - horizontal_default: left - vertical_default: up - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 - - platform: heatpumpir - protocol: greeyt - horizontal_default: left - vertical_default: up - name: HeatpumpIR Climate - min_temperature: 18 - max_temperature: 30 +<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/common.yaml b/tests/components/hitachi_ac344/common.yaml new file mode 100644 index 0000000000..960f032035 --- /dev/null +++ b/tests/components/hitachi_ac344/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac344 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac344/test.bk72xx-ard.yaml b/tests/components/hitachi_ac344/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..06e1aea364 --- /dev/null +++ b/tests/components/hitachi_ac344/test.bk72xx-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO6 + +<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-ard.yaml b/tests/components/hitachi_ac344/test.esp32-ard.yaml index 684d5899de..7b012aa64c 100644 --- a/tests/components/hitachi_ac344/test.esp32-ard.yaml +++ b/tests/components/hitachi_ac344/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac344 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-c3-ard.yaml b/tests/components/hitachi_ac344/test.esp32-c3-ard.yaml index 684d5899de..7b012aa64c 100644 --- a/tests/components/hitachi_ac344/test.esp32-c3-ard.yaml +++ b/tests/components/hitachi_ac344/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac344 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml index 684d5899de..7b012aa64c 100644 --- a/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml +++ b/tests/components/hitachi_ac344/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac344 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp32-idf.yaml b/tests/components/hitachi_ac344/test.esp32-idf.yaml index 684d5899de..7b012aa64c 100644 --- a/tests/components/hitachi_ac344/test.esp32-idf.yaml +++ b/tests/components/hitachi_ac344/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac344 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac344/test.esp8266-ard.yaml b/tests/components/hitachi_ac344/test.esp8266-ard.yaml index e6203e3084..f5097fcf5f 100644 --- a/tests/components/hitachi_ac344/test.esp8266-ard.yaml +++ b/tests/components/hitachi_ac344/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: hitachi_ac344 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/common.yaml b/tests/components/hitachi_ac424/common.yaml new file mode 100644 index 0000000000..ad904c73a3 --- /dev/null +++ b/tests/components/hitachi_ac424/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: hitachi_ac424 + name: Hitachi Climate diff --git a/tests/components/hitachi_ac424/test.bk72xx-ard.yaml b/tests/components/hitachi_ac424/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..06e1aea364 --- /dev/null +++ b/tests/components/hitachi_ac424/test.bk72xx-ard.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO6 + +<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-ard.yaml b/tests/components/hitachi_ac424/test.esp32-ard.yaml index a09821b9c6..7b012aa64c 100644 --- a/tests/components/hitachi_ac424/test.esp32-ard.yaml +++ b/tests/components/hitachi_ac424/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac424 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-c3-ard.yaml b/tests/components/hitachi_ac424/test.esp32-c3-ard.yaml index a09821b9c6..7b012aa64c 100644 --- a/tests/components/hitachi_ac424/test.esp32-c3-ard.yaml +++ b/tests/components/hitachi_ac424/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac424 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml b/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml index a09821b9c6..7b012aa64c 100644 --- a/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml +++ b/tests/components/hitachi_ac424/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac424 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp32-idf.yaml b/tests/components/hitachi_ac424/test.esp32-idf.yaml index a09821b9c6..7b012aa64c 100644 --- a/tests/components/hitachi_ac424/test.esp32-idf.yaml +++ b/tests/components/hitachi_ac424/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: hitachi_ac424 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hitachi_ac424/test.esp8266-ard.yaml b/tests/components/hitachi_ac424/test.esp8266-ard.yaml index 78b9e7c98c..f5097fcf5f 100644 --- a/tests/components/hitachi_ac424/test.esp8266-ard.yaml +++ b/tests/components/hitachi_ac424/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: hitachi_ac424 - name: Hitachi Climate +<<: !include common.yaml diff --git a/tests/components/hlw8012/common.yaml b/tests/components/hlw8012/common.yaml new file mode 100644 index 0000000000..d0d376a43a --- /dev/null +++ b/tests/components/hlw8012/common.yaml @@ -0,0 +1,21 @@ +sensor: + - platform: hlw8012 + model: hlw8012 + sel_pin: ${sel_pin} + cf_pin: ${cf_pin} + cf1_pin: ${cf1_pin} + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE diff --git a/tests/components/hlw8012/test.esp32-ard.yaml b/tests/components/hlw8012/test.esp32-ard.yaml index 5b2d865722..8b42b21b54 100644 --- a/tests/components/hlw8012/test.esp32-ard.yaml +++ b/tests/components/hlw8012/test.esp32-ard.yaml @@ -1,21 +1,6 @@ -sensor: - - platform: hlw8012 - model: hlw8012 - sel_pin: 12 - cf_pin: 14 - cf1_pin: 13 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE +substitutions: + sel_pin: GPIO12 + cf_pin: GPIO13 + cf1_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp32-c3-ard.yaml b/tests/components/hlw8012/test.esp32-c3-ard.yaml index da6053a1b9..8b0d069ce2 100644 --- a/tests/components/hlw8012/test.esp32-c3-ard.yaml +++ b/tests/components/hlw8012/test.esp32-c3-ard.yaml @@ -1,21 +1,6 @@ -sensor: - - platform: hlw8012 - model: hlw8012 - sel_pin: 2 - cf_pin: 4 - cf1_pin: 3 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE +substitutions: + sel_pin: GPIO2 + cf_pin: GPIO3 + cf1_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp32-c3-idf.yaml b/tests/components/hlw8012/test.esp32-c3-idf.yaml index da6053a1b9..8b0d069ce2 100644 --- a/tests/components/hlw8012/test.esp32-c3-idf.yaml +++ b/tests/components/hlw8012/test.esp32-c3-idf.yaml @@ -1,21 +1,6 @@ -sensor: - - platform: hlw8012 - model: hlw8012 - sel_pin: 2 - cf_pin: 4 - cf1_pin: 3 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE +substitutions: + sel_pin: GPIO2 + cf_pin: GPIO3 + cf1_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp32-idf.yaml b/tests/components/hlw8012/test.esp32-idf.yaml index 5b2d865722..8b42b21b54 100644 --- a/tests/components/hlw8012/test.esp32-idf.yaml +++ b/tests/components/hlw8012/test.esp32-idf.yaml @@ -1,21 +1,6 @@ -sensor: - - platform: hlw8012 - model: hlw8012 - sel_pin: 12 - cf_pin: 14 - cf1_pin: 13 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE +substitutions: + sel_pin: GPIO12 + cf_pin: GPIO13 + cf1_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/hlw8012/test.esp8266-ard.yaml b/tests/components/hlw8012/test.esp8266-ard.yaml index 5b2d865722..8b42b21b54 100644 --- a/tests/components/hlw8012/test.esp8266-ard.yaml +++ b/tests/components/hlw8012/test.esp8266-ard.yaml @@ -1,21 +1,6 @@ -sensor: - - platform: hlw8012 - model: hlw8012 - sel_pin: 12 - cf_pin: 14 - cf1_pin: 13 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE +substitutions: + sel_pin: GPIO12 + cf_pin: GPIO13 + cf1_pin: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/hlw8012/test.rp2040-ard.yaml b/tests/components/hlw8012/test.rp2040-ard.yaml index da6053a1b9..8b0d069ce2 100644 --- a/tests/components/hlw8012/test.rp2040-ard.yaml +++ b/tests/components/hlw8012/test.rp2040-ard.yaml @@ -1,21 +1,6 @@ -sensor: - - platform: hlw8012 - model: hlw8012 - sel_pin: 2 - cf_pin: 4 - cf1_pin: 3 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE +substitutions: + sel_pin: GPIO2 + cf_pin: GPIO3 + cf1_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hm3301/common.yaml b/tests/components/hm3301/common.yaml new file mode 100644 index 0000000000..b533130569 --- /dev/null +++ b/tests/components/hm3301/common.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_hm3301 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: hm3301 + pm_1_0: + name: PM1.0 + pm_2_5: + name: PM2.5 + pm_10_0: + name: PM10.0 + aqi: + name: AQI + calculation_type: CAQI diff --git a/tests/components/hm3301/test.esp32-ard.yaml b/tests/components/hm3301/test.esp32-ard.yaml index 413e88a265..63c3bd6afd 100644 --- a/tests/components/hm3301/test.esp32-ard.yaml +++ b/tests/components/hm3301/test.esp32-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_hm3301 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hm3301 - pm_1_0: - name: PM1.0 - pm_2_5: - name: PM2.5 - pm_10_0: - name: PM10.0 - aqi: - name: AQI - calculation_type: CAQI +<<: !include common.yaml diff --git a/tests/components/hm3301/test.esp32-c3-ard.yaml b/tests/components/hm3301/test.esp32-c3-ard.yaml index eb6c4a14e6..ee2c29ca4e 100644 --- a/tests/components/hm3301/test.esp32-c3-ard.yaml +++ b/tests/components/hm3301/test.esp32-c3-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_hm3301 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hm3301 - pm_1_0: - name: PM1.0 - pm_2_5: - name: PM2.5 - pm_10_0: - name: PM10.0 - aqi: - name: AQI - calculation_type: CAQI +<<: !include common.yaml diff --git a/tests/components/hm3301/test.esp32-c3-idf.yaml b/tests/components/hm3301/test.esp32-c3-idf.yaml index eb6c4a14e6..ee2c29ca4e 100644 --- a/tests/components/hm3301/test.esp32-c3-idf.yaml +++ b/tests/components/hm3301/test.esp32-c3-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_hm3301 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hm3301 - pm_1_0: - name: PM1.0 - pm_2_5: - name: PM2.5 - pm_10_0: - name: PM10.0 - aqi: - name: AQI - calculation_type: CAQI +<<: !include common.yaml diff --git a/tests/components/hm3301/test.esp32-idf.yaml b/tests/components/hm3301/test.esp32-idf.yaml index 413e88a265..63c3bd6afd 100644 --- a/tests/components/hm3301/test.esp32-idf.yaml +++ b/tests/components/hm3301/test.esp32-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_hm3301 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hm3301 - pm_1_0: - name: PM1.0 - pm_2_5: - name: PM2.5 - pm_10_0: - name: PM10.0 - aqi: - name: AQI - calculation_type: CAQI +<<: !include common.yaml diff --git a/tests/components/hm3301/test.esp8266-ard.yaml b/tests/components/hm3301/test.esp8266-ard.yaml index eb6c4a14e6..ee2c29ca4e 100644 --- a/tests/components/hm3301/test.esp8266-ard.yaml +++ b/tests/components/hm3301/test.esp8266-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_hm3301 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hm3301 - pm_1_0: - name: PM1.0 - pm_2_5: - name: PM2.5 - pm_10_0: - name: PM10.0 - aqi: - name: AQI - calculation_type: CAQI +<<: !include common.yaml diff --git a/tests/components/hm3301/test.rp2040-ard.yaml b/tests/components/hm3301/test.rp2040-ard.yaml index eb6c4a14e6..ee2c29ca4e 100644 --- a/tests/components/hm3301/test.rp2040-ard.yaml +++ b/tests/components/hm3301/test.rp2040-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_hm3301 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hm3301 - pm_1_0: - name: PM1.0 - pm_2_5: - name: PM2.5 - pm_10_0: - name: PM10.0 - aqi: - name: AQI - calculation_type: CAQI +<<: !include common.yaml diff --git a/tests/components/hmc5883l/common.yaml b/tests/components/hmc5883l/common.yaml new file mode 100644 index 0000000000..1c90f5f1c6 --- /dev/null +++ b/tests/components/hmc5883l/common.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_hmc5883l + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: hmc5883l + address: 0x68 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z + heading: + name: HMC5883L Heading + range: 130uT + oversampling: 8x + update_interval: 15s diff --git a/tests/components/hmc5883l/test.esp32-ard.yaml b/tests/components/hmc5883l/test.esp32-ard.yaml index db632fc411..63c3bd6afd 100644 --- a/tests/components/hmc5883l/test.esp32-ard.yaml +++ b/tests/components/hmc5883l/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_hmc5883l - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hmc5883l - address: 0x68 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z - heading: - name: HMC5883L Heading - range: 130uT - oversampling: 8x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp32-c3-ard.yaml b/tests/components/hmc5883l/test.esp32-c3-ard.yaml index e65b2e5c5b..ee2c29ca4e 100644 --- a/tests/components/hmc5883l/test.esp32-c3-ard.yaml +++ b/tests/components/hmc5883l/test.esp32-c3-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_hmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hmc5883l - address: 0x68 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z - heading: - name: HMC5883L Heading - range: 130uT - oversampling: 8x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp32-c3-idf.yaml b/tests/components/hmc5883l/test.esp32-c3-idf.yaml index e65b2e5c5b..ee2c29ca4e 100644 --- a/tests/components/hmc5883l/test.esp32-c3-idf.yaml +++ b/tests/components/hmc5883l/test.esp32-c3-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_hmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hmc5883l - address: 0x68 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z - heading: - name: HMC5883L Heading - range: 130uT - oversampling: 8x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp32-idf.yaml b/tests/components/hmc5883l/test.esp32-idf.yaml index db632fc411..63c3bd6afd 100644 --- a/tests/components/hmc5883l/test.esp32-idf.yaml +++ b/tests/components/hmc5883l/test.esp32-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_hmc5883l - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hmc5883l - address: 0x68 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z - heading: - name: HMC5883L Heading - range: 130uT - oversampling: 8x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.esp8266-ard.yaml b/tests/components/hmc5883l/test.esp8266-ard.yaml index e65b2e5c5b..ee2c29ca4e 100644 --- a/tests/components/hmc5883l/test.esp8266-ard.yaml +++ b/tests/components/hmc5883l/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_hmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hmc5883l - address: 0x68 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z - heading: - name: HMC5883L Heading - range: 130uT - oversampling: 8x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/hmc5883l/test.rp2040-ard.yaml b/tests/components/hmc5883l/test.rp2040-ard.yaml index e65b2e5c5b..ee2c29ca4e 100644 --- a/tests/components/hmc5883l/test.rp2040-ard.yaml +++ b/tests/components/hmc5883l/test.rp2040-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_hmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hmc5883l - address: 0x68 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z - heading: - name: HMC5883L Heading - range: 130uT - oversampling: 8x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/common.yaml b/tests/components/honeywell_hih_i2c/common.yaml new file mode 100644 index 0000000000..a5f3eef187 --- /dev/null +++ b/tests/components/honeywell_hih_i2c/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_honeywell_hih + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: honeywell_hih_i2c + temperature: + name: Temperature + humidity: + name: Humidity + update_interval: 15s diff --git a/tests/components/honeywell_hih_i2c/test.esp32-ard.yaml b/tests/components/honeywell_hih_i2c/test.esp32-ard.yaml index 0119aec3f3..63c3bd6afd 100644 --- a/tests/components/honeywell_hih_i2c/test.esp32-ard.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_honeywell_hih - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: honeywell_hih_i2c - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml b/tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml index 5dae3d5d52..ee2c29ca4e 100644 --- a/tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_honeywell_hih - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywell_hih_i2c - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml index 5dae3d5d52..ee2c29ca4e 100644 --- a/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_honeywell_hih - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywell_hih_i2c - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml b/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml index 0119aec3f3..63c3bd6afd 100644 --- a/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_honeywell_hih - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: honeywell_hih_i2c - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml b/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml index 5dae3d5d52..ee2c29ca4e 100644 --- a/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml +++ b/tests/components/honeywell_hih_i2c/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_honeywell_hih - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywell_hih_i2c - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml b/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml index 5dae3d5d52..ee2c29ca4e 100644 --- a/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml +++ b/tests/components/honeywell_hih_i2c/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_honeywell_hih - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywell_hih_i2c - temperature: - name: Temperature - humidity: - name: Humidity - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/honeywellabp/common.yaml b/tests/components/honeywellabp/common.yaml new file mode 100644 index 0000000000..21a3ef6ee3 --- /dev/null +++ b/tests/components/honeywellabp/common.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_bme280 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: honeywellabp + cs_pin: ${cs_pin} + pressure: + name: Honeywell pressure + min_pressure: 0 + max_pressure: 15 + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp/test.esp32-ard.yaml b/tests/components/honeywellabp/test.esp32-ard.yaml index 6bf9fa0f4d..54e027a614 100644 --- a/tests/components/honeywellabp/test.esp32-ard.yaml +++ b/tests/components/honeywellabp/test.esp32-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_bme280 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: honeywellabp - cs_pin: 12 - pressure: - name: Honeywell pressure - min_pressure: 0 - max_pressure: 15 - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp32-c3-ard.yaml b/tests/components/honeywellabp/test.esp32-c3-ard.yaml index a53e3296dd..2415ba5dc6 100644 --- a/tests/components/honeywellabp/test.esp32-c3-ard.yaml +++ b/tests/components/honeywellabp/test.esp32-c3-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_honeywellabp - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: honeywellabp - cs_pin: 8 - pressure: - name: Honeywell pressure - min_pressure: 0 - max_pressure: 15 - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp32-c3-idf.yaml b/tests/components/honeywellabp/test.esp32-c3-idf.yaml index a53e3296dd..2415ba5dc6 100644 --- a/tests/components/honeywellabp/test.esp32-c3-idf.yaml +++ b/tests/components/honeywellabp/test.esp32-c3-idf.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_honeywellabp - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: honeywellabp - cs_pin: 8 - pressure: - name: Honeywell pressure - min_pressure: 0 - max_pressure: 15 - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp32-idf.yaml b/tests/components/honeywellabp/test.esp32-idf.yaml index 6bf9fa0f4d..54e027a614 100644 --- a/tests/components/honeywellabp/test.esp32-idf.yaml +++ b/tests/components/honeywellabp/test.esp32-idf.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_bme280 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: honeywellabp - cs_pin: 12 - pressure: - name: Honeywell pressure - min_pressure: 0 - max_pressure: 15 - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.esp8266-ard.yaml b/tests/components/honeywellabp/test.esp8266-ard.yaml index 31988c035e..dbd158d030 100644 --- a/tests/components/honeywellabp/test.esp8266-ard.yaml +++ b/tests/components/honeywellabp/test.esp8266-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_bme280 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -sensor: - - platform: honeywellabp - cs_pin: 15 - pressure: - name: Honeywell pressure - min_pressure: 0 - max_pressure: 15 - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp/test.rp2040-ard.yaml b/tests/components/honeywellabp/test.rp2040-ard.yaml index 2e0c471fa0..f6c3f1eeca 100644 --- a/tests/components/honeywellabp/test.rp2040-ard.yaml +++ b/tests/components/honeywellabp/test.rp2040-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_bme280 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -sensor: - - platform: honeywellabp - cs_pin: 6 - pressure: - name: Honeywell pressure - min_pressure: 0 - max_pressure: 15 - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/common.yaml b/tests/components/honeywellabp2_i2c/common.yaml new file mode 100644 index 0000000000..6752a69866 --- /dev/null +++ b/tests/components/honeywellabp2_i2c/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_honeywellabp2 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: honeywellabp2_i2c + address: 0x28 + pressure: + name: Honeywell2 pressure + min_pressure: 0 + max_pressure: 16000 + transfer_function: A + temperature: + name: Honeywell temperature diff --git a/tests/components/honeywellabp2_i2c/test.esp32-ard.yaml b/tests/components/honeywellabp2_i2c/test.esp32-ard.yaml index 0f0d61ca06..63c3bd6afd 100644 --- a/tests/components/honeywellabp2_i2c/test.esp32-ard.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_honeywellabp2 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: honeywellabp2_i2c - address: 0x28 - pressure: - name: Honeywell2 pressure - min_pressure: 0 - max_pressure: 16000 - transfer_function: A - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml b/tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml index b47411c238..ee2c29ca4e 100644 --- a/tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_honeywellabp2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywellabp2_i2c - address: 0x28 - pressure: - name: Honeywell2 pressure - min_pressure: 0 - max_pressure: 16000 - transfer_function: A - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml b/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml index b47411c238..ee2c29ca4e 100644 --- a/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_honeywellabp2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywellabp2_i2c - address: 0x28 - pressure: - name: Honeywell2 pressure - min_pressure: 0 - max_pressure: 16000 - transfer_function: A - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml b/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml index 0f0d61ca06..63c3bd6afd 100644 --- a/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_honeywellabp2 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: honeywellabp2_i2c - address: 0x28 - pressure: - name: Honeywell2 pressure - min_pressure: 0 - max_pressure: 16000 - transfer_function: A - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml b/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml index b47411c238..ee2c29ca4e 100644 --- a/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml +++ b/tests/components/honeywellabp2_i2c/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_honeywellabp2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywellabp2_i2c - address: 0x28 - pressure: - name: Honeywell2 pressure - min_pressure: 0 - max_pressure: 16000 - transfer_function: A - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml b/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml index b47411c238..ee2c29ca4e 100644 --- a/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml +++ b/tests/components/honeywellabp2_i2c/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_honeywellabp2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: honeywellabp2_i2c - address: 0x28 - pressure: - name: Honeywell2 pressure - min_pressure: 0 - max_pressure: 16000 - transfer_function: A - temperature: - name: Honeywell temperature +<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/common.yaml b/tests/components/hrxl_maxsonar_wr/common.yaml new file mode 100644 index 0000000000..d74ada716d --- /dev/null +++ b/tests/components/hrxl_maxsonar_wr/common.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_hrxl_maxsonar_wr + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +sensor: + - platform: hrxl_maxsonar_wr + id: hrxl_maxsonar_wr_sensor + name: Rainwater Tank Level diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml index da283cc072..811f6b72a6 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_hrxl_maxsonar_wr - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: hrxl_maxsonar_wr - id: hrxl_maxsonar_wr_sensor - name: Rainwater Tank Level +<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml index 729cb96120..b516342f3b 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_hrxl_maxsonar_wr - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: hrxl_maxsonar_wr - id: hrxl_maxsonar_wr_sensor - name: Rainwater Tank Level +<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml index 729cb96120..b516342f3b 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_hrxl_maxsonar_wr - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: hrxl_maxsonar_wr - id: hrxl_maxsonar_wr_sensor - name: Rainwater Tank Level +<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml b/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml index da283cc072..811f6b72a6 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_hrxl_maxsonar_wr - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 -sensor: - - platform: hrxl_maxsonar_wr - id: hrxl_maxsonar_wr_sensor - name: Rainwater Tank Level +<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml index 729cb96120..b516342f3b 100644 --- a/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_hrxl_maxsonar_wr - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: hrxl_maxsonar_wr - id: hrxl_maxsonar_wr_sensor - name: Rainwater Tank Level +<<: !include common.yaml diff --git a/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml b/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml index 729cb96120..b516342f3b 100644 --- a/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml +++ b/tests/components/hrxl_maxsonar_wr/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_hrxl_maxsonar_wr - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: hrxl_maxsonar_wr - id: hrxl_maxsonar_wr_sensor - name: Rainwater Tank Level +<<: !include common.yaml diff --git a/tests/components/hte501/common.yaml b/tests/components/hte501/common.yaml new file mode 100644 index 0000000000..7c57b5bc6a --- /dev/null +++ b/tests/components/hte501/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_hte501 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: hte501 + address: 0x40 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hte501/test.esp32-ard.yaml b/tests/components/hte501/test.esp32-ard.yaml index 83e4d26603..63c3bd6afd 100644 --- a/tests/components/hte501/test.esp32-ard.yaml +++ b/tests/components/hte501/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hte501 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hte501 - address: 0x40 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hte501/test.esp32-c3-ard.yaml b/tests/components/hte501/test.esp32-c3-ard.yaml index e14b589dbd..ee2c29ca4e 100644 --- a/tests/components/hte501/test.esp32-c3-ard.yaml +++ b/tests/components/hte501/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hte501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hte501 - address: 0x40 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hte501/test.esp32-c3-idf.yaml b/tests/components/hte501/test.esp32-c3-idf.yaml index e14b589dbd..ee2c29ca4e 100644 --- a/tests/components/hte501/test.esp32-c3-idf.yaml +++ b/tests/components/hte501/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hte501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hte501 - address: 0x40 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hte501/test.esp32-idf.yaml b/tests/components/hte501/test.esp32-idf.yaml index 83e4d26603..63c3bd6afd 100644 --- a/tests/components/hte501/test.esp32-idf.yaml +++ b/tests/components/hte501/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hte501 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hte501 - address: 0x40 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hte501/test.esp8266-ard.yaml b/tests/components/hte501/test.esp8266-ard.yaml index e14b589dbd..ee2c29ca4e 100644 --- a/tests/components/hte501/test.esp8266-ard.yaml +++ b/tests/components/hte501/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hte501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hte501 - address: 0x40 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hte501/test.rp2040-ard.yaml b/tests/components/hte501/test.rp2040-ard.yaml index e14b589dbd..ee2c29ca4e 100644 --- a/tests/components/hte501/test.rp2040-ard.yaml +++ b/tests/components/hte501/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_hte501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hte501 - address: 0x40 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/htu21d/common.yaml b/tests/components/htu21d/common.yaml new file mode 100644 index 0000000000..f12c1ca46e --- /dev/null +++ b/tests/components/htu21d/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_htu21d + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: htu21d + model: htu21d + temperature: + name: Temperature + humidity: + name: Humidity + heater: + name: Heater + update_interval: 15s diff --git a/tests/components/htu21d/test.esp32-ard.yaml b/tests/components/htu21d/test.esp32-ard.yaml index 6655a1cc1a..63c3bd6afd 100644 --- a/tests/components/htu21d/test.esp32-ard.yaml +++ b/tests/components/htu21d/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_htu21d - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: htu21d - model: htu21d - temperature: - name: Temperature - humidity: - name: Humidity - heater: - name: Heater - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/htu21d/test.esp32-c3-ard.yaml b/tests/components/htu21d/test.esp32-c3-ard.yaml index 8131d13661..ee2c29ca4e 100644 --- a/tests/components/htu21d/test.esp32-c3-ard.yaml +++ b/tests/components/htu21d/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_htu21d - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: htu21d - model: htu21d - temperature: - name: Temperature - humidity: - name: Humidity - heater: - name: Heater - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/htu21d/test.esp32-c3-idf.yaml b/tests/components/htu21d/test.esp32-c3-idf.yaml index 8131d13661..ee2c29ca4e 100644 --- a/tests/components/htu21d/test.esp32-c3-idf.yaml +++ b/tests/components/htu21d/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_htu21d - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: htu21d - model: htu21d - temperature: - name: Temperature - humidity: - name: Humidity - heater: - name: Heater - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/htu21d/test.esp32-idf.yaml b/tests/components/htu21d/test.esp32-idf.yaml index 6655a1cc1a..63c3bd6afd 100644 --- a/tests/components/htu21d/test.esp32-idf.yaml +++ b/tests/components/htu21d/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_htu21d - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: htu21d - model: htu21d - temperature: - name: Temperature - humidity: - name: Humidity - heater: - name: Heater - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/htu21d/test.esp8266-ard.yaml b/tests/components/htu21d/test.esp8266-ard.yaml index 8131d13661..ee2c29ca4e 100644 --- a/tests/components/htu21d/test.esp8266-ard.yaml +++ b/tests/components/htu21d/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_htu21d - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: htu21d - model: htu21d - temperature: - name: Temperature - humidity: - name: Humidity - heater: - name: Heater - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/htu21d/test.rp2040-ard.yaml b/tests/components/htu21d/test.rp2040-ard.yaml index 8131d13661..ee2c29ca4e 100644 --- a/tests/components/htu21d/test.rp2040-ard.yaml +++ b/tests/components/htu21d/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_htu21d - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: htu21d - model: htu21d - temperature: - name: Temperature - humidity: - name: Humidity - heater: - name: Heater - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/htu31d/common.yaml b/tests/components/htu31d/common.yaml index c8ef2fede9..735cdc7cbf 100644 --- a/tests/components/htu31d/common.yaml +++ b/tests/components/htu31d/common.yaml @@ -1,9 +1,12 @@ i2c: + - id: i2c_htu31d + scl: ${scl_pin} + sda: ${sda_pin} sensor: - platform: htu31d temperature: - name: Living Room Temperature 7 + name: Living Room Temperature humidity: - name: Living Room Humidity 7 + name: Living Room Humidity update_interval: 15s diff --git a/tests/components/htu31d/test.esp32-ard.yaml b/tests/components/htu31d/test.esp32-ard.yaml index dade44d145..63c3bd6afd 100644 --- a/tests/components/htu31d/test.esp32-ard.yaml +++ b/tests/components/htu31d/test.esp32-ard.yaml @@ -1 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/htu31d/test.esp32-c3-ard.yaml b/tests/components/htu31d/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/htu31d/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/htu31d/test.esp32-c3-idf.yaml b/tests/components/htu31d/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/htu31d/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/htu31d/test.esp32-idf.yaml b/tests/components/htu31d/test.esp32-idf.yaml index dade44d145..63c3bd6afd 100644 --- a/tests/components/htu31d/test.esp32-idf.yaml +++ b/tests/components/htu31d/test.esp32-idf.yaml @@ -1 +1,5 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/htu31d/test.esp8266-ard.yaml b/tests/components/htu31d/test.esp8266-ard.yaml index dade44d145..ee2c29ca4e 100644 --- a/tests/components/htu31d/test.esp8266-ard.yaml +++ b/tests/components/htu31d/test.esp8266-ard.yaml @@ -1 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/htu31d/test.rp2040-ard.yaml b/tests/components/htu31d/test.rp2040-ard.yaml new file mode 100644 index 0000000000..ee2c29ca4e --- /dev/null +++ b/tests/components/htu31d/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hx711/common.yaml b/tests/components/hx711/common.yaml new file mode 100644 index 0000000000..be22fca678 --- /dev/null +++ b/tests/components/hx711/common.yaml @@ -0,0 +1,7 @@ +sensor: + - platform: hx711 + name: HX711 Value + clk_pin: ${clk_pin} + dout_pin: ${dout_pin} + gain: 128 + update_interval: 15s diff --git a/tests/components/hx711/test.esp32-ard.yaml b/tests/components/hx711/test.esp32-ard.yaml index 554b184422..6423867395 100644 --- a/tests/components/hx711/test.esp32-ard.yaml +++ b/tests/components/hx711/test.esp32-ard.yaml @@ -1,7 +1,5 @@ -sensor: - - platform: hx711 - name: HX711 Value - dout_pin: 14 - clk_pin: 15 - gain: 128 - update_interval: 15s +substitutions: + clk_pin: GPIO16 + dout_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-c3-ard.yaml b/tests/components/hx711/test.esp32-c3-ard.yaml index 9850417440..08a6e705c0 100644 --- a/tests/components/hx711/test.esp32-c3-ard.yaml +++ b/tests/components/hx711/test.esp32-c3-ard.yaml @@ -1,7 +1,5 @@ -sensor: - - platform: hx711 - name: HX711 Value - dout_pin: 4 - clk_pin: 5 - gain: 128 - update_interval: 15s +substitutions: + clk_pin: GPIO5 + dout_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-c3-idf.yaml b/tests/components/hx711/test.esp32-c3-idf.yaml index 9850417440..08a6e705c0 100644 --- a/tests/components/hx711/test.esp32-c3-idf.yaml +++ b/tests/components/hx711/test.esp32-c3-idf.yaml @@ -1,7 +1,5 @@ -sensor: - - platform: hx711 - name: HX711 Value - dout_pin: 4 - clk_pin: 5 - gain: 128 - update_interval: 15s +substitutions: + clk_pin: GPIO5 + dout_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hx711/test.esp32-idf.yaml b/tests/components/hx711/test.esp32-idf.yaml index 554b184422..6423867395 100644 --- a/tests/components/hx711/test.esp32-idf.yaml +++ b/tests/components/hx711/test.esp32-idf.yaml @@ -1,7 +1,5 @@ -sensor: - - platform: hx711 - name: HX711 Value - dout_pin: 14 - clk_pin: 15 - gain: 128 - update_interval: 15s +substitutions: + clk_pin: GPIO16 + dout_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/hx711/test.esp8266-ard.yaml b/tests/components/hx711/test.esp8266-ard.yaml index 9850417440..08a6e705c0 100644 --- a/tests/components/hx711/test.esp8266-ard.yaml +++ b/tests/components/hx711/test.esp8266-ard.yaml @@ -1,7 +1,5 @@ -sensor: - - platform: hx711 - name: HX711 Value - dout_pin: 4 - clk_pin: 5 - gain: 128 - update_interval: 15s +substitutions: + clk_pin: GPIO5 + dout_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hx711/test.rp2040-ard.yaml b/tests/components/hx711/test.rp2040-ard.yaml index 9850417440..08a6e705c0 100644 --- a/tests/components/hx711/test.rp2040-ard.yaml +++ b/tests/components/hx711/test.rp2040-ard.yaml @@ -1,7 +1,5 @@ -sensor: - - platform: hx711 - name: HX711 Value - dout_pin: 4 - clk_pin: 5 - gain: 128 - update_interval: 15s +substitutions: + clk_pin: GPIO5 + dout_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/common.yaml b/tests/components/hydreon_rgxx/common.yaml new file mode 100644 index 0000000000..e11c6c91c9 --- /dev/null +++ b/tests/components/hydreon_rgxx/common.yaml @@ -0,0 +1,37 @@ +uart: + - id: uart_hydreon_rgxx + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +binary_sensor: + - platform: hydreon_rgxx + hydreon_rgxx_id: hydreon_rg9 + too_cold: + name: rg9_toocold + em_sat: + name: rg9_emsat + lens_bad: + name: rg9_lens_bad + +sensor: + - platform: hydreon_rgxx + id: hydreon_rg9 + model: RG 9 + moisture: + name: hydreon_rain + id: hydreon_rain + temperature: + name: hydreon_temperature + disable_led: true + - platform: hydreon_rgxx + id: hydreon_rg15 + model: RG_15 + acc: + name: hydreon_acc + event_acc: + name: hydreon_event_acc + total_acc: + name: hydreon_total_acc + r_int: + name: hydreon_r_int diff --git a/tests/components/hydreon_rgxx/test.esp32-ard.yaml b/tests/components/hydreon_rgxx/test.esp32-ard.yaml index b6f9486d86..f486544afa 100644 --- a/tests/components/hydreon_rgxx/test.esp32-ard.yaml +++ b/tests/components/hydreon_rgxx/test.esp32-ard.yaml @@ -1,37 +1,5 @@ -uart: - - id: uart_hydreon_rgxx - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -binary_sensor: - - platform: hydreon_rgxx - hydreon_rgxx_id: hydreon_rg9 - too_cold: - name: rg9_toocold - em_sat: - name: rg9_emsat - lens_bad: - name: rg9_lens_bad - -sensor: - - platform: hydreon_rgxx - id: hydreon_rg9 - model: RG 9 - moisture: - name: hydreon_rain - id: hydreon_rain - temperature: - name: hydreon_temperature - disable_led: true - - platform: hydreon_rgxx - id: hydreon_rg15 - model: RG_15 - acc: - name: hydreon_acc - event_acc: - name: hydreon_event_acc - total_acc: - name: hydreon_total_acc - r_int: - name: hydreon_r_int +<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml b/tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml index f62f4942db..b516342f3b 100644 --- a/tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml +++ b/tests/components/hydreon_rgxx/test.esp32-c3-ard.yaml @@ -1,37 +1,5 @@ -uart: - - id: uart_hydreon_rgxx - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -binary_sensor: - - platform: hydreon_rgxx - hydreon_rgxx_id: hydreon_rg9 - too_cold: - name: rg9_toocold - em_sat: - name: rg9_emsat - lens_bad: - name: rg9_lens_bad - -sensor: - - platform: hydreon_rgxx - id: hydreon_rg9 - model: RG 9 - moisture: - name: hydreon_rain - id: hydreon_rain - temperature: - name: hydreon_temperature - disable_led: true - - platform: hydreon_rgxx - id: hydreon_rg15 - model: RG_15 - acc: - name: hydreon_acc - event_acc: - name: hydreon_event_acc - total_acc: - name: hydreon_total_acc - r_int: - name: hydreon_r_int +<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml b/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml index f62f4942db..b516342f3b 100644 --- a/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml +++ b/tests/components/hydreon_rgxx/test.esp32-c3-idf.yaml @@ -1,37 +1,5 @@ -uart: - - id: uart_hydreon_rgxx - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -binary_sensor: - - platform: hydreon_rgxx - hydreon_rgxx_id: hydreon_rg9 - too_cold: - name: rg9_toocold - em_sat: - name: rg9_emsat - lens_bad: - name: rg9_lens_bad - -sensor: - - platform: hydreon_rgxx - id: hydreon_rg9 - model: RG 9 - moisture: - name: hydreon_rain - id: hydreon_rain - temperature: - name: hydreon_temperature - disable_led: true - - platform: hydreon_rgxx - id: hydreon_rg15 - model: RG_15 - acc: - name: hydreon_acc - event_acc: - name: hydreon_event_acc - total_acc: - name: hydreon_total_acc - r_int: - name: hydreon_r_int +<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp32-idf.yaml b/tests/components/hydreon_rgxx/test.esp32-idf.yaml index b6f9486d86..f486544afa 100644 --- a/tests/components/hydreon_rgxx/test.esp32-idf.yaml +++ b/tests/components/hydreon_rgxx/test.esp32-idf.yaml @@ -1,37 +1,5 @@ -uart: - - id: uart_hydreon_rgxx - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -binary_sensor: - - platform: hydreon_rgxx - hydreon_rgxx_id: hydreon_rg9 - too_cold: - name: rg9_toocold - em_sat: - name: rg9_emsat - lens_bad: - name: rg9_lens_bad - -sensor: - - platform: hydreon_rgxx - id: hydreon_rg9 - model: RG 9 - moisture: - name: hydreon_rain - id: hydreon_rain - temperature: - name: hydreon_temperature - disable_led: true - - platform: hydreon_rgxx - id: hydreon_rg15 - model: RG_15 - acc: - name: hydreon_acc - event_acc: - name: hydreon_event_acc - total_acc: - name: hydreon_total_acc - r_int: - name: hydreon_r_int +<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.esp8266-ard.yaml b/tests/components/hydreon_rgxx/test.esp8266-ard.yaml index f62f4942db..b516342f3b 100644 --- a/tests/components/hydreon_rgxx/test.esp8266-ard.yaml +++ b/tests/components/hydreon_rgxx/test.esp8266-ard.yaml @@ -1,37 +1,5 @@ -uart: - - id: uart_hydreon_rgxx - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -binary_sensor: - - platform: hydreon_rgxx - hydreon_rgxx_id: hydreon_rg9 - too_cold: - name: rg9_toocold - em_sat: - name: rg9_emsat - lens_bad: - name: rg9_lens_bad - -sensor: - - platform: hydreon_rgxx - id: hydreon_rg9 - model: RG 9 - moisture: - name: hydreon_rain - id: hydreon_rain - temperature: - name: hydreon_temperature - disable_led: true - - platform: hydreon_rgxx - id: hydreon_rg15 - model: RG_15 - acc: - name: hydreon_acc - event_acc: - name: hydreon_event_acc - total_acc: - name: hydreon_total_acc - r_int: - name: hydreon_r_int +<<: !include common.yaml diff --git a/tests/components/hydreon_rgxx/test.rp2040-ard.yaml b/tests/components/hydreon_rgxx/test.rp2040-ard.yaml index f62f4942db..b516342f3b 100644 --- a/tests/components/hydreon_rgxx/test.rp2040-ard.yaml +++ b/tests/components/hydreon_rgxx/test.rp2040-ard.yaml @@ -1,37 +1,5 @@ -uart: - - id: uart_hydreon_rgxx - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -binary_sensor: - - platform: hydreon_rgxx - hydreon_rgxx_id: hydreon_rg9 - too_cold: - name: rg9_toocold - em_sat: - name: rg9_emsat - lens_bad: - name: rg9_lens_bad - -sensor: - - platform: hydreon_rgxx - id: hydreon_rg9 - model: RG 9 - moisture: - name: hydreon_rain - id: hydreon_rain - temperature: - name: hydreon_temperature - disable_led: true - - platform: hydreon_rgxx - id: hydreon_rg15 - model: RG_15 - acc: - name: hydreon_acc - event_acc: - name: hydreon_event_acc - total_acc: - name: hydreon_total_acc - r_int: - name: hydreon_r_int +<<: !include common.yaml diff --git a/tests/components/hyt271/common.yaml b/tests/components/hyt271/common.yaml new file mode 100644 index 0000000000..7a4371173f --- /dev/null +++ b/tests/components/hyt271/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_hyt271 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: hyt271 + temperature: + name: Temperature + humidity: + name: Humidity diff --git a/tests/components/hyt271/test.esp32-ard.yaml b/tests/components/hyt271/test.esp32-ard.yaml index 297611a89b..63c3bd6afd 100644 --- a/tests/components/hyt271/test.esp32-ard.yaml +++ b/tests/components/hyt271/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_hyt271 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hyt271 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hyt271/test.esp32-c3-ard.yaml b/tests/components/hyt271/test.esp32-c3-ard.yaml index c44f0a1f5d..ee2c29ca4e 100644 --- a/tests/components/hyt271/test.esp32-c3-ard.yaml +++ b/tests/components/hyt271/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_hyt271 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hyt271 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hyt271/test.esp32-c3-idf.yaml b/tests/components/hyt271/test.esp32-c3-idf.yaml index c44f0a1f5d..ee2c29ca4e 100644 --- a/tests/components/hyt271/test.esp32-c3-idf.yaml +++ b/tests/components/hyt271/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_hyt271 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hyt271 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hyt271/test.esp32-idf.yaml b/tests/components/hyt271/test.esp32-idf.yaml index 297611a89b..63c3bd6afd 100644 --- a/tests/components/hyt271/test.esp32-idf.yaml +++ b/tests/components/hyt271/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_hyt271 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: hyt271 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hyt271/test.esp8266-ard.yaml b/tests/components/hyt271/test.esp8266-ard.yaml index c44f0a1f5d..ee2c29ca4e 100644 --- a/tests/components/hyt271/test.esp8266-ard.yaml +++ b/tests/components/hyt271/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_hyt271 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hyt271 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/hyt271/test.rp2040-ard.yaml b/tests/components/hyt271/test.rp2040-ard.yaml index c44f0a1f5d..ee2c29ca4e 100644 --- a/tests/components/hyt271/test.rp2040-ard.yaml +++ b/tests/components/hyt271/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_hyt271 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: hyt271 - temperature: - name: Temperature - humidity: - name: Humidity +<<: !include common.yaml From 4273449003d8387c4c26d14d54983f432b540a14 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:53 -0600 Subject: [PATCH 0959/1052] [CI] Consolidate some tests (K, L) (#8201) --- tests/components/key_collector/common.yaml | 28 +++ .../key_collector/test.esp32-ard.yaml | 33 +--- .../key_collector/test.esp32-c3-ard.yaml | 33 +--- .../key_collector/test.esp32-c3-idf.yaml | 33 +--- .../key_collector/test.esp32-idf.yaml | 33 +--- .../key_collector/test.esp8266-ard.yaml | 33 +--- .../key_collector/test.rp2040-ard.yaml | 33 +--- tests/components/kmeteriso/common.yaml | 12 ++ .../components/kmeteriso/test.esp32-ard.yaml | 15 +- .../kmeteriso/test.esp32-c3-ard.yaml | 15 +- .../kmeteriso/test.esp32-c3-idf.yaml | 15 +- .../components/kmeteriso/test.esp32-idf.yaml | 15 +- .../kmeteriso/test.esp8266-ard.yaml | 15 +- .../components/kmeteriso/test.rp2040-ard.yaml | 15 +- tests/components/kuntze/common.yaml | 15 ++ tests/components/kuntze/test.esp32-ard.yaml | 19 +- .../components/kuntze/test.esp32-c3-ard.yaml | 19 +- .../components/kuntze/test.esp32-c3-idf.yaml | 19 +- tests/components/kuntze/test.esp32-idf.yaml | 19 +- tests/components/kuntze/test.esp8266-ard.yaml | 19 +- tests/components/kuntze/test.rp2040-ard.yaml | 19 +- tests/components/lcd_gpio/common.yaml | 13 ++ tests/components/lcd_gpio/test.esp32-ard.yaml | 22 +-- .../lcd_gpio/test.esp32-c3-ard.yaml | 22 +-- .../lcd_gpio/test.esp32-c3-idf.yaml | 22 +-- tests/components/lcd_gpio/test.esp32-idf.yaml | 22 +-- .../components/lcd_gpio/test.esp8266-ard.yaml | 22 +-- .../components/lcd_gpio/test.rp2040-ard.yaml | 22 +-- tests/components/lcd_menu/common.yaml | 118 ++++++++++++ tests/components/lcd_menu/test.esp32-ard.yaml | 125 +------------ .../lcd_menu/test.esp32-c3-ard.yaml | 125 +------------ .../lcd_menu/test.esp32-c3-idf.yaml | 125 +------------ tests/components/lcd_menu/test.esp32-idf.yaml | 125 +------------ .../components/lcd_menu/test.esp8266-ard.yaml | 125 +------------ .../components/lcd_menu/test.rp2040-ard.yaml | 125 +------------ tests/components/lcd_pcf8574/common.yaml | 22 +++ .../lcd_pcf8574/test.esp32-ard.yaml | 25 +-- .../lcd_pcf8574/test.esp32-c3-ard.yaml | 25 +-- .../lcd_pcf8574/test.esp32-c3-idf.yaml | 25 +-- .../lcd_pcf8574/test.esp32-idf.yaml | 25 +-- .../lcd_pcf8574/test.esp8266-ard.yaml | 25 +-- .../lcd_pcf8574/test.rp2040-ard.yaml | 25 +-- tests/components/ld2410/common.yaml | 169 +++++++++++++++++ tests/components/ld2410/test.esp32-ard.yaml | 172 +----------------- .../components/ld2410/test.esp32-c3-ard.yaml | 172 +----------------- .../components/ld2410/test.esp32-c3-idf.yaml | 172 +----------------- tests/components/ld2410/test.esp32-idf.yaml | 172 +----------------- tests/components/ld2410/test.esp8266-ard.yaml | 172 +----------------- tests/components/ld2410/test.rp2040-ard.yaml | 172 +----------------- tests/components/ld2420/common.yaml | 132 ++++++++++++++ tests/components/ld2420/test.esp32-ard.yaml | 135 +------------- .../components/ld2420/test.esp32-c3-ard.yaml | 135 +------------- .../components/ld2420/test.esp32-c3-idf.yaml | 135 +------------- tests/components/ld2420/test.esp32-idf.yaml | 135 +------------- tests/components/ld2420/test.esp8266-ard.yaml | 135 +------------- tests/components/ld2420/test.rp2040-ard.yaml | 135 +------------- tests/components/lilygo_t5_47/common.yaml | 24 +++ .../lilygo_t5_47/test.esp32-ard.yaml | 29 +-- .../lilygo_t5_47/test.esp32-c3-ard.yaml | 29 +-- .../lilygo_t5_47/test.esp32-c3-idf.yaml | 29 +-- .../lilygo_t5_47/test.esp32-idf.yaml | 29 +-- .../lilygo_t5_47/test.esp8266-ard.yaml | 29 +-- .../lilygo_t5_47/test.rp2040-ard.yaml | 29 +-- tests/components/ltr390/common.yaml | 38 ++++ tests/components/ltr390/test.esp32-ard.yaml | 41 +---- .../components/ltr390/test.esp32-c3-ard.yaml | 23 +-- .../components/ltr390/test.esp32-c3-idf.yaml | 23 +-- tests/components/ltr390/test.esp32-idf.yaml | 23 +-- tests/components/ltr390/test.esp8266-ard.yaml | 25 +-- tests/components/ltr390/test.rp2040-ard.yaml | 23 +-- 70 files changed, 895 insertions(+), 3284 deletions(-) create mode 100644 tests/components/key_collector/common.yaml create mode 100644 tests/components/kmeteriso/common.yaml create mode 100644 tests/components/kuntze/common.yaml create mode 100644 tests/components/lcd_gpio/common.yaml create mode 100644 tests/components/lcd_menu/common.yaml create mode 100644 tests/components/lcd_pcf8574/common.yaml create mode 100644 tests/components/ld2410/common.yaml create mode 100644 tests/components/ld2420/common.yaml create mode 100644 tests/components/lilygo_t5_47/common.yaml create mode 100644 tests/components/ltr390/common.yaml diff --git a/tests/components/key_collector/common.yaml b/tests/components/key_collector/common.yaml new file mode 100644 index 0000000000..d58922ca91 --- /dev/null +++ b/tests/components/key_collector/common.yaml @@ -0,0 +1,28 @@ +matrix_keypad: + id: keypad + rows: + - pin: ${pin_r0} + - pin: ${pin_r1} + columns: + - pin: ${pin_c0} + - pin: ${pin_c1} + keys: "1234" + has_pulldowns: true + +key_collector: + - id: reader + source_id: keypad + min_length: 4 + max_length: 4 + on_progress: + - logger.log: + format: "input progress: '%s', started by '%c'" + args: ['x.c_str()', "(start == 0 ? '~' : start)"] + on_result: + - logger.log: + format: "input result: '%s', started by '%c', ended by '%c'" + args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"] + on_timeout: + - logger.log: + format: "input timeout: '%s', started by '%c'" + args: ['x.c_str()', "(start == 0 ? '~' : start)"] diff --git a/tests/components/key_collector/test.esp32-ard.yaml b/tests/components/key_collector/test.esp32-ard.yaml index 7cbe9c0fc1..de144aa46b 100644 --- a/tests/components/key_collector/test.esp32-ard.yaml +++ b/tests/components/key_collector/test.esp32-ard.yaml @@ -1,28 +1,7 @@ -matrix_keypad: - id: keypad - rows: - - pin: 12 - - pin: 13 - columns: - - pin: 14 - - pin: 15 - keys: "1234" - has_pulldowns: true +substitutions: + pin_r0: GPIO12 + pin_r1: GPIO13 + pin_c0: GPIO14 + pin_c1: GPIO15 -key_collector: - - id: reader - source_id: keypad - min_length: 4 - max_length: 4 - on_progress: - - logger.log: - format: "input progress: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] - on_result: - - logger.log: - format: "input result: '%s', started by '%c', ended by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"] - on_timeout: - - logger.log: - format: "input timeout: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] +<<: !include common.yaml diff --git a/tests/components/key_collector/test.esp32-c3-ard.yaml b/tests/components/key_collector/test.esp32-c3-ard.yaml index 1f133c5cd8..b580ab7843 100644 --- a/tests/components/key_collector/test.esp32-c3-ard.yaml +++ b/tests/components/key_collector/test.esp32-c3-ard.yaml @@ -1,28 +1,7 @@ -matrix_keypad: - id: keypad - rows: - - pin: 1 - - pin: 2 - columns: - - pin: 3 - - pin: 4 - keys: "1234" - has_pulldowns: true +substitutions: + pin_r0: GPIO2 + pin_r1: GPIO3 + pin_c0: GPIO4 + pin_c1: GPIO5 -key_collector: - - id: reader - source_id: keypad - min_length: 4 - max_length: 4 - on_progress: - - logger.log: - format: "input progress: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] - on_result: - - logger.log: - format: "input result: '%s', started by '%c', ended by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"] - on_timeout: - - logger.log: - format: "input timeout: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] +<<: !include common.yaml diff --git a/tests/components/key_collector/test.esp32-c3-idf.yaml b/tests/components/key_collector/test.esp32-c3-idf.yaml index 1f133c5cd8..b580ab7843 100644 --- a/tests/components/key_collector/test.esp32-c3-idf.yaml +++ b/tests/components/key_collector/test.esp32-c3-idf.yaml @@ -1,28 +1,7 @@ -matrix_keypad: - id: keypad - rows: - - pin: 1 - - pin: 2 - columns: - - pin: 3 - - pin: 4 - keys: "1234" - has_pulldowns: true +substitutions: + pin_r0: GPIO2 + pin_r1: GPIO3 + pin_c0: GPIO4 + pin_c1: GPIO5 -key_collector: - - id: reader - source_id: keypad - min_length: 4 - max_length: 4 - on_progress: - - logger.log: - format: "input progress: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] - on_result: - - logger.log: - format: "input result: '%s', started by '%c', ended by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"] - on_timeout: - - logger.log: - format: "input timeout: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] +<<: !include common.yaml diff --git a/tests/components/key_collector/test.esp32-idf.yaml b/tests/components/key_collector/test.esp32-idf.yaml index 7cbe9c0fc1..de144aa46b 100644 --- a/tests/components/key_collector/test.esp32-idf.yaml +++ b/tests/components/key_collector/test.esp32-idf.yaml @@ -1,28 +1,7 @@ -matrix_keypad: - id: keypad - rows: - - pin: 12 - - pin: 13 - columns: - - pin: 14 - - pin: 15 - keys: "1234" - has_pulldowns: true +substitutions: + pin_r0: GPIO12 + pin_r1: GPIO13 + pin_c0: GPIO14 + pin_c1: GPIO15 -key_collector: - - id: reader - source_id: keypad - min_length: 4 - max_length: 4 - on_progress: - - logger.log: - format: "input progress: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] - on_result: - - logger.log: - format: "input result: '%s', started by '%c', ended by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"] - on_timeout: - - logger.log: - format: "input timeout: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] +<<: !include common.yaml diff --git a/tests/components/key_collector/test.esp8266-ard.yaml b/tests/components/key_collector/test.esp8266-ard.yaml index 7cbe9c0fc1..de144aa46b 100644 --- a/tests/components/key_collector/test.esp8266-ard.yaml +++ b/tests/components/key_collector/test.esp8266-ard.yaml @@ -1,28 +1,7 @@ -matrix_keypad: - id: keypad - rows: - - pin: 12 - - pin: 13 - columns: - - pin: 14 - - pin: 15 - keys: "1234" - has_pulldowns: true +substitutions: + pin_r0: GPIO12 + pin_r1: GPIO13 + pin_c0: GPIO14 + pin_c1: GPIO15 -key_collector: - - id: reader - source_id: keypad - min_length: 4 - max_length: 4 - on_progress: - - logger.log: - format: "input progress: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] - on_result: - - logger.log: - format: "input result: '%s', started by '%c', ended by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"] - on_timeout: - - logger.log: - format: "input timeout: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] +<<: !include common.yaml diff --git a/tests/components/key_collector/test.rp2040-ard.yaml b/tests/components/key_collector/test.rp2040-ard.yaml index 1f133c5cd8..b580ab7843 100644 --- a/tests/components/key_collector/test.rp2040-ard.yaml +++ b/tests/components/key_collector/test.rp2040-ard.yaml @@ -1,28 +1,7 @@ -matrix_keypad: - id: keypad - rows: - - pin: 1 - - pin: 2 - columns: - - pin: 3 - - pin: 4 - keys: "1234" - has_pulldowns: true +substitutions: + pin_r0: GPIO2 + pin_r1: GPIO3 + pin_c0: GPIO4 + pin_c1: GPIO5 -key_collector: - - id: reader - source_id: keypad - min_length: 4 - max_length: 4 - on_progress: - - logger.log: - format: "input progress: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] - on_result: - - logger.log: - format: "input result: '%s', started by '%c', ended by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)", "(end == 0 ? '~' : end)"] - on_timeout: - - logger.log: - format: "input timeout: '%s', started by '%c'" - args: ['x.c_str()', "(start == 0 ? '~' : start)"] +<<: !include common.yaml diff --git a/tests/components/kmeteriso/common.yaml b/tests/components/kmeteriso/common.yaml new file mode 100644 index 0000000000..6b68175904 --- /dev/null +++ b/tests/components/kmeteriso/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_kmeteriso + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: kmeteriso + temperature: + name: Outside Temperature + internal_temperature: + name: Internal Temperature + update_interval: 15s diff --git a/tests/components/kmeteriso/test.esp32-ard.yaml b/tests/components/kmeteriso/test.esp32-ard.yaml index 2c375dda31..63c3bd6afd 100644 --- a/tests/components/kmeteriso/test.esp32-ard.yaml +++ b/tests/components/kmeteriso/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_kmeteriso - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: kmeteriso - temperature: - name: Outside Temperature - internal_temperature: - name: Internal Temperature - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp32-c3-ard.yaml b/tests/components/kmeteriso/test.esp32-c3-ard.yaml index 7780cfea32..ee2c29ca4e 100644 --- a/tests/components/kmeteriso/test.esp32-c3-ard.yaml +++ b/tests/components/kmeteriso/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_kmeteriso - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: kmeteriso - temperature: - name: Outside Temperature - internal_temperature: - name: Internal Temperature - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp32-c3-idf.yaml b/tests/components/kmeteriso/test.esp32-c3-idf.yaml index 7780cfea32..ee2c29ca4e 100644 --- a/tests/components/kmeteriso/test.esp32-c3-idf.yaml +++ b/tests/components/kmeteriso/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_kmeteriso - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: kmeteriso - temperature: - name: Outside Temperature - internal_temperature: - name: Internal Temperature - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp32-idf.yaml b/tests/components/kmeteriso/test.esp32-idf.yaml index 2c375dda31..63c3bd6afd 100644 --- a/tests/components/kmeteriso/test.esp32-idf.yaml +++ b/tests/components/kmeteriso/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_kmeteriso - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: kmeteriso - temperature: - name: Outside Temperature - internal_temperature: - name: Internal Temperature - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.esp8266-ard.yaml b/tests/components/kmeteriso/test.esp8266-ard.yaml index 7780cfea32..ee2c29ca4e 100644 --- a/tests/components/kmeteriso/test.esp8266-ard.yaml +++ b/tests/components/kmeteriso/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_kmeteriso - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: kmeteriso - temperature: - name: Outside Temperature - internal_temperature: - name: Internal Temperature - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/kmeteriso/test.rp2040-ard.yaml b/tests/components/kmeteriso/test.rp2040-ard.yaml index 7780cfea32..ee2c29ca4e 100644 --- a/tests/components/kmeteriso/test.rp2040-ard.yaml +++ b/tests/components/kmeteriso/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_kmeteriso - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: kmeteriso - temperature: - name: Outside Temperature - internal_temperature: - name: Internal Temperature - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/kuntze/common.yaml b/tests/components/kuntze/common.yaml new file mode 100644 index 0000000000..4daecea242 --- /dev/null +++ b/tests/components/kuntze/common.yaml @@ -0,0 +1,15 @@ +uart: + - id: uart_kuntze + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +modbus: + flow_control_pin: ${flow_control_pin} + +sensor: + - platform: kuntze + ph: + name: Kuntze pH + temperature: + name: Kuntze temperature diff --git a/tests/components/kuntze/test.esp32-ard.yaml b/tests/components/kuntze/test.esp32-ard.yaml index 6b6c638971..bd767a8ece 100644 --- a/tests/components/kuntze/test.esp32-ard.yaml +++ b/tests/components/kuntze/test.esp32-ard.yaml @@ -1,15 +1,6 @@ -uart: - - id: uart_kuntze - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 13 - -sensor: - - platform: kuntze - ph: - name: Kuntze pH - temperature: - name: Kuntze temperature +<<: !include common.yaml diff --git a/tests/components/kuntze/test.esp32-c3-ard.yaml b/tests/components/kuntze/test.esp32-c3-ard.yaml index 08278c3c82..452031a5aa 100644 --- a/tests/components/kuntze/test.esp32-c3-ard.yaml +++ b/tests/components/kuntze/test.esp32-c3-ard.yaml @@ -1,15 +1,6 @@ -uart: - - id: uart_kuntze - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: kuntze - ph: - name: Kuntze pH - temperature: - name: Kuntze temperature +<<: !include common.yaml diff --git a/tests/components/kuntze/test.esp32-c3-idf.yaml b/tests/components/kuntze/test.esp32-c3-idf.yaml index 08278c3c82..452031a5aa 100644 --- a/tests/components/kuntze/test.esp32-c3-idf.yaml +++ b/tests/components/kuntze/test.esp32-c3-idf.yaml @@ -1,15 +1,6 @@ -uart: - - id: uart_kuntze - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: kuntze - ph: - name: Kuntze pH - temperature: - name: Kuntze temperature +<<: !include common.yaml diff --git a/tests/components/kuntze/test.esp32-idf.yaml b/tests/components/kuntze/test.esp32-idf.yaml index 6b6c638971..bd767a8ece 100644 --- a/tests/components/kuntze/test.esp32-idf.yaml +++ b/tests/components/kuntze/test.esp32-idf.yaml @@ -1,15 +1,6 @@ -uart: - - id: uart_kuntze - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 13 - -sensor: - - platform: kuntze - ph: - name: Kuntze pH - temperature: - name: Kuntze temperature +<<: !include common.yaml diff --git a/tests/components/kuntze/test.esp8266-ard.yaml b/tests/components/kuntze/test.esp8266-ard.yaml index eba6cddc2d..29c98d0957 100644 --- a/tests/components/kuntze/test.esp8266-ard.yaml +++ b/tests/components/kuntze/test.esp8266-ard.yaml @@ -1,15 +1,6 @@ -uart: - - id: uart_kuntze - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO13 -modbus: - flow_control_pin: 13 - -sensor: - - platform: kuntze - ph: - name: Kuntze pH - temperature: - name: Kuntze temperature +<<: !include common.yaml diff --git a/tests/components/kuntze/test.rp2040-ard.yaml b/tests/components/kuntze/test.rp2040-ard.yaml index 08278c3c82..452031a5aa 100644 --- a/tests/components/kuntze/test.rp2040-ard.yaml +++ b/tests/components/kuntze/test.rp2040-ard.yaml @@ -1,15 +1,6 @@ -uart: - - id: uart_kuntze - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - flow_control_pin: 3 - -sensor: - - platform: kuntze - ph: - name: Kuntze pH - temperature: - name: Kuntze temperature +<<: !include common.yaml diff --git a/tests/components/lcd_gpio/common.yaml b/tests/components/lcd_gpio/common.yaml new file mode 100644 index 0000000000..bd842454a1 --- /dev/null +++ b/tests/components/lcd_gpio/common.yaml @@ -0,0 +1,13 @@ +display: + - platform: lcd_gpio + id: my_lcd_gpio + dimensions: 18x4 + data_pins: + - number: ${d0_pin} + - number: ${d1_pin} + - number: ${d2_pin} + - number: ${d3_pin} + enable_pin: ${enable_pin} + rs_pin: ${rs_pin} + lambda: |- + it.print("Hello World!"); diff --git a/tests/components/lcd_gpio/test.esp32-ard.yaml b/tests/components/lcd_gpio/test.esp32-ard.yaml index d2b33aeb3a..9c2af456b5 100644 --- a/tests/components/lcd_gpio/test.esp32-ard.yaml +++ b/tests/components/lcd_gpio/test.esp32-ard.yaml @@ -1,13 +1,9 @@ -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 12 - - number: 13 - - number: 14 - - number: 15 - enable_pin: 16 - rs_pin: 5 - lambda: |- - it.print("Hello World!"); +substitutions: + d0_pin: GPIO12 + d1_pin: GPIO13 + d2_pin: GPIO14 + d3_pin: GPIO15 + enable_pin: GPIO16 + rs_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp32-c3-ard.yaml b/tests/components/lcd_gpio/test.esp32-c3-ard.yaml index b89715a755..b6b05f3ab4 100644 --- a/tests/components/lcd_gpio/test.esp32-c3-ard.yaml +++ b/tests/components/lcd_gpio/test.esp32-c3-ard.yaml @@ -1,13 +1,9 @@ -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 1 - - number: 2 - - number: 3 - - number: 4 - enable_pin: 5 - rs_pin: 6 - lambda: |- - it.print("Hello World!"); +substitutions: + d0_pin: GPIO1 + d1_pin: GPIO2 + d2_pin: GPIO3 + d3_pin: GPIO4 + enable_pin: GPIO5 + rs_pin: GPIO6 + +<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp32-c3-idf.yaml b/tests/components/lcd_gpio/test.esp32-c3-idf.yaml index b89715a755..b6b05f3ab4 100644 --- a/tests/components/lcd_gpio/test.esp32-c3-idf.yaml +++ b/tests/components/lcd_gpio/test.esp32-c3-idf.yaml @@ -1,13 +1,9 @@ -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 1 - - number: 2 - - number: 3 - - number: 4 - enable_pin: 5 - rs_pin: 6 - lambda: |- - it.print("Hello World!"); +substitutions: + d0_pin: GPIO1 + d1_pin: GPIO2 + d2_pin: GPIO3 + d3_pin: GPIO4 + enable_pin: GPIO5 + rs_pin: GPIO6 + +<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp32-idf.yaml b/tests/components/lcd_gpio/test.esp32-idf.yaml index d2b33aeb3a..9c2af456b5 100644 --- a/tests/components/lcd_gpio/test.esp32-idf.yaml +++ b/tests/components/lcd_gpio/test.esp32-idf.yaml @@ -1,13 +1,9 @@ -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 12 - - number: 13 - - number: 14 - - number: 15 - enable_pin: 16 - rs_pin: 5 - lambda: |- - it.print("Hello World!"); +substitutions: + d0_pin: GPIO12 + d1_pin: GPIO13 + d2_pin: GPIO14 + d3_pin: GPIO15 + enable_pin: GPIO16 + rs_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.esp8266-ard.yaml b/tests/components/lcd_gpio/test.esp8266-ard.yaml index d2b33aeb3a..9c2af456b5 100644 --- a/tests/components/lcd_gpio/test.esp8266-ard.yaml +++ b/tests/components/lcd_gpio/test.esp8266-ard.yaml @@ -1,13 +1,9 @@ -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 12 - - number: 13 - - number: 14 - - number: 15 - enable_pin: 16 - rs_pin: 5 - lambda: |- - it.print("Hello World!"); +substitutions: + d0_pin: GPIO12 + d1_pin: GPIO13 + d2_pin: GPIO14 + d3_pin: GPIO15 + enable_pin: GPIO16 + rs_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/lcd_gpio/test.rp2040-ard.yaml b/tests/components/lcd_gpio/test.rp2040-ard.yaml index b89715a755..b6b05f3ab4 100644 --- a/tests/components/lcd_gpio/test.rp2040-ard.yaml +++ b/tests/components/lcd_gpio/test.rp2040-ard.yaml @@ -1,13 +1,9 @@ -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 1 - - number: 2 - - number: 3 - - number: 4 - enable_pin: 5 - rs_pin: 6 - lambda: |- - it.print("Hello World!"); +substitutions: + d0_pin: GPIO1 + d1_pin: GPIO2 + d2_pin: GPIO3 + d3_pin: GPIO4 + enable_pin: GPIO5 + rs_pin: GPIO6 + +<<: !include common.yaml diff --git a/tests/components/lcd_menu/common.yaml b/tests/components/lcd_menu/common.yaml new file mode 100644 index 0000000000..970c18e0d2 --- /dev/null +++ b/tests/components/lcd_menu/common.yaml @@ -0,0 +1,118 @@ +number: + - platform: template + id: test_number + min_value: 0 + step: 1 + max_value: 10 + optimistic: true + +select: + - platform: template + id: test_select + options: + - one + - two + optimistic: true + +switch: + - platform: template + name: Template Switch + id: my_switch + optimistic: true + +display: + - platform: lcd_gpio + id: my_lcd_gpio + dimensions: 18x4 + data_pins: + - number: ${d0_pin} + - number: ${d1_pin} + - number: ${d2_pin} + - number: ${d3_pin} + enable_pin: ${enable_pin} + rs_pin: ${rs_pin} + lambda: |- + it.print("Hello World!"); + +lcd_menu: + id: test_lcd_menu + display_id: my_lcd_gpio + mark_back: 0x5e + mark_selected: 0x3e + mark_editing: 0x2a + mark_submenu: 0x7e + active: false + mode: rotary + on_enter: + then: + lambda: 'ESP_LOGI("lcd_menu", "root enter");' + on_leave: + then: + lambda: 'ESP_LOGI("lcd_menu", "root leave");' + items: + - type: back + text: Back + - type: label + - type: menu + text: Submenu 1 + items: + - type: back + text: Back + - type: menu + text: Submenu 21 + items: + - type: back + text: Back + - type: command + text: Show Main + on_value: + then: + - display_menu.show_main: test_lcd_menu + - type: select + text: Enum Item + immediate_edit: true + select: test_select + on_enter: + then: + lambda: 'ESP_LOGI("lcd_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_leave: + then: + lambda: 'ESP_LOGI("lcd_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_value: + then: + lambda: 'ESP_LOGI("lcd_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + - type: number + text: Number + number: test_number + on_enter: + then: + lambda: 'ESP_LOGI("lcd_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_leave: + then: + lambda: 'ESP_LOGI("lcd_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + on_value: + then: + lambda: 'ESP_LOGI("lcd_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' + - type: command + text: Hide + on_value: + then: + - display_menu.hide: test_lcd_menu + - type: switch + text: Switch + switch: my_switch + on_text: Bright + off_text: Dark + immediate_edit: false + on_value: + then: + lambda: 'ESP_LOGI("lcd_menu", "switch value: %s", it->get_value_text().c_str());' + - type: custom + text: !lambda 'return "Custom";' + value_lambda: 'return "Val";' + on_next: + then: + lambda: 'ESP_LOGI("lcd_menu", "custom next: %s", it->get_text().c_str());' + on_prev: + then: + lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());' diff --git a/tests/components/lcd_menu/test.esp32-ard.yaml b/tests/components/lcd_menu/test.esp32-ard.yaml index 833ea2169a..9c2af456b5 100644 --- a/tests/components/lcd_menu/test.esp32-ard.yaml +++ b/tests/components/lcd_menu/test.esp32-ard.yaml @@ -1,118 +1,9 @@ -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true +substitutions: + d0_pin: GPIO12 + d1_pin: GPIO13 + d2_pin: GPIO14 + d3_pin: GPIO15 + enable_pin: GPIO16 + rs_pin: GPIO5 -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - name: Template Switch - id: my_switch - optimistic: true - -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 12 - - number: 13 - - number: 14 - - number: 15 - enable_pin: 16 - rs_pin: 5 - lambda: |- - it.print("Hello World!"); - -lcd_menu: - id: test_lcd_menu - display_id: my_lcd_gpio - mark_back: 0x5e - mark_selected: 0x3e - mark_editing: 0x2a - mark_submenu: 0x7e - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "root leave");' - items: - - type: back - text: Back - - type: label - - type: menu - text: Submenu 1 - items: - - type: back - text: Back - - type: menu - text: Submenu 21 - items: - - type: back - text: Back - - type: command - text: Show Main - on_value: - then: - - display_menu.show_main: test_lcd_menu - - type: select - text: Enum Item - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: Number - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: Hide - on_value: - then: - - display_menu.hide: test_lcd_menu - - type: switch - text: Switch - switch: my_switch - on_text: Bright - off_text: Dark - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp32-c3-ard.yaml b/tests/components/lcd_menu/test.esp32-c3-ard.yaml index 39d2278d3d..b6b05f3ab4 100644 --- a/tests/components/lcd_menu/test.esp32-c3-ard.yaml +++ b/tests/components/lcd_menu/test.esp32-c3-ard.yaml @@ -1,118 +1,9 @@ -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true +substitutions: + d0_pin: GPIO1 + d1_pin: GPIO2 + d2_pin: GPIO3 + d3_pin: GPIO4 + enable_pin: GPIO5 + rs_pin: GPIO6 -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - name: Template Switch - id: my_switch - optimistic: true - -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 1 - - number: 2 - - number: 3 - - number: 4 - enable_pin: 5 - rs_pin: 6 - lambda: |- - it.print("Hello World!"); - -lcd_menu: - id: test_lcd_menu - display_id: my_lcd_gpio - mark_back: 0x5e - mark_selected: 0x3e - mark_editing: 0x2a - mark_submenu: 0x7e - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "root leave");' - items: - - type: back - text: Back - - type: label - - type: menu - text: Submenu 1 - items: - - type: back - text: Back - - type: menu - text: Submenu 21 - items: - - type: back - text: Back - - type: command - text: Show Main - on_value: - then: - - display_menu.show_main: test_lcd_menu - - type: select - text: Enum Item - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: Number - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: Hide - on_value: - then: - - display_menu.hide: test_lcd_menu - - type: switch - text: Switch - switch: my_switch - on_text: Bright - off_text: Dark - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp32-c3-idf.yaml b/tests/components/lcd_menu/test.esp32-c3-idf.yaml index 39d2278d3d..b6b05f3ab4 100644 --- a/tests/components/lcd_menu/test.esp32-c3-idf.yaml +++ b/tests/components/lcd_menu/test.esp32-c3-idf.yaml @@ -1,118 +1,9 @@ -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true +substitutions: + d0_pin: GPIO1 + d1_pin: GPIO2 + d2_pin: GPIO3 + d3_pin: GPIO4 + enable_pin: GPIO5 + rs_pin: GPIO6 -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - name: Template Switch - id: my_switch - optimistic: true - -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 1 - - number: 2 - - number: 3 - - number: 4 - enable_pin: 5 - rs_pin: 6 - lambda: |- - it.print("Hello World!"); - -lcd_menu: - id: test_lcd_menu - display_id: my_lcd_gpio - mark_back: 0x5e - mark_selected: 0x3e - mark_editing: 0x2a - mark_submenu: 0x7e - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "root leave");' - items: - - type: back - text: Back - - type: label - - type: menu - text: Submenu 1 - items: - - type: back - text: Back - - type: menu - text: Submenu 21 - items: - - type: back - text: Back - - type: command - text: Show Main - on_value: - then: - - display_menu.show_main: test_lcd_menu - - type: select - text: Enum Item - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: Number - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: Hide - on_value: - then: - - display_menu.hide: test_lcd_menu - - type: switch - text: Switch - switch: my_switch - on_text: Bright - off_text: Dark - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp32-idf.yaml b/tests/components/lcd_menu/test.esp32-idf.yaml index 833ea2169a..9c2af456b5 100644 --- a/tests/components/lcd_menu/test.esp32-idf.yaml +++ b/tests/components/lcd_menu/test.esp32-idf.yaml @@ -1,118 +1,9 @@ -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true +substitutions: + d0_pin: GPIO12 + d1_pin: GPIO13 + d2_pin: GPIO14 + d3_pin: GPIO15 + enable_pin: GPIO16 + rs_pin: GPIO5 -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - name: Template Switch - id: my_switch - optimistic: true - -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 12 - - number: 13 - - number: 14 - - number: 15 - enable_pin: 16 - rs_pin: 5 - lambda: |- - it.print("Hello World!"); - -lcd_menu: - id: test_lcd_menu - display_id: my_lcd_gpio - mark_back: 0x5e - mark_selected: 0x3e - mark_editing: 0x2a - mark_submenu: 0x7e - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "root leave");' - items: - - type: back - text: Back - - type: label - - type: menu - text: Submenu 1 - items: - - type: back - text: Back - - type: menu - text: Submenu 21 - items: - - type: back - text: Back - - type: command - text: Show Main - on_value: - then: - - display_menu.show_main: test_lcd_menu - - type: select - text: Enum Item - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: Number - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: Hide - on_value: - then: - - display_menu.hide: test_lcd_menu - - type: switch - text: Switch - switch: my_switch - on_text: Bright - off_text: Dark - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.esp8266-ard.yaml b/tests/components/lcd_menu/test.esp8266-ard.yaml index 833ea2169a..9c2af456b5 100644 --- a/tests/components/lcd_menu/test.esp8266-ard.yaml +++ b/tests/components/lcd_menu/test.esp8266-ard.yaml @@ -1,118 +1,9 @@ -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true +substitutions: + d0_pin: GPIO12 + d1_pin: GPIO13 + d2_pin: GPIO14 + d3_pin: GPIO15 + enable_pin: GPIO16 + rs_pin: GPIO5 -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - name: Template Switch - id: my_switch - optimistic: true - -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 12 - - number: 13 - - number: 14 - - number: 15 - enable_pin: 16 - rs_pin: 5 - lambda: |- - it.print("Hello World!"); - -lcd_menu: - id: test_lcd_menu - display_id: my_lcd_gpio - mark_back: 0x5e - mark_selected: 0x3e - mark_editing: 0x2a - mark_submenu: 0x7e - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "root leave");' - items: - - type: back - text: Back - - type: label - - type: menu - text: Submenu 1 - items: - - type: back - text: Back - - type: menu - text: Submenu 21 - items: - - type: back - text: Back - - type: command - text: Show Main - on_value: - then: - - display_menu.show_main: test_lcd_menu - - type: select - text: Enum Item - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: Number - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: Hide - on_value: - then: - - display_menu.hide: test_lcd_menu - - type: switch - text: Switch - switch: my_switch - on_text: Bright - off_text: Dark - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/lcd_menu/test.rp2040-ard.yaml b/tests/components/lcd_menu/test.rp2040-ard.yaml index 39d2278d3d..b6b05f3ab4 100644 --- a/tests/components/lcd_menu/test.rp2040-ard.yaml +++ b/tests/components/lcd_menu/test.rp2040-ard.yaml @@ -1,118 +1,9 @@ -number: - - platform: template - id: test_number - min_value: 0 - step: 1 - max_value: 10 - optimistic: true +substitutions: + d0_pin: GPIO1 + d1_pin: GPIO2 + d2_pin: GPIO3 + d3_pin: GPIO4 + enable_pin: GPIO5 + rs_pin: GPIO6 -select: - - platform: template - id: test_select - options: - - one - - two - optimistic: true - -switch: - - platform: template - name: Template Switch - id: my_switch - optimistic: true - -display: - - platform: lcd_gpio - id: my_lcd_gpio - dimensions: 18x4 - data_pins: - - number: 1 - - number: 2 - - number: 3 - - number: 4 - enable_pin: 5 - rs_pin: 6 - lambda: |- - it.print("Hello World!"); - -lcd_menu: - id: test_lcd_menu - display_id: my_lcd_gpio - mark_back: 0x5e - mark_selected: 0x3e - mark_editing: 0x2a - mark_submenu: 0x7e - active: false - mode: rotary - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "root enter");' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "root leave");' - items: - - type: back - text: Back - - type: label - - type: menu - text: Submenu 1 - items: - - type: back - text: Back - - type: menu - text: Submenu 21 - items: - - type: back - text: Back - - type: command - text: Show Main - on_value: - then: - - display_menu.show_main: test_lcd_menu - - type: select - text: Enum Item - immediate_edit: true - select: test_select - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "select enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "select leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "select value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: number - text: Number - number: test_number - on_enter: - then: - lambda: 'ESP_LOGI("lcd_menu", "number enter: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_leave: - then: - lambda: 'ESP_LOGI("lcd_menu", "number leave: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "number value: %s, %s", it->get_text().c_str(), it->get_value_text().c_str());' - - type: command - text: Hide - on_value: - then: - - display_menu.hide: test_lcd_menu - - type: switch - text: Switch - switch: my_switch - on_text: Bright - off_text: Dark - immediate_edit: false - on_value: - then: - lambda: 'ESP_LOGI("lcd_menu", "switch value: %s", it->get_value_text().c_str());' - - type: custom - text: !lambda 'return "Custom";' - value_lambda: 'return "Val";' - on_next: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom next: %s", it->get_text().c_str());' - on_prev: - then: - lambda: 'ESP_LOGI("lcd_menu", "custom prev: %s", it->get_text().c_str());' +<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/common.yaml b/tests/components/lcd_pcf8574/common.yaml new file mode 100644 index 0000000000..8b03cf5497 --- /dev/null +++ b/tests/components/lcd_pcf8574/common.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_lcd_pcf8574 + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: lcd_pcf8574 + dimensions: 18x4 + address: 0x3F + user_characters: + - position: 0 + data: + - 0b00000 + - 0b01010 + - 0b00000 + - 0b00100 + - 0b00100 + - 0b10001 + - 0b01110 + - 0b00000 + lambda: |- + it.print("Hello World!"); diff --git a/tests/components/lcd_pcf8574/test.esp32-ard.yaml b/tests/components/lcd_pcf8574/test.esp32-ard.yaml index 9d7d475f30..63c3bd6afd 100644 --- a/tests/components/lcd_pcf8574/test.esp32-ard.yaml +++ b/tests/components/lcd_pcf8574/test.esp32-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_lcd_pcf8574 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -display: - - platform: lcd_pcf8574 - dimensions: 18x4 - address: 0x3F - user_characters: - - position: 0 - data: - - 0b00000 - - 0b01010 - - 0b00000 - - 0b00100 - - 0b00100 - - 0b10001 - - 0b01110 - - 0b00000 - lambda: |- - it.print("Hello World!"); +<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml b/tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml index 41eba26950..ee2c29ca4e 100644 --- a/tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml +++ b/tests/components/lcd_pcf8574/test.esp32-c3-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_lcd_pcf8574 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -display: - - platform: lcd_pcf8574 - dimensions: 18x4 - address: 0x3F - user_characters: - - position: 0 - data: - - 0b00000 - - 0b01010 - - 0b00000 - - 0b00100 - - 0b00100 - - 0b10001 - - 0b01110 - - 0b00000 - lambda: |- - it.print("Hello World!"); +<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml b/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml index 41eba26950..ee2c29ca4e 100644 --- a/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml +++ b/tests/components/lcd_pcf8574/test.esp32-c3-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_lcd_pcf8574 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -display: - - platform: lcd_pcf8574 - dimensions: 18x4 - address: 0x3F - user_characters: - - position: 0 - data: - - 0b00000 - - 0b01010 - - 0b00000 - - 0b00100 - - 0b00100 - - 0b10001 - - 0b01110 - - 0b00000 - lambda: |- - it.print("Hello World!"); +<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp32-idf.yaml b/tests/components/lcd_pcf8574/test.esp32-idf.yaml index 9d7d475f30..63c3bd6afd 100644 --- a/tests/components/lcd_pcf8574/test.esp32-idf.yaml +++ b/tests/components/lcd_pcf8574/test.esp32-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_lcd_pcf8574 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -display: - - platform: lcd_pcf8574 - dimensions: 18x4 - address: 0x3F - user_characters: - - position: 0 - data: - - 0b00000 - - 0b01010 - - 0b00000 - - 0b00100 - - 0b00100 - - 0b10001 - - 0b01110 - - 0b00000 - lambda: |- - it.print("Hello World!"); +<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.esp8266-ard.yaml b/tests/components/lcd_pcf8574/test.esp8266-ard.yaml index 41eba26950..ee2c29ca4e 100644 --- a/tests/components/lcd_pcf8574/test.esp8266-ard.yaml +++ b/tests/components/lcd_pcf8574/test.esp8266-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_lcd_pcf8574 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -display: - - platform: lcd_pcf8574 - dimensions: 18x4 - address: 0x3F - user_characters: - - position: 0 - data: - - 0b00000 - - 0b01010 - - 0b00000 - - 0b00100 - - 0b00100 - - 0b10001 - - 0b01110 - - 0b00000 - lambda: |- - it.print("Hello World!"); +<<: !include common.yaml diff --git a/tests/components/lcd_pcf8574/test.rp2040-ard.yaml b/tests/components/lcd_pcf8574/test.rp2040-ard.yaml index 41eba26950..ee2c29ca4e 100644 --- a/tests/components/lcd_pcf8574/test.rp2040-ard.yaml +++ b/tests/components/lcd_pcf8574/test.rp2040-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_lcd_pcf8574 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -display: - - platform: lcd_pcf8574 - dimensions: 18x4 - address: 0x3F - user_characters: - - position: 0 - data: - - 0b00000 - - 0b01010 - - 0b00000 - - 0b00100 - - 0b00100 - - 0b10001 - - 0b01110 - - 0b00000 - lambda: |- - it.print("Hello World!"); +<<: !include common.yaml diff --git a/tests/components/ld2410/common.yaml b/tests/components/ld2410/common.yaml new file mode 100644 index 0000000000..e975fcf757 --- /dev/null +++ b/tests/components/ld2410/common.yaml @@ -0,0 +1,169 @@ +uart: + - id: uart_ld2410 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +ld2410: + id: my_ld2410 + +binary_sensor: + - platform: ld2410 + has_target: + name: presence + has_moving_target: + name: movement + has_still_target: + name: still + out_pin_presence_status: + name: out pin presence status + +button: + - platform: ld2410 + factory_reset: + name: factory reset + restart: + name: restart + query_params: + name: query params + +number: + - platform: ld2410 + light_threshold: + name: light threshold + timeout: + name: timeout + max_move_distance_gate: + name: max move distance gate + max_still_distance_gate: + name: max still distance gate + g0: + move_threshold: + name: g0 move threshold + still_threshold: + name: g0 still threshold + g1: + move_threshold: + name: g1 move threshold + still_threshold: + name: g1 still threshold + g2: + move_threshold: + name: g2 move threshold + still_threshold: + name: g2 still threshold + g3: + move_threshold: + name: g3 move threshold + still_threshold: + name: g3 still threshold + g4: + move_threshold: + name: g4 move threshold + still_threshold: + name: g4 still threshold + g5: + move_threshold: + name: g5 move threshold + still_threshold: + name: g5 still threshold + g6: + move_threshold: + name: g6 move threshold + still_threshold: + name: g6 still threshold + g7: + move_threshold: + name: g7 move threshold + still_threshold: + name: g7 still threshold + g8: + move_threshold: + name: g8 move threshold + still_threshold: + name: g8 still threshold + +select: + - platform: ld2410 + distance_resolution: + name: distance resolution + baud_rate: + name: baud rate + light_function: + name: light function + out_pin_level: + name: out ping level + +sensor: + - platform: ld2410 + light: + name: light + moving_distance: + name: Moving distance + still_distance: + name: Still Distance + moving_energy: + name: Move Energy + still_energy: + name: Still Energy + detection_distance: + name: Distance Detection + g0: + move_energy: + name: g0 move energy + still_energy: + name: g0 still energy + g1: + move_energy: + name: g1 move energy + still_energy: + name: g1 still energy + g2: + move_energy: + name: g2 move energy + still_energy: + name: g2 still energy + g3: + move_energy: + name: g3 move energy + still_energy: + name: g3 still energy + g4: + move_energy: + name: g4 move energy + still_energy: + name: g4 still energy + g5: + move_energy: + name: g5 move energy + still_energy: + name: g5 still energy + g6: + move_energy: + name: g6 move energy + still_energy: + name: g6 still energy + g7: + move_energy: + name: g7 move energy + still_energy: + name: g7 still energy + g8: + move_energy: + name: g8 move energy + still_energy: + name: g8 still energy + +switch: + - platform: ld2410 + engineering_mode: + name: control ld2410 engineering mode + bluetooth: + name: control ld2410 bluetooth + +text_sensor: + - platform: ld2410 + version: + name: presenece sensor version + mac_address: + name: presenece sensor mac address diff --git a/tests/components/ld2410/test.esp32-ard.yaml b/tests/components/ld2410/test.esp32-ard.yaml index 48ed179d93..f486544afa 100644 --- a/tests/components/ld2410/test.esp32-ard.yaml +++ b/tests/components/ld2410/test.esp32-ard.yaml @@ -1,169 +1,5 @@ -uart: - - id: uart_ld2410 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -ld2410: - id: my_ld2410 - -binary_sensor: - - platform: ld2410 - has_target: - name: presence - has_moving_target: - name: movement - has_still_target: - name: still - out_pin_presence_status: - name: out pin presence status - -button: - - platform: ld2410 - factory_reset: - name: factory reset - restart: - name: restart - query_params: - name: query params - -number: - - platform: ld2410 - light_threshold: - name: light threshold - timeout: - name: timeout - max_move_distance_gate: - name: max move distance gate - max_still_distance_gate: - name: max still distance gate - g0: - move_threshold: - name: g0 move threshold - still_threshold: - name: g0 still threshold - g1: - move_threshold: - name: g1 move threshold - still_threshold: - name: g1 still threshold - g2: - move_threshold: - name: g2 move threshold - still_threshold: - name: g2 still threshold - g3: - move_threshold: - name: g3 move threshold - still_threshold: - name: g3 still threshold - g4: - move_threshold: - name: g4 move threshold - still_threshold: - name: g4 still threshold - g5: - move_threshold: - name: g5 move threshold - still_threshold: - name: g5 still threshold - g6: - move_threshold: - name: g6 move threshold - still_threshold: - name: g6 still threshold - g7: - move_threshold: - name: g7 move threshold - still_threshold: - name: g7 still threshold - g8: - move_threshold: - name: g8 move threshold - still_threshold: - name: g8 still threshold - -select: - - platform: ld2410 - distance_resolution: - name: distance resolution - baud_rate: - name: baud rate - light_function: - name: light function - out_pin_level: - name: out ping level - -sensor: - - platform: ld2410 - light: - name: light - moving_distance: - name: Moving distance - still_distance: - name: Still Distance - moving_energy: - name: Move Energy - still_energy: - name: Still Energy - detection_distance: - name: Distance Detection - g0: - move_energy: - name: g0 move energy - still_energy: - name: g0 still energy - g1: - move_energy: - name: g1 move energy - still_energy: - name: g1 still energy - g2: - move_energy: - name: g2 move energy - still_energy: - name: g2 still energy - g3: - move_energy: - name: g3 move energy - still_energy: - name: g3 still energy - g4: - move_energy: - name: g4 move energy - still_energy: - name: g4 still energy - g5: - move_energy: - name: g5 move energy - still_energy: - name: g5 still energy - g6: - move_energy: - name: g6 move energy - still_energy: - name: g6 still energy - g7: - move_energy: - name: g7 move energy - still_energy: - name: g7 still energy - g8: - move_energy: - name: g8 move energy - still_energy: - name: g8 still energy - -switch: - - platform: ld2410 - engineering_mode: - name: control ld2410 engineering mode - bluetooth: - name: control ld2410 bluetooth - -text_sensor: - - platform: ld2410 - version: - name: presenece sensor version - mac_address: - name: presenece sensor mac address +<<: !include common.yaml diff --git a/tests/components/ld2410/test.esp32-c3-ard.yaml b/tests/components/ld2410/test.esp32-c3-ard.yaml index afcaa81b3e..b516342f3b 100644 --- a/tests/components/ld2410/test.esp32-c3-ard.yaml +++ b/tests/components/ld2410/test.esp32-c3-ard.yaml @@ -1,169 +1,5 @@ -uart: - - id: uart_ld2410 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2410: - id: my_ld2410 - -binary_sensor: - - platform: ld2410 - has_target: - name: presence - has_moving_target: - name: movement - has_still_target: - name: still - out_pin_presence_status: - name: out pin presence status - -button: - - platform: ld2410 - factory_reset: - name: factory reset - restart: - name: restart - query_params: - name: query params - -number: - - platform: ld2410 - light_threshold: - name: light threshold - timeout: - name: timeout - max_move_distance_gate: - name: max move distance gate - max_still_distance_gate: - name: max still distance gate - g0: - move_threshold: - name: g0 move threshold - still_threshold: - name: g0 still threshold - g1: - move_threshold: - name: g1 move threshold - still_threshold: - name: g1 still threshold - g2: - move_threshold: - name: g2 move threshold - still_threshold: - name: g2 still threshold - g3: - move_threshold: - name: g3 move threshold - still_threshold: - name: g3 still threshold - g4: - move_threshold: - name: g4 move threshold - still_threshold: - name: g4 still threshold - g5: - move_threshold: - name: g5 move threshold - still_threshold: - name: g5 still threshold - g6: - move_threshold: - name: g6 move threshold - still_threshold: - name: g6 still threshold - g7: - move_threshold: - name: g7 move threshold - still_threshold: - name: g7 still threshold - g8: - move_threshold: - name: g8 move threshold - still_threshold: - name: g8 still threshold - -select: - - platform: ld2410 - distance_resolution: - name: distance resolution - baud_rate: - name: baud rate - light_function: - name: light function - out_pin_level: - name: out ping level - -sensor: - - platform: ld2410 - light: - name: light - moving_distance: - name: Moving distance - still_distance: - name: Still Distance - moving_energy: - name: Move Energy - still_energy: - name: Still Energy - detection_distance: - name: Distance Detection - g0: - move_energy: - name: g0 move energy - still_energy: - name: g0 still energy - g1: - move_energy: - name: g1 move energy - still_energy: - name: g1 still energy - g2: - move_energy: - name: g2 move energy - still_energy: - name: g2 still energy - g3: - move_energy: - name: g3 move energy - still_energy: - name: g3 still energy - g4: - move_energy: - name: g4 move energy - still_energy: - name: g4 still energy - g5: - move_energy: - name: g5 move energy - still_energy: - name: g5 still energy - g6: - move_energy: - name: g6 move energy - still_energy: - name: g6 still energy - g7: - move_energy: - name: g7 move energy - still_energy: - name: g7 still energy - g8: - move_energy: - name: g8 move energy - still_energy: - name: g8 still energy - -switch: - - platform: ld2410 - engineering_mode: - name: control ld2410 engineering mode - bluetooth: - name: control ld2410 bluetooth - -text_sensor: - - platform: ld2410 - version: - name: presenece sensor version - mac_address: - name: presenece sensor mac address +<<: !include common.yaml diff --git a/tests/components/ld2410/test.esp32-c3-idf.yaml b/tests/components/ld2410/test.esp32-c3-idf.yaml index afcaa81b3e..b516342f3b 100644 --- a/tests/components/ld2410/test.esp32-c3-idf.yaml +++ b/tests/components/ld2410/test.esp32-c3-idf.yaml @@ -1,169 +1,5 @@ -uart: - - id: uart_ld2410 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2410: - id: my_ld2410 - -binary_sensor: - - platform: ld2410 - has_target: - name: presence - has_moving_target: - name: movement - has_still_target: - name: still - out_pin_presence_status: - name: out pin presence status - -button: - - platform: ld2410 - factory_reset: - name: factory reset - restart: - name: restart - query_params: - name: query params - -number: - - platform: ld2410 - light_threshold: - name: light threshold - timeout: - name: timeout - max_move_distance_gate: - name: max move distance gate - max_still_distance_gate: - name: max still distance gate - g0: - move_threshold: - name: g0 move threshold - still_threshold: - name: g0 still threshold - g1: - move_threshold: - name: g1 move threshold - still_threshold: - name: g1 still threshold - g2: - move_threshold: - name: g2 move threshold - still_threshold: - name: g2 still threshold - g3: - move_threshold: - name: g3 move threshold - still_threshold: - name: g3 still threshold - g4: - move_threshold: - name: g4 move threshold - still_threshold: - name: g4 still threshold - g5: - move_threshold: - name: g5 move threshold - still_threshold: - name: g5 still threshold - g6: - move_threshold: - name: g6 move threshold - still_threshold: - name: g6 still threshold - g7: - move_threshold: - name: g7 move threshold - still_threshold: - name: g7 still threshold - g8: - move_threshold: - name: g8 move threshold - still_threshold: - name: g8 still threshold - -select: - - platform: ld2410 - distance_resolution: - name: distance resolution - baud_rate: - name: baud rate - light_function: - name: light function - out_pin_level: - name: out ping level - -sensor: - - platform: ld2410 - light: - name: light - moving_distance: - name: Moving distance - still_distance: - name: Still Distance - moving_energy: - name: Move Energy - still_energy: - name: Still Energy - detection_distance: - name: Distance Detection - g0: - move_energy: - name: g0 move energy - still_energy: - name: g0 still energy - g1: - move_energy: - name: g1 move energy - still_energy: - name: g1 still energy - g2: - move_energy: - name: g2 move energy - still_energy: - name: g2 still energy - g3: - move_energy: - name: g3 move energy - still_energy: - name: g3 still energy - g4: - move_energy: - name: g4 move energy - still_energy: - name: g4 still energy - g5: - move_energy: - name: g5 move energy - still_energy: - name: g5 still energy - g6: - move_energy: - name: g6 move energy - still_energy: - name: g6 still energy - g7: - move_energy: - name: g7 move energy - still_energy: - name: g7 still energy - g8: - move_energy: - name: g8 move energy - still_energy: - name: g8 still energy - -switch: - - platform: ld2410 - engineering_mode: - name: control ld2410 engineering mode - bluetooth: - name: control ld2410 bluetooth - -text_sensor: - - platform: ld2410 - version: - name: presenece sensor version - mac_address: - name: presenece sensor mac address +<<: !include common.yaml diff --git a/tests/components/ld2410/test.esp32-idf.yaml b/tests/components/ld2410/test.esp32-idf.yaml index 48ed179d93..f486544afa 100644 --- a/tests/components/ld2410/test.esp32-idf.yaml +++ b/tests/components/ld2410/test.esp32-idf.yaml @@ -1,169 +1,5 @@ -uart: - - id: uart_ld2410 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -ld2410: - id: my_ld2410 - -binary_sensor: - - platform: ld2410 - has_target: - name: presence - has_moving_target: - name: movement - has_still_target: - name: still - out_pin_presence_status: - name: out pin presence status - -button: - - platform: ld2410 - factory_reset: - name: factory reset - restart: - name: restart - query_params: - name: query params - -number: - - platform: ld2410 - light_threshold: - name: light threshold - timeout: - name: timeout - max_move_distance_gate: - name: max move distance gate - max_still_distance_gate: - name: max still distance gate - g0: - move_threshold: - name: g0 move threshold - still_threshold: - name: g0 still threshold - g1: - move_threshold: - name: g1 move threshold - still_threshold: - name: g1 still threshold - g2: - move_threshold: - name: g2 move threshold - still_threshold: - name: g2 still threshold - g3: - move_threshold: - name: g3 move threshold - still_threshold: - name: g3 still threshold - g4: - move_threshold: - name: g4 move threshold - still_threshold: - name: g4 still threshold - g5: - move_threshold: - name: g5 move threshold - still_threshold: - name: g5 still threshold - g6: - move_threshold: - name: g6 move threshold - still_threshold: - name: g6 still threshold - g7: - move_threshold: - name: g7 move threshold - still_threshold: - name: g7 still threshold - g8: - move_threshold: - name: g8 move threshold - still_threshold: - name: g8 still threshold - -select: - - platform: ld2410 - distance_resolution: - name: distance resolution - baud_rate: - name: baud rate - light_function: - name: light function - out_pin_level: - name: out ping level - -sensor: - - platform: ld2410 - light: - name: light - moving_distance: - name: Moving distance - still_distance: - name: Still Distance - moving_energy: - name: Move Energy - still_energy: - name: Still Energy - detection_distance: - name: Distance Detection - g0: - move_energy: - name: g0 move energy - still_energy: - name: g0 still energy - g1: - move_energy: - name: g1 move energy - still_energy: - name: g1 still energy - g2: - move_energy: - name: g2 move energy - still_energy: - name: g2 still energy - g3: - move_energy: - name: g3 move energy - still_energy: - name: g3 still energy - g4: - move_energy: - name: g4 move energy - still_energy: - name: g4 still energy - g5: - move_energy: - name: g5 move energy - still_energy: - name: g5 still energy - g6: - move_energy: - name: g6 move energy - still_energy: - name: g6 still energy - g7: - move_energy: - name: g7 move energy - still_energy: - name: g7 still energy - g8: - move_energy: - name: g8 move energy - still_energy: - name: g8 still energy - -switch: - - platform: ld2410 - engineering_mode: - name: control ld2410 engineering mode - bluetooth: - name: control ld2410 bluetooth - -text_sensor: - - platform: ld2410 - version: - name: presenece sensor version - mac_address: - name: presenece sensor mac address +<<: !include common.yaml diff --git a/tests/components/ld2410/test.esp8266-ard.yaml b/tests/components/ld2410/test.esp8266-ard.yaml index afcaa81b3e..b516342f3b 100644 --- a/tests/components/ld2410/test.esp8266-ard.yaml +++ b/tests/components/ld2410/test.esp8266-ard.yaml @@ -1,169 +1,5 @@ -uart: - - id: uart_ld2410 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2410: - id: my_ld2410 - -binary_sensor: - - platform: ld2410 - has_target: - name: presence - has_moving_target: - name: movement - has_still_target: - name: still - out_pin_presence_status: - name: out pin presence status - -button: - - platform: ld2410 - factory_reset: - name: factory reset - restart: - name: restart - query_params: - name: query params - -number: - - platform: ld2410 - light_threshold: - name: light threshold - timeout: - name: timeout - max_move_distance_gate: - name: max move distance gate - max_still_distance_gate: - name: max still distance gate - g0: - move_threshold: - name: g0 move threshold - still_threshold: - name: g0 still threshold - g1: - move_threshold: - name: g1 move threshold - still_threshold: - name: g1 still threshold - g2: - move_threshold: - name: g2 move threshold - still_threshold: - name: g2 still threshold - g3: - move_threshold: - name: g3 move threshold - still_threshold: - name: g3 still threshold - g4: - move_threshold: - name: g4 move threshold - still_threshold: - name: g4 still threshold - g5: - move_threshold: - name: g5 move threshold - still_threshold: - name: g5 still threshold - g6: - move_threshold: - name: g6 move threshold - still_threshold: - name: g6 still threshold - g7: - move_threshold: - name: g7 move threshold - still_threshold: - name: g7 still threshold - g8: - move_threshold: - name: g8 move threshold - still_threshold: - name: g8 still threshold - -select: - - platform: ld2410 - distance_resolution: - name: distance resolution - baud_rate: - name: baud rate - light_function: - name: light function - out_pin_level: - name: out ping level - -sensor: - - platform: ld2410 - light: - name: light - moving_distance: - name: Moving distance - still_distance: - name: Still Distance - moving_energy: - name: Move Energy - still_energy: - name: Still Energy - detection_distance: - name: Distance Detection - g0: - move_energy: - name: g0 move energy - still_energy: - name: g0 still energy - g1: - move_energy: - name: g1 move energy - still_energy: - name: g1 still energy - g2: - move_energy: - name: g2 move energy - still_energy: - name: g2 still energy - g3: - move_energy: - name: g3 move energy - still_energy: - name: g3 still energy - g4: - move_energy: - name: g4 move energy - still_energy: - name: g4 still energy - g5: - move_energy: - name: g5 move energy - still_energy: - name: g5 still energy - g6: - move_energy: - name: g6 move energy - still_energy: - name: g6 still energy - g7: - move_energy: - name: g7 move energy - still_energy: - name: g7 still energy - g8: - move_energy: - name: g8 move energy - still_energy: - name: g8 still energy - -switch: - - platform: ld2410 - engineering_mode: - name: control ld2410 engineering mode - bluetooth: - name: control ld2410 bluetooth - -text_sensor: - - platform: ld2410 - version: - name: presenece sensor version - mac_address: - name: presenece sensor mac address +<<: !include common.yaml diff --git a/tests/components/ld2410/test.rp2040-ard.yaml b/tests/components/ld2410/test.rp2040-ard.yaml index afcaa81b3e..b516342f3b 100644 --- a/tests/components/ld2410/test.rp2040-ard.yaml +++ b/tests/components/ld2410/test.rp2040-ard.yaml @@ -1,169 +1,5 @@ -uart: - - id: uart_ld2410 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2410: - id: my_ld2410 - -binary_sensor: - - platform: ld2410 - has_target: - name: presence - has_moving_target: - name: movement - has_still_target: - name: still - out_pin_presence_status: - name: out pin presence status - -button: - - platform: ld2410 - factory_reset: - name: factory reset - restart: - name: restart - query_params: - name: query params - -number: - - platform: ld2410 - light_threshold: - name: light threshold - timeout: - name: timeout - max_move_distance_gate: - name: max move distance gate - max_still_distance_gate: - name: max still distance gate - g0: - move_threshold: - name: g0 move threshold - still_threshold: - name: g0 still threshold - g1: - move_threshold: - name: g1 move threshold - still_threshold: - name: g1 still threshold - g2: - move_threshold: - name: g2 move threshold - still_threshold: - name: g2 still threshold - g3: - move_threshold: - name: g3 move threshold - still_threshold: - name: g3 still threshold - g4: - move_threshold: - name: g4 move threshold - still_threshold: - name: g4 still threshold - g5: - move_threshold: - name: g5 move threshold - still_threshold: - name: g5 still threshold - g6: - move_threshold: - name: g6 move threshold - still_threshold: - name: g6 still threshold - g7: - move_threshold: - name: g7 move threshold - still_threshold: - name: g7 still threshold - g8: - move_threshold: - name: g8 move threshold - still_threshold: - name: g8 still threshold - -select: - - platform: ld2410 - distance_resolution: - name: distance resolution - baud_rate: - name: baud rate - light_function: - name: light function - out_pin_level: - name: out ping level - -sensor: - - platform: ld2410 - light: - name: light - moving_distance: - name: Moving distance - still_distance: - name: Still Distance - moving_energy: - name: Move Energy - still_energy: - name: Still Energy - detection_distance: - name: Distance Detection - g0: - move_energy: - name: g0 move energy - still_energy: - name: g0 still energy - g1: - move_energy: - name: g1 move energy - still_energy: - name: g1 still energy - g2: - move_energy: - name: g2 move energy - still_energy: - name: g2 still energy - g3: - move_energy: - name: g3 move energy - still_energy: - name: g3 still energy - g4: - move_energy: - name: g4 move energy - still_energy: - name: g4 still energy - g5: - move_energy: - name: g5 move energy - still_energy: - name: g5 still energy - g6: - move_energy: - name: g6 move energy - still_energy: - name: g6 still energy - g7: - move_energy: - name: g7 move energy - still_energy: - name: g7 still energy - g8: - move_energy: - name: g8 move energy - still_energy: - name: g8 still energy - -switch: - - platform: ld2410 - engineering_mode: - name: control ld2410 engineering mode - bluetooth: - name: control ld2410 bluetooth - -text_sensor: - - platform: ld2410 - version: - name: presenece sensor version - mac_address: - name: presenece sensor mac address +<<: !include common.yaml diff --git a/tests/components/ld2420/common.yaml b/tests/components/ld2420/common.yaml new file mode 100644 index 0000000000..76748aa8f0 --- /dev/null +++ b/tests/components/ld2420/common.yaml @@ -0,0 +1,132 @@ +uart: + - id: uart_ld2420 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +ld2420: + id: my_ld2420 + +binary_sensor: + - platform: ld2420 + has_target: + name: Presence + +button: + - platform: ld2420 + apply_config: + name: Apply Config + factory_reset: + name: Factory Reset + restart_module: + name: Restart Module + revert_config: + name: Undo Edits + +number: + - platform: ld2420 + presence_timeout: + name: Detection Presence Timeout + min_gate_distance: + name: Detection Gate Minimum + max_gate_distance: + name: Detection Gate Maximum + gate_move_sensitivity: + name: Move Calibration Sensitivity Factor + gate_still_sensitivity: + name: Still Calibration Sensitivity Factor + gate_0: + move_threshold: + name: Gate 0 Move Threshold + still_threshold: + name: Gate 0 Still Threshold + gate_1: + move_threshold: + name: Gate 1 Move Threshold + still_threshold: + name: Gate 1 Still Threshold + gate_2: + move_threshold: + name: Gate 2 Move Threshold + still_threshold: + name: Gate 2 Still Threshold + gate_3: + move_threshold: + name: Gate 3 Move Threshold + still_threshold: + name: Gate 3 Still Threshold + gate_4: + move_threshold: + name: Gate 4 Move Threshold + still_threshold: + name: Gate 4 Still Threshold + gate_5: + move_threshold: + name: Gate 5 Move Threshold + still_threshold: + name: Gate 5 Still Threshold + gate_6: + move_threshold: + name: Gate 6 Move Threshold + still_threshold: + name: Gate 6 Still Threshold + gate_7: + move_threshold: + name: Gate 7 Move Threshold + still_threshold: + name: Gate 7 Still Threshold + gate_8: + move_threshold: + name: Gate 8 Move Threshold + still_threshold: + name: Gate 8 Still Threshold + gate_9: + move_threshold: + name: Gate 9 Move Threshold + still_threshold: + name: Gate 9 Still Threshold + gate_10: + move_threshold: + name: Gate 10 Move Threshold + still_threshold: + name: Gate 10 Still Threshold + gate_11: + move_threshold: + name: Gate 11 Move Threshold + still_threshold: + name: Gate 11 Still Threshold + gate_12: + move_threshold: + name: Gate 12 Move Threshold + still_threshold: + name: Gate 12 Still Threshold + gate_13: + move_threshold: + name: Gate 13 Move Threshold + still_threshold: + name: Gate 13 Still Threshold + gate_14: + move_threshold: + name: Gate 14 Move Threshold + still_threshold: + name: Gate 14 Still Threshold + gate_15: + move_threshold: + name: Gate 15 Move Threshold + still_threshold: + name: Gate 15 Still Threshold + +select: + - platform: ld2420 + operating_mode: + name: Operating Mode + +sensor: + - platform: ld2420 + moving_distance: + name: "Moving distance (cm)" + +text_sensor: + - platform: ld2420 + fw_version: + name: LD2420 Firmware diff --git a/tests/components/ld2420/test.esp32-ard.yaml b/tests/components/ld2420/test.esp32-ard.yaml index 8c883664ec..f486544afa 100644 --- a/tests/components/ld2420/test.esp32-ard.yaml +++ b/tests/components/ld2420/test.esp32-ard.yaml @@ -1,132 +1,5 @@ -uart: - - id: uart_ld2420 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -ld2420: - id: my_ld2420 - -binary_sensor: - - platform: ld2420 - has_target: - name: Presence - -button: - - platform: ld2420 - apply_config: - name: Apply Config - factory_reset: - name: Factory Reset - restart_module: - name: Restart Module - revert_config: - name: Undo Edits - -number: - - platform: ld2420 - presence_timeout: - name: Detection Presence Timeout - min_gate_distance: - name: Detection Gate Minimum - max_gate_distance: - name: Detection Gate Maximum - gate_move_sensitivity: - name: Move Calibration Sensitivity Factor - gate_still_sensitivity: - name: Still Calibration Sensitivity Factor - gate_0: - move_threshold: - name: Gate 0 Move Threshold - still_threshold: - name: Gate 0 Still Threshold - gate_1: - move_threshold: - name: Gate 1 Move Threshold - still_threshold: - name: Gate 1 Still Threshold - gate_2: - move_threshold: - name: Gate 2 Move Threshold - still_threshold: - name: Gate 2 Still Threshold - gate_3: - move_threshold: - name: Gate 3 Move Threshold - still_threshold: - name: Gate 3 Still Threshold - gate_4: - move_threshold: - name: Gate 4 Move Threshold - still_threshold: - name: Gate 4 Still Threshold - gate_5: - move_threshold: - name: Gate 5 Move Threshold - still_threshold: - name: Gate 5 Still Threshold - gate_6: - move_threshold: - name: Gate 6 Move Threshold - still_threshold: - name: Gate 6 Still Threshold - gate_7: - move_threshold: - name: Gate 7 Move Threshold - still_threshold: - name: Gate 7 Still Threshold - gate_8: - move_threshold: - name: Gate 8 Move Threshold - still_threshold: - name: Gate 8 Still Threshold - gate_9: - move_threshold: - name: Gate 9 Move Threshold - still_threshold: - name: Gate 9 Still Threshold - gate_10: - move_threshold: - name: Gate 10 Move Threshold - still_threshold: - name: Gate 10 Still Threshold - gate_11: - move_threshold: - name: Gate 11 Move Threshold - still_threshold: - name: Gate 11 Still Threshold - gate_12: - move_threshold: - name: Gate 12 Move Threshold - still_threshold: - name: Gate 12 Still Threshold - gate_13: - move_threshold: - name: Gate 13 Move Threshold - still_threshold: - name: Gate 13 Still Threshold - gate_14: - move_threshold: - name: Gate 14 Move Threshold - still_threshold: - name: Gate 14 Still Threshold - gate_15: - move_threshold: - name: Gate 15 Move Threshold - still_threshold: - name: Gate 15 Still Threshold - -select: - - platform: ld2420 - operating_mode: - name: Operating Mode - -sensor: - - platform: ld2420 - moving_distance: - name: "Moving distance (cm)" - -text_sensor: - - platform: ld2420 - fw_version: - name: LD2420 Firmware +<<: !include common.yaml diff --git a/tests/components/ld2420/test.esp32-c3-ard.yaml b/tests/components/ld2420/test.esp32-c3-ard.yaml index 5e0b9db1c2..b516342f3b 100644 --- a/tests/components/ld2420/test.esp32-c3-ard.yaml +++ b/tests/components/ld2420/test.esp32-c3-ard.yaml @@ -1,132 +1,5 @@ -uart: - - id: uart_ld2420 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2420: - id: my_ld2420 - -binary_sensor: - - platform: ld2420 - has_target: - name: Presence - -button: - - platform: ld2420 - apply_config: - name: Apply Config - factory_reset: - name: Factory Reset - restart_module: - name: Restart Module - revert_config: - name: Undo Edits - -number: - - platform: ld2420 - presence_timeout: - name: Detection Presence Timeout - min_gate_distance: - name: Detection Gate Minimum - max_gate_distance: - name: Detection Gate Maximum - gate_move_sensitivity: - name: Move Calibration Sensitivity Factor - gate_still_sensitivity: - name: Still Calibration Sensitivity Factor - gate_0: - move_threshold: - name: Gate 0 Move Threshold - still_threshold: - name: Gate 0 Still Threshold - gate_1: - move_threshold: - name: Gate 1 Move Threshold - still_threshold: - name: Gate 1 Still Threshold - gate_2: - move_threshold: - name: Gate 2 Move Threshold - still_threshold: - name: Gate 2 Still Threshold - gate_3: - move_threshold: - name: Gate 3 Move Threshold - still_threshold: - name: Gate 3 Still Threshold - gate_4: - move_threshold: - name: Gate 4 Move Threshold - still_threshold: - name: Gate 4 Still Threshold - gate_5: - move_threshold: - name: Gate 5 Move Threshold - still_threshold: - name: Gate 5 Still Threshold - gate_6: - move_threshold: - name: Gate 6 Move Threshold - still_threshold: - name: Gate 6 Still Threshold - gate_7: - move_threshold: - name: Gate 7 Move Threshold - still_threshold: - name: Gate 7 Still Threshold - gate_8: - move_threshold: - name: Gate 8 Move Threshold - still_threshold: - name: Gate 8 Still Threshold - gate_9: - move_threshold: - name: Gate 9 Move Threshold - still_threshold: - name: Gate 9 Still Threshold - gate_10: - move_threshold: - name: Gate 10 Move Threshold - still_threshold: - name: Gate 10 Still Threshold - gate_11: - move_threshold: - name: Gate 11 Move Threshold - still_threshold: - name: Gate 11 Still Threshold - gate_12: - move_threshold: - name: Gate 12 Move Threshold - still_threshold: - name: Gate 12 Still Threshold - gate_13: - move_threshold: - name: Gate 13 Move Threshold - still_threshold: - name: Gate 13 Still Threshold - gate_14: - move_threshold: - name: Gate 14 Move Threshold - still_threshold: - name: Gate 14 Still Threshold - gate_15: - move_threshold: - name: Gate 15 Move Threshold - still_threshold: - name: Gate 15 Still Threshold - -select: - - platform: ld2420 - operating_mode: - name: Operating Mode - -sensor: - - platform: ld2420 - moving_distance: - name: "Moving distance (cm)" - -text_sensor: - - platform: ld2420 - fw_version: - name: LD2420 Firmware +<<: !include common.yaml diff --git a/tests/components/ld2420/test.esp32-c3-idf.yaml b/tests/components/ld2420/test.esp32-c3-idf.yaml index 5e0b9db1c2..b516342f3b 100644 --- a/tests/components/ld2420/test.esp32-c3-idf.yaml +++ b/tests/components/ld2420/test.esp32-c3-idf.yaml @@ -1,132 +1,5 @@ -uart: - - id: uart_ld2420 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2420: - id: my_ld2420 - -binary_sensor: - - platform: ld2420 - has_target: - name: Presence - -button: - - platform: ld2420 - apply_config: - name: Apply Config - factory_reset: - name: Factory Reset - restart_module: - name: Restart Module - revert_config: - name: Undo Edits - -number: - - platform: ld2420 - presence_timeout: - name: Detection Presence Timeout - min_gate_distance: - name: Detection Gate Minimum - max_gate_distance: - name: Detection Gate Maximum - gate_move_sensitivity: - name: Move Calibration Sensitivity Factor - gate_still_sensitivity: - name: Still Calibration Sensitivity Factor - gate_0: - move_threshold: - name: Gate 0 Move Threshold - still_threshold: - name: Gate 0 Still Threshold - gate_1: - move_threshold: - name: Gate 1 Move Threshold - still_threshold: - name: Gate 1 Still Threshold - gate_2: - move_threshold: - name: Gate 2 Move Threshold - still_threshold: - name: Gate 2 Still Threshold - gate_3: - move_threshold: - name: Gate 3 Move Threshold - still_threshold: - name: Gate 3 Still Threshold - gate_4: - move_threshold: - name: Gate 4 Move Threshold - still_threshold: - name: Gate 4 Still Threshold - gate_5: - move_threshold: - name: Gate 5 Move Threshold - still_threshold: - name: Gate 5 Still Threshold - gate_6: - move_threshold: - name: Gate 6 Move Threshold - still_threshold: - name: Gate 6 Still Threshold - gate_7: - move_threshold: - name: Gate 7 Move Threshold - still_threshold: - name: Gate 7 Still Threshold - gate_8: - move_threshold: - name: Gate 8 Move Threshold - still_threshold: - name: Gate 8 Still Threshold - gate_9: - move_threshold: - name: Gate 9 Move Threshold - still_threshold: - name: Gate 9 Still Threshold - gate_10: - move_threshold: - name: Gate 10 Move Threshold - still_threshold: - name: Gate 10 Still Threshold - gate_11: - move_threshold: - name: Gate 11 Move Threshold - still_threshold: - name: Gate 11 Still Threshold - gate_12: - move_threshold: - name: Gate 12 Move Threshold - still_threshold: - name: Gate 12 Still Threshold - gate_13: - move_threshold: - name: Gate 13 Move Threshold - still_threshold: - name: Gate 13 Still Threshold - gate_14: - move_threshold: - name: Gate 14 Move Threshold - still_threshold: - name: Gate 14 Still Threshold - gate_15: - move_threshold: - name: Gate 15 Move Threshold - still_threshold: - name: Gate 15 Still Threshold - -select: - - platform: ld2420 - operating_mode: - name: Operating Mode - -sensor: - - platform: ld2420 - moving_distance: - name: "Moving distance (cm)" - -text_sensor: - - platform: ld2420 - fw_version: - name: LD2420 Firmware +<<: !include common.yaml diff --git a/tests/components/ld2420/test.esp32-idf.yaml b/tests/components/ld2420/test.esp32-idf.yaml index 8c883664ec..f486544afa 100644 --- a/tests/components/ld2420/test.esp32-idf.yaml +++ b/tests/components/ld2420/test.esp32-idf.yaml @@ -1,132 +1,5 @@ -uart: - - id: uart_ld2420 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -ld2420: - id: my_ld2420 - -binary_sensor: - - platform: ld2420 - has_target: - name: Presence - -button: - - platform: ld2420 - apply_config: - name: Apply Config - factory_reset: - name: Factory Reset - restart_module: - name: Restart Module - revert_config: - name: Undo Edits - -number: - - platform: ld2420 - presence_timeout: - name: Detection Presence Timeout - min_gate_distance: - name: Detection Gate Minimum - max_gate_distance: - name: Detection Gate Maximum - gate_move_sensitivity: - name: Move Calibration Sensitivity Factor - gate_still_sensitivity: - name: Still Calibration Sensitivity Factor - gate_0: - move_threshold: - name: Gate 0 Move Threshold - still_threshold: - name: Gate 0 Still Threshold - gate_1: - move_threshold: - name: Gate 1 Move Threshold - still_threshold: - name: Gate 1 Still Threshold - gate_2: - move_threshold: - name: Gate 2 Move Threshold - still_threshold: - name: Gate 2 Still Threshold - gate_3: - move_threshold: - name: Gate 3 Move Threshold - still_threshold: - name: Gate 3 Still Threshold - gate_4: - move_threshold: - name: Gate 4 Move Threshold - still_threshold: - name: Gate 4 Still Threshold - gate_5: - move_threshold: - name: Gate 5 Move Threshold - still_threshold: - name: Gate 5 Still Threshold - gate_6: - move_threshold: - name: Gate 6 Move Threshold - still_threshold: - name: Gate 6 Still Threshold - gate_7: - move_threshold: - name: Gate 7 Move Threshold - still_threshold: - name: Gate 7 Still Threshold - gate_8: - move_threshold: - name: Gate 8 Move Threshold - still_threshold: - name: Gate 8 Still Threshold - gate_9: - move_threshold: - name: Gate 9 Move Threshold - still_threshold: - name: Gate 9 Still Threshold - gate_10: - move_threshold: - name: Gate 10 Move Threshold - still_threshold: - name: Gate 10 Still Threshold - gate_11: - move_threshold: - name: Gate 11 Move Threshold - still_threshold: - name: Gate 11 Still Threshold - gate_12: - move_threshold: - name: Gate 12 Move Threshold - still_threshold: - name: Gate 12 Still Threshold - gate_13: - move_threshold: - name: Gate 13 Move Threshold - still_threshold: - name: Gate 13 Still Threshold - gate_14: - move_threshold: - name: Gate 14 Move Threshold - still_threshold: - name: Gate 14 Still Threshold - gate_15: - move_threshold: - name: Gate 15 Move Threshold - still_threshold: - name: Gate 15 Still Threshold - -select: - - platform: ld2420 - operating_mode: - name: Operating Mode - -sensor: - - platform: ld2420 - moving_distance: - name: "Moving distance (cm)" - -text_sensor: - - platform: ld2420 - fw_version: - name: LD2420 Firmware +<<: !include common.yaml diff --git a/tests/components/ld2420/test.esp8266-ard.yaml b/tests/components/ld2420/test.esp8266-ard.yaml index 5e0b9db1c2..b516342f3b 100644 --- a/tests/components/ld2420/test.esp8266-ard.yaml +++ b/tests/components/ld2420/test.esp8266-ard.yaml @@ -1,132 +1,5 @@ -uart: - - id: uart_ld2420 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2420: - id: my_ld2420 - -binary_sensor: - - platform: ld2420 - has_target: - name: Presence - -button: - - platform: ld2420 - apply_config: - name: Apply Config - factory_reset: - name: Factory Reset - restart_module: - name: Restart Module - revert_config: - name: Undo Edits - -number: - - platform: ld2420 - presence_timeout: - name: Detection Presence Timeout - min_gate_distance: - name: Detection Gate Minimum - max_gate_distance: - name: Detection Gate Maximum - gate_move_sensitivity: - name: Move Calibration Sensitivity Factor - gate_still_sensitivity: - name: Still Calibration Sensitivity Factor - gate_0: - move_threshold: - name: Gate 0 Move Threshold - still_threshold: - name: Gate 0 Still Threshold - gate_1: - move_threshold: - name: Gate 1 Move Threshold - still_threshold: - name: Gate 1 Still Threshold - gate_2: - move_threshold: - name: Gate 2 Move Threshold - still_threshold: - name: Gate 2 Still Threshold - gate_3: - move_threshold: - name: Gate 3 Move Threshold - still_threshold: - name: Gate 3 Still Threshold - gate_4: - move_threshold: - name: Gate 4 Move Threshold - still_threshold: - name: Gate 4 Still Threshold - gate_5: - move_threshold: - name: Gate 5 Move Threshold - still_threshold: - name: Gate 5 Still Threshold - gate_6: - move_threshold: - name: Gate 6 Move Threshold - still_threshold: - name: Gate 6 Still Threshold - gate_7: - move_threshold: - name: Gate 7 Move Threshold - still_threshold: - name: Gate 7 Still Threshold - gate_8: - move_threshold: - name: Gate 8 Move Threshold - still_threshold: - name: Gate 8 Still Threshold - gate_9: - move_threshold: - name: Gate 9 Move Threshold - still_threshold: - name: Gate 9 Still Threshold - gate_10: - move_threshold: - name: Gate 10 Move Threshold - still_threshold: - name: Gate 10 Still Threshold - gate_11: - move_threshold: - name: Gate 11 Move Threshold - still_threshold: - name: Gate 11 Still Threshold - gate_12: - move_threshold: - name: Gate 12 Move Threshold - still_threshold: - name: Gate 12 Still Threshold - gate_13: - move_threshold: - name: Gate 13 Move Threshold - still_threshold: - name: Gate 13 Still Threshold - gate_14: - move_threshold: - name: Gate 14 Move Threshold - still_threshold: - name: Gate 14 Still Threshold - gate_15: - move_threshold: - name: Gate 15 Move Threshold - still_threshold: - name: Gate 15 Still Threshold - -select: - - platform: ld2420 - operating_mode: - name: Operating Mode - -sensor: - - platform: ld2420 - moving_distance: - name: "Moving distance (cm)" - -text_sensor: - - platform: ld2420 - fw_version: - name: LD2420 Firmware +<<: !include common.yaml diff --git a/tests/components/ld2420/test.rp2040-ard.yaml b/tests/components/ld2420/test.rp2040-ard.yaml index 5e0b9db1c2..b516342f3b 100644 --- a/tests/components/ld2420/test.rp2040-ard.yaml +++ b/tests/components/ld2420/test.rp2040-ard.yaml @@ -1,132 +1,5 @@ -uart: - - id: uart_ld2420 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -ld2420: - id: my_ld2420 - -binary_sensor: - - platform: ld2420 - has_target: - name: Presence - -button: - - platform: ld2420 - apply_config: - name: Apply Config - factory_reset: - name: Factory Reset - restart_module: - name: Restart Module - revert_config: - name: Undo Edits - -number: - - platform: ld2420 - presence_timeout: - name: Detection Presence Timeout - min_gate_distance: - name: Detection Gate Minimum - max_gate_distance: - name: Detection Gate Maximum - gate_move_sensitivity: - name: Move Calibration Sensitivity Factor - gate_still_sensitivity: - name: Still Calibration Sensitivity Factor - gate_0: - move_threshold: - name: Gate 0 Move Threshold - still_threshold: - name: Gate 0 Still Threshold - gate_1: - move_threshold: - name: Gate 1 Move Threshold - still_threshold: - name: Gate 1 Still Threshold - gate_2: - move_threshold: - name: Gate 2 Move Threshold - still_threshold: - name: Gate 2 Still Threshold - gate_3: - move_threshold: - name: Gate 3 Move Threshold - still_threshold: - name: Gate 3 Still Threshold - gate_4: - move_threshold: - name: Gate 4 Move Threshold - still_threshold: - name: Gate 4 Still Threshold - gate_5: - move_threshold: - name: Gate 5 Move Threshold - still_threshold: - name: Gate 5 Still Threshold - gate_6: - move_threshold: - name: Gate 6 Move Threshold - still_threshold: - name: Gate 6 Still Threshold - gate_7: - move_threshold: - name: Gate 7 Move Threshold - still_threshold: - name: Gate 7 Still Threshold - gate_8: - move_threshold: - name: Gate 8 Move Threshold - still_threshold: - name: Gate 8 Still Threshold - gate_9: - move_threshold: - name: Gate 9 Move Threshold - still_threshold: - name: Gate 9 Still Threshold - gate_10: - move_threshold: - name: Gate 10 Move Threshold - still_threshold: - name: Gate 10 Still Threshold - gate_11: - move_threshold: - name: Gate 11 Move Threshold - still_threshold: - name: Gate 11 Still Threshold - gate_12: - move_threshold: - name: Gate 12 Move Threshold - still_threshold: - name: Gate 12 Still Threshold - gate_13: - move_threshold: - name: Gate 13 Move Threshold - still_threshold: - name: Gate 13 Still Threshold - gate_14: - move_threshold: - name: Gate 14 Move Threshold - still_threshold: - name: Gate 14 Still Threshold - gate_15: - move_threshold: - name: Gate 15 Move Threshold - still_threshold: - name: Gate 15 Still Threshold - -select: - - platform: ld2420 - operating_mode: - name: Operating Mode - -sensor: - - platform: ld2420 - moving_distance: - name: "Moving distance (cm)" - -text_sensor: - - platform: ld2420 - fw_version: - name: LD2420 Firmware +<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/common.yaml b/tests/components/lilygo_t5_47/common.yaml new file mode 100644 index 0000000000..f539c58d74 --- /dev/null +++ b/tests/components/lilygo_t5_47/common.yaml @@ -0,0 +1,24 @@ +i2c: + - id: i2c_lilygo_t5_47 + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: lilygo_t5_47 + id: lilygo_touchscreen + interrupt_pin: ${interrupt_pin} + display: ssd1306_display + on_touch: + - logger.log: + format: Touch at (%d, %d) + args: [touch.x, touch.y] diff --git a/tests/components/lilygo_t5_47/test.esp32-ard.yaml b/tests/components/lilygo_t5_47/test.esp32-ard.yaml index 35eb3df022..342f0b6d8b 100644 --- a/tests/components/lilygo_t5_47/test.esp32-ard.yaml +++ b/tests/components/lilygo_t5_47/test.esp32-ard.yaml @@ -1,24 +1,7 @@ -i2c: - - id: i2c_lilygo_t5_47 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: lilygo_t5_47 - id: lilygo_touchscreen - interrupt_pin: 14 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml b/tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml index 41e81103ac..061a98ce24 100644 --- a/tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml +++ b/tests/components/lilygo_t5_47/test.esp32-c3-ard.yaml @@ -1,24 +1,7 @@ -i2c: - - id: i2c_lilygo_t5_47 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: lilygo_t5_47 - id: lilygo_touchscreen - interrupt_pin: 6 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml b/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml index 41e81103ac..061a98ce24 100644 --- a/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml +++ b/tests/components/lilygo_t5_47/test.esp32-c3-idf.yaml @@ -1,24 +1,7 @@ -i2c: - - id: i2c_lilygo_t5_47 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: lilygo_t5_47 - id: lilygo_touchscreen - interrupt_pin: 6 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp32-idf.yaml b/tests/components/lilygo_t5_47/test.esp32-idf.yaml index 35eb3df022..342f0b6d8b 100644 --- a/tests/components/lilygo_t5_47/test.esp32-idf.yaml +++ b/tests/components/lilygo_t5_47/test.esp32-idf.yaml @@ -1,24 +1,7 @@ -i2c: - - id: i2c_lilygo_t5_47 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: lilygo_t5_47 - id: lilygo_touchscreen - interrupt_pin: 14 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.esp8266-ard.yaml b/tests/components/lilygo_t5_47/test.esp8266-ard.yaml index 766c492b12..b446a75f13 100644 --- a/tests/components/lilygo_t5_47/test.esp8266-ard.yaml +++ b/tests/components/lilygo_t5_47/test.esp8266-ard.yaml @@ -1,24 +1,7 @@ -i2c: - - id: i2c_lilygo_t5_47 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + interrupt_pin: GPIO12 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: lilygo_t5_47 - id: lilygo_touchscreen - interrupt_pin: 12 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/lilygo_t5_47/test.rp2040-ard.yaml b/tests/components/lilygo_t5_47/test.rp2040-ard.yaml index 41e81103ac..061a98ce24 100644 --- a/tests/components/lilygo_t5_47/test.rp2040-ard.yaml +++ b/tests/components/lilygo_t5_47/test.rp2040-ard.yaml @@ -1,24 +1,7 @@ -i2c: - - id: i2c_lilygo_t5_47 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: lilygo_t5_47 - id: lilygo_touchscreen - interrupt_pin: 6 - display: ssd1306_display - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/ltr390/common.yaml b/tests/components/ltr390/common.yaml new file mode 100644 index 0000000000..2eebe9d1c3 --- /dev/null +++ b/tests/components/ltr390/common.yaml @@ -0,0 +1,38 @@ +i2c: + - id: i2c_ltr390 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ltr390 + uv: + name: LTR390 UV + uv_index: + name: LTR390 UVI + light: + name: LTR390 Light + ambient_light: + name: LTR390 ALS + gain: X3 + resolution: 18 + window_correction_factor: 1.0 + address: 0x53 + update_interval: 60s + - platform: ltr390 + uv: + name: LTR390 UV + uv_index: + name: LTR390 UVI + light: + name: LTR390 Light + ambient_light: + name: LTR390 ALS + gain: + ambient_light: X9 + uv: X3 + resolution: + ambient_light: 18 + uv: 13 + window_correction_factor: 1.0 + address: 0x53 + update_interval: 60s diff --git a/tests/components/ltr390/test.esp32-ard.yaml b/tests/components/ltr390/test.esp32-ard.yaml index bdfe349b77..63c3bd6afd 100644 --- a/tests/components/ltr390/test.esp32-ard.yaml +++ b/tests/components/ltr390/test.esp32-ard.yaml @@ -1,38 +1,5 @@ -i2c: - - id: i2c_ltr390 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ltr390 - uv: - name: LTR390 UV - uv_index: - name: LTR390 UVI - light: - name: LTR390 Light - ambient_light: - name: LTR390 ALS - gain: X3 - resolution: 18 - window_correction_factor: 1.0 - address: 0x53 - update_interval: 60s - - platform: ltr390 - uv: - name: LTR390 UV - uv_index: - name: LTR390 UVI - light: - name: LTR390 Light - ambient_light: - name: LTR390 ALS - gain: - ambient_light: X9 - uv: X3 - resolution: - ambient_light: 18 - uv: 13 - window_correction_factor: 1.0 - address: 0x53 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ltr390/test.esp32-c3-ard.yaml b/tests/components/ltr390/test.esp32-c3-ard.yaml index fee0f37ce1..ee2c29ca4e 100644 --- a/tests/components/ltr390/test.esp32-c3-ard.yaml +++ b/tests/components/ltr390/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ltr390 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ltr390 - uv: - name: LTR390 UV - uv_index: - name: LTR390 UVI - light: - name: LTR390 Light - ambient_light: - name: LTR390 ALS - gain: X3 - resolution: 18 - window_correction_factor: 1.0 - address: 0x53 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ltr390/test.esp32-c3-idf.yaml b/tests/components/ltr390/test.esp32-c3-idf.yaml index fee0f37ce1..ee2c29ca4e 100644 --- a/tests/components/ltr390/test.esp32-c3-idf.yaml +++ b/tests/components/ltr390/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ltr390 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ltr390 - uv: - name: LTR390 UV - uv_index: - name: LTR390 UVI - light: - name: LTR390 Light - ambient_light: - name: LTR390 ALS - gain: X3 - resolution: 18 - window_correction_factor: 1.0 - address: 0x53 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ltr390/test.esp32-idf.yaml b/tests/components/ltr390/test.esp32-idf.yaml index 9786c7dac3..63c3bd6afd 100644 --- a/tests/components/ltr390/test.esp32-idf.yaml +++ b/tests/components/ltr390/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ltr390 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ltr390 - uv: - name: LTR390 UV - uv_index: - name: LTR390 UVI - light: - name: LTR390 Light - ambient_light: - name: LTR390 ALS - gain: X3 - resolution: 18 - window_correction_factor: 1.0 - address: 0x53 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ltr390/test.esp8266-ard.yaml b/tests/components/ltr390/test.esp8266-ard.yaml index 149f46f9c8..ee2c29ca4e 100644 --- a/tests/components/ltr390/test.esp8266-ard.yaml +++ b/tests/components/ltr390/test.esp8266-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_ltr390 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ltr390 - uv: - name: LTR390 UV - uv_index: - name: LTR390 UVI - light: - name: LTR390 Light - ambient_light: - name: LTR390 ALS - gain: - ambient_light: X9 - uv: X3 - resolution: 18 - window_correction_factor: 1.0 - address: 0x53 - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ltr390/test.rp2040-ard.yaml b/tests/components/ltr390/test.rp2040-ard.yaml index fee0f37ce1..ee2c29ca4e 100644 --- a/tests/components/ltr390/test.rp2040-ard.yaml +++ b/tests/components/ltr390/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ltr390 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ltr390 - uv: - name: LTR390 UV - uv_index: - name: LTR390 UVI - light: - name: LTR390 Light - ambient_light: - name: LTR390 ALS - gain: X3 - resolution: 18 - window_correction_factor: 1.0 - address: 0x53 - update_interval: 60s +<<: !include common.yaml From 4e4566361f074cbcd33b5462cafad81bc6b96275 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:05:59 -0600 Subject: [PATCH 0960/1052] [CI] Consolidate some tests (M) (#8202) --- tests/components/max31855/common.yaml | 13 ++++ tests/components/max31855/test.esp32-ard.yaml | 18 ++---- .../max31855/test.esp32-c3-ard.yaml | 18 ++---- .../max31855/test.esp32-c3-idf.yaml | 18 ++---- tests/components/max31855/test.esp32-idf.yaml | 18 ++---- .../components/max31855/test.esp8266-ard.yaml | 18 ++---- .../components/max31855/test.rp2040-ard.yaml | 18 ++---- tests/components/max31856/common.yaml | 13 ++++ tests/components/max31856/test.esp32-ard.yaml | 18 ++---- .../max31856/test.esp32-c3-ard.yaml | 19 ++---- .../max31856/test.esp32-c3-idf.yaml | 19 ++---- tests/components/max31856/test.esp32-idf.yaml | 18 ++---- .../components/max31856/test.esp8266-ard.yaml | 19 ++---- .../components/max31856/test.rp2040-ard.yaml | 19 ++---- tests/components/max31865/common.yaml | 13 ++++ tests/components/max31865/test.esp32-ard.yaml | 18 ++---- .../max31865/test.esp32-c3-ard.yaml | 18 ++---- .../max31865/test.esp32-c3-idf.yaml | 18 ++---- tests/components/max31865/test.esp32-idf.yaml | 18 ++---- .../components/max31865/test.esp8266-ard.yaml | 18 ++---- .../components/max31865/test.rp2040-ard.yaml | 18 ++---- tests/components/max44009/common.yaml | 12 ++++ tests/components/max44009/test.esp32-ard.yaml | 15 ++--- .../max44009/test.esp32-c3-ard.yaml | 15 ++--- .../max44009/test.esp32-c3-idf.yaml | 15 ++--- tests/components/max44009/test.esp32-idf.yaml | 15 ++--- .../components/max44009/test.esp8266-ard.yaml | 15 ++--- .../components/max44009/test.rp2040-ard.yaml | 15 ++--- tests/components/max6675/common.yaml | 11 ++++ tests/components/max6675/test.esp32-ard.yaml | 16 ++--- .../components/max6675/test.esp32-c3-ard.yaml | 16 ++--- .../components/max6675/test.esp32-c3-idf.yaml | 16 ++--- tests/components/max6675/test.esp32-idf.yaml | 16 ++--- .../components/max6675/test.esp8266-ard.yaml | 16 ++--- tests/components/max6675/test.rp2040-ard.yaml | 16 ++--- tests/components/max6956/common.yaml | 19 ++++++ tests/components/max6956/test.esp32-ard.yaml | 22 ++----- .../components/max6956/test.esp32-c3-ard.yaml | 22 ++----- .../components/max6956/test.esp32-c3-idf.yaml | 22 ++----- tests/components/max6956/test.esp32-idf.yaml | 22 ++----- .../components/max6956/test.esp8266-ard.yaml | 22 ++----- tests/components/max6956/test.rp2040-ard.yaml | 22 ++----- tests/components/max7219/common.yaml | 12 ++++ tests/components/max7219/test.esp32-ard.yaml | 17 ++--- .../components/max7219/test.esp32-c3-ard.yaml | 17 ++--- .../components/max7219/test.esp32-c3-idf.yaml | 17 ++--- tests/components/max7219/test.esp32-idf.yaml | 17 ++--- .../components/max7219/test.esp8266-ard.yaml | 17 ++--- tests/components/max7219/test.rp2040-ard.yaml | 17 ++--- tests/components/max7219digit/common.yaml | 16 +++++ .../max7219digit/test.esp32-ard.yaml | 21 ++----- .../max7219digit/test.esp32-c3-ard.yaml | 21 ++----- .../max7219digit/test.esp32-c3-idf.yaml | 21 ++----- .../max7219digit/test.esp32-idf.yaml | 21 ++----- .../max7219digit/test.esp8266-ard.yaml | 21 ++----- .../max7219digit/test.rp2040-ard.yaml | 21 ++----- tests/components/max9611/common.yaml | 18 ++++++ tests/components/max9611/test.esp32-ard.yaml | 21 ++----- .../components/max9611/test.esp32-c3-ard.yaml | 21 ++----- .../components/max9611/test.esp32-c3-idf.yaml | 21 ++----- tests/components/max9611/test.esp32-idf.yaml | 21 ++----- .../components/max9611/test.esp8266-ard.yaml | 21 ++----- tests/components/max9611/test.rp2040-ard.yaml | 21 ++----- tests/components/mcp23008/common.yaml | 23 +++++++ tests/components/mcp23008/test.esp32-ard.yaml | 26 ++------ .../mcp23008/test.esp32-c3-ard.yaml | 26 ++------ .../mcp23008/test.esp32-c3-idf.yaml | 26 ++------ tests/components/mcp23008/test.esp32-idf.yaml | 26 ++------ .../components/mcp23008/test.esp8266-ard.yaml | 26 ++------ .../components/mcp23008/test.rp2040-ard.yaml | 26 ++------ tests/components/mcp23016/common.yaml | 23 +++++++ tests/components/mcp23016/test.esp32-ard.yaml | 26 ++------ .../mcp23016/test.esp32-c3-ard.yaml | 26 ++------ .../mcp23016/test.esp32-c3-idf.yaml | 26 ++------ tests/components/mcp23016/test.esp32-idf.yaml | 26 ++------ .../components/mcp23016/test.esp8266-ard.yaml | 26 ++------ .../components/mcp23016/test.rp2040-ard.yaml | 26 ++------ tests/components/mcp23017/common.yaml | 23 +++++++ tests/components/mcp23017/test.esp32-ard.yaml | 26 ++------ .../mcp23017/test.esp32-c3-ard.yaml | 26 ++------ .../mcp23017/test.esp32-c3-idf.yaml | 26 ++------ tests/components/mcp23017/test.esp32-idf.yaml | 26 ++------ .../components/mcp23017/test.esp8266-ard.yaml | 26 ++------ .../components/mcp23017/test.rp2040-ard.yaml | 26 ++------ tests/components/mcp23s08/common.yaml | 10 +++ tests/components/mcp23s08/test.esp32-ard.yaml | 15 ++--- .../mcp23s08/test.esp32-c3-ard.yaml | 15 ++--- .../mcp23s08/test.esp32-c3-idf.yaml | 15 ++--- tests/components/mcp23s08/test.esp32-idf.yaml | 15 ++--- .../components/mcp23s08/test.esp8266-ard.yaml | 15 ++--- .../components/mcp23s08/test.rp2040-ard.yaml | 15 ++--- tests/components/mcp23s17/common.yaml | 10 +++ tests/components/mcp23s17/test.esp32-ard.yaml | 15 ++--- .../mcp23s17/test.esp32-c3-ard.yaml | 15 ++--- .../mcp23s17/test.esp32-c3-idf.yaml | 15 ++--- tests/components/mcp23s17/test.esp32-idf.yaml | 15 ++--- .../components/mcp23s17/test.esp8266-ard.yaml | 15 ++--- .../components/mcp23s17/test.rp2040-ard.yaml | 15 ++--- tests/components/mcp2515/common.yaml | 44 +++++++++++++ tests/components/mcp2515/test.esp32-ard.yaml | 49 ++------------- .../components/mcp2515/test.esp32-c3-ard.yaml | 49 ++------------- .../components/mcp2515/test.esp32-c3-idf.yaml | 49 ++------------- tests/components/mcp2515/test.esp32-idf.yaml | 49 ++------------- .../components/mcp2515/test.esp8266-ard.yaml | 49 ++------------- tests/components/mcp2515/test.rp2040-ard.yaml | 49 ++------------- tests/components/mcp3008/common.yaml | 17 +++++ tests/components/mcp3008/test.esp32-ard.yaml | 22 ++----- .../components/mcp3008/test.esp32-c3-ard.yaml | 22 ++----- .../components/mcp3008/test.esp32-c3-idf.yaml | 22 ++----- tests/components/mcp3008/test.esp32-idf.yaml | 22 ++----- .../components/mcp3008/test.esp8266-ard.yaml | 22 ++----- tests/components/mcp3008/test.rp2040-ard.yaml | 22 ++----- tests/components/mcp3204/common.yaml | 16 +++++ tests/components/mcp3204/test.esp32-ard.yaml | 21 ++----- .../components/mcp3204/test.esp32-c3-ard.yaml | 21 ++----- .../components/mcp3204/test.esp32-c3-idf.yaml | 21 ++----- tests/components/mcp3204/test.esp32-idf.yaml | 21 ++----- .../components/mcp3204/test.esp8266-ard.yaml | 21 ++----- tests/components/mcp3204/test.rp2040-ard.yaml | 21 ++----- tests/components/mcp4725/common.yaml | 8 +++ tests/components/mcp4725/test.esp32-ard.yaml | 11 ++-- .../components/mcp4725/test.esp32-c3-ard.yaml | 11 ++-- .../components/mcp4725/test.esp32-c3-idf.yaml | 11 ++-- tests/components/mcp4725/test.esp32-idf.yaml | 11 ++-- .../components/mcp4725/test.esp8266-ard.yaml | 11 ++-- tests/components/mcp4725/test.rp2040-ard.yaml | 11 ++-- tests/components/mcp4728/common.yaml | 31 +++++++++ tests/components/mcp4728/test.esp32-ard.yaml | 34 ++-------- .../components/mcp4728/test.esp32-c3-ard.yaml | 34 ++-------- .../components/mcp4728/test.esp32-c3-idf.yaml | 34 ++-------- tests/components/mcp4728/test.esp32-idf.yaml | 34 ++-------- .../components/mcp4728/test.esp8266-ard.yaml | 34 ++-------- tests/components/mcp4728/test.rp2040-ard.yaml | 34 ++-------- tests/components/mcp47a1/common.yaml | 8 +++ tests/components/mcp47a1/test.esp32-ard.yaml | 11 ++-- .../components/mcp47a1/test.esp32-c3-ard.yaml | 11 ++-- .../components/mcp47a1/test.esp32-c3-idf.yaml | 11 ++-- tests/components/mcp47a1/test.esp32-idf.yaml | 11 ++-- .../components/mcp47a1/test.esp8266-ard.yaml | 11 ++-- tests/components/mcp47a1/test.rp2040-ard.yaml | 11 ++-- tests/components/mcp9600/common.yaml | 12 ++++ tests/components/mcp9600/test.esp32-ard.yaml | 15 ++--- .../components/mcp9600/test.esp32-c3-ard.yaml | 15 ++--- .../components/mcp9600/test.esp32-c3-idf.yaml | 15 ++--- tests/components/mcp9600/test.esp32-idf.yaml | 15 ++--- .../components/mcp9600/test.esp8266-ard.yaml | 15 ++--- tests/components/mcp9600/test.rp2040-ard.yaml | 15 ++--- tests/components/mcp9808/common.yaml | 8 +++ tests/components/mcp9808/test.esp32-ard.yaml | 11 ++-- .../components/mcp9808/test.esp32-c3-ard.yaml | 11 ++-- .../components/mcp9808/test.esp32-c3-idf.yaml | 11 ++-- tests/components/mcp9808/test.esp32-idf.yaml | 11 ++-- .../components/mcp9808/test.esp8266-ard.yaml | 11 ++-- tests/components/mcp9808/test.rp2040-ard.yaml | 11 ++-- tests/components/mhz19/common.yaml | 14 +++++ tests/components/mhz19/test.esp32-ard.yaml | 17 ++--- tests/components/mhz19/test.esp32-c3-ard.yaml | 17 ++--- tests/components/mhz19/test.esp32-c3-idf.yaml | 17 ++--- tests/components/mhz19/test.esp32-idf.yaml | 17 ++--- tests/components/mhz19/test.esp8266-ard.yaml | 17 ++--- tests/components/mhz19/test.rp2040-ard.yaml | 17 ++--- tests/components/micronova/common.yaml | 49 +++++++++++++++ .../components/micronova/test.esp32-ard.yaml | 53 ++-------------- .../micronova/test.esp32-c3-ard.yaml | 53 ++-------------- .../micronova/test.esp32-c3-idf.yaml | 53 ++-------------- .../components/micronova/test.esp32-idf.yaml | 53 ++-------------- .../micronova/test.esp8266-ard.yaml | 53 ++-------------- .../components/micronova/test.rp2040-ard.yaml | 53 ++-------------- tests/components/microphone/common.yaml | 11 ++++ .../components/microphone/test.esp32-ard.yaml | 16 +++-- .../microphone/test.esp32-c3-ard.yaml | 16 ++--- .../microphone/test.esp32-c3-idf.yaml | 16 ++--- .../components/microphone/test.esp32-idf.yaml | 16 +++-- tests/components/mics_4514/common.yaml | 20 ++++++ .../components/mics_4514/test.esp32-ard.yaml | 23 ++----- .../mics_4514/test.esp32-c3-ard.yaml | 23 ++----- .../mics_4514/test.esp32-c3-idf.yaml | 23 ++----- .../components/mics_4514/test.esp32-idf.yaml | 23 ++----- .../mics_4514/test.esp8266-ard.yaml | 23 ++----- .../components/mics_4514/test.rp2040-ard.yaml | 23 ++----- tests/components/midea/common.yaml | 59 +++++++++++++++++ tests/components/midea/test.esp32-ard.yaml | 63 ++----------------- tests/components/midea/test.esp32-c3-ard.yaml | 63 ++----------------- tests/components/midea/test.esp8266-ard.yaml | 63 ++----------------- tests/components/mlx90393/common.yaml | 20 ++++++ tests/components/mlx90393/test.esp32-ard.yaml | 23 ++----- .../mlx90393/test.esp32-c3-ard.yaml | 23 ++----- .../mlx90393/test.esp32-c3-idf.yaml | 23 ++----- tests/components/mlx90393/test.esp32-idf.yaml | 23 ++----- .../components/mlx90393/test.esp8266-ard.yaml | 23 ++----- .../components/mlx90393/test.rp2040-ard.yaml | 23 ++----- tests/components/mlx90614/common.yaml | 12 ++++ tests/components/mlx90614/test.esp32-ard.yaml | 15 ++--- .../mlx90614/test.esp32-c3-ard.yaml | 15 ++--- .../mlx90614/test.esp32-c3-idf.yaml | 15 ++--- tests/components/mlx90614/test.esp32-idf.yaml | 15 ++--- .../components/mlx90614/test.esp8266-ard.yaml | 15 ++--- .../components/mlx90614/test.rp2040-ard.yaml | 15 ++--- tests/components/mmc5603/common.yaml | 14 +++++ tests/components/mmc5603/test.esp32-ard.yaml | 17 ++--- .../components/mmc5603/test.esp32-c3-ard.yaml | 17 ++--- .../components/mmc5603/test.esp32-c3-idf.yaml | 17 ++--- tests/components/mmc5603/test.esp32-idf.yaml | 17 ++--- .../components/mmc5603/test.esp8266-ard.yaml | 17 ++--- tests/components/mmc5603/test.rp2040-ard.yaml | 17 ++--- tests/components/mmc5983/common.yaml | 16 +++++ tests/components/mmc5983/test.esp32-ard.yaml | 19 ++---- .../components/mmc5983/test.esp32-c3-ard.yaml | 19 ++---- .../components/mmc5983/test.esp32-c3-idf.yaml | 19 ++---- tests/components/mmc5983/test.esp32-idf.yaml | 19 ++---- .../components/mmc5983/test.esp8266-ard.yaml | 19 ++---- tests/components/mmc5983/test.rp2040-ard.yaml | 19 ++---- tests/components/modbus/common.yaml | 9 +++ tests/components/modbus/test.esp32-ard.yaml | 13 ++-- .../components/modbus/test.esp32-c3-ard.yaml | 13 ++-- .../components/modbus/test.esp32-c3-idf.yaml | 13 ++-- tests/components/modbus/test.esp32-idf.yaml | 13 ++-- tests/components/modbus/test.esp8266-ard.yaml | 13 ++-- tests/components/modbus/test.rp2040-ard.yaml | 13 ++-- .../components/modbus_controller/common.yaml | 35 +++++++++++ .../modbus_controller/test.esp32-ard.yaml | 41 +++--------- .../modbus_controller/test.esp32-c3-ard.yaml | 20 +++--- .../modbus_controller/test.esp32-c3-idf.yaml | 20 +++--- .../modbus_controller/test.esp32-idf.yaml | 25 +++----- .../modbus_controller/test.esp8266-ard.yaml | 20 +++--- .../modbus_controller/test.rp2040-ard.yaml | 20 +++--- tests/components/monochromatic/common.yaml | 40 ++++++++++++ .../monochromatic/test.esp32-ard.yaml | 43 ++----------- .../monochromatic/test.esp32-c3-ard.yaml | 43 ++----------- .../monochromatic/test.esp32-c3-idf.yaml | 43 ++----------- .../monochromatic/test.esp32-idf.yaml | 43 ++----------- .../monochromatic/test.esp8266-ard.yaml | 43 ++----------- .../monochromatic/test.rp2040-ard.yaml | 43 ++----------- tests/components/mpl3115a2/common.yaml | 12 ++++ .../components/mpl3115a2/test.esp32-ard.yaml | 15 ++--- .../mpl3115a2/test.esp32-c3-ard.yaml | 15 ++--- .../mpl3115a2/test.esp32-c3-idf.yaml | 15 ++--- .../components/mpl3115a2/test.esp32-idf.yaml | 15 ++--- .../mpl3115a2/test.esp8266-ard.yaml | 15 ++--- .../components/mpl3115a2/test.rp2040-ard.yaml | 15 ++--- tests/components/mpu6050/common.yaml | 22 +++++++ tests/components/mpu6050/test.esp32-ard.yaml | 25 ++------ .../components/mpu6050/test.esp32-c3-ard.yaml | 25 ++------ .../components/mpu6050/test.esp32-c3-idf.yaml | 25 ++------ tests/components/mpu6050/test.esp32-idf.yaml | 25 ++------ .../components/mpu6050/test.esp8266-ard.yaml | 25 ++------ tests/components/mpu6050/test.rp2040-ard.yaml | 25 ++------ tests/components/mpu6886/common.yaml | 22 +++++++ tests/components/mpu6886/test.esp32-ard.yaml | 25 ++------ .../components/mpu6886/test.esp32-c3-ard.yaml | 25 ++------ .../components/mpu6886/test.esp32-c3-idf.yaml | 25 ++------ tests/components/mpu6886/test.esp32-idf.yaml | 25 ++------ .../components/mpu6886/test.esp8266-ard.yaml | 25 ++------ tests/components/mpu6886/test.rp2040-ard.yaml | 25 ++------ .../components/mqtt_subscribe/common-ard.yaml | 36 +++++++++++ .../components/mqtt_subscribe/common-idf.yaml | 37 +++++++++++ .../mqtt_subscribe/test.esp32-ard.yaml | 37 +---------- .../mqtt_subscribe/test.esp32-c3-ard.yaml | 37 +---------- .../mqtt_subscribe/test.esp32-c3-idf.yaml | 38 +---------- .../mqtt_subscribe/test.esp32-idf.yaml | 38 +---------- .../mqtt_subscribe/test.esp8266-ard.yaml | 37 +---------- tests/components/ms5611/common.yaml | 13 ++++ tests/components/ms5611/test.esp32-ard.yaml | 16 ++--- .../components/ms5611/test.esp32-c3-ard.yaml | 16 ++--- .../components/ms5611/test.esp32-c3-idf.yaml | 16 ++--- tests/components/ms5611/test.esp32-idf.yaml | 16 ++--- tests/components/ms5611/test.esp8266-ard.yaml | 16 ++--- tests/components/ms5611/test.rp2040-ard.yaml | 16 ++--- 268 files changed, 1873 insertions(+), 4030 deletions(-) create mode 100644 tests/components/max31855/common.yaml create mode 100644 tests/components/max31856/common.yaml create mode 100644 tests/components/max31865/common.yaml create mode 100644 tests/components/max44009/common.yaml create mode 100644 tests/components/max6675/common.yaml create mode 100644 tests/components/max6956/common.yaml create mode 100644 tests/components/max7219/common.yaml create mode 100644 tests/components/max7219digit/common.yaml create mode 100644 tests/components/max9611/common.yaml create mode 100644 tests/components/mcp23008/common.yaml create mode 100644 tests/components/mcp23016/common.yaml create mode 100644 tests/components/mcp23017/common.yaml create mode 100644 tests/components/mcp23s08/common.yaml create mode 100644 tests/components/mcp23s17/common.yaml create mode 100644 tests/components/mcp2515/common.yaml create mode 100644 tests/components/mcp3008/common.yaml create mode 100644 tests/components/mcp3204/common.yaml create mode 100644 tests/components/mcp4725/common.yaml create mode 100644 tests/components/mcp4728/common.yaml create mode 100644 tests/components/mcp47a1/common.yaml create mode 100644 tests/components/mcp9600/common.yaml create mode 100644 tests/components/mcp9808/common.yaml create mode 100644 tests/components/mhz19/common.yaml create mode 100644 tests/components/micronova/common.yaml create mode 100644 tests/components/microphone/common.yaml create mode 100644 tests/components/mics_4514/common.yaml create mode 100644 tests/components/midea/common.yaml create mode 100644 tests/components/mlx90393/common.yaml create mode 100644 tests/components/mlx90614/common.yaml create mode 100644 tests/components/mmc5603/common.yaml create mode 100644 tests/components/mmc5983/common.yaml create mode 100644 tests/components/modbus/common.yaml create mode 100644 tests/components/modbus_controller/common.yaml create mode 100644 tests/components/monochromatic/common.yaml create mode 100644 tests/components/mpl3115a2/common.yaml create mode 100644 tests/components/mpu6050/common.yaml create mode 100644 tests/components/mpu6886/common.yaml create mode 100644 tests/components/mqtt_subscribe/common-ard.yaml create mode 100644 tests/components/mqtt_subscribe/common-idf.yaml create mode 100644 tests/components/ms5611/common.yaml diff --git a/tests/components/max31855/common.yaml b/tests/components/max31855/common.yaml new file mode 100644 index 0000000000..7136c597d5 --- /dev/null +++ b/tests/components/max31855/common.yaml @@ -0,0 +1,13 @@ +spi: + - id: spi_max31855 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: max31855 + name: MAX31855 Temperature + cs_pin: ${cs_pin} + update_interval: 15s + reference_temperature: + name: MAX31855 Internal Temperature diff --git a/tests/components/max31855/test.esp32-ard.yaml b/tests/components/max31855/test.esp32-ard.yaml index 25fee986d2..54e027a614 100644 --- a/tests/components/max31855/test.esp32-ard.yaml +++ b/tests/components/max31855/test.esp32-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31855 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max31855 - name: MAX31855 Temperature - cs_pin: 12 - update_interval: 15s - reference_temperature: - name: MAX31855 Internal Temperature +<<: !include common.yaml diff --git a/tests/components/max31855/test.esp32-c3-ard.yaml b/tests/components/max31855/test.esp32-c3-ard.yaml index e7c8f3f824..2415ba5dc6 100644 --- a/tests/components/max31855/test.esp32-c3-ard.yaml +++ b/tests/components/max31855/test.esp32-c3-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31855 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: max31855 - name: MAX31855 Temperature - cs_pin: 8 - update_interval: 15s - reference_temperature: - name: MAX31855 Internal Temperature +<<: !include common.yaml diff --git a/tests/components/max31855/test.esp32-c3-idf.yaml b/tests/components/max31855/test.esp32-c3-idf.yaml index e7c8f3f824..2415ba5dc6 100644 --- a/tests/components/max31855/test.esp32-c3-idf.yaml +++ b/tests/components/max31855/test.esp32-c3-idf.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31855 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: max31855 - name: MAX31855 Temperature - cs_pin: 8 - update_interval: 15s - reference_temperature: - name: MAX31855 Internal Temperature +<<: !include common.yaml diff --git a/tests/components/max31855/test.esp32-idf.yaml b/tests/components/max31855/test.esp32-idf.yaml index 25fee986d2..54e027a614 100644 --- a/tests/components/max31855/test.esp32-idf.yaml +++ b/tests/components/max31855/test.esp32-idf.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31855 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max31855 - name: MAX31855 Temperature - cs_pin: 12 - update_interval: 15s - reference_temperature: - name: MAX31855 Internal Temperature +<<: !include common.yaml diff --git a/tests/components/max31855/test.esp8266-ard.yaml b/tests/components/max31855/test.esp8266-ard.yaml index 7e02d41fce..dbd158d030 100644 --- a/tests/components/max31855/test.esp8266-ard.yaml +++ b/tests/components/max31855/test.esp8266-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31855 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -sensor: - - platform: max31855 - name: MAX31855 Temperature - cs_pin: 15 - update_interval: 15s - reference_temperature: - name: MAX31855 Internal Temperature +<<: !include common.yaml diff --git a/tests/components/max31855/test.rp2040-ard.yaml b/tests/components/max31855/test.rp2040-ard.yaml index 379d4d33d6..f6c3f1eeca 100644 --- a/tests/components/max31855/test.rp2040-ard.yaml +++ b/tests/components/max31855/test.rp2040-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31855 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -sensor: - - platform: max31855 - name: MAX31855 Temperature - cs_pin: 6 - update_interval: 15s - reference_temperature: - name: MAX31855 Internal Temperature +<<: !include common.yaml diff --git a/tests/components/max31856/common.yaml b/tests/components/max31856/common.yaml new file mode 100644 index 0000000000..4f7c3ad408 --- /dev/null +++ b/tests/components/max31856/common.yaml @@ -0,0 +1,13 @@ +spi: + - id: spi_max31856 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: max31856 + name: MAX31856 Temperature + cs_pin: ${cs_pin} + update_interval: 15s + mains_filter: 50Hz + thermocouple_type: N diff --git a/tests/components/max31856/test.esp32-ard.yaml b/tests/components/max31856/test.esp32-ard.yaml index 9a4da6b2a2..54e027a614 100644 --- a/tests/components/max31856/test.esp32-ard.yaml +++ b/tests/components/max31856/test.esp32-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31856 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max31856 - name: MAX31856 Temperature - cs_pin: 12 - update_interval: 15s - mains_filter: 50Hz - thermocouple_type: N +<<: !include common.yaml diff --git a/tests/components/max31856/test.esp32-c3-ard.yaml b/tests/components/max31856/test.esp32-c3-ard.yaml index 71bbfffb7b..2415ba5dc6 100644 --- a/tests/components/max31856/test.esp32-c3-ard.yaml +++ b/tests/components/max31856/test.esp32-c3-ard.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_max31856 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 - -sensor: - - platform: max31856 - name: MAX31856 Temperature - cs_pin: 8 - update_interval: 15s - mains_filter: 50Hz - thermocouple_type: N +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 +<<: !include common.yaml diff --git a/tests/components/max31856/test.esp32-c3-idf.yaml b/tests/components/max31856/test.esp32-c3-idf.yaml index 71bbfffb7b..2415ba5dc6 100644 --- a/tests/components/max31856/test.esp32-c3-idf.yaml +++ b/tests/components/max31856/test.esp32-c3-idf.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_max31856 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 - -sensor: - - platform: max31856 - name: MAX31856 Temperature - cs_pin: 8 - update_interval: 15s - mains_filter: 50Hz - thermocouple_type: N +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 +<<: !include common.yaml diff --git a/tests/components/max31856/test.esp32-idf.yaml b/tests/components/max31856/test.esp32-idf.yaml index 9a4da6b2a2..54e027a614 100644 --- a/tests/components/max31856/test.esp32-idf.yaml +++ b/tests/components/max31856/test.esp32-idf.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31856 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max31856 - name: MAX31856 Temperature - cs_pin: 12 - update_interval: 15s - mains_filter: 50Hz - thermocouple_type: N +<<: !include common.yaml diff --git a/tests/components/max31856/test.esp8266-ard.yaml b/tests/components/max31856/test.esp8266-ard.yaml index b9c42542fd..dbd158d030 100644 --- a/tests/components/max31856/test.esp8266-ard.yaml +++ b/tests/components/max31856/test.esp8266-ard.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_max31856 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 - -sensor: - - platform: max31856 - name: MAX31856 Temperature - cs_pin: 15 - update_interval: 15s - mains_filter: 50Hz - thermocouple_type: N +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 +<<: !include common.yaml diff --git a/tests/components/max31856/test.rp2040-ard.yaml b/tests/components/max31856/test.rp2040-ard.yaml index 8607eb18cf..f6c3f1eeca 100644 --- a/tests/components/max31856/test.rp2040-ard.yaml +++ b/tests/components/max31856/test.rp2040-ard.yaml @@ -1,14 +1,7 @@ -spi: - - id: spi_max31856 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 - -sensor: - - platform: max31856 - name: MAX31856 Temperature - cs_pin: 6 - update_interval: 15s - mains_filter: 50Hz - thermocouple_type: N +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 +<<: !include common.yaml diff --git a/tests/components/max31865/common.yaml b/tests/components/max31865/common.yaml new file mode 100644 index 0000000000..5bb7bda5aa --- /dev/null +++ b/tests/components/max31865/common.yaml @@ -0,0 +1,13 @@ +spi: + - id: spi_max31865 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: max31865 + name: MAX31865 Temperature + cs_pin: ${cs_pin} + update_interval: 15s + reference_resistance: 430 Ω + rtd_nominal_resistance: 100 Ω diff --git a/tests/components/max31865/test.esp32-ard.yaml b/tests/components/max31865/test.esp32-ard.yaml index 8326a578ee..54e027a614 100644 --- a/tests/components/max31865/test.esp32-ard.yaml +++ b/tests/components/max31865/test.esp32-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31865 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max31865 - name: MAX31865 Temperature - cs_pin: 12 - update_interval: 15s - reference_resistance: 430 Ω - rtd_nominal_resistance: 100 Ω +<<: !include common.yaml diff --git a/tests/components/max31865/test.esp32-c3-ard.yaml b/tests/components/max31865/test.esp32-c3-ard.yaml index 45de22331e..2415ba5dc6 100644 --- a/tests/components/max31865/test.esp32-c3-ard.yaml +++ b/tests/components/max31865/test.esp32-c3-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31865 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: max31865 - name: MAX31865 Temperature - cs_pin: 8 - update_interval: 15s - reference_resistance: 430 Ω - rtd_nominal_resistance: 100 Ω +<<: !include common.yaml diff --git a/tests/components/max31865/test.esp32-c3-idf.yaml b/tests/components/max31865/test.esp32-c3-idf.yaml index 45de22331e..2415ba5dc6 100644 --- a/tests/components/max31865/test.esp32-c3-idf.yaml +++ b/tests/components/max31865/test.esp32-c3-idf.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31865 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: max31865 - name: MAX31865 Temperature - cs_pin: 8 - update_interval: 15s - reference_resistance: 430 Ω - rtd_nominal_resistance: 100 Ω +<<: !include common.yaml diff --git a/tests/components/max31865/test.esp32-idf.yaml b/tests/components/max31865/test.esp32-idf.yaml index 8326a578ee..54e027a614 100644 --- a/tests/components/max31865/test.esp32-idf.yaml +++ b/tests/components/max31865/test.esp32-idf.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31865 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max31865 - name: MAX31865 Temperature - cs_pin: 12 - update_interval: 15s - reference_resistance: 430 Ω - rtd_nominal_resistance: 100 Ω +<<: !include common.yaml diff --git a/tests/components/max31865/test.esp8266-ard.yaml b/tests/components/max31865/test.esp8266-ard.yaml index 4828019acc..dbd158d030 100644 --- a/tests/components/max31865/test.esp8266-ard.yaml +++ b/tests/components/max31865/test.esp8266-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31865 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -sensor: - - platform: max31865 - name: MAX31865 Temperature - cs_pin: 15 - update_interval: 15s - reference_resistance: 430 Ω - rtd_nominal_resistance: 100 Ω +<<: !include common.yaml diff --git a/tests/components/max31865/test.rp2040-ard.yaml b/tests/components/max31865/test.rp2040-ard.yaml index 5af64b41ad..f6c3f1eeca 100644 --- a/tests/components/max31865/test.rp2040-ard.yaml +++ b/tests/components/max31865/test.rp2040-ard.yaml @@ -1,13 +1,7 @@ -spi: - - id: spi_max31865 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -sensor: - - platform: max31865 - name: MAX31865 Temperature - cs_pin: 6 - update_interval: 15s - reference_resistance: 430 Ω - rtd_nominal_resistance: 100 Ω +<<: !include common.yaml diff --git a/tests/components/max44009/common.yaml b/tests/components/max44009/common.yaml new file mode 100644 index 0000000000..ef51740895 --- /dev/null +++ b/tests/components/max44009/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_max44009 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: max44009 + name: MAX44009 Brightness + internal: true + mode: low_power + address: 0x4A + update_interval: 30s diff --git a/tests/components/max44009/test.esp32-ard.yaml b/tests/components/max44009/test.esp32-ard.yaml index 56eecebc4a..63c3bd6afd 100644 --- a/tests/components/max44009/test.esp32-ard.yaml +++ b/tests/components/max44009/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_max44009 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: max44009 - name: MAX44009 Brightness - internal: true - mode: low_power - address: 0x4A - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/max44009/test.esp32-c3-ard.yaml b/tests/components/max44009/test.esp32-c3-ard.yaml index 593d4bd48c..ee2c29ca4e 100644 --- a/tests/components/max44009/test.esp32-c3-ard.yaml +++ b/tests/components/max44009/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_max44009 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max44009 - name: MAX44009 Brightness - internal: true - mode: low_power - address: 0x4A - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/max44009/test.esp32-c3-idf.yaml b/tests/components/max44009/test.esp32-c3-idf.yaml index 593d4bd48c..ee2c29ca4e 100644 --- a/tests/components/max44009/test.esp32-c3-idf.yaml +++ b/tests/components/max44009/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_max44009 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max44009 - name: MAX44009 Brightness - internal: true - mode: low_power - address: 0x4A - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/max44009/test.esp32-idf.yaml b/tests/components/max44009/test.esp32-idf.yaml index 56eecebc4a..63c3bd6afd 100644 --- a/tests/components/max44009/test.esp32-idf.yaml +++ b/tests/components/max44009/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_max44009 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: max44009 - name: MAX44009 Brightness - internal: true - mode: low_power - address: 0x4A - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/max44009/test.esp8266-ard.yaml b/tests/components/max44009/test.esp8266-ard.yaml index 593d4bd48c..ee2c29ca4e 100644 --- a/tests/components/max44009/test.esp8266-ard.yaml +++ b/tests/components/max44009/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_max44009 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max44009 - name: MAX44009 Brightness - internal: true - mode: low_power - address: 0x4A - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/max44009/test.rp2040-ard.yaml b/tests/components/max44009/test.rp2040-ard.yaml index 593d4bd48c..ee2c29ca4e 100644 --- a/tests/components/max44009/test.rp2040-ard.yaml +++ b/tests/components/max44009/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_max44009 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max44009 - name: MAX44009 Brightness - internal: true - mode: low_power - address: 0x4A - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/max6675/common.yaml b/tests/components/max6675/common.yaml new file mode 100644 index 0000000000..5b4e04b317 --- /dev/null +++ b/tests/components/max6675/common.yaml @@ -0,0 +1,11 @@ +spi: + - id: spi_max6675 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sensor: + - platform: max6675 + name: Temperature + cs_pin: ${cs_pin} + update_interval: 15s diff --git a/tests/components/max6675/test.esp32-ard.yaml b/tests/components/max6675/test.esp32-ard.yaml index 9771bf9d5f..54e027a614 100644 --- a/tests/components/max6675/test.esp32-ard.yaml +++ b/tests/components/max6675/test.esp32-ard.yaml @@ -1,11 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max6675 - name: Temperature - cs_pin: 12 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/max6675/test.esp32-c3-ard.yaml b/tests/components/max6675/test.esp32-c3-ard.yaml index 2f05102ca1..2415ba5dc6 100644 --- a/tests/components/max6675/test.esp32-c3-ard.yaml +++ b/tests/components/max6675/test.esp32-c3-ard.yaml @@ -1,11 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: max6675 - name: Temperature - cs_pin: 8 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/max6675/test.esp32-c3-idf.yaml b/tests/components/max6675/test.esp32-c3-idf.yaml index 2f05102ca1..2415ba5dc6 100644 --- a/tests/components/max6675/test.esp32-c3-idf.yaml +++ b/tests/components/max6675/test.esp32-c3-idf.yaml @@ -1,11 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -sensor: - - platform: max6675 - name: Temperature - cs_pin: 8 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/max6675/test.esp32-idf.yaml b/tests/components/max6675/test.esp32-idf.yaml index 9771bf9d5f..54e027a614 100644 --- a/tests/components/max6675/test.esp32-idf.yaml +++ b/tests/components/max6675/test.esp32-idf.yaml @@ -1,11 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -sensor: - - platform: max6675 - name: Temperature - cs_pin: 12 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/max6675/test.esp8266-ard.yaml b/tests/components/max6675/test.esp8266-ard.yaml index f67e9e04a8..dbd158d030 100644 --- a/tests/components/max6675/test.esp8266-ard.yaml +++ b/tests/components/max6675/test.esp8266-ard.yaml @@ -1,11 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -sensor: - - platform: max6675 - name: Temperature - cs_pin: 15 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/max6675/test.rp2040-ard.yaml b/tests/components/max6675/test.rp2040-ard.yaml index 89c0932f94..f6c3f1eeca 100644 --- a/tests/components/max6675/test.rp2040-ard.yaml +++ b/tests/components/max6675/test.rp2040-ard.yaml @@ -1,11 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -sensor: - - platform: max6675 - name: Temperature - cs_pin: 6 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/max6956/common.yaml b/tests/components/max6956/common.yaml new file mode 100644 index 0000000000..e44e3464f8 --- /dev/null +++ b/tests/components/max6956/common.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_max6956 + scl: ${scl_pin} + sda: ${sda_pin} + +max6956: + - id: max6956_1 + address: 0x40 + +binary_sensor: + - platform: gpio + name: Max Input Pin + pin: + max6956: max6956_1 + number: 4 + mode: + input: true + pullup: true + inverted: false diff --git a/tests/components/max6956/test.esp32-ard.yaml b/tests/components/max6956/test.esp32-ard.yaml index abd1404634..63c3bd6afd 100644 --- a/tests/components/max6956/test.esp32-ard.yaml +++ b/tests/components/max6956/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_max6956 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -max6956: - - id: max6956_1 - address: 0x40 - -binary_sensor: - - platform: gpio - name: Max Input Pin 4 - pin: - max6956: max6956_1 - number: 4 - mode: - input: true - pullup: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/max6956/test.esp32-c3-ard.yaml b/tests/components/max6956/test.esp32-c3-ard.yaml index 690941784c..ee2c29ca4e 100644 --- a/tests/components/max6956/test.esp32-c3-ard.yaml +++ b/tests/components/max6956/test.esp32-c3-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_max6956 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -max6956: - - id: max6956_1 - address: 0x40 - -binary_sensor: - - platform: gpio - name: Max Input Pin 4 - pin: - max6956: max6956_1 - number: 4 - mode: - input: true - pullup: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/max6956/test.esp32-c3-idf.yaml b/tests/components/max6956/test.esp32-c3-idf.yaml index 690941784c..ee2c29ca4e 100644 --- a/tests/components/max6956/test.esp32-c3-idf.yaml +++ b/tests/components/max6956/test.esp32-c3-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_max6956 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -max6956: - - id: max6956_1 - address: 0x40 - -binary_sensor: - - platform: gpio - name: Max Input Pin 4 - pin: - max6956: max6956_1 - number: 4 - mode: - input: true - pullup: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/max6956/test.esp32-idf.yaml b/tests/components/max6956/test.esp32-idf.yaml index abd1404634..63c3bd6afd 100644 --- a/tests/components/max6956/test.esp32-idf.yaml +++ b/tests/components/max6956/test.esp32-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_max6956 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -max6956: - - id: max6956_1 - address: 0x40 - -binary_sensor: - - platform: gpio - name: Max Input Pin 4 - pin: - max6956: max6956_1 - number: 4 - mode: - input: true - pullup: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/max6956/test.esp8266-ard.yaml b/tests/components/max6956/test.esp8266-ard.yaml index 690941784c..ee2c29ca4e 100644 --- a/tests/components/max6956/test.esp8266-ard.yaml +++ b/tests/components/max6956/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_max6956 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -max6956: - - id: max6956_1 - address: 0x40 - -binary_sensor: - - platform: gpio - name: Max Input Pin 4 - pin: - max6956: max6956_1 - number: 4 - mode: - input: true - pullup: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/max6956/test.rp2040-ard.yaml b/tests/components/max6956/test.rp2040-ard.yaml index 690941784c..ee2c29ca4e 100644 --- a/tests/components/max6956/test.rp2040-ard.yaml +++ b/tests/components/max6956/test.rp2040-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_max6956 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -max6956: - - id: max6956_1 - address: 0x40 - -binary_sensor: - - platform: gpio - name: Max Input Pin 4 - pin: - max6956: max6956_1 - number: 4 - mode: - input: true - pullup: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/max7219/common.yaml b/tests/components/max7219/common.yaml new file mode 100644 index 0000000000..0060db191e --- /dev/null +++ b/tests/components/max7219/common.yaml @@ -0,0 +1,12 @@ +spi: + - id: spi_max6675 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +display: + - platform: max7219 + cs_pin: ${cs_pin} + num_chips: 1 + lambda: |- + it.print("01234567"); diff --git a/tests/components/max7219/test.esp32-ard.yaml b/tests/components/max7219/test.esp32-ard.yaml index 2985345a48..54e027a614 100644 --- a/tests/components/max7219/test.esp32-ard.yaml +++ b/tests/components/max7219/test.esp32-ard.yaml @@ -1,12 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -display: - - platform: max7219 - cs_pin: 12 - num_chips: 1 - lambda: |- - it.print("01234567"); +<<: !include common.yaml diff --git a/tests/components/max7219/test.esp32-c3-ard.yaml b/tests/components/max7219/test.esp32-c3-ard.yaml index fa1ac15f33..2415ba5dc6 100644 --- a/tests/components/max7219/test.esp32-c3-ard.yaml +++ b/tests/components/max7219/test.esp32-c3-ard.yaml @@ -1,12 +1,7 @@ -spi: - - id: spi_max7219 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -display: - - platform: max7219 - cs_pin: 8 - num_chips: 1 - lambda: |- - it.print("01234567"); +<<: !include common.yaml diff --git a/tests/components/max7219/test.esp32-c3-idf.yaml b/tests/components/max7219/test.esp32-c3-idf.yaml index fa1ac15f33..2415ba5dc6 100644 --- a/tests/components/max7219/test.esp32-c3-idf.yaml +++ b/tests/components/max7219/test.esp32-c3-idf.yaml @@ -1,12 +1,7 @@ -spi: - - id: spi_max7219 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -display: - - platform: max7219 - cs_pin: 8 - num_chips: 1 - lambda: |- - it.print("01234567"); +<<: !include common.yaml diff --git a/tests/components/max7219/test.esp32-idf.yaml b/tests/components/max7219/test.esp32-idf.yaml index 2985345a48..54e027a614 100644 --- a/tests/components/max7219/test.esp32-idf.yaml +++ b/tests/components/max7219/test.esp32-idf.yaml @@ -1,12 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -display: - - platform: max7219 - cs_pin: 12 - num_chips: 1 - lambda: |- - it.print("01234567"); +<<: !include common.yaml diff --git a/tests/components/max7219/test.esp8266-ard.yaml b/tests/components/max7219/test.esp8266-ard.yaml index a8c280daff..dbd158d030 100644 --- a/tests/components/max7219/test.esp8266-ard.yaml +++ b/tests/components/max7219/test.esp8266-ard.yaml @@ -1,12 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -display: - - platform: max7219 - cs_pin: 15 - num_chips: 1 - lambda: |- - it.print("01234567"); +<<: !include common.yaml diff --git a/tests/components/max7219/test.rp2040-ard.yaml b/tests/components/max7219/test.rp2040-ard.yaml index 37b2220649..f6c3f1eeca 100644 --- a/tests/components/max7219/test.rp2040-ard.yaml +++ b/tests/components/max7219/test.rp2040-ard.yaml @@ -1,12 +1,7 @@ -spi: - - id: spi_max6675 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -display: - - platform: max7219 - cs_pin: 6 - num_chips: 1 - lambda: |- - it.print("01234567"); +<<: !include common.yaml diff --git a/tests/components/max7219digit/common.yaml b/tests/components/max7219digit/common.yaml new file mode 100644 index 0000000000..a5a3bd57fb --- /dev/null +++ b/tests/components/max7219digit/common.yaml @@ -0,0 +1,16 @@ +spi: + - id: spi_max7219digit + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +display: + - platform: max7219digit + cs_pin: ${cs_pin} + num_chips: 4 + rotate_chip: 0 + intensity: 10 + scroll_mode: STOP + id: my_matrix + lambda: |- + it.printdigit("hello"); diff --git a/tests/components/max7219digit/test.esp32-ard.yaml b/tests/components/max7219digit/test.esp32-ard.yaml index 7f3aed964a..54e027a614 100644 --- a/tests/components/max7219digit/test.esp32-ard.yaml +++ b/tests/components/max7219digit/test.esp32-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_max7219digit - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -display: - - platform: max7219digit - cs_pin: 12 - num_chips: 4 - rotate_chip: 0 - intensity: 10 - scroll_mode: STOP - id: my_matrix - lambda: |- - it.printdigit("hello"); +<<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp32-c3-ard.yaml b/tests/components/max7219digit/test.esp32-c3-ard.yaml index 0c04784380..2415ba5dc6 100644 --- a/tests/components/max7219digit/test.esp32-c3-ard.yaml +++ b/tests/components/max7219digit/test.esp32-c3-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_max7219digit - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -display: - - platform: max7219digit - cs_pin: 8 - num_chips: 4 - rotate_chip: 0 - intensity: 10 - scroll_mode: STOP - id: my_matrix - lambda: |- - it.printdigit("hello"); +<<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp32-c3-idf.yaml b/tests/components/max7219digit/test.esp32-c3-idf.yaml index 0c04784380..2415ba5dc6 100644 --- a/tests/components/max7219digit/test.esp32-c3-idf.yaml +++ b/tests/components/max7219digit/test.esp32-c3-idf.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_max7219digit - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -display: - - platform: max7219digit - cs_pin: 8 - num_chips: 4 - rotate_chip: 0 - intensity: 10 - scroll_mode: STOP - id: my_matrix - lambda: |- - it.printdigit("hello"); +<<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp32-idf.yaml b/tests/components/max7219digit/test.esp32-idf.yaml index 7f3aed964a..54e027a614 100644 --- a/tests/components/max7219digit/test.esp32-idf.yaml +++ b/tests/components/max7219digit/test.esp32-idf.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_max7219digit - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -display: - - platform: max7219digit - cs_pin: 12 - num_chips: 4 - rotate_chip: 0 - intensity: 10 - scroll_mode: STOP - id: my_matrix - lambda: |- - it.printdigit("hello"); +<<: !include common.yaml diff --git a/tests/components/max7219digit/test.esp8266-ard.yaml b/tests/components/max7219digit/test.esp8266-ard.yaml index 52587e8b0e..dbd158d030 100644 --- a/tests/components/max7219digit/test.esp8266-ard.yaml +++ b/tests/components/max7219digit/test.esp8266-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_max7219digit - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -display: - - platform: max7219digit - cs_pin: 15 - num_chips: 4 - rotate_chip: 0 - intensity: 10 - scroll_mode: STOP - id: my_matrix - lambda: |- - it.printdigit("hello"); +<<: !include common.yaml diff --git a/tests/components/max7219digit/test.rp2040-ard.yaml b/tests/components/max7219digit/test.rp2040-ard.yaml index f986483ec2..f6c3f1eeca 100644 --- a/tests/components/max7219digit/test.rp2040-ard.yaml +++ b/tests/components/max7219digit/test.rp2040-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_max7219digit - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -display: - - platform: max7219digit - cs_pin: 6 - num_chips: 4 - rotate_chip: 0 - intensity: 10 - scroll_mode: STOP - id: my_matrix - lambda: |- - it.printdigit("hello"); +<<: !include common.yaml diff --git a/tests/components/max9611/common.yaml b/tests/components/max9611/common.yaml new file mode 100644 index 0000000000..c3c00fdf85 --- /dev/null +++ b/tests/components/max9611/common.yaml @@ -0,0 +1,18 @@ +i2c: + - id: i2c_max9611 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: max9611 + shunt_resistance: 0.2 ohm + gain: 1X + voltage: + name: Max9611 Voltage + current: + name: Max9611 Current + power: + name: Max9611 Watts + temperature: + name: Max9611 Temperature + update_interval: 1s diff --git a/tests/components/max9611/test.esp32-ard.yaml b/tests/components/max9611/test.esp32-ard.yaml index 5c480cc815..63c3bd6afd 100644 --- a/tests/components/max9611/test.esp32-ard.yaml +++ b/tests/components/max9611/test.esp32-ard.yaml @@ -1,18 +1,5 @@ -i2c: - - id: i2c_max9611 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: max9611 - shunt_resistance: 0.2 ohm - gain: 1X - voltage: - name: Max9611 Voltage - current: - name: Max9611 Current - power: - name: Max9611 Watts - temperature: - name: Max9611 Temp - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/max9611/test.esp32-c3-ard.yaml b/tests/components/max9611/test.esp32-c3-ard.yaml index 00f8330280..ee2c29ca4e 100644 --- a/tests/components/max9611/test.esp32-c3-ard.yaml +++ b/tests/components/max9611/test.esp32-c3-ard.yaml @@ -1,18 +1,5 @@ -i2c: - - id: i2c_max9611 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max9611 - shunt_resistance: 0.2 ohm - gain: 1X - voltage: - name: Max9611 Voltage - current: - name: Max9611 Current - power: - name: Max9611 Watts - temperature: - name: Max9611 Temp - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/max9611/test.esp32-c3-idf.yaml b/tests/components/max9611/test.esp32-c3-idf.yaml index 00f8330280..ee2c29ca4e 100644 --- a/tests/components/max9611/test.esp32-c3-idf.yaml +++ b/tests/components/max9611/test.esp32-c3-idf.yaml @@ -1,18 +1,5 @@ -i2c: - - id: i2c_max9611 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max9611 - shunt_resistance: 0.2 ohm - gain: 1X - voltage: - name: Max9611 Voltage - current: - name: Max9611 Current - power: - name: Max9611 Watts - temperature: - name: Max9611 Temp - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/max9611/test.esp32-idf.yaml b/tests/components/max9611/test.esp32-idf.yaml index 5c480cc815..63c3bd6afd 100644 --- a/tests/components/max9611/test.esp32-idf.yaml +++ b/tests/components/max9611/test.esp32-idf.yaml @@ -1,18 +1,5 @@ -i2c: - - id: i2c_max9611 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: max9611 - shunt_resistance: 0.2 ohm - gain: 1X - voltage: - name: Max9611 Voltage - current: - name: Max9611 Current - power: - name: Max9611 Watts - temperature: - name: Max9611 Temp - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/max9611/test.esp8266-ard.yaml b/tests/components/max9611/test.esp8266-ard.yaml index 00f8330280..ee2c29ca4e 100644 --- a/tests/components/max9611/test.esp8266-ard.yaml +++ b/tests/components/max9611/test.esp8266-ard.yaml @@ -1,18 +1,5 @@ -i2c: - - id: i2c_max9611 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max9611 - shunt_resistance: 0.2 ohm - gain: 1X - voltage: - name: Max9611 Voltage - current: - name: Max9611 Current - power: - name: Max9611 Watts - temperature: - name: Max9611 Temp - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/max9611/test.rp2040-ard.yaml b/tests/components/max9611/test.rp2040-ard.yaml index 00f8330280..ee2c29ca4e 100644 --- a/tests/components/max9611/test.rp2040-ard.yaml +++ b/tests/components/max9611/test.rp2040-ard.yaml @@ -1,18 +1,5 @@ -i2c: - - id: i2c_max9611 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: max9611 - shunt_resistance: 0.2 ohm - gain: 1X - voltage: - name: Max9611 Voltage - current: - name: Max9611 Current - power: - name: Max9611 Watts - temperature: - name: Max9611 Temp - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/mcp23008/common.yaml b/tests/components/mcp23008/common.yaml new file mode 100644 index 0000000000..1954766d25 --- /dev/null +++ b/tests/components/mcp23008/common.yaml @@ -0,0 +1,23 @@ +i2c: + - id: i2c_mcp23008 + scl: ${scl_pin} + sda: ${sda_pin} + +mcp23008: + id: mcp23008_hub + +binary_sensor: + - platform: gpio + id: mcp23008_binary_sensor + pin: + mcp23xxx: mcp23008_hub + number: 0 + mode: INPUT + +switch: + - platform: gpio + id: mcp23008_switch + pin: + mcp23xxx: mcp23008_hub + number: 1 + mode: OUTPUT diff --git a/tests/components/mcp23008/test.esp32-ard.yaml b/tests/components/mcp23008/test.esp32-ard.yaml index cbf03f371c..63c3bd6afd 100644 --- a/tests/components/mcp23008/test.esp32-ard.yaml +++ b/tests/components/mcp23008/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23008 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp23008: - id: mcp23008_hub - -binary_sensor: - - platform: gpio - id: mcp23008_binary_sensor - pin: - mcp23xxx: mcp23008_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23008_switch - pin: - mcp23xxx: mcp23008_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp32-c3-ard.yaml b/tests/components/mcp23008/test.esp32-c3-ard.yaml index eabd5a7311..ee2c29ca4e 100644 --- a/tests/components/mcp23008/test.esp32-c3-ard.yaml +++ b/tests/components/mcp23008/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23008 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23008: - id: mcp23008_hub - -binary_sensor: - - platform: gpio - id: mcp23008_binary_sensor - pin: - mcp23xxx: mcp23008_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23008_switch - pin: - mcp23xxx: mcp23008_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp32-c3-idf.yaml b/tests/components/mcp23008/test.esp32-c3-idf.yaml index eabd5a7311..ee2c29ca4e 100644 --- a/tests/components/mcp23008/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23008/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23008 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23008: - id: mcp23008_hub - -binary_sensor: - - platform: gpio - id: mcp23008_binary_sensor - pin: - mcp23xxx: mcp23008_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23008_switch - pin: - mcp23xxx: mcp23008_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp32-idf.yaml b/tests/components/mcp23008/test.esp32-idf.yaml index cbf03f371c..63c3bd6afd 100644 --- a/tests/components/mcp23008/test.esp32-idf.yaml +++ b/tests/components/mcp23008/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23008 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp23008: - id: mcp23008_hub - -binary_sensor: - - platform: gpio - id: mcp23008_binary_sensor - pin: - mcp23xxx: mcp23008_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23008_switch - pin: - mcp23xxx: mcp23008_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23008/test.esp8266-ard.yaml b/tests/components/mcp23008/test.esp8266-ard.yaml index eabd5a7311..ee2c29ca4e 100644 --- a/tests/components/mcp23008/test.esp8266-ard.yaml +++ b/tests/components/mcp23008/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23008 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23008: - id: mcp23008_hub - -binary_sensor: - - platform: gpio - id: mcp23008_binary_sensor - pin: - mcp23xxx: mcp23008_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23008_switch - pin: - mcp23xxx: mcp23008_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23008/test.rp2040-ard.yaml b/tests/components/mcp23008/test.rp2040-ard.yaml index eabd5a7311..ee2c29ca4e 100644 --- a/tests/components/mcp23008/test.rp2040-ard.yaml +++ b/tests/components/mcp23008/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23008 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23008: - id: mcp23008_hub - -binary_sensor: - - platform: gpio - id: mcp23008_binary_sensor - pin: - mcp23xxx: mcp23008_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23008_switch - pin: - mcp23xxx: mcp23008_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23016/common.yaml b/tests/components/mcp23016/common.yaml new file mode 100644 index 0000000000..109cb34b21 --- /dev/null +++ b/tests/components/mcp23016/common.yaml @@ -0,0 +1,23 @@ +i2c: + - id: i2c_mcp23016 + scl: ${scl_pin} + sda: ${sda_pin} + +mcp23016: + id: mcp23016_hub + +binary_sensor: + - platform: gpio + id: mcp23016_binary_sensor + pin: + mcp23016: mcp23016_hub + number: 0 + mode: INPUT + +switch: + - platform: gpio + id: mcp23016_switch + pin: + mcp23016: mcp23016_hub + number: 1 + mode: OUTPUT diff --git a/tests/components/mcp23016/test.esp32-ard.yaml b/tests/components/mcp23016/test.esp32-ard.yaml index 48574a9b01..63c3bd6afd 100644 --- a/tests/components/mcp23016/test.esp32-ard.yaml +++ b/tests/components/mcp23016/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23016 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp23016: - id: mcp23016_hub - -binary_sensor: - - platform: gpio - id: mcp23016_binary_sensor - pin: - mcp23016: mcp23016_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23016_switch - pin: - mcp23016: mcp23016_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp32-c3-ard.yaml b/tests/components/mcp23016/test.esp32-c3-ard.yaml index 2211931e3d..ee2c29ca4e 100644 --- a/tests/components/mcp23016/test.esp32-c3-ard.yaml +++ b/tests/components/mcp23016/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23016 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23016: - id: mcp23016_hub - -binary_sensor: - - platform: gpio - id: mcp23016_binary_sensor - pin: - mcp23016: mcp23016_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23016_switch - pin: - mcp23016: mcp23016_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp32-c3-idf.yaml b/tests/components/mcp23016/test.esp32-c3-idf.yaml index 2211931e3d..ee2c29ca4e 100644 --- a/tests/components/mcp23016/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23016/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23016 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23016: - id: mcp23016_hub - -binary_sensor: - - platform: gpio - id: mcp23016_binary_sensor - pin: - mcp23016: mcp23016_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23016_switch - pin: - mcp23016: mcp23016_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp32-idf.yaml b/tests/components/mcp23016/test.esp32-idf.yaml index 48574a9b01..63c3bd6afd 100644 --- a/tests/components/mcp23016/test.esp32-idf.yaml +++ b/tests/components/mcp23016/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23016 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp23016: - id: mcp23016_hub - -binary_sensor: - - platform: gpio - id: mcp23016_binary_sensor - pin: - mcp23016: mcp23016_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23016_switch - pin: - mcp23016: mcp23016_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23016/test.esp8266-ard.yaml b/tests/components/mcp23016/test.esp8266-ard.yaml index 2211931e3d..ee2c29ca4e 100644 --- a/tests/components/mcp23016/test.esp8266-ard.yaml +++ b/tests/components/mcp23016/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23016 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23016: - id: mcp23016_hub - -binary_sensor: - - platform: gpio - id: mcp23016_binary_sensor - pin: - mcp23016: mcp23016_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23016_switch - pin: - mcp23016: mcp23016_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23016/test.rp2040-ard.yaml b/tests/components/mcp23016/test.rp2040-ard.yaml index 2211931e3d..ee2c29ca4e 100644 --- a/tests/components/mcp23016/test.rp2040-ard.yaml +++ b/tests/components/mcp23016/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23016 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23016: - id: mcp23016_hub - -binary_sensor: - - platform: gpio - id: mcp23016_binary_sensor - pin: - mcp23016: mcp23016_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23016_switch - pin: - mcp23016: mcp23016_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23017/common.yaml b/tests/components/mcp23017/common.yaml new file mode 100644 index 0000000000..74949bba76 --- /dev/null +++ b/tests/components/mcp23017/common.yaml @@ -0,0 +1,23 @@ +i2c: + - id: i2c_mcp23017 + scl: ${scl_pin} + sda: ${sda_pin} + +mcp23017: + id: mcp23017_hub + +binary_sensor: + - platform: gpio + id: mcp23017_binary_sensor + pin: + mcp23xxx: mcp23017_hub + number: 0 + mode: INPUT + +switch: + - platform: gpio + id: mcp23017_switch + pin: + mcp23xxx: mcp23017_hub + number: 1 + mode: OUTPUT diff --git a/tests/components/mcp23017/test.esp32-ard.yaml b/tests/components/mcp23017/test.esp32-ard.yaml index 9b7c45eb8a..63c3bd6afd 100644 --- a/tests/components/mcp23017/test.esp32-ard.yaml +++ b/tests/components/mcp23017/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23017 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp23017: - id: mcp23017_hub - -binary_sensor: - - platform: gpio - id: mcp23017_binary_sensor - pin: - mcp23xxx: mcp23017_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23017_switch - pin: - mcp23xxx: mcp23017_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp32-c3-ard.yaml b/tests/components/mcp23017/test.esp32-c3-ard.yaml index 863b2b8f0b..ee2c29ca4e 100644 --- a/tests/components/mcp23017/test.esp32-c3-ard.yaml +++ b/tests/components/mcp23017/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23017 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23017: - id: mcp23017_hub - -binary_sensor: - - platform: gpio - id: mcp23017_binary_sensor - pin: - mcp23xxx: mcp23017_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23017_switch - pin: - mcp23xxx: mcp23017_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp32-c3-idf.yaml b/tests/components/mcp23017/test.esp32-c3-idf.yaml index 863b2b8f0b..ee2c29ca4e 100644 --- a/tests/components/mcp23017/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23017/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23017 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23017: - id: mcp23017_hub - -binary_sensor: - - platform: gpio - id: mcp23017_binary_sensor - pin: - mcp23xxx: mcp23017_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23017_switch - pin: - mcp23xxx: mcp23017_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp32-idf.yaml b/tests/components/mcp23017/test.esp32-idf.yaml index 9b7c45eb8a..63c3bd6afd 100644 --- a/tests/components/mcp23017/test.esp32-idf.yaml +++ b/tests/components/mcp23017/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23017 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp23017: - id: mcp23017_hub - -binary_sensor: - - platform: gpio - id: mcp23017_binary_sensor - pin: - mcp23xxx: mcp23017_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23017_switch - pin: - mcp23xxx: mcp23017_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23017/test.esp8266-ard.yaml b/tests/components/mcp23017/test.esp8266-ard.yaml index 863b2b8f0b..ee2c29ca4e 100644 --- a/tests/components/mcp23017/test.esp8266-ard.yaml +++ b/tests/components/mcp23017/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23017 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23017: - id: mcp23017_hub - -binary_sensor: - - platform: gpio - id: mcp23017_binary_sensor - pin: - mcp23xxx: mcp23017_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23017_switch - pin: - mcp23xxx: mcp23017_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23017/test.rp2040-ard.yaml b/tests/components/mcp23017/test.rp2040-ard.yaml index 863b2b8f0b..ee2c29ca4e 100644 --- a/tests/components/mcp23017/test.rp2040-ard.yaml +++ b/tests/components/mcp23017/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -i2c: - - id: i2c_mcp23017 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp23017: - id: mcp23017_hub - -binary_sensor: - - platform: gpio - id: mcp23017_binary_sensor - pin: - mcp23xxx: mcp23017_hub - number: 0 - mode: INPUT - -switch: - - platform: gpio - id: mcp23017_switch - pin: - mcp23xxx: mcp23017_hub - number: 1 - mode: OUTPUT +<<: !include common.yaml diff --git a/tests/components/mcp23s08/common.yaml b/tests/components/mcp23s08/common.yaml new file mode 100644 index 0000000000..b89088fe15 --- /dev/null +++ b/tests/components/mcp23s08/common.yaml @@ -0,0 +1,10 @@ +spi: + - id: spi_mcp23s08 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +mcp23s08: + - id: mcp23s08_hub + cs_pin: ${cs_pin} + deviceaddress: 0 diff --git a/tests/components/mcp23s08/test.esp32-ard.yaml b/tests/components/mcp23s08/test.esp32-ard.yaml index 0b26035c3e..54e027a614 100644 --- a/tests/components/mcp23s08/test.esp32-ard.yaml +++ b/tests/components/mcp23s08/test.esp32-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s08 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp23s08: - - id: mcp23s08_hub - cs_pin: 12 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp32-c3-ard.yaml b/tests/components/mcp23s08/test.esp32-c3-ard.yaml index f1af8a71a9..2415ba5dc6 100644 --- a/tests/components/mcp23s08/test.esp32-c3-ard.yaml +++ b/tests/components/mcp23s08/test.esp32-c3-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s08 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp23s08: - - id: mcp23s08_hub - cs_pin: 8 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp32-c3-idf.yaml b/tests/components/mcp23s08/test.esp32-c3-idf.yaml index f1af8a71a9..2415ba5dc6 100644 --- a/tests/components/mcp23s08/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23s08/test.esp32-c3-idf.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s08 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp23s08: - - id: mcp23s08_hub - cs_pin: 8 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp32-idf.yaml b/tests/components/mcp23s08/test.esp32-idf.yaml index 0b26035c3e..54e027a614 100644 --- a/tests/components/mcp23s08/test.esp32-idf.yaml +++ b/tests/components/mcp23s08/test.esp32-idf.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s08 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp23s08: - - id: mcp23s08_hub - cs_pin: 12 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.esp8266-ard.yaml b/tests/components/mcp23s08/test.esp8266-ard.yaml index eff856aca9..dbd158d030 100644 --- a/tests/components/mcp23s08/test.esp8266-ard.yaml +++ b/tests/components/mcp23s08/test.esp8266-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s08 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -mcp23s08: - - id: mcp23s08_hub - cs_pin: 15 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s08/test.rp2040-ard.yaml b/tests/components/mcp23s08/test.rp2040-ard.yaml index 1b23d2d3b5..f6c3f1eeca 100644 --- a/tests/components/mcp23s08/test.rp2040-ard.yaml +++ b/tests/components/mcp23s08/test.rp2040-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s08 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -mcp23s08: - - id: mcp23s08_hub - cs_pin: 6 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s17/common.yaml b/tests/components/mcp23s17/common.yaml new file mode 100644 index 0000000000..3fb27ef625 --- /dev/null +++ b/tests/components/mcp23s17/common.yaml @@ -0,0 +1,10 @@ +spi: + - id: spi_mcp23s17 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +mcp23s17: + - id: mcp23s17_hub + cs_pin: ${cs_pin} + deviceaddress: 0 diff --git a/tests/components/mcp23s17/test.esp32-ard.yaml b/tests/components/mcp23s17/test.esp32-ard.yaml index 9a42c12e85..54e027a614 100644 --- a/tests/components/mcp23s17/test.esp32-ard.yaml +++ b/tests/components/mcp23s17/test.esp32-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s17 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp23s17: - - id: mcp23s17_hub - cs_pin: 12 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp32-c3-ard.yaml b/tests/components/mcp23s17/test.esp32-c3-ard.yaml index d83f66d3b1..2415ba5dc6 100644 --- a/tests/components/mcp23s17/test.esp32-c3-ard.yaml +++ b/tests/components/mcp23s17/test.esp32-c3-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s17 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp23s17: - - id: mcp23s17_hub - cs_pin: 8 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp32-c3-idf.yaml b/tests/components/mcp23s17/test.esp32-c3-idf.yaml index d83f66d3b1..2415ba5dc6 100644 --- a/tests/components/mcp23s17/test.esp32-c3-idf.yaml +++ b/tests/components/mcp23s17/test.esp32-c3-idf.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s17 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp23s17: - - id: mcp23s17_hub - cs_pin: 8 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp32-idf.yaml b/tests/components/mcp23s17/test.esp32-idf.yaml index 9a42c12e85..54e027a614 100644 --- a/tests/components/mcp23s17/test.esp32-idf.yaml +++ b/tests/components/mcp23s17/test.esp32-idf.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s17 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp23s17: - - id: mcp23s17_hub - cs_pin: 12 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.esp8266-ard.yaml b/tests/components/mcp23s17/test.esp8266-ard.yaml index 36dac63f6f..dbd158d030 100644 --- a/tests/components/mcp23s17/test.esp8266-ard.yaml +++ b/tests/components/mcp23s17/test.esp8266-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s17 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -mcp23s17: - - id: mcp23s17_hub - cs_pin: 15 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp23s17/test.rp2040-ard.yaml b/tests/components/mcp23s17/test.rp2040-ard.yaml index 2730f6a9d6..f6c3f1eeca 100644 --- a/tests/components/mcp23s17/test.rp2040-ard.yaml +++ b/tests/components/mcp23s17/test.rp2040-ard.yaml @@ -1,10 +1,7 @@ -spi: - - id: spi_mcp23s17 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -mcp23s17: - - id: mcp23s17_hub - cs_pin: 6 - deviceaddress: 0 +<<: !include common.yaml diff --git a/tests/components/mcp2515/common.yaml b/tests/components/mcp2515/common.yaml new file mode 100644 index 0000000000..96a72a3ec3 --- /dev/null +++ b/tests/components/mcp2515/common.yaml @@ -0,0 +1,44 @@ +spi: + - id: spi_mcp2515 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +canbus: + - platform: mcp2515 + id: mcp2515_can + cs_pin: ${cs_pin} + can_id: 4 + bit_rate: 50kbps + on_frame: + - can_id: 500 + then: + - lambda: |- + std::string b(x.begin(), x.end()); + ESP_LOGD("can_id 500", "%s", b.c_str()); + - can_id: 23 + then: + - if: + condition: + lambda: "return x[0] == 0x11;" + then: + logger.log: "x[0] == 0x11" + - can_id: 0b00000000000000000000001000000 + can_id_mask: 0b11111000000000011111111000000 + use_extended_id: true + then: + - lambda: |- + auto pdo_id = can_id >> 14; + switch (pdo_id) + { + case 117: + ESP_LOGD("canbus", "exhaust_fan_duty"); + break; + case 118: + ESP_LOGD("canbus", "supply_fan_duty"); + break; + case 119: + ESP_LOGD("canbus", "supply_fan_flow"); + break; + // to be continued... + } diff --git a/tests/components/mcp2515/test.esp32-ard.yaml b/tests/components/mcp2515/test.esp32-ard.yaml index 07fae36cc3..54e027a614 100644 --- a/tests/components/mcp2515/test.esp32-ard.yaml +++ b/tests/components/mcp2515/test.esp32-ard.yaml @@ -1,44 +1,7 @@ -spi: - - id: spi_mcp2515 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -canbus: - - platform: mcp2515 - id: mcp2515_can - cs_pin: 12 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("can_id 500", "%s", b.c_str()); - - can_id: 23 - then: - - if: - condition: - lambda: "return x[0] == 0x11;" - then: - logger.log: "x[0] == 0x11" - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp32-c3-ard.yaml b/tests/components/mcp2515/test.esp32-c3-ard.yaml index 3ceeea268f..2415ba5dc6 100644 --- a/tests/components/mcp2515/test.esp32-c3-ard.yaml +++ b/tests/components/mcp2515/test.esp32-c3-ard.yaml @@ -1,44 +1,7 @@ -spi: - - id: spi_mcp2515 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -canbus: - - platform: mcp2515 - id: mcp2515_can - cs_pin: 8 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("can_id 500", "%s", b.c_str()); - - can_id: 23 - then: - - if: - condition: - lambda: "return x[0] == 0x11;" - then: - logger.log: "x[0] == 0x11" - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp32-c3-idf.yaml b/tests/components/mcp2515/test.esp32-c3-idf.yaml index 3ceeea268f..2415ba5dc6 100644 --- a/tests/components/mcp2515/test.esp32-c3-idf.yaml +++ b/tests/components/mcp2515/test.esp32-c3-idf.yaml @@ -1,44 +1,7 @@ -spi: - - id: spi_mcp2515 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -canbus: - - platform: mcp2515 - id: mcp2515_can - cs_pin: 8 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("can_id 500", "%s", b.c_str()); - - can_id: 23 - then: - - if: - condition: - lambda: "return x[0] == 0x11;" - then: - logger.log: "x[0] == 0x11" - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp32-idf.yaml b/tests/components/mcp2515/test.esp32-idf.yaml index 07fae36cc3..54e027a614 100644 --- a/tests/components/mcp2515/test.esp32-idf.yaml +++ b/tests/components/mcp2515/test.esp32-idf.yaml @@ -1,44 +1,7 @@ -spi: - - id: spi_mcp2515 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -canbus: - - platform: mcp2515 - id: mcp2515_can - cs_pin: 12 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("can_id 500", "%s", b.c_str()); - - can_id: 23 - then: - - if: - condition: - lambda: "return x[0] == 0x11;" - then: - logger.log: "x[0] == 0x11" - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/mcp2515/test.esp8266-ard.yaml b/tests/components/mcp2515/test.esp8266-ard.yaml index 1096a0e809..dbd158d030 100644 --- a/tests/components/mcp2515/test.esp8266-ard.yaml +++ b/tests/components/mcp2515/test.esp8266-ard.yaml @@ -1,44 +1,7 @@ -spi: - - id: spi_mcp2515 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -canbus: - - platform: mcp2515 - id: mcp2515_can - cs_pin: 15 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("can_id 500", "%s", b.c_str()); - - can_id: 23 - then: - - if: - condition: - lambda: "return x[0] == 0x11;" - then: - logger.log: "x[0] == 0x11" - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/mcp2515/test.rp2040-ard.yaml b/tests/components/mcp2515/test.rp2040-ard.yaml index 678c817d3d..f6c3f1eeca 100644 --- a/tests/components/mcp2515/test.rp2040-ard.yaml +++ b/tests/components/mcp2515/test.rp2040-ard.yaml @@ -1,44 +1,7 @@ -spi: - - id: spi_mcp2515 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -canbus: - - platform: mcp2515 - id: mcp2515_can - cs_pin: 6 - can_id: 4 - bit_rate: 50kbps - on_frame: - - can_id: 500 - then: - - lambda: |- - std::string b(x.begin(), x.end()); - ESP_LOGD("can_id 500", "%s", b.c_str()); - - can_id: 23 - then: - - if: - condition: - lambda: "return x[0] == 0x11;" - then: - logger.log: "x[0] == 0x11" - - can_id: 0b00000000000000000000001000000 - can_id_mask: 0b11111000000000011111111000000 - use_extended_id: true - then: - - lambda: |- - auto pdo_id = can_id >> 14; - switch (pdo_id) - { - case 117: - ESP_LOGD("canbus", "exhaust_fan_duty"); - break; - case 118: - ESP_LOGD("canbus", "supply_fan_duty"); - break; - case 119: - ESP_LOGD("canbus", "supply_fan_flow"); - break; - // to be continued... - } +<<: !include common.yaml diff --git a/tests/components/mcp3008/common.yaml b/tests/components/mcp3008/common.yaml new file mode 100644 index 0000000000..646d3a20e9 --- /dev/null +++ b/tests/components/mcp3008/common.yaml @@ -0,0 +1,17 @@ +spi: + - id: spi_mcp3008 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +mcp3008: + - id: mcp3008_hub + cs_pin: ${cs_pin} + +sensor: + - platform: mcp3008 + id: mcp3008_sensor + mcp3008_id: mcp3008_hub + number: 0 + reference_voltage: 3.19 + update_interval: 5s diff --git a/tests/components/mcp3008/test.esp32-ard.yaml b/tests/components/mcp3008/test.esp32-ard.yaml index a66fbeb7a1..54e027a614 100644 --- a/tests/components/mcp3008/test.esp32-ard.yaml +++ b/tests/components/mcp3008/test.esp32-ard.yaml @@ -1,17 +1,7 @@ -spi: - - id: spi_mcp3008 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp3008: - - id: mcp3008_hub - cs_pin: 12 - -sensor: - - platform: mcp3008 - id: mcp3008_sensor - mcp3008_id: mcp3008_hub - number: 0 - reference_voltage: 3.19 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp32-c3-ard.yaml b/tests/components/mcp3008/test.esp32-c3-ard.yaml index 9e66372e4f..2415ba5dc6 100644 --- a/tests/components/mcp3008/test.esp32-c3-ard.yaml +++ b/tests/components/mcp3008/test.esp32-c3-ard.yaml @@ -1,17 +1,7 @@ -spi: - - id: spi_mcp3008 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp3008: - - id: mcp3008_hub - cs_pin: 8 - -sensor: - - platform: mcp3008 - id: mcp3008_sensor - mcp3008_id: mcp3008_hub - number: 0 - reference_voltage: 3.19 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp32-c3-idf.yaml b/tests/components/mcp3008/test.esp32-c3-idf.yaml index 9e66372e4f..2415ba5dc6 100644 --- a/tests/components/mcp3008/test.esp32-c3-idf.yaml +++ b/tests/components/mcp3008/test.esp32-c3-idf.yaml @@ -1,17 +1,7 @@ -spi: - - id: spi_mcp3008 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp3008: - - id: mcp3008_hub - cs_pin: 8 - -sensor: - - platform: mcp3008 - id: mcp3008_sensor - mcp3008_id: mcp3008_hub - number: 0 - reference_voltage: 3.19 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp32-idf.yaml b/tests/components/mcp3008/test.esp32-idf.yaml index a66fbeb7a1..54e027a614 100644 --- a/tests/components/mcp3008/test.esp32-idf.yaml +++ b/tests/components/mcp3008/test.esp32-idf.yaml @@ -1,17 +1,7 @@ -spi: - - id: spi_mcp3008 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp3008: - - id: mcp3008_hub - cs_pin: 12 - -sensor: - - platform: mcp3008 - id: mcp3008_sensor - mcp3008_id: mcp3008_hub - number: 0 - reference_voltage: 3.19 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3008/test.esp8266-ard.yaml b/tests/components/mcp3008/test.esp8266-ard.yaml index eaccca0765..dbd158d030 100644 --- a/tests/components/mcp3008/test.esp8266-ard.yaml +++ b/tests/components/mcp3008/test.esp8266-ard.yaml @@ -1,17 +1,7 @@ -spi: - - id: spi_mcp3008 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -mcp3008: - - id: mcp3008_hub - cs_pin: 15 - -sensor: - - platform: mcp3008 - id: mcp3008_sensor - mcp3008_id: mcp3008_hub - number: 0 - reference_voltage: 3.19 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3008/test.rp2040-ard.yaml b/tests/components/mcp3008/test.rp2040-ard.yaml index 8ab9630553..f6c3f1eeca 100644 --- a/tests/components/mcp3008/test.rp2040-ard.yaml +++ b/tests/components/mcp3008/test.rp2040-ard.yaml @@ -1,17 +1,7 @@ -spi: - - id: spi_mcp3008 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -mcp3008: - - id: mcp3008_hub - cs_pin: 6 - -sensor: - - platform: mcp3008 - id: mcp3008_sensor - mcp3008_id: mcp3008_hub - number: 0 - reference_voltage: 3.19 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3204/common.yaml b/tests/components/mcp3204/common.yaml new file mode 100644 index 0000000000..f102500c81 --- /dev/null +++ b/tests/components/mcp3204/common.yaml @@ -0,0 +1,16 @@ +spi: + - id: spi_mcp3204 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +mcp3204: + - id: mcp3204_hub + cs_pin: ${cs_pin} + +sensor: + - platform: mcp3204 + id: mcp3204_sensor + mcp3204_id: mcp3204_hub + number: 0 + update_interval: 5s diff --git a/tests/components/mcp3204/test.esp32-ard.yaml b/tests/components/mcp3204/test.esp32-ard.yaml index c340797c8e..54e027a614 100644 --- a/tests/components/mcp3204/test.esp32-ard.yaml +++ b/tests/components/mcp3204/test.esp32-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_mcp3204 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp3204: - - id: mcp3204_hub - cs_pin: 12 - -sensor: - - platform: mcp3204 - id: mcp3204_sensor - mcp3204_id: mcp3204_hub - number: 0 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp32-c3-ard.yaml b/tests/components/mcp3204/test.esp32-c3-ard.yaml index 5bf5ba81e1..2415ba5dc6 100644 --- a/tests/components/mcp3204/test.esp32-c3-ard.yaml +++ b/tests/components/mcp3204/test.esp32-c3-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_mcp3204 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp3204: - - id: mcp3204_hub - cs_pin: 8 - -sensor: - - platform: mcp3204 - id: mcp3204_sensor - mcp3204_id: mcp3204_hub - number: 0 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp32-c3-idf.yaml b/tests/components/mcp3204/test.esp32-c3-idf.yaml index 5bf5ba81e1..2415ba5dc6 100644 --- a/tests/components/mcp3204/test.esp32-c3-idf.yaml +++ b/tests/components/mcp3204/test.esp32-c3-idf.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_mcp3204 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -mcp3204: - - id: mcp3204_hub - cs_pin: 8 - -sensor: - - platform: mcp3204 - id: mcp3204_sensor - mcp3204_id: mcp3204_hub - number: 0 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp32-idf.yaml b/tests/components/mcp3204/test.esp32-idf.yaml index c340797c8e..54e027a614 100644 --- a/tests/components/mcp3204/test.esp32-idf.yaml +++ b/tests/components/mcp3204/test.esp32-idf.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_mcp3204 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -mcp3204: - - id: mcp3204_hub - cs_pin: 12 - -sensor: - - platform: mcp3204 - id: mcp3204_sensor - mcp3204_id: mcp3204_hub - number: 0 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3204/test.esp8266-ard.yaml b/tests/components/mcp3204/test.esp8266-ard.yaml index d208e3e06c..dbd158d030 100644 --- a/tests/components/mcp3204/test.esp8266-ard.yaml +++ b/tests/components/mcp3204/test.esp8266-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_mcp3204 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -mcp3204: - - id: mcp3204_hub - cs_pin: 15 - -sensor: - - platform: mcp3204 - id: mcp3204_sensor - mcp3204_id: mcp3204_hub - number: 0 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp3204/test.rp2040-ard.yaml b/tests/components/mcp3204/test.rp2040-ard.yaml index 63f30e3621..f6c3f1eeca 100644 --- a/tests/components/mcp3204/test.rp2040-ard.yaml +++ b/tests/components/mcp3204/test.rp2040-ard.yaml @@ -1,16 +1,7 @@ -spi: - - id: spi_mcp3204 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -mcp3204: - - id: mcp3204_hub - cs_pin: 6 - -sensor: - - platform: mcp3204 - id: mcp3204_sensor - mcp3204_id: mcp3204_hub - number: 0 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/mcp4725/common.yaml b/tests/components/mcp4725/common.yaml new file mode 100644 index 0000000000..0ccc649f2e --- /dev/null +++ b/tests/components/mcp4725/common.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_mcp4725 + scl: ${scl_pin} + sda: ${sda_pin} + +output: + - platform: mcp4725 + id: mcp4725_dac_output diff --git a/tests/components/mcp4725/test.esp32-ard.yaml b/tests/components/mcp4725/test.esp32-ard.yaml index a523ad95e1..63c3bd6afd 100644 --- a/tests/components/mcp4725/test.esp32-ard.yaml +++ b/tests/components/mcp4725/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp4725 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -output: - - platform: mcp4725 - id: mcp4725_dac_output +<<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp32-c3-ard.yaml b/tests/components/mcp4725/test.esp32-c3-ard.yaml index 5fc799203d..ee2c29ca4e 100644 --- a/tests/components/mcp4725/test.esp32-c3-ard.yaml +++ b/tests/components/mcp4725/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp4725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp4725 - id: mcp4725_dac_output +<<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp32-c3-idf.yaml b/tests/components/mcp4725/test.esp32-c3-idf.yaml index 5fc799203d..ee2c29ca4e 100644 --- a/tests/components/mcp4725/test.esp32-c3-idf.yaml +++ b/tests/components/mcp4725/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp4725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp4725 - id: mcp4725_dac_output +<<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp32-idf.yaml b/tests/components/mcp4725/test.esp32-idf.yaml index a523ad95e1..63c3bd6afd 100644 --- a/tests/components/mcp4725/test.esp32-idf.yaml +++ b/tests/components/mcp4725/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp4725 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -output: - - platform: mcp4725 - id: mcp4725_dac_output +<<: !include common.yaml diff --git a/tests/components/mcp4725/test.esp8266-ard.yaml b/tests/components/mcp4725/test.esp8266-ard.yaml index 5fc799203d..ee2c29ca4e 100644 --- a/tests/components/mcp4725/test.esp8266-ard.yaml +++ b/tests/components/mcp4725/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp4725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp4725 - id: mcp4725_dac_output +<<: !include common.yaml diff --git a/tests/components/mcp4725/test.rp2040-ard.yaml b/tests/components/mcp4725/test.rp2040-ard.yaml index 5fc799203d..ee2c29ca4e 100644 --- a/tests/components/mcp4725/test.rp2040-ard.yaml +++ b/tests/components/mcp4725/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp4725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp4725 - id: mcp4725_dac_output +<<: !include common.yaml diff --git a/tests/components/mcp4728/common.yaml b/tests/components/mcp4728/common.yaml new file mode 100644 index 0000000000..b42818e4e6 --- /dev/null +++ b/tests/components/mcp4728/common.yaml @@ -0,0 +1,31 @@ +i2c: + - id: i2c_mcp4728 + scl: ${scl_pin} + sda: ${sda_pin} + +mcp4728: + - id: mcp4728_dac + +output: + - platform: mcp4728 + id: mcp4728_dac_output_a + channel: A + vref: vdd + power_down: normal + - platform: mcp4728 + id: mcp4728_dac_output_b + channel: B + vref: internal + gain: X1 + power_down: gnd_1k + - platform: mcp4728 + id: mcp4728_dac_output_c + channel: C + vref: vdd + power_down: gnd_100k + - platform: mcp4728 + id: mcp4728_dac_output_d + channel: D + vref: internal + gain: X2 + power_down: gnd_500k diff --git a/tests/components/mcp4728/test.esp32-ard.yaml b/tests/components/mcp4728/test.esp32-ard.yaml index b29a6ee53c..63c3bd6afd 100644 --- a/tests/components/mcp4728/test.esp32-ard.yaml +++ b/tests/components/mcp4728/test.esp32-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_mcp4728 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp4728: - - id: mcp4728_dac - -output: - - platform: mcp4728 - id: mcp4728_dac_output_a - channel: A - vref: vdd - power_down: normal - - platform: mcp4728 - id: mcp4728_dac_output_b - channel: B - vref: internal - gain: X1 - power_down: gnd_1k - - platform: mcp4728 - id: mcp4728_dac_output_c - channel: C - vref: vdd - power_down: gnd_100k - - platform: mcp4728 - id: mcp4728_dac_output_d - channel: D - vref: internal - gain: X2 - power_down: gnd_500k +<<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp32-c3-ard.yaml b/tests/components/mcp4728/test.esp32-c3-ard.yaml index 2f24dd0b8c..ee2c29ca4e 100644 --- a/tests/components/mcp4728/test.esp32-c3-ard.yaml +++ b/tests/components/mcp4728/test.esp32-c3-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_mcp4728 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp4728: - - id: mcp4728_dac - -output: - - platform: mcp4728 - id: mcp4728_dac_output_a - channel: A - vref: vdd - power_down: normal - - platform: mcp4728 - id: mcp4728_dac_output_b - channel: B - vref: internal - gain: X1 - power_down: gnd_1k - - platform: mcp4728 - id: mcp4728_dac_output_c - channel: C - vref: vdd - power_down: gnd_100k - - platform: mcp4728 - id: mcp4728_dac_output_d - channel: D - vref: internal - gain: X2 - power_down: gnd_500k +<<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp32-c3-idf.yaml b/tests/components/mcp4728/test.esp32-c3-idf.yaml index 2f24dd0b8c..ee2c29ca4e 100644 --- a/tests/components/mcp4728/test.esp32-c3-idf.yaml +++ b/tests/components/mcp4728/test.esp32-c3-idf.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_mcp4728 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp4728: - - id: mcp4728_dac - -output: - - platform: mcp4728 - id: mcp4728_dac_output_a - channel: A - vref: vdd - power_down: normal - - platform: mcp4728 - id: mcp4728_dac_output_b - channel: B - vref: internal - gain: X1 - power_down: gnd_1k - - platform: mcp4728 - id: mcp4728_dac_output_c - channel: C - vref: vdd - power_down: gnd_100k - - platform: mcp4728 - id: mcp4728_dac_output_d - channel: D - vref: internal - gain: X2 - power_down: gnd_500k +<<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp32-idf.yaml b/tests/components/mcp4728/test.esp32-idf.yaml index b29a6ee53c..63c3bd6afd 100644 --- a/tests/components/mcp4728/test.esp32-idf.yaml +++ b/tests/components/mcp4728/test.esp32-idf.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_mcp4728 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -mcp4728: - - id: mcp4728_dac - -output: - - platform: mcp4728 - id: mcp4728_dac_output_a - channel: A - vref: vdd - power_down: normal - - platform: mcp4728 - id: mcp4728_dac_output_b - channel: B - vref: internal - gain: X1 - power_down: gnd_1k - - platform: mcp4728 - id: mcp4728_dac_output_c - channel: C - vref: vdd - power_down: gnd_100k - - platform: mcp4728 - id: mcp4728_dac_output_d - channel: D - vref: internal - gain: X2 - power_down: gnd_500k +<<: !include common.yaml diff --git a/tests/components/mcp4728/test.esp8266-ard.yaml b/tests/components/mcp4728/test.esp8266-ard.yaml index 2f24dd0b8c..ee2c29ca4e 100644 --- a/tests/components/mcp4728/test.esp8266-ard.yaml +++ b/tests/components/mcp4728/test.esp8266-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_mcp4728 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp4728: - - id: mcp4728_dac - -output: - - platform: mcp4728 - id: mcp4728_dac_output_a - channel: A - vref: vdd - power_down: normal - - platform: mcp4728 - id: mcp4728_dac_output_b - channel: B - vref: internal - gain: X1 - power_down: gnd_1k - - platform: mcp4728 - id: mcp4728_dac_output_c - channel: C - vref: vdd - power_down: gnd_100k - - platform: mcp4728 - id: mcp4728_dac_output_d - channel: D - vref: internal - gain: X2 - power_down: gnd_500k +<<: !include common.yaml diff --git a/tests/components/mcp4728/test.rp2040-ard.yaml b/tests/components/mcp4728/test.rp2040-ard.yaml index 2f24dd0b8c..ee2c29ca4e 100644 --- a/tests/components/mcp4728/test.rp2040-ard.yaml +++ b/tests/components/mcp4728/test.rp2040-ard.yaml @@ -1,31 +1,5 @@ -i2c: - - id: i2c_mcp4728 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -mcp4728: - - id: mcp4728_dac - -output: - - platform: mcp4728 - id: mcp4728_dac_output_a - channel: A - vref: vdd - power_down: normal - - platform: mcp4728 - id: mcp4728_dac_output_b - channel: B - vref: internal - gain: X1 - power_down: gnd_1k - - platform: mcp4728 - id: mcp4728_dac_output_c - channel: C - vref: vdd - power_down: gnd_100k - - platform: mcp4728 - id: mcp4728_dac_output_d - channel: D - vref: internal - gain: X2 - power_down: gnd_500k +<<: !include common.yaml diff --git a/tests/components/mcp47a1/common.yaml b/tests/components/mcp47a1/common.yaml new file mode 100644 index 0000000000..59e28d37b3 --- /dev/null +++ b/tests/components/mcp47a1/common.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_mcp47a1 + scl: ${scl_pin} + sda: ${sda_pin} + +output: + - platform: mcp47a1 + id: output_mcp47a1 diff --git a/tests/components/mcp47a1/test.esp32-ard.yaml b/tests/components/mcp47a1/test.esp32-ard.yaml index 9e2133de66..63c3bd6afd 100644 --- a/tests/components/mcp47a1/test.esp32-ard.yaml +++ b/tests/components/mcp47a1/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp47a1 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -output: - - platform: mcp47a1 - id: output_mcp47a1 +<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp32-c3-ard.yaml b/tests/components/mcp47a1/test.esp32-c3-ard.yaml index 68273e00eb..ee2c29ca4e 100644 --- a/tests/components/mcp47a1/test.esp32-c3-ard.yaml +++ b/tests/components/mcp47a1/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp47a1 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp47a1 - id: output_mcp47a1 +<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp32-c3-idf.yaml b/tests/components/mcp47a1/test.esp32-c3-idf.yaml index 68273e00eb..ee2c29ca4e 100644 --- a/tests/components/mcp47a1/test.esp32-c3-idf.yaml +++ b/tests/components/mcp47a1/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp47a1 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp47a1 - id: output_mcp47a1 +<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp32-idf.yaml b/tests/components/mcp47a1/test.esp32-idf.yaml index 9e2133de66..63c3bd6afd 100644 --- a/tests/components/mcp47a1/test.esp32-idf.yaml +++ b/tests/components/mcp47a1/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp47a1 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -output: - - platform: mcp47a1 - id: output_mcp47a1 +<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.esp8266-ard.yaml b/tests/components/mcp47a1/test.esp8266-ard.yaml index 68273e00eb..ee2c29ca4e 100644 --- a/tests/components/mcp47a1/test.esp8266-ard.yaml +++ b/tests/components/mcp47a1/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp47a1 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp47a1 - id: output_mcp47a1 +<<: !include common.yaml diff --git a/tests/components/mcp47a1/test.rp2040-ard.yaml b/tests/components/mcp47a1/test.rp2040-ard.yaml index 68273e00eb..ee2c29ca4e 100644 --- a/tests/components/mcp47a1/test.rp2040-ard.yaml +++ b/tests/components/mcp47a1/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp47a1 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -output: - - platform: mcp47a1 - id: output_mcp47a1 +<<: !include common.yaml diff --git a/tests/components/mcp9600/common.yaml b/tests/components/mcp9600/common.yaml new file mode 100644 index 0000000000..e3c9df79e4 --- /dev/null +++ b/tests/components/mcp9600/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_mcp9600 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mcp9600 + thermocouple_type: K + hot_junction: + name: Thermocouple Temperature + cold_junction: + name: Ambient Temperature diff --git a/tests/components/mcp9600/test.esp32-ard.yaml b/tests/components/mcp9600/test.esp32-ard.yaml index 0c94f099ae..63c3bd6afd 100644 --- a/tests/components/mcp9600/test.esp32-ard.yaml +++ b/tests/components/mcp9600/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mcp9600 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mcp9600 - thermocouple_type: K - hot_junction: - name: Thermocouple Temperature - cold_junction: - name: Ambient Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp32-c3-ard.yaml b/tests/components/mcp9600/test.esp32-c3-ard.yaml index b07f4589ce..ee2c29ca4e 100644 --- a/tests/components/mcp9600/test.esp32-c3-ard.yaml +++ b/tests/components/mcp9600/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mcp9600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9600 - thermocouple_type: K - hot_junction: - name: Thermocouple Temperature - cold_junction: - name: Ambient Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp32-c3-idf.yaml b/tests/components/mcp9600/test.esp32-c3-idf.yaml index b07f4589ce..ee2c29ca4e 100644 --- a/tests/components/mcp9600/test.esp32-c3-idf.yaml +++ b/tests/components/mcp9600/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mcp9600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9600 - thermocouple_type: K - hot_junction: - name: Thermocouple Temperature - cold_junction: - name: Ambient Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp32-idf.yaml b/tests/components/mcp9600/test.esp32-idf.yaml index 0c94f099ae..63c3bd6afd 100644 --- a/tests/components/mcp9600/test.esp32-idf.yaml +++ b/tests/components/mcp9600/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mcp9600 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mcp9600 - thermocouple_type: K - hot_junction: - name: Thermocouple Temperature - cold_junction: - name: Ambient Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9600/test.esp8266-ard.yaml b/tests/components/mcp9600/test.esp8266-ard.yaml index b07f4589ce..ee2c29ca4e 100644 --- a/tests/components/mcp9600/test.esp8266-ard.yaml +++ b/tests/components/mcp9600/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mcp9600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9600 - thermocouple_type: K - hot_junction: - name: Thermocouple Temperature - cold_junction: - name: Ambient Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9600/test.rp2040-ard.yaml b/tests/components/mcp9600/test.rp2040-ard.yaml index b07f4589ce..ee2c29ca4e 100644 --- a/tests/components/mcp9600/test.rp2040-ard.yaml +++ b/tests/components/mcp9600/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mcp9600 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9600 - thermocouple_type: K - hot_junction: - name: Thermocouple Temperature - cold_junction: - name: Ambient Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9808/common.yaml b/tests/components/mcp9808/common.yaml new file mode 100644 index 0000000000..ccfd5d13ce --- /dev/null +++ b/tests/components/mcp9808/common.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_mcp9808 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mcp9808 + name: MCP9808 Temperature diff --git a/tests/components/mcp9808/test.esp32-ard.yaml b/tests/components/mcp9808/test.esp32-ard.yaml index 1e5affdac0..63c3bd6afd 100644 --- a/tests/components/mcp9808/test.esp32-ard.yaml +++ b/tests/components/mcp9808/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp9808 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mcp9808 - name: MCP9808 Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp32-c3-ard.yaml b/tests/components/mcp9808/test.esp32-c3-ard.yaml index 86b4d7f181..ee2c29ca4e 100644 --- a/tests/components/mcp9808/test.esp32-c3-ard.yaml +++ b/tests/components/mcp9808/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp9808 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9808 - name: MCP9808 Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp32-c3-idf.yaml b/tests/components/mcp9808/test.esp32-c3-idf.yaml index 86b4d7f181..ee2c29ca4e 100644 --- a/tests/components/mcp9808/test.esp32-c3-idf.yaml +++ b/tests/components/mcp9808/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp9808 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9808 - name: MCP9808 Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp32-idf.yaml b/tests/components/mcp9808/test.esp32-idf.yaml index 1e5affdac0..63c3bd6afd 100644 --- a/tests/components/mcp9808/test.esp32-idf.yaml +++ b/tests/components/mcp9808/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp9808 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mcp9808 - name: MCP9808 Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9808/test.esp8266-ard.yaml b/tests/components/mcp9808/test.esp8266-ard.yaml index 86b4d7f181..ee2c29ca4e 100644 --- a/tests/components/mcp9808/test.esp8266-ard.yaml +++ b/tests/components/mcp9808/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp9808 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9808 - name: MCP9808 Temperature +<<: !include common.yaml diff --git a/tests/components/mcp9808/test.rp2040-ard.yaml b/tests/components/mcp9808/test.rp2040-ard.yaml index 86b4d7f181..ee2c29ca4e 100644 --- a/tests/components/mcp9808/test.rp2040-ard.yaml +++ b/tests/components/mcp9808/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_mcp9808 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mcp9808 - name: MCP9808 Temperature +<<: !include common.yaml diff --git a/tests/components/mhz19/common.yaml b/tests/components/mhz19/common.yaml new file mode 100644 index 0000000000..8b7e732068 --- /dev/null +++ b/tests/components/mhz19/common.yaml @@ -0,0 +1,14 @@ +uart: + - id: uart_mhz19 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: mhz19 + co2: + name: MH-Z19 CO2 Value + temperature: + name: MH-Z19 Temperature + automatic_baseline_calibration: false + update_interval: 15s diff --git a/tests/components/mhz19/test.esp32-ard.yaml b/tests/components/mhz19/test.esp32-ard.yaml index 0e30713b54..f486544afa 100644 --- a/tests/components/mhz19/test.esp32-ard.yaml +++ b/tests/components/mhz19/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_mhz19 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: mhz19 - co2: - name: MH-Z19 CO2 Value - temperature: - name: MH-Z19 Temperature - automatic_baseline_calibration: false - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/mhz19/test.esp32-c3-ard.yaml b/tests/components/mhz19/test.esp32-c3-ard.yaml index 1edfa49c23..b516342f3b 100644 --- a/tests/components/mhz19/test.esp32-c3-ard.yaml +++ b/tests/components/mhz19/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_mhz19 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: mhz19 - co2: - name: MH-Z19 CO2 Value - temperature: - name: MH-Z19 Temperature - automatic_baseline_calibration: false - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/mhz19/test.esp32-c3-idf.yaml b/tests/components/mhz19/test.esp32-c3-idf.yaml index 1edfa49c23..b516342f3b 100644 --- a/tests/components/mhz19/test.esp32-c3-idf.yaml +++ b/tests/components/mhz19/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_mhz19 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: mhz19 - co2: - name: MH-Z19 CO2 Value - temperature: - name: MH-Z19 Temperature - automatic_baseline_calibration: false - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/mhz19/test.esp32-idf.yaml b/tests/components/mhz19/test.esp32-idf.yaml index 0e30713b54..f486544afa 100644 --- a/tests/components/mhz19/test.esp32-idf.yaml +++ b/tests/components/mhz19/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_mhz19 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: mhz19 - co2: - name: MH-Z19 CO2 Value - temperature: - name: MH-Z19 Temperature - automatic_baseline_calibration: false - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/mhz19/test.esp8266-ard.yaml b/tests/components/mhz19/test.esp8266-ard.yaml index 1edfa49c23..b516342f3b 100644 --- a/tests/components/mhz19/test.esp8266-ard.yaml +++ b/tests/components/mhz19/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_mhz19 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: mhz19 - co2: - name: MH-Z19 CO2 Value - temperature: - name: MH-Z19 Temperature - automatic_baseline_calibration: false - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/mhz19/test.rp2040-ard.yaml b/tests/components/mhz19/test.rp2040-ard.yaml index 1edfa49c23..b516342f3b 100644 --- a/tests/components/mhz19/test.rp2040-ard.yaml +++ b/tests/components/mhz19/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_mhz19 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: mhz19 - co2: - name: MH-Z19 CO2 Value - temperature: - name: MH-Z19 Temperature - automatic_baseline_calibration: false - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/micronova/common.yaml b/tests/components/micronova/common.yaml new file mode 100644 index 0000000000..661c9330c6 --- /dev/null +++ b/tests/components/micronova/common.yaml @@ -0,0 +1,49 @@ +uart: + - id: uart_micronova + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +micronova: + enable_rx_pin: ${enable_rx_pin} + +button: + - platform: micronova + custom_button: + name: Custom Micronova Button + memory_location: 0xA0 + memory_address: 0x7D + memory_data: 0x0F + +number: + - platform: micronova + thermostat_temperature: + name: Micronova Thermostaat + step: 1 + power_level: + name: Micronova Power level + +sensor: + - platform: micronova + room_temperature: + name: Room Temperature + fumes_temperature: + name: Fumes Temperature + water_temperature: + name: Water temperature + water_pressure: + name: Water pressure + stove_power: + name: Stove Power + fan_speed: + fan_rpm_offset: 240 + name: Fan RPM + memory_address_sensor: + memory_location: 0x20 + memory_address: 0x7d + name: Adres sensor + +switch: + - platform: micronova + stove: + name: Stove on/off diff --git a/tests/components/micronova/test.esp32-ard.yaml b/tests/components/micronova/test.esp32-ard.yaml index 9156f7d6a9..35d041e047 100644 --- a/tests/components/micronova/test.esp32-ard.yaml +++ b/tests/components/micronova/test.esp32-ard.yaml @@ -1,49 +1,6 @@ -uart: - - id: uart_micronova - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + enable_rx_pin: GPIO13 -micronova: - enable_rx_pin: 18 - -button: - - platform: micronova - custom_button: - name: Custom Micronova Button - memory_location: 0xA0 - memory_address: 0x7D - memory_data: 0x0F - -number: - - platform: micronova - thermostat_temperature: - name: Micronova Thermostaat - step: 1 - power_level: - name: Micronova Power level - -sensor: - - platform: micronova - room_temperature: - name: Room Temperature - fumes_temperature: - name: Fumes Temperature - water_temperature: - name: Water temperature - water_pressure: - name: Water pressure - stove_power: - name: Stove Power - fan_speed: - fan_rpm_offset: 240 - name: Fan RPM - memory_address_sensor: - memory_location: 0x20 - memory_address: 0x7d - name: Adres sensor - -switch: - - platform: micronova - stove: - name: Stove on/off +<<: !include common.yaml diff --git a/tests/components/micronova/test.esp32-c3-ard.yaml b/tests/components/micronova/test.esp32-c3-ard.yaml index ec9699909e..993071999f 100644 --- a/tests/components/micronova/test.esp32-c3-ard.yaml +++ b/tests/components/micronova/test.esp32-c3-ard.yaml @@ -1,49 +1,6 @@ -uart: - - id: uart_micronova - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + enable_rx_pin: GPIO3 -micronova: - enable_rx_pin: 6 - -button: - - platform: micronova - custom_button: - name: Custom Micronova Button - memory_location: 0xA0 - memory_address: 0x7D - memory_data: 0x0F - -number: - - platform: micronova - thermostat_temperature: - name: Micronova Thermostaat - step: 1 - power_level: - name: Micronova Power level - -sensor: - - platform: micronova - room_temperature: - name: Room Temperature - fumes_temperature: - name: Fumes Temperature - water_temperature: - name: Water temperature - water_pressure: - name: Water pressure - stove_power: - name: Stove Power - fan_speed: - fan_rpm_offset: 240 - name: Fan RPM - memory_address_sensor: - memory_location: 0x20 - memory_address: 0x7d - name: Adres sensor - -switch: - - platform: micronova - stove: - name: Stove on/off +<<: !include common.yaml diff --git a/tests/components/micronova/test.esp32-c3-idf.yaml b/tests/components/micronova/test.esp32-c3-idf.yaml index ec9699909e..993071999f 100644 --- a/tests/components/micronova/test.esp32-c3-idf.yaml +++ b/tests/components/micronova/test.esp32-c3-idf.yaml @@ -1,49 +1,6 @@ -uart: - - id: uart_micronova - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + enable_rx_pin: GPIO3 -micronova: - enable_rx_pin: 6 - -button: - - platform: micronova - custom_button: - name: Custom Micronova Button - memory_location: 0xA0 - memory_address: 0x7D - memory_data: 0x0F - -number: - - platform: micronova - thermostat_temperature: - name: Micronova Thermostaat - step: 1 - power_level: - name: Micronova Power level - -sensor: - - platform: micronova - room_temperature: - name: Room Temperature - fumes_temperature: - name: Fumes Temperature - water_temperature: - name: Water temperature - water_pressure: - name: Water pressure - stove_power: - name: Stove Power - fan_speed: - fan_rpm_offset: 240 - name: Fan RPM - memory_address_sensor: - memory_location: 0x20 - memory_address: 0x7d - name: Adres sensor - -switch: - - platform: micronova - stove: - name: Stove on/off +<<: !include common.yaml diff --git a/tests/components/micronova/test.esp32-idf.yaml b/tests/components/micronova/test.esp32-idf.yaml index 9156f7d6a9..35d041e047 100644 --- a/tests/components/micronova/test.esp32-idf.yaml +++ b/tests/components/micronova/test.esp32-idf.yaml @@ -1,49 +1,6 @@ -uart: - - id: uart_micronova - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + enable_rx_pin: GPIO13 -micronova: - enable_rx_pin: 18 - -button: - - platform: micronova - custom_button: - name: Custom Micronova Button - memory_location: 0xA0 - memory_address: 0x7D - memory_data: 0x0F - -number: - - platform: micronova - thermostat_temperature: - name: Micronova Thermostaat - step: 1 - power_level: - name: Micronova Power level - -sensor: - - platform: micronova - room_temperature: - name: Room Temperature - fumes_temperature: - name: Fumes Temperature - water_temperature: - name: Water temperature - water_pressure: - name: Water pressure - stove_power: - name: Stove Power - fan_speed: - fan_rpm_offset: 240 - name: Fan RPM - memory_address_sensor: - memory_location: 0x20 - memory_address: 0x7d - name: Adres sensor - -switch: - - platform: micronova - stove: - name: Stove on/off +<<: !include common.yaml diff --git a/tests/components/micronova/test.esp8266-ard.yaml b/tests/components/micronova/test.esp8266-ard.yaml index d10ab7ad7a..048fb82d72 100644 --- a/tests/components/micronova/test.esp8266-ard.yaml +++ b/tests/components/micronova/test.esp8266-ard.yaml @@ -1,49 +1,6 @@ -uart: - - id: uart_micronova - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + enable_rx_pin: GPIO13 -micronova: - enable_rx_pin: 16 - -button: - - platform: micronova - custom_button: - name: Custom Micronova Button - memory_location: 0xA0 - memory_address: 0x7D - memory_data: 0x0F - -number: - - platform: micronova - thermostat_temperature: - name: Micronova Thermostaat - step: 1 - power_level: - name: Micronova Power level - -sensor: - - platform: micronova - room_temperature: - name: Room Temperature - fumes_temperature: - name: Fumes Temperature - water_temperature: - name: Water temperature - water_pressure: - name: Water pressure - stove_power: - name: Stove Power - fan_speed: - fan_rpm_offset: 240 - name: Fan RPM - memory_address_sensor: - memory_location: 0x20 - memory_address: 0x7d - name: Adres sensor - -switch: - - platform: micronova - stove: - name: Stove on/off +<<: !include common.yaml diff --git a/tests/components/micronova/test.rp2040-ard.yaml b/tests/components/micronova/test.rp2040-ard.yaml index ec9699909e..993071999f 100644 --- a/tests/components/micronova/test.rp2040-ard.yaml +++ b/tests/components/micronova/test.rp2040-ard.yaml @@ -1,49 +1,6 @@ -uart: - - id: uart_micronova - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + enable_rx_pin: GPIO3 -micronova: - enable_rx_pin: 6 - -button: - - platform: micronova - custom_button: - name: Custom Micronova Button - memory_location: 0xA0 - memory_address: 0x7D - memory_data: 0x0F - -number: - - platform: micronova - thermostat_temperature: - name: Micronova Thermostaat - step: 1 - power_level: - name: Micronova Power level - -sensor: - - platform: micronova - room_temperature: - name: Room Temperature - fumes_temperature: - name: Fumes Temperature - water_temperature: - name: Water temperature - water_pressure: - name: Water pressure - stove_power: - name: Stove Power - fan_speed: - fan_rpm_offset: 240 - name: Fan RPM - memory_address_sensor: - memory_location: 0x20 - memory_address: 0x7d - name: Adres sensor - -switch: - - platform: micronova - stove: - name: Stove on/off +<<: !include common.yaml diff --git a/tests/components/microphone/common.yaml b/tests/components/microphone/common.yaml new file mode 100644 index 0000000000..ea79266281 --- /dev/null +++ b/tests/components/microphone/common.yaml @@ -0,0 +1,11 @@ +i2s_audio: + i2s_bclk_pin: ${i2s_bclk_pin} + i2s_lrclk_pin: ${i2s_lrclk_pin} + i2s_mclk_pin: ${i2s_mclk_pin} + +microphone: + - platform: i2s_audio + id: mic_id_external + i2s_din_pin: ${i2s_din_pin} + adc_type: external + pdm: false diff --git a/tests/components/microphone/test.esp32-ard.yaml b/tests/components/microphone/test.esp32-ard.yaml index 166eedb54d..392df582cc 100644 --- a/tests/components/microphone/test.esp32-ard.yaml +++ b/tests/components/microphone/test.esp32-ard.yaml @@ -1,15 +1,13 @@ -i2s_audio: - i2s_lrclk_pin: 13 - i2s_bclk_pin: 14 - i2s_mclk_pin: 15 +substitutions: + i2s_bclk_pin: GPIO15 + i2s_lrclk_pin: GPIO16 + i2s_mclk_pin: GPIO17 + i2s_din_pin: GPIO33 + +<<: !include common.yaml microphone: - platform: i2s_audio id: mic_id_adc adc_pin: 32 adc_type: internal - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 33 - adc_type: external - pdm: false diff --git a/tests/components/microphone/test.esp32-c3-ard.yaml b/tests/components/microphone/test.esp32-c3-ard.yaml index 706a38f910..c28dc553f5 100644 --- a/tests/components/microphone/test.esp32-c3-ard.yaml +++ b/tests/components/microphone/test.esp32-c3-ard.yaml @@ -1,11 +1,7 @@ -i2s_audio: - i2s_lrclk_pin: 6 - i2s_bclk_pin: 7 - i2s_mclk_pin: 8 +substitutions: + i2s_bclk_pin: GPIO6 + i2s_lrclk_pin: GPIO7 + i2s_mclk_pin: GPIO8 + i2s_din_pin: GPIO3 -microphone: - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 3 - adc_type: external - pdm: false +<<: !include common.yaml diff --git a/tests/components/microphone/test.esp32-c3-idf.yaml b/tests/components/microphone/test.esp32-c3-idf.yaml index 706a38f910..c28dc553f5 100644 --- a/tests/components/microphone/test.esp32-c3-idf.yaml +++ b/tests/components/microphone/test.esp32-c3-idf.yaml @@ -1,11 +1,7 @@ -i2s_audio: - i2s_lrclk_pin: 6 - i2s_bclk_pin: 7 - i2s_mclk_pin: 8 +substitutions: + i2s_bclk_pin: GPIO6 + i2s_lrclk_pin: GPIO7 + i2s_mclk_pin: GPIO8 + i2s_din_pin: GPIO3 -microphone: - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 3 - adc_type: external - pdm: false +<<: !include common.yaml diff --git a/tests/components/microphone/test.esp32-idf.yaml b/tests/components/microphone/test.esp32-idf.yaml index 166eedb54d..392df582cc 100644 --- a/tests/components/microphone/test.esp32-idf.yaml +++ b/tests/components/microphone/test.esp32-idf.yaml @@ -1,15 +1,13 @@ -i2s_audio: - i2s_lrclk_pin: 13 - i2s_bclk_pin: 14 - i2s_mclk_pin: 15 +substitutions: + i2s_bclk_pin: GPIO15 + i2s_lrclk_pin: GPIO16 + i2s_mclk_pin: GPIO17 + i2s_din_pin: GPIO33 + +<<: !include common.yaml microphone: - platform: i2s_audio id: mic_id_adc adc_pin: 32 adc_type: internal - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 33 - adc_type: external - pdm: false diff --git a/tests/components/mics_4514/common.yaml b/tests/components/mics_4514/common.yaml new file mode 100644 index 0000000000..0bc3f3e654 --- /dev/null +++ b/tests/components/mics_4514/common.yaml @@ -0,0 +1,20 @@ +i2c: + - id: i2c_mics_4514 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mics_4514 + update_interval: 60s + nitrogen_dioxide: + name: MICS-4514 NO2 + carbon_monoxide: + name: MICS-4514 CO + methane: + name: MICS-4514 CH4 + hydrogen: + name: MICS-4514 H2 + ethanol: + name: MICS-4514 C2H5OH + ammonia: + name: MICS-4514 NH3 diff --git a/tests/components/mics_4514/test.esp32-ard.yaml b/tests/components/mics_4514/test.esp32-ard.yaml index 234839c91c..63c3bd6afd 100644 --- a/tests/components/mics_4514/test.esp32-ard.yaml +++ b/tests/components/mics_4514/test.esp32-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mics_4514 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mics_4514 - update_interval: 60s - nitrogen_dioxide: - name: MICS-4514 NO2 - carbon_monoxide: - name: MICS-4514 CO - methane: - name: MICS-4514 CH4 - hydrogen: - name: MICS-4514 H2 - ethanol: - name: MICS-4514 C2H5OH - ammonia: - name: MICS-4514 NH3 +<<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp32-c3-ard.yaml b/tests/components/mics_4514/test.esp32-c3-ard.yaml index 72369bec01..ee2c29ca4e 100644 --- a/tests/components/mics_4514/test.esp32-c3-ard.yaml +++ b/tests/components/mics_4514/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mics_4514 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mics_4514 - update_interval: 60s - nitrogen_dioxide: - name: MICS-4514 NO2 - carbon_monoxide: - name: MICS-4514 CO - methane: - name: MICS-4514 CH4 - hydrogen: - name: MICS-4514 H2 - ethanol: - name: MICS-4514 C2H5OH - ammonia: - name: MICS-4514 NH3 +<<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp32-c3-idf.yaml b/tests/components/mics_4514/test.esp32-c3-idf.yaml index 72369bec01..ee2c29ca4e 100644 --- a/tests/components/mics_4514/test.esp32-c3-idf.yaml +++ b/tests/components/mics_4514/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mics_4514 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mics_4514 - update_interval: 60s - nitrogen_dioxide: - name: MICS-4514 NO2 - carbon_monoxide: - name: MICS-4514 CO - methane: - name: MICS-4514 CH4 - hydrogen: - name: MICS-4514 H2 - ethanol: - name: MICS-4514 C2H5OH - ammonia: - name: MICS-4514 NH3 +<<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp32-idf.yaml b/tests/components/mics_4514/test.esp32-idf.yaml index 234839c91c..63c3bd6afd 100644 --- a/tests/components/mics_4514/test.esp32-idf.yaml +++ b/tests/components/mics_4514/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mics_4514 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mics_4514 - update_interval: 60s - nitrogen_dioxide: - name: MICS-4514 NO2 - carbon_monoxide: - name: MICS-4514 CO - methane: - name: MICS-4514 CH4 - hydrogen: - name: MICS-4514 H2 - ethanol: - name: MICS-4514 C2H5OH - ammonia: - name: MICS-4514 NH3 +<<: !include common.yaml diff --git a/tests/components/mics_4514/test.esp8266-ard.yaml b/tests/components/mics_4514/test.esp8266-ard.yaml index 72369bec01..ee2c29ca4e 100644 --- a/tests/components/mics_4514/test.esp8266-ard.yaml +++ b/tests/components/mics_4514/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mics_4514 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mics_4514 - update_interval: 60s - nitrogen_dioxide: - name: MICS-4514 NO2 - carbon_monoxide: - name: MICS-4514 CO - methane: - name: MICS-4514 CH4 - hydrogen: - name: MICS-4514 H2 - ethanol: - name: MICS-4514 C2H5OH - ammonia: - name: MICS-4514 NH3 +<<: !include common.yaml diff --git a/tests/components/mics_4514/test.rp2040-ard.yaml b/tests/components/mics_4514/test.rp2040-ard.yaml index 72369bec01..ee2c29ca4e 100644 --- a/tests/components/mics_4514/test.rp2040-ard.yaml +++ b/tests/components/mics_4514/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mics_4514 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mics_4514 - update_interval: 60s - nitrogen_dioxide: - name: MICS-4514 NO2 - carbon_monoxide: - name: MICS-4514 CO - methane: - name: MICS-4514 CH4 - hydrogen: - name: MICS-4514 H2 - ethanol: - name: MICS-4514 C2H5OH - ammonia: - name: MICS-4514 NH3 +<<: !include common.yaml diff --git a/tests/components/midea/common.yaml b/tests/components/midea/common.yaml new file mode 100644 index 0000000000..07385e29a1 --- /dev/null +++ b/tests/components/midea/common.yaml @@ -0,0 +1,59 @@ +wifi: + ssid: MySSID + password: password1 + +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +uart: + - id: uart_midea + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +climate: + - platform: midea + id: midea_unit + name: Midea Climate + on_control: + - logger.log: Control message received! + - lambda: |- + x.set_mode(CLIMATE_MODE_FAN_ONLY); + on_state: + - logger.log: State changed! + transmitter_id: + period: 1s + num_attempts: 5 + timeout: 2s + beeper: false + autoconf: true + visual: + min_temperature: 17 °C + max_temperature: 30 °C + temperature_step: 0.5 °C + supported_modes: + - FAN_ONLY + - HEAT_COOL + - COOL + - HEAT + - DRY + custom_fan_modes: + - SILENT + - TURBO + supported_presets: + - ECO + - BOOST + - SLEEP + custom_presets: + - FREEZE_PROTECTION + supported_swing_modes: + - VERTICAL + - HORIZONTAL + - BOTH + outdoor_temperature: + name: Temp + power_usage: + name: Power + humidity_setpoint: + name: Humidity diff --git a/tests/components/midea/test.esp32-ard.yaml b/tests/components/midea/test.esp32-ard.yaml index 5c638b9613..7f55d6a52d 100644 --- a/tests/components/midea/test.esp32-ard.yaml +++ b/tests/components/midea/test.esp32-ard.yaml @@ -1,59 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + pin: GPIO2 -remote_transmitter: - pin: 18 - carrier_duty_percent: 50% - -uart: - - id: uart_midea - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -climate: - - platform: midea - id: midea_unit - name: Midea Climate - on_control: - - logger.log: Control message received! - - lambda: |- - x.set_mode(CLIMATE_MODE_FAN_ONLY); - on_state: - - logger.log: State changed! - transmitter_id: - period: 1s - num_attempts: 5 - timeout: 2s - beeper: false - autoconf: true - visual: - min_temperature: 17 °C - max_temperature: 30 °C - temperature_step: 0.5 °C - supported_modes: - - FAN_ONLY - - HEAT_COOL - - COOL - - HEAT - - DRY - custom_fan_modes: - - SILENT - - TURBO - supported_presets: - - ECO - - BOOST - - SLEEP - custom_presets: - - FREEZE_PROTECTION - supported_swing_modes: - - VERTICAL - - HORIZONTAL - - BOTH - outdoor_temperature: - name: Temp - power_usage: - name: Power - humidity_setpoint: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/midea/test.esp32-c3-ard.yaml b/tests/components/midea/test.esp32-c3-ard.yaml index bcb8635eaf..a879df3ca4 100644 --- a/tests/components/midea/test.esp32-c3-ard.yaml +++ b/tests/components/midea/test.esp32-c3-ard.yaml @@ -1,59 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + pin: GPIO2 -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% - -uart: - - id: uart_midea - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -climate: - - platform: midea - id: midea_unit - name: Midea Climate - on_control: - - logger.log: Control message received! - - lambda: |- - x.set_mode(CLIMATE_MODE_FAN_ONLY); - on_state: - - logger.log: State changed! - transmitter_id: - period: 1s - num_attempts: 5 - timeout: 2s - beeper: false - autoconf: true - visual: - min_temperature: 17 °C - max_temperature: 30 °C - temperature_step: 0.5 °C - supported_modes: - - FAN_ONLY - - HEAT_COOL - - COOL - - HEAT - - DRY - custom_fan_modes: - - SILENT - - TURBO - supported_presets: - - ECO - - BOOST - - SLEEP - custom_presets: - - FREEZE_PROTECTION - supported_swing_modes: - - VERTICAL - - HORIZONTAL - - BOTH - outdoor_temperature: - name: Temp - power_usage: - name: Power - humidity_setpoint: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/midea/test.esp8266-ard.yaml b/tests/components/midea/test.esp8266-ard.yaml index b0ed7eb472..4f50bd7e5e 100644 --- a/tests/components/midea/test.esp8266-ard.yaml +++ b/tests/components/midea/test.esp8266-ard.yaml @@ -1,59 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + pin: GPIO15 -remote_transmitter: - pin: 12 - carrier_duty_percent: 50% - -uart: - - id: uart_midea - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -climate: - - platform: midea - id: midea_unit - name: Midea Climate - on_control: - - logger.log: Control message received! - - lambda: |- - x.set_mode(CLIMATE_MODE_FAN_ONLY); - on_state: - - logger.log: State changed! - transmitter_id: - period: 1s - num_attempts: 5 - timeout: 2s - beeper: false - autoconf: true - visual: - min_temperature: 17 °C - max_temperature: 30 °C - temperature_step: 0.5 °C - supported_modes: - - FAN_ONLY - - HEAT_COOL - - COOL - - HEAT - - DRY - custom_fan_modes: - - SILENT - - TURBO - supported_presets: - - ECO - - BOOST - - SLEEP - custom_presets: - - FREEZE_PROTECTION - supported_swing_modes: - - VERTICAL - - HORIZONTAL - - BOTH - outdoor_temperature: - name: Temp - power_usage: - name: Power - humidity_setpoint: - name: Humidity +<<: !include common.yaml diff --git a/tests/components/mlx90393/common.yaml b/tests/components/mlx90393/common.yaml new file mode 100644 index 0000000000..a7ab0867cc --- /dev/null +++ b/tests/components/mlx90393/common.yaml @@ -0,0 +1,20 @@ +i2c: + - id: i2c_mlx90393 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mlx90393 + oversampling: 1 + filter: 0 + gain: 3X + x_axis: + name: mlxxaxis + y_axis: + name: mlxyaxis + z_axis: + name: mlxzaxis + resolution: 17BIT + temperature: + name: mlxtemp + oversampling: 2 diff --git a/tests/components/mlx90393/test.esp32-ard.yaml b/tests/components/mlx90393/test.esp32-ard.yaml index 089fd136f4..63c3bd6afd 100644 --- a/tests/components/mlx90393/test.esp32-ard.yaml +++ b/tests/components/mlx90393/test.esp32-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mlx90393 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mlx90393 - oversampling: 1 - filter: 0 - gain: 3X - x_axis: - name: mlxxaxis - y_axis: - name: mlxyaxis - z_axis: - name: mlxzaxis - resolution: 17BIT - temperature: - name: mlxtemp - oversampling: 2 +<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-c3-ard.yaml b/tests/components/mlx90393/test.esp32-c3-ard.yaml index 549eea8032..ee2c29ca4e 100644 --- a/tests/components/mlx90393/test.esp32-c3-ard.yaml +++ b/tests/components/mlx90393/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mlx90393 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90393 - oversampling: 1 - filter: 0 - gain: 3X - x_axis: - name: mlxxaxis - y_axis: - name: mlxyaxis - z_axis: - name: mlxzaxis - resolution: 17BIT - temperature: - name: mlxtemp - oversampling: 2 +<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-c3-idf.yaml b/tests/components/mlx90393/test.esp32-c3-idf.yaml index 549eea8032..ee2c29ca4e 100644 --- a/tests/components/mlx90393/test.esp32-c3-idf.yaml +++ b/tests/components/mlx90393/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mlx90393 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90393 - oversampling: 1 - filter: 0 - gain: 3X - x_axis: - name: mlxxaxis - y_axis: - name: mlxyaxis - z_axis: - name: mlxzaxis - resolution: 17BIT - temperature: - name: mlxtemp - oversampling: 2 +<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp32-idf.yaml b/tests/components/mlx90393/test.esp32-idf.yaml index 089fd136f4..63c3bd6afd 100644 --- a/tests/components/mlx90393/test.esp32-idf.yaml +++ b/tests/components/mlx90393/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mlx90393 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mlx90393 - oversampling: 1 - filter: 0 - gain: 3X - x_axis: - name: mlxxaxis - y_axis: - name: mlxyaxis - z_axis: - name: mlxzaxis - resolution: 17BIT - temperature: - name: mlxtemp - oversampling: 2 +<<: !include common.yaml diff --git a/tests/components/mlx90393/test.esp8266-ard.yaml b/tests/components/mlx90393/test.esp8266-ard.yaml index 549eea8032..ee2c29ca4e 100644 --- a/tests/components/mlx90393/test.esp8266-ard.yaml +++ b/tests/components/mlx90393/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mlx90393 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90393 - oversampling: 1 - filter: 0 - gain: 3X - x_axis: - name: mlxxaxis - y_axis: - name: mlxyaxis - z_axis: - name: mlxzaxis - resolution: 17BIT - temperature: - name: mlxtemp - oversampling: 2 +<<: !include common.yaml diff --git a/tests/components/mlx90393/test.rp2040-ard.yaml b/tests/components/mlx90393/test.rp2040-ard.yaml index 549eea8032..ee2c29ca4e 100644 --- a/tests/components/mlx90393/test.rp2040-ard.yaml +++ b/tests/components/mlx90393/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_mlx90393 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90393 - oversampling: 1 - filter: 0 - gain: 3X - x_axis: - name: mlxxaxis - y_axis: - name: mlxyaxis - z_axis: - name: mlxzaxis - resolution: 17BIT - temperature: - name: mlxtemp - oversampling: 2 +<<: !include common.yaml diff --git a/tests/components/mlx90614/common.yaml b/tests/components/mlx90614/common.yaml new file mode 100644 index 0000000000..03279e6e14 --- /dev/null +++ b/tests/components/mlx90614/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_mlx90614 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mlx90614 + ambient: + name: Ambient + object: + name: Object + emissivity: 1.0 diff --git a/tests/components/mlx90614/test.esp32-ard.yaml b/tests/components/mlx90614/test.esp32-ard.yaml index 8c1ee68f42..63c3bd6afd 100644 --- a/tests/components/mlx90614/test.esp32-ard.yaml +++ b/tests/components/mlx90614/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mlx90614 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mlx90614 - ambient: - name: Ambient - object: - name: Object - emissivity: 1.0 +<<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp32-c3-ard.yaml b/tests/components/mlx90614/test.esp32-c3-ard.yaml index a863e0ee2e..ee2c29ca4e 100644 --- a/tests/components/mlx90614/test.esp32-c3-ard.yaml +++ b/tests/components/mlx90614/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mlx90614 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90614 - ambient: - name: Ambient - object: - name: Object - emissivity: 1.0 +<<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp32-c3-idf.yaml b/tests/components/mlx90614/test.esp32-c3-idf.yaml index a863e0ee2e..ee2c29ca4e 100644 --- a/tests/components/mlx90614/test.esp32-c3-idf.yaml +++ b/tests/components/mlx90614/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mlx90614 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90614 - ambient: - name: Ambient - object: - name: Object - emissivity: 1.0 +<<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp32-idf.yaml b/tests/components/mlx90614/test.esp32-idf.yaml index 8c1ee68f42..63c3bd6afd 100644 --- a/tests/components/mlx90614/test.esp32-idf.yaml +++ b/tests/components/mlx90614/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mlx90614 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mlx90614 - ambient: - name: Ambient - object: - name: Object - emissivity: 1.0 +<<: !include common.yaml diff --git a/tests/components/mlx90614/test.esp8266-ard.yaml b/tests/components/mlx90614/test.esp8266-ard.yaml index a863e0ee2e..ee2c29ca4e 100644 --- a/tests/components/mlx90614/test.esp8266-ard.yaml +++ b/tests/components/mlx90614/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mlx90614 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90614 - ambient: - name: Ambient - object: - name: Object - emissivity: 1.0 +<<: !include common.yaml diff --git a/tests/components/mlx90614/test.rp2040-ard.yaml b/tests/components/mlx90614/test.rp2040-ard.yaml index a863e0ee2e..ee2c29ca4e 100644 --- a/tests/components/mlx90614/test.rp2040-ard.yaml +++ b/tests/components/mlx90614/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mlx90614 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mlx90614 - ambient: - name: Ambient - object: - name: Object - emissivity: 1.0 +<<: !include common.yaml diff --git a/tests/components/mmc5603/common.yaml b/tests/components/mmc5603/common.yaml new file mode 100644 index 0000000000..83235f596e --- /dev/null +++ b/tests/components/mmc5603/common.yaml @@ -0,0 +1,14 @@ +i2c: + - id: i2c_mmc5603 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mmc5603 + address: 0x30 + field_strength_x: + name: HMC5883L Field Strength X + field_strength_y: + name: HMC5883L Field Strength Y + field_strength_z: + name: HMC5883L Field Strength Z diff --git a/tests/components/mmc5603/test.esp32-ard.yaml b/tests/components/mmc5603/test.esp32-ard.yaml index fbb83cd9f8..63c3bd6afd 100644 --- a/tests/components/mmc5603/test.esp32-ard.yaml +++ b/tests/components/mmc5603/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_mmc5603 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mmc5603 - address: 0x30 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z +<<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp32-c3-ard.yaml b/tests/components/mmc5603/test.esp32-c3-ard.yaml index 834591bb39..ee2c29ca4e 100644 --- a/tests/components/mmc5603/test.esp32-c3-ard.yaml +++ b/tests/components/mmc5603/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_mmc5603 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5603 - address: 0x30 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z +<<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp32-c3-idf.yaml b/tests/components/mmc5603/test.esp32-c3-idf.yaml index 834591bb39..ee2c29ca4e 100644 --- a/tests/components/mmc5603/test.esp32-c3-idf.yaml +++ b/tests/components/mmc5603/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_mmc5603 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5603 - address: 0x30 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z +<<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp32-idf.yaml b/tests/components/mmc5603/test.esp32-idf.yaml index fbb83cd9f8..63c3bd6afd 100644 --- a/tests/components/mmc5603/test.esp32-idf.yaml +++ b/tests/components/mmc5603/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_mmc5603 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mmc5603 - address: 0x30 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z +<<: !include common.yaml diff --git a/tests/components/mmc5603/test.esp8266-ard.yaml b/tests/components/mmc5603/test.esp8266-ard.yaml index 834591bb39..ee2c29ca4e 100644 --- a/tests/components/mmc5603/test.esp8266-ard.yaml +++ b/tests/components/mmc5603/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_mmc5603 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5603 - address: 0x30 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z +<<: !include common.yaml diff --git a/tests/components/mmc5603/test.rp2040-ard.yaml b/tests/components/mmc5603/test.rp2040-ard.yaml index 834591bb39..ee2c29ca4e 100644 --- a/tests/components/mmc5603/test.rp2040-ard.yaml +++ b/tests/components/mmc5603/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -i2c: - - id: i2c_mmc5603 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5603 - address: 0x30 - field_strength_x: - name: HMC5883L Field Strength X - field_strength_y: - name: HMC5883L Field Strength Y - field_strength_z: - name: HMC5883L Field Strength Z +<<: !include common.yaml diff --git a/tests/components/mmc5983/common.yaml b/tests/components/mmc5983/common.yaml new file mode 100644 index 0000000000..949ff527e7 --- /dev/null +++ b/tests/components/mmc5983/common.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_mmc5983 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mmc5983 + field_strength_x: + name: "Magnet X" + id: magnet_x + field_strength_y: + name: "Magnet Y" + id: magnet_y + field_strength_z: + name: "Magnet Z" + id: magnet_z diff --git a/tests/components/mmc5983/test.esp32-ard.yaml b/tests/components/mmc5983/test.esp32-ard.yaml index 6104be9b83..63c3bd6afd 100644 --- a/tests/components/mmc5983/test.esp32-ard.yaml +++ b/tests/components/mmc5983/test.esp32-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_mmc5983 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mmc5983 - field_strength_x: - name: "Magnet X" - id: magnet_x - field_strength_y: - name: "Magnet Y" - id: magnet_y - field_strength_z: - name: "Magnet Z" - id: magnet_z +<<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp32-c3-ard.yaml b/tests/components/mmc5983/test.esp32-c3-ard.yaml index 68d821e9a5..ee2c29ca4e 100644 --- a/tests/components/mmc5983/test.esp32-c3-ard.yaml +++ b/tests/components/mmc5983/test.esp32-c3-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_mmc5983 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5983 - field_strength_x: - name: "Magnet X" - id: magnet_x - field_strength_y: - name: "Magnet Y" - id: magnet_y - field_strength_z: - name: "Magnet Z" - id: magnet_z +<<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp32-c3-idf.yaml b/tests/components/mmc5983/test.esp32-c3-idf.yaml index 68d821e9a5..ee2c29ca4e 100644 --- a/tests/components/mmc5983/test.esp32-c3-idf.yaml +++ b/tests/components/mmc5983/test.esp32-c3-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_mmc5983 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5983 - field_strength_x: - name: "Magnet X" - id: magnet_x - field_strength_y: - name: "Magnet Y" - id: magnet_y - field_strength_z: - name: "Magnet Z" - id: magnet_z +<<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp32-idf.yaml b/tests/components/mmc5983/test.esp32-idf.yaml index 6104be9b83..63c3bd6afd 100644 --- a/tests/components/mmc5983/test.esp32-idf.yaml +++ b/tests/components/mmc5983/test.esp32-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_mmc5983 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mmc5983 - field_strength_x: - name: "Magnet X" - id: magnet_x - field_strength_y: - name: "Magnet Y" - id: magnet_y - field_strength_z: - name: "Magnet Z" - id: magnet_z +<<: !include common.yaml diff --git a/tests/components/mmc5983/test.esp8266-ard.yaml b/tests/components/mmc5983/test.esp8266-ard.yaml index 68d821e9a5..ee2c29ca4e 100644 --- a/tests/components/mmc5983/test.esp8266-ard.yaml +++ b/tests/components/mmc5983/test.esp8266-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_mmc5983 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5983 - field_strength_x: - name: "Magnet X" - id: magnet_x - field_strength_y: - name: "Magnet Y" - id: magnet_y - field_strength_z: - name: "Magnet Z" - id: magnet_z +<<: !include common.yaml diff --git a/tests/components/mmc5983/test.rp2040-ard.yaml b/tests/components/mmc5983/test.rp2040-ard.yaml index 68d821e9a5..ee2c29ca4e 100644 --- a/tests/components/mmc5983/test.rp2040-ard.yaml +++ b/tests/components/mmc5983/test.rp2040-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_mmc5983 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mmc5983 - field_strength_x: - name: "Magnet X" - id: magnet_x - field_strength_y: - name: "Magnet Y" - id: magnet_y - field_strength_z: - name: "Magnet Z" - id: magnet_z +<<: !include common.yaml diff --git a/tests/components/modbus/common.yaml b/tests/components/modbus/common.yaml new file mode 100644 index 0000000000..3ec9518585 --- /dev/null +++ b/tests/components/modbus/common.yaml @@ -0,0 +1,9 @@ +uart: + - id: uart_modbus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +modbus: + id: mod_bus1 + flow_control_pin: ${flow_control_pin} diff --git a/tests/components/modbus/test.esp32-ard.yaml b/tests/components/modbus/test.esp32-ard.yaml index 20cf238b1b..bd767a8ece 100644 --- a/tests/components/modbus/test.esp32-ard.yaml +++ b/tests/components/modbus/test.esp32-ard.yaml @@ -1,9 +1,6 @@ -uart: - - id: uart_modbus - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - id: mod_bus1 - flow_control_pin: 15 +<<: !include common.yaml diff --git a/tests/components/modbus/test.esp32-c3-ard.yaml b/tests/components/modbus/test.esp32-c3-ard.yaml index d22b507be0..452031a5aa 100644 --- a/tests/components/modbus/test.esp32-c3-ard.yaml +++ b/tests/components/modbus/test.esp32-c3-ard.yaml @@ -1,9 +1,6 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - id: mod_bus1 - flow_control_pin: 6 +<<: !include common.yaml diff --git a/tests/components/modbus/test.esp32-c3-idf.yaml b/tests/components/modbus/test.esp32-c3-idf.yaml index d22b507be0..452031a5aa 100644 --- a/tests/components/modbus/test.esp32-c3-idf.yaml +++ b/tests/components/modbus/test.esp32-c3-idf.yaml @@ -1,9 +1,6 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - id: mod_bus1 - flow_control_pin: 6 +<<: !include common.yaml diff --git a/tests/components/modbus/test.esp32-idf.yaml b/tests/components/modbus/test.esp32-idf.yaml index 20cf238b1b..bd767a8ece 100644 --- a/tests/components/modbus/test.esp32-idf.yaml +++ b/tests/components/modbus/test.esp32-idf.yaml @@ -1,9 +1,6 @@ -uart: - - id: uart_modbus - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO12 + rx_pin: GPIO14 + flow_control_pin: GPIO13 -modbus: - id: mod_bus1 - flow_control_pin: 15 +<<: !include common.yaml diff --git a/tests/components/modbus/test.esp8266-ard.yaml b/tests/components/modbus/test.esp8266-ard.yaml index 560c044766..29c98d0957 100644 --- a/tests/components/modbus/test.esp8266-ard.yaml +++ b/tests/components/modbus/test.esp8266-ard.yaml @@ -1,9 +1,6 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO13 -modbus: - id: mod_bus1 - flow_control_pin: 12 +<<: !include common.yaml diff --git a/tests/components/modbus/test.rp2040-ard.yaml b/tests/components/modbus/test.rp2040-ard.yaml index d22b507be0..452031a5aa 100644 --- a/tests/components/modbus/test.rp2040-ard.yaml +++ b/tests/components/modbus/test.rp2040-ard.yaml @@ -1,9 +1,6 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + flow_control_pin: GPIO3 -modbus: - id: mod_bus1 - flow_control_pin: 6 +<<: !include common.yaml diff --git a/tests/components/modbus_controller/common.yaml b/tests/components/modbus_controller/common.yaml new file mode 100644 index 0000000000..93d8391ff5 --- /dev/null +++ b/tests/components/modbus_controller/common.yaml @@ -0,0 +1,35 @@ +uart: + - id: uart_modbus_client + tx_pin: ${client_tx_pin} + rx_pin: ${client_rx_pin} + baud_rate: 9600 + - id: uart_modbus_server + tx_pin: ${server_tx_pin} + rx_pin: ${server_rx_pin} + baud_rate: 9600 + +modbus: + - id: mod_bus1 + uart_id: uart_modbus_client + flow_control_pin: ${flow_control_pin} + - id: mod_bus2 + uart_id: uart_modbus_server + role: server + +modbus_controller: + - id: modbus_controller1 + address: 0x2 + modbus_id: mod_bus1 + allow_duplicate_commands: false + on_online: + then: + logger.log: "Module Online" + - id: modbus_controller2 + address: 0x2 + modbus_id: mod_bus2 + server_registers: + - address: 0x0000 + value_type: S_DWORD_R + read_lambda: |- + return 42.3; + max_cmd_retries: 0 diff --git a/tests/components/modbus_controller/test.esp32-ard.yaml b/tests/components/modbus_controller/test.esp32-ard.yaml index f5c5c10125..548b8c0666 100644 --- a/tests/components/modbus_controller/test.esp32-ard.yaml +++ b/tests/components/modbus_controller/test.esp32-ard.yaml @@ -1,35 +1,8 @@ -uart: - - id: uart_modbus_client - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - - id: uart_modbus_server - tx_pin: 1 - rx_pin: 3 - baud_rate: 9600 +substitutions: + client_tx_pin: GPIO12 + client_rx_pin: GPIO14 + server_tx_pin: GPIO16 + server_rx_pin: GPIO17 + flow_control_pin: GPIO13 -modbus: - - id: mod_bus1 - uart_id: uart_modbus_client - flow_control_pin: 15 - - id: mod_bus2 - uart_id: uart_modbus_server - role: server - -modbus_controller: - - id: modbus_controller1 - address: 0x2 - modbus_id: mod_bus1 - allow_duplicate_commands: false - on_online: - then: - logger.log: "Module Online" - - id: modbus_controller2 - address: 0x2 - modbus_id: mod_bus2 - server_registers: - - address: 0x0000 - value_type: S_DWORD_R - read_lambda: |- - return 42.3; - max_cmd_retries: 0 +<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp32-c3-ard.yaml b/tests/components/modbus_controller/test.esp32-c3-ard.yaml index 476e65ecb0..f5b770ff58 100644 --- a/tests/components/modbus_controller/test.esp32-c3-ard.yaml +++ b/tests/components/modbus_controller/test.esp32-c3-ard.yaml @@ -1,14 +1,8 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + client_tx_pin: GPIO4 + client_rx_pin: GPIO5 + server_tx_pin: GPIO6 + server_rx_pin: GPIO7 + flow_control_pin: GPIO3 -modbus: - id: mod_bus1 - flow_control_pin: 6 - -modbus_controller: - - id: modbus_controller1 - address: 0x2 - modbus_id: mod_bus1 +<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp32-c3-idf.yaml b/tests/components/modbus_controller/test.esp32-c3-idf.yaml index 476e65ecb0..f5b770ff58 100644 --- a/tests/components/modbus_controller/test.esp32-c3-idf.yaml +++ b/tests/components/modbus_controller/test.esp32-c3-idf.yaml @@ -1,14 +1,8 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + client_tx_pin: GPIO4 + client_rx_pin: GPIO5 + server_tx_pin: GPIO6 + server_rx_pin: GPIO7 + flow_control_pin: GPIO3 -modbus: - id: mod_bus1 - flow_control_pin: 6 - -modbus_controller: - - id: modbus_controller1 - address: 0x2 - modbus_id: mod_bus1 +<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp32-idf.yaml b/tests/components/modbus_controller/test.esp32-idf.yaml index 0e1849dd88..548b8c0666 100644 --- a/tests/components/modbus_controller/test.esp32-idf.yaml +++ b/tests/components/modbus_controller/test.esp32-idf.yaml @@ -1,19 +1,8 @@ -uart: - - id: uart_modbus - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + client_tx_pin: GPIO12 + client_rx_pin: GPIO14 + server_tx_pin: GPIO16 + server_rx_pin: GPIO17 + flow_control_pin: GPIO13 -modbus: - id: mod_bus1 - flow_control_pin: 15 - -modbus_controller: - - id: modbus_controller1 - address: 0x2 - modbus_id: mod_bus1 - allow_duplicate_commands: true - on_offline: - then: - logger.log: "Module Offline" - max_cmd_retries: 10 +<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.esp8266-ard.yaml b/tests/components/modbus_controller/test.esp8266-ard.yaml index 67cac65d1b..c68a57cbde 100644 --- a/tests/components/modbus_controller/test.esp8266-ard.yaml +++ b/tests/components/modbus_controller/test.esp8266-ard.yaml @@ -1,14 +1,8 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + client_tx_pin: GPIO1 + client_rx_pin: GPIO3 + server_tx_pin: GPIO4 + server_rx_pin: GPIO5 + flow_control_pin: GPIO13 -modbus: - id: mod_bus1 - flow_control_pin: 12 - -modbus_controller: - - id: modbus_controller1 - address: 0x2 - modbus_id: mod_bus1 +<<: !include common.yaml diff --git a/tests/components/modbus_controller/test.rp2040-ard.yaml b/tests/components/modbus_controller/test.rp2040-ard.yaml index 476e65ecb0..80528bc12b 100644 --- a/tests/components/modbus_controller/test.rp2040-ard.yaml +++ b/tests/components/modbus_controller/test.rp2040-ard.yaml @@ -1,14 +1,8 @@ -uart: - - id: uart_modbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + client_tx_pin: GPIO2 + client_rx_pin: GPIO3 + server_tx_pin: GPIO4 + server_rx_pin: GPIO5 + flow_control_pin: GPIO6 -modbus: - id: mod_bus1 - flow_control_pin: 6 - -modbus_controller: - - id: modbus_controller1 - address: 0x2 - modbus_id: mod_bus1 +<<: !include common.yaml diff --git a/tests/components/monochromatic/common.yaml b/tests/components/monochromatic/common.yaml new file mode 100644 index 0000000000..9915e086eb --- /dev/null +++ b/tests/components/monochromatic/common.yaml @@ -0,0 +1,40 @@ +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin} + +light: + - platform: monochromatic + name: Monochromatic Light + id: monochromatic_light + output: light_output_1 + gamma_correct: 2.8 + default_transition_length: 2s + effects: + - strobe: + - flicker: + - flicker: + name: My Flicker + alpha: 98% + intensity: 1.5% + - lambda: + name: My Custom Effect + update_interval: 1s + lambda: |- + static int state = 0; + state += 1; + if (state == 4) + state = 0; + - pulse: + transition_length: 10s + update_interval: 20s + min_brightness: 10% + max_brightness: 90% + - pulse: + name: pulse2 + transition_length: + on_length: 10s + off_length: 5s + update_interval: 15s + min_brightness: 10% + max_brightness: 90% diff --git a/tests/components/monochromatic/test.esp32-ard.yaml b/tests/components/monochromatic/test.esp32-ard.yaml index 9524efcb2d..feabf013fd 100644 --- a/tests/components/monochromatic/test.esp32-ard.yaml +++ b/tests/components/monochromatic/test.esp32-ard.yaml @@ -1,40 +1,5 @@ -output: - - platform: ledc - id: light_output_1 - pin: 4 +substitutions: + light_platform: ledc + pin: GPIO2 -light: - - platform: monochromatic - name: Monochromatic Light - id: monochromatic_light - output: light_output_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% +<<: !include common.yaml diff --git a/tests/components/monochromatic/test.esp32-c3-ard.yaml b/tests/components/monochromatic/test.esp32-c3-ard.yaml index 9524efcb2d..feabf013fd 100644 --- a/tests/components/monochromatic/test.esp32-c3-ard.yaml +++ b/tests/components/monochromatic/test.esp32-c3-ard.yaml @@ -1,40 +1,5 @@ -output: - - platform: ledc - id: light_output_1 - pin: 4 +substitutions: + light_platform: ledc + pin: GPIO2 -light: - - platform: monochromatic - name: Monochromatic Light - id: monochromatic_light - output: light_output_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% +<<: !include common.yaml diff --git a/tests/components/monochromatic/test.esp32-c3-idf.yaml b/tests/components/monochromatic/test.esp32-c3-idf.yaml index 9524efcb2d..feabf013fd 100644 --- a/tests/components/monochromatic/test.esp32-c3-idf.yaml +++ b/tests/components/monochromatic/test.esp32-c3-idf.yaml @@ -1,40 +1,5 @@ -output: - - platform: ledc - id: light_output_1 - pin: 4 +substitutions: + light_platform: ledc + pin: GPIO2 -light: - - platform: monochromatic - name: Monochromatic Light - id: monochromatic_light - output: light_output_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% +<<: !include common.yaml diff --git a/tests/components/monochromatic/test.esp32-idf.yaml b/tests/components/monochromatic/test.esp32-idf.yaml index 9524efcb2d..feabf013fd 100644 --- a/tests/components/monochromatic/test.esp32-idf.yaml +++ b/tests/components/monochromatic/test.esp32-idf.yaml @@ -1,40 +1,5 @@ -output: - - platform: ledc - id: light_output_1 - pin: 4 +substitutions: + light_platform: ledc + pin: GPIO2 -light: - - platform: monochromatic - name: Monochromatic Light - id: monochromatic_light - output: light_output_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% +<<: !include common.yaml diff --git a/tests/components/monochromatic/test.esp8266-ard.yaml b/tests/components/monochromatic/test.esp8266-ard.yaml index 94d849581d..65bfb329f1 100644 --- a/tests/components/monochromatic/test.esp8266-ard.yaml +++ b/tests/components/monochromatic/test.esp8266-ard.yaml @@ -1,40 +1,5 @@ -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 4 +substitutions: + light_platform: esp8266_pwm + pin: GPIO5 -light: - - platform: monochromatic - name: Monochromatic Light - id: monochromatic_light - output: light_output_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% +<<: !include common.yaml diff --git a/tests/components/monochromatic/test.rp2040-ard.yaml b/tests/components/monochromatic/test.rp2040-ard.yaml index 093577e256..d329597d46 100644 --- a/tests/components/monochromatic/test.rp2040-ard.yaml +++ b/tests/components/monochromatic/test.rp2040-ard.yaml @@ -1,40 +1,5 @@ -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 4 +substitutions: + light_platform: rp2040_pwm + pin: GPIO2 -light: - - platform: monochromatic - name: Monochromatic Light - id: monochromatic_light - output: light_output_1 - gamma_correct: 2.8 - default_transition_length: 2s - effects: - - strobe: - - flicker: - - flicker: - name: My Flicker - alpha: 98% - intensity: 1.5% - - lambda: - name: My Custom Effect - update_interval: 1s - lambda: |- - static int state = 0; - state += 1; - if (state == 4) - state = 0; - - pulse: - transition_length: 10s - update_interval: 20s - min_brightness: 10% - max_brightness: 90% - - pulse: - name: pulse2 - transition_length: - on_length: 10s - off_length: 5s - update_interval: 15s - min_brightness: 10% - max_brightness: 90% +<<: !include common.yaml diff --git a/tests/components/mpl3115a2/common.yaml b/tests/components/mpl3115a2/common.yaml new file mode 100644 index 0000000000..f2f65885b3 --- /dev/null +++ b/tests/components/mpl3115a2/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_mpl3115a2 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mpl3115a2 + temperature: + name: MPL3115A2 Temperature + pressure: + name: MPL3115A2 Pressure + update_interval: 10s diff --git a/tests/components/mpl3115a2/test.esp32-ard.yaml b/tests/components/mpl3115a2/test.esp32-ard.yaml index 5e9d6d190d..63c3bd6afd 100644 --- a/tests/components/mpl3115a2/test.esp32-ard.yaml +++ b/tests/components/mpl3115a2/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mpl3115a2 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mpl3115a2 - temperature: - name: MPL3115A2 Temperature - pressure: - name: MPL3115A2 Pressure - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp32-c3-ard.yaml b/tests/components/mpl3115a2/test.esp32-c3-ard.yaml index 9cbe08d920..ee2c29ca4e 100644 --- a/tests/components/mpl3115a2/test.esp32-c3-ard.yaml +++ b/tests/components/mpl3115a2/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mpl3115a2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpl3115a2 - temperature: - name: MPL3115A2 Temperature - pressure: - name: MPL3115A2 Pressure - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp32-c3-idf.yaml b/tests/components/mpl3115a2/test.esp32-c3-idf.yaml index 9cbe08d920..ee2c29ca4e 100644 --- a/tests/components/mpl3115a2/test.esp32-c3-idf.yaml +++ b/tests/components/mpl3115a2/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mpl3115a2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpl3115a2 - temperature: - name: MPL3115A2 Temperature - pressure: - name: MPL3115A2 Pressure - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp32-idf.yaml b/tests/components/mpl3115a2/test.esp32-idf.yaml index 5e9d6d190d..63c3bd6afd 100644 --- a/tests/components/mpl3115a2/test.esp32-idf.yaml +++ b/tests/components/mpl3115a2/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mpl3115a2 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mpl3115a2 - temperature: - name: MPL3115A2 Temperature - pressure: - name: MPL3115A2 Pressure - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.esp8266-ard.yaml b/tests/components/mpl3115a2/test.esp8266-ard.yaml index 9cbe08d920..ee2c29ca4e 100644 --- a/tests/components/mpl3115a2/test.esp8266-ard.yaml +++ b/tests/components/mpl3115a2/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mpl3115a2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpl3115a2 - temperature: - name: MPL3115A2 Temperature - pressure: - name: MPL3115A2 Pressure - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/mpl3115a2/test.rp2040-ard.yaml b/tests/components/mpl3115a2/test.rp2040-ard.yaml index 9cbe08d920..ee2c29ca4e 100644 --- a/tests/components/mpl3115a2/test.rp2040-ard.yaml +++ b/tests/components/mpl3115a2/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_mpl3115a2 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpl3115a2 - temperature: - name: MPL3115A2 Temperature - pressure: - name: MPL3115A2 Pressure - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/mpu6050/common.yaml b/tests/components/mpu6050/common.yaml new file mode 100644 index 0000000000..e9d4995534 --- /dev/null +++ b/tests/components/mpu6050/common.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_mpu6050 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mpu6050 + address: 0x68 + accel_x: + name: MPU6050 Accel X + accel_y: + name: MPU6050 Accel Y + accel_z: + name: MPU6050 Accel z + gyro_x: + name: MPU6050 Gyro X + gyro_y: + name: MPU6050 Gyro Y + gyro_z: + name: MPU6050 Gyro z + temperature: + name: MPU6050 Temperature diff --git a/tests/components/mpu6050/test.esp32-ard.yaml b/tests/components/mpu6050/test.esp32-ard.yaml index 45bca55dea..63c3bd6afd 100644 --- a/tests/components/mpu6050/test.esp32-ard.yaml +++ b/tests/components/mpu6050/test.esp32-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6050 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mpu6050 - address: 0x68 - accel_x: - name: MPU6050 Accel X - accel_y: - name: MPU6050 Accel Y - accel_z: - name: MPU6050 Accel z - gyro_x: - name: MPU6050 Gyro X - gyro_y: - name: MPU6050 Gyro Y - gyro_z: - name: MPU6050 Gyro z - temperature: - name: MPU6050 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp32-c3-ard.yaml b/tests/components/mpu6050/test.esp32-c3-ard.yaml index 39c8506d2b..ee2c29ca4e 100644 --- a/tests/components/mpu6050/test.esp32-c3-ard.yaml +++ b/tests/components/mpu6050/test.esp32-c3-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6050 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6050 - address: 0x68 - accel_x: - name: MPU6050 Accel X - accel_y: - name: MPU6050 Accel Y - accel_z: - name: MPU6050 Accel z - gyro_x: - name: MPU6050 Gyro X - gyro_y: - name: MPU6050 Gyro Y - gyro_z: - name: MPU6050 Gyro z - temperature: - name: MPU6050 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp32-c3-idf.yaml b/tests/components/mpu6050/test.esp32-c3-idf.yaml index 39c8506d2b..ee2c29ca4e 100644 --- a/tests/components/mpu6050/test.esp32-c3-idf.yaml +++ b/tests/components/mpu6050/test.esp32-c3-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6050 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6050 - address: 0x68 - accel_x: - name: MPU6050 Accel X - accel_y: - name: MPU6050 Accel Y - accel_z: - name: MPU6050 Accel z - gyro_x: - name: MPU6050 Gyro X - gyro_y: - name: MPU6050 Gyro Y - gyro_z: - name: MPU6050 Gyro z - temperature: - name: MPU6050 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp32-idf.yaml b/tests/components/mpu6050/test.esp32-idf.yaml index 45bca55dea..63c3bd6afd 100644 --- a/tests/components/mpu6050/test.esp32-idf.yaml +++ b/tests/components/mpu6050/test.esp32-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6050 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mpu6050 - address: 0x68 - accel_x: - name: MPU6050 Accel X - accel_y: - name: MPU6050 Accel Y - accel_z: - name: MPU6050 Accel z - gyro_x: - name: MPU6050 Gyro X - gyro_y: - name: MPU6050 Gyro Y - gyro_z: - name: MPU6050 Gyro z - temperature: - name: MPU6050 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6050/test.esp8266-ard.yaml b/tests/components/mpu6050/test.esp8266-ard.yaml index 39c8506d2b..ee2c29ca4e 100644 --- a/tests/components/mpu6050/test.esp8266-ard.yaml +++ b/tests/components/mpu6050/test.esp8266-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6050 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6050 - address: 0x68 - accel_x: - name: MPU6050 Accel X - accel_y: - name: MPU6050 Accel Y - accel_z: - name: MPU6050 Accel z - gyro_x: - name: MPU6050 Gyro X - gyro_y: - name: MPU6050 Gyro Y - gyro_z: - name: MPU6050 Gyro z - temperature: - name: MPU6050 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6050/test.rp2040-ard.yaml b/tests/components/mpu6050/test.rp2040-ard.yaml index 39c8506d2b..ee2c29ca4e 100644 --- a/tests/components/mpu6050/test.rp2040-ard.yaml +++ b/tests/components/mpu6050/test.rp2040-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6050 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6050 - address: 0x68 - accel_x: - name: MPU6050 Accel X - accel_y: - name: MPU6050 Accel Y - accel_z: - name: MPU6050 Accel z - gyro_x: - name: MPU6050 Gyro X - gyro_y: - name: MPU6050 Gyro Y - gyro_z: - name: MPU6050 Gyro z - temperature: - name: MPU6050 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6886/common.yaml b/tests/components/mpu6886/common.yaml new file mode 100644 index 0000000000..9c3e283cc3 --- /dev/null +++ b/tests/components/mpu6886/common.yaml @@ -0,0 +1,22 @@ +i2c: + - id: i2c_mpu6886 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: mpu6886 + address: 0x68 + accel_x: + name: MPU6886 Accel X + accel_y: + name: MPU6886 Accel Y + accel_z: + name: MPU6886 Accel z + gyro_x: + name: MPU6886 Gyro X + gyro_y: + name: MPU6886 Gyro Y + gyro_z: + name: MPU6886 Gyro z + temperature: + name: MPU6886 Temperature diff --git a/tests/components/mpu6886/test.esp32-ard.yaml b/tests/components/mpu6886/test.esp32-ard.yaml index 84e4d56739..63c3bd6afd 100644 --- a/tests/components/mpu6886/test.esp32-ard.yaml +++ b/tests/components/mpu6886/test.esp32-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6886 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mpu6886 - address: 0x68 - accel_x: - name: MPU6886 Accel X - accel_y: - name: MPU6886 Accel Y - accel_z: - name: MPU6886 Accel z - gyro_x: - name: MPU6886 Gyro X - gyro_y: - name: MPU6886 Gyro Y - gyro_z: - name: MPU6886 Gyro z - temperature: - name: MPU6886 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp32-c3-ard.yaml b/tests/components/mpu6886/test.esp32-c3-ard.yaml index fad51a80b4..ee2c29ca4e 100644 --- a/tests/components/mpu6886/test.esp32-c3-ard.yaml +++ b/tests/components/mpu6886/test.esp32-c3-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6886 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6886 - address: 0x68 - accel_x: - name: MPU6886 Accel X - accel_y: - name: MPU6886 Accel Y - accel_z: - name: MPU6886 Accel z - gyro_x: - name: MPU6886 Gyro X - gyro_y: - name: MPU6886 Gyro Y - gyro_z: - name: MPU6886 Gyro z - temperature: - name: MPU6886 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp32-c3-idf.yaml b/tests/components/mpu6886/test.esp32-c3-idf.yaml index fad51a80b4..ee2c29ca4e 100644 --- a/tests/components/mpu6886/test.esp32-c3-idf.yaml +++ b/tests/components/mpu6886/test.esp32-c3-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6886 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6886 - address: 0x68 - accel_x: - name: MPU6886 Accel X - accel_y: - name: MPU6886 Accel Y - accel_z: - name: MPU6886 Accel z - gyro_x: - name: MPU6886 Gyro X - gyro_y: - name: MPU6886 Gyro Y - gyro_z: - name: MPU6886 Gyro z - temperature: - name: MPU6886 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp32-idf.yaml b/tests/components/mpu6886/test.esp32-idf.yaml index 84e4d56739..63c3bd6afd 100644 --- a/tests/components/mpu6886/test.esp32-idf.yaml +++ b/tests/components/mpu6886/test.esp32-idf.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6886 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: mpu6886 - address: 0x68 - accel_x: - name: MPU6886 Accel X - accel_y: - name: MPU6886 Accel Y - accel_z: - name: MPU6886 Accel z - gyro_x: - name: MPU6886 Gyro X - gyro_y: - name: MPU6886 Gyro Y - gyro_z: - name: MPU6886 Gyro z - temperature: - name: MPU6886 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6886/test.esp8266-ard.yaml b/tests/components/mpu6886/test.esp8266-ard.yaml index fad51a80b4..ee2c29ca4e 100644 --- a/tests/components/mpu6886/test.esp8266-ard.yaml +++ b/tests/components/mpu6886/test.esp8266-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6886 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6886 - address: 0x68 - accel_x: - name: MPU6886 Accel X - accel_y: - name: MPU6886 Accel Y - accel_z: - name: MPU6886 Accel z - gyro_x: - name: MPU6886 Gyro X - gyro_y: - name: MPU6886 Gyro Y - gyro_z: - name: MPU6886 Gyro z - temperature: - name: MPU6886 Temperature +<<: !include common.yaml diff --git a/tests/components/mpu6886/test.rp2040-ard.yaml b/tests/components/mpu6886/test.rp2040-ard.yaml index fad51a80b4..ee2c29ca4e 100644 --- a/tests/components/mpu6886/test.rp2040-ard.yaml +++ b/tests/components/mpu6886/test.rp2040-ard.yaml @@ -1,22 +1,5 @@ -i2c: - - id: i2c_mpu6886 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: mpu6886 - address: 0x68 - accel_x: - name: MPU6886 Accel X - accel_y: - name: MPU6886 Accel Y - accel_z: - name: MPU6886 Accel z - gyro_x: - name: MPU6886 Gyro X - gyro_y: - name: MPU6886 Gyro Y - gyro_z: - name: MPU6886 Gyro z - temperature: - name: MPU6886 Temperature +<<: !include common.yaml diff --git a/tests/components/mqtt_subscribe/common-ard.yaml b/tests/components/mqtt_subscribe/common-ard.yaml new file mode 100644 index 0000000000..13ed311b17 --- /dev/null +++ b/tests/components/mqtt_subscribe/common-ard.yaml @@ -0,0 +1,36 @@ +wifi: + ssid: MySSID + password: password1 + +mqtt: + broker: test.mosquitto.org + port: 1883 + discovery: true + discovery_prefix: homeassistant + log_topic: + on_message: + topic: testing/sensor/testing_sensor/state + qos: 0 + then: + - logger.log: Mqtt Test + +sensor: + - platform: mqtt_subscribe + name: MQTT Subscribe Sensor + topic: mqtt/topic + id: the_sensor + qos: 2 + on_value: + - mqtt.publish_json: + topic: the/topic + payload: |- + root["key"] = id(the_sensor).state; + root["greeting"] = "Hello World"; + +text_sensor: + - platform: mqtt_subscribe + name: MQTT Subscribe Text + topic: "the/topic" + qos: 2 + on_value: + - logger.log: "Text sensor got value" diff --git a/tests/components/mqtt_subscribe/common-idf.yaml b/tests/components/mqtt_subscribe/common-idf.yaml new file mode 100644 index 0000000000..070672f15c --- /dev/null +++ b/tests/components/mqtt_subscribe/common-idf.yaml @@ -0,0 +1,37 @@ +wifi: + ssid: MySSID + password: password1 + +mqtt: + broker: test.mosquitto.org + port: 1883 + discovery: true + discovery_prefix: homeassistant + idf_send_async: false + log_topic: + on_message: + topic: testing/sensor/testing_sensor/state + qos: 0 + then: + - logger.log: Mqtt Test + +sensor: + - platform: mqtt_subscribe + name: MQTT Subscribe Sensor + topic: mqtt/topic + id: the_sensor + qos: 2 + on_value: + - mqtt.publish_json: + topic: the/topic + payload: |- + root["key"] = id(the_sensor).state; + root["greeting"] = "Hello World"; + +text_sensor: + - platform: mqtt_subscribe + name: MQTT Subscribe Text + topic: "the/topic" + qos: 2 + on_value: + - logger.log: "Text sensor got value" diff --git a/tests/components/mqtt_subscribe/test.esp32-ard.yaml b/tests/components/mqtt_subscribe/test.esp32-ard.yaml index 13ed311b17..28589ee06c 100644 --- a/tests/components/mqtt_subscribe/test.esp32-ard.yaml +++ b/tests/components/mqtt_subscribe/test.esp32-ard.yaml @@ -1,36 +1 @@ -wifi: - ssid: MySSID - password: password1 - -mqtt: - broker: test.mosquitto.org - port: 1883 - discovery: true - discovery_prefix: homeassistant - log_topic: - on_message: - topic: testing/sensor/testing_sensor/state - qos: 0 - then: - - logger.log: Mqtt Test - -sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Sensor - topic: mqtt/topic - id: the_sensor - qos: 2 - on_value: - - mqtt.publish_json: - topic: the/topic - payload: |- - root["key"] = id(the_sensor).state; - root["greeting"] = "Hello World"; - -text_sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Text - topic: "the/topic" - qos: 2 - on_value: - - logger.log: "Text sensor got value" +<<: !include common-ard.yaml diff --git a/tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml b/tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml index 13ed311b17..28589ee06c 100644 --- a/tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml +++ b/tests/components/mqtt_subscribe/test.esp32-c3-ard.yaml @@ -1,36 +1 @@ -wifi: - ssid: MySSID - password: password1 - -mqtt: - broker: test.mosquitto.org - port: 1883 - discovery: true - discovery_prefix: homeassistant - log_topic: - on_message: - topic: testing/sensor/testing_sensor/state - qos: 0 - then: - - logger.log: Mqtt Test - -sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Sensor - topic: mqtt/topic - id: the_sensor - qos: 2 - on_value: - - mqtt.publish_json: - topic: the/topic - payload: |- - root["key"] = id(the_sensor).state; - root["greeting"] = "Hello World"; - -text_sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Text - topic: "the/topic" - qos: 2 - on_value: - - logger.log: "Text sensor got value" +<<: !include common-ard.yaml diff --git a/tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml b/tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml index 070672f15c..2cb6d82536 100644 --- a/tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml +++ b/tests/components/mqtt_subscribe/test.esp32-c3-idf.yaml @@ -1,37 +1 @@ -wifi: - ssid: MySSID - password: password1 - -mqtt: - broker: test.mosquitto.org - port: 1883 - discovery: true - discovery_prefix: homeassistant - idf_send_async: false - log_topic: - on_message: - topic: testing/sensor/testing_sensor/state - qos: 0 - then: - - logger.log: Mqtt Test - -sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Sensor - topic: mqtt/topic - id: the_sensor - qos: 2 - on_value: - - mqtt.publish_json: - topic: the/topic - payload: |- - root["key"] = id(the_sensor).state; - root["greeting"] = "Hello World"; - -text_sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Text - topic: "the/topic" - qos: 2 - on_value: - - logger.log: "Text sensor got value" +<<: !include common-idf.yaml diff --git a/tests/components/mqtt_subscribe/test.esp32-idf.yaml b/tests/components/mqtt_subscribe/test.esp32-idf.yaml index 070672f15c..2cb6d82536 100644 --- a/tests/components/mqtt_subscribe/test.esp32-idf.yaml +++ b/tests/components/mqtt_subscribe/test.esp32-idf.yaml @@ -1,37 +1 @@ -wifi: - ssid: MySSID - password: password1 - -mqtt: - broker: test.mosquitto.org - port: 1883 - discovery: true - discovery_prefix: homeassistant - idf_send_async: false - log_topic: - on_message: - topic: testing/sensor/testing_sensor/state - qos: 0 - then: - - logger.log: Mqtt Test - -sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Sensor - topic: mqtt/topic - id: the_sensor - qos: 2 - on_value: - - mqtt.publish_json: - topic: the/topic - payload: |- - root["key"] = id(the_sensor).state; - root["greeting"] = "Hello World"; - -text_sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Text - topic: "the/topic" - qos: 2 - on_value: - - logger.log: "Text sensor got value" +<<: !include common-idf.yaml diff --git a/tests/components/mqtt_subscribe/test.esp8266-ard.yaml b/tests/components/mqtt_subscribe/test.esp8266-ard.yaml index 13ed311b17..28589ee06c 100644 --- a/tests/components/mqtt_subscribe/test.esp8266-ard.yaml +++ b/tests/components/mqtt_subscribe/test.esp8266-ard.yaml @@ -1,36 +1 @@ -wifi: - ssid: MySSID - password: password1 - -mqtt: - broker: test.mosquitto.org - port: 1883 - discovery: true - discovery_prefix: homeassistant - log_topic: - on_message: - topic: testing/sensor/testing_sensor/state - qos: 0 - then: - - logger.log: Mqtt Test - -sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Sensor - topic: mqtt/topic - id: the_sensor - qos: 2 - on_value: - - mqtt.publish_json: - topic: the/topic - payload: |- - root["key"] = id(the_sensor).state; - root["greeting"] = "Hello World"; - -text_sensor: - - platform: mqtt_subscribe - name: MQTT Subscribe Text - topic: "the/topic" - qos: 2 - on_value: - - logger.log: "Text sensor got value" +<<: !include common-ard.yaml diff --git a/tests/components/ms5611/common.yaml b/tests/components/ms5611/common.yaml new file mode 100644 index 0000000000..d0417faa86 --- /dev/null +++ b/tests/components/ms5611/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_ms5611 + scl: ${i2c_scl} + sda: ${i2c_sda} + +sensor: + - platform: ms5611 + temperature: + name: Outside Temperature + pressure: + name: Outside Pressure + address: 0x77 + update_interval: 15s diff --git a/tests/components/ms5611/test.esp32-ard.yaml b/tests/components/ms5611/test.esp32-ard.yaml index b090eeaa93..1037d5d35b 100644 --- a/tests/components/ms5611/test.esp32-ard.yaml +++ b/tests/components/ms5611/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ms5611 - scl: 16 - sda: 17 +substitutions: + i2c_scl: GPIO16 + i2c_sda: GPIO17 -sensor: - - platform: ms5611 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - address: 0x77 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ms5611/test.esp32-c3-ard.yaml b/tests/components/ms5611/test.esp32-c3-ard.yaml index 8f18501eef..d7ae0d5161 100644 --- a/tests/components/ms5611/test.esp32-c3-ard.yaml +++ b/tests/components/ms5611/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ms5611 - scl: 5 - sda: 4 +substitutions: + i2c_scl: GPIO5 + i2c_sda: GPIO4 -sensor: - - platform: ms5611 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - address: 0x77 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ms5611/test.esp32-c3-idf.yaml b/tests/components/ms5611/test.esp32-c3-idf.yaml index 8f18501eef..d7ae0d5161 100644 --- a/tests/components/ms5611/test.esp32-c3-idf.yaml +++ b/tests/components/ms5611/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ms5611 - scl: 5 - sda: 4 +substitutions: + i2c_scl: GPIO5 + i2c_sda: GPIO4 -sensor: - - platform: ms5611 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - address: 0x77 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ms5611/test.esp32-idf.yaml b/tests/components/ms5611/test.esp32-idf.yaml index b090eeaa93..1037d5d35b 100644 --- a/tests/components/ms5611/test.esp32-idf.yaml +++ b/tests/components/ms5611/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ms5611 - scl: 16 - sda: 17 +substitutions: + i2c_scl: GPIO16 + i2c_sda: GPIO17 -sensor: - - platform: ms5611 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - address: 0x77 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ms5611/test.esp8266-ard.yaml b/tests/components/ms5611/test.esp8266-ard.yaml index 8f18501eef..d7ae0d5161 100644 --- a/tests/components/ms5611/test.esp8266-ard.yaml +++ b/tests/components/ms5611/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ms5611 - scl: 5 - sda: 4 +substitutions: + i2c_scl: GPIO5 + i2c_sda: GPIO4 -sensor: - - platform: ms5611 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - address: 0x77 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ms5611/test.rp2040-ard.yaml b/tests/components/ms5611/test.rp2040-ard.yaml index 8f18501eef..d7ae0d5161 100644 --- a/tests/components/ms5611/test.rp2040-ard.yaml +++ b/tests/components/ms5611/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_ms5611 - scl: 5 - sda: 4 +substitutions: + i2c_scl: GPIO5 + i2c_sda: GPIO4 -sensor: - - platform: ms5611 - temperature: - name: Outside Temperature - pressure: - name: Outside Pressure - address: 0x77 - update_interval: 15s +<<: !include common.yaml From 55203143dfb057ff78fcfff6e0cce16e7ce8d391 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 17:06:08 -0600 Subject: [PATCH 0961/1052] [CI] Consolidate some tests (I, J) (#8200) --- tests/components/i2c/common.yaml | 4 ++ tests/components/i2c/test.esp32-ard.yaml | 9 ++-- tests/components/i2c/test.esp32-c3-ard.yaml | 9 ++-- tests/components/i2c/test.esp32-c3-idf.yaml | 9 ++-- tests/components/i2c/test.esp32-idf.yaml | 9 ++-- tests/components/i2c/test.esp8266-ard.yaml | 9 ++-- tests/components/i2c/test.rp2040-ard.yaml | 9 ++-- tests/components/i2c_device/common.yaml | 8 ++++ .../components/i2c_device/test.esp32-ard.yaml | 11 ++--- .../i2c_device/test.esp32-c3-ard.yaml | 11 ++--- .../i2c_device/test.esp32-c3-idf.yaml | 11 ++--- .../components/i2c_device/test.esp32-idf.yaml | 11 ++--- .../i2c_device/test.esp8266-ard.yaml | 11 ++--- .../i2c_device/test.rp2040-ard.yaml | 11 ++--- tests/components/i2s_audio/common.yaml | 4 ++ .../components/i2s_audio/test.esp32-ard.yaml | 10 ++-- .../i2s_audio/test.esp32-c3-ard.yaml | 10 ++-- .../i2s_audio/test.esp32-c3-idf.yaml | 10 ++-- .../components/i2s_audio/test.esp32-idf.yaml | 10 ++-- tests/components/iaqcore/common.yaml | 11 +++++ tests/components/iaqcore/test.esp32-ard.yaml | 14 ++---- .../components/iaqcore/test.esp32-c3-ard.yaml | 14 ++---- .../components/iaqcore/test.esp32-c3-idf.yaml | 14 ++---- tests/components/iaqcore/test.esp32-idf.yaml | 14 ++---- .../components/iaqcore/test.esp8266-ard.yaml | 14 ++---- tests/components/iaqcore/test.rp2040-ard.yaml | 14 ++---- tests/components/ili9xxx/common.yaml | 36 +++++++++++++++ tests/components/ili9xxx/test.esp32-ard.yaml | 45 ++++-------------- .../components/ili9xxx/test.esp32-c3-ard.yaml | 46 +++++-------------- .../components/ili9xxx/test.esp32-c3-idf.yaml | 46 +++++-------------- tests/components/ili9xxx/test.esp32-idf.yaml | 32 ++++--------- .../components/ili9xxx/test.esp8266-ard.yaml | 46 +++++-------------- tests/components/ili9xxx/test.rp2040-ard.yaml | 46 +++++-------------- tests/components/ina219/common.yaml | 20 ++++++++ tests/components/ina219/test.esp32-ard.yaml | 23 ++-------- .../components/ina219/test.esp32-c3-ard.yaml | 23 ++-------- .../components/ina219/test.esp32-c3-idf.yaml | 23 ++-------- tests/components/ina219/test.esp32-idf.yaml | 23 ++-------- tests/components/ina219/test.esp8266-ard.yaml | 23 ++-------- tests/components/ina219/test.rp2040-ard.yaml | 23 ++-------- tests/components/ina226/common.yaml | 19 ++++++++ tests/components/ina226/test.esp32-ard.yaml | 22 ++------- .../components/ina226/test.esp32-c3-ard.yaml | 22 ++------- .../components/ina226/test.esp32-c3-idf.yaml | 22 ++------- tests/components/ina226/test.esp32-idf.yaml | 22 ++------- tests/components/ina226/test.esp8266-ard.yaml | 22 ++------- tests/components/ina226/test.rp2040-ard.yaml | 22 ++------- tests/components/ina260/common.yaml | 15 ++++++ tests/components/ina260/test.esp32-ard.yaml | 18 ++------ .../components/ina260/test.esp32-c3-ard.yaml | 18 ++------ .../components/ina260/test.esp32-c3-idf.yaml | 18 ++------ tests/components/ina260/test.esp32-idf.yaml | 18 ++------ tests/components/ina260/test.esp8266-ard.yaml | 18 ++------ tests/components/ina260/test.rp2040-ard.yaml | 18 ++------ tests/components/ina3221/common.yaml | 19 ++++++++ tests/components/ina3221/test.esp32-ard.yaml | 22 ++------- .../components/ina3221/test.esp32-c3-ard.yaml | 22 ++------- .../components/ina3221/test.esp32-c3-idf.yaml | 22 ++------- tests/components/ina3221/test.esp32-idf.yaml | 22 ++------- .../components/ina3221/test.esp8266-ard.yaml | 22 ++------- tests/components/ina3221/test.rp2040-ard.yaml | 22 ++------- ...st.esp32-s2-ard.yaml => common-esp32.yaml} | 4 +- .../integration/test.esp32-ard.yaml | 13 ++---- .../integration/test.esp32-c3-ard.yaml | 13 ++---- .../integration/test.esp32-c3-idf.yaml | 4 ++ .../integration/test.esp32-idf.yaml | 13 ++---- .../integration/test.esp32-s3-ard.yaml | 9 ---- .../internal_temperature/common.yaml | 3 ++ .../internal_temperature/test.bk72xx-ard.yaml | 4 +- .../internal_temperature/test.esp32-ard.yaml | 4 +- .../test.esp32-c3-ard.yaml | 4 +- .../test.esp32-c3-idf.yaml | 4 +- .../internal_temperature/test.esp32-idf.yaml | 4 +- .../test.esp32-s2-ard.yaml | 4 +- .../test.esp32-s2-idf.yaml | 1 + .../test.esp32-s3-ard.yaml | 4 +- .../test.esp32-s3-idf.yaml | 1 + .../internal_temperature/test.rp2040-ard.yaml | 4 +- tests/components/jsn_sr04t/common.yaml | 11 +++++ .../components/jsn_sr04t/test.esp32-ard.yaml | 17 ++----- .../jsn_sr04t/test.esp32-c3-ard.yaml | 17 ++----- .../jsn_sr04t/test.esp32-c3-idf.yaml | 17 ++----- .../components/jsn_sr04t/test.esp32-idf.yaml | 17 ++----- .../jsn_sr04t/test.esp8266-ard.yaml | 17 ++----- .../components/jsn_sr04t/test.rp2040-ard.yaml | 17 ++----- 85 files changed, 464 insertions(+), 893 deletions(-) create mode 100644 tests/components/i2c/common.yaml create mode 100644 tests/components/i2c_device/common.yaml create mode 100644 tests/components/i2s_audio/common.yaml create mode 100644 tests/components/iaqcore/common.yaml create mode 100644 tests/components/ili9xxx/common.yaml create mode 100644 tests/components/ina219/common.yaml create mode 100644 tests/components/ina226/common.yaml create mode 100644 tests/components/ina260/common.yaml create mode 100644 tests/components/ina3221/common.yaml rename tests/components/integration/{test.esp32-s2-ard.yaml => common-esp32.yaml} (78%) create mode 100644 tests/components/integration/test.esp32-c3-idf.yaml delete mode 100644 tests/components/integration/test.esp32-s3-ard.yaml create mode 100644 tests/components/internal_temperature/common.yaml create mode 100644 tests/components/internal_temperature/test.esp32-s2-idf.yaml create mode 100644 tests/components/internal_temperature/test.esp32-s3-idf.yaml create mode 100644 tests/components/jsn_sr04t/common.yaml diff --git a/tests/components/i2c/common.yaml b/tests/components/i2c/common.yaml new file mode 100644 index 0000000000..d70cd595ad --- /dev/null +++ b/tests/components/i2c/common.yaml @@ -0,0 +1,4 @@ +i2c: + - id: i2c_i2c + scl: ${scl_pin} + sda: ${sda_pin} diff --git a/tests/components/i2c/test.esp32-ard.yaml b/tests/components/i2c/test.esp32-ard.yaml index 19114a9e5d..63c3bd6afd 100644 --- a/tests/components/i2c/test.esp32-ard.yaml +++ b/tests/components/i2c/test.esp32-ard.yaml @@ -1,4 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/i2c/test.esp32-c3-ard.yaml b/tests/components/i2c/test.esp32-c3-ard.yaml index a881438faa..ee2c29ca4e 100644 --- a/tests/components/i2c/test.esp32-c3-ard.yaml +++ b/tests/components/i2c/test.esp32-c3-ard.yaml @@ -1,4 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/i2c/test.esp32-c3-idf.yaml b/tests/components/i2c/test.esp32-c3-idf.yaml index a881438faa..ee2c29ca4e 100644 --- a/tests/components/i2c/test.esp32-c3-idf.yaml +++ b/tests/components/i2c/test.esp32-c3-idf.yaml @@ -1,4 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/i2c/test.esp32-idf.yaml b/tests/components/i2c/test.esp32-idf.yaml index 19114a9e5d..63c3bd6afd 100644 --- a/tests/components/i2c/test.esp32-idf.yaml +++ b/tests/components/i2c/test.esp32-idf.yaml @@ -1,4 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/i2c/test.esp8266-ard.yaml b/tests/components/i2c/test.esp8266-ard.yaml index a881438faa..ee2c29ca4e 100644 --- a/tests/components/i2c/test.esp8266-ard.yaml +++ b/tests/components/i2c/test.esp8266-ard.yaml @@ -1,4 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/i2c/test.rp2040-ard.yaml b/tests/components/i2c/test.rp2040-ard.yaml index a881438faa..ee2c29ca4e 100644 --- a/tests/components/i2c/test.rp2040-ard.yaml +++ b/tests/components/i2c/test.rp2040-ard.yaml @@ -1,4 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/i2c_device/common.yaml b/tests/components/i2c_device/common.yaml new file mode 100644 index 0000000000..35a6f8f7f0 --- /dev/null +++ b/tests/components/i2c_device/common.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_i2c_device + scl: ${scl_pin} + sda: ${sda_pin} + +i2c_device: + id: i2cdev + address: 0x2C diff --git a/tests/components/i2c_device/test.esp32-ard.yaml b/tests/components/i2c_device/test.esp32-ard.yaml index 6169d113f8..63c3bd6afd 100644 --- a/tests/components/i2c_device/test.esp32-ard.yaml +++ b/tests/components/i2c_device/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c_device: - id: i2cdev - address: 0x2C +<<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp32-c3-ard.yaml b/tests/components/i2c_device/test.esp32-c3-ard.yaml index 5d53d12208..ee2c29ca4e 100644 --- a/tests/components/i2c_device/test.esp32-c3-ard.yaml +++ b/tests/components/i2c_device/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c_device: - id: i2cdev - address: 0x2C +<<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp32-c3-idf.yaml b/tests/components/i2c_device/test.esp32-c3-idf.yaml index 5d53d12208..ee2c29ca4e 100644 --- a/tests/components/i2c_device/test.esp32-c3-idf.yaml +++ b/tests/components/i2c_device/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c_device: - id: i2cdev - address: 0x2C +<<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp32-idf.yaml b/tests/components/i2c_device/test.esp32-idf.yaml index 6169d113f8..63c3bd6afd 100644 --- a/tests/components/i2c_device/test.esp32-idf.yaml +++ b/tests/components/i2c_device/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c_device: - id: i2cdev - address: 0x2C +<<: !include common.yaml diff --git a/tests/components/i2c_device/test.esp8266-ard.yaml b/tests/components/i2c_device/test.esp8266-ard.yaml index 5d53d12208..ee2c29ca4e 100644 --- a/tests/components/i2c_device/test.esp8266-ard.yaml +++ b/tests/components/i2c_device/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c_device: - id: i2cdev - address: 0x2C +<<: !include common.yaml diff --git a/tests/components/i2c_device/test.rp2040-ard.yaml b/tests/components/i2c_device/test.rp2040-ard.yaml index 5d53d12208..ee2c29ca4e 100644 --- a/tests/components/i2c_device/test.rp2040-ard.yaml +++ b/tests/components/i2c_device/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c_device: - id: i2cdev - address: 0x2C +<<: !include common.yaml diff --git a/tests/components/i2s_audio/common.yaml b/tests/components/i2s_audio/common.yaml new file mode 100644 index 0000000000..2cecdc93b7 --- /dev/null +++ b/tests/components/i2s_audio/common.yaml @@ -0,0 +1,4 @@ +i2s_audio: + i2s_bclk_pin: ${i2s_bclk_pin} + i2s_lrclk_pin: ${i2s_lrclk_pin} + i2s_mclk_pin: ${i2s_mclk_pin} diff --git a/tests/components/i2s_audio/test.esp32-ard.yaml b/tests/components/i2s_audio/test.esp32-ard.yaml index 938dd5c25f..ce751d7d4a 100644 --- a/tests/components/i2s_audio/test.esp32-ard.yaml +++ b/tests/components/i2s_audio/test.esp32-ard.yaml @@ -1,4 +1,6 @@ -i2s_audio: - i2s_bclk_pin: 27 - i2s_lrclk_pin: 26 - i2s_mclk_pin: 25 +substitutions: + i2s_bclk_pin: GPIO15 + i2s_lrclk_pin: GPIO16 + i2s_mclk_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/i2s_audio/test.esp32-c3-ard.yaml b/tests/components/i2s_audio/test.esp32-c3-ard.yaml index 08cd56b1a7..5490846ae9 100644 --- a/tests/components/i2s_audio/test.esp32-c3-ard.yaml +++ b/tests/components/i2s_audio/test.esp32-c3-ard.yaml @@ -1,4 +1,6 @@ -i2s_audio: - i2s_bclk_pin: 7 - i2s_lrclk_pin: 6 - i2s_mclk_pin: 5 +substitutions: + i2s_bclk_pin: GPIO5 + i2s_lrclk_pin: GPIO6 + i2s_mclk_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/i2s_audio/test.esp32-c3-idf.yaml b/tests/components/i2s_audio/test.esp32-c3-idf.yaml index 08cd56b1a7..5490846ae9 100644 --- a/tests/components/i2s_audio/test.esp32-c3-idf.yaml +++ b/tests/components/i2s_audio/test.esp32-c3-idf.yaml @@ -1,4 +1,6 @@ -i2s_audio: - i2s_bclk_pin: 7 - i2s_lrclk_pin: 6 - i2s_mclk_pin: 5 +substitutions: + i2s_bclk_pin: GPIO5 + i2s_lrclk_pin: GPIO6 + i2s_mclk_pin: GPIO7 + +<<: !include common.yaml diff --git a/tests/components/i2s_audio/test.esp32-idf.yaml b/tests/components/i2s_audio/test.esp32-idf.yaml index 938dd5c25f..ce751d7d4a 100644 --- a/tests/components/i2s_audio/test.esp32-idf.yaml +++ b/tests/components/i2s_audio/test.esp32-idf.yaml @@ -1,4 +1,6 @@ -i2s_audio: - i2s_bclk_pin: 27 - i2s_lrclk_pin: 26 - i2s_mclk_pin: 25 +substitutions: + i2s_bclk_pin: GPIO15 + i2s_lrclk_pin: GPIO16 + i2s_mclk_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/iaqcore/common.yaml b/tests/components/iaqcore/common.yaml new file mode 100644 index 0000000000..927b836c52 --- /dev/null +++ b/tests/components/iaqcore/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_iaqcore + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: iaqcore + co2: + name: iAQ Core CO2 Sensor + tvoc: + name: iAQ Core TVOC Sensor diff --git a/tests/components/iaqcore/test.esp32-ard.yaml b/tests/components/iaqcore/test.esp32-ard.yaml index 26b01dadf9..63c3bd6afd 100644 --- a/tests/components/iaqcore/test.esp32-ard.yaml +++ b/tests/components/iaqcore/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_iaqcore - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: iaqcore - co2: - name: iAQ Core CO2 Sensor - tvoc: - name: iAQ Core TVOC Sensor +<<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp32-c3-ard.yaml b/tests/components/iaqcore/test.esp32-c3-ard.yaml index a1809dffd7..ee2c29ca4e 100644 --- a/tests/components/iaqcore/test.esp32-c3-ard.yaml +++ b/tests/components/iaqcore/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_iaqcore - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: iaqcore - co2: - name: iAQ Core CO2 Sensor - tvoc: - name: iAQ Core TVOC Sensor +<<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp32-c3-idf.yaml b/tests/components/iaqcore/test.esp32-c3-idf.yaml index a1809dffd7..ee2c29ca4e 100644 --- a/tests/components/iaqcore/test.esp32-c3-idf.yaml +++ b/tests/components/iaqcore/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_iaqcore - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: iaqcore - co2: - name: iAQ Core CO2 Sensor - tvoc: - name: iAQ Core TVOC Sensor +<<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp32-idf.yaml b/tests/components/iaqcore/test.esp32-idf.yaml index 26b01dadf9..63c3bd6afd 100644 --- a/tests/components/iaqcore/test.esp32-idf.yaml +++ b/tests/components/iaqcore/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_iaqcore - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: iaqcore - co2: - name: iAQ Core CO2 Sensor - tvoc: - name: iAQ Core TVOC Sensor +<<: !include common.yaml diff --git a/tests/components/iaqcore/test.esp8266-ard.yaml b/tests/components/iaqcore/test.esp8266-ard.yaml index a1809dffd7..ee2c29ca4e 100644 --- a/tests/components/iaqcore/test.esp8266-ard.yaml +++ b/tests/components/iaqcore/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_iaqcore - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: iaqcore - co2: - name: iAQ Core CO2 Sensor - tvoc: - name: iAQ Core TVOC Sensor +<<: !include common.yaml diff --git a/tests/components/iaqcore/test.rp2040-ard.yaml b/tests/components/iaqcore/test.rp2040-ard.yaml index a1809dffd7..ee2c29ca4e 100644 --- a/tests/components/iaqcore/test.rp2040-ard.yaml +++ b/tests/components/iaqcore/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_iaqcore - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: iaqcore - co2: - name: iAQ Core CO2 Sensor - tvoc: - name: iAQ Core TVOC Sensor +<<: !include common.yaml diff --git a/tests/components/ili9xxx/common.yaml b/tests/components/ili9xxx/common.yaml new file mode 100644 index 0000000000..b182d110cd --- /dev/null +++ b/tests/components/ili9xxx/common.yaml @@ -0,0 +1,36 @@ +spi: + - id: spi_main_lcd + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ili9xxx + invert_colors: true + dimensions: 320x240 + transform: + swap_xy: true + mirror_x: true + mirror_y: false + model: TFT 2.4 + color_palette: GRAYSCALE + cs_pin: ${cs_pin1} + dc_pin: ${dc_pin1} + reset_pin: ${reset_pin1} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: ili9xxx + invert_colors: false + color_palette: 8bit + dimensions: + width: 320 + height: 240 + offset_width: 20 + offset_height: 10 + model: TFT 2.4 + cs_pin: ${cs_pin2} + dc_pin: ${dc_pin2} + reset_pin: ${reset_pin2} + auto_clear_enabled: false + rotation: 90 + lambda: |- + it.fill(Color::WHITE); diff --git a/tests/components/ili9xxx/test.esp32-ard.yaml b/tests/components/ili9xxx/test.esp32-ard.yaml index c00c38ce3e..2e006d2521 100644 --- a/tests/components/ili9xxx/test.esp32-ard.yaml +++ b/tests/components/ili9xxx/test.esp32-ard.yaml @@ -1,36 +1,11 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin1: GPIO12 + dc_pin1: GPIO13 + reset_pin1: GPIO14 + cs_pin2: GPIO25 + dc_pin2: GPIO26 + reset_pin2: GPIO27 -display: - - platform: ili9xxx - invert_colors: true - dimensions: 320x240 - transform: - swap_xy: true - mirror_x: true - mirror_y: false - model: TFT 2.4 - color_palette: GRAYSCALE - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: ili9xxx - invert_colors: false - color_palette: 8bit - dimensions: - width: 320 - height: 240 - offset_width: 20 - offset_height: 10 - model: TFT 2.4 - cs_pin: 25 - dc_pin: 26 - reset_pin: 27 - auto_clear_enabled: false - rotation: 90 - lambda: |- - it.fill(Color::WHITE); +<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp32-c3-ard.yaml b/tests/components/ili9xxx/test.esp32-c3-ard.yaml index fd03bd54b7..3037785e81 100644 --- a/tests/components/ili9xxx/test.esp32-c3-ard.yaml +++ b/tests/components/ili9xxx/test.esp32-c3-ard.yaml @@ -1,36 +1,12 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin1: GPIO8 + dc_pin1: GPIO9 + reset_pin1: GPIO10 + cs_pin2: GPIO2 + dc_pin2: GPIO3 + reset_pin2: GPIO4 -display: - - platform: ili9xxx - invert_colors: true - dimensions: 320x240 - transform: - swap_xy: true - mirror_x: true - mirror_y: false - model: TFT 2.4 - color_palette: GRAYSCALE - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: ili9xxx - invert_colors: false - dimensions: - width: 320 - height: 240 - offset_width: 20 - offset_height: 10 - model: TFT 2.4 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - auto_clear_enabled: false - rotation: 90 - lambda: |- - it.fill(Color::WHITE); +<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp32-c3-idf.yaml b/tests/components/ili9xxx/test.esp32-c3-idf.yaml index fd03bd54b7..3037785e81 100644 --- a/tests/components/ili9xxx/test.esp32-c3-idf.yaml +++ b/tests/components/ili9xxx/test.esp32-c3-idf.yaml @@ -1,36 +1,12 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin1: GPIO8 + dc_pin1: GPIO9 + reset_pin1: GPIO10 + cs_pin2: GPIO2 + dc_pin2: GPIO3 + reset_pin2: GPIO4 -display: - - platform: ili9xxx - invert_colors: true - dimensions: 320x240 - transform: - swap_xy: true - mirror_x: true - mirror_y: false - model: TFT 2.4 - color_palette: GRAYSCALE - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: ili9xxx - invert_colors: false - dimensions: - width: 320 - height: 240 - offset_width: 20 - offset_height: 10 - model: TFT 2.4 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - auto_clear_enabled: false - rotation: 90 - lambda: |- - it.fill(Color::WHITE); +<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp32-idf.yaml b/tests/components/ili9xxx/test.esp32-idf.yaml index 6da62f69d2..2e006d2521 100644 --- a/tests/components/ili9xxx/test.esp32-idf.yaml +++ b/tests/components/ili9xxx/test.esp32-idf.yaml @@ -1,23 +1,11 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin1: GPIO12 + dc_pin1: GPIO13 + reset_pin1: GPIO14 + cs_pin2: GPIO25 + dc_pin2: GPIO26 + reset_pin2: GPIO27 -display: - - platform: ili9xxx - invert_colors: true - dimensions: 320x240 - transform: - swap_xy: true - mirror_x: true - mirror_y: false - model: custom - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - init_sequence: - - [0xFF, 0x77, 0x01, 0x00, 0x00, 0x10] - - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.esp8266-ard.yaml b/tests/components/ili9xxx/test.esp8266-ard.yaml index b8192e69d1..5dee055fd4 100644 --- a/tests/components/ili9xxx/test.esp8266-ard.yaml +++ b/tests/components/ili9xxx/test.esp8266-ard.yaml @@ -1,36 +1,12 @@ -spi: - - id: spi_main_lcd - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin1: GPIO5 + dc_pin1: GPIO15 + reset_pin1: GPIO16 + cs_pin2: GPIO2 + dc_pin2: GPIO4 + reset_pin2: GPIO0 -display: - - platform: ili9xxx - invert_colors: true - dimensions: 320x240 - transform: - swap_xy: true - mirror_x: true - mirror_y: false - model: TFT 2.4 - color_palette: GRAYSCALE - cs_pin: 5 - dc_pin: 15 - reset_pin: 16 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: ili9xxx - invert_colors: false - dimensions: - width: 320 - height: 240 - offset_width: 20 - offset_height: 10 - model: TFT 2.4 - cs_pin: 2 - dc_pin: 4 - reset_pin: 0 - auto_clear_enabled: false - rotation: 90 - lambda: |- - it.fill(Color::WHITE); +<<: !include common.yaml diff --git a/tests/components/ili9xxx/test.rp2040-ard.yaml b/tests/components/ili9xxx/test.rp2040-ard.yaml index 0423f41a1c..74c9b906e9 100644 --- a/tests/components/ili9xxx/test.rp2040-ard.yaml +++ b/tests/components/ili9xxx/test.rp2040-ard.yaml @@ -1,36 +1,12 @@ -spi: - - id: spi_main_lcd - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin1: GPIO5 + dc_pin1: GPIO15 + reset_pin1: GPIO16 + cs_pin2: GPIO20 + dc_pin2: GPIO21 + reset_pin2: GPIO22 -display: - - platform: ili9xxx - invert_colors: true - dimensions: 320x240 - transform: - swap_xy: true - mirror_x: true - mirror_y: false - model: TFT 2.4 - color_palette: GRAYSCALE - cs_pin: 5 - dc_pin: 15 - reset_pin: 16 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: ili9xxx - invert_colors: false - dimensions: - width: 320 - height: 240 - offset_width: 20 - offset_height: 10 - model: TFT 2.4 - cs_pin: 20 - dc_pin: 21 - reset_pin: 22 - auto_clear_enabled: false - rotation: 90 - lambda: |- - it.fill(Color::WHITE); +<<: !include common.yaml diff --git a/tests/components/ina219/common.yaml b/tests/components/ina219/common.yaml new file mode 100644 index 0000000000..4ca4c9ed8f --- /dev/null +++ b/tests/components/ina219/common.yaml @@ -0,0 +1,20 @@ +i2c: + - id: i2c_ina219 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ina219 + address: 0x40 + shunt_resistance: 0.1 ohm + current: + name: INA219 Current + power: + name: INA219 Power + bus_voltage: + name: INA219 Bus Voltage + shunt_voltage: + name: INA219 Shunt Voltage + max_voltage: 32.0V + max_current: 3.2A + update_interval: 15s diff --git a/tests/components/ina219/test.esp32-ard.yaml b/tests/components/ina219/test.esp32-ard.yaml index affbec67c4..63c3bd6afd 100644 --- a/tests/components/ina219/test.esp32-ard.yaml +++ b/tests/components/ina219/test.esp32-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ina219 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina219 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA219 Current - power: - name: INA219 Power - bus_voltage: - name: INA219 Bus Voltage - shunt_voltage: - name: INA219 Shunt Voltage - max_voltage: 32.0V - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina219/test.esp32-c3-ard.yaml b/tests/components/ina219/test.esp32-c3-ard.yaml index 586add9d16..ee2c29ca4e 100644 --- a/tests/components/ina219/test.esp32-c3-ard.yaml +++ b/tests/components/ina219/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ina219 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina219 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA219 Current - power: - name: INA219 Power - bus_voltage: - name: INA219 Bus Voltage - shunt_voltage: - name: INA219 Shunt Voltage - max_voltage: 32.0V - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina219/test.esp32-c3-idf.yaml b/tests/components/ina219/test.esp32-c3-idf.yaml index 586add9d16..ee2c29ca4e 100644 --- a/tests/components/ina219/test.esp32-c3-idf.yaml +++ b/tests/components/ina219/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ina219 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina219 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA219 Current - power: - name: INA219 Power - bus_voltage: - name: INA219 Bus Voltage - shunt_voltage: - name: INA219 Shunt Voltage - max_voltage: 32.0V - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina219/test.esp32-idf.yaml b/tests/components/ina219/test.esp32-idf.yaml index affbec67c4..63c3bd6afd 100644 --- a/tests/components/ina219/test.esp32-idf.yaml +++ b/tests/components/ina219/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ina219 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina219 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA219 Current - power: - name: INA219 Power - bus_voltage: - name: INA219 Bus Voltage - shunt_voltage: - name: INA219 Shunt Voltage - max_voltage: 32.0V - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina219/test.esp8266-ard.yaml b/tests/components/ina219/test.esp8266-ard.yaml index 586add9d16..ee2c29ca4e 100644 --- a/tests/components/ina219/test.esp8266-ard.yaml +++ b/tests/components/ina219/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ina219 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina219 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA219 Current - power: - name: INA219 Power - bus_voltage: - name: INA219 Bus Voltage - shunt_voltage: - name: INA219 Shunt Voltage - max_voltage: 32.0V - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina219/test.rp2040-ard.yaml b/tests/components/ina219/test.rp2040-ard.yaml index 586add9d16..ee2c29ca4e 100644 --- a/tests/components/ina219/test.rp2040-ard.yaml +++ b/tests/components/ina219/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_ina219 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina219 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA219 Current - power: - name: INA219 Power - bus_voltage: - name: INA219 Bus Voltage - shunt_voltage: - name: INA219 Shunt Voltage - max_voltage: 32.0V - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina226/common.yaml b/tests/components/ina226/common.yaml new file mode 100644 index 0000000000..8bcd038e50 --- /dev/null +++ b/tests/components/ina226/common.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_ina226 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ina226 + address: 0x40 + shunt_resistance: 0.1 ohm + current: + name: INA226 Current + power: + name: INA226 Power + bus_voltage: + name: INA226 Bus Voltage + shunt_voltage: + name: INA226 Shunt Voltage + max_current: 3.2A + update_interval: 15s diff --git a/tests/components/ina226/test.esp32-ard.yaml b/tests/components/ina226/test.esp32-ard.yaml index feab5e146c..63c3bd6afd 100644 --- a/tests/components/ina226/test.esp32-ard.yaml +++ b/tests/components/ina226/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina226 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina226 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA226 Current - power: - name: INA226 Power - bus_voltage: - name: INA226 Bus Voltage - shunt_voltage: - name: INA226 Shunt Voltage - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina226/test.esp32-c3-ard.yaml b/tests/components/ina226/test.esp32-c3-ard.yaml index 6581763294..ee2c29ca4e 100644 --- a/tests/components/ina226/test.esp32-c3-ard.yaml +++ b/tests/components/ina226/test.esp32-c3-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina226 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina226 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA226 Current - power: - name: INA226 Power - bus_voltage: - name: INA226 Bus Voltage - shunt_voltage: - name: INA226 Shunt Voltage - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina226/test.esp32-c3-idf.yaml b/tests/components/ina226/test.esp32-c3-idf.yaml index 6581763294..ee2c29ca4e 100644 --- a/tests/components/ina226/test.esp32-c3-idf.yaml +++ b/tests/components/ina226/test.esp32-c3-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina226 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina226 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA226 Current - power: - name: INA226 Power - bus_voltage: - name: INA226 Bus Voltage - shunt_voltage: - name: INA226 Shunt Voltage - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina226/test.esp32-idf.yaml b/tests/components/ina226/test.esp32-idf.yaml index feab5e146c..63c3bd6afd 100644 --- a/tests/components/ina226/test.esp32-idf.yaml +++ b/tests/components/ina226/test.esp32-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina226 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina226 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA226 Current - power: - name: INA226 Power - bus_voltage: - name: INA226 Bus Voltage - shunt_voltage: - name: INA226 Shunt Voltage - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina226/test.esp8266-ard.yaml b/tests/components/ina226/test.esp8266-ard.yaml index 6581763294..ee2c29ca4e 100644 --- a/tests/components/ina226/test.esp8266-ard.yaml +++ b/tests/components/ina226/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina226 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina226 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA226 Current - power: - name: INA226 Power - bus_voltage: - name: INA226 Bus Voltage - shunt_voltage: - name: INA226 Shunt Voltage - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina226/test.rp2040-ard.yaml b/tests/components/ina226/test.rp2040-ard.yaml index 6581763294..ee2c29ca4e 100644 --- a/tests/components/ina226/test.rp2040-ard.yaml +++ b/tests/components/ina226/test.rp2040-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina226 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina226 - address: 0x40 - shunt_resistance: 0.1 ohm - current: - name: INA226 Current - power: - name: INA226 Power - bus_voltage: - name: INA226 Bus Voltage - shunt_voltage: - name: INA226 Shunt Voltage - max_current: 3.2A - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina260/common.yaml b/tests/components/ina260/common.yaml new file mode 100644 index 0000000000..ab94b2e509 --- /dev/null +++ b/tests/components/ina260/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_ina260 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ina260 + address: 0x40 + current: + name: INA260 Current + power: + name: INA260 Power + bus_voltage: + name: INA260 Voltage + update_interval: 60s diff --git a/tests/components/ina260/test.esp32-ard.yaml b/tests/components/ina260/test.esp32-ard.yaml index be6cf73bff..63c3bd6afd 100644 --- a/tests/components/ina260/test.esp32-ard.yaml +++ b/tests/components/ina260/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_ina260 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina260 - address: 0x40 - current: - name: INA260 Current - power: - name: INA260 Power - bus_voltage: - name: INA260 Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ina260/test.esp32-c3-ard.yaml b/tests/components/ina260/test.esp32-c3-ard.yaml index a1da63351d..ee2c29ca4e 100644 --- a/tests/components/ina260/test.esp32-c3-ard.yaml +++ b/tests/components/ina260/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_ina260 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina260 - address: 0x40 - current: - name: INA260 Current - power: - name: INA260 Power - bus_voltage: - name: INA260 Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ina260/test.esp32-c3-idf.yaml b/tests/components/ina260/test.esp32-c3-idf.yaml index a1da63351d..ee2c29ca4e 100644 --- a/tests/components/ina260/test.esp32-c3-idf.yaml +++ b/tests/components/ina260/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_ina260 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina260 - address: 0x40 - current: - name: INA260 Current - power: - name: INA260 Power - bus_voltage: - name: INA260 Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ina260/test.esp32-idf.yaml b/tests/components/ina260/test.esp32-idf.yaml index be6cf73bff..63c3bd6afd 100644 --- a/tests/components/ina260/test.esp32-idf.yaml +++ b/tests/components/ina260/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_ina260 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina260 - address: 0x40 - current: - name: INA260 Current - power: - name: INA260 Power - bus_voltage: - name: INA260 Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ina260/test.esp8266-ard.yaml b/tests/components/ina260/test.esp8266-ard.yaml index a1da63351d..ee2c29ca4e 100644 --- a/tests/components/ina260/test.esp8266-ard.yaml +++ b/tests/components/ina260/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_ina260 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina260 - address: 0x40 - current: - name: INA260 Current - power: - name: INA260 Power - bus_voltage: - name: INA260 Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ina260/test.rp2040-ard.yaml b/tests/components/ina260/test.rp2040-ard.yaml index a1da63351d..ee2c29ca4e 100644 --- a/tests/components/ina260/test.rp2040-ard.yaml +++ b/tests/components/ina260/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_ina260 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina260 - address: 0x40 - current: - name: INA260 Current - power: - name: INA260 Power - bus_voltage: - name: INA260 Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/ina3221/common.yaml b/tests/components/ina3221/common.yaml new file mode 100644 index 0000000000..ba1abdfe3a --- /dev/null +++ b/tests/components/ina3221/common.yaml @@ -0,0 +1,19 @@ +i2c: + - id: i2c_ina3221 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: ina3221 + address: 0x40 + channel_1: + shunt_resistance: 0.1 ohm + current: + name: INA3221 Channel 1 Current + power: + name: INA3221 Channel 1 Power + bus_voltage: + name: INA3221 Channel 1 Bus Voltage + shunt_voltage: + name: INA3221 Channel 1 Shunt Voltage + update_interval: 15s diff --git a/tests/components/ina3221/test.esp32-ard.yaml b/tests/components/ina3221/test.esp32-ard.yaml index ad9cf79e38..63c3bd6afd 100644 --- a/tests/components/ina3221/test.esp32-ard.yaml +++ b/tests/components/ina3221/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina3221 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina3221 - address: 0x40 - channel_1: - shunt_resistance: 0.1 ohm - current: - name: INA3221 Channel 1 Current - power: - name: INA3221 Channel 1 Power - bus_voltage: - name: INA3221 Channel 1 Bus Voltage - shunt_voltage: - name: INA3221 Channel 1 Shunt Voltage - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina3221/test.esp32-c3-ard.yaml b/tests/components/ina3221/test.esp32-c3-ard.yaml index 55990871a0..ee2c29ca4e 100644 --- a/tests/components/ina3221/test.esp32-c3-ard.yaml +++ b/tests/components/ina3221/test.esp32-c3-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina3221 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina3221 - address: 0x40 - channel_1: - shunt_resistance: 0.1 ohm - current: - name: INA3221 Channel 1 Current - power: - name: INA3221 Channel 1 Power - bus_voltage: - name: INA3221 Channel 1 Bus Voltage - shunt_voltage: - name: INA3221 Channel 1 Shunt Voltage - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina3221/test.esp32-c3-idf.yaml b/tests/components/ina3221/test.esp32-c3-idf.yaml index 55990871a0..ee2c29ca4e 100644 --- a/tests/components/ina3221/test.esp32-c3-idf.yaml +++ b/tests/components/ina3221/test.esp32-c3-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina3221 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina3221 - address: 0x40 - channel_1: - shunt_resistance: 0.1 ohm - current: - name: INA3221 Channel 1 Current - power: - name: INA3221 Channel 1 Power - bus_voltage: - name: INA3221 Channel 1 Bus Voltage - shunt_voltage: - name: INA3221 Channel 1 Shunt Voltage - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina3221/test.esp32-idf.yaml b/tests/components/ina3221/test.esp32-idf.yaml index ad9cf79e38..63c3bd6afd 100644 --- a/tests/components/ina3221/test.esp32-idf.yaml +++ b/tests/components/ina3221/test.esp32-idf.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina3221 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: ina3221 - address: 0x40 - channel_1: - shunt_resistance: 0.1 ohm - current: - name: INA3221 Channel 1 Current - power: - name: INA3221 Channel 1 Power - bus_voltage: - name: INA3221 Channel 1 Bus Voltage - shunt_voltage: - name: INA3221 Channel 1 Shunt Voltage - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina3221/test.esp8266-ard.yaml b/tests/components/ina3221/test.esp8266-ard.yaml index 55990871a0..ee2c29ca4e 100644 --- a/tests/components/ina3221/test.esp8266-ard.yaml +++ b/tests/components/ina3221/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina3221 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina3221 - address: 0x40 - channel_1: - shunt_resistance: 0.1 ohm - current: - name: INA3221 Channel 1 Current - power: - name: INA3221 Channel 1 Power - bus_voltage: - name: INA3221 Channel 1 Bus Voltage - shunt_voltage: - name: INA3221 Channel 1 Shunt Voltage - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/ina3221/test.rp2040-ard.yaml b/tests/components/ina3221/test.rp2040-ard.yaml index 55990871a0..ee2c29ca4e 100644 --- a/tests/components/ina3221/test.rp2040-ard.yaml +++ b/tests/components/ina3221/test.rp2040-ard.yaml @@ -1,19 +1,5 @@ -i2c: - - id: i2c_ina3221 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: ina3221 - address: 0x40 - channel_1: - shunt_resistance: 0.1 ohm - current: - name: INA3221 Channel 1 Current - power: - name: INA3221 Channel 1 Power - bus_voltage: - name: INA3221 Channel 1 Bus Voltage - shunt_voltage: - name: INA3221 Channel 1 Shunt Voltage - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/integration/test.esp32-s2-ard.yaml b/tests/components/integration/common-esp32.yaml similarity index 78% rename from tests/components/integration/test.esp32-s2-ard.yaml rename to tests/components/integration/common-esp32.yaml index 1415952571..248106fd60 100644 --- a/tests/components/integration/test.esp32-s2-ard.yaml +++ b/tests/components/integration/common-esp32.yaml @@ -1,8 +1,8 @@ sensor: - platform: adc id: my_sensor - pin: 1 - attenuation: 11db + pin: ${pin} + attenuation: 12db - platform: integration sensor: my_sensor name: Integration Sensor diff --git a/tests/components/integration/test.esp32-ard.yaml b/tests/components/integration/test.esp32-ard.yaml index 0095fdb1ff..f84495e521 100644 --- a/tests/components/integration/test.esp32-ard.yaml +++ b/tests/components/integration/test.esp32-ard.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: A0 - attenuation: 2.5db - - platform: integration - sensor: my_sensor - name: Integration Sensor - time_unit: s +substitutions: + pin: A0 + +<<: !include common-esp32.yaml diff --git a/tests/components/integration/test.esp32-c3-ard.yaml b/tests/components/integration/test.esp32-c3-ard.yaml index b68cb9f87d..5105e645f3 100644 --- a/tests/components/integration/test.esp32-c3-ard.yaml +++ b/tests/components/integration/test.esp32-c3-ard.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 4 - attenuation: 11db - - platform: integration - sensor: my_sensor - name: Integration Sensor - time_unit: s +substitutions: + pin: GPIO1 + +<<: !include common-esp32.yaml diff --git a/tests/components/integration/test.esp32-c3-idf.yaml b/tests/components/integration/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..5105e645f3 --- /dev/null +++ b/tests/components/integration/test.esp32-c3-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO1 + +<<: !include common-esp32.yaml diff --git a/tests/components/integration/test.esp32-idf.yaml b/tests/components/integration/test.esp32-idf.yaml index 0095fdb1ff..f84495e521 100644 --- a/tests/components/integration/test.esp32-idf.yaml +++ b/tests/components/integration/test.esp32-idf.yaml @@ -1,9 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: A0 - attenuation: 2.5db - - platform: integration - sensor: my_sensor - name: Integration Sensor - time_unit: s +substitutions: + pin: A0 + +<<: !include common-esp32.yaml diff --git a/tests/components/integration/test.esp32-s3-ard.yaml b/tests/components/integration/test.esp32-s3-ard.yaml deleted file mode 100644 index 1415952571..0000000000 --- a/tests/components/integration/test.esp32-s3-ard.yaml +++ /dev/null @@ -1,9 +0,0 @@ -sensor: - - platform: adc - id: my_sensor - pin: 1 - attenuation: 11db - - platform: integration - sensor: my_sensor - name: Integration Sensor - time_unit: s diff --git a/tests/components/internal_temperature/common.yaml b/tests/components/internal_temperature/common.yaml new file mode 100644 index 0000000000..19f740339d --- /dev/null +++ b/tests/components/internal_temperature/common.yaml @@ -0,0 +1,3 @@ +sensor: + - platform: internal_temperature + name: Internal Temperature diff --git a/tests/components/internal_temperature/test.bk72xx-ard.yaml b/tests/components/internal_temperature/test.bk72xx-ard.yaml index 28df4a6d9f..dade44d145 100644 --- a/tests/components/internal_temperature/test.bk72xx-ard.yaml +++ b/tests/components/internal_temperature/test.bk72xx-ard.yaml @@ -1,3 +1 @@ -sensor: - - platform: internal_temperature - name: "Internal Temperature" +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-ard.yaml b/tests/components/internal_temperature/test.esp32-ard.yaml index 19f740339d..dade44d145 100644 --- a/tests/components/internal_temperature/test.esp32-ard.yaml +++ b/tests/components/internal_temperature/test.esp32-ard.yaml @@ -1,3 +1 @@ -sensor: - - platform: internal_temperature - name: Internal Temperature +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-c3-ard.yaml b/tests/components/internal_temperature/test.esp32-c3-ard.yaml index 19f740339d..dade44d145 100644 --- a/tests/components/internal_temperature/test.esp32-c3-ard.yaml +++ b/tests/components/internal_temperature/test.esp32-c3-ard.yaml @@ -1,3 +1 @@ -sensor: - - platform: internal_temperature - name: Internal Temperature +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-c3-idf.yaml b/tests/components/internal_temperature/test.esp32-c3-idf.yaml index 28df4a6d9f..dade44d145 100644 --- a/tests/components/internal_temperature/test.esp32-c3-idf.yaml +++ b/tests/components/internal_temperature/test.esp32-c3-idf.yaml @@ -1,3 +1 @@ -sensor: - - platform: internal_temperature - name: "Internal Temperature" +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-idf.yaml b/tests/components/internal_temperature/test.esp32-idf.yaml index 19f740339d..dade44d145 100644 --- a/tests/components/internal_temperature/test.esp32-idf.yaml +++ b/tests/components/internal_temperature/test.esp32-idf.yaml @@ -1,3 +1 @@ -sensor: - - platform: internal_temperature - name: Internal Temperature +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-s2-ard.yaml b/tests/components/internal_temperature/test.esp32-s2-ard.yaml index 19f740339d..dade44d145 100644 --- a/tests/components/internal_temperature/test.esp32-s2-ard.yaml +++ b/tests/components/internal_temperature/test.esp32-s2-ard.yaml @@ -1,3 +1 @@ -sensor: - - platform: internal_temperature - name: Internal Temperature +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-s2-idf.yaml b/tests/components/internal_temperature/test.esp32-s2-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/internal_temperature/test.esp32-s2-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.esp32-s3-ard.yaml b/tests/components/internal_temperature/test.esp32-s3-ard.yaml index 9eb1ec0b0f..bdd704756c 100644 --- a/tests/components/internal_temperature/test.esp32-s3-ard.yaml +++ b/tests/components/internal_temperature/test.esp32-s3-ard.yaml @@ -1,6 +1,4 @@ -sensor: - - platform: internal_temperature - name: Internal Temperature +<<: !include common.yaml esp32: framework: diff --git a/tests/components/internal_temperature/test.esp32-s3-idf.yaml b/tests/components/internal_temperature/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/internal_temperature/test.esp32-s3-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/internal_temperature/test.rp2040-ard.yaml b/tests/components/internal_temperature/test.rp2040-ard.yaml index 19f740339d..dade44d145 100644 --- a/tests/components/internal_temperature/test.rp2040-ard.yaml +++ b/tests/components/internal_temperature/test.rp2040-ard.yaml @@ -1,3 +1 @@ -sensor: - - platform: internal_temperature - name: Internal Temperature +<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/common.yaml b/tests/components/jsn_sr04t/common.yaml new file mode 100644 index 0000000000..d6871d5e91 --- /dev/null +++ b/tests/components/jsn_sr04t/common.yaml @@ -0,0 +1,11 @@ +uart: + - id: uart_jsn_sr04t + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: jsn_sr04t + id: jsn_sr04t_sensor + name: jsn_sr04t Distance + update_interval: 1s diff --git a/tests/components/jsn_sr04t/test.esp32-ard.yaml b/tests/components/jsn_sr04t/test.esp32-ard.yaml index 32b4221b3f..f486544afa 100644 --- a/tests/components/jsn_sr04t/test.esp32-ard.yaml +++ b/tests/components/jsn_sr04t/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_jsn_sr04t - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: jsn_sr04t - id: jsn_sr04t_sensor - name: "jsn_sr04t Distance" - uart_id: uart_jsn_sr04t - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp32-c3-ard.yaml b/tests/components/jsn_sr04t/test.esp32-c3-ard.yaml index 5a37418a7d..b516342f3b 100644 --- a/tests/components/jsn_sr04t/test.esp32-c3-ard.yaml +++ b/tests/components/jsn_sr04t/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_jsn_sr04t - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: jsn_sr04t - id: jsn_sr04t_sensor - name: "jsn_sr04t Distance" - uart_id: uart_jsn_sr04t - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml b/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml index 5a37418a7d..b516342f3b 100644 --- a/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml +++ b/tests/components/jsn_sr04t/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_jsn_sr04t - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: jsn_sr04t - id: jsn_sr04t_sensor - name: "jsn_sr04t Distance" - uart_id: uart_jsn_sr04t - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp32-idf.yaml b/tests/components/jsn_sr04t/test.esp32-idf.yaml index 32b4221b3f..f486544afa 100644 --- a/tests/components/jsn_sr04t/test.esp32-idf.yaml +++ b/tests/components/jsn_sr04t/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_jsn_sr04t - tx_pin: - number: 17 - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: jsn_sr04t - id: jsn_sr04t_sensor - name: "jsn_sr04t Distance" - uart_id: uart_jsn_sr04t - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.esp8266-ard.yaml b/tests/components/jsn_sr04t/test.esp8266-ard.yaml index 5a37418a7d..b516342f3b 100644 --- a/tests/components/jsn_sr04t/test.esp8266-ard.yaml +++ b/tests/components/jsn_sr04t/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_jsn_sr04t - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: jsn_sr04t - id: jsn_sr04t_sensor - name: "jsn_sr04t Distance" - uart_id: uart_jsn_sr04t - update_interval: 1s +<<: !include common.yaml diff --git a/tests/components/jsn_sr04t/test.rp2040-ard.yaml b/tests/components/jsn_sr04t/test.rp2040-ard.yaml index 5a37418a7d..b516342f3b 100644 --- a/tests/components/jsn_sr04t/test.rp2040-ard.yaml +++ b/tests/components/jsn_sr04t/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_jsn_sr04t - tx_pin: - number: 4 - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: jsn_sr04t - id: jsn_sr04t_sensor - name: "jsn_sr04t Distance" - uart_id: uart_jsn_sr04t - update_interval: 1s +<<: !include common.yaml From 2e61229aed75f8db4d3f7e4d36f483e0ed70318b Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:09:37 -0500 Subject: [PATCH 0962/1052] [i2c] Workaround for i2c on s2 (#8188) --- esphome/components/i2c/i2c_bus_esp_idf.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/i2c/i2c_bus_esp_idf.cpp b/esphome/components/i2c/i2c_bus_esp_idf.cpp index ac3a754024..c7bba70582 100644 --- a/esphome/components/i2c/i2c_bus_esp_idf.cpp +++ b/esphome/components/i2c/i2c_bus_esp_idf.cpp @@ -39,6 +39,10 @@ void IDFI2CBus::setup() { conf.scl_io_num = scl_pin_; conf.scl_pullup_en = scl_pullup_enabled_; conf.master.clk_speed = frequency_; +#ifdef USE_ESP32_VARIANT_ESP32S2 + // workaround for https://github.com/esphome/issues/issues/6718 + conf.clk_flags = I2C_SCLK_SRC_FLAG_AWARE_DFS; +#endif esp_err_t err = i2c_param_config(port_, &conf); if (err != ESP_OK) { ESP_LOGW(TAG, "i2c_param_config failed: %s", esp_err_to_name(err)); From 9b56f9cc6da5216ea342a80e0976d806350e0314 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 5 Feb 2025 10:13:21 +1100 Subject: [PATCH 0963/1052] [lvgl] add triggers for swipe gestures (#8190) --- esphome/components/lvgl/__init__.py | 13 ++++++++--- esphome/components/lvgl/defines.py | 7 +++++- esphome/components/lvgl/schemas.py | 5 ++--- esphome/components/lvgl/trigger.py | 30 ++++++++++++++++++++++--- tests/components/lvgl/lvgl-package.yaml | 12 ++++++++++ 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index c64ffcb5f2..07f5d94543 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -61,7 +61,14 @@ from .types import ( lv_style_t, lvgl_ns, ) -from .widgets import Widget, add_widgets, get_scr_act, set_obj_properties, styles_used +from .widgets import ( + LvScrActType, + Widget, + add_widgets, + get_scr_act, + set_obj_properties, + styles_used, +) from .widgets.animimg import animimg_spec from .widgets.arc import arc_spec from .widgets.button import button_spec @@ -318,7 +325,7 @@ async def to_code(configs): config[df.CONF_RESUME_ON_INPUT], ) await cg.register_component(lv_component, config) - Widget.create(config[CONF_ID], lv_component, obj_spec, config) + Widget.create(config[CONF_ID], lv_component, LvScrActType(), config) lv_scr_act = get_scr_act(lv_component) async with LvContext(): @@ -391,7 +398,7 @@ FINAL_VALIDATE_SCHEMA = final_validation LVGL_SCHEMA = ( cv.polling_component_schema("1s") - .extend(obj_schema(obj_spec)) + .extend(obj_schema(LvScrActType())) .extend( { cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent), diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 733a6bc180..119a358e1d 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -146,6 +146,8 @@ TYPE_FLEX = "flex" TYPE_GRID = "grid" TYPE_NONE = "none" +DIRECTIONS = LvConstant("LV_DIR_", "LEFT", "RIGHT", "BOTTOM", "TOP") + LV_FONTS = list(f"montserrat_{s}" for s in range(8, 50, 2)) + [ "dejavu_16_persian_hebrew", "simsun_16_cjk", @@ -169,9 +171,13 @@ LV_EVENT_MAP = { "CANCEL": "CANCEL", "ALL_EVENTS": "ALL", "CHANGE": "VALUE_CHANGED", + "GESTURE": "GESTURE", } LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP) +SWIPE_TRIGGERS = tuple( + f"on_swipe_{x.lower()}" for x in DIRECTIONS.choices + ("up", "down") +) LV_ANIM = LvConstant( @@ -250,7 +256,6 @@ KEYBOARD_MODES = LvConstant( "NUMBER", ) ROLLER_MODES = LvConstant("LV_ROLLER_MODE_", "NORMAL", "INFINITE") -DIRECTIONS = LvConstant("LV_DIR_", "LEFT", "RIGHT", "BOTTOM", "TOP") TILE_DIRECTIONS = DIRECTIONS.extend("HOR", "VER", "ALL") CHILD_ALIGNMENTS = LvConstant( "LV_ALIGN_", diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index f0318dd17a..ae50d5b2e1 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -211,10 +211,9 @@ def part_schema(parts): def automation_schema(typ: LvType): + events = df.LV_EVENT_TRIGGERS + df.SWIPE_TRIGGERS if typ.has_on_value: - events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,) - else: - events = df.LV_EVENT_TRIGGERS + events = events + (CONF_ON_VALUE,) args = typ.get_arg_type() if isinstance(typ, LvType) else [] args.append(lv_event_t_ptr) return { diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py index fb856df04e..b76f90fecd 100644 --- a/esphome/components/lvgl/trigger.py +++ b/esphome/components/lvgl/trigger.py @@ -7,8 +7,10 @@ from .defines import ( CONF_ALIGN_TO, CONF_X, CONF_Y, + DIRECTIONS, LV_EVENT_MAP, LV_EVENT_TRIGGERS, + SWIPE_TRIGGERS, literal, ) from .lvcode import ( @@ -23,7 +25,7 @@ from .lvcode import ( lvgl_static, ) from .types import LV_EVENT -from .widgets import widget_map +from .widgets import LvScrActType, get_scr_act, widget_map async def generate_triggers(): @@ -33,6 +35,9 @@ async def generate_triggers(): """ for w in widget_map.values(): + if isinstance(w.type, LvScrActType): + w = get_scr_act(w.var) + if w.config: for event, conf in { event: conf @@ -43,6 +48,24 @@ async def generate_triggers(): w.add_flag("LV_OBJ_FLAG_CLICKABLE") event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()]) await add_trigger(conf, w, event) + + for event, conf in { + event: conf + for event, conf in w.config.items() + if event in SWIPE_TRIGGERS + }.items(): + conf = conf[0] + dir = event[9:].upper() + dir = {"UP": "TOP", "DOWN": "BOTTOM"}.get(dir, dir) + dir = DIRECTIONS.mapper(dir) + w.clear_flag("LV_OBJ_FLAG_SCROLLABLE") + selected = literal( + f"lv_indev_get_gesture_dir(lv_indev_get_act()) == {dir}" + ) + await add_trigger( + conf, w, literal("LV_EVENT_GESTURE"), is_selected=selected + ) + for conf in w.config.get(CONF_ON_VALUE, ()): await add_trigger( conf, @@ -61,13 +84,14 @@ async def generate_triggers(): lv.obj_align_to(w.obj, target, align, x, y) -async def add_trigger(conf, w, *events): +async def add_trigger(conf, w, *events, is_selected=None): + is_selected = is_selected or w.is_selected() tid = conf[CONF_TRIGGER_ID] trigger = cg.new_Pvariable(tid) args = w.get_args() + [(lv_event_t_ptr, "event")] value = w.get_values() await automation.build_automation(trigger, args, conf) async with LambdaContext(EVENT_ARG, where=tid) as context: - with LvConditional(w.is_selected()): + with LvConditional(is_selected): lv_add(trigger.trigger(*value, literal("event"))) lv_add(lvgl_static.add_event_cb(w.obj, await context.get_lambda(), *events)) diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index c51a3d03e7..c527f51b1e 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -133,6 +133,18 @@ lvgl: pages: - id: page1 + on_swipe_top: + logger.log: "swiped up" + on_swipe_bottom: + logger.log: "swiped down" + on_swipe_up: + logger.log: "swiped up" + on_swipe_down: + logger.log: "swiped down" + on_swipe_left: + logger.log: "swiped left" + on_swipe_right: + logger.log: "swiped right" bg_image_src: cat_image on_load: - logger.log: page loaded From 1215d2ffeba5c06e3ff33aadddea23842b530fe9 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 5 Feb 2025 10:22:40 +1100 Subject: [PATCH 0964/1052] [xxtea] Extract encryption functions to separate component (#8183) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/udp/__init__.py | 2 +- esphome/components/udp/udp_component.cpp | 55 +++--------------------- esphome/components/xxtea/__init__.py | 3 ++ esphome/components/xxtea/xxtea.cpp | 46 ++++++++++++++++++++ esphome/components/xxtea/xxtea.h | 26 +++++++++++ 6 files changed, 82 insertions(+), 51 deletions(-) create mode 100644 esphome/components/xxtea/__init__.py create mode 100644 esphome/components/xxtea/xxtea.cpp create mode 100644 esphome/components/xxtea/xxtea.h diff --git a/CODEOWNERS b/CODEOWNERS index d26e153c1a..03e26bcb84 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -499,5 +499,6 @@ esphome/components/xiaomi_mhoc401/* @vevsvevs esphome/components/xiaomi_rtcgq02lm/* @jesserockz esphome/components/xl9535/* @mreditor97 esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68 +esphome/components/xxtea/* @clydebarrow esphome/components/zhlt01/* @cfeenstra1024 esphome/components/zio_ultrasonic/* @kahrendt diff --git a/esphome/components/udp/__init__.py b/esphome/components/udp/__init__.py index 5485663f1c..140d1e4236 100644 --- a/esphome/components/udp/__init__.py +++ b/esphome/components/udp/__init__.py @@ -18,7 +18,7 @@ from esphome.cpp_generator import MockObjClass CODEOWNERS = ["@clydebarrow"] DEPENDENCIES = ["network"] -AUTO_LOAD = ["socket"] +AUTO_LOAD = ["socket", "xxtea"] MULTI_CONF = True udp_ns = cg.esphome_ns.namespace("udp") diff --git a/esphome/components/udp/udp_component.cpp b/esphome/components/udp/udp_component.cpp index 30f7356879..59cba8c7fe 100644 --- a/esphome/components/udp/udp_component.cpp +++ b/esphome/components/udp/udp_component.cpp @@ -3,6 +3,8 @@ #include "esphome/components/network/util.h" #include "udp_component.h" +#include "esphome/components/xxtea/xxtea.h" + namespace esphome { namespace udp { @@ -47,54 +49,7 @@ namespace udp { */ static const char *const TAG = "udp"; -/** - * XXTEA implementation, using 256 bit key. - */ - -static const uint32_t DELTA = 0x9e3779b9; -#define MX ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p ^ e) & 7] ^ z))) - -/** - * Encrypt a block of data in-place - */ - -static void xxtea_encrypt(uint32_t *v, size_t n, const uint32_t *k) { - uint32_t z, y, sum, e; - size_t p; - size_t q = 6 + 52 / n; - sum = 0; - z = v[n - 1]; - while (q-- != 0) { - sum += DELTA; - e = (sum >> 2); - for (p = 0; p != n - 1; p++) { - y = v[p + 1]; - z = v[p] += MX; - } - y = v[0]; - z = v[n - 1] += MX; - } -} - -static void xxtea_decrypt(uint32_t *v, size_t n, const uint32_t *k) { - uint32_t z, y, sum, e; - size_t p; - size_t q = 6 + 52 / n; - sum = q * DELTA; - y = v[0]; - while (q-- != 0) { - e = (sum >> 2); - for (p = n - 1; p != 0; p--) { - z = v[p - 1]; - y = v[p] -= MX; - } - z = v[n - 1]; - y = v[0] -= MX; - sum -= DELTA; - } -} - -inline static size_t round4(size_t value) { return (value + 3) & ~3; } +static size_t round4(size_t value) { return (value + 3) & ~3; } union FuData { uint32_t u32; @@ -312,7 +267,7 @@ void UDPComponent::flush_() { memcpy(buffer, this->header_.data(), this->header_.size()); memcpy(buffer + header_len, this->data_.data(), this->data_.size()); if (this->is_encrypted_()) { - xxtea_encrypt(buffer + header_len, len, (uint32_t *) this->encryption_key_.data()); + xxtea::encrypt(buffer + header_len, len, (uint32_t *) this->encryption_key_.data()); } auto total_len = (header_len + len) * 4; this->send_packet_(buffer, total_len); @@ -503,7 +458,7 @@ void UDPComponent::process_(uint8_t *buf, const size_t len) { #endif if (!provider.encryption_key.empty()) { - xxtea_decrypt((uint32_t *) buf, (end - buf) / 4, (uint32_t *) provider.encryption_key.data()); + xxtea::decrypt((uint32_t *) buf, (end - buf) / 4, (uint32_t *) provider.encryption_key.data()); } byte = *buf++; if (byte == ROLLING_CODE_KEY) { diff --git a/esphome/components/xxtea/__init__.py b/esphome/components/xxtea/__init__.py new file mode 100644 index 0000000000..7a7bce1781 --- /dev/null +++ b/esphome/components/xxtea/__init__.py @@ -0,0 +1,3 @@ +"""ESPHome XXTEA encryption component.""" + +CODEOWNERS = ["@clydebarrow"] diff --git a/esphome/components/xxtea/xxtea.cpp b/esphome/components/xxtea/xxtea.cpp new file mode 100644 index 0000000000..aae663ee01 --- /dev/null +++ b/esphome/components/xxtea/xxtea.cpp @@ -0,0 +1,46 @@ +#include "xxtea.h" + +namespace esphome { +namespace xxtea { + +static const uint32_t DELTA = 0x9e3779b9; +#define MX ((((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p ^ e) & 7] ^ z))) + +void encrypt(uint32_t *v, size_t n, const uint32_t *k) { + uint32_t z, y, sum, e; + size_t p; + size_t q = 6 + 52 / n; + sum = 0; + z = v[n - 1]; + while (q-- != 0) { + sum += DELTA; + e = (sum >> 2); + for (p = 0; p != n - 1; p++) { + y = v[p + 1]; + z = v[p] += MX; + } + y = v[0]; + z = v[n - 1] += MX; + } +} + +void decrypt(uint32_t *v, size_t n, const uint32_t *k) { + uint32_t z, y, sum, e; + size_t p; + size_t q = 6 + 52 / n; + sum = q * DELTA; + y = v[0]; + while (q-- != 0) { + e = (sum >> 2); + for (p = n - 1; p != 0; p--) { + z = v[p - 1]; + y = v[p] -= MX; + } + z = v[n - 1]; + y = v[0] -= MX; + sum -= DELTA; + } +} + +} // namespace xxtea +} // namespace esphome diff --git a/esphome/components/xxtea/xxtea.h b/esphome/components/xxtea/xxtea.h new file mode 100644 index 0000000000..86afbd1d46 --- /dev/null +++ b/esphome/components/xxtea/xxtea.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +namespace esphome { +namespace xxtea { + +/** + * Encrypt a block of data in-place using XXTEA algorithm with 256-bit key + * @param v Data to encrypt (as array of 32-bit words) + * @param n Number of 32-bit words in data + * @param k Key (array of 8 32-bit words) + */ +void encrypt(uint32_t *v, size_t n, const uint32_t *k); + +/** + * Decrypt a block of data in-place using XXTEA algorithm with 256-bit key + * @param v Data to decrypt (as array of 32-bit words) + * @param n Number of 32-bit words in data + * @param k Key (array of 8 32-bit words) + */ +void decrypt(uint32_t *v, size_t n, const uint32_t *k); + +} // namespace xxtea +} // namespace esphome From 977333a73c1960b3fc5664a4af38721cf8a78285 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 5 Feb 2025 12:44:51 +1100 Subject: [PATCH 0965/1052] [lvgl] Make layouts work properly on base display (#8193) --- esphome/components/lvgl/__init__.py | 146 +++++++++++++++------------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 07f5d94543..22571c2550 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -396,75 +396,87 @@ def add_hello_world(config): FINAL_VALIDATE_SCHEMA = final_validation -LVGL_SCHEMA = ( - cv.polling_component_schema("1s") - .extend(obj_schema(LvScrActType())) - .extend( - { - cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent), - cv.GenerateID(df.CONF_DISPLAYS): display_schema, - cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16), - cv.Optional(df.CONF_DEFAULT_FONT, default="montserrat_14"): lvalid.lv_font, - cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean, - cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int, - cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage, - cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of( - *df.LV_LOG_LEVELS, upper=True - ), - cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of( - "big_endian", "little_endian" - ), - cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list( - cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)}) - .extend(STYLE_SCHEMA) - .extend( +LVGL_SCHEMA = cv.All( + container_schema( + obj_spec, + cv.polling_component_schema("1s") + .extend( + { + cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent), + cv.GenerateID(df.CONF_DISPLAYS): display_schema, + cv.Optional(df.CONF_COLOR_DEPTH, default=16): cv.one_of(16), + cv.Optional( + df.CONF_DEFAULT_FONT, default="montserrat_14" + ): lvalid.lv_font, + cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean, + cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int, + cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage, + cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of( + *df.LV_LOG_LEVELS, upper=True + ), + cv.Optional(df.CONF_BYTE_ORDER, default="big_endian"): cv.one_of( + "big_endian", "little_endian" + ), + cv.Optional(df.CONF_STYLE_DEFINITIONS): cv.ensure_list( + cv.Schema({cv.Required(CONF_ID): cv.declare_id(lv_style_t)}) + .extend(STYLE_SCHEMA) + .extend( + { + cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, + cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments, + cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, + cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, + } + ) + ), + cv.Optional(CONF_ON_IDLE): validate_automation( { - cv.Optional(df.CONF_GRID_CELL_X_ALIGN): grid_alignments, - cv.Optional(df.CONF_GRID_CELL_Y_ALIGN): grid_alignments, - cv.Optional(df.CONF_PAD_ROW): lvalid.pixels, - cv.Optional(df.CONF_PAD_COLUMN): lvalid.pixels, + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(IdleTrigger), + cv.Required(CONF_TIMEOUT): cv.templatable( + cv.positive_time_period_milliseconds + ), } - ) - ), - cv.Optional(CONF_ON_IDLE): validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(IdleTrigger), - cv.Required(CONF_TIMEOUT): cv.templatable( - cv.positive_time_period_milliseconds - ), - } - ), - cv.Optional(df.CONF_ON_PAUSE): validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), - } - ), - cv.Optional(df.CONF_ON_RESUME): validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), - } - ), - cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list(WIDGET_SCHEMA), - cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list( - container_schema(page_spec) - ), - cv.Optional(df.CONF_MSGBOXES): cv.ensure_list(MSGBOX_SCHEMA), - cv.Optional(df.CONF_PAGE_WRAP, default=True): lv_bool, - cv.Optional(df.CONF_TOP_LAYER): container_schema(obj_spec), - cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color, - cv.Optional(df.CONF_THEME): cv.Schema( - {cv.Optional(name): obj_schema(w) for name, w in WIDGET_TYPES.items()} - ), - cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA, - cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, - cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, - cv.Optional(df.CONF_KEYPADS, default=None): KEYPADS_CONFIG, - cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t), - cv.Optional(df.CONF_RESUME_ON_INPUT, default=True): cv.boolean, - } - ) - .extend(DISP_BG_SCHEMA) - .add_extra(add_hello_world) + ), + cv.Optional(df.CONF_ON_PAUSE): validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), + } + ), + cv.Optional(df.CONF_ON_RESUME): validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PauseTrigger), + } + ), + cv.Exclusive(df.CONF_WIDGETS, CONF_PAGES): cv.ensure_list( + WIDGET_SCHEMA + ), + cv.Exclusive(CONF_PAGES, CONF_PAGES): cv.ensure_list( + container_schema(page_spec) + ), + cv.Optional(df.CONF_MSGBOXES): cv.ensure_list(MSGBOX_SCHEMA), + cv.Optional(df.CONF_PAGE_WRAP, default=True): lv_bool, + cv.Optional(df.CONF_TOP_LAYER): container_schema(obj_spec), + cv.Optional( + df.CONF_TRANSPARENCY_KEY, default=0x000400 + ): lvalid.lv_color, + cv.Optional(df.CONF_THEME): cv.Schema( + { + cv.Optional(name): obj_schema(w) + for name, w in WIDGET_TYPES.items() + } + ), + cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA, + cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, + cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, + cv.Optional(df.CONF_KEYPADS, default=None): KEYPADS_CONFIG, + cv.GenerateID(df.CONF_DEFAULT_GROUP): cv.declare_id(lv_group_t), + cv.Optional(df.CONF_RESUME_ON_INPUT, default=True): cv.boolean, + } + ) + .extend(DISP_BG_SCHEMA), + ), + cv.has_at_most_one_key(CONF_PAGES, df.CONF_LAYOUT), + add_hello_world, ) From 4d8f58db94caff2087c96688be72a053b1dccd07 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:34:30 +1100 Subject: [PATCH 0966/1052] [preferences] Better handling of flash_write_interval (#8199) --- esphome/components/preferences/__init__.py | 6 ++---- esphome/components/preferences/syncer.h | 13 ++++++++++--- tests/components/preferences/test.esp32-idf.yaml | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 tests/components/preferences/test.esp32-idf.yaml diff --git a/esphome/components/preferences/__init__.py b/esphome/components/preferences/__init__.py index 4844ad6c02..1da6d02045 100644 --- a/esphome/components/preferences/__init__.py +++ b/esphome/components/preferences/__init__.py @@ -1,6 +1,6 @@ -from esphome.const import CONF_ID import esphome.codegen as cg import esphome.config_validation as cv +from esphome.const import CONF_ID CODEOWNERS = ["@esphome/core"] @@ -11,9 +11,7 @@ CONF_FLASH_WRITE_INTERVAL = "flash_write_interval" CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(IntervalSyncer), - cv.Optional( - CONF_FLASH_WRITE_INTERVAL, default="60s" - ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_FLASH_WRITE_INTERVAL, default="60s"): cv.update_interval, } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/preferences/syncer.h b/esphome/components/preferences/syncer.h index af1fe9ba4a..8976a1fe15 100644 --- a/esphome/components/preferences/syncer.h +++ b/esphome/components/preferences/syncer.h @@ -8,15 +8,22 @@ namespace preferences { class IntervalSyncer : public Component { public: - void set_write_interval(uint32_t write_interval) { write_interval_ = write_interval; } + void set_write_interval(uint32_t write_interval) { this->write_interval_ = write_interval; } void setup() override { - set_interval(write_interval_, []() { global_preferences->sync(); }); + if (this->write_interval_ != 0) { + set_interval(this->write_interval_, []() { global_preferences->sync(); }); + } + } + void loop() override { + if (this->write_interval_ == 0) { + global_preferences->sync(); + } } void on_shutdown() override { global_preferences->sync(); } float get_setup_priority() const override { return setup_priority::BUS; } protected: - uint32_t write_interval_; + uint32_t write_interval_{60000}; }; } // namespace preferences diff --git a/tests/components/preferences/test.esp32-idf.yaml b/tests/components/preferences/test.esp32-idf.yaml new file mode 100644 index 0000000000..4c7c176fdc --- /dev/null +++ b/tests/components/preferences/test.esp32-idf.yaml @@ -0,0 +1,2 @@ +preferences: + flash_write_interval: 20s From cecce0f3cbde410b0cd96db63d9648c35b659eaf Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 20:37:15 -0600 Subject: [PATCH 0967/1052] [CI] Consolidate some tests (N, O, P) (#8204) --- tests/components/ntc/common.yaml | 25 ++ tests/components/ntc/test.esp32-ard.yaml | 30 +-- tests/components/ntc/test.esp32-c3-ard.yaml | 30 +-- tests/components/ntc/test.esp32-c3-idf.yaml | 4 + tests/components/ntc/test.esp32-idf.yaml | 30 +-- tests/components/ntc/test.esp32-s2-ard.yaml | 30 +-- tests/components/ntc/test.esp32-s2-idf.yaml | 4 + tests/components/ntc/test.esp32-s3-ard.yaml | 30 +-- tests/components/ntc/test.esp32-s3-idf.yaml | 4 + tests/components/ntc/test.esp8266-ard.yaml | 29 +- tests/components/ntc/test.rp2040-ard.yaml | 29 +- tests/components/output/common.yaml | 13 + tests/components/output/test.esp32-ard.yaml | 16 +- .../components/output/test.esp32-c3-ard.yaml | 16 +- .../components/output/test.esp32-c3-idf.yaml | 16 +- tests/components/output/test.esp32-idf.yaml | 16 +- tests/components/output/test.esp8266-ard.yaml | 16 +- tests/components/output/test.rp2040-ard.yaml | 16 +- tests/components/partition/common-ard.yaml | 22 ++ tests/components/partition/common-idf.yaml | 21 ++ .../components/partition/test.esp32-ard.yaml | 27 +- .../partition/test.esp32-c3-ard.yaml | 27 +- .../partition/test.esp32-c3-idf.yaml | 26 +- .../components/partition/test.esp32-idf.yaml | 26 +- tests/components/pca6416a/common.yaml | 17 ++ tests/components/pca6416a/test.esp32-ard.yaml | 20 +- .../pca6416a/test.esp32-c3-ard.yaml | 20 +- .../pca6416a/test.esp32-c3-idf.yaml | 20 +- tests/components/pca6416a/test.esp32-idf.yaml | 20 +- .../components/pca6416a/test.esp8266-ard.yaml | 20 +- .../components/pca6416a/test.rp2040-ard.yaml | 20 +- tests/components/pca9554/common.yaml | 26 ++ tests/components/pca9554/test.esp32-ard.yaml | 29 +- .../components/pca9554/test.esp32-c3-ard.yaml | 29 +- .../components/pca9554/test.esp32-c3-idf.yaml | 29 +- tests/components/pca9554/test.esp32-idf.yaml | 29 +- .../components/pca9554/test.esp8266-ard.yaml | 29 +- tests/components/pca9554/test.rp2040-ard.yaml | 29 +- tests/components/pca9685/common.yaml | 34 +++ tests/components/pca9685/test.esp32-ard.yaml | 37 +-- .../components/pca9685/test.esp32-c3-ard.yaml | 37 +-- .../components/pca9685/test.esp32-c3-idf.yaml | 37 +-- tests/components/pca9685/test.esp32-idf.yaml | 37 +-- .../components/pca9685/test.esp8266-ard.yaml | 37 +-- tests/components/pca9685/test.rp2040-ard.yaml | 29 +- tests/components/pcd8544/common.yaml | 14 + tests/components/pcd8544/test.esp32-ard.yaml | 21 +- .../components/pcd8544/test.esp32-c3-ard.yaml | 21 +- .../components/pcd8544/test.esp32-c3-idf.yaml | 21 +- tests/components/pcd8544/test.esp32-idf.yaml | 21 +- .../components/pcd8544/test.esp8266-ard.yaml | 21 +- tests/components/pcd8544/test.rp2040-ard.yaml | 21 +- tests/components/pcf85063/common.yaml | 12 + tests/components/pcf85063/test.esp32-ard.yaml | 15 +- .../pcf85063/test.esp32-c3-ard.yaml | 15 +- .../pcf85063/test.esp32-c3-idf.yaml | 15 +- tests/components/pcf85063/test.esp32-idf.yaml | 15 +- .../components/pcf85063/test.esp8266-ard.yaml | 15 +- .../components/pcf85063/test.rp2040-ard.yaml | 15 +- tests/components/pcf8563/common.yaml | 12 + tests/components/pcf8563/test.esp32-ard.yaml | 15 +- .../components/pcf8563/test.esp32-c3-ard.yaml | 15 +- .../components/pcf8563/test.esp32-c3-idf.yaml | 15 +- tests/components/pcf8563/test.esp32-idf.yaml | 15 +- .../components/pcf8563/test.esp8266-ard.yaml | 15 +- tests/components/pcf8563/test.rp2040-ard.yaml | 15 +- tests/components/pcf8574/common.yaml | 28 ++ tests/components/pcf8574/test.esp32-ard.yaml | 31 +-- .../components/pcf8574/test.esp32-c3-ard.yaml | 31 +-- .../components/pcf8574/test.esp32-c3-idf.yaml | 31 +-- tests/components/pcf8574/test.esp32-idf.yaml | 31 +-- .../components/pcf8574/test.esp8266-ard.yaml | 31 +-- tests/components/pcf8574/test.rp2040-ard.yaml | 31 +-- tests/components/pipsolar/common.yaml | 249 +++++++++++++++++ tests/components/pipsolar/test.esp32-ard.yaml | 252 +----------------- .../pipsolar/test.esp32-c3-ard.yaml | 252 +----------------- .../pipsolar/test.esp32-c3-idf.yaml | 252 +----------------- tests/components/pipsolar/test.esp32-idf.yaml | 252 +----------------- .../components/pipsolar/test.esp8266-ard.yaml | 252 +----------------- .../components/pipsolar/test.rp2040-ard.yaml | 252 +----------------- tests/components/pm1006/common.yaml | 10 + tests/components/pm1006/test.esp32-ard.yaml | 13 +- .../components/pm1006/test.esp32-c3-ard.yaml | 13 +- .../components/pm1006/test.esp32-c3-idf.yaml | 13 +- tests/components/pm1006/test.esp32-idf.yaml | 13 +- tests/components/pm1006/test.esp8266-ard.yaml | 13 +- tests/components/pm1006/test.rp2040-ard.yaml | 13 +- tests/components/pmsa003i/common.yaml | 27 ++ tests/components/pmsa003i/test.esp32-ard.yaml | 30 +-- .../pmsa003i/test.esp32-c3-ard.yaml | 30 +-- .../pmsa003i/test.esp32-c3-idf.yaml | 30 +-- tests/components/pmsa003i/test.esp32-idf.yaml | 30 +-- .../components/pmsa003i/test.esp8266-ard.yaml | 30 +-- .../components/pmsa003i/test.rp2040-ard.yaml | 30 +-- tests/components/pmsx003/common.yaml | 34 +++ tests/components/pmsx003/test.esp32-ard.yaml | 37 +-- .../components/pmsx003/test.esp32-c3-ard.yaml | 37 +-- .../components/pmsx003/test.esp32-c3-idf.yaml | 37 +-- tests/components/pmsx003/test.esp32-idf.yaml | 37 +-- .../components/pmsx003/test.esp8266-ard.yaml | 37 +-- tests/components/pmsx003/test.rp2040-ard.yaml | 37 +-- tests/components/pmwcs3/common.yaml | 15 ++ tests/components/pmwcs3/test.esp32-ard.yaml | 18 +- .../components/pmwcs3/test.esp32-c3-ard.yaml | 18 +- .../components/pmwcs3/test.esp32-c3-idf.yaml | 18 +- tests/components/pmwcs3/test.esp32-idf.yaml | 18 +- tests/components/pmwcs3/test.esp8266-ard.yaml | 18 +- tests/components/pmwcs3/test.rp2040-ard.yaml | 18 +- tests/components/pn532_i2c/common.yaml | 13 + .../components/pn532_i2c/test.esp32-ard.yaml | 16 +- .../pn532_i2c/test.esp32-c3-ard.yaml | 16 +- .../pn532_i2c/test.esp32-c3-idf.yaml | 16 +- .../components/pn532_i2c/test.esp32-idf.yaml | 16 +- .../pn532_i2c/test.esp8266-ard.yaml | 16 +- .../components/pn532_i2c/test.rp2040-ard.yaml | 16 +- tests/components/pn532_spi/common.yaml | 15 ++ .../components/pn532_spi/test.esp32-ard.yaml | 20 +- .../pn532_spi/test.esp32-c3-ard.yaml | 20 +- .../pn532_spi/test.esp32-c3-idf.yaml | 20 +- .../components/pn532_spi/test.esp32-idf.yaml | 20 +- .../pn532_spi/test.esp8266-ard.yaml | 20 +- .../components/pn532_spi/test.rp2040-ard.yaml | 20 +- tests/components/pn7150_i2c/common.yaml | 35 +++ .../components/pn7150_i2c/test.esp32-ard.yaml | 40 +-- .../pn7150_i2c/test.esp32-c3-ard.yaml | 40 +-- .../pn7150_i2c/test.esp32-c3-idf.yaml | 40 +-- .../components/pn7150_i2c/test.esp32-idf.yaml | 40 +-- .../pn7150_i2c/test.esp8266-ard.yaml | 40 +-- .../pn7150_i2c/test.rp2040-ard.yaml | 40 +-- tests/components/pn7160_i2c/common.yaml | 35 +++ .../components/pn7160_i2c/test.esp32-ard.yaml | 40 +-- .../pn7160_i2c/test.esp32-c3-ard.yaml | 40 +-- .../pn7160_i2c/test.esp32-c3-idf.yaml | 40 +-- .../components/pn7160_i2c/test.esp32-idf.yaml | 40 +-- .../pn7160_i2c/test.esp8266-ard.yaml | 40 +-- .../pn7160_i2c/test.rp2040-ard.yaml | 40 +-- tests/components/pn7160_spi/common.yaml | 37 +++ .../components/pn7160_spi/test.esp32-ard.yaml | 44 +-- .../pn7160_spi/test.esp32-c3-ard.yaml | 44 +-- .../pn7160_spi/test.esp32-c3-idf.yaml | 44 +-- .../components/pn7160_spi/test.esp32-idf.yaml | 44 +-- .../pn7160_spi/test.esp8266-ard.yaml | 44 +-- .../pn7160_spi/test.rp2040-ard.yaml | 44 +-- tests/components/pylontech/common.yaml | 48 ++++ .../components/pylontech/test.esp32-ard.yaml | 51 +--- .../pylontech/test.esp32-c3-ard.yaml | 51 +--- .../pylontech/test.esp32-c3-idf.yaml | 51 +--- .../components/pylontech/test.esp32-idf.yaml | 51 +--- .../pylontech/test.esp8266-ard.yaml | 51 +--- .../components/pylontech/test.rp2040-ard.yaml | 51 +--- tests/components/pzem004t/common.yaml | 14 + tests/components/pzem004t/test.esp32-ard.yaml | 17 +- .../pzem004t/test.esp32-c3-ard.yaml | 17 +- .../pzem004t/test.esp32-c3-idf.yaml | 17 +- tests/components/pzem004t/test.esp32-idf.yaml | 17 +- .../components/pzem004t/test.esp8266-ard.yaml | 17 +- .../components/pzem004t/test.rp2040-ard.yaml | 17 +- tests/components/pzemac/common.yaml | 28 ++ tests/components/pzemac/test.esp32-ard.yaml | 31 +-- .../components/pzemac/test.esp32-c3-ard.yaml | 31 +-- .../components/pzemac/test.esp32-c3-idf.yaml | 31 +-- tests/components/pzemac/test.esp32-idf.yaml | 31 +-- tests/components/pzemac/test.esp8266-ard.yaml | 31 +-- tests/components/pzemac/test.rp2040-ard.yaml | 31 +-- tests/components/pzemdc/common.yaml | 23 ++ tests/components/pzemdc/test.esp32-ard.yaml | 26 +- .../components/pzemdc/test.esp32-c3-ard.yaml | 26 +- .../components/pzemdc/test.esp32-c3-idf.yaml | 26 +- tests/components/pzemdc/test.esp32-idf.yaml | 26 +- tests/components/pzemdc/test.esp8266-ard.yaml | 26 +- tests/components/pzemdc/test.rp2040-ard.yaml | 26 +- 171 files changed, 1479 insertions(+), 4560 deletions(-) create mode 100644 tests/components/ntc/common.yaml create mode 100644 tests/components/ntc/test.esp32-c3-idf.yaml create mode 100644 tests/components/ntc/test.esp32-s2-idf.yaml create mode 100644 tests/components/ntc/test.esp32-s3-idf.yaml create mode 100644 tests/components/output/common.yaml create mode 100644 tests/components/partition/common-ard.yaml create mode 100644 tests/components/partition/common-idf.yaml create mode 100644 tests/components/pca6416a/common.yaml create mode 100644 tests/components/pca9554/common.yaml create mode 100644 tests/components/pca9685/common.yaml create mode 100644 tests/components/pcd8544/common.yaml create mode 100644 tests/components/pcf85063/common.yaml create mode 100644 tests/components/pcf8563/common.yaml create mode 100644 tests/components/pcf8574/common.yaml create mode 100644 tests/components/pipsolar/common.yaml create mode 100644 tests/components/pm1006/common.yaml create mode 100644 tests/components/pmsa003i/common.yaml create mode 100644 tests/components/pmsx003/common.yaml create mode 100644 tests/components/pmwcs3/common.yaml create mode 100644 tests/components/pn532_i2c/common.yaml create mode 100644 tests/components/pn532_spi/common.yaml create mode 100644 tests/components/pn7150_i2c/common.yaml create mode 100644 tests/components/pn7160_i2c/common.yaml create mode 100644 tests/components/pn7160_spi/common.yaml create mode 100644 tests/components/pylontech/common.yaml create mode 100644 tests/components/pzem004t/common.yaml create mode 100644 tests/components/pzemac/common.yaml create mode 100644 tests/components/pzemdc/common.yaml diff --git a/tests/components/ntc/common.yaml b/tests/components/ntc/common.yaml new file mode 100644 index 0000000000..79ae7f601d --- /dev/null +++ b/tests/components/ntc/common.yaml @@ -0,0 +1,25 @@ +sensor: + - platform: adc + id: my_sensor + pin: ${pin} + - platform: resistance + sensor: my_sensor + configuration: DOWNSTREAM + resistor: 10kΩ + reference_voltage: 3.3V + name: Resistance + id: resist + - platform: ntc + sensor: resist + name: NTC Sensor + calibration: + b_constant: 3950 + reference_resistance: 10k + reference_temperature: 25°C + - platform: ntc + sensor: resist + name: NTC Sensor2 + calibration: + - 10.0kOhm -> 25°C + - 27.219kOhm -> 0°C + - 14.674kOhm -> 15°C diff --git a/tests/components/ntc/test.esp32-ard.yaml b/tests/components/ntc/test.esp32-ard.yaml index 3e0e901b6e..06864605a6 100644 --- a/tests/components/ntc/test.esp32-ard.yaml +++ b/tests/components/ntc/test.esp32-ard.yaml @@ -1,26 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 32 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist - - platform: ntc - sensor: resist - name: NTC Sensor - calibration: - b_constant: 3950 - reference_resistance: 10k - reference_temperature: 25°C - - platform: ntc - sensor: resist - name: NTC Sensor2 - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C +substitutions: + pin: GPIO32 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-c3-ard.yaml b/tests/components/ntc/test.esp32-c3-ard.yaml index c0edb83d9d..37fb325f4a 100644 --- a/tests/components/ntc/test.esp32-c3-ard.yaml +++ b/tests/components/ntc/test.esp32-c3-ard.yaml @@ -1,26 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 4 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist - - platform: ntc - sensor: resist - name: NTC Sensor - calibration: - b_constant: 3950 - reference_resistance: 10k - reference_temperature: 25°C - - platform: ntc - sensor: resist - name: NTC Sensor2 - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-c3-idf.yaml b/tests/components/ntc/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..37fb325f4a --- /dev/null +++ b/tests/components/ntc/test.esp32-c3-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-idf.yaml b/tests/components/ntc/test.esp32-idf.yaml index 3e0e901b6e..06864605a6 100644 --- a/tests/components/ntc/test.esp32-idf.yaml +++ b/tests/components/ntc/test.esp32-idf.yaml @@ -1,26 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 32 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist - - platform: ntc - sensor: resist - name: NTC Sensor - calibration: - b_constant: 3950 - reference_resistance: 10k - reference_temperature: 25°C - - platform: ntc - sensor: resist - name: NTC Sensor2 - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C +substitutions: + pin: GPIO32 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-s2-ard.yaml b/tests/components/ntc/test.esp32-s2-ard.yaml index c0edb83d9d..37fb325f4a 100644 --- a/tests/components/ntc/test.esp32-s2-ard.yaml +++ b/tests/components/ntc/test.esp32-s2-ard.yaml @@ -1,26 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 4 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist - - platform: ntc - sensor: resist - name: NTC Sensor - calibration: - b_constant: 3950 - reference_resistance: 10k - reference_temperature: 25°C - - platform: ntc - sensor: resist - name: NTC Sensor2 - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-s2-idf.yaml b/tests/components/ntc/test.esp32-s2-idf.yaml new file mode 100644 index 0000000000..37fb325f4a --- /dev/null +++ b/tests/components/ntc/test.esp32-s2-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-s3-ard.yaml b/tests/components/ntc/test.esp32-s3-ard.yaml index c0edb83d9d..37fb325f4a 100644 --- a/tests/components/ntc/test.esp32-s3-ard.yaml +++ b/tests/components/ntc/test.esp32-s3-ard.yaml @@ -1,26 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 4 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist - - platform: ntc - sensor: resist - name: NTC Sensor - calibration: - b_constant: 3950 - reference_resistance: 10k - reference_temperature: 25°C - - platform: ntc - sensor: resist - name: NTC Sensor2 - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp32-s3-idf.yaml b/tests/components/ntc/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..37fb325f4a --- /dev/null +++ b/tests/components/ntc/test.esp32-s3-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.esp8266-ard.yaml b/tests/components/ntc/test.esp8266-ard.yaml index 370d16d3f7..4a6d36cbb4 100644 --- a/tests/components/ntc/test.esp8266-ard.yaml +++ b/tests/components/ntc/test.esp8266-ard.yaml @@ -1,25 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: A0 - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist - - platform: ntc - sensor: resist - name: NTC Sensor - calibration: - b_constant: 3950 - reference_resistance: 10k - reference_temperature: 25°C - - platform: ntc - sensor: resist - name: NTC Sensor2 - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C +substitutions: + pin: A0 + +<<: !include common.yaml diff --git a/tests/components/ntc/test.rp2040-ard.yaml b/tests/components/ntc/test.rp2040-ard.yaml index 9c7ba7b539..9479437434 100644 --- a/tests/components/ntc/test.rp2040-ard.yaml +++ b/tests/components/ntc/test.rp2040-ard.yaml @@ -1,25 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 26 - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist - - platform: ntc - sensor: resist - name: NTC Sensor - calibration: - b_constant: 3950 - reference_resistance: 10k - reference_temperature: 25°C - - platform: ntc - sensor: resist - name: NTC Sensor2 - calibration: - - 10.0kOhm -> 25°C - - 27.219kOhm -> 0°C - - 14.674kOhm -> 15°C +substitutions: + pin: GPIO26 + +<<: !include common.yaml diff --git a/tests/components/output/common.yaml b/tests/components/output/common.yaml new file mode 100644 index 0000000000..5f31ae50a8 --- /dev/null +++ b/tests/components/output/common.yaml @@ -0,0 +1,13 @@ +esphome: + on_boot: + then: + - output.turn_off: light_output_1 + - output.turn_on: light_output_1 + - output.set_level: + id: light_output_1 + level: 50% + +output: + - platform: ${output_platform} + id: light_output_1 + pin: ${pin} diff --git a/tests/components/output/test.esp32-ard.yaml b/tests/components/output/test.esp32-ard.yaml index 480f9dfe1f..7687f2a7c8 100644 --- a/tests/components/output/test.esp32-ard.yaml +++ b/tests/components/output/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -esphome: - on_boot: - then: - - output.turn_off: light_output_1 - - output.turn_on: light_output_1 - - output.set_level: - id: light_output_1 - level: 50% +substitutions: + output_platform: ledc + pin: GPIO12 -output: - - platform: ledc - id: light_output_1 - pin: 12 +<<: !include common.yaml diff --git a/tests/components/output/test.esp32-c3-ard.yaml b/tests/components/output/test.esp32-c3-ard.yaml index c56d85c296..2227643703 100644 --- a/tests/components/output/test.esp32-c3-ard.yaml +++ b/tests/components/output/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -esphome: - on_boot: - then: - - output.turn_off: light_output_1 - - output.turn_on: light_output_1 - - output.set_level: - id: light_output_1 - level: 50% +substitutions: + output_platform: ledc + pin: GPIO1 -output: - - platform: ledc - id: light_output_1 - pin: 1 +<<: !include common.yaml diff --git a/tests/components/output/test.esp32-c3-idf.yaml b/tests/components/output/test.esp32-c3-idf.yaml index c56d85c296..2227643703 100644 --- a/tests/components/output/test.esp32-c3-idf.yaml +++ b/tests/components/output/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -esphome: - on_boot: - then: - - output.turn_off: light_output_1 - - output.turn_on: light_output_1 - - output.set_level: - id: light_output_1 - level: 50% +substitutions: + output_platform: ledc + pin: GPIO1 -output: - - platform: ledc - id: light_output_1 - pin: 1 +<<: !include common.yaml diff --git a/tests/components/output/test.esp32-idf.yaml b/tests/components/output/test.esp32-idf.yaml index 480f9dfe1f..7687f2a7c8 100644 --- a/tests/components/output/test.esp32-idf.yaml +++ b/tests/components/output/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -esphome: - on_boot: - then: - - output.turn_off: light_output_1 - - output.turn_on: light_output_1 - - output.set_level: - id: light_output_1 - level: 50% +substitutions: + output_platform: ledc + pin: GPIO12 -output: - - platform: ledc - id: light_output_1 - pin: 12 +<<: !include common.yaml diff --git a/tests/components/output/test.esp8266-ard.yaml b/tests/components/output/test.esp8266-ard.yaml index d9cb353636..34152066ba 100644 --- a/tests/components/output/test.esp8266-ard.yaml +++ b/tests/components/output/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -esphome: - on_boot: - then: - - output.turn_off: light_output_1 - - output.turn_on: light_output_1 - - output.set_level: - id: light_output_1 - level: 50% +substitutions: + output_platform: esp8266_pwm + pin: GPIO12 -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 12 +<<: !include common.yaml diff --git a/tests/components/output/test.rp2040-ard.yaml b/tests/components/output/test.rp2040-ard.yaml index 399259fdd9..cc8d89ee06 100644 --- a/tests/components/output/test.rp2040-ard.yaml +++ b/tests/components/output/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -esphome: - on_boot: - then: - - output.turn_off: light_output_1 - - output.turn_on: light_output_1 - - output.set_level: - id: light_output_1 - level: 50% +substitutions: + output_platform: rp2040_pwm + pin: GPIO12 -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 12 +<<: !include common.yaml diff --git a/tests/components/partition/common-ard.yaml b/tests/components/partition/common-ard.yaml new file mode 100644 index 0000000000..654eacf54f --- /dev/null +++ b/tests/components/partition/common-ard.yaml @@ -0,0 +1,22 @@ +light: + - platform: ${light_platform} + id: part_leds + default_transition_length: 500ms + chipset: ws2812 + num_leds: 256 + rgb_order: GRB + rmt_channel: 1 + pin: ${pin} + - platform: partition + name: Partition Light + segments: + - id: part_leds + from: 0 + to: 0 + - id: part_leds + from: 1 + to: 10 + - id: part_leds + from: 20 + to: 25 + - single_light_id: part_leds diff --git a/tests/components/partition/common-idf.yaml b/tests/components/partition/common-idf.yaml new file mode 100644 index 0000000000..b2ceadd6f7 --- /dev/null +++ b/tests/components/partition/common-idf.yaml @@ -0,0 +1,21 @@ +light: + - platform: ${light_platform} + id: part_leds + default_transition_length: 500ms + chipset: ws2812 + num_leds: 256 + rgb_order: GRB + pin: ${pin} + - platform: partition + name: Partition Light + segments: + - id: part_leds + from: 0 + to: 0 + - id: part_leds + from: 1 + to: 10 + - id: part_leds + from: 20 + to: 25 + - single_light_id: part_leds diff --git a/tests/components/partition/test.esp32-ard.yaml b/tests/components/partition/test.esp32-ard.yaml index c8eae67d40..32cf2a64ba 100644 --- a/tests/components/partition/test.esp32-ard.yaml +++ b/tests/components/partition/test.esp32-ard.yaml @@ -1,22 +1,5 @@ -light: - - platform: fastled_clockless - id: part_leds - chipset: WS2812B - pin: 2 - num_leds: 256 - rgb_order: GRB - default_transition_length: 0s - color_correct: [50%, 50%, 50%] - - platform: partition - name: Partition Light - segments: - - id: part_leds - from: 0 - to: 0 - - id: part_leds - from: 1 - to: 10 - - id: part_leds - from: 20 - to: 25 - - single_light_id: part_leds +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO2 + +<<: !include common-ard.yaml diff --git a/tests/components/partition/test.esp32-c3-ard.yaml b/tests/components/partition/test.esp32-c3-ard.yaml index 77cfc5ad44..32cf2a64ba 100644 --- a/tests/components/partition/test.esp32-c3-ard.yaml +++ b/tests/components/partition/test.esp32-c3-ard.yaml @@ -1,22 +1,5 @@ -light: - - platform: esp32_rmt_led_strip - id: part_leds - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - rmt_channel: 0 - - platform: partition - name: Partition Light - segments: - - id: part_leds - from: 0 - to: 0 - - id: part_leds - from: 1 - to: 10 - - id: part_leds - from: 20 - to: 25 - - single_light_id: part_leds +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO2 + +<<: !include common-ard.yaml diff --git a/tests/components/partition/test.esp32-c3-idf.yaml b/tests/components/partition/test.esp32-c3-idf.yaml index 397e1b0642..d9dc4f6804 100644 --- a/tests/components/partition/test.esp32-c3-idf.yaml +++ b/tests/components/partition/test.esp32-c3-idf.yaml @@ -1,21 +1,5 @@ -light: - - platform: esp32_rmt_led_strip - id: part_leds - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - - platform: partition - name: Partition Light - segments: - - id: part_leds - from: 0 - to: 0 - - id: part_leds - from: 1 - to: 10 - - id: part_leds - from: 20 - to: 25 - - single_light_id: part_leds +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO2 + +<<: !include common-idf.yaml diff --git a/tests/components/partition/test.esp32-idf.yaml b/tests/components/partition/test.esp32-idf.yaml index 397e1b0642..7dc7d15553 100644 --- a/tests/components/partition/test.esp32-idf.yaml +++ b/tests/components/partition/test.esp32-idf.yaml @@ -1,21 +1,5 @@ -light: - - platform: esp32_rmt_led_strip - id: part_leds - default_transition_length: 500ms - chipset: ws2812 - rgb_order: GRB - num_leds: 256 - pin: 2 - - platform: partition - name: Partition Light - segments: - - id: part_leds - from: 0 - to: 0 - - id: part_leds - from: 1 - to: 10 - - id: part_leds - from: 20 - to: 25 - - single_light_id: part_leds +substitutions: + light_platform: esp32_rmt_led_strip + pin: GPIO12 + +<<: !include common-idf.yaml diff --git a/tests/components/pca6416a/common.yaml b/tests/components/pca6416a/common.yaml new file mode 100644 index 0000000000..ea538387e4 --- /dev/null +++ b/tests/components/pca6416a/common.yaml @@ -0,0 +1,17 @@ +i2c: + - id: i2c_pca6416a + scl: ${scl_pin} + sda: ${sda_pin} + +pca6416a: + - id: pca6416a_hub + address: 0x21 + +binary_sensor: + - platform: gpio + name: PCA6416A Binary Sensor + pin: + pca6416a: pca6416a_hub + number: 15 + mode: INPUT + inverted: true diff --git a/tests/components/pca6416a/test.esp32-ard.yaml b/tests/components/pca6416a/test.esp32-ard.yaml index 669e9416e4..63c3bd6afd 100644 --- a/tests/components/pca6416a/test.esp32-ard.yaml +++ b/tests/components/pca6416a/test.esp32-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_pca6416a - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pca6416a: - - id: pca6416a_hub - address: 0x21 - -binary_sensor: - - platform: gpio - name: PCA6416A Binary Sensor - pin: - pca6416a: pca6416a_hub - number: 15 - mode: INPUT - inverted: true +<<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp32-c3-ard.yaml b/tests/components/pca6416a/test.esp32-c3-ard.yaml index fe940c44cc..ee2c29ca4e 100644 --- a/tests/components/pca6416a/test.esp32-c3-ard.yaml +++ b/tests/components/pca6416a/test.esp32-c3-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_pca6416a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca6416a: - - id: pca6416a_hub - address: 0x21 - -binary_sensor: - - platform: gpio - name: PCA6416A Binary Sensor - pin: - pca6416a: pca6416a_hub - number: 15 - mode: INPUT - inverted: true +<<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp32-c3-idf.yaml b/tests/components/pca6416a/test.esp32-c3-idf.yaml index fe940c44cc..ee2c29ca4e 100644 --- a/tests/components/pca6416a/test.esp32-c3-idf.yaml +++ b/tests/components/pca6416a/test.esp32-c3-idf.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_pca6416a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca6416a: - - id: pca6416a_hub - address: 0x21 - -binary_sensor: - - platform: gpio - name: PCA6416A Binary Sensor - pin: - pca6416a: pca6416a_hub - number: 15 - mode: INPUT - inverted: true +<<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp32-idf.yaml b/tests/components/pca6416a/test.esp32-idf.yaml index 669e9416e4..63c3bd6afd 100644 --- a/tests/components/pca6416a/test.esp32-idf.yaml +++ b/tests/components/pca6416a/test.esp32-idf.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_pca6416a - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pca6416a: - - id: pca6416a_hub - address: 0x21 - -binary_sensor: - - platform: gpio - name: PCA6416A Binary Sensor - pin: - pca6416a: pca6416a_hub - number: 15 - mode: INPUT - inverted: true +<<: !include common.yaml diff --git a/tests/components/pca6416a/test.esp8266-ard.yaml b/tests/components/pca6416a/test.esp8266-ard.yaml index fe940c44cc..ee2c29ca4e 100644 --- a/tests/components/pca6416a/test.esp8266-ard.yaml +++ b/tests/components/pca6416a/test.esp8266-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_pca6416a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca6416a: - - id: pca6416a_hub - address: 0x21 - -binary_sensor: - - platform: gpio - name: PCA6416A Binary Sensor - pin: - pca6416a: pca6416a_hub - number: 15 - mode: INPUT - inverted: true +<<: !include common.yaml diff --git a/tests/components/pca6416a/test.rp2040-ard.yaml b/tests/components/pca6416a/test.rp2040-ard.yaml index fe940c44cc..ee2c29ca4e 100644 --- a/tests/components/pca6416a/test.rp2040-ard.yaml +++ b/tests/components/pca6416a/test.rp2040-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_pca6416a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca6416a: - - id: pca6416a_hub - address: 0x21 - -binary_sensor: - - platform: gpio - name: PCA6416A Binary Sensor - pin: - pca6416a: pca6416a_hub - number: 15 - mode: INPUT - inverted: true +<<: !include common.yaml diff --git a/tests/components/pca9554/common.yaml b/tests/components/pca9554/common.yaml new file mode 100644 index 0000000000..db2ec2b826 --- /dev/null +++ b/tests/components/pca9554/common.yaml @@ -0,0 +1,26 @@ +i2c: + - id: i2c_pca9554 + scl: ${scl_pin} + sda: ${sda_pin} + +pca9554: + - id: pca9554_hub + pin_count: 8 + address: 0x3F + +binary_sensor: + - platform: gpio + id: pca9554_input + name: PCA9554 Binary Sensor + pin: + pca9554: pca9554_hub + number: 1 + mode: INPUT + inverted: true + - platform: gpio + id: pca9554_output + pin: + pca9554: pca9554_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/pca9554/test.esp32-ard.yaml b/tests/components/pca9554/test.esp32-ard.yaml index 8fe9686303..63c3bd6afd 100644 --- a/tests/components/pca9554/test.esp32-ard.yaml +++ b/tests/components/pca9554/test.esp32-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_pca9554 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pca9554: - - id: pca9554_hub - pin_count: 8 - address: 0x3F - -binary_sensor: - - platform: gpio - id: pca9554_input - name: PCA9554 Binary Sensor - pin: - pca9554: pca9554_hub - number: 1 - mode: INPUT - inverted: true - - platform: gpio - id: pca9554_output - pin: - pca9554: pca9554_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pca9554/test.esp32-c3-ard.yaml b/tests/components/pca9554/test.esp32-c3-ard.yaml index 0ff453e64f..ee2c29ca4e 100644 --- a/tests/components/pca9554/test.esp32-c3-ard.yaml +++ b/tests/components/pca9554/test.esp32-c3-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_pca9554 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9554: - - id: pca9554_hub - pin_count: 8 - address: 0x3F - -binary_sensor: - - platform: gpio - id: pca9554_input - name: PCA9554 Binary Sensor - pin: - pca9554: pca9554_hub - number: 1 - mode: INPUT - inverted: true - - platform: gpio - id: pca9554_output - pin: - pca9554: pca9554_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pca9554/test.esp32-c3-idf.yaml b/tests/components/pca9554/test.esp32-c3-idf.yaml index 0ff453e64f..ee2c29ca4e 100644 --- a/tests/components/pca9554/test.esp32-c3-idf.yaml +++ b/tests/components/pca9554/test.esp32-c3-idf.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_pca9554 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9554: - - id: pca9554_hub - pin_count: 8 - address: 0x3F - -binary_sensor: - - platform: gpio - id: pca9554_input - name: PCA9554 Binary Sensor - pin: - pca9554: pca9554_hub - number: 1 - mode: INPUT - inverted: true - - platform: gpio - id: pca9554_output - pin: - pca9554: pca9554_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pca9554/test.esp32-idf.yaml b/tests/components/pca9554/test.esp32-idf.yaml index 8fe9686303..63c3bd6afd 100644 --- a/tests/components/pca9554/test.esp32-idf.yaml +++ b/tests/components/pca9554/test.esp32-idf.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_pca9554 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pca9554: - - id: pca9554_hub - pin_count: 8 - address: 0x3F - -binary_sensor: - - platform: gpio - id: pca9554_input - name: PCA9554 Binary Sensor - pin: - pca9554: pca9554_hub - number: 1 - mode: INPUT - inverted: true - - platform: gpio - id: pca9554_output - pin: - pca9554: pca9554_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pca9554/test.esp8266-ard.yaml b/tests/components/pca9554/test.esp8266-ard.yaml index 0ff453e64f..ee2c29ca4e 100644 --- a/tests/components/pca9554/test.esp8266-ard.yaml +++ b/tests/components/pca9554/test.esp8266-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_pca9554 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9554: - - id: pca9554_hub - pin_count: 8 - address: 0x3F - -binary_sensor: - - platform: gpio - id: pca9554_input - name: PCA9554 Binary Sensor - pin: - pca9554: pca9554_hub - number: 1 - mode: INPUT - inverted: true - - platform: gpio - id: pca9554_output - pin: - pca9554: pca9554_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pca9554/test.rp2040-ard.yaml b/tests/components/pca9554/test.rp2040-ard.yaml index 0ff453e64f..ee2c29ca4e 100644 --- a/tests/components/pca9554/test.rp2040-ard.yaml +++ b/tests/components/pca9554/test.rp2040-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_pca9554 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9554: - - id: pca9554_hub - pin_count: 8 - address: 0x3F - -binary_sensor: - - platform: gpio - id: pca9554_input - name: PCA9554 Binary Sensor - pin: - pca9554: pca9554_hub - number: 1 - mode: INPUT - inverted: true - - platform: gpio - id: pca9554_output - pin: - pca9554: pca9554_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pca9685/common.yaml b/tests/components/pca9685/common.yaml new file mode 100644 index 0000000000..57cdc5de9b --- /dev/null +++ b/tests/components/pca9685/common.yaml @@ -0,0 +1,34 @@ +i2c: + - id: i2c_pca9685 + scl: ${scl_pin} + sda: ${sda_pin} + +pca9685: + frequency: 500 + address: 0x0 + +output: + - platform: pca9685 + id: pca_0 + channel: 0 + - platform: pca9685 + id: pca_1 + channel: 1 + - platform: pca9685 + id: pca_2 + channel: 2 + - platform: pca9685 + id: pca_3 + channel: 3 + - platform: pca9685 + id: pca_4 + channel: 4 + - platform: pca9685 + id: pca_5 + channel: 5 + - platform: pca9685 + id: pca_6 + channel: 6 + - platform: pca9685 + id: pca_7 + channel: 7 diff --git a/tests/components/pca9685/test.esp32-ard.yaml b/tests/components/pca9685/test.esp32-ard.yaml index d02a16bcd1..63c3bd6afd 100644 --- a/tests/components/pca9685/test.esp32-ard.yaml +++ b/tests/components/pca9685/test.esp32-ard.yaml @@ -1,34 +1,5 @@ -i2c: - - id: i2c_pca9685 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pca9685: - frequency: 500 - address: 0x0 - -output: - - platform: pca9685 - id: pca_0 - channel: 0 - - platform: pca9685 - id: pca_1 - channel: 1 - - platform: pca9685 - id: pca_2 - channel: 2 - - platform: pca9685 - id: pca_3 - channel: 3 - - platform: pca9685 - id: pca_4 - channel: 4 - - platform: pca9685 - id: pca_5 - channel: 5 - - platform: pca9685 - id: pca_6 - channel: 6 - - platform: pca9685 - id: pca_7 - channel: 7 +<<: !include common.yaml diff --git a/tests/components/pca9685/test.esp32-c3-ard.yaml b/tests/components/pca9685/test.esp32-c3-ard.yaml index e532f323be..ee2c29ca4e 100644 --- a/tests/components/pca9685/test.esp32-c3-ard.yaml +++ b/tests/components/pca9685/test.esp32-c3-ard.yaml @@ -1,34 +1,5 @@ -i2c: - - id: i2c_pca9685 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9685: - frequency: 500 - address: 0x0 - -output: - - platform: pca9685 - id: pca_0 - channel: 0 - - platform: pca9685 - id: pca_1 - channel: 1 - - platform: pca9685 - id: pca_2 - channel: 2 - - platform: pca9685 - id: pca_3 - channel: 3 - - platform: pca9685 - id: pca_4 - channel: 4 - - platform: pca9685 - id: pca_5 - channel: 5 - - platform: pca9685 - id: pca_6 - channel: 6 - - platform: pca9685 - id: pca_7 - channel: 7 +<<: !include common.yaml diff --git a/tests/components/pca9685/test.esp32-c3-idf.yaml b/tests/components/pca9685/test.esp32-c3-idf.yaml index e532f323be..ee2c29ca4e 100644 --- a/tests/components/pca9685/test.esp32-c3-idf.yaml +++ b/tests/components/pca9685/test.esp32-c3-idf.yaml @@ -1,34 +1,5 @@ -i2c: - - id: i2c_pca9685 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9685: - frequency: 500 - address: 0x0 - -output: - - platform: pca9685 - id: pca_0 - channel: 0 - - platform: pca9685 - id: pca_1 - channel: 1 - - platform: pca9685 - id: pca_2 - channel: 2 - - platform: pca9685 - id: pca_3 - channel: 3 - - platform: pca9685 - id: pca_4 - channel: 4 - - platform: pca9685 - id: pca_5 - channel: 5 - - platform: pca9685 - id: pca_6 - channel: 6 - - platform: pca9685 - id: pca_7 - channel: 7 +<<: !include common.yaml diff --git a/tests/components/pca9685/test.esp32-idf.yaml b/tests/components/pca9685/test.esp32-idf.yaml index d02a16bcd1..63c3bd6afd 100644 --- a/tests/components/pca9685/test.esp32-idf.yaml +++ b/tests/components/pca9685/test.esp32-idf.yaml @@ -1,34 +1,5 @@ -i2c: - - id: i2c_pca9685 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pca9685: - frequency: 500 - address: 0x0 - -output: - - platform: pca9685 - id: pca_0 - channel: 0 - - platform: pca9685 - id: pca_1 - channel: 1 - - platform: pca9685 - id: pca_2 - channel: 2 - - platform: pca9685 - id: pca_3 - channel: 3 - - platform: pca9685 - id: pca_4 - channel: 4 - - platform: pca9685 - id: pca_5 - channel: 5 - - platform: pca9685 - id: pca_6 - channel: 6 - - platform: pca9685 - id: pca_7 - channel: 7 +<<: !include common.yaml diff --git a/tests/components/pca9685/test.esp8266-ard.yaml b/tests/components/pca9685/test.esp8266-ard.yaml index e532f323be..ee2c29ca4e 100644 --- a/tests/components/pca9685/test.esp8266-ard.yaml +++ b/tests/components/pca9685/test.esp8266-ard.yaml @@ -1,34 +1,5 @@ -i2c: - - id: i2c_pca9685 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9685: - frequency: 500 - address: 0x0 - -output: - - platform: pca9685 - id: pca_0 - channel: 0 - - platform: pca9685 - id: pca_1 - channel: 1 - - platform: pca9685 - id: pca_2 - channel: 2 - - platform: pca9685 - id: pca_3 - channel: 3 - - platform: pca9685 - id: pca_4 - channel: 4 - - platform: pca9685 - id: pca_5 - channel: 5 - - platform: pca9685 - id: pca_6 - channel: 6 - - platform: pca9685 - id: pca_7 - channel: 7 +<<: !include common.yaml diff --git a/tests/components/pca9685/test.rp2040-ard.yaml b/tests/components/pca9685/test.rp2040-ard.yaml index 0ff453e64f..ee2c29ca4e 100644 --- a/tests/components/pca9685/test.rp2040-ard.yaml +++ b/tests/components/pca9685/test.rp2040-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_pca9554 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pca9554: - - id: pca9554_hub - pin_count: 8 - address: 0x3F - -binary_sensor: - - platform: gpio - id: pca9554_input - name: PCA9554 Binary Sensor - pin: - pca9554: pca9554_hub - number: 1 - mode: INPUT - inverted: true - - platform: gpio - id: pca9554_output - pin: - pca9554: pca9554_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pcd8544/common.yaml b/tests/components/pcd8544/common.yaml new file mode 100644 index 0000000000..0fb969eeb8 --- /dev/null +++ b/tests/components/pcd8544/common.yaml @@ -0,0 +1,14 @@ +spi: + - id: spi_pcd8544 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +display: + - platform: pcd8544 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + contrast: 60 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/pcd8544/test.esp32-ard.yaml b/tests/components/pcd8544/test.esp32-ard.yaml index 20c05c407f..09e9db5a38 100644 --- a/tests/components/pcd8544/test.esp32-ard.yaml +++ b/tests/components/pcd8544/test.esp32-ard.yaml @@ -1,14 +1,9 @@ -spi: - - id: spi_pcd8544 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: pcd8544 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - contrast: 60 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp32-c3-ard.yaml b/tests/components/pcd8544/test.esp32-c3-ard.yaml index 57771d2d73..c5c932c92c 100644 --- a/tests/components/pcd8544/test.esp32-c3-ard.yaml +++ b/tests/components/pcd8544/test.esp32-c3-ard.yaml @@ -1,14 +1,9 @@ -spi: - - id: spi_pcd8544 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: pcd8544 - cs_pin: 2 - dc_pin: 3 - reset_pin: 1 - contrast: 60 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp32-c3-idf.yaml b/tests/components/pcd8544/test.esp32-c3-idf.yaml index 57771d2d73..c5c932c92c 100644 --- a/tests/components/pcd8544/test.esp32-c3-idf.yaml +++ b/tests/components/pcd8544/test.esp32-c3-idf.yaml @@ -1,14 +1,9 @@ -spi: - - id: spi_pcd8544 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: pcd8544 - cs_pin: 2 - dc_pin: 3 - reset_pin: 1 - contrast: 60 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp32-idf.yaml b/tests/components/pcd8544/test.esp32-idf.yaml index 20c05c407f..09e9db5a38 100644 --- a/tests/components/pcd8544/test.esp32-idf.yaml +++ b/tests/components/pcd8544/test.esp32-idf.yaml @@ -1,14 +1,9 @@ -spi: - - id: spi_pcd8544 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: pcd8544 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - contrast: 60 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/pcd8544/test.esp8266-ard.yaml b/tests/components/pcd8544/test.esp8266-ard.yaml index 6e6022c6d2..3f023a60eb 100644 --- a/tests/components/pcd8544/test.esp8266-ard.yaml +++ b/tests/components/pcd8544/test.esp8266-ard.yaml @@ -1,14 +1,9 @@ -spi: - - id: spi_pcd8544 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: pcd8544 - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - contrast: 60 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/pcd8544/test.rp2040-ard.yaml b/tests/components/pcd8544/test.rp2040-ard.yaml index 7181f99fb1..d7fd6ee294 100644 --- a/tests/components/pcd8544/test.rp2040-ard.yaml +++ b/tests/components/pcd8544/test.rp2040-ard.yaml @@ -1,14 +1,9 @@ -spi: - - id: spi_pcd8544 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: pcd8544 - cs_pin: 6 - dc_pin: 5 - reset_pin: 7 - contrast: 60 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/pcf85063/common.yaml b/tests/components/pcf85063/common.yaml new file mode 100644 index 0000000000..f3b68412c5 --- /dev/null +++ b/tests/components/pcf85063/common.yaml @@ -0,0 +1,12 @@ +esphome: + on_boot: + - pcf85063.read_time + - pcf85063.write_time + +i2c: + - id: i2c_pcf85063 + scl: ${scl_pin} + sda: ${sda_pin} + +time: + - platform: pcf85063 diff --git a/tests/components/pcf85063/test.esp32-ard.yaml b/tests/components/pcf85063/test.esp32-ard.yaml index 9cce415103..63c3bd6afd 100644 --- a/tests/components/pcf85063/test.esp32-ard.yaml +++ b/tests/components/pcf85063/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf85063.read_time - - pcf85063.write_time +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_pcf85063 - scl: 16 - sda: 17 - -time: - - platform: pcf85063 +<<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp32-c3-ard.yaml b/tests/components/pcf85063/test.esp32-c3-ard.yaml index 9e1a3da81e..ee2c29ca4e 100644 --- a/tests/components/pcf85063/test.esp32-c3-ard.yaml +++ b/tests/components/pcf85063/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf85063.read_time - - pcf85063.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf85063 - scl: 5 - sda: 4 - -time: - - platform: pcf85063 +<<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp32-c3-idf.yaml b/tests/components/pcf85063/test.esp32-c3-idf.yaml index 9e1a3da81e..ee2c29ca4e 100644 --- a/tests/components/pcf85063/test.esp32-c3-idf.yaml +++ b/tests/components/pcf85063/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf85063.read_time - - pcf85063.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf85063 - scl: 5 - sda: 4 - -time: - - platform: pcf85063 +<<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp32-idf.yaml b/tests/components/pcf85063/test.esp32-idf.yaml index 9cce415103..63c3bd6afd 100644 --- a/tests/components/pcf85063/test.esp32-idf.yaml +++ b/tests/components/pcf85063/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf85063.read_time - - pcf85063.write_time +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_pcf85063 - scl: 16 - sda: 17 - -time: - - platform: pcf85063 +<<: !include common.yaml diff --git a/tests/components/pcf85063/test.esp8266-ard.yaml b/tests/components/pcf85063/test.esp8266-ard.yaml index 9e1a3da81e..ee2c29ca4e 100644 --- a/tests/components/pcf85063/test.esp8266-ard.yaml +++ b/tests/components/pcf85063/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf85063.read_time - - pcf85063.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf85063 - scl: 5 - sda: 4 - -time: - - platform: pcf85063 +<<: !include common.yaml diff --git a/tests/components/pcf85063/test.rp2040-ard.yaml b/tests/components/pcf85063/test.rp2040-ard.yaml index 9e1a3da81e..ee2c29ca4e 100644 --- a/tests/components/pcf85063/test.rp2040-ard.yaml +++ b/tests/components/pcf85063/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf85063.read_time - - pcf85063.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf85063 - scl: 5 - sda: 4 - -time: - - platform: pcf85063 +<<: !include common.yaml diff --git a/tests/components/pcf8563/common.yaml b/tests/components/pcf8563/common.yaml new file mode 100644 index 0000000000..30be6b893c --- /dev/null +++ b/tests/components/pcf8563/common.yaml @@ -0,0 +1,12 @@ +esphome: + on_boot: + - pcf8563.read_time + - pcf8563.write_time + +i2c: + - id: i2c_pcf8563 + scl: ${scl_pin} + sda: ${sda_pin} + +time: + - platform: pcf8563 diff --git a/tests/components/pcf8563/test.esp32-ard.yaml b/tests/components/pcf8563/test.esp32-ard.yaml index e95b487b19..63c3bd6afd 100644 --- a/tests/components/pcf8563/test.esp32-ard.yaml +++ b/tests/components/pcf8563/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf8563.read_time - - pcf8563.write_time +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_pcf8563 - scl: 16 - sda: 17 - -time: - - platform: pcf8563 +<<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp32-c3-ard.yaml b/tests/components/pcf8563/test.esp32-c3-ard.yaml index f91a465e0f..ee2c29ca4e 100644 --- a/tests/components/pcf8563/test.esp32-c3-ard.yaml +++ b/tests/components/pcf8563/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf8563.read_time - - pcf8563.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 - -time: - - platform: pcf8563 +<<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp32-c3-idf.yaml b/tests/components/pcf8563/test.esp32-c3-idf.yaml index f91a465e0f..ee2c29ca4e 100644 --- a/tests/components/pcf8563/test.esp32-c3-idf.yaml +++ b/tests/components/pcf8563/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf8563.read_time - - pcf8563.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 - -time: - - platform: pcf8563 +<<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp32-idf.yaml b/tests/components/pcf8563/test.esp32-idf.yaml index e95b487b19..63c3bd6afd 100644 --- a/tests/components/pcf8563/test.esp32-idf.yaml +++ b/tests/components/pcf8563/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf8563.read_time - - pcf8563.write_time +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_pcf8563 - scl: 16 - sda: 17 - -time: - - platform: pcf8563 +<<: !include common.yaml diff --git a/tests/components/pcf8563/test.esp8266-ard.yaml b/tests/components/pcf8563/test.esp8266-ard.yaml index f91a465e0f..ee2c29ca4e 100644 --- a/tests/components/pcf8563/test.esp8266-ard.yaml +++ b/tests/components/pcf8563/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf8563.read_time - - pcf8563.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 - -time: - - platform: pcf8563 +<<: !include common.yaml diff --git a/tests/components/pcf8563/test.rp2040-ard.yaml b/tests/components/pcf8563/test.rp2040-ard.yaml index f91a465e0f..ee2c29ca4e 100644 --- a/tests/components/pcf8563/test.rp2040-ard.yaml +++ b/tests/components/pcf8563/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -esphome: - on_boot: - - pcf8563.read_time - - pcf8563.write_time +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 - -time: - - platform: pcf8563 +<<: !include common.yaml diff --git a/tests/components/pcf8574/common.yaml b/tests/components/pcf8574/common.yaml new file mode 100644 index 0000000000..f5fcfad64a --- /dev/null +++ b/tests/components/pcf8574/common.yaml @@ -0,0 +1,28 @@ +i2c: + - id: i2c_pcf8574 + scl: ${scl_pin} + sda: ${sda_pin} + +pcf8574: + - id: pcf8574_hub + address: 0x21 + pcf8575: false + +binary_sensor: + - platform: gpio + id: pcf8574_binary_sensor + name: PCF Binary Sensor + pin: + pcf8574: pcf8574_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: pcf8574_output + pin: + pcf8574: pcf8574_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/pcf8574/test.esp32-ard.yaml b/tests/components/pcf8574/test.esp32-ard.yaml index aeed55f4fe..63c3bd6afd 100644 --- a/tests/components/pcf8574/test.esp32-ard.yaml +++ b/tests/components/pcf8574/test.esp32-ard.yaml @@ -1,28 +1,5 @@ -i2c: - - id: i2c_pcf8563 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pcf8574: - - id: pcf8574_hub - address: 0x21 - pcf8575: false - -binary_sensor: - - platform: gpio - id: pcf8574_binary_sensor - name: PCF Binary Sensor - pin: - pcf8574: pcf8574_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: pcf8574_output - pin: - pcf8574: pcf8574_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp32-c3-ard.yaml b/tests/components/pcf8574/test.esp32-c3-ard.yaml index 551e425892..ee2c29ca4e 100644 --- a/tests/components/pcf8574/test.esp32-c3-ard.yaml +++ b/tests/components/pcf8574/test.esp32-c3-ard.yaml @@ -1,28 +1,5 @@ -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pcf8574: - - id: pcf8574_hub - address: 0x21 - pcf8575: false - -binary_sensor: - - platform: gpio - id: pcf8574_binary_sensor - name: PCF Binary Sensor - pin: - pcf8574: pcf8574_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: pcf8574_output - pin: - pcf8574: pcf8574_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp32-c3-idf.yaml b/tests/components/pcf8574/test.esp32-c3-idf.yaml index 551e425892..ee2c29ca4e 100644 --- a/tests/components/pcf8574/test.esp32-c3-idf.yaml +++ b/tests/components/pcf8574/test.esp32-c3-idf.yaml @@ -1,28 +1,5 @@ -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pcf8574: - - id: pcf8574_hub - address: 0x21 - pcf8575: false - -binary_sensor: - - platform: gpio - id: pcf8574_binary_sensor - name: PCF Binary Sensor - pin: - pcf8574: pcf8574_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: pcf8574_output - pin: - pcf8574: pcf8574_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp32-idf.yaml b/tests/components/pcf8574/test.esp32-idf.yaml index aeed55f4fe..63c3bd6afd 100644 --- a/tests/components/pcf8574/test.esp32-idf.yaml +++ b/tests/components/pcf8574/test.esp32-idf.yaml @@ -1,28 +1,5 @@ -i2c: - - id: i2c_pcf8563 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pcf8574: - - id: pcf8574_hub - address: 0x21 - pcf8575: false - -binary_sensor: - - platform: gpio - id: pcf8574_binary_sensor - name: PCF Binary Sensor - pin: - pcf8574: pcf8574_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: pcf8574_output - pin: - pcf8574: pcf8574_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pcf8574/test.esp8266-ard.yaml b/tests/components/pcf8574/test.esp8266-ard.yaml index 551e425892..ee2c29ca4e 100644 --- a/tests/components/pcf8574/test.esp8266-ard.yaml +++ b/tests/components/pcf8574/test.esp8266-ard.yaml @@ -1,28 +1,5 @@ -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pcf8574: - - id: pcf8574_hub - address: 0x21 - pcf8575: false - -binary_sensor: - - platform: gpio - id: pcf8574_binary_sensor - name: PCF Binary Sensor - pin: - pcf8574: pcf8574_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: pcf8574_output - pin: - pcf8574: pcf8574_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pcf8574/test.rp2040-ard.yaml b/tests/components/pcf8574/test.rp2040-ard.yaml index 551e425892..ee2c29ca4e 100644 --- a/tests/components/pcf8574/test.rp2040-ard.yaml +++ b/tests/components/pcf8574/test.rp2040-ard.yaml @@ -1,28 +1,5 @@ -i2c: - - id: i2c_pcf8563 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pcf8574: - - id: pcf8574_hub - address: 0x21 - pcf8575: false - -binary_sensor: - - platform: gpio - id: pcf8574_binary_sensor - name: PCF Binary Sensor - pin: - pcf8574: pcf8574_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: pcf8574_output - pin: - pcf8574: pcf8574_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/pipsolar/common.yaml b/tests/components/pipsolar/common.yaml new file mode 100644 index 0000000000..64e2c0476d --- /dev/null +++ b/tests/components/pipsolar/common.yaml @@ -0,0 +1,249 @@ +esphome: + on_boot: + then: + - output.pipsolar.set_level: + id: inverter0_battery_recharge_voltage_out + value: 48.0 + +uart: + - id: uart_pipsolar + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +pipsolar: + id: inverter0 + +binary_sensor: + - platform: pipsolar + pipsolar_id: inverter0 + add_sbu_priority_version: + id: inverter0_add_sbu_priority_version + name: inverter0_add_sbu_priority_version + configuration_status: + id: inverter0_configuration_status + name: inverter0_configuration_status + scc_firmware_version: + id: inverter0_scc_firmware_version + name: inverter0_scc_firmware_version + load_status: + id: inverter0_load_status + name: inverter0_load_status + battery_voltage_to_steady_while_charging: + id: inverter0_battery_voltage_to_steady_while_charging + name: inverter0_battery_voltage_to_steady_while_charging + charging_status: + id: inverter0_charging_status + name: inverter0_charging_status + scc_charging_status: + id: inverter0_scc_charging_status + name: inverter0_scc_charging_status + ac_charging_status: + id: inverter0_ac_charging_status + name: inverter0_ac_charging_status + charging_to_floating_mode: + id: inverter0_charging_to_floating_mode + name: inverter0_charging_to_floating_mode + switch_on: + id: inverter0_switch_on + name: inverter0_switch_on + dustproof_installed: + id: inverter0_dustproof_installed + name: inverter0_dustproof_installed + silence_buzzer_open_buzzer: + id: inverter0_silence_buzzer_open_buzzer + name: inverter0_silence_buzzer_open_buzzer + overload_bypass_function: + id: inverter0_overload_bypass_function + name: inverter0_overload_bypass_function + lcd_escape_to_default: + id: inverter0_lcd_escape_to_default + name: inverter0_lcd_escape_to_default + overload_restart_function: + id: inverter0_overload_restart_function + name: inverter0_overload_restart_function + over_temperature_restart_function: + id: inverter0_over_temperature_restart_function + name: inverter0_over_temperature_restart_function + backlight_on: + id: inverter0_backlight_on + name: inverter0_backlight_on + +output: + - platform: pipsolar + pipsolar_id: inverter0 + battery_recharge_voltage: + id: inverter0_battery_recharge_voltage_out + +sensor: + - platform: pipsolar + pipsolar_id: inverter0 + grid_rating_voltage: + id: inverter0_grid_rating_voltage + name: inverter0_grid_rating_voltage + grid_rating_current: + id: inverter0_grid_rating_current + name: inverter0_grid_rating_current + ac_output_rating_voltage: + id: inverter0_ac_output_rating_voltage + name: inverter0_ac_output_rating_voltage + ac_output_rating_frequency: + id: inverter0_ac_output_rating_frequency + name: inverter0_ac_output_rating_frequency + ac_output_rating_current: + id: inverter0_ac_output_rating_current + name: inverter0_ac_output_rating_current + ac_output_rating_apparent_power: + id: inverter0_ac_output_rating_apparent_power + name: inverter0_ac_output_rating_apparent_power + ac_output_rating_active_power: + id: inverter0_ac_output_rating_active_power + name: inverter0_ac_output_rating_active_power + battery_rating_voltage: + id: inverter0_battery_rating_voltage + name: inverter0_battery_rating_voltage + battery_recharge_voltage: + id: inverter0_battery_recharge_voltage + name: inverter0_battery_recharge_voltage + battery_under_voltage: + id: inverter0_battery_under_voltage + name: inverter0_battery_under_voltage + battery_bulk_voltage: + id: inverter0_battery_bulk_voltage + name: inverter0_battery_bulk_voltage + battery_float_voltage: + id: inverter0_battery_float_voltage + name: inverter0_battery_float_voltage + battery_type: + id: inverter0_battery_type + name: inverter0_battery_type + current_max_ac_charging_current: + id: inverter0_current_max_ac_charging_current + name: inverter0_current_max_ac_charging_current + current_max_charging_current: + id: inverter0_current_max_charging_current + name: inverter0_current_max_charging_current + input_voltage_range: + id: inverter0_input_voltage_range + name: inverter0_input_voltage_range + output_source_priority: + id: inverter0_output_source_priority + name: inverter0_output_source_priority + charger_source_priority: + id: inverter0_charger_source_priority + name: inverter0_charger_source_priority + parallel_max_num: + id: inverter0_parallel_max_num + name: inverter0_parallel_max_num + machine_type: + id: inverter0_machine_type + name: inverter0_machine_type + topology: + id: inverter0_topology + name: inverter0_topology + output_mode: + id: inverter0_output_mode + name: inverter0_output_mode + battery_redischarge_voltage: + id: inverter0_battery_redischarge_voltage + name: inverter0_battery_redischarge_voltage + pv_ok_condition_for_parallel: + id: inverter0_pv_ok_condition_for_parallel + name: inverter0_pv_ok_condition_for_parallel + pv_power_balance: + id: inverter0_pv_power_balance + name: inverter0_pv_power_balance + grid_voltage: + id: inverter0_grid_voltage + name: inverter0_grid_voltage + grid_frequency: + id: inverter0_grid_frequency + name: inverter0_grid_frequency + ac_output_voltage: + id: inverter0_ac_output_voltage + name: inverter0_ac_output_voltage + ac_output_frequency: + id: inverter0_ac_output_frequency + name: inverter0_ac_output_frequency + ac_output_apparent_power: + id: inverter0_ac_output_apparent_power + name: inverter0_ac_output_apparent_power + ac_output_active_power: + id: inverter0_ac_output_active_power + name: inverter0_ac_output_active_power + output_load_percent: + id: inverter0_output_load_percent + name: inverter0_output_load_percent + bus_voltage: + id: inverter0_bus_voltage + name: inverter0_bus_voltage + battery_voltage: + id: inverter0_battery_voltage + name: inverter0_battery_voltage + battery_charging_current: + id: inverter0_battery_charging_current + name: inverter0_battery_charging_current + battery_capacity_percent: + id: inverter0_battery_capacity_percent + name: inverter0_battery_capacity_percent + inverter_heat_sink_temperature: + id: inverter0_inverter_heat_sink_temperature + name: inverter0_inverter_heat_sink_temperature + pv_input_current_for_battery: + id: inverter0_pv_input_current_for_battery + name: inverter0_pv_input_current_for_battery + pv_input_voltage: + id: inverter0_pv_input_voltage + name: inverter0_pv_input_voltage + battery_voltage_scc: + id: inverter0_battery_voltage_scc + name: inverter0_battery_voltage_scc + battery_discharge_current: + id: inverter0_battery_discharge_current + name: inverter0_battery_discharge_current + battery_voltage_offset_for_fans_on: + id: inverter0_battery_voltage_offset_for_fans_on + name: inverter0_battery_voltage_offset_for_fans_on + eeprom_version: + id: inverter0_eeprom_version + name: inverter0_eeprom_version + pv_charging_power: + id: inverter0_pv_charging_power + name: inverter0_pv_charging_power + +switch: + - platform: pipsolar + pipsolar_id: inverter0 + output_source_priority_utility: + name: inverter0_output_source_priority_utility + output_source_priority_solar: + name: inverter0_output_source_priority_solar + output_source_priority_battery: + name: inverter0_output_source_priority_battery + output_source_priority_hybrid: + name: inverter0_output_source_priority_hybrid + input_voltage_range: + name: inverter0_input_voltage_range + pv_ok_condition_for_parallel: + name: inverter0_pv_ok_condition_for_parallel + pv_power_balance: + name: inverter0_pv_power_balance + +text_sensor: + - platform: pipsolar + pipsolar_id: inverter0 + device_mode: + id: inverter0_device_mode + name: inverter0_device_mode + last_qpigs: + id: inverter0_last_qpigs + name: inverter0_last_qpigs + last_qpiri: + id: inverter0_last_qpiri + name: inverter0_last_qpiri + last_qmod: + id: inverter0_last_qmod + name: inverter0_last_qmod + last_qflag: + id: inverter0_last_qflag + name: inverter0_last_qflag diff --git a/tests/components/pipsolar/test.esp32-ard.yaml b/tests/components/pipsolar/test.esp32-ard.yaml index b7a7e0cbd9..f486544afa 100644 --- a/tests/components/pipsolar/test.esp32-ard.yaml +++ b/tests/components/pipsolar/test.esp32-ard.yaml @@ -1,249 +1,5 @@ -esphome: - on_boot: - then: - - output.pipsolar.set_level: - id: inverter0_battery_recharge_voltage_out - value: 48.0 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_pipsolar - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 - -pipsolar: - id: inverter0 - -binary_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - add_sbu_priority_version: - id: inverter0_add_sbu_priority_version - name: inverter0_add_sbu_priority_version - configuration_status: - id: inverter0_configuration_status - name: inverter0_configuration_status - scc_firmware_version: - id: inverter0_scc_firmware_version - name: inverter0_scc_firmware_version - load_status: - id: inverter0_load_status - name: inverter0_load_status - battery_voltage_to_steady_while_charging: - id: inverter0_battery_voltage_to_steady_while_charging - name: inverter0_battery_voltage_to_steady_while_charging - charging_status: - id: inverter0_charging_status - name: inverter0_charging_status - scc_charging_status: - id: inverter0_scc_charging_status - name: inverter0_scc_charging_status - ac_charging_status: - id: inverter0_ac_charging_status - name: inverter0_ac_charging_status - charging_to_floating_mode: - id: inverter0_charging_to_floating_mode - name: inverter0_charging_to_floating_mode - switch_on: - id: inverter0_switch_on - name: inverter0_switch_on - dustproof_installed: - id: inverter0_dustproof_installed - name: inverter0_dustproof_installed - silence_buzzer_open_buzzer: - id: inverter0_silence_buzzer_open_buzzer - name: inverter0_silence_buzzer_open_buzzer - overload_bypass_function: - id: inverter0_overload_bypass_function - name: inverter0_overload_bypass_function - lcd_escape_to_default: - id: inverter0_lcd_escape_to_default - name: inverter0_lcd_escape_to_default - overload_restart_function: - id: inverter0_overload_restart_function - name: inverter0_overload_restart_function - over_temperature_restart_function: - id: inverter0_over_temperature_restart_function - name: inverter0_over_temperature_restart_function - backlight_on: - id: inverter0_backlight_on - name: inverter0_backlight_on - -output: - - platform: pipsolar - pipsolar_id: inverter0 - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage_out - -sensor: - - platform: pipsolar - pipsolar_id: inverter0 - grid_rating_voltage: - id: inverter0_grid_rating_voltage - name: inverter0_grid_rating_voltage - grid_rating_current: - id: inverter0_grid_rating_current - name: inverter0_grid_rating_current - ac_output_rating_voltage: - id: inverter0_ac_output_rating_voltage - name: inverter0_ac_output_rating_voltage - ac_output_rating_frequency: - id: inverter0_ac_output_rating_frequency - name: inverter0_ac_output_rating_frequency - ac_output_rating_current: - id: inverter0_ac_output_rating_current - name: inverter0_ac_output_rating_current - ac_output_rating_apparent_power: - id: inverter0_ac_output_rating_apparent_power - name: inverter0_ac_output_rating_apparent_power - ac_output_rating_active_power: - id: inverter0_ac_output_rating_active_power - name: inverter0_ac_output_rating_active_power - battery_rating_voltage: - id: inverter0_battery_rating_voltage - name: inverter0_battery_rating_voltage - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage - name: inverter0_battery_recharge_voltage - battery_under_voltage: - id: inverter0_battery_under_voltage - name: inverter0_battery_under_voltage - battery_bulk_voltage: - id: inverter0_battery_bulk_voltage - name: inverter0_battery_bulk_voltage - battery_float_voltage: - id: inverter0_battery_float_voltage - name: inverter0_battery_float_voltage - battery_type: - id: inverter0_battery_type - name: inverter0_battery_type - current_max_ac_charging_current: - id: inverter0_current_max_ac_charging_current - name: inverter0_current_max_ac_charging_current - current_max_charging_current: - id: inverter0_current_max_charging_current - name: inverter0_current_max_charging_current - input_voltage_range: - id: inverter0_input_voltage_range - name: inverter0_input_voltage_range - output_source_priority: - id: inverter0_output_source_priority - name: inverter0_output_source_priority - charger_source_priority: - id: inverter0_charger_source_priority - name: inverter0_charger_source_priority - parallel_max_num: - id: inverter0_parallel_max_num - name: inverter0_parallel_max_num - machine_type: - id: inverter0_machine_type - name: inverter0_machine_type - topology: - id: inverter0_topology - name: inverter0_topology - output_mode: - id: inverter0_output_mode - name: inverter0_output_mode - battery_redischarge_voltage: - id: inverter0_battery_redischarge_voltage - name: inverter0_battery_redischarge_voltage - pv_ok_condition_for_parallel: - id: inverter0_pv_ok_condition_for_parallel - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - id: inverter0_pv_power_balance - name: inverter0_pv_power_balance - grid_voltage: - id: inverter0_grid_voltage - name: inverter0_grid_voltage - grid_frequency: - id: inverter0_grid_frequency - name: inverter0_grid_frequency - ac_output_voltage: - id: inverter0_ac_output_voltage - name: inverter0_ac_output_voltage - ac_output_frequency: - id: inverter0_ac_output_frequency - name: inverter0_ac_output_frequency - ac_output_apparent_power: - id: inverter0_ac_output_apparent_power - name: inverter0_ac_output_apparent_power - ac_output_active_power: - id: inverter0_ac_output_active_power - name: inverter0_ac_output_active_power - output_load_percent: - id: inverter0_output_load_percent - name: inverter0_output_load_percent - bus_voltage: - id: inverter0_bus_voltage - name: inverter0_bus_voltage - battery_voltage: - id: inverter0_battery_voltage - name: inverter0_battery_voltage - battery_charging_current: - id: inverter0_battery_charging_current - name: inverter0_battery_charging_current - battery_capacity_percent: - id: inverter0_battery_capacity_percent - name: inverter0_battery_capacity_percent - inverter_heat_sink_temperature: - id: inverter0_inverter_heat_sink_temperature - name: inverter0_inverter_heat_sink_temperature - pv_input_current_for_battery: - id: inverter0_pv_input_current_for_battery - name: inverter0_pv_input_current_for_battery - pv_input_voltage: - id: inverter0_pv_input_voltage - name: inverter0_pv_input_voltage - battery_voltage_scc: - id: inverter0_battery_voltage_scc - name: inverter0_battery_voltage_scc - battery_discharge_current: - id: inverter0_battery_discharge_current - name: inverter0_battery_discharge_current - battery_voltage_offset_for_fans_on: - id: inverter0_battery_voltage_offset_for_fans_on - name: inverter0_battery_voltage_offset_for_fans_on - eeprom_version: - id: inverter0_eeprom_version - name: inverter0_eeprom_version - pv_charging_power: - id: inverter0_pv_charging_power - name: inverter0_pv_charging_power - -switch: - - platform: pipsolar - pipsolar_id: inverter0 - output_source_priority_utility: - name: inverter0_output_source_priority_utility - output_source_priority_solar: - name: inverter0_output_source_priority_solar - output_source_priority_battery: - name: inverter0_output_source_priority_battery - output_source_priority_hybrid: - name: inverter0_output_source_priority_hybrid - input_voltage_range: - name: inverter0_input_voltage_range - pv_ok_condition_for_parallel: - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - name: inverter0_pv_power_balance - -text_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - device_mode: - id: inverter0_device_mode - name: inverter0_device_mode - last_qpigs: - id: inverter0_last_qpigs - name: inverter0_last_qpigs - last_qpiri: - id: inverter0_last_qpiri - name: inverter0_last_qpiri - last_qmod: - id: inverter0_last_qmod - name: inverter0_last_qmod - last_qflag: - id: inverter0_last_qflag - name: inverter0_last_qflag +<<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-c3-ard.yaml b/tests/components/pipsolar/test.esp32-c3-ard.yaml index 83d7070669..b516342f3b 100644 --- a/tests/components/pipsolar/test.esp32-c3-ard.yaml +++ b/tests/components/pipsolar/test.esp32-c3-ard.yaml @@ -1,249 +1,5 @@ -esphome: - on_boot: - then: - - output.pipsolar.set_level: - id: inverter0_battery_recharge_voltage_out - value: 48.0 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pipsolar - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -pipsolar: - id: inverter0 - -binary_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - add_sbu_priority_version: - id: inverter0_add_sbu_priority_version - name: inverter0_add_sbu_priority_version - configuration_status: - id: inverter0_configuration_status - name: inverter0_configuration_status - scc_firmware_version: - id: inverter0_scc_firmware_version - name: inverter0_scc_firmware_version - load_status: - id: inverter0_load_status - name: inverter0_load_status - battery_voltage_to_steady_while_charging: - id: inverter0_battery_voltage_to_steady_while_charging - name: inverter0_battery_voltage_to_steady_while_charging - charging_status: - id: inverter0_charging_status - name: inverter0_charging_status - scc_charging_status: - id: inverter0_scc_charging_status - name: inverter0_scc_charging_status - ac_charging_status: - id: inverter0_ac_charging_status - name: inverter0_ac_charging_status - charging_to_floating_mode: - id: inverter0_charging_to_floating_mode - name: inverter0_charging_to_floating_mode - switch_on: - id: inverter0_switch_on - name: inverter0_switch_on - dustproof_installed: - id: inverter0_dustproof_installed - name: inverter0_dustproof_installed - silence_buzzer_open_buzzer: - id: inverter0_silence_buzzer_open_buzzer - name: inverter0_silence_buzzer_open_buzzer - overload_bypass_function: - id: inverter0_overload_bypass_function - name: inverter0_overload_bypass_function - lcd_escape_to_default: - id: inverter0_lcd_escape_to_default - name: inverter0_lcd_escape_to_default - overload_restart_function: - id: inverter0_overload_restart_function - name: inverter0_overload_restart_function - over_temperature_restart_function: - id: inverter0_over_temperature_restart_function - name: inverter0_over_temperature_restart_function - backlight_on: - id: inverter0_backlight_on - name: inverter0_backlight_on - -output: - - platform: pipsolar - pipsolar_id: inverter0 - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage_out - -sensor: - - platform: pipsolar - pipsolar_id: inverter0 - grid_rating_voltage: - id: inverter0_grid_rating_voltage - name: inverter0_grid_rating_voltage - grid_rating_current: - id: inverter0_grid_rating_current - name: inverter0_grid_rating_current - ac_output_rating_voltage: - id: inverter0_ac_output_rating_voltage - name: inverter0_ac_output_rating_voltage - ac_output_rating_frequency: - id: inverter0_ac_output_rating_frequency - name: inverter0_ac_output_rating_frequency - ac_output_rating_current: - id: inverter0_ac_output_rating_current - name: inverter0_ac_output_rating_current - ac_output_rating_apparent_power: - id: inverter0_ac_output_rating_apparent_power - name: inverter0_ac_output_rating_apparent_power - ac_output_rating_active_power: - id: inverter0_ac_output_rating_active_power - name: inverter0_ac_output_rating_active_power - battery_rating_voltage: - id: inverter0_battery_rating_voltage - name: inverter0_battery_rating_voltage - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage - name: inverter0_battery_recharge_voltage - battery_under_voltage: - id: inverter0_battery_under_voltage - name: inverter0_battery_under_voltage - battery_bulk_voltage: - id: inverter0_battery_bulk_voltage - name: inverter0_battery_bulk_voltage - battery_float_voltage: - id: inverter0_battery_float_voltage - name: inverter0_battery_float_voltage - battery_type: - id: inverter0_battery_type - name: inverter0_battery_type - current_max_ac_charging_current: - id: inverter0_current_max_ac_charging_current - name: inverter0_current_max_ac_charging_current - current_max_charging_current: - id: inverter0_current_max_charging_current - name: inverter0_current_max_charging_current - input_voltage_range: - id: inverter0_input_voltage_range - name: inverter0_input_voltage_range - output_source_priority: - id: inverter0_output_source_priority - name: inverter0_output_source_priority - charger_source_priority: - id: inverter0_charger_source_priority - name: inverter0_charger_source_priority - parallel_max_num: - id: inverter0_parallel_max_num - name: inverter0_parallel_max_num - machine_type: - id: inverter0_machine_type - name: inverter0_machine_type - topology: - id: inverter0_topology - name: inverter0_topology - output_mode: - id: inverter0_output_mode - name: inverter0_output_mode - battery_redischarge_voltage: - id: inverter0_battery_redischarge_voltage - name: inverter0_battery_redischarge_voltage - pv_ok_condition_for_parallel: - id: inverter0_pv_ok_condition_for_parallel - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - id: inverter0_pv_power_balance - name: inverter0_pv_power_balance - grid_voltage: - id: inverter0_grid_voltage - name: inverter0_grid_voltage - grid_frequency: - id: inverter0_grid_frequency - name: inverter0_grid_frequency - ac_output_voltage: - id: inverter0_ac_output_voltage - name: inverter0_ac_output_voltage - ac_output_frequency: - id: inverter0_ac_output_frequency - name: inverter0_ac_output_frequency - ac_output_apparent_power: - id: inverter0_ac_output_apparent_power - name: inverter0_ac_output_apparent_power - ac_output_active_power: - id: inverter0_ac_output_active_power - name: inverter0_ac_output_active_power - output_load_percent: - id: inverter0_output_load_percent - name: inverter0_output_load_percent - bus_voltage: - id: inverter0_bus_voltage - name: inverter0_bus_voltage - battery_voltage: - id: inverter0_battery_voltage - name: inverter0_battery_voltage - battery_charging_current: - id: inverter0_battery_charging_current - name: inverter0_battery_charging_current - battery_capacity_percent: - id: inverter0_battery_capacity_percent - name: inverter0_battery_capacity_percent - inverter_heat_sink_temperature: - id: inverter0_inverter_heat_sink_temperature - name: inverter0_inverter_heat_sink_temperature - pv_input_current_for_battery: - id: inverter0_pv_input_current_for_battery - name: inverter0_pv_input_current_for_battery - pv_input_voltage: - id: inverter0_pv_input_voltage - name: inverter0_pv_input_voltage - battery_voltage_scc: - id: inverter0_battery_voltage_scc - name: inverter0_battery_voltage_scc - battery_discharge_current: - id: inverter0_battery_discharge_current - name: inverter0_battery_discharge_current - battery_voltage_offset_for_fans_on: - id: inverter0_battery_voltage_offset_for_fans_on - name: inverter0_battery_voltage_offset_for_fans_on - eeprom_version: - id: inverter0_eeprom_version - name: inverter0_eeprom_version - pv_charging_power: - id: inverter0_pv_charging_power - name: inverter0_pv_charging_power - -switch: - - platform: pipsolar - pipsolar_id: inverter0 - output_source_priority_utility: - name: inverter0_output_source_priority_utility - output_source_priority_solar: - name: inverter0_output_source_priority_solar - output_source_priority_battery: - name: inverter0_output_source_priority_battery - output_source_priority_hybrid: - name: inverter0_output_source_priority_hybrid - input_voltage_range: - name: inverter0_input_voltage_range - pv_ok_condition_for_parallel: - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - name: inverter0_pv_power_balance - -text_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - device_mode: - id: inverter0_device_mode - name: inverter0_device_mode - last_qpigs: - id: inverter0_last_qpigs - name: inverter0_last_qpigs - last_qpiri: - id: inverter0_last_qpiri - name: inverter0_last_qpiri - last_qmod: - id: inverter0_last_qmod - name: inverter0_last_qmod - last_qflag: - id: inverter0_last_qflag - name: inverter0_last_qflag +<<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-c3-idf.yaml b/tests/components/pipsolar/test.esp32-c3-idf.yaml index 83d7070669..b516342f3b 100644 --- a/tests/components/pipsolar/test.esp32-c3-idf.yaml +++ b/tests/components/pipsolar/test.esp32-c3-idf.yaml @@ -1,249 +1,5 @@ -esphome: - on_boot: - then: - - output.pipsolar.set_level: - id: inverter0_battery_recharge_voltage_out - value: 48.0 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pipsolar - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -pipsolar: - id: inverter0 - -binary_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - add_sbu_priority_version: - id: inverter0_add_sbu_priority_version - name: inverter0_add_sbu_priority_version - configuration_status: - id: inverter0_configuration_status - name: inverter0_configuration_status - scc_firmware_version: - id: inverter0_scc_firmware_version - name: inverter0_scc_firmware_version - load_status: - id: inverter0_load_status - name: inverter0_load_status - battery_voltage_to_steady_while_charging: - id: inverter0_battery_voltage_to_steady_while_charging - name: inverter0_battery_voltage_to_steady_while_charging - charging_status: - id: inverter0_charging_status - name: inverter0_charging_status - scc_charging_status: - id: inverter0_scc_charging_status - name: inverter0_scc_charging_status - ac_charging_status: - id: inverter0_ac_charging_status - name: inverter0_ac_charging_status - charging_to_floating_mode: - id: inverter0_charging_to_floating_mode - name: inverter0_charging_to_floating_mode - switch_on: - id: inverter0_switch_on - name: inverter0_switch_on - dustproof_installed: - id: inverter0_dustproof_installed - name: inverter0_dustproof_installed - silence_buzzer_open_buzzer: - id: inverter0_silence_buzzer_open_buzzer - name: inverter0_silence_buzzer_open_buzzer - overload_bypass_function: - id: inverter0_overload_bypass_function - name: inverter0_overload_bypass_function - lcd_escape_to_default: - id: inverter0_lcd_escape_to_default - name: inverter0_lcd_escape_to_default - overload_restart_function: - id: inverter0_overload_restart_function - name: inverter0_overload_restart_function - over_temperature_restart_function: - id: inverter0_over_temperature_restart_function - name: inverter0_over_temperature_restart_function - backlight_on: - id: inverter0_backlight_on - name: inverter0_backlight_on - -output: - - platform: pipsolar - pipsolar_id: inverter0 - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage_out - -sensor: - - platform: pipsolar - pipsolar_id: inverter0 - grid_rating_voltage: - id: inverter0_grid_rating_voltage - name: inverter0_grid_rating_voltage - grid_rating_current: - id: inverter0_grid_rating_current - name: inverter0_grid_rating_current - ac_output_rating_voltage: - id: inverter0_ac_output_rating_voltage - name: inverter0_ac_output_rating_voltage - ac_output_rating_frequency: - id: inverter0_ac_output_rating_frequency - name: inverter0_ac_output_rating_frequency - ac_output_rating_current: - id: inverter0_ac_output_rating_current - name: inverter0_ac_output_rating_current - ac_output_rating_apparent_power: - id: inverter0_ac_output_rating_apparent_power - name: inverter0_ac_output_rating_apparent_power - ac_output_rating_active_power: - id: inverter0_ac_output_rating_active_power - name: inverter0_ac_output_rating_active_power - battery_rating_voltage: - id: inverter0_battery_rating_voltage - name: inverter0_battery_rating_voltage - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage - name: inverter0_battery_recharge_voltage - battery_under_voltage: - id: inverter0_battery_under_voltage - name: inverter0_battery_under_voltage - battery_bulk_voltage: - id: inverter0_battery_bulk_voltage - name: inverter0_battery_bulk_voltage - battery_float_voltage: - id: inverter0_battery_float_voltage - name: inverter0_battery_float_voltage - battery_type: - id: inverter0_battery_type - name: inverter0_battery_type - current_max_ac_charging_current: - id: inverter0_current_max_ac_charging_current - name: inverter0_current_max_ac_charging_current - current_max_charging_current: - id: inverter0_current_max_charging_current - name: inverter0_current_max_charging_current - input_voltage_range: - id: inverter0_input_voltage_range - name: inverter0_input_voltage_range - output_source_priority: - id: inverter0_output_source_priority - name: inverter0_output_source_priority - charger_source_priority: - id: inverter0_charger_source_priority - name: inverter0_charger_source_priority - parallel_max_num: - id: inverter0_parallel_max_num - name: inverter0_parallel_max_num - machine_type: - id: inverter0_machine_type - name: inverter0_machine_type - topology: - id: inverter0_topology - name: inverter0_topology - output_mode: - id: inverter0_output_mode - name: inverter0_output_mode - battery_redischarge_voltage: - id: inverter0_battery_redischarge_voltage - name: inverter0_battery_redischarge_voltage - pv_ok_condition_for_parallel: - id: inverter0_pv_ok_condition_for_parallel - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - id: inverter0_pv_power_balance - name: inverter0_pv_power_balance - grid_voltage: - id: inverter0_grid_voltage - name: inverter0_grid_voltage - grid_frequency: - id: inverter0_grid_frequency - name: inverter0_grid_frequency - ac_output_voltage: - id: inverter0_ac_output_voltage - name: inverter0_ac_output_voltage - ac_output_frequency: - id: inverter0_ac_output_frequency - name: inverter0_ac_output_frequency - ac_output_apparent_power: - id: inverter0_ac_output_apparent_power - name: inverter0_ac_output_apparent_power - ac_output_active_power: - id: inverter0_ac_output_active_power - name: inverter0_ac_output_active_power - output_load_percent: - id: inverter0_output_load_percent - name: inverter0_output_load_percent - bus_voltage: - id: inverter0_bus_voltage - name: inverter0_bus_voltage - battery_voltage: - id: inverter0_battery_voltage - name: inverter0_battery_voltage - battery_charging_current: - id: inverter0_battery_charging_current - name: inverter0_battery_charging_current - battery_capacity_percent: - id: inverter0_battery_capacity_percent - name: inverter0_battery_capacity_percent - inverter_heat_sink_temperature: - id: inverter0_inverter_heat_sink_temperature - name: inverter0_inverter_heat_sink_temperature - pv_input_current_for_battery: - id: inverter0_pv_input_current_for_battery - name: inverter0_pv_input_current_for_battery - pv_input_voltage: - id: inverter0_pv_input_voltage - name: inverter0_pv_input_voltage - battery_voltage_scc: - id: inverter0_battery_voltage_scc - name: inverter0_battery_voltage_scc - battery_discharge_current: - id: inverter0_battery_discharge_current - name: inverter0_battery_discharge_current - battery_voltage_offset_for_fans_on: - id: inverter0_battery_voltage_offset_for_fans_on - name: inverter0_battery_voltage_offset_for_fans_on - eeprom_version: - id: inverter0_eeprom_version - name: inverter0_eeprom_version - pv_charging_power: - id: inverter0_pv_charging_power - name: inverter0_pv_charging_power - -switch: - - platform: pipsolar - pipsolar_id: inverter0 - output_source_priority_utility: - name: inverter0_output_source_priority_utility - output_source_priority_solar: - name: inverter0_output_source_priority_solar - output_source_priority_battery: - name: inverter0_output_source_priority_battery - output_source_priority_hybrid: - name: inverter0_output_source_priority_hybrid - input_voltage_range: - name: inverter0_input_voltage_range - pv_ok_condition_for_parallel: - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - name: inverter0_pv_power_balance - -text_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - device_mode: - id: inverter0_device_mode - name: inverter0_device_mode - last_qpigs: - id: inverter0_last_qpigs - name: inverter0_last_qpigs - last_qpiri: - id: inverter0_last_qpiri - name: inverter0_last_qpiri - last_qmod: - id: inverter0_last_qmod - name: inverter0_last_qmod - last_qflag: - id: inverter0_last_qflag - name: inverter0_last_qflag +<<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp32-idf.yaml b/tests/components/pipsolar/test.esp32-idf.yaml index b7a7e0cbd9..f486544afa 100644 --- a/tests/components/pipsolar/test.esp32-idf.yaml +++ b/tests/components/pipsolar/test.esp32-idf.yaml @@ -1,249 +1,5 @@ -esphome: - on_boot: - then: - - output.pipsolar.set_level: - id: inverter0_battery_recharge_voltage_out - value: 48.0 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_pipsolar - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 - -pipsolar: - id: inverter0 - -binary_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - add_sbu_priority_version: - id: inverter0_add_sbu_priority_version - name: inverter0_add_sbu_priority_version - configuration_status: - id: inverter0_configuration_status - name: inverter0_configuration_status - scc_firmware_version: - id: inverter0_scc_firmware_version - name: inverter0_scc_firmware_version - load_status: - id: inverter0_load_status - name: inverter0_load_status - battery_voltage_to_steady_while_charging: - id: inverter0_battery_voltage_to_steady_while_charging - name: inverter0_battery_voltage_to_steady_while_charging - charging_status: - id: inverter0_charging_status - name: inverter0_charging_status - scc_charging_status: - id: inverter0_scc_charging_status - name: inverter0_scc_charging_status - ac_charging_status: - id: inverter0_ac_charging_status - name: inverter0_ac_charging_status - charging_to_floating_mode: - id: inverter0_charging_to_floating_mode - name: inverter0_charging_to_floating_mode - switch_on: - id: inverter0_switch_on - name: inverter0_switch_on - dustproof_installed: - id: inverter0_dustproof_installed - name: inverter0_dustproof_installed - silence_buzzer_open_buzzer: - id: inverter0_silence_buzzer_open_buzzer - name: inverter0_silence_buzzer_open_buzzer - overload_bypass_function: - id: inverter0_overload_bypass_function - name: inverter0_overload_bypass_function - lcd_escape_to_default: - id: inverter0_lcd_escape_to_default - name: inverter0_lcd_escape_to_default - overload_restart_function: - id: inverter0_overload_restart_function - name: inverter0_overload_restart_function - over_temperature_restart_function: - id: inverter0_over_temperature_restart_function - name: inverter0_over_temperature_restart_function - backlight_on: - id: inverter0_backlight_on - name: inverter0_backlight_on - -output: - - platform: pipsolar - pipsolar_id: inverter0 - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage_out - -sensor: - - platform: pipsolar - pipsolar_id: inverter0 - grid_rating_voltage: - id: inverter0_grid_rating_voltage - name: inverter0_grid_rating_voltage - grid_rating_current: - id: inverter0_grid_rating_current - name: inverter0_grid_rating_current - ac_output_rating_voltage: - id: inverter0_ac_output_rating_voltage - name: inverter0_ac_output_rating_voltage - ac_output_rating_frequency: - id: inverter0_ac_output_rating_frequency - name: inverter0_ac_output_rating_frequency - ac_output_rating_current: - id: inverter0_ac_output_rating_current - name: inverter0_ac_output_rating_current - ac_output_rating_apparent_power: - id: inverter0_ac_output_rating_apparent_power - name: inverter0_ac_output_rating_apparent_power - ac_output_rating_active_power: - id: inverter0_ac_output_rating_active_power - name: inverter0_ac_output_rating_active_power - battery_rating_voltage: - id: inverter0_battery_rating_voltage - name: inverter0_battery_rating_voltage - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage - name: inverter0_battery_recharge_voltage - battery_under_voltage: - id: inverter0_battery_under_voltage - name: inverter0_battery_under_voltage - battery_bulk_voltage: - id: inverter0_battery_bulk_voltage - name: inverter0_battery_bulk_voltage - battery_float_voltage: - id: inverter0_battery_float_voltage - name: inverter0_battery_float_voltage - battery_type: - id: inverter0_battery_type - name: inverter0_battery_type - current_max_ac_charging_current: - id: inverter0_current_max_ac_charging_current - name: inverter0_current_max_ac_charging_current - current_max_charging_current: - id: inverter0_current_max_charging_current - name: inverter0_current_max_charging_current - input_voltage_range: - id: inverter0_input_voltage_range - name: inverter0_input_voltage_range - output_source_priority: - id: inverter0_output_source_priority - name: inverter0_output_source_priority - charger_source_priority: - id: inverter0_charger_source_priority - name: inverter0_charger_source_priority - parallel_max_num: - id: inverter0_parallel_max_num - name: inverter0_parallel_max_num - machine_type: - id: inverter0_machine_type - name: inverter0_machine_type - topology: - id: inverter0_topology - name: inverter0_topology - output_mode: - id: inverter0_output_mode - name: inverter0_output_mode - battery_redischarge_voltage: - id: inverter0_battery_redischarge_voltage - name: inverter0_battery_redischarge_voltage - pv_ok_condition_for_parallel: - id: inverter0_pv_ok_condition_for_parallel - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - id: inverter0_pv_power_balance - name: inverter0_pv_power_balance - grid_voltage: - id: inverter0_grid_voltage - name: inverter0_grid_voltage - grid_frequency: - id: inverter0_grid_frequency - name: inverter0_grid_frequency - ac_output_voltage: - id: inverter0_ac_output_voltage - name: inverter0_ac_output_voltage - ac_output_frequency: - id: inverter0_ac_output_frequency - name: inverter0_ac_output_frequency - ac_output_apparent_power: - id: inverter0_ac_output_apparent_power - name: inverter0_ac_output_apparent_power - ac_output_active_power: - id: inverter0_ac_output_active_power - name: inverter0_ac_output_active_power - output_load_percent: - id: inverter0_output_load_percent - name: inverter0_output_load_percent - bus_voltage: - id: inverter0_bus_voltage - name: inverter0_bus_voltage - battery_voltage: - id: inverter0_battery_voltage - name: inverter0_battery_voltage - battery_charging_current: - id: inverter0_battery_charging_current - name: inverter0_battery_charging_current - battery_capacity_percent: - id: inverter0_battery_capacity_percent - name: inverter0_battery_capacity_percent - inverter_heat_sink_temperature: - id: inverter0_inverter_heat_sink_temperature - name: inverter0_inverter_heat_sink_temperature - pv_input_current_for_battery: - id: inverter0_pv_input_current_for_battery - name: inverter0_pv_input_current_for_battery - pv_input_voltage: - id: inverter0_pv_input_voltage - name: inverter0_pv_input_voltage - battery_voltage_scc: - id: inverter0_battery_voltage_scc - name: inverter0_battery_voltage_scc - battery_discharge_current: - id: inverter0_battery_discharge_current - name: inverter0_battery_discharge_current - battery_voltage_offset_for_fans_on: - id: inverter0_battery_voltage_offset_for_fans_on - name: inverter0_battery_voltage_offset_for_fans_on - eeprom_version: - id: inverter0_eeprom_version - name: inverter0_eeprom_version - pv_charging_power: - id: inverter0_pv_charging_power - name: inverter0_pv_charging_power - -switch: - - platform: pipsolar - pipsolar_id: inverter0 - output_source_priority_utility: - name: inverter0_output_source_priority_utility - output_source_priority_solar: - name: inverter0_output_source_priority_solar - output_source_priority_battery: - name: inverter0_output_source_priority_battery - output_source_priority_hybrid: - name: inverter0_output_source_priority_hybrid - input_voltage_range: - name: inverter0_input_voltage_range - pv_ok_condition_for_parallel: - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - name: inverter0_pv_power_balance - -text_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - device_mode: - id: inverter0_device_mode - name: inverter0_device_mode - last_qpigs: - id: inverter0_last_qpigs - name: inverter0_last_qpigs - last_qpiri: - id: inverter0_last_qpiri - name: inverter0_last_qpiri - last_qmod: - id: inverter0_last_qmod - name: inverter0_last_qmod - last_qflag: - id: inverter0_last_qflag - name: inverter0_last_qflag +<<: !include common.yaml diff --git a/tests/components/pipsolar/test.esp8266-ard.yaml b/tests/components/pipsolar/test.esp8266-ard.yaml index 83d7070669..b516342f3b 100644 --- a/tests/components/pipsolar/test.esp8266-ard.yaml +++ b/tests/components/pipsolar/test.esp8266-ard.yaml @@ -1,249 +1,5 @@ -esphome: - on_boot: - then: - - output.pipsolar.set_level: - id: inverter0_battery_recharge_voltage_out - value: 48.0 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pipsolar - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -pipsolar: - id: inverter0 - -binary_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - add_sbu_priority_version: - id: inverter0_add_sbu_priority_version - name: inverter0_add_sbu_priority_version - configuration_status: - id: inverter0_configuration_status - name: inverter0_configuration_status - scc_firmware_version: - id: inverter0_scc_firmware_version - name: inverter0_scc_firmware_version - load_status: - id: inverter0_load_status - name: inverter0_load_status - battery_voltage_to_steady_while_charging: - id: inverter0_battery_voltage_to_steady_while_charging - name: inverter0_battery_voltage_to_steady_while_charging - charging_status: - id: inverter0_charging_status - name: inverter0_charging_status - scc_charging_status: - id: inverter0_scc_charging_status - name: inverter0_scc_charging_status - ac_charging_status: - id: inverter0_ac_charging_status - name: inverter0_ac_charging_status - charging_to_floating_mode: - id: inverter0_charging_to_floating_mode - name: inverter0_charging_to_floating_mode - switch_on: - id: inverter0_switch_on - name: inverter0_switch_on - dustproof_installed: - id: inverter0_dustproof_installed - name: inverter0_dustproof_installed - silence_buzzer_open_buzzer: - id: inverter0_silence_buzzer_open_buzzer - name: inverter0_silence_buzzer_open_buzzer - overload_bypass_function: - id: inverter0_overload_bypass_function - name: inverter0_overload_bypass_function - lcd_escape_to_default: - id: inverter0_lcd_escape_to_default - name: inverter0_lcd_escape_to_default - overload_restart_function: - id: inverter0_overload_restart_function - name: inverter0_overload_restart_function - over_temperature_restart_function: - id: inverter0_over_temperature_restart_function - name: inverter0_over_temperature_restart_function - backlight_on: - id: inverter0_backlight_on - name: inverter0_backlight_on - -output: - - platform: pipsolar - pipsolar_id: inverter0 - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage_out - -sensor: - - platform: pipsolar - pipsolar_id: inverter0 - grid_rating_voltage: - id: inverter0_grid_rating_voltage - name: inverter0_grid_rating_voltage - grid_rating_current: - id: inverter0_grid_rating_current - name: inverter0_grid_rating_current - ac_output_rating_voltage: - id: inverter0_ac_output_rating_voltage - name: inverter0_ac_output_rating_voltage - ac_output_rating_frequency: - id: inverter0_ac_output_rating_frequency - name: inverter0_ac_output_rating_frequency - ac_output_rating_current: - id: inverter0_ac_output_rating_current - name: inverter0_ac_output_rating_current - ac_output_rating_apparent_power: - id: inverter0_ac_output_rating_apparent_power - name: inverter0_ac_output_rating_apparent_power - ac_output_rating_active_power: - id: inverter0_ac_output_rating_active_power - name: inverter0_ac_output_rating_active_power - battery_rating_voltage: - id: inverter0_battery_rating_voltage - name: inverter0_battery_rating_voltage - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage - name: inverter0_battery_recharge_voltage - battery_under_voltage: - id: inverter0_battery_under_voltage - name: inverter0_battery_under_voltage - battery_bulk_voltage: - id: inverter0_battery_bulk_voltage - name: inverter0_battery_bulk_voltage - battery_float_voltage: - id: inverter0_battery_float_voltage - name: inverter0_battery_float_voltage - battery_type: - id: inverter0_battery_type - name: inverter0_battery_type - current_max_ac_charging_current: - id: inverter0_current_max_ac_charging_current - name: inverter0_current_max_ac_charging_current - current_max_charging_current: - id: inverter0_current_max_charging_current - name: inverter0_current_max_charging_current - input_voltage_range: - id: inverter0_input_voltage_range - name: inverter0_input_voltage_range - output_source_priority: - id: inverter0_output_source_priority - name: inverter0_output_source_priority - charger_source_priority: - id: inverter0_charger_source_priority - name: inverter0_charger_source_priority - parallel_max_num: - id: inverter0_parallel_max_num - name: inverter0_parallel_max_num - machine_type: - id: inverter0_machine_type - name: inverter0_machine_type - topology: - id: inverter0_topology - name: inverter0_topology - output_mode: - id: inverter0_output_mode - name: inverter0_output_mode - battery_redischarge_voltage: - id: inverter0_battery_redischarge_voltage - name: inverter0_battery_redischarge_voltage - pv_ok_condition_for_parallel: - id: inverter0_pv_ok_condition_for_parallel - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - id: inverter0_pv_power_balance - name: inverter0_pv_power_balance - grid_voltage: - id: inverter0_grid_voltage - name: inverter0_grid_voltage - grid_frequency: - id: inverter0_grid_frequency - name: inverter0_grid_frequency - ac_output_voltage: - id: inverter0_ac_output_voltage - name: inverter0_ac_output_voltage - ac_output_frequency: - id: inverter0_ac_output_frequency - name: inverter0_ac_output_frequency - ac_output_apparent_power: - id: inverter0_ac_output_apparent_power - name: inverter0_ac_output_apparent_power - ac_output_active_power: - id: inverter0_ac_output_active_power - name: inverter0_ac_output_active_power - output_load_percent: - id: inverter0_output_load_percent - name: inverter0_output_load_percent - bus_voltage: - id: inverter0_bus_voltage - name: inverter0_bus_voltage - battery_voltage: - id: inverter0_battery_voltage - name: inverter0_battery_voltage - battery_charging_current: - id: inverter0_battery_charging_current - name: inverter0_battery_charging_current - battery_capacity_percent: - id: inverter0_battery_capacity_percent - name: inverter0_battery_capacity_percent - inverter_heat_sink_temperature: - id: inverter0_inverter_heat_sink_temperature - name: inverter0_inverter_heat_sink_temperature - pv_input_current_for_battery: - id: inverter0_pv_input_current_for_battery - name: inverter0_pv_input_current_for_battery - pv_input_voltage: - id: inverter0_pv_input_voltage - name: inverter0_pv_input_voltage - battery_voltage_scc: - id: inverter0_battery_voltage_scc - name: inverter0_battery_voltage_scc - battery_discharge_current: - id: inverter0_battery_discharge_current - name: inverter0_battery_discharge_current - battery_voltage_offset_for_fans_on: - id: inverter0_battery_voltage_offset_for_fans_on - name: inverter0_battery_voltage_offset_for_fans_on - eeprom_version: - id: inverter0_eeprom_version - name: inverter0_eeprom_version - pv_charging_power: - id: inverter0_pv_charging_power - name: inverter0_pv_charging_power - -switch: - - platform: pipsolar - pipsolar_id: inverter0 - output_source_priority_utility: - name: inverter0_output_source_priority_utility - output_source_priority_solar: - name: inverter0_output_source_priority_solar - output_source_priority_battery: - name: inverter0_output_source_priority_battery - output_source_priority_hybrid: - name: inverter0_output_source_priority_hybrid - input_voltage_range: - name: inverter0_input_voltage_range - pv_ok_condition_for_parallel: - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - name: inverter0_pv_power_balance - -text_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - device_mode: - id: inverter0_device_mode - name: inverter0_device_mode - last_qpigs: - id: inverter0_last_qpigs - name: inverter0_last_qpigs - last_qpiri: - id: inverter0_last_qpiri - name: inverter0_last_qpiri - last_qmod: - id: inverter0_last_qmod - name: inverter0_last_qmod - last_qflag: - id: inverter0_last_qflag - name: inverter0_last_qflag +<<: !include common.yaml diff --git a/tests/components/pipsolar/test.rp2040-ard.yaml b/tests/components/pipsolar/test.rp2040-ard.yaml index 83d7070669..b516342f3b 100644 --- a/tests/components/pipsolar/test.rp2040-ard.yaml +++ b/tests/components/pipsolar/test.rp2040-ard.yaml @@ -1,249 +1,5 @@ -esphome: - on_boot: - then: - - output.pipsolar.set_level: - id: inverter0_battery_recharge_voltage_out - value: 48.0 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pipsolar - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 - -pipsolar: - id: inverter0 - -binary_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - add_sbu_priority_version: - id: inverter0_add_sbu_priority_version - name: inverter0_add_sbu_priority_version - configuration_status: - id: inverter0_configuration_status - name: inverter0_configuration_status - scc_firmware_version: - id: inverter0_scc_firmware_version - name: inverter0_scc_firmware_version - load_status: - id: inverter0_load_status - name: inverter0_load_status - battery_voltage_to_steady_while_charging: - id: inverter0_battery_voltage_to_steady_while_charging - name: inverter0_battery_voltage_to_steady_while_charging - charging_status: - id: inverter0_charging_status - name: inverter0_charging_status - scc_charging_status: - id: inverter0_scc_charging_status - name: inverter0_scc_charging_status - ac_charging_status: - id: inverter0_ac_charging_status - name: inverter0_ac_charging_status - charging_to_floating_mode: - id: inverter0_charging_to_floating_mode - name: inverter0_charging_to_floating_mode - switch_on: - id: inverter0_switch_on - name: inverter0_switch_on - dustproof_installed: - id: inverter0_dustproof_installed - name: inverter0_dustproof_installed - silence_buzzer_open_buzzer: - id: inverter0_silence_buzzer_open_buzzer - name: inverter0_silence_buzzer_open_buzzer - overload_bypass_function: - id: inverter0_overload_bypass_function - name: inverter0_overload_bypass_function - lcd_escape_to_default: - id: inverter0_lcd_escape_to_default - name: inverter0_lcd_escape_to_default - overload_restart_function: - id: inverter0_overload_restart_function - name: inverter0_overload_restart_function - over_temperature_restart_function: - id: inverter0_over_temperature_restart_function - name: inverter0_over_temperature_restart_function - backlight_on: - id: inverter0_backlight_on - name: inverter0_backlight_on - -output: - - platform: pipsolar - pipsolar_id: inverter0 - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage_out - -sensor: - - platform: pipsolar - pipsolar_id: inverter0 - grid_rating_voltage: - id: inverter0_grid_rating_voltage - name: inverter0_grid_rating_voltage - grid_rating_current: - id: inverter0_grid_rating_current - name: inverter0_grid_rating_current - ac_output_rating_voltage: - id: inverter0_ac_output_rating_voltage - name: inverter0_ac_output_rating_voltage - ac_output_rating_frequency: - id: inverter0_ac_output_rating_frequency - name: inverter0_ac_output_rating_frequency - ac_output_rating_current: - id: inverter0_ac_output_rating_current - name: inverter0_ac_output_rating_current - ac_output_rating_apparent_power: - id: inverter0_ac_output_rating_apparent_power - name: inverter0_ac_output_rating_apparent_power - ac_output_rating_active_power: - id: inverter0_ac_output_rating_active_power - name: inverter0_ac_output_rating_active_power - battery_rating_voltage: - id: inverter0_battery_rating_voltage - name: inverter0_battery_rating_voltage - battery_recharge_voltage: - id: inverter0_battery_recharge_voltage - name: inverter0_battery_recharge_voltage - battery_under_voltage: - id: inverter0_battery_under_voltage - name: inverter0_battery_under_voltage - battery_bulk_voltage: - id: inverter0_battery_bulk_voltage - name: inverter0_battery_bulk_voltage - battery_float_voltage: - id: inverter0_battery_float_voltage - name: inverter0_battery_float_voltage - battery_type: - id: inverter0_battery_type - name: inverter0_battery_type - current_max_ac_charging_current: - id: inverter0_current_max_ac_charging_current - name: inverter0_current_max_ac_charging_current - current_max_charging_current: - id: inverter0_current_max_charging_current - name: inverter0_current_max_charging_current - input_voltage_range: - id: inverter0_input_voltage_range - name: inverter0_input_voltage_range - output_source_priority: - id: inverter0_output_source_priority - name: inverter0_output_source_priority - charger_source_priority: - id: inverter0_charger_source_priority - name: inverter0_charger_source_priority - parallel_max_num: - id: inverter0_parallel_max_num - name: inverter0_parallel_max_num - machine_type: - id: inverter0_machine_type - name: inverter0_machine_type - topology: - id: inverter0_topology - name: inverter0_topology - output_mode: - id: inverter0_output_mode - name: inverter0_output_mode - battery_redischarge_voltage: - id: inverter0_battery_redischarge_voltage - name: inverter0_battery_redischarge_voltage - pv_ok_condition_for_parallel: - id: inverter0_pv_ok_condition_for_parallel - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - id: inverter0_pv_power_balance - name: inverter0_pv_power_balance - grid_voltage: - id: inverter0_grid_voltage - name: inverter0_grid_voltage - grid_frequency: - id: inverter0_grid_frequency - name: inverter0_grid_frequency - ac_output_voltage: - id: inverter0_ac_output_voltage - name: inverter0_ac_output_voltage - ac_output_frequency: - id: inverter0_ac_output_frequency - name: inverter0_ac_output_frequency - ac_output_apparent_power: - id: inverter0_ac_output_apparent_power - name: inverter0_ac_output_apparent_power - ac_output_active_power: - id: inverter0_ac_output_active_power - name: inverter0_ac_output_active_power - output_load_percent: - id: inverter0_output_load_percent - name: inverter0_output_load_percent - bus_voltage: - id: inverter0_bus_voltage - name: inverter0_bus_voltage - battery_voltage: - id: inverter0_battery_voltage - name: inverter0_battery_voltage - battery_charging_current: - id: inverter0_battery_charging_current - name: inverter0_battery_charging_current - battery_capacity_percent: - id: inverter0_battery_capacity_percent - name: inverter0_battery_capacity_percent - inverter_heat_sink_temperature: - id: inverter0_inverter_heat_sink_temperature - name: inverter0_inverter_heat_sink_temperature - pv_input_current_for_battery: - id: inverter0_pv_input_current_for_battery - name: inverter0_pv_input_current_for_battery - pv_input_voltage: - id: inverter0_pv_input_voltage - name: inverter0_pv_input_voltage - battery_voltage_scc: - id: inverter0_battery_voltage_scc - name: inverter0_battery_voltage_scc - battery_discharge_current: - id: inverter0_battery_discharge_current - name: inverter0_battery_discharge_current - battery_voltage_offset_for_fans_on: - id: inverter0_battery_voltage_offset_for_fans_on - name: inverter0_battery_voltage_offset_for_fans_on - eeprom_version: - id: inverter0_eeprom_version - name: inverter0_eeprom_version - pv_charging_power: - id: inverter0_pv_charging_power - name: inverter0_pv_charging_power - -switch: - - platform: pipsolar - pipsolar_id: inverter0 - output_source_priority_utility: - name: inverter0_output_source_priority_utility - output_source_priority_solar: - name: inverter0_output_source_priority_solar - output_source_priority_battery: - name: inverter0_output_source_priority_battery - output_source_priority_hybrid: - name: inverter0_output_source_priority_hybrid - input_voltage_range: - name: inverter0_input_voltage_range - pv_ok_condition_for_parallel: - name: inverter0_pv_ok_condition_for_parallel - pv_power_balance: - name: inverter0_pv_power_balance - -text_sensor: - - platform: pipsolar - pipsolar_id: inverter0 - device_mode: - id: inverter0_device_mode - name: inverter0_device_mode - last_qpigs: - id: inverter0_last_qpigs - name: inverter0_last_qpigs - last_qpiri: - id: inverter0_last_qpiri - name: inverter0_last_qpiri - last_qmod: - id: inverter0_last_qmod - name: inverter0_last_qmod - last_qflag: - id: inverter0_last_qflag - name: inverter0_last_qflag +<<: !include common.yaml diff --git a/tests/components/pm1006/common.yaml b/tests/components/pm1006/common.yaml new file mode 100644 index 0000000000..4e3e880f4e --- /dev/null +++ b/tests/components/pm1006/common.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_pm1006 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: pm1006 + pm_2_5: + name: Particulate Matter 2.5µm Concentration diff --git a/tests/components/pm1006/test.esp32-ard.yaml b/tests/components/pm1006/test.esp32-ard.yaml index 635af37b25..f486544afa 100644 --- a/tests/components/pm1006/test.esp32-ard.yaml +++ b/tests/components/pm1006/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_pm1006 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: pm1006 - pm_2_5: - name: Particulate Matter 2.5µm Concentration +<<: !include common.yaml diff --git a/tests/components/pm1006/test.esp32-c3-ard.yaml b/tests/components/pm1006/test.esp32-c3-ard.yaml index 15ee077f3e..b516342f3b 100644 --- a/tests/components/pm1006/test.esp32-c3-ard.yaml +++ b/tests/components/pm1006/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_pm1006 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pm1006 - pm_2_5: - name: Particulate Matter 2.5µm Concentration +<<: !include common.yaml diff --git a/tests/components/pm1006/test.esp32-c3-idf.yaml b/tests/components/pm1006/test.esp32-c3-idf.yaml index 15ee077f3e..b516342f3b 100644 --- a/tests/components/pm1006/test.esp32-c3-idf.yaml +++ b/tests/components/pm1006/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_pm1006 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pm1006 - pm_2_5: - name: Particulate Matter 2.5µm Concentration +<<: !include common.yaml diff --git a/tests/components/pm1006/test.esp32-idf.yaml b/tests/components/pm1006/test.esp32-idf.yaml index 635af37b25..f486544afa 100644 --- a/tests/components/pm1006/test.esp32-idf.yaml +++ b/tests/components/pm1006/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_pm1006 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: pm1006 - pm_2_5: - name: Particulate Matter 2.5µm Concentration +<<: !include common.yaml diff --git a/tests/components/pm1006/test.esp8266-ard.yaml b/tests/components/pm1006/test.esp8266-ard.yaml index 15ee077f3e..b516342f3b 100644 --- a/tests/components/pm1006/test.esp8266-ard.yaml +++ b/tests/components/pm1006/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_pm1006 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pm1006 - pm_2_5: - name: Particulate Matter 2.5µm Concentration +<<: !include common.yaml diff --git a/tests/components/pm1006/test.rp2040-ard.yaml b/tests/components/pm1006/test.rp2040-ard.yaml index 15ee077f3e..b516342f3b 100644 --- a/tests/components/pm1006/test.rp2040-ard.yaml +++ b/tests/components/pm1006/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_pm1006 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pm1006 - pm_2_5: - name: Particulate Matter 2.5µm Concentration +<<: !include common.yaml diff --git a/tests/components/pmsa003i/common.yaml b/tests/components/pmsa003i/common.yaml new file mode 100644 index 0000000000..95e62da694 --- /dev/null +++ b/tests/components/pmsa003i/common.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_pmsa003i + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: pmsa003i + pm_1_0: + name: PMSA003i PM1.0 + pm_2_5: + name: PMSA003i PM2.5 + pm_10_0: + name: PMSA003i PM10.0 + pmc_0_3: + name: PMSA003i PMC <0.3µm + pmc_0_5: + name: PMSA003i PMC <0.5µm + pmc_1_0: + name: PMSA003i PMC <1µm + pmc_2_5: + name: PMSA003i PMC <2.5µm + pmc_5_0: + name: PMSA003i PMC <5µm + pmc_10_0: + name: PMSA003i PMC <10µm + address: 0x12 + standard_units: true diff --git a/tests/components/pmsa003i/test.esp32-ard.yaml b/tests/components/pmsa003i/test.esp32-ard.yaml index d8d96400f6..63c3bd6afd 100644 --- a/tests/components/pmsa003i/test.esp32-ard.yaml +++ b/tests/components/pmsa003i/test.esp32-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_pmsa003i - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: pmsa003i - pm_1_0: - name: PMSA003i PM1.0 - pm_2_5: - name: PMSA003i PM2.5 - pm_10_0: - name: PMSA003i PM10.0 - pmc_0_3: - name: PMSA003i PMC <0.3µm - pmc_0_5: - name: PMSA003i PMC <0.5µm - pmc_1_0: - name: PMSA003i PMC <1µm - pmc_2_5: - name: PMSA003i PMC <2.5µm - pmc_5_0: - name: PMSA003i PMC <5µm - pmc_10_0: - name: PMSA003i PMC <10µm - address: 0x12 - standard_units: true +<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp32-c3-ard.yaml b/tests/components/pmsa003i/test.esp32-c3-ard.yaml index 70e28303a2..ee2c29ca4e 100644 --- a/tests/components/pmsa003i/test.esp32-c3-ard.yaml +++ b/tests/components/pmsa003i/test.esp32-c3-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_pmsa003i - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmsa003i - pm_1_0: - name: PMSA003i PM1.0 - pm_2_5: - name: PMSA003i PM2.5 - pm_10_0: - name: PMSA003i PM10.0 - pmc_0_3: - name: PMSA003i PMC <0.3µm - pmc_0_5: - name: PMSA003i PMC <0.5µm - pmc_1_0: - name: PMSA003i PMC <1µm - pmc_2_5: - name: PMSA003i PMC <2.5µm - pmc_5_0: - name: PMSA003i PMC <5µm - pmc_10_0: - name: PMSA003i PMC <10µm - address: 0x12 - standard_units: true +<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp32-c3-idf.yaml b/tests/components/pmsa003i/test.esp32-c3-idf.yaml index 70e28303a2..ee2c29ca4e 100644 --- a/tests/components/pmsa003i/test.esp32-c3-idf.yaml +++ b/tests/components/pmsa003i/test.esp32-c3-idf.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_pmsa003i - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmsa003i - pm_1_0: - name: PMSA003i PM1.0 - pm_2_5: - name: PMSA003i PM2.5 - pm_10_0: - name: PMSA003i PM10.0 - pmc_0_3: - name: PMSA003i PMC <0.3µm - pmc_0_5: - name: PMSA003i PMC <0.5µm - pmc_1_0: - name: PMSA003i PMC <1µm - pmc_2_5: - name: PMSA003i PMC <2.5µm - pmc_5_0: - name: PMSA003i PMC <5µm - pmc_10_0: - name: PMSA003i PMC <10µm - address: 0x12 - standard_units: true +<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp32-idf.yaml b/tests/components/pmsa003i/test.esp32-idf.yaml index d8d96400f6..63c3bd6afd 100644 --- a/tests/components/pmsa003i/test.esp32-idf.yaml +++ b/tests/components/pmsa003i/test.esp32-idf.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_pmsa003i - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: pmsa003i - pm_1_0: - name: PMSA003i PM1.0 - pm_2_5: - name: PMSA003i PM2.5 - pm_10_0: - name: PMSA003i PM10.0 - pmc_0_3: - name: PMSA003i PMC <0.3µm - pmc_0_5: - name: PMSA003i PMC <0.5µm - pmc_1_0: - name: PMSA003i PMC <1µm - pmc_2_5: - name: PMSA003i PMC <2.5µm - pmc_5_0: - name: PMSA003i PMC <5µm - pmc_10_0: - name: PMSA003i PMC <10µm - address: 0x12 - standard_units: true +<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.esp8266-ard.yaml b/tests/components/pmsa003i/test.esp8266-ard.yaml index 70e28303a2..ee2c29ca4e 100644 --- a/tests/components/pmsa003i/test.esp8266-ard.yaml +++ b/tests/components/pmsa003i/test.esp8266-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_pmsa003i - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmsa003i - pm_1_0: - name: PMSA003i PM1.0 - pm_2_5: - name: PMSA003i PM2.5 - pm_10_0: - name: PMSA003i PM10.0 - pmc_0_3: - name: PMSA003i PMC <0.3µm - pmc_0_5: - name: PMSA003i PMC <0.5µm - pmc_1_0: - name: PMSA003i PMC <1µm - pmc_2_5: - name: PMSA003i PMC <2.5µm - pmc_5_0: - name: PMSA003i PMC <5µm - pmc_10_0: - name: PMSA003i PMC <10µm - address: 0x12 - standard_units: true +<<: !include common.yaml diff --git a/tests/components/pmsa003i/test.rp2040-ard.yaml b/tests/components/pmsa003i/test.rp2040-ard.yaml index 70e28303a2..ee2c29ca4e 100644 --- a/tests/components/pmsa003i/test.rp2040-ard.yaml +++ b/tests/components/pmsa003i/test.rp2040-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_pmsa003i - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmsa003i - pm_1_0: - name: PMSA003i PM1.0 - pm_2_5: - name: PMSA003i PM2.5 - pm_10_0: - name: PMSA003i PM10.0 - pmc_0_3: - name: PMSA003i PMC <0.3µm - pmc_0_5: - name: PMSA003i PMC <0.5µm - pmc_1_0: - name: PMSA003i PMC <1µm - pmc_2_5: - name: PMSA003i PMC <2.5µm - pmc_5_0: - name: PMSA003i PMC <5µm - pmc_10_0: - name: PMSA003i PMC <10µm - address: 0x12 - standard_units: true +<<: !include common.yaml diff --git a/tests/components/pmsx003/common.yaml b/tests/components/pmsx003/common.yaml new file mode 100644 index 0000000000..7b9ca5b091 --- /dev/null +++ b/tests/components/pmsx003/common.yaml @@ -0,0 +1,34 @@ +uart: + - id: uart_pmsx003 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: pmsx003 + type: PMSX003 + pm_1_0: + name: PM 1.0 Concentration + pm_2_5: + name: PM 2.5 Concentration + pm_10_0: + name: PM 10.0 Concentration + pm_1_0_std: + name: PM 1.0 Standard Atmospher Concentration + pm_2_5_std: + name: PM 2.5 Standard Atmospher Concentration + pm_10_0_std: + name: PM 10.0 Standard Atmospher Concentration + pm_0_3um: + name: Particulate Count >0.3um + pm_0_5um: + name: Particulate Count >0.5um + pm_1_0um: + name: Particulate Count >1.0um + pm_2_5um: + name: Particulate Count >2.5um + pm_5_0um: + name: Particulate Count >5.0um + pm_10_0um: + name: Particulate Count >10.0um + update_interval: 30s diff --git a/tests/components/pmsx003/test.esp32-ard.yaml b/tests/components/pmsx003/test.esp32-ard.yaml index 5e7ebbbb2e..f486544afa 100644 --- a/tests/components/pmsx003/test.esp32-ard.yaml +++ b/tests/components/pmsx003/test.esp32-ard.yaml @@ -1,34 +1,5 @@ -uart: - - id: uart_pmsx003 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: pmsx003 - type: PMSX003 - pm_1_0: - name: PM 1.0 Concentration - pm_2_5: - name: PM 2.5 Concentration - pm_10_0: - name: PM 10.0 Concentration - pm_1_0_std: - name: PM 1.0 Standard Atmospher Concentration - pm_2_5_std: - name: PM 2.5 Standard Atmospher Concentration - pm_10_0_std: - name: PM 10.0 Standard Atmospher Concentration - pm_0_3um: - name: Particulate Count >0.3um - pm_0_5um: - name: Particulate Count >0.5um - pm_1_0um: - name: Particulate Count >1.0um - pm_2_5um: - name: Particulate Count >2.5um - pm_5_0um: - name: Particulate Count >5.0um - pm_10_0um: - name: Particulate Count >10.0um - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp32-c3-ard.yaml b/tests/components/pmsx003/test.esp32-c3-ard.yaml index 58adc9390a..b516342f3b 100644 --- a/tests/components/pmsx003/test.esp32-c3-ard.yaml +++ b/tests/components/pmsx003/test.esp32-c3-ard.yaml @@ -1,34 +1,5 @@ -uart: - - id: uart_pmsx003 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pmsx003 - type: PMSX003 - pm_1_0: - name: PM 1.0 Concentration - pm_2_5: - name: PM 2.5 Concentration - pm_10_0: - name: PM 10.0 Concentration - pm_1_0_std: - name: PM 1.0 Standard Atmospher Concentration - pm_2_5_std: - name: PM 2.5 Standard Atmospher Concentration - pm_10_0_std: - name: PM 10.0 Standard Atmospher Concentration - pm_0_3um: - name: Particulate Count >0.3um - pm_0_5um: - name: Particulate Count >0.5um - pm_1_0um: - name: Particulate Count >1.0um - pm_2_5um: - name: Particulate Count >2.5um - pm_5_0um: - name: Particulate Count >5.0um - pm_10_0um: - name: Particulate Count >10.0um - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp32-c3-idf.yaml b/tests/components/pmsx003/test.esp32-c3-idf.yaml index 58adc9390a..b516342f3b 100644 --- a/tests/components/pmsx003/test.esp32-c3-idf.yaml +++ b/tests/components/pmsx003/test.esp32-c3-idf.yaml @@ -1,34 +1,5 @@ -uart: - - id: uart_pmsx003 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pmsx003 - type: PMSX003 - pm_1_0: - name: PM 1.0 Concentration - pm_2_5: - name: PM 2.5 Concentration - pm_10_0: - name: PM 10.0 Concentration - pm_1_0_std: - name: PM 1.0 Standard Atmospher Concentration - pm_2_5_std: - name: PM 2.5 Standard Atmospher Concentration - pm_10_0_std: - name: PM 10.0 Standard Atmospher Concentration - pm_0_3um: - name: Particulate Count >0.3um - pm_0_5um: - name: Particulate Count >0.5um - pm_1_0um: - name: Particulate Count >1.0um - pm_2_5um: - name: Particulate Count >2.5um - pm_5_0um: - name: Particulate Count >5.0um - pm_10_0um: - name: Particulate Count >10.0um - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp32-idf.yaml b/tests/components/pmsx003/test.esp32-idf.yaml index 5e7ebbbb2e..f486544afa 100644 --- a/tests/components/pmsx003/test.esp32-idf.yaml +++ b/tests/components/pmsx003/test.esp32-idf.yaml @@ -1,34 +1,5 @@ -uart: - - id: uart_pmsx003 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: pmsx003 - type: PMSX003 - pm_1_0: - name: PM 1.0 Concentration - pm_2_5: - name: PM 2.5 Concentration - pm_10_0: - name: PM 10.0 Concentration - pm_1_0_std: - name: PM 1.0 Standard Atmospher Concentration - pm_2_5_std: - name: PM 2.5 Standard Atmospher Concentration - pm_10_0_std: - name: PM 10.0 Standard Atmospher Concentration - pm_0_3um: - name: Particulate Count >0.3um - pm_0_5um: - name: Particulate Count >0.5um - pm_1_0um: - name: Particulate Count >1.0um - pm_2_5um: - name: Particulate Count >2.5um - pm_5_0um: - name: Particulate Count >5.0um - pm_10_0um: - name: Particulate Count >10.0um - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/pmsx003/test.esp8266-ard.yaml b/tests/components/pmsx003/test.esp8266-ard.yaml index 58adc9390a..b516342f3b 100644 --- a/tests/components/pmsx003/test.esp8266-ard.yaml +++ b/tests/components/pmsx003/test.esp8266-ard.yaml @@ -1,34 +1,5 @@ -uart: - - id: uart_pmsx003 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pmsx003 - type: PMSX003 - pm_1_0: - name: PM 1.0 Concentration - pm_2_5: - name: PM 2.5 Concentration - pm_10_0: - name: PM 10.0 Concentration - pm_1_0_std: - name: PM 1.0 Standard Atmospher Concentration - pm_2_5_std: - name: PM 2.5 Standard Atmospher Concentration - pm_10_0_std: - name: PM 10.0 Standard Atmospher Concentration - pm_0_3um: - name: Particulate Count >0.3um - pm_0_5um: - name: Particulate Count >0.5um - pm_1_0um: - name: Particulate Count >1.0um - pm_2_5um: - name: Particulate Count >2.5um - pm_5_0um: - name: Particulate Count >5.0um - pm_10_0um: - name: Particulate Count >10.0um - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/pmsx003/test.rp2040-ard.yaml b/tests/components/pmsx003/test.rp2040-ard.yaml index 58adc9390a..b516342f3b 100644 --- a/tests/components/pmsx003/test.rp2040-ard.yaml +++ b/tests/components/pmsx003/test.rp2040-ard.yaml @@ -1,34 +1,5 @@ -uart: - - id: uart_pmsx003 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pmsx003 - type: PMSX003 - pm_1_0: - name: PM 1.0 Concentration - pm_2_5: - name: PM 2.5 Concentration - pm_10_0: - name: PM 10.0 Concentration - pm_1_0_std: - name: PM 1.0 Standard Atmospher Concentration - pm_2_5_std: - name: PM 2.5 Standard Atmospher Concentration - pm_10_0_std: - name: PM 10.0 Standard Atmospher Concentration - pm_0_3um: - name: Particulate Count >0.3um - pm_0_5um: - name: Particulate Count >0.5um - pm_1_0um: - name: Particulate Count >1.0um - pm_2_5um: - name: Particulate Count >2.5um - pm_5_0um: - name: Particulate Count >5.0um - pm_10_0um: - name: Particulate Count >10.0um - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/pmwcs3/common.yaml b/tests/components/pmwcs3/common.yaml new file mode 100644 index 0000000000..dcf59d0b6e --- /dev/null +++ b/tests/components/pmwcs3/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_pmwcs3 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: pmwcs3 + e25: + name: pmwcs3_e25 + ec: + name: pmwcs3_ec + temperature: + name: pmwcs3_temperature + vwc: + name: pmwcs3_vwc diff --git a/tests/components/pmwcs3/test.esp32-ard.yaml b/tests/components/pmwcs3/test.esp32-ard.yaml index 787eaca650..63c3bd6afd 100644 --- a/tests/components/pmwcs3/test.esp32-ard.yaml +++ b/tests/components/pmwcs3/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_pmwcs3 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: pmwcs3 - e25: - name: pmwcs3_e25 - ec: - name: pmwcs3_ec - temperature: - name: pmwcs3_temperature - vwc: - name: pmwcs3_vwc +<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp32-c3-ard.yaml b/tests/components/pmwcs3/test.esp32-c3-ard.yaml index 7e7e72692d..ee2c29ca4e 100644 --- a/tests/components/pmwcs3/test.esp32-c3-ard.yaml +++ b/tests/components/pmwcs3/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_pmwcs3 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmwcs3 - e25: - name: pmwcs3_e25 - ec: - name: pmwcs3_ec - temperature: - name: pmwcs3_temperature - vwc: - name: pmwcs3_vwc +<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp32-c3-idf.yaml b/tests/components/pmwcs3/test.esp32-c3-idf.yaml index 7e7e72692d..ee2c29ca4e 100644 --- a/tests/components/pmwcs3/test.esp32-c3-idf.yaml +++ b/tests/components/pmwcs3/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_pmwcs3 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmwcs3 - e25: - name: pmwcs3_e25 - ec: - name: pmwcs3_ec - temperature: - name: pmwcs3_temperature - vwc: - name: pmwcs3_vwc +<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp32-idf.yaml b/tests/components/pmwcs3/test.esp32-idf.yaml index 787eaca650..63c3bd6afd 100644 --- a/tests/components/pmwcs3/test.esp32-idf.yaml +++ b/tests/components/pmwcs3/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_pmwcs3 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: pmwcs3 - e25: - name: pmwcs3_e25 - ec: - name: pmwcs3_ec - temperature: - name: pmwcs3_temperature - vwc: - name: pmwcs3_vwc +<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.esp8266-ard.yaml b/tests/components/pmwcs3/test.esp8266-ard.yaml index 7e7e72692d..ee2c29ca4e 100644 --- a/tests/components/pmwcs3/test.esp8266-ard.yaml +++ b/tests/components/pmwcs3/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_pmwcs3 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmwcs3 - e25: - name: pmwcs3_e25 - ec: - name: pmwcs3_ec - temperature: - name: pmwcs3_temperature - vwc: - name: pmwcs3_vwc +<<: !include common.yaml diff --git a/tests/components/pmwcs3/test.rp2040-ard.yaml b/tests/components/pmwcs3/test.rp2040-ard.yaml index 7e7e72692d..ee2c29ca4e 100644 --- a/tests/components/pmwcs3/test.rp2040-ard.yaml +++ b/tests/components/pmwcs3/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_pmwcs3 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: pmwcs3 - e25: - name: pmwcs3_e25 - ec: - name: pmwcs3_ec - temperature: - name: pmwcs3_temperature - vwc: - name: pmwcs3_vwc +<<: !include common.yaml diff --git a/tests/components/pn532_i2c/common.yaml b/tests/components/pn532_i2c/common.yaml new file mode 100644 index 0000000000..db9abd4b13 --- /dev/null +++ b/tests/components/pn532_i2c/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_pn532 + scl: ${scl_pin} + sda: ${sda_pin} + +pn532_i2c: + id: pn532_nfcc + +binary_sensor: + - platform: pn532 + pn532_id: pn532_nfcc + name: PN532 NFC Tag + uid: 74-10-37-94 diff --git a/tests/components/pn532_i2c/test.esp32-ard.yaml b/tests/components/pn532_i2c/test.esp32-ard.yaml index a50533b1d0..63c3bd6afd 100644 --- a/tests/components/pn532_i2c/test.esp32-ard.yaml +++ b/tests/components/pn532_i2c/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_pn532 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pn532_i2c: - id: pn532_nfcc - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp32-c3-ard.yaml b/tests/components/pn532_i2c/test.esp32-c3-ard.yaml index 62816d2ace..ee2c29ca4e 100644 --- a/tests/components/pn532_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/pn532_i2c/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_pn532 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pn532_i2c: - id: pn532_nfcc - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp32-c3-idf.yaml b/tests/components/pn532_i2c/test.esp32-c3-idf.yaml index 62816d2ace..ee2c29ca4e 100644 --- a/tests/components/pn532_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/pn532_i2c/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_pn532 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pn532_i2c: - id: pn532_nfcc - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp32-idf.yaml b/tests/components/pn532_i2c/test.esp32-idf.yaml index a50533b1d0..63c3bd6afd 100644 --- a/tests/components/pn532_i2c/test.esp32-idf.yaml +++ b/tests/components/pn532_i2c/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_pn532 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -pn532_i2c: - id: pn532_nfcc - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.esp8266-ard.yaml b/tests/components/pn532_i2c/test.esp8266-ard.yaml index 62816d2ace..ee2c29ca4e 100644 --- a/tests/components/pn532_i2c/test.esp8266-ard.yaml +++ b/tests/components/pn532_i2c/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_pn532 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pn532_i2c: - id: pn532_nfcc - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_i2c/test.rp2040-ard.yaml b/tests/components/pn532_i2c/test.rp2040-ard.yaml index 62816d2ace..ee2c29ca4e 100644 --- a/tests/components/pn532_i2c/test.rp2040-ard.yaml +++ b/tests/components/pn532_i2c/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_pn532 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -pn532_i2c: - id: pn532_nfcc - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_spi/common.yaml b/tests/components/pn532_spi/common.yaml new file mode 100644 index 0000000000..d5b8bc405e --- /dev/null +++ b/tests/components/pn532_spi/common.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_pn532 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +pn532_spi: + id: pn532_nfcc + cs_pin: ${cs_pin} + +binary_sensor: + - platform: pn532 + pn532_id: pn532_nfcc + name: PN532 NFC Tag + uid: 74-10-37-94 diff --git a/tests/components/pn532_spi/test.esp32-ard.yaml b/tests/components/pn532_spi/test.esp32-ard.yaml index 18a382a007..bce56f398a 100644 --- a/tests/components/pn532_spi/test.esp32-ard.yaml +++ b/tests/components/pn532_spi/test.esp32-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_pn532 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + cs_pin: GPIO12 -pn532_spi: - id: pn532_nfcc - cs_pin: 12 - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp32-c3-ard.yaml b/tests/components/pn532_spi/test.esp32-c3-ard.yaml index d21d50aa5c..2415ba5dc6 100644 --- a/tests/components/pn532_spi/test.esp32-c3-ard.yaml +++ b/tests/components/pn532_spi/test.esp32-c3-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_pn532 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -pn532_spi: - id: pn532_nfcc - cs_pin: 4 - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp32-c3-idf.yaml b/tests/components/pn532_spi/test.esp32-c3-idf.yaml index d21d50aa5c..2415ba5dc6 100644 --- a/tests/components/pn532_spi/test.esp32-c3-idf.yaml +++ b/tests/components/pn532_spi/test.esp32-c3-idf.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_pn532 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -pn532_spi: - id: pn532_nfcc - cs_pin: 4 - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp32-idf.yaml b/tests/components/pn532_spi/test.esp32-idf.yaml index 18a382a007..bce56f398a 100644 --- a/tests/components/pn532_spi/test.esp32-idf.yaml +++ b/tests/components/pn532_spi/test.esp32-idf.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_pn532 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + cs_pin: GPIO12 -pn532_spi: - id: pn532_nfcc - cs_pin: 12 - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.esp8266-ard.yaml b/tests/components/pn532_spi/test.esp8266-ard.yaml index 1dba38e63e..bd5c203e35 100644 --- a/tests/components/pn532_spi/test.esp8266-ard.yaml +++ b/tests/components/pn532_spi/test.esp8266-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_pn532 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 -pn532_spi: - id: pn532_nfcc - cs_pin: 15 - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn532_spi/test.rp2040-ard.yaml b/tests/components/pn532_spi/test.rp2040-ard.yaml index ab02b2cc47..f6c3f1eeca 100644 --- a/tests/components/pn532_spi/test.rp2040-ard.yaml +++ b/tests/components/pn532_spi/test.rp2040-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_pn532 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -pn532_spi: - id: pn532_nfcc - cs_pin: 6 - -binary_sensor: - - platform: pn532 - pn532_id: pn532_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/common.yaml b/tests/components/pn7150_i2c/common.yaml new file mode 100644 index 0000000000..ef852b7a78 --- /dev/null +++ b/tests/components/pn7150_i2c/common.yaml @@ -0,0 +1,35 @@ +esphome: + on_boot: + then: + - tag.set_clean_mode: nfcc_pn7150 + - tag.set_format_mode: nfcc_pn7150 + - tag.set_read_mode: nfcc_pn7150 + - tag.set_write_message: + message: https://www.home-assistant.io/tag/pulse + include_android_app_record: false + - tag.set_write_mode: nfcc_pn7150 + - tag.set_emulation_message: + message: https://www.home-assistant.io/tag/pulse + include_android_app_record: false + - tag.emulation_off: nfcc_pn7150 + - tag.emulation_on: nfcc_pn7150 + - tag.polling_off: nfcc_pn7150 + - tag.polling_on: nfcc_pn7150 + +i2c: + - id: i2c_pn7150 + scl: ${scl_pin} + sda: ${sda_pin} + +pn7150_i2c: + id: nfcc_pn7150 + irq_pin: ${irq_pin} + ven_pin: ${ven_pin} + emulation_message: https://www.home-assistant.io/tag/pulse_ce + tag_ttl: 1000ms + on_tag: + - logger.log: "Tag" + on_tag_removed: + - logger.log: "Tag removed" + on_emulated_tag_scan: + - logger.log: "Tag emulated" diff --git a/tests/components/pn7150_i2c/test.esp32-ard.yaml b/tests/components/pn7150_i2c/test.esp32-ard.yaml index 23d1061608..1643bec317 100644 --- a/tests/components/pn7150_i2c/test.esp32-ard.yaml +++ b/tests/components/pn7150_i2c/test.esp32-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7150 - - tag.set_format_mode: nfcc_pn7150 - - tag.set_read_mode: nfcc_pn7150 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7150 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7150 - - tag.emulation_on: nfcc_pn7150 - - tag.polling_off: nfcc_pn7150 - - tag.polling_on: nfcc_pn7150 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO14 + ven_pin: GPIO15 -i2c: - - id: i2c_pn7150 - scl: 16 - sda: 17 - -pn7150_i2c: - id: nfcc_pn7150 - irq_pin: 12 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp32-c3-ard.yaml b/tests/components/pn7150_i2c/test.esp32-c3-ard.yaml index aee1886cd4..2067143411 100644 --- a/tests/components/pn7150_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/pn7150_i2c/test.esp32-c3-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7150 - - tag.set_format_mode: nfcc_pn7150 - - tag.set_read_mode: nfcc_pn7150 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7150 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7150 - - tag.emulation_on: nfcc_pn7150 - - tag.polling_off: nfcc_pn7150 - - tag.polling_on: nfcc_pn7150 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 + ven_pin: GPIO7 -i2c: - - id: i2c_pn7150 - scl: 5 - sda: 4 - -pn7150_i2c: - id: nfcc_pn7150 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml b/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml index aee1886cd4..2067143411 100644 --- a/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/pn7150_i2c/test.esp32-c3-idf.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7150 - - tag.set_format_mode: nfcc_pn7150 - - tag.set_read_mode: nfcc_pn7150 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7150 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7150 - - tag.emulation_on: nfcc_pn7150 - - tag.polling_off: nfcc_pn7150 - - tag.polling_on: nfcc_pn7150 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 + ven_pin: GPIO7 -i2c: - - id: i2c_pn7150 - scl: 5 - sda: 4 - -pn7150_i2c: - id: nfcc_pn7150 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp32-idf.yaml b/tests/components/pn7150_i2c/test.esp32-idf.yaml index 23d1061608..1643bec317 100644 --- a/tests/components/pn7150_i2c/test.esp32-idf.yaml +++ b/tests/components/pn7150_i2c/test.esp32-idf.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7150 - - tag.set_format_mode: nfcc_pn7150 - - tag.set_read_mode: nfcc_pn7150 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7150 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7150 - - tag.emulation_on: nfcc_pn7150 - - tag.polling_off: nfcc_pn7150 - - tag.polling_on: nfcc_pn7150 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO14 + ven_pin: GPIO15 -i2c: - - id: i2c_pn7150 - scl: 16 - sda: 17 - -pn7150_i2c: - id: nfcc_pn7150 - irq_pin: 12 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.esp8266-ard.yaml b/tests/components/pn7150_i2c/test.esp8266-ard.yaml index 6017d548ca..7111fc9e00 100644 --- a/tests/components/pn7150_i2c/test.esp8266-ard.yaml +++ b/tests/components/pn7150_i2c/test.esp8266-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7150 - - tag.set_format_mode: nfcc_pn7150 - - tag.set_read_mode: nfcc_pn7150 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7150 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7150 - - tag.emulation_on: nfcc_pn7150 - - tag.polling_off: nfcc_pn7150 - - tag.polling_on: nfcc_pn7150 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO12 + ven_pin: GPIO13 -i2c: - - id: i2c_pn7150 - scl: 5 - sda: 4 - -pn7150_i2c: - id: nfcc_pn7150 - irq_pin: 12 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7150_i2c/test.rp2040-ard.yaml b/tests/components/pn7150_i2c/test.rp2040-ard.yaml index aee1886cd4..2067143411 100644 --- a/tests/components/pn7150_i2c/test.rp2040-ard.yaml +++ b/tests/components/pn7150_i2c/test.rp2040-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7150 - - tag.set_format_mode: nfcc_pn7150 - - tag.set_read_mode: nfcc_pn7150 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7150 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7150 - - tag.emulation_on: nfcc_pn7150 - - tag.polling_off: nfcc_pn7150 - - tag.polling_on: nfcc_pn7150 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 + ven_pin: GPIO7 -i2c: - - id: i2c_pn7150 - scl: 5 - sda: 4 - -pn7150_i2c: - id: nfcc_pn7150 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/common.yaml b/tests/components/pn7160_i2c/common.yaml new file mode 100644 index 0000000000..0a7c9bd6bb --- /dev/null +++ b/tests/components/pn7160_i2c/common.yaml @@ -0,0 +1,35 @@ +esphome: + on_boot: + then: + - tag.set_clean_mode: nfcc_pn7160 + - tag.set_format_mode: nfcc_pn7160 + - tag.set_read_mode: nfcc_pn7160 + - tag.set_write_message: + message: https://www.home-assistant.io/tag/pulse + include_android_app_record: false + - tag.set_write_mode: nfcc_pn7160 + - tag.set_emulation_message: + message: https://www.home-assistant.io/tag/pulse + include_android_app_record: false + - tag.emulation_off: nfcc_pn7160 + - tag.emulation_on: nfcc_pn7160 + - tag.polling_off: nfcc_pn7160 + - tag.polling_on: nfcc_pn7160 + +i2c: + - id: i2c_pn7160 + scl: ${scl_pin} + sda: ${sda_pin} + +pn7150_i2c: + id: nfcc_pn7160 + irq_pin: ${irq_pin} + ven_pin: ${ven_pin} + emulation_message: https://www.home-assistant.io/tag/pulse_ce + tag_ttl: 1000ms + on_tag: + - logger.log: "Tag" + on_tag_removed: + - logger.log: "Tag removed" + on_emulated_tag_scan: + - logger.log: "Tag emulated" diff --git a/tests/components/pn7160_i2c/test.esp32-ard.yaml b/tests/components/pn7160_i2c/test.esp32-ard.yaml index d1a3cf5c77..1643bec317 100644 --- a/tests/components/pn7160_i2c/test.esp32-ard.yaml +++ b/tests/components/pn7160_i2c/test.esp32-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO14 + ven_pin: GPIO15 -i2c: - - id: i2c_pn7160 - scl: 16 - sda: 17 - -pn7150_i2c: - id: nfcc_pn7160 - irq_pin: 12 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp32-c3-ard.yaml b/tests/components/pn7160_i2c/test.esp32-c3-ard.yaml index d1d7947352..2067143411 100644 --- a/tests/components/pn7160_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/pn7160_i2c/test.esp32-c3-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 + ven_pin: GPIO7 -i2c: - - id: i2c_pn7160 - scl: 5 - sda: 4 - -pn7160_i2c: - id: nfcc_pn7160 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml b/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml index d1d7947352..2067143411 100644 --- a/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/pn7160_i2c/test.esp32-c3-idf.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 + ven_pin: GPIO7 -i2c: - - id: i2c_pn7160 - scl: 5 - sda: 4 - -pn7160_i2c: - id: nfcc_pn7160 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp32-idf.yaml b/tests/components/pn7160_i2c/test.esp32-idf.yaml index d1a3cf5c77..1643bec317 100644 --- a/tests/components/pn7160_i2c/test.esp32-idf.yaml +++ b/tests/components/pn7160_i2c/test.esp32-idf.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + irq_pin: GPIO14 + ven_pin: GPIO15 -i2c: - - id: i2c_pn7160 - scl: 16 - sda: 17 - -pn7150_i2c: - id: nfcc_pn7160 - irq_pin: 12 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.esp8266-ard.yaml b/tests/components/pn7160_i2c/test.esp8266-ard.yaml index 57bd965fc9..7111fc9e00 100644 --- a/tests/components/pn7160_i2c/test.esp8266-ard.yaml +++ b/tests/components/pn7160_i2c/test.esp8266-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO12 + ven_pin: GPIO13 -i2c: - - id: i2c_pn7160 - scl: 5 - sda: 4 - -pn7150_i2c: - id: nfcc_pn7160 - irq_pin: 12 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_i2c/test.rp2040-ard.yaml b/tests/components/pn7160_i2c/test.rp2040-ard.yaml index 5224b465ed..2067143411 100644 --- a/tests/components/pn7160_i2c/test.rp2040-ard.yaml +++ b/tests/components/pn7160_i2c/test.rp2040-ard.yaml @@ -1,35 +1,7 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + irq_pin: GPIO6 + ven_pin: GPIO7 -i2c: - - id: i2c_pn7160 - scl: 5 - sda: 4 - -pn7150_i2c: - id: nfcc_pn7160 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_spi/common.yaml b/tests/components/pn7160_spi/common.yaml new file mode 100644 index 0000000000..9e8d22f835 --- /dev/null +++ b/tests/components/pn7160_spi/common.yaml @@ -0,0 +1,37 @@ +esphome: + on_boot: + then: + - tag.set_clean_mode: nfcc_pn7160 + - tag.set_format_mode: nfcc_pn7160 + - tag.set_read_mode: nfcc_pn7160 + - tag.set_write_message: + message: https://www.home-assistant.io/tag/pulse + include_android_app_record: false + - tag.set_write_mode: nfcc_pn7160 + - tag.set_emulation_message: + message: https://www.home-assistant.io/tag/pulse + include_android_app_record: false + - tag.emulation_off: nfcc_pn7160 + - tag.emulation_on: nfcc_pn7160 + - tag.polling_off: nfcc_pn7160 + - tag.polling_on: nfcc_pn7160 + +spi: + - id: spi_pn7160 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +pn7160_spi: + id: nfcc_pn7160 + cs_pin: ${cs_pin} + irq_pin: ${irq_pin} + ven_pin: ${ven_pin} + emulation_message: https://www.home-assistant.io/tag/pulse_ce + tag_ttl: 1000ms + on_tag: + - logger.log: "Tag" + on_tag_removed: + - logger.log: "Tag removed" + on_emulated_tag_scan: + - logger.log: "Tag emulated" diff --git a/tests/components/pn7160_spi/test.esp32-ard.yaml b/tests/components/pn7160_spi/test.esp32-ard.yaml index 0319648f13..f6073d0416 100644 --- a/tests/components/pn7160_spi/test.esp32-ard.yaml +++ b/tests/components/pn7160_spi/test.esp32-ard.yaml @@ -1,37 +1,9 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + cs_pin: GPIO12 + irq_pin: GPIO13 + ven_pin: GPIO14 -spi: - - id: spi_pn7160 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 - -pn7160_spi: - id: nfcc_pn7160 - cs_pin: 12 - irq_pin: 14 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp32-c3-ard.yaml b/tests/components/pn7160_spi/test.esp32-c3-ard.yaml index fd19a53b2b..f8a07fad2f 100644 --- a/tests/components/pn7160_spi/test.esp32-c3-ard.yaml +++ b/tests/components/pn7160_spi/test.esp32-c3-ard.yaml @@ -1,37 +1,9 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + irq_pin: GPIO9 + ven_pin: GPIO10 -spi: - - id: spi_pn7160 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 - -pn7160_spi: - id: nfcc_pn7160 - cs_pin: 4 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp32-c3-idf.yaml b/tests/components/pn7160_spi/test.esp32-c3-idf.yaml index fd19a53b2b..f8a07fad2f 100644 --- a/tests/components/pn7160_spi/test.esp32-c3-idf.yaml +++ b/tests/components/pn7160_spi/test.esp32-c3-idf.yaml @@ -1,37 +1,9 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + irq_pin: GPIO9 + ven_pin: GPIO10 -spi: - - id: spi_pn7160 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 - -pn7160_spi: - id: nfcc_pn7160 - cs_pin: 4 - irq_pin: 2 - ven_pin: 3 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp32-idf.yaml b/tests/components/pn7160_spi/test.esp32-idf.yaml index 0319648f13..f6073d0416 100644 --- a/tests/components/pn7160_spi/test.esp32-idf.yaml +++ b/tests/components/pn7160_spi/test.esp32-idf.yaml @@ -1,37 +1,9 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + cs_pin: GPIO12 + irq_pin: GPIO13 + ven_pin: GPIO14 -spi: - - id: spi_pn7160 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 - -pn7160_spi: - id: nfcc_pn7160 - cs_pin: 12 - irq_pin: 14 - ven_pin: 13 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.esp8266-ard.yaml b/tests/components/pn7160_spi/test.esp8266-ard.yaml index fa356d5610..cbe27533a7 100644 --- a/tests/components/pn7160_spi/test.esp8266-ard.yaml +++ b/tests/components/pn7160_spi/test.esp8266-ard.yaml @@ -1,37 +1,9 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + irq_pin: GPIO15 + ven_pin: GPIO16 -spi: - - id: spi_pn7160 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 - -pn7160_spi: - id: nfcc_pn7160 - cs_pin: 15 - irq_pin: 4 - ven_pin: 5 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pn7160_spi/test.rp2040-ard.yaml b/tests/components/pn7160_spi/test.rp2040-ard.yaml index b36650032f..70cd2425fa 100644 --- a/tests/components/pn7160_spi/test.rp2040-ard.yaml +++ b/tests/components/pn7160_spi/test.rp2040-ard.yaml @@ -1,37 +1,9 @@ -esphome: - on_boot: - then: - - tag.set_clean_mode: nfcc_pn7160 - - tag.set_format_mode: nfcc_pn7160 - - tag.set_read_mode: nfcc_pn7160 - - tag.set_write_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.set_write_mode: nfcc_pn7160 - - tag.set_emulation_message: - message: https://www.home-assistant.io/tag/pulse - include_android_app_record: false - - tag.emulation_off: nfcc_pn7160 - - tag.emulation_on: nfcc_pn7160 - - tag.polling_off: nfcc_pn7160 - - tag.polling_on: nfcc_pn7160 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + irq_pin: GPIO15 + ven_pin: GPIO16 -spi: - - id: spi_pn7160 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 - -pn7160_spi: - id: nfcc_pn7160 - cs_pin: 6 - irq_pin: 7 - ven_pin: 5 - emulation_message: https://www.home-assistant.io/tag/pulse_ce - tag_ttl: 1000ms - on_tag: - - logger.log: "Tag" - on_tag_removed: - - logger.log: "Tag removed" - on_emulated_tag_scan: - - logger.log: "Tag emulated" +<<: !include common.yaml diff --git a/tests/components/pylontech/common.yaml b/tests/components/pylontech/common.yaml new file mode 100644 index 0000000000..6852685be7 --- /dev/null +++ b/tests/components/pylontech/common.yaml @@ -0,0 +1,48 @@ +uart: + - id: uart_pylontech0 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +pylontech: + - id: pylontech0 + - id: pylontech1 + +sensor: + - platform: pylontech + pylontech_id: pylontech0 + battery: 1 + voltage: + id: pyl01_voltage + current: + id: pyl01_current + coulomb: + id: pyl01_soc + mos_temperature: + id: pyl01_mos_temperature + - platform: pylontech + pylontech_id: pylontech1 + battery: 1 + voltage: + id: pyl13_voltage + temperature_low: + id: pyl13_temperature_low + temperature_high: + id: pyl13_temperature_high + voltage_low: + id: pyl13_voltage_low + voltage_high: + id: pyl13_voltage_high + +text_sensor: + - platform: pylontech + pylontech_id: pylontech0 + battery: 1 + base_state: + id: pyl0_base_state + voltage_state: + id: pyl0_voltage_state + current_state: + id: pyl0_current_state + temperature_state: + id: pyl0_temperature_state diff --git a/tests/components/pylontech/test.esp32-ard.yaml b/tests/components/pylontech/test.esp32-ard.yaml index a4c168fb47..f486544afa 100644 --- a/tests/components/pylontech/test.esp32-ard.yaml +++ b/tests/components/pylontech/test.esp32-ard.yaml @@ -1,48 +1,5 @@ -uart: - - id: uart_pylontech0 - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -pylontech: - - id: pylontech0 - - id: pylontech1 - -sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - voltage: - id: pyl01_voltage - current: - id: pyl01_current - coulomb: - id: pyl01_soc - mos_temperature: - id: pyl01_mos_temperature - - platform: pylontech - pylontech_id: pylontech1 - battery: 1 - voltage: - id: pyl13_voltage - temperature_low: - id: pyl13_temperature_low - temperature_high: - id: pyl13_temperature_high - voltage_low: - id: pyl13_voltage_low - voltage_high: - id: pyl13_voltage_high - -text_sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - base_state: - id: pyl0_base_state - voltage_state: - id: pyl0_voltage_state - current_state: - id: pyl0_current_state - temperature_state: - id: pyl0_temperature_state +<<: !include common.yaml diff --git a/tests/components/pylontech/test.esp32-c3-ard.yaml b/tests/components/pylontech/test.esp32-c3-ard.yaml index f7ec493422..b516342f3b 100644 --- a/tests/components/pylontech/test.esp32-c3-ard.yaml +++ b/tests/components/pylontech/test.esp32-c3-ard.yaml @@ -1,48 +1,5 @@ -uart: - - id: uart_pylontech0 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -pylontech: - - id: pylontech0 - - id: pylontech1 - -sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - voltage: - id: pyl01_voltage - current: - id: pyl01_current - coulomb: - id: pyl01_soc - mos_temperature: - id: pyl01_mos_temperature - - platform: pylontech - pylontech_id: pylontech1 - battery: 1 - voltage: - id: pyl13_voltage - temperature_low: - id: pyl13_temperature_low - temperature_high: - id: pyl13_temperature_high - voltage_low: - id: pyl13_voltage_low - voltage_high: - id: pyl13_voltage_high - -text_sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - base_state: - id: pyl0_base_state - voltage_state: - id: pyl0_voltage_state - current_state: - id: pyl0_current_state - temperature_state: - id: pyl0_temperature_state +<<: !include common.yaml diff --git a/tests/components/pylontech/test.esp32-c3-idf.yaml b/tests/components/pylontech/test.esp32-c3-idf.yaml index f7ec493422..b516342f3b 100644 --- a/tests/components/pylontech/test.esp32-c3-idf.yaml +++ b/tests/components/pylontech/test.esp32-c3-idf.yaml @@ -1,48 +1,5 @@ -uart: - - id: uart_pylontech0 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -pylontech: - - id: pylontech0 - - id: pylontech1 - -sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - voltage: - id: pyl01_voltage - current: - id: pyl01_current - coulomb: - id: pyl01_soc - mos_temperature: - id: pyl01_mos_temperature - - platform: pylontech - pylontech_id: pylontech1 - battery: 1 - voltage: - id: pyl13_voltage - temperature_low: - id: pyl13_temperature_low - temperature_high: - id: pyl13_temperature_high - voltage_low: - id: pyl13_voltage_low - voltage_high: - id: pyl13_voltage_high - -text_sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - base_state: - id: pyl0_base_state - voltage_state: - id: pyl0_voltage_state - current_state: - id: pyl0_current_state - temperature_state: - id: pyl0_temperature_state +<<: !include common.yaml diff --git a/tests/components/pylontech/test.esp32-idf.yaml b/tests/components/pylontech/test.esp32-idf.yaml index a4c168fb47..f486544afa 100644 --- a/tests/components/pylontech/test.esp32-idf.yaml +++ b/tests/components/pylontech/test.esp32-idf.yaml @@ -1,48 +1,5 @@ -uart: - - id: uart_pylontech0 - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -pylontech: - - id: pylontech0 - - id: pylontech1 - -sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - voltage: - id: pyl01_voltage - current: - id: pyl01_current - coulomb: - id: pyl01_soc - mos_temperature: - id: pyl01_mos_temperature - - platform: pylontech - pylontech_id: pylontech1 - battery: 1 - voltage: - id: pyl13_voltage - temperature_low: - id: pyl13_temperature_low - temperature_high: - id: pyl13_temperature_high - voltage_low: - id: pyl13_voltage_low - voltage_high: - id: pyl13_voltage_high - -text_sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - base_state: - id: pyl0_base_state - voltage_state: - id: pyl0_voltage_state - current_state: - id: pyl0_current_state - temperature_state: - id: pyl0_temperature_state +<<: !include common.yaml diff --git a/tests/components/pylontech/test.esp8266-ard.yaml b/tests/components/pylontech/test.esp8266-ard.yaml index f7ec493422..b516342f3b 100644 --- a/tests/components/pylontech/test.esp8266-ard.yaml +++ b/tests/components/pylontech/test.esp8266-ard.yaml @@ -1,48 +1,5 @@ -uart: - - id: uart_pylontech0 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -pylontech: - - id: pylontech0 - - id: pylontech1 - -sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - voltage: - id: pyl01_voltage - current: - id: pyl01_current - coulomb: - id: pyl01_soc - mos_temperature: - id: pyl01_mos_temperature - - platform: pylontech - pylontech_id: pylontech1 - battery: 1 - voltage: - id: pyl13_voltage - temperature_low: - id: pyl13_temperature_low - temperature_high: - id: pyl13_temperature_high - voltage_low: - id: pyl13_voltage_low - voltage_high: - id: pyl13_voltage_high - -text_sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - base_state: - id: pyl0_base_state - voltage_state: - id: pyl0_voltage_state - current_state: - id: pyl0_current_state - temperature_state: - id: pyl0_temperature_state +<<: !include common.yaml diff --git a/tests/components/pylontech/test.rp2040-ard.yaml b/tests/components/pylontech/test.rp2040-ard.yaml index f7ec493422..b516342f3b 100644 --- a/tests/components/pylontech/test.rp2040-ard.yaml +++ b/tests/components/pylontech/test.rp2040-ard.yaml @@ -1,48 +1,5 @@ -uart: - - id: uart_pylontech0 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -pylontech: - - id: pylontech0 - - id: pylontech1 - -sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - voltage: - id: pyl01_voltage - current: - id: pyl01_current - coulomb: - id: pyl01_soc - mos_temperature: - id: pyl01_mos_temperature - - platform: pylontech - pylontech_id: pylontech1 - battery: 1 - voltage: - id: pyl13_voltage - temperature_low: - id: pyl13_temperature_low - temperature_high: - id: pyl13_temperature_high - voltage_low: - id: pyl13_voltage_low - voltage_high: - id: pyl13_voltage_high - -text_sensor: - - platform: pylontech - pylontech_id: pylontech0 - battery: 1 - base_state: - id: pyl0_base_state - voltage_state: - id: pyl0_voltage_state - current_state: - id: pyl0_current_state - temperature_state: - id: pyl0_temperature_state +<<: !include common.yaml diff --git a/tests/components/pzem004t/common.yaml b/tests/components/pzem004t/common.yaml new file mode 100644 index 0000000000..75f7f30fc9 --- /dev/null +++ b/tests/components/pzem004t/common.yaml @@ -0,0 +1,14 @@ +uart: + - id: uart_pzem004t + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +sensor: + - platform: pzem004t + voltage: + name: PZEM004T Voltage + current: + name: PZEM004T Current + power: + name: PZEM004T Power diff --git a/tests/components/pzem004t/test.esp32-ard.yaml b/tests/components/pzem004t/test.esp32-ard.yaml index 23f2bd0eca..f486544afa 100644 --- a/tests/components/pzem004t/test.esp32-ard.yaml +++ b/tests/components/pzem004t/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_pzem004t - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: pzem004t - voltage: - name: PZEM004T Voltage - current: - name: PZEM004T Current - power: - name: PZEM004T Power +<<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp32-c3-ard.yaml b/tests/components/pzem004t/test.esp32-c3-ard.yaml index b9c93f8761..b516342f3b 100644 --- a/tests/components/pzem004t/test.esp32-c3-ard.yaml +++ b/tests/components/pzem004t/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_pzem004t - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pzem004t - voltage: - name: PZEM004T Voltage - current: - name: PZEM004T Current - power: - name: PZEM004T Power +<<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp32-c3-idf.yaml b/tests/components/pzem004t/test.esp32-c3-idf.yaml index b9c93f8761..b516342f3b 100644 --- a/tests/components/pzem004t/test.esp32-c3-idf.yaml +++ b/tests/components/pzem004t/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_pzem004t - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pzem004t - voltage: - name: PZEM004T Voltage - current: - name: PZEM004T Current - power: - name: PZEM004T Power +<<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp32-idf.yaml b/tests/components/pzem004t/test.esp32-idf.yaml index 23f2bd0eca..f486544afa 100644 --- a/tests/components/pzem004t/test.esp32-idf.yaml +++ b/tests/components/pzem004t/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_pzem004t - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: pzem004t - voltage: - name: PZEM004T Voltage - current: - name: PZEM004T Current - power: - name: PZEM004T Power +<<: !include common.yaml diff --git a/tests/components/pzem004t/test.esp8266-ard.yaml b/tests/components/pzem004t/test.esp8266-ard.yaml index b9c93f8761..b516342f3b 100644 --- a/tests/components/pzem004t/test.esp8266-ard.yaml +++ b/tests/components/pzem004t/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_pzem004t - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pzem004t - voltage: - name: PZEM004T Voltage - current: - name: PZEM004T Current - power: - name: PZEM004T Power +<<: !include common.yaml diff --git a/tests/components/pzem004t/test.rp2040-ard.yaml b/tests/components/pzem004t/test.rp2040-ard.yaml index b9c93f8761..b516342f3b 100644 --- a/tests/components/pzem004t/test.rp2040-ard.yaml +++ b/tests/components/pzem004t/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_pzem004t - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: pzem004t - voltage: - name: PZEM004T Voltage - current: - name: PZEM004T Current - power: - name: PZEM004T Power +<<: !include common.yaml diff --git a/tests/components/pzemac/common.yaml b/tests/components/pzemac/common.yaml new file mode 100644 index 0000000000..e50f4ad2f2 --- /dev/null +++ b/tests/components/pzemac/common.yaml @@ -0,0 +1,28 @@ +esphome: + on_boot: + then: + - pzemac.reset_energy: pzemac1 + +uart: + - id: uart_pzemac + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +modbus: + +sensor: + - platform: pzemac + id: pzemac1 + voltage: + name: PZEMAC Voltage + current: + name: PZEMAC Current + power: + name: PZEMAC Power + energy: + name: PZEMAC Energy + frequency: + name: PZEMAC Frequency + power_factor: + name: PZEMAC Power Factor diff --git a/tests/components/pzemac/test.esp32-ard.yaml b/tests/components/pzemac/test.esp32-ard.yaml index ce431a6100..f486544afa 100644 --- a/tests/components/pzemac/test.esp32-ard.yaml +++ b/tests/components/pzemac/test.esp32-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - pzemac.reset_energy: pzemac1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_pzemac - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -modbus: - -sensor: - - platform: pzemac - id: pzemac1 - voltage: - name: PZEMAC Voltage - current: - name: PZEMAC Current - power: - name: PZEMAC Power - energy: - name: PZEMAC Energy - frequency: - name: PZEMAC Frequency - power_factor: - name: PZEMAC Power Factor +<<: !include common.yaml diff --git a/tests/components/pzemac/test.esp32-c3-ard.yaml b/tests/components/pzemac/test.esp32-c3-ard.yaml index 6d9abbebe9..b516342f3b 100644 --- a/tests/components/pzemac/test.esp32-c3-ard.yaml +++ b/tests/components/pzemac/test.esp32-c3-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - pzemac.reset_energy: pzemac1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemac - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -modbus: - -sensor: - - platform: pzemac - id: pzemac1 - voltage: - name: PZEMAC Voltage - current: - name: PZEMAC Current - power: - name: PZEMAC Power - energy: - name: PZEMAC Energy - frequency: - name: PZEMAC Frequency - power_factor: - name: PZEMAC Power Factor +<<: !include common.yaml diff --git a/tests/components/pzemac/test.esp32-c3-idf.yaml b/tests/components/pzemac/test.esp32-c3-idf.yaml index 6d9abbebe9..b516342f3b 100644 --- a/tests/components/pzemac/test.esp32-c3-idf.yaml +++ b/tests/components/pzemac/test.esp32-c3-idf.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - pzemac.reset_energy: pzemac1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemac - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -modbus: - -sensor: - - platform: pzemac - id: pzemac1 - voltage: - name: PZEMAC Voltage - current: - name: PZEMAC Current - power: - name: PZEMAC Power - energy: - name: PZEMAC Energy - frequency: - name: PZEMAC Frequency - power_factor: - name: PZEMAC Power Factor +<<: !include common.yaml diff --git a/tests/components/pzemac/test.esp32-idf.yaml b/tests/components/pzemac/test.esp32-idf.yaml index ce431a6100..f486544afa 100644 --- a/tests/components/pzemac/test.esp32-idf.yaml +++ b/tests/components/pzemac/test.esp32-idf.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - pzemac.reset_energy: pzemac1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_pzemac - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -modbus: - -sensor: - - platform: pzemac - id: pzemac1 - voltage: - name: PZEMAC Voltage - current: - name: PZEMAC Current - power: - name: PZEMAC Power - energy: - name: PZEMAC Energy - frequency: - name: PZEMAC Frequency - power_factor: - name: PZEMAC Power Factor +<<: !include common.yaml diff --git a/tests/components/pzemac/test.esp8266-ard.yaml b/tests/components/pzemac/test.esp8266-ard.yaml index 6d9abbebe9..b516342f3b 100644 --- a/tests/components/pzemac/test.esp8266-ard.yaml +++ b/tests/components/pzemac/test.esp8266-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - pzemac.reset_energy: pzemac1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemac - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -modbus: - -sensor: - - platform: pzemac - id: pzemac1 - voltage: - name: PZEMAC Voltage - current: - name: PZEMAC Current - power: - name: PZEMAC Power - energy: - name: PZEMAC Energy - frequency: - name: PZEMAC Frequency - power_factor: - name: PZEMAC Power Factor +<<: !include common.yaml diff --git a/tests/components/pzemac/test.rp2040-ard.yaml b/tests/components/pzemac/test.rp2040-ard.yaml index 6d9abbebe9..b516342f3b 100644 --- a/tests/components/pzemac/test.rp2040-ard.yaml +++ b/tests/components/pzemac/test.rp2040-ard.yaml @@ -1,28 +1,5 @@ -esphome: - on_boot: - then: - - pzemac.reset_energy: pzemac1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemac - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -modbus: - -sensor: - - platform: pzemac - id: pzemac1 - voltage: - name: PZEMAC Voltage - current: - name: PZEMAC Current - power: - name: PZEMAC Power - energy: - name: PZEMAC Energy - frequency: - name: PZEMAC Frequency - power_factor: - name: PZEMAC Power Factor +<<: !include common.yaml diff --git a/tests/components/pzemdc/common.yaml b/tests/components/pzemdc/common.yaml new file mode 100644 index 0000000000..db1868d682 --- /dev/null +++ b/tests/components/pzemdc/common.yaml @@ -0,0 +1,23 @@ +esphome: + on_boot: + then: + - pzemdc.reset_energy: pzemdc1 + +uart: + - id: uart_pzemdc + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + stop_bits: 2 + +sensor: + - platform: pzemdc + id: pzemdc1 + voltage: + name: PZEMDC Voltage + current: + name: PZEMDC Current + power: + name: PZEMDC Power + energy: + name: PZEMDC Energy diff --git a/tests/components/pzemdc/test.esp32-ard.yaml b/tests/components/pzemdc/test.esp32-ard.yaml index 9cc61137de..f486544afa 100644 --- a/tests/components/pzemdc/test.esp32-ard.yaml +++ b/tests/components/pzemdc/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - pzemdc.reset_energy: pzemdc1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_pzemdc - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - stop_bits: 2 - -sensor: - - platform: pzemdc - id: pzemdc1 - voltage: - name: PZEMDC Voltage - current: - name: PZEMDC Current - power: - name: PZEMDC Power - energy: - name: PZEMDC Energy +<<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp32-c3-ard.yaml b/tests/components/pzemdc/test.esp32-c3-ard.yaml index 02114b781d..b516342f3b 100644 --- a/tests/components/pzemdc/test.esp32-c3-ard.yaml +++ b/tests/components/pzemdc/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - pzemdc.reset_energy: pzemdc1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemdc - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - stop_bits: 2 - -sensor: - - platform: pzemdc - id: pzemdc1 - voltage: - name: PZEMDC Voltage - current: - name: PZEMDC Current - power: - name: PZEMDC Power - energy: - name: PZEMDC Energy +<<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp32-c3-idf.yaml b/tests/components/pzemdc/test.esp32-c3-idf.yaml index 02114b781d..b516342f3b 100644 --- a/tests/components/pzemdc/test.esp32-c3-idf.yaml +++ b/tests/components/pzemdc/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - pzemdc.reset_energy: pzemdc1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemdc - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - stop_bits: 2 - -sensor: - - platform: pzemdc - id: pzemdc1 - voltage: - name: PZEMDC Voltage - current: - name: PZEMDC Current - power: - name: PZEMDC Power - energy: - name: PZEMDC Energy +<<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp32-idf.yaml b/tests/components/pzemdc/test.esp32-idf.yaml index 9cc61137de..f486544afa 100644 --- a/tests/components/pzemdc/test.esp32-idf.yaml +++ b/tests/components/pzemdc/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - pzemdc.reset_energy: pzemdc1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_pzemdc - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - stop_bits: 2 - -sensor: - - platform: pzemdc - id: pzemdc1 - voltage: - name: PZEMDC Voltage - current: - name: PZEMDC Current - power: - name: PZEMDC Power - energy: - name: PZEMDC Energy +<<: !include common.yaml diff --git a/tests/components/pzemdc/test.esp8266-ard.yaml b/tests/components/pzemdc/test.esp8266-ard.yaml index 02114b781d..b516342f3b 100644 --- a/tests/components/pzemdc/test.esp8266-ard.yaml +++ b/tests/components/pzemdc/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - pzemdc.reset_energy: pzemdc1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemdc - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - stop_bits: 2 - -sensor: - - platform: pzemdc - id: pzemdc1 - voltage: - name: PZEMDC Voltage - current: - name: PZEMDC Current - power: - name: PZEMDC Power - energy: - name: PZEMDC Energy +<<: !include common.yaml diff --git a/tests/components/pzemdc/test.rp2040-ard.yaml b/tests/components/pzemdc/test.rp2040-ard.yaml index 02114b781d..b516342f3b 100644 --- a/tests/components/pzemdc/test.rp2040-ard.yaml +++ b/tests/components/pzemdc/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -esphome: - on_boot: - then: - - pzemdc.reset_energy: pzemdc1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_pzemdc - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - stop_bits: 2 - -sensor: - - platform: pzemdc - id: pzemdc1 - voltage: - name: PZEMDC Voltage - current: - name: PZEMDC Current - power: - name: PZEMDC Power - energy: - name: PZEMDC Energy +<<: !include common.yaml From bf6874b52eca6033435f743f9a896977b790ea84 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Tue, 4 Feb 2025 20:37:22 -0600 Subject: [PATCH 0968/1052] [CI] Consolidate some tests (Q, R) (#8205) --- tests/components/qmc5883l/common.yaml | 21 ++++++++++ tests/components/qmc5883l/test.esp32-ard.yaml | 24 ++---------- .../qmc5883l/test.esp32-c3-ard.yaml | 24 ++---------- .../qmc5883l/test.esp32-c3-idf.yaml | 24 ++---------- tests/components/qmc5883l/test.esp32-idf.yaml | 24 ++---------- .../components/qmc5883l/test.esp8266-ard.yaml | 24 ++---------- .../components/qmc5883l/test.rp2040-ard.yaml | 24 ++---------- tests/components/qmp6988/common.yaml | 16 ++++++++ tests/components/qmp6988/test.esp32-ard.yaml | 19 ++-------- .../components/qmp6988/test.esp32-c3-ard.yaml | 19 ++-------- .../components/qmp6988/test.esp32-c3-idf.yaml | 19 ++-------- tests/components/qmp6988/test.esp32-idf.yaml | 19 ++-------- .../components/qmp6988/test.esp8266-ard.yaml | 19 ++-------- tests/components/qmp6988/test.rp2040-ard.yaml | 19 ++-------- tests/components/qr_code/common.yaml | 24 ++++++++++++ tests/components/qr_code/test.esp32-ard.yaml | 31 ++++----------- .../components/qr_code/test.esp32-c3-ard.yaml | 32 ++++------------ .../components/qr_code/test.esp32-c3-idf.yaml | 32 ++++------------ tests/components/qr_code/test.esp32-idf.yaml | 31 ++++----------- .../components/qr_code/test.esp8266-ard.yaml | 32 ++++------------ tests/components/qr_code/test.rp2040-ard.yaml | 32 ++++------------ tests/components/qwiic_pir/common.yaml | 8 ++++ .../components/qwiic_pir/test.esp32-ard.yaml | 11 ++---- .../qwiic_pir/test.esp32-c3-ard.yaml | 11 ++---- .../qwiic_pir/test.esp32-c3-idf.yaml | 11 ++---- .../components/qwiic_pir/test.esp32-idf.yaml | 11 ++---- .../qwiic_pir/test.esp8266-ard.yaml | 11 ++---- .../components/qwiic_pir/test.rp2040-ard.yaml | 11 ++---- tests/components/rc522_i2c/common.yaml | 17 +++++++++ .../components/rc522_i2c/test.esp32-ard.yaml | 20 ++-------- .../rc522_i2c/test.esp32-c3-ard.yaml | 20 ++-------- .../rc522_i2c/test.esp32-c3-idf.yaml | 20 ++-------- .../components/rc522_i2c/test.esp32-idf.yaml | 20 ++-------- .../rc522_i2c/test.esp8266-ard.yaml | 20 ++-------- .../components/rc522_i2c/test.rp2040-ard.yaml | 20 ++-------- tests/components/rc522_spi/common.yaml | 15 ++++++++ .../components/rc522_spi/test.esp32-ard.yaml | 20 +++------- .../rc522_spi/test.esp32-c3-ard.yaml | 20 +++------- .../rc522_spi/test.esp32-c3-idf.yaml | 20 +++------- .../components/rc522_spi/test.esp32-idf.yaml | 20 +++------- .../rc522_spi/test.esp8266-ard.yaml | 20 +++------- .../components/rc522_spi/test.rp2040-ard.yaml | 20 +++------- tests/components/rdm6300/common.yaml | 12 ++++++ tests/components/rdm6300/test.esp32-ard.yaml | 15 ++------ .../components/rdm6300/test.esp32-c3-ard.yaml | 15 ++------ .../components/rdm6300/test.esp32-c3-idf.yaml | 15 ++------ tests/components/rdm6300/test.esp32-idf.yaml | 15 ++------ .../components/rdm6300/test.esp8266-ard.yaml | 15 ++------ tests/components/rdm6300/test.rp2040-ard.yaml | 15 ++------ tests/components/resistance/common.yaml | 11 ++++++ .../components/resistance/test.esp32-ard.yaml | 16 ++------ .../resistance/test.esp32-c3-ard.yaml | 16 ++------ .../resistance/test.esp32-c3-idf.yaml | 4 ++ .../components/resistance/test.esp32-idf.yaml | 16 ++------ .../resistance/test.esp32-s2-ard.yaml | 16 ++------ .../resistance/test.esp32-s2-idf.yaml | 4 ++ .../resistance/test.esp32-s3-ard.yaml | 16 ++------ .../resistance/test.esp32-s3-idf.yaml | 4 ++ .../resistance/test.esp8266-ard.yaml | 15 ++------ .../resistance/test.rp2040-ard.yaml | 16 ++------ tests/components/rf_bridge/common.yaml | 35 +++++++++++++++++ .../components/rf_bridge/test.esp32-ard.yaml | 38 ++----------------- .../rf_bridge/test.esp32-c3-ard.yaml | 38 ++----------------- .../rf_bridge/test.esp32-c3-idf.yaml | 38 ++----------------- .../components/rf_bridge/test.esp32-idf.yaml | 38 ++----------------- .../rf_bridge/test.esp8266-ard.yaml | 38 ++----------------- .../components/rf_bridge/test.rp2040-ard.yaml | 38 ++----------------- tests/components/rgb/common.yaml | 18 +++++++++ tests/components/rgb/test.esp32-ard.yaml | 23 +++-------- tests/components/rgb/test.esp32-c3-ard.yaml | 23 +++-------- tests/components/rgb/test.esp32-c3-idf.yaml | 23 +++-------- tests/components/rgb/test.esp32-idf.yaml | 23 +++-------- tests/components/rgb/test.esp8266-ard.yaml | 23 +++-------- tests/components/rgb/test.rp2040-ard.yaml | 23 +++-------- tests/components/rgbct/common.yaml | 28 ++++++++++++++ tests/components/rgbct/test.esp32-ard.yaml | 35 ++++------------- tests/components/rgbct/test.esp32-c3-ard.yaml | 35 ++++------------- tests/components/rgbct/test.esp32-c3-idf.yaml | 35 ++++------------- tests/components/rgbct/test.esp32-idf.yaml | 35 ++++------------- tests/components/rgbct/test.esp8266-ard.yaml | 35 ++++------------- tests/components/rgbct/test.rp2040-ard.yaml | 35 ++++------------- tests/components/rgbw/common.yaml | 22 +++++++++++ tests/components/rgbw/test.esp32-ard.yaml | 28 ++++---------- tests/components/rgbw/test.esp32-c3-ard.yaml | 28 ++++---------- tests/components/rgbw/test.esp32-c3-idf.yaml | 28 ++++---------- tests/components/rgbw/test.esp32-idf.yaml | 28 ++++---------- tests/components/rgbw/test.esp8266-ard.yaml | 28 ++++---------- tests/components/rgbw/test.rp2040-ard.yaml | 28 ++++---------- tests/components/rgbww/common.yaml | 28 ++++++++++++++ tests/components/rgbww/test.esp32-ard.yaml | 35 ++++------------- tests/components/rgbww/test.esp32-c3-ard.yaml | 35 ++++------------- tests/components/rgbww/test.esp32-c3-idf.yaml | 35 ++++------------- tests/components/rgbww/test.esp32-idf.yaml | 35 ++++------------- tests/components/rgbww/test.esp8266-ard.yaml | 35 ++++------------- tests/components/rgbww/test.rp2040-ard.yaml | 35 ++++------------- tests/components/rotary_encoder/common.yaml | 25 ++++++++++++ .../rotary_encoder/test.esp32-ard.yaml | 31 +++------------ .../rotary_encoder/test.esp32-c3-ard.yaml | 31 +++------------ .../rotary_encoder/test.esp32-c3-idf.yaml | 31 +++------------ .../rotary_encoder/test.esp32-idf.yaml | 31 +++------------ .../rotary_encoder/test.esp8266-ard.yaml | 31 +++------------ .../rotary_encoder/test.rp2040-ard.yaml | 31 +++------------ tests/components/rtttl/common.yaml | 15 ++++++++ tests/components/rtttl/test.esp32-ard.yaml | 19 ++-------- tests/components/rtttl/test.esp32-c3-ard.yaml | 19 ++-------- tests/components/rtttl/test.esp32-c3-idf.yaml | 19 ++-------- tests/components/rtttl/test.esp32-idf.yaml | 19 ++-------- tests/components/rtttl/test.esp8266-ard.yaml | 18 ++------- tests/components/rtttl/test.rp2040-ard.yaml | 18 ++------- 109 files changed, 795 insertions(+), 1719 deletions(-) create mode 100644 tests/components/qmc5883l/common.yaml create mode 100644 tests/components/qmp6988/common.yaml create mode 100644 tests/components/qr_code/common.yaml create mode 100644 tests/components/qwiic_pir/common.yaml create mode 100644 tests/components/rc522_i2c/common.yaml create mode 100644 tests/components/rc522_spi/common.yaml create mode 100644 tests/components/rdm6300/common.yaml create mode 100644 tests/components/resistance/common.yaml create mode 100644 tests/components/resistance/test.esp32-c3-idf.yaml create mode 100644 tests/components/resistance/test.esp32-s2-idf.yaml create mode 100644 tests/components/resistance/test.esp32-s3-idf.yaml create mode 100644 tests/components/rf_bridge/common.yaml create mode 100644 tests/components/rgb/common.yaml create mode 100644 tests/components/rgbct/common.yaml create mode 100644 tests/components/rgbw/common.yaml create mode 100644 tests/components/rgbww/common.yaml create mode 100644 tests/components/rotary_encoder/common.yaml create mode 100644 tests/components/rtttl/common.yaml diff --git a/tests/components/qmc5883l/common.yaml b/tests/components/qmc5883l/common.yaml new file mode 100644 index 0000000000..5d8ac73b4f --- /dev/null +++ b/tests/components/qmc5883l/common.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_qmc5883l + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: qmc5883l + address: 0x0D + field_strength_x: + name: QMC5883L Field Strength X + field_strength_y: + name: QMC5883L Field Strength Y + field_strength_z: + name: QMC5883L Field Strength Z + heading: + name: QMC5883L Heading + temperature: + name: QMC5883L Temperature + range: 800uT + oversampling: 256x + update_interval: 15s diff --git a/tests/components/qmc5883l/test.esp32-ard.yaml b/tests/components/qmc5883l/test.esp32-ard.yaml index 9acd391497..63c3bd6afd 100644 --- a/tests/components/qmc5883l/test.esp32-ard.yaml +++ b/tests/components/qmc5883l/test.esp32-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_qmc5883l - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: qmc5883l - address: 0x0D - field_strength_x: - name: QMC5883L Field Strength X - field_strength_y: - name: QMC5883L Field Strength Y - field_strength_z: - name: QMC5883L Field Strength Z - heading: - name: QMC5883L Heading - temperature: - name: QMC5883L Temperature - range: 800uT - oversampling: 256x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp32-c3-ard.yaml b/tests/components/qmc5883l/test.esp32-c3-ard.yaml index 841bbd5d1e..ee2c29ca4e 100644 --- a/tests/components/qmc5883l/test.esp32-c3-ard.yaml +++ b/tests/components/qmc5883l/test.esp32-c3-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_qmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmc5883l - address: 0x0D - field_strength_x: - name: QMC5883L Field Strength X - field_strength_y: - name: QMC5883L Field Strength Y - field_strength_z: - name: QMC5883L Field Strength Z - heading: - name: QMC5883L Heading - temperature: - name: QMC5883L Temperature - range: 800uT - oversampling: 256x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp32-c3-idf.yaml b/tests/components/qmc5883l/test.esp32-c3-idf.yaml index 841bbd5d1e..ee2c29ca4e 100644 --- a/tests/components/qmc5883l/test.esp32-c3-idf.yaml +++ b/tests/components/qmc5883l/test.esp32-c3-idf.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_qmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmc5883l - address: 0x0D - field_strength_x: - name: QMC5883L Field Strength X - field_strength_y: - name: QMC5883L Field Strength Y - field_strength_z: - name: QMC5883L Field Strength Z - heading: - name: QMC5883L Heading - temperature: - name: QMC5883L Temperature - range: 800uT - oversampling: 256x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp32-idf.yaml b/tests/components/qmc5883l/test.esp32-idf.yaml index 9acd391497..63c3bd6afd 100644 --- a/tests/components/qmc5883l/test.esp32-idf.yaml +++ b/tests/components/qmc5883l/test.esp32-idf.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_qmc5883l - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: qmc5883l - address: 0x0D - field_strength_x: - name: QMC5883L Field Strength X - field_strength_y: - name: QMC5883L Field Strength Y - field_strength_z: - name: QMC5883L Field Strength Z - heading: - name: QMC5883L Heading - temperature: - name: QMC5883L Temperature - range: 800uT - oversampling: 256x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.esp8266-ard.yaml b/tests/components/qmc5883l/test.esp8266-ard.yaml index 841bbd5d1e..ee2c29ca4e 100644 --- a/tests/components/qmc5883l/test.esp8266-ard.yaml +++ b/tests/components/qmc5883l/test.esp8266-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_qmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmc5883l - address: 0x0D - field_strength_x: - name: QMC5883L Field Strength X - field_strength_y: - name: QMC5883L Field Strength Y - field_strength_z: - name: QMC5883L Field Strength Z - heading: - name: QMC5883L Heading - temperature: - name: QMC5883L Temperature - range: 800uT - oversampling: 256x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/qmc5883l/test.rp2040-ard.yaml b/tests/components/qmc5883l/test.rp2040-ard.yaml index 841bbd5d1e..ee2c29ca4e 100644 --- a/tests/components/qmc5883l/test.rp2040-ard.yaml +++ b/tests/components/qmc5883l/test.rp2040-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_qmc5883l - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmc5883l - address: 0x0D - field_strength_x: - name: QMC5883L Field Strength X - field_strength_y: - name: QMC5883L Field Strength Y - field_strength_z: - name: QMC5883L Field Strength Z - heading: - name: QMC5883L Heading - temperature: - name: QMC5883L Temperature - range: 800uT - oversampling: 256x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/qmp6988/common.yaml b/tests/components/qmp6988/common.yaml new file mode 100644 index 0000000000..cb4b221df0 --- /dev/null +++ b/tests/components/qmp6988/common.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_qmp6988 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: qmp6988 + temperature: + name: QMP6988 Temperature + oversampling: 32x + pressure: + name: QMP6988 Pressure + oversampling: 2x + address: 0x70 + update_interval: 30s + iir_filter: 16x diff --git a/tests/components/qmp6988/test.esp32-ard.yaml b/tests/components/qmp6988/test.esp32-ard.yaml index f3fbf75bbe..63c3bd6afd 100644 --- a/tests/components/qmp6988/test.esp32-ard.yaml +++ b/tests/components/qmp6988/test.esp32-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_qmp6988 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: qmp6988 - temperature: - name: QMP6988 Temperature - oversampling: 32x - pressure: - name: QMP6988 Pressure - oversampling: 2x - address: 0x70 - update_interval: 30s - iir_filter: 16x +<<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp32-c3-ard.yaml b/tests/components/qmp6988/test.esp32-c3-ard.yaml index bcd87ae6b8..ee2c29ca4e 100644 --- a/tests/components/qmp6988/test.esp32-c3-ard.yaml +++ b/tests/components/qmp6988/test.esp32-c3-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_qmp6988 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmp6988 - temperature: - name: QMP6988 Temperature - oversampling: 32x - pressure: - name: QMP6988 Pressure - oversampling: 2x - address: 0x70 - update_interval: 30s - iir_filter: 16x +<<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp32-c3-idf.yaml b/tests/components/qmp6988/test.esp32-c3-idf.yaml index bcd87ae6b8..ee2c29ca4e 100644 --- a/tests/components/qmp6988/test.esp32-c3-idf.yaml +++ b/tests/components/qmp6988/test.esp32-c3-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_qmp6988 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmp6988 - temperature: - name: QMP6988 Temperature - oversampling: 32x - pressure: - name: QMP6988 Pressure - oversampling: 2x - address: 0x70 - update_interval: 30s - iir_filter: 16x +<<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp32-idf.yaml b/tests/components/qmp6988/test.esp32-idf.yaml index f3fbf75bbe..63c3bd6afd 100644 --- a/tests/components/qmp6988/test.esp32-idf.yaml +++ b/tests/components/qmp6988/test.esp32-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_qmp6988 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: qmp6988 - temperature: - name: QMP6988 Temperature - oversampling: 32x - pressure: - name: QMP6988 Pressure - oversampling: 2x - address: 0x70 - update_interval: 30s - iir_filter: 16x +<<: !include common.yaml diff --git a/tests/components/qmp6988/test.esp8266-ard.yaml b/tests/components/qmp6988/test.esp8266-ard.yaml index bcd87ae6b8..ee2c29ca4e 100644 --- a/tests/components/qmp6988/test.esp8266-ard.yaml +++ b/tests/components/qmp6988/test.esp8266-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_qmp6988 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmp6988 - temperature: - name: QMP6988 Temperature - oversampling: 32x - pressure: - name: QMP6988 Pressure - oversampling: 2x - address: 0x70 - update_interval: 30s - iir_filter: 16x +<<: !include common.yaml diff --git a/tests/components/qmp6988/test.rp2040-ard.yaml b/tests/components/qmp6988/test.rp2040-ard.yaml index bcd87ae6b8..ee2c29ca4e 100644 --- a/tests/components/qmp6988/test.rp2040-ard.yaml +++ b/tests/components/qmp6988/test.rp2040-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_qmp6988 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: qmp6988 - temperature: - name: QMP6988 Temperature - oversampling: 32x - pressure: - name: QMP6988 Pressure - oversampling: 2x - address: 0x70 - update_interval: 30s - iir_filter: 16x +<<: !include common.yaml diff --git a/tests/components/qr_code/common.yaml b/tests/components/qr_code/common.yaml new file mode 100644 index 0000000000..85c2ee076b --- /dev/null +++ b/tests/components/qr_code/common.yaml @@ -0,0 +1,24 @@ +spi: + - id: spi_main_lcd + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ili9xxx + id: main_lcd + model: ili9342 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + invert_colors: false + lambda: |- + // Draw a QR code in the center of the screen + auto scale = 2; + auto size = id(homepage_qr).get_size() * scale; + auto x = (it.get_width() / 2) - (size / 2); + auto y = (it.get_height() / 2) - (size / 2); + it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); + +qr_code: + - id: homepage_qr + value: https://esphome.io/index.html diff --git a/tests/components/qr_code/test.esp32-ard.yaml b/tests/components/qr_code/test.esp32-ard.yaml index 8689d4d73f..bad5241f79 100644 --- a/tests/components/qr_code/test.esp32-ard.yaml +++ b/tests/components/qr_code/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 12 - dc_pin: 13 - reset_pin: 21 - invert_colors: false - lambda: |- - // Draw a QR code in the center of the screen - auto scale = 2; - auto size = id(homepage_qr).get_size() * scale; - auto x = (it.get_width() / 2) - (size / 2); - auto y = (it.get_height() / 2) - (size / 2); - it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); - -qr_code: - - id: homepage_qr - value: https://esphome.io/index.html +<<: !include common.yaml diff --git a/tests/components/qr_code/test.esp32-c3-ard.yaml b/tests/components/qr_code/test.esp32-c3-ard.yaml index 3690d2598c..c5c932c92c 100644 --- a/tests/components/qr_code/test.esp32-c3-ard.yaml +++ b/tests/components/qr_code/test.esp32-c3-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false - lambda: |- - // Draw a QR code in the center of the screen - auto scale = 2; - auto size = id(homepage_qr).get_size() * scale; - auto x = (it.get_width() / 2) - (size / 2); - auto y = (it.get_height() / 2) - (size / 2); - it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); - -qr_code: - - id: homepage_qr - value: https://esphome.io/index.html +<<: !include common.yaml diff --git a/tests/components/qr_code/test.esp32-c3-idf.yaml b/tests/components/qr_code/test.esp32-c3-idf.yaml index 3690d2598c..c5c932c92c 100644 --- a/tests/components/qr_code/test.esp32-c3-idf.yaml +++ b/tests/components/qr_code/test.esp32-c3-idf.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false - lambda: |- - // Draw a QR code in the center of the screen - auto scale = 2; - auto size = id(homepage_qr).get_size() * scale; - auto x = (it.get_width() / 2) - (size / 2); - auto y = (it.get_height() / 2) - (size / 2); - it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); - -qr_code: - - id: homepage_qr - value: https://esphome.io/index.html +<<: !include common.yaml diff --git a/tests/components/qr_code/test.esp32-idf.yaml b/tests/components/qr_code/test.esp32-idf.yaml index 8689d4d73f..bad5241f79 100644 --- a/tests/components/qr_code/test.esp32-idf.yaml +++ b/tests/components/qr_code/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_main_lcd - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 12 - dc_pin: 13 - reset_pin: 21 - invert_colors: false - lambda: |- - // Draw a QR code in the center of the screen - auto scale = 2; - auto size = id(homepage_qr).get_size() * scale; - auto x = (it.get_width() / 2) - (size / 2); - auto y = (it.get_height() / 2) - (size / 2); - it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); - -qr_code: - - id: homepage_qr - value: https://esphome.io/index.html +<<: !include common.yaml diff --git a/tests/components/qr_code/test.esp8266-ard.yaml b/tests/components/qr_code/test.esp8266-ard.yaml index 02dc183440..3f023a60eb 100644 --- a/tests/components/qr_code/test.esp8266-ard.yaml +++ b/tests/components/qr_code/test.esp8266-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 5 - dc_pin: 15 - reset_pin: 16 - invert_colors: false - lambda: |- - // Draw a QR code in the center of the screen - auto scale = 2; - auto size = id(homepage_qr).get_size() * scale; - auto x = (it.get_width() / 2) - (size / 2); - auto y = (it.get_height() / 2) - (size / 2); - it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); - -qr_code: - - id: homepage_qr - value: https://esphome.io/index.html +<<: !include common.yaml diff --git a/tests/components/qr_code/test.rp2040-ard.yaml b/tests/components/qr_code/test.rp2040-ard.yaml index 0d86f8d213..d7fd6ee294 100644 --- a/tests/components/qr_code/test.rp2040-ard.yaml +++ b/tests/components/qr_code/test.rp2040-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_main_lcd - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ili9xxx - id: main_lcd - model: ili9342 - cs_pin: 20 - dc_pin: 21 - reset_pin: 22 - invert_colors: false - lambda: |- - // Draw a QR code in the center of the screen - auto scale = 2; - auto size = id(homepage_qr).get_size() * scale; - auto x = (it.get_width() / 2) - (size / 2); - auto y = (it.get_height() / 2) - (size / 2); - it.qr_code(x, y, id(homepage_qr), Color(255,255,255), scale); - -qr_code: - - id: homepage_qr - value: https://esphome.io/index.html +<<: !include common.yaml diff --git a/tests/components/qwiic_pir/common.yaml b/tests/components/qwiic_pir/common.yaml new file mode 100644 index 0000000000..d4b733405d --- /dev/null +++ b/tests/components/qwiic_pir/common.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_qwiic_pir + scl: ${scl_pin} + sda: ${sda_pin} + +binary_sensor: + - platform: qwiic_pir + name: Qwiic PIR Motion Sensor diff --git a/tests/components/qwiic_pir/test.esp32-ard.yaml b/tests/components/qwiic_pir/test.esp32-ard.yaml index da2e275cf3..63c3bd6afd 100644 --- a/tests/components/qwiic_pir/test.esp32-ard.yaml +++ b/tests/components/qwiic_pir/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_qwiic_pir - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -binary_sensor: - - platform: qwiic_pir - name: Qwiic PIR Motion Sensor +<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp32-c3-ard.yaml b/tests/components/qwiic_pir/test.esp32-c3-ard.yaml index ad52ac91c5..ee2c29ca4e 100644 --- a/tests/components/qwiic_pir/test.esp32-c3-ard.yaml +++ b/tests/components/qwiic_pir/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_qwiic_pir - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -binary_sensor: - - platform: qwiic_pir - name: Qwiic PIR Motion Sensor +<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp32-c3-idf.yaml b/tests/components/qwiic_pir/test.esp32-c3-idf.yaml index ad52ac91c5..ee2c29ca4e 100644 --- a/tests/components/qwiic_pir/test.esp32-c3-idf.yaml +++ b/tests/components/qwiic_pir/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_qwiic_pir - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -binary_sensor: - - platform: qwiic_pir - name: Qwiic PIR Motion Sensor +<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp32-idf.yaml b/tests/components/qwiic_pir/test.esp32-idf.yaml index da2e275cf3..63c3bd6afd 100644 --- a/tests/components/qwiic_pir/test.esp32-idf.yaml +++ b/tests/components/qwiic_pir/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_qwiic_pir - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -binary_sensor: - - platform: qwiic_pir - name: Qwiic PIR Motion Sensor +<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.esp8266-ard.yaml b/tests/components/qwiic_pir/test.esp8266-ard.yaml index ad52ac91c5..ee2c29ca4e 100644 --- a/tests/components/qwiic_pir/test.esp8266-ard.yaml +++ b/tests/components/qwiic_pir/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_qwiic_pir - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -binary_sensor: - - platform: qwiic_pir - name: Qwiic PIR Motion Sensor +<<: !include common.yaml diff --git a/tests/components/qwiic_pir/test.rp2040-ard.yaml b/tests/components/qwiic_pir/test.rp2040-ard.yaml index ad52ac91c5..ee2c29ca4e 100644 --- a/tests/components/qwiic_pir/test.rp2040-ard.yaml +++ b/tests/components/qwiic_pir/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_qwiic_pir - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -binary_sensor: - - platform: qwiic_pir - name: Qwiic PIR Motion Sensor +<<: !include common.yaml diff --git a/tests/components/rc522_i2c/common.yaml b/tests/components/rc522_i2c/common.yaml new file mode 100644 index 0000000000..b8b7a41bc7 --- /dev/null +++ b/tests/components/rc522_i2c/common.yaml @@ -0,0 +1,17 @@ +i2c: + - id: i2c_rc522 + scl: ${scl_pin} + sda: ${sda_pin} + +rc522_i2c: + - id: rc522_nfcc + update_interval: 1s + on_tag: + - lambda: |- + ESP_LOGD("main", "Found tag %s", x.c_str()); + +binary_sensor: + - platform: rc522 + rc522_id: rc522_nfcc + name: RC522 NFC Tag + uid: 74-10-37-94 diff --git a/tests/components/rc522_i2c/test.esp32-ard.yaml b/tests/components/rc522_i2c/test.esp32-ard.yaml index 69b7d892a4..63c3bd6afd 100644 --- a/tests/components/rc522_i2c/test.esp32-ard.yaml +++ b/tests/components/rc522_i2c/test.esp32-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_rc522 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -rc522_i2c: - - id: rc522_nfcc - update_interval: 1s - on_tag: - - lambda: |- - ESP_LOGD("main", "Found tag %s", x.c_str()); - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: RC522 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp32-c3-ard.yaml b/tests/components/rc522_i2c/test.esp32-c3-ard.yaml index 8c8819e257..ee2c29ca4e 100644 --- a/tests/components/rc522_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/rc522_i2c/test.esp32-c3-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_rc522 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -rc522_i2c: - - id: rc522_nfcc - update_interval: 1s - on_tag: - - lambda: |- - ESP_LOGD("main", "Found tag %s", x.c_str()); - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: RC522 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp32-c3-idf.yaml b/tests/components/rc522_i2c/test.esp32-c3-idf.yaml index 8c8819e257..ee2c29ca4e 100644 --- a/tests/components/rc522_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/rc522_i2c/test.esp32-c3-idf.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_rc522 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -rc522_i2c: - - id: rc522_nfcc - update_interval: 1s - on_tag: - - lambda: |- - ESP_LOGD("main", "Found tag %s", x.c_str()); - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: RC522 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp32-idf.yaml b/tests/components/rc522_i2c/test.esp32-idf.yaml index 69b7d892a4..63c3bd6afd 100644 --- a/tests/components/rc522_i2c/test.esp32-idf.yaml +++ b/tests/components/rc522_i2c/test.esp32-idf.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_rc522 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -rc522_i2c: - - id: rc522_nfcc - update_interval: 1s - on_tag: - - lambda: |- - ESP_LOGD("main", "Found tag %s", x.c_str()); - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: RC522 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.esp8266-ard.yaml b/tests/components/rc522_i2c/test.esp8266-ard.yaml index 8c8819e257..ee2c29ca4e 100644 --- a/tests/components/rc522_i2c/test.esp8266-ard.yaml +++ b/tests/components/rc522_i2c/test.esp8266-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_rc522 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -rc522_i2c: - - id: rc522_nfcc - update_interval: 1s - on_tag: - - lambda: |- - ESP_LOGD("main", "Found tag %s", x.c_str()); - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: RC522 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_i2c/test.rp2040-ard.yaml b/tests/components/rc522_i2c/test.rp2040-ard.yaml index 8c8819e257..ee2c29ca4e 100644 --- a/tests/components/rc522_i2c/test.rp2040-ard.yaml +++ b/tests/components/rc522_i2c/test.rp2040-ard.yaml @@ -1,17 +1,5 @@ -i2c: - - id: i2c_rc522 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -rc522_i2c: - - id: rc522_nfcc - update_interval: 1s - on_tag: - - lambda: |- - ESP_LOGD("main", "Found tag %s", x.c_str()); - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: RC522 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_spi/common.yaml b/tests/components/rc522_spi/common.yaml new file mode 100644 index 0000000000..5c42858993 --- /dev/null +++ b/tests/components/rc522_spi/common.yaml @@ -0,0 +1,15 @@ +spi: + - id: spi_rc522 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +rc522_spi: + id: rc522_nfcc + cs_pin: ${cs_pin} + +binary_sensor: + - platform: rc522 + rc522_id: rc522_nfcc + name: PN532 NFC Tag + uid: 74-10-37-94 diff --git a/tests/components/rc522_spi/test.esp32-ard.yaml b/tests/components/rc522_spi/test.esp32-ard.yaml index 5c0b698a08..54e027a614 100644 --- a/tests/components/rc522_spi/test.esp32-ard.yaml +++ b/tests/components/rc522_spi/test.esp32-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_rc522 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -rc522_spi: - id: rc522_nfcc - cs_pin: 12 - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp32-c3-ard.yaml b/tests/components/rc522_spi/test.esp32-c3-ard.yaml index 8bcab84700..2415ba5dc6 100644 --- a/tests/components/rc522_spi/test.esp32-c3-ard.yaml +++ b/tests/components/rc522_spi/test.esp32-c3-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_rc522 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -rc522_spi: - id: rc522_nfcc - cs_pin: 4 - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp32-c3-idf.yaml b/tests/components/rc522_spi/test.esp32-c3-idf.yaml index 8bcab84700..2415ba5dc6 100644 --- a/tests/components/rc522_spi/test.esp32-c3-idf.yaml +++ b/tests/components/rc522_spi/test.esp32-c3-idf.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_rc522 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -rc522_spi: - id: rc522_nfcc - cs_pin: 4 - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp32-idf.yaml b/tests/components/rc522_spi/test.esp32-idf.yaml index 5c0b698a08..54e027a614 100644 --- a/tests/components/rc522_spi/test.esp32-idf.yaml +++ b/tests/components/rc522_spi/test.esp32-idf.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_rc522 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + cs_pin: GPIO5 -rc522_spi: - id: rc522_nfcc - cs_pin: 12 - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.esp8266-ard.yaml b/tests/components/rc522_spi/test.esp8266-ard.yaml index 3c33311266..dbd158d030 100644 --- a/tests/components/rc522_spi/test.esp8266-ard.yaml +++ b/tests/components/rc522_spi/test.esp8266-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_rc522 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO15 -rc522_spi: - id: rc522_nfcc - cs_pin: 15 - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rc522_spi/test.rp2040-ard.yaml b/tests/components/rc522_spi/test.rp2040-ard.yaml index ed2827dbb9..f6c3f1eeca 100644 --- a/tests/components/rc522_spi/test.rp2040-ard.yaml +++ b/tests/components/rc522_spi/test.rp2040-ard.yaml @@ -1,15 +1,7 @@ -spi: - - id: spi_rc522 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -rc522_spi: - id: rc522_nfcc - cs_pin: 6 - -binary_sensor: - - platform: rc522 - rc522_id: rc522_nfcc - name: PN532 NFC Tag - uid: 74-10-37-94 +<<: !include common.yaml diff --git a/tests/components/rdm6300/common.yaml b/tests/components/rdm6300/common.yaml new file mode 100644 index 0000000000..118a295471 --- /dev/null +++ b/tests/components/rdm6300/common.yaml @@ -0,0 +1,12 @@ +uart: + - id: uart_rdm6300 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +rdm6300: + +binary_sensor: + - platform: rdm6300 + uid: 7616525 + name: RDM6300 NFC Tag diff --git a/tests/components/rdm6300/test.esp32-ard.yaml b/tests/components/rdm6300/test.esp32-ard.yaml index 4159248124..f486544afa 100644 --- a/tests/components/rdm6300/test.esp32-ard.yaml +++ b/tests/components/rdm6300/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_rdm6300 - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -rdm6300: - -binary_sensor: - - platform: rdm6300 - uid: 7616525 - name: RDM6300 NFC Tag +<<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp32-c3-ard.yaml b/tests/components/rdm6300/test.esp32-c3-ard.yaml index b92fce06e2..b516342f3b 100644 --- a/tests/components/rdm6300/test.esp32-c3-ard.yaml +++ b/tests/components/rdm6300/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_rdm6300 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rdm6300: - -binary_sensor: - - platform: rdm6300 - uid: 7616525 - name: RDM6300 NFC Tag +<<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp32-c3-idf.yaml b/tests/components/rdm6300/test.esp32-c3-idf.yaml index b92fce06e2..b516342f3b 100644 --- a/tests/components/rdm6300/test.esp32-c3-idf.yaml +++ b/tests/components/rdm6300/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_rdm6300 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rdm6300: - -binary_sensor: - - platform: rdm6300 - uid: 7616525 - name: RDM6300 NFC Tag +<<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp32-idf.yaml b/tests/components/rdm6300/test.esp32-idf.yaml index 4159248124..f486544afa 100644 --- a/tests/components/rdm6300/test.esp32-idf.yaml +++ b/tests/components/rdm6300/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_rdm6300 - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -rdm6300: - -binary_sensor: - - platform: rdm6300 - uid: 7616525 - name: RDM6300 NFC Tag +<<: !include common.yaml diff --git a/tests/components/rdm6300/test.esp8266-ard.yaml b/tests/components/rdm6300/test.esp8266-ard.yaml index b92fce06e2..b516342f3b 100644 --- a/tests/components/rdm6300/test.esp8266-ard.yaml +++ b/tests/components/rdm6300/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_rdm6300 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rdm6300: - -binary_sensor: - - platform: rdm6300 - uid: 7616525 - name: RDM6300 NFC Tag +<<: !include common.yaml diff --git a/tests/components/rdm6300/test.rp2040-ard.yaml b/tests/components/rdm6300/test.rp2040-ard.yaml index b92fce06e2..b516342f3b 100644 --- a/tests/components/rdm6300/test.rp2040-ard.yaml +++ b/tests/components/rdm6300/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_rdm6300 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rdm6300: - -binary_sensor: - - platform: rdm6300 - uid: 7616525 - name: RDM6300 NFC Tag +<<: !include common.yaml diff --git a/tests/components/resistance/common.yaml b/tests/components/resistance/common.yaml new file mode 100644 index 0000000000..b3eec49548 --- /dev/null +++ b/tests/components/resistance/common.yaml @@ -0,0 +1,11 @@ +sensor: + - platform: adc + id: my_sensor + pin: ${pin} + - platform: resistance + sensor: my_sensor + configuration: DOWNSTREAM + resistor: 10kΩ + reference_voltage: 3.3V + name: Resistance + id: resist diff --git a/tests/components/resistance/test.esp32-ard.yaml b/tests/components/resistance/test.esp32-ard.yaml index b1ffc64972..06864605a6 100644 --- a/tests/components/resistance/test.esp32-ard.yaml +++ b/tests/components/resistance/test.esp32-ard.yaml @@ -1,12 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 32 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist +substitutions: + pin: GPIO32 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-c3-ard.yaml b/tests/components/resistance/test.esp32-c3-ard.yaml index 84e23d5115..37fb325f4a 100644 --- a/tests/components/resistance/test.esp32-c3-ard.yaml +++ b/tests/components/resistance/test.esp32-c3-ard.yaml @@ -1,12 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 4 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-c3-idf.yaml b/tests/components/resistance/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..37fb325f4a --- /dev/null +++ b/tests/components/resistance/test.esp32-c3-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-idf.yaml b/tests/components/resistance/test.esp32-idf.yaml index b1ffc64972..06864605a6 100644 --- a/tests/components/resistance/test.esp32-idf.yaml +++ b/tests/components/resistance/test.esp32-idf.yaml @@ -1,12 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 32 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist +substitutions: + pin: GPIO32 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-s2-ard.yaml b/tests/components/resistance/test.esp32-s2-ard.yaml index 4ebd6b5c49..1910f325ae 100644 --- a/tests/components/resistance/test.esp32-s2-ard.yaml +++ b/tests/components/resistance/test.esp32-s2-ard.yaml @@ -1,12 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 1 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist +substitutions: + pin: GPIO1 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-s2-idf.yaml b/tests/components/resistance/test.esp32-s2-idf.yaml new file mode 100644 index 0000000000..1910f325ae --- /dev/null +++ b/tests/components/resistance/test.esp32-s2-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO1 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-s3-ard.yaml b/tests/components/resistance/test.esp32-s3-ard.yaml index 4ebd6b5c49..1910f325ae 100644 --- a/tests/components/resistance/test.esp32-s3-ard.yaml +++ b/tests/components/resistance/test.esp32-s3-ard.yaml @@ -1,12 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: 1 - attenuation: 11db - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist +substitutions: + pin: GPIO1 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp32-s3-idf.yaml b/tests/components/resistance/test.esp32-s3-idf.yaml new file mode 100644 index 0000000000..1910f325ae --- /dev/null +++ b/tests/components/resistance/test.esp32-s3-idf.yaml @@ -0,0 +1,4 @@ +substitutions: + pin: GPIO1 + +<<: !include common.yaml diff --git a/tests/components/resistance/test.esp8266-ard.yaml b/tests/components/resistance/test.esp8266-ard.yaml index f723f7c7c7..9e34247530 100644 --- a/tests/components/resistance/test.esp8266-ard.yaml +++ b/tests/components/resistance/test.esp8266-ard.yaml @@ -1,11 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - pin: VCC - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist +substitutions: + pin: VCC + +<<: !include common.yaml diff --git a/tests/components/resistance/test.rp2040-ard.yaml b/tests/components/resistance/test.rp2040-ard.yaml index 5cc643926a..9e34247530 100644 --- a/tests/components/resistance/test.rp2040-ard.yaml +++ b/tests/components/resistance/test.rp2040-ard.yaml @@ -1,12 +1,4 @@ -sensor: - - platform: adc - id: my_sensor - name: VSYS - pin: VCC - - platform: resistance - sensor: my_sensor - configuration: DOWNSTREAM - resistor: 10kΩ - reference_voltage: 3.3V - name: Resistance - id: resist +substitutions: + pin: VCC + +<<: !include common.yaml diff --git a/tests/components/rf_bridge/common.yaml b/tests/components/rf_bridge/common.yaml new file mode 100644 index 0000000000..eaadc4bb9c --- /dev/null +++ b/tests/components/rf_bridge/common.yaml @@ -0,0 +1,35 @@ +uart: + - id: uart_rf_bridge + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +rf_bridge: + on_code_received: + - lambda: |- + uint32_t test; + test = data.sync; + test = data.low; + test = data.high; + test = data.code; + - rf_bridge.send_code: + sync: 0x1234 + low: 0x1234 + high: 0x1234 + code: 0x123456 + - rf_bridge.learn + on_advanced_code_received: + - lambda: |- + uint32_t test; + std::string test_code; + test = data.length; + test = data.protocol; + test_code = data.code; + - rf_bridge.start_advanced_sniffing: + - rf_bridge.stop_advanced_sniffing: + - rf_bridge.send_advanced_code: + length: 0x04 + protocol: 0x01 + code: "ABC123" + - rf_bridge.send_raw: + raw: "AAA5070008001000ABC12355" diff --git a/tests/components/rf_bridge/test.esp32-ard.yaml b/tests/components/rf_bridge/test.esp32-ard.yaml index 9ade7f0ac0..f486544afa 100644 --- a/tests/components/rf_bridge/test.esp32-ard.yaml +++ b/tests/components/rf_bridge/test.esp32-ard.yaml @@ -1,35 +1,5 @@ -uart: - - id: uart_rf_bridge - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -rf_bridge: - on_code_received: - - lambda: |- - uint32_t test; - test = data.sync; - test = data.low; - test = data.high; - test = data.code; - - rf_bridge.send_code: - sync: 0x1234 - low: 0x1234 - high: 0x1234 - code: 0x123456 - - rf_bridge.learn - on_advanced_code_received: - - lambda: |- - uint32_t test; - std::string test_code; - test = data.length; - test = data.protocol; - test_code = data.code; - - rf_bridge.start_advanced_sniffing: - - rf_bridge.stop_advanced_sniffing: - - rf_bridge.send_advanced_code: - length: 0x04 - protocol: 0x01 - code: "ABC123" - - rf_bridge.send_raw: - raw: "AAA5070008001000ABC12355" +<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp32-c3-ard.yaml b/tests/components/rf_bridge/test.esp32-c3-ard.yaml index 95a7aa861a..b516342f3b 100644 --- a/tests/components/rf_bridge/test.esp32-c3-ard.yaml +++ b/tests/components/rf_bridge/test.esp32-c3-ard.yaml @@ -1,35 +1,5 @@ -uart: - - id: uart_rf_bridge - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rf_bridge: - on_code_received: - - lambda: |- - uint32_t test; - test = data.sync; - test = data.low; - test = data.high; - test = data.code; - - rf_bridge.send_code: - sync: 0x1234 - low: 0x1234 - high: 0x1234 - code: 0x123456 - - rf_bridge.learn - on_advanced_code_received: - - lambda: |- - uint32_t test; - std::string test_code; - test = data.length; - test = data.protocol; - test_code = data.code; - - rf_bridge.start_advanced_sniffing: - - rf_bridge.stop_advanced_sniffing: - - rf_bridge.send_advanced_code: - length: 0x04 - protocol: 0x01 - code: "ABC123" - - rf_bridge.send_raw: - raw: "AAA5070008001000ABC12355" +<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp32-c3-idf.yaml b/tests/components/rf_bridge/test.esp32-c3-idf.yaml index 95a7aa861a..b516342f3b 100644 --- a/tests/components/rf_bridge/test.esp32-c3-idf.yaml +++ b/tests/components/rf_bridge/test.esp32-c3-idf.yaml @@ -1,35 +1,5 @@ -uart: - - id: uart_rf_bridge - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rf_bridge: - on_code_received: - - lambda: |- - uint32_t test; - test = data.sync; - test = data.low; - test = data.high; - test = data.code; - - rf_bridge.send_code: - sync: 0x1234 - low: 0x1234 - high: 0x1234 - code: 0x123456 - - rf_bridge.learn - on_advanced_code_received: - - lambda: |- - uint32_t test; - std::string test_code; - test = data.length; - test = data.protocol; - test_code = data.code; - - rf_bridge.start_advanced_sniffing: - - rf_bridge.stop_advanced_sniffing: - - rf_bridge.send_advanced_code: - length: 0x04 - protocol: 0x01 - code: "ABC123" - - rf_bridge.send_raw: - raw: "AAA5070008001000ABC12355" +<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp32-idf.yaml b/tests/components/rf_bridge/test.esp32-idf.yaml index 9ade7f0ac0..f486544afa 100644 --- a/tests/components/rf_bridge/test.esp32-idf.yaml +++ b/tests/components/rf_bridge/test.esp32-idf.yaml @@ -1,35 +1,5 @@ -uart: - - id: uart_rf_bridge - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -rf_bridge: - on_code_received: - - lambda: |- - uint32_t test; - test = data.sync; - test = data.low; - test = data.high; - test = data.code; - - rf_bridge.send_code: - sync: 0x1234 - low: 0x1234 - high: 0x1234 - code: 0x123456 - - rf_bridge.learn - on_advanced_code_received: - - lambda: |- - uint32_t test; - std::string test_code; - test = data.length; - test = data.protocol; - test_code = data.code; - - rf_bridge.start_advanced_sniffing: - - rf_bridge.stop_advanced_sniffing: - - rf_bridge.send_advanced_code: - length: 0x04 - protocol: 0x01 - code: "ABC123" - - rf_bridge.send_raw: - raw: "AAA5070008001000ABC12355" +<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.esp8266-ard.yaml b/tests/components/rf_bridge/test.esp8266-ard.yaml index 95a7aa861a..b516342f3b 100644 --- a/tests/components/rf_bridge/test.esp8266-ard.yaml +++ b/tests/components/rf_bridge/test.esp8266-ard.yaml @@ -1,35 +1,5 @@ -uart: - - id: uart_rf_bridge - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rf_bridge: - on_code_received: - - lambda: |- - uint32_t test; - test = data.sync; - test = data.low; - test = data.high; - test = data.code; - - rf_bridge.send_code: - sync: 0x1234 - low: 0x1234 - high: 0x1234 - code: 0x123456 - - rf_bridge.learn - on_advanced_code_received: - - lambda: |- - uint32_t test; - std::string test_code; - test = data.length; - test = data.protocol; - test_code = data.code; - - rf_bridge.start_advanced_sniffing: - - rf_bridge.stop_advanced_sniffing: - - rf_bridge.send_advanced_code: - length: 0x04 - protocol: 0x01 - code: "ABC123" - - rf_bridge.send_raw: - raw: "AAA5070008001000ABC12355" +<<: !include common.yaml diff --git a/tests/components/rf_bridge/test.rp2040-ard.yaml b/tests/components/rf_bridge/test.rp2040-ard.yaml index 95a7aa861a..b516342f3b 100644 --- a/tests/components/rf_bridge/test.rp2040-ard.yaml +++ b/tests/components/rf_bridge/test.rp2040-ard.yaml @@ -1,35 +1,5 @@ -uart: - - id: uart_rf_bridge - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -rf_bridge: - on_code_received: - - lambda: |- - uint32_t test; - test = data.sync; - test = data.low; - test = data.high; - test = data.code; - - rf_bridge.send_code: - sync: 0x1234 - low: 0x1234 - high: 0x1234 - code: 0x123456 - - rf_bridge.learn - on_advanced_code_received: - - lambda: |- - uint32_t test; - std::string test_code; - test = data.length; - test = data.protocol; - test_code = data.code; - - rf_bridge.start_advanced_sniffing: - - rf_bridge.stop_advanced_sniffing: - - rf_bridge.send_advanced_code: - length: 0x04 - protocol: 0x01 - code: "ABC123" - - rf_bridge.send_raw: - raw: "AAA5070008001000ABC12355" +<<: !include common.yaml diff --git a/tests/components/rgb/common.yaml b/tests/components/rgb/common.yaml new file mode 100644 index 0000000000..9f25efa431 --- /dev/null +++ b/tests/components/rgb/common.yaml @@ -0,0 +1,18 @@ +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin2} + - platform: ${light_platform} + id: light_output_3 + pin: ${pin3} + +light: + - platform: rgb + name: RGB Light + id: rgb_light + red: light_output_1 + green: light_output_2 + blue: light_output_3 diff --git a/tests/components/rgb/test.esp32-ard.yaml b/tests/components/rgb/test.esp32-ard.yaml index 2173e718be..d78ccec952 100644 --- a/tests/components/rgb/test.esp32-ard.yaml +++ b/tests/components/rgb/test.esp32-ard.yaml @@ -1,18 +1,7 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 -light: - - platform: rgb - name: RGB Light - id: rgb_light - red: light_output_1 - green: light_output_2 - blue: light_output_3 +<<: !include common.yaml diff --git a/tests/components/rgb/test.esp32-c3-ard.yaml b/tests/components/rgb/test.esp32-c3-ard.yaml index 30ff1527b4..1fe4a4bb90 100644 --- a/tests/components/rgb/test.esp32-c3-ard.yaml +++ b/tests/components/rgb/test.esp32-c3-ard.yaml @@ -1,18 +1,7 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 -light: - - platform: rgb - name: RGB Light - id: rgb_light - red: light_output_1 - green: light_output_2 - blue: light_output_3 +<<: !include common.yaml diff --git a/tests/components/rgb/test.esp32-c3-idf.yaml b/tests/components/rgb/test.esp32-c3-idf.yaml index 30ff1527b4..1fe4a4bb90 100644 --- a/tests/components/rgb/test.esp32-c3-idf.yaml +++ b/tests/components/rgb/test.esp32-c3-idf.yaml @@ -1,18 +1,7 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 -light: - - platform: rgb - name: RGB Light - id: rgb_light - red: light_output_1 - green: light_output_2 - blue: light_output_3 +<<: !include common.yaml diff --git a/tests/components/rgb/test.esp32-idf.yaml b/tests/components/rgb/test.esp32-idf.yaml index 2173e718be..d78ccec952 100644 --- a/tests/components/rgb/test.esp32-idf.yaml +++ b/tests/components/rgb/test.esp32-idf.yaml @@ -1,18 +1,7 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 -light: - - platform: rgb - name: RGB Light - id: rgb_light - red: light_output_1 - green: light_output_2 - blue: light_output_3 +<<: !include common.yaml diff --git a/tests/components/rgb/test.esp8266-ard.yaml b/tests/components/rgb/test.esp8266-ard.yaml index 60c5a7e04f..3c9fa80f9e 100644 --- a/tests/components/rgb/test.esp8266-ard.yaml +++ b/tests/components/rgb/test.esp8266-ard.yaml @@ -1,18 +1,7 @@ -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 12 - - platform: esp8266_pwm - id: light_output_2 - pin: 13 - - platform: esp8266_pwm - id: light_output_3 - pin: 14 +substitutions: + light_platform: esp8266_pwm + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 -light: - - platform: rgb - name: RGB Light - id: rgb_light - red: light_output_1 - green: light_output_2 - blue: light_output_3 +<<: !include common.yaml diff --git a/tests/components/rgb/test.rp2040-ard.yaml b/tests/components/rgb/test.rp2040-ard.yaml index fd6519707b..7d8e595f72 100644 --- a/tests/components/rgb/test.rp2040-ard.yaml +++ b/tests/components/rgb/test.rp2040-ard.yaml @@ -1,18 +1,7 @@ -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 12 - - platform: rp2040_pwm - id: light_output_2 - pin: 13 - - platform: rp2040_pwm - id: light_output_3 - pin: 14 +substitutions: + light_platform: rp2040_pwm + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 -light: - - platform: rgb - name: RGB Light - id: rgb_light - red: light_output_1 - green: light_output_2 - blue: light_output_3 +<<: !include common.yaml diff --git a/tests/components/rgbct/common.yaml b/tests/components/rgbct/common.yaml new file mode 100644 index 0000000000..65bb248e95 --- /dev/null +++ b/tests/components/rgbct/common.yaml @@ -0,0 +1,28 @@ +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin2} + - platform: ${light_platform} + id: light_output_3 + pin: ${pin3} + - platform: ${light_platform} + id: light_output_4 + pin: ${pin4} + - platform: ${light_platform} + id: light_output_5 + pin: ${pin5} + +light: + - platform: rgbct + name: RGBCT Light + red: light_output_1 + green: light_output_2 + blue: light_output_3 + color_temperature: light_output_4 + white_brightness: light_output_5 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + color_interlock: true diff --git a/tests/components/rgbct/test.esp32-ard.yaml b/tests/components/rgbct/test.esp32-ard.yaml index d9758c9ec7..1ecc626e9c 100644 --- a/tests/components/rgbct/test.esp32-ard.yaml +++ b/tests/components/rgbct/test.esp32-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 - - platform: ledc - id: light_output_4 - pin: 15 - - platform: ledc - id: light_output_5 - pin: 16 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 + pin5: GPIO16 -light: - - platform: rgbct - name: RGBCT Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - color_temperature: light_output_4 - white_brightness: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbct/test.esp32-c3-ard.yaml b/tests/components/rgbct/test.esp32-c3-ard.yaml index 426c4b8937..27a1fbca4d 100644 --- a/tests/components/rgbct/test.esp32-c3-ard.yaml +++ b/tests/components/rgbct/test.esp32-c3-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 - - platform: ledc - id: light_output_4 - pin: 4 - - platform: ledc - id: light_output_5 - pin: 5 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 + pin5: GPIO6 -light: - - platform: rgbct - name: RGBCT Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - color_temperature: light_output_4 - white_brightness: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbct/test.esp32-c3-idf.yaml b/tests/components/rgbct/test.esp32-c3-idf.yaml index 426c4b8937..27a1fbca4d 100644 --- a/tests/components/rgbct/test.esp32-c3-idf.yaml +++ b/tests/components/rgbct/test.esp32-c3-idf.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 - - platform: ledc - id: light_output_4 - pin: 4 - - platform: ledc - id: light_output_5 - pin: 5 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 + pin5: GPIO6 -light: - - platform: rgbct - name: RGBCT Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - color_temperature: light_output_4 - white_brightness: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbct/test.esp32-idf.yaml b/tests/components/rgbct/test.esp32-idf.yaml index d9758c9ec7..1ecc626e9c 100644 --- a/tests/components/rgbct/test.esp32-idf.yaml +++ b/tests/components/rgbct/test.esp32-idf.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 - - platform: ledc - id: light_output_4 - pin: 15 - - platform: ledc - id: light_output_5 - pin: 16 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 + pin5: GPIO16 -light: - - platform: rgbct - name: RGBCT Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - color_temperature: light_output_4 - white_brightness: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbct/test.esp8266-ard.yaml b/tests/components/rgbct/test.esp8266-ard.yaml index b7008c9ae3..4b66a5927f 100644 --- a/tests/components/rgbct/test.esp8266-ard.yaml +++ b/tests/components/rgbct/test.esp8266-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 12 - - platform: esp8266_pwm - id: light_output_2 - pin: 13 - - platform: esp8266_pwm - id: light_output_3 - pin: 14 - - platform: esp8266_pwm - id: light_output_4 - pin: 15 - - platform: esp8266_pwm - id: light_output_5 - pin: 16 +substitutions: + light_platform: esp8266_pwm + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 + pin5: GPIO16 -light: - - platform: rgbct - name: RGBCT Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - color_temperature: light_output_4 - white_brightness: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbct/test.rp2040-ard.yaml b/tests/components/rgbct/test.rp2040-ard.yaml index e7e959b2a4..36be8b7711 100644 --- a/tests/components/rgbct/test.rp2040-ard.yaml +++ b/tests/components/rgbct/test.rp2040-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 12 - - platform: rp2040_pwm - id: light_output_2 - pin: 13 - - platform: rp2040_pwm - id: light_output_3 - pin: 14 - - platform: rp2040_pwm - id: light_output_4 - pin: 15 - - platform: rp2040_pwm - id: light_output_5 - pin: 16 +substitutions: + light_platform: rp2040_pwm + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 + pin5: GPIO6 -light: - - platform: rgbct - name: RGBCT Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - color_temperature: light_output_4 - white_brightness: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbw/common.yaml b/tests/components/rgbw/common.yaml new file mode 100644 index 0000000000..b0f44869d3 --- /dev/null +++ b/tests/components/rgbw/common.yaml @@ -0,0 +1,22 @@ +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin2} + - platform: ${light_platform} + id: light_output_3 + pin: ${pin3} + - platform: ${light_platform} + id: light_output_4 + pin: ${pin4} + +light: + - platform: rgbw + name: RGBW Light + red: light_output_1 + green: light_output_2 + blue: light_output_3 + white: light_output_4 + color_interlock: true diff --git a/tests/components/rgbw/test.esp32-ard.yaml b/tests/components/rgbw/test.esp32-ard.yaml index 6e9e92a03c..ea8efd1a34 100644 --- a/tests/components/rgbw/test.esp32-ard.yaml +++ b/tests/components/rgbw/test.esp32-ard.yaml @@ -1,22 +1,8 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 - - platform: ledc - id: light_output_4 - pin: 15 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 -light: - - platform: rgbw - name: RGBW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - white: light_output_4 - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbw/test.esp32-c3-ard.yaml b/tests/components/rgbw/test.esp32-c3-ard.yaml index c5d4fceb9d..b44734344e 100644 --- a/tests/components/rgbw/test.esp32-c3-ard.yaml +++ b/tests/components/rgbw/test.esp32-c3-ard.yaml @@ -1,22 +1,8 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 - - platform: ledc - id: light_output_4 - pin: 4 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 -light: - - platform: rgbw - name: RGBW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - white: light_output_4 - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbw/test.esp32-c3-idf.yaml b/tests/components/rgbw/test.esp32-c3-idf.yaml index c5d4fceb9d..b44734344e 100644 --- a/tests/components/rgbw/test.esp32-c3-idf.yaml +++ b/tests/components/rgbw/test.esp32-c3-idf.yaml @@ -1,22 +1,8 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 - - platform: ledc - id: light_output_4 - pin: 4 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 -light: - - platform: rgbw - name: RGBW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - white: light_output_4 - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbw/test.esp32-idf.yaml b/tests/components/rgbw/test.esp32-idf.yaml index 6e9e92a03c..ea8efd1a34 100644 --- a/tests/components/rgbw/test.esp32-idf.yaml +++ b/tests/components/rgbw/test.esp32-idf.yaml @@ -1,22 +1,8 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 - - platform: ledc - id: light_output_4 - pin: 15 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 -light: - - platform: rgbw - name: RGBW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - white: light_output_4 - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbw/test.esp8266-ard.yaml b/tests/components/rgbw/test.esp8266-ard.yaml index 54098613e4..31d6805b4e 100644 --- a/tests/components/rgbw/test.esp8266-ard.yaml +++ b/tests/components/rgbw/test.esp8266-ard.yaml @@ -1,22 +1,8 @@ -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 12 - - platform: esp8266_pwm - id: light_output_2 - pin: 13 - - platform: esp8266_pwm - id: light_output_3 - pin: 14 - - platform: esp8266_pwm - id: light_output_4 - pin: 15 +substitutions: + light_platform: esp8266_pwm + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 -light: - - platform: rgbw - name: RGBW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - white: light_output_4 - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbw/test.rp2040-ard.yaml b/tests/components/rgbw/test.rp2040-ard.yaml index 6a4437b898..bd22aa46f3 100644 --- a/tests/components/rgbw/test.rp2040-ard.yaml +++ b/tests/components/rgbw/test.rp2040-ard.yaml @@ -1,22 +1,8 @@ -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 12 - - platform: rp2040_pwm - id: light_output_2 - pin: 13 - - platform: rp2040_pwm - id: light_output_3 - pin: 14 - - platform: rp2040_pwm - id: light_output_4 - pin: 15 +substitutions: + light_platform: rp2040_pwm + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 -light: - - platform: rgbw - name: RGBW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - white: light_output_4 - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbww/common.yaml b/tests/components/rgbww/common.yaml new file mode 100644 index 0000000000..0013960c10 --- /dev/null +++ b/tests/components/rgbww/common.yaml @@ -0,0 +1,28 @@ +output: + - platform: ${light_platform} + id: light_output_1 + pin: ${pin1} + - platform: ${light_platform} + id: light_output_2 + pin: ${pin2} + - platform: ${light_platform} + id: light_output_3 + pin: ${pin3} + - platform: ${light_platform} + id: light_output_4 + pin: ${pin4} + - platform: ${light_platform} + id: light_output_5 + pin: ${pin5} + +light: + - platform: rgbww + name: RGBWW Light + red: light_output_1 + green: light_output_2 + blue: light_output_3 + cold_white: light_output_4 + warm_white: light_output_5 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + color_interlock: true diff --git a/tests/components/rgbww/test.esp32-ard.yaml b/tests/components/rgbww/test.esp32-ard.yaml index c24b6b7746..1ecc626e9c 100644 --- a/tests/components/rgbww/test.esp32-ard.yaml +++ b/tests/components/rgbww/test.esp32-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 - - platform: ledc - id: light_output_4 - pin: 15 - - platform: ledc - id: light_output_5 - pin: 16 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 + pin5: GPIO16 -light: - - platform: rgbww - name: RGBWW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - cold_white: light_output_4 - warm_white: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbww/test.esp32-c3-ard.yaml b/tests/components/rgbww/test.esp32-c3-ard.yaml index 49e9c7f331..27a1fbca4d 100644 --- a/tests/components/rgbww/test.esp32-c3-ard.yaml +++ b/tests/components/rgbww/test.esp32-c3-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 - - platform: ledc - id: light_output_4 - pin: 4 - - platform: ledc - id: light_output_5 - pin: 5 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 + pin5: GPIO6 -light: - - platform: rgbww - name: RGBWW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - cold_white: light_output_4 - warm_white: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbww/test.esp32-c3-idf.yaml b/tests/components/rgbww/test.esp32-c3-idf.yaml index 49e9c7f331..27a1fbca4d 100644 --- a/tests/components/rgbww/test.esp32-c3-idf.yaml +++ b/tests/components/rgbww/test.esp32-c3-idf.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 1 - - platform: ledc - id: light_output_2 - pin: 2 - - platform: ledc - id: light_output_3 - pin: 3 - - platform: ledc - id: light_output_4 - pin: 4 - - platform: ledc - id: light_output_5 - pin: 5 +substitutions: + light_platform: ledc + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 + pin5: GPIO6 -light: - - platform: rgbww - name: RGBWW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - cold_white: light_output_4 - warm_white: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbww/test.esp32-idf.yaml b/tests/components/rgbww/test.esp32-idf.yaml index c24b6b7746..1ecc626e9c 100644 --- a/tests/components/rgbww/test.esp32-idf.yaml +++ b/tests/components/rgbww/test.esp32-idf.yaml @@ -1,28 +1,9 @@ -output: - - platform: ledc - id: light_output_1 - pin: 12 - - platform: ledc - id: light_output_2 - pin: 13 - - platform: ledc - id: light_output_3 - pin: 14 - - platform: ledc - id: light_output_4 - pin: 15 - - platform: ledc - id: light_output_5 - pin: 16 +substitutions: + light_platform: ledc + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 + pin5: GPIO16 -light: - - platform: rgbww - name: RGBWW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - cold_white: light_output_4 - warm_white: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbww/test.esp8266-ard.yaml b/tests/components/rgbww/test.esp8266-ard.yaml index 4ea26e6526..4b66a5927f 100644 --- a/tests/components/rgbww/test.esp8266-ard.yaml +++ b/tests/components/rgbww/test.esp8266-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: esp8266_pwm - id: light_output_1 - pin: 12 - - platform: esp8266_pwm - id: light_output_2 - pin: 13 - - platform: esp8266_pwm - id: light_output_3 - pin: 14 - - platform: esp8266_pwm - id: light_output_4 - pin: 15 - - platform: esp8266_pwm - id: light_output_5 - pin: 16 +substitutions: + light_platform: esp8266_pwm + pin1: GPIO12 + pin2: GPIO13 + pin3: GPIO14 + pin4: GPIO15 + pin5: GPIO16 -light: - - platform: rgbww - name: RGBWW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - cold_white: light_output_4 - warm_white: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rgbww/test.rp2040-ard.yaml b/tests/components/rgbww/test.rp2040-ard.yaml index 0986f06e78..36be8b7711 100644 --- a/tests/components/rgbww/test.rp2040-ard.yaml +++ b/tests/components/rgbww/test.rp2040-ard.yaml @@ -1,28 +1,9 @@ -output: - - platform: rp2040_pwm - id: light_output_1 - pin: 12 - - platform: rp2040_pwm - id: light_output_2 - pin: 13 - - platform: rp2040_pwm - id: light_output_3 - pin: 14 - - platform: rp2040_pwm - id: light_output_4 - pin: 15 - - platform: rp2040_pwm - id: light_output_5 - pin: 16 +substitutions: + light_platform: rp2040_pwm + pin1: GPIO2 + pin2: GPIO3 + pin3: GPIO4 + pin4: GPIO5 + pin5: GPIO6 -light: - - platform: rgbww - name: RGBWW Light - red: light_output_1 - green: light_output_2 - blue: light_output_3 - cold_white: light_output_4 - warm_white: light_output_5 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - color_interlock: true +<<: !include common.yaml diff --git a/tests/components/rotary_encoder/common.yaml b/tests/components/rotary_encoder/common.yaml new file mode 100644 index 0000000000..57ffdab817 --- /dev/null +++ b/tests/components/rotary_encoder/common.yaml @@ -0,0 +1,25 @@ +sensor: + - platform: rotary_encoder + name: Rotary Encoder + id: rotary_encoder1 + pin_a: ${pin_a} + pin_b: ${pin_b} + pin_reset: ${pin_reset} + filters: + - or: + - debounce: 0.1s + - delta: 10 + resolution: 4 + min_value: -10 + max_value: 30 + on_value: + - sensor.rotary_encoder.set_value: + id: rotary_encoder1 + value: 10 + - sensor.rotary_encoder.set_value: + id: rotary_encoder1 + value: !lambda "return -1;" + on_clockwise: + - logger.log: Clockwise + on_anticlockwise: + - logger.log: Anticlockwise diff --git a/tests/components/rotary_encoder/test.esp32-ard.yaml b/tests/components/rotary_encoder/test.esp32-ard.yaml index da3843f82d..48a624aa37 100644 --- a/tests/components/rotary_encoder/test.esp32-ard.yaml +++ b/tests/components/rotary_encoder/test.esp32-ard.yaml @@ -1,25 +1,6 @@ -sensor: - - platform: rotary_encoder - name: Rotary Encoder - id: rotary_encoder1 - pin_a: 13 - pin_b: 14 - pin_reset: 15 - filters: - - or: - - debounce: 0.1s - - delta: 10 - resolution: 4 - min_value: -10 - max_value: 30 - on_value: - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: 10 - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: !lambda "return -1;" - on_clockwise: - - logger.log: Clockwise - on_anticlockwise: - - logger.log: Anticlockwise +substitutions: + pin_a: GPIO12 + pin_b: GPIO13 + pin_reset: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.esp32-c3-ard.yaml b/tests/components/rotary_encoder/test.esp32-c3-ard.yaml index 59f8b56abf..b71a454bdd 100644 --- a/tests/components/rotary_encoder/test.esp32-c3-ard.yaml +++ b/tests/components/rotary_encoder/test.esp32-c3-ard.yaml @@ -1,25 +1,6 @@ -sensor: - - platform: rotary_encoder - name: Rotary Encoder - id: rotary_encoder1 - pin_a: 3 - pin_b: 4 - pin_reset: 5 - filters: - - or: - - debounce: 0.1s - - delta: 10 - resolution: 4 - min_value: -10 - max_value: 30 - on_value: - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: 10 - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: !lambda "return -1;" - on_clockwise: - - logger.log: Clockwise - on_anticlockwise: - - logger.log: Anticlockwise +substitutions: + pin_a: GPIO2 + pin_b: GPIO3 + pin_reset: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.esp32-c3-idf.yaml b/tests/components/rotary_encoder/test.esp32-c3-idf.yaml index 59f8b56abf..b71a454bdd 100644 --- a/tests/components/rotary_encoder/test.esp32-c3-idf.yaml +++ b/tests/components/rotary_encoder/test.esp32-c3-idf.yaml @@ -1,25 +1,6 @@ -sensor: - - platform: rotary_encoder - name: Rotary Encoder - id: rotary_encoder1 - pin_a: 3 - pin_b: 4 - pin_reset: 5 - filters: - - or: - - debounce: 0.1s - - delta: 10 - resolution: 4 - min_value: -10 - max_value: 30 - on_value: - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: 10 - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: !lambda "return -1;" - on_clockwise: - - logger.log: Clockwise - on_anticlockwise: - - logger.log: Anticlockwise +substitutions: + pin_a: GPIO2 + pin_b: GPIO3 + pin_reset: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.esp32-idf.yaml b/tests/components/rotary_encoder/test.esp32-idf.yaml index da3843f82d..48a624aa37 100644 --- a/tests/components/rotary_encoder/test.esp32-idf.yaml +++ b/tests/components/rotary_encoder/test.esp32-idf.yaml @@ -1,25 +1,6 @@ -sensor: - - platform: rotary_encoder - name: Rotary Encoder - id: rotary_encoder1 - pin_a: 13 - pin_b: 14 - pin_reset: 15 - filters: - - or: - - debounce: 0.1s - - delta: 10 - resolution: 4 - min_value: -10 - max_value: 30 - on_value: - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: 10 - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: !lambda "return -1;" - on_clockwise: - - logger.log: Clockwise - on_anticlockwise: - - logger.log: Anticlockwise +substitutions: + pin_a: GPIO12 + pin_b: GPIO13 + pin_reset: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.esp8266-ard.yaml b/tests/components/rotary_encoder/test.esp8266-ard.yaml index da3843f82d..48a624aa37 100644 --- a/tests/components/rotary_encoder/test.esp8266-ard.yaml +++ b/tests/components/rotary_encoder/test.esp8266-ard.yaml @@ -1,25 +1,6 @@ -sensor: - - platform: rotary_encoder - name: Rotary Encoder - id: rotary_encoder1 - pin_a: 13 - pin_b: 14 - pin_reset: 15 - filters: - - or: - - debounce: 0.1s - - delta: 10 - resolution: 4 - min_value: -10 - max_value: 30 - on_value: - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: 10 - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: !lambda "return -1;" - on_clockwise: - - logger.log: Clockwise - on_anticlockwise: - - logger.log: Anticlockwise +substitutions: + pin_a: GPIO12 + pin_b: GPIO13 + pin_reset: GPIO14 + +<<: !include common.yaml diff --git a/tests/components/rotary_encoder/test.rp2040-ard.yaml b/tests/components/rotary_encoder/test.rp2040-ard.yaml index da3843f82d..b71a454bdd 100644 --- a/tests/components/rotary_encoder/test.rp2040-ard.yaml +++ b/tests/components/rotary_encoder/test.rp2040-ard.yaml @@ -1,25 +1,6 @@ -sensor: - - platform: rotary_encoder - name: Rotary Encoder - id: rotary_encoder1 - pin_a: 13 - pin_b: 14 - pin_reset: 15 - filters: - - or: - - debounce: 0.1s - - delta: 10 - resolution: 4 - min_value: -10 - max_value: 30 - on_value: - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: 10 - - sensor.rotary_encoder.set_value: - id: rotary_encoder1 - value: !lambda "return -1;" - on_clockwise: - - logger.log: Clockwise - on_anticlockwise: - - logger.log: Anticlockwise +substitutions: + pin_a: GPIO2 + pin_b: GPIO3 + pin_reset: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/rtttl/common.yaml b/tests/components/rtttl/common.yaml new file mode 100644 index 0000000000..86b52ca3de --- /dev/null +++ b/tests/components/rtttl/common.yaml @@ -0,0 +1,15 @@ +esphome: + on_boot: + then: + - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' + - rtttl.stop + +output: + - platform: ${output_platform} + id: rtttl_output + pin: ${pin} + frequency: 1500Hz + max_power: 0.5 + +rtttl: + output: rtttl_output diff --git a/tests/components/rtttl/test.esp32-ard.yaml b/tests/components/rtttl/test.esp32-ard.yaml index 367a670741..26da1ce1d6 100644 --- a/tests/components/rtttl/test.esp32-ard.yaml +++ b/tests/components/rtttl/test.esp32-ard.yaml @@ -1,16 +1,5 @@ -esphome: - on_boot: - then: - - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' - - rtttl.stop +substitutions: + output_platform: ledc + pin: GPIO14 -output: - - platform: ledc - id: rtttl_output - pin: 13 - frequency: 1500Hz - channel: 14 - max_power: 0.5 - -rtttl: - output: rtttl_output +<<: !include common.yaml diff --git a/tests/components/rtttl/test.esp32-c3-ard.yaml b/tests/components/rtttl/test.esp32-c3-ard.yaml index c525f417de..7476963591 100644 --- a/tests/components/rtttl/test.esp32-c3-ard.yaml +++ b/tests/components/rtttl/test.esp32-c3-ard.yaml @@ -1,16 +1,5 @@ -esphome: - on_boot: - then: - - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' - - rtttl.stop +substitutions: + output_platform: ledc + pin: GPIO4 -output: - - platform: ledc - id: rtttl_output - pin: 1 - frequency: 1500Hz - channel: 14 - max_power: 0.5 - -rtttl: - output: rtttl_output +<<: !include common.yaml diff --git a/tests/components/rtttl/test.esp32-c3-idf.yaml b/tests/components/rtttl/test.esp32-c3-idf.yaml index c525f417de..7476963591 100644 --- a/tests/components/rtttl/test.esp32-c3-idf.yaml +++ b/tests/components/rtttl/test.esp32-c3-idf.yaml @@ -1,16 +1,5 @@ -esphome: - on_boot: - then: - - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' - - rtttl.stop +substitutions: + output_platform: ledc + pin: GPIO4 -output: - - platform: ledc - id: rtttl_output - pin: 1 - frequency: 1500Hz - channel: 14 - max_power: 0.5 - -rtttl: - output: rtttl_output +<<: !include common.yaml diff --git a/tests/components/rtttl/test.esp32-idf.yaml b/tests/components/rtttl/test.esp32-idf.yaml index 367a670741..26da1ce1d6 100644 --- a/tests/components/rtttl/test.esp32-idf.yaml +++ b/tests/components/rtttl/test.esp32-idf.yaml @@ -1,16 +1,5 @@ -esphome: - on_boot: - then: - - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' - - rtttl.stop +substitutions: + output_platform: ledc + pin: GPIO14 -output: - - platform: ledc - id: rtttl_output - pin: 13 - frequency: 1500Hz - channel: 14 - max_power: 0.5 - -rtttl: - output: rtttl_output +<<: !include common.yaml diff --git a/tests/components/rtttl/test.esp8266-ard.yaml b/tests/components/rtttl/test.esp8266-ard.yaml index c3b87c0f72..23a7f43b42 100644 --- a/tests/components/rtttl/test.esp8266-ard.yaml +++ b/tests/components/rtttl/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -esphome: - on_boot: - then: - - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' - - rtttl.stop +substitutions: + output_platform: esp8266_pwm + pin: GPIO14 -output: - - platform: esp8266_pwm - id: rtttl_output - pin: 13 - frequency: 1500Hz - max_power: 0.5 - -rtttl: - output: rtttl_output +<<: !include common.yaml diff --git a/tests/components/rtttl/test.rp2040-ard.yaml b/tests/components/rtttl/test.rp2040-ard.yaml index ea240aa34d..d70f7c74ec 100644 --- a/tests/components/rtttl/test.rp2040-ard.yaml +++ b/tests/components/rtttl/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -esphome: - on_boot: - then: - - rtttl.play: 'siren:d=8,o=5,b=100:d,e,d,e,d,e,d,e' - - rtttl.stop +substitutions: + output_platform: rp2040_pwm + pin: GPIO4 -output: - - platform: rp2040_pwm - id: rtttl_output - pin: 3 - frequency: 1500Hz - max_power: 0.5 - -rtttl: - output: rtttl_output +<<: !include common.yaml From 65ca000e6d72819b5bb48aa6fe53a0a210fa90ee Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Tue, 4 Feb 2025 18:43:44 -0800 Subject: [PATCH 0969/1052] [prometheus] Add update entity to prometheus metrics (#8173) --- .../prometheus/prometheus_handler.cpp | 91 +++++++++++++++++++ .../prometheus/prometheus_handler.h | 27 ++++-- tests/components/prometheus/common.yaml | 23 +++++ 3 files changed, 132 insertions(+), 9 deletions(-) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index 2d39d8ef3f..c31d34f000 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -77,6 +77,12 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) { this->media_player_row_(stream, obj, area, node, friendly_name); #endif +#ifdef USE_UPDATE + this->update_entity_type_(stream); + for (auto *obj : App.get_updates()) + this->update_entity_row_(stream, obj, area, node, friendly_name); +#endif + req->send(stream); } @@ -679,6 +685,91 @@ void PrometheusHandler::media_player_row_(AsyncResponseStream *stream, media_pla } #endif +#ifdef USE_UPDATE +void PrometheusHandler::update_entity_type_(AsyncResponseStream *stream) { + stream->print(F("#TYPE esphome_update_entity_state gauge\n")); + stream->print(F("#TYPE esphome_update_entity_info gauge\n")); + stream->print(F("#TYPE esphome_update_entity_failed gauge\n")); +} + +void PrometheusHandler::handle_update_state_(AsyncResponseStream *stream, update::UpdateState state) { + switch (state) { + case update::UpdateState::UPDATE_STATE_UNKNOWN: + stream->print("unknown"); + break; + case update::UpdateState::UPDATE_STATE_NO_UPDATE: + stream->print("none"); + break; + case update::UpdateState::UPDATE_STATE_AVAILABLE: + stream->print("available"); + break; + case update::UpdateState::UPDATE_STATE_INSTALLING: + stream->print("installing"); + break; + default: + stream->print("invalid"); + break; + } +} + +void PrometheusHandler::update_entity_row_(AsyncResponseStream *stream, update::UpdateEntity *obj, std::string &area, + std::string &node, std::string &friendly_name) { + if (obj->is_internal() && !this->include_internal_) + return; + if (obj->has_state()) { + // We have a valid value, output this value + stream->print(F("esphome_update_entity_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 0\n")); + // First update state + stream->print(F("esphome_update_entity_state{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\",value=\"")); + handle_update_state_(stream, obj->state); + stream->print(F("\"} ")); + stream->print(F("1.0")); + stream->print(F("\n")); + // Next update info + stream->print(F("esphome_update_entity_info{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\",current_version=\"")); + stream->print(obj->update_info.current_version.c_str()); + stream->print(F("\",latest_version=\"")); + stream->print(obj->update_info.latest_version.c_str()); + stream->print(F("\",title=\"")); + stream->print(obj->update_info.title.c_str()); + stream->print(F("\"} ")); + stream->print(F("1.0")); + stream->print(F("\n")); + } else { + // Invalid state + stream->print(F("esphome_update_entity_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 1\n")); + } +} +#endif + } // namespace prometheus } // namespace esphome #endif diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 41a06537ed..08a6e8dc8a 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -75,7 +75,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_BINARY_SENSOR /// Return the type for prometheus void binary_sensor_type_(AsyncResponseStream *stream); - /// Return the sensor state as prometheus data point + /// Return the binary sensor state as prometheus data point void binary_sensor_row_(AsyncResponseStream *stream, binary_sensor::BinarySensor *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -83,7 +83,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_FAN /// Return the type for prometheus void fan_type_(AsyncResponseStream *stream); - /// Return the sensor state as prometheus data point + /// Return the fan state as prometheus data point void fan_row_(AsyncResponseStream *stream, fan::Fan *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -91,7 +91,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_LIGHT /// Return the type for prometheus void light_type_(AsyncResponseStream *stream); - /// Return the Light Values state as prometheus data point + /// Return the light values state as prometheus data point void light_row_(AsyncResponseStream *stream, light::LightState *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -99,7 +99,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_COVER /// Return the type for prometheus void cover_type_(AsyncResponseStream *stream); - /// Return the switch Values state as prometheus data point + /// Return the cover values state as prometheus data point void cover_row_(AsyncResponseStream *stream, cover::Cover *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -107,7 +107,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_SWITCH /// Return the type for prometheus void switch_type_(AsyncResponseStream *stream); - /// Return the switch Values state as prometheus data point + /// Return the switch values state as prometheus data point void switch_row_(AsyncResponseStream *stream, switch_::Switch *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -115,7 +115,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_LOCK /// Return the type for prometheus void lock_type_(AsyncResponseStream *stream); - /// Return the lock Values state as prometheus data point + /// Return the lock values state as prometheus data point void lock_row_(AsyncResponseStream *stream, lock::Lock *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -123,7 +123,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_TEXT_SENSOR /// Return the type for prometheus void text_sensor_type_(AsyncResponseStream *stream); - /// Return the lock Values state as prometheus data point + /// Return the text sensor values state as prometheus data point void text_sensor_row_(AsyncResponseStream *stream, text_sensor::TextSensor *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -131,7 +131,7 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_NUMBER /// Return the type for prometheus void number_type_(AsyncResponseStream *stream); - /// Return the sensor state as prometheus data point + /// Return the number state as prometheus data point void number_row_(AsyncResponseStream *stream, number::Number *obj, std::string &area, std::string &node, std::string &friendly_name); #endif @@ -147,11 +147,20 @@ class PrometheusHandler : public AsyncWebHandler, public Component { #ifdef USE_MEDIA_PLAYER /// Return the type for prometheus void media_player_type_(AsyncResponseStream *stream); - /// Return the select state as prometheus data point + /// Return the media player state as prometheus data point void media_player_row_(AsyncResponseStream *stream, media_player::MediaPlayer *obj, std::string &area, std::string &node, std::string &friendly_name); #endif +#ifdef USE_UPDATE + /// Return the type for prometheus + void update_entity_type_(AsyncResponseStream *stream); + /// Return the update state and info as prometheus data point + void update_entity_row_(AsyncResponseStream *stream, update::UpdateEntity *obj, std::string &area, std::string &node, + std::string &friendly_name); + void handle_update_state_(AsyncResponseStream *stream, update::UpdateState state); +#endif + web_server_base::WebServerBase *base_; bool include_internal_{false}; std::map relabel_map_id_; diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index 1b87c1d6c1..9205c27f2a 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -1,12 +1,35 @@ +substitutions: + verify_ssl: "false" + esphome: name: livingroomdevice friendly_name: Living Room Device area: Living Room + on_boot: + then: + - if: + condition: + update.is_available: + then: + - logger.log: "Update available" + - update.perform: + force_update: true wifi: ssid: MySSID password: password1 +http_request: + verify_ssl: ${verify_ssl} + +ota: + - platform: http_request + +update: + - platform: http_request + name: Firmware Update + source: http://example.com/manifest.json + sensor: - platform: template id: template_sensor1 From 57739b8bb0979eea61822f4e3a6f50fc0837e434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Schr=C3=B6ter?= <5852313+janschroeter@users.noreply.github.com> Date: Wed, 5 Feb 2025 03:53:05 +0100 Subject: [PATCH 0970/1052] [uponor_smatrix] add target temperature as sensor (#7745) --- .../uponor_smatrix/sensor/__init__.py | 18 ++++++++++++++---- .../sensor/uponor_smatrix_sensor.cpp | 5 +++++ .../sensor/uponor_smatrix_sensor.h | 1 + tests/components/uponor_smatrix/common.yaml | 2 ++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/esphome/components/uponor_smatrix/sensor/__init__.py b/esphome/components/uponor_smatrix/sensor/__init__.py index 89097aef18..f2b34538ba 100644 --- a/esphome/components/uponor_smatrix/sensor/__init__.py +++ b/esphome/components/uponor_smatrix/sensor/__init__.py @@ -1,11 +1,12 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import sensor +import esphome.config_validation as cv from esphome.const import ( CONF_EXTERNAL_TEMPERATURE, CONF_HUMIDITY, - CONF_TEMPERATURE, CONF_ID, + CONF_TARGET_TEMPERATURE, + CONF_TEMPERATURE, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT, @@ -14,10 +15,10 @@ from esphome.const import ( ) from .. import ( - uponor_smatrix_ns, - UponorSmatrixDevice, UPONOR_SMATRIX_DEVICE_SCHEMA, + UponorSmatrixDevice, register_uponor_smatrix_device, + uponor_smatrix_ns, ) DEPENDENCIES = ["uponor_smatrix"] @@ -50,6 +51,12 @@ CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend( device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_TARGET_TEMPERATURE): sensor.sensor_schema( + unit_of_measurement=UNIT_CELSIUS, + accuracy_decimals=1, + device_class=DEVICE_CLASS_TEMPERATURE, + state_class=STATE_CLASS_MEASUREMENT, + ), } ).extend(UPONOR_SMATRIX_DEVICE_SCHEMA) @@ -68,3 +75,6 @@ async def to_code(config): if humidity_config := config.get(CONF_HUMIDITY): sens = await sensor.new_sensor(humidity_config) cg.add(var.set_humidity_sensor(sens)) + if target_temperature_config := config.get(CONF_TARGET_TEMPERATURE): + sens = await sensor.new_sensor(target_temperature_config) + cg.add(var.set_target_temperature_sensor(sens)) diff --git a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp index 2fd2a36efc..47ff3a17f5 100644 --- a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +++ b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp @@ -12,6 +12,7 @@ void UponorSmatrixSensor::dump_config() { LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "External Temperature", this->external_temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); + LOG_SENSOR(" ", "Target Temperature", this->target_temperature_sensor_); } void UponorSmatrixSensor::on_device_data(const UponorSmatrixData *data, size_t data_len) { @@ -29,6 +30,10 @@ void UponorSmatrixSensor::on_device_data(const UponorSmatrixData *data, size_t d if (this->humidity_sensor_ != nullptr) this->humidity_sensor_->publish_state(data[i].value & 0x00FF); break; + case UPONOR_ID_TARGET_TEMP: + if (this->target_temperature_sensor_ != nullptr) + this->target_temperature_sensor_->publish_state(raw_to_celsius(data[i].value)); + break; } } } diff --git a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h index 5e38117a21..97d0d21838 100644 --- a/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +++ b/esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h @@ -11,6 +11,7 @@ class UponorSmatrixSensor : public sensor::Sensor, public Component, public Upon SUB_SENSOR(temperature) SUB_SENSOR(external_temperature) SUB_SENSOR(humidity) + SUB_SENSOR(target_temperature) public: void dump_config() override; diff --git a/tests/components/uponor_smatrix/common.yaml b/tests/components/uponor_smatrix/common.yaml index cfdbacaa4c..8ee92bdfc5 100644 --- a/tests/components/uponor_smatrix/common.yaml +++ b/tests/components/uponor_smatrix/common.yaml @@ -36,3 +36,5 @@ sensor: name: Thermostat Temperature Living Room external_temperature: name: Thermostat Floor Temperature Living Room + target_temperature: + name: Thermostat Target Temperature Living Room From e337bd7beb41d28c8d06026cb18f37c725e684dd Mon Sep 17 00:00:00 2001 From: bdm310 Date: Wed, 5 Feb 2025 02:53:23 -0800 Subject: [PATCH 0971/1052] [sdl] Implement binary sensors from keystrokes (#8207) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- CODEOWNERS | 2 +- esphome/components/sdl/binary_sensor.py | 270 ++++++++++++++++++++++++ esphome/components/sdl/sdl_esphome.cpp | 16 ++ esphome/components/sdl/sdl_esphome.h | 9 + tests/components/sdl/common.yaml | 11 + 5 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 esphome/components/sdl/binary_sensor.py diff --git a/CODEOWNERS b/CODEOWNERS index 03e26bcb84..eab02efffb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -357,7 +357,7 @@ esphome/components/rtttl/* @glmnet esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti esphome/components/scd4x/* @martgras @sjtrny esphome/components/script/* @esphome/core -esphome/components/sdl/* @clydebarrow +esphome/components/sdl/* @bdm310 @clydebarrow esphome/components/sdm_meter/* @jesserockz @polyfaces esphome/components/sdp3x/* @Azimath esphome/components/seeed_mr24hpc1/* @limengdu diff --git a/esphome/components/sdl/binary_sensor.py b/esphome/components/sdl/binary_sensor.py new file mode 100644 index 0000000000..3ea6c2d218 --- /dev/null +++ b/esphome/components/sdl/binary_sensor.py @@ -0,0 +1,270 @@ +import esphome.codegen as cg +from esphome.components import binary_sensor +from esphome.components.binary_sensor import BinarySensor +import esphome.config_validation as cv +from esphome.const import CONF_KEY +from esphome.core import Lambda +from esphome.cpp_generator import ExpressionStatement, RawExpression + +from .display import CONF_SDL_ID, Sdl + +CODEOWNERS = ["@bdm310"] + +STATE_ARG = "state" + +SDL_KEYMAP = { + "SDLK_UNKNOWN": 0, + "SDLK_FIRST": 0, + "SDLK_BACKSPACE": 8, + "SDLK_TAB": 9, + "SDLK_CLEAR": 12, + "SDLK_RETURN": 13, + "SDLK_PAUSE": 19, + "SDLK_ESCAPE": 27, + "SDLK_SPACE": 32, + "SDLK_EXCLAIM": 33, + "SDLK_QUOTEDBL": 34, + "SDLK_HASH": 35, + "SDLK_DOLLAR": 36, + "SDLK_AMPERSAND": 38, + "SDLK_QUOTE": 39, + "SDLK_LEFTPAREN": 40, + "SDLK_RIGHTPAREN": 41, + "SDLK_ASTERISK": 42, + "SDLK_PLUS": 43, + "SDLK_COMMA": 44, + "SDLK_MINUS": 45, + "SDLK_PERIOD": 46, + "SDLK_SLASH": 47, + "SDLK_0": 48, + "SDLK_1": 49, + "SDLK_2": 50, + "SDLK_3": 51, + "SDLK_4": 52, + "SDLK_5": 53, + "SDLK_6": 54, + "SDLK_7": 55, + "SDLK_8": 56, + "SDLK_9": 57, + "SDLK_COLON": 58, + "SDLK_SEMICOLON": 59, + "SDLK_LESS": 60, + "SDLK_EQUALS": 61, + "SDLK_GREATER": 62, + "SDLK_QUESTION": 63, + "SDLK_AT": 64, + "SDLK_LEFTBRACKET": 91, + "SDLK_BACKSLASH": 92, + "SDLK_RIGHTBRACKET": 93, + "SDLK_CARET": 94, + "SDLK_UNDERSCORE": 95, + "SDLK_BACKQUOTE": 96, + "SDLK_a": 97, + "SDLK_b": 98, + "SDLK_c": 99, + "SDLK_d": 100, + "SDLK_e": 101, + "SDLK_f": 102, + "SDLK_g": 103, + "SDLK_h": 104, + "SDLK_i": 105, + "SDLK_j": 106, + "SDLK_k": 107, + "SDLK_l": 108, + "SDLK_m": 109, + "SDLK_n": 110, + "SDLK_o": 111, + "SDLK_p": 112, + "SDLK_q": 113, + "SDLK_r": 114, + "SDLK_s": 115, + "SDLK_t": 116, + "SDLK_u": 117, + "SDLK_v": 118, + "SDLK_w": 119, + "SDLK_x": 120, + "SDLK_y": 121, + "SDLK_z": 122, + "SDLK_DELETE": 127, + "SDLK_WORLD_0": 160, + "SDLK_WORLD_1": 161, + "SDLK_WORLD_2": 162, + "SDLK_WORLD_3": 163, + "SDLK_WORLD_4": 164, + "SDLK_WORLD_5": 165, + "SDLK_WORLD_6": 166, + "SDLK_WORLD_7": 167, + "SDLK_WORLD_8": 168, + "SDLK_WORLD_9": 169, + "SDLK_WORLD_10": 170, + "SDLK_WORLD_11": 171, + "SDLK_WORLD_12": 172, + "SDLK_WORLD_13": 173, + "SDLK_WORLD_14": 174, + "SDLK_WORLD_15": 175, + "SDLK_WORLD_16": 176, + "SDLK_WORLD_17": 177, + "SDLK_WORLD_18": 178, + "SDLK_WORLD_19": 179, + "SDLK_WORLD_20": 180, + "SDLK_WORLD_21": 181, + "SDLK_WORLD_22": 182, + "SDLK_WORLD_23": 183, + "SDLK_WORLD_24": 184, + "SDLK_WORLD_25": 185, + "SDLK_WORLD_26": 186, + "SDLK_WORLD_27": 187, + "SDLK_WORLD_28": 188, + "SDLK_WORLD_29": 189, + "SDLK_WORLD_30": 190, + "SDLK_WORLD_31": 191, + "SDLK_WORLD_32": 192, + "SDLK_WORLD_33": 193, + "SDLK_WORLD_34": 194, + "SDLK_WORLD_35": 195, + "SDLK_WORLD_36": 196, + "SDLK_WORLD_37": 197, + "SDLK_WORLD_38": 198, + "SDLK_WORLD_39": 199, + "SDLK_WORLD_40": 200, + "SDLK_WORLD_41": 201, + "SDLK_WORLD_42": 202, + "SDLK_WORLD_43": 203, + "SDLK_WORLD_44": 204, + "SDLK_WORLD_45": 205, + "SDLK_WORLD_46": 206, + "SDLK_WORLD_47": 207, + "SDLK_WORLD_48": 208, + "SDLK_WORLD_49": 209, + "SDLK_WORLD_50": 210, + "SDLK_WORLD_51": 211, + "SDLK_WORLD_52": 212, + "SDLK_WORLD_53": 213, + "SDLK_WORLD_54": 214, + "SDLK_WORLD_55": 215, + "SDLK_WORLD_56": 216, + "SDLK_WORLD_57": 217, + "SDLK_WORLD_58": 218, + "SDLK_WORLD_59": 219, + "SDLK_WORLD_60": 220, + "SDLK_WORLD_61": 221, + "SDLK_WORLD_62": 222, + "SDLK_WORLD_63": 223, + "SDLK_WORLD_64": 224, + "SDLK_WORLD_65": 225, + "SDLK_WORLD_66": 226, + "SDLK_WORLD_67": 227, + "SDLK_WORLD_68": 228, + "SDLK_WORLD_69": 229, + "SDLK_WORLD_70": 230, + "SDLK_WORLD_71": 231, + "SDLK_WORLD_72": 232, + "SDLK_WORLD_73": 233, + "SDLK_WORLD_74": 234, + "SDLK_WORLD_75": 235, + "SDLK_WORLD_76": 236, + "SDLK_WORLD_77": 237, + "SDLK_WORLD_78": 238, + "SDLK_WORLD_79": 239, + "SDLK_WORLD_80": 240, + "SDLK_WORLD_81": 241, + "SDLK_WORLD_82": 242, + "SDLK_WORLD_83": 243, + "SDLK_WORLD_84": 244, + "SDLK_WORLD_85": 245, + "SDLK_WORLD_86": 246, + "SDLK_WORLD_87": 247, + "SDLK_WORLD_88": 248, + "SDLK_WORLD_89": 249, + "SDLK_WORLD_90": 250, + "SDLK_WORLD_91": 251, + "SDLK_WORLD_92": 252, + "SDLK_WORLD_93": 253, + "SDLK_WORLD_94": 254, + "SDLK_WORLD_95": 255, + "SDLK_KP0": 256, + "SDLK_KP1": 257, + "SDLK_KP2": 258, + "SDLK_KP3": 259, + "SDLK_KP4": 260, + "SDLK_KP5": 261, + "SDLK_KP6": 262, + "SDLK_KP7": 263, + "SDLK_KP8": 264, + "SDLK_KP9": 265, + "SDLK_KP_PERIOD": 266, + "SDLK_KP_DIVIDE": 267, + "SDLK_KP_MULTIPLY": 268, + "SDLK_KP_MINUS": 269, + "SDLK_KP_PLUS": 270, + "SDLK_KP_ENTER": 271, + "SDLK_KP_EQUALS": 272, + "SDLK_UP": 273, + "SDLK_DOWN": 274, + "SDLK_RIGHT": 275, + "SDLK_LEFT": 276, + "SDLK_INSERT": 277, + "SDLK_HOME": 278, + "SDLK_END": 279, + "SDLK_PAGEUP": 280, + "SDLK_PAGEDOWN": 281, + "SDLK_F1": 282, + "SDLK_F2": 283, + "SDLK_F3": 284, + "SDLK_F4": 285, + "SDLK_F5": 286, + "SDLK_F6": 287, + "SDLK_F7": 288, + "SDLK_F8": 289, + "SDLK_F9": 290, + "SDLK_F10": 291, + "SDLK_F11": 292, + "SDLK_F12": 293, + "SDLK_F13": 294, + "SDLK_F14": 295, + "SDLK_F15": 296, + "SDLK_NUMLOCK": 300, + "SDLK_CAPSLOCK": 301, + "SDLK_SCROLLOCK": 302, + "SDLK_RSHIFT": 303, + "SDLK_LSHIFT": 304, + "SDLK_RCTRL": 305, + "SDLK_LCTRL": 306, + "SDLK_RALT": 307, + "SDLK_LALT": 308, + "SDLK_RMETA": 309, + "SDLK_LMETA": 310, + "SDLK_LSUPER": 311, + "SDLK_RSUPER": 312, + "SDLK_MODE": 313, + "SDLK_COMPOSE": 314, + "SDLK_HELP": 315, + "SDLK_PRINT": 316, + "SDLK_SYSREQ": 317, + "SDLK_BREAK": 318, + "SDLK_MENU": 319, + "SDLK_POWER": 320, + "SDLK_EURO": 321, + "SDLK_UNDO": 322, +} + +CONFIG_SCHEMA = ( + binary_sensor.binary_sensor_schema(BinarySensor) + .extend( + { + cv.Required(CONF_KEY): cv.enum(SDL_KEYMAP), + cv.GenerateID(CONF_SDL_ID): cv.use_id(Sdl), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) + + +async def to_code(config): + var = await binary_sensor.new_binary_sensor(config) + parent = await cg.get_variable(config[CONF_SDL_ID]) + listener = Lambda( + str(ExpressionStatement(var.publish_state(RawExpression(STATE_ARG)))) + ) + listener = await cg.process_lambda(listener, [(cg.bool_, STATE_ARG)]) + cg.add(parent.add_key_listener(config[CONF_KEY], listener)) diff --git a/esphome/components/sdl/sdl_esphome.cpp b/esphome/components/sdl/sdl_esphome.cpp index 8f0821a2fa..42dfe687e9 100644 --- a/esphome/components/sdl/sdl_esphome.cpp +++ b/esphome/components/sdl/sdl_esphome.cpp @@ -61,6 +61,12 @@ void Sdl::draw_pixel_at(int x, int y, Color color) { this->y_high_ = y; } +void Sdl::process_key(uint32_t keycode, bool down) { + auto callback = this->key_callbacks_.find(keycode); + if (callback != this->key_callbacks_.end()) + callback->second(down); +} + void Sdl::loop() { SDL_Event e; if (SDL_PollEvent(&e)) { @@ -87,6 +93,16 @@ void Sdl::loop() { } break; + case SDL_KEYDOWN: + ESP_LOGD(TAG, "keydown %d", e.key.keysym.sym); + this->process_key(e.key.keysym.sym, true); + break; + + case SDL_KEYUP: + ESP_LOGD(TAG, "keyup %d", e.key.keysym.sym); + this->process_key(e.key.keysym.sym, false); + break; + case SDL_WINDOWEVENT: switch (e.window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: diff --git a/esphome/components/sdl/sdl_esphome.h b/esphome/components/sdl/sdl_esphome.h index 4b0e59c9fe..39ea3ed417 100644 --- a/esphome/components/sdl/sdl_esphome.h +++ b/esphome/components/sdl/sdl_esphome.h @@ -7,6 +7,7 @@ #include "esphome/components/display/display.h" #define SDL_MAIN_HANDLED #include "SDL.h" +#include namespace esphome { namespace sdl { @@ -22,6 +23,7 @@ class Sdl : public display::Display { void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; void draw_pixel_at(int x, int y, Color color) override; + void process_key(uint32_t keycode, bool down); void set_dimensions(uint16_t width, uint16_t height) { this->width_ = width; this->height_ = height; @@ -30,6 +32,12 @@ class Sdl : public display::Display { int get_height() override { return this->height_; } float get_setup_priority() const override { return setup_priority::HARDWARE; } void dump_config() override { LOG_DISPLAY("", "SDL", this); } + void add_key_listener(int32_t keycode, std::function &&callback) { + if (!this->key_callbacks_.count(keycode)) { + this->key_callbacks_[keycode] = CallbackManager(); + } + this->key_callbacks_[keycode].add(std::move(callback)); + } int mouse_x{}; int mouse_y{}; @@ -48,6 +56,7 @@ class Sdl : public display::Display { uint16_t y_low_{0}; uint16_t x_high_{0}; uint16_t y_high_{0}; + std::map> key_callbacks_{}; }; } // namespace sdl } // namespace esphome diff --git a/tests/components/sdl/common.yaml b/tests/components/sdl/common.yaml index 0192f054b5..50fa4a5990 100644 --- a/tests/components/sdl/common.yaml +++ b/tests/components/sdl/common.yaml @@ -10,3 +10,14 @@ display: dimensions: width: 450 height: 600 + +binary_sensor: + - platform: sdl + id: key_up + key: SDLK_a + - platform: sdl + id: key_down + key: SDLK_d + - platform: sdl + id: key_enter + key: SDLK_s From 4eb551864df65c95a25f6169b26d18eae975c4a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 20:33:57 +0100 Subject: [PATCH 0972/1052] Bump the docker-actions group with 2 updates (#8215) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci-docker.yml | 4 ++-- .github/workflows/release.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index e156dbf1e2..8de6191205 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -46,9 +46,9 @@ jobs: with: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.8.0 + uses: docker/setup-buildx-action@v3.9.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v3.3.0 + uses: docker/setup-qemu-action@v3.4.0 - name: Set TAG run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d406ee0069..aa41cf2790 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,10 +89,10 @@ jobs: python-version: "3.9" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.8.0 + uses: docker/setup-buildx-action@v3.9.0 - name: Set up QEMU if: matrix.platform != 'linux/amd64' - uses: docker/setup-qemu-action@v3.3.0 + uses: docker/setup-qemu-action@v3.4.0 - name: Log in to docker hub uses: docker/login-action@v3.3.0 @@ -183,7 +183,7 @@ jobs: merge-multiple: true - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3.8.0 + uses: docker/setup-buildx-action@v3.9.0 - name: Log in to docker hub if: matrix.registry == 'dockerhub' From 7e626b04f2465bc7dd074dd029c2b31ef1694287 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 6 Feb 2025 23:09:24 -0500 Subject: [PATCH 0973/1052] [esp32_rmt] Set pull-up and open-drain modes based on pin schema (#8178) Co-authored-by: Keith Burzinski --- .../remote_receiver/remote_receiver_esp32.cpp | 6 ++++++ .../components/remote_transmitter/__init__.py | 17 +++++++++-------- .../remote_transmitter/remote_transmitter.h | 2 -- .../remote_transmitter_esp32.cpp | 14 ++++++++++---- .../remote_transmitter/esp32-common-idf.yaml | 1 - .../remote_transmitter/test.esp32-c3-idf.yaml | 1 - .../remote_transmitter/test.esp32-idf.yaml | 1 - .../remote_transmitter/test.esp32-s3-idf.yaml | 1 - 8 files changed, 25 insertions(+), 18 deletions(-) diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index 8a36971e36..2b6032cdf2 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -2,6 +2,7 @@ #include "esphome/core/log.h" #ifdef USE_ESP32 +#include namespace esphome { namespace remote_receiver { @@ -62,6 +63,11 @@ void RemoteReceiverComponent::setup() { this->mark_failed(); return; } + if (this->pin_->get_flags() & gpio::FLAG_PULLUP) { + gpio_pullup_en(gpio_num_t(this->pin_->get_pin())); + } else { + gpio_pullup_dis(gpio_num_t(this->pin_->get_pin())); + } error = rmt_enable(this->channel_); if (error != ESP_OK) { this->error_code_ = error; diff --git a/esphome/components/remote_transmitter/__init__.py b/esphome/components/remote_transmitter/__init__.py index e3462fb246..e7a94c175e 100644 --- a/esphome/components/remote_transmitter/__init__.py +++ b/esphome/components/remote_transmitter/__init__.py @@ -8,6 +8,8 @@ from esphome.const import ( CONF_CLOCK_RESOLUTION, CONF_ID, CONF_INVERTED, + CONF_MODE, + CONF_OPEN_DRAIN, CONF_PIN, CONF_RMT_CHANNEL, CONF_RMT_SYMBOLS, @@ -20,7 +22,6 @@ AUTO_LOAD = ["remote_base"] CONF_EOT_LEVEL = "eot_level" CONF_ON_TRANSMIT = "on_transmit" CONF_ON_COMPLETE = "on_complete" -CONF_ONE_WIRE = "one_wire" remote_transmitter_ns = cg.esphome_ns.namespace("remote_transmitter") RemoteTransmitterComponent = remote_transmitter_ns.class_( @@ -44,7 +45,6 @@ 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_ONE_WIRE): cv.All(cv.only_with_esp_idf, cv.boolean), cv.Optional(CONF_USE_DMA): cv.All(cv.only_with_esp_idf, cv.boolean), cv.SplitDefault( CONF_RMT_SYMBOLS, @@ -74,14 +74,15 @@ async def to_code(config): cg.add(var.set_clock_resolution(config[CONF_CLOCK_RESOLUTION])) if CONF_USE_DMA in config: cg.add(var.set_with_dma(config[CONF_USE_DMA])) - if CONF_ONE_WIRE in config: - cg.add(var.set_one_wire(config[CONF_ONE_WIRE])) if CONF_EOT_LEVEL in config: cg.add(var.set_eot_level(config[CONF_EOT_LEVEL])) - elif CONF_ONE_WIRE in config and config[CONF_ONE_WIRE]: - cg.add(var.set_eot_level(True)) - elif CONF_INVERTED in config[CONF_PIN] and config[CONF_PIN][CONF_INVERTED]: - cg.add(var.set_eot_level(True)) + else: + cg.add( + var.set_eot_level( + config[CONF_PIN][CONF_MODE][CONF_OPEN_DRAIN] + or config[CONF_PIN][CONF_INVERTED] + ) + ) else: if (rmt_channel := config.get(CONF_RMT_CHANNEL, None)) is not None: var = cg.new_Pvariable(config[CONF_ID], pin, rmt_channel) diff --git a/esphome/components/remote_transmitter/remote_transmitter.h b/esphome/components/remote_transmitter/remote_transmitter.h index fd1d182063..0a8f354c72 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.h +++ b/esphome/components/remote_transmitter/remote_transmitter.h @@ -40,7 +40,6 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #if defined(USE_ESP32) && ESP_IDF_VERSION_MAJOR >= 5 void set_with_dma(bool with_dma) { this->with_dma_ = with_dma; } - void set_one_wire(bool one_wire) { this->one_wire_ = one_wire; } void set_eot_level(bool eot_level) { this->eot_level_ = eot_level; } void digital_write(bool value); #endif @@ -69,7 +68,6 @@ class RemoteTransmitterComponent : public remote_base::RemoteTransmitterBase, #if ESP_IDF_VERSION_MAJOR >= 5 std::vector rmt_temp_; bool with_dma_{false}; - bool one_wire_{false}; bool eot_level_{false}; rmt_channel_handle_t channel_{NULL}; rmt_encoder_handle_t encoder_{NULL}; diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index cd7f366373..01a3980673 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -3,6 +3,7 @@ #include "esphome/core/application.h" #ifdef USE_ESP32 +#include namespace esphome { namespace remote_transmitter { @@ -18,7 +19,6 @@ void RemoteTransmitterComponent::setup() { void RemoteTransmitterComponent::dump_config() { ESP_LOGCONFIG(TAG, "Remote Transmitter:"); #if ESP_IDF_VERSION_MAJOR >= 5 - ESP_LOGCONFIG(TAG, " One wire: %s", this->one_wire_ ? "true" : "false"); ESP_LOGCONFIG(TAG, " Clock resolution: %" PRIu32 " hz", this->clock_resolution_); ESP_LOGCONFIG(TAG, " RMT symbols: %" PRIu32, this->rmt_symbols_); #else @@ -68,6 +68,7 @@ void RemoteTransmitterComponent::configure_rmt_() { esp_err_t error; if (!this->initialized_) { + bool open_drain = (this->pin_->get_flags() & gpio::FLAG_OPEN_DRAIN) != 0; rmt_tx_channel_config_t channel; memset(&channel, 0, sizeof(channel)); channel.clk_src = RMT_CLK_SRC_DEFAULT; @@ -75,8 +76,8 @@ void RemoteTransmitterComponent::configure_rmt_() { channel.gpio_num = gpio_num_t(this->pin_->get_pin()); channel.mem_block_symbols = this->rmt_symbols_; channel.trans_queue_depth = 1; - channel.flags.io_loop_back = this->one_wire_; - channel.flags.io_od_mode = this->one_wire_; + channel.flags.io_loop_back = open_drain; + channel.flags.io_od_mode = open_drain; channel.flags.invert_out = 0; channel.flags.with_dma = this->with_dma_; channel.intr_priority = 0; @@ -91,6 +92,11 @@ void RemoteTransmitterComponent::configure_rmt_() { this->mark_failed(); return; } + if (this->pin_->get_flags() & gpio::FLAG_PULLUP) { + gpio_pullup_en(gpio_num_t(this->pin_->get_pin())); + } else { + gpio_pullup_dis(gpio_num_t(this->pin_->get_pin())); + } rmt_copy_encoder_config_t encoder; memset(&encoder, 0, sizeof(encoder)); @@ -109,7 +115,7 @@ void RemoteTransmitterComponent::configure_rmt_() { this->mark_failed(); return; } - this->digital_write(this->one_wire_ || this->inverted_); + this->digital_write(open_drain || this->inverted_); this->initialized_ = true; } diff --git a/tests/components/remote_transmitter/esp32-common-idf.yaml b/tests/components/remote_transmitter/esp32-common-idf.yaml index 3b8b5e2aef..c5d4ab7b96 100644 --- a/tests/components/remote_transmitter/esp32-common-idf.yaml +++ b/tests/components/remote_transmitter/esp32-common-idf.yaml @@ -3,7 +3,6 @@ remote_transmitter: pin: ${pin} carrier_duty_percent: 50% clock_resolution: ${clock_resolution} - one_wire: ${one_wire} rmt_symbols: ${rmt_symbols} use_dma: ${use_dma} diff --git a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml index 1a27f29dac..be526014bd 100644 --- a/tests/components/remote_transmitter/test.esp32-c3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-c3-idf.yaml @@ -1,7 +1,6 @@ substitutions: pin: GPIO2 clock_resolution: "2000000" - one_wire: "true" rmt_symbols: "64" use_dma: "true" diff --git a/tests/components/remote_transmitter/test.esp32-idf.yaml b/tests/components/remote_transmitter/test.esp32-idf.yaml index 1a27f29dac..be526014bd 100644 --- a/tests/components/remote_transmitter/test.esp32-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-idf.yaml @@ -1,7 +1,6 @@ substitutions: pin: GPIO2 clock_resolution: "2000000" - one_wire: "true" rmt_symbols: "64" use_dma: "true" diff --git a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml index 25bdbd4772..cb86020064 100644 --- a/tests/components/remote_transmitter/test.esp32-s3-idf.yaml +++ b/tests/components/remote_transmitter/test.esp32-s3-idf.yaml @@ -1,7 +1,6 @@ substitutions: pin: GPIO38 clock_resolution: "2000000" - one_wire: "true" rmt_symbols: "64" use_dma: "true" From 9e3359cdf23685b6b6b9c4df4c824c0037847ed7 Mon Sep 17 00:00:00 2001 From: G-Two <7310260+G-Two@users.noreply.github.com> Date: Fri, 7 Feb 2025 00:08:06 -0500 Subject: [PATCH 0974/1052] Add Toto protocol to remote receiver and transmitter (#8177) --- esphome/components/remote_base/__init__.py | 108 +++++++++++++----- .../components/remote_base/toto_protocol.cpp | 100 ++++++++++++++++ .../components/remote_base/toto_protocol.h | 45 ++++++++ .../remote_receiver/common-actions.yaml | 5 + .../remote_transmitter/common-buttons.yaml | 7 ++ 5 files changed, 237 insertions(+), 28 deletions(-) create mode 100644 esphome/components/remote_base/toto_protocol.cpp create mode 100644 esphome/components/remote_base/toto_protocol.h diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 3d1c10a092..daea4e5c11 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -1,41 +1,41 @@ -import esphome.codegen as cg -import esphome.config_validation as cv from esphome import automation +import esphome.codegen as cg from esphome.components import binary_sensor +import esphome.config_validation as cv from esphome.const import ( + CONF_ADDRESS, + CONF_BUTTON, + CONF_CARRIER_FREQUENCY, + CONF_CHANNEL, + CONF_CHECK, + CONF_CODE, + CONF_COMMAND, CONF_COMMAND_REPEATS, CONF_DATA, - CONF_TRIGGER_ID, - CONF_NBITS, - CONF_ADDRESS, - CONF_COMMAND, - CONF_CODE, - CONF_PULSE_LENGTH, - CONF_SYNC, - CONF_ZERO, - CONF_ONE, - CONF_INVERTED, - CONF_PROTOCOL, - CONF_GROUP, + CONF_DELTA, CONF_DEVICE, - CONF_SECOND, - CONF_STATE, - CONF_CHANNEL, CONF_FAMILY, - CONF_REPEAT, - CONF_WAIT_TIME, - CONF_TIMES, - CONF_TYPE_ID, - CONF_CARRIER_FREQUENCY, + CONF_GROUP, + CONF_ID, + CONF_INVERTED, + CONF_LEVEL, + CONF_MAGNITUDE, + CONF_NBITS, + CONF_ONE, + CONF_PROTOCOL, + CONF_PULSE_LENGTH, CONF_RC_CODE_1, CONF_RC_CODE_2, - CONF_MAGNITUDE, + CONF_REPEAT, + CONF_SECOND, + CONF_STATE, + CONF_SYNC, + CONF_TIMES, + CONF_TRIGGER_ID, + CONF_TYPE_ID, + CONF_WAIT_TIME, CONF_WAND_ID, - CONF_LEVEL, - CONF_DELTA, - CONF_ID, - CONF_BUTTON, - CONF_CHECK, + CONF_ZERO, ) from esphome.core import coroutine from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor @@ -1963,3 +1963,55 @@ async def mirage_action(var, config, args): vec_ = cg.std_vector.template(cg.uint8) template_ = await cg.templatable(config[CONF_CODE], args, vec_, vec_) cg.add(var.set_code(template_)) + + +# Toto +( + TotoData, + TotoBinarySensor, + TotoTrigger, + TotoAction, + TotoDumper, +) = declare_protocol("Toto") + +TOTO_SCHEMA = cv.Schema( + { + cv.Optional(CONF_RC_CODE_1, default=0): cv.hex_int_range(0, 0xF), + cv.Optional(CONF_RC_CODE_2, default=0): cv.hex_int_range(0, 0xF), + cv.Required(CONF_COMMAND): cv.hex_uint8_t, + } +) + + +@register_binary_sensor("toto", TotoBinarySensor, TOTO_SCHEMA) +def toto_binary_sensor(var, config): + cg.add( + var.set_data( + cg.StructInitializer( + TotoData, + ("rc_code_1", config[CONF_RC_CODE_1]), + ("rc_code_2", config[CONF_RC_CODE_2]), + ("command", config[CONF_COMMAND]), + ) + ) + ) + + +@register_trigger("toto", TotoTrigger, TotoData) +def toto_trigger(var, config): + pass + + +@register_dumper("toto", TotoDumper) +def toto_dumper(var, config): + pass + + +@register_action("toto", TotoAction, TOTO_SCHEMA) +async def Toto_action(var, config, args): + template_ = await cg.templatable(config[CONF_RC_CODE_1], args, cg.uint8) + cg.add(var.set_rc_code_1(template_)) + template_ = await cg.templatable(config[CONF_RC_CODE_2], args, cg.uint8) + cg.add(var.set_rc_code_2(template_)) + template_ = await cg.templatable(config[CONF_COMMAND], args, cg.uint8) + cg.add(var.set_command(template_)) diff --git a/esphome/components/remote_base/toto_protocol.cpp b/esphome/components/remote_base/toto_protocol.cpp new file mode 100644 index 0000000000..ba21263bc8 --- /dev/null +++ b/esphome/components/remote_base/toto_protocol.cpp @@ -0,0 +1,100 @@ +#include "toto_protocol.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace remote_base { + +static const char *const TAG = "remote.toto"; + +static const uint32_t PREAMBLE_HIGH_US = 6200; +static const uint32_t PREAMBLE_LOW_US = 2800; +static const uint32_t BIT_HIGH_US = 550; +static const uint32_t BIT_ONE_LOW_US = 1700; +static const uint32_t BIT_ZERO_LOW_US = 550; +static const uint32_t TOTO_HEADER = 0x2008; + +void TotoProtocol::encode(RemoteTransmitData *dst, const TotoData &data) { + uint32_t payload = 0; + + payload = data.rc_code_1 << 20; + payload |= data.rc_code_2 << 16; + payload |= data.command << 8; + payload |= ((payload & 0xFF0000) >> 16) ^ ((payload & 0x00FF00) >> 8); + + dst->reserve(80); + dst->set_carrier_frequency(38000); + dst->item(PREAMBLE_HIGH_US, PREAMBLE_LOW_US); + + for (uint32_t mask = 1UL << 14; mask; mask >>= 1) { + if (TOTO_HEADER & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + for (uint32_t mask = 1UL << 23; mask; mask >>= 1) { + if (payload & mask) { + dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); + } else { + dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); + } + } + + dst->mark(BIT_HIGH_US); +} +optional TotoProtocol::decode(RemoteReceiveData src) { + uint16_t header = 0; + uint32_t payload = 0; + + TotoData data{ + .rc_code_1 = 0, + .rc_code_2 = 0, + .command = 0, + }; + + if (!src.expect_item(PREAMBLE_HIGH_US, PREAMBLE_LOW_US)) { + return {}; + } + + for (uint32_t mask = 1UL << 14; mask; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + header |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + header &= ~mask; + } else { + return {}; + } + } + + if (header != TOTO_HEADER) { + return {}; + } + + for (uint32_t mask = 1UL << 23; mask; mask >>= 1) { + if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { + payload |= mask; + } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { + payload &= ~mask; + } else { + return {}; + } + } + + if ((((payload & 0xFF0000) >> 16) ^ ((payload & 0x00FF00) >> 8)) != (payload & 0x0000FF)) { + return {}; + } + + data.rc_code_1 = (payload & 0xF00000) >> 20; + data.rc_code_2 = (payload & 0x0F0000) >> 16; + data.command = (payload & 0x00FF00) >> 8; + + return data; +} +void TotoProtocol::dump(const TotoData &data) { + ESP_LOGI(TAG, "Received Toto data: rc_code_1=0x%01X, rc_code_2=0x%01X, command=0x%02X", data.rc_code_1, + data.rc_code_2, data.command); +} + +} // namespace remote_base +} // namespace esphome diff --git a/esphome/components/remote_base/toto_protocol.h b/esphome/components/remote_base/toto_protocol.h new file mode 100644 index 0000000000..e62714bbbf --- /dev/null +++ b/esphome/components/remote_base/toto_protocol.h @@ -0,0 +1,45 @@ +#pragma once + +#include "remote_base.h" + +namespace esphome { +namespace remote_base { + +struct TotoData { + uint8_t rc_code_1 : 4; + uint8_t rc_code_2 : 4; + uint8_t command; + + bool operator==(const TotoData &rhs) const { + return (rc_code_1 == rhs.rc_code_1) && (rc_code_2 == rhs.rc_code_2) && (command == rhs.command); + } +}; + +class TotoProtocol : public RemoteProtocol { + public: + void encode(RemoteTransmitData *dst, const TotoData &data) override; + optional decode(RemoteReceiveData src) override; + void dump(const TotoData &data) override; +}; + +DECLARE_REMOTE_PROTOCOL(Toto) + +template class TotoAction : public RemoteTransmitterActionBase { + public: + TEMPLATABLE_VALUE(uint8_t, rc_code_1) + TEMPLATABLE_VALUE(uint8_t, rc_code_2) + TEMPLATABLE_VALUE(uint8_t, command) + + void encode(RemoteTransmitData *dst, Ts... x) override { + TotoData data{}; + data.rc_code_1 = this->rc_code_1_.value(x...); + data.rc_code_2 = this->rc_code_2_.value(x...); + data.command = this->command_.value(x...); + this->set_send_times(this->send_times_.value_or(x..., 3)); + this->set_send_wait(this->send_wait_.value_or(x..., 32000)); + TotoProtocol().encode(dst, data); + } +}; + +} // namespace remote_base +} // namespace esphome diff --git a/tests/components/remote_receiver/common-actions.yaml b/tests/components/remote_receiver/common-actions.yaml index 23589aed22..c1f576d20e 100644 --- a/tests/components/remote_receiver/common-actions.yaml +++ b/tests/components/remote_receiver/common-actions.yaml @@ -142,3 +142,8 @@ on_mirage: then: - lambda: |- ESP_LOGD("mirage", "Mirage data: %s", format_hex(x.data).c_str()); +on_toto: + then: + - logger.log: + format: "on_toto: %u %u %u" + args: ["x.rc_code_1", "x.rc_code_2", "x.command"] diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index c6a2453b20..b037c50e12 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -190,3 +190,10 @@ button: channel: 1 button: 1 check: 1 + - platform: template + name: Toto + on_press: + - remote_transmitter.transmit_toto: + command: 0xEC + rc_code_1: 0x0D + rc_code_2: 0x0D From da3d007d7b869fad9ec56aa0d0e3945c9bd32411 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 9 Feb 2025 15:40:19 -0600 Subject: [PATCH 0975/1052] Markdown tweaks/updates (#8211) --- CONTRIBUTING.md | 20 +++++++++++--------- README.md | 6 +++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c92d91159..7be7bdac2c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,12 +1,14 @@ -# Contributing to ESPHome +# Contributing to ESPHome [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/) -For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome +We welcome contributions to the ESPHome suite of code and documentation! -Things to note when contributing: +Please read our [contributing guide](https://esphome.io/guides/contributing.html) if you wish to contribute to the +project and be sure to join us on [Discord](https://discord.gg/KhAMKrd). - - Please test your changes :) - - If a new feature is added or an existing user-facing feature is changed, you should also - update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs) - for more information. - - Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files - which checks if your new feature compiles correctly. +**See also:** + +[Documentation](https://esphome.io) -- [Issues](https://github.com/esphome/issues/issues) -- [Feature requests](https://github.com/esphome/feature-requests/issues) + +--- + +[![ESPHome - A project from the Open Home Foundation](https://www.openhomefoundation.org/badges/esphome.png)](https://www.openhomefoundation.org/) diff --git a/README.md b/README.md index 8e3d8f71aa..4f527870b8 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ -**Documentation:** https://esphome.io/ +--- -For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues). +[Documentation](https://esphome.io) -- [Issues](https://github.com/esphome/issues/issues) -- [Feature requests](https://github.com/esphome/feature-requests/issues) -For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues). +--- [![ESPHome - A project from the Open Home Foundation](https://www.openhomefoundation.org/badges/esphome.png)](https://www.openhomefoundation.org/) From 353924257ab1e38ca9d3228e1ac12a0e52055179 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 9 Feb 2025 15:43:10 -0600 Subject: [PATCH 0976/1052] [CI] Consolidate some tests (S) (#8206) --- tests/components/scd30/common.yaml | 20 +++++++ tests/components/scd30/test.esp32-ard.yaml | 23 ++------ tests/components/scd30/test.esp32-c3-ard.yaml | 23 ++------ tests/components/scd30/test.esp32-c3-idf.yaml | 23 ++------ tests/components/scd30/test.esp32-idf.yaml | 23 ++------ tests/components/scd30/test.esp8266-ard.yaml | 23 ++------ tests/components/scd30/test.rp2040-ard.yaml | 23 ++------ tests/components/scd4x/common.yaml | 20 +++++++ tests/components/scd4x/test.esp32-ard.yaml | 23 ++------ tests/components/scd4x/test.esp32-c3-ard.yaml | 23 ++------ tests/components/scd4x/test.esp32-c3-idf.yaml | 23 ++------ tests/components/scd4x/test.esp32-idf.yaml | 23 ++------ tests/components/scd4x/test.esp8266-ard.yaml | 23 ++------ tests/components/scd4x/test.rp2040-ard.yaml | 23 ++------ tests/components/sdm_meter/common.yaml | 23 ++++++++ .../components/sdm_meter/test.esp32-ard.yaml | 26 ++-------- .../sdm_meter/test.esp32-c3-ard.yaml | 26 ++-------- .../sdm_meter/test.esp32-c3-idf.yaml | 26 ++-------- .../components/sdm_meter/test.esp32-idf.yaml | 26 ++-------- .../sdm_meter/test.esp8266-ard.yaml | 26 ++-------- .../components/sdm_meter/test.rp2040-ard.yaml | 26 ++-------- tests/components/sdp3x/common.yaml | 11 ++++ tests/components/sdp3x/test.esp32-ard.yaml | 14 ++--- tests/components/sdp3x/test.esp32-c3-ard.yaml | 14 ++--- tests/components/sdp3x/test.esp32-c3-idf.yaml | 14 ++--- tests/components/sdp3x/test.esp32-idf.yaml | 14 ++--- tests/components/sdp3x/test.esp8266-ard.yaml | 14 ++--- tests/components/sdp3x/test.rp2040-ard.yaml | 14 ++--- tests/components/sds011/common.yaml | 14 +++++ tests/components/sds011/test.esp32-ard.yaml | 17 ++---- .../components/sds011/test.esp32-c3-ard.yaml | 17 ++---- .../components/sds011/test.esp32-c3-idf.yaml | 17 ++---- tests/components/sds011/test.esp32-idf.yaml | 17 ++---- tests/components/sds011/test.esp8266-ard.yaml | 17 ++---- tests/components/sds011/test.rp2040-ard.yaml | 17 ++---- tests/components/selec_meter/common.yaml | 45 ++++++++++++++++ .../selec_meter/test.esp32-ard.yaml | 48 ++--------------- .../selec_meter/test.esp32-c3-ard.yaml | 48 ++--------------- .../selec_meter/test.esp32-c3-idf.yaml | 48 ++--------------- .../selec_meter/test.esp32-idf.yaml | 48 ++--------------- .../selec_meter/test.esp8266-ard.yaml | 48 ++--------------- .../selec_meter/test.rp2040-ard.yaml | 48 ++--------------- tests/components/sen0321/common.yaml | 10 ++++ tests/components/sen0321/test.esp32-ard.yaml | 13 ++--- .../components/sen0321/test.esp32-c3-ard.yaml | 13 ++--- .../components/sen0321/test.esp32-c3-idf.yaml | 13 ++--- tests/components/sen0321/test.esp32-idf.yaml | 13 ++--- .../components/sen0321/test.esp8266-ard.yaml | 13 ++--- tests/components/sen0321/test.rp2040-ard.yaml | 13 ++--- tests/components/sen21231/common.yaml | 9 ++++ tests/components/sen21231/test.esp32-ard.yaml | 12 ++--- .../sen21231/test.esp32-c3-ard.yaml | 12 ++--- .../sen21231/test.esp32-c3-idf.yaml | 12 ++--- tests/components/sen21231/test.esp32-idf.yaml | 12 ++--- .../components/sen21231/test.esp8266-ard.yaml | 12 ++--- .../components/sen21231/test.rp2040-ard.yaml | 12 ++--- tests/components/sen5x/common.yaml | 49 +++++++++++++++++ tests/components/sen5x/test.esp32-ard.yaml | 52 ++----------------- tests/components/sen5x/test.esp32-c3-ard.yaml | 52 ++----------------- tests/components/sen5x/test.esp32-c3-idf.yaml | 52 ++----------------- tests/components/sen5x/test.esp32-idf.yaml | 52 ++----------------- tests/components/sen5x/test.esp8266-ard.yaml | 52 ++----------------- tests/components/sen5x/test.rp2040-ard.yaml | 52 ++----------------- tests/components/senseair/common.yaml | 19 +++++++ tests/components/senseair/test.esp32-ard.yaml | 22 ++------ .../senseair/test.esp32-c3-ard.yaml | 22 ++------ .../senseair/test.esp32-c3-idf.yaml | 22 ++------ tests/components/senseair/test.esp32-idf.yaml | 22 ++------ .../components/senseair/test.esp8266-ard.yaml | 22 ++------ .../components/senseair/test.rp2040-ard.yaml | 22 ++------ tests/components/servo/common.yaml | 19 +++++++ tests/components/servo/test.esp32-ard.yaml | 22 ++------ tests/components/servo/test.esp32-c3-ard.yaml | 22 ++------ tests/components/servo/test.esp32-c3-idf.yaml | 22 ++------ tests/components/servo/test.esp32-idf.yaml | 22 ++------ tests/components/servo/test.esp8266-ard.yaml | 22 ++------ tests/components/servo/test.rp2040-ard.yaml | 22 ++------ tests/components/sfa30/common.yaml | 15 ++++++ tests/components/sfa30/test.esp32-ard.yaml | 18 ++----- tests/components/sfa30/test.esp32-c3-ard.yaml | 18 ++----- tests/components/sfa30/test.esp32-c3-idf.yaml | 18 ++----- tests/components/sfa30/test.esp32-idf.yaml | 18 ++----- tests/components/sfa30/test.esp8266-ard.yaml | 18 ++----- tests/components/sfa30/test.rp2040-ard.yaml | 18 ++----- tests/components/sgp30/common.yaml | 15 ++++++ tests/components/sgp30/test.esp32-ard.yaml | 18 ++----- tests/components/sgp30/test.esp32-c3-ard.yaml | 18 ++----- tests/components/sgp30/test.esp32-c3-idf.yaml | 18 ++----- tests/components/sgp30/test.esp32-idf.yaml | 18 ++----- tests/components/sgp30/test.esp8266-ard.yaml | 18 ++----- tests/components/sgp30/test.rp2040-ard.yaml | 18 ++----- tests/components/sgp4x/common.yaml | 27 ++++++++++ tests/components/sgp4x/test.esp32-ard.yaml | 30 ++--------- tests/components/sgp4x/test.esp32-c3-ard.yaml | 30 ++--------- tests/components/sgp4x/test.esp32-c3-idf.yaml | 30 ++--------- tests/components/sgp4x/test.esp32-idf.yaml | 30 ++--------- tests/components/sgp4x/test.esp8266-ard.yaml | 30 ++--------- tests/components/sgp4x/test.rp2040-ard.yaml | 30 ++--------- tests/components/sht3xd/common.yaml | 13 +++++ tests/components/sht3xd/test.esp32-ard.yaml | 16 ++---- .../components/sht3xd/test.esp32-c3-ard.yaml | 16 ++---- .../components/sht3xd/test.esp32-c3-idf.yaml | 16 ++---- tests/components/sht3xd/test.esp32-idf.yaml | 16 ++---- tests/components/sht3xd/test.esp8266-ard.yaml | 16 ++---- tests/components/sht3xd/test.rp2040-ard.yaml | 16 ++---- tests/components/sht4x/common.yaml | 13 +++++ tests/components/sht4x/test.esp32-ard.yaml | 16 ++---- tests/components/sht4x/test.esp32-c3-ard.yaml | 16 ++---- tests/components/sht4x/test.esp32-c3-idf.yaml | 16 ++---- tests/components/sht4x/test.esp32-idf.yaml | 16 ++---- tests/components/sht4x/test.esp8266-ard.yaml | 16 ++---- tests/components/sht4x/test.rp2040-ard.yaml | 16 ++---- tests/components/shtcx/common.yaml | 13 +++++ tests/components/shtcx/test.esp32-ard.yaml | 16 ++---- tests/components/shtcx/test.esp32-c3-ard.yaml | 16 ++---- tests/components/shtcx/test.esp32-c3-idf.yaml | 16 ++---- tests/components/shtcx/test.esp32-idf.yaml | 16 ++---- tests/components/shtcx/test.esp8266-ard.yaml | 16 ++---- tests/components/shtcx/test.rp2040-ard.yaml | 16 ++---- tests/components/sim800l/common.yaml | 37 +++++++++++++ tests/components/sim800l/test.esp32-ard.yaml | 40 ++------------ .../components/sim800l/test.esp32-c3-ard.yaml | 40 ++------------ .../components/sim800l/test.esp32-c3-idf.yaml | 40 ++------------ tests/components/sim800l/test.esp32-idf.yaml | 40 ++------------ .../components/sim800l/test.esp8266-ard.yaml | 40 ++------------ tests/components/sim800l/test.rp2040-ard.yaml | 40 ++------------ tests/components/sm16716/common.yaml | 4 +- tests/components/sm16716/test.esp32-ard.yaml | 4 ++ .../components/sm16716/test.esp32-c3-ard.yaml | 4 ++ .../components/sm16716/test.esp32-c3-idf.yaml | 4 ++ tests/components/sm16716/test.esp32-idf.yaml | 4 ++ .../components/sm16716/test.esp8266-ard.yaml | 4 ++ tests/components/sm16716/test.rp2040-ard.yaml | 4 ++ tests/components/sm2135/common.yaml | 4 +- tests/components/sm2135/test.esp32-ard.yaml | 4 ++ .../components/sm2135/test.esp32-c3-ard.yaml | 4 ++ .../components/sm2135/test.esp32-c3-idf.yaml | 4 ++ tests/components/sm2135/test.esp32-idf.yaml | 4 ++ tests/components/sm2135/test.esp8266-ard.yaml | 4 ++ tests/components/sm2135/test.rp2040-ard.yaml | 4 ++ tests/components/sm2235/common.yaml | 4 +- tests/components/sm2235/test.esp32-ard.yaml | 4 ++ .../components/sm2235/test.esp32-c3-ard.yaml | 4 ++ .../components/sm2235/test.esp32-c3-idf.yaml | 4 ++ tests/components/sm2235/test.esp32-idf.yaml | 4 ++ tests/components/sm2235/test.esp8266-ard.yaml | 4 ++ tests/components/sm2235/test.rp2040-ard.yaml | 4 ++ tests/components/sm2335/common.yaml | 4 +- tests/components/sm2335/test.esp32-ard.yaml | 4 ++ .../components/sm2335/test.esp32-c3-ard.yaml | 4 ++ .../components/sm2335/test.esp32-c3-idf.yaml | 4 ++ tests/components/sm2335/test.esp32-idf.yaml | 4 ++ tests/components/sm2335/test.esp8266-ard.yaml | 4 ++ tests/components/sm2335/test.rp2040-ard.yaml | 4 ++ tests/components/sm300d2/common.yaml | 23 ++++++++ tests/components/sm300d2/test.esp32-ard.yaml | 26 ++-------- .../components/sm300d2/test.esp32-c3-ard.yaml | 26 ++-------- .../components/sm300d2/test.esp32-c3-idf.yaml | 26 ++-------- tests/components/sm300d2/test.esp32-idf.yaml | 26 ++-------- .../components/sm300d2/test.esp8266-ard.yaml | 26 ++-------- tests/components/sm300d2/test.rp2040-ard.yaml | 26 ++-------- tests/components/sml/common.yaml | 31 +++++++++++ tests/components/sml/test.esp32-ard.yaml | 34 ++---------- tests/components/sml/test.esp32-c3-ard.yaml | 34 ++---------- tests/components/sml/test.esp32-c3-idf.yaml | 34 ++---------- tests/components/sml/test.esp32-idf.yaml | 34 ++---------- tests/components/sml/test.esp8266-ard.yaml | 34 ++---------- tests/components/sml/test.rp2040-ard.yaml | 34 ++---------- tests/components/smt100/common.yaml | 19 +++++++ tests/components/smt100/test.esp32-ard.yaml | 22 ++------ .../components/smt100/test.esp32-c3-ard.yaml | 22 ++------ .../components/smt100/test.esp32-c3-idf.yaml | 22 ++------ tests/components/smt100/test.esp32-idf.yaml | 22 ++------ tests/components/smt100/test.esp8266-ard.yaml | 22 ++------ tests/components/smt100/test.rp2040-ard.yaml | 22 ++------ tests/components/sn74hc165/common.yaml | 14 +++++ .../components/sn74hc165/test.esp32-ard.yaml | 19 +++---- .../sn74hc165/test.esp32-c3-ard.yaml | 19 +++---- .../sn74hc165/test.esp32-c3-idf.yaml | 19 +++---- .../components/sn74hc165/test.esp32-idf.yaml | 19 +++---- .../sn74hc165/test.esp8266-ard.yaml | 19 +++---- .../components/sn74hc165/test.rp2040-ard.yaml | 19 +++---- tests/components/sn74hc595/common.yaml | 26 ++++++++++ .../components/sn74hc595/test.esp32-ard.yaml | 37 ++++--------- .../sn74hc595/test.esp32-c3-ard.yaml | 37 ++++--------- .../sn74hc595/test.esp32-c3-idf.yaml | 37 ++++--------- .../components/sn74hc595/test.esp32-idf.yaml | 37 ++++--------- .../sn74hc595/test.esp8266-ard.yaml | 37 ++++--------- .../components/sn74hc595/test.rp2040-ard.yaml | 37 ++++--------- tests/components/sonoff_d1/common.yaml | 12 +++++ .../components/sonoff_d1/test.esp32-ard.yaml | 15 ++---- .../sonoff_d1/test.esp32-c3-ard.yaml | 5 ++ .../sonoff_d1/test.esp32-c3-idf.yaml | 5 ++ .../components/sonoff_d1/test.esp32-idf.yaml | 15 ++---- .../sonoff_d1/test.esp8266-ard.yaml | 15 ++---- .../components/sonoff_d1/test.rp2040-ard.yaml | 5 ++ .../speaker/audio_dac.esp32-ard.yaml | 9 ++++ .../speaker/audio_dac.esp32-c3-ard.yaml | 9 ++++ .../speaker/audio_dac.esp32-c3-idf.yaml | 9 ++++ .../speaker/audio_dac.esp32-idf.yaml | 9 ++++ .../components/speaker/common-audio_dac.yaml | 36 +++++++++++++ tests/components/speaker/common.yaml | 26 ++++++++++ tests/components/speaker/test.esp32-ard.yaml | 33 +++--------- .../components/speaker/test.esp32-c3-ard.yaml | 33 +++--------- .../components/speaker/test.esp32-c3-idf.yaml | 33 +++--------- tests/components/speaker/test.esp32-idf.yaml | 42 +++------------ tests/components/speed/common.yaml | 9 ++++ tests/components/speed/test.esp32-ard.yaml | 12 ++--- tests/components/speed/test.esp32-c3-ard.yaml | 12 ++--- tests/components/speed/test.esp32-c3-idf.yaml | 12 ++--- tests/components/speed/test.esp32-idf.yaml | 12 ++--- tests/components/speed/test.esp8266-ard.yaml | 12 ++--- tests/components/speed/test.rp2040-ard.yaml | 12 ++--- tests/components/spi/common.yaml | 5 ++ tests/components/spi/test.esp32-ard.yaml | 11 ++-- tests/components/spi/test.esp32-c3-ard.yaml | 11 ++-- tests/components/spi/test.esp32-c3-idf.yaml | 11 ++-- tests/components/spi/test.esp32-idf.yaml | 11 ++-- tests/components/spi/test.esp32-s3-idf.yaml | 4 +- tests/components/spi/test.esp8266-ard.yaml | 11 ++-- tests/components/spi/test.rp2040-ard.yaml | 11 ++-- tests/components/spi_device/common.yaml | 11 ++++ .../components/spi_device/test.esp32-ard.yaml | 15 ++---- .../spi_device/test.esp32-c3-ard.yaml | 15 ++---- .../spi_device/test.esp32-c3-idf.yaml | 15 ++---- .../components/spi_device/test.esp32-idf.yaml | 15 ++---- .../spi_device/test.esp8266-ard.yaml | 15 ++---- .../spi_device/test.rp2040-ard.yaml | 15 ++---- tests/components/spi_led_strip/common.yaml | 12 +++++ .../spi_led_strip/test.esp32-ard.yaml | 16 ++---- .../spi_led_strip/test.esp32-c3-ard.yaml | 16 ++---- .../spi_led_strip/test.esp32-c3-idf.yaml | 16 ++---- .../spi_led_strip/test.esp32-idf.yaml | 16 ++---- .../spi_led_strip/test.esp8266-ard.yaml | 16 ++---- .../spi_led_strip/test.rp2040-ard.yaml | 16 ++---- tests/components/sps30/common.yaml | 36 +++++++++++++ tests/components/sps30/test.esp32-ard.yaml | 39 ++------------ tests/components/sps30/test.esp32-c3-ard.yaml | 39 ++------------ tests/components/sps30/test.esp32-c3-idf.yaml | 39 ++------------ tests/components/sps30/test.esp32-idf.yaml | 39 ++------------ tests/components/sps30/test.esp8266-ard.yaml | 39 ++------------ tests/components/sps30/test.rp2040-ard.yaml | 39 ++------------ tests/components/ssd1306_i2c/common.yaml | 25 +++++++++ .../ssd1306_i2c/test.esp32-ard.yaml | 29 ++--------- .../ssd1306_i2c/test.esp32-c3-ard.yaml | 29 ++--------- .../ssd1306_i2c/test.esp32-c3-idf.yaml | 29 ++--------- .../ssd1306_i2c/test.esp32-idf.yaml | 29 ++--------- .../ssd1306_i2c/test.esp8266-ard.yaml | 29 ++--------- .../ssd1306_i2c/test.rp2040-ard.yaml | 29 ++--------- tests/components/ssd1306_spi/common.yaml | 24 +++++++++ .../ssd1306_spi/test.esp32-ard.yaml | 31 +++-------- .../ssd1306_spi/test.esp32-c3-ard.yaml | 32 +++--------- .../ssd1306_spi/test.esp32-c3-idf.yaml | 32 +++--------- .../ssd1306_spi/test.esp32-idf.yaml | 31 +++-------- .../ssd1306_spi/test.esp8266-ard.yaml | 32 +++--------- .../ssd1306_spi/test.rp2040-ard.yaml | 32 +++--------- tests/components/ssd1322_spi/common.yaml | 24 +++++++++ .../ssd1322_spi/test.esp32-ard.yaml | 31 +++-------- .../ssd1322_spi/test.esp32-c3-ard.yaml | 32 +++--------- .../ssd1322_spi/test.esp32-c3-idf.yaml | 32 +++--------- .../ssd1322_spi/test.esp32-idf.yaml | 31 +++-------- .../ssd1322_spi/test.esp8266-ard.yaml | 32 +++--------- .../ssd1322_spi/test.rp2040-ard.yaml | 32 +++--------- tests/components/ssd1325_spi/common.yaml | 24 +++++++++ .../ssd1325_spi/test.esp32-ard.yaml | 31 +++-------- .../ssd1325_spi/test.esp32-c3-ard.yaml | 32 +++--------- .../ssd1325_spi/test.esp32-c3-idf.yaml | 32 +++--------- .../ssd1325_spi/test.esp32-idf.yaml | 31 +++-------- .../ssd1325_spi/test.esp8266-ard.yaml | 32 +++--------- .../ssd1325_spi/test.rp2040-ard.yaml | 32 +++--------- tests/components/ssd1327_i2c/common.yaml | 24 +++++++++ .../ssd1327_i2c/test.esp32-ard.yaml | 28 ++-------- .../ssd1327_i2c/test.esp32-c3-ard.yaml | 28 ++-------- .../ssd1327_i2c/test.esp32-c3-idf.yaml | 28 ++-------- .../ssd1327_i2c/test.esp32-idf.yaml | 28 ++-------- .../ssd1327_i2c/test.esp8266-ard.yaml | 28 ++-------- .../ssd1327_i2c/test.rp2040-ard.yaml | 28 ++-------- tests/components/ssd1327_spi/common.yaml | 24 +++++++++ .../ssd1327_spi/test.esp32-ard.yaml | 31 +++-------- .../ssd1327_spi/test.esp32-c3-ard.yaml | 32 +++--------- .../ssd1327_spi/test.esp32-c3-idf.yaml | 32 +++--------- .../ssd1327_spi/test.esp32-idf.yaml | 31 +++-------- .../ssd1327_spi/test.esp8266-ard.yaml | 32 +++--------- .../ssd1327_spi/test.rp2040-ard.yaml | 32 +++--------- tests/components/ssd1331_spi/common.yaml | 23 ++++++++ .../ssd1331_spi/test.esp32-ard.yaml | 30 +++-------- .../ssd1331_spi/test.esp32-c3-ard.yaml | 31 +++-------- .../ssd1331_spi/test.esp32-c3-idf.yaml | 31 +++-------- .../ssd1331_spi/test.esp32-idf.yaml | 30 +++-------- .../ssd1331_spi/test.esp8266-ard.yaml | 31 +++-------- .../ssd1331_spi/test.rp2040-ard.yaml | 31 +++-------- tests/components/ssd1351_spi/common.yaml | 24 +++++++++ .../ssd1351_spi/test.esp32-ard.yaml | 31 +++-------- .../ssd1351_spi/test.esp32-c3-ard.yaml | 32 +++--------- .../ssd1351_spi/test.esp32-c3-idf.yaml | 32 +++--------- .../ssd1351_spi/test.esp32-idf.yaml | 31 +++-------- .../ssd1351_spi/test.esp8266-ard.yaml | 32 +++--------- .../ssd1351_spi/test.rp2040-ard.yaml | 32 +++--------- tests/components/st7567_i2c/common.yaml | 23 ++++++++ .../components/st7567_i2c/test.esp32-ard.yaml | 27 ++-------- .../st7567_i2c/test.esp32-c3-ard.yaml | 27 ++-------- .../st7567_i2c/test.esp32-c3-idf.yaml | 27 ++-------- .../components/st7567_i2c/test.esp32-idf.yaml | 27 ++-------- .../st7567_i2c/test.esp8266-ard.yaml | 27 ++-------- .../st7567_i2c/test.rp2040-ard.yaml | 27 ++-------- tests/components/st7567_spi/common.yaml | 23 ++++++++ .../components/st7567_spi/test.esp32-ard.yaml | 30 +++-------- .../st7567_spi/test.esp32-c3-ard.yaml | 31 +++-------- .../st7567_spi/test.esp32-c3-idf.yaml | 31 +++-------- .../components/st7567_spi/test.esp32-idf.yaml | 30 +++-------- .../st7567_spi/test.esp8266-ard.yaml | 31 +++-------- .../st7567_spi/test.rp2040-ard.yaml | 31 +++-------- tests/components/st7701s/common.yaml | 42 ++++----------- .../components/st7701s/test.esp32-s3-idf.yaml | 10 ++++ tests/components/st7735/common.yaml | 28 ++++++++++ tests/components/st7735/test.esp32-ard.yaml | 35 +++---------- .../components/st7735/test.esp32-c3-ard.yaml | 36 +++---------- .../components/st7735/test.esp32-c3-idf.yaml | 36 +++---------- tests/components/st7735/test.esp32-idf.yaml | 35 +++---------- tests/components/st7735/test.esp8266-ard.yaml | 36 +++---------- tests/components/st7735/test.rp2040-ard.yaml | 36 +++---------- tests/components/st7789v/common.yaml | 24 +++++++++ tests/components/st7789v/test.esp32-ard.yaml | 31 +++-------- .../components/st7789v/test.esp32-c3-ard.yaml | 32 +++--------- .../components/st7789v/test.esp32-c3-idf.yaml | 32 +++--------- tests/components/st7789v/test.esp32-idf.yaml | 31 +++-------- .../components/st7789v/test.esp8266-ard.yaml | 32 +++--------- tests/components/st7789v/test.rp2040-ard.yaml | 32 +++--------- tests/components/st7920/common.yaml | 23 ++++++++ tests/components/st7920/test.esp32-ard.yaml | 28 ++-------- .../components/st7920/test.esp32-c3-ard.yaml | 29 +++-------- .../components/st7920/test.esp32-c3-idf.yaml | 29 +++-------- tests/components/st7920/test.esp32-idf.yaml | 28 ++-------- tests/components/st7920/test.esp8266-ard.yaml | 29 +++-------- tests/components/st7920/test.rp2040-ard.yaml | 29 +++-------- tests/components/sts3x/common.yaml | 10 ++++ tests/components/sts3x/test.esp32-ard.yaml | 13 ++--- tests/components/sts3x/test.esp32-c3-ard.yaml | 13 ++--- tests/components/sts3x/test.esp32-c3-idf.yaml | 13 ++--- tests/components/sts3x/test.esp32-idf.yaml | 13 ++--- tests/components/sts3x/test.esp8266-ard.yaml | 13 ++--- tests/components/sts3x/test.rp2040-ard.yaml | 13 ++--- tests/components/sun_gtil2/common.yaml | 40 ++++++++++++++ .../components/sun_gtil2/test.esp32-ard.yaml | 46 ++-------------- .../sun_gtil2/test.esp32-c3-ard.yaml | 46 ++-------------- .../sun_gtil2/test.esp32-c3-idf.yaml | 46 ++-------------- .../components/sun_gtil2/test.esp32-idf.yaml | 46 ++-------------- .../sun_gtil2/test.esp8266-ard.yaml | 46 ++-------------- .../components/sun_gtil2/test.rp2040-ard.yaml | 46 ++-------------- tests/components/sx1509/common.yaml | 33 ++++++++++++ tests/components/sx1509/test.esp32-ard.yaml | 36 ++----------- .../components/sx1509/test.esp32-c3-ard.yaml | 36 ++----------- .../components/sx1509/test.esp32-c3-idf.yaml | 36 ++----------- tests/components/sx1509/test.esp32-idf.yaml | 36 ++----------- tests/components/sx1509/test.esp8266-ard.yaml | 36 ++----------- tests/components/sx1509/test.rp2040-ard.yaml | 36 ++----------- 356 files changed, 2598 insertions(+), 5745 deletions(-) create mode 100644 tests/components/scd30/common.yaml create mode 100644 tests/components/scd4x/common.yaml create mode 100644 tests/components/sdm_meter/common.yaml create mode 100644 tests/components/sdp3x/common.yaml create mode 100644 tests/components/sds011/common.yaml create mode 100644 tests/components/selec_meter/common.yaml create mode 100644 tests/components/sen0321/common.yaml create mode 100644 tests/components/sen21231/common.yaml create mode 100644 tests/components/sen5x/common.yaml create mode 100644 tests/components/senseair/common.yaml create mode 100644 tests/components/servo/common.yaml create mode 100644 tests/components/sfa30/common.yaml create mode 100644 tests/components/sgp30/common.yaml create mode 100644 tests/components/sgp4x/common.yaml create mode 100644 tests/components/sht3xd/common.yaml create mode 100644 tests/components/sht4x/common.yaml create mode 100644 tests/components/shtcx/common.yaml create mode 100644 tests/components/sim800l/common.yaml create mode 100644 tests/components/sm300d2/common.yaml create mode 100644 tests/components/sml/common.yaml create mode 100644 tests/components/smt100/common.yaml create mode 100644 tests/components/sn74hc165/common.yaml create mode 100644 tests/components/sn74hc595/common.yaml create mode 100644 tests/components/sonoff_d1/common.yaml create mode 100644 tests/components/sonoff_d1/test.esp32-c3-ard.yaml create mode 100644 tests/components/sonoff_d1/test.esp32-c3-idf.yaml create mode 100644 tests/components/sonoff_d1/test.rp2040-ard.yaml create mode 100644 tests/components/speaker/audio_dac.esp32-ard.yaml create mode 100644 tests/components/speaker/audio_dac.esp32-c3-ard.yaml create mode 100644 tests/components/speaker/audio_dac.esp32-c3-idf.yaml create mode 100644 tests/components/speaker/audio_dac.esp32-idf.yaml create mode 100644 tests/components/speaker/common-audio_dac.yaml create mode 100644 tests/components/speaker/common.yaml create mode 100644 tests/components/speed/common.yaml create mode 100644 tests/components/spi/common.yaml create mode 100644 tests/components/spi_device/common.yaml create mode 100644 tests/components/spi_led_strip/common.yaml create mode 100644 tests/components/sps30/common.yaml create mode 100644 tests/components/ssd1306_i2c/common.yaml create mode 100644 tests/components/ssd1306_spi/common.yaml create mode 100644 tests/components/ssd1322_spi/common.yaml create mode 100644 tests/components/ssd1325_spi/common.yaml create mode 100644 tests/components/ssd1327_i2c/common.yaml create mode 100644 tests/components/ssd1327_spi/common.yaml create mode 100644 tests/components/ssd1331_spi/common.yaml create mode 100644 tests/components/ssd1351_spi/common.yaml create mode 100644 tests/components/st7567_i2c/common.yaml create mode 100644 tests/components/st7567_spi/common.yaml create mode 100644 tests/components/st7735/common.yaml create mode 100644 tests/components/st7789v/common.yaml create mode 100644 tests/components/st7920/common.yaml create mode 100644 tests/components/sts3x/common.yaml create mode 100644 tests/components/sun_gtil2/common.yaml create mode 100644 tests/components/sx1509/common.yaml diff --git a/tests/components/scd30/common.yaml b/tests/components/scd30/common.yaml new file mode 100644 index 0000000000..1c45c67af0 --- /dev/null +++ b/tests/components/scd30/common.yaml @@ -0,0 +1,20 @@ +i2c: + - id: i2c_scd30 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: scd30 + co2: + name: SCD30 CO2 + temperature: + id: scd30_temperature + name: SCD30 Temperature + humidity: + name: SCD30 Humidity + address: 0x61 + automatic_self_calibration: true + altitude_compensation: 10m + ambient_pressure_compensation: 961mBar + temperature_offset: 4.2C + update_interval: 15s diff --git a/tests/components/scd30/test.esp32-ard.yaml b/tests/components/scd30/test.esp32-ard.yaml index b48f8054c8..63c3bd6afd 100644 --- a/tests/components/scd30/test.esp32-ard.yaml +++ b/tests/components/scd30/test.esp32-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: scd30 - co2: - name: SCD30 CO2 - temperature: - id: scd30_temperature - name: SCD30 Temperature - humidity: - name: SCD30 Humidity - address: 0x61 - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd30/test.esp32-c3-ard.yaml b/tests/components/scd30/test.esp32-c3-ard.yaml index 80f02a1b87..ee2c29ca4e 100644 --- a/tests/components/scd30/test.esp32-c3-ard.yaml +++ b/tests/components/scd30/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd30 - co2: - name: SCD30 CO2 - temperature: - id: scd30_temperature - name: SCD30 Temperature - humidity: - name: SCD30 Humidity - address: 0x61 - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd30/test.esp32-c3-idf.yaml b/tests/components/scd30/test.esp32-c3-idf.yaml index 80f02a1b87..ee2c29ca4e 100644 --- a/tests/components/scd30/test.esp32-c3-idf.yaml +++ b/tests/components/scd30/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd30 - co2: - name: SCD30 CO2 - temperature: - id: scd30_temperature - name: SCD30 Temperature - humidity: - name: SCD30 Humidity - address: 0x61 - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd30/test.esp32-idf.yaml b/tests/components/scd30/test.esp32-idf.yaml index b48f8054c8..63c3bd6afd 100644 --- a/tests/components/scd30/test.esp32-idf.yaml +++ b/tests/components/scd30/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: scd30 - co2: - name: SCD30 CO2 - temperature: - id: scd30_temperature - name: SCD30 Temperature - humidity: - name: SCD30 Humidity - address: 0x61 - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd30/test.esp8266-ard.yaml b/tests/components/scd30/test.esp8266-ard.yaml index 80f02a1b87..ee2c29ca4e 100644 --- a/tests/components/scd30/test.esp8266-ard.yaml +++ b/tests/components/scd30/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd30 - co2: - name: SCD30 CO2 - temperature: - id: scd30_temperature - name: SCD30 Temperature - humidity: - name: SCD30 Humidity - address: 0x61 - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd30/test.rp2040-ard.yaml b/tests/components/scd30/test.rp2040-ard.yaml index 80f02a1b87..ee2c29ca4e 100644 --- a/tests/components/scd30/test.rp2040-ard.yaml +++ b/tests/components/scd30/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd30 - co2: - name: SCD30 CO2 - temperature: - id: scd30_temperature - name: SCD30 Temperature - humidity: - name: SCD30 Humidity - address: 0x61 - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd4x/common.yaml b/tests/components/scd4x/common.yaml new file mode 100644 index 0000000000..dfd35e57de --- /dev/null +++ b/tests/components/scd4x/common.yaml @@ -0,0 +1,20 @@ +i2c: + - id: i2c_scd4x + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: scd4x + id: scd40 + co2: + name: SCD4X CO2 + temperature: + id: scd4x_temperature + name: SCD4X Temperature + humidity: + name: SCD4X Humidity + automatic_self_calibration: true + altitude_compensation: 10m + ambient_pressure_compensation: 961mBar + temperature_offset: 4.2C + update_interval: 15s diff --git a/tests/components/scd4x/test.esp32-ard.yaml b/tests/components/scd4x/test.esp32-ard.yaml index 02cec921d2..63c3bd6afd 100644 --- a/tests/components/scd4x/test.esp32-ard.yaml +++ b/tests/components/scd4x/test.esp32-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd4x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: scd4x - id: scd40 - co2: - name: SCD4X CO2 - temperature: - id: scd4x_temperature - name: SCD4X Temperature - humidity: - name: SCD4X Humidity - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd4x/test.esp32-c3-ard.yaml b/tests/components/scd4x/test.esp32-c3-ard.yaml index 353293be65..ee2c29ca4e 100644 --- a/tests/components/scd4x/test.esp32-c3-ard.yaml +++ b/tests/components/scd4x/test.esp32-c3-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd4x - id: scd40 - co2: - name: SCD4X CO2 - temperature: - id: scd4x_temperature - name: SCD4X Temperature - humidity: - name: SCD4X Humidity - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd4x/test.esp32-c3-idf.yaml b/tests/components/scd4x/test.esp32-c3-idf.yaml index 353293be65..ee2c29ca4e 100644 --- a/tests/components/scd4x/test.esp32-c3-idf.yaml +++ b/tests/components/scd4x/test.esp32-c3-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd4x - id: scd40 - co2: - name: SCD4X CO2 - temperature: - id: scd4x_temperature - name: SCD4X Temperature - humidity: - name: SCD4X Humidity - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd4x/test.esp32-idf.yaml b/tests/components/scd4x/test.esp32-idf.yaml index 02cec921d2..63c3bd6afd 100644 --- a/tests/components/scd4x/test.esp32-idf.yaml +++ b/tests/components/scd4x/test.esp32-idf.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd4x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: scd4x - id: scd40 - co2: - name: SCD4X CO2 - temperature: - id: scd4x_temperature - name: SCD4X Temperature - humidity: - name: SCD4X Humidity - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd4x/test.esp8266-ard.yaml b/tests/components/scd4x/test.esp8266-ard.yaml index 353293be65..ee2c29ca4e 100644 --- a/tests/components/scd4x/test.esp8266-ard.yaml +++ b/tests/components/scd4x/test.esp8266-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd4x - id: scd40 - co2: - name: SCD4X CO2 - temperature: - id: scd4x_temperature - name: SCD4X Temperature - humidity: - name: SCD4X Humidity - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/scd4x/test.rp2040-ard.yaml b/tests/components/scd4x/test.rp2040-ard.yaml index 353293be65..ee2c29ca4e 100644 --- a/tests/components/scd4x/test.rp2040-ard.yaml +++ b/tests/components/scd4x/test.rp2040-ard.yaml @@ -1,20 +1,5 @@ -i2c: - - id: i2c_scd4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: scd4x - id: scd40 - co2: - name: SCD4X CO2 - temperature: - id: scd4x_temperature - name: SCD4X Temperature - humidity: - name: SCD4X Humidity - automatic_self_calibration: true - altitude_compensation: 10m - ambient_pressure_compensation: 961mBar - temperature_offset: 4.2C - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sdm_meter/common.yaml b/tests/components/sdm_meter/common.yaml new file mode 100644 index 0000000000..60c71a796b --- /dev/null +++ b/tests/components/sdm_meter/common.yaml @@ -0,0 +1,23 @@ +uart: + - id: uart_sdm_meter + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: sdm_meter + phase_a: + current: + name: Phase A Current + voltage: + name: Phase A Voltage + active_power: + name: Phase A Power + power_factor: + name: Phase A Power Factor + apparent_power: + name: Phase A Apparent Power + reactive_power: + name: Phase A Reactive Power + phase_angle: + name: Phase A Phase Angle diff --git a/tests/components/sdm_meter/test.esp32-ard.yaml b/tests/components/sdm_meter/test.esp32-ard.yaml index eb3958db19..f486544afa 100644 --- a/tests/components/sdm_meter/test.esp32-ard.yaml +++ b/tests/components/sdm_meter/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sdm_meter - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: sdm_meter - phase_a: - current: - name: Phase A Current - voltage: - name: Phase A Voltage - active_power: - name: Phase A Power - power_factor: - name: Phase A Power Factor - apparent_power: - name: Phase A Apparent Power - reactive_power: - name: Phase A Reactive Power - phase_angle: - name: Phase A Phase Angle +<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp32-c3-ard.yaml b/tests/components/sdm_meter/test.esp32-c3-ard.yaml index 0c2144f983..b516342f3b 100644 --- a/tests/components/sdm_meter/test.esp32-c3-ard.yaml +++ b/tests/components/sdm_meter/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sdm_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sdm_meter - phase_a: - current: - name: Phase A Current - voltage: - name: Phase A Voltage - active_power: - name: Phase A Power - power_factor: - name: Phase A Power Factor - apparent_power: - name: Phase A Apparent Power - reactive_power: - name: Phase A Reactive Power - phase_angle: - name: Phase A Phase Angle +<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp32-c3-idf.yaml b/tests/components/sdm_meter/test.esp32-c3-idf.yaml index 0c2144f983..b516342f3b 100644 --- a/tests/components/sdm_meter/test.esp32-c3-idf.yaml +++ b/tests/components/sdm_meter/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sdm_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sdm_meter - phase_a: - current: - name: Phase A Current - voltage: - name: Phase A Voltage - active_power: - name: Phase A Power - power_factor: - name: Phase A Power Factor - apparent_power: - name: Phase A Apparent Power - reactive_power: - name: Phase A Reactive Power - phase_angle: - name: Phase A Phase Angle +<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp32-idf.yaml b/tests/components/sdm_meter/test.esp32-idf.yaml index eb3958db19..f486544afa 100644 --- a/tests/components/sdm_meter/test.esp32-idf.yaml +++ b/tests/components/sdm_meter/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sdm_meter - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: sdm_meter - phase_a: - current: - name: Phase A Current - voltage: - name: Phase A Voltage - active_power: - name: Phase A Power - power_factor: - name: Phase A Power Factor - apparent_power: - name: Phase A Apparent Power - reactive_power: - name: Phase A Reactive Power - phase_angle: - name: Phase A Phase Angle +<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.esp8266-ard.yaml b/tests/components/sdm_meter/test.esp8266-ard.yaml index 0c2144f983..b516342f3b 100644 --- a/tests/components/sdm_meter/test.esp8266-ard.yaml +++ b/tests/components/sdm_meter/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sdm_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sdm_meter - phase_a: - current: - name: Phase A Current - voltage: - name: Phase A Voltage - active_power: - name: Phase A Power - power_factor: - name: Phase A Power Factor - apparent_power: - name: Phase A Apparent Power - reactive_power: - name: Phase A Reactive Power - phase_angle: - name: Phase A Phase Angle +<<: !include common.yaml diff --git a/tests/components/sdm_meter/test.rp2040-ard.yaml b/tests/components/sdm_meter/test.rp2040-ard.yaml index 0c2144f983..b516342f3b 100644 --- a/tests/components/sdm_meter/test.rp2040-ard.yaml +++ b/tests/components/sdm_meter/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sdm_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sdm_meter - phase_a: - current: - name: Phase A Current - voltage: - name: Phase A Voltage - active_power: - name: Phase A Power - power_factor: - name: Phase A Power Factor - apparent_power: - name: Phase A Apparent Power - reactive_power: - name: Phase A Reactive Power - phase_angle: - name: Phase A Phase Angle +<<: !include common.yaml diff --git a/tests/components/sdp3x/common.yaml b/tests/components/sdp3x/common.yaml new file mode 100644 index 0000000000..d3c5491ca5 --- /dev/null +++ b/tests/components/sdp3x/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_sdp3x + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sdp3x + id: filter_pressure + name: HVAC Filter Pressure drop + accuracy_decimals: 3 + update_interval: 5s diff --git a/tests/components/sdp3x/test.esp32-ard.yaml b/tests/components/sdp3x/test.esp32-ard.yaml index 00666082eb..63c3bd6afd 100644 --- a/tests/components/sdp3x/test.esp32-ard.yaml +++ b/tests/components/sdp3x/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_sdp3x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sdp3x - id: filter_pressure - name: HVAC Filter Pressure drop - accuracy_decimals: 3 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp32-c3-ard.yaml b/tests/components/sdp3x/test.esp32-c3-ard.yaml index 42b90f1b81..ee2c29ca4e 100644 --- a/tests/components/sdp3x/test.esp32-c3-ard.yaml +++ b/tests/components/sdp3x/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_sdp3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sdp3x - id: filter_pressure - name: HVAC Filter Pressure drop - accuracy_decimals: 3 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp32-c3-idf.yaml b/tests/components/sdp3x/test.esp32-c3-idf.yaml index 42b90f1b81..ee2c29ca4e 100644 --- a/tests/components/sdp3x/test.esp32-c3-idf.yaml +++ b/tests/components/sdp3x/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_sdp3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sdp3x - id: filter_pressure - name: HVAC Filter Pressure drop - accuracy_decimals: 3 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp32-idf.yaml b/tests/components/sdp3x/test.esp32-idf.yaml index 00666082eb..63c3bd6afd 100644 --- a/tests/components/sdp3x/test.esp32-idf.yaml +++ b/tests/components/sdp3x/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_sdp3x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sdp3x - id: filter_pressure - name: HVAC Filter Pressure drop - accuracy_decimals: 3 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sdp3x/test.esp8266-ard.yaml b/tests/components/sdp3x/test.esp8266-ard.yaml index 42b90f1b81..ee2c29ca4e 100644 --- a/tests/components/sdp3x/test.esp8266-ard.yaml +++ b/tests/components/sdp3x/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_sdp3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sdp3x - id: filter_pressure - name: HVAC Filter Pressure drop - accuracy_decimals: 3 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sdp3x/test.rp2040-ard.yaml b/tests/components/sdp3x/test.rp2040-ard.yaml index 42b90f1b81..ee2c29ca4e 100644 --- a/tests/components/sdp3x/test.rp2040-ard.yaml +++ b/tests/components/sdp3x/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_sdp3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sdp3x - id: filter_pressure - name: HVAC Filter Pressure drop - accuracy_decimals: 3 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sds011/common.yaml b/tests/components/sds011/common.yaml new file mode 100644 index 0000000000..c7574e1d7d --- /dev/null +++ b/tests/components/sds011/common.yaml @@ -0,0 +1,14 @@ +uart: + - id: uart_sdm_sds011 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 115200 + +sensor: + - platform: sds011 + pm_2_5: + name: SDS011 PM2.5 + pm_10_0: + name: SDS011 PM10.0 + rx_only: false + update_interval: 5min diff --git a/tests/components/sds011/test.esp32-ard.yaml b/tests/components/sds011/test.esp32-ard.yaml index 275390f14c..f486544afa 100644 --- a/tests/components/sds011/test.esp32-ard.yaml +++ b/tests/components/sds011/test.esp32-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_sdm_sds011 - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: sds011 - pm_2_5: - name: SDS011 PM2.5 - pm_10_0: - name: SDS011 PM10.0 - rx_only: false - update_interval: 5min +<<: !include common.yaml diff --git a/tests/components/sds011/test.esp32-c3-ard.yaml b/tests/components/sds011/test.esp32-c3-ard.yaml index e680a62dfe..b516342f3b 100644 --- a/tests/components/sds011/test.esp32-c3-ard.yaml +++ b/tests/components/sds011/test.esp32-c3-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_sdm_sds011 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sds011 - pm_2_5: - name: SDS011 PM2.5 - pm_10_0: - name: SDS011 PM10.0 - rx_only: false - update_interval: 5min +<<: !include common.yaml diff --git a/tests/components/sds011/test.esp32-c3-idf.yaml b/tests/components/sds011/test.esp32-c3-idf.yaml index e680a62dfe..b516342f3b 100644 --- a/tests/components/sds011/test.esp32-c3-idf.yaml +++ b/tests/components/sds011/test.esp32-c3-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_sdm_sds011 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sds011 - pm_2_5: - name: SDS011 PM2.5 - pm_10_0: - name: SDS011 PM10.0 - rx_only: false - update_interval: 5min +<<: !include common.yaml diff --git a/tests/components/sds011/test.esp32-idf.yaml b/tests/components/sds011/test.esp32-idf.yaml index 275390f14c..f486544afa 100644 --- a/tests/components/sds011/test.esp32-idf.yaml +++ b/tests/components/sds011/test.esp32-idf.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_sdm_sds011 - tx_pin: 17 - rx_pin: 16 - baud_rate: 115200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: sds011 - pm_2_5: - name: SDS011 PM2.5 - pm_10_0: - name: SDS011 PM10.0 - rx_only: false - update_interval: 5min +<<: !include common.yaml diff --git a/tests/components/sds011/test.esp8266-ard.yaml b/tests/components/sds011/test.esp8266-ard.yaml index e680a62dfe..b516342f3b 100644 --- a/tests/components/sds011/test.esp8266-ard.yaml +++ b/tests/components/sds011/test.esp8266-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_sdm_sds011 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sds011 - pm_2_5: - name: SDS011 PM2.5 - pm_10_0: - name: SDS011 PM10.0 - rx_only: false - update_interval: 5min +<<: !include common.yaml diff --git a/tests/components/sds011/test.rp2040-ard.yaml b/tests/components/sds011/test.rp2040-ard.yaml index e680a62dfe..b516342f3b 100644 --- a/tests/components/sds011/test.rp2040-ard.yaml +++ b/tests/components/sds011/test.rp2040-ard.yaml @@ -1,14 +1,5 @@ -uart: - - id: uart_sdm_sds011 - tx_pin: 4 - rx_pin: 5 - baud_rate: 115200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sds011 - pm_2_5: - name: SDS011 PM2.5 - pm_10_0: - name: SDS011 PM10.0 - rx_only: false - update_interval: 5min +<<: !include common.yaml diff --git a/tests/components/selec_meter/common.yaml b/tests/components/selec_meter/common.yaml new file mode 100644 index 0000000000..f2714ce828 --- /dev/null +++ b/tests/components/selec_meter/common.yaml @@ -0,0 +1,45 @@ +uart: + - id: uart_selec_meter + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: selec_meter + total_active_energy: + name: SelecEM2M Total Active Energy + import_active_energy: + name: SelecEM2M Import Active Energy + export_active_energy: + name: SelecEM2M Export Active Energy + total_reactive_energy: + name: SelecEM2M Total Reactive Energy + import_reactive_energy: + name: SelecEM2M Import Reactive Energy + export_reactive_energy: + name: SelecEM2M Export Reactive Energy + apparent_energy: + name: SelecEM2M Apparent Energy + active_power: + name: SelecEM2M Active Power + reactive_power: + name: SelecEM2M Reactive Power + apparent_power: + name: SelecEM2M Apparent Power + voltage: + name: SelecEM2M Voltage + current: + name: SelecEM2M Current + power_factor: + name: SelecEM2M Power Factor + frequency: + name: SelecEM2M Frequency + maximum_demand_active_power: + name: SelecEM2M Maximum Demand Active Power + disabled_by_default: true + maximum_demand_reactive_power: + name: SelecEM2M Maximum Demand Reactive Power + disabled_by_default: true + maximum_demand_apparent_power: + name: SelecEM2M Maximum Demand Apparent Power + disabled_by_default: true diff --git a/tests/components/selec_meter/test.esp32-ard.yaml b/tests/components/selec_meter/test.esp32-ard.yaml index 648adc1757..f486544afa 100644 --- a/tests/components/selec_meter/test.esp32-ard.yaml +++ b/tests/components/selec_meter/test.esp32-ard.yaml @@ -1,45 +1,5 @@ -uart: - - id: uart_selec_meter - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: selec_meter - total_active_energy: - name: SelecEM2M Total Active Energy - import_active_energy: - name: SelecEM2M Import Active Energy - export_active_energy: - name: SelecEM2M Export Active Energy - total_reactive_energy: - name: SelecEM2M Total Reactive Energy - import_reactive_energy: - name: SelecEM2M Import Reactive Energy - export_reactive_energy: - name: SelecEM2M Export Reactive Energy - apparent_energy: - name: SelecEM2M Apparent Energy - active_power: - name: SelecEM2M Active Power - reactive_power: - name: SelecEM2M Reactive Power - apparent_power: - name: SelecEM2M Apparent Power - voltage: - name: SelecEM2M Voltage - current: - name: SelecEM2M Current - power_factor: - name: SelecEM2M Power Factor - frequency: - name: SelecEM2M Frequency - maximum_demand_active_power: - name: SelecEM2M Maximum Demand Active Power - disabled_by_default: true - maximum_demand_reactive_power: - name: SelecEM2M Maximum Demand Reactive Power - disabled_by_default: true - maximum_demand_apparent_power: - name: SelecEM2M Maximum Demand Apparent Power - disabled_by_default: true +<<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp32-c3-ard.yaml b/tests/components/selec_meter/test.esp32-c3-ard.yaml index 5f6e69f96f..b516342f3b 100644 --- a/tests/components/selec_meter/test.esp32-c3-ard.yaml +++ b/tests/components/selec_meter/test.esp32-c3-ard.yaml @@ -1,45 +1,5 @@ -uart: - - id: uart_selec_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: selec_meter - total_active_energy: - name: SelecEM2M Total Active Energy - import_active_energy: - name: SelecEM2M Import Active Energy - export_active_energy: - name: SelecEM2M Export Active Energy - total_reactive_energy: - name: SelecEM2M Total Reactive Energy - import_reactive_energy: - name: SelecEM2M Import Reactive Energy - export_reactive_energy: - name: SelecEM2M Export Reactive Energy - apparent_energy: - name: SelecEM2M Apparent Energy - active_power: - name: SelecEM2M Active Power - reactive_power: - name: SelecEM2M Reactive Power - apparent_power: - name: SelecEM2M Apparent Power - voltage: - name: SelecEM2M Voltage - current: - name: SelecEM2M Current - power_factor: - name: SelecEM2M Power Factor - frequency: - name: SelecEM2M Frequency - maximum_demand_active_power: - name: SelecEM2M Maximum Demand Active Power - disabled_by_default: true - maximum_demand_reactive_power: - name: SelecEM2M Maximum Demand Reactive Power - disabled_by_default: true - maximum_demand_apparent_power: - name: SelecEM2M Maximum Demand Apparent Power - disabled_by_default: true +<<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp32-c3-idf.yaml b/tests/components/selec_meter/test.esp32-c3-idf.yaml index 5f6e69f96f..b516342f3b 100644 --- a/tests/components/selec_meter/test.esp32-c3-idf.yaml +++ b/tests/components/selec_meter/test.esp32-c3-idf.yaml @@ -1,45 +1,5 @@ -uart: - - id: uart_selec_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: selec_meter - total_active_energy: - name: SelecEM2M Total Active Energy - import_active_energy: - name: SelecEM2M Import Active Energy - export_active_energy: - name: SelecEM2M Export Active Energy - total_reactive_energy: - name: SelecEM2M Total Reactive Energy - import_reactive_energy: - name: SelecEM2M Import Reactive Energy - export_reactive_energy: - name: SelecEM2M Export Reactive Energy - apparent_energy: - name: SelecEM2M Apparent Energy - active_power: - name: SelecEM2M Active Power - reactive_power: - name: SelecEM2M Reactive Power - apparent_power: - name: SelecEM2M Apparent Power - voltage: - name: SelecEM2M Voltage - current: - name: SelecEM2M Current - power_factor: - name: SelecEM2M Power Factor - frequency: - name: SelecEM2M Frequency - maximum_demand_active_power: - name: SelecEM2M Maximum Demand Active Power - disabled_by_default: true - maximum_demand_reactive_power: - name: SelecEM2M Maximum Demand Reactive Power - disabled_by_default: true - maximum_demand_apparent_power: - name: SelecEM2M Maximum Demand Apparent Power - disabled_by_default: true +<<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp32-idf.yaml b/tests/components/selec_meter/test.esp32-idf.yaml index 648adc1757..f486544afa 100644 --- a/tests/components/selec_meter/test.esp32-idf.yaml +++ b/tests/components/selec_meter/test.esp32-idf.yaml @@ -1,45 +1,5 @@ -uart: - - id: uart_selec_meter - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: selec_meter - total_active_energy: - name: SelecEM2M Total Active Energy - import_active_energy: - name: SelecEM2M Import Active Energy - export_active_energy: - name: SelecEM2M Export Active Energy - total_reactive_energy: - name: SelecEM2M Total Reactive Energy - import_reactive_energy: - name: SelecEM2M Import Reactive Energy - export_reactive_energy: - name: SelecEM2M Export Reactive Energy - apparent_energy: - name: SelecEM2M Apparent Energy - active_power: - name: SelecEM2M Active Power - reactive_power: - name: SelecEM2M Reactive Power - apparent_power: - name: SelecEM2M Apparent Power - voltage: - name: SelecEM2M Voltage - current: - name: SelecEM2M Current - power_factor: - name: SelecEM2M Power Factor - frequency: - name: SelecEM2M Frequency - maximum_demand_active_power: - name: SelecEM2M Maximum Demand Active Power - disabled_by_default: true - maximum_demand_reactive_power: - name: SelecEM2M Maximum Demand Reactive Power - disabled_by_default: true - maximum_demand_apparent_power: - name: SelecEM2M Maximum Demand Apparent Power - disabled_by_default: true +<<: !include common.yaml diff --git a/tests/components/selec_meter/test.esp8266-ard.yaml b/tests/components/selec_meter/test.esp8266-ard.yaml index 5f6e69f96f..b516342f3b 100644 --- a/tests/components/selec_meter/test.esp8266-ard.yaml +++ b/tests/components/selec_meter/test.esp8266-ard.yaml @@ -1,45 +1,5 @@ -uart: - - id: uart_selec_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: selec_meter - total_active_energy: - name: SelecEM2M Total Active Energy - import_active_energy: - name: SelecEM2M Import Active Energy - export_active_energy: - name: SelecEM2M Export Active Energy - total_reactive_energy: - name: SelecEM2M Total Reactive Energy - import_reactive_energy: - name: SelecEM2M Import Reactive Energy - export_reactive_energy: - name: SelecEM2M Export Reactive Energy - apparent_energy: - name: SelecEM2M Apparent Energy - active_power: - name: SelecEM2M Active Power - reactive_power: - name: SelecEM2M Reactive Power - apparent_power: - name: SelecEM2M Apparent Power - voltage: - name: SelecEM2M Voltage - current: - name: SelecEM2M Current - power_factor: - name: SelecEM2M Power Factor - frequency: - name: SelecEM2M Frequency - maximum_demand_active_power: - name: SelecEM2M Maximum Demand Active Power - disabled_by_default: true - maximum_demand_reactive_power: - name: SelecEM2M Maximum Demand Reactive Power - disabled_by_default: true - maximum_demand_apparent_power: - name: SelecEM2M Maximum Demand Apparent Power - disabled_by_default: true +<<: !include common.yaml diff --git a/tests/components/selec_meter/test.rp2040-ard.yaml b/tests/components/selec_meter/test.rp2040-ard.yaml index 5f6e69f96f..b516342f3b 100644 --- a/tests/components/selec_meter/test.rp2040-ard.yaml +++ b/tests/components/selec_meter/test.rp2040-ard.yaml @@ -1,45 +1,5 @@ -uart: - - id: uart_selec_meter - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: selec_meter - total_active_energy: - name: SelecEM2M Total Active Energy - import_active_energy: - name: SelecEM2M Import Active Energy - export_active_energy: - name: SelecEM2M Export Active Energy - total_reactive_energy: - name: SelecEM2M Total Reactive Energy - import_reactive_energy: - name: SelecEM2M Import Reactive Energy - export_reactive_energy: - name: SelecEM2M Export Reactive Energy - apparent_energy: - name: SelecEM2M Apparent Energy - active_power: - name: SelecEM2M Active Power - reactive_power: - name: SelecEM2M Reactive Power - apparent_power: - name: SelecEM2M Apparent Power - voltage: - name: SelecEM2M Voltage - current: - name: SelecEM2M Current - power_factor: - name: SelecEM2M Power Factor - frequency: - name: SelecEM2M Frequency - maximum_demand_active_power: - name: SelecEM2M Maximum Demand Active Power - disabled_by_default: true - maximum_demand_reactive_power: - name: SelecEM2M Maximum Demand Reactive Power - disabled_by_default: true - maximum_demand_apparent_power: - name: SelecEM2M Maximum Demand Apparent Power - disabled_by_default: true +<<: !include common.yaml diff --git a/tests/components/sen0321/common.yaml b/tests/components/sen0321/common.yaml new file mode 100644 index 0000000000..8b9fdff4a1 --- /dev/null +++ b/tests/components/sen0321/common.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_sen0321 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sen0321 + name: Workshop Ozone Sensor + id: sen0321_ozone + update_interval: 10s diff --git a/tests/components/sen0321/test.esp32-ard.yaml b/tests/components/sen0321/test.esp32-ard.yaml index 44f76bf5e6..63c3bd6afd 100644 --- a/tests/components/sen0321/test.esp32-ard.yaml +++ b/tests/components/sen0321/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sen0321 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sen0321 - name: Workshop Ozone Sensor - id: sen0321_ozone - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sen0321/test.esp32-c3-ard.yaml b/tests/components/sen0321/test.esp32-c3-ard.yaml index 7fa461a7fa..ee2c29ca4e 100644 --- a/tests/components/sen0321/test.esp32-c3-ard.yaml +++ b/tests/components/sen0321/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sen0321 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen0321 - name: Workshop Ozone Sensor - id: sen0321_ozone - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sen0321/test.esp32-c3-idf.yaml b/tests/components/sen0321/test.esp32-c3-idf.yaml index 7fa461a7fa..ee2c29ca4e 100644 --- a/tests/components/sen0321/test.esp32-c3-idf.yaml +++ b/tests/components/sen0321/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sen0321 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen0321 - name: Workshop Ozone Sensor - id: sen0321_ozone - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sen0321/test.esp32-idf.yaml b/tests/components/sen0321/test.esp32-idf.yaml index 44f76bf5e6..63c3bd6afd 100644 --- a/tests/components/sen0321/test.esp32-idf.yaml +++ b/tests/components/sen0321/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sen0321 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sen0321 - name: Workshop Ozone Sensor - id: sen0321_ozone - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sen0321/test.esp8266-ard.yaml b/tests/components/sen0321/test.esp8266-ard.yaml index 7fa461a7fa..ee2c29ca4e 100644 --- a/tests/components/sen0321/test.esp8266-ard.yaml +++ b/tests/components/sen0321/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sen0321 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen0321 - name: Workshop Ozone Sensor - id: sen0321_ozone - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sen0321/test.rp2040-ard.yaml b/tests/components/sen0321/test.rp2040-ard.yaml index 7fa461a7fa..ee2c29ca4e 100644 --- a/tests/components/sen0321/test.rp2040-ard.yaml +++ b/tests/components/sen0321/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sen0321 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen0321 - name: Workshop Ozone Sensor - id: sen0321_ozone - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sen21231/common.yaml b/tests/components/sen21231/common.yaml new file mode 100644 index 0000000000..6fa1d04aa2 --- /dev/null +++ b/tests/components/sen21231/common.yaml @@ -0,0 +1,9 @@ +i2c: + - id: i2c_sen21231 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sen21231 + id: sen21231_sensor1 + name: Person Sensor diff --git a/tests/components/sen21231/test.esp32-ard.yaml b/tests/components/sen21231/test.esp32-ard.yaml index 3173683f17..63c3bd6afd 100644 --- a/tests/components/sen21231/test.esp32-ard.yaml +++ b/tests/components/sen21231/test.esp32-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_sen21231 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sen21231 - id: sen21231_sensor1 - name: Person Sensor +<<: !include common.yaml diff --git a/tests/components/sen21231/test.esp32-c3-ard.yaml b/tests/components/sen21231/test.esp32-c3-ard.yaml index efd7843f56..ee2c29ca4e 100644 --- a/tests/components/sen21231/test.esp32-c3-ard.yaml +++ b/tests/components/sen21231/test.esp32-c3-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_sen21231 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen21231 - id: sen21231_sensor1 - name: Person Sensor +<<: !include common.yaml diff --git a/tests/components/sen21231/test.esp32-c3-idf.yaml b/tests/components/sen21231/test.esp32-c3-idf.yaml index efd7843f56..ee2c29ca4e 100644 --- a/tests/components/sen21231/test.esp32-c3-idf.yaml +++ b/tests/components/sen21231/test.esp32-c3-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_sen21231 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen21231 - id: sen21231_sensor1 - name: Person Sensor +<<: !include common.yaml diff --git a/tests/components/sen21231/test.esp32-idf.yaml b/tests/components/sen21231/test.esp32-idf.yaml index 3173683f17..63c3bd6afd 100644 --- a/tests/components/sen21231/test.esp32-idf.yaml +++ b/tests/components/sen21231/test.esp32-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_sen21231 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sen21231 - id: sen21231_sensor1 - name: Person Sensor +<<: !include common.yaml diff --git a/tests/components/sen21231/test.esp8266-ard.yaml b/tests/components/sen21231/test.esp8266-ard.yaml index efd7843f56..ee2c29ca4e 100644 --- a/tests/components/sen21231/test.esp8266-ard.yaml +++ b/tests/components/sen21231/test.esp8266-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_sen21231 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen21231 - id: sen21231_sensor1 - name: Person Sensor +<<: !include common.yaml diff --git a/tests/components/sen21231/test.rp2040-ard.yaml b/tests/components/sen21231/test.rp2040-ard.yaml index efd7843f56..ee2c29ca4e 100644 --- a/tests/components/sen21231/test.rp2040-ard.yaml +++ b/tests/components/sen21231/test.rp2040-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_sen21231 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen21231 - id: sen21231_sensor1 - name: Person Sensor +<<: !include common.yaml diff --git a/tests/components/sen5x/common.yaml b/tests/components/sen5x/common.yaml new file mode 100644 index 0000000000..9adf268048 --- /dev/null +++ b/tests/components/sen5x/common.yaml @@ -0,0 +1,49 @@ +i2c: + - id: i2c_sen5x + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sen5x + id: sen54 + temperature: + name: Temperature + accuracy_decimals: 1 + humidity: + name: Humidity + accuracy_decimals: 0 + pm_1_0: + name: PM <1µm Weight concentration + id: pm_1_0 + accuracy_decimals: 1 + pm_2_5: + name: PM <2.5µm Weight concentration + id: pm_2_5 + accuracy_decimals: 1 + pm_4_0: + name: PM <4µm Weight concentration + id: pm_4_0 + accuracy_decimals: 1 + pm_10_0: + name: PM <10µm Weight concentration + id: pm_10_0 + accuracy_decimals: 1 + nox: + name: NOx + voc: + name: VOC + algorithm_tuning: + index_offset: 100 + learning_time_offset_hours: 12 + learning_time_gain_hours: 12 + gating_max_duration_minutes: 180 + std_initial: 50 + gain_factor: 230 + temperature_compensation: + offset: 0 + normalized_offset_slope: 0 + time_constant: 0 + auto_cleaning_interval: 604800s + acceleration_mode: low + store_baseline: true + address: 0x69 diff --git a/tests/components/sen5x/test.esp32-ard.yaml b/tests/components/sen5x/test.esp32-ard.yaml index b8f89c435f..63c3bd6afd 100644 --- a/tests/components/sen5x/test.esp32-ard.yaml +++ b/tests/components/sen5x/test.esp32-ard.yaml @@ -1,49 +1,5 @@ -i2c: - - id: i2c_sen5x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sen5x - id: sen54 - temperature: - name: Temperature - accuracy_decimals: 1 - humidity: - name: Humidity - accuracy_decimals: 0 - pm_1_0: - name: PM <1µm Weight concentration - id: pm_1_0 - accuracy_decimals: 1 - pm_2_5: - name: PM <2.5µm Weight concentration - id: pm_2_5 - accuracy_decimals: 1 - pm_4_0: - name: PM <4µm Weight concentration - id: pm_4_0 - accuracy_decimals: 1 - pm_10_0: - name: PM <10µm Weight concentration - id: pm_10_0 - accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - temperature_compensation: - offset: 0 - normalized_offset_slope: 0 - time_constant: 0 - auto_cleaning_interval: 604800s - acceleration_mode: low - store_baseline: true - address: 0x69 +<<: !include common.yaml diff --git a/tests/components/sen5x/test.esp32-c3-ard.yaml b/tests/components/sen5x/test.esp32-c3-ard.yaml index 3352a59b17..ee2c29ca4e 100644 --- a/tests/components/sen5x/test.esp32-c3-ard.yaml +++ b/tests/components/sen5x/test.esp32-c3-ard.yaml @@ -1,49 +1,5 @@ -i2c: - - id: i2c_sen5x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen5x - id: sen54 - temperature: - name: Temperature - accuracy_decimals: 1 - humidity: - name: Humidity - accuracy_decimals: 0 - pm_1_0: - name: PM <1µm Weight concentration - id: pm_1_0 - accuracy_decimals: 1 - pm_2_5: - name: PM <2.5µm Weight concentration - id: pm_2_5 - accuracy_decimals: 1 - pm_4_0: - name: PM <4µm Weight concentration - id: pm_4_0 - accuracy_decimals: 1 - pm_10_0: - name: PM <10µm Weight concentration - id: pm_10_0 - accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - temperature_compensation: - offset: 0 - normalized_offset_slope: 0 - time_constant: 0 - auto_cleaning_interval: 604800s - acceleration_mode: low - store_baseline: true - address: 0x69 +<<: !include common.yaml diff --git a/tests/components/sen5x/test.esp32-c3-idf.yaml b/tests/components/sen5x/test.esp32-c3-idf.yaml index 3352a59b17..ee2c29ca4e 100644 --- a/tests/components/sen5x/test.esp32-c3-idf.yaml +++ b/tests/components/sen5x/test.esp32-c3-idf.yaml @@ -1,49 +1,5 @@ -i2c: - - id: i2c_sen5x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen5x - id: sen54 - temperature: - name: Temperature - accuracy_decimals: 1 - humidity: - name: Humidity - accuracy_decimals: 0 - pm_1_0: - name: PM <1µm Weight concentration - id: pm_1_0 - accuracy_decimals: 1 - pm_2_5: - name: PM <2.5µm Weight concentration - id: pm_2_5 - accuracy_decimals: 1 - pm_4_0: - name: PM <4µm Weight concentration - id: pm_4_0 - accuracy_decimals: 1 - pm_10_0: - name: PM <10µm Weight concentration - id: pm_10_0 - accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - temperature_compensation: - offset: 0 - normalized_offset_slope: 0 - time_constant: 0 - auto_cleaning_interval: 604800s - acceleration_mode: low - store_baseline: true - address: 0x69 +<<: !include common.yaml diff --git a/tests/components/sen5x/test.esp32-idf.yaml b/tests/components/sen5x/test.esp32-idf.yaml index b8f89c435f..63c3bd6afd 100644 --- a/tests/components/sen5x/test.esp32-idf.yaml +++ b/tests/components/sen5x/test.esp32-idf.yaml @@ -1,49 +1,5 @@ -i2c: - - id: i2c_sen5x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sen5x - id: sen54 - temperature: - name: Temperature - accuracy_decimals: 1 - humidity: - name: Humidity - accuracy_decimals: 0 - pm_1_0: - name: PM <1µm Weight concentration - id: pm_1_0 - accuracy_decimals: 1 - pm_2_5: - name: PM <2.5µm Weight concentration - id: pm_2_5 - accuracy_decimals: 1 - pm_4_0: - name: PM <4µm Weight concentration - id: pm_4_0 - accuracy_decimals: 1 - pm_10_0: - name: PM <10µm Weight concentration - id: pm_10_0 - accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - temperature_compensation: - offset: 0 - normalized_offset_slope: 0 - time_constant: 0 - auto_cleaning_interval: 604800s - acceleration_mode: low - store_baseline: true - address: 0x69 +<<: !include common.yaml diff --git a/tests/components/sen5x/test.esp8266-ard.yaml b/tests/components/sen5x/test.esp8266-ard.yaml index 3352a59b17..ee2c29ca4e 100644 --- a/tests/components/sen5x/test.esp8266-ard.yaml +++ b/tests/components/sen5x/test.esp8266-ard.yaml @@ -1,49 +1,5 @@ -i2c: - - id: i2c_sen5x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen5x - id: sen54 - temperature: - name: Temperature - accuracy_decimals: 1 - humidity: - name: Humidity - accuracy_decimals: 0 - pm_1_0: - name: PM <1µm Weight concentration - id: pm_1_0 - accuracy_decimals: 1 - pm_2_5: - name: PM <2.5µm Weight concentration - id: pm_2_5 - accuracy_decimals: 1 - pm_4_0: - name: PM <4µm Weight concentration - id: pm_4_0 - accuracy_decimals: 1 - pm_10_0: - name: PM <10µm Weight concentration - id: pm_10_0 - accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - temperature_compensation: - offset: 0 - normalized_offset_slope: 0 - time_constant: 0 - auto_cleaning_interval: 604800s - acceleration_mode: low - store_baseline: true - address: 0x69 +<<: !include common.yaml diff --git a/tests/components/sen5x/test.rp2040-ard.yaml b/tests/components/sen5x/test.rp2040-ard.yaml index 3352a59b17..ee2c29ca4e 100644 --- a/tests/components/sen5x/test.rp2040-ard.yaml +++ b/tests/components/sen5x/test.rp2040-ard.yaml @@ -1,49 +1,5 @@ -i2c: - - id: i2c_sen5x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sen5x - id: sen54 - temperature: - name: Temperature - accuracy_decimals: 1 - humidity: - name: Humidity - accuracy_decimals: 0 - pm_1_0: - name: PM <1µm Weight concentration - id: pm_1_0 - accuracy_decimals: 1 - pm_2_5: - name: PM <2.5µm Weight concentration - id: pm_2_5 - accuracy_decimals: 1 - pm_4_0: - name: PM <4µm Weight concentration - id: pm_4_0 - accuracy_decimals: 1 - pm_10_0: - name: PM <10µm Weight concentration - id: pm_10_0 - accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - temperature_compensation: - offset: 0 - normalized_offset_slope: 0 - time_constant: 0 - auto_cleaning_interval: 604800s - acceleration_mode: low - store_baseline: true - address: 0x69 +<<: !include common.yaml diff --git a/tests/components/senseair/common.yaml b/tests/components/senseair/common.yaml new file mode 100644 index 0000000000..23a933affe --- /dev/null +++ b/tests/components/senseair/common.yaml @@ -0,0 +1,19 @@ +uart: + - id: uart_senseair + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: senseair + id: senseair0 + co2: + name: SenseAir CO2 Value + on_value: + then: + - senseair.background_calibration: senseair0 + - senseair.background_calibration_result: senseair0 + - senseair.abc_get_period: senseair0 + - senseair.abc_enable: senseair0 + - senseair.abc_disable: senseair0 + update_interval: 15s diff --git a/tests/components/senseair/test.esp32-ard.yaml b/tests/components/senseair/test.esp32-ard.yaml index daa4645f59..f486544afa 100644 --- a/tests/components/senseair/test.esp32-ard.yaml +++ b/tests/components/senseair/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_senseair - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: senseair - id: senseair0 - co2: - name: SenseAir CO2 Value - on_value: - then: - - senseair.background_calibration: senseair0 - - senseair.background_calibration_result: senseair0 - - senseair.abc_get_period: senseair0 - - senseair.abc_enable: senseair0 - - senseair.abc_disable: senseair0 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/senseair/test.esp32-c3-ard.yaml b/tests/components/senseair/test.esp32-c3-ard.yaml index 41a441f496..b516342f3b 100644 --- a/tests/components/senseair/test.esp32-c3-ard.yaml +++ b/tests/components/senseair/test.esp32-c3-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_senseair - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: senseair - id: senseair0 - co2: - name: SenseAir CO2 Value - on_value: - then: - - senseair.background_calibration: senseair0 - - senseair.background_calibration_result: senseair0 - - senseair.abc_get_period: senseair0 - - senseair.abc_enable: senseair0 - - senseair.abc_disable: senseair0 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/senseair/test.esp32-c3-idf.yaml b/tests/components/senseair/test.esp32-c3-idf.yaml index 41a441f496..b516342f3b 100644 --- a/tests/components/senseair/test.esp32-c3-idf.yaml +++ b/tests/components/senseair/test.esp32-c3-idf.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_senseair - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: senseair - id: senseair0 - co2: - name: SenseAir CO2 Value - on_value: - then: - - senseair.background_calibration: senseair0 - - senseair.background_calibration_result: senseair0 - - senseair.abc_get_period: senseair0 - - senseair.abc_enable: senseair0 - - senseair.abc_disable: senseair0 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/senseair/test.esp32-idf.yaml b/tests/components/senseair/test.esp32-idf.yaml index daa4645f59..f486544afa 100644 --- a/tests/components/senseair/test.esp32-idf.yaml +++ b/tests/components/senseair/test.esp32-idf.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_senseair - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: senseair - id: senseair0 - co2: - name: SenseAir CO2 Value - on_value: - then: - - senseair.background_calibration: senseair0 - - senseair.background_calibration_result: senseair0 - - senseair.abc_get_period: senseair0 - - senseair.abc_enable: senseair0 - - senseair.abc_disable: senseair0 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/senseair/test.esp8266-ard.yaml b/tests/components/senseair/test.esp8266-ard.yaml index 41a441f496..b516342f3b 100644 --- a/tests/components/senseair/test.esp8266-ard.yaml +++ b/tests/components/senseair/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_senseair - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: senseair - id: senseair0 - co2: - name: SenseAir CO2 Value - on_value: - then: - - senseair.background_calibration: senseair0 - - senseair.background_calibration_result: senseair0 - - senseair.abc_get_period: senseair0 - - senseair.abc_enable: senseair0 - - senseair.abc_disable: senseair0 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/senseair/test.rp2040-ard.yaml b/tests/components/senseair/test.rp2040-ard.yaml index 41a441f496..b516342f3b 100644 --- a/tests/components/senseair/test.rp2040-ard.yaml +++ b/tests/components/senseair/test.rp2040-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_senseair - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: senseair - id: senseair0 - co2: - name: SenseAir CO2 Value - on_value: - then: - - senseair.background_calibration: senseair0 - - senseair.background_calibration_result: senseair0 - - senseair.abc_get_period: senseair0 - - senseair.abc_enable: senseair0 - - senseair.abc_disable: senseair0 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/servo/common.yaml b/tests/components/servo/common.yaml new file mode 100644 index 0000000000..2cfa370c32 --- /dev/null +++ b/tests/components/servo/common.yaml @@ -0,0 +1,19 @@ +esphome: + on_boot: + then: + - servo.write: + id: test_servo + level: -100.0% + - servo.detach: test_servo + +output: + - platform: ${output_platform} + id: servo_output_1 + pin: ${pin} + +servo: + id: test_servo + output: servo_output_1 + restore: true + min_level: 4% + max_level: 8% diff --git a/tests/components/servo/test.esp32-ard.yaml b/tests/components/servo/test.esp32-ard.yaml index e769f055b4..26da1ce1d6 100644 --- a/tests/components/servo/test.esp32-ard.yaml +++ b/tests/components/servo/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -esphome: - on_boot: - then: - - servo.write: - id: test_servo - level: -100.0% - - servo.detach: test_servo +substitutions: + output_platform: ledc + pin: GPIO14 -output: - - platform: ledc - id: servo_output_1 - pin: 12 - -servo: - id: test_servo - output: servo_output_1 - restore: true - min_level: 4% - max_level: 8% +<<: !include common.yaml diff --git a/tests/components/servo/test.esp32-c3-ard.yaml b/tests/components/servo/test.esp32-c3-ard.yaml index 29ebea3359..7476963591 100644 --- a/tests/components/servo/test.esp32-c3-ard.yaml +++ b/tests/components/servo/test.esp32-c3-ard.yaml @@ -1,19 +1,5 @@ -esphome: - on_boot: - then: - - servo.write: - id: test_servo - level: -100.0% - - servo.detach: test_servo +substitutions: + output_platform: ledc + pin: GPIO4 -output: - - platform: ledc - id: servo_output_1 - pin: 1 - -servo: - id: test_servo - output: servo_output_1 - restore: true - min_level: 4% - max_level: 8% +<<: !include common.yaml diff --git a/tests/components/servo/test.esp32-c3-idf.yaml b/tests/components/servo/test.esp32-c3-idf.yaml index 29ebea3359..7476963591 100644 --- a/tests/components/servo/test.esp32-c3-idf.yaml +++ b/tests/components/servo/test.esp32-c3-idf.yaml @@ -1,19 +1,5 @@ -esphome: - on_boot: - then: - - servo.write: - id: test_servo - level: -100.0% - - servo.detach: test_servo +substitutions: + output_platform: ledc + pin: GPIO4 -output: - - platform: ledc - id: servo_output_1 - pin: 1 - -servo: - id: test_servo - output: servo_output_1 - restore: true - min_level: 4% - max_level: 8% +<<: !include common.yaml diff --git a/tests/components/servo/test.esp32-idf.yaml b/tests/components/servo/test.esp32-idf.yaml index e769f055b4..26da1ce1d6 100644 --- a/tests/components/servo/test.esp32-idf.yaml +++ b/tests/components/servo/test.esp32-idf.yaml @@ -1,19 +1,5 @@ -esphome: - on_boot: - then: - - servo.write: - id: test_servo - level: -100.0% - - servo.detach: test_servo +substitutions: + output_platform: ledc + pin: GPIO14 -output: - - platform: ledc - id: servo_output_1 - pin: 12 - -servo: - id: test_servo - output: servo_output_1 - restore: true - min_level: 4% - max_level: 8% +<<: !include common.yaml diff --git a/tests/components/servo/test.esp8266-ard.yaml b/tests/components/servo/test.esp8266-ard.yaml index 48b4421641..23a7f43b42 100644 --- a/tests/components/servo/test.esp8266-ard.yaml +++ b/tests/components/servo/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -esphome: - on_boot: - then: - - servo.write: - id: test_servo - level: -100.0% - - servo.detach: test_servo +substitutions: + output_platform: esp8266_pwm + pin: GPIO14 -output: - - platform: esp8266_pwm - id: servo_output_1 - pin: 12 - -servo: - id: test_servo - output: servo_output_1 - restore: true - min_level: 4% - max_level: 8% +<<: !include common.yaml diff --git a/tests/components/servo/test.rp2040-ard.yaml b/tests/components/servo/test.rp2040-ard.yaml index 75efa9407e..d70f7c74ec 100644 --- a/tests/components/servo/test.rp2040-ard.yaml +++ b/tests/components/servo/test.rp2040-ard.yaml @@ -1,19 +1,5 @@ -esphome: - on_boot: - then: - - servo.write: - id: test_servo - level: -100.0% - - servo.detach: test_servo +substitutions: + output_platform: rp2040_pwm + pin: GPIO4 -output: - - platform: rp2040_pwm - id: servo_output_1 - pin: 12 - -servo: - id: test_servo - output: servo_output_1 - restore: true - min_level: 4% - max_level: 8% +<<: !include common.yaml diff --git a/tests/components/sfa30/common.yaml b/tests/components/sfa30/common.yaml new file mode 100644 index 0000000000..e3b38aa7fb --- /dev/null +++ b/tests/components/sfa30/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_sfa30 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sfa30 + formaldehyde: + name: SFA30 formaldehyde + temperature: + name: SFA30 temperature + humidity: + name: SFA30 humidity + address: 0x5D + update_interval: 30s diff --git a/tests/components/sfa30/test.esp32-ard.yaml b/tests/components/sfa30/test.esp32-ard.yaml index dc7e4988e5..63c3bd6afd 100644 --- a/tests/components/sfa30/test.esp32-ard.yaml +++ b/tests/components/sfa30/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sfa30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sfa30 - formaldehyde: - name: "SFA30 formaldehyde" - temperature: - name: "SFA30 temperature" - humidity: - name: "SFA30 humidity" - address: 0x5D - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/sfa30/test.esp32-c3-ard.yaml b/tests/components/sfa30/test.esp32-c3-ard.yaml index 119059e4e2..ee2c29ca4e 100644 --- a/tests/components/sfa30/test.esp32-c3-ard.yaml +++ b/tests/components/sfa30/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sfa30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sfa30 - formaldehyde: - name: "SFA30 formaldehyde" - temperature: - name: "SFA30 temperature" - humidity: - name: "SFA30 humidity" - address: 0x5D - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/sfa30/test.esp32-c3-idf.yaml b/tests/components/sfa30/test.esp32-c3-idf.yaml index 119059e4e2..ee2c29ca4e 100644 --- a/tests/components/sfa30/test.esp32-c3-idf.yaml +++ b/tests/components/sfa30/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sfa30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sfa30 - formaldehyde: - name: "SFA30 formaldehyde" - temperature: - name: "SFA30 temperature" - humidity: - name: "SFA30 humidity" - address: 0x5D - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/sfa30/test.esp32-idf.yaml b/tests/components/sfa30/test.esp32-idf.yaml index dc7e4988e5..63c3bd6afd 100644 --- a/tests/components/sfa30/test.esp32-idf.yaml +++ b/tests/components/sfa30/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sfa30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sfa30 - formaldehyde: - name: "SFA30 formaldehyde" - temperature: - name: "SFA30 temperature" - humidity: - name: "SFA30 humidity" - address: 0x5D - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/sfa30/test.esp8266-ard.yaml b/tests/components/sfa30/test.esp8266-ard.yaml index 119059e4e2..ee2c29ca4e 100644 --- a/tests/components/sfa30/test.esp8266-ard.yaml +++ b/tests/components/sfa30/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sfa30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sfa30 - formaldehyde: - name: "SFA30 formaldehyde" - temperature: - name: "SFA30 temperature" - humidity: - name: "SFA30 humidity" - address: 0x5D - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/sfa30/test.rp2040-ard.yaml b/tests/components/sfa30/test.rp2040-ard.yaml index 119059e4e2..ee2c29ca4e 100644 --- a/tests/components/sfa30/test.rp2040-ard.yaml +++ b/tests/components/sfa30/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sfa30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sfa30 - formaldehyde: - name: "SFA30 formaldehyde" - temperature: - name: "SFA30 temperature" - humidity: - name: "SFA30 humidity" - address: 0x5D - update_interval: 30s +<<: !include common.yaml diff --git a/tests/components/sgp30/common.yaml b/tests/components/sgp30/common.yaml new file mode 100644 index 0000000000..1db5bc67d1 --- /dev/null +++ b/tests/components/sgp30/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_sgp30 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sgp30 + eco2: + name: Workshop eCO2 + accuracy_decimals: 1 + tvoc: + name: Workshop TVOC + accuracy_decimals: 1 + address: 0x58 + update_interval: 5s diff --git a/tests/components/sgp30/test.esp32-ard.yaml b/tests/components/sgp30/test.esp32-ard.yaml index 6ea23c25cd..63c3bd6afd 100644 --- a/tests/components/sgp30/test.esp32-ard.yaml +++ b/tests/components/sgp30/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sgp30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sgp30 - eco2: - name: Workshop eCO2 - accuracy_decimals: 1 - tvoc: - name: Workshop TVOC - accuracy_decimals: 1 - address: 0x58 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp30/test.esp32-c3-ard.yaml b/tests/components/sgp30/test.esp32-c3-ard.yaml index 45de67e94b..ee2c29ca4e 100644 --- a/tests/components/sgp30/test.esp32-c3-ard.yaml +++ b/tests/components/sgp30/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sgp30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp30 - eco2: - name: Workshop eCO2 - accuracy_decimals: 1 - tvoc: - name: Workshop TVOC - accuracy_decimals: 1 - address: 0x58 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp30/test.esp32-c3-idf.yaml b/tests/components/sgp30/test.esp32-c3-idf.yaml index 45de67e94b..ee2c29ca4e 100644 --- a/tests/components/sgp30/test.esp32-c3-idf.yaml +++ b/tests/components/sgp30/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sgp30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp30 - eco2: - name: Workshop eCO2 - accuracy_decimals: 1 - tvoc: - name: Workshop TVOC - accuracy_decimals: 1 - address: 0x58 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp30/test.esp32-idf.yaml b/tests/components/sgp30/test.esp32-idf.yaml index 6ea23c25cd..63c3bd6afd 100644 --- a/tests/components/sgp30/test.esp32-idf.yaml +++ b/tests/components/sgp30/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sgp30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sgp30 - eco2: - name: Workshop eCO2 - accuracy_decimals: 1 - tvoc: - name: Workshop TVOC - accuracy_decimals: 1 - address: 0x58 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp30/test.esp8266-ard.yaml b/tests/components/sgp30/test.esp8266-ard.yaml index 45de67e94b..ee2c29ca4e 100644 --- a/tests/components/sgp30/test.esp8266-ard.yaml +++ b/tests/components/sgp30/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sgp30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp30 - eco2: - name: Workshop eCO2 - accuracy_decimals: 1 - tvoc: - name: Workshop TVOC - accuracy_decimals: 1 - address: 0x58 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp30/test.rp2040-ard.yaml b/tests/components/sgp30/test.rp2040-ard.yaml index 45de67e94b..ee2c29ca4e 100644 --- a/tests/components/sgp30/test.rp2040-ard.yaml +++ b/tests/components/sgp30/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_sgp30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp30 - eco2: - name: Workshop eCO2 - accuracy_decimals: 1 - tvoc: - name: Workshop TVOC - accuracy_decimals: 1 - address: 0x58 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp4x/common.yaml b/tests/components/sgp4x/common.yaml new file mode 100644 index 0000000000..adb678d542 --- /dev/null +++ b/tests/components/sgp4x/common.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_sgp4x + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sgp4x + voc: + name: VOC Index + id: sgp40_voc_index + algorithm_tuning: + index_offset: 100 + learning_time_offset_hours: 12 + learning_time_gain_hours: 12 + gating_max_duration_minutes: 180 + std_initial: 50 + gain_factor: 230 + nox: + name: NOx + algorithm_tuning: + index_offset: 100 + learning_time_offset_hours: 12 + learning_time_gain_hours: 12 + gating_max_duration_minutes: 180 + std_initial: 50 + gain_factor: 230 + update_interval: 5s diff --git a/tests/components/sgp4x/test.esp32-ard.yaml b/tests/components/sgp4x/test.esp32-ard.yaml index c7380b5a10..63c3bd6afd 100644 --- a/tests/components/sgp4x/test.esp32-ard.yaml +++ b/tests/components/sgp4x/test.esp32-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_sgp4x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sgp4x - voc: - name: VOC Index - id: sgp40_voc_index - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - nox: - name: NOx - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp32-c3-ard.yaml b/tests/components/sgp4x/test.esp32-c3-ard.yaml index b2876478bd..ee2c29ca4e 100644 --- a/tests/components/sgp4x/test.esp32-c3-ard.yaml +++ b/tests/components/sgp4x/test.esp32-c3-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_sgp4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp4x - voc: - name: VOC Index - id: sgp40_voc_index - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - nox: - name: NOx - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp32-c3-idf.yaml b/tests/components/sgp4x/test.esp32-c3-idf.yaml index b2876478bd..ee2c29ca4e 100644 --- a/tests/components/sgp4x/test.esp32-c3-idf.yaml +++ b/tests/components/sgp4x/test.esp32-c3-idf.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_sgp4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp4x - voc: - name: VOC Index - id: sgp40_voc_index - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - nox: - name: NOx - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp32-idf.yaml b/tests/components/sgp4x/test.esp32-idf.yaml index c7380b5a10..63c3bd6afd 100644 --- a/tests/components/sgp4x/test.esp32-idf.yaml +++ b/tests/components/sgp4x/test.esp32-idf.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_sgp4x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sgp4x - voc: - name: VOC Index - id: sgp40_voc_index - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - nox: - name: NOx - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp4x/test.esp8266-ard.yaml b/tests/components/sgp4x/test.esp8266-ard.yaml index b2876478bd..ee2c29ca4e 100644 --- a/tests/components/sgp4x/test.esp8266-ard.yaml +++ b/tests/components/sgp4x/test.esp8266-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_sgp4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp4x - voc: - name: VOC Index - id: sgp40_voc_index - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - nox: - name: NOx - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sgp4x/test.rp2040-ard.yaml b/tests/components/sgp4x/test.rp2040-ard.yaml index b2876478bd..ee2c29ca4e 100644 --- a/tests/components/sgp4x/test.rp2040-ard.yaml +++ b/tests/components/sgp4x/test.rp2040-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_sgp4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sgp4x - voc: - name: VOC Index - id: sgp40_voc_index - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - nox: - name: NOx - algorithm_tuning: - index_offset: 100 - learning_time_offset_hours: 12 - learning_time_gain_hours: 12 - gating_max_duration_minutes: 180 - std_initial: 50 - gain_factor: 230 - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/sht3xd/common.yaml b/tests/components/sht3xd/common.yaml new file mode 100644 index 0000000000..2426ebfbb9 --- /dev/null +++ b/tests/components/sht3xd/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_sht3xd + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sht3xd + temperature: + name: SHT3XD Temperature + humidity: + name: SHT3XD Humidity + address: 0x44 + update_interval: 15s diff --git a/tests/components/sht3xd/test.esp32-ard.yaml b/tests/components/sht3xd/test.esp32-ard.yaml index 2b6ee50760..63c3bd6afd 100644 --- a/tests/components/sht3xd/test.esp32-ard.yaml +++ b/tests/components/sht3xd/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht3xd - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sht3xd - temperature: - name: SHT3XD Temperature - humidity: - name: SHT3XD Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp32-c3-ard.yaml b/tests/components/sht3xd/test.esp32-c3-ard.yaml index 0409ff65c6..ee2c29ca4e 100644 --- a/tests/components/sht3xd/test.esp32-c3-ard.yaml +++ b/tests/components/sht3xd/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht3xd - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht3xd - temperature: - name: SHT3XD Temperature - humidity: - name: SHT3XD Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp32-c3-idf.yaml b/tests/components/sht3xd/test.esp32-c3-idf.yaml index 0409ff65c6..ee2c29ca4e 100644 --- a/tests/components/sht3xd/test.esp32-c3-idf.yaml +++ b/tests/components/sht3xd/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht3xd - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht3xd - temperature: - name: SHT3XD Temperature - humidity: - name: SHT3XD Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp32-idf.yaml b/tests/components/sht3xd/test.esp32-idf.yaml index 2b6ee50760..63c3bd6afd 100644 --- a/tests/components/sht3xd/test.esp32-idf.yaml +++ b/tests/components/sht3xd/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht3xd - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sht3xd - temperature: - name: SHT3XD Temperature - humidity: - name: SHT3XD Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht3xd/test.esp8266-ard.yaml b/tests/components/sht3xd/test.esp8266-ard.yaml index 0409ff65c6..ee2c29ca4e 100644 --- a/tests/components/sht3xd/test.esp8266-ard.yaml +++ b/tests/components/sht3xd/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht3xd - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht3xd - temperature: - name: SHT3XD Temperature - humidity: - name: SHT3XD Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht3xd/test.rp2040-ard.yaml b/tests/components/sht3xd/test.rp2040-ard.yaml index 0409ff65c6..ee2c29ca4e 100644 --- a/tests/components/sht3xd/test.rp2040-ard.yaml +++ b/tests/components/sht3xd/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht3xd - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht3xd - temperature: - name: SHT3XD Temperature - humidity: - name: SHT3XD Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht4x/common.yaml b/tests/components/sht4x/common.yaml new file mode 100644 index 0000000000..703a8fa32b --- /dev/null +++ b/tests/components/sht4x/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_sht4x + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sht4x + temperature: + name: SHT4X Temperature + humidity: + name: SHT4X Humidity + address: 0x44 + update_interval: 15s diff --git a/tests/components/sht4x/test.esp32-ard.yaml b/tests/components/sht4x/test.esp32-ard.yaml index 13ec524d7d..63c3bd6afd 100644 --- a/tests/components/sht4x/test.esp32-ard.yaml +++ b/tests/components/sht4x/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht4x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sht4x - temperature: - name: SHT4X Temperature - humidity: - name: SHT4X Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht4x/test.esp32-c3-ard.yaml b/tests/components/sht4x/test.esp32-c3-ard.yaml index 0bcdd864f6..ee2c29ca4e 100644 --- a/tests/components/sht4x/test.esp32-c3-ard.yaml +++ b/tests/components/sht4x/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht4x - temperature: - name: SHT4X Temperature - humidity: - name: SHT4X Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht4x/test.esp32-c3-idf.yaml b/tests/components/sht4x/test.esp32-c3-idf.yaml index 0bcdd864f6..ee2c29ca4e 100644 --- a/tests/components/sht4x/test.esp32-c3-idf.yaml +++ b/tests/components/sht4x/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht4x - temperature: - name: SHT4X Temperature - humidity: - name: SHT4X Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht4x/test.esp32-idf.yaml b/tests/components/sht4x/test.esp32-idf.yaml index 13ec524d7d..63c3bd6afd 100644 --- a/tests/components/sht4x/test.esp32-idf.yaml +++ b/tests/components/sht4x/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht4x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sht4x - temperature: - name: SHT4X Temperature - humidity: - name: SHT4X Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht4x/test.esp8266-ard.yaml b/tests/components/sht4x/test.esp8266-ard.yaml index 0bcdd864f6..ee2c29ca4e 100644 --- a/tests/components/sht4x/test.esp8266-ard.yaml +++ b/tests/components/sht4x/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht4x - temperature: - name: SHT4X Temperature - humidity: - name: SHT4X Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sht4x/test.rp2040-ard.yaml b/tests/components/sht4x/test.rp2040-ard.yaml index 0bcdd864f6..ee2c29ca4e 100644 --- a/tests/components/sht4x/test.rp2040-ard.yaml +++ b/tests/components/sht4x/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_sht4x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sht4x - temperature: - name: SHT4X Temperature - humidity: - name: SHT4X Humidity - address: 0x44 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/shtcx/common.yaml b/tests/components/shtcx/common.yaml new file mode 100644 index 0000000000..0211319124 --- /dev/null +++ b/tests/components/shtcx/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_shtcx + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: shtcx + temperature: + name: SHTCX Temperature + humidity: + name: SHTCX Humidity + address: 0x70 + update_interval: 15s diff --git a/tests/components/shtcx/test.esp32-ard.yaml b/tests/components/shtcx/test.esp32-ard.yaml index 619bac9548..63c3bd6afd 100644 --- a/tests/components/shtcx/test.esp32-ard.yaml +++ b/tests/components/shtcx/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_shtcx - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: shtcx - temperature: - name: SHTCX Temperature - humidity: - name: SHTCX Humidity - address: 0x70 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/shtcx/test.esp32-c3-ard.yaml b/tests/components/shtcx/test.esp32-c3-ard.yaml index c1c7a2a63f..ee2c29ca4e 100644 --- a/tests/components/shtcx/test.esp32-c3-ard.yaml +++ b/tests/components/shtcx/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_shtcx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: shtcx - temperature: - name: SHTCX Temperature - humidity: - name: SHTCX Humidity - address: 0x70 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/shtcx/test.esp32-c3-idf.yaml b/tests/components/shtcx/test.esp32-c3-idf.yaml index c1c7a2a63f..ee2c29ca4e 100644 --- a/tests/components/shtcx/test.esp32-c3-idf.yaml +++ b/tests/components/shtcx/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_shtcx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: shtcx - temperature: - name: SHTCX Temperature - humidity: - name: SHTCX Humidity - address: 0x70 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/shtcx/test.esp32-idf.yaml b/tests/components/shtcx/test.esp32-idf.yaml index 619bac9548..63c3bd6afd 100644 --- a/tests/components/shtcx/test.esp32-idf.yaml +++ b/tests/components/shtcx/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_shtcx - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: shtcx - temperature: - name: SHTCX Temperature - humidity: - name: SHTCX Humidity - address: 0x70 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/shtcx/test.esp8266-ard.yaml b/tests/components/shtcx/test.esp8266-ard.yaml index c1c7a2a63f..ee2c29ca4e 100644 --- a/tests/components/shtcx/test.esp8266-ard.yaml +++ b/tests/components/shtcx/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_shtcx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: shtcx - temperature: - name: SHTCX Temperature - humidity: - name: SHTCX Humidity - address: 0x70 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/shtcx/test.rp2040-ard.yaml b/tests/components/shtcx/test.rp2040-ard.yaml index c1c7a2a63f..ee2c29ca4e 100644 --- a/tests/components/shtcx/test.rp2040-ard.yaml +++ b/tests/components/shtcx/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_shtcx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: shtcx - temperature: - name: SHTCX Temperature - humidity: - name: SHTCX Humidity - address: 0x70 - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/sim800l/common.yaml b/tests/components/sim800l/common.yaml new file mode 100644 index 0000000000..1b4e2e1af6 --- /dev/null +++ b/tests/components/sim800l/common.yaml @@ -0,0 +1,37 @@ +esphome: + on_boot: + then: + - sim800l.send_sms: + recipient: '+15551234567' + message: Hello there + - sim800l.dial: + recipient: '+15551234567' + - sim800l.connect + - sim800l.disconnect + - sim800l.send_ussd: + ussd: test_ussd + +uart: + - id: uart_sim800l + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sim800l: + on_sms_received: + - lambda: |- + std::string str; + str = sender; + str = message; + - sim800l.send_sms: + message: hello you + recipient: "+1234" + - sim800l.dial: + recipient: "+1234" + on_incoming_call: + - logger.log: + format: "Incoming call from '%s'" + args: ["caller_id.c_str()"] + - sim800l.disconnect + on_ussd_received: + - logger.log: "ussd_received" diff --git a/tests/components/sim800l/test.esp32-ard.yaml b/tests/components/sim800l/test.esp32-ard.yaml index c116548c6f..f486544afa 100644 --- a/tests/components/sim800l/test.esp32-ard.yaml +++ b/tests/components/sim800l/test.esp32-ard.yaml @@ -1,37 +1,5 @@ -esphome: - on_boot: - then: - - sim800l.send_sms: - recipient: '+15551234567' - message: Hello there - - sim800l.dial: - recipient: '+15551234567' - - sim800l.connect - - sim800l.disconnect - - sim800l.send_ussd: - ussd: test_ussd +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_sim800l - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -sim800l: - on_sms_received: - - lambda: |- - std::string str; - str = sender; - str = message; - - sim800l.send_sms: - message: hello you - recipient: "+1234" - - sim800l.dial: - recipient: "+1234" - on_incoming_call: - - logger.log: - format: "Incoming call from '%s'" - args: ["caller_id.c_str()"] - - sim800l.disconnect - on_ussd_received: - - logger.log: "ussd_received" +<<: !include common.yaml diff --git a/tests/components/sim800l/test.esp32-c3-ard.yaml b/tests/components/sim800l/test.esp32-c3-ard.yaml index 7ff359d1e7..b516342f3b 100644 --- a/tests/components/sim800l/test.esp32-c3-ard.yaml +++ b/tests/components/sim800l/test.esp32-c3-ard.yaml @@ -1,37 +1,5 @@ -esphome: - on_boot: - then: - - sim800l.send_sms: - recipient: '+15551234567' - message: Hello there - - sim800l.dial: - recipient: '+15551234567' - - sim800l.connect - - sim800l.disconnect - - sim800l.send_ussd: - ussd: test_ussd +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_sim800l - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -sim800l: - on_sms_received: - - lambda: |- - std::string str; - str = sender; - str = message; - - sim800l.send_sms: - message: hello you - recipient: "+1234" - - sim800l.dial: - recipient: "+1234" - on_incoming_call: - - logger.log: - format: "Incoming call from '%s'" - args: ["caller_id.c_str()"] - - sim800l.disconnect - on_ussd_received: - - logger.log: "ussd_received" +<<: !include common.yaml diff --git a/tests/components/sim800l/test.esp32-c3-idf.yaml b/tests/components/sim800l/test.esp32-c3-idf.yaml index 7ff359d1e7..b516342f3b 100644 --- a/tests/components/sim800l/test.esp32-c3-idf.yaml +++ b/tests/components/sim800l/test.esp32-c3-idf.yaml @@ -1,37 +1,5 @@ -esphome: - on_boot: - then: - - sim800l.send_sms: - recipient: '+15551234567' - message: Hello there - - sim800l.dial: - recipient: '+15551234567' - - sim800l.connect - - sim800l.disconnect - - sim800l.send_ussd: - ussd: test_ussd +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_sim800l - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -sim800l: - on_sms_received: - - lambda: |- - std::string str; - str = sender; - str = message; - - sim800l.send_sms: - message: hello you - recipient: "+1234" - - sim800l.dial: - recipient: "+1234" - on_incoming_call: - - logger.log: - format: "Incoming call from '%s'" - args: ["caller_id.c_str()"] - - sim800l.disconnect - on_ussd_received: - - logger.log: "ussd_received" +<<: !include common.yaml diff --git a/tests/components/sim800l/test.esp32-idf.yaml b/tests/components/sim800l/test.esp32-idf.yaml index c116548c6f..f486544afa 100644 --- a/tests/components/sim800l/test.esp32-idf.yaml +++ b/tests/components/sim800l/test.esp32-idf.yaml @@ -1,37 +1,5 @@ -esphome: - on_boot: - then: - - sim800l.send_sms: - recipient: '+15551234567' - message: Hello there - - sim800l.dial: - recipient: '+15551234567' - - sim800l.connect - - sim800l.disconnect - - sim800l.send_ussd: - ussd: test_ussd +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -uart: - - id: uart_sim800l - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -sim800l: - on_sms_received: - - lambda: |- - std::string str; - str = sender; - str = message; - - sim800l.send_sms: - message: hello you - recipient: "+1234" - - sim800l.dial: - recipient: "+1234" - on_incoming_call: - - logger.log: - format: "Incoming call from '%s'" - args: ["caller_id.c_str()"] - - sim800l.disconnect - on_ussd_received: - - logger.log: "ussd_received" +<<: !include common.yaml diff --git a/tests/components/sim800l/test.esp8266-ard.yaml b/tests/components/sim800l/test.esp8266-ard.yaml index 7ff359d1e7..b516342f3b 100644 --- a/tests/components/sim800l/test.esp8266-ard.yaml +++ b/tests/components/sim800l/test.esp8266-ard.yaml @@ -1,37 +1,5 @@ -esphome: - on_boot: - then: - - sim800l.send_sms: - recipient: '+15551234567' - message: Hello there - - sim800l.dial: - recipient: '+15551234567' - - sim800l.connect - - sim800l.disconnect - - sim800l.send_ussd: - ussd: test_ussd +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_sim800l - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -sim800l: - on_sms_received: - - lambda: |- - std::string str; - str = sender; - str = message; - - sim800l.send_sms: - message: hello you - recipient: "+1234" - - sim800l.dial: - recipient: "+1234" - on_incoming_call: - - logger.log: - format: "Incoming call from '%s'" - args: ["caller_id.c_str()"] - - sim800l.disconnect - on_ussd_received: - - logger.log: "ussd_received" +<<: !include common.yaml diff --git a/tests/components/sim800l/test.rp2040-ard.yaml b/tests/components/sim800l/test.rp2040-ard.yaml index 7ff359d1e7..b516342f3b 100644 --- a/tests/components/sim800l/test.rp2040-ard.yaml +++ b/tests/components/sim800l/test.rp2040-ard.yaml @@ -1,37 +1,5 @@ -esphome: - on_boot: - then: - - sim800l.send_sms: - recipient: '+15551234567' - message: Hello there - - sim800l.dial: - recipient: '+15551234567' - - sim800l.connect - - sim800l.disconnect - - sim800l.send_ussd: - ussd: test_ussd +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -uart: - - id: uart_sim800l - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -sim800l: - on_sms_received: - - lambda: |- - std::string str; - str = sender; - str = message; - - sim800l.send_sms: - message: hello you - recipient: "+1234" - - sim800l.dial: - recipient: "+1234" - on_incoming_call: - - logger.log: - format: "Incoming call from '%s'" - args: ["caller_id.c_str()"] - - sim800l.disconnect - on_ussd_received: - - logger.log: "ussd_received" +<<: !include common.yaml diff --git a/tests/components/sm16716/common.yaml b/tests/components/sm16716/common.yaml index 3bf2712f4e..db61d0b1c2 100644 --- a/tests/components/sm16716/common.yaml +++ b/tests/components/sm16716/common.yaml @@ -1,6 +1,6 @@ sm16716: - clock_pin: 4 - data_pin: 5 + clock_pin: ${clock_pin} + data_pin: ${data_pin} num_channels: 3 num_chips: 1 diff --git a/tests/components/sm16716/test.esp32-ard.yaml b/tests/components/sm16716/test.esp32-ard.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm16716/test.esp32-ard.yaml +++ b/tests/components/sm16716/test.esp32-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm16716/test.esp32-c3-ard.yaml b/tests/components/sm16716/test.esp32-c3-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm16716/test.esp32-c3-ard.yaml +++ b/tests/components/sm16716/test.esp32-c3-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm16716/test.esp32-c3-idf.yaml b/tests/components/sm16716/test.esp32-c3-idf.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm16716/test.esp32-c3-idf.yaml +++ b/tests/components/sm16716/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm16716/test.esp32-idf.yaml b/tests/components/sm16716/test.esp32-idf.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm16716/test.esp32-idf.yaml +++ b/tests/components/sm16716/test.esp32-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm16716/test.esp8266-ard.yaml b/tests/components/sm16716/test.esp8266-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm16716/test.esp8266-ard.yaml +++ b/tests/components/sm16716/test.esp8266-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm16716/test.rp2040-ard.yaml b/tests/components/sm16716/test.rp2040-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm16716/test.rp2040-ard.yaml +++ b/tests/components/sm16716/test.rp2040-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2135/common.yaml b/tests/components/sm2135/common.yaml index 9a0de60839..8f807a9be5 100644 --- a/tests/components/sm2135/common.yaml +++ b/tests/components/sm2135/common.yaml @@ -1,6 +1,6 @@ sm2135: - clock_pin: 4 - data_pin: 5 + clock_pin: ${clock_pin} + data_pin: ${data_pin} rgb_current: 20mA cw_current: 60mA diff --git a/tests/components/sm2135/test.esp32-ard.yaml b/tests/components/sm2135/test.esp32-ard.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm2135/test.esp32-ard.yaml +++ b/tests/components/sm2135/test.esp32-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm2135/test.esp32-c3-ard.yaml b/tests/components/sm2135/test.esp32-c3-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2135/test.esp32-c3-ard.yaml +++ b/tests/components/sm2135/test.esp32-c3-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2135/test.esp32-c3-idf.yaml b/tests/components/sm2135/test.esp32-c3-idf.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2135/test.esp32-c3-idf.yaml +++ b/tests/components/sm2135/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2135/test.esp32-idf.yaml b/tests/components/sm2135/test.esp32-idf.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm2135/test.esp32-idf.yaml +++ b/tests/components/sm2135/test.esp32-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm2135/test.esp8266-ard.yaml b/tests/components/sm2135/test.esp8266-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2135/test.esp8266-ard.yaml +++ b/tests/components/sm2135/test.esp8266-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2135/test.rp2040-ard.yaml b/tests/components/sm2135/test.rp2040-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2135/test.rp2040-ard.yaml +++ b/tests/components/sm2135/test.rp2040-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2235/common.yaml b/tests/components/sm2235/common.yaml index 043d43d6f1..d97fa4eb6c 100644 --- a/tests/components/sm2235/common.yaml +++ b/tests/components/sm2235/common.yaml @@ -1,6 +1,6 @@ sm2235: - clock_pin: 4 - data_pin: 5 + clock_pin: ${clock_pin} + data_pin: ${data_pin} max_power_color_channels: 9 max_power_white_channels: 9 diff --git a/tests/components/sm2235/test.esp32-ard.yaml b/tests/components/sm2235/test.esp32-ard.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm2235/test.esp32-ard.yaml +++ b/tests/components/sm2235/test.esp32-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm2235/test.esp32-c3-ard.yaml b/tests/components/sm2235/test.esp32-c3-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2235/test.esp32-c3-ard.yaml +++ b/tests/components/sm2235/test.esp32-c3-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2235/test.esp32-c3-idf.yaml b/tests/components/sm2235/test.esp32-c3-idf.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2235/test.esp32-c3-idf.yaml +++ b/tests/components/sm2235/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2235/test.esp32-idf.yaml b/tests/components/sm2235/test.esp32-idf.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm2235/test.esp32-idf.yaml +++ b/tests/components/sm2235/test.esp32-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm2235/test.esp8266-ard.yaml b/tests/components/sm2235/test.esp8266-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2235/test.esp8266-ard.yaml +++ b/tests/components/sm2235/test.esp8266-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2235/test.rp2040-ard.yaml b/tests/components/sm2235/test.rp2040-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2235/test.rp2040-ard.yaml +++ b/tests/components/sm2235/test.rp2040-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2335/common.yaml b/tests/components/sm2335/common.yaml index a5b2aedeb5..51d40aab21 100644 --- a/tests/components/sm2335/common.yaml +++ b/tests/components/sm2335/common.yaml @@ -1,6 +1,6 @@ sm2335: - clock_pin: 4 - data_pin: 5 + clock_pin: ${clock_pin} + data_pin: ${data_pin} max_power_color_channels: 9 max_power_white_channels: 9 diff --git a/tests/components/sm2335/test.esp32-ard.yaml b/tests/components/sm2335/test.esp32-ard.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm2335/test.esp32-ard.yaml +++ b/tests/components/sm2335/test.esp32-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm2335/test.esp32-c3-ard.yaml b/tests/components/sm2335/test.esp32-c3-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2335/test.esp32-c3-ard.yaml +++ b/tests/components/sm2335/test.esp32-c3-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2335/test.esp32-c3-idf.yaml b/tests/components/sm2335/test.esp32-c3-idf.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2335/test.esp32-c3-idf.yaml +++ b/tests/components/sm2335/test.esp32-c3-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2335/test.esp32-idf.yaml b/tests/components/sm2335/test.esp32-idf.yaml index dade44d145..d295973e3f 100644 --- a/tests/components/sm2335/test.esp32-idf.yaml +++ b/tests/components/sm2335/test.esp32-idf.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + <<: !include common.yaml diff --git a/tests/components/sm2335/test.esp8266-ard.yaml b/tests/components/sm2335/test.esp8266-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2335/test.esp8266-ard.yaml +++ b/tests/components/sm2335/test.esp8266-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm2335/test.rp2040-ard.yaml b/tests/components/sm2335/test.rp2040-ard.yaml index dade44d145..7808481215 100644 --- a/tests/components/sm2335/test.rp2040-ard.yaml +++ b/tests/components/sm2335/test.rp2040-ard.yaml @@ -1 +1,5 @@ +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + <<: !include common.yaml diff --git a/tests/components/sm300d2/common.yaml b/tests/components/sm300d2/common.yaml new file mode 100644 index 0000000000..a231b63816 --- /dev/null +++ b/tests/components/sm300d2/common.yaml @@ -0,0 +1,23 @@ +uart: + - id: uart_sm300d2 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: sm300d2 + co2: + name: SM300D2 CO2 Value + formaldehyde: + name: SM300D2 Formaldehyde Value + tvoc: + name: SM300D2 TVOC Value + pm_2_5: + name: SM300D2 PM2.5 Value + pm_10_0: + name: SM300D2 PM10 Value + temperature: + name: SM300D2 Temperature Value + humidity: + name: SM300D2 Humidity Value + update_interval: 60s diff --git a/tests/components/sm300d2/test.esp32-ard.yaml b/tests/components/sm300d2/test.esp32-ard.yaml index 92dba4fb3b..f486544afa 100644 --- a/tests/components/sm300d2/test.esp32-ard.yaml +++ b/tests/components/sm300d2/test.esp32-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sm300d2 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: sm300d2 - co2: - name: SM300D2 CO2 Value - formaldehyde: - name: SM300D2 Formaldehyde Value - tvoc: - name: SM300D2 TVOC Value - pm_2_5: - name: SM300D2 PM2.5 Value - pm_10_0: - name: SM300D2 PM10 Value - temperature: - name: SM300D2 Temperature Value - humidity: - name: SM300D2 Humidity Value - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp32-c3-ard.yaml b/tests/components/sm300d2/test.esp32-c3-ard.yaml index bcd0a728b2..b516342f3b 100644 --- a/tests/components/sm300d2/test.esp32-c3-ard.yaml +++ b/tests/components/sm300d2/test.esp32-c3-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sm300d2 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sm300d2 - co2: - name: SM300D2 CO2 Value - formaldehyde: - name: SM300D2 Formaldehyde Value - tvoc: - name: SM300D2 TVOC Value - pm_2_5: - name: SM300D2 PM2.5 Value - pm_10_0: - name: SM300D2 PM10 Value - temperature: - name: SM300D2 Temperature Value - humidity: - name: SM300D2 Humidity Value - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp32-c3-idf.yaml b/tests/components/sm300d2/test.esp32-c3-idf.yaml index bcd0a728b2..b516342f3b 100644 --- a/tests/components/sm300d2/test.esp32-c3-idf.yaml +++ b/tests/components/sm300d2/test.esp32-c3-idf.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sm300d2 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sm300d2 - co2: - name: SM300D2 CO2 Value - formaldehyde: - name: SM300D2 Formaldehyde Value - tvoc: - name: SM300D2 TVOC Value - pm_2_5: - name: SM300D2 PM2.5 Value - pm_10_0: - name: SM300D2 PM10 Value - temperature: - name: SM300D2 Temperature Value - humidity: - name: SM300D2 Humidity Value - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp32-idf.yaml b/tests/components/sm300d2/test.esp32-idf.yaml index 92dba4fb3b..f486544afa 100644 --- a/tests/components/sm300d2/test.esp32-idf.yaml +++ b/tests/components/sm300d2/test.esp32-idf.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sm300d2 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: sm300d2 - co2: - name: SM300D2 CO2 Value - formaldehyde: - name: SM300D2 Formaldehyde Value - tvoc: - name: SM300D2 TVOC Value - pm_2_5: - name: SM300D2 PM2.5 Value - pm_10_0: - name: SM300D2 PM10 Value - temperature: - name: SM300D2 Temperature Value - humidity: - name: SM300D2 Humidity Value - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/sm300d2/test.esp8266-ard.yaml b/tests/components/sm300d2/test.esp8266-ard.yaml index bcd0a728b2..b516342f3b 100644 --- a/tests/components/sm300d2/test.esp8266-ard.yaml +++ b/tests/components/sm300d2/test.esp8266-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sm300d2 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sm300d2 - co2: - name: SM300D2 CO2 Value - formaldehyde: - name: SM300D2 Formaldehyde Value - tvoc: - name: SM300D2 TVOC Value - pm_2_5: - name: SM300D2 PM2.5 Value - pm_10_0: - name: SM300D2 PM10 Value - temperature: - name: SM300D2 Temperature Value - humidity: - name: SM300D2 Humidity Value - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/sm300d2/test.rp2040-ard.yaml b/tests/components/sm300d2/test.rp2040-ard.yaml index bcd0a728b2..b516342f3b 100644 --- a/tests/components/sm300d2/test.rp2040-ard.yaml +++ b/tests/components/sm300d2/test.rp2040-ard.yaml @@ -1,23 +1,5 @@ -uart: - - id: uart_sm300d2 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: sm300d2 - co2: - name: SM300D2 CO2 Value - formaldehyde: - name: SM300D2 Formaldehyde Value - tvoc: - name: SM300D2 TVOC Value - pm_2_5: - name: SM300D2 PM2.5 Value - pm_10_0: - name: SM300D2 PM10 Value - temperature: - name: SM300D2 Temperature Value - humidity: - name: SM300D2 Humidity Value - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/sml/common.yaml b/tests/components/sml/common.yaml new file mode 100644 index 0000000000..a50d25eeee --- /dev/null +++ b/tests/components/sml/common.yaml @@ -0,0 +1,31 @@ +uart: + - id: uart_sml + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sml: + id: mysml + on_data: + - logger.log: "SML on_data" + +sensor: + - platform: sml + name: Total energy + sml_id: mysml + server_id: 0123456789abcdef + obis_code: "1-0:1.8.0" + unit_of_measurement: kWh + accuracy_decimals: 1 + device_class: energy + state_class: total_increasing + filters: + - multiply: 0.0001 + +text_sensor: + - platform: sml + name: Manufacturer + sml_id: mysml + server_id: 0123456789abcdef + obis_code: "129-129:199.130.3" + format: text diff --git a/tests/components/sml/test.esp32-ard.yaml b/tests/components/sml/test.esp32-ard.yaml index 7217199380..f486544afa 100644 --- a/tests/components/sml/test.esp32-ard.yaml +++ b/tests/components/sml/test.esp32-ard.yaml @@ -1,31 +1,5 @@ -uart: - - id: uart_sml - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sml: - id: mysml - on_data: - - logger.log: "SML on_data" - -sensor: - - platform: sml - name: Total energy - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "1-0:1.8.0" - unit_of_measurement: kWh - accuracy_decimals: 1 - device_class: energy - state_class: total_increasing - filters: - - multiply: 0.0001 - -text_sensor: - - platform: sml - name: Manufacturer - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "129-129:199.130.3" - format: text +<<: !include common.yaml diff --git a/tests/components/sml/test.esp32-c3-ard.yaml b/tests/components/sml/test.esp32-c3-ard.yaml index 903f968c26..b516342f3b 100644 --- a/tests/components/sml/test.esp32-c3-ard.yaml +++ b/tests/components/sml/test.esp32-c3-ard.yaml @@ -1,31 +1,5 @@ -uart: - - id: uart_sml - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sml: - id: mysml - on_data: - - logger.log: "SML on_data" - -sensor: - - platform: sml - name: Total energy - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "1-0:1.8.0" - unit_of_measurement: kWh - accuracy_decimals: 1 - device_class: energy - state_class: total_increasing - filters: - - multiply: 0.0001 - -text_sensor: - - platform: sml - name: Manufacturer - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "129-129:199.130.3" - format: text +<<: !include common.yaml diff --git a/tests/components/sml/test.esp32-c3-idf.yaml b/tests/components/sml/test.esp32-c3-idf.yaml index 903f968c26..b516342f3b 100644 --- a/tests/components/sml/test.esp32-c3-idf.yaml +++ b/tests/components/sml/test.esp32-c3-idf.yaml @@ -1,31 +1,5 @@ -uart: - - id: uart_sml - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sml: - id: mysml - on_data: - - logger.log: "SML on_data" - -sensor: - - platform: sml - name: Total energy - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "1-0:1.8.0" - unit_of_measurement: kWh - accuracy_decimals: 1 - device_class: energy - state_class: total_increasing - filters: - - multiply: 0.0001 - -text_sensor: - - platform: sml - name: Manufacturer - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "129-129:199.130.3" - format: text +<<: !include common.yaml diff --git a/tests/components/sml/test.esp32-idf.yaml b/tests/components/sml/test.esp32-idf.yaml index 7217199380..f486544afa 100644 --- a/tests/components/sml/test.esp32-idf.yaml +++ b/tests/components/sml/test.esp32-idf.yaml @@ -1,31 +1,5 @@ -uart: - - id: uart_sml - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sml: - id: mysml - on_data: - - logger.log: "SML on_data" - -sensor: - - platform: sml - name: Total energy - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "1-0:1.8.0" - unit_of_measurement: kWh - accuracy_decimals: 1 - device_class: energy - state_class: total_increasing - filters: - - multiply: 0.0001 - -text_sensor: - - platform: sml - name: Manufacturer - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "129-129:199.130.3" - format: text +<<: !include common.yaml diff --git a/tests/components/sml/test.esp8266-ard.yaml b/tests/components/sml/test.esp8266-ard.yaml index 903f968c26..b516342f3b 100644 --- a/tests/components/sml/test.esp8266-ard.yaml +++ b/tests/components/sml/test.esp8266-ard.yaml @@ -1,31 +1,5 @@ -uart: - - id: uart_sml - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sml: - id: mysml - on_data: - - logger.log: "SML on_data" - -sensor: - - platform: sml - name: Total energy - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "1-0:1.8.0" - unit_of_measurement: kWh - accuracy_decimals: 1 - device_class: energy - state_class: total_increasing - filters: - - multiply: 0.0001 - -text_sensor: - - platform: sml - name: Manufacturer - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "129-129:199.130.3" - format: text +<<: !include common.yaml diff --git a/tests/components/sml/test.rp2040-ard.yaml b/tests/components/sml/test.rp2040-ard.yaml index 903f968c26..b516342f3b 100644 --- a/tests/components/sml/test.rp2040-ard.yaml +++ b/tests/components/sml/test.rp2040-ard.yaml @@ -1,31 +1,5 @@ -uart: - - id: uart_sml - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sml: - id: mysml - on_data: - - logger.log: "SML on_data" - -sensor: - - platform: sml - name: Total energy - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "1-0:1.8.0" - unit_of_measurement: kWh - accuracy_decimals: 1 - device_class: energy - state_class: total_increasing - filters: - - multiply: 0.0001 - -text_sensor: - - platform: sml - name: Manufacturer - sml_id: mysml - server_id: 0123456789abcdef - obis_code: "129-129:199.130.3" - format: text +<<: !include common.yaml diff --git a/tests/components/smt100/common.yaml b/tests/components/smt100/common.yaml new file mode 100644 index 0000000000..f86bd762e7 --- /dev/null +++ b/tests/components/smt100/common.yaml @@ -0,0 +1,19 @@ +uart: + - id: uart_smt100 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +sensor: + - platform: smt100 + counts: + name: Counts + dielectric_constant: + name: Dielectric Constant + temperature: + name: Temperature + moisture: + name: Moisture + voltage: + name: Voltage + update_interval: 60s diff --git a/tests/components/smt100/test.esp32-ard.yaml b/tests/components/smt100/test.esp32-ard.yaml index 7c19f4bc45..f486544afa 100644 --- a/tests/components/smt100/test.esp32-ard.yaml +++ b/tests/components/smt100/test.esp32-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_smt100 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: smt100 - counts: - name: Counts - dielectric_constant: - name: Dielectric Constant - temperature: - name: Temperature - moisture: - name: Moisture - voltage: - name: Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/smt100/test.esp32-c3-ard.yaml b/tests/components/smt100/test.esp32-c3-ard.yaml index 4277f2567b..b516342f3b 100644 --- a/tests/components/smt100/test.esp32-c3-ard.yaml +++ b/tests/components/smt100/test.esp32-c3-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_smt100 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: smt100 - counts: - name: Counts - dielectric_constant: - name: Dielectric Constant - temperature: - name: Temperature - moisture: - name: Moisture - voltage: - name: Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/smt100/test.esp32-c3-idf.yaml b/tests/components/smt100/test.esp32-c3-idf.yaml index 4277f2567b..b516342f3b 100644 --- a/tests/components/smt100/test.esp32-c3-idf.yaml +++ b/tests/components/smt100/test.esp32-c3-idf.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_smt100 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: smt100 - counts: - name: Counts - dielectric_constant: - name: Dielectric Constant - temperature: - name: Temperature - moisture: - name: Moisture - voltage: - name: Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/smt100/test.esp32-idf.yaml b/tests/components/smt100/test.esp32-idf.yaml index 7c19f4bc45..f486544afa 100644 --- a/tests/components/smt100/test.esp32-idf.yaml +++ b/tests/components/smt100/test.esp32-idf.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_smt100 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: smt100 - counts: - name: Counts - dielectric_constant: - name: Dielectric Constant - temperature: - name: Temperature - moisture: - name: Moisture - voltage: - name: Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/smt100/test.esp8266-ard.yaml b/tests/components/smt100/test.esp8266-ard.yaml index 4277f2567b..b516342f3b 100644 --- a/tests/components/smt100/test.esp8266-ard.yaml +++ b/tests/components/smt100/test.esp8266-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_smt100 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: smt100 - counts: - name: Counts - dielectric_constant: - name: Dielectric Constant - temperature: - name: Temperature - moisture: - name: Moisture - voltage: - name: Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/smt100/test.rp2040-ard.yaml b/tests/components/smt100/test.rp2040-ard.yaml index 4277f2567b..b516342f3b 100644 --- a/tests/components/smt100/test.rp2040-ard.yaml +++ b/tests/components/smt100/test.rp2040-ard.yaml @@ -1,19 +1,5 @@ -uart: - - id: uart_smt100 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: smt100 - counts: - name: Counts - dielectric_constant: - name: Dielectric Constant - temperature: - name: Temperature - moisture: - name: Moisture - voltage: - name: Voltage - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/sn74hc165/common.yaml b/tests/components/sn74hc165/common.yaml new file mode 100644 index 0000000000..f98711048d --- /dev/null +++ b/tests/components/sn74hc165/common.yaml @@ -0,0 +1,14 @@ +sn74hc165: + id: sn74hc165_hub + clock_pin: ${clock_pin} + data_pin: ${data_pin} + load_pin: ${load_pin} + clock_inhibit_pin: ${clock_inhibit_pin} + sr_count: 2 + +binary_sensor: + - platform: gpio + id: sn74hc165_pin_0 + pin: + sn74hc165: sn74hc165_hub + number: 0 diff --git a/tests/components/sn74hc165/test.esp32-ard.yaml b/tests/components/sn74hc165/test.esp32-ard.yaml index 55b06aec9b..27f963312f 100644 --- a/tests/components/sn74hc165/test.esp32-ard.yaml +++ b/tests/components/sn74hc165/test.esp32-ard.yaml @@ -1,14 +1,7 @@ -sn74hc165: - id: sn74hc165_hub - clock_pin: 13 - data_pin: 14 - load_pin: 15 - clock_inhibit_pin: 16 - sr_count: 2 +substitutions: + clock_pin: GPIO13 + data_pin: GPIO14 + load_pin: GPIO15 + clock_inhibit_pin: GPIO16 -binary_sensor: - - platform: gpio - id: sn74hc165_pin_0 - pin: - sn74hc165: sn74hc165_hub - number: 0 +<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp32-c3-ard.yaml b/tests/components/sn74hc165/test.esp32-c3-ard.yaml index f687b23c9d..0a3db917b7 100644 --- a/tests/components/sn74hc165/test.esp32-c3-ard.yaml +++ b/tests/components/sn74hc165/test.esp32-c3-ard.yaml @@ -1,14 +1,7 @@ -sn74hc165: - id: sn74hc165_hub - clock_pin: 3 - data_pin: 4 - load_pin: 5 - clock_inhibit_pin: 6 - sr_count: 2 +substitutions: + clock_pin: GPIO3 + data_pin: GPIO4 + load_pin: GPIO5 + clock_inhibit_pin: GPIO6 -binary_sensor: - - platform: gpio - id: sn74hc165_pin_0 - pin: - sn74hc165: sn74hc165_hub - number: 0 +<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp32-c3-idf.yaml b/tests/components/sn74hc165/test.esp32-c3-idf.yaml index f687b23c9d..0a3db917b7 100644 --- a/tests/components/sn74hc165/test.esp32-c3-idf.yaml +++ b/tests/components/sn74hc165/test.esp32-c3-idf.yaml @@ -1,14 +1,7 @@ -sn74hc165: - id: sn74hc165_hub - clock_pin: 3 - data_pin: 4 - load_pin: 5 - clock_inhibit_pin: 6 - sr_count: 2 +substitutions: + clock_pin: GPIO3 + data_pin: GPIO4 + load_pin: GPIO5 + clock_inhibit_pin: GPIO6 -binary_sensor: - - platform: gpio - id: sn74hc165_pin_0 - pin: - sn74hc165: sn74hc165_hub - number: 0 +<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp32-idf.yaml b/tests/components/sn74hc165/test.esp32-idf.yaml index 55b06aec9b..27f963312f 100644 --- a/tests/components/sn74hc165/test.esp32-idf.yaml +++ b/tests/components/sn74hc165/test.esp32-idf.yaml @@ -1,14 +1,7 @@ -sn74hc165: - id: sn74hc165_hub - clock_pin: 13 - data_pin: 14 - load_pin: 15 - clock_inhibit_pin: 16 - sr_count: 2 +substitutions: + clock_pin: GPIO13 + data_pin: GPIO14 + load_pin: GPIO15 + clock_inhibit_pin: GPIO16 -binary_sensor: - - platform: gpio - id: sn74hc165_pin_0 - pin: - sn74hc165: sn74hc165_hub - number: 0 +<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.esp8266-ard.yaml b/tests/components/sn74hc165/test.esp8266-ard.yaml index 55b06aec9b..27f963312f 100644 --- a/tests/components/sn74hc165/test.esp8266-ard.yaml +++ b/tests/components/sn74hc165/test.esp8266-ard.yaml @@ -1,14 +1,7 @@ -sn74hc165: - id: sn74hc165_hub - clock_pin: 13 - data_pin: 14 - load_pin: 15 - clock_inhibit_pin: 16 - sr_count: 2 +substitutions: + clock_pin: GPIO13 + data_pin: GPIO14 + load_pin: GPIO15 + clock_inhibit_pin: GPIO16 -binary_sensor: - - platform: gpio - id: sn74hc165_pin_0 - pin: - sn74hc165: sn74hc165_hub - number: 0 +<<: !include common.yaml diff --git a/tests/components/sn74hc165/test.rp2040-ard.yaml b/tests/components/sn74hc165/test.rp2040-ard.yaml index f687b23c9d..0a3db917b7 100644 --- a/tests/components/sn74hc165/test.rp2040-ard.yaml +++ b/tests/components/sn74hc165/test.rp2040-ard.yaml @@ -1,14 +1,7 @@ -sn74hc165: - id: sn74hc165_hub - clock_pin: 3 - data_pin: 4 - load_pin: 5 - clock_inhibit_pin: 6 - sr_count: 2 +substitutions: + clock_pin: GPIO3 + data_pin: GPIO4 + load_pin: GPIO5 + clock_inhibit_pin: GPIO6 -binary_sensor: - - platform: gpio - id: sn74hc165_pin_0 - pin: - sn74hc165: sn74hc165_hub - number: 0 +<<: !include common.yaml diff --git a/tests/components/sn74hc595/common.yaml b/tests/components/sn74hc595/common.yaml new file mode 100644 index 0000000000..fc297909f5 --- /dev/null +++ b/tests/components/sn74hc595/common.yaml @@ -0,0 +1,26 @@ +spi: + - id: spi_sn74hc595 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +sn74hc595: + - id: sn74hc595_hub + clock_pin: ${clock_pin} + data_pin: ${data_pin} + latch_pin: ${latch_pin1} + oe_pin: ${oe_pin1} + sr_count: 2 + - id: sn74hc595_hub_2 + latch_pin: ${latch_pin2} + oe_pin: ${oe_pin2} + type: spi + sr_count: 2 + +switch: + - platform: gpio + name: SN74HC595 Pin 0 + pin: + sn74hc595: sn74hc595_hub_2 + number: 0 + inverted: false diff --git a/tests/components/sn74hc595/test.esp32-ard.yaml b/tests/components/sn74hc595/test.esp32-ard.yaml index f695395797..a4bab64862 100644 --- a/tests/components/sn74hc595/test.esp32-ard.yaml +++ b/tests/components/sn74hc595/test.esp32-ard.yaml @@ -1,27 +1,12 @@ -spi: - - id: spi_sn74hc595 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + clock_pin: GPIO15 + data_pin: GPIO14 + latch_pin1: GPIO21 + oe_pin1: GPIO22 + latch_pin2: GPIO23 + oe_pin2: GPIO25 -sn74hc595: - - id: sn74hc595_hub - clock_pin: 12 - data_pin: 13 - latch_pin: 14 - oe_pin: 18 - sr_count: 2 - - id: sn74hc595_hub_2 - latch_pin: 21 - oe_pin: 22 - spi_id: spi_sn74hc595 - type: spi - sr_count: 2 - -switch: - - platform: gpio - name: SN74HC595 Pin 0 - pin: - sn74hc595: sn74hc595_hub_2 - number: 0 - inverted: false +<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp32-c3-ard.yaml b/tests/components/sn74hc595/test.esp32-c3-ard.yaml index 9b093899d3..14c928be88 100644 --- a/tests/components/sn74hc595/test.esp32-c3-ard.yaml +++ b/tests/components/sn74hc595/test.esp32-c3-ard.yaml @@ -1,27 +1,12 @@ -spi: - - id: spi_sn74hc595 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO8 + clock_pin: GPIO5 + data_pin: GPIO4 + latch_pin1: GPIO1 + oe_pin1: GPIO2 + latch_pin2: GPIO3 + oe_pin2: GPIO9 -sn74hc595: - - id: sn74hc595_hub - clock_pin: 0 - data_pin: 1 - latch_pin: 2 - oe_pin: 3 - sr_count: 2 - - id: sn74hc595_hub_2 - latch_pin: 8 - oe_pin: 9 - spi_id: spi_sn74hc595 - type: spi - sr_count: 2 - -switch: - - platform: gpio - name: SN74HC595 Pin 0 - pin: - sn74hc595: sn74hc595_hub_2 - number: 0 - inverted: false +<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp32-c3-idf.yaml b/tests/components/sn74hc595/test.esp32-c3-idf.yaml index 9b093899d3..14c928be88 100644 --- a/tests/components/sn74hc595/test.esp32-c3-idf.yaml +++ b/tests/components/sn74hc595/test.esp32-c3-idf.yaml @@ -1,27 +1,12 @@ -spi: - - id: spi_sn74hc595 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO8 + clock_pin: GPIO5 + data_pin: GPIO4 + latch_pin1: GPIO1 + oe_pin1: GPIO2 + latch_pin2: GPIO3 + oe_pin2: GPIO9 -sn74hc595: - - id: sn74hc595_hub - clock_pin: 0 - data_pin: 1 - latch_pin: 2 - oe_pin: 3 - sr_count: 2 - - id: sn74hc595_hub_2 - latch_pin: 8 - oe_pin: 9 - spi_id: spi_sn74hc595 - type: spi - sr_count: 2 - -switch: - - platform: gpio - name: SN74HC595 Pin 0 - pin: - sn74hc595: sn74hc595_hub_2 - number: 0 - inverted: false +<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp32-idf.yaml b/tests/components/sn74hc595/test.esp32-idf.yaml index f695395797..a4bab64862 100644 --- a/tests/components/sn74hc595/test.esp32-idf.yaml +++ b/tests/components/sn74hc595/test.esp32-idf.yaml @@ -1,27 +1,12 @@ -spi: - - id: spi_sn74hc595 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO18 + clock_pin: GPIO15 + data_pin: GPIO14 + latch_pin1: GPIO21 + oe_pin1: GPIO22 + latch_pin2: GPIO23 + oe_pin2: GPIO25 -sn74hc595: - - id: sn74hc595_hub - clock_pin: 12 - data_pin: 13 - latch_pin: 14 - oe_pin: 18 - sr_count: 2 - - id: sn74hc595_hub_2 - latch_pin: 21 - oe_pin: 22 - spi_id: spi_sn74hc595 - type: spi - sr_count: 2 - -switch: - - platform: gpio - name: SN74HC595 Pin 0 - pin: - sn74hc595: sn74hc595_hub_2 - number: 0 - inverted: false +<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.esp8266-ard.yaml b/tests/components/sn74hc595/test.esp8266-ard.yaml index 64bf5d1925..cad11feca8 100644 --- a/tests/components/sn74hc595/test.esp8266-ard.yaml +++ b/tests/components/sn74hc595/test.esp8266-ard.yaml @@ -1,27 +1,12 @@ -spi: - - id: spi_sn74hc595 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + clock_pin: GPIO5 + data_pin: GPIO4 + latch_pin1: GPIO2 + oe_pin1: GPIO0 + latch_pin2: GPIO3 + oe_pin2: GPIO1 -sn74hc595: - - id: sn74hc595_hub - clock_pin: 0 - data_pin: 2 - latch_pin: 4 - oe_pin: 5 - sr_count: 2 - - id: sn74hc595_hub_2 - latch_pin: 15 - oe_pin: 16 - spi_id: spi_sn74hc595 - type: spi - sr_count: 2 - -switch: - - platform: gpio - name: SN74HC595 Pin 0 - pin: - sn74hc595: sn74hc595_hub_2 - number: 0 - inverted: false +<<: !include common.yaml diff --git a/tests/components/sn74hc595/test.rp2040-ard.yaml b/tests/components/sn74hc595/test.rp2040-ard.yaml index de8e192659..14c928be88 100644 --- a/tests/components/sn74hc595/test.rp2040-ard.yaml +++ b/tests/components/sn74hc595/test.rp2040-ard.yaml @@ -1,27 +1,12 @@ -spi: - - id: spi_sn74hc595 - clk_pin: 6 - mosi_pin: 5 - miso_pin: 4 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO8 + clock_pin: GPIO5 + data_pin: GPIO4 + latch_pin1: GPIO1 + oe_pin1: GPIO2 + latch_pin2: GPIO3 + oe_pin2: GPIO9 -sn74hc595: - - id: sn74hc595_hub - clock_pin: 0 - data_pin: 1 - latch_pin: 2 - oe_pin: 3 - sr_count: 2 - - id: sn74hc595_hub_2 - latch_pin: 8 - oe_pin: 9 - spi_id: spi_sn74hc595 - type: spi - sr_count: 2 - -switch: - - platform: gpio - name: SN74HC595 Pin 0 - pin: - sn74hc595: sn74hc595_hub_2 - number: 0 - inverted: false +<<: !include common.yaml diff --git a/tests/components/sonoff_d1/common.yaml b/tests/components/sonoff_d1/common.yaml new file mode 100644 index 0000000000..d2d4043b95 --- /dev/null +++ b/tests/components/sonoff_d1/common.yaml @@ -0,0 +1,12 @@ +uart: + - id: uart_sonoff_d1 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +light: + - platform: sonoff_d1 + id: d1_light + name: Sonoff D1 Dimmer + restore_mode: RESTORE_DEFAULT_OFF + use_rm433_remote: false diff --git a/tests/components/sonoff_d1/test.esp32-ard.yaml b/tests/components/sonoff_d1/test.esp32-ard.yaml index dc35e3b6ac..f486544afa 100644 --- a/tests/components/sonoff_d1/test.esp32-ard.yaml +++ b/tests/components/sonoff_d1/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_sonoff_d1 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -light: - - platform: sonoff_d1 - id: d1_light - name: Sonoff D1 Dimmer - restore_mode: RESTORE_DEFAULT_OFF - use_rm433_remote: false +<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp32-c3-ard.yaml b/tests/components/sonoff_d1/test.esp32-c3-ard.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/sonoff_d1/test.esp32-c3-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp32-c3-idf.yaml b/tests/components/sonoff_d1/test.esp32-c3-idf.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/sonoff_d1/test.esp32-c3-idf.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp32-idf.yaml b/tests/components/sonoff_d1/test.esp32-idf.yaml index dc35e3b6ac..f486544afa 100644 --- a/tests/components/sonoff_d1/test.esp32-idf.yaml +++ b/tests/components/sonoff_d1/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_sonoff_d1 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -light: - - platform: sonoff_d1 - id: d1_light - name: Sonoff D1 Dimmer - restore_mode: RESTORE_DEFAULT_OFF - use_rm433_remote: false +<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.esp8266-ard.yaml b/tests/components/sonoff_d1/test.esp8266-ard.yaml index c4a62f4cb3..b516342f3b 100644 --- a/tests/components/sonoff_d1/test.esp8266-ard.yaml +++ b/tests/components/sonoff_d1/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -uart: - - id: uart_sonoff_d1 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -light: - - platform: sonoff_d1 - id: d1_light - name: Sonoff D1 Dimmer - restore_mode: RESTORE_DEFAULT_OFF - use_rm433_remote: false +<<: !include common.yaml diff --git a/tests/components/sonoff_d1/test.rp2040-ard.yaml b/tests/components/sonoff_d1/test.rp2040-ard.yaml new file mode 100644 index 0000000000..b516342f3b --- /dev/null +++ b/tests/components/sonoff_d1/test.rp2040-ard.yaml @@ -0,0 +1,5 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/speaker/audio_dac.esp32-ard.yaml b/tests/components/speaker/audio_dac.esp32-ard.yaml new file mode 100644 index 0000000000..75d9ddf92b --- /dev/null +++ b/tests/components/speaker/audio_dac.esp32-ard.yaml @@ -0,0 +1,9 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + i2s_bclk_pin: GPIO27 + i2s_lrclk_pin: GPIO26 + i2s_mclk_pin: GPIO25 + i2s_dout_pin: GPIO23 + +<<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/audio_dac.esp32-c3-ard.yaml b/tests/components/speaker/audio_dac.esp32-c3-ard.yaml new file mode 100644 index 0000000000..1004d2143e --- /dev/null +++ b/tests/components/speaker/audio_dac.esp32-c3-ard.yaml @@ -0,0 +1,9 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + i2s_bclk_pin: GPIO7 + i2s_lrclk_pin: GPIO6 + i2s_mclk_pin: GPIO9 + i2s_dout_pin: GPIO8 + +<<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/audio_dac.esp32-c3-idf.yaml b/tests/components/speaker/audio_dac.esp32-c3-idf.yaml new file mode 100644 index 0000000000..1004d2143e --- /dev/null +++ b/tests/components/speaker/audio_dac.esp32-c3-idf.yaml @@ -0,0 +1,9 @@ +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + i2s_bclk_pin: GPIO7 + i2s_lrclk_pin: GPIO6 + i2s_mclk_pin: GPIO9 + i2s_dout_pin: GPIO8 + +<<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/audio_dac.esp32-idf.yaml b/tests/components/speaker/audio_dac.esp32-idf.yaml new file mode 100644 index 0000000000..75d9ddf92b --- /dev/null +++ b/tests/components/speaker/audio_dac.esp32-idf.yaml @@ -0,0 +1,9 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + i2s_bclk_pin: GPIO27 + i2s_lrclk_pin: GPIO26 + i2s_mclk_pin: GPIO25 + i2s_dout_pin: GPIO23 + +<<: !include common-audio_dac.yaml diff --git a/tests/components/speaker/common-audio_dac.yaml b/tests/components/speaker/common-audio_dac.yaml new file mode 100644 index 0000000000..41b994d4d4 --- /dev/null +++ b/tests/components/speaker/common-audio_dac.yaml @@ -0,0 +1,36 @@ +esphome: + on_boot: + then: + - speaker.mute_on: + - speaker.mute_off: + - if: + condition: speaker.is_stopped + then: + - speaker.play: [0, 1, 2, 3] + - speaker.volume_set: 0.9 + - if: + condition: speaker.is_playing + then: + - speaker.finish: + - speaker.stop: + +i2c: + - id: i2c_audio_dac + scl: ${scl_pin} + sda: ${sda_pin} + +i2s_audio: + i2s_lrclk_pin: ${i2s_bclk_pin} + i2s_bclk_pin: ${i2s_lrclk_pin} + i2s_mclk_pin: ${i2s_mclk_pin} + +audio_dac: + - platform: aic3204 + id: internal_dac + +speaker: + - platform: i2s_audio + id: speaker_with_audio_dac_id + audio_dac: internal_dac + dac_type: external + i2s_dout_pin: ${i2s_dout_pin} diff --git a/tests/components/speaker/common.yaml b/tests/components/speaker/common.yaml new file mode 100644 index 0000000000..c04674ee29 --- /dev/null +++ b/tests/components/speaker/common.yaml @@ -0,0 +1,26 @@ +esphome: + on_boot: + then: + - speaker.mute_on: + - speaker.mute_off: + - if: + condition: speaker.is_stopped + then: + - speaker.play: [0, 1, 2, 3] + - speaker.volume_set: 0.9 + - if: + condition: speaker.is_playing + then: + - speaker.finish: + - speaker.stop: + +i2s_audio: + i2s_lrclk_pin: ${i2s_bclk_pin} + i2s_bclk_pin: ${i2s_lrclk_pin} + i2s_mclk_pin: ${i2s_mclk_pin} + +speaker: + - platform: i2s_audio + id: speaker_id + dac_type: external + i2s_dout_pin: ${i2s_dout_pin} diff --git a/tests/components/speaker/test.esp32-ard.yaml b/tests/components/speaker/test.esp32-ard.yaml index 396b4d95ea..e2439ebdf2 100644 --- a/tests/components/speaker/test.esp32-ard.yaml +++ b/tests/components/speaker/test.esp32-ard.yaml @@ -1,26 +1,9 @@ -esphome: - on_boot: - then: - - speaker.mute_on: - - speaker.mute_off: - - if: - condition: speaker.is_stopped - then: - - speaker.play: [0, 1, 2, 3] - - speaker.volume_set: 0.9 - - if: - condition: speaker.is_playing - then: - - speaker.finish: - - speaker.stop: +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + i2s_bclk_pin: GPIO27 + i2s_lrclk_pin: GPIO26 + i2s_mclk_pin: GPIO25 + i2s_dout_pin: GPIO23 -i2s_audio: - i2s_lrclk_pin: 16 - i2s_bclk_pin: 17 - i2s_mclk_pin: 15 - -speaker: - - platform: i2s_audio - id: speaker_id - dac_type: external - i2s_dout_pin: 13 +<<: !include common.yaml diff --git a/tests/components/speaker/test.esp32-c3-ard.yaml b/tests/components/speaker/test.esp32-c3-ard.yaml index 636aeba766..ddcf051fab 100644 --- a/tests/components/speaker/test.esp32-c3-ard.yaml +++ b/tests/components/speaker/test.esp32-c3-ard.yaml @@ -1,26 +1,9 @@ -esphome: - on_boot: - then: - - speaker.mute_on: - - speaker.mute_off: - - if: - condition: speaker.is_stopped - then: - - speaker.play: [0, 1, 2, 3] - - speaker.volume_set: 0.9 - - if: - condition: speaker.is_playing - then: - - speaker.finish: - - speaker.stop: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + i2s_bclk_pin: GPIO7 + i2s_lrclk_pin: GPIO6 + i2s_mclk_pin: GPIO9 + i2s_dout_pin: GPIO8 -i2s_audio: - i2s_lrclk_pin: 6 - i2s_bclk_pin: 7 - i2s_mclk_pin: 5 - -speaker: - - platform: i2s_audio - id: speaker_id - dac_type: external - i2s_dout_pin: 3 +<<: !include common.yaml diff --git a/tests/components/speaker/test.esp32-c3-idf.yaml b/tests/components/speaker/test.esp32-c3-idf.yaml index 636aeba766..ddcf051fab 100644 --- a/tests/components/speaker/test.esp32-c3-idf.yaml +++ b/tests/components/speaker/test.esp32-c3-idf.yaml @@ -1,26 +1,9 @@ -esphome: - on_boot: - then: - - speaker.mute_on: - - speaker.mute_off: - - if: - condition: speaker.is_stopped - then: - - speaker.play: [0, 1, 2, 3] - - speaker.volume_set: 0.9 - - if: - condition: speaker.is_playing - then: - - speaker.finish: - - speaker.stop: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + i2s_bclk_pin: GPIO7 + i2s_lrclk_pin: GPIO6 + i2s_mclk_pin: GPIO9 + i2s_dout_pin: GPIO8 -i2s_audio: - i2s_lrclk_pin: 6 - i2s_bclk_pin: 7 - i2s_mclk_pin: 5 - -speaker: - - platform: i2s_audio - id: speaker_id - dac_type: external - i2s_dout_pin: 3 +<<: !include common.yaml diff --git a/tests/components/speaker/test.esp32-idf.yaml b/tests/components/speaker/test.esp32-idf.yaml index b69440b133..e2439ebdf2 100644 --- a/tests/components/speaker/test.esp32-idf.yaml +++ b/tests/components/speaker/test.esp32-idf.yaml @@ -1,35 +1,9 @@ -esphome: - on_boot: - then: - - speaker.mute_on: - - speaker.mute_off: - - if: - condition: speaker.is_stopped - then: - - speaker.play: [0, 1, 2, 3] - - speaker.volume_set: 0.9 - - if: - condition: speaker.is_playing - then: - - speaker.finish: - - speaker.stop: +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + i2s_bclk_pin: GPIO27 + i2s_lrclk_pin: GPIO26 + i2s_mclk_pin: GPIO25 + i2s_dout_pin: GPIO23 -i2s_audio: - i2s_lrclk_pin: 16 - i2s_bclk_pin: 17 - i2s_mclk_pin: 15 - -i2c: - scl: 12 - sda: 10 - -audio_dac: - - platform: aic3204 - id: internal_dac - -speaker: - - platform: i2s_audio - id: speaker_with_audio_dac_id - audio_dac: internal_dac - dac_type: external - i2s_dout_pin: 14 +<<: !include common.yaml diff --git a/tests/components/speed/common.yaml b/tests/components/speed/common.yaml new file mode 100644 index 0000000000..be8172af7e --- /dev/null +++ b/tests/components/speed/common.yaml @@ -0,0 +1,9 @@ +output: + - platform: ${output_platform} + id: fan_output_1 + pin: ${pin} + +fan: + - platform: speed + id: fan_speed + output: fan_output_1 diff --git a/tests/components/speed/test.esp32-ard.yaml b/tests/components/speed/test.esp32-ard.yaml index 29a55e9edd..26da1ce1d6 100644 --- a/tests/components/speed/test.esp32-ard.yaml +++ b/tests/components/speed/test.esp32-ard.yaml @@ -1,9 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 12 +substitutions: + output_platform: ledc + pin: GPIO14 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 +<<: !include common.yaml diff --git a/tests/components/speed/test.esp32-c3-ard.yaml b/tests/components/speed/test.esp32-c3-ard.yaml index fa1920676e..7476963591 100644 --- a/tests/components/speed/test.esp32-c3-ard.yaml +++ b/tests/components/speed/test.esp32-c3-ard.yaml @@ -1,9 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 2 +substitutions: + output_platform: ledc + pin: GPIO4 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 +<<: !include common.yaml diff --git a/tests/components/speed/test.esp32-c3-idf.yaml b/tests/components/speed/test.esp32-c3-idf.yaml index fa1920676e..7476963591 100644 --- a/tests/components/speed/test.esp32-c3-idf.yaml +++ b/tests/components/speed/test.esp32-c3-idf.yaml @@ -1,9 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 2 +substitutions: + output_platform: ledc + pin: GPIO4 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 +<<: !include common.yaml diff --git a/tests/components/speed/test.esp32-idf.yaml b/tests/components/speed/test.esp32-idf.yaml index 29a55e9edd..26da1ce1d6 100644 --- a/tests/components/speed/test.esp32-idf.yaml +++ b/tests/components/speed/test.esp32-idf.yaml @@ -1,9 +1,5 @@ -output: - - platform: ledc - id: fan_output_1 - pin: 12 +substitutions: + output_platform: ledc + pin: GPIO14 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 +<<: !include common.yaml diff --git a/tests/components/speed/test.esp8266-ard.yaml b/tests/components/speed/test.esp8266-ard.yaml index 6ed9949cf5..23a7f43b42 100644 --- a/tests/components/speed/test.esp8266-ard.yaml +++ b/tests/components/speed/test.esp8266-ard.yaml @@ -1,9 +1,5 @@ -output: - - platform: esp8266_pwm - id: fan_output_1 - pin: 12 +substitutions: + output_platform: esp8266_pwm + pin: GPIO14 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 +<<: !include common.yaml diff --git a/tests/components/speed/test.rp2040-ard.yaml b/tests/components/speed/test.rp2040-ard.yaml index 02b572db75..d70f7c74ec 100644 --- a/tests/components/speed/test.rp2040-ard.yaml +++ b/tests/components/speed/test.rp2040-ard.yaml @@ -1,9 +1,5 @@ -output: - - platform: rp2040_pwm - id: fan_output_1 - pin: 12 +substitutions: + output_platform: rp2040_pwm + pin: GPIO4 -fan: - - platform: speed - id: fan_speed - output: fan_output_1 +<<: !include common.yaml diff --git a/tests/components/spi/common.yaml b/tests/components/spi/common.yaml new file mode 100644 index 0000000000..04b4779957 --- /dev/null +++ b/tests/components/spi/common.yaml @@ -0,0 +1,5 @@ +spi: + - id: spi_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} diff --git a/tests/components/spi/test.esp32-ard.yaml b/tests/components/spi/test.esp32-ard.yaml index 1cdcf461dd..448e54fea6 100644 --- a/tests/components/spi/test.esp32-ard.yaml +++ b/tests/components/spi/test.esp32-ard.yaml @@ -1,5 +1,6 @@ -spi: - - id: spi_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + +<<: !include common.yaml diff --git a/tests/components/spi/test.esp32-c3-ard.yaml b/tests/components/spi/test.esp32-c3-ard.yaml index f49470ad07..bfa12b1755 100644 --- a/tests/components/spi/test.esp32-c3-ard.yaml +++ b/tests/components/spi/test.esp32-c3-ard.yaml @@ -1,5 +1,6 @@ -spi: - - id: spi_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/spi/test.esp32-c3-idf.yaml b/tests/components/spi/test.esp32-c3-idf.yaml index f49470ad07..bfa12b1755 100644 --- a/tests/components/spi/test.esp32-c3-idf.yaml +++ b/tests/components/spi/test.esp32-c3-idf.yaml @@ -1,5 +1,6 @@ -spi: - - id: spi_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/spi/test.esp32-idf.yaml b/tests/components/spi/test.esp32-idf.yaml index 1cdcf461dd..448e54fea6 100644 --- a/tests/components/spi/test.esp32-idf.yaml +++ b/tests/components/spi/test.esp32-idf.yaml @@ -1,5 +1,6 @@ -spi: - - id: spi_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 + +<<: !include common.yaml diff --git a/tests/components/spi/test.esp32-s3-idf.yaml b/tests/components/spi/test.esp32-s3-idf.yaml index 8db934023a..d394c5d7a4 100644 --- a/tests/components/spi/test.esp32-s3-idf.yaml +++ b/tests/components/spi/test.esp32-s3-idf.yaml @@ -9,8 +9,8 @@ spi: interface: hardware - id: quad_spi type: quad - clk_pin: 47 interface: spi3 + clk_pin: 47 data_pins: - number: 40 allow_other_uses: false @@ -18,7 +18,7 @@ spi: - 42 - 43 - id: spi_id_3 + interface: any clk_pin: 8 mosi_pin: 9 - interface: any diff --git a/tests/components/spi/test.esp8266-ard.yaml b/tests/components/spi/test.esp8266-ard.yaml index 83f110921f..b9545d4f6a 100644 --- a/tests/components/spi/test.esp8266-ard.yaml +++ b/tests/components/spi/test.esp8266-ard.yaml @@ -1,5 +1,6 @@ -spi: - - id: spi_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + +<<: !include common.yaml diff --git a/tests/components/spi/test.rp2040-ard.yaml b/tests/components/spi/test.rp2040-ard.yaml index 1e39d247fe..81a8acafd8 100644 --- a/tests/components/spi/test.rp2040-ard.yaml +++ b/tests/components/spi/test.rp2040-ard.yaml @@ -1,5 +1,6 @@ -spi: - - id: spi_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/spi_device/common.yaml b/tests/components/spi_device/common.yaml new file mode 100644 index 0000000000..636d82202b --- /dev/null +++ b/tests/components/spi_device/common.yaml @@ -0,0 +1,11 @@ +spi: + - id: spi_device1 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +spi_device: + id: spi_device_test + data_rate: 2MHz + spi_mode: 3 + bit_order: lsb_first diff --git a/tests/components/spi_device/test.esp32-ard.yaml b/tests/components/spi_device/test.esp32-ard.yaml index b539cb3ec4..448e54fea6 100644 --- a/tests/components/spi_device/test.esp32-ard.yaml +++ b/tests/components/spi_device/test.esp32-ard.yaml @@ -1,11 +1,6 @@ -spi: - - id: spi_device1 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 -spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first +<<: !include common.yaml diff --git a/tests/components/spi_device/test.esp32-c3-ard.yaml b/tests/components/spi_device/test.esp32-c3-ard.yaml index 99c0ac1ebb..bfa12b1755 100644 --- a/tests/components/spi_device/test.esp32-c3-ard.yaml +++ b/tests/components/spi_device/test.esp32-c3-ard.yaml @@ -1,11 +1,6 @@ -spi: - - id: spi_device1 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 -spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first +<<: !include common.yaml diff --git a/tests/components/spi_device/test.esp32-c3-idf.yaml b/tests/components/spi_device/test.esp32-c3-idf.yaml index 99c0ac1ebb..bfa12b1755 100644 --- a/tests/components/spi_device/test.esp32-c3-idf.yaml +++ b/tests/components/spi_device/test.esp32-c3-idf.yaml @@ -1,11 +1,6 @@ -spi: - - id: spi_device1 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 -spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first +<<: !include common.yaml diff --git a/tests/components/spi_device/test.esp32-idf.yaml b/tests/components/spi_device/test.esp32-idf.yaml index b539cb3ec4..448e54fea6 100644 --- a/tests/components/spi_device/test.esp32-idf.yaml +++ b/tests/components/spi_device/test.esp32-idf.yaml @@ -1,11 +1,6 @@ -spi: - - id: spi_device1 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + miso_pin: GPIO15 -spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first +<<: !include common.yaml diff --git a/tests/components/spi_device/test.esp8266-ard.yaml b/tests/components/spi_device/test.esp8266-ard.yaml index 988825ce2d..b9545d4f6a 100644 --- a/tests/components/spi_device/test.esp8266-ard.yaml +++ b/tests/components/spi_device/test.esp8266-ard.yaml @@ -1,11 +1,6 @@ -spi: - - id: spi_device1 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 -spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first +<<: !include common.yaml diff --git a/tests/components/spi_device/test.rp2040-ard.yaml b/tests/components/spi_device/test.rp2040-ard.yaml index 6020643f21..81a8acafd8 100644 --- a/tests/components/spi_device/test.rp2040-ard.yaml +++ b/tests/components/spi_device/test.rp2040-ard.yaml @@ -1,11 +1,6 @@ -spi: - - id: spi_device1 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 -spi_device: - id: spi_device_test - data_rate: 2MHz - spi_mode: 3 - bit_order: lsb_first +<<: !include common.yaml diff --git a/tests/components/spi_led_strip/common.yaml b/tests/components/spi_led_strip/common.yaml new file mode 100644 index 0000000000..80b98a63a4 --- /dev/null +++ b/tests/components/spi_led_strip/common.yaml @@ -0,0 +1,12 @@ +spi: + - id: spi_spi_led_strip + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +light: + - platform: spi_led_strip + num_leds: 4 + color_correct: [80%, 60%, 100%] + id: rgb_led + name: RGB LED + data_rate: 8MHz diff --git a/tests/components/spi_led_strip/test.esp32-ard.yaml b/tests/components/spi_led_strip/test.esp32-ard.yaml index f4a760bf4c..8906602ef4 100644 --- a/tests/components/spi_led_strip/test.esp32-ard.yaml +++ b/tests/components/spi_led_strip/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -spi: - - id: spi_spi_led_strip - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 -light: - - platform: spi_led_strip - num_leds: 4 - color_correct: [80%, 60%, 100%] - id: rgb_led - name: "RGB LED" - data_rate: 8MHz +<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp32-c3-ard.yaml b/tests/components/spi_led_strip/test.esp32-c3-ard.yaml index 983ad2863f..a85b587070 100644 --- a/tests/components/spi_led_strip/test.esp32-c3-ard.yaml +++ b/tests/components/spi_led_strip/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -spi: - - id: spi_spi_led_strip - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 -light: - - platform: spi_led_strip - num_leds: 4 - color_correct: [80%, 60%, 100%] - id: rgb_led - name: "RGB LED" - data_rate: 8MHz +<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp32-c3-idf.yaml b/tests/components/spi_led_strip/test.esp32-c3-idf.yaml index 983ad2863f..a85b587070 100644 --- a/tests/components/spi_led_strip/test.esp32-c3-idf.yaml +++ b/tests/components/spi_led_strip/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -spi: - - id: spi_spi_led_strip - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 -light: - - platform: spi_led_strip - num_leds: 4 - color_correct: [80%, 60%, 100%] - id: rgb_led - name: "RGB LED" - data_rate: 8MHz +<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp32-idf.yaml b/tests/components/spi_led_strip/test.esp32-idf.yaml index f4a760bf4c..8906602ef4 100644 --- a/tests/components/spi_led_strip/test.esp32-idf.yaml +++ b/tests/components/spi_led_strip/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -spi: - - id: spi_spi_led_strip - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 -light: - - platform: spi_led_strip - num_leds: 4 - color_correct: [80%, 60%, 100%] - id: rgb_led - name: "RGB LED" - data_rate: 8MHz +<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.esp8266-ard.yaml b/tests/components/spi_led_strip/test.esp8266-ard.yaml index 8e76303b6a..7baaa62ed5 100644 --- a/tests/components/spi_led_strip/test.esp8266-ard.yaml +++ b/tests/components/spi_led_strip/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -spi: - - id: spi_spi_led_strip - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 -light: - - platform: spi_led_strip - num_leds: 4 - color_correct: [80%, 60%, 100%] - id: rgb_led - name: "RGB LED" - data_rate: 8MHz +<<: !include common.yaml diff --git a/tests/components/spi_led_strip/test.rp2040-ard.yaml b/tests/components/spi_led_strip/test.rp2040-ard.yaml index 9d12f1592b..411cfbe00e 100644 --- a/tests/components/spi_led_strip/test.rp2040-ard.yaml +++ b/tests/components/spi_led_strip/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -spi: - - id: spi_spi_led_strip - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 -light: - - platform: spi_led_strip - num_leds: 4 - color_correct: [80%, 60%, 100%] - id: rgb_led - name: "RGB LED" - data_rate: 8MHz +<<: !include common.yaml diff --git a/tests/components/sps30/common.yaml b/tests/components/sps30/common.yaml new file mode 100644 index 0000000000..2fbe2c747a --- /dev/null +++ b/tests/components/sps30/common.yaml @@ -0,0 +1,36 @@ +i2c: + - id: i2c_sps30 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sps30 + pm_1_0: + name: Workshop PM <1µm Weight concentration + id: workshop_PM_1_0 + pm_2_5: + name: Workshop PM <2.5µm Weight concentration + id: workshop_PM_2_5 + pm_4_0: + name: Workshop PM <4µm Weight concentration + id: workshop_PM_4_0 + pm_10_0: + name: Workshop PM <10µm Weight concentration + id: workshop_PM_10_0 + pmc_0_5: + name: Workshop PM <0.5µm Number concentration + id: workshop_PMC_0_5 + pmc_1_0: + name: Workshop PM <1µm Number concentration + id: workshop_PMC_1_0 + pmc_2_5: + name: Workshop PM <2.5µm Number concentration + id: workshop_PMC_2_5 + pmc_4_0: + name: Workshop PM <4µm Number concentration + id: workshop_PMC_4_0 + pmc_10_0: + name: Workshop PM <10µm Number concentration + id: workshop_PMC_10_0 + address: 0x69 + update_interval: 10s diff --git a/tests/components/sps30/test.esp32-ard.yaml b/tests/components/sps30/test.esp32-ard.yaml index f9d1ee4e55..63c3bd6afd 100644 --- a/tests/components/sps30/test.esp32-ard.yaml +++ b/tests/components/sps30/test.esp32-ard.yaml @@ -1,36 +1,5 @@ -i2c: - - id: i2c_sps30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sps30 - pm_1_0: - name: Workshop PM <1µm Weight concentration - id: workshop_PM_1_0 - pm_2_5: - name: Workshop PM <2.5µm Weight concentration - id: workshop_PM_2_5 - pm_4_0: - name: Workshop PM <4µm Weight concentration - id: workshop_PM_4_0 - pm_10_0: - name: Workshop PM <10µm Weight concentration - id: workshop_PM_10_0 - pmc_0_5: - name: Workshop PM <0.5µm Number concentration - id: workshop_PMC_0_5 - pmc_1_0: - name: Workshop PM <1µm Number concentration - id: workshop_PMC_1_0 - pmc_2_5: - name: Workshop PM <2.5µm Number concentration - id: workshop_PMC_2_5 - pmc_4_0: - name: Workshop PM <4µm Number concentration - id: workshop_PMC_4_0 - pmc_10_0: - name: Workshop PM <10µm Number concentration - id: workshop_PMC_10_0 - address: 0x69 - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sps30/test.esp32-c3-ard.yaml b/tests/components/sps30/test.esp32-c3-ard.yaml index e071a00936..ee2c29ca4e 100644 --- a/tests/components/sps30/test.esp32-c3-ard.yaml +++ b/tests/components/sps30/test.esp32-c3-ard.yaml @@ -1,36 +1,5 @@ -i2c: - - id: i2c_sps30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sps30 - pm_1_0: - name: Workshop PM <1µm Weight concentration - id: workshop_PM_1_0 - pm_2_5: - name: Workshop PM <2.5µm Weight concentration - id: workshop_PM_2_5 - pm_4_0: - name: Workshop PM <4µm Weight concentration - id: workshop_PM_4_0 - pm_10_0: - name: Workshop PM <10µm Weight concentration - id: workshop_PM_10_0 - pmc_0_5: - name: Workshop PM <0.5µm Number concentration - id: workshop_PMC_0_5 - pmc_1_0: - name: Workshop PM <1µm Number concentration - id: workshop_PMC_1_0 - pmc_2_5: - name: Workshop PM <2.5µm Number concentration - id: workshop_PMC_2_5 - pmc_4_0: - name: Workshop PM <4µm Number concentration - id: workshop_PMC_4_0 - pmc_10_0: - name: Workshop PM <10µm Number concentration - id: workshop_PMC_10_0 - address: 0x69 - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sps30/test.esp32-c3-idf.yaml b/tests/components/sps30/test.esp32-c3-idf.yaml index e071a00936..ee2c29ca4e 100644 --- a/tests/components/sps30/test.esp32-c3-idf.yaml +++ b/tests/components/sps30/test.esp32-c3-idf.yaml @@ -1,36 +1,5 @@ -i2c: - - id: i2c_sps30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sps30 - pm_1_0: - name: Workshop PM <1µm Weight concentration - id: workshop_PM_1_0 - pm_2_5: - name: Workshop PM <2.5µm Weight concentration - id: workshop_PM_2_5 - pm_4_0: - name: Workshop PM <4µm Weight concentration - id: workshop_PM_4_0 - pm_10_0: - name: Workshop PM <10µm Weight concentration - id: workshop_PM_10_0 - pmc_0_5: - name: Workshop PM <0.5µm Number concentration - id: workshop_PMC_0_5 - pmc_1_0: - name: Workshop PM <1µm Number concentration - id: workshop_PMC_1_0 - pmc_2_5: - name: Workshop PM <2.5µm Number concentration - id: workshop_PMC_2_5 - pmc_4_0: - name: Workshop PM <4µm Number concentration - id: workshop_PMC_4_0 - pmc_10_0: - name: Workshop PM <10µm Number concentration - id: workshop_PMC_10_0 - address: 0x69 - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sps30/test.esp32-idf.yaml b/tests/components/sps30/test.esp32-idf.yaml index f9d1ee4e55..63c3bd6afd 100644 --- a/tests/components/sps30/test.esp32-idf.yaml +++ b/tests/components/sps30/test.esp32-idf.yaml @@ -1,36 +1,5 @@ -i2c: - - id: i2c_sps30 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sps30 - pm_1_0: - name: Workshop PM <1µm Weight concentration - id: workshop_PM_1_0 - pm_2_5: - name: Workshop PM <2.5µm Weight concentration - id: workshop_PM_2_5 - pm_4_0: - name: Workshop PM <4µm Weight concentration - id: workshop_PM_4_0 - pm_10_0: - name: Workshop PM <10µm Weight concentration - id: workshop_PM_10_0 - pmc_0_5: - name: Workshop PM <0.5µm Number concentration - id: workshop_PMC_0_5 - pmc_1_0: - name: Workshop PM <1µm Number concentration - id: workshop_PMC_1_0 - pmc_2_5: - name: Workshop PM <2.5µm Number concentration - id: workshop_PMC_2_5 - pmc_4_0: - name: Workshop PM <4µm Number concentration - id: workshop_PMC_4_0 - pmc_10_0: - name: Workshop PM <10µm Number concentration - id: workshop_PMC_10_0 - address: 0x69 - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sps30/test.esp8266-ard.yaml b/tests/components/sps30/test.esp8266-ard.yaml index e071a00936..ee2c29ca4e 100644 --- a/tests/components/sps30/test.esp8266-ard.yaml +++ b/tests/components/sps30/test.esp8266-ard.yaml @@ -1,36 +1,5 @@ -i2c: - - id: i2c_sps30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sps30 - pm_1_0: - name: Workshop PM <1µm Weight concentration - id: workshop_PM_1_0 - pm_2_5: - name: Workshop PM <2.5µm Weight concentration - id: workshop_PM_2_5 - pm_4_0: - name: Workshop PM <4µm Weight concentration - id: workshop_PM_4_0 - pm_10_0: - name: Workshop PM <10µm Weight concentration - id: workshop_PM_10_0 - pmc_0_5: - name: Workshop PM <0.5µm Number concentration - id: workshop_PMC_0_5 - pmc_1_0: - name: Workshop PM <1µm Number concentration - id: workshop_PMC_1_0 - pmc_2_5: - name: Workshop PM <2.5µm Number concentration - id: workshop_PMC_2_5 - pmc_4_0: - name: Workshop PM <4µm Number concentration - id: workshop_PMC_4_0 - pmc_10_0: - name: Workshop PM <10µm Number concentration - id: workshop_PMC_10_0 - address: 0x69 - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/sps30/test.rp2040-ard.yaml b/tests/components/sps30/test.rp2040-ard.yaml index e071a00936..ee2c29ca4e 100644 --- a/tests/components/sps30/test.rp2040-ard.yaml +++ b/tests/components/sps30/test.rp2040-ard.yaml @@ -1,36 +1,5 @@ -i2c: - - id: i2c_sps30 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sps30 - pm_1_0: - name: Workshop PM <1µm Weight concentration - id: workshop_PM_1_0 - pm_2_5: - name: Workshop PM <2.5µm Weight concentration - id: workshop_PM_2_5 - pm_4_0: - name: Workshop PM <4µm Weight concentration - id: workshop_PM_4_0 - pm_10_0: - name: Workshop PM <10µm Weight concentration - id: workshop_PM_10_0 - pmc_0_5: - name: Workshop PM <0.5µm Number concentration - id: workshop_PMC_0_5 - pmc_1_0: - name: Workshop PM <1µm Number concentration - id: workshop_PMC_1_0 - pmc_2_5: - name: Workshop PM <2.5µm Number concentration - id: workshop_PMC_2_5 - pmc_4_0: - name: Workshop PM <4µm Number concentration - id: workshop_PMC_4_0 - pmc_10_0: - name: Workshop PM <10µm Number concentration - id: workshop_PMC_10_0 - address: 0x69 - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/common.yaml b/tests/components/ssd1306_i2c/common.yaml new file mode 100644 index 0000000000..d17f83f03a --- /dev/null +++ b/tests/components/ssd1306_i2c/common.yaml @@ -0,0 +1,25 @@ +i2c: + - id: i2c_ssd1306_i2c + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1306_i2c + model: SSD1306_128X64 + reset_pin: ${reset_pin} + address: 0x3C + id: display1 + contrast: 60% + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, 10, 10); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1306_i2c/test.esp32-ard.yaml b/tests/components/ssd1306_i2c/test.esp32-ard.yaml index dddc67309c..1ca773e06c 100644 --- a/tests/components/ssd1306_i2c/test.esp32-ard.yaml +++ b/tests/components/ssd1306_i2c/test.esp32-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_ssd1306_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - model: SSD1306_128X64 - reset_pin: 3 - address: 0x3C - id: display1 - contrast: 60% - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml b/tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml index f4a301db51..4eaff7fa4a 100644 --- a/tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1306_i2c/test.esp32-c3-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_ssd1306_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - model: SSD1306_128X64 - reset_pin: 3 - address: 0x3C - id: display1 - contrast: 60% - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml b/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml index f4a301db51..4eaff7fa4a 100644 --- a/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1306_i2c/test.esp32-c3-idf.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_ssd1306_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - model: SSD1306_128X64 - reset_pin: 3 - address: 0x3C - id: display1 - contrast: 60% - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp32-idf.yaml b/tests/components/ssd1306_i2c/test.esp32-idf.yaml index dddc67309c..1ca773e06c 100644 --- a/tests/components/ssd1306_i2c/test.esp32-idf.yaml +++ b/tests/components/ssd1306_i2c/test.esp32-idf.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_ssd1306_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1306_i2c - model: SSD1306_128X64 - reset_pin: 3 - address: 0x3C - id: display1 - contrast: 60% - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.esp8266-ard.yaml b/tests/components/ssd1306_i2c/test.esp8266-ard.yaml index f4a301db51..af91c21a0d 100644 --- a/tests/components/ssd1306_i2c/test.esp8266-ard.yaml +++ b/tests/components/ssd1306_i2c/test.esp8266-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_ssd1306_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO2 -display: - - platform: ssd1306_i2c - model: SSD1306_128X64 - reset_pin: 3 - address: 0x3C - id: display1 - contrast: 60% - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_i2c/test.rp2040-ard.yaml b/tests/components/ssd1306_i2c/test.rp2040-ard.yaml index f4a301db51..4eaff7fa4a 100644 --- a/tests/components/ssd1306_i2c/test.rp2040-ard.yaml +++ b/tests/components/ssd1306_i2c/test.rp2040-ard.yaml @@ -1,25 +1,6 @@ -i2c: - - id: i2c_ssd1306_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - model: SSD1306_128X64 - reset_pin: 3 - address: 0x3C - id: display1 - contrast: 60% - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/common.yaml b/tests/components/ssd1306_spi/common.yaml new file mode 100644 index 0000000000..71705f32d2 --- /dev/null +++ b/tests/components/ssd1306_spi/common.yaml @@ -0,0 +1,24 @@ +spi: + - id: spi_ssd1306_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ssd1306_spi + model: SSD1306 128x64 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1306_spi/test.esp32-ard.yaml b/tests/components/ssd1306_spi/test.esp32-ard.yaml index b0e5e0f1a2..bad5241f79 100644 --- a/tests/components/ssd1306_spi/test.esp32-ard.yaml +++ b/tests/components/ssd1306_spi/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1306_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1306_spi - model: SSD1306 128x64 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1306_spi/test.esp32-c3-ard.yaml index 01b2d0e4a8..c5c932c92c 100644 --- a/tests/components/ssd1306_spi/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1306_spi/test.esp32-c3-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1306_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1306_spi - model: SSD1306 128x64 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml index 01b2d0e4a8..c5c932c92c 100644 --- a/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1306_spi/test.esp32-c3-idf.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1306_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1306_spi - model: SSD1306 128x64 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp32-idf.yaml b/tests/components/ssd1306_spi/test.esp32-idf.yaml index b0e5e0f1a2..bad5241f79 100644 --- a/tests/components/ssd1306_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1306_spi/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1306_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1306_spi - model: SSD1306 128x64 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.esp8266-ard.yaml b/tests/components/ssd1306_spi/test.esp8266-ard.yaml index 135e364bb2..3f023a60eb 100644 --- a/tests/components/ssd1306_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1306_spi/test.esp8266-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1306_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_spi - model: SSD1306 128x64 - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1306_spi/test.rp2040-ard.yaml b/tests/components/ssd1306_spi/test.rp2040-ard.yaml index 94c4b85158..d7fd6ee294 100644 --- a/tests/components/ssd1306_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1306_spi/test.rp2040-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1306_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_spi - model: SSD1306 128x64 - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/common.yaml b/tests/components/ssd1322_spi/common.yaml new file mode 100644 index 0000000000..b67392794c --- /dev/null +++ b/tests/components/ssd1322_spi/common.yaml @@ -0,0 +1,24 @@ +spi: + - id: spi_ssd1322_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ssd1322_spi + model: SSD1322 256x64 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1322_spi/test.esp32-ard.yaml b/tests/components/ssd1322_spi/test.esp32-ard.yaml index aa6d0fbf01..bad5241f79 100644 --- a/tests/components/ssd1322_spi/test.esp32-ard.yaml +++ b/tests/components/ssd1322_spi/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1322_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1322_spi - model: SSD1322 256x64 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1322_spi/test.esp32-c3-ard.yaml index 4fa9f86594..c5c932c92c 100644 --- a/tests/components/ssd1322_spi/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1322_spi/test.esp32-c3-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1322_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1322_spi - model: SSD1322 256x64 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml index 4fa9f86594..c5c932c92c 100644 --- a/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1322_spi/test.esp32-c3-idf.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1322_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1322_spi - model: SSD1322 256x64 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp32-idf.yaml b/tests/components/ssd1322_spi/test.esp32-idf.yaml index aa6d0fbf01..bad5241f79 100644 --- a/tests/components/ssd1322_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1322_spi/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1322_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1322_spi - model: SSD1322 256x64 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.esp8266-ard.yaml b/tests/components/ssd1322_spi/test.esp8266-ard.yaml index a5aa565c09..3f023a60eb 100644 --- a/tests/components/ssd1322_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1322_spi/test.esp8266-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1322_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1322_spi - model: SSD1322 256x64 - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1322_spi/test.rp2040-ard.yaml b/tests/components/ssd1322_spi/test.rp2040-ard.yaml index 59544e7878..d7fd6ee294 100644 --- a/tests/components/ssd1322_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1322_spi/test.rp2040-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1322_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1322_spi - model: SSD1322 256x64 - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/common.yaml b/tests/components/ssd1325_spi/common.yaml new file mode 100644 index 0000000000..eaa8b619ca --- /dev/null +++ b/tests/components/ssd1325_spi/common.yaml @@ -0,0 +1,24 @@ +spi: + - id: spi_ssd1325_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ssd1325_spi + model: SSD1325 128x64 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1325_spi/test.esp32-ard.yaml b/tests/components/ssd1325_spi/test.esp32-ard.yaml index 84d94eff28..bad5241f79 100644 --- a/tests/components/ssd1325_spi/test.esp32-ard.yaml +++ b/tests/components/ssd1325_spi/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1325_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1325_spi - model: SSD1325 128x64 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1325_spi/test.esp32-c3-ard.yaml index 0fa8cb6488..c5c932c92c 100644 --- a/tests/components/ssd1325_spi/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1325_spi/test.esp32-c3-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1325_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1325_spi - model: SSD1325 128x64 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml index 0fa8cb6488..c5c932c92c 100644 --- a/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1325_spi/test.esp32-c3-idf.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1325_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1325_spi - model: SSD1325 128x64 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp32-idf.yaml b/tests/components/ssd1325_spi/test.esp32-idf.yaml index 84d94eff28..bad5241f79 100644 --- a/tests/components/ssd1325_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1325_spi/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1325_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1325_spi - model: SSD1325 128x64 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.esp8266-ard.yaml b/tests/components/ssd1325_spi/test.esp8266-ard.yaml index 9d7e483585..3f023a60eb 100644 --- a/tests/components/ssd1325_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1325_spi/test.esp8266-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1325_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1325_spi - model: SSD1325 128x64 - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1325_spi/test.rp2040-ard.yaml b/tests/components/ssd1325_spi/test.rp2040-ard.yaml index 869663c19d..d7fd6ee294 100644 --- a/tests/components/ssd1325_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1325_spi/test.rp2040-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1325_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1325_spi - model: SSD1325 128x64 - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/common.yaml b/tests/components/ssd1327_i2c/common.yaml new file mode 100644 index 0000000000..72a122c3d7 --- /dev/null +++ b/tests/components/ssd1327_i2c/common.yaml @@ -0,0 +1,24 @@ +i2c: + - id: i2c_ssd1327_i2c + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1327_i2c + model: SSD1327_128x128 + reset_pin: ${reset_pin} + address: 0x3C + id: display1 + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, 10, 10); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1327_i2c/test.esp32-ard.yaml b/tests/components/ssd1327_i2c/test.esp32-ard.yaml index e72a9c7b7a..1ca773e06c 100644 --- a/tests/components/ssd1327_i2c/test.esp32-ard.yaml +++ b/tests/components/ssd1327_i2c/test.esp32-ard.yaml @@ -1,24 +1,6 @@ -i2c: - - id: i2c_ssd1327_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1327_i2c - model: SSD1327_128x128 - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml b/tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml index cd28795ff1..4eaff7fa4a 100644 --- a/tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1327_i2c/test.esp32-c3-ard.yaml @@ -1,24 +1,6 @@ -i2c: - - id: i2c_ssd1327_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: ssd1327_i2c - model: SSD1327_128x128 - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml b/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml index cd28795ff1..4eaff7fa4a 100644 --- a/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1327_i2c/test.esp32-c3-idf.yaml @@ -1,24 +1,6 @@ -i2c: - - id: i2c_ssd1327_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: ssd1327_i2c - model: SSD1327_128x128 - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp32-idf.yaml b/tests/components/ssd1327_i2c/test.esp32-idf.yaml index e72a9c7b7a..1ca773e06c 100644 --- a/tests/components/ssd1327_i2c/test.esp32-idf.yaml +++ b/tests/components/ssd1327_i2c/test.esp32-idf.yaml @@ -1,24 +1,6 @@ -i2c: - - id: i2c_ssd1327_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: ssd1327_i2c - model: SSD1327_128x128 - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.esp8266-ard.yaml b/tests/components/ssd1327_i2c/test.esp8266-ard.yaml index cd28795ff1..af91c21a0d 100644 --- a/tests/components/ssd1327_i2c/test.esp8266-ard.yaml +++ b/tests/components/ssd1327_i2c/test.esp8266-ard.yaml @@ -1,24 +1,6 @@ -i2c: - - id: i2c_ssd1327_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO2 -display: - - platform: ssd1327_i2c - model: SSD1327_128x128 - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_i2c/test.rp2040-ard.yaml b/tests/components/ssd1327_i2c/test.rp2040-ard.yaml index cd28795ff1..4eaff7fa4a 100644 --- a/tests/components/ssd1327_i2c/test.rp2040-ard.yaml +++ b/tests/components/ssd1327_i2c/test.rp2040-ard.yaml @@ -1,24 +1,6 @@ -i2c: - - id: i2c_ssd1327_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: ssd1327_i2c - model: SSD1327_128x128 - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/common.yaml b/tests/components/ssd1327_spi/common.yaml new file mode 100644 index 0000000000..85f4d4736b --- /dev/null +++ b/tests/components/ssd1327_spi/common.yaml @@ -0,0 +1,24 @@ +spi: + - id: spi_ssd1327_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ssd1327_spi + model: SSD1327 128x128 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1327_spi/test.esp32-ard.yaml b/tests/components/ssd1327_spi/test.esp32-ard.yaml index e103ded187..bad5241f79 100644 --- a/tests/components/ssd1327_spi/test.esp32-ard.yaml +++ b/tests/components/ssd1327_spi/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1327_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1327_spi - model: SSD1327 128x128 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1327_spi/test.esp32-c3-ard.yaml index ec5795d716..c5c932c92c 100644 --- a/tests/components/ssd1327_spi/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1327_spi/test.esp32-c3-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1327_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1327_spi - model: SSD1327 128x128 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml index ec5795d716..c5c932c92c 100644 --- a/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1327_spi/test.esp32-c3-idf.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1327_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1327_spi - model: SSD1327 128x128 - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp32-idf.yaml b/tests/components/ssd1327_spi/test.esp32-idf.yaml index e103ded187..bad5241f79 100644 --- a/tests/components/ssd1327_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1327_spi/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1327_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1327_spi - model: SSD1327 128x128 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.esp8266-ard.yaml b/tests/components/ssd1327_spi/test.esp8266-ard.yaml index 30455d24ee..3f023a60eb 100644 --- a/tests/components/ssd1327_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1327_spi/test.esp8266-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1327_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1327_spi - model: SSD1327 128x128 - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1327_spi/test.rp2040-ard.yaml b/tests/components/ssd1327_spi/test.rp2040-ard.yaml index f819ab2c41..d7fd6ee294 100644 --- a/tests/components/ssd1327_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1327_spi/test.rp2040-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1327_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1327_spi - model: SSD1327 128x128 - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/common.yaml b/tests/components/ssd1331_spi/common.yaml new file mode 100644 index 0000000000..40726101e8 --- /dev/null +++ b/tests/components/ssd1331_spi/common.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_ssd1331_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ssd1331_spi + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1331_spi/test.esp32-ard.yaml b/tests/components/ssd1331_spi/test.esp32-ard.yaml index e9eb8ea9ad..bad5241f79 100644 --- a/tests/components/ssd1331_spi/test.esp32-ard.yaml +++ b/tests/components/ssd1331_spi/test.esp32-ard.yaml @@ -1,24 +1,8 @@ -spi: - - id: spi_ssd1331_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1331_spi - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1331_spi/test.esp32-c3-ard.yaml index 9a35918faf..c5c932c92c 100644 --- a/tests/components/ssd1331_spi/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1331_spi/test.esp32-c3-ard.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_ssd1331_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1331_spi - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml index 9a35918faf..c5c932c92c 100644 --- a/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1331_spi/test.esp32-c3-idf.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_ssd1331_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1331_spi - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp32-idf.yaml b/tests/components/ssd1331_spi/test.esp32-idf.yaml index e9eb8ea9ad..bad5241f79 100644 --- a/tests/components/ssd1331_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1331_spi/test.esp32-idf.yaml @@ -1,24 +1,8 @@ -spi: - - id: spi_ssd1331_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1331_spi - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.esp8266-ard.yaml b/tests/components/ssd1331_spi/test.esp8266-ard.yaml index 3b319ef38e..3f023a60eb 100644 --- a/tests/components/ssd1331_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1331_spi/test.esp8266-ard.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_ssd1331_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1331_spi - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1331_spi/test.rp2040-ard.yaml b/tests/components/ssd1331_spi/test.rp2040-ard.yaml index 947685b07a..d7fd6ee294 100644 --- a/tests/components/ssd1331_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1331_spi/test.rp2040-ard.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_ssd1331_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1331_spi - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/common.yaml b/tests/components/ssd1351_spi/common.yaml new file mode 100644 index 0000000000..fa495ff0c3 --- /dev/null +++ b/tests/components/ssd1351_spi/common.yaml @@ -0,0 +1,24 @@ +spi: + - id: spi_ssd1351_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: ssd1351_spi + model: "SSD1351 128x128" + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/ssd1351_spi/test.esp32-ard.yaml b/tests/components/ssd1351_spi/test.esp32-ard.yaml index 8342cb972b..bad5241f79 100644 --- a/tests/components/ssd1351_spi/test.esp32-ard.yaml +++ b/tests/components/ssd1351_spi/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1351_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1351_spi - model: "SSD1351 128x128" - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp32-c3-ard.yaml b/tests/components/ssd1351_spi/test.esp32-c3-ard.yaml index 2a7266f502..c5c932c92c 100644 --- a/tests/components/ssd1351_spi/test.esp32-c3-ard.yaml +++ b/tests/components/ssd1351_spi/test.esp32-c3-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1351_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1351_spi - model: "SSD1351 128x128" - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml b/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml index 2a7266f502..c5c932c92c 100644 --- a/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml +++ b/tests/components/ssd1351_spi/test.esp32-c3-idf.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1351_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: ssd1351_spi - model: "SSD1351 128x128" - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp32-idf.yaml b/tests/components/ssd1351_spi/test.esp32-idf.yaml index 8342cb972b..bad5241f79 100644 --- a/tests/components/ssd1351_spi/test.esp32-idf.yaml +++ b/tests/components/ssd1351_spi/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_ssd1351_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: ssd1351_spi - model: "SSD1351 128x128" - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.esp8266-ard.yaml b/tests/components/ssd1351_spi/test.esp8266-ard.yaml index 7ed9a31dde..3f023a60eb 100644 --- a/tests/components/ssd1351_spi/test.esp8266-ard.yaml +++ b/tests/components/ssd1351_spi/test.esp8266-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1351_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1351_spi - model: "SSD1351 128x128" - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/ssd1351_spi/test.rp2040-ard.yaml b/tests/components/ssd1351_spi/test.rp2040-ard.yaml index 72936d046b..d7fd6ee294 100644 --- a/tests/components/ssd1351_spi/test.rp2040-ard.yaml +++ b/tests/components/ssd1351_spi/test.rp2040-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_ssd1351_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1351_spi - model: "SSD1351 128x128" - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_i2c/common.yaml b/tests/components/st7567_i2c/common.yaml new file mode 100644 index 0000000000..41c65e5110 --- /dev/null +++ b/tests/components/st7567_i2c/common.yaml @@ -0,0 +1,23 @@ +i2c: + - id: i2c_st7567_i2c + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: st7567_i2c + reset_pin: ${reset_pin} + address: 0x3C + id: display1 + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, 10, 10); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7567_i2c/test.esp32-ard.yaml b/tests/components/st7567_i2c/test.esp32-ard.yaml index b7aee61b68..1ca773e06c 100644 --- a/tests/components/st7567_i2c/test.esp32-ard.yaml +++ b/tests/components/st7567_i2c/test.esp32-ard.yaml @@ -1,23 +1,6 @@ -i2c: - - id: i2c_st7567_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: st7567_i2c - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp32-c3-ard.yaml b/tests/components/st7567_i2c/test.esp32-c3-ard.yaml index d779040500..4eaff7fa4a 100644 --- a/tests/components/st7567_i2c/test.esp32-c3-ard.yaml +++ b/tests/components/st7567_i2c/test.esp32-c3-ard.yaml @@ -1,23 +1,6 @@ -i2c: - - id: i2c_st7567_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: st7567_i2c - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp32-c3-idf.yaml b/tests/components/st7567_i2c/test.esp32-c3-idf.yaml index d779040500..4eaff7fa4a 100644 --- a/tests/components/st7567_i2c/test.esp32-c3-idf.yaml +++ b/tests/components/st7567_i2c/test.esp32-c3-idf.yaml @@ -1,23 +1,6 @@ -i2c: - - id: i2c_st7567_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: st7567_i2c - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp32-idf.yaml b/tests/components/st7567_i2c/test.esp32-idf.yaml index b7aee61b68..1ca773e06c 100644 --- a/tests/components/st7567_i2c/test.esp32-idf.yaml +++ b/tests/components/st7567_i2c/test.esp32-idf.yaml @@ -1,23 +1,6 @@ -i2c: - - id: i2c_st7567_i2c - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + reset_pin: GPIO15 -display: - - platform: st7567_i2c - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.esp8266-ard.yaml b/tests/components/st7567_i2c/test.esp8266-ard.yaml index d779040500..af91c21a0d 100644 --- a/tests/components/st7567_i2c/test.esp8266-ard.yaml +++ b/tests/components/st7567_i2c/test.esp8266-ard.yaml @@ -1,23 +1,6 @@ -i2c: - - id: i2c_st7567_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO2 -display: - - platform: st7567_i2c - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_i2c/test.rp2040-ard.yaml b/tests/components/st7567_i2c/test.rp2040-ard.yaml index d779040500..4eaff7fa4a 100644 --- a/tests/components/st7567_i2c/test.rp2040-ard.yaml +++ b/tests/components/st7567_i2c/test.rp2040-ard.yaml @@ -1,23 +1,6 @@ -i2c: - - id: i2c_st7567_i2c - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 + reset_pin: GPIO3 -display: - - platform: st7567_i2c - reset_pin: 3 - address: 0x3C - id: display1 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, 10, 10); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_spi/common.yaml b/tests/components/st7567_spi/common.yaml new file mode 100644 index 0000000000..037a700239 --- /dev/null +++ b/tests/components/st7567_spi/common.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_st7567_spi + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: st7567_spi + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7567_spi/test.esp32-ard.yaml b/tests/components/st7567_spi/test.esp32-ard.yaml index bb4530248f..bad5241f79 100644 --- a/tests/components/st7567_spi/test.esp32-ard.yaml +++ b/tests/components/st7567_spi/test.esp32-ard.yaml @@ -1,24 +1,8 @@ -spi: - - id: spi_st7567_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: st7567_spi - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp32-c3-ard.yaml b/tests/components/st7567_spi/test.esp32-c3-ard.yaml index b799ce7302..c5c932c92c 100644 --- a/tests/components/st7567_spi/test.esp32-c3-ard.yaml +++ b/tests/components/st7567_spi/test.esp32-c3-ard.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_st7567_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: st7567_spi - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp32-c3-idf.yaml b/tests/components/st7567_spi/test.esp32-c3-idf.yaml index b799ce7302..c5c932c92c 100644 --- a/tests/components/st7567_spi/test.esp32-c3-idf.yaml +++ b/tests/components/st7567_spi/test.esp32-c3-idf.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_st7567_spi - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: st7567_spi - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp32-idf.yaml b/tests/components/st7567_spi/test.esp32-idf.yaml index bb4530248f..bad5241f79 100644 --- a/tests/components/st7567_spi/test.esp32-idf.yaml +++ b/tests/components/st7567_spi/test.esp32-idf.yaml @@ -1,24 +1,8 @@ -spi: - - id: spi_st7567_spi - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: st7567_spi - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.esp8266-ard.yaml b/tests/components/st7567_spi/test.esp8266-ard.yaml index bbc47e67f6..3f023a60eb 100644 --- a/tests/components/st7567_spi/test.esp8266-ard.yaml +++ b/tests/components/st7567_spi/test.esp8266-ard.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_st7567_spi - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: st7567_spi - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7567_spi/test.rp2040-ard.yaml b/tests/components/st7567_spi/test.rp2040-ard.yaml index 1b491101a8..d7fd6ee294 100644 --- a/tests/components/st7567_spi/test.rp2040-ard.yaml +++ b/tests/components/st7567_spi/test.rp2040-ard.yaml @@ -1,24 +1,9 @@ -spi: - - id: spi_st7567_spi - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: st7567_spi - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7701s/common.yaml b/tests/components/st7701s/common.yaml index 6e4fccc9cb..b94fadfe52 100644 --- a/tests/components/st7701s/common.yaml +++ b/tests/components/st7701s/common.yaml @@ -1,21 +1,7 @@ -psram: - mode: octal - speed: 80MHz spi: - - id: lcd_spi - clk_pin: 41 - mosi_pin: 48 - -i2c: - sda: 39 - scl: 40 - scan: false - id: bus_a - -pca9554: - - id: p_c_a - pin_count: 16 - address: 0x20 + - id: spi_st7701s + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} display: - platform: st7701s @@ -28,16 +14,12 @@ display: transform: mirror_x: true mirror_y: true - cs_pin: - pca9554: p_c_a - number: 4 - reset_pin: - pca9554: p_c_a - number: 5 - de_pin: 18 - hsync_pin: 16 - vsync_pin: 17 - pclk_pin: 21 + cs_pin: ${cs_pin} + reset_pin: ${reset_pin} + de_pin: ${de_pin} + hsync_pin: ${hsync_pin} + vsync_pin: ${vsync_pin} + pclk_pin: ${pclk_pin} init_sequence: - 1 - [0x23, 0xA, 0xB] @@ -45,13 +27,11 @@ display: - [0x23, 0xA, 0xB] - delay 0.2s data_pins: - - number: 0 - ignore_strapping_warning: true + - 0 - 1 - 2 - 3 - - number: 4 - ignore_strapping_warning: false + - 4 - 5 - 6 - 7 diff --git a/tests/components/st7701s/test.esp32-s3-idf.yaml b/tests/components/st7701s/test.esp32-s3-idf.yaml index dade44d145..cd09b31f6e 100644 --- a/tests/components/st7701s/test.esp32-s3-idf.yaml +++ b/tests/components/st7701s/test.esp32-s3-idf.yaml @@ -1 +1,11 @@ +substitutions: + clk_pin: GPIO41 + mosi_pin: GPIO48 + cs_pin: GPIO44 + de_pin: GPIO18 + reset_pin: GPIO47 + hsync_pin: GPIO16 + vsync_pin: GPIO17 + pclk_pin: GPIO21 + <<: !include common.yaml diff --git a/tests/components/st7735/common.yaml b/tests/components/st7735/common.yaml new file mode 100644 index 0000000000..c140652eda --- /dev/null +++ b/tests/components/st7735/common.yaml @@ -0,0 +1,28 @@ +spi: + - id: spi_st7735 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: st7735 + model: INITR_18BLACKTAB + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + device_width: 128 + device_height: 160 + col_start: 0 + row_start: 0 + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7735/test.esp32-ard.yaml b/tests/components/st7735/test.esp32-ard.yaml index fd3f6cade6..bad5241f79 100644 --- a/tests/components/st7735/test.esp32-ard.yaml +++ b/tests/components/st7735/test.esp32-ard.yaml @@ -1,29 +1,8 @@ -spi: - - id: spi_st7735 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: st7735 - model: "INITR_18BLACKTAB" - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - device_width: 128 - device_height: 160 - col_start: 0 - row_start: 0 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7735/test.esp32-c3-ard.yaml b/tests/components/st7735/test.esp32-c3-ard.yaml index fc6c2421c4..c5c932c92c 100644 --- a/tests/components/st7735/test.esp32-c3-ard.yaml +++ b/tests/components/st7735/test.esp32-c3-ard.yaml @@ -1,29 +1,9 @@ -spi: - - id: spi_st7735 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: st7735 - model: "INITR_18BLACKTAB" - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - device_width: 128 - device_height: 160 - col_start: 0 - row_start: 0 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7735/test.esp32-c3-idf.yaml b/tests/components/st7735/test.esp32-c3-idf.yaml index fc6c2421c4..c5c932c92c 100644 --- a/tests/components/st7735/test.esp32-c3-idf.yaml +++ b/tests/components/st7735/test.esp32-c3-idf.yaml @@ -1,29 +1,9 @@ -spi: - - id: spi_st7735 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: st7735 - model: "INITR_18BLACKTAB" - cs_pin: 2 - dc_pin: 3 - reset_pin: 4 - device_width: 128 - device_height: 160 - col_start: 0 - row_start: 0 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7735/test.esp32-idf.yaml b/tests/components/st7735/test.esp32-idf.yaml index fd3f6cade6..bad5241f79 100644 --- a/tests/components/st7735/test.esp32-idf.yaml +++ b/tests/components/st7735/test.esp32-idf.yaml @@ -1,29 +1,8 @@ -spi: - - id: spi_st7735 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: st7735 - model: "INITR_18BLACKTAB" - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - device_width: 128 - device_height: 160 - col_start: 0 - row_start: 0 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7735/test.esp8266-ard.yaml b/tests/components/st7735/test.esp8266-ard.yaml index ea8fa93c36..3f023a60eb 100644 --- a/tests/components/st7735/test.esp8266-ard.yaml +++ b/tests/components/st7735/test.esp8266-ard.yaml @@ -1,29 +1,9 @@ -spi: - - id: spi_st7735 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: st7735 - model: "INITR_18BLACKTAB" - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - device_width: 128 - device_height: 160 - col_start: 0 - row_start: 0 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7735/test.rp2040-ard.yaml b/tests/components/st7735/test.rp2040-ard.yaml index 828f9a3ae1..d7fd6ee294 100644 --- a/tests/components/st7735/test.rp2040-ard.yaml +++ b/tests/components/st7735/test.rp2040-ard.yaml @@ -1,29 +1,9 @@ -spi: - - id: spi_st7735 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: st7735 - model: "INITR_18BLACKTAB" - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - device_width: 128 - device_height: 160 - col_start: 0 - row_start: 0 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7789v/common.yaml b/tests/components/st7789v/common.yaml new file mode 100644 index 0000000000..d5f74809e0 --- /dev/null +++ b/tests/components/st7789v/common.yaml @@ -0,0 +1,24 @@ +spi: + - id: spi_st7789v + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: st7789v + model: TTGO TDisplay 135x240 + cs_pin: ${cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7789v/test.esp32-ard.yaml b/tests/components/st7789v/test.esp32-ard.yaml index 54a9db6da1..bad5241f79 100644 --- a/tests/components/st7789v/test.esp32-ard.yaml +++ b/tests/components/st7789v/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_st7789v - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: st7789v - model: TTGO TDisplay 135x240 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7789v/test.esp32-c3-ard.yaml b/tests/components/st7789v/test.esp32-c3-ard.yaml index 1cb8d22fcb..c5c932c92c 100644 --- a/tests/components/st7789v/test.esp32-c3-ard.yaml +++ b/tests/components/st7789v/test.esp32-c3-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_st7789v - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: st7789v - model: TTGO TDisplay 135x240 - cs_pin: 2 - dc_pin: 3 - reset_pin: 8 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7789v/test.esp32-c3-idf.yaml b/tests/components/st7789v/test.esp32-c3-idf.yaml index 1cb8d22fcb..c5c932c92c 100644 --- a/tests/components/st7789v/test.esp32-c3-idf.yaml +++ b/tests/components/st7789v/test.esp32-c3-idf.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_st7789v - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 + dc_pin: GPIO9 + reset_pin: GPIO10 -display: - - platform: st7789v - model: TTGO TDisplay 135x240 - cs_pin: 2 - dc_pin: 3 - reset_pin: 8 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7789v/test.esp32-idf.yaml b/tests/components/st7789v/test.esp32-idf.yaml index 54a9db6da1..bad5241f79 100644 --- a/tests/components/st7789v/test.esp32-idf.yaml +++ b/tests/components/st7789v/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -spi: - - id: spi_st7789v - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 + dc_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: st7789v - model: TTGO TDisplay 135x240 - cs_pin: 12 - dc_pin: 13 - reset_pin: 14 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7789v/test.esp8266-ard.yaml b/tests/components/st7789v/test.esp8266-ard.yaml index deeceb8c9a..3f023a60eb 100644 --- a/tests/components/st7789v/test.esp8266-ard.yaml +++ b/tests/components/st7789v/test.esp8266-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_st7789v - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: st7789v - model: TTGO TDisplay 135x240 - cs_pin: 15 - dc_pin: 16 - reset_pin: 5 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7789v/test.rp2040-ard.yaml b/tests/components/st7789v/test.rp2040-ard.yaml index 778aa2aa55..d7fd6ee294 100644 --- a/tests/components/st7789v/test.rp2040-ard.yaml +++ b/tests/components/st7789v/test.rp2040-ard.yaml @@ -1,25 +1,9 @@ -spi: - - id: spi_st7789v - clk_pin: 2 - mosi_pin: 3 - miso_pin: 8 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 + dc_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: st7789v - model: TTGO TDisplay 135x240 - cs_pin: 5 - dc_pin: 6 - reset_pin: 7 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7920/common.yaml b/tests/components/st7920/common.yaml new file mode 100644 index 0000000000..9ede271f01 --- /dev/null +++ b/tests/components/st7920/common.yaml @@ -0,0 +1,23 @@ +spi: + - id: spi_st7920 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: st7920 + cs_pin: ${cs_pin} + height: 128 + width: 64 + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - id: page2 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); diff --git a/tests/components/st7920/test.esp32-ard.yaml b/tests/components/st7920/test.esp32-ard.yaml index cdcbc85642..04d2633d2b 100644 --- a/tests/components/st7920/test.esp32-ard.yaml +++ b/tests/components/st7920/test.esp32-ard.yaml @@ -1,24 +1,6 @@ -spi: - - id: spi_st7920 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 -display: - - platform: st7920 - cs_pin: 12 - height: 128 - width: 64 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7920/test.esp32-c3-ard.yaml b/tests/components/st7920/test.esp32-c3-ard.yaml index 84ae88461f..2415ba5dc6 100644 --- a/tests/components/st7920/test.esp32-c3-ard.yaml +++ b/tests/components/st7920/test.esp32-c3-ard.yaml @@ -1,24 +1,7 @@ -spi: - - id: spi_st7920 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -display: - - platform: st7920 - cs_pin: 2 - height: 128 - width: 64 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7920/test.esp32-c3-idf.yaml b/tests/components/st7920/test.esp32-c3-idf.yaml index 84ae88461f..2415ba5dc6 100644 --- a/tests/components/st7920/test.esp32-c3-idf.yaml +++ b/tests/components/st7920/test.esp32-c3-idf.yaml @@ -1,24 +1,7 @@ -spi: - - id: spi_st7920 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO5 + cs_pin: GPIO8 -display: - - platform: st7920 - cs_pin: 2 - height: 128 - width: 64 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7920/test.esp32-idf.yaml b/tests/components/st7920/test.esp32-idf.yaml index cdcbc85642..04d2633d2b 100644 --- a/tests/components/st7920/test.esp32-idf.yaml +++ b/tests/components/st7920/test.esp32-idf.yaml @@ -1,24 +1,6 @@ -spi: - - id: spi_st7920 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO12 -display: - - platform: st7920 - cs_pin: 12 - height: 128 - width: 64 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7920/test.esp8266-ard.yaml b/tests/components/st7920/test.esp8266-ard.yaml index 0450bf1c5e..bd5c203e35 100644 --- a/tests/components/st7920/test.esp8266-ard.yaml +++ b/tests/components/st7920/test.esp8266-ard.yaml @@ -1,24 +1,7 @@ -spi: - - id: spi_st7920 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + cs_pin: GPIO5 -display: - - platform: st7920 - cs_pin: 15 - height: 128 - width: 64 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/st7920/test.rp2040-ard.yaml b/tests/components/st7920/test.rp2040-ard.yaml index f442820e7b..f6c3f1eeca 100644 --- a/tests/components/st7920/test.rp2040-ard.yaml +++ b/tests/components/st7920/test.rp2040-ard.yaml @@ -1,24 +1,7 @@ -spi: - - id: spi_st7920 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + miso_pin: GPIO4 + cs_pin: GPIO5 -display: - - platform: st7920 - cs_pin: 5 - height: 128 - width: 64 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - id: page2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - on_page_change: - from: page1 - to: page2 - then: - lambda: |- - ESP_LOGD("display", "1 -> 2"); +<<: !include common.yaml diff --git a/tests/components/sts3x/common.yaml b/tests/components/sts3x/common.yaml new file mode 100644 index 0000000000..1feac4bc3f --- /dev/null +++ b/tests/components/sts3x/common.yaml @@ -0,0 +1,10 @@ +i2c: + - id: i2c_sts3x + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: sts3x + id: sts3x_sensor + name: STS3X Temperature + address: 0x4A diff --git a/tests/components/sts3x/test.esp32-ard.yaml b/tests/components/sts3x/test.esp32-ard.yaml index a74d61e748..63c3bd6afd 100644 --- a/tests/components/sts3x/test.esp32-ard.yaml +++ b/tests/components/sts3x/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sts3x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sts3x - id: sts3x_sensor - name: STS3X Temperature - address: 0x4A +<<: !include common.yaml diff --git a/tests/components/sts3x/test.esp32-c3-ard.yaml b/tests/components/sts3x/test.esp32-c3-ard.yaml index 87980ce3a7..ee2c29ca4e 100644 --- a/tests/components/sts3x/test.esp32-c3-ard.yaml +++ b/tests/components/sts3x/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sts3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sts3x - id: sts3x_sensor - name: STS3X Temperature - address: 0x4A +<<: !include common.yaml diff --git a/tests/components/sts3x/test.esp32-c3-idf.yaml b/tests/components/sts3x/test.esp32-c3-idf.yaml index 87980ce3a7..ee2c29ca4e 100644 --- a/tests/components/sts3x/test.esp32-c3-idf.yaml +++ b/tests/components/sts3x/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sts3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sts3x - id: sts3x_sensor - name: STS3X Temperature - address: 0x4A +<<: !include common.yaml diff --git a/tests/components/sts3x/test.esp32-idf.yaml b/tests/components/sts3x/test.esp32-idf.yaml index a74d61e748..63c3bd6afd 100644 --- a/tests/components/sts3x/test.esp32-idf.yaml +++ b/tests/components/sts3x/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sts3x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: sts3x - id: sts3x_sensor - name: STS3X Temperature - address: 0x4A +<<: !include common.yaml diff --git a/tests/components/sts3x/test.esp8266-ard.yaml b/tests/components/sts3x/test.esp8266-ard.yaml index 87980ce3a7..ee2c29ca4e 100644 --- a/tests/components/sts3x/test.esp8266-ard.yaml +++ b/tests/components/sts3x/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sts3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sts3x - id: sts3x_sensor - name: STS3X Temperature - address: 0x4A +<<: !include common.yaml diff --git a/tests/components/sts3x/test.rp2040-ard.yaml b/tests/components/sts3x/test.rp2040-ard.yaml index 87980ce3a7..ee2c29ca4e 100644 --- a/tests/components/sts3x/test.rp2040-ard.yaml +++ b/tests/components/sts3x/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -i2c: - - id: i2c_sts3x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: sts3x - id: sts3x_sensor - name: STS3X Temperature - address: 0x4A +<<: !include common.yaml diff --git a/tests/components/sun_gtil2/common.yaml b/tests/components/sun_gtil2/common.yaml new file mode 100644 index 0000000000..15cc892d90 --- /dev/null +++ b/tests/components/sun_gtil2/common.yaml @@ -0,0 +1,40 @@ +uart: + - id: uart_sun_gtil2 + rx_pin: ${rx_pin} + baud_rate: 9600 + +sun_gtil2: + +sensor: + - platform: sun_gtil2 + temperature: + id: gtil_temperature + name: Heatsink Temperature + filters: + - throttle_average: 30s + dc_voltage: + id: gtil_dc_voltage + name: DC Voltage + filters: + - throttle_average: 30s + ac_voltage: + id: gtil_ac_voltage + name: AC Voltage + filters: + - throttle_average: 30s + ac_power: + id: gtil_ac_power + name: AC Power + dc_power: + id: gtil_dc_power + name: DC Power + limiter_power: + id: gtil_limiter_power + +text_sensor: + - platform: sun_gtil2 + state: + id: gtil_state + name: State + serial_number: + id: gtil_serial_number diff --git a/tests/components/sun_gtil2/test.esp32-ard.yaml b/tests/components/sun_gtil2/test.esp32-ard.yaml index ed1e68e574..ad420099ff 100644 --- a/tests/components/sun_gtil2/test.esp32-ard.yaml +++ b/tests/components/sun_gtil2/test.esp32-ard.yaml @@ -1,44 +1,4 @@ -uart: - - id: control_to_display - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + rx_pin: GPIO16 -sun_gtil2: - uart_id: control_to_display - -sensor: - - platform: sun_gtil2 - temperature: - id: gtil_temperature - name: "Heatsink Temperature" - filters: - - throttle_average: 30s - dc_voltage: - id: gtil_dc_voltage - name: "DC Voltage" - filters: - - throttle_average: 30s - ac_voltage: - id: gtil_ac_voltage - name: "AC Voltage" - filters: - - throttle_average: 30s - ac_power: - id: gtil_ac_power - name: "AC Power" - dc_power: - id: gtil_dc_power - name: "DC Power" - limiter_power: - id: gtil_limiter_power - internal: true - -text_sensor: - - platform: sun_gtil2 - state: - id: gtil_state - name: "State" - serial_number: - id: gtil_serial_number - internal: true +<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp32-c3-ard.yaml b/tests/components/sun_gtil2/test.esp32-c3-ard.yaml index 6ec834db98..b8a6b85616 100644 --- a/tests/components/sun_gtil2/test.esp32-c3-ard.yaml +++ b/tests/components/sun_gtil2/test.esp32-c3-ard.yaml @@ -1,44 +1,4 @@ -uart: - - id: control_to_display - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + rx_pin: GPIO5 -sun_gtil2: - uart_id: control_to_display - -sensor: - - platform: sun_gtil2 - temperature: - id: gtil_temperature - name: "Heatsink Temperature" - filters: - - throttle_average: 30s - dc_voltage: - id: gtil_dc_voltage - name: "DC Voltage" - filters: - - throttle_average: 30s - ac_voltage: - id: gtil_ac_voltage - name: "AC Voltage" - filters: - - throttle_average: 30s - ac_power: - id: gtil_ac_power - name: "AC Power" - dc_power: - id: gtil_dc_power - name: "DC Power" - limiter_power: - id: gtil_limiter_power - internal: true - -text_sensor: - - platform: sun_gtil2 - state: - id: gtil_state - name: "State" - serial_number: - id: gtil_serial_number - internal: true +<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp32-c3-idf.yaml b/tests/components/sun_gtil2/test.esp32-c3-idf.yaml index 6ec834db98..b8a6b85616 100644 --- a/tests/components/sun_gtil2/test.esp32-c3-idf.yaml +++ b/tests/components/sun_gtil2/test.esp32-c3-idf.yaml @@ -1,44 +1,4 @@ -uart: - - id: control_to_display - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + rx_pin: GPIO5 -sun_gtil2: - uart_id: control_to_display - -sensor: - - platform: sun_gtil2 - temperature: - id: gtil_temperature - name: "Heatsink Temperature" - filters: - - throttle_average: 30s - dc_voltage: - id: gtil_dc_voltage - name: "DC Voltage" - filters: - - throttle_average: 30s - ac_voltage: - id: gtil_ac_voltage - name: "AC Voltage" - filters: - - throttle_average: 30s - ac_power: - id: gtil_ac_power - name: "AC Power" - dc_power: - id: gtil_dc_power - name: "DC Power" - limiter_power: - id: gtil_limiter_power - internal: true - -text_sensor: - - platform: sun_gtil2 - state: - id: gtil_state - name: "State" - serial_number: - id: gtil_serial_number - internal: true +<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp32-idf.yaml b/tests/components/sun_gtil2/test.esp32-idf.yaml index ed1e68e574..ad420099ff 100644 --- a/tests/components/sun_gtil2/test.esp32-idf.yaml +++ b/tests/components/sun_gtil2/test.esp32-idf.yaml @@ -1,44 +1,4 @@ -uart: - - id: control_to_display - rx_pin: - number: 16 - baud_rate: 9600 +substitutions: + rx_pin: GPIO16 -sun_gtil2: - uart_id: control_to_display - -sensor: - - platform: sun_gtil2 - temperature: - id: gtil_temperature - name: "Heatsink Temperature" - filters: - - throttle_average: 30s - dc_voltage: - id: gtil_dc_voltage - name: "DC Voltage" - filters: - - throttle_average: 30s - ac_voltage: - id: gtil_ac_voltage - name: "AC Voltage" - filters: - - throttle_average: 30s - ac_power: - id: gtil_ac_power - name: "AC Power" - dc_power: - id: gtil_dc_power - name: "DC Power" - limiter_power: - id: gtil_limiter_power - internal: true - -text_sensor: - - platform: sun_gtil2 - state: - id: gtil_state - name: "State" - serial_number: - id: gtil_serial_number - internal: true +<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.esp8266-ard.yaml b/tests/components/sun_gtil2/test.esp8266-ard.yaml index 6ec834db98..b8a6b85616 100644 --- a/tests/components/sun_gtil2/test.esp8266-ard.yaml +++ b/tests/components/sun_gtil2/test.esp8266-ard.yaml @@ -1,44 +1,4 @@ -uart: - - id: control_to_display - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + rx_pin: GPIO5 -sun_gtil2: - uart_id: control_to_display - -sensor: - - platform: sun_gtil2 - temperature: - id: gtil_temperature - name: "Heatsink Temperature" - filters: - - throttle_average: 30s - dc_voltage: - id: gtil_dc_voltage - name: "DC Voltage" - filters: - - throttle_average: 30s - ac_voltage: - id: gtil_ac_voltage - name: "AC Voltage" - filters: - - throttle_average: 30s - ac_power: - id: gtil_ac_power - name: "AC Power" - dc_power: - id: gtil_dc_power - name: "DC Power" - limiter_power: - id: gtil_limiter_power - internal: true - -text_sensor: - - platform: sun_gtil2 - state: - id: gtil_state - name: "State" - serial_number: - id: gtil_serial_number - internal: true +<<: !include common.yaml diff --git a/tests/components/sun_gtil2/test.rp2040-ard.yaml b/tests/components/sun_gtil2/test.rp2040-ard.yaml index 6ec834db98..b8a6b85616 100644 --- a/tests/components/sun_gtil2/test.rp2040-ard.yaml +++ b/tests/components/sun_gtil2/test.rp2040-ard.yaml @@ -1,44 +1,4 @@ -uart: - - id: control_to_display - rx_pin: - number: 5 - baud_rate: 9600 +substitutions: + rx_pin: GPIO5 -sun_gtil2: - uart_id: control_to_display - -sensor: - - platform: sun_gtil2 - temperature: - id: gtil_temperature - name: "Heatsink Temperature" - filters: - - throttle_average: 30s - dc_voltage: - id: gtil_dc_voltage - name: "DC Voltage" - filters: - - throttle_average: 30s - ac_voltage: - id: gtil_ac_voltage - name: "AC Voltage" - filters: - - throttle_average: 30s - ac_power: - id: gtil_ac_power - name: "AC Power" - dc_power: - id: gtil_dc_power - name: "DC Power" - limiter_power: - id: gtil_limiter_power - internal: true - -text_sensor: - - platform: sun_gtil2 - state: - id: gtil_state - name: "State" - serial_number: - id: gtil_serial_number - internal: true +<<: !include common.yaml diff --git a/tests/components/sx1509/common.yaml b/tests/components/sx1509/common.yaml new file mode 100644 index 0000000000..a09d850649 --- /dev/null +++ b/tests/components/sx1509/common.yaml @@ -0,0 +1,33 @@ +i2c: + - id: i2c_sx1509 + scl: ${scl_pin} + sda: ${sda_pin} + +sx1509: + - id: sx1509_hub + address: 0x3E + +binary_sensor: + - platform: gpio + name: GPIO SX1509 Test + pin: + sx1509: sx1509_hub + number: 3 + +switch: + - platform: gpio + name: GPIO SX1509 Test Out Open Drain + pin: + sx1509: sx1509_hub + number: 0 + mode: + output: true + open_drain: true + + - platform: gpio + name: GPIO SX1509 Test Out Standard + pin: + sx1509: sx1509_hub + number: 1 + mode: + output: true diff --git a/tests/components/sx1509/test.esp32-ard.yaml b/tests/components/sx1509/test.esp32-ard.yaml index aa1d161a43..63c3bd6afd 100644 --- a/tests/components/sx1509/test.esp32-ard.yaml +++ b/tests/components/sx1509/test.esp32-ard.yaml @@ -1,33 +1,5 @@ -i2c: - - id: i2c_sx1509 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sx1509: - - id: sx1509_hub - address: 0x3E - -binary_sensor: - - platform: gpio - name: GPIO SX1509 Test - pin: - sx1509: sx1509_hub - number: 3 - -switch: - - platform: gpio - name: GPIO SX1509 Test Out Open Drain - pin: - sx1509: sx1509_hub - number: 0 - mode: - output: true - open_drain: true - - - platform: gpio - name: GPIO SX1509 Test Out Standard - pin: - sx1509: sx1509_hub - number: 1 - mode: - output: true +<<: !include common.yaml diff --git a/tests/components/sx1509/test.esp32-c3-ard.yaml b/tests/components/sx1509/test.esp32-c3-ard.yaml index 0397812880..ee2c29ca4e 100644 --- a/tests/components/sx1509/test.esp32-c3-ard.yaml +++ b/tests/components/sx1509/test.esp32-c3-ard.yaml @@ -1,33 +1,5 @@ -i2c: - - id: i2c_sx1509 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sx1509: - - id: sx1509_hub - address: 0x3E - -binary_sensor: - - platform: gpio - name: GPIO SX1509 Test - pin: - sx1509: sx1509_hub - number: 3 - -switch: - - platform: gpio - name: GPIO SX1509 Test Out Open Drain - pin: - sx1509: sx1509_hub - number: 0 - mode: - output: true - open_drain: true - - - platform: gpio - name: GPIO SX1509 Test Out Standard - pin: - sx1509: sx1509_hub - number: 1 - mode: - output: true +<<: !include common.yaml diff --git a/tests/components/sx1509/test.esp32-c3-idf.yaml b/tests/components/sx1509/test.esp32-c3-idf.yaml index 0397812880..ee2c29ca4e 100644 --- a/tests/components/sx1509/test.esp32-c3-idf.yaml +++ b/tests/components/sx1509/test.esp32-c3-idf.yaml @@ -1,33 +1,5 @@ -i2c: - - id: i2c_sx1509 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sx1509: - - id: sx1509_hub - address: 0x3E - -binary_sensor: - - platform: gpio - name: GPIO SX1509 Test - pin: - sx1509: sx1509_hub - number: 3 - -switch: - - platform: gpio - name: GPIO SX1509 Test Out Open Drain - pin: - sx1509: sx1509_hub - number: 0 - mode: - output: true - open_drain: true - - - platform: gpio - name: GPIO SX1509 Test Out Standard - pin: - sx1509: sx1509_hub - number: 1 - mode: - output: true +<<: !include common.yaml diff --git a/tests/components/sx1509/test.esp32-idf.yaml b/tests/components/sx1509/test.esp32-idf.yaml index aa1d161a43..63c3bd6afd 100644 --- a/tests/components/sx1509/test.esp32-idf.yaml +++ b/tests/components/sx1509/test.esp32-idf.yaml @@ -1,33 +1,5 @@ -i2c: - - id: i2c_sx1509 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sx1509: - - id: sx1509_hub - address: 0x3E - -binary_sensor: - - platform: gpio - name: GPIO SX1509 Test - pin: - sx1509: sx1509_hub - number: 3 - -switch: - - platform: gpio - name: GPIO SX1509 Test Out Open Drain - pin: - sx1509: sx1509_hub - number: 0 - mode: - output: true - open_drain: true - - - platform: gpio - name: GPIO SX1509 Test Out Standard - pin: - sx1509: sx1509_hub - number: 1 - mode: - output: true +<<: !include common.yaml diff --git a/tests/components/sx1509/test.esp8266-ard.yaml b/tests/components/sx1509/test.esp8266-ard.yaml index 0397812880..ee2c29ca4e 100644 --- a/tests/components/sx1509/test.esp8266-ard.yaml +++ b/tests/components/sx1509/test.esp8266-ard.yaml @@ -1,33 +1,5 @@ -i2c: - - id: i2c_sx1509 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sx1509: - - id: sx1509_hub - address: 0x3E - -binary_sensor: - - platform: gpio - name: GPIO SX1509 Test - pin: - sx1509: sx1509_hub - number: 3 - -switch: - - platform: gpio - name: GPIO SX1509 Test Out Open Drain - pin: - sx1509: sx1509_hub - number: 0 - mode: - output: true - open_drain: true - - - platform: gpio - name: GPIO SX1509 Test Out Standard - pin: - sx1509: sx1509_hub - number: 1 - mode: - output: true +<<: !include common.yaml diff --git a/tests/components/sx1509/test.rp2040-ard.yaml b/tests/components/sx1509/test.rp2040-ard.yaml index 0397812880..ee2c29ca4e 100644 --- a/tests/components/sx1509/test.rp2040-ard.yaml +++ b/tests/components/sx1509/test.rp2040-ard.yaml @@ -1,33 +1,5 @@ -i2c: - - id: i2c_sx1509 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sx1509: - - id: sx1509_hub - address: 0x3E - -binary_sensor: - - platform: gpio - name: GPIO SX1509 Test - pin: - sx1509: sx1509_hub - number: 3 - -switch: - - platform: gpio - name: GPIO SX1509 Test Out Open Drain - pin: - sx1509: sx1509_hub - number: 0 - mode: - output: true - open_drain: true - - - platform: gpio - name: GPIO SX1509 Test Out Standard - pin: - sx1509: sx1509_hub - number: 1 - mode: - output: true +<<: !include common.yaml From dc8646cda65da5c17acb4c70a3b6ceaa2f54bbe7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 9 Feb 2025 15:43:17 -0600 Subject: [PATCH 0977/1052] [CI] Consolidate some tests (U, V, W, X, Y, Z) (#8210) --- tests/components/ufire_ec/common.yaml | 25 ++ tests/components/ufire_ec/test.esp32-ard.yaml | 28 +-- .../ufire_ec/test.esp32-c3-ard.yaml | 28 +-- .../ufire_ec/test.esp32-c3-idf.yaml | 28 +-- tests/components/ufire_ec/test.esp32-idf.yaml | 28 +-- .../components/ufire_ec/test.esp8266-ard.yaml | 28 +-- .../components/ufire_ec/test.rp2040-ard.yaml | 28 +-- tests/components/ufire_ise/common.yaml | 25 ++ .../components/ufire_ise/test.esp32-ard.yaml | 28 +-- .../ufire_ise/test.esp32-c3-ard.yaml | 28 +-- .../ufire_ise/test.esp32-c3-idf.yaml | 28 +-- .../components/ufire_ise/test.esp32-idf.yaml | 28 +-- .../ufire_ise/test.esp8266-ard.yaml | 28 +-- .../components/ufire_ise/test.rp2040-ard.yaml | 28 +-- tests/components/uln2003/common.yaml | 29 +++ tests/components/uln2003/test.esp32-ard.yaml | 34 +-- .../components/uln2003/test.esp32-c3-ard.yaml | 34 +-- .../components/uln2003/test.esp32-c3-idf.yaml | 34 +-- tests/components/uln2003/test.esp32-idf.yaml | 34 +-- .../components/uln2003/test.esp8266-ard.yaml | 34 +-- tests/components/uln2003/test.rp2040-ard.yaml | 34 +-- tests/components/vbus/common.yaml | 42 ++++ tests/components/vbus/test.esp32-ard.yaml | 45 +--- tests/components/vbus/test.esp32-c3-ard.yaml | 45 +--- tests/components/vbus/test.esp32-c3-idf.yaml | 45 +--- tests/components/vbus/test.esp32-idf.yaml | 45 +--- tests/components/vbus/test.esp8266-ard.yaml | 45 +--- tests/components/vbus/test.rp2040-ard.yaml | 45 +--- tests/components/veml3235/common.yaml | 15 ++ tests/components/veml3235/test.esp32-ard.yaml | 18 +- .../veml3235/test.esp32-c3-ard.yaml | 18 +- .../veml3235/test.esp32-c3-idf.yaml | 18 +- tests/components/veml3235/test.esp32-idf.yaml | 18 +- .../components/veml3235/test.esp8266-ard.yaml | 18 +- .../components/veml3235/test.rp2040-ard.yaml | 18 +- tests/components/veml7700/common.yaml | 5 + tests/components/veml7700/test.esp32-ard.yaml | 7 +- .../veml7700/test.esp32-c3-ard.yaml | 7 +- .../veml7700/test.esp32-c3-idf.yaml | 7 +- tests/components/veml7700/test.esp32-idf.yaml | 7 +- .../components/veml7700/test.esp8266-ard.yaml | 7 +- .../components/veml7700/test.rp2040-ard.yaml | 7 +- tests/components/vl53l0x/common.yaml | 12 + tests/components/vl53l0x/test.esp32-ard.yaml | 15 +- .../components/vl53l0x/test.esp32-c3-ard.yaml | 15 +- .../components/vl53l0x/test.esp32-c3-idf.yaml | 15 +- tests/components/vl53l0x/test.esp32-idf.yaml | 15 +- .../components/vl53l0x/test.esp8266-ard.yaml | 15 +- tests/components/vl53l0x/test.rp2040-ard.yaml | 15 +- tests/components/voice_assistant/common.yaml | 57 +++++ .../voice_assistant/test.esp32-ard.yaml | 63 +----- .../voice_assistant/test.esp32-c3-ard.yaml | 62 +---- .../voice_assistant/test.esp32-c3-idf.yaml | 62 +---- .../voice_assistant/test.esp32-idf.yaml | 62 +---- tests/components/waveshare_epaper/common.yaml | 190 ++++++++++++++++ .../waveshare_epaper/test.esp32-ard.yaml | 214 +----------------- .../waveshare_epaper/test.esp32-c3-ard.yaml | 130 +---------- .../waveshare_epaper/test.esp32-c3-idf.yaml | 130 +---------- .../waveshare_epaper/test.esp32-idf.yaml | 130 +---------- .../waveshare_epaper/test.esp8266-ard.yaml | 130 +---------- .../waveshare_epaper/test.rp2040-ard.yaml | 130 +---------- tests/components/whirlpool/common.yaml | 7 + .../components/whirlpool/test.esp32-ard.yaml | 9 +- .../whirlpool/test.esp32-c3-ard.yaml | 9 +- .../whirlpool/test.esp32-c3-idf.yaml | 9 +- .../components/whirlpool/test.esp32-idf.yaml | 9 +- .../whirlpool/test.esp8266-ard.yaml | 9 +- tests/components/whynter/common.yaml | 7 + tests/components/whynter/test.esp32-ard.yaml | 9 +- .../components/whynter/test.esp32-c3-ard.yaml | 9 +- .../components/whynter/test.esp32-c3-idf.yaml | 9 +- tests/components/whynter/test.esp32-idf.yaml | 9 +- .../components/whynter/test.esp8266-ard.yaml | 9 +- tests/components/wl_134/common.yaml | 10 + tests/components/wl_134/test.esp32-ard.yaml | 13 +- .../components/wl_134/test.esp32-c3-ard.yaml | 13 +- .../components/wl_134/test.esp32-c3-idf.yaml | 13 +- tests/components/wl_134/test.esp32-idf.yaml | 13 +- tests/components/wl_134/test.esp8266-ard.yaml | 13 +- tests/components/wl_134/test.rp2040-ard.yaml | 13 +- tests/components/x9c/common.yaml | 8 + tests/components/x9c/test.esp32-ard.yaml | 14 +- tests/components/x9c/test.esp32-c3-ard.yaml | 14 +- tests/components/x9c/test.esp32-c3-idf.yaml | 14 +- tests/components/x9c/test.esp32-idf.yaml | 14 +- tests/components/x9c/test.esp8266-ard.yaml | 14 +- tests/components/x9c/test.rp2040-ard.yaml | 14 +- tests/components/xgzp68xx/common.yaml | 12 + tests/components/xgzp68xx/test.esp32-ard.yaml | 15 +- .../xgzp68xx/test.esp32-c3-ard.yaml | 15 +- .../xgzp68xx/test.esp32-c3-idf.yaml | 15 +- tests/components/xgzp68xx/test.esp32-idf.yaml | 15 +- .../components/xgzp68xx/test.esp8266-ard.yaml | 15 +- .../components/xgzp68xx/test.rp2040-ard.yaml | 15 +- tests/components/xl9535/common.yaml | 26 +++ tests/components/xl9535/test.esp32-ard.yaml | 29 +-- .../components/xl9535/test.esp32-c3-ard.yaml | 29 +-- .../components/xl9535/test.esp32-c3-idf.yaml | 29 +-- tests/components/xl9535/test.esp32-idf.yaml | 29 +-- tests/components/xl9535/test.esp8266-ard.yaml | 29 +-- tests/components/xl9535/test.rp2040-ard.yaml | 29 +-- tests/components/xpt2046/common.yaml | 35 +++ tests/components/xpt2046/test.esp32-ard.yaml | 44 +--- .../components/xpt2046/test.esp32-c3-ard.yaml | 44 +--- .../components/xpt2046/test.esp32-c3-idf.yaml | 44 +--- tests/components/xpt2046/test.esp32-idf.yaml | 44 +--- .../components/xpt2046/test.esp32-s2-ard.yaml | 38 ---- .../components/xpt2046/test.esp8266-ard.yaml | 44 +--- tests/components/xpt2046/test.rp2040-ard.yaml | 44 +--- tests/components/yashima/common.yaml | 7 + tests/components/yashima/test.esp32-ard.yaml | 9 +- .../components/yashima/test.esp32-c3-ard.yaml | 9 +- .../components/yashima/test.esp32-c3-idf.yaml | 9 +- tests/components/yashima/test.esp32-idf.yaml | 9 +- .../components/yashima/test.esp8266-ard.yaml | 9 +- tests/components/zhlt01/common.yaml | 7 + tests/components/zhlt01/test.esp32-ard.yaml | 9 +- .../components/zhlt01/test.esp32-c3-ard.yaml | 9 +- .../components/zhlt01/test.esp32-c3-idf.yaml | 9 +- tests/components/zhlt01/test.esp32-idf.yaml | 9 +- tests/components/zhlt01/test.esp8266-ard.yaml | 9 +- tests/components/zio_ultrasonic/common.yaml | 9 + .../zio_ultrasonic/test.esp32-ard.yaml | 12 +- .../zio_ultrasonic/test.esp32-c3-ard.yaml | 12 +- .../zio_ultrasonic/test.esp32-c3-idf.yaml | 12 +- .../zio_ultrasonic/test.esp32-idf.yaml | 12 +- .../zio_ultrasonic/test.esp8266-ard.yaml | 12 +- .../zio_ultrasonic/test.rp2040-ard.yaml | 12 +- tests/components/zyaura/common.yaml | 10 + tests/components/zyaura/test.esp32-ard.yaml | 15 +- .../components/zyaura/test.esp32-c3-ard.yaml | 15 +- .../components/zyaura/test.esp32-c3-idf.yaml | 15 +- tests/components/zyaura/test.esp32-idf.yaml | 15 +- tests/components/zyaura/test.esp8266-ard.yaml | 15 +- tests/components/zyaura/test.rp2040-ard.yaml | 15 +- 135 files changed, 1070 insertions(+), 2701 deletions(-) create mode 100644 tests/components/ufire_ec/common.yaml create mode 100644 tests/components/ufire_ise/common.yaml create mode 100644 tests/components/uln2003/common.yaml create mode 100644 tests/components/vbus/common.yaml create mode 100644 tests/components/veml3235/common.yaml create mode 100644 tests/components/vl53l0x/common.yaml create mode 100644 tests/components/voice_assistant/common.yaml create mode 100644 tests/components/waveshare_epaper/common.yaml create mode 100644 tests/components/whirlpool/common.yaml create mode 100644 tests/components/whynter/common.yaml create mode 100644 tests/components/wl_134/common.yaml create mode 100644 tests/components/x9c/common.yaml create mode 100644 tests/components/xgzp68xx/common.yaml create mode 100644 tests/components/xl9535/common.yaml create mode 100644 tests/components/xpt2046/common.yaml delete mode 100644 tests/components/xpt2046/test.esp32-s2-ard.yaml create mode 100644 tests/components/yashima/common.yaml create mode 100644 tests/components/zhlt01/common.yaml create mode 100644 tests/components/zio_ultrasonic/common.yaml create mode 100644 tests/components/zyaura/common.yaml diff --git a/tests/components/ufire_ec/common.yaml b/tests/components/ufire_ec/common.yaml new file mode 100644 index 0000000000..dcc957aaee --- /dev/null +++ b/tests/components/ufire_ec/common.yaml @@ -0,0 +1,25 @@ +esphome: + on_boot: + then: + - ufire_ec.calibrate_probe: + id: ufire_ec_board + solution: 0.146 + temperature: !lambda "return id(test_sensor).state;" + - ufire_ec.reset: + +i2c: + - id: i2c_ufire_ec + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: template + id: test_sensor + lambda: "return 21;" + - platform: ufire_ec + id: ufire_ec_board + ec: + name: Ufire EC + temperature_sensor: test_sensor + temperature_compensation: 20.0 + temperature_coefficient: 0.019 diff --git a/tests/components/ufire_ec/test.esp32-ard.yaml b/tests/components/ufire_ec/test.esp32-ard.yaml index 5e6a0daa9e..63c3bd6afd 100644 --- a/tests/components/ufire_ec/test.esp32-ard.yaml +++ b/tests/components/ufire_ec/test.esp32-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ec.calibrate_probe: - id: ufire_ec_board - solution: 0.146 - temperature: !lambda "return id(test_sensor).state;" - - ufire_ec.reset: +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_ufire_ec - scl: 16 - sda: 17 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ec - id: ufire_ec_board - ec: - name: Ufire EC - temperature_sensor: test_sensor - temperature_compensation: 20.0 - temperature_coefficient: 0.019 +<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp32-c3-ard.yaml b/tests/components/ufire_ec/test.esp32-c3-ard.yaml index aa72c992b8..ee2c29ca4e 100644 --- a/tests/components/ufire_ec/test.esp32-c3-ard.yaml +++ b/tests/components/ufire_ec/test.esp32-c3-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ec.calibrate_probe: - id: ufire_ec_board - solution: 0.146 - temperature: !lambda "return id(test_sensor).state;" - - ufire_ec.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ec - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ec - id: ufire_ec_board - ec: - name: Ufire EC - temperature_sensor: test_sensor - temperature_compensation: 20.0 - temperature_coefficient: 0.019 +<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp32-c3-idf.yaml b/tests/components/ufire_ec/test.esp32-c3-idf.yaml index aa72c992b8..ee2c29ca4e 100644 --- a/tests/components/ufire_ec/test.esp32-c3-idf.yaml +++ b/tests/components/ufire_ec/test.esp32-c3-idf.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ec.calibrate_probe: - id: ufire_ec_board - solution: 0.146 - temperature: !lambda "return id(test_sensor).state;" - - ufire_ec.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ec - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ec - id: ufire_ec_board - ec: - name: Ufire EC - temperature_sensor: test_sensor - temperature_compensation: 20.0 - temperature_coefficient: 0.019 +<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp32-idf.yaml b/tests/components/ufire_ec/test.esp32-idf.yaml index 5e6a0daa9e..63c3bd6afd 100644 --- a/tests/components/ufire_ec/test.esp32-idf.yaml +++ b/tests/components/ufire_ec/test.esp32-idf.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ec.calibrate_probe: - id: ufire_ec_board - solution: 0.146 - temperature: !lambda "return id(test_sensor).state;" - - ufire_ec.reset: +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_ufire_ec - scl: 16 - sda: 17 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ec - id: ufire_ec_board - ec: - name: Ufire EC - temperature_sensor: test_sensor - temperature_compensation: 20.0 - temperature_coefficient: 0.019 +<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.esp8266-ard.yaml b/tests/components/ufire_ec/test.esp8266-ard.yaml index aa72c992b8..ee2c29ca4e 100644 --- a/tests/components/ufire_ec/test.esp8266-ard.yaml +++ b/tests/components/ufire_ec/test.esp8266-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ec.calibrate_probe: - id: ufire_ec_board - solution: 0.146 - temperature: !lambda "return id(test_sensor).state;" - - ufire_ec.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ec - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ec - id: ufire_ec_board - ec: - name: Ufire EC - temperature_sensor: test_sensor - temperature_compensation: 20.0 - temperature_coefficient: 0.019 +<<: !include common.yaml diff --git a/tests/components/ufire_ec/test.rp2040-ard.yaml b/tests/components/ufire_ec/test.rp2040-ard.yaml index aa72c992b8..ee2c29ca4e 100644 --- a/tests/components/ufire_ec/test.rp2040-ard.yaml +++ b/tests/components/ufire_ec/test.rp2040-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ec.calibrate_probe: - id: ufire_ec_board - solution: 0.146 - temperature: !lambda "return id(test_sensor).state;" - - ufire_ec.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ec - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ec - id: ufire_ec_board - ec: - name: Ufire EC - temperature_sensor: test_sensor - temperature_compensation: 20.0 - temperature_coefficient: 0.019 +<<: !include common.yaml diff --git a/tests/components/ufire_ise/common.yaml b/tests/components/ufire_ise/common.yaml new file mode 100644 index 0000000000..d6ead8c479 --- /dev/null +++ b/tests/components/ufire_ise/common.yaml @@ -0,0 +1,25 @@ +esphome: + on_boot: + then: + - ufire_ise.calibrate_probe_high: + id: ufire_ise_sensor + solution: 7.0 + - ufire_ise.calibrate_probe_low: + id: ufire_ise_sensor + solution: 4.0 + - ufire_ise.reset: + +i2c: + - id: i2c_ufire_ise + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: template + id: test_sensor + lambda: "return 21;" + - platform: ufire_ise + id: ufire_ise_sensor + temperature_sensor: test_sensor + ph: + name: Ufire pH diff --git a/tests/components/ufire_ise/test.esp32-ard.yaml b/tests/components/ufire_ise/test.esp32-ard.yaml index 9ed0ac433a..63c3bd6afd 100644 --- a/tests/components/ufire_ise/test.esp32-ard.yaml +++ b/tests/components/ufire_ise/test.esp32-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ise.calibrate_probe_high: - id: ufire_ise_sensor - solution: 7.0 - - ufire_ise.calibrate_probe_low: - id: ufire_ise_sensor - solution: 4.0 - - ufire_ise.reset: +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_ufire_ise - scl: 16 - sda: 17 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ise - id: ufire_ise_sensor - temperature_sensor: test_sensor - ph: - name: Ufire pH +<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp32-c3-ard.yaml b/tests/components/ufire_ise/test.esp32-c3-ard.yaml index 36aec73113..ee2c29ca4e 100644 --- a/tests/components/ufire_ise/test.esp32-c3-ard.yaml +++ b/tests/components/ufire_ise/test.esp32-c3-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ise.calibrate_probe_high: - id: ufire_ise_sensor - solution: 7.0 - - ufire_ise.calibrate_probe_low: - id: ufire_ise_sensor - solution: 4.0 - - ufire_ise.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ise - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ise - id: ufire_ise_sensor - temperature_sensor: test_sensor - ph: - name: Ufire pH +<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp32-c3-idf.yaml b/tests/components/ufire_ise/test.esp32-c3-idf.yaml index 36aec73113..ee2c29ca4e 100644 --- a/tests/components/ufire_ise/test.esp32-c3-idf.yaml +++ b/tests/components/ufire_ise/test.esp32-c3-idf.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ise.calibrate_probe_high: - id: ufire_ise_sensor - solution: 7.0 - - ufire_ise.calibrate_probe_low: - id: ufire_ise_sensor - solution: 4.0 - - ufire_ise.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ise - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ise - id: ufire_ise_sensor - temperature_sensor: test_sensor - ph: - name: Ufire pH +<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp32-idf.yaml b/tests/components/ufire_ise/test.esp32-idf.yaml index 9ed0ac433a..63c3bd6afd 100644 --- a/tests/components/ufire_ise/test.esp32-idf.yaml +++ b/tests/components/ufire_ise/test.esp32-idf.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ise.calibrate_probe_high: - id: ufire_ise_sensor - solution: 7.0 - - ufire_ise.calibrate_probe_low: - id: ufire_ise_sensor - solution: 4.0 - - ufire_ise.reset: +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -i2c: - - id: i2c_ufire_ise - scl: 16 - sda: 17 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ise - id: ufire_ise_sensor - temperature_sensor: test_sensor - ph: - name: Ufire pH +<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.esp8266-ard.yaml b/tests/components/ufire_ise/test.esp8266-ard.yaml index 36aec73113..ee2c29ca4e 100644 --- a/tests/components/ufire_ise/test.esp8266-ard.yaml +++ b/tests/components/ufire_ise/test.esp8266-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ise.calibrate_probe_high: - id: ufire_ise_sensor - solution: 7.0 - - ufire_ise.calibrate_probe_low: - id: ufire_ise_sensor - solution: 4.0 - - ufire_ise.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ise - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ise - id: ufire_ise_sensor - temperature_sensor: test_sensor - ph: - name: Ufire pH +<<: !include common.yaml diff --git a/tests/components/ufire_ise/test.rp2040-ard.yaml b/tests/components/ufire_ise/test.rp2040-ard.yaml index 36aec73113..ee2c29ca4e 100644 --- a/tests/components/ufire_ise/test.rp2040-ard.yaml +++ b/tests/components/ufire_ise/test.rp2040-ard.yaml @@ -1,25 +1,5 @@ -esphome: - on_boot: - then: - - ufire_ise.calibrate_probe_high: - id: ufire_ise_sensor - solution: 7.0 - - ufire_ise.calibrate_probe_low: - id: ufire_ise_sensor - solution: 4.0 - - ufire_ise.reset: +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -i2c: - - id: i2c_ufire_ise - scl: 5 - sda: 4 - -sensor: - - platform: template - id: test_sensor - lambda: "return 21;" - - platform: ufire_ise - id: ufire_ise_sensor - temperature_sensor: test_sensor - ph: - name: Ufire pH +<<: !include common.yaml diff --git a/tests/components/uln2003/common.yaml b/tests/components/uln2003/common.yaml new file mode 100644 index 0000000000..f848030592 --- /dev/null +++ b/tests/components/uln2003/common.yaml @@ -0,0 +1,29 @@ +esphome: + on_boot: + then: + - stepper.report_position: + id: uln2003_stepper + position: 250 + - stepper.set_target: + id: uln2003_stepper + target: 250 + - stepper.set_acceleration: + id: uln2003_stepper + acceleration: 250 steps/s^2 + - stepper.set_deceleration: + id: uln2003_stepper + deceleration: 250 steps/s^2 + - stepper.set_speed: + id: uln2003_stepper + speed: 250 steps/s + +stepper: + - platform: uln2003 + id: uln2003_stepper + pin_a: ${pin_a} + pin_b: ${pin_b} + pin_c: ${pin_c} + pin_d: ${pin_d} + max_speed: 250 steps/s + acceleration: 100 steps/s^2 + deceleration: 200 steps/s^2 diff --git a/tests/components/uln2003/test.esp32-ard.yaml b/tests/components/uln2003/test.esp32-ard.yaml index 61a6e94396..ee4cff0923 100644 --- a/tests/components/uln2003/test.esp32-ard.yaml +++ b/tests/components/uln2003/test.esp32-ard.yaml @@ -1,29 +1,7 @@ -esphome: - on_boot: - then: - - stepper.report_position: - id: uln2003_stepper - position: 250 - - stepper.set_target: - id: uln2003_stepper - target: 250 - - stepper.set_acceleration: - id: uln2003_stepper - acceleration: 250 steps/s^2 - - stepper.set_deceleration: - id: uln2003_stepper - deceleration: 250 steps/s^2 - - stepper.set_speed: - id: uln2003_stepper - speed: 250 steps/s +substitutions: + pin_a: GPIO12 + pin_b: GPIO13 + pin_c: GPIO14 + pin_d: GPIO15 -stepper: - - platform: uln2003 - id: uln2003_stepper - pin_a: 12 - pin_b: 13 - pin_c: 14 - pin_d: 15 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +<<: !include common.yaml diff --git a/tests/components/uln2003/test.esp32-c3-ard.yaml b/tests/components/uln2003/test.esp32-c3-ard.yaml index 2d19d4dba3..11d16a4d5d 100644 --- a/tests/components/uln2003/test.esp32-c3-ard.yaml +++ b/tests/components/uln2003/test.esp32-c3-ard.yaml @@ -1,29 +1,7 @@ -esphome: - on_boot: - then: - - stepper.report_position: - id: uln2003_stepper - position: 250 - - stepper.set_target: - id: uln2003_stepper - target: 250 - - stepper.set_acceleration: - id: uln2003_stepper - acceleration: 250 steps/s^2 - - stepper.set_deceleration: - id: uln2003_stepper - deceleration: 250 steps/s^2 - - stepper.set_speed: - id: uln2003_stepper - speed: 250 steps/s +substitutions: + pin_a: GPIO0 + pin_b: GPIO1 + pin_c: GPIO2 + pin_d: GPIO3 -stepper: - - platform: uln2003 - id: uln2003_stepper - pin_a: 0 - pin_b: 1 - pin_c: 2 - pin_d: 3 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +<<: !include common.yaml diff --git a/tests/components/uln2003/test.esp32-c3-idf.yaml b/tests/components/uln2003/test.esp32-c3-idf.yaml index 2d19d4dba3..11d16a4d5d 100644 --- a/tests/components/uln2003/test.esp32-c3-idf.yaml +++ b/tests/components/uln2003/test.esp32-c3-idf.yaml @@ -1,29 +1,7 @@ -esphome: - on_boot: - then: - - stepper.report_position: - id: uln2003_stepper - position: 250 - - stepper.set_target: - id: uln2003_stepper - target: 250 - - stepper.set_acceleration: - id: uln2003_stepper - acceleration: 250 steps/s^2 - - stepper.set_deceleration: - id: uln2003_stepper - deceleration: 250 steps/s^2 - - stepper.set_speed: - id: uln2003_stepper - speed: 250 steps/s +substitutions: + pin_a: GPIO0 + pin_b: GPIO1 + pin_c: GPIO2 + pin_d: GPIO3 -stepper: - - platform: uln2003 - id: uln2003_stepper - pin_a: 0 - pin_b: 1 - pin_c: 2 - pin_d: 3 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +<<: !include common.yaml diff --git a/tests/components/uln2003/test.esp32-idf.yaml b/tests/components/uln2003/test.esp32-idf.yaml index 61a6e94396..ee4cff0923 100644 --- a/tests/components/uln2003/test.esp32-idf.yaml +++ b/tests/components/uln2003/test.esp32-idf.yaml @@ -1,29 +1,7 @@ -esphome: - on_boot: - then: - - stepper.report_position: - id: uln2003_stepper - position: 250 - - stepper.set_target: - id: uln2003_stepper - target: 250 - - stepper.set_acceleration: - id: uln2003_stepper - acceleration: 250 steps/s^2 - - stepper.set_deceleration: - id: uln2003_stepper - deceleration: 250 steps/s^2 - - stepper.set_speed: - id: uln2003_stepper - speed: 250 steps/s +substitutions: + pin_a: GPIO12 + pin_b: GPIO13 + pin_c: GPIO14 + pin_d: GPIO15 -stepper: - - platform: uln2003 - id: uln2003_stepper - pin_a: 12 - pin_b: 13 - pin_c: 14 - pin_d: 15 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +<<: !include common.yaml diff --git a/tests/components/uln2003/test.esp8266-ard.yaml b/tests/components/uln2003/test.esp8266-ard.yaml index 61a6e94396..ee4cff0923 100644 --- a/tests/components/uln2003/test.esp8266-ard.yaml +++ b/tests/components/uln2003/test.esp8266-ard.yaml @@ -1,29 +1,7 @@ -esphome: - on_boot: - then: - - stepper.report_position: - id: uln2003_stepper - position: 250 - - stepper.set_target: - id: uln2003_stepper - target: 250 - - stepper.set_acceleration: - id: uln2003_stepper - acceleration: 250 steps/s^2 - - stepper.set_deceleration: - id: uln2003_stepper - deceleration: 250 steps/s^2 - - stepper.set_speed: - id: uln2003_stepper - speed: 250 steps/s +substitutions: + pin_a: GPIO12 + pin_b: GPIO13 + pin_c: GPIO14 + pin_d: GPIO15 -stepper: - - platform: uln2003 - id: uln2003_stepper - pin_a: 12 - pin_b: 13 - pin_c: 14 - pin_d: 15 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +<<: !include common.yaml diff --git a/tests/components/uln2003/test.rp2040-ard.yaml b/tests/components/uln2003/test.rp2040-ard.yaml index 2d19d4dba3..11d16a4d5d 100644 --- a/tests/components/uln2003/test.rp2040-ard.yaml +++ b/tests/components/uln2003/test.rp2040-ard.yaml @@ -1,29 +1,7 @@ -esphome: - on_boot: - then: - - stepper.report_position: - id: uln2003_stepper - position: 250 - - stepper.set_target: - id: uln2003_stepper - target: 250 - - stepper.set_acceleration: - id: uln2003_stepper - acceleration: 250 steps/s^2 - - stepper.set_deceleration: - id: uln2003_stepper - deceleration: 250 steps/s^2 - - stepper.set_speed: - id: uln2003_stepper - speed: 250 steps/s +substitutions: + pin_a: GPIO0 + pin_b: GPIO1 + pin_c: GPIO2 + pin_d: GPIO3 -stepper: - - platform: uln2003 - id: uln2003_stepper - pin_a: 0 - pin_b: 1 - pin_c: 2 - pin_d: 3 - max_speed: 250 steps/s - acceleration: 100 steps/s^2 - deceleration: 200 steps/s^2 +<<: !include common.yaml diff --git a/tests/components/vbus/common.yaml b/tests/components/vbus/common.yaml new file mode 100644 index 0000000000..a1f94cd839 --- /dev/null +++ b/tests/components/vbus/common.yaml @@ -0,0 +1,42 @@ +uart: + - id: uart_vbus + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +vbus: + +binary_sensor: + - platform: vbus + model: deltasol_bs_plus + relay1: + name: Relay 1 On + relay2: + name: Relay 2 On + sensor1_error: + name: Sensor 1 Error + - platform: vbus + model: custom + command: 0x100 + source: 0x1234 + dest: 0x10 + binary_sensors: + - id: vcustom_b + name: VBus Custom Binary Sensor + lambda: return x[0] & 1; + +sensor: + - platform: vbus + model: deltasol c + temperature_1: + name: Temperature 1 + temperature_2: + name: Temperature 2 + temperature_3: + name: Temperature 3 + operating_hours_1: + name: Operating Hours 1 + heat_quantity: + name: Heat Quantity + time: + name: System Time diff --git a/tests/components/vbus/test.esp32-ard.yaml b/tests/components/vbus/test.esp32-ard.yaml index a0e5ca42cc..f486544afa 100644 --- a/tests/components/vbus/test.esp32-ard.yaml +++ b/tests/components/vbus/test.esp32-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_vbus - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -vbus: - -binary_sensor: - - platform: vbus - model: deltasol_bs_plus - relay1: - name: Relay 1 On - relay2: - name: Relay 2 On - sensor1_error: - name: Sensor 1 Error - - platform: vbus - model: custom - command: 0x100 - source: 0x1234 - dest: 0x10 - binary_sensors: - - id: vcustom_b - name: VBus Custom Binary Sensor - lambda: return x[0] & 1; - -sensor: - - platform: vbus - model: deltasol c - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - temperature_3: - name: Temperature 3 - operating_hours_1: - name: Operating Hours 1 - heat_quantity: - name: Heat Quantity - time: - name: System Time +<<: !include common.yaml diff --git a/tests/components/vbus/test.esp32-c3-ard.yaml b/tests/components/vbus/test.esp32-c3-ard.yaml index 67ee542031..b516342f3b 100644 --- a/tests/components/vbus/test.esp32-c3-ard.yaml +++ b/tests/components/vbus/test.esp32-c3-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_vbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -vbus: - -binary_sensor: - - platform: vbus - model: deltasol_bs_plus - relay1: - name: Relay 1 On - relay2: - name: Relay 2 On - sensor1_error: - name: Sensor 1 Error - - platform: vbus - model: custom - command: 0x100 - source: 0x1234 - dest: 0x10 - binary_sensors: - - id: vcustom_b - name: VBus Custom Binary Sensor - lambda: return x[0] & 1; - -sensor: - - platform: vbus - model: deltasol c - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - temperature_3: - name: Temperature 3 - operating_hours_1: - name: Operating Hours 1 - heat_quantity: - name: Heat Quantity - time: - name: System Time +<<: !include common.yaml diff --git a/tests/components/vbus/test.esp32-c3-idf.yaml b/tests/components/vbus/test.esp32-c3-idf.yaml index 67ee542031..b516342f3b 100644 --- a/tests/components/vbus/test.esp32-c3-idf.yaml +++ b/tests/components/vbus/test.esp32-c3-idf.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_vbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -vbus: - -binary_sensor: - - platform: vbus - model: deltasol_bs_plus - relay1: - name: Relay 1 On - relay2: - name: Relay 2 On - sensor1_error: - name: Sensor 1 Error - - platform: vbus - model: custom - command: 0x100 - source: 0x1234 - dest: 0x10 - binary_sensors: - - id: vcustom_b - name: VBus Custom Binary Sensor - lambda: return x[0] & 1; - -sensor: - - platform: vbus - model: deltasol c - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - temperature_3: - name: Temperature 3 - operating_hours_1: - name: Operating Hours 1 - heat_quantity: - name: Heat Quantity - time: - name: System Time +<<: !include common.yaml diff --git a/tests/components/vbus/test.esp32-idf.yaml b/tests/components/vbus/test.esp32-idf.yaml index a0e5ca42cc..f486544afa 100644 --- a/tests/components/vbus/test.esp32-idf.yaml +++ b/tests/components/vbus/test.esp32-idf.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_vbus - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -vbus: - -binary_sensor: - - platform: vbus - model: deltasol_bs_plus - relay1: - name: Relay 1 On - relay2: - name: Relay 2 On - sensor1_error: - name: Sensor 1 Error - - platform: vbus - model: custom - command: 0x100 - source: 0x1234 - dest: 0x10 - binary_sensors: - - id: vcustom_b - name: VBus Custom Binary Sensor - lambda: return x[0] & 1; - -sensor: - - platform: vbus - model: deltasol c - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - temperature_3: - name: Temperature 3 - operating_hours_1: - name: Operating Hours 1 - heat_quantity: - name: Heat Quantity - time: - name: System Time +<<: !include common.yaml diff --git a/tests/components/vbus/test.esp8266-ard.yaml b/tests/components/vbus/test.esp8266-ard.yaml index 67ee542031..b516342f3b 100644 --- a/tests/components/vbus/test.esp8266-ard.yaml +++ b/tests/components/vbus/test.esp8266-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_vbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -vbus: - -binary_sensor: - - platform: vbus - model: deltasol_bs_plus - relay1: - name: Relay 1 On - relay2: - name: Relay 2 On - sensor1_error: - name: Sensor 1 Error - - platform: vbus - model: custom - command: 0x100 - source: 0x1234 - dest: 0x10 - binary_sensors: - - id: vcustom_b - name: VBus Custom Binary Sensor - lambda: return x[0] & 1; - -sensor: - - platform: vbus - model: deltasol c - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - temperature_3: - name: Temperature 3 - operating_hours_1: - name: Operating Hours 1 - heat_quantity: - name: Heat Quantity - time: - name: System Time +<<: !include common.yaml diff --git a/tests/components/vbus/test.rp2040-ard.yaml b/tests/components/vbus/test.rp2040-ard.yaml index 67ee542031..b516342f3b 100644 --- a/tests/components/vbus/test.rp2040-ard.yaml +++ b/tests/components/vbus/test.rp2040-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_vbus - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -vbus: - -binary_sensor: - - platform: vbus - model: deltasol_bs_plus - relay1: - name: Relay 1 On - relay2: - name: Relay 2 On - sensor1_error: - name: Sensor 1 Error - - platform: vbus - model: custom - command: 0x100 - source: 0x1234 - dest: 0x10 - binary_sensors: - - id: vcustom_b - name: VBus Custom Binary Sensor - lambda: return x[0] & 1; - -sensor: - - platform: vbus - model: deltasol c - temperature_1: - name: Temperature 1 - temperature_2: - name: Temperature 2 - temperature_3: - name: Temperature 3 - operating_hours_1: - name: Operating Hours 1 - heat_quantity: - name: Heat Quantity - time: - name: System Time +<<: !include common.yaml diff --git a/tests/components/veml3235/common.yaml b/tests/components/veml3235/common.yaml new file mode 100644 index 0000000000..b89a9e12c7 --- /dev/null +++ b/tests/components/veml3235/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_veml3235 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: veml3235 + id: veml3235_sensor + name: VEML3235 Light Sensor + auto_gain: true + auto_gain_threshold_high: 90% + auto_gain_threshold_low: 15% + digital_gain: 1X + gain: 1X + integration_time: 50ms diff --git a/tests/components/veml3235/test.esp32-ard.yaml b/tests/components/veml3235/test.esp32-ard.yaml index 3442fa4317..63c3bd6afd 100644 --- a/tests/components/veml3235/test.esp32-ard.yaml +++ b/tests/components/veml3235/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_veml3235 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: veml3235 - id: veml3235_sensor - name: VEML3235 Light Sensor - auto_gain: true - auto_gain_threshold_high: 90% - auto_gain_threshold_low: 15% - digital_gain: 1X - gain: 1X - integration_time: 50ms +<<: !include common.yaml diff --git a/tests/components/veml3235/test.esp32-c3-ard.yaml b/tests/components/veml3235/test.esp32-c3-ard.yaml index 1f79c5f50c..ee2c29ca4e 100644 --- a/tests/components/veml3235/test.esp32-c3-ard.yaml +++ b/tests/components/veml3235/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_veml3235 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: veml3235 - id: veml3235_sensor - name: VEML3235 Light Sensor - auto_gain: true - auto_gain_threshold_high: 90% - auto_gain_threshold_low: 15% - digital_gain: 1X - gain: 1X - integration_time: 50ms +<<: !include common.yaml diff --git a/tests/components/veml3235/test.esp32-c3-idf.yaml b/tests/components/veml3235/test.esp32-c3-idf.yaml index 1f79c5f50c..ee2c29ca4e 100644 --- a/tests/components/veml3235/test.esp32-c3-idf.yaml +++ b/tests/components/veml3235/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_veml3235 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: veml3235 - id: veml3235_sensor - name: VEML3235 Light Sensor - auto_gain: true - auto_gain_threshold_high: 90% - auto_gain_threshold_low: 15% - digital_gain: 1X - gain: 1X - integration_time: 50ms +<<: !include common.yaml diff --git a/tests/components/veml3235/test.esp32-idf.yaml b/tests/components/veml3235/test.esp32-idf.yaml index 3442fa4317..63c3bd6afd 100644 --- a/tests/components/veml3235/test.esp32-idf.yaml +++ b/tests/components/veml3235/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_veml3235 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: veml3235 - id: veml3235_sensor - name: VEML3235 Light Sensor - auto_gain: true - auto_gain_threshold_high: 90% - auto_gain_threshold_low: 15% - digital_gain: 1X - gain: 1X - integration_time: 50ms +<<: !include common.yaml diff --git a/tests/components/veml3235/test.esp8266-ard.yaml b/tests/components/veml3235/test.esp8266-ard.yaml index 1f79c5f50c..ee2c29ca4e 100644 --- a/tests/components/veml3235/test.esp8266-ard.yaml +++ b/tests/components/veml3235/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_veml3235 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: veml3235 - id: veml3235_sensor - name: VEML3235 Light Sensor - auto_gain: true - auto_gain_threshold_high: 90% - auto_gain_threshold_low: 15% - digital_gain: 1X - gain: 1X - integration_time: 50ms +<<: !include common.yaml diff --git a/tests/components/veml3235/test.rp2040-ard.yaml b/tests/components/veml3235/test.rp2040-ard.yaml index 1f79c5f50c..ee2c29ca4e 100644 --- a/tests/components/veml3235/test.rp2040-ard.yaml +++ b/tests/components/veml3235/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_veml3235 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: veml3235 - id: veml3235_sensor - name: VEML3235 Light Sensor - auto_gain: true - auto_gain_threshold_high: 90% - auto_gain_threshold_low: 15% - digital_gain: 1X - gain: 1X - integration_time: 50ms +<<: !include common.yaml diff --git a/tests/components/veml7700/common.yaml b/tests/components/veml7700/common.yaml index 6620c8d7e1..af4ebee6e7 100644 --- a/tests/components/veml7700/common.yaml +++ b/tests/components/veml7700/common.yaml @@ -1,3 +1,8 @@ +i2c: + - id: i2c_veml7700 + scl: ${scl_pin} + sda: ${sda_pin} + sensor: - platform: veml7700 address: 0x10 diff --git a/tests/components/veml7700/test.esp32-ard.yaml b/tests/components/veml7700/test.esp32-ard.yaml index 4b812a1392..63c3bd6afd 100644 --- a/tests/components/veml7700/test.esp32-ard.yaml +++ b/tests/components/veml7700/test.esp32-ard.yaml @@ -1,6 +1,5 @@ -i2c: - - id: i2c_veml7700 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 <<: !include common.yaml diff --git a/tests/components/veml7700/test.esp32-c3-ard.yaml b/tests/components/veml7700/test.esp32-c3-ard.yaml index ce0fa0125b..ee2c29ca4e 100644 --- a/tests/components/veml7700/test.esp32-c3-ard.yaml +++ b/tests/components/veml7700/test.esp32-c3-ard.yaml @@ -1,6 +1,5 @@ -i2c: - - id: i2c_veml7700 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/veml7700/test.esp32-c3-idf.yaml b/tests/components/veml7700/test.esp32-c3-idf.yaml index ce0fa0125b..ee2c29ca4e 100644 --- a/tests/components/veml7700/test.esp32-c3-idf.yaml +++ b/tests/components/veml7700/test.esp32-c3-idf.yaml @@ -1,6 +1,5 @@ -i2c: - - id: i2c_veml7700 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/veml7700/test.esp32-idf.yaml b/tests/components/veml7700/test.esp32-idf.yaml index 4b812a1392..63c3bd6afd 100644 --- a/tests/components/veml7700/test.esp32-idf.yaml +++ b/tests/components/veml7700/test.esp32-idf.yaml @@ -1,6 +1,5 @@ -i2c: - - id: i2c_veml7700 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 <<: !include common.yaml diff --git a/tests/components/veml7700/test.esp8266-ard.yaml b/tests/components/veml7700/test.esp8266-ard.yaml index ce0fa0125b..ee2c29ca4e 100644 --- a/tests/components/veml7700/test.esp8266-ard.yaml +++ b/tests/components/veml7700/test.esp8266-ard.yaml @@ -1,6 +1,5 @@ -i2c: - - id: i2c_veml7700 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/veml7700/test.rp2040-ard.yaml b/tests/components/veml7700/test.rp2040-ard.yaml index ce0fa0125b..ee2c29ca4e 100644 --- a/tests/components/veml7700/test.rp2040-ard.yaml +++ b/tests/components/veml7700/test.rp2040-ard.yaml @@ -1,6 +1,5 @@ -i2c: - - id: i2c_veml7700 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 <<: !include common.yaml diff --git a/tests/components/vl53l0x/common.yaml b/tests/components/vl53l0x/common.yaml new file mode 100644 index 0000000000..973e481b1a --- /dev/null +++ b/tests/components/vl53l0x/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_vl53l0x + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: vl53l0x + name: VL53L0x Distance + address: 0x29 + enable_pin: 3 + timeout: 200us + update_interval: 60s diff --git a/tests/components/vl53l0x/test.esp32-ard.yaml b/tests/components/vl53l0x/test.esp32-ard.yaml index 8f35de0e72..63c3bd6afd 100644 --- a/tests/components/vl53l0x/test.esp32-ard.yaml +++ b/tests/components/vl53l0x/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_vl53l0x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: vl53l0x - name: VL53L0x Distance - address: 0x29 - enable_pin: 3 - timeout: 200us - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp32-c3-ard.yaml b/tests/components/vl53l0x/test.esp32-c3-ard.yaml index 832f7dcfbc..ee2c29ca4e 100644 --- a/tests/components/vl53l0x/test.esp32-c3-ard.yaml +++ b/tests/components/vl53l0x/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_vl53l0x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: vl53l0x - name: VL53L0x Distance - address: 0x29 - enable_pin: 3 - timeout: 200us - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp32-c3-idf.yaml b/tests/components/vl53l0x/test.esp32-c3-idf.yaml index 832f7dcfbc..ee2c29ca4e 100644 --- a/tests/components/vl53l0x/test.esp32-c3-idf.yaml +++ b/tests/components/vl53l0x/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_vl53l0x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: vl53l0x - name: VL53L0x Distance - address: 0x29 - enable_pin: 3 - timeout: 200us - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp32-idf.yaml b/tests/components/vl53l0x/test.esp32-idf.yaml index 8f35de0e72..63c3bd6afd 100644 --- a/tests/components/vl53l0x/test.esp32-idf.yaml +++ b/tests/components/vl53l0x/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_vl53l0x - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: vl53l0x - name: VL53L0x Distance - address: 0x29 - enable_pin: 3 - timeout: 200us - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.esp8266-ard.yaml b/tests/components/vl53l0x/test.esp8266-ard.yaml index 832f7dcfbc..ee2c29ca4e 100644 --- a/tests/components/vl53l0x/test.esp8266-ard.yaml +++ b/tests/components/vl53l0x/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_vl53l0x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: vl53l0x - name: VL53L0x Distance - address: 0x29 - enable_pin: 3 - timeout: 200us - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/vl53l0x/test.rp2040-ard.yaml b/tests/components/vl53l0x/test.rp2040-ard.yaml index 832f7dcfbc..ee2c29ca4e 100644 --- a/tests/components/vl53l0x/test.rp2040-ard.yaml +++ b/tests/components/vl53l0x/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_vl53l0x - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: vl53l0x - name: VL53L0x Distance - address: 0x29 - enable_pin: 3 - timeout: 200us - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/voice_assistant/common.yaml b/tests/components/voice_assistant/common.yaml new file mode 100644 index 0000000000..e7374941f7 --- /dev/null +++ b/tests/components/voice_assistant/common.yaml @@ -0,0 +1,57 @@ +esphome: + on_boot: + then: + - voice_assistant.start + - voice_assistant.start_continuous + - voice_assistant.stop + +wifi: + ssid: MySSID + password: password1 + +api: + +i2s_audio: + i2s_lrclk_pin: ${i2s_lrclk_pin} + i2s_bclk_pin: ${i2s_bclk_pin} + i2s_mclk_pin: ${i2s_mclk_pin} + +microphone: + - platform: i2s_audio + id: mic_id_external + i2s_din_pin: ${i2s_din_pin} + adc_type: external + pdm: false + +speaker: + - platform: i2s_audio + id: speaker_id + dac_type: external + i2s_dout_pin: ${i2s_dout_pin} + +voice_assistant: + microphone: mic_id_external + speaker: speaker_id + conversation_timeout: 60s + on_listening: + - logger.log: "Voice assistant microphone listening" + on_start: + - logger.log: "Voice assistant started" + on_stt_end: + - logger.log: + format: "Voice assistant STT ended with result %s" + args: [x.c_str()] + on_tts_start: + - logger.log: + format: "Voice assistant TTS started with text %s" + args: [x.c_str()] + on_tts_end: + - logger.log: + format: "Voice assistant TTS ended with url %s" + args: [x.c_str()] + on_end: + - logger.log: "Voice assistant ended" + on_error: + - logger.log: + format: "Voice assistant error - code %s, message: %s" + args: [code.c_str(), message.c_str()] diff --git a/tests/components/voice_assistant/test.esp32-ard.yaml b/tests/components/voice_assistant/test.esp32-ard.yaml index cbf9460087..f6e553f9dc 100644 --- a/tests/components/voice_assistant/test.esp32-ard.yaml +++ b/tests/components/voice_assistant/test.esp32-ard.yaml @@ -1,57 +1,8 @@ -esphome: - on_boot: - then: - - voice_assistant.start - - voice_assistant.start_continuous - - voice_assistant.stop +substitutions: + i2s_lrclk_pin: GPIO16 + i2s_bclk_pin: GPIO17 + i2s_mclk_pin: GPIO15 + i2s_din_pin: GPIO13 + i2s_dout_pin: GPIO12 -wifi: - ssid: MySSID - password: password1 - -api: - -i2s_audio: - i2s_lrclk_pin: 16 - i2s_bclk_pin: 17 - i2s_mclk_pin: 15 - -microphone: - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 13 - adc_type: external - pdm: false - -speaker: - - platform: i2s_audio - id: speaker_id - dac_type: external - i2s_dout_pin: 12 - -voice_assistant: - microphone: mic_id_external - speaker: speaker_id - conversation_timeout: 60s - on_listening: - - logger.log: "Voice assistant microphone listening" - on_start: - - logger.log: "Voice assistant started" - on_stt_end: - - logger.log: - format: "Voice assistant STT ended with result %s" - args: [x.c_str()] - on_tts_start: - - logger.log: - format: "Voice assistant TTS started with text %s" - args: [x.c_str()] - on_tts_end: - - logger.log: - format: "Voice assistant TTS ended with url %s" - args: [x.c_str()] - on_end: - - logger.log: "Voice assistant ended" - on_error: - - logger.log: - format: "Voice assistant error - code %s, message: %s" - args: [code.c_str(), message.c_str()] +<<: !include common.yaml diff --git a/tests/components/voice_assistant/test.esp32-c3-ard.yaml b/tests/components/voice_assistant/test.esp32-c3-ard.yaml index 86357fad36..f596d927cb 100644 --- a/tests/components/voice_assistant/test.esp32-c3-ard.yaml +++ b/tests/components/voice_assistant/test.esp32-c3-ard.yaml @@ -1,56 +1,8 @@ -esphome: - on_boot: - then: - - voice_assistant.start - - voice_assistant.start_continuous - - voice_assistant.stop +substitutions: + i2s_lrclk_pin: GPIO6 + i2s_bclk_pin: GPIO7 + i2s_mclk_pin: GPIO5 + i2s_din_pin: GPIO3 + i2s_dout_pin: GPIO2 -wifi: - ssid: MySSID - password: password1 - -api: - -i2s_audio: - i2s_lrclk_pin: 6 - i2s_bclk_pin: 7 - i2s_mclk_pin: 5 - -microphone: - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 3 - adc_type: external - pdm: false - -speaker: - - platform: i2s_audio - id: speaker_id - dac_type: external - i2s_dout_pin: 2 - -voice_assistant: - microphone: mic_id_external - speaker: speaker_id - on_listening: - - logger.log: "Voice assistant microphone listening" - on_start: - - logger.log: "Voice assistant started" - on_stt_end: - - logger.log: - format: "Voice assistant STT ended with result %s" - args: [x.c_str()] - on_tts_start: - - logger.log: - format: "Voice assistant TTS started with text %s" - args: [x.c_str()] - on_tts_end: - - logger.log: - format: "Voice assistant TTS ended with url %s" - args: [x.c_str()] - on_end: - - logger.log: "Voice assistant ended" - on_error: - - logger.log: - format: "Voice assistant error - code %s, message: %s" - args: [code.c_str(), message.c_str()] +<<: !include common.yaml diff --git a/tests/components/voice_assistant/test.esp32-c3-idf.yaml b/tests/components/voice_assistant/test.esp32-c3-idf.yaml index 86357fad36..f596d927cb 100644 --- a/tests/components/voice_assistant/test.esp32-c3-idf.yaml +++ b/tests/components/voice_assistant/test.esp32-c3-idf.yaml @@ -1,56 +1,8 @@ -esphome: - on_boot: - then: - - voice_assistant.start - - voice_assistant.start_continuous - - voice_assistant.stop +substitutions: + i2s_lrclk_pin: GPIO6 + i2s_bclk_pin: GPIO7 + i2s_mclk_pin: GPIO5 + i2s_din_pin: GPIO3 + i2s_dout_pin: GPIO2 -wifi: - ssid: MySSID - password: password1 - -api: - -i2s_audio: - i2s_lrclk_pin: 6 - i2s_bclk_pin: 7 - i2s_mclk_pin: 5 - -microphone: - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 3 - adc_type: external - pdm: false - -speaker: - - platform: i2s_audio - id: speaker_id - dac_type: external - i2s_dout_pin: 2 - -voice_assistant: - microphone: mic_id_external - speaker: speaker_id - on_listening: - - logger.log: "Voice assistant microphone listening" - on_start: - - logger.log: "Voice assistant started" - on_stt_end: - - logger.log: - format: "Voice assistant STT ended with result %s" - args: [x.c_str()] - on_tts_start: - - logger.log: - format: "Voice assistant TTS started with text %s" - args: [x.c_str()] - on_tts_end: - - logger.log: - format: "Voice assistant TTS ended with url %s" - args: [x.c_str()] - on_end: - - logger.log: "Voice assistant ended" - on_error: - - logger.log: - format: "Voice assistant error - code %s, message: %s" - args: [code.c_str(), message.c_str()] +<<: !include common.yaml diff --git a/tests/components/voice_assistant/test.esp32-idf.yaml b/tests/components/voice_assistant/test.esp32-idf.yaml index da9b50721f..f6e553f9dc 100644 --- a/tests/components/voice_assistant/test.esp32-idf.yaml +++ b/tests/components/voice_assistant/test.esp32-idf.yaml @@ -1,56 +1,8 @@ -esphome: - on_boot: - then: - - voice_assistant.start - - voice_assistant.start_continuous - - voice_assistant.stop +substitutions: + i2s_lrclk_pin: GPIO16 + i2s_bclk_pin: GPIO17 + i2s_mclk_pin: GPIO15 + i2s_din_pin: GPIO13 + i2s_dout_pin: GPIO12 -wifi: - ssid: MySSID - password: password1 - -api: - -i2s_audio: - i2s_lrclk_pin: 16 - i2s_bclk_pin: 17 - i2s_mclk_pin: 15 - -microphone: - - platform: i2s_audio - id: mic_id_external - i2s_din_pin: 13 - adc_type: external - pdm: false - -speaker: - - platform: i2s_audio - id: speaker_id - dac_type: external - i2s_dout_pin: 12 - -voice_assistant: - microphone: mic_id_external - speaker: speaker_id - on_listening: - - logger.log: "Voice assistant microphone listening" - on_start: - - logger.log: "Voice assistant started" - on_stt_end: - - logger.log: - format: "Voice assistant STT ended with result %s" - args: [x.c_str()] - on_tts_start: - - logger.log: - format: "Voice assistant TTS started with text %s" - args: [x.c_str()] - on_tts_end: - - logger.log: - format: "Voice assistant TTS ended with url %s" - args: [x.c_str()] - on_end: - - logger.log: "Voice assistant ended" - on_error: - - logger.log: - format: "Voice assistant error - code %s, message: %s" - args: [code.c_str(), message.c_str()] +<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml new file mode 100644 index 0000000000..92c443908e --- /dev/null +++ b/tests/components/waveshare_epaper/common.yaml @@ -0,0 +1,190 @@ +spi: + - id: spi_waveshare_epaper + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + +display: + - platform: waveshare_epaper + model: 2.13in-ttgo-b1 + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.13in-ttgo-b74 + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.90in + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + reset_duration: 200ms + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.90inv2 + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.90in-dke + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.70in-b + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.70in-bv2 + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 1.54in-m5coreink-m09 + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.13inv3 + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.13inv2 + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 7.50in-bv3-bwr + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); diff --git a/tests/components/waveshare_epaper/test.esp32-ard.yaml b/tests/components/waveshare_epaper/test.esp32-ard.yaml index 944f98a1e9..c658ea91ee 100644 --- a/tests/components/waveshare_epaper/test.esp32-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp32-ard.yaml @@ -1,207 +1,9 @@ ---- -spi: - - id: spi_id_1 - clk_pin: - number: GPIO18 - mosi_pin: - number: GPIO23 - miso_pin: - number: GPIO19 - interface: hardware +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO18 + dc_pin: GPIO19 + busy_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: waveshare_epaper - model: 2.13in-ttgo-b1 - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.13in-ttgo-b74 - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.90in - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - full_update_every: 30 - reset_duration: 200ms - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.90inv2 - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.90in-dke - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - full_update_every: 1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.70in-b - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.70in-bv2 - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 1.54in-m5coreink-m09 - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.13inv3 - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.13inv2 - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 7.50in-bv3-bwr - spi_id: spi_id_1 - cs_pin: - allow_other_uses: true - number: GPIO25 - dc_pin: - allow_other_uses: true - number: GPIO26 - busy_pin: - allow_other_uses: true - number: GPIO27 - reset_pin: - allow_other_uses: true - number: GPIO32 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml b/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml index 5d651bd180..e2873f0fe9 100644 --- a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml @@ -1,123 +1,9 @@ -spi: - - id: spi_waveshare_epaper - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + cs_pin: GPIO0 + dc_pin: GPIO5 + busy_pin: GPIO3 + reset_pin: GPIO4 -display: - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.13in-ttgo-b1 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90in - full_update_every: 30 - reset_duration: 200ms - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90inv2 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-b - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-bv2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 1.54in-m5coreink-m09 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 7.50in-bv3-bwr - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml index 5d651bd180..e2873f0fe9 100644 --- a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml @@ -1,123 +1,9 @@ -spi: - - id: spi_waveshare_epaper - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + cs_pin: GPIO0 + dc_pin: GPIO5 + busy_pin: GPIO3 + reset_pin: GPIO4 -display: - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.13in-ttgo-b1 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90in - full_update_every: 30 - reset_duration: 200ms - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90inv2 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-b - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-bv2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 1.54in-m5coreink-m09 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 7.50in-bv3-bwr - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-idf.yaml b/tests/components/waveshare_epaper/test.esp32-idf.yaml index 47f894d967..c658ea91ee 100644 --- a/tests/components/waveshare_epaper/test.esp32-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-idf.yaml @@ -1,123 +1,9 @@ -spi: - - id: spi_bme280 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO16 + mosi_pin: GPIO17 + cs_pin: GPIO18 + dc_pin: GPIO19 + busy_pin: GPIO13 + reset_pin: GPIO14 -display: - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.13in-ttgo-b1 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90in - full_update_every: 30 - reset_duration: 200ms - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90inv2 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-b - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-bv2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 1.54in-m5coreink-m09 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 7.50in-bv3-bwr - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp8266-ard.yaml b/tests/components/waveshare_epaper/test.esp8266-ard.yaml index ceda328598..bc178b7009 100644 --- a/tests/components/waveshare_epaper/test.esp8266-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp8266-ard.yaml @@ -1,123 +1,9 @@ -spi: - - id: spi_bme280 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + cs_pin: GPIO15 + dc_pin: GPIO12 + busy_pin: GPIO5 + reset_pin: GPIO4 -display: - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.13in-ttgo-b1 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90in - full_update_every: 30 - reset_duration: 200ms - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.90inv2 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-b - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 2.70in-bv2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 1.54in-m5coreink-m09 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 4 - dc_pin: - allow_other_uses: true - number: 4 - busy_pin: - allow_other_uses: true - number: 4 - reset_pin: - allow_other_uses: true - number: 4 - model: 7.50in-bv3-bwr - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.rp2040-ard.yaml b/tests/components/waveshare_epaper/test.rp2040-ard.yaml index be7e780033..801b0b51c5 100644 --- a/tests/components/waveshare_epaper/test.rp2040-ard.yaml +++ b/tests/components/waveshare_epaper/test.rp2040-ard.yaml @@ -1,123 +1,9 @@ -spi: - - id: spi_bme280 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO2 + mosi_pin: GPIO3 + cs_pin: GPIO5 + dc_pin: GPIO4 + busy_pin: GPIO0 + reset_pin: GPIO1 -display: - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 5 - dc_pin: - allow_other_uses: true - number: 5 - busy_pin: - allow_other_uses: true - number: 5 - reset_pin: - allow_other_uses: true - number: 5 - model: 2.13in-ttgo-b1 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 5 - dc_pin: - allow_other_uses: true - number: 5 - busy_pin: - allow_other_uses: true - number: 5 - reset_pin: - allow_other_uses: true - number: 5 - model: 2.90in - full_update_every: 30 - reset_duration: 200ms - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 5 - dc_pin: - allow_other_uses: true - number: 5 - busy_pin: - allow_other_uses: true - number: 5 - reset_pin: - allow_other_uses: true - number: 5 - model: 2.90inv2 - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 5 - dc_pin: - allow_other_uses: true - number: 5 - busy_pin: - allow_other_uses: true - number: 5 - reset_pin: - allow_other_uses: true - number: 5 - model: 2.70in-b - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 5 - dc_pin: - allow_other_uses: true - number: 5 - busy_pin: - allow_other_uses: true - number: 5 - reset_pin: - allow_other_uses: true - number: 5 - model: 2.70in-bv2 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 5 - dc_pin: - allow_other_uses: true - number: 5 - busy_pin: - allow_other_uses: true - number: 5 - reset_pin: - allow_other_uses: true - number: 5 - model: 1.54in-m5coreink-m09 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - cs_pin: - allow_other_uses: true - number: 5 - dc_pin: - allow_other_uses: true - number: 5 - busy_pin: - allow_other_uses: true - number: 5 - reset_pin: - allow_other_uses: true - number: 5 - model: 7.50in-bv3-bwr - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); +<<: !include common.yaml diff --git a/tests/components/whirlpool/common.yaml b/tests/components/whirlpool/common.yaml new file mode 100644 index 0000000000..804c1aac26 --- /dev/null +++ b/tests/components/whirlpool/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: whirlpool + name: Whirlpool Climate diff --git a/tests/components/whirlpool/test.esp32-ard.yaml b/tests/components/whirlpool/test.esp32-ard.yaml index 4d0afc39a4..7b012aa64c 100644 --- a/tests/components/whirlpool/test.esp32-ard.yaml +++ b/tests/components/whirlpool/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whirlpool - name: Whirlpool Climate +<<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp32-c3-ard.yaml b/tests/components/whirlpool/test.esp32-c3-ard.yaml index 4d0afc39a4..7b012aa64c 100644 --- a/tests/components/whirlpool/test.esp32-c3-ard.yaml +++ b/tests/components/whirlpool/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whirlpool - name: Whirlpool Climate +<<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp32-c3-idf.yaml b/tests/components/whirlpool/test.esp32-c3-idf.yaml index 4d0afc39a4..7b012aa64c 100644 --- a/tests/components/whirlpool/test.esp32-c3-idf.yaml +++ b/tests/components/whirlpool/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whirlpool - name: Whirlpool Climate +<<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp32-idf.yaml b/tests/components/whirlpool/test.esp32-idf.yaml index 4d0afc39a4..7b012aa64c 100644 --- a/tests/components/whirlpool/test.esp32-idf.yaml +++ b/tests/components/whirlpool/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whirlpool - name: Whirlpool Climate +<<: !include common.yaml diff --git a/tests/components/whirlpool/test.esp8266-ard.yaml b/tests/components/whirlpool/test.esp8266-ard.yaml index efd530c160..f5097fcf5f 100644 --- a/tests/components/whirlpool/test.esp8266-ard.yaml +++ b/tests/components/whirlpool/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: whirlpool - name: Whirlpool Climate +<<: !include common.yaml diff --git a/tests/components/whynter/common.yaml b/tests/components/whynter/common.yaml new file mode 100644 index 0000000000..04ad6bed54 --- /dev/null +++ b/tests/components/whynter/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: whynter + name: Whynter Climate diff --git a/tests/components/whynter/test.esp32-ard.yaml b/tests/components/whynter/test.esp32-ard.yaml index dc8fb9584d..7b012aa64c 100644 --- a/tests/components/whynter/test.esp32-ard.yaml +++ b/tests/components/whynter/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whynter - name: Whynter Climate +<<: !include common.yaml diff --git a/tests/components/whynter/test.esp32-c3-ard.yaml b/tests/components/whynter/test.esp32-c3-ard.yaml index dc8fb9584d..7b012aa64c 100644 --- a/tests/components/whynter/test.esp32-c3-ard.yaml +++ b/tests/components/whynter/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whynter - name: Whynter Climate +<<: !include common.yaml diff --git a/tests/components/whynter/test.esp32-c3-idf.yaml b/tests/components/whynter/test.esp32-c3-idf.yaml index dc8fb9584d..7b012aa64c 100644 --- a/tests/components/whynter/test.esp32-c3-idf.yaml +++ b/tests/components/whynter/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whynter - name: Whynter Climate +<<: !include common.yaml diff --git a/tests/components/whynter/test.esp32-idf.yaml b/tests/components/whynter/test.esp32-idf.yaml index dc8fb9584d..7b012aa64c 100644 --- a/tests/components/whynter/test.esp32-idf.yaml +++ b/tests/components/whynter/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: whynter - name: Whynter Climate +<<: !include common.yaml diff --git a/tests/components/whynter/test.esp8266-ard.yaml b/tests/components/whynter/test.esp8266-ard.yaml index a656a7427d..f5097fcf5f 100644 --- a/tests/components/whynter/test.esp8266-ard.yaml +++ b/tests/components/whynter/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: whynter - name: Whynter Climate +<<: !include common.yaml diff --git a/tests/components/wl_134/common.yaml b/tests/components/wl_134/common.yaml new file mode 100644 index 0000000000..71c50be79b --- /dev/null +++ b/tests/components/wl_134/common.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_wl_134 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +text_sensor: + - platform: wl_134 + name: Transponder Code + reset: true diff --git a/tests/components/wl_134/test.esp32-ard.yaml b/tests/components/wl_134/test.esp32-ard.yaml index d517889d28..f486544afa 100644 --- a/tests/components/wl_134/test.esp32-ard.yaml +++ b/tests/components/wl_134/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_wl_134 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -text_sensor: - - platform: wl_134 - name: Transponder Code - reset: true +<<: !include common.yaml diff --git a/tests/components/wl_134/test.esp32-c3-ard.yaml b/tests/components/wl_134/test.esp32-c3-ard.yaml index 7cda1ba060..b516342f3b 100644 --- a/tests/components/wl_134/test.esp32-c3-ard.yaml +++ b/tests/components/wl_134/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_wl_134 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -text_sensor: - - platform: wl_134 - name: Transponder Code - reset: true +<<: !include common.yaml diff --git a/tests/components/wl_134/test.esp32-c3-idf.yaml b/tests/components/wl_134/test.esp32-c3-idf.yaml index 7cda1ba060..b516342f3b 100644 --- a/tests/components/wl_134/test.esp32-c3-idf.yaml +++ b/tests/components/wl_134/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_wl_134 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -text_sensor: - - platform: wl_134 - name: Transponder Code - reset: true +<<: !include common.yaml diff --git a/tests/components/wl_134/test.esp32-idf.yaml b/tests/components/wl_134/test.esp32-idf.yaml index d517889d28..f486544afa 100644 --- a/tests/components/wl_134/test.esp32-idf.yaml +++ b/tests/components/wl_134/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_wl_134 - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -text_sensor: - - platform: wl_134 - name: Transponder Code - reset: true +<<: !include common.yaml diff --git a/tests/components/wl_134/test.esp8266-ard.yaml b/tests/components/wl_134/test.esp8266-ard.yaml index 7cda1ba060..b516342f3b 100644 --- a/tests/components/wl_134/test.esp8266-ard.yaml +++ b/tests/components/wl_134/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_wl_134 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -text_sensor: - - platform: wl_134 - name: Transponder Code - reset: true +<<: !include common.yaml diff --git a/tests/components/wl_134/test.rp2040-ard.yaml b/tests/components/wl_134/test.rp2040-ard.yaml index 7cda1ba060..b516342f3b 100644 --- a/tests/components/wl_134/test.rp2040-ard.yaml +++ b/tests/components/wl_134/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_wl_134 - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -text_sensor: - - platform: wl_134 - name: Transponder Code - reset: true +<<: !include common.yaml diff --git a/tests/components/x9c/common.yaml b/tests/components/x9c/common.yaml new file mode 100644 index 0000000000..07713c66c3 --- /dev/null +++ b/tests/components/x9c/common.yaml @@ -0,0 +1,8 @@ +output: + - platform: x9c + id: test_x9c + cs_pin: ${cs_pin} + inc_pin: ${inc_pin} + ud_pin: ${ud_pin} + initial_value: 0.5 + step_delay: 50us diff --git a/tests/components/x9c/test.esp32-ard.yaml b/tests/components/x9c/test.esp32-ard.yaml index f587b69b4f..6dfe3a67eb 100644 --- a/tests/components/x9c/test.esp32-ard.yaml +++ b/tests/components/x9c/test.esp32-ard.yaml @@ -1,8 +1,6 @@ -output: - - platform: x9c - id: test_x9c - cs_pin: 13 - inc_pin: 14 - ud_pin: 15 - initial_value: 0.5 - step_delay: 50us +substitutions: + cs_pin: GPIO13 + inc_pin: GPIO14 + ud_pin: GPIO15 + +<<: !include common.yaml diff --git a/tests/components/x9c/test.esp32-c3-ard.yaml b/tests/components/x9c/test.esp32-c3-ard.yaml index 972c743fcd..b06e15a98c 100644 --- a/tests/components/x9c/test.esp32-c3-ard.yaml +++ b/tests/components/x9c/test.esp32-c3-ard.yaml @@ -1,8 +1,6 @@ -output: - - platform: x9c - id: test_x9c - cs_pin: 3 - inc_pin: 4 - ud_pin: 5 - initial_value: 0.5 - step_delay: 50us +substitutions: + cs_pin: GPIO3 + inc_pin: GPIO4 + ud_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/x9c/test.esp32-c3-idf.yaml b/tests/components/x9c/test.esp32-c3-idf.yaml index 972c743fcd..b06e15a98c 100644 --- a/tests/components/x9c/test.esp32-c3-idf.yaml +++ b/tests/components/x9c/test.esp32-c3-idf.yaml @@ -1,8 +1,6 @@ -output: - - platform: x9c - id: test_x9c - cs_pin: 3 - inc_pin: 4 - ud_pin: 5 - initial_value: 0.5 - step_delay: 50us +substitutions: + cs_pin: GPIO3 + inc_pin: GPIO4 + ud_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/x9c/test.esp32-idf.yaml b/tests/components/x9c/test.esp32-idf.yaml index f587b69b4f..6dfe3a67eb 100644 --- a/tests/components/x9c/test.esp32-idf.yaml +++ b/tests/components/x9c/test.esp32-idf.yaml @@ -1,8 +1,6 @@ -output: - - platform: x9c - id: test_x9c - cs_pin: 13 - inc_pin: 14 - ud_pin: 15 - initial_value: 0.5 - step_delay: 50us +substitutions: + cs_pin: GPIO13 + inc_pin: GPIO14 + ud_pin: GPIO15 + +<<: !include common.yaml diff --git a/tests/components/x9c/test.esp8266-ard.yaml b/tests/components/x9c/test.esp8266-ard.yaml index f587b69b4f..6dfe3a67eb 100644 --- a/tests/components/x9c/test.esp8266-ard.yaml +++ b/tests/components/x9c/test.esp8266-ard.yaml @@ -1,8 +1,6 @@ -output: - - platform: x9c - id: test_x9c - cs_pin: 13 - inc_pin: 14 - ud_pin: 15 - initial_value: 0.5 - step_delay: 50us +substitutions: + cs_pin: GPIO13 + inc_pin: GPIO14 + ud_pin: GPIO15 + +<<: !include common.yaml diff --git a/tests/components/x9c/test.rp2040-ard.yaml b/tests/components/x9c/test.rp2040-ard.yaml index 972c743fcd..b06e15a98c 100644 --- a/tests/components/x9c/test.rp2040-ard.yaml +++ b/tests/components/x9c/test.rp2040-ard.yaml @@ -1,8 +1,6 @@ -output: - - platform: x9c - id: test_x9c - cs_pin: 3 - inc_pin: 4 - ud_pin: 5 - initial_value: 0.5 - step_delay: 50us +substitutions: + cs_pin: GPIO3 + inc_pin: GPIO4 + ud_pin: GPIO5 + +<<: !include common.yaml diff --git a/tests/components/xgzp68xx/common.yaml b/tests/components/xgzp68xx/common.yaml new file mode 100644 index 0000000000..224dd9ed14 --- /dev/null +++ b/tests/components/xgzp68xx/common.yaml @@ -0,0 +1,12 @@ +i2c: + - id: i2c_xgzp68xx + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: xgzp68xx + k_value: 4096 + temperature: + name: Pressure Temperature + pressure: + name: Differential pressure diff --git a/tests/components/xgzp68xx/test.esp32-ard.yaml b/tests/components/xgzp68xx/test.esp32-ard.yaml index fb55421123..63c3bd6afd 100644 --- a/tests/components/xgzp68xx/test.esp32-ard.yaml +++ b/tests/components/xgzp68xx/test.esp32-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_xgzp68xx - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: xgzp68xx - k_value: 4096 - temperature: - name: Pressure Temperature - pressure: - name: Differential pressure +<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp32-c3-ard.yaml b/tests/components/xgzp68xx/test.esp32-c3-ard.yaml index 25df8cc225..ee2c29ca4e 100644 --- a/tests/components/xgzp68xx/test.esp32-c3-ard.yaml +++ b/tests/components/xgzp68xx/test.esp32-c3-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_xgzp68xx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: xgzp68xx - k_value: 4096 - temperature: - name: Pressure Temperature - pressure: - name: Differential pressure +<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp32-c3-idf.yaml b/tests/components/xgzp68xx/test.esp32-c3-idf.yaml index 25df8cc225..ee2c29ca4e 100644 --- a/tests/components/xgzp68xx/test.esp32-c3-idf.yaml +++ b/tests/components/xgzp68xx/test.esp32-c3-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_xgzp68xx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: xgzp68xx - k_value: 4096 - temperature: - name: Pressure Temperature - pressure: - name: Differential pressure +<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp32-idf.yaml b/tests/components/xgzp68xx/test.esp32-idf.yaml index fb55421123..63c3bd6afd 100644 --- a/tests/components/xgzp68xx/test.esp32-idf.yaml +++ b/tests/components/xgzp68xx/test.esp32-idf.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_xgzp68xx - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: xgzp68xx - k_value: 4096 - temperature: - name: Pressure Temperature - pressure: - name: Differential pressure +<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.esp8266-ard.yaml b/tests/components/xgzp68xx/test.esp8266-ard.yaml index 25df8cc225..ee2c29ca4e 100644 --- a/tests/components/xgzp68xx/test.esp8266-ard.yaml +++ b/tests/components/xgzp68xx/test.esp8266-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_xgzp68xx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: xgzp68xx - k_value: 4096 - temperature: - name: Pressure Temperature - pressure: - name: Differential pressure +<<: !include common.yaml diff --git a/tests/components/xgzp68xx/test.rp2040-ard.yaml b/tests/components/xgzp68xx/test.rp2040-ard.yaml index 25df8cc225..ee2c29ca4e 100644 --- a/tests/components/xgzp68xx/test.rp2040-ard.yaml +++ b/tests/components/xgzp68xx/test.rp2040-ard.yaml @@ -1,12 +1,5 @@ -i2c: - - id: i2c_xgzp68xx - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: xgzp68xx - k_value: 4096 - temperature: - name: Pressure Temperature - pressure: - name: Differential pressure +<<: !include common.yaml diff --git a/tests/components/xl9535/common.yaml b/tests/components/xl9535/common.yaml new file mode 100644 index 0000000000..e01163cf12 --- /dev/null +++ b/tests/components/xl9535/common.yaml @@ -0,0 +1,26 @@ +i2c: + - id: i2c_xl9535 + scl: ${scl_pin} + sda: ${sda_pin} + +xl9535: + - id: xl9535_hub + address: 0x20 + +binary_sensor: + - platform: gpio + name: XL9535 Pin 0 + pin: + xl9535: xl9535_hub + number: 0 + mode: + input: true + inverted: false + - platform: gpio + name: XL9535 Pin 17 + pin: + xl9535: xl9535_hub + number: 17 + mode: + input: true + inverted: false diff --git a/tests/components/xl9535/test.esp32-ard.yaml b/tests/components/xl9535/test.esp32-ard.yaml index a65aae890e..63c3bd6afd 100644 --- a/tests/components/xl9535/test.esp32-ard.yaml +++ b/tests/components/xl9535/test.esp32-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_xl9535 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -xl9535: - - id: xl9535_hub - address: 0x20 - -binary_sensor: - - platform: gpio - name: XL9535 Pin 0 - pin: - xl9535: xl9535_hub - number: 0 - mode: - input: true - inverted: false - - platform: gpio - name: XL9535 Pin 17 - pin: - xl9535: xl9535_hub - number: 17 - mode: - input: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/xl9535/test.esp32-c3-ard.yaml b/tests/components/xl9535/test.esp32-c3-ard.yaml index 178adf870e..ee2c29ca4e 100644 --- a/tests/components/xl9535/test.esp32-c3-ard.yaml +++ b/tests/components/xl9535/test.esp32-c3-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_xl9535 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -xl9535: - - id: xl9535_hub - address: 0x20 - -binary_sensor: - - platform: gpio - name: XL9535 Pin 0 - pin: - xl9535: xl9535_hub - number: 0 - mode: - input: true - inverted: false - - platform: gpio - name: XL9535 Pin 17 - pin: - xl9535: xl9535_hub - number: 17 - mode: - input: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/xl9535/test.esp32-c3-idf.yaml b/tests/components/xl9535/test.esp32-c3-idf.yaml index 178adf870e..ee2c29ca4e 100644 --- a/tests/components/xl9535/test.esp32-c3-idf.yaml +++ b/tests/components/xl9535/test.esp32-c3-idf.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_xl9535 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -xl9535: - - id: xl9535_hub - address: 0x20 - -binary_sensor: - - platform: gpio - name: XL9535 Pin 0 - pin: - xl9535: xl9535_hub - number: 0 - mode: - input: true - inverted: false - - platform: gpio - name: XL9535 Pin 17 - pin: - xl9535: xl9535_hub - number: 17 - mode: - input: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/xl9535/test.esp32-idf.yaml b/tests/components/xl9535/test.esp32-idf.yaml index a65aae890e..63c3bd6afd 100644 --- a/tests/components/xl9535/test.esp32-idf.yaml +++ b/tests/components/xl9535/test.esp32-idf.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_xl9535 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -xl9535: - - id: xl9535_hub - address: 0x20 - -binary_sensor: - - platform: gpio - name: XL9535 Pin 0 - pin: - xl9535: xl9535_hub - number: 0 - mode: - input: true - inverted: false - - platform: gpio - name: XL9535 Pin 17 - pin: - xl9535: xl9535_hub - number: 17 - mode: - input: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/xl9535/test.esp8266-ard.yaml b/tests/components/xl9535/test.esp8266-ard.yaml index 178adf870e..ee2c29ca4e 100644 --- a/tests/components/xl9535/test.esp8266-ard.yaml +++ b/tests/components/xl9535/test.esp8266-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_xl9535 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -xl9535: - - id: xl9535_hub - address: 0x20 - -binary_sensor: - - platform: gpio - name: XL9535 Pin 0 - pin: - xl9535: xl9535_hub - number: 0 - mode: - input: true - inverted: false - - platform: gpio - name: XL9535 Pin 17 - pin: - xl9535: xl9535_hub - number: 17 - mode: - input: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/xl9535/test.rp2040-ard.yaml b/tests/components/xl9535/test.rp2040-ard.yaml index 178adf870e..ee2c29ca4e 100644 --- a/tests/components/xl9535/test.rp2040-ard.yaml +++ b/tests/components/xl9535/test.rp2040-ard.yaml @@ -1,26 +1,5 @@ -i2c: - - id: i2c_xl9535 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -xl9535: - - id: xl9535_hub - address: 0x20 - -binary_sensor: - - platform: gpio - name: XL9535 Pin 0 - pin: - xl9535: xl9535_hub - number: 0 - mode: - input: true - inverted: false - - platform: gpio - name: XL9535 Pin 17 - pin: - xl9535: xl9535_hub - number: 17 - mode: - input: true - inverted: false +<<: !include common.yaml diff --git a/tests/components/xpt2046/common.yaml b/tests/components/xpt2046/common.yaml new file mode 100644 index 0000000000..9ef680cff4 --- /dev/null +++ b/tests/components/xpt2046/common.yaml @@ -0,0 +1,35 @@ +spi: + - id: spi_xpt2046 + clk_pin: ${clk_pin} + mosi_pin: ${mosi_pin} + miso_pin: ${miso_pin} + +display: + - platform: ili9xxx + id: xpt_display + dimensions: 320x240 + model: TFT 2.4 + cs_pin: ${disp_cs_pin} + dc_pin: ${dc_pin} + reset_pin: ${reset_pin} + invert_colors: false + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: xpt2046 + id: xpt_touchscreen + cs_pin: ${cs_pin} + interrupt_pin: ${interrupt_pin} + display: xpt_display + update_interval: 50ms + threshold: 400 + calibration: + x_min: 280 + x_max: 3860 + y_min: 340 + y_max: 3860 + on_touch: + - logger.log: + format: Touch at (%d, %d) + args: [touch.x, touch.y] diff --git a/tests/components/xpt2046/test.esp32-ard.yaml b/tests/components/xpt2046/test.esp32-ard.yaml index 9e305791e0..b39174947b 100644 --- a/tests/components/xpt2046/test.esp32-ard.yaml +++ b/tests/components/xpt2046/test.esp32-ard.yaml @@ -1,35 +1,11 @@ -spi: - - id: spi_xpt2046 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO17 + mosi_pin: GPIO18 + miso_pin: GPIO19 + dc_pin: GPIO13 + cs_pin: GPIO14 + disp_cs_pin: GPIO4 + interrupt_pin: GPIO21 + reset_pin: GPIO22 -display: - - platform: ili9xxx - id: xpt_display - dimensions: 320x240 - model: TFT 2.4 - cs_pin: 13 - dc_pin: 14 - reset_pin: 21 - invert_colors: false - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: xpt2046 - id: xpt_touchscreen - cs_pin: 18 - interrupt_pin: 19 - display: xpt_display - update_interval: 50ms - threshold: 400 - calibration: - x_min: 280 - x_max: 3860 - y_min: 340 - y_max: 3860 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-c3-ard.yaml b/tests/components/xpt2046/test.esp32-c3-ard.yaml index c03fd6b345..79b84902ac 100644 --- a/tests/components/xpt2046/test.esp32-c3-ard.yaml +++ b/tests/components/xpt2046/test.esp32-c3-ard.yaml @@ -1,35 +1,11 @@ -spi: - - id: spi_xpt2046 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO4 + mosi_pin: GPIO5 + miso_pin: GPIO6 + dc_pin: GPIO7 + cs_pin: GPIO0 + disp_cs_pin: GPIO1 + interrupt_pin: GPIO3 + reset_pin: GPIO10 -display: - - platform: ili9xxx - id: xpt_display - dimensions: 320x240 - model: TFT 2.4 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: xpt2046 - id: xpt_touchscreen - cs_pin: 4 - interrupt_pin: 3 - display: xpt_display - update_interval: 50ms - threshold: 400 - calibration: - x_min: 28 - x_max: 280 - y_min: 340 - y_max: 3860 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-c3-idf.yaml b/tests/components/xpt2046/test.esp32-c3-idf.yaml index 787ca9b1ed..79b84902ac 100644 --- a/tests/components/xpt2046/test.esp32-c3-idf.yaml +++ b/tests/components/xpt2046/test.esp32-c3-idf.yaml @@ -1,35 +1,11 @@ -spi: - - id: spi_xpt2046 - clk_pin: 6 - mosi_pin: 7 - miso_pin: 5 +substitutions: + clk_pin: GPIO4 + mosi_pin: GPIO5 + miso_pin: GPIO6 + dc_pin: GPIO7 + cs_pin: GPIO0 + disp_cs_pin: GPIO1 + interrupt_pin: GPIO3 + reset_pin: GPIO10 -display: - - platform: ili9xxx - id: xpt_display - dimensions: 320x240 - model: TFT 2.4 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: xpt2046 - id: xpt_touchscreen - cs_pin: 4 - interrupt_pin: 3 - display: xpt_display - update_interval: 50ms - threshold: 400 - calibration: - x_min: 50 - x_max: 280 - y_min: 340 - y_max: 3860 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-idf.yaml b/tests/components/xpt2046/test.esp32-idf.yaml index e79997146b..b39174947b 100644 --- a/tests/components/xpt2046/test.esp32-idf.yaml +++ b/tests/components/xpt2046/test.esp32-idf.yaml @@ -1,35 +1,11 @@ -spi: - - id: spi_xpt2046 - clk_pin: 16 - mosi_pin: 17 - miso_pin: 15 +substitutions: + clk_pin: GPIO17 + mosi_pin: GPIO18 + miso_pin: GPIO19 + dc_pin: GPIO13 + cs_pin: GPIO14 + disp_cs_pin: GPIO4 + interrupt_pin: GPIO21 + reset_pin: GPIO22 -display: - - platform: ili9xxx - id: xpt_display - dimensions: 320x240 - model: TFT 2.4 - cs_pin: 13 - dc_pin: 14 - reset_pin: 21 - invert_colors: false - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: xpt2046 - id: xpt_touchscreen - cs_pin: 18 - interrupt_pin: 19 - display: xpt_display - update_interval: 50ms - threshold: 400 - calibration: - x_min: 50 - x_max: 280 - y_min: 340 - y_max: 3860 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/xpt2046/test.esp32-s2-ard.yaml b/tests/components/xpt2046/test.esp32-s2-ard.yaml deleted file mode 100644 index df2a99b4f5..0000000000 --- a/tests/components/xpt2046/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,38 +0,0 @@ -spi: - clk_pin: 7 - mosi_pin: 11 - miso_pin: 9 - -display: - - platform: ili9xxx - id: my_display - model: ili9341 - cs_pin: 5 - dc_pin: 12 - reset_pin: 33 - auto_clear_enabled: false - data_rate: 40MHz - dimensions: 320x240 - update_interval: never - invert_colors: false - transform: - mirror_y: false - mirror_x: false - swap_xy: true - -touchscreen: - - platform: xpt2046 - display: my_display - id: my_toucher - update_interval: 50ms - cs_pin: 18 - threshold: 300 - calibration: - x_min: 210 - x_max: 3890 - y_min: 170 - y_max: 3730 - transform: - mirror_x: false - mirror_y: true - swap_xy: true diff --git a/tests/components/xpt2046/test.esp8266-ard.yaml b/tests/components/xpt2046/test.esp8266-ard.yaml index ab71f7b8bc..246c5c8953 100644 --- a/tests/components/xpt2046/test.esp8266-ard.yaml +++ b/tests/components/xpt2046/test.esp8266-ard.yaml @@ -1,35 +1,11 @@ -spi: - - id: spi_xpt2046 - clk_pin: 14 - mosi_pin: 13 - miso_pin: 12 +substitutions: + clk_pin: GPIO14 + mosi_pin: GPIO13 + miso_pin: GPIO12 + dc_pin: GPIO15 + cs_pin: GPIO16 + disp_cs_pin: GPIO4 + interrupt_pin: GPIO5 + reset_pin: GPIO2 -display: - - platform: ili9xxx - id: xpt_display - dimensions: 320x240 - model: TFT 2.4 - cs_pin: 15 - dc_pin: 4 - reset_pin: 5 - invert_colors: false - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: xpt2046 - id: xpt_touchscreen - cs_pin: 0 - interrupt_pin: 16 - display: xpt_display - update_interval: 50ms - threshold: 400 - calibration: - x_min: 50 - x_max: 280 - y_min: 340 - y_max: 3860 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/xpt2046/test.rp2040-ard.yaml b/tests/components/xpt2046/test.rp2040-ard.yaml index 622e69ac98..e693b363d9 100644 --- a/tests/components/xpt2046/test.rp2040-ard.yaml +++ b/tests/components/xpt2046/test.rp2040-ard.yaml @@ -1,35 +1,11 @@ -spi: - - id: spi_xpt2046 - clk_pin: 2 - mosi_pin: 3 - miso_pin: 4 +substitutions: + clk_pin: GPIO6 + mosi_pin: GPIO7 + miso_pin: GPIO8 + dc_pin: GPIO9 + cs_pin: GPIO0 + disp_cs_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ili9xxx - id: xpt_display - dimensions: 320x240 - model: TFT 2.4 - cs_pin: 8 - dc_pin: 9 - reset_pin: 10 - invert_colors: false - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: xpt2046 - id: xpt_touchscreen - cs_pin: 5 - interrupt_pin: 6 - display: xpt_display - update_interval: 50ms - threshold: 400 - calibration: - x_min: 280 - x_max: 3860 - y_min: 340 - y_max: 3860 - on_touch: - - logger.log: - format: Touch at (%d, %d) - args: [touch.x, touch.y] +<<: !include common.yaml diff --git a/tests/components/yashima/common.yaml b/tests/components/yashima/common.yaml new file mode 100644 index 0000000000..bfe181f1a6 --- /dev/null +++ b/tests/components/yashima/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: yashima + name: Yashima Climate diff --git a/tests/components/yashima/test.esp32-ard.yaml b/tests/components/yashima/test.esp32-ard.yaml index 4b6d6daee4..7b012aa64c 100644 --- a/tests/components/yashima/test.esp32-ard.yaml +++ b/tests/components/yashima/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: yashima - name: Yashima Climate +<<: !include common.yaml diff --git a/tests/components/yashima/test.esp32-c3-ard.yaml b/tests/components/yashima/test.esp32-c3-ard.yaml index 4b6d6daee4..7b012aa64c 100644 --- a/tests/components/yashima/test.esp32-c3-ard.yaml +++ b/tests/components/yashima/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: yashima - name: Yashima Climate +<<: !include common.yaml diff --git a/tests/components/yashima/test.esp32-c3-idf.yaml b/tests/components/yashima/test.esp32-c3-idf.yaml index 4b6d6daee4..7b012aa64c 100644 --- a/tests/components/yashima/test.esp32-c3-idf.yaml +++ b/tests/components/yashima/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: yashima - name: Yashima Climate +<<: !include common.yaml diff --git a/tests/components/yashima/test.esp32-idf.yaml b/tests/components/yashima/test.esp32-idf.yaml index 4b6d6daee4..7b012aa64c 100644 --- a/tests/components/yashima/test.esp32-idf.yaml +++ b/tests/components/yashima/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: yashima - name: Yashima Climate +<<: !include common.yaml diff --git a/tests/components/yashima/test.esp8266-ard.yaml b/tests/components/yashima/test.esp8266-ard.yaml index 296a7ede25..f5097fcf5f 100644 --- a/tests/components/yashima/test.esp8266-ard.yaml +++ b/tests/components/yashima/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: yashima - name: Yashima Climate +<<: !include common.yaml diff --git a/tests/components/zhlt01/common.yaml b/tests/components/zhlt01/common.yaml new file mode 100644 index 0000000000..0adbe77325 --- /dev/null +++ b/tests/components/zhlt01/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: zhlt01 + name: ZH/LT-01 Climate diff --git a/tests/components/zhlt01/test.esp32-ard.yaml b/tests/components/zhlt01/test.esp32-ard.yaml index d1dc3b4926..7b012aa64c 100644 --- a/tests/components/zhlt01/test.esp32-ard.yaml +++ b/tests/components/zhlt01/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: zhlt01 - name: ZH/LT-01 Climate +<<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp32-c3-ard.yaml b/tests/components/zhlt01/test.esp32-c3-ard.yaml index d1dc3b4926..7b012aa64c 100644 --- a/tests/components/zhlt01/test.esp32-c3-ard.yaml +++ b/tests/components/zhlt01/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: zhlt01 - name: ZH/LT-01 Climate +<<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp32-c3-idf.yaml b/tests/components/zhlt01/test.esp32-c3-idf.yaml index d1dc3b4926..7b012aa64c 100644 --- a/tests/components/zhlt01/test.esp32-c3-idf.yaml +++ b/tests/components/zhlt01/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: zhlt01 - name: ZH/LT-01 Climate +<<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp32-idf.yaml b/tests/components/zhlt01/test.esp32-idf.yaml index d1dc3b4926..7b012aa64c 100644 --- a/tests/components/zhlt01/test.esp32-idf.yaml +++ b/tests/components/zhlt01/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: zhlt01 - name: ZH/LT-01 Climate +<<: !include common.yaml diff --git a/tests/components/zhlt01/test.esp8266-ard.yaml b/tests/components/zhlt01/test.esp8266-ard.yaml index 40a00bc458..f5097fcf5f 100644 --- a/tests/components/zhlt01/test.esp8266-ard.yaml +++ b/tests/components/zhlt01/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: zhlt01 - name: ZH/LT-01 Climate +<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/common.yaml b/tests/components/zio_ultrasonic/common.yaml new file mode 100644 index 0000000000..e13853d8f1 --- /dev/null +++ b/tests/components/zio_ultrasonic/common.yaml @@ -0,0 +1,9 @@ +i2c: + - id: i2c_zio_ultrasonic + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: zio_ultrasonic + name: "Distance" + update_interval: 60s diff --git a/tests/components/zio_ultrasonic/test.esp32-ard.yaml b/tests/components/zio_ultrasonic/test.esp32-ard.yaml index ad4050307e..63c3bd6afd 100644 --- a/tests/components/zio_ultrasonic/test.esp32-ard.yaml +++ b/tests/components/zio_ultrasonic/test.esp32-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_zio_ultrasonic - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: zio_ultrasonic - name: "Distance" - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml b/tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml index 36e1697a38..ee2c29ca4e 100644 --- a/tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml +++ b/tests/components/zio_ultrasonic/test.esp32-c3-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_zio_ultrasonic - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: zio_ultrasonic - name: "Distance" - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml b/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml index 36e1697a38..ee2c29ca4e 100644 --- a/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml +++ b/tests/components/zio_ultrasonic/test.esp32-c3-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_zio_ultrasonic - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: zio_ultrasonic - name: "Distance" - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp32-idf.yaml b/tests/components/zio_ultrasonic/test.esp32-idf.yaml index ad4050307e..63c3bd6afd 100644 --- a/tests/components/zio_ultrasonic/test.esp32-idf.yaml +++ b/tests/components/zio_ultrasonic/test.esp32-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_zio_ultrasonic - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: zio_ultrasonic - name: "Distance" - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.esp8266-ard.yaml b/tests/components/zio_ultrasonic/test.esp8266-ard.yaml index 36e1697a38..ee2c29ca4e 100644 --- a/tests/components/zio_ultrasonic/test.esp8266-ard.yaml +++ b/tests/components/zio_ultrasonic/test.esp8266-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_zio_ultrasonic - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: zio_ultrasonic - name: "Distance" - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/zio_ultrasonic/test.rp2040-ard.yaml b/tests/components/zio_ultrasonic/test.rp2040-ard.yaml index 36e1697a38..ee2c29ca4e 100644 --- a/tests/components/zio_ultrasonic/test.rp2040-ard.yaml +++ b/tests/components/zio_ultrasonic/test.rp2040-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_zio_ultrasonic - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: zio_ultrasonic - name: "Distance" - update_interval: 60s +<<: !include common.yaml diff --git a/tests/components/zyaura/common.yaml b/tests/components/zyaura/common.yaml new file mode 100644 index 0000000000..e2a994a317 --- /dev/null +++ b/tests/components/zyaura/common.yaml @@ -0,0 +1,10 @@ +sensor: + - platform: zyaura + clock_pin: ${clock_pin} + data_pin: ${data_pin} + co2: + name: ZyAura CO2 + temperature: + name: ZyAura Temperature + humidity: + name: ZyAura Humidity diff --git a/tests/components/zyaura/test.esp32-ard.yaml b/tests/components/zyaura/test.esp32-ard.yaml index 29116a978b..d295973e3f 100644 --- a/tests/components/zyaura/test.esp32-ard.yaml +++ b/tests/components/zyaura/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -sensor: - - platform: zyaura - clock_pin: 16 - data_pin: 17 - co2: - name: ZyAura CO2 - temperature: - name: ZyAura Temperature - humidity: - name: ZyAura Humidity +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/zyaura/test.esp32-c3-ard.yaml b/tests/components/zyaura/test.esp32-c3-ard.yaml index 90205c468c..7808481215 100644 --- a/tests/components/zyaura/test.esp32-c3-ard.yaml +++ b/tests/components/zyaura/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -sensor: - - platform: zyaura - clock_pin: 5 - data_pin: 4 - co2: - name: ZyAura CO2 - temperature: - name: ZyAura Temperature - humidity: - name: ZyAura Humidity +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/zyaura/test.esp32-c3-idf.yaml b/tests/components/zyaura/test.esp32-c3-idf.yaml index 90205c468c..7808481215 100644 --- a/tests/components/zyaura/test.esp32-c3-idf.yaml +++ b/tests/components/zyaura/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -sensor: - - platform: zyaura - clock_pin: 5 - data_pin: 4 - co2: - name: ZyAura CO2 - temperature: - name: ZyAura Temperature - humidity: - name: ZyAura Humidity +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/zyaura/test.esp32-idf.yaml b/tests/components/zyaura/test.esp32-idf.yaml index 29116a978b..d295973e3f 100644 --- a/tests/components/zyaura/test.esp32-idf.yaml +++ b/tests/components/zyaura/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -sensor: - - platform: zyaura - clock_pin: 16 - data_pin: 17 - co2: - name: ZyAura CO2 - temperature: - name: ZyAura Temperature - humidity: - name: ZyAura Humidity +substitutions: + clock_pin: GPIO16 + data_pin: GPIO17 + +<<: !include common.yaml diff --git a/tests/components/zyaura/test.esp8266-ard.yaml b/tests/components/zyaura/test.esp8266-ard.yaml index 90205c468c..7808481215 100644 --- a/tests/components/zyaura/test.esp8266-ard.yaml +++ b/tests/components/zyaura/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -sensor: - - platform: zyaura - clock_pin: 5 - data_pin: 4 - co2: - name: ZyAura CO2 - temperature: - name: ZyAura Temperature - humidity: - name: ZyAura Humidity +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + +<<: !include common.yaml diff --git a/tests/components/zyaura/test.rp2040-ard.yaml b/tests/components/zyaura/test.rp2040-ard.yaml index 90205c468c..7808481215 100644 --- a/tests/components/zyaura/test.rp2040-ard.yaml +++ b/tests/components/zyaura/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -sensor: - - platform: zyaura - clock_pin: 5 - data_pin: 4 - co2: - name: ZyAura CO2 - temperature: - name: ZyAura Temperature - humidity: - name: ZyAura Humidity +substitutions: + clock_pin: GPIO5 + data_pin: GPIO4 + +<<: !include common.yaml From 8897a9866d4c80df8e528aacd73d05c604ba83a7 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 9 Feb 2025 15:43:21 -0600 Subject: [PATCH 0978/1052] [CI] Consolidate some tests (T) (#8208) --- tests/components/t6615/common.yaml | 10 +++ tests/components/t6615/test.esp32-ard.yaml | 13 +-- tests/components/t6615/test.esp32-c3-ard.yaml | 13 +-- tests/components/t6615/test.esp32-c3-idf.yaml | 13 +-- tests/components/t6615/test.esp32-idf.yaml | 13 +-- tests/components/t6615/test.esp8266-ard.yaml | 13 +-- tests/components/t6615/test.rp2040-ard.yaml | 13 +-- tests/components/tc74/common.yaml | 8 ++ tests/components/tc74/test.esp32-ard.yaml | 11 +-- tests/components/tc74/test.esp32-c3-ard.yaml | 11 +-- tests/components/tc74/test.esp32-c3-idf.yaml | 11 +-- tests/components/tc74/test.esp32-idf.yaml | 11 +-- tests/components/tc74/test.esp8266-ard.yaml | 11 +-- tests/components/tc74/test.rp2040-ard.yaml | 11 +-- tests/components/tca9548a/common.yaml | 15 ++++ tests/components/tca9548a/test.esp32-ard.yaml | 18 +--- .../tca9548a/test.esp32-c3-ard.yaml | 18 +--- .../tca9548a/test.esp32-c3-idf.yaml | 18 +--- tests/components/tca9548a/test.esp32-idf.yaml | 18 +--- .../components/tca9548a/test.esp8266-ard.yaml | 18 +--- .../components/tca9548a/test.rp2040-ard.yaml | 18 +--- tests/components/tca9555/common.yaml | 27 ++++++ tests/components/tca9555/test.esp32-ard.yaml | 30 +------ .../components/tca9555/test.esp32-c3-ard.yaml | 30 +------ .../components/tca9555/test.esp32-c3-idf.yaml | 30 +------ tests/components/tca9555/test.esp32-idf.yaml | 30 +------ .../components/tca9555/test.esp8266-ard.yaml | 30 +------ tests/components/tca9555/test.rp2040-ard.yaml | 30 +------ tests/components/tcl112/common.yaml | 15 ++++ tests/components/tcl112/test.esp32-ard.yaml | 17 +--- .../components/tcl112/test.esp32-c3-ard.yaml | 17 +--- .../components/tcl112/test.esp32-c3-idf.yaml | 17 +--- tests/components/tcl112/test.esp32-idf.yaml | 17 +--- tests/components/tcl112/test.esp8266-ard.yaml | 17 +--- tests/components/tcs34725/common.yaml | 21 +++++ tests/components/tcs34725/test.esp32-ard.yaml | 24 +----- .../tcs34725/test.esp32-c3-ard.yaml | 24 +----- .../tcs34725/test.esp32-c3-idf.yaml | 24 +----- tests/components/tcs34725/test.esp32-idf.yaml | 24 +----- .../components/tcs34725/test.esp8266-ard.yaml | 24 +----- .../components/tcs34725/test.rp2040-ard.yaml | 24 +----- tests/components/tee501/common.yaml | 9 ++ tests/components/tee501/test.esp32-ard.yaml | 12 +-- .../components/tee501/test.esp32-c3-ard.yaml | 12 +-- .../components/tee501/test.esp32-c3-idf.yaml | 12 +-- tests/components/tee501/test.esp32-idf.yaml | 12 +-- tests/components/tee501/test.esp8266-ard.yaml | 12 +-- tests/components/tee501/test.rp2040-ard.yaml | 12 +-- tests/components/teleinfo/common.yaml | 42 ++++++++++ tests/components/teleinfo/test.esp32-ard.yaml | 45 +--------- .../teleinfo/test.esp32-c3-ard.yaml | 45 +--------- .../teleinfo/test.esp32-c3-idf.yaml | 45 +--------- tests/components/teleinfo/test.esp32-idf.yaml | 45 +--------- .../components/teleinfo/test.esp8266-ard.yaml | 45 +--------- .../components/teleinfo/test.rp2040-ard.yaml | 45 +--------- tests/components/tlc59208f/common.yaml | 50 +++++++++++ .../components/tlc59208f/test.esp32-ard.yaml | 53 +----------- .../tlc59208f/test.esp32-c3-ard.yaml | 53 +----------- .../tlc59208f/test.esp32-c3-idf.yaml | 53 +----------- .../components/tlc59208f/test.esp32-idf.yaml | 53 +----------- .../tlc59208f/test.esp8266-ard.yaml | 53 +----------- .../components/tlc59208f/test.rp2040-ard.yaml | 53 +----------- tests/components/tm1621/common.yaml | 12 +++ tests/components/tm1621/test.esp32-ard.yaml | 19 ++--- .../components/tm1621/test.esp32-c3-ard.yaml | 19 ++--- .../components/tm1621/test.esp32-c3-idf.yaml | 19 ++--- tests/components/tm1621/test.esp32-idf.yaml | 19 ++--- tests/components/tm1621/test.esp8266-ard.yaml | 19 ++--- tests/components/tm1621/test.rp2040-ard.yaml | 19 ++--- tests/components/tm1637/common.yaml | 7 ++ tests/components/tm1637/test.esp32-ard.yaml | 12 ++- .../components/tm1637/test.esp32-c3-ard.yaml | 12 ++- .../components/tm1637/test.esp32-c3-idf.yaml | 12 ++- tests/components/tm1637/test.esp32-idf.yaml | 12 ++- tests/components/tm1637/test.esp8266-ard.yaml | 12 ++- tests/components/tm1637/test.rp2040-ard.yaml | 12 ++- tests/components/tmp102/common.yaml | 8 ++ tests/components/tmp102/test.esp32-ard.yaml | 11 +-- .../components/tmp102/test.esp32-c3-ard.yaml | 11 +-- .../components/tmp102/test.esp32-c3-idf.yaml | 11 +-- tests/components/tmp102/test.esp32-idf.yaml | 11 +-- tests/components/tmp102/test.esp8266-ard.yaml | 11 +-- tests/components/tmp102/test.rp2040-ard.yaml | 11 +-- tests/components/tmp1075/common.yaml | 16 ++++ tests/components/tmp1075/test.esp32-ard.yaml | 19 +---- .../components/tmp1075/test.esp32-c3-ard.yaml | 19 +---- .../components/tmp1075/test.esp32-c3-idf.yaml | 19 +---- tests/components/tmp1075/test.esp32-idf.yaml | 19 +---- .../components/tmp1075/test.esp8266-ard.yaml | 19 +---- tests/components/tmp1075/test.rp2040-ard.yaml | 19 +---- tests/components/tmp117/common.yaml | 9 ++ tests/components/tmp117/test.esp32-ard.yaml | 12 +-- .../components/tmp117/test.esp32-c3-ard.yaml | 12 +-- .../components/tmp117/test.esp32-c3-idf.yaml | 12 +-- tests/components/tmp117/test.esp32-idf.yaml | 12 +-- tests/components/tmp117/test.esp8266-ard.yaml | 12 +-- tests/components/tmp117/test.rp2040-ard.yaml | 12 +-- tests/components/tof10120/common.yaml | 9 ++ tests/components/tof10120/test.esp32-ard.yaml | 12 +-- .../tof10120/test.esp32-c3-ard.yaml | 12 +-- .../tof10120/test.esp32-c3-idf.yaml | 12 +-- tests/components/tof10120/test.esp32-idf.yaml | 12 +-- .../components/tof10120/test.esp8266-ard.yaml | 12 +-- .../components/tof10120/test.rp2040-ard.yaml | 12 +-- tests/components/toshiba/common.yaml | 7 ++ tests/components/toshiba/test.esp32-ard.yaml | 9 +- .../components/toshiba/test.esp32-c3-ard.yaml | 9 +- .../components/toshiba/test.esp32-c3-idf.yaml | 9 +- tests/components/toshiba/test.esp32-idf.yaml | 9 +- .../components/toshiba/test.esp8266-ard.yaml | 9 +- .../components/total_daily_energy/common.yaml | 32 ++++++++ .../total_daily_energy/test.esp32-ard.yaml | 36 ++------ .../total_daily_energy/test.esp32-c3-ard.yaml | 36 ++------ .../total_daily_energy/test.esp32-c3-idf.yaml | 36 ++------ .../total_daily_energy/test.esp32-idf.yaml | 36 ++------ .../total_daily_energy/test.esp8266-ard.yaml | 36 ++------ .../total_daily_energy/test.rp2040-ard.yaml | 36 ++------ tests/components/tsl2561/common.yaml | 13 +++ tests/components/tsl2561/test.esp32-ard.yaml | 16 +--- .../components/tsl2561/test.esp32-c3-ard.yaml | 16 +--- .../components/tsl2561/test.esp32-c3-idf.yaml | 16 +--- tests/components/tsl2561/test.esp32-idf.yaml | 16 +--- .../components/tsl2561/test.esp8266-ard.yaml | 16 +--- tests/components/tsl2561/test.rp2040-ard.yaml | 16 +--- tests/components/tsl2591/common.yaml | 25 ++++++ tests/components/tsl2591/test.esp32-ard.yaml | 28 +------ .../components/tsl2591/test.esp32-c3-ard.yaml | 28 +------ .../components/tsl2591/test.esp32-c3-idf.yaml | 28 +------ tests/components/tsl2591/test.esp32-idf.yaml | 28 +------ .../components/tsl2591/test.esp8266-ard.yaml | 28 +------ tests/components/tsl2591/test.rp2040-ard.yaml | 28 +------ tests/components/tt21100/common.yaml | 25 ++++++ tests/components/tt21100/test.esp32-ard.yaml | 31 ++----- .../components/tt21100/test.esp32-c3-ard.yaml | 31 ++----- .../components/tt21100/test.esp32-c3-idf.yaml | 31 ++----- tests/components/tt21100/test.esp32-idf.yaml | 31 ++----- .../components/tt21100/test.esp32-s2-ard.yaml | 44 ---------- .../components/tt21100/test.esp8266-ard.yaml | 31 ++----- tests/components/tt21100/test.rp2040-ard.yaml | 31 ++----- tests/components/ttp229_bsf/common.yaml | 8 ++ .../components/ttp229_bsf/test.esp32-ard.yaml | 11 +-- .../ttp229_bsf/test.esp32-c3-ard.yaml | 11 +-- .../ttp229_bsf/test.esp32-c3-idf.yaml | 11 +-- .../components/ttp229_bsf/test.esp32-idf.yaml | 11 +-- .../ttp229_bsf/test.esp8266-ard.yaml | 11 +-- .../ttp229_bsf/test.rp2040-ard.yaml | 11 +-- tests/components/ttp229_lsf/common.yaml | 11 +++ .../components/ttp229_lsf/test.esp32-ard.yaml | 14 +--- .../ttp229_lsf/test.esp32-c3-ard.yaml | 14 +--- .../ttp229_lsf/test.esp32-c3-idf.yaml | 14 +--- .../components/ttp229_lsf/test.esp32-idf.yaml | 14 +--- .../ttp229_lsf/test.esp8266-ard.yaml | 14 +--- .../ttp229_lsf/test.rp2040-ard.yaml | 14 +--- tests/components/tuya/common.yaml | 78 ++++++++++++++++++ tests/components/tuya/test.esp32-ard.yaml | 82 ++----------------- tests/components/tuya/test.esp32-c3-ard.yaml | 82 ++----------------- tests/components/tuya/test.esp32-c3-idf.yaml | 82 ++----------------- tests/components/tuya/test.esp32-idf.yaml | 82 ++----------------- tests/components/tuya/test.esp8266-ard.yaml | 82 ++----------------- tests/components/tuya/test.rp2040-ard.yaml | 82 ++----------------- 160 files changed, 1045 insertions(+), 2640 deletions(-) create mode 100644 tests/components/t6615/common.yaml create mode 100644 tests/components/tc74/common.yaml create mode 100644 tests/components/tca9548a/common.yaml create mode 100644 tests/components/tca9555/common.yaml create mode 100644 tests/components/tcl112/common.yaml create mode 100644 tests/components/tcs34725/common.yaml create mode 100644 tests/components/tee501/common.yaml create mode 100644 tests/components/teleinfo/common.yaml create mode 100644 tests/components/tlc59208f/common.yaml create mode 100644 tests/components/tm1621/common.yaml create mode 100644 tests/components/tm1637/common.yaml create mode 100644 tests/components/tmp102/common.yaml create mode 100644 tests/components/tmp1075/common.yaml create mode 100644 tests/components/tmp117/common.yaml create mode 100644 tests/components/tof10120/common.yaml create mode 100644 tests/components/toshiba/common.yaml create mode 100644 tests/components/total_daily_energy/common.yaml create mode 100644 tests/components/tsl2561/common.yaml create mode 100644 tests/components/tsl2591/common.yaml create mode 100644 tests/components/tt21100/common.yaml delete mode 100644 tests/components/tt21100/test.esp32-s2-ard.yaml create mode 100644 tests/components/ttp229_bsf/common.yaml create mode 100644 tests/components/ttp229_lsf/common.yaml create mode 100644 tests/components/tuya/common.yaml diff --git a/tests/components/t6615/common.yaml b/tests/components/t6615/common.yaml new file mode 100644 index 0000000000..3ad715ae2b --- /dev/null +++ b/tests/components/t6615/common.yaml @@ -0,0 +1,10 @@ +uart: + - id: uart_t6615 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 19200 + +sensor: + - platform: t6615 + co2: + name: CO2 Sensor diff --git a/tests/components/t6615/test.esp32-ard.yaml b/tests/components/t6615/test.esp32-ard.yaml index 2cfaa0ae5b..f486544afa 100644 --- a/tests/components/t6615/test.esp32-ard.yaml +++ b/tests/components/t6615/test.esp32-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_t6615 - tx_pin: 17 - rx_pin: 16 - baud_rate: 19200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: t6615 - co2: - name: CO2 Sensor +<<: !include common.yaml diff --git a/tests/components/t6615/test.esp32-c3-ard.yaml b/tests/components/t6615/test.esp32-c3-ard.yaml index e8690c770f..b516342f3b 100644 --- a/tests/components/t6615/test.esp32-c3-ard.yaml +++ b/tests/components/t6615/test.esp32-c3-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_t6615 - tx_pin: 4 - rx_pin: 5 - baud_rate: 19200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: t6615 - co2: - name: CO2 Sensor +<<: !include common.yaml diff --git a/tests/components/t6615/test.esp32-c3-idf.yaml b/tests/components/t6615/test.esp32-c3-idf.yaml index e8690c770f..b516342f3b 100644 --- a/tests/components/t6615/test.esp32-c3-idf.yaml +++ b/tests/components/t6615/test.esp32-c3-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_t6615 - tx_pin: 4 - rx_pin: 5 - baud_rate: 19200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: t6615 - co2: - name: CO2 Sensor +<<: !include common.yaml diff --git a/tests/components/t6615/test.esp32-idf.yaml b/tests/components/t6615/test.esp32-idf.yaml index 2cfaa0ae5b..f486544afa 100644 --- a/tests/components/t6615/test.esp32-idf.yaml +++ b/tests/components/t6615/test.esp32-idf.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_t6615 - tx_pin: 17 - rx_pin: 16 - baud_rate: 19200 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -sensor: - - platform: t6615 - co2: - name: CO2 Sensor +<<: !include common.yaml diff --git a/tests/components/t6615/test.esp8266-ard.yaml b/tests/components/t6615/test.esp8266-ard.yaml index e8690c770f..b516342f3b 100644 --- a/tests/components/t6615/test.esp8266-ard.yaml +++ b/tests/components/t6615/test.esp8266-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_t6615 - tx_pin: 4 - rx_pin: 5 - baud_rate: 19200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: t6615 - co2: - name: CO2 Sensor +<<: !include common.yaml diff --git a/tests/components/t6615/test.rp2040-ard.yaml b/tests/components/t6615/test.rp2040-ard.yaml index e8690c770f..b516342f3b 100644 --- a/tests/components/t6615/test.rp2040-ard.yaml +++ b/tests/components/t6615/test.rp2040-ard.yaml @@ -1,10 +1,5 @@ -uart: - - id: uart_t6615 - tx_pin: 4 - rx_pin: 5 - baud_rate: 19200 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -sensor: - - platform: t6615 - co2: - name: CO2 Sensor +<<: !include common.yaml diff --git a/tests/components/tc74/common.yaml b/tests/components/tc74/common.yaml new file mode 100644 index 0000000000..88f5c91e12 --- /dev/null +++ b/tests/components/tc74/common.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tc74 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tc74 + name: TC74 Temperature diff --git a/tests/components/tc74/test.esp32-ard.yaml b/tests/components/tc74/test.esp32-ard.yaml index ef9b40e184..63c3bd6afd 100644 --- a/tests/components/tc74/test.esp32-ard.yaml +++ b/tests/components/tc74/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tc74 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tc74 - name: TC74 Temperature +<<: !include common.yaml diff --git a/tests/components/tc74/test.esp32-c3-ard.yaml b/tests/components/tc74/test.esp32-c3-ard.yaml index e1a373fbf4..ee2c29ca4e 100644 --- a/tests/components/tc74/test.esp32-c3-ard.yaml +++ b/tests/components/tc74/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tc74 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tc74 - name: TC74 Temperature +<<: !include common.yaml diff --git a/tests/components/tc74/test.esp32-c3-idf.yaml b/tests/components/tc74/test.esp32-c3-idf.yaml index e1a373fbf4..ee2c29ca4e 100644 --- a/tests/components/tc74/test.esp32-c3-idf.yaml +++ b/tests/components/tc74/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tc74 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tc74 - name: TC74 Temperature +<<: !include common.yaml diff --git a/tests/components/tc74/test.esp32-idf.yaml b/tests/components/tc74/test.esp32-idf.yaml index ef9b40e184..63c3bd6afd 100644 --- a/tests/components/tc74/test.esp32-idf.yaml +++ b/tests/components/tc74/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tc74 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tc74 - name: TC74 Temperature +<<: !include common.yaml diff --git a/tests/components/tc74/test.esp8266-ard.yaml b/tests/components/tc74/test.esp8266-ard.yaml index e1a373fbf4..ee2c29ca4e 100644 --- a/tests/components/tc74/test.esp8266-ard.yaml +++ b/tests/components/tc74/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tc74 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tc74 - name: TC74 Temperature +<<: !include common.yaml diff --git a/tests/components/tc74/test.rp2040-ard.yaml b/tests/components/tc74/test.rp2040-ard.yaml index e1a373fbf4..ee2c29ca4e 100644 --- a/tests/components/tc74/test.rp2040-ard.yaml +++ b/tests/components/tc74/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tc74 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tc74 - name: TC74 Temperature +<<: !include common.yaml diff --git a/tests/components/tca9548a/common.yaml b/tests/components/tca9548a/common.yaml new file mode 100644 index 0000000000..67e812e08b --- /dev/null +++ b/tests/components/tca9548a/common.yaml @@ -0,0 +1,15 @@ +i2c: + - id: i2c_tca9548a + scl: ${scl_pin} + sda: ${sda_pin} + +tca9548a: + - id: multiplex0 + address: 0x70 + channels: + - bus_id: multiplex0_chan0 + channel: 0 + i2c_id: i2c_tca9548a + - id: multiplex1 + address: 0x71 + i2c_id: multiplex0_chan0 diff --git a/tests/components/tca9548a/test.esp32-ard.yaml b/tests/components/tca9548a/test.esp32-ard.yaml index 7edb83c821..63c3bd6afd 100644 --- a/tests/components/tca9548a/test.esp32-ard.yaml +++ b/tests/components/tca9548a/test.esp32-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_tca9548a - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -tca9548a: - - id: multiplex0 - address: 0x70 - channels: - - bus_id: multiplex0_chan0 - channel: 0 - i2c_id: i2c_tca9548a - - id: multiplex1 - address: 0x71 - i2c_id: multiplex0_chan0 +<<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp32-c3-ard.yaml b/tests/components/tca9548a/test.esp32-c3-ard.yaml index 2294530d14..ee2c29ca4e 100644 --- a/tests/components/tca9548a/test.esp32-c3-ard.yaml +++ b/tests/components/tca9548a/test.esp32-c3-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_tca9548a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9548a: - - id: multiplex0 - address: 0x70 - channels: - - bus_id: multiplex0_chan0 - channel: 0 - i2c_id: i2c_tca9548a - - id: multiplex1 - address: 0x71 - i2c_id: multiplex0_chan0 +<<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp32-c3-idf.yaml b/tests/components/tca9548a/test.esp32-c3-idf.yaml index 2294530d14..ee2c29ca4e 100644 --- a/tests/components/tca9548a/test.esp32-c3-idf.yaml +++ b/tests/components/tca9548a/test.esp32-c3-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_tca9548a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9548a: - - id: multiplex0 - address: 0x70 - channels: - - bus_id: multiplex0_chan0 - channel: 0 - i2c_id: i2c_tca9548a - - id: multiplex1 - address: 0x71 - i2c_id: multiplex0_chan0 +<<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp32-idf.yaml b/tests/components/tca9548a/test.esp32-idf.yaml index 7edb83c821..63c3bd6afd 100644 --- a/tests/components/tca9548a/test.esp32-idf.yaml +++ b/tests/components/tca9548a/test.esp32-idf.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_tca9548a - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -tca9548a: - - id: multiplex0 - address: 0x70 - channels: - - bus_id: multiplex0_chan0 - channel: 0 - i2c_id: i2c_tca9548a - - id: multiplex1 - address: 0x71 - i2c_id: multiplex0_chan0 +<<: !include common.yaml diff --git a/tests/components/tca9548a/test.esp8266-ard.yaml b/tests/components/tca9548a/test.esp8266-ard.yaml index 2294530d14..ee2c29ca4e 100644 --- a/tests/components/tca9548a/test.esp8266-ard.yaml +++ b/tests/components/tca9548a/test.esp8266-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_tca9548a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9548a: - - id: multiplex0 - address: 0x70 - channels: - - bus_id: multiplex0_chan0 - channel: 0 - i2c_id: i2c_tca9548a - - id: multiplex1 - address: 0x71 - i2c_id: multiplex0_chan0 +<<: !include common.yaml diff --git a/tests/components/tca9548a/test.rp2040-ard.yaml b/tests/components/tca9548a/test.rp2040-ard.yaml index 2294530d14..ee2c29ca4e 100644 --- a/tests/components/tca9548a/test.rp2040-ard.yaml +++ b/tests/components/tca9548a/test.rp2040-ard.yaml @@ -1,15 +1,5 @@ -i2c: - - id: i2c_tca9548a - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9548a: - - id: multiplex0 - address: 0x70 - channels: - - bus_id: multiplex0_chan0 - channel: 0 - i2c_id: i2c_tca9548a - - id: multiplex1 - address: 0x71 - i2c_id: multiplex0_chan0 +<<: !include common.yaml diff --git a/tests/components/tca9555/common.yaml b/tests/components/tca9555/common.yaml new file mode 100644 index 0000000000..0fc3086786 --- /dev/null +++ b/tests/components/tca9555/common.yaml @@ -0,0 +1,27 @@ +i2c: + - id: i2c_tca9555 + scl: ${scl_pin} + sda: ${sda_pin} + +tca9555: + - id: tca9555_hub + address: 0x21 + +binary_sensor: + - platform: gpio + id: tca9555_binary_sensor + name: TCA9555 Binary Sensor + pin: + tca9555: tca9555_hub + number: 1 + mode: INPUT + inverted: true + +output: + - platform: gpio + id: tca9555_output + pin: + tca9555: tca9555_hub + number: 0 + mode: OUTPUT + inverted: false diff --git a/tests/components/tca9555/test.esp32-ard.yaml b/tests/components/tca9555/test.esp32-ard.yaml index e0c046b443..63c3bd6afd 100644 --- a/tests/components/tca9555/test.esp32-ard.yaml +++ b/tests/components/tca9555/test.esp32-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_tca9555 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -tca9555: - - id: tca9555_hub - address: 0x21 - -binary_sensor: - - platform: gpio - id: tca9555_binary_sensor - name: TCA9555 Binary Sensor - pin: - tca9555: tca9555_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: tca9555_output - pin: - tca9555: tca9555_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/tca9555/test.esp32-c3-ard.yaml b/tests/components/tca9555/test.esp32-c3-ard.yaml index 5c49b27640..ee2c29ca4e 100644 --- a/tests/components/tca9555/test.esp32-c3-ard.yaml +++ b/tests/components/tca9555/test.esp32-c3-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_tca9555 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9555: - - id: tca9555_hub - address: 0x21 - -binary_sensor: - - platform: gpio - id: tca9555_binary_sensor - name: TCA9555 Binary Sensor - pin: - tca9555: tca9555_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: tca9555_output - pin: - tca9555: tca9555_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/tca9555/test.esp32-c3-idf.yaml b/tests/components/tca9555/test.esp32-c3-idf.yaml index 5c49b27640..ee2c29ca4e 100644 --- a/tests/components/tca9555/test.esp32-c3-idf.yaml +++ b/tests/components/tca9555/test.esp32-c3-idf.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_tca9555 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9555: - - id: tca9555_hub - address: 0x21 - -binary_sensor: - - platform: gpio - id: tca9555_binary_sensor - name: TCA9555 Binary Sensor - pin: - tca9555: tca9555_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: tca9555_output - pin: - tca9555: tca9555_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/tca9555/test.esp32-idf.yaml b/tests/components/tca9555/test.esp32-idf.yaml index e0c046b443..63c3bd6afd 100644 --- a/tests/components/tca9555/test.esp32-idf.yaml +++ b/tests/components/tca9555/test.esp32-idf.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_tca9555 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -tca9555: - - id: tca9555_hub - address: 0x21 - -binary_sensor: - - platform: gpio - id: tca9555_binary_sensor - name: TCA9555 Binary Sensor - pin: - tca9555: tca9555_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: tca9555_output - pin: - tca9555: tca9555_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/tca9555/test.esp8266-ard.yaml b/tests/components/tca9555/test.esp8266-ard.yaml index 5c49b27640..ee2c29ca4e 100644 --- a/tests/components/tca9555/test.esp8266-ard.yaml +++ b/tests/components/tca9555/test.esp8266-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_tca9555 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9555: - - id: tca9555_hub - address: 0x21 - -binary_sensor: - - platform: gpio - id: tca9555_binary_sensor - name: TCA9555 Binary Sensor - pin: - tca9555: tca9555_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: tca9555_output - pin: - tca9555: tca9555_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/tca9555/test.rp2040-ard.yaml b/tests/components/tca9555/test.rp2040-ard.yaml index 5c49b27640..ee2c29ca4e 100644 --- a/tests/components/tca9555/test.rp2040-ard.yaml +++ b/tests/components/tca9555/test.rp2040-ard.yaml @@ -1,27 +1,5 @@ -i2c: - - id: i2c_tca9555 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tca9555: - - id: tca9555_hub - address: 0x21 - -binary_sensor: - - platform: gpio - id: tca9555_binary_sensor - name: TCA9555 Binary Sensor - pin: - tca9555: tca9555_hub - number: 1 - mode: INPUT - inverted: true - -output: - - platform: gpio - id: tca9555_output - pin: - tca9555: tca9555_hub - number: 0 - mode: OUTPUT - inverted: false +<<: !include common.yaml diff --git a/tests/components/tcl112/common.yaml b/tests/components/tcl112/common.yaml new file mode 100644 index 0000000000..0e43de4a4a --- /dev/null +++ b/tests/components/tcl112/common.yaml @@ -0,0 +1,15 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +sensor: + - platform: template + id: tcl112_sensor + lambda: "return 21;" + +climate: + - platform: tcl112 + name: TCL112 Climate with Sensor + supports_heat: true + supports_cool: true + sensor: tcl112_sensor diff --git a/tests/components/tcl112/test.esp32-ard.yaml b/tests/components/tcl112/test.esp32-ard.yaml index 03c0e84fe5..7b012aa64c 100644 --- a/tests/components/tcl112/test.esp32-ard.yaml +++ b/tests/components/tcl112/test.esp32-ard.yaml @@ -1,15 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -sensor: - - platform: template - id: tcl112_sensor - lambda: "return 21;" - -climate: - - platform: tcl112 - name: TCL112 Climate with Sensor - supports_heat: true - supports_cool: true - sensor: tcl112_sensor +<<: !include common.yaml diff --git a/tests/components/tcl112/test.esp32-c3-ard.yaml b/tests/components/tcl112/test.esp32-c3-ard.yaml index 03c0e84fe5..7b012aa64c 100644 --- a/tests/components/tcl112/test.esp32-c3-ard.yaml +++ b/tests/components/tcl112/test.esp32-c3-ard.yaml @@ -1,15 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -sensor: - - platform: template - id: tcl112_sensor - lambda: "return 21;" - -climate: - - platform: tcl112 - name: TCL112 Climate with Sensor - supports_heat: true - supports_cool: true - sensor: tcl112_sensor +<<: !include common.yaml diff --git a/tests/components/tcl112/test.esp32-c3-idf.yaml b/tests/components/tcl112/test.esp32-c3-idf.yaml index 03c0e84fe5..7b012aa64c 100644 --- a/tests/components/tcl112/test.esp32-c3-idf.yaml +++ b/tests/components/tcl112/test.esp32-c3-idf.yaml @@ -1,15 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -sensor: - - platform: template - id: tcl112_sensor - lambda: "return 21;" - -climate: - - platform: tcl112 - name: TCL112 Climate with Sensor - supports_heat: true - supports_cool: true - sensor: tcl112_sensor +<<: !include common.yaml diff --git a/tests/components/tcl112/test.esp32-idf.yaml b/tests/components/tcl112/test.esp32-idf.yaml index 03c0e84fe5..7b012aa64c 100644 --- a/tests/components/tcl112/test.esp32-idf.yaml +++ b/tests/components/tcl112/test.esp32-idf.yaml @@ -1,15 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -sensor: - - platform: template - id: tcl112_sensor - lambda: "return 21;" - -climate: - - platform: tcl112 - name: TCL112 Climate with Sensor - supports_heat: true - supports_cool: true - sensor: tcl112_sensor +<<: !include common.yaml diff --git a/tests/components/tcl112/test.esp8266-ard.yaml b/tests/components/tcl112/test.esp8266-ard.yaml index 0a85536928..f5097fcf5f 100644 --- a/tests/components/tcl112/test.esp8266-ard.yaml +++ b/tests/components/tcl112/test.esp8266-ard.yaml @@ -1,15 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -sensor: - - platform: template - id: tcl112_sensor - lambda: "return 21;" - -climate: - - platform: tcl112 - name: TCL112 Climate with Sensor - supports_heat: true - supports_cool: true - sensor: tcl112_sensor +<<: !include common.yaml diff --git a/tests/components/tcs34725/common.yaml b/tests/components/tcs34725/common.yaml new file mode 100644 index 0000000000..5296988fa5 --- /dev/null +++ b/tests/components/tcs34725/common.yaml @@ -0,0 +1,21 @@ +i2c: + - id: i2c_tcs34725 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tcs34725 + red_channel: + name: Red Channel + green_channel: + name: Green Channel + blue_channel: + name: Blue Channel + clear_channel: + name: Clear Channel + illuminance: + name: Illuminance + color_temperature: + name: Color Temperature + integration_time: 614ms + gain: 60x diff --git a/tests/components/tcs34725/test.esp32-ard.yaml b/tests/components/tcs34725/test.esp32-ard.yaml index 86ef82962e..63c3bd6afd 100644 --- a/tests/components/tcs34725/test.esp32-ard.yaml +++ b/tests/components/tcs34725/test.esp32-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_tcs34725 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tcs34725 - red_channel: - name: Red Channel - green_channel: - name: Green Channel - blue_channel: - name: Blue Channel - clear_channel: - name: Clear Channel - illuminance: - name: Illuminance - color_temperature: - name: Color Temperature - integration_time: 614ms - gain: 60x +<<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp32-c3-ard.yaml b/tests/components/tcs34725/test.esp32-c3-ard.yaml index 9b459c9104..ee2c29ca4e 100644 --- a/tests/components/tcs34725/test.esp32-c3-ard.yaml +++ b/tests/components/tcs34725/test.esp32-c3-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_tcs34725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tcs34725 - red_channel: - name: Red Channel - green_channel: - name: Green Channel - blue_channel: - name: Blue Channel - clear_channel: - name: Clear Channel - illuminance: - name: Illuminance - color_temperature: - name: Color Temperature - integration_time: 614ms - gain: 60x +<<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp32-c3-idf.yaml b/tests/components/tcs34725/test.esp32-c3-idf.yaml index 9b459c9104..ee2c29ca4e 100644 --- a/tests/components/tcs34725/test.esp32-c3-idf.yaml +++ b/tests/components/tcs34725/test.esp32-c3-idf.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_tcs34725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tcs34725 - red_channel: - name: Red Channel - green_channel: - name: Green Channel - blue_channel: - name: Blue Channel - clear_channel: - name: Clear Channel - illuminance: - name: Illuminance - color_temperature: - name: Color Temperature - integration_time: 614ms - gain: 60x +<<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp32-idf.yaml b/tests/components/tcs34725/test.esp32-idf.yaml index 86ef82962e..63c3bd6afd 100644 --- a/tests/components/tcs34725/test.esp32-idf.yaml +++ b/tests/components/tcs34725/test.esp32-idf.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_tcs34725 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tcs34725 - red_channel: - name: Red Channel - green_channel: - name: Green Channel - blue_channel: - name: Blue Channel - clear_channel: - name: Clear Channel - illuminance: - name: Illuminance - color_temperature: - name: Color Temperature - integration_time: 614ms - gain: 60x +<<: !include common.yaml diff --git a/tests/components/tcs34725/test.esp8266-ard.yaml b/tests/components/tcs34725/test.esp8266-ard.yaml index 9b459c9104..ee2c29ca4e 100644 --- a/tests/components/tcs34725/test.esp8266-ard.yaml +++ b/tests/components/tcs34725/test.esp8266-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_tcs34725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tcs34725 - red_channel: - name: Red Channel - green_channel: - name: Green Channel - blue_channel: - name: Blue Channel - clear_channel: - name: Clear Channel - illuminance: - name: Illuminance - color_temperature: - name: Color Temperature - integration_time: 614ms - gain: 60x +<<: !include common.yaml diff --git a/tests/components/tcs34725/test.rp2040-ard.yaml b/tests/components/tcs34725/test.rp2040-ard.yaml index 9b459c9104..ee2c29ca4e 100644 --- a/tests/components/tcs34725/test.rp2040-ard.yaml +++ b/tests/components/tcs34725/test.rp2040-ard.yaml @@ -1,21 +1,5 @@ -i2c: - - id: i2c_tcs34725 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tcs34725 - red_channel: - name: Red Channel - green_channel: - name: Green Channel - blue_channel: - name: Blue Channel - clear_channel: - name: Clear Channel - illuminance: - name: Illuminance - color_temperature: - name: Color Temperature - integration_time: 614ms - gain: 60x +<<: !include common.yaml diff --git a/tests/components/tee501/common.yaml b/tests/components/tee501/common.yaml new file mode 100644 index 0000000000..c01ab7e37a --- /dev/null +++ b/tests/components/tee501/common.yaml @@ -0,0 +1,9 @@ +i2c: + - id: i2c_tee501 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tee501 + name: TEE501 Temperature + address: 0x48 diff --git a/tests/components/tee501/test.esp32-ard.yaml b/tests/components/tee501/test.esp32-ard.yaml index acf6fed4bf..63c3bd6afd 100644 --- a/tests/components/tee501/test.esp32-ard.yaml +++ b/tests/components/tee501/test.esp32-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tee501 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tee501 - name: TEE501 Temperature - address: 0x48 +<<: !include common.yaml diff --git a/tests/components/tee501/test.esp32-c3-ard.yaml b/tests/components/tee501/test.esp32-c3-ard.yaml index 11991a6153..ee2c29ca4e 100644 --- a/tests/components/tee501/test.esp32-c3-ard.yaml +++ b/tests/components/tee501/test.esp32-c3-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tee501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tee501 - name: TEE501 Temperature - address: 0x48 +<<: !include common.yaml diff --git a/tests/components/tee501/test.esp32-c3-idf.yaml b/tests/components/tee501/test.esp32-c3-idf.yaml index 11991a6153..ee2c29ca4e 100644 --- a/tests/components/tee501/test.esp32-c3-idf.yaml +++ b/tests/components/tee501/test.esp32-c3-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tee501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tee501 - name: TEE501 Temperature - address: 0x48 +<<: !include common.yaml diff --git a/tests/components/tee501/test.esp32-idf.yaml b/tests/components/tee501/test.esp32-idf.yaml index acf6fed4bf..63c3bd6afd 100644 --- a/tests/components/tee501/test.esp32-idf.yaml +++ b/tests/components/tee501/test.esp32-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tee501 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tee501 - name: TEE501 Temperature - address: 0x48 +<<: !include common.yaml diff --git a/tests/components/tee501/test.esp8266-ard.yaml b/tests/components/tee501/test.esp8266-ard.yaml index 11991a6153..ee2c29ca4e 100644 --- a/tests/components/tee501/test.esp8266-ard.yaml +++ b/tests/components/tee501/test.esp8266-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tee501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tee501 - name: TEE501 Temperature - address: 0x48 +<<: !include common.yaml diff --git a/tests/components/tee501/test.rp2040-ard.yaml b/tests/components/tee501/test.rp2040-ard.yaml index 11991a6153..ee2c29ca4e 100644 --- a/tests/components/tee501/test.rp2040-ard.yaml +++ b/tests/components/tee501/test.rp2040-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tee501 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tee501 - name: TEE501 Temperature - address: 0x48 +<<: !include common.yaml diff --git a/tests/components/teleinfo/common.yaml b/tests/components/teleinfo/common.yaml new file mode 100644 index 0000000000..90b684e977 --- /dev/null +++ b/tests/components/teleinfo/common.yaml @@ -0,0 +1,42 @@ +uart: + - id: uart_teleinfo + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 1200 + parity: EVEN + +button: + - platform: template + name: Poller component suspend test + on_press: + - component.suspend: test_teleinfo + - delay: 20s + - component.update: test_teleinfo + - delay: 20s + - component.resume: test_teleinfo + - delay: 20s + - component.resume: + id: test_teleinfo + update_interval: 2s + - delay: 20s + - component.resume: + id: test_teleinfo + update_interval: !lambda return 2500; + +teleinfo: + id: test_teleinfo + historical_mode: true + update_interval: 60s + +sensor: + - platform: teleinfo + name: hchc + tag_name: HCHC + teleinfo_id: test_teleinfo + unit_of_measurement: Wh + +text_sensor: + - platform: teleinfo + name: optarif + tag_name: OPTARIF + teleinfo_id: test_teleinfo diff --git a/tests/components/teleinfo/test.esp32-ard.yaml b/tests/components/teleinfo/test.esp32-ard.yaml index a5bd176143..f486544afa 100644 --- a/tests/components/teleinfo/test.esp32-ard.yaml +++ b/tests/components/teleinfo/test.esp32-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_teleinfo - tx_pin: 17 - rx_pin: 16 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -button: - - platform: template - name: Poller component suspend test - on_press: - - component.suspend: test_teleinfo - - delay: 20s - - component.update: test_teleinfo - - delay: 20s - - component.resume: test_teleinfo - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: 2s - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: !lambda return 2500; - -teleinfo: - id: test_teleinfo - historical_mode: true - update_interval: 60s - -sensor: - - platform: teleinfo - name: hchc - tag_name: HCHC - teleinfo_id: test_teleinfo - unit_of_measurement: Wh - -text_sensor: - - platform: teleinfo - name: optarif - tag_name: OPTARIF - teleinfo_id: test_teleinfo +<<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp32-c3-ard.yaml b/tests/components/teleinfo/test.esp32-c3-ard.yaml index 55641e1e01..b516342f3b 100644 --- a/tests/components/teleinfo/test.esp32-c3-ard.yaml +++ b/tests/components/teleinfo/test.esp32-c3-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_teleinfo - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -button: - - platform: template - name: Poller component suspend test - on_press: - - component.suspend: test_teleinfo - - delay: 20s - - component.update: test_teleinfo - - delay: 20s - - component.resume: test_teleinfo - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: 2s - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: !lambda return 2500; - -teleinfo: - id: test_teleinfo - historical_mode: true - update_interval: 60s - -sensor: - - platform: teleinfo - name: hchc - tag_name: HCHC - teleinfo_id: test_teleinfo - unit_of_measurement: Wh - -text_sensor: - - platform: teleinfo - name: optarif - tag_name: OPTARIF - teleinfo_id: test_teleinfo +<<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp32-c3-idf.yaml b/tests/components/teleinfo/test.esp32-c3-idf.yaml index 55641e1e01..b516342f3b 100644 --- a/tests/components/teleinfo/test.esp32-c3-idf.yaml +++ b/tests/components/teleinfo/test.esp32-c3-idf.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_teleinfo - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -button: - - platform: template - name: Poller component suspend test - on_press: - - component.suspend: test_teleinfo - - delay: 20s - - component.update: test_teleinfo - - delay: 20s - - component.resume: test_teleinfo - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: 2s - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: !lambda return 2500; - -teleinfo: - id: test_teleinfo - historical_mode: true - update_interval: 60s - -sensor: - - platform: teleinfo - name: hchc - tag_name: HCHC - teleinfo_id: test_teleinfo - unit_of_measurement: Wh - -text_sensor: - - platform: teleinfo - name: optarif - tag_name: OPTARIF - teleinfo_id: test_teleinfo +<<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp32-idf.yaml b/tests/components/teleinfo/test.esp32-idf.yaml index a5bd176143..f486544afa 100644 --- a/tests/components/teleinfo/test.esp32-idf.yaml +++ b/tests/components/teleinfo/test.esp32-idf.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_teleinfo - tx_pin: 17 - rx_pin: 16 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 -button: - - platform: template - name: Poller component suspend test - on_press: - - component.suspend: test_teleinfo - - delay: 20s - - component.update: test_teleinfo - - delay: 20s - - component.resume: test_teleinfo - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: 2s - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: !lambda return 2500; - -teleinfo: - id: test_teleinfo - historical_mode: true - update_interval: 60s - -sensor: - - platform: teleinfo - name: hchc - tag_name: HCHC - teleinfo_id: test_teleinfo - unit_of_measurement: Wh - -text_sensor: - - platform: teleinfo - name: optarif - tag_name: OPTARIF - teleinfo_id: test_teleinfo +<<: !include common.yaml diff --git a/tests/components/teleinfo/test.esp8266-ard.yaml b/tests/components/teleinfo/test.esp8266-ard.yaml index 55641e1e01..b516342f3b 100644 --- a/tests/components/teleinfo/test.esp8266-ard.yaml +++ b/tests/components/teleinfo/test.esp8266-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_teleinfo - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -button: - - platform: template - name: Poller component suspend test - on_press: - - component.suspend: test_teleinfo - - delay: 20s - - component.update: test_teleinfo - - delay: 20s - - component.resume: test_teleinfo - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: 2s - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: !lambda return 2500; - -teleinfo: - id: test_teleinfo - historical_mode: true - update_interval: 60s - -sensor: - - platform: teleinfo - name: hchc - tag_name: HCHC - teleinfo_id: test_teleinfo - unit_of_measurement: Wh - -text_sensor: - - platform: teleinfo - name: optarif - tag_name: OPTARIF - teleinfo_id: test_teleinfo +<<: !include common.yaml diff --git a/tests/components/teleinfo/test.rp2040-ard.yaml b/tests/components/teleinfo/test.rp2040-ard.yaml index 55641e1e01..b516342f3b 100644 --- a/tests/components/teleinfo/test.rp2040-ard.yaml +++ b/tests/components/teleinfo/test.rp2040-ard.yaml @@ -1,42 +1,5 @@ -uart: - - id: uart_teleinfo - tx_pin: 4 - rx_pin: 5 - baud_rate: 1200 - parity: EVEN +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 -button: - - platform: template - name: Poller component suspend test - on_press: - - component.suspend: test_teleinfo - - delay: 20s - - component.update: test_teleinfo - - delay: 20s - - component.resume: test_teleinfo - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: 2s - - delay: 20s - - component.resume: - id: test_teleinfo - update_interval: !lambda return 2500; - -teleinfo: - id: test_teleinfo - historical_mode: true - update_interval: 60s - -sensor: - - platform: teleinfo - name: hchc - tag_name: HCHC - teleinfo_id: test_teleinfo - unit_of_measurement: Wh - -text_sensor: - - platform: teleinfo - name: optarif - tag_name: OPTARIF - teleinfo_id: test_teleinfo +<<: !include common.yaml diff --git a/tests/components/tlc59208f/common.yaml b/tests/components/tlc59208f/common.yaml new file mode 100644 index 0000000000..49460dcefc --- /dev/null +++ b/tests/components/tlc59208f/common.yaml @@ -0,0 +1,50 @@ +i2c: + - id: i2c_tlc59208f + scl: ${scl_pin} + sda: ${sda_pin} + +tlc59208f: + - address: 0x20 + id: tlc59208f_1 + - address: 0x22 + id: tlc59208f_2 + - address: 0x24 + id: tlc59208f_3 + +output: + - platform: tlc59208f + id: tlc_0 + channel: 0 + tlc59208f_id: tlc59208f_1 + - platform: tlc59208f + id: tlc_1 + channel: 1 + tlc59208f_id: tlc59208f_1 + - platform: tlc59208f + id: tlc_2 + channel: 2 + tlc59208f_id: tlc59208f_1 + - platform: tlc59208f + id: tlc_3 + channel: 0 + tlc59208f_id: tlc59208f_2 + - platform: tlc59208f + id: tlc_4 + channel: 1 + tlc59208f_id: tlc59208f_2 + - platform: tlc59208f + id: tlc_5 + channel: 2 + tlc59208f_id: tlc59208f_2 + - platform: tlc59208f + id: tlc_6 + channel: 0 + tlc59208f_id: tlc59208f_3 + - platform: tlc59208f + id: tlc_7 + channel: 1 + tlc59208f_id: tlc59208f_3 + - platform: tlc59208f + id: tlc_8 + channel: 2 + tlc59208f_id: tlc59208f_3 diff --git a/tests/components/tlc59208f/test.esp32-ard.yaml b/tests/components/tlc59208f/test.esp32-ard.yaml index 2639de3b3d..63c3bd6afd 100644 --- a/tests/components/tlc59208f/test.esp32-ard.yaml +++ b/tests/components/tlc59208f/test.esp32-ard.yaml @@ -1,50 +1,5 @@ -i2c: - - id: i2c_tlc59208f - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -tlc59208f: - - address: 0x20 - id: tlc59208f_1 - - address: 0x22 - id: tlc59208f_2 - - address: 0x24 - id: tlc59208f_3 - -output: - - platform: tlc59208f - id: tlc_0 - channel: 0 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_1 - channel: 1 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_2 - channel: 2 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_3 - channel: 0 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_4 - channel: 1 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_5 - channel: 2 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_6 - channel: 0 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_7 - channel: 1 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_8 - channel: 2 - tlc59208f_id: tlc59208f_3 +<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp32-c3-ard.yaml b/tests/components/tlc59208f/test.esp32-c3-ard.yaml index 923ea4b4a4..ee2c29ca4e 100644 --- a/tests/components/tlc59208f/test.esp32-c3-ard.yaml +++ b/tests/components/tlc59208f/test.esp32-c3-ard.yaml @@ -1,50 +1,5 @@ -i2c: - - id: i2c_tlc59208f - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tlc59208f: - - address: 0x20 - id: tlc59208f_1 - - address: 0x22 - id: tlc59208f_2 - - address: 0x24 - id: tlc59208f_3 - -output: - - platform: tlc59208f - id: tlc_0 - channel: 0 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_1 - channel: 1 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_2 - channel: 2 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_3 - channel: 0 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_4 - channel: 1 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_5 - channel: 2 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_6 - channel: 0 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_7 - channel: 1 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_8 - channel: 2 - tlc59208f_id: tlc59208f_3 +<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp32-c3-idf.yaml b/tests/components/tlc59208f/test.esp32-c3-idf.yaml index 923ea4b4a4..ee2c29ca4e 100644 --- a/tests/components/tlc59208f/test.esp32-c3-idf.yaml +++ b/tests/components/tlc59208f/test.esp32-c3-idf.yaml @@ -1,50 +1,5 @@ -i2c: - - id: i2c_tlc59208f - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tlc59208f: - - address: 0x20 - id: tlc59208f_1 - - address: 0x22 - id: tlc59208f_2 - - address: 0x24 - id: tlc59208f_3 - -output: - - platform: tlc59208f - id: tlc_0 - channel: 0 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_1 - channel: 1 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_2 - channel: 2 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_3 - channel: 0 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_4 - channel: 1 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_5 - channel: 2 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_6 - channel: 0 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_7 - channel: 1 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_8 - channel: 2 - tlc59208f_id: tlc59208f_3 +<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp32-idf.yaml b/tests/components/tlc59208f/test.esp32-idf.yaml index 2639de3b3d..63c3bd6afd 100644 --- a/tests/components/tlc59208f/test.esp32-idf.yaml +++ b/tests/components/tlc59208f/test.esp32-idf.yaml @@ -1,50 +1,5 @@ -i2c: - - id: i2c_tlc59208f - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -tlc59208f: - - address: 0x20 - id: tlc59208f_1 - - address: 0x22 - id: tlc59208f_2 - - address: 0x24 - id: tlc59208f_3 - -output: - - platform: tlc59208f - id: tlc_0 - channel: 0 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_1 - channel: 1 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_2 - channel: 2 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_3 - channel: 0 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_4 - channel: 1 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_5 - channel: 2 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_6 - channel: 0 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_7 - channel: 1 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_8 - channel: 2 - tlc59208f_id: tlc59208f_3 +<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.esp8266-ard.yaml b/tests/components/tlc59208f/test.esp8266-ard.yaml index 923ea4b4a4..ee2c29ca4e 100644 --- a/tests/components/tlc59208f/test.esp8266-ard.yaml +++ b/tests/components/tlc59208f/test.esp8266-ard.yaml @@ -1,50 +1,5 @@ -i2c: - - id: i2c_tlc59208f - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tlc59208f: - - address: 0x20 - id: tlc59208f_1 - - address: 0x22 - id: tlc59208f_2 - - address: 0x24 - id: tlc59208f_3 - -output: - - platform: tlc59208f - id: tlc_0 - channel: 0 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_1 - channel: 1 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_2 - channel: 2 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_3 - channel: 0 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_4 - channel: 1 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_5 - channel: 2 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_6 - channel: 0 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_7 - channel: 1 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_8 - channel: 2 - tlc59208f_id: tlc59208f_3 +<<: !include common.yaml diff --git a/tests/components/tlc59208f/test.rp2040-ard.yaml b/tests/components/tlc59208f/test.rp2040-ard.yaml index 923ea4b4a4..ee2c29ca4e 100644 --- a/tests/components/tlc59208f/test.rp2040-ard.yaml +++ b/tests/components/tlc59208f/test.rp2040-ard.yaml @@ -1,50 +1,5 @@ -i2c: - - id: i2c_tlc59208f - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -tlc59208f: - - address: 0x20 - id: tlc59208f_1 - - address: 0x22 - id: tlc59208f_2 - - address: 0x24 - id: tlc59208f_3 - -output: - - platform: tlc59208f - id: tlc_0 - channel: 0 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_1 - channel: 1 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_2 - channel: 2 - tlc59208f_id: tlc59208f_1 - - platform: tlc59208f - id: tlc_3 - channel: 0 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_4 - channel: 1 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_5 - channel: 2 - tlc59208f_id: tlc59208f_2 - - platform: tlc59208f - id: tlc_6 - channel: 0 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_7 - channel: 1 - tlc59208f_id: tlc59208f_3 - - platform: tlc59208f - id: tlc_8 - channel: 2 - tlc59208f_id: tlc59208f_3 +<<: !include common.yaml diff --git a/tests/components/tm1621/common.yaml b/tests/components/tm1621/common.yaml new file mode 100644 index 0000000000..77f7ae74fe --- /dev/null +++ b/tests/components/tm1621/common.yaml @@ -0,0 +1,12 @@ +display: + - platform: tm1621 + id: tm1621_display + cs_pin: ${cs_pin} + data_pin: ${data_pin} + read_pin: ${read_pin} + write_pin: ${write_pin} + lambda: |- + it.printf(0, "%.1f", 20.0); + it.display_celsius(true); + it.printf(1, "%.1f", 20.0); + it.display_humidity(true); diff --git a/tests/components/tm1621/test.esp32-ard.yaml b/tests/components/tm1621/test.esp32-ard.yaml index 8eab46f000..0441e4bffe 100644 --- a/tests/components/tm1621/test.esp32-ard.yaml +++ b/tests/components/tm1621/test.esp32-ard.yaml @@ -1,12 +1,7 @@ -display: - - platform: tm1621 - id: tm1621_display - cs_pin: 15 - data_pin: 14 - read_pin: 12 - write_pin: 13 - lambda: |- - it.printf(0, "%.1f", 20.0); - it.display_celsius(true); - it.printf(1, "%.1f", 20.0); - it.display_humidity(true); +substitutions: + cs_pin: GPIO16 + data_pin: GPIO17 + read_pin: GPIO12 + write_pin: GPIO13 + +<<: !include common.yaml diff --git a/tests/components/tm1621/test.esp32-c3-ard.yaml b/tests/components/tm1621/test.esp32-c3-ard.yaml index cddd64f31f..562ced7485 100644 --- a/tests/components/tm1621/test.esp32-c3-ard.yaml +++ b/tests/components/tm1621/test.esp32-c3-ard.yaml @@ -1,12 +1,7 @@ -display: - - platform: tm1621 - id: tm1621_display - cs_pin: 7 - data_pin: 4 - read_pin: 5 - write_pin: 6 - lambda: |- - it.printf(0, "%.1f", 20.0); - it.display_celsius(true); - it.printf(1, "%.1f", 20.0); - it.display_humidity(true); +substitutions: + cs_pin: GPIO6 + data_pin: GPIO7 + read_pin: GPIO2 + write_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/tm1621/test.esp32-c3-idf.yaml b/tests/components/tm1621/test.esp32-c3-idf.yaml index cddd64f31f..562ced7485 100644 --- a/tests/components/tm1621/test.esp32-c3-idf.yaml +++ b/tests/components/tm1621/test.esp32-c3-idf.yaml @@ -1,12 +1,7 @@ -display: - - platform: tm1621 - id: tm1621_display - cs_pin: 7 - data_pin: 4 - read_pin: 5 - write_pin: 6 - lambda: |- - it.printf(0, "%.1f", 20.0); - it.display_celsius(true); - it.printf(1, "%.1f", 20.0); - it.display_humidity(true); +substitutions: + cs_pin: GPIO6 + data_pin: GPIO7 + read_pin: GPIO2 + write_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/tm1621/test.esp32-idf.yaml b/tests/components/tm1621/test.esp32-idf.yaml index 8eab46f000..0441e4bffe 100644 --- a/tests/components/tm1621/test.esp32-idf.yaml +++ b/tests/components/tm1621/test.esp32-idf.yaml @@ -1,12 +1,7 @@ -display: - - platform: tm1621 - id: tm1621_display - cs_pin: 15 - data_pin: 14 - read_pin: 12 - write_pin: 13 - lambda: |- - it.printf(0, "%.1f", 20.0); - it.display_celsius(true); - it.printf(1, "%.1f", 20.0); - it.display_humidity(true); +substitutions: + cs_pin: GPIO16 + data_pin: GPIO17 + read_pin: GPIO12 + write_pin: GPIO13 + +<<: !include common.yaml diff --git a/tests/components/tm1621/test.esp8266-ard.yaml b/tests/components/tm1621/test.esp8266-ard.yaml index 8eab46f000..ee7b62ce35 100644 --- a/tests/components/tm1621/test.esp8266-ard.yaml +++ b/tests/components/tm1621/test.esp8266-ard.yaml @@ -1,12 +1,7 @@ -display: - - platform: tm1621 - id: tm1621_display - cs_pin: 15 - data_pin: 14 - read_pin: 12 - write_pin: 13 - lambda: |- - it.printf(0, "%.1f", 20.0); - it.display_celsius(true); - it.printf(1, "%.1f", 20.0); - it.display_humidity(true); +substitutions: + cs_pin: GPIO15 + data_pin: GPIO14 + read_pin: GPIO12 + write_pin: GPIO13 + +<<: !include common.yaml diff --git a/tests/components/tm1621/test.rp2040-ard.yaml b/tests/components/tm1621/test.rp2040-ard.yaml index cddd64f31f..562ced7485 100644 --- a/tests/components/tm1621/test.rp2040-ard.yaml +++ b/tests/components/tm1621/test.rp2040-ard.yaml @@ -1,12 +1,7 @@ -display: - - platform: tm1621 - id: tm1621_display - cs_pin: 7 - data_pin: 4 - read_pin: 5 - write_pin: 6 - lambda: |- - it.printf(0, "%.1f", 20.0); - it.display_celsius(true); - it.printf(1, "%.1f", 20.0); - it.display_humidity(true); +substitutions: + cs_pin: GPIO6 + data_pin: GPIO7 + read_pin: GPIO2 + write_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/tm1637/common.yaml b/tests/components/tm1637/common.yaml new file mode 100644 index 0000000000..8d01e29877 --- /dev/null +++ b/tests/components/tm1637/common.yaml @@ -0,0 +1,7 @@ +display: + - platform: tm1637 + clk_pin: ${clk_pin} + dio_pin: ${dio_pin} + intensity: 3 + lambda: |- + it.print("1234"); diff --git a/tests/components/tm1637/test.esp32-ard.yaml b/tests/components/tm1637/test.esp32-ard.yaml index bf5f331cca..2c5786c47c 100644 --- a/tests/components/tm1637/test.esp32-ard.yaml +++ b/tests/components/tm1637/test.esp32-ard.yaml @@ -1,7 +1,5 @@ -display: - - platform: tm1637 - clk_pin: 15 - dio_pin: 14 - intensity: 3 - lambda: |- - it.print("1234"); +substitutions: + clk_pin: GPIO14 + dio_pin: GPIO13 + +<<: !include common.yaml diff --git a/tests/components/tm1637/test.esp32-c3-ard.yaml b/tests/components/tm1637/test.esp32-c3-ard.yaml index fa4c95b443..96f6708a3b 100644 --- a/tests/components/tm1637/test.esp32-c3-ard.yaml +++ b/tests/components/tm1637/test.esp32-c3-ard.yaml @@ -1,7 +1,5 @@ -display: - - platform: tm1637 - clk_pin: 5 - dio_pin: 4 - intensity: 3 - lambda: |- - it.print("1234"); +substitutions: + clk_pin: GPIO4 + dio_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/tm1637/test.esp32-c3-idf.yaml b/tests/components/tm1637/test.esp32-c3-idf.yaml index fa4c95b443..96f6708a3b 100644 --- a/tests/components/tm1637/test.esp32-c3-idf.yaml +++ b/tests/components/tm1637/test.esp32-c3-idf.yaml @@ -1,7 +1,5 @@ -display: - - platform: tm1637 - clk_pin: 5 - dio_pin: 4 - intensity: 3 - lambda: |- - it.print("1234"); +substitutions: + clk_pin: GPIO4 + dio_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/tm1637/test.esp32-idf.yaml b/tests/components/tm1637/test.esp32-idf.yaml index bf5f331cca..2c5786c47c 100644 --- a/tests/components/tm1637/test.esp32-idf.yaml +++ b/tests/components/tm1637/test.esp32-idf.yaml @@ -1,7 +1,5 @@ -display: - - platform: tm1637 - clk_pin: 15 - dio_pin: 14 - intensity: 3 - lambda: |- - it.print("1234"); +substitutions: + clk_pin: GPIO14 + dio_pin: GPIO13 + +<<: !include common.yaml diff --git a/tests/components/tm1637/test.esp8266-ard.yaml b/tests/components/tm1637/test.esp8266-ard.yaml index fa4c95b443..2c5786c47c 100644 --- a/tests/components/tm1637/test.esp8266-ard.yaml +++ b/tests/components/tm1637/test.esp8266-ard.yaml @@ -1,7 +1,5 @@ -display: - - platform: tm1637 - clk_pin: 5 - dio_pin: 4 - intensity: 3 - lambda: |- - it.print("1234"); +substitutions: + clk_pin: GPIO14 + dio_pin: GPIO13 + +<<: !include common.yaml diff --git a/tests/components/tm1637/test.rp2040-ard.yaml b/tests/components/tm1637/test.rp2040-ard.yaml index fa4c95b443..96f6708a3b 100644 --- a/tests/components/tm1637/test.rp2040-ard.yaml +++ b/tests/components/tm1637/test.rp2040-ard.yaml @@ -1,7 +1,5 @@ -display: - - platform: tm1637 - clk_pin: 5 - dio_pin: 4 - intensity: 3 - lambda: |- - it.print("1234"); +substitutions: + clk_pin: GPIO4 + dio_pin: GPIO3 + +<<: !include common.yaml diff --git a/tests/components/tmp102/common.yaml b/tests/components/tmp102/common.yaml new file mode 100644 index 0000000000..afc4a27fad --- /dev/null +++ b/tests/components/tmp102/common.yaml @@ -0,0 +1,8 @@ +i2c: + - id: i2c_tmp102 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tmp102 + name: TMP102 Temperature diff --git a/tests/components/tmp102/test.esp32-ard.yaml b/tests/components/tmp102/test.esp32-ard.yaml index 840bf7edb3..63c3bd6afd 100644 --- a/tests/components/tmp102/test.esp32-ard.yaml +++ b/tests/components/tmp102/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tmp102 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tmp102 - name: TMP102 Temperature +<<: !include common.yaml diff --git a/tests/components/tmp102/test.esp32-c3-ard.yaml b/tests/components/tmp102/test.esp32-c3-ard.yaml index c1d35fca3f..ee2c29ca4e 100644 --- a/tests/components/tmp102/test.esp32-c3-ard.yaml +++ b/tests/components/tmp102/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tmp102 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp102 - name: TMP102 Temperature +<<: !include common.yaml diff --git a/tests/components/tmp102/test.esp32-c3-idf.yaml b/tests/components/tmp102/test.esp32-c3-idf.yaml index c1d35fca3f..ee2c29ca4e 100644 --- a/tests/components/tmp102/test.esp32-c3-idf.yaml +++ b/tests/components/tmp102/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tmp102 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp102 - name: TMP102 Temperature +<<: !include common.yaml diff --git a/tests/components/tmp102/test.esp32-idf.yaml b/tests/components/tmp102/test.esp32-idf.yaml index 840bf7edb3..63c3bd6afd 100644 --- a/tests/components/tmp102/test.esp32-idf.yaml +++ b/tests/components/tmp102/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tmp102 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tmp102 - name: TMP102 Temperature +<<: !include common.yaml diff --git a/tests/components/tmp102/test.esp8266-ard.yaml b/tests/components/tmp102/test.esp8266-ard.yaml index c1d35fca3f..ee2c29ca4e 100644 --- a/tests/components/tmp102/test.esp8266-ard.yaml +++ b/tests/components/tmp102/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tmp102 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp102 - name: TMP102 Temperature +<<: !include common.yaml diff --git a/tests/components/tmp102/test.rp2040-ard.yaml b/tests/components/tmp102/test.rp2040-ard.yaml index c1d35fca3f..ee2c29ca4e 100644 --- a/tests/components/tmp102/test.rp2040-ard.yaml +++ b/tests/components/tmp102/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -i2c: - - id: i2c_tmp102 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp102 - name: TMP102 Temperature +<<: !include common.yaml diff --git a/tests/components/tmp1075/common.yaml b/tests/components/tmp1075/common.yaml new file mode 100644 index 0000000000..4c4c6c6f35 --- /dev/null +++ b/tests/components/tmp1075/common.yaml @@ -0,0 +1,16 @@ +i2c: + - id: i2c_tmp1075 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tmp1075 + name: Temperature TMP1075 + conversion_rate: 27.5ms + alert: + limit_low: 50 + limit_high: 75 + fault_count: 1 + polarity: active_high + function: comparator + update_interval: 10s diff --git a/tests/components/tmp1075/test.esp32-ard.yaml b/tests/components/tmp1075/test.esp32-ard.yaml index 6c50d0da77..63c3bd6afd 100644 --- a/tests/components/tmp1075/test.esp32-ard.yaml +++ b/tests/components/tmp1075/test.esp32-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_tmp1075 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tmp1075 - name: Temperature TMP1075 - conversion_rate: 27.5ms - alert: - limit_low: 50 - limit_high: 75 - fault_count: 1 - polarity: active_high - function: comparator - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp32-c3-ard.yaml b/tests/components/tmp1075/test.esp32-c3-ard.yaml index 99433aa655..ee2c29ca4e 100644 --- a/tests/components/tmp1075/test.esp32-c3-ard.yaml +++ b/tests/components/tmp1075/test.esp32-c3-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_tmp1075 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp1075 - name: Temperature TMP1075 - conversion_rate: 27.5ms - alert: - limit_low: 50 - limit_high: 75 - fault_count: 1 - polarity: active_high - function: comparator - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp32-c3-idf.yaml b/tests/components/tmp1075/test.esp32-c3-idf.yaml index 99433aa655..ee2c29ca4e 100644 --- a/tests/components/tmp1075/test.esp32-c3-idf.yaml +++ b/tests/components/tmp1075/test.esp32-c3-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_tmp1075 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp1075 - name: Temperature TMP1075 - conversion_rate: 27.5ms - alert: - limit_low: 50 - limit_high: 75 - fault_count: 1 - polarity: active_high - function: comparator - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp32-idf.yaml b/tests/components/tmp1075/test.esp32-idf.yaml index 6c50d0da77..63c3bd6afd 100644 --- a/tests/components/tmp1075/test.esp32-idf.yaml +++ b/tests/components/tmp1075/test.esp32-idf.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_tmp1075 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tmp1075 - name: Temperature TMP1075 - conversion_rate: 27.5ms - alert: - limit_low: 50 - limit_high: 75 - fault_count: 1 - polarity: active_high - function: comparator - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/tmp1075/test.esp8266-ard.yaml b/tests/components/tmp1075/test.esp8266-ard.yaml index 99433aa655..ee2c29ca4e 100644 --- a/tests/components/tmp1075/test.esp8266-ard.yaml +++ b/tests/components/tmp1075/test.esp8266-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_tmp1075 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp1075 - name: Temperature TMP1075 - conversion_rate: 27.5ms - alert: - limit_low: 50 - limit_high: 75 - fault_count: 1 - polarity: active_high - function: comparator - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/tmp1075/test.rp2040-ard.yaml b/tests/components/tmp1075/test.rp2040-ard.yaml index 99433aa655..ee2c29ca4e 100644 --- a/tests/components/tmp1075/test.rp2040-ard.yaml +++ b/tests/components/tmp1075/test.rp2040-ard.yaml @@ -1,16 +1,5 @@ -i2c: - - id: i2c_tmp1075 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp1075 - name: Temperature TMP1075 - conversion_rate: 27.5ms - alert: - limit_low: 50 - limit_high: 75 - fault_count: 1 - polarity: active_high - function: comparator - update_interval: 10s +<<: !include common.yaml diff --git a/tests/components/tmp117/common.yaml b/tests/components/tmp117/common.yaml new file mode 100644 index 0000000000..f4a5688933 --- /dev/null +++ b/tests/components/tmp117/common.yaml @@ -0,0 +1,9 @@ +i2c: + - id: i2c_tmp117 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tmp117 + name: TMP117 Temperature + update_interval: 5s diff --git a/tests/components/tmp117/test.esp32-ard.yaml b/tests/components/tmp117/test.esp32-ard.yaml index 03e0dd4e8e..63c3bd6afd 100644 --- a/tests/components/tmp117/test.esp32-ard.yaml +++ b/tests/components/tmp117/test.esp32-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tmp117 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tmp117 - name: TMP117 Temperature - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tmp117/test.esp32-c3-ard.yaml b/tests/components/tmp117/test.esp32-c3-ard.yaml index 61fc2cc03d..ee2c29ca4e 100644 --- a/tests/components/tmp117/test.esp32-c3-ard.yaml +++ b/tests/components/tmp117/test.esp32-c3-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tmp117 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp117 - name: TMP117 Temperature - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tmp117/test.esp32-c3-idf.yaml b/tests/components/tmp117/test.esp32-c3-idf.yaml index 61fc2cc03d..ee2c29ca4e 100644 --- a/tests/components/tmp117/test.esp32-c3-idf.yaml +++ b/tests/components/tmp117/test.esp32-c3-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tmp117 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp117 - name: TMP117 Temperature - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tmp117/test.esp32-idf.yaml b/tests/components/tmp117/test.esp32-idf.yaml index 03e0dd4e8e..63c3bd6afd 100644 --- a/tests/components/tmp117/test.esp32-idf.yaml +++ b/tests/components/tmp117/test.esp32-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tmp117 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tmp117 - name: TMP117 Temperature - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tmp117/test.esp8266-ard.yaml b/tests/components/tmp117/test.esp8266-ard.yaml index 61fc2cc03d..ee2c29ca4e 100644 --- a/tests/components/tmp117/test.esp8266-ard.yaml +++ b/tests/components/tmp117/test.esp8266-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tmp117 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp117 - name: TMP117 Temperature - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tmp117/test.rp2040-ard.yaml b/tests/components/tmp117/test.rp2040-ard.yaml index 61fc2cc03d..ee2c29ca4e 100644 --- a/tests/components/tmp117/test.rp2040-ard.yaml +++ b/tests/components/tmp117/test.rp2040-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tmp117 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tmp117 - name: TMP117 Temperature - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tof10120/common.yaml b/tests/components/tof10120/common.yaml new file mode 100644 index 0000000000..67643323d9 --- /dev/null +++ b/tests/components/tof10120/common.yaml @@ -0,0 +1,9 @@ +i2c: + - id: i2c_tof10120 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tof10120 + name: Distance sensor + update_interval: 5s diff --git a/tests/components/tof10120/test.esp32-ard.yaml b/tests/components/tof10120/test.esp32-ard.yaml index 74541ecde8..63c3bd6afd 100644 --- a/tests/components/tof10120/test.esp32-ard.yaml +++ b/tests/components/tof10120/test.esp32-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tof10120 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tof10120 - name: Distance sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tof10120/test.esp32-c3-ard.yaml b/tests/components/tof10120/test.esp32-c3-ard.yaml index 01cde0df6a..ee2c29ca4e 100644 --- a/tests/components/tof10120/test.esp32-c3-ard.yaml +++ b/tests/components/tof10120/test.esp32-c3-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tof10120 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tof10120 - name: Distance sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tof10120/test.esp32-c3-idf.yaml b/tests/components/tof10120/test.esp32-c3-idf.yaml index 01cde0df6a..ee2c29ca4e 100644 --- a/tests/components/tof10120/test.esp32-c3-idf.yaml +++ b/tests/components/tof10120/test.esp32-c3-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tof10120 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tof10120 - name: Distance sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tof10120/test.esp32-idf.yaml b/tests/components/tof10120/test.esp32-idf.yaml index 74541ecde8..63c3bd6afd 100644 --- a/tests/components/tof10120/test.esp32-idf.yaml +++ b/tests/components/tof10120/test.esp32-idf.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tof10120 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tof10120 - name: Distance sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tof10120/test.esp8266-ard.yaml b/tests/components/tof10120/test.esp8266-ard.yaml index 01cde0df6a..ee2c29ca4e 100644 --- a/tests/components/tof10120/test.esp8266-ard.yaml +++ b/tests/components/tof10120/test.esp8266-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tof10120 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tof10120 - name: Distance sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/tof10120/test.rp2040-ard.yaml b/tests/components/tof10120/test.rp2040-ard.yaml index 01cde0df6a..ee2c29ca4e 100644 --- a/tests/components/tof10120/test.rp2040-ard.yaml +++ b/tests/components/tof10120/test.rp2040-ard.yaml @@ -1,9 +1,5 @@ -i2c: - - id: i2c_tof10120 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tof10120 - name: Distance sensor - update_interval: 5s +<<: !include common.yaml diff --git a/tests/components/toshiba/common.yaml b/tests/components/toshiba/common.yaml new file mode 100644 index 0000000000..79a833980e --- /dev/null +++ b/tests/components/toshiba/common.yaml @@ -0,0 +1,7 @@ +remote_transmitter: + pin: ${pin} + carrier_duty_percent: 50% + +climate: + - platform: toshiba + name: Toshiba Climate diff --git a/tests/components/toshiba/test.esp32-ard.yaml b/tests/components/toshiba/test.esp32-ard.yaml index c134c7f5bd..7b012aa64c 100644 --- a/tests/components/toshiba/test.esp32-ard.yaml +++ b/tests/components/toshiba/test.esp32-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: toshiba - name: Toshiba Climate +<<: !include common.yaml diff --git a/tests/components/toshiba/test.esp32-c3-ard.yaml b/tests/components/toshiba/test.esp32-c3-ard.yaml index c134c7f5bd..7b012aa64c 100644 --- a/tests/components/toshiba/test.esp32-c3-ard.yaml +++ b/tests/components/toshiba/test.esp32-c3-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: toshiba - name: Toshiba Climate +<<: !include common.yaml diff --git a/tests/components/toshiba/test.esp32-c3-idf.yaml b/tests/components/toshiba/test.esp32-c3-idf.yaml index c134c7f5bd..7b012aa64c 100644 --- a/tests/components/toshiba/test.esp32-c3-idf.yaml +++ b/tests/components/toshiba/test.esp32-c3-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: toshiba - name: Toshiba Climate +<<: !include common.yaml diff --git a/tests/components/toshiba/test.esp32-idf.yaml b/tests/components/toshiba/test.esp32-idf.yaml index c134c7f5bd..7b012aa64c 100644 --- a/tests/components/toshiba/test.esp32-idf.yaml +++ b/tests/components/toshiba/test.esp32-idf.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 2 - carrier_duty_percent: 50% +substitutions: + pin: GPIO2 -climate: - - platform: toshiba - name: Toshiba Climate +<<: !include common.yaml diff --git a/tests/components/toshiba/test.esp8266-ard.yaml b/tests/components/toshiba/test.esp8266-ard.yaml index 8730a5d4ab..f5097fcf5f 100644 --- a/tests/components/toshiba/test.esp8266-ard.yaml +++ b/tests/components/toshiba/test.esp8266-ard.yaml @@ -1,7 +1,4 @@ -remote_transmitter: - pin: 5 - carrier_duty_percent: 50% +substitutions: + pin: GPIO5 -climate: - - platform: toshiba - name: Toshiba Climate +<<: !include common.yaml diff --git a/tests/components/total_daily_energy/common.yaml b/tests/components/total_daily_energy/common.yaml new file mode 100644 index 0000000000..ae4d30408b --- /dev/null +++ b/tests/components/total_daily_energy/common.yaml @@ -0,0 +1,32 @@ +wifi: + ssid: MySSID + password: password1 + +time: + - platform: sntp + id: sntp_time + +sensor: + - platform: hlw8012 + sel_pin: ${sel_pin} + cf_pin: ${cf_pin} + cf1_pin: ${cf1_pin} + current: + name: HLW8012 Current + voltage: + name: HLW8012 Voltage + power: + name: HLW8012 Power + id: hlw8012_power + energy: + name: HLW8012 Energy + id: hlw8012_energy + update_interval: 15s + current_resistor: 0.001 ohm + voltage_divider: 2351 + change_mode_every: "never" + initial_mode: VOLTAGE + model: hlw8012 + - platform: total_daily_energy + name: HLW8012 Total Daily Energy + power_id: hlw8012_power diff --git a/tests/components/total_daily_energy/test.esp32-ard.yaml b/tests/components/total_daily_energy/test.esp32-ard.yaml index 34d452aae5..8b42b21b54 100644 --- a/tests/components/total_daily_energy/test.esp32-ard.yaml +++ b/tests/components/total_daily_energy/test.esp32-ard.yaml @@ -1,32 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + sel_pin: GPIO12 + cf_pin: GPIO13 + cf1_pin: GPIO14 -time: - - platform: sntp - id: sntp_time - -sensor: - - platform: hlw8012 - sel_pin: 15 - cf_pin: 14 - cf1_pin: 13 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE - model: hlw8012 - - platform: total_daily_energy - name: HLW8012 Total Daily Energy - power_id: hlw8012_power +<<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.esp32-c3-ard.yaml b/tests/components/total_daily_energy/test.esp32-c3-ard.yaml index 71afa45ed5..8b0d069ce2 100644 --- a/tests/components/total_daily_energy/test.esp32-c3-ard.yaml +++ b/tests/components/total_daily_energy/test.esp32-c3-ard.yaml @@ -1,32 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + sel_pin: GPIO2 + cf_pin: GPIO3 + cf1_pin: GPIO4 -time: - - platform: sntp - id: sntp_time - -sensor: - - platform: hlw8012 - sel_pin: 5 - cf_pin: 4 - cf1_pin: 3 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE - model: hlw8012 - - platform: total_daily_energy - name: HLW8012 Total Daily Energy - power_id: hlw8012_power +<<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.esp32-c3-idf.yaml b/tests/components/total_daily_energy/test.esp32-c3-idf.yaml index 71afa45ed5..8b0d069ce2 100644 --- a/tests/components/total_daily_energy/test.esp32-c3-idf.yaml +++ b/tests/components/total_daily_energy/test.esp32-c3-idf.yaml @@ -1,32 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + sel_pin: GPIO2 + cf_pin: GPIO3 + cf1_pin: GPIO4 -time: - - platform: sntp - id: sntp_time - -sensor: - - platform: hlw8012 - sel_pin: 5 - cf_pin: 4 - cf1_pin: 3 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE - model: hlw8012 - - platform: total_daily_energy - name: HLW8012 Total Daily Energy - power_id: hlw8012_power +<<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.esp32-idf.yaml b/tests/components/total_daily_energy/test.esp32-idf.yaml index 34d452aae5..8b42b21b54 100644 --- a/tests/components/total_daily_energy/test.esp32-idf.yaml +++ b/tests/components/total_daily_energy/test.esp32-idf.yaml @@ -1,32 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + sel_pin: GPIO12 + cf_pin: GPIO13 + cf1_pin: GPIO14 -time: - - platform: sntp - id: sntp_time - -sensor: - - platform: hlw8012 - sel_pin: 15 - cf_pin: 14 - cf1_pin: 13 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE - model: hlw8012 - - platform: total_daily_energy - name: HLW8012 Total Daily Energy - power_id: hlw8012_power +<<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.esp8266-ard.yaml b/tests/components/total_daily_energy/test.esp8266-ard.yaml index 34d452aae5..8b42b21b54 100644 --- a/tests/components/total_daily_energy/test.esp8266-ard.yaml +++ b/tests/components/total_daily_energy/test.esp8266-ard.yaml @@ -1,32 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + sel_pin: GPIO12 + cf_pin: GPIO13 + cf1_pin: GPIO14 -time: - - platform: sntp - id: sntp_time - -sensor: - - platform: hlw8012 - sel_pin: 15 - cf_pin: 14 - cf1_pin: 13 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE - model: hlw8012 - - platform: total_daily_energy - name: HLW8012 Total Daily Energy - power_id: hlw8012_power +<<: !include common.yaml diff --git a/tests/components/total_daily_energy/test.rp2040-ard.yaml b/tests/components/total_daily_energy/test.rp2040-ard.yaml index 71afa45ed5..8b0d069ce2 100644 --- a/tests/components/total_daily_energy/test.rp2040-ard.yaml +++ b/tests/components/total_daily_energy/test.rp2040-ard.yaml @@ -1,32 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + sel_pin: GPIO2 + cf_pin: GPIO3 + cf1_pin: GPIO4 -time: - - platform: sntp - id: sntp_time - -sensor: - - platform: hlw8012 - sel_pin: 5 - cf_pin: 4 - cf1_pin: 3 - current: - name: HLW8012 Current - voltage: - name: HLW8012 Voltage - power: - name: HLW8012 Power - id: hlw8012_power - energy: - name: HLW8012 Energy - id: hlw8012_energy - update_interval: 15s - current_resistor: 0.001 ohm - voltage_divider: 2351 - change_mode_every: "never" - initial_mode: VOLTAGE - model: hlw8012 - - platform: total_daily_energy - name: HLW8012 Total Daily Energy - power_id: hlw8012_power +<<: !include common.yaml diff --git a/tests/components/tsl2561/common.yaml b/tests/components/tsl2561/common.yaml new file mode 100644 index 0000000000..d2b4f75df3 --- /dev/null +++ b/tests/components/tsl2561/common.yaml @@ -0,0 +1,13 @@ +i2c: + - id: i2c_tsl2561 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tsl2561 + name: TSL2561 Ambient Light + address: 0x39 + is_cs_package: true + integration_time: 402ms + gain: 16x + update_interval: 15s diff --git a/tests/components/tsl2561/test.esp32-ard.yaml b/tests/components/tsl2561/test.esp32-ard.yaml index 8d43c62414..63c3bd6afd 100644 --- a/tests/components/tsl2561/test.esp32-ard.yaml +++ b/tests/components/tsl2561/test.esp32-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_tsl2561 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tsl2561 - name: TSL2561 Ambient Light - address: 0x39 - is_cs_package: true - integration_time: 402ms - gain: 16x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp32-c3-ard.yaml b/tests/components/tsl2561/test.esp32-c3-ard.yaml index 1ea768c5d9..ee2c29ca4e 100644 --- a/tests/components/tsl2561/test.esp32-c3-ard.yaml +++ b/tests/components/tsl2561/test.esp32-c3-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_tsl2561 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2561 - name: TSL2561 Ambient Light - address: 0x39 - is_cs_package: true - integration_time: 402ms - gain: 16x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp32-c3-idf.yaml b/tests/components/tsl2561/test.esp32-c3-idf.yaml index 1ea768c5d9..ee2c29ca4e 100644 --- a/tests/components/tsl2561/test.esp32-c3-idf.yaml +++ b/tests/components/tsl2561/test.esp32-c3-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_tsl2561 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2561 - name: TSL2561 Ambient Light - address: 0x39 - is_cs_package: true - integration_time: 402ms - gain: 16x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp32-idf.yaml b/tests/components/tsl2561/test.esp32-idf.yaml index 8d43c62414..63c3bd6afd 100644 --- a/tests/components/tsl2561/test.esp32-idf.yaml +++ b/tests/components/tsl2561/test.esp32-idf.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_tsl2561 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tsl2561 - name: TSL2561 Ambient Light - address: 0x39 - is_cs_package: true - integration_time: 402ms - gain: 16x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2561/test.esp8266-ard.yaml b/tests/components/tsl2561/test.esp8266-ard.yaml index 1ea768c5d9..ee2c29ca4e 100644 --- a/tests/components/tsl2561/test.esp8266-ard.yaml +++ b/tests/components/tsl2561/test.esp8266-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_tsl2561 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2561 - name: TSL2561 Ambient Light - address: 0x39 - is_cs_package: true - integration_time: 402ms - gain: 16x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2561/test.rp2040-ard.yaml b/tests/components/tsl2561/test.rp2040-ard.yaml index 1ea768c5d9..ee2c29ca4e 100644 --- a/tests/components/tsl2561/test.rp2040-ard.yaml +++ b/tests/components/tsl2561/test.rp2040-ard.yaml @@ -1,13 +1,5 @@ -i2c: - - id: i2c_tsl2561 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2561 - name: TSL2561 Ambient Light - address: 0x39 - is_cs_package: true - integration_time: 402ms - gain: 16x - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2591/common.yaml b/tests/components/tsl2591/common.yaml new file mode 100644 index 0000000000..d58c46fb48 --- /dev/null +++ b/tests/components/tsl2591/common.yaml @@ -0,0 +1,25 @@ +i2c: + - id: i2c_tsl2591 + scl: ${scl_pin} + sda: ${sda_pin} + +sensor: + - platform: tsl2591 + id: test_tsl2591 + address: 0x29 + integration_time: 600ms + gain: high + visible: + name: tsl2591 visible + id: tsl2591_vis + unit_of_measurement: pH + infrared: + name: tsl2591 infrared + id: tsl2591_ir + full_spectrum: + name: tsl2591 full_spectrum + id: tsl2591_fs + calculated_lux: + name: tsl2591 calculated_lux + id: tsl2591_cl + update_interval: 15s diff --git a/tests/components/tsl2591/test.esp32-ard.yaml b/tests/components/tsl2591/test.esp32-ard.yaml index 14f9311ae6..63c3bd6afd 100644 --- a/tests/components/tsl2591/test.esp32-ard.yaml +++ b/tests/components/tsl2591/test.esp32-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_tsl2591 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tsl2591 - id: test_tsl2591 - address: 0x29 - integration_time: 600ms - gain: high - visible: - name: tsl2591 visible - id: tsl2591_vis - unit_of_measurement: pH - infrared: - name: tsl2591 infrared - id: tsl2591_ir - full_spectrum: - name: tsl2591 full_spectrum - id: tsl2591_fs - calculated_lux: - name: tsl2591 calculated_lux - id: tsl2591_cl - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp32-c3-ard.yaml b/tests/components/tsl2591/test.esp32-c3-ard.yaml index de57ef548a..ee2c29ca4e 100644 --- a/tests/components/tsl2591/test.esp32-c3-ard.yaml +++ b/tests/components/tsl2591/test.esp32-c3-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_tsl2591 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2591 - id: test_tsl2591 - address: 0x29 - integration_time: 600ms - gain: high - visible: - name: tsl2591 visible - id: tsl2591_vis - unit_of_measurement: pH - infrared: - name: tsl2591 infrared - id: tsl2591_ir - full_spectrum: - name: tsl2591 full_spectrum - id: tsl2591_fs - calculated_lux: - name: tsl2591 calculated_lux - id: tsl2591_cl - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp32-c3-idf.yaml b/tests/components/tsl2591/test.esp32-c3-idf.yaml index de57ef548a..ee2c29ca4e 100644 --- a/tests/components/tsl2591/test.esp32-c3-idf.yaml +++ b/tests/components/tsl2591/test.esp32-c3-idf.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_tsl2591 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2591 - id: test_tsl2591 - address: 0x29 - integration_time: 600ms - gain: high - visible: - name: tsl2591 visible - id: tsl2591_vis - unit_of_measurement: pH - infrared: - name: tsl2591 infrared - id: tsl2591_ir - full_spectrum: - name: tsl2591 full_spectrum - id: tsl2591_fs - calculated_lux: - name: tsl2591 calculated_lux - id: tsl2591_cl - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp32-idf.yaml b/tests/components/tsl2591/test.esp32-idf.yaml index 14f9311ae6..63c3bd6afd 100644 --- a/tests/components/tsl2591/test.esp32-idf.yaml +++ b/tests/components/tsl2591/test.esp32-idf.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_tsl2591 - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -sensor: - - platform: tsl2591 - id: test_tsl2591 - address: 0x29 - integration_time: 600ms - gain: high - visible: - name: tsl2591 visible - id: tsl2591_vis - unit_of_measurement: pH - infrared: - name: tsl2591 infrared - id: tsl2591_ir - full_spectrum: - name: tsl2591 full_spectrum - id: tsl2591_fs - calculated_lux: - name: tsl2591 calculated_lux - id: tsl2591_cl - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2591/test.esp8266-ard.yaml b/tests/components/tsl2591/test.esp8266-ard.yaml index de57ef548a..ee2c29ca4e 100644 --- a/tests/components/tsl2591/test.esp8266-ard.yaml +++ b/tests/components/tsl2591/test.esp8266-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_tsl2591 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2591 - id: test_tsl2591 - address: 0x29 - integration_time: 600ms - gain: high - visible: - name: tsl2591 visible - id: tsl2591_vis - unit_of_measurement: pH - infrared: - name: tsl2591 infrared - id: tsl2591_ir - full_spectrum: - name: tsl2591 full_spectrum - id: tsl2591_fs - calculated_lux: - name: tsl2591 calculated_lux - id: tsl2591_cl - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tsl2591/test.rp2040-ard.yaml b/tests/components/tsl2591/test.rp2040-ard.yaml index de57ef548a..ee2c29ca4e 100644 --- a/tests/components/tsl2591/test.rp2040-ard.yaml +++ b/tests/components/tsl2591/test.rp2040-ard.yaml @@ -1,25 +1,5 @@ -i2c: - - id: i2c_tsl2591 - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -sensor: - - platform: tsl2591 - id: test_tsl2591 - address: 0x29 - integration_time: 600ms - gain: high - visible: - name: tsl2591 visible - id: tsl2591_vis - unit_of_measurement: pH - infrared: - name: tsl2591 infrared - id: tsl2591_ir - full_spectrum: - name: tsl2591 full_spectrum - id: tsl2591_fs - calculated_lux: - name: tsl2591 calculated_lux - id: tsl2591_cl - update_interval: 15s +<<: !include common.yaml diff --git a/tests/components/tt21100/common.yaml b/tests/components/tt21100/common.yaml new file mode 100644 index 0000000000..a5d7970429 --- /dev/null +++ b/tests/components/tt21100/common.yaml @@ -0,0 +1,25 @@ +i2c: + - id: i2c_tt21100 + scl: ${scl_pin} + sda: ${sda_pin} + +display: + - platform: ssd1306_i2c + id: ssd1306_display + model: SSD1306_128X64 + reset_pin: ${disp_reset_pin} + pages: + - id: page1 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + +touchscreen: + - platform: tt21100 + display: ssd1306_display + interrupt_pin: ${interrupt_pin} + reset_pin: ${reset_pin} + +binary_sensor: + - platform: tt21100 + name: Home Button + index: 1 diff --git a/tests/components/tt21100/test.esp32-ard.yaml b/tests/components/tt21100/test.esp32-ard.yaml index 2419b0ad6a..05598719f9 100644 --- a/tests/components/tt21100/test.esp32-ard.yaml +++ b/tests/components/tt21100/test.esp32-ard.yaml @@ -1,25 +1,8 @@ -i2c: - - id: i2c_tt21100 - scl: 16 - sda: 17 +substitutions: + disp_reset_pin: GPIO12 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: tt21100 - display: ssd1306_display - interrupt_pin: 14 - reset_pin: 15 - -binary_sensor: - - platform: tt21100 - name: Home Button - index: 1 +<<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-c3-ard.yaml b/tests/components/tt21100/test.esp32-c3-ard.yaml index 17b8c8065a..36a8ce2778 100644 --- a/tests/components/tt21100/test.esp32-c3-ard.yaml +++ b/tests/components/tt21100/test.esp32-c3-ard.yaml @@ -1,25 +1,8 @@ -i2c: - - id: i2c_tt21100 - scl: 5 - sda: 4 +substitutions: + disp_reset_pin: GPIO10 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: tt21100 - display: ssd1306_display - interrupt_pin: 6 - reset_pin: 7 - -binary_sensor: - - platform: tt21100 - name: Home Button - index: 1 +<<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-c3-idf.yaml b/tests/components/tt21100/test.esp32-c3-idf.yaml index 17b8c8065a..36a8ce2778 100644 --- a/tests/components/tt21100/test.esp32-c3-idf.yaml +++ b/tests/components/tt21100/test.esp32-c3-idf.yaml @@ -1,25 +1,8 @@ -i2c: - - id: i2c_tt21100 - scl: 5 - sda: 4 +substitutions: + disp_reset_pin: GPIO10 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: tt21100 - display: ssd1306_display - interrupt_pin: 6 - reset_pin: 7 - -binary_sensor: - - platform: tt21100 - name: Home Button - index: 1 +<<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-idf.yaml b/tests/components/tt21100/test.esp32-idf.yaml index 2419b0ad6a..05598719f9 100644 --- a/tests/components/tt21100/test.esp32-idf.yaml +++ b/tests/components/tt21100/test.esp32-idf.yaml @@ -1,25 +1,8 @@ -i2c: - - id: i2c_tt21100 - scl: 16 - sda: 17 +substitutions: + disp_reset_pin: GPIO12 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: tt21100 - display: ssd1306_display - interrupt_pin: 14 - reset_pin: 15 - -binary_sensor: - - platform: tt21100 - name: Home Button - index: 1 +<<: !include common.yaml diff --git a/tests/components/tt21100/test.esp32-s2-ard.yaml b/tests/components/tt21100/test.esp32-s2-ard.yaml deleted file mode 100644 index 86b9e7530d..0000000000 --- a/tests/components/tt21100/test.esp32-s2-ard.yaml +++ /dev/null @@ -1,44 +0,0 @@ -i2c: - sda: GPIO8 - scl: GPIO18 - -spi: - clk_pin: 7 - mosi_pin: 11 - miso_pin: 9 - -display: - - platform: ili9xxx - id: my_display - model: ili9341 - cs_pin: 5 - dc_pin: 12 - reset_pin: 33 - auto_clear_enabled: false - data_rate: 40MHz - dimensions: 320x240 - update_interval: never - invert_colors: false - transform: - mirror_y: false - mirror_x: false - swap_xy: true - -touchscreen: - - platform: tt21100 - address: 0x24 - interrupt_pin: GPIO3 - on_touch: - - logger.log: "Touchscreen:: Touched" - -binary_sensor: - - platform: tt21100 - index: 0 - name: "Home" - - - platform: touchscreen - name: FanLo - x_min: 0 - x_max: 105 - y_min: 0 - y_max: 80 diff --git a/tests/components/tt21100/test.esp8266-ard.yaml b/tests/components/tt21100/test.esp8266-ard.yaml index 1393019417..05598719f9 100644 --- a/tests/components/tt21100/test.esp8266-ard.yaml +++ b/tests/components/tt21100/test.esp8266-ard.yaml @@ -1,25 +1,8 @@ -i2c: - - id: i2c_tt21100 - scl: 5 - sda: 4 +substitutions: + disp_reset_pin: GPIO12 + scl_pin: GPIO13 + sda_pin: GPIO14 + interrupt_pin: GPIO15 + reset_pin: GPIO16 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 13 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: tt21100 - display: ssd1306_display - interrupt_pin: 14 - reset_pin: 15 - -binary_sensor: - - platform: tt21100 - name: Home Button - index: 1 +<<: !include common.yaml diff --git a/tests/components/tt21100/test.rp2040-ard.yaml b/tests/components/tt21100/test.rp2040-ard.yaml index 17b8c8065a..36a8ce2778 100644 --- a/tests/components/tt21100/test.rp2040-ard.yaml +++ b/tests/components/tt21100/test.rp2040-ard.yaml @@ -1,25 +1,8 @@ -i2c: - - id: i2c_tt21100 - scl: 5 - sda: 4 +substitutions: + disp_reset_pin: GPIO10 + scl_pin: GPIO0 + sda_pin: GPIO1 + interrupt_pin: GPIO2 + reset_pin: GPIO3 -display: - - platform: ssd1306_i2c - id: ssd1306_display - model: SSD1306_128X64 - reset_pin: 3 - pages: - - id: page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - -touchscreen: - - platform: tt21100 - display: ssd1306_display - interrupt_pin: 6 - reset_pin: 7 - -binary_sensor: - - platform: tt21100 - name: Home Button - index: 1 +<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/common.yaml b/tests/components/ttp229_bsf/common.yaml new file mode 100644 index 0000000000..42c26a5d51 --- /dev/null +++ b/tests/components/ttp229_bsf/common.yaml @@ -0,0 +1,8 @@ +ttp229_bsf: + scl_pin: ${scl_pin} + sdo_pin: ${sdo_pin} + +binary_sensor: + - platform: ttp229_bsf + name: TTP229 Channel 0 + channel: 0 diff --git a/tests/components/ttp229_bsf/test.esp32-ard.yaml b/tests/components/ttp229_bsf/test.esp32-ard.yaml index edee6d164e..80ed75293f 100644 --- a/tests/components/ttp229_bsf/test.esp32-ard.yaml +++ b/tests/components/ttp229_bsf/test.esp32-ard.yaml @@ -1,8 +1,5 @@ -ttp229_bsf: - scl_pin: 16 - sdo_pin: 17 +substitutions: + scl_pin: GPIO16 + sdo_pin: GPIO17 -binary_sensor: - - platform: ttp229_bsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp32-c3-ard.yaml b/tests/components/ttp229_bsf/test.esp32-c3-ard.yaml index 2006061c6e..135b213edc 100644 --- a/tests/components/ttp229_bsf/test.esp32-c3-ard.yaml +++ b/tests/components/ttp229_bsf/test.esp32-c3-ard.yaml @@ -1,8 +1,5 @@ -ttp229_bsf: - scl_pin: 5 - sdo_pin: 4 +substitutions: + scl_pin: GPIO5 + sdo_pin: GPIO4 -binary_sensor: - - platform: ttp229_bsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml b/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml index 2006061c6e..135b213edc 100644 --- a/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml +++ b/tests/components/ttp229_bsf/test.esp32-c3-idf.yaml @@ -1,8 +1,5 @@ -ttp229_bsf: - scl_pin: 5 - sdo_pin: 4 +substitutions: + scl_pin: GPIO5 + sdo_pin: GPIO4 -binary_sensor: - - platform: ttp229_bsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp32-idf.yaml b/tests/components/ttp229_bsf/test.esp32-idf.yaml index edee6d164e..80ed75293f 100644 --- a/tests/components/ttp229_bsf/test.esp32-idf.yaml +++ b/tests/components/ttp229_bsf/test.esp32-idf.yaml @@ -1,8 +1,5 @@ -ttp229_bsf: - scl_pin: 16 - sdo_pin: 17 +substitutions: + scl_pin: GPIO16 + sdo_pin: GPIO17 -binary_sensor: - - platform: ttp229_bsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.esp8266-ard.yaml b/tests/components/ttp229_bsf/test.esp8266-ard.yaml index 2006061c6e..135b213edc 100644 --- a/tests/components/ttp229_bsf/test.esp8266-ard.yaml +++ b/tests/components/ttp229_bsf/test.esp8266-ard.yaml @@ -1,8 +1,5 @@ -ttp229_bsf: - scl_pin: 5 - sdo_pin: 4 +substitutions: + scl_pin: GPIO5 + sdo_pin: GPIO4 -binary_sensor: - - platform: ttp229_bsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_bsf/test.rp2040-ard.yaml b/tests/components/ttp229_bsf/test.rp2040-ard.yaml index 2006061c6e..135b213edc 100644 --- a/tests/components/ttp229_bsf/test.rp2040-ard.yaml +++ b/tests/components/ttp229_bsf/test.rp2040-ard.yaml @@ -1,8 +1,5 @@ -ttp229_bsf: - scl_pin: 5 - sdo_pin: 4 +substitutions: + scl_pin: GPIO5 + sdo_pin: GPIO4 -binary_sensor: - - platform: ttp229_bsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/common.yaml b/tests/components/ttp229_lsf/common.yaml new file mode 100644 index 0000000000..5c0dbf9517 --- /dev/null +++ b/tests/components/ttp229_lsf/common.yaml @@ -0,0 +1,11 @@ +i2c: + - id: i2c_ttp229_lsf + scl: ${scl_pin} + sda: ${sda_pin} + +ttp229_lsf: + +binary_sensor: + - platform: ttp229_lsf + name: TTP229 Channel 0 + channel: 0 diff --git a/tests/components/ttp229_lsf/test.esp32-ard.yaml b/tests/components/ttp229_lsf/test.esp32-ard.yaml index 81fb965883..63c3bd6afd 100644 --- a/tests/components/ttp229_lsf/test.esp32-ard.yaml +++ b/tests/components/ttp229_lsf/test.esp32-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_ttp229_lsf - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -ttp229_lsf: - -binary_sensor: - - platform: ttp229_lsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp32-c3-ard.yaml b/tests/components/ttp229_lsf/test.esp32-c3-ard.yaml index 3927aff40e..ee2c29ca4e 100644 --- a/tests/components/ttp229_lsf/test.esp32-c3-ard.yaml +++ b/tests/components/ttp229_lsf/test.esp32-c3-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_ttp229_lsf - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ttp229_lsf: - -binary_sensor: - - platform: ttp229_lsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml b/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml index 3927aff40e..ee2c29ca4e 100644 --- a/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml +++ b/tests/components/ttp229_lsf/test.esp32-c3-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_ttp229_lsf - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ttp229_lsf: - -binary_sensor: - - platform: ttp229_lsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp32-idf.yaml b/tests/components/ttp229_lsf/test.esp32-idf.yaml index 81fb965883..63c3bd6afd 100644 --- a/tests/components/ttp229_lsf/test.esp32-idf.yaml +++ b/tests/components/ttp229_lsf/test.esp32-idf.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_ttp229_lsf - scl: 16 - sda: 17 +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 -ttp229_lsf: - -binary_sensor: - - platform: ttp229_lsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.esp8266-ard.yaml b/tests/components/ttp229_lsf/test.esp8266-ard.yaml index 3927aff40e..ee2c29ca4e 100644 --- a/tests/components/ttp229_lsf/test.esp8266-ard.yaml +++ b/tests/components/ttp229_lsf/test.esp8266-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_ttp229_lsf - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ttp229_lsf: - -binary_sensor: - - platform: ttp229_lsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/ttp229_lsf/test.rp2040-ard.yaml b/tests/components/ttp229_lsf/test.rp2040-ard.yaml index 3927aff40e..ee2c29ca4e 100644 --- a/tests/components/ttp229_lsf/test.rp2040-ard.yaml +++ b/tests/components/ttp229_lsf/test.rp2040-ard.yaml @@ -1,11 +1,5 @@ -i2c: - - id: i2c_ttp229_lsf - scl: 5 - sda: 4 +substitutions: + scl_pin: GPIO5 + sda_pin: GPIO4 -ttp229_lsf: - -binary_sensor: - - platform: ttp229_lsf - name: TTP229 Channel 0 - channel: 0 +<<: !include common.yaml diff --git a/tests/components/tuya/common.yaml b/tests/components/tuya/common.yaml new file mode 100644 index 0000000000..fcf8a2d96b --- /dev/null +++ b/tests/components/tuya/common.yaml @@ -0,0 +1,78 @@ +wifi: + ssid: MySSID + password: password1 + +uart: + - id: uart_tuya + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + baud_rate: 9600 + +tuya: + status_pin: + number: ${status_pin} + inverted: true + on_datapoint_update: + - sensor_datapoint: 6 + datapoint_type: raw + then: + - logger.log: Datapoint 6 updated + +binary_sensor: + - platform: tuya + id: tuya_binary_sensor + sensor_datapoint: 1 + +climate: + - platform: tuya + id: tuya_climate + switch_datapoint: 1 + target_temperature_datapoint: 3 + current_temperature_multiplier: 0.5 + target_temperature_multiplier: 0.5 + reports_fahrenheit: true + +cover: + - platform: tuya + id: tuya_cover + position_datapoint: 2 + +light: + - platform: tuya + id: tuya_light + switch_datapoint: 1 + dimmer_datapoint: 2 + min_value_datapoint: 3 + color_temperature_datapoint: 4 + min_value: 1 + max_value: 100 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + gamma_correct: 1 + +number: + - platform: tuya + id: tuya_number + number_datapoint: 102 + min_value: 0 + max_value: 17 + step: 1 + +select: + - platform: tuya + id: tuya_select + enum_datapoint: 42 + options: + 0: Internal + 1: Floor + 2: Both + +sensor: + - platform: tuya + id: tuya_sensor + sensor_datapoint: 1 + +switch: + - platform: tuya + id: tuya_switch + switch_datapoint: 1 diff --git a/tests/components/tuya/test.esp32-ard.yaml b/tests/components/tuya/test.esp32-ard.yaml index 9105522dcd..926a46cf73 100644 --- a/tests/components/tuya/test.esp32-ard.yaml +++ b/tests/components/tuya/test.esp32-ard.yaml @@ -1,78 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + status_pin: GPIO12 -uart: - - id: uart_tuya - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -tuya: - status_pin: - number: 15 - inverted: true - on_datapoint_update: - - sensor_datapoint: 6 - datapoint_type: raw - then: - - logger.log: Datapoint 6 updated - -binary_sensor: - - platform: tuya - id: tuya_binary_sensor - sensor_datapoint: 1 - -climate: - - platform: tuya - id: tuya_climate - switch_datapoint: 1 - target_temperature_datapoint: 3 - current_temperature_multiplier: 0.5 - target_temperature_multiplier: 0.5 - reports_fahrenheit: true - -cover: - - platform: tuya - id: tuya_cover - position_datapoint: 2 - -light: - - platform: tuya - id: tuya_light - switch_datapoint: 1 - dimmer_datapoint: 2 - min_value_datapoint: 3 - color_temperature_datapoint: 4 - min_value: 1 - max_value: 100 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - gamma_correct: 1 - -number: - - platform: tuya - id: tuya_number - number_datapoint: 102 - min_value: 0 - max_value: 17 - step: 1 - -select: - - platform: tuya - id: tuya_select - enum_datapoint: 42 - options: - 0: Internal - 1: Floor - 2: Both - -sensor: - - platform: tuya - id: tuya_sensor - sensor_datapoint: 1 - -switch: - - platform: tuya - id: tuya_switch - switch_datapoint: 1 +<<: !include common.yaml diff --git a/tests/components/tuya/test.esp32-c3-ard.yaml b/tests/components/tuya/test.esp32-c3-ard.yaml index 4892e807b1..c62a0b10f6 100644 --- a/tests/components/tuya/test.esp32-c3-ard.yaml +++ b/tests/components/tuya/test.esp32-c3-ard.yaml @@ -1,78 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + status_pin: GPIO2 -uart: - - id: uart_tuya - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -tuya: - status_pin: - number: 6 - inverted: true - on_datapoint_update: - - sensor_datapoint: 6 - datapoint_type: raw - then: - - logger.log: Datapoint 6 updated - -binary_sensor: - - platform: tuya - id: tuya_binary_sensor - sensor_datapoint: 1 - -climate: - - platform: tuya - id: tuya_climate - switch_datapoint: 1 - target_temperature_datapoint: 3 - current_temperature_multiplier: 0.5 - target_temperature_multiplier: 0.5 - reports_fahrenheit: true - -cover: - - platform: tuya - id: tuya_cover - position_datapoint: 2 - -light: - - platform: tuya - id: tuya_light - switch_datapoint: 1 - dimmer_datapoint: 2 - min_value_datapoint: 3 - color_temperature_datapoint: 4 - min_value: 1 - max_value: 100 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - gamma_correct: 1 - -number: - - platform: tuya - id: tuya_number - number_datapoint: 102 - min_value: 0 - max_value: 17 - step: 1 - -select: - - platform: tuya - id: tuya_select - enum_datapoint: 42 - options: - 0: Internal - 1: Floor - 2: Both - -sensor: - - platform: tuya - id: tuya_sensor - sensor_datapoint: 1 - -switch: - - platform: tuya - id: tuya_switch - switch_datapoint: 1 +<<: !include common.yaml diff --git a/tests/components/tuya/test.esp32-c3-idf.yaml b/tests/components/tuya/test.esp32-c3-idf.yaml index 4892e807b1..c62a0b10f6 100644 --- a/tests/components/tuya/test.esp32-c3-idf.yaml +++ b/tests/components/tuya/test.esp32-c3-idf.yaml @@ -1,78 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + status_pin: GPIO2 -uart: - - id: uart_tuya - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -tuya: - status_pin: - number: 6 - inverted: true - on_datapoint_update: - - sensor_datapoint: 6 - datapoint_type: raw - then: - - logger.log: Datapoint 6 updated - -binary_sensor: - - platform: tuya - id: tuya_binary_sensor - sensor_datapoint: 1 - -climate: - - platform: tuya - id: tuya_climate - switch_datapoint: 1 - target_temperature_datapoint: 3 - current_temperature_multiplier: 0.5 - target_temperature_multiplier: 0.5 - reports_fahrenheit: true - -cover: - - platform: tuya - id: tuya_cover - position_datapoint: 2 - -light: - - platform: tuya - id: tuya_light - switch_datapoint: 1 - dimmer_datapoint: 2 - min_value_datapoint: 3 - color_temperature_datapoint: 4 - min_value: 1 - max_value: 100 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - gamma_correct: 1 - -number: - - platform: tuya - id: tuya_number - number_datapoint: 102 - min_value: 0 - max_value: 17 - step: 1 - -select: - - platform: tuya - id: tuya_select - enum_datapoint: 42 - options: - 0: Internal - 1: Floor - 2: Both - -sensor: - - platform: tuya - id: tuya_sensor - sensor_datapoint: 1 - -switch: - - platform: tuya - id: tuya_switch - switch_datapoint: 1 +<<: !include common.yaml diff --git a/tests/components/tuya/test.esp32-idf.yaml b/tests/components/tuya/test.esp32-idf.yaml index 9105522dcd..926a46cf73 100644 --- a/tests/components/tuya/test.esp32-idf.yaml +++ b/tests/components/tuya/test.esp32-idf.yaml @@ -1,78 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO17 + rx_pin: GPIO16 + status_pin: GPIO12 -uart: - - id: uart_tuya - tx_pin: 17 - rx_pin: 16 - baud_rate: 9600 - -tuya: - status_pin: - number: 15 - inverted: true - on_datapoint_update: - - sensor_datapoint: 6 - datapoint_type: raw - then: - - logger.log: Datapoint 6 updated - -binary_sensor: - - platform: tuya - id: tuya_binary_sensor - sensor_datapoint: 1 - -climate: - - platform: tuya - id: tuya_climate - switch_datapoint: 1 - target_temperature_datapoint: 3 - current_temperature_multiplier: 0.5 - target_temperature_multiplier: 0.5 - reports_fahrenheit: true - -cover: - - platform: tuya - id: tuya_cover - position_datapoint: 2 - -light: - - platform: tuya - id: tuya_light - switch_datapoint: 1 - dimmer_datapoint: 2 - min_value_datapoint: 3 - color_temperature_datapoint: 4 - min_value: 1 - max_value: 100 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - gamma_correct: 1 - -number: - - platform: tuya - id: tuya_number - number_datapoint: 102 - min_value: 0 - max_value: 17 - step: 1 - -select: - - platform: tuya - id: tuya_select - enum_datapoint: 42 - options: - 0: Internal - 1: Floor - 2: Both - -sensor: - - platform: tuya - id: tuya_sensor - sensor_datapoint: 1 - -switch: - - platform: tuya - id: tuya_switch - switch_datapoint: 1 +<<: !include common.yaml diff --git a/tests/components/tuya/test.esp8266-ard.yaml b/tests/components/tuya/test.esp8266-ard.yaml index 56177fb982..11d46ed50e 100644 --- a/tests/components/tuya/test.esp8266-ard.yaml +++ b/tests/components/tuya/test.esp8266-ard.yaml @@ -1,78 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + status_pin: GPIO12 -uart: - - id: uart_tuya - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -tuya: - status_pin: - number: 16 - inverted: true - on_datapoint_update: - - sensor_datapoint: 6 - datapoint_type: raw - then: - - logger.log: Datapoint 6 updated - -binary_sensor: - - platform: tuya - id: tuya_binary_sensor - sensor_datapoint: 1 - -climate: - - platform: tuya - id: tuya_climate - switch_datapoint: 1 - target_temperature_datapoint: 3 - current_temperature_multiplier: 0.5 - target_temperature_multiplier: 0.5 - reports_fahrenheit: true - -cover: - - platform: tuya - id: tuya_cover - position_datapoint: 2 - -light: - - platform: tuya - id: tuya_light - switch_datapoint: 1 - dimmer_datapoint: 2 - min_value_datapoint: 3 - color_temperature_datapoint: 4 - min_value: 1 - max_value: 100 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - gamma_correct: 1 - -number: - - platform: tuya - id: tuya_number - number_datapoint: 102 - min_value: 0 - max_value: 17 - step: 1 - -select: - - platform: tuya - id: tuya_select - enum_datapoint: 42 - options: - 0: Internal - 1: Floor - 2: Both - -sensor: - - platform: tuya - id: tuya_sensor - sensor_datapoint: 1 - -switch: - - platform: tuya - id: tuya_switch - switch_datapoint: 1 +<<: !include common.yaml diff --git a/tests/components/tuya/test.rp2040-ard.yaml b/tests/components/tuya/test.rp2040-ard.yaml index 4892e807b1..11d46ed50e 100644 --- a/tests/components/tuya/test.rp2040-ard.yaml +++ b/tests/components/tuya/test.rp2040-ard.yaml @@ -1,78 +1,6 @@ -wifi: - ssid: MySSID - password: password1 +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + status_pin: GPIO12 -uart: - - id: uart_tuya - tx_pin: 4 - rx_pin: 5 - baud_rate: 9600 - -tuya: - status_pin: - number: 6 - inverted: true - on_datapoint_update: - - sensor_datapoint: 6 - datapoint_type: raw - then: - - logger.log: Datapoint 6 updated - -binary_sensor: - - platform: tuya - id: tuya_binary_sensor - sensor_datapoint: 1 - -climate: - - platform: tuya - id: tuya_climate - switch_datapoint: 1 - target_temperature_datapoint: 3 - current_temperature_multiplier: 0.5 - target_temperature_multiplier: 0.5 - reports_fahrenheit: true - -cover: - - platform: tuya - id: tuya_cover - position_datapoint: 2 - -light: - - platform: tuya - id: tuya_light - switch_datapoint: 1 - dimmer_datapoint: 2 - min_value_datapoint: 3 - color_temperature_datapoint: 4 - min_value: 1 - max_value: 100 - cold_white_color_temperature: 153 mireds - warm_white_color_temperature: 500 mireds - gamma_correct: 1 - -number: - - platform: tuya - id: tuya_number - number_datapoint: 102 - min_value: 0 - max_value: 17 - step: 1 - -select: - - platform: tuya - id: tuya_select - enum_datapoint: 42 - options: - 0: Internal - 1: Floor - 2: Both - -sensor: - - platform: tuya - id: tuya_sensor - sensor_datapoint: 1 - -switch: - - platform: tuya - id: tuya_switch - switch_datapoint: 1 +<<: !include common.yaml From 0cd3af2fcd475d4e724669de10b2ec0710ca60ab Mon Sep 17 00:00:00 2001 From: guillempages Date: Mon, 10 Feb 2025 01:17:29 +0100 Subject: [PATCH 0979/1052] [online_image]Pin specific version of JPEG library (#8217) --- esphome/components/online_image/__init__.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/online_image/__init__.py b/esphome/components/online_image/__init__.py index c476270571..6b69bc240b 100644 --- a/esphome/components/online_image/__init__.py +++ b/esphome/components/online_image/__init__.py @@ -66,7 +66,7 @@ class JPEGFormat(Format): def actions(self): cg.add_define("USE_ONLINE_IMAGE_JPEG_SUPPORT") - cg.add_library("JPEGDEC", "1.6.2", "https://github.com/bitbank2/JPEGDEC") + cg.add_library("JPEGDEC", None, "https://github.com/bitbank2/JPEGDEC#ca1e0f2") class PNGFormat(Format): diff --git a/platformio.ini b/platformio.ini index cf11139b73..4153310480 100644 --- a/platformio.ini +++ b/platformio.ini @@ -42,7 +42,7 @@ lib_deps = pavlodn/HaierProtocol@0.9.31 ; haier kikuchan98/pngle@1.0.2 ; online_image ; Using the repository directly, otherwise ESP-IDF can't use the library - https://github.com/bitbank2/JPEGDEC.git#1.6.2 ; online_image + https://github.com/bitbank2/JPEGDEC.git#ca1e0f2 ; online_image ; This is using the repository until a new release is published to PlatformIO https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library lvgl/lvgl@8.4.0 ; lvgl From ff7d232ee6ed86b69e7abfa139a8d83af07c620b Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:53:26 +1100 Subject: [PATCH 0980/1052] [logger] Add runtime level select (#8222) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/logger/__init__.py | 48 ++++++++++++++++--- esphome/components/logger/logger.cpp | 27 ++++++----- esphome/components/logger/logger.h | 18 ++++--- esphome/components/logger/select/__init__.py | 29 +++++++++++ .../logger/select/logger_level_select.cpp | 27 +++++++++++ .../logger/select/logger_level_select.h | 15 ++++++ esphome/core/defines.h | 3 ++ .../logger/common-default_uart.yaml | 12 ++++- 9 files changed, 154 insertions(+), 26 deletions(-) create mode 100644 esphome/components/logger/select/__init__.py create mode 100644 esphome/components/logger/select/logger_level_select.cpp create mode 100644 esphome/components/logger/select/logger_level_select.h diff --git a/CODEOWNERS b/CODEOWNERS index eab02efffb..d4b3d7eff9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -242,6 +242,7 @@ esphome/components/lightwaverf/* @max246 esphome/components/lilygo_t5_47/touchscreen/* @jesserockz esphome/components/lock/* @esphome/core esphome/components/logger/* @esphome/core +esphome/components/logger/select/* @clydebarrow esphome/components/ltr390/* @latonita @sjtrny esphome/components/ltr501/* @latonita esphome/components/ltr_als_ps/* @latonita diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 6e92777058..a89bf95c77 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -35,7 +35,7 @@ from esphome.const import ( PLATFORM_RP2040, PLATFORM_RTL87XX, ) -from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority +from esphome.core import CORE, Lambda, coroutine_with_priority CODEOWNERS = ["@esphome/core"] logger_ns = cg.esphome_ns.namespace("logger") @@ -77,6 +77,9 @@ USB_SERIAL_JTAG = "USB_SERIAL_JTAG" USB_CDC = "USB_CDC" DEFAULT = "DEFAULT" +CONF_INITIAL_LEVEL = "initial_level" +CONF_LOGGER_ID = "logger_id" + UART_SELECTION_ESP32 = { VARIANT_ESP32: [UART0, UART1, UART2], VARIANT_ESP32S2: [UART0, UART1, USB_CDC], @@ -154,11 +157,11 @@ def uart_selection(value): def validate_local_no_higher_than_global(value): - global_level = value.get(CONF_LEVEL, "DEBUG") + global_level = LOG_LEVEL_SEVERITY.index(value[CONF_LEVEL]) for tag, level in value.get(CONF_LOGS, {}).items(): - if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level): - raise EsphomeError( - f"The local log level {level} for {tag} must be less severe than the global log level {global_level}." + if LOG_LEVEL_SEVERITY.index(level) > global_level: + raise cv.Invalid( + f"The configured log level for {tag} ({level}) must be no more severe than the global log level {value[CONF_LEVEL]}." ) return value @@ -209,6 +212,7 @@ CONFIG_SCHEMA = cv.All( cv.string: is_log_level, } ), + cv.Optional(CONF_INITIAL_LEVEL): is_log_level, cv.Optional(CONF_ON_MESSAGE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoggerMessageTrigger), @@ -227,7 +231,14 @@ CONFIG_SCHEMA = cv.All( @coroutine_with_priority(90.0) async def to_code(config): baud_rate = config[CONF_BAUD_RATE] - log = cg.new_Pvariable(config[CONF_ID], baud_rate, config[CONF_TX_BUFFER_SIZE]) + level = config[CONF_LEVEL] + initial_level = LOG_LEVELS[config.get(CONF_INITIAL_LEVEL, level)] + log = cg.new_Pvariable( + config[CONF_ID], + baud_rate, + config[CONF_TX_BUFFER_SIZE], + ) + cg.add(log.set_log_level(initial_level)) if CONF_HARDWARE_UART in config: cg.add( log.set_uart_selection( @@ -239,7 +250,6 @@ async def to_code(config): for tag, level in config[CONF_LOGS].items(): cg.add(log.set_log_level(tag, LOG_LEVELS[level])) - level = config[CONF_LEVEL] cg.add_define("USE_LOGGER") this_severity = LOG_LEVEL_SEVERITY.index(level) cg.add_build_flag(f"-DESPHOME_LOG_LEVEL={LOG_LEVELS[level]}") @@ -367,3 +377,27 @@ async def logger_log_action_to_code(config, action_id, template_arg, args): lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void) return cg.new_Pvariable(action_id, template_arg, lambda_) + + +@automation.register_action( + "logger.set_level", + LambdaAction, + cv.maybe_simple_value( + { + cv.GenerateID(CONF_LOGGER_ID): cv.use_id(Logger), + cv.Required(CONF_LEVEL): is_log_level, + cv.Optional(CONF_TAG): cv.string, + }, + key=CONF_LEVEL, + ), +) +async def logger_set_level_to_code(config, action_id, template_arg, args): + level = LOG_LEVELS[config[CONF_LEVEL]] + logger = await cg.get_variable(config[CONF_LOGGER_ID]) + if tag := config.get(CONF_TAG): + text = str(cg.statement(logger.set_log_level(tag, level))) + else: + text = str(cg.statement(logger.set_log_level(level))) + + lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void) + return cg.new_Pvariable(action_id, template_arg, lambda_) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 36934c7459..79fc4cf499 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -105,12 +105,9 @@ int HOT Logger::level_for(const char *tag) { // Uses std::vector<> for low memory footprint, though the vector // could be sorted to minimize lookup times. This feature isn't used that // much anyway so it doesn't matter too much. - for (auto &it : this->log_levels_) { - if (it.tag == tag) { - return it.level; - } - } - return ESPHOME_LOG_LEVEL; + if (this->log_levels_.count(tag) != 0) + return this->log_levels_[tag]; + return this->current_level_; } void HOT Logger::log_message_(int level, const char *tag, int offset) { @@ -167,9 +164,7 @@ void Logger::loop() { #endif void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; } -void Logger::set_log_level(const std::string &tag, int log_level) { - this->log_levels_.push_back(LogLevelOverride{tag, log_level}); -} +void Logger::set_log_level(const std::string &tag, int log_level) { this->log_levels_[tag] = log_level; } #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) UARTSelection Logger::get_uart() const { return this->uart_; } @@ -183,18 +178,28 @@ const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DE void Logger::dump_config() { ESP_LOGCONFIG(TAG, "Logger:"); - ESP_LOGCONFIG(TAG, " Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); + ESP_LOGCONFIG(TAG, " Max Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); + ESP_LOGCONFIG(TAG, " Initial Level: %s", 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_()); #endif for (auto &it : this->log_levels_) { - ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.tag.c_str(), LOG_LEVELS[it.level]); + ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_LEVELS[it.second]); } } void Logger::write_footer_() { this->write_to_buffer_(ESPHOME_LOG_RESET_COLOR, strlen(ESPHOME_LOG_RESET_COLOR)); } +void Logger::set_log_level(int level) { + if (level > ESPHOME_LOG_LEVEL) { + level = ESPHOME_LOG_LEVEL; + ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); + } + this->current_level_ = level; + this->level_callback_.call(level); +} + Logger *global_logger = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace logger diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index b55cfb0771..c4c873e020 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -1,11 +1,12 @@ #pragma once #include -#include +#include #include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #ifdef USE_ARDUINO #if defined(USE_ESP8266) || defined(USE_ESP32) @@ -74,8 +75,11 @@ class Logger : public Component { UARTSelection get_uart() const; #endif + /// Set the default log level for this logger. + void set_log_level(int level); /// Set the log level of the specified tag. void set_log_level(const std::string &tag, int log_level); + int get_log_level() { return this->current_level_; } // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) @@ -88,6 +92,9 @@ class Logger : public Component { /// Register a callback that will be called for every log message sent void add_on_log_callback(std::function &&callback); + // add a listener for log level changes + void add_listener(std::function &&callback) { this->level_callback_.add(std::move(callback)); } + float get_setup_priority() const override; void log_vprintf_(int level, const char *tag, int line, const char *format, va_list args); // NOLINT @@ -159,17 +166,14 @@ class Logger : public Component { #ifdef USE_ESP_IDF uart_port_t uart_num_; #endif - struct LogLevelOverride { - std::string tag; - int level; - }; - std::vector log_levels_; + std::map log_levels_{}; CallbackManager log_callback_{}; + int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE}; /// Prevents recursive log calls, if true a log message is already being processed. bool recursion_guard_ = false; void *main_task_ = nullptr; + CallbackManager level_callback_{}; }; - extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) class LoggerMessageTrigger : public Trigger { diff --git a/esphome/components/logger/select/__init__.py b/esphome/components/logger/select/__init__.py new file mode 100644 index 0000000000..b1fc881537 --- /dev/null +++ b/esphome/components/logger/select/__init__.py @@ -0,0 +1,29 @@ +import esphome.codegen as cg +from esphome.components import select +import esphome.config_validation as cv +from esphome.const import CONF_LEVEL, CONF_LOGGER, ENTITY_CATEGORY_CONFIG, ICON_BUG +from esphome.core import CORE +from esphome.cpp_helpers import register_component, register_parented + +from .. import CONF_LOGGER_ID, LOG_LEVEL_SEVERITY, Logger, logger_ns + +CODEOWNERS = ["@clydebarrow"] + +LoggerLevelSelect = logger_ns.class_("LoggerLevelSelect", select.Select, cg.Component) + +CONFIG_SCHEMA = select.select_schema( + LoggerLevelSelect, icon=ICON_BUG, entity_category=ENTITY_CATEGORY_CONFIG +).extend( + { + cv.GenerateID(CONF_LOGGER_ID): cv.use_id(Logger), + } +) + + +async def to_code(config): + levels = LOG_LEVEL_SEVERITY + index = levels.index(CORE.config[CONF_LOGGER][CONF_LEVEL]) + levels = levels[: index + 1] + var = await select.new_select(config, options=levels) + await register_parented(var, config[CONF_LOGGER_ID]) + await register_component(var, config) diff --git a/esphome/components/logger/select/logger_level_select.cpp b/esphome/components/logger/select/logger_level_select.cpp new file mode 100644 index 0000000000..b71a6e02a2 --- /dev/null +++ b/esphome/components/logger/select/logger_level_select.cpp @@ -0,0 +1,27 @@ +#include "logger_level_select.h" + +namespace esphome { +namespace logger { + +void LoggerLevelSelect::publish_state(int level) { + auto value = this->at(level); + if (!value) { + return; + } + Select::publish_state(value.value()); +} + +void LoggerLevelSelect::setup() { + this->parent_->add_listener([this](int level) { this->publish_state(level); }); + this->publish_state(this->parent_->get_log_level()); +} + +void LoggerLevelSelect::control(const std::string &value) { + auto level = this->index_of(value); + if (!level) + return; + this->parent_->set_log_level(level.value()); +} + +} // namespace logger +} // namespace esphome diff --git a/esphome/components/logger/select/logger_level_select.h b/esphome/components/logger/select/logger_level_select.h new file mode 100644 index 0000000000..2c92c84d13 --- /dev/null +++ b/esphome/components/logger/select/logger_level_select.h @@ -0,0 +1,15 @@ +#pragma once + +#include "esphome/components/select/select.h" +#include "esphome/core/component.h" +#include "esphome/components/logger/logger.h" +namespace esphome { +namespace logger { +class LoggerLevelSelect : public Component, public select::Select, public Parented { + public: + void publish_state(int level); + void setup() override; + void control(const std::string &value) override; +}; +} // namespace logger +} // namespace esphome diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 8407391bce..dc0ac3c1e8 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -14,6 +14,9 @@ #define ESPHOME_PROJECT_VERSION_30 "v2" #define ESPHOME_VARIANT "ESP32" +// logger +#define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_VERY_VERBOSE + // Feature flags #define USE_ALARM_CONTROL_PANEL #define USE_AUDIO_FLAC_SUPPORT diff --git a/tests/components/logger/common-default_uart.yaml b/tests/components/logger/common-default_uart.yaml index 70b485daac..e8b56043eb 100644 --- a/tests/components/logger/common-default_uart.yaml +++ b/tests/components/logger/common-default_uart.yaml @@ -1,7 +1,17 @@ esphome: on_boot: then: - - logger.log: Hello world + - logger.log: + level: warn + format: "Warning: Logger level is %d" + args: [id(logger_id).get_log_level()] + - logger.set_level: WARN logger: + id: logger_id level: DEBUG + initial_level: INFO + +select: + - platform: logger + name: Logger Level From 45b8810ab8a888a1cf43aae308770d159a87e25f Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Sun, 9 Feb 2025 21:55:16 -0500 Subject: [PATCH 0981/1052] [online_image] Set Accept header (#8216) --- .../components/online_image/online_image.cpp | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index b08c14b721..da0e88a904 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -100,7 +100,35 @@ void OnlineImage::update() { } ESP_LOGI(TAG, "Updating image %s", this->url_.c_str()); - this->downloader_ = this->parent_->get(this->url_); + std::list headers = {}; + + http_request::Header accept_header; + accept_header.name = "Accept"; + std::string accept_mime_type; + switch (this->format_) { +#ifdef USE_ONLINE_IMAGE_BMP_SUPPORT + case ImageFormat::BMP: + accept_mime_type = "image/bmp"; + break; +#endif // ONLINE_IMAGE_BMP_SUPPORT +#ifdef USE_ONLINE_IMAGE_JPEG_SUPPORT + case ImageFormat::JPEG: + accept_mime_type = "image/jpeg"; + break; +#endif // USE_ONLINE_IMAGE_JPEG_SUPPORT +#ifdef USE_ONLINE_IMAGE_PNG_SUPPORT + case ImageFormat::PNG: + accept_mime_type = "image/png"; + break; +#endif // ONLINE_IMAGE_PNG_SUPPORT + default: + accept_mime_type = "image/*"; + } + accept_header.value = (accept_mime_type + ",*/*;q=0.8").c_str(); + + headers.push_back(accept_header); + + this->downloader_ = this->parent_->get(this->url_, headers); if (this->downloader_ == nullptr) { ESP_LOGE(TAG, "Download failed."); From 66c35a943244c3b279dc79741cb4e47422f39f84 Mon Sep 17 00:00:00 2001 From: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Date: Mon, 10 Feb 2025 14:46:05 +1100 Subject: [PATCH 0982/1052] [waveshare_epaper] Rationalise and complete tests (#8221) --- .../components/waveshare_epaper/display.py | 4 +- tests/components/waveshare_epaper/common.yaml | 765 ++++++++++++++++-- .../waveshare_epaper/test.esp32-ard.yaml | 12 +- .../waveshare_epaper/test.esp32-c3-ard.yaml | 8 +- .../waveshare_epaper/test.esp32-c3-idf.yaml | 8 +- .../waveshare_epaper/test.esp32-idf.yaml | 8 +- .../waveshare_epaper/test.esp8266-ard.yaml | 8 +- .../waveshare_epaper/test.rp2040-ard.yaml | 6 +- 8 files changed, 709 insertions(+), 110 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index d5240b2674..3a5151682f 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -120,7 +120,7 @@ MODELS = { "2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74), "2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), - "gdew029t5": ("c", GDEW029T5), + "gdew029t5": ("b", GDEW029T5), "2.70in": ("b", WaveshareEPaper2P7In), "2.70in-b": ("b", WaveshareEPaper2P7InB), "2.70in-bv2": ("b", WaveshareEPaper2P7InBV2), @@ -143,7 +143,7 @@ MODELS = { "7.50in-hd-b": ("b", WaveshareEPaper7P5InHDB), "2.13in-ttgo-dke": ("c", WaveshareEPaper2P13InDKE), "2.13inv3": ("c", WaveshareEPaper2P13InV3), - "1.54in-m5coreink-m09": ("c", GDEW0154M09), + "1.54in-m5coreink-m09": ("b", GDEW0154M09), "13.3in-k": ("b", WaveshareEPaper13P3InK), } diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index 92c443908e..ff9ddb955f 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -4,8 +4,150 @@ spi: mosi_pin: ${mosi_pin} display: + # 1.54 inch displays - platform: waveshare_epaper + id: epd_1_54 + model: 1.54in + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_1_54v2 + model: 1.54inv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_1_54v2b + model: 1.54inv2-b + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_1_54m09 + model: 1.54in-m5coreink-m09 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + # 2.13 inch displays + - platform: waveshare_epaper + id: epd_2_13 + model: 2.13in + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_13v2 + model: 2.13inv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_13ttgo + model: 2.13in-ttgo + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_13ttgo_b1 model: 2.13in-ttgo-b1 + spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -21,8 +163,31 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + id: epd_2_13ttgo_b73 + model: 2.13in-ttgo-b73 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_13ttgo_b74 model: 2.13in-ttgo-b74 + spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -38,8 +203,129 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + id: epd_2_13dke + model: 2.13in-ttgo-dke + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_13v3 + model: 2.13inv3 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + # 2.7 inch displays + - platform: waveshare_epaper + id: epd_2_70 + model: 2.70in + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_70b + model: 2.70in-b + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_70bv2 + model: 2.70in-bv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_70v2 + model: 2.70inv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + # 2.9 inch displays + - platform: waveshare_epaper + id: epd_2_90 model: 2.90in + spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -56,8 +342,400 @@ display: reset_duration: 200ms lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_90v2 + model: 2.90inv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + reset_duration: 200ms + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_90b + model: 2.90in-b + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_90bv3 + model: 2.90in-bv3 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_90v2r2 + model: 2.90inv2-r2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_2_90dke + model: 2.90in-dke + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + reset_duration: 200ms + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_gdew029t5 + model: gdew029t5 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + # 4.2 inch displays + - platform: waveshare_epaper + id: epd_4_20 + model: 4.20in + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_4_20bv2 + model: 4.20in-bv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + # 5.83 inch displays + - platform: waveshare_epaper + id: epd_5_83 + model: 5.83in + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_5_83v2 + model: 5.83inv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + # 7.5 inch displays + - platform: waveshare_epaper + id: epd_7_50 + model: 7.50in + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_7_50bv2 + model: 7.50in-bv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_7_50bv3 + model: 7.50in-bv3 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_7_50bv3_bwr + model: 7.50in-bv3-bwr + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_7_50bc + model: 7.50in-bc + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_7_50v2 + model: 7.50inv2 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_7_50v2alt + model: 7.50inv2alt + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + id: epd_7_50hdb + model: 7.50in-hd-b + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + # 13.3 inch displays + - platform: waveshare_epaper + id: epd_13_3k + model: 13.3in-k + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + + - platform: waveshare_epaper + model: 2.90in + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + reset_duration: 200ms + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper model: 2.90inv2 + spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -73,25 +751,10 @@ display: full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.90in-dke - cs_pin: - allow_other_uses: true - number: ${cs_pin} - dc_pin: - allow_other_uses: true - number: ${dc_pin} - busy_pin: - allow_other_uses: true - number: ${busy_pin} - reset_pin: - allow_other_uses: true - number: ${reset_pin} - full_update_every: 1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper model: 2.70in-b + spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} @@ -106,74 +769,10 @@ display: number: ${reset_pin} lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper model: 2.70in-bv2 - cs_pin: - allow_other_uses: true - number: ${cs_pin} - dc_pin: - allow_other_uses: true - number: ${dc_pin} - busy_pin: - allow_other_uses: true - number: ${busy_pin} - reset_pin: - allow_other_uses: true - number: ${reset_pin} - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 1.54in-m5coreink-m09 - cs_pin: - allow_other_uses: true - number: ${cs_pin} - dc_pin: - allow_other_uses: true - number: ${dc_pin} - busy_pin: - allow_other_uses: true - number: ${busy_pin} - reset_pin: - allow_other_uses: true - number: ${reset_pin} - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.13inv3 - cs_pin: - allow_other_uses: true - number: ${cs_pin} - dc_pin: - allow_other_uses: true - number: ${dc_pin} - busy_pin: - allow_other_uses: true - number: ${busy_pin} - reset_pin: - allow_other_uses: true - number: ${reset_pin} - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 2.13inv2 - cs_pin: - allow_other_uses: true - number: ${cs_pin} - dc_pin: - allow_other_uses: true - number: ${dc_pin} - busy_pin: - allow_other_uses: true - number: ${busy_pin} - reset_pin: - allow_other_uses: true - number: ${reset_pin} - full_update_every: 30 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: waveshare_epaper - model: 7.50in-bv3-bwr + spi_id: spi_waveshare_epaper cs_pin: allow_other_uses: true number: ${cs_pin} diff --git a/tests/components/waveshare_epaper/test.esp32-ard.yaml b/tests/components/waveshare_epaper/test.esp32-ard.yaml index c658ea91ee..c36345b984 100644 --- a/tests/components/waveshare_epaper/test.esp32-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp32-ard.yaml @@ -1,9 +1,9 @@ substitutions: - clk_pin: GPIO16 - mosi_pin: GPIO17 - cs_pin: GPIO18 - dc_pin: GPIO19 - busy_pin: GPIO13 - reset_pin: GPIO14 + clk_pin: GPIO18 + mosi_pin: GPIO23 + cs_pin: GPIO25 + dc_pin: GPIO26 + busy_pin: GPIO27 + reset_pin: GPIO32 <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml b/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml index e2873f0fe9..4cb230f6f2 100644 --- a/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp32-c3-ard.yaml @@ -1,9 +1,9 @@ substitutions: clk_pin: GPIO6 mosi_pin: GPIO7 - cs_pin: GPIO0 - dc_pin: GPIO5 - busy_pin: GPIO3 - reset_pin: GPIO4 + cs_pin: GPIO4 + dc_pin: GPIO1 + busy_pin: GPIO2 + reset_pin: GPIO3 <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml index e2873f0fe9..4cb230f6f2 100644 --- a/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-c3-idf.yaml @@ -1,9 +1,9 @@ substitutions: clk_pin: GPIO6 mosi_pin: GPIO7 - cs_pin: GPIO0 - dc_pin: GPIO5 - busy_pin: GPIO3 - reset_pin: GPIO4 + cs_pin: GPIO4 + dc_pin: GPIO1 + busy_pin: GPIO2 + reset_pin: GPIO3 <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp32-idf.yaml b/tests/components/waveshare_epaper/test.esp32-idf.yaml index c658ea91ee..9e8b2fdec8 100644 --- a/tests/components/waveshare_epaper/test.esp32-idf.yaml +++ b/tests/components/waveshare_epaper/test.esp32-idf.yaml @@ -1,9 +1,9 @@ substitutions: clk_pin: GPIO16 mosi_pin: GPIO17 - cs_pin: GPIO18 - dc_pin: GPIO19 - busy_pin: GPIO13 - reset_pin: GPIO14 + cs_pin: GPIO4 + dc_pin: GPIO5 + busy_pin: GPIO18 + reset_pin: GPIO19 <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.esp8266-ard.yaml b/tests/components/waveshare_epaper/test.esp8266-ard.yaml index bc178b7009..ee8199bcc0 100644 --- a/tests/components/waveshare_epaper/test.esp8266-ard.yaml +++ b/tests/components/waveshare_epaper/test.esp8266-ard.yaml @@ -1,9 +1,9 @@ substitutions: clk_pin: GPIO14 mosi_pin: GPIO13 - cs_pin: GPIO15 - dc_pin: GPIO12 - busy_pin: GPIO5 - reset_pin: GPIO4 + cs_pin: GPIO4 + dc_pin: GPIO5 + busy_pin: GPIO15 + reset_pin: GPIO16 <<: !include common.yaml diff --git a/tests/components/waveshare_epaper/test.rp2040-ard.yaml b/tests/components/waveshare_epaper/test.rp2040-ard.yaml index 801b0b51c5..e92f6d421d 100644 --- a/tests/components/waveshare_epaper/test.rp2040-ard.yaml +++ b/tests/components/waveshare_epaper/test.rp2040-ard.yaml @@ -2,8 +2,8 @@ substitutions: clk_pin: GPIO2 mosi_pin: GPIO3 cs_pin: GPIO5 - dc_pin: GPIO4 - busy_pin: GPIO0 - reset_pin: GPIO1 + dc_pin: GPIO6 + busy_pin: GPIO7 + reset_pin: GPIO8 <<: !include common.yaml From fd24b1423c5957ee4c648904aa2d1a87467c1b27 Mon Sep 17 00:00:00 2001 From: Awesome Walrus <74941879+QRPp@users.noreply.github.com> Date: Mon, 10 Feb 2025 03:54:37 +0000 Subject: [PATCH 0983/1052] Fix pref conflict of WiFi creds and fast_connect (#8219) --- esphome/components/wifi/wifi_component.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index eef962b8c4..4a50553305 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -66,7 +66,7 @@ void WiFiComponent::start() { this->pref_ = global_preferences->make_preference(hash, true); if (this->fast_connect_) { - this->fast_connect_pref_ = global_preferences->make_preference(hash, false); + this->fast_connect_pref_ = global_preferences->make_preference(hash + 1, false); } SavedWifiSettings save{}; From 0d13e2040dbf0e488544fd357e38f88129155b67 Mon Sep 17 00:00:00 2001 From: Stefan Rado <628587+kroimon@users.noreply.github.com> Date: Mon, 10 Feb 2025 05:12:46 +0100 Subject: [PATCH 0984/1052] Don't activate venv in devcontainer (#8128) --- .pre-commit-config.yaml | 2 +- script/{run-in-env => run-in-env.py} | 36 +++++++++++++++++----------- 2 files changed, 23 insertions(+), 15 deletions(-) rename script/{run-in-env => run-in-env.py} (63%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index adf0ac6fc2..212d822ff8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,6 +45,6 @@ repos: hooks: - id: pylint name: pylint - entry: python script/run-in-env pylint + entry: python3 script/run-in-env.py pylint language: system types: [python] diff --git a/script/run-in-env b/script/run-in-env.py similarity index 63% rename from script/run-in-env rename to script/run-in-env.py index 57bfddccf7..57121266be 100644 --- a/script/run-in-env +++ b/script/run-in-env.py @@ -7,6 +7,13 @@ import sys def find_and_activate_virtualenv(): + if ( + ("VIRTUAL_ENV" in os.environ) + or os.environ.get("DEVCONTAINER", False) + or os.environ.get("ESPHOME_NO_VENV", False) + ): + return + try: # Get the top-level directory of the git repository my_path = subprocess.check_output( @@ -17,7 +24,7 @@ def find_and_activate_virtualenv(): "Error: Not a git repository or unable to determine the top-level directory.", file=sys.stderr, ) - sys.exit(1) + return # Check for virtual environments for venv in ["venv", ".venv", "."]: @@ -29,25 +36,26 @@ def find_and_activate_virtualenv(): ) if activate_path.exists(): # Activate the virtual environment by updating PATH - env = os.environ.copy() venv_bin_dir = activate_path.parent - env["PATH"] = f"{venv_bin_dir}{os.pathsep}{env['PATH']}" - env["VIRTUAL_ENV"] = str(venv_bin_dir.parent) + os.environ["PATH"] = f"{venv_bin_dir}{os.pathsep}{os.environ['PATH']}" + os.environ["VIRTUAL_ENV"] = str(venv_bin_dir.parent) print(f"Activated virtual environment: {venv_bin_dir.parent}") - - # Execute the remaining arguments in the new environment - if len(sys.argv) > 1: - subprocess.run(sys.argv[1:], env=env, check=False) - else: - print( - "No command provided to run in the virtual environment.", - file=sys.stderr, - ) return print("No virtual environment found.", file=sys.stderr) - sys.exit(1) + + +def run_command(): + # Execute the remaining arguments in the new environment + if len(sys.argv) > 1: + subprocess.run(sys.argv[1:], check=False) + else: + print( + "No command provided to run in the virtual environment.", + file=sys.stderr, + ) if __name__ == "__main__": find_and_activate_virtualenv() + run_command() From 1ab1768b6a21b0dd270706288a418f68df1fdb8c Mon Sep 17 00:00:00 2001 From: Igor Novgorodov Date: Mon, 10 Feb 2025 05:32:54 +0100 Subject: [PATCH 0985/1052] Add ADC sampling method option (#8131) Co-authored-by: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> --- esphome/components/adc/__init__.py | 8 +++ esphome/components/adc/adc_sensor.h | 17 ++++++ esphome/components/adc/adc_sensor_common.cpp | 55 +++++++++++++++++++ esphome/components/adc/adc_sensor_esp32.cpp | 13 +++-- esphome/components/adc/adc_sensor_esp8266.cpp | 16 ++++-- .../components/adc/adc_sensor_libretiny.cpp | 17 ++++-- esphome/components/adc/adc_sensor_rp2040.cpp | 23 ++++---- esphome/components/adc/sensor.py | 12 +++- tests/component_tests/sensor/test_sensor.yaml | 1 + 9 files changed, 132 insertions(+), 30 deletions(-) diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index d8d21523b9..be420475fb 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -36,6 +36,14 @@ ATTENUATION_MODES = { "auto": "auto", } +sampling_mode = adc_ns.enum("SamplingMode", is_class=True) + +SAMPLING_MODES = { + "avg": sampling_mode.AVG, + "min": sampling_mode.MIN, + "max": sampling_mode.MAX, +} + adc1_channel_t = cg.global_ns.enum("adc1_channel_t") adc2_channel_t = cg.global_ns.enum("adc2_channel_t") diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 7a3e1c8da7..62f2461245 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -28,6 +28,21 @@ static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_11; #endif #endif // USE_ESP32 +enum class SamplingMode : uint8_t { AVG = 0, MIN = 1, MAX = 2 }; +const LogString *sampling_mode_to_str(SamplingMode mode); + +class Aggregator { + public: + void add_sample(uint32_t value); + uint32_t aggregate(); + Aggregator(SamplingMode mode); + + protected: + SamplingMode mode_{SamplingMode::AVG}; + uint32_t aggr_{0}; + uint32_t samples_{0}; +}; + class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler { public: #ifdef USE_ESP32 @@ -54,6 +69,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; } void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; } void set_sample_count(uint8_t sample_count); + void set_sampling_mode(SamplingMode sampling_mode); float sample() override; #ifdef USE_ESP8266 @@ -68,6 +84,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage InternalGPIOPin *pin_; bool output_raw_{false}; uint8_t sample_count_{1}; + SamplingMode sampling_mode_{SamplingMode::AVG}; #ifdef USE_RP2040 bool is_temperature_{false}; diff --git a/esphome/components/adc/adc_sensor_common.cpp b/esphome/components/adc/adc_sensor_common.cpp index 2dccd55fcd..c7509c7c7a 100644 --- a/esphome/components/adc/adc_sensor_common.cpp +++ b/esphome/components/adc/adc_sensor_common.cpp @@ -6,6 +6,59 @@ namespace adc { static const char *const TAG = "adc.common"; +const LogString *sampling_mode_to_str(SamplingMode mode) { + switch (mode) { + case SamplingMode::AVG: + return LOG_STR("average"); + case SamplingMode::MIN: + return LOG_STR("minimum"); + case SamplingMode::MAX: + return LOG_STR("maximum"); + } + return LOG_STR("unknown"); +} + +Aggregator::Aggregator(SamplingMode mode) { + this->mode_ = mode; + // set to max uint if mode is "min" + if (mode == SamplingMode::MIN) { + this->aggr_ = UINT32_MAX; + } +} + +void Aggregator::add_sample(uint32_t value) { + this->samples_ += 1; + + switch (this->mode_) { + case SamplingMode::AVG: + this->aggr_ += value; + break; + + case SamplingMode::MIN: + if (value < this->aggr_) { + this->aggr_ = value; + } + break; + + case SamplingMode::MAX: + if (value > this->aggr_) { + this->aggr_ = value; + } + } +} + +uint32_t Aggregator::aggregate() { + if (this->mode_ == SamplingMode::AVG) { + if (this->samples_ == 0) { + return this->aggr_; + } + + return (this->aggr_ + (this->samples_ >> 1)) / this->samples_; // NOLINT(clang-analyzer-core.DivideZero) + } + + return this->aggr_; +} + void ADCSensor::update() { float value_v = this->sample(); ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v); @@ -18,6 +71,8 @@ void ADCSensor::set_sample_count(uint8_t sample_count) { } } +void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; } + float ADCSensor::get_setup_priority() const { return setup_priority::DATA; } } // namespace adc diff --git a/esphome/components/adc/adc_sensor_esp32.cpp b/esphome/components/adc/adc_sensor_esp32.cpp index 24e3750091..0f1d802937 100644 --- a/esphome/components/adc/adc_sensor_esp32.cpp +++ b/esphome/components/adc/adc_sensor_esp32.cpp @@ -78,12 +78,14 @@ void ADCSensor::dump_config() { } } ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_); + ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_))); LOG_UPDATE_INTERVAL(this); } float ADCSensor::sample() { if (!this->autorange_) { - uint32_t sum = 0; + auto aggr = Aggregator(this->sampling_mode_); + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { int raw = -1; if (this->channel1_ != ADC1_CHANNEL_MAX) { @@ -94,13 +96,14 @@ float ADCSensor::sample() { if (raw == -1) { return NAN; } - sum += raw; + + aggr.add_sample(raw); } - sum = (sum + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) if (this->output_raw_) { - return sum; + return aggr.aggregate(); } - uint32_t mv = esp_adc_cal_raw_to_voltage(sum, &this->cal_characteristics_[(int32_t) this->attenuation_]); + uint32_t mv = + esp_adc_cal_raw_to_voltage(aggr.aggregate(), &this->cal_characteristics_[(int32_t) this->attenuation_]); return mv / 1000.0f; } diff --git a/esphome/components/adc/adc_sensor_esp8266.cpp b/esphome/components/adc/adc_sensor_esp8266.cpp index c9b6f8b652..9a12009abc 100644 --- a/esphome/components/adc/adc_sensor_esp8266.cpp +++ b/esphome/components/adc/adc_sensor_esp8266.cpp @@ -31,23 +31,27 @@ 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_))); LOG_UPDATE_INTERVAL(this); } float ADCSensor::sample() { - uint32_t raw = 0; + auto aggr = Aggregator(this->sampling_mode_); + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { + uint32_t raw = 0; #ifdef USE_ADC_SENSOR_VCC - raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance) + raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance) #else - raw += analogRead(this->pin_->get_pin()); // NOLINT + raw = analogRead(this->pin_->get_pin()); // NOLINT #endif // USE_ADC_SENSOR_VCC + aggr.add_sample(raw); } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + if (this->output_raw_) { - return raw; + return aggr.aggregate(); } - return raw / 1024.0f; + return aggr.aggregate() / 1024.0f; } std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; } diff --git a/esphome/components/adc/adc_sensor_libretiny.cpp b/esphome/components/adc/adc_sensor_libretiny.cpp index cd04477b3f..9e75ed414c 100644 --- a/esphome/components/adc/adc_sensor_libretiny.cpp +++ b/esphome/components/adc/adc_sensor_libretiny.cpp @@ -23,23 +23,28 @@ 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_))); LOG_UPDATE_INTERVAL(this); } float ADCSensor::sample() { uint32_t raw = 0; + auto aggr = Aggregator(this->sampling_mode_); + if (this->output_raw_) { for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += analogRead(this->pin_->get_pin()); // NOLINT + raw = analogRead(this->pin_->get_pin()); // NOLINT + aggr.add_sample(raw); } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) - return raw; + return aggr.aggregate(); } + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT + raw = analogReadVoltage(this->pin_->get_pin()); // NOLINT + aggr.add_sample(raw); } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) - return raw / 1000.0f; + + return aggr.aggregate() / 1000.0f; } } // namespace adc diff --git a/esphome/components/adc/adc_sensor_rp2040.cpp b/esphome/components/adc/adc_sensor_rp2040.cpp index 520ff3bacc..f6cf1bac7a 100644 --- a/esphome/components/adc/adc_sensor_rp2040.cpp +++ b/esphome/components/adc/adc_sensor_rp2040.cpp @@ -34,24 +34,28 @@ void ADCSensor::dump_config() { #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_))); LOG_UPDATE_INTERVAL(this); } float ADCSensor::sample() { + uint32_t raw = 0; + auto aggr = Aggregator(this->sampling_mode_); + if (this->is_temperature_) { adc_set_temp_sensor_enabled(true); delay(1); adc_select_input(4); - uint32_t raw = 0; + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += adc_read(); + raw = adc_read(); + aggr.add_sample(raw); } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) adc_set_temp_sensor_enabled(false); if (this->output_raw_) { - return raw; + return aggr.aggregate(); } - return raw * 3.3f / 4096.0f; + return aggr.aggregate() * 3.3f / 4096.0f; } uint8_t pin = this->pin_->get_pin(); @@ -68,11 +72,10 @@ float ADCSensor::sample() { adc_gpio_init(pin); adc_select_input(pin - 26); - uint32_t raw = 0; for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - raw += adc_read(); + raw = adc_read(); + aggr.add_sample(raw); } - raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) #ifdef CYW43_USES_VSYS_PIN if (pin == PICO_VSYS_PIN) { @@ -81,10 +84,10 @@ float ADCSensor::sample() { #endif // CYW43_USES_VSYS_PIN if (this->output_raw_) { - return raw; + return aggr.aggregate(); } float coeff = pin == PICO_VSYS_PIN ? 3.0f : 1.0f; - return raw * 3.3f / 4096.0f * coeff; + return aggr.aggregate() * 3.3f / 4096.0f * coeff; } } // namespace adc diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 59ea9e184c..3309bd04c5 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -1,11 +1,9 @@ import logging import esphome.codegen as cg -import esphome.config_validation as cv -import esphome.final_validate as fv -from esphome.core import CORE from esphome.components import sensor, voltage_sampler from esphome.components.esp32 import get_esp32_variant +import esphome.config_validation as cv from esphome.const import ( CONF_ATTENUATION, CONF_ID, @@ -17,10 +15,14 @@ from esphome.const import ( STATE_CLASS_MEASUREMENT, UNIT_VOLT, ) +from esphome.core import CORE +import esphome.final_validate as fv + from . import ( ATTENUATION_MODES, ESP32_VARIANT_ADC1_PIN_TO_CHANNEL, ESP32_VARIANT_ADC2_PIN_TO_CHANNEL, + SAMPLING_MODES, adc_ns, validate_adc_pin, ) @@ -30,9 +32,11 @@ _LOGGER = logging.getLogger(__name__) AUTO_LOAD = ["voltage_sampler"] CONF_SAMPLES = "samples" +CONF_SAMPLING_MODE = "sampling_mode" _attenuation = cv.enum(ATTENUATION_MODES, lower=True) +_sampling_mode = cv.enum(SAMPLING_MODES, lower=True) def validate_config(config): @@ -88,6 +92,7 @@ CONFIG_SCHEMA = cv.All( cv.only_on_esp32, _attenuation ), cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255), + cv.Optional(CONF_SAMPLING_MODE, default="avg"): _sampling_mode, } ) .extend(cv.polling_component_schema("60s")), @@ -112,6 +117,7 @@ async def to_code(config): cg.add(var.set_output_raw(config[CONF_RAW])) cg.add(var.set_sample_count(config[CONF_SAMPLES])) + cg.add(var.set_sampling_mode(config[CONF_SAMPLING_MODE])) if attenuation := config.get(CONF_ATTENUATION): if attenuation == "auto": diff --git a/tests/component_tests/sensor/test_sensor.yaml b/tests/component_tests/sensor/test_sensor.yaml index 612b8e5e56..0f0ad5e94e 100644 --- a/tests/component_tests/sensor/test_sensor.yaml +++ b/tests/component_tests/sensor/test_sensor.yaml @@ -10,5 +10,6 @@ sensor: pin: A0 id: s_1 name: test s1 + sampling_mode: min update_interval: 60s device_class: voltage From 8be9f02693fdd383b4bfcde3fb3216068472cc3e Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Sun, 9 Feb 2025 23:42:40 -0500 Subject: [PATCH 0986/1052] [ota] Increase socket timeout earlier in OTA script (#8129) --- esphome/espota2.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/espota2.py b/esphome/espota2.py index 94b845b246..4f2a08fb4a 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -249,6 +249,9 @@ def perform_ota( send_check(sock, result, "auth result") receive_exactly(sock, 1, "auth result", RESPONSE_AUTH_OK) + # Set higher timeout during upload + sock.settimeout(30.0) + upload_size = len(upload_contents) upload_size_encoded = [ (upload_size >> 24) & 0xFF, @@ -271,8 +274,6 @@ def perform_ota( # show the actual progress sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, UPLOAD_BUFFER_SIZE) - # Set higher timeout during upload - sock.settimeout(30.0) start_time = time.perf_counter() offset = 0 From 84836f15db00e8eecc73fa06b169fa7265ed653b Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Mon, 10 Feb 2025 13:00:23 -0600 Subject: [PATCH 0987/1052] [speaker] Media Player Components PR9 (#8171) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + esphome/components/media_player/__init__.py | 59 +- esphome/components/media_player/automation.h | 13 +- .../components/media_player/media_player.cpp | 8 + .../components/media_player/media_player.h | 12 +- .../speaker/media_player/__init__.py | 458 ++++++++++++++ .../speaker/media_player/audio_pipeline.cpp | 560 +++++++++++++++++ .../speaker/media_player/audio_pipeline.h | 158 +++++ .../speaker/media_player/automation.h | 26 + .../media_player/speaker_media_player.cpp | 577 ++++++++++++++++++ .../media_player/speaker_media_player.h | 160 +++++ tests/components/media_player/common.yaml | 2 + .../speaker/common-media_player.yaml | 12 + .../speaker/media_player.esp32-idf.yaml | 9 + .../speaker/media_player.esp32-s3-idf.yaml | 9 + 15 files changed, 2043 insertions(+), 21 deletions(-) create mode 100644 esphome/components/speaker/media_player/__init__.py create mode 100644 esphome/components/speaker/media_player/audio_pipeline.cpp create mode 100644 esphome/components/speaker/media_player/audio_pipeline.h create mode 100644 esphome/components/speaker/media_player/automation.h create mode 100644 esphome/components/speaker/media_player/speaker_media_player.cpp create mode 100644 esphome/components/speaker/media_player/speaker_media_player.h create mode 100644 tests/components/speaker/common-media_player.yaml create mode 100644 tests/components/speaker/media_player.esp32-idf.yaml create mode 100644 tests/components/speaker/media_player.esp32-s3-idf.yaml diff --git a/CODEOWNERS b/CODEOWNERS index d4b3d7eff9..26e36befe5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -390,6 +390,7 @@ esphome/components/sn74hc165/* @jesserockz esphome/components/socket/* @esphome/core esphome/components/sonoff_d1/* @anatoly-savchenkov esphome/components/speaker/* @jesserockz @kahrendt +esphome/components/speaker/media_player/* @kahrendt @synesthesiam esphome/components/spi/* @clydebarrow @esphome/core esphome/components/spi_device/* @clydebarrow esphome/components/spi_led_strip/* @clydebarrow diff --git a/esphome/components/media_player/__init__.py b/esphome/components/media_player/__init__.py index a46b30db29..b2543ac05f 100644 --- a/esphome/components/media_player/__init__.py +++ b/esphome/components/media_player/__init__.py @@ -1,5 +1,4 @@ from esphome import automation -from esphome.automation import maybe_simple_id import esphome.codegen as cg import esphome.config_validation as cv from esphome.const import ( @@ -21,6 +20,16 @@ media_player_ns = cg.esphome_ns.namespace("media_player") MediaPlayer = media_player_ns.class_("MediaPlayer") +MediaPlayerSupportedFormat = media_player_ns.struct("MediaPlayerSupportedFormat") + +MediaPlayerFormatPurpose = media_player_ns.enum( + "MediaPlayerFormatPurpose", is_class=True +) +MEDIA_PLAYER_FORMAT_PURPOSE_ENUM = { + "default": MediaPlayerFormatPurpose.PURPOSE_DEFAULT, + "announcement": MediaPlayerFormatPurpose.PURPOSE_ANNOUNCEMENT, +} + PlayAction = media_player_ns.class_( "PlayAction", automation.Action, cg.Parented.template(MediaPlayer) @@ -47,7 +56,7 @@ VolumeSetAction = media_player_ns.class_( "VolumeSetAction", automation.Action, cg.Parented.template(MediaPlayer) ) - +CONF_ANNOUNCEMENT = "announcement" CONF_ON_PLAY = "on_play" CONF_ON_PAUSE = "on_pause" CONF_ON_ANNOUNCEMENT = "on_announcement" @@ -125,7 +134,16 @@ MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( ) -MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(MediaPlayer)}) +MEDIA_PLAYER_ACTION_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.use_id(MediaPlayer), + cv.Optional(CONF_ANNOUNCEMENT, default=False): cv.templatable(cv.boolean), + } +) + +MEDIA_PLAYER_CONDITION_SCHEMA = automation.maybe_simple_id( + {cv.GenerateID(): cv.use_id(MediaPlayer)} +) @automation.register_action( @@ -135,6 +153,7 @@ MEDIA_PLAYER_ACTION_SCHEMA = maybe_simple_id({cv.GenerateID(): cv.use_id(MediaPl { cv.GenerateID(): cv.use_id(MediaPlayer), cv.Required(CONF_MEDIA_URL): cv.templatable(cv.url), + cv.Optional(CONF_ANNOUNCEMENT, default=False): cv.templatable(cv.boolean), }, key=CONF_MEDIA_URL, ), @@ -143,7 +162,9 @@ async def media_player_play_media_action(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) media_url = await cg.templatable(config[CONF_MEDIA_URL], args, cg.std_string) + announcement = await cg.templatable(config[CONF_ANNOUNCEMENT], args, cg.bool_) cg.add(var.set_media_url(media_url)) + cg.add(var.set_announcement(announcement)) return var @@ -161,19 +182,27 @@ async def media_player_play_media_action(config, action_id, template_arg, args): @automation.register_action( "media_player.volume_down", VolumeDownAction, MEDIA_PLAYER_ACTION_SCHEMA ) -@automation.register_condition( - "media_player.is_idle", IsIdleCondition, MEDIA_PLAYER_ACTION_SCHEMA -) -@automation.register_condition( - "media_player.is_paused", IsPausedCondition, MEDIA_PLAYER_ACTION_SCHEMA -) -@automation.register_condition( - "media_player.is_playing", IsPlayingCondition, MEDIA_PLAYER_ACTION_SCHEMA -) -@automation.register_condition( - "media_player.is_announcing", IsAnnouncingCondition, MEDIA_PLAYER_ACTION_SCHEMA -) async def media_player_action(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + announcement = await cg.templatable(config[CONF_ANNOUNCEMENT], args, cg.bool_) + cg.add(var.set_announcement(announcement)) + return var + + +@automation.register_condition( + "media_player.is_idle", IsIdleCondition, MEDIA_PLAYER_CONDITION_SCHEMA +) +@automation.register_condition( + "media_player.is_paused", IsPausedCondition, MEDIA_PLAYER_CONDITION_SCHEMA +) +@automation.register_condition( + "media_player.is_playing", IsPlayingCondition, MEDIA_PLAYER_CONDITION_SCHEMA +) +@automation.register_condition( + "media_player.is_announcing", IsAnnouncingCondition, MEDIA_PLAYER_CONDITION_SCHEMA +) +async def media_player_condition(config, action_id, template_arg, args): var = cg.new_Pvariable(action_id, template_arg) await cg.register_parented(var, config[CONF_ID]) return var diff --git a/esphome/components/media_player/automation.h b/esphome/components/media_player/automation.h index 7b9220c4a5..422c224a85 100644 --- a/esphome/components/media_player/automation.h +++ b/esphome/components/media_player/automation.h @@ -10,7 +10,10 @@ namespace media_player { template class MediaPlayerCommandAction : public Action, public Parented { public: - void play(Ts... x) override { this->parent_->make_call().set_command(Command).perform(); } + TEMPLATABLE_VALUE(bool, announcement); + void play(Ts... x) override { + this->parent_->make_call().set_command(Command).set_announcement(this->announcement_.value(x...)).perform(); + } }; template @@ -28,7 +31,13 @@ using VolumeDownAction = MediaPlayerCommandAction class PlayMediaAction : public Action, public Parented { TEMPLATABLE_VALUE(std::string, media_url) - void play(Ts... x) override { this->parent_->make_call().set_media_url(this->media_url_.value(x...)).perform(); } + TEMPLATABLE_VALUE(bool, announcement) + void play(Ts... x) override { + this->parent_->make_call() + .set_media_url(this->media_url_.value(x...)) + .set_announcement(this->announcement_.value(x...)) + .perform(); + } }; template class VolumeSetAction : public Action, public Parented { diff --git a/esphome/components/media_player/media_player.cpp b/esphome/components/media_player/media_player.cpp index b5190d8573..01304d9135 100644 --- a/esphome/components/media_player/media_player.cpp +++ b/esphome/components/media_player/media_player.cpp @@ -41,6 +41,14 @@ const char *media_player_command_to_string(MediaPlayerCommand command) { return "VOLUME_UP"; case MEDIA_PLAYER_COMMAND_VOLUME_DOWN: return "VOLUME_DOWN"; + case MEDIA_PLAYER_COMMAND_ENQUEUE: + return "ENQUEUE"; + case MEDIA_PLAYER_COMMAND_REPEAT_ONE: + return "REPEAT_ONE"; + case MEDIA_PLAYER_COMMAND_REPEAT_OFF: + return "REPEAT_OFF"; + case MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST: + return "CLEAR_PLAYLIST"; default: return "UNKNOWN"; } diff --git a/esphome/components/media_player/media_player.h b/esphome/components/media_player/media_player.h index 78b3ed6216..ee5889901c 100644 --- a/esphome/components/media_player/media_player.h +++ b/esphome/components/media_player/media_player.h @@ -24,6 +24,10 @@ enum MediaPlayerCommand : uint8_t { MEDIA_PLAYER_COMMAND_TOGGLE = 5, MEDIA_PLAYER_COMMAND_VOLUME_UP = 6, MEDIA_PLAYER_COMMAND_VOLUME_DOWN = 7, + MEDIA_PLAYER_COMMAND_ENQUEUE = 8, + MEDIA_PLAYER_COMMAND_REPEAT_ONE = 9, + MEDIA_PLAYER_COMMAND_REPEAT_OFF = 10, + MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST = 11, }; const char *media_player_command_to_string(MediaPlayerCommand command); @@ -72,10 +76,10 @@ class MediaPlayerCall { void perform(); - const optional &get_command() const { return command_; } - const optional &get_media_url() const { return media_url_; } - const optional &get_volume() const { return volume_; } - const optional &get_announcement() const { return announcement_; } + const optional &get_command() const { return this->command_; } + const optional &get_media_url() const { return this->media_url_; } + const optional &get_volume() const { return this->volume_; } + const optional &get_announcement() const { return this->announcement_; } protected: void validate_(); diff --git a/esphome/components/speaker/media_player/__init__.py b/esphome/components/speaker/media_player/__init__.py new file mode 100644 index 0000000000..14b72cacc0 --- /dev/null +++ b/esphome/components/speaker/media_player/__init__.py @@ -0,0 +1,458 @@ +"""Speaker Media Player Setup.""" + +import hashlib +import logging +from pathlib import Path + +from esphome import automation, external_files +import esphome.codegen as cg +from esphome.components import audio, esp32, media_player, speaker +import esphome.config_validation as cv +from esphome.const import ( + CONF_BUFFER_SIZE, + CONF_FILE, + CONF_FILES, + CONF_FORMAT, + CONF_ID, + CONF_NUM_CHANNELS, + CONF_PATH, + CONF_RAW_DATA_ID, + CONF_SAMPLE_RATE, + CONF_SPEAKER, + CONF_TASK_STACK_IN_PSRAM, + CONF_TYPE, + CONF_URL, +) +from esphome.core import CORE, HexInt +from esphome.core.entity_helpers import inherit_property_from +from esphome.external_files import download_content + +_LOGGER = logging.getLogger(__name__) + +AUTO_LOAD = ["audio", "psram"] + +CODEOWNERS = ["@kahrendt", "@synesthesiam"] +DOMAIN = "media_player" + +TYPE_LOCAL = "local" +TYPE_WEB = "web" + +CONF_ANNOUNCEMENT = "announcement" +CONF_ANNOUNCEMENT_PIPELINE = "announcement_pipeline" +CONF_CODEC_SUPPORT_ENABLED = "codec_support_enabled" +CONF_ENQUEUE = "enqueue" +CONF_MEDIA_FILE = "media_file" +CONF_MEDIA_PIPELINE = "media_pipeline" +CONF_ON_MUTE = "on_mute" +CONF_ON_UNMUTE = "on_unmute" +CONF_ON_VOLUME = "on_volume" +CONF_STREAM = "stream" +CONF_VOLUME_INCREMENT = "volume_increment" +CONF_VOLUME_MIN = "volume_min" +CONF_VOLUME_MAX = "volume_max" + + +speaker_ns = cg.esphome_ns.namespace("speaker") +SpeakerMediaPlayer = speaker_ns.class_( + "SpeakerMediaPlayer", + media_player.MediaPlayer, + cg.Component, +) + +AudioPipeline = speaker_ns.class_("AudioPipeline") +AudioPipelineType = speaker_ns.enum("AudioPipelineType", is_class=True) +AUDIO_PIPELINE_TYPE_ENUM = { + "MEDIA": AudioPipelineType.MEDIA, + "ANNOUNCEMENT": AudioPipelineType.ANNOUNCEMENT, +} + +PlayOnDeviceMediaAction = speaker_ns.class_( + "PlayOnDeviceMediaAction", + automation.Action, + cg.Parented.template(SpeakerMediaPlayer), +) +StopStreamAction = speaker_ns.class_( + "StopStreamAction", automation.Action, cg.Parented.template(SpeakerMediaPlayer) +) + + +def _compute_local_file_path(value: dict) -> Path: + url = value[CONF_URL] + h = hashlib.new("sha256") + h.update(url.encode()) + key = h.hexdigest()[:8] + base_dir = external_files.compute_local_file_dir(DOMAIN) + _LOGGER.debug("_compute_local_file_path: base_dir=%s", base_dir / key) + return base_dir / key + + +def _download_web_file(value): + url = value[CONF_URL] + path = _compute_local_file_path(value) + + download_content(url, path) + _LOGGER.debug("download_web_file: path=%s", path) + return value + + +# Returns a media_player.MediaPlayerSupportedFormat struct with the configured +# format, sample rate, number of channels, purpose, and bytes per sample +def _get_supported_format_struct(pipeline, type): + args = [ + media_player.MediaPlayerSupportedFormat, + ] + + if pipeline[CONF_FORMAT] == "FLAC": + args.append(("format", "flac")) + elif pipeline[CONF_FORMAT] == "MP3": + args.append(("format", "mp3")) + elif pipeline[CONF_FORMAT] == "WAV": + args.append(("format", "wav")) + + args.append(("sample_rate", pipeline[CONF_SAMPLE_RATE])) + args.append(("num_channels", pipeline[CONF_NUM_CHANNELS])) + + if type == "MEDIA": + args.append( + ( + "purpose", + media_player.MEDIA_PLAYER_FORMAT_PURPOSE_ENUM["default"], + ) + ) + elif type == "ANNOUNCEMENT": + args.append( + ( + "purpose", + media_player.MEDIA_PLAYER_FORMAT_PURPOSE_ENUM["announcement"], + ) + ) + if pipeline[CONF_FORMAT] != "MP3": + args.append(("sample_bytes", 2)) + + return cg.StructInitializer(*args) + + +def _file_schema(value): + if isinstance(value, str): + return _validate_file_shorthand(value) + return TYPED_FILE_SCHEMA(value) + + +def _read_audio_file_and_type(file_config): + conf_file = file_config[CONF_FILE] + file_source = conf_file[CONF_TYPE] + if file_source == TYPE_LOCAL: + path = CORE.relative_config_path(conf_file[CONF_PATH]) + elif file_source == TYPE_WEB: + path = _compute_local_file_path(conf_file) + else: + raise cv.Invalid("Unsupported file source.") + + with open(path, "rb") as f: + data = f.read() + + import puremagic + + file_type: str = puremagic.from_string(data) + if file_type.startswith("."): + file_type = file_type[1:] + + media_file_type = audio.AUDIO_FILE_TYPE_ENUM["NONE"] + if file_type in ("wav"): + media_file_type = audio.AUDIO_FILE_TYPE_ENUM["WAV"] + elif file_type in ("mp3", "mpeg", "mpga"): + media_file_type = audio.AUDIO_FILE_TYPE_ENUM["MP3"] + elif file_type in ("flac"): + media_file_type = audio.AUDIO_FILE_TYPE_ENUM["FLAC"] + + return data, media_file_type + + +def _validate_file_shorthand(value): + value = cv.string_strict(value) + if value.startswith("http://") or value.startswith("https://"): + return _file_schema( + { + CONF_TYPE: TYPE_WEB, + CONF_URL: value, + } + ) + return _file_schema( + { + CONF_TYPE: TYPE_LOCAL, + CONF_PATH: value, + } + ) + + +def _validate_pipeline(config): + # Inherit transcoder settings from speaker if not manually set + inherit_property_from(CONF_NUM_CHANNELS, CONF_SPEAKER)(config) + inherit_property_from(CONF_SAMPLE_RATE, CONF_SPEAKER)(config) + + # Validate the transcoder settings is compatible with the speaker + audio.final_validate_audio_schema( + "speaker media_player", + audio_device=CONF_SPEAKER, + bits_per_sample=16, + channels=config.get(CONF_NUM_CHANNELS), + sample_rate=config.get(CONF_SAMPLE_RATE), + )(config) + + return config + + +def _validate_repeated_speaker(config): + if (announcement_config := config.get(CONF_ANNOUNCEMENT_PIPELINE)) and ( + media_config := config.get(CONF_MEDIA_PIPELINE) + ): + if announcement_config[CONF_SPEAKER] == media_config[CONF_SPEAKER]: + raise cv.Invalid( + "The announcement and media pipelines cannot use the same speaker. Use the `mixer` speaker component to create two source speakers." + ) + + return config + + +def _validate_supported_local_file(config): + for file_config in config.get(CONF_FILES, []): + _, media_file_type = _read_audio_file_and_type(file_config) + if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]): + raise cv.Invalid("Unsupported local media file.") + if not config[CONF_CODEC_SUPPORT_ENABLED] and str(media_file_type) != str( + audio.AUDIO_FILE_TYPE_ENUM["WAV"] + ): + # Only wav files are supported + raise cv.Invalid( + f"Unsupported local media file type, set {CONF_CODEC_SUPPORT_ENABLED} to true or convert the media file to wav" + ) + + return config + + +LOCAL_SCHEMA = cv.Schema( + { + cv.Required(CONF_PATH): cv.file_, + } +) + +WEB_SCHEMA = cv.All( + { + cv.Required(CONF_URL): cv.url, + }, + _download_web_file, +) + + +TYPED_FILE_SCHEMA = cv.typed_schema( + { + TYPE_LOCAL: LOCAL_SCHEMA, + TYPE_WEB: WEB_SCHEMA, + }, +) + + +MEDIA_FILE_TYPE_SCHEMA = cv.Schema( + { + cv.Required(CONF_ID): cv.declare_id(audio.AudioFile), + cv.Required(CONF_FILE): _file_schema, + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + } +) + +PIPELINE_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(AudioPipeline), + cv.Required(CONF_SPEAKER): cv.use_id(speaker.Speaker), + cv.Optional(CONF_FORMAT, default="FLAC"): cv.enum(audio.AUDIO_FILE_TYPE_ENUM), + cv.Optional(CONF_SAMPLE_RATE): cv.int_range(min=1), + cv.Optional(CONF_NUM_CHANNELS): cv.int_range(1, 2), + } +) + +CONFIG_SCHEMA = cv.All( + media_player.MEDIA_PLAYER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(SpeakerMediaPlayer), + cv.Required(CONF_ANNOUNCEMENT_PIPELINE): PIPELINE_SCHEMA, + cv.Optional(CONF_MEDIA_PIPELINE): PIPELINE_SCHEMA, + cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range( + min=4000, max=4000000 + ), + cv.Optional(CONF_CODEC_SUPPORT_ENABLED, default=True): cv.boolean, + 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_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), + cv.Optional(CONF_ON_UNMUTE): automation.validate_automation(single=True), + cv.Optional(CONF_ON_VOLUME): automation.validate_automation(single=True), + } + ), + cv.only_with_esp_idf, + _validate_repeated_speaker, +) + + +FINAL_VALIDATE_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_ANNOUNCEMENT_PIPELINE): _validate_pipeline, + cv.Optional(CONF_MEDIA_PIPELINE): _validate_pipeline, + }, + extra=cv.ALLOW_EXTRA, + ), + _validate_supported_local_file, +) + + +async def to_code(config): + if config[CONF_CODEC_SUPPORT_ENABLED]: + # Compile all supported audio codecs and optimize the wifi settings + + cg.add_define("USE_AUDIO_FLAC_SUPPORT", True) + cg.add_define("USE_AUDIO_MP3_SUPPORT", True) + + # Wifi settings based on https://github.com/espressif/esp-adf/issues/297#issuecomment-783811702 + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM", 16) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM", 512) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_STATIC_TX_BUFFER", True) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_TX_BUFFER_TYPE", 0) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM", 8) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM", 32) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED", True) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_TX_BA_WIN", 16) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED", True) + esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_RX_BA_WIN", 32) + esp32.add_idf_sdkconfig_option("CONFIG_LWIP_MAX_ACTIVE_TCP", 16) + esp32.add_idf_sdkconfig_option("CONFIG_LWIP_MAX_LISTENING_TCP", 16) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_MAXRTX", 12) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_SYNMAXRTX", 6) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_MSS", 1436) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_MSL", 60000) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_SND_BUF_DEFAULT", 65535) + esp32.add_idf_sdkconfig_option( + "CONFIG_TCP_WND_DEFAULT", 65535 + ) # Adjusted from referenced settings to avoid compilation error + esp32.add_idf_sdkconfig_option("CONFIG_TCP_RECVMBOX_SIZE", 512) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_QUEUE_OOSEQ", True) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_OVERSIZE_MSS", True) + esp32.add_idf_sdkconfig_option("CONFIG_LWIP_WND_SCALE", True) + esp32.add_idf_sdkconfig_option("CONFIG_TCP_RCV_SCALE", 3) + esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 512) + + # Allocate wifi buffers in PSRAM + esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True) + + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await media_player.register_media_player(var, config) + + cg.add_define("USE_OTA_STATE_CALLBACK") + + cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE])) + + cg.add(var.set_task_stack_in_psram(config[CONF_TASK_STACK_IN_PSRAM])) + if config[CONF_TASK_STACK_IN_PSRAM]: + esp32.add_idf_sdkconfig_option( + "CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY", True + ) + + cg.add(var.set_volume_increment(config[CONF_VOLUME_INCREMENT])) + cg.add(var.set_volume_max(config[CONF_VOLUME_MAX])) + cg.add(var.set_volume_min(config[CONF_VOLUME_MIN])) + + announcement_pipeline_config = config[CONF_ANNOUNCEMENT_PIPELINE] + spkr = await cg.get_variable(announcement_pipeline_config[CONF_SPEAKER]) + cg.add(var.set_announcement_speaker(spkr)) + if announcement_pipeline_config[CONF_FORMAT] != "NONE": + cg.add( + var.set_announcement_format( + _get_supported_format_struct( + announcement_pipeline_config, "ANNOUNCEMENT" + ) + ) + ) + + if media_pipeline_config := config.get(CONF_MEDIA_PIPELINE): + spkr = await cg.get_variable(media_pipeline_config[CONF_SPEAKER]) + cg.add(var.set_media_speaker(spkr)) + if media_pipeline_config[CONF_FORMAT] != "NONE": + cg.add( + var.set_media_format( + _get_supported_format_struct(media_pipeline_config, "MEDIA") + ) + ) + + if on_mute := config.get(CONF_ON_MUTE): + await automation.build_automation( + var.get_mute_trigger(), + [], + on_mute, + ) + if on_unmute := config.get(CONF_ON_UNMUTE): + await automation.build_automation( + var.get_unmute_trigger(), + [], + on_unmute, + ) + if on_volume := config.get(CONF_ON_VOLUME): + await automation.build_automation( + var.get_volume_trigger(), + [(cg.float_, "x")], + on_volume, + ) + + for file_config in config.get(CONF_FILES, []): + data, media_file_type = _read_audio_file_and_type(file_config) + + rhs = [HexInt(x) for x in data] + prog_arr = cg.progmem_array(file_config[CONF_RAW_DATA_ID], rhs) + + media_files_struct = cg.StructInitializer( + audio.AudioFile, + ( + "data", + prog_arr, + ), + ( + "length", + len(rhs), + ), + ( + "file_type", + media_file_type, + ), + ) + + cg.new_Pvariable( + file_config[CONF_ID], + media_files_struct, + ) + + +@automation.register_action( + "media_player.speaker.play_on_device_media_file", + PlayOnDeviceMediaAction, + cv.maybe_simple_value( + { + cv.GenerateID(): cv.use_id(SpeakerMediaPlayer), + cv.Required(CONF_MEDIA_FILE): cv.use_id(audio.AudioFile), + cv.Optional(CONF_ANNOUNCEMENT, default=False): cv.templatable(cv.boolean), + cv.Optional(CONF_ENQUEUE, default=False): cv.templatable(cv.boolean), + }, + key=CONF_MEDIA_FILE, + ), +) +async def play_on_device_media_media_action(config, action_id, template_arg, args): + var = cg.new_Pvariable(action_id, template_arg) + await cg.register_parented(var, config[CONF_ID]) + media_file = await cg.get_variable(config[CONF_MEDIA_FILE]) + announcement = await cg.templatable(config[CONF_ANNOUNCEMENT], args, cg.bool_) + enqueue = await cg.templatable(config[CONF_ENQUEUE], args, cg.bool_) + + cg.add(var.set_audio_file(media_file)) + cg.add(var.set_announcement(announcement)) + cg.add(var.set_enqueue(enqueue)) + return var diff --git a/esphome/components/speaker/media_player/audio_pipeline.cpp b/esphome/components/speaker/media_player/audio_pipeline.cpp new file mode 100644 index 0000000000..73ec5a3334 --- /dev/null +++ b/esphome/components/speaker/media_player/audio_pipeline.cpp @@ -0,0 +1,560 @@ +#include "audio_pipeline.h" + +#ifdef USE_ESP_IDF + +#include "esphome/core/defines.h" +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace speaker { + +static const uint32_t INITIAL_BUFFER_MS = 1000; // Start playback after buffering this duration of the file + +static const uint32_t READ_TASK_STACK_SIZE = 5 * 1024; +static const uint32_t DECODE_TASK_STACK_SIZE = 3 * 1024; + +static const uint32_t INFO_ERROR_QUEUE_COUNT = 5; + +static const char *const TAG = "speaker_media_player.pipeline"; + +enum EventGroupBits : uint32_t { + // MESSAGE_* bits are only set by their respective tasks + + // Stops all activity in the pipeline elements; cleared by process_state() and set by stop() or by each task + PIPELINE_COMMAND_STOP = (1 << 0), + + // Read audio from an HTTP source; cleared by reader task and set by start_url + READER_COMMAND_INIT_HTTP = (1 << 4), + // Read audio from an audio file from the flash; cleared by reader task and set by start_file + READER_COMMAND_INIT_FILE = (1 << 5), + + // Audio file type is read after checking it is supported; cleared by decoder task + READER_MESSAGE_LOADED_MEDIA_TYPE = (1 << 6), + // Reader is done (either through a failure or just end of the stream); cleared by reader task + READER_MESSAGE_FINISHED = (1 << 7), + // Error reading the file; cleared by process_state() + READER_MESSAGE_ERROR = (1 << 8), + + // Decoder is done (either through a faiilure or the end of the stream); cleared by decoder task + DECODER_MESSAGE_FINISHED = (1 << 12), + // Error decoding the file; cleared by process_state() by decoder task + DECODER_MESSAGE_ERROR = (1 << 13), +}; + +AudioPipeline::AudioPipeline(speaker::Speaker *speaker, size_t buffer_size, bool task_stack_in_psram, + std::string base_name, UBaseType_t priority) + : base_name_(std::move(base_name)), + priority_(priority), + task_stack_in_psram_(task_stack_in_psram), + speaker_(speaker), + buffer_size_(buffer_size) { + this->allocate_communications_(); + this->transfer_buffer_size_ = std::min(buffer_size_ / 4, DEFAULT_TRANSFER_BUFFER_SIZE); +} + +void AudioPipeline::start_url(const std::string &uri) { + if (this->is_playing_) { + xEventGroupSetBits(this->event_group_, PIPELINE_COMMAND_STOP); + } + this->current_uri_ = uri; + this->pending_url_ = true; +} + +void AudioPipeline::start_file(audio::AudioFile *audio_file) { + if (this->is_playing_) { + xEventGroupSetBits(this->event_group_, PIPELINE_COMMAND_STOP); + } + this->current_audio_file_ = audio_file; + this->pending_file_ = true; +} + +esp_err_t AudioPipeline::stop() { + xEventGroupSetBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP); + + return ESP_OK; +} +void AudioPipeline::set_pause_state(bool pause_state) { + this->speaker_->set_pause_state(pause_state); + + this->pause_state_ = pause_state; +} + +void AudioPipeline::suspend_tasks() { + if (this->read_task_handle_ != nullptr) { + vTaskSuspend(this->read_task_handle_); + } + if (this->decode_task_handle_ != nullptr) { + vTaskSuspend(this->decode_task_handle_); + } +} + +void AudioPipeline::resume_tasks() { + if (this->read_task_handle_ != nullptr) { + vTaskResume(this->read_task_handle_); + } + if (this->decode_task_handle_ != nullptr) { + vTaskResume(this->decode_task_handle_); + } +} + +AudioPipelineState AudioPipeline::process_state() { + /* + * Log items from info error queue + */ + InfoErrorEvent event; + if (this->info_error_queue_ != nullptr) { + while (xQueueReceive(this->info_error_queue_, &event, 0)) { + switch (event.source) { + case InfoErrorSource::READER: + if (event.err.has_value()) { + ESP_LOGE(TAG, "Media reader encountered an error: %s", esp_err_to_name(event.err.value())); + } else if (event.file_type.has_value()) { + ESP_LOGD(TAG, "Reading %s file type", audio_file_type_to_string(event.file_type.value())); + } + + break; + case InfoErrorSource::DECODER: + if (event.err.has_value()) { + ESP_LOGE(TAG, "Decoder encountered an error: %s", esp_err_to_name(event.err.value())); + } + + if (event.audio_stream_info.has_value()) { + ESP_LOGD(TAG, "Decoded audio has %d channels, %" PRId32 " Hz sample rate, and %d bits per sample", + event.audio_stream_info.value().get_channels(), event.audio_stream_info.value().get_sample_rate(), + event.audio_stream_info.value().get_bits_per_sample()); + } + + if (event.decoding_err.has_value()) { + switch (event.decoding_err.value()) { + case DecodingError::FAILED_HEADER: + ESP_LOGE(TAG, "Failed to parse the file's header."); + break; + case DecodingError::INCOMPATIBLE_BITS_PER_SAMPLE: + ESP_LOGE(TAG, "Incompatible bits per sample. Only 16 bits per sample is supported"); + break; + case DecodingError::INCOMPATIBLE_CHANNELS: + ESP_LOGE(TAG, "Incompatible number of channels. Only 1 or 2 channel audio is supported."); + break; + } + } + break; + } + } + } + + /* + * Determine the current state based on the event group bits and tasks' status + */ + + EventBits_t event_bits = xEventGroupGetBits(this->event_group_); + + if (this->pending_url_ || this->pending_file_) { + // Init command pending + if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) { + // Only start if there is no pending stop command + if ((this->read_task_handle_ == nullptr) || (this->decode_task_handle_ == nullptr)) { + // At least one task isn't running + this->start_tasks_(); + } + + if (this->pending_url_) { + xEventGroupSetBits(this->event_group_, EventGroupBits::READER_COMMAND_INIT_HTTP); + this->playback_ms_ = 0; + this->pending_url_ = false; + } else if (this->pending_file_) { + xEventGroupSetBits(this->event_group_, EventGroupBits::READER_COMMAND_INIT_FILE); + this->playback_ms_ = 0; + this->pending_file_ = false; + } + + this->is_playing_ = true; + return AudioPipelineState::PLAYING; + } + } + + if ((event_bits & EventGroupBits::READER_MESSAGE_FINISHED) && + (!(event_bits & EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE) && + (event_bits & EventGroupBits::DECODER_MESSAGE_FINISHED))) { + // Tasks are finished and there's no media in between the reader and decoder + + if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) { + // Stop command is fully processed, so clear the command bit + xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP); + } + + if (!this->is_playing_) { + // The tasks have been stopped for two ``process_state`` calls in a row, so delete the tasks + if ((this->read_task_handle_ != nullptr) || (this->decode_task_handle_ != nullptr)) { + this->delete_tasks_(); + this->speaker_->stop(); + } + } + this->is_playing_ = false; + return AudioPipelineState::STOPPED; + } + + if ((event_bits & EventGroupBits::READER_MESSAGE_ERROR)) { + xEventGroupClearBits(this->event_group_, EventGroupBits::READER_MESSAGE_ERROR); + return AudioPipelineState::ERROR_READING; + } + + if ((event_bits & EventGroupBits::DECODER_MESSAGE_ERROR)) { + xEventGroupClearBits(this->event_group_, EventGroupBits::DECODER_MESSAGE_ERROR); + return AudioPipelineState::ERROR_DECODING; + } + + if (this->pause_state_) { + return AudioPipelineState::PAUSED; + } + + if ((this->read_task_handle_ == nullptr) && (this->decode_task_handle_ == nullptr)) { + // No tasks are running, so the pipeline is stopped. + xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP); + return AudioPipelineState::STOPPED; + } + + this->is_playing_ = true; + return AudioPipelineState::PLAYING; +} + +esp_err_t AudioPipeline::allocate_communications_() { + if (this->event_group_ == nullptr) + this->event_group_ = xEventGroupCreate(); + + if (this->event_group_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + if (this->info_error_queue_ == nullptr) + this->info_error_queue_ = xQueueCreate(INFO_ERROR_QUEUE_COUNT, sizeof(InfoErrorEvent)); + + if (this->info_error_queue_ == nullptr) + return ESP_ERR_NO_MEM; + + return ESP_OK; +} + +esp_err_t AudioPipeline::start_tasks_() { + if (this->read_task_handle_ == nullptr) { + if (this->read_task_stack_buffer_ == nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + this->read_task_stack_buffer_ = stack_allocator.allocate(READ_TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + this->read_task_stack_buffer_ = stack_allocator.allocate(READ_TASK_STACK_SIZE); + } + } + + if (this->read_task_stack_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + if (this->read_task_handle_ == nullptr) { + this->read_task_handle_ = + xTaskCreateStatic(read_task, (this->base_name_ + "_read").c_str(), READ_TASK_STACK_SIZE, (void *) this, + this->priority_, this->read_task_stack_buffer_, &this->read_task_stack_); + } + + if (this->read_task_handle_ == nullptr) { + return ESP_ERR_INVALID_STATE; + } + } + + if (this->decode_task_handle_ == nullptr) { + if (this->decode_task_stack_buffer_ == nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + this->decode_task_stack_buffer_ = stack_allocator.allocate(DECODE_TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + this->decode_task_stack_buffer_ = stack_allocator.allocate(DECODE_TASK_STACK_SIZE); + } + } + + if (this->decode_task_stack_buffer_ == nullptr) { + return ESP_ERR_NO_MEM; + } + + if (this->decode_task_handle_ == nullptr) { + this->decode_task_handle_ = + xTaskCreateStatic(decode_task, (this->base_name_ + "_decode").c_str(), DECODE_TASK_STACK_SIZE, (void *) this, + this->priority_, this->decode_task_stack_buffer_, &this->decode_task_stack_); + } + + if (this->decode_task_handle_ == nullptr) { + return ESP_ERR_INVALID_STATE; + } + } + + return ESP_OK; +} + +void AudioPipeline::delete_tasks_() { + if (this->read_task_handle_ != nullptr) { + vTaskDelete(this->read_task_handle_); + + if (this->read_task_stack_buffer_ != nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + stack_allocator.deallocate(this->read_task_stack_buffer_, READ_TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + stack_allocator.deallocate(this->read_task_stack_buffer_, READ_TASK_STACK_SIZE); + } + + this->read_task_stack_buffer_ = nullptr; + this->read_task_handle_ = nullptr; + } + } + + if (this->decode_task_handle_ != nullptr) { + vTaskDelete(this->decode_task_handle_); + + if (this->decode_task_stack_buffer_ != nullptr) { + if (this->task_stack_in_psram_) { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_EXTERNAL); + stack_allocator.deallocate(this->decode_task_stack_buffer_, DECODE_TASK_STACK_SIZE); + } else { + RAMAllocator stack_allocator(RAMAllocator::ALLOC_INTERNAL); + stack_allocator.deallocate(this->decode_task_stack_buffer_, DECODE_TASK_STACK_SIZE); + } + + this->decode_task_stack_buffer_ = nullptr; + this->decode_task_handle_ = nullptr; + } + } +} + +void AudioPipeline::read_task(void *params) { + AudioPipeline *this_pipeline = (AudioPipeline *) params; + + while (true) { + xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED); + + // Wait until the pipeline notifies us the source of the media file + EventBits_t event_bits = + xEventGroupWaitBits(this_pipeline->event_group_, + EventGroupBits::READER_COMMAND_INIT_FILE | EventGroupBits::READER_COMMAND_INIT_HTTP | + EventGroupBits::PIPELINE_COMMAND_STOP, // Bit message to read + pdFALSE, // Clear the bit on exit + pdFALSE, // Wait for all the bits, + portMAX_DELAY); // Block indefinitely until bit is set + + if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) { + xEventGroupClearBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_FINISHED | + EventGroupBits::READER_COMMAND_INIT_FILE | + EventGroupBits::READER_COMMAND_INIT_HTTP); + InfoErrorEvent event; + event.source = InfoErrorSource::READER; + esp_err_t err = ESP_OK; + + std::unique_ptr reader = + make_unique(this_pipeline->transfer_buffer_size_); + + if (event_bits & EventGroupBits::READER_COMMAND_INIT_FILE) { + err = reader->start(this_pipeline->current_audio_file_, this_pipeline->current_audio_file_type_); + } else { + err = reader->start(this_pipeline->current_uri_, this_pipeline->current_audio_file_type_); + } + + if (err == ESP_OK) { + size_t file_ring_buffer_size = this_pipeline->buffer_size_; + + std::shared_ptr temp_ring_buffer; + + if (!this_pipeline->raw_file_ring_buffer_.use_count()) { + temp_ring_buffer = RingBuffer::create(file_ring_buffer_size); + this_pipeline->raw_file_ring_buffer_ = temp_ring_buffer; + } + + if (!this_pipeline->raw_file_ring_buffer_.use_count()) { + err = ESP_ERR_NO_MEM; + } else { + reader->add_sink(this_pipeline->raw_file_ring_buffer_); + } + } + + if (err != ESP_OK) { + // Send specific error message + event.err = err; + xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY); + + // Setting up the reader failed, stop the pipeline + xEventGroupSetBits(this_pipeline->event_group_, + EventGroupBits::READER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP); + } else { + // Send the file type to the pipeline + event.file_type = this_pipeline->current_audio_file_type_; + xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY); + xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE); + } + + while (true) { + event_bits = xEventGroupGetBits(this_pipeline->event_group_); + + if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) { + break; + } + + audio::AudioReaderState reader_state = reader->read(); + + if (reader_state == audio::AudioReaderState::FINISHED) { + break; + } else if (reader_state == audio::AudioReaderState::FAILED) { + xEventGroupSetBits(this_pipeline->event_group_, + EventGroupBits::READER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP); + break; + } + } + event_bits = xEventGroupGetBits(this_pipeline->event_group_); + if ((event_bits & EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE) || + (this_pipeline->raw_file_ring_buffer_.use_count() == 1)) { + // Decoder task hasn't started yet, so delay a bit before releasing ownership of the ring buffer + delay(10); + } + } + } +} + +void AudioPipeline::decode_task(void *params) { + AudioPipeline *this_pipeline = (AudioPipeline *) params; + + while (true) { + xEventGroupSetBits(this_pipeline->event_group_, EventGroupBits::DECODER_MESSAGE_FINISHED); + + // Wait until the reader notifies us that the media type is available + EventBits_t event_bits = xEventGroupWaitBits(this_pipeline->event_group_, + EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE | + EventGroupBits::PIPELINE_COMMAND_STOP, // Bit message to read + pdFALSE, // Clear the bit on exit + pdFALSE, // Wait for all the bits, + portMAX_DELAY); // Block indefinitely until bit is set + + if (!(event_bits & EventGroupBits::PIPELINE_COMMAND_STOP)) { + xEventGroupClearBits(this_pipeline->event_group_, + EventGroupBits::DECODER_MESSAGE_FINISHED | EventGroupBits::READER_MESSAGE_LOADED_MEDIA_TYPE); + InfoErrorEvent event; + event.source = InfoErrorSource::DECODER; + + std::unique_ptr decoder = + make_unique(this_pipeline->transfer_buffer_size_, this_pipeline->transfer_buffer_size_); + + esp_err_t err = decoder->start(this_pipeline->current_audio_file_type_); + decoder->add_source(this_pipeline->raw_file_ring_buffer_); + + if (err != ESP_OK) { + // Send specific error message + event.err = err; + xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY); + + // Setting up the decoder failed, stop the pipeline + xEventGroupSetBits(this_pipeline->event_group_, + EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP); + } + + bool has_stream_info = false; + bool started_playback = false; + + size_t initial_bytes_to_buffer = 0; + + while (true) { + event_bits = xEventGroupGetBits(this_pipeline->event_group_); + + if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) { + break; + } + + // Update pause state + if (!started_playback) { + if (!(event_bits & EventGroupBits::READER_MESSAGE_FINISHED)) { + decoder->set_pause_output_state(true); + } else { + started_playback = true; + } + } else { + decoder->set_pause_output_state(this_pipeline->pause_state_); + } + + // Stop gracefully if the reader has finished + audio::AudioDecoderState decoder_state = decoder->decode(event_bits & EventGroupBits::READER_MESSAGE_FINISHED); + + if ((decoder_state == audio::AudioDecoderState::DECODING) || + (decoder_state == audio::AudioDecoderState::FINISHED)) { + this_pipeline->playback_ms_ = decoder->get_playback_ms(); + } + + if (decoder_state == audio::AudioDecoderState::FINISHED) { + break; + } else if (decoder_state == audio::AudioDecoderState::FAILED) { + if (!has_stream_info) { + event.decoding_err = DecodingError::FAILED_HEADER; + xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY); + } + xEventGroupSetBits(this_pipeline->event_group_, + EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP); + break; + } + + if (!has_stream_info && decoder->get_audio_stream_info().has_value()) { + has_stream_info = true; + + this_pipeline->current_audio_stream_info_ = decoder->get_audio_stream_info().value(); + + // Send the stream information to the pipeline + event.audio_stream_info = this_pipeline->current_audio_stream_info_; + + if (this_pipeline->current_audio_stream_info_.get_bits_per_sample() != 16) { + // Error state, incompatible bits per sample + event.decoding_err = DecodingError::INCOMPATIBLE_BITS_PER_SAMPLE; + xEventGroupSetBits(this_pipeline->event_group_, + EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP); + } else if ((this_pipeline->current_audio_stream_info_.get_channels() > 2)) { + // Error state, incompatible number of channels + event.decoding_err = DecodingError::INCOMPATIBLE_CHANNELS; + xEventGroupSetBits(this_pipeline->event_group_, + EventGroupBits::DECODER_MESSAGE_ERROR | EventGroupBits::PIPELINE_COMMAND_STOP); + } else { + // Send audio directly to the speaker + this_pipeline->speaker_->set_audio_stream_info(this_pipeline->current_audio_stream_info_); + decoder->add_sink(this_pipeline->speaker_); + } + + initial_bytes_to_buffer = std::min(this_pipeline->current_audio_stream_info_.ms_to_bytes(INITIAL_BUFFER_MS), + this_pipeline->buffer_size_ * 3 / 4); + + switch (this_pipeline->current_audio_file_type_) { +#ifdef USE_AUDIO_MP3_SUPPORT + case audio::AudioFileType::MP3: + initial_bytes_to_buffer /= 8; // Estimate the MP3 compression factor is 8 + break; +#endif +#ifdef USE_AUDIO_FLAC_SUPPORT + case audio::AudioFileType::FLAC: + initial_bytes_to_buffer /= 2; // Estimate the FLAC compression factor is 2 + break; +#endif + default: + break; + } + xQueueSend(this_pipeline->info_error_queue_, &event, portMAX_DELAY); + } + + if (!started_playback && has_stream_info) { + // Verify enough data is available before starting playback + std::shared_ptr temp_ring_buffer = this_pipeline->raw_file_ring_buffer_.lock(); + if (temp_ring_buffer->available() >= initial_bytes_to_buffer) { + started_playback = true; + } + } + } + } + } +} + +} // namespace speaker +} // namespace esphome + +#endif diff --git a/esphome/components/speaker/media_player/audio_pipeline.h b/esphome/components/speaker/media_player/audio_pipeline.h new file mode 100644 index 0000000000..c382e1eebe --- /dev/null +++ b/esphome/components/speaker/media_player/audio_pipeline.h @@ -0,0 +1,158 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include "esphome/components/audio/audio.h" +#include "esphome/components/audio/audio_reader.h" +#include "esphome/components/audio/audio_decoder.h" +#include "esphome/components/speaker/speaker.h" + +#include "esphome/core/ring_buffer.h" + +#include "esp_err.h" + +#include +#include +#include + +namespace esphome { +namespace speaker { + +// Internal sink/source buffers for reader and decoder +static const size_t DEFAULT_TRANSFER_BUFFER_SIZE = 24 * 1024; + +enum class AudioPipelineType : uint8_t { + MEDIA, + ANNOUNCEMENT, +}; + +enum class AudioPipelineState : uint8_t { + STARTING_FILE, + STARTING_URL, + PLAYING, + STOPPING, + STOPPED, + PAUSED, + ERROR_READING, + ERROR_DECODING, +}; + +enum class InfoErrorSource : uint8_t { + READER = 0, + DECODER, +}; + +enum class DecodingError : uint8_t { + FAILED_HEADER = 0, + INCOMPATIBLE_BITS_PER_SAMPLE, + INCOMPATIBLE_CHANNELS, +}; + +// Used to pass information from each task. +struct InfoErrorEvent { + InfoErrorSource source; + optional err; + optional file_type; + optional audio_stream_info; + optional decoding_err; +}; + +class AudioPipeline { + public: + /// @param speaker ESPHome speaker component for pipeline's audio output + /// @param buffer_size Size of the buffer in bytes between the reader and decoder + /// @param task_stack_in_psram True if the task stack should be allocated in PSRAM, false otherwise + /// @param task_name FreeRTOS task base name + /// @param priority FreeRTOS task priority + AudioPipeline(speaker::Speaker *speaker, size_t buffer_size, bool task_stack_in_psram, std::string base_name, + UBaseType_t priority); + + /// @brief Starts an audio pipeline given a media url + /// @param uri media file url + /// @return ESP_OK if successful or an appropriate error if not + void start_url(const std::string &uri); + + /// @brief Starts an audio pipeline given a AudioFile pointer + /// @param audio_file pointer to an AudioFile object + /// @return ESP_OK if successful or an appropriate error if not + void start_file(audio::AudioFile *audio_file); + + /// @brief Stops the pipeline. Sends a stop signal to each task (if running) and clears the ring buffers. + /// @return ESP_OK if successful or ESP_ERR_TIMEOUT if the tasks did not indicate they stopped + esp_err_t stop(); + + /// @brief Processes the state of the audio pipeline based on the info_error_queue_ and event_group_. Handles creating + /// and stopping the pipeline tasks. Needs to be regularly called to update the internal pipeline state. + /// @return AudioPipelineState + AudioPipelineState process_state(); + + /// @brief Suspends any running tasks + void suspend_tasks(); + /// @brief Resumes any running tasks + void resume_tasks(); + + uint32_t get_playback_ms() { return this->playback_ms_; } + + void set_pause_state(bool pause_state); + + protected: + /// @brief Allocates the event group and info error queue. + /// @return ESP_OK if successful or ESP_ERR_NO_MEM if it is unable to allocate all parts + esp_err_t allocate_communications_(); + + /// @brief Common start code for the pipeline, regardless if the source is a file or url. + /// @return ESP_OK if successful or an appropriate error if not + esp_err_t start_tasks_(); + + /// @brief Resets the task related pointers and deallocates their stacks. + void delete_tasks_(); + + std::string base_name_; + UBaseType_t priority_; + + uint32_t playback_ms_{0}; + + bool is_playing_{false}; + bool pause_state_{false}; + bool task_stack_in_psram_; + + // Pending file start state used to ensure the pipeline fully stops before attempting to start the next file + bool pending_url_{false}; + bool pending_file_{false}; + + speaker::Speaker *speaker_{nullptr}; + + std::string current_uri_{}; + audio::AudioFile *current_audio_file_{nullptr}; + + audio::AudioFileType current_audio_file_type_; + audio::AudioStreamInfo current_audio_stream_info_; + + size_t buffer_size_; // Ring buffer between reader and decoder + size_t transfer_buffer_size_; // Internal source/sink buffers for the audio reader and decoder + + std::weak_ptr raw_file_ring_buffer_; + + // Handles basic control/state of the three tasks + EventGroupHandle_t event_group_{nullptr}; + + // Receives detailed info (file type, stream info, resampling info) or specific errors from the three tasks + QueueHandle_t info_error_queue_{nullptr}; + + // Handles reading the media file from flash or a url + static void read_task(void *params); + TaskHandle_t read_task_handle_{nullptr}; + StaticTask_t read_task_stack_; + StackType_t *read_task_stack_buffer_{nullptr}; + + // Decodes the media file into PCM audio + static void decode_task(void *params); + TaskHandle_t decode_task_handle_{nullptr}; + StaticTask_t decode_task_stack_; + StackType_t *decode_task_stack_buffer_{nullptr}; +}; + +} // namespace speaker +} // namespace esphome + +#endif diff --git a/esphome/components/speaker/media_player/automation.h b/esphome/components/speaker/media_player/automation.h new file mode 100644 index 0000000000..d1a01aabc4 --- /dev/null +++ b/esphome/components/speaker/media_player/automation.h @@ -0,0 +1,26 @@ +#pragma once + +#include "speaker_media_player.h" + +#ifdef USE_ESP_IDF + +#include "esphome/components/audio/audio.h" +#include "esphome/core/automation.h" + +namespace esphome { +namespace speaker { + +template class PlayOnDeviceMediaAction : public Action, public Parented { + TEMPLATABLE_VALUE(audio::AudioFile *, audio_file) + TEMPLATABLE_VALUE(bool, announcement) + TEMPLATABLE_VALUE(bool, enqueue) + void play(Ts... x) override { + this->parent_->play_file(this->audio_file_.value(x...), this->announcement_.value(x...), + this->enqueue_.value(x...)); + } +}; + +} // namespace speaker +} // namespace esphome + +#endif diff --git a/esphome/components/speaker/media_player/speaker_media_player.cpp b/esphome/components/speaker/media_player/speaker_media_player.cpp new file mode 100644 index 0000000000..0a2585ce60 --- /dev/null +++ b/esphome/components/speaker/media_player/speaker_media_player.cpp @@ -0,0 +1,577 @@ +#include "speaker_media_player.h" + +#ifdef USE_ESP_IDF + +#include "esphome/core/log.h" + +#include "esphome/components/audio/audio.h" +#ifdef USE_OTA +#include "esphome/components/ota/ota_backend.h" +#endif + +namespace esphome { +namespace speaker { + +// Framework: +// - Media player that can handle two streams: one for media and one for announcements +// - Each stream has an individual speaker component for output +// - Each stream is handled by an ``AudioPipeline`` object with two parts/tasks +// - ``AudioReader`` handles reading from an HTTP source or from a PROGMEM flash set at compile time +// - ``AudioDecoder`` handles decoding the audio file. All formats are limited to two channels and 16 bits per sample +// - FLAC +// - MP3 (based on the libhelix decoder) +// - WAV +// - Each task runs until it is done processing the file or it receives a stop command +// - Inter-task communication uses a FreeRTOS Event Group +// - The ``AudioPipeline`` sets up a ring buffer between the reader and decoder tasks. The decoder task outputs audio +// directly to a speaker component. +// - The pipelines internal state needs to be processed by regularly calling ``process_state``. +// - Generic media player commands are received by the ``control`` function. The commands are added to the +// ``media_control_command_queue_`` to be processed in the component's loop +// - Local file play back is initiatied with ``play_file`` and adds it to the ``media_control_command_queue_`` +// - Starting a stream intializes the appropriate pipeline or stops it if it is already running +// - Volume and mute commands are achieved by the ``mute``, ``unmute``, ``set_volume`` functions. +// - Volume commands are ignored if the media control queue is full to avoid crashing with rapid volume +// increases/decreases. +// - These functions all send the appropriate information to the speakers to implement. +// - Pausing is implemented in the decoder task and is also sent directly to the media speaker component to decrease +// latency. +// - The components main loop performs housekeeping: +// - It reads the media control queue and processes it directly +// - It determines the overall state of the media player by considering the state of each pipeline +// - announcement playback takes highest priority +// - Handles playlists and repeating by starting the appropriate file when a previous file is finished +// - Logging only happens in the main loop task to reduce task stack memory usage. + +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() { + state = media_player::MEDIA_PLAYER_STATE_IDLE; + + this->media_control_command_queue_ = xQueueCreate(MEDIA_CONTROLS_QUEUE_LENGTH, sizeof(MediaCallCommand)); + + this->pref_ = global_preferences->make_preference(this->get_object_id_hash()); + + VolumeRestoreState volume_restore_state; + if (this->pref_.load(&volume_restore_state)) { + 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_mute_state_(false); + } + +#ifdef USE_OTA + ota::get_global_ota_callback()->add_on_state_callback( + [this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) { + if (state == ota::OTA_STARTED) { + if (this->media_pipeline_ != nullptr) { + this->media_pipeline_->suspend_tasks(); + } + if (this->announcement_pipeline_ != nullptr) { + this->announcement_pipeline_->suspend_tasks(); + } + } else if (state == ota::OTA_ERROR) { + if (this->media_pipeline_ != nullptr) { + this->media_pipeline_->resume_tasks(); + } + if (this->announcement_pipeline_ != nullptr) { + this->announcement_pipeline_->resume_tasks(); + } + } + }); +#endif + + this->announcement_pipeline_ = + make_unique(this->announcement_speaker_, this->buffer_size_, this->task_stack_in_psram_, "ann", + ANNOUNCEMENT_PIPELINE_TASK_PRIORITY); + + if (this->announcement_pipeline_ == nullptr) { + ESP_LOGE(TAG, "Failed to create announcement pipeline"); + this->mark_failed(); + } + + if (!this->single_pipeline_()) { + this->media_pipeline_ = make_unique(this->media_speaker_, this->buffer_size_, + this->task_stack_in_psram_, "ann", MEDIA_PIPELINE_TASK_PRIORITY); + + if (this->media_pipeline_ == nullptr) { + ESP_LOGE(TAG, "Failed to create media pipeline"); + this->mark_failed(); + } + + // Setup callback to track the duration of audio played by the media pipeline + this->media_speaker_->add_audio_output_callback( + [this](uint32_t new_playback_ms, uint32_t remainder_us, uint32_t pending_ms, uint32_t write_timestamp) { + this->playback_ms_ += new_playback_ms; + this->remainder_us_ = remainder_us; + this->pending_ms_ = pending_ms; + this->last_audio_write_timestamp_ = write_timestamp; + this->playback_us_ = this->playback_ms_ * 1000 + this->remainder_us_; + }); + } + + ESP_LOGI(TAG, "Set up speaker media player"); +} + +void SpeakerMediaPlayer::set_playlist_delay_ms(AudioPipelineType pipeline_type, uint32_t delay_ms) { + switch (pipeline_type) { + case AudioPipelineType::ANNOUNCEMENT: + this->announcement_playlist_delay_ms_ = delay_ms; + break; + case AudioPipelineType::MEDIA: + this->media_playlist_delay_ms_ = delay_ms; + break; + } +} + +void SpeakerMediaPlayer::watch_media_commands_() { + if (!this->is_ready()) { + return; + } + + MediaCallCommand media_command; + esp_err_t err = ESP_OK; + + if (xQueueReceive(this->media_control_command_queue_, &media_command, 0) == pdTRUE) { + bool new_url = media_command.new_url.has_value() && media_command.new_url.value(); + bool new_file = media_command.new_file.has_value() && media_command.new_file.value(); + + if (new_url || new_file) { + bool enqueue = media_command.enqueue.has_value() && media_command.enqueue.value(); + + if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) { + // Announcement playlist/pipeline + + if (!enqueue) { + // Clear the queue and ensure the loaded next item doesn't start playing + this->cancel_timeout("next_ann"); + this->announcement_playlist_.clear(); + } + + PlaylistItem playlist_item; + if (new_url) { + playlist_item.url = this->announcement_url_; + if (!enqueue) { + // Not adding to the queue, so directly start playback and internally unpause the pipeline + this->announcement_pipeline_->start_url(playlist_item.url.value()); + this->announcement_pipeline_->set_pause_state(false); + } + } else { + playlist_item.file = this->announcement_file_; + if (!enqueue) { + // Not adding to the queue, so directly start playback and internally unpause the pipeline + this->announcement_pipeline_->start_file(playlist_item.file.value()); + this->announcement_pipeline_->set_pause_state(false); + } + } + this->announcement_playlist_.push_back(playlist_item); + } else { + // Media playlist/pipeline + + if (!enqueue) { + // Clear the queue and ensure the loaded next item doesn't start playing + this->cancel_timeout("next_media"); + this->media_playlist_.clear(); + } + + this->is_paused_ = false; + PlaylistItem playlist_item; + if (new_url) { + playlist_item.url = this->media_url_; + if (!enqueue) { + // Not adding to the queue, so directly start playback and internally unpause the pipeline + this->media_pipeline_->start_url(playlist_item.url.value()); + this->media_pipeline_->set_pause_state(false); + } + } else { + playlist_item.file = this->media_file_; + if (!enqueue) { + // Not adding to the queue, so directly start playback and internally unpause the pipeline + this->media_pipeline_->start_file(playlist_item.file.value()); + this->media_pipeline_->set_pause_state(false); + } + } + this->media_playlist_.push_back(playlist_item); + } + + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error starting the audio pipeline: %s", esp_err_to_name(err)); + this->status_set_error(); + } else { + this->status_clear_error(); + } + + return; // Don't process the new file play command further + } + + if (media_command.volume.has_value()) { + this->set_volume_(media_command.volume.value()); + this->publish_state(); + } + + if (media_command.command.has_value()) { + switch (media_command.command.value()) { + case media_player::MEDIA_PLAYER_COMMAND_PLAY: + if ((this->media_pipeline_ != nullptr) && (this->is_paused_)) { + this->media_pipeline_->set_pause_state(false); + } + this->is_paused_ = false; + break; + case media_player::MEDIA_PLAYER_COMMAND_PAUSE: + if ((this->media_pipeline_ != nullptr) && (!this->is_paused_)) { + this->media_pipeline_->set_pause_state(true); + } + this->is_paused_ = true; + break; + case media_player::MEDIA_PLAYER_COMMAND_STOP: + if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) { + if (this->announcement_pipeline_ != nullptr) { + this->cancel_timeout("next_ann"); + this->announcement_playlist_.clear(); + this->announcement_pipeline_->stop(); + } + } else { + if (this->media_pipeline_ != nullptr) { + this->cancel_timeout("next_media"); + this->media_playlist_.clear(); + this->media_pipeline_->stop(); + } + } + break; + case media_player::MEDIA_PLAYER_COMMAND_TOGGLE: + if (this->media_pipeline_ != nullptr) { + if (this->is_paused_) { + this->media_pipeline_->set_pause_state(false); + this->is_paused_ = false; + } else { + this->media_pipeline_->set_pause_state(true); + this->is_paused_ = true; + } + } + break; + case media_player::MEDIA_PLAYER_COMMAND_MUTE: { + this->set_mute_state_(true); + + this->publish_state(); + break; + } + case media_player::MEDIA_PLAYER_COMMAND_UNMUTE: + this->set_mute_state_(false); + this->publish_state(); + break; + case media_player::MEDIA_PLAYER_COMMAND_VOLUME_UP: + this->set_volume_(std::min(1.0f, this->volume + this->volume_increment_)); + this->publish_state(); + break; + case media_player::MEDIA_PLAYER_COMMAND_VOLUME_DOWN: + this->set_volume_(std::max(0.0f, this->volume - this->volume_increment_)); + this->publish_state(); + break; + case media_player::MEDIA_PLAYER_COMMAND_REPEAT_ONE: + if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) { + this->announcement_repeat_one_ = true; + } else { + this->media_repeat_one_ = true; + } + break; + case media_player::MEDIA_PLAYER_COMMAND_REPEAT_OFF: + if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) { + this->announcement_repeat_one_ = false; + } else { + this->media_repeat_one_ = false; + } + break; + case media_player::MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST: + if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) { + if (this->announcement_playlist_.empty()) { + this->announcement_playlist_.resize(1); + } + } else { + if (this->media_playlist_.empty()) { + this->media_playlist_.resize(1); + } + } + break; + default: + break; + } + } + } +} + +void SpeakerMediaPlayer::loop() { + this->watch_media_commands_(); + + // Determine state of the media player + media_player::MediaPlayerState old_state = this->state; + + AudioPipelineState old_media_pipeline_state = this->media_pipeline_state_; + if (this->media_pipeline_ != nullptr) { + this->media_pipeline_state_ = this->media_pipeline_->process_state(); + this->decoded_playback_ms_ = this->media_pipeline_->get_playback_ms(); + } + + if (this->media_pipeline_state_ == AudioPipelineState::ERROR_READING) { + ESP_LOGE(TAG, "The media pipeline's file reader encountered an error."); + } else if (this->media_pipeline_state_ == AudioPipelineState::ERROR_DECODING) { + ESP_LOGE(TAG, "The media pipeline's audio decoder encountered an error."); + } + + AudioPipelineState old_announcement_pipeline_state = this->announcement_pipeline_state_; + if (this->announcement_pipeline_ != nullptr) { + this->announcement_pipeline_state_ = this->announcement_pipeline_->process_state(); + } + + if (this->announcement_pipeline_state_ == AudioPipelineState::ERROR_READING) { + ESP_LOGE(TAG, "The announcement pipeline's file reader encountered an error."); + } else if (this->announcement_pipeline_state_ == AudioPipelineState::ERROR_DECODING) { + ESP_LOGE(TAG, "The announcement pipeline's audio decoder encountered an error."); + } + + if (this->announcement_pipeline_state_ != AudioPipelineState::STOPPED) { + this->state = media_player::MEDIA_PLAYER_STATE_ANNOUNCING; + } else { + if (!this->announcement_playlist_.empty()) { + uint32_t timeout_ms = 0; + if (old_announcement_pipeline_state == AudioPipelineState::PLAYING) { + // Finished the current announcement file + if (!this->announcement_repeat_one_) { + // Pop item off the playlist if repeat is disabled + this->announcement_playlist_.pop_front(); + } + // Only delay starting playback if moving on the next playlist item or repeating the current item + timeout_ms = this->announcement_playlist_delay_ms_; + } + + if (!this->announcement_playlist_.empty()) { + // Start the next announcement file + PlaylistItem playlist_item = this->announcement_playlist_.front(); + if (playlist_item.url.has_value()) { + this->announcement_pipeline_->start_url(playlist_item.url.value()); + } else if (playlist_item.file.has_value()) { + this->announcement_pipeline_->start_file(playlist_item.file.value()); + } + + if (timeout_ms > 0) { + // Pause pipeline internally to facilitiate delay between items + this->announcement_pipeline_->set_pause_state(true); + // Internally unpause the pipeline after the delay between playlist items + this->set_timeout("next_ann", timeout_ms, + [this]() { this->announcement_pipeline_->set_pause_state(this->is_paused_); }); + } + } + } else { + if (this->is_paused_) { + this->state = media_player::MEDIA_PLAYER_STATE_PAUSED; + } else if (this->media_pipeline_state_ == AudioPipelineState::PLAYING) { + this->state = media_player::MEDIA_PLAYER_STATE_PLAYING; + } else if (this->media_pipeline_state_ == AudioPipelineState::STOPPED) { + // Reset playback durations + this->decoded_playback_ms_ = 0; + this->playback_us_ = 0; + this->playback_ms_ = 0; + this->remainder_us_ = 0; + this->pending_ms_ = 0; + + if (!media_playlist_.empty()) { + uint32_t timeout_ms = 0; + if (old_media_pipeline_state == AudioPipelineState::PLAYING) { + // Finished the current media file + if (!this->media_repeat_one_) { + // Pop item off the playlist if repeat is disabled + this->media_playlist_.pop_front(); + } + // Only delay starting playback if moving on the next playlist item or repeating the current item + timeout_ms = this->announcement_playlist_delay_ms_; + } + if (!this->media_playlist_.empty()) { + PlaylistItem playlist_item = this->media_playlist_.front(); + if (playlist_item.url.has_value()) { + this->media_pipeline_->start_url(playlist_item.url.value()); + } else if (playlist_item.file.has_value()) { + this->media_pipeline_->start_file(playlist_item.file.value()); + } + + if (timeout_ms > 0) { + // Pause pipeline internally to facilitiate delay between items + this->media_pipeline_->set_pause_state(true); + // Internally unpause the pipeline after the delay between playlist items + this->set_timeout("next_media", timeout_ms, + [this]() { this->media_pipeline_->set_pause_state(this->is_paused_); }); + } + } + } else { + this->state = media_player::MEDIA_PLAYER_STATE_IDLE; + } + } + } + } + + if (this->state != old_state) { + this->publish_state(); + ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state)); + } +} + +void SpeakerMediaPlayer::play_file(audio::AudioFile *media_file, bool announcement, bool enqueue) { + if (!this->is_ready()) { + // Ignore any commands sent before the media player is setup + return; + } + + MediaCallCommand media_command; + + media_command.new_file = true; + if (this->single_pipeline_() || announcement) { + this->announcement_file_ = media_file; + media_command.announce = true; + } else { + this->media_file_ = media_file; + media_command.announce = false; + } + media_command.enqueue = enqueue; + xQueueSend(this->media_control_command_queue_, &media_command, portMAX_DELAY); +} + +void SpeakerMediaPlayer::control(const media_player::MediaPlayerCall &call) { + if (!this->is_ready()) { + // Ignore any commands sent before the media player is setup + return; + } + + MediaCallCommand media_command; + + if (this->single_pipeline_() || (call.get_announcement().has_value() && call.get_announcement().value())) { + media_command.announce = true; + } else { + media_command.announce = false; + } + + if (call.get_media_url().has_value()) { + std::string new_uri = call.get_media_url().value(); + + media_command.new_url = true; + if (this->single_pipeline_() || (call.get_announcement().has_value() && call.get_announcement().value())) { + this->announcement_url_ = new_uri; + } else { + this->media_url_ = new_uri; + } + + if (call.get_command().has_value()) { + if (call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_ENQUEUE) { + media_command.enqueue = true; + } + } + + xQueueSend(this->media_control_command_queue_, &media_command, portMAX_DELAY); + return; + } + + if (call.get_volume().has_value()) { + media_command.volume = call.get_volume().value(); + // Wait 0 ticks for queue to be free, volume sets aren't that important! + xQueueSend(this->media_control_command_queue_, &media_command, 0); + return; + } + + if (call.get_command().has_value()) { + media_command.command = call.get_command().value(); + TickType_t ticks_to_wait = portMAX_DELAY; + if ((call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_VOLUME_UP) || + (call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_VOLUME_DOWN)) { + ticks_to_wait = 0; // Wait 0 ticks for queue to be free, volume sets aren't that important! + } + xQueueSend(this->media_control_command_queue_, &media_command, ticks_to_wait); + return; + } +} + +media_player::MediaPlayerTraits SpeakerMediaPlayer::get_traits() { + auto traits = media_player::MediaPlayerTraits(); + if (!this->single_pipeline_()) { + traits.set_supports_pause(true); + } + + if (this->announcement_format_.has_value()) { + traits.get_supported_formats().push_back(this->announcement_format_.value()); + } + if (this->media_format_.has_value()) { + traits.get_supported_formats().push_back(this->media_format_.value()); + } else if (this->single_pipeline_() && this->announcement_format_.has_value()) { + // Only one pipeline is defined, so use the announcement format (if configured) for the default purpose + media_player::MediaPlayerSupportedFormat media_format = this->announcement_format_.value(); + media_format.purpose = media_player::MediaPlayerFormatPurpose::PURPOSE_DEFAULT; + traits.get_supported_formats().push_back(media_format); + } + + return traits; +}; + +void SpeakerMediaPlayer::save_volume_restore_state_() { + VolumeRestoreState volume_restore_state; + volume_restore_state.volume = this->volume; + volume_restore_state.is_muted = this->is_muted_; + this->pref_.save(&volume_restore_state); +} + +void SpeakerMediaPlayer::set_mute_state_(bool mute_state) { + if (this->media_speaker_ != nullptr) { + this->media_speaker_->set_mute_state(mute_state); + } + if (this->announcement_speaker_ != nullptr) { + this->announcement_speaker_->set_mute_state(mute_state); + } + + bool old_mute_state = this->is_muted_; + this->is_muted_ = mute_state; + + this->save_volume_restore_state_(); + + if (old_mute_state != mute_state) { + if (mute_state) { + this->defer([this]() { this->mute_trigger_->trigger(); }); + } else { + this->defer([this]() { this->unmute_trigger_->trigger(); }); + } + } +} + +void SpeakerMediaPlayer::set_volume_(float volume, bool publish) { + // Remap the volume to fit with in the configured limits + float bounded_volume = remap(volume, 0.0f, 1.0f, this->volume_min_, this->volume_max_); + + if (this->media_speaker_ != nullptr) { + this->media_speaker_->set_volume(bounded_volume); + } + + if (this->announcement_speaker_ != nullptr) { + this->announcement_speaker_->set_volume(bounded_volume); + } + + if (publish) { + this->volume = volume; + this->save_volume_restore_state_(); + } + + // Turn on the mute state if the volume is effectively zero, off otherwise + if (volume < 0.001) { + this->set_mute_state_(true); + } else { + this->set_mute_state_(false); + } + + this->defer([this, volume]() { this->volume_trigger_->trigger(volume); }); +} + +} // namespace speaker +} // namespace esphome + +#endif diff --git a/esphome/components/speaker/media_player/speaker_media_player.h b/esphome/components/speaker/media_player/speaker_media_player.h new file mode 100644 index 0000000000..6cbce91866 --- /dev/null +++ b/esphome/components/speaker/media_player/speaker_media_player.h @@ -0,0 +1,160 @@ +#pragma once + +#ifdef USE_ESP_IDF + +#include "audio_pipeline.h" + +#include "esphome/components/audio/audio.h" + +#include "esphome/components/media_player/media_player.h" +#include "esphome/components/speaker/speaker.h" + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" + +#include +#include +#include + +namespace esphome { +namespace speaker { + +struct MediaCallCommand { + optional command; + optional volume; + optional announce; + optional new_url; + optional new_file; + optional enqueue; +}; + +struct PlaylistItem { + optional url; + optional file; +}; + +struct VolumeRestoreState { + float volume; + bool is_muted; +}; + +class SpeakerMediaPlayer : public Component, public media_player::MediaPlayer { + public: + float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; } + void setup() override; + void loop() override; + + // MediaPlayer implementations + media_player::MediaPlayerTraits get_traits() override; + bool is_muted() const override { return this->is_muted_; } + + void set_buffer_size(size_t buffer_size) { this->buffer_size_ = buffer_size; } + void set_task_stack_in_psram(bool task_stack_in_psram) { this->task_stack_in_psram_ = task_stack_in_psram; } + + // 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; } + + void set_volume_max(float volume_max) { this->volume_max_ = volume_max; } + void set_volume_min(float volume_min) { this->volume_min_ = volume_min; } + + void set_announcement_speaker(Speaker *announcement_speaker) { this->announcement_speaker_ = announcement_speaker; } + void set_announcement_format(const media_player::MediaPlayerSupportedFormat &announcement_format) { + this->announcement_format_ = announcement_format; + } + void set_media_speaker(Speaker *media_speaker) { this->media_speaker_ = media_speaker; } + void set_media_format(const media_player::MediaPlayerSupportedFormat &media_format) { + this->media_format_ = media_format; + } + + Trigger<> *get_mute_trigger() const { return this->mute_trigger_; } + Trigger<> *get_unmute_trigger() const { return this->unmute_trigger_; } + Trigger *get_volume_trigger() const { return this->volume_trigger_; } + + void play_file(audio::AudioFile *media_file, bool announcement, bool enqueue); + + uint32_t get_playback_ms() const { return this->playback_ms_; } + uint32_t get_playback_us() const { return this->playback_us_; } + uint32_t get_decoded_playback_ms() const { return this->decoded_playback_ms_; } + + void set_playlist_delay_ms(AudioPipelineType pipeline_type, uint32_t delay_ms); + + protected: + // Receives commands from HA or from the voice assistant component + // Sends commands to the media_control_commanda_queue_ + void control(const media_player::MediaPlayerCall &call) override; + + /// @brief Updates this->volume and saves volume/mute state to flash for restortation if publish is true. + void set_volume_(float volume, bool publish = true); + + /// @brief Sets the mute state. Restores previous volume if unmuting. Always saves volume/mute state to flash for + /// restoration. + /// @param mute_state If true, audio will be muted. If false, audio will be unmuted + void set_mute_state_(bool mute_state); + + /// @brief Saves the current volume and mute state to the flash for restoration. + void save_volume_restore_state_(); + + /// Returns true if the media player has only the announcement pipeline defined, false if both the announcement and + /// media pipelines are defined. + inline bool single_pipeline_() { return (this->media_speaker_ == nullptr); } + + // Processes commands from media_control_command_queue_. + void watch_media_commands_(); + + std::unique_ptr announcement_pipeline_; + std::unique_ptr media_pipeline_; + Speaker *media_speaker_{nullptr}; + Speaker *announcement_speaker_{nullptr}; + + optional media_format_; + AudioPipelineState media_pipeline_state_{AudioPipelineState::STOPPED}; + std::string media_url_{}; // only modified by control function + audio::AudioFile *media_file_{}; // only modified by play_file function + bool media_repeat_one_{false}; + uint32_t media_playlist_delay_ms_{0}; + + optional announcement_format_; + AudioPipelineState announcement_pipeline_state_{AudioPipelineState::STOPPED}; + std::string announcement_url_{}; // only modified by control function + audio::AudioFile *announcement_file_{}; // only modified by play_file function + bool announcement_repeat_one_{false}; + uint32_t announcement_playlist_delay_ms_{0}; + + QueueHandle_t media_control_command_queue_; + + std::deque announcement_playlist_; + std::deque media_playlist_; + + size_t buffer_size_; + + bool task_stack_in_psram_; + + bool is_paused_{false}; + bool is_muted_{false}; + + // The amount to change the volume on volume up/down commands + float volume_increment_; + + float volume_max_; + float volume_min_; + + // Used to save volume/mute state for restoration on reboot + ESPPreferenceObject pref_; + + Trigger<> *mute_trigger_ = new Trigger<>(); + Trigger<> *unmute_trigger_ = new Trigger<>(); + Trigger *volume_trigger_ = new Trigger(); + + uint32_t decoded_playback_ms_{0}; + uint32_t playback_us_{0}; + uint32_t playback_ms_{0}; + uint32_t remainder_us_{0}; + uint32_t pending_ms_{0}; + uint32_t last_audio_write_timestamp_{0}; +}; + +} // namespace speaker +} // namespace esphome + +#endif diff --git a/tests/components/media_player/common.yaml b/tests/components/media_player/common.yaml index af0d5c7765..763bc231c0 100644 --- a/tests/components/media_player/common.yaml +++ b/tests/components/media_player/common.yaml @@ -21,6 +21,8 @@ media_player: - media_player.pause: on_play: - media_player.stop: + - media_player.stop: + announcement: true on_pause: - media_player.toggle: - wait_until: diff --git a/tests/components/speaker/common-media_player.yaml b/tests/components/speaker/common-media_player.yaml new file mode 100644 index 0000000000..edc9f670fc --- /dev/null +++ b/tests/components/speaker/common-media_player.yaml @@ -0,0 +1,12 @@ +<<: !include common.yaml + +media_player: + - platform: speaker + id: speaker_media_player_id + announcement_pipeline: + speaker: speaker_id + buffer_size: 1000000 + volume_increment: 0.02 + volume_max: 0.95 + volume_min: 0.0 + task_stack_in_psram: true diff --git a/tests/components/speaker/media_player.esp32-idf.yaml b/tests/components/speaker/media_player.esp32-idf.yaml new file mode 100644 index 0000000000..4712e4bae8 --- /dev/null +++ b/tests/components/speaker/media_player.esp32-idf.yaml @@ -0,0 +1,9 @@ +substitutions: + scl_pin: GPIO16 + sda_pin: GPIO17 + i2s_bclk_pin: GPIO27 + i2s_lrclk_pin: GPIO26 + i2s_mclk_pin: GPIO25 + i2s_dout_pin: GPIO23 + +<<: !include common-media_player.yaml diff --git a/tests/components/speaker/media_player.esp32-s3-idf.yaml b/tests/components/speaker/media_player.esp32-s3-idf.yaml new file mode 100644 index 0000000000..b3eec04d23 --- /dev/null +++ b/tests/components/speaker/media_player.esp32-s3-idf.yaml @@ -0,0 +1,9 @@ +substitutions: + scl_pin: GPIO2 + sda_pin: GPIO3 + i2s_bclk_pin: GPIO4 + i2s_lrclk_pin: GPIO5 + i2s_mclk_pin: GPIO6 + i2s_dout_pin: GPIO7 + +<<: !include common-media_player.yaml From abdf215d3a54eff4b33b5d76207372245b5d1031 Mon Sep 17 00:00:00 2001 From: mystster Date: Tue, 11 Feb 2025 05:29:27 +0900 Subject: [PATCH 0988/1052] Add partial update of GDEW029T5 e-paper display (#8162) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- .../components/waveshare_epaper/display.py | 2 +- .../waveshare_epaper/waveshare_epaper.cpp | 242 ++++++++++++++++-- .../waveshare_epaper/waveshare_epaper.h | 21 +- tests/components/waveshare_epaper/common.yaml | 1 + 4 files changed, 236 insertions(+), 30 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 3a5151682f..81afc159a5 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -120,7 +120,7 @@ MODELS = { "2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74), "2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), - "gdew029t5": ("b", GDEW029T5), + "gdew029t5": ("c", GDEW029T5), "2.70in": ("b", WaveshareEPaper2P7In), "2.70in-b": ("b", WaveshareEPaper2P7InB), "2.70in-bv2": ("b", WaveshareEPaper2P7InBV2), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index fb9e8ff6e5..2ff271f999 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1596,15 +1596,108 @@ void WaveshareEPaper2P9InV2R2::set_full_update_every(uint32_t full_update_every) // Datasheet: // - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1680.pdf // - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h +// - https://github.com/ZinggJM/GxEPD2/blob/master/src/epd/GxEPD2_290_T5.cpp +// - http://www.e-paper-display.com/GDEW029T5%20V3.1%20Specification5c22.pdf? // ======================================================== -void GDEW029T5::initialize() { - // from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37 - // EPD hardware init start - this->reset_(); +// full screen update LUT +static const uint8_t LUT_20_VCOMDC_29_5[] = { + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_21_WW_29_5[] = { + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_22_BW_29_5[] = { + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14, + 0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_23_WB_29_5[] = { + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, + 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_24_BB_29_5[] = { + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14, + 0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +// partial screen update LUT +static const uint8_t LUT_20_VCOMDC_PARTIAL_29_5[] = { + 0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_21_WW_PARTIAL_29_5[] = { + 0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_22_BW_PARTIAL_29_5[] = { + 0x80, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_23_WB_PARTIAL_29_5[] = { + 0x40, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const uint8_t LUT_24_BB_PARTIAL_29_5[] = { + 0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +void GDEW029T5::power_on_() { + if (!this->power_is_on_) { + this->command(0x04); + this->wait_until_idle_(); + } + this->power_is_on_ = true; +} + +void GDEW029T5::power_off_() { + this->command(0x02); + this->wait_until_idle_(); + this->power_is_on_ = false; +} + +void GDEW029T5::deep_sleep() { + this->power_off_(); + if (this->deep_sleep_between_updates_) { + this->command(0x07); // deep sleep + this->data(0xA5); // check code + ESP_LOGD(TAG, "go to deep sleep"); + this->is_deep_sleep_ = true; + } +} + +void GDEW029T5::init_display_() { + // from https://github.com/ZinggJM/GxEPD2/blob/master/src/epd/GxEPD2_290_T5.cpp + + // Hardware Initialization + if (this->deep_sleep_between_updates_ && this->is_deep_sleep_) { + ESP_LOGI(TAG, "wake up from deep sleep"); + this->reset_(); + this->is_deep_sleep_ = false; + } // COMMAND POWER SETTINGS - this->command(0x00); + this->command(0x01); this->data(0x03); this->data(0x00); this->data(0x2b); @@ -1617,40 +1710,122 @@ void GDEW029T5::initialize() { this->data(0x17); this->data(0x17); - // COMMAND POWER ON - this->command(0x04); - this->wait_until_idle_(); - - // Not sure what this does but it's in the Adafruit EPD library - this->command(0xFF); - this->wait_until_idle_(); + this->power_on_(); // COMMAND PANEL SETTING this->command(0x00); // 128x296 resolution: 10 - // LUT from OTP: 0 + // LUT from register: 1 // B/W mode (doesn't work): 1 // scan-up: 1 // shift-right: 1 // booster ON: 1 // no soft reset: 1 - this->data(0b10011111); + this->data(0b10111111); + this->data(0x0d); // VCOM to 0V fast + this->command(0x30); // PLL setting + this->data(0x3a); // 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + this->command(0x61); // resolution setting + this->data(this->get_width_internal()); + this->data(this->get_height_internal() >> 8); + this->data(this->get_height_internal() & 0xFF); - // COMMAND RESOLUTION SETTING - // set to 128x296 by COMMAND PANEL SETTING - - // COMMAND VCOM AND DATA INTERVAL SETTING - // use defaults for white border and ESPHome image polarity - - // EPD hardware init end + ESP_LOGD(TAG, "panel setting done"); } + +void GDEW029T5::initialize() { + // from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37 + if (this->reset_pin_ != nullptr) + this->deep_sleep_between_updates_ = true; + + // old buffer for partial update + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + this->old_buffer_ = allocator.allocate(this->get_buffer_length_()); + if (this->old_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate old buffer for display!"); + return; + } + for (size_t i = 0; i < this->get_buffer_length_(); i++) { + this->old_buffer_[i] = 0xFF; + } +} + +// initialize for full(normal) update +void GDEW029T5::init_full_() { + this->init_display_(); + this->command(0x82); // vcom_DC setting + this->data(0x08); + this->command(0X50); // VCOM AND DATA INTERVAL SETTING + this->data(0x97); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + this->command(0x20); + this->write_lut_(LUT_20_VCOMDC_29_5, sizeof(LUT_20_VCOMDC_29_5)); + this->command(0x21); + this->write_lut_(LUT_21_WW_29_5, sizeof(LUT_21_WW_29_5)); + this->command(0x22); + this->write_lut_(LUT_22_BW_29_5, sizeof(LUT_22_BW_29_5)); + this->command(0x23); + this->write_lut_(LUT_23_WB_29_5, sizeof(LUT_23_WB_29_5)); + this->command(0x24); + this->write_lut_(LUT_24_BB_29_5, sizeof(LUT_24_BB_29_5)); + ESP_LOGD(TAG, "initialized full update"); +} + +// initialzie for partial update +void GDEW029T5::init_partial_() { + this->init_display_(); + this->command(0x82); // vcom_DC setting + this->data(0x08); + this->command(0X50); // VCOM AND DATA INTERVAL SETTING + this->data(0x17); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 + this->command(0x20); + this->write_lut_(LUT_20_VCOMDC_PARTIAL_29_5, sizeof(LUT_20_VCOMDC_PARTIAL_29_5)); + this->command(0x21); + this->write_lut_(LUT_21_WW_PARTIAL_29_5, sizeof(LUT_21_WW_PARTIAL_29_5)); + this->command(0x22); + this->write_lut_(LUT_22_BW_PARTIAL_29_5, sizeof(LUT_22_BW_PARTIAL_29_5)); + this->command(0x23); + this->write_lut_(LUT_23_WB_PARTIAL_29_5, sizeof(LUT_23_WB_PARTIAL_29_5)); + this->command(0x24); + this->write_lut_(LUT_24_BB_PARTIAL_29_5, sizeof(LUT_24_BB_PARTIAL_29_5)); + ESP_LOGD(TAG, "initialized partial update"); +} + void HOT GDEW029T5::display() { + bool full_update = this->at_update_ == 0; + if (full_update) { + this->init_full_(); + } else { + this->init_partial_(); + this->command(0x91); // partial in + // set partial window + this->command(0x90); + // this->data(0); + this->data(0); + // this->data(0); + this->data((this->get_width_internal() - 1) % 256); + this->data(0); + this->data(0); + this->data(((this->get_height_internal() - 1)) / 256); + this->data(((this->get_height_internal() - 1)) % 256); + this->data(0x01); + } + // input old buffer data + this->command(0x10); + delay(2); + this->start_data_(); + for (size_t i = 0; i < this->get_buffer_length_(); i++) { + this->write_byte(this->old_buffer_[i]); + } + this->end_data_(); + delay(2); + // COMMAND DATA START TRANSMISSION 2 (B/W only) this->command(0x13); delay(2); this->start_data_(); for (size_t i = 0; i < this->get_buffer_length_(); i++) { this->write_byte(this->buffer_[i]); + this->old_buffer_[i] = this->buffer_[i]; } this->end_data_(); delay(2); @@ -1660,10 +1835,28 @@ void HOT GDEW029T5::display() { delay(2); this->wait_until_idle_(); - // COMMAND POWER OFF - // NOTE: power off < deep sleep - this->command(0x02); + if (full_update) { + ESP_LOGD(TAG, "full update done"); + } else { + this->command(0x92); // partial out + ESP_LOGD(TAG, "partial update done"); + } + + this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; + // COMMAND deep sleep + this->deep_sleep(); } + +void GDEW029T5::write_lut_(const uint8_t *lut, const uint8_t size) { + // COMMAND WRITE LUT REGISTER + this->start_data_(); + for (uint8_t i = 0; i < size; i++) + this->write_byte(lut[i]); + this->end_data_(); +} + +void GDEW029T5::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } + int GDEW029T5::get_width_internal() { return 128; } int GDEW029T5::get_height_internal() { return 296; } void GDEW029T5::dump_config() { @@ -1672,6 +1865,7 @@ void GDEW029T5::dump_config() { LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_); + ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 0fc1051268..e0c8001a50 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -254,16 +254,27 @@ class GDEW029T5 : public WaveshareEPaper { void dump_config() override; - void deep_sleep() override { - // COMMAND DEEP SLEEP - this->command(0x07); - this->data(0xA5); // check byte - } + void deep_sleep() override; + void set_full_update_every(uint32_t full_update_every); protected: + void init_display_(); + void init_full_(); + void init_partial_(); + void write_lut_(const uint8_t *lut, uint8_t size); + void power_off_(); + void power_on_(); int get_width_internal() override; int get_height_internal() override; + + private: + uint32_t full_update_every_{30}; + uint32_t at_update_{0}; + bool deep_sleep_between_updates_{false}; + bool power_is_on_{false}; + bool is_deep_sleep_{false}; + uint8_t *old_buffer_{nullptr}; }; class WaveshareEPaper2P7InV2 : public WaveshareEPaper { diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index ff9ddb955f..1d77e0cf38 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -459,6 +459,7 @@ display: reset_pin: allow_other_uses: true number: ${reset_pin} + full_update_every: 30 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); From b667ceacedbbc5f248801460aef6432b62c6097c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=A1ta=20GEEK?= <63705187+TataGEEK@users.noreply.github.com> Date: Tue, 11 Feb 2025 01:35:56 +0100 Subject: [PATCH 0989/1052] Add waveshare 2.9inch e-Paper HAT (D) (#7906) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- .../components/waveshare_epaper/display.py | 4 + .../waveshare_epaper/waveshare_epaper.cpp | 87 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 20 +++++ tests/components/waveshare_epaper/common.yaml | 19 ++++ 4 files changed, 130 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 81afc159a5..f0064c6a4d 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -55,6 +55,9 @@ GDEW029T5 = waveshare_epaper_ns.class_("GDEW029T5", WaveshareEPaper) WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_( "WaveshareEPaper2P9InDKE", WaveshareEPaper ) +WaveshareEPaper2P9InD = waveshare_epaper_ns.class_( + "WaveshareEPaper2P9InD", WaveshareEPaper +) WaveshareEPaper4P2In = waveshare_epaper_ns.class_( "WaveshareEPaper4P2In", WaveshareEPaper ) @@ -128,6 +131,7 @@ MODELS = { "2.90in-b": ("b", WaveshareEPaper2P9InB), "2.90in-bv3": ("b", WaveshareEPaper2P9InBV3), "2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2), + "2.90in-d": ("b", WaveshareEPaper2P9InD), "2.90in-dke": ("c", WaveshareEPaper2P9InDKE), "4.20in": ("b", WaveshareEPaper4P2In), "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 2ff271f999..f240c98096 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1211,6 +1211,93 @@ void WaveshareEPaper2P9InB::dump_config() { LOG_UPDATE_INTERVAL(this); } +// ======================================================== +// Waveshare 2.9-inch E-Paper (Type D) +// Waveshare WIKI: https://www.waveshare.com/wiki/Pico-ePaper-2.9-D +// Datasheet: https://www.waveshare.com/w/upload/b/b5/2.9inch_e-Paper_(D)_Specification.pdf +// ======================================================== + +void WaveshareEPaper2P9InD::initialize() { + // EPD hardware init start + this->reset_(); + + // Booster Soft Start + this->command(0x06); // Command: BTST + this->data(0x17); // Soft start configuration Phase A + this->data(0x17); // Soft start configuration Phase B + this->data(0x17); // Soft start configuration Phase C + + // Power Setting + this->command(0x01); // Command: PWR + this->data(0x03); // Intern DC/DC for VDH/VDL and VGH/VGL + this->data(0x00); // Default configuration VCOM_HV and VGHL_LV + this->data(0x2b); // VDH = 10.8 V + this->data(0x2b); // VDL = -10.8 V + + // Power ON + this->command(0x04); // Command: PON + this->wait_until_idle_(); + + // Panel settings + this->command(0x00); // Command: PSR + this->data(0x1F); // LUT from OTP, black and white mode, default scan + + // PLL Control + this->command(0x30); // Command: PLL + this->data(0x3A); // Default PLL frequency + + // Resolution settings + this->command(0x61); // Command: TRES + this->data(0x80); // Width: 128 + this->data(0x01); // Height MSB: 296 + this->data(0x28); // Height LSB: 296 + + // VCOM and data interval settings + this->command(0x50); // Command: CDI + this->data(0x77); + + // VCOM_DC settings + this->command(0x82); // Command: VDCS + this->data(0x12); // Dafault VCOM_DC +} + +void WaveshareEPaper2P9InD::display() { + // Start transmitting old data (clearing buffer) + this->command(0x10); // Command: DTM1 (OLD frame data) + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + // Start transmitting new data (updated content) + this->command(0x13); // Command: DTM2 (NEW frame data) + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + // Refresh Display + this->command(0x12); // Command: DRF + this->wait_until_idle_(); + + // Enter Power Off + this->command(0x02); // Command: POF + this->wait_until_idle_(); + + // Enter Deep Sleep + this->command(0x07); // Command: DSLP + this->data(0xA5); +} + +int WaveshareEPaper2P9InD::get_width_internal() { return 128; } +int WaveshareEPaper2P9InD::get_height_internal() { return 296; } +void WaveshareEPaper2P9InD::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 2.9in (D)"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + // DKE 2.9 // https://www.badge.team/docs/badges/sha2017/hardware/#e-ink-display-the-dke-group-depg0290b1 // https://www.badge.team/docs/badges/sha2017/hardware/DEPG0290B01V3.0.pdf diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index e0c8001a50..c2277f2962 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -427,6 +427,26 @@ class WaveshareEPaper2P9InDKE : public WaveshareEPaper { int get_height_internal() override; }; +class WaveshareEPaper2P9InD : public WaveshareEPaper { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND DEEP SLEEP + this->command(0x07); + this->data(0xA5); + } + + protected: + int get_width_internal() override; + + int get_height_internal() override; +}; + class WaveshareEPaper4P2In : public WaveshareEPaper { public: void initialize() override; diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index 1d77e0cf38..4a67ccd67d 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -714,6 +714,25 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + model: 2.90in-d + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + reset_duration: 200ms + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper model: 2.90in spi_id: spi_waveshare_epaper From 8b7aa4c1102f2102d38a2d189e678b6761ee7d51 Mon Sep 17 00:00:00 2001 From: guillempages Date: Tue, 11 Feb 2025 01:39:03 +0100 Subject: [PATCH 0990/1052] [http_request]Use std::string for headers (#8225) --- esphome/components/http_request/http_request.h | 4 ++-- esphome/components/http_request/http_request_arduino.cpp | 2 +- esphome/components/http_request/http_request_idf.cpp | 2 +- esphome/components/online_image/online_image.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index b2ce718ec4..e98fd1a475 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -18,8 +18,8 @@ namespace esphome { namespace http_request { struct Header { - const char *name; - const char *value; + std::string name; + std::string value; }; // Some common HTTP status codes diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index 85a1312aaa..b0067e7839 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -96,7 +96,7 @@ std::shared_ptr HttpRequestArduino::start(std::string url, std::s container->client_.setUserAgent(this->useragent_); } for (const auto &header : headers) { - container->client_.addHeader(header.name, header.value, false, true); + container->client_.addHeader(header.name.c_str(), header.value.c_str(), false, true); } // returned needed headers must be collected before the requests diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 66f064c2ce..78c37403f5 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -84,7 +84,7 @@ std::shared_ptr HttpRequestIDF::start(std::string url, std::strin container->set_secure(secure); for (const auto &header : headers) { - esp_http_client_set_header(client, header.name, header.value); + esp_http_client_set_header(client, header.name.c_str(), header.value.c_str()); } const int body_len = body.length(); diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index da0e88a904..a1c530c2ab 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -124,7 +124,7 @@ void OnlineImage::update() { default: accept_mime_type = "image/*"; } - accept_header.value = (accept_mime_type + ",*/*;q=0.8").c_str(); + accept_header.value = accept_mime_type + ",*/*;q=0.8"; headers.push_back(accept_header); From c9e7562aff0b3a94c47baf3dc946646564432a3e Mon Sep 17 00:00:00 2001 From: guillempages Date: Tue, 11 Feb 2025 12:12:13 +0100 Subject: [PATCH 0991/1052] [online_image] Improve error handling (#8212) --- esphome/components/online_image/image_decoder.cpp | 12 +++++++++++- esphome/components/online_image/image_decoder.h | 11 ++++++----- esphome/components/online_image/jpeg_image.cpp | 7 ++++--- esphome/components/online_image/jpeg_image.h | 2 +- esphome/components/online_image/online_image.cpp | 7 ++++++- esphome/components/online_image/png_image.cpp | 7 ++++++- esphome/components/online_image/png_image.h | 2 +- 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/esphome/components/online_image/image_decoder.cpp b/esphome/components/online_image/image_decoder.cpp index 2958d8671d..d8c0cc33c4 100644 --- a/esphome/components/online_image/image_decoder.cpp +++ b/esphome/components/online_image/image_decoder.cpp @@ -25,6 +25,15 @@ void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { } } +DownloadBuffer::DownloadBuffer(size_t size) : size_(size) { + this->buffer_ = this->allocator_.allocate(size); + this->reset(); + if (!this->buffer_) { + ESP_LOGE(TAG, "Initial allocation of download buffer failed!"); + this->size_ = 0; + } +} + uint8_t *DownloadBuffer::data(size_t offset) { if (offset > this->size_) { ESP_LOGE(TAG, "Tried to access beyond download buffer bounds!!!"); @@ -46,12 +55,13 @@ size_t DownloadBuffer::resize(size_t size) { return size; } this->allocator_.deallocate(this->buffer_, this->size_); - this->size_ = size; this->buffer_ = this->allocator_.allocate(size); this->reset(); if (this->buffer_) { + this->size_ = size; return size; } else { + this->size_ = 0; return 0; } } diff --git a/esphome/components/online_image/image_decoder.h b/esphome/components/online_image/image_decoder.h index 957af49ac9..d11b8b46d3 100644 --- a/esphome/components/online_image/image_decoder.h +++ b/esphome/components/online_image/image_decoder.h @@ -29,8 +29,12 @@ class ImageDecoder { * @brief Initialize the decoder. * * @param download_size The total number of bytes that need to be downloaded for the image. + * @return int Returns 0 on success, a {@see DecodeError} value in case of an error. */ - virtual void prepare(size_t download_size) { this->download_size_ = download_size; } + virtual int prepare(size_t download_size) { + this->download_size_ = download_size; + return 0; + } /** * @brief Decode a part of the image. It will try reading from the buffer. @@ -83,10 +87,7 @@ class ImageDecoder { class DownloadBuffer { public: - DownloadBuffer(size_t size) : size_(size) { - this->buffer_ = this->allocator_.allocate(size); - this->reset(); - } + DownloadBuffer(size_t size); virtual ~DownloadBuffer() { this->allocator_.deallocate(this->buffer_, this->size_); } diff --git a/esphome/components/online_image/jpeg_image.cpp b/esphome/components/online_image/jpeg_image.cpp index 773b85a2c4..0aff576da8 100644 --- a/esphome/components/online_image/jpeg_image.cpp +++ b/esphome/components/online_image/jpeg_image.cpp @@ -41,13 +41,14 @@ static int draw_callback(JPEGDRAW *jpeg) { return 1; } -void JpegDecoder::prepare(size_t download_size) { +int JpegDecoder::prepare(size_t download_size) { ImageDecoder::prepare(download_size); auto size = this->image_->resize_download_buffer(download_size); if (size < download_size) { - ESP_LOGE(TAG, "Resize failed!"); - // TODO: return an error code; + ESP_LOGE(TAG, "Download buffer resize failed!"); + return DECODE_ERROR_OUT_OF_MEMORY; } + return 0; } int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { diff --git a/esphome/components/online_image/jpeg_image.h b/esphome/components/online_image/jpeg_image.h index f04a35655a..fd488d6138 100644 --- a/esphome/components/online_image/jpeg_image.h +++ b/esphome/components/online_image/jpeg_image.h @@ -21,7 +21,7 @@ class JpegDecoder : public ImageDecoder { JpegDecoder(OnlineImage *image) : ImageDecoder(image) {} ~JpegDecoder() override {} - void prepare(size_t download_size) override; + int prepare(size_t download_size) override; int HOT decode(uint8_t *buffer, size_t size) override; protected: diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index a1c530c2ab..3b3d00a044 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -178,7 +178,12 @@ void OnlineImage::update() { this->download_error_callback_.call(); return; } - this->decoder_->prepare(total_size); + auto prepare_result = this->decoder_->prepare(total_size); + if (prepare_result < 0) { + this->end_connection_(); + this->download_error_callback_.call(); + return; + } ESP_LOGI(TAG, "Downloading image (Size: %d)", total_size); this->start_time_ = ::time(nullptr); } diff --git a/esphome/components/online_image/png_image.cpp b/esphome/components/online_image/png_image.cpp index 6bc968c7ba..fc5fb554bf 100644 --- a/esphome/components/online_image/png_image.cpp +++ b/esphome/components/online_image/png_image.cpp @@ -40,11 +40,16 @@ static void draw_callback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, ui decoder->draw(x, y, w, h, color); } -void PngDecoder::prepare(size_t download_size) { +int PngDecoder::prepare(size_t download_size) { ImageDecoder::prepare(download_size); + if (!this->pngle_) { + ESP_LOGE(TAG, "PNG decoder engine not initialized!"); + return DECODE_ERROR_OUT_OF_MEMORY; + } pngle_set_user_data(this->pngle_, this); pngle_set_init_callback(this->pngle_, init_callback); pngle_set_draw_callback(this->pngle_, draw_callback); + return 0; } int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { diff --git a/esphome/components/online_image/png_image.h b/esphome/components/online_image/png_image.h index c137227907..39f445c588 100644 --- a/esphome/components/online_image/png_image.h +++ b/esphome/components/online_image/png_image.h @@ -21,7 +21,7 @@ class PngDecoder : public ImageDecoder { PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {} ~PngDecoder() override { pngle_destroy(this->pngle_); } - void prepare(size_t download_size) override; + int prepare(size_t download_size) override; int HOT decode(uint8_t *buffer, size_t size) override; protected: From 46d19d82c2df3885690ed3f67a78e95728bafb77 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 11 Feb 2025 12:14:59 -0600 Subject: [PATCH 0992/1052] [speaker] Bugfix: Ensure all audio is played after completely decoding a file (#8231) --- .../components/speaker/media_player/audio_pipeline.cpp | 10 +++++++++- .../components/speaker/media_player/audio_pipeline.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/esphome/components/speaker/media_player/audio_pipeline.cpp b/esphome/components/speaker/media_player/audio_pipeline.cpp index 73ec5a3334..b49cf3ddda 100644 --- a/esphome/components/speaker/media_player/audio_pipeline.cpp +++ b/esphome/components/speaker/media_player/audio_pipeline.cpp @@ -182,13 +182,21 @@ AudioPipelineState AudioPipeline::process_state() { if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) { // Stop command is fully processed, so clear the command bit xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP); + this->hard_stop_ = true; } if (!this->is_playing_) { // The tasks have been stopped for two ``process_state`` calls in a row, so delete the tasks if ((this->read_task_handle_ != nullptr) || (this->decode_task_handle_ != nullptr)) { this->delete_tasks_(); - this->speaker_->stop(); + if (this->hard_stop_) { + // Stop command was sent, so immediately end of the playback + this->speaker_->stop(); + this->hard_stop_ = false; + } else { + // Decoded all the audio, so let the speaker finish playing before stopping + this->speaker_->finish(); + } } } this->is_playing_ = false; diff --git a/esphome/components/speaker/media_player/audio_pipeline.h b/esphome/components/speaker/media_player/audio_pipeline.h index c382e1eebe..722d9cbb2a 100644 --- a/esphome/components/speaker/media_player/audio_pipeline.h +++ b/esphome/components/speaker/media_player/audio_pipeline.h @@ -112,6 +112,7 @@ class AudioPipeline { uint32_t playback_ms_{0}; + bool hard_stop_{false}; bool is_playing_{false}; bool pause_state_{false}; bool task_stack_in_psram_; From 33f9d66e8188ce68c7083aac909857cdd5281069 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Tue, 11 Feb 2025 12:20:39 -0600 Subject: [PATCH 0993/1052] [voice_assistant] Add announce support (#8232) --- .../voice_assistant/voice_assistant.cpp | 38 ++++++++++++------- .../voice_assistant/voice_assistant.h | 8 ++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index 6f164f69d3..4b02867967 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -1,4 +1,5 @@ #include "voice_assistant.h" +#include "esphome/core/defines.h" #ifdef USE_VOICE_ASSISTANT @@ -127,7 +128,7 @@ void VoiceAssistant::clear_buffers_() { } #ifdef USE_SPEAKER - if (this->speaker_buffer_ != nullptr) { + if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { memset(this->speaker_buffer_, 0, SPEAKER_BUFFER_SIZE); this->speaker_buffer_size_ = 0; @@ -159,7 +160,7 @@ void VoiceAssistant::deallocate_buffers_() { this->input_buffer_ = nullptr; #ifdef USE_SPEAKER - if (this->speaker_buffer_ != nullptr) { + if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { ExternalRAMAllocator speaker_deallocator(ExternalRAMAllocator::ALLOW_FAILURE); speaker_deallocator.deallocate(this->speaker_buffer_, SPEAKER_BUFFER_SIZE); this->speaker_buffer_ = nullptr; @@ -389,14 +390,7 @@ void VoiceAssistant::loop() { } #endif if (playing) { - this->set_timeout("playing", 2000, [this]() { - this->cancel_timeout("speaker-timeout"); - this->set_state_(State::IDLE, State::IDLE); - - api::VoiceAssistantAnnounceFinished msg; - msg.success = true; - this->api_client_->send_voice_assistant_announce_finished(msg); - }); + this->start_playback_timeout_(); } break; } @@ -614,6 +608,8 @@ void VoiceAssistant::request_stop() { this->desired_state_ = State::IDLE; break; case State::AWAITING_RESPONSE: + this->signal_stop_(); + break; case State::STREAMING_RESPONSE: case State::RESPONSE_FINISHED: break; // Let the incoming audio stream finish then it will go to idle. @@ -631,6 +627,17 @@ void VoiceAssistant::signal_stop_() { this->api_client_->send_voice_assistant_request(msg); } +void VoiceAssistant::start_playback_timeout_() { + this->set_timeout("playing", 100, [this]() { + this->cancel_timeout("speaker-timeout"); + this->set_state_(State::IDLE, State::IDLE); + + api::VoiceAssistantAnnounceFinished msg; + msg.success = true; + this->api_client_->send_voice_assistant_announce_finished(msg); + }); +} + void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { ESP_LOGD(TAG, "Event Type: %" PRId32, msg.event_type); switch (msg.event_type) { @@ -715,6 +722,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { #ifdef USE_MEDIA_PLAYER if (this->media_player_ != nullptr) { this->media_player_->make_call().set_media_url(url).set_announcement(true).perform(); + // Start the playback timeout, as the media player state isn't immediately updated + this->start_playback_timeout_(); } #endif this->tts_end_trigger_->trigger(url); @@ -725,7 +734,11 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { } case api::enums::VOICE_ASSISTANT_RUN_END: { ESP_LOGD(TAG, "Assist Pipeline ended"); - if (this->state_ == State::STREAMING_MICROPHONE) { + if ((this->state_ == State::STARTING_PIPELINE) || (this->state_ == State::AWAITING_RESPONSE)) { + // Pipeline ended before starting microphone + // Or there wasn't a TTS start event ("nevermind") + this->set_state_(State::IDLE, State::IDLE); + } else if (this->state_ == State::STREAMING_MICROPHONE) { this->ring_buffer_->reset(); #ifdef USE_ESP_ADF if (this->use_wake_word_) { @@ -736,9 +749,6 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { { this->set_state_(State::IDLE, State::IDLE); } - } else if (this->state_ == State::AWAITING_RESPONSE) { - // No TTS start event ("nevermind") - this->set_state_(State::IDLE, State::IDLE); } this->defer([this]() { this->end_trigger_->trigger(); }); break; diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index 0016d3157c..12124c1486 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -40,6 +40,7 @@ enum VoiceAssistantFeature : uint32_t { FEATURE_SPEAKER = 1 << 1, FEATURE_API_AUDIO = 1 << 2, FEATURE_TIMERS = 1 << 3, + FEATURE_ANNOUNCE = 1 << 4, }; enum class State { @@ -136,6 +137,12 @@ class VoiceAssistant : public Component { flags |= VoiceAssistantFeature::FEATURE_TIMERS; } +#ifdef USE_MEDIA_PLAYER + if (this->media_player_ != nullptr) { + flags |= VoiceAssistantFeature::FEATURE_ANNOUNCE; + } +#endif + return flags; } @@ -209,6 +216,7 @@ class VoiceAssistant : public Component { void set_state_(State state); void set_state_(State state, State desired_state); void signal_stop_(); + void start_playback_timeout_(); std::unique_ptr socket_ = nullptr; struct sockaddr_storage dest_addr_; From 6b3f3e1da6ed7725eaa5bacc65b069190f2787e3 Mon Sep 17 00:00:00 2001 From: Jordan Zucker Date: Tue, 11 Feb 2025 11:51:55 -0800 Subject: [PATCH 0994/1052] [prometheus] Adding valve entity metrics (#8223) --- .../prometheus/prometheus_handler.cpp | 54 +++++++++++++++++++ .../prometheus/prometheus_handler.h | 8 +++ tests/components/prometheus/common.yaml | 8 +++ 3 files changed, 70 insertions(+) diff --git a/esphome/components/prometheus/prometheus_handler.cpp b/esphome/components/prometheus/prometheus_handler.cpp index c31d34f000..794df299a1 100644 --- a/esphome/components/prometheus/prometheus_handler.cpp +++ b/esphome/components/prometheus/prometheus_handler.cpp @@ -83,6 +83,12 @@ void PrometheusHandler::handleRequest(AsyncWebServerRequest *req) { this->update_entity_row_(stream, obj, area, node, friendly_name); #endif +#ifdef USE_VALVE + this->valve_type_(stream); + for (auto *obj : App.get_valves()) + this->valve_row_(stream, obj, area, node, friendly_name); +#endif + req->send(stream); } @@ -770,6 +776,54 @@ void PrometheusHandler::update_entity_row_(AsyncResponseStream *stream, update:: } #endif +#ifdef USE_VALVE +void PrometheusHandler::valve_type_(AsyncResponseStream *stream) { + stream->print(F("#TYPE esphome_valve_operation gauge\n")); + stream->print(F("#TYPE esphome_valve_failed gauge\n")); + stream->print(F("#TYPE esphome_valve_position gauge\n")); +} + +void PrometheusHandler::valve_row_(AsyncResponseStream *stream, valve::Valve *obj, std::string &area, std::string &node, + std::string &friendly_name) { + if (obj->is_internal() && !this->include_internal_) + return; + stream->print(F("esphome_valve_failed{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} 0\n")); + // Data itself + stream->print(F("esphome_valve_operation{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\",operation=\"")); + stream->print(valve::valve_operation_to_str(obj->current_operation)); + stream->print(F("\"} ")); + stream->print(F("1.0")); + stream->print(F("\n")); + // Now see if position is supported + if (obj->get_traits().get_supports_position()) { + stream->print(F("esphome_valve_position{id=\"")); + stream->print(relabel_id_(obj).c_str()); + add_area_label_(stream, area); + add_node_label_(stream, node); + add_friendly_name_label_(stream, friendly_name); + stream->print(F("\",name=\"")); + stream->print(relabel_name_(obj).c_str()); + stream->print(F("\"} ")); + stream->print(obj->position); + stream->print(F("\n")); + } +} +#endif + } // namespace prometheus } // namespace esphome #endif diff --git a/esphome/components/prometheus/prometheus_handler.h b/esphome/components/prometheus/prometheus_handler.h index 08a6e8dc8a..b77dbc462b 100644 --- a/esphome/components/prometheus/prometheus_handler.h +++ b/esphome/components/prometheus/prometheus_handler.h @@ -161,6 +161,14 @@ class PrometheusHandler : public AsyncWebHandler, public Component { void handle_update_state_(AsyncResponseStream *stream, update::UpdateState state); #endif +#ifdef USE_VALVE + /// Return the type for prometheus + void valve_type_(AsyncResponseStream *stream); + /// Return the valve state as prometheus data point + void valve_row_(AsyncResponseStream *stream, valve::Valve *obj, std::string &area, std::string &node, + std::string &friendly_name); +#endif + web_server_base::WebServerBase *base_; bool include_internal_{false}; std::map relabel_map_id_; diff --git a/tests/components/prometheus/common.yaml b/tests/components/prometheus/common.yaml index 9205c27f2a..7c226b6782 100644 --- a/tests/components/prometheus/common.yaml +++ b/tests/components/prometheus/common.yaml @@ -121,6 +121,14 @@ number: max_value: 100 step: 1 +valve: + - platform: template + name: "Template Valve" + lambda: |- + return VALVE_OPEN; + optimistic: true + has_position: true + prometheus: include_internal: true relabel: From 14d7931bd6b12d4b91846af1a83196a9b06651e2 Mon Sep 17 00:00:00 2001 From: tmpeh <41875356+tmpeh@users.noreply.github.com> Date: Tue, 11 Feb 2025 21:41:52 +0100 Subject: [PATCH 0995/1052] Added Waveshare e-paper display model "7.50inv2p" to the waveshare_epaper component. (#7751) Co-authored-by: Tim Pehla Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- .../components/waveshare_epaper/display.py | 4 + .../waveshare_epaper/waveshare_epaper.cpp | 203 ++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 37 ++++ tests/components/waveshare_epaper/common.yaml | 20 ++ 4 files changed, 264 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index f0064c6a4d..2d8619bc5c 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -91,6 +91,9 @@ WaveshareEPaper7P5InV2 = waveshare_epaper_ns.class_( WaveshareEPaper7P5InV2alt = waveshare_epaper_ns.class_( "WaveshareEPaper7P5InV2alt", WaveshareEPaper ) +WaveshareEPaper7P5InV2P = waveshare_epaper_ns.class_( + "WaveshareEPaper7P5InV2P", WaveshareEPaper +) WaveshareEPaper7P5InHDB = waveshare_epaper_ns.class_( "WaveshareEPaper7P5InHDB", WaveshareEPaper ) @@ -144,6 +147,7 @@ MODELS = { "7.50in-bc": ("b", WaveshareEPaper7P5InBC), "7.50inv2": ("b", WaveshareEPaper7P5InV2), "7.50inv2alt": ("b", WaveshareEPaper7P5InV2alt), + "7.50inv2p": ("c", WaveshareEPaper7P5InV2P), "7.50in-hd-b": ("b", WaveshareEPaper7P5InHDB), "2.13in-ttgo-dke": ("c", WaveshareEPaper2P13InDKE), "2.13inv3": ("c", WaveshareEPaper2P13InV3), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index f240c98096..9f8c32721d 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -3100,6 +3100,209 @@ void WaveshareEPaper7P5InV2alt::dump_config() { LOG_UPDATE_INTERVAL(this); } +/* 7.50inV2 with partial and fast refresh */ +bool WaveshareEPaper7P5InV2P::wait_until_idle_() { + if (this->busy_pin_ == nullptr) { + return true; + } + + const uint32_t start = millis(); + while (this->busy_pin_->digital_read()) { + this->command(0x71); + if (millis() - start > this->idle_timeout_()) { + ESP_LOGE(TAG, "Timeout while displaying image!"); + return false; + } + App.feed_wdt(); + delay(10); + } + return true; +} + +void WaveshareEPaper7P5InV2P::reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(true); + delay(20); + this->reset_pin_->digital_write(false); + delay(2); + this->reset_pin_->digital_write(true); + delay(20); + } +} + +void WaveshareEPaper7P5InV2P::turn_on_display_() { + this->command(0x12); + delay(100); // NOLINT + this->wait_until_idle_(); +} + +void WaveshareEPaper7P5InV2P::initialize() { + this->reset_(); + + // COMMAND POWER SETTING + this->command(0x01); + this->data(0x07); + this->data(0x07); + this->data(0x3f); + this->data(0x3f); + + // COMMAND BOOSTER SOFT START + this->command(0x06); + this->data(0x17); + this->data(0x17); + this->data(0x28); + this->data(0x17); + + // COMMAND POWER DRIVER HAT UP + this->command(0x04); + delay(100); // NOLINT + this->wait_until_idle_(); + + // COMMAND PANEL SETTING + this->command(0x00); + this->data(0x1F); + + // COMMAND RESOLUTION SETTING + this->command(0x61); + this->data(0x03); + this->data(0x20); + this->data(0x01); + this->data(0xE0); + + // COMMAND DUAL SPI MM_EN, DUSPI_EN + this->command(0x15); + this->data(0x00); + + // COMMAND VCOM AND DATA INTERVAL SETTING + this->command(0x50); + this->data(0x10); + this->data(0x07); + + // COMMAND TCON SETTING + this->command(0x60); + this->data(0x22); + + // COMMAND ENABLE FAST UPDATE + this->command(0xE0); + this->data(0x02); + this->command(0xE5); + this->data(0x5A); + + // COMMAND POWER DRIVER HAT DOWN + this->command(0x02); +} + +void HOT WaveshareEPaper7P5InV2P::display() { + uint32_t buf_len = this->get_buffer_length_(); + + // COMMAND POWER ON + ESP_LOGI(TAG, "Power on the display and hat"); + + this->command(0x04); + delay(200); // NOLINT + this->wait_until_idle_(); + + if (this->full_update_every_ == 1) { + this->command(0x13); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(~(this->buffer_[i])); + } + + this->turn_on_display_(); + + this->command(0x02); + this->wait_until_idle_(); + return; + } + + this->command(0x50); + this->data(0xA9); + this->data(0x07); + + if (this->at_update_ == 0) { + // Enable fast refresh + this->command(0xE5); + this->data(0x5A); + + this->command(0x92); + + this->command(0x10); + delay(2); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(~(this->buffer_[i])); + } + + delay(100); // NOLINT + this->wait_until_idle_(); + + this->command(0x13); + delay(2); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(this->buffer_[i]); + } + + delay(100); // NOLINT + this->wait_until_idle_(); + + this->turn_on_display_(); + + } else { + // Enable partial refresh + this->command(0xE5); + this->data(0x6E); + + // Activate partial refresh and set window bounds + this->command(0x91); + this->command(0x90); + + this->data(0x00); + this->data(0x00); + this->data((get_width_internal() - 1) >> 8 & 0xFF); + this->data((get_width_internal() - 1) & 0xFF); + + this->data(0x00); + this->data(0x00); + this->data((get_height_internal() - 1) >> 8 & 0xFF); + this->data((get_height_internal() - 1) & 0xFF); + + this->data(0x01); + + this->command(0x13); + delay(2); + for (uint32_t i = 0; i < buf_len; i++) { + this->data(this->buffer_[i]); + } + + delay(100); // NOLINT + this->wait_until_idle_(); + + this->turn_on_display_(); + } + + ESP_LOGV(TAG, "Before command(0x02) (>> power off)"); + this->command(0x02); + this->wait_until_idle_(); + ESP_LOGV(TAG, "After command(0x02) (>> power off)"); + + this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; +} + +int WaveshareEPaper7P5InV2P::get_width_internal() { return 800; } +int WaveshareEPaper7P5InV2P::get_height_internal() { return 480; } +uint32_t WaveshareEPaper7P5InV2P::idle_timeout_() { return 10000; } +void WaveshareEPaper7P5InV2P::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 7.50inv2p"); + ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} +void WaveshareEPaper7P5InV2P::set_full_update_every(uint32_t full_update_every) { + this->full_update_every_ = full_update_every; +} + /* 7.50in-bc */ void WaveshareEPaper7P5InBC::initialize() { /* The command sequence is similar to the 7P5In display but differs in subtle ways diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index c2277f2962..73090fa009 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -775,6 +775,43 @@ class WaveshareEPaper7P5InV2alt : public WaveshareEPaper7P5InV2 { }; }; +class WaveshareEPaper7P5InV2P : public WaveshareEPaper { + public: + bool wait_until_idle_(); + + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND POWER OFF + this->command(0x02); + this->wait_until_idle_(); + // COMMAND DEEP SLEEP + this->command(0x07); + this->data(0xA5); // check byte + } + + void set_full_update_every(uint32_t full_update_every); + + protected: + int get_width_internal() override; + + int get_height_internal() override; + + uint32_t idle_timeout_() override; + + uint32_t full_update_every_{30}; + uint32_t at_update_{0}; + + private: + void reset_(); + + void turn_on_display_(); +}; + class WaveshareEPaper7P5InHDB : public WaveshareEPaper { public: void initialize() override; diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index 4a67ccd67d..667bbb1bb0 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -675,6 +675,26 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + id: epd_7_50inv2p + model: 7.50inv2p + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper id: epd_7_50hdb model: 7.50in-hd-b From 0401ee94282c3b8e436c3983e23233171fad0c77 Mon Sep 17 00:00:00 2001 From: Rachasak Ragkamnerd Date: Wed, 12 Feb 2025 04:35:07 +0700 Subject: [PATCH 0996/1052] added Waveshare BWR Mode for the 4.2in Display (#7995) Co-authored-by: rrachasak Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- .../components/waveshare_epaper/display.py | 4 ++ .../waveshare_epaper/waveshare_epaper.cpp | 58 +++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 28 +++++++++ tests/components/waveshare_epaper/common.yaml | 19 ++++++ 4 files changed, 109 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 2d8619bc5c..f6e56c543d 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -64,6 +64,9 @@ WaveshareEPaper4P2In = waveshare_epaper_ns.class_( WaveshareEPaper4P2InBV2 = waveshare_epaper_ns.class_( "WaveshareEPaper4P2InBV2", WaveshareEPaper ) +WaveshareEPaper4P2InBV2BWR = waveshare_epaper_ns.class_( + "WaveshareEPaper4P2InBV2BWR", WaveshareEPaperBWR +) WaveshareEPaper5P8In = waveshare_epaper_ns.class_( "WaveshareEPaper5P8In", WaveshareEPaper ) @@ -138,6 +141,7 @@ MODELS = { "2.90in-dke": ("c", WaveshareEPaper2P9InDKE), "4.20in": ("b", WaveshareEPaper4P2In), "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), + "4.20in-bv2-bwr": ("b", WaveshareEPaper4P2InBV2BWR), "5.83in": ("b", WaveshareEPaper5P8In), "5.83inv2": ("b", WaveshareEPaper5P8InV2), "7.50in": ("b", WaveshareEPaper7P5In), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 9f8c32721d..a4439e2d7d 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -2274,6 +2274,64 @@ void WaveshareEPaper4P2InBV2::dump_config() { LOG_UPDATE_INTERVAL(this); } +// ======================================================== +// 4.20in Type B With Red colour support (LUT from OTP) +// Datasheet: +// - https://www.waveshare.com/w/upload/2/20/4.2inch-e-paper-module-user-manual-en.pdf +// - https://github.com/waveshare/e-Paper/blob/master/RaspberryPi_JetsonNano/c/lib/e-Paper/EPD_4in2b_V2.c +// The implementation is an adaptation of WaveshareEPaper4P2InBV2 class +// ======================================================== +void WaveshareEPaper4P2InBV2BWR::initialize() { + // these exact timings are required for a proper reset/init + this->reset_pin_->digital_write(false); + delay(2); + this->reset_pin_->digital_write(true); + delay(200); // NOLINT + + // COMMAND POWER ON + this->command(0x04); + this->wait_until_idle_(); + + // COMMAND PANEL SETTING + this->command(0x00); + this->data(0x0f); // LUT from OTP +} + +void HOT WaveshareEPaper4P2InBV2BWR::display() { + const uint32_t buf_len = this->get_buffer_length_() / 2u; + + this->command(0x10); // Send BW data Transmission + delay(2); // Delay to prevent Watchdog error + for (uint32_t i = 0; i < buf_len; ++i) { + this->data(this->buffer_[i]); + } + + this->command(0x13); // Send red data Transmission + delay(2); // Delay to prevent Watchdog error + for (uint32_t i = 0; i < buf_len; ++i) { + // Red color need to flip bit from the buffer. Otherwise, red will conqure the screen! + this->data(~this->buffer_[buf_len + i]); + } + + // COMMAND DISPLAY REFRESH + this->command(0x12); + this->wait_until_idle_(); + + // COMMAND POWER OFF + // NOTE: power off < deep sleep + this->command(0x02); +} +int WaveshareEPaper4P2InBV2BWR::get_width_internal() { return 400; } +int WaveshareEPaper4P2InBV2BWR::get_height_internal() { return 300; } +void WaveshareEPaper4P2InBV2BWR::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 4.2in (B V2) BWR-Mode"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + void WaveshareEPaper5P8In::initialize() { // COMMAND POWER SETTING this->command(0x01); diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 73090fa009..4d811228f1 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -518,6 +518,34 @@ class WaveshareEPaper4P2InBV2 : public WaveshareEPaper { int get_height_internal() override; }; +class WaveshareEPaper4P2InBV2BWR : public WaveshareEPaperBWR { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND VCOM AND DATA INTERVAL SETTING + this->command(0x50); + this->data(0xF7); // border floating + + // COMMAND POWER OFF + this->command(0x02); + this->wait_until_idle_(); + + // COMMAND DEEP SLEEP + this->command(0x07); + this->data(0xA5); // check code + } + + protected: + int get_width_internal() override; + + int get_height_internal() override; +}; + class WaveshareEPaper5P8In : public WaveshareEPaper { public: void initialize() override; diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index 667bbb1bb0..5889b4659e 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -502,6 +502,25 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + id: epd_4_20in_bv2_bwr + model: 4.20in-bv2-bwr + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + # 5.83 inch displays - platform: waveshare_epaper id: epd_5_83 From a2f1b902388f6d8c89b2f332199273e479beed65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20Koek?= Date: Tue, 11 Feb 2025 23:16:33 +0000 Subject: [PATCH 0997/1052] Add GDEY029T94 support (#7931) Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> --- .../components/waveshare_epaper/display.py | 2 + .../waveshare_epaper/waveshare_epaper.cpp | 75 ++++++++++++++++++- .../waveshare_epaper/waveshare_epaper.h | 19 +++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index f6e56c543d..7b51eb338c 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -52,6 +52,7 @@ WaveshareEPaper2P9InV2R2 = waveshare_epaper_ns.class_( "WaveshareEPaper2P9InV2R2", WaveshareEPaper ) GDEW029T5 = waveshare_epaper_ns.class_("GDEW029T5", WaveshareEPaper) +GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper) WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_( "WaveshareEPaper2P9InDKE", WaveshareEPaper ) @@ -136,6 +137,7 @@ MODELS = { "2.70inv2": ("b", WaveshareEPaper2P7InV2), "2.90in-b": ("b", WaveshareEPaper2P9InB), "2.90in-bv3": ("b", WaveshareEPaper2P9InBV3), + "gdey029t94": ("c", GDEY029T94), "2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2), "2.90in-d": ("b", WaveshareEPaper2P9InD), "2.90in-dke": ("c", WaveshareEPaper2P9InDKE), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index a4439e2d7d..1e8671bfa6 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -1677,9 +1677,82 @@ int WaveshareEPaper2P9InV2R2::get_width_controller() { return this->get_width_in void WaveshareEPaper2P9InV2R2::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } +// ======================================================== +// Good Display 2.9in black/white +// Datasheet: +// - https://files.seeedstudio.com/wiki/Other_Display/29-epaper/GDEY029T94.pdf +// - +// https://github.com/Allen-Kuang/e-ink_Demo/blob/main/2.9%20inch%20E-paper%20-%20monocolor%20128x296/example/Display_EPD_W21.cpp +// ======================================================== + +void GDEY029T94::initialize() { + // EPD hardware init start + this->reset_(); + + this->wait_until_idle_(); + this->command(0x12); // SWRESET + this->wait_until_idle_(); + + this->command(0x01); // Driver output control + this->data((this->get_height_internal() - 1) % 256); + this->data((this->get_height_internal() - 1) / 256); + this->data(0x00); + + this->command(0x11); // data entry mode + this->data(0x03); + + this->command(0x44); // set Ram-X address start/end position + this->data(0x00); + this->data(this->get_width_internal() / 8 - 1); + + this->command(0x45); // set Ram-Y address start/end position + this->data(0x00); + this->data(0x00); + this->data((this->get_height_internal() - 1) % 256); + this->data((this->get_height_internal() - 1) / 256); + + this->command(0x3C); // BorderWavefrom + this->data(0x05); + + this->command(0x21); // Display update control + this->data(0x00); + this->data(0x80); + + this->command(0x18); // Read built-in temperature sensor + this->data(0x80); + + this->command(0x4E); // set RAM x address count to 0; + this->data(0x00); + this->command(0x4F); // set RAM y address count to 0X199; + this->command(0x00); + this->command(0x00); + this->wait_until_idle_(); +} +void HOT GDEY029T94::display() { + this->command(0x24); // write RAM for black(0)/white (1) + this->start_data_(); + for (uint32_t i = 0; i < this->get_buffer_length_(); i++) { + this->write_byte(this->buffer_[i]); + } + this->end_data_(); + this->command(0x22); // Display Update Control + this->data(0xF7); + this->command(0x20); // Activate Display Update Sequence + this->wait_until_idle_(); +} +int GDEY029T94::get_width_internal() { return 128; } +int GDEY029T94::get_height_internal() { return 296; } +void GDEY029T94::dump_config() { + LOG_DISPLAY("", "E-Paper (Good Display)", this); + ESP_LOGCONFIG(TAG, " Model: 2.9in GDEY029T94"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} // ======================================================== -// Good Display 2.9in black/white/grey +// Good Display 2.9in black/white // Datasheet: // - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1680.pdf // - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 4d811228f1..54e7619ebc 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -277,6 +277,25 @@ class GDEW029T5 : public WaveshareEPaper { uint8_t *old_buffer_{nullptr}; }; +class GDEY029T94 : public WaveshareEPaper { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + this->command(0x10); // Enter deep sleep + this->data(0x01); + } + + protected: + int get_width_internal() override; + + int get_height_internal() override; +}; + class WaveshareEPaper2P7InV2 : public WaveshareEPaper { public: void initialize() override; From 88cfdc33d458475c4dc3fa67653f7abf355dd7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Gr=C3=BCner?= Date: Wed, 12 Feb 2025 00:17:34 +0100 Subject: [PATCH 0998/1052] GDEY042T81 e-paper displays support (#8061) Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com> --- .../components/waveshare_epaper/display.py | 2 + .../waveshare_epaper/waveshare_epaper.cpp | 200 ++++++++++++++++++ .../waveshare_epaper/waveshare_epaper.h | 37 ++++ tests/components/waveshare_epaper/common.yaml | 20 ++ 4 files changed, 259 insertions(+) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 7b51eb338c..72823e4e36 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -56,6 +56,7 @@ GDEY029T94 = waveshare_epaper_ns.class_("GDEY029T94", WaveshareEPaper) WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_( "WaveshareEPaper2P9InDKE", WaveshareEPaper ) +GDEY042T81 = waveshare_epaper_ns.class_("GDEY042T81", WaveshareEPaper) WaveshareEPaper2P9InD = waveshare_epaper_ns.class_( "WaveshareEPaper2P9InD", WaveshareEPaper ) @@ -141,6 +142,7 @@ MODELS = { "2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2), "2.90in-d": ("b", WaveshareEPaper2P9InD), "2.90in-dke": ("c", WaveshareEPaper2P9InDKE), + "gdey042t81": ("c", GDEY042T81), "4.20in": ("b", WaveshareEPaper4P2In), "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), "4.20in-bv2-bwr": ("b", WaveshareEPaper4P2InBV2BWR), diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 1e8671bfa6..2a540a1b75 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -2169,6 +2169,206 @@ void GDEW0154M09::dump_config() { LOG_UPDATE_INTERVAL(this); } +// ======================================================== +// Good Display 4.2in black/white GDEY042T81 (SSD1683) +// Product page: +// - https://www.good-display.com/product/386.html +// Datasheet: +// - https://v4.cecdn.yun300.cn/100001_1909185148/GDEY042T81.pdf +// - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1683.PDF +// Reference code from GoodDisplay: +// - https://www.good-display.com/companyfile/1572.html (2024-08-01 15:40:41) +// Other reference code: +// - https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp +// ======================================================== + +void GDEY042T81::initialize() { + this->init_display_(); + ESP_LOGD(TAG, "Initialization complete, set the display to deep sleep"); + this->deep_sleep(); +} + +// conflicting documentation / examples regarding reset timings +// https://v4.cecdn.yun300.cn/100001_1909185148/SSD1683.PDF -> 10ms +// GD sample code (Display_EPD_W21.cpp, see above) -> 10 ms +// https://v4.cecdn.yun300.cn/100001_1909185148/GDEY042T81.pdf (section 14.2) -> 0.2ms (200us) +// https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L351 +// -> 10ms +// 10 ms seems to work, so we use this +GDEY042T81::GDEY042T81() { this->reset_duration_ = 10; } + +void GDEY042T81::reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(false); + delay(reset_duration_); // NOLINT + this->reset_pin_->digital_write(true); + delay(reset_duration_); // NOLINT + } +} + +void GDEY042T81::init_display_() { + this->reset_(); + + this->wait_until_idle_(); + this->command(0x12); // SWRESET + this->wait_until_idle_(); + + // Specify number of lines for the driver: 300 (MUX 300) + // https://v4.cecdn.yun300.cn/100001_1909185148/SSD1683.PDF (section 8.1) + // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L354 + this->command(0x01); // driver output control + this->data(0x2B); // (height - 1) % 256 + this->data(0x01); // (height - 1) / 256 + this->data(0x00); + + // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L360 + this->command(0x3C); // BorderWaveform + this->data(0x01); + this->command(0x18); // Read built-in temperature sensor + this->data(0x80); + + // GD sample code (Display_EPD_W21.cpp@90ff) + this->command(0x11); // data entry mode + this->data(0x03); + // set windows (0,0,400,300) + this->command(0x44); // set Ram-X address start/end position + this->data(0); + this->data(0x31); // (width / 8 -1) + + this->command(0x45); // set Ram-y address start/end position + this->data(0); + this->data(0); + this->data(0x2B); // (height - 1) % 256 + this->data(0x01); // (height - 1) / 256 + + // set cursor (0,0) + this->command(0x4E); // set RAM x address count to 0; + this->data(0); + this->command(0x4F); // set RAM y address count to 0; + this->data(0); + this->data(0); + + this->wait_until_idle_(); +} + +// https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L366 +void GDEY042T81::update_full_() { + this->command(0x21); // display update control + this->data(0x40); // bypass RED as 0 + this->data(0x00); // single chip application + + // only ever do a fast update because slow updates are only relevant + // for lower operating temperatures + // see + // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_290_GDEY029T94.h#L30 + // + // Should slow/fast updates be made configurable similar to how GxEPD2 does it? No idea if anyone would need it... + this->command(0x1A); // Write to temperature register + this->data(0x6E); + this->command(0x22); + this->data(0xd7); + + this->command(0x20); + this->wait_until_idle_(); +} + +// https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L389 +void GDEY042T81::update_part_() { + this->command(0x21); // display update control + this->data(0x00); // RED normal + this->data(0x00); // single chip application + + this->command(0x22); + this->data(0xfc); + + this->command(0x20); + this->wait_until_idle_(); +} + +void HOT GDEY042T81::display() { + ESP_LOGD(TAG, "Wake up the display"); + this->init_display_(); + + if (!this->wait_until_idle_()) { + this->status_set_warning(); + ESP_LOGE(TAG, "Failed to perform update, display is busy"); + return; + } + + // basic code structure copied from WaveshareEPaper2P9InV2R2 + if (this->full_update_every_ == 1) { + ESP_LOGD(TAG, "Full update"); + // do single full update + this->command(0x24); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + // TurnOnDisplay + this->update_full_(); + return; + } + + // if (this->full_update_every_ == 1 || + if (this->at_update_ == 0) { + ESP_LOGD(TAG, "Update"); + // do base update + this->command(0x24); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + this->command(0x26); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + // TurnOnDisplay; + this->update_full_(); + } else { + // do partial update (full screen) + // no need to load a LUT for GoodDisplays as they seem to have the LUT onboard + // GD example code (Display_EPD_W21.cpp@283ff) + // + // not setting the BorderWaveform here again (contrary to the GD example) because according to + // https://github.com/ZinggJM/GxEPD2/blob/03d8e7a533c1493f762e392ead12f1bcb7fab8f9/src/gdey/GxEPD2_420_GDEY042T81.cpp#L358 + // it seems to be enough to set it during display initialization + ESP_LOGD(TAG, "Partial update"); + this->reset_(); + if (!this->wait_until_idle_()) { + this->status_set_warning(); + ESP_LOGE(TAG, "Failed to perform partial update, display is busy"); + return; + } + + this->command(0x24); + this->start_data_(); + this->write_array(this->buffer_, this->get_buffer_length_()); + this->end_data_(); + + // TurnOnDisplay + this->update_part_(); + } + + this->at_update_ = (this->at_update_ + 1) % this->full_update_every_; + this->wait_until_idle_(); + ESP_LOGD(TAG, "Set the display back to deep sleep"); + this->deep_sleep(); +} +void GDEY042T81::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; } +int GDEY042T81::get_width_internal() { return 400; } +int GDEY042T81::get_height_internal() { return 300; } +uint32_t GDEY042T81::idle_timeout_() { return 5000; } +void GDEY042T81::dump_config() { + LOG_DISPLAY("", "GoodDisplay E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 4.2in B/W GDEY042T81"); + ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + static const uint8_t LUT_VCOM_DC_4_2[] = { 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 54e7619ebc..1e7cb6c6c7 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -466,6 +466,43 @@ class WaveshareEPaper2P9InD : public WaveshareEPaper { int get_height_internal() override; }; +class GDEY042T81 : public WaveshareEPaper { + public: + GDEY042T81(); + + void initialize() override; + + void display() override; + + void dump_config() override; + + void deep_sleep() override { + // COMMAND POWER OFF + this->command(0x22); + this->data(0x83); + this->command(0x20); + // COMMAND DEEP SLEEP + this->command(0x10); + this->data(0x01); + } + + void set_full_update_every(uint32_t full_update_every); + + protected: + uint32_t full_update_every_{30}; + uint32_t at_update_{0}; + + int get_width_internal() override; + int get_height_internal() override; + uint32_t idle_timeout_() override; + + private: + void reset_(); + void update_full_(); + void update_part_(); + void init_display_(); +}; + class WaveshareEPaper4P2In : public WaveshareEPaper { public: void initialize() override; diff --git a/tests/components/waveshare_epaper/common.yaml b/tests/components/waveshare_epaper/common.yaml index 5889b4659e..09ba1af778 100644 --- a/tests/components/waveshare_epaper/common.yaml +++ b/tests/components/waveshare_epaper/common.yaml @@ -463,6 +463,26 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + - platform: waveshare_epaper + id: epd_gdew042t81 + model: gdey042t81 + spi_id: spi_waveshare_epaper + cs_pin: + allow_other_uses: true + number: ${cs_pin} + dc_pin: + allow_other_uses: true + number: ${dc_pin} + busy_pin: + allow_other_uses: true + number: ${busy_pin} + reset_pin: + allow_other_uses: true + number: ${reset_pin} + full_update_every: 30 + lambda: |- + it.rectangle(0, 0, it.get_width(), it.get_height()); + # 4.2 inch displays - platform: waveshare_epaper id: epd_4_20 From 7bb2c3c4968185d6cc4848986b0f3cdc71e7da5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Neil=20S=C3=A9gard?= <55683504+NeilSCGH@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:31:56 +0100 Subject: [PATCH 0999/1052] Add support for Waveshare 7.3" ACeP 7-Color display (#6380) --- .../components/waveshare_epaper/display.py | 5 + .../waveshare_epaper/waveshare_213v3.cpp | 6 +- .../waveshare_epaper/waveshare_epaper.cpp | 311 +++++++++++++++++- .../waveshare_epaper/waveshare_epaper.h | 56 +++- 4 files changed, 369 insertions(+), 9 deletions(-) diff --git a/esphome/components/waveshare_epaper/display.py b/esphome/components/waveshare_epaper/display.py index 72823e4e36..8acb6ac68f 100644 --- a/esphome/components/waveshare_epaper/display.py +++ b/esphome/components/waveshare_epaper/display.py @@ -24,6 +24,7 @@ WaveshareEPaper = waveshare_epaper_ns.class_("WaveshareEPaper", WaveshareEPaperB WaveshareEPaperBWR = waveshare_epaper_ns.class_( "WaveshareEPaperBWR", WaveshareEPaperBase ) +WaveshareEPaper7C = waveshare_epaper_ns.class_("WaveshareEPaper7C", WaveshareEPaperBase) WaveshareEPaperTypeA = waveshare_epaper_ns.class_( "WaveshareEPaperTypeA", WaveshareEPaper ) @@ -75,6 +76,9 @@ WaveshareEPaper5P8In = waveshare_epaper_ns.class_( WaveshareEPaper5P8InV2 = waveshare_epaper_ns.class_( "WaveshareEPaper5P8InV2", WaveshareEPaper ) +WaveshareEPaper7P3InF = waveshare_epaper_ns.class_( + "WaveshareEPaper7P3InF", WaveshareEPaper7C +) WaveshareEPaper7P5In = waveshare_epaper_ns.class_( "WaveshareEPaper7P5In", WaveshareEPaper ) @@ -148,6 +152,7 @@ MODELS = { "4.20in-bv2-bwr": ("b", WaveshareEPaper4P2InBV2BWR), "5.83in": ("b", WaveshareEPaper5P8In), "5.83inv2": ("b", WaveshareEPaper5P8InV2), + "7.30in-f": ("b", WaveshareEPaper7P3InF), "7.50in": ("b", WaveshareEPaper7P5In), "7.50in-bv2": ("b", WaveshareEPaper7P5InBV2), "7.50in-bv3": ("b", WaveshareEPaper7P5InBV3), diff --git a/esphome/components/waveshare_epaper/waveshare_213v3.cpp b/esphome/components/waveshare_epaper/waveshare_213v3.cpp index 85d7033d4b..316cd80ccd 100644 --- a/esphome/components/waveshare_epaper/waveshare_213v3.cpp +++ b/esphome/components/waveshare_epaper/waveshare_213v3.cpp @@ -87,7 +87,11 @@ void WaveshareEPaper2P13InV3::send_reset_() { } void WaveshareEPaper2P13InV3::setup() { - setup_pins_(); + this->init_internal_(this->get_buffer_length_()); + this->setup_pins_(); + this->spi_setup(); + this->reset_(); + delay(20); this->send_reset_(); // as a one-off delay this is not worth working around. diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index 2a540a1b75..96fc82fcdd 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -3,6 +3,7 @@ #include "esphome/core/application.h" #include "esphome/core/helpers.h" #include +#include namespace esphome { namespace waveshare_epaper { @@ -110,8 +111,14 @@ static const uint8_t PARTIAL_UPD_2IN9_LUT[PARTIAL_UPD_2IN9_LUT_SIZE] = }; // clang-format on -void WaveshareEPaperBase::setup_pins_() { +void WaveshareEPaperBase::setup() { this->init_internal_(this->get_buffer_length_()); + this->setup_pins_(); + this->spi_setup(); + this->reset_(); + this->initialize(); +} +void WaveshareEPaperBase::setup_pins_() { this->dc_pin_->setup(); // OUTPUT this->dc_pin_->digital_write(false); if (this->reset_pin_ != nullptr) { @@ -121,9 +128,6 @@ void WaveshareEPaperBase::setup_pins_() { if (this->busy_pin_ != nullptr) { this->busy_pin_->setup(); // INPUT } - this->spi_setup(); - - this->reset_(); } float WaveshareEPaperBase::get_setup_priority() const { return setup_priority::PROCESSOR; } void WaveshareEPaperBase::command(uint8_t value) { @@ -173,6 +177,87 @@ void WaveshareEPaper::fill(Color color) { for (uint32_t i = 0; i < this->get_buffer_length_(); i++) this->buffer_[i] = fill; } +void WaveshareEPaper7C::setup() { + this->init_internal_7c_(this->get_buffer_length_()); + this->setup_pins_(); + this->spi_setup(); + this->reset_(); + this->initialize(); +} +void WaveshareEPaper7C::init_internal_7c_(uint32_t buffer_length) { + ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); + uint32_t small_buffer_length = buffer_length / NUM_BUFFERS; + + for (int i = 0; i < NUM_BUFFERS; i++) { + this->buffers_[i] = allocator.allocate(small_buffer_length); + if (this->buffers_[i] == nullptr) { + ESP_LOGE(TAG, "Could not allocate buffer %d for display!", i); + for (auto &buffer : this->buffers_) { + allocator.deallocate(buffer, small_buffer_length); + buffer = nullptr; + } + return; + } + } + this->clear(); +} +uint8_t WaveshareEPaper7C::color_to_hex(Color color) { + uint8_t hex_code; + if (color.red > 127) { + if (color.green > 170) { + if (color.blue > 127) { + hex_code = 0x1; // White + } else { + hex_code = 0x5; // Yellow + } + } else if (color.green > 85) { + hex_code = 0x6; // Orange + } else { + hex_code = 0x4; // Red (or Magenta) + } + } else { + if (color.green > 127) { + if (color.blue > 127) { + hex_code = 0x3; // Cyan -> Blue + } else { + hex_code = 0x2; // Green + } + } else { + if (color.blue > 127) { + hex_code = 0x3; // Blue + } else { + hex_code = 0x0; // Black + } + } + } + + return hex_code; +} +void WaveshareEPaper7C::fill(Color color) { + uint8_t pixel_color; + if (color.is_on()) { + pixel_color = this->color_to_hex(color); + } else { + pixel_color = 0x1; + } + + if (this->buffers_[0] == nullptr) { + ESP_LOGE(TAG, "Buffer unavailable!"); + } else { + uint32_t small_buffer_length = this->get_buffer_length_() / NUM_BUFFERS; + for (auto &buffer : this->buffers_) { + for (uint32_t buffer_pos = 0; buffer_pos < small_buffer_length; buffer_pos += 3) { + // We store 8 bitset<3> in 3 bytes + // | byte 1 | byte 2 | byte 3 | + // |aaabbbaa|abbbaaab|bbaaabbb| + buffer[buffer_pos + 0] = pixel_color << 5 | pixel_color << 2 | pixel_color >> 1; + buffer[buffer_pos + 1] = pixel_color << 7 | pixel_color << 4 | pixel_color << 1 | pixel_color >> 2; + buffer[buffer_pos + 2] = pixel_color << 6 | pixel_color << 3 | pixel_color << 0; + } + App.feed_wdt(); + } + } +} void HOT WaveshareEPaper::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; @@ -193,6 +278,9 @@ uint32_t WaveshareEPaper::get_buffer_length_() { uint32_t WaveshareEPaperBWR::get_buffer_length_() { return this->get_width_controller() * this->get_height_internal() / 4u; } // black and red buffer +uint32_t WaveshareEPaper7C::get_buffer_length_() { + return this->get_width_controller() * this->get_height_internal() / 8u * 3u; +} // 7 colors buffer, 1 pixel = 3 bits, we will store 8 pixels in 24 bits = 3 bytes void WaveshareEPaperBWR::fill(Color color) { this->filled_rectangle(0, 0, this->get_width(), this->get_height(), color); @@ -219,7 +307,33 @@ void HOT WaveshareEPaperBWR::draw_absolute_pixel_internal(int x, int y, Color co this->buffer_[pos + buf_half_len] &= ~(0x80 >> subpos); } } +void HOT WaveshareEPaper7C::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; + uint8_t pixel_bits = this->color_to_hex(color); + uint32_t small_buffer_length = this->get_buffer_length_() / NUM_BUFFERS; + uint32_t pixel_position = x + y * this->get_width_controller(); + uint32_t first_bit_position = pixel_position * 3; + uint32_t byte_position = first_bit_position / 8u; + uint32_t byte_subposition = first_bit_position % 8u; + uint32_t buffer_position = byte_position / small_buffer_length; + uint32_t buffer_subposition = byte_position % small_buffer_length; + + if (byte_subposition <= 5) { + this->buffers_[buffer_position][buffer_subposition] = + (this->buffers_[buffer_position][buffer_subposition] & (0xFF ^ (0b111 << (5 - byte_subposition)))) | + (pixel_bits << (5 - byte_subposition)); + } else { + this->buffers_[buffer_position][buffer_subposition + 0] = + (this->buffers_[buffer_position][buffer_subposition + 0] & (0xFF ^ (0b111 >> (byte_subposition - 5)))) | + (pixel_bits >> (byte_subposition - 5)); + + this->buffers_[buffer_position][buffer_subposition + 1] = (this->buffers_[buffer_position][buffer_subposition + 1] & + (0xFF ^ (0xFF & (0b111 << (13 - byte_subposition))))) | + (pixel_bits << (13 - byte_subposition)); + } +} void WaveshareEPaperBase::start_command_() { this->dc_pin_->digital_write(false); this->enable(); @@ -3193,6 +3307,195 @@ void WaveshareEPaper7P5In::dump_config() { LOG_PIN(" Busy Pin: ", this->busy_pin_); LOG_UPDATE_INTERVAL(this); } +void WaveshareEPaper7P3InF::initialize() { + if (this->buffers_[0] == nullptr) { + ESP_LOGE(TAG, "Buffer unavailable!"); + return; + } + + this->reset_(); + delay(20); + this->wait_until_idle_(); + + // COMMAND CMDH + this->command(0xAA); + this->data(0x49); + this->data(0x55); + this->data(0x20); + this->data(0x08); + this->data(0x09); + this->data(0x18); + + this->command(0x01); + this->data(0x3F); + this->data(0x00); + this->data(0x32); + this->data(0x2A); + this->data(0x0E); + this->data(0x2A); + + this->command(0x00); + this->data(0x5F); + this->data(0x69); + + this->command(0x03); + this->data(0x00); + this->data(0x54); + this->data(0x00); + this->data(0x44); + + this->command(0x05); + this->data(0x40); + this->data(0x1F); + this->data(0x1F); + this->data(0x2C); + + this->command(0x06); + this->data(0x6F); + this->data(0x1F); + this->data(0x1F); + this->data(0x22); + + this->command(0x08); + this->data(0x6F); + this->data(0x1F); + this->data(0x1F); + this->data(0x22); + + // COMMAND IPC + this->command(0x13); + this->data(0x00); + this->data(0x04); + + this->command(0x30); + this->data(0x3C); + + // COMMAND TSE + this->command(0x41); + this->data(0x00); + + this->command(0x50); + this->data(0x3F); + + this->command(0x60); + this->data(0x02); + this->data(0x00); + + this->command(0x61); + this->data(0x03); + this->data(0x20); + this->data(0x01); + this->data(0xE0); + + this->command(0x82); + this->data(0x1E); + + this->command(0x84); + this->data(0x00); + + // COMMAND AGID + this->command(0x86); + this->data(0x00); + + this->command(0xE3); + this->data(0x2F); + + // COMMAND CCSET + this->command(0xE0); + this->data(0x00); + + // COMMAND TSSET + this->command(0xE6); + this->data(0x00); + + ESP_LOGI(TAG, "Display initialized successfully"); +} +void HOT WaveshareEPaper7P3InF::display() { + if (this->buffers_[0] == nullptr) { + ESP_LOGE(TAG, "Buffer unavailable!"); + return; + } + + // INITIALIZATION + ESP_LOGI(TAG, "Initialise the display"); + this->initialize(); + + // COMMAND DATA START TRANSMISSION + ESP_LOGI(TAG, "Sending data to the display"); + this->command(0x10); + uint32_t small_buffer_length = this->get_buffer_length_() / NUM_BUFFERS; + uint8_t byte_to_send; + for (auto &buffer : this->buffers_) { + for (uint32_t buffer_pos = 0; buffer_pos < small_buffer_length; buffer_pos += 3) { + std::bitset<24> triplet = + buffer[buffer_pos + 0] << 16 | buffer[buffer_pos + 1] << 8 | buffer[buffer_pos + 2] << 0; + // 8 bitset<3> are stored in 3 bytes + // |aaabbbaa|abbbaaab|bbaaabbb| + // | byte 1 | byte 2 | byte 3 | + byte_to_send = ((triplet >> 17).to_ulong() & 0b01110000) | ((triplet >> 18).to_ulong() & 0b00000111); + this->data(byte_to_send); + + byte_to_send = ((triplet >> 11).to_ulong() & 0b01110000) | ((triplet >> 12).to_ulong() & 0b00000111); + this->data(byte_to_send); + + byte_to_send = ((triplet >> 5).to_ulong() & 0b01110000) | ((triplet >> 6).to_ulong() & 0b00000111); + this->data(byte_to_send); + + byte_to_send = ((triplet << 1).to_ulong() & 0b01110000) | ((triplet << 0).to_ulong() & 0b00000111); + this->data(byte_to_send); + } + App.feed_wdt(); + } + + // COMMAND POWER ON + ESP_LOGI(TAG, "Power on the display"); + this->command(0x04); + this->wait_until_idle_(); + + // COMMAND REFRESH SCREEN + ESP_LOGI(TAG, "Refresh the display"); + this->command(0x12); + this->data(0x00); + this->wait_until_idle_(); + + // COMMAND POWER OFF + ESP_LOGI(TAG, "Power off the display"); + this->command(0x02); + this->data(0x00); + this->wait_until_idle_(); + + ESP_LOGI(TAG, "Set the display to deep sleep"); + this->command(0x07); + this->data(0xA5); +} +int WaveshareEPaper7P3InF::get_width_internal() { return 800; } +int WaveshareEPaper7P3InF::get_height_internal() { return 480; } +uint32_t WaveshareEPaper7P3InF::idle_timeout_() { return 35000; } +void WaveshareEPaper7P3InF::dump_config() { + LOG_DISPLAY("", "Waveshare E-Paper", this); + ESP_LOGCONFIG(TAG, " Model: 7.3in-F"); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + LOG_UPDATE_INTERVAL(this); +} + +bool WaveshareEPaper7P3InF::wait_until_idle_() { + if (this->busy_pin_ == nullptr) { + return true; + } + const uint32_t start = millis(); + while (this->busy_pin_->digital_read()) { + if (millis() - start > this->idle_timeout_()) { + ESP_LOGE(TAG, "Timeout while displaying image!"); + return false; + } + App.feed_wdt(); + delay(10); + } + delay(200); // NOLINT + return true; +} bool WaveshareEPaper7P5InV2::wait_until_idle_() { if (this->busy_pin_ == nullptr) { return true; diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.h b/esphome/components/waveshare_epaper/waveshare_epaper.h index 1e7cb6c6c7..d6387cd643 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.h +++ b/esphome/components/waveshare_epaper/waveshare_epaper.h @@ -27,10 +27,7 @@ class WaveshareEPaperBase : public display::DisplayBuffer, void update() override; - void setup() override { - this->setup_pins_(); - this->initialize(); - } + void setup() override; void on_safe_shutdown() override; @@ -86,6 +83,23 @@ class WaveshareEPaperBWR : public WaveshareEPaperBase { uint32_t get_buffer_length_() override; }; +class WaveshareEPaper7C : public WaveshareEPaperBase { + public: + uint8_t color_to_hex(Color color); + void fill(Color color) override; + + display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } + + protected: + void draw_absolute_pixel_internal(int x, int y, Color color) override; + uint32_t get_buffer_length_() override; + void setup() override; + void init_internal_7c_(uint32_t buffer_length); + + static const int NUM_BUFFERS = 10; + uint8_t *buffers_[NUM_BUFFERS]; +}; + enum WaveshareEPaperTypeAModel { WAVESHARE_EPAPER_1_54_IN = 0, WAVESHARE_EPAPER_1_54_IN_V2, @@ -160,6 +174,7 @@ enum WaveshareEPaperTypeBModel { WAVESHARE_EPAPER_2_7_IN_B_V2, WAVESHARE_EPAPER_4_2_IN, WAVESHARE_EPAPER_4_2_IN_B_V2, + WAVESHARE_EPAPER_7_3_IN_F, WAVESHARE_EPAPER_7_5_IN, WAVESHARE_EPAPER_7_5_INV2, WAVESHARE_EPAPER_7_5_IN_B_V2, @@ -668,6 +683,39 @@ class WaveshareEPaper5P8InV2 : public WaveshareEPaper { int get_height_internal() override; }; +class WaveshareEPaper7P3InF : public WaveshareEPaper7C { + public: + void initialize() override; + + void display() override; + + void dump_config() override; + + protected: + int get_width_internal() override; + + int get_height_internal() override; + + uint32_t idle_timeout_() override; + + void deep_sleep() override { ; } + + bool wait_until_idle_(); + + bool deep_sleep_between_updates_{true}; + + void reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(true); + delay(20); + this->reset_pin_->digital_write(false); + delay(1); + this->reset_pin_->digital_write(true); + delay(20); + } + }; +}; + class WaveshareEPaper7P5In : public WaveshareEPaper { public: void initialize() override; From 0b6c416680b24efb217d39a88d1bac5e6daed665 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:16:17 +1300 Subject: [PATCH 1000/1052] Bump esphome-dashboard to 20250212.0 (#8235) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0d93c3cc2d..bbf0ae7f3d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,7 +12,7 @@ pyserial==3.5 platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 -esphome-dashboard==20241217.1 +esphome-dashboard==20250212.0 aioesphomeapi==24.6.2 zeroconf==0.143.0 puremagic==1.27 From 2b75e34719acfea017f436c63036050f1c9f3651 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:53:43 +1300 Subject: [PATCH 1001/1052] Bump version to 2025.2.0b1 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 16bfda9478..696cc599fc 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0-dev" +__version__ = "2025.2.0b1" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From ab0d38fbdae7fe5f3d95cdf5c5675d8f6fee3deb Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 12 Feb 2025 13:53:43 +1300 Subject: [PATCH 1002/1052] Bump version to 2025.3.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 16bfda9478..f74ea64148 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0-dev" +__version__ = "2025.3.0-dev" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 3b7a7a2262183682502e74b969b9d52938f93385 Mon Sep 17 00:00:00 2001 From: guillempages Date: Wed, 12 Feb 2025 10:55:32 +0100 Subject: [PATCH 1003/1052] [online_image]Fix reset if buffer not allocated (#8236) --- .../components/online_image/image_decoder.cpp | 11 +++++++---- esphome/components/online_image/jpeg_image.cpp | 6 ++++-- .../components/online_image/online_image.cpp | 15 ++++++++------- esphome/components/online_image/online_image.h | 17 +++++++++++++++-- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/esphome/components/online_image/image_decoder.cpp b/esphome/components/online_image/image_decoder.cpp index d8c0cc33c4..0ab7dadde3 100644 --- a/esphome/components/online_image/image_decoder.cpp +++ b/esphome/components/online_image/image_decoder.cpp @@ -9,10 +9,10 @@ namespace online_image { static const char *const TAG = "online_image.decoder"; bool ImageDecoder::set_size(int width, int height) { - bool resized = this->image_->resize_(width, height); + bool success = this->image_->resize_(width, height) > 0; this->x_scale_ = static_cast(this->image_->buffer_width_) / width; this->y_scale_ = static_cast(this->image_->buffer_height_) / height; - return resized; + return success; } void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { @@ -51,8 +51,9 @@ size_t DownloadBuffer::read(size_t len) { } size_t DownloadBuffer::resize(size_t size) { - if (this->size_ == size) { - return size; + if (this->size_ >= size) { + // Avoid useless reallocations; if the buffer is big enough, don't reallocate. + return this->size_; } this->allocator_.deallocate(this->buffer_, this->size_); this->buffer_ = this->allocator_.allocate(size); @@ -61,6 +62,8 @@ size_t DownloadBuffer::resize(size_t size) { this->size_ = size; return size; } else { + ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", size, + this->allocator_.get_max_free_block_size()); this->size_ = 0; return 0; } diff --git a/esphome/components/online_image/jpeg_image.cpp b/esphome/components/online_image/jpeg_image.cpp index 0aff576da8..e5ee3dd8bf 100644 --- a/esphome/components/online_image/jpeg_image.cpp +++ b/esphome/components/online_image/jpeg_image.cpp @@ -58,7 +58,7 @@ int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { } if (!this->jpeg_.openRAM(buffer, size, draw_callback)) { - ESP_LOGE(TAG, "Could not open image for decoding."); + ESP_LOGE(TAG, "Could not open image for decoding: %d", this->jpeg_.getLastError()); return DECODE_ERROR_INVALID_TYPE; } auto jpeg_type = this->jpeg_.getJPEGType(); @@ -73,7 +73,9 @@ int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { this->jpeg_.setUserPointer(this); this->jpeg_.setPixelType(RGB8888); - this->set_size(this->jpeg_.getWidth(), this->jpeg_.getHeight()); + if (!this->set_size(this->jpeg_.getWidth(), this->jpeg_.getHeight())) { + return DECODE_ERROR_OUT_OF_MEMORY; + } if (!this->jpeg_.decode(0, 0, 0)) { ESP_LOGE(TAG, "Error while decoding."); this->jpeg_.close(); diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 3b3d00a044..3411018901 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -64,33 +64,34 @@ void OnlineImage::release() { } } -bool OnlineImage::resize_(int width_in, int height_in) { +size_t OnlineImage::resize_(int width_in, int height_in) { int width = this->fixed_width_; int height = this->fixed_height_; - if (this->auto_resize_()) { + if (this->is_auto_resize_()) { width = width_in; height = height_in; if (this->width_ != width && this->height_ != height) { this->release(); } } - if (this->buffer_) { - return false; - } size_t new_size = this->get_buffer_size_(width, height); + if (this->buffer_) { + // Buffer already allocated => no need to resize + return new_size; + } ESP_LOGD(TAG, "Allocating new buffer of %zu bytes", new_size); this->buffer_ = this->allocator_.allocate(new_size); if (this->buffer_ == nullptr) { ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", new_size, this->allocator_.get_max_free_block_size()); this->end_connection_(); - return false; + return 0; } this->buffer_width_ = width; this->buffer_height_ = height; this->width_ = width; ESP_LOGV(TAG, "New size: (%d, %d)", width, height); - return true; + return new_size; } void OnlineImage::update() { diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 4abc047083..2d10e528b1 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -99,9 +99,22 @@ class OnlineImage : public PollingComponent, int get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; } - ESPHOME_ALWAYS_INLINE bool auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } + ESPHOME_ALWAYS_INLINE bool is_auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } - bool resize_(int width, int height); + /** + * @brief Resize the image buffer to the requested dimensions. + * + * The buffer will be allocated if not existing. + * If the dimensions have been fixed in the yaml config, the buffer will be created + * with those dimensions and not resized, even on request. + * Otherwise, the old buffer will be deallocated and a new buffer with the requested + * allocated + * + * @param width + * @param height + * @return 0 if no memory could be allocated, the size of the new buffer otherwise. + */ + size_t resize_(int width, int height); /** * @brief Draw a pixel into the buffer. From 43319d4c8aa0ec0a3718c5c01e6e2c383b7918b7 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:05:46 +1300 Subject: [PATCH 1004/1052] [core] Ignore dot-prefixed config entries when looking for target platform (#8240) --- esphome/core/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index 7152414463..2077af02a7 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -217,6 +217,8 @@ def preload_core_config(config, result) -> str: target_platforms = [] for domain, _ in config.items(): + if domain.startswith("."): + continue if _is_target_platform(domain): target_platforms += [domain] From 4a95468fd26f5d4c30d43396b775644853776835 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Feb 2025 19:17:00 -0600 Subject: [PATCH 1005/1052] Bump zeroconf to 0.144.1 (#8238) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bbf0ae7f3d..8eca21c4bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 aioesphomeapi==24.6.2 -zeroconf==0.143.0 +zeroconf==0.144.1 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import glyphsets==1.0.0 From 72f64618717e3e21d769d5d5940b423f97fdc1d9 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Feb 2025 15:57:57 +1300 Subject: [PATCH 1006/1052] [core] Fix ``config_dir`` for dashboard (#8242) --- esphome/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 2d856d99c5..2a7b8b9d91 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -582,7 +582,7 @@ class EsphomeCore: @property def config_dir(self): - return os.path.dirname(os.path.abspath(self.config_path)) + return os.path.abspath(os.path.dirname(self.config_path)) @property def data_dir(self): From 2868210d463f81e5057cb42b5f8e75a145d24b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Poczkodi?= Date: Thu, 13 Feb 2025 04:07:14 +0100 Subject: [PATCH 1007/1052] [cse7766] Remove stream dependency (#7720) Co-authored-by: Keith Burzinski --- esphome/components/cse7766/cse7766.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 3907c195d0..88a91e374a 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -1,8 +1,5 @@ #include "cse7766.h" #include "esphome/core/log.h" -#include -#include -#include namespace esphome { namespace cse7766 { @@ -72,12 +69,8 @@ bool CSE7766Component::check_byte_() { void CSE7766Component::parse_data_() { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE { - std::stringstream ss; - ss << "Raw data:" << std::hex << std::uppercase << std::setfill('0'); - for (uint8_t i = 0; i < 23; i++) { - ss << ' ' << std::setw(2) << static_cast(this->raw_data_[i]); - } - ESP_LOGVV(TAG, "%s", ss.str().c_str()); + std::string s = format_hex_pretty(this->raw_data_, sizeof(this->raw_data_)); + ESP_LOGVV(TAG, "Raw data: %s", s.c_str()); } #endif @@ -211,21 +204,20 @@ void CSE7766Component::parse_data_() { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE { - std::stringstream ss; - ss << "Parsed:"; + std::string buf = "Parsed:"; if (have_voltage) { - ss << " V=" << voltage << "V"; + buf += str_sprintf(" V=%fV", voltage); } if (have_current) { - ss << " I=" << current * 1000.0f << "mA (~" << calculated_current * 1000.0f << "mA)"; + buf += str_sprintf(" I=%fmA (~%fmA)", current * 1000.0f, calculated_current * 1000.0f); } if (have_power) { - ss << " P=" << power << "W"; + buf += str_sprintf(" P=%fW", power); } if (energy != 0.0f) { - ss << " E=" << energy << "kWh (" << cf_pulses << ")"; + buf += str_sprintf(" E=%fkWh (%u)", energy, cf_pulses); } - ESP_LOGVV(TAG, "%s", ss.str().c_str()); + ESP_LOGVV(TAG, "%s", buf.c_str()); } #endif } From e190ef9e9bf4d50f0c683e415c7a5e2abd0a2368 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 12 Feb 2025 21:37:29 -0600 Subject: [PATCH 1008/1052] [graph] Remove ``stream`` dependency (#8243) --- esphome/components/graph/graph.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index 09f7557714..cbe059b255 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -4,9 +4,6 @@ #include "esphome/core/log.h" #include "esphome/core/hal.h" #include -#include -#include // std::cout, std::fixed -#include namespace esphome { namespace graph { @@ -231,9 +228,8 @@ void GraphLegend::init(Graph *g) { ESP_LOGI(TAGL, " %s %d %d", txtstr.c_str(), fw, fh); if (this->values_ != VALUE_POSITION_TYPE_NONE) { - std::stringstream ss; - ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state(); - std::string valstr = ss.str(); + std::string valstr = + value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); if (this->units_) { valstr += trace->sensor_->get_unit_of_measurement(); } @@ -368,9 +364,8 @@ void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_of if (legend_->values_ != VALUE_POSITION_TYPE_NONE) { int xv = x + legend_->xv_; int yv = y + legend_->yv_; - std::stringstream ss; - ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state(); - std::string valstr = ss.str(); + std::string valstr = + value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); if (legend_->units_) { valstr += trace->sensor_->get_unit_of_measurement(); } From ace953bd501d299ec95f86432477fe0b47a26767 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 12 Feb 2025 22:34:16 -0600 Subject: [PATCH 1009/1052] [modbus_controller] Remove `stream` dependency (#8244) --- .../text_sensor/modbus_textsensor.cpp | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp index acdcacc083..89e86741b0 100644 --- a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +++ b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp @@ -1,8 +1,6 @@ #include "modbus_textsensor.h" #include "esphome/core/log.h" -#include -#include namespace esphome { namespace modbus_controller { @@ -12,20 +10,17 @@ static const char *const TAG = "modbus_controller.text_sensor"; void ModbusTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Modbus Controller Text Sensor", this); } void ModbusTextSensor::parse_and_publish(const std::vector &data) { - std::ostringstream output; + std::string output_str{}; uint8_t items_left = this->response_bytes; uint8_t index = this->offset; - char buffer[5]; while ((items_left > 0) && index < data.size()) { uint8_t b = data[index]; switch (this->encode_) { case RawEncoding::HEXBYTES: - sprintf(buffer, "%02x", b); - output << buffer; + output_str += str_snprintf("%02x", 2, b); break; case RawEncoding::COMMA: - sprintf(buffer, index != this->offset ? ",%d" : "%d", b); - output << buffer; + output_str += str_sprintf(index != this->offset ? ",%d" : "%d", b); break; case RawEncoding::ANSI: if (b < 0x20) @@ -33,25 +28,24 @@ void ModbusTextSensor::parse_and_publish(const std::vector &data) { // FALLTHROUGH // Anything else no encoding default: - output << (char) b; + output_str += (char) b; break; } items_left--; index++; } - auto result = output.str(); // Is there a lambda registered // call it with the pre converted value and the raw data array if (this->transform_func_.has_value()) { // the lambda can parse the response itself - auto val = (*this->transform_func_)(this, result, data); + auto val = (*this->transform_func_)(this, output_str, data); if (val.has_value()) { ESP_LOGV(TAG, "Value overwritten by lambda"); - result = val.value(); + output_str = val.value(); } } - this->publish_state(result); + this->publish_state(output_str); } } // namespace modbus_controller From fa029e8fc7b553472baf81a1b200fe138a9554a5 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Thu, 13 Feb 2025 01:40:02 -0600 Subject: [PATCH 1010/1052] [modbus_controller] Extend tests (#8245) --- .../components/modbus_controller/common.yaml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/components/modbus_controller/common.yaml b/tests/components/modbus_controller/common.yaml index 93d8391ff5..7fa9f8dae3 100644 --- a/tests/components/modbus_controller/common.yaml +++ b/tests/components/modbus_controller/common.yaml @@ -33,3 +33,73 @@ modbus_controller: read_lambda: |- return 42.3; max_cmd_retries: 0 + +binary_sensor: + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_binary_sensor1 + name: Test Binary Sensor + register_type: read + address: 0x3200 + bitmask: 0x80 + +number: + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_number1 + name: Test Number + address: 0x9001 + value_type: U_WORD + multiply: 1.0 + +output: + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_output1 + address: 2048 + register_type: holding + value_type: U_WORD + multiply: 1000 + +select: + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_select1 + name: Test Select + address: 1000 + value_type: U_WORD + optionsmap: + "Zero": 0 + "One": 1 + "Two": 2 + "Three": 3 + +sensor: + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_sensor1 + name: Test Sensor + register_type: holding + address: 0x9001 + unit_of_measurement: "AH" + value_type: U_WORD + +switch: + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_switch1 + name: Test Switch + register_type: coil + address: 0x15 + bitmask: 1 + +text_sensor: + - platform: modbus_controller + modbus_controller_id: modbus_controller1 + id: modbus_text_sensor1 + name: Test Text Sensor + register_type: holding + address: 0x9013 + register_count: 3 + raw_encode: HEXBYTES + response_size: 6 From 077ee5b7143d1ea033ec513f354b07e07be1b62c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:05:46 +1300 Subject: [PATCH 1011/1052] [core] Ignore dot-prefixed config entries when looking for target platform (#8240) --- esphome/core/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/config.py b/esphome/core/config.py index 7152414463..2077af02a7 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -217,6 +217,8 @@ def preload_core_config(config, result) -> str: target_platforms = [] for domain, _ in config.items(): + if domain.startswith("."): + continue if _is_target_platform(domain): target_platforms += [domain] From c8e7e275a4bedde565867288416acb06bbe0cc51 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 12 Feb 2025 19:17:00 -0600 Subject: [PATCH 1012/1052] Bump zeroconf to 0.144.1 (#8238) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bbf0ae7f3d..8eca21c4bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 aioesphomeapi==24.6.2 -zeroconf==0.143.0 +zeroconf==0.144.1 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import glyphsets==1.0.0 From 4740f12ce8e5e6d63278496e3e8dc909d5a33ab3 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Feb 2025 15:57:57 +1300 Subject: [PATCH 1013/1052] [core] Fix ``config_dir`` for dashboard (#8242) --- esphome/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/__init__.py b/esphome/core/__init__.py index 2d856d99c5..2a7b8b9d91 100644 --- a/esphome/core/__init__.py +++ b/esphome/core/__init__.py @@ -582,7 +582,7 @@ class EsphomeCore: @property def config_dir(self): - return os.path.dirname(os.path.abspath(self.config_path)) + return os.path.abspath(os.path.dirname(self.config_path)) @property def data_dir(self): From 35d303809e8d5b4ed11e0b6bc70065bf417aed75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Poczkodi?= Date: Thu, 13 Feb 2025 04:07:14 +0100 Subject: [PATCH 1014/1052] [cse7766] Remove stream dependency (#7720) Co-authored-by: Keith Burzinski --- esphome/components/cse7766/cse7766.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 3907c195d0..88a91e374a 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -1,8 +1,5 @@ #include "cse7766.h" #include "esphome/core/log.h" -#include -#include -#include namespace esphome { namespace cse7766 { @@ -72,12 +69,8 @@ bool CSE7766Component::check_byte_() { void CSE7766Component::parse_data_() { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE { - std::stringstream ss; - ss << "Raw data:" << std::hex << std::uppercase << std::setfill('0'); - for (uint8_t i = 0; i < 23; i++) { - ss << ' ' << std::setw(2) << static_cast(this->raw_data_[i]); - } - ESP_LOGVV(TAG, "%s", ss.str().c_str()); + std::string s = format_hex_pretty(this->raw_data_, sizeof(this->raw_data_)); + ESP_LOGVV(TAG, "Raw data: %s", s.c_str()); } #endif @@ -211,21 +204,20 @@ void CSE7766Component::parse_data_() { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE { - std::stringstream ss; - ss << "Parsed:"; + std::string buf = "Parsed:"; if (have_voltage) { - ss << " V=" << voltage << "V"; + buf += str_sprintf(" V=%fV", voltage); } if (have_current) { - ss << " I=" << current * 1000.0f << "mA (~" << calculated_current * 1000.0f << "mA)"; + buf += str_sprintf(" I=%fmA (~%fmA)", current * 1000.0f, calculated_current * 1000.0f); } if (have_power) { - ss << " P=" << power << "W"; + buf += str_sprintf(" P=%fW", power); } if (energy != 0.0f) { - ss << " E=" << energy << "kWh (" << cf_pulses << ")"; + buf += str_sprintf(" E=%fkWh (%u)", energy, cf_pulses); } - ESP_LOGVV(TAG, "%s", ss.str().c_str()); + ESP_LOGVV(TAG, "%s", buf.c_str()); } #endif } From e9a537784e314484dff0e2bd32377177465c33fe Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 12 Feb 2025 21:37:29 -0600 Subject: [PATCH 1015/1052] [graph] Remove ``stream`` dependency (#8243) --- esphome/components/graph/graph.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/esphome/components/graph/graph.cpp b/esphome/components/graph/graph.cpp index 09f7557714..cbe059b255 100644 --- a/esphome/components/graph/graph.cpp +++ b/esphome/components/graph/graph.cpp @@ -4,9 +4,6 @@ #include "esphome/core/log.h" #include "esphome/core/hal.h" #include -#include -#include // std::cout, std::fixed -#include namespace esphome { namespace graph { @@ -231,9 +228,8 @@ void GraphLegend::init(Graph *g) { ESP_LOGI(TAGL, " %s %d %d", txtstr.c_str(), fw, fh); if (this->values_ != VALUE_POSITION_TYPE_NONE) { - std::stringstream ss; - ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state(); - std::string valstr = ss.str(); + std::string valstr = + value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); if (this->units_) { valstr += trace->sensor_->get_unit_of_measurement(); } @@ -368,9 +364,8 @@ void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_of if (legend_->values_ != VALUE_POSITION_TYPE_NONE) { int xv = x + legend_->xv_; int yv = y + legend_->yv_; - std::stringstream ss; - ss << std::fixed << std::setprecision(trace->sensor_->get_accuracy_decimals()) << trace->sensor_->get_state(); - std::string valstr = ss.str(); + std::string valstr = + value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals()); if (legend_->units_) { valstr += trace->sensor_->get_unit_of_measurement(); } From be5639faf1b34fccc0a99c77dabeb87417eb3332 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Wed, 12 Feb 2025 22:34:16 -0600 Subject: [PATCH 1016/1052] [modbus_controller] Remove `stream` dependency (#8244) --- .../text_sensor/modbus_textsensor.cpp | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp index acdcacc083..89e86741b0 100644 --- a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +++ b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp @@ -1,8 +1,6 @@ #include "modbus_textsensor.h" #include "esphome/core/log.h" -#include -#include namespace esphome { namespace modbus_controller { @@ -12,20 +10,17 @@ static const char *const TAG = "modbus_controller.text_sensor"; void ModbusTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Modbus Controller Text Sensor", this); } void ModbusTextSensor::parse_and_publish(const std::vector &data) { - std::ostringstream output; + std::string output_str{}; uint8_t items_left = this->response_bytes; uint8_t index = this->offset; - char buffer[5]; while ((items_left > 0) && index < data.size()) { uint8_t b = data[index]; switch (this->encode_) { case RawEncoding::HEXBYTES: - sprintf(buffer, "%02x", b); - output << buffer; + output_str += str_snprintf("%02x", 2, b); break; case RawEncoding::COMMA: - sprintf(buffer, index != this->offset ? ",%d" : "%d", b); - output << buffer; + output_str += str_sprintf(index != this->offset ? ",%d" : "%d", b); break; case RawEncoding::ANSI: if (b < 0x20) @@ -33,25 +28,24 @@ void ModbusTextSensor::parse_and_publish(const std::vector &data) { // FALLTHROUGH // Anything else no encoding default: - output << (char) b; + output_str += (char) b; break; } items_left--; index++; } - auto result = output.str(); // Is there a lambda registered // call it with the pre converted value and the raw data array if (this->transform_func_.has_value()) { // the lambda can parse the response itself - auto val = (*this->transform_func_)(this, result, data); + auto val = (*this->transform_func_)(this, output_str, data); if (val.has_value()) { ESP_LOGV(TAG, "Value overwritten by lambda"); - result = val.value(); + output_str = val.value(); } } - this->publish_state(result); + this->publish_state(output_str); } } // namespace modbus_controller From b0f6dd7d9c44690b8e33c87b63acd033a72dc233 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Thu, 13 Feb 2025 20:44:12 +1300 Subject: [PATCH 1017/1052] Bump version to 2025.2.0b2 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 696cc599fc..7390187d7e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0b1" +__version__ = "2025.2.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 46b6dcdfbf63b660be98e4c6c8448e59b1fae153 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:56:08 -0500 Subject: [PATCH 1018/1052] [logger] Fix bug causing global log level to be overwritten (#8248) --- esphome/components/logger/__init__.py | 4 ++-- esphome/components/logger/logger.cpp | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index a89bf95c77..113f306327 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -247,8 +247,8 @@ async def to_code(config): ) cg.add(log.pre_setup()) - for tag, level in config[CONF_LOGS].items(): - cg.add(log.set_log_level(tag, LOG_LEVELS[level])) + for tag, log_level in config[CONF_LOGS].items(): + cg.add(log.set_log_level(tag, LOG_LEVELS[log_level])) cg.add_define("USE_LOGGER") this_severity = LOG_LEVEL_SEVERITY.index(level) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 79fc4cf499..57f0ba9f9a 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -102,9 +102,6 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr #endif int HOT Logger::level_for(const char *tag) { - // Uses std::vector<> for low memory footprint, though the vector - // could be sorted to minimize lookup times. This feature isn't used that - // much anyway so it doesn't matter too much. if (this->log_levels_.count(tag) != 0) return this->log_levels_[tag]; return this->current_level_; From 788c41e6f4365f4b3dd6b7fd28d245ac3007dbd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20M=C3=A1rai?= Date: Fri, 14 Feb 2025 01:15:01 +0100 Subject: [PATCH 1019/1052] Add support for the DAC on the S2 (#8030) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32_dac/esp32_dac.cpp | 23 +++++++++++++++------- esphome/components/esp32_dac/esp32_dac.h | 8 ++++++++ esphome/components/esp32_dac/output.py | 20 +++++++++++++++---- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/esphome/components/esp32_dac/esp32_dac.cpp b/esphome/components/esp32_dac/esp32_dac.cpp index 7f37e2ce47..6f1577b8b1 100644 --- a/esphome/components/esp32_dac/esp32_dac.cpp +++ b/esphome/components/esp32_dac/esp32_dac.cpp @@ -7,13 +7,16 @@ #ifdef USE_ARDUINO #include #endif -#ifdef USE_ESP_IDF -#include -#endif namespace esphome { namespace esp32_dac { +#ifdef USE_ESP32_VARIANT_ESP32S2 +static constexpr uint8_t DAC0_PIN = 17; +#else +static constexpr uint8_t DAC0_PIN = 25; +#endif + static const char *const TAG = "esp32_dac"; void ESP32DAC::setup() { @@ -22,8 +25,15 @@ void ESP32DAC::setup() { this->turn_off(); #ifdef USE_ESP_IDF - auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - dac_output_enable(channel); + const dac_channel_t channel = this->pin_->get_pin() == DAC0_PIN ? DAC_CHAN_0 : DAC_CHAN_1; + const dac_oneshot_config_t oneshot_cfg{channel}; + dac_oneshot_new_channel(&oneshot_cfg, &this->dac_handle_); +#endif +} + +void ESP32DAC::on_safe_shutdown() { +#ifdef USE_ESP_IDF + dac_oneshot_del_channel(this->dac_handle_); #endif } @@ -40,8 +50,7 @@ void ESP32DAC::write_state(float state) { state = state * 255; #ifdef USE_ESP_IDF - auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - dac_output_voltage(channel, (uint8_t) state); + dac_oneshot_output_voltage(this->dac_handle_, state); #endif #ifdef USE_ARDUINO dacWrite(this->pin_->get_pin(), state); diff --git a/esphome/components/esp32_dac/esp32_dac.h b/esphome/components/esp32_dac/esp32_dac.h index 0fb1ddebf0..63d0c914a1 100644 --- a/esphome/components/esp32_dac/esp32_dac.h +++ b/esphome/components/esp32_dac/esp32_dac.h @@ -7,6 +7,10 @@ #ifdef USE_ESP32 +#ifdef USE_ESP_IDF +#include +#endif + namespace esphome { namespace esp32_dac { @@ -16,6 +20,7 @@ class ESP32DAC : public output::FloatOutput, public Component { /// Initialize pin void setup() override; + void on_safe_shutdown() override; void dump_config() override; /// HARDWARE setup_priority float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -24,6 +29,9 @@ class ESP32DAC : public output::FloatOutput, public Component { void write_state(float state) override; InternalGPIOPin *pin_; +#ifdef USE_ESP_IDF + dac_oneshot_handle_t dac_handle_; +#endif }; } // namespace esp32_dac diff --git a/esphome/components/esp32_dac/output.py b/esphome/components/esp32_dac/output.py index f119198618..c80780986f 100644 --- a/esphome/components/esp32_dac/output.py +++ b/esphome/components/esp32_dac/output.py @@ -1,15 +1,27 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import pins from esphome.components import output -import esphome.config_validation as cv -import esphome.codegen as cg +from esphome.components.esp32 import get_esp32_variant +from esphome.components.esp32.const import VARIANT_ESP32, VARIANT_ESP32S2 from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN DEPENDENCIES = ["esp32"] +DAC_PINS = { + VARIANT_ESP32: (25, 26), + VARIANT_ESP32S2: (17, 18), +} + def valid_dac_pin(value): - num = value[CONF_NUMBER] - cv.one_of(25, 26)(num) + variant = get_esp32_variant() + try: + valid_pins = DAC_PINS[variant] + except KeyError as ex: + raise cv.Invalid(f"DAC is not supported on {variant}") from ex + given_pin = value[CONF_NUMBER] + cv.one_of(*valid_pins)(given_pin) return value From 143b0d3de429a0d9adca0f888829af56ab10904d Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 14 Feb 2025 14:27:11 +1300 Subject: [PATCH 1020/1052] Fix crash when storage file doesnt exist yet (#8249) --- esphome/dashboard/web_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 67712da9b6..b8562aaccb 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -853,7 +853,7 @@ class InfoRequestHandler(BaseHandler): dashboard = DASHBOARD entry = dashboard.entries.get(yaml_path) - if not entry: + if not entry or entry.storage is None: self.set_status(404) return From 1111aa167f319bad7a778b54745cf495662e8138 Mon Sep 17 00:00:00 2001 From: guillempages Date: Wed, 12 Feb 2025 10:55:32 +0100 Subject: [PATCH 1021/1052] [online_image]Fix reset if buffer not allocated (#8236) --- .../components/online_image/image_decoder.cpp | 11 +++++++---- esphome/components/online_image/jpeg_image.cpp | 6 ++++-- .../components/online_image/online_image.cpp | 15 ++++++++------- esphome/components/online_image/online_image.h | 17 +++++++++++++++-- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/esphome/components/online_image/image_decoder.cpp b/esphome/components/online_image/image_decoder.cpp index d8c0cc33c4..0ab7dadde3 100644 --- a/esphome/components/online_image/image_decoder.cpp +++ b/esphome/components/online_image/image_decoder.cpp @@ -9,10 +9,10 @@ namespace online_image { static const char *const TAG = "online_image.decoder"; bool ImageDecoder::set_size(int width, int height) { - bool resized = this->image_->resize_(width, height); + bool success = this->image_->resize_(width, height) > 0; this->x_scale_ = static_cast(this->image_->buffer_width_) / width; this->y_scale_ = static_cast(this->image_->buffer_height_) / height; - return resized; + return success; } void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { @@ -51,8 +51,9 @@ size_t DownloadBuffer::read(size_t len) { } size_t DownloadBuffer::resize(size_t size) { - if (this->size_ == size) { - return size; + if (this->size_ >= size) { + // Avoid useless reallocations; if the buffer is big enough, don't reallocate. + return this->size_; } this->allocator_.deallocate(this->buffer_, this->size_); this->buffer_ = this->allocator_.allocate(size); @@ -61,6 +62,8 @@ size_t DownloadBuffer::resize(size_t size) { this->size_ = size; return size; } else { + ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", size, + this->allocator_.get_max_free_block_size()); this->size_ = 0; return 0; } diff --git a/esphome/components/online_image/jpeg_image.cpp b/esphome/components/online_image/jpeg_image.cpp index 0aff576da8..e5ee3dd8bf 100644 --- a/esphome/components/online_image/jpeg_image.cpp +++ b/esphome/components/online_image/jpeg_image.cpp @@ -58,7 +58,7 @@ int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { } if (!this->jpeg_.openRAM(buffer, size, draw_callback)) { - ESP_LOGE(TAG, "Could not open image for decoding."); + ESP_LOGE(TAG, "Could not open image for decoding: %d", this->jpeg_.getLastError()); return DECODE_ERROR_INVALID_TYPE; } auto jpeg_type = this->jpeg_.getJPEGType(); @@ -73,7 +73,9 @@ int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { this->jpeg_.setUserPointer(this); this->jpeg_.setPixelType(RGB8888); - this->set_size(this->jpeg_.getWidth(), this->jpeg_.getHeight()); + if (!this->set_size(this->jpeg_.getWidth(), this->jpeg_.getHeight())) { + return DECODE_ERROR_OUT_OF_MEMORY; + } if (!this->jpeg_.decode(0, 0, 0)) { ESP_LOGE(TAG, "Error while decoding."); this->jpeg_.close(); diff --git a/esphome/components/online_image/online_image.cpp b/esphome/components/online_image/online_image.cpp index 3b3d00a044..3411018901 100644 --- a/esphome/components/online_image/online_image.cpp +++ b/esphome/components/online_image/online_image.cpp @@ -64,33 +64,34 @@ void OnlineImage::release() { } } -bool OnlineImage::resize_(int width_in, int height_in) { +size_t OnlineImage::resize_(int width_in, int height_in) { int width = this->fixed_width_; int height = this->fixed_height_; - if (this->auto_resize_()) { + if (this->is_auto_resize_()) { width = width_in; height = height_in; if (this->width_ != width && this->height_ != height) { this->release(); } } - if (this->buffer_) { - return false; - } size_t new_size = this->get_buffer_size_(width, height); + if (this->buffer_) { + // Buffer already allocated => no need to resize + return new_size; + } ESP_LOGD(TAG, "Allocating new buffer of %zu bytes", new_size); this->buffer_ = this->allocator_.allocate(new_size); if (this->buffer_ == nullptr) { ESP_LOGE(TAG, "allocation of %zu bytes failed. Biggest block in heap: %zu Bytes", new_size, this->allocator_.get_max_free_block_size()); this->end_connection_(); - return false; + return 0; } this->buffer_width_ = width; this->buffer_height_ = height; this->width_ = width; ESP_LOGV(TAG, "New size: (%d, %d)", width, height); - return true; + return new_size; } void OnlineImage::update() { diff --git a/esphome/components/online_image/online_image.h b/esphome/components/online_image/online_image.h index 4abc047083..2d10e528b1 100644 --- a/esphome/components/online_image/online_image.h +++ b/esphome/components/online_image/online_image.h @@ -99,9 +99,22 @@ class OnlineImage : public PollingComponent, int get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; } - ESPHOME_ALWAYS_INLINE bool auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } + ESPHOME_ALWAYS_INLINE bool is_auto_resize_() const { return this->fixed_width_ == 0 || this->fixed_height_ == 0; } - bool resize_(int width, int height); + /** + * @brief Resize the image buffer to the requested dimensions. + * + * The buffer will be allocated if not existing. + * If the dimensions have been fixed in the yaml config, the buffer will be created + * with those dimensions and not resized, even on request. + * Otherwise, the old buffer will be deallocated and a new buffer with the requested + * allocated + * + * @param width + * @param height + * @return 0 if no memory could be allocated, the size of the new buffer otherwise. + */ + size_t resize_(int width, int height); /** * @brief Draw a pixel into the buffer. From 92ad6286aa0440292caac66eb9374474608bdd36 Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:56:08 -0500 Subject: [PATCH 1022/1052] [logger] Fix bug causing global log level to be overwritten (#8248) --- esphome/components/logger/__init__.py | 4 ++-- esphome/components/logger/logger.cpp | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index a89bf95c77..113f306327 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -247,8 +247,8 @@ async def to_code(config): ) cg.add(log.pre_setup()) - for tag, level in config[CONF_LOGS].items(): - cg.add(log.set_log_level(tag, LOG_LEVELS[level])) + for tag, log_level in config[CONF_LOGS].items(): + cg.add(log.set_log_level(tag, LOG_LEVELS[log_level])) cg.add_define("USE_LOGGER") this_severity = LOG_LEVEL_SEVERITY.index(level) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 79fc4cf499..57f0ba9f9a 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -102,9 +102,6 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr #endif int HOT Logger::level_for(const char *tag) { - // Uses std::vector<> for low memory footprint, though the vector - // could be sorted to minimize lookup times. This feature isn't used that - // much anyway so it doesn't matter too much. if (this->log_levels_.count(tag) != 0) return this->log_levels_[tag]; return this->current_level_; From 6999cc0581ae1e69fbbaab8027e0e43d77670849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20M=C3=A1rai?= Date: Fri, 14 Feb 2025 01:15:01 +0100 Subject: [PATCH 1023/1052] Add support for the DAC on the S2 (#8030) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esp32_dac/esp32_dac.cpp | 23 +++++++++++++++------- esphome/components/esp32_dac/esp32_dac.h | 8 ++++++++ esphome/components/esp32_dac/output.py | 20 +++++++++++++++---- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/esphome/components/esp32_dac/esp32_dac.cpp b/esphome/components/esp32_dac/esp32_dac.cpp index 7f37e2ce47..6f1577b8b1 100644 --- a/esphome/components/esp32_dac/esp32_dac.cpp +++ b/esphome/components/esp32_dac/esp32_dac.cpp @@ -7,13 +7,16 @@ #ifdef USE_ARDUINO #include #endif -#ifdef USE_ESP_IDF -#include -#endif namespace esphome { namespace esp32_dac { +#ifdef USE_ESP32_VARIANT_ESP32S2 +static constexpr uint8_t DAC0_PIN = 17; +#else +static constexpr uint8_t DAC0_PIN = 25; +#endif + static const char *const TAG = "esp32_dac"; void ESP32DAC::setup() { @@ -22,8 +25,15 @@ void ESP32DAC::setup() { this->turn_off(); #ifdef USE_ESP_IDF - auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - dac_output_enable(channel); + const dac_channel_t channel = this->pin_->get_pin() == DAC0_PIN ? DAC_CHAN_0 : DAC_CHAN_1; + const dac_oneshot_config_t oneshot_cfg{channel}; + dac_oneshot_new_channel(&oneshot_cfg, &this->dac_handle_); +#endif +} + +void ESP32DAC::on_safe_shutdown() { +#ifdef USE_ESP_IDF + dac_oneshot_del_channel(this->dac_handle_); #endif } @@ -40,8 +50,7 @@ void ESP32DAC::write_state(float state) { state = state * 255; #ifdef USE_ESP_IDF - auto channel = pin_->get_pin() == 25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - dac_output_voltage(channel, (uint8_t) state); + dac_oneshot_output_voltage(this->dac_handle_, state); #endif #ifdef USE_ARDUINO dacWrite(this->pin_->get_pin(), state); diff --git a/esphome/components/esp32_dac/esp32_dac.h b/esphome/components/esp32_dac/esp32_dac.h index 0fb1ddebf0..63d0c914a1 100644 --- a/esphome/components/esp32_dac/esp32_dac.h +++ b/esphome/components/esp32_dac/esp32_dac.h @@ -7,6 +7,10 @@ #ifdef USE_ESP32 +#ifdef USE_ESP_IDF +#include +#endif + namespace esphome { namespace esp32_dac { @@ -16,6 +20,7 @@ class ESP32DAC : public output::FloatOutput, public Component { /// Initialize pin void setup() override; + void on_safe_shutdown() override; void dump_config() override; /// HARDWARE setup_priority float get_setup_priority() const override { return setup_priority::HARDWARE; } @@ -24,6 +29,9 @@ class ESP32DAC : public output::FloatOutput, public Component { void write_state(float state) override; InternalGPIOPin *pin_; +#ifdef USE_ESP_IDF + dac_oneshot_handle_t dac_handle_; +#endif }; } // namespace esp32_dac diff --git a/esphome/components/esp32_dac/output.py b/esphome/components/esp32_dac/output.py index f119198618..c80780986f 100644 --- a/esphome/components/esp32_dac/output.py +++ b/esphome/components/esp32_dac/output.py @@ -1,15 +1,27 @@ +import esphome.codegen as cg +import esphome.config_validation as cv from esphome import pins from esphome.components import output -import esphome.config_validation as cv -import esphome.codegen as cg +from esphome.components.esp32 import get_esp32_variant +from esphome.components.esp32.const import VARIANT_ESP32, VARIANT_ESP32S2 from esphome.const import CONF_ID, CONF_NUMBER, CONF_PIN DEPENDENCIES = ["esp32"] +DAC_PINS = { + VARIANT_ESP32: (25, 26), + VARIANT_ESP32S2: (17, 18), +} + def valid_dac_pin(value): - num = value[CONF_NUMBER] - cv.one_of(25, 26)(num) + variant = get_esp32_variant() + try: + valid_pins = DAC_PINS[variant] + except KeyError as ex: + raise cv.Invalid(f"DAC is not supported on {variant}") from ex + given_pin = value[CONF_NUMBER] + cv.one_of(*valid_pins)(given_pin) return value From daa796003174623ab31532158c8d3d8ef484b9e5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 14 Feb 2025 14:27:11 +1300 Subject: [PATCH 1024/1052] Fix crash when storage file doesnt exist yet (#8249) --- esphome/dashboard/web_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/dashboard/web_server.py b/esphome/dashboard/web_server.py index 67712da9b6..b8562aaccb 100644 --- a/esphome/dashboard/web_server.py +++ b/esphome/dashboard/web_server.py @@ -853,7 +853,7 @@ class InfoRequestHandler(BaseHandler): dashboard = DASHBOARD entry = dashboard.entries.get(yaml_path) - if not entry: + if not entry or entry.storage is None: self.set_status(404) return From e17582544e51aad38233ae8e2f249572d91bfe54 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 14 Feb 2025 14:28:42 +1300 Subject: [PATCH 1025/1052] Bump version to 2025.2.0b3 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7390187d7e..2f3854b8b3 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0b2" +__version__ = "2025.2.0b3" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 93c2878c213acb8cbb88f604b21abeb1302ab2ba Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sat, 15 Feb 2025 23:02:51 -0800 Subject: [PATCH 1026/1052] don't crash on null pages (#8254) Co-authored-by: Samuel Sieb --- esphome/components/display/display.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 202c64ef14..b12a81e050 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -815,8 +815,20 @@ void Display::test_card() { DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {} void DisplayPage::show() { this->parent_->show_page(this); } -void DisplayPage::show_next() { this->next_->show(); } -void DisplayPage::show_prev() { this->prev_->show(); } +void DisplayPage::show_next() { + if (this->next_ == nullptr) { + ESP_LOGE(TAG, "no next page"); + return; + } + this->next_->show(); +} +void DisplayPage::show_prev() { + if (this->prev_ == nullptr) { + ESP_LOGE(TAG, "no previous page"); + return; + } + this->prev_->show(); +} void DisplayPage::set_parent(Display *parent) { this->parent_ = parent; } void DisplayPage::set_prev(DisplayPage *prev) { this->prev_ = prev; } void DisplayPage::set_next(DisplayPage *next) { this->next_ = next; } From e21ef22706e5241a8c1cd3dc6b1583d3f061898d Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:09:42 +0100 Subject: [PATCH 1027/1052] [scd30] Increase minimal CONF_UPDATE_INTERVAL from 1 to 2 seconds (#8256) --- esphome/components/scd30/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index a900c51a58..cee8cc7b71 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -75,7 +75,7 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.All( cv.positive_time_period_seconds, cv.Range( - min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800) + min=core.TimePeriod(seconds=2), max=core.TimePeriod(seconds=1800) ), ), } From 2e66b3367258a1fd4cb02d46cdcbd32002498c06 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 16 Feb 2025 11:53:19 -0800 Subject: [PATCH 1028/1052] Bump zeroconf to 0.144.3 (#8253) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8eca21c4bb..bb5bc768e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 aioesphomeapi==24.6.2 -zeroconf==0.144.1 +zeroconf==0.144.3 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import glyphsets==1.0.0 From a47e27885fe1c249f7594da8249d4f8d6891def2 Mon Sep 17 00:00:00 2001 From: Ali Jafri Date: Mon, 17 Feb 2025 03:35:54 +0530 Subject: [PATCH 1029/1052] DHT platform now supports modules with inbuilt external resistor (#8257) --- esphome/components/dht/dht.cpp | 3 ++- esphome/components/dht/sensor.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 5a18f6f36e..f2a33a26ac 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -23,6 +23,7 @@ void DHT::dump_config() { } else { ESP_LOGCONFIG(TAG, " Model: DHT22 (or equivalent)"); } + ESP_LOGCONFIG(TAG, " Internal Pull-up: %s", ONOFF(this->pin_->get_flags() & gpio::FLAG_PULLUP)); LOG_UPDATE_INTERVAL(this); @@ -101,7 +102,7 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r } else { delayMicroseconds(800); } - this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); + this->pin_->pin_mode(this->pin_->get_flags()); { InterruptLock lock; diff --git a/esphome/components/dht/sensor.py b/esphome/components/dht/sensor.py index da92a97e1f..be53df2625 100644 --- a/esphome/components/dht/sensor.py +++ b/esphome/components/dht/sensor.py @@ -34,7 +34,7 @@ DHT = dht_ns.class_("DHT", cg.PollingComponent) CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(DHT), - cv.Required(CONF_PIN): pins.internal_gpio_input_pin_schema, + cv.Required(CONF_PIN): pins.internal_gpio_input_pullup_pin_schema, cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, accuracy_decimals=1, From a67b85eabf4f584ef4a7d136bee5f04421d8defe Mon Sep 17 00:00:00 2001 From: Samuel Sieb Date: Sat, 15 Feb 2025 23:02:51 -0800 Subject: [PATCH 1030/1052] don't crash on null pages (#8254) Co-authored-by: Samuel Sieb --- esphome/components/display/display.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/esphome/components/display/display.cpp b/esphome/components/display/display.cpp index 202c64ef14..b12a81e050 100644 --- a/esphome/components/display/display.cpp +++ b/esphome/components/display/display.cpp @@ -815,8 +815,20 @@ void Display::test_card() { DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {} void DisplayPage::show() { this->parent_->show_page(this); } -void DisplayPage::show_next() { this->next_->show(); } -void DisplayPage::show_prev() { this->prev_->show(); } +void DisplayPage::show_next() { + if (this->next_ == nullptr) { + ESP_LOGE(TAG, "no next page"); + return; + } + this->next_->show(); +} +void DisplayPage::show_prev() { + if (this->prev_ == nullptr) { + ESP_LOGE(TAG, "no previous page"); + return; + } + this->prev_->show(); +} void DisplayPage::set_parent(Display *parent) { this->parent_ = parent; } void DisplayPage::set_prev(DisplayPage *prev) { this->prev_ = prev; } void DisplayPage::set_next(DisplayPage *next) { this->next_ = next; } From c0804d665d5eed0e82474304c399b87b0a25e0ab Mon Sep 17 00:00:00 2001 From: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:09:42 +0100 Subject: [PATCH 1031/1052] [scd30] Increase minimal CONF_UPDATE_INTERVAL from 1 to 2 seconds (#8256) --- esphome/components/scd30/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index a900c51a58..cee8cc7b71 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -75,7 +75,7 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_UPDATE_INTERVAL, default="60s"): cv.All( cv.positive_time_period_seconds, cv.Range( - min=core.TimePeriod(seconds=1), max=core.TimePeriod(seconds=1800) + min=core.TimePeriod(seconds=2), max=core.TimePeriod(seconds=1800) ), ), } From e89603fe3b1b489b57ec91436d192ed651bb0048 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 16 Feb 2025 11:53:19 -0800 Subject: [PATCH 1032/1052] Bump zeroconf to 0.144.3 (#8253) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8eca21c4bb..bb5bc768e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 aioesphomeapi==24.6.2 -zeroconf==0.144.1 +zeroconf==0.144.3 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import glyphsets==1.0.0 From 9ee5227fe05f8f91b9a907644dc0565136a3afdd Mon Sep 17 00:00:00 2001 From: Ali Jafri Date: Mon, 17 Feb 2025 03:35:54 +0530 Subject: [PATCH 1033/1052] DHT platform now supports modules with inbuilt external resistor (#8257) --- esphome/components/dht/dht.cpp | 3 ++- esphome/components/dht/sensor.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 5a18f6f36e..f2a33a26ac 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -23,6 +23,7 @@ void DHT::dump_config() { } else { ESP_LOGCONFIG(TAG, " Model: DHT22 (or equivalent)"); } + ESP_LOGCONFIG(TAG, " Internal Pull-up: %s", ONOFF(this->pin_->get_flags() & gpio::FLAG_PULLUP)); LOG_UPDATE_INTERVAL(this); @@ -101,7 +102,7 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r } else { delayMicroseconds(800); } - this->pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP); + this->pin_->pin_mode(this->pin_->get_flags()); { InterruptLock lock; diff --git a/esphome/components/dht/sensor.py b/esphome/components/dht/sensor.py index da92a97e1f..be53df2625 100644 --- a/esphome/components/dht/sensor.py +++ b/esphome/components/dht/sensor.py @@ -34,7 +34,7 @@ DHT = dht_ns.class_("DHT", cg.PollingComponent) CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(DHT), - cv.Required(CONF_PIN): pins.internal_gpio_input_pin_schema, + cv.Required(CONF_PIN): pins.internal_gpio_input_pullup_pin_schema, cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, accuracy_decimals=1, From 78b55e22ee4148d3769c50548d73e752b3765bb4 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 17 Feb 2025 12:14:06 +1300 Subject: [PATCH 1034/1052] Bump version to 2025.2.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 2f3854b8b3..7c96e6eefb 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0b3" +__version__ = "2025.2.0b4" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 1eb658cc5b0c8bdae8c7601298714e60eef9c415 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 17 Feb 2025 15:48:24 -0600 Subject: [PATCH 1035/1052] Replace glyphsets with esphome_glyphsets (#8261) --- esphome/components/font/__init__.py | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index e2051298fe..4f569379be 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -5,8 +5,8 @@ import os from pathlib import Path import re +import esphome_glyphsets as glyphsets import freetype -import glyphsets import requests from esphome import core, external_files diff --git a/requirements.txt b/requirements.txt index bb5bc768e1..b3a48a0442 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ aioesphomeapi==24.6.2 zeroconf==0.144.3 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import -glyphsets==1.0.0 +esphome-glyphsets==0.1.0 pillow==10.4.0 freetype-py==2.5.1 From c21b8bd4172c6b4bd7468b5d9e74de85078fb7c8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 17 Feb 2025 16:19:11 -0600 Subject: [PATCH 1036/1052] Switch to native arm runners for docker CI (#8262) --- .github/workflows/ci-docker.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 8de6191205..303a6a777f 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -33,11 +33,11 @@ concurrency: jobs: check-docker: name: Build docker containers - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - arch: [amd64, aarch64] + os: ["ubuntu-latest", "ubuntu-24.04-arm"] build_type: ["ha-addon", "docker", "lint"] steps: - uses: actions/checkout@v4.1.7 @@ -47,8 +47,6 @@ jobs: python-version: "3.9" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3.9.0 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3.4.0 - name: Set TAG run: | @@ -58,6 +56,6 @@ jobs: run: | docker/build.py \ --tag "${TAG}" \ - --arch "${{ matrix.arch }}" \ + --arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \ --build-type "${{ matrix.build_type }}" \ build From 74f7197543e2792262507595b5eccfd8dc0b32f4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 17 Feb 2025 16:27:06 -0600 Subject: [PATCH 1037/1052] Bump aioesphomeapi to 29.1.0 (#8105) --- esphome/components/api/client.py | 11 ++++++++--- requirements.txt | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/client.py b/esphome/components/api/client.py index dd013c8c34..c61b8d5ea3 100644 --- a/esphome/components/api/client.py +++ b/esphome/components/api/client.py @@ -1,12 +1,11 @@ from __future__ import annotations import asyncio -import logging from datetime import datetime -from typing import Any +import logging +from typing import TYPE_CHECKING, Any from aioesphomeapi import APIClient -from aioesphomeapi.api_pb2 import SubscribeLogsResponse from aioesphomeapi.log_runner import async_run from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__ @@ -14,6 +13,12 @@ from esphome.core import CORE from . import CONF_ENCRYPTION +if TYPE_CHECKING: + from aioesphomeapi.api_pb2 import ( + SubscribeLogsResponse, # pylint: disable=no-name-in-module + ) + + _LOGGER = logging.getLogger(__name__) diff --git a/requirements.txt b/requirements.txt index b3a48a0442..c15dcbbbf7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 -aioesphomeapi==24.6.2 +aioesphomeapi==29.1.0 zeroconf==0.144.3 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import From f9da8dbfb85e329be30dfcf02bfc6431ef1fa81c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 17 Feb 2025 15:48:24 -0600 Subject: [PATCH 1038/1052] Replace glyphsets with esphome_glyphsets (#8261) --- esphome/components/font/__init__.py | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index e2051298fe..4f569379be 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -5,8 +5,8 @@ import os from pathlib import Path import re +import esphome_glyphsets as glyphsets import freetype -import glyphsets import requests from esphome import core, external_files diff --git a/requirements.txt b/requirements.txt index bb5bc768e1..b3a48a0442 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ aioesphomeapi==24.6.2 zeroconf==0.144.3 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import -glyphsets==1.0.0 +esphome-glyphsets==0.1.0 pillow==10.4.0 freetype-py==2.5.1 From 64c8bcef2e562ebdb2d398d5c66c80c417b8129f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 17 Feb 2025 16:27:06 -0600 Subject: [PATCH 1039/1052] Bump aioesphomeapi to 29.1.0 (#8105) --- esphome/components/api/client.py | 11 ++++++++--- requirements.txt | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/esphome/components/api/client.py b/esphome/components/api/client.py index dd013c8c34..c61b8d5ea3 100644 --- a/esphome/components/api/client.py +++ b/esphome/components/api/client.py @@ -1,12 +1,11 @@ from __future__ import annotations import asyncio -import logging from datetime import datetime -from typing import Any +import logging +from typing import TYPE_CHECKING, Any from aioesphomeapi import APIClient -from aioesphomeapi.api_pb2 import SubscribeLogsResponse from aioesphomeapi.log_runner import async_run from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__ @@ -14,6 +13,12 @@ from esphome.core import CORE from . import CONF_ENCRYPTION +if TYPE_CHECKING: + from aioesphomeapi.api_pb2 import ( + SubscribeLogsResponse, # pylint: disable=no-name-in-module + ) + + _LOGGER = logging.getLogger(__name__) diff --git a/requirements.txt b/requirements.txt index b3a48a0442..c15dcbbbf7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ platformio==6.1.16 # When updating platformio, also update Dockerfile esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 -aioesphomeapi==24.6.2 +aioesphomeapi==29.1.0 zeroconf==0.144.3 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import From 6583e178107e63446ad9a21b3ebf82b7533d8c93 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:39:42 +1300 Subject: [PATCH 1040/1052] Bump version to 2025.2.0b5 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7c96e6eefb..7f576fce67 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0b4" +__version__ = "2025.2.0b5" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From abbd72e8028d8fdfdd5caf3fd46023d19ce0dea4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Feb 2025 11:10:33 -0600 Subject: [PATCH 1041/1052] Use the process CPU count to determine how many children to create (#8268) --- esphome/core/config.py | 16 ++++++++++++---- script/clang-format | 22 +++++++++++++++------- script/clang-tidy | 4 ++-- script/helpers.py | 11 +++++++++++ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/esphome/core/config.py b/esphome/core/config.py index 2077af02a7..359b78acf1 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -1,5 +1,4 @@ import logging -import multiprocessing import os from pathlib import Path @@ -94,10 +93,19 @@ def valid_project_name(value: str): return value +def get_usable_cpu_count() -> int: + """Return the number of CPUs that can be used for processes. + On Python 3.13+ this is the number of CPUs that can be used for processes. + On older Python versions this is the number of CPUs. + """ + return ( + os.process_cpu_count() if hasattr(os, "process_cpu_count") else os.cpu_count() + ) + + if "ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT" in os.environ: _compile_process_limit_default = min( - int(os.environ["ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT"]), - multiprocessing.cpu_count(), + int(os.environ["ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT"]), get_usable_cpu_count() ) else: _compile_process_limit_default = cv.UNDEFINED @@ -156,7 +164,7 @@ CONFIG_SCHEMA = cv.All( ), cv.Optional( CONF_COMPILE_PROCESS_LIMIT, default=_compile_process_limit_default - ): cv.int_range(min=1, max=multiprocessing.cpu_count()), + ): cv.int_range(min=1, max=get_usable_cpu_count()), } ), validate_hostname, diff --git a/script/clang-format b/script/clang-format index d922c5b6f1..b1e84a56b7 100755 --- a/script/clang-format +++ b/script/clang-format @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import argparse -import multiprocessing import os import queue import re @@ -11,7 +10,13 @@ import threading import click import colorama -from helpers import filter_changed, get_binary, git_ls_files, print_error_for_file +from helpers import ( + filter_changed, + get_binary, + get_usable_cpu_count, + git_ls_files, + print_error_for_file, +) def run_format(executable, args, queue, lock, failed_files): @@ -25,7 +30,9 @@ def run_format(executable, args, queue, lock, failed_files): invocation.extend(["--dry-run", "-Werror"]) invocation.append(path) - proc = subprocess.run(invocation, capture_output=True, encoding="utf-8") + proc = subprocess.run( + invocation, capture_output=True, encoding="utf-8", check=False + ) if proc.returncode != 0: with lock: print_error_for_file(path, proc.stderr) @@ -45,7 +52,7 @@ def main(): "-j", "--jobs", type=int, - default=multiprocessing.cpu_count(), + default=get_usable_cpu_count(), help="number of format instances to be run in parallel.", ) parser.add_argument( @@ -80,7 +87,8 @@ def main(): lock = threading.Lock() for _ in range(args.jobs): t = threading.Thread( - target=run_format, args=(executable, args, task_queue, lock, failed_files) + target=run_format, + args=(executable, args, task_queue, lock, failed_files), ) t.daemon = True t.start() @@ -95,7 +103,7 @@ def main(): # Wait for all threads to be done. task_queue.join() - except FileNotFoundError as ex: + except FileNotFoundError: return 1 except KeyboardInterrupt: print() @@ -103,7 +111,7 @@ def main(): # Kill subprocesses (and ourselves!) # No simple, clean alternative appears to be available. os.kill(0, 9) - return 2 # Will not execute. + return 2 # Will not execute. return len(failed_files) diff --git a/script/clang-tidy b/script/clang-tidy index 5c19f81043..51705f955b 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -1,7 +1,6 @@ #!/usr/bin/env python3 import argparse -import multiprocessing import os import queue import re @@ -19,6 +18,7 @@ from helpers import ( filter_changed, filter_grep, get_binary, + get_usable_cpu_count, git_ls_files, load_idedata, print_error_for_file, @@ -170,7 +170,7 @@ def main(): "-j", "--jobs", type=int, - default=multiprocessing.cpu_count(), + default=get_usable_cpu_count(), help="number of tidy instances to be run in parallel.", ) parser.add_argument( diff --git a/script/helpers.py b/script/helpers.py index 6f36faaeb1..6148371e32 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -188,3 +188,14 @@ def get_binary(name: str, version: str) -> str: """ ) raise + + +def get_usable_cpu_count() -> int: + """Return the number of CPUs that can be used for processes. + + On Python 3.13+ this is the number of CPUs that can be used for processes. + On older Python versions this is the number of CPUs. + """ + return ( + os.process_cpu_count() if hasattr(os, "process_cpu_count") else os.cpu_count() + ) From 56034e3e79331e9b38db5f4a733486929da753c3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Feb 2025 11:11:58 -0600 Subject: [PATCH 1042/1052] Bump openssh-client to 1:9.2p1-2+deb12u4 to fix docker builds (#8269) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1db1ee7b51..6da5c52d64 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -35,7 +35,7 @@ RUN \ iputils-ping=3:20221126-1+deb12u1 \ git=1:2.39.5-0+deb12u1 \ curl=7.88.1-10+deb12u8 \ - openssh-client=1:9.2p1-2+deb12u3 \ + openssh-client=1:9.2p1-2+deb12u4 \ python3-cffi=1.15.1-5 \ libcairo2=1.16.0-7 \ libmagic1=1:5.44-3 \ From b3db04a3d300838c1a3154eed77dc6220d5655eb Mon Sep 17 00:00:00 2001 From: G-Two <7310260+G-Two@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:30:03 -0500 Subject: [PATCH 1043/1052] Increase default repeat delay for Toto remote transmitter protocol (#8265) --- esphome/components/remote_base/toto_protocol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/remote_base/toto_protocol.h b/esphome/components/remote_base/toto_protocol.h index e62714bbbf..6a635b0f7c 100644 --- a/esphome/components/remote_base/toto_protocol.h +++ b/esphome/components/remote_base/toto_protocol.h @@ -36,7 +36,7 @@ template class TotoAction : public RemoteTransmitterActionBaserc_code_2_.value(x...); data.command = this->command_.value(x...); this->set_send_times(this->send_times_.value_or(x..., 3)); - this->set_send_wait(this->send_wait_.value_or(x..., 32000)); + this->set_send_wait(this->send_wait_.value_or(x..., 36000)); TotoProtocol().encode(dst, data); } }; From 02bf33c54887103ec957a78245d5a41f66151212 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Feb 2025 11:38:41 -0600 Subject: [PATCH 1044/1052] Bump zeroconf to 0.145.1 (#8267) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c15dcbbbf7..1de6e3dd06 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 aioesphomeapi==29.1.0 -zeroconf==0.144.3 +zeroconf==0.145.1 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import esphome-glyphsets==0.1.0 From d9b419eaf543e2a51674c3eb61d87b84d04eb172 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Feb 2025 11:11:58 -0600 Subject: [PATCH 1045/1052] Bump openssh-client to 1:9.2p1-2+deb12u4 to fix docker builds (#8269) --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1db1ee7b51..6da5c52d64 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -35,7 +35,7 @@ RUN \ iputils-ping=3:20221126-1+deb12u1 \ git=1:2.39.5-0+deb12u1 \ curl=7.88.1-10+deb12u8 \ - openssh-client=1:9.2p1-2+deb12u3 \ + openssh-client=1:9.2p1-2+deb12u4 \ python3-cffi=1.15.1-5 \ libcairo2=1.16.0-7 \ libmagic1=1:5.44-3 \ From a973adda67ab9ed8df5dabf3e314745d0b0dec99 Mon Sep 17 00:00:00 2001 From: G-Two <7310260+G-Two@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:30:03 -0500 Subject: [PATCH 1046/1052] Increase default repeat delay for Toto remote transmitter protocol (#8265) --- esphome/components/remote_base/toto_protocol.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/remote_base/toto_protocol.h b/esphome/components/remote_base/toto_protocol.h index e62714bbbf..6a635b0f7c 100644 --- a/esphome/components/remote_base/toto_protocol.h +++ b/esphome/components/remote_base/toto_protocol.h @@ -36,7 +36,7 @@ template class TotoAction : public RemoteTransmitterActionBaserc_code_2_.value(x...); data.command = this->command_.value(x...); this->set_send_times(this->send_times_.value_or(x..., 3)); - this->set_send_wait(this->send_wait_.value_or(x..., 32000)); + this->set_send_wait(this->send_wait_.value_or(x..., 36000)); TotoProtocol().encode(dst, data); } }; From c5ebf7683e06ac9bf8c9a5aaa7da47da6bffde8a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 18 Feb 2025 11:38:41 -0600 Subject: [PATCH 1047/1052] Bump zeroconf to 0.145.1 (#8267) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c15dcbbbf7..1de6e3dd06 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ esptool==4.7.0 click==8.1.7 esphome-dashboard==20250212.0 aioesphomeapi==29.1.0 -zeroconf==0.144.3 +zeroconf==0.145.1 puremagic==1.27 ruamel.yaml==0.18.6 # dashboard_import esphome-glyphsets==0.1.0 From 4d380214df3bf0f3554bb24bf4f4c9d8a15daef8 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 19 Feb 2025 09:22:52 +1300 Subject: [PATCH 1048/1052] Bump version to 2025.2.0b6 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index 7f576fce67..fe4ed20c57 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0b5" +__version__ = "2025.2.0b6" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 7006bd24a5b85ff4bd93e7218dbf230b2fe0a991 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Feb 2025 23:23:56 +0000 Subject: [PATCH 1049/1052] Bump actions/cache from 4.2.0 to 4.2.1 in /.github/actions/restore-python (#8273) --- .github/actions/restore-python/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index e95eb6331f..c53e64a7b9 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -22,7 +22,7 @@ runs: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache/restore@v4.2.0 + uses: actions/cache/restore@v4.2.1 with: path: venv # yamllint disable-line rule:line-length From ba79e2d7b1216aed0ca2c0f0a9d113077720992b Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:40:15 +1300 Subject: [PATCH 1050/1052] Bump version to 2025.2.0 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index fe4ed20c57..9f765dbf81 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2025.2.0b6" +__version__ = "2025.2.0" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" VALID_SUBSTITUTIONS_CHARACTERS = ( From 7529fb10b462c4d9c75b68942746f4052dfda570 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:46:22 +1300 Subject: [PATCH 1051/1052] Bump actions/cache from 4.2.0 to 4.2.1 (#8271) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab77db5ca5..bd79356b3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment id: cache-venv - uses: actions/cache@v4.2.0 + uses: actions/cache@v4.2.1 with: path: venv # yamllint disable-line rule:line-length @@ -303,14 +303,14 @@ jobs: - name: Cache platformio if: github.ref == 'refs/heads/dev' - uses: actions/cache@v4.2.0 + uses: actions/cache@v4.2.1 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} - name: Cache platformio if: github.ref != 'refs/heads/dev' - uses: actions/cache/restore@v4.2.0 + uses: actions/cache/restore@v4.2.1 with: path: ~/.platformio key: platformio-${{ matrix.pio_cache_key }} From 302008356471506fdd37d9a0cbe2548642cbaa15 Mon Sep 17 00:00:00 2001 From: Katherine Whitlock Date: Wed, 19 Feb 2025 14:24:43 -0500 Subject: [PATCH 1052/1052] Ruff format for CI (#8276) --- .github/workflows/ci.yml | 12 ++++---- .pre-commit-config.yaml | 2 +- esphome/__main__.py | 2 +- esphome/components/climate/__init__.py | 1 - .../components/esp32_ble_beacon/__init__.py | 4 ++- esphome/components/esp8266/gpio.py | 3 +- esphome/components/haier/climate.py | 23 +++++++------- esphome/components/mqtt/__init__.py | 4 +-- .../components/nfc/binary_sensor/__init__.py | 8 ++--- .../opentherm/binary_sensor/__init__.py | 8 ++--- esphome/components/opentherm/generate.py | 12 ++++---- .../components/opentherm/sensor/__init__.py | 11 ++++--- esphome/components/pn532/binary_sensor.py | 8 ++--- esphome/components/rc522/binary_sensor.py | 8 ++--- esphome/components/thermostat/climate.py | 2 +- esphome/config_validation.py | 3 +- esphome/cpp_generator.py | 6 ++-- esphome/wizard.py | 30 ++++++++----------- esphome/writer.py | 4 +-- requirements_test.txt | 2 +- tests/unit_tests/test_cpp_generator.py | 9 ++---- 21 files changed, 75 insertions(+), 87 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd79356b3c..59dc31e9f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,8 +61,8 @@ jobs: pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt pip install -e . - black: - name: Check black + ruff: + name: Check ruff runs-on: ubuntu-24.04 needs: - common @@ -74,10 +74,10 @@ jobs: with: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - - name: Run black + - name: Run Ruff run: | . venv/bin/activate - black --verbose esphome tests + ruff format esphome tests - name: Suggested changes run: script/ci-suggest-changes if: always() @@ -255,7 +255,7 @@ jobs: runs-on: ubuntu-24.04 needs: - common - - black + - ruff - ci-custom - clang-format - flake8 @@ -482,7 +482,7 @@ jobs: runs-on: ubuntu-24.04 needs: - common - - black + - ruff - ci-custom - clang-format - flake8 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 212d822ff8..667a8f2e8b 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.5.4 + rev: v0.9.2 hooks: # Run the linter. - id: ruff diff --git a/esphome/__main__.py b/esphome/__main__.py index 2a0bd8f2b3..770c1a8fcf 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -66,7 +66,7 @@ def choose_prompt(options, purpose: str = None): return options[0][1] safe_print( - f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:' + f"Found multiple options{f' for {purpose}' if purpose else ''}, please choose one:" ) for i, (desc, _) in enumerate(options): safe_print(f" [{i + 1}] {desc}") diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index aa705e7332..445507c620 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -128,7 +128,6 @@ VISUAL_TEMPERATURE_STEP_SCHEMA = cv.Schema( def visual_temperature_step(value): - # Allow defining target/current temperature steps separately if isinstance(value, dict): return VISUAL_TEMPERATURE_STEP_SCHEMA(value) diff --git a/esphome/components/esp32_ble_beacon/__init__.py b/esphome/components/esp32_ble_beacon/__init__.py index f97f289a0a..6e0d103aa0 100644 --- a/esphome/components/esp32_ble_beacon/__init__.py +++ b/esphome/components/esp32_ble_beacon/__init__.py @@ -66,7 +66,9 @@ FINAL_VALIDATE_SCHEMA = esp32_ble.validate_variant async def to_code(config): uuid = config[CONF_UUID].hex - uuid_arr = [cg.RawExpression(f"0x{uuid[i:i + 2]}") for i in range(0, len(uuid), 2)] + uuid_arr = [ + cg.RawExpression(f"0x{uuid[i : i + 2]}") for i in range(0, len(uuid), 2) + ] var = cg.new_Pvariable(config[CONF_ID], uuid_arr) parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID]) diff --git a/esphome/components/esp8266/gpio.py b/esphome/components/esp8266/gpio.py index 53016d2130..050efaacae 100644 --- a/esphome/components/esp8266/gpio.py +++ b/esphome/components/esp8266/gpio.py @@ -112,8 +112,7 @@ def validate_supports(value): ) if is_pullup and num == 16: raise cv.Invalid( - "GPIO Pin 16 does not support pullup pin mode. " - "Please choose another pin.", + "GPIO Pin 16 does not support pullup pin mode. Please choose another pin.", [CONF_MODE, CONF_PULLUP], ) if is_pulldown and num != 16: diff --git a/esphome/components/haier/climate.py b/esphome/components/haier/climate.py index f2dc7174cb..f77d624649 100644 --- a/esphome/components/haier/climate.py +++ b/esphome/components/haier/climate.py @@ -1,9 +1,15 @@ -import logging -import esphome.codegen as cg -import esphome.config_validation as cv -import esphome.final_validate as fv -from esphome.components import uart, climate, logger +import logging + from esphome import automation +import esphome.codegen as cg +from esphome.components import climate, logger, uart +from esphome.components.climate import ( + CONF_CURRENT_TEMPERATURE, + ClimateMode, + ClimatePreset, + ClimateSwingMode, +) +import esphome.config_validation as cv from esphome.const import ( CONF_BEEPER, CONF_DISPLAY, @@ -24,12 +30,7 @@ from esphome.const import ( CONF_VISUAL, CONF_WIFI, ) -from esphome.components.climate import ( - ClimateMode, - ClimatePreset, - ClimateSwingMode, - CONF_CURRENT_TEMPERATURE, -) +import esphome.final_validate as fv _LOGGER = logging.getLogger(__name__) diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index e1002478a1..99f8ad76d8 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -36,6 +36,7 @@ from esphome.const import ( CONF_PAYLOAD_AVAILABLE, CONF_PAYLOAD_NOT_AVAILABLE, CONF_PORT, + CONF_PUBLISH_NAN_AS_NONE, CONF_QOS, CONF_REBOOT_TIMEOUT, CONF_RETAIN, @@ -49,7 +50,6 @@ from esphome.const import ( CONF_USE_ABBREVIATIONS, CONF_USERNAME, CONF_WILL_MESSAGE, - CONF_PUBLISH_NAN_AS_NONE, PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_ESP8266, @@ -406,7 +406,7 @@ async def to_code(config): if CONF_SSL_FINGERPRINTS in config: for fingerprint in config[CONF_SSL_FINGERPRINTS]: arr = [ - cg.RawExpression(f"0x{fingerprint[i:i + 2]}") for i in range(0, 40, 2) + cg.RawExpression(f"0x{fingerprint[i : i + 2]}") for i in range(0, 40, 2) ] cg.add(var.add_ssl_fingerprint(arr)) cg.add_build_flag("-DASYNC_TCP_SSL_ENABLED=1") diff --git a/esphome/components/nfc/binary_sensor/__init__.py b/esphome/components/nfc/binary_sensor/__init__.py index 21c8298ea8..47cf014550 100644 --- a/esphome/components/nfc/binary_sensor/__init__.py +++ b/esphome/components/nfc/binary_sensor/__init__.py @@ -1,9 +1,10 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import binary_sensor +import esphome.config_validation as cv from esphome.const import CONF_UID from esphome.core import HexInt -from .. import nfc_ns, Nfcc, NfcTagListener + +from .. import Nfcc, NfcTagListener, nfc_ns DEPENDENCIES = ["nfc"] @@ -25,8 +26,7 @@ def validate_uid(value): for x in value.split("-"): if len(x) != 2: raise cv.Invalid( - "Each part (separated by '-') of the UID must be two characters " - "long." + "Each part (separated by '-') of the UID must be two characters long." ) try: x = int(x, 16) diff --git a/esphome/components/opentherm/binary_sensor/__init__.py b/esphome/components/opentherm/binary_sensor/__init__.py index 643734f90c..d4c7861a1d 100644 --- a/esphome/components/opentherm/binary_sensor/__init__.py +++ b/esphome/components/opentherm/binary_sensor/__init__.py @@ -1,8 +1,9 @@ from typing import Any -import esphome.config_validation as cv from esphome.components import binary_sensor -from .. import const, schema, validate, generate +import esphome.config_validation as cv + +from .. import const, generate, schema, validate DEPENDENCIES = [const.OPENTHERM] COMPONENT_TYPE = const.BINARY_SENSOR @@ -11,8 +12,7 @@ COMPONENT_TYPE = const.BINARY_SENSOR def get_entity_validation_schema(entity: schema.BinarySensorSchema) -> cv.Schema: return binary_sensor.binary_sensor_schema( device_class=( - entity.device_class - or binary_sensor._UNDEF # pylint: disable=protected-access + entity.device_class or binary_sensor._UNDEF # pylint: disable=protected-access ), icon=(entity.icon or binary_sensor._UNDEF), # pylint: disable=protected-access ) diff --git a/esphome/components/opentherm/generate.py b/esphome/components/opentherm/generate.py index 6b6a0255a8..a97754d52c 100644 --- a/esphome/components/opentherm/generate.py +++ b/esphome/components/opentherm/generate.py @@ -3,8 +3,9 @@ from typing import Any, Callable, Optional import esphome.codegen as cg from esphome.const import CONF_ID + from . import const -from .schema import TSchema, SettingSchema +from .schema import SettingSchema, TSchema opentherm_ns = cg.esphome_ns.namespace("opentherm") OpenthermHub = opentherm_ns.class_("OpenthermHub", cg.Component) @@ -112,11 +113,10 @@ def add_messages(hub: cg.MockObj, keys: list[str], schemas: dict[str, TSchema]): msg_expr = cg.RawExpression(f"esphome::opentherm::MessageId::{msg}") if keep_updated: cg.add(hub.add_repeating_message(msg_expr)) + elif order is not None: + cg.add(hub.add_initial_message(msg_expr, order)) else: - if order is not None: - cg.add(hub.add_initial_message(msg_expr, order)) - else: - cg.add(hub.add_initial_message(msg_expr)) + cg.add(hub.add_initial_message(msg_expr)) def add_property_set(var: cg.MockObj, config_key: str, config: dict[str, Any]) -> None: @@ -128,7 +128,7 @@ Create = Callable[[dict[str, Any], str, cg.MockObj], Awaitable[cg.Pvariable]] def create_only_conf( - create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]] + create: Callable[[dict[str, Any]], Awaitable[cg.Pvariable]], ) -> Create: return lambda conf, _key, _hub: create(conf) diff --git a/esphome/components/opentherm/sensor/__init__.py b/esphome/components/opentherm/sensor/__init__.py index 546a79054b..86c842b299 100644 --- a/esphome/components/opentherm/sensor/__init__.py +++ b/esphome/components/opentherm/sensor/__init__.py @@ -1,8 +1,9 @@ from typing import Any -import esphome.config_validation as cv from esphome.components import sensor -from .. import const, schema, validate, generate +import esphome.config_validation as cv + +from .. import const, generate, schema, validate DEPENDENCIES = [const.OPENTHERM] COMPONENT_TYPE = const.SENSOR @@ -22,11 +23,9 @@ MSG_DATA_TYPES = { def get_entity_validation_schema(entity: schema.SensorSchema) -> cv.Schema: return sensor.sensor_schema( - unit_of_measurement=entity.unit_of_measurement - or sensor._UNDEF, # pylint: disable=protected-access + unit_of_measurement=entity.unit_of_measurement or sensor._UNDEF, # pylint: disable=protected-access accuracy_decimals=entity.accuracy_decimals, - device_class=entity.device_class - or sensor._UNDEF, # pylint: disable=protected-access + device_class=entity.device_class or sensor._UNDEF, # pylint: disable=protected-access icon=entity.icon or sensor._UNDEF, # pylint: disable=protected-access state_class=entity.state_class, ).extend( diff --git a/esphome/components/pn532/binary_sensor.py b/esphome/components/pn532/binary_sensor.py index 9bcae30750..b9c3103c65 100644 --- a/esphome/components/pn532/binary_sensor.py +++ b/esphome/components/pn532/binary_sensor.py @@ -1,9 +1,10 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import binary_sensor +import esphome.config_validation as cv from esphome.const import CONF_UID from esphome.core import HexInt -from . import pn532_ns, PN532, CONF_PN532_ID + +from . import CONF_PN532_ID, PN532, pn532_ns DEPENDENCIES = ["pn532"] @@ -13,8 +14,7 @@ def validate_uid(value): for x in value.split("-"): if len(x) != 2: raise cv.Invalid( - "Each part (separated by '-') of the UID must be two characters " - "long." + "Each part (separated by '-') of the UID must be two characters long." ) try: x = int(x, 16) diff --git a/esphome/components/rc522/binary_sensor.py b/esphome/components/rc522/binary_sensor.py index 716c0eca76..87f81c2223 100644 --- a/esphome/components/rc522/binary_sensor.py +++ b/esphome/components/rc522/binary_sensor.py @@ -1,9 +1,10 @@ import esphome.codegen as cg -import esphome.config_validation as cv from esphome.components import binary_sensor +import esphome.config_validation as cv from esphome.const import CONF_UID from esphome.core import HexInt -from . import rc522_ns, RC522, CONF_RC522_ID + +from . import CONF_RC522_ID, RC522, rc522_ns DEPENDENCIES = ["rc522"] @@ -13,8 +14,7 @@ def validate_uid(value): for x in value.split("-"): if len(x) != 2: raise cv.Invalid( - "Each part (separated by '-') of the UID must be two characters " - "long." + "Each part (separated by '-') of the UID must be two characters long." ) try: x = int(x, 16) diff --git a/esphome/components/thermostat/climate.py b/esphome/components/thermostat/climate.py index a529bbd474..638aad7c06 100644 --- a/esphome/components/thermostat/climate.py +++ b/esphome/components/thermostat/climate.py @@ -137,7 +137,7 @@ def validate_temperature_preset(preset, root_config, name, requirements): def generate_comparable_preset(config, name): - comparable_preset = f"{CONF_PRESET}:\n" f" - {CONF_NAME}: {name}\n" + comparable_preset = f"{CONF_PRESET}:\n - {CONF_NAME}: {name}\n" if CONF_DEFAULT_TARGET_TEMPERATURE_LOW in config: comparable_preset += f" {CONF_DEFAULT_TARGET_TEMPERATURE_LOW}: {config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW]}\n" diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 27d11e4ded..9f7ce4d2e3 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1223,8 +1223,7 @@ def subscribe_topic(value): if index != len(value) - 1: # If there are multiple wildcards, this will also trigger raise Invalid( - "Multi-level wildcard must be the last " - "character in the topic filter." + "Multi-level wildcard must be the last character in the topic filter." ) if len(value) > 1 and value[index - 1] != "/": raise Invalid("Multi-level wildcard must be after a topic level separator.") diff --git a/esphome/cpp_generator.py b/esphome/cpp_generator.py index 4e283868e1..eb0bd25d1d 100644 --- a/esphome/cpp_generator.py +++ b/esphome/cpp_generator.py @@ -506,9 +506,9 @@ def with_local_variable(id_: ID, rhs: SafeExpType, callback: Callable, *args) -> """ # throw if the callback is async: - assert not inspect.iscoroutinefunction( - callback - ), "with_local_variable() callback cannot be async!" + assert not inspect.iscoroutinefunction(callback), ( + "with_local_variable() callback cannot be async!" + ) CORE.add(RawStatement("{")) # output opening curly brace obj = variable(id_, rhs, None, True) diff --git a/esphome/wizard.py b/esphome/wizard.py index eecbbdb172..7fdf245c76 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -144,17 +144,17 @@ def wizard_file(**kwargs): # Configure API if "password" in kwargs: - config += f" password: \"{kwargs['password']}\"\n" + config += f' password: "{kwargs["password"]}"\n' if "api_encryption_key" in kwargs: - config += f" encryption:\n key: \"{kwargs['api_encryption_key']}\"\n" + config += f' encryption:\n key: "{kwargs["api_encryption_key"]}"\n' # Configure OTA config += "\nota:\n" config += " - platform: esphome\n" if "ota_password" in kwargs: - config += f" password: \"{kwargs['ota_password']}\"" + config += f' password: "{kwargs["ota_password"]}"' elif "password" in kwargs: - config += f" password: \"{kwargs['password']}\"" + config += f' password: "{kwargs["password"]}"' # Configuring wifi config += "\n\nwifi:\n" @@ -181,18 +181,14 @@ def wizard_file(**kwargs): password: "{fallback_psk}" captive_portal: - """.format( - **kwargs - ) + """.format(**kwargs) else: config += """ # Enable fallback hotspot in case wifi connection fails ap: ssid: "{fallback_name}" password: "{fallback_psk}" - """.format( - **kwargs - ) + """.format(**kwargs) return config @@ -388,19 +384,19 @@ def wizard(path): safe_print() # Don't sleep because user needs to copy link if platform == "ESP32": - safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcu-32s')}\".") + safe_print(f'For example "{color(Fore.BOLD_WHITE, "nodemcu-32s")}".') boards_list = esp32_boards.BOARDS.items() elif platform == "ESP8266": - safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'nodemcuv2')}\".") + safe_print(f'For example "{color(Fore.BOLD_WHITE, "nodemcuv2")}".') boards_list = esp8266_boards.BOARDS.items() elif platform == "BK72XX": - safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'cb2s')}\".") + safe_print(f'For example "{color(Fore.BOLD_WHITE, "cb2s")}".') boards_list = bk72xx_boards.BOARDS.items() elif platform == "RTL87XX": - safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'wr3')}\".") + safe_print(f'For example "{color(Fore.BOLD_WHITE, "wr3")}".') boards_list = rtl87xx_boards.BOARDS.items() elif platform == "RP2040": - safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'rpipicow')}\".") + safe_print(f'For example "{color(Fore.BOLD_WHITE, "rpipicow")}".') boards_list = rp2040_boards.BOARDS.items() else: @@ -439,7 +435,7 @@ def wizard(path): f"First, what's the {color(Fore.GREEN, 'SSID')} (the name) of the WiFi network {name} should connect to?" ) sleep(1.5) - safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'Abraham Linksys')}\".") + safe_print(f'For example "{color(Fore.BOLD_WHITE, "Abraham Linksys")}".') while True: ssid = safe_input(color(Fore.BOLD_WHITE, "(ssid): ")) try: @@ -465,7 +461,7 @@ def wizard(path): f"Now please state the {color(Fore.GREEN, 'password')} of the WiFi network so that I can connect to it (Leave empty for no password)" ) safe_print() - safe_print(f"For example \"{color(Fore.BOLD_WHITE, 'PASSWORD42')}\"") + safe_print(f'For example "{color(Fore.BOLD_WHITE, "PASSWORD42")}"') sleep(0.5) psk = safe_input(color(Fore.BOLD_WHITE, "(PSK): ")) safe_print( diff --git a/esphome/writer.py b/esphome/writer.py index 90446ae4b1..39423db64c 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -212,9 +212,7 @@ def write_platformio_project(): write_platformio_ini(content) -DEFINES_H_FORMAT = ( - ESPHOME_H_FORMAT -) = """\ +DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\ #pragma once #include "esphome/core/macros.h" {} diff --git a/requirements_test.txt b/requirements_test.txt index 5d94f7f640..d836efc148 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==3.2.7 flake8==7.0.0 # also change in .pre-commit-config.yaml when updating -black==24.4.2 # also change in .pre-commit-config.yaml when updating +ruff==0.9.2 # also change in .pre-commit-config.yaml when updating pyupgrade==3.15.2 # also change in .pre-commit-config.yaml when updating pre-commit diff --git a/tests/unit_tests/test_cpp_generator.py b/tests/unit_tests/test_cpp_generator.py index 6f4b5a40bc..95633ca0c6 100644 --- a/tests/unit_tests/test_cpp_generator.py +++ b/tests/unit_tests/test_cpp_generator.py @@ -1,11 +1,9 @@ from collections.abc import Iterator - import math import pytest -from esphome import cpp_generator as cg -from esphome import cpp_types as ct +from esphome import cpp_generator as cg, cpp_types as ct class TestExpressions: @@ -156,10 +154,7 @@ class TestLambdaExpression: actual = str(target) assert actual == ( - "[=](int32_t foo, float bar) {\n" - " if ((foo == 5) && (bar < 10))) {\n" - " }\n" - "}" + "[=](int32_t foo, float bar) {\n if ((foo == 5) && (bar < 10))) {\n }\n}" ) def test_str__with_return(self):